@aws/lsp-codewhisperer 0.0.61 → 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 +39 -0
  2. package/out/language-server/agenticChat/agenticChatController.d.ts +3 -2
  3. package/out/language-server/agenticChat/agenticChatController.js +96 -30
  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,44 @@
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
+
35
+ ## [0.0.62](https://github.com/aws/language-servers/compare/lsp-codewhisperer/v0.0.61...lsp-codewhisperer/v0.0.62) (2025-07-02)
36
+
37
+
38
+ ### Bug Fixes
39
+
40
+ * **amazonq:** show active file in Context dropdown ([#1815](https://github.com/aws/language-servers/issues/1815)) ([fe1156c](https://github.com/aws/language-servers/commit/fe1156cdd6becbda4b7218cbb06f615a5d919def))
41
+
3
42
  ## [0.0.61](https://github.com/aws/language-servers/compare/lsp-codewhisperer/v0.0.60...lsp-codewhisperer/v0.0.61) (2025-07-02)
4
43
 
5
44
 
@@ -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');
@@ -454,11 +483,9 @@ class AgenticChatController {
454
483
  // Combine additional context with active file and get file list to display at top of response
455
484
  const contextItems = [...additionalContext, ...activeFile];
456
485
  triggerContext.documentReference = this.#additionalContextProvider.getFileListFromContext(contextItems);
457
- if (additionalContext.length) {
458
- triggerContext.documentReference =
459
- this.#additionalContextProvider.getFileListFromContext(additionalContext);
460
- }
461
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);
462
489
  // Get the initial request input
463
490
  const initialRequestInput = await this.#prepareRequestInput(params, session, triggerContext, additionalContext, chatResultStream, customContext);
464
491
  // Generate a unique ID for this prompt
@@ -597,7 +624,7 @@ class AgenticChatController {
597
624
  this.#debug(`LLM Response Latency: ${llmLatency}`);
598
625
  // This is needed to handle the case where the response stream times out
599
626
  // and we want to auto-retry
600
- if (!result.success && result.error.startsWith(constants_2.responseTimeoutPartialMsg)) {
627
+ if (!result.success && result.error.startsWith(constants_2.RESPONSE_TIMEOUT_PARTIAL_MSG)) {
601
628
  const content = 'You took too long to respond - try to split up the work into smaller steps. Do not apologize.';
602
629
  if (this.#isPromptCanceled(token, session, promptId)) {
603
630
  // Only skip adding message to the history DB, continue executing to avoid unexpected stop for the conversation
@@ -652,7 +679,7 @@ class AgenticChatController {
652
679
  if (pendingToolUses.length === 0) {
653
680
  this.recordChunk('agent_loop_done');
654
681
  // No more tool uses, we're done
655
- 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);
656
683
  finalResult = result;
657
684
  break;
658
685
  }
@@ -673,7 +700,7 @@ class AgenticChatController {
673
700
  metric.setDimension('requestIds', metric.metric.requestIds);
674
701
  const toolNames = this.#toolUseLatencies.map(item => item.toolName);
675
702
  const toolUseIds = this.#toolUseLatencies.map(item => item.toolUseId);
676
- 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);
677
704
  }
678
705
  else {
679
706
  // Send an error card to UI?
@@ -682,7 +709,7 @@ class AgenticChatController {
682
709
  status: codewhisperer_streaming_1.ToolResultStatus.ERROR,
683
710
  content: [{ text: result.error }],
684
711
  }));
685
- 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);
686
713
  if (result.error.startsWith('ToolUse input is invalid JSON:')) {
687
714
  content =
688
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.';
@@ -728,7 +755,7 @@ class AgenticChatController {
728
755
  */
729
756
  truncateRequest(request, pinnedContext) {
730
757
  // TODO: Confirm if this limit applies to SendMessage and rename this constant
731
- let remainingCharacterBudget = constants_2.generateAssistantResponseInputLimit;
758
+ let remainingCharacterBudget = constants_2.GENERATE_ASSISTANT_RESPONSE_INPUT_LIMIT;
732
759
  if (!request?.conversationState?.currentMessage?.userInputMessage) {
733
760
  return remainingCharacterBudget;
734
761
  }
@@ -736,9 +763,9 @@ class AgenticChatController {
736
763
  // 1. prioritize user input message
737
764
  let truncatedUserInputMessage = '';
738
765
  if (message) {
739
- if (message.length > constants_2.generateAssistantResponseInputLimit) {
740
- this.#debug(`Truncating userInputMessage to ${constants_2.generateAssistantResponseInputLimit} characters}`);
741
- 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);
742
769
  remainingCharacterBudget = remainingCharacterBudget - truncatedUserInputMessage.length;
743
770
  request.conversationState.currentMessage.userInputMessage.content = truncatedUserInputMessage;
744
771
  }
@@ -1139,7 +1166,7 @@ class AgenticChatController {
1139
1166
  #shouldSendBackErrorContent(toolResult) {
1140
1167
  if (toolResult.status === codewhisperer_streaming_1.ToolResultStatus.ERROR) {
1141
1168
  for (const content of toolResult.content ?? []) {
1142
- 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)) {
1143
1170
  // do not send the content response back for this case to avoid unnecessary messages
1144
1171
  return false;
1145
1172
  }
@@ -1234,7 +1261,7 @@ class AgenticChatController {
1234
1261
  }
1235
1262
  if ((result.text && result.text.length > maxToolResponseSize) ||
1236
1263
  (result.json && JSON.stringify(result.json).length > maxToolResponseSize)) {
1237
- throw Error(`${toolUse.name} ${constants_2.outputLimitExceedsPartialMsg} ${maxToolResponseSize}`);
1264
+ throw Error(`${toolUse.name} ${constants_2.OUTPUT_LIMIT_EXCEEDS_PARTIAL_MSG} ${maxToolResponseSize}`);
1238
1265
  }
1239
1266
  }
1240
1267
  /**
@@ -1467,9 +1494,30 @@ class AgenticChatController {
1467
1494
  body = '```shell\n' + commandString;
1468
1495
  break;
1469
1496
  }
1470
- case 'fsReplace':
1471
1497
  case 'fsWrite': {
1472
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}`);
1502
+ buttons = [{ id: 'allow-tools', text: 'Allow', icon: 'ok', status: 'clear' }];
1503
+ header = {
1504
+ icon: 'warning',
1505
+ iconForegroundStatus: 'warning',
1506
+ body: builtInPermission
1507
+ ? '#### Allow file modification'
1508
+ : '#### Allow file modification outside of your workspace',
1509
+ buttons,
1510
+ };
1511
+ body = builtInPermission
1512
+ ? `I need permission to modify files.\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}`);
1473
1521
  buttons = [{ id: 'allow-tools', text: 'Allow', icon: 'ok', status: 'clear' }];
1474
1522
  header = {
1475
1523
  icon: 'warning',
@@ -1481,7 +1529,7 @@ class AgenticChatController {
1481
1529
  };
1482
1530
  body = builtInPermission
1483
1531
  ? `I need permission to modify files.\n\`${writeFilePath}\``
1484
- : `I need permission to modify files in your workspace.\n\`${writeFilePath}\``;
1532
+ : `I need permission to modify files outside of your workspace.\n\`${writeFilePath}\``;
1485
1533
  break;
1486
1534
  }
1487
1535
  case 'fsRead':
@@ -1497,6 +1545,9 @@ class AgenticChatController {
1497
1545
  };
1498
1546
  if (toolName === 'fsRead') {
1499
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)}`);
1500
1551
  const formattedPaths = [];
1501
1552
  paths.forEach(element => formattedPaths.push(`\`${element}\``));
1502
1553
  body = builtInPermission
@@ -1505,6 +1556,9 @@ class AgenticChatController {
1505
1556
  }
1506
1557
  else {
1507
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}`);
1508
1562
  body = builtInPermission
1509
1563
  ? `I need permission to list directories.\n\`${readFilePath}\``
1510
1564
  : `I need permission to list directories outside the workspace.\n\`${readFilePath}\``;
@@ -1815,6 +1869,8 @@ class AgenticChatController {
1815
1869
  const requestID = (0, utils_2.getRequestID)(err) ?? '';
1816
1870
  metric.setDimension('cwsprChatResponseCode', (0, utils_2.getHttpStatusCode)(err) ?? 0);
1817
1871
  metric.setDimension('languageServerVersion', this.#features.runtime.serverInfo.version);
1872
+ metric.setDimension('codewhispererCustomizationArn', this.#customizationArn);
1873
+ metric.setDimension('enabled', agenticCodingMode);
1818
1874
  metric.metric.requestIds = [requestID];
1819
1875
  metric.metric.cwsprChatMessageId = errorMessageId;
1820
1876
  metric.metric.cwsprChatConversationId = conversationId;
@@ -1845,7 +1901,7 @@ class AgenticChatController {
1845
1901
  this.#telemetryController.emitMessageResponseError(tabId, metric.metric, requestID, customErrMessage, agenticCodingMode);
1846
1902
  }
1847
1903
  else {
1848
- 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);
1849
1905
  }
1850
1906
  let authFollowType = undefined;
1851
1907
  // first check if there is an AmazonQ related auth follow up
@@ -1911,7 +1967,7 @@ class AgenticChatController {
1911
1967
  this.#features.logging.error(`Unknown Error: ${lsp_core_2.loggingUtils.formatErr(err)}`);
1912
1968
  return new server_interface_1.ResponseError(server_interface_1.LSPErrorCodes.RequestFailed, err.message, {
1913
1969
  type: 'answer',
1914
- 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,
1915
1971
  messageId: errorMessageId,
1916
1972
  buttons: [],
1917
1973
  });
@@ -2103,11 +2159,13 @@ class AgenticChatController {
2103
2159
  });
2104
2160
  }
2105
2161
  onSourceLinkClick() { }
2106
- onTabAdd(params) {
2107
- this.#telemetryController.activeTabId = params.tabId;
2162
+ /**
2163
+ * @deprecated use aws/chat/listAvailableModels server request instead
2164
+ */
2165
+ #legacySetModelId(tabId, session) {
2108
2166
  // Since model selection is mandatory, the only time modelId is not set is when the chat history is empty.
2109
2167
  // In that case, we use the default modelId.
2110
- let modelId = this.#chatHistoryDb.getModelId() ?? constants_2.defaultModelId;
2168
+ let modelId = this.#chatHistoryDb.getModelId() ?? constants_2.DEFAULT_MODEL_ID;
2111
2169
  const region = AmazonQTokenServiceManager_1.AmazonQTokenServiceManager.getInstance().getRegion();
2112
2170
  if (region === 'eu-central-1') {
2113
2171
  // Only 3.7 Sonnet is available in eu-central-1 for now
@@ -2115,7 +2173,11 @@ class AgenticChatController {
2115
2173
  // @ts-ignore
2116
2174
  this.#features.chat.chatOptionsUpdate({ region });
2117
2175
  }
2118
- 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;
2119
2181
  if (!params.restoredTab) {
2120
2182
  this.sendPinnedContext(params.tabId);
2121
2183
  }
@@ -2124,7 +2186,7 @@ class AgenticChatController {
2124
2186
  if (!success) {
2125
2187
  return new server_interface_1.ResponseError(protocol_2.ErrorCodes.InternalError, sessionResult.error);
2126
2188
  }
2127
- session.modelId = modelId;
2189
+ this.#legacySetModelId(params.tabId, session);
2128
2190
  // Get the saved pair programming mode from the database or default to true if not found
2129
2191
  const savedPairProgrammingMode = this.#chatHistoryDb.getPairProgrammingMode();
2130
2192
  session.pairProgrammingMode = savedPairProgrammingMode !== undefined ? savedPairProgrammingMode : true;
@@ -2490,8 +2552,8 @@ class AgenticChatController {
2490
2552
  const timeoutPromise = new Promise((_, reject) => {
2491
2553
  timeoutId = setTimeout(() => {
2492
2554
  abortController.abort();
2493
- reject(new errors_2.AgenticChatError(`${constants_2.responseTimeoutPartialMsg} ${constants_2.responseTimeoutMs}ms`, 'ResponseProcessingTimeout'));
2494
- }, 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);
2495
2557
  });
2496
2558
  const streamWriter = chatResultStream.getResultStreamWriter();
2497
2559
  const processResponsePromise = this.#processAgenticChatResponse(response, metric, chatResultStream, streamWriter, session, contextList, abortController.signal);
@@ -2626,6 +2688,10 @@ class AgenticChatController {
2626
2688
  * nothing is happening.
2627
2689
  */
2628
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
+ }
2629
2695
  const toolUseId = toolUseEvent.toolUseId;
2630
2696
  if (!toolUseEvent.stop && toolUseId) {
2631
2697
  if (!toolUseStartTimes[toolUseId]) {
@@ -2636,9 +2702,9 @@ class AgenticChatController {
2636
2702
  }
2637
2703
  this.#debug(`ToolUseEvent ${toolUseId} started`);
2638
2704
  toolUseLoadingTimeouts[toolUseId] = setTimeout(async () => {
2639
- 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`);
2640
2706
  await streamWriter.write({ ...constants_1.loadingMessage, messageId: `loading-${toolUseId}` });
2641
- }, constants_2.loadingThresholdMs);
2707
+ }, constants_2.LOADING_THRESHOLD_MS);
2642
2708
  }
2643
2709
  }
2644
2710
  else if (toolUseEvent.stop && toolUseId) {