diff --git a/packages/amazonq/.changes/next-release/Feature-b92f1e8b-78a0-46a6-ba9b-2300c884fcbc.json b/packages/amazonq/.changes/next-release/Feature-b92f1e8b-78a0-46a6-ba9b-2300c884fcbc.json new file mode 100644 index 00000000000..20664e7f542 --- /dev/null +++ b/packages/amazonq/.changes/next-release/Feature-b92f1e8b-78a0-46a6-ba9b-2300c884fcbc.json @@ -0,0 +1,4 @@ +{ + "type": "Feature", + "description": "Add acknowledgement button for amazon q chat disclaimer" +} diff --git a/packages/core/src/amazonq/webview/generators/webViewContent.ts b/packages/core/src/amazonq/webview/generators/webViewContent.ts index fb83ab895a6..6b935e83642 100644 --- a/packages/core/src/amazonq/webview/generators/webViewContent.ts +++ b/packages/core/src/amazonq/webview/generators/webViewContent.ts @@ -78,6 +78,7 @@ export class WebViewContentGenerator { const featureConfigsString = await this.generateFeatureConfigsData() const disabledCommandsString = isSageMaker() ? `['/dev', '/transform']` : '[]' + const disclaimerAcknowledged = globals.globalState.tryGet('aws.amazonq.disclaimerAcknowledged', Boolean, false) return ` @@ -86,7 +87,7 @@ export class WebViewContentGenerator { const init = () => { createMynahUI(acquireVsCodeApi(), ${ (await AuthUtil.instance.getChatAuthState()).amazonQ === 'connected' - },${featureConfigsString},${showWelcomePage},${disabledCommandsString}); + },${featureConfigsString},${showWelcomePage},${disclaimerAcknowledged},${disabledCommandsString}); } ` diff --git a/packages/core/src/amazonq/webview/messages/messageDispatcher.ts b/packages/core/src/amazonq/webview/messages/messageDispatcher.ts index 244f6ae1583..95e301a0470 100644 --- a/packages/core/src/amazonq/webview/messages/messageDispatcher.ts +++ b/packages/core/src/amazonq/webview/messages/messageDispatcher.ts @@ -11,7 +11,7 @@ import { getLogger } from '../../../shared/logger' import { amazonqMark } from '../../../shared/performance/marks' import { telemetry } from '../../../shared/telemetry' import { AmazonQChatMessageDuration } from '../../messages/chatMessageDuration' -import { openUrl } from '../../../shared' +import { globals, openUrl } from '../../../shared' import { isClickTelemetry, isOpenAgentTelemetry } from '../ui/telemetry/actions' export function dispatchWebViewMessagesToApps( @@ -60,12 +60,19 @@ export function dispatchWebViewMessagesToApps( source: msg.trigger, result: 'Succeeded', }) + return } else if (isClickTelemetry(msg)) { telemetry.ui_click.emit({ elementId: msg.source, result: 'Succeeded', }) + return } + return + } + case 'disclaimer-acknowledged': { + globals.globalState.tryUpdate('aws.amazonq.disclaimerAcknowledged', true) + return } } diff --git a/packages/core/src/amazonq/webview/ui/commands.ts b/packages/core/src/amazonq/webview/ui/commands.ts index ea925d93bef..643595e3e1f 100644 --- a/packages/core/src/amazonq/webview/ui/commands.ts +++ b/packages/core/src/amazonq/webview/ui/commands.ts @@ -10,6 +10,7 @@ type MessageCommand = | 'tab-was-removed' | 'tab-was-changed' | 'ui-is-ready' + | 'disclaimer-acknowledged' | 'ui-focus' | 'follow-up-was-clicked' | 'auth-follow-up-was-clicked' diff --git a/packages/core/src/amazonq/webview/ui/main.ts b/packages/core/src/amazonq/webview/ui/main.ts index 58511517e7a..d4243928a1b 100644 --- a/packages/core/src/amazonq/webview/ui/main.ts +++ b/packages/core/src/amazonq/webview/ui/main.ts @@ -31,14 +31,17 @@ import { tryNewMap } from '../../util/functionUtils' import { welcomeScreenTabData } from './walkthrough/welcome' import { agentWalkthroughDataModel } from './walkthrough/agent' import { createClickTelemetry, createOpenAgentTelemetry } from './telemetry/actions' +import { disclaimerAcknowledgeButtonId, disclaimerCard } from './texts/disclaimer' export const createMynahUI = ( ideApi: any, amazonQEnabled: boolean, featureConfigsSerialized: [string, FeatureContext][], showWelcomePage: boolean, + disclaimerAcknowledged: boolean, disabledCommands?: string[] ) => { + let disclaimerCardActive = !disclaimerAcknowledged // eslint-disable-next-line prefer-const let mynahUI: MynahUI // eslint-disable-next-line prefer-const @@ -542,6 +545,7 @@ export const createMynahUI = ( // make sure to show/hide it accordingly mynahUI.updateStore(tabID, { quickActionCommands: tabDataGenerator.quickActionsGenerator.generateForTab('unknown'), + ...(disclaimerCardActive ? { promptInputStickyCard: disclaimerCard } : {}), }) connector.onTabAdd(tabID) }, @@ -609,43 +613,64 @@ export const createMynahUI = ( }, onVote: connector.onChatItemVoted, onInBodyButtonClicked: (tabId, messageId, action, eventId) => { - if (action.id === 'quick-start') { - /** - * quick start is the action on the welcome page. When its - * clicked it collapses the view and puts it into regular - * "chat" which is cwc - */ - tabsStorage.updateTabTypeFromUnknown(tabId, 'cwc') - - // show quick start in the current tab instead of a new one - mynahUI.updateStore(tabId, { - tabHeaderDetails: undefined, - compactMode: false, - tabBackground: false, - promptInputText: '/', - promptInputLabel: undefined, - chatItems: [], - }) + switch (action.id) { + case disclaimerAcknowledgeButtonId: { + disclaimerCardActive = false - ideApi.postMessage(createClickTelemetry('amazonq-welcome-quick-start-button')) - return - } + // post message to tell VSCode that disclaimer is acknowledged + ideApi.postMessage({ + command: 'disclaimer-acknowledged', + }) - if (action.id === 'explore') { - const newTabId = mynahUI.updateStore('', agentWalkthroughDataModel) - if (newTabId === undefined) { - mynahUI.notify({ - content: uiComponentsTexts.noMoreTabsTooltip, - type: NotificationType.WARNING, + // create telemetry + ideApi.postMessage(createClickTelemetry('amazonq-disclaimer-acknowledge-button')) + + // remove all disclaimer cards from all tabs + Object.keys(mynahUI.getAllTabs()).forEach((storeTabKey) => { + // eslint-disable-next-line unicorn/no-null + mynahUI.updateStore(storeTabKey, { promptInputStickyCard: null }) }) return } - tabsStorage.updateTabTypeFromUnknown(newTabId, 'agentWalkthrough') - ideApi.postMessage(createClickTelemetry('amazonq-welcome-explore-button')) - return - } + case 'quick-start': { + /** + * quick start is the action on the welcome page. When its + * clicked it collapses the view and puts it into regular + * "chat" which is cwc + */ + tabsStorage.updateTabTypeFromUnknown(tabId, 'cwc') + + // show quick start in the current tab instead of a new one + mynahUI.updateStore(tabId, { + tabHeaderDetails: undefined, + compactMode: false, + tabBackground: false, + promptInputText: '/', + promptInputLabel: undefined, + chatItems: [], + }) - connector.onCustomFormAction(tabId, messageId, action, eventId) + ideApi.postMessage(createClickTelemetry('amazonq-welcome-quick-start-button')) + return + } + case 'explore': { + const newTabId = mynahUI.updateStore('', agentWalkthroughDataModel) + if (newTabId === undefined) { + mynahUI.notify({ + content: uiComponentsTexts.noMoreTabsTooltip, + type: NotificationType.WARNING, + }) + return + } + tabsStorage.updateTabTypeFromUnknown(newTabId, 'agentWalkthrough') + ideApi.postMessage(createClickTelemetry('amazonq-welcome-explore-button')) + return + } + default: { + connector.onCustomFormAction(tabId, messageId, action, eventId) + return + } + } }, onCustomFormAction: (tabId, action, eventId) => { connector.onCustomFormAction(tabId, undefined, action, eventId) @@ -786,9 +811,12 @@ export const createMynahUI = ( tabs: { 'tab-1': { isSelected: true, - store: showWelcomePage - ? welcomeScreenTabData(tabDataGenerator).store - : tabDataGenerator.getTabData('cwc', true), + store: { + ...(showWelcomePage + ? welcomeScreenTabData(tabDataGenerator).store + : tabDataGenerator.getTabData('cwc', true)), + ...(disclaimerCardActive ? { promptInputStickyCard: disclaimerCard } : {}), + }, }, }, defaults: { diff --git a/packages/core/src/amazonq/webview/ui/tabs/generator.ts b/packages/core/src/amazonq/webview/ui/tabs/generator.ts index 8865d00e400..b3263218c1d 100644 --- a/packages/core/src/amazonq/webview/ui/tabs/generator.ts +++ b/packages/core/src/amazonq/webview/ui/tabs/generator.ts @@ -47,7 +47,7 @@ export class TabDataGenerator { const tabData: MynahUIDataModel = { tabTitle: taskName ?? TabTypeDataMap[tabType].title, promptInputInfo: - 'Amazon Q Developer uses generative AI. You may need to verify responses. See the [AWS Responsible AI Policy](https://aws.amazon.com/machine-learning/responsible-ai/policy/). Amazon Q Developer processes data across all US Regions. See [here](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/cross-region-inference.html) for more info. Amazon Q may retain chats to provide and maintain the service.', + 'Amazon Q Developer uses generative AI. You may need to verify responses. See the [AWS Responsible AI Policy](https://aws.amazon.com/machine-learning/responsible-ai/policy/).', quickActionCommands: this.quickActionsGenerator.generateForTab(tabType), promptInputPlaceholder: TabTypeDataMap[tabType].placeholder, contextCommands: TabTypeDataMap[tabType].contextCommands, diff --git a/packages/core/src/amazonq/webview/ui/texts/disclaimer.ts b/packages/core/src/amazonq/webview/ui/texts/disclaimer.ts new file mode 100644 index 00000000000..fc666796272 --- /dev/null +++ b/packages/core/src/amazonq/webview/ui/texts/disclaimer.ts @@ -0,0 +1,20 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ChatItem, MynahIcons } from '@aws/mynah-ui' + +export const disclaimerAcknowledgeButtonId = 'amazonq-disclaimer-acknowledge-button-id' +export const disclaimerCard: Partial = { + messageId: 'amazonq-disclaimer-card', + body: 'Amazon Q Developer uses generative AI. You may need to verify responses. See the [AWS Responsible AI Policy](https://aws.amazon.com/machine-learning/responsible-ai/policy/). Amazon Q Developer processes data across all US Regions. See [here](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/cross-region-inference.html) for more info. Amazon Q may retain chats to provide and maintain the service.', + buttons: [ + { + text: 'Acknowledge', + id: disclaimerAcknowledgeButtonId, + status: 'info', + icon: MynahIcons.OK, + }, + ], +} diff --git a/packages/core/src/shared/globalState.ts b/packages/core/src/shared/globalState.ts index bbdb03b7cd5..5cce9ff6f84 100644 --- a/packages/core/src/shared/globalState.ts +++ b/packages/core/src/shared/globalState.ts @@ -34,6 +34,7 @@ export type globalKey = | 'aws.amazonq.securityIssueFilters' | 'aws.amazonq.notifications' | 'aws.amazonq.welcomeChatShowCount' + | 'aws.amazonq.disclaimerAcknowledged' | 'aws.notifications' | 'aws.notifications.dev' // keys to store notifications for testing | 'aws.downloadPath'