@aws/lsp-codewhisperer 0.0.64 → 0.0.66
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.
- package/CHANGELOG.md +57 -0
- package/out/client/token/bearer-token-service.json +1 -1
- package/out/language-server/agenticChat/agenticChatController.d.ts +13 -0
- package/out/language-server/agenticChat/agenticChatController.js +547 -125
- package/out/language-server/agenticChat/agenticChatController.js.map +1 -1
- package/out/language-server/agenticChat/constants/constants.d.ts +12 -0
- package/out/language-server/agenticChat/constants/constants.js +73 -1
- package/out/language-server/agenticChat/constants/constants.js.map +1 -1
- package/out/language-server/agenticChat/constants/toolConstants.d.ts +24 -0
- package/out/language-server/agenticChat/constants/toolConstants.js +35 -0
- package/out/language-server/agenticChat/constants/toolConstants.js.map +1 -0
- package/out/language-server/agenticChat/context/additionalContextProvider.d.ts +1 -1
- package/out/language-server/agenticChat/context/additionalContextProvider.js +44 -4
- package/out/language-server/agenticChat/context/additionalContextProvider.js.map +1 -1
- package/out/language-server/agenticChat/context/agenticChatTriggerContext.d.ts +11 -2
- package/out/language-server/agenticChat/context/agenticChatTriggerContext.js +32 -2
- package/out/language-server/agenticChat/context/agenticChatTriggerContext.js.map +1 -1
- package/out/language-server/agenticChat/context/contextCommandsProvider.js +1 -1
- package/out/language-server/agenticChat/context/contextCommandsProvider.js.map +1 -1
- package/out/language-server/agenticChat/context/contextUtils.d.ts +16 -0
- package/out/language-server/agenticChat/context/contextUtils.js +29 -0
- package/out/language-server/agenticChat/context/contextUtils.js.map +1 -1
- package/out/language-server/agenticChat/qAgenticChatServer.js +6 -2
- package/out/language-server/agenticChat/qAgenticChatServer.js.map +1 -1
- package/out/language-server/agenticChat/tools/chatDb/chatDb.d.ts +11 -3
- package/out/language-server/agenticChat/tools/chatDb/chatDb.js +103 -31
- package/out/language-server/agenticChat/tools/chatDb/chatDb.js.map +1 -1
- package/out/language-server/agenticChat/tools/chatDb/util.d.ts +4 -0
- package/out/language-server/agenticChat/tools/chatDb/util.js.map +1 -1
- package/out/language-server/agenticChat/tools/executeBash.d.ts +15 -5
- package/out/language-server/agenticChat/tools/executeBash.js +97 -22
- package/out/language-server/agenticChat/tools/executeBash.js.map +1 -1
- package/out/language-server/agenticChat/tools/mcp/mcpManager.js +1 -1
- package/out/language-server/agenticChat/tools/mcp/mcpManager.js.map +1 -1
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReview.d.ts +211 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReview.js +630 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReview.js.map +1 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReviewConstants.d.ts +34 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReviewConstants.js +200 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReviewConstants.js.map +1 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReviewErrors.d.ts +12 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReviewErrors.js +32 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReviewErrors.js.map +1 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReviewSchemas.d.ts +289 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReviewSchemas.js +140 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReviewSchemas.js.map +1 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReviewTypes.d.ts +58 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReviewTypes.js +3 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReviewTypes.js.map +1 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReviewUtils.d.ts +156 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReviewUtils.js +363 -0
- package/out/language-server/agenticChat/tools/qCodeAnalysis/qCodeReviewUtils.js.map +1 -0
- package/out/language-server/agenticChat/tools/toolServer.d.ts +1 -0
- package/out/language-server/agenticChat/tools/toolServer.js +90 -39
- package/out/language-server/agenticChat/tools/toolServer.js.map +1 -1
- package/out/language-server/chat/chatSessionService.d.ts +1 -0
- package/out/language-server/chat/chatSessionService.js +5 -2
- package/out/language-server/chat/chatSessionService.js.map +1 -1
- package/out/language-server/chat/quickActions.d.ts +7 -1
- package/out/language-server/chat/quickActions.js +7 -1
- package/out/language-server/chat/quickActions.js.map +1 -1
- package/out/language-server/chat/telemetry/chatTelemetryController.d.ts +4 -3
- package/out/language-server/chat/telemetry/chatTelemetryController.js +22 -3
- package/out/language-server/chat/telemetry/chatTelemetryController.js.map +1 -1
- package/out/language-server/configuration/qConfigurationServer.d.ts +1 -0
- package/out/language-server/configuration/qConfigurationServer.js.map +1 -1
- package/out/language-server/inline-completion/auto-trigger/autoTrigger.d.ts +2 -0
- package/out/language-server/inline-completion/auto-trigger/autoTrigger.js +69 -1
- package/out/language-server/inline-completion/auto-trigger/autoTrigger.js.map +1 -1
- package/out/language-server/inline-completion/auto-trigger/editPredictionAutoTrigger.js +6 -1
- package/out/language-server/inline-completion/auto-trigger/editPredictionAutoTrigger.js.map +1 -1
- package/out/language-server/inline-completion/auto-trigger/editPredictionConfig.d.ts +1 -0
- package/out/language-server/inline-completion/auto-trigger/editPredictionConfig.js +1 -0
- package/out/language-server/inline-completion/auto-trigger/editPredictionConfig.js.map +1 -1
- package/out/language-server/inline-completion/codeWhispererServer.js +72 -43
- package/out/language-server/inline-completion/codeWhispererServer.js.map +1 -1
- package/out/language-server/workspaceContext/artifactManager.d.ts +4 -1
- package/out/language-server/workspaceContext/artifactManager.js +16 -1
- package/out/language-server/workspaceContext/artifactManager.js.map +1 -1
- package/out/language-server/workspaceContext/dependency/dependencyDiscoverer.d.ts +2 -1
- package/out/language-server/workspaceContext/dependency/dependencyDiscoverer.js +9 -6
- package/out/language-server/workspaceContext/dependency/dependencyDiscoverer.js.map +1 -1
- package/out/language-server/workspaceContext/dependency/dependencyHandler/LanguageDependencyHandler.d.ts +7 -2
- package/out/language-server/workspaceContext/dependency/dependencyHandler/LanguageDependencyHandler.js +20 -7
- package/out/language-server/workspaceContext/dependency/dependencyHandler/LanguageDependencyHandler.js.map +1 -1
- package/out/language-server/workspaceContext/dependency/dependencyHandler/LanguageDependencyHandlerFactory.d.ts +2 -2
- package/out/language-server/workspaceContext/dependency/dependencyHandler/LanguageDependencyHandlerFactory.js +4 -4
- package/out/language-server/workspaceContext/dependency/dependencyHandler/LanguageDependencyHandlerFactory.js.map +1 -1
- package/out/language-server/workspaceContext/fileUploadJobManager.js +3 -1
- package/out/language-server/workspaceContext/fileUploadJobManager.js.map +1 -1
- package/out/language-server/workspaceContext/workspaceContextServer.js +32 -19
- package/out/language-server/workspaceContext/workspaceContextServer.js.map +1 -1
- package/out/language-server/workspaceContext/workspaceFolderManager.d.ts +5 -3
- package/out/language-server/workspaceContext/workspaceFolderManager.js +80 -59
- package/out/language-server/workspaceContext/workspaceFolderManager.js.map +1 -1
- package/out/shared/activeUserTracker.d.ts +52 -0
- package/out/shared/activeUserTracker.js +164 -0
- package/out/shared/activeUserTracker.js.map +1 -0
- package/out/shared/amazonQServiceManager/configurationUtils.js +6 -0
- package/out/shared/amazonQServiceManager/configurationUtils.js.map +1 -1
- package/out/shared/codeWhispererService.js +3 -1
- package/out/shared/codeWhispererService.js.map +1 -1
- package/out/shared/telemetry/telemetryService.d.ts +2 -0
- package/out/shared/telemetry/telemetryService.js +2 -0
- package/out/shared/telemetry/telemetryService.js.map +1 -1
- package/out/shared/telemetry/types.d.ts +9 -1
- package/out/shared/telemetry/types.js +1 -0
- package/out/shared/telemetry/types.js.map +1 -1
- package/package.json +8 -6
|
@@ -7,7 +7,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
exports.AgenticChatController = void 0;
|
|
8
8
|
const crypto = require("crypto");
|
|
9
9
|
const path = require("path");
|
|
10
|
+
const os = require("os");
|
|
10
11
|
const codewhisperer_streaming_1 = require("@amzn/codewhisperer-streaming");
|
|
12
|
+
const toolConstants_1 = require("./constants/toolConstants");
|
|
11
13
|
const protocol_1 = require("@aws/language-server-runtimes/protocol");
|
|
12
14
|
const protocol_2 = require("@aws/language-server-runtimes/protocol");
|
|
13
15
|
const server_interface_1 = require("@aws/language-server-runtimes/server-interface");
|
|
@@ -49,6 +51,8 @@ const errors_2 = require("./errors");
|
|
|
49
51
|
const vscode_uri_1 = require("vscode-uri");
|
|
50
52
|
const executeBash_2 = require("./tools/executeBash");
|
|
51
53
|
const userWrittenCodeTracker_1 = require("../../shared/userWrittenCodeTracker");
|
|
54
|
+
const qCodeReview_1 = require("./tools/qCodeAnalysis/qCodeReview");
|
|
55
|
+
const qCodeReviewConstants_1 = require("./tools/qCodeAnalysis/qCodeReviewConstants");
|
|
52
56
|
const mcpEventHandler_1 = require("./tools/mcp/mcpEventHandler");
|
|
53
57
|
const mcpUtils_1 = require("./tools/mcp/mcpUtils");
|
|
54
58
|
const mcpManager_1 = require("./tools/mcp/mcpManager");
|
|
@@ -59,6 +63,7 @@ const modelSelection_1 = require("./constants/modelSelection");
|
|
|
59
63
|
const imageVerification_1 = require("../../shared/imageVerification");
|
|
60
64
|
const path_1 = require("@aws/lsp-core/out/util/path");
|
|
61
65
|
const agenticChatControllerHelper_1 = require("./utils/agenticChatControllerHelper");
|
|
66
|
+
const activeUserTracker_1 = require("../../shared/activeUserTracker");
|
|
62
67
|
class AgenticChatController {
|
|
63
68
|
#features;
|
|
64
69
|
#chatSessionManagementService;
|
|
@@ -78,6 +83,7 @@ class AgenticChatController {
|
|
|
78
83
|
#mcpEventHandler;
|
|
79
84
|
#paidTierMode;
|
|
80
85
|
#origin;
|
|
86
|
+
#activeUserTracker;
|
|
81
87
|
// latency metrics
|
|
82
88
|
#llmRequestStartTime = 0;
|
|
83
89
|
#toolCallLatencies = [];
|
|
@@ -85,6 +91,9 @@ class AgenticChatController {
|
|
|
85
91
|
#timeToFirstChunk = -1;
|
|
86
92
|
#timeBetweenChunks = [];
|
|
87
93
|
#lastChunkTime = 0;
|
|
94
|
+
// A/B testing allocation
|
|
95
|
+
#abTestingFetchingTimeout;
|
|
96
|
+
#abTestingAllocation;
|
|
88
97
|
/**
|
|
89
98
|
* Determines the appropriate message ID for a tool use based on tool type and name
|
|
90
99
|
* @param toolType The type of tool being used
|
|
@@ -94,7 +103,34 @@ class AgenticChatController {
|
|
|
94
103
|
#getMessageIdForToolUse(toolType, toolUse) {
|
|
95
104
|
const toolUseId = toolUse.toolUseId;
|
|
96
105
|
// Return plain toolUseId for executeBash, add "_permission" suffix for all other tools
|
|
97
|
-
return toolUse.name ===
|
|
106
|
+
return toolUse.name === toolConstants_1.EXECUTE_BASH || toolType === toolConstants_1.EXECUTE_BASH
|
|
107
|
+
? toolUseId
|
|
108
|
+
: `${toolUseId}${toolConstants_1.SUFFIX_PERMISSION}`;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Logs system information that can be helpful for debugging customer issues
|
|
112
|
+
*/
|
|
113
|
+
logSystemInformation() {
|
|
114
|
+
const clientInfo = this.#features.lsp.getClientInitializeParams()?.clientInfo;
|
|
115
|
+
const systemInfo = {
|
|
116
|
+
languageServerVersion: this.#features.runtime.serverInfo.version ?? 'unknown',
|
|
117
|
+
clientName: clientInfo?.name ?? 'unknown',
|
|
118
|
+
clientVersion: clientInfo?.version ?? 'unknown',
|
|
119
|
+
OS: os.platform(),
|
|
120
|
+
OSVersion: os.release(),
|
|
121
|
+
ComputeEnv: process.env.COMPUTE_ENV ?? 'unknown',
|
|
122
|
+
extensionVersion: this.#features.lsp.getClientInitializeParams()?.initializationOptions?.aws?.clientInfo?.extension
|
|
123
|
+
?.version,
|
|
124
|
+
};
|
|
125
|
+
this.#features.logging.info(`System Information: ${JSON.stringify(systemInfo)}`);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Determines the appropriate message ID for a compaction confirmation
|
|
129
|
+
* @param messageId The original messageId
|
|
130
|
+
* @returns The message ID to use
|
|
131
|
+
*/
|
|
132
|
+
#getMessageIdForCompact(messageId) {
|
|
133
|
+
return `${messageId}_compact`;
|
|
98
134
|
}
|
|
99
135
|
constructor(chatSessionManagementService, features, telemetryService, serviceManager) {
|
|
100
136
|
this.#features = features;
|
|
@@ -113,6 +149,7 @@ class AgenticChatController {
|
|
|
113
149
|
this.#contextCommandsProvider = new contextCommandsProvider_1.ContextCommandsProvider(this.#features.logging, this.#features.chat, this.#features.workspace, this.#features.lsp);
|
|
114
150
|
this.#mcpEventHandler = new mcpEventHandler_1.McpEventHandler(features, telemetryService);
|
|
115
151
|
this.#origin = (0, utils_2.getOriginFromClientInfo)(this.#features.lsp.getClientInitializeParams()?.clientInfo?.name);
|
|
152
|
+
this.#activeUserTracker = activeUserTracker_1.ActiveUserTracker.getInstance(this.#features);
|
|
116
153
|
}
|
|
117
154
|
async onExecuteCommand(params, _token) {
|
|
118
155
|
this.#log(`onExecuteCommand: ${params.command}`);
|
|
@@ -129,16 +166,16 @@ class AgenticChatController {
|
|
|
129
166
|
async onButtonClick(params) {
|
|
130
167
|
this.#log(`onButtonClick event with params: ${JSON.stringify(params)}`);
|
|
131
168
|
const session = this.#chatSessionManagementService.getSession(params.tabId);
|
|
132
|
-
if (params.buttonId ===
|
|
133
|
-
params.buttonId ===
|
|
134
|
-
params.buttonId ===
|
|
135
|
-
params.buttonId ===
|
|
169
|
+
if (params.buttonId === toolConstants_1.BUTTON_RUN_SHELL_COMMAND ||
|
|
170
|
+
params.buttonId === toolConstants_1.BUTTON_REJECT_SHELL_COMMAND ||
|
|
171
|
+
params.buttonId === toolConstants_1.BUTTON_REJECT_MCP_TOOL ||
|
|
172
|
+
params.buttonId === toolConstants_1.BUTTON_ALLOW_TOOLS) {
|
|
136
173
|
if (!session.data) {
|
|
137
174
|
return { success: false, failureReason: `could not find chat session for tab: ${params.tabId} ` };
|
|
138
175
|
}
|
|
139
176
|
// For 'allow-tools', remove suffix as permission card needs to be seperate from file list card
|
|
140
|
-
const messageId = params.buttonId ===
|
|
141
|
-
? params.messageId.replace(
|
|
177
|
+
const messageId = params.buttonId === toolConstants_1.BUTTON_ALLOW_TOOLS && params.messageId.endsWith(toolConstants_1.SUFFIX_PERMISSION)
|
|
178
|
+
? params.messageId.replace(toolConstants_1.SUFFIX_PERMISSION, '')
|
|
142
179
|
: params.messageId;
|
|
143
180
|
const handler = session.data.getDeferredToolExecution(messageId);
|
|
144
181
|
if (!handler?.reject || !handler.resolve) {
|
|
@@ -147,7 +184,7 @@ class AgenticChatController {
|
|
|
147
184
|
failureReason: `could not find deferred tool execution for message: ${messageId} `,
|
|
148
185
|
};
|
|
149
186
|
}
|
|
150
|
-
params.buttonId ===
|
|
187
|
+
params.buttonId === toolConstants_1.BUTTON_REJECT_SHELL_COMMAND || params.buttonId === toolConstants_1.BUTTON_REJECT_MCP_TOOL
|
|
151
188
|
? (() => {
|
|
152
189
|
handler.reject(new toolShared_1.ToolApprovalException('Command was rejected.', true));
|
|
153
190
|
this.#stoppedToolUses.add(messageId);
|
|
@@ -157,12 +194,12 @@ class AgenticChatController {
|
|
|
157
194
|
success: true,
|
|
158
195
|
};
|
|
159
196
|
}
|
|
160
|
-
else if (params.buttonId ===
|
|
197
|
+
else if (params.buttonId === toolConstants_1.BUTTON_UNDO_CHANGES) {
|
|
161
198
|
const toolUseId = params.messageId;
|
|
162
199
|
try {
|
|
163
200
|
await this.#undoFileChange(toolUseId, session.data);
|
|
164
201
|
this.#updateUndoButtonAfterClick(params.tabId, toolUseId, session.data);
|
|
165
|
-
this.#telemetryController.emitInteractWithAgenticChat('RejectDiff', params.tabId, session.data?.pairProgrammingMode, session.data?.getConversationType());
|
|
202
|
+
this.#telemetryController.emitInteractWithAgenticChat('RejectDiff', params.tabId, session.data?.pairProgrammingMode, session.data?.getConversationType(), this.#abTestingAllocation?.experimentName, this.#abTestingAllocation?.userVariation);
|
|
166
203
|
}
|
|
167
204
|
catch (err) {
|
|
168
205
|
return { success: false, failureReason: err.message };
|
|
@@ -171,23 +208,23 @@ class AgenticChatController {
|
|
|
171
208
|
success: true,
|
|
172
209
|
};
|
|
173
210
|
}
|
|
174
|
-
else if (params.buttonId ===
|
|
175
|
-
const toolUseId = params.messageId.replace(
|
|
211
|
+
else if (params.buttonId === toolConstants_1.BUTTON_UNDO_ALL_CHANGES) {
|
|
212
|
+
const toolUseId = params.messageId.replace(toolConstants_1.SUFFIX_UNDOALL, '');
|
|
176
213
|
await this.#undoAllFileChanges(params.tabId, toolUseId, session.data);
|
|
177
214
|
return {
|
|
178
215
|
success: true,
|
|
179
216
|
};
|
|
180
217
|
}
|
|
181
|
-
else if (params.buttonId ===
|
|
218
|
+
else if (params.buttonId === toolConstants_1.BUTTON_STOP_SHELL_COMMAND) {
|
|
182
219
|
this.#stoppedToolUses.add(params.messageId);
|
|
183
220
|
await this.#renderStoppedShellCommand(params.tabId, params.messageId);
|
|
184
221
|
return { success: true };
|
|
185
222
|
}
|
|
186
|
-
else if (params.buttonId ===
|
|
223
|
+
else if (params.buttonId === toolConstants_1.BUTTON_PAIDTIER_UPGRADE_Q_LEARNMORE) {
|
|
187
224
|
(0, paidTier_1.onPaidTierLearnMore)(this.#features.lsp, this.#features.logging);
|
|
188
225
|
return { success: true };
|
|
189
226
|
}
|
|
190
|
-
else if (params.buttonId ===
|
|
227
|
+
else if (params.buttonId === toolConstants_1.BUTTON_PAIDTIER_UPGRADE_Q) {
|
|
191
228
|
await this.onManageSubscription(params.tabId);
|
|
192
229
|
return { success: true };
|
|
193
230
|
}
|
|
@@ -219,7 +256,7 @@ class AgenticChatController {
|
|
|
219
256
|
return;
|
|
220
257
|
}
|
|
221
258
|
const fileList = cachedToolUse.chatResult?.header?.fileList;
|
|
222
|
-
const button = cachedToolUse.chatResult?.header?.buttons?.filter(button => button.id !==
|
|
259
|
+
const button = cachedToolUse.chatResult?.header?.buttons?.filter(button => button.id !== toolConstants_1.BUTTON_UNDO_CHANGES);
|
|
223
260
|
const updatedHeader = {
|
|
224
261
|
...cachedToolUse.chatResult?.header,
|
|
225
262
|
buttons: button,
|
|
@@ -267,7 +304,7 @@ class AgenticChatController {
|
|
|
267
304
|
return;
|
|
268
305
|
}
|
|
269
306
|
for (const messageId of [...toUndo].reverse()) {
|
|
270
|
-
await this.onButtonClick({ buttonId:
|
|
307
|
+
await this.onButtonClick({ buttonId: toolConstants_1.BUTTON_UNDO_CHANGES, messageId, tabId });
|
|
271
308
|
}
|
|
272
309
|
}
|
|
273
310
|
async onOpenFileDialog(params, token) {
|
|
@@ -371,6 +408,8 @@ class AgenticChatController {
|
|
|
371
408
|
this.#contextCommandsProvider?.dispose();
|
|
372
409
|
this.#userWrittenCodeTracker?.dispose();
|
|
373
410
|
this.#mcpEventHandler.dispose();
|
|
411
|
+
this.#activeUserTracker.dispose();
|
|
412
|
+
clearInterval(this.#abTestingFetchingTimeout);
|
|
374
413
|
}
|
|
375
414
|
async onListConversations(params) {
|
|
376
415
|
return this.#tabBarController.onListConversations(params);
|
|
@@ -433,11 +472,19 @@ class AgenticChatController {
|
|
|
433
472
|
if (!success) {
|
|
434
473
|
return new server_interface_1.ResponseError(protocol_2.ErrorCodes.InternalError, sessionResult.error);
|
|
435
474
|
}
|
|
475
|
+
const compactIds = session.getAllDeferredCompactMessageIds();
|
|
476
|
+
await this.#invalidateCompactCommand(params.tabId, compactIds);
|
|
436
477
|
session.rejectAllDeferredToolExecutions(new toolShared_1.ToolApprovalException('Command ignored: new prompt', false));
|
|
437
478
|
await this.#invalidateAllShellCommands(params.tabId, session);
|
|
438
479
|
const metric = new metric_1.Metric({
|
|
439
480
|
cwsprChatConversationType: 'AgenticChat',
|
|
481
|
+
experimentName: this.#abTestingAllocation?.experimentName,
|
|
482
|
+
userVariation: this.#abTestingAllocation?.userVariation,
|
|
440
483
|
});
|
|
484
|
+
const isNewActiveUser = this.#activeUserTracker.isNewActiveUser();
|
|
485
|
+
if (isNewActiveUser) {
|
|
486
|
+
this.#telemetryController.emitActiveUser();
|
|
487
|
+
}
|
|
441
488
|
try {
|
|
442
489
|
const triggerContext = await this.#getTriggerContext(params, metric);
|
|
443
490
|
if (triggerContext.programmingLanguage?.languageName) {
|
|
@@ -456,13 +503,15 @@ class AgenticChatController {
|
|
|
456
503
|
this.#log('cancellation requested');
|
|
457
504
|
// Abort all operations immediately
|
|
458
505
|
session.abortRequest();
|
|
506
|
+
const compactIds = session.getAllDeferredCompactMessageIds();
|
|
507
|
+
await this.#invalidateCompactCommand(params.tabId, compactIds);
|
|
459
508
|
void this.#invalidateAllShellCommands(params.tabId, session);
|
|
460
509
|
session.rejectAllDeferredToolExecutions(new lsp_core_1.CancellationError('user'));
|
|
461
510
|
// Then update UI to inform the user
|
|
462
511
|
await this.#showUndoAllIfRequired(chatResultStream, session);
|
|
463
512
|
await chatResultStream.updateOngoingProgressResult('Canceled');
|
|
464
513
|
// Finally, send telemetry/metrics
|
|
465
|
-
this.#telemetryController.emitInteractWithAgenticChat('StopChat', params.tabId, session.pairProgrammingMode, session.getConversationType());
|
|
514
|
+
this.#telemetryController.emitInteractWithAgenticChat('StopChat', params.tabId, session.pairProgrammingMode, session.getConversationType(), this.#abTestingAllocation?.experimentName, this.#abTestingAllocation?.userVariation);
|
|
466
515
|
metric.setDimension('languageServerVersion', this.#features.runtime.serverInfo.version);
|
|
467
516
|
metric.setDimension('codewhispererCustomizationArn', this.#customizationArn);
|
|
468
517
|
metric.setDimension('enabled', session.pairProgrammingMode);
|
|
@@ -470,8 +519,11 @@ class AgenticChatController {
|
|
|
470
519
|
});
|
|
471
520
|
session.setConversationType('AgenticChat');
|
|
472
521
|
const additionalContext = await this.#additionalContextProvider.getAdditionalContext(triggerContext, params.tabId, params.context);
|
|
473
|
-
// Add active file to context list if it
|
|
474
|
-
const activeFile = triggerContext.text &&
|
|
522
|
+
// Add active file to context list if it's not already there
|
|
523
|
+
const activeFile = triggerContext.text &&
|
|
524
|
+
triggerContext.relativeFilePath &&
|
|
525
|
+
triggerContext.activeFilePath &&
|
|
526
|
+
!additionalContext.some(item => item.path === triggerContext.activeFilePath)
|
|
475
527
|
? [
|
|
476
528
|
{
|
|
477
529
|
name: path.basename(triggerContext.relativeFilePath),
|
|
@@ -490,19 +542,31 @@ class AgenticChatController {
|
|
|
490
542
|
const customContext = await this.#additionalContextProvider.getImageBlocksFromContext(params.context, params.tabId);
|
|
491
543
|
// Add image context to triggerContext.documentReference for transparency
|
|
492
544
|
await this.#additionalContextProvider.appendCustomContextToTriggerContext(triggerContext, params.context, params.tabId);
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
545
|
+
let finalResult;
|
|
546
|
+
if (params.prompt.command === quickActions_1.QuickAction.Compact) {
|
|
547
|
+
// Get the compaction request input
|
|
548
|
+
const compactionRequestInput = this.#getCompactionRequestInput(session);
|
|
549
|
+
// Generate a unique ID for this prompt
|
|
550
|
+
const promptId = crypto.randomUUID();
|
|
551
|
+
session.setCurrentPromptId(promptId);
|
|
552
|
+
// Start the compaction call
|
|
553
|
+
finalResult = await this.#runCompaction(compactionRequestInput, session, metric, chatResultStream, params.tabId, promptId, session.conversationId, token, triggerContext.documentReference);
|
|
554
|
+
}
|
|
555
|
+
else {
|
|
556
|
+
// Get the initial request input
|
|
557
|
+
const initialRequestInput = await this.#prepareRequestInput(params, session, triggerContext, additionalContext, chatResultStream, customContext);
|
|
558
|
+
// Generate a unique ID for this prompt
|
|
559
|
+
const promptId = crypto.randomUUID();
|
|
560
|
+
session.setCurrentPromptId(promptId);
|
|
561
|
+
// Start the agent loop
|
|
562
|
+
finalResult = await this.#runAgentLoop(initialRequestInput, session, metric, chatResultStream, params.tabId, promptId, session.conversationId, token, triggerContext.documentReference, additionalContext.filter(item => item.pinned));
|
|
563
|
+
}
|
|
564
|
+
// Result Handling - This happens only once
|
|
565
|
+
return await this.#handleFinalResult(finalResult, session, params.tabId, metric, triggerContext, isNewConversation, chatResultStream);
|
|
502
566
|
}
|
|
503
567
|
catch (err) {
|
|
504
568
|
// HACK: the chat-client needs to have a partial event with the associated messageId sent before it can accept the final result.
|
|
505
|
-
// Without this, the `
|
|
569
|
+
// Without this, the `working` indicator never goes away.
|
|
506
570
|
// Note: buttons being explicitly empty is required for this hack to work.
|
|
507
571
|
const errorMessageId = `error-message-id-${(0, uuid_1.v4)()}`;
|
|
508
572
|
await this.#sendProgressToClient({
|
|
@@ -534,9 +598,131 @@ class AgenticChatController {
|
|
|
534
598
|
this.#debug('Preparing request input');
|
|
535
599
|
// Get profileArn from the service manager if available
|
|
536
600
|
const profileArn = this.#serviceManager?.getActiveProfileArn();
|
|
537
|
-
const requestInput = await this.#triggerContext.getChatParamsFromTrigger(params, triggerContext, codewhisperer_streaming_1.ChatTriggerType.MANUAL, this.#customizationArn, chatResultStream, profileArn,
|
|
601
|
+
const requestInput = await this.#triggerContext.getChatParamsFromTrigger(params, triggerContext, codewhisperer_streaming_1.ChatTriggerType.MANUAL, this.#customizationArn, chatResultStream, profileArn, this.#getTools(session), additionalContext, session.modelId, this.#origin, customContext);
|
|
602
|
+
return requestInput;
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Prepares the initial request input for the chat prompt
|
|
606
|
+
*/
|
|
607
|
+
#getCompactionRequestInput(session) {
|
|
608
|
+
this.#debug('Preparing compaction request input');
|
|
609
|
+
// Get profileArn from the service manager if available
|
|
610
|
+
const profileArn = this.#serviceManager?.getActiveProfileArn();
|
|
611
|
+
const requestInput = this.#triggerContext.getCompactionChatCommandInput(profileArn, this.#getTools(session), session.modelId, this.#origin);
|
|
538
612
|
return requestInput;
|
|
539
613
|
}
|
|
614
|
+
/**
|
|
615
|
+
* Runs the compaction, making requests and processing tool uses until completion
|
|
616
|
+
*/
|
|
617
|
+
#shouldCompact(currentRequestCount) {
|
|
618
|
+
// 80% of 570K limit
|
|
619
|
+
if (currentRequestCount > 456_000) {
|
|
620
|
+
this.#debug(`Current request total character count is: ${currentRequestCount}, prompting user to compact`);
|
|
621
|
+
return true;
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Runs the compaction to compact history into a single summary
|
|
629
|
+
*/
|
|
630
|
+
async #runCompaction(compactionRequestInput, session, metric, chatResultStream, tabId, promptId, conversationIdentifier, token, documentReference) {
|
|
631
|
+
let currentRequestInput = { ...compactionRequestInput };
|
|
632
|
+
let finalResult = null;
|
|
633
|
+
metric.recordStart();
|
|
634
|
+
this.#debug(`Running compaction for conversation id:`, conversationIdentifier || '');
|
|
635
|
+
this.#timeToFirstChunk = -1;
|
|
636
|
+
this.#timeBetweenChunks = [];
|
|
637
|
+
// Check for cancellation
|
|
638
|
+
if (this.#isPromptCanceled(token, session, promptId)) {
|
|
639
|
+
this.#debug('Stopping compaction loop - cancelled by user');
|
|
640
|
+
throw new lsp_core_1.CancellationError('user');
|
|
641
|
+
}
|
|
642
|
+
const currentMessage = currentRequestInput.conversationState?.currentMessage;
|
|
643
|
+
let messages = [];
|
|
644
|
+
let characterCount = 0;
|
|
645
|
+
if (currentMessage) {
|
|
646
|
+
// Get and process the messages from history DB to maintain invariants for service requests
|
|
647
|
+
try {
|
|
648
|
+
const { messages: historyMessages, count: historyCharCount } = this.#chatHistoryDb.fixAndGetHistory(tabId, currentMessage, []);
|
|
649
|
+
messages = historyMessages;
|
|
650
|
+
characterCount = historyCharCount;
|
|
651
|
+
}
|
|
652
|
+
catch (err) {
|
|
653
|
+
if (err instanceof chatDb_1.ToolResultValidationError) {
|
|
654
|
+
this.#features.logging.error(`Tool validation error: ${err.message}`);
|
|
655
|
+
return (finalResult || {
|
|
656
|
+
success: false,
|
|
657
|
+
error: 'Compaction loop failed to produce a final result',
|
|
658
|
+
data: { chatResult: {}, toolUses: {} },
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
currentRequestInput.conversationState.history = messages.map(msg => (0, util_1.messageToStreamingMessage)(msg));
|
|
664
|
+
const resultStreamWriter = chatResultStream.getResultStreamWriter();
|
|
665
|
+
if (currentRequestInput.conversationState.history.length == 0) {
|
|
666
|
+
// early terminate
|
|
667
|
+
await resultStreamWriter.write({
|
|
668
|
+
type: 'answer',
|
|
669
|
+
body: 'History is empty, there is nothing to compact.',
|
|
670
|
+
messageId: (0, uuid_1.v4)(),
|
|
671
|
+
});
|
|
672
|
+
return {
|
|
673
|
+
success: true,
|
|
674
|
+
data: {
|
|
675
|
+
chatResult: {},
|
|
676
|
+
toolUses: {},
|
|
677
|
+
},
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
else {
|
|
681
|
+
await resultStreamWriter.write({
|
|
682
|
+
type: 'answer',
|
|
683
|
+
body: 'Compacting your chat history, this may take a moment.',
|
|
684
|
+
messageId: (0, uuid_1.v4)(),
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
await resultStreamWriter.close();
|
|
688
|
+
// Add loading message before making the request
|
|
689
|
+
const loadingMessageId = `loading-${(0, uuid_1.v4)()}`;
|
|
690
|
+
await chatResultStream.writeResultBlock({ ...constants_1.loadingMessage, messageId: loadingMessageId });
|
|
691
|
+
this.#debug(`Compacting history with ${characterCount} characters`);
|
|
692
|
+
this.#llmRequestStartTime = Date.now();
|
|
693
|
+
// Phase 3: Request Execution
|
|
694
|
+
// Note: these logs are very noisy, but contain information redacted on the backend.
|
|
695
|
+
this.#debug(`generateAssistantResponse/SendMessage Request: ${JSON.stringify(currentRequestInput, undefined, 2)}`);
|
|
696
|
+
const response = await session.getChatResponse(currentRequestInput);
|
|
697
|
+
if (response.$metadata.requestId) {
|
|
698
|
+
metric.mergeWith({
|
|
699
|
+
requestIds: [response.$metadata.requestId],
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
this.#features.logging.info(`generateAssistantResponse/SendMessage ResponseMetadata: ${lsp_core_2.loggingUtils.formatObj(response.$metadata)}`);
|
|
703
|
+
await chatResultStream.removeResultBlock(loadingMessageId);
|
|
704
|
+
// Phase 4: Response Processing
|
|
705
|
+
const result = await this.#processAgenticChatResponseWithTimeout(response, metric.mergeWith({
|
|
706
|
+
cwsprChatResponseCode: response.$metadata.httpStatusCode,
|
|
707
|
+
cwsprChatMessageId: response.$metadata.requestId,
|
|
708
|
+
}), chatResultStream, session, documentReference, true);
|
|
709
|
+
const llmLatency = Date.now() - this.#llmRequestStartTime;
|
|
710
|
+
this.#debug(`LLM Response Latency for compaction: ${llmLatency}`);
|
|
711
|
+
this.#telemetryController.emitAgencticLoop_InvokeLLM(response.$metadata.requestId, conversationIdentifier ?? '', 'AgenticChatWithCompaction', undefined, undefined, 'Succeeded', this.#features.runtime.serverInfo.version ?? '', session.modelId, llmLatency, [], this.#timeToFirstChunk, this.#timeBetweenChunks, session.pairProgrammingMode);
|
|
712
|
+
// replace the history with summary in history DB
|
|
713
|
+
if (result.data?.chatResult.body !== undefined) {
|
|
714
|
+
this.#chatHistoryDb.replaceWithSummary(tabId, 'cwc', conversationIdentifier ?? '', {
|
|
715
|
+
body: result.data?.chatResult.body,
|
|
716
|
+
type: 'prompt',
|
|
717
|
+
shouldDisplayMessage: true,
|
|
718
|
+
timestamp: new Date(),
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
this.#features.logging.warn('No ChatResult body in response, skipping adding to history');
|
|
723
|
+
}
|
|
724
|
+
return result;
|
|
725
|
+
}
|
|
540
726
|
/**
|
|
541
727
|
* Runs the agent loop, making requests and processing tool uses until completion
|
|
542
728
|
*/
|
|
@@ -545,7 +731,9 @@ class AgenticChatController {
|
|
|
545
731
|
let finalResult = null;
|
|
546
732
|
let iterationCount = 0;
|
|
547
733
|
let shouldDisplayMessage = true;
|
|
734
|
+
let currentRequestCount = 0;
|
|
548
735
|
metric.recordStart();
|
|
736
|
+
this.logSystemInformation();
|
|
549
737
|
while (true) {
|
|
550
738
|
iterationCount++;
|
|
551
739
|
this.#debug(`Agent loop iteration ${iterationCount} for conversation id:`, conversationIdentifier || '');
|
|
@@ -570,7 +758,11 @@ class AgenticChatController {
|
|
|
570
758
|
if (currentMessage) {
|
|
571
759
|
// Get and process the messages from history DB to maintain invariants for service requests
|
|
572
760
|
try {
|
|
573
|
-
|
|
761
|
+
const newUserInputCount = this.#chatHistoryDb.calculateNewMessageCharacterCount(currentMessage, pinnedContextMessages);
|
|
762
|
+
const { messages: historyMessages, count: historyCharacterCount } = this.#chatHistoryDb.fixAndGetHistory(tabId, currentMessage, pinnedContextMessages, newUserInputCount);
|
|
763
|
+
messages = historyMessages;
|
|
764
|
+
currentRequestCount = newUserInputCount + historyCharacterCount;
|
|
765
|
+
this.#debug(`Request total character count: ${currentRequestCount}`);
|
|
574
766
|
}
|
|
575
767
|
catch (err) {
|
|
576
768
|
if (err instanceof chatDb_1.ToolResultValidationError) {
|
|
@@ -579,7 +771,7 @@ class AgenticChatController {
|
|
|
579
771
|
}
|
|
580
772
|
}
|
|
581
773
|
}
|
|
582
|
-
//
|
|
774
|
+
// Do not include chatHistory for requests going to Mynah Backend
|
|
583
775
|
currentRequestInput.conversationState.history = currentRequestInput.conversationState?.currentMessage
|
|
584
776
|
?.userInputMessage?.userIntent
|
|
585
777
|
? []
|
|
@@ -590,7 +782,7 @@ class AgenticChatController {
|
|
|
590
782
|
this.#llmRequestStartTime = Date.now();
|
|
591
783
|
// Phase 3: Request Execution
|
|
592
784
|
// Note: these logs are very noisy, but contain information redacted on the backend.
|
|
593
|
-
this.#debug(`generateAssistantResponse/SendMessage Request: ${JSON.stringify(currentRequestInput,
|
|
785
|
+
this.#debug(`generateAssistantResponse/SendMessage Request: ${JSON.stringify(currentRequestInput, this.#imageReplacer, 2)}`);
|
|
594
786
|
const response = await session.getChatResponse(currentRequestInput);
|
|
595
787
|
if (response.$metadata.requestId) {
|
|
596
788
|
metric.mergeWith({
|
|
@@ -683,7 +875,7 @@ class AgenticChatController {
|
|
|
683
875
|
if (pendingToolUses.length === 0) {
|
|
684
876
|
this.recordChunk('agent_loop_done');
|
|
685
877
|
// No more tool uses, we're done
|
|
686
|
-
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);
|
|
878
|
+
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, this.#abTestingAllocation?.experimentName, this.#abTestingAllocation?.userVariation);
|
|
687
879
|
finalResult = result;
|
|
688
880
|
break;
|
|
689
881
|
}
|
|
@@ -704,7 +896,7 @@ class AgenticChatController {
|
|
|
704
896
|
metric.setDimension('requestIds', metric.metric.requestIds);
|
|
705
897
|
const toolNames = this.#toolUseLatencies.map(item => item.toolName);
|
|
706
898
|
const toolUseIds = this.#toolUseLatencies.map(item => item.toolUseId);
|
|
707
|
-
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);
|
|
899
|
+
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, this.#abTestingAllocation?.experimentName, this.#abTestingAllocation?.userVariation);
|
|
708
900
|
}
|
|
709
901
|
else {
|
|
710
902
|
// Send an error card to UI?
|
|
@@ -713,7 +905,7 @@ class AgenticChatController {
|
|
|
713
905
|
status: codewhisperer_streaming_1.ToolResultStatus.ERROR,
|
|
714
906
|
content: [{ text: result.error }],
|
|
715
907
|
}));
|
|
716
|
-
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);
|
|
908
|
+
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, this.#abTestingAllocation?.experimentName, this.#abTestingAllocation?.userVariation);
|
|
717
909
|
if (result.error.startsWith('ToolUse input is invalid JSON:')) {
|
|
718
910
|
content =
|
|
719
911
|
'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,6 +920,16 @@ class AgenticChatController {
|
|
|
728
920
|
}
|
|
729
921
|
currentRequestInput = this.#updateRequestInputWithToolResults(currentRequestInput, toolResults, content);
|
|
730
922
|
}
|
|
923
|
+
if (this.#shouldCompact(currentRequestCount)) {
|
|
924
|
+
const messageId = this.#getMessageIdForCompact((0, uuid_1.v4)());
|
|
925
|
+
const confirmationResult = this.#processCompactConfirmation(messageId);
|
|
926
|
+
const cachedButtonBlockId = await chatResultStream.writeResultBlock(confirmationResult);
|
|
927
|
+
await this.waitForCompactApproval(messageId, chatResultStream, cachedButtonBlockId, session);
|
|
928
|
+
// Get the compaction request input
|
|
929
|
+
const compactionRequestInput = this.#getCompactionRequestInput(session);
|
|
930
|
+
// Start the compaction call
|
|
931
|
+
return await this.#runCompaction(compactionRequestInput, session, metric, chatResultStream, tabId, promptId, session.conversationId, token, documentReference);
|
|
932
|
+
}
|
|
731
933
|
return (finalResult || {
|
|
732
934
|
success: false,
|
|
733
935
|
error: 'Agent loop failed to produce a final result',
|
|
@@ -860,36 +1062,36 @@ class AgenticChatController {
|
|
|
860
1062
|
// remove progress UI
|
|
861
1063
|
await chatResultStream.removeResultBlockAndUpdateUI(agenticChatResultStream_1.progressPrefix + toolUse.toolUseId);
|
|
862
1064
|
// fsRead and listDirectory write to an existing card and could show nothing in the current position
|
|
863
|
-
if (![
|
|
1065
|
+
if (![toolConstants_1.FS_WRITE, toolConstants_1.FS_REPLACE, toolConstants_1.FS_READ, toolConstants_1.LIST_DIRECTORY].includes(toolUse.name)) {
|
|
864
1066
|
await this.#showUndoAllIfRequired(chatResultStream, session);
|
|
865
1067
|
}
|
|
866
1068
|
// fsWrite can take a long time, so we render fsWrite Explanatory upon partial streaming responses.
|
|
867
|
-
if (toolUse.name !==
|
|
1069
|
+
if (toolUse.name !== toolConstants_1.FS_WRITE && toolUse.name !== toolConstants_1.FS_REPLACE) {
|
|
868
1070
|
const { explanation } = toolUse.input;
|
|
869
1071
|
if (explanation) {
|
|
870
1072
|
await chatResultStream.writeResultBlock({
|
|
871
1073
|
type: 'directive',
|
|
872
|
-
messageId: toolUse.toolUseId +
|
|
1074
|
+
messageId: toolUse.toolUseId + toolConstants_1.SUFFIX_EXPLANATION,
|
|
873
1075
|
body: explanation,
|
|
874
1076
|
});
|
|
875
1077
|
}
|
|
876
1078
|
}
|
|
877
1079
|
switch (toolUse.name) {
|
|
878
|
-
case
|
|
879
|
-
case
|
|
880
|
-
case
|
|
881
|
-
case
|
|
882
|
-
case
|
|
883
|
-
case
|
|
884
|
-
case
|
|
1080
|
+
case toolConstants_1.FS_READ:
|
|
1081
|
+
case toolConstants_1.LIST_DIRECTORY:
|
|
1082
|
+
case toolConstants_1.GREP_SEARCH:
|
|
1083
|
+
case toolConstants_1.FILE_SEARCH:
|
|
1084
|
+
case toolConstants_1.FS_WRITE:
|
|
1085
|
+
case toolConstants_1.FS_REPLACE:
|
|
1086
|
+
case toolConstants_1.EXECUTE_BASH: {
|
|
885
1087
|
const toolMap = {
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
1088
|
+
[toolConstants_1.FS_READ]: { Tool: fsRead_1.FsRead },
|
|
1089
|
+
[toolConstants_1.LIST_DIRECTORY]: { Tool: listDirectory_1.ListDirectory },
|
|
1090
|
+
[toolConstants_1.FS_WRITE]: { Tool: fsWrite_1.FsWrite },
|
|
1091
|
+
[toolConstants_1.FS_REPLACE]: { Tool: fsReplace_1.FsReplace },
|
|
1092
|
+
[toolConstants_1.EXECUTE_BASH]: { Tool: executeBash_1.ExecuteBash },
|
|
1093
|
+
[toolConstants_1.GREP_SEARCH]: { Tool: grepSearch_1.GrepSearch },
|
|
1094
|
+
[toolConstants_1.FILE_SEARCH]: { Tool: fileSearch_1.FileSearch },
|
|
893
1095
|
};
|
|
894
1096
|
const { Tool } = toolMap[toolUse.name];
|
|
895
1097
|
const tool = new Tool(this.#features);
|
|
@@ -903,23 +1105,26 @@ class AgenticChatController {
|
|
|
903
1105
|
const { requiresAcceptance, warning, commandCategory } = await tool.requiresAcceptance(toolUse.input, approvedPaths);
|
|
904
1106
|
// Honor built-in permission if available, otherwise use tool's requiresAcceptance
|
|
905
1107
|
// const requiresAcceptance = builtInPermission || toolRequiresAcceptance
|
|
906
|
-
if (requiresAcceptance || toolUse.name ===
|
|
1108
|
+
if (requiresAcceptance || toolUse.name === toolConstants_1.EXECUTE_BASH) {
|
|
907
1109
|
// for executeBash, we till send the confirmation message without action buttons
|
|
908
1110
|
const confirmationResult = this.#processToolConfirmation(toolUse, requiresAcceptance, warning, commandCategory);
|
|
909
1111
|
cachedButtonBlockId = await chatResultStream.writeResultBlock(confirmationResult);
|
|
910
|
-
const isExecuteBash = toolUse.name ===
|
|
1112
|
+
const isExecuteBash = toolUse.name === toolConstants_1.EXECUTE_BASH;
|
|
911
1113
|
if (isExecuteBash) {
|
|
912
|
-
this.#telemetryController.emitInteractWithAgenticChat('GeneratedCommand', tabId, session.pairProgrammingMode, session.getConversationType());
|
|
1114
|
+
this.#telemetryController.emitInteractWithAgenticChat('GeneratedCommand', tabId, session.pairProgrammingMode, session.getConversationType(), this.#abTestingAllocation?.experimentName, this.#abTestingAllocation?.userVariation);
|
|
913
1115
|
}
|
|
914
1116
|
if (requiresAcceptance) {
|
|
915
1117
|
await this.waitForToolApproval(toolUse, chatResultStream, cachedButtonBlockId, session, toolUse.name);
|
|
916
1118
|
}
|
|
917
1119
|
if (isExecuteBash) {
|
|
918
|
-
this.#telemetryController.emitInteractWithAgenticChat('RunCommand', tabId, session.pairProgrammingMode, session.getConversationType());
|
|
1120
|
+
this.#telemetryController.emitInteractWithAgenticChat('RunCommand', tabId, session.pairProgrammingMode, session.getConversationType(), this.#abTestingAllocation?.experimentName, this.#abTestingAllocation?.userVariation);
|
|
919
1121
|
}
|
|
920
1122
|
}
|
|
921
1123
|
break;
|
|
922
1124
|
}
|
|
1125
|
+
case qCodeReview_1.QCodeReview.toolName:
|
|
1126
|
+
// no need to write tool message for code review
|
|
1127
|
+
break;
|
|
923
1128
|
// — DEFAULT ⇒ Only MCP tools, but can also handle generic tool execution messages
|
|
924
1129
|
default:
|
|
925
1130
|
// Get original server and tool names from the mapping
|
|
@@ -961,7 +1166,7 @@ class AgenticChatController {
|
|
|
961
1166
|
}
|
|
962
1167
|
break;
|
|
963
1168
|
}
|
|
964
|
-
if (toolUse.name ===
|
|
1169
|
+
if (toolUse.name === toolConstants_1.FS_WRITE || toolUse.name === toolConstants_1.FS_REPLACE) {
|
|
965
1170
|
const input = toolUse.input;
|
|
966
1171
|
const document = await this.#triggerContext.getTextDocumentFromPath(input.path, true, true);
|
|
967
1172
|
session.toolUseLookup.set(toolUse.toolUseId, {
|
|
@@ -969,6 +1174,22 @@ class AgenticChatController {
|
|
|
969
1174
|
fileChange: { before: document?.getText() },
|
|
970
1175
|
});
|
|
971
1176
|
}
|
|
1177
|
+
if (toolUse.name === qCodeReview_1.QCodeReview.toolName) {
|
|
1178
|
+
try {
|
|
1179
|
+
let initialInput = JSON.parse(JSON.stringify(toolUse.input));
|
|
1180
|
+
let ruleArtifacts = await this.#additionalContextProvider.collectWorkspaceRules(tabId);
|
|
1181
|
+
if (ruleArtifacts !== undefined || ruleArtifacts !== null) {
|
|
1182
|
+
this.#features.logging.info(`RuleArtifacts: ${JSON.stringify(ruleArtifacts)}`);
|
|
1183
|
+
let pathsToRulesMap = ruleArtifacts.map(ruleArtifact => ({ path: ruleArtifact.id }));
|
|
1184
|
+
this.#features.logging.info(`PathsToRules: ${JSON.stringify(pathsToRulesMap)}`);
|
|
1185
|
+
initialInput['ruleArtifacts'] = pathsToRulesMap;
|
|
1186
|
+
}
|
|
1187
|
+
toolUse.input = initialInput;
|
|
1188
|
+
}
|
|
1189
|
+
catch (e) {
|
|
1190
|
+
this.#features.logging.warn(`could not parse QCodeReview tool input: ${e}`);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
972
1193
|
// After approval, add the path to the approved paths in the session
|
|
973
1194
|
const inputPath = toolUse.input?.path || toolUse.input?.cwd;
|
|
974
1195
|
if (inputPath) {
|
|
@@ -995,27 +1216,27 @@ class AgenticChatController {
|
|
|
995
1216
|
content: [toolResultContent],
|
|
996
1217
|
});
|
|
997
1218
|
switch (toolUse.name) {
|
|
998
|
-
case
|
|
999
|
-
case
|
|
1000
|
-
case
|
|
1219
|
+
case toolConstants_1.FS_READ:
|
|
1220
|
+
case toolConstants_1.LIST_DIRECTORY:
|
|
1221
|
+
case toolConstants_1.FILE_SEARCH:
|
|
1001
1222
|
const initialListDirResult = this.#processReadOrListOrSearch(toolUse, chatResultStream);
|
|
1002
1223
|
if (initialListDirResult) {
|
|
1003
1224
|
await chatResultStream.writeResultBlock(initialListDirResult);
|
|
1004
1225
|
}
|
|
1005
1226
|
break;
|
|
1006
1227
|
// no need to write tool result for listDir,fsRead,fileSearch into chat stream
|
|
1007
|
-
case
|
|
1228
|
+
case toolConstants_1.EXECUTE_BASH:
|
|
1008
1229
|
// no need to write tool result for listDir and fsRead into chat stream
|
|
1009
1230
|
// executeBash will stream the output instead of waiting until the end
|
|
1010
1231
|
break;
|
|
1011
|
-
case
|
|
1232
|
+
case toolConstants_1.GREP_SEARCH:
|
|
1012
1233
|
const grepSearchResult = this.#processGrepSearchResult(toolUse, result, chatResultStream);
|
|
1013
1234
|
if (grepSearchResult) {
|
|
1014
1235
|
await chatResultStream.writeResultBlock(grepSearchResult);
|
|
1015
1236
|
}
|
|
1016
1237
|
break;
|
|
1017
|
-
case
|
|
1018
|
-
case
|
|
1238
|
+
case toolConstants_1.FS_REPLACE:
|
|
1239
|
+
case toolConstants_1.FS_WRITE:
|
|
1019
1240
|
const input = toolUse.input;
|
|
1020
1241
|
// Load from the filesystem instead of workspace.
|
|
1021
1242
|
// Workspace is likely out of date - when files
|
|
@@ -1033,9 +1254,23 @@ class AgenticChatController {
|
|
|
1033
1254
|
fileChange: { ...cachedToolUse.fileChange, after: doc?.getText() },
|
|
1034
1255
|
});
|
|
1035
1256
|
}
|
|
1036
|
-
this.#telemetryController.emitInteractWithAgenticChat('GeneratedDiff', tabId, session.pairProgrammingMode, session.getConversationType());
|
|
1257
|
+
this.#telemetryController.emitInteractWithAgenticChat('GeneratedDiff', tabId, session.pairProgrammingMode, session.getConversationType(), this.#abTestingAllocation?.experimentName, this.#abTestingAllocation?.userVariation);
|
|
1037
1258
|
await chatResultStream.writeResultBlock(chatResult);
|
|
1038
1259
|
break;
|
|
1260
|
+
case qCodeReview_1.QCodeReview.toolName:
|
|
1261
|
+
// no need to write tool result for code review, this is handled by model via chat
|
|
1262
|
+
// Push result in message so that it is picked by IDE plugin to show in issues panel
|
|
1263
|
+
const qCodeReviewResult = result;
|
|
1264
|
+
if (qCodeReviewResult?.output?.kind === 'json' &&
|
|
1265
|
+
qCodeReviewResult.output.success &&
|
|
1266
|
+
qCodeReviewResult.output.content?.findingsByFile) {
|
|
1267
|
+
await chatResultStream.writeResultBlock({
|
|
1268
|
+
type: 'tool',
|
|
1269
|
+
messageId: toolUse.toolUseId + qCodeReviewConstants_1.FINDINGS_MESSAGE_SUFFIX,
|
|
1270
|
+
body: qCodeReviewResult.output.content.findingsByFile,
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
break;
|
|
1039
1274
|
// — DEFAULT ⇒ MCP tools
|
|
1040
1275
|
default:
|
|
1041
1276
|
await this.#handleMcpToolResult(toolUse, result, session, chatResultStream);
|
|
@@ -1056,7 +1291,7 @@ class AgenticChatController {
|
|
|
1056
1291
|
});
|
|
1057
1292
|
}
|
|
1058
1293
|
}
|
|
1059
|
-
this.#telemetryController.emitToolUseSuggested(toolUse, session.conversationId ?? '', this.#features.runtime.serverInfo.version ?? '', latency, session.pairProgrammingMode);
|
|
1294
|
+
this.#telemetryController.emitToolUseSuggested(toolUse, session.conversationId ?? '', this.#features.runtime.serverInfo.version ?? '', latency, session.pairProgrammingMode, this.#abTestingAllocation?.experimentName, this.#abTestingAllocation?.userVariation);
|
|
1060
1295
|
}
|
|
1061
1296
|
}
|
|
1062
1297
|
catch (err) {
|
|
@@ -1086,12 +1321,12 @@ class AgenticChatController {
|
|
|
1086
1321
|
continue;
|
|
1087
1322
|
}
|
|
1088
1323
|
// Rethrow error for executeBash or any named tool
|
|
1089
|
-
if (toolUse.name ===
|
|
1324
|
+
if (toolUse.name === toolConstants_1.EXECUTE_BASH || toolUse.name) {
|
|
1090
1325
|
throw err;
|
|
1091
1326
|
}
|
|
1092
1327
|
}
|
|
1093
1328
|
// display fs write failure status in the UX of that file card
|
|
1094
|
-
if ((toolUse.name ===
|
|
1329
|
+
if ((toolUse.name === toolConstants_1.FS_WRITE || toolUse.name === toolConstants_1.FS_REPLACE) && toolUse.toolUseId) {
|
|
1095
1330
|
const existingCard = chatResultStream.getMessageBlockId(toolUse.toolUseId);
|
|
1096
1331
|
const fsParam = toolUse.input;
|
|
1097
1332
|
if (fsParam.path) {
|
|
@@ -1125,7 +1360,7 @@ class AgenticChatController {
|
|
|
1125
1360
|
}
|
|
1126
1361
|
}
|
|
1127
1362
|
}
|
|
1128
|
-
else if (toolUse.name ===
|
|
1363
|
+
else if (toolUse.name === toolConstants_1.EXECUTE_BASH && toolUse.toolUseId) {
|
|
1129
1364
|
const existingCard = chatResultStream.getMessageBlockId(toolUse.toolUseId);
|
|
1130
1365
|
const command = toolUse.input.command;
|
|
1131
1366
|
const completedErrorResult = {
|
|
@@ -1183,10 +1418,10 @@ class AgenticChatController {
|
|
|
1183
1418
|
* Updates the currentUndoAllId state in the session
|
|
1184
1419
|
*/
|
|
1185
1420
|
#updateUndoAllState(toolUse, session) {
|
|
1186
|
-
if (toolUse.name ===
|
|
1421
|
+
if (toolUse.name === toolConstants_1.FS_READ || toolUse.name === toolConstants_1.LIST_DIRECTORY) {
|
|
1187
1422
|
return;
|
|
1188
1423
|
}
|
|
1189
|
-
if (toolUse.name ===
|
|
1424
|
+
if (toolUse.name === toolConstants_1.FS_WRITE || toolUse.name === toolConstants_1.FS_REPLACE) {
|
|
1190
1425
|
if (session.currentUndoAllId === undefined) {
|
|
1191
1426
|
session.currentUndoAllId = toolUse.toolUseId;
|
|
1192
1427
|
}
|
|
@@ -1221,10 +1456,10 @@ class AgenticChatController {
|
|
|
1221
1456
|
}
|
|
1222
1457
|
await chatResultStream.writeResultBlock({
|
|
1223
1458
|
type: 'answer',
|
|
1224
|
-
messageId: `${session.currentUndoAllId}
|
|
1459
|
+
messageId: `${session.currentUndoAllId}${toolConstants_1.SUFFIX_UNDOALL}`,
|
|
1225
1460
|
buttons: [
|
|
1226
1461
|
{
|
|
1227
|
-
id:
|
|
1462
|
+
id: toolConstants_1.BUTTON_UNDO_ALL_CHANGES,
|
|
1228
1463
|
text: 'Undo all changes',
|
|
1229
1464
|
icon: 'undo',
|
|
1230
1465
|
status: 'clear',
|
|
@@ -1252,11 +1487,11 @@ class AgenticChatController {
|
|
|
1252
1487
|
#validateToolResult(toolUse, result) {
|
|
1253
1488
|
let maxToolResponseSize;
|
|
1254
1489
|
switch (toolUse.name) {
|
|
1255
|
-
case
|
|
1256
|
-
case
|
|
1490
|
+
case toolConstants_1.FS_READ:
|
|
1491
|
+
case toolConstants_1.EXECUTE_BASH:
|
|
1257
1492
|
// fsRead and executeBash already have truncation logic
|
|
1258
1493
|
return;
|
|
1259
|
-
case
|
|
1494
|
+
case toolConstants_1.LIST_DIRECTORY:
|
|
1260
1495
|
maxToolResponseSize = 50_000;
|
|
1261
1496
|
break;
|
|
1262
1497
|
default:
|
|
@@ -1283,8 +1518,32 @@ class AgenticChatController {
|
|
|
1283
1518
|
return undefined;
|
|
1284
1519
|
}
|
|
1285
1520
|
}
|
|
1521
|
+
#getToolOverWritableStream(chatResultStream, toolUse) {
|
|
1522
|
+
const toolMsgId = toolUse.toolUseId;
|
|
1523
|
+
return new WritableStream({
|
|
1524
|
+
write: async (chunk) => {
|
|
1525
|
+
if (this.#stoppedToolUses.has(toolMsgId))
|
|
1526
|
+
return;
|
|
1527
|
+
await chatResultStream.removeResultBlockAndUpdateUI(toolMsgId);
|
|
1528
|
+
await chatResultStream.writeResultBlock({
|
|
1529
|
+
type: 'tool',
|
|
1530
|
+
messageId: toolMsgId,
|
|
1531
|
+
body: chunk,
|
|
1532
|
+
});
|
|
1533
|
+
},
|
|
1534
|
+
close: async () => {
|
|
1535
|
+
if (this.#stoppedToolUses.has(toolMsgId))
|
|
1536
|
+
return;
|
|
1537
|
+
await chatResultStream.removeResultBlockAndUpdateUI(toolMsgId);
|
|
1538
|
+
this.#stoppedToolUses.add(toolMsgId);
|
|
1539
|
+
},
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1286
1542
|
#getWritableStream(chatResultStream, toolUse) {
|
|
1287
|
-
if (toolUse.name
|
|
1543
|
+
if (toolUse.name === qCodeReview_1.QCodeReview.toolName) {
|
|
1544
|
+
return this.#getToolOverWritableStream(chatResultStream, toolUse);
|
|
1545
|
+
}
|
|
1546
|
+
if (toolUse.name !== toolConstants_1.EXECUTE_BASH) {
|
|
1288
1547
|
return;
|
|
1289
1548
|
}
|
|
1290
1549
|
const toolMsgId = toolUse.toolUseId;
|
|
@@ -1292,7 +1551,7 @@ class AgenticChatController {
|
|
|
1292
1551
|
let headerEmitted = false;
|
|
1293
1552
|
const initialHeader = {
|
|
1294
1553
|
body: 'shell',
|
|
1295
|
-
buttons: [{ id:
|
|
1554
|
+
buttons: [{ id: toolConstants_1.BUTTON_STOP_SHELL_COMMAND, text: 'Cancel', icon: 'stop' }],
|
|
1296
1555
|
};
|
|
1297
1556
|
const completedHeader = {
|
|
1298
1557
|
body: 'shell',
|
|
@@ -1340,7 +1599,7 @@ class AgenticChatController {
|
|
|
1340
1599
|
#getUpdateToolConfirmResult(toolUse, isAccept, originalToolName, toolType) {
|
|
1341
1600
|
const toolName = originalToolName ?? (toolType || toolUse.name);
|
|
1342
1601
|
// Handle bash commands with special formatting
|
|
1343
|
-
if (toolName ===
|
|
1602
|
+
if (toolName === toolConstants_1.EXECUTE_BASH) {
|
|
1344
1603
|
return {
|
|
1345
1604
|
messageId: toolUse.toolUseId,
|
|
1346
1605
|
type: 'tool',
|
|
@@ -1356,7 +1615,7 @@ class AgenticChatController {
|
|
|
1356
1615
|
text: 'Rejected',
|
|
1357
1616
|
},
|
|
1358
1617
|
}),
|
|
1359
|
-
buttons: isAccept ? [{ id:
|
|
1618
|
+
buttons: isAccept ? [{ id: toolConstants_1.BUTTON_STOP_SHELL_COMMAND, text: 'Cancel', icon: 'stop' }] : [],
|
|
1360
1619
|
},
|
|
1361
1620
|
};
|
|
1362
1621
|
}
|
|
@@ -1364,10 +1623,10 @@ class AgenticChatController {
|
|
|
1364
1623
|
let header;
|
|
1365
1624
|
let body;
|
|
1366
1625
|
switch (toolName) {
|
|
1367
|
-
case
|
|
1368
|
-
case
|
|
1369
|
-
case
|
|
1370
|
-
case
|
|
1626
|
+
case toolConstants_1.FS_REPLACE:
|
|
1627
|
+
case toolConstants_1.FS_WRITE:
|
|
1628
|
+
case toolConstants_1.FS_READ:
|
|
1629
|
+
case toolConstants_1.LIST_DIRECTORY:
|
|
1371
1630
|
header = {
|
|
1372
1631
|
body: undefined,
|
|
1373
1632
|
status: {
|
|
@@ -1377,7 +1636,7 @@ class AgenticChatController {
|
|
|
1377
1636
|
},
|
|
1378
1637
|
};
|
|
1379
1638
|
break;
|
|
1380
|
-
case
|
|
1639
|
+
case toolConstants_1.FILE_SEARCH:
|
|
1381
1640
|
const searchPath = toolUse.input.path;
|
|
1382
1641
|
header = {
|
|
1383
1642
|
body: 'File Search',
|
|
@@ -1444,7 +1703,7 @@ class AgenticChatController {
|
|
|
1444
1703
|
status: {
|
|
1445
1704
|
status: 'error',
|
|
1446
1705
|
icon: 'stop',
|
|
1447
|
-
text: '
|
|
1706
|
+
text: 'Canceled',
|
|
1448
1707
|
},
|
|
1449
1708
|
buttons: [],
|
|
1450
1709
|
},
|
|
@@ -1453,6 +1712,97 @@ class AgenticChatController {
|
|
|
1453
1712
|
},
|
|
1454
1713
|
});
|
|
1455
1714
|
}
|
|
1715
|
+
#processCompactConfirmation(messageId) {
|
|
1716
|
+
const buttons = [{ id: 'allow-tools', text: 'Allow', icon: 'ok', status: 'clear' }];
|
|
1717
|
+
const header = {
|
|
1718
|
+
icon: 'warning',
|
|
1719
|
+
iconForegroundStatus: 'warning',
|
|
1720
|
+
body: constants_2.COMPACTION_HEADER_BODY,
|
|
1721
|
+
buttons,
|
|
1722
|
+
};
|
|
1723
|
+
const body = constants_2.COMPACTION_BODY;
|
|
1724
|
+
return {
|
|
1725
|
+
type: 'tool',
|
|
1726
|
+
messageId,
|
|
1727
|
+
header,
|
|
1728
|
+
body,
|
|
1729
|
+
};
|
|
1730
|
+
}
|
|
1731
|
+
/**
|
|
1732
|
+
* Creates a promise that does not resolve until the user accepts or rejects the compaction usage.
|
|
1733
|
+
* @param messageId
|
|
1734
|
+
* @param resultStream
|
|
1735
|
+
* @param promptBlockId id of approval block. This allows us to overwrite the buttons with 'accepted' or 'rejected' text.
|
|
1736
|
+
*/
|
|
1737
|
+
async waitForCompactApproval(messageId, resultStream, promptBlockId, session) {
|
|
1738
|
+
const deferred = this.#createDeferred();
|
|
1739
|
+
session.setDeferredToolExecution(messageId, deferred.resolve, deferred.reject);
|
|
1740
|
+
this.#log(`Prompting for compaction approval for messageId: ${messageId}`);
|
|
1741
|
+
await deferred.promise;
|
|
1742
|
+
// Note: we want to overwrite the button block because it already exists in the stream.
|
|
1743
|
+
await resultStream.overwriteResultBlock(this.#getUpdateCompactConfirmResult(messageId), promptBlockId);
|
|
1744
|
+
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Creates an updated ChatResult for compaction confirmation
|
|
1747
|
+
* @param messageId The messageId
|
|
1748
|
+
* @returns ChatResult with appropriate confirmation UI
|
|
1749
|
+
*/
|
|
1750
|
+
#getUpdateCompactConfirmResult(messageId) {
|
|
1751
|
+
let header;
|
|
1752
|
+
let body;
|
|
1753
|
+
header = {
|
|
1754
|
+
body: undefined,
|
|
1755
|
+
status: {
|
|
1756
|
+
status: 'success',
|
|
1757
|
+
icon: 'ok',
|
|
1758
|
+
text: 'Allowed',
|
|
1759
|
+
},
|
|
1760
|
+
};
|
|
1761
|
+
return {
|
|
1762
|
+
messageId,
|
|
1763
|
+
type: 'tool',
|
|
1764
|
+
body,
|
|
1765
|
+
header,
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
#renderStopShellCommandButton() {
|
|
1769
|
+
const stopKey = this.#getKeyBinding('aws.amazonq.stopCmdExecution');
|
|
1770
|
+
return {
|
|
1771
|
+
id: 'stop-shell-command',
|
|
1772
|
+
text: 'Cancel',
|
|
1773
|
+
icon: 'stop',
|
|
1774
|
+
...(stopKey ? { description: `Stop: ${stopKey}` } : {}),
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
#getKeyBinding(commandId) {
|
|
1778
|
+
// Check for feature flag
|
|
1779
|
+
const shortcut = this.#features.lsp.getClientInitializeParams()?.initializationOptions?.aws?.awsClientCapabilities?.q
|
|
1780
|
+
?.shortcut;
|
|
1781
|
+
if (!shortcut) {
|
|
1782
|
+
return null;
|
|
1783
|
+
}
|
|
1784
|
+
let defaultKey = '';
|
|
1785
|
+
const OS = os.platform();
|
|
1786
|
+
switch (commandId) {
|
|
1787
|
+
case 'aws.amazonq.runCmdExecution':
|
|
1788
|
+
defaultKey = OS === 'darwin' ? constants_2.DEFAULT_MACOS_RUN_SHORTCUT : constants_2.DEFAULT_WINDOW_RUN_SHORTCUT;
|
|
1789
|
+
break;
|
|
1790
|
+
case 'aws.amazonq.rejectCmdExecution':
|
|
1791
|
+
defaultKey = OS === 'darwin' ? constants_2.DEFAULT_MACOS_REJECT_SHORTCUT : constants_2.DEFAULT_WINDOW_REJECT_SHORTCUT;
|
|
1792
|
+
break;
|
|
1793
|
+
case 'aws.amazonq.stopCmdExecution':
|
|
1794
|
+
defaultKey = OS === 'darwin' ? constants_2.DEFAULT_MACOS_STOP_SHORTCUT : constants_2.DEFAULT_WINDOW_STOP_SHORTCUT;
|
|
1795
|
+
break;
|
|
1796
|
+
default:
|
|
1797
|
+
this.#log(`#getKeyBinding: ${commandId} shortcut is supported by Q `);
|
|
1798
|
+
break;
|
|
1799
|
+
}
|
|
1800
|
+
if (defaultKey === '') {
|
|
1801
|
+
return null;
|
|
1802
|
+
}
|
|
1803
|
+
//TODO: handle case: user change default keybind, suggestion: read `keybinding.json` provided by VSC
|
|
1804
|
+
return defaultKey;
|
|
1805
|
+
}
|
|
1456
1806
|
#processToolConfirmation(toolUse, requiresAcceptance, warning, commandCategory, toolType, builtInPermission) {
|
|
1457
1807
|
const toolName = toolType || toolUse.name;
|
|
1458
1808
|
let buttons = [];
|
|
@@ -1460,13 +1810,13 @@ class AgenticChatController {
|
|
|
1460
1810
|
let body;
|
|
1461
1811
|
// Configure tool-specific UI elements
|
|
1462
1812
|
switch (toolName) {
|
|
1463
|
-
case
|
|
1813
|
+
case toolConstants_1.EXECUTE_BASH: {
|
|
1464
1814
|
const commandString = toolUse.input.command;
|
|
1465
1815
|
buttons = requiresAcceptance
|
|
1466
1816
|
? [
|
|
1467
|
-
{ id:
|
|
1817
|
+
{ id: toolConstants_1.BUTTON_RUN_SHELL_COMMAND, text: 'Run', icon: 'play' },
|
|
1468
1818
|
{
|
|
1469
|
-
id:
|
|
1819
|
+
id: toolConstants_1.BUTTON_REJECT_SHELL_COMMAND,
|
|
1470
1820
|
status: 'dimmed-clear',
|
|
1471
1821
|
text: 'Reject',
|
|
1472
1822
|
icon: 'cancel',
|
|
@@ -1498,12 +1848,12 @@ class AgenticChatController {
|
|
|
1498
1848
|
body = '```shell\n' + commandString;
|
|
1499
1849
|
break;
|
|
1500
1850
|
}
|
|
1501
|
-
case
|
|
1851
|
+
case toolConstants_1.FS_WRITE: {
|
|
1502
1852
|
const writeFilePath = toolUse.input.path;
|
|
1503
1853
|
// Validate the path using our synchronous utility
|
|
1504
1854
|
(0, pathValidation_1.validatePathBasic)(writeFilePath);
|
|
1505
1855
|
this.#debug(`Processing ${toolUse.name} for path: ${writeFilePath}`);
|
|
1506
|
-
buttons = [{ id:
|
|
1856
|
+
buttons = [{ id: toolConstants_1.BUTTON_ALLOW_TOOLS, text: 'Allow', icon: 'ok', status: 'clear' }];
|
|
1507
1857
|
header = {
|
|
1508
1858
|
icon: 'warning',
|
|
1509
1859
|
iconForegroundStatus: 'warning',
|
|
@@ -1517,12 +1867,12 @@ class AgenticChatController {
|
|
|
1517
1867
|
: `I need permission to modify files outside of your workspace.\n\`${writeFilePath}\``;
|
|
1518
1868
|
break;
|
|
1519
1869
|
}
|
|
1520
|
-
case
|
|
1870
|
+
case toolConstants_1.FS_REPLACE: {
|
|
1521
1871
|
const writeFilePath = toolUse.input.path;
|
|
1522
1872
|
// For replace, we need to verify the file exists
|
|
1523
1873
|
(0, pathValidation_1.validatePathExists)(writeFilePath);
|
|
1524
1874
|
this.#debug(`Processing ${toolUse.name} for path: ${writeFilePath}`);
|
|
1525
|
-
buttons = [{ id:
|
|
1875
|
+
buttons = [{ id: toolConstants_1.BUTTON_ALLOW_TOOLS, text: 'Allow', icon: 'ok', status: 'clear' }];
|
|
1526
1876
|
header = {
|
|
1527
1877
|
icon: 'warning',
|
|
1528
1878
|
iconForegroundStatus: 'warning',
|
|
@@ -1536,9 +1886,9 @@ class AgenticChatController {
|
|
|
1536
1886
|
: `I need permission to modify files outside of your workspace.\n\`${writeFilePath}\``;
|
|
1537
1887
|
break;
|
|
1538
1888
|
}
|
|
1539
|
-
case
|
|
1540
|
-
case
|
|
1541
|
-
buttons = [{ id:
|
|
1889
|
+
case toolConstants_1.FS_READ:
|
|
1890
|
+
case toolConstants_1.LIST_DIRECTORY: {
|
|
1891
|
+
buttons = [{ id: toolConstants_1.BUTTON_ALLOW_TOOLS, text: 'Allow', icon: 'ok', status: 'clear' }];
|
|
1542
1892
|
header = {
|
|
1543
1893
|
icon: 'tools',
|
|
1544
1894
|
iconForegroundStatus: 'tools',
|
|
@@ -1547,7 +1897,7 @@ class AgenticChatController {
|
|
|
1547
1897
|
: '#### Allow read-only tools outside your workspace',
|
|
1548
1898
|
buttons,
|
|
1549
1899
|
};
|
|
1550
|
-
if (toolName ===
|
|
1900
|
+
if (toolName === toolConstants_1.FS_READ) {
|
|
1551
1901
|
const paths = toolUse.input.paths;
|
|
1552
1902
|
// Validate paths using our synchronous utility
|
|
1553
1903
|
(0, pathValidation_1.validatePaths)(paths);
|
|
@@ -1571,7 +1921,7 @@ class AgenticChatController {
|
|
|
1571
1921
|
}
|
|
1572
1922
|
default: {
|
|
1573
1923
|
// — DEFAULT ⇒ MCP tools
|
|
1574
|
-
buttons = [{ id:
|
|
1924
|
+
buttons = [{ id: toolConstants_1.BUTTON_ALLOW_TOOLS, text: 'Allow', icon: 'ok', status: 'clear' }];
|
|
1575
1925
|
header = {
|
|
1576
1926
|
icon: 'tools',
|
|
1577
1927
|
iconForegroundStatus: 'warning',
|
|
@@ -1589,7 +1939,7 @@ class AgenticChatController {
|
|
|
1589
1939
|
type: 'tool',
|
|
1590
1940
|
messageId: this.#getMessageIdForToolUse(toolType, toolUse),
|
|
1591
1941
|
header,
|
|
1592
|
-
body: warning ? (toolName ===
|
|
1942
|
+
body: warning ? (toolName === toolConstants_1.EXECUTE_BASH ? '' : '\n\n') + body : body,
|
|
1593
1943
|
};
|
|
1594
1944
|
}
|
|
1595
1945
|
else {
|
|
@@ -1602,9 +1952,9 @@ class AgenticChatController {
|
|
|
1602
1952
|
icon: 'tools',
|
|
1603
1953
|
body: `${toolName}`,
|
|
1604
1954
|
buttons: [
|
|
1605
|
-
{ id:
|
|
1955
|
+
{ id: toolConstants_1.BUTTON_ALLOW_TOOLS, text: 'Run', icon: 'play', status: 'clear' },
|
|
1606
1956
|
{
|
|
1607
|
-
id:
|
|
1957
|
+
id: toolConstants_1.BUTTON_REJECT_MCP_TOOL,
|
|
1608
1958
|
text: 'Reject',
|
|
1609
1959
|
icon: 'cancel',
|
|
1610
1960
|
status: 'dimmed-clear',
|
|
@@ -1650,7 +2000,7 @@ class AgenticChatController {
|
|
|
1650
2000
|
},
|
|
1651
2001
|
},
|
|
1652
2002
|
},
|
|
1653
|
-
buttons: [{ id:
|
|
2003
|
+
buttons: [{ id: toolConstants_1.BUTTON_UNDO_CHANGES, text: 'Undo', icon: 'undo' }],
|
|
1654
2004
|
},
|
|
1655
2005
|
};
|
|
1656
2006
|
}
|
|
@@ -1664,7 +2014,7 @@ class AgenticChatController {
|
|
|
1664
2014
|
chatResultStream.setMessageIdToUpdateForTool(toolUse.name, messageIdToUpdate);
|
|
1665
2015
|
}
|
|
1666
2016
|
let currentPaths = [];
|
|
1667
|
-
if (toolUse.name ===
|
|
2017
|
+
if (toolUse.name === toolConstants_1.FS_READ) {
|
|
1668
2018
|
currentPaths = toolUse.input?.paths;
|
|
1669
2019
|
}
|
|
1670
2020
|
else {
|
|
@@ -1695,9 +2045,9 @@ class AgenticChatController {
|
|
|
1695
2045
|
}
|
|
1696
2046
|
else {
|
|
1697
2047
|
title =
|
|
1698
|
-
toolUse.name ===
|
|
2048
|
+
toolUse.name === toolConstants_1.FS_READ
|
|
1699
2049
|
? `${itemCount} file${itemCount > 1 ? 's' : ''} read`
|
|
1700
|
-
: toolUse.name ===
|
|
2050
|
+
: toolUse.name === toolConstants_1.FILE_SEARCH
|
|
1701
2051
|
? `${itemCount} ${itemCount === 1 ? 'directory' : 'directories'} searched`
|
|
1702
2052
|
: `${itemCount} ${itemCount === 1 ? 'directory' : 'directories'} listed`;
|
|
1703
2053
|
}
|
|
@@ -1724,7 +2074,7 @@ class AgenticChatController {
|
|
|
1724
2074
|
* Process grep search results and format them for display in the chat UI
|
|
1725
2075
|
*/
|
|
1726
2076
|
#processGrepSearchResult(toolUse, result, chatResultStream) {
|
|
1727
|
-
if (toolUse.name !==
|
|
2077
|
+
if (toolUse.name !== toolConstants_1.GREP_SEARCH) {
|
|
1728
2078
|
return undefined;
|
|
1729
2079
|
}
|
|
1730
2080
|
let messageIdToUpdate = toolUse.toolUseId;
|
|
@@ -1804,22 +2154,22 @@ class AgenticChatController {
|
|
|
1804
2154
|
/**
|
|
1805
2155
|
* Handles the final result after the agent loop completes
|
|
1806
2156
|
*/
|
|
1807
|
-
async #handleFinalResult(result, session,
|
|
2157
|
+
async #handleFinalResult(result, session, tabId, metric, triggerContext, isNewConversation, chatResultStream) {
|
|
1808
2158
|
if (!result.success) {
|
|
1809
2159
|
throw new errors_2.AgenticChatError(result.error, 'FailedResult');
|
|
1810
2160
|
}
|
|
1811
2161
|
const conversationId = session.conversationId;
|
|
1812
2162
|
this.#debug('Final session conversation id:', conversationId || '');
|
|
1813
2163
|
if (conversationId) {
|
|
1814
|
-
this.#telemetryController.setConversationId(
|
|
2164
|
+
this.#telemetryController.setConversationId(tabId, conversationId);
|
|
1815
2165
|
if (isNewConversation) {
|
|
1816
|
-
this.#telemetryController.updateTriggerInfo(
|
|
2166
|
+
this.#telemetryController.updateTriggerInfo(tabId, {
|
|
1817
2167
|
startTrigger: {
|
|
1818
2168
|
hasUserSnippet: metric.metric.cwsprChatHasCodeSnippet ?? false,
|
|
1819
2169
|
triggerType: triggerContext.triggerType,
|
|
1820
2170
|
},
|
|
1821
2171
|
});
|
|
1822
|
-
this.#telemetryController.emitStartConversationMetric(
|
|
2172
|
+
this.#telemetryController.emitStartConversationMetric(tabId, metric.metric);
|
|
1823
2173
|
}
|
|
1824
2174
|
}
|
|
1825
2175
|
metric.setDimension('codewhispererCustomizationArn', this.#customizationArn);
|
|
@@ -1853,8 +2203,8 @@ class AgenticChatController {
|
|
|
1853
2203
|
cwsprChatPinnedPromptContextCount: triggerContext.contextInfo.pinnedContextCount.promptContextCount,
|
|
1854
2204
|
});
|
|
1855
2205
|
}
|
|
1856
|
-
await this.#telemetryController.emitAddMessageMetric(
|
|
1857
|
-
this.#telemetryController.updateTriggerInfo(
|
|
2206
|
+
await this.#telemetryController.emitAddMessageMetric(tabId, metric.metric, 'Succeeded');
|
|
2207
|
+
this.#telemetryController.updateTriggerInfo(tabId, {
|
|
1858
2208
|
lastMessageTrigger: {
|
|
1859
2209
|
...triggerContext,
|
|
1860
2210
|
messageId: result.data?.chatResult.messageId,
|
|
@@ -2104,7 +2454,7 @@ class AgenticChatController {
|
|
|
2104
2454
|
const session = this.#chatSessionManagementService.getSession(params.tabId);
|
|
2105
2455
|
const toolUseId = params.messageId;
|
|
2106
2456
|
const toolUse = toolUseId ? session.data?.toolUseLookup.get(toolUseId) : undefined;
|
|
2107
|
-
if (toolUse?.name ===
|
|
2457
|
+
if (toolUse?.name === toolConstants_1.FS_WRITE || toolUse?.name === toolConstants_1.FS_REPLACE) {
|
|
2108
2458
|
const input = toolUse.input;
|
|
2109
2459
|
this.#features.lsp.workspace.openFileDiff({
|
|
2110
2460
|
originalFileUri: input.path,
|
|
@@ -2113,7 +2463,7 @@ class AgenticChatController {
|
|
|
2113
2463
|
fileContent: toolUse.fileChange?.after,
|
|
2114
2464
|
});
|
|
2115
2465
|
}
|
|
2116
|
-
else if (toolUse?.name ===
|
|
2466
|
+
else if (toolUse?.name === toolConstants_1.FS_READ) {
|
|
2117
2467
|
await this.#features.lsp.window.showDocument({ uri: vscode_uri_1.URI.file(params.filePath).toString() });
|
|
2118
2468
|
}
|
|
2119
2469
|
else {
|
|
@@ -2323,9 +2673,31 @@ class AgenticChatController {
|
|
|
2323
2673
|
});
|
|
2324
2674
|
return triggerContext;
|
|
2325
2675
|
}
|
|
2676
|
+
async #invalidateCompactCommand(tabId, messageIds) {
|
|
2677
|
+
for (const messageId of messageIds) {
|
|
2678
|
+
await this.#features.chat.sendChatUpdate({
|
|
2679
|
+
tabId,
|
|
2680
|
+
state: { inProgress: false },
|
|
2681
|
+
data: {
|
|
2682
|
+
messages: [
|
|
2683
|
+
{
|
|
2684
|
+
messageId,
|
|
2685
|
+
type: 'tool',
|
|
2686
|
+
body: constants_2.COMPACTION_BODY,
|
|
2687
|
+
header: {
|
|
2688
|
+
body: constants_2.COMPACTION_HEADER_BODY,
|
|
2689
|
+
status: { icon: 'block', text: 'Ignored' },
|
|
2690
|
+
buttons: [],
|
|
2691
|
+
},
|
|
2692
|
+
},
|
|
2693
|
+
],
|
|
2694
|
+
},
|
|
2695
|
+
});
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2326
2698
|
async #invalidateAllShellCommands(tabId, session) {
|
|
2327
2699
|
for (const [toolUseId, toolUse] of session.toolUseLookup.entries()) {
|
|
2328
|
-
if (toolUse.name !==
|
|
2700
|
+
if (toolUse.name !== toolConstants_1.EXECUTE_BASH || this.#stoppedToolUses.has(toolUseId))
|
|
2329
2701
|
continue;
|
|
2330
2702
|
const params = toolUse.input;
|
|
2331
2703
|
const command = params.command;
|
|
@@ -2550,7 +2922,7 @@ class AgenticChatController {
|
|
|
2550
2922
|
return;
|
|
2551
2923
|
}
|
|
2552
2924
|
}
|
|
2553
|
-
async #processAgenticChatResponseWithTimeout(response, metric, chatResultStream, session, contextList) {
|
|
2925
|
+
async #processAgenticChatResponseWithTimeout(response, metric, chatResultStream, session, contextList, isCompaction) {
|
|
2554
2926
|
const abortController = new AbortController();
|
|
2555
2927
|
let timeoutId;
|
|
2556
2928
|
const timeoutPromise = new Promise((_, reject) => {
|
|
@@ -2560,7 +2932,7 @@ class AgenticChatController {
|
|
|
2560
2932
|
}, constants_2.RESPONSE_TIMEOUT_MS);
|
|
2561
2933
|
});
|
|
2562
2934
|
const streamWriter = chatResultStream.getResultStreamWriter();
|
|
2563
|
-
const processResponsePromise = this.#processAgenticChatResponse(response, metric, chatResultStream, streamWriter, session, contextList, abortController.signal);
|
|
2935
|
+
const processResponsePromise = this.#processAgenticChatResponse(response, metric, chatResultStream, streamWriter, session, contextList, abortController.signal, isCompaction);
|
|
2564
2936
|
try {
|
|
2565
2937
|
const result = await Promise.race([processResponsePromise, timeoutPromise]);
|
|
2566
2938
|
clearTimeout(timeoutId);
|
|
@@ -2584,7 +2956,7 @@ class AgenticChatController {
|
|
|
2584
2956
|
}
|
|
2585
2957
|
const toolUses = Object.values(data.toolUses);
|
|
2586
2958
|
for (const toolUse of toolUses) {
|
|
2587
|
-
if ((toolUse.name ===
|
|
2959
|
+
if ((toolUse.name === toolConstants_1.FS_WRITE || toolUse.name === toolConstants_1.FS_REPLACE) && typeof toolUse.input === 'string') {
|
|
2588
2960
|
const filepath = extractKey(toolUse.input, 'path');
|
|
2589
2961
|
const msgId = agenticChatResultStream_1.progressPrefix + toolUse.toolUseId;
|
|
2590
2962
|
// render fs write UI as soon as fs write starts
|
|
@@ -2613,7 +2985,7 @@ class AgenticChatController {
|
|
|
2613
2985
|
}
|
|
2614
2986
|
// render the tool use explanatory as soon as this is received for fsWrite/fsReplace
|
|
2615
2987
|
const explanation = extractKey(toolUse.input, 'explanation');
|
|
2616
|
-
const messageId = agenticChatResultStream_1.progressPrefix + toolUse.toolUseId +
|
|
2988
|
+
const messageId = agenticChatResultStream_1.progressPrefix + toolUse.toolUseId + toolConstants_1.SUFFIX_EXPLANATION;
|
|
2617
2989
|
if (explanation && !chatResultStream.hasMessage(messageId)) {
|
|
2618
2990
|
await streamWriter.close();
|
|
2619
2991
|
await chatResultStream.writeResultBlock({
|
|
@@ -2625,12 +2997,12 @@ class AgenticChatController {
|
|
|
2625
2997
|
}
|
|
2626
2998
|
}
|
|
2627
2999
|
}
|
|
2628
|
-
async #processAgenticChatResponse(response, metric, chatResultStream, streamWriter, session, contextList, abortSignal) {
|
|
3000
|
+
async #processAgenticChatResponse(response, metric, chatResultStream, streamWriter, session, contextList, abortSignal, isCompaction) {
|
|
2629
3001
|
const requestId = response.$metadata.requestId;
|
|
2630
3002
|
const chatEventParser = new agenticChatEventParser_1.AgenticChatEventParser(requestId, metric, this.#features.logging);
|
|
2631
3003
|
// Display context transparency list once at the beginning of response
|
|
2632
3004
|
// Use a flag to track if contextList has been sent already to avoid ux flickering
|
|
2633
|
-
if (contextList?.filePaths && contextList.filePaths.length > 0 && !session.contextListSent) {
|
|
3005
|
+
if (!isCompaction && contextList?.filePaths && contextList.filePaths.length > 0 && !session.contextListSent) {
|
|
2634
3006
|
await streamWriter.write({ body: '', contextList });
|
|
2635
3007
|
session.contextListSent = true;
|
|
2636
3008
|
}
|
|
@@ -2664,7 +3036,7 @@ class AgenticChatController {
|
|
|
2664
3036
|
if (chatEvent.assistantResponseEvent && result.data.chatResult.body) {
|
|
2665
3037
|
this.recordChunk('chunk');
|
|
2666
3038
|
}
|
|
2667
|
-
//
|
|
3039
|
+
// update the UI with response
|
|
2668
3040
|
if (chatEvent.assistantResponseEvent || chatEvent.codeReferenceEvent) {
|
|
2669
3041
|
await streamWriter.write(result.data.chatResult);
|
|
2670
3042
|
}
|
|
@@ -2674,9 +3046,17 @@ class AgenticChatController {
|
|
|
2674
3046
|
}
|
|
2675
3047
|
}
|
|
2676
3048
|
if (isEmptyResponse) {
|
|
2677
|
-
// If the response is empty, we need to send an empty answer message to remove the
|
|
3049
|
+
// If the response is empty, we need to send an empty answer message to remove the Working... indicator
|
|
2678
3050
|
await streamWriter.write({ type: 'answer', body: '', messageId: (0, uuid_1.v4)() });
|
|
2679
3051
|
}
|
|
3052
|
+
if (isCompaction) {
|
|
3053
|
+
// Show a dummy message to the UI for compaction completion
|
|
3054
|
+
await streamWriter.write({
|
|
3055
|
+
type: 'answer',
|
|
3056
|
+
body: 'Conversation history has been compacted successfully!',
|
|
3057
|
+
messageId: (0, uuid_1.v4)(),
|
|
3058
|
+
});
|
|
3059
|
+
}
|
|
2680
3060
|
await streamWriter.close();
|
|
2681
3061
|
metric.mergeWith({
|
|
2682
3062
|
cwsprChatFullResponseLatency: metric.getTimeElapsed(),
|
|
@@ -2900,12 +3280,54 @@ class AgenticChatController {
|
|
|
2900
3280
|
body: (0, textFormatting_1.toolResultMessage)(toolUse, result),
|
|
2901
3281
|
});
|
|
2902
3282
|
}
|
|
3283
|
+
scheduleABTestingFetching(userContext) {
|
|
3284
|
+
if (!userContext) {
|
|
3285
|
+
return;
|
|
3286
|
+
}
|
|
3287
|
+
this.#abTestingFetchingTimeout = setInterval(() => {
|
|
3288
|
+
let codeWhispererServiceToken;
|
|
3289
|
+
try {
|
|
3290
|
+
codeWhispererServiceToken = AmazonQTokenServiceManager_1.AmazonQTokenServiceManager.getInstance().getCodewhispererService();
|
|
3291
|
+
}
|
|
3292
|
+
catch (error) {
|
|
3293
|
+
// getCodewhispererService only returns the cwspr client if the service manager was initialized
|
|
3294
|
+
// i.e. profile was selected otherwise it throws an error
|
|
3295
|
+
// we will not evaluate a/b status until profile is selected and service manager is fully initialized
|
|
3296
|
+
return;
|
|
3297
|
+
}
|
|
3298
|
+
// Clear interval once we have the CodewhispererService
|
|
3299
|
+
clearInterval(this.#abTestingFetchingTimeout);
|
|
3300
|
+
this.#abTestingFetchingTimeout = undefined;
|
|
3301
|
+
codeWhispererServiceToken
|
|
3302
|
+
.listFeatureEvaluations({ userContext })
|
|
3303
|
+
.then(result => {
|
|
3304
|
+
const feature = result.featureEvaluations?.find(feature => feature.feature === 'MaestroWorkspaceContext');
|
|
3305
|
+
if (feature) {
|
|
3306
|
+
this.#abTestingAllocation = {
|
|
3307
|
+
experimentName: feature.feature,
|
|
3308
|
+
userVariation: feature.variation,
|
|
3309
|
+
};
|
|
3310
|
+
}
|
|
3311
|
+
})
|
|
3312
|
+
.catch(error => {
|
|
3313
|
+
this.#features.logging.debug(`Error fetching AB testing result: ${error}`);
|
|
3314
|
+
});
|
|
3315
|
+
}, 5000);
|
|
3316
|
+
}
|
|
2903
3317
|
#log(...messages) {
|
|
2904
3318
|
this.#features.logging.log(messages.join(' '));
|
|
2905
3319
|
}
|
|
2906
3320
|
#debug(...messages) {
|
|
2907
3321
|
this.#features.logging.debug(messages.join(' '));
|
|
2908
3322
|
}
|
|
3323
|
+
// Helper function to sanitize the 'images' field for logging by replacing large binary data (e.g., Uint8Array) with a concise summary.
|
|
3324
|
+
// This prevents logs from being overwhelmed by raw byte arrays and keeps log output readable.
|
|
3325
|
+
#imageReplacer(key, value) {
|
|
3326
|
+
if (key === 'bytes' && value && typeof value.length === 'number') {
|
|
3327
|
+
return `[Uint8Array, length: ${value.length}]`;
|
|
3328
|
+
}
|
|
3329
|
+
return value;
|
|
3330
|
+
}
|
|
2909
3331
|
}
|
|
2910
3332
|
exports.AgenticChatController = AgenticChatController;
|
|
2911
3333
|
//# sourceMappingURL=agenticChatController.js.map
|