Skip to content

Commit da5ba25

Browse files
authored
feat(amazonq): Integrate named agents with chat (#5674)
* Integrate named agents with chat * address feedback
1 parent e3082d2 commit da5ba25

File tree

14 files changed

+1197
-802
lines changed

14 files changed

+1197
-802
lines changed

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,33 @@ class Browser(parent: Disposable, private val webUri: URI, val project: Project)
114114
highlightCommand: HighlightCommand?,
115115
activeProfile: QRegionProfile?,
116116
): String {
117-
val quickActionConfig = generateQuickActionConfig()
118117
val postMessageToJavaJsCode = receiveMessageQuery.inject("JSON.stringify(message)")
118+
val connectorAdapterPath = "http://mynah/js/connectorAdapter.js"
119+
generateQuickActionConfig()
119120
// language=HTML
120121
val jsScripts = """
122+
<script type="text/javascript" src="$connectorAdapterPath"></script>
121123
<script type="text/javascript" src="$webUri" defer onload="init()"></script>
124+
122125
<script type="text/javascript">
126+
123127
const init = () => {
128+
const hybridChatConnector = connectorAdapter.initiateAdapter(
129+
${MeetQSettings.getInstance().reinvent2024OnboardingCount < MAX_ONBOARDING_PAGE_COUNT},
130+
${MeetQSettings.getInstance().disclaimerAcknowledged},
131+
$isFeatureDevAvailable,
132+
$isCodeTransformAvailable,
133+
$isDocAvailable,
134+
$isCodeScanAvailable,
135+
$isCodeTestAvailable,
136+
{
137+
postMessage: message => {
138+
$postMessageToJavaJsCode
139+
}
140+
},
141+
142+
"${activeProfile?.profileName.orEmpty()}")
143+
const commands = [hybridChatConnector.initialQuickActions[0], hybridChatConnector.initialQuickActions[1]]
124144
amazonQChat.createChat(
125145
{
126146
postMessage: message => {
@@ -129,11 +149,11 @@ class Browser(parent: Disposable, private val webUri: URI, val project: Project)
129149
},
130150
{
131151
agenticMode: true,
132-
quickActionCommands: $quickActionConfig,
152+
quickActionCommands: commands,
133153
disclaimerAcknowledged: ${MeetQSettings.getInstance().disclaimerAcknowledged},
134154
pairProgrammingAcknowledged: ${!MeetQSettings.getInstance().amazonQChatPairProgramming}
135155
},
136-
null,
156+
hybridChatConnector,
137157
{}
138158
139159
);

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,13 @@ class BrowserConnector(
128128
.onEach { json ->
129129
val node = serializer.toNode(json)
130130
when (node.command) {
131+
// this is sent when the named agents UI is ready
132+
"ui-is-ready" -> {
133+
uiReady.complete(true)
134+
RunOnceUtil.runOnceForApp("AmazonQ-UI-Ready") {
135+
MeetQSettings.getInstance().reinvent2024OnboardingCount += 1
136+
}
137+
}
131138
CHAT_DISCLAIMER_ACKNOWLEDGED -> {
132139
MeetQSettings.getInstance().disclaimerAcknowledged = true
133140
}
@@ -156,13 +163,14 @@ class BrowserConnector(
156163
}
157164

158165
val tabType = node.tabType
159-
if (tabType == null) {
166+
if (tabType == null || tabType == "cwc") {
160167
handleFlareChatMessages(browser, node)
161-
}
162-
connections.filter { connection -> connection.app.tabTypes.contains(tabType) }.forEach { connection ->
163-
launch {
164-
val message = serializer.deserialize(node, connection.messageTypeRegistry)
165-
connection.messagesFromUiToApp.publish(message)
168+
} else {
169+
connections.filter { connection -> connection.app.tabTypes.contains(tabType) }.forEach { connection ->
170+
launch {
171+
val message = serializer.deserialize(node, connection.messageTypeRegistry)
172+
connection.messagesFromUiToApp.publish(message)
173+
}
166174
}
167175
}
168176
}
@@ -182,7 +190,7 @@ class BrowserConnector(
182190
// Send inbound messages to the browser
183191
val inboundMessages = connections.map { it.messagesFromAppToUi.flow }.merge()
184192
inboundMessages
185-
.onEach { browser.post(serializer.serialize(it)) }
193+
.onEach { browser.postChat(serializer.serialize(it)) }
186194
.launchIn(this)
187195
}
188196

plugins/amazonq/mynah-ui/package-lock.json

Lines changed: 76 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

plugins/amazonq/mynah-ui/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"lintfix": "eslint -c .eslintrc.js --fix --ext .ts ."
1313
},
1414
"dependencies": {
15-
"@aws/mynah-ui-chat": "npm:@aws/mynah-ui@4.22.1",
15+
"@aws/mynah-ui-chat": "npm:@aws/mynah-ui@4.30.3",
1616
"@types/node": "^14.18.5",
1717
"fs-extra": "^10.0.1",
1818
"sanitize-html": "^2.12.1",
@@ -22,6 +22,7 @@
2222
},
2323
"devDependencies": {
2424
"@aws/fully-qualified-names": "^2.1.1",
25+
"@aws/chat-client": "^0.1.4",
2526
"@types/sanitize-html": "^2.8.0",
2627
"@typescript-eslint/eslint-plugin": "^5.38.0",
2728
"@typescript-eslint/parser": "^5.38.0",
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
import {ChatPrompt, MynahUI, QuickActionCommand, QuickActionCommandGroup} from '@aws/mynah-ui-chat'
6+
import { isTabType } from './ui/storages/tabsStorage'
7+
import { WebviewUIHandler } from './ui/main'
8+
import { TabDataGenerator } from './ui/tabs/generator'
9+
import { ChatClientAdapter, ChatEventHandler } from '@aws/chat-client'
10+
import { FqnExtractor } from "./fqn/extractor";
11+
12+
export * from "./ui/main";
13+
14+
declare global {
15+
interface Window { fqnExtractor: FqnExtractor; }
16+
}
17+
18+
window.fqnExtractor = new FqnExtractor();
19+
20+
export const initiateAdapter = (showWelcomePage: boolean,
21+
disclaimerAcknowledged: boolean,
22+
isFeatureDevEnabled: boolean,
23+
isCodeTransformEnabled: boolean,
24+
isDocEnabled: boolean,
25+
isCodeScanEnabled: boolean,
26+
isCodeTestEnabled: boolean,
27+
ideApiPostMessage: (message: any) => void,
28+
profileName?: string) : HybridChatAdapter => {
29+
return new HybridChatAdapter(showWelcomePage, disclaimerAcknowledged, isFeatureDevEnabled, isCodeTransformEnabled, isDocEnabled, isCodeScanEnabled, isCodeTestEnabled, ideApiPostMessage, profileName)
30+
}
31+
32+
33+
// Ref: https://github.com/aws/aws-toolkit-vscode/blob/e9ea8082ffe0b9968a873437407d0b6b31b9e1a5/packages/core/src/amazonq/webview/ui/connectorAdapter.ts#L14
34+
export class HybridChatAdapter implements ChatClientAdapter {
35+
private uiHandler?: WebviewUIHandler
36+
37+
private mynahUIRef?: { mynahUI: MynahUI}
38+
39+
constructor(
40+
41+
private showWelcomePage: boolean,
42+
private disclaimerAcknowledged: boolean,
43+
private isFeatureDevEnabled: boolean,
44+
private isCodeTransformEnabled: boolean,
45+
private isDocEnabled: boolean,
46+
private isCodeScanEnabled: boolean,
47+
private isCodeTestEnabled: boolean,
48+
private ideApiPostMessage: (message: any) => void,
49+
private profileName?: string,
50+
51+
) {}
52+
53+
/**
54+
* First we create the ui handler to get the props, then once mynah UI gets created flare will re-inject the
55+
* mynah UI instance on the hybrid chat adapter
56+
*/
57+
createChatEventHandler(mynahUIRef: { mynahUI: MynahUI }): ChatEventHandler {
58+
this.mynahUIRef = mynahUIRef
59+
60+
this.uiHandler = new WebviewUIHandler({
61+
postMessage: this.ideApiPostMessage,
62+
mynahUIRef: this.mynahUIRef,
63+
showWelcomePage: this.showWelcomePage,
64+
disclaimerAcknowledged: this.disclaimerAcknowledged,
65+
isFeatureDevEnabled: this.isFeatureDevEnabled,
66+
isCodeTransformEnabled: this.isCodeTransformEnabled,
67+
isDocEnabled: this.isDocEnabled,
68+
isCodeScanEnabled: this.isCodeScanEnabled,
69+
isCodeTestEnabled: this.isCodeTestEnabled,
70+
profileName: this.profileName,
71+
hybridChat: true,
72+
})
73+
74+
return this.uiHandler.mynahUIProps
75+
}
76+
77+
isSupportedTab(tabId: string): boolean {
78+
const tabType = this.uiHandler?.tabsStorage.getTab(tabId)?.type
79+
if (!tabType) {
80+
return false
81+
}
82+
return isTabType(tabType) && tabType !== 'cwc'
83+
}
84+
85+
async handleMessageReceive(message: MessageEvent): Promise<void> {
86+
if (this.uiHandler) {
87+
return this.uiHandler?.connector?.handleMessageReceive(message)
88+
}
89+
90+
console.error('unknown message: ', message.data)
91+
}
92+
93+
isSupportedQuickAction(command: string): boolean {
94+
return (
95+
command === '/dev' ||
96+
command === '/test' ||
97+
command === '/review' ||
98+
command === '/doc' ||
99+
command === '/transform'
100+
)
101+
}
102+
103+
handleQuickAction(prompt: ChatPrompt, tabId: string, eventId: string | undefined): void {
104+
return this.uiHandler?.quickActionHandler?.handleCommand(prompt, tabId, eventId)
105+
}
106+
107+
get initialQuickActions(): QuickActionCommandGroup[] {
108+
const tabDataGenerator = new TabDataGenerator({
109+
isFeatureDevEnabled: this.isFeatureDevEnabled,
110+
isCodeTransformEnabled: this.isCodeTransformEnabled,
111+
isDocEnabled: this.isDocEnabled,
112+
isCodeScanEnabled: this.isCodeScanEnabled,
113+
isCodeTestEnabled: this.isCodeTestEnabled,
114+
profileName: this.profileName
115+
})
116+
return tabDataGenerator.quickActionsGenerator.generateForTab('cwc') ?? []
117+
}
118+
}
119+
120+

plugins/amazonq/mynah-ui/src/mynah-ui/fqn/extractor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*!
2-
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

plugins/amazonq/mynah-ui/src/mynah-ui/fqn/java-import-reader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*!
2-
* Copyright 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

plugins/amazonq/mynah-ui/src/mynah-ui/ui/connector.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export class Connector {
109109
private readonly tabsStorage
110110
private readonly amazonqCommonsConnector: AmazonQCommonsConnector
111111

112-
private isUIReady = false
112+
isUIReady = false
113113

114114
constructor(props: ConnectorProps) {
115115
this.sendMessageToExtension = props.sendMessageToExtension

0 commit comments

Comments
 (0)