diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QRefreshPanelAction.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QRefreshPanelAction.kt index 8b22e156399..84ba01c0e4c 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QRefreshPanelAction.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QRefreshPanelAction.kt @@ -11,7 +11,6 @@ import com.intellij.openapi.project.DumbAwareAction import com.intellij.util.messages.Topic import software.aws.toolkits.jetbrains.services.amazonq.toolwindow.AmazonQToolWindow import software.aws.toolkits.resources.AmazonQBundle -import software.aws.toolkits.resources.message import java.util.EventListener class QRefreshPanelAction : DumbAwareAction(AmazonQBundle.message("amazonq.refresh.panel"), null, AllIcons.Actions.Refresh) { diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindow.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindow.kt index 7e0cc266c11..5df972bba09 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindow.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindow.kt @@ -24,7 +24,6 @@ class AmazonQToolWindow private constructor( val component get() = chatPanel.component - fun disposeAndRecreate() { Disposer.dispose(chatPanel) chatPanel = AmazonQPanel(project, scope) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindowFactory.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindowFactory.kt index a61fa43855b..3ae16dd96c6 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindowFactory.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindowFactory.kt @@ -66,7 +66,7 @@ class AmazonQToolWindowFactory : ToolWindowFactory, DumbAware { ToolkitConnectionManager.getInstance(project).activeConnectionForFeature(QConnection.getInstance())?.let { qConn -> openMeetQPage(project) } - prepareChatContent(project, qPanel) + preparePanelContent(project, qPanel) } } ) @@ -75,7 +75,7 @@ class AmazonQToolWindowFactory : ToolWindowFactory, DumbAware { RefreshQChatPanelButtonPressedListener.TOPIC, object : RefreshQChatPanelButtonPressedListener { override fun onRefresh() { - prepareChatContent(project, qPanel) + preparePanelContent(project, qPanel) } } ) @@ -85,8 +85,7 @@ class AmazonQToolWindowFactory : ToolWindowFactory, DumbAware { object : BearerTokenProviderListener { override fun onChange(providerId: String, newScopes: List?) { if (ToolkitConnectionManager.getInstance(project).connectionStateForFeature(QConnection.getInstance()) == BearerTokenAuthState.AUTHORIZED) { - AmazonQToolWindow.getInstance(project).disposeAndRecreate() - prepareChatContent(project, qPanel) + preparePanelContent(project, qPanel) } } } @@ -98,13 +97,12 @@ class AmazonQToolWindowFactory : ToolWindowFactory, DumbAware { // note we name myProject intentionally ow it will shadow the "project" provided by the IDE override fun onProfileSelected(myProject: Project, profile: QRegionProfile?) { if (project.isDisposed) return - AmazonQToolWindow.getInstance(project).disposeAndRecreate() - prepareChatContent(project, qPanel) + preparePanelContent(project, qPanel) } } ) - prepareChatContent(project, qPanel) + preparePanelContent(project, qPanel) val content = contentManager.factory.createContent(mainPanel, null, false).also { it.isCloseable = true @@ -114,7 +112,7 @@ class AmazonQToolWindowFactory : ToolWindowFactory, DumbAware { contentManager.addContent(content) } - private fun prepareChatContent( + private fun preparePanelContent( project: Project, qPanel: Wrapper, ) { 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 d84ef7b3abf..b9a6efd5b48 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 @@ -11,7 +11,9 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.runBlocking 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.lsp.model.aws.chat.GENERIC_COMMAND import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GenericCommandParams +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SEND_TO_PROMPT import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SendToPromptParams import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.TriggerType import software.aws.toolkits.jetbrains.services.amazonq.messages.AmazonQMessage @@ -36,12 +38,12 @@ class ActionRegistrar { val fileContext = contextExtractor.extractContextForTrigger(ExtractionTriggerType.ContextMenu) val codeSelection = "\n```\n${fileContext.focusAreaContext?.codeSelection?.trimIndent()?.trim()}\n```\n" var uiMessage: FlareUiMessage? = null - if (command.verb != "sendToPrompt") { + if (command.verb != SEND_TO_PROMPT) { val params = GenericCommandParams(selection = codeSelection, triggerType = TriggerType.CONTEXT_MENU, genericCommand = command.name) - uiMessage = FlareUiMessage(command = "genericCommand", params = params) + uiMessage = FlareUiMessage(command = GENERIC_COMMAND, params = params) } else { val params = SendToPromptParams(selection = codeSelection, triggerType = TriggerType.CONTEXT_MENU) - uiMessage = FlareUiMessage(command = "sendToPrompt", params = params) + uiMessage = FlareUiMessage(command = SEND_TO_PROMPT, params = params) } AsyncChatUiListener.notifyPartialMessageUpdate(uiMessage) } @@ -49,10 +51,6 @@ class ActionRegistrar { } } - fun reportMessageClick(command: EditorContextCommand, issue: MutableMap, project: Project) { - _messages.tryEmit(CodeScanIssueActionMessage(command, issue, project)) - } - // provide singleton access companion object { val instance = ActionRegistrar() diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/codescan/actions/CodeScanQActions.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/codescan/actions/CodeScanQActions.kt deleted file mode 100644 index 1dba4220223..00000000000 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/codescan/actions/CodeScanQActions.kt +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package software.aws.toolkits.jetbrains.services.cwc.commands.codescan.actions - -import com.intellij.openapi.actionSystem.ActionManager -import com.intellij.openapi.actionSystem.AnAction -import com.intellij.openapi.actionSystem.AnActionEvent -import com.intellij.openapi.actionSystem.CommonDataKeys -import com.intellij.openapi.actionSystem.DataKey -import com.intellij.openapi.project.DumbAware -import software.aws.toolkits.jetbrains.services.cwc.commands.ActionRegistrar -import software.aws.toolkits.jetbrains.services.cwc.commands.EditorContextCommand - -open class CodeScanQActions(private val command: EditorContextCommand) : AnAction(), DumbAware { - override fun actionPerformed(e: AnActionEvent) { - val issueDataKey = DataKey.create>("amazonq.codescan.explainissue") - val issueContext = e.getData(issueDataKey) ?: return - val project = e.getData(CommonDataKeys.PROJECT) ?: return - - ActionManager.getInstance().getAction("q.openchat").actionPerformed(e) - - ActionRegistrar.instance.reportMessageClick(command, issueContext, project) - } -} 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 48a10d2a381..bf744d4b569 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 @@ -3,6 +3,55 @@ package software.aws.toolkits.jetbrains.services.cwc.commands.codescan.actions -import software.aws.toolkits.jetbrains.services.cwc.commands.EditorContextCommand +import com.intellij.openapi.actionSystem.ActionManager +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.DataKey +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.project.DumbAware +import kotlinx.coroutines.runBlocking +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.lsp.model.aws.chat.ChatPrompt +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SEND_TO_PROMPT +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SendToPromptParams +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.TriggerType -class ExplainCodeIssueAction : CodeScanQActions(EditorContextCommand.ExplainCodeScanIssue) +class ExplainCodeIssueAction : AnAction(), DumbAware { + override fun actionPerformed(e: AnActionEvent) { + val issueDataKey = DataKey.create>("amazonq.codescan.explainissue") + val issueContext = e.getData(issueDataKey) ?: return + + ActionManager.getInstance().getAction("q.openchat").actionPerformed(e) + + ApplicationManager.getApplication().executeOnPooledThread { + runBlocking { + // https://github.com/aws/aws-toolkit-vscode/blob/master/packages/amazonq/src/lsp/chat/commands.ts#L30 + val codeSelection = "\n```\n${issueContext["code"]?.trimIndent()?.trim()}\n```\n" + + val prompt = "Explain the issue \n\n " + + "Issue: \"${issueContext["title"]}\" \n" + + "Code: $codeSelection" + + val modelPrompt = "Explain the issue ${issueContext["title"]} \n\n " + + "Issue: \"${issueContext["title"]}\" \n" + + "Description: ${issueContext["description"]} \n" + + "Code: $codeSelection and generate the code demonstrating the fix" + + val params = SendToPromptParams( + selection = codeSelection, + triggerType = TriggerType.CONTEXT_MENU, + prompt = ChatPrompt( + prompt = prompt, + escapedPrompt = modelPrompt, + command = null + ), + autoSubmit = true + ) + + val uiMessage = FlareUiMessage(SEND_TO_PROMPT, params) + AsyncChatUiListener.notifyPartialMessageUpdate(uiMessage) + } + } + } +} diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorUtil.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorUtil.kt index 5b0c93c7c53..0d8406488d9 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorUtil.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorUtil.kt @@ -32,7 +32,8 @@ object CodeWhispererEditorUtil { val fileName = getFileName(psiFile) val programmingLanguage = psiFile.programmingLanguage() val fileRelativePath = getRelativePathToContentRoot(editor) - return FileContextInfo(caretContext, fileName, programmingLanguage, fileRelativePath) + val fileUri = getFileUri(psiFile) + return FileContextInfo(caretContext, fileName, programmingLanguage, fileRelativePath, fileUri) } fun extractCaretContext(editor: Editor): CaretContext { @@ -73,6 +74,11 @@ object CodeWhispererEditorUtil { private fun getFileName(psiFile: PsiFile): String = psiFile.name.substring(0, psiFile.name.length.coerceAtMost(CodeWhispererConstants.FILENAME_CHARS_LIMIT)) + private fun getFileUri(psiFile: PsiFile): String? = + psiFile.virtualFile?.takeIf { it.isValid }?.let { vFile -> + vFile.url.substring(0, vFile.url.length.coerceAtMost(CodeWhispererConstants.FILENAME_CHARS_LIMIT)) + } + fun getRelativePathToContentRoot(editor: Editor): String? = editor.project?.let { project -> FileDocumentManager.getInstance().getFile(editor.document)?.let { vFile -> diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/model/CodeWhispererModel.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/model/CodeWhispererModel.kt index c424c3414ef..61157a2a1d1 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/model/CodeWhispererModel.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/model/CodeWhispererModel.kt @@ -47,6 +47,7 @@ data class FileContextInfo( val filename: String, val programmingLanguage: CodeWhispererProgrammingLanguage, val fileRelativePath: String?, + val fileUri: String?, ) data class RecommendationContext( diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererServiceTest.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererServiceTest.kt index dbe0a5d8ad9..37015577346 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererServiceTest.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererServiceTest.kt @@ -74,7 +74,8 @@ class CodeWhispererServiceTest : CodeWhispererTestBase() { CaretContext(leftFileContext = "", rightFileContext = "public class Main {}", leftContextOnCurrentLine = ""), "main.java", CodeWhispererJava.INSTANCE, - "main.java" + "main.java", + file.virtualFile.url ) ) } diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt index 6b2f8542852..fd6b9a0c49f 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt @@ -127,7 +127,7 @@ fun aFileContextInfo(language: CodeWhispererProgrammingLanguage? = null): FileCo CodeWhispererJava.INSTANCE ).random() - return FileContextInfo(caretContextInfo, fileName, programmingLanguage, fileRelativePath) + return FileContextInfo(caretContextInfo, fileName, programmingLanguage, fileRelativePath, null) } fun aTriggerType(): CodewhispererTriggerType = diff --git a/plugins/amazonq/mynah-ui/src/mynah-ui/ui/texts/disclaimer.ts b/plugins/amazonq/mynah-ui/src/mynah-ui/ui/texts/disclaimer.ts index 4bf62164a8b..4e06601420b 100644 --- a/plugins/amazonq/mynah-ui/src/mynah-ui/ui/texts/disclaimer.ts +++ b/plugins/amazonq/mynah-ui/src/mynah-ui/ui/texts/disclaimer.ts @@ -8,7 +8,7 @@ import { ChatItem, MynahIcons } from '@aws/mynah-ui-chat' 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.', + 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 may retain chats to provide and maintain the service. For information on the AWS Regions where Amazon Q may perform inference, see [the documentation](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/cross-region-processing.html#cross-region-inference).', buttons: [ { text: 'Acknowledge', 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 21ef074c400..cb33a19b53f 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 @@ -24,12 +24,16 @@ import com.intellij.util.net.HttpConfigurable import com.intellij.util.net.JdkProxyProvider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Job import kotlinx.coroutines.async import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.future.asCompletableFuture +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -52,6 +56,7 @@ import org.eclipse.lsp4j.jsonrpc.json.JsonRpcMethod import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage import org.eclipse.lsp4j.launch.LSPLauncher import org.slf4j.event.Level +import software.aws.toolkits.core.utils.debug import software.aws.toolkits.core.utils.getLogger import software.aws.toolkits.core.utils.info import software.aws.toolkits.core.utils.warn @@ -128,6 +133,9 @@ class AmazonQLspService(private val project: Project, private val cs: CoroutineS val encryptionManager get() = instance.getCompleted().encryptionManager + private val heartbeatJob: Job + private val restartTimestamps = ArrayDeque() + private val restartMutex = Mutex() // Separate mutex for restart tracking val rawEndpoint get() = instance.getCompleted().rawEndpoint @@ -166,9 +174,50 @@ class AmazonQLspService(private val project: Project, private val cs: CoroutineS init { instance = start() + + // Initialize heartbeat job + heartbeatJob = cs.launch { + while (isActive) { + delay(5.seconds) // Check every 5 seconds + val shouldLoop = checkConnectionStatus() + if (!shouldLoop) { + break + } + } + } + } + + private suspend fun checkConnectionStatus(): Boolean { + try { + val currentInstance = mutex.withLock { instance }.await() + + // Check if the launcher's Future (startListening) is done + // If it's done, that means the connection has been terminated + if (currentInstance.launcherFuture.isDone) { + LOG.debug { "LSP server connection terminated, checking restart limits" } + val canRestart = checkForRemainingRestartAttempts() + if (!canRestart) { + return false + } + LOG.debug { "Restarting LSP server" } + restart() + } else { + LOG.debug { "LSP server is currently running" } + } + } catch (e: Exception) { + LOG.debug(e) { "Connection status check failed, checking restart limits" } + val canRestart = checkForRemainingRestartAttempts() + if (!canRestart) { + return false + } + LOG.debug { "Restarting LSP server" } + restart() + } + return true } override fun dispose() { + heartbeatJob.cancel() } suspend fun restart() = mutex.withLock { @@ -195,6 +244,25 @@ class AmazonQLspService(private val project: Project, private val cs: CoroutineS instance = start() } + private suspend fun checkForRemainingRestartAttempts(): Boolean = restartMutex.withLock { + val currentTime = System.currentTimeMillis() + + while (restartTimestamps.isNotEmpty() && + currentTime - restartTimestamps.first() > RESTART_WINDOW_MS + ) { + restartTimestamps.removeFirst() + } + + if (restartTimestamps.size < MAX_RESTARTS) { + restartTimestamps.addLast(currentTime) + return true + } + + LOG.info { "Rate limit reached for LSP server restarts. Stop attempting to restart." } + + return false + } + suspend fun execute(runnable: suspend AmazonQLspService.(AmazonQLanguageServer) -> T): T { val lsp = withTimeout(10.seconds) { val holder = mutex.withLock { instance }.await() @@ -212,6 +280,8 @@ class AmazonQLspService(private val project: Project, private val cs: CoroutineS companion object { private val LOG = getLogger() + private const val MAX_RESTARTS = 5 + private const val RESTART_WINDOW_MS = 3 * 60 * 1000 fun getInstance(project: Project) = project.service() @Deprecated("Easy to accidentally freeze EDT") @@ -241,7 +311,7 @@ private class AmazonQServerInstance(private val project: Project, private val cs get() = launcher.remoteEndpoint @Suppress("ForbiddenVoid") - private val launcherFuture: Future + val launcherFuture: Future private val launcherHandler: KillableProcessHandler val initializeResult: Deferred 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 1f00ebc79f8..84a712094b1 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 @@ -25,6 +25,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ErrorParams import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GetSerializedChatResult import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SEND_CHAT_COMMAND_PROMPT +import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileDialog import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileSelectedListener import java.util.UUID @@ -140,19 +141,28 @@ class ChatCommunicationManager(private val cs: CoroutineScope) { val incomingType = params.authFollowupType val connectionManager = ToolkitConnectionManager.getInstance(project) try { - connectionManager.activeConnectionForFeature(QConnection.getInstance())?.let { - reauthConnectionIfNeeded(project, it, isReAuth = true) - } when (incomingType) { AuthFollowupType.USE_SUPPORTED_AUTH -> { - project.messageBus.syncPublisher(QRegionProfileSelectedListener.TOPIC) - .onProfileSelected(project, QRegionProfileManager.getInstance().activeProfile(project)) + val activeProfile = QRegionProfileManager.getInstance().activeProfile(project) + if (activeProfile != null) { + project.messageBus.syncPublisher(QRegionProfileSelectedListener.TOPIC) + .onProfileSelected(project, QRegionProfileManager.getInstance().activeProfile(project)) + } else { + QRegionProfileDialog( + project, + selectedProfile = null + ).show() + } + return } AuthFollowupType.RE_AUTH, AuthFollowupType.MISSING_SCOPES, AuthFollowupType.FULL_AUTH, -> { + connectionManager.activeConnectionForFeature(QConnection.getInstance())?.let { + reauthConnectionIfNeeded(project, it, isReAuth = true) + } return } else -> { diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/ChatPrompt.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/ChatPrompt.kt index e01c9fdff37..80e39191891 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/ChatPrompt.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/ChatPrompt.kt @@ -9,7 +9,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.ContextCom data class ChatPrompt( val prompt: String, val escapedPrompt: String, - val command: String, + val command: String?, ) data class SendChatPromptRequest( diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/FlareChatCommands.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/FlareChatCommands.kt index 1dc7579b35b..d964435c9eb 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/FlareChatCommands.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/FlareChatCommands.kt @@ -38,6 +38,7 @@ const val DID_REMOVE_FILE = "aws/didRemoveFileOrDirectory" const val DID_CREATE_DIRECTORY = "aws/didCreateDirectory" const val GET_SERIALIZED_CHAT_REQUEST_METHOD = "aws/chat/getSerializedChat" +const val GENERIC_COMMAND = "genericCommand" const val OPEN_FILE_DIFF = "aws/openFileDiff" const val OPEN_SETTINGS = "openSettings" @@ -47,4 +48,5 @@ const val PROMPT_INPUT_OPTIONS_CHANGE = "aws/chat/promptInputOptionChange" const val SEND_CHAT_COMMAND_PROMPT = "aws/chat/sendChatPrompt" const val SHOW_SAVE_FILE_DIALOG_REQUEST_METHOD = "aws/showSaveFileDialog" const val STOP_CHAT_RESPONSE = "stopChatResponse" +const val SEND_TO_PROMPT = "sendToPrompt" const val TELEMETRY_EVENT = "telemetry/event" diff --git a/plugins/core/sdk-codegen/codegen-resources/codewhispererruntime/service-2.json b/plugins/core/sdk-codegen/codegen-resources/codewhispererruntime/service-2.json index 16805ea2b68..d75fdc527bd 100644 --- a/plugins/core/sdk-codegen/codegen-resources/codewhispererruntime/service-2.json +++ b/plugins/core/sdk-codegen/codegen-resources/codewhispererruntime/service-2.json @@ -1596,9 +1596,16 @@ "leftFileContent":{"shape":"FileContextLeftFileContentString"}, "rightFileContent":{"shape":"FileContextRightFileContentString"}, "filename":{"shape":"FileContextFilenameString"}, + "fileUri": {"shape":"FileContextFileUriString"}, "programmingLanguage":{"shape":"ProgrammingLanguage"} } }, + "FileContextFileUriString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, "FileContextFilenameString":{ "type":"string", "max":1024,