@vybestack/llxprt-code-core 0.2.24 → 0.3.4-nightly.250912.5e46408e
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/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/src/code_assist/converter.js +35 -3
- package/dist/src/code_assist/converter.js.map +1 -1
- package/dist/src/code_assist/oauth2.d.ts +1 -1
- package/dist/src/code_assist/oauth2.js +18 -25
- package/dist/src/code_assist/oauth2.js.map +1 -1
- package/dist/src/config/config.d.ts +33 -2
- package/dist/src/config/config.js +73 -13
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/storage.d.ts +33 -0
- package/dist/src/config/storage.js +93 -0
- package/dist/src/config/storage.js.map +1 -0
- package/dist/src/core/client.d.ts +8 -5
- package/dist/src/core/client.js +119 -20
- package/dist/src/core/client.js.map +1 -1
- package/dist/src/core/contentGenerator.js +11 -0
- package/dist/src/core/contentGenerator.js.map +1 -1
- package/dist/src/core/coreToolScheduler.d.ts +18 -12
- package/dist/src/core/coreToolScheduler.js +235 -241
- package/dist/src/core/coreToolScheduler.js.map +1 -1
- package/dist/src/core/logger.d.ts +3 -1
- package/dist/src/core/logger.js +5 -3
- package/dist/src/core/logger.js.map +1 -1
- package/dist/src/core/nonInteractiveToolExecutor.js +2 -2
- package/dist/src/core/nonInteractiveToolExecutor.js.map +1 -1
- package/dist/src/core/subagent.js +2 -12
- package/dist/src/core/subagent.js.map +1 -1
- package/dist/src/core/turn.d.ts +13 -2
- package/dist/src/core/turn.js +15 -4
- package/dist/src/core/turn.js.map +1 -1
- package/dist/src/filters/EmojiFilter.d.ts +5 -0
- package/dist/src/filters/EmojiFilter.js +3 -2
- package/dist/src/filters/EmojiFilter.js.map +1 -1
- package/dist/src/ide/detect-ide.d.ts +8 -3
- package/dist/src/ide/detect-ide.js +29 -11
- package/dist/src/ide/detect-ide.js.map +1 -1
- package/dist/src/ide/ide-client.d.ts +5 -4
- package/dist/src/ide/ide-client.js +21 -17
- package/dist/src/ide/ide-client.js.map +1 -1
- package/dist/src/ide/ide-installer.d.ts +1 -1
- package/dist/src/ide/ide-installer.js +29 -20
- package/dist/src/ide/ide-installer.js.map +1 -1
- package/dist/src/ide/process-utils.d.ts +7 -5
- package/dist/src/ide/process-utils.js +80 -47
- package/dist/src/ide/process-utils.js.map +1 -1
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +7 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/file-token-store.d.ts +58 -0
- package/dist/src/mcp/file-token-store.js +181 -0
- package/dist/src/mcp/file-token-store.js.map +1 -0
- package/dist/src/mcp/oauth-token-storage.d.ts +13 -28
- package/dist/src/mcp/oauth-token-storage.js +21 -87
- package/dist/src/mcp/oauth-token-storage.js.map +1 -1
- package/dist/src/mcp/oauth-utils.d.ts +8 -0
- package/dist/src/mcp/oauth-utils.js +41 -27
- package/dist/src/mcp/oauth-utils.js.map +1 -1
- package/dist/src/mcp/token-store.d.ts +99 -0
- package/dist/src/mcp/token-store.js +77 -0
- package/dist/src/mcp/token-store.js.map +1 -0
- package/dist/src/parsers/TextToolCallParser.d.ts +5 -0
- package/dist/src/parsers/TextToolCallParser.js +3 -13
- package/dist/src/parsers/TextToolCallParser.js.map +1 -1
- package/dist/src/providers/anthropic/AnthropicProvider.d.ts +5 -0
- package/dist/src/providers/anthropic/AnthropicProvider.js +26 -8
- package/dist/src/providers/anthropic/AnthropicProvider.js.map +1 -1
- package/dist/src/providers/gemini/GeminiProvider.js +16 -1
- package/dist/src/providers/gemini/GeminiProvider.js.map +1 -1
- package/dist/src/providers/openai/OpenAIProvider.d.ts +15 -0
- package/dist/src/providers/openai/OpenAIProvider.js +99 -48
- package/dist/src/providers/openai/OpenAIProvider.js.map +1 -1
- package/dist/src/services/gitService.d.ts +3 -1
- package/dist/src/services/gitService.js +20 -10
- package/dist/src/services/gitService.js.map +1 -1
- package/dist/src/services/history/ContentConverters.js +40 -1
- package/dist/src/services/history/ContentConverters.js.map +1 -1
- package/dist/src/services/loopDetectionService.js +8 -2
- package/dist/src/services/loopDetectionService.js.map +1 -1
- package/dist/src/services/shellExecutionService.js +2 -1
- package/dist/src/services/shellExecutionService.js.map +1 -1
- package/dist/src/settings/SettingsService.d.ts +5 -0
- package/dist/src/settings/SettingsService.js +5 -0
- package/dist/src/settings/SettingsService.js.map +1 -1
- package/dist/src/settings/settingsServiceInstance.d.ts +5 -0
- package/dist/src/settings/settingsServiceInstance.js +5 -0
- package/dist/src/settings/settingsServiceInstance.js.map +1 -1
- package/dist/src/settings/types.d.ts +5 -0
- package/dist/src/settings/types.js +3 -1
- package/dist/src/settings/types.js.map +1 -1
- package/dist/src/tools/IToolFormatter.d.ts +5 -0
- package/dist/src/tools/IToolFormatter.js +3 -13
- package/dist/src/tools/IToolFormatter.js.map +1 -1
- package/dist/src/tools/ToolFormatter.d.ts +18 -0
- package/dist/src/tools/ToolFormatter.js +41 -13
- package/dist/src/tools/ToolFormatter.js.map +1 -1
- package/dist/src/tools/doubleEscapeUtils.d.ts +3 -13
- package/dist/src/tools/doubleEscapeUtils.js +5 -0
- package/dist/src/tools/doubleEscapeUtils.js.map +1 -1
- package/dist/src/tools/edit.js +10 -1
- package/dist/src/tools/edit.js.map +1 -1
- package/dist/src/tools/glob.d.ts +0 -4
- package/dist/src/tools/glob.js +18 -42
- package/dist/src/tools/glob.js.map +1 -1
- package/dist/src/tools/grep.d.ts +0 -4
- package/dist/src/tools/grep.js +41 -77
- package/dist/src/tools/grep.js.map +1 -1
- package/dist/src/tools/ls.d.ts +2 -6
- package/dist/src/tools/ls.js +18 -13
- package/dist/src/tools/ls.js.map +1 -1
- package/dist/src/tools/mcp-client.d.ts +7 -0
- package/dist/src/tools/mcp-client.js +26 -21
- package/dist/src/tools/mcp-client.js.map +1 -1
- package/dist/src/tools/mcp-tool.js +12 -2
- package/dist/src/tools/mcp-tool.js.map +1 -1
- package/dist/src/tools/memoryTool.js +7 -2
- package/dist/src/tools/memoryTool.js.map +1 -1
- package/dist/src/tools/read-file.js +2 -30
- package/dist/src/tools/read-file.js.map +1 -1
- package/dist/src/tools/read-many-files.js +29 -52
- package/dist/src/tools/read-many-files.js.map +1 -1
- package/dist/src/tools/ripGrep.d.ts +46 -0
- package/dist/src/tools/ripGrep.js +368 -0
- package/dist/src/tools/ripGrep.js.map +1 -0
- package/dist/src/tools/shell.js +13 -2
- package/dist/src/tools/shell.js.map +1 -1
- package/dist/src/tools/todo-pause.js +0 -1
- package/dist/src/tools/todo-pause.js.map +1 -1
- package/dist/src/tools/tool-error.d.ts +18 -2
- package/dist/src/tools/tool-error.js +27 -1
- package/dist/src/tools/tool-error.js.map +1 -1
- package/dist/src/tools/tool-registry.d.ts +13 -2
- package/dist/src/tools/tool-registry.js +30 -2
- package/dist/src/tools/tool-registry.js.map +1 -1
- package/dist/src/tools/tools.d.ts +7 -5
- package/dist/src/tools/tools.js +12 -0
- package/dist/src/tools/tools.js.map +1 -1
- package/dist/src/tools/web-fetch.js +101 -76
- package/dist/src/tools/web-fetch.js.map +1 -1
- package/dist/src/tools/web-search-invocation.js +13 -1
- package/dist/src/tools/web-search-invocation.js.map +1 -1
- package/dist/src/utils/deterministicEditCorrector.d.ts +28 -0
- package/dist/src/utils/deterministicEditCorrector.js +295 -0
- package/dist/src/utils/deterministicEditCorrector.js.map +1 -0
- package/dist/src/utils/editCorrector.d.ts +4 -4
- package/dist/src/utils/editCorrector.js +15 -213
- package/dist/src/utils/editCorrector.js.map +1 -1
- package/dist/src/utils/editor.js +1 -1
- package/dist/src/utils/editor.js.map +1 -1
- package/dist/src/utils/errors.d.ts +19 -0
- package/dist/src/utils/errors.js +32 -0
- package/dist/src/utils/errors.js.map +1 -1
- package/dist/src/utils/fileUtils.d.ts +2 -7
- package/dist/src/utils/fileUtils.js +16 -47
- package/dist/src/utils/fileUtils.js.map +1 -1
- package/dist/src/utils/filesearch/fileSearch.d.ts +1 -0
- package/dist/src/utils/filesearch/fileSearch.js +14 -9
- package/dist/src/utils/filesearch/fileSearch.js.map +1 -1
- package/dist/src/utils/ignorePatterns.d.ts +103 -0
- package/dist/src/utils/ignorePatterns.js +220 -0
- package/dist/src/utils/ignorePatterns.js.map +1 -0
- package/dist/src/utils/installationManager.d.ts +16 -0
- package/dist/src/utils/installationManager.js +50 -0
- package/dist/src/utils/installationManager.js.map +1 -0
- package/dist/src/utils/memoryDiscovery.d.ts +1 -1
- package/dist/src/utils/memoryDiscovery.js +64 -43
- package/dist/src/utils/memoryDiscovery.js.map +1 -1
- package/dist/src/utils/paths.d.ts +0 -17
- package/dist/src/utils/paths.js +0 -26
- package/dist/src/utils/paths.js.map +1 -1
- package/dist/src/utils/schemaValidator.js +4 -0
- package/dist/src/utils/schemaValidator.js.map +1 -1
- package/dist/src/utils/shell-utils.d.ts +1 -1
- package/dist/src/utils/shell-utils.js +23 -29
- package/dist/src/utils/shell-utils.js.map +1 -1
- package/dist/src/utils/tool-utils.d.ts +19 -0
- package/dist/src/utils/tool-utils.js +58 -0
- package/dist/src/utils/tool-utils.js.map +1 -0
- package/dist/src/utils/userAccountManager.d.ts +20 -0
- package/dist/src/utils/userAccountManager.js +122 -0
- package/dist/src/utils/userAccountManager.js.map +1 -0
- package/dist/src/utils/workspaceContext.js +11 -5
- package/dist/src/utils/workspaceContext.js.map +1 -1
- package/package.json +7 -3
- package/dist/src/utils/nextSpeakerChecker.d.ts +0 -12
- package/dist/src/utils/nextSpeakerChecker.js +0 -97
- package/dist/src/utils/nextSpeakerChecker.js.map +0 -1
- package/dist/src/utils/user_account.d.ts +0 -9
- package/dist/src/utils/user_account.js +0 -109
- package/dist/src/utils/user_account.js.map +0 -1
- package/dist/src/utils/user_id.d.ts +0 -11
- package/dist/src/utils/user_id.js +0 -49
- package/dist/src/utils/user_id.js.map +0 -1
@@ -4,10 +4,11 @@
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
5
5
|
*/
|
6
6
|
import { ToolConfirmationOutcome, ApprovalMode, logToolCall, ToolCallEvent, ToolErrorType, } from '../index.js';
|
7
|
-
import { ToolCallTrackerService } from '../services/tool-call-tracker-service.js';
|
8
7
|
import { getResponseTextFromParts } from '../utils/generateContentResponseUtilities.js';
|
9
8
|
import { isModifiableDeclarativeTool, modifyWithEditor, } from '../tools/modifiable-tool.js';
|
10
9
|
import * as Diff from 'diff';
|
10
|
+
import levenshtein from 'fast-levenshtein';
|
11
|
+
import { doesToolInvocationMatch } from '../utils/tool-utils.js';
|
11
12
|
/**
|
12
13
|
* Formats tool output for a Gemini FunctionResponse.
|
13
14
|
*/
|
@@ -25,62 +26,56 @@ export function convertToFunctionResponse(toolName, callId, llmContent) {
|
|
25
26
|
? llmContent[0]
|
26
27
|
: llmContent;
|
27
28
|
if (typeof contentToProcess === 'string') {
|
28
|
-
return createFunctionResponsePart(callId, toolName, contentToProcess);
|
29
|
+
return [createFunctionResponsePart(callId, toolName, contentToProcess)];
|
29
30
|
}
|
30
31
|
if (Array.isArray(contentToProcess)) {
|
31
|
-
//
|
32
|
-
const stringElements = contentToProcess.filter((item) => typeof item === 'string');
|
33
|
-
if (stringElements.length === contentToProcess.length) {
|
34
|
-
// All elements are strings, join them
|
35
|
-
return createFunctionResponsePart(callId, toolName, stringElements.join('\n'));
|
36
|
-
}
|
37
|
-
// If the array contains Part objects, check if any are already function responses
|
32
|
+
// Check if any part already has a function response to avoid duplicates
|
38
33
|
const hasFunctionResponse = contentToProcess.some((part) => typeof part === 'object' && part.functionResponse);
|
39
34
|
if (hasFunctionResponse) {
|
40
|
-
// Already has function response(s), return as-is
|
41
|
-
return contentToProcess;
|
35
|
+
// Already has function response(s), return as-is without creating duplicates
|
36
|
+
return toParts(contentToProcess);
|
42
37
|
}
|
43
|
-
//
|
44
|
-
|
45
|
-
|
38
|
+
// No existing function response, create one
|
39
|
+
const functionResponse = createFunctionResponsePart(callId, toolName, 'Tool execution succeeded.');
|
40
|
+
return [functionResponse, ...toParts(contentToProcess)];
|
46
41
|
}
|
47
42
|
// After this point, contentToProcess is a single Part object.
|
48
43
|
if (contentToProcess.functionResponse) {
|
49
|
-
if (contentToProcess.functionResponse.response?.content) {
|
50
|
-
const stringifiedOutput = getResponseTextFromParts(contentToProcess.functionResponse.response
|
51
|
-
return createFunctionResponsePart(callId, toolName, stringifiedOutput);
|
44
|
+
if (contentToProcess.functionResponse.response?.['content']) {
|
45
|
+
const stringifiedOutput = getResponseTextFromParts(contentToProcess.functionResponse.response['content']) || '';
|
46
|
+
return [createFunctionResponsePart(callId, toolName, stringifiedOutput)];
|
52
47
|
}
|
53
|
-
// It's a functionResponse
|
54
|
-
return
|
55
|
-
functionResponse: {
|
56
|
-
...contentToProcess.functionResponse,
|
57
|
-
id: callId,
|
58
|
-
name: toolName,
|
59
|
-
},
|
60
|
-
};
|
48
|
+
// It's a functionResponse that we should pass through as is.
|
49
|
+
return [contentToProcess];
|
61
50
|
}
|
62
51
|
if (contentToProcess.inlineData || contentToProcess.fileData) {
|
63
52
|
const mimeType = contentToProcess.inlineData?.mimeType ||
|
64
53
|
contentToProcess.fileData?.mimeType ||
|
65
54
|
'unknown';
|
66
|
-
|
67
|
-
return
|
68
|
-
functionResponse: {
|
69
|
-
id: callId,
|
70
|
-
name: toolName,
|
71
|
-
response: {
|
72
|
-
output: `Binary content of type ${mimeType} was processed.`,
|
73
|
-
// Include the binary content in a special field
|
74
|
-
binaryContent: contentToProcess,
|
75
|
-
},
|
76
|
-
},
|
77
|
-
};
|
55
|
+
const functionResponse = createFunctionResponsePart(callId, toolName, `Binary content of type ${mimeType} was processed.`);
|
56
|
+
return [functionResponse, contentToProcess];
|
78
57
|
}
|
79
58
|
if (contentToProcess.text !== undefined) {
|
80
|
-
return
|
59
|
+
return [
|
60
|
+
createFunctionResponsePart(callId, toolName, contentToProcess.text),
|
61
|
+
];
|
81
62
|
}
|
82
63
|
// Default case for other kinds of parts.
|
83
|
-
return
|
64
|
+
return [
|
65
|
+
createFunctionResponsePart(callId, toolName, 'Tool execution succeeded.'),
|
66
|
+
];
|
67
|
+
}
|
68
|
+
function toParts(input) {
|
69
|
+
const parts = [];
|
70
|
+
for (const part of Array.isArray(input) ? input : [input]) {
|
71
|
+
if (typeof part === 'string') {
|
72
|
+
parts.push({ text: part });
|
73
|
+
}
|
74
|
+
else if (part) {
|
75
|
+
parts.push(part);
|
76
|
+
}
|
77
|
+
}
|
78
|
+
return parts;
|
84
79
|
}
|
85
80
|
const createErrorResponse = (request, error, errorType) => ({
|
86
81
|
callId: request.callId,
|
@@ -114,12 +109,13 @@ export class CoreToolScheduler {
|
|
114
109
|
onToolCallsUpdate;
|
115
110
|
getPreferredEditor;
|
116
111
|
config;
|
117
|
-
pendingQueue = [];
|
118
|
-
isProcessingBatch = false;
|
119
112
|
onEditorClose;
|
113
|
+
isFinalizingToolCalls = false;
|
114
|
+
isScheduling = false;
|
115
|
+
requestQueue = [];
|
120
116
|
constructor(options) {
|
121
117
|
this.config = options.config;
|
122
|
-
this.toolRegistry = options.
|
118
|
+
this.toolRegistry = options.config.getToolRegistry();
|
123
119
|
this.outputUpdateHandler = options.outputUpdateHandler;
|
124
120
|
this.onAllToolCallsComplete = options.onAllToolCallsComplete;
|
125
121
|
this.onToolCallsUpdate = options.onToolCallsUpdate;
|
@@ -138,7 +134,6 @@ export class CoreToolScheduler {
|
|
138
134
|
const existingStartTime = currentCall.startTime;
|
139
135
|
const toolInstance = currentCall.tool;
|
140
136
|
const invocation = currentCall.invocation;
|
141
|
-
const existingSignal = currentCall.signal;
|
142
137
|
const outcome = currentCall.outcome;
|
143
138
|
switch (newStatus) {
|
144
139
|
case 'success': {
|
@@ -186,7 +181,6 @@ export class CoreToolScheduler {
|
|
186
181
|
startTime: existingStartTime,
|
187
182
|
outcome,
|
188
183
|
invocation,
|
189
|
-
signal: existingSignal,
|
190
184
|
};
|
191
185
|
case 'cancelled': {
|
192
186
|
const durationMs = existingStartTime
|
@@ -265,7 +259,7 @@ export class CoreToolScheduler {
|
|
265
259
|
}
|
266
260
|
});
|
267
261
|
this.notifyToolCallsUpdate();
|
268
|
-
|
262
|
+
this.checkAndNotifyCompletion();
|
269
263
|
}
|
270
264
|
setArgsInternal(targetCallId, args) {
|
271
265
|
this.toolCalls = this.toolCalls.map((call) => {
|
@@ -291,6 +285,10 @@ export class CoreToolScheduler {
|
|
291
285
|
};
|
292
286
|
});
|
293
287
|
}
|
288
|
+
isRunning() {
|
289
|
+
return (this.isFinalizingToolCalls ||
|
290
|
+
this.toolCalls.some((call) => call.status === 'executing' || call.status === 'awaiting_approval'));
|
291
|
+
}
|
294
292
|
buildInvocation(tool, args) {
|
295
293
|
try {
|
296
294
|
return tool.build(args);
|
@@ -302,77 +300,124 @@ export class CoreToolScheduler {
|
|
302
300
|
return new Error(String(e));
|
303
301
|
}
|
304
302
|
}
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
303
|
+
/**
|
304
|
+
* Generates a suggestion string for a tool name that was not found in the registry.
|
305
|
+
* It finds the closest matches based on Levenshtein distance.
|
306
|
+
* @param unknownToolName The tool name that was not found.
|
307
|
+
* @param topN The number of suggestions to return. Defaults to 3.
|
308
|
+
* @returns A suggestion string like " Did you mean 'tool'?" or " Did you mean one of: 'tool1', 'tool2'?", or an empty string if no suggestions are found.
|
309
|
+
*/
|
310
|
+
getToolSuggestion(unknownToolName, topN = 3) {
|
311
|
+
const allToolNames = this.toolRegistry.getAllToolNames();
|
312
|
+
const matches = allToolNames.map((toolName) => ({
|
313
|
+
name: toolName,
|
314
|
+
distance: levenshtein.get(unknownToolName, toolName),
|
315
|
+
}));
|
316
|
+
matches.sort((a, b) => a.distance - b.distance);
|
317
|
+
const topNResults = matches.slice(0, topN);
|
318
|
+
if (topNResults.length === 0) {
|
319
|
+
return '';
|
314
320
|
}
|
315
|
-
const
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
request
|
330
|
-
|
331
|
-
|
321
|
+
const suggestedNames = topNResults
|
322
|
+
.map((match) => `"${match.name}"`)
|
323
|
+
.join(', ');
|
324
|
+
if (topNResults.length > 1) {
|
325
|
+
return ` Did you mean one of: ${suggestedNames}?`;
|
326
|
+
}
|
327
|
+
else {
|
328
|
+
return ` Did you mean ${suggestedNames}?`;
|
329
|
+
}
|
330
|
+
}
|
331
|
+
schedule(request, signal) {
|
332
|
+
if (this.isRunning() || this.isScheduling) {
|
333
|
+
return new Promise((resolve, reject) => {
|
334
|
+
const abortHandler = () => {
|
335
|
+
// Find and remove the request from the queue
|
336
|
+
const index = this.requestQueue.findIndex((item) => item.request === request);
|
337
|
+
if (index > -1) {
|
338
|
+
this.requestQueue.splice(index, 1);
|
339
|
+
reject(new Error('Tool call cancelled while in queue.'));
|
340
|
+
}
|
332
341
|
};
|
342
|
+
signal.addEventListener('abort', abortHandler, { once: true });
|
343
|
+
this.requestQueue.push({
|
344
|
+
request,
|
345
|
+
signal,
|
346
|
+
resolve: () => {
|
347
|
+
signal.removeEventListener('abort', abortHandler);
|
348
|
+
resolve();
|
349
|
+
},
|
350
|
+
reject: (reason) => {
|
351
|
+
signal.removeEventListener('abort', abortHandler);
|
352
|
+
reject(reason);
|
353
|
+
},
|
354
|
+
});
|
355
|
+
});
|
356
|
+
}
|
357
|
+
return this._schedule(request, signal);
|
358
|
+
}
|
359
|
+
async _schedule(request, signal) {
|
360
|
+
this.isScheduling = true;
|
361
|
+
try {
|
362
|
+
if (this.isRunning()) {
|
363
|
+
throw new Error('Cannot schedule new tool calls while other tool calls are actively running (executing or awaiting approval).');
|
333
364
|
}
|
334
|
-
const
|
335
|
-
|
365
|
+
const requestsToProcess = Array.isArray(request) ? request : [request];
|
366
|
+
const newToolCalls = requestsToProcess.map((reqInfo) => {
|
367
|
+
const toolInstance = this.toolRegistry.getTool(reqInfo.name);
|
368
|
+
if (!toolInstance) {
|
369
|
+
const suggestion = this.getToolSuggestion(reqInfo.name);
|
370
|
+
const errorMessage = `Tool "${reqInfo.name}" not found in registry. Tools must use the exact names that are registered.${suggestion}`;
|
371
|
+
return {
|
372
|
+
status: 'error',
|
373
|
+
request: reqInfo,
|
374
|
+
response: createErrorResponse(reqInfo, new Error(errorMessage), ToolErrorType.TOOL_NOT_REGISTERED),
|
375
|
+
durationMs: 0,
|
376
|
+
};
|
377
|
+
}
|
378
|
+
const invocationOrError = this.buildInvocation(toolInstance, reqInfo.args);
|
379
|
+
if (invocationOrError instanceof Error) {
|
380
|
+
return {
|
381
|
+
status: 'error',
|
382
|
+
request: reqInfo,
|
383
|
+
tool: toolInstance,
|
384
|
+
response: createErrorResponse(reqInfo, invocationOrError, ToolErrorType.INVALID_TOOL_PARAMS),
|
385
|
+
durationMs: 0,
|
386
|
+
};
|
387
|
+
}
|
336
388
|
return {
|
337
|
-
status: '
|
389
|
+
status: 'validating',
|
338
390
|
request: reqInfo,
|
339
391
|
tool: toolInstance,
|
340
|
-
|
341
|
-
|
392
|
+
invocation: invocationOrError,
|
393
|
+
startTime: Date.now(),
|
342
394
|
};
|
343
|
-
}
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
invocation: invocationOrError,
|
349
|
-
startTime: Date.now(),
|
350
|
-
signal,
|
351
|
-
};
|
352
|
-
});
|
353
|
-
this.toolCalls = this.toolCalls.concat(newToolCalls);
|
354
|
-
this.notifyToolCallsUpdate();
|
355
|
-
for (const toolCall of newToolCalls) {
|
356
|
-
if (toolCall.status !== 'validating') {
|
357
|
-
continue;
|
358
|
-
}
|
359
|
-
const { request: reqInfo, invocation } = toolCall;
|
360
|
-
try {
|
361
|
-
if (signal.aborted) {
|
362
|
-
this.setStatusInternal(reqInfo.callId, 'cancelled', 'Tool call cancelled by user.');
|
395
|
+
});
|
396
|
+
this.toolCalls = this.toolCalls.concat(newToolCalls);
|
397
|
+
this.notifyToolCallsUpdate();
|
398
|
+
for (const toolCall of newToolCalls) {
|
399
|
+
if (toolCall.status !== 'validating') {
|
363
400
|
continue;
|
364
401
|
}
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
402
|
+
const { request: reqInfo, invocation } = toolCall;
|
403
|
+
try {
|
404
|
+
if (signal.aborted) {
|
405
|
+
this.setStatusInternal(reqInfo.callId, 'cancelled', 'Tool call cancelled by user.');
|
406
|
+
continue;
|
407
|
+
}
|
370
408
|
const confirmationDetails = await invocation.shouldConfirmExecute(signal);
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
409
|
+
if (!confirmationDetails) {
|
410
|
+
this.setToolCallOutcome(reqInfo.callId, ToolConfirmationOutcome.ProceedAlways);
|
411
|
+
this.setStatusInternal(reqInfo.callId, 'scheduled');
|
412
|
+
continue;
|
413
|
+
}
|
414
|
+
const allowedTools = this.config.getAllowedTools() || [];
|
415
|
+
if (this.config.getApprovalMode() === ApprovalMode.YOLO ||
|
416
|
+
doesToolInvocationMatch(toolCall.tool, invocation, allowedTools)) {
|
417
|
+
this.setToolCallOutcome(reqInfo.callId, ToolConfirmationOutcome.ProceedAlways);
|
418
|
+
this.setStatusInternal(reqInfo.callId, 'scheduled');
|
419
|
+
}
|
420
|
+
else {
|
376
421
|
// Allow IDE to resolve confirmation
|
377
422
|
if (confirmationDetails.type === 'edit' &&
|
378
423
|
confirmationDetails.ideConfirmation) {
|
@@ -392,26 +437,17 @@ export class CoreToolScheduler {
|
|
392
437
|
};
|
393
438
|
this.setStatusInternal(reqInfo.callId, 'awaiting_approval', wrappedConfirmationDetails);
|
394
439
|
}
|
395
|
-
else {
|
396
|
-
// Either no confirmation needed or command is always allowed
|
397
|
-
this.setToolCallOutcome(reqInfo.callId, ToolConfirmationOutcome.ProceedAlways);
|
398
|
-
this.setStatusInternal(reqInfo.callId, 'scheduled');
|
399
|
-
}
|
400
440
|
}
|
401
|
-
|
402
|
-
catch (error) {
|
403
|
-
// Check if the error is due to an aborted signal
|
404
|
-
if (signal.aborted ||
|
405
|
-
(error instanceof Error && error.name === 'AbortError')) {
|
406
|
-
this.setStatusInternal(reqInfo.callId, 'cancelled', 'Tool call was cancelled');
|
407
|
-
}
|
408
|
-
else {
|
441
|
+
catch (error) {
|
409
442
|
this.setStatusInternal(reqInfo.callId, 'error', createErrorResponse(reqInfo, error instanceof Error ? error : new Error(String(error)), ToolErrorType.UNHANDLED_EXCEPTION));
|
410
443
|
}
|
411
444
|
}
|
445
|
+
this.attemptExecutionOfScheduledCalls(signal);
|
446
|
+
void this.checkAndNotifyCompletion();
|
447
|
+
}
|
448
|
+
finally {
|
449
|
+
this.isScheduling = false;
|
412
450
|
}
|
413
|
-
this.attemptExecutionOfScheduledCalls(signal);
|
414
|
-
void this.checkAndNotifyCompletion();
|
415
451
|
}
|
416
452
|
async handleConfirmationResponse(callId, originalOnConfirm, outcome, signal, payload) {
|
417
453
|
const toolCall = this.toolCalls.find((c) => c.request.callId === callId && c.status === 'awaiting_approval');
|
@@ -422,13 +458,6 @@ export class CoreToolScheduler {
|
|
422
458
|
await this.autoApproveCompatiblePendingTools(signal, callId);
|
423
459
|
}
|
424
460
|
this.setToolCallOutcome(callId, outcome);
|
425
|
-
// Store the command if user selected "always allow"
|
426
|
-
if (outcome === ToolConfirmationOutcome.ProceedAlways &&
|
427
|
-
toolCall &&
|
428
|
-
toolCall.status === 'awaiting_approval' &&
|
429
|
-
toolCall.confirmationDetails.type === 'exec') {
|
430
|
-
this.config.addAlwaysAllowedCommand(toolCall.confirmationDetails.rootCommand);
|
431
|
-
}
|
432
461
|
if (outcome === ToolConfirmationOutcome.Cancel || signal.aborted) {
|
433
462
|
this.setStatusInternal(callId, 'cancelled', 'User did not allow tool call');
|
434
463
|
}
|
@@ -484,98 +513,78 @@ export class CoreToolScheduler {
|
|
484
513
|
});
|
485
514
|
}
|
486
515
|
attemptExecutionOfScheduledCalls(signal) {
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
516
|
+
const allCallsFinalOrScheduled = this.toolCalls.every((call) => call.status === 'scheduled' ||
|
517
|
+
call.status === 'cancelled' ||
|
518
|
+
call.status === 'success' ||
|
519
|
+
call.status === 'error');
|
520
|
+
if (allCallsFinalOrScheduled) {
|
521
|
+
const callsToExecute = this.toolCalls.filter((call) => call.status === 'scheduled');
|
522
|
+
callsToExecute.forEach((toolCall) => {
|
523
|
+
if (toolCall.status !== 'scheduled')
|
524
|
+
return;
|
525
|
+
const scheduledCall = toolCall;
|
526
|
+
const { callId, name: toolName } = scheduledCall.request;
|
527
|
+
const invocation = scheduledCall.invocation;
|
528
|
+
this.setStatusInternal(callId, 'executing');
|
529
|
+
const liveOutputCallback = scheduledCall.tool.canUpdateOutput && this.outputUpdateHandler
|
530
|
+
? (outputChunk) => {
|
531
|
+
if (this.outputUpdateHandler) {
|
532
|
+
this.outputUpdateHandler(callId, outputChunk);
|
533
|
+
}
|
534
|
+
this.toolCalls = this.toolCalls.map((tc) => tc.request.callId === callId && tc.status === 'executing'
|
535
|
+
? { ...tc, liveOutput: outputChunk }
|
536
|
+
: tc);
|
537
|
+
this.notifyToolCallsUpdate();
|
538
|
+
}
|
539
|
+
: undefined;
|
540
|
+
invocation
|
541
|
+
.execute(signal, liveOutputCallback)
|
542
|
+
.then(async (toolResult) => {
|
543
|
+
if (signal.aborted) {
|
544
|
+
this.setStatusInternal(callId, 'cancelled', 'User cancelled tool execution.');
|
545
|
+
return;
|
546
|
+
}
|
547
|
+
if (toolResult.error === undefined) {
|
548
|
+
const response = convertToFunctionResponse(toolName, callId, toolResult.llmContent);
|
549
|
+
// Return BOTH the tool call and response as an array
|
550
|
+
// This ensures they're always paired and added to history atomically
|
551
|
+
const responseParts = [
|
552
|
+
// First, the tool call
|
553
|
+
{
|
554
|
+
functionCall: {
|
555
|
+
id: callId,
|
556
|
+
name: toolName,
|
557
|
+
args: scheduledCall.request.args,
|
558
|
+
},
|
559
|
+
},
|
560
|
+
// Then, spread the response(s) since convertToFunctionResponse returns Part[]
|
561
|
+
...response,
|
562
|
+
];
|
563
|
+
const successResponse = {
|
564
|
+
callId,
|
565
|
+
responseParts,
|
566
|
+
resultDisplay: toolResult.returnDisplay,
|
567
|
+
error: undefined,
|
568
|
+
errorType: undefined,
|
569
|
+
};
|
570
|
+
this.setStatusInternal(callId, 'success', successResponse);
|
571
|
+
}
|
572
|
+
else {
|
573
|
+
// It is a failure
|
574
|
+
const error = new Error(toolResult.error.message);
|
575
|
+
const errorResponse = createErrorResponse(scheduledCall.request, error, toolResult.error.type);
|
576
|
+
this.setStatusInternal(callId, 'error', errorResponse);
|
577
|
+
}
|
578
|
+
})
|
579
|
+
.catch((executionError) => {
|
580
|
+
this.setStatusInternal(callId, 'error', createErrorResponse(scheduledCall.request, executionError instanceof Error
|
581
|
+
? executionError
|
582
|
+
: new Error(String(executionError)), ToolErrorType.UNHANDLED_EXCEPTION));
|
583
|
+
});
|
584
|
+
});
|
491
585
|
}
|
492
|
-
callsToExecute.forEach((toolCall) => {
|
493
|
-
const toolSignal = toolCall.signal ||
|
494
|
-
signal ||
|
495
|
-
new AbortController().signal;
|
496
|
-
this.executeToolCall(toolCall, toolSignal);
|
497
|
-
});
|
498
|
-
}
|
499
|
-
executeToolCall(toolCall, signal) {
|
500
|
-
if (toolCall.status !== 'scheduled')
|
501
|
-
return;
|
502
|
-
const scheduledCall = toolCall;
|
503
|
-
const { callId, name: toolName } = scheduledCall.request;
|
504
|
-
const invocation = scheduledCall.invocation;
|
505
|
-
this.setStatusInternal(callId, 'executing');
|
506
|
-
// Start tracking the tool call execution
|
507
|
-
const sessionId = typeof this.config.getSessionId === 'function'
|
508
|
-
? this.config.getSessionId()
|
509
|
-
: 'default-session';
|
510
|
-
const toolCallId = ToolCallTrackerService.startTrackingToolCall(sessionId, toolName, scheduledCall.request.args);
|
511
|
-
const liveOutputCallback = scheduledCall.tool.canUpdateOutput && this.outputUpdateHandler
|
512
|
-
? (outputChunk) => {
|
513
|
-
if (this.outputUpdateHandler) {
|
514
|
-
this.outputUpdateHandler(callId, outputChunk);
|
515
|
-
}
|
516
|
-
this.toolCalls = this.toolCalls.map((tc) => tc.request.callId === callId && tc.status === 'executing'
|
517
|
-
? { ...tc, liveOutput: outputChunk }
|
518
|
-
: tc);
|
519
|
-
this.notifyToolCallsUpdate();
|
520
|
-
}
|
521
|
-
: undefined;
|
522
|
-
invocation
|
523
|
-
.execute(signal, liveOutputCallback)
|
524
|
-
.then(async (toolResult) => {
|
525
|
-
if (signal.aborted) {
|
526
|
-
// Mark tool call as failed if aborted
|
527
|
-
if (toolCallId) {
|
528
|
-
ToolCallTrackerService.failToolCallTracking(sessionId, toolCallId);
|
529
|
-
}
|
530
|
-
this.setStatusInternal(callId, 'cancelled', 'User cancelled tool execution.');
|
531
|
-
return;
|
532
|
-
}
|
533
|
-
// Mark tool call as completed
|
534
|
-
if (toolCallId) {
|
535
|
-
await ToolCallTrackerService.completeToolCallTracking(sessionId, toolCallId);
|
536
|
-
}
|
537
|
-
if (toolResult.error === undefined) {
|
538
|
-
const functionResponse = convertToFunctionResponse(toolName, callId, toolResult.llmContent);
|
539
|
-
// Return BOTH the tool call and response as an array
|
540
|
-
const responseParts = [
|
541
|
-
// First, the tool call
|
542
|
-
{
|
543
|
-
functionCall: {
|
544
|
-
id: callId,
|
545
|
-
name: toolName,
|
546
|
-
args: scheduledCall.request.args,
|
547
|
-
},
|
548
|
-
},
|
549
|
-
// Then, the response
|
550
|
-
functionResponse,
|
551
|
-
];
|
552
|
-
const successResponse = {
|
553
|
-
callId,
|
554
|
-
responseParts: responseParts,
|
555
|
-
resultDisplay: toolResult.returnDisplay,
|
556
|
-
error: undefined,
|
557
|
-
errorType: undefined,
|
558
|
-
};
|
559
|
-
this.setStatusInternal(callId, 'success', successResponse);
|
560
|
-
}
|
561
|
-
else {
|
562
|
-
// It is a failure
|
563
|
-
const error = new Error(toolResult.error.message);
|
564
|
-
const errorResponse = createErrorResponse(scheduledCall.request, error, toolResult.error.type);
|
565
|
-
this.setStatusInternal(callId, 'error', errorResponse);
|
566
|
-
}
|
567
|
-
})
|
568
|
-
.catch((executionError) => {
|
569
|
-
// Mark tool call as failed on error
|
570
|
-
if (toolCallId) {
|
571
|
-
ToolCallTrackerService.failToolCallTracking(sessionId, toolCallId);
|
572
|
-
}
|
573
|
-
this.setStatusInternal(callId, 'error', createErrorResponse(scheduledCall.request, executionError instanceof Error
|
574
|
-
? executionError
|
575
|
-
: new Error(String(executionError)), ToolErrorType.UNHANDLED_EXCEPTION));
|
576
|
-
});
|
577
586
|
}
|
578
|
-
async checkAndNotifyCompletion(
|
587
|
+
async checkAndNotifyCompletion() {
|
579
588
|
const allCallsAreTerminal = this.toolCalls.every((call) => call.status === 'success' ||
|
580
589
|
call.status === 'error' ||
|
581
590
|
call.status === 'cancelled');
|
@@ -586,33 +595,18 @@ export class CoreToolScheduler {
|
|
586
595
|
logToolCall(this.config, new ToolCallEvent(call));
|
587
596
|
}
|
588
597
|
if (this.onAllToolCallsComplete) {
|
598
|
+
this.isFinalizingToolCalls = true;
|
589
599
|
await this.onAllToolCallsComplete(completedCalls);
|
600
|
+
this.isFinalizingToolCalls = false;
|
590
601
|
}
|
591
602
|
this.notifyToolCallsUpdate();
|
592
|
-
//
|
593
|
-
this.
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
if (this.pendingQueue.length === 0 || this.isProcessingBatch) {
|
599
|
-
return;
|
600
|
-
}
|
601
|
-
// Process all queued requests as the next batch
|
602
|
-
const queuedRequests = [...this.pendingQueue];
|
603
|
-
this.pendingQueue = [];
|
604
|
-
// Collect all requests and signals
|
605
|
-
const allRequests = [];
|
606
|
-
let commonSignal;
|
607
|
-
queuedRequests.forEach((item) => {
|
608
|
-
allRequests.push(item.request);
|
609
|
-
if (!commonSignal && item.signal) {
|
610
|
-
commonSignal = item.signal;
|
603
|
+
// After completion, process the next item in the queue.
|
604
|
+
if (this.requestQueue.length > 0) {
|
605
|
+
const next = this.requestQueue.shift();
|
606
|
+
this._schedule(next.request, next.signal)
|
607
|
+
.then(next.resolve)
|
608
|
+
.catch(next.reject);
|
611
609
|
}
|
612
|
-
});
|
613
|
-
if (allRequests.length > 0) {
|
614
|
-
// Schedule the entire batch at once
|
615
|
-
await this.schedule(allRequests, commonSignal || new AbortController().signal);
|
616
610
|
}
|
617
611
|
}
|
618
612
|
notifyToolCallsUpdate() {
|