@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/src/tools.ts
CHANGED
|
@@ -3,13 +3,12 @@ import { select, text } from '@clack/prompts';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import readline from 'readline';
|
|
6
|
-
import { spawn
|
|
6
|
+
import { spawn } from 'child_process';
|
|
7
7
|
import { glob } from 'glob';
|
|
8
8
|
import axios from 'axios';
|
|
9
9
|
import { Tool, ExecutionMode, AuthType } from './types.js';
|
|
10
10
|
import type { Message, ToolDefinition } from './ai-client/types.js';
|
|
11
|
-
import
|
|
12
|
-
import { colors, icons, styleHelpers } from './theme.js';
|
|
11
|
+
import { colors, icons } from './theme.js';
|
|
13
12
|
import { getLogger } from './logger.js';
|
|
14
13
|
import { getCancellationManager } from './cancellation.js';
|
|
15
14
|
import { SystemPromptGenerator } from './system-prompt-generator.js';
|
|
@@ -344,6 +343,8 @@ This is useful when working with skills that have local dependencies.
|
|
|
344
343
|
skillPath?: string;
|
|
345
344
|
}> {
|
|
346
345
|
const { command, cwd, description, timeout = 120, run_in_bg = false, skillPath } = params;
|
|
346
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
347
|
+
void description;
|
|
347
348
|
|
|
348
349
|
// Determine effective working directory
|
|
349
350
|
// Only use cwd if the command doesn't contain 'cd' (let LLM control directory)
|
|
@@ -484,7 +485,7 @@ This is useful when working with skills that have local dependencies.
|
|
|
484
485
|
output.push(text);
|
|
485
486
|
});
|
|
486
487
|
|
|
487
|
-
childProcess.on('close', (
|
|
488
|
+
childProcess.on('close', (_code: number) => {
|
|
488
489
|
// Silent cleanup - don't log to avoid noise during normal operation
|
|
489
490
|
// Note: On Windows with PowerShell, the shell process exits after
|
|
490
491
|
// the command completes
|
|
@@ -515,8 +516,8 @@ This is useful when working with skills that have local dependencies.
|
|
|
515
516
|
const stdoutResult = truncateTail(result.stdout);
|
|
516
517
|
const stderrResult = truncateTail(result.stderr);
|
|
517
518
|
|
|
518
|
-
|
|
519
|
-
|
|
519
|
+
const stdout = stdoutResult.content;
|
|
520
|
+
const stderr = stderrResult.content;
|
|
520
521
|
let truncationNotice = '';
|
|
521
522
|
|
|
522
523
|
if (stdoutResult.truncated) {
|
|
@@ -1097,6 +1098,8 @@ edit(
|
|
|
1097
1098
|
new_string: string;
|
|
1098
1099
|
}): Promise<{ success: boolean; message: string; diff?: string; firstChangedLine?: number }> {
|
|
1099
1100
|
const { file_path, instruction, old_string, new_string } = params;
|
|
1101
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1102
|
+
void instruction;
|
|
1100
1103
|
|
|
1101
1104
|
try {
|
|
1102
1105
|
const absolutePath = path.resolve(file_path);
|
|
@@ -1507,7 +1510,9 @@ export class TaskTool implements Tool {
|
|
|
1507
1510
|
const config = getConfigManager();
|
|
1508
1511
|
|
|
1509
1512
|
const authConfig = config.getAuthConfig();
|
|
1510
|
-
|
|
1513
|
+
// aiClient is created for future use when executeParallelAgents supports it
|
|
1514
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1515
|
+
const _aiClient = createAIClient(authConfig);
|
|
1511
1516
|
|
|
1512
1517
|
const toolRegistry = getToolRegistry();
|
|
1513
1518
|
|
|
@@ -1518,7 +1523,7 @@ export class TaskTool implements Tool {
|
|
|
1518
1523
|
mode,
|
|
1519
1524
|
agentManager,
|
|
1520
1525
|
toolRegistry,
|
|
1521
|
-
|
|
1526
|
+
config
|
|
1522
1527
|
);
|
|
1523
1528
|
}
|
|
1524
1529
|
|
|
@@ -1550,7 +1555,6 @@ export class TaskTool implements Tool {
|
|
|
1550
1555
|
mode,
|
|
1551
1556
|
agentManager,
|
|
1552
1557
|
toolRegistry,
|
|
1553
|
-
aiClient,
|
|
1554
1558
|
config
|
|
1555
1559
|
);
|
|
1556
1560
|
|
|
@@ -1696,11 +1700,29 @@ export class TaskTool implements Tool {
|
|
|
1696
1700
|
): Promise<{ success: boolean; cancelled?: boolean; message: string; result?: any }> {
|
|
1697
1701
|
const indent = ' '.repeat(indentLevel);
|
|
1698
1702
|
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1703
|
+
// Get SDK adapter from session for SDK mode output
|
|
1704
|
+
let sdkOutputAdapter: any = null;
|
|
1705
|
+
let isSdkMode = false;
|
|
1706
|
+
try {
|
|
1707
|
+
const { getSingletonSession } = await import('./session.js');
|
|
1708
|
+
const session = getSingletonSession();
|
|
1709
|
+
if (session) {
|
|
1710
|
+
isSdkMode = (session as any).isSdkMode;
|
|
1711
|
+
sdkOutputAdapter = (session as any).sdkOutputAdapter;
|
|
1712
|
+
}
|
|
1713
|
+
} catch {
|
|
1714
|
+
// Session not available
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
// SDK mode: use adapter output (guiAgent.run() handles SDK output internally)
|
|
1718
|
+
// Only output console messages in non-SDK mode
|
|
1719
|
+
if (!isSdkMode) {
|
|
1720
|
+
console.log(`${indent}${colors.primaryBright(`${icons.robot} GUI Agent`)}: ${description}`);
|
|
1721
|
+
console.log(
|
|
1722
|
+
`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`
|
|
1723
|
+
);
|
|
1724
|
+
console.log('');
|
|
1725
|
+
}
|
|
1704
1726
|
|
|
1705
1727
|
// Get VLM configuration for local mode
|
|
1706
1728
|
// NOTE: guiSubagentBaseUrl must be explicitly configured, NOT fallback to baseUrl
|
|
@@ -1713,18 +1735,30 @@ export class TaskTool implements Tool {
|
|
|
1713
1735
|
|
|
1714
1736
|
// Log mode information
|
|
1715
1737
|
if (isRemoteMode) {
|
|
1716
|
-
|
|
1738
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1739
|
+
// SDK mode: use adapter output
|
|
1740
|
+
sdkOutputAdapter.outputInfo('Using remote VLM service');
|
|
1741
|
+
} else {
|
|
1742
|
+
// Normal mode: console output
|
|
1743
|
+
console.log(`${indent}${colors.info(`${icons.brain} Using remote VLM service`)}`);
|
|
1744
|
+
}
|
|
1717
1745
|
} else {
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1746
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1747
|
+
// SDK mode: use adapter output
|
|
1748
|
+
sdkOutputAdapter.outputInfo('Using local VLM configuration');
|
|
1749
|
+
} else {
|
|
1750
|
+
// Normal mode: console output
|
|
1751
|
+
console.log(`${indent}${colors.info(`${icons.brain} Using local VLM configuration`)}`);
|
|
1752
|
+
// Local mode requires explicit VLM configuration
|
|
1753
|
+
if (!baseUrl || !apiKey || !modelName) {
|
|
1754
|
+
return {
|
|
1755
|
+
success: false,
|
|
1756
|
+
message: `GUI task "${description}" failed: VLM not configured. Please run /model to configure Vision-Language Model first.`,
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
console.log(`${indent}${colors.textMuted(` Model: ${modelName}`)}`);
|
|
1760
|
+
console.log(`${indent}${colors.textMuted(` Base URL: ${baseUrl}`)}`);
|
|
1725
1761
|
}
|
|
1726
|
-
console.log(`${indent}${colors.textMuted(` Model: ${modelName}`)}`);
|
|
1727
|
-
console.log(`${indent}${colors.textMuted(` Base URL: ${baseUrl}`)}`);
|
|
1728
1762
|
}
|
|
1729
1763
|
console.log('');
|
|
1730
1764
|
|
|
@@ -1735,7 +1769,7 @@ export class TaskTool implements Tool {
|
|
|
1735
1769
|
const { getSingletonSession } = await import('./session.js');
|
|
1736
1770
|
const session = getSingletonSession();
|
|
1737
1771
|
taskId = session?.getTaskId() || null;
|
|
1738
|
-
} catch
|
|
1772
|
+
} catch {
|
|
1739
1773
|
taskId = null;
|
|
1740
1774
|
}
|
|
1741
1775
|
}
|
|
@@ -1831,6 +1865,7 @@ export class TaskTool implements Tool {
|
|
|
1831
1865
|
loopIntervalInMs: 500,
|
|
1832
1866
|
showAIDebugInfo: config.get('showAIDebugInfo') || false,
|
|
1833
1867
|
indentLevel: indentLevel,
|
|
1868
|
+
sdkOutputAdapter: isSdkMode ? sdkOutputAdapter : null,
|
|
1834
1869
|
});
|
|
1835
1870
|
|
|
1836
1871
|
// Add constraints to prompt if any
|
|
@@ -1873,9 +1908,15 @@ export class TaskTool implements Tool {
|
|
|
1873
1908
|
const iterations = conversationsWithoutScreenshots.filter(
|
|
1874
1909
|
(c: any) => c.from === 'human' && c.screenshotContext
|
|
1875
1910
|
).length;
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1911
|
+
// SDK mode: use adapter output
|
|
1912
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1913
|
+
sdkOutputAdapter.outputGUIAgentComplete(description, iterations);
|
|
1914
|
+
} else {
|
|
1915
|
+
// Normal mode: console output
|
|
1916
|
+
console.log(
|
|
1917
|
+
`${indent}${colors.success(`${icons.check} GUI task completed in ${iterations} iterations`)}`
|
|
1918
|
+
);
|
|
1919
|
+
}
|
|
1879
1920
|
return {
|
|
1880
1921
|
success: true,
|
|
1881
1922
|
message: `GUI task "${description}" completed`,
|
|
@@ -1890,10 +1931,17 @@ export class TaskTool implements Tool {
|
|
|
1890
1931
|
},
|
|
1891
1932
|
};
|
|
1892
1933
|
} else if (result.status === 'call_llm') {
|
|
1893
|
-
//
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1934
|
+
// SDK mode: use adapter output
|
|
1935
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1936
|
+
sdkOutputAdapter.outputGUIAgentStatus('call_llm', conversationsWithoutScreenshots.filter(
|
|
1937
|
+
(c: any) => c.from === 'human' && c.screenshotContext
|
|
1938
|
+
).length);
|
|
1939
|
+
} else {
|
|
1940
|
+
// Normal mode: console output
|
|
1941
|
+
console.log(
|
|
1942
|
+
`${indent}${colors.warning(`${icons.warning} GUI agent returned to main agent for LLM decision`)}`
|
|
1943
|
+
);
|
|
1944
|
+
}
|
|
1897
1945
|
return {
|
|
1898
1946
|
success: true,
|
|
1899
1947
|
message: `GUI task "${description}" returned for LLM decision`,
|
|
@@ -1910,6 +1958,10 @@ export class TaskTool implements Tool {
|
|
|
1910
1958
|
},
|
|
1911
1959
|
};
|
|
1912
1960
|
} else if (result.status === 'user_stopped') {
|
|
1961
|
+
// SDK mode: use adapter output
|
|
1962
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1963
|
+
sdkOutputAdapter.outputGUIAgentCancelled(description);
|
|
1964
|
+
}
|
|
1913
1965
|
return {
|
|
1914
1966
|
success: true,
|
|
1915
1967
|
message: `GUI task "${description}" stopped by user`,
|
|
@@ -1928,6 +1980,10 @@ export class TaskTool implements Tool {
|
|
|
1928
1980
|
} else {
|
|
1929
1981
|
// status is 'error' or other non-success status
|
|
1930
1982
|
const errorMsg = result.error || 'Unknown error';
|
|
1983
|
+
// SDK mode: use adapter output
|
|
1984
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1985
|
+
sdkOutputAdapter.outputGUIAgentError(description, errorMsg);
|
|
1986
|
+
}
|
|
1931
1987
|
return {
|
|
1932
1988
|
success: false,
|
|
1933
1989
|
message: `GUI task "${description}" failed: ${errorMsg}`,
|
|
@@ -1954,6 +2010,10 @@ export class TaskTool implements Tool {
|
|
|
1954
2010
|
// If the user cancelled the task, ignore any API errors (like 429)
|
|
1955
2011
|
// and return cancelled status instead
|
|
1956
2012
|
if (cancelled || cancellationManager.isOperationCancelled()) {
|
|
2013
|
+
// SDK mode: use adapter output
|
|
2014
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2015
|
+
sdkOutputAdapter.outputGUIAgentCancelled(description);
|
|
2016
|
+
}
|
|
1957
2017
|
return {
|
|
1958
2018
|
success: true,
|
|
1959
2019
|
cancelled: true, // Mark as cancelled so main agent won't continue
|
|
@@ -1963,6 +2023,10 @@ export class TaskTool implements Tool {
|
|
|
1963
2023
|
}
|
|
1964
2024
|
|
|
1965
2025
|
if (error.message === 'Operation cancelled by user') {
|
|
2026
|
+
// SDK mode: use adapter output
|
|
2027
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2028
|
+
sdkOutputAdapter.outputGUIAgentCancelled(description);
|
|
2029
|
+
}
|
|
1966
2030
|
return {
|
|
1967
2031
|
success: true,
|
|
1968
2032
|
message: `GUI task "${description}" cancelled by user`,
|
|
@@ -1971,6 +2035,10 @@ export class TaskTool implements Tool {
|
|
|
1971
2035
|
}
|
|
1972
2036
|
|
|
1973
2037
|
// Return failure without throwing - let the main agent handle it
|
|
2038
|
+
// SDK mode: use adapter output
|
|
2039
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2040
|
+
sdkOutputAdapter.outputGUIAgentError(description, error.message);
|
|
2041
|
+
}
|
|
1974
2042
|
return {
|
|
1975
2043
|
success: false,
|
|
1976
2044
|
message: `GUI task "${description}" failed: ${error.message}`,
|
|
@@ -1987,7 +2055,6 @@ export class TaskTool implements Tool {
|
|
|
1987
2055
|
mode: ExecutionMode,
|
|
1988
2056
|
agentManager: any,
|
|
1989
2057
|
toolRegistry: any,
|
|
1990
|
-
aiClient: any,
|
|
1991
2058
|
config: any,
|
|
1992
2059
|
indentLevel: number = 1
|
|
1993
2060
|
): Promise<{ success: boolean; message: string; result?: any }> {
|
|
@@ -1997,6 +2064,20 @@ export class TaskTool implements Tool {
|
|
|
1997
2064
|
throw new Error(`Agent ${subagent_type} not found`);
|
|
1998
2065
|
}
|
|
1999
2066
|
|
|
2067
|
+
// Get SDK adapter from session for subagent output
|
|
2068
|
+
let sdkOutputAdapter: any = null;
|
|
2069
|
+
let isSdkMode = false;
|
|
2070
|
+
try {
|
|
2071
|
+
const { getSingletonSession } = await import('./session.js');
|
|
2072
|
+
const session = getSingletonSession();
|
|
2073
|
+
if (session) {
|
|
2074
|
+
isSdkMode = (session as any).isSdkMode;
|
|
2075
|
+
sdkOutputAdapter = (session as any).sdkOutputAdapter;
|
|
2076
|
+
}
|
|
2077
|
+
} catch {
|
|
2078
|
+
// Session not available
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2000
2081
|
// Special handling for gui-subagent: directly call GUIAgent.run() instead of subagent message loop
|
|
2001
2082
|
if (subagent_type === 'gui-subagent') {
|
|
2002
2083
|
// Get RemoteAIClient instance from session (if available)
|
|
@@ -2007,7 +2088,7 @@ export class TaskTool implements Tool {
|
|
|
2007
2088
|
if (session) {
|
|
2008
2089
|
remoteAIClient = session.getRemoteAIClient();
|
|
2009
2090
|
}
|
|
2010
|
-
} catch
|
|
2091
|
+
} catch {
|
|
2011
2092
|
// Session not available, keep undefined
|
|
2012
2093
|
remoteAIClient = undefined;
|
|
2013
2094
|
}
|
|
@@ -2047,21 +2128,23 @@ export class TaskTool implements Tool {
|
|
|
2047
2128
|
}
|
|
2048
2129
|
}
|
|
2049
2130
|
|
|
2050
|
-
// Create AI client for this subagent
|
|
2131
|
+
// Create AI client for this subagent - each subagent gets its own independent client
|
|
2051
2132
|
let subAgentClient;
|
|
2052
2133
|
let isRemoteMode = false;
|
|
2053
2134
|
let mainTaskId: string | null = null;
|
|
2054
2135
|
const authConfig = config.getAuthConfig();
|
|
2055
2136
|
|
|
2056
2137
|
if (authConfig.type === AuthType.OAUTH_XAGENT) {
|
|
2057
|
-
// Remote mode:
|
|
2138
|
+
// Remote mode: create independent RemoteAIClient for each subagent
|
|
2139
|
+
// This prevents message queue conflicts when multiple subagents run in parallel
|
|
2058
2140
|
const session = getSingletonSession();
|
|
2059
|
-
const
|
|
2141
|
+
const remoteAIClient = session?.getRemoteAIClient();
|
|
2060
2142
|
|
|
2061
|
-
if (
|
|
2062
|
-
|
|
2143
|
+
if (remoteAIClient) {
|
|
2144
|
+
// Clone or create independent client for this subagent
|
|
2145
|
+
// RemoteAIClient should be designed to handle concurrent requests
|
|
2146
|
+
subAgentClient = remoteAIClient;
|
|
2063
2147
|
isRemoteMode = true;
|
|
2064
|
-
// Get the main taskId from session - subagent shares the same taskId as the parent task
|
|
2065
2148
|
mainTaskId = session?.getTaskId() || null;
|
|
2066
2149
|
} else {
|
|
2067
2150
|
subAgentClient = createAIClient(authConfig);
|
|
@@ -2080,7 +2163,7 @@ export class TaskTool implements Tool {
|
|
|
2080
2163
|
}
|
|
2081
2164
|
|
|
2082
2165
|
const indent = ' '.repeat(indentLevel);
|
|
2083
|
-
const
|
|
2166
|
+
const _indentNext = ' '.repeat(indentLevel + 1);
|
|
2084
2167
|
const agentName = agent.name || subagent_type;
|
|
2085
2168
|
|
|
2086
2169
|
// Track execution history for better reporting to main agent
|
|
@@ -2144,7 +2227,7 @@ export class TaskTool implements Tool {
|
|
|
2144
2227
|
}
|
|
2145
2228
|
}
|
|
2146
2229
|
}
|
|
2147
|
-
} catch
|
|
2230
|
+
} catch {
|
|
2148
2231
|
// Ignore polling errors
|
|
2149
2232
|
}
|
|
2150
2233
|
}, 10);
|
|
@@ -2176,7 +2259,7 @@ export class TaskTool implements Tool {
|
|
|
2176
2259
|
}
|
|
2177
2260
|
};
|
|
2178
2261
|
|
|
2179
|
-
|
|
2262
|
+
const messages: Message[] = [
|
|
2180
2263
|
{ role: 'system', content: enhancedSystemPrompt },
|
|
2181
2264
|
{ role: 'user', content: fullPrompt },
|
|
2182
2265
|
];
|
|
@@ -2200,9 +2283,11 @@ export class TaskTool implements Tool {
|
|
|
2200
2283
|
});
|
|
2201
2284
|
|
|
2202
2285
|
let iteration = 0;
|
|
2203
|
-
|
|
2286
|
+
let lastContentStr = ''; // Track last content for final result
|
|
2204
2287
|
|
|
2205
|
-
|
|
2288
|
+
// Main agent style loop: continue until AI returns no more tool_calls
|
|
2289
|
+
// eslint-disable-next-line no-constant-condition
|
|
2290
|
+
while (true) {
|
|
2206
2291
|
iteration++;
|
|
2207
2292
|
|
|
2208
2293
|
// Check for cancellation before each iteration
|
|
@@ -2281,157 +2366,71 @@ export class TaskTool implements Tool {
|
|
|
2281
2366
|
|
|
2282
2367
|
// Display reasoning content if present
|
|
2283
2368
|
if (reasoningContent) {
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2369
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2370
|
+
sdkOutputAdapter.outputThinking(reasoningContent, 'compact');
|
|
2371
|
+
} else {
|
|
2372
|
+
console.log(`\n${indent}${colors.textDim(`${icons.brain} Thinking Process:`)}`);
|
|
2373
|
+
const truncatedReasoning =
|
|
2374
|
+
reasoningContent.length > 500
|
|
2375
|
+
? reasoningContent.substring(0, 500) + '...'
|
|
2376
|
+
: reasoningContent;
|
|
2377
|
+
const indentedReasoning = indentMultiline(truncatedReasoning, indent);
|
|
2378
|
+
console.log(`${indentedReasoning}\n`);
|
|
2379
|
+
}
|
|
2291
2380
|
}
|
|
2292
2381
|
|
|
2293
2382
|
// Display assistant response (if there's any text content) with proper indentation
|
|
2294
2383
|
if (contentStr) {
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2384
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2385
|
+
sdkOutputAdapter.outputAssistant(contentStr);
|
|
2386
|
+
} else {
|
|
2387
|
+
console.log(`\n${indent}${colors.primaryBright(agentName)}: ${description}`);
|
|
2388
|
+
const truncatedContent =
|
|
2389
|
+
contentStr.length > 500 ? contentStr.substring(0, 500) + '...' : contentStr;
|
|
2390
|
+
const indentedContent = indentMultiline(truncatedContent, indent);
|
|
2391
|
+
console.log(`${indentedContent}\n`);
|
|
2392
|
+
}
|
|
2300
2393
|
}
|
|
2301
2394
|
|
|
2302
|
-
// Process tool calls
|
|
2395
|
+
// Process tool calls in parallel (照搬 session 的实现)
|
|
2303
2396
|
if (toolCalls && toolCalls.length > 0) {
|
|
2304
|
-
|
|
2397
|
+
// Prepare all tool calls with their indices
|
|
2398
|
+
const preparedToolCalls = toolCalls.map((toolCall: any, index: number) => {
|
|
2305
2399
|
const { name, arguments: params } = toolCall.function;
|
|
2306
|
-
|
|
2307
2400
|
let parsedParams: any;
|
|
2308
2401
|
try {
|
|
2309
2402
|
parsedParams = typeof params === 'string' ? JSON.parse(params) : params;
|
|
2310
|
-
} catch
|
|
2403
|
+
} catch {
|
|
2311
2404
|
parsedParams = params;
|
|
2312
2405
|
}
|
|
2406
|
+
return { name, params: parsedParams, id: toolCall.id, index };
|
|
2407
|
+
});
|
|
2313
2408
|
|
|
2314
|
-
|
|
2409
|
+
// Display all tool call info first
|
|
2410
|
+
for (const tc of preparedToolCalls as Array<{ name: string; params: any; id: string }>) {
|
|
2411
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2412
|
+
sdkOutputAdapter.outputToolStart(tc.name, tc.params);
|
|
2413
|
+
} else {
|
|
2414
|
+
console.log(`${indent}${colors.textMuted(`${icons.loading} Tool: ${tc.name}`)}`);
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2315
2417
|
|
|
2418
|
+
// Execute all tool calls in parallel
|
|
2419
|
+
const executePromises = preparedToolCalls.map(async (tc: { name: string; params: any; id: string; index: number }) => {
|
|
2316
2420
|
try {
|
|
2317
2421
|
// Check cancellation before tool execution
|
|
2318
2422
|
checkCancellation();
|
|
2319
2423
|
|
|
2320
2424
|
const toolResult: any = await cancellationManager.withCancellation(
|
|
2321
|
-
toolRegistry.execute(name,
|
|
2322
|
-
`subagent-${subagent_type}-${name}-${iteration}`
|
|
2425
|
+
toolRegistry.execute(tc.name, tc.params, mode, indent),
|
|
2426
|
+
`subagent-${subagent_type}-${tc.name}-${iteration}`
|
|
2323
2427
|
);
|
|
2324
|
-
|
|
2325
|
-
// Get showToolDetails config to control result display
|
|
2326
|
-
const showToolDetails = config.get('showToolDetails') || false;
|
|
2327
|
-
|
|
2328
|
-
// Prepare result preview for history
|
|
2329
|
-
const resultPreview =
|
|
2330
|
-
typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult, null, 2);
|
|
2331
|
-
const truncatedPreview =
|
|
2332
|
-
resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
|
|
2333
|
-
|
|
2334
|
-
// Special handling for different tools (consistent with session.ts display logic)
|
|
2335
|
-
const isTodoTool = name === 'todo_write' || name === 'todo_read';
|
|
2336
|
-
const isEditTool = name === 'Edit';
|
|
2337
|
-
const isWriteTool = name === 'Write';
|
|
2338
|
-
const isDeleteTool = name === 'DeleteFile';
|
|
2339
|
-
const hasDiff = isEditTool && toolResult?.diff;
|
|
2340
|
-
const hasFilePreview = isWriteTool && toolResult?.preview;
|
|
2341
|
-
const hasDeleteInfo = isDeleteTool && toolResult?.filePath;
|
|
2342
|
-
|
|
2343
|
-
// Import render functions for consistent display
|
|
2344
|
-
const { renderDiff, renderLines } = await import('./theme.js');
|
|
2345
|
-
|
|
2346
|
-
if (isTodoTool) {
|
|
2347
|
-
// Display todo list
|
|
2348
|
-
console.log(`${indent}${colors.success(`${icons.check} Todo List:`)}`);
|
|
2349
|
-
const todos = toolResult?.todos || [];
|
|
2350
|
-
if (todos.length === 0) {
|
|
2351
|
-
console.log(`${indent} ${colors.textMuted('No tasks')}`);
|
|
2352
|
-
} else {
|
|
2353
|
-
const statusConfig: Record<
|
|
2354
|
-
string,
|
|
2355
|
-
{ icon: string; color: (text: string) => string; label: string }
|
|
2356
|
-
> = {
|
|
2357
|
-
pending: { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
|
|
2358
|
-
in_progress: { icon: icons.loading, color: colors.warning, label: 'In Progress' },
|
|
2359
|
-
completed: { icon: icons.success, color: colors.success, label: 'Completed' },
|
|
2360
|
-
failed: { icon: icons.error, color: colors.error, label: 'Failed' },
|
|
2361
|
-
};
|
|
2362
|
-
for (const todo of todos) {
|
|
2363
|
-
const status = statusConfig[todo.status] || statusConfig['pending'];
|
|
2364
|
-
console.log(
|
|
2365
|
-
`${indent} ${status.color(status.icon)} ${status.color(status.label)}: ${colors.text(todo.task)}`
|
|
2366
|
-
);
|
|
2367
|
-
}
|
|
2368
|
-
}
|
|
2369
|
-
if (toolResult?.message) {
|
|
2370
|
-
console.log(`${indent}${colors.textDim(toolResult.message)}`);
|
|
2371
|
-
}
|
|
2372
|
-
console.log('');
|
|
2373
|
-
} else if (hasDiff) {
|
|
2374
|
-
// Display edit result with diff
|
|
2375
|
-
console.log('');
|
|
2376
|
-
const diffOutput = renderDiff(toolResult.diff);
|
|
2377
|
-
const indentedDiff = diffOutput
|
|
2378
|
-
.split('\n')
|
|
2379
|
-
.map((line) => `${indent} ${line}`)
|
|
2380
|
-
.join('\n');
|
|
2381
|
-
console.log(`${indentedDiff}\n`);
|
|
2382
|
-
} else if (hasFilePreview) {
|
|
2383
|
-
// Display new file content in preview style
|
|
2384
|
-
console.log('');
|
|
2385
|
-
console.log(`${indent}${colors.success(`${icons.file} ${toolResult.filePath}`)}`);
|
|
2386
|
-
console.log(`${indent}${colors.textDim(` ${toolResult.lineCount} lines`)}`);
|
|
2387
|
-
console.log('');
|
|
2388
|
-
console.log(renderLines(toolResult.preview, { maxLines: 10, indent: indent + ' ' }));
|
|
2389
|
-
console.log('');
|
|
2390
|
-
} else if (hasDeleteInfo) {
|
|
2391
|
-
// Display DeleteFile result
|
|
2392
|
-
console.log('');
|
|
2393
|
-
console.log(
|
|
2394
|
-
`${indent}${colors.success(`${icons.check} Deleted: ${toolResult.filePath}`)}`
|
|
2395
|
-
);
|
|
2396
|
-
console.log('');
|
|
2397
|
-
} else if (showToolDetails) {
|
|
2398
|
-
// Show full result details
|
|
2399
|
-
const indentedPreview = indentMultiline(resultPreview, indent);
|
|
2400
|
-
console.log(
|
|
2401
|
-
`${indent}${colors.success(`${icons.check} Tool Result:`)}\n${indentedPreview}\n`
|
|
2402
|
-
);
|
|
2403
|
-
} else if (toolResult && toolResult.success === false) {
|
|
2404
|
-
// Tool failed
|
|
2405
|
-
console.log(
|
|
2406
|
-
`${indent}${colors.error(`${icons.cross} ${toolResult.message || 'Failed'}`)}\n`
|
|
2407
|
-
);
|
|
2408
|
-
} else if (toolResult) {
|
|
2409
|
-
// Show brief preview by default
|
|
2410
|
-
const indentedPreview = indentMultiline(truncatedPreview, indent);
|
|
2411
|
-
console.log(
|
|
2412
|
-
`${indent}${colors.success(`${icons.check} Completed`)}\n${indentedPreview}\n`
|
|
2413
|
-
);
|
|
2414
|
-
} else {
|
|
2415
|
-
console.log(`${indent}${colors.textDim('(no result)')}\n`);
|
|
2416
|
-
}
|
|
2417
|
-
|
|
2418
|
-
// Record successful tool execution in history (use truncated preview to save memory)
|
|
2419
|
-
executionHistory.push({
|
|
2420
|
-
tool: name,
|
|
2421
|
-
status: 'success',
|
|
2422
|
-
params: parsedParams,
|
|
2423
|
-
result: truncatedPreview,
|
|
2424
|
-
timestamp: new Date().toISOString(),
|
|
2425
|
-
});
|
|
2426
|
-
|
|
2427
|
-
messages.push({
|
|
2428
|
-
role: 'tool',
|
|
2429
|
-
content: JSON.stringify(toolResult),
|
|
2430
|
-
tool_call_id: toolCall.id,
|
|
2431
|
-
});
|
|
2428
|
+
return { ...tc, toolResult, error: undefined };
|
|
2432
2429
|
} catch (error: any) {
|
|
2433
2430
|
if (error.message === 'Operation cancelled by user') {
|
|
2434
|
-
|
|
2431
|
+
if (!isSdkMode || !sdkOutputAdapter) {
|
|
2432
|
+
console.log(`${indent}${colors.warning(`⚠️ Operation cancelled`)}\n`);
|
|
2433
|
+
}
|
|
2435
2434
|
cancellationManager.off('cancelled', cancelHandler);
|
|
2436
2435
|
cleanupStdinPolling();
|
|
2437
2436
|
const summaryPreview =
|
|
@@ -2452,58 +2451,196 @@ export class TaskTool implements Tool {
|
|
|
2452
2451
|
},
|
|
2453
2452
|
};
|
|
2454
2453
|
}
|
|
2455
|
-
|
|
2454
|
+
return { ...tc, toolResult: undefined, error: error.message };
|
|
2455
|
+
}
|
|
2456
|
+
});
|
|
2457
|
+
|
|
2458
|
+
const settledResults = await Promise.all(executePromises);
|
|
2459
|
+
|
|
2460
|
+
// Check for cancellation in results
|
|
2461
|
+
const cancellationResult = settledResults.find(
|
|
2462
|
+
(r): r is { success: boolean; message: string; result: any } =>
|
|
2463
|
+
'success' in r && r.success === false
|
|
2464
|
+
);
|
|
2465
|
+
if (cancellationResult) {
|
|
2466
|
+
return cancellationResult;
|
|
2467
|
+
}
|
|
2468
|
+
|
|
2469
|
+
// Create a map to store results by tool call index to maintain original order (match session implementation)
|
|
2470
|
+
type ToolResultType = { name: string; params: any; toolResult: any; error?: string; id: string; index: number };
|
|
2471
|
+
const resultsByIndex = new Map<number, ToolResultType>();
|
|
2472
|
+
const usedIndices = new Set<number>();
|
|
2473
|
+
|
|
2474
|
+
for (const result of settledResults as unknown as ToolResultType[]) {
|
|
2475
|
+
// Find the first unused original index that matches the tool name
|
|
2476
|
+
const originalIndex = preparedToolCalls.findIndex((tc: { name: string }, idx: number) =>
|
|
2477
|
+
tc.name === result.name && !usedIndices.has(idx)
|
|
2478
|
+
);
|
|
2479
|
+
if (originalIndex !== -1) {
|
|
2480
|
+
usedIndices.add(originalIndex);
|
|
2481
|
+
resultsByIndex.set(originalIndex, result);
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
// Import render functions for consistent display
|
|
2486
|
+
const { renderDiff, renderLines } = await import('./theme.js');
|
|
2487
|
+
|
|
2488
|
+
// Process results in the original tool_calls order
|
|
2489
|
+
for (let i = 0; i < preparedToolCalls.length; i++) {
|
|
2490
|
+
const result = resultsByIndex.get(i);
|
|
2491
|
+
if (!result) continue;
|
|
2492
|
+
|
|
2493
|
+
const { name, params: parsedParams, toolResult, error } = result;
|
|
2494
|
+
|
|
2495
|
+
// Get showToolDetails config to control result display
|
|
2496
|
+
const showToolDetails = config.get('showToolDetails') || false;
|
|
2497
|
+
|
|
2498
|
+
// Prepare result preview for history
|
|
2499
|
+
const resultPreview =
|
|
2500
|
+
typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult, null, 2);
|
|
2501
|
+
const truncatedPreview =
|
|
2502
|
+
resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
|
|
2503
|
+
|
|
2504
|
+
if (error) {
|
|
2505
|
+
// Handle error case
|
|
2506
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2507
|
+
sdkOutputAdapter.outputToolError(name, error);
|
|
2508
|
+
} else {
|
|
2509
|
+
console.log(`${indent}${colors.error(`${icons.cross} Error:`)} ${error}\n`);
|
|
2510
|
+
}
|
|
2456
2511
|
|
|
2457
2512
|
// Record failed tool execution in history
|
|
2458
2513
|
executionHistory.push({
|
|
2459
2514
|
tool: name,
|
|
2460
2515
|
status: 'error',
|
|
2461
2516
|
params: parsedParams,
|
|
2462
|
-
error
|
|
2517
|
+
error,
|
|
2463
2518
|
timestamp: new Date().toISOString(),
|
|
2464
2519
|
});
|
|
2465
2520
|
|
|
2466
2521
|
messages.push({
|
|
2467
2522
|
role: 'tool',
|
|
2468
|
-
content: JSON.stringify({ error
|
|
2469
|
-
tool_call_id:
|
|
2523
|
+
content: JSON.stringify({ error }),
|
|
2524
|
+
tool_call_id: result.id,
|
|
2525
|
+
});
|
|
2526
|
+
} else {
|
|
2527
|
+
// Handle success case - display result
|
|
2528
|
+
// SDK mode: output tool result via adapter
|
|
2529
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2530
|
+
sdkOutputAdapter.outputToolResult(name, toolResult);
|
|
2531
|
+
}
|
|
2532
|
+
// Normal mode: console output (SDK mode already output via adapter above)
|
|
2533
|
+
if (!isSdkMode || !sdkOutputAdapter) {
|
|
2534
|
+
// Special handling for different tools (consistent with session.ts display logic)
|
|
2535
|
+
const isTodoTool = name === 'todo_write' || name === 'todo_read';
|
|
2536
|
+
const isEditTool = name === 'Edit';
|
|
2537
|
+
const isWriteTool = name === 'Write';
|
|
2538
|
+
const isDeleteTool = name === 'DeleteFile';
|
|
2539
|
+
const hasDiff = isEditTool && toolResult?.diff;
|
|
2540
|
+
const hasFilePreview = isWriteTool && toolResult?.preview;
|
|
2541
|
+
const hasDeleteInfo = isDeleteTool && toolResult?.filePath;
|
|
2542
|
+
|
|
2543
|
+
if (isTodoTool) {
|
|
2544
|
+
// Display todo list
|
|
2545
|
+
console.log(`${indent}${colors.success(`${icons.check} Todo List:`)}`);
|
|
2546
|
+
const todos = toolResult?.todos || [];
|
|
2547
|
+
if (todos.length === 0) {
|
|
2548
|
+
console.log(`${indent} ${colors.textMuted('No tasks')}`);
|
|
2549
|
+
} else {
|
|
2550
|
+
const statusConfig: Record<
|
|
2551
|
+
string,
|
|
2552
|
+
{ icon: string; color: (text: string) => string; label: string }
|
|
2553
|
+
> = {
|
|
2554
|
+
pending: { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
|
|
2555
|
+
in_progress: { icon: icons.loading, color: colors.warning, label: 'In Progress' },
|
|
2556
|
+
completed: { icon: icons.success, color: colors.success, label: 'Completed' },
|
|
2557
|
+
failed: { icon: icons.error, color: colors.error, label: 'Failed' },
|
|
2558
|
+
};
|
|
2559
|
+
for (const todo of todos) {
|
|
2560
|
+
const status = statusConfig[todo.status] || statusConfig['pending'];
|
|
2561
|
+
console.log(
|
|
2562
|
+
`${indent} ${status.color(status.icon)} ${status.color(status.label)}: ${colors.text(todo.task)}`
|
|
2563
|
+
);
|
|
2564
|
+
}
|
|
2565
|
+
}
|
|
2566
|
+
if (toolResult?.message) {
|
|
2567
|
+
console.log(`${indent}${colors.textDim(toolResult.message)}`);
|
|
2568
|
+
}
|
|
2569
|
+
console.log('');
|
|
2570
|
+
} else if (hasDiff) {
|
|
2571
|
+
// Display edit result with diff
|
|
2572
|
+
console.log('');
|
|
2573
|
+
const diffOutput = renderDiff(toolResult.diff);
|
|
2574
|
+
const indentedDiff = diffOutput
|
|
2575
|
+
.split('\n')
|
|
2576
|
+
.map((line) => `${indent} ${line}`)
|
|
2577
|
+
.join('\n');
|
|
2578
|
+
console.log(`${indentedDiff}\n`);
|
|
2579
|
+
} else if (hasFilePreview) {
|
|
2580
|
+
// Display new file content in preview style
|
|
2581
|
+
console.log('');
|
|
2582
|
+
console.log(`${indent}${colors.success(`${icons.file} ${toolResult.filePath}`)}`);
|
|
2583
|
+
console.log(`${indent}${colors.textDim(` ${toolResult.lineCount} lines`)}`);
|
|
2584
|
+
console.log('');
|
|
2585
|
+
console.log(renderLines(toolResult.preview, { maxLines: 10, indent: indent + ' ' }));
|
|
2586
|
+
console.log('');
|
|
2587
|
+
} else if (hasDeleteInfo) {
|
|
2588
|
+
// Display DeleteFile result
|
|
2589
|
+
console.log('');
|
|
2590
|
+
console.log(
|
|
2591
|
+
`${indent}${colors.success(`${icons.check} Deleted: ${toolResult.filePath}`)}`
|
|
2592
|
+
);
|
|
2593
|
+
console.log('');
|
|
2594
|
+
} else if (showToolDetails) {
|
|
2595
|
+
// Show full result details
|
|
2596
|
+
const indentedPreview = indentMultiline(resultPreview, indent);
|
|
2597
|
+
console.log(
|
|
2598
|
+
`${indent}${colors.success(`${icons.check} Tool Result:`)}\n${indentedPreview}\n`
|
|
2599
|
+
);
|
|
2600
|
+
} else if (toolResult && toolResult.success === false) {
|
|
2601
|
+
// Tool failed
|
|
2602
|
+
console.log(
|
|
2603
|
+
`${indent}${colors.error(`${icons.cross} ${toolResult.message || 'Failed'}`)}\n`
|
|
2604
|
+
);
|
|
2605
|
+
} else if (toolResult) {
|
|
2606
|
+
// Show brief preview by default
|
|
2607
|
+
const indentedPreview = indentMultiline(truncatedPreview, indent);
|
|
2608
|
+
console.log(
|
|
2609
|
+
`${indent}${colors.success(`${icons.check} Completed`)}\n${indentedPreview}\n`
|
|
2610
|
+
);
|
|
2611
|
+
} else {
|
|
2612
|
+
console.log(`${indent}${colors.textDim('(no result)')}\n`);
|
|
2613
|
+
}
|
|
2614
|
+
}
|
|
2615
|
+
|
|
2616
|
+
// Record successful tool execution in history (use truncated preview to save memory)
|
|
2617
|
+
executionHistory.push({
|
|
2618
|
+
tool: name,
|
|
2619
|
+
status: 'success',
|
|
2620
|
+
params: parsedParams,
|
|
2621
|
+
result: truncatedPreview,
|
|
2622
|
+
timestamp: new Date().toISOString(),
|
|
2623
|
+
});
|
|
2624
|
+
|
|
2625
|
+
messages.push({
|
|
2626
|
+
role: 'tool',
|
|
2627
|
+
content: JSON.stringify(toolResult),
|
|
2628
|
+
tool_call_id: result.id,
|
|
2470
2629
|
});
|
|
2471
2630
|
}
|
|
2472
2631
|
}
|
|
2473
|
-
|
|
2632
|
+
if (!isSdkMode || !sdkOutputAdapter) {
|
|
2633
|
+
console.log('');
|
|
2634
|
+
}
|
|
2474
2635
|
continue; // Continue to next iteration to get final response
|
|
2475
2636
|
}
|
|
2476
2637
|
|
|
2477
|
-
// No more tool calls
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
const summaryPreview =
|
|
2482
|
-
contentStr.length > 300 ? contentStr.substring(0, 300) + '...' : contentStr;
|
|
2483
|
-
return {
|
|
2484
|
-
success: true,
|
|
2485
|
-
message: `Task "${description}" completed by ${subagent_type}`,
|
|
2486
|
-
result: {
|
|
2487
|
-
summary: summaryPreview,
|
|
2488
|
-
executionHistory: {
|
|
2489
|
-
totalIterations: iteration,
|
|
2490
|
-
toolsExecuted: executionHistory.length,
|
|
2491
|
-
successfulTools: executionHistory.filter((t) => t.status === 'success').length,
|
|
2492
|
-
failedTools: executionHistory.filter((t) => t.status === 'error').length,
|
|
2493
|
-
history: executionHistory,
|
|
2494
|
-
},
|
|
2495
|
-
},
|
|
2496
|
-
};
|
|
2638
|
+
// No more tool calls - break loop (same as main agent)
|
|
2639
|
+
lastContentStr = contentStr || '';
|
|
2640
|
+
break;
|
|
2497
2641
|
}
|
|
2498
2642
|
|
|
2499
|
-
//
|
|
2500
|
-
// Get the last assistant message content
|
|
2501
|
-
const lastAssistantMsg = messages.filter((m) => m.role === 'assistant').pop();
|
|
2502
|
-
const lastContentStr =
|
|
2503
|
-
typeof lastAssistantMsg?.content === 'string'
|
|
2504
|
-
? lastAssistantMsg.content
|
|
2505
|
-
: JSON.stringify(lastAssistantMsg?.content || '');
|
|
2506
|
-
|
|
2643
|
+
// Loop ended - return result (same as main agent pattern)
|
|
2507
2644
|
cancellationManager.off('cancelled', cancelHandler);
|
|
2508
2645
|
cleanupStdinPolling();
|
|
2509
2646
|
|
|
@@ -2511,7 +2648,7 @@ export class TaskTool implements Tool {
|
|
|
2511
2648
|
lastContentStr.length > 300 ? lastContentStr.substring(0, 300) + '...' : lastContentStr;
|
|
2512
2649
|
return {
|
|
2513
2650
|
success: true,
|
|
2514
|
-
message: `Task "${description}" completed
|
|
2651
|
+
message: `Task "${description}" completed by ${subagent_type}`,
|
|
2515
2652
|
result: {
|
|
2516
2653
|
summary: summaryPreview,
|
|
2517
2654
|
executionHistory: {
|
|
@@ -2520,7 +2657,6 @@ export class TaskTool implements Tool {
|
|
|
2520
2657
|
successfulTools: executionHistory.filter((t) => t.status === 'success').length,
|
|
2521
2658
|
failedTools: executionHistory.filter((t) => t.status === 'error').length,
|
|
2522
2659
|
history: executionHistory,
|
|
2523
|
-
maxIterationsReached: true,
|
|
2524
2660
|
},
|
|
2525
2661
|
},
|
|
2526
2662
|
};
|
|
@@ -2532,7 +2668,7 @@ export class TaskTool implements Tool {
|
|
|
2532
2668
|
mode: ExecutionMode,
|
|
2533
2669
|
agentManager: any,
|
|
2534
2670
|
toolRegistry: any,
|
|
2535
|
-
|
|
2671
|
+
config: any,
|
|
2536
2672
|
indentLevel: number = 1
|
|
2537
2673
|
): Promise<{ success: boolean; message: string; results: any[]; errors: any[] }> {
|
|
2538
2674
|
const indent = ' '.repeat(indentLevel);
|
|
@@ -2568,7 +2704,7 @@ export class TaskTool implements Tool {
|
|
|
2568
2704
|
}
|
|
2569
2705
|
}
|
|
2570
2706
|
}
|
|
2571
|
-
} catch
|
|
2707
|
+
} catch {
|
|
2572
2708
|
// Ignore polling errors
|
|
2573
2709
|
}
|
|
2574
2710
|
}, 10);
|
|
@@ -2598,7 +2734,7 @@ export class TaskTool implements Tool {
|
|
|
2598
2734
|
|
|
2599
2735
|
const startTime = Date.now();
|
|
2600
2736
|
|
|
2601
|
-
const agentPromises = agents.map(async (agentTask,
|
|
2737
|
+
const agentPromises = agents.map(async (agentTask, _index) => {
|
|
2602
2738
|
// Check if cancelled
|
|
2603
2739
|
if (cancelled || cancellationManager.isOperationCancelled()) {
|
|
2604
2740
|
return {
|
|
@@ -2619,7 +2755,7 @@ export class TaskTool implements Tool {
|
|
|
2619
2755
|
mode,
|
|
2620
2756
|
agentManager,
|
|
2621
2757
|
toolRegistry,
|
|
2622
|
-
|
|
2758
|
+
config,
|
|
2623
2759
|
indentLevel + 1
|
|
2624
2760
|
);
|
|
2625
2761
|
|
|
@@ -2869,6 +3005,29 @@ export class AskUserQuestionTool implements Tool {
|
|
|
2869
3005
|
options?: string[];
|
|
2870
3006
|
multiSelect?: boolean;
|
|
2871
3007
|
}>;
|
|
3008
|
+
}): Promise<{ answers: string[] }> {
|
|
3009
|
+
// Check if in SDK mode
|
|
3010
|
+
const sdkMode = (this as any)._sdkMode;
|
|
3011
|
+
const sdkAdapter = (this as any)._sdkOutputAdapter;
|
|
3012
|
+
|
|
3013
|
+
if (sdkMode && sdkAdapter) {
|
|
3014
|
+
return this.executeSdk(params, sdkAdapter);
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
// Regular TUI mode
|
|
3018
|
+
return this.executeTui(params);
|
|
3019
|
+
}
|
|
3020
|
+
|
|
3021
|
+
/**
|
|
3022
|
+
* Execute in TUI mode using @clack/prompts
|
|
3023
|
+
*/
|
|
3024
|
+
private async executeTui(params: {
|
|
3025
|
+
questions: Array<{
|
|
3026
|
+
question: string;
|
|
3027
|
+
header?: string;
|
|
3028
|
+
options?: string[];
|
|
3029
|
+
multiSelect?: boolean;
|
|
3030
|
+
}>;
|
|
2872
3031
|
}): Promise<{ answers: string[] }> {
|
|
2873
3032
|
const { questions } = params;
|
|
2874
3033
|
|
|
@@ -2902,6 +3061,54 @@ export class AskUserQuestionTool implements Tool {
|
|
|
2902
3061
|
throw new Error(`Failed to ask user questions: ${error.message}`);
|
|
2903
3062
|
}
|
|
2904
3063
|
}
|
|
3064
|
+
|
|
3065
|
+
/**
|
|
3066
|
+
* Execute in SDK mode - output question request and wait for response
|
|
3067
|
+
*/
|
|
3068
|
+
private async executeSdk(
|
|
3069
|
+
params: {
|
|
3070
|
+
questions: Array<{
|
|
3071
|
+
question: string;
|
|
3072
|
+
header?: string;
|
|
3073
|
+
options?: string[];
|
|
3074
|
+
multiSelect?: boolean;
|
|
3075
|
+
}>;
|
|
3076
|
+
},
|
|
3077
|
+
sdkAdapter: any
|
|
3078
|
+
): Promise<{ answers: string[] }> {
|
|
3079
|
+
const { questions } = params;
|
|
3080
|
+
|
|
3081
|
+
if (questions.length === 0 || questions.length > 4) {
|
|
3082
|
+
throw new Error('Must provide 1-4 questions');
|
|
3083
|
+
}
|
|
3084
|
+
|
|
3085
|
+
const requestId = `question_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
3086
|
+
|
|
3087
|
+
// Output question request through SDK adapter
|
|
3088
|
+
sdkAdapter.outputQuestionRequest({
|
|
3089
|
+
requestId,
|
|
3090
|
+
questions
|
|
3091
|
+
});
|
|
3092
|
+
|
|
3093
|
+
// Wait for SDK question response
|
|
3094
|
+
// The response will be handled by session.ts which has access to the SDK input
|
|
3095
|
+
// For now, we use a polling mechanism or wait for a specific event
|
|
3096
|
+
|
|
3097
|
+
try {
|
|
3098
|
+
// Import the session to get response handling
|
|
3099
|
+
const { getSingletonSession } = await import('./session.js');
|
|
3100
|
+
const session = getSingletonSession();
|
|
3101
|
+
if (!session) {
|
|
3102
|
+
throw new Error('SDK session not available');
|
|
3103
|
+
}
|
|
3104
|
+
const answers = await session.waitForQuestionResponse(requestId);
|
|
3105
|
+
|
|
3106
|
+
sdkAdapter.outputQuestionResponse(requestId, answers);
|
|
3107
|
+
return { answers };
|
|
3108
|
+
} catch (error: any) {
|
|
3109
|
+
throw new Error(`Failed to get SDK question response: ${error.message}`);
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
2905
3112
|
}
|
|
2906
3113
|
|
|
2907
3114
|
export class SaveMemoryTool implements Tool {
|
|
@@ -3319,7 +3526,6 @@ export class InvokeSkillTool implements Tool {
|
|
|
3319
3526
|
|
|
3320
3527
|
try {
|
|
3321
3528
|
const { getSkillInvoker } = await import('./skill-invoker.js');
|
|
3322
|
-
const { SkillExecutionParams } = (await import('./skill-invoker.js')) as any;
|
|
3323
3529
|
const skillInvoker = getSkillInvoker();
|
|
3324
3530
|
|
|
3325
3531
|
await skillInvoker.initialize();
|
|
@@ -3468,6 +3674,8 @@ export class ToolRegistry {
|
|
|
3468
3674
|
private todoWriteTool: TodoWriteTool;
|
|
3469
3675
|
private backgroundTasks: Map<string, { process: any; startTime: number; output: string[] }> =
|
|
3470
3676
|
new Map();
|
|
3677
|
+
private _isSdkMode: boolean = false;
|
|
3678
|
+
private _sdkOutputAdapter: any = null;
|
|
3471
3679
|
|
|
3472
3680
|
constructor() {
|
|
3473
3681
|
this.todoWriteTool = new TodoWriteTool();
|
|
@@ -3572,13 +3780,39 @@ export class ToolRegistry {
|
|
|
3572
3780
|
registeredCount++;
|
|
3573
3781
|
|
|
3574
3782
|
if (toolName !== originalName) {
|
|
3575
|
-
|
|
3783
|
+
// 在 SDK 模式下不输出重命名信息
|
|
3784
|
+
if (!this._isSdkMode) {
|
|
3785
|
+
console.log(`[MCP] Tool '${originalName}' renamed to '${toolName}' to avoid conflict`);
|
|
3786
|
+
}
|
|
3576
3787
|
}
|
|
3577
3788
|
}
|
|
3578
3789
|
}
|
|
3579
3790
|
|
|
3580
3791
|
if (registeredCount > 0) {
|
|
3581
|
-
|
|
3792
|
+
// 在 SDK 模式下不输出注册信息(MCP 相关输出已在 session 中处理)
|
|
3793
|
+
if (!this._isSdkMode) {
|
|
3794
|
+
console.log(`[MCP] Registered ${registeredCount} tool(s)`);
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
|
|
3799
|
+
/**
|
|
3800
|
+
* Set SDK mode for the tool registry.
|
|
3801
|
+
* In SDK mode, tool execution output is sent to the SDK output adapter.
|
|
3802
|
+
*/
|
|
3803
|
+
async setSdkMode(enabled: boolean, adapter: any): Promise<void> {
|
|
3804
|
+
this._isSdkMode = enabled;
|
|
3805
|
+
this._sdkOutputAdapter = adapter;
|
|
3806
|
+
// Mark all tools as SDK mode enabled
|
|
3807
|
+
for (const [, tool] of this.tools) {
|
|
3808
|
+
(tool as any)._sdkMode = enabled;
|
|
3809
|
+
(tool as any)._sdkOutputAdapter = adapter;
|
|
3810
|
+
}
|
|
3811
|
+
|
|
3812
|
+
// Initialize SDK mode for TaskTool specifically
|
|
3813
|
+
const taskTool = this.tools.get('task') as any;
|
|
3814
|
+
if (taskTool) {
|
|
3815
|
+
await taskTool.setSdkMode?.(enabled, adapter);
|
|
3582
3816
|
}
|
|
3583
3817
|
}
|
|
3584
3818
|
|
|
@@ -4124,7 +4358,7 @@ export class ToolRegistry {
|
|
|
4124
4358
|
};
|
|
4125
4359
|
break;
|
|
4126
4360
|
|
|
4127
|
-
default:
|
|
4361
|
+
default: {
|
|
4128
4362
|
// For MCP tools, use their inputSchema; for other unknown tools, keep empty schema
|
|
4129
4363
|
const mcpTool = tool as any;
|
|
4130
4364
|
if (mcpTool._isMcpTool && mcpTool.inputSchema) {
|
|
@@ -4154,6 +4388,7 @@ export class ToolRegistry {
|
|
|
4154
4388
|
required: [],
|
|
4155
4389
|
};
|
|
4156
4390
|
}
|
|
4391
|
+
}
|
|
4157
4392
|
}
|
|
4158
4393
|
|
|
4159
4394
|
return {
|
|
@@ -4190,11 +4425,11 @@ export class ToolRegistry {
|
|
|
4190
4425
|
}
|
|
4191
4426
|
|
|
4192
4427
|
// Try to find MCP tool with just the tool name (try each server)
|
|
4193
|
-
for (const [fullName,
|
|
4428
|
+
for (const [fullName, _tool] of allMcpTools) {
|
|
4194
4429
|
// Split only on the first __ to preserve underscores in tool names
|
|
4195
4430
|
const firstUnderscoreIndex = fullName.indexOf('__');
|
|
4196
4431
|
if (firstUnderscoreIndex === -1) continue;
|
|
4197
|
-
const [
|
|
4432
|
+
const [_serverName, actualToolName] = [
|
|
4198
4433
|
fullName.substring(0, firstUnderscoreIndex),
|
|
4199
4434
|
fullName.substring(firstUnderscoreIndex + 2),
|
|
4200
4435
|
];
|
|
@@ -4227,6 +4462,9 @@ export class ToolRegistry {
|
|
|
4227
4462
|
throw new Error(`Tool ${toolName} is not allowed in ${executionMode} mode`);
|
|
4228
4463
|
}
|
|
4229
4464
|
|
|
4465
|
+
const isSdkMode = this._isSdkMode;
|
|
4466
|
+
const sdkOutputAdapter = this._sdkOutputAdapter;
|
|
4467
|
+
|
|
4230
4468
|
// Smart approval mode
|
|
4231
4469
|
if (executionMode === ExecutionMode.SMART) {
|
|
4232
4470
|
const debugMode = process.env.DEBUG === 'smart-approval';
|
|
@@ -4254,10 +4492,15 @@ export class ToolRegistry {
|
|
|
4254
4492
|
const isRemoteMode = authConfig.type === AuthType.OAUTH_XAGENT;
|
|
4255
4493
|
if (isRemoteMode && toolName === 'InvokeSkill') {
|
|
4256
4494
|
console.log('');
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4495
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4496
|
+
sdkOutputAdapter.outputInfo(`[Smart Mode] Remote mode: tool '${toolName}' auto-approved (remote LLM already approved)`);
|
|
4497
|
+
} else {
|
|
4498
|
+
console.log('');
|
|
4499
|
+
console.log(
|
|
4500
|
+
`${indent}${colors.success(`✅ [Smart Mode] Remote mode: tool '${toolName}' auto-approved (remote LLM already approved)`)}`
|
|
4501
|
+
);
|
|
4502
|
+
console.log('');
|
|
4503
|
+
}
|
|
4261
4504
|
return await cancellationManager.withCancellation(
|
|
4262
4505
|
tool.execute(params, executionMode),
|
|
4263
4506
|
`tool-${toolName}`
|
|
@@ -4268,6 +4511,11 @@ export class ToolRegistry {
|
|
|
4268
4511
|
|
|
4269
4512
|
const approvalEngine = getSmartApprovalEngine(debugMode);
|
|
4270
4513
|
|
|
4514
|
+
// Set SDK mode for approval engine if in SDK mode
|
|
4515
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4516
|
+
approvalEngine.setSdkMode(true, sdkOutputAdapter);
|
|
4517
|
+
}
|
|
4518
|
+
|
|
4271
4519
|
// Evaluate tool call
|
|
4272
4520
|
const result = await approvalEngine.evaluate({
|
|
4273
4521
|
toolName,
|
|
@@ -4278,49 +4526,66 @@ export class ToolRegistry {
|
|
|
4278
4526
|
// Decide whether to execute based on approval result
|
|
4279
4527
|
if (result.decision === 'approved') {
|
|
4280
4528
|
// Whitelist or AI approval passed, execute directly
|
|
4281
|
-
|
|
4282
|
-
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4529
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4530
|
+
sdkOutputAdapter.outputInfo(`[Smart Mode] Tool '${toolName}' passed approval, executing directly`);
|
|
4531
|
+
sdkOutputAdapter.outputInfo(`Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}, Latency: ${result.latency}ms`);
|
|
4532
|
+
} else {
|
|
4533
|
+
console.log('');
|
|
4534
|
+
console.log(
|
|
4535
|
+
`${indent}${colors.success(`✅ [Smart Mode] Tool '${toolName}' passed approval, executing directly`)}`
|
|
4536
|
+
);
|
|
4537
|
+
console.log(
|
|
4538
|
+
`${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`
|
|
4539
|
+
);
|
|
4540
|
+
console.log(`${indent}${colors.textDim(` Latency: ${result.latency}ms`)}`);
|
|
4541
|
+
console.log('');
|
|
4542
|
+
}
|
|
4290
4543
|
return await cancellationManager.withCancellation(
|
|
4291
4544
|
tool.execute(params, executionMode),
|
|
4292
4545
|
`tool-${toolName}`
|
|
4293
4546
|
);
|
|
4294
4547
|
} else if (result.decision === 'requires_confirmation') {
|
|
4295
4548
|
// Requires user confirmation
|
|
4296
|
-
const confirmed = await approvalEngine.requestConfirmation(result);
|
|
4549
|
+
const confirmed = await approvalEngine.requestConfirmation(result, toolName, params);
|
|
4297
4550
|
|
|
4298
4551
|
if (confirmed) {
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4552
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4553
|
+
sdkOutputAdapter.outputInfo(`[Smart Mode] User confirmed execution of tool '${toolName}'`);
|
|
4554
|
+
} else {
|
|
4555
|
+
console.log('');
|
|
4556
|
+
console.log(
|
|
4557
|
+
`${indent}${colors.success(`✅ [Smart Mode] User confirmed execution of tool '${toolName}'`)}`
|
|
4558
|
+
);
|
|
4559
|
+
console.log('');
|
|
4560
|
+
}
|
|
4304
4561
|
return await cancellationManager.withCancellation(
|
|
4305
4562
|
tool.execute(params, executionMode),
|
|
4306
4563
|
`tool-${toolName}`
|
|
4307
4564
|
);
|
|
4565
|
+
} else {
|
|
4566
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4567
|
+
sdkOutputAdapter.outputWarning(`[Smart Mode] User cancelled execution of tool '${toolName}'`);
|
|
4568
|
+
} else {
|
|
4569
|
+
console.log('');
|
|
4570
|
+
console.log(
|
|
4571
|
+
`${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled execution of tool '${toolName}'`)}`
|
|
4572
|
+
);
|
|
4573
|
+
console.log('');
|
|
4574
|
+
}
|
|
4575
|
+
throw new Error(`Tool execution cancelled by user: ${toolName}`);
|
|
4576
|
+
}
|
|
4577
|
+
} else {
|
|
4578
|
+
// Rejected execution
|
|
4579
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4580
|
+
sdkOutputAdapter.outputError(`[Smart Mode] Tool '${toolName}' execution rejected`, { reason: result.description });
|
|
4308
4581
|
} else {
|
|
4309
4582
|
console.log('');
|
|
4310
4583
|
console.log(
|
|
4311
|
-
`${indent}${colors.
|
|
4584
|
+
`${indent}${colors.error(`❌ [Smart Mode] Tool '${toolName}' execution rejected`)}`
|
|
4312
4585
|
);
|
|
4586
|
+
console.log(`${indent}${colors.textDim(` Reason: ${result.description}`)}`);
|
|
4313
4587
|
console.log('');
|
|
4314
|
-
throw new Error(`Tool execution cancelled by user: ${toolName}`);
|
|
4315
4588
|
}
|
|
4316
|
-
} else {
|
|
4317
|
-
// Rejected execution
|
|
4318
|
-
console.log('');
|
|
4319
|
-
console.log(
|
|
4320
|
-
`${indent}${colors.error(`❌ [Smart Mode] Tool '${toolName}' execution rejected`)}`
|
|
4321
|
-
);
|
|
4322
|
-
console.log(`${indent}${colors.textDim(` Reason: ${result.description}`)}`);
|
|
4323
|
-
console.log('');
|
|
4324
4589
|
throw new Error(`Tool execution rejected: ${toolName}`);
|
|
4325
4590
|
}
|
|
4326
4591
|
}
|
|
@@ -4352,13 +4617,18 @@ export class ToolRegistry {
|
|
|
4352
4617
|
|
|
4353
4618
|
// Get server info for display
|
|
4354
4619
|
const server = mcpManager.getServer(serverName);
|
|
4355
|
-
const
|
|
4620
|
+
const _serverTools = server?.getToolNames() || [];
|
|
4621
|
+
|
|
4622
|
+
const isSdkMode = this._isSdkMode;
|
|
4623
|
+
const sdkOutputAdapter = this._sdkOutputAdapter;
|
|
4356
4624
|
|
|
4357
4625
|
// Display tool call info
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4626
|
+
if (!isSdkMode || !sdkOutputAdapter) {
|
|
4627
|
+
console.log('');
|
|
4628
|
+
console.log(
|
|
4629
|
+
`${indent}${colors.warning(`${icons.tool} MCP Tool Call: ${serverName}::${actualToolName}`)}`
|
|
4630
|
+
);
|
|
4631
|
+
}
|
|
4362
4632
|
|
|
4363
4633
|
// Smart approval mode for MCP tools
|
|
4364
4634
|
if (executionMode === ExecutionMode.SMART) {
|
|
@@ -4371,12 +4641,21 @@ export class ToolRegistry {
|
|
|
4371
4641
|
|
|
4372
4642
|
// Remote mode: remote LLM has already approved the tool, auto-approve
|
|
4373
4643
|
if (isRemoteMode) {
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4644
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4645
|
+
sdkOutputAdapter.outputInfo(`[Smart Mode] Remote mode: MCP tool '${serverName}::${actualToolName}' auto-approved`);
|
|
4646
|
+
} else {
|
|
4647
|
+
console.log(
|
|
4648
|
+
`${indent}${colors.success(`✅ [Smart Mode] Remote mode: MCP tool '${serverName}::${actualToolName}' auto-approved`)}`
|
|
4649
|
+
);
|
|
4650
|
+
}
|
|
4377
4651
|
} else {
|
|
4378
4652
|
const approvalEngine = getSmartApprovalEngine(debugMode);
|
|
4379
4653
|
|
|
4654
|
+
// Set SDK mode for approval engine if in SDK mode
|
|
4655
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4656
|
+
approvalEngine.setSdkMode(true, sdkOutputAdapter);
|
|
4657
|
+
}
|
|
4658
|
+
|
|
4380
4659
|
// Evaluate MCP tool call
|
|
4381
4660
|
const result = await approvalEngine.evaluate({
|
|
4382
4661
|
toolName: `MCP[${serverName}]::${actualToolName}`,
|
|
@@ -4385,23 +4664,40 @@ export class ToolRegistry {
|
|
|
4385
4664
|
});
|
|
4386
4665
|
|
|
4387
4666
|
if (result.decision === 'approved') {
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
const confirmed = await approvalEngine.requestConfirmation(result);
|
|
4396
|
-
if (!confirmed) {
|
|
4667
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4668
|
+
sdkOutputAdapter.outputInfo(`[Smart Mode] MCP tool '${serverName}::${actualToolName}' passed approval`);
|
|
4669
|
+
sdkOutputAdapter.outputInfo(`Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`);
|
|
4670
|
+
} else {
|
|
4671
|
+
console.log(
|
|
4672
|
+
`${indent}${colors.success(`✅ [Smart Mode] MCP tool '${serverName}::${actualToolName}' passed approval`)}`
|
|
4673
|
+
);
|
|
4397
4674
|
console.log(
|
|
4398
|
-
`${indent}${colors.
|
|
4675
|
+
`${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`
|
|
4399
4676
|
);
|
|
4677
|
+
}
|
|
4678
|
+
} else if (result.decision === 'requires_confirmation') {
|
|
4679
|
+
const confirmed = await approvalEngine.requestConfirmation(
|
|
4680
|
+
result,
|
|
4681
|
+
`MCP[${serverName}]::${actualToolName}`,
|
|
4682
|
+
params
|
|
4683
|
+
);
|
|
4684
|
+
if (!confirmed) {
|
|
4685
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4686
|
+
sdkOutputAdapter.outputWarning(`[Smart Mode] User cancelled MCP tool execution`);
|
|
4687
|
+
} else {
|
|
4688
|
+
console.log(
|
|
4689
|
+
`${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled MCP tool execution`)}`
|
|
4690
|
+
);
|
|
4691
|
+
}
|
|
4400
4692
|
throw new Error(`Tool execution cancelled by user: ${toolName}`);
|
|
4401
4693
|
}
|
|
4402
4694
|
} else {
|
|
4403
|
-
|
|
4404
|
-
|
|
4695
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4696
|
+
sdkOutputAdapter.outputError(`[Smart Mode] MCP tool execution rejected`, { reason: result.description });
|
|
4697
|
+
} else {
|
|
4698
|
+
console.log(`${indent}${colors.error(`❌ [Smart Mode] MCP tool execution rejected`)}`);
|
|
4699
|
+
console.log(`${indent}${colors.textDim(` Reason: ${result.description}`)}`);
|
|
4700
|
+
}
|
|
4405
4701
|
throw new Error(`Tool execution rejected: ${toolName}`);
|
|
4406
4702
|
}
|
|
4407
4703
|
}
|