@xagent-ai/cli 1.3.0 → 1.3.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/.github/release.yml +76 -0
- package/.github/workflows/ci.yml +3 -0
- package/.github/workflows/release.yml +11 -17
- package/README.md +2 -2
- package/README_CN.md +2 -2
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +7 -3
- package/dist/agents.js.map +1 -1
- package/dist/ai-client/factory.d.ts +0 -12
- package/dist/ai-client/factory.d.ts.map +1 -1
- package/dist/ai-client/factory.js +0 -32
- package/dist/ai-client/factory.js.map +1 -1
- package/dist/ai-client/index.js +1 -1
- package/dist/ai-client/index.js.map +1 -1
- package/dist/ai-client/providers/anthropic.d.ts.map +1 -1
- package/dist/ai-client/providers/anthropic.js +10 -4
- package/dist/ai-client/providers/anthropic.js.map +1 -1
- package/dist/ai-client/providers/openai.d.ts.map +1 -1
- package/dist/ai-client/providers/openai.js +8 -4
- package/dist/ai-client/providers/openai.js.map +1 -1
- package/dist/ai-client/providers/remote.d.ts +0 -1
- package/dist/ai-client/providers/remote.d.ts.map +1 -1
- package/dist/ai-client/providers/remote.js +11 -10
- package/dist/ai-client/providers/remote.js.map +1 -1
- package/dist/ai-client/types.d.ts +14 -0
- package/dist/ai-client/types.d.ts.map +1 -1
- package/dist/ai-client/types.js +17 -0
- package/dist/ai-client/types.js.map +1 -1
- package/dist/ai-client-factory.d.ts.map +1 -1
- package/dist/ai-client-factory.js +4 -4
- package/dist/ai-client-factory.js.map +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +10 -12
- package/dist/auth.js.map +1 -1
- package/dist/cancellation.d.ts.map +1 -1
- package/dist/cancellation.js +3 -5
- package/dist/cancellation.js.map +1 -1
- package/dist/checkpoint.d.ts +1 -0
- package/dist/checkpoint.d.ts.map +1 -1
- package/dist/checkpoint.js +37 -4
- package/dist/checkpoint.js.map +1 -1
- package/dist/cli.js +132 -32
- package/dist/cli.js.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/context-compressor.d.ts +1 -2
- package/dist/context-compressor.d.ts.map +1 -1
- package/dist/context-compressor.js +23 -18
- package/dist/context-compressor.js.map +1 -1
- package/dist/conversation.d.ts +1 -1
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +8 -7
- package/dist/conversation.js.map +1 -1
- package/dist/gui-subagent/action-parser/actionParser.js +2 -2
- package/dist/gui-subagent/action-parser/actionParser.js.map +1 -1
- package/dist/gui-subagent/agent/gui-agent.d.ts +10 -0
- package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -1
- package/dist/gui-subagent/agent/gui-agent.js +105 -32
- package/dist/gui-subagent/agent/gui-agent.js.map +1 -1
- package/dist/gui-subagent/index.d.ts +7 -0
- package/dist/gui-subagent/index.d.ts.map +1 -1
- package/dist/gui-subagent/index.js +2 -0
- package/dist/gui-subagent/index.js.map +1 -1
- package/dist/gui-subagent/operator/computer-operator.d.ts.map +1 -1
- package/dist/gui-subagent/operator/computer-operator.js +2 -0
- package/dist/gui-subagent/operator/computer-operator.js.map +1 -1
- package/dist/input-processor.js +2 -2
- package/dist/input-processor.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +1 -1
- package/dist/logger.js.map +1 -1
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +83 -21
- package/dist/mcp.js.map +1 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +3 -3
- package/dist/memory.js.map +1 -1
- package/dist/output-util.d.ts +27 -0
- package/dist/output-util.d.ts.map +1 -0
- package/dist/output-util.js +74 -0
- package/dist/output-util.js.map +1 -0
- package/dist/retry.js +1 -1
- package/dist/retry.js.map +1 -1
- package/dist/ripgrep.d.ts.map +1 -1
- package/dist/ripgrep.js +5 -3
- package/dist/ripgrep.js.map +1 -1
- package/dist/sdk-output-adapter.d.ts +265 -0
- package/dist/sdk-output-adapter.d.ts.map +1 -0
- package/dist/sdk-output-adapter.js +701 -0
- package/dist/sdk-output-adapter.js.map +1 -0
- package/dist/sdk-session.d.ts +13 -0
- package/dist/sdk-session.d.ts.map +1 -0
- package/dist/sdk-session.js +50 -0
- package/dist/sdk-session.js.map +1 -0
- package/dist/session-manager.js +3 -3
- package/dist/session-manager.js.map +1 -1
- package/dist/session.d.ts +96 -2
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +849 -262
- package/dist/session.js.map +1 -1
- package/dist/shell.d.ts.map +1 -1
- package/dist/shell.js +5 -4
- package/dist/shell.js.map +1 -1
- package/dist/skill-installer.js +3 -3
- package/dist/skill-installer.js.map +1 -1
- package/dist/skill-invoker.d.ts +1 -1
- package/dist/skill-invoker.d.ts.map +1 -1
- package/dist/skill-invoker.js +2 -2
- package/dist/skill-invoker.js.map +1 -1
- package/dist/skill-loader.js +6 -5
- package/dist/skill-loader.js.map +1 -1
- package/dist/skill-manager.d.ts.map +1 -1
- package/dist/skill-manager.js +3 -2
- package/dist/skill-manager.js.map +1 -1
- package/dist/slash-commands.d.ts +1 -1
- package/dist/slash-commands.d.ts.map +1 -1
- package/dist/slash-commands.js +24 -11
- package/dist/slash-commands.js.map +1 -1
- package/dist/smart-approval.d.ts +20 -1
- package/dist/smart-approval.d.ts.map +1 -1
- package/dist/smart-approval.js +58 -1
- package/dist/smart-approval.js.map +1 -1
- package/dist/system-prompt-generator.js +3 -3
- package/dist/system-prompt-generator.js.map +1 -1
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +8 -7
- package/dist/theme.js.map +1 -1
- package/dist/tools.d.ts +15 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +487 -215
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +57 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +49 -0
- package/dist/types.js.map +1 -1
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +12 -9
- package/dist/update.js.map +1 -1
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +1 -2
- package/dist/workflow.js.map +1 -1
- package/docs/third-party-models.md +16 -15
- package/package.json +3 -1
- package/src/agents.ts +7 -3
- package/src/ai-client/factory.ts +1 -36
- package/src/ai-client/index.ts +1 -1
- package/src/ai-client/providers/anthropic.ts +12 -3
- package/src/ai-client/providers/openai.ts +10 -4
- package/src/ai-client/providers/remote.ts +13 -10
- package/src/ai-client/types.ts +19 -0
- package/src/ai-client-factory.ts +5 -5
- package/src/auth.ts +11 -13
- package/src/cancellation.ts +3 -6
- package/src/checkpoint.ts +40 -4
- package/src/cli.ts +154 -37
- package/src/config.ts +1 -1
- package/src/context-compressor.ts +28 -23
- package/src/conversation.ts +9 -7
- package/src/gui-subagent/action-parser/actionParser.ts +2 -2
- package/src/gui-subagent/agent/gui-agent.ts +117 -34
- package/src/gui-subagent/index.ts +8 -0
- package/src/gui-subagent/operator/computer-operator.ts +2 -1
- package/src/input-processor.ts +2 -2
- package/src/logger.ts +2 -4
- package/src/mcp.ts +86 -23
- package/src/memory.ts +3 -4
- package/src/output-util.ts +80 -0
- package/src/retry.ts +1 -1
- package/src/ripgrep.ts +5 -3
- package/src/sdk-output-adapter.ts +842 -0
- package/src/sdk-session.ts +62 -0
- package/src/session-manager.ts +3 -3
- package/src/session.ts +942 -302
- package/src/shell.ts +6 -5
- package/src/skill-installer.ts +3 -3
- package/src/skill-invoker.ts +3 -4
- package/src/skill-loader.ts +7 -7
- package/src/skill-manager.ts +4 -3
- package/src/slash-commands.ts +24 -16
- package/src/smart-approval.ts +76 -1
- package/src/system-prompt-generator.ts +3 -3
- package/src/theme.ts +9 -8
- package/src/tools.ts +563 -267
- package/src/types.ts +118 -0
- package/src/update.ts +12 -9
- package/src/workflow.ts +2 -4
- package/test/cli-launch.test.ts +279 -0
- package/vitest.config.ts +2 -0
- /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
package/dist/tools.js
CHANGED
|
@@ -283,6 +283,8 @@ This is useful when working with skills that have local dependencies.
|
|
|
283
283
|
allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
|
|
284
284
|
async execute(params) {
|
|
285
285
|
const { command, cwd, description, timeout = 120, run_in_bg = false, skillPath } = params;
|
|
286
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
287
|
+
void description;
|
|
286
288
|
// Determine effective working directory
|
|
287
289
|
// Only use cwd if the command doesn't contain 'cd' (let LLM control directory)
|
|
288
290
|
let effectiveCwd;
|
|
@@ -406,7 +408,7 @@ This is useful when working with skills that have local dependencies.
|
|
|
406
408
|
const text = data.toString();
|
|
407
409
|
output.push(text);
|
|
408
410
|
});
|
|
409
|
-
childProcess.on('close', (
|
|
411
|
+
childProcess.on('close', (_code) => {
|
|
410
412
|
// Silent cleanup - don't log to avoid noise during normal operation
|
|
411
413
|
// Note: On Windows with PowerShell, the shell process exits after
|
|
412
414
|
// the command completes
|
|
@@ -434,8 +436,8 @@ This is useful when working with skills that have local dependencies.
|
|
|
434
436
|
// Apply truncation to stdout and stderr separately
|
|
435
437
|
const stdoutResult = truncateTail(result.stdout);
|
|
436
438
|
const stderrResult = truncateTail(result.stderr);
|
|
437
|
-
|
|
438
|
-
|
|
439
|
+
const stdout = stdoutResult.content;
|
|
440
|
+
const stderr = stderrResult.content;
|
|
439
441
|
let truncationNotice = '';
|
|
440
442
|
if (stdoutResult.truncated) {
|
|
441
443
|
truncationNotice += buildTruncationNotice(stdoutResult) + '\n';
|
|
@@ -929,6 +931,8 @@ edit(
|
|
|
929
931
|
allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
|
|
930
932
|
async execute(params) {
|
|
931
933
|
const { file_path, instruction, old_string, new_string } = params;
|
|
934
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
935
|
+
void instruction;
|
|
932
936
|
try {
|
|
933
937
|
const absolutePath = path.resolve(file_path);
|
|
934
938
|
// Check if file exists
|
|
@@ -1244,10 +1248,12 @@ export class TaskTool {
|
|
|
1244
1248
|
const { getConfigManager } = await import('./config.js');
|
|
1245
1249
|
const config = getConfigManager();
|
|
1246
1250
|
const authConfig = config.getAuthConfig();
|
|
1247
|
-
|
|
1251
|
+
// aiClient is created for future use when executeParallelAgents supports it
|
|
1252
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1253
|
+
const _aiClient = createAIClient(authConfig);
|
|
1248
1254
|
const toolRegistry = getToolRegistry();
|
|
1249
1255
|
if (params.agents && params.agents.length > 0) {
|
|
1250
|
-
return await this.executeParallelAgents(params.agents, params.description, mode, agentManager, toolRegistry,
|
|
1256
|
+
return await this.executeParallelAgents(params.agents, params.description, mode, agentManager, toolRegistry, config);
|
|
1251
1257
|
}
|
|
1252
1258
|
if (!params.subagent_type) {
|
|
1253
1259
|
throw new Error('subagent_type is required for Task tool');
|
|
@@ -1264,7 +1270,7 @@ export class TaskTool {
|
|
|
1264
1270
|
agents: params.agents?.length,
|
|
1265
1271
|
}));
|
|
1266
1272
|
}
|
|
1267
|
-
const result = await this.executeSingleAgent(params.subagent_type, prompt, params.description, params.useContext ?? true, params.constraints || [], mode, agentManager, toolRegistry,
|
|
1273
|
+
const result = await this.executeSingleAgent(params.subagent_type, prompt, params.description, params.useContext ?? true, params.constraints || [], mode, agentManager, toolRegistry, config);
|
|
1268
1274
|
return result;
|
|
1269
1275
|
}
|
|
1270
1276
|
catch (error) {
|
|
@@ -1358,9 +1364,27 @@ export class TaskTool {
|
|
|
1358
1364
|
*/
|
|
1359
1365
|
async executeGUIAgent(prompt, description, agent, mode, config, indentLevel = 1, remoteAIClient) {
|
|
1360
1366
|
const indent = ' '.repeat(indentLevel);
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1367
|
+
// Get SDK adapter from session for SDK mode output
|
|
1368
|
+
let sdkOutputAdapter = null;
|
|
1369
|
+
let isSdkMode = false;
|
|
1370
|
+
try {
|
|
1371
|
+
const { getSingletonSession } = await import('./session.js');
|
|
1372
|
+
const session = getSingletonSession();
|
|
1373
|
+
if (session) {
|
|
1374
|
+
isSdkMode = session.isSdkMode;
|
|
1375
|
+
sdkOutputAdapter = session.sdkOutputAdapter;
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
catch {
|
|
1379
|
+
// Session not available
|
|
1380
|
+
}
|
|
1381
|
+
// SDK mode: use adapter output (guiAgent.run() handles SDK output internally)
|
|
1382
|
+
// Only output console messages in non-SDK mode
|
|
1383
|
+
if (!isSdkMode) {
|
|
1384
|
+
console.log(`${indent}${colors.primaryBright(`${icons.robot} GUI Agent`)}: ${description}`);
|
|
1385
|
+
console.log(`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`);
|
|
1386
|
+
console.log('');
|
|
1387
|
+
}
|
|
1364
1388
|
// Get VLM configuration for local mode
|
|
1365
1389
|
// NOTE: guiSubagentBaseUrl must be explicitly configured, NOT fallback to baseUrl
|
|
1366
1390
|
const baseUrl = config.get('guiSubagentBaseUrl') || '';
|
|
@@ -1370,19 +1394,33 @@ export class TaskTool {
|
|
|
1370
1394
|
const isRemoteMode = !!remoteAIClient;
|
|
1371
1395
|
// Log mode information
|
|
1372
1396
|
if (isRemoteMode) {
|
|
1373
|
-
|
|
1397
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1398
|
+
// SDK mode: use adapter output
|
|
1399
|
+
sdkOutputAdapter.outputInfo('Using remote VLM service');
|
|
1400
|
+
}
|
|
1401
|
+
else {
|
|
1402
|
+
// Normal mode: console output
|
|
1403
|
+
console.log(`${indent}${colors.info(`${icons.brain} Using remote VLM service`)}`);
|
|
1404
|
+
}
|
|
1374
1405
|
}
|
|
1375
1406
|
else {
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
};
|
|
1407
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1408
|
+
// SDK mode: use adapter output
|
|
1409
|
+
sdkOutputAdapter.outputInfo('Using local VLM configuration');
|
|
1410
|
+
}
|
|
1411
|
+
else {
|
|
1412
|
+
// Normal mode: console output
|
|
1413
|
+
console.log(`${indent}${colors.info(`${icons.brain} Using local VLM configuration`)}`);
|
|
1414
|
+
// Local mode requires explicit VLM configuration
|
|
1415
|
+
if (!baseUrl || !apiKey || !modelName) {
|
|
1416
|
+
return {
|
|
1417
|
+
success: false,
|
|
1418
|
+
message: `GUI task "${description}" failed: VLM not configured. Please run /model to configure Vision-Language Model first.`,
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
console.log(`${indent}${colors.textMuted(` Model: ${modelName}`)}`);
|
|
1422
|
+
console.log(`${indent}${colors.textMuted(` Base URL: ${baseUrl}`)}`);
|
|
1383
1423
|
}
|
|
1384
|
-
console.log(`${indent}${colors.textMuted(` Model: ${modelName}`)}`);
|
|
1385
|
-
console.log(`${indent}${colors.textMuted(` Base URL: ${baseUrl}`)}`);
|
|
1386
1424
|
}
|
|
1387
1425
|
console.log('');
|
|
1388
1426
|
// Get taskId from session for tracking (remote mode only)
|
|
@@ -1393,7 +1431,7 @@ export class TaskTool {
|
|
|
1393
1431
|
const session = getSingletonSession();
|
|
1394
1432
|
taskId = session?.getTaskId() || null;
|
|
1395
1433
|
}
|
|
1396
|
-
catch
|
|
1434
|
+
catch {
|
|
1397
1435
|
taskId = null;
|
|
1398
1436
|
}
|
|
1399
1437
|
}
|
|
@@ -1478,6 +1516,7 @@ export class TaskTool {
|
|
|
1478
1516
|
loopIntervalInMs: 500,
|
|
1479
1517
|
showAIDebugInfo: config.get('showAIDebugInfo') || false,
|
|
1480
1518
|
indentLevel: indentLevel,
|
|
1519
|
+
sdkOutputAdapter: isSdkMode ? sdkOutputAdapter : null,
|
|
1481
1520
|
});
|
|
1482
1521
|
// Add constraints to prompt if any
|
|
1483
1522
|
const fullPrompt = prompt;
|
|
@@ -1510,7 +1549,14 @@ export class TaskTool {
|
|
|
1510
1549
|
}));
|
|
1511
1550
|
if (result.status === 'end') {
|
|
1512
1551
|
const iterations = conversationsWithoutScreenshots.filter((c) => c.from === 'human' && c.screenshotContext).length;
|
|
1513
|
-
|
|
1552
|
+
// SDK mode: use adapter output
|
|
1553
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1554
|
+
sdkOutputAdapter.outputGUIAgentComplete(description, iterations);
|
|
1555
|
+
}
|
|
1556
|
+
else {
|
|
1557
|
+
// Normal mode: console output
|
|
1558
|
+
console.log(`${indent}${colors.success(`${icons.check} GUI task completed in ${iterations} iterations`)}`);
|
|
1559
|
+
}
|
|
1514
1560
|
return {
|
|
1515
1561
|
success: true,
|
|
1516
1562
|
message: `GUI task "${description}" completed`,
|
|
@@ -1526,8 +1572,14 @@ export class TaskTool {
|
|
|
1526
1572
|
};
|
|
1527
1573
|
}
|
|
1528
1574
|
else if (result.status === 'call_llm') {
|
|
1529
|
-
//
|
|
1530
|
-
|
|
1575
|
+
// SDK mode: use adapter output
|
|
1576
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1577
|
+
sdkOutputAdapter.outputGUIAgentStatus('call_llm', conversationsWithoutScreenshots.filter((c) => c.from === 'human' && c.screenshotContext).length);
|
|
1578
|
+
}
|
|
1579
|
+
else {
|
|
1580
|
+
// Normal mode: console output
|
|
1581
|
+
console.log(`${indent}${colors.warning(`${icons.warning} GUI agent returned to main agent for LLM decision`)}`);
|
|
1582
|
+
}
|
|
1531
1583
|
return {
|
|
1532
1584
|
success: true,
|
|
1533
1585
|
message: `GUI task "${description}" returned for LLM decision`,
|
|
@@ -1543,6 +1595,10 @@ export class TaskTool {
|
|
|
1543
1595
|
};
|
|
1544
1596
|
}
|
|
1545
1597
|
else if (result.status === 'user_stopped') {
|
|
1598
|
+
// SDK mode: use adapter output
|
|
1599
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1600
|
+
sdkOutputAdapter.outputGUIAgentCancelled(description);
|
|
1601
|
+
}
|
|
1546
1602
|
return {
|
|
1547
1603
|
success: true,
|
|
1548
1604
|
message: `GUI task "${description}" stopped by user`,
|
|
@@ -1560,6 +1616,10 @@ export class TaskTool {
|
|
|
1560
1616
|
else {
|
|
1561
1617
|
// status is 'error' or other non-success status
|
|
1562
1618
|
const errorMsg = result.error || 'Unknown error';
|
|
1619
|
+
// SDK mode: use adapter output
|
|
1620
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1621
|
+
sdkOutputAdapter.outputGUIAgentError(description, errorMsg);
|
|
1622
|
+
}
|
|
1563
1623
|
return {
|
|
1564
1624
|
success: false,
|
|
1565
1625
|
message: `GUI task "${description}" failed: ${errorMsg}`,
|
|
@@ -1583,6 +1643,10 @@ export class TaskTool {
|
|
|
1583
1643
|
// If the user cancelled the task, ignore any API errors (like 429)
|
|
1584
1644
|
// and return cancelled status instead
|
|
1585
1645
|
if (cancelled || cancellationManager.isOperationCancelled()) {
|
|
1646
|
+
// SDK mode: use adapter output
|
|
1647
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1648
|
+
sdkOutputAdapter.outputGUIAgentCancelled(description);
|
|
1649
|
+
}
|
|
1586
1650
|
return {
|
|
1587
1651
|
success: true,
|
|
1588
1652
|
cancelled: true, // Mark as cancelled so main agent won't continue
|
|
@@ -1591,6 +1655,10 @@ export class TaskTool {
|
|
|
1591
1655
|
};
|
|
1592
1656
|
}
|
|
1593
1657
|
if (error.message === 'Operation cancelled by user') {
|
|
1658
|
+
// SDK mode: use adapter output
|
|
1659
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1660
|
+
sdkOutputAdapter.outputGUIAgentCancelled(description);
|
|
1661
|
+
}
|
|
1594
1662
|
return {
|
|
1595
1663
|
success: true,
|
|
1596
1664
|
message: `GUI task "${description}" cancelled by user`,
|
|
@@ -1598,17 +1666,35 @@ export class TaskTool {
|
|
|
1598
1666
|
};
|
|
1599
1667
|
}
|
|
1600
1668
|
// Return failure without throwing - let the main agent handle it
|
|
1669
|
+
// SDK mode: use adapter output
|
|
1670
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1671
|
+
sdkOutputAdapter.outputGUIAgentError(description, error.message);
|
|
1672
|
+
}
|
|
1601
1673
|
return {
|
|
1602
1674
|
success: false,
|
|
1603
1675
|
message: `GUI task "${description}" failed: ${error.message}`,
|
|
1604
1676
|
};
|
|
1605
1677
|
}
|
|
1606
1678
|
}
|
|
1607
|
-
async executeSingleAgent(subagent_type, prompt, description, useContext, constraints, mode, agentManager, toolRegistry,
|
|
1679
|
+
async executeSingleAgent(subagent_type, prompt, description, useContext, constraints, mode, agentManager, toolRegistry, config, indentLevel = 1) {
|
|
1608
1680
|
const agent = agentManager.getAgent(subagent_type);
|
|
1609
1681
|
if (!agent) {
|
|
1610
1682
|
throw new Error(`Agent ${subagent_type} not found`);
|
|
1611
1683
|
}
|
|
1684
|
+
// Get SDK adapter from session for subagent output
|
|
1685
|
+
let sdkOutputAdapter = null;
|
|
1686
|
+
let isSdkMode = false;
|
|
1687
|
+
try {
|
|
1688
|
+
const { getSingletonSession } = await import('./session.js');
|
|
1689
|
+
const session = getSingletonSession();
|
|
1690
|
+
if (session) {
|
|
1691
|
+
isSdkMode = session.isSdkMode;
|
|
1692
|
+
sdkOutputAdapter = session.sdkOutputAdapter;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
catch {
|
|
1696
|
+
// Session not available
|
|
1697
|
+
}
|
|
1612
1698
|
// Special handling for gui-subagent: directly call GUIAgent.run() instead of subagent message loop
|
|
1613
1699
|
if (subagent_type === 'gui-subagent') {
|
|
1614
1700
|
// Get RemoteAIClient instance from session (if available)
|
|
@@ -1620,7 +1706,7 @@ export class TaskTool {
|
|
|
1620
1706
|
remoteAIClient = session.getRemoteAIClient();
|
|
1621
1707
|
}
|
|
1622
1708
|
}
|
|
1623
|
-
catch
|
|
1709
|
+
catch {
|
|
1624
1710
|
// Session not available, keep undefined
|
|
1625
1711
|
remoteAIClient = undefined;
|
|
1626
1712
|
}
|
|
@@ -1649,19 +1735,21 @@ export class TaskTool {
|
|
|
1649
1735
|
modelName = agent.model;
|
|
1650
1736
|
}
|
|
1651
1737
|
}
|
|
1652
|
-
// Create AI client for this subagent
|
|
1738
|
+
// Create AI client for this subagent - each subagent gets its own independent client
|
|
1653
1739
|
let subAgentClient;
|
|
1654
1740
|
let isRemoteMode = false;
|
|
1655
1741
|
let mainTaskId = null;
|
|
1656
1742
|
const authConfig = config.getAuthConfig();
|
|
1657
1743
|
if (authConfig.type === AuthType.OAUTH_XAGENT) {
|
|
1658
|
-
// Remote mode:
|
|
1744
|
+
// Remote mode: create independent RemoteAIClient for each subagent
|
|
1745
|
+
// This prevents message queue conflicts when multiple subagents run in parallel
|
|
1659
1746
|
const session = getSingletonSession();
|
|
1660
|
-
const
|
|
1661
|
-
if (
|
|
1662
|
-
|
|
1747
|
+
const remoteAIClient = session?.getRemoteAIClient();
|
|
1748
|
+
if (remoteAIClient) {
|
|
1749
|
+
// Clone or create independent client for this subagent
|
|
1750
|
+
// RemoteAIClient should be designed to handle concurrent requests
|
|
1751
|
+
subAgentClient = remoteAIClient;
|
|
1663
1752
|
isRemoteMode = true;
|
|
1664
|
-
// Get the main taskId from session - subagent shares the same taskId as the parent task
|
|
1665
1753
|
mainTaskId = session?.getTaskId() || null;
|
|
1666
1754
|
}
|
|
1667
1755
|
else {
|
|
@@ -1681,7 +1769,7 @@ export class TaskTool {
|
|
|
1681
1769
|
subAgentClient = createAIClient(subAuthConfig);
|
|
1682
1770
|
}
|
|
1683
1771
|
const indent = ' '.repeat(indentLevel);
|
|
1684
|
-
const
|
|
1772
|
+
const _indentNext = ' '.repeat(indentLevel + 1);
|
|
1685
1773
|
const agentName = agent.name || subagent_type;
|
|
1686
1774
|
// Track execution history for better reporting to main agent
|
|
1687
1775
|
const executionHistory = [];
|
|
@@ -1729,7 +1817,7 @@ export class TaskTool {
|
|
|
1729
1817
|
}
|
|
1730
1818
|
}
|
|
1731
1819
|
}
|
|
1732
|
-
catch
|
|
1820
|
+
catch {
|
|
1733
1821
|
// Ignore polling errors
|
|
1734
1822
|
}
|
|
1735
1823
|
}, 10);
|
|
@@ -1756,7 +1844,7 @@ export class TaskTool {
|
|
|
1756
1844
|
throw new Error('Operation cancelled by user');
|
|
1757
1845
|
}
|
|
1758
1846
|
};
|
|
1759
|
-
|
|
1847
|
+
const messages = [
|
|
1760
1848
|
{ role: 'system', content: enhancedSystemPrompt },
|
|
1761
1849
|
{ role: 'user', content: fullPrompt },
|
|
1762
1850
|
];
|
|
@@ -1777,8 +1865,10 @@ export class TaskTool {
|
|
|
1777
1865
|
};
|
|
1778
1866
|
});
|
|
1779
1867
|
let iteration = 0;
|
|
1780
|
-
|
|
1781
|
-
|
|
1868
|
+
let lastContentStr = ''; // Track last content for final result
|
|
1869
|
+
// Main agent style loop: continue until AI returns no more tool_calls
|
|
1870
|
+
// eslint-disable-next-line no-constant-condition
|
|
1871
|
+
while (true) {
|
|
1782
1872
|
iteration++;
|
|
1783
1873
|
// Check for cancellation before each iteration
|
|
1784
1874
|
checkCancellation();
|
|
@@ -1843,134 +1933,66 @@ export class TaskTool {
|
|
|
1843
1933
|
messages.push(assistantMessage);
|
|
1844
1934
|
// Display reasoning content if present
|
|
1845
1935
|
if (reasoningContent) {
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1936
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1937
|
+
sdkOutputAdapter.outputThinking(reasoningContent, 'compact');
|
|
1938
|
+
}
|
|
1939
|
+
else {
|
|
1940
|
+
console.log(`\n${indent}${colors.textDim(`${icons.brain} Thinking Process:`)}`);
|
|
1941
|
+
const truncatedReasoning = reasoningContent.length > 500
|
|
1942
|
+
? reasoningContent.substring(0, 500) + '...'
|
|
1943
|
+
: reasoningContent;
|
|
1944
|
+
const indentedReasoning = indentMultiline(truncatedReasoning, indent);
|
|
1945
|
+
console.log(`${indentedReasoning}\n`);
|
|
1946
|
+
}
|
|
1852
1947
|
}
|
|
1853
1948
|
// Display assistant response (if there's any text content) with proper indentation
|
|
1854
1949
|
if (contentStr) {
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1950
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1951
|
+
sdkOutputAdapter.outputAssistant(contentStr);
|
|
1952
|
+
}
|
|
1953
|
+
else {
|
|
1954
|
+
console.log(`\n${indent}${colors.primaryBright(agentName)}: ${description}`);
|
|
1955
|
+
const truncatedContent = contentStr.length > 500 ? contentStr.substring(0, 500) + '...' : contentStr;
|
|
1956
|
+
const indentedContent = indentMultiline(truncatedContent, indent);
|
|
1957
|
+
console.log(`${indentedContent}\n`);
|
|
1958
|
+
}
|
|
1859
1959
|
}
|
|
1860
|
-
// Process tool calls
|
|
1960
|
+
// Process tool calls in parallel (照搬 session 的实现)
|
|
1861
1961
|
if (toolCalls && toolCalls.length > 0) {
|
|
1862
|
-
|
|
1962
|
+
// Prepare all tool calls with their indices
|
|
1963
|
+
const preparedToolCalls = toolCalls.map((toolCall, index) => {
|
|
1863
1964
|
const { name, arguments: params } = toolCall.function;
|
|
1864
1965
|
let parsedParams;
|
|
1865
1966
|
try {
|
|
1866
1967
|
parsedParams = typeof params === 'string' ? JSON.parse(params) : params;
|
|
1867
1968
|
}
|
|
1868
|
-
catch
|
|
1969
|
+
catch {
|
|
1869
1970
|
parsedParams = params;
|
|
1870
1971
|
}
|
|
1871
|
-
|
|
1972
|
+
return { name, params: parsedParams, id: toolCall.id, index };
|
|
1973
|
+
});
|
|
1974
|
+
// Display all tool call info first
|
|
1975
|
+
for (const tc of preparedToolCalls) {
|
|
1976
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1977
|
+
sdkOutputAdapter.outputToolStart(tc.name, tc.params);
|
|
1978
|
+
}
|
|
1979
|
+
else {
|
|
1980
|
+
console.log(`${indent}${colors.textMuted(`${icons.loading} Tool: ${tc.name}`)}`);
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
// Execute all tool calls in parallel
|
|
1984
|
+
const executePromises = preparedToolCalls.map(async (tc) => {
|
|
1872
1985
|
try {
|
|
1873
1986
|
// Check cancellation before tool execution
|
|
1874
1987
|
checkCancellation();
|
|
1875
|
-
const toolResult = await cancellationManager.withCancellation(toolRegistry.execute(name,
|
|
1876
|
-
|
|
1877
|
-
const showToolDetails = config.get('showToolDetails') || false;
|
|
1878
|
-
// Prepare result preview for history
|
|
1879
|
-
const resultPreview = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult, null, 2);
|
|
1880
|
-
const truncatedPreview = resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
|
|
1881
|
-
// Special handling for different tools (consistent with session.ts display logic)
|
|
1882
|
-
const isTodoTool = name === 'todo_write' || name === 'todo_read';
|
|
1883
|
-
const isEditTool = name === 'Edit';
|
|
1884
|
-
const isWriteTool = name === 'Write';
|
|
1885
|
-
const isDeleteTool = name === 'DeleteFile';
|
|
1886
|
-
const hasDiff = isEditTool && toolResult?.diff;
|
|
1887
|
-
const hasFilePreview = isWriteTool && toolResult?.preview;
|
|
1888
|
-
const hasDeleteInfo = isDeleteTool && toolResult?.filePath;
|
|
1889
|
-
// Import render functions for consistent display
|
|
1890
|
-
const { renderDiff, renderLines } = await import('./theme.js');
|
|
1891
|
-
if (isTodoTool) {
|
|
1892
|
-
// Display todo list
|
|
1893
|
-
console.log(`${indent}${colors.success(`${icons.check} Todo List:`)}`);
|
|
1894
|
-
const todos = toolResult?.todos || [];
|
|
1895
|
-
if (todos.length === 0) {
|
|
1896
|
-
console.log(`${indent} ${colors.textMuted('No tasks')}`);
|
|
1897
|
-
}
|
|
1898
|
-
else {
|
|
1899
|
-
const statusConfig = {
|
|
1900
|
-
pending: { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
|
|
1901
|
-
in_progress: { icon: icons.loading, color: colors.warning, label: 'In Progress' },
|
|
1902
|
-
completed: { icon: icons.success, color: colors.success, label: 'Completed' },
|
|
1903
|
-
failed: { icon: icons.error, color: colors.error, label: 'Failed' },
|
|
1904
|
-
};
|
|
1905
|
-
for (const todo of todos) {
|
|
1906
|
-
const status = statusConfig[todo.status] || statusConfig['pending'];
|
|
1907
|
-
console.log(`${indent} ${status.color(status.icon)} ${status.color(status.label)}: ${colors.text(todo.task)}`);
|
|
1908
|
-
}
|
|
1909
|
-
}
|
|
1910
|
-
if (toolResult?.message) {
|
|
1911
|
-
console.log(`${indent}${colors.textDim(toolResult.message)}`);
|
|
1912
|
-
}
|
|
1913
|
-
console.log('');
|
|
1914
|
-
}
|
|
1915
|
-
else if (hasDiff) {
|
|
1916
|
-
// Display edit result with diff
|
|
1917
|
-
console.log('');
|
|
1918
|
-
const diffOutput = renderDiff(toolResult.diff);
|
|
1919
|
-
const indentedDiff = diffOutput
|
|
1920
|
-
.split('\n')
|
|
1921
|
-
.map((line) => `${indent} ${line}`)
|
|
1922
|
-
.join('\n');
|
|
1923
|
-
console.log(`${indentedDiff}\n`);
|
|
1924
|
-
}
|
|
1925
|
-
else if (hasFilePreview) {
|
|
1926
|
-
// Display new file content in preview style
|
|
1927
|
-
console.log('');
|
|
1928
|
-
console.log(`${indent}${colors.success(`${icons.file} ${toolResult.filePath}`)}`);
|
|
1929
|
-
console.log(`${indent}${colors.textDim(` ${toolResult.lineCount} lines`)}`);
|
|
1930
|
-
console.log('');
|
|
1931
|
-
console.log(renderLines(toolResult.preview, { maxLines: 10, indent: indent + ' ' }));
|
|
1932
|
-
console.log('');
|
|
1933
|
-
}
|
|
1934
|
-
else if (hasDeleteInfo) {
|
|
1935
|
-
// Display DeleteFile result
|
|
1936
|
-
console.log('');
|
|
1937
|
-
console.log(`${indent}${colors.success(`${icons.check} Deleted: ${toolResult.filePath}`)}`);
|
|
1938
|
-
console.log('');
|
|
1939
|
-
}
|
|
1940
|
-
else if (showToolDetails) {
|
|
1941
|
-
// Show full result details
|
|
1942
|
-
const indentedPreview = indentMultiline(resultPreview, indent);
|
|
1943
|
-
console.log(`${indent}${colors.success(`${icons.check} Tool Result:`)}\n${indentedPreview}\n`);
|
|
1944
|
-
}
|
|
1945
|
-
else if (toolResult && toolResult.success === false) {
|
|
1946
|
-
// Tool failed
|
|
1947
|
-
console.log(`${indent}${colors.error(`${icons.cross} ${toolResult.message || 'Failed'}`)}\n`);
|
|
1948
|
-
}
|
|
1949
|
-
else if (toolResult) {
|
|
1950
|
-
// Show brief preview by default
|
|
1951
|
-
const indentedPreview = indentMultiline(truncatedPreview, indent);
|
|
1952
|
-
console.log(`${indent}${colors.success(`${icons.check} Completed`)}\n${indentedPreview}\n`);
|
|
1953
|
-
}
|
|
1954
|
-
else {
|
|
1955
|
-
console.log(`${indent}${colors.textDim('(no result)')}\n`);
|
|
1956
|
-
}
|
|
1957
|
-
// Record successful tool execution in history (use truncated preview to save memory)
|
|
1958
|
-
executionHistory.push({
|
|
1959
|
-
tool: name,
|
|
1960
|
-
status: 'success',
|
|
1961
|
-
params: parsedParams,
|
|
1962
|
-
result: truncatedPreview,
|
|
1963
|
-
timestamp: new Date().toISOString(),
|
|
1964
|
-
});
|
|
1965
|
-
messages.push({
|
|
1966
|
-
role: 'tool',
|
|
1967
|
-
content: JSON.stringify(toolResult),
|
|
1968
|
-
tool_call_id: toolCall.id,
|
|
1969
|
-
});
|
|
1988
|
+
const toolResult = await cancellationManager.withCancellation(toolRegistry.execute(tc.name, tc.params, mode, indent), `subagent-${subagent_type}-${tc.name}-${iteration}`);
|
|
1989
|
+
return { ...tc, toolResult, error: undefined };
|
|
1970
1990
|
}
|
|
1971
1991
|
catch (error) {
|
|
1972
1992
|
if (error.message === 'Operation cancelled by user') {
|
|
1973
|
-
|
|
1993
|
+
if (!isSdkMode || !sdkOutputAdapter) {
|
|
1994
|
+
console.log(`${indent}${colors.warning(`⚠️ Operation cancelled`)}\n`);
|
|
1995
|
+
}
|
|
1974
1996
|
cancellationManager.off('cancelled', cancelHandler);
|
|
1975
1997
|
cleanupStdinPolling();
|
|
1976
1998
|
const summaryPreview = contentStr.length > 300 ? contentStr.substring(0, 300) + '...' : contentStr;
|
|
@@ -1990,56 +2012,174 @@ export class TaskTool {
|
|
|
1990
2012
|
},
|
|
1991
2013
|
};
|
|
1992
2014
|
}
|
|
1993
|
-
|
|
2015
|
+
return { ...tc, toolResult: undefined, error: error.message };
|
|
2016
|
+
}
|
|
2017
|
+
});
|
|
2018
|
+
const settledResults = await Promise.all(executePromises);
|
|
2019
|
+
// Check for cancellation in results
|
|
2020
|
+
const cancellationResult = settledResults.find((r) => 'success' in r && r.success === false);
|
|
2021
|
+
if (cancellationResult) {
|
|
2022
|
+
return cancellationResult;
|
|
2023
|
+
}
|
|
2024
|
+
const resultsByIndex = new Map();
|
|
2025
|
+
const usedIndices = new Set();
|
|
2026
|
+
for (const result of settledResults) {
|
|
2027
|
+
// Find the first unused original index that matches the tool name
|
|
2028
|
+
const originalIndex = preparedToolCalls.findIndex((tc, idx) => tc.name === result.name && !usedIndices.has(idx));
|
|
2029
|
+
if (originalIndex !== -1) {
|
|
2030
|
+
usedIndices.add(originalIndex);
|
|
2031
|
+
resultsByIndex.set(originalIndex, result);
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
// Import render functions for consistent display
|
|
2035
|
+
const { renderDiff, renderLines } = await import('./theme.js');
|
|
2036
|
+
// Process results in the original tool_calls order
|
|
2037
|
+
for (let i = 0; i < preparedToolCalls.length; i++) {
|
|
2038
|
+
const result = resultsByIndex.get(i);
|
|
2039
|
+
if (!result)
|
|
2040
|
+
continue;
|
|
2041
|
+
const { name, params: parsedParams, toolResult, error } = result;
|
|
2042
|
+
// Get showToolDetails config to control result display
|
|
2043
|
+
const showToolDetails = config.get('showToolDetails') || false;
|
|
2044
|
+
// Prepare result preview for history
|
|
2045
|
+
const resultPreview = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult, null, 2);
|
|
2046
|
+
const truncatedPreview = resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
|
|
2047
|
+
if (error) {
|
|
2048
|
+
// Handle error case
|
|
2049
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2050
|
+
sdkOutputAdapter.outputToolError(name, error);
|
|
2051
|
+
}
|
|
2052
|
+
else {
|
|
2053
|
+
console.log(`${indent}${colors.error(`${icons.cross} Error:`)} ${error}\n`);
|
|
2054
|
+
}
|
|
1994
2055
|
// Record failed tool execution in history
|
|
1995
2056
|
executionHistory.push({
|
|
1996
2057
|
tool: name,
|
|
1997
2058
|
status: 'error',
|
|
1998
2059
|
params: parsedParams,
|
|
1999
|
-
error
|
|
2060
|
+
error,
|
|
2000
2061
|
timestamp: new Date().toISOString(),
|
|
2001
2062
|
});
|
|
2002
2063
|
messages.push({
|
|
2003
2064
|
role: 'tool',
|
|
2004
|
-
content: JSON.stringify({ error
|
|
2005
|
-
tool_call_id:
|
|
2065
|
+
content: JSON.stringify({ error }),
|
|
2066
|
+
tool_call_id: result.id,
|
|
2067
|
+
});
|
|
2068
|
+
}
|
|
2069
|
+
else {
|
|
2070
|
+
// Handle success case - display result
|
|
2071
|
+
// SDK mode: output tool result via adapter
|
|
2072
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2073
|
+
sdkOutputAdapter.outputToolResult(name, toolResult);
|
|
2074
|
+
}
|
|
2075
|
+
// Normal mode: console output (SDK mode already output via adapter above)
|
|
2076
|
+
if (!isSdkMode || !sdkOutputAdapter) {
|
|
2077
|
+
// Special handling for different tools (consistent with session.ts display logic)
|
|
2078
|
+
const isTodoTool = name === 'todo_write' || name === 'todo_read';
|
|
2079
|
+
const isEditTool = name === 'Edit';
|
|
2080
|
+
const isWriteTool = name === 'Write';
|
|
2081
|
+
const isDeleteTool = name === 'DeleteFile';
|
|
2082
|
+
const hasDiff = isEditTool && toolResult?.diff;
|
|
2083
|
+
const hasFilePreview = isWriteTool && toolResult?.preview;
|
|
2084
|
+
const hasDeleteInfo = isDeleteTool && toolResult?.filePath;
|
|
2085
|
+
if (isTodoTool) {
|
|
2086
|
+
// Display todo list
|
|
2087
|
+
console.log(`${indent}${colors.success(`${icons.check} Todo List:`)}`);
|
|
2088
|
+
const todos = toolResult?.todos || [];
|
|
2089
|
+
if (todos.length === 0) {
|
|
2090
|
+
console.log(`${indent} ${colors.textMuted('No tasks')}`);
|
|
2091
|
+
}
|
|
2092
|
+
else {
|
|
2093
|
+
const statusConfig = {
|
|
2094
|
+
pending: { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
|
|
2095
|
+
in_progress: { icon: icons.loading, color: colors.warning, label: 'In Progress' },
|
|
2096
|
+
completed: { icon: icons.success, color: colors.success, label: 'Completed' },
|
|
2097
|
+
failed: { icon: icons.error, color: colors.error, label: 'Failed' },
|
|
2098
|
+
};
|
|
2099
|
+
for (const todo of todos) {
|
|
2100
|
+
const status = statusConfig[todo.status] || statusConfig['pending'];
|
|
2101
|
+
console.log(`${indent} ${status.color(status.icon)} ${status.color(status.label)}: ${colors.text(todo.task)}`);
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
if (toolResult?.message) {
|
|
2105
|
+
console.log(`${indent}${colors.textDim(toolResult.message)}`);
|
|
2106
|
+
}
|
|
2107
|
+
console.log('');
|
|
2108
|
+
}
|
|
2109
|
+
else if (hasDiff) {
|
|
2110
|
+
// Display edit result with diff
|
|
2111
|
+
console.log('');
|
|
2112
|
+
const diffOutput = renderDiff(toolResult.diff);
|
|
2113
|
+
const indentedDiff = diffOutput
|
|
2114
|
+
.split('\n')
|
|
2115
|
+
.map((line) => `${indent} ${line}`)
|
|
2116
|
+
.join('\n');
|
|
2117
|
+
console.log(`${indentedDiff}\n`);
|
|
2118
|
+
}
|
|
2119
|
+
else if (hasFilePreview) {
|
|
2120
|
+
// Display new file content in preview style
|
|
2121
|
+
console.log('');
|
|
2122
|
+
console.log(`${indent}${colors.success(`${icons.file} ${toolResult.filePath}`)}`);
|
|
2123
|
+
console.log(`${indent}${colors.textDim(` ${toolResult.lineCount} lines`)}`);
|
|
2124
|
+
console.log('');
|
|
2125
|
+
console.log(renderLines(toolResult.preview, { maxLines: 10, indent: indent + ' ' }));
|
|
2126
|
+
console.log('');
|
|
2127
|
+
}
|
|
2128
|
+
else if (hasDeleteInfo) {
|
|
2129
|
+
// Display DeleteFile result
|
|
2130
|
+
console.log('');
|
|
2131
|
+
console.log(`${indent}${colors.success(`${icons.check} Deleted: ${toolResult.filePath}`)}`);
|
|
2132
|
+
console.log('');
|
|
2133
|
+
}
|
|
2134
|
+
else if (showToolDetails) {
|
|
2135
|
+
// Show full result details
|
|
2136
|
+
const indentedPreview = indentMultiline(resultPreview, indent);
|
|
2137
|
+
console.log(`${indent}${colors.success(`${icons.check} Tool Result:`)}\n${indentedPreview}\n`);
|
|
2138
|
+
}
|
|
2139
|
+
else if (toolResult && toolResult.success === false) {
|
|
2140
|
+
// Tool failed
|
|
2141
|
+
console.log(`${indent}${colors.error(`${icons.cross} ${toolResult.message || 'Failed'}`)}\n`);
|
|
2142
|
+
}
|
|
2143
|
+
else if (toolResult) {
|
|
2144
|
+
// Show brief preview by default
|
|
2145
|
+
const indentedPreview = indentMultiline(truncatedPreview, indent);
|
|
2146
|
+
console.log(`${indent}${colors.success(`${icons.check} Completed`)}\n${indentedPreview}\n`);
|
|
2147
|
+
}
|
|
2148
|
+
else {
|
|
2149
|
+
console.log(`${indent}${colors.textDim('(no result)')}\n`);
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
// Record successful tool execution in history (use truncated preview to save memory)
|
|
2153
|
+
executionHistory.push({
|
|
2154
|
+
tool: name,
|
|
2155
|
+
status: 'success',
|
|
2156
|
+
params: parsedParams,
|
|
2157
|
+
result: truncatedPreview,
|
|
2158
|
+
timestamp: new Date().toISOString(),
|
|
2159
|
+
});
|
|
2160
|
+
messages.push({
|
|
2161
|
+
role: 'tool',
|
|
2162
|
+
content: JSON.stringify(toolResult),
|
|
2163
|
+
tool_call_id: result.id,
|
|
2006
2164
|
});
|
|
2007
2165
|
}
|
|
2008
2166
|
}
|
|
2009
|
-
|
|
2167
|
+
if (!isSdkMode || !sdkOutputAdapter) {
|
|
2168
|
+
console.log('');
|
|
2169
|
+
}
|
|
2010
2170
|
continue; // Continue to next iteration to get final response
|
|
2011
2171
|
}
|
|
2012
|
-
// No more tool calls
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
const summaryPreview = contentStr.length > 300 ? contentStr.substring(0, 300) + '...' : contentStr;
|
|
2016
|
-
return {
|
|
2017
|
-
success: true,
|
|
2018
|
-
message: `Task "${description}" completed by ${subagent_type}`,
|
|
2019
|
-
result: {
|
|
2020
|
-
summary: summaryPreview,
|
|
2021
|
-
executionHistory: {
|
|
2022
|
-
totalIterations: iteration,
|
|
2023
|
-
toolsExecuted: executionHistory.length,
|
|
2024
|
-
successfulTools: executionHistory.filter((t) => t.status === 'success').length,
|
|
2025
|
-
failedTools: executionHistory.filter((t) => t.status === 'error').length,
|
|
2026
|
-
history: executionHistory,
|
|
2027
|
-
},
|
|
2028
|
-
},
|
|
2029
|
-
};
|
|
2172
|
+
// No more tool calls - break loop (same as main agent)
|
|
2173
|
+
lastContentStr = contentStr || '';
|
|
2174
|
+
break;
|
|
2030
2175
|
}
|
|
2031
|
-
//
|
|
2032
|
-
// Get the last assistant message content
|
|
2033
|
-
const lastAssistantMsg = messages.filter((m) => m.role === 'assistant').pop();
|
|
2034
|
-
const lastContentStr = typeof lastAssistantMsg?.content === 'string'
|
|
2035
|
-
? lastAssistantMsg.content
|
|
2036
|
-
: JSON.stringify(lastAssistantMsg?.content || '');
|
|
2176
|
+
// Loop ended - return result (same as main agent pattern)
|
|
2037
2177
|
cancellationManager.off('cancelled', cancelHandler);
|
|
2038
2178
|
cleanupStdinPolling();
|
|
2039
2179
|
const summaryPreview = lastContentStr.length > 300 ? lastContentStr.substring(0, 300) + '...' : lastContentStr;
|
|
2040
2180
|
return {
|
|
2041
2181
|
success: true,
|
|
2042
|
-
message: `Task "${description}" completed
|
|
2182
|
+
message: `Task "${description}" completed by ${subagent_type}`,
|
|
2043
2183
|
result: {
|
|
2044
2184
|
summary: summaryPreview,
|
|
2045
2185
|
executionHistory: {
|
|
@@ -2048,12 +2188,11 @@ export class TaskTool {
|
|
|
2048
2188
|
successfulTools: executionHistory.filter((t) => t.status === 'success').length,
|
|
2049
2189
|
failedTools: executionHistory.filter((t) => t.status === 'error').length,
|
|
2050
2190
|
history: executionHistory,
|
|
2051
|
-
maxIterationsReached: true,
|
|
2052
2191
|
},
|
|
2053
2192
|
},
|
|
2054
2193
|
};
|
|
2055
2194
|
}
|
|
2056
|
-
async executeParallelAgents(agents, description, mode, agentManager, toolRegistry,
|
|
2195
|
+
async executeParallelAgents(agents, description, mode, agentManager, toolRegistry, config, indentLevel = 1) {
|
|
2057
2196
|
const indent = ' '.repeat(indentLevel);
|
|
2058
2197
|
const indentNext = ' '.repeat(indentLevel + 1);
|
|
2059
2198
|
const cancellationManager = getCancellationManager();
|
|
@@ -2086,7 +2225,7 @@ export class TaskTool {
|
|
|
2086
2225
|
}
|
|
2087
2226
|
}
|
|
2088
2227
|
}
|
|
2089
|
-
catch
|
|
2228
|
+
catch {
|
|
2090
2229
|
// Ignore polling errors
|
|
2091
2230
|
}
|
|
2092
2231
|
}, 10);
|
|
@@ -2108,7 +2247,7 @@ export class TaskTool {
|
|
|
2108
2247
|
cancellationManager.on('cancelled', cancelHandler);
|
|
2109
2248
|
console.log(`\n${indent}${colors.accent('◆')} ${colors.primaryBright('Parallel Agents')}: ${agents.length} running...`);
|
|
2110
2249
|
const startTime = Date.now();
|
|
2111
|
-
const agentPromises = agents.map(async (agentTask,
|
|
2250
|
+
const agentPromises = agents.map(async (agentTask, _index) => {
|
|
2112
2251
|
// Check if cancelled
|
|
2113
2252
|
if (cancelled || cancellationManager.isOperationCancelled()) {
|
|
2114
2253
|
return {
|
|
@@ -2119,7 +2258,7 @@ export class TaskTool {
|
|
|
2119
2258
|
};
|
|
2120
2259
|
}
|
|
2121
2260
|
try {
|
|
2122
|
-
const result = await this.executeSingleAgent(agentTask.subagent_type, agentTask.prompt, agentTask.description, agentTask.useContext ?? true, agentTask.constraints || [], mode, agentManager, toolRegistry,
|
|
2261
|
+
const result = await this.executeSingleAgent(agentTask.subagent_type, agentTask.prompt, agentTask.description, agentTask.useContext ?? true, agentTask.constraints || [], mode, agentManager, toolRegistry, config, indentLevel + 1);
|
|
2123
2262
|
return {
|
|
2124
2263
|
success: true,
|
|
2125
2264
|
agent: agentTask.subagent_type,
|
|
@@ -2329,6 +2468,19 @@ export class AskUserQuestionTool {
|
|
|
2329
2468
|
ExecutionMode.SMART,
|
|
2330
2469
|
];
|
|
2331
2470
|
async execute(params) {
|
|
2471
|
+
// Check if in SDK mode
|
|
2472
|
+
const sdkMode = this._sdkMode;
|
|
2473
|
+
const sdkAdapter = this._sdkOutputAdapter;
|
|
2474
|
+
if (sdkMode && sdkAdapter) {
|
|
2475
|
+
return this.executeSdk(params, sdkAdapter);
|
|
2476
|
+
}
|
|
2477
|
+
// Regular TUI mode
|
|
2478
|
+
return this.executeTui(params);
|
|
2479
|
+
}
|
|
2480
|
+
/**
|
|
2481
|
+
* Execute in TUI mode using @clack/prompts
|
|
2482
|
+
*/
|
|
2483
|
+
async executeTui(params) {
|
|
2332
2484
|
const { questions } = params;
|
|
2333
2485
|
try {
|
|
2334
2486
|
if (questions.length === 0 || questions.length > 4) {
|
|
@@ -2357,6 +2509,38 @@ export class AskUserQuestionTool {
|
|
|
2357
2509
|
throw new Error(`Failed to ask user questions: ${error.message}`);
|
|
2358
2510
|
}
|
|
2359
2511
|
}
|
|
2512
|
+
/**
|
|
2513
|
+
* Execute in SDK mode - output question request and wait for response
|
|
2514
|
+
*/
|
|
2515
|
+
async executeSdk(params, sdkAdapter) {
|
|
2516
|
+
const { questions } = params;
|
|
2517
|
+
if (questions.length === 0 || questions.length > 4) {
|
|
2518
|
+
throw new Error('Must provide 1-4 questions');
|
|
2519
|
+
}
|
|
2520
|
+
const requestId = `question_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
2521
|
+
// Output question request through SDK adapter
|
|
2522
|
+
sdkAdapter.outputQuestionRequest({
|
|
2523
|
+
requestId,
|
|
2524
|
+
questions
|
|
2525
|
+
});
|
|
2526
|
+
// Wait for SDK question response
|
|
2527
|
+
// The response will be handled by session.ts which has access to the SDK input
|
|
2528
|
+
// For now, we use a polling mechanism or wait for a specific event
|
|
2529
|
+
try {
|
|
2530
|
+
// Import the session to get response handling
|
|
2531
|
+
const { getSingletonSession } = await import('./session.js');
|
|
2532
|
+
const session = getSingletonSession();
|
|
2533
|
+
if (!session) {
|
|
2534
|
+
throw new Error('SDK session not available');
|
|
2535
|
+
}
|
|
2536
|
+
const answers = await session.waitForQuestionResponse(requestId);
|
|
2537
|
+
sdkAdapter.outputQuestionResponse(requestId, answers);
|
|
2538
|
+
return { answers };
|
|
2539
|
+
}
|
|
2540
|
+
catch (error) {
|
|
2541
|
+
throw new Error(`Failed to get SDK question response: ${error.message}`);
|
|
2542
|
+
}
|
|
2543
|
+
}
|
|
2360
2544
|
}
|
|
2361
2545
|
export class SaveMemoryTool {
|
|
2362
2546
|
name = 'save_memory';
|
|
@@ -2696,7 +2880,6 @@ export class InvokeSkillTool {
|
|
|
2696
2880
|
const { skillId, taskDescription, inputFile, outputFile, options } = params;
|
|
2697
2881
|
try {
|
|
2698
2882
|
const { getSkillInvoker } = await import('./skill-invoker.js');
|
|
2699
|
-
const { SkillExecutionParams } = (await import('./skill-invoker.js'));
|
|
2700
2883
|
const skillInvoker = getSkillInvoker();
|
|
2701
2884
|
await skillInvoker.initialize();
|
|
2702
2885
|
// Verify skill exists
|
|
@@ -2827,6 +3010,8 @@ export class ToolRegistry {
|
|
|
2827
3010
|
tools = new Map();
|
|
2828
3011
|
todoWriteTool;
|
|
2829
3012
|
backgroundTasks = new Map();
|
|
3013
|
+
_isSdkMode = false;
|
|
3014
|
+
_sdkOutputAdapter = null;
|
|
2830
3015
|
constructor() {
|
|
2831
3016
|
this.todoWriteTool = new TodoWriteTool();
|
|
2832
3017
|
this.registerDefaultTools();
|
|
@@ -2915,12 +3100,36 @@ export class ToolRegistry {
|
|
|
2915
3100
|
this.tools.set(toolName, mcpTool);
|
|
2916
3101
|
registeredCount++;
|
|
2917
3102
|
if (toolName !== originalName) {
|
|
2918
|
-
|
|
3103
|
+
// 在 SDK 模式下不输出重命名信息
|
|
3104
|
+
if (!this._isSdkMode) {
|
|
3105
|
+
console.log(`[MCP] Tool '${originalName}' renamed to '${toolName}' to avoid conflict`);
|
|
3106
|
+
}
|
|
2919
3107
|
}
|
|
2920
3108
|
}
|
|
2921
3109
|
}
|
|
2922
3110
|
if (registeredCount > 0) {
|
|
2923
|
-
|
|
3111
|
+
// 在 SDK 模式下不输出注册信息(MCP 相关输出已在 session 中处理)
|
|
3112
|
+
if (!this._isSdkMode) {
|
|
3113
|
+
console.log(`[MCP] Registered ${registeredCount} tool(s)`);
|
|
3114
|
+
}
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
/**
|
|
3118
|
+
* Set SDK mode for the tool registry.
|
|
3119
|
+
* In SDK mode, tool execution output is sent to the SDK output adapter.
|
|
3120
|
+
*/
|
|
3121
|
+
async setSdkMode(enabled, adapter) {
|
|
3122
|
+
this._isSdkMode = enabled;
|
|
3123
|
+
this._sdkOutputAdapter = adapter;
|
|
3124
|
+
// Mark all tools as SDK mode enabled
|
|
3125
|
+
for (const [, tool] of this.tools) {
|
|
3126
|
+
tool._sdkMode = enabled;
|
|
3127
|
+
tool._sdkOutputAdapter = adapter;
|
|
3128
|
+
}
|
|
3129
|
+
// Initialize SDK mode for TaskTool specifically
|
|
3130
|
+
const taskTool = this.tools.get('task');
|
|
3131
|
+
if (taskTool) {
|
|
3132
|
+
await taskTool.setSdkMode?.(enabled, adapter);
|
|
2924
3133
|
}
|
|
2925
3134
|
}
|
|
2926
3135
|
/**
|
|
@@ -3426,7 +3635,7 @@ export class ToolRegistry {
|
|
|
3426
3635
|
required: ['skill'],
|
|
3427
3636
|
};
|
|
3428
3637
|
break;
|
|
3429
|
-
default:
|
|
3638
|
+
default: {
|
|
3430
3639
|
// For MCP tools, use their inputSchema; for other unknown tools, keep empty schema
|
|
3431
3640
|
const mcpTool = tool;
|
|
3432
3641
|
if (mcpTool._isMcpTool && mcpTool.inputSchema) {
|
|
@@ -3455,6 +3664,7 @@ export class ToolRegistry {
|
|
|
3455
3664
|
required: [],
|
|
3456
3665
|
};
|
|
3457
3666
|
}
|
|
3667
|
+
}
|
|
3458
3668
|
}
|
|
3459
3669
|
return {
|
|
3460
3670
|
type: 'function',
|
|
@@ -3481,12 +3691,12 @@ export class ToolRegistry {
|
|
|
3481
3691
|
return await this.executeMCPTool(toolName, params, executionMode, indent);
|
|
3482
3692
|
}
|
|
3483
3693
|
// Try to find MCP tool with just the tool name (try each server)
|
|
3484
|
-
for (const [fullName,
|
|
3694
|
+
for (const [fullName, _tool] of allMcpTools) {
|
|
3485
3695
|
// Split only on the first __ to preserve underscores in tool names
|
|
3486
3696
|
const firstUnderscoreIndex = fullName.indexOf('__');
|
|
3487
3697
|
if (firstUnderscoreIndex === -1)
|
|
3488
3698
|
continue;
|
|
3489
|
-
const [
|
|
3699
|
+
const [_serverName, actualToolName] = [
|
|
3490
3700
|
fullName.substring(0, firstUnderscoreIndex),
|
|
3491
3701
|
fullName.substring(firstUnderscoreIndex + 2),
|
|
3492
3702
|
];
|
|
@@ -3509,6 +3719,8 @@ export class ToolRegistry {
|
|
|
3509
3719
|
if (!tool.allowedModes.includes(executionMode)) {
|
|
3510
3720
|
throw new Error(`Tool ${toolName} is not allowed in ${executionMode} mode`);
|
|
3511
3721
|
}
|
|
3722
|
+
const isSdkMode = this._isSdkMode;
|
|
3723
|
+
const sdkOutputAdapter = this._sdkOutputAdapter;
|
|
3512
3724
|
// Smart approval mode
|
|
3513
3725
|
if (executionMode === ExecutionMode.SMART) {
|
|
3514
3726
|
const debugMode = process.env.DEBUG === 'smart-approval';
|
|
@@ -3529,12 +3741,22 @@ export class ToolRegistry {
|
|
|
3529
3741
|
const isRemoteMode = authConfig.type === AuthType.OAUTH_XAGENT;
|
|
3530
3742
|
if (isRemoteMode && toolName === 'InvokeSkill') {
|
|
3531
3743
|
console.log('');
|
|
3532
|
-
|
|
3533
|
-
|
|
3744
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
3745
|
+
sdkOutputAdapter.outputInfo(`[Smart Mode] Remote mode: tool '${toolName}' auto-approved (remote LLM already approved)`);
|
|
3746
|
+
}
|
|
3747
|
+
else {
|
|
3748
|
+
console.log('');
|
|
3749
|
+
console.log(`${indent}${colors.success(`✅ [Smart Mode] Remote mode: tool '${toolName}' auto-approved (remote LLM already approved)`)}`);
|
|
3750
|
+
console.log('');
|
|
3751
|
+
}
|
|
3534
3752
|
return await cancellationManager.withCancellation(tool.execute(params, executionMode), `tool-${toolName}`);
|
|
3535
3753
|
}
|
|
3536
3754
|
const { getSmartApprovalEngine } = await import('./smart-approval.js');
|
|
3537
3755
|
const approvalEngine = getSmartApprovalEngine(debugMode);
|
|
3756
|
+
// Set SDK mode for approval engine if in SDK mode
|
|
3757
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
3758
|
+
approvalEngine.setSdkMode(true, sdkOutputAdapter);
|
|
3759
|
+
}
|
|
3538
3760
|
// Evaluate tool call
|
|
3539
3761
|
const result = await approvalEngine.evaluate({
|
|
3540
3762
|
toolName,
|
|
@@ -3544,35 +3766,56 @@ export class ToolRegistry {
|
|
|
3544
3766
|
// Decide whether to execute based on approval result
|
|
3545
3767
|
if (result.decision === 'approved') {
|
|
3546
3768
|
// Whitelist or AI approval passed, execute directly
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3769
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
3770
|
+
sdkOutputAdapter.outputInfo(`[Smart Mode] Tool '${toolName}' passed approval, executing directly`);
|
|
3771
|
+
sdkOutputAdapter.outputInfo(`Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}, Latency: ${result.latency}ms`);
|
|
3772
|
+
}
|
|
3773
|
+
else {
|
|
3774
|
+
console.log('');
|
|
3775
|
+
console.log(`${indent}${colors.success(`✅ [Smart Mode] Tool '${toolName}' passed approval, executing directly`)}`);
|
|
3776
|
+
console.log(`${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`);
|
|
3777
|
+
console.log(`${indent}${colors.textDim(` Latency: ${result.latency}ms`)}`);
|
|
3778
|
+
console.log('');
|
|
3779
|
+
}
|
|
3552
3780
|
return await cancellationManager.withCancellation(tool.execute(params, executionMode), `tool-${toolName}`);
|
|
3553
3781
|
}
|
|
3554
3782
|
else if (result.decision === 'requires_confirmation') {
|
|
3555
3783
|
// Requires user confirmation
|
|
3556
|
-
const confirmed = await approvalEngine.requestConfirmation(result);
|
|
3784
|
+
const confirmed = await approvalEngine.requestConfirmation(result, toolName, params);
|
|
3557
3785
|
if (confirmed) {
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3786
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
3787
|
+
sdkOutputAdapter.outputInfo(`[Smart Mode] User confirmed execution of tool '${toolName}'`);
|
|
3788
|
+
}
|
|
3789
|
+
else {
|
|
3790
|
+
console.log('');
|
|
3791
|
+
console.log(`${indent}${colors.success(`✅ [Smart Mode] User confirmed execution of tool '${toolName}'`)}`);
|
|
3792
|
+
console.log('');
|
|
3793
|
+
}
|
|
3561
3794
|
return await cancellationManager.withCancellation(tool.execute(params, executionMode), `tool-${toolName}`);
|
|
3562
3795
|
}
|
|
3563
3796
|
else {
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3797
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
3798
|
+
sdkOutputAdapter.outputWarning(`[Smart Mode] User cancelled execution of tool '${toolName}'`);
|
|
3799
|
+
}
|
|
3800
|
+
else {
|
|
3801
|
+
console.log('');
|
|
3802
|
+
console.log(`${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled execution of tool '${toolName}'`)}`);
|
|
3803
|
+
console.log('');
|
|
3804
|
+
}
|
|
3567
3805
|
throw new Error(`Tool execution cancelled by user: ${toolName}`);
|
|
3568
3806
|
}
|
|
3569
3807
|
}
|
|
3570
3808
|
else {
|
|
3571
3809
|
// Rejected execution
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3810
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
3811
|
+
sdkOutputAdapter.outputError(`[Smart Mode] Tool '${toolName}' execution rejected`, { reason: result.description });
|
|
3812
|
+
}
|
|
3813
|
+
else {
|
|
3814
|
+
console.log('');
|
|
3815
|
+
console.log(`${indent}${colors.error(`❌ [Smart Mode] Tool '${toolName}' execution rejected`)}`);
|
|
3816
|
+
console.log(`${indent}${colors.textDim(` Reason: ${result.description}`)}`);
|
|
3817
|
+
console.log('');
|
|
3818
|
+
}
|
|
3576
3819
|
throw new Error(`Tool execution rejected: ${toolName}`);
|
|
3577
3820
|
}
|
|
3578
3821
|
}
|
|
@@ -3592,10 +3835,14 @@ export class ToolRegistry {
|
|
|
3592
3835
|
const actualToolName = toolName.substring(firstUnderscoreIndex + 2);
|
|
3593
3836
|
// Get server info for display
|
|
3594
3837
|
const server = mcpManager.getServer(serverName);
|
|
3595
|
-
const
|
|
3838
|
+
const _serverTools = server?.getToolNames() || [];
|
|
3839
|
+
const isSdkMode = this._isSdkMode;
|
|
3840
|
+
const sdkOutputAdapter = this._sdkOutputAdapter;
|
|
3596
3841
|
// Display tool call info
|
|
3597
|
-
|
|
3598
|
-
|
|
3842
|
+
if (!isSdkMode || !sdkOutputAdapter) {
|
|
3843
|
+
console.log('');
|
|
3844
|
+
console.log(`${indent}${colors.warning(`${icons.tool} MCP Tool Call: ${serverName}::${actualToolName}`)}`);
|
|
3845
|
+
}
|
|
3599
3846
|
// Smart approval mode for MCP tools
|
|
3600
3847
|
if (executionMode === ExecutionMode.SMART) {
|
|
3601
3848
|
const debugMode = process.env.DEBUG === 'smart-approval';
|
|
@@ -3606,10 +3853,19 @@ export class ToolRegistry {
|
|
|
3606
3853
|
const isRemoteMode = authConfig.type === AuthType.OAUTH_XAGENT;
|
|
3607
3854
|
// Remote mode: remote LLM has already approved the tool, auto-approve
|
|
3608
3855
|
if (isRemoteMode) {
|
|
3609
|
-
|
|
3856
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
3857
|
+
sdkOutputAdapter.outputInfo(`[Smart Mode] Remote mode: MCP tool '${serverName}::${actualToolName}' auto-approved`);
|
|
3858
|
+
}
|
|
3859
|
+
else {
|
|
3860
|
+
console.log(`${indent}${colors.success(`✅ [Smart Mode] Remote mode: MCP tool '${serverName}::${actualToolName}' auto-approved`)}`);
|
|
3861
|
+
}
|
|
3610
3862
|
}
|
|
3611
3863
|
else {
|
|
3612
3864
|
const approvalEngine = getSmartApprovalEngine(debugMode);
|
|
3865
|
+
// Set SDK mode for approval engine if in SDK mode
|
|
3866
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
3867
|
+
approvalEngine.setSdkMode(true, sdkOutputAdapter);
|
|
3868
|
+
}
|
|
3613
3869
|
// Evaluate MCP tool call
|
|
3614
3870
|
const result = await approvalEngine.evaluate({
|
|
3615
3871
|
toolName: `MCP[${serverName}]::${actualToolName}`,
|
|
@@ -3617,19 +3873,35 @@ export class ToolRegistry {
|
|
|
3617
3873
|
timestamp: Date.now(),
|
|
3618
3874
|
});
|
|
3619
3875
|
if (result.decision === 'approved') {
|
|
3620
|
-
|
|
3621
|
-
|
|
3876
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
3877
|
+
sdkOutputAdapter.outputInfo(`[Smart Mode] MCP tool '${serverName}::${actualToolName}' passed approval`);
|
|
3878
|
+
sdkOutputAdapter.outputInfo(`Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`);
|
|
3879
|
+
}
|
|
3880
|
+
else {
|
|
3881
|
+
console.log(`${indent}${colors.success(`✅ [Smart Mode] MCP tool '${serverName}::${actualToolName}' passed approval`)}`);
|
|
3882
|
+
console.log(`${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`);
|
|
3883
|
+
}
|
|
3622
3884
|
}
|
|
3623
3885
|
else if (result.decision === 'requires_confirmation') {
|
|
3624
|
-
const confirmed = await approvalEngine.requestConfirmation(result);
|
|
3886
|
+
const confirmed = await approvalEngine.requestConfirmation(result, `MCP[${serverName}]::${actualToolName}`, params);
|
|
3625
3887
|
if (!confirmed) {
|
|
3626
|
-
|
|
3888
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
3889
|
+
sdkOutputAdapter.outputWarning(`[Smart Mode] User cancelled MCP tool execution`);
|
|
3890
|
+
}
|
|
3891
|
+
else {
|
|
3892
|
+
console.log(`${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled MCP tool execution`)}`);
|
|
3893
|
+
}
|
|
3627
3894
|
throw new Error(`Tool execution cancelled by user: ${toolName}`);
|
|
3628
3895
|
}
|
|
3629
3896
|
}
|
|
3630
3897
|
else {
|
|
3631
|
-
|
|
3632
|
-
|
|
3898
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
3899
|
+
sdkOutputAdapter.outputError(`[Smart Mode] MCP tool execution rejected`, { reason: result.description });
|
|
3900
|
+
}
|
|
3901
|
+
else {
|
|
3902
|
+
console.log(`${indent}${colors.error(`❌ [Smart Mode] MCP tool execution rejected`)}`);
|
|
3903
|
+
console.log(`${indent}${colors.textDim(` Reason: ${result.description}`)}`);
|
|
3904
|
+
}
|
|
3633
3905
|
throw new Error(`Tool execution rejected: ${toolName}`);
|
|
3634
3906
|
}
|
|
3635
3907
|
}
|