From 2e70fc384263521e4fb50cd022615e1329e5db40 Mon Sep 17 00:00:00 2001
From: Richard Li <742829+rli@users.noreply.github.com>
Date: Tue, 3 Jun 2025 11:07:18 -0700
Subject: [PATCH 01/10] feat(amazonq): expose 'Manage Subscriptions' action for
builder id paid tier (#5777)
---
.../META-INF/plugin-codewhisperer.xml | 2 +
.../explorer/QStatusBarLoggedInActionGroup.kt | 23 ++++----
.../resources/AmazonQBundle.properties | 1 +
.../actions/ManageSubscription.kt | 58 +++++++++++++++++++
4 files changed, 73 insertions(+), 11 deletions(-)
create mode 100644 plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/actions/ManageSubscription.kt
diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/resources/META-INF/plugin-codewhisperer.xml b/plugins/amazonq/codewhisperer/jetbrains-community/resources/META-INF/plugin-codewhisperer.xml
index 6a2088d39c0..7fdcf371ad4 100644
--- a/plugins/amazonq/codewhisperer/jetbrains-community/resources/META-INF/plugin-codewhisperer.xml
+++ b/plugins/amazonq/codewhisperer/jetbrains-community/resources/META-INF/plugin-codewhisperer.xml
@@ -139,5 +139,7 @@
class="software.aws.toolkits.jetbrains.services.cwc.commands.codescan.actions.CodeScanCompleteAction" />
+
diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/explorer/QStatusBarLoggedInActionGroup.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/explorer/QStatusBarLoggedInActionGroup.kt
index 3dd88d45d22..53f6fe82b2d 100644
--- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/explorer/QStatusBarLoggedInActionGroup.kt
+++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/explorer/QStatusBarLoggedInActionGroup.kt
@@ -12,15 +12,12 @@ import com.intellij.openapi.project.Project
import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
import software.aws.toolkits.jetbrains.core.credentials.actions.SsoLogoutAction
-import software.aws.toolkits.jetbrains.core.credentials.pinning.CodeWhispererConnection
import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection
import software.aws.toolkits.jetbrains.core.credentials.sono.isSono
-import software.aws.toolkits.jetbrains.services.amazonq.actions.QSwitchProfilesAction
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager
import software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererConnectOnGithubAction
import software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererLearnMoreAction
import software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererProvideFeedbackAction
-import software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererShowSettingsAction
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.actions.CodeWhispererCodeScanRunAction
import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.actions.ActionProvider
import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.actions.Customize
@@ -54,6 +51,7 @@ class QStatusBarLoggedInActionGroup : DefaultActionGroup() {
override fun getChildren(e: AnActionEvent?) = e?.project?.let {
val isPendingActiveProfile = QRegionProfileManager.getInstance().hasValidConnectionButNoActiveProfile(it)
+ val actionManager = ActionManager.getInstance()
buildList {
if (!isPendingActiveProfile) {
addAll(buildActionListForActiveProfileSelected(it, actionProvider))
@@ -64,15 +62,18 @@ class QStatusBarLoggedInActionGroup : DefaultActionGroup() {
addAll(buildActionListForConnectHelp(actionProvider))
add(Separator.create())
- add(CodeWhispererShowSettingsAction())
- (
- ToolkitConnectionManager.getInstance(it).activeConnectionForFeature(QConnection.getInstance()) as? AwsBearerTokenConnection
- )?.takeIf { !it.isSono() }
- ?.let { add(QSwitchProfilesAction()) }
- ToolkitConnectionManager.getInstance(it).activeConnectionForFeature(CodeWhispererConnection.getInstance())?.let { c ->
- (c as? AwsBearerTokenConnection)?.let { connection ->
- add(SsoLogoutAction(connection))
+ add(actionManager.getAction("codewhisperer.settings"))
+
+ val connection = ToolkitConnectionManager.getInstance(it).activeConnectionForFeature(QConnection.getInstance()) as? AwsBearerTokenConnection
+
+ if (connection != null) {
+ if (!connection.isSono()) {
+ add(actionManager.getAction("codewhisperer.switchProfiles"))
+ } else {
+ add(actionManager.getAction("q.manage.subscription"))
}
+
+ add(SsoLogoutAction(connection))
}
}.toTypedArray()
}.orEmpty()
diff --git a/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties b/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties
index ab7face28d9..2a1fb1068ed 100644
--- a/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties
+++ b/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties
@@ -9,6 +9,7 @@ amazonqInlineChat.popup.title=Enter Instructions for Q
amazonq.refresh.panel=Refresh Chat Session
amazonq.title=Amazon Q
amazonq.workspace.settings.open.prompt=Workspace index is now enabled. You can disable it from Amazon Q settings.
+action.q.manage.subscription.text=Manage Subscription
action.q.profile.usage.text=You changed your profile
action.q.profile.usage=You''re using the ''{0}'' profile for Amazon Q.
action.q.switchProfiles.text=Change Profile
diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/actions/ManageSubscription.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/actions/ManageSubscription.kt
new file mode 100644
index 00000000000..10568619c64
--- /dev/null
+++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/actions/ManageSubscription.kt
@@ -0,0 +1,58 @@
+// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+package software.aws.toolkits.jetbrains.services.codewhisperer.actions
+
+import com.intellij.openapi.actionSystem.ActionUpdateThread
+import com.intellij.openapi.actionSystem.AnAction
+import com.intellij.openapi.actionSystem.AnActionEvent
+import com.intellij.openapi.progress.currentThreadCoroutineScope
+import com.intellij.openapi.project.DumbAware
+import kotlinx.coroutines.future.await
+import kotlinx.coroutines.launch
+import org.eclipse.lsp4j.ExecuteCommandParams
+import software.aws.toolkits.core.utils.error
+import software.aws.toolkits.core.utils.getLogger
+import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection
+import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
+import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection
+import software.aws.toolkits.jetbrains.core.credentials.sono.isSono
+import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
+
+class ManageSubscription : AnAction(), DumbAware {
+ override fun getActionUpdateThread() = ActionUpdateThread.BGT
+
+ override fun update(e: AnActionEvent) {
+ val project = e.project
+ // disable if user is IdC
+ if (project == null) {
+ e.presentation.isEnabledAndVisible = false
+ } else {
+ val connection = ToolkitConnectionManager.getInstance(project)
+ .activeConnectionForFeature(QConnection.getInstance()) as? AwsBearerTokenConnection
+
+ e.presentation.isEnabledAndVisible = connection.isSono()
+ }
+ }
+
+ override fun actionPerformed(e: AnActionEvent) {
+ val project = e.project ?: return
+ currentThreadCoroutineScope().launch {
+ AmazonQLspService.getInstance(project).execute { lsp ->
+ lsp.workspaceService.executeCommand(
+ ExecuteCommandParams().apply {
+ this.command = "aws/chat/manageSubscription"
+ }
+ )
+ }.handleAsync { _, ex ->
+ if (ex != null) {
+ LOG.error(ex) { "Failed aws/chat/manageSubscription" }
+ }
+ }.await()
+ }
+ }
+
+ companion object {
+ private val LOG = getLogger()
+ }
+}
From 4b833652547c855fbb76d6be03c6395e691a69c6 Mon Sep 17 00:00:00 2001
From: Richard Li
Date: Tue, 3 Jun 2025 11:10:15 -0700
Subject: [PATCH 02/10] Revert "feat(amazonq): expose 'Manage Subscriptions'
action for builder id paid tier (#5777)"
This reverts commit 2e70fc384263521e4fb50cd022615e1329e5db40.
---
.../META-INF/plugin-codewhisperer.xml | 2 -
.../explorer/QStatusBarLoggedInActionGroup.kt | 23 ++++----
.../resources/AmazonQBundle.properties | 1 -
.../actions/ManageSubscription.kt | 58 -------------------
4 files changed, 11 insertions(+), 73 deletions(-)
delete mode 100644 plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/actions/ManageSubscription.kt
diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/resources/META-INF/plugin-codewhisperer.xml b/plugins/amazonq/codewhisperer/jetbrains-community/resources/META-INF/plugin-codewhisperer.xml
index 7fdcf371ad4..6a2088d39c0 100644
--- a/plugins/amazonq/codewhisperer/jetbrains-community/resources/META-INF/plugin-codewhisperer.xml
+++ b/plugins/amazonq/codewhisperer/jetbrains-community/resources/META-INF/plugin-codewhisperer.xml
@@ -139,7 +139,5 @@
class="software.aws.toolkits.jetbrains.services.cwc.commands.codescan.actions.CodeScanCompleteAction" />
-
diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/explorer/QStatusBarLoggedInActionGroup.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/explorer/QStatusBarLoggedInActionGroup.kt
index 53f6fe82b2d..3dd88d45d22 100644
--- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/explorer/QStatusBarLoggedInActionGroup.kt
+++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/explorer/QStatusBarLoggedInActionGroup.kt
@@ -12,12 +12,15 @@ import com.intellij.openapi.project.Project
import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
import software.aws.toolkits.jetbrains.core.credentials.actions.SsoLogoutAction
+import software.aws.toolkits.jetbrains.core.credentials.pinning.CodeWhispererConnection
import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection
import software.aws.toolkits.jetbrains.core.credentials.sono.isSono
+import software.aws.toolkits.jetbrains.services.amazonq.actions.QSwitchProfilesAction
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager
import software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererConnectOnGithubAction
import software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererLearnMoreAction
import software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererProvideFeedbackAction
+import software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererShowSettingsAction
import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.actions.CodeWhispererCodeScanRunAction
import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.actions.ActionProvider
import software.aws.toolkits.jetbrains.services.codewhisperer.explorer.actions.Customize
@@ -51,7 +54,6 @@ class QStatusBarLoggedInActionGroup : DefaultActionGroup() {
override fun getChildren(e: AnActionEvent?) = e?.project?.let {
val isPendingActiveProfile = QRegionProfileManager.getInstance().hasValidConnectionButNoActiveProfile(it)
- val actionManager = ActionManager.getInstance()
buildList {
if (!isPendingActiveProfile) {
addAll(buildActionListForActiveProfileSelected(it, actionProvider))
@@ -62,18 +64,15 @@ class QStatusBarLoggedInActionGroup : DefaultActionGroup() {
addAll(buildActionListForConnectHelp(actionProvider))
add(Separator.create())
- add(actionManager.getAction("codewhisperer.settings"))
-
- val connection = ToolkitConnectionManager.getInstance(it).activeConnectionForFeature(QConnection.getInstance()) as? AwsBearerTokenConnection
-
- if (connection != null) {
- if (!connection.isSono()) {
- add(actionManager.getAction("codewhisperer.switchProfiles"))
- } else {
- add(actionManager.getAction("q.manage.subscription"))
+ add(CodeWhispererShowSettingsAction())
+ (
+ ToolkitConnectionManager.getInstance(it).activeConnectionForFeature(QConnection.getInstance()) as? AwsBearerTokenConnection
+ )?.takeIf { !it.isSono() }
+ ?.let { add(QSwitchProfilesAction()) }
+ ToolkitConnectionManager.getInstance(it).activeConnectionForFeature(CodeWhispererConnection.getInstance())?.let { c ->
+ (c as? AwsBearerTokenConnection)?.let { connection ->
+ add(SsoLogoutAction(connection))
}
-
- add(SsoLogoutAction(connection))
}
}.toTypedArray()
}.orEmpty()
diff --git a/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties b/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties
index 2a1fb1068ed..ab7face28d9 100644
--- a/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties
+++ b/plugins/amazonq/shared/jetbrains-community/resources/software/aws/toolkits/resources/AmazonQBundle.properties
@@ -9,7 +9,6 @@ amazonqInlineChat.popup.title=Enter Instructions for Q
amazonq.refresh.panel=Refresh Chat Session
amazonq.title=Amazon Q
amazonq.workspace.settings.open.prompt=Workspace index is now enabled. You can disable it from Amazon Q settings.
-action.q.manage.subscription.text=Manage Subscription
action.q.profile.usage.text=You changed your profile
action.q.profile.usage=You''re using the ''{0}'' profile for Amazon Q.
action.q.switchProfiles.text=Change Profile
diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/actions/ManageSubscription.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/actions/ManageSubscription.kt
deleted file mode 100644
index 10568619c64..00000000000
--- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/actions/ManageSubscription.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
-// SPDX-License-Identifier: Apache-2.0
-
-package software.aws.toolkits.jetbrains.services.codewhisperer.actions
-
-import com.intellij.openapi.actionSystem.ActionUpdateThread
-import com.intellij.openapi.actionSystem.AnAction
-import com.intellij.openapi.actionSystem.AnActionEvent
-import com.intellij.openapi.progress.currentThreadCoroutineScope
-import com.intellij.openapi.project.DumbAware
-import kotlinx.coroutines.future.await
-import kotlinx.coroutines.launch
-import org.eclipse.lsp4j.ExecuteCommandParams
-import software.aws.toolkits.core.utils.error
-import software.aws.toolkits.core.utils.getLogger
-import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection
-import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
-import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection
-import software.aws.toolkits.jetbrains.core.credentials.sono.isSono
-import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
-
-class ManageSubscription : AnAction(), DumbAware {
- override fun getActionUpdateThread() = ActionUpdateThread.BGT
-
- override fun update(e: AnActionEvent) {
- val project = e.project
- // disable if user is IdC
- if (project == null) {
- e.presentation.isEnabledAndVisible = false
- } else {
- val connection = ToolkitConnectionManager.getInstance(project)
- .activeConnectionForFeature(QConnection.getInstance()) as? AwsBearerTokenConnection
-
- e.presentation.isEnabledAndVisible = connection.isSono()
- }
- }
-
- override fun actionPerformed(e: AnActionEvent) {
- val project = e.project ?: return
- currentThreadCoroutineScope().launch {
- AmazonQLspService.getInstance(project).execute { lsp ->
- lsp.workspaceService.executeCommand(
- ExecuteCommandParams().apply {
- this.command = "aws/chat/manageSubscription"
- }
- )
- }.handleAsync { _, ex ->
- if (ex != null) {
- LOG.error(ex) { "Failed aws/chat/manageSubscription" }
- }
- }.await()
- }
- }
-
- companion object {
- private val LOG = getLogger()
- }
-}
From 38e73696523e8976c524c04f7d45f12b6b33e9ff Mon Sep 17 00:00:00 2001
From: manodnyab <66754471+manodnyab@users.noreply.github.com>
Date: Wed, 4 Jun 2025 10:01:33 -0700
Subject: [PATCH 03/10] Send active file in editor (#5783)
---
.../jetbrains/services/amazonq/webview/BrowserConnector.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
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 9d39606ba8b..83d6a532ef1 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
@@ -224,7 +224,7 @@ class BrowserConnector(
)
val serializedEnrichmentParams = serializer.objectMapper.valueToTree(enrichmentParams)
- val chatParams: ObjectNode = (node as ObjectNode)
+ val chatParams: ObjectNode = (node.params as ObjectNode)
.setAll(serializedEnrichmentParams)
val tabId = requestFromUi.params.tabId
@@ -235,7 +235,7 @@ class BrowserConnector(
val result = AmazonQLspService.executeIfRunning(project) { server ->
encryptionManager = this.encryptionManager
- val encryptedParams = EncryptedChatParams(this.encryptionManager.encrypt(chatParams.params), partialResultToken)
+ val encryptedParams = EncryptedChatParams(this.encryptionManager.encrypt(chatParams), partialResultToken)
rawEndpoint.request(SEND_CHAT_COMMAND_PROMPT, encryptedParams) as CompletableFuture
} ?: (CompletableFuture.failedFuture(IllegalStateException("LSP Server not running")))
From 514132075a7d8cb41bdde5bf5f82fcf31b2dc194 Mon Sep 17 00:00:00 2001
From: manodnyab <66754471+manodnyab@users.noreply.github.com>
Date: Wed, 4 Jun 2025 11:10:05 -0700
Subject: [PATCH 04/10] Fix creation of prompts not opening files in the editor
(#5781)
There was a threading issue where since the file was opened in EDT and the result was sent back to the server on another thread, it returned the result before the file was opened. This PR makes it synchronous
---
.../amazonq/lsp/AmazonQLanguageClientImpl.kt | 25 +++++++++++--------
1 file changed, 14 insertions(+), 11 deletions(-)
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 01eda2ea2c1..dcaad8b7143 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
@@ -158,18 +158,21 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC
// The filepath sent by the server contains unicode characters which need to be
// decoded for JB file handling APIs to be handle to handle file operations
val fileToOpen = URLDecoder.decode(params.uri, StandardCharsets.UTF_8.name())
- ApplicationManager.getApplication().invokeLater {
- try {
- val virtualFile = VirtualFileManager.getInstance().findFileByUrl(fileToOpen)
- ?: throw IllegalArgumentException("Cannot find file: $fileToOpen")
-
- FileEditorManager.getInstance(project).openFile(virtualFile, true)
- } catch (e: Exception) {
- LOG.warn { "Failed to show document: $fileToOpen" }
- }
- }
+ return CompletableFuture.supplyAsync(
+ {
+ try {
+ val virtualFile = VirtualFileManager.getInstance().refreshAndFindFileByUrl(fileToOpen)
+ ?: throw IllegalArgumentException("Cannot find file: $fileToOpen")
- return CompletableFuture.completedFuture(ShowDocumentResult(true))
+ FileEditorManager.getInstance(project).openFile(virtualFile, true)
+ ShowDocumentResult(true)
+ } catch (e: Exception) {
+ LOG.warn { "Failed to show document: $fileToOpen" }
+ ShowDocumentResult(false)
+ }
+ },
+ ApplicationManager.getApplication()::invokeLater
+ )
} catch (e: Exception) {
LOG.warn { "Error showing document" }
return CompletableFuture.completedFuture(ShowDocumentResult(false))
From 69f152243d19e6f902f7543889fbc0551fef9821 Mon Sep 17 00:00:00 2001
From: manodnyab <66754471+manodnyab@users.noreply.github.com>
Date: Wed, 4 Jun 2025 18:58:15 -0700
Subject: [PATCH 05/10] Update changelog (#5785)
---
.../feature-f8a047df-70ed-4c42-b9f7-f3b97e9c9a6e.json | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 .changes/next-release/feature-f8a047df-70ed-4c42-b9f7-f3b97e9c9a6e.json
diff --git a/.changes/next-release/feature-f8a047df-70ed-4c42-b9f7-f3b97e9c9a6e.json b/.changes/next-release/feature-f8a047df-70ed-4c42-b9f7-f3b97e9c9a6e.json
new file mode 100644
index 00000000000..e1fcfbfe7da
--- /dev/null
+++ b/.changes/next-release/feature-f8a047df-70ed-4c42-b9f7-f3b97e9c9a6e.json
@@ -0,0 +1,4 @@
+{
+ "type" : "feature",
+ "description" : "Agentic coding experience: Amazon Q can now write code and run shell commands on your behalf"
+}
\ No newline at end of file
From 060a13248a44e88cece515370b65175efaa38eeb Mon Sep 17 00:00:00 2001
From: Richard Li <742829+rli@users.noreply.github.com>
Date: Wed, 4 Jun 2025 21:14:56 -0700
Subject: [PATCH 06/10] fix(amazonq): fix issue where chat messages get sent
to all projects (#5787)
* fix(amazonq): fix issue where chat messages get sent to all projects
* callsites
---------
Co-authored-by: manodnyab <66754471+manodnyab@users.noreply.github.com>
---
.../services/cwc/commands/ActionRegistrar.kt | 4 ++--
.../codescan/actions/ExplainCodeIssueAction.kt | 10 +++++++++-
.../amazonq/lsp/AmazonQLanguageClientImpl.kt | 1 +
.../amazonq/lsp/flareChat/AsyncChatUiListener.kt | 14 ++++++++------
.../lsp/flareChat/ChatCommunicationManager.kt | 8 ++++----
5 files changed, 24 insertions(+), 13 deletions(-)
diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/ActionRegistrar.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/ActionRegistrar.kt
index b9a6efd5b48..e5e92f7544b 100644
--- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/ActionRegistrar.kt
+++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/ActionRegistrar.kt
@@ -29,7 +29,7 @@ class ActionRegistrar {
fun reportMessageClick(command: EditorContextCommand, project: Project) {
if (command == EditorContextCommand.GenerateUnitTests) {
- AsyncChatUiListener.notifyPartialMessageUpdate(Gson().toJson(TestCommandMessage()))
+ AsyncChatUiListener.notifyPartialMessageUpdate(project, Gson().toJson(TestCommandMessage()))
} else {
// new agentic chat route
ApplicationManager.getApplication().executeOnPooledThread {
@@ -45,7 +45,7 @@ class ActionRegistrar {
val params = SendToPromptParams(selection = codeSelection, triggerType = TriggerType.CONTEXT_MENU)
uiMessage = FlareUiMessage(command = SEND_TO_PROMPT, params = params)
}
- AsyncChatUiListener.notifyPartialMessageUpdate(uiMessage)
+ AsyncChatUiListener.notifyPartialMessageUpdate(project, uiMessage)
}
}
}
diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/codescan/actions/ExplainCodeIssueAction.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/codescan/actions/ExplainCodeIssueAction.kt
index bf744d4b569..94f572984a9 100644
--- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/codescan/actions/ExplainCodeIssueAction.kt
+++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/codescan/actions/ExplainCodeIssueAction.kt
@@ -4,6 +4,7 @@
package software.aws.toolkits.jetbrains.services.cwc.commands.codescan.actions
import com.intellij.openapi.actionSystem.ActionManager
+import com.intellij.openapi.actionSystem.ActionUpdateThread
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.DataKey
@@ -18,7 +19,14 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SendT
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.TriggerType
class ExplainCodeIssueAction : AnAction(), DumbAware {
+ override fun getActionUpdateThread() = ActionUpdateThread.BGT
+
+ override fun update(e: AnActionEvent) {
+ e.presentation.isEnabledAndVisible = e.project != null
+ }
+
override fun actionPerformed(e: AnActionEvent) {
+ val project = e.project ?: return
val issueDataKey = DataKey.create>("amazonq.codescan.explainissue")
val issueContext = e.getData(issueDataKey) ?: return
@@ -50,7 +58,7 @@ class ExplainCodeIssueAction : AnAction(), DumbAware {
)
val uiMessage = FlareUiMessage(SEND_TO_PROMPT, params)
- AsyncChatUiListener.notifyPartialMessageUpdate(uiMessage)
+ AsyncChatUiListener.notifyPartialMessageUpdate(project, uiMessage)
}
}
}
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 dcaad8b7143..577b51cf612 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
@@ -315,6 +315,7 @@ class AmazonQLanguageClientImpl(private val project: Project) : AmazonQLanguageC
override fun sendChatUpdate(params: LSPAny): CompletableFuture {
AsyncChatUiListener.notifyPartialMessageUpdate(
+ project,
FlareUiMessage(
command = CHAT_SEND_UPDATE,
params = params,
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 c6992526ec9..21d812cf075 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
@@ -3,10 +3,11 @@
package software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat
-import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.project.Project
import com.intellij.util.messages.Topic
import java.util.EventListener
+@Deprecated("Why are we using a message bus for this????????")
interface AsyncChatUiListener : EventListener {
@Deprecated("shouldn't need this version")
fun onChange(command: String) {}
@@ -14,16 +15,17 @@ interface AsyncChatUiListener : EventListener {
fun onChange(command: FlareUiMessage) {}
companion object {
- @Topic.AppLevel
+ @Topic.ProjectLevel
val TOPIC = Topic.create("Partial chat message provider", AsyncChatUiListener::class.java)
- fun notifyPartialMessageUpdate(command: FlareUiMessage) {
- ApplicationManager.getApplication().messageBus.syncPublisher(TOPIC).onChange(command)
+ @Deprecated("Why are we using a message bus for this????????")
+ fun notifyPartialMessageUpdate(project: Project, command: FlareUiMessage) {
+ project.messageBus.syncPublisher(TOPIC).onChange(command)
}
@Deprecated("shouldn't need this version")
- fun notifyPartialMessageUpdate(command: String) {
- ApplicationManager.getApplication().messageBus.syncPublisher(TOPIC).onChange(command)
+ fun notifyPartialMessageUpdate(project: Project, command: String) {
+ project.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 bd64d7336f8..ac7deaa4134 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
@@ -37,7 +37,7 @@ import java.util.concurrent.CompletableFuture
import java.util.concurrent.ConcurrentHashMap
@Service(Service.Level.PROJECT)
-class ChatCommunicationManager(private val cs: CoroutineScope) {
+class ChatCommunicationManager(private val project: Project, private val cs: CoroutineScope) {
val uiReady = CompletableDeferred()
private val chatPartialResultMap = ConcurrentHashMap()
private val inflightRequestByTabId = ConcurrentHashMap>()
@@ -53,7 +53,7 @@ class ChatCommunicationManager(private val cs: CoroutineScope) {
fun notifyUi(uiMessage: FlareUiMessage) {
cs.launch {
uiReady.await()
- AsyncChatUiListener.notifyPartialMessageUpdate(uiMessage)
+ AsyncChatUiListener.notifyPartialMessageUpdate(project, uiMessage)
}
}
@@ -148,7 +148,7 @@ class ChatCommunicationManager(private val cs: CoroutineScope) {
params = partialChatResult,
isPartialResult = true
)
- AsyncChatUiListener.notifyPartialMessageUpdate(uiMessage)
+ AsyncChatUiListener.notifyPartialMessageUpdate(project, uiMessage)
finalResultProcessed[token] = true
ChatAsyncResultManager.getInstance(project).setResult(token, partialResultMap)
return
@@ -169,7 +169,7 @@ class ChatCommunicationManager(private val cs: CoroutineScope) {
params = partialChatResult,
isPartialResult = true
)
- AsyncChatUiListener.notifyPartialMessageUpdate(uiMessage)
+ AsyncChatUiListener.notifyPartialMessageUpdate(project, uiMessage)
}
}
}
From bc9582385f7d3b69645db146d5ff981134df03ea Mon Sep 17 00:00:00 2001
From: Richard Li <742829+rli@users.noreply.github.com>
Date: Wed, 4 Jun 2025 21:27:52 -0700
Subject: [PATCH 07/10] fix(amazonq): add metrics for flare/node resolution
(#5786)
We need some visibility on how prevalent failures are
---
.../services/amazonq/lsp/AmazonQLspService.kt | 20 ++++
.../amazonq/lsp/artifacts/ArtifactHelper.kt | 63 +++++++---
.../amazonq/lsp/artifacts/ArtifactManager.kt | 108 ++++++++++++------
.../lsp/artifacts/ArtifactHelperTest.kt | 4 +-
.../services/telemetry/otel/OtelBase.kt | 3 +
5 files changed, 148 insertions(+), 50 deletions(-)
diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
index f5226359c55..28c28100f97 100644
--- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
+++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt
@@ -77,10 +77,12 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.textdocument.TextDoc
import software.aws.toolkits.jetbrains.services.amazonq.lsp.util.WorkspaceFolderUtil.createWorkspaceFolders
import software.aws.toolkits.jetbrains.services.amazonq.lsp.workspace.WorkspaceServiceHandler
import software.aws.toolkits.jetbrains.services.amazonq.profile.QDefaultServiceConfig
+import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl
import software.aws.toolkits.jetbrains.services.telemetry.ClientMetadata
import software.aws.toolkits.jetbrains.settings.LspSettings
import software.aws.toolkits.jetbrains.utils.notifyInfo
import software.aws.toolkits.resources.message
+import software.aws.toolkits.telemetry.Telemetry
import java.io.IOException
import java.io.OutputStreamWriter
import java.io.PipedInputStream
@@ -526,20 +528,36 @@ private class AmazonQServerInstance(private val project: Project, private val cs
* may fail to start in that case. The caller should handle potential runtime initialization failures.
*/
private fun getNodeRuntimePath(nodePath: Path): Path {
+ val resolveNodeMetric = { isBundled: Boolean, success: Boolean ->
+ Telemetry.languageserver.setup.use {
+ it.id("q")
+ it.metadata("languageServerSetupStage", "resolveNode")
+ it.metadata("credentialStartUrl", getStartUrl(project))
+ it.setAttribute("isBundledNode", isBundled)
+ it.success(success)
+ }
+ }
+
if (Files.exists(nodePath) && Files.isExecutable(nodePath)) {
+ resolveNodeMetric(true, true)
return nodePath
}
+
// use alternative node runtime if it is not found
LOG.warn { "Node Runtime download failed. Fallback to user specified node runtime " }
// attempt to use user provided node runtime path
val nodeRuntime = LspSettings.getInstance().getNodeRuntimePath()
if (!nodeRuntime.isNullOrEmpty()) {
LOG.info { "Using node from $nodeRuntime " }
+
+ resolveNodeMetric(false, true)
return Path.of(nodeRuntime)
} else {
val localNode = locateNodeCommand()
if (localNode != null) {
LOG.info { "Using node from ${localNode.toAbsolutePath()}" }
+
+ resolveNodeMetric(false, true)
return localNode
}
notifyInfo(
@@ -557,6 +575,8 @@ private class AmazonQServerInstance(private val project: Project, private val cs
) { _, notification -> notification.expire() }
)
)
+
+ resolveNodeMetric(false, false)
return nodePath
}
}
diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelper.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelper.kt
index 97f2db11fed..dc8d25235dc 100644
--- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelper.kt
+++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelper.kt
@@ -17,7 +17,10 @@ import software.aws.toolkits.core.utils.getLogger
import software.aws.toolkits.core.utils.info
import software.aws.toolkits.core.utils.warn
import software.aws.toolkits.jetbrains.core.saveFileFromUrl
+import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl
import software.aws.toolkits.resources.AwsCoreBundle
+import software.aws.toolkits.telemetry.LanguageServerSetupStage
+import software.aws.toolkits.telemetry.Telemetry
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
@@ -106,12 +109,12 @@ class ArtifactHelper(private val lspArtifactsPath: Path = DEFAULT_ARTIFACT_PATH,
}
suspend fun tryDownloadLspArtifacts(project: Project, targetVersion: Version, target: VersionTarget): Path? {
- val temporaryDownloadPath = Files.createTempDirectory("lsp-dl")
- val downloadPath = lspArtifactsPath.resolve(targetVersion.serverVersion.toString())
+ val destinationPath = lspArtifactsPath.resolve(targetVersion.serverVersion.toString())
while (currentAttempt.get() < maxDownloadAttempts) {
currentAttempt.incrementAndGet()
logger.info { "Attempt ${currentAttempt.get()} of $maxDownloadAttempts to download LSP artifacts" }
+ val temporaryDownloadPath = Files.createTempDirectory("lsp-dl")
try {
return withBackgroundProgress(
@@ -119,20 +122,20 @@ class ArtifactHelper(private val lspArtifactsPath: Path = DEFAULT_ARTIFACT_PATH,
AwsCoreBundle.message("amazonqFeatureDev.placeholder.downloading_and_extracting_lsp_artifacts"),
cancellable = true
) {
- if (downloadLspArtifacts(temporaryDownloadPath, target) && !target.contents.isNullOrEmpty()) {
- moveFilesFromSourceToDestination(temporaryDownloadPath, downloadPath)
+ if (downloadLspArtifacts(project, temporaryDownloadPath, target) && !target.contents.isNullOrEmpty()) {
+ moveFilesFromSourceToDestination(temporaryDownloadPath, destinationPath)
target.contents
.mapNotNull { it.filename }
- .forEach { filename -> extractZipFile(downloadPath.resolve(filename), downloadPath) }
- logger.info { "Successfully downloaded and moved LSP artifacts to $downloadPath" }
+ .forEach { filename -> extractZipFile(destinationPath.resolve(filename), destinationPath) }
+ logger.info { "Successfully downloaded and moved LSP artifacts to $destinationPath" }
val thirdPartyLicenses = targetVersion.thirdPartyLicenses
logger.info {
- "Installing Amazon Q Language Server v${targetVersion.serverVersion} to: $downloadPath. " +
+ "Installing Amazon Q Language Server v${targetVersion.serverVersion} to: $destinationPath. " +
if (thirdPartyLicenses == null) "" else "Attribution notice can be found at $thirdPartyLicenses"
}
- return@withBackgroundProgress downloadPath
+ return@withBackgroundProgress destinationPath
}
return@withBackgroundProgress null
@@ -146,7 +149,7 @@ class ArtifactHelper(private val lspArtifactsPath: Path = DEFAULT_ARTIFACT_PATH,
else -> { logger.error(e) { "Failed to download/move LSP artifacts on attempt ${currentAttempt.get()}" } }
}
temporaryDownloadPath.toFile().deleteRecursively()
- downloadPath.toFile().deleteRecursively()
+ destinationPath.toFile().deleteRecursively()
}
}
logger.error { "Failed to download LSP artifacts after $maxDownloadAttempts attempts" }
@@ -154,7 +157,7 @@ class ArtifactHelper(private val lspArtifactsPath: Path = DEFAULT_ARTIFACT_PATH,
}
@VisibleForTesting
- internal fun downloadLspArtifacts(downloadPath: Path, target: VersionTarget?): Boolean {
+ internal fun downloadLspArtifacts(project: Project, downloadPath: Path, target: VersionTarget?): Boolean {
if (target == null || target.contents.isNullOrEmpty()) {
logger.warn { "No target contents available for download" }
return false
@@ -171,7 +174,7 @@ class ArtifactHelper(private val lspArtifactsPath: Path = DEFAULT_ARTIFACT_PATH,
logger.warn { "No hash available for ${content.filename}" }
return@forEach
}
- downloadAndValidateFile(content.url, filePath, contentHash)
+ downloadAndValidateFile(project, content.url, filePath, contentHash)
}
validateDownloadedFiles(downloadPath, target.contents)
} catch (e: Exception) {
@@ -182,18 +185,46 @@ class ArtifactHelper(private val lspArtifactsPath: Path = DEFAULT_ARTIFACT_PATH,
return true
}
- private fun downloadAndValidateFile(url: String, filePath: Path, expectedHash: String) {
+ private fun downloadAndValidateFile(project: Project, url: String, filePath: Path, expectedHash: String) {
+ val recordDownload = { runnable: () -> Unit ->
+ Telemetry.languageserver.setup.use { telemetry ->
+ telemetry.id("q")
+ telemetry.languageServerSetupStage(LanguageServerSetupStage.GetServer)
+ telemetry.metadata("credentialStartUrl", getStartUrl(project))
+ telemetry.success(true)
+
+ try {
+ runnable()
+ } catch (t: Throwable) {
+ telemetry.success(false)
+ telemetry.recordException(t)
+ }
+ }
+ }
+
try {
if (!filePath.exists()) {
logger.info { "Downloading file: ${filePath.fileName}" }
- saveFileFromUrl(url, filePath, ProgressManager.getInstance().progressIndicator)
+ recordDownload { saveFileFromUrl(url, filePath, ProgressManager.getInstance().progressIndicator) }
}
if (!validateFileHash(filePath, expectedHash)) {
logger.warn { "Hash mismatch for ${filePath.fileName}, re-downloading" }
filePath.deleteIfExists()
- saveFileFromUrl(url, filePath)
- if (!validateFileHash(filePath, expectedHash)) {
- throw LspException("Hash mismatch after re-download for ${filePath.fileName}", LspException.ErrorCode.HASH_MISMATCH)
+ recordDownload { saveFileFromUrl(url, filePath) }
+
+ Telemetry.languageserver.setup.use {
+ it.id("q")
+ it.languageServerSetupStage(LanguageServerSetupStage.Validate)
+ it.metadata("credentialStartUrl", getStartUrl(project))
+ it.success(true)
+
+ if (!validateFileHash(filePath, expectedHash)) {
+ it.success(false)
+
+ val exception = LspException("Hash mismatch after re-download for ${filePath.fileName}", LspException.ErrorCode.HASH_MISMATCH)
+ it.recordException(exception)
+ throw exception
+ }
}
}
} catch (e: Exception) {
diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactManager.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactManager.kt
index b0a4d8e7e9a..d7970612854 100644
--- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactManager.kt
+++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactManager.kt
@@ -19,6 +19,10 @@ import software.aws.toolkits.core.utils.info
import software.aws.toolkits.core.utils.warn
import software.aws.toolkits.jetbrains.AwsPlugin
import software.aws.toolkits.jetbrains.AwsToolkit
+import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl
+import software.aws.toolkits.telemetry.LanguageServerSetupStage
+import software.aws.toolkits.telemetry.MetricResult
+import software.aws.toolkits.telemetry.Telemetry
import java.nio.file.Path
@Service
@@ -57,42 +61,82 @@ class ArtifactManager @NonInjectable internal constructor(private val manifestFe
return mutex.withLock {
coroutineScope {
async {
- try {
- val manifest = manifestFetcher.fetch() ?: throw LspException(
- "Language Support is not available, as manifest is missing.",
- LspException.ErrorCode.MANIFEST_FETCH_FAILED
- )
- val lspVersions = getLSPVersionsFromManifestWithSpecifiedRange(manifest)
-
- artifactHelper.removeDelistedVersions(lspVersions.deListedVersions)
-
- if (lspVersions.inRangeVersions.isEmpty()) {
- // No versions are found which are in the given range. Fallback to local lsp artifacts.
- val localLspArtifacts = artifactHelper.getAllLocalLspArtifactsWithinManifestRange(DEFAULT_VERSION_RANGE)
- if (localLspArtifacts.isNotEmpty()) {
- return@async localLspArtifacts.first().first
+ Telemetry.languageserver.setup.use { all ->
+ all.id("q")
+ all.languageServerSetupStage(LanguageServerSetupStage.All)
+ all.metadata("credentialStartUrl", getStartUrl(project))
+ all.result(MetricResult.Succeeded)
+
+ try {
+ val lspVersions = Telemetry.languageserver.setup.use { telemetry ->
+ telemetry.id("q")
+ telemetry.languageServerSetupStage(LanguageServerSetupStage.GetManifest)
+ telemetry.metadata("credentialStartUrl", getStartUrl(project))
+
+ val exception = LspException(
+ "Language Support is not available, as manifest is missing.",
+ LspException.ErrorCode.MANIFEST_FETCH_FAILED
+ )
+ telemetry.success(true)
+ val manifest = manifestFetcher.fetch() ?: run {
+ telemetry.recordException(exception)
+ telemetry.success(false)
+ throw exception
+ }
+
+ getLSPVersionsFromManifestWithSpecifiedRange(manifest)
}
- throw LspException("Language server versions not found in manifest.", LspException.ErrorCode.NO_COMPATIBLE_LSP_VERSION)
- }
- val targetVersion = lspVersions.inRangeVersions.first()
+ artifactHelper.removeDelistedVersions(lspVersions.deListedVersions)
+
+ if (lspVersions.inRangeVersions.isEmpty()) {
+ // No versions are found which are in the given range. Fallback to local lsp artifacts.
+ val localLspArtifacts = artifactHelper.getAllLocalLspArtifactsWithinManifestRange(DEFAULT_VERSION_RANGE)
+ if (localLspArtifacts.isNotEmpty()) {
+ return@async localLspArtifacts.first().first
+ }
+ throw LspException("Language server versions not found in manifest.", LspException.ErrorCode.NO_COMPATIBLE_LSP_VERSION)
+ }
- // If there is an LSP Manifest with the same version
- val target = getTargetFromLspManifest(targetVersion)
- // Get Local LSP files and check if we can re-use existing LSP Artifacts
- val artifactPath: Path = if (artifactHelper.getExistingLspArtifacts(targetVersion, target)) {
- artifactHelper.getAllLocalLspArtifactsWithinManifestRange(DEFAULT_VERSION_RANGE).first().first
- } else {
- artifactHelper.tryDownloadLspArtifacts(project, targetVersion, target)
- ?: throw LspException("Failed to download LSP artifacts", LspException.ErrorCode.DOWNLOAD_FAILED)
+ val targetVersion = lspVersions.inRangeVersions.first()
+
+ // If there is an LSP Manifest with the same version
+ val target = getTargetFromLspManifest(targetVersion)
+ // Get Local LSP files and check if we can re-use existing LSP Artifacts
+ val artifactPath: Path = if (artifactHelper.getExistingLspArtifacts(targetVersion, target)) {
+ artifactHelper.getAllLocalLspArtifactsWithinManifestRange(DEFAULT_VERSION_RANGE).first().first
+ } else {
+ artifactHelper.tryDownloadLspArtifacts(project, targetVersion, target)
+ ?: throw LspException("Failed to download LSP artifacts", LspException.ErrorCode.DOWNLOAD_FAILED)
+ }
+
+ artifactHelper.deleteOlderLspArtifacts(DEFAULT_VERSION_RANGE)
+
+ Telemetry.languageserver.setup.use {
+ it.id("q")
+ it.languageServerSetupStage(LanguageServerSetupStage.Launch)
+ it.metadata("credentialStartUrl", getStartUrl(project))
+ it.setAttribute("isBundledArtifact", false)
+ it.success(true)
+ }
+ return@async artifactPath
+ } catch (e: Exception) {
+ logger.warn(e) { "Failed to resolve assets from Flare CDN" }
+ val path = AwsToolkit.PLUGINS_INFO[AwsPlugin.Q]?.path?.resolve("flare") ?: error("not even bundled")
+ logger.info { "Falling back to bundled assets at $path" }
+
+ all.recordException(e)
+ all.result(MetricResult.Failed)
+
+ Telemetry.languageserver.setup.use {
+ it.id("q")
+ it.languageServerSetupStage(LanguageServerSetupStage.Launch)
+ it.metadata("credentialStartUrl", getStartUrl(project))
+ it.setAttribute("isBundledArtifact", true)
+ it.success(false)
+ }
+ return@async path
}
- artifactHelper.deleteOlderLspArtifacts(DEFAULT_VERSION_RANGE)
- return@async artifactPath
- } catch (e: Exception) {
- logger.warn(e) { "Failed to resolve assets from Flare CDN" }
- val path = AwsToolkit.PLUGINS_INFO[AwsPlugin.Q]?.path?.resolve("flare") ?: error("not even bundled")
- logger.info { "Falling back to bundled assets at $path" }
- return@async path
}
}
}.also {
diff --git a/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelperTest.kt b/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelperTest.kt
index 367e2a31a97..72f3961fb58 100644
--- a/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelperTest.kt
+++ b/plugins/amazonq/shared/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/ArtifactHelperTest.kt
@@ -199,7 +199,7 @@ class ArtifactHelperTest {
val version = Version(serverVersion = "1.0.0")
val spyArtifactHelper = spyk(artifactHelper)
- every { spyArtifactHelper.downloadLspArtifacts(any(), any()) } returns false
+ every { spyArtifactHelper.downloadLspArtifacts(mockProject, any(), any()) } returns false
assertThat(runBlocking { artifactHelper.tryDownloadLspArtifacts(mockProject, version, VersionTarget(contents = contents)) }).isEqualTo(null)
}
@@ -210,7 +210,7 @@ class ArtifactHelperTest {
val target = VersionTarget(contents = contents)
val spyArtifactHelper = spyk(artifactHelper)
- every { spyArtifactHelper.downloadLspArtifacts(any(), any()) } returns true
+ every { spyArtifactHelper.downloadLspArtifacts(mockProject, any(), any()) } returns true
mockkStatic("software.aws.toolkits.jetbrains.services.amazonq.lsp.artifacts.LspUtilsKt")
every { moveFilesFromSourceToDestination(any(), any()) } just Runs
every { extractZipFile(any(), any()) } just Runs
diff --git a/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/otel/OtelBase.kt b/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/otel/OtelBase.kt
index 623663d85db..22cf1231e9b 100644
--- a/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/otel/OtelBase.kt
+++ b/plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/services/telemetry/otel/OtelBase.kt
@@ -201,6 +201,9 @@ abstract class AbstractBaseSpan>(internal
override fun recordException(exception: Throwable): SpanType {
delegate.recordException(exception)
+
+ setAttribute("reason", exception::class.java.canonicalName)
+ setAttribute("reasonDesc", exception.message)
return this as SpanType
}
From a6d80b4af89b7d3a419628223f8ac3c39a1dfc6b Mon Sep 17 00:00:00 2001
From: Sam Stewart
Date: Thu, 5 Jun 2025 02:54:23 -0700
Subject: [PATCH 08/10] fix(amazonq): make q chat unavalable in IDE version
2024.2.1 (#5788)
* make chat unaccessible on 242.2.1
* detekt
* feedback
* detekt
* alias
* simplify
* fix product code
* Update plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QUtils.kt
Co-authored-by: Richard Li <742829+rli@users.noreply.github.com>
* update message
* Update plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties
* revert
---------
Co-authored-by: manodnyab <66754471+manodnyab@users.noreply.github.com>
Co-authored-by: Richard Li <742829+rli@users.noreply.github.com>
---
.../services/amazonq/toolwindow/AmazonQPanel.kt | 5 +++++
.../aws/toolkits/jetbrains/services/amazonq/QUtils.kt | 11 +++++++++++
.../aws/toolkits/resources/MessagesBundle.properties | 2 +-
3 files changed, 17 insertions(+), 1 deletion(-)
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 2ff79e68005..53323b638b2 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
@@ -25,6 +25,7 @@ import software.aws.toolkits.jetbrains.isDeveloperMode
import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitContext
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.isQSupportedInThisVersion
import software.aws.toolkits.jetbrains.services.amazonq.lsp.AmazonQLspService
import software.aws.toolkits.jetbrains.services.amazonq.lsp.artifacts.ArtifactManager
import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AsyncChatUiListener
@@ -42,6 +43,7 @@ import software.aws.toolkits.jetbrains.services.amazonqCodeTest.auth.isCodeTestA
import software.aws.toolkits.jetbrains.services.amazonqDoc.auth.isDocAvailable
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.auth.isFeatureDevAvailable
import software.aws.toolkits.jetbrains.services.codemodernizer.utils.isCodeTransformAvailable
+import software.aws.toolkits.resources.message
import java.util.concurrent.CompletableFuture
import javax.swing.JButton
@@ -102,6 +104,9 @@ class AmazonQPanel(val project: Project, private val scope: CoroutineScope) : Di
webviewContainer.add(JBTextArea("JCEF not supported"))
}
browser.complete(null)
+ } else if (!isQSupportedInThisVersion()) {
+ webviewContainer.add(JBTextArea("${message("q.unavailable")}\n ${message("q.unavailable.node")}"))
+ browser.complete(null)
} else {
val loadingPanel = JBLoadingPanel(null, this)
val wrapper = Wrapper()
diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QUtils.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QUtils.kt
index f6dea5c681e..55b6c46d7ef 100644
--- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QUtils.kt
+++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QUtils.kt
@@ -3,7 +3,9 @@
package software.aws.toolkits.jetbrains.services.amazonq
+import com.intellij.openapi.application.ApplicationInfo
import com.intellij.openapi.project.Project
+import com.intellij.openapi.util.BuildNumber
import com.intellij.openapi.util.SystemInfo
import software.amazon.awssdk.services.codewhispererruntime.model.IdeCategory
import software.amazon.awssdk.services.codewhispererruntime.model.OperatingSystem
@@ -52,3 +54,12 @@ fun codeWhispererUserContext(): UserContext = ClientMetadata.getDefault().let {
.ideVersion(it.awsVersion)
.build()
}
+
+fun isQSupportedInThisVersion(): Boolean {
+ val currentBuild = ApplicationInfo.getInstance().build.withoutProductCode()
+
+ return !(
+ currentBuild.baselineVersion == 242 &&
+ BuildNumber.fromString("242.22855.74")?.let { currentBuild < it } == true
+ )
+}
diff --git a/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties b/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties
index 0871d6258f5..9f769d120ab 100644
--- a/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties
+++ b/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties
@@ -1656,7 +1656,7 @@ q.session_configuration=Extend your IDE sessions
q.session_configuration.description=Your maximum session length for Amazon Q can be extended to 90 days by your administrator. For more information, refer to How to extend the session duration for Amazon Q in the IDE in the IAM Identity Center User Guide.
q.sign.in=Get Started
q.ui.prompt.transform=/transform
-q.unavailable=\ Not supported in v2023.2.0
+q.unavailable=\ Amazon Q Chat is not supported in IDE versions <= v2024.2.1
q.unavailable.node=Please update to the latest IDE version
q.window.title=Amazon Q Chat
rds.aurora=Aurora
From ea7c07133ee1d2ec95a1965726af6ea28d22b4e3 Mon Sep 17 00:00:00 2001
From: aws-toolkit-automation <>
Date: Thu, 5 Jun 2025 15:08:31 +0000
Subject: [PATCH 09/10] Updating version to 3.74
---
.changes/3.74.json | 11 +++++++++++
.../bugfix-061149bd-c6ef-4c86-9f12-98e38fe3b576.json | 4 ----
.../feature-f8a047df-70ed-4c42-b9f7-f3b97e9c9a6e.json | 4 ----
CHANGELOG.md | 4 ++++
gradle.properties | 2 +-
5 files changed, 16 insertions(+), 9 deletions(-)
create mode 100644 .changes/3.74.json
delete mode 100644 .changes/next-release/bugfix-061149bd-c6ef-4c86-9f12-98e38fe3b576.json
delete mode 100644 .changes/next-release/feature-f8a047df-70ed-4c42-b9f7-f3b97e9c9a6e.json
diff --git a/.changes/3.74.json b/.changes/3.74.json
new file mode 100644
index 00000000000..ceea3211ddd
--- /dev/null
+++ b/.changes/3.74.json
@@ -0,0 +1,11 @@
+{
+ "date" : "2025-06-05",
+ "version" : "3.74",
+ "entries" : [ {
+ "type" : "feature",
+ "description" : "Agentic coding experience: Amazon Q can now write code and run shell commands on your behalf"
+ }, {
+ "type" : "bugfix",
+ "description" : "Support full Unicode range in inline chat panel on Windows"
+ } ]
+}
\ No newline at end of file
diff --git a/.changes/next-release/bugfix-061149bd-c6ef-4c86-9f12-98e38fe3b576.json b/.changes/next-release/bugfix-061149bd-c6ef-4c86-9f12-98e38fe3b576.json
deleted file mode 100644
index 3a97907cb1d..00000000000
--- a/.changes/next-release/bugfix-061149bd-c6ef-4c86-9f12-98e38fe3b576.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type" : "bugfix",
- "description" : "Support full Unicode range in inline chat panel on Windows"
-}
\ No newline at end of file
diff --git a/.changes/next-release/feature-f8a047df-70ed-4c42-b9f7-f3b97e9c9a6e.json b/.changes/next-release/feature-f8a047df-70ed-4c42-b9f7-f3b97e9c9a6e.json
deleted file mode 100644
index e1fcfbfe7da..00000000000
--- a/.changes/next-release/feature-f8a047df-70ed-4c42-b9f7-f3b97e9c9a6e.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "type" : "feature",
- "description" : "Agentic coding experience: Amazon Q can now write code and run shell commands on your behalf"
-}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6e0116b680f..85632cf8a2b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+# _3.74_ (2025-06-05)
+- **(Feature)** Agentic coding experience: Amazon Q can now write code and run shell commands on your behalf
+- **(Bug Fix)** Support full Unicode range in inline chat panel on Windows
+
# _3.73_ (2025-05-29)
- **(Bug Fix)** /transform: handle InvalidGrantException properly when polling job status
diff --git a/gradle.properties b/gradle.properties
index 65bf15a2f2f..d5491e7d80f 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0
# Toolkit Version
-toolkitVersion=3.74-SNAPSHOT
+toolkitVersion=3.74
# Publish Settings
publishToken=
From d6d032a693de616c6748bd8dcd88ca7534beb138 Mon Sep 17 00:00:00 2001
From: aws-toolkit-automation <>
Date: Thu, 5 Jun 2025 16:44:45 +0000
Subject: [PATCH 10/10] Updating SNAPSHOT version to 3.75-SNAPSHOT
---
gradle.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gradle.properties b/gradle.properties
index d5491e7d80f..6aabb46f9e0 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -2,7 +2,7 @@
# SPDX-License-Identifier: Apache-2.0
# Toolkit Version
-toolkitVersion=3.74
+toolkitVersion=3.75-SNAPSHOT
# Publish Settings
publishToken=