diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQPanel.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQPanel.kt index 75cf419c23d..92e81d376b1 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQPanel.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQPanel.kt @@ -27,6 +27,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.apps.AppConnection import software.aws.toolkits.jetbrains.services.amazonq.commands.MessageTypeRegistry import software.aws.toolkits.jetbrains.services.amazonq.lsp.artifacts.ArtifactManager import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AsyncChatUiListener +import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.FlareUiMessage import software.aws.toolkits.jetbrains.services.amazonq.messages.AmazonQMessage import software.aws.toolkits.jetbrains.services.amazonq.messages.MessageConnector import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager @@ -55,8 +56,12 @@ class AmazonQPanel(val project: Project, private val scope: CoroutineScope) : Di project.messageBus.connect().subscribe( AsyncChatUiListener.TOPIC, object : AsyncChatUiListener { - override fun onChange(message: String) { - browser.get()?.postChat(message) + override fun onChange(command: String) { + browser.get()?.postChat(command) + } + + override fun onChange(command: FlareUiMessage) { + browser.get()?.postChat(command) } } ) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt index 2c456ea84c8..91975a72284 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt @@ -1,9 +1,10 @@ // Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 - +@file:Suppress("BannedImports") package software.aws.toolkits.jetbrains.services.amazonq.webview import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.google.gson.Gson import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer @@ -12,6 +13,7 @@ import org.cef.CefApp import software.aws.toolkits.jetbrains.services.amazonq.CodeWhispererFeatureConfigService import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AwsServerCapabilitiesProvider +import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.FlareUiMessage import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfile import software.aws.toolkits.jetbrains.services.amazonq.util.HighlightCommand import software.aws.toolkits.jetbrains.services.amazonq.util.createBrowser @@ -63,6 +65,9 @@ class Browser(parent: Disposable, private val webUri: URI, val project: Project) fun component() = jcefBrowser.component + fun postChat(command: FlareUiMessage) = postChat(Gson().toJson(command)) + + @Deprecated("shouldn't need this version") fun postChat(message: String) { jcefBrowser .cefBrowser diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt index e1d0e775316..d44a33b51e9 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/BrowserConnector.kt @@ -34,6 +34,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService import software.aws.toolkits.jetbrains.services.amazonq.lsp.encryption.JwtEncryptionManager import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AwsServerCapabilitiesProvider import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.ChatCommunicationManager +import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.FlareUiMessage import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.AUTH_FOLLOW_UP_CLICKED import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.AuthFollowUpClickNotification import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ButtonClickNotification @@ -254,7 +255,9 @@ class BrowserConnector( var encryptionManager: JwtEncryptionManager? = null val result = AmazonQLspService.executeIfRunning(project) { server -> encryptionManager = this.encryptionManager - encryptionManager?.encrypt(chatParams)?.let { EncryptedChatParams(it, partialResultToken) }?.let { server.sendChatPrompt(it) } + + val encryptedParams = EncryptedChatParams(this.encryptionManager.encrypt(chatParams), partialResultToken) + server.sendChatPrompt(encryptedParams) } ?: (CompletableFuture.failedFuture(IllegalStateException("LSP Server not running"))) // We assume there is only one outgoing request per tab because the input is @@ -270,11 +273,9 @@ class BrowserConnector( var encryptionManager: JwtEncryptionManager? = null val result = AmazonQLspService.executeIfRunning(project) { server -> encryptionManager = this.encryptionManager - encryptionManager?.encrypt(quickActionParams)?.let { - EncryptedQuickActionChatParams(it, partialResultToken) - }?.let { - server.sendQuickAction(it) - } + + val encryptedParams = EncryptedQuickActionChatParams(this.encryptionManager.encrypt(quickActionParams), partialResultToken) + server.sendQuickAction(encryptedParams) } ?: (CompletableFuture.failedFuture(IllegalStateException("LSP Server not running"))) // We assume there is only one outgoing request per tab because the input is @@ -429,13 +430,18 @@ class BrowserConnector( handleChatNotification(node) { server, params -> val result = server.tabBarActions(params) - result.whenComplete { params1, error -> + result.whenComplete { actions, error -> try { if (error != null) { throw error } - val res = ChatCommunicationManager.convertNotificationToJsonForChat(CHAT_TAB_BAR_ACTIONS, params1) - browser.postChat(res) + + browser.postChat( + FlareUiMessage( + command = CHAT_TAB_BAR_ACTIONS, + params = actions ?: emptyMap() + ) + ) } catch (e: Exception) { LOG.error { "Failed to perform chat tab bar action $e" } params.tabId?.let { diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt index aecf8692113..8da47a7d33a 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLanguageClientImpl.kt @@ -3,7 +3,6 @@ @file:Suppress("BannedImports") package software.aws.toolkits.jetbrains.services.amazonq.lsp -import com.google.gson.Gson import com.intellij.diff.DiffContentFactory import com.intellij.diff.DiffManager import com.intellij.diff.DiffManagerEx @@ -26,6 +25,7 @@ import org.eclipse.lsp4j.PublishDiagnosticsParams import org.eclipse.lsp4j.ShowDocumentParams import org.eclipse.lsp4j.ShowDocumentResult import org.eclipse.lsp4j.ShowMessageRequestParams +import software.aws.toolkits.core.utils.error import software.aws.toolkits.core.utils.getLogger import software.aws.toolkits.core.utils.warn import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection @@ -33,6 +33,7 @@ import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AsyncChatUiListener import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.ChatCommunicationManager +import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.FlareUiMessage import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.LSPAny import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_OPEN_TAB import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_SEND_CONTEXT_COMMANDS @@ -138,14 +139,13 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC val result = CompletableFuture() ChatCommunicationManager.pendingTabRequests[requestId] = result - val uiMessage = """ - { - "command": "$CHAT_OPEN_TAB", - "params": ${Gson().toJson(params)}, - "requestId": "$requestId" - } - """.trimIndent() - AsyncChatUiListener.notifyPartialMessageUpdate(uiMessage) + AsyncChatUiListener.notifyPartialMessageUpdate( + FlareUiMessage( + command = CHAT_OPEN_TAB, + params = params, + requestId = requestId, + ) + ) result.orTimeout(30000, TimeUnit.MILLISECONDS) .whenComplete { _, error -> @@ -191,14 +191,13 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC ChatCommunicationManager.pendingSerializedChatRequests[requestId] = result - val uiMessage = """ - { - "command": "$GET_SERIALIZED_CHAT_REQUEST_METHOD", - "params": ${Gson().toJson(params)}, - "requestId": "$requestId" - } - """.trimIndent() - AsyncChatUiListener.notifyPartialMessageUpdate(uiMessage) + AsyncChatUiListener.notifyPartialMessageUpdate( + FlareUiMessage( + command = GET_SERIALIZED_CHAT_REQUEST_METHOD, + params = params, + requestId = requestId, + ) + ) result.orTimeout(30000, TimeUnit.MILLISECONDS) .whenComplete { _, error -> @@ -257,19 +256,17 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC try { chatCommunicationManager.handlePartialResultProgressNotification(project, params) } catch (e: Exception) { - error("Cannot handle partial chat") + LOG.error(e) { "Cannot handle partial chat" } } } override fun sendChatUpdate(params: ChatUpdateParams): CompletableFuture { - val uiMessage = """ - { - "command":"$CHAT_SEND_UPDATE", - "params":${Gson().toJson(params)} - } - """.trimIndent() - - AsyncChatUiListener.notifyPartialMessageUpdate(uiMessage) + AsyncChatUiListener.notifyPartialMessageUpdate( + FlareUiMessage( + command = CHAT_SEND_UPDATE, + params = params, + ) + ) return CompletableFuture.completedFuture(Unit) } @@ -343,14 +340,12 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC ) override fun sendContextCommands(params: LSPAny): CompletableFuture { - val showContextCommands = """ - { - "command":"$CHAT_SEND_CONTEXT_COMMANDS", - "params": ${Gson().toJson(params)} - } - """.trimIndent() - - AsyncChatUiListener.notifyPartialMessageUpdate(showContextCommands) + AsyncChatUiListener.notifyPartialMessageUpdate( + FlareUiMessage( + command = CHAT_SEND_CONTEXT_COMMANDS, + params = params ?: error("received empty payload for $CHAT_SEND_CONTEXT_COMMANDS"), + ) + ) return CompletableFuture.completedFuture(Unit) } diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AsyncChatUiListener.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AsyncChatUiListener.kt index 9aa538be924..c6992526ec9 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AsyncChatUiListener.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/AsyncChatUiListener.kt @@ -8,14 +8,22 @@ import com.intellij.util.messages.Topic import java.util.EventListener interface AsyncChatUiListener : EventListener { - fun onChange(message: String) {} + @Deprecated("shouldn't need this version") + fun onChange(command: String) {} + + fun onChange(command: FlareUiMessage) {} companion object { @Topic.AppLevel val TOPIC = Topic.create("Partial chat message provider", AsyncChatUiListener::class.java) - fun notifyPartialMessageUpdate(message: String) { - ApplicationManager.getApplication().messageBus.syncPublisher(TOPIC).onChange(message) + fun notifyPartialMessageUpdate(command: FlareUiMessage) { + ApplicationManager.getApplication().messageBus.syncPublisher(TOPIC).onChange(command) + } + + @Deprecated("shouldn't need this version") + fun notifyPartialMessageUpdate(command: String) { + ApplicationManager.getApplication().messageBus.syncPublisher(TOPIC).onChange(command) } } } diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/ChatCommunicationManager.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/ChatCommunicationManager.kt index 249e7c65b19..2fdb765ec53 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/ChatCommunicationManager.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/ChatCommunicationManager.kt @@ -154,13 +154,5 @@ class ChatCommunicationManager { fun completeTabOpen(requestId: String, tabId: String) { pendingTabRequests.remove(requestId)?.complete(OpenTabResult(tabId)) } - - inline fun convertNotificationToJsonForChat(command: String, params: T? = null) = - """ - { - "command":"$command", - "params": ${if (params != null) Gson().toJson(params) else "{}"} - } - """.trimIndent() } } diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/FlareUiMessage.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/FlareUiMessage.kt new file mode 100644 index 00000000000..7bc5fe2461f --- /dev/null +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/FlareUiMessage.kt @@ -0,0 +1,10 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat + +data class FlareUiMessage( + val command: String, + val params: Any, + val requestId: String? = null, +)