From 2cf169855f5b2d1d3b465835a44fd179b2ad0617 Mon Sep 17 00:00:00 2001 From: Conor Stewart Date: Thu, 31 Jul 2025 17:18:39 -0700 Subject: [PATCH 01/13] feat: context command pending state passed to ui, triggered when indexing complete --- chat-client/src/client/mynahUi.ts | 1 + .../agenticChat/agenticChatController.ts | 13 +++++++++++++ .../context/contextCommandsProvider.ts | 15 +++++++++++++++ .../src/shared/localProjectContextController.ts | 4 ++++ 4 files changed, 33 insertions(+) diff --git a/chat-client/src/client/mynahUi.ts b/chat-client/src/client/mynahUi.ts index bd2fb05114..d90e6e5b4b 100644 --- a/chat-client/src/client/mynahUi.ts +++ b/chat-client/src/client/mynahUi.ts @@ -1546,6 +1546,7 @@ ${params.message}`, commands: toContextCommands(child.commands), })), icon: toMynahIcon(command.icon), + disabled: command.pending === true, })) } diff --git a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts index 2e60f8c741..e456a7b76a 100644 --- a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts +++ b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts @@ -359,6 +359,17 @@ export class AgenticChatController implements ChatHandlers { this.#features.workspace, this.#features.lsp ) + + // Set up index completion callback to toggle workspacePending + LocalProjectContextController.getInstance() + .then(controller => { + controller.onIndexBuildComplete = () => { + this.#contextCommandsProvider.maybeUpdateWorkspacePending() + } + }) + .catch(err => { + this.#features.logging.error(`Failed to set up index completion callback: ${err}`) + }) this.#mcpEventHandler = new McpEventHandler(features, telemetryService) this.#origin = getOriginFromClientInfo(this.#features.lsp.getClientInitializeParams()?.clientInfo?.name) this.#activeUserTracker = ActiveUserTracker.getInstance(this.#features) @@ -3433,9 +3444,11 @@ export class AgenticChatController implements ChatHandlers { */ async onReady() { await this.restorePreviousChats() + await this.#contextCommandsProvider.processContextCommandUpdate([]) try { const localProjectContextController = await LocalProjectContextController.getInstance() const contextItems = await localProjectContextController.getContextCommandItems() + this.#contextCommandsProvider.filesAndFoldersPending = false await this.#contextCommandsProvider.processContextCommandUpdate(contextItems) void this.#contextCommandsProvider.maybeUpdateCodeSymbols() } catch (error) { diff --git a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts index 632360cd67..bce477621c 100644 --- a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts +++ b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts @@ -12,6 +12,9 @@ import { activeFileCmd } from './additionalContextProvider' export class ContextCommandsProvider implements Disposable { private promptFileWatcher?: FSWatcher private cachedContextCommands?: ContextCommandItem[] + private codeSymbolsPending = true + public filesAndFoldersPending = true + private workspacePending = true constructor( private readonly logging: Logging, private readonly chat: Chat, @@ -105,6 +108,7 @@ export class ContextCommandsProvider implements Disposable { ], description: 'Add all files in a folder to context', icon: 'folder', + pending: this.filesAndFoldersPending, } const fileCmds: ContextCommand[] = [activeFileCmd] @@ -118,6 +122,7 @@ export class ContextCommandsProvider implements Disposable { ], description: 'Add a file to context', icon: 'file', + pending: this.filesAndFoldersPending, } const codeCmds: ContextCommand[] = [] @@ -131,6 +136,7 @@ export class ContextCommandsProvider implements Disposable { ], description: 'Add code to context', icon: 'code-block', + pending: this.codeSymbolsPending, } const promptCmds: ContextCommand[] = [] @@ -156,6 +162,7 @@ export class ContextCommandsProvider implements Disposable { command: '@workspace', id: '@workspace', description: 'Reference all code in workspace', + pending: this.workspacePending, } const commands = [workspaceCmd, folderCmdGroup, fileCmdGroup, codeCmdGroup, promptCmdGroup] @@ -209,11 +216,19 @@ export class ContextCommandsProvider implements Disposable { await LocalProjectContextController.getInstance() ).shouldUpdateContextCommandSymbolsOnce() if (needUpdate) { + this.codeSymbolsPending = false const items = await (await LocalProjectContextController.getInstance()).getContextCommandItems() await this.processContextCommandUpdate(items) } } + async maybeUpdateWorkspacePending() { + if (this.workspacePending) { + this.workspacePending = false + void this.processContextCommandUpdate(this.cachedContextCommands ?? []) + } + } + dispose() { void this.promptFileWatcher?.close() } diff --git a/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts b/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts index 27317e954e..8b419bbda3 100644 --- a/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts +++ b/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts @@ -50,6 +50,8 @@ export interface LocalProjectContextInitializationOptions { export class LocalProjectContextController { // Event handler for context items updated public onContextItemsUpdated: ((contextItems: ContextCommandItem[]) => Promise) | undefined + // Event handler for index build completion + public onIndexBuildComplete: (() => void) | undefined private static instance: LocalProjectContextController | undefined private workspaceFolders: WorkspaceFolder[] @@ -212,6 +214,7 @@ export class LocalProjectContextController { if (this._isIndexingInProgress) { return } + try { this._isIndexingInProgress = true if (this._vecLib) { @@ -229,6 +232,7 @@ export class LocalProjectContextController { const projectRoot = URI.parse(this.workspaceFolders.sort()[0].uri).fsPath await this._vecLib?.buildIndex(sourceFiles, projectRoot, indexingType) this.log.info('Context index built successfully') + this.onIndexBuildComplete?.() } } catch (error) { this.log.error(`Error building index: ${error}`) From ce32f5e009325b97972c1507d241cad6aca4dae8 Mon Sep 17 00:00:00 2001 From: Conor Stewart Date: Mon, 4 Aug 2025 17:21:39 -0700 Subject: [PATCH 02/13] refactored pending flow --- .../agenticChat/agenticChatController.ts | 13 +------------ .../context/contextCommandsProvider.ts | 17 +++++++++++------ 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts index e456a7b76a..a9238d89f1 100644 --- a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts +++ b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts @@ -360,16 +360,6 @@ export class AgenticChatController implements ChatHandlers { this.#features.lsp ) - // Set up index completion callback to toggle workspacePending - LocalProjectContextController.getInstance() - .then(controller => { - controller.onIndexBuildComplete = () => { - this.#contextCommandsProvider.maybeUpdateWorkspacePending() - } - }) - .catch(err => { - this.#features.logging.error(`Failed to set up index completion callback: ${err}`) - }) this.#mcpEventHandler = new McpEventHandler(features, telemetryService) this.#origin = getOriginFromClientInfo(this.#features.lsp.getClientInitializeParams()?.clientInfo?.name) this.#activeUserTracker = ActiveUserTracker.getInstance(this.#features) @@ -3444,11 +3434,10 @@ export class AgenticChatController implements ChatHandlers { */ async onReady() { await this.restorePreviousChats() - await this.#contextCommandsProvider.processContextCommandUpdate([]) try { const localProjectContextController = await LocalProjectContextController.getInstance() const contextItems = await localProjectContextController.getContextCommandItems() - this.#contextCommandsProvider.filesAndFoldersPending = false + this.#contextCommandsProvider.setFilesAndFoldersPending(false) await this.#contextCommandsProvider.processContextCommandUpdate(contextItems) void this.#contextCommandsProvider.maybeUpdateCodeSymbols() } catch (error) { diff --git a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts index bce477621c..c4f2f8f128 100644 --- a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts +++ b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts @@ -13,7 +13,7 @@ export class ContextCommandsProvider implements Disposable { private promptFileWatcher?: FSWatcher private cachedContextCommands?: ContextCommandItem[] private codeSymbolsPending = true - public filesAndFoldersPending = true + private filesAndFoldersPending = true private workspacePending = true constructor( private readonly logging: Logging, @@ -25,6 +25,8 @@ export class ContextCommandsProvider implements Disposable { this.registerContextCommandHandler().catch(e => this.logging.error(`Error registering context command handler: ${e}`) ) + //send initial pending state to client immediately + void this.processContextCommandUpdate([]).catch(() => {}) } private async registerContextCommandHandler() { @@ -33,6 +35,12 @@ export class ContextCommandsProvider implements Disposable { controller.onContextItemsUpdated = async contextItems => { await this.processContextCommandUpdate(contextItems) } + controller.onIndexBuildComplete = () => { + if (this.workspacePending) { + this.workspacePending = false + void this.processContextCommandUpdate(this.cachedContextCommands ?? []) + } + } } catch (e) { this.logging.warn(`Error processing context command update: ${e}`) } @@ -222,11 +230,8 @@ export class ContextCommandsProvider implements Disposable { } } - async maybeUpdateWorkspacePending() { - if (this.workspacePending) { - this.workspacePending = false - void this.processContextCommandUpdate(this.cachedContextCommands ?? []) - } + setFilesAndFoldersPending(value: boolean) { + this.filesAndFoldersPending = value } dispose() { From c9d39227cf773ef8c66144cf9286353b22b7323a Mon Sep 17 00:00:00 2001 From: Conor Stewart Date: Tue, 5 Aug 2025 10:16:25 -0700 Subject: [PATCH 03/13] fix: removed extra spaces --- .../src/language-server/agenticChat/agenticChatController.ts | 1 - .../src/shared/localProjectContextController.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts index 7b1479d741..079a8d4724 100644 --- a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts +++ b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts @@ -366,7 +366,6 @@ export class AgenticChatController implements ChatHandlers { this.#features.workspace, this.#features.lsp ) - this.#mcpEventHandler = new McpEventHandler(features, telemetryService) this.#origin = getOriginFromClientInfo(getClientName(this.#features.lsp.getClientInitializeParams())) this.#activeUserTracker = ActiveUserTracker.getInstance(this.#features) diff --git a/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts b/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts index 8b419bbda3..fdb442271f 100644 --- a/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts +++ b/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts @@ -214,7 +214,6 @@ export class LocalProjectContextController { if (this._isIndexingInProgress) { return } - try { this._isIndexingInProgress = true if (this._vecLib) { From 832162cd472e96fd9a2ac908358e4e683a414bbe Mon Sep 17 00:00:00 2001 From: Conor Stewart Date: Wed, 6 Aug 2025 12:37:22 -0700 Subject: [PATCH 04/13] fix: workspace pending turned back on when building index (when config is changed) --- .../agenticChat/context/contextCommandsProvider.ts | 6 +++--- .../src/shared/localProjectContextController.ts | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts index c4f2f8f128..aef91fcb88 100644 --- a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts +++ b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts @@ -35,9 +35,9 @@ export class ContextCommandsProvider implements Disposable { controller.onContextItemsUpdated = async contextItems => { await this.processContextCommandUpdate(contextItems) } - controller.onIndexBuildComplete = () => { - if (this.workspacePending) { - this.workspacePending = false + controller.onIndexingInProgressChanged = (indexingInProgress: boolean) => { + if (this.workspacePending !== indexingInProgress) { + this.workspacePending = indexingInProgress void this.processContextCommandUpdate(this.cachedContextCommands ?? []) } } diff --git a/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts b/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts index fdb442271f..9f725bcb1b 100644 --- a/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts +++ b/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts @@ -51,7 +51,7 @@ export class LocalProjectContextController { // Event handler for context items updated public onContextItemsUpdated: ((contextItems: ContextCommandItem[]) => Promise) | undefined // Event handler for index build completion - public onIndexBuildComplete: (() => void) | undefined + public onIndexingInProgressChanged: ((enabled: boolean) => void) | undefined private static instance: LocalProjectContextController | undefined private workspaceFolders: WorkspaceFolder[] @@ -216,6 +216,7 @@ export class LocalProjectContextController { } try { this._isIndexingInProgress = true + this.onIndexingInProgressChanged?.(this._isIndexingInProgress) if (this._vecLib) { if (!this.workspaceFolders.length) { this.log.info('skip building index because no workspace folder found') @@ -231,12 +232,12 @@ export class LocalProjectContextController { const projectRoot = URI.parse(this.workspaceFolders.sort()[0].uri).fsPath await this._vecLib?.buildIndex(sourceFiles, projectRoot, indexingType) this.log.info('Context index built successfully') - this.onIndexBuildComplete?.() } } catch (error) { this.log.error(`Error building index: ${error}`) } finally { this._isIndexingInProgress = false + this.onIndexingInProgressChanged?.(this._isIndexingInProgress) } } From eba7b908205ca661959c54c3e082a122a78f56c3 Mon Sep 17 00:00:00 2001 From: Conor Stewart Date: Wed, 6 Aug 2025 14:58:53 -0700 Subject: [PATCH 05/13] fix: log errors if inital context commands fail to send --- .../agenticChat/context/contextCommandsProvider.ts | 4 +++- .../src/shared/localProjectContextController.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts index aef91fcb88..a486cb15ae 100644 --- a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts +++ b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts @@ -26,7 +26,9 @@ export class ContextCommandsProvider implements Disposable { this.logging.error(`Error registering context command handler: ${e}`) ) //send initial pending state to client immediately - void this.processContextCommandUpdate([]).catch(() => {}) + void this.processContextCommandUpdate([]).catch(e => + this.logging.error(`Failed to send initial context commands: ${e}`) + ) } private async registerContextCommandHandler() { diff --git a/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts b/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts index 9f725bcb1b..63143f1c75 100644 --- a/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts +++ b/server/aws-lsp-codewhisperer/src/shared/localProjectContextController.ts @@ -50,7 +50,7 @@ export interface LocalProjectContextInitializationOptions { export class LocalProjectContextController { // Event handler for context items updated public onContextItemsUpdated: ((contextItems: ContextCommandItem[]) => Promise) | undefined - // Event handler for index build completion + // Event handler for when index is being built public onIndexingInProgressChanged: ((enabled: boolean) => void) | undefined private static instance: LocalProjectContextController | undefined From 477b80b3d9d0331f5aa23111784e2c2075b72d31 Mon Sep 17 00:00:00 2001 From: Conor Stewart Date: Thu, 7 Aug 2025 12:31:21 -0700 Subject: [PATCH 06/13] refactor: changed pending state from boolean to string to send to ui --- .../agenticChat/context/contextCommandsProvider.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts index a486cb15ae..c46bd4f0ba 100644 --- a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts +++ b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts @@ -118,7 +118,7 @@ export class ContextCommandsProvider implements Disposable { ], description: 'Add all files in a folder to context', icon: 'folder', - pending: this.filesAndFoldersPending, + disabledText: this.filesAndFoldersPending ? 'pending' : undefined, } const fileCmds: ContextCommand[] = [activeFileCmd] @@ -132,7 +132,7 @@ export class ContextCommandsProvider implements Disposable { ], description: 'Add a file to context', icon: 'file', - pending: this.filesAndFoldersPending, + disabledText: this.filesAndFoldersPending ? 'pending' : undefined, } const codeCmds: ContextCommand[] = [] @@ -146,7 +146,7 @@ export class ContextCommandsProvider implements Disposable { ], description: 'Add code to context', icon: 'code-block', - pending: this.codeSymbolsPending, + disabledText: this.codeSymbolsPending ? 'pending' : undefined, } const promptCmds: ContextCommand[] = [] @@ -168,11 +168,12 @@ export class ContextCommandsProvider implements Disposable { icon: 'image', placeholder: 'Select an image file', } - const workspaceCmd = { + + const workspaceCmd: ContextCommand = { command: '@workspace', id: '@workspace', description: 'Reference all code in workspace', - pending: this.workspacePending, + disabledText: this.workspacePending ? 'pending' : undefined, } const commands = [workspaceCmd, folderCmdGroup, fileCmdGroup, codeCmdGroup, promptCmdGroup] From 21c0c3154387f015659eb0009c3b9dbedc389381 Mon Sep 17 00:00:00 2001 From: Conor Stewart Date: Thu, 7 Aug 2025 16:49:13 -0700 Subject: [PATCH 07/13] refactor: update from pending to disabledText --- chat-client/src/client/mynahUi.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chat-client/src/client/mynahUi.ts b/chat-client/src/client/mynahUi.ts index d4d6af555e..71deadf2f1 100644 --- a/chat-client/src/client/mynahUi.ts +++ b/chat-client/src/client/mynahUi.ts @@ -1549,7 +1549,7 @@ ${params.message}`, commands: toContextCommands(child.commands), })), icon: toMynahIcon(command.icon), - disabled: command.pending === true, + disabled: command.disabledText != null, })) } From e029335a1ac9fb97110e15d22f5b7c91ec143892 Mon Sep 17 00:00:00 2001 From: Conor Stewart Date: Mon, 11 Aug 2025 14:25:22 -0700 Subject: [PATCH 08/13] fix: send intial pending state before any other action in contextCommandsProvider --- .../agenticChat/context/contextCommandsProvider.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts index c46bd4f0ba..cf43a49547 100644 --- a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts +++ b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts @@ -21,14 +21,14 @@ export class ContextCommandsProvider implements Disposable { private readonly workspace: Workspace, private readonly lsp: Lsp ) { - this.registerPromptFileWatcher() - this.registerContextCommandHandler().catch(e => - this.logging.error(`Error registering context command handler: ${e}`) - ) //send initial pending state to client immediately void this.processContextCommandUpdate([]).catch(e => this.logging.error(`Failed to send initial context commands: ${e}`) ) + this.registerPromptFileWatcher() + this.registerContextCommandHandler().catch(e => + this.logging.error(`Error registering context command handler: ${e}`) + ) } private async registerContextCommandHandler() { From 10b68e04555517fed6b6c23912cade60f6403027 Mon Sep 17 00:00:00 2001 From: Conor Stewart Date: Wed, 13 Aug 2025 14:48:34 -0700 Subject: [PATCH 09/13] fix: send initial pending context commands onReady, instead of before --- .../agenticChat/agenticChatController.ts | 1 + .../agenticChat/context/contextCommandsProvider.ts | 14 ++++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts index 378a0bf75b..9d91107580 100644 --- a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts +++ b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts @@ -3486,6 +3486,7 @@ export class AgenticChatController implements ChatHandlers { */ async onReady() { await this.restorePreviousChats() + this.#contextCommandsProvider.onReady() try { const localProjectContextController = await LocalProjectContextController.getInstance() const contextItems = await localProjectContextController.getContextCommandItems() diff --git a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts index cf43a49547..4a4f5cd4fb 100644 --- a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts +++ b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts @@ -15,22 +15,28 @@ export class ContextCommandsProvider implements Disposable { private codeSymbolsPending = true private filesAndFoldersPending = true private workspacePending = true + private initialStateSent = false constructor( private readonly logging: Logging, private readonly chat: Chat, private readonly workspace: Workspace, private readonly lsp: Lsp ) { - //send initial pending state to client immediately - void this.processContextCommandUpdate([]).catch(e => - this.logging.error(`Failed to send initial context commands: ${e}`) - ) this.registerPromptFileWatcher() this.registerContextCommandHandler().catch(e => this.logging.error(`Error registering context command handler: ${e}`) ) } + onReady() { + if (!this.initialStateSent) { + this.initialStateSent = true + void this.processContextCommandUpdate([]).catch(e => + this.logging.error(`Failed to send initial context commands: ${e}`) + ) + } + } + private async registerContextCommandHandler() { try { const controller = await LocalProjectContextController.getInstance() From 137aea139b3825a346b29085c4a1c35599972a0c Mon Sep 17 00:00:00 2001 From: Conor Stewart Date: Wed, 13 Aug 2025 17:12:03 -0700 Subject: [PATCH 10/13] test: added unit test for onReady --- .../context/contextCommandsProvider.test.ts | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.test.ts b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.test.ts index d387c96d82..11db0e0312 100644 --- a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.test.ts +++ b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.test.ts @@ -58,6 +58,26 @@ describe('ContextCommandsProvider', () => { }) }) + describe('onReady', () => { + it('should call processContextCommandUpdate with empty array on first call', async () => { + const processUpdateSpy = sinon.spy(provider, 'processContextCommandUpdate') + + provider.onReady() + + sinon.assert.calledOnce(processUpdateSpy) + sinon.assert.calledWith(processUpdateSpy, []) + }) + + it('should not call processContextCommandUpdate on subsequent calls', async () => { + const processUpdateSpy = sinon.spy(provider, 'processContextCommandUpdate') + + provider.onReady() + provider.onReady() + + sinon.assert.calledOnce(processUpdateSpy) + }) + }) + describe('onContextItemsUpdated', () => { it('should call processContextCommandUpdate when controller raises event', async () => { const mockContextItems: ContextCommandItem[] = [ From 1edfe240751bf98e9afead0efc5be8f02ea06ccb Mon Sep 17 00:00:00 2001 From: Conor Stewart Date: Thu, 14 Aug 2025 15:47:18 -0700 Subject: [PATCH 11/13] test: added unit test for when indexingInProgress is changed --- .../context/contextCommandsProvider.test.ts | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.test.ts b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.test.ts index 11db0e0312..dbada5b8d7 100644 --- a/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.test.ts +++ b/server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.test.ts @@ -3,6 +3,7 @@ import * as sinon from 'sinon' import { TestFeatures } from '@aws/language-server-runtimes/testing' import * as chokidar from 'chokidar' import { ContextCommandItem } from 'local-indexing' +import { LocalProjectContextController } from '../../../shared/localProjectContextController' describe('ContextCommandsProvider', () => { let provider: ContextCommandsProvider @@ -21,6 +22,12 @@ describe('ContextCommandsProvider', () => { testFeatures.workspace.fs.exists = fsExistsStub testFeatures.workspace.fs.readdir = fsReadDirStub + + sinon.stub(LocalProjectContextController, 'getInstance').resolves({ + onContextItemsUpdated: sinon.stub(), + onIndexingInProgressChanged: sinon.stub(), + } as any) + provider = new ContextCommandsProvider( testFeatures.logging, testFeatures.chat, @@ -98,4 +105,29 @@ describe('ContextCommandsProvider', () => { sinon.assert.calledWith(processUpdateSpy, mockContextItems) }) }) + + describe('onIndexingInProgressChanged', () => { + it('should update workspacePending and call processContextCommandUpdate when indexing status changes', async () => { + let capturedCallback: ((indexingInProgress: boolean) => void) | undefined + + const mockController = { + onContextItemsUpdated: sinon.stub(), + set onIndexingInProgressChanged(callback: (indexingInProgress: boolean) => void) { + capturedCallback = callback + }, + } + + const processUpdateSpy = sinon.spy(provider, 'processContextCommandUpdate') + ;(LocalProjectContextController.getInstance as sinon.SinonStub).resolves(mockController as any) + + // Set initial state to false so condition is met + ;(provider as any).workspacePending = false + + await (provider as any).registerContextCommandHandler() + + capturedCallback?.(true) + + sinon.assert.calledWith(processUpdateSpy, []) + }) + }) }) From d555c50df68d28fa9e017fc8162ef1b9de34a269 Mon Sep 17 00:00:00 2001 From: Sherry Lu Date: Thu, 16 Oct 2025 14:03:42 -0700 Subject: [PATCH 12/13] chore: fix the mynal ui test --- chat-client/src/client/mynahUi.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/chat-client/src/client/mynahUi.test.ts b/chat-client/src/client/mynahUi.test.ts index 3a13772926..ef864fc7f1 100644 --- a/chat-client/src/client/mynahUi.test.ts +++ b/chat-client/src/client/mynahUi.test.ts @@ -634,6 +634,7 @@ describe('MynahUI', () => { route: ['/workspace', 'src/file1.ts'], icon: 'file', children: undefined, + disabled: false, }, ], promptTopBarTitle: '@', From 8d7e4835952234a46f7186842f48161d2921fd53 Mon Sep 17 00:00:00 2001 From: Sherry Lu Date: Thu, 16 Oct 2025 15:59:41 -0700 Subject: [PATCH 13/13] chore: fix the mynal test for lack of disabled field --- chat-client/src/client/mynahUi.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat-client/src/client/mynahUi.test.ts b/chat-client/src/client/mynahUi.test.ts index ef864fc7f1..1f9f6c4e57 100644 --- a/chat-client/src/client/mynahUi.test.ts +++ b/chat-client/src/client/mynahUi.test.ts @@ -691,6 +691,7 @@ describe('MynahUI', () => { ...activeEditorCommand, description: 'file:///workspace/src/active.ts', children: undefined, + disabled: false, }, ], promptTopBarTitle: '@Pin Context', @@ -730,7 +731,7 @@ describe('MynahUI', () => { // Verify updateStore was called with empty context items // Active editor should be removed since no textDocument was provided sinon.assert.calledWith(updateStoreSpy, tabId, { - promptTopBarContextItems: [{ ...fileCommand, children: undefined }], + promptTopBarContextItems: [{ ...fileCommand, children: undefined, disabled: false }], promptTopBarTitle: '@', promptTopBarButton: null, })