@xagent-ai/cli 1.2.2 → 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/ISSUE_TEMPLATE/bug_report.md +38 -38
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
- package/.github/release.yml +76 -0
- package/.github/workflows/ci.yml +75 -0
- package/.github/workflows/release.yml +103 -0
- package/.gitmodules +3 -3
- package/README.md +326 -280
- package/README_CN.md +325 -279
- 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 +40 -0
- package/dist/ai-client/factory.d.ts.map +1 -0
- package/dist/ai-client/factory.js +100 -0
- package/dist/ai-client/factory.js.map +1 -0
- package/dist/ai-client/index.d.ts +20 -0
- package/dist/ai-client/index.d.ts.map +1 -0
- package/dist/ai-client/index.js +49 -0
- package/dist/ai-client/index.js.map +1 -0
- package/dist/ai-client/providers/anthropic.d.ts +57 -0
- package/dist/ai-client/providers/anthropic.d.ts.map +1 -0
- package/dist/ai-client/providers/anthropic.js +406 -0
- package/dist/ai-client/providers/anthropic.js.map +1 -0
- package/dist/ai-client/providers/openai.d.ts +57 -0
- package/dist/ai-client/providers/openai.d.ts.map +1 -0
- package/dist/ai-client/providers/openai.js +290 -0
- package/dist/ai-client/providers/openai.js.map +1 -0
- package/dist/ai-client/providers/remote.d.ts +110 -0
- package/dist/ai-client/providers/remote.d.ts.map +1 -0
- package/dist/ai-client/providers/remote.js +352 -0
- package/dist/ai-client/providers/remote.js.map +1 -0
- package/dist/ai-client/registry.d.ts +51 -0
- package/dist/ai-client/registry.d.ts.map +1 -0
- package/dist/ai-client/registry.js +81 -0
- package/dist/ai-client/registry.js.map +1 -0
- package/dist/ai-client/types.d.ts +274 -0
- package/dist/ai-client/types.d.ts.map +1 -0
- package/dist/ai-client/types.js +90 -0
- package/dist/ai-client/types.js.map +1 -0
- package/dist/ai-client-factory.d.ts +62 -0
- package/dist/ai-client-factory.d.ts.map +1 -0
- package/dist/ai-client-factory.js +157 -0
- package/dist/ai-client-factory.js.map +1 -0
- package/dist/auth.d.ts +23 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +164 -174
- package/dist/auth.js.map +1 -1
- package/dist/cancellation.d.ts +5 -4
- package/dist/cancellation.d.ts.map +1 -1
- package/dist/cancellation.js +53 -32
- package/dist/cancellation.js.map +1 -1
- package/dist/checkpoint.d.ts +2 -1
- package/dist/checkpoint.d.ts.map +1 -1
- package/dist/checkpoint.js +39 -6
- package/dist/checkpoint.js.map +1 -1
- package/dist/cli.js +742 -29
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +10 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +62 -25
- package/dist/config.js.map +1 -1
- package/dist/context-compressor.d.ts +82 -18
- package/dist/context-compressor.d.ts.map +1 -1
- package/dist/context-compressor.js +718 -154
- 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.d.ts.map +1 -1
- package/dist/gui-subagent/action-parser/actionParser.js +6 -4
- package/dist/gui-subagent/action-parser/actionParser.js.map +1 -1
- package/dist/gui-subagent/agent/gui-agent.d.ts +39 -2
- package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -1
- package/dist/gui-subagent/agent/gui-agent.js +189 -74
- package/dist/gui-subagent/agent/gui-agent.js.map +1 -1
- package/dist/gui-subagent/index.d.ts +23 -1
- package/dist/gui-subagent/index.d.ts.map +1 -1
- package/dist/gui-subagent/index.js +6 -0
- package/dist/gui-subagent/index.js.map +1 -1
- package/dist/gui-subagent/operator/base-operator.d.ts.map +1 -1
- package/dist/gui-subagent/operator/base-operator.js +0 -1
- package/dist/gui-subagent/operator/base-operator.js.map +1 -1
- package/dist/gui-subagent/operator/computer-operator.d.ts.map +1 -1
- package/dist/gui-subagent/operator/computer-operator.js +31 -8
- package/dist/gui-subagent/operator/computer-operator.js.map +1 -1
- package/dist/gui-subagent/types/actions.d.ts +1 -1
- package/dist/gui-subagent/types/actions.d.ts.map +1 -1
- package/dist/gui-subagent/types/actions.js +0 -1
- package/dist/gui-subagent/types/actions.js.map +1 -1
- package/dist/gui-subagent/types/operator.d.ts +1 -1
- package/dist/gui-subagent/types/operator.d.ts.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/input-processor.d.ts.map +1 -1
- package/dist/input-processor.js +8 -5
- 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 +7 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +157 -49
- 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 +29 -0
- package/dist/ripgrep.d.ts.map +1 -0
- package/dist/ripgrep.js +294 -0
- package/dist/ripgrep.js.map +1 -0
- package/dist/sdk-output-adapter.d.ts +34 -1
- package/dist/sdk-output-adapter.d.ts.map +1 -1
- package/dist/sdk-output-adapter.js +67 -2
- package/dist/sdk-output-adapter.js.map +1 -1
- package/dist/sdk-session.d.ts.map +1 -1
- package/dist/sdk-session.js +2 -0
- package/dist/sdk-session.js.map +1 -1
- package/dist/session-manager.js +3 -3
- package/dist/session-manager.js.map +1 -1
- package/dist/session.d.ts +116 -6
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +1416 -448
- package/dist/session.js.map +1 -1
- package/dist/shell.d.ts +33 -0
- package/dist/shell.d.ts.map +1 -0
- package/dist/shell.js +126 -0
- package/dist/shell.js.map +1 -0
- package/dist/skill-installer.d.ts +38 -0
- package/dist/skill-installer.d.ts.map +1 -0
- package/dist/skill-installer.js +447 -0
- package/dist/skill-installer.js.map +1 -0
- package/dist/skill-invoker.d.ts +8 -2
- package/dist/skill-invoker.d.ts.map +1 -1
- package/dist/skill-invoker.js +36 -15
- package/dist/skill-invoker.js.map +1 -1
- package/dist/skill-loader.d.ts +8 -3
- package/dist/skill-loader.d.ts.map +1 -1
- package/dist/skill-loader.js +51 -48
- package/dist/skill-loader.js.map +1 -1
- package/dist/skill-manager.d.ts +85 -0
- package/dist/skill-manager.d.ts.map +1 -0
- package/dist/skill-manager.js +341 -0
- package/dist/skill-manager.js.map +1 -0
- package/dist/slash-commands.d.ts +39 -2
- package/dist/slash-commands.d.ts.map +1 -1
- package/dist/slash-commands.js +934 -305
- 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 +125 -56
- package/dist/smart-approval.js.map +1 -1
- package/dist/system-prompt-generator.d.ts +6 -0
- package/dist/system-prompt-generator.d.ts.map +1 -1
- package/dist/system-prompt-generator.js +86 -36
- package/dist/system-prompt-generator.js.map +1 -1
- package/dist/terminal.d.ts +28 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +82 -0
- package/dist/terminal.js.map +1 -0
- 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 +38 -7
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +1249 -617
- package/dist/tools.js.map +1 -1
- package/dist/truncate.d.ts +55 -0
- package/dist/truncate.d.ts.map +1 -0
- package/dist/truncate.js +130 -0
- package/dist/truncate.js.map +1 -0
- package/dist/types.d.ts +84 -9
- 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 +28 -36
- package/dist/update.js.map +1 -1
- package/dist/workflow.d.ts +5 -1
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +61 -49
- package/dist/workflow.js.map +1 -1
- package/docs/architecture/mcp-integration-guide.md +304 -194
- package/docs/architecture/overview.md +169 -169
- package/docs/architecture/tool-system-design.md +134 -134
- package/docs/cli/commands.md +349 -238
- package/docs/smart-mode.md +281 -281
- package/docs/third-party-models.md +440 -439
- package/find-skills/SKILL.md +133 -0
- package/package.json +91 -90
- package/scripts/install-ripgrep.js +241 -0
- package/src/agents.ts +7 -3
- package/src/ai-client/factory.ts +116 -0
- package/src/ai-client/index.ts +61 -0
- package/src/ai-client/providers/anthropic.ts +475 -0
- package/src/ai-client/providers/openai.ts +348 -0
- package/src/ai-client/providers/remote.ts +439 -0
- package/src/ai-client/registry.ts +97 -0
- package/src/ai-client/types.ts +364 -0
- package/src/ai-client-factory.ts +204 -0
- package/src/auth.ts +661 -614
- package/src/cancellation.ts +202 -176
- package/src/checkpoint.ts +255 -219
- package/src/cli.ts +1523 -743
- package/src/config.ts +341 -297
- package/src/context-compressor.ts +987 -290
- package/src/conversation.ts +290 -288
- package/src/gui-subagent/action-parser/actionParser.ts +318 -315
- package/src/gui-subagent/action-parser/constants.ts +14 -14
- package/src/gui-subagent/action-parser/index.ts +8 -8
- package/src/gui-subagent/action-parser/types.ts +31 -31
- package/src/gui-subagent/agent/gui-agent.ts +1234 -1089
- package/src/gui-subagent/agent/index.ts +5 -5
- package/src/gui-subagent/index.ts +185 -163
- package/src/gui-subagent/operator/base-operator.ts +244 -245
- package/src/gui-subagent/operator/computer-operator.ts +541 -520
- package/src/gui-subagent/operator/index.ts +6 -6
- package/src/gui-subagent/types/actions.ts +260 -262
- package/src/gui-subagent/types/index.ts +6 -6
- package/src/gui-subagent/types/operator.ts +106 -106
- package/src/gui-subagent/utils.ts +51 -51
- package/src/index.ts +17 -18
- package/src/input-processor.ts +8 -5
- package/src/logger.ts +436 -438
- package/src/mcp.ts +793 -682
- package/src/memory.ts +343 -344
- package/src/output-util.ts +80 -0
- package/src/retry.ts +1 -1
- package/src/ripgrep.ts +370 -0
- package/src/sdk-output-adapter.ts +842 -0
- package/src/sdk-session.ts +62 -0
- package/src/session-manager.ts +308 -308
- package/src/session.ts +1775 -573
- package/src/shell.ts +134 -0
- package/src/skill-installer.ts +518 -0
- package/src/skill-invoker.ts +959 -935
- package/src/skill-loader.ts +501 -496
- package/src/skill-manager.ts +385 -0
- package/src/slash-commands.ts +2189 -1389
- package/src/smart-approval.ts +193 -74
- package/src/system-prompt-generator.ts +91 -36
- package/src/terminal.ts +96 -0
- package/src/theme.ts +739 -738
- package/src/tools.ts +1790 -931
- package/src/truncate.ts +173 -0
- package/src/types.ts +337 -198
- package/src/update.ts +33 -40
- package/src/workflow.ts +521 -508
- package/test/cli-launch.test.ts +279 -0
- package/tsconfig.json +22 -22
- package/vitest.config.ts +21 -19
- package/dist/ai-client.d.ts +0 -86
- package/dist/ai-client.d.ts.map +0 -1
- package/dist/ai-client.js +0 -1372
- package/dist/ai-client.js.map +0 -1
- package/dist/gui-subagent/operator/browser-operator.d.ts +0 -36
- package/dist/gui-subagent/operator/browser-operator.d.ts.map +0 -1
- package/dist/gui-subagent/operator/browser-operator.js +0 -306
- package/dist/gui-subagent/operator/browser-operator.js.map +0 -1
- package/dist/gui-subagent/operator/desktop-operator.d.ts +0 -55
- package/dist/gui-subagent/operator/desktop-operator.d.ts.map +0 -1
- package/dist/gui-subagent/operator/desktop-operator.js +0 -527
- package/dist/gui-subagent/operator/desktop-operator.js.map +0 -1
- package/dist/hook.d.ts +0 -73
- package/dist/hook.d.ts.map +0 -1
- package/dist/hook.js +0 -156
- package/dist/hook.js.map +0 -1
- package/dist/input-history.d.ts +0 -24
- package/dist/input-history.d.ts.map +0 -1
- package/dist/input-history.js +0 -94
- package/dist/input-history.js.map +0 -1
- package/dist/keyboard-manager.d.ts +0 -151
- package/dist/keyboard-manager.d.ts.map +0 -1
- package/dist/keyboard-manager.js +0 -396
- package/dist/keyboard-manager.js.map +0 -1
- package/dist/print-system-prompt.d.ts +0 -2
- package/dist/print-system-prompt.d.ts.map +0 -1
- package/dist/print-system-prompt.js +0 -40
- package/dist/print-system-prompt.js.map +0 -1
- package/dist/remote-ai-client.d.ts +0 -104
- package/dist/remote-ai-client.d.ts.map +0 -1
- package/dist/remote-ai-client.js +0 -552
- package/dist/remote-ai-client.js.map +0 -1
- package/dist/sdk-session-v2.d.ts +0 -13
- package/dist/sdk-session-v2.d.ts.map +0 -1
- package/dist/sdk-session-v2.js +0 -46
- package/dist/sdk-session-v2.js.map +0 -1
- package/dist/test-boundary-conditions.d.ts.map +0 -1
- package/dist/test-boundary-conditions.js.map +0 -1
- package/dist/test-cancellation-fix.d.ts.map +0 -1
- package/dist/test-cancellation-fix.js.map +0 -1
- package/dist/test-input-history.d.ts.map +0 -1
- package/dist/test-input-history.js.map +0 -1
- package/dist/test-interaction-flow.d.ts.map +0 -1
- package/dist/test-interaction-flow.js.map +0 -1
- package/dist/test-quick.d.ts.map +0 -1
- package/dist/test-quick.js.map +0 -1
- package/dist/test-user-interaction.d.ts.map +0 -1
- package/dist/test-user-interaction.js.map +0 -1
- package/dist/tools/edit-diff.d.ts +0 -32
- package/dist/tools/edit-diff.d.ts.map +0 -1
- package/dist/tools/edit-diff.js +0 -185
- package/dist/tools/edit-diff.js.map +0 -1
- package/dist/tools/edit.d.ts +0 -11
- package/dist/tools/edit.d.ts.map +0 -1
- package/dist/tools/edit.js +0 -129
- package/dist/tools/edit.js.map +0 -1
- package/dist/unified-session.d.ts +0 -42
- package/dist/unified-session.d.ts.map +0 -1
- package/dist/unified-session.js +0 -271
- package/dist/unified-session.js.map +0 -1
- package/skills/.claude-plugin/marketplace.json +0 -45
- package/skills/README.md +0 -94
- package/skills/THIRD_PARTY_NOTICES.md +0 -405
- package/skills/skills/algorithmic-art/LICENSE.txt +0 -202
- package/skills/skills/algorithmic-art/SKILL.md +0 -405
- package/skills/skills/algorithmic-art/templates/generator_template.js +0 -223
- package/skills/skills/algorithmic-art/templates/viewer.html +0 -599
- package/skills/skills/brand-guidelines/LICENSE.txt +0 -202
- package/skills/skills/brand-guidelines/SKILL.md +0 -73
- package/skills/skills/canvas-design/LICENSE.txt +0 -202
- package/skills/skills/canvas-design/SKILL.md +0 -130
- package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +0 -94
- package/skills/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Jura-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Lora-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
- package/skills/skills/doc-coauthoring/SKILL.md +0 -375
- package/skills/skills/docx/LICENSE.txt +0 -30
- package/skills/skills/docx/SKILL.md +0 -197
- package/skills/skills/docx/docx-js.md +0 -350
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/skills/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
- package/skills/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
- package/skills/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
- package/skills/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
- package/skills/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/skills/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/skills/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/skills/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/skills/skills/docx/ooxml/scripts/pack.py +0 -159
- package/skills/skills/docx/ooxml/scripts/unpack.py +0 -29
- package/skills/skills/docx/ooxml/scripts/validate.py +0 -69
- package/skills/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
- package/skills/skills/docx/ooxml/scripts/validation/base.py +0 -951
- package/skills/skills/docx/ooxml/scripts/validation/docx.py +0 -274
- package/skills/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
- package/skills/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
- package/skills/skills/docx/ooxml.md +0 -610
- package/skills/skills/docx/scripts/__init__.py +0 -1
- package/skills/skills/docx/scripts/document.py +0 -1276
- package/skills/skills/docx/scripts/templates/comments.xml +0 -3
- package/skills/skills/docx/scripts/templates/commentsExtended.xml +0 -3
- package/skills/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
- package/skills/skills/docx/scripts/templates/commentsIds.xml +0 -3
- package/skills/skills/docx/scripts/templates/people.xml +0 -3
- package/skills/skills/docx/scripts/utilities.py +0 -374
- package/skills/skills/frontend-design/LICENSE.txt +0 -177
- package/skills/skills/frontend-design/SKILL.md +0 -42
- package/skills/skills/internal-comms/LICENSE.txt +0 -202
- package/skills/skills/internal-comms/SKILL.md +0 -32
- package/skills/skills/internal-comms/examples/3p-updates.md +0 -47
- package/skills/skills/internal-comms/examples/company-newsletter.md +0 -65
- package/skills/skills/internal-comms/examples/faq-answers.md +0 -30
- package/skills/skills/internal-comms/examples/general-comms.md +0 -16
- package/skills/skills/mcp-builder/LICENSE.txt +0 -202
- package/skills/skills/mcp-builder/SKILL.md +0 -236
- package/skills/skills/mcp-builder/reference/evaluation.md +0 -602
- package/skills/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
- package/skills/skills/mcp-builder/reference/node_mcp_server.md +0 -970
- package/skills/skills/mcp-builder/reference/python_mcp_server.md +0 -719
- package/skills/skills/mcp-builder/scripts/connections.py +0 -151
- package/skills/skills/mcp-builder/scripts/evaluation.py +0 -373
- package/skills/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
- package/skills/skills/mcp-builder/scripts/requirements.txt +0 -2
- package/skills/skills/pdf/LICENSE.txt +0 -30
- package/skills/skills/pdf/SKILL.md +0 -294
- package/skills/skills/pdf/forms.md +0 -205
- package/skills/skills/pdf/reference.md +0 -612
- package/skills/skills/pdf/scripts/check_bounding_boxes.py +0 -70
- package/skills/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
- package/skills/skills/pdf/scripts/check_fillable_fields.py +0 -12
- package/skills/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
- package/skills/skills/pdf/scripts/create_validation_image.py +0 -41
- package/skills/skills/pdf/scripts/extract_form_field_info.py +0 -152
- package/skills/skills/pdf/scripts/fill_fillable_fields.py +0 -114
- package/skills/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
- package/skills/skills/pptx/LICENSE.txt +0 -30
- package/skills/skills/pptx/SKILL.md +0 -484
- package/skills/skills/pptx/html2pptx.md +0 -625
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/skills/skills/pptx/ooxml/schemas/mce/mc.xsd +0 -75
- package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
- package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
- package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
- package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/skills/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/skills/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/skills/skills/pptx/ooxml/scripts/pack.py +0 -159
- package/skills/skills/pptx/ooxml/scripts/unpack.py +0 -29
- package/skills/skills/pptx/ooxml/scripts/validate.py +0 -69
- package/skills/skills/pptx/ooxml/scripts/validation/__init__.py +0 -15
- package/skills/skills/pptx/ooxml/scripts/validation/base.py +0 -951
- package/skills/skills/pptx/ooxml/scripts/validation/docx.py +0 -274
- package/skills/skills/pptx/ooxml/scripts/validation/pptx.py +0 -315
- package/skills/skills/pptx/ooxml/scripts/validation/redlining.py +0 -279
- package/skills/skills/pptx/ooxml.md +0 -427
- package/skills/skills/pptx/scripts/html2pptx.js +0 -979
- package/skills/skills/pptx/scripts/inventory.py +0 -1020
- package/skills/skills/pptx/scripts/rearrange.py +0 -231
- package/skills/skills/pptx/scripts/replace.py +0 -385
- package/skills/skills/pptx/scripts/thumbnail.py +0 -450
- package/skills/skills/skill-creator/LICENSE.txt +0 -202
- package/skills/skills/skill-creator/SKILL.md +0 -356
- package/skills/skills/skill-creator/references/output-patterns.md +0 -82
- package/skills/skills/skill-creator/references/workflows.md +0 -28
- package/skills/skills/skill-creator/scripts/init_skill.py +0 -303
- package/skills/skills/skill-creator/scripts/package_skill.py +0 -110
- package/skills/skills/skill-creator/scripts/quick_validate.py +0 -95
- package/skills/skills/slack-gif-creator/LICENSE.txt +0 -202
- package/skills/skills/slack-gif-creator/SKILL.md +0 -254
- package/skills/skills/slack-gif-creator/core/easing.py +0 -234
- package/skills/skills/slack-gif-creator/core/frame_composer.py +0 -176
- package/skills/skills/slack-gif-creator/core/gif_builder.py +0 -269
- package/skills/skills/slack-gif-creator/core/validators.py +0 -136
- package/skills/skills/slack-gif-creator/requirements.txt +0 -4
- package/skills/skills/theme-factory/LICENSE.txt +0 -202
- package/skills/skills/theme-factory/SKILL.md +0 -59
- package/skills/skills/theme-factory/theme-showcase.pdf +0 -0
- package/skills/skills/theme-factory/themes/arctic-frost.md +0 -19
- package/skills/skills/theme-factory/themes/botanical-garden.md +0 -19
- package/skills/skills/theme-factory/themes/desert-rose.md +0 -19
- package/skills/skills/theme-factory/themes/forest-canopy.md +0 -19
- package/skills/skills/theme-factory/themes/golden-hour.md +0 -19
- package/skills/skills/theme-factory/themes/midnight-galaxy.md +0 -19
- package/skills/skills/theme-factory/themes/modern-minimalist.md +0 -19
- package/skills/skills/theme-factory/themes/ocean-depths.md +0 -19
- package/skills/skills/theme-factory/themes/sunset-boulevard.md +0 -19
- package/skills/skills/theme-factory/themes/tech-innovation.md +0 -19
- package/skills/skills/web-artifacts-builder/LICENSE.txt +0 -202
- package/skills/skills/web-artifacts-builder/SKILL.md +0 -74
- package/skills/skills/web-artifacts-builder/scripts/bundle-artifact.sh +0 -54
- package/skills/skills/web-artifacts-builder/scripts/init-artifact.sh +0 -322
- package/skills/skills/webapp-testing/LICENSE.txt +0 -202
- package/skills/skills/webapp-testing/SKILL.md +0 -96
- package/skills/skills/webapp-testing/examples/console_logging.py +0 -35
- package/skills/skills/webapp-testing/examples/element_discovery.py +0 -40
- package/skills/skills/webapp-testing/examples/static_html_automation.py +0 -33
- package/skills/skills/webapp-testing/scripts/with_server.py +0 -106
- package/skills/skills/xlsx/LICENSE.txt +0 -30
- package/skills/skills/xlsx/SKILL.md +0 -289
- package/skills/skills/xlsx/recalc.py +0 -178
- package/skills/spec/agent-skills-spec.md +0 -3
- package/skills/template/SKILL.md +0 -6
- package/src/ai-client.ts +0 -1560
- package/src/remote-ai-client.ts +0 -664
- /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
package/dist/tools.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
|
+
import { select, text } from '@clack/prompts';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
import { fileURLToPath } from 'url';
|
|
4
5
|
import readline from 'readline';
|
|
5
|
-
import {
|
|
6
|
-
import { promisify } from 'util';
|
|
6
|
+
import { spawn } from 'child_process';
|
|
7
7
|
import { glob } from 'glob';
|
|
8
8
|
import axios from 'axios';
|
|
9
|
-
import inquirer from 'inquirer';
|
|
10
9
|
import { ExecutionMode, AuthType } from './types.js';
|
|
11
10
|
import { colors, icons } from './theme.js';
|
|
12
11
|
import { getLogger } from './logger.js';
|
|
13
12
|
import { getCancellationManager } from './cancellation.js';
|
|
14
13
|
import { SystemPromptGenerator } from './system-prompt-generator.js';
|
|
15
|
-
|
|
14
|
+
import { getSingletonSession } from './session.js';
|
|
15
|
+
import { ripgrep, fdFind } from './ripgrep.js';
|
|
16
|
+
import { getShellConfig, killProcessTree, quoteShellCommand } from './shell.js';
|
|
17
|
+
import { truncateTail, buildTruncationNotice } from './truncate.js';
|
|
18
|
+
import { createAIClient } from './ai-client-factory.js';
|
|
16
19
|
//
|
|
17
20
|
// Tool Description Pattern
|
|
18
21
|
//
|
|
@@ -64,8 +67,16 @@ export class ReadTool {
|
|
|
64
67
|
- Use offset and limit for large files to avoid loading entire content
|
|
65
68
|
- Combine with ListDirectory to explore project structure first
|
|
66
69
|
- Don't re-read files unnecessarily`;
|
|
67
|
-
allowedModes = [
|
|
70
|
+
allowedModes = [
|
|
71
|
+
ExecutionMode.YOLO,
|
|
72
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
73
|
+
ExecutionMode.PLAN,
|
|
74
|
+
ExecutionMode.SMART,
|
|
75
|
+
];
|
|
68
76
|
async execute(params) {
|
|
77
|
+
if (!params || typeof params.filePath !== 'string') {
|
|
78
|
+
throw new Error('filePath is required and must be a string');
|
|
79
|
+
}
|
|
69
80
|
const { filePath, offset = 0, limit } = params;
|
|
70
81
|
try {
|
|
71
82
|
// Handle ~ (user home directory) in file paths
|
|
@@ -82,10 +93,18 @@ export class ReadTool {
|
|
|
82
93
|
const absolutePath = path.resolve(resolvedPath);
|
|
83
94
|
const content = await fs.readFile(absolutePath, 'utf-8');
|
|
84
95
|
const lines = content.split('\n');
|
|
96
|
+
const totalLines = lines.length;
|
|
85
97
|
const startLine = Math.max(0, offset);
|
|
86
|
-
const endLine = limit !== undefined ? Math.min(
|
|
98
|
+
const endLine = limit !== undefined ? Math.min(totalLines, startLine + limit) : totalLines;
|
|
87
99
|
const selectedLines = lines.slice(startLine, endLine);
|
|
88
|
-
|
|
100
|
+
const result = selectedLines.join('\n');
|
|
101
|
+
// Add truncation notice if content is limited
|
|
102
|
+
if (limit !== undefined && endLine < totalLines) {
|
|
103
|
+
const remaining = totalLines - endLine;
|
|
104
|
+
const nextOffset = endLine;
|
|
105
|
+
return (result + `\n\n[${remaining} more lines in file. Use offset=${nextOffset} to continue]`);
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
89
108
|
}
|
|
90
109
|
catch (error) {
|
|
91
110
|
// Show user-friendly path in error message
|
|
@@ -146,7 +165,7 @@ export class WriteTool {
|
|
|
146
165
|
message: `Successfully wrote to ${filePath}`,
|
|
147
166
|
filePath,
|
|
148
167
|
lineCount,
|
|
149
|
-
preview: isTruncated ? preview + '\n...' : preview
|
|
168
|
+
preview: isTruncated ? preview + '\n...' : preview,
|
|
150
169
|
};
|
|
151
170
|
}
|
|
152
171
|
catch (error) {
|
|
@@ -156,7 +175,7 @@ export class WriteTool {
|
|
|
156
175
|
}
|
|
157
176
|
export class GrepTool {
|
|
158
177
|
name = 'Grep';
|
|
159
|
-
description = `Search for text patterns within files using
|
|
178
|
+
description = `Search for text patterns within files using ripgrep. This is your PRIMARY tool for finding specific code, functions, or content.
|
|
160
179
|
|
|
161
180
|
# When to Use
|
|
162
181
|
- Finding specific function definitions or calls
|
|
@@ -173,89 +192,41 @@ export class GrepTool {
|
|
|
173
192
|
# Parameters
|
|
174
193
|
- \`pattern\`: Regex or literal string to search for
|
|
175
194
|
- \`path\`: (Optional) Directory to search in, default: "."
|
|
176
|
-
- \`
|
|
177
|
-
- \`
|
|
178
|
-
- \`
|
|
179
|
-
- \`fixed_strings\`: (Optional) Treat pattern as literal string, default: false
|
|
195
|
+
- \`glob\`: (Optional) File glob pattern to include (e.g., "*.ts", "**/*.js")
|
|
196
|
+
- \`ignoreCase\`: (Optional) Case-insensitive search, default: false
|
|
197
|
+
- \`literal\`: (Optional) Treat pattern as literal string, default: false
|
|
180
198
|
- \`context\`: (Optional) Lines of context before/after matches
|
|
181
|
-
- \`no_ignore\`: (Optional) Don't ignore node_modules/.git, default: false
|
|
182
199
|
|
|
183
200
|
# Examples
|
|
184
201
|
- Find function: Grep(pattern="function myFunction")
|
|
185
202
|
- Find with context: Grep(pattern="TODO", context=3)
|
|
186
|
-
- TypeScript only: Grep(pattern="interface",
|
|
203
|
+
- TypeScript only: Grep(pattern="interface", glob="*.ts")
|
|
204
|
+
- Case-insensitive: Grep(pattern="error", ignoreCase=true)
|
|
187
205
|
|
|
188
206
|
# Best Practices
|
|
189
|
-
- Use
|
|
190
|
-
- Use
|
|
207
|
+
- Use ignoreCase=true for short patterns to reduce false positives
|
|
208
|
+
- Use literal=true if your pattern has special regex characters
|
|
191
209
|
- Use context to see the surrounding code for each match
|
|
192
|
-
- Combine with
|
|
193
|
-
allowedModes = [
|
|
210
|
+
- Combine with glob to narrow down file types`;
|
|
211
|
+
allowedModes = [
|
|
212
|
+
ExecutionMode.YOLO,
|
|
213
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
214
|
+
ExecutionMode.PLAN,
|
|
215
|
+
ExecutionMode.SMART,
|
|
216
|
+
];
|
|
194
217
|
async execute(params) {
|
|
195
|
-
const { pattern, path: searchPath = '.',
|
|
218
|
+
const { pattern, path: searchPath = '.', glob: includeGlob, ignoreCase = false, literal = false, context, limit, } = params;
|
|
196
219
|
try {
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
ignore: ignorePatterns
|
|
220
|
+
const result = await ripgrep({
|
|
221
|
+
pattern,
|
|
222
|
+
path: searchPath,
|
|
223
|
+
glob: includeGlob,
|
|
224
|
+
ignoreCase,
|
|
225
|
+
literal,
|
|
226
|
+
context,
|
|
227
|
+
limit,
|
|
206
228
|
});
|
|
207
|
-
|
|
208
|
-
for (const file of files) {
|
|
209
|
-
const fullPath = path.join(absolutePath, file);
|
|
210
|
-
if (include && !file.match(include)) {
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
try {
|
|
214
|
-
const content = await fs.readFile(fullPath, 'utf-8');
|
|
215
|
-
const lines = content.split('\n');
|
|
216
|
-
lines.forEach((line, index) => {
|
|
217
|
-
let matches = false;
|
|
218
|
-
if (fixed_strings) {
|
|
219
|
-
matches = case_sensitive
|
|
220
|
-
? line.includes(pattern)
|
|
221
|
-
: line.toLowerCase().includes(pattern.toLowerCase());
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
224
|
-
try {
|
|
225
|
-
const flags = case_sensitive ? 'g' : 'gi';
|
|
226
|
-
const regex = new RegExp(pattern, flags);
|
|
227
|
-
matches = regex.test(line);
|
|
228
|
-
}
|
|
229
|
-
catch (e) {
|
|
230
|
-
matches = case_sensitive
|
|
231
|
-
? line.includes(pattern)
|
|
232
|
-
: line.toLowerCase().includes(pattern.toLowerCase());
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
if (matches) {
|
|
236
|
-
const contextLines = [];
|
|
237
|
-
if (before || context) {
|
|
238
|
-
const beforeCount = before || context || 0;
|
|
239
|
-
for (let i = Math.max(0, index - beforeCount); i < index; i++) {
|
|
240
|
-
contextLines.push(`${fullPath}:${i + 1}:${lines[i].trim()}`);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
contextLines.push(`${fullPath}:${index + 1}:${line.trim()}`);
|
|
244
|
-
if (after || context) {
|
|
245
|
-
const afterCount = after || context || 0;
|
|
246
|
-
for (let i = index + 1; i < Math.min(lines.length, index + 1 + afterCount); i++) {
|
|
247
|
-
contextLines.push(`${fullPath}:${i + 1}:${lines[i].trim()}`);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
results.push(...contextLines);
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
catch (error) {
|
|
255
|
-
continue;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
return results;
|
|
229
|
+
return result.split('\n').filter((line) => line.trim());
|
|
259
230
|
}
|
|
260
231
|
catch (error) {
|
|
261
232
|
throw new Error(`Grep failed: ${error.message}`);
|
|
@@ -287,13 +258,23 @@ export class BashTool {
|
|
|
287
258
|
- \`description\`: (Optional) Description of what the command does
|
|
288
259
|
- \`timeout\`: (Optional) Timeout in seconds, default: 120
|
|
289
260
|
- \`run_in_bg\`: (Optional) Run in background, default: false
|
|
261
|
+
- \`skillPath\`: (Optional) Skill directory path - when provided, NODE_PATH will include the skill's node_modules for dependency resolution
|
|
290
262
|
|
|
291
263
|
# Examples
|
|
292
264
|
- Install dependencies: Bash(command="npm install", description="Install npm dependencies")
|
|
265
|
+
- Run in skill directory with local deps: Bash(command="npm install docx", skillPath="~/.xagent/skills/docx")
|
|
266
|
+
|
|
267
|
+
# NODE_PATH Resolution
|
|
268
|
+
When \`skillPath\` is provided, the command will have access to:
|
|
269
|
+
- \`<skillPath>/node_modules\` (skill's local dependencies)
|
|
270
|
+
- xAgent's global node_modules
|
|
271
|
+
|
|
272
|
+
This is useful when working with skills that have local dependencies.
|
|
293
273
|
- Run tests: Bash(command="npm test", description="Run unit tests")
|
|
294
274
|
- Build project: Bash(command="npm run build", description="Build the project")
|
|
295
275
|
|
|
296
276
|
# Best Practices
|
|
277
|
+
- To install npm packages that persist across sessions, use: \`XAGENT_USER_NPM=1 npm install <package>\`
|
|
297
278
|
- Always provide a description for context
|
|
298
279
|
- Set appropriate timeout for long-running commands
|
|
299
280
|
- Use run_in_bg=true for commands that take a long time
|
|
@@ -301,7 +282,9 @@ export class BashTool {
|
|
|
301
282
|
- Use absolute paths or paths relative to project root`;
|
|
302
283
|
allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
|
|
303
284
|
async execute(params) {
|
|
304
|
-
const { command, cwd, description, timeout = 120, run_in_bg = false } = params;
|
|
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;
|
|
305
288
|
// Determine effective working directory
|
|
306
289
|
// Only use cwd if the command doesn't contain 'cd' (let LLM control directory)
|
|
307
290
|
let effectiveCwd;
|
|
@@ -318,26 +301,104 @@ export class BashTool {
|
|
|
318
301
|
// No cwd provided, use default
|
|
319
302
|
effectiveCwd = undefined;
|
|
320
303
|
}
|
|
321
|
-
//
|
|
322
|
-
const
|
|
304
|
+
// Resolve actual working directory
|
|
305
|
+
const actualCwd = effectiveCwd || process.cwd();
|
|
306
|
+
// Set up environment with NODE_PATH for node commands
|
|
307
|
+
const builtinNodeModulesPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'node_modules');
|
|
308
|
+
// Get user skills path from config (unified path: ~/.xagent/skills)
|
|
309
|
+
const { getConfigManager } = await import('./config.js');
|
|
310
|
+
const configManager = getConfigManager();
|
|
311
|
+
const userSkillsPath = configManager.getUserSkillsPath();
|
|
312
|
+
// Skill deps path: ~/.xagent/skills/{skillName}/node_modules
|
|
313
|
+
const builtinDepsPath = userSkillsPath ? path.join(userSkillsPath, 'builtin-deps') : null;
|
|
314
|
+
// Determine which node_modules to use
|
|
315
|
+
let skillNodeModulesPath = null;
|
|
316
|
+
// Priority 1: skillPath parameter (workspace scenario - LLM works in workspace, not skill dir)
|
|
317
|
+
if (skillPath) {
|
|
318
|
+
if (skillPath.includes('/builtin-deps/')) {
|
|
319
|
+
// Skill with deps in builtin-deps directory
|
|
320
|
+
const match = skillPath.match(/\/builtin-deps\/([^/]+)/);
|
|
321
|
+
if (match) {
|
|
322
|
+
skillNodeModulesPath = path.join(builtinDepsPath, match[1], 'node_modules');
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
// Regular skill
|
|
327
|
+
skillNodeModulesPath = path.join(skillPath, 'node_modules');
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// Priority 2: Check if we're inside a skill directory
|
|
331
|
+
else if (userSkillsPath && userSkillsPath.trim() && actualCwd.startsWith(userSkillsPath)) {
|
|
332
|
+
const relativePath = actualCwd.substring(userSkillsPath.length);
|
|
333
|
+
const pathParts = relativePath.split(path.sep).filter(Boolean);
|
|
334
|
+
if (pathParts.length > 0) {
|
|
335
|
+
if (pathParts[0] === 'builtin-deps' && pathParts.length > 1) {
|
|
336
|
+
// Skill with local deps in builtin-deps
|
|
337
|
+
const skillName = pathParts[1];
|
|
338
|
+
skillNodeModulesPath = path.join(builtinDepsPath, skillName, 'node_modules');
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
// Regular skill
|
|
342
|
+
const skillName = pathParts[0];
|
|
343
|
+
const skillRoot = path.join(userSkillsPath, skillName);
|
|
344
|
+
try {
|
|
345
|
+
const skillMdPath = path.join(skillRoot, 'SKILL.md');
|
|
346
|
+
await fs.access(skillMdPath);
|
|
347
|
+
skillNodeModulesPath = path.join(skillRoot, 'node_modules');
|
|
348
|
+
}
|
|
349
|
+
catch {
|
|
350
|
+
// Not a skill directory, skip
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Build NODE_PATH - skill's node_modules takes precedence (last-wins)
|
|
356
|
+
let nodePath;
|
|
357
|
+
if (skillNodeModulesPath) {
|
|
358
|
+
nodePath = `${skillNodeModulesPath}${path.delimiter}${builtinNodeModulesPath}`;
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
nodePath = builtinNodeModulesPath;
|
|
362
|
+
}
|
|
323
363
|
const env = {
|
|
324
364
|
...process.env,
|
|
325
|
-
NODE_PATH:
|
|
365
|
+
NODE_PATH: nodePath
|
|
326
366
|
};
|
|
327
|
-
//
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
367
|
+
// Handle npm install commands
|
|
368
|
+
const isNpmInstall = /\bnpm\s+install\b/i.test(command);
|
|
369
|
+
let finalCommand = command;
|
|
370
|
+
if (isNpmInstall && skillNodeModulesPath) {
|
|
371
|
+
// Install to skill's own node_modules
|
|
372
|
+
await fs.mkdir(skillNodeModulesPath, { recursive: true }).catch(() => { });
|
|
373
|
+
finalCommand = command.replace(/\bnpm\s+install\b/i, `npm install --prefix "${skillNodeModulesPath}"`);
|
|
374
|
+
}
|
|
375
|
+
// Get shell configuration (Windows Git Bash detection, etc.)
|
|
376
|
+
const { shell, args } = getShellConfig();
|
|
377
|
+
// Set up cross-platform encoding environment for command execution
|
|
378
|
+
if (process.platform === 'win32') {
|
|
379
|
+
// Windows: set code page to UTF-8 and ensure console output encoding
|
|
380
|
+
// chcp 65001 sets the console code page to UTF-8
|
|
381
|
+
// Use *>$null to suppress output (PowerShell-style, not CMD-style)
|
|
382
|
+
finalCommand = `chcp 65001 *>$null; [Console]::OutputEncoding = [System.Text.Encoding]::UTF8; [System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ${finalCommand}`;
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
// Unix/macOS: set locale to UTF-8 for proper encoding handling
|
|
386
|
+
finalCommand = `export LC_ALL=C.UTF-8; export LANG=C.UTF-8; export PYTHONIOENCODING=utf-8; ${finalCommand}`;
|
|
387
|
+
}
|
|
388
|
+
const shellArgs = [...args, quoteShellCommand(finalCommand)];
|
|
332
389
|
try {
|
|
333
390
|
if (run_in_bg) {
|
|
334
391
|
const taskId = `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
335
|
-
const
|
|
392
|
+
const spawnOptions = {
|
|
336
393
|
cwd: effectiveCwd || process.cwd(),
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
394
|
+
env,
|
|
395
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
396
|
+
};
|
|
397
|
+
// On Windows, don't use detached mode for PowerShell as it breaks output piping
|
|
398
|
+
if (process.platform !== 'win32') {
|
|
399
|
+
spawnOptions.detached = true;
|
|
400
|
+
}
|
|
401
|
+
const childProcess = spawn(shell, shellArgs, spawnOptions);
|
|
341
402
|
const output = [];
|
|
342
403
|
childProcess.stdout?.on('data', (data) => {
|
|
343
404
|
const text = data.toString();
|
|
@@ -347,44 +408,128 @@ export class BashTool {
|
|
|
347
408
|
const text = data.toString();
|
|
348
409
|
output.push(text);
|
|
349
410
|
});
|
|
350
|
-
childProcess.on('close', (
|
|
351
|
-
|
|
411
|
+
childProcess.on('close', (_code) => {
|
|
412
|
+
// Silent cleanup - don't log to avoid noise during normal operation
|
|
413
|
+
// Note: On Windows with PowerShell, the shell process exits after
|
|
414
|
+
// the command completes
|
|
352
415
|
});
|
|
353
416
|
const toolRegistry = getToolRegistry();
|
|
354
417
|
toolRegistry.addBackgroundTask(taskId, {
|
|
355
418
|
process: childProcess,
|
|
356
419
|
startTime: Date.now(),
|
|
357
|
-
output
|
|
420
|
+
output,
|
|
358
421
|
});
|
|
359
422
|
return {
|
|
360
423
|
stdout: '',
|
|
361
424
|
stderr: '',
|
|
362
425
|
exitCode: 0,
|
|
363
|
-
taskId
|
|
426
|
+
taskId,
|
|
364
427
|
};
|
|
365
428
|
}
|
|
366
429
|
else {
|
|
367
|
-
|
|
430
|
+
// Execute command with spawn for better control
|
|
431
|
+
const result = await this.spawnWithTimeout(shell, shellArgs, {
|
|
368
432
|
cwd: effectiveCwd || process.cwd(),
|
|
369
|
-
|
|
370
|
-
timeout
|
|
371
|
-
env
|
|
433
|
+
env,
|
|
434
|
+
timeout,
|
|
372
435
|
});
|
|
436
|
+
// Apply truncation to stdout and stderr separately
|
|
437
|
+
const stdoutResult = truncateTail(result.stdout);
|
|
438
|
+
const stderrResult = truncateTail(result.stderr);
|
|
439
|
+
const stdout = stdoutResult.content;
|
|
440
|
+
const stderr = stderrResult.content;
|
|
441
|
+
let truncationNotice = '';
|
|
442
|
+
if (stdoutResult.truncated) {
|
|
443
|
+
truncationNotice += buildTruncationNotice(stdoutResult) + '\n';
|
|
444
|
+
}
|
|
445
|
+
if (stderrResult.truncated) {
|
|
446
|
+
truncationNotice += buildTruncationNotice(stderrResult) + '\n';
|
|
447
|
+
}
|
|
373
448
|
return {
|
|
374
449
|
stdout,
|
|
375
450
|
stderr,
|
|
376
|
-
exitCode:
|
|
451
|
+
exitCode: result.exitCode,
|
|
452
|
+
truncated: stdoutResult.truncated || stderrResult.truncated,
|
|
453
|
+
truncationNotice: truncationNotice || undefined,
|
|
377
454
|
};
|
|
378
455
|
}
|
|
379
456
|
}
|
|
380
457
|
catch (error) {
|
|
458
|
+
// Check if this was a timeout
|
|
459
|
+
if (error.message === 'timeout') {
|
|
460
|
+
return {
|
|
461
|
+
stdout: '',
|
|
462
|
+
stderr: 'Command timed out',
|
|
463
|
+
exitCode: -1,
|
|
464
|
+
truncated: false,
|
|
465
|
+
};
|
|
466
|
+
}
|
|
381
467
|
return {
|
|
382
468
|
stdout: error.stdout || '',
|
|
383
469
|
stderr: error.stderr || error.message,
|
|
384
|
-
exitCode: error.code || 1
|
|
470
|
+
exitCode: error.code || 1,
|
|
385
471
|
};
|
|
386
472
|
}
|
|
387
473
|
}
|
|
474
|
+
/**
|
|
475
|
+
* Execute a command with timeout support and proper process termination.
|
|
476
|
+
*/
|
|
477
|
+
spawnWithTimeout(shell, args, options) {
|
|
478
|
+
return new Promise((resolve, reject) => {
|
|
479
|
+
const { cwd, env, timeout } = options;
|
|
480
|
+
let timedOut = false;
|
|
481
|
+
let timeoutHandle;
|
|
482
|
+
const spawnOptions = {
|
|
483
|
+
cwd,
|
|
484
|
+
env,
|
|
485
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
486
|
+
};
|
|
487
|
+
// On Windows, don't use detached mode for PowerShell as it breaks output piping
|
|
488
|
+
if (process.platform !== 'win32') {
|
|
489
|
+
spawnOptions.detached = true;
|
|
490
|
+
}
|
|
491
|
+
const child = spawn(shell, args, spawnOptions);
|
|
492
|
+
const stdoutChunks = [];
|
|
493
|
+
const stderrChunks = [];
|
|
494
|
+
// Set timeout if provided
|
|
495
|
+
if (timeout > 0) {
|
|
496
|
+
timeoutHandle = setTimeout(() => {
|
|
497
|
+
timedOut = true;
|
|
498
|
+
if (child.pid) {
|
|
499
|
+
killProcessTree(child.pid);
|
|
500
|
+
}
|
|
501
|
+
}, timeout * 1000);
|
|
502
|
+
}
|
|
503
|
+
// Stream stdout
|
|
504
|
+
child.stdout?.on('data', (data) => {
|
|
505
|
+
stdoutChunks.push(data);
|
|
506
|
+
});
|
|
507
|
+
// Stream stderr
|
|
508
|
+
child.stderr?.on('data', (data) => {
|
|
509
|
+
stderrChunks.push(data);
|
|
510
|
+
});
|
|
511
|
+
// Handle process exit
|
|
512
|
+
child.on('close', (code) => {
|
|
513
|
+
if (timeoutHandle)
|
|
514
|
+
clearTimeout(timeoutHandle);
|
|
515
|
+
if (timedOut) {
|
|
516
|
+
reject(new Error('timeout'));
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
resolve({
|
|
520
|
+
stdout: Buffer.concat(stdoutChunks).toString('utf-8'),
|
|
521
|
+
stderr: Buffer.concat(stderrChunks).toString('utf-8'),
|
|
522
|
+
exitCode: code ?? -1,
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
// Handle spawn errors
|
|
526
|
+
child.on('error', (err) => {
|
|
527
|
+
if (timeoutHandle)
|
|
528
|
+
clearTimeout(timeoutHandle);
|
|
529
|
+
reject(err);
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
}
|
|
388
533
|
}
|
|
389
534
|
export class ListDirectoryTool {
|
|
390
535
|
name = 'ListDirectory';
|
|
@@ -416,7 +561,12 @@ export class ListDirectoryTool {
|
|
|
416
561
|
- Results are absolute paths
|
|
417
562
|
- Ignores node_modules and .git by default
|
|
418
563
|
- Combine with Read to examine file contents`;
|
|
419
|
-
allowedModes = [
|
|
564
|
+
allowedModes = [
|
|
565
|
+
ExecutionMode.YOLO,
|
|
566
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
567
|
+
ExecutionMode.PLAN,
|
|
568
|
+
ExecutionMode.SMART,
|
|
569
|
+
];
|
|
420
570
|
async execute(params) {
|
|
421
571
|
const { path: dirPath = '.', recursive = false } = params;
|
|
422
572
|
try {
|
|
@@ -429,9 +579,9 @@ export class ListDirectoryTool {
|
|
|
429
579
|
const files = await glob(pattern, {
|
|
430
580
|
cwd: absolutePath,
|
|
431
581
|
nodir: false,
|
|
432
|
-
ignore: ['node_modules/**', '.git/**']
|
|
582
|
+
ignore: ['node_modules/**', '.git/**'],
|
|
433
583
|
});
|
|
434
|
-
return files.map(file => path.join(absolutePath, file));
|
|
584
|
+
return files.map((file) => path.join(absolutePath, file));
|
|
435
585
|
}
|
|
436
586
|
catch (error) {
|
|
437
587
|
throw new Error(`Failed to list directory: ${error.message}`);
|
|
@@ -440,7 +590,7 @@ export class ListDirectoryTool {
|
|
|
440
590
|
}
|
|
441
591
|
export class SearchFilesTool {
|
|
442
592
|
name = 'SearchFiles';
|
|
443
|
-
description = `Search for files matching a glob pattern. This is your PRIMARY tool for finding files by name or extension.
|
|
593
|
+
description = `Search for files matching a glob pattern using fd. This is your PRIMARY tool for finding files by name or extension.
|
|
444
594
|
|
|
445
595
|
# When to Use
|
|
446
596
|
- Finding all files of a certain type (*.ts, *.json, *.md)
|
|
@@ -475,21 +625,35 @@ export class SearchFilesTool {
|
|
|
475
625
|
- Combine with path parameter to search specific directories
|
|
476
626
|
- Use limit parameter to avoid huge result sets
|
|
477
627
|
- Results are file paths, not content (use Grep on results if needed)`;
|
|
478
|
-
allowedModes = [
|
|
628
|
+
allowedModes = [
|
|
629
|
+
ExecutionMode.YOLO,
|
|
630
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
631
|
+
ExecutionMode.PLAN,
|
|
632
|
+
ExecutionMode.SMART,
|
|
633
|
+
];
|
|
479
634
|
async execute(params) {
|
|
480
635
|
const { pattern, path: searchPath = '.', limit = 1000 } = params;
|
|
481
636
|
try {
|
|
482
|
-
const
|
|
483
|
-
|
|
484
|
-
|
|
637
|
+
const output = await fdFind({
|
|
638
|
+
pattern,
|
|
639
|
+
path: searchPath,
|
|
640
|
+
limit,
|
|
485
641
|
});
|
|
642
|
+
if (output === 'No files found') {
|
|
643
|
+
return {
|
|
644
|
+
files: [],
|
|
645
|
+
total: 0,
|
|
646
|
+
truncated: false,
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
const files = output.split('\n').filter((line) => line.trim());
|
|
486
650
|
const total = files.length;
|
|
487
651
|
const truncated = total > limit;
|
|
488
652
|
const result = truncated ? files.slice(0, limit) : files;
|
|
489
653
|
return {
|
|
490
654
|
files: result,
|
|
491
655
|
total,
|
|
492
|
-
truncated
|
|
656
|
+
truncated,
|
|
493
657
|
};
|
|
494
658
|
}
|
|
495
659
|
catch (error) {
|
|
@@ -532,7 +696,7 @@ export class DeleteFileTool {
|
|
|
532
696
|
return {
|
|
533
697
|
success: true,
|
|
534
698
|
message: `Successfully deleted ${filePath}`,
|
|
535
|
-
filePath
|
|
699
|
+
filePath,
|
|
536
700
|
};
|
|
537
701
|
}
|
|
538
702
|
catch (error) {
|
|
@@ -574,7 +738,7 @@ export class CreateDirectoryTool {
|
|
|
574
738
|
await fs.mkdir(absolutePath, { recursive });
|
|
575
739
|
return {
|
|
576
740
|
success: true,
|
|
577
|
-
message: `Successfully created directory ${dirPath}
|
|
741
|
+
message: `Successfully created directory ${dirPath}`,
|
|
578
742
|
};
|
|
579
743
|
}
|
|
580
744
|
catch (error) {
|
|
@@ -584,29 +748,29 @@ export class CreateDirectoryTool {
|
|
|
584
748
|
}
|
|
585
749
|
// 编辑工具辅助函数
|
|
586
750
|
function detectLineEnding(content) {
|
|
587
|
-
const crlfIdx = content.indexOf(
|
|
588
|
-
const lfIdx = content.indexOf(
|
|
751
|
+
const crlfIdx = content.indexOf('\r\n');
|
|
752
|
+
const lfIdx = content.indexOf('\n');
|
|
589
753
|
if (lfIdx === -1)
|
|
590
|
-
return
|
|
754
|
+
return '\n';
|
|
591
755
|
if (crlfIdx === -1)
|
|
592
|
-
return
|
|
593
|
-
return crlfIdx < lfIdx ?
|
|
756
|
+
return '\n';
|
|
757
|
+
return crlfIdx < lfIdx ? '\r\n' : '\n';
|
|
594
758
|
}
|
|
595
759
|
function normalizeToLF(text) {
|
|
596
|
-
return text.replace(/\r\n/g,
|
|
760
|
+
return text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
597
761
|
}
|
|
598
762
|
function restoreLineEndings(text, ending) {
|
|
599
|
-
return ending ===
|
|
763
|
+
return ending === '\r\n' ? text.replace(/\n/g, '\r\n') : text;
|
|
600
764
|
}
|
|
601
765
|
function normalizeForFuzzyMatch(text) {
|
|
602
|
-
return
|
|
603
|
-
.split(
|
|
766
|
+
return text
|
|
767
|
+
.split('\n')
|
|
604
768
|
.map((line) => line.trimEnd())
|
|
605
|
-
.join(
|
|
769
|
+
.join('\n')
|
|
606
770
|
.replace(/['‘’""]/g, "'")
|
|
607
771
|
.replace(/["""]/g, '"')
|
|
608
|
-
.replace(/[—–‑−]/g,
|
|
609
|
-
.replace(/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g,
|
|
772
|
+
.replace(/[—–‑−]/g, '-')
|
|
773
|
+
.replace(/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g, ' ');
|
|
610
774
|
}
|
|
611
775
|
function fuzzyFindText(content, oldText) {
|
|
612
776
|
const exactIndex = content.indexOf(oldText);
|
|
@@ -640,14 +804,16 @@ function fuzzyFindText(content, oldText) {
|
|
|
640
804
|
};
|
|
641
805
|
}
|
|
642
806
|
function stripBom(content) {
|
|
643
|
-
return content.startsWith(
|
|
807
|
+
return content.startsWith('\uFEFF')
|
|
808
|
+
? { bom: '\uFEFF', text: content.slice(1) }
|
|
809
|
+
: { bom: '', text: content };
|
|
644
810
|
}
|
|
645
811
|
async function generateDiffString(oldContent, newContent, contextLines = 4) {
|
|
646
|
-
const diffModule = await import(
|
|
812
|
+
const diffModule = await import('diff');
|
|
647
813
|
const parts = diffModule.diffLines(oldContent, newContent);
|
|
648
814
|
const output = [];
|
|
649
|
-
const oldLines = oldContent.split(
|
|
650
|
-
const newLines = newContent.split(
|
|
815
|
+
const oldLines = oldContent.split('\n');
|
|
816
|
+
const newLines = newContent.split('\n');
|
|
651
817
|
const maxLineNum = Math.max(oldLines.length, newLines.length);
|
|
652
818
|
const lineNumWidth = String(maxLineNum).length;
|
|
653
819
|
let oldLineNum = 1;
|
|
@@ -656,8 +822,8 @@ async function generateDiffString(oldContent, newContent, contextLines = 4) {
|
|
|
656
822
|
let firstChangedLine;
|
|
657
823
|
for (let i = 0; i < parts.length; i++) {
|
|
658
824
|
const part = parts[i];
|
|
659
|
-
const raw = part.value.split(
|
|
660
|
-
if (raw[raw.length - 1] ===
|
|
825
|
+
const raw = part.value.split('\n');
|
|
826
|
+
if (raw[raw.length - 1] === '') {
|
|
661
827
|
raw.pop();
|
|
662
828
|
}
|
|
663
829
|
if (part.added || part.removed) {
|
|
@@ -666,12 +832,12 @@ async function generateDiffString(oldContent, newContent, contextLines = 4) {
|
|
|
666
832
|
}
|
|
667
833
|
for (const line of raw) {
|
|
668
834
|
if (part.added) {
|
|
669
|
-
const lineNum = String(newLineNum).padStart(lineNumWidth,
|
|
835
|
+
const lineNum = String(newLineNum).padStart(lineNumWidth, ' ');
|
|
670
836
|
output.push(`+${lineNum} ${line}`);
|
|
671
837
|
newLineNum++;
|
|
672
838
|
}
|
|
673
839
|
else {
|
|
674
|
-
const lineNum = String(oldLineNum).padStart(lineNumWidth,
|
|
840
|
+
const lineNum = String(oldLineNum).padStart(lineNumWidth, ' ');
|
|
675
841
|
output.push(`-${lineNum} ${line}`);
|
|
676
842
|
oldLineNum++;
|
|
677
843
|
}
|
|
@@ -693,18 +859,18 @@ async function generateDiffString(oldContent, newContent, contextLines = 4) {
|
|
|
693
859
|
linesToShow = linesToShow.slice(0, contextLines);
|
|
694
860
|
}
|
|
695
861
|
if (skipStart > 0) {
|
|
696
|
-
output.push(` ${
|
|
862
|
+
output.push(` ${''.padStart(lineNumWidth, ' ')} ...`);
|
|
697
863
|
oldLineNum += skipStart;
|
|
698
864
|
newLineNum += skipStart;
|
|
699
865
|
}
|
|
700
866
|
for (const line of linesToShow) {
|
|
701
|
-
const lineNum = String(oldLineNum).padStart(lineNumWidth,
|
|
867
|
+
const lineNum = String(oldLineNum).padStart(lineNumWidth, ' ');
|
|
702
868
|
output.push(` ${lineNum} ${line}`);
|
|
703
869
|
oldLineNum++;
|
|
704
870
|
newLineNum++;
|
|
705
871
|
}
|
|
706
872
|
if (skipEnd > 0) {
|
|
707
|
-
output.push(` ${
|
|
873
|
+
output.push(` ${''.padStart(lineNumWidth, ' ')} ...`);
|
|
708
874
|
}
|
|
709
875
|
}
|
|
710
876
|
else {
|
|
@@ -714,7 +880,7 @@ async function generateDiffString(oldContent, newContent, contextLines = 4) {
|
|
|
714
880
|
lastWasChange = false;
|
|
715
881
|
}
|
|
716
882
|
}
|
|
717
|
-
return { diff: output.join(
|
|
883
|
+
return { diff: output.join('\n'), firstChangedLine };
|
|
718
884
|
}
|
|
719
885
|
export class EditTool {
|
|
720
886
|
name = 'Edit';
|
|
@@ -765,6 +931,8 @@ edit(
|
|
|
765
931
|
allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
|
|
766
932
|
async execute(params) {
|
|
767
933
|
const { file_path, instruction, old_string, new_string } = params;
|
|
934
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
935
|
+
void instruction;
|
|
768
936
|
try {
|
|
769
937
|
const absolutePath = path.resolve(file_path);
|
|
770
938
|
// Check if file exists
|
|
@@ -779,7 +947,7 @@ edit(
|
|
|
779
947
|
}
|
|
780
948
|
// Read the file
|
|
781
949
|
const buffer = await fs.readFile(absolutePath);
|
|
782
|
-
const rawContent = buffer.toString(
|
|
950
|
+
const rawContent = buffer.toString('utf-8');
|
|
783
951
|
// Strip BOM before matching
|
|
784
952
|
const { bom, text: content } = stripBom(rawContent);
|
|
785
953
|
const originalEnding = detectLineEnding(content);
|
|
@@ -817,7 +985,7 @@ edit(
|
|
|
817
985
|
};
|
|
818
986
|
}
|
|
819
987
|
const finalContent = bom + restoreLineEndings(newContent, originalEnding);
|
|
820
|
-
await fs.writeFile(absolutePath, finalContent,
|
|
988
|
+
await fs.writeFile(absolutePath, finalContent, 'utf-8');
|
|
821
989
|
const diffResult = await generateDiffString(baseContent, newContent);
|
|
822
990
|
return {
|
|
823
991
|
success: true,
|
|
@@ -863,7 +1031,12 @@ export class WebSearchTool {
|
|
|
863
1031
|
- Combine with web_fetch to get full content from relevant URLs
|
|
864
1032
|
- Use quotes for exact phrase matching
|
|
865
1033
|
- Consider adding context like year or version in query`;
|
|
866
|
-
allowedModes = [
|
|
1034
|
+
allowedModes = [
|
|
1035
|
+
ExecutionMode.YOLO,
|
|
1036
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
1037
|
+
ExecutionMode.PLAN,
|
|
1038
|
+
ExecutionMode.SMART,
|
|
1039
|
+
];
|
|
867
1040
|
async execute(params) {
|
|
868
1041
|
const { query } = params;
|
|
869
1042
|
try {
|
|
@@ -877,14 +1050,14 @@ export class WebSearchTool {
|
|
|
877
1050
|
}
|
|
878
1051
|
const response = await axios.post(`${baseUrl}/search`, { query }, {
|
|
879
1052
|
headers: {
|
|
880
|
-
|
|
881
|
-
'Content-Type': 'application/json'
|
|
1053
|
+
Authorization: `Bearer ${searchApiKey}`,
|
|
1054
|
+
'Content-Type': 'application/json',
|
|
882
1055
|
},
|
|
883
|
-
timeout: 30000
|
|
1056
|
+
timeout: 30000,
|
|
884
1057
|
});
|
|
885
1058
|
return {
|
|
886
1059
|
results: response.data.results || [],
|
|
887
|
-
message: `Found ${response.data.results?.length || 0} results for "${query}"
|
|
1060
|
+
message: `Found ${response.data.results?.length || 0} results for "${query}"`,
|
|
888
1061
|
};
|
|
889
1062
|
}
|
|
890
1063
|
catch (error) {
|
|
@@ -940,22 +1113,27 @@ Each task needs:
|
|
|
940
1113
|
- Don't batch multiple completions - update as you go
|
|
941
1114
|
- Keep task descriptions clear and actionable
|
|
942
1115
|
- Use appropriate priority levels to indicate urgency`;
|
|
943
|
-
allowedModes = [
|
|
1116
|
+
allowedModes = [
|
|
1117
|
+
ExecutionMode.YOLO,
|
|
1118
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
1119
|
+
ExecutionMode.PLAN,
|
|
1120
|
+
ExecutionMode.SMART,
|
|
1121
|
+
];
|
|
944
1122
|
todoList = [];
|
|
945
1123
|
async execute(params) {
|
|
946
1124
|
const { todos } = params;
|
|
947
1125
|
try {
|
|
948
1126
|
this.todoList = todos;
|
|
949
1127
|
const summary = {
|
|
950
|
-
pending: todos.filter(t => t.status === 'pending').length,
|
|
951
|
-
in_progress: todos.filter(t => t.status === 'in_progress').length,
|
|
952
|
-
completed: todos.filter(t => t.status === 'completed').length,
|
|
953
|
-
failed: todos.filter(t => t.status === 'failed').length
|
|
1128
|
+
pending: todos.filter((t) => t.status === 'pending').length,
|
|
1129
|
+
in_progress: todos.filter((t) => t.status === 'in_progress').length,
|
|
1130
|
+
completed: todos.filter((t) => t.status === 'completed').length,
|
|
1131
|
+
failed: todos.filter((t) => t.status === 'failed').length,
|
|
954
1132
|
};
|
|
955
1133
|
return {
|
|
956
1134
|
success: true,
|
|
957
1135
|
message: `Updated todo list: ${summary.pending} pending, ${summary.in_progress} in progress, ${summary.completed} completed, ${summary.failed} failed`,
|
|
958
|
-
todos: this.todoList
|
|
1136
|
+
todos: this.todoList,
|
|
959
1137
|
};
|
|
960
1138
|
}
|
|
961
1139
|
catch (error) {
|
|
@@ -987,7 +1165,12 @@ export class TodoReadTool {
|
|
|
987
1165
|
# Best Practices
|
|
988
1166
|
- Use todo_write to modify the list, not todo_read
|
|
989
1167
|
- Check todo_read after todo_write to verify updates`;
|
|
990
|
-
allowedModes = [
|
|
1168
|
+
allowedModes = [
|
|
1169
|
+
ExecutionMode.YOLO,
|
|
1170
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
1171
|
+
ExecutionMode.PLAN,
|
|
1172
|
+
ExecutionMode.SMART,
|
|
1173
|
+
];
|
|
991
1174
|
todoWriteTool;
|
|
992
1175
|
constructor(todoWriteTool) {
|
|
993
1176
|
this.todoWriteTool = todoWriteTool;
|
|
@@ -997,14 +1180,14 @@ export class TodoReadTool {
|
|
|
997
1180
|
const todos = this.todoWriteTool.getTodos();
|
|
998
1181
|
const summary = {
|
|
999
1182
|
total: todos.length,
|
|
1000
|
-
pending: todos.filter(t => t.status === 'pending').length,
|
|
1001
|
-
in_progress: todos.filter(t => t.status === 'in_progress').length,
|
|
1002
|
-
completed: todos.filter(t => t.status === 'completed').length,
|
|
1003
|
-
failed: todos.filter(t => t.status === 'failed').length
|
|
1183
|
+
pending: todos.filter((t) => t.status === 'pending').length,
|
|
1184
|
+
in_progress: todos.filter((t) => t.status === 'in_progress').length,
|
|
1185
|
+
completed: todos.filter((t) => t.status === 'completed').length,
|
|
1186
|
+
failed: todos.filter((t) => t.status === 'failed').length,
|
|
1004
1187
|
};
|
|
1005
1188
|
return {
|
|
1006
1189
|
todos,
|
|
1007
|
-
summary
|
|
1190
|
+
summary,
|
|
1008
1191
|
};
|
|
1009
1192
|
}
|
|
1010
1193
|
catch (error) {
|
|
@@ -1051,7 +1234,12 @@ export class TaskTool {
|
|
|
1051
1234
|
- Include relevant context (file paths, requirements, constraints)
|
|
1052
1235
|
- Set appropriate executionMode if needed
|
|
1053
1236
|
- For parallel execution, ensure tasks are truly independent`;
|
|
1054
|
-
allowedModes = [
|
|
1237
|
+
allowedModes = [
|
|
1238
|
+
ExecutionMode.YOLO,
|
|
1239
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
1240
|
+
ExecutionMode.PLAN,
|
|
1241
|
+
ExecutionMode.SMART,
|
|
1242
|
+
];
|
|
1055
1243
|
async execute(params, _executionMode) {
|
|
1056
1244
|
const mode = params.executionMode || _executionMode || ExecutionMode.YOLO;
|
|
1057
1245
|
try {
|
|
@@ -1059,16 +1247,13 @@ export class TaskTool {
|
|
|
1059
1247
|
const agentManager = getAgentManager(process.cwd());
|
|
1060
1248
|
const { getConfigManager } = await import('./config.js');
|
|
1061
1249
|
const config = getConfigManager();
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
baseUrl: config.get('baseUrl'),
|
|
1067
|
-
modelName: config.get('modelName') || 'Qwen3-Coder'
|
|
1068
|
-
});
|
|
1250
|
+
const authConfig = config.getAuthConfig();
|
|
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);
|
|
1069
1254
|
const toolRegistry = getToolRegistry();
|
|
1070
1255
|
if (params.agents && params.agents.length > 0) {
|
|
1071
|
-
return await this.executeParallelAgents(params.agents, params.description, mode, agentManager, toolRegistry,
|
|
1256
|
+
return await this.executeParallelAgents(params.agents, params.description, mode, agentManager, toolRegistry, config);
|
|
1072
1257
|
}
|
|
1073
1258
|
if (!params.subagent_type) {
|
|
1074
1259
|
throw new Error('subagent_type is required for Task tool');
|
|
@@ -1076,15 +1261,16 @@ export class TaskTool {
|
|
|
1076
1261
|
// Support both 'prompt' and 'query' parameter names (tool definition uses 'query')
|
|
1077
1262
|
const prompt = params.prompt || params.query;
|
|
1078
1263
|
if (!prompt) {
|
|
1079
|
-
throw new Error('Task query/prompt is required. Received params: ' +
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1264
|
+
throw new Error('Task query/prompt is required. Received params: ' +
|
|
1265
|
+
JSON.stringify({
|
|
1266
|
+
subagent_type: params.subagent_type,
|
|
1267
|
+
prompt: params.prompt,
|
|
1268
|
+
query: params.query,
|
|
1269
|
+
description: params.description,
|
|
1270
|
+
agents: params.agents?.length,
|
|
1271
|
+
}));
|
|
1272
|
+
}
|
|
1273
|
+
const result = await this.executeSingleAgent(params.subagent_type, prompt, params.description, params.useContext ?? true, params.constraints || [], mode, agentManager, toolRegistry, config);
|
|
1088
1274
|
return result;
|
|
1089
1275
|
}
|
|
1090
1276
|
catch (error) {
|
|
@@ -1095,11 +1281,16 @@ export class TaskTool {
|
|
|
1095
1281
|
* Create unified VLM caller
|
|
1096
1282
|
* Uses remote VLM if remoteAIClient is provided, otherwise uses local VLM
|
|
1097
1283
|
* Both modes receive full messages array for consistent behavior
|
|
1284
|
+
* @param remoteAIClient - Remote AI client for VLM calls
|
|
1285
|
+
* @param taskId - Task identifier for backend tracking
|
|
1286
|
+
* @param localConfig - Local VLM configuration
|
|
1287
|
+
* @param isFirstVlmCallRef - Reference to boolean tracking if this is the first VLM call
|
|
1288
|
+
* @param signal - Abort signal for cancellation
|
|
1098
1289
|
*/
|
|
1099
|
-
createRemoteVlmCaller(remoteAIClient, taskId, localConfig, signal) {
|
|
1290
|
+
createRemoteVlmCaller(remoteAIClient, taskId, localConfig, isFirstVlmCallRef, signal) {
|
|
1100
1291
|
// Remote mode: use RemoteAIClient
|
|
1101
1292
|
if (remoteAIClient) {
|
|
1102
|
-
return this.createRemoteVLMCaller(remoteAIClient, taskId, signal);
|
|
1293
|
+
return this.createRemoteVLMCaller(remoteAIClient, taskId, isFirstVlmCallRef, signal);
|
|
1103
1294
|
}
|
|
1104
1295
|
// Local mode: use local API
|
|
1105
1296
|
return this.createLocalVLMCaller(localConfig, signal);
|
|
@@ -1107,11 +1298,24 @@ export class TaskTool {
|
|
|
1107
1298
|
/**
|
|
1108
1299
|
* Create remote VLM caller using RemoteAIClient
|
|
1109
1300
|
* Now receives full messages array for consistent behavior with local mode
|
|
1301
|
+
* @param remoteAIClient - Remote AI client
|
|
1302
|
+
* @param taskId - Task identifier for backend tracking
|
|
1303
|
+
* @param isFirstVlmCallRef - Reference to boolean tracking if this is the first VLM call
|
|
1304
|
+
* @param signal - Abort signal for cancellation
|
|
1110
1305
|
*/
|
|
1111
|
-
createRemoteVLMCaller(remoteAIClient, taskId, signal) {
|
|
1112
|
-
return async (messages, systemPrompt) => {
|
|
1306
|
+
createRemoteVLMCaller(remoteAIClient, taskId, isFirstVlmCallRef, signal) {
|
|
1307
|
+
return async (messages, systemPrompt, _taskId, _isFirstVlmCallRef) => {
|
|
1113
1308
|
try {
|
|
1114
|
-
|
|
1309
|
+
// Use the ref to track first call status for the backend
|
|
1310
|
+
const status = isFirstVlmCallRef.current ? 'begin' : 'continue';
|
|
1311
|
+
const result = await remoteAIClient.invokeVLM(messages, systemPrompt, {
|
|
1312
|
+
signal,
|
|
1313
|
+
taskId,
|
|
1314
|
+
status,
|
|
1315
|
+
});
|
|
1316
|
+
// Update ref after call so subsequent calls use 'continue'
|
|
1317
|
+
isFirstVlmCallRef.current = false;
|
|
1318
|
+
return result;
|
|
1115
1319
|
}
|
|
1116
1320
|
catch (error) {
|
|
1117
1321
|
throw new Error(`Remote VLM call failed: ${error.message}`);
|
|
@@ -1141,7 +1345,7 @@ export class TaskTool {
|
|
|
1141
1345
|
method: 'POST',
|
|
1142
1346
|
headers: {
|
|
1143
1347
|
'Content-Type': 'application/json',
|
|
1144
|
-
|
|
1348
|
+
Authorization: `Bearer ${apiKey}`,
|
|
1145
1349
|
},
|
|
1146
1350
|
body: JSON.stringify(requestBody),
|
|
1147
1351
|
signal: abortSignal,
|
|
@@ -1150,7 +1354,7 @@ export class TaskTool {
|
|
|
1150
1354
|
const errorText = await response.text();
|
|
1151
1355
|
throw new Error(`VLM API error: ${errorText}`);
|
|
1152
1356
|
}
|
|
1153
|
-
const result = await response.json();
|
|
1357
|
+
const result = (await response.json());
|
|
1154
1358
|
return result.choices?.[0]?.message?.content || '';
|
|
1155
1359
|
};
|
|
1156
1360
|
}
|
|
@@ -1160,30 +1364,63 @@ export class TaskTool {
|
|
|
1160
1364
|
*/
|
|
1161
1365
|
async executeGUIAgent(prompt, description, agent, mode, config, indentLevel = 1, remoteAIClient) {
|
|
1162
1366
|
const indent = ' '.repeat(indentLevel);
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
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
|
+
}
|
|
1388
|
+
// Get VLM configuration for local mode
|
|
1389
|
+
// NOTE: guiSubagentBaseUrl must be explicitly configured, NOT fallback to baseUrl
|
|
1390
|
+
const baseUrl = config.get('guiSubagentBaseUrl') || '';
|
|
1391
|
+
const apiKey = config.get('guiSubagentApiKey') || '';
|
|
1392
|
+
const modelName = config.get('guiSubagentModel') || '';
|
|
1170
1393
|
// Determine mode: remote if remoteAIClient exists, otherwise local
|
|
1171
1394
|
const isRemoteMode = !!remoteAIClient;
|
|
1172
1395
|
// Log mode information
|
|
1173
1396
|
if (isRemoteMode) {
|
|
1174
|
-
|
|
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
|
+
}
|
|
1175
1405
|
}
|
|
1176
1406
|
else {
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
};
|
|
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}`)}`);
|
|
1184
1423
|
}
|
|
1185
|
-
console.log(`${indent}${colors.textMuted(` Model: ${modelName}`)}`);
|
|
1186
|
-
console.log(`${indent}${colors.textMuted(` Base URL: ${baseUrl}`)}`);
|
|
1187
1424
|
}
|
|
1188
1425
|
console.log('');
|
|
1189
1426
|
// Get taskId from session for tracking (remote mode only)
|
|
@@ -1194,27 +1431,31 @@ export class TaskTool {
|
|
|
1194
1431
|
const session = getSingletonSession();
|
|
1195
1432
|
taskId = session?.getTaskId() || null;
|
|
1196
1433
|
}
|
|
1197
|
-
catch
|
|
1434
|
+
catch {
|
|
1198
1435
|
taskId = null;
|
|
1199
1436
|
}
|
|
1200
1437
|
}
|
|
1438
|
+
// Track first VLM call for proper status management
|
|
1439
|
+
const isFirstVlmCallRef = { current: true };
|
|
1201
1440
|
// Create remoteVlmCaller using the unified method (handles both local and remote modes)
|
|
1202
|
-
const remoteVlmCaller = this.createRemoteVlmCaller(remoteAIClient, taskId, { baseUrl, apiKey, modelName });
|
|
1441
|
+
const remoteVlmCaller = this.createRemoteVlmCaller(remoteAIClient, taskId, { baseUrl, apiKey, modelName }, isFirstVlmCallRef);
|
|
1203
1442
|
// Set up stdin polling for ESC cancellation
|
|
1204
1443
|
let rawModeEnabled = false;
|
|
1205
1444
|
let stdinPollingInterval = null;
|
|
1206
1445
|
const cancellationManager = getCancellationManager();
|
|
1207
1446
|
const logger = getLogger();
|
|
1208
1447
|
const setupStdinPolling = () => {
|
|
1448
|
+
logger.debug(`[GUIAgent ESC] setupStdinPolling called, process.stdin.isTTY: ${process.stdin.isTTY}`);
|
|
1209
1449
|
if (process.stdin.isTTY) {
|
|
1210
1450
|
try {
|
|
1211
1451
|
process.stdin.setRawMode(true);
|
|
1212
1452
|
rawModeEnabled = true;
|
|
1213
1453
|
process.stdin.resume();
|
|
1214
1454
|
readline.emitKeypressEvents(process.stdin);
|
|
1455
|
+
logger.debug(`[GUIAgent ESC] Raw mode enabled successfully`);
|
|
1215
1456
|
}
|
|
1216
1457
|
catch (e) {
|
|
1217
|
-
logger.debug(`[GUIAgent] Could not set raw mode: ${e}`);
|
|
1458
|
+
logger.debug(`[GUIAgent ESC] Could not set raw mode: ${e.message}`);
|
|
1218
1459
|
}
|
|
1219
1460
|
stdinPollingInterval = setInterval(() => {
|
|
1220
1461
|
try {
|
|
@@ -1222,14 +1463,23 @@ export class TaskTool {
|
|
|
1222
1463
|
const chunk = process.stdin.read(1);
|
|
1223
1464
|
if (chunk && chunk.length > 0) {
|
|
1224
1465
|
const code = chunk[0];
|
|
1225
|
-
if (code ===
|
|
1226
|
-
|
|
1466
|
+
if (code === 0x1b) {
|
|
1467
|
+
// ESC
|
|
1468
|
+
logger.debug('[GUIAgent ESC Polling] ESC detected! Code: 0x1b');
|
|
1227
1469
|
cancellationManager.cancel();
|
|
1228
1470
|
}
|
|
1471
|
+
else {
|
|
1472
|
+
// Log other key codes for debugging
|
|
1473
|
+
logger.debug(`[GUIAgent ESC Polling] Key code: 0x${code.toString(16)}`);
|
|
1474
|
+
}
|
|
1229
1475
|
}
|
|
1230
1476
|
}
|
|
1477
|
+
else {
|
|
1478
|
+
logger.debug('[GUIAgent ESC Polling] rawModeEnabled is false');
|
|
1479
|
+
}
|
|
1231
1480
|
}
|
|
1232
1481
|
catch (e) {
|
|
1482
|
+
logger.debug(`[GUIAgent ESC Polling] Error: ${e.message}`);
|
|
1233
1483
|
// Ignore polling errors
|
|
1234
1484
|
}
|
|
1235
1485
|
}, 10);
|
|
@@ -1248,7 +1498,9 @@ export class TaskTool {
|
|
|
1248
1498
|
};
|
|
1249
1499
|
cancellationManager.on('cancelled', cancelHandler);
|
|
1250
1500
|
// Start polling for ESC
|
|
1501
|
+
logger.debug(`[GUIAgent ESC] About to call setupStdinPolling`);
|
|
1251
1502
|
setupStdinPolling();
|
|
1503
|
+
logger.debug(`[GUIAgent ESC] setupStdinPolling called`);
|
|
1252
1504
|
try {
|
|
1253
1505
|
// Import and create GUIAgent
|
|
1254
1506
|
const { createGUISubAgent } = await import('./gui-subagent/index.js');
|
|
@@ -1256,11 +1508,15 @@ export class TaskTool {
|
|
|
1256
1508
|
model: !isRemoteMode ? modelName : undefined,
|
|
1257
1509
|
modelBaseUrl: !isRemoteMode ? baseUrl : undefined,
|
|
1258
1510
|
modelApiKey: !isRemoteMode ? apiKey : undefined,
|
|
1511
|
+
taskId: taskId || undefined,
|
|
1512
|
+
isFirstVlmCallRef,
|
|
1259
1513
|
remoteVlmCaller,
|
|
1260
1514
|
isLocalMode: !isRemoteMode,
|
|
1261
|
-
maxLoopCount:
|
|
1515
|
+
maxLoopCount: 100,
|
|
1262
1516
|
loopIntervalInMs: 500,
|
|
1263
1517
|
showAIDebugInfo: config.get('showAIDebugInfo') || false,
|
|
1518
|
+
indentLevel: indentLevel,
|
|
1519
|
+
sdkOutputAdapter: isSdkMode ? sdkOutputAdapter : null,
|
|
1264
1520
|
});
|
|
1265
1521
|
// Add constraints to prompt if any
|
|
1266
1522
|
const fullPrompt = prompt;
|
|
@@ -1278,7 +1534,7 @@ export class TaskTool {
|
|
|
1278
1534
|
success: true,
|
|
1279
1535
|
cancelled: true, // Mark as cancelled so main agent won't continue
|
|
1280
1536
|
message: `GUI task "${description}" cancelled by user`,
|
|
1281
|
-
result: 'Task cancelled'
|
|
1537
|
+
result: 'Task cancelled',
|
|
1282
1538
|
};
|
|
1283
1539
|
}
|
|
1284
1540
|
cleanupStdinPolling();
|
|
@@ -1286,28 +1542,96 @@ export class TaskTool {
|
|
|
1286
1542
|
// Flush stdout to ensure all output is displayed before returning
|
|
1287
1543
|
process.stdout.write('\n');
|
|
1288
1544
|
// Return result based on GUIAgent status
|
|
1545
|
+
// Always return all info except screenshots (base64) to avoid huge payload
|
|
1546
|
+
const conversationsWithoutScreenshots = result.conversations.map((conv) => ({
|
|
1547
|
+
...conv,
|
|
1548
|
+
screenshotBase64: undefined, // Remove screenshots to avoid huge payload
|
|
1549
|
+
}));
|
|
1289
1550
|
if (result.status === 'end') {
|
|
1290
|
-
const iterations =
|
|
1291
|
-
|
|
1551
|
+
const iterations = conversationsWithoutScreenshots.filter((c) => c.from === 'human' && c.screenshotContext).length;
|
|
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
|
+
}
|
|
1292
1560
|
return {
|
|
1293
1561
|
success: true,
|
|
1294
1562
|
message: `GUI task "${description}" completed`,
|
|
1295
|
-
result:
|
|
1563
|
+
result: {
|
|
1564
|
+
status: result.status,
|
|
1565
|
+
iterations,
|
|
1566
|
+
actions: conversationsWithoutScreenshots
|
|
1567
|
+
.filter((c) => c.from === 'assistant' && c.actionType)
|
|
1568
|
+
.map((c) => c.actionType),
|
|
1569
|
+
conversations: conversationsWithoutScreenshots,
|
|
1570
|
+
error: result.error,
|
|
1571
|
+
},
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
else if (result.status === 'call_llm') {
|
|
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
|
+
}
|
|
1583
|
+
return {
|
|
1584
|
+
success: true,
|
|
1585
|
+
message: `GUI task "${description}" returned for LLM decision`,
|
|
1586
|
+
result: {
|
|
1587
|
+
status: result.status,
|
|
1588
|
+
iterations: conversationsWithoutScreenshots.filter((c) => c.from === 'human' && c.screenshotContext).length,
|
|
1589
|
+
actions: conversationsWithoutScreenshots
|
|
1590
|
+
.filter((c) => c.from === 'assistant' && c.actionType)
|
|
1591
|
+
.map((c) => c.actionType),
|
|
1592
|
+
conversations: conversationsWithoutScreenshots,
|
|
1593
|
+
error: result.error,
|
|
1594
|
+
},
|
|
1296
1595
|
};
|
|
1297
1596
|
}
|
|
1298
1597
|
else if (result.status === 'user_stopped') {
|
|
1598
|
+
// SDK mode: use adapter output
|
|
1599
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1600
|
+
sdkOutputAdapter.outputGUIAgentCancelled(description);
|
|
1601
|
+
}
|
|
1299
1602
|
return {
|
|
1300
1603
|
success: true,
|
|
1301
1604
|
message: `GUI task "${description}" stopped by user`,
|
|
1302
|
-
result:
|
|
1605
|
+
result: {
|
|
1606
|
+
status: result.status,
|
|
1607
|
+
iterations: conversationsWithoutScreenshots.filter((c) => c.from === 'human' && c.screenshotContext).length,
|
|
1608
|
+
actions: conversationsWithoutScreenshots
|
|
1609
|
+
.filter((c) => c.from === 'assistant' && c.actionType)
|
|
1610
|
+
.map((c) => c.actionType),
|
|
1611
|
+
conversations: conversationsWithoutScreenshots,
|
|
1612
|
+
stopped: true,
|
|
1613
|
+
},
|
|
1303
1614
|
};
|
|
1304
1615
|
}
|
|
1305
1616
|
else {
|
|
1306
1617
|
// status is 'error' or other non-success status
|
|
1307
1618
|
const errorMsg = result.error || 'Unknown error';
|
|
1619
|
+
// SDK mode: use adapter output
|
|
1620
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1621
|
+
sdkOutputAdapter.outputGUIAgentError(description, errorMsg);
|
|
1622
|
+
}
|
|
1308
1623
|
return {
|
|
1309
1624
|
success: false,
|
|
1310
|
-
message: `GUI task "${description}" failed: ${errorMsg}
|
|
1625
|
+
message: `GUI task "${description}" failed: ${errorMsg}`,
|
|
1626
|
+
result: {
|
|
1627
|
+
status: result.status,
|
|
1628
|
+
iterations: conversationsWithoutScreenshots.filter((c) => c.from === 'human' && c.screenshotContext).length,
|
|
1629
|
+
actions: conversationsWithoutScreenshots
|
|
1630
|
+
.filter((c) => c.from === 'assistant' && c.actionType)
|
|
1631
|
+
.map((c) => c.actionType),
|
|
1632
|
+
conversations: conversationsWithoutScreenshots,
|
|
1633
|
+
error: result.error,
|
|
1634
|
+
},
|
|
1311
1635
|
};
|
|
1312
1636
|
}
|
|
1313
1637
|
}
|
|
@@ -1319,32 +1643,58 @@ export class TaskTool {
|
|
|
1319
1643
|
// If the user cancelled the task, ignore any API errors (like 429)
|
|
1320
1644
|
// and return cancelled status instead
|
|
1321
1645
|
if (cancelled || cancellationManager.isOperationCancelled()) {
|
|
1646
|
+
// SDK mode: use adapter output
|
|
1647
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1648
|
+
sdkOutputAdapter.outputGUIAgentCancelled(description);
|
|
1649
|
+
}
|
|
1322
1650
|
return {
|
|
1323
1651
|
success: true,
|
|
1324
1652
|
cancelled: true, // Mark as cancelled so main agent won't continue
|
|
1325
1653
|
message: `GUI task "${description}" cancelled by user`,
|
|
1326
|
-
result: 'Task cancelled'
|
|
1654
|
+
result: 'Task cancelled',
|
|
1327
1655
|
};
|
|
1328
1656
|
}
|
|
1329
1657
|
if (error.message === 'Operation cancelled by user') {
|
|
1658
|
+
// SDK mode: use adapter output
|
|
1659
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1660
|
+
sdkOutputAdapter.outputGUIAgentCancelled(description);
|
|
1661
|
+
}
|
|
1330
1662
|
return {
|
|
1331
1663
|
success: true,
|
|
1332
1664
|
message: `GUI task "${description}" cancelled by user`,
|
|
1333
|
-
result: 'Task cancelled'
|
|
1665
|
+
result: 'Task cancelled',
|
|
1334
1666
|
};
|
|
1335
1667
|
}
|
|
1336
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
|
+
}
|
|
1337
1673
|
return {
|
|
1338
1674
|
success: false,
|
|
1339
|
-
message: `GUI task "${description}" failed: ${error.message}
|
|
1675
|
+
message: `GUI task "${description}" failed: ${error.message}`,
|
|
1340
1676
|
};
|
|
1341
1677
|
}
|
|
1342
1678
|
}
|
|
1343
|
-
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) {
|
|
1344
1680
|
const agent = agentManager.getAgent(subagent_type);
|
|
1345
1681
|
if (!agent) {
|
|
1346
1682
|
throw new Error(`Agent ${subagent_type} not found`);
|
|
1347
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
|
+
}
|
|
1348
1698
|
// Special handling for gui-subagent: directly call GUIAgent.run() instead of subagent message loop
|
|
1349
1699
|
if (subagent_type === 'gui-subagent') {
|
|
1350
1700
|
// Get RemoteAIClient instance from session (if available)
|
|
@@ -1356,7 +1706,7 @@ export class TaskTool {
|
|
|
1356
1706
|
remoteAIClient = session.getRemoteAIClient();
|
|
1357
1707
|
}
|
|
1358
1708
|
}
|
|
1359
|
-
catch
|
|
1709
|
+
catch {
|
|
1360
1710
|
// Session not available, keep undefined
|
|
1361
1711
|
remoteAIClient = undefined;
|
|
1362
1712
|
}
|
|
@@ -1385,28 +1735,55 @@ export class TaskTool {
|
|
|
1385
1735
|
modelName = agent.model;
|
|
1386
1736
|
}
|
|
1387
1737
|
}
|
|
1388
|
-
// Create
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1738
|
+
// Create AI client for this subagent - each subagent gets its own independent client
|
|
1739
|
+
let subAgentClient;
|
|
1740
|
+
let isRemoteMode = false;
|
|
1741
|
+
let mainTaskId = null;
|
|
1742
|
+
const authConfig = config.getAuthConfig();
|
|
1743
|
+
if (authConfig.type === AuthType.OAUTH_XAGENT) {
|
|
1744
|
+
// Remote mode: create independent RemoteAIClient for each subagent
|
|
1745
|
+
// This prevents message queue conflicts when multiple subagents run in parallel
|
|
1746
|
+
const session = getSingletonSession();
|
|
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;
|
|
1752
|
+
isRemoteMode = true;
|
|
1753
|
+
mainTaskId = session?.getTaskId() || null;
|
|
1754
|
+
}
|
|
1755
|
+
else {
|
|
1756
|
+
subAgentClient = createAIClient(authConfig);
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
else {
|
|
1760
|
+
// Local mode: create client with subagent-specific model config
|
|
1761
|
+
const subAuthConfig = {
|
|
1762
|
+
...authConfig,
|
|
1763
|
+
type: AuthType.OPENAI_COMPATIBLE,
|
|
1764
|
+
apiKey: apiKey,
|
|
1765
|
+
baseUrl: baseUrl,
|
|
1766
|
+
modelName: modelName,
|
|
1767
|
+
showAIDebugInfo: config.get('showAIDebugInfo') || false,
|
|
1768
|
+
};
|
|
1769
|
+
subAgentClient = createAIClient(subAuthConfig);
|
|
1770
|
+
}
|
|
1397
1771
|
const indent = ' '.repeat(indentLevel);
|
|
1398
|
-
const
|
|
1772
|
+
const _indentNext = ' '.repeat(indentLevel + 1);
|
|
1399
1773
|
const agentName = agent.name || subagent_type;
|
|
1400
1774
|
// Track execution history for better reporting to main agent
|
|
1401
1775
|
const executionHistory = [];
|
|
1402
1776
|
// Helper function to indent multi-line content
|
|
1403
1777
|
const indentMultiline = (content, baseIndent) => {
|
|
1404
|
-
return content
|
|
1778
|
+
return content
|
|
1779
|
+
.split('\n')
|
|
1780
|
+
.map((line) => `${baseIndent} ${line}`)
|
|
1781
|
+
.join('\n');
|
|
1405
1782
|
};
|
|
1406
1783
|
const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, mode, agent);
|
|
1407
1784
|
const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(agent.systemPrompt);
|
|
1408
1785
|
const fullPrompt = constraints.length > 0
|
|
1409
|
-
? `${prompt}\n\nConstraints:\n${constraints.map(c => `- ${c}`).join('\n')}`
|
|
1786
|
+
? `${prompt}\n\nConstraints:\n${constraints.map((c) => `- ${c}`).join('\n')}`
|
|
1410
1787
|
: prompt;
|
|
1411
1788
|
// Set up raw mode and stdin polling for ESC detection
|
|
1412
1789
|
const cancellationManager = getCancellationManager();
|
|
@@ -1432,14 +1809,15 @@ export class TaskTool {
|
|
|
1432
1809
|
const chunk = process.stdin.read(1);
|
|
1433
1810
|
if (chunk && chunk.length > 0) {
|
|
1434
1811
|
const code = chunk[0];
|
|
1435
|
-
if (code ===
|
|
1812
|
+
if (code === 0x1b) {
|
|
1813
|
+
// ESC
|
|
1436
1814
|
logger.debug('[TaskTool] ESC detected via polling!');
|
|
1437
1815
|
cancellationManager.cancel();
|
|
1438
1816
|
}
|
|
1439
1817
|
}
|
|
1440
1818
|
}
|
|
1441
1819
|
}
|
|
1442
|
-
catch
|
|
1820
|
+
catch {
|
|
1443
1821
|
// Ignore polling errors
|
|
1444
1822
|
}
|
|
1445
1823
|
}, 10);
|
|
@@ -1466,9 +1844,9 @@ export class TaskTool {
|
|
|
1466
1844
|
throw new Error('Operation cancelled by user');
|
|
1467
1845
|
}
|
|
1468
1846
|
};
|
|
1469
|
-
|
|
1847
|
+
const messages = [
|
|
1470
1848
|
{ role: 'system', content: enhancedSystemPrompt },
|
|
1471
|
-
{ role: 'user', content: fullPrompt }
|
|
1849
|
+
{ role: 'user', content: fullPrompt },
|
|
1472
1850
|
];
|
|
1473
1851
|
const availableTools = agentManager.getAvailableToolsForAgent(agent, mode);
|
|
1474
1852
|
const allToolDefinitions = toolRegistry.getToolDefinitions();
|
|
@@ -1482,21 +1860,34 @@ export class TaskTool {
|
|
|
1482
1860
|
function: {
|
|
1483
1861
|
name: toolName,
|
|
1484
1862
|
description: `Tool: ${toolName}`,
|
|
1485
|
-
parameters: { type: 'object', properties: {}, required: [] }
|
|
1486
|
-
}
|
|
1863
|
+
parameters: { type: 'object', properties: {}, required: [] },
|
|
1864
|
+
},
|
|
1487
1865
|
};
|
|
1488
1866
|
});
|
|
1489
1867
|
let iteration = 0;
|
|
1490
|
-
|
|
1491
|
-
|
|
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) {
|
|
1492
1872
|
iteration++;
|
|
1493
1873
|
// Check for cancellation before each iteration
|
|
1494
1874
|
checkCancellation();
|
|
1495
|
-
//
|
|
1496
|
-
const
|
|
1875
|
+
// Prepare chat options with taskId and model names for remote mode
|
|
1876
|
+
const chatOptions = {
|
|
1497
1877
|
tools: toolDefinitions,
|
|
1498
|
-
temperature: 0.7
|
|
1499
|
-
}
|
|
1878
|
+
temperature: 0.7,
|
|
1879
|
+
};
|
|
1880
|
+
// Pass taskId, status, and model names for remote mode subagent calls
|
|
1881
|
+
// Subagent shares the same taskId as the main task
|
|
1882
|
+
if (isRemoteMode && mainTaskId) {
|
|
1883
|
+
chatOptions.taskId = mainTaskId;
|
|
1884
|
+
chatOptions.status = iteration === 1 ? 'begin' : 'continue';
|
|
1885
|
+
// Pass model names to ensure subagent uses the same models as main task
|
|
1886
|
+
chatOptions.llmModelName = config.get('remote_llmModelName');
|
|
1887
|
+
chatOptions.vlmModelName = config.get('remote_vlmModelName');
|
|
1888
|
+
}
|
|
1889
|
+
// Use withCancellation to make API call cancellable
|
|
1890
|
+
const result = (await cancellationManager.withCancellation(subAgentClient.chatCompletion(messages, chatOptions), `api-${subagent_type}-${iteration}`));
|
|
1500
1891
|
// Check for cancellation after API call
|
|
1501
1892
|
checkCancellation();
|
|
1502
1893
|
if (!result || !result.choices || result.choices.length === 0) {
|
|
@@ -1514,8 +1905,8 @@ export class TaskTool {
|
|
|
1514
1905
|
}
|
|
1515
1906
|
else if (Array.isArray(messageContent)) {
|
|
1516
1907
|
const textParts = messageContent
|
|
1517
|
-
.filter(item => typeof item?.text === 'string' && item.text.trim() !== '')
|
|
1518
|
-
.map(item => item.text);
|
|
1908
|
+
.filter((item) => typeof item?.text === 'string' && item.text.trim() !== '')
|
|
1909
|
+
.map((item) => item.text);
|
|
1519
1910
|
contentStr = textParts.join('');
|
|
1520
1911
|
hasValidContent = textParts.length > 0;
|
|
1521
1912
|
}
|
|
@@ -1531,133 +1922,77 @@ export class TaskTool {
|
|
|
1531
1922
|
if (choice.finish_reason === 'length') {
|
|
1532
1923
|
throw new Error(`Sub-agent ${subagent_type} response truncated due to length limits`);
|
|
1533
1924
|
}
|
|
1534
|
-
// Add assistant message to conversation
|
|
1535
|
-
|
|
1925
|
+
// Add assistant message to conversation (必须包含 tool_calls,否则 tool_result 无法匹配)
|
|
1926
|
+
const assistantMessage = { role: 'assistant', content: contentStr };
|
|
1927
|
+
if (toolCalls && toolCalls.length > 0) {
|
|
1928
|
+
assistantMessage.tool_calls = toolCalls;
|
|
1929
|
+
}
|
|
1930
|
+
if (reasoningContent) {
|
|
1931
|
+
assistantMessage.reasoning_content = reasoningContent;
|
|
1932
|
+
}
|
|
1933
|
+
messages.push(assistantMessage);
|
|
1536
1934
|
// Display reasoning content if present
|
|
1537
1935
|
if (reasoningContent) {
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
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
|
+
}
|
|
1542
1947
|
}
|
|
1543
1948
|
// Display assistant response (if there's any text content) with proper indentation
|
|
1544
1949
|
if (contentStr) {
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
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
|
+
}
|
|
1549
1959
|
}
|
|
1550
|
-
// Process tool calls
|
|
1960
|
+
// Process tool calls in parallel (照搬 session 的实现)
|
|
1551
1961
|
if (toolCalls && toolCalls.length > 0) {
|
|
1552
|
-
|
|
1962
|
+
// Prepare all tool calls with their indices
|
|
1963
|
+
const preparedToolCalls = toolCalls.map((toolCall, index) => {
|
|
1553
1964
|
const { name, arguments: params } = toolCall.function;
|
|
1554
1965
|
let parsedParams;
|
|
1555
1966
|
try {
|
|
1556
1967
|
parsedParams = typeof params === 'string' ? JSON.parse(params) : params;
|
|
1557
1968
|
}
|
|
1558
|
-
catch
|
|
1969
|
+
catch {
|
|
1559
1970
|
parsedParams = params;
|
|
1560
1971
|
}
|
|
1561
|
-
|
|
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) => {
|
|
1562
1985
|
try {
|
|
1563
1986
|
// Check cancellation before tool execution
|
|
1564
1987
|
checkCancellation();
|
|
1565
|
-
const toolResult = await cancellationManager.withCancellation(toolRegistry.execute(name,
|
|
1566
|
-
|
|
1567
|
-
const showToolDetails = config.get('showToolDetails') || false;
|
|
1568
|
-
// Prepare result preview for history
|
|
1569
|
-
const resultPreview = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult, null, 2);
|
|
1570
|
-
const truncatedPreview = resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
|
|
1571
|
-
// Special handling for different tools (consistent with session.ts display logic)
|
|
1572
|
-
const isTodoTool = name === 'todo_write' || name === 'todo_read';
|
|
1573
|
-
const isEditTool = name === 'Edit';
|
|
1574
|
-
const isWriteTool = name === 'Write';
|
|
1575
|
-
const isDeleteTool = name === 'DeleteFile';
|
|
1576
|
-
const hasDiff = isEditTool && toolResult?.diff;
|
|
1577
|
-
const hasFilePreview = isWriteTool && toolResult?.preview;
|
|
1578
|
-
const hasDeleteInfo = isDeleteTool && toolResult?.filePath;
|
|
1579
|
-
// Import render functions for consistent display
|
|
1580
|
-
const { renderDiff, renderLines } = await import('./theme.js');
|
|
1581
|
-
if (isTodoTool) {
|
|
1582
|
-
// Display todo list
|
|
1583
|
-
console.log(`${indent}${colors.success(`${icons.check} Todo List:`)}`);
|
|
1584
|
-
const todos = toolResult?.todos || [];
|
|
1585
|
-
if (todos.length === 0) {
|
|
1586
|
-
console.log(`${indent} ${colors.textMuted('No tasks')}`);
|
|
1587
|
-
}
|
|
1588
|
-
else {
|
|
1589
|
-
const statusConfig = {
|
|
1590
|
-
'pending': { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
|
|
1591
|
-
'in_progress': { icon: icons.loading, color: colors.warning, label: 'In Progress' },
|
|
1592
|
-
'completed': { icon: icons.success, color: colors.success, label: 'Completed' },
|
|
1593
|
-
'failed': { icon: icons.error, color: colors.error, label: 'Failed' }
|
|
1594
|
-
};
|
|
1595
|
-
for (const todo of todos) {
|
|
1596
|
-
const status = statusConfig[todo.status] || statusConfig['pending'];
|
|
1597
|
-
console.log(`${indent} ${status.color(status.icon)} ${status.color(status.label)}: ${colors.text(todo.task)}`);
|
|
1598
|
-
}
|
|
1599
|
-
}
|
|
1600
|
-
if (toolResult?.message) {
|
|
1601
|
-
console.log(`${indent}${colors.textDim(toolResult.message)}`);
|
|
1602
|
-
}
|
|
1603
|
-
console.log('');
|
|
1604
|
-
}
|
|
1605
|
-
else if (hasDiff) {
|
|
1606
|
-
// Display edit result with diff
|
|
1607
|
-
console.log('');
|
|
1608
|
-
const diffOutput = renderDiff(toolResult.diff);
|
|
1609
|
-
const indentedDiff = diffOutput.split('\n').map(line => `${indent} ${line}`).join('\n');
|
|
1610
|
-
console.log(`${indentedDiff}\n`);
|
|
1611
|
-
}
|
|
1612
|
-
else if (hasFilePreview) {
|
|
1613
|
-
// Display new file content in preview style
|
|
1614
|
-
console.log('');
|
|
1615
|
-
console.log(`${indent}${colors.success(`${icons.file} ${toolResult.filePath}`)}`);
|
|
1616
|
-
console.log(`${indent}${colors.textDim(` ${toolResult.lineCount} lines`)}`);
|
|
1617
|
-
console.log('');
|
|
1618
|
-
console.log(renderLines(toolResult.preview, { maxLines: 10, indent: indent + ' ' }));
|
|
1619
|
-
console.log('');
|
|
1620
|
-
}
|
|
1621
|
-
else if (hasDeleteInfo) {
|
|
1622
|
-
// Display DeleteFile result
|
|
1623
|
-
console.log('');
|
|
1624
|
-
console.log(`${indent}${colors.success(`${icons.check} Deleted: ${toolResult.filePath}`)}`);
|
|
1625
|
-
console.log('');
|
|
1626
|
-
}
|
|
1627
|
-
else if (showToolDetails) {
|
|
1628
|
-
// Show full result details
|
|
1629
|
-
const indentedPreview = indentMultiline(resultPreview, indent);
|
|
1630
|
-
console.log(`${indent}${colors.success(`${icons.check} Tool Result:`)}\n${indentedPreview}\n`);
|
|
1631
|
-
}
|
|
1632
|
-
else if (toolResult && toolResult.success === false) {
|
|
1633
|
-
// Tool failed
|
|
1634
|
-
console.log(`${indent}${colors.error(`${icons.cross} ${toolResult.message || 'Failed'}`)}\n`);
|
|
1635
|
-
}
|
|
1636
|
-
else if (toolResult) {
|
|
1637
|
-
// Show brief preview by default
|
|
1638
|
-
const indentedPreview = indentMultiline(truncatedPreview, indent);
|
|
1639
|
-
console.log(`${indent}${colors.success(`${icons.check} Completed`)}\n${indentedPreview}\n`);
|
|
1640
|
-
}
|
|
1641
|
-
else {
|
|
1642
|
-
console.log(`${indent}${colors.textDim('(no result)')}\n`);
|
|
1643
|
-
}
|
|
1644
|
-
// Record successful tool execution in history (use truncated preview to save memory)
|
|
1645
|
-
executionHistory.push({
|
|
1646
|
-
tool: name,
|
|
1647
|
-
status: 'success',
|
|
1648
|
-
params: parsedParams,
|
|
1649
|
-
result: truncatedPreview,
|
|
1650
|
-
timestamp: new Date().toISOString()
|
|
1651
|
-
});
|
|
1652
|
-
messages.push({
|
|
1653
|
-
role: 'tool',
|
|
1654
|
-
content: JSON.stringify(toolResult),
|
|
1655
|
-
tool_call_id: toolCall.id
|
|
1656
|
-
});
|
|
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 };
|
|
1657
1990
|
}
|
|
1658
1991
|
catch (error) {
|
|
1659
1992
|
if (error.message === 'Operation cancelled by user') {
|
|
1660
|
-
|
|
1993
|
+
if (!isSdkMode || !sdkOutputAdapter) {
|
|
1994
|
+
console.log(`${indent}${colors.warning(`⚠️ Operation cancelled`)}\n`);
|
|
1995
|
+
}
|
|
1661
1996
|
cancellationManager.off('cancelled', cancelHandler);
|
|
1662
1997
|
cleanupStdinPolling();
|
|
1663
1998
|
const summaryPreview = contentStr.length > 300 ? contentStr.substring(0, 300) + '...' : contentStr;
|
|
@@ -1669,78 +2004,195 @@ export class TaskTool {
|
|
|
1669
2004
|
executionHistory: {
|
|
1670
2005
|
totalIterations: iteration,
|
|
1671
2006
|
toolsExecuted: executionHistory.length,
|
|
1672
|
-
successfulTools: executionHistory.filter(t => t.status === 'success').length,
|
|
1673
|
-
failedTools: executionHistory.filter(t => t.status === 'error').length,
|
|
2007
|
+
successfulTools: executionHistory.filter((t) => t.status === 'success').length,
|
|
2008
|
+
failedTools: executionHistory.filter((t) => t.status === 'error').length,
|
|
1674
2009
|
history: executionHistory,
|
|
1675
|
-
cancelled: true
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
2010
|
+
cancelled: true,
|
|
2011
|
+
},
|
|
2012
|
+
},
|
|
1678
2013
|
};
|
|
1679
2014
|
}
|
|
1680
|
-
|
|
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
|
+
}
|
|
1681
2055
|
// Record failed tool execution in history
|
|
1682
2056
|
executionHistory.push({
|
|
1683
2057
|
tool: name,
|
|
1684
2058
|
status: 'error',
|
|
1685
2059
|
params: parsedParams,
|
|
1686
|
-
error
|
|
1687
|
-
timestamp: new Date().toISOString()
|
|
2060
|
+
error,
|
|
2061
|
+
timestamp: new Date().toISOString(),
|
|
1688
2062
|
});
|
|
1689
2063
|
messages.push({
|
|
1690
2064
|
role: 'tool',
|
|
1691
|
-
content: JSON.stringify({ error
|
|
1692
|
-
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,
|
|
1693
2164
|
});
|
|
1694
2165
|
}
|
|
1695
2166
|
}
|
|
1696
|
-
|
|
2167
|
+
if (!isSdkMode || !sdkOutputAdapter) {
|
|
2168
|
+
console.log('');
|
|
2169
|
+
}
|
|
1697
2170
|
continue; // Continue to next iteration to get final response
|
|
1698
2171
|
}
|
|
1699
|
-
// No more tool calls
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
const summaryPreview = contentStr.length > 300 ? contentStr.substring(0, 300) + '...' : contentStr;
|
|
1703
|
-
return {
|
|
1704
|
-
success: true,
|
|
1705
|
-
message: `Task "${description}" completed by ${subagent_type}`,
|
|
1706
|
-
result: {
|
|
1707
|
-
summary: summaryPreview,
|
|
1708
|
-
executionHistory: {
|
|
1709
|
-
totalIterations: iteration,
|
|
1710
|
-
toolsExecuted: executionHistory.length,
|
|
1711
|
-
successfulTools: executionHistory.filter(t => t.status === 'success').length,
|
|
1712
|
-
failedTools: executionHistory.filter(t => t.status === 'error').length,
|
|
1713
|
-
history: executionHistory
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
};
|
|
2172
|
+
// No more tool calls - break loop (same as main agent)
|
|
2173
|
+
lastContentStr = contentStr || '';
|
|
2174
|
+
break;
|
|
1717
2175
|
}
|
|
1718
|
-
//
|
|
1719
|
-
// Get the last assistant message content
|
|
1720
|
-
const lastAssistantMsg = messages.filter(m => m.role === 'assistant').pop();
|
|
1721
|
-
const lastContentStr = typeof lastAssistantMsg?.content === 'string'
|
|
1722
|
-
? lastAssistantMsg.content
|
|
1723
|
-
: JSON.stringify(lastAssistantMsg?.content || '');
|
|
2176
|
+
// Loop ended - return result (same as main agent pattern)
|
|
1724
2177
|
cancellationManager.off('cancelled', cancelHandler);
|
|
1725
2178
|
cleanupStdinPolling();
|
|
1726
2179
|
const summaryPreview = lastContentStr.length > 300 ? lastContentStr.substring(0, 300) + '...' : lastContentStr;
|
|
1727
2180
|
return {
|
|
1728
2181
|
success: true,
|
|
1729
|
-
message: `Task "${description}" completed
|
|
2182
|
+
message: `Task "${description}" completed by ${subagent_type}`,
|
|
1730
2183
|
result: {
|
|
1731
2184
|
summary: summaryPreview,
|
|
1732
2185
|
executionHistory: {
|
|
1733
2186
|
totalIterations: iteration,
|
|
1734
2187
|
toolsExecuted: executionHistory.length,
|
|
1735
|
-
successfulTools: executionHistory.filter(t => t.status === 'success').length,
|
|
1736
|
-
failedTools: executionHistory.filter(t => t.status === 'error').length,
|
|
2188
|
+
successfulTools: executionHistory.filter((t) => t.status === 'success').length,
|
|
2189
|
+
failedTools: executionHistory.filter((t) => t.status === 'error').length,
|
|
1737
2190
|
history: executionHistory,
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
}
|
|
2191
|
+
},
|
|
2192
|
+
},
|
|
1741
2193
|
};
|
|
1742
2194
|
}
|
|
1743
|
-
async executeParallelAgents(agents, description, mode, agentManager, toolRegistry,
|
|
2195
|
+
async executeParallelAgents(agents, description, mode, agentManager, toolRegistry, config, indentLevel = 1) {
|
|
1744
2196
|
const indent = ' '.repeat(indentLevel);
|
|
1745
2197
|
const indentNext = ' '.repeat(indentLevel + 1);
|
|
1746
2198
|
const cancellationManager = getCancellationManager();
|
|
@@ -1765,14 +2217,15 @@ export class TaskTool {
|
|
|
1765
2217
|
const chunk = process.stdin.read(1);
|
|
1766
2218
|
if (chunk && chunk.length > 0) {
|
|
1767
2219
|
const code = chunk[0];
|
|
1768
|
-
if (code ===
|
|
2220
|
+
if (code === 0x1b) {
|
|
2221
|
+
// ESC
|
|
1769
2222
|
logger.debug('[ParallelAgents] ESC detected via polling!');
|
|
1770
2223
|
cancellationManager.cancel();
|
|
1771
2224
|
}
|
|
1772
2225
|
}
|
|
1773
2226
|
}
|
|
1774
2227
|
}
|
|
1775
|
-
catch
|
|
2228
|
+
catch {
|
|
1776
2229
|
// Ignore polling errors
|
|
1777
2230
|
}
|
|
1778
2231
|
}, 10);
|
|
@@ -1794,23 +2247,23 @@ export class TaskTool {
|
|
|
1794
2247
|
cancellationManager.on('cancelled', cancelHandler);
|
|
1795
2248
|
console.log(`\n${indent}${colors.accent('◆')} ${colors.primaryBright('Parallel Agents')}: ${agents.length} running...`);
|
|
1796
2249
|
const startTime = Date.now();
|
|
1797
|
-
const agentPromises = agents.map(async (agentTask,
|
|
2250
|
+
const agentPromises = agents.map(async (agentTask, _index) => {
|
|
1798
2251
|
// Check if cancelled
|
|
1799
2252
|
if (cancelled || cancellationManager.isOperationCancelled()) {
|
|
1800
2253
|
return {
|
|
1801
2254
|
success: false,
|
|
1802
2255
|
agent: agentTask.subagent_type,
|
|
1803
2256
|
description: agentTask.description,
|
|
1804
|
-
error: 'Operation cancelled by user'
|
|
2257
|
+
error: 'Operation cancelled by user',
|
|
1805
2258
|
};
|
|
1806
2259
|
}
|
|
1807
2260
|
try {
|
|
1808
|
-
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);
|
|
1809
2262
|
return {
|
|
1810
2263
|
success: true,
|
|
1811
2264
|
agent: agentTask.subagent_type,
|
|
1812
2265
|
description: agentTask.description,
|
|
1813
|
-
result: result.result
|
|
2266
|
+
result: result.result,
|
|
1814
2267
|
};
|
|
1815
2268
|
}
|
|
1816
2269
|
catch (error) {
|
|
@@ -1818,14 +2271,14 @@ export class TaskTool {
|
|
|
1818
2271
|
success: false,
|
|
1819
2272
|
agent: agentTask.subagent_type,
|
|
1820
2273
|
description: agentTask.description,
|
|
1821
|
-
error: error.message
|
|
2274
|
+
error: error.message,
|
|
1822
2275
|
};
|
|
1823
2276
|
}
|
|
1824
2277
|
});
|
|
1825
2278
|
const results = await Promise.all(agentPromises);
|
|
1826
2279
|
const duration = Date.now() - startTime;
|
|
1827
|
-
const successfulAgents = results.filter(r => r.success);
|
|
1828
|
-
const failedAgents = results.filter(r => !r.success);
|
|
2280
|
+
const successfulAgents = results.filter((r) => r.success);
|
|
2281
|
+
const failedAgents = results.filter((r) => !r.success);
|
|
1829
2282
|
console.log(`${indent}${colors.success('✔')} Parallel task completed in ${colors.textMuted(duration + 'ms')}`);
|
|
1830
2283
|
console.log(`${indent}${colors.info('ℹ')} Success: ${successfulAgents.length}/${agents.length} agents\n`);
|
|
1831
2284
|
if (failedAgents.length > 0) {
|
|
@@ -1841,16 +2294,16 @@ export class TaskTool {
|
|
|
1841
2294
|
return {
|
|
1842
2295
|
success: failedAgents.length === 0,
|
|
1843
2296
|
message: `Parallel task "${description}" completed: ${successfulAgents.length}/${agents.length} successful`,
|
|
1844
|
-
results: successfulAgents.map(r => ({
|
|
2297
|
+
results: successfulAgents.map((r) => ({
|
|
1845
2298
|
agent: r.agent,
|
|
1846
2299
|
description: r.description,
|
|
1847
|
-
result: r.result
|
|
2300
|
+
result: r.result,
|
|
1848
2301
|
})),
|
|
1849
|
-
errors: failedAgents.map(r => ({
|
|
2302
|
+
errors: failedAgents.map((r) => ({
|
|
1850
2303
|
agent: r.agent,
|
|
1851
2304
|
description: r.description,
|
|
1852
|
-
error: r.error
|
|
1853
|
-
}))
|
|
2305
|
+
error: r.error,
|
|
2306
|
+
})),
|
|
1854
2307
|
};
|
|
1855
2308
|
}
|
|
1856
2309
|
}
|
|
@@ -1882,7 +2335,12 @@ export class ReadBashOutputTool {
|
|
|
1882
2335
|
- Use appropriate poll_interval based on expected task duration
|
|
1883
2336
|
- Check status to see if task is still running or completed
|
|
1884
2337
|
- Combine with todo_write to track background task progress`;
|
|
1885
|
-
allowedModes = [
|
|
2338
|
+
allowedModes = [
|
|
2339
|
+
ExecutionMode.YOLO,
|
|
2340
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
2341
|
+
ExecutionMode.PLAN,
|
|
2342
|
+
ExecutionMode.SMART,
|
|
2343
|
+
];
|
|
1886
2344
|
async execute(params) {
|
|
1887
2345
|
const { task_id, poll_interval = 10 } = params;
|
|
1888
2346
|
try {
|
|
@@ -1892,7 +2350,7 @@ export class ReadBashOutputTool {
|
|
|
1892
2350
|
throw new Error(`Task ${task_id} not found`);
|
|
1893
2351
|
}
|
|
1894
2352
|
const interval = Math.min(Math.max(poll_interval, 1), 120);
|
|
1895
|
-
await new Promise(resolve => setTimeout(resolve, interval * 1000));
|
|
2353
|
+
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
|
|
1896
2354
|
const duration = Date.now() - task.startTime;
|
|
1897
2355
|
const output = task.output.join('');
|
|
1898
2356
|
const status = task.process.exitCode === null ? 'running' : 'completed';
|
|
@@ -1900,7 +2358,7 @@ export class ReadBashOutputTool {
|
|
|
1900
2358
|
taskId: task_id,
|
|
1901
2359
|
output,
|
|
1902
2360
|
status,
|
|
1903
|
-
duration: Math.floor(duration / 1000)
|
|
2361
|
+
duration: Math.floor(duration / 1000),
|
|
1904
2362
|
};
|
|
1905
2363
|
}
|
|
1906
2364
|
catch (error) {
|
|
@@ -1936,7 +2394,12 @@ export class WebFetchTool {
|
|
|
1936
2394
|
- Use specific prompts to extract relevant information
|
|
1937
2395
|
- Check if the page is accessible if you get errors
|
|
1938
2396
|
- Large pages may be truncated due to size limits`;
|
|
1939
|
-
allowedModes = [
|
|
2397
|
+
allowedModes = [
|
|
2398
|
+
ExecutionMode.YOLO,
|
|
2399
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
2400
|
+
ExecutionMode.PLAN,
|
|
2401
|
+
ExecutionMode.SMART,
|
|
2402
|
+
];
|
|
1940
2403
|
async execute(params) {
|
|
1941
2404
|
const { prompt } = params;
|
|
1942
2405
|
try {
|
|
@@ -1948,7 +2411,7 @@ export class WebFetchTool {
|
|
|
1948
2411
|
const response = await axios.get(url, {
|
|
1949
2412
|
timeout: 30000,
|
|
1950
2413
|
maxContentLength: 10 * 1024 * 1024,
|
|
1951
|
-
validateStatus: () => true
|
|
2414
|
+
validateStatus: () => true,
|
|
1952
2415
|
});
|
|
1953
2416
|
let content = response.data;
|
|
1954
2417
|
if (typeof content === 'object') {
|
|
@@ -1957,7 +2420,7 @@ export class WebFetchTool {
|
|
|
1957
2420
|
return {
|
|
1958
2421
|
content,
|
|
1959
2422
|
url,
|
|
1960
|
-
status: response.status
|
|
2423
|
+
status: response.status,
|
|
1961
2424
|
};
|
|
1962
2425
|
}
|
|
1963
2426
|
catch (error) {
|
|
@@ -1998,8 +2461,26 @@ export class AskUserQuestionTool {
|
|
|
1998
2461
|
- Provide options when possible for faster response
|
|
1999
2462
|
- Use multiSelect=true when multiple answers are valid
|
|
2000
2463
|
- Be clear and concise in question wording`;
|
|
2001
|
-
allowedModes = [
|
|
2464
|
+
allowedModes = [
|
|
2465
|
+
ExecutionMode.YOLO,
|
|
2466
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
2467
|
+
ExecutionMode.PLAN,
|
|
2468
|
+
ExecutionMode.SMART,
|
|
2469
|
+
];
|
|
2002
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) {
|
|
2003
2484
|
const { questions } = params;
|
|
2004
2485
|
try {
|
|
2005
2486
|
if (questions.length === 0 || questions.length > 4) {
|
|
@@ -2008,26 +2489,18 @@ export class AskUserQuestionTool {
|
|
|
2008
2489
|
const answers = [];
|
|
2009
2490
|
for (const q of questions) {
|
|
2010
2491
|
if (q.options && q.options.length > 0) {
|
|
2011
|
-
const
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
default: q.multiSelect ? [] : q.options[0]
|
|
2018
|
-
}
|
|
2019
|
-
]);
|
|
2020
|
-
answers.push(Array.isArray(result.answer) ? result.answer.join(', ') : result.answer);
|
|
2492
|
+
const options = q.options.map((opt) => ({ value: opt, label: opt }));
|
|
2493
|
+
const result = await select({
|
|
2494
|
+
message: q.question,
|
|
2495
|
+
options,
|
|
2496
|
+
});
|
|
2497
|
+
answers.push(result);
|
|
2021
2498
|
}
|
|
2022
2499
|
else {
|
|
2023
|
-
const result = await
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
message: q.question
|
|
2028
|
-
}
|
|
2029
|
-
]);
|
|
2030
|
-
answers.push(result.answer);
|
|
2500
|
+
const result = (await text({
|
|
2501
|
+
message: q.question,
|
|
2502
|
+
}));
|
|
2503
|
+
answers.push(result);
|
|
2031
2504
|
}
|
|
2032
2505
|
}
|
|
2033
2506
|
return { answers };
|
|
@@ -2036,6 +2509,38 @@ export class AskUserQuestionTool {
|
|
|
2036
2509
|
throw new Error(`Failed to ask user questions: ${error.message}`);
|
|
2037
2510
|
}
|
|
2038
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
|
+
}
|
|
2039
2544
|
}
|
|
2040
2545
|
export class SaveMemoryTool {
|
|
2041
2546
|
name = 'save_memory';
|
|
@@ -2066,7 +2571,12 @@ export class SaveMemoryTool {
|
|
|
2066
2571
|
- Keep facts concise and specific
|
|
2067
2572
|
- Remember project-specific conventions for consistency
|
|
2068
2573
|
- This persists across sessions (global memory)`;
|
|
2069
|
-
allowedModes = [
|
|
2574
|
+
allowedModes = [
|
|
2575
|
+
ExecutionMode.YOLO,
|
|
2576
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
2577
|
+
ExecutionMode.PLAN,
|
|
2578
|
+
ExecutionMode.SMART,
|
|
2579
|
+
];
|
|
2070
2580
|
async execute(params) {
|
|
2071
2581
|
const { fact } = params;
|
|
2072
2582
|
try {
|
|
@@ -2075,7 +2585,7 @@ export class SaveMemoryTool {
|
|
|
2075
2585
|
await memoryManager.saveMemory(fact, 'global');
|
|
2076
2586
|
return {
|
|
2077
2587
|
success: true,
|
|
2078
|
-
message: `Successfully saved fact to memory
|
|
2588
|
+
message: `Successfully saved fact to memory`,
|
|
2079
2589
|
};
|
|
2080
2590
|
}
|
|
2081
2591
|
catch (error) {
|
|
@@ -2111,14 +2621,19 @@ export class ExitPlanModeTool {
|
|
|
2111
2621
|
- Include all necessary steps and considerations
|
|
2112
2622
|
- The plan will be saved for reference during execution
|
|
2113
2623
|
- Use this only when truly ready to start coding`;
|
|
2114
|
-
allowedModes = [
|
|
2624
|
+
allowedModes = [
|
|
2625
|
+
ExecutionMode.YOLO,
|
|
2626
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
2627
|
+
ExecutionMode.PLAN,
|
|
2628
|
+
ExecutionMode.SMART,
|
|
2629
|
+
];
|
|
2115
2630
|
async execute(params) {
|
|
2116
2631
|
const { plan } = params;
|
|
2117
2632
|
try {
|
|
2118
2633
|
return {
|
|
2119
2634
|
success: true,
|
|
2120
2635
|
message: 'Plan completed and ready for execution',
|
|
2121
|
-
plan
|
|
2636
|
+
plan,
|
|
2122
2637
|
};
|
|
2123
2638
|
}
|
|
2124
2639
|
catch (error) {
|
|
@@ -2162,7 +2677,7 @@ export class XmlEscapeTool {
|
|
|
2162
2677
|
{ char: '<', replacement: '<' },
|
|
2163
2678
|
{ char: '>', replacement: '>' },
|
|
2164
2679
|
{ char: '"', replacement: '"' },
|
|
2165
|
-
{ char: "'", replacement: ''' }
|
|
2680
|
+
{ char: "'", replacement: ''' },
|
|
2166
2681
|
];
|
|
2167
2682
|
let changes = 0;
|
|
2168
2683
|
for (const { char, replacement } of specialChars) {
|
|
@@ -2177,7 +2692,7 @@ export class XmlEscapeTool {
|
|
|
2177
2692
|
const additionalChars = [
|
|
2178
2693
|
{ char: '©', replacement: '©' },
|
|
2179
2694
|
{ char: '®', replacement: '®' },
|
|
2180
|
-
{ char: '€', replacement: '€' }
|
|
2695
|
+
{ char: '€', replacement: '€' },
|
|
2181
2696
|
];
|
|
2182
2697
|
for (const { char, replacement } of additionalChars) {
|
|
2183
2698
|
const regex = new RegExp(this.escapeRegExp(char), 'g');
|
|
@@ -2192,7 +2707,7 @@ export class XmlEscapeTool {
|
|
|
2192
2707
|
return {
|
|
2193
2708
|
success: true,
|
|
2194
2709
|
message: `Successfully escaped ${changes} character(s) in ${file_path}`,
|
|
2195
|
-
changes
|
|
2710
|
+
changes,
|
|
2196
2711
|
};
|
|
2197
2712
|
}
|
|
2198
2713
|
catch (error) {
|
|
@@ -2232,7 +2747,12 @@ export class ImageReadTool {
|
|
|
2232
2747
|
- Provide clear prompts for what to look for
|
|
2233
2748
|
- Use task_brief for context
|
|
2234
2749
|
- Supports PNG, JPG, GIF, WEBP, SVG, BMP`;
|
|
2235
|
-
allowedModes = [
|
|
2750
|
+
allowedModes = [
|
|
2751
|
+
ExecutionMode.YOLO,
|
|
2752
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
2753
|
+
ExecutionMode.PLAN,
|
|
2754
|
+
ExecutionMode.SMART,
|
|
2755
|
+
];
|
|
2236
2756
|
async execute(params) {
|
|
2237
2757
|
const { image_input, prompt, task_brief, input_type = 'file_path', mime_type } = params;
|
|
2238
2758
|
try {
|
|
@@ -2245,16 +2765,11 @@ export class ImageReadTool {
|
|
|
2245
2765
|
else {
|
|
2246
2766
|
imageData = image_input;
|
|
2247
2767
|
}
|
|
2248
|
-
const { AIClient } = await import('./ai-client.js');
|
|
2249
2768
|
const configManager = await import('./config.js');
|
|
2250
2769
|
const { getConfigManager } = configManager;
|
|
2251
2770
|
const config = getConfigManager();
|
|
2252
|
-
const
|
|
2253
|
-
|
|
2254
|
-
apiKey: config.get('apiKey'),
|
|
2255
|
-
baseUrl: config.get('baseUrl'),
|
|
2256
|
-
modelName: config.get('modelName') || 'Qwen3-Coder'
|
|
2257
|
-
});
|
|
2771
|
+
const authConfig = config.getAuthConfig();
|
|
2772
|
+
const aiClient = createAIClient(authConfig);
|
|
2258
2773
|
const textContent = task_brief ? `${task_brief}\n\n${prompt}` : prompt;
|
|
2259
2774
|
const messages = [
|
|
2260
2775
|
{
|
|
@@ -2262,19 +2777,19 @@ export class ImageReadTool {
|
|
|
2262
2777
|
content: [
|
|
2263
2778
|
{
|
|
2264
2779
|
type: 'text',
|
|
2265
|
-
text: textContent
|
|
2780
|
+
text: textContent,
|
|
2266
2781
|
},
|
|
2267
2782
|
{
|
|
2268
2783
|
type: 'image_url',
|
|
2269
2784
|
image_url: {
|
|
2270
|
-
url: `data:${mime_type || 'image/jpeg'};base64,${imageData}
|
|
2271
|
-
}
|
|
2272
|
-
}
|
|
2273
|
-
]
|
|
2274
|
-
}
|
|
2785
|
+
url: `data:${mime_type || 'image/jpeg'};base64,${imageData}`,
|
|
2786
|
+
},
|
|
2787
|
+
},
|
|
2788
|
+
],
|
|
2789
|
+
},
|
|
2275
2790
|
];
|
|
2276
2791
|
const result = await aiClient.chatCompletion(messages, {
|
|
2277
|
-
temperature: 0.7
|
|
2792
|
+
temperature: 0.7,
|
|
2278
2793
|
});
|
|
2279
2794
|
const messageContent = result.choices[0]?.message?.content;
|
|
2280
2795
|
const analysis = typeof messageContent === 'string' ? messageContent : '';
|
|
@@ -2283,8 +2798,8 @@ export class ImageReadTool {
|
|
|
2283
2798
|
image_info: {
|
|
2284
2799
|
input_type,
|
|
2285
2800
|
prompt,
|
|
2286
|
-
task_brief
|
|
2287
|
-
}
|
|
2801
|
+
task_brief,
|
|
2802
|
+
},
|
|
2288
2803
|
};
|
|
2289
2804
|
}
|
|
2290
2805
|
catch (error) {
|
|
@@ -2365,7 +2880,6 @@ export class InvokeSkillTool {
|
|
|
2365
2880
|
const { skillId, taskDescription, inputFile, outputFile, options } = params;
|
|
2366
2881
|
try {
|
|
2367
2882
|
const { getSkillInvoker } = await import('./skill-invoker.js');
|
|
2368
|
-
const { SkillExecutionParams } = await import('./skill-invoker.js');
|
|
2369
2883
|
const skillInvoker = getSkillInvoker();
|
|
2370
2884
|
await skillInvoker.initialize();
|
|
2371
2885
|
// Verify skill exists
|
|
@@ -2380,7 +2894,7 @@ export class InvokeSkillTool {
|
|
|
2380
2894
|
taskDescription,
|
|
2381
2895
|
inputFile,
|
|
2382
2896
|
outputFile,
|
|
2383
|
-
options
|
|
2897
|
+
options,
|
|
2384
2898
|
});
|
|
2385
2899
|
if (result.success) {
|
|
2386
2900
|
return {
|
|
@@ -2390,7 +2904,7 @@ export class InvokeSkillTool {
|
|
|
2390
2904
|
task: taskDescription,
|
|
2391
2905
|
result: result.output,
|
|
2392
2906
|
files: result.files,
|
|
2393
|
-
nextSteps: result.nextSteps
|
|
2907
|
+
nextSteps: result.nextSteps,
|
|
2394
2908
|
};
|
|
2395
2909
|
}
|
|
2396
2910
|
else {
|
|
@@ -2404,7 +2918,7 @@ export class InvokeSkillTool {
|
|
|
2404
2918
|
taskDescription,
|
|
2405
2919
|
inputFile,
|
|
2406
2920
|
outputFile,
|
|
2407
|
-
options
|
|
2921
|
+
options,
|
|
2408
2922
|
});
|
|
2409
2923
|
if (result.success) {
|
|
2410
2924
|
return {
|
|
@@ -2414,7 +2928,8 @@ export class InvokeSkillTool {
|
|
|
2414
2928
|
task: taskDescription,
|
|
2415
2929
|
result: result.output,
|
|
2416
2930
|
files: result.files,
|
|
2417
|
-
nextSteps: result.nextSteps
|
|
2931
|
+
nextSteps: result.nextSteps,
|
|
2932
|
+
skillPath: result.skillPath
|
|
2418
2933
|
};
|
|
2419
2934
|
}
|
|
2420
2935
|
else {
|
|
@@ -2495,6 +3010,8 @@ export class ToolRegistry {
|
|
|
2495
3010
|
tools = new Map();
|
|
2496
3011
|
todoWriteTool;
|
|
2497
3012
|
backgroundTasks = new Map();
|
|
3013
|
+
_isSdkMode = false;
|
|
3014
|
+
_sdkOutputAdapter = null;
|
|
2498
3015
|
constructor() {
|
|
2499
3016
|
this.todoWriteTool = new TodoWriteTool();
|
|
2500
3017
|
this.registerDefaultTools();
|
|
@@ -2542,7 +3059,8 @@ export class ToolRegistry {
|
|
|
2542
3059
|
let registeredCount = 0;
|
|
2543
3060
|
for (const [fullName, tool] of mcpTools) {
|
|
2544
3061
|
const firstUnderscoreIndex = fullName.indexOf('__');
|
|
2545
|
-
if (firstUnderscoreIndex === -1 ||
|
|
3062
|
+
if (firstUnderscoreIndex === -1 ||
|
|
3063
|
+
firstUnderscoreIndex === 0 ||
|
|
2546
3064
|
firstUnderscoreIndex === fullName.length - 2)
|
|
2547
3065
|
continue;
|
|
2548
3066
|
const serverName = fullName.substring(0, firstUnderscoreIndex);
|
|
@@ -2577,17 +3095,41 @@ export class ToolRegistry {
|
|
|
2577
3095
|
const { getMCPManager } = await import('./mcp.js');
|
|
2578
3096
|
const mcpManager = getMCPManager();
|
|
2579
3097
|
return await mcpManager.callTool(fullName, params);
|
|
2580
|
-
}
|
|
3098
|
+
},
|
|
2581
3099
|
};
|
|
2582
3100
|
this.tools.set(toolName, mcpTool);
|
|
2583
3101
|
registeredCount++;
|
|
2584
3102
|
if (toolName !== originalName) {
|
|
2585
|
-
|
|
3103
|
+
// 在 SDK 模式下不输出重命名信息
|
|
3104
|
+
if (!this._isSdkMode) {
|
|
3105
|
+
console.log(`[MCP] Tool '${originalName}' renamed to '${toolName}' to avoid conflict`);
|
|
3106
|
+
}
|
|
2586
3107
|
}
|
|
2587
3108
|
}
|
|
2588
3109
|
}
|
|
2589
3110
|
if (registeredCount > 0) {
|
|
2590
|
-
|
|
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);
|
|
2591
3133
|
}
|
|
2592
3134
|
}
|
|
2593
3135
|
/**
|
|
@@ -2621,11 +3163,11 @@ export class ToolRegistry {
|
|
|
2621
3163
|
this.backgroundTasks.delete(taskId);
|
|
2622
3164
|
}
|
|
2623
3165
|
getToolDefinitions() {
|
|
2624
|
-
return Array.from(this.tools.values()).map(tool => {
|
|
3166
|
+
return Array.from(this.tools.values()).map((tool) => {
|
|
2625
3167
|
let parameters = {
|
|
2626
3168
|
type: 'object',
|
|
2627
3169
|
properties: {},
|
|
2628
|
-
required: []
|
|
3170
|
+
required: [],
|
|
2629
3171
|
};
|
|
2630
3172
|
// Define specific parameters for each tool
|
|
2631
3173
|
switch (tool.name) {
|
|
@@ -2635,18 +3177,18 @@ export class ToolRegistry {
|
|
|
2635
3177
|
properties: {
|
|
2636
3178
|
filePath: {
|
|
2637
3179
|
type: 'string',
|
|
2638
|
-
description: 'The absolute path to the file to read'
|
|
3180
|
+
description: 'The absolute path to the file to read',
|
|
2639
3181
|
},
|
|
2640
3182
|
offset: {
|
|
2641
3183
|
type: 'number',
|
|
2642
|
-
description: 'Optional: Line number to start reading from (0-based)'
|
|
3184
|
+
description: 'Optional: Line number to start reading from (0-based)',
|
|
2643
3185
|
},
|
|
2644
3186
|
limit: {
|
|
2645
3187
|
type: 'number',
|
|
2646
|
-
description: 'Optional: Maximum number of lines to read'
|
|
2647
|
-
}
|
|
3188
|
+
description: 'Optional: Maximum number of lines to read',
|
|
3189
|
+
},
|
|
2648
3190
|
},
|
|
2649
|
-
required: ['filePath']
|
|
3191
|
+
required: ['filePath'],
|
|
2650
3192
|
};
|
|
2651
3193
|
break;
|
|
2652
3194
|
case 'Write':
|
|
@@ -2655,14 +3197,14 @@ export class ToolRegistry {
|
|
|
2655
3197
|
properties: {
|
|
2656
3198
|
filePath: {
|
|
2657
3199
|
type: 'string',
|
|
2658
|
-
description: 'The absolute path to the file to write'
|
|
3200
|
+
description: 'The absolute path to the file to write',
|
|
2659
3201
|
},
|
|
2660
3202
|
content: {
|
|
2661
3203
|
type: 'string',
|
|
2662
|
-
description: 'The content to write to the file'
|
|
2663
|
-
}
|
|
3204
|
+
description: 'The content to write to the file',
|
|
3205
|
+
},
|
|
2664
3206
|
},
|
|
2665
|
-
required: ['filePath', 'content']
|
|
3207
|
+
required: ['filePath', 'content'],
|
|
2666
3208
|
};
|
|
2667
3209
|
break;
|
|
2668
3210
|
case 'Grep':
|
|
@@ -2671,26 +3213,34 @@ export class ToolRegistry {
|
|
|
2671
3213
|
properties: {
|
|
2672
3214
|
pattern: {
|
|
2673
3215
|
type: 'string',
|
|
2674
|
-
description: 'The regex pattern to search for'
|
|
3216
|
+
description: 'The regex pattern or literal string to search for',
|
|
2675
3217
|
},
|
|
2676
3218
|
path: {
|
|
2677
3219
|
type: 'string',
|
|
2678
|
-
description: 'Optional: The path to search in (default: current directory)'
|
|
3220
|
+
description: 'Optional: The path to search in (default: current directory)',
|
|
2679
3221
|
},
|
|
2680
|
-
|
|
3222
|
+
glob: {
|
|
2681
3223
|
type: 'string',
|
|
2682
|
-
description: 'Optional: Glob pattern to filter files'
|
|
3224
|
+
description: 'Optional: Glob pattern to filter files (e.g., "*.ts", "**/*.js")',
|
|
2683
3225
|
},
|
|
2684
|
-
|
|
3226
|
+
ignoreCase: {
|
|
2685
3227
|
type: 'boolean',
|
|
2686
|
-
description: 'Optional: Case-
|
|
3228
|
+
description: 'Optional: Case-insensitive search (default: false)',
|
|
3229
|
+
},
|
|
3230
|
+
literal: {
|
|
3231
|
+
type: 'boolean',
|
|
3232
|
+
description: 'Optional: Treat pattern as literal string (default: false)',
|
|
2687
3233
|
},
|
|
2688
3234
|
context: {
|
|
2689
3235
|
type: 'number',
|
|
2690
|
-
description: 'Optional: Number of context lines to show'
|
|
2691
|
-
}
|
|
3236
|
+
description: 'Optional: Number of context lines to show before and after',
|
|
3237
|
+
},
|
|
3238
|
+
limit: {
|
|
3239
|
+
type: 'number',
|
|
3240
|
+
description: 'Optional: Maximum number of matches to return',
|
|
3241
|
+
},
|
|
2692
3242
|
},
|
|
2693
|
-
required: ['pattern']
|
|
3243
|
+
required: ['pattern'],
|
|
2694
3244
|
};
|
|
2695
3245
|
break;
|
|
2696
3246
|
case 'Bash':
|
|
@@ -2699,26 +3249,26 @@ export class ToolRegistry {
|
|
|
2699
3249
|
properties: {
|
|
2700
3250
|
command: {
|
|
2701
3251
|
type: 'string',
|
|
2702
|
-
description: 'The shell command to execute'
|
|
3252
|
+
description: 'The shell command to execute',
|
|
2703
3253
|
},
|
|
2704
3254
|
cwd: {
|
|
2705
3255
|
type: 'string',
|
|
2706
|
-
description: 'Optional: Working directory'
|
|
3256
|
+
description: 'Optional: Working directory',
|
|
2707
3257
|
},
|
|
2708
3258
|
description: {
|
|
2709
3259
|
type: 'string',
|
|
2710
|
-
description: 'Optional: Brief description of the command'
|
|
3260
|
+
description: 'Optional: Brief description of the command',
|
|
2711
3261
|
},
|
|
2712
3262
|
timeout: {
|
|
2713
3263
|
type: 'number',
|
|
2714
|
-
description: 'Optional: Timeout in seconds (default: 120)'
|
|
3264
|
+
description: 'Optional: Timeout in seconds (default: 120)',
|
|
2715
3265
|
},
|
|
2716
3266
|
run_in_bg: {
|
|
2717
3267
|
type: 'boolean',
|
|
2718
|
-
description: 'Optional: Run in background (default: false)'
|
|
2719
|
-
}
|
|
3268
|
+
description: 'Optional: Run in background (default: false)',
|
|
3269
|
+
},
|
|
2720
3270
|
},
|
|
2721
|
-
required: ['command']
|
|
3271
|
+
required: ['command'],
|
|
2722
3272
|
};
|
|
2723
3273
|
break;
|
|
2724
3274
|
case 'ListDirectory':
|
|
@@ -2727,14 +3277,14 @@ export class ToolRegistry {
|
|
|
2727
3277
|
properties: {
|
|
2728
3278
|
path: {
|
|
2729
3279
|
type: 'string',
|
|
2730
|
-
description: 'Optional: The directory path to list (default: current directory)'
|
|
3280
|
+
description: 'Optional: The directory path to list (default: current directory)',
|
|
2731
3281
|
},
|
|
2732
3282
|
recursive: {
|
|
2733
3283
|
type: 'boolean',
|
|
2734
|
-
description: 'Optional: List recursively (default: false)'
|
|
2735
|
-
}
|
|
3284
|
+
description: 'Optional: List recursively (default: false)',
|
|
3285
|
+
},
|
|
2736
3286
|
},
|
|
2737
|
-
required: []
|
|
3287
|
+
required: [],
|
|
2738
3288
|
};
|
|
2739
3289
|
break;
|
|
2740
3290
|
case 'SearchFiles':
|
|
@@ -2743,18 +3293,18 @@ export class ToolRegistry {
|
|
|
2743
3293
|
properties: {
|
|
2744
3294
|
pattern: {
|
|
2745
3295
|
type: 'string',
|
|
2746
|
-
description: 'The glob pattern to match files'
|
|
3296
|
+
description: 'The glob pattern to match files',
|
|
2747
3297
|
},
|
|
2748
3298
|
path: {
|
|
2749
3299
|
type: 'string',
|
|
2750
|
-
description: 'Optional: The path to search in (default: current directory)'
|
|
3300
|
+
description: 'Optional: The path to search in (default: current directory)',
|
|
2751
3301
|
},
|
|
2752
3302
|
limit: {
|
|
2753
3303
|
type: 'integer',
|
|
2754
|
-
description: 'Optional: Maximum number of results to return (default: 1000)'
|
|
2755
|
-
}
|
|
3304
|
+
description: 'Optional: Maximum number of results to return (default: 1000)',
|
|
3305
|
+
},
|
|
2756
3306
|
},
|
|
2757
|
-
required: ['pattern']
|
|
3307
|
+
required: ['pattern'],
|
|
2758
3308
|
};
|
|
2759
3309
|
break;
|
|
2760
3310
|
case 'DeleteFile':
|
|
@@ -2763,10 +3313,10 @@ export class ToolRegistry {
|
|
|
2763
3313
|
properties: {
|
|
2764
3314
|
filePath: {
|
|
2765
3315
|
type: 'string',
|
|
2766
|
-
description: 'The path to the file to delete'
|
|
2767
|
-
}
|
|
3316
|
+
description: 'The path to the file to delete',
|
|
3317
|
+
},
|
|
2768
3318
|
},
|
|
2769
|
-
required: ['filePath']
|
|
3319
|
+
required: ['filePath'],
|
|
2770
3320
|
};
|
|
2771
3321
|
break;
|
|
2772
3322
|
case 'CreateDirectory':
|
|
@@ -2775,14 +3325,14 @@ export class ToolRegistry {
|
|
|
2775
3325
|
properties: {
|
|
2776
3326
|
dirPath: {
|
|
2777
3327
|
type: 'string',
|
|
2778
|
-
description: 'The directory path to create'
|
|
3328
|
+
description: 'The directory path to create',
|
|
2779
3329
|
},
|
|
2780
3330
|
recursive: {
|
|
2781
3331
|
type: 'boolean',
|
|
2782
|
-
description: 'Optional: Create parent directories (default: true)'
|
|
2783
|
-
}
|
|
3332
|
+
description: 'Optional: Create parent directories (default: true)',
|
|
3333
|
+
},
|
|
2784
3334
|
},
|
|
2785
|
-
required: ['dirPath']
|
|
3335
|
+
required: ['dirPath'],
|
|
2786
3336
|
};
|
|
2787
3337
|
break;
|
|
2788
3338
|
case 'Edit':
|
|
@@ -2791,22 +3341,22 @@ export class ToolRegistry {
|
|
|
2791
3341
|
properties: {
|
|
2792
3342
|
file_path: {
|
|
2793
3343
|
type: 'string',
|
|
2794
|
-
description: 'The absolute path to the file to edit'
|
|
3344
|
+
description: 'The absolute path to the file to edit',
|
|
2795
3345
|
},
|
|
2796
3346
|
instruction: {
|
|
2797
3347
|
type: 'string',
|
|
2798
|
-
description: 'Description of what needs to be changed'
|
|
3348
|
+
description: 'Description of what needs to be changed',
|
|
2799
3349
|
},
|
|
2800
3350
|
old_string: {
|
|
2801
3351
|
type: 'string',
|
|
2802
|
-
description: 'The exact text to replace (supports fuzzy matching)'
|
|
3352
|
+
description: 'The exact text to replace (supports fuzzy matching)',
|
|
2803
3353
|
},
|
|
2804
3354
|
new_string: {
|
|
2805
3355
|
type: 'string',
|
|
2806
|
-
description: 'The new text to replace with'
|
|
2807
|
-
}
|
|
3356
|
+
description: 'The new text to replace with',
|
|
3357
|
+
},
|
|
2808
3358
|
},
|
|
2809
|
-
required: ['file_path', 'instruction', 'old_string', 'new_string']
|
|
3359
|
+
required: ['file_path', 'instruction', 'old_string', 'new_string'],
|
|
2810
3360
|
};
|
|
2811
3361
|
break;
|
|
2812
3362
|
case 'web_search':
|
|
@@ -2815,10 +3365,10 @@ export class ToolRegistry {
|
|
|
2815
3365
|
properties: {
|
|
2816
3366
|
query: {
|
|
2817
3367
|
type: 'string',
|
|
2818
|
-
description: 'The search query'
|
|
2819
|
-
}
|
|
3368
|
+
description: 'The search query',
|
|
3369
|
+
},
|
|
2820
3370
|
},
|
|
2821
|
-
required: ['query']
|
|
3371
|
+
required: ['query'],
|
|
2822
3372
|
};
|
|
2823
3373
|
break;
|
|
2824
3374
|
case 'todo_write':
|
|
@@ -2833,21 +3383,24 @@ export class ToolRegistry {
|
|
|
2833
3383
|
properties: {
|
|
2834
3384
|
id: { type: 'string' },
|
|
2835
3385
|
task: { type: 'string' },
|
|
2836
|
-
status: {
|
|
2837
|
-
|
|
3386
|
+
status: {
|
|
3387
|
+
type: 'string',
|
|
3388
|
+
enum: ['pending', 'in_progress', 'completed', 'failed'],
|
|
3389
|
+
},
|
|
3390
|
+
priority: { type: 'string', enum: ['high', 'medium', 'low'] },
|
|
2838
3391
|
},
|
|
2839
|
-
required: ['id', 'task', 'status']
|
|
2840
|
-
}
|
|
2841
|
-
}
|
|
3392
|
+
required: ['id', 'task', 'status'],
|
|
3393
|
+
},
|
|
3394
|
+
},
|
|
2842
3395
|
},
|
|
2843
|
-
required: ['todos']
|
|
3396
|
+
required: ['todos'],
|
|
2844
3397
|
};
|
|
2845
3398
|
break;
|
|
2846
3399
|
case 'todo_read':
|
|
2847
3400
|
parameters = {
|
|
2848
3401
|
type: 'object',
|
|
2849
3402
|
properties: {},
|
|
2850
|
-
required: []
|
|
3403
|
+
required: [],
|
|
2851
3404
|
};
|
|
2852
3405
|
break;
|
|
2853
3406
|
case 'task':
|
|
@@ -2856,7 +3409,7 @@ export class ToolRegistry {
|
|
|
2856
3409
|
properties: {
|
|
2857
3410
|
description: {
|
|
2858
3411
|
type: 'string',
|
|
2859
|
-
description: 'Brief description of the task (3-5 words)'
|
|
3412
|
+
description: 'Brief description of the task (3-5 words)',
|
|
2860
3413
|
},
|
|
2861
3414
|
agents: {
|
|
2862
3415
|
type: 'array',
|
|
@@ -2866,50 +3419,66 @@ export class ToolRegistry {
|
|
|
2866
3419
|
properties: {
|
|
2867
3420
|
description: {
|
|
2868
3421
|
type: 'string',
|
|
2869
|
-
description: 'Brief description of the sub-agent task'
|
|
3422
|
+
description: 'Brief description of the sub-agent task',
|
|
2870
3423
|
},
|
|
2871
3424
|
prompt: {
|
|
2872
3425
|
type: 'string',
|
|
2873
|
-
description: 'The task for the sub-agent to perform'
|
|
3426
|
+
description: 'The task for the sub-agent to perform',
|
|
2874
3427
|
},
|
|
2875
3428
|
subagent_type: {
|
|
2876
3429
|
type: 'string',
|
|
2877
|
-
enum: [
|
|
2878
|
-
|
|
3430
|
+
enum: [
|
|
3431
|
+
'general-purpose',
|
|
3432
|
+
'plan-agent',
|
|
3433
|
+
'explore-agent',
|
|
3434
|
+
'frontend-tester',
|
|
3435
|
+
'code-reviewer',
|
|
3436
|
+
'frontend-developer',
|
|
3437
|
+
'backend-developer',
|
|
3438
|
+
],
|
|
3439
|
+
description: 'The type of specialized agent',
|
|
2879
3440
|
},
|
|
2880
3441
|
constraints: {
|
|
2881
3442
|
type: 'array',
|
|
2882
3443
|
items: { type: 'string' },
|
|
2883
|
-
description: 'Optional: Constraints or limitations'
|
|
2884
|
-
}
|
|
3444
|
+
description: 'Optional: Constraints or limitations',
|
|
3445
|
+
},
|
|
2885
3446
|
},
|
|
2886
|
-
required: ['description', 'prompt', 'subagent_type']
|
|
2887
|
-
}
|
|
3447
|
+
required: ['description', 'prompt', 'subagent_type'],
|
|
3448
|
+
},
|
|
2888
3449
|
},
|
|
2889
3450
|
prompt: {
|
|
2890
3451
|
type: 'string',
|
|
2891
|
-
description: 'Optional: The task for the agent to perform (use agents for parallel execution)'
|
|
3452
|
+
description: 'Optional: The task for the agent to perform (use agents for parallel execution)',
|
|
2892
3453
|
},
|
|
2893
3454
|
subagent_type: {
|
|
2894
3455
|
type: 'string',
|
|
2895
|
-
enum: [
|
|
2896
|
-
|
|
3456
|
+
enum: [
|
|
3457
|
+
'general-purpose',
|
|
3458
|
+
'plan-agent',
|
|
3459
|
+
'explore-agent',
|
|
3460
|
+
'frontend-tester',
|
|
3461
|
+
'code-reviewer',
|
|
3462
|
+
'frontend-developer',
|
|
3463
|
+
'backend-developer',
|
|
3464
|
+
],
|
|
3465
|
+
description: 'Optional: The type of specialized agent (use agents for parallel execution)',
|
|
2897
3466
|
},
|
|
2898
3467
|
useContext: {
|
|
2899
3468
|
type: 'boolean',
|
|
2900
|
-
description: 'Optional: Include main agent context'
|
|
3469
|
+
description: 'Optional: Include main agent context',
|
|
2901
3470
|
},
|
|
2902
3471
|
outputFormat: {
|
|
2903
3472
|
type: 'string',
|
|
2904
|
-
description: 'Optional: Output format template'
|
|
3473
|
+
description: 'Optional: Output format template',
|
|
2905
3474
|
},
|
|
2906
3475
|
constraints: {
|
|
2907
3476
|
type: 'array',
|
|
2908
3477
|
items: { type: 'string' },
|
|
2909
|
-
description: 'Optional: Constraints or limitations'
|
|
2910
|
-
}
|
|
3478
|
+
description: 'Optional: Constraints or limitations',
|
|
3479
|
+
},
|
|
2911
3480
|
},
|
|
2912
|
-
required: ['description']
|
|
3481
|
+
required: ['description'],
|
|
2913
3482
|
};
|
|
2914
3483
|
break;
|
|
2915
3484
|
case 'ReadBashOutput':
|
|
@@ -2918,14 +3487,14 @@ export class ToolRegistry {
|
|
|
2918
3487
|
properties: {
|
|
2919
3488
|
task_id: {
|
|
2920
3489
|
type: 'string',
|
|
2921
|
-
description: 'The ID of the task'
|
|
3490
|
+
description: 'The ID of the task',
|
|
2922
3491
|
},
|
|
2923
3492
|
poll_interval: {
|
|
2924
3493
|
type: 'number',
|
|
2925
|
-
description: 'Optional: Polling interval in seconds (default: 10)'
|
|
2926
|
-
}
|
|
3494
|
+
description: 'Optional: Polling interval in seconds (default: 10)',
|
|
3495
|
+
},
|
|
2927
3496
|
},
|
|
2928
|
-
required: ['task_id']
|
|
3497
|
+
required: ['task_id'],
|
|
2929
3498
|
};
|
|
2930
3499
|
break;
|
|
2931
3500
|
case 'web_fetch':
|
|
@@ -2934,10 +3503,10 @@ export class ToolRegistry {
|
|
|
2934
3503
|
properties: {
|
|
2935
3504
|
prompt: {
|
|
2936
3505
|
type: 'string',
|
|
2937
|
-
description: 'Prompt containing URL(s) and processing instructions'
|
|
2938
|
-
}
|
|
3506
|
+
description: 'Prompt containing URL(s) and processing instructions',
|
|
3507
|
+
},
|
|
2939
3508
|
},
|
|
2940
|
-
required: ['prompt']
|
|
3509
|
+
required: ['prompt'],
|
|
2941
3510
|
};
|
|
2942
3511
|
break;
|
|
2943
3512
|
case 'ask_user_question':
|
|
@@ -2955,15 +3524,15 @@ export class ToolRegistry {
|
|
|
2955
3524
|
options: {
|
|
2956
3525
|
type: 'array',
|
|
2957
3526
|
items: { type: 'string' },
|
|
2958
|
-
description: 'Available choices (2-4 options)'
|
|
3527
|
+
description: 'Available choices (2-4 options)',
|
|
2959
3528
|
},
|
|
2960
|
-
multiSelect: { type: 'boolean' }
|
|
3529
|
+
multiSelect: { type: 'boolean' },
|
|
2961
3530
|
},
|
|
2962
|
-
required: ['question', 'header', 'options', 'multiSelect']
|
|
2963
|
-
}
|
|
2964
|
-
}
|
|
3531
|
+
required: ['question', 'header', 'options', 'multiSelect'],
|
|
3532
|
+
},
|
|
3533
|
+
},
|
|
2965
3534
|
},
|
|
2966
|
-
required: ['questions']
|
|
3535
|
+
required: ['questions'],
|
|
2967
3536
|
};
|
|
2968
3537
|
break;
|
|
2969
3538
|
case 'save_memory':
|
|
@@ -2972,10 +3541,10 @@ export class ToolRegistry {
|
|
|
2972
3541
|
properties: {
|
|
2973
3542
|
fact: {
|
|
2974
3543
|
type: 'string',
|
|
2975
|
-
description: 'The specific fact to remember'
|
|
2976
|
-
}
|
|
3544
|
+
description: 'The specific fact to remember',
|
|
3545
|
+
},
|
|
2977
3546
|
},
|
|
2978
|
-
required: ['fact']
|
|
3547
|
+
required: ['fact'],
|
|
2979
3548
|
};
|
|
2980
3549
|
break;
|
|
2981
3550
|
case 'exit_plan_mode':
|
|
@@ -2984,10 +3553,10 @@ export class ToolRegistry {
|
|
|
2984
3553
|
properties: {
|
|
2985
3554
|
plan: {
|
|
2986
3555
|
type: 'string',
|
|
2987
|
-
description: 'The plan to present'
|
|
2988
|
-
}
|
|
3556
|
+
description: 'The plan to present',
|
|
3557
|
+
},
|
|
2989
3558
|
},
|
|
2990
|
-
required: ['plan']
|
|
3559
|
+
required: ['plan'],
|
|
2991
3560
|
};
|
|
2992
3561
|
break;
|
|
2993
3562
|
case 'xml_escape':
|
|
@@ -2996,14 +3565,14 @@ export class ToolRegistry {
|
|
|
2996
3565
|
properties: {
|
|
2997
3566
|
file_path: {
|
|
2998
3567
|
type: 'string',
|
|
2999
|
-
description: 'The absolute path to the XML/HTML file'
|
|
3568
|
+
description: 'The absolute path to the XML/HTML file',
|
|
3000
3569
|
},
|
|
3001
3570
|
escape_all: {
|
|
3002
3571
|
type: 'boolean',
|
|
3003
|
-
description: 'Optional: Escape all special characters (default: false)'
|
|
3004
|
-
}
|
|
3572
|
+
description: 'Optional: Escape all special characters (default: false)',
|
|
3573
|
+
},
|
|
3005
3574
|
},
|
|
3006
|
-
required: ['file_path']
|
|
3575
|
+
required: ['file_path'],
|
|
3007
3576
|
};
|
|
3008
3577
|
break;
|
|
3009
3578
|
case 'image_read':
|
|
@@ -3012,27 +3581,27 @@ export class ToolRegistry {
|
|
|
3012
3581
|
properties: {
|
|
3013
3582
|
image_input: {
|
|
3014
3583
|
type: 'string',
|
|
3015
|
-
description: 'Image file path or base64 data'
|
|
3584
|
+
description: 'Image file path or base64 data',
|
|
3016
3585
|
},
|
|
3017
3586
|
prompt: {
|
|
3018
3587
|
type: 'string',
|
|
3019
|
-
description: 'Comprehensive VLM instruction'
|
|
3588
|
+
description: 'Comprehensive VLM instruction',
|
|
3020
3589
|
},
|
|
3021
3590
|
task_brief: {
|
|
3022
3591
|
type: 'string',
|
|
3023
|
-
description: 'Brief task description (max 15 words)'
|
|
3592
|
+
description: 'Brief task description (max 15 words)',
|
|
3024
3593
|
},
|
|
3025
3594
|
input_type: {
|
|
3026
3595
|
type: 'string',
|
|
3027
3596
|
enum: ['file_path', 'base64'],
|
|
3028
|
-
description: 'Input type (default: file_path)'
|
|
3597
|
+
description: 'Input type (default: file_path)',
|
|
3029
3598
|
},
|
|
3030
3599
|
mime_type: {
|
|
3031
3600
|
type: 'string',
|
|
3032
|
-
description: 'Optional: MIME type for base64 input'
|
|
3033
|
-
}
|
|
3601
|
+
description: 'Optional: MIME type for base64 input',
|
|
3602
|
+
},
|
|
3034
3603
|
},
|
|
3035
|
-
required: ['image_input', 'prompt']
|
|
3604
|
+
required: ['image_input', 'prompt'],
|
|
3036
3605
|
};
|
|
3037
3606
|
break;
|
|
3038
3607
|
case 'Skill':
|
|
@@ -3041,17 +3610,17 @@ export class ToolRegistry {
|
|
|
3041
3610
|
properties: {
|
|
3042
3611
|
skill: {
|
|
3043
3612
|
type: 'string',
|
|
3044
|
-
description: 'The skill name to execute'
|
|
3045
|
-
}
|
|
3613
|
+
description: 'The skill name to execute',
|
|
3614
|
+
},
|
|
3046
3615
|
},
|
|
3047
|
-
required: ['skill']
|
|
3616
|
+
required: ['skill'],
|
|
3048
3617
|
};
|
|
3049
3618
|
break;
|
|
3050
3619
|
case 'ListSkills':
|
|
3051
3620
|
parameters = {
|
|
3052
3621
|
type: 'object',
|
|
3053
3622
|
properties: {},
|
|
3054
|
-
required: []
|
|
3623
|
+
required: [],
|
|
3055
3624
|
};
|
|
3056
3625
|
break;
|
|
3057
3626
|
case 'GetSkillDetails':
|
|
@@ -3060,13 +3629,13 @@ export class ToolRegistry {
|
|
|
3060
3629
|
properties: {
|
|
3061
3630
|
skill: {
|
|
3062
3631
|
type: 'string',
|
|
3063
|
-
description: 'The skill name/id to get details for'
|
|
3064
|
-
}
|
|
3632
|
+
description: 'The skill name/id to get details for',
|
|
3633
|
+
},
|
|
3065
3634
|
},
|
|
3066
|
-
required: ['skill']
|
|
3635
|
+
required: ['skill'],
|
|
3067
3636
|
};
|
|
3068
3637
|
break;
|
|
3069
|
-
default:
|
|
3638
|
+
default: {
|
|
3070
3639
|
// For MCP tools, use their inputSchema; for other unknown tools, keep empty schema
|
|
3071
3640
|
const mcpTool = tool;
|
|
3072
3641
|
if (mcpTool._isMcpTool && mcpTool.inputSchema) {
|
|
@@ -3074,13 +3643,13 @@ export class ToolRegistry {
|
|
|
3074
3643
|
parameters = {
|
|
3075
3644
|
type: 'object',
|
|
3076
3645
|
properties: {},
|
|
3077
|
-
required: []
|
|
3646
|
+
required: [],
|
|
3078
3647
|
};
|
|
3079
3648
|
if (mcpTool.inputSchema.properties) {
|
|
3080
3649
|
for (const [paramName, paramDef] of Object.entries(mcpTool.inputSchema.properties)) {
|
|
3081
3650
|
parameters.properties[paramName] = {
|
|
3082
3651
|
type: paramDef.type || 'string',
|
|
3083
|
-
description: paramDef.description || ''
|
|
3652
|
+
description: paramDef.description || '',
|
|
3084
3653
|
};
|
|
3085
3654
|
}
|
|
3086
3655
|
}
|
|
@@ -3092,17 +3661,18 @@ export class ToolRegistry {
|
|
|
3092
3661
|
parameters = {
|
|
3093
3662
|
type: 'object',
|
|
3094
3663
|
properties: {},
|
|
3095
|
-
required: []
|
|
3664
|
+
required: [],
|
|
3096
3665
|
};
|
|
3097
3666
|
}
|
|
3667
|
+
}
|
|
3098
3668
|
}
|
|
3099
3669
|
return {
|
|
3100
3670
|
type: 'function',
|
|
3101
3671
|
function: {
|
|
3102
3672
|
name: tool.name,
|
|
3103
3673
|
description: tool.description,
|
|
3104
|
-
parameters
|
|
3105
|
-
}
|
|
3674
|
+
parameters,
|
|
3675
|
+
},
|
|
3106
3676
|
};
|
|
3107
3677
|
});
|
|
3108
3678
|
}
|
|
@@ -3121,14 +3691,14 @@ export class ToolRegistry {
|
|
|
3121
3691
|
return await this.executeMCPTool(toolName, params, executionMode, indent);
|
|
3122
3692
|
}
|
|
3123
3693
|
// Try to find MCP tool with just the tool name (try each server)
|
|
3124
|
-
for (const [fullName,
|
|
3694
|
+
for (const [fullName, _tool] of allMcpTools) {
|
|
3125
3695
|
// Split only on the first __ to preserve underscores in tool names
|
|
3126
3696
|
const firstUnderscoreIndex = fullName.indexOf('__');
|
|
3127
3697
|
if (firstUnderscoreIndex === -1)
|
|
3128
3698
|
continue;
|
|
3129
|
-
const [
|
|
3699
|
+
const [_serverName, actualToolName] = [
|
|
3130
3700
|
fullName.substring(0, firstUnderscoreIndex),
|
|
3131
|
-
fullName.substring(firstUnderscoreIndex + 2)
|
|
3701
|
+
fullName.substring(firstUnderscoreIndex + 2),
|
|
3132
3702
|
];
|
|
3133
3703
|
if (actualToolName === toolName) {
|
|
3134
3704
|
return await this.executeMCPTool(fullName, params, executionMode, indent);
|
|
@@ -3149,6 +3719,8 @@ export class ToolRegistry {
|
|
|
3149
3719
|
if (!tool.allowedModes.includes(executionMode)) {
|
|
3150
3720
|
throw new Error(`Tool ${toolName} is not allowed in ${executionMode} mode`);
|
|
3151
3721
|
}
|
|
3722
|
+
const isSdkMode = this._isSdkMode;
|
|
3723
|
+
const sdkOutputAdapter = this._sdkOutputAdapter;
|
|
3152
3724
|
// Smart approval mode
|
|
3153
3725
|
if (executionMode === ExecutionMode.SMART) {
|
|
3154
3726
|
const debugMode = process.env.DEBUG === 'smart-approval';
|
|
@@ -3169,50 +3741,81 @@ export class ToolRegistry {
|
|
|
3169
3741
|
const isRemoteMode = authConfig.type === AuthType.OAUTH_XAGENT;
|
|
3170
3742
|
if (isRemoteMode && toolName === 'InvokeSkill') {
|
|
3171
3743
|
console.log('');
|
|
3172
|
-
|
|
3173
|
-
|
|
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
|
+
}
|
|
3174
3752
|
return await cancellationManager.withCancellation(tool.execute(params, executionMode), `tool-${toolName}`);
|
|
3175
3753
|
}
|
|
3176
3754
|
const { getSmartApprovalEngine } = await import('./smart-approval.js');
|
|
3177
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
|
+
}
|
|
3178
3760
|
// Evaluate tool call
|
|
3179
3761
|
const result = await approvalEngine.evaluate({
|
|
3180
3762
|
toolName,
|
|
3181
3763
|
params,
|
|
3182
|
-
timestamp: Date.now()
|
|
3764
|
+
timestamp: Date.now(),
|
|
3183
3765
|
});
|
|
3184
3766
|
// Decide whether to execute based on approval result
|
|
3185
3767
|
if (result.decision === 'approved') {
|
|
3186
3768
|
// Whitelist or AI approval passed, execute directly
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
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
|
+
}
|
|
3192
3780
|
return await cancellationManager.withCancellation(tool.execute(params, executionMode), `tool-${toolName}`);
|
|
3193
3781
|
}
|
|
3194
3782
|
else if (result.decision === 'requires_confirmation') {
|
|
3195
3783
|
// Requires user confirmation
|
|
3196
|
-
const confirmed = await approvalEngine.requestConfirmation(result);
|
|
3784
|
+
const confirmed = await approvalEngine.requestConfirmation(result, toolName, params);
|
|
3197
3785
|
if (confirmed) {
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
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
|
+
}
|
|
3201
3794
|
return await cancellationManager.withCancellation(tool.execute(params, executionMode), `tool-${toolName}`);
|
|
3202
3795
|
}
|
|
3203
3796
|
else {
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
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
|
+
}
|
|
3207
3805
|
throw new Error(`Tool execution cancelled by user: ${toolName}`);
|
|
3208
3806
|
}
|
|
3209
3807
|
}
|
|
3210
3808
|
else {
|
|
3211
3809
|
// Rejected execution
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
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
|
+
}
|
|
3216
3819
|
throw new Error(`Tool execution rejected: ${toolName}`);
|
|
3217
3820
|
}
|
|
3218
3821
|
}
|
|
@@ -3232,10 +3835,14 @@ export class ToolRegistry {
|
|
|
3232
3835
|
const actualToolName = toolName.substring(firstUnderscoreIndex + 2);
|
|
3233
3836
|
// Get server info for display
|
|
3234
3837
|
const server = mcpManager.getServer(serverName);
|
|
3235
|
-
const
|
|
3838
|
+
const _serverTools = server?.getToolNames() || [];
|
|
3839
|
+
const isSdkMode = this._isSdkMode;
|
|
3840
|
+
const sdkOutputAdapter = this._sdkOutputAdapter;
|
|
3236
3841
|
// Display tool call info
|
|
3237
|
-
|
|
3238
|
-
|
|
3842
|
+
if (!isSdkMode || !sdkOutputAdapter) {
|
|
3843
|
+
console.log('');
|
|
3844
|
+
console.log(`${indent}${colors.warning(`${icons.tool} MCP Tool Call: ${serverName}::${actualToolName}`)}`);
|
|
3845
|
+
}
|
|
3239
3846
|
// Smart approval mode for MCP tools
|
|
3240
3847
|
if (executionMode === ExecutionMode.SMART) {
|
|
3241
3848
|
const debugMode = process.env.DEBUG === 'smart-approval';
|
|
@@ -3246,30 +3853,55 @@ export class ToolRegistry {
|
|
|
3246
3853
|
const isRemoteMode = authConfig.type === AuthType.OAUTH_XAGENT;
|
|
3247
3854
|
// Remote mode: remote LLM has already approved the tool, auto-approve
|
|
3248
3855
|
if (isRemoteMode) {
|
|
3249
|
-
|
|
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
|
+
}
|
|
3250
3862
|
}
|
|
3251
3863
|
else {
|
|
3252
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
|
+
}
|
|
3253
3869
|
// Evaluate MCP tool call
|
|
3254
3870
|
const result = await approvalEngine.evaluate({
|
|
3255
3871
|
toolName: `MCP[${serverName}]::${actualToolName}`,
|
|
3256
3872
|
params,
|
|
3257
|
-
timestamp: Date.now()
|
|
3873
|
+
timestamp: Date.now(),
|
|
3258
3874
|
});
|
|
3259
3875
|
if (result.decision === 'approved') {
|
|
3260
|
-
|
|
3261
|
-
|
|
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
|
+
}
|
|
3262
3884
|
}
|
|
3263
3885
|
else if (result.decision === 'requires_confirmation') {
|
|
3264
|
-
const confirmed = await approvalEngine.requestConfirmation(result);
|
|
3886
|
+
const confirmed = await approvalEngine.requestConfirmation(result, `MCP[${serverName}]::${actualToolName}`, params);
|
|
3265
3887
|
if (!confirmed) {
|
|
3266
|
-
|
|
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
|
+
}
|
|
3267
3894
|
throw new Error(`Tool execution cancelled by user: ${toolName}`);
|
|
3268
3895
|
}
|
|
3269
3896
|
}
|
|
3270
3897
|
else {
|
|
3271
|
-
|
|
3272
|
-
|
|
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
|
+
}
|
|
3273
3905
|
throw new Error(`Tool execution rejected: ${toolName}`);
|
|
3274
3906
|
}
|
|
3275
3907
|
}
|