@aws/lsp-codewhisperer 0.0.62 → 0.0.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/out/language-server/agenticChat/agenticChatController.d.ts +3 -2
  3. package/out/language-server/agenticChat/agenticChatController.js +96 -26
  4. package/out/language-server/agenticChat/agenticChatController.js.map +1 -1
  5. package/out/language-server/agenticChat/constants/constants.d.ts +9 -0
  6. package/out/language-server/agenticChat/constants/constants.js +16 -0
  7. package/out/language-server/agenticChat/constants/constants.js.map +1 -0
  8. package/out/language-server/agenticChat/constants/modelSelection.d.ts +7 -0
  9. package/out/language-server/agenticChat/constants/modelSelection.js +21 -0
  10. package/out/language-server/agenticChat/constants/modelSelection.js.map +1 -0
  11. package/out/language-server/agenticChat/context/additionalContextProvider.d.ts +5 -0
  12. package/out/language-server/agenticChat/context/additionalContextProvider.js +52 -13
  13. package/out/language-server/agenticChat/context/additionalContextProvider.js.map +1 -1
  14. package/out/language-server/agenticChat/context/contextCommandsProvider.js +1 -1
  15. package/out/language-server/agenticChat/context/contextCommandsProvider.js.map +1 -1
  16. package/out/language-server/agenticChat/qAgenticChatServer.js +3 -0
  17. package/out/language-server/agenticChat/qAgenticChatServer.js.map +1 -1
  18. package/out/language-server/agenticChat/tools/mcp/chokidarFileWatcher.d.ts +8 -0
  19. package/out/language-server/agenticChat/tools/mcp/chokidarFileWatcher.js +47 -0
  20. package/out/language-server/agenticChat/tools/mcp/chokidarFileWatcher.js.map +1 -0
  21. package/out/language-server/agenticChat/tools/mcp/mcpEventHandler.d.ts +4 -0
  22. package/out/language-server/agenticChat/tools/mcp/mcpEventHandler.js +87 -8
  23. package/out/language-server/agenticChat/tools/mcp/mcpEventHandler.js.map +1 -1
  24. package/out/language-server/agenticChat/tools/toolServer.js +1 -1
  25. package/out/language-server/agenticChat/tools/toolServer.js.map +1 -1
  26. package/out/language-server/agenticChat/utils/agenticChatControllerHelper.d.ts +8 -0
  27. package/out/language-server/agenticChat/utils/agenticChatControllerHelper.js +15 -0
  28. package/out/language-server/agenticChat/utils/agenticChatControllerHelper.js.map +1 -0
  29. package/out/language-server/agenticChat/utils/pathValidation.d.ts +21 -0
  30. package/out/language-server/agenticChat/utils/pathValidation.js +44 -0
  31. package/out/language-server/agenticChat/utils/pathValidation.js.map +1 -0
  32. package/out/language-server/chat/chatController.d.ts +1 -1
  33. package/out/language-server/chat/chatController.js.map +1 -1
  34. package/out/language-server/chat/telemetry/chatTelemetryController.d.ts +1 -1
  35. package/out/language-server/chat/telemetry/chatTelemetryController.js +2 -1
  36. package/out/language-server/chat/telemetry/chatTelemetryController.js.map +1 -1
  37. package/out/language-server/inline-completion/codeWhispererServer.js +53 -29
  38. package/out/language-server/inline-completion/codeWhispererServer.js.map +1 -1
  39. package/out/language-server/localProjectContext/localProjectContextServer.js +21 -5
  40. package/out/language-server/localProjectContext/localProjectContextServer.js.map +1 -1
  41. package/out/language-server/netTransform/artifactManager.d.ts +4 -1
  42. package/out/language-server/netTransform/artifactManager.js +53 -2
  43. package/out/language-server/netTransform/artifactManager.js.map +1 -1
  44. package/out/language-server/netTransform/models.d.ts +32 -2
  45. package/out/language-server/netTransform/models.js.map +1 -1
  46. package/out/language-server/netTransform/tests/mockData.js +1 -1
  47. package/out/language-server/netTransform/tests/mockData.js.map +1 -1
  48. package/out/language-server/workspaceContext/dependency/dependencyEventBundler.d.ts +3 -3
  49. package/out/language-server/workspaceContext/dependency/dependencyEventBundler.js +3 -3
  50. package/out/language-server/workspaceContext/dependency/dependencyHandler/JSTSDependencyHandler.js +1 -2
  51. package/out/language-server/workspaceContext/dependency/dependencyHandler/JSTSDependencyHandler.js.map +1 -1
  52. package/out/language-server/workspaceContext/dependency/dependencyHandler/JavaDependencyHandler.js +1 -2
  53. package/out/language-server/workspaceContext/dependency/dependencyHandler/JavaDependencyHandler.js.map +1 -1
  54. package/out/language-server/workspaceContext/dependency/dependencyHandler/LanguageDependencyHandler.d.ts +5 -6
  55. package/out/language-server/workspaceContext/dependency/dependencyHandler/LanguageDependencyHandler.js +17 -27
  56. package/out/language-server/workspaceContext/dependency/dependencyHandler/LanguageDependencyHandler.js.map +1 -1
  57. package/out/language-server/workspaceContext/dependency/dependencyHandler/PythonDependencyHandler.js +1 -2
  58. package/out/language-server/workspaceContext/dependency/dependencyHandler/PythonDependencyHandler.js.map +1 -1
  59. package/out/language-server/workspaceContext/util.js +3 -1
  60. package/out/language-server/workspaceContext/util.js.map +1 -1
  61. package/out/language-server/workspaceContext/workspaceContextServer.js +21 -10
  62. package/out/language-server/workspaceContext/workspaceContextServer.js.map +1 -1
  63. package/out/language-server/workspaceContext/workspaceFolderManager.d.ts +1 -2
  64. package/out/language-server/workspaceContext/workspaceFolderManager.js +24 -30
  65. package/out/language-server/workspaceContext/workspaceFolderManager.js.map +1 -1
  66. package/out/shared/constants.d.ts +1 -0
  67. package/out/shared/constants.js +2 -1
  68. package/out/shared/constants.js.map +1 -1
  69. package/out/shared/imageVerification.d.ts +1 -0
  70. package/out/shared/imageVerification.js +3 -2
  71. package/out/shared/imageVerification.js.map +1 -1
  72. package/out/shared/languageDetection.js +1 -1
  73. package/out/shared/languageDetection.js.map +1 -1
  74. package/out/shared/streamingClientService.js +2 -2
  75. package/out/shared/streamingClientService.js.map +1 -1
  76. package/out/shared/telemetry/telemetryService.js +1 -2
  77. package/out/shared/telemetry/telemetryService.js.map +1 -1
  78. package/package.json +1 -1
  79. package/out/language-server/agenticChat/constants.d.ts +0 -8
  80. package/out/language-server/agenticChat/constants.js +0 -12
  81. package/out/language-server/agenticChat/constants.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.63](https://github.com/aws/language-servers/compare/lsp-codewhisperer/v0.0.62...lsp-codewhisperer/v0.0.63) (2025-07-08)
4
+
5
+
6
+ ### Features
7
+
8
+ * added file watchers to listen to mcp and persona config ([#1714](https://github.com/aws/language-servers/issues/1714)) ([4c5a7f8](https://github.com/aws/language-servers/commit/4c5a7f893bad37bea1946d37d06f57197c3ef04b))
9
+ * adding streakLength back for UserTriggerDecisionEvent ([#1841](https://github.com/aws/language-servers/issues/1841)) ([7052132](https://github.com/aws/language-servers/commit/7052132a5198944ef05ddbf857d622ba518e71da))
10
+ * **amazonq:** add transformation preferences functionality to input gen ([#1792](https://github.com/aws/language-servers/issues/1792)) ([095f737](https://github.com/aws/language-servers/commit/095f737b86e6234b2568c6d4deafbbb90967bdbc))
11
+ * **amazonq:** update workspace context server A/B testing filter ([#1830](https://github.com/aws/language-servers/issues/1830)) ([faeeee3](https://github.com/aws/language-servers/commit/faeeee3da7a8712f3501055ba8d485528185cdb6))
12
+ * **flags:** change flag name to enablewebformtransform([#1804](https://github.com/aws/language-servers/issues/1804)) ([3b6c3be](https://github.com/aws/language-servers/commit/3b6c3be7630248cd00c19c16637f016d799ef8d1))
13
+ * passing partialResultToken to onInlineCompletionHandler result for EDITS ([#1840](https://github.com/aws/language-servers/issues/1840)) ([270d5a3](https://github.com/aws/language-servers/commit/270d5a3c5adba6b49d938f310ac89ae9b7fbc401))
14
+ * support listAvailableModels server request ([#1808](https://github.com/aws/language-servers/issues/1808)) ([9f1ddb3](https://github.com/aws/language-servers/commit/9f1ddb327778dba6da49337b79c5fef19023b52d))
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * adding agenticcoding field to amazonqaddMessage metric([#1849](https://github.com/aws/language-servers/issues/1849)) ([d677c52](https://github.com/aws/language-servers/commit/d677c52c6139859bc0f2dd8e7ffe6a85b87db3f6))
20
+ * adding files on VSC windows properly triggers reindexing ([#1820](https://github.com/aws/language-servers/issues/1820)) ([0c2d8eb](https://github.com/aws/language-servers/commit/0c2d8eb7dd875dfe86d1b2d094ec53a2a1221b97))
21
+ * **amazonq:** allow taking .jpg file as image context, add image cont… ([#1814](https://github.com/aws/language-servers/issues/1814)) ([4d36fa4](https://github.com/aws/language-servers/commit/4d36fa4a0a04692dba720bc0288c6cee7f45a1fc))
22
+ * **amazonq:** change the customer UI message to out of the workspace ([#1822](https://github.com/aws/language-servers/issues/1822)) ([624def5](https://github.com/aws/language-servers/commit/624def51e4d9e21ee8d045ffe528455b69cdfecb))
23
+ * **amazonq:** change the image filter used in open file dialog ([#1838](https://github.com/aws/language-servers/issues/1838)) ([d9da4cb](https://github.com/aws/language-servers/commit/d9da4cbb7b1995ef43aaba1b7e67d26fd61a3c57))
24
+ * **amazonq:** fix to add upper limit validation for tool description ([#1760](https://github.com/aws/language-servers/issues/1760)) ([2d18a3b](https://github.com/aws/language-servers/commit/2d18a3ba69d22b26dea5170656d79b9eacc202b1))
25
+ * **amazonq:** fix typo in image context list ([#1836](https://github.com/aws/language-servers/issues/1836)) ([179b553](https://github.com/aws/language-servers/commit/179b553a1444201e696fd52e7705dc0c05154eab))
26
+ * **amazonq:** handle undefined paths gracefully and retry ([#1825](https://github.com/aws/language-servers/issues/1825)) ([c52b017](https://github.com/aws/language-servers/commit/c52b017eef0666433cbb0b6d8086254dc1af5fee))
27
+ * **amazonq:** include tsx and jsx files in workspace context server ([#1790](https://github.com/aws/language-servers/issues/1790)) ([79691ef](https://github.com/aws/language-servers/commit/79691ef607d9bc98032fe2e59a5031601a4dba9a))
28
+ * **amazonq:** make workspace context server upload dependency chunks sequentially ([#1769](https://github.com/aws/language-servers/issues/1769)) ([c8329e6](https://github.com/aws/language-servers/commit/c8329e6b90be2c24d72a4525b8903384746de2ab))
29
+ * **amazonq:** prevent WCS matching workspaceFolder with only prefix ([#1782](https://github.com/aws/language-servers/issues/1782)) ([988d952](https://github.com/aws/language-servers/commit/988d952485b0f026200a19d17cacd323cd9e359e))
30
+ * **amazonq:** shouldn't exit inline flow before we're sure there is no Edit/Completion trigger ([#1819](https://github.com/aws/language-servers/issues/1819)) ([dc8d89b](https://github.com/aws/language-servers/commit/dc8d89b39ee230aba6cfb032f81bda3476a5cc84))
31
+ * imagecontext image name bug, mutliple images in pinned context ([#1834](https://github.com/aws/language-servers/issues/1834)) ([27d60ab](https://github.com/aws/language-servers/commit/27d60ab5f5249635a9e73be1ee96ecb820133f9a))
32
+ * remove redundent thinking... for file operations ([#1839](https://github.com/aws/language-servers/issues/1839)) ([0078602](https://github.com/aws/language-servers/commit/00786023c9c257c9bb8066c36715864b32b4e069))
33
+ * should always trigger EDIT suggestion if triggering via acceptance ([#1826](https://github.com/aws/language-servers/issues/1826)) ([6c9e522](https://github.com/aws/language-servers/commit/6c9e5225a58d7cf43931d84e7ae63275d6f9c066))
34
+
3
35
  ## [0.0.62](https://github.com/aws/language-servers/compare/lsp-codewhisperer/v0.0.61...lsp-codewhisperer/v0.0.62) (2025-07-02)
4
36
 
5
37
 
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import { ToolUse } from '@amzn/codewhisperer-streaming';
6
6
  import { ChatCommandInput } from '../../shared/streamingClientService';
7
- import { Status, ButtonClickParams, ButtonClickResult, InlineChatResultParams, PromptInputOptionChangeParams, RuleClickParams, ListRulesParams, ActiveEditorChangedParams, PinnedContextParams, ExecuteCommandParams, FollowUpClickParams, OpenFileDialogParams, OpenFileDialogResult } from '@aws/language-server-runtimes/protocol';
7
+ import { Status, ButtonClickParams, ButtonClickResult, InlineChatResultParams, PromptInputOptionChangeParams, RuleClickParams, ListRulesParams, ActiveEditorChangedParams, PinnedContextParams, ExecuteCommandParams, FollowUpClickParams, ListAvailableModelsParams, ListAvailableModelsResult, OpenFileDialogParams, OpenFileDialogResult } from '@aws/language-server-runtimes/protocol';
8
8
  import { FeedbackParams, InsertToCursorPositionParams, InlineChatParams, ConversationClickParams, ListConversationsParams, ListMcpServersParams, McpServerClickParams, TabBarActionParams, CreatePromptParams, FileClickParams } from '@aws/language-server-runtimes/protocol';
9
9
  import { CancellationToken, Chat, ChatParams, ChatResult, EndChatParams, QuickActionParams, ResponseError, TabAddParams, TabRemoveParams, TabChangeParams, InlineChatResult } from '@aws/language-server-runtimes/server-interface';
10
10
  import { Features, LspHandlers } from '../types';
@@ -16,7 +16,7 @@ import { ChatSessionService } from '../chat/chatSessionService';
16
16
  import { AgenticChatResultStream } from './agenticChatResultStream';
17
17
  import { AdditionalContentEntryAddition } from './context/agenticChatTriggerContext';
18
18
  import { PaidTierMode } from '../paidTier/paidTier';
19
- type ChatHandlers = Omit<LspHandlers<Chat>, 'openTab' | 'sendChatUpdate' | 'sendContextCommands' | 'onListConversations' | 'onConversationClick' | 'onListMcpServers' | 'onMcpServerClick' | 'onTabBarAction' | 'getSerializedChat' | 'chatOptionsUpdate' | 'onListMcpServers' | 'onMcpServerClick' | 'onListRules' | 'sendPinnedContext' | 'onActiveEditorChanged' | 'onPinnedContextAdd' | 'onPinnedContextRemove' | 'onOpenFileDialog' | 'onListAvailableModels'>;
19
+ type ChatHandlers = Omit<LspHandlers<Chat>, 'openTab' | 'sendChatUpdate' | 'sendContextCommands' | 'onListConversations' | 'onConversationClick' | 'onListMcpServers' | 'onMcpServerClick' | 'onTabBarAction' | 'getSerializedChat' | 'chatOptionsUpdate' | 'onListRules' | 'sendPinnedContext' | 'onActiveEditorChanged' | 'onPinnedContextAdd' | 'onPinnedContextRemove' | 'onOpenFileDialog' | 'onListAvailableModels'>;
20
20
  export declare class AgenticChatController implements ChatHandlers {
21
21
  #private;
22
22
  constructor(chatSessionManagementService: ChatSessionManagementService, features: Features, telemetryService: TelemetryService, serviceManager?: AmazonQBaseServiceManager);
@@ -42,6 +42,7 @@ export declare class AgenticChatController implements ChatHandlers {
42
42
  list: import("@aws/language-server-runtimes/protocol").DetailedListGroup[];
43
43
  }>;
44
44
  onMcpServerClick(params: McpServerClickParams): Promise<any>;
45
+ onListAvailableModels(params: ListAvailableModelsParams): Promise<ListAvailableModelsResult>;
45
46
  onChatPrompt(params: ChatParams, token: CancellationToken): Promise<ChatResult | ResponseError<ChatResult>>;
46
47
  truncatePinnedContext(remainingCharacterBudget: number, pinnedContext?: AdditionalContentEntryAddition[]): number;
47
48
  /**
@@ -38,12 +38,13 @@ const listDirectory_1 = require("./tools/listDirectory");
38
38
  const fsWrite_1 = require("./tools/fsWrite");
39
39
  const executeBash_1 = require("./tools/executeBash");
40
40
  const toolShared_1 = require("./tools/toolShared");
41
+ const pathValidation_1 = require("./utils/pathValidation");
41
42
  const grepSearch_1 = require("./tools/grepSearch");
42
43
  const fileSearch_1 = require("./tools/fileSearch");
43
44
  const fsReplace_1 = require("./tools/fsReplace");
44
45
  const lsp_core_2 = require("@aws/lsp-core");
45
46
  const diff_1 = require("diff");
46
- const constants_2 = require("./constants");
47
+ const constants_2 = require("./constants/constants");
47
48
  const errors_2 = require("./errors");
48
49
  const vscode_uri_1 = require("vscode-uri");
49
50
  const executeBash_2 = require("./tools/executeBash");
@@ -54,8 +55,10 @@ const mcpManager_1 = require("./tools/mcp/mcpManager");
54
55
  const mcpTool_1 = require("./tools/mcp/mcpTool");
55
56
  const paidTier_1 = require("../paidTier/paidTier");
56
57
  const util_1 = require("./tools/chatDb/util");
58
+ const modelSelection_1 = require("./constants/modelSelection");
57
59
  const imageVerification_1 = require("../../shared/imageVerification");
58
60
  const path_1 = require("@aws/lsp-core/out/util/path");
61
+ const agenticChatControllerHelper_1 = require("./utils/agenticChatControllerHelper");
59
62
  class AgenticChatController {
60
63
  #features;
61
64
  #chatSessionManagementService;
@@ -267,7 +270,7 @@ class AgenticChatController {
267
270
  if (params.fileType === 'image') {
268
271
  // 1. Prompt user for file selection
269
272
  const supportedExtensions = imageVerification_1.DEFAULT_IMAGE_VERIFICATION_OPTIONS.supportedExtensions;
270
- const filters = { 'Image Files': supportedExtensions.map(ext => `*.${ext}`) };
273
+ const filters = { 'Image Files': supportedExtensions };
271
274
  const result = await this.#features.lsp.window.showOpenDialog({
272
275
  canSelectFiles: true,
273
276
  canSelectFolders: false,
@@ -287,7 +290,7 @@ class AgenticChatController {
287
290
  let errorMessage;
288
291
  for (const filePath of result.uris) {
289
292
  // Extract filename from the URI for error messages
290
- const fileName = filePath.split('/').pop() || '';
293
+ const fileName = path.basename(filePath) || '';
291
294
  const sanitizedPath = (0, path_1.sanitize)(filePath);
292
295
  // Get file size and content for verification
293
296
  const size = await this.#features.workspace.fs.getFileSize(sanitizedPath);
@@ -363,6 +366,7 @@ class AgenticChatController {
363
366
  this.#chatHistoryDb.close();
364
367
  this.#contextCommandsProvider?.dispose();
365
368
  this.#userWrittenCodeTracker?.dispose();
369
+ this.#mcpEventHandler.dispose();
366
370
  }
367
371
  async onListConversations(params) {
368
372
  return this.#tabBarController.onListConversations(params);
@@ -382,6 +386,28 @@ class AgenticChatController {
382
386
  async onMcpServerClick(params) {
383
387
  return this.#mcpEventHandler.onMcpServerClick(params);
384
388
  }
389
+ async onListAvailableModels(params) {
390
+ const region = AmazonQTokenServiceManager_1.AmazonQTokenServiceManager.getInstance().getRegion();
391
+ const models = region && modelSelection_1.MODEL_OPTIONS_FOR_REGION[region] ? modelSelection_1.MODEL_OPTIONS_FOR_REGION[region] : modelSelection_1.MODEL_OPTIONS;
392
+ const sessionResult = this.#chatSessionManagementService.getSession(params.tabId);
393
+ const { data: session, success } = sessionResult;
394
+ if (!success) {
395
+ return {
396
+ tabId: params.tabId,
397
+ models: models,
398
+ };
399
+ }
400
+ const savedModelId = this.#chatHistoryDb.getModelId();
401
+ const selectedModelId = savedModelId && models.some(model => model.id === savedModelId)
402
+ ? savedModelId
403
+ : (0, agenticChatControllerHelper_1.getLatestAvailableModel)(region).id;
404
+ session.modelId = selectedModelId;
405
+ return {
406
+ tabId: params.tabId,
407
+ models: models,
408
+ selectedModelId: selectedModelId,
409
+ };
410
+ }
385
411
  async #sendProgressToClient(chunk, partialResultToken) {
386
412
  if (!(0, utils_2.isNullish)(partialResultToken)) {
387
413
  await this.#features.lsp.sendProgress(protocol_1.chatRequestType, partialResultToken, chunk);
@@ -433,6 +459,9 @@ class AgenticChatController {
433
459
  await chatResultStream.updateOngoingProgressResult('Canceled');
434
460
  // Finally, send telemetry/metrics
435
461
  this.#telemetryController.emitInteractWithAgenticChat('StopChat', params.tabId, session.pairProgrammingMode, session.getConversationType());
462
+ metric.setDimension('languageServerVersion', this.#features.runtime.serverInfo.version);
463
+ metric.setDimension('codewhispererCustomizationArn', this.#customizationArn);
464
+ metric.setDimension('enabled', session.pairProgrammingMode);
436
465
  await this.#telemetryController.emitAddMessageMetric(params.tabId, metric.metric, 'Cancelled');
437
466
  });
438
467
  session.setConversationType('AgenticChat');
@@ -455,6 +484,8 @@ class AgenticChatController {
455
484
  const contextItems = [...additionalContext, ...activeFile];
456
485
  triggerContext.documentReference = this.#additionalContextProvider.getFileListFromContext(contextItems);
457
486
  const customContext = await this.#additionalContextProvider.getImageBlocksFromContext(params.context, params.tabId);
487
+ // Add image context to triggerContext.documentReference for transparency
488
+ await this.#additionalContextProvider.appendCustomContextToTriggerContext(triggerContext, params.context, params.tabId);
458
489
  // Get the initial request input
459
490
  const initialRequestInput = await this.#prepareRequestInput(params, session, triggerContext, additionalContext, chatResultStream, customContext);
460
491
  // Generate a unique ID for this prompt
@@ -593,7 +624,7 @@ class AgenticChatController {
593
624
  this.#debug(`LLM Response Latency: ${llmLatency}`);
594
625
  // This is needed to handle the case where the response stream times out
595
626
  // and we want to auto-retry
596
- if (!result.success && result.error.startsWith(constants_2.responseTimeoutPartialMsg)) {
627
+ if (!result.success && result.error.startsWith(constants_2.RESPONSE_TIMEOUT_PARTIAL_MSG)) {
597
628
  const content = 'You took too long to respond - try to split up the work into smaller steps. Do not apologize.';
598
629
  if (this.#isPromptCanceled(token, session, promptId)) {
599
630
  // Only skip adding message to the history DB, continue executing to avoid unexpected stop for the conversation
@@ -648,7 +679,7 @@ class AgenticChatController {
648
679
  if (pendingToolUses.length === 0) {
649
680
  this.recordChunk('agent_loop_done');
650
681
  // No more tool uses, we're done
651
- this.#telemetryController.emitAgencticLoop_InvokeLLM(response.$metadata.requestId, conversationId, 'AgenticChat', undefined, undefined, 'Succeeded', this.#features.runtime.serverInfo.version ?? '', llmLatency, this.#toolCallLatencies, this.#timeToFirstChunk, this.#timeBetweenChunks, session.pairProgrammingMode);
682
+ this.#telemetryController.emitAgencticLoop_InvokeLLM(response.$metadata.requestId, conversationId, 'AgenticChat', undefined, undefined, 'Succeeded', this.#features.runtime.serverInfo.version ?? '', session.modelId, llmLatency, this.#toolCallLatencies, this.#timeToFirstChunk, this.#timeBetweenChunks, session.pairProgrammingMode);
652
683
  finalResult = result;
653
684
  break;
654
685
  }
@@ -669,7 +700,7 @@ class AgenticChatController {
669
700
  metric.setDimension('requestIds', metric.metric.requestIds);
670
701
  const toolNames = this.#toolUseLatencies.map(item => item.toolName);
671
702
  const toolUseIds = this.#toolUseLatencies.map(item => item.toolUseId);
672
- this.#telemetryController.emitAgencticLoop_InvokeLLM(response.$metadata.requestId, conversationId, 'AgenticChatWithToolUse', toolNames ?? undefined, toolUseIds ?? undefined, 'Succeeded', this.#features.runtime.serverInfo.version ?? '', llmLatency, this.#toolCallLatencies, this.#timeToFirstChunk, this.#timeBetweenChunks, session.pairProgrammingMode);
703
+ this.#telemetryController.emitAgencticLoop_InvokeLLM(response.$metadata.requestId, conversationId, 'AgenticChatWithToolUse', toolNames ?? undefined, toolUseIds ?? undefined, 'Succeeded', this.#features.runtime.serverInfo.version ?? '', session.modelId, llmLatency, this.#toolCallLatencies, this.#timeToFirstChunk, this.#timeBetweenChunks, session.pairProgrammingMode);
673
704
  }
674
705
  else {
675
706
  // Send an error card to UI?
@@ -678,7 +709,7 @@ class AgenticChatController {
678
709
  status: codewhisperer_streaming_1.ToolResultStatus.ERROR,
679
710
  content: [{ text: result.error }],
680
711
  }));
681
- this.#telemetryController.emitAgencticLoop_InvokeLLM(response.$metadata.requestId, conversationId, 'AgenticChatWithToolUse', undefined, undefined, 'Failed', this.#features.runtime.serverInfo.version ?? '', llmLatency, this.#toolCallLatencies, this.#timeToFirstChunk, this.#timeBetweenChunks, session.pairProgrammingMode);
712
+ this.#telemetryController.emitAgencticLoop_InvokeLLM(response.$metadata.requestId, conversationId, 'AgenticChatWithToolUse', undefined, undefined, 'Failed', this.#features.runtime.serverInfo.version ?? '', session.modelId, llmLatency, this.#toolCallLatencies, this.#timeToFirstChunk, this.#timeBetweenChunks, session.pairProgrammingMode);
682
713
  if (result.error.startsWith('ToolUse input is invalid JSON:')) {
683
714
  content =
684
715
  'Your toolUse input is incomplete, try again. If the error happens consistently, break this task down into multiple tool uses with smaller input. Do not apologize.';
@@ -724,7 +755,7 @@ class AgenticChatController {
724
755
  */
725
756
  truncateRequest(request, pinnedContext) {
726
757
  // TODO: Confirm if this limit applies to SendMessage and rename this constant
727
- let remainingCharacterBudget = constants_2.generateAssistantResponseInputLimit;
758
+ let remainingCharacterBudget = constants_2.GENERATE_ASSISTANT_RESPONSE_INPUT_LIMIT;
728
759
  if (!request?.conversationState?.currentMessage?.userInputMessage) {
729
760
  return remainingCharacterBudget;
730
761
  }
@@ -732,9 +763,9 @@ class AgenticChatController {
732
763
  // 1. prioritize user input message
733
764
  let truncatedUserInputMessage = '';
734
765
  if (message) {
735
- if (message.length > constants_2.generateAssistantResponseInputLimit) {
736
- this.#debug(`Truncating userInputMessage to ${constants_2.generateAssistantResponseInputLimit} characters}`);
737
- truncatedUserInputMessage = message.substring(0, constants_2.generateAssistantResponseInputLimit);
766
+ if (message.length > constants_2.GENERATE_ASSISTANT_RESPONSE_INPUT_LIMIT) {
767
+ this.#debug(`Truncating userInputMessage to ${constants_2.GENERATE_ASSISTANT_RESPONSE_INPUT_LIMIT} characters}`);
768
+ truncatedUserInputMessage = message.substring(0, constants_2.GENERATE_ASSISTANT_RESPONSE_INPUT_LIMIT);
738
769
  remainingCharacterBudget = remainingCharacterBudget - truncatedUserInputMessage.length;
739
770
  request.conversationState.currentMessage.userInputMessage.content = truncatedUserInputMessage;
740
771
  }
@@ -1135,7 +1166,7 @@ class AgenticChatController {
1135
1166
  #shouldSendBackErrorContent(toolResult) {
1136
1167
  if (toolResult.status === codewhisperer_streaming_1.ToolResultStatus.ERROR) {
1137
1168
  for (const content of toolResult.content ?? []) {
1138
- if (content.json && JSON.stringify(content.json).includes(constants_2.outputLimitExceedsPartialMsg)) {
1169
+ if (content.json && JSON.stringify(content.json).includes(constants_2.OUTPUT_LIMIT_EXCEEDS_PARTIAL_MSG)) {
1139
1170
  // do not send the content response back for this case to avoid unnecessary messages
1140
1171
  return false;
1141
1172
  }
@@ -1230,7 +1261,7 @@ class AgenticChatController {
1230
1261
  }
1231
1262
  if ((result.text && result.text.length > maxToolResponseSize) ||
1232
1263
  (result.json && JSON.stringify(result.json).length > maxToolResponseSize)) {
1233
- throw Error(`${toolUse.name} ${constants_2.outputLimitExceedsPartialMsg} ${maxToolResponseSize}`);
1264
+ throw Error(`${toolUse.name} ${constants_2.OUTPUT_LIMIT_EXCEEDS_PARTIAL_MSG} ${maxToolResponseSize}`);
1234
1265
  }
1235
1266
  }
1236
1267
  /**
@@ -1463,9 +1494,11 @@ class AgenticChatController {
1463
1494
  body = '```shell\n' + commandString;
1464
1495
  break;
1465
1496
  }
1466
- case 'fsReplace':
1467
1497
  case 'fsWrite': {
1468
1498
  const writeFilePath = toolUse.input.path;
1499
+ // Validate the path using our synchronous utility
1500
+ (0, pathValidation_1.validatePathBasic)(writeFilePath);
1501
+ this.#debug(`Processing ${toolUse.name} for path: ${writeFilePath}`);
1469
1502
  buttons = [{ id: 'allow-tools', text: 'Allow', icon: 'ok', status: 'clear' }];
1470
1503
  header = {
1471
1504
  icon: 'warning',
@@ -1477,7 +1510,26 @@ class AgenticChatController {
1477
1510
  };
1478
1511
  body = builtInPermission
1479
1512
  ? `I need permission to modify files.\n\`${writeFilePath}\``
1480
- : `I need permission to modify files in your workspace.\n\`${writeFilePath}\``;
1513
+ : `I need permission to modify files outside of your workspace.\n\`${writeFilePath}\``;
1514
+ break;
1515
+ }
1516
+ case 'fsReplace': {
1517
+ const writeFilePath = toolUse.input.path;
1518
+ // For replace, we need to verify the file exists
1519
+ (0, pathValidation_1.validatePathExists)(writeFilePath);
1520
+ this.#debug(`Processing ${toolUse.name} for path: ${writeFilePath}`);
1521
+ buttons = [{ id: 'allow-tools', text: 'Allow', icon: 'ok', status: 'clear' }];
1522
+ header = {
1523
+ icon: 'warning',
1524
+ iconForegroundStatus: 'warning',
1525
+ body: builtInPermission
1526
+ ? '#### Allow file modification'
1527
+ : '#### Allow file modification outside of your workspace',
1528
+ buttons,
1529
+ };
1530
+ body = builtInPermission
1531
+ ? `I need permission to modify files.\n\`${writeFilePath}\``
1532
+ : `I need permission to modify files outside of your workspace.\n\`${writeFilePath}\``;
1481
1533
  break;
1482
1534
  }
1483
1535
  case 'fsRead':
@@ -1493,6 +1545,9 @@ class AgenticChatController {
1493
1545
  };
1494
1546
  if (toolName === 'fsRead') {
1495
1547
  const paths = toolUse.input.paths;
1548
+ // Validate paths using our synchronous utility
1549
+ (0, pathValidation_1.validatePaths)(paths);
1550
+ this.#debug(`Processing ${toolUse.name} for paths: ${JSON.stringify(paths)}`);
1496
1551
  const formattedPaths = [];
1497
1552
  paths.forEach(element => formattedPaths.push(`\`${element}\``));
1498
1553
  body = builtInPermission
@@ -1501,6 +1556,9 @@ class AgenticChatController {
1501
1556
  }
1502
1557
  else {
1503
1558
  const readFilePath = toolUse.input.path;
1559
+ // Validate the path using our synchronous utility
1560
+ (0, pathValidation_1.validatePathExists)(readFilePath);
1561
+ this.#debug(`Processing ${toolUse.name} for path: ${readFilePath}`);
1504
1562
  body = builtInPermission
1505
1563
  ? `I need permission to list directories.\n\`${readFilePath}\``
1506
1564
  : `I need permission to list directories outside the workspace.\n\`${readFilePath}\``;
@@ -1811,6 +1869,8 @@ class AgenticChatController {
1811
1869
  const requestID = (0, utils_2.getRequestID)(err) ?? '';
1812
1870
  metric.setDimension('cwsprChatResponseCode', (0, utils_2.getHttpStatusCode)(err) ?? 0);
1813
1871
  metric.setDimension('languageServerVersion', this.#features.runtime.serverInfo.version);
1872
+ metric.setDimension('codewhispererCustomizationArn', this.#customizationArn);
1873
+ metric.setDimension('enabled', agenticCodingMode);
1814
1874
  metric.metric.requestIds = [requestID];
1815
1875
  metric.metric.cwsprChatMessageId = errorMessageId;
1816
1876
  metric.metric.cwsprChatConversationId = conversationId;
@@ -1841,7 +1901,7 @@ class AgenticChatController {
1841
1901
  this.#telemetryController.emitMessageResponseError(tabId, metric.metric, requestID, customErrMessage, agenticCodingMode);
1842
1902
  }
1843
1903
  else {
1844
- this.#telemetryController.emitMessageResponseError(tabId, metric.metric, requestID, errorMessage ?? constants_2.genericErrorMsg, agenticCodingMode);
1904
+ this.#telemetryController.emitMessageResponseError(tabId, metric.metric, requestID, errorMessage ?? constants_2.GENERIC_ERROR_MS, agenticCodingMode);
1845
1905
  }
1846
1906
  let authFollowType = undefined;
1847
1907
  // first check if there is an AmazonQ related auth follow up
@@ -1907,7 +1967,7 @@ class AgenticChatController {
1907
1967
  this.#features.logging.error(`Unknown Error: ${lsp_core_2.loggingUtils.formatErr(err)}`);
1908
1968
  return new server_interface_1.ResponseError(server_interface_1.LSPErrorCodes.RequestFailed, err.message, {
1909
1969
  type: 'answer',
1910
- body: requestID ? `${constants_2.genericErrorMsg} \n\nRequest ID: ${requestID}` : constants_2.genericErrorMsg,
1970
+ body: requestID ? `${constants_2.GENERIC_ERROR_MS} \n\nRequest ID: ${requestID}` : constants_2.GENERIC_ERROR_MS,
1911
1971
  messageId: errorMessageId,
1912
1972
  buttons: [],
1913
1973
  });
@@ -2099,11 +2159,13 @@ class AgenticChatController {
2099
2159
  });
2100
2160
  }
2101
2161
  onSourceLinkClick() { }
2102
- onTabAdd(params) {
2103
- this.#telemetryController.activeTabId = params.tabId;
2162
+ /**
2163
+ * @deprecated use aws/chat/listAvailableModels server request instead
2164
+ */
2165
+ #legacySetModelId(tabId, session) {
2104
2166
  // Since model selection is mandatory, the only time modelId is not set is when the chat history is empty.
2105
2167
  // In that case, we use the default modelId.
2106
- let modelId = this.#chatHistoryDb.getModelId() ?? constants_2.defaultModelId;
2168
+ let modelId = this.#chatHistoryDb.getModelId() ?? constants_2.DEFAULT_MODEL_ID;
2107
2169
  const region = AmazonQTokenServiceManager_1.AmazonQTokenServiceManager.getInstance().getRegion();
2108
2170
  if (region === 'eu-central-1') {
2109
2171
  // Only 3.7 Sonnet is available in eu-central-1 for now
@@ -2111,7 +2173,11 @@ class AgenticChatController {
2111
2173
  // @ts-ignore
2112
2174
  this.#features.chat.chatOptionsUpdate({ region });
2113
2175
  }
2114
- this.#features.chat.chatOptionsUpdate({ modelId: modelId, tabId: params.tabId });
2176
+ this.#features.chat.chatOptionsUpdate({ modelId: modelId, tabId: tabId });
2177
+ session.modelId = modelId;
2178
+ }
2179
+ onTabAdd(params) {
2180
+ this.#telemetryController.activeTabId = params.tabId;
2115
2181
  if (!params.restoredTab) {
2116
2182
  this.sendPinnedContext(params.tabId);
2117
2183
  }
@@ -2120,7 +2186,7 @@ class AgenticChatController {
2120
2186
  if (!success) {
2121
2187
  return new server_interface_1.ResponseError(protocol_2.ErrorCodes.InternalError, sessionResult.error);
2122
2188
  }
2123
- session.modelId = modelId;
2189
+ this.#legacySetModelId(params.tabId, session);
2124
2190
  // Get the saved pair programming mode from the database or default to true if not found
2125
2191
  const savedPairProgrammingMode = this.#chatHistoryDb.getPairProgrammingMode();
2126
2192
  session.pairProgrammingMode = savedPairProgrammingMode !== undefined ? savedPairProgrammingMode : true;
@@ -2486,8 +2552,8 @@ class AgenticChatController {
2486
2552
  const timeoutPromise = new Promise((_, reject) => {
2487
2553
  timeoutId = setTimeout(() => {
2488
2554
  abortController.abort();
2489
- reject(new errors_2.AgenticChatError(`${constants_2.responseTimeoutPartialMsg} ${constants_2.responseTimeoutMs}ms`, 'ResponseProcessingTimeout'));
2490
- }, constants_2.responseTimeoutMs);
2555
+ reject(new errors_2.AgenticChatError(`${constants_2.RESPONSE_TIMEOUT_PARTIAL_MSG} ${constants_2.RESPONSE_TIMEOUT_MS}ms`, 'ResponseProcessingTimeout'));
2556
+ }, constants_2.RESPONSE_TIMEOUT_MS);
2491
2557
  });
2492
2558
  const streamWriter = chatResultStream.getResultStreamWriter();
2493
2559
  const processResponsePromise = this.#processAgenticChatResponse(response, metric, chatResultStream, streamWriter, session, contextList, abortController.signal);
@@ -2622,6 +2688,10 @@ class AgenticChatController {
2622
2688
  * nothing is happening.
2623
2689
  */
2624
2690
  async #showLoadingIfRequired(toolUseEvent, streamWriter, toolUseStartTimes, toolUseLoadingTimeouts) {
2691
+ const canWrite = new Set(this.#features.agent.getBuiltInWriteToolNames());
2692
+ if (toolUseEvent.name && canWrite.has(toolUseEvent.name)) {
2693
+ return;
2694
+ }
2625
2695
  const toolUseId = toolUseEvent.toolUseId;
2626
2696
  if (!toolUseEvent.stop && toolUseId) {
2627
2697
  if (!toolUseStartTimes[toolUseId]) {
@@ -2632,9 +2702,9 @@ class AgenticChatController {
2632
2702
  }
2633
2703
  this.#debug(`ToolUseEvent ${toolUseId} started`);
2634
2704
  toolUseLoadingTimeouts[toolUseId] = setTimeout(async () => {
2635
- this.#debug(`ToolUseEvent ${toolUseId} is taking longer than ${constants_2.loadingThresholdMs}ms, showing loading indicator`);
2705
+ this.#debug(`ToolUseEvent ${toolUseId} is taking longer than ${constants_2.LOADING_THRESHOLD_MS}ms, showing loading indicator`);
2636
2706
  await streamWriter.write({ ...constants_1.loadingMessage, messageId: `loading-${toolUseId}` });
2637
- }, constants_2.loadingThresholdMs);
2707
+ }, constants_2.LOADING_THRESHOLD_MS);
2638
2708
  }
2639
2709
  }
2640
2710
  else if (toolUseEvent.stop && toolUseId) {