@xagent-ai/cli 1.1.1 → 1.2.1
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/README.md +1 -1
- package/README_CN.md +1 -1
- package/dist/agents.js +164 -164
- package/dist/agents.js.map +1 -1
- package/dist/ai-client.d.ts +3 -0
- package/dist/ai-client.d.ts.map +1 -1
- package/dist/ai-client.js +115 -25
- package/dist/ai-client.js.map +1 -1
- package/dist/auth.js +4 -4
- package/dist/auth.js.map +1 -1
- package/dist/cli.js +184 -1
- package/dist/cli.js.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/memory.d.ts +5 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +77 -37
- package/dist/memory.js.map +1 -1
- package/dist/remote-ai-client.d.ts +1 -8
- package/dist/remote-ai-client.d.ts.map +1 -1
- package/dist/remote-ai-client.js +55 -61
- package/dist/remote-ai-client.js.map +1 -1
- package/dist/retry.d.ts +35 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +166 -0
- package/dist/retry.js.map +1 -0
- package/dist/session.d.ts +0 -5
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +186 -164
- package/dist/session.js.map +1 -1
- package/dist/slash-commands.d.ts +1 -0
- package/dist/slash-commands.d.ts.map +1 -1
- package/dist/slash-commands.js +91 -9
- package/dist/slash-commands.js.map +1 -1
- package/dist/smart-approval.d.ts.map +1 -1
- package/dist/smart-approval.js +5 -4
- package/dist/smart-approval.js.map +1 -1
- package/dist/system-prompt-generator.d.ts.map +1 -1
- package/dist/system-prompt-generator.js +149 -139
- package/dist/system-prompt-generator.js.map +1 -1
- package/dist/theme.d.ts +48 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +254 -0
- package/dist/theme.js.map +1 -1
- package/dist/tools/edit-diff.d.ts +32 -0
- package/dist/tools/edit-diff.d.ts.map +1 -0
- package/dist/tools/edit-diff.js +185 -0
- package/dist/tools/edit-diff.js.map +1 -0
- package/dist/tools/edit.d.ts +11 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +129 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools.d.ts +19 -5
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +979 -631
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/agents.ts +504 -504
- package/src/ai-client.ts +133 -31
- package/src/auth.ts +4 -4
- package/src/cli.ts +195 -1
- package/src/config.ts +3 -3
- package/src/memory.ts +83 -42
- package/src/remote-ai-client.ts +69 -76
- package/src/retry.ts +217 -0
- package/src/session.ts +1733 -1844
- package/src/slash-commands.ts +98 -9
- package/src/smart-approval.ts +626 -625
- package/src/system-prompt-generator.ts +853 -843
- package/src/theme.ts +284 -0
- package/src/tools.ts +3889 -3483
- package/src/types.ts +1 -0
package/dist/session.js
CHANGED
|
@@ -21,7 +21,7 @@ import { getConversationManager } from './conversation.js';
|
|
|
21
21
|
import { getSessionManager } from './session-manager.js';
|
|
22
22
|
import { SlashCommandHandler, parseInput } from './slash-commands.js';
|
|
23
23
|
import { SystemPromptGenerator } from './system-prompt-generator.js';
|
|
24
|
-
import { theme, icons, colors, styleHelpers, renderMarkdown } from './theme.js';
|
|
24
|
+
import { theme, icons, colors, styleHelpers, renderMarkdown, renderDiff, renderLines } from './theme.js';
|
|
25
25
|
import { getCancellationManager } from './cancellation.js';
|
|
26
26
|
import { getContextCompressor } from './context-compressor.js';
|
|
27
27
|
import { getLogger } from './logger.js';
|
|
@@ -66,6 +66,11 @@ export class InteractiveSession {
|
|
|
66
66
|
// Register /clear callback, clear local conversation when clearing dialogue
|
|
67
67
|
this.slashCommandHandler.setClearCallback(() => {
|
|
68
68
|
this.conversation = [];
|
|
69
|
+
this.toolCalls = [];
|
|
70
|
+
this.currentTaskId = null;
|
|
71
|
+
this.taskCompleted = false;
|
|
72
|
+
this.isFirstApiCall = true;
|
|
73
|
+
this.slashCommandHandler.setConversationHistory([]);
|
|
69
74
|
});
|
|
70
75
|
// Register MCP update callback, update system prompt
|
|
71
76
|
this.slashCommandHandler.setSystemPromptUpdateCallback(async () => {
|
|
@@ -117,8 +122,8 @@ export class InteractiveSession {
|
|
|
117
122
|
console.log('');
|
|
118
123
|
console.log(colors.gradient('╔════════════════════════════════════════════════════════════╗'));
|
|
119
124
|
console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
|
|
120
|
-
console.log(' '.repeat(
|
|
121
|
-
console.log(' '.repeat(
|
|
125
|
+
console.log(colors.gradient('║') + ' '.repeat(13) + '🤖 ' + colors.gradient('XAGENT CLI') + ' '.repeat(32) + colors.gradient(' ║'));
|
|
126
|
+
console.log(colors.gradient('║') + ' '.repeat(16) + colors.textMuted(`v${packageJson.version}`) + ' '.repeat(36) + colors.gradient(' ║'));
|
|
122
127
|
console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
|
|
123
128
|
console.log(colors.gradient('╚════════════════════════════════════════════════════════════╝'));
|
|
124
129
|
console.log(colors.textMuted(' AI-powered command-line assistant'));
|
|
@@ -167,7 +172,7 @@ export class InteractiveSession {
|
|
|
167
172
|
if (authConfig.apiKey && selectedAuthType === AuthType.OAUTH_XAGENT) {
|
|
168
173
|
clearInterval(spinnerInterval);
|
|
169
174
|
process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear the line
|
|
170
|
-
const baseUrl = authConfig.xagentApiBaseUrl || 'https://
|
|
175
|
+
const baseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
|
|
171
176
|
let isValid = await this.validateToken(baseUrl, authConfig.apiKey);
|
|
172
177
|
// Try refresh token if validation failed
|
|
173
178
|
if (!isValid && authConfig.refreshToken) {
|
|
@@ -190,8 +195,8 @@ export class InteractiveSession {
|
|
|
190
195
|
}
|
|
191
196
|
if (!isValid) {
|
|
192
197
|
console.log('');
|
|
193
|
-
console.log(colors.warning('
|
|
194
|
-
console.log(colors.info('Please
|
|
198
|
+
console.log(colors.warning('Your xAgent session has expired or is not configured'));
|
|
199
|
+
console.log(colors.info('Please select an authentication method to continue.'));
|
|
195
200
|
console.log('');
|
|
196
201
|
// Clear invalid credentials and persist
|
|
197
202
|
// Note: Do NOT overwrite selectedAuthType - let user re-select their preferred auth method
|
|
@@ -242,7 +247,7 @@ export class InteractiveSession {
|
|
|
242
247
|
logger.debug('[SESSION] Final selectedAuthType:', String(selectedAuthType));
|
|
243
248
|
logger.debug('[SESSION] Creating RemoteAIClient?', String(selectedAuthType === AuthType.OAUTH_XAGENT));
|
|
244
249
|
if (selectedAuthType === AuthType.OAUTH_XAGENT) {
|
|
245
|
-
const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://
|
|
250
|
+
const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
|
|
246
251
|
// In OAuth XAGENT mode, we still pass apiKey (can be empty or used for other purposes)
|
|
247
252
|
this.remoteAIClient = new RemoteAIClient(authConfig.apiKey || '', webBaseUrl, authConfig.showAIDebugInfo);
|
|
248
253
|
logger.debug('[DEBUG Initialize] RemoteAIClient created successfully');
|
|
@@ -372,11 +377,14 @@ export class InteractiveSession {
|
|
|
372
377
|
process.exit(1);
|
|
373
378
|
}
|
|
374
379
|
const authConfig = authService.getAuthConfig();
|
|
375
|
-
// VLM configuration is optional -
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
+
// VLM configuration is optional - only show for non-OAuth (local) mode
|
|
381
|
+
// Remote mode uses backend VLM configuration
|
|
382
|
+
if (authType !== AuthType.OAUTH_XAGENT) {
|
|
383
|
+
console.log('');
|
|
384
|
+
console.log(colors.info(`${icons.info} VLM configuration is optional.`));
|
|
385
|
+
console.log(colors.info(`You can configure it later using the /vlm command if needed.`));
|
|
386
|
+
console.log('');
|
|
387
|
+
}
|
|
380
388
|
// Save LLM config only, skip VLM for now
|
|
381
389
|
await this.configManager.setAuthConfig(authConfig);
|
|
382
390
|
}
|
|
@@ -776,11 +784,7 @@ export class InteractiveSession {
|
|
|
776
784
|
const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
|
|
777
785
|
const messages = [
|
|
778
786
|
{ role: 'system', content: `${enhancedSystemPrompt}\n\n${memory}`, timestamp: Date.now() },
|
|
779
|
-
...this.conversation
|
|
780
|
-
role: msg.role,
|
|
781
|
-
content: msg.content,
|
|
782
|
-
timestamp: msg.timestamp
|
|
783
|
-
}))
|
|
787
|
+
...this.conversation
|
|
784
788
|
];
|
|
785
789
|
const operationId = `ai-response-${Date.now()}`;
|
|
786
790
|
const response = await this.cancellationManager.withCancellation(chatCompletion(messages, {
|
|
@@ -920,11 +924,7 @@ export class InteractiveSession {
|
|
|
920
924
|
// Build messages with system prompt (与本地模式一致)
|
|
921
925
|
const messages = [
|
|
922
926
|
{ role: 'system', content: `${enhancedSystemPrompt}\n\n${memory}`, timestamp: Date.now() },
|
|
923
|
-
...this.conversation
|
|
924
|
-
role: msg.role,
|
|
925
|
-
content: msg.content,
|
|
926
|
-
timestamp: msg.timestamp
|
|
927
|
-
}))
|
|
927
|
+
...this.conversation
|
|
928
928
|
];
|
|
929
929
|
// Call unified LLM API with cancellation support
|
|
930
930
|
const operationId = `remote-ai-response-${Date.now()}`;
|
|
@@ -973,7 +973,10 @@ export class InteractiveSession {
|
|
|
973
973
|
});
|
|
974
974
|
// Handle tool calls
|
|
975
975
|
if (toolCalls.length > 0) {
|
|
976
|
-
await this.
|
|
976
|
+
await this.handleToolCalls(toolCalls, async () => {
|
|
977
|
+
// Remote mode continuation: reuse existing taskId
|
|
978
|
+
await this.generateRemoteResponse(0, this.currentTaskId || undefined);
|
|
979
|
+
});
|
|
977
980
|
}
|
|
978
981
|
// Checkpoint support (consistent with local mode)
|
|
979
982
|
if (this.checkpointManager.isEnabled()) {
|
|
@@ -1027,9 +1030,9 @@ export class InteractiveSession {
|
|
|
1027
1030
|
});
|
|
1028
1031
|
// Reinitialize RemoteAIClient with new token
|
|
1029
1032
|
if (authConfig.apiKey) {
|
|
1030
|
-
const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://
|
|
1033
|
+
const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
|
|
1031
1034
|
logger.debug('[DEBUG generateRemoteResponse] Reinitializing RemoteAIClient with new token');
|
|
1032
|
-
const newWebBaseUrl = authConfig.xagentApiBaseUrl || 'https://
|
|
1035
|
+
const newWebBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
|
|
1033
1036
|
this.remoteAIClient = new RemoteAIClient(authConfig.apiKey, newWebBaseUrl, authConfig.showAIDebugInfo);
|
|
1034
1037
|
}
|
|
1035
1038
|
else {
|
|
@@ -1050,7 +1053,7 @@ export class InteractiveSession {
|
|
|
1050
1053
|
return;
|
|
1051
1054
|
}
|
|
1052
1055
|
}
|
|
1053
|
-
async handleToolCalls(toolCalls) {
|
|
1056
|
+
async handleToolCalls(toolCalls, onComplete) {
|
|
1054
1057
|
// Mark that tool execution is in progress
|
|
1055
1058
|
this._isOperationInProgress = true;
|
|
1056
1059
|
const toolRegistry = getToolRegistry();
|
|
@@ -1066,7 +1069,7 @@ export class InteractiveSession {
|
|
|
1066
1069
|
catch (e) {
|
|
1067
1070
|
parsedParams = params;
|
|
1068
1071
|
}
|
|
1069
|
-
return { name, params: parsedParams, index };
|
|
1072
|
+
return { name, params: parsedParams, index, id: toolCall.id };
|
|
1070
1073
|
});
|
|
1071
1074
|
// Display all tool calls info
|
|
1072
1075
|
for (const { name, params } of preparedToolCalls) {
|
|
@@ -1084,6 +1087,7 @@ export class InteractiveSession {
|
|
|
1084
1087
|
// Execute all tools in parallel
|
|
1085
1088
|
const results = await toolRegistry.executeAll(preparedToolCalls.map(tc => ({ name: tc.name, params: tc.params })), this.executionMode);
|
|
1086
1089
|
// Process results and maintain order
|
|
1090
|
+
let hasError = false;
|
|
1087
1091
|
for (const { tool, result, error } of results) {
|
|
1088
1092
|
const toolCall = preparedToolCalls.find(tc => tc.name === tool);
|
|
1089
1093
|
if (!toolCall)
|
|
@@ -1095,11 +1099,13 @@ export class InteractiveSession {
|
|
|
1095
1099
|
if (error === 'Operation cancelled by user') {
|
|
1096
1100
|
return;
|
|
1097
1101
|
}
|
|
1102
|
+
hasError = true;
|
|
1098
1103
|
console.log('');
|
|
1099
1104
|
console.log(`${indent}${colors.error(`${icons.cross} Tool Error: ${error}`)}`);
|
|
1100
1105
|
this.conversation.push({
|
|
1101
1106
|
role: 'tool',
|
|
1102
1107
|
content: JSON.stringify({ error }),
|
|
1108
|
+
tool_call_id: toolCall.id,
|
|
1103
1109
|
timestamp: Date.now()
|
|
1104
1110
|
});
|
|
1105
1111
|
}
|
|
@@ -1109,6 +1115,22 @@ export class InteractiveSession {
|
|
|
1109
1115
|
const displayIndent = isGuiSubagent ? indent + ' ' : indent;
|
|
1110
1116
|
// Always show details for todo tools so users can see their task lists
|
|
1111
1117
|
const isTodoTool = tool === 'todo_write' || tool === 'todo_read';
|
|
1118
|
+
// Special handling for edit tool with diff
|
|
1119
|
+
const isEditTool = tool === 'edit';
|
|
1120
|
+
const hasDiff = isEditTool && result?.diff;
|
|
1121
|
+
// Special handling for Write tool with file preview
|
|
1122
|
+
const isWriteTool = tool === 'Write';
|
|
1123
|
+
const hasFilePreview = isWriteTool && result?.preview;
|
|
1124
|
+
// Special handling for DeleteFile tool
|
|
1125
|
+
const isDeleteTool = tool === 'DeleteFile';
|
|
1126
|
+
const hasDeleteInfo = isDeleteTool && result?.filePath;
|
|
1127
|
+
// Special handling for task tool (subagent)
|
|
1128
|
+
const isTaskTool = tool === 'task' && params?.subagent_type;
|
|
1129
|
+
// Check if tool is an MCP wrapper tool by looking up in tool registry
|
|
1130
|
+
const { getToolRegistry } = await import('./tools.js');
|
|
1131
|
+
const toolRegistry = getToolRegistry();
|
|
1132
|
+
const toolDef = toolRegistry.get(tool);
|
|
1133
|
+
const isMcpTool = toolDef && toolDef._isMcpTool === true;
|
|
1112
1134
|
if (isTodoTool) {
|
|
1113
1135
|
console.log('');
|
|
1114
1136
|
console.log(`${displayIndent}${colors.success(`${icons.check} Todo List:`)}`);
|
|
@@ -1118,6 +1140,113 @@ export class InteractiveSession {
|
|
|
1118
1140
|
console.log(`${displayIndent}${colors.textDim(result.message)}`);
|
|
1119
1141
|
}
|
|
1120
1142
|
}
|
|
1143
|
+
else if (hasDiff) {
|
|
1144
|
+
// Show edit result with diff
|
|
1145
|
+
console.log('');
|
|
1146
|
+
const diffOutput = renderDiff(result.diff);
|
|
1147
|
+
const indentedDiff = diffOutput.split('\n').map(line => `${displayIndent} ${line}`).join('\n');
|
|
1148
|
+
console.log(`${indentedDiff}`);
|
|
1149
|
+
}
|
|
1150
|
+
else if (hasFilePreview) {
|
|
1151
|
+
// Show new file content in diff-like style
|
|
1152
|
+
console.log('');
|
|
1153
|
+
console.log(`${displayIndent}${colors.success(`${icons.file} ${result.filePath}`)}`);
|
|
1154
|
+
console.log(`${displayIndent}${colors.textDim(` ${result.lineCount} lines`)}`);
|
|
1155
|
+
console.log('');
|
|
1156
|
+
console.log(renderLines(result.preview, { maxLines: 10, indent: displayIndent + ' ' }));
|
|
1157
|
+
}
|
|
1158
|
+
else if (hasDeleteInfo) {
|
|
1159
|
+
// Show DeleteFile result
|
|
1160
|
+
console.log('');
|
|
1161
|
+
console.log(`${displayIndent}${colors.success(`${icons.check} Deleted: ${result.filePath}`)}`);
|
|
1162
|
+
}
|
|
1163
|
+
else if (isTaskTool) {
|
|
1164
|
+
// Special handling for task tool (subagent) - show friendly summary
|
|
1165
|
+
console.log('');
|
|
1166
|
+
const subagentType = params.subagent_type;
|
|
1167
|
+
const subagentName = params.description || (params.prompt ? params.prompt.substring(0, 50).replace(/\n/g, ' ') : 'Unknown task');
|
|
1168
|
+
if (result?.success) {
|
|
1169
|
+
console.log(`${displayIndent}${colors.success(`${icons.check} ${subagentType}: Completed`)}`);
|
|
1170
|
+
console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
|
|
1171
|
+
if (result.message) {
|
|
1172
|
+
console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
else if (result?.cancelled) {
|
|
1176
|
+
console.log(`${displayIndent}${colors.warning(`${icons.cross} ${subagentType}: Cancelled`)}`);
|
|
1177
|
+
console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
|
|
1178
|
+
}
|
|
1179
|
+
else {
|
|
1180
|
+
console.log(`${displayIndent}${colors.error(`${icons.cross} ${subagentType}: Failed`)}`);
|
|
1181
|
+
console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
|
|
1182
|
+
if (result?.message) {
|
|
1183
|
+
console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
else if (isMcpTool) {
|
|
1188
|
+
// Special handling for MCP tools - show friendly summary
|
|
1189
|
+
console.log('');
|
|
1190
|
+
// Extract server name and tool name from tool name (format: serverName__toolName)
|
|
1191
|
+
let serverName = 'MCP';
|
|
1192
|
+
let toolDisplayName = tool;
|
|
1193
|
+
if (tool.includes('__')) {
|
|
1194
|
+
const parts = tool.split('__');
|
|
1195
|
+
serverName = parts[0];
|
|
1196
|
+
toolDisplayName = parts.slice(1).join('__');
|
|
1197
|
+
}
|
|
1198
|
+
// Try to extract meaningful content from MCP result
|
|
1199
|
+
let summary = '';
|
|
1200
|
+
if (result?.content && Array.isArray(result.content) && result.content.length > 0) {
|
|
1201
|
+
const firstBlock = result.content[0];
|
|
1202
|
+
if (firstBlock?.type === 'text' && firstBlock?.text) {
|
|
1203
|
+
const text = firstBlock.text;
|
|
1204
|
+
if (typeof text === 'string') {
|
|
1205
|
+
// Detect HTML content
|
|
1206
|
+
if (text.trim().startsWith('<!DOCTYPE') || text.trim().startsWith('<html')) {
|
|
1207
|
+
summary = '[HTML content fetched]';
|
|
1208
|
+
}
|
|
1209
|
+
else {
|
|
1210
|
+
// Try to parse if it's JSON
|
|
1211
|
+
try {
|
|
1212
|
+
const parsed = JSON.parse(text);
|
|
1213
|
+
if (Array.isArray(parsed) && parsed.length > 0 && parsed[0]?.title) {
|
|
1214
|
+
// Search results format
|
|
1215
|
+
summary = `Found ${parsed.length} result(s)`;
|
|
1216
|
+
}
|
|
1217
|
+
else if (parsed?.message) {
|
|
1218
|
+
summary = parsed.message;
|
|
1219
|
+
}
|
|
1220
|
+
else if (typeof parsed === 'string') {
|
|
1221
|
+
summary = parsed.substring(0, 100);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
catch {
|
|
1225
|
+
// Not JSON, use as-is with truncation
|
|
1226
|
+
summary = text.substring(0, 100);
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
else if (result?.message) {
|
|
1233
|
+
summary = result.message;
|
|
1234
|
+
}
|
|
1235
|
+
if (result?.success !== false) {
|
|
1236
|
+
console.log(`${displayIndent}${colors.success(`${icons.check} ${serverName}: Success`)}`);
|
|
1237
|
+
console.log(`${displayIndent}${colors.textDim(` Tool: ${toolDisplayName}`)}`);
|
|
1238
|
+
if (summary) {
|
|
1239
|
+
console.log(`${displayIndent}${colors.textDim(` ${summary}`)}`);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
else {
|
|
1243
|
+
console.log(`${displayIndent}${colors.error(`${icons.cross} ${serverName}: Failed`)}`);
|
|
1244
|
+
console.log(`${displayIndent}${colors.textDim(` Tool: ${toolDisplayName}`)}`);
|
|
1245
|
+
if (result?.message || result?.error) {
|
|
1246
|
+
console.log(`${displayIndent}${colors.textDim(` ${result?.message || result?.error}`)}`);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1121
1250
|
else if (showToolDetails) {
|
|
1122
1251
|
console.log('');
|
|
1123
1252
|
console.log(`${displayIndent}${colors.success(`${icons.check} Tool Result:`)}`);
|
|
@@ -1128,7 +1257,12 @@ export class InteractiveSession {
|
|
|
1128
1257
|
console.log(`${displayIndent}${colors.error(`${icons.cross} ${result.message || 'Failed'}`)}`);
|
|
1129
1258
|
}
|
|
1130
1259
|
else if (result) {
|
|
1131
|
-
|
|
1260
|
+
// Show brief preview by default (consistent with subagent behavior)
|
|
1261
|
+
const resultPreview = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
1262
|
+
const truncatedPreview = resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
|
|
1263
|
+
// Indent the preview
|
|
1264
|
+
const indentedPreview = truncatedPreview.split('\n').map(line => `${displayIndent} ${line}`).join('\n');
|
|
1265
|
+
console.log(`${indentedPreview}`);
|
|
1132
1266
|
}
|
|
1133
1267
|
else {
|
|
1134
1268
|
console.log(`${displayIndent}${colors.textDim('(no result)')}`);
|
|
@@ -1152,13 +1286,13 @@ export class InteractiveSession {
|
|
|
1152
1286
|
this.conversation.push({
|
|
1153
1287
|
role: 'tool',
|
|
1154
1288
|
content: JSON.stringify(result),
|
|
1289
|
+
tool_call_id: toolCall.id,
|
|
1155
1290
|
timestamp: Date.now()
|
|
1156
1291
|
});
|
|
1157
1292
|
}
|
|
1158
1293
|
}
|
|
1159
1294
|
// Logic: Only skip returning results to main agent when user explicitly cancelled (ESC)
|
|
1160
1295
|
// For all other cases (success, failure, errors), always return results for further processing
|
|
1161
|
-
const guiSubagentFailed = preparedToolCalls.some(tc => tc.name === 'task' && tc.params?.subagent_type === 'gui-subagent');
|
|
1162
1296
|
const guiSubagentCancelled = preparedToolCalls.some(tc => tc.name === 'task' && tc.params?.subagent_type === 'gui-subagent' && results.some(r => r.tool === 'task' && r.result?.cancelled === true));
|
|
1163
1297
|
// If GUI agent was cancelled by user, don't continue generating response
|
|
1164
1298
|
// This avoids wasting API calls and tokens on cancelled tasks
|
|
@@ -1168,9 +1302,27 @@ export class InteractiveSession {
|
|
|
1168
1302
|
this._isOperationInProgress = false;
|
|
1169
1303
|
return;
|
|
1170
1304
|
}
|
|
1171
|
-
//
|
|
1172
|
-
|
|
1173
|
-
|
|
1305
|
+
// Handle errors and completion based on whether onComplete callback is provided
|
|
1306
|
+
if (hasError) {
|
|
1307
|
+
this._isOperationInProgress = false;
|
|
1308
|
+
if (onComplete) {
|
|
1309
|
+
// Remote mode: callback handles error state (throws to mark task cancelled)
|
|
1310
|
+
throw new Error('Tool execution failed');
|
|
1311
|
+
}
|
|
1312
|
+
else {
|
|
1313
|
+
// Local mode: throw error to mark task as cancelled
|
|
1314
|
+
throw new Error('Tool execution failed');
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
// Continue based on mode
|
|
1318
|
+
if (onComplete) {
|
|
1319
|
+
// Remote mode: use provided callback
|
|
1320
|
+
await onComplete();
|
|
1321
|
+
}
|
|
1322
|
+
else {
|
|
1323
|
+
// Local mode: default behavior
|
|
1324
|
+
await this.generateResponse();
|
|
1325
|
+
}
|
|
1174
1326
|
}
|
|
1175
1327
|
/**
|
|
1176
1328
|
* Get user-friendly description for tool
|
|
@@ -1182,10 +1334,10 @@ export class InteractiveSession {
|
|
|
1182
1334
|
'Grep': (p) => `Search text: "${p.pattern}"`,
|
|
1183
1335
|
'Bash': (p) => `Execute command: ${this.truncateCommand(p.command)}`,
|
|
1184
1336
|
'ListDirectory': (p) => `List directory: ${this.truncatePath(p.path || '.')}`,
|
|
1185
|
-
'
|
|
1337
|
+
'SearchFiles': (p) => `Search files: ${p.pattern}`,
|
|
1186
1338
|
'DeleteFile': (p) => `Delete file: ${this.truncatePath(p.filePath)}`,
|
|
1187
1339
|
'CreateDirectory': (p) => `Create directory: ${this.truncatePath(p.dirPath)}`,
|
|
1188
|
-
'
|
|
1340
|
+
'edit': (p) => `Edit text: ${this.truncatePath(p.file_path)}`,
|
|
1189
1341
|
'web_search': (p) => `Web search: "${p.query}"`,
|
|
1190
1342
|
'todo_write': () => `Update todo list`,
|
|
1191
1343
|
'todo_read': () => `Read todo list`,
|
|
@@ -1205,136 +1357,6 @@ export class InteractiveSession {
|
|
|
1205
1357
|
const getDescription = descriptions[toolName];
|
|
1206
1358
|
return getDescription ? getDescription(params) : `Execute tool: ${toolName}`;
|
|
1207
1359
|
}
|
|
1208
|
-
/**
|
|
1209
|
-
* Handle tool calls for remote AI mode
|
|
1210
|
-
* Executes tools and then continues the conversation with results
|
|
1211
|
-
*/
|
|
1212
|
-
async handleRemoteToolCalls(toolCalls) {
|
|
1213
|
-
// Mark that tool execution is in progress
|
|
1214
|
-
this._isOperationInProgress = true;
|
|
1215
|
-
const toolRegistry = getToolRegistry();
|
|
1216
|
-
const showToolDetails = this.configManager.get('showToolDetails') || false;
|
|
1217
|
-
const indent = this.getIndent();
|
|
1218
|
-
// Prepare all tool calls
|
|
1219
|
-
const preparedToolCalls = toolCalls.map((toolCall, index) => {
|
|
1220
|
-
const { name, arguments: params } = toolCall.function;
|
|
1221
|
-
let parsedParams;
|
|
1222
|
-
try {
|
|
1223
|
-
parsedParams = typeof params === 'string' ? JSON.parse(params) : params;
|
|
1224
|
-
}
|
|
1225
|
-
catch (e) {
|
|
1226
|
-
parsedParams = params;
|
|
1227
|
-
}
|
|
1228
|
-
return { name, params: parsedParams, index };
|
|
1229
|
-
});
|
|
1230
|
-
// Display all tool calls info
|
|
1231
|
-
for (const { name, params } of preparedToolCalls) {
|
|
1232
|
-
if (showToolDetails) {
|
|
1233
|
-
console.log('');
|
|
1234
|
-
console.log(`${indent}${colors.warning(`${icons.tool} Tool Call: ${name}`)}`);
|
|
1235
|
-
console.log(`${indent}${colors.textDim(JSON.stringify(params, null, 2))}`);
|
|
1236
|
-
}
|
|
1237
|
-
else {
|
|
1238
|
-
const toolDescription = this.getToolDescription(name, params);
|
|
1239
|
-
console.log('');
|
|
1240
|
-
console.log(`${indent}${colors.textMuted(`${icons.loading} ${toolDescription}`)}`);
|
|
1241
|
-
}
|
|
1242
|
-
}
|
|
1243
|
-
// Execute all tools in parallel
|
|
1244
|
-
const results = await toolRegistry.executeAll(preparedToolCalls.map(tc => ({ name: tc.name, params: tc.params })), this.executionMode);
|
|
1245
|
-
// Process results and maintain order
|
|
1246
|
-
let hasError = false;
|
|
1247
|
-
for (const { tool, result, error } of results) {
|
|
1248
|
-
const toolCall = preparedToolCalls.find(tc => tc.name === tool);
|
|
1249
|
-
if (!toolCall)
|
|
1250
|
-
continue;
|
|
1251
|
-
const { params } = toolCall;
|
|
1252
|
-
if (error) {
|
|
1253
|
-
// Clear the operation flag
|
|
1254
|
-
this._isOperationInProgress = false;
|
|
1255
|
-
if (error === 'Operation cancelled by user') {
|
|
1256
|
-
return;
|
|
1257
|
-
}
|
|
1258
|
-
hasError = true;
|
|
1259
|
-
console.log('');
|
|
1260
|
-
console.log(`${indent}${colors.error(`${icons.cross} Tool Error: ${error}`)}`);
|
|
1261
|
-
this.conversation.push({
|
|
1262
|
-
role: 'tool',
|
|
1263
|
-
content: JSON.stringify({ error }),
|
|
1264
|
-
timestamp: Date.now()
|
|
1265
|
-
});
|
|
1266
|
-
}
|
|
1267
|
-
else {
|
|
1268
|
-
// Use correct indent for gui-subagent tasks
|
|
1269
|
-
const isGuiSubagent = tool === 'task' && params?.subagent_type === 'gui-subagent';
|
|
1270
|
-
const displayIndent = isGuiSubagent ? indent + ' ' : indent;
|
|
1271
|
-
// Always show details for todo tools so users can see their task lists
|
|
1272
|
-
const isTodoTool = tool === 'todo_write' || tool === 'todo_read';
|
|
1273
|
-
if (isTodoTool) {
|
|
1274
|
-
console.log('');
|
|
1275
|
-
console.log(`${displayIndent}${colors.success(`${icons.check} Todo List:`)}`);
|
|
1276
|
-
console.log(this.renderTodoList(result.todos || result.todos, displayIndent));
|
|
1277
|
-
// Show summary if available
|
|
1278
|
-
if (result.message) {
|
|
1279
|
-
console.log(`${displayIndent}${colors.textDim(result.message)}`);
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
else if (showToolDetails) {
|
|
1283
|
-
console.log('');
|
|
1284
|
-
console.log(`${displayIndent}${colors.success(`${icons.check} Tool Result:`)}`);
|
|
1285
|
-
console.log(`${displayIndent}${colors.textDim(JSON.stringify(result, null, 2))}`);
|
|
1286
|
-
}
|
|
1287
|
-
else if (result.success === false) {
|
|
1288
|
-
// GUI task or other tool failed
|
|
1289
|
-
console.log(`${displayIndent}${colors.error(`${icons.cross} ${result.message || 'Failed'}`)}`);
|
|
1290
|
-
}
|
|
1291
|
-
else {
|
|
1292
|
-
console.log(`${displayIndent}${colors.success(`${icons.check} Completed`)}`);
|
|
1293
|
-
}
|
|
1294
|
-
const toolCallRecord = {
|
|
1295
|
-
tool,
|
|
1296
|
-
params,
|
|
1297
|
-
result,
|
|
1298
|
-
timestamp: Date.now()
|
|
1299
|
-
};
|
|
1300
|
-
this.toolCalls.push(toolCallRecord);
|
|
1301
|
-
// Record tool output to session manager
|
|
1302
|
-
await this.sessionManager.addOutput({
|
|
1303
|
-
role: 'tool',
|
|
1304
|
-
content: JSON.stringify(result),
|
|
1305
|
-
toolName: tool,
|
|
1306
|
-
toolParams: params,
|
|
1307
|
-
toolResult: result,
|
|
1308
|
-
timestamp: Date.now()
|
|
1309
|
-
});
|
|
1310
|
-
this.conversation.push({
|
|
1311
|
-
role: 'tool',
|
|
1312
|
-
content: JSON.stringify(result),
|
|
1313
|
-
timestamp: Date.now()
|
|
1314
|
-
});
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
// Logic: Only skip returning results to main agent when user explicitly cancelled (ESC)
|
|
1318
|
-
// For all other cases (success, failure, errors), always return results for further processing
|
|
1319
|
-
const guiSubagentFailed = preparedToolCalls.some(tc => tc.name === 'task' && tc.params?.subagent_type === 'gui-subagent');
|
|
1320
|
-
const guiSubagentCancelled = preparedToolCalls.some(tc => tc.name === 'task' && tc.params?.subagent_type === 'gui-subagent' && results.some(r => r.tool === 'task' && r.result?.cancelled === true));
|
|
1321
|
-
// If GUI agent was cancelled by user, don't continue generating response
|
|
1322
|
-
// This avoids wasting API calls and tokens on cancelled tasks
|
|
1323
|
-
if (guiSubagentCancelled) {
|
|
1324
|
-
console.log('');
|
|
1325
|
-
console.log(`${indent}${colors.textMuted('GUI task cancelled by user')}`);
|
|
1326
|
-
this._isOperationInProgress = false;
|
|
1327
|
-
return;
|
|
1328
|
-
}
|
|
1329
|
-
// If any tool call failed, throw error to mark task as cancelled
|
|
1330
|
-
if (hasError) {
|
|
1331
|
-
throw new Error('Tool execution failed');
|
|
1332
|
-
}
|
|
1333
|
-
// For all other cases (GUI success/failure, other tool errors), return results to main agent
|
|
1334
|
-
// This allows main agent to decide how to handle failures (retry, fallback, user notification, etc.)
|
|
1335
|
-
// Reuse existing taskId instead of generating new one
|
|
1336
|
-
await this.generateRemoteResponse(0, this.currentTaskId || undefined);
|
|
1337
|
-
}
|
|
1338
1360
|
/**
|
|
1339
1361
|
* Truncate path for display
|
|
1340
1362
|
*/
|