@aws/lsp-codewhisperer 0.0.65 → 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 +29 -0
- package/out/language-server/agenticChat/agenticChatController.d.ts +7 -0
- package/out/language-server/agenticChat/agenticChatController.js +415 -114
- 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.js +37 -2
- 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 +1 -1
- 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 +0 -6
- package/out/language-server/agenticChat/tools/executeBash.js +5 -21
- package/out/language-server/agenticChat/tools/executeBash.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 +1 -0
- package/out/language-server/chat/telemetry/chatTelemetryController.js +9 -0
- package/out/language-server/chat/telemetry/chatTelemetryController.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 +29 -17
- package/out/language-server/inline-completion/codeWhispererServer.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/codeWhispererService.js +3 -1
- package/out/shared/codeWhispererService.js.map +1 -1
- package/out/shared/telemetry/telemetryService.js +1 -2
- package/out/shared/telemetry/telemetryService.js.map +1 -1
- package/out/shared/telemetry/types.d.ts +7 -1
- package/out/shared/telemetry/types.js +1 -0
- package/out/shared/telemetry/types.js.map +1 -1
- package/package.json +8 -6
|
@@ -9,6 +9,7 @@ const crypto = require("crypto");
|
|
|
9
9
|
const path = require("path");
|
|
10
10
|
const os = require("os");
|
|
11
11
|
const codewhisperer_streaming_1 = require("@amzn/codewhisperer-streaming");
|
|
12
|
+
const toolConstants_1 = require("./constants/toolConstants");
|
|
12
13
|
const protocol_1 = require("@aws/language-server-runtimes/protocol");
|
|
13
14
|
const protocol_2 = require("@aws/language-server-runtimes/protocol");
|
|
14
15
|
const server_interface_1 = require("@aws/language-server-runtimes/server-interface");
|
|
@@ -62,6 +63,7 @@ const modelSelection_1 = require("./constants/modelSelection");
|
|
|
62
63
|
const imageVerification_1 = require("../../shared/imageVerification");
|
|
63
64
|
const path_1 = require("@aws/lsp-core/out/util/path");
|
|
64
65
|
const agenticChatControllerHelper_1 = require("./utils/agenticChatControllerHelper");
|
|
66
|
+
const activeUserTracker_1 = require("../../shared/activeUserTracker");
|
|
65
67
|
class AgenticChatController {
|
|
66
68
|
#features;
|
|
67
69
|
#chatSessionManagementService;
|
|
@@ -81,6 +83,7 @@ class AgenticChatController {
|
|
|
81
83
|
#mcpEventHandler;
|
|
82
84
|
#paidTierMode;
|
|
83
85
|
#origin;
|
|
86
|
+
#activeUserTracker;
|
|
84
87
|
// latency metrics
|
|
85
88
|
#llmRequestStartTime = 0;
|
|
86
89
|
#toolCallLatencies = [];
|
|
@@ -100,7 +103,9 @@ class AgenticChatController {
|
|
|
100
103
|
#getMessageIdForToolUse(toolType, toolUse) {
|
|
101
104
|
const toolUseId = toolUse.toolUseId;
|
|
102
105
|
// Return plain toolUseId for executeBash, add "_permission" suffix for all other tools
|
|
103
|
-
return toolUse.name ===
|
|
106
|
+
return toolUse.name === toolConstants_1.EXECUTE_BASH || toolType === toolConstants_1.EXECUTE_BASH
|
|
107
|
+
? toolUseId
|
|
108
|
+
: `${toolUseId}${toolConstants_1.SUFFIX_PERMISSION}`;
|
|
104
109
|
}
|
|
105
110
|
/**
|
|
106
111
|
* Logs system information that can be helpful for debugging customer issues
|
|
@@ -119,6 +124,14 @@ class AgenticChatController {
|
|
|
119
124
|
};
|
|
120
125
|
this.#features.logging.info(`System Information: ${JSON.stringify(systemInfo)}`);
|
|
121
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`;
|
|
134
|
+
}
|
|
122
135
|
constructor(chatSessionManagementService, features, telemetryService, serviceManager) {
|
|
123
136
|
this.#features = features;
|
|
124
137
|
this.#chatSessionManagementService = chatSessionManagementService;
|
|
@@ -136,6 +149,7 @@ class AgenticChatController {
|
|
|
136
149
|
this.#contextCommandsProvider = new contextCommandsProvider_1.ContextCommandsProvider(this.#features.logging, this.#features.chat, this.#features.workspace, this.#features.lsp);
|
|
137
150
|
this.#mcpEventHandler = new mcpEventHandler_1.McpEventHandler(features, telemetryService);
|
|
138
151
|
this.#origin = (0, utils_2.getOriginFromClientInfo)(this.#features.lsp.getClientInitializeParams()?.clientInfo?.name);
|
|
152
|
+
this.#activeUserTracker = activeUserTracker_1.ActiveUserTracker.getInstance(this.#features);
|
|
139
153
|
}
|
|
140
154
|
async onExecuteCommand(params, _token) {
|
|
141
155
|
this.#log(`onExecuteCommand: ${params.command}`);
|
|
@@ -152,16 +166,16 @@ class AgenticChatController {
|
|
|
152
166
|
async onButtonClick(params) {
|
|
153
167
|
this.#log(`onButtonClick event with params: ${JSON.stringify(params)}`);
|
|
154
168
|
const session = this.#chatSessionManagementService.getSession(params.tabId);
|
|
155
|
-
if (params.buttonId ===
|
|
156
|
-
params.buttonId ===
|
|
157
|
-
params.buttonId ===
|
|
158
|
-
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) {
|
|
159
173
|
if (!session.data) {
|
|
160
174
|
return { success: false, failureReason: `could not find chat session for tab: ${params.tabId} ` };
|
|
161
175
|
}
|
|
162
176
|
// For 'allow-tools', remove suffix as permission card needs to be seperate from file list card
|
|
163
|
-
const messageId = params.buttonId ===
|
|
164
|
-
? 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, '')
|
|
165
179
|
: params.messageId;
|
|
166
180
|
const handler = session.data.getDeferredToolExecution(messageId);
|
|
167
181
|
if (!handler?.reject || !handler.resolve) {
|
|
@@ -170,7 +184,7 @@ class AgenticChatController {
|
|
|
170
184
|
failureReason: `could not find deferred tool execution for message: ${messageId} `,
|
|
171
185
|
};
|
|
172
186
|
}
|
|
173
|
-
params.buttonId ===
|
|
187
|
+
params.buttonId === toolConstants_1.BUTTON_REJECT_SHELL_COMMAND || params.buttonId === toolConstants_1.BUTTON_REJECT_MCP_TOOL
|
|
174
188
|
? (() => {
|
|
175
189
|
handler.reject(new toolShared_1.ToolApprovalException('Command was rejected.', true));
|
|
176
190
|
this.#stoppedToolUses.add(messageId);
|
|
@@ -180,7 +194,7 @@ class AgenticChatController {
|
|
|
180
194
|
success: true,
|
|
181
195
|
};
|
|
182
196
|
}
|
|
183
|
-
else if (params.buttonId ===
|
|
197
|
+
else if (params.buttonId === toolConstants_1.BUTTON_UNDO_CHANGES) {
|
|
184
198
|
const toolUseId = params.messageId;
|
|
185
199
|
try {
|
|
186
200
|
await this.#undoFileChange(toolUseId, session.data);
|
|
@@ -194,23 +208,23 @@ class AgenticChatController {
|
|
|
194
208
|
success: true,
|
|
195
209
|
};
|
|
196
210
|
}
|
|
197
|
-
else if (params.buttonId ===
|
|
198
|
-
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, '');
|
|
199
213
|
await this.#undoAllFileChanges(params.tabId, toolUseId, session.data);
|
|
200
214
|
return {
|
|
201
215
|
success: true,
|
|
202
216
|
};
|
|
203
217
|
}
|
|
204
|
-
else if (params.buttonId ===
|
|
218
|
+
else if (params.buttonId === toolConstants_1.BUTTON_STOP_SHELL_COMMAND) {
|
|
205
219
|
this.#stoppedToolUses.add(params.messageId);
|
|
206
220
|
await this.#renderStoppedShellCommand(params.tabId, params.messageId);
|
|
207
221
|
return { success: true };
|
|
208
222
|
}
|
|
209
|
-
else if (params.buttonId ===
|
|
223
|
+
else if (params.buttonId === toolConstants_1.BUTTON_PAIDTIER_UPGRADE_Q_LEARNMORE) {
|
|
210
224
|
(0, paidTier_1.onPaidTierLearnMore)(this.#features.lsp, this.#features.logging);
|
|
211
225
|
return { success: true };
|
|
212
226
|
}
|
|
213
|
-
else if (params.buttonId ===
|
|
227
|
+
else if (params.buttonId === toolConstants_1.BUTTON_PAIDTIER_UPGRADE_Q) {
|
|
214
228
|
await this.onManageSubscription(params.tabId);
|
|
215
229
|
return { success: true };
|
|
216
230
|
}
|
|
@@ -242,7 +256,7 @@ class AgenticChatController {
|
|
|
242
256
|
return;
|
|
243
257
|
}
|
|
244
258
|
const fileList = cachedToolUse.chatResult?.header?.fileList;
|
|
245
|
-
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);
|
|
246
260
|
const updatedHeader = {
|
|
247
261
|
...cachedToolUse.chatResult?.header,
|
|
248
262
|
buttons: button,
|
|
@@ -290,7 +304,7 @@ class AgenticChatController {
|
|
|
290
304
|
return;
|
|
291
305
|
}
|
|
292
306
|
for (const messageId of [...toUndo].reverse()) {
|
|
293
|
-
await this.onButtonClick({ buttonId:
|
|
307
|
+
await this.onButtonClick({ buttonId: toolConstants_1.BUTTON_UNDO_CHANGES, messageId, tabId });
|
|
294
308
|
}
|
|
295
309
|
}
|
|
296
310
|
async onOpenFileDialog(params, token) {
|
|
@@ -394,6 +408,7 @@ class AgenticChatController {
|
|
|
394
408
|
this.#contextCommandsProvider?.dispose();
|
|
395
409
|
this.#userWrittenCodeTracker?.dispose();
|
|
396
410
|
this.#mcpEventHandler.dispose();
|
|
411
|
+
this.#activeUserTracker.dispose();
|
|
397
412
|
clearInterval(this.#abTestingFetchingTimeout);
|
|
398
413
|
}
|
|
399
414
|
async onListConversations(params) {
|
|
@@ -457,6 +472,8 @@ class AgenticChatController {
|
|
|
457
472
|
if (!success) {
|
|
458
473
|
return new server_interface_1.ResponseError(protocol_2.ErrorCodes.InternalError, sessionResult.error);
|
|
459
474
|
}
|
|
475
|
+
const compactIds = session.getAllDeferredCompactMessageIds();
|
|
476
|
+
await this.#invalidateCompactCommand(params.tabId, compactIds);
|
|
460
477
|
session.rejectAllDeferredToolExecutions(new toolShared_1.ToolApprovalException('Command ignored: new prompt', false));
|
|
461
478
|
await this.#invalidateAllShellCommands(params.tabId, session);
|
|
462
479
|
const metric = new metric_1.Metric({
|
|
@@ -464,6 +481,10 @@ class AgenticChatController {
|
|
|
464
481
|
experimentName: this.#abTestingAllocation?.experimentName,
|
|
465
482
|
userVariation: this.#abTestingAllocation?.userVariation,
|
|
466
483
|
});
|
|
484
|
+
const isNewActiveUser = this.#activeUserTracker.isNewActiveUser();
|
|
485
|
+
if (isNewActiveUser) {
|
|
486
|
+
this.#telemetryController.emitActiveUser();
|
|
487
|
+
}
|
|
467
488
|
try {
|
|
468
489
|
const triggerContext = await this.#getTriggerContext(params, metric);
|
|
469
490
|
if (triggerContext.programmingLanguage?.languageName) {
|
|
@@ -482,6 +503,8 @@ class AgenticChatController {
|
|
|
482
503
|
this.#log('cancellation requested');
|
|
483
504
|
// Abort all operations immediately
|
|
484
505
|
session.abortRequest();
|
|
506
|
+
const compactIds = session.getAllDeferredCompactMessageIds();
|
|
507
|
+
await this.#invalidateCompactCommand(params.tabId, compactIds);
|
|
485
508
|
void this.#invalidateAllShellCommands(params.tabId, session);
|
|
486
509
|
session.rejectAllDeferredToolExecutions(new lsp_core_1.CancellationError('user'));
|
|
487
510
|
// Then update UI to inform the user
|
|
@@ -519,19 +542,31 @@ class AgenticChatController {
|
|
|
519
542
|
const customContext = await this.#additionalContextProvider.getImageBlocksFromContext(params.context, params.tabId);
|
|
520
543
|
// Add image context to triggerContext.documentReference for transparency
|
|
521
544
|
await this.#additionalContextProvider.appendCustomContextToTriggerContext(triggerContext, params.context, params.tabId);
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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);
|
|
531
566
|
}
|
|
532
567
|
catch (err) {
|
|
533
568
|
// HACK: the chat-client needs to have a partial event with the associated messageId sent before it can accept the final result.
|
|
534
|
-
// Without this, the `
|
|
569
|
+
// Without this, the `working` indicator never goes away.
|
|
535
570
|
// Note: buttons being explicitly empty is required for this hack to work.
|
|
536
571
|
const errorMessageId = `error-message-id-${(0, uuid_1.v4)()}`;
|
|
537
572
|
await this.#sendProgressToClient({
|
|
@@ -563,9 +598,131 @@ class AgenticChatController {
|
|
|
563
598
|
this.#debug('Preparing request input');
|
|
564
599
|
// Get profileArn from the service manager if available
|
|
565
600
|
const profileArn = this.#serviceManager?.getActiveProfileArn();
|
|
566
|
-
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);
|
|
567
612
|
return requestInput;
|
|
568
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
|
+
}
|
|
569
726
|
/**
|
|
570
727
|
* Runs the agent loop, making requests and processing tool uses until completion
|
|
571
728
|
*/
|
|
@@ -574,6 +731,7 @@ class AgenticChatController {
|
|
|
574
731
|
let finalResult = null;
|
|
575
732
|
let iterationCount = 0;
|
|
576
733
|
let shouldDisplayMessage = true;
|
|
734
|
+
let currentRequestCount = 0;
|
|
577
735
|
metric.recordStart();
|
|
578
736
|
this.logSystemInformation();
|
|
579
737
|
while (true) {
|
|
@@ -600,7 +758,11 @@ class AgenticChatController {
|
|
|
600
758
|
if (currentMessage) {
|
|
601
759
|
// Get and process the messages from history DB to maintain invariants for service requests
|
|
602
760
|
try {
|
|
603
|
-
|
|
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}`);
|
|
604
766
|
}
|
|
605
767
|
catch (err) {
|
|
606
768
|
if (err instanceof chatDb_1.ToolResultValidationError) {
|
|
@@ -609,7 +771,7 @@ class AgenticChatController {
|
|
|
609
771
|
}
|
|
610
772
|
}
|
|
611
773
|
}
|
|
612
|
-
//
|
|
774
|
+
// Do not include chatHistory for requests going to Mynah Backend
|
|
613
775
|
currentRequestInput.conversationState.history = currentRequestInput.conversationState?.currentMessage
|
|
614
776
|
?.userInputMessage?.userIntent
|
|
615
777
|
? []
|
|
@@ -620,7 +782,7 @@ class AgenticChatController {
|
|
|
620
782
|
this.#llmRequestStartTime = Date.now();
|
|
621
783
|
// Phase 3: Request Execution
|
|
622
784
|
// Note: these logs are very noisy, but contain information redacted on the backend.
|
|
623
|
-
this.#debug(`generateAssistantResponse/SendMessage Request: ${JSON.stringify(currentRequestInput,
|
|
785
|
+
this.#debug(`generateAssistantResponse/SendMessage Request: ${JSON.stringify(currentRequestInput, this.#imageReplacer, 2)}`);
|
|
624
786
|
const response = await session.getChatResponse(currentRequestInput);
|
|
625
787
|
if (response.$metadata.requestId) {
|
|
626
788
|
metric.mergeWith({
|
|
@@ -758,6 +920,16 @@ class AgenticChatController {
|
|
|
758
920
|
}
|
|
759
921
|
currentRequestInput = this.#updateRequestInputWithToolResults(currentRequestInput, toolResults, content);
|
|
760
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
|
+
}
|
|
761
933
|
return (finalResult || {
|
|
762
934
|
success: false,
|
|
763
935
|
error: 'Agent loop failed to produce a final result',
|
|
@@ -890,36 +1062,36 @@ class AgenticChatController {
|
|
|
890
1062
|
// remove progress UI
|
|
891
1063
|
await chatResultStream.removeResultBlockAndUpdateUI(agenticChatResultStream_1.progressPrefix + toolUse.toolUseId);
|
|
892
1064
|
// fsRead and listDirectory write to an existing card and could show nothing in the current position
|
|
893
|
-
if (![
|
|
1065
|
+
if (![toolConstants_1.FS_WRITE, toolConstants_1.FS_REPLACE, toolConstants_1.FS_READ, toolConstants_1.LIST_DIRECTORY].includes(toolUse.name)) {
|
|
894
1066
|
await this.#showUndoAllIfRequired(chatResultStream, session);
|
|
895
1067
|
}
|
|
896
1068
|
// fsWrite can take a long time, so we render fsWrite Explanatory upon partial streaming responses.
|
|
897
|
-
if (toolUse.name !==
|
|
1069
|
+
if (toolUse.name !== toolConstants_1.FS_WRITE && toolUse.name !== toolConstants_1.FS_REPLACE) {
|
|
898
1070
|
const { explanation } = toolUse.input;
|
|
899
1071
|
if (explanation) {
|
|
900
1072
|
await chatResultStream.writeResultBlock({
|
|
901
1073
|
type: 'directive',
|
|
902
|
-
messageId: toolUse.toolUseId +
|
|
1074
|
+
messageId: toolUse.toolUseId + toolConstants_1.SUFFIX_EXPLANATION,
|
|
903
1075
|
body: explanation,
|
|
904
1076
|
});
|
|
905
1077
|
}
|
|
906
1078
|
}
|
|
907
1079
|
switch (toolUse.name) {
|
|
908
|
-
case
|
|
909
|
-
case
|
|
910
|
-
case
|
|
911
|
-
case
|
|
912
|
-
case
|
|
913
|
-
case
|
|
914
|
-
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: {
|
|
915
1087
|
const toolMap = {
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
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 },
|
|
923
1095
|
};
|
|
924
1096
|
const { Tool } = toolMap[toolUse.name];
|
|
925
1097
|
const tool = new Tool(this.#features);
|
|
@@ -933,11 +1105,11 @@ class AgenticChatController {
|
|
|
933
1105
|
const { requiresAcceptance, warning, commandCategory } = await tool.requiresAcceptance(toolUse.input, approvedPaths);
|
|
934
1106
|
// Honor built-in permission if available, otherwise use tool's requiresAcceptance
|
|
935
1107
|
// const requiresAcceptance = builtInPermission || toolRequiresAcceptance
|
|
936
|
-
if (requiresAcceptance || toolUse.name ===
|
|
1108
|
+
if (requiresAcceptance || toolUse.name === toolConstants_1.EXECUTE_BASH) {
|
|
937
1109
|
// for executeBash, we till send the confirmation message without action buttons
|
|
938
1110
|
const confirmationResult = this.#processToolConfirmation(toolUse, requiresAcceptance, warning, commandCategory);
|
|
939
1111
|
cachedButtonBlockId = await chatResultStream.writeResultBlock(confirmationResult);
|
|
940
|
-
const isExecuteBash = toolUse.name ===
|
|
1112
|
+
const isExecuteBash = toolUse.name === toolConstants_1.EXECUTE_BASH;
|
|
941
1113
|
if (isExecuteBash) {
|
|
942
1114
|
this.#telemetryController.emitInteractWithAgenticChat('GeneratedCommand', tabId, session.pairProgrammingMode, session.getConversationType(), this.#abTestingAllocation?.experimentName, this.#abTestingAllocation?.userVariation);
|
|
943
1115
|
}
|
|
@@ -994,7 +1166,7 @@ class AgenticChatController {
|
|
|
994
1166
|
}
|
|
995
1167
|
break;
|
|
996
1168
|
}
|
|
997
|
-
if (toolUse.name ===
|
|
1169
|
+
if (toolUse.name === toolConstants_1.FS_WRITE || toolUse.name === toolConstants_1.FS_REPLACE) {
|
|
998
1170
|
const input = toolUse.input;
|
|
999
1171
|
const document = await this.#triggerContext.getTextDocumentFromPath(input.path, true, true);
|
|
1000
1172
|
session.toolUseLookup.set(toolUse.toolUseId, {
|
|
@@ -1044,27 +1216,27 @@ class AgenticChatController {
|
|
|
1044
1216
|
content: [toolResultContent],
|
|
1045
1217
|
});
|
|
1046
1218
|
switch (toolUse.name) {
|
|
1047
|
-
case
|
|
1048
|
-
case
|
|
1049
|
-
case
|
|
1219
|
+
case toolConstants_1.FS_READ:
|
|
1220
|
+
case toolConstants_1.LIST_DIRECTORY:
|
|
1221
|
+
case toolConstants_1.FILE_SEARCH:
|
|
1050
1222
|
const initialListDirResult = this.#processReadOrListOrSearch(toolUse, chatResultStream);
|
|
1051
1223
|
if (initialListDirResult) {
|
|
1052
1224
|
await chatResultStream.writeResultBlock(initialListDirResult);
|
|
1053
1225
|
}
|
|
1054
1226
|
break;
|
|
1055
1227
|
// no need to write tool result for listDir,fsRead,fileSearch into chat stream
|
|
1056
|
-
case
|
|
1228
|
+
case toolConstants_1.EXECUTE_BASH:
|
|
1057
1229
|
// no need to write tool result for listDir and fsRead into chat stream
|
|
1058
1230
|
// executeBash will stream the output instead of waiting until the end
|
|
1059
1231
|
break;
|
|
1060
|
-
case
|
|
1232
|
+
case toolConstants_1.GREP_SEARCH:
|
|
1061
1233
|
const grepSearchResult = this.#processGrepSearchResult(toolUse, result, chatResultStream);
|
|
1062
1234
|
if (grepSearchResult) {
|
|
1063
1235
|
await chatResultStream.writeResultBlock(grepSearchResult);
|
|
1064
1236
|
}
|
|
1065
1237
|
break;
|
|
1066
|
-
case
|
|
1067
|
-
case
|
|
1238
|
+
case toolConstants_1.FS_REPLACE:
|
|
1239
|
+
case toolConstants_1.FS_WRITE:
|
|
1068
1240
|
const input = toolUse.input;
|
|
1069
1241
|
// Load from the filesystem instead of workspace.
|
|
1070
1242
|
// Workspace is likely out of date - when files
|
|
@@ -1149,12 +1321,12 @@ class AgenticChatController {
|
|
|
1149
1321
|
continue;
|
|
1150
1322
|
}
|
|
1151
1323
|
// Rethrow error for executeBash or any named tool
|
|
1152
|
-
if (toolUse.name ===
|
|
1324
|
+
if (toolUse.name === toolConstants_1.EXECUTE_BASH || toolUse.name) {
|
|
1153
1325
|
throw err;
|
|
1154
1326
|
}
|
|
1155
1327
|
}
|
|
1156
1328
|
// display fs write failure status in the UX of that file card
|
|
1157
|
-
if ((toolUse.name ===
|
|
1329
|
+
if ((toolUse.name === toolConstants_1.FS_WRITE || toolUse.name === toolConstants_1.FS_REPLACE) && toolUse.toolUseId) {
|
|
1158
1330
|
const existingCard = chatResultStream.getMessageBlockId(toolUse.toolUseId);
|
|
1159
1331
|
const fsParam = toolUse.input;
|
|
1160
1332
|
if (fsParam.path) {
|
|
@@ -1188,7 +1360,7 @@ class AgenticChatController {
|
|
|
1188
1360
|
}
|
|
1189
1361
|
}
|
|
1190
1362
|
}
|
|
1191
|
-
else if (toolUse.name ===
|
|
1363
|
+
else if (toolUse.name === toolConstants_1.EXECUTE_BASH && toolUse.toolUseId) {
|
|
1192
1364
|
const existingCard = chatResultStream.getMessageBlockId(toolUse.toolUseId);
|
|
1193
1365
|
const command = toolUse.input.command;
|
|
1194
1366
|
const completedErrorResult = {
|
|
@@ -1246,10 +1418,10 @@ class AgenticChatController {
|
|
|
1246
1418
|
* Updates the currentUndoAllId state in the session
|
|
1247
1419
|
*/
|
|
1248
1420
|
#updateUndoAllState(toolUse, session) {
|
|
1249
|
-
if (toolUse.name ===
|
|
1421
|
+
if (toolUse.name === toolConstants_1.FS_READ || toolUse.name === toolConstants_1.LIST_DIRECTORY) {
|
|
1250
1422
|
return;
|
|
1251
1423
|
}
|
|
1252
|
-
if (toolUse.name ===
|
|
1424
|
+
if (toolUse.name === toolConstants_1.FS_WRITE || toolUse.name === toolConstants_1.FS_REPLACE) {
|
|
1253
1425
|
if (session.currentUndoAllId === undefined) {
|
|
1254
1426
|
session.currentUndoAllId = toolUse.toolUseId;
|
|
1255
1427
|
}
|
|
@@ -1284,10 +1456,10 @@ class AgenticChatController {
|
|
|
1284
1456
|
}
|
|
1285
1457
|
await chatResultStream.writeResultBlock({
|
|
1286
1458
|
type: 'answer',
|
|
1287
|
-
messageId: `${session.currentUndoAllId}
|
|
1459
|
+
messageId: `${session.currentUndoAllId}${toolConstants_1.SUFFIX_UNDOALL}`,
|
|
1288
1460
|
buttons: [
|
|
1289
1461
|
{
|
|
1290
|
-
id:
|
|
1462
|
+
id: toolConstants_1.BUTTON_UNDO_ALL_CHANGES,
|
|
1291
1463
|
text: 'Undo all changes',
|
|
1292
1464
|
icon: 'undo',
|
|
1293
1465
|
status: 'clear',
|
|
@@ -1315,11 +1487,11 @@ class AgenticChatController {
|
|
|
1315
1487
|
#validateToolResult(toolUse, result) {
|
|
1316
1488
|
let maxToolResponseSize;
|
|
1317
1489
|
switch (toolUse.name) {
|
|
1318
|
-
case
|
|
1319
|
-
case
|
|
1490
|
+
case toolConstants_1.FS_READ:
|
|
1491
|
+
case toolConstants_1.EXECUTE_BASH:
|
|
1320
1492
|
// fsRead and executeBash already have truncation logic
|
|
1321
1493
|
return;
|
|
1322
|
-
case
|
|
1494
|
+
case toolConstants_1.LIST_DIRECTORY:
|
|
1323
1495
|
maxToolResponseSize = 50_000;
|
|
1324
1496
|
break;
|
|
1325
1497
|
default:
|
|
@@ -1371,7 +1543,7 @@ class AgenticChatController {
|
|
|
1371
1543
|
if (toolUse.name === qCodeReview_1.QCodeReview.toolName) {
|
|
1372
1544
|
return this.#getToolOverWritableStream(chatResultStream, toolUse);
|
|
1373
1545
|
}
|
|
1374
|
-
if (toolUse.name !==
|
|
1546
|
+
if (toolUse.name !== toolConstants_1.EXECUTE_BASH) {
|
|
1375
1547
|
return;
|
|
1376
1548
|
}
|
|
1377
1549
|
const toolMsgId = toolUse.toolUseId;
|
|
@@ -1379,7 +1551,7 @@ class AgenticChatController {
|
|
|
1379
1551
|
let headerEmitted = false;
|
|
1380
1552
|
const initialHeader = {
|
|
1381
1553
|
body: 'shell',
|
|
1382
|
-
buttons: [{ id:
|
|
1554
|
+
buttons: [{ id: toolConstants_1.BUTTON_STOP_SHELL_COMMAND, text: 'Cancel', icon: 'stop' }],
|
|
1383
1555
|
};
|
|
1384
1556
|
const completedHeader = {
|
|
1385
1557
|
body: 'shell',
|
|
@@ -1427,7 +1599,7 @@ class AgenticChatController {
|
|
|
1427
1599
|
#getUpdateToolConfirmResult(toolUse, isAccept, originalToolName, toolType) {
|
|
1428
1600
|
const toolName = originalToolName ?? (toolType || toolUse.name);
|
|
1429
1601
|
// Handle bash commands with special formatting
|
|
1430
|
-
if (toolName ===
|
|
1602
|
+
if (toolName === toolConstants_1.EXECUTE_BASH) {
|
|
1431
1603
|
return {
|
|
1432
1604
|
messageId: toolUse.toolUseId,
|
|
1433
1605
|
type: 'tool',
|
|
@@ -1443,7 +1615,7 @@ class AgenticChatController {
|
|
|
1443
1615
|
text: 'Rejected',
|
|
1444
1616
|
},
|
|
1445
1617
|
}),
|
|
1446
|
-
buttons: isAccept ? [{ id:
|
|
1618
|
+
buttons: isAccept ? [{ id: toolConstants_1.BUTTON_STOP_SHELL_COMMAND, text: 'Cancel', icon: 'stop' }] : [],
|
|
1447
1619
|
},
|
|
1448
1620
|
};
|
|
1449
1621
|
}
|
|
@@ -1451,10 +1623,10 @@ class AgenticChatController {
|
|
|
1451
1623
|
let header;
|
|
1452
1624
|
let body;
|
|
1453
1625
|
switch (toolName) {
|
|
1454
|
-
case
|
|
1455
|
-
case
|
|
1456
|
-
case
|
|
1457
|
-
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:
|
|
1458
1630
|
header = {
|
|
1459
1631
|
body: undefined,
|
|
1460
1632
|
status: {
|
|
@@ -1464,7 +1636,7 @@ class AgenticChatController {
|
|
|
1464
1636
|
},
|
|
1465
1637
|
};
|
|
1466
1638
|
break;
|
|
1467
|
-
case
|
|
1639
|
+
case toolConstants_1.FILE_SEARCH:
|
|
1468
1640
|
const searchPath = toolUse.input.path;
|
|
1469
1641
|
header = {
|
|
1470
1642
|
body: 'File Search',
|
|
@@ -1531,7 +1703,7 @@ class AgenticChatController {
|
|
|
1531
1703
|
status: {
|
|
1532
1704
|
status: 'error',
|
|
1533
1705
|
icon: 'stop',
|
|
1534
|
-
text: '
|
|
1706
|
+
text: 'Canceled',
|
|
1535
1707
|
},
|
|
1536
1708
|
buttons: [],
|
|
1537
1709
|
},
|
|
@@ -1540,6 +1712,97 @@ class AgenticChatController {
|
|
|
1540
1712
|
},
|
|
1541
1713
|
});
|
|
1542
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
|
+
}
|
|
1543
1806
|
#processToolConfirmation(toolUse, requiresAcceptance, warning, commandCategory, toolType, builtInPermission) {
|
|
1544
1807
|
const toolName = toolType || toolUse.name;
|
|
1545
1808
|
let buttons = [];
|
|
@@ -1547,13 +1810,13 @@ class AgenticChatController {
|
|
|
1547
1810
|
let body;
|
|
1548
1811
|
// Configure tool-specific UI elements
|
|
1549
1812
|
switch (toolName) {
|
|
1550
|
-
case
|
|
1813
|
+
case toolConstants_1.EXECUTE_BASH: {
|
|
1551
1814
|
const commandString = toolUse.input.command;
|
|
1552
1815
|
buttons = requiresAcceptance
|
|
1553
1816
|
? [
|
|
1554
|
-
{ id:
|
|
1817
|
+
{ id: toolConstants_1.BUTTON_RUN_SHELL_COMMAND, text: 'Run', icon: 'play' },
|
|
1555
1818
|
{
|
|
1556
|
-
id:
|
|
1819
|
+
id: toolConstants_1.BUTTON_REJECT_SHELL_COMMAND,
|
|
1557
1820
|
status: 'dimmed-clear',
|
|
1558
1821
|
text: 'Reject',
|
|
1559
1822
|
icon: 'cancel',
|
|
@@ -1585,12 +1848,12 @@ class AgenticChatController {
|
|
|
1585
1848
|
body = '```shell\n' + commandString;
|
|
1586
1849
|
break;
|
|
1587
1850
|
}
|
|
1588
|
-
case
|
|
1851
|
+
case toolConstants_1.FS_WRITE: {
|
|
1589
1852
|
const writeFilePath = toolUse.input.path;
|
|
1590
1853
|
// Validate the path using our synchronous utility
|
|
1591
1854
|
(0, pathValidation_1.validatePathBasic)(writeFilePath);
|
|
1592
1855
|
this.#debug(`Processing ${toolUse.name} for path: ${writeFilePath}`);
|
|
1593
|
-
buttons = [{ id:
|
|
1856
|
+
buttons = [{ id: toolConstants_1.BUTTON_ALLOW_TOOLS, text: 'Allow', icon: 'ok', status: 'clear' }];
|
|
1594
1857
|
header = {
|
|
1595
1858
|
icon: 'warning',
|
|
1596
1859
|
iconForegroundStatus: 'warning',
|
|
@@ -1604,12 +1867,12 @@ class AgenticChatController {
|
|
|
1604
1867
|
: `I need permission to modify files outside of your workspace.\n\`${writeFilePath}\``;
|
|
1605
1868
|
break;
|
|
1606
1869
|
}
|
|
1607
|
-
case
|
|
1870
|
+
case toolConstants_1.FS_REPLACE: {
|
|
1608
1871
|
const writeFilePath = toolUse.input.path;
|
|
1609
1872
|
// For replace, we need to verify the file exists
|
|
1610
1873
|
(0, pathValidation_1.validatePathExists)(writeFilePath);
|
|
1611
1874
|
this.#debug(`Processing ${toolUse.name} for path: ${writeFilePath}`);
|
|
1612
|
-
buttons = [{ id:
|
|
1875
|
+
buttons = [{ id: toolConstants_1.BUTTON_ALLOW_TOOLS, text: 'Allow', icon: 'ok', status: 'clear' }];
|
|
1613
1876
|
header = {
|
|
1614
1877
|
icon: 'warning',
|
|
1615
1878
|
iconForegroundStatus: 'warning',
|
|
@@ -1623,9 +1886,9 @@ class AgenticChatController {
|
|
|
1623
1886
|
: `I need permission to modify files outside of your workspace.\n\`${writeFilePath}\``;
|
|
1624
1887
|
break;
|
|
1625
1888
|
}
|
|
1626
|
-
case
|
|
1627
|
-
case
|
|
1628
|
-
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' }];
|
|
1629
1892
|
header = {
|
|
1630
1893
|
icon: 'tools',
|
|
1631
1894
|
iconForegroundStatus: 'tools',
|
|
@@ -1634,7 +1897,7 @@ class AgenticChatController {
|
|
|
1634
1897
|
: '#### Allow read-only tools outside your workspace',
|
|
1635
1898
|
buttons,
|
|
1636
1899
|
};
|
|
1637
|
-
if (toolName ===
|
|
1900
|
+
if (toolName === toolConstants_1.FS_READ) {
|
|
1638
1901
|
const paths = toolUse.input.paths;
|
|
1639
1902
|
// Validate paths using our synchronous utility
|
|
1640
1903
|
(0, pathValidation_1.validatePaths)(paths);
|
|
@@ -1658,7 +1921,7 @@ class AgenticChatController {
|
|
|
1658
1921
|
}
|
|
1659
1922
|
default: {
|
|
1660
1923
|
// — DEFAULT ⇒ MCP tools
|
|
1661
|
-
buttons = [{ id:
|
|
1924
|
+
buttons = [{ id: toolConstants_1.BUTTON_ALLOW_TOOLS, text: 'Allow', icon: 'ok', status: 'clear' }];
|
|
1662
1925
|
header = {
|
|
1663
1926
|
icon: 'tools',
|
|
1664
1927
|
iconForegroundStatus: 'warning',
|
|
@@ -1676,7 +1939,7 @@ class AgenticChatController {
|
|
|
1676
1939
|
type: 'tool',
|
|
1677
1940
|
messageId: this.#getMessageIdForToolUse(toolType, toolUse),
|
|
1678
1941
|
header,
|
|
1679
|
-
body: warning ? (toolName ===
|
|
1942
|
+
body: warning ? (toolName === toolConstants_1.EXECUTE_BASH ? '' : '\n\n') + body : body,
|
|
1680
1943
|
};
|
|
1681
1944
|
}
|
|
1682
1945
|
else {
|
|
@@ -1689,9 +1952,9 @@ class AgenticChatController {
|
|
|
1689
1952
|
icon: 'tools',
|
|
1690
1953
|
body: `${toolName}`,
|
|
1691
1954
|
buttons: [
|
|
1692
|
-
{ id:
|
|
1955
|
+
{ id: toolConstants_1.BUTTON_ALLOW_TOOLS, text: 'Run', icon: 'play', status: 'clear' },
|
|
1693
1956
|
{
|
|
1694
|
-
id:
|
|
1957
|
+
id: toolConstants_1.BUTTON_REJECT_MCP_TOOL,
|
|
1695
1958
|
text: 'Reject',
|
|
1696
1959
|
icon: 'cancel',
|
|
1697
1960
|
status: 'dimmed-clear',
|
|
@@ -1737,7 +2000,7 @@ class AgenticChatController {
|
|
|
1737
2000
|
},
|
|
1738
2001
|
},
|
|
1739
2002
|
},
|
|
1740
|
-
buttons: [{ id:
|
|
2003
|
+
buttons: [{ id: toolConstants_1.BUTTON_UNDO_CHANGES, text: 'Undo', icon: 'undo' }],
|
|
1741
2004
|
},
|
|
1742
2005
|
};
|
|
1743
2006
|
}
|
|
@@ -1751,7 +2014,7 @@ class AgenticChatController {
|
|
|
1751
2014
|
chatResultStream.setMessageIdToUpdateForTool(toolUse.name, messageIdToUpdate);
|
|
1752
2015
|
}
|
|
1753
2016
|
let currentPaths = [];
|
|
1754
|
-
if (toolUse.name ===
|
|
2017
|
+
if (toolUse.name === toolConstants_1.FS_READ) {
|
|
1755
2018
|
currentPaths = toolUse.input?.paths;
|
|
1756
2019
|
}
|
|
1757
2020
|
else {
|
|
@@ -1782,9 +2045,9 @@ class AgenticChatController {
|
|
|
1782
2045
|
}
|
|
1783
2046
|
else {
|
|
1784
2047
|
title =
|
|
1785
|
-
toolUse.name ===
|
|
2048
|
+
toolUse.name === toolConstants_1.FS_READ
|
|
1786
2049
|
? `${itemCount} file${itemCount > 1 ? 's' : ''} read`
|
|
1787
|
-
: toolUse.name ===
|
|
2050
|
+
: toolUse.name === toolConstants_1.FILE_SEARCH
|
|
1788
2051
|
? `${itemCount} ${itemCount === 1 ? 'directory' : 'directories'} searched`
|
|
1789
2052
|
: `${itemCount} ${itemCount === 1 ? 'directory' : 'directories'} listed`;
|
|
1790
2053
|
}
|
|
@@ -1811,7 +2074,7 @@ class AgenticChatController {
|
|
|
1811
2074
|
* Process grep search results and format them for display in the chat UI
|
|
1812
2075
|
*/
|
|
1813
2076
|
#processGrepSearchResult(toolUse, result, chatResultStream) {
|
|
1814
|
-
if (toolUse.name !==
|
|
2077
|
+
if (toolUse.name !== toolConstants_1.GREP_SEARCH) {
|
|
1815
2078
|
return undefined;
|
|
1816
2079
|
}
|
|
1817
2080
|
let messageIdToUpdate = toolUse.toolUseId;
|
|
@@ -1891,22 +2154,22 @@ class AgenticChatController {
|
|
|
1891
2154
|
/**
|
|
1892
2155
|
* Handles the final result after the agent loop completes
|
|
1893
2156
|
*/
|
|
1894
|
-
async #handleFinalResult(result, session,
|
|
2157
|
+
async #handleFinalResult(result, session, tabId, metric, triggerContext, isNewConversation, chatResultStream) {
|
|
1895
2158
|
if (!result.success) {
|
|
1896
2159
|
throw new errors_2.AgenticChatError(result.error, 'FailedResult');
|
|
1897
2160
|
}
|
|
1898
2161
|
const conversationId = session.conversationId;
|
|
1899
2162
|
this.#debug('Final session conversation id:', conversationId || '');
|
|
1900
2163
|
if (conversationId) {
|
|
1901
|
-
this.#telemetryController.setConversationId(
|
|
2164
|
+
this.#telemetryController.setConversationId(tabId, conversationId);
|
|
1902
2165
|
if (isNewConversation) {
|
|
1903
|
-
this.#telemetryController.updateTriggerInfo(
|
|
2166
|
+
this.#telemetryController.updateTriggerInfo(tabId, {
|
|
1904
2167
|
startTrigger: {
|
|
1905
2168
|
hasUserSnippet: metric.metric.cwsprChatHasCodeSnippet ?? false,
|
|
1906
2169
|
triggerType: triggerContext.triggerType,
|
|
1907
2170
|
},
|
|
1908
2171
|
});
|
|
1909
|
-
this.#telemetryController.emitStartConversationMetric(
|
|
2172
|
+
this.#telemetryController.emitStartConversationMetric(tabId, metric.metric);
|
|
1910
2173
|
}
|
|
1911
2174
|
}
|
|
1912
2175
|
metric.setDimension('codewhispererCustomizationArn', this.#customizationArn);
|
|
@@ -1940,8 +2203,8 @@ class AgenticChatController {
|
|
|
1940
2203
|
cwsprChatPinnedPromptContextCount: triggerContext.contextInfo.pinnedContextCount.promptContextCount,
|
|
1941
2204
|
});
|
|
1942
2205
|
}
|
|
1943
|
-
await this.#telemetryController.emitAddMessageMetric(
|
|
1944
|
-
this.#telemetryController.updateTriggerInfo(
|
|
2206
|
+
await this.#telemetryController.emitAddMessageMetric(tabId, metric.metric, 'Succeeded');
|
|
2207
|
+
this.#telemetryController.updateTriggerInfo(tabId, {
|
|
1945
2208
|
lastMessageTrigger: {
|
|
1946
2209
|
...triggerContext,
|
|
1947
2210
|
messageId: result.data?.chatResult.messageId,
|
|
@@ -2191,7 +2454,7 @@ class AgenticChatController {
|
|
|
2191
2454
|
const session = this.#chatSessionManagementService.getSession(params.tabId);
|
|
2192
2455
|
const toolUseId = params.messageId;
|
|
2193
2456
|
const toolUse = toolUseId ? session.data?.toolUseLookup.get(toolUseId) : undefined;
|
|
2194
|
-
if (toolUse?.name ===
|
|
2457
|
+
if (toolUse?.name === toolConstants_1.FS_WRITE || toolUse?.name === toolConstants_1.FS_REPLACE) {
|
|
2195
2458
|
const input = toolUse.input;
|
|
2196
2459
|
this.#features.lsp.workspace.openFileDiff({
|
|
2197
2460
|
originalFileUri: input.path,
|
|
@@ -2200,7 +2463,7 @@ class AgenticChatController {
|
|
|
2200
2463
|
fileContent: toolUse.fileChange?.after,
|
|
2201
2464
|
});
|
|
2202
2465
|
}
|
|
2203
|
-
else if (toolUse?.name ===
|
|
2466
|
+
else if (toolUse?.name === toolConstants_1.FS_READ) {
|
|
2204
2467
|
await this.#features.lsp.window.showDocument({ uri: vscode_uri_1.URI.file(params.filePath).toString() });
|
|
2205
2468
|
}
|
|
2206
2469
|
else {
|
|
@@ -2410,9 +2673,31 @@ class AgenticChatController {
|
|
|
2410
2673
|
});
|
|
2411
2674
|
return triggerContext;
|
|
2412
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
|
+
}
|
|
2413
2698
|
async #invalidateAllShellCommands(tabId, session) {
|
|
2414
2699
|
for (const [toolUseId, toolUse] of session.toolUseLookup.entries()) {
|
|
2415
|
-
if (toolUse.name !==
|
|
2700
|
+
if (toolUse.name !== toolConstants_1.EXECUTE_BASH || this.#stoppedToolUses.has(toolUseId))
|
|
2416
2701
|
continue;
|
|
2417
2702
|
const params = toolUse.input;
|
|
2418
2703
|
const command = params.command;
|
|
@@ -2637,7 +2922,7 @@ class AgenticChatController {
|
|
|
2637
2922
|
return;
|
|
2638
2923
|
}
|
|
2639
2924
|
}
|
|
2640
|
-
async #processAgenticChatResponseWithTimeout(response, metric, chatResultStream, session, contextList) {
|
|
2925
|
+
async #processAgenticChatResponseWithTimeout(response, metric, chatResultStream, session, contextList, isCompaction) {
|
|
2641
2926
|
const abortController = new AbortController();
|
|
2642
2927
|
let timeoutId;
|
|
2643
2928
|
const timeoutPromise = new Promise((_, reject) => {
|
|
@@ -2647,7 +2932,7 @@ class AgenticChatController {
|
|
|
2647
2932
|
}, constants_2.RESPONSE_TIMEOUT_MS);
|
|
2648
2933
|
});
|
|
2649
2934
|
const streamWriter = chatResultStream.getResultStreamWriter();
|
|
2650
|
-
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);
|
|
2651
2936
|
try {
|
|
2652
2937
|
const result = await Promise.race([processResponsePromise, timeoutPromise]);
|
|
2653
2938
|
clearTimeout(timeoutId);
|
|
@@ -2671,7 +2956,7 @@ class AgenticChatController {
|
|
|
2671
2956
|
}
|
|
2672
2957
|
const toolUses = Object.values(data.toolUses);
|
|
2673
2958
|
for (const toolUse of toolUses) {
|
|
2674
|
-
if ((toolUse.name ===
|
|
2959
|
+
if ((toolUse.name === toolConstants_1.FS_WRITE || toolUse.name === toolConstants_1.FS_REPLACE) && typeof toolUse.input === 'string') {
|
|
2675
2960
|
const filepath = extractKey(toolUse.input, 'path');
|
|
2676
2961
|
const msgId = agenticChatResultStream_1.progressPrefix + toolUse.toolUseId;
|
|
2677
2962
|
// render fs write UI as soon as fs write starts
|
|
@@ -2700,7 +2985,7 @@ class AgenticChatController {
|
|
|
2700
2985
|
}
|
|
2701
2986
|
// render the tool use explanatory as soon as this is received for fsWrite/fsReplace
|
|
2702
2987
|
const explanation = extractKey(toolUse.input, 'explanation');
|
|
2703
|
-
const messageId = agenticChatResultStream_1.progressPrefix + toolUse.toolUseId +
|
|
2988
|
+
const messageId = agenticChatResultStream_1.progressPrefix + toolUse.toolUseId + toolConstants_1.SUFFIX_EXPLANATION;
|
|
2704
2989
|
if (explanation && !chatResultStream.hasMessage(messageId)) {
|
|
2705
2990
|
await streamWriter.close();
|
|
2706
2991
|
await chatResultStream.writeResultBlock({
|
|
@@ -2712,12 +2997,12 @@ class AgenticChatController {
|
|
|
2712
2997
|
}
|
|
2713
2998
|
}
|
|
2714
2999
|
}
|
|
2715
|
-
async #processAgenticChatResponse(response, metric, chatResultStream, streamWriter, session, contextList, abortSignal) {
|
|
3000
|
+
async #processAgenticChatResponse(response, metric, chatResultStream, streamWriter, session, contextList, abortSignal, isCompaction) {
|
|
2716
3001
|
const requestId = response.$metadata.requestId;
|
|
2717
3002
|
const chatEventParser = new agenticChatEventParser_1.AgenticChatEventParser(requestId, metric, this.#features.logging);
|
|
2718
3003
|
// Display context transparency list once at the beginning of response
|
|
2719
3004
|
// Use a flag to track if contextList has been sent already to avoid ux flickering
|
|
2720
|
-
if (contextList?.filePaths && contextList.filePaths.length > 0 && !session.contextListSent) {
|
|
3005
|
+
if (!isCompaction && contextList?.filePaths && contextList.filePaths.length > 0 && !session.contextListSent) {
|
|
2721
3006
|
await streamWriter.write({ body: '', contextList });
|
|
2722
3007
|
session.contextListSent = true;
|
|
2723
3008
|
}
|
|
@@ -2751,7 +3036,7 @@ class AgenticChatController {
|
|
|
2751
3036
|
if (chatEvent.assistantResponseEvent && result.data.chatResult.body) {
|
|
2752
3037
|
this.recordChunk('chunk');
|
|
2753
3038
|
}
|
|
2754
|
-
//
|
|
3039
|
+
// update the UI with response
|
|
2755
3040
|
if (chatEvent.assistantResponseEvent || chatEvent.codeReferenceEvent) {
|
|
2756
3041
|
await streamWriter.write(result.data.chatResult);
|
|
2757
3042
|
}
|
|
@@ -2761,9 +3046,17 @@ class AgenticChatController {
|
|
|
2761
3046
|
}
|
|
2762
3047
|
}
|
|
2763
3048
|
if (isEmptyResponse) {
|
|
2764
|
-
// 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
|
|
2765
3050
|
await streamWriter.write({ type: 'answer', body: '', messageId: (0, uuid_1.v4)() });
|
|
2766
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
|
+
}
|
|
2767
3060
|
await streamWriter.close();
|
|
2768
3061
|
metric.mergeWith({
|
|
2769
3062
|
cwsprChatFullResponseLatency: metric.getTimeElapsed(),
|
|
@@ -3027,6 +3320,14 @@ class AgenticChatController {
|
|
|
3027
3320
|
#debug(...messages) {
|
|
3028
3321
|
this.#features.logging.debug(messages.join(' '));
|
|
3029
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
|
+
}
|
|
3030
3331
|
}
|
|
3031
3332
|
exports.AgenticChatController = AgenticChatController;
|
|
3032
3333
|
//# sourceMappingURL=agenticChatController.js.map
|