@xagent-ai/cli 1.2.2 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.md +38 -38
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
- package/.github/release.yml +76 -0
- package/.github/workflows/ci.yml +75 -0
- package/.github/workflows/release.yml +103 -0
- package/.gitmodules +3 -3
- package/README.md +326 -280
- package/README_CN.md +325 -279
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +7 -3
- package/dist/agents.js.map +1 -1
- package/dist/ai-client/factory.d.ts +40 -0
- package/dist/ai-client/factory.d.ts.map +1 -0
- package/dist/ai-client/factory.js +100 -0
- package/dist/ai-client/factory.js.map +1 -0
- package/dist/ai-client/index.d.ts +20 -0
- package/dist/ai-client/index.d.ts.map +1 -0
- package/dist/ai-client/index.js +49 -0
- package/dist/ai-client/index.js.map +1 -0
- package/dist/ai-client/providers/anthropic.d.ts +57 -0
- package/dist/ai-client/providers/anthropic.d.ts.map +1 -0
- package/dist/ai-client/providers/anthropic.js +406 -0
- package/dist/ai-client/providers/anthropic.js.map +1 -0
- package/dist/ai-client/providers/openai.d.ts +57 -0
- package/dist/ai-client/providers/openai.d.ts.map +1 -0
- package/dist/ai-client/providers/openai.js +290 -0
- package/dist/ai-client/providers/openai.js.map +1 -0
- package/dist/ai-client/providers/remote.d.ts +110 -0
- package/dist/ai-client/providers/remote.d.ts.map +1 -0
- package/dist/ai-client/providers/remote.js +352 -0
- package/dist/ai-client/providers/remote.js.map +1 -0
- package/dist/ai-client/registry.d.ts +51 -0
- package/dist/ai-client/registry.d.ts.map +1 -0
- package/dist/ai-client/registry.js +81 -0
- package/dist/ai-client/registry.js.map +1 -0
- package/dist/ai-client/types.d.ts +274 -0
- package/dist/ai-client/types.d.ts.map +1 -0
- package/dist/ai-client/types.js +90 -0
- package/dist/ai-client/types.js.map +1 -0
- package/dist/ai-client-factory.d.ts +62 -0
- package/dist/ai-client-factory.d.ts.map +1 -0
- package/dist/ai-client-factory.js +157 -0
- package/dist/ai-client-factory.js.map +1 -0
- package/dist/auth.d.ts +23 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +164 -174
- package/dist/auth.js.map +1 -1
- package/dist/cancellation.d.ts +5 -4
- package/dist/cancellation.d.ts.map +1 -1
- package/dist/cancellation.js +53 -32
- package/dist/cancellation.js.map +1 -1
- package/dist/checkpoint.d.ts +2 -1
- package/dist/checkpoint.d.ts.map +1 -1
- package/dist/checkpoint.js +39 -6
- package/dist/checkpoint.js.map +1 -1
- package/dist/cli.js +742 -29
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +10 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +62 -25
- package/dist/config.js.map +1 -1
- package/dist/context-compressor.d.ts +82 -18
- package/dist/context-compressor.d.ts.map +1 -1
- package/dist/context-compressor.js +718 -154
- package/dist/context-compressor.js.map +1 -1
- package/dist/conversation.d.ts +1 -1
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +8 -7
- package/dist/conversation.js.map +1 -1
- package/dist/gui-subagent/action-parser/actionParser.d.ts.map +1 -1
- package/dist/gui-subagent/action-parser/actionParser.js +6 -4
- package/dist/gui-subagent/action-parser/actionParser.js.map +1 -1
- package/dist/gui-subagent/agent/gui-agent.d.ts +39 -2
- package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -1
- package/dist/gui-subagent/agent/gui-agent.js +189 -74
- package/dist/gui-subagent/agent/gui-agent.js.map +1 -1
- package/dist/gui-subagent/index.d.ts +23 -1
- package/dist/gui-subagent/index.d.ts.map +1 -1
- package/dist/gui-subagent/index.js +6 -0
- package/dist/gui-subagent/index.js.map +1 -1
- package/dist/gui-subagent/operator/base-operator.d.ts.map +1 -1
- package/dist/gui-subagent/operator/base-operator.js +0 -1
- package/dist/gui-subagent/operator/base-operator.js.map +1 -1
- package/dist/gui-subagent/operator/computer-operator.d.ts.map +1 -1
- package/dist/gui-subagent/operator/computer-operator.js +31 -8
- package/dist/gui-subagent/operator/computer-operator.js.map +1 -1
- package/dist/gui-subagent/types/actions.d.ts +1 -1
- package/dist/gui-subagent/types/actions.d.ts.map +1 -1
- package/dist/gui-subagent/types/actions.js +0 -1
- package/dist/gui-subagent/types/actions.js.map +1 -1
- package/dist/gui-subagent/types/operator.d.ts +1 -1
- package/dist/gui-subagent/types/operator.d.ts.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/input-processor.d.ts.map +1 -1
- package/dist/input-processor.js +8 -5
- package/dist/input-processor.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +1 -1
- package/dist/logger.js.map +1 -1
- package/dist/mcp.d.ts +7 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +157 -49
- package/dist/mcp.js.map +1 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +3 -3
- package/dist/memory.js.map +1 -1
- package/dist/output-util.d.ts +27 -0
- package/dist/output-util.d.ts.map +1 -0
- package/dist/output-util.js +74 -0
- package/dist/output-util.js.map +1 -0
- package/dist/retry.js +1 -1
- package/dist/retry.js.map +1 -1
- package/dist/ripgrep.d.ts +29 -0
- package/dist/ripgrep.d.ts.map +1 -0
- package/dist/ripgrep.js +294 -0
- package/dist/ripgrep.js.map +1 -0
- package/dist/sdk-output-adapter.d.ts +34 -1
- package/dist/sdk-output-adapter.d.ts.map +1 -1
- package/dist/sdk-output-adapter.js +67 -2
- package/dist/sdk-output-adapter.js.map +1 -1
- package/dist/sdk-session.d.ts.map +1 -1
- package/dist/sdk-session.js +2 -0
- package/dist/sdk-session.js.map +1 -1
- package/dist/session-manager.js +3 -3
- package/dist/session-manager.js.map +1 -1
- package/dist/session.d.ts +116 -6
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +1416 -448
- package/dist/session.js.map +1 -1
- package/dist/shell.d.ts +33 -0
- package/dist/shell.d.ts.map +1 -0
- package/dist/shell.js +126 -0
- package/dist/shell.js.map +1 -0
- package/dist/skill-installer.d.ts +38 -0
- package/dist/skill-installer.d.ts.map +1 -0
- package/dist/skill-installer.js +447 -0
- package/dist/skill-installer.js.map +1 -0
- package/dist/skill-invoker.d.ts +8 -2
- package/dist/skill-invoker.d.ts.map +1 -1
- package/dist/skill-invoker.js +36 -15
- package/dist/skill-invoker.js.map +1 -1
- package/dist/skill-loader.d.ts +8 -3
- package/dist/skill-loader.d.ts.map +1 -1
- package/dist/skill-loader.js +51 -48
- package/dist/skill-loader.js.map +1 -1
- package/dist/skill-manager.d.ts +85 -0
- package/dist/skill-manager.d.ts.map +1 -0
- package/dist/skill-manager.js +341 -0
- package/dist/skill-manager.js.map +1 -0
- package/dist/slash-commands.d.ts +39 -2
- package/dist/slash-commands.d.ts.map +1 -1
- package/dist/slash-commands.js +934 -305
- package/dist/slash-commands.js.map +1 -1
- package/dist/smart-approval.d.ts +20 -1
- package/dist/smart-approval.d.ts.map +1 -1
- package/dist/smart-approval.js +125 -56
- package/dist/smart-approval.js.map +1 -1
- package/dist/system-prompt-generator.d.ts +6 -0
- package/dist/system-prompt-generator.d.ts.map +1 -1
- package/dist/system-prompt-generator.js +86 -36
- package/dist/system-prompt-generator.js.map +1 -1
- package/dist/terminal.d.ts +28 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +82 -0
- package/dist/terminal.js.map +1 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +8 -7
- package/dist/theme.js.map +1 -1
- package/dist/tools.d.ts +38 -7
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +1249 -617
- package/dist/tools.js.map +1 -1
- package/dist/truncate.d.ts +55 -0
- package/dist/truncate.d.ts.map +1 -0
- package/dist/truncate.js +130 -0
- package/dist/truncate.js.map +1 -0
- package/dist/types.d.ts +84 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +49 -0
- package/dist/types.js.map +1 -1
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +28 -36
- package/dist/update.js.map +1 -1
- package/dist/workflow.d.ts +5 -1
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +61 -49
- package/dist/workflow.js.map +1 -1
- package/docs/architecture/mcp-integration-guide.md +304 -194
- package/docs/architecture/overview.md +169 -169
- package/docs/architecture/tool-system-design.md +134 -134
- package/docs/cli/commands.md +349 -238
- package/docs/smart-mode.md +281 -281
- package/docs/third-party-models.md +440 -439
- package/find-skills/SKILL.md +133 -0
- package/package.json +91 -90
- package/scripts/install-ripgrep.js +241 -0
- package/src/agents.ts +7 -3
- package/src/ai-client/factory.ts +116 -0
- package/src/ai-client/index.ts +61 -0
- package/src/ai-client/providers/anthropic.ts +475 -0
- package/src/ai-client/providers/openai.ts +348 -0
- package/src/ai-client/providers/remote.ts +439 -0
- package/src/ai-client/registry.ts +97 -0
- package/src/ai-client/types.ts +364 -0
- package/src/ai-client-factory.ts +204 -0
- package/src/auth.ts +661 -614
- package/src/cancellation.ts +202 -176
- package/src/checkpoint.ts +255 -219
- package/src/cli.ts +1523 -743
- package/src/config.ts +341 -297
- package/src/context-compressor.ts +987 -290
- package/src/conversation.ts +290 -288
- package/src/gui-subagent/action-parser/actionParser.ts +318 -315
- package/src/gui-subagent/action-parser/constants.ts +14 -14
- package/src/gui-subagent/action-parser/index.ts +8 -8
- package/src/gui-subagent/action-parser/types.ts +31 -31
- package/src/gui-subagent/agent/gui-agent.ts +1234 -1089
- package/src/gui-subagent/agent/index.ts +5 -5
- package/src/gui-subagent/index.ts +185 -163
- package/src/gui-subagent/operator/base-operator.ts +244 -245
- package/src/gui-subagent/operator/computer-operator.ts +541 -520
- package/src/gui-subagent/operator/index.ts +6 -6
- package/src/gui-subagent/types/actions.ts +260 -262
- package/src/gui-subagent/types/index.ts +6 -6
- package/src/gui-subagent/types/operator.ts +106 -106
- package/src/gui-subagent/utils.ts +51 -51
- package/src/index.ts +17 -18
- package/src/input-processor.ts +8 -5
- package/src/logger.ts +436 -438
- package/src/mcp.ts +793 -682
- package/src/memory.ts +343 -344
- package/src/output-util.ts +80 -0
- package/src/retry.ts +1 -1
- package/src/ripgrep.ts +370 -0
- package/src/sdk-output-adapter.ts +842 -0
- package/src/sdk-session.ts +62 -0
- package/src/session-manager.ts +308 -308
- package/src/session.ts +1775 -573
- package/src/shell.ts +134 -0
- package/src/skill-installer.ts +518 -0
- package/src/skill-invoker.ts +959 -935
- package/src/skill-loader.ts +501 -496
- package/src/skill-manager.ts +385 -0
- package/src/slash-commands.ts +2189 -1389
- package/src/smart-approval.ts +193 -74
- package/src/system-prompt-generator.ts +91 -36
- package/src/terminal.ts +96 -0
- package/src/theme.ts +739 -738
- package/src/tools.ts +1790 -931
- package/src/truncate.ts +173 -0
- package/src/types.ts +337 -198
- package/src/update.ts +33 -40
- package/src/workflow.ts +521 -508
- package/test/cli-launch.test.ts +279 -0
- package/tsconfig.json +22 -22
- package/vitest.config.ts +21 -19
- package/dist/ai-client.d.ts +0 -86
- package/dist/ai-client.d.ts.map +0 -1
- package/dist/ai-client.js +0 -1372
- package/dist/ai-client.js.map +0 -1
- package/dist/gui-subagent/operator/browser-operator.d.ts +0 -36
- package/dist/gui-subagent/operator/browser-operator.d.ts.map +0 -1
- package/dist/gui-subagent/operator/browser-operator.js +0 -306
- package/dist/gui-subagent/operator/browser-operator.js.map +0 -1
- package/dist/gui-subagent/operator/desktop-operator.d.ts +0 -55
- package/dist/gui-subagent/operator/desktop-operator.d.ts.map +0 -1
- package/dist/gui-subagent/operator/desktop-operator.js +0 -527
- package/dist/gui-subagent/operator/desktop-operator.js.map +0 -1
- package/dist/hook.d.ts +0 -73
- package/dist/hook.d.ts.map +0 -1
- package/dist/hook.js +0 -156
- package/dist/hook.js.map +0 -1
- package/dist/input-history.d.ts +0 -24
- package/dist/input-history.d.ts.map +0 -1
- package/dist/input-history.js +0 -94
- package/dist/input-history.js.map +0 -1
- package/dist/keyboard-manager.d.ts +0 -151
- package/dist/keyboard-manager.d.ts.map +0 -1
- package/dist/keyboard-manager.js +0 -396
- package/dist/keyboard-manager.js.map +0 -1
- package/dist/print-system-prompt.d.ts +0 -2
- package/dist/print-system-prompt.d.ts.map +0 -1
- package/dist/print-system-prompt.js +0 -40
- package/dist/print-system-prompt.js.map +0 -1
- package/dist/remote-ai-client.d.ts +0 -104
- package/dist/remote-ai-client.d.ts.map +0 -1
- package/dist/remote-ai-client.js +0 -552
- package/dist/remote-ai-client.js.map +0 -1
- package/dist/sdk-session-v2.d.ts +0 -13
- package/dist/sdk-session-v2.d.ts.map +0 -1
- package/dist/sdk-session-v2.js +0 -46
- package/dist/sdk-session-v2.js.map +0 -1
- package/dist/test-boundary-conditions.d.ts.map +0 -1
- package/dist/test-boundary-conditions.js.map +0 -1
- package/dist/test-cancellation-fix.d.ts.map +0 -1
- package/dist/test-cancellation-fix.js.map +0 -1
- package/dist/test-input-history.d.ts.map +0 -1
- package/dist/test-input-history.js.map +0 -1
- package/dist/test-interaction-flow.d.ts.map +0 -1
- package/dist/test-interaction-flow.js.map +0 -1
- package/dist/test-quick.d.ts.map +0 -1
- package/dist/test-quick.js.map +0 -1
- package/dist/test-user-interaction.d.ts.map +0 -1
- package/dist/test-user-interaction.js.map +0 -1
- package/dist/tools/edit-diff.d.ts +0 -32
- package/dist/tools/edit-diff.d.ts.map +0 -1
- package/dist/tools/edit-diff.js +0 -185
- package/dist/tools/edit-diff.js.map +0 -1
- package/dist/tools/edit.d.ts +0 -11
- package/dist/tools/edit.d.ts.map +0 -1
- package/dist/tools/edit.js +0 -129
- package/dist/tools/edit.js.map +0 -1
- package/dist/unified-session.d.ts +0 -42
- package/dist/unified-session.d.ts.map +0 -1
- package/dist/unified-session.js +0 -271
- package/dist/unified-session.js.map +0 -1
- package/skills/.claude-plugin/marketplace.json +0 -45
- package/skills/README.md +0 -94
- package/skills/THIRD_PARTY_NOTICES.md +0 -405
- package/skills/skills/algorithmic-art/LICENSE.txt +0 -202
- package/skills/skills/algorithmic-art/SKILL.md +0 -405
- package/skills/skills/algorithmic-art/templates/generator_template.js +0 -223
- package/skills/skills/algorithmic-art/templates/viewer.html +0 -599
- package/skills/skills/brand-guidelines/LICENSE.txt +0 -202
- package/skills/skills/brand-guidelines/SKILL.md +0 -73
- package/skills/skills/canvas-design/LICENSE.txt +0 -202
- package/skills/skills/canvas-design/SKILL.md +0 -130
- package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +0 -94
- package/skills/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Jura-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Lora-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
- package/skills/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +0 -93
- package/skills/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
- package/skills/skills/doc-coauthoring/SKILL.md +0 -375
- package/skills/skills/docx/LICENSE.txt +0 -30
- package/skills/skills/docx/SKILL.md +0 -197
- package/skills/skills/docx/docx-js.md +0 -350
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/skills/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
- package/skills/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
- package/skills/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
- package/skills/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
- package/skills/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/skills/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/skills/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/skills/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/skills/skills/docx/ooxml/scripts/pack.py +0 -159
- package/skills/skills/docx/ooxml/scripts/unpack.py +0 -29
- package/skills/skills/docx/ooxml/scripts/validate.py +0 -69
- package/skills/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
- package/skills/skills/docx/ooxml/scripts/validation/base.py +0 -951
- package/skills/skills/docx/ooxml/scripts/validation/docx.py +0 -274
- package/skills/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
- package/skills/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
- package/skills/skills/docx/ooxml.md +0 -610
- package/skills/skills/docx/scripts/__init__.py +0 -1
- package/skills/skills/docx/scripts/document.py +0 -1276
- package/skills/skills/docx/scripts/templates/comments.xml +0 -3
- package/skills/skills/docx/scripts/templates/commentsExtended.xml +0 -3
- package/skills/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
- package/skills/skills/docx/scripts/templates/commentsIds.xml +0 -3
- package/skills/skills/docx/scripts/templates/people.xml +0 -3
- package/skills/skills/docx/scripts/utilities.py +0 -374
- package/skills/skills/frontend-design/LICENSE.txt +0 -177
- package/skills/skills/frontend-design/SKILL.md +0 -42
- package/skills/skills/internal-comms/LICENSE.txt +0 -202
- package/skills/skills/internal-comms/SKILL.md +0 -32
- package/skills/skills/internal-comms/examples/3p-updates.md +0 -47
- package/skills/skills/internal-comms/examples/company-newsletter.md +0 -65
- package/skills/skills/internal-comms/examples/faq-answers.md +0 -30
- package/skills/skills/internal-comms/examples/general-comms.md +0 -16
- package/skills/skills/mcp-builder/LICENSE.txt +0 -202
- package/skills/skills/mcp-builder/SKILL.md +0 -236
- package/skills/skills/mcp-builder/reference/evaluation.md +0 -602
- package/skills/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
- package/skills/skills/mcp-builder/reference/node_mcp_server.md +0 -970
- package/skills/skills/mcp-builder/reference/python_mcp_server.md +0 -719
- package/skills/skills/mcp-builder/scripts/connections.py +0 -151
- package/skills/skills/mcp-builder/scripts/evaluation.py +0 -373
- package/skills/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
- package/skills/skills/mcp-builder/scripts/requirements.txt +0 -2
- package/skills/skills/pdf/LICENSE.txt +0 -30
- package/skills/skills/pdf/SKILL.md +0 -294
- package/skills/skills/pdf/forms.md +0 -205
- package/skills/skills/pdf/reference.md +0 -612
- package/skills/skills/pdf/scripts/check_bounding_boxes.py +0 -70
- package/skills/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
- package/skills/skills/pdf/scripts/check_fillable_fields.py +0 -12
- package/skills/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
- package/skills/skills/pdf/scripts/create_validation_image.py +0 -41
- package/skills/skills/pdf/scripts/extract_form_field_info.py +0 -152
- package/skills/skills/pdf/scripts/fill_fillable_fields.py +0 -114
- package/skills/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
- package/skills/skills/pptx/LICENSE.txt +0 -30
- package/skills/skills/pptx/SKILL.md +0 -484
- package/skills/skills/pptx/html2pptx.md +0 -625
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
- package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
- package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
- package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
- package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
- package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
- package/skills/skills/pptx/ooxml/schemas/mce/mc.xsd +0 -75
- package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
- package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
- package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
- package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
- package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
- package/skills/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
- package/skills/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
- package/skills/skills/pptx/ooxml/scripts/pack.py +0 -159
- package/skills/skills/pptx/ooxml/scripts/unpack.py +0 -29
- package/skills/skills/pptx/ooxml/scripts/validate.py +0 -69
- package/skills/skills/pptx/ooxml/scripts/validation/__init__.py +0 -15
- package/skills/skills/pptx/ooxml/scripts/validation/base.py +0 -951
- package/skills/skills/pptx/ooxml/scripts/validation/docx.py +0 -274
- package/skills/skills/pptx/ooxml/scripts/validation/pptx.py +0 -315
- package/skills/skills/pptx/ooxml/scripts/validation/redlining.py +0 -279
- package/skills/skills/pptx/ooxml.md +0 -427
- package/skills/skills/pptx/scripts/html2pptx.js +0 -979
- package/skills/skills/pptx/scripts/inventory.py +0 -1020
- package/skills/skills/pptx/scripts/rearrange.py +0 -231
- package/skills/skills/pptx/scripts/replace.py +0 -385
- package/skills/skills/pptx/scripts/thumbnail.py +0 -450
- package/skills/skills/skill-creator/LICENSE.txt +0 -202
- package/skills/skills/skill-creator/SKILL.md +0 -356
- package/skills/skills/skill-creator/references/output-patterns.md +0 -82
- package/skills/skills/skill-creator/references/workflows.md +0 -28
- package/skills/skills/skill-creator/scripts/init_skill.py +0 -303
- package/skills/skills/skill-creator/scripts/package_skill.py +0 -110
- package/skills/skills/skill-creator/scripts/quick_validate.py +0 -95
- package/skills/skills/slack-gif-creator/LICENSE.txt +0 -202
- package/skills/skills/slack-gif-creator/SKILL.md +0 -254
- package/skills/skills/slack-gif-creator/core/easing.py +0 -234
- package/skills/skills/slack-gif-creator/core/frame_composer.py +0 -176
- package/skills/skills/slack-gif-creator/core/gif_builder.py +0 -269
- package/skills/skills/slack-gif-creator/core/validators.py +0 -136
- package/skills/skills/slack-gif-creator/requirements.txt +0 -4
- package/skills/skills/theme-factory/LICENSE.txt +0 -202
- package/skills/skills/theme-factory/SKILL.md +0 -59
- package/skills/skills/theme-factory/theme-showcase.pdf +0 -0
- package/skills/skills/theme-factory/themes/arctic-frost.md +0 -19
- package/skills/skills/theme-factory/themes/botanical-garden.md +0 -19
- package/skills/skills/theme-factory/themes/desert-rose.md +0 -19
- package/skills/skills/theme-factory/themes/forest-canopy.md +0 -19
- package/skills/skills/theme-factory/themes/golden-hour.md +0 -19
- package/skills/skills/theme-factory/themes/midnight-galaxy.md +0 -19
- package/skills/skills/theme-factory/themes/modern-minimalist.md +0 -19
- package/skills/skills/theme-factory/themes/ocean-depths.md +0 -19
- package/skills/skills/theme-factory/themes/sunset-boulevard.md +0 -19
- package/skills/skills/theme-factory/themes/tech-innovation.md +0 -19
- package/skills/skills/web-artifacts-builder/LICENSE.txt +0 -202
- package/skills/skills/web-artifacts-builder/SKILL.md +0 -74
- package/skills/skills/web-artifacts-builder/scripts/bundle-artifact.sh +0 -54
- package/skills/skills/web-artifacts-builder/scripts/init-artifact.sh +0 -322
- package/skills/skills/webapp-testing/LICENSE.txt +0 -202
- package/skills/skills/webapp-testing/SKILL.md +0 -96
- package/skills/skills/webapp-testing/examples/console_logging.py +0 -35
- package/skills/skills/webapp-testing/examples/element_discovery.py +0 -40
- package/skills/skills/webapp-testing/examples/static_html_automation.py +0 -33
- package/skills/skills/webapp-testing/scripts/with_server.py +0 -106
- package/skills/skills/xlsx/LICENSE.txt +0 -30
- package/skills/skills/xlsx/SKILL.md +0 -289
- package/skills/skills/xlsx/recalc.py +0 -178
- package/skills/spec/agent-skills-spec.md +0 -3
- package/skills/template/SKILL.md +0 -6
- package/src/ai-client.ts +0 -1560
- package/src/remote-ai-client.ts +0 -664
- /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
package/dist/session.js
CHANGED
|
@@ -5,11 +5,18 @@ import axios from 'axios';
|
|
|
5
5
|
import crypto from 'crypto';
|
|
6
6
|
import ora from 'ora';
|
|
7
7
|
import { createRequire } from 'module';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import fsPromises from 'fs/promises';
|
|
12
|
+
import os from 'os';
|
|
8
13
|
const require = createRequire(import.meta.url);
|
|
9
14
|
const packageJson = require('../package.json');
|
|
10
|
-
import { ExecutionMode, AuthType } from './types.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
15
|
+
import { ExecutionMode, AuthType, } from './types.js';
|
|
16
|
+
import { createAIClient } from './ai-client-factory.js';
|
|
17
|
+
import { detectThinkingKeywords, getThinkingTokens } from './ai-client/types.js';
|
|
18
|
+
import { TokenInvalidError } from './ai-client/types.js';
|
|
19
|
+
import { fetchDefaultModels } from './ai-client/providers/remote.js';
|
|
13
20
|
import { getConfigManager } from './config.js';
|
|
14
21
|
import { AuthService, selectAuthType } from './auth.js';
|
|
15
22
|
import { getToolRegistry } from './tools.js';
|
|
@@ -21,10 +28,11 @@ import { getConversationManager } from './conversation.js';
|
|
|
21
28
|
import { getSessionManager } from './session-manager.js';
|
|
22
29
|
import { SlashCommandHandler, parseInput } from './slash-commands.js';
|
|
23
30
|
import { SystemPromptGenerator } from './system-prompt-generator.js';
|
|
24
|
-
import { theme, icons, colors, styleHelpers, renderMarkdown, renderDiff, renderLines } from './theme.js';
|
|
31
|
+
import { theme, icons, colors, styleHelpers, renderMarkdown, renderDiff, renderLines, } from './theme.js';
|
|
25
32
|
import { getCancellationManager } from './cancellation.js';
|
|
26
|
-
import { getContextCompressor } from './context-compressor.js';
|
|
33
|
+
import { getContextCompressor, } from './context-compressor.js';
|
|
27
34
|
import { getLogger } from './logger.js';
|
|
35
|
+
import { ensureTtySane, setupEscKeyHandler } from './terminal.js';
|
|
28
36
|
const logger = getLogger();
|
|
29
37
|
export class InteractiveSession {
|
|
30
38
|
conversationManager;
|
|
@@ -33,7 +41,7 @@ export class InteractiveSession {
|
|
|
33
41
|
aiClient = null;
|
|
34
42
|
remoteAIClient = null;
|
|
35
43
|
conversation = [];
|
|
36
|
-
|
|
44
|
+
tool_calls = [];
|
|
37
45
|
executionMode;
|
|
38
46
|
slashCommandHandler;
|
|
39
47
|
configManager;
|
|
@@ -50,10 +58,21 @@ export class InteractiveSession {
|
|
|
50
58
|
currentTaskId = null;
|
|
51
59
|
taskCompleted = false;
|
|
52
60
|
isFirstApiCall = true;
|
|
61
|
+
sdkOutputAdapter = null;
|
|
62
|
+
isSdkMode = false;
|
|
63
|
+
sdkInputBuffer = [];
|
|
64
|
+
resolveInput = null;
|
|
65
|
+
_currentRequestId = null;
|
|
66
|
+
heartbeatTimeout = null;
|
|
67
|
+
heartbeatTimeoutMs = 300000; // 5 minutes timeout for long AI responses
|
|
68
|
+
lastActivityTime = Date.now();
|
|
69
|
+
// SDK response handling for approvals and questions
|
|
70
|
+
approvalPromises = new Map();
|
|
71
|
+
questionPromises = new Map();
|
|
53
72
|
constructor(indentLevel = 0) {
|
|
54
73
|
this.rl = readline.createInterface({
|
|
55
74
|
input: process.stdin,
|
|
56
|
-
output: process.stdout
|
|
75
|
+
output: process.stdout,
|
|
57
76
|
});
|
|
58
77
|
this.configManager = getConfigManager(process.cwd());
|
|
59
78
|
this.agentManager = getAgentManager(process.cwd());
|
|
@@ -66,7 +85,7 @@ export class InteractiveSession {
|
|
|
66
85
|
// Register /clear callback, clear local conversation when clearing dialogue
|
|
67
86
|
this.slashCommandHandler.setClearCallback(() => {
|
|
68
87
|
this.conversation = [];
|
|
69
|
-
this.
|
|
88
|
+
this.tool_calls = [];
|
|
70
89
|
this.currentTaskId = null;
|
|
71
90
|
this.taskCompleted = false;
|
|
72
91
|
this.isFirstApiCall = true;
|
|
@@ -76,6 +95,10 @@ export class InteractiveSession {
|
|
|
76
95
|
this.slashCommandHandler.setSystemPromptUpdateCallback(async () => {
|
|
77
96
|
await this.updateSystemPrompt();
|
|
78
97
|
});
|
|
98
|
+
// Register config update callback, update aiClient config when /auth changes config
|
|
99
|
+
this.slashCommandHandler.setConfigUpdateCallback(() => {
|
|
100
|
+
this.updateAiClientConfig();
|
|
101
|
+
});
|
|
79
102
|
this.executionMode = ExecutionMode.DEFAULT;
|
|
80
103
|
this.cancellationManager = getCancellationManager();
|
|
81
104
|
this.indentLevel = indentLevel;
|
|
@@ -88,59 +111,226 @@ export class InteractiveSession {
|
|
|
88
111
|
setAIClient(aiClient) {
|
|
89
112
|
this.aiClient = aiClient;
|
|
90
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Update aiClient config when /auth changes config (called from callback)
|
|
116
|
+
*/
|
|
117
|
+
updateAiClientConfig() {
|
|
118
|
+
const authConfig = this.configManager.getAuthConfig();
|
|
119
|
+
const isRemote = authConfig.type === AuthType.OAUTH_XAGENT;
|
|
120
|
+
if (isRemote) {
|
|
121
|
+
// Already in remote mode, no change needed
|
|
122
|
+
if (this.remoteAIClient !== null) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// Switch to remote: clear local client, create remote client
|
|
126
|
+
this.aiClient = null;
|
|
127
|
+
this.remoteAIClient = createAIClient(authConfig);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Already in local mode, no change needed
|
|
131
|
+
if (this.aiClient !== null) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// Switch to local: clear remote client, create local client
|
|
135
|
+
this.remoteAIClient = null;
|
|
136
|
+
this.aiClient = createAIClient(authConfig);
|
|
137
|
+
}
|
|
138
|
+
this.slashCommandHandler.setRemoteAIClient(this.remoteAIClient);
|
|
139
|
+
}
|
|
91
140
|
setExecutionMode(mode) {
|
|
92
141
|
this.executionMode = mode;
|
|
93
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Set SDK mode for programmatic access.
|
|
145
|
+
* In SDK mode, output is formatted as JSON to stdout.
|
|
146
|
+
*/
|
|
147
|
+
setSdkMode(adapter) {
|
|
148
|
+
this.isSdkMode = true;
|
|
149
|
+
this.sdkOutputAdapter = adapter;
|
|
150
|
+
// Initialize SDK mode for other modules using centralized output util
|
|
151
|
+
const { initOutputMode } = require('./output-util.js');
|
|
152
|
+
initOutputMode(true, adapter);
|
|
153
|
+
// Initialize SmartApprovalEngine in SDK mode
|
|
154
|
+
this.initSmartApprovalSdkMode(adapter).catch(() => {
|
|
155
|
+
// Silently ignore errors - not critical
|
|
156
|
+
});
|
|
157
|
+
// Initialize tool registry in SDK mode (fire and forget, doesn't need to await)
|
|
158
|
+
this.initToolRegistrySdkMode(adapter).catch(() => {
|
|
159
|
+
// Silently ignore errors - tool registry init is not critical
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Initialize SmartApprovalEngine in SDK mode.
|
|
164
|
+
*/
|
|
165
|
+
async initSmartApprovalSdkMode(adapter) {
|
|
166
|
+
const { getSmartApprovalEngine } = await import('./smart-approval.js');
|
|
167
|
+
const approvalEngine = getSmartApprovalEngine();
|
|
168
|
+
approvalEngine.setSdkMode(true, adapter);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Initialize tool registry in SDK mode.
|
|
172
|
+
*/
|
|
173
|
+
async initToolRegistrySdkMode(adapter) {
|
|
174
|
+
const toolRegistry = getToolRegistry();
|
|
175
|
+
await toolRegistry.setSdkMode(true, adapter);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Get SDK mode status.
|
|
179
|
+
*/
|
|
180
|
+
getIsSdkMode() {
|
|
181
|
+
return this.isSdkMode;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Output assistant response - handles SDK mode and normal mode differently.
|
|
185
|
+
*/
|
|
186
|
+
outputAssistant(content, reasoningContent) {
|
|
187
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
188
|
+
this.sdkOutputAdapter.outputAssistant(content, reasoningContent);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
const indent = this.getIndent();
|
|
192
|
+
console.log('');
|
|
193
|
+
console.log(`${indent}${colors.primaryBright(`${icons.robot} Assistant:`)}`);
|
|
194
|
+
console.log(`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`);
|
|
195
|
+
console.log('');
|
|
196
|
+
const renderedContent = renderMarkdown(content, (process.stdout.columns || 80) - indent.length * 2);
|
|
197
|
+
console.log(`${indent}${renderedContent.replace(/^/gm, indent)}`);
|
|
198
|
+
console.log('');
|
|
199
|
+
}
|
|
200
|
+
}
|
|
94
201
|
/**
|
|
95
202
|
* Update system prompt to reflect MCP changes (called after add/remove MCP)
|
|
96
203
|
*/
|
|
97
204
|
async updateSystemPrompt() {
|
|
205
|
+
// Reload skills to pick up any newly added/removed skills
|
|
206
|
+
// First reset the SkillLoader to clear all cached skills
|
|
207
|
+
const { resetSkillLoader } = await import('./skill-loader.js');
|
|
208
|
+
resetSkillLoader();
|
|
209
|
+
// Then reload the skill invoker
|
|
210
|
+
const skillInvoker = (await import('./skill-invoker.js')).getSkillInvoker();
|
|
211
|
+
await skillInvoker.reload();
|
|
98
212
|
const toolRegistry = getToolRegistry();
|
|
99
213
|
const promptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode, undefined, this.mcpManager);
|
|
100
214
|
// Use the current agent's original system prompt as base
|
|
101
215
|
const baseSystemPrompt = this.currentAgent?.systemPrompt || 'You are xAgent, an AI-powered CLI tool.';
|
|
102
216
|
const newSystemPrompt = await promptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
|
|
103
217
|
// Replace old system prompt with new one
|
|
104
|
-
this.conversation = this.conversation.filter(msg => msg.role !== 'system');
|
|
218
|
+
this.conversation = this.conversation.filter((msg) => msg.role !== 'system');
|
|
105
219
|
this.conversation.unshift({
|
|
106
220
|
role: 'system',
|
|
107
221
|
content: newSystemPrompt,
|
|
108
|
-
timestamp: Date.now()
|
|
222
|
+
timestamp: Date.now(),
|
|
109
223
|
});
|
|
110
224
|
// Sync to slashCommandHandler
|
|
111
225
|
this.slashCommandHandler.setConversationHistory(this.conversation);
|
|
112
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* Watch for skill updates from CLI and update system prompt accordingly
|
|
229
|
+
*/
|
|
230
|
+
startSkillUpdateWatcher() {
|
|
231
|
+
const SKILL_STATE_FILE = '.skill-state.json';
|
|
232
|
+
const configManager = getConfigManager();
|
|
233
|
+
const userSkillsPath = configManager.getUserSkillsPath();
|
|
234
|
+
const stateFilePath = userSkillsPath
|
|
235
|
+
? path.join(userSkillsPath, SKILL_STATE_FILE)
|
|
236
|
+
: null;
|
|
237
|
+
if (!stateFilePath) {
|
|
238
|
+
return; // No user skills path configured
|
|
239
|
+
}
|
|
240
|
+
let lastUpdateTime = 0;
|
|
241
|
+
// Initialize with current state to avoid triggering update on first check
|
|
242
|
+
try {
|
|
243
|
+
const { existsSync, readFileSync } = require('fs');
|
|
244
|
+
if (existsSync(stateFilePath)) {
|
|
245
|
+
const content = readFileSync(stateFilePath, 'utf-8');
|
|
246
|
+
const state = JSON.parse(content);
|
|
247
|
+
lastUpdateTime = state.lastSkillUpdate || 0;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
// Silent fail
|
|
252
|
+
}
|
|
253
|
+
// Check for updates every 2 seconds
|
|
254
|
+
const checkInterval = setInterval(async () => {
|
|
255
|
+
try {
|
|
256
|
+
const { existsSync, readFileSync } = await import('fs');
|
|
257
|
+
if (existsSync(stateFilePath)) {
|
|
258
|
+
const content = readFileSync(stateFilePath, 'utf-8');
|
|
259
|
+
const state = JSON.parse(content);
|
|
260
|
+
if (state.lastSkillUpdate && state.lastSkillUpdate > lastUpdateTime) {
|
|
261
|
+
lastUpdateTime = state.lastSkillUpdate;
|
|
262
|
+
// Update system prompt with new skills
|
|
263
|
+
await this.updateSystemPrompt();
|
|
264
|
+
if (!this.isSdkMode) {
|
|
265
|
+
console.log(colors.textMuted(' 🔄 Skills updated from CLI'));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
// Silent fail - watcher is optional
|
|
272
|
+
}
|
|
273
|
+
}, 2000);
|
|
274
|
+
// Clean up on session end
|
|
275
|
+
this._skillWatcherInterval = checkInterval;
|
|
276
|
+
}
|
|
113
277
|
setAgent(agent) {
|
|
114
278
|
this.currentAgent = agent;
|
|
115
279
|
}
|
|
116
|
-
async start() {
|
|
280
|
+
async start(initializedCount = 0) {
|
|
117
281
|
// Set this session as the singleton for access from other modules
|
|
118
282
|
setSingletonSession(this);
|
|
119
283
|
// Initialize taskId for GUI operations
|
|
120
284
|
this.currentTaskId = crypto.randomUUID();
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
285
|
+
const _separator = icons.separator.repeat(60);
|
|
286
|
+
if (!this.isSdkMode) {
|
|
287
|
+
// Normal mode: show ASCII art welcome
|
|
288
|
+
console.log('');
|
|
289
|
+
console.log(colors.gradient('╔════════════════════════════════════════════════════════════╗'));
|
|
290
|
+
console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
|
|
291
|
+
console.log(colors.gradient('║') +
|
|
292
|
+
' '.repeat(13) +
|
|
293
|
+
'🤖 ' +
|
|
294
|
+
colors.gradient('XAGENT CLI') +
|
|
295
|
+
' '.repeat(32) +
|
|
296
|
+
colors.gradient(' ║'));
|
|
297
|
+
console.log(colors.gradient('║') +
|
|
298
|
+
' '.repeat(16) +
|
|
299
|
+
colors.textMuted(`v${packageJson.version}`) +
|
|
300
|
+
' '.repeat(36) +
|
|
301
|
+
colors.gradient(' ║'));
|
|
302
|
+
console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
|
|
303
|
+
console.log(colors.gradient('╚════════════════════════════════════════════════════════════╝'));
|
|
304
|
+
console.log(colors.textMuted(' AI-powered command-line assistant'));
|
|
305
|
+
// Show initialization message if skills were initialized
|
|
306
|
+
if (initializedCount > 0) {
|
|
307
|
+
console.log(colors.textMuted(` ✨ Initialized ${initializedCount} built-in skills`));
|
|
308
|
+
}
|
|
309
|
+
console.log('');
|
|
310
|
+
}
|
|
131
311
|
await this.initialize();
|
|
132
312
|
this.showWelcomeMessage();
|
|
313
|
+
// Start watching for skill updates from CLI
|
|
314
|
+
this.startSkillUpdateWatcher();
|
|
315
|
+
// SDK 模式初始化:设置输出适配器
|
|
316
|
+
if (this.isSdkMode) {
|
|
317
|
+
// Start heartbeat timeout monitoring in SDK mode
|
|
318
|
+
this.startHeartbeatMonitoring();
|
|
319
|
+
}
|
|
320
|
+
// Set up ESC key handler using the terminal module
|
|
321
|
+
// This avoids conflicts with readline and provides clean ESC detection
|
|
322
|
+
let _escCleanup;
|
|
323
|
+
if (process.stdin.isTTY) {
|
|
324
|
+
_escCleanup = setupEscKeyHandler(() => {
|
|
325
|
+
if (this._isOperationInProgress) {
|
|
326
|
+
// An operation is running, let it be cancelled
|
|
327
|
+
this.cancellationManager.cancel();
|
|
328
|
+
}
|
|
329
|
+
// No operation running, ignore ESC
|
|
330
|
+
});
|
|
331
|
+
}
|
|
133
332
|
// Track if an operation is in progress
|
|
134
333
|
this._isOperationInProgress = false;
|
|
135
|
-
// Listen for ESC cancellation - only cancel operations, don't exit the program
|
|
136
|
-
const cancelHandler = () => {
|
|
137
|
-
if (this._isOperationInProgress) {
|
|
138
|
-
// An operation is running, let it be cancelled
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
// No operation running, ignore ESC or show a message
|
|
142
|
-
};
|
|
143
|
-
this.cancellationManager.on('cancelled', cancelHandler);
|
|
144
334
|
this.promptLoop();
|
|
145
335
|
// Keep the promise pending until shutdown
|
|
146
336
|
return new Promise((resolve) => {
|
|
@@ -159,7 +349,7 @@ export class InteractiveSession {
|
|
|
159
349
|
frameIndex = (frameIndex + 1) % frames.length;
|
|
160
350
|
}, 120);
|
|
161
351
|
logger.debug('[SESSION] 调用 configManager.load()...');
|
|
162
|
-
|
|
352
|
+
this.configManager.load();
|
|
163
353
|
logger.debug('[SESSION] Config loaded');
|
|
164
354
|
let authConfig = this.configManager.getAuthConfig();
|
|
165
355
|
let selectedAuthType = this.configManager.get('selectedAuthType');
|
|
@@ -186,9 +376,8 @@ export class InteractiveSession {
|
|
|
186
376
|
clearInterval(refreshInterval);
|
|
187
377
|
process.stdout.write('\r' + ' '.repeat(50) + '\r');
|
|
188
378
|
if (newToken) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
await this.configManager.save('global');
|
|
379
|
+
this.configManager.set('apiKey', newToken);
|
|
380
|
+
this.configManager.save('global');
|
|
192
381
|
authConfig.apiKey = newToken;
|
|
193
382
|
isValid = true;
|
|
194
383
|
}
|
|
@@ -198,20 +387,18 @@ export class InteractiveSession {
|
|
|
198
387
|
console.log(colors.warning('Your xAgent session has expired or is not configured'));
|
|
199
388
|
console.log(colors.info('Please select an authentication method to continue.'));
|
|
200
389
|
console.log('');
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
await this.configManager.save('global');
|
|
206
|
-
await this.configManager.load();
|
|
390
|
+
this.configManager.set('apiKey', '');
|
|
391
|
+
this.configManager.set('refreshToken', '');
|
|
392
|
+
this.configManager.save('global');
|
|
393
|
+
this.configManager.load();
|
|
207
394
|
authConfig = this.configManager.getAuthConfig();
|
|
208
395
|
await this.setupAuthentication();
|
|
209
396
|
authConfig = this.configManager.getAuthConfig();
|
|
210
|
-
// Recreate readline interface after
|
|
397
|
+
// Recreate readline interface after interactive prompt
|
|
211
398
|
this.rl.close();
|
|
212
399
|
this.rl = readline.createInterface({
|
|
213
400
|
input: process.stdin,
|
|
214
|
-
output: process.stdout
|
|
401
|
+
output: process.stdout,
|
|
215
402
|
});
|
|
216
403
|
this.rl.on('close', () => {
|
|
217
404
|
// readline closed
|
|
@@ -226,11 +413,11 @@ export class InteractiveSession {
|
|
|
226
413
|
authConfig = this.configManager.getAuthConfig();
|
|
227
414
|
selectedAuthType = this.configManager.get('selectedAuthType');
|
|
228
415
|
logger.debug('[SESSION] selectedAuthType (after setup):', String(selectedAuthType));
|
|
229
|
-
// Recreate readline interface after
|
|
416
|
+
// Recreate readline interface after interactive prompt
|
|
230
417
|
this.rl.close();
|
|
231
418
|
this.rl = readline.createInterface({
|
|
232
419
|
input: process.stdin,
|
|
233
|
-
output: process.stdout
|
|
420
|
+
output: process.stdout,
|
|
234
421
|
});
|
|
235
422
|
this.rl.on('close', () => {
|
|
236
423
|
// readline closed
|
|
@@ -241,21 +428,42 @@ export class InteractiveSession {
|
|
|
241
428
|
process.stdout.write('\r' + ' '.repeat(50) + '\r');
|
|
242
429
|
}
|
|
243
430
|
// For OPENAI_COMPATIBLE with API key, skip validation and proceed directly
|
|
244
|
-
|
|
245
|
-
this.contextCompressor.setAIClient(this.aiClient);
|
|
246
|
-
// Initialize remote AI client for OAuth XAGENT mode
|
|
247
|
-
logger.debug('[SESSION] Final selectedAuthType:', String(selectedAuthType));
|
|
248
|
-
logger.debug('[SESSION] Creating RemoteAIClient?', String(selectedAuthType === AuthType.OAUTH_XAGENT));
|
|
431
|
+
// Initialize AI clients and set contextCompressor appropriately
|
|
249
432
|
if (selectedAuthType === AuthType.OAUTH_XAGENT) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
433
|
+
// Remote mode: fetch default models if not set
|
|
434
|
+
const currentLlm = this.configManager.get('remote_llmModelName');
|
|
435
|
+
const currentVlm = this.configManager.get('remote_vlmModelName');
|
|
436
|
+
if (!currentLlm || !currentVlm) {
|
|
437
|
+
const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
|
|
438
|
+
try {
|
|
439
|
+
const defaults = await fetchDefaultModels(authConfig.apiKey || '', webBaseUrl);
|
|
440
|
+
if (!currentLlm && defaults.llm?.name) {
|
|
441
|
+
this.configManager.set('remote_llmModelName', defaults.llm.name);
|
|
442
|
+
}
|
|
443
|
+
if (!currentVlm && defaults.vlm?.name) {
|
|
444
|
+
this.configManager.set('remote_vlmModelName', defaults.vlm.name);
|
|
445
|
+
}
|
|
446
|
+
this.configManager.save('global');
|
|
447
|
+
}
|
|
448
|
+
catch (error) {
|
|
449
|
+
logger.debug('[SESSION] Failed to fetch default models:', error.message);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
// Remote mode: create RemoteAIClient and use it for context compression
|
|
453
|
+
this.remoteAIClient = createAIClient(authConfig);
|
|
454
|
+
this.contextCompressor.setAIClient(this.remoteAIClient);
|
|
253
455
|
logger.debug('[DEBUG Initialize] RemoteAIClient created successfully');
|
|
254
456
|
}
|
|
255
457
|
else {
|
|
458
|
+
// Local mode: create local AIClient
|
|
459
|
+
this.aiClient = createAIClient(authConfig);
|
|
460
|
+
this.contextCompressor.setAIClient(this.aiClient);
|
|
256
461
|
logger.debug('[DEBUG Initialize] RemoteAIClient NOT created (not OAuth XAGENT mode)');
|
|
257
462
|
}
|
|
258
|
-
|
|
463
|
+
// Sync remoteAIClient reference to slashCommandHandler for /provider command
|
|
464
|
+
this.slashCommandHandler.setRemoteAIClient(this.remoteAIClient);
|
|
465
|
+
this.executionMode =
|
|
466
|
+
this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
|
|
259
467
|
await this.agentManager.loadAgents();
|
|
260
468
|
await this.memoryManager.loadMemory();
|
|
261
469
|
await this.conversationManager.initialize();
|
|
@@ -267,24 +475,45 @@ export class InteractiveSession {
|
|
|
267
475
|
this.slashCommandHandler.setConversationHistory(this.conversation);
|
|
268
476
|
const mcpServers = this.configManager.getMcpServers();
|
|
269
477
|
Object.entries(mcpServers).forEach(([name, config]) => {
|
|
270
|
-
|
|
478
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
479
|
+
this.sdkOutputAdapter.outputMCPRegistering(name, config.transport || 'stdio');
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
console.log(`📝 Registering MCP server: ${name} (${config.transport})`);
|
|
483
|
+
}
|
|
271
484
|
this.mcpManager.registerServer(name, config);
|
|
272
485
|
});
|
|
273
486
|
// Eagerly connect to MCP servers to get tool definitions
|
|
274
487
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
275
488
|
try {
|
|
276
|
-
|
|
489
|
+
const serverCount = Object.keys(mcpServers).length;
|
|
490
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
491
|
+
this.sdkOutputAdapter.outputMCPLoading(serverCount);
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
console.log(`${colors.info(`${icons.brain} Connecting to ${serverCount} MCP server(s)...`)}`);
|
|
495
|
+
}
|
|
277
496
|
await this.mcpManager.connectAllServers();
|
|
278
497
|
const connectedCount = Array.from(this.mcpManager.getAllServers()).filter((s) => s.isServerConnected()).length;
|
|
279
498
|
const mcpTools = this.mcpManager.getToolDefinitions();
|
|
280
|
-
|
|
499
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
500
|
+
this.sdkOutputAdapter.outputMCPConnected(serverCount, connectedCount, mcpTools.length);
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
console.log(`${colors.success(`✓ ${connectedCount}/${serverCount} MCP server(s) connected (${mcpTools.length} tools available)`)}`);
|
|
504
|
+
}
|
|
281
505
|
// Register MCP tools with the tool registry (hide MCP origin from LLM)
|
|
282
506
|
const toolRegistry = getToolRegistry();
|
|
283
507
|
const allMcpTools = this.mcpManager.getAllTools();
|
|
284
508
|
toolRegistry.registerMCPTools(allMcpTools);
|
|
285
509
|
}
|
|
286
510
|
catch (error) {
|
|
287
|
-
|
|
511
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
512
|
+
this.sdkOutputAdapter.outputMCPConnectionFailed(error.message);
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
console.log(`${colors.warning(`⚠ MCP connection failed: ${error.message}`)}`);
|
|
516
|
+
}
|
|
288
517
|
}
|
|
289
518
|
}
|
|
290
519
|
const checkpointingConfig = this.configManager.getCheckpointingConfig();
|
|
@@ -292,8 +521,13 @@ export class InteractiveSession {
|
|
|
292
521
|
this.checkpointManager = getCheckpointManager(process.cwd(), checkpointingConfig.enabled, checkpointingConfig.maxCheckpoints);
|
|
293
522
|
await this.checkpointManager.initialize();
|
|
294
523
|
}
|
|
295
|
-
this.currentAgent = this.agentManager.getAgent('general-purpose');
|
|
296
|
-
|
|
524
|
+
this.currentAgent = this.agentManager.getAgent('general-purpose') ?? null;
|
|
525
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
526
|
+
this.sdkOutputAdapter.outputSuccess('Initialization complete');
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
console.log(colors.success('✔ Initialization complete'));
|
|
530
|
+
}
|
|
297
531
|
}
|
|
298
532
|
catch (error) {
|
|
299
533
|
const spinner = ora({ text: '', spinner: 'dots', color: 'red' }).start();
|
|
@@ -315,11 +549,11 @@ export class InteractiveSession {
|
|
|
315
549
|
logger.debug('[SESSION] Sending validation request to:', url);
|
|
316
550
|
const response = await axios.get(url, {
|
|
317
551
|
headers: {
|
|
318
|
-
|
|
319
|
-
'Content-Type': 'application/json'
|
|
552
|
+
Authorization: `Bearer ${apiKey}`,
|
|
553
|
+
'Content-Type': 'application/json',
|
|
320
554
|
},
|
|
321
555
|
httpsAgent,
|
|
322
|
-
timeout: 10000
|
|
556
|
+
timeout: 10000,
|
|
323
557
|
});
|
|
324
558
|
logger.debug('[SESSION] Validation response status:', String(response.status));
|
|
325
559
|
return response.status === 200;
|
|
@@ -341,7 +575,7 @@ export class InteractiveSession {
|
|
|
341
575
|
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
342
576
|
const response = await axios.post(url, { refreshToken }, {
|
|
343
577
|
httpsAgent,
|
|
344
|
-
timeout: 10000
|
|
578
|
+
timeout: 10000,
|
|
345
579
|
});
|
|
346
580
|
if (response.status === 200) {
|
|
347
581
|
const data = response.data;
|
|
@@ -351,7 +585,7 @@ export class InteractiveSession {
|
|
|
351
585
|
return null;
|
|
352
586
|
}
|
|
353
587
|
}
|
|
354
|
-
catch
|
|
588
|
+
catch {
|
|
355
589
|
return null;
|
|
356
590
|
}
|
|
357
591
|
}
|
|
@@ -363,11 +597,14 @@ export class InteractiveSession {
|
|
|
363
597
|
console.log('');
|
|
364
598
|
const authType = await selectAuthType();
|
|
365
599
|
this.configManager.set('selectedAuthType', authType);
|
|
600
|
+
// Get xagentApiBaseUrl from config (respects XAGENT_BASE_URL env var)
|
|
601
|
+
const config = this.configManager.getAuthConfig();
|
|
366
602
|
const authService = new AuthService({
|
|
367
603
|
type: authType,
|
|
368
604
|
apiKey: '',
|
|
369
605
|
baseUrl: '',
|
|
370
|
-
modelName: ''
|
|
606
|
+
modelName: '',
|
|
607
|
+
xagentApiBaseUrl: config.xagentApiBaseUrl,
|
|
371
608
|
});
|
|
372
609
|
const success = await authService.authenticate();
|
|
373
610
|
if (!success) {
|
|
@@ -377,67 +614,175 @@ export class InteractiveSession {
|
|
|
377
614
|
process.exit(1);
|
|
378
615
|
}
|
|
379
616
|
const authConfig = authService.getAuthConfig();
|
|
617
|
+
// Clear modelName for remote mode
|
|
618
|
+
if (authType === AuthType.OAUTH_XAGENT) {
|
|
619
|
+
authConfig.modelName = '';
|
|
620
|
+
}
|
|
380
621
|
// VLM configuration is optional - only show for non-OAuth (local) mode
|
|
381
622
|
// Remote mode uses backend VLM configuration
|
|
382
623
|
if (authType !== AuthType.OAUTH_XAGENT) {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
624
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
625
|
+
this.sdkOutputAdapter.outputInfo('VLM configuration is optional. You can configure it later using the /model command if needed.');
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
console.log('');
|
|
629
|
+
console.log(colors.info(`${icons.info} VLM configuration is optional.`));
|
|
630
|
+
console.log(colors.info(`You can configure it later using the /model command if needed.`));
|
|
631
|
+
console.log('');
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
this.configManager.setAuthConfig(authConfig);
|
|
635
|
+
// Set default remote model settings if not already set
|
|
636
|
+
if (authType === AuthType.OAUTH_XAGENT) {
|
|
637
|
+
if (!this.configManager.get('remote_llmModelName')) {
|
|
638
|
+
this.configManager.set('remote_llmModelName', '');
|
|
639
|
+
}
|
|
640
|
+
if (!this.configManager.get('remote_vlmModelName')) {
|
|
641
|
+
this.configManager.set('remote_vlmModelName', '');
|
|
642
|
+
}
|
|
643
|
+
this.configManager.save('global');
|
|
387
644
|
}
|
|
388
|
-
// Save LLM config only, skip VLM for now
|
|
389
|
-
await this.configManager.setAuthConfig(authConfig);
|
|
390
645
|
}
|
|
391
646
|
showWelcomeMessage() {
|
|
392
647
|
const language = this.configManager.getLanguage();
|
|
393
648
|
const separator = icons.separator.repeat(40);
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
649
|
+
if (!this.isSdkMode) {
|
|
650
|
+
console.log('');
|
|
651
|
+
console.log(colors.border(separator));
|
|
652
|
+
if (language === 'zh') {
|
|
653
|
+
console.log(colors.primaryBright(`${icons.sparkles} Welcome to XAGENT CLI!`));
|
|
654
|
+
console.log(colors.textMuted('Type /help to see available commands'));
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
console.log(colors.primaryBright(`${icons.sparkles} Welcome to XAGENT CLI!`));
|
|
658
|
+
console.log(colors.textMuted('Type /help to see available commands'));
|
|
659
|
+
}
|
|
660
|
+
console.log(colors.border(separator));
|
|
661
|
+
console.log('');
|
|
403
662
|
}
|
|
404
|
-
console.log(colors.border(separator));
|
|
405
|
-
console.log('');
|
|
406
663
|
this.showExecutionMode();
|
|
664
|
+
// In SDK mode, output ready signal
|
|
665
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
666
|
+
this.sdkOutputAdapter.outputReady();
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Start heartbeat timeout monitoring in SDK mode
|
|
671
|
+
*/
|
|
672
|
+
startHeartbeatMonitoring() {
|
|
673
|
+
this.stopHeartbeatMonitoring();
|
|
674
|
+
// Check heartbeat timeout periodically
|
|
675
|
+
this.heartbeatTimeout = setInterval(() => {
|
|
676
|
+
const elapsed = Date.now() - this.lastActivityTime;
|
|
677
|
+
if (elapsed > this.heartbeatTimeoutMs) {
|
|
678
|
+
// Heartbeat timeout - no activity for too long
|
|
679
|
+
this.sdkOutputAdapter?.output({
|
|
680
|
+
type: 'error',
|
|
681
|
+
subtype: 'heartbeat_timeout',
|
|
682
|
+
timestamp: Date.now(),
|
|
683
|
+
data: {
|
|
684
|
+
message: 'Heartbeat timeout - no activity detected',
|
|
685
|
+
elapsed_ms: elapsed,
|
|
686
|
+
timeout_ms: this.heartbeatTimeoutMs
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
// Stop monitoring
|
|
690
|
+
this.stopHeartbeatMonitoring();
|
|
691
|
+
}
|
|
692
|
+
}, 30000); // Check every 30 seconds
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* Stop heartbeat timeout monitoring
|
|
696
|
+
*/
|
|
697
|
+
stopHeartbeatMonitoring() {
|
|
698
|
+
if (this.heartbeatTimeout) {
|
|
699
|
+
clearInterval(this.heartbeatTimeout);
|
|
700
|
+
this.heartbeatTimeout = null;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Reset heartbeat timeout (called on activity)
|
|
705
|
+
*/
|
|
706
|
+
resetHeartbeatTimeout() {
|
|
707
|
+
this.lastActivityTime = Date.now();
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Stop heartbeat monitoring (public method for cleanup)
|
|
711
|
+
* Used by SDK session to clean up when session ends
|
|
712
|
+
*/
|
|
713
|
+
stopHeartbeatMonitor() {
|
|
714
|
+
this.stopHeartbeatMonitoring();
|
|
407
715
|
}
|
|
408
716
|
showExecutionMode() {
|
|
409
717
|
const modeConfig = {
|
|
410
718
|
[ExecutionMode.YOLO]: {
|
|
411
719
|
color: colors.error,
|
|
412
720
|
icon: icons.fire,
|
|
413
|
-
description: 'Execute commands without confirmation'
|
|
721
|
+
description: 'Execute commands without confirmation',
|
|
414
722
|
},
|
|
415
723
|
[ExecutionMode.ACCEPT_EDITS]: {
|
|
416
724
|
color: colors.warning,
|
|
417
725
|
icon: icons.check,
|
|
418
|
-
description: 'Accept all edits automatically'
|
|
726
|
+
description: 'Accept all edits automatically',
|
|
419
727
|
},
|
|
420
728
|
[ExecutionMode.PLAN]: {
|
|
421
729
|
color: colors.info,
|
|
422
730
|
icon: icons.brain,
|
|
423
|
-
description: 'Plan before executing'
|
|
731
|
+
description: 'Plan before executing',
|
|
424
732
|
},
|
|
425
733
|
[ExecutionMode.DEFAULT]: {
|
|
426
734
|
color: colors.success,
|
|
427
735
|
icon: icons.bolt,
|
|
428
|
-
description: 'Safe execution with confirmations'
|
|
736
|
+
description: 'Safe execution with confirmations',
|
|
429
737
|
},
|
|
430
738
|
[ExecutionMode.SMART]: {
|
|
431
739
|
color: colors.primaryBright,
|
|
432
740
|
icon: icons.sparkles,
|
|
433
|
-
description: 'Smart approval with intelligent security checks'
|
|
434
|
-
}
|
|
741
|
+
description: 'Smart approval with intelligent security checks',
|
|
742
|
+
},
|
|
435
743
|
};
|
|
436
744
|
const config = modeConfig[this.executionMode];
|
|
437
745
|
const modeName = this.executionMode;
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
746
|
+
if (!this.isSdkMode) {
|
|
747
|
+
console.log(colors.textMuted(`${icons.info} Current Mode:`));
|
|
748
|
+
console.log(` ${config.color(config.icon)} ${styleHelpers.text.bold(config.color(modeName))}`);
|
|
749
|
+
console.log(` ${colors.textDim(` ${config.description}`)}`);
|
|
750
|
+
console.log('');
|
|
751
|
+
}
|
|
752
|
+
this.showRemoteModelInfo();
|
|
753
|
+
}
|
|
754
|
+
showRemoteModelInfo() {
|
|
755
|
+
const authConfig = this.configManager.getAuthConfig();
|
|
756
|
+
const isRemote = authConfig.type === AuthType.OAUTH_XAGENT;
|
|
757
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
758
|
+
// SDK 模式:通过 adapter 输出
|
|
759
|
+
if (isRemote) {
|
|
760
|
+
const llmModel = authConfig.remote_llmModelName || 'Not set';
|
|
761
|
+
const vlmModel = authConfig.remote_vlmModelName || 'Not set';
|
|
762
|
+
this.sdkOutputAdapter.outputInfo(`Remote Models - LLM: ${llmModel}, VLM: ${vlmModel}`);
|
|
763
|
+
}
|
|
764
|
+
else {
|
|
765
|
+
const modelName = authConfig.modelName || 'Not set';
|
|
766
|
+
const guiSubagentModel = this.configManager.get('guiSubagentModel') || 'Not set';
|
|
767
|
+
this.sdkOutputAdapter.outputInfo(`Local Models - LLM: ${modelName}, VLM: ${guiSubagentModel}`);
|
|
768
|
+
}
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
// 正常模式:控制台输出
|
|
772
|
+
if (isRemote) {
|
|
773
|
+
const llmModel = authConfig.remote_llmModelName || colors.textMuted('Not set');
|
|
774
|
+
const vlmModel = authConfig.remote_vlmModelName || colors.textMuted('Not set');
|
|
775
|
+
console.log(colors.textMuted(`${icons.brain} Remote Models:`));
|
|
776
|
+
console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('LLM:')} ${llmModel}`);
|
|
777
|
+
console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('VLM:')} ${vlmModel}`);
|
|
778
|
+
}
|
|
779
|
+
else {
|
|
780
|
+
const modelName = authConfig.modelName || colors.textMuted('Not set');
|
|
781
|
+
const guiSubagentModel = this.configManager.get('guiSubagentModel') || colors.textMuted('Not set');
|
|
782
|
+
console.log(colors.textMuted(`${icons.brain} Local Models:`));
|
|
783
|
+
console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('LLM:')} ${modelName}`);
|
|
784
|
+
console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('VLM:')} ${guiSubagentModel}`);
|
|
785
|
+
}
|
|
441
786
|
console.log('');
|
|
442
787
|
}
|
|
443
788
|
async promptLoop() {
|
|
@@ -445,19 +790,21 @@ export class InteractiveSession {
|
|
|
445
790
|
if (this._isShuttingDown) {
|
|
446
791
|
return;
|
|
447
792
|
}
|
|
793
|
+
// In SDK mode, use a different input loop
|
|
794
|
+
if (this.isSdkMode) {
|
|
795
|
+
await this.sdkPromptLoop();
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
448
798
|
// Recreate readline interface for input
|
|
449
799
|
if (this.rl) {
|
|
450
800
|
this.rl.close();
|
|
451
801
|
}
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
}
|
|
456
|
-
process.stdin.resume();
|
|
457
|
-
readline.emitKeypressEvents(process.stdin);
|
|
802
|
+
// Ensure TTY is in proper state for input handling
|
|
803
|
+
// This handles any state left by @clack/prompts or other interactions
|
|
804
|
+
ensureTtySane();
|
|
458
805
|
this.rl = readline.createInterface({
|
|
459
806
|
input: process.stdin,
|
|
460
|
-
output: process.stdout
|
|
807
|
+
output: process.stdout,
|
|
461
808
|
});
|
|
462
809
|
const prompt = `${colors.primaryBright('❯')} `;
|
|
463
810
|
this.rl.question(prompt, async (input) => {
|
|
@@ -474,14 +821,58 @@ export class InteractiveSession {
|
|
|
474
821
|
});
|
|
475
822
|
}
|
|
476
823
|
async handleInput(input) {
|
|
824
|
+
// Reset heartbeat timeout on any input activity
|
|
825
|
+
this.resetHeartbeatTimeout();
|
|
477
826
|
const trimmedInput = input.trim();
|
|
478
827
|
if (!trimmedInput) {
|
|
479
828
|
return;
|
|
480
829
|
}
|
|
830
|
+
// Check for SDK JSON message format
|
|
831
|
+
if (this.isSdkMode) {
|
|
832
|
+
const { isSdkMessage, parseSdkMessage } = await import('./types.js');
|
|
833
|
+
if (isSdkMessage(trimmedInput)) {
|
|
834
|
+
const sdkMessage = parseSdkMessage(trimmedInput);
|
|
835
|
+
if (sdkMessage) {
|
|
836
|
+
if (sdkMessage.type === 'ping') {
|
|
837
|
+
// Handle ping - respond with pong
|
|
838
|
+
await this.handlePing(sdkMessage);
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
else if (sdkMessage.type === 'control_request') {
|
|
842
|
+
// Handle control request
|
|
843
|
+
await this.handleControlRequest(sdkMessage);
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
else if (sdkMessage.type === 'user') {
|
|
847
|
+
// Store request_id for tracking
|
|
848
|
+
this._currentRequestId = sdkMessage.request_id || null;
|
|
849
|
+
// Handle user message from SDK
|
|
850
|
+
await this.processUserMessage(sdkMessage.content);
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
else if (sdkMessage.type === 'approval_response') {
|
|
854
|
+
// Handle approval response
|
|
855
|
+
await this.handleApprovalResponse(sdkMessage);
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
else if (sdkMessage.type === 'question_response') {
|
|
859
|
+
// Handle question response
|
|
860
|
+
await this.handleQuestionResponse(sdkMessage);
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
else {
|
|
866
|
+
// Not a JSON SDK message, treat as regular text
|
|
867
|
+
await this.processUserMessage(trimmedInput);
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
481
871
|
if (trimmedInput.startsWith('/')) {
|
|
482
872
|
const handled = await this.slashCommandHandler.handleCommand(trimmedInput);
|
|
483
873
|
if (handled) {
|
|
484
|
-
this.executionMode =
|
|
874
|
+
this.executionMode =
|
|
875
|
+
this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
|
|
485
876
|
// Sync conversation history to slashCommandHandler
|
|
486
877
|
this.slashCommandHandler.setConversationHistory(this.conversation);
|
|
487
878
|
}
|
|
@@ -493,6 +884,297 @@ export class InteractiveSession {
|
|
|
493
884
|
}
|
|
494
885
|
await this.processUserMessage(trimmedInput);
|
|
495
886
|
}
|
|
887
|
+
/**
|
|
888
|
+
* SDK prompt loop - reads input from stdin without showing prompt
|
|
889
|
+
*/
|
|
890
|
+
async sdkPromptLoop() {
|
|
891
|
+
// Read input from stdin directly without outputting prompt
|
|
892
|
+
const input = await this.readSdkInput();
|
|
893
|
+
if (this._isShuttingDown || input === null) {
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
try {
|
|
897
|
+
await this.handleInput(input);
|
|
898
|
+
}
|
|
899
|
+
catch (err) {
|
|
900
|
+
this.sdkOutputAdapter?.output({
|
|
901
|
+
type: 'error',
|
|
902
|
+
subtype: 'general',
|
|
903
|
+
timestamp: Date.now(),
|
|
904
|
+
data: { message: err.message }
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
// Continue the loop
|
|
908
|
+
this.sdkPromptLoop();
|
|
909
|
+
}
|
|
910
|
+
sdkRl = null;
|
|
911
|
+
sdkInputProcessing = false;
|
|
912
|
+
/**
|
|
913
|
+
* Check if a line is an SDK control message (approval_response, question_response)
|
|
914
|
+
*/
|
|
915
|
+
isSdkControlMessage(line) {
|
|
916
|
+
try {
|
|
917
|
+
const parsed = JSON.parse(line);
|
|
918
|
+
return parsed.type === 'approval_response' || parsed.type === 'question_response';
|
|
919
|
+
}
|
|
920
|
+
catch {
|
|
921
|
+
return false;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Process an SDK input line (returns true if processed)
|
|
926
|
+
*/
|
|
927
|
+
async processSdkInputLine(line) {
|
|
928
|
+
const { isSdkMessage, parseSdkMessage } = await import('./types.js');
|
|
929
|
+
if (isSdkMessage(line)) {
|
|
930
|
+
const sdkMessage = parseSdkMessage(line);
|
|
931
|
+
if (sdkMessage) {
|
|
932
|
+
if (sdkMessage.type === 'ping') {
|
|
933
|
+
await this.handlePing(sdkMessage);
|
|
934
|
+
return true;
|
|
935
|
+
}
|
|
936
|
+
else if (sdkMessage.type === 'control_request') {
|
|
937
|
+
await this.handleControlRequest(sdkMessage);
|
|
938
|
+
return true;
|
|
939
|
+
}
|
|
940
|
+
else if (sdkMessage.type === 'user') {
|
|
941
|
+
this._currentRequestId = sdkMessage.request_id || null;
|
|
942
|
+
await this.processUserMessage(sdkMessage.content);
|
|
943
|
+
return true;
|
|
944
|
+
}
|
|
945
|
+
else if (sdkMessage.type === 'approval_response') {
|
|
946
|
+
await this.handleApprovalResponse(sdkMessage);
|
|
947
|
+
return true;
|
|
948
|
+
}
|
|
949
|
+
else if (sdkMessage.type === 'question_response') {
|
|
950
|
+
await this.handleQuestionResponse(sdkMessage);
|
|
951
|
+
return true;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
return false;
|
|
956
|
+
}
|
|
957
|
+
/**
|
|
958
|
+
* Read a line of input from stdin (SDK mode)
|
|
959
|
+
* Uses readline 'line' event for reliable stdin reading
|
|
960
|
+
* SDK control messages (approval_response, question_response) bypass the main loop
|
|
961
|
+
*/
|
|
962
|
+
readSdkInput() {
|
|
963
|
+
return new Promise((resolve) => {
|
|
964
|
+
// Create readline interface if not exists
|
|
965
|
+
if (!this.sdkRl) {
|
|
966
|
+
this.sdkRl = readline.createInterface({
|
|
967
|
+
input: process.stdin,
|
|
968
|
+
crlfDelay: Infinity,
|
|
969
|
+
});
|
|
970
|
+
// Handle line events - SDK control messages bypass normal flow
|
|
971
|
+
this.sdkRl.on('line', async (line) => {
|
|
972
|
+
const cleanLine = line
|
|
973
|
+
.replace(/^\uFEFF/, '')
|
|
974
|
+
// eslint-disable-next-line no-control-regex
|
|
975
|
+
.replace(/[\x00-\x1F\x7F-\x9F]/g, '');
|
|
976
|
+
// Check if this is an SDK control message
|
|
977
|
+
if (this.isSdkControlMessage(cleanLine)) {
|
|
978
|
+
// Process immediately, don't wait for main loop
|
|
979
|
+
// We need to set a flag to prevent re-entrancy issues
|
|
980
|
+
if (this.sdkInputProcessing) {
|
|
981
|
+
// Already processing, queue it
|
|
982
|
+
this.sdkInputBuffer.push(cleanLine);
|
|
983
|
+
}
|
|
984
|
+
else {
|
|
985
|
+
this.sdkInputProcessing = true;
|
|
986
|
+
try {
|
|
987
|
+
await this.processSdkInputLine(cleanLine);
|
|
988
|
+
}
|
|
989
|
+
finally {
|
|
990
|
+
this.sdkInputProcessing = false;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
// Regular input
|
|
996
|
+
if (this.resolveInput) {
|
|
997
|
+
// Immediate handler available, resolve immediately
|
|
998
|
+
this.resolveInput(cleanLine);
|
|
999
|
+
this.resolveInput = null;
|
|
1000
|
+
}
|
|
1001
|
+
else {
|
|
1002
|
+
// No handler available, queue the message
|
|
1003
|
+
this.sdkInputBuffer.push(cleanLine);
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
// Handle close events
|
|
1007
|
+
this.sdkRl.on('close', () => {
|
|
1008
|
+
if (this.resolveInput) {
|
|
1009
|
+
this.resolveInput(null);
|
|
1010
|
+
this.resolveInput = null;
|
|
1011
|
+
}
|
|
1012
|
+
});
|
|
1013
|
+
// Handle errors
|
|
1014
|
+
this.sdkRl.on('error', () => {
|
|
1015
|
+
if (this.resolveInput) {
|
|
1016
|
+
this.resolveInput(null);
|
|
1017
|
+
this.resolveInput = null;
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
// Check for SDK control messages in buffer first
|
|
1022
|
+
for (let i = 0; i < this.sdkInputBuffer.length; i++) {
|
|
1023
|
+
const line = this.sdkInputBuffer[i];
|
|
1024
|
+
if (this.isSdkControlMessage(line)) {
|
|
1025
|
+
this.sdkInputBuffer.splice(i, 1);
|
|
1026
|
+
resolve(line);
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
if (this.sdkInputBuffer.length > 0) {
|
|
1031
|
+
const line = this.sdkInputBuffer.shift();
|
|
1032
|
+
resolve(line);
|
|
1033
|
+
return;
|
|
1034
|
+
}
|
|
1035
|
+
// Set up the resolve callback
|
|
1036
|
+
this.resolveInput = (value) => {
|
|
1037
|
+
resolve(value);
|
|
1038
|
+
};
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1042
|
+
* Handle SDK ping messages (heartbeat)
|
|
1043
|
+
*/
|
|
1044
|
+
async handlePing(pingMessage) {
|
|
1045
|
+
const requestId = pingMessage.request_id || `ping_${Date.now()}`;
|
|
1046
|
+
// Reset activity timestamp on ping (heartbeat activity)
|
|
1047
|
+
this.lastActivityTime = Date.now();
|
|
1048
|
+
// Send pong response through SDK adapter for consistency
|
|
1049
|
+
this.sdkOutputAdapter?.output({
|
|
1050
|
+
type: 'system',
|
|
1051
|
+
subtype: 'pong',
|
|
1052
|
+
timestamp: Date.now(),
|
|
1053
|
+
data: {
|
|
1054
|
+
type: 'pong',
|
|
1055
|
+
request_id: requestId,
|
|
1056
|
+
timestamp: Date.now()
|
|
1057
|
+
}
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Handle SDK control requests
|
|
1062
|
+
*/
|
|
1063
|
+
async handleControlRequest(request) {
|
|
1064
|
+
// Update activity to prevent heartbeat timeout during control requests
|
|
1065
|
+
this.lastActivityTime = Date.now();
|
|
1066
|
+
const { request_id, request: req } = request;
|
|
1067
|
+
switch (req.subtype) {
|
|
1068
|
+
case 'interrupt':
|
|
1069
|
+
this.sdkOutputAdapter?.outputSystem('interrupt', { request_id });
|
|
1070
|
+
this._isShuttingDown = true;
|
|
1071
|
+
process.exit(0);
|
|
1072
|
+
break;
|
|
1073
|
+
case 'set_permission_mode':
|
|
1074
|
+
{
|
|
1075
|
+
const { ExecutionMode } = await import('./types.js');
|
|
1076
|
+
const modeMap = {
|
|
1077
|
+
'default': ExecutionMode.DEFAULT,
|
|
1078
|
+
'acceptEdits': ExecutionMode.ACCEPT_EDITS,
|
|
1079
|
+
'plan': ExecutionMode.PLAN,
|
|
1080
|
+
'bypassPermissions': ExecutionMode.YOLO,
|
|
1081
|
+
};
|
|
1082
|
+
const mode = modeMap[req.mode] || ExecutionMode.SMART;
|
|
1083
|
+
this.executionMode = mode;
|
|
1084
|
+
this.sdkOutputAdapter?.outputSystem('permission_mode_changed', {
|
|
1085
|
+
request_id,
|
|
1086
|
+
mode: req.mode
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
break;
|
|
1090
|
+
case 'set_model':
|
|
1091
|
+
this.sdkOutputAdapter?.outputSystem('model_changed', {
|
|
1092
|
+
request_id,
|
|
1093
|
+
model: req.model
|
|
1094
|
+
});
|
|
1095
|
+
break;
|
|
1096
|
+
default:
|
|
1097
|
+
this.sdkOutputAdapter?.outputWarning(`Unknown control request: ${req.subtype}`);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
/**
|
|
1101
|
+
* Handle SDK approval responses
|
|
1102
|
+
*/
|
|
1103
|
+
async handleApprovalResponse(response) {
|
|
1104
|
+
const { request_id, approved } = response;
|
|
1105
|
+
const pending = this.approvalPromises.get(request_id);
|
|
1106
|
+
if (pending) {
|
|
1107
|
+
pending.resolve(approved);
|
|
1108
|
+
this.approvalPromises.delete(request_id);
|
|
1109
|
+
}
|
|
1110
|
+
else {
|
|
1111
|
+
this.sdkOutputAdapter?.outputWarning(`Unknown approval request ID: ${request_id}`);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Handle SDK question responses
|
|
1116
|
+
*/
|
|
1117
|
+
async handleQuestionResponse(response) {
|
|
1118
|
+
const { request_id, answers } = response;
|
|
1119
|
+
const pending = this.questionPromises.get(request_id);
|
|
1120
|
+
if (pending) {
|
|
1121
|
+
pending.resolve(answers);
|
|
1122
|
+
this.questionPromises.delete(request_id);
|
|
1123
|
+
}
|
|
1124
|
+
else {
|
|
1125
|
+
this.sdkOutputAdapter?.outputWarning(`Unknown question request ID: ${request_id}`);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Wait for SDK approval response
|
|
1130
|
+
*/
|
|
1131
|
+
async waitForApprovalResponse(requestId, timeoutMs = 300000) {
|
|
1132
|
+
return new Promise((resolve, reject) => {
|
|
1133
|
+
const timeout = setTimeout(() => {
|
|
1134
|
+
const pending = this.approvalPromises.get(requestId);
|
|
1135
|
+
if (pending) {
|
|
1136
|
+
pending.reject(new Error('Approval request timeout'));
|
|
1137
|
+
this.approvalPromises.delete(requestId);
|
|
1138
|
+
}
|
|
1139
|
+
resolve(false);
|
|
1140
|
+
}, timeoutMs);
|
|
1141
|
+
this.approvalPromises.set(requestId, {
|
|
1142
|
+
resolve: (approved) => {
|
|
1143
|
+
clearTimeout(timeout);
|
|
1144
|
+
resolve(approved);
|
|
1145
|
+
},
|
|
1146
|
+
reject: (err) => {
|
|
1147
|
+
clearTimeout(timeout);
|
|
1148
|
+
reject(err);
|
|
1149
|
+
}
|
|
1150
|
+
});
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* Wait for SDK question response
|
|
1155
|
+
*/
|
|
1156
|
+
async waitForQuestionResponse(requestId, timeoutMs = 300000) {
|
|
1157
|
+
return new Promise((resolve, reject) => {
|
|
1158
|
+
const timeout = setTimeout(() => {
|
|
1159
|
+
const pending = this.questionPromises.get(requestId);
|
|
1160
|
+
if (pending) {
|
|
1161
|
+
pending.reject(new Error('Question request timeout'));
|
|
1162
|
+
this.questionPromises.delete(requestId);
|
|
1163
|
+
}
|
|
1164
|
+
resolve([]); // Return empty array on timeout instead of rejecting
|
|
1165
|
+
}, timeoutMs);
|
|
1166
|
+
this.questionPromises.set(requestId, {
|
|
1167
|
+
resolve: (answers) => {
|
|
1168
|
+
clearTimeout(timeout);
|
|
1169
|
+
resolve(answers);
|
|
1170
|
+
},
|
|
1171
|
+
reject: (err) => {
|
|
1172
|
+
clearTimeout(timeout);
|
|
1173
|
+
reject(err);
|
|
1174
|
+
}
|
|
1175
|
+
});
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
496
1178
|
async handleSubAgentCommand(input) {
|
|
497
1179
|
const [agentType, ...taskParts] = input.slice(1).split(' ');
|
|
498
1180
|
const task = taskParts.join(' ');
|
|
@@ -511,11 +1193,11 @@ export class InteractiveSession {
|
|
|
511
1193
|
this.currentAgent = agent;
|
|
512
1194
|
await this.processUserMessage(task, agent);
|
|
513
1195
|
}
|
|
514
|
-
async processUserMessage(message,
|
|
515
|
-
const inputs = parseInput(message);
|
|
516
|
-
const textInput = inputs.find(i => i.type === 'text');
|
|
517
|
-
const fileInputs = inputs.filter(i => i.type === 'file');
|
|
518
|
-
const commandInput = inputs.find(i => i.type === 'command');
|
|
1196
|
+
async processUserMessage(message, _agent) {
|
|
1197
|
+
const inputs = await parseInput(message);
|
|
1198
|
+
const textInput = inputs.find((i) => i.type === 'text');
|
|
1199
|
+
const fileInputs = inputs.filter((i) => i.type === 'file');
|
|
1200
|
+
const commandInput = inputs.find((i) => i.type === 'command');
|
|
519
1201
|
if (commandInput) {
|
|
520
1202
|
await this.executeShellCommand(commandInput.content);
|
|
521
1203
|
return;
|
|
@@ -538,7 +1220,7 @@ export class InteractiveSession {
|
|
|
538
1220
|
type: 'text',
|
|
539
1221
|
content: userContent,
|
|
540
1222
|
rawInput: message,
|
|
541
|
-
timestamp: Date.now()
|
|
1223
|
+
timestamp: Date.now(),
|
|
542
1224
|
};
|
|
543
1225
|
await this.sessionManager.addInput(sessionInput);
|
|
544
1226
|
// Calculate thinking tokens based on config and user input
|
|
@@ -552,25 +1234,19 @@ export class InteractiveSession {
|
|
|
552
1234
|
const userMessage = {
|
|
553
1235
|
role: 'user',
|
|
554
1236
|
content: userContent,
|
|
555
|
-
timestamp: Date.now()
|
|
1237
|
+
timestamp: Date.now(),
|
|
556
1238
|
};
|
|
557
|
-
// Save last user message for recovery after compression
|
|
558
|
-
const lastUserMessage = userMessage;
|
|
559
1239
|
this.conversation.push(userMessage);
|
|
560
1240
|
await this.conversationManager.addMessage(userMessage);
|
|
561
|
-
// Check if context compression is needed
|
|
562
|
-
await this.checkAndCompressContext(lastUserMessage);
|
|
563
1241
|
// Use remote AI client if available (OAuth XAGENT mode)
|
|
564
1242
|
const currentSelectedAuthType = this.configManager.get('selectedAuthType');
|
|
565
|
-
logger.debug(
|
|
566
|
-
logger.debug('[DEBUG processUserMessage] selectedAuthType:', String(currentSelectedAuthType));
|
|
567
|
-
logger.debug('[DEBUG processUserMessage] AuthType.OAUTH_XAGENT:', String(AuthType.OAUTH_XAGENT));
|
|
1243
|
+
logger.debug(`[DEBUG] processUserMessage: remoteAIClient exists=${!!this.remoteAIClient}, selectedAuthType=${currentSelectedAuthType}`);
|
|
568
1244
|
if (this.remoteAIClient) {
|
|
569
|
-
logger.debug('[DEBUG
|
|
1245
|
+
logger.debug('[DEBUG] Using generateRemoteResponse (remote mode)');
|
|
570
1246
|
await this.generateRemoteResponse(thinkingTokens);
|
|
571
1247
|
}
|
|
572
1248
|
else {
|
|
573
|
-
logger.debug('[DEBUG
|
|
1249
|
+
logger.debug('[DEBUG] Using generateResponse (local mode)');
|
|
574
1250
|
await this.generateResponse(thinkingTokens);
|
|
575
1251
|
}
|
|
576
1252
|
}
|
|
@@ -578,6 +1254,11 @@ export class InteractiveSession {
|
|
|
578
1254
|
const indent = this.getIndent();
|
|
579
1255
|
const thinkingConfig = this.configManager.getThinkingConfig();
|
|
580
1256
|
const displayMode = thinkingConfig.displayMode || 'compact';
|
|
1257
|
+
// SDK 模式:使用 adapter 输出
|
|
1258
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
1259
|
+
this.sdkOutputAdapter.outputThinking(reasoningContent, displayMode);
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
581
1262
|
const separator = icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length);
|
|
582
1263
|
console.log('');
|
|
583
1264
|
console.log(`${indent}${colors.border(separator)}`);
|
|
@@ -588,7 +1269,7 @@ export class InteractiveSession {
|
|
|
588
1269
|
console.log('');
|
|
589
1270
|
console.log(`${indent}${colors.textDim(reasoningContent.replace(/^/gm, indent))}`);
|
|
590
1271
|
break;
|
|
591
|
-
case 'compact':
|
|
1272
|
+
case 'compact': {
|
|
592
1273
|
// Compact display, truncate partial content
|
|
593
1274
|
const maxLength = 500;
|
|
594
1275
|
const truncatedContent = reasoningContent.length > maxLength
|
|
@@ -599,6 +1280,7 @@ export class InteractiveSession {
|
|
|
599
1280
|
console.log(`${indent}${colors.textDim(truncatedContent.replace(/^/gm, indent))}`);
|
|
600
1281
|
console.log(`${indent}${colors.textDim(`[${reasoningContent.length} chars total]`)}`);
|
|
601
1282
|
break;
|
|
1283
|
+
}
|
|
602
1284
|
case 'indicator':
|
|
603
1285
|
// Show indicator only
|
|
604
1286
|
console.log(`${indent}${colors.textDim(`${icons.brain} Thinking process completed`)}`);
|
|
@@ -615,34 +1297,69 @@ export class InteractiveSession {
|
|
|
615
1297
|
/**
|
|
616
1298
|
* Check and compress conversation context
|
|
617
1299
|
*/
|
|
618
|
-
async checkAndCompressContext(
|
|
1300
|
+
async checkAndCompressContext() {
|
|
619
1301
|
const compressionConfig = this.configManager.getContextCompressionConfig();
|
|
620
1302
|
if (!compressionConfig.enabled) {
|
|
621
1303
|
return;
|
|
622
1304
|
}
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
1305
|
+
const indent = this.getIndent();
|
|
1306
|
+
const _currentTokens = this.contextCompressor.estimateContextTokens(this.conversation);
|
|
1307
|
+
const currentMessages = this.conversation.length;
|
|
1308
|
+
const { needsCompression, reason, tokenCount } = this.contextCompressor.needsCompression(this.conversation, compressionConfig);
|
|
1309
|
+
if (!needsCompression) {
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
// Extract threshold and contextWindow from reason
|
|
1313
|
+
const thresholdMatch = reason.match(/budget\s*\((\d+)/);
|
|
1314
|
+
const contextWindowMatch = reason.match(/contextWindow:\s*(\d+)/);
|
|
1315
|
+
const threshold = thresholdMatch ? parseInt(thresholdMatch[1], 10) : 0;
|
|
1316
|
+
const contextWindow = contextWindowMatch ? parseInt(contextWindowMatch[1], 10) : 0;
|
|
1317
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
1318
|
+
this.sdkOutputAdapter.outputContextCompressionTriggered(reason);
|
|
1319
|
+
}
|
|
1320
|
+
else {
|
|
626
1321
|
console.log('');
|
|
627
|
-
console.log(`${indent}${colors.
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
1322
|
+
console.log(`${indent}${colors.success(`${icons.sparkles} Compressing context (${currentMessages} msgs, ${tokenCount.toLocaleString()} > ${threshold.toLocaleString()}/${contextWindow.toLocaleString()} tokens, ${Math.round((tokenCount / contextWindow) * 100)}% of context window)...`)}`);
|
|
1323
|
+
}
|
|
1324
|
+
const toolRegistry = getToolRegistry();
|
|
1325
|
+
const baseSystemPrompt = this.currentAgent?.systemPrompt || 'You are a helpful AI assistant.';
|
|
1326
|
+
const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode);
|
|
1327
|
+
const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
|
|
1328
|
+
const result = await this.contextCompressor.compressContext(this.conversation, enhancedSystemPrompt, compressionConfig);
|
|
1329
|
+
if (result.wasCompressed) {
|
|
1330
|
+
this.conversation = result.compressedMessages;
|
|
1331
|
+
const reductionPercent = Math.round((1 - result.compressedSize / result.originalSize) * 100);
|
|
1332
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
1333
|
+
this.sdkOutputAdapter.outputContextCompressionResult(result.originalSize, result.compressedSize, reductionPercent, result.originalMessageCount, result.compressedMessageCount);
|
|
1334
|
+
}
|
|
1335
|
+
else {
|
|
1336
|
+
console.log(`${indent}${colors.success(`${icons.success} Compressed ${result.originalMessageCount} → ${result.compressedMessageCount} messages (${reductionPercent}% smaller)`)}`);
|
|
1337
|
+
}
|
|
1338
|
+
// Summary is embedded in first user message, look for it
|
|
1339
|
+
// The format is: "[Conversation Summary - X messages compressed]\n\n${summary}"
|
|
1340
|
+
let summaryMessage = result.compressedMessages.find((m) => m.role === 'user' && m.content.includes('[Conversation Summary'));
|
|
1341
|
+
if (summaryMessage) {
|
|
1342
|
+
// Extract summary content after the header
|
|
1343
|
+
const match = summaryMessage.content.match(/\[Conversation Summary.*?\]:\n\n(.+)/s);
|
|
1344
|
+
if (match) {
|
|
1345
|
+
summaryMessage = {
|
|
1346
|
+
role: 'assistant',
|
|
1347
|
+
content: match[1],
|
|
1348
|
+
timestamp: summaryMessage.timestamp,
|
|
1349
|
+
};
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
if (summaryMessage && summaryMessage.content) {
|
|
1353
|
+
const maxPreviewLength = 800;
|
|
1354
|
+
let summaryContent = summaryMessage.content;
|
|
1355
|
+
const isTruncated = summaryContent.length > maxPreviewLength;
|
|
1356
|
+
if (isTruncated) {
|
|
1357
|
+
summaryContent = summaryContent.substring(0, maxPreviewLength) + '\n...';
|
|
1358
|
+
}
|
|
1359
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
1360
|
+
this.sdkOutputAdapter.outputContextCompressionSummary('Conversation compressed successfully', summaryContent, isTruncated, summaryMessage.content.length);
|
|
1361
|
+
}
|
|
1362
|
+
else {
|
|
646
1363
|
console.log('');
|
|
647
1364
|
console.log(`${indent}${theme.predefinedStyles.title(`${icons.sparkles} Conversation Summary`)}`);
|
|
648
1365
|
const separator = icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length * 2);
|
|
@@ -654,13 +1371,9 @@ export class InteractiveSession {
|
|
|
654
1371
|
}
|
|
655
1372
|
console.log(`${indent}${colors.border(separator)}`);
|
|
656
1373
|
}
|
|
657
|
-
// Restore user messages after compression, ensuring user message exists for API calls
|
|
658
|
-
if (lastUserMessage) {
|
|
659
|
-
this.conversation.push(lastUserMessage);
|
|
660
|
-
}
|
|
661
|
-
// Sync compressed conversation history to slashCommandHandler
|
|
662
|
-
this.slashCommandHandler.setConversationHistory(this.conversation);
|
|
663
1374
|
}
|
|
1375
|
+
// Sync compressed conversation history to slashCommandHandler
|
|
1376
|
+
this.slashCommandHandler.setConversationHistory(this.conversation);
|
|
664
1377
|
}
|
|
665
1378
|
}
|
|
666
1379
|
async executeShellCommand(command) {
|
|
@@ -683,15 +1396,15 @@ export class InteractiveSession {
|
|
|
683
1396
|
tool: 'Bash',
|
|
684
1397
|
params: { command },
|
|
685
1398
|
result,
|
|
686
|
-
timestamp: Date.now()
|
|
1399
|
+
timestamp: Date.now(),
|
|
687
1400
|
};
|
|
688
|
-
this.
|
|
1401
|
+
this.tool_calls.push(toolCall);
|
|
689
1402
|
// Record command execution to session manager
|
|
690
1403
|
await this.sessionManager.addInput({
|
|
691
1404
|
type: 'command',
|
|
692
1405
|
content: command,
|
|
693
1406
|
rawInput: command,
|
|
694
|
-
timestamp: Date.now()
|
|
1407
|
+
timestamp: Date.now(),
|
|
695
1408
|
});
|
|
696
1409
|
await this.sessionManager.addOutput({
|
|
697
1410
|
role: 'tool',
|
|
@@ -699,7 +1412,7 @@ export class InteractiveSession {
|
|
|
699
1412
|
toolName: 'Bash',
|
|
700
1413
|
toolParams: { command },
|
|
701
1414
|
toolResult: result,
|
|
702
|
-
timestamp: Date.now()
|
|
1415
|
+
timestamp: Date.now(),
|
|
703
1416
|
});
|
|
704
1417
|
}
|
|
705
1418
|
catch (error) {
|
|
@@ -724,11 +1437,22 @@ export class InteractiveSession {
|
|
|
724
1437
|
/**
|
|
725
1438
|
* Create remote mode LLM caller
|
|
726
1439
|
*/
|
|
727
|
-
createRemoteCaller(taskId,
|
|
1440
|
+
createRemoteCaller(taskId, _status) {
|
|
728
1441
|
const client = this.remoteAIClient;
|
|
729
1442
|
return {
|
|
730
|
-
chatCompletion: (messages, options) =>
|
|
731
|
-
|
|
1443
|
+
chatCompletion: (messages, options) => {
|
|
1444
|
+
// Must fetch authConfig inside the closure, otherwise it captures stale config
|
|
1445
|
+
const authConfig = this.configManager.getAuthConfig();
|
|
1446
|
+
logger.debug(`[DEBUG] createRemoteCaller: llmModelName=${authConfig.remote_llmModelName}, vlmModelName=${authConfig.remote_vlmModelName}`);
|
|
1447
|
+
return client.chatCompletion(messages, {
|
|
1448
|
+
...options,
|
|
1449
|
+
taskId,
|
|
1450
|
+
status: options.isFirstApiCall ? 'begin' : 'continue',
|
|
1451
|
+
llmModelName: authConfig.remote_llmModelName,
|
|
1452
|
+
vlmModelName: authConfig.remote_vlmModelName
|
|
1453
|
+
});
|
|
1454
|
+
},
|
|
1455
|
+
isRemote: true,
|
|
732
1456
|
};
|
|
733
1457
|
}
|
|
734
1458
|
/**
|
|
@@ -738,47 +1462,43 @@ export class InteractiveSession {
|
|
|
738
1462
|
const client = this.aiClient;
|
|
739
1463
|
return {
|
|
740
1464
|
chatCompletion: (messages, options) => client.chatCompletion(messages, options),
|
|
741
|
-
isRemote: false
|
|
1465
|
+
isRemote: false,
|
|
742
1466
|
};
|
|
743
1467
|
}
|
|
744
|
-
async generateResponse(thinkingTokens = 0,
|
|
1468
|
+
async generateResponse(thinkingTokens = 0, _customAIClient, existingTaskId) {
|
|
745
1469
|
// Use existing taskId or create new one for this user interaction
|
|
746
1470
|
// If taskId already exists (e.g., from tool calls), reuse it
|
|
747
1471
|
const taskId = existingTaskId || this.currentTaskId || crypto.randomUUID();
|
|
748
1472
|
this.currentTaskId = taskId;
|
|
749
|
-
|
|
750
|
-
//
|
|
1473
|
+
// isFirstApiCall is reset in generateRemoteResponse for new tasks
|
|
1474
|
+
// For continuation calls (existingTaskId provided), keep previous value
|
|
1475
|
+
// Use unified LLM Caller with taskId (automatically selects local or remote mode)
|
|
751
1476
|
const status = this.isFirstApiCall ? 'begin' : 'continue';
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
if (
|
|
756
|
-
// Custom client (used by remote mode) - pass taskId and status
|
|
757
|
-
chatCompletion = (messages, options) => customAIClient.chatCompletion(messages, { ...options, taskId, status });
|
|
758
|
-
isRemote = true;
|
|
759
|
-
}
|
|
760
|
-
else {
|
|
761
|
-
// Use unified LLM Caller with taskId (automatically selects local or remote mode)
|
|
762
|
-
const caller = this.createLLMCaller(taskId, status);
|
|
763
|
-
chatCompletion = caller.chatCompletion;
|
|
764
|
-
isRemote = caller.isRemote;
|
|
765
|
-
}
|
|
766
|
-
if (!isRemote && !this.aiClient && !customAIClient) {
|
|
1477
|
+
const caller = this.createLLMCaller(taskId, status);
|
|
1478
|
+
const chatCompletion = caller.chatCompletion;
|
|
1479
|
+
const isRemote = caller.isRemote;
|
|
1480
|
+
if (!isRemote && !this.aiClient) {
|
|
767
1481
|
console.log(colors.error('AI client not initialized'));
|
|
768
1482
|
return;
|
|
769
1483
|
}
|
|
770
1484
|
// Mark that an operation is in progress
|
|
771
1485
|
this._isOperationInProgress = true;
|
|
772
|
-
const indent = this.getIndent();
|
|
773
1486
|
const thinkingText = colors.textMuted(`Thinking... (Press ESC to cancel)`);
|
|
774
1487
|
const icon = colors.primary(icons.brain);
|
|
775
1488
|
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
776
1489
|
let frameIndex = 0;
|
|
1490
|
+
// SDK 模式下不显示 spinner
|
|
1491
|
+
const showThinkingSpinner = !this.isSdkMode;
|
|
1492
|
+
let spinnerInterval = null;
|
|
777
1493
|
// Custom spinner: only icon rotates, text stays static
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
1494
|
+
if (showThinkingSpinner) {
|
|
1495
|
+
spinnerInterval = setInterval(() => {
|
|
1496
|
+
process.stdout.write(`\r${colors.primary(frames[frameIndex])} ${icon} ${thinkingText}`);
|
|
1497
|
+
frameIndex = (frameIndex + 1) % frames.length;
|
|
1498
|
+
}, 120);
|
|
1499
|
+
}
|
|
1500
|
+
let content = '';
|
|
1501
|
+
let reasoningContent = '';
|
|
782
1502
|
try {
|
|
783
1503
|
const memory = await this.memoryManager.loadMemory();
|
|
784
1504
|
const toolRegistry = getToolRegistry();
|
|
@@ -790,81 +1510,94 @@ export class InteractiveSession {
|
|
|
790
1510
|
const toolDefinitions = toolRegistry.getToolDefinitions();
|
|
791
1511
|
// Available tools for this session
|
|
792
1512
|
const availableTools = this.executionMode !== ExecutionMode.DEFAULT && allowedToolNames.length > 0
|
|
793
|
-
? toolDefinitions.filter((tool) =>
|
|
1513
|
+
? toolDefinitions.filter((tool) => typeof tool.function?.name === 'string' &&
|
|
1514
|
+
allowedToolNames.includes(tool.function.name))
|
|
794
1515
|
: toolDefinitions;
|
|
795
|
-
const baseSystemPrompt = this.currentAgent?.systemPrompt;
|
|
1516
|
+
const baseSystemPrompt = this.currentAgent?.systemPrompt ?? '';
|
|
796
1517
|
const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode, undefined, this.mcpManager);
|
|
797
1518
|
const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
|
|
798
1519
|
const messages = [
|
|
799
1520
|
{ role: 'system', content: `${enhancedSystemPrompt}\n\n${memory}`, timestamp: Date.now() },
|
|
800
|
-
...this.conversation
|
|
1521
|
+
...this.conversation,
|
|
801
1522
|
];
|
|
802
1523
|
const operationId = `ai-response-${Date.now()}`;
|
|
803
1524
|
const response = await this.cancellationManager.withCancellation(chatCompletion(messages, {
|
|
804
1525
|
tools: availableTools,
|
|
805
1526
|
toolChoice: availableTools.length > 0 ? 'auto' : 'none',
|
|
806
|
-
thinkingTokens
|
|
1527
|
+
thinkingTokens,
|
|
1528
|
+
isFirstApiCall: this.isFirstApiCall,
|
|
807
1529
|
}), operationId);
|
|
808
1530
|
// Mark that first API call is complete
|
|
809
1531
|
this.isFirstApiCall = false;
|
|
810
|
-
|
|
1532
|
+
if (spinnerInterval)
|
|
1533
|
+
clearInterval(spinnerInterval);
|
|
811
1534
|
process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r'); // Clear spinner line
|
|
812
1535
|
const assistantMessage = response.choices[0].message;
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
: '';
|
|
816
|
-
const reasoningContent = assistantMessage.reasoning_content || '';
|
|
1536
|
+
content = typeof assistantMessage.content === 'string' ? assistantMessage.content : '';
|
|
1537
|
+
reasoningContent = assistantMessage.reasoning_content || '';
|
|
817
1538
|
// Display reasoning content if available and thinking mode is enabled
|
|
818
1539
|
if (reasoningContent && this.configManager.getThinkingConfig().enabled) {
|
|
819
1540
|
this.displayThinkingContent(reasoningContent);
|
|
820
1541
|
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
console.log(`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`);
|
|
824
|
-
console.log('');
|
|
825
|
-
const renderedContent = renderMarkdown(content, (process.stdout.columns || 80) - indent.length * 2);
|
|
826
|
-
console.log(`${indent}${renderedContent.replace(/^/gm, indent)}`);
|
|
827
|
-
console.log('');
|
|
1542
|
+
// Output assistant response
|
|
1543
|
+
this.outputAssistant(content, reasoningContent);
|
|
828
1544
|
this.conversation.push({
|
|
829
1545
|
role: 'assistant',
|
|
830
1546
|
content,
|
|
831
1547
|
timestamp: Date.now(),
|
|
832
|
-
reasoningContent,
|
|
833
|
-
|
|
1548
|
+
reasoning_content: reasoningContent,
|
|
1549
|
+
tool_calls: assistantMessage.tool_calls,
|
|
834
1550
|
});
|
|
835
1551
|
// Record output to session manager
|
|
836
1552
|
await this.sessionManager.addOutput({
|
|
837
1553
|
role: 'assistant',
|
|
838
1554
|
content,
|
|
839
1555
|
timestamp: Date.now(),
|
|
840
|
-
reasoningContent,
|
|
841
|
-
|
|
1556
|
+
reasoning_content: reasoningContent,
|
|
1557
|
+
tool_calls: assistantMessage.tool_calls,
|
|
842
1558
|
});
|
|
843
1559
|
if (assistantMessage.tool_calls) {
|
|
844
1560
|
await this.handleToolCalls(assistantMessage.tool_calls);
|
|
845
1561
|
}
|
|
1562
|
+
else {
|
|
1563
|
+
await this.checkAndCompressContext();
|
|
1564
|
+
}
|
|
846
1565
|
if (this.checkpointManager.isEnabled()) {
|
|
847
|
-
await this.checkpointManager.createCheckpoint(`Response generated at ${new Date().toLocaleString()}`, [...this.conversation], [...this.
|
|
1566
|
+
await this.checkpointManager.createCheckpoint(`Response generated at ${new Date().toLocaleString()}`, [...this.conversation], [...this.tool_calls]);
|
|
1567
|
+
}
|
|
1568
|
+
// Signal request completion to SDK (no tools to execute)
|
|
1569
|
+
if (this.isSdkMode && this.sdkOutputAdapter && this._currentRequestId) {
|
|
1570
|
+
this.sdkOutputAdapter.outputRequestDone(this._currentRequestId, 'success');
|
|
1571
|
+
this._currentRequestId = null;
|
|
848
1572
|
}
|
|
849
1573
|
// Operation completed successfully, clear the flag
|
|
850
1574
|
this._isOperationInProgress = false;
|
|
851
1575
|
}
|
|
852
1576
|
catch (error) {
|
|
853
|
-
|
|
1577
|
+
if (spinnerInterval)
|
|
1578
|
+
clearInterval(spinnerInterval);
|
|
854
1579
|
process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r');
|
|
855
1580
|
// Clear the operation flag
|
|
856
1581
|
this._isOperationInProgress = false;
|
|
1582
|
+
// Signal request completion to SDK
|
|
1583
|
+
if (this.isSdkMode && this.sdkOutputAdapter && this._currentRequestId) {
|
|
1584
|
+
const status = error.message === 'Operation cancelled by user' ? 'cancelled' : 'error';
|
|
1585
|
+
this.sdkOutputAdapter.outputRequestDone(this._currentRequestId, status);
|
|
1586
|
+
this._currentRequestId = null;
|
|
1587
|
+
}
|
|
857
1588
|
if (error.message === 'Operation cancelled by user') {
|
|
858
|
-
//
|
|
1589
|
+
// Notify backend to cancel the task
|
|
859
1590
|
if (this.remoteAIClient && this.currentTaskId) {
|
|
860
|
-
await this.remoteAIClient.cancelTask(this.currentTaskId).catch(() => { });
|
|
1591
|
+
await this.remoteAIClient.cancelTask?.(this.currentTaskId).catch(() => { });
|
|
861
1592
|
}
|
|
862
1593
|
return;
|
|
863
1594
|
}
|
|
864
|
-
//
|
|
865
|
-
|
|
1595
|
+
// Distinguish error types: timeout vs other failures
|
|
1596
|
+
const isTimeout = error.message.includes('timeout') || error.message.includes('Timeout');
|
|
1597
|
+
const failureReason = isTimeout ? 'timeout' : 'failure';
|
|
1598
|
+
logger.debug(`[Session] Task failed: taskId=${this.currentTaskId}, error: ${error.message}, reason: ${failureReason}`);
|
|
866
1599
|
if (this.remoteAIClient && this.currentTaskId) {
|
|
867
|
-
await this.remoteAIClient.
|
|
1600
|
+
await this.remoteAIClient.failTask?.(this.currentTaskId, failureReason).catch(() => { });
|
|
868
1601
|
}
|
|
869
1602
|
console.log(colors.error(`Error: ${error.message}`));
|
|
870
1603
|
}
|
|
@@ -881,12 +1614,10 @@ export class InteractiveSession {
|
|
|
881
1614
|
const taskId = existingTaskId || crypto.randomUUID();
|
|
882
1615
|
this.currentTaskId = taskId;
|
|
883
1616
|
logger.debug(`[Session] generateRemoteResponse: taskId=${taskId}, existingTaskId=${!!existingTaskId}`);
|
|
884
|
-
//
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
// Determine status based on whether this is the first API call
|
|
889
|
-
const status = this.isFirstApiCall ? 'begin' : 'continue';
|
|
1617
|
+
// Each new user message is a fresh task - always set isFirstApiCall = true
|
|
1618
|
+
// This ensures status is 'begin' for every user message
|
|
1619
|
+
this.isFirstApiCall = true;
|
|
1620
|
+
const status = 'begin';
|
|
890
1621
|
logger.debug(`[Session] Status for this call: ${status}, isFirstApiCall=${this.isFirstApiCall}`);
|
|
891
1622
|
// Check if remote client is available
|
|
892
1623
|
if (!this.remoteAIClient) {
|
|
@@ -894,18 +1625,23 @@ export class InteractiveSession {
|
|
|
894
1625
|
return;
|
|
895
1626
|
}
|
|
896
1627
|
try {
|
|
897
|
-
//
|
|
898
|
-
|
|
1628
|
+
// Use unified generateResponse without passing customAIClient,
|
|
1629
|
+
// let createLLMCaller handle remote/local selection
|
|
1630
|
+
await this.generateResponse(thinkingTokens, undefined, taskId);
|
|
899
1631
|
// Mark task as completed (发送 status: 'end')
|
|
900
1632
|
logger.debug(`[Session] Task completed: taskId=${this.currentTaskId}`);
|
|
901
|
-
if (this.currentTaskId) {
|
|
902
|
-
await this.remoteAIClient.completeTask(this.currentTaskId);
|
|
1633
|
+
if (this.remoteAIClient && this.currentTaskId) {
|
|
1634
|
+
await this.remoteAIClient.completeTask?.(this.currentTaskId);
|
|
903
1635
|
}
|
|
904
1636
|
}
|
|
905
1637
|
catch (error) {
|
|
906
1638
|
// Clear the operation flag
|
|
907
1639
|
this._isOperationInProgress = false;
|
|
908
1640
|
if (error.message === 'Operation cancelled by user') {
|
|
1641
|
+
// Notify backend to cancel the task
|
|
1642
|
+
if (this.remoteAIClient && this.currentTaskId) {
|
|
1643
|
+
await this.remoteAIClient.cancelTask?.(this.currentTaskId).catch(() => { });
|
|
1644
|
+
}
|
|
909
1645
|
return;
|
|
910
1646
|
}
|
|
911
1647
|
// Handle token invalid error - trigger re-authentication
|
|
@@ -915,46 +1651,48 @@ export class InteractiveSession {
|
|
|
915
1651
|
console.log(colors.info('Your browser session has been logged out. Please log in again.'));
|
|
916
1652
|
console.log('');
|
|
917
1653
|
// Clear invalid credentials and persist
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
1654
|
+
this.configManager.set('apiKey', '');
|
|
1655
|
+
this.configManager.set('refreshToken', '');
|
|
1656
|
+
this.configManager.save('global');
|
|
921
1657
|
logger.debug('[DEBUG generateRemoteResponse] Cleared invalid credentials, starting re-authentication...');
|
|
922
1658
|
// Re-authenticate
|
|
923
1659
|
await this.setupAuthentication();
|
|
924
1660
|
// Reload config to ensure we have the latest authConfig
|
|
925
|
-
|
|
926
|
-
await this.configManager.load();
|
|
1661
|
+
this.configManager.load();
|
|
927
1662
|
const authConfig = this.configManager.getAuthConfig();
|
|
928
1663
|
logger.debug('[DEBUG generateRemoteResponse] After re-auth:');
|
|
929
|
-
logger.debug(' - authConfig.apiKey exists:',
|
|
930
|
-
// Recreate readline interface after
|
|
1664
|
+
logger.debug(' - authConfig.apiKey exists:', authConfig.apiKey ? 'true' : 'false');
|
|
1665
|
+
// Recreate readline interface after interactive prompt
|
|
931
1666
|
this.rl.close();
|
|
932
1667
|
this.rl = readline.createInterface({
|
|
933
1668
|
input: process.stdin,
|
|
934
|
-
output: process.stdout
|
|
1669
|
+
output: process.stdout,
|
|
935
1670
|
});
|
|
936
1671
|
this.rl.on('close', () => {
|
|
937
1672
|
logger.debug('DEBUG: readline interface closed');
|
|
938
1673
|
});
|
|
939
1674
|
// Reinitialize RemoteAIClient with new token
|
|
940
1675
|
if (authConfig.apiKey) {
|
|
941
|
-
const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
|
|
942
1676
|
logger.debug('[DEBUG generateRemoteResponse] Reinitializing RemoteAIClient with new token');
|
|
943
|
-
this.remoteAIClient =
|
|
1677
|
+
this.remoteAIClient = createAIClient(authConfig);
|
|
944
1678
|
}
|
|
945
1679
|
else {
|
|
946
1680
|
logger.debug('[DEBUG generateRemoteResponse] WARNING: No apiKey after re-authentication!');
|
|
947
1681
|
}
|
|
1682
|
+
// Sync remoteAIClient reference to slashCommandHandler for /provider command
|
|
1683
|
+
this.slashCommandHandler.setRemoteAIClient(this.remoteAIClient);
|
|
948
1684
|
// Retry the current operation
|
|
949
1685
|
console.log('');
|
|
950
1686
|
console.log(colors.info('Retrying with new authentication...'));
|
|
951
1687
|
console.log('');
|
|
952
1688
|
return this.generateRemoteResponse(thinkingTokens);
|
|
953
1689
|
}
|
|
954
|
-
//
|
|
955
|
-
|
|
1690
|
+
// Distinguish error types: timeout vs other failures
|
|
1691
|
+
const isTimeout = error.message.includes('timeout') || error.message.includes('Timeout');
|
|
1692
|
+
const failureReason = isTimeout ? 'timeout' : 'failure';
|
|
1693
|
+
logger.debug(`[Session] Task failed: taskId=${this.currentTaskId}, error: ${error.message}, reason: ${failureReason}`);
|
|
956
1694
|
if (this.remoteAIClient && this.currentTaskId) {
|
|
957
|
-
await this.remoteAIClient.
|
|
1695
|
+
await this.remoteAIClient.failTask?.(this.currentTaskId, failureReason).catch(() => { });
|
|
958
1696
|
}
|
|
959
1697
|
console.log(colors.error(`Error: ${error.message}`));
|
|
960
1698
|
return;
|
|
@@ -973,238 +1711,284 @@ export class InteractiveSession {
|
|
|
973
1711
|
try {
|
|
974
1712
|
parsedParams = typeof params === 'string' ? JSON.parse(params) : params;
|
|
975
1713
|
}
|
|
976
|
-
catch
|
|
1714
|
+
catch {
|
|
977
1715
|
parsedParams = params;
|
|
978
1716
|
}
|
|
979
1717
|
return { name, params: parsedParams, index, id: toolCall.id };
|
|
980
1718
|
});
|
|
981
1719
|
// Display all tool calls info
|
|
982
1720
|
for (const { name, params } of preparedToolCalls) {
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
console.log(`${indent}${colors.textDim(JSON.stringify(params, null, 2))}`);
|
|
1721
|
+
// SDK mode: use adapter output
|
|
1722
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
1723
|
+
this.sdkOutputAdapter.outputToolStart(name, params);
|
|
987
1724
|
}
|
|
988
1725
|
else {
|
|
989
|
-
|
|
990
|
-
|
|
1726
|
+
// Normal mode: console output
|
|
1727
|
+
if (showToolDetails) {
|
|
1728
|
+
console.log('');
|
|
1729
|
+
console.log(`${indent}${colors.warning(`${icons.tool} Tool Call: ${name}`)}`);
|
|
1730
|
+
console.log(`${indent}${colors.textDim(JSON.stringify(params, null, 2))}`);
|
|
1731
|
+
}
|
|
1732
|
+
else {
|
|
1733
|
+
const toolDescription = this.getToolDescription(name, params);
|
|
1734
|
+
console.log(`${indent}${colors.textMuted(`${icons.loading} ${toolDescription}`)}`);
|
|
1735
|
+
}
|
|
991
1736
|
}
|
|
992
1737
|
}
|
|
993
1738
|
// Execute all tools in parallel
|
|
994
|
-
const results = await toolRegistry.executeAll(preparedToolCalls.map(tc => ({ name: tc.name, params: tc.params })), this.executionMode);
|
|
995
|
-
//
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1739
|
+
const results = await toolRegistry.executeAll(preparedToolCalls.map((tc) => ({ name: tc.name, params: tc.params })), this.executionMode);
|
|
1740
|
+
// Create a map to store results by tool call index to maintain original order
|
|
1741
|
+
const resultsByIndex = new Map();
|
|
1742
|
+
const usedIndices = new Set();
|
|
1743
|
+
for (const result of results) {
|
|
1744
|
+
// Find the first unused original index in preparedToolCalls that matches the tool name
|
|
1745
|
+
const originalIndex = preparedToolCalls.findIndex((tc, idx) => tc.name === result.tool && !usedIndices.has(idx));
|
|
1746
|
+
if (originalIndex !== -1) {
|
|
1747
|
+
usedIndices.add(originalIndex);
|
|
1748
|
+
resultsByIndex.set(originalIndex, result);
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
// Process results in the original tool_calls order (critical for Anthropic format APIs)
|
|
1752
|
+
let _hasError = false;
|
|
1753
|
+
for (let i = 0; i < preparedToolCalls.length; i++) {
|
|
1754
|
+
const toolCall = preparedToolCalls[i];
|
|
1755
|
+
const { name: tool, params } = toolCall;
|
|
1756
|
+
const resultData = resultsByIndex.get(i);
|
|
1757
|
+
const result = resultData?.result;
|
|
1758
|
+
const error = resultData?.error;
|
|
1002
1759
|
if (error) {
|
|
1003
1760
|
if (error === 'Operation cancelled by user') {
|
|
1761
|
+
// Notify backend to cancel the task
|
|
1762
|
+
if (this.remoteAIClient && this.currentTaskId) {
|
|
1763
|
+
await this.remoteAIClient.cancelTask?.(this.currentTaskId).catch(() => { });
|
|
1764
|
+
}
|
|
1765
|
+
// 清理 conversation 中未完成的 tool_call
|
|
1766
|
+
this.cleanupIncompleteToolCalls();
|
|
1004
1767
|
this._isOperationInProgress = false;
|
|
1005
1768
|
return;
|
|
1006
1769
|
}
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1770
|
+
_hasError = true;
|
|
1771
|
+
// SDK mode: use adapter output
|
|
1772
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
1773
|
+
this.sdkOutputAdapter.outputToolError(tool, error);
|
|
1774
|
+
}
|
|
1775
|
+
else {
|
|
1776
|
+
// Normal mode: console output
|
|
1777
|
+
console.log('');
|
|
1778
|
+
console.log(`${indent}${colors.error(`${icons.cross} Tool Error: ${tool} - ${error}`)}`);
|
|
1779
|
+
}
|
|
1780
|
+
// Add detailed error info including tool name and params for AI understanding and correction
|
|
1011
1781
|
this.conversation.push({
|
|
1012
1782
|
role: 'tool',
|
|
1013
1783
|
content: JSON.stringify({
|
|
1014
1784
|
name: tool,
|
|
1015
1785
|
parameters: params,
|
|
1016
|
-
error: error
|
|
1786
|
+
error: error,
|
|
1017
1787
|
}),
|
|
1018
1788
|
tool_call_id: toolCall.id,
|
|
1019
|
-
timestamp: Date.now()
|
|
1789
|
+
timestamp: Date.now(),
|
|
1020
1790
|
});
|
|
1021
1791
|
}
|
|
1022
1792
|
else {
|
|
1793
|
+
// SDK mode: output tool result via adapter
|
|
1794
|
+
if (this.isSdkMode && this.sdkOutputAdapter) {
|
|
1795
|
+
this.sdkOutputAdapter.outputToolResult(tool, result);
|
|
1796
|
+
}
|
|
1023
1797
|
// Use correct indent for gui-subagent tasks
|
|
1024
1798
|
const isGuiSubagent = tool === 'task' && params?.subagent_type === 'gui-subagent';
|
|
1025
1799
|
const displayIndent = isGuiSubagent ? indent + ' ' : indent;
|
|
1026
|
-
//
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
else if (hasDiff) {
|
|
1054
|
-
// Show edit result with diff
|
|
1055
|
-
console.log('');
|
|
1056
|
-
const diffOutput = renderDiff(result.diff);
|
|
1057
|
-
const indentedDiff = diffOutput.split('\n').map(line => `${displayIndent} ${line}`).join('\n');
|
|
1058
|
-
console.log(`${indentedDiff}`);
|
|
1059
|
-
}
|
|
1060
|
-
else if (hasFilePreview) {
|
|
1061
|
-
// Show new file content in diff-like style
|
|
1062
|
-
console.log('');
|
|
1063
|
-
console.log(`${displayIndent}${colors.success(`${icons.file} ${result.filePath}`)}`);
|
|
1064
|
-
console.log(`${displayIndent}${colors.textDim(` ${result.lineCount} lines`)}`);
|
|
1065
|
-
console.log('');
|
|
1066
|
-
console.log(renderLines(result.preview, { maxLines: 10, indent: displayIndent + ' ' }));
|
|
1067
|
-
}
|
|
1068
|
-
else if (hasDeleteInfo) {
|
|
1069
|
-
// Show DeleteFile result
|
|
1070
|
-
console.log('');
|
|
1071
|
-
console.log(`${displayIndent}${colors.success(`${icons.check} Deleted: ${result.filePath}`)}`);
|
|
1072
|
-
}
|
|
1073
|
-
else if (isTaskTool) {
|
|
1074
|
-
// Special handling for task tool (subagent) - show friendly summary
|
|
1075
|
-
console.log('');
|
|
1076
|
-
const subagentType = params.subagent_type;
|
|
1077
|
-
const subagentName = params.description || (params.prompt ? params.prompt.substring(0, 50).replace(/\n/g, ' ') : 'Unknown task');
|
|
1078
|
-
if (result?.success) {
|
|
1079
|
-
console.log(`${displayIndent}${colors.success(`${icons.check} ${subagentType}: Completed`)}`);
|
|
1080
|
-
console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
|
|
1081
|
-
if (result.message) {
|
|
1082
|
-
console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
|
|
1800
|
+
// Normal mode: console output (SDK mode already output via adapter above)
|
|
1801
|
+
if (!this.isSdkMode || !this.sdkOutputAdapter) {
|
|
1802
|
+
// Always show details for todo tools so users can see their task lists
|
|
1803
|
+
const isTodoTool = tool === 'todo_write' || tool === 'todo_read';
|
|
1804
|
+
// Special handling for edit tool with diff
|
|
1805
|
+
const isEditTool = tool === 'Edit';
|
|
1806
|
+
const hasDiff = isEditTool && result?.diff;
|
|
1807
|
+
// Special handling for Write tool with file preview
|
|
1808
|
+
const isWriteTool = tool === 'Write';
|
|
1809
|
+
const hasFilePreview = isWriteTool && result?.preview;
|
|
1810
|
+
// Special handling for DeleteFile tool
|
|
1811
|
+
const isDeleteTool = tool === 'DeleteFile';
|
|
1812
|
+
const hasDeleteInfo = isDeleteTool && result?.filePath;
|
|
1813
|
+
// Special handling for task tool (subagent)
|
|
1814
|
+
const isTaskTool = tool === 'task' && params?.subagent_type;
|
|
1815
|
+
// Check if tool is an MCP wrapper tool by looking up in tool registry
|
|
1816
|
+
const { getToolRegistry } = await import('./tools.js');
|
|
1817
|
+
const toolRegistry = getToolRegistry();
|
|
1818
|
+
const toolDef = toolRegistry.get(tool);
|
|
1819
|
+
const isMcpTool = toolDef && toolDef._isMcpTool === true;
|
|
1820
|
+
if (isTodoTool) {
|
|
1821
|
+
console.log('');
|
|
1822
|
+
console.log(`${displayIndent}${colors.success(`${icons.check} Todo List:`)}`);
|
|
1823
|
+
console.log(this.renderTodoList(result?.todos || [], displayIndent));
|
|
1824
|
+
// Show summary if available
|
|
1825
|
+
if (result?.message) {
|
|
1826
|
+
console.log(`${displayIndent}${colors.textDim(result.message)}`);
|
|
1083
1827
|
}
|
|
1084
1828
|
}
|
|
1085
|
-
else if (
|
|
1086
|
-
|
|
1087
|
-
console.log(
|
|
1829
|
+
else if (hasDiff) {
|
|
1830
|
+
// Show edit result with diff
|
|
1831
|
+
console.log('');
|
|
1832
|
+
const diffOutput = renderDiff(result.diff);
|
|
1833
|
+
const indentedDiff = diffOutput
|
|
1834
|
+
.split('\n')
|
|
1835
|
+
.map((line) => `${displayIndent} ${line}`)
|
|
1836
|
+
.join('\n');
|
|
1837
|
+
console.log(`${indentedDiff}`);
|
|
1088
1838
|
}
|
|
1089
|
-
else {
|
|
1090
|
-
|
|
1091
|
-
console.log(
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1839
|
+
else if (hasFilePreview) {
|
|
1840
|
+
// Show new file content in diff-like style
|
|
1841
|
+
console.log('');
|
|
1842
|
+
console.log(`${displayIndent}${colors.success(`${icons.file} ${result.filePath}`)}`);
|
|
1843
|
+
console.log(`${displayIndent}${colors.textDim(` ${result.lineCount} lines`)}`);
|
|
1844
|
+
console.log('');
|
|
1845
|
+
console.log(renderLines(result.preview, { maxLines: 10, indent: displayIndent + ' ' }));
|
|
1095
1846
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
// Extract server name and tool name from tool name (format: serverName__toolName)
|
|
1101
|
-
let serverName = 'MCP';
|
|
1102
|
-
let toolDisplayName = tool;
|
|
1103
|
-
if (tool.includes('__')) {
|
|
1104
|
-
const parts = tool.split('__');
|
|
1105
|
-
serverName = parts[0];
|
|
1106
|
-
toolDisplayName = parts.slice(1).join('__');
|
|
1847
|
+
else if (hasDeleteInfo) {
|
|
1848
|
+
// Show DeleteFile result
|
|
1849
|
+
console.log('');
|
|
1850
|
+
console.log(`${displayIndent}${colors.success(`${icons.check} Deleted: ${result.filePath}`)}`);
|
|
1107
1851
|
}
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
const
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1852
|
+
else if (isTaskTool) {
|
|
1853
|
+
// Special handling for task tool (subagent) - show friendly summary
|
|
1854
|
+
console.log('');
|
|
1855
|
+
const subagentType = params.subagent_type;
|
|
1856
|
+
const subagentName = params.description ||
|
|
1857
|
+
(params.prompt ? params.prompt.substring(0, 50).replace(/\n/g, ' ') : 'Unknown task');
|
|
1858
|
+
if (result?.success) {
|
|
1859
|
+
console.log(`${displayIndent}${colors.success(`${icons.check} ${subagentType}: Completed`)}`);
|
|
1860
|
+
console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
|
|
1861
|
+
if (result.message) {
|
|
1862
|
+
console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
else if (result?.cancelled) {
|
|
1866
|
+
console.log(`${displayIndent}${colors.warning(`${icons.cross} ${subagentType}: Cancelled`)}`);
|
|
1867
|
+
console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
|
|
1868
|
+
}
|
|
1869
|
+
else {
|
|
1870
|
+
console.log(`${displayIndent}${colors.error(`${icons.cross} ${subagentType}: Failed`)}`);
|
|
1871
|
+
console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
|
|
1872
|
+
if (result?.message) {
|
|
1873
|
+
console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
else if (isMcpTool) {
|
|
1878
|
+
// Special handling for MCP tools - show friendly summary
|
|
1879
|
+
console.log('');
|
|
1880
|
+
// Extract server name and tool name from tool name (format: serverName__toolName)
|
|
1881
|
+
let serverName = 'MCP';
|
|
1882
|
+
let toolDisplayName = tool;
|
|
1883
|
+
if (tool.includes('__')) {
|
|
1884
|
+
const parts = tool.split('__');
|
|
1885
|
+
serverName = parts[0];
|
|
1886
|
+
toolDisplayName = parts.slice(1).join('__');
|
|
1887
|
+
}
|
|
1888
|
+
// Try to extract meaningful content from MCP result
|
|
1889
|
+
let summary = '';
|
|
1890
|
+
if (result?.content && Array.isArray(result.content) && result.content.length > 0) {
|
|
1891
|
+
const firstBlock = result.content[0];
|
|
1892
|
+
if (firstBlock?.type === 'text' && firstBlock?.text) {
|
|
1893
|
+
const text = firstBlock.text;
|
|
1894
|
+
if (typeof text === 'string') {
|
|
1895
|
+
// Detect HTML content
|
|
1896
|
+
if (text.trim().startsWith('<!DOCTYPE') || text.trim().startsWith('<html')) {
|
|
1897
|
+
summary = '[HTML content fetched]';
|
|
1898
|
+
}
|
|
1899
|
+
else {
|
|
1900
|
+
// Try to parse if it's JSON
|
|
1901
|
+
try {
|
|
1902
|
+
const parsed = JSON.parse(text);
|
|
1903
|
+
if (Array.isArray(parsed) && parsed.length > 0 && parsed[0]?.title) {
|
|
1904
|
+
// Search results format
|
|
1905
|
+
summary = `Found ${parsed.length} result(s)`;
|
|
1906
|
+
}
|
|
1907
|
+
else if (parsed?.message) {
|
|
1908
|
+
summary = parsed.message;
|
|
1909
|
+
}
|
|
1910
|
+
else if (typeof parsed === 'string') {
|
|
1911
|
+
summary = parsed.substring(0, 100);
|
|
1912
|
+
}
|
|
1129
1913
|
}
|
|
1130
|
-
|
|
1131
|
-
|
|
1914
|
+
catch {
|
|
1915
|
+
// Not JSON, use as-is with truncation
|
|
1916
|
+
summary = text.substring(0, 100);
|
|
1132
1917
|
}
|
|
1133
1918
|
}
|
|
1134
|
-
catch {
|
|
1135
|
-
// Not JSON, use as-is with truncation
|
|
1136
|
-
summary = text.substring(0, 100);
|
|
1137
|
-
}
|
|
1138
1919
|
}
|
|
1139
1920
|
}
|
|
1140
1921
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
summary = result.message;
|
|
1144
|
-
}
|
|
1145
|
-
if (result?.success !== false) {
|
|
1146
|
-
console.log(`${displayIndent}${colors.success(`${icons.check} ${serverName}: Success`)}`);
|
|
1147
|
-
console.log(`${displayIndent}${colors.textDim(` Tool: ${toolDisplayName}`)}`);
|
|
1148
|
-
if (summary) {
|
|
1149
|
-
console.log(`${displayIndent}${colors.textDim(` ${summary}`)}`);
|
|
1922
|
+
else if (result?.message) {
|
|
1923
|
+
summary = result.message;
|
|
1150
1924
|
}
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1925
|
+
if (result?.success !== false) {
|
|
1926
|
+
console.log(`${displayIndent}${colors.success(`${icons.check} ${serverName}: Success`)}`);
|
|
1927
|
+
console.log(`${displayIndent}${colors.textDim(` Tool: ${toolDisplayName}`)}`);
|
|
1928
|
+
if (summary) {
|
|
1929
|
+
console.log(`${displayIndent}${colors.textDim(` ${summary}`)}`);
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
else {
|
|
1933
|
+
console.log(`${displayIndent}${colors.error(`${icons.cross} ${serverName}: Failed`)}`);
|
|
1934
|
+
console.log(`${displayIndent}${colors.textDim(` Tool: ${toolDisplayName}`)}`);
|
|
1935
|
+
if (result?.message || result?.error) {
|
|
1936
|
+
console.log(`${displayIndent}${colors.textDim(` ${result?.message || result?.error}`)}`);
|
|
1937
|
+
}
|
|
1157
1938
|
}
|
|
1158
1939
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1940
|
+
else if (tool === 'InvokeSkill') {
|
|
1941
|
+
// Special handling for InvokeSkill - show friendly summary
|
|
1942
|
+
console.log('');
|
|
1943
|
+
const skillName = params?.skillId || 'Unknown skill';
|
|
1944
|
+
const taskDesc = params?.taskDescription || '';
|
|
1945
|
+
if (result?.success) {
|
|
1946
|
+
console.log(`${displayIndent}${colors.success(`${icons.check} Skill: Completed`)}`);
|
|
1947
|
+
console.log(`${displayIndent}${colors.textDim(` Skill: ${skillName}`)}`);
|
|
1948
|
+
if (taskDesc) {
|
|
1949
|
+
const truncatedTask = taskDesc.length > 60 ? taskDesc.substring(0, 60) + '...' : taskDesc;
|
|
1950
|
+
console.log(`${displayIndent}${colors.textDim(` Task: ${truncatedTask}`)}`);
|
|
1951
|
+
}
|
|
1171
1952
|
}
|
|
1953
|
+
else {
|
|
1954
|
+
console.log(`${displayIndent}${colors.error(`${icons.cross} Skill: Failed`)}`);
|
|
1955
|
+
console.log(`${displayIndent}${colors.textDim(` Skill: ${skillName}`)}`);
|
|
1956
|
+
if (result?.message) {
|
|
1957
|
+
console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
else if (showToolDetails) {
|
|
1962
|
+
console.log('');
|
|
1963
|
+
console.log(`${displayIndent}${colors.success(`${icons.check} Tool Result:`)}`);
|
|
1964
|
+
console.log(`${displayIndent}${colors.textDim(JSON.stringify(result, null, 2))}`);
|
|
1965
|
+
}
|
|
1966
|
+
else if (result && result.success === false) {
|
|
1967
|
+
// GUI task or other tool failed
|
|
1968
|
+
console.log(`${displayIndent}${colors.error(`${icons.cross} ${result.message || 'Failed'}`)}`);
|
|
1969
|
+
}
|
|
1970
|
+
else if (result) {
|
|
1971
|
+
// Show brief preview by default (consistent with subagent behavior)
|
|
1972
|
+
const resultPreview = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
1973
|
+
const truncatedPreview = resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
|
|
1974
|
+
// Indent the preview
|
|
1975
|
+
const indentedPreview = truncatedPreview
|
|
1976
|
+
.split('\n')
|
|
1977
|
+
.map((line) => `${displayIndent} ${line}`)
|
|
1978
|
+
.join('\n');
|
|
1979
|
+
console.log(`${indentedPreview}`);
|
|
1172
1980
|
}
|
|
1173
1981
|
else {
|
|
1174
|
-
console.log(`${displayIndent}${colors.
|
|
1175
|
-
console.log(`${displayIndent}${colors.textDim(` Skill: ${skillName}`)}`);
|
|
1176
|
-
if (result?.message) {
|
|
1177
|
-
console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
|
|
1178
|
-
}
|
|
1982
|
+
console.log(`${displayIndent}${colors.textDim('(no result)')}`);
|
|
1179
1983
|
}
|
|
1180
1984
|
}
|
|
1181
|
-
else if (showToolDetails) {
|
|
1182
|
-
console.log('');
|
|
1183
|
-
console.log(`${displayIndent}${colors.success(`${icons.check} Tool Result:`)}`);
|
|
1184
|
-
console.log(`${displayIndent}${colors.textDim(JSON.stringify(result, null, 2))}`);
|
|
1185
|
-
}
|
|
1186
|
-
else if (result && result.success === false) {
|
|
1187
|
-
// GUI task or other tool failed
|
|
1188
|
-
console.log(`${displayIndent}${colors.error(`${icons.cross} ${result.message || 'Failed'}`)}`);
|
|
1189
|
-
}
|
|
1190
|
-
else if (result) {
|
|
1191
|
-
// Show brief preview by default (consistent with subagent behavior)
|
|
1192
|
-
const resultPreview = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
1193
|
-
const truncatedPreview = resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
|
|
1194
|
-
// Indent the preview
|
|
1195
|
-
const indentedPreview = truncatedPreview.split('\n').map(line => `${displayIndent} ${line}`).join('\n');
|
|
1196
|
-
console.log(`${indentedPreview}`);
|
|
1197
|
-
}
|
|
1198
|
-
else {
|
|
1199
|
-
console.log(`${displayIndent}${colors.textDim('(no result)')}`);
|
|
1200
|
-
}
|
|
1201
1985
|
const toolCallRecord = {
|
|
1202
1986
|
tool,
|
|
1203
1987
|
params,
|
|
1204
1988
|
result,
|
|
1205
|
-
timestamp: Date.now()
|
|
1989
|
+
timestamp: Date.now(),
|
|
1206
1990
|
};
|
|
1207
|
-
this.
|
|
1991
|
+
this.tool_calls.push(toolCallRecord);
|
|
1208
1992
|
// Record tool output to session manager
|
|
1209
1993
|
await this.sessionManager.addOutput({
|
|
1210
1994
|
role: 'tool',
|
|
@@ -1212,77 +1996,104 @@ export class InteractiveSession {
|
|
|
1212
1996
|
toolName: tool,
|
|
1213
1997
|
toolParams: params,
|
|
1214
1998
|
toolResult: result,
|
|
1215
|
-
timestamp: Date.now()
|
|
1999
|
+
timestamp: Date.now(),
|
|
1216
2000
|
});
|
|
1217
|
-
//
|
|
2001
|
+
// Unified message format with tool name and params
|
|
2002
|
+
// Format: OpenAI-compatible tool result with plain text content
|
|
1218
2003
|
this.conversation.push({
|
|
1219
2004
|
role: 'tool',
|
|
1220
|
-
content: JSON.stringify(
|
|
1221
|
-
name: tool,
|
|
1222
|
-
parameters: params,
|
|
1223
|
-
result: result
|
|
1224
|
-
}),
|
|
2005
|
+
content: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
|
|
1225
2006
|
tool_call_id: toolCall.id,
|
|
1226
|
-
timestamp: Date.now()
|
|
2007
|
+
timestamp: Date.now(),
|
|
1227
2008
|
});
|
|
1228
2009
|
}
|
|
1229
2010
|
}
|
|
1230
2011
|
// Logic: Only skip returning results to main agent when user explicitly cancelled (ESC)
|
|
1231
2012
|
// For all other cases (success, failure, errors), always return results for further processing
|
|
1232
|
-
const guiSubagentCancelled = preparedToolCalls.some(tc => tc.name === 'task' &&
|
|
2013
|
+
const guiSubagentCancelled = preparedToolCalls.some((tc) => tc.name === 'task' &&
|
|
2014
|
+
tc.params?.subagent_type === 'gui-subagent' &&
|
|
2015
|
+
results.some((r) => r.tool === 'task' && r.result?.cancelled === true));
|
|
1233
2016
|
// If GUI agent was cancelled by user, don't continue generating response
|
|
1234
2017
|
// This avoids wasting API calls and tokens on cancelled tasks
|
|
1235
2018
|
if (guiSubagentCancelled) {
|
|
1236
|
-
console.log('');
|
|
1237
|
-
console.log(`${indent}${colors.textMuted('GUI task cancelled by user')}`);
|
|
1238
2019
|
this._isOperationInProgress = false;
|
|
1239
2020
|
return;
|
|
1240
2021
|
}
|
|
1241
|
-
//
|
|
1242
|
-
if (hasError) {
|
|
1243
|
-
this._isOperationInProgress = false;
|
|
1244
|
-
// 不再抛出异常,而是将错误结果返回给 AI,让 AI 决定如何处理
|
|
1245
|
-
// 这样可以避免工具错误导致程序退出
|
|
1246
|
-
}
|
|
1247
|
-
// Continue based on mode - 统一处理,无论是否有错误
|
|
2022
|
+
// Continue based on mode - unified handling for both success and error cases
|
|
1248
2023
|
if (onComplete) {
|
|
2024
|
+
await this.checkAndCompressContext();
|
|
1249
2025
|
// Remote mode: use provided callback
|
|
1250
2026
|
await onComplete();
|
|
1251
2027
|
}
|
|
1252
2028
|
else {
|
|
2029
|
+
await this.checkAndCompressContext();
|
|
1253
2030
|
// Local mode: default behavior - continue with generateResponse
|
|
1254
2031
|
await this.generateResponse();
|
|
1255
2032
|
}
|
|
1256
2033
|
}
|
|
2034
|
+
/**
|
|
2035
|
+
* Clean up incomplete tool calls from conversation after cancellation
|
|
2036
|
+
* This removes assistant messages with tool_calls that don't have corresponding tool_results
|
|
2037
|
+
*/
|
|
2038
|
+
async cleanupIncompleteToolCalls() {
|
|
2039
|
+
// 从后往前找到包含 tool_calls 的 assistant 消息
|
|
2040
|
+
for (let i = this.conversation.length - 1; i >= 0; i--) {
|
|
2041
|
+
const msg = this.conversation[i];
|
|
2042
|
+
if (msg.role === 'assistant' && msg.tool_calls?.length) {
|
|
2043
|
+
// 收集所有 tool_call IDs
|
|
2044
|
+
const allToolCallIds = new Set(msg.tool_calls.map((tc) => tc.id));
|
|
2045
|
+
// 找出哪些 tool_call IDs 已经有对应的 tool_result
|
|
2046
|
+
const completedToolCallIds = new Set();
|
|
2047
|
+
for (let k = i + 1; k < this.conversation.length; k++) {
|
|
2048
|
+
const resultMsg = this.conversation[k];
|
|
2049
|
+
if (resultMsg.role === 'tool' && resultMsg.tool_call_id) {
|
|
2050
|
+
completedToolCallIds.add(resultMsg.tool_call_id);
|
|
2051
|
+
}
|
|
2052
|
+
else if (resultMsg.role === 'user' || resultMsg.role === 'assistant') {
|
|
2053
|
+
break; // 遇到下一个角色消息就停止
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
// 找出未完成的 tool_call IDs
|
|
2057
|
+
const incompleteToolCallIds = [...allToolCallIds].filter(id => !completedToolCallIds.has(id));
|
|
2058
|
+
// 如果所有 tool_call 都已完成,不需要清理
|
|
2059
|
+
if (incompleteToolCallIds.length === 0) {
|
|
2060
|
+
break;
|
|
2061
|
+
}
|
|
2062
|
+
// 只移除未完成的 tool_call,不移除已完成的 tool_result
|
|
2063
|
+
msg.tool_calls = msg.tool_calls.filter((tc) => !incompleteToolCallIds.includes(tc.id));
|
|
2064
|
+
break;
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
}
|
|
1257
2068
|
/**
|
|
1258
2069
|
* Get user-friendly description for tool
|
|
1259
2070
|
*/
|
|
1260
2071
|
getToolDescription(toolName, params) {
|
|
1261
2072
|
const descriptions = {
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
2073
|
+
Read: (p) => `Read file: ${this.truncatePath(p.filePath)}`,
|
|
2074
|
+
Write: (p) => `Write file: ${this.truncatePath(p.filePath)}`,
|
|
2075
|
+
Grep: (p) => `Search text: "${p.pattern}"`,
|
|
2076
|
+
Bash: (p) => `Execute command: ${this.truncateCommand(p.command)}`,
|
|
2077
|
+
ListDirectory: (p) => `List directory: ${this.truncatePath(p.path || '.')}`,
|
|
2078
|
+
SearchFiles: (p) => `Search files: ${p.pattern}`,
|
|
2079
|
+
DeleteFile: (p) => `Delete file: ${this.truncatePath(p.filePath)}`,
|
|
2080
|
+
CreateDirectory: (p) => `Create directory: ${this.truncatePath(p.dirPath)}`,
|
|
2081
|
+
Edit: (p) => `Edit text: ${this.truncatePath(p.file_path)}`,
|
|
2082
|
+
web_search: (p) => `Web search: "${p.query}"`,
|
|
2083
|
+
todo_write: () => `Update todo list`,
|
|
2084
|
+
todo_read: () => `Read todo list`,
|
|
2085
|
+
task: (p) => `Launch subtask: ${p.description}`,
|
|
2086
|
+
ReadBashOutput: (p) => `Read task output: ${p.task_id}`,
|
|
2087
|
+
web_fetch: () => `Fetch web content`,
|
|
2088
|
+
ask_user_question: () => `Ask user`,
|
|
2089
|
+
save_memory: () => `Save memory`,
|
|
2090
|
+
exit_plan_mode: () => `Complete plan`,
|
|
2091
|
+
xml_escape: (p) => `XML escape: ${this.truncatePath(p.file_path)}`,
|
|
2092
|
+
image_read: (p) => `Read image: ${this.truncatePath(p.image_input)}`,
|
|
1282
2093
|
// 'Skill': (p) => `Execute skill: ${p.skill}`,
|
|
1283
2094
|
// 'ListSkills': () => `List available skills`,
|
|
1284
2095
|
// 'GetSkillDetails': (p) => `Get skill details: ${p.skill}`,
|
|
1285
|
-
|
|
2096
|
+
InvokeSkill: (p) => `Invoke skill: ${p.skillId} - ${this.truncatePath(p.taskDescription || '', 40)}`,
|
|
1286
2097
|
};
|
|
1287
2098
|
const getDescription = descriptions[toolName];
|
|
1288
2099
|
return getDescription ? getDescription(params) : `Execute tool: ${toolName}`;
|
|
@@ -1315,10 +2126,10 @@ export class InteractiveSession {
|
|
|
1315
2126
|
return `${indent}${colors.textMuted('No tasks')}`;
|
|
1316
2127
|
}
|
|
1317
2128
|
const statusConfig = {
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
2129
|
+
pending: { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
|
|
2130
|
+
in_progress: { icon: icons.loading, color: colors.warning, label: 'In Progress' },
|
|
2131
|
+
completed: { icon: icons.success, color: colors.success, label: 'Completed' },
|
|
2132
|
+
failed: { icon: icons.error, color: colors.error, label: 'Failed' },
|
|
1322
2133
|
};
|
|
1323
2134
|
const lines = [];
|
|
1324
2135
|
for (const todo of todos) {
|
|
@@ -1431,7 +2242,155 @@ export class InteractiveSession {
|
|
|
1431
2242
|
return this.currentTaskId;
|
|
1432
2243
|
}
|
|
1433
2244
|
}
|
|
2245
|
+
/**
|
|
2246
|
+
* Clean up stale temporary workspaces from previous sessions.
|
|
2247
|
+
* Called at startup to ensure no leftover temp files accumulate.
|
|
2248
|
+
*/
|
|
2249
|
+
async function cleanupStaleWorkspaces() {
|
|
2250
|
+
try {
|
|
2251
|
+
const configManager = getConfigManager();
|
|
2252
|
+
const workspacePath = configManager.getWorkspacePath();
|
|
2253
|
+
// Use default workspace path if workspacePath is empty or undefined
|
|
2254
|
+
const effectiveWorkspacePath = (workspacePath && workspacePath.trim())
|
|
2255
|
+
? workspacePath
|
|
2256
|
+
: os.homedir();
|
|
2257
|
+
const baseWorkspaceDir = path.join(effectiveWorkspacePath, '.xagent', 'workspace');
|
|
2258
|
+
// First, verify the workspace directory exists before attempting cleanup
|
|
2259
|
+
try {
|
|
2260
|
+
const stats = await fsPromises.stat(baseWorkspaceDir);
|
|
2261
|
+
if (!stats.isDirectory()) {
|
|
2262
|
+
return; // Not a directory, skip cleanup
|
|
2263
|
+
}
|
|
2264
|
+
}
|
|
2265
|
+
catch {
|
|
2266
|
+
// Directory doesn't exist - nothing to clean
|
|
2267
|
+
return;
|
|
2268
|
+
}
|
|
2269
|
+
try {
|
|
2270
|
+
const entries = await fsPromises.readdir(baseWorkspaceDir, { withFileTypes: true });
|
|
2271
|
+
let cleanedCount = 0;
|
|
2272
|
+
for (const entry of entries) {
|
|
2273
|
+
if (entry.isDirectory()) {
|
|
2274
|
+
const fullPath = path.join(baseWorkspaceDir, entry.name);
|
|
2275
|
+
try {
|
|
2276
|
+
await fsPromises.rm(fullPath, { recursive: true, force: true });
|
|
2277
|
+
cleanedCount++;
|
|
2278
|
+
}
|
|
2279
|
+
catch {
|
|
2280
|
+
// Skip directories that can't be removed (in use or permission issues)
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
if (cleanedCount > 0) {
|
|
2285
|
+
console.log(colors.textDim(`🧹 Cleaned up ${cleanedCount} stale workspace(s)`));
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
catch {
|
|
2289
|
+
// Can't read directory - skip cleanup
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
catch {
|
|
2293
|
+
// Config not available - skip cleanup
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
/**
|
|
2297
|
+
* Initialize built-in skills on first run.
|
|
2298
|
+
* Checks if user skills directory is empty or doesn't exist,
|
|
2299
|
+
* then copies all built-in skills including the protected find-skills.
|
|
2300
|
+
* @returns Number of skills initialized, or 0 if no initialization was needed.
|
|
2301
|
+
*/
|
|
2302
|
+
async function initializeSkillsOnDemand() {
|
|
2303
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
2304
|
+
const __dirname = path.dirname(__filename);
|
|
2305
|
+
// Get user skills directory (respects OS-specific paths)
|
|
2306
|
+
const configManager = getConfigManager();
|
|
2307
|
+
const userSkillsPath = configManager.getUserSkillsPath() || path.join(os.homedir(), '.xagent', 'skills');
|
|
2308
|
+
// Check if user skills directory exists and has skills
|
|
2309
|
+
let hasSkills = false;
|
|
2310
|
+
try {
|
|
2311
|
+
const entries = await fsPromises.readdir(userSkillsPath, { withFileTypes: true });
|
|
2312
|
+
hasSkills = entries.some(e => e.isDirectory());
|
|
2313
|
+
}
|
|
2314
|
+
catch {
|
|
2315
|
+
hasSkills = false;
|
|
2316
|
+
}
|
|
2317
|
+
// If skills already exist, skip initialization
|
|
2318
|
+
if (hasSkills) {
|
|
2319
|
+
return 0;
|
|
2320
|
+
}
|
|
2321
|
+
// Ensure user skills directory exists
|
|
2322
|
+
await fsPromises.mkdir(userSkillsPath, { recursive: true });
|
|
2323
|
+
// Define skill source directories
|
|
2324
|
+
const builtinSkillsDir = path.join(__dirname, '..', 'skills', 'skills');
|
|
2325
|
+
const findSkillsDir = path.join(__dirname, '..', 'find-skills');
|
|
2326
|
+
const skillsToInstall = [];
|
|
2327
|
+
// Add find-skills from root directory
|
|
2328
|
+
if (fs.existsSync(findSkillsDir) && fs.existsSync(path.join(findSkillsDir, 'SKILL.md'))) {
|
|
2329
|
+
skillsToInstall.push({ source: findSkillsDir, name: 'find-skills' });
|
|
2330
|
+
}
|
|
2331
|
+
// Add skills from skills/skills directory
|
|
2332
|
+
if (fs.existsSync(builtinSkillsDir)) {
|
|
2333
|
+
const entries = fs.readdirSync(builtinSkillsDir, { withFileTypes: true });
|
|
2334
|
+
for (const entry of entries) {
|
|
2335
|
+
if (entry.isDirectory()) {
|
|
2336
|
+
const skillPath = path.join(builtinSkillsDir, entry.name);
|
|
2337
|
+
if (fs.existsSync(path.join(skillPath, 'SKILL.md'))) {
|
|
2338
|
+
skillsToInstall.push({ source: skillPath, name: entry.name });
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
if (skillsToInstall.length === 0) {
|
|
2344
|
+
return 0;
|
|
2345
|
+
}
|
|
2346
|
+
// Create user skills directory (already done above, but ensure it exists)
|
|
2347
|
+
await fsPromises.mkdir(userSkillsPath, { recursive: true });
|
|
2348
|
+
// Copy all skills
|
|
2349
|
+
for (const { source, name } of skillsToInstall) {
|
|
2350
|
+
const destPath = path.join(userSkillsPath, name);
|
|
2351
|
+
if (!fs.existsSync(destPath)) {
|
|
2352
|
+
await copyDirectoryRecursiveAsync(source, destPath);
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
return skillsToInstall.length;
|
|
2356
|
+
}
|
|
2357
|
+
// Synchronous version (kept for backwards compatibility)
|
|
2358
|
+
function _copyDirectoryRecursive(src, dest) {
|
|
2359
|
+
if (!fs.existsSync(dest)) {
|
|
2360
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
2361
|
+
}
|
|
2362
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
2363
|
+
for (const entry of entries) {
|
|
2364
|
+
const srcPath = path.join(src, entry.name);
|
|
2365
|
+
const destPath = path.join(dest, entry.name);
|
|
2366
|
+
if (entry.isDirectory()) {
|
|
2367
|
+
_copyDirectoryRecursive(srcPath, destPath);
|
|
2368
|
+
}
|
|
2369
|
+
else if (entry.isFile()) {
|
|
2370
|
+
fs.copyFileSync(srcPath, destPath);
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
// Asynchronous version for concurrent-safe initialization
|
|
2375
|
+
async function copyDirectoryRecursiveAsync(src, dest) {
|
|
2376
|
+
await fsPromises.mkdir(dest, { recursive: true });
|
|
2377
|
+
const entries = await fsPromises.readdir(src, { withFileTypes: true });
|
|
2378
|
+
for (const entry of entries) {
|
|
2379
|
+
const srcPath = path.join(src, entry.name);
|
|
2380
|
+
const destPath = path.join(dest, entry.name);
|
|
2381
|
+
if (entry.isDirectory()) {
|
|
2382
|
+
await copyDirectoryRecursiveAsync(srcPath, destPath);
|
|
2383
|
+
}
|
|
2384
|
+
else if (entry.isFile()) {
|
|
2385
|
+
await fsPromises.copyFile(srcPath, destPath);
|
|
2386
|
+
}
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
1434
2389
|
export async function startInteractiveSession() {
|
|
2390
|
+
// Clean up any leftover temp workspaces from previous sessions
|
|
2391
|
+
await cleanupStaleWorkspaces();
|
|
2392
|
+
// Initialize built-in skills on first run (silent, returns count)
|
|
2393
|
+
const initializedCount = await initializeSkillsOnDemand();
|
|
1435
2394
|
const session = new InteractiveSession();
|
|
1436
2395
|
// Flag to control shutdown
|
|
1437
2396
|
session._isShuttingDown = false;
|
|
@@ -1467,6 +2426,15 @@ export async function startInteractiveSession() {
|
|
|
1467
2426
|
// Force exit
|
|
1468
2427
|
process.exit(0);
|
|
1469
2428
|
});
|
|
2429
|
+
await session.start(initializedCount);
|
|
2430
|
+
// Check for updates on startup
|
|
2431
|
+
try {
|
|
2432
|
+
const { checkUpdatesOnStartup } = await import('./update.js');
|
|
2433
|
+
await checkUpdatesOnStartup();
|
|
2434
|
+
}
|
|
2435
|
+
catch {
|
|
2436
|
+
// Silently ignore update check failures
|
|
2437
|
+
}
|
|
1470
2438
|
await session.start();
|
|
1471
2439
|
}
|
|
1472
2440
|
// Singleton session instance for access from other modules
|