@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/src/tools.ts
CHANGED
|
@@ -1,21 +1,22 @@
|
|
|
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 { Tool, ExecutionMode, AuthType } from './types.js';
|
|
11
|
-
import type { Message, ToolDefinition } from './ai-client.js';
|
|
12
|
-
import { colors, icons
|
|
10
|
+
import type { Message, ToolDefinition } from './ai-client/types.js';
|
|
11
|
+
import { colors, icons } from './theme.js';
|
|
13
12
|
import { getLogger } from './logger.js';
|
|
14
13
|
import { getCancellationManager } from './cancellation.js';
|
|
15
14
|
import { SystemPromptGenerator } from './system-prompt-generator.js';
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
import { getSingletonSession } from './session.js';
|
|
16
|
+
import { ripgrep, fdFind } from './ripgrep.js';
|
|
17
|
+
import { getShellConfig, killProcessTree, quoteShellCommand } from './shell.js';
|
|
18
|
+
import { truncateTail, buildTruncationNotice } from './truncate.js';
|
|
19
|
+
import { createAIClient } from './ai-client-factory.js';
|
|
19
20
|
|
|
20
21
|
//
|
|
21
22
|
// Tool Description Pattern
|
|
@@ -70,9 +71,17 @@ export class ReadTool implements Tool {
|
|
|
70
71
|
- Combine with ListDirectory to explore project structure first
|
|
71
72
|
- Don't re-read files unnecessarily`;
|
|
72
73
|
|
|
73
|
-
allowedModes = [
|
|
74
|
+
allowedModes = [
|
|
75
|
+
ExecutionMode.YOLO,
|
|
76
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
77
|
+
ExecutionMode.PLAN,
|
|
78
|
+
ExecutionMode.SMART,
|
|
79
|
+
];
|
|
74
80
|
|
|
75
81
|
async execute(params: { filePath: string; offset?: number; limit?: number }): Promise<string> {
|
|
82
|
+
if (!params || typeof params.filePath !== 'string') {
|
|
83
|
+
throw new Error('filePath is required and must be a string');
|
|
84
|
+
}
|
|
76
85
|
const { filePath, offset = 0, limit } = params;
|
|
77
86
|
|
|
78
87
|
try {
|
|
@@ -89,13 +98,24 @@ export class ReadTool implements Tool {
|
|
|
89
98
|
}
|
|
90
99
|
const absolutePath = path.resolve(resolvedPath);
|
|
91
100
|
const content = await fs.readFile(absolutePath, 'utf-8');
|
|
92
|
-
|
|
101
|
+
|
|
93
102
|
const lines = content.split('\n');
|
|
103
|
+
const totalLines = lines.length;
|
|
94
104
|
const startLine = Math.max(0, offset);
|
|
95
|
-
const endLine = limit !== undefined ? Math.min(
|
|
105
|
+
const endLine = limit !== undefined ? Math.min(totalLines, startLine + limit) : totalLines;
|
|
96
106
|
const selectedLines = lines.slice(startLine, endLine);
|
|
97
|
-
|
|
98
|
-
|
|
107
|
+
const result = selectedLines.join('\n');
|
|
108
|
+
|
|
109
|
+
// Add truncation notice if content is limited
|
|
110
|
+
if (limit !== undefined && endLine < totalLines) {
|
|
111
|
+
const remaining = totalLines - endLine;
|
|
112
|
+
const nextOffset = endLine;
|
|
113
|
+
return (
|
|
114
|
+
result + `\n\n[${remaining} more lines in file. Use offset=${nextOffset} to continue]`
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return result;
|
|
99
119
|
} catch (error: any) {
|
|
100
120
|
// Show user-friendly path in error message
|
|
101
121
|
let displayPath = filePath;
|
|
@@ -142,7 +162,16 @@ export class WriteTool implements Tool {
|
|
|
142
162
|
- For partial edits, use Edit tool instead`;
|
|
143
163
|
allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
|
|
144
164
|
|
|
145
|
-
async execute(params: {
|
|
165
|
+
async execute(params: {
|
|
166
|
+
filePath: string;
|
|
167
|
+
content: string;
|
|
168
|
+
}): Promise<{
|
|
169
|
+
success: boolean;
|
|
170
|
+
message: string;
|
|
171
|
+
filePath: string;
|
|
172
|
+
lineCount: number;
|
|
173
|
+
preview?: string;
|
|
174
|
+
}> {
|
|
146
175
|
const { filePath, content } = params;
|
|
147
176
|
|
|
148
177
|
try {
|
|
@@ -161,7 +190,7 @@ export class WriteTool implements Tool {
|
|
|
161
190
|
message: `Successfully wrote to ${filePath}`,
|
|
162
191
|
filePath,
|
|
163
192
|
lineCount,
|
|
164
|
-
preview: isTruncated ? preview + '\n...' : preview
|
|
193
|
+
preview: isTruncated ? preview + '\n...' : preview,
|
|
165
194
|
};
|
|
166
195
|
} catch (error: any) {
|
|
167
196
|
throw new Error(`Failed to write file ${filePath}: ${error.message}`);
|
|
@@ -171,7 +200,7 @@ export class WriteTool implements Tool {
|
|
|
171
200
|
|
|
172
201
|
export class GrepTool implements Tool {
|
|
173
202
|
name = 'Grep';
|
|
174
|
-
description = `Search for text patterns within files using
|
|
203
|
+
description = `Search for text patterns within files using ripgrep. This is your PRIMARY tool for finding specific code, functions, or content.
|
|
175
204
|
|
|
176
205
|
# When to Use
|
|
177
206
|
- Finding specific function definitions or calls
|
|
@@ -188,122 +217,60 @@ export class GrepTool implements Tool {
|
|
|
188
217
|
# Parameters
|
|
189
218
|
- \`pattern\`: Regex or literal string to search for
|
|
190
219
|
- \`path\`: (Optional) Directory to search in, default: "."
|
|
191
|
-
- \`
|
|
192
|
-
- \`
|
|
193
|
-
- \`
|
|
194
|
-
- \`fixed_strings\`: (Optional) Treat pattern as literal string, default: false
|
|
220
|
+
- \`glob\`: (Optional) File glob pattern to include (e.g., "*.ts", "**/*.js")
|
|
221
|
+
- \`ignoreCase\`: (Optional) Case-insensitive search, default: false
|
|
222
|
+
- \`literal\`: (Optional) Treat pattern as literal string, default: false
|
|
195
223
|
- \`context\`: (Optional) Lines of context before/after matches
|
|
196
|
-
- \`no_ignore\`: (Optional) Don't ignore node_modules/.git, default: false
|
|
197
224
|
|
|
198
225
|
# Examples
|
|
199
226
|
- Find function: Grep(pattern="function myFunction")
|
|
200
227
|
- Find with context: Grep(pattern="TODO", context=3)
|
|
201
|
-
- TypeScript only: Grep(pattern="interface",
|
|
228
|
+
- TypeScript only: Grep(pattern="interface", glob="*.ts")
|
|
229
|
+
- Case-insensitive: Grep(pattern="error", ignoreCase=true)
|
|
202
230
|
|
|
203
231
|
# Best Practices
|
|
204
|
-
- Use
|
|
205
|
-
- Use
|
|
232
|
+
- Use ignoreCase=true for short patterns to reduce false positives
|
|
233
|
+
- Use literal=true if your pattern has special regex characters
|
|
206
234
|
- Use context to see the surrounding code for each match
|
|
207
|
-
- Combine with
|
|
208
|
-
allowedModes = [
|
|
235
|
+
- Combine with glob to narrow down file types`;
|
|
236
|
+
allowedModes = [
|
|
237
|
+
ExecutionMode.YOLO,
|
|
238
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
239
|
+
ExecutionMode.PLAN,
|
|
240
|
+
ExecutionMode.SMART,
|
|
241
|
+
];
|
|
209
242
|
|
|
210
243
|
async execute(params: {
|
|
211
244
|
pattern: string;
|
|
212
245
|
path?: string;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
fixed_strings?: boolean;
|
|
246
|
+
glob?: string;
|
|
247
|
+
ignoreCase?: boolean;
|
|
248
|
+
literal?: boolean;
|
|
217
249
|
context?: number;
|
|
218
|
-
|
|
219
|
-
before?: number;
|
|
220
|
-
no_ignore?: boolean;
|
|
250
|
+
limit?: number;
|
|
221
251
|
}): Promise<string[]> {
|
|
222
252
|
const {
|
|
223
253
|
pattern,
|
|
224
254
|
path: searchPath = '.',
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
fixed_strings = false,
|
|
255
|
+
glob: includeGlob,
|
|
256
|
+
ignoreCase = false,
|
|
257
|
+
literal = false,
|
|
229
258
|
context,
|
|
230
|
-
|
|
231
|
-
before,
|
|
232
|
-
no_ignore = false
|
|
259
|
+
limit,
|
|
233
260
|
} = params;
|
|
234
|
-
|
|
261
|
+
|
|
235
262
|
try {
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
nodir: true,
|
|
245
|
-
ignore: ignorePatterns
|
|
263
|
+
const result = await ripgrep({
|
|
264
|
+
pattern,
|
|
265
|
+
path: searchPath,
|
|
266
|
+
glob: includeGlob,
|
|
267
|
+
ignoreCase,
|
|
268
|
+
literal,
|
|
269
|
+
context,
|
|
270
|
+
limit,
|
|
246
271
|
});
|
|
247
272
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
for (const file of files) {
|
|
251
|
-
const fullPath = path.join(absolutePath, file);
|
|
252
|
-
if (include && !file.match(include)) {
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
const content = await fs.readFile(fullPath, 'utf-8');
|
|
258
|
-
const lines = content.split('\n');
|
|
259
|
-
|
|
260
|
-
lines.forEach((line, index) => {
|
|
261
|
-
let matches = false;
|
|
262
|
-
|
|
263
|
-
if (fixed_strings) {
|
|
264
|
-
matches = case_sensitive
|
|
265
|
-
? line.includes(pattern)
|
|
266
|
-
: line.toLowerCase().includes(pattern.toLowerCase());
|
|
267
|
-
} else {
|
|
268
|
-
try {
|
|
269
|
-
const flags = case_sensitive ? 'g' : 'gi';
|
|
270
|
-
const regex = new RegExp(pattern, flags);
|
|
271
|
-
matches = regex.test(line);
|
|
272
|
-
} catch (e) {
|
|
273
|
-
matches = case_sensitive
|
|
274
|
-
? line.includes(pattern)
|
|
275
|
-
: line.toLowerCase().includes(pattern.toLowerCase());
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
if (matches) {
|
|
280
|
-
const contextLines: string[] = [];
|
|
281
|
-
|
|
282
|
-
if (before || context) {
|
|
283
|
-
const beforeCount = before || context || 0;
|
|
284
|
-
for (let i = Math.max(0, index - beforeCount); i < index; i++) {
|
|
285
|
-
contextLines.push(`${fullPath}:${i + 1}:${lines[i].trim()}`);
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
contextLines.push(`${fullPath}:${index + 1}:${line.trim()}`);
|
|
290
|
-
|
|
291
|
-
if (after || context) {
|
|
292
|
-
const afterCount = after || context || 0;
|
|
293
|
-
for (let i = index + 1; i < Math.min(lines.length, index + 1 + afterCount); i++) {
|
|
294
|
-
contextLines.push(`${fullPath}:${i + 1}:${lines[i].trim()}`);
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
results.push(...contextLines);
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
} catch (error) {
|
|
302
|
-
continue;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return results;
|
|
273
|
+
return result.split('\n').filter((line) => line.trim());
|
|
307
274
|
} catch (error: any) {
|
|
308
275
|
throw new Error(`Grep failed: ${error.message}`);
|
|
309
276
|
}
|
|
@@ -335,13 +302,23 @@ export class BashTool implements Tool {
|
|
|
335
302
|
- \`description\`: (Optional) Description of what the command does
|
|
336
303
|
- \`timeout\`: (Optional) Timeout in seconds, default: 120
|
|
337
304
|
- \`run_in_bg\`: (Optional) Run in background, default: false
|
|
305
|
+
- \`skillPath\`: (Optional) Skill directory path - when provided, NODE_PATH will include the skill's node_modules for dependency resolution
|
|
338
306
|
|
|
339
307
|
# Examples
|
|
340
308
|
- Install dependencies: Bash(command="npm install", description="Install npm dependencies")
|
|
309
|
+
- Run in skill directory with local deps: Bash(command="npm install docx", skillPath="~/.xagent/skills/docx")
|
|
310
|
+
|
|
311
|
+
# NODE_PATH Resolution
|
|
312
|
+
When \`skillPath\` is provided, the command will have access to:
|
|
313
|
+
- \`<skillPath>/node_modules\` (skill's local dependencies)
|
|
314
|
+
- xAgent's global node_modules
|
|
315
|
+
|
|
316
|
+
This is useful when working with skills that have local dependencies.
|
|
341
317
|
- Run tests: Bash(command="npm test", description="Run unit tests")
|
|
342
318
|
- Build project: Bash(command="npm run build", description="Build the project")
|
|
343
319
|
|
|
344
320
|
# Best Practices
|
|
321
|
+
- To install npm packages that persist across sessions, use: \`XAGENT_USER_NPM=1 npm install <package>\`
|
|
345
322
|
- Always provide a description for context
|
|
346
323
|
- Set appropriate timeout for long-running commands
|
|
347
324
|
- Use run_in_bg=true for commands that take a long time
|
|
@@ -355,14 +332,25 @@ export class BashTool implements Tool {
|
|
|
355
332
|
description?: string;
|
|
356
333
|
timeout?: number;
|
|
357
334
|
run_in_bg?: boolean;
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
335
|
+
skillPath?: string; // Skill directory path for NODE_PATH resolution
|
|
336
|
+
}): Promise<{
|
|
337
|
+
stdout: string;
|
|
338
|
+
stderr: string;
|
|
339
|
+
exitCode: number;
|
|
340
|
+
taskId?: string;
|
|
341
|
+
truncated?: boolean;
|
|
342
|
+
truncationNotice?: string;
|
|
343
|
+
skillPath?: string;
|
|
344
|
+
}> {
|
|
345
|
+
const { command, cwd, description, timeout = 120, run_in_bg = false, skillPath } = params;
|
|
346
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
347
|
+
void description;
|
|
348
|
+
|
|
361
349
|
// Determine effective working directory
|
|
362
350
|
// Only use cwd if the command doesn't contain 'cd' (let LLM control directory)
|
|
363
351
|
let effectiveCwd: string | undefined;
|
|
364
352
|
const hasCdCommand = /cd\s+["']?[^"&|;]+["']?/.test(command);
|
|
365
|
-
|
|
353
|
+
|
|
366
354
|
if (cwd && !hasCdCommand) {
|
|
367
355
|
// Command doesn't control its own directory, use provided cwd
|
|
368
356
|
effectiveCwd = cwd;
|
|
@@ -373,82 +361,270 @@ export class BashTool implements Tool {
|
|
|
373
361
|
// No cwd provided, use default
|
|
374
362
|
effectiveCwd = undefined;
|
|
375
363
|
}
|
|
376
|
-
|
|
377
|
-
//
|
|
378
|
-
const
|
|
379
|
-
|
|
364
|
+
|
|
365
|
+
// Resolve actual working directory
|
|
366
|
+
const actualCwd = effectiveCwd || process.cwd();
|
|
367
|
+
|
|
368
|
+
// Set up environment with NODE_PATH for node commands
|
|
369
|
+
const builtinNodeModulesPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'node_modules');
|
|
370
|
+
|
|
371
|
+
// Get user skills path from config (unified path: ~/.xagent/skills)
|
|
372
|
+
const { getConfigManager } = await import('./config.js');
|
|
373
|
+
const configManager = getConfigManager();
|
|
374
|
+
const userSkillsPath = configManager.getUserSkillsPath();
|
|
375
|
+
|
|
376
|
+
// Skill deps path: ~/.xagent/skills/{skillName}/node_modules
|
|
377
|
+
const builtinDepsPath = userSkillsPath ? path.join(userSkillsPath, 'builtin-deps') : null;
|
|
378
|
+
|
|
379
|
+
// Determine which node_modules to use
|
|
380
|
+
let skillNodeModulesPath: string | null = null;
|
|
381
|
+
|
|
382
|
+
// Priority 1: skillPath parameter (workspace scenario - LLM works in workspace, not skill dir)
|
|
383
|
+
if (skillPath) {
|
|
384
|
+
if (skillPath.includes('/builtin-deps/')) {
|
|
385
|
+
// Skill with deps in builtin-deps directory
|
|
386
|
+
const match = skillPath.match(/\/builtin-deps\/([^/]+)/);
|
|
387
|
+
if (match) {
|
|
388
|
+
skillNodeModulesPath = path.join(builtinDepsPath!, match[1], 'node_modules');
|
|
389
|
+
}
|
|
390
|
+
} else {
|
|
391
|
+
// Regular skill
|
|
392
|
+
skillNodeModulesPath = path.join(skillPath, 'node_modules');
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
// Priority 2: Check if we're inside a skill directory
|
|
396
|
+
else if (userSkillsPath && userSkillsPath.trim() && actualCwd.startsWith(userSkillsPath)) {
|
|
397
|
+
const relativePath = actualCwd.substring(userSkillsPath.length);
|
|
398
|
+
const pathParts = relativePath.split(path.sep).filter(Boolean);
|
|
399
|
+
|
|
400
|
+
if (pathParts.length > 0) {
|
|
401
|
+
if (pathParts[0] === 'builtin-deps' && pathParts.length > 1) {
|
|
402
|
+
// Skill with local deps in builtin-deps
|
|
403
|
+
const skillName = pathParts[1];
|
|
404
|
+
skillNodeModulesPath = path.join(builtinDepsPath!, skillName, 'node_modules');
|
|
405
|
+
} else {
|
|
406
|
+
// Regular skill
|
|
407
|
+
const skillName = pathParts[0];
|
|
408
|
+
const skillRoot = path.join(userSkillsPath, skillName);
|
|
409
|
+
try {
|
|
410
|
+
const skillMdPath = path.join(skillRoot, 'SKILL.md');
|
|
411
|
+
await fs.access(skillMdPath);
|
|
412
|
+
skillNodeModulesPath = path.join(skillRoot, 'node_modules');
|
|
413
|
+
} catch {
|
|
414
|
+
// Not a skill directory, skip
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Build NODE_PATH - skill's node_modules takes precedence (last-wins)
|
|
421
|
+
let nodePath: string;
|
|
422
|
+
if (skillNodeModulesPath) {
|
|
423
|
+
nodePath = `${skillNodeModulesPath}${path.delimiter}${builtinNodeModulesPath}`;
|
|
424
|
+
} else {
|
|
425
|
+
nodePath = builtinNodeModulesPath;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const env: Record<string, string> = {
|
|
380
429
|
...process.env,
|
|
381
|
-
NODE_PATH:
|
|
430
|
+
NODE_PATH: nodePath
|
|
382
431
|
};
|
|
383
|
-
|
|
384
|
-
//
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
432
|
+
|
|
433
|
+
// Handle npm install commands
|
|
434
|
+
const isNpmInstall = /\bnpm\s+install\b/i.test(command);
|
|
435
|
+
let finalCommand = command;
|
|
436
|
+
|
|
437
|
+
if (isNpmInstall && skillNodeModulesPath) {
|
|
438
|
+
// Install to skill's own node_modules
|
|
439
|
+
await fs.mkdir(skillNodeModulesPath, { recursive: true }).catch(() => {});
|
|
440
|
+
finalCommand = command.replace(/\bnpm\s+install\b/i, `npm install --prefix "${skillNodeModulesPath}"`);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Get shell configuration (Windows Git Bash detection, etc.)
|
|
444
|
+
const { shell, args } = getShellConfig();
|
|
445
|
+
|
|
446
|
+
// Set up cross-platform encoding environment for command execution
|
|
447
|
+
if (process.platform === 'win32') {
|
|
448
|
+
// Windows: set code page to UTF-8 and ensure console output encoding
|
|
449
|
+
// chcp 65001 sets the console code page to UTF-8
|
|
450
|
+
// Use *>$null to suppress output (PowerShell-style, not CMD-style)
|
|
451
|
+
finalCommand = `chcp 65001 *>$null; [Console]::OutputEncoding = [System.Text.Encoding]::UTF8; [System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ${finalCommand}`;
|
|
452
|
+
} else {
|
|
453
|
+
// Unix/macOS: set locale to UTF-8 for proper encoding handling
|
|
454
|
+
finalCommand = `export LC_ALL=C.UTF-8; export LANG=C.UTF-8; export PYTHONIOENCODING=utf-8; ${finalCommand}`;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const shellArgs = [...args, quoteShellCommand(finalCommand)];
|
|
458
|
+
|
|
390
459
|
try {
|
|
391
460
|
if (run_in_bg) {
|
|
392
461
|
const taskId = `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
393
|
-
|
|
394
|
-
const
|
|
462
|
+
|
|
463
|
+
const spawnOptions: any = {
|
|
395
464
|
cwd: effectiveCwd || process.cwd(),
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
465
|
+
env,
|
|
466
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// On Windows, don't use detached mode for PowerShell as it breaks output piping
|
|
470
|
+
if (process.platform !== 'win32') {
|
|
471
|
+
spawnOptions.detached = true;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
const childProcess = spawn(shell, shellArgs, spawnOptions);
|
|
475
|
+
|
|
401
476
|
const output: string[] = [];
|
|
402
|
-
|
|
477
|
+
|
|
403
478
|
childProcess.stdout?.on('data', (data: Buffer) => {
|
|
404
479
|
const text = data.toString();
|
|
405
480
|
output.push(text);
|
|
406
481
|
});
|
|
407
|
-
|
|
482
|
+
|
|
408
483
|
childProcess.stderr?.on('data', (data: Buffer) => {
|
|
409
484
|
const text = data.toString();
|
|
410
485
|
output.push(text);
|
|
411
486
|
});
|
|
412
|
-
|
|
413
|
-
childProcess.on('close', (
|
|
414
|
-
|
|
487
|
+
|
|
488
|
+
childProcess.on('close', (_code: number) => {
|
|
489
|
+
// Silent cleanup - don't log to avoid noise during normal operation
|
|
490
|
+
// Note: On Windows with PowerShell, the shell process exits after
|
|
491
|
+
// the command completes
|
|
415
492
|
});
|
|
416
|
-
|
|
493
|
+
|
|
417
494
|
const toolRegistry = getToolRegistry();
|
|
418
495
|
(toolRegistry as any).addBackgroundTask(taskId, {
|
|
419
496
|
process: childProcess,
|
|
420
497
|
startTime: Date.now(),
|
|
421
|
-
output
|
|
498
|
+
output,
|
|
422
499
|
});
|
|
423
|
-
|
|
500
|
+
|
|
424
501
|
return {
|
|
425
502
|
stdout: '',
|
|
426
503
|
stderr: '',
|
|
427
504
|
exitCode: 0,
|
|
428
|
-
taskId
|
|
505
|
+
taskId,
|
|
429
506
|
};
|
|
430
507
|
} else {
|
|
431
|
-
|
|
508
|
+
// Execute command with spawn for better control
|
|
509
|
+
const result = await this.spawnWithTimeout(shell, shellArgs, {
|
|
432
510
|
cwd: effectiveCwd || process.cwd(),
|
|
433
|
-
|
|
434
|
-
timeout
|
|
435
|
-
env
|
|
511
|
+
env,
|
|
512
|
+
timeout,
|
|
436
513
|
});
|
|
437
514
|
|
|
515
|
+
// Apply truncation to stdout and stderr separately
|
|
516
|
+
const stdoutResult = truncateTail(result.stdout);
|
|
517
|
+
const stderrResult = truncateTail(result.stderr);
|
|
518
|
+
|
|
519
|
+
const stdout = stdoutResult.content;
|
|
520
|
+
const stderr = stderrResult.content;
|
|
521
|
+
let truncationNotice = '';
|
|
522
|
+
|
|
523
|
+
if (stdoutResult.truncated) {
|
|
524
|
+
truncationNotice += buildTruncationNotice(stdoutResult) + '\n';
|
|
525
|
+
}
|
|
526
|
+
if (stderrResult.truncated) {
|
|
527
|
+
truncationNotice += buildTruncationNotice(stderrResult) + '\n';
|
|
528
|
+
}
|
|
529
|
+
|
|
438
530
|
return {
|
|
439
531
|
stdout,
|
|
440
532
|
stderr,
|
|
441
|
-
exitCode:
|
|
533
|
+
exitCode: result.exitCode,
|
|
534
|
+
truncated: stdoutResult.truncated || stderrResult.truncated,
|
|
535
|
+
truncationNotice: truncationNotice || undefined,
|
|
442
536
|
};
|
|
443
537
|
}
|
|
444
538
|
} catch (error: any) {
|
|
539
|
+
// Check if this was a timeout
|
|
540
|
+
if (error.message === 'timeout') {
|
|
541
|
+
return {
|
|
542
|
+
stdout: '',
|
|
543
|
+
stderr: 'Command timed out',
|
|
544
|
+
exitCode: -1,
|
|
545
|
+
truncated: false,
|
|
546
|
+
};
|
|
547
|
+
}
|
|
445
548
|
return {
|
|
446
549
|
stdout: error.stdout || '',
|
|
447
550
|
stderr: error.stderr || error.message,
|
|
448
|
-
exitCode: error.code || 1
|
|
551
|
+
exitCode: error.code || 1,
|
|
449
552
|
};
|
|
450
553
|
}
|
|
451
554
|
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Execute a command with timeout support and proper process termination.
|
|
558
|
+
*/
|
|
559
|
+
private spawnWithTimeout(
|
|
560
|
+
shell: string,
|
|
561
|
+
args: string[],
|
|
562
|
+
options: { cwd: string; env: NodeJS.ProcessEnv; timeout: number }
|
|
563
|
+
): Promise<{ stdout: string; stderr: string; exitCode: number }> {
|
|
564
|
+
return new Promise((resolve, reject) => {
|
|
565
|
+
const { cwd, env, timeout } = options;
|
|
566
|
+
let timedOut = false;
|
|
567
|
+
let timeoutHandle: NodeJS.Timeout | undefined;
|
|
568
|
+
|
|
569
|
+
const spawnOptions: any = {
|
|
570
|
+
cwd,
|
|
571
|
+
env,
|
|
572
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
// On Windows, don't use detached mode for PowerShell as it breaks output piping
|
|
576
|
+
if (process.platform !== 'win32') {
|
|
577
|
+
spawnOptions.detached = true;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const child = spawn(shell, args, spawnOptions);
|
|
581
|
+
|
|
582
|
+
const stdoutChunks: Buffer[] = [];
|
|
583
|
+
const stderrChunks: Buffer[] = [];
|
|
584
|
+
|
|
585
|
+
// Set timeout if provided
|
|
586
|
+
if (timeout > 0) {
|
|
587
|
+
timeoutHandle = setTimeout(() => {
|
|
588
|
+
timedOut = true;
|
|
589
|
+
if (child.pid) {
|
|
590
|
+
killProcessTree(child.pid);
|
|
591
|
+
}
|
|
592
|
+
}, timeout * 1000);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Stream stdout
|
|
596
|
+
child.stdout?.on('data', (data: Buffer) => {
|
|
597
|
+
stdoutChunks.push(data);
|
|
598
|
+
});
|
|
599
|
+
|
|
600
|
+
// Stream stderr
|
|
601
|
+
child.stderr?.on('data', (data: Buffer) => {
|
|
602
|
+
stderrChunks.push(data);
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
// Handle process exit
|
|
606
|
+
child.on('close', (code: number) => {
|
|
607
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
608
|
+
|
|
609
|
+
if (timedOut) {
|
|
610
|
+
reject(new Error('timeout'));
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
resolve({
|
|
615
|
+
stdout: Buffer.concat(stdoutChunks).toString('utf-8'),
|
|
616
|
+
stderr: Buffer.concat(stderrChunks).toString('utf-8'),
|
|
617
|
+
exitCode: code ?? -1,
|
|
618
|
+
});
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
// Handle spawn errors
|
|
622
|
+
child.on('error', (err) => {
|
|
623
|
+
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
624
|
+
reject(err);
|
|
625
|
+
});
|
|
626
|
+
});
|
|
627
|
+
}
|
|
452
628
|
}
|
|
453
629
|
|
|
454
630
|
export class ListDirectoryTool implements Tool {
|
|
@@ -481,27 +657,32 @@ export class ListDirectoryTool implements Tool {
|
|
|
481
657
|
- Results are absolute paths
|
|
482
658
|
- Ignores node_modules and .git by default
|
|
483
659
|
- Combine with Read to examine file contents`;
|
|
484
|
-
allowedModes = [
|
|
660
|
+
allowedModes = [
|
|
661
|
+
ExecutionMode.YOLO,
|
|
662
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
663
|
+
ExecutionMode.PLAN,
|
|
664
|
+
ExecutionMode.SMART,
|
|
665
|
+
];
|
|
485
666
|
|
|
486
667
|
async execute(params: { path?: string; recursive?: boolean }): Promise<string[]> {
|
|
487
668
|
const { path: dirPath = '.', recursive = false } = params;
|
|
488
|
-
|
|
669
|
+
|
|
489
670
|
try {
|
|
490
671
|
const absolutePath = path.resolve(dirPath);
|
|
491
|
-
|
|
672
|
+
|
|
492
673
|
const stats = await fs.stat(absolutePath).catch(() => null);
|
|
493
674
|
if (!stats || !stats.isDirectory()) {
|
|
494
675
|
throw new Error(`Directory does not exist: ${dirPath}`);
|
|
495
676
|
}
|
|
496
|
-
|
|
677
|
+
|
|
497
678
|
const pattern = recursive ? '**/*' : '*';
|
|
498
679
|
const files = await glob(pattern, {
|
|
499
680
|
cwd: absolutePath,
|
|
500
681
|
nodir: false,
|
|
501
|
-
ignore: ['node_modules/**', '.git/**']
|
|
682
|
+
ignore: ['node_modules/**', '.git/**'],
|
|
502
683
|
});
|
|
503
684
|
|
|
504
|
-
return files.map(file => path.join(absolutePath, file));
|
|
685
|
+
return files.map((file) => path.join(absolutePath, file));
|
|
505
686
|
} catch (error: any) {
|
|
506
687
|
throw new Error(`Failed to list directory: ${error.message}`);
|
|
507
688
|
}
|
|
@@ -519,7 +700,7 @@ export interface SearchFilesResult {
|
|
|
519
700
|
|
|
520
701
|
export class SearchFilesTool implements Tool {
|
|
521
702
|
name = 'SearchFiles';
|
|
522
|
-
description = `Search for files matching a glob pattern. This is your PRIMARY tool for finding files by name or extension.
|
|
703
|
+
description = `Search for files matching a glob pattern using fd. This is your PRIMARY tool for finding files by name or extension.
|
|
523
704
|
|
|
524
705
|
# When to Use
|
|
525
706
|
- Finding all files of a certain type (*.ts, *.json, *.md)
|
|
@@ -554,17 +735,37 @@ export class SearchFilesTool implements Tool {
|
|
|
554
735
|
- Combine with path parameter to search specific directories
|
|
555
736
|
- Use limit parameter to avoid huge result sets
|
|
556
737
|
- Results are file paths, not content (use Grep on results if needed)`;
|
|
557
|
-
allowedModes = [
|
|
738
|
+
allowedModes = [
|
|
739
|
+
ExecutionMode.YOLO,
|
|
740
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
741
|
+
ExecutionMode.PLAN,
|
|
742
|
+
ExecutionMode.SMART,
|
|
743
|
+
];
|
|
558
744
|
|
|
559
|
-
async execute(params: {
|
|
745
|
+
async execute(params: {
|
|
746
|
+
pattern: string;
|
|
747
|
+
path?: string;
|
|
748
|
+
limit?: number;
|
|
749
|
+
}): Promise<SearchFilesResult> {
|
|
560
750
|
const { pattern, path: searchPath = '.', limit = 1000 } = params;
|
|
561
751
|
|
|
562
752
|
try {
|
|
563
|
-
const
|
|
564
|
-
|
|
565
|
-
|
|
753
|
+
const output = await fdFind({
|
|
754
|
+
pattern,
|
|
755
|
+
path: searchPath,
|
|
756
|
+
limit,
|
|
566
757
|
});
|
|
567
758
|
|
|
759
|
+
if (output === 'No files found') {
|
|
760
|
+
return {
|
|
761
|
+
files: [],
|
|
762
|
+
total: 0,
|
|
763
|
+
truncated: false,
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
const files = output.split('\n').filter((line) => line.trim());
|
|
768
|
+
|
|
568
769
|
const total = files.length;
|
|
569
770
|
const truncated = total > limit;
|
|
570
771
|
const result = truncated ? files.slice(0, limit) : files;
|
|
@@ -572,7 +773,7 @@ export class SearchFilesTool implements Tool {
|
|
|
572
773
|
return {
|
|
573
774
|
files: result,
|
|
574
775
|
total,
|
|
575
|
-
truncated
|
|
776
|
+
truncated,
|
|
576
777
|
};
|
|
577
778
|
} catch (error: any) {
|
|
578
779
|
throw new Error(`Search failed: ${error.message}`);
|
|
@@ -608,7 +809,9 @@ export class DeleteFileTool implements Tool {
|
|
|
608
809
|
- This action is irreversible - be certain before executing`;
|
|
609
810
|
allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
|
|
610
811
|
|
|
611
|
-
async execute(params: {
|
|
812
|
+
async execute(params: {
|
|
813
|
+
filePath: string;
|
|
814
|
+
}): Promise<{ success: boolean; message: string; filePath: string }> {
|
|
612
815
|
const { filePath } = params;
|
|
613
816
|
|
|
614
817
|
try {
|
|
@@ -618,7 +821,7 @@ export class DeleteFileTool implements Tool {
|
|
|
618
821
|
return {
|
|
619
822
|
success: true,
|
|
620
823
|
message: `Successfully deleted ${filePath}`,
|
|
621
|
-
filePath
|
|
824
|
+
filePath,
|
|
622
825
|
};
|
|
623
826
|
} catch (error: any) {
|
|
624
827
|
throw new Error(`Failed to delete file ${filePath}: ${error.message}`);
|
|
@@ -654,16 +857,19 @@ export class CreateDirectoryTool implements Tool {
|
|
|
654
857
|
- Consider the overall project structure before creating`;
|
|
655
858
|
allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
|
|
656
859
|
|
|
657
|
-
async execute(params: {
|
|
860
|
+
async execute(params: {
|
|
861
|
+
dirPath: string;
|
|
862
|
+
recursive?: boolean;
|
|
863
|
+
}): Promise<{ success: boolean; message: string }> {
|
|
658
864
|
const { dirPath, recursive = true } = params;
|
|
659
|
-
|
|
865
|
+
|
|
660
866
|
try {
|
|
661
867
|
const absolutePath = path.resolve(dirPath);
|
|
662
868
|
await fs.mkdir(absolutePath, { recursive });
|
|
663
|
-
|
|
869
|
+
|
|
664
870
|
return {
|
|
665
871
|
success: true,
|
|
666
|
-
message: `Successfully created directory ${dirPath}
|
|
872
|
+
message: `Successfully created directory ${dirPath}`,
|
|
667
873
|
};
|
|
668
874
|
} catch (error: any) {
|
|
669
875
|
throw new Error(`Failed to create directory ${dirPath}: ${error.message}`);
|
|
@@ -672,165 +878,169 @@ export class CreateDirectoryTool implements Tool {
|
|
|
672
878
|
}
|
|
673
879
|
|
|
674
880
|
// 编辑工具辅助函数
|
|
675
|
-
function detectLineEnding(content: string):
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
881
|
+
function detectLineEnding(content: string): '\r\n' | '\n' {
|
|
882
|
+
const crlfIdx = content.indexOf('\r\n');
|
|
883
|
+
const lfIdx = content.indexOf('\n');
|
|
884
|
+
if (lfIdx === -1) return '\n';
|
|
885
|
+
if (crlfIdx === -1) return '\n';
|
|
886
|
+
return crlfIdx < lfIdx ? '\r\n' : '\n';
|
|
681
887
|
}
|
|
682
888
|
|
|
683
889
|
function normalizeToLF(text: string): string {
|
|
684
|
-
|
|
890
|
+
return text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
685
891
|
}
|
|
686
892
|
|
|
687
|
-
function restoreLineEndings(text: string, ending:
|
|
688
|
-
|
|
893
|
+
function restoreLineEndings(text: string, ending: '\r\n' | '\n'): string {
|
|
894
|
+
return ending === '\r\n' ? text.replace(/\n/g, '\r\n') : text;
|
|
689
895
|
}
|
|
690
896
|
|
|
691
897
|
function normalizeForFuzzyMatch(text: string): string {
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
.replace(/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g, " ")
|
|
701
|
-
);
|
|
898
|
+
return text
|
|
899
|
+
.split('\n')
|
|
900
|
+
.map((line) => line.trimEnd())
|
|
901
|
+
.join('\n')
|
|
902
|
+
.replace(/['‘’""]/g, "'")
|
|
903
|
+
.replace(/["""]/g, '"')
|
|
904
|
+
.replace(/[—–‑−]/g, '-')
|
|
905
|
+
.replace(/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g, ' ');
|
|
702
906
|
}
|
|
703
907
|
|
|
704
908
|
interface FuzzyMatchResult {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
909
|
+
found: boolean;
|
|
910
|
+
index: number;
|
|
911
|
+
matchLength: number;
|
|
912
|
+
usedFuzzyMatch: boolean;
|
|
913
|
+
contentForReplacement: string;
|
|
710
914
|
}
|
|
711
915
|
|
|
712
916
|
function fuzzyFindText(content: string, oldText: string): FuzzyMatchResult {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
917
|
+
const exactIndex = content.indexOf(oldText);
|
|
918
|
+
if (exactIndex !== -1) {
|
|
919
|
+
return {
|
|
920
|
+
found: true,
|
|
921
|
+
index: exactIndex,
|
|
922
|
+
matchLength: oldText.length,
|
|
923
|
+
usedFuzzyMatch: false,
|
|
924
|
+
contentForReplacement: content,
|
|
925
|
+
};
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
const fuzzyContent = normalizeForFuzzyMatch(content);
|
|
929
|
+
const fuzzyOldText = normalizeForFuzzyMatch(oldText);
|
|
930
|
+
const fuzzyIndex = fuzzyContent.indexOf(fuzzyOldText);
|
|
931
|
+
|
|
932
|
+
if (fuzzyIndex === -1) {
|
|
933
|
+
return {
|
|
934
|
+
found: false,
|
|
935
|
+
index: -1,
|
|
936
|
+
matchLength: 0,
|
|
937
|
+
usedFuzzyMatch: false,
|
|
938
|
+
contentForReplacement: content,
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
return {
|
|
943
|
+
found: true,
|
|
944
|
+
index: fuzzyIndex,
|
|
945
|
+
matchLength: fuzzyOldText.length,
|
|
946
|
+
usedFuzzyMatch: true,
|
|
947
|
+
contentForReplacement: fuzzyContent,
|
|
948
|
+
};
|
|
745
949
|
}
|
|
746
950
|
|
|
747
951
|
function stripBom(content: string): { bom: string; text: string } {
|
|
748
|
-
|
|
952
|
+
return content.startsWith('\uFEFF')
|
|
953
|
+
? { bom: '\uFEFF', text: content.slice(1) }
|
|
954
|
+
: { bom: '', text: content };
|
|
749
955
|
}
|
|
750
956
|
|
|
751
|
-
async function generateDiffString(
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
957
|
+
async function generateDiffString(
|
|
958
|
+
oldContent: string,
|
|
959
|
+
newContent: string,
|
|
960
|
+
contextLines = 4
|
|
961
|
+
): Promise<{ diff: string; firstChangedLine: number | undefined }> {
|
|
962
|
+
const diffModule = await import('diff');
|
|
963
|
+
const parts = diffModule.diffLines(oldContent, newContent);
|
|
964
|
+
const output: string[] = [];
|
|
965
|
+
|
|
966
|
+
const oldLines = oldContent.split('\n');
|
|
967
|
+
const newLines = newContent.split('\n');
|
|
968
|
+
const maxLineNum = Math.max(oldLines.length, newLines.length);
|
|
969
|
+
const lineNumWidth = String(maxLineNum).length;
|
|
970
|
+
|
|
971
|
+
let oldLineNum = 1;
|
|
972
|
+
let newLineNum = 1;
|
|
973
|
+
let lastWasChange = false;
|
|
974
|
+
let firstChangedLine: number | undefined;
|
|
975
|
+
|
|
976
|
+
for (let i = 0; i < parts.length; i++) {
|
|
977
|
+
const part = parts[i];
|
|
978
|
+
const raw = part.value.split('\n');
|
|
979
|
+
if (raw[raw.length - 1] === '') {
|
|
980
|
+
raw.pop();
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
if (part.added || part.removed) {
|
|
984
|
+
if (firstChangedLine === undefined) {
|
|
985
|
+
firstChangedLine = newLineNum;
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
for (const line of raw) {
|
|
989
|
+
if (part.added) {
|
|
990
|
+
const lineNum = String(newLineNum).padStart(lineNumWidth, ' ');
|
|
991
|
+
output.push(`+${lineNum} ${line}`);
|
|
992
|
+
newLineNum++;
|
|
993
|
+
} else {
|
|
994
|
+
const lineNum = String(oldLineNum).padStart(lineNumWidth, ' ');
|
|
995
|
+
output.push(`-${lineNum} ${line}`);
|
|
996
|
+
oldLineNum++;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
lastWasChange = true;
|
|
1000
|
+
} else {
|
|
1001
|
+
const nextPartIsChange = i < parts.length - 1 && (parts[i + 1].added || parts[i + 1].removed);
|
|
1002
|
+
|
|
1003
|
+
if (lastWasChange || nextPartIsChange) {
|
|
1004
|
+
let linesToShow = raw;
|
|
1005
|
+
let skipStart = 0;
|
|
1006
|
+
let skipEnd = 0;
|
|
1007
|
+
|
|
1008
|
+
if (!lastWasChange) {
|
|
1009
|
+
skipStart = Math.max(0, raw.length - contextLines);
|
|
1010
|
+
linesToShow = raw.slice(skipStart);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
if (!nextPartIsChange && linesToShow.length > contextLines) {
|
|
1014
|
+
skipEnd = linesToShow.length - contextLines;
|
|
1015
|
+
linesToShow = linesToShow.slice(0, contextLines);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
if (skipStart > 0) {
|
|
1019
|
+
output.push(` ${''.padStart(lineNumWidth, ' ')} ...`);
|
|
1020
|
+
oldLineNum += skipStart;
|
|
1021
|
+
newLineNum += skipStart;
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
for (const line of linesToShow) {
|
|
1025
|
+
const lineNum = String(oldLineNum).padStart(lineNumWidth, ' ');
|
|
1026
|
+
output.push(` ${lineNum} ${line}`);
|
|
1027
|
+
oldLineNum++;
|
|
1028
|
+
newLineNum++;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
if (skipEnd > 0) {
|
|
1032
|
+
output.push(` ${''.padStart(lineNumWidth, ' ')} ...`);
|
|
1033
|
+
}
|
|
1034
|
+
} else {
|
|
1035
|
+
oldLineNum += raw.length;
|
|
1036
|
+
newLineNum += raw.length;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
lastWasChange = false;
|
|
1040
|
+
}
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
return { diff: output.join('\n'), firstChangedLine };
|
|
834
1044
|
}
|
|
835
1045
|
|
|
836
1046
|
export class EditTool implements Tool {
|
|
@@ -888,10 +1098,12 @@ edit(
|
|
|
888
1098
|
new_string: string;
|
|
889
1099
|
}): Promise<{ success: boolean; message: string; diff?: string; firstChangedLine?: number }> {
|
|
890
1100
|
const { file_path, instruction, old_string, new_string } = params;
|
|
891
|
-
|
|
1101
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1102
|
+
void instruction;
|
|
1103
|
+
|
|
892
1104
|
try {
|
|
893
1105
|
const absolutePath = path.resolve(file_path);
|
|
894
|
-
|
|
1106
|
+
|
|
895
1107
|
// Check if file exists
|
|
896
1108
|
try {
|
|
897
1109
|
await fs.access(absolutePath);
|
|
@@ -904,7 +1116,7 @@ edit(
|
|
|
904
1116
|
|
|
905
1117
|
// Read the file
|
|
906
1118
|
const buffer = await fs.readFile(absolutePath);
|
|
907
|
-
const rawContent = buffer.toString(
|
|
1119
|
+
const rawContent = buffer.toString('utf-8');
|
|
908
1120
|
|
|
909
1121
|
// Strip BOM before matching
|
|
910
1122
|
const { bom, text: content } = stripBom(rawContent);
|
|
@@ -952,7 +1164,7 @@ edit(
|
|
|
952
1164
|
}
|
|
953
1165
|
|
|
954
1166
|
const finalContent = bom + restoreLineEndings(newContent, originalEnding);
|
|
955
|
-
await fs.writeFile(absolutePath, finalContent,
|
|
1167
|
+
await fs.writeFile(absolutePath, finalContent, 'utf-8');
|
|
956
1168
|
|
|
957
1169
|
const diffResult = await generateDiffString(baseContent, newContent);
|
|
958
1170
|
|
|
@@ -1000,38 +1212,43 @@ export class WebSearchTool implements Tool {
|
|
|
1000
1212
|
- Combine with web_fetch to get full content from relevant URLs
|
|
1001
1213
|
- Use quotes for exact phrase matching
|
|
1002
1214
|
- Consider adding context like year or version in query`;
|
|
1003
|
-
allowedModes = [
|
|
1215
|
+
allowedModes = [
|
|
1216
|
+
ExecutionMode.YOLO,
|
|
1217
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
1218
|
+
ExecutionMode.PLAN,
|
|
1219
|
+
ExecutionMode.SMART,
|
|
1220
|
+
];
|
|
1004
1221
|
|
|
1005
1222
|
async execute(params: { query: string }): Promise<{ results: any[]; message: string }> {
|
|
1006
1223
|
const { query } = params;
|
|
1007
|
-
|
|
1224
|
+
|
|
1008
1225
|
try {
|
|
1009
1226
|
const configManager = await import('./config.js');
|
|
1010
1227
|
const { getConfigManager } = configManager;
|
|
1011
1228
|
const config = getConfigManager();
|
|
1012
|
-
|
|
1229
|
+
|
|
1013
1230
|
const searchApiKey = config.get('searchApiKey');
|
|
1014
1231
|
const baseUrl = config.get('baseUrl') || 'https://apis.xagent.cn/v1';
|
|
1015
|
-
|
|
1232
|
+
|
|
1016
1233
|
if (!searchApiKey) {
|
|
1017
1234
|
throw new Error('Search API key not configured. Please set searchApiKey in settings.');
|
|
1018
1235
|
}
|
|
1019
|
-
|
|
1236
|
+
|
|
1020
1237
|
const response = await axios.post(
|
|
1021
1238
|
`${baseUrl}/search`,
|
|
1022
1239
|
{ query },
|
|
1023
1240
|
{
|
|
1024
1241
|
headers: {
|
|
1025
|
-
|
|
1026
|
-
'Content-Type': 'application/json'
|
|
1242
|
+
Authorization: `Bearer ${searchApiKey}`,
|
|
1243
|
+
'Content-Type': 'application/json',
|
|
1027
1244
|
},
|
|
1028
|
-
timeout: 30000
|
|
1245
|
+
timeout: 30000,
|
|
1029
1246
|
}
|
|
1030
1247
|
);
|
|
1031
|
-
|
|
1248
|
+
|
|
1032
1249
|
return {
|
|
1033
1250
|
results: response.data.results || [],
|
|
1034
|
-
message: `Found ${response.data.results?.length || 0} results for "${query}"
|
|
1251
|
+
message: `Found ${response.data.results?.length || 0} results for "${query}"`,
|
|
1035
1252
|
};
|
|
1036
1253
|
} catch (error: any) {
|
|
1037
1254
|
throw new Error(`Web search failed: ${error.message}`);
|
|
@@ -1087,29 +1304,44 @@ Each task needs:
|
|
|
1087
1304
|
- Don't batch multiple completions - update as you go
|
|
1088
1305
|
- Keep task descriptions clear and actionable
|
|
1089
1306
|
- Use appropriate priority levels to indicate urgency`;
|
|
1090
|
-
allowedModes = [
|
|
1091
|
-
|
|
1092
|
-
|
|
1307
|
+
allowedModes = [
|
|
1308
|
+
ExecutionMode.YOLO,
|
|
1309
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
1310
|
+
ExecutionMode.PLAN,
|
|
1311
|
+
ExecutionMode.SMART,
|
|
1312
|
+
];
|
|
1313
|
+
|
|
1314
|
+
private todoList: Array<{
|
|
1315
|
+
id: string;
|
|
1316
|
+
task: string;
|
|
1317
|
+
status: 'pending' | 'in_progress' | 'completed' | 'failed';
|
|
1318
|
+
priority: 'high' | 'medium' | 'low';
|
|
1319
|
+
}> = [];
|
|
1093
1320
|
|
|
1094
1321
|
async execute(params: {
|
|
1095
|
-
todos: Array<{
|
|
1322
|
+
todos: Array<{
|
|
1323
|
+
id: string;
|
|
1324
|
+
task: string;
|
|
1325
|
+
status: 'pending' | 'in_progress' | 'completed' | 'failed';
|
|
1326
|
+
priority: 'high' | 'medium' | 'low';
|
|
1327
|
+
}>;
|
|
1096
1328
|
}): Promise<{ success: boolean; message: string; todos: any[] }> {
|
|
1097
1329
|
const { todos } = params;
|
|
1098
|
-
|
|
1330
|
+
|
|
1099
1331
|
try {
|
|
1100
1332
|
this.todoList = todos;
|
|
1101
|
-
|
|
1333
|
+
|
|
1102
1334
|
const summary = {
|
|
1103
|
-
pending: todos.filter(t => t.status === 'pending').length,
|
|
1104
|
-
in_progress: todos.filter(t => t.status === 'in_progress').length,
|
|
1105
|
-
completed: todos.filter(t => t.status === 'completed').length,
|
|
1106
|
-
failed: todos.filter(t => t.status === 'failed').length
|
|
1335
|
+
pending: todos.filter((t) => t.status === 'pending').length,
|
|
1336
|
+
in_progress: todos.filter((t) => t.status === 'in_progress').length,
|
|
1337
|
+
completed: todos.filter((t) => t.status === 'completed').length,
|
|
1338
|
+
failed: todos.filter((t) => t.status === 'failed').length,
|
|
1107
1339
|
};
|
|
1108
|
-
|
|
1340
|
+
|
|
1109
1341
|
return {
|
|
1110
1342
|
success: true,
|
|
1111
1343
|
message: `Updated todo list: ${summary.pending} pending, ${summary.in_progress} in progress, ${summary.completed} completed, ${summary.failed} failed`,
|
|
1112
|
-
todos: this.todoList
|
|
1344
|
+
todos: this.todoList,
|
|
1113
1345
|
};
|
|
1114
1346
|
} catch (error: any) {
|
|
1115
1347
|
throw new Error(`Failed to update todo list: ${error.message}`);
|
|
@@ -1142,7 +1374,12 @@ export class TodoReadTool implements Tool {
|
|
|
1142
1374
|
# Best Practices
|
|
1143
1375
|
- Use todo_write to modify the list, not todo_read
|
|
1144
1376
|
- Check todo_read after todo_write to verify updates`;
|
|
1145
|
-
allowedModes = [
|
|
1377
|
+
allowedModes = [
|
|
1378
|
+
ExecutionMode.YOLO,
|
|
1379
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
1380
|
+
ExecutionMode.PLAN,
|
|
1381
|
+
ExecutionMode.SMART,
|
|
1382
|
+
];
|
|
1146
1383
|
|
|
1147
1384
|
private todoWriteTool: TodoWriteTool;
|
|
1148
1385
|
|
|
@@ -1153,18 +1390,18 @@ export class TodoReadTool implements Tool {
|
|
|
1153
1390
|
async execute(): Promise<{ todos: any[]; summary: any }> {
|
|
1154
1391
|
try {
|
|
1155
1392
|
const todos = this.todoWriteTool.getTodos();
|
|
1156
|
-
|
|
1393
|
+
|
|
1157
1394
|
const summary = {
|
|
1158
1395
|
total: todos.length,
|
|
1159
|
-
pending: todos.filter(t => t.status === 'pending').length,
|
|
1160
|
-
in_progress: todos.filter(t => t.status === 'in_progress').length,
|
|
1161
|
-
completed: todos.filter(t => t.status === 'completed').length,
|
|
1162
|
-
failed: todos.filter(t => t.status === 'failed').length
|
|
1396
|
+
pending: todos.filter((t) => t.status === 'pending').length,
|
|
1397
|
+
in_progress: todos.filter((t) => t.status === 'in_progress').length,
|
|
1398
|
+
completed: todos.filter((t) => t.status === 'completed').length,
|
|
1399
|
+
failed: todos.filter((t) => t.status === 'failed').length,
|
|
1163
1400
|
};
|
|
1164
|
-
|
|
1401
|
+
|
|
1165
1402
|
return {
|
|
1166
1403
|
todos,
|
|
1167
|
-
summary
|
|
1404
|
+
summary,
|
|
1168
1405
|
};
|
|
1169
1406
|
} catch (error: any) {
|
|
1170
1407
|
throw new Error(`Failed to read todo list: ${error.message}`);
|
|
@@ -1175,7 +1412,15 @@ export class TodoReadTool implements Tool {
|
|
|
1175
1412
|
export interface SubAgentTask {
|
|
1176
1413
|
description: string;
|
|
1177
1414
|
prompt: string;
|
|
1178
|
-
subagent_type:
|
|
1415
|
+
subagent_type:
|
|
1416
|
+
| 'general-purpose'
|
|
1417
|
+
| 'plan-agent'
|
|
1418
|
+
| 'explore-agent'
|
|
1419
|
+
| 'frontend-tester'
|
|
1420
|
+
| 'code-reviewer'
|
|
1421
|
+
| 'frontend-developer'
|
|
1422
|
+
| 'backend-developer'
|
|
1423
|
+
| 'gui-subagent';
|
|
1179
1424
|
useContext?: boolean;
|
|
1180
1425
|
outputFormat?: string;
|
|
1181
1426
|
constraints?: string[];
|
|
@@ -1225,39 +1470,52 @@ export class TaskTool implements Tool {
|
|
|
1225
1470
|
- Include relevant context (file paths, requirements, constraints)
|
|
1226
1471
|
- Set appropriate executionMode if needed
|
|
1227
1472
|
- For parallel execution, ensure tasks are truly independent`;
|
|
1228
|
-
allowedModes = [
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1473
|
+
allowedModes = [
|
|
1474
|
+
ExecutionMode.YOLO,
|
|
1475
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
1476
|
+
ExecutionMode.PLAN,
|
|
1477
|
+
ExecutionMode.SMART,
|
|
1478
|
+
];
|
|
1479
|
+
|
|
1480
|
+
async execute(
|
|
1481
|
+
params: {
|
|
1482
|
+
description: string;
|
|
1483
|
+
prompt?: string;
|
|
1484
|
+
query?: string; // Support both prompt and query (tool definition uses query)
|
|
1485
|
+
subagent_type?:
|
|
1486
|
+
| 'general-purpose'
|
|
1487
|
+
| 'plan-agent'
|
|
1488
|
+
| 'explore-agent'
|
|
1489
|
+
| 'frontend-tester'
|
|
1490
|
+
| 'code-reviewer'
|
|
1491
|
+
| 'frontend-developer'
|
|
1492
|
+
| 'backend-developer'
|
|
1493
|
+
| 'gui-subagent';
|
|
1494
|
+
agents?: SubAgentTask[];
|
|
1495
|
+
useContext?: boolean;
|
|
1496
|
+
outputFormat?: string;
|
|
1497
|
+
constraints?: string[];
|
|
1498
|
+
executionMode?: ExecutionMode;
|
|
1499
|
+
parallel?: boolean;
|
|
1500
|
+
},
|
|
1501
|
+
_executionMode?: ExecutionMode
|
|
1502
|
+
): Promise<{ success: boolean; message: string; result?: any }> {
|
|
1242
1503
|
const mode = params.executionMode || _executionMode || ExecutionMode.YOLO;
|
|
1243
|
-
|
|
1504
|
+
|
|
1244
1505
|
try {
|
|
1245
1506
|
const { getAgentManager } = await import('./agents.js');
|
|
1246
1507
|
const agentManager = getAgentManager(process.cwd());
|
|
1247
|
-
|
|
1508
|
+
|
|
1248
1509
|
const { getConfigManager } = await import('./config.js');
|
|
1249
1510
|
const config = getConfigManager();
|
|
1250
|
-
|
|
1251
|
-
const
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
modelName: config.get('modelName') || 'Qwen3-Coder'
|
|
1257
|
-
});
|
|
1258
|
-
|
|
1511
|
+
|
|
1512
|
+
const authConfig = config.getAuthConfig();
|
|
1513
|
+
// aiClient is created for future use when executeParallelAgents supports it
|
|
1514
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1515
|
+
const _aiClient = createAIClient(authConfig);
|
|
1516
|
+
|
|
1259
1517
|
const toolRegistry = getToolRegistry();
|
|
1260
|
-
|
|
1518
|
+
|
|
1261
1519
|
if (params.agents && params.agents.length > 0) {
|
|
1262
1520
|
return await this.executeParallelAgents(
|
|
1263
1521
|
params.agents,
|
|
@@ -1265,10 +1523,10 @@ export class TaskTool implements Tool {
|
|
|
1265
1523
|
mode,
|
|
1266
1524
|
agentManager,
|
|
1267
1525
|
toolRegistry,
|
|
1268
|
-
|
|
1526
|
+
config
|
|
1269
1527
|
);
|
|
1270
1528
|
}
|
|
1271
|
-
|
|
1529
|
+
|
|
1272
1530
|
if (!params.subagent_type) {
|
|
1273
1531
|
throw new Error('subagent_type is required for Task tool');
|
|
1274
1532
|
}
|
|
@@ -1276,13 +1534,16 @@ export class TaskTool implements Tool {
|
|
|
1276
1534
|
// Support both 'prompt' and 'query' parameter names (tool definition uses 'query')
|
|
1277
1535
|
const prompt = params.prompt || params.query;
|
|
1278
1536
|
if (!prompt) {
|
|
1279
|
-
throw new Error(
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1537
|
+
throw new Error(
|
|
1538
|
+
'Task query/prompt is required. Received params: ' +
|
|
1539
|
+
JSON.stringify({
|
|
1540
|
+
subagent_type: params.subagent_type,
|
|
1541
|
+
prompt: params.prompt,
|
|
1542
|
+
query: params.query,
|
|
1543
|
+
description: params.description,
|
|
1544
|
+
agents: params.agents?.length,
|
|
1545
|
+
})
|
|
1546
|
+
);
|
|
1286
1547
|
}
|
|
1287
1548
|
|
|
1288
1549
|
const result = await this.executeSingleAgent(
|
|
@@ -1294,10 +1555,9 @@ export class TaskTool implements Tool {
|
|
|
1294
1555
|
mode,
|
|
1295
1556
|
agentManager,
|
|
1296
1557
|
toolRegistry,
|
|
1297
|
-
aiClient,
|
|
1298
1558
|
config
|
|
1299
1559
|
);
|
|
1300
|
-
|
|
1560
|
+
|
|
1301
1561
|
return result;
|
|
1302
1562
|
} catch (error: any) {
|
|
1303
1563
|
throw new Error(`Task execution failed: ${error.message}`);
|
|
@@ -1308,16 +1568,27 @@ export class TaskTool implements Tool {
|
|
|
1308
1568
|
* Create unified VLM caller
|
|
1309
1569
|
* Uses remote VLM if remoteAIClient is provided, otherwise uses local VLM
|
|
1310
1570
|
* Both modes receive full messages array for consistent behavior
|
|
1571
|
+
* @param remoteAIClient - Remote AI client for VLM calls
|
|
1572
|
+
* @param taskId - Task identifier for backend tracking
|
|
1573
|
+
* @param localConfig - Local VLM configuration
|
|
1574
|
+
* @param isFirstVlmCallRef - Reference to boolean tracking if this is the first VLM call
|
|
1575
|
+
* @param signal - Abort signal for cancellation
|
|
1311
1576
|
*/
|
|
1312
1577
|
private createRemoteVlmCaller(
|
|
1313
1578
|
remoteAIClient: any,
|
|
1314
1579
|
taskId: string | null,
|
|
1315
1580
|
localConfig: { baseUrl: string; apiKey: string; modelName: string },
|
|
1581
|
+
isFirstVlmCallRef: { current: boolean },
|
|
1316
1582
|
signal?: AbortSignal
|
|
1317
|
-
): (
|
|
1583
|
+
): (
|
|
1584
|
+
messages: any[],
|
|
1585
|
+
systemPrompt: string,
|
|
1586
|
+
taskId: string,
|
|
1587
|
+
isFirstVlmCallRef: { current: boolean }
|
|
1588
|
+
) => Promise<string> {
|
|
1318
1589
|
// Remote mode: use RemoteAIClient
|
|
1319
1590
|
if (remoteAIClient) {
|
|
1320
|
-
return this.createRemoteVLMCaller(remoteAIClient, taskId, signal);
|
|
1591
|
+
return this.createRemoteVLMCaller(remoteAIClient, taskId, isFirstVlmCallRef, signal);
|
|
1321
1592
|
}
|
|
1322
1593
|
|
|
1323
1594
|
// Local mode: use local API
|
|
@@ -1327,11 +1598,39 @@ export class TaskTool implements Tool {
|
|
|
1327
1598
|
/**
|
|
1328
1599
|
* Create remote VLM caller using RemoteAIClient
|
|
1329
1600
|
* Now receives full messages array for consistent behavior with local mode
|
|
1601
|
+
* @param remoteAIClient - Remote AI client
|
|
1602
|
+
* @param taskId - Task identifier for backend tracking
|
|
1603
|
+
* @param isFirstVlmCallRef - Reference to boolean tracking if this is the first VLM call
|
|
1604
|
+
* @param signal - Abort signal for cancellation
|
|
1330
1605
|
*/
|
|
1331
|
-
private createRemoteVLMCaller(
|
|
1332
|
-
|
|
1606
|
+
private createRemoteVLMCaller(
|
|
1607
|
+
remoteAIClient: any,
|
|
1608
|
+
taskId: string | null,
|
|
1609
|
+
isFirstVlmCallRef: { current: boolean },
|
|
1610
|
+
signal?: AbortSignal
|
|
1611
|
+
): (
|
|
1612
|
+
messages: any[],
|
|
1613
|
+
systemPrompt: string,
|
|
1614
|
+
taskId: string,
|
|
1615
|
+
isFirstVlmCallRef: { current: boolean }
|
|
1616
|
+
) => Promise<string> {
|
|
1617
|
+
return async (
|
|
1618
|
+
messages: any[],
|
|
1619
|
+
systemPrompt: string,
|
|
1620
|
+
_taskId: string,
|
|
1621
|
+
_isFirstVlmCallRef: { current: boolean }
|
|
1622
|
+
): Promise<string> => {
|
|
1333
1623
|
try {
|
|
1334
|
-
|
|
1624
|
+
// Use the ref to track first call status for the backend
|
|
1625
|
+
const status = isFirstVlmCallRef.current ? 'begin' : 'continue';
|
|
1626
|
+
const result = await remoteAIClient.invokeVLM(messages, systemPrompt, {
|
|
1627
|
+
signal,
|
|
1628
|
+
taskId,
|
|
1629
|
+
status,
|
|
1630
|
+
});
|
|
1631
|
+
// Update ref after call so subsequent calls use 'continue'
|
|
1632
|
+
isFirstVlmCallRef.current = false;
|
|
1633
|
+
return result;
|
|
1335
1634
|
} catch (error: any) {
|
|
1336
1635
|
throw new Error(`Remote VLM call failed: ${error.message}`);
|
|
1337
1636
|
}
|
|
@@ -1368,7 +1667,7 @@ export class TaskTool implements Tool {
|
|
|
1368
1667
|
method: 'POST',
|
|
1369
1668
|
headers: {
|
|
1370
1669
|
'Content-Type': 'application/json',
|
|
1371
|
-
|
|
1670
|
+
Authorization: `Bearer ${apiKey}`,
|
|
1372
1671
|
},
|
|
1373
1672
|
body: JSON.stringify(requestBody),
|
|
1374
1673
|
signal: abortSignal,
|
|
@@ -1379,7 +1678,9 @@ export class TaskTool implements Tool {
|
|
|
1379
1678
|
throw new Error(`VLM API error: ${errorText}`);
|
|
1380
1679
|
}
|
|
1381
1680
|
|
|
1382
|
-
const result = await response.json() as {
|
|
1681
|
+
const result = (await response.json()) as {
|
|
1682
|
+
choices?: Array<{ message?: { content?: string } }>;
|
|
1683
|
+
};
|
|
1383
1684
|
return result.choices?.[0]?.message?.content || '';
|
|
1384
1685
|
};
|
|
1385
1686
|
}
|
|
@@ -1399,32 +1700,65 @@ export class TaskTool implements Tool {
|
|
|
1399
1700
|
): Promise<{ success: boolean; cancelled?: boolean; message: string; result?: any }> {
|
|
1400
1701
|
const indent = ' '.repeat(indentLevel);
|
|
1401
1702
|
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1703
|
+
// Get SDK adapter from session for SDK mode output
|
|
1704
|
+
let sdkOutputAdapter: any = null;
|
|
1705
|
+
let isSdkMode = false;
|
|
1706
|
+
try {
|
|
1707
|
+
const { getSingletonSession } = await import('./session.js');
|
|
1708
|
+
const session = getSingletonSession();
|
|
1709
|
+
if (session) {
|
|
1710
|
+
isSdkMode = (session as any).isSdkMode;
|
|
1711
|
+
sdkOutputAdapter = (session as any).sdkOutputAdapter;
|
|
1712
|
+
}
|
|
1713
|
+
} catch {
|
|
1714
|
+
// Session not available
|
|
1715
|
+
}
|
|
1405
1716
|
|
|
1406
|
-
//
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1717
|
+
// SDK mode: use adapter output (guiAgent.run() handles SDK output internally)
|
|
1718
|
+
// Only output console messages in non-SDK mode
|
|
1719
|
+
if (!isSdkMode) {
|
|
1720
|
+
console.log(`${indent}${colors.primaryBright(`${icons.robot} GUI Agent`)}: ${description}`);
|
|
1721
|
+
console.log(
|
|
1722
|
+
`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`
|
|
1723
|
+
);
|
|
1724
|
+
console.log('');
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
// Get VLM configuration for local mode
|
|
1728
|
+
// NOTE: guiSubagentBaseUrl must be explicitly configured, NOT fallback to baseUrl
|
|
1729
|
+
const baseUrl = config.get('guiSubagentBaseUrl') || '';
|
|
1730
|
+
const apiKey = config.get('guiSubagentApiKey') || '';
|
|
1731
|
+
const modelName = config.get('guiSubagentModel') || '';
|
|
1410
1732
|
|
|
1411
1733
|
// Determine mode: remote if remoteAIClient exists, otherwise local
|
|
1412
1734
|
const isRemoteMode = !!remoteAIClient;
|
|
1413
1735
|
|
|
1414
1736
|
// Log mode information
|
|
1415
1737
|
if (isRemoteMode) {
|
|
1416
|
-
|
|
1738
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1739
|
+
// SDK mode: use adapter output
|
|
1740
|
+
sdkOutputAdapter.outputInfo('Using remote VLM service');
|
|
1741
|
+
} else {
|
|
1742
|
+
// Normal mode: console output
|
|
1743
|
+
console.log(`${indent}${colors.info(`${icons.brain} Using remote VLM service`)}`);
|
|
1744
|
+
}
|
|
1417
1745
|
} else {
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1746
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1747
|
+
// SDK mode: use adapter output
|
|
1748
|
+
sdkOutputAdapter.outputInfo('Using local VLM configuration');
|
|
1749
|
+
} else {
|
|
1750
|
+
// Normal mode: console output
|
|
1751
|
+
console.log(`${indent}${colors.info(`${icons.brain} Using local VLM configuration`)}`);
|
|
1752
|
+
// Local mode requires explicit VLM configuration
|
|
1753
|
+
if (!baseUrl || !apiKey || !modelName) {
|
|
1754
|
+
return {
|
|
1755
|
+
success: false,
|
|
1756
|
+
message: `GUI task "${description}" failed: VLM not configured. Please run /model to configure Vision-Language Model first.`,
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
console.log(`${indent}${colors.textMuted(` Model: ${modelName}`)}`);
|
|
1760
|
+
console.log(`${indent}${colors.textMuted(` Base URL: ${baseUrl}`)}`);
|
|
1425
1761
|
}
|
|
1426
|
-
console.log(`${indent}${colors.textMuted(` Model: ${modelName}`)}`);
|
|
1427
|
-
console.log(`${indent}${colors.textMuted(` Base URL: ${baseUrl}`)}`);
|
|
1428
1762
|
}
|
|
1429
1763
|
console.log('');
|
|
1430
1764
|
|
|
@@ -1435,13 +1769,21 @@ export class TaskTool implements Tool {
|
|
|
1435
1769
|
const { getSingletonSession } = await import('./session.js');
|
|
1436
1770
|
const session = getSingletonSession();
|
|
1437
1771
|
taskId = session?.getTaskId() || null;
|
|
1438
|
-
} catch
|
|
1772
|
+
} catch {
|
|
1439
1773
|
taskId = null;
|
|
1440
1774
|
}
|
|
1441
1775
|
}
|
|
1442
1776
|
|
|
1777
|
+
// Track first VLM call for proper status management
|
|
1778
|
+
const isFirstVlmCallRef = { current: true };
|
|
1779
|
+
|
|
1443
1780
|
// Create remoteVlmCaller using the unified method (handles both local and remote modes)
|
|
1444
|
-
const remoteVlmCaller = this.createRemoteVlmCaller(
|
|
1781
|
+
const remoteVlmCaller = this.createRemoteVlmCaller(
|
|
1782
|
+
remoteAIClient,
|
|
1783
|
+
taskId,
|
|
1784
|
+
{ baseUrl, apiKey, modelName },
|
|
1785
|
+
isFirstVlmCallRef
|
|
1786
|
+
);
|
|
1445
1787
|
|
|
1446
1788
|
// Set up stdin polling for ESC cancellation
|
|
1447
1789
|
let rawModeEnabled = false;
|
|
@@ -1450,14 +1792,16 @@ export class TaskTool implements Tool {
|
|
|
1450
1792
|
const logger = getLogger();
|
|
1451
1793
|
|
|
1452
1794
|
const setupStdinPolling = () => {
|
|
1795
|
+
logger.debug(`[GUIAgent ESC] setupStdinPolling called, process.stdin.isTTY: ${process.stdin.isTTY}`);
|
|
1453
1796
|
if (process.stdin.isTTY) {
|
|
1454
1797
|
try {
|
|
1455
1798
|
process.stdin.setRawMode(true);
|
|
1456
1799
|
rawModeEnabled = true;
|
|
1457
1800
|
process.stdin.resume();
|
|
1458
1801
|
readline.emitKeypressEvents(process.stdin);
|
|
1459
|
-
|
|
1460
|
-
|
|
1802
|
+
logger.debug(`[GUIAgent ESC] Raw mode enabled successfully`);
|
|
1803
|
+
} catch (e: any) {
|
|
1804
|
+
logger.debug(`[GUIAgent ESC] Could not set raw mode: ${e.message}`);
|
|
1461
1805
|
}
|
|
1462
1806
|
|
|
1463
1807
|
stdinPollingInterval = setInterval(() => {
|
|
@@ -1466,13 +1810,20 @@ export class TaskTool implements Tool {
|
|
|
1466
1810
|
const chunk = process.stdin.read(1);
|
|
1467
1811
|
if (chunk && chunk.length > 0) {
|
|
1468
1812
|
const code = chunk[0];
|
|
1469
|
-
if (code ===
|
|
1470
|
-
|
|
1813
|
+
if (code === 0x1b) {
|
|
1814
|
+
// ESC
|
|
1815
|
+
logger.debug('[GUIAgent ESC Polling] ESC detected! Code: 0x1b');
|
|
1471
1816
|
cancellationManager.cancel();
|
|
1817
|
+
} else {
|
|
1818
|
+
// Log other key codes for debugging
|
|
1819
|
+
logger.debug(`[GUIAgent ESC Polling] Key code: 0x${code.toString(16)}`);
|
|
1472
1820
|
}
|
|
1473
1821
|
}
|
|
1822
|
+
} else {
|
|
1823
|
+
logger.debug('[GUIAgent ESC Polling] rawModeEnabled is false');
|
|
1474
1824
|
}
|
|
1475
|
-
} catch (e) {
|
|
1825
|
+
} catch (e: any) {
|
|
1826
|
+
logger.debug(`[GUIAgent ESC Polling] Error: ${e.message}`);
|
|
1476
1827
|
// Ignore polling errors
|
|
1477
1828
|
}
|
|
1478
1829
|
}, 10);
|
|
@@ -1494,7 +1845,9 @@ export class TaskTool implements Tool {
|
|
|
1494
1845
|
cancellationManager.on('cancelled', cancelHandler);
|
|
1495
1846
|
|
|
1496
1847
|
// Start polling for ESC
|
|
1848
|
+
logger.debug(`[GUIAgent ESC] About to call setupStdinPolling`);
|
|
1497
1849
|
setupStdinPolling();
|
|
1850
|
+
logger.debug(`[GUIAgent ESC] setupStdinPolling called`);
|
|
1498
1851
|
|
|
1499
1852
|
try {
|
|
1500
1853
|
// Import and create GUIAgent
|
|
@@ -1504,11 +1857,15 @@ export class TaskTool implements Tool {
|
|
|
1504
1857
|
model: !isRemoteMode ? modelName : undefined,
|
|
1505
1858
|
modelBaseUrl: !isRemoteMode ? baseUrl : undefined,
|
|
1506
1859
|
modelApiKey: !isRemoteMode ? apiKey : undefined,
|
|
1860
|
+
taskId: taskId || undefined,
|
|
1861
|
+
isFirstVlmCallRef,
|
|
1507
1862
|
remoteVlmCaller,
|
|
1508
1863
|
isLocalMode: !isRemoteMode,
|
|
1509
|
-
maxLoopCount:
|
|
1864
|
+
maxLoopCount: 100,
|
|
1510
1865
|
loopIntervalInMs: 500,
|
|
1511
1866
|
showAIDebugInfo: config.get('showAIDebugInfo') || false,
|
|
1867
|
+
indentLevel: indentLevel,
|
|
1868
|
+
sdkOutputAdapter: isSdkMode ? sdkOutputAdapter : null,
|
|
1512
1869
|
});
|
|
1513
1870
|
|
|
1514
1871
|
// Add constraints to prompt if any
|
|
@@ -1528,9 +1885,9 @@ export class TaskTool implements Tool {
|
|
|
1528
1885
|
process.stdout.write('\n');
|
|
1529
1886
|
return {
|
|
1530
1887
|
success: true,
|
|
1531
|
-
cancelled: true,
|
|
1888
|
+
cancelled: true, // Mark as cancelled so main agent won't continue
|
|
1532
1889
|
message: `GUI task "${description}" cancelled by user`,
|
|
1533
|
-
result: 'Task cancelled'
|
|
1890
|
+
result: 'Task cancelled',
|
|
1534
1891
|
};
|
|
1535
1892
|
}
|
|
1536
1893
|
|
|
@@ -1541,26 +1898,106 @@ export class TaskTool implements Tool {
|
|
|
1541
1898
|
process.stdout.write('\n');
|
|
1542
1899
|
|
|
1543
1900
|
// Return result based on GUIAgent status
|
|
1901
|
+
// Always return all info except screenshots (base64) to avoid huge payload
|
|
1902
|
+
const conversationsWithoutScreenshots = result.conversations.map((conv: any) => ({
|
|
1903
|
+
...conv,
|
|
1904
|
+
screenshotBase64: undefined, // Remove screenshots to avoid huge payload
|
|
1905
|
+
}));
|
|
1906
|
+
|
|
1544
1907
|
if (result.status === 'end') {
|
|
1545
|
-
const iterations =
|
|
1546
|
-
|
|
1908
|
+
const iterations = conversationsWithoutScreenshots.filter(
|
|
1909
|
+
(c: any) => c.from === 'human' && c.screenshotContext
|
|
1910
|
+
).length;
|
|
1911
|
+
// SDK mode: use adapter output
|
|
1912
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1913
|
+
sdkOutputAdapter.outputGUIAgentComplete(description, iterations);
|
|
1914
|
+
} else {
|
|
1915
|
+
// Normal mode: console output
|
|
1916
|
+
console.log(
|
|
1917
|
+
`${indent}${colors.success(`${icons.check} GUI task completed in ${iterations} iterations`)}`
|
|
1918
|
+
);
|
|
1919
|
+
}
|
|
1547
1920
|
return {
|
|
1548
1921
|
success: true,
|
|
1549
1922
|
message: `GUI task "${description}" completed`,
|
|
1550
|
-
result:
|
|
1923
|
+
result: {
|
|
1924
|
+
status: result.status,
|
|
1925
|
+
iterations,
|
|
1926
|
+
actions: conversationsWithoutScreenshots
|
|
1927
|
+
.filter((c: any) => c.from === 'assistant' && c.actionType)
|
|
1928
|
+
.map((c: any) => c.actionType),
|
|
1929
|
+
conversations: conversationsWithoutScreenshots,
|
|
1930
|
+
error: result.error,
|
|
1931
|
+
},
|
|
1932
|
+
};
|
|
1933
|
+
} else if (result.status === 'call_llm') {
|
|
1934
|
+
// SDK mode: use adapter output
|
|
1935
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1936
|
+
sdkOutputAdapter.outputGUIAgentStatus('call_llm', conversationsWithoutScreenshots.filter(
|
|
1937
|
+
(c: any) => c.from === 'human' && c.screenshotContext
|
|
1938
|
+
).length);
|
|
1939
|
+
} else {
|
|
1940
|
+
// Normal mode: console output
|
|
1941
|
+
console.log(
|
|
1942
|
+
`${indent}${colors.warning(`${icons.warning} GUI agent returned to main agent for LLM decision`)}`
|
|
1943
|
+
);
|
|
1944
|
+
}
|
|
1945
|
+
return {
|
|
1946
|
+
success: true,
|
|
1947
|
+
message: `GUI task "${description}" returned for LLM decision`,
|
|
1948
|
+
result: {
|
|
1949
|
+
status: result.status,
|
|
1950
|
+
iterations: conversationsWithoutScreenshots.filter(
|
|
1951
|
+
(c: any) => c.from === 'human' && c.screenshotContext
|
|
1952
|
+
).length,
|
|
1953
|
+
actions: conversationsWithoutScreenshots
|
|
1954
|
+
.filter((c: any) => c.from === 'assistant' && c.actionType)
|
|
1955
|
+
.map((c: any) => c.actionType),
|
|
1956
|
+
conversations: conversationsWithoutScreenshots,
|
|
1957
|
+
error: result.error,
|
|
1958
|
+
},
|
|
1551
1959
|
};
|
|
1552
1960
|
} else if (result.status === 'user_stopped') {
|
|
1961
|
+
// SDK mode: use adapter output
|
|
1962
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1963
|
+
sdkOutputAdapter.outputGUIAgentCancelled(description);
|
|
1964
|
+
}
|
|
1553
1965
|
return {
|
|
1554
1966
|
success: true,
|
|
1555
1967
|
message: `GUI task "${description}" stopped by user`,
|
|
1556
|
-
result:
|
|
1968
|
+
result: {
|
|
1969
|
+
status: result.status,
|
|
1970
|
+
iterations: conversationsWithoutScreenshots.filter(
|
|
1971
|
+
(c: any) => c.from === 'human' && c.screenshotContext
|
|
1972
|
+
).length,
|
|
1973
|
+
actions: conversationsWithoutScreenshots
|
|
1974
|
+
.filter((c: any) => c.from === 'assistant' && c.actionType)
|
|
1975
|
+
.map((c: any) => c.actionType),
|
|
1976
|
+
conversations: conversationsWithoutScreenshots,
|
|
1977
|
+
stopped: true,
|
|
1978
|
+
},
|
|
1557
1979
|
};
|
|
1558
1980
|
} else {
|
|
1559
1981
|
// status is 'error' or other non-success status
|
|
1560
1982
|
const errorMsg = result.error || 'Unknown error';
|
|
1983
|
+
// SDK mode: use adapter output
|
|
1984
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
1985
|
+
sdkOutputAdapter.outputGUIAgentError(description, errorMsg);
|
|
1986
|
+
}
|
|
1561
1987
|
return {
|
|
1562
1988
|
success: false,
|
|
1563
|
-
message: `GUI task "${description}" failed: ${errorMsg}
|
|
1989
|
+
message: `GUI task "${description}" failed: ${errorMsg}`,
|
|
1990
|
+
result: {
|
|
1991
|
+
status: result.status,
|
|
1992
|
+
iterations: conversationsWithoutScreenshots.filter(
|
|
1993
|
+
(c: any) => c.from === 'human' && c.screenshotContext
|
|
1994
|
+
).length,
|
|
1995
|
+
actions: conversationsWithoutScreenshots
|
|
1996
|
+
.filter((c: any) => c.from === 'assistant' && c.actionType)
|
|
1997
|
+
.map((c: any) => c.actionType),
|
|
1998
|
+
conversations: conversationsWithoutScreenshots,
|
|
1999
|
+
error: result.error,
|
|
2000
|
+
},
|
|
1564
2001
|
};
|
|
1565
2002
|
}
|
|
1566
2003
|
} catch (error: any) {
|
|
@@ -1573,26 +2010,38 @@ export class TaskTool implements Tool {
|
|
|
1573
2010
|
// If the user cancelled the task, ignore any API errors (like 429)
|
|
1574
2011
|
// and return cancelled status instead
|
|
1575
2012
|
if (cancelled || cancellationManager.isOperationCancelled()) {
|
|
2013
|
+
// SDK mode: use adapter output
|
|
2014
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2015
|
+
sdkOutputAdapter.outputGUIAgentCancelled(description);
|
|
2016
|
+
}
|
|
1576
2017
|
return {
|
|
1577
2018
|
success: true,
|
|
1578
|
-
cancelled: true,
|
|
2019
|
+
cancelled: true, // Mark as cancelled so main agent won't continue
|
|
1579
2020
|
message: `GUI task "${description}" cancelled by user`,
|
|
1580
|
-
result: 'Task cancelled'
|
|
2021
|
+
result: 'Task cancelled',
|
|
1581
2022
|
};
|
|
1582
2023
|
}
|
|
1583
2024
|
|
|
1584
2025
|
if (error.message === 'Operation cancelled by user') {
|
|
2026
|
+
// SDK mode: use adapter output
|
|
2027
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2028
|
+
sdkOutputAdapter.outputGUIAgentCancelled(description);
|
|
2029
|
+
}
|
|
1585
2030
|
return {
|
|
1586
2031
|
success: true,
|
|
1587
2032
|
message: `GUI task "${description}" cancelled by user`,
|
|
1588
|
-
result: 'Task cancelled'
|
|
2033
|
+
result: 'Task cancelled',
|
|
1589
2034
|
};
|
|
1590
2035
|
}
|
|
1591
2036
|
|
|
1592
2037
|
// Return failure without throwing - let the main agent handle it
|
|
2038
|
+
// SDK mode: use adapter output
|
|
2039
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2040
|
+
sdkOutputAdapter.outputGUIAgentError(description, error.message);
|
|
2041
|
+
}
|
|
1593
2042
|
return {
|
|
1594
2043
|
success: false,
|
|
1595
|
-
message: `GUI task "${description}" failed: ${error.message}
|
|
2044
|
+
message: `GUI task "${description}" failed: ${error.message}`,
|
|
1596
2045
|
};
|
|
1597
2046
|
}
|
|
1598
2047
|
}
|
|
@@ -1606,7 +2055,6 @@ export class TaskTool implements Tool {
|
|
|
1606
2055
|
mode: ExecutionMode,
|
|
1607
2056
|
agentManager: any,
|
|
1608
2057
|
toolRegistry: any,
|
|
1609
|
-
aiClient: any,
|
|
1610
2058
|
config: any,
|
|
1611
2059
|
indentLevel: number = 1
|
|
1612
2060
|
): Promise<{ success: boolean; message: string; result?: any }> {
|
|
@@ -1616,6 +2064,20 @@ export class TaskTool implements Tool {
|
|
|
1616
2064
|
throw new Error(`Agent ${subagent_type} not found`);
|
|
1617
2065
|
}
|
|
1618
2066
|
|
|
2067
|
+
// Get SDK adapter from session for subagent output
|
|
2068
|
+
let sdkOutputAdapter: any = null;
|
|
2069
|
+
let isSdkMode = false;
|
|
2070
|
+
try {
|
|
2071
|
+
const { getSingletonSession } = await import('./session.js');
|
|
2072
|
+
const session = getSingletonSession();
|
|
2073
|
+
if (session) {
|
|
2074
|
+
isSdkMode = (session as any).isSdkMode;
|
|
2075
|
+
sdkOutputAdapter = (session as any).sdkOutputAdapter;
|
|
2076
|
+
}
|
|
2077
|
+
} catch {
|
|
2078
|
+
// Session not available
|
|
2079
|
+
}
|
|
2080
|
+
|
|
1619
2081
|
// Special handling for gui-subagent: directly call GUIAgent.run() instead of subagent message loop
|
|
1620
2082
|
if (subagent_type === 'gui-subagent') {
|
|
1621
2083
|
// Get RemoteAIClient instance from session (if available)
|
|
@@ -1626,7 +2088,7 @@ export class TaskTool implements Tool {
|
|
|
1626
2088
|
if (session) {
|
|
1627
2089
|
remoteAIClient = session.getRemoteAIClient();
|
|
1628
2090
|
}
|
|
1629
|
-
} catch
|
|
2091
|
+
} catch {
|
|
1630
2092
|
// Session not available, keep undefined
|
|
1631
2093
|
remoteAIClient = undefined;
|
|
1632
2094
|
}
|
|
@@ -1646,7 +2108,7 @@ export class TaskTool implements Tool {
|
|
|
1646
2108
|
let modelName = config.get('modelName') || 'Qwen3-Coder';
|
|
1647
2109
|
let baseUrl = config.get('baseUrl') || 'https://apis.xagent.cn/v1';
|
|
1648
2110
|
let apiKey = config.get('apiKey') || '';
|
|
1649
|
-
|
|
2111
|
+
|
|
1650
2112
|
if (agent.model) {
|
|
1651
2113
|
// If agent has a model field, it can be a model name or a config reference like 'guiSubagentModel'
|
|
1652
2114
|
if (typeof agent.model === 'string' && agent.model.endsWith('Model')) {
|
|
@@ -1666,41 +2128,71 @@ export class TaskTool implements Tool {
|
|
|
1666
2128
|
}
|
|
1667
2129
|
}
|
|
1668
2130
|
|
|
1669
|
-
// Create
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
2131
|
+
// Create AI client for this subagent - each subagent gets its own independent client
|
|
2132
|
+
let subAgentClient;
|
|
2133
|
+
let isRemoteMode = false;
|
|
2134
|
+
let mainTaskId: string | null = null;
|
|
2135
|
+
const authConfig = config.getAuthConfig();
|
|
2136
|
+
|
|
2137
|
+
if (authConfig.type === AuthType.OAUTH_XAGENT) {
|
|
2138
|
+
// Remote mode: create independent RemoteAIClient for each subagent
|
|
2139
|
+
// This prevents message queue conflicts when multiple subagents run in parallel
|
|
2140
|
+
const session = getSingletonSession();
|
|
2141
|
+
const remoteAIClient = session?.getRemoteAIClient();
|
|
2142
|
+
|
|
2143
|
+
if (remoteAIClient) {
|
|
2144
|
+
// Clone or create independent client for this subagent
|
|
2145
|
+
// RemoteAIClient should be designed to handle concurrent requests
|
|
2146
|
+
subAgentClient = remoteAIClient;
|
|
2147
|
+
isRemoteMode = true;
|
|
2148
|
+
mainTaskId = session?.getTaskId() || null;
|
|
2149
|
+
} else {
|
|
2150
|
+
subAgentClient = createAIClient(authConfig);
|
|
2151
|
+
}
|
|
2152
|
+
} else {
|
|
2153
|
+
// Local mode: create client with subagent-specific model config
|
|
2154
|
+
const subAuthConfig = {
|
|
2155
|
+
...authConfig,
|
|
2156
|
+
type: AuthType.OPENAI_COMPATIBLE,
|
|
2157
|
+
apiKey: apiKey,
|
|
2158
|
+
baseUrl: baseUrl,
|
|
2159
|
+
modelName: modelName,
|
|
2160
|
+
showAIDebugInfo: config.get('showAIDebugInfo') || false,
|
|
2161
|
+
};
|
|
2162
|
+
subAgentClient = createAIClient(subAuthConfig);
|
|
2163
|
+
}
|
|
2164
|
+
|
|
1679
2165
|
const indent = ' '.repeat(indentLevel);
|
|
1680
|
-
const
|
|
2166
|
+
const _indentNext = ' '.repeat(indentLevel + 1);
|
|
1681
2167
|
const agentName = agent.name || subagent_type;
|
|
1682
2168
|
|
|
1683
2169
|
// Track execution history for better reporting to main agent
|
|
1684
2170
|
const executionHistory: Array<{
|
|
1685
2171
|
tool: string;
|
|
1686
2172
|
status: 'success' | 'error';
|
|
1687
|
-
params: any;
|
|
1688
|
-
result?: any;
|
|
1689
|
-
error?: string;
|
|
2173
|
+
params: any; // 工具调用参数
|
|
2174
|
+
result?: any; // 工具执行结果(成功时)
|
|
2175
|
+
error?: string; // 错误信息(失败时)
|
|
1690
2176
|
timestamp: string;
|
|
1691
2177
|
}> = [];
|
|
1692
2178
|
|
|
1693
2179
|
// Helper function to indent multi-line content
|
|
1694
2180
|
const indentMultiline = (content: string, baseIndent: string): string => {
|
|
1695
|
-
return content
|
|
2181
|
+
return content
|
|
2182
|
+
.split('\n')
|
|
2183
|
+
.map((line) => `${baseIndent} ${line}`)
|
|
2184
|
+
.join('\n');
|
|
1696
2185
|
};
|
|
1697
|
-
|
|
2186
|
+
|
|
1698
2187
|
const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, mode, agent);
|
|
1699
|
-
const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
2188
|
+
const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(
|
|
2189
|
+
agent.systemPrompt
|
|
2190
|
+
);
|
|
2191
|
+
|
|
2192
|
+
const fullPrompt =
|
|
2193
|
+
constraints.length > 0
|
|
2194
|
+
? `${prompt}\n\nConstraints:\n${constraints.map((c) => `- ${c}`).join('\n')}`
|
|
2195
|
+
: prompt;
|
|
1704
2196
|
|
|
1705
2197
|
// Set up raw mode and stdin polling for ESC detection
|
|
1706
2198
|
const cancellationManager = getCancellationManager();
|
|
@@ -1728,13 +2220,14 @@ export class TaskTool implements Tool {
|
|
|
1728
2220
|
const chunk = process.stdin.read(1);
|
|
1729
2221
|
if (chunk && chunk.length > 0) {
|
|
1730
2222
|
const code = chunk[0];
|
|
1731
|
-
if (code ===
|
|
2223
|
+
if (code === 0x1b) {
|
|
2224
|
+
// ESC
|
|
1732
2225
|
logger.debug('[TaskTool] ESC detected via polling!');
|
|
1733
2226
|
cancellationManager.cancel();
|
|
1734
2227
|
}
|
|
1735
2228
|
}
|
|
1736
2229
|
}
|
|
1737
|
-
} catch
|
|
2230
|
+
} catch {
|
|
1738
2231
|
// Ignore polling errors
|
|
1739
2232
|
}
|
|
1740
2233
|
}, 10);
|
|
@@ -1765,15 +2258,15 @@ export class TaskTool implements Tool {
|
|
|
1765
2258
|
throw new Error('Operation cancelled by user');
|
|
1766
2259
|
}
|
|
1767
2260
|
};
|
|
1768
|
-
|
|
1769
|
-
|
|
2261
|
+
|
|
2262
|
+
const messages: Message[] = [
|
|
1770
2263
|
{ role: 'system', content: enhancedSystemPrompt },
|
|
1771
|
-
{ role: 'user', content: fullPrompt }
|
|
2264
|
+
{ role: 'user', content: fullPrompt },
|
|
1772
2265
|
];
|
|
1773
|
-
|
|
2266
|
+
|
|
1774
2267
|
const availableTools = agentManager.getAvailableToolsForAgent(agent, mode);
|
|
1775
2268
|
const allToolDefinitions = toolRegistry.getToolDefinitions();
|
|
1776
|
-
|
|
2269
|
+
|
|
1777
2270
|
const toolDefinitions: ToolDefinition[] = availableTools.map((toolName: string) => {
|
|
1778
2271
|
const fullDef = allToolDefinitions.find((def: any) => def.function.name === toolName);
|
|
1779
2272
|
if (fullDef) {
|
|
@@ -1784,28 +2277,43 @@ export class TaskTool implements Tool {
|
|
|
1784
2277
|
function: {
|
|
1785
2278
|
name: toolName,
|
|
1786
2279
|
description: `Tool: ${toolName}`,
|
|
1787
|
-
parameters: { type: 'object', properties: {}, required: [] }
|
|
1788
|
-
}
|
|
2280
|
+
parameters: { type: 'object', properties: {}, required: [] },
|
|
2281
|
+
},
|
|
1789
2282
|
};
|
|
1790
2283
|
});
|
|
1791
2284
|
|
|
1792
2285
|
let iteration = 0;
|
|
1793
|
-
|
|
2286
|
+
let lastContentStr = ''; // Track last content for final result
|
|
1794
2287
|
|
|
1795
|
-
|
|
2288
|
+
// Main agent style loop: continue until AI returns no more tool_calls
|
|
2289
|
+
// eslint-disable-next-line no-constant-condition
|
|
2290
|
+
while (true) {
|
|
1796
2291
|
iteration++;
|
|
1797
|
-
|
|
2292
|
+
|
|
1798
2293
|
// Check for cancellation before each iteration
|
|
1799
2294
|
checkCancellation();
|
|
1800
|
-
|
|
2295
|
+
|
|
2296
|
+
// Prepare chat options with taskId and model names for remote mode
|
|
2297
|
+
const chatOptions: any = {
|
|
2298
|
+
tools: toolDefinitions,
|
|
2299
|
+
temperature: 0.7,
|
|
2300
|
+
};
|
|
2301
|
+
|
|
2302
|
+
// Pass taskId, status, and model names for remote mode subagent calls
|
|
2303
|
+
// Subagent shares the same taskId as the main task
|
|
2304
|
+
if (isRemoteMode && mainTaskId) {
|
|
2305
|
+
chatOptions.taskId = mainTaskId;
|
|
2306
|
+
chatOptions.status = iteration === 1 ? 'begin' : 'continue';
|
|
2307
|
+
// Pass model names to ensure subagent uses the same models as main task
|
|
2308
|
+
chatOptions.llmModelName = config.get('remote_llmModelName');
|
|
2309
|
+
chatOptions.vlmModelName = config.get('remote_vlmModelName');
|
|
2310
|
+
}
|
|
2311
|
+
|
|
1801
2312
|
// Use withCancellation to make API call cancellable
|
|
1802
|
-
const result = await cancellationManager.withCancellation(
|
|
1803
|
-
subAgentClient.chatCompletion(messages,
|
|
1804
|
-
tools: toolDefinitions,
|
|
1805
|
-
temperature: 0.7
|
|
1806
|
-
}),
|
|
2313
|
+
const result = (await cancellationManager.withCancellation(
|
|
2314
|
+
subAgentClient.chatCompletion(messages, chatOptions),
|
|
1807
2315
|
`api-${subagent_type}-${iteration}`
|
|
1808
|
-
) as any;
|
|
2316
|
+
)) as any;
|
|
1809
2317
|
|
|
1810
2318
|
// Check for cancellation after API call
|
|
1811
2319
|
checkCancellation();
|
|
@@ -1827,8 +2335,8 @@ export class TaskTool implements Tool {
|
|
|
1827
2335
|
hasValidContent = messageContent.trim() !== '';
|
|
1828
2336
|
} else if (Array.isArray(messageContent)) {
|
|
1829
2337
|
const textParts = messageContent
|
|
1830
|
-
.filter(item => typeof item?.text === 'string' && item.text.trim() !== '')
|
|
1831
|
-
.map(item => item.text);
|
|
2338
|
+
.filter((item) => typeof item?.text === 'string' && item.text.trim() !== '')
|
|
2339
|
+
.map((item) => item.text);
|
|
1832
2340
|
contentStr = textParts.join('');
|
|
1833
2341
|
hasValidContent = textParts.length > 0;
|
|
1834
2342
|
} else {
|
|
@@ -1846,143 +2354,87 @@ export class TaskTool implements Tool {
|
|
|
1846
2354
|
throw new Error(`Sub-agent ${subagent_type} response truncated due to length limits`);
|
|
1847
2355
|
}
|
|
1848
2356
|
|
|
1849
|
-
// Add assistant message to conversation
|
|
1850
|
-
|
|
2357
|
+
// Add assistant message to conversation (必须包含 tool_calls,否则 tool_result 无法匹配)
|
|
2358
|
+
const assistantMessage: any = { role: 'assistant', content: contentStr };
|
|
2359
|
+
if (toolCalls && toolCalls.length > 0) {
|
|
2360
|
+
assistantMessage.tool_calls = toolCalls;
|
|
2361
|
+
}
|
|
2362
|
+
if (reasoningContent) {
|
|
2363
|
+
assistantMessage.reasoning_content = reasoningContent;
|
|
2364
|
+
}
|
|
2365
|
+
messages.push(assistantMessage as Message);
|
|
1851
2366
|
|
|
1852
2367
|
// Display reasoning content if present
|
|
1853
2368
|
if (reasoningContent) {
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
2369
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2370
|
+
sdkOutputAdapter.outputThinking(reasoningContent, 'compact');
|
|
2371
|
+
} else {
|
|
2372
|
+
console.log(`\n${indent}${colors.textDim(`${icons.brain} Thinking Process:`)}`);
|
|
2373
|
+
const truncatedReasoning =
|
|
2374
|
+
reasoningContent.length > 500
|
|
2375
|
+
? reasoningContent.substring(0, 500) + '...'
|
|
2376
|
+
: reasoningContent;
|
|
2377
|
+
const indentedReasoning = indentMultiline(truncatedReasoning, indent);
|
|
2378
|
+
console.log(`${indentedReasoning}\n`);
|
|
2379
|
+
}
|
|
1858
2380
|
}
|
|
1859
2381
|
|
|
1860
2382
|
// Display assistant response (if there's any text content) with proper indentation
|
|
1861
2383
|
if (contentStr) {
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
2384
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2385
|
+
sdkOutputAdapter.outputAssistant(contentStr);
|
|
2386
|
+
} else {
|
|
2387
|
+
console.log(`\n${indent}${colors.primaryBright(agentName)}: ${description}`);
|
|
2388
|
+
const truncatedContent =
|
|
2389
|
+
contentStr.length > 500 ? contentStr.substring(0, 500) + '...' : contentStr;
|
|
2390
|
+
const indentedContent = indentMultiline(truncatedContent, indent);
|
|
2391
|
+
console.log(`${indentedContent}\n`);
|
|
2392
|
+
}
|
|
1866
2393
|
}
|
|
1867
2394
|
|
|
1868
|
-
// Process tool calls
|
|
2395
|
+
// Process tool calls in parallel (照搬 session 的实现)
|
|
1869
2396
|
if (toolCalls && toolCalls.length > 0) {
|
|
1870
|
-
|
|
2397
|
+
// Prepare all tool calls with their indices
|
|
2398
|
+
const preparedToolCalls = toolCalls.map((toolCall: any, index: number) => {
|
|
1871
2399
|
const { name, arguments: params } = toolCall.function;
|
|
1872
|
-
|
|
1873
2400
|
let parsedParams: any;
|
|
1874
2401
|
try {
|
|
1875
2402
|
parsedParams = typeof params === 'string' ? JSON.parse(params) : params;
|
|
1876
|
-
} catch
|
|
2403
|
+
} catch {
|
|
1877
2404
|
parsedParams = params;
|
|
1878
2405
|
}
|
|
2406
|
+
return { name, params: parsedParams, id: toolCall.id, index };
|
|
2407
|
+
});
|
|
1879
2408
|
|
|
1880
|
-
|
|
2409
|
+
// Display all tool call info first
|
|
2410
|
+
for (const tc of preparedToolCalls as Array<{ name: string; params: any; id: string }>) {
|
|
2411
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2412
|
+
sdkOutputAdapter.outputToolStart(tc.name, tc.params);
|
|
2413
|
+
} else {
|
|
2414
|
+
console.log(`${indent}${colors.textMuted(`${icons.loading} Tool: ${tc.name}`)}`);
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
1881
2417
|
|
|
2418
|
+
// Execute all tool calls in parallel
|
|
2419
|
+
const executePromises = preparedToolCalls.map(async (tc: { name: string; params: any; id: string; index: number }) => {
|
|
1882
2420
|
try {
|
|
1883
2421
|
// Check cancellation before tool execution
|
|
1884
2422
|
checkCancellation();
|
|
1885
|
-
|
|
2423
|
+
|
|
1886
2424
|
const toolResult: any = await cancellationManager.withCancellation(
|
|
1887
|
-
toolRegistry.execute(name,
|
|
1888
|
-
`subagent-${subagent_type}-${name}-${iteration}`
|
|
2425
|
+
toolRegistry.execute(tc.name, tc.params, mode, indent),
|
|
2426
|
+
`subagent-${subagent_type}-${tc.name}-${iteration}`
|
|
1889
2427
|
);
|
|
1890
|
-
|
|
1891
|
-
// Get showToolDetails config to control result display
|
|
1892
|
-
const showToolDetails = config.get('showToolDetails') || false;
|
|
1893
|
-
|
|
1894
|
-
// Prepare result preview for history
|
|
1895
|
-
const resultPreview = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult, null, 2);
|
|
1896
|
-
const truncatedPreview = resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
|
|
1897
|
-
|
|
1898
|
-
// Special handling for different tools (consistent with session.ts display logic)
|
|
1899
|
-
const isTodoTool = name === 'todo_write' || name === 'todo_read';
|
|
1900
|
-
const isEditTool = name === 'Edit';
|
|
1901
|
-
const isWriteTool = name === 'Write';
|
|
1902
|
-
const isDeleteTool = name === 'DeleteFile';
|
|
1903
|
-
const hasDiff = isEditTool && toolResult?.diff;
|
|
1904
|
-
const hasFilePreview = isWriteTool && toolResult?.preview;
|
|
1905
|
-
const hasDeleteInfo = isDeleteTool && toolResult?.filePath;
|
|
1906
|
-
|
|
1907
|
-
// Import render functions for consistent display
|
|
1908
|
-
const { renderDiff, renderLines } = await import('./theme.js');
|
|
1909
|
-
|
|
1910
|
-
if (isTodoTool) {
|
|
1911
|
-
// Display todo list
|
|
1912
|
-
console.log(`${indent}${colors.success(`${icons.check} Todo List:`)}`);
|
|
1913
|
-
const todos = toolResult?.todos || [];
|
|
1914
|
-
if (todos.length === 0) {
|
|
1915
|
-
console.log(`${indent} ${colors.textMuted('No tasks')}`);
|
|
1916
|
-
} else {
|
|
1917
|
-
const statusConfig: Record<string, { icon: string; color: (text: string) => string; label: string }> = {
|
|
1918
|
-
'pending': { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
|
|
1919
|
-
'in_progress': { icon: icons.loading, color: colors.warning, label: 'In Progress' },
|
|
1920
|
-
'completed': { icon: icons.success, color: colors.success, label: 'Completed' },
|
|
1921
|
-
'failed': { icon: icons.error, color: colors.error, label: 'Failed' }
|
|
1922
|
-
};
|
|
1923
|
-
for (const todo of todos) {
|
|
1924
|
-
const status = statusConfig[todo.status] || statusConfig['pending'];
|
|
1925
|
-
console.log(`${indent} ${status.color(status.icon)} ${status.color(status.label)}: ${colors.text(todo.task)}`);
|
|
1926
|
-
}
|
|
1927
|
-
}
|
|
1928
|
-
if (toolResult?.message) {
|
|
1929
|
-
console.log(`${indent}${colors.textDim(toolResult.message)}`);
|
|
1930
|
-
}
|
|
1931
|
-
console.log('');
|
|
1932
|
-
} else if (hasDiff) {
|
|
1933
|
-
// Display edit result with diff
|
|
1934
|
-
console.log('');
|
|
1935
|
-
const diffOutput = renderDiff(toolResult.diff);
|
|
1936
|
-
const indentedDiff = diffOutput.split('\n').map(line => `${indent} ${line}`).join('\n');
|
|
1937
|
-
console.log(`${indentedDiff}\n`);
|
|
1938
|
-
} else if (hasFilePreview) {
|
|
1939
|
-
// Display new file content in preview style
|
|
1940
|
-
console.log('');
|
|
1941
|
-
console.log(`${indent}${colors.success(`${icons.file} ${toolResult.filePath}`)}`);
|
|
1942
|
-
console.log(`${indent}${colors.textDim(` ${toolResult.lineCount} lines`)}`);
|
|
1943
|
-
console.log('');
|
|
1944
|
-
console.log(renderLines(toolResult.preview, { maxLines: 10, indent: indent + ' ' }));
|
|
1945
|
-
console.log('');
|
|
1946
|
-
} else if (hasDeleteInfo) {
|
|
1947
|
-
// Display DeleteFile result
|
|
1948
|
-
console.log('');
|
|
1949
|
-
console.log(`${indent}${colors.success(`${icons.check} Deleted: ${toolResult.filePath}`)}`);
|
|
1950
|
-
console.log('');
|
|
1951
|
-
} else if (showToolDetails) {
|
|
1952
|
-
// Show full result details
|
|
1953
|
-
const indentedPreview = indentMultiline(resultPreview, indent);
|
|
1954
|
-
console.log(`${indent}${colors.success(`${icons.check} Tool Result:`)}\n${indentedPreview}\n`);
|
|
1955
|
-
} else if (toolResult && toolResult.success === false) {
|
|
1956
|
-
// Tool failed
|
|
1957
|
-
console.log(`${indent}${colors.error(`${icons.cross} ${toolResult.message || 'Failed'}`)}\n`);
|
|
1958
|
-
} else if (toolResult) {
|
|
1959
|
-
// Show brief preview by default
|
|
1960
|
-
const indentedPreview = indentMultiline(truncatedPreview, indent);
|
|
1961
|
-
console.log(`${indent}${colors.success(`${icons.check} Completed`)}\n${indentedPreview}\n`);
|
|
1962
|
-
} else {
|
|
1963
|
-
console.log(`${indent}${colors.textDim('(no result)')}\n`);
|
|
1964
|
-
}
|
|
1965
|
-
|
|
1966
|
-
// Record successful tool execution in history (use truncated preview to save memory)
|
|
1967
|
-
executionHistory.push({
|
|
1968
|
-
tool: name,
|
|
1969
|
-
status: 'success',
|
|
1970
|
-
params: parsedParams,
|
|
1971
|
-
result: truncatedPreview,
|
|
1972
|
-
timestamp: new Date().toISOString()
|
|
1973
|
-
});
|
|
1974
|
-
|
|
1975
|
-
messages.push({
|
|
1976
|
-
role: 'tool',
|
|
1977
|
-
content: JSON.stringify(toolResult),
|
|
1978
|
-
tool_call_id: toolCall.id
|
|
1979
|
-
});
|
|
2428
|
+
return { ...tc, toolResult, error: undefined };
|
|
1980
2429
|
} catch (error: any) {
|
|
1981
2430
|
if (error.message === 'Operation cancelled by user') {
|
|
1982
|
-
|
|
2431
|
+
if (!isSdkMode || !sdkOutputAdapter) {
|
|
2432
|
+
console.log(`${indent}${colors.warning(`⚠️ Operation cancelled`)}\n`);
|
|
2433
|
+
}
|
|
1983
2434
|
cancellationManager.off('cancelled', cancelHandler);
|
|
1984
2435
|
cleanupStdinPolling();
|
|
1985
|
-
const summaryPreview =
|
|
2436
|
+
const summaryPreview =
|
|
2437
|
+
contentStr.length > 300 ? contentStr.substring(0, 300) + '...' : contentStr;
|
|
1986
2438
|
return {
|
|
1987
2439
|
success: false,
|
|
1988
2440
|
message: `Task "${description}" cancelled by user`,
|
|
@@ -1991,92 +2443,232 @@ export class TaskTool implements Tool {
|
|
|
1991
2443
|
executionHistory: {
|
|
1992
2444
|
totalIterations: iteration,
|
|
1993
2445
|
toolsExecuted: executionHistory.length,
|
|
1994
|
-
successfulTools: executionHistory.filter(t => t.status === 'success').length,
|
|
1995
|
-
failedTools: executionHistory.filter(t => t.status === 'error').length,
|
|
2446
|
+
successfulTools: executionHistory.filter((t) => t.status === 'success').length,
|
|
2447
|
+
failedTools: executionHistory.filter((t) => t.status === 'error').length,
|
|
1996
2448
|
history: executionHistory,
|
|
1997
|
-
cancelled: true
|
|
2449
|
+
cancelled: true,
|
|
2450
|
+
},
|
|
2451
|
+
},
|
|
2452
|
+
};
|
|
2453
|
+
}
|
|
2454
|
+
return { ...tc, toolResult: undefined, error: error.message };
|
|
2455
|
+
}
|
|
2456
|
+
});
|
|
2457
|
+
|
|
2458
|
+
const settledResults = await Promise.all(executePromises);
|
|
2459
|
+
|
|
2460
|
+
// Check for cancellation in results
|
|
2461
|
+
const cancellationResult = settledResults.find(
|
|
2462
|
+
(r): r is { success: boolean; message: string; result: any } =>
|
|
2463
|
+
'success' in r && r.success === false
|
|
2464
|
+
);
|
|
2465
|
+
if (cancellationResult) {
|
|
2466
|
+
return cancellationResult;
|
|
2467
|
+
}
|
|
2468
|
+
|
|
2469
|
+
// Create a map to store results by tool call index to maintain original order (match session implementation)
|
|
2470
|
+
type ToolResultType = { name: string; params: any; toolResult: any; error?: string; id: string; index: number };
|
|
2471
|
+
const resultsByIndex = new Map<number, ToolResultType>();
|
|
2472
|
+
const usedIndices = new Set<number>();
|
|
2473
|
+
|
|
2474
|
+
for (const result of settledResults as unknown as ToolResultType[]) {
|
|
2475
|
+
// Find the first unused original index that matches the tool name
|
|
2476
|
+
const originalIndex = preparedToolCalls.findIndex((tc: { name: string }, idx: number) =>
|
|
2477
|
+
tc.name === result.name && !usedIndices.has(idx)
|
|
2478
|
+
);
|
|
2479
|
+
if (originalIndex !== -1) {
|
|
2480
|
+
usedIndices.add(originalIndex);
|
|
2481
|
+
resultsByIndex.set(originalIndex, result);
|
|
2482
|
+
}
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
// Import render functions for consistent display
|
|
2486
|
+
const { renderDiff, renderLines } = await import('./theme.js');
|
|
2487
|
+
|
|
2488
|
+
// Process results in the original tool_calls order
|
|
2489
|
+
for (let i = 0; i < preparedToolCalls.length; i++) {
|
|
2490
|
+
const result = resultsByIndex.get(i);
|
|
2491
|
+
if (!result) continue;
|
|
2492
|
+
|
|
2493
|
+
const { name, params: parsedParams, toolResult, error } = result;
|
|
2494
|
+
|
|
2495
|
+
// Get showToolDetails config to control result display
|
|
2496
|
+
const showToolDetails = config.get('showToolDetails') || false;
|
|
2497
|
+
|
|
2498
|
+
// Prepare result preview for history
|
|
2499
|
+
const resultPreview =
|
|
2500
|
+
typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult, null, 2);
|
|
2501
|
+
const truncatedPreview =
|
|
2502
|
+
resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
|
|
2503
|
+
|
|
2504
|
+
if (error) {
|
|
2505
|
+
// Handle error case
|
|
2506
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2507
|
+
sdkOutputAdapter.outputToolError(name, error);
|
|
2508
|
+
} else {
|
|
2509
|
+
console.log(`${indent}${colors.error(`${icons.cross} Error:`)} ${error}\n`);
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
// Record failed tool execution in history
|
|
2513
|
+
executionHistory.push({
|
|
2514
|
+
tool: name,
|
|
2515
|
+
status: 'error',
|
|
2516
|
+
params: parsedParams,
|
|
2517
|
+
error,
|
|
2518
|
+
timestamp: new Date().toISOString(),
|
|
2519
|
+
});
|
|
2520
|
+
|
|
2521
|
+
messages.push({
|
|
2522
|
+
role: 'tool',
|
|
2523
|
+
content: JSON.stringify({ error }),
|
|
2524
|
+
tool_call_id: result.id,
|
|
2525
|
+
});
|
|
2526
|
+
} else {
|
|
2527
|
+
// Handle success case - display result
|
|
2528
|
+
// SDK mode: output tool result via adapter
|
|
2529
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
2530
|
+
sdkOutputAdapter.outputToolResult(name, toolResult);
|
|
2531
|
+
}
|
|
2532
|
+
// Normal mode: console output (SDK mode already output via adapter above)
|
|
2533
|
+
if (!isSdkMode || !sdkOutputAdapter) {
|
|
2534
|
+
// Special handling for different tools (consistent with session.ts display logic)
|
|
2535
|
+
const isTodoTool = name === 'todo_write' || name === 'todo_read';
|
|
2536
|
+
const isEditTool = name === 'Edit';
|
|
2537
|
+
const isWriteTool = name === 'Write';
|
|
2538
|
+
const isDeleteTool = name === 'DeleteFile';
|
|
2539
|
+
const hasDiff = isEditTool && toolResult?.diff;
|
|
2540
|
+
const hasFilePreview = isWriteTool && toolResult?.preview;
|
|
2541
|
+
const hasDeleteInfo = isDeleteTool && toolResult?.filePath;
|
|
2542
|
+
|
|
2543
|
+
if (isTodoTool) {
|
|
2544
|
+
// Display todo list
|
|
2545
|
+
console.log(`${indent}${colors.success(`${icons.check} Todo List:`)}`);
|
|
2546
|
+
const todos = toolResult?.todos || [];
|
|
2547
|
+
if (todos.length === 0) {
|
|
2548
|
+
console.log(`${indent} ${colors.textMuted('No tasks')}`);
|
|
2549
|
+
} else {
|
|
2550
|
+
const statusConfig: Record<
|
|
2551
|
+
string,
|
|
2552
|
+
{ icon: string; color: (text: string) => string; label: string }
|
|
2553
|
+
> = {
|
|
2554
|
+
pending: { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
|
|
2555
|
+
in_progress: { icon: icons.loading, color: colors.warning, label: 'In Progress' },
|
|
2556
|
+
completed: { icon: icons.success, color: colors.success, label: 'Completed' },
|
|
2557
|
+
failed: { icon: icons.error, color: colors.error, label: 'Failed' },
|
|
2558
|
+
};
|
|
2559
|
+
for (const todo of todos) {
|
|
2560
|
+
const status = statusConfig[todo.status] || statusConfig['pending'];
|
|
2561
|
+
console.log(
|
|
2562
|
+
`${indent} ${status.color(status.icon)} ${status.color(status.label)}: ${colors.text(todo.task)}`
|
|
2563
|
+
);
|
|
1998
2564
|
}
|
|
1999
2565
|
}
|
|
2000
|
-
|
|
2566
|
+
if (toolResult?.message) {
|
|
2567
|
+
console.log(`${indent}${colors.textDim(toolResult.message)}`);
|
|
2568
|
+
}
|
|
2569
|
+
console.log('');
|
|
2570
|
+
} else if (hasDiff) {
|
|
2571
|
+
// Display edit result with diff
|
|
2572
|
+
console.log('');
|
|
2573
|
+
const diffOutput = renderDiff(toolResult.diff);
|
|
2574
|
+
const indentedDiff = diffOutput
|
|
2575
|
+
.split('\n')
|
|
2576
|
+
.map((line) => `${indent} ${line}`)
|
|
2577
|
+
.join('\n');
|
|
2578
|
+
console.log(`${indentedDiff}\n`);
|
|
2579
|
+
} else if (hasFilePreview) {
|
|
2580
|
+
// Display new file content in preview style
|
|
2581
|
+
console.log('');
|
|
2582
|
+
console.log(`${indent}${colors.success(`${icons.file} ${toolResult.filePath}`)}`);
|
|
2583
|
+
console.log(`${indent}${colors.textDim(` ${toolResult.lineCount} lines`)}`);
|
|
2584
|
+
console.log('');
|
|
2585
|
+
console.log(renderLines(toolResult.preview, { maxLines: 10, indent: indent + ' ' }));
|
|
2586
|
+
console.log('');
|
|
2587
|
+
} else if (hasDeleteInfo) {
|
|
2588
|
+
// Display DeleteFile result
|
|
2589
|
+
console.log('');
|
|
2590
|
+
console.log(
|
|
2591
|
+
`${indent}${colors.success(`${icons.check} Deleted: ${toolResult.filePath}`)}`
|
|
2592
|
+
);
|
|
2593
|
+
console.log('');
|
|
2594
|
+
} else if (showToolDetails) {
|
|
2595
|
+
// Show full result details
|
|
2596
|
+
const indentedPreview = indentMultiline(resultPreview, indent);
|
|
2597
|
+
console.log(
|
|
2598
|
+
`${indent}${colors.success(`${icons.check} Tool Result:`)}\n${indentedPreview}\n`
|
|
2599
|
+
);
|
|
2600
|
+
} else if (toolResult && toolResult.success === false) {
|
|
2601
|
+
// Tool failed
|
|
2602
|
+
console.log(
|
|
2603
|
+
`${indent}${colors.error(`${icons.cross} ${toolResult.message || 'Failed'}`)}\n`
|
|
2604
|
+
);
|
|
2605
|
+
} else if (toolResult) {
|
|
2606
|
+
// Show brief preview by default
|
|
2607
|
+
const indentedPreview = indentMultiline(truncatedPreview, indent);
|
|
2608
|
+
console.log(
|
|
2609
|
+
`${indent}${colors.success(`${icons.check} Completed`)}\n${indentedPreview}\n`
|
|
2610
|
+
);
|
|
2611
|
+
} else {
|
|
2612
|
+
console.log(`${indent}${colors.textDim('(no result)')}\n`);
|
|
2613
|
+
}
|
|
2001
2614
|
}
|
|
2002
|
-
console.log(`${indent}${colors.error(`${icons.cross} Error:`)} ${error.message}\n`);
|
|
2003
2615
|
|
|
2004
|
-
// Record
|
|
2616
|
+
// Record successful tool execution in history (use truncated preview to save memory)
|
|
2005
2617
|
executionHistory.push({
|
|
2006
2618
|
tool: name,
|
|
2007
|
-
status: '
|
|
2619
|
+
status: 'success',
|
|
2008
2620
|
params: parsedParams,
|
|
2009
|
-
|
|
2010
|
-
timestamp: new Date().toISOString()
|
|
2621
|
+
result: truncatedPreview,
|
|
2622
|
+
timestamp: new Date().toISOString(),
|
|
2011
2623
|
});
|
|
2012
2624
|
|
|
2013
2625
|
messages.push({
|
|
2014
2626
|
role: 'tool',
|
|
2015
|
-
content: JSON.stringify(
|
|
2016
|
-
tool_call_id:
|
|
2627
|
+
content: JSON.stringify(toolResult),
|
|
2628
|
+
tool_call_id: result.id,
|
|
2017
2629
|
});
|
|
2018
2630
|
}
|
|
2019
2631
|
}
|
|
2020
|
-
|
|
2632
|
+
if (!isSdkMode || !sdkOutputAdapter) {
|
|
2633
|
+
console.log('');
|
|
2634
|
+
}
|
|
2021
2635
|
continue; // Continue to next iteration to get final response
|
|
2022
2636
|
}
|
|
2023
2637
|
|
|
2024
|
-
// No more tool calls
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
const summaryPreview = contentStr.length > 300 ? contentStr.substring(0, 300) + '...' : contentStr;
|
|
2029
|
-
return {
|
|
2030
|
-
success: true,
|
|
2031
|
-
message: `Task "${description}" completed by ${subagent_type}`,
|
|
2032
|
-
result: {
|
|
2033
|
-
summary: summaryPreview,
|
|
2034
|
-
executionHistory: {
|
|
2035
|
-
totalIterations: iteration,
|
|
2036
|
-
toolsExecuted: executionHistory.length,
|
|
2037
|
-
successfulTools: executionHistory.filter(t => t.status === 'success').length,
|
|
2038
|
-
failedTools: executionHistory.filter(t => t.status === 'error').length,
|
|
2039
|
-
history: executionHistory
|
|
2040
|
-
}
|
|
2041
|
-
}
|
|
2042
|
-
};
|
|
2638
|
+
// No more tool calls - break loop (same as main agent)
|
|
2639
|
+
lastContentStr = contentStr || '';
|
|
2640
|
+
break;
|
|
2043
2641
|
}
|
|
2044
2642
|
|
|
2045
|
-
//
|
|
2046
|
-
// Get the last assistant message content
|
|
2047
|
-
const lastAssistantMsg = messages.filter(m => m.role === 'assistant').pop();
|
|
2048
|
-
const lastContentStr = typeof lastAssistantMsg?.content === 'string'
|
|
2049
|
-
? lastAssistantMsg.content
|
|
2050
|
-
: JSON.stringify(lastAssistantMsg?.content || '');
|
|
2051
|
-
|
|
2643
|
+
// Loop ended - return result (same as main agent pattern)
|
|
2052
2644
|
cancellationManager.off('cancelled', cancelHandler);
|
|
2053
2645
|
cleanupStdinPolling();
|
|
2054
2646
|
|
|
2055
|
-
const summaryPreview =
|
|
2647
|
+
const summaryPreview =
|
|
2648
|
+
lastContentStr.length > 300 ? lastContentStr.substring(0, 300) + '...' : lastContentStr;
|
|
2056
2649
|
return {
|
|
2057
2650
|
success: true,
|
|
2058
|
-
message: `Task "${description}" completed
|
|
2651
|
+
message: `Task "${description}" completed by ${subagent_type}`,
|
|
2059
2652
|
result: {
|
|
2060
2653
|
summary: summaryPreview,
|
|
2061
2654
|
executionHistory: {
|
|
2062
2655
|
totalIterations: iteration,
|
|
2063
2656
|
toolsExecuted: executionHistory.length,
|
|
2064
|
-
successfulTools: executionHistory.filter(t => t.status === 'success').length,
|
|
2065
|
-
failedTools: executionHistory.filter(t => t.status === 'error').length,
|
|
2657
|
+
successfulTools: executionHistory.filter((t) => t.status === 'success').length,
|
|
2658
|
+
failedTools: executionHistory.filter((t) => t.status === 'error').length,
|
|
2066
2659
|
history: executionHistory,
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
}
|
|
2660
|
+
},
|
|
2661
|
+
},
|
|
2070
2662
|
};
|
|
2071
2663
|
}
|
|
2072
|
-
|
|
2664
|
+
|
|
2073
2665
|
private async executeParallelAgents(
|
|
2074
2666
|
agents: SubAgentTask[],
|
|
2075
2667
|
description: string,
|
|
2076
2668
|
mode: ExecutionMode,
|
|
2077
2669
|
agentManager: any,
|
|
2078
2670
|
toolRegistry: any,
|
|
2079
|
-
|
|
2671
|
+
config: any,
|
|
2080
2672
|
indentLevel: number = 1
|
|
2081
2673
|
): Promise<{ success: boolean; message: string; results: any[]; errors: any[] }> {
|
|
2082
2674
|
const indent = ' '.repeat(indentLevel);
|
|
@@ -2105,13 +2697,14 @@ export class TaskTool implements Tool {
|
|
|
2105
2697
|
const chunk = process.stdin.read(1);
|
|
2106
2698
|
if (chunk && chunk.length > 0) {
|
|
2107
2699
|
const code = chunk[0];
|
|
2108
|
-
if (code ===
|
|
2700
|
+
if (code === 0x1b) {
|
|
2701
|
+
// ESC
|
|
2109
2702
|
logger.debug('[ParallelAgents] ESC detected via polling!');
|
|
2110
2703
|
cancellationManager.cancel();
|
|
2111
2704
|
}
|
|
2112
2705
|
}
|
|
2113
2706
|
}
|
|
2114
|
-
} catch
|
|
2707
|
+
} catch {
|
|
2115
2708
|
// Ignore polling errors
|
|
2116
2709
|
}
|
|
2117
2710
|
}, 10);
|
|
@@ -2135,18 +2728,20 @@ export class TaskTool implements Tool {
|
|
|
2135
2728
|
};
|
|
2136
2729
|
cancellationManager.on('cancelled', cancelHandler);
|
|
2137
2730
|
|
|
2138
|
-
console.log(
|
|
2731
|
+
console.log(
|
|
2732
|
+
`\n${indent}${colors.accent('◆')} ${colors.primaryBright('Parallel Agents')}: ${agents.length} running...`
|
|
2733
|
+
);
|
|
2139
2734
|
|
|
2140
2735
|
const startTime = Date.now();
|
|
2141
2736
|
|
|
2142
|
-
const agentPromises = agents.map(async (agentTask,
|
|
2737
|
+
const agentPromises = agents.map(async (agentTask, _index) => {
|
|
2143
2738
|
// Check if cancelled
|
|
2144
2739
|
if (cancelled || cancellationManager.isOperationCancelled()) {
|
|
2145
2740
|
return {
|
|
2146
2741
|
success: false,
|
|
2147
2742
|
agent: agentTask.subagent_type,
|
|
2148
2743
|
description: agentTask.description,
|
|
2149
|
-
error: 'Operation cancelled by user'
|
|
2744
|
+
error: 'Operation cancelled by user',
|
|
2150
2745
|
};
|
|
2151
2746
|
}
|
|
2152
2747
|
|
|
@@ -2160,35 +2755,39 @@ export class TaskTool implements Tool {
|
|
|
2160
2755
|
mode,
|
|
2161
2756
|
agentManager,
|
|
2162
2757
|
toolRegistry,
|
|
2163
|
-
|
|
2758
|
+
config,
|
|
2164
2759
|
indentLevel + 1
|
|
2165
2760
|
);
|
|
2166
|
-
|
|
2761
|
+
|
|
2167
2762
|
return {
|
|
2168
2763
|
success: true,
|
|
2169
2764
|
agent: agentTask.subagent_type,
|
|
2170
2765
|
description: agentTask.description,
|
|
2171
|
-
result: result.result
|
|
2766
|
+
result: result.result,
|
|
2172
2767
|
};
|
|
2173
2768
|
} catch (error: any) {
|
|
2174
2769
|
return {
|
|
2175
2770
|
success: false,
|
|
2176
2771
|
agent: agentTask.subagent_type,
|
|
2177
2772
|
description: agentTask.description,
|
|
2178
|
-
error: error.message
|
|
2773
|
+
error: error.message,
|
|
2179
2774
|
};
|
|
2180
2775
|
}
|
|
2181
2776
|
});
|
|
2182
|
-
|
|
2777
|
+
|
|
2183
2778
|
const results = await Promise.all(agentPromises);
|
|
2184
|
-
|
|
2779
|
+
|
|
2185
2780
|
const duration = Date.now() - startTime;
|
|
2186
|
-
|
|
2187
|
-
const successfulAgents = results.filter(r => r.success);
|
|
2188
|
-
const failedAgents = results.filter(r => !r.success);
|
|
2189
|
-
|
|
2190
|
-
console.log(
|
|
2191
|
-
|
|
2781
|
+
|
|
2782
|
+
const successfulAgents = results.filter((r) => r.success);
|
|
2783
|
+
const failedAgents = results.filter((r) => !r.success);
|
|
2784
|
+
|
|
2785
|
+
console.log(
|
|
2786
|
+
`${indent}${colors.success('✔')} Parallel task completed in ${colors.textMuted(duration + 'ms')}`
|
|
2787
|
+
);
|
|
2788
|
+
console.log(
|
|
2789
|
+
`${indent}${colors.info('ℹ')} Success: ${successfulAgents.length}/${agents.length} agents\n`
|
|
2790
|
+
);
|
|
2192
2791
|
|
|
2193
2792
|
if (failedAgents.length > 0) {
|
|
2194
2793
|
console.log(`${indent}${colors.error('✖')} Failed agents:`);
|
|
@@ -2205,16 +2804,16 @@ export class TaskTool implements Tool {
|
|
|
2205
2804
|
return {
|
|
2206
2805
|
success: failedAgents.length === 0,
|
|
2207
2806
|
message: `Parallel task "${description}" completed: ${successfulAgents.length}/${agents.length} successful`,
|
|
2208
|
-
results: successfulAgents.map(r => ({
|
|
2807
|
+
results: successfulAgents.map((r) => ({
|
|
2209
2808
|
agent: r.agent,
|
|
2210
2809
|
description: r.description,
|
|
2211
|
-
result: r.result
|
|
2810
|
+
result: r.result,
|
|
2212
2811
|
})),
|
|
2213
|
-
errors: failedAgents.map(r => ({
|
|
2812
|
+
errors: failedAgents.map((r) => ({
|
|
2214
2813
|
agent: r.agent,
|
|
2215
2814
|
description: r.description,
|
|
2216
|
-
error: r.error
|
|
2217
|
-
}))
|
|
2815
|
+
error: r.error,
|
|
2816
|
+
})),
|
|
2218
2817
|
};
|
|
2219
2818
|
}
|
|
2220
2819
|
}
|
|
@@ -2247,34 +2846,39 @@ export class ReadBashOutputTool implements Tool {
|
|
|
2247
2846
|
- Use appropriate poll_interval based on expected task duration
|
|
2248
2847
|
- Check status to see if task is still running or completed
|
|
2249
2848
|
- Combine with todo_write to track background task progress`;
|
|
2250
|
-
allowedModes = [
|
|
2849
|
+
allowedModes = [
|
|
2850
|
+
ExecutionMode.YOLO,
|
|
2851
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
2852
|
+
ExecutionMode.PLAN,
|
|
2853
|
+
ExecutionMode.SMART,
|
|
2854
|
+
];
|
|
2251
2855
|
|
|
2252
2856
|
async execute(params: {
|
|
2253
2857
|
task_id: string;
|
|
2254
2858
|
poll_interval?: number;
|
|
2255
2859
|
}): Promise<{ taskId: string; output: string; status: string; duration: number }> {
|
|
2256
2860
|
const { task_id, poll_interval = 10 } = params;
|
|
2257
|
-
|
|
2861
|
+
|
|
2258
2862
|
try {
|
|
2259
2863
|
const toolRegistry = getToolRegistry();
|
|
2260
2864
|
const task = (toolRegistry as any).getBackgroundTask(task_id);
|
|
2261
|
-
|
|
2865
|
+
|
|
2262
2866
|
if (!task) {
|
|
2263
2867
|
throw new Error(`Task ${task_id} not found`);
|
|
2264
2868
|
}
|
|
2265
|
-
|
|
2869
|
+
|
|
2266
2870
|
const interval = Math.min(Math.max(poll_interval, 1), 120);
|
|
2267
|
-
await new Promise(resolve => setTimeout(resolve, interval * 1000));
|
|
2268
|
-
|
|
2871
|
+
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
|
|
2872
|
+
|
|
2269
2873
|
const duration = Date.now() - task.startTime;
|
|
2270
2874
|
const output = task.output.join('');
|
|
2271
2875
|
const status = task.process.exitCode === null ? 'running' : 'completed';
|
|
2272
|
-
|
|
2876
|
+
|
|
2273
2877
|
return {
|
|
2274
2878
|
taskId: task_id,
|
|
2275
2879
|
output,
|
|
2276
2880
|
status,
|
|
2277
|
-
duration: Math.floor(duration / 1000)
|
|
2881
|
+
duration: Math.floor(duration / 1000),
|
|
2278
2882
|
};
|
|
2279
2883
|
} catch (error: any) {
|
|
2280
2884
|
throw new Error(`Failed to read bash output: ${error.message}`);
|
|
@@ -2310,36 +2914,43 @@ export class WebFetchTool implements Tool {
|
|
|
2310
2914
|
- Use specific prompts to extract relevant information
|
|
2311
2915
|
- Check if the page is accessible if you get errors
|
|
2312
2916
|
- Large pages may be truncated due to size limits`;
|
|
2313
|
-
allowedModes = [
|
|
2917
|
+
allowedModes = [
|
|
2918
|
+
ExecutionMode.YOLO,
|
|
2919
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
2920
|
+
ExecutionMode.PLAN,
|
|
2921
|
+
ExecutionMode.SMART,
|
|
2922
|
+
];
|
|
2314
2923
|
|
|
2315
|
-
async execute(params: {
|
|
2924
|
+
async execute(params: {
|
|
2925
|
+
prompt: string;
|
|
2926
|
+
}): Promise<{ content: string; url: string; status: number }> {
|
|
2316
2927
|
const { prompt } = params;
|
|
2317
|
-
|
|
2928
|
+
|
|
2318
2929
|
try {
|
|
2319
2930
|
const urlMatch = prompt.match(/https?:\/\/[^\s]+/i);
|
|
2320
|
-
|
|
2931
|
+
|
|
2321
2932
|
if (!urlMatch) {
|
|
2322
2933
|
throw new Error('No URL found in prompt');
|
|
2323
2934
|
}
|
|
2324
|
-
|
|
2935
|
+
|
|
2325
2936
|
const url = urlMatch[0];
|
|
2326
|
-
|
|
2937
|
+
|
|
2327
2938
|
const response = await axios.get(url, {
|
|
2328
2939
|
timeout: 30000,
|
|
2329
2940
|
maxContentLength: 10 * 1024 * 1024,
|
|
2330
|
-
validateStatus: () => true
|
|
2941
|
+
validateStatus: () => true,
|
|
2331
2942
|
});
|
|
2332
|
-
|
|
2943
|
+
|
|
2333
2944
|
let content = response.data;
|
|
2334
|
-
|
|
2945
|
+
|
|
2335
2946
|
if (typeof content === 'object') {
|
|
2336
2947
|
content = JSON.stringify(content, null, 2);
|
|
2337
2948
|
}
|
|
2338
|
-
|
|
2949
|
+
|
|
2339
2950
|
return {
|
|
2340
2951
|
content,
|
|
2341
2952
|
url,
|
|
2342
|
-
status: response.status
|
|
2953
|
+
status: response.status,
|
|
2343
2954
|
};
|
|
2344
2955
|
} catch (error: any) {
|
|
2345
2956
|
throw new Error(`Failed to fetch URL: ${error.message}`);
|
|
@@ -2380,7 +2991,12 @@ export class AskUserQuestionTool implements Tool {
|
|
|
2380
2991
|
- Provide options when possible for faster response
|
|
2381
2992
|
- Use multiSelect=true when multiple answers are valid
|
|
2382
2993
|
- Be clear and concise in question wording`;
|
|
2383
|
-
allowedModes = [
|
|
2994
|
+
allowedModes = [
|
|
2995
|
+
ExecutionMode.YOLO,
|
|
2996
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
2997
|
+
ExecutionMode.PLAN,
|
|
2998
|
+
ExecutionMode.SMART,
|
|
2999
|
+
];
|
|
2384
3000
|
|
|
2385
3001
|
async execute(params: {
|
|
2386
3002
|
questions: Array<{
|
|
@@ -2389,47 +3005,110 @@ export class AskUserQuestionTool implements Tool {
|
|
|
2389
3005
|
options?: string[];
|
|
2390
3006
|
multiSelect?: boolean;
|
|
2391
3007
|
}>;
|
|
3008
|
+
}): Promise<{ answers: string[] }> {
|
|
3009
|
+
// Check if in SDK mode
|
|
3010
|
+
const sdkMode = (this as any)._sdkMode;
|
|
3011
|
+
const sdkAdapter = (this as any)._sdkOutputAdapter;
|
|
3012
|
+
|
|
3013
|
+
if (sdkMode && sdkAdapter) {
|
|
3014
|
+
return this.executeSdk(params, sdkAdapter);
|
|
3015
|
+
}
|
|
3016
|
+
|
|
3017
|
+
// Regular TUI mode
|
|
3018
|
+
return this.executeTui(params);
|
|
3019
|
+
}
|
|
3020
|
+
|
|
3021
|
+
/**
|
|
3022
|
+
* Execute in TUI mode using @clack/prompts
|
|
3023
|
+
*/
|
|
3024
|
+
private async executeTui(params: {
|
|
3025
|
+
questions: Array<{
|
|
3026
|
+
question: string;
|
|
3027
|
+
header?: string;
|
|
3028
|
+
options?: string[];
|
|
3029
|
+
multiSelect?: boolean;
|
|
3030
|
+
}>;
|
|
2392
3031
|
}): Promise<{ answers: string[] }> {
|
|
2393
3032
|
const { questions } = params;
|
|
2394
|
-
|
|
3033
|
+
|
|
2395
3034
|
try {
|
|
2396
3035
|
if (questions.length === 0 || questions.length > 4) {
|
|
2397
3036
|
throw new Error('Must provide 1-4 questions');
|
|
2398
3037
|
}
|
|
2399
|
-
|
|
3038
|
+
|
|
2400
3039
|
const answers: string[] = [];
|
|
2401
|
-
|
|
3040
|
+
|
|
2402
3041
|
for (const q of questions) {
|
|
2403
3042
|
if (q.options && q.options.length > 0) {
|
|
2404
|
-
const
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
}
|
|
2412
|
-
]);
|
|
2413
|
-
|
|
2414
|
-
answers.push(Array.isArray(result.answer) ? result.answer.join(', ') : result.answer);
|
|
3043
|
+
const options = q.options.map((opt) => ({ value: opt, label: opt }));
|
|
3044
|
+
const result = await select({
|
|
3045
|
+
message: q.question,
|
|
3046
|
+
options,
|
|
3047
|
+
});
|
|
3048
|
+
|
|
3049
|
+
answers.push(result as string);
|
|
2415
3050
|
} else {
|
|
2416
|
-
const result = await
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
}
|
|
2422
|
-
]);
|
|
2423
|
-
|
|
2424
|
-
answers.push(result.answer);
|
|
3051
|
+
const result = (await text({
|
|
3052
|
+
message: q.question,
|
|
3053
|
+
})) as string;
|
|
3054
|
+
|
|
3055
|
+
answers.push(result);
|
|
2425
3056
|
}
|
|
2426
3057
|
}
|
|
2427
|
-
|
|
3058
|
+
|
|
2428
3059
|
return { answers };
|
|
2429
3060
|
} catch (error: any) {
|
|
2430
3061
|
throw new Error(`Failed to ask user questions: ${error.message}`);
|
|
2431
3062
|
}
|
|
2432
3063
|
}
|
|
3064
|
+
|
|
3065
|
+
/**
|
|
3066
|
+
* Execute in SDK mode - output question request and wait for response
|
|
3067
|
+
*/
|
|
3068
|
+
private async executeSdk(
|
|
3069
|
+
params: {
|
|
3070
|
+
questions: Array<{
|
|
3071
|
+
question: string;
|
|
3072
|
+
header?: string;
|
|
3073
|
+
options?: string[];
|
|
3074
|
+
multiSelect?: boolean;
|
|
3075
|
+
}>;
|
|
3076
|
+
},
|
|
3077
|
+
sdkAdapter: any
|
|
3078
|
+
): Promise<{ answers: string[] }> {
|
|
3079
|
+
const { questions } = params;
|
|
3080
|
+
|
|
3081
|
+
if (questions.length === 0 || questions.length > 4) {
|
|
3082
|
+
throw new Error('Must provide 1-4 questions');
|
|
3083
|
+
}
|
|
3084
|
+
|
|
3085
|
+
const requestId = `question_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
3086
|
+
|
|
3087
|
+
// Output question request through SDK adapter
|
|
3088
|
+
sdkAdapter.outputQuestionRequest({
|
|
3089
|
+
requestId,
|
|
3090
|
+
questions
|
|
3091
|
+
});
|
|
3092
|
+
|
|
3093
|
+
// Wait for SDK question response
|
|
3094
|
+
// The response will be handled by session.ts which has access to the SDK input
|
|
3095
|
+
// For now, we use a polling mechanism or wait for a specific event
|
|
3096
|
+
|
|
3097
|
+
try {
|
|
3098
|
+
// Import the session to get response handling
|
|
3099
|
+
const { getSingletonSession } = await import('./session.js');
|
|
3100
|
+
const session = getSingletonSession();
|
|
3101
|
+
if (!session) {
|
|
3102
|
+
throw new Error('SDK session not available');
|
|
3103
|
+
}
|
|
3104
|
+
const answers = await session.waitForQuestionResponse(requestId);
|
|
3105
|
+
|
|
3106
|
+
sdkAdapter.outputQuestionResponse(requestId, answers);
|
|
3107
|
+
return { answers };
|
|
3108
|
+
} catch (error: any) {
|
|
3109
|
+
throw new Error(`Failed to get SDK question response: ${error.message}`);
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
2433
3112
|
}
|
|
2434
3113
|
|
|
2435
3114
|
export class SaveMemoryTool implements Tool {
|
|
@@ -2461,20 +3140,25 @@ export class SaveMemoryTool implements Tool {
|
|
|
2461
3140
|
- Keep facts concise and specific
|
|
2462
3141
|
- Remember project-specific conventions for consistency
|
|
2463
3142
|
- This persists across sessions (global memory)`;
|
|
2464
|
-
allowedModes = [
|
|
3143
|
+
allowedModes = [
|
|
3144
|
+
ExecutionMode.YOLO,
|
|
3145
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
3146
|
+
ExecutionMode.PLAN,
|
|
3147
|
+
ExecutionMode.SMART,
|
|
3148
|
+
];
|
|
2465
3149
|
|
|
2466
3150
|
async execute(params: { fact: string }): Promise<{ success: boolean; message: string }> {
|
|
2467
3151
|
const { fact } = params;
|
|
2468
|
-
|
|
3152
|
+
|
|
2469
3153
|
try {
|
|
2470
3154
|
const { getMemoryManager } = await import('./memory.js');
|
|
2471
3155
|
const memoryManager = getMemoryManager(process.cwd());
|
|
2472
|
-
|
|
3156
|
+
|
|
2473
3157
|
await memoryManager.saveMemory(fact, 'global');
|
|
2474
|
-
|
|
3158
|
+
|
|
2475
3159
|
return {
|
|
2476
3160
|
success: true,
|
|
2477
|
-
message: `Successfully saved fact to memory
|
|
3161
|
+
message: `Successfully saved fact to memory`,
|
|
2478
3162
|
};
|
|
2479
3163
|
} catch (error: any) {
|
|
2480
3164
|
throw new Error(`Failed to save memory: ${error.message}`);
|
|
@@ -2510,16 +3194,23 @@ export class ExitPlanModeTool implements Tool {
|
|
|
2510
3194
|
- Include all necessary steps and considerations
|
|
2511
3195
|
- The plan will be saved for reference during execution
|
|
2512
3196
|
- Use this only when truly ready to start coding`;
|
|
2513
|
-
allowedModes = [
|
|
3197
|
+
allowedModes = [
|
|
3198
|
+
ExecutionMode.YOLO,
|
|
3199
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
3200
|
+
ExecutionMode.PLAN,
|
|
3201
|
+
ExecutionMode.SMART,
|
|
3202
|
+
];
|
|
2514
3203
|
|
|
2515
|
-
async execute(params: {
|
|
3204
|
+
async execute(params: {
|
|
3205
|
+
plan: string;
|
|
3206
|
+
}): Promise<{ success: boolean; message: string; plan: string }> {
|
|
2516
3207
|
const { plan } = params;
|
|
2517
|
-
|
|
3208
|
+
|
|
2518
3209
|
try {
|
|
2519
3210
|
return {
|
|
2520
3211
|
success: true,
|
|
2521
3212
|
message: 'Plan completed and ready for execution',
|
|
2522
|
-
plan
|
|
3213
|
+
plan,
|
|
2523
3214
|
};
|
|
2524
3215
|
} catch (error: any) {
|
|
2525
3216
|
throw new Error(`Failed to exit plan mode: ${error.message}`);
|
|
@@ -2559,21 +3250,21 @@ export class XmlEscapeTool implements Tool {
|
|
|
2559
3250
|
escape_all?: boolean;
|
|
2560
3251
|
}): Promise<{ success: boolean; message: string; changes: number }> {
|
|
2561
3252
|
const { file_path, escape_all = false } = params;
|
|
2562
|
-
|
|
3253
|
+
|
|
2563
3254
|
try {
|
|
2564
3255
|
const absolutePath = path.resolve(file_path);
|
|
2565
3256
|
let content = await fs.readFile(absolutePath, 'utf-8');
|
|
2566
|
-
|
|
3257
|
+
|
|
2567
3258
|
const specialChars = [
|
|
2568
3259
|
{ char: '&', replacement: '&' },
|
|
2569
3260
|
{ char: '<', replacement: '<' },
|
|
2570
3261
|
{ char: '>', replacement: '>' },
|
|
2571
3262
|
{ char: '"', replacement: '"' },
|
|
2572
|
-
{ char: "'", replacement: ''' }
|
|
3263
|
+
{ char: "'", replacement: ''' },
|
|
2573
3264
|
];
|
|
2574
|
-
|
|
3265
|
+
|
|
2575
3266
|
let changes = 0;
|
|
2576
|
-
|
|
3267
|
+
|
|
2577
3268
|
for (const { char, replacement } of specialChars) {
|
|
2578
3269
|
const regex = new RegExp(this.escapeRegExp(char), 'g');
|
|
2579
3270
|
const matches = content.match(regex);
|
|
@@ -2582,14 +3273,14 @@ export class XmlEscapeTool implements Tool {
|
|
|
2582
3273
|
content = content.replace(regex, replacement);
|
|
2583
3274
|
}
|
|
2584
3275
|
}
|
|
2585
|
-
|
|
3276
|
+
|
|
2586
3277
|
if (escape_all) {
|
|
2587
3278
|
const additionalChars = [
|
|
2588
3279
|
{ char: '©', replacement: '©' },
|
|
2589
3280
|
{ char: '®', replacement: '®' },
|
|
2590
|
-
{ char: '€', replacement: '€' }
|
|
3281
|
+
{ char: '€', replacement: '€' },
|
|
2591
3282
|
];
|
|
2592
|
-
|
|
3283
|
+
|
|
2593
3284
|
for (const { char, replacement } of additionalChars) {
|
|
2594
3285
|
const regex = new RegExp(this.escapeRegExp(char), 'g');
|
|
2595
3286
|
const matches = content.match(regex);
|
|
@@ -2599,13 +3290,13 @@ export class XmlEscapeTool implements Tool {
|
|
|
2599
3290
|
}
|
|
2600
3291
|
}
|
|
2601
3292
|
}
|
|
2602
|
-
|
|
3293
|
+
|
|
2603
3294
|
await fs.writeFile(absolutePath, content, 'utf-8');
|
|
2604
|
-
|
|
3295
|
+
|
|
2605
3296
|
return {
|
|
2606
3297
|
success: true,
|
|
2607
3298
|
message: `Successfully escaped ${changes} character(s) in ${file_path}`,
|
|
2608
|
-
changes
|
|
3299
|
+
changes,
|
|
2609
3300
|
};
|
|
2610
3301
|
} catch (error: any) {
|
|
2611
3302
|
throw new Error(`Failed to escape XML/HTML in file ${file_path}: ${error.message}`);
|
|
@@ -2646,7 +3337,12 @@ export class ImageReadTool implements Tool {
|
|
|
2646
3337
|
- Provide clear prompts for what to look for
|
|
2647
3338
|
- Use task_brief for context
|
|
2648
3339
|
- Supports PNG, JPG, GIF, WEBP, SVG, BMP`;
|
|
2649
|
-
allowedModes = [
|
|
3340
|
+
allowedModes = [
|
|
3341
|
+
ExecutionMode.YOLO,
|
|
3342
|
+
ExecutionMode.ACCEPT_EDITS,
|
|
3343
|
+
ExecutionMode.PLAN,
|
|
3344
|
+
ExecutionMode.SMART,
|
|
3345
|
+
];
|
|
2650
3346
|
|
|
2651
3347
|
async execute(params: {
|
|
2652
3348
|
image_input: string;
|
|
@@ -2656,10 +3352,10 @@ export class ImageReadTool implements Tool {
|
|
|
2656
3352
|
mime_type?: string;
|
|
2657
3353
|
}): Promise<{ analysis: string; image_info: any }> {
|
|
2658
3354
|
const { image_input, prompt, task_brief, input_type = 'file_path', mime_type } = params;
|
|
2659
|
-
|
|
3355
|
+
|
|
2660
3356
|
try {
|
|
2661
3357
|
let imageData: string;
|
|
2662
|
-
|
|
3358
|
+
|
|
2663
3359
|
if (input_type === 'file_path') {
|
|
2664
3360
|
const absolutePath = path.resolve(image_input);
|
|
2665
3361
|
const imageBuffer = await fs.readFile(absolutePath);
|
|
@@ -2667,19 +3363,14 @@ export class ImageReadTool implements Tool {
|
|
|
2667
3363
|
} else {
|
|
2668
3364
|
imageData = image_input;
|
|
2669
3365
|
}
|
|
2670
|
-
|
|
2671
|
-
const { AIClient } = await import('./ai-client.js');
|
|
3366
|
+
|
|
2672
3367
|
const configManager = await import('./config.js');
|
|
2673
3368
|
const { getConfigManager } = configManager;
|
|
2674
3369
|
const config = getConfigManager();
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
baseUrl: config.get('baseUrl'),
|
|
2680
|
-
modelName: config.get('modelName') || 'Qwen3-Coder'
|
|
2681
|
-
});
|
|
2682
|
-
|
|
3370
|
+
const authConfig = config.getAuthConfig();
|
|
3371
|
+
|
|
3372
|
+
const aiClient = createAIClient(authConfig);
|
|
3373
|
+
|
|
2683
3374
|
const textContent = task_brief ? `${task_brief}\n\n${prompt}` : prompt;
|
|
2684
3375
|
const messages: Message[] = [
|
|
2685
3376
|
{
|
|
@@ -2687,32 +3378,32 @@ export class ImageReadTool implements Tool {
|
|
|
2687
3378
|
content: [
|
|
2688
3379
|
{
|
|
2689
3380
|
type: 'text',
|
|
2690
|
-
text: textContent
|
|
3381
|
+
text: textContent,
|
|
2691
3382
|
},
|
|
2692
3383
|
{
|
|
2693
3384
|
type: 'image_url' as const,
|
|
2694
3385
|
image_url: {
|
|
2695
|
-
url: `data:${mime_type || 'image/jpeg'};base64,${imageData}
|
|
2696
|
-
}
|
|
2697
|
-
}
|
|
2698
|
-
]
|
|
2699
|
-
}
|
|
3386
|
+
url: `data:${mime_type || 'image/jpeg'};base64,${imageData}`,
|
|
3387
|
+
},
|
|
3388
|
+
},
|
|
3389
|
+
],
|
|
3390
|
+
},
|
|
2700
3391
|
];
|
|
2701
|
-
|
|
3392
|
+
|
|
2702
3393
|
const result = await aiClient.chatCompletion(messages, {
|
|
2703
|
-
temperature: 0.7
|
|
3394
|
+
temperature: 0.7,
|
|
2704
3395
|
});
|
|
2705
|
-
|
|
3396
|
+
|
|
2706
3397
|
const messageContent = result.choices[0]?.message?.content;
|
|
2707
3398
|
const analysis = typeof messageContent === 'string' ? messageContent : '';
|
|
2708
|
-
|
|
3399
|
+
|
|
2709
3400
|
return {
|
|
2710
3401
|
analysis,
|
|
2711
3402
|
image_info: {
|
|
2712
3403
|
input_type,
|
|
2713
3404
|
prompt,
|
|
2714
|
-
task_brief
|
|
2715
|
-
}
|
|
3405
|
+
task_brief,
|
|
3406
|
+
},
|
|
2716
3407
|
};
|
|
2717
3408
|
} catch (error: any) {
|
|
2718
3409
|
throw new Error(`Failed to read image: ${error.message}`);
|
|
@@ -2802,13 +3493,16 @@ export class InvokeSkillTool implements Tool {
|
|
|
2802
3493
|
- Skills will guide you through their specific workflows`;
|
|
2803
3494
|
allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
|
|
2804
3495
|
|
|
2805
|
-
async execute(
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
3496
|
+
async execute(
|
|
3497
|
+
params: {
|
|
3498
|
+
skillId: string;
|
|
3499
|
+
taskDescription: string;
|
|
3500
|
+
inputFile?: string;
|
|
3501
|
+
outputFile?: string;
|
|
3502
|
+
options?: Record<string, any>;
|
|
3503
|
+
},
|
|
3504
|
+
_executionMode?: ExecutionMode
|
|
3505
|
+
): Promise<{
|
|
2812
3506
|
success: boolean;
|
|
2813
3507
|
message: string;
|
|
2814
3508
|
skill: string;
|
|
@@ -2825,12 +3519,13 @@ export class InvokeSkillTool implements Tool {
|
|
|
2825
3519
|
reason: string;
|
|
2826
3520
|
}>;
|
|
2827
3521
|
guidance?: string;
|
|
3522
|
+
/** Skill directory path for dependency management */
|
|
3523
|
+
skillPath?: string;
|
|
2828
3524
|
}> {
|
|
2829
3525
|
const { skillId, taskDescription, inputFile, outputFile, options } = params;
|
|
2830
3526
|
|
|
2831
3527
|
try {
|
|
2832
3528
|
const { getSkillInvoker } = await import('./skill-invoker.js');
|
|
2833
|
-
const { SkillExecutionParams } = await import('./skill-invoker.js') as any;
|
|
2834
3529
|
const skillInvoker = getSkillInvoker();
|
|
2835
3530
|
|
|
2836
3531
|
await skillInvoker.initialize();
|
|
@@ -2847,7 +3542,7 @@ export class InvokeSkillTool implements Tool {
|
|
|
2847
3542
|
taskDescription,
|
|
2848
3543
|
inputFile,
|
|
2849
3544
|
outputFile,
|
|
2850
|
-
options
|
|
3545
|
+
options,
|
|
2851
3546
|
});
|
|
2852
3547
|
|
|
2853
3548
|
if (result.success) {
|
|
@@ -2858,7 +3553,7 @@ export class InvokeSkillTool implements Tool {
|
|
|
2858
3553
|
task: taskDescription,
|
|
2859
3554
|
result: result.output,
|
|
2860
3555
|
files: result.files,
|
|
2861
|
-
nextSteps: result.nextSteps
|
|
3556
|
+
nextSteps: result.nextSteps,
|
|
2862
3557
|
};
|
|
2863
3558
|
} else {
|
|
2864
3559
|
throw new Error(result.error || 'Failed to execute matched skill');
|
|
@@ -2872,7 +3567,7 @@ export class InvokeSkillTool implements Tool {
|
|
|
2872
3567
|
taskDescription,
|
|
2873
3568
|
inputFile,
|
|
2874
3569
|
outputFile,
|
|
2875
|
-
options
|
|
3570
|
+
options,
|
|
2876
3571
|
});
|
|
2877
3572
|
|
|
2878
3573
|
if (result.success) {
|
|
@@ -2883,7 +3578,8 @@ export class InvokeSkillTool implements Tool {
|
|
|
2883
3578
|
task: taskDescription,
|
|
2884
3579
|
result: result.output,
|
|
2885
3580
|
files: result.files,
|
|
2886
|
-
nextSteps: result.nextSteps
|
|
3581
|
+
nextSteps: result.nextSteps,
|
|
3582
|
+
skillPath: result.skillPath
|
|
2887
3583
|
};
|
|
2888
3584
|
} else {
|
|
2889
3585
|
throw new Error(result.error);
|
|
@@ -2943,7 +3639,7 @@ export class InvokeSkillTool implements Tool {
|
|
|
2943
3639
|
|
|
2944
3640
|
// async execute(params: { skill: string }): Promise<{ success: boolean; details: any }> {
|
|
2945
3641
|
// const { skill } = params;
|
|
2946
|
-
|
|
3642
|
+
|
|
2947
3643
|
// if (!skill) {
|
|
2948
3644
|
// throw new Error('Skill parameter is required');
|
|
2949
3645
|
// }
|
|
@@ -2976,7 +3672,10 @@ export class InvokeSkillTool implements Tool {
|
|
|
2976
3672
|
export class ToolRegistry {
|
|
2977
3673
|
private tools: Map<string, Tool> = new Map();
|
|
2978
3674
|
private todoWriteTool: TodoWriteTool;
|
|
2979
|
-
private backgroundTasks: Map<string, { process: any; startTime: number; output: string[] }> =
|
|
3675
|
+
private backgroundTasks: Map<string, { process: any; startTime: number; output: string[] }> =
|
|
3676
|
+
new Map();
|
|
3677
|
+
private _isSdkMode: boolean = false;
|
|
3678
|
+
private _sdkOutputAdapter: any = null;
|
|
2980
3679
|
|
|
2981
3680
|
constructor() {
|
|
2982
3681
|
this.todoWriteTool = new TodoWriteTool();
|
|
@@ -2989,7 +3688,7 @@ export class ToolRegistry {
|
|
|
2989
3688
|
this.register(new GrepTool());
|
|
2990
3689
|
this.register(new BashTool());
|
|
2991
3690
|
this.register(new ListDirectoryTool());
|
|
2992
|
-
this.register(new SearchFilesTool());
|
|
3691
|
+
this.register(new SearchFilesTool());
|
|
2993
3692
|
this.register(new DeleteFileTool());
|
|
2994
3693
|
this.register(new CreateDirectoryTool());
|
|
2995
3694
|
this.register(new EditTool());
|
|
@@ -3027,23 +3726,21 @@ export class ToolRegistry {
|
|
|
3027
3726
|
registerMCPTools(mcpTools: Map<string, any>): void {
|
|
3028
3727
|
let registeredCount = 0;
|
|
3029
3728
|
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
const firstUnderscoreIndex = fullName.indexOf('__');
|
|
3033
|
-
|
|
3034
|
-
if (firstUnderscoreIndex === -1 || firstUnderscoreIndex === 0 ||
|
|
3035
|
-
|
|
3036
|
-
firstUnderscoreIndex === fullName.length - 2) continue;
|
|
3037
|
-
|
|
3038
|
-
|
|
3729
|
+
for (const [fullName, tool] of mcpTools) {
|
|
3730
|
+
const firstUnderscoreIndex = fullName.indexOf('__');
|
|
3039
3731
|
|
|
3040
|
-
|
|
3732
|
+
if (
|
|
3733
|
+
firstUnderscoreIndex === -1 ||
|
|
3734
|
+
firstUnderscoreIndex === 0 ||
|
|
3735
|
+
firstUnderscoreIndex === fullName.length - 2
|
|
3736
|
+
)
|
|
3737
|
+
continue;
|
|
3041
3738
|
|
|
3042
|
-
|
|
3739
|
+
const serverName = fullName.substring(0, firstUnderscoreIndex);
|
|
3043
3740
|
|
|
3044
|
-
|
|
3741
|
+
const originalName = fullName.substring(firstUnderscoreIndex + 2);
|
|
3045
3742
|
|
|
3046
|
-
|
|
3743
|
+
if (!originalName || originalName.trim() === '') continue;
|
|
3047
3744
|
|
|
3048
3745
|
// Auto-rename if conflict, ensure unique name
|
|
3049
3746
|
let toolName = originalName;
|
|
@@ -3077,19 +3774,45 @@ export class ToolRegistry {
|
|
|
3077
3774
|
const { getMCPManager } = await import('./mcp.js');
|
|
3078
3775
|
const mcpManager = getMCPManager();
|
|
3079
3776
|
return await mcpManager.callTool(fullName, params);
|
|
3080
|
-
}
|
|
3777
|
+
},
|
|
3081
3778
|
};
|
|
3082
3779
|
this.tools.set(toolName, mcpTool);
|
|
3083
3780
|
registeredCount++;
|
|
3084
3781
|
|
|
3085
3782
|
if (toolName !== originalName) {
|
|
3086
|
-
|
|
3783
|
+
// 在 SDK 模式下不输出重命名信息
|
|
3784
|
+
if (!this._isSdkMode) {
|
|
3785
|
+
console.log(`[MCP] Tool '${originalName}' renamed to '${toolName}' to avoid conflict`);
|
|
3786
|
+
}
|
|
3087
3787
|
}
|
|
3088
3788
|
}
|
|
3089
3789
|
}
|
|
3090
3790
|
|
|
3091
3791
|
if (registeredCount > 0) {
|
|
3092
|
-
|
|
3792
|
+
// 在 SDK 模式下不输出注册信息(MCP 相关输出已在 session 中处理)
|
|
3793
|
+
if (!this._isSdkMode) {
|
|
3794
|
+
console.log(`[MCP] Registered ${registeredCount} tool(s)`);
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
|
|
3799
|
+
/**
|
|
3800
|
+
* Set SDK mode for the tool registry.
|
|
3801
|
+
* In SDK mode, tool execution output is sent to the SDK output adapter.
|
|
3802
|
+
*/
|
|
3803
|
+
async setSdkMode(enabled: boolean, adapter: any): Promise<void> {
|
|
3804
|
+
this._isSdkMode = enabled;
|
|
3805
|
+
this._sdkOutputAdapter = adapter;
|
|
3806
|
+
// Mark all tools as SDK mode enabled
|
|
3807
|
+
for (const [, tool] of this.tools) {
|
|
3808
|
+
(tool as any)._sdkMode = enabled;
|
|
3809
|
+
(tool as any)._sdkOutputAdapter = adapter;
|
|
3810
|
+
}
|
|
3811
|
+
|
|
3812
|
+
// Initialize SDK mode for TaskTool specifically
|
|
3813
|
+
const taskTool = this.tools.get('task') as any;
|
|
3814
|
+
if (taskTool) {
|
|
3815
|
+
await taskTool.setSdkMode?.(enabled, adapter);
|
|
3093
3816
|
}
|
|
3094
3817
|
}
|
|
3095
3818
|
|
|
@@ -3118,11 +3841,16 @@ export class ToolRegistry {
|
|
|
3118
3841
|
return Array.from(this.tools.values());
|
|
3119
3842
|
}
|
|
3120
3843
|
|
|
3121
|
-
addBackgroundTask(
|
|
3844
|
+
addBackgroundTask(
|
|
3845
|
+
taskId: string,
|
|
3846
|
+
task: { process: any; startTime: number; output: string[] }
|
|
3847
|
+
): void {
|
|
3122
3848
|
this.backgroundTasks.set(taskId, task);
|
|
3123
3849
|
}
|
|
3124
3850
|
|
|
3125
|
-
getBackgroundTask(
|
|
3851
|
+
getBackgroundTask(
|
|
3852
|
+
taskId: string
|
|
3853
|
+
): { process: any; startTime: number; output: string[] } | undefined {
|
|
3126
3854
|
return this.backgroundTasks.get(taskId);
|
|
3127
3855
|
}
|
|
3128
3856
|
|
|
@@ -3131,11 +3859,11 @@ export class ToolRegistry {
|
|
|
3131
3859
|
}
|
|
3132
3860
|
|
|
3133
3861
|
getToolDefinitions(): any[] {
|
|
3134
|
-
return Array.from(this.tools.values()).map(tool => {
|
|
3862
|
+
return Array.from(this.tools.values()).map((tool) => {
|
|
3135
3863
|
let parameters: any = {
|
|
3136
3864
|
type: 'object',
|
|
3137
3865
|
properties: {},
|
|
3138
|
-
required: []
|
|
3866
|
+
required: [],
|
|
3139
3867
|
};
|
|
3140
3868
|
|
|
3141
3869
|
// Define specific parameters for each tool
|
|
@@ -3146,18 +3874,18 @@ export class ToolRegistry {
|
|
|
3146
3874
|
properties: {
|
|
3147
3875
|
filePath: {
|
|
3148
3876
|
type: 'string',
|
|
3149
|
-
description: 'The absolute path to the file to read'
|
|
3877
|
+
description: 'The absolute path to the file to read',
|
|
3150
3878
|
},
|
|
3151
3879
|
offset: {
|
|
3152
3880
|
type: 'number',
|
|
3153
|
-
description: 'Optional: Line number to start reading from (0-based)'
|
|
3881
|
+
description: 'Optional: Line number to start reading from (0-based)',
|
|
3154
3882
|
},
|
|
3155
3883
|
limit: {
|
|
3156
3884
|
type: 'number',
|
|
3157
|
-
description: 'Optional: Maximum number of lines to read'
|
|
3158
|
-
}
|
|
3885
|
+
description: 'Optional: Maximum number of lines to read',
|
|
3886
|
+
},
|
|
3159
3887
|
},
|
|
3160
|
-
required: ['filePath']
|
|
3888
|
+
required: ['filePath'],
|
|
3161
3889
|
};
|
|
3162
3890
|
break;
|
|
3163
3891
|
|
|
@@ -3167,14 +3895,14 @@ export class ToolRegistry {
|
|
|
3167
3895
|
properties: {
|
|
3168
3896
|
filePath: {
|
|
3169
3897
|
type: 'string',
|
|
3170
|
-
description: 'The absolute path to the file to write'
|
|
3898
|
+
description: 'The absolute path to the file to write',
|
|
3171
3899
|
},
|
|
3172
3900
|
content: {
|
|
3173
3901
|
type: 'string',
|
|
3174
|
-
description: 'The content to write to the file'
|
|
3175
|
-
}
|
|
3902
|
+
description: 'The content to write to the file',
|
|
3903
|
+
},
|
|
3176
3904
|
},
|
|
3177
|
-
required: ['filePath', 'content']
|
|
3905
|
+
required: ['filePath', 'content'],
|
|
3178
3906
|
};
|
|
3179
3907
|
break;
|
|
3180
3908
|
|
|
@@ -3184,26 +3912,34 @@ export class ToolRegistry {
|
|
|
3184
3912
|
properties: {
|
|
3185
3913
|
pattern: {
|
|
3186
3914
|
type: 'string',
|
|
3187
|
-
description: 'The regex pattern to search for'
|
|
3915
|
+
description: 'The regex pattern or literal string to search for',
|
|
3188
3916
|
},
|
|
3189
3917
|
path: {
|
|
3190
3918
|
type: 'string',
|
|
3191
|
-
description: 'Optional: The path to search in (default: current directory)'
|
|
3919
|
+
description: 'Optional: The path to search in (default: current directory)',
|
|
3192
3920
|
},
|
|
3193
|
-
|
|
3921
|
+
glob: {
|
|
3194
3922
|
type: 'string',
|
|
3195
|
-
description: 'Optional: Glob pattern to filter files'
|
|
3923
|
+
description: 'Optional: Glob pattern to filter files (e.g., "*.ts", "**/*.js")',
|
|
3924
|
+
},
|
|
3925
|
+
ignoreCase: {
|
|
3926
|
+
type: 'boolean',
|
|
3927
|
+
description: 'Optional: Case-insensitive search (default: false)',
|
|
3196
3928
|
},
|
|
3197
|
-
|
|
3929
|
+
literal: {
|
|
3198
3930
|
type: 'boolean',
|
|
3199
|
-
description: 'Optional:
|
|
3931
|
+
description: 'Optional: Treat pattern as literal string (default: false)',
|
|
3200
3932
|
},
|
|
3201
3933
|
context: {
|
|
3202
3934
|
type: 'number',
|
|
3203
|
-
description: 'Optional: Number of context lines to show'
|
|
3204
|
-
}
|
|
3935
|
+
description: 'Optional: Number of context lines to show before and after',
|
|
3936
|
+
},
|
|
3937
|
+
limit: {
|
|
3938
|
+
type: 'number',
|
|
3939
|
+
description: 'Optional: Maximum number of matches to return',
|
|
3940
|
+
},
|
|
3205
3941
|
},
|
|
3206
|
-
required: ['pattern']
|
|
3942
|
+
required: ['pattern'],
|
|
3207
3943
|
};
|
|
3208
3944
|
break;
|
|
3209
3945
|
|
|
@@ -3213,26 +3949,26 @@ export class ToolRegistry {
|
|
|
3213
3949
|
properties: {
|
|
3214
3950
|
command: {
|
|
3215
3951
|
type: 'string',
|
|
3216
|
-
description: 'The shell command to execute'
|
|
3952
|
+
description: 'The shell command to execute',
|
|
3217
3953
|
},
|
|
3218
3954
|
cwd: {
|
|
3219
3955
|
type: 'string',
|
|
3220
|
-
description: 'Optional: Working directory'
|
|
3956
|
+
description: 'Optional: Working directory',
|
|
3221
3957
|
},
|
|
3222
3958
|
description: {
|
|
3223
3959
|
type: 'string',
|
|
3224
|
-
description: 'Optional: Brief description of the command'
|
|
3960
|
+
description: 'Optional: Brief description of the command',
|
|
3225
3961
|
},
|
|
3226
3962
|
timeout: {
|
|
3227
3963
|
type: 'number',
|
|
3228
|
-
description: 'Optional: Timeout in seconds (default: 120)'
|
|
3964
|
+
description: 'Optional: Timeout in seconds (default: 120)',
|
|
3229
3965
|
},
|
|
3230
3966
|
run_in_bg: {
|
|
3231
3967
|
type: 'boolean',
|
|
3232
|
-
description: 'Optional: Run in background (default: false)'
|
|
3233
|
-
}
|
|
3968
|
+
description: 'Optional: Run in background (default: false)',
|
|
3969
|
+
},
|
|
3234
3970
|
},
|
|
3235
|
-
required: ['command']
|
|
3971
|
+
required: ['command'],
|
|
3236
3972
|
};
|
|
3237
3973
|
break;
|
|
3238
3974
|
|
|
@@ -3242,14 +3978,14 @@ export class ToolRegistry {
|
|
|
3242
3978
|
properties: {
|
|
3243
3979
|
path: {
|
|
3244
3980
|
type: 'string',
|
|
3245
|
-
description: 'Optional: The directory path to list (default: current directory)'
|
|
3981
|
+
description: 'Optional: The directory path to list (default: current directory)',
|
|
3246
3982
|
},
|
|
3247
3983
|
recursive: {
|
|
3248
3984
|
type: 'boolean',
|
|
3249
|
-
description: 'Optional: List recursively (default: false)'
|
|
3250
|
-
}
|
|
3985
|
+
description: 'Optional: List recursively (default: false)',
|
|
3986
|
+
},
|
|
3251
3987
|
},
|
|
3252
|
-
required: []
|
|
3988
|
+
required: [],
|
|
3253
3989
|
};
|
|
3254
3990
|
break;
|
|
3255
3991
|
|
|
@@ -3259,18 +3995,18 @@ export class ToolRegistry {
|
|
|
3259
3995
|
properties: {
|
|
3260
3996
|
pattern: {
|
|
3261
3997
|
type: 'string',
|
|
3262
|
-
description: 'The glob pattern to match files'
|
|
3998
|
+
description: 'The glob pattern to match files',
|
|
3263
3999
|
},
|
|
3264
4000
|
path: {
|
|
3265
4001
|
type: 'string',
|
|
3266
|
-
description: 'Optional: The path to search in (default: current directory)'
|
|
4002
|
+
description: 'Optional: The path to search in (default: current directory)',
|
|
3267
4003
|
},
|
|
3268
4004
|
limit: {
|
|
3269
4005
|
type: 'integer',
|
|
3270
|
-
description: 'Optional: Maximum number of results to return (default: 1000)'
|
|
3271
|
-
}
|
|
4006
|
+
description: 'Optional: Maximum number of results to return (default: 1000)',
|
|
4007
|
+
},
|
|
3272
4008
|
},
|
|
3273
|
-
required: ['pattern']
|
|
4009
|
+
required: ['pattern'],
|
|
3274
4010
|
};
|
|
3275
4011
|
break;
|
|
3276
4012
|
|
|
@@ -3280,10 +4016,10 @@ export class ToolRegistry {
|
|
|
3280
4016
|
properties: {
|
|
3281
4017
|
filePath: {
|
|
3282
4018
|
type: 'string',
|
|
3283
|
-
description: 'The path to the file to delete'
|
|
3284
|
-
}
|
|
4019
|
+
description: 'The path to the file to delete',
|
|
4020
|
+
},
|
|
3285
4021
|
},
|
|
3286
|
-
required: ['filePath']
|
|
4022
|
+
required: ['filePath'],
|
|
3287
4023
|
};
|
|
3288
4024
|
break;
|
|
3289
4025
|
|
|
@@ -3293,14 +4029,14 @@ export class ToolRegistry {
|
|
|
3293
4029
|
properties: {
|
|
3294
4030
|
dirPath: {
|
|
3295
4031
|
type: 'string',
|
|
3296
|
-
description: 'The directory path to create'
|
|
4032
|
+
description: 'The directory path to create',
|
|
3297
4033
|
},
|
|
3298
4034
|
recursive: {
|
|
3299
4035
|
type: 'boolean',
|
|
3300
|
-
description: 'Optional: Create parent directories (default: true)'
|
|
3301
|
-
}
|
|
4036
|
+
description: 'Optional: Create parent directories (default: true)',
|
|
4037
|
+
},
|
|
3302
4038
|
},
|
|
3303
|
-
required: ['dirPath']
|
|
4039
|
+
required: ['dirPath'],
|
|
3304
4040
|
};
|
|
3305
4041
|
break;
|
|
3306
4042
|
|
|
@@ -3310,22 +4046,22 @@ export class ToolRegistry {
|
|
|
3310
4046
|
properties: {
|
|
3311
4047
|
file_path: {
|
|
3312
4048
|
type: 'string',
|
|
3313
|
-
description: 'The absolute path to the file to edit'
|
|
4049
|
+
description: 'The absolute path to the file to edit',
|
|
3314
4050
|
},
|
|
3315
4051
|
instruction: {
|
|
3316
4052
|
type: 'string',
|
|
3317
|
-
description: 'Description of what needs to be changed'
|
|
4053
|
+
description: 'Description of what needs to be changed',
|
|
3318
4054
|
},
|
|
3319
4055
|
old_string: {
|
|
3320
4056
|
type: 'string',
|
|
3321
|
-
description: 'The exact text to replace (supports fuzzy matching)'
|
|
4057
|
+
description: 'The exact text to replace (supports fuzzy matching)',
|
|
3322
4058
|
},
|
|
3323
4059
|
new_string: {
|
|
3324
4060
|
type: 'string',
|
|
3325
|
-
description: 'The new text to replace with'
|
|
3326
|
-
}
|
|
4061
|
+
description: 'The new text to replace with',
|
|
4062
|
+
},
|
|
3327
4063
|
},
|
|
3328
|
-
required: ['file_path', 'instruction', 'old_string', 'new_string']
|
|
4064
|
+
required: ['file_path', 'instruction', 'old_string', 'new_string'],
|
|
3329
4065
|
};
|
|
3330
4066
|
break;
|
|
3331
4067
|
|
|
@@ -3335,10 +4071,10 @@ export class ToolRegistry {
|
|
|
3335
4071
|
properties: {
|
|
3336
4072
|
query: {
|
|
3337
4073
|
type: 'string',
|
|
3338
|
-
description: 'The search query'
|
|
3339
|
-
}
|
|
4074
|
+
description: 'The search query',
|
|
4075
|
+
},
|
|
3340
4076
|
},
|
|
3341
|
-
required: ['query']
|
|
4077
|
+
required: ['query'],
|
|
3342
4078
|
};
|
|
3343
4079
|
break;
|
|
3344
4080
|
|
|
@@ -3354,14 +4090,17 @@ export class ToolRegistry {
|
|
|
3354
4090
|
properties: {
|
|
3355
4091
|
id: { type: 'string' },
|
|
3356
4092
|
task: { type: 'string' },
|
|
3357
|
-
status: {
|
|
3358
|
-
|
|
4093
|
+
status: {
|
|
4094
|
+
type: 'string',
|
|
4095
|
+
enum: ['pending', 'in_progress', 'completed', 'failed'],
|
|
4096
|
+
},
|
|
4097
|
+
priority: { type: 'string', enum: ['high', 'medium', 'low'] },
|
|
3359
4098
|
},
|
|
3360
|
-
required: ['id', 'task', 'status']
|
|
3361
|
-
}
|
|
3362
|
-
}
|
|
4099
|
+
required: ['id', 'task', 'status'],
|
|
4100
|
+
},
|
|
4101
|
+
},
|
|
3363
4102
|
},
|
|
3364
|
-
required: ['todos']
|
|
4103
|
+
required: ['todos'],
|
|
3365
4104
|
};
|
|
3366
4105
|
break;
|
|
3367
4106
|
|
|
@@ -3369,7 +4108,7 @@ export class ToolRegistry {
|
|
|
3369
4108
|
parameters = {
|
|
3370
4109
|
type: 'object',
|
|
3371
4110
|
properties: {},
|
|
3372
|
-
required: []
|
|
4111
|
+
required: [],
|
|
3373
4112
|
};
|
|
3374
4113
|
break;
|
|
3375
4114
|
|
|
@@ -3379,60 +4118,79 @@ export class ToolRegistry {
|
|
|
3379
4118
|
properties: {
|
|
3380
4119
|
description: {
|
|
3381
4120
|
type: 'string',
|
|
3382
|
-
description: 'Brief description of the task (3-5 words)'
|
|
4121
|
+
description: 'Brief description of the task (3-5 words)',
|
|
3383
4122
|
},
|
|
3384
4123
|
agents: {
|
|
3385
4124
|
type: 'array',
|
|
3386
|
-
description:
|
|
4125
|
+
description:
|
|
4126
|
+
'Optional: Array of agents to run in parallel for comprehensive analysis',
|
|
3387
4127
|
items: {
|
|
3388
4128
|
type: 'object',
|
|
3389
4129
|
properties: {
|
|
3390
4130
|
description: {
|
|
3391
4131
|
type: 'string',
|
|
3392
|
-
description: 'Brief description of the sub-agent task'
|
|
4132
|
+
description: 'Brief description of the sub-agent task',
|
|
3393
4133
|
},
|
|
3394
4134
|
prompt: {
|
|
3395
4135
|
type: 'string',
|
|
3396
|
-
description: 'The task for the sub-agent to perform'
|
|
4136
|
+
description: 'The task for the sub-agent to perform',
|
|
3397
4137
|
},
|
|
3398
4138
|
subagent_type: {
|
|
3399
4139
|
type: 'string',
|
|
3400
|
-
enum: [
|
|
3401
|
-
|
|
4140
|
+
enum: [
|
|
4141
|
+
'general-purpose',
|
|
4142
|
+
'plan-agent',
|
|
4143
|
+
'explore-agent',
|
|
4144
|
+
'frontend-tester',
|
|
4145
|
+
'code-reviewer',
|
|
4146
|
+
'frontend-developer',
|
|
4147
|
+
'backend-developer',
|
|
4148
|
+
],
|
|
4149
|
+
description: 'The type of specialized agent',
|
|
3402
4150
|
},
|
|
3403
4151
|
constraints: {
|
|
3404
4152
|
type: 'array',
|
|
3405
4153
|
items: { type: 'string' },
|
|
3406
|
-
description: 'Optional: Constraints or limitations'
|
|
3407
|
-
}
|
|
4154
|
+
description: 'Optional: Constraints or limitations',
|
|
4155
|
+
},
|
|
3408
4156
|
},
|
|
3409
|
-
required: ['description', 'prompt', 'subagent_type']
|
|
3410
|
-
}
|
|
4157
|
+
required: ['description', 'prompt', 'subagent_type'],
|
|
4158
|
+
},
|
|
3411
4159
|
},
|
|
3412
4160
|
prompt: {
|
|
3413
4161
|
type: 'string',
|
|
3414
|
-
description:
|
|
4162
|
+
description:
|
|
4163
|
+
'Optional: The task for the agent to perform (use agents for parallel execution)',
|
|
3415
4164
|
},
|
|
3416
4165
|
subagent_type: {
|
|
3417
4166
|
type: 'string',
|
|
3418
|
-
enum: [
|
|
3419
|
-
|
|
4167
|
+
enum: [
|
|
4168
|
+
'general-purpose',
|
|
4169
|
+
'plan-agent',
|
|
4170
|
+
'explore-agent',
|
|
4171
|
+
'frontend-tester',
|
|
4172
|
+
'code-reviewer',
|
|
4173
|
+
'frontend-developer',
|
|
4174
|
+
'backend-developer',
|
|
4175
|
+
],
|
|
4176
|
+
description:
|
|
4177
|
+
'Optional: The type of specialized agent (use agents for parallel execution)',
|
|
3420
4178
|
},
|
|
3421
4179
|
useContext: {
|
|
3422
4180
|
type: 'boolean',
|
|
3423
|
-
description: 'Optional: Include main agent context'
|
|
4181
|
+
description: 'Optional: Include main agent context',
|
|
3424
4182
|
},
|
|
3425
4183
|
outputFormat: {
|
|
3426
4184
|
type: 'string',
|
|
3427
|
-
description: 'Optional: Output format template'
|
|
4185
|
+
description: 'Optional: Output format template',
|
|
3428
4186
|
},
|
|
3429
4187
|
constraints: {
|
|
3430
4188
|
type: 'array',
|
|
3431
4189
|
items: { type: 'string' },
|
|
3432
|
-
description: 'Optional: Constraints or limitations'
|
|
3433
|
-
}
|
|
4190
|
+
description: 'Optional: Constraints or limitations',
|
|
4191
|
+
},
|
|
3434
4192
|
},
|
|
3435
|
-
required: ['description']
|
|
4193
|
+
required: ['description'],
|
|
3436
4194
|
};
|
|
3437
4195
|
break;
|
|
3438
4196
|
|
|
@@ -3442,14 +4200,14 @@ export class ToolRegistry {
|
|
|
3442
4200
|
properties: {
|
|
3443
4201
|
task_id: {
|
|
3444
4202
|
type: 'string',
|
|
3445
|
-
description: 'The ID of the task'
|
|
4203
|
+
description: 'The ID of the task',
|
|
3446
4204
|
},
|
|
3447
4205
|
poll_interval: {
|
|
3448
4206
|
type: 'number',
|
|
3449
|
-
description: 'Optional: Polling interval in seconds (default: 10)'
|
|
3450
|
-
}
|
|
4207
|
+
description: 'Optional: Polling interval in seconds (default: 10)',
|
|
4208
|
+
},
|
|
3451
4209
|
},
|
|
3452
|
-
required: ['task_id']
|
|
4210
|
+
required: ['task_id'],
|
|
3453
4211
|
};
|
|
3454
4212
|
break;
|
|
3455
4213
|
|
|
@@ -3459,10 +4217,10 @@ export class ToolRegistry {
|
|
|
3459
4217
|
properties: {
|
|
3460
4218
|
prompt: {
|
|
3461
4219
|
type: 'string',
|
|
3462
|
-
description: 'Prompt containing URL(s) and processing instructions'
|
|
3463
|
-
}
|
|
4220
|
+
description: 'Prompt containing URL(s) and processing instructions',
|
|
4221
|
+
},
|
|
3464
4222
|
},
|
|
3465
|
-
required: ['prompt']
|
|
4223
|
+
required: ['prompt'],
|
|
3466
4224
|
};
|
|
3467
4225
|
break;
|
|
3468
4226
|
|
|
@@ -3481,15 +4239,15 @@ export class ToolRegistry {
|
|
|
3481
4239
|
options: {
|
|
3482
4240
|
type: 'array',
|
|
3483
4241
|
items: { type: 'string' },
|
|
3484
|
-
description: 'Available choices (2-4 options)'
|
|
4242
|
+
description: 'Available choices (2-4 options)',
|
|
3485
4243
|
},
|
|
3486
|
-
multiSelect: { type: 'boolean' }
|
|
4244
|
+
multiSelect: { type: 'boolean' },
|
|
3487
4245
|
},
|
|
3488
|
-
required: ['question', 'header', 'options', 'multiSelect']
|
|
3489
|
-
}
|
|
3490
|
-
}
|
|
4246
|
+
required: ['question', 'header', 'options', 'multiSelect'],
|
|
4247
|
+
},
|
|
4248
|
+
},
|
|
3491
4249
|
},
|
|
3492
|
-
required: ['questions']
|
|
4250
|
+
required: ['questions'],
|
|
3493
4251
|
};
|
|
3494
4252
|
break;
|
|
3495
4253
|
|
|
@@ -3499,10 +4257,10 @@ export class ToolRegistry {
|
|
|
3499
4257
|
properties: {
|
|
3500
4258
|
fact: {
|
|
3501
4259
|
type: 'string',
|
|
3502
|
-
description: 'The specific fact to remember'
|
|
3503
|
-
}
|
|
4260
|
+
description: 'The specific fact to remember',
|
|
4261
|
+
},
|
|
3504
4262
|
},
|
|
3505
|
-
required: ['fact']
|
|
4263
|
+
required: ['fact'],
|
|
3506
4264
|
};
|
|
3507
4265
|
break;
|
|
3508
4266
|
|
|
@@ -3512,10 +4270,10 @@ export class ToolRegistry {
|
|
|
3512
4270
|
properties: {
|
|
3513
4271
|
plan: {
|
|
3514
4272
|
type: 'string',
|
|
3515
|
-
description: 'The plan to present'
|
|
3516
|
-
}
|
|
4273
|
+
description: 'The plan to present',
|
|
4274
|
+
},
|
|
3517
4275
|
},
|
|
3518
|
-
required: ['plan']
|
|
4276
|
+
required: ['plan'],
|
|
3519
4277
|
};
|
|
3520
4278
|
break;
|
|
3521
4279
|
|
|
@@ -3525,14 +4283,14 @@ export class ToolRegistry {
|
|
|
3525
4283
|
properties: {
|
|
3526
4284
|
file_path: {
|
|
3527
4285
|
type: 'string',
|
|
3528
|
-
description: 'The absolute path to the XML/HTML file'
|
|
4286
|
+
description: 'The absolute path to the XML/HTML file',
|
|
3529
4287
|
},
|
|
3530
4288
|
escape_all: {
|
|
3531
4289
|
type: 'boolean',
|
|
3532
|
-
description: 'Optional: Escape all special characters (default: false)'
|
|
3533
|
-
}
|
|
4290
|
+
description: 'Optional: Escape all special characters (default: false)',
|
|
4291
|
+
},
|
|
3534
4292
|
},
|
|
3535
|
-
required: ['file_path']
|
|
4293
|
+
required: ['file_path'],
|
|
3536
4294
|
};
|
|
3537
4295
|
break;
|
|
3538
4296
|
|
|
@@ -3542,27 +4300,27 @@ export class ToolRegistry {
|
|
|
3542
4300
|
properties: {
|
|
3543
4301
|
image_input: {
|
|
3544
4302
|
type: 'string',
|
|
3545
|
-
description: 'Image file path or base64 data'
|
|
4303
|
+
description: 'Image file path or base64 data',
|
|
3546
4304
|
},
|
|
3547
4305
|
prompt: {
|
|
3548
4306
|
type: 'string',
|
|
3549
|
-
description: 'Comprehensive VLM instruction'
|
|
4307
|
+
description: 'Comprehensive VLM instruction',
|
|
3550
4308
|
},
|
|
3551
4309
|
task_brief: {
|
|
3552
4310
|
type: 'string',
|
|
3553
|
-
description: 'Brief task description (max 15 words)'
|
|
4311
|
+
description: 'Brief task description (max 15 words)',
|
|
3554
4312
|
},
|
|
3555
4313
|
input_type: {
|
|
3556
4314
|
type: 'string',
|
|
3557
4315
|
enum: ['file_path', 'base64'],
|
|
3558
|
-
description: 'Input type (default: file_path)'
|
|
4316
|
+
description: 'Input type (default: file_path)',
|
|
3559
4317
|
},
|
|
3560
4318
|
mime_type: {
|
|
3561
4319
|
type: 'string',
|
|
3562
|
-
description: 'Optional: MIME type for base64 input'
|
|
3563
|
-
}
|
|
4320
|
+
description: 'Optional: MIME type for base64 input',
|
|
4321
|
+
},
|
|
3564
4322
|
},
|
|
3565
|
-
required: ['image_input', 'prompt']
|
|
4323
|
+
required: ['image_input', 'prompt'],
|
|
3566
4324
|
};
|
|
3567
4325
|
break;
|
|
3568
4326
|
|
|
@@ -3572,10 +4330,10 @@ export class ToolRegistry {
|
|
|
3572
4330
|
properties: {
|
|
3573
4331
|
skill: {
|
|
3574
4332
|
type: 'string',
|
|
3575
|
-
description: 'The skill name to execute'
|
|
3576
|
-
}
|
|
4333
|
+
description: 'The skill name to execute',
|
|
4334
|
+
},
|
|
3577
4335
|
},
|
|
3578
|
-
required: ['skill']
|
|
4336
|
+
required: ['skill'],
|
|
3579
4337
|
};
|
|
3580
4338
|
break;
|
|
3581
4339
|
|
|
@@ -3583,7 +4341,7 @@ export class ToolRegistry {
|
|
|
3583
4341
|
parameters = {
|
|
3584
4342
|
type: 'object',
|
|
3585
4343
|
properties: {},
|
|
3586
|
-
required: []
|
|
4344
|
+
required: [],
|
|
3587
4345
|
};
|
|
3588
4346
|
break;
|
|
3589
4347
|
|
|
@@ -3593,14 +4351,14 @@ export class ToolRegistry {
|
|
|
3593
4351
|
properties: {
|
|
3594
4352
|
skill: {
|
|
3595
4353
|
type: 'string',
|
|
3596
|
-
description: 'The skill name/id to get details for'
|
|
3597
|
-
}
|
|
4354
|
+
description: 'The skill name/id to get details for',
|
|
4355
|
+
},
|
|
3598
4356
|
},
|
|
3599
|
-
required: ['skill']
|
|
4357
|
+
required: ['skill'],
|
|
3600
4358
|
};
|
|
3601
4359
|
break;
|
|
3602
4360
|
|
|
3603
|
-
default:
|
|
4361
|
+
default: {
|
|
3604
4362
|
// For MCP tools, use their inputSchema; for other unknown tools, keep empty schema
|
|
3605
4363
|
const mcpTool = tool as any;
|
|
3606
4364
|
if (mcpTool._isMcpTool && mcpTool.inputSchema) {
|
|
@@ -3608,13 +4366,15 @@ export class ToolRegistry {
|
|
|
3608
4366
|
parameters = {
|
|
3609
4367
|
type: 'object',
|
|
3610
4368
|
properties: {},
|
|
3611
|
-
required: []
|
|
4369
|
+
required: [],
|
|
3612
4370
|
};
|
|
3613
4371
|
if (mcpTool.inputSchema.properties) {
|
|
3614
|
-
for (const [paramName, paramDef] of Object.entries<any>(
|
|
4372
|
+
for (const [paramName, paramDef] of Object.entries<any>(
|
|
4373
|
+
mcpTool.inputSchema.properties
|
|
4374
|
+
)) {
|
|
3615
4375
|
parameters.properties[paramName] = {
|
|
3616
4376
|
type: paramDef.type || 'string',
|
|
3617
|
-
description: paramDef.description || ''
|
|
4377
|
+
description: paramDef.description || '',
|
|
3618
4378
|
};
|
|
3619
4379
|
}
|
|
3620
4380
|
}
|
|
@@ -3625,9 +4385,10 @@ export class ToolRegistry {
|
|
|
3625
4385
|
parameters = {
|
|
3626
4386
|
type: 'object',
|
|
3627
4387
|
properties: {},
|
|
3628
|
-
required: []
|
|
4388
|
+
required: [],
|
|
3629
4389
|
};
|
|
3630
4390
|
}
|
|
4391
|
+
}
|
|
3631
4392
|
}
|
|
3632
4393
|
|
|
3633
4394
|
return {
|
|
@@ -3635,13 +4396,18 @@ export class ToolRegistry {
|
|
|
3635
4396
|
function: {
|
|
3636
4397
|
name: tool.name,
|
|
3637
4398
|
description: tool.description,
|
|
3638
|
-
parameters
|
|
3639
|
-
}
|
|
4399
|
+
parameters,
|
|
4400
|
+
},
|
|
3640
4401
|
};
|
|
3641
4402
|
});
|
|
3642
4403
|
}
|
|
3643
4404
|
|
|
3644
|
-
async execute(
|
|
4405
|
+
async execute(
|
|
4406
|
+
toolName: string,
|
|
4407
|
+
params: any,
|
|
4408
|
+
executionMode: ExecutionMode,
|
|
4409
|
+
indent: string = ''
|
|
4410
|
+
): Promise<any> {
|
|
3645
4411
|
// First try to execute as local tool
|
|
3646
4412
|
const localTool = this.tools.get(toolName);
|
|
3647
4413
|
if (localTool) {
|
|
@@ -3659,13 +4425,13 @@ export class ToolRegistry {
|
|
|
3659
4425
|
}
|
|
3660
4426
|
|
|
3661
4427
|
// Try to find MCP tool with just the tool name (try each server)
|
|
3662
|
-
for (const [fullName,
|
|
4428
|
+
for (const [fullName, _tool] of allMcpTools) {
|
|
3663
4429
|
// Split only on the first __ to preserve underscores in tool names
|
|
3664
4430
|
const firstUnderscoreIndex = fullName.indexOf('__');
|
|
3665
4431
|
if (firstUnderscoreIndex === -1) continue;
|
|
3666
|
-
const [
|
|
4432
|
+
const [_serverName, actualToolName] = [
|
|
3667
4433
|
fullName.substring(0, firstUnderscoreIndex),
|
|
3668
|
-
fullName.substring(firstUnderscoreIndex + 2)
|
|
4434
|
+
fullName.substring(firstUnderscoreIndex + 2),
|
|
3669
4435
|
];
|
|
3670
4436
|
if (actualToolName === toolName) {
|
|
3671
4437
|
return await this.executeMCPTool(fullName, params, executionMode, indent);
|
|
@@ -3679,7 +4445,12 @@ export class ToolRegistry {
|
|
|
3679
4445
|
/**
|
|
3680
4446
|
* Execute local tool (extracted for reuse)
|
|
3681
4447
|
*/
|
|
3682
|
-
private async executeLocalTool(
|
|
4448
|
+
private async executeLocalTool(
|
|
4449
|
+
toolName: string,
|
|
4450
|
+
params: any,
|
|
4451
|
+
executionMode: ExecutionMode,
|
|
4452
|
+
indent: string
|
|
4453
|
+
): Promise<any> {
|
|
3683
4454
|
const tool = this.get(toolName);
|
|
3684
4455
|
const cancellationManager = getCancellationManager();
|
|
3685
4456
|
|
|
@@ -3688,11 +4459,12 @@ export class ToolRegistry {
|
|
|
3688
4459
|
}
|
|
3689
4460
|
|
|
3690
4461
|
if (!tool.allowedModes.includes(executionMode)) {
|
|
3691
|
-
throw new Error(
|
|
3692
|
-
`Tool ${toolName} is not allowed in ${executionMode} mode`
|
|
3693
|
-
);
|
|
4462
|
+
throw new Error(`Tool ${toolName} is not allowed in ${executionMode} mode`);
|
|
3694
4463
|
}
|
|
3695
4464
|
|
|
4465
|
+
const isSdkMode = this._isSdkMode;
|
|
4466
|
+
const sdkOutputAdapter = this._sdkOutputAdapter;
|
|
4467
|
+
|
|
3696
4468
|
// Smart approval mode
|
|
3697
4469
|
if (executionMode === ExecutionMode.SMART) {
|
|
3698
4470
|
const debugMode = process.env.DEBUG === 'smart-approval';
|
|
@@ -3702,7 +4474,9 @@ export class ToolRegistry {
|
|
|
3702
4474
|
if (debugMode) {
|
|
3703
4475
|
const { getLogger } = await import('./logger.js');
|
|
3704
4476
|
const logger = getLogger();
|
|
3705
|
-
logger.debug(
|
|
4477
|
+
logger.debug(
|
|
4478
|
+
`[SmartApprovalEngine] Tool '${toolName}' bypassed smart approval completely`
|
|
4479
|
+
);
|
|
3706
4480
|
}
|
|
3707
4481
|
return await cancellationManager.withCancellation(
|
|
3708
4482
|
tool.execute(params, executionMode),
|
|
@@ -3718,8 +4492,15 @@ export class ToolRegistry {
|
|
|
3718
4492
|
const isRemoteMode = authConfig.type === AuthType.OAUTH_XAGENT;
|
|
3719
4493
|
if (isRemoteMode && toolName === 'InvokeSkill') {
|
|
3720
4494
|
console.log('');
|
|
3721
|
-
|
|
3722
|
-
|
|
4495
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4496
|
+
sdkOutputAdapter.outputInfo(`[Smart Mode] Remote mode: tool '${toolName}' auto-approved (remote LLM already approved)`);
|
|
4497
|
+
} else {
|
|
4498
|
+
console.log('');
|
|
4499
|
+
console.log(
|
|
4500
|
+
`${indent}${colors.success(`✅ [Smart Mode] Remote mode: tool '${toolName}' auto-approved (remote LLM already approved)`)}`
|
|
4501
|
+
);
|
|
4502
|
+
console.log('');
|
|
4503
|
+
}
|
|
3723
4504
|
return await cancellationManager.withCancellation(
|
|
3724
4505
|
tool.execute(params, executionMode),
|
|
3725
4506
|
`tool-${toolName}`
|
|
@@ -3730,49 +4511,81 @@ export class ToolRegistry {
|
|
|
3730
4511
|
|
|
3731
4512
|
const approvalEngine = getSmartApprovalEngine(debugMode);
|
|
3732
4513
|
|
|
4514
|
+
// Set SDK mode for approval engine if in SDK mode
|
|
4515
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4516
|
+
approvalEngine.setSdkMode(true, sdkOutputAdapter);
|
|
4517
|
+
}
|
|
4518
|
+
|
|
3733
4519
|
// Evaluate tool call
|
|
3734
4520
|
const result = await approvalEngine.evaluate({
|
|
3735
4521
|
toolName,
|
|
3736
4522
|
params,
|
|
3737
|
-
timestamp: Date.now()
|
|
4523
|
+
timestamp: Date.now(),
|
|
3738
4524
|
});
|
|
3739
4525
|
|
|
3740
4526
|
// Decide whether to execute based on approval result
|
|
3741
4527
|
if (result.decision === 'approved') {
|
|
3742
4528
|
// Whitelist or AI approval passed, execute directly
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
4529
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4530
|
+
sdkOutputAdapter.outputInfo(`[Smart Mode] Tool '${toolName}' passed approval, executing directly`);
|
|
4531
|
+
sdkOutputAdapter.outputInfo(`Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}, Latency: ${result.latency}ms`);
|
|
4532
|
+
} else {
|
|
4533
|
+
console.log('');
|
|
4534
|
+
console.log(
|
|
4535
|
+
`${indent}${colors.success(`✅ [Smart Mode] Tool '${toolName}' passed approval, executing directly`)}`
|
|
4536
|
+
);
|
|
4537
|
+
console.log(
|
|
4538
|
+
`${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`
|
|
4539
|
+
);
|
|
4540
|
+
console.log(`${indent}${colors.textDim(` Latency: ${result.latency}ms`)}`);
|
|
4541
|
+
console.log('');
|
|
4542
|
+
}
|
|
3748
4543
|
return await cancellationManager.withCancellation(
|
|
3749
4544
|
tool.execute(params, executionMode),
|
|
3750
4545
|
`tool-${toolName}`
|
|
3751
4546
|
);
|
|
3752
4547
|
} else if (result.decision === 'requires_confirmation') {
|
|
3753
4548
|
// Requires user confirmation
|
|
3754
|
-
const confirmed = await approvalEngine.requestConfirmation(result);
|
|
4549
|
+
const confirmed = await approvalEngine.requestConfirmation(result, toolName, params);
|
|
3755
4550
|
|
|
3756
4551
|
if (confirmed) {
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
4552
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4553
|
+
sdkOutputAdapter.outputInfo(`[Smart Mode] User confirmed execution of tool '${toolName}'`);
|
|
4554
|
+
} else {
|
|
4555
|
+
console.log('');
|
|
4556
|
+
console.log(
|
|
4557
|
+
`${indent}${colors.success(`✅ [Smart Mode] User confirmed execution of tool '${toolName}'`)}`
|
|
4558
|
+
);
|
|
4559
|
+
console.log('');
|
|
4560
|
+
}
|
|
3760
4561
|
return await cancellationManager.withCancellation(
|
|
3761
4562
|
tool.execute(params, executionMode),
|
|
3762
4563
|
`tool-${toolName}`
|
|
3763
4564
|
);
|
|
3764
4565
|
} else {
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
4566
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4567
|
+
sdkOutputAdapter.outputWarning(`[Smart Mode] User cancelled execution of tool '${toolName}'`);
|
|
4568
|
+
} else {
|
|
4569
|
+
console.log('');
|
|
4570
|
+
console.log(
|
|
4571
|
+
`${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled execution of tool '${toolName}'`)}`
|
|
4572
|
+
);
|
|
4573
|
+
console.log('');
|
|
4574
|
+
}
|
|
3768
4575
|
throw new Error(`Tool execution cancelled by user: ${toolName}`);
|
|
3769
4576
|
}
|
|
3770
4577
|
} else {
|
|
3771
4578
|
// Rejected execution
|
|
3772
|
-
|
|
3773
|
-
|
|
3774
|
-
|
|
3775
|
-
|
|
4579
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4580
|
+
sdkOutputAdapter.outputError(`[Smart Mode] Tool '${toolName}' execution rejected`, { reason: result.description });
|
|
4581
|
+
} else {
|
|
4582
|
+
console.log('');
|
|
4583
|
+
console.log(
|
|
4584
|
+
`${indent}${colors.error(`❌ [Smart Mode] Tool '${toolName}' execution rejected`)}`
|
|
4585
|
+
);
|
|
4586
|
+
console.log(`${indent}${colors.textDim(` Reason: ${result.description}`)}`);
|
|
4587
|
+
console.log('');
|
|
4588
|
+
}
|
|
3776
4589
|
throw new Error(`Tool execution rejected: ${toolName}`);
|
|
3777
4590
|
}
|
|
3778
4591
|
}
|
|
@@ -3787,7 +4600,12 @@ export class ToolRegistry {
|
|
|
3787
4600
|
/**
|
|
3788
4601
|
* Execute an MCP tool call
|
|
3789
4602
|
*/
|
|
3790
|
-
private async executeMCPTool(
|
|
4603
|
+
private async executeMCPTool(
|
|
4604
|
+
toolName: string,
|
|
4605
|
+
params: any,
|
|
4606
|
+
executionMode: ExecutionMode,
|
|
4607
|
+
indent: string = ''
|
|
4608
|
+
): Promise<any> {
|
|
3791
4609
|
const { getMCPManager } = await import('./mcp.js');
|
|
3792
4610
|
const mcpManager = getMCPManager();
|
|
3793
4611
|
const cancellationManager = getCancellationManager();
|
|
@@ -3796,14 +4614,21 @@ export class ToolRegistry {
|
|
|
3796
4614
|
const firstUnderscoreIndex = toolName.indexOf('__');
|
|
3797
4615
|
const serverName = toolName.substring(0, firstUnderscoreIndex);
|
|
3798
4616
|
const actualToolName = toolName.substring(firstUnderscoreIndex + 2);
|
|
3799
|
-
|
|
4617
|
+
|
|
3800
4618
|
// Get server info for display
|
|
3801
4619
|
const server = mcpManager.getServer(serverName);
|
|
3802
|
-
const
|
|
3803
|
-
|
|
4620
|
+
const _serverTools = server?.getToolNames() || [];
|
|
4621
|
+
|
|
4622
|
+
const isSdkMode = this._isSdkMode;
|
|
4623
|
+
const sdkOutputAdapter = this._sdkOutputAdapter;
|
|
4624
|
+
|
|
3804
4625
|
// Display tool call info
|
|
3805
|
-
|
|
3806
|
-
|
|
4626
|
+
if (!isSdkMode || !sdkOutputAdapter) {
|
|
4627
|
+
console.log('');
|
|
4628
|
+
console.log(
|
|
4629
|
+
`${indent}${colors.warning(`${icons.tool} MCP Tool Call: ${serverName}::${actualToolName}`)}`
|
|
4630
|
+
);
|
|
4631
|
+
}
|
|
3807
4632
|
|
|
3808
4633
|
// Smart approval mode for MCP tools
|
|
3809
4634
|
if (executionMode === ExecutionMode.SMART) {
|
|
@@ -3816,29 +4641,63 @@ export class ToolRegistry {
|
|
|
3816
4641
|
|
|
3817
4642
|
// Remote mode: remote LLM has already approved the tool, auto-approve
|
|
3818
4643
|
if (isRemoteMode) {
|
|
3819
|
-
|
|
4644
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4645
|
+
sdkOutputAdapter.outputInfo(`[Smart Mode] Remote mode: MCP tool '${serverName}::${actualToolName}' auto-approved`);
|
|
4646
|
+
} else {
|
|
4647
|
+
console.log(
|
|
4648
|
+
`${indent}${colors.success(`✅ [Smart Mode] Remote mode: MCP tool '${serverName}::${actualToolName}' auto-approved`)}`
|
|
4649
|
+
);
|
|
4650
|
+
}
|
|
3820
4651
|
} else {
|
|
3821
4652
|
const approvalEngine = getSmartApprovalEngine(debugMode);
|
|
3822
4653
|
|
|
4654
|
+
// Set SDK mode for approval engine if in SDK mode
|
|
4655
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4656
|
+
approvalEngine.setSdkMode(true, sdkOutputAdapter);
|
|
4657
|
+
}
|
|
4658
|
+
|
|
3823
4659
|
// Evaluate MCP tool call
|
|
3824
4660
|
const result = await approvalEngine.evaluate({
|
|
3825
4661
|
toolName: `MCP[${serverName}]::${actualToolName}`,
|
|
3826
4662
|
params,
|
|
3827
|
-
timestamp: Date.now()
|
|
4663
|
+
timestamp: Date.now(),
|
|
3828
4664
|
});
|
|
3829
4665
|
|
|
3830
4666
|
if (result.decision === 'approved') {
|
|
3831
|
-
|
|
3832
|
-
|
|
4667
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4668
|
+
sdkOutputAdapter.outputInfo(`[Smart Mode] MCP tool '${serverName}::${actualToolName}' passed approval`);
|
|
4669
|
+
sdkOutputAdapter.outputInfo(`Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`);
|
|
4670
|
+
} else {
|
|
4671
|
+
console.log(
|
|
4672
|
+
`${indent}${colors.success(`✅ [Smart Mode] MCP tool '${serverName}::${actualToolName}' passed approval`)}`
|
|
4673
|
+
);
|
|
4674
|
+
console.log(
|
|
4675
|
+
`${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`
|
|
4676
|
+
);
|
|
4677
|
+
}
|
|
3833
4678
|
} else if (result.decision === 'requires_confirmation') {
|
|
3834
|
-
const confirmed = await approvalEngine.requestConfirmation(
|
|
4679
|
+
const confirmed = await approvalEngine.requestConfirmation(
|
|
4680
|
+
result,
|
|
4681
|
+
`MCP[${serverName}]::${actualToolName}`,
|
|
4682
|
+
params
|
|
4683
|
+
);
|
|
3835
4684
|
if (!confirmed) {
|
|
3836
|
-
|
|
4685
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4686
|
+
sdkOutputAdapter.outputWarning(`[Smart Mode] User cancelled MCP tool execution`);
|
|
4687
|
+
} else {
|
|
4688
|
+
console.log(
|
|
4689
|
+
`${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled MCP tool execution`)}`
|
|
4690
|
+
);
|
|
4691
|
+
}
|
|
3837
4692
|
throw new Error(`Tool execution cancelled by user: ${toolName}`);
|
|
3838
4693
|
}
|
|
3839
4694
|
} else {
|
|
3840
|
-
|
|
3841
|
-
|
|
4695
|
+
if (isSdkMode && sdkOutputAdapter) {
|
|
4696
|
+
sdkOutputAdapter.outputError(`[Smart Mode] MCP tool execution rejected`, { reason: result.description });
|
|
4697
|
+
} else {
|
|
4698
|
+
console.log(`${indent}${colors.error(`❌ [Smart Mode] MCP tool execution rejected`)}`);
|
|
4699
|
+
console.log(`${indent}${colors.textDim(` Reason: ${result.description}`)}`);
|
|
4700
|
+
}
|
|
3842
4701
|
throw new Error(`Tool execution rejected: ${toolName}`);
|
|
3843
4702
|
}
|
|
3844
4703
|
}
|