@xagent-ai/cli 1.2.1 → 1.3.0
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/workflows/ci.yml +72 -0
- package/.github/workflows/release.yml +109 -0
- package/.gitmodules +3 -3
- package/README.md +326 -280
- package/README_CN.md +325 -279
- package/dist/ai-client/factory.d.ts +52 -0
- package/dist/ai-client/factory.d.ts.map +1 -0
- package/dist/ai-client/factory.js +132 -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 +400 -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 +286 -0
- package/dist/ai-client/providers/openai.js.map +1 -0
- package/dist/ai-client/providers/remote.d.ts +111 -0
- package/dist/ai-client/providers/remote.d.ts.map +1 -0
- package/dist/ai-client/providers/remote.js +351 -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 +260 -0
- package/dist/ai-client/types.d.ts.map +1 -0
- package/dist/ai-client/types.js +73 -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 +160 -168
- 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 +55 -32
- package/dist/cancellation.js.map +1 -1
- package/dist/checkpoint.d.ts +1 -1
- package/dist/checkpoint.d.ts.map +1 -1
- package/dist/checkpoint.js +2 -2
- package/dist/checkpoint.js.map +1 -1
- package/dist/cli.js +626 -13
- 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 +81 -16
- package/dist/context-compressor.d.ts.map +1 -1
- package/dist/context-compressor.js +712 -153
- package/dist/context-compressor.js.map +1 -1
- package/dist/gui-subagent/action-parser/actionParser.d.ts.map +1 -1
- package/dist/gui-subagent/action-parser/actionParser.js +4 -2
- package/dist/gui-subagent/action-parser/actionParser.js.map +1 -1
- package/dist/gui-subagent/agent/gui-agent.d.ts +29 -2
- package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -1
- package/dist/gui-subagent/agent/gui-agent.js +87 -45
- package/dist/gui-subagent/agent/gui-agent.js.map +1 -1
- package/dist/gui-subagent/index.d.ts +16 -1
- package/dist/gui-subagent/index.d.ts.map +1 -1
- package/dist/gui-subagent/index.js +4 -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 +29 -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 +6 -3
- package/dist/input-processor.js.map +1 -1
- package/dist/mcp.d.ts +5 -0
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +81 -35
- package/dist/mcp.js.map +1 -1
- package/dist/ripgrep.d.ts +29 -0
- package/dist/ripgrep.d.ts.map +1 -0
- package/dist/ripgrep.js +292 -0
- package/dist/ripgrep.js.map +1 -0
- package/dist/session.d.ts +23 -7
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +658 -347
- 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 +125 -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 +7 -1
- package/dist/skill-invoker.d.ts.map +1 -1
- package/dist/skill-invoker.js +34 -13
- 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 +46 -44
- 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 +340 -0
- package/dist/skill-manager.js.map +1 -0
- package/dist/slash-commands.d.ts +38 -1
- package/dist/slash-commands.d.ts.map +1 -1
- package/dist/slash-commands.js +912 -296
- package/dist/slash-commands.js.map +1 -1
- package/dist/smart-approval.d.ts.map +1 -1
- package/dist/smart-approval.js +83 -71
- 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 +88 -38
- 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/tools.d.ts +23 -7
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +800 -440
- 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 +27 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +17 -28
- 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 +60 -47
- 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 +439 -439
- package/find-skills/SKILL.md +133 -0
- package/package.json +89 -90
- package/scripts/install-ripgrep.js +241 -0
- package/src/ai-client/factory.ts +151 -0
- package/src/ai-client/index.ts +61 -0
- package/src/ai-client/providers/anthropic.ts +466 -0
- package/src/ai-client/providers/openai.ts +342 -0
- package/src/ai-client/providers/remote.ts +436 -0
- package/src/ai-client/registry.ts +97 -0
- package/src/ai-client/types.ts +345 -0
- package/src/ai-client-factory.ts +204 -0
- package/src/auth.ts +663 -614
- package/src/cancellation.ts +205 -176
- package/src/checkpoint.ts +219 -219
- package/src/cli.ts +1406 -743
- package/src/config.ts +341 -297
- package/src/context-compressor.ts +982 -290
- package/src/conversation.ts +288 -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 +1151 -1089
- package/src/gui-subagent/agent/index.ts +5 -5
- package/src/gui-subagent/index.ts +177 -163
- package/src/gui-subagent/operator/base-operator.ts +244 -245
- package/src/gui-subagent/operator/computer-operator.ts +540 -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 +6 -3
- package/src/logger.ts +438 -438
- package/src/mcp.ts +730 -682
- package/src/memory.ts +344 -344
- package/src/ripgrep.ts +368 -0
- package/src/session-manager.ts +308 -308
- package/src/session.ts +955 -390
- package/src/shell.ts +133 -0
- package/src/skill-installer.ts +518 -0
- package/src/skill-invoker.ts +960 -935
- package/src/skill-loader.ts +501 -496
- package/src/skill-manager.ts +384 -0
- package/src/slash-commands.ts +2181 -1389
- package/src/smart-approval.ts +117 -73
- package/src/system-prompt-generator.ts +89 -34
- package/src/terminal.ts +96 -0
- package/src/theme.ts +738 -738
- package/src/tools.ts +1336 -773
- package/src/truncate.ts +173 -0
- package/src/types.ts +219 -198
- package/src/update.ts +22 -32
- package/src/workflow.ts +523 -508
- package/tsconfig.json +22 -22
- package/vitest.config.ts +19 -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-output-adapter.d.ts +0 -232
- package/dist/sdk-output-adapter.d.ts.map +0 -1
- package/dist/sdk-output-adapter.js +0 -636
- package/dist/sdk-output-adapter.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/sdk-session.d.ts +0 -13
- package/dist/sdk-session.d.ts.map +0 -1
- package/dist/sdk-session.js +0 -48
- package/dist/sdk-session.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/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;
|
|
@@ -53,7 +61,7 @@ export class InteractiveSession {
|
|
|
53
61
|
constructor(indentLevel = 0) {
|
|
54
62
|
this.rl = readline.createInterface({
|
|
55
63
|
input: process.stdin,
|
|
56
|
-
output: process.stdout
|
|
64
|
+
output: process.stdout,
|
|
57
65
|
});
|
|
58
66
|
this.configManager = getConfigManager(process.cwd());
|
|
59
67
|
this.agentManager = getAgentManager(process.cwd());
|
|
@@ -66,7 +74,7 @@ export class InteractiveSession {
|
|
|
66
74
|
// Register /clear callback, clear local conversation when clearing dialogue
|
|
67
75
|
this.slashCommandHandler.setClearCallback(() => {
|
|
68
76
|
this.conversation = [];
|
|
69
|
-
this.
|
|
77
|
+
this.tool_calls = [];
|
|
70
78
|
this.currentTaskId = null;
|
|
71
79
|
this.taskCompleted = false;
|
|
72
80
|
this.isFirstApiCall = true;
|
|
@@ -76,6 +84,10 @@ export class InteractiveSession {
|
|
|
76
84
|
this.slashCommandHandler.setSystemPromptUpdateCallback(async () => {
|
|
77
85
|
await this.updateSystemPrompt();
|
|
78
86
|
});
|
|
87
|
+
// Register config update callback, update aiClient config when /auth changes config
|
|
88
|
+
this.slashCommandHandler.setConfigUpdateCallback(() => {
|
|
89
|
+
this.updateAiClientConfig();
|
|
90
|
+
});
|
|
79
91
|
this.executionMode = ExecutionMode.DEFAULT;
|
|
80
92
|
this.cancellationManager = getCancellationManager();
|
|
81
93
|
this.indentLevel = indentLevel;
|
|
@@ -88,6 +100,32 @@ export class InteractiveSession {
|
|
|
88
100
|
setAIClient(aiClient) {
|
|
89
101
|
this.aiClient = aiClient;
|
|
90
102
|
}
|
|
103
|
+
/**
|
|
104
|
+
* Update aiClient config when /auth changes config (called from callback)
|
|
105
|
+
*/
|
|
106
|
+
updateAiClientConfig() {
|
|
107
|
+
const authConfig = this.configManager.getAuthConfig();
|
|
108
|
+
const isRemote = authConfig.type === AuthType.OAUTH_XAGENT;
|
|
109
|
+
if (isRemote) {
|
|
110
|
+
// Already in remote mode, no change needed
|
|
111
|
+
if (this.remoteAIClient !== null) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// Switch to remote: clear local client, create remote client
|
|
115
|
+
this.aiClient = null;
|
|
116
|
+
this.remoteAIClient = createAIClient(authConfig);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Already in local mode, no change needed
|
|
120
|
+
if (this.aiClient !== null) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
// Switch to local: clear remote client, create local client
|
|
124
|
+
this.remoteAIClient = null;
|
|
125
|
+
this.aiClient = createAIClient(authConfig);
|
|
126
|
+
}
|
|
127
|
+
this.slashCommandHandler.setRemoteAIClient(this.remoteAIClient);
|
|
128
|
+
}
|
|
91
129
|
setExecutionMode(mode) {
|
|
92
130
|
this.executionMode = mode;
|
|
93
131
|
}
|
|
@@ -95,25 +133,68 @@ export class InteractiveSession {
|
|
|
95
133
|
* Update system prompt to reflect MCP changes (called after add/remove MCP)
|
|
96
134
|
*/
|
|
97
135
|
async updateSystemPrompt() {
|
|
136
|
+
// Reload skills to pick up any newly added/removed skills
|
|
137
|
+
// First reset the SkillLoader to clear all cached skills
|
|
138
|
+
const { resetSkillLoader } = await import('./skill-loader.js');
|
|
139
|
+
resetSkillLoader();
|
|
140
|
+
// Then reload the skill invoker
|
|
141
|
+
const skillInvoker = (await import('./skill-invoker.js')).getSkillInvoker();
|
|
142
|
+
await skillInvoker.reload();
|
|
98
143
|
const toolRegistry = getToolRegistry();
|
|
99
144
|
const promptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode, undefined, this.mcpManager);
|
|
100
145
|
// Use the current agent's original system prompt as base
|
|
101
146
|
const baseSystemPrompt = this.currentAgent?.systemPrompt || 'You are xAgent, an AI-powered CLI tool.';
|
|
102
147
|
const newSystemPrompt = await promptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
|
|
103
148
|
// Replace old system prompt with new one
|
|
104
|
-
this.conversation = this.conversation.filter(msg => msg.role !== 'system');
|
|
149
|
+
this.conversation = this.conversation.filter((msg) => msg.role !== 'system');
|
|
105
150
|
this.conversation.unshift({
|
|
106
151
|
role: 'system',
|
|
107
152
|
content: newSystemPrompt,
|
|
108
|
-
timestamp: Date.now()
|
|
153
|
+
timestamp: Date.now(),
|
|
109
154
|
});
|
|
110
155
|
// Sync to slashCommandHandler
|
|
111
156
|
this.slashCommandHandler.setConversationHistory(this.conversation);
|
|
112
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Watch for skill updates from CLI and update system prompt accordingly
|
|
160
|
+
*/
|
|
161
|
+
startSkillUpdateWatcher() {
|
|
162
|
+
const SKILL_STATE_FILE = '.skill-state.json';
|
|
163
|
+
const configManager = getConfigManager();
|
|
164
|
+
const userSkillsPath = configManager.getUserSkillsPath();
|
|
165
|
+
const stateFilePath = userSkillsPath
|
|
166
|
+
? path.join(userSkillsPath, SKILL_STATE_FILE)
|
|
167
|
+
: null;
|
|
168
|
+
if (!stateFilePath) {
|
|
169
|
+
return; // No user skills path configured
|
|
170
|
+
}
|
|
171
|
+
let lastUpdateTime = 0;
|
|
172
|
+
// Check for updates every 2 seconds
|
|
173
|
+
const checkInterval = setInterval(async () => {
|
|
174
|
+
try {
|
|
175
|
+
const { existsSync, readFileSync } = await import('fs');
|
|
176
|
+
if (existsSync(stateFilePath)) {
|
|
177
|
+
const content = readFileSync(stateFilePath, 'utf-8');
|
|
178
|
+
const state = JSON.parse(content);
|
|
179
|
+
if (state.lastSkillUpdate && state.lastSkillUpdate > lastUpdateTime) {
|
|
180
|
+
lastUpdateTime = state.lastSkillUpdate;
|
|
181
|
+
// Update system prompt with new skills
|
|
182
|
+
await this.updateSystemPrompt();
|
|
183
|
+
console.log(colors.textMuted(' 🔄 Skills updated from CLI'));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
// Silent fail - watcher is optional
|
|
189
|
+
}
|
|
190
|
+
}, 2000);
|
|
191
|
+
// Clean up on session end
|
|
192
|
+
this._skillWatcherInterval = checkInterval;
|
|
193
|
+
}
|
|
113
194
|
setAgent(agent) {
|
|
114
195
|
this.currentAgent = agent;
|
|
115
196
|
}
|
|
116
|
-
async start() {
|
|
197
|
+
async start(initializedCount = 0) {
|
|
117
198
|
// Set this session as the singleton for access from other modules
|
|
118
199
|
setSingletonSession(this);
|
|
119
200
|
// Initialize taskId for GUI operations
|
|
@@ -122,25 +203,43 @@ export class InteractiveSession {
|
|
|
122
203
|
console.log('');
|
|
123
204
|
console.log(colors.gradient('╔════════════════════════════════════════════════════════════╗'));
|
|
124
205
|
console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
|
|
125
|
-
console.log(colors.gradient('║') +
|
|
126
|
-
|
|
206
|
+
console.log(colors.gradient('║') +
|
|
207
|
+
' '.repeat(13) +
|
|
208
|
+
'🤖 ' +
|
|
209
|
+
colors.gradient('XAGENT CLI') +
|
|
210
|
+
' '.repeat(32) +
|
|
211
|
+
colors.gradient(' ║'));
|
|
212
|
+
console.log(colors.gradient('║') +
|
|
213
|
+
' '.repeat(16) +
|
|
214
|
+
colors.textMuted(`v${packageJson.version}`) +
|
|
215
|
+
' '.repeat(36) +
|
|
216
|
+
colors.gradient(' ║'));
|
|
127
217
|
console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
|
|
128
218
|
console.log(colors.gradient('╚════════════════════════════════════════════════════════════╝'));
|
|
129
219
|
console.log(colors.textMuted(' AI-powered command-line assistant'));
|
|
220
|
+
// Show initialization message if skills were initialized
|
|
221
|
+
if (initializedCount > 0) {
|
|
222
|
+
console.log(colors.textMuted(` ✨ Initialized ${initializedCount} built-in skills`));
|
|
223
|
+
}
|
|
130
224
|
console.log('');
|
|
131
225
|
await this.initialize();
|
|
132
226
|
this.showWelcomeMessage();
|
|
227
|
+
// Start watching for skill updates from CLI
|
|
228
|
+
this.startSkillUpdateWatcher();
|
|
229
|
+
// Set up ESC key handler using the terminal module
|
|
230
|
+
// This avoids conflicts with readline and provides clean ESC detection
|
|
231
|
+
let escCleanup;
|
|
232
|
+
if (process.stdin.isTTY) {
|
|
233
|
+
escCleanup = setupEscKeyHandler(() => {
|
|
234
|
+
if (this._isOperationInProgress) {
|
|
235
|
+
// An operation is running, let it be cancelled
|
|
236
|
+
this.cancellationManager.cancel();
|
|
237
|
+
}
|
|
238
|
+
// No operation running, ignore ESC
|
|
239
|
+
});
|
|
240
|
+
}
|
|
133
241
|
// Track if an operation is in progress
|
|
134
242
|
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
243
|
this.promptLoop();
|
|
145
244
|
// Keep the promise pending until shutdown
|
|
146
245
|
return new Promise((resolve) => {
|
|
@@ -159,7 +258,7 @@ export class InteractiveSession {
|
|
|
159
258
|
frameIndex = (frameIndex + 1) % frames.length;
|
|
160
259
|
}, 120);
|
|
161
260
|
logger.debug('[SESSION] 调用 configManager.load()...');
|
|
162
|
-
|
|
261
|
+
this.configManager.load();
|
|
163
262
|
logger.debug('[SESSION] Config loaded');
|
|
164
263
|
let authConfig = this.configManager.getAuthConfig();
|
|
165
264
|
let selectedAuthType = this.configManager.get('selectedAuthType');
|
|
@@ -186,9 +285,8 @@ export class InteractiveSession {
|
|
|
186
285
|
clearInterval(refreshInterval);
|
|
187
286
|
process.stdout.write('\r' + ' '.repeat(50) + '\r');
|
|
188
287
|
if (newToken) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
await this.configManager.save('global');
|
|
288
|
+
this.configManager.set('apiKey', newToken);
|
|
289
|
+
this.configManager.save('global');
|
|
192
290
|
authConfig.apiKey = newToken;
|
|
193
291
|
isValid = true;
|
|
194
292
|
}
|
|
@@ -198,20 +296,18 @@ export class InteractiveSession {
|
|
|
198
296
|
console.log(colors.warning('Your xAgent session has expired or is not configured'));
|
|
199
297
|
console.log(colors.info('Please select an authentication method to continue.'));
|
|
200
298
|
console.log('');
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
await this.configManager.save('global');
|
|
206
|
-
await this.configManager.load();
|
|
299
|
+
this.configManager.set('apiKey', '');
|
|
300
|
+
this.configManager.set('refreshToken', '');
|
|
301
|
+
this.configManager.save('global');
|
|
302
|
+
this.configManager.load();
|
|
207
303
|
authConfig = this.configManager.getAuthConfig();
|
|
208
304
|
await this.setupAuthentication();
|
|
209
305
|
authConfig = this.configManager.getAuthConfig();
|
|
210
|
-
// Recreate readline interface after
|
|
306
|
+
// Recreate readline interface after interactive prompt
|
|
211
307
|
this.rl.close();
|
|
212
308
|
this.rl = readline.createInterface({
|
|
213
309
|
input: process.stdin,
|
|
214
|
-
output: process.stdout
|
|
310
|
+
output: process.stdout,
|
|
215
311
|
});
|
|
216
312
|
this.rl.on('close', () => {
|
|
217
313
|
// readline closed
|
|
@@ -226,11 +322,11 @@ export class InteractiveSession {
|
|
|
226
322
|
authConfig = this.configManager.getAuthConfig();
|
|
227
323
|
selectedAuthType = this.configManager.get('selectedAuthType');
|
|
228
324
|
logger.debug('[SESSION] selectedAuthType (after setup):', String(selectedAuthType));
|
|
229
|
-
// Recreate readline interface after
|
|
325
|
+
// Recreate readline interface after interactive prompt
|
|
230
326
|
this.rl.close();
|
|
231
327
|
this.rl = readline.createInterface({
|
|
232
328
|
input: process.stdin,
|
|
233
|
-
output: process.stdout
|
|
329
|
+
output: process.stdout,
|
|
234
330
|
});
|
|
235
331
|
this.rl.on('close', () => {
|
|
236
332
|
// readline closed
|
|
@@ -241,21 +337,42 @@ export class InteractiveSession {
|
|
|
241
337
|
process.stdout.write('\r' + ' '.repeat(50) + '\r');
|
|
242
338
|
}
|
|
243
339
|
// 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));
|
|
340
|
+
// Initialize AI clients and set contextCompressor appropriately
|
|
249
341
|
if (selectedAuthType === AuthType.OAUTH_XAGENT) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
342
|
+
// Remote mode: fetch default models if not set
|
|
343
|
+
const currentLlm = this.configManager.get('remote_llmModelName');
|
|
344
|
+
const currentVlm = this.configManager.get('remote_vlmModelName');
|
|
345
|
+
if (!currentLlm || !currentVlm) {
|
|
346
|
+
const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
|
|
347
|
+
try {
|
|
348
|
+
const defaults = await fetchDefaultModels(authConfig.apiKey || '', webBaseUrl);
|
|
349
|
+
if (!currentLlm && defaults.llm?.name) {
|
|
350
|
+
this.configManager.set('remote_llmModelName', defaults.llm.name);
|
|
351
|
+
}
|
|
352
|
+
if (!currentVlm && defaults.vlm?.name) {
|
|
353
|
+
this.configManager.set('remote_vlmModelName', defaults.vlm.name);
|
|
354
|
+
}
|
|
355
|
+
this.configManager.save('global');
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
logger.debug('[SESSION] Failed to fetch default models:', error.message);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
// Remote mode: create RemoteAIClient and use it for context compression
|
|
362
|
+
this.remoteAIClient = createAIClient(authConfig);
|
|
363
|
+
this.contextCompressor.setAIClient(this.remoteAIClient);
|
|
253
364
|
logger.debug('[DEBUG Initialize] RemoteAIClient created successfully');
|
|
254
365
|
}
|
|
255
366
|
else {
|
|
367
|
+
// Local mode: create local AIClient
|
|
368
|
+
this.aiClient = createAIClient(authConfig);
|
|
369
|
+
this.contextCompressor.setAIClient(this.aiClient);
|
|
256
370
|
logger.debug('[DEBUG Initialize] RemoteAIClient NOT created (not OAuth XAGENT mode)');
|
|
257
371
|
}
|
|
258
|
-
|
|
372
|
+
// Sync remoteAIClient reference to slashCommandHandler for /provider command
|
|
373
|
+
this.slashCommandHandler.setRemoteAIClient(this.remoteAIClient);
|
|
374
|
+
this.executionMode =
|
|
375
|
+
this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
|
|
259
376
|
await this.agentManager.loadAgents();
|
|
260
377
|
await this.memoryManager.loadMemory();
|
|
261
378
|
await this.conversationManager.initialize();
|
|
@@ -292,7 +409,7 @@ export class InteractiveSession {
|
|
|
292
409
|
this.checkpointManager = getCheckpointManager(process.cwd(), checkpointingConfig.enabled, checkpointingConfig.maxCheckpoints);
|
|
293
410
|
await this.checkpointManager.initialize();
|
|
294
411
|
}
|
|
295
|
-
this.currentAgent = this.agentManager.getAgent('general-purpose');
|
|
412
|
+
this.currentAgent = this.agentManager.getAgent('general-purpose') ?? null;
|
|
296
413
|
console.log(colors.success('✔ Initialization complete'));
|
|
297
414
|
}
|
|
298
415
|
catch (error) {
|
|
@@ -315,11 +432,11 @@ export class InteractiveSession {
|
|
|
315
432
|
logger.debug('[SESSION] Sending validation request to:', url);
|
|
316
433
|
const response = await axios.get(url, {
|
|
317
434
|
headers: {
|
|
318
|
-
|
|
319
|
-
'Content-Type': 'application/json'
|
|
435
|
+
Authorization: `Bearer ${apiKey}`,
|
|
436
|
+
'Content-Type': 'application/json',
|
|
320
437
|
},
|
|
321
438
|
httpsAgent,
|
|
322
|
-
timeout: 10000
|
|
439
|
+
timeout: 10000,
|
|
323
440
|
});
|
|
324
441
|
logger.debug('[SESSION] Validation response status:', String(response.status));
|
|
325
442
|
return response.status === 200;
|
|
@@ -341,7 +458,7 @@ export class InteractiveSession {
|
|
|
341
458
|
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
342
459
|
const response = await axios.post(url, { refreshToken }, {
|
|
343
460
|
httpsAgent,
|
|
344
|
-
timeout: 10000
|
|
461
|
+
timeout: 10000,
|
|
345
462
|
});
|
|
346
463
|
if (response.status === 200) {
|
|
347
464
|
const data = response.data;
|
|
@@ -363,11 +480,14 @@ export class InteractiveSession {
|
|
|
363
480
|
console.log('');
|
|
364
481
|
const authType = await selectAuthType();
|
|
365
482
|
this.configManager.set('selectedAuthType', authType);
|
|
483
|
+
// Get xagentApiBaseUrl from config (respects XAGENT_BASE_URL env var)
|
|
484
|
+
const config = this.configManager.getAuthConfig();
|
|
366
485
|
const authService = new AuthService({
|
|
367
486
|
type: authType,
|
|
368
487
|
apiKey: '',
|
|
369
488
|
baseUrl: '',
|
|
370
|
-
modelName: ''
|
|
489
|
+
modelName: '',
|
|
490
|
+
xagentApiBaseUrl: config.xagentApiBaseUrl,
|
|
371
491
|
});
|
|
372
492
|
const success = await authService.authenticate();
|
|
373
493
|
if (!success) {
|
|
@@ -377,16 +497,29 @@ export class InteractiveSession {
|
|
|
377
497
|
process.exit(1);
|
|
378
498
|
}
|
|
379
499
|
const authConfig = authService.getAuthConfig();
|
|
500
|
+
// Clear modelName for remote mode
|
|
501
|
+
if (authType === AuthType.OAUTH_XAGENT) {
|
|
502
|
+
authConfig.modelName = '';
|
|
503
|
+
}
|
|
380
504
|
// VLM configuration is optional - only show for non-OAuth (local) mode
|
|
381
505
|
// Remote mode uses backend VLM configuration
|
|
382
506
|
if (authType !== AuthType.OAUTH_XAGENT) {
|
|
383
507
|
console.log('');
|
|
384
508
|
console.log(colors.info(`${icons.info} VLM configuration is optional.`));
|
|
385
|
-
console.log(colors.info(`You can configure it later using the /
|
|
509
|
+
console.log(colors.info(`You can configure it later using the /model command if needed.`));
|
|
386
510
|
console.log('');
|
|
387
511
|
}
|
|
388
|
-
|
|
389
|
-
|
|
512
|
+
this.configManager.setAuthConfig(authConfig);
|
|
513
|
+
// Set default remote model settings if not already set
|
|
514
|
+
if (authType === AuthType.OAUTH_XAGENT) {
|
|
515
|
+
if (!this.configManager.get('remote_llmModelName')) {
|
|
516
|
+
this.configManager.set('remote_llmModelName', '');
|
|
517
|
+
}
|
|
518
|
+
if (!this.configManager.get('remote_vlmModelName')) {
|
|
519
|
+
this.configManager.set('remote_vlmModelName', '');
|
|
520
|
+
}
|
|
521
|
+
this.configManager.save('global');
|
|
522
|
+
}
|
|
390
523
|
}
|
|
391
524
|
showWelcomeMessage() {
|
|
392
525
|
const language = this.configManager.getLanguage();
|
|
@@ -410,28 +543,28 @@ export class InteractiveSession {
|
|
|
410
543
|
[ExecutionMode.YOLO]: {
|
|
411
544
|
color: colors.error,
|
|
412
545
|
icon: icons.fire,
|
|
413
|
-
description: 'Execute commands without confirmation'
|
|
546
|
+
description: 'Execute commands without confirmation',
|
|
414
547
|
},
|
|
415
548
|
[ExecutionMode.ACCEPT_EDITS]: {
|
|
416
549
|
color: colors.warning,
|
|
417
550
|
icon: icons.check,
|
|
418
|
-
description: 'Accept all edits automatically'
|
|
551
|
+
description: 'Accept all edits automatically',
|
|
419
552
|
},
|
|
420
553
|
[ExecutionMode.PLAN]: {
|
|
421
554
|
color: colors.info,
|
|
422
555
|
icon: icons.brain,
|
|
423
|
-
description: 'Plan before executing'
|
|
556
|
+
description: 'Plan before executing',
|
|
424
557
|
},
|
|
425
558
|
[ExecutionMode.DEFAULT]: {
|
|
426
559
|
color: colors.success,
|
|
427
560
|
icon: icons.bolt,
|
|
428
|
-
description: 'Safe execution with confirmations'
|
|
561
|
+
description: 'Safe execution with confirmations',
|
|
429
562
|
},
|
|
430
563
|
[ExecutionMode.SMART]: {
|
|
431
564
|
color: colors.primaryBright,
|
|
432
565
|
icon: icons.sparkles,
|
|
433
|
-
description: 'Smart approval with intelligent security checks'
|
|
434
|
-
}
|
|
566
|
+
description: 'Smart approval with intelligent security checks',
|
|
567
|
+
},
|
|
435
568
|
};
|
|
436
569
|
const config = modeConfig[this.executionMode];
|
|
437
570
|
const modeName = this.executionMode;
|
|
@@ -439,6 +572,26 @@ export class InteractiveSession {
|
|
|
439
572
|
console.log(` ${config.color(config.icon)} ${styleHelpers.text.bold(config.color(modeName))}`);
|
|
440
573
|
console.log(` ${colors.textDim(` ${config.description}`)}`);
|
|
441
574
|
console.log('');
|
|
575
|
+
this.showRemoteModelInfo();
|
|
576
|
+
}
|
|
577
|
+
showRemoteModelInfo() {
|
|
578
|
+
const authConfig = this.configManager.getAuthConfig();
|
|
579
|
+
const isRemote = authConfig.type === AuthType.OAUTH_XAGENT;
|
|
580
|
+
if (isRemote) {
|
|
581
|
+
const llmModel = authConfig.remote_llmModelName || colors.textMuted('Not set');
|
|
582
|
+
const vlmModel = authConfig.remote_vlmModelName || colors.textMuted('Not set');
|
|
583
|
+
console.log(colors.textMuted(`${icons.brain} Remote Models:`));
|
|
584
|
+
console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('LLM:')} ${llmModel}`);
|
|
585
|
+
console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('VLM:')} ${vlmModel}`);
|
|
586
|
+
}
|
|
587
|
+
else {
|
|
588
|
+
const modelName = authConfig.modelName || colors.textMuted('Not set');
|
|
589
|
+
const guiSubagentModel = this.configManager.get('guiSubagentModel') || colors.textMuted('Not set');
|
|
590
|
+
console.log(colors.textMuted(`${icons.brain} Local Models:`));
|
|
591
|
+
console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('LLM:')} ${modelName}`);
|
|
592
|
+
console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('VLM:')} ${guiSubagentModel}`);
|
|
593
|
+
}
|
|
594
|
+
console.log('');
|
|
442
595
|
}
|
|
443
596
|
async promptLoop() {
|
|
444
597
|
// Check if we're shutting down
|
|
@@ -449,15 +602,12 @@ export class InteractiveSession {
|
|
|
449
602
|
if (this.rl) {
|
|
450
603
|
this.rl.close();
|
|
451
604
|
}
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
}
|
|
456
|
-
process.stdin.resume();
|
|
457
|
-
readline.emitKeypressEvents(process.stdin);
|
|
605
|
+
// Ensure TTY is in proper state for input handling
|
|
606
|
+
// This handles any state left by @clack/prompts or other interactions
|
|
607
|
+
ensureTtySane();
|
|
458
608
|
this.rl = readline.createInterface({
|
|
459
609
|
input: process.stdin,
|
|
460
|
-
output: process.stdout
|
|
610
|
+
output: process.stdout,
|
|
461
611
|
});
|
|
462
612
|
const prompt = `${colors.primaryBright('❯')} `;
|
|
463
613
|
this.rl.question(prompt, async (input) => {
|
|
@@ -481,7 +631,8 @@ export class InteractiveSession {
|
|
|
481
631
|
if (trimmedInput.startsWith('/')) {
|
|
482
632
|
const handled = await this.slashCommandHandler.handleCommand(trimmedInput);
|
|
483
633
|
if (handled) {
|
|
484
|
-
this.executionMode =
|
|
634
|
+
this.executionMode =
|
|
635
|
+
this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
|
|
485
636
|
// Sync conversation history to slashCommandHandler
|
|
486
637
|
this.slashCommandHandler.setConversationHistory(this.conversation);
|
|
487
638
|
}
|
|
@@ -513,9 +664,9 @@ export class InteractiveSession {
|
|
|
513
664
|
}
|
|
514
665
|
async processUserMessage(message, agent) {
|
|
515
666
|
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');
|
|
667
|
+
const textInput = inputs.find((i) => i.type === 'text');
|
|
668
|
+
const fileInputs = inputs.filter((i) => i.type === 'file');
|
|
669
|
+
const commandInput = inputs.find((i) => i.type === 'command');
|
|
519
670
|
if (commandInput) {
|
|
520
671
|
await this.executeShellCommand(commandInput.content);
|
|
521
672
|
return;
|
|
@@ -538,7 +689,7 @@ export class InteractiveSession {
|
|
|
538
689
|
type: 'text',
|
|
539
690
|
content: userContent,
|
|
540
691
|
rawInput: message,
|
|
541
|
-
timestamp: Date.now()
|
|
692
|
+
timestamp: Date.now(),
|
|
542
693
|
};
|
|
543
694
|
await this.sessionManager.addInput(sessionInput);
|
|
544
695
|
// Calculate thinking tokens based on config and user input
|
|
@@ -552,25 +703,19 @@ export class InteractiveSession {
|
|
|
552
703
|
const userMessage = {
|
|
553
704
|
role: 'user',
|
|
554
705
|
content: userContent,
|
|
555
|
-
timestamp: Date.now()
|
|
706
|
+
timestamp: Date.now(),
|
|
556
707
|
};
|
|
557
|
-
// Save last user message for recovery after compression
|
|
558
|
-
const lastUserMessage = userMessage;
|
|
559
708
|
this.conversation.push(userMessage);
|
|
560
709
|
await this.conversationManager.addMessage(userMessage);
|
|
561
|
-
// Check if context compression is needed
|
|
562
|
-
await this.checkAndCompressContext(lastUserMessage);
|
|
563
710
|
// Use remote AI client if available (OAuth XAGENT mode)
|
|
564
711
|
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));
|
|
712
|
+
logger.debug(`[DEBUG] processUserMessage: remoteAIClient exists=${!!this.remoteAIClient}, selectedAuthType=${currentSelectedAuthType}`);
|
|
568
713
|
if (this.remoteAIClient) {
|
|
569
|
-
logger.debug('[DEBUG
|
|
714
|
+
logger.debug('[DEBUG] Using generateRemoteResponse (remote mode)');
|
|
570
715
|
await this.generateRemoteResponse(thinkingTokens);
|
|
571
716
|
}
|
|
572
717
|
else {
|
|
573
|
-
logger.debug('[DEBUG
|
|
718
|
+
logger.debug('[DEBUG] Using generateResponse (local mode)');
|
|
574
719
|
await this.generateResponse(thinkingTokens);
|
|
575
720
|
}
|
|
576
721
|
}
|
|
@@ -615,52 +760,68 @@ export class InteractiveSession {
|
|
|
615
760
|
/**
|
|
616
761
|
* Check and compress conversation context
|
|
617
762
|
*/
|
|
618
|
-
async checkAndCompressContext(
|
|
763
|
+
async checkAndCompressContext() {
|
|
619
764
|
const compressionConfig = this.configManager.getContextCompressionConfig();
|
|
620
765
|
if (!compressionConfig.enabled) {
|
|
621
766
|
return;
|
|
622
767
|
}
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
768
|
+
const indent = this.getIndent();
|
|
769
|
+
const currentTokens = this.contextCompressor.estimateContextTokens(this.conversation);
|
|
770
|
+
const currentMessages = this.conversation.length;
|
|
771
|
+
const { needsCompression, reason, tokenCount } = this.contextCompressor.needsCompression(this.conversation, compressionConfig);
|
|
772
|
+
if (!needsCompression) {
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
// Extract threshold and contextWindow from reason
|
|
776
|
+
const thresholdMatch = reason.match(/budget\s*\((\d+)/);
|
|
777
|
+
const contextWindowMatch = reason.match(/contextWindow:\s*(\d+)/);
|
|
778
|
+
const threshold = thresholdMatch ? parseInt(thresholdMatch[1], 10) : 0;
|
|
779
|
+
const contextWindow = contextWindowMatch ? parseInt(contextWindowMatch[1], 10) : 0;
|
|
780
|
+
console.log('');
|
|
781
|
+
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)...`)}`);
|
|
782
|
+
const toolRegistry = getToolRegistry();
|
|
783
|
+
const baseSystemPrompt = this.currentAgent?.systemPrompt || 'You are a helpful AI assistant.';
|
|
784
|
+
const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode);
|
|
785
|
+
const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
|
|
786
|
+
const result = await this.contextCompressor.compressContext(this.conversation, enhancedSystemPrompt, compressionConfig);
|
|
787
|
+
if (result.wasCompressed) {
|
|
788
|
+
this.conversation = result.compressedMessages;
|
|
789
|
+
const reductionPercent = Math.round((1 - result.compressedSize / result.originalSize) * 100);
|
|
790
|
+
console.log(`${indent}${colors.success(`${icons.success} Compressed ${result.originalMessageCount} → ${result.compressedMessageCount} messages (${reductionPercent}% smaller)`)}`);
|
|
791
|
+
// Summary is embedded in first user message, look for it
|
|
792
|
+
// The format is: "[Conversation Summary - X messages compressed]\n\n${summary}"
|
|
793
|
+
let summaryMessage = result.compressedMessages.find((m) => m.role === 'user' && m.content.includes('[Conversation Summary'));
|
|
794
|
+
if (summaryMessage) {
|
|
795
|
+
// Extract summary content after the header
|
|
796
|
+
const match = summaryMessage.content.match(/\[Conversation Summary.*?\]:\n\n(.+)/s);
|
|
797
|
+
if (match) {
|
|
798
|
+
summaryMessage = {
|
|
799
|
+
role: 'assistant',
|
|
800
|
+
content: match[1],
|
|
801
|
+
timestamp: summaryMessage.timestamp,
|
|
802
|
+
};
|
|
656
803
|
}
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
804
|
+
}
|
|
805
|
+
if (summaryMessage && summaryMessage.content) {
|
|
806
|
+
const maxPreviewLength = 800;
|
|
807
|
+
let summaryContent = summaryMessage.content;
|
|
808
|
+
const isTruncated = summaryContent.length > maxPreviewLength;
|
|
809
|
+
if (isTruncated) {
|
|
810
|
+
summaryContent = summaryContent.substring(0, maxPreviewLength) + '\n...';
|
|
660
811
|
}
|
|
661
|
-
|
|
662
|
-
|
|
812
|
+
console.log('');
|
|
813
|
+
console.log(`${indent}${theme.predefinedStyles.title(`${icons.sparkles} Conversation Summary`)}`);
|
|
814
|
+
const separator = icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length * 2);
|
|
815
|
+
console.log(`${indent}${colors.border(separator)}`);
|
|
816
|
+
const renderedSummary = renderMarkdown(summaryContent, (process.stdout.columns || 80) - indent.length * 4);
|
|
817
|
+
console.log(`${indent}${theme.predefinedStyles.dim(renderedSummary).replace(/^/gm, indent)}`);
|
|
818
|
+
if (isTruncated) {
|
|
819
|
+
console.log(`${indent}${colors.textMuted(`(... ${summaryMessage.content.length - maxPreviewLength} more chars hidden)`)}`);
|
|
820
|
+
}
|
|
821
|
+
console.log(`${indent}${colors.border(separator)}`);
|
|
663
822
|
}
|
|
823
|
+
// Sync compressed conversation history to slashCommandHandler
|
|
824
|
+
this.slashCommandHandler.setConversationHistory(this.conversation);
|
|
664
825
|
}
|
|
665
826
|
}
|
|
666
827
|
async executeShellCommand(command) {
|
|
@@ -683,15 +844,15 @@ export class InteractiveSession {
|
|
|
683
844
|
tool: 'Bash',
|
|
684
845
|
params: { command },
|
|
685
846
|
result,
|
|
686
|
-
timestamp: Date.now()
|
|
847
|
+
timestamp: Date.now(),
|
|
687
848
|
};
|
|
688
|
-
this.
|
|
849
|
+
this.tool_calls.push(toolCall);
|
|
689
850
|
// Record command execution to session manager
|
|
690
851
|
await this.sessionManager.addInput({
|
|
691
852
|
type: 'command',
|
|
692
853
|
content: command,
|
|
693
854
|
rawInput: command,
|
|
694
|
-
timestamp: Date.now()
|
|
855
|
+
timestamp: Date.now(),
|
|
695
856
|
});
|
|
696
857
|
await this.sessionManager.addOutput({
|
|
697
858
|
role: 'tool',
|
|
@@ -699,7 +860,7 @@ export class InteractiveSession {
|
|
|
699
860
|
toolName: 'Bash',
|
|
700
861
|
toolParams: { command },
|
|
701
862
|
toolResult: result,
|
|
702
|
-
timestamp: Date.now()
|
|
863
|
+
timestamp: Date.now(),
|
|
703
864
|
});
|
|
704
865
|
}
|
|
705
866
|
catch (error) {
|
|
@@ -727,8 +888,19 @@ export class InteractiveSession {
|
|
|
727
888
|
createRemoteCaller(taskId, status) {
|
|
728
889
|
const client = this.remoteAIClient;
|
|
729
890
|
return {
|
|
730
|
-
chatCompletion: (messages, options) =>
|
|
731
|
-
|
|
891
|
+
chatCompletion: (messages, options) => {
|
|
892
|
+
// Must fetch authConfig inside the closure, otherwise it captures stale config
|
|
893
|
+
const authConfig = this.configManager.getAuthConfig();
|
|
894
|
+
logger.debug(`[DEBUG] createRemoteCaller: llmModelName=${authConfig.remote_llmModelName}, vlmModelName=${authConfig.remote_vlmModelName}`);
|
|
895
|
+
return client.chatCompletion(messages, {
|
|
896
|
+
...options,
|
|
897
|
+
taskId,
|
|
898
|
+
status: options.isFirstApiCall ? 'begin' : 'continue',
|
|
899
|
+
llmModelName: authConfig.remote_llmModelName,
|
|
900
|
+
vlmModelName: authConfig.remote_vlmModelName
|
|
901
|
+
});
|
|
902
|
+
},
|
|
903
|
+
isRemote: true,
|
|
732
904
|
};
|
|
733
905
|
}
|
|
734
906
|
/**
|
|
@@ -738,18 +910,21 @@ export class InteractiveSession {
|
|
|
738
910
|
const client = this.aiClient;
|
|
739
911
|
return {
|
|
740
912
|
chatCompletion: (messages, options) => client.chatCompletion(messages, options),
|
|
741
|
-
isRemote: false
|
|
913
|
+
isRemote: false,
|
|
742
914
|
};
|
|
743
915
|
}
|
|
744
|
-
async generateResponse(thinkingTokens = 0) {
|
|
745
|
-
//
|
|
746
|
-
|
|
916
|
+
async generateResponse(thinkingTokens = 0, _customAIClient, existingTaskId) {
|
|
917
|
+
// Use existing taskId or create new one for this user interaction
|
|
918
|
+
// If taskId already exists (e.g., from tool calls), reuse it
|
|
919
|
+
const taskId = existingTaskId || this.currentTaskId || crypto.randomUUID();
|
|
747
920
|
this.currentTaskId = taskId;
|
|
748
|
-
|
|
749
|
-
//
|
|
750
|
-
const status = this.isFirstApiCall ? 'begin' : 'continue';
|
|
921
|
+
// isFirstApiCall is reset in generateRemoteResponse for new tasks
|
|
922
|
+
// For continuation calls (existingTaskId provided), keep previous value
|
|
751
923
|
// Use unified LLM Caller with taskId (automatically selects local or remote mode)
|
|
752
|
-
const
|
|
924
|
+
const status = this.isFirstApiCall ? 'begin' : 'continue';
|
|
925
|
+
const caller = this.createLLMCaller(taskId, status);
|
|
926
|
+
const chatCompletion = caller.chatCompletion;
|
|
927
|
+
const isRemote = caller.isRemote;
|
|
753
928
|
if (!isRemote && !this.aiClient) {
|
|
754
929
|
console.log(colors.error('AI client not initialized'));
|
|
755
930
|
return;
|
|
@@ -777,29 +952,29 @@ export class InteractiveSession {
|
|
|
777
952
|
const toolDefinitions = toolRegistry.getToolDefinitions();
|
|
778
953
|
// Available tools for this session
|
|
779
954
|
const availableTools = this.executionMode !== ExecutionMode.DEFAULT && allowedToolNames.length > 0
|
|
780
|
-
? toolDefinitions.filter((tool) =>
|
|
955
|
+
? toolDefinitions.filter((tool) => typeof tool.function?.name === 'string' &&
|
|
956
|
+
allowedToolNames.includes(tool.function.name))
|
|
781
957
|
: toolDefinitions;
|
|
782
|
-
const baseSystemPrompt = this.currentAgent?.systemPrompt;
|
|
958
|
+
const baseSystemPrompt = this.currentAgent?.systemPrompt ?? '';
|
|
783
959
|
const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode, undefined, this.mcpManager);
|
|
784
960
|
const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
|
|
785
961
|
const messages = [
|
|
786
962
|
{ role: 'system', content: `${enhancedSystemPrompt}\n\n${memory}`, timestamp: Date.now() },
|
|
787
|
-
...this.conversation
|
|
963
|
+
...this.conversation,
|
|
788
964
|
];
|
|
789
965
|
const operationId = `ai-response-${Date.now()}`;
|
|
790
966
|
const response = await this.cancellationManager.withCancellation(chatCompletion(messages, {
|
|
791
967
|
tools: availableTools,
|
|
792
968
|
toolChoice: availableTools.length > 0 ? 'auto' : 'none',
|
|
793
|
-
thinkingTokens
|
|
969
|
+
thinkingTokens,
|
|
970
|
+
isFirstApiCall: this.isFirstApiCall,
|
|
794
971
|
}), operationId);
|
|
795
972
|
// Mark that first API call is complete
|
|
796
973
|
this.isFirstApiCall = false;
|
|
797
974
|
clearInterval(spinnerInterval);
|
|
798
975
|
process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r'); // Clear spinner line
|
|
799
976
|
const assistantMessage = response.choices[0].message;
|
|
800
|
-
const content = typeof assistantMessage.content === 'string'
|
|
801
|
-
? assistantMessage.content
|
|
802
|
-
: '';
|
|
977
|
+
const content = typeof assistantMessage.content === 'string' ? assistantMessage.content : '';
|
|
803
978
|
const reasoningContent = assistantMessage.reasoning_content || '';
|
|
804
979
|
// Display reasoning content if available and thinking mode is enabled
|
|
805
980
|
if (reasoningContent && this.configManager.getThinkingConfig().enabled) {
|
|
@@ -816,22 +991,25 @@ export class InteractiveSession {
|
|
|
816
991
|
role: 'assistant',
|
|
817
992
|
content,
|
|
818
993
|
timestamp: Date.now(),
|
|
819
|
-
reasoningContent,
|
|
820
|
-
|
|
994
|
+
reasoning_content: reasoningContent,
|
|
995
|
+
tool_calls: assistantMessage.tool_calls,
|
|
821
996
|
});
|
|
822
997
|
// Record output to session manager
|
|
823
998
|
await this.sessionManager.addOutput({
|
|
824
999
|
role: 'assistant',
|
|
825
1000
|
content,
|
|
826
1001
|
timestamp: Date.now(),
|
|
827
|
-
reasoningContent,
|
|
828
|
-
|
|
1002
|
+
reasoning_content: reasoningContent,
|
|
1003
|
+
tool_calls: assistantMessage.tool_calls,
|
|
829
1004
|
});
|
|
830
1005
|
if (assistantMessage.tool_calls) {
|
|
831
1006
|
await this.handleToolCalls(assistantMessage.tool_calls);
|
|
832
1007
|
}
|
|
1008
|
+
else {
|
|
1009
|
+
await this.checkAndCompressContext();
|
|
1010
|
+
}
|
|
833
1011
|
if (this.checkpointManager.isEnabled()) {
|
|
834
|
-
await this.checkpointManager.createCheckpoint(`Response generated at ${new Date().toLocaleString()}`, [...this.conversation], [...this.
|
|
1012
|
+
await this.checkpointManager.createCheckpoint(`Response generated at ${new Date().toLocaleString()}`, [...this.conversation], [...this.tool_calls]);
|
|
835
1013
|
}
|
|
836
1014
|
// Operation completed successfully, clear the flag
|
|
837
1015
|
this._isOperationInProgress = false;
|
|
@@ -842,16 +1020,18 @@ export class InteractiveSession {
|
|
|
842
1020
|
// Clear the operation flag
|
|
843
1021
|
this._isOperationInProgress = false;
|
|
844
1022
|
if (error.message === 'Operation cancelled by user') {
|
|
845
|
-
//
|
|
1023
|
+
// Notify backend to cancel the task
|
|
846
1024
|
if (this.remoteAIClient && this.currentTaskId) {
|
|
847
|
-
await this.remoteAIClient.cancelTask(this.currentTaskId);
|
|
1025
|
+
await this.remoteAIClient.cancelTask?.(this.currentTaskId).catch(() => { });
|
|
848
1026
|
}
|
|
849
1027
|
return;
|
|
850
1028
|
}
|
|
851
|
-
//
|
|
852
|
-
|
|
1029
|
+
// Distinguish error types: timeout vs other failures
|
|
1030
|
+
const isTimeout = error.message.includes('timeout') || error.message.includes('Timeout');
|
|
1031
|
+
const failureReason = isTimeout ? 'timeout' : 'failure';
|
|
1032
|
+
logger.debug(`[Session] Task failed: taskId=${this.currentTaskId}, error: ${error.message}, reason: ${failureReason}`);
|
|
853
1033
|
if (this.remoteAIClient && this.currentTaskId) {
|
|
854
|
-
await this.remoteAIClient.
|
|
1034
|
+
await this.remoteAIClient.failTask?.(this.currentTaskId, failureReason).catch(() => { });
|
|
855
1035
|
}
|
|
856
1036
|
console.log(colors.error(`Error: ${error.message}`));
|
|
857
1037
|
}
|
|
@@ -868,134 +1048,34 @@ export class InteractiveSession {
|
|
|
868
1048
|
const taskId = existingTaskId || crypto.randomUUID();
|
|
869
1049
|
this.currentTaskId = taskId;
|
|
870
1050
|
logger.debug(`[Session] generateRemoteResponse: taskId=${taskId}, existingTaskId=${!!existingTaskId}`);
|
|
871
|
-
//
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
// Determine status based on whether this is the first API call
|
|
876
|
-
const status = this.isFirstApiCall ? 'begin' : 'continue';
|
|
1051
|
+
// Each new user message is a fresh task - always set isFirstApiCall = true
|
|
1052
|
+
// This ensures status is 'begin' for every user message
|
|
1053
|
+
this.isFirstApiCall = true;
|
|
1054
|
+
const status = 'begin';
|
|
877
1055
|
logger.debug(`[Session] Status for this call: ${status}, isFirstApiCall=${this.isFirstApiCall}`);
|
|
878
|
-
//
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
return this.generateResponse(thinkingTokens);
|
|
1056
|
+
// Check if remote client is available
|
|
1057
|
+
if (!this.remoteAIClient) {
|
|
1058
|
+
console.log(colors.error('Remote AI client not initialized'));
|
|
1059
|
+
return;
|
|
883
1060
|
}
|
|
884
|
-
const indent = this.getIndent();
|
|
885
|
-
const thinkingText = colors.textMuted(`Thinking... (Press ESC to cancel)`);
|
|
886
|
-
const icon = colors.primary(icons.brain);
|
|
887
|
-
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
888
|
-
let frameIndex = 0;
|
|
889
|
-
// Mark that an operation is in progress
|
|
890
|
-
this._isOperationInProgress = true;
|
|
891
|
-
// Custom spinner: only icon rotates, text stays static
|
|
892
|
-
const spinnerInterval = setInterval(() => {
|
|
893
|
-
process.stdout.write(`\r${colors.primary(frames[frameIndex])} ${icon} ${thinkingText}`);
|
|
894
|
-
frameIndex = (frameIndex + 1) % frames.length;
|
|
895
|
-
}, 120);
|
|
896
1061
|
try {
|
|
897
|
-
//
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
const toolRegistry = getToolRegistry();
|
|
901
|
-
const allowedToolNames = this.currentAgent
|
|
902
|
-
? this.agentManager.getAvailableToolsForAgent(this.currentAgent, this.executionMode)
|
|
903
|
-
: [];
|
|
904
|
-
const allToolDefinitions = toolRegistry.getToolDefinitions();
|
|
905
|
-
const availableTools = this.executionMode !== ExecutionMode.DEFAULT && allowedToolNames.length > 0
|
|
906
|
-
? allToolDefinitions.filter((tool) => allowedToolNames.includes(tool.function.name))
|
|
907
|
-
: allToolDefinitions;
|
|
908
|
-
// Convert to the format expected by backend (与本地模式一致使用 availableTools)
|
|
909
|
-
const tools = availableTools.map((tool) => ({
|
|
910
|
-
type: 'function',
|
|
911
|
-
function: {
|
|
912
|
-
name: tool.function.name,
|
|
913
|
-
description: tool.function.description || '',
|
|
914
|
-
parameters: tool.function.parameters || {
|
|
915
|
-
type: 'object',
|
|
916
|
-
properties: {}
|
|
917
|
-
}
|
|
918
|
-
}
|
|
919
|
-
}));
|
|
920
|
-
// Generate system prompt (与本地模式一致)
|
|
921
|
-
const baseSystemPrompt = this.currentAgent?.systemPrompt || 'You are a helpful AI assistant.';
|
|
922
|
-
const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode);
|
|
923
|
-
const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
|
|
924
|
-
// Build messages with system prompt (与本地模式一致)
|
|
925
|
-
const messages = [
|
|
926
|
-
{ role: 'system', content: `${enhancedSystemPrompt}\n\n${memory}`, timestamp: Date.now() },
|
|
927
|
-
...this.conversation
|
|
928
|
-
];
|
|
929
|
-
// Call unified LLM API with cancellation support
|
|
930
|
-
const operationId = `remote-ai-response-${Date.now()}`;
|
|
931
|
-
const response = await this.cancellationManager.withCancellation(chatCompletion(messages, {
|
|
932
|
-
tools,
|
|
933
|
-
toolChoice: tools.length > 0 ? 'auto' : 'none',
|
|
934
|
-
thinkingTokens
|
|
935
|
-
}), operationId);
|
|
936
|
-
// Mark that first API call is complete
|
|
937
|
-
this.isFirstApiCall = false;
|
|
938
|
-
clearInterval(spinnerInterval);
|
|
939
|
-
process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r');
|
|
940
|
-
console.log('');
|
|
941
|
-
// 使用统一的响应格式(与本地模式一致)
|
|
942
|
-
const assistantMessage = response.choices[0].message;
|
|
943
|
-
const content = typeof assistantMessage.content === 'string'
|
|
944
|
-
? assistantMessage.content
|
|
945
|
-
: '';
|
|
946
|
-
const reasoningContent = assistantMessage.reasoning_content || '';
|
|
947
|
-
const toolCalls = assistantMessage.tool_calls || [];
|
|
948
|
-
// Display reasoning content if available and thinking mode is enabled (与本地模式一致)
|
|
949
|
-
if (reasoningContent && this.configManager.getThinkingConfig().enabled) {
|
|
950
|
-
this.displayThinkingContent(reasoningContent);
|
|
951
|
-
}
|
|
952
|
-
console.log(`${indent}${colors.primaryBright(`${icons.robot} Assistant:`)}`);
|
|
953
|
-
console.log(`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`);
|
|
954
|
-
console.log('');
|
|
955
|
-
const renderedContent = renderMarkdown(content, (process.stdout.columns || 80) - indent.length * 2);
|
|
956
|
-
console.log(`${indent}${renderedContent.replace(/^/gm, indent)}`);
|
|
957
|
-
console.log('');
|
|
958
|
-
// Add assistant message to conversation (consistent with local mode, including reasoningContent)
|
|
959
|
-
this.conversation.push({
|
|
960
|
-
role: 'assistant',
|
|
961
|
-
content,
|
|
962
|
-
timestamp: Date.now(),
|
|
963
|
-
reasoningContent,
|
|
964
|
-
toolCalls: toolCalls
|
|
965
|
-
});
|
|
966
|
-
// Record output to session manager (consistent with local mode, including reasoningContent and toolCalls)
|
|
967
|
-
await this.sessionManager.addOutput({
|
|
968
|
-
role: 'assistant',
|
|
969
|
-
content,
|
|
970
|
-
timestamp: Date.now(),
|
|
971
|
-
reasoningContent,
|
|
972
|
-
toolCalls
|
|
973
|
-
});
|
|
974
|
-
// Handle tool calls
|
|
975
|
-
if (toolCalls.length > 0) {
|
|
976
|
-
await this.handleToolCalls(toolCalls, async () => {
|
|
977
|
-
// Remote mode continuation: reuse existing taskId
|
|
978
|
-
await this.generateRemoteResponse(0, this.currentTaskId || undefined);
|
|
979
|
-
});
|
|
980
|
-
}
|
|
981
|
-
// Checkpoint support (consistent with local mode)
|
|
982
|
-
if (this.checkpointManager.isEnabled()) {
|
|
983
|
-
await this.checkpointManager.createCheckpoint(`Response generated at ${new Date().toLocaleString()}`, [...this.conversation], [...this.toolCalls]);
|
|
984
|
-
}
|
|
985
|
-
// Operation completed successfully
|
|
986
|
-
this._isOperationInProgress = false;
|
|
1062
|
+
// Use unified generateResponse without passing customAIClient,
|
|
1063
|
+
// let createLLMCaller handle remote/local selection
|
|
1064
|
+
await this.generateResponse(thinkingTokens, undefined, taskId);
|
|
987
1065
|
// Mark task as completed (发送 status: 'end')
|
|
988
1066
|
logger.debug(`[Session] Task completed: taskId=${this.currentTaskId}`);
|
|
989
1067
|
if (this.remoteAIClient && this.currentTaskId) {
|
|
990
|
-
await this.remoteAIClient.completeTask(this.currentTaskId);
|
|
1068
|
+
await this.remoteAIClient.completeTask?.(this.currentTaskId);
|
|
991
1069
|
}
|
|
992
1070
|
}
|
|
993
1071
|
catch (error) {
|
|
994
|
-
clearInterval(spinnerInterval);
|
|
995
|
-
process.stdout.write('\r' + ' '.repeat(process.stdout.columns || 80) + '\r');
|
|
996
1072
|
// Clear the operation flag
|
|
997
1073
|
this._isOperationInProgress = false;
|
|
998
1074
|
if (error.message === 'Operation cancelled by user') {
|
|
1075
|
+
// Notify backend to cancel the task
|
|
1076
|
+
if (this.remoteAIClient && this.currentTaskId) {
|
|
1077
|
+
await this.remoteAIClient.cancelTask?.(this.currentTaskId).catch(() => { });
|
|
1078
|
+
}
|
|
999
1079
|
return;
|
|
1000
1080
|
}
|
|
1001
1081
|
// Handle token invalid error - trigger re-authentication
|
|
@@ -1005,49 +1085,48 @@ export class InteractiveSession {
|
|
|
1005
1085
|
console.log(colors.info('Your browser session has been logged out. Please log in again.'));
|
|
1006
1086
|
console.log('');
|
|
1007
1087
|
// Clear invalid credentials and persist
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
await this.configManager.save('global');
|
|
1088
|
+
this.configManager.set('apiKey', '');
|
|
1089
|
+
this.configManager.set('refreshToken', '');
|
|
1090
|
+
this.configManager.save('global');
|
|
1012
1091
|
logger.debug('[DEBUG generateRemoteResponse] Cleared invalid credentials, starting re-authentication...');
|
|
1013
1092
|
// Re-authenticate
|
|
1014
1093
|
await this.setupAuthentication();
|
|
1015
1094
|
// Reload config to ensure we have the latest authConfig
|
|
1016
|
-
|
|
1017
|
-
await this.configManager.load();
|
|
1095
|
+
this.configManager.load();
|
|
1018
1096
|
const authConfig = this.configManager.getAuthConfig();
|
|
1019
1097
|
logger.debug('[DEBUG generateRemoteResponse] After re-auth:');
|
|
1020
1098
|
logger.debug(' - authConfig.apiKey exists:', !!authConfig.apiKey ? 'true' : 'false');
|
|
1021
|
-
|
|
1022
|
-
// Recreate readline interface after inquirer
|
|
1099
|
+
// Recreate readline interface after interactive prompt
|
|
1023
1100
|
this.rl.close();
|
|
1024
1101
|
this.rl = readline.createInterface({
|
|
1025
1102
|
input: process.stdin,
|
|
1026
|
-
output: process.stdout
|
|
1103
|
+
output: process.stdout,
|
|
1027
1104
|
});
|
|
1028
1105
|
this.rl.on('close', () => {
|
|
1029
1106
|
logger.debug('DEBUG: readline interface closed');
|
|
1030
1107
|
});
|
|
1031
1108
|
// Reinitialize RemoteAIClient with new token
|
|
1032
1109
|
if (authConfig.apiKey) {
|
|
1033
|
-
const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
|
|
1034
1110
|
logger.debug('[DEBUG generateRemoteResponse] Reinitializing RemoteAIClient with new token');
|
|
1035
|
-
|
|
1036
|
-
this.remoteAIClient = new RemoteAIClient(authConfig.apiKey, newWebBaseUrl, authConfig.showAIDebugInfo);
|
|
1111
|
+
this.remoteAIClient = createAIClient(authConfig);
|
|
1037
1112
|
}
|
|
1038
1113
|
else {
|
|
1039
1114
|
logger.debug('[DEBUG generateRemoteResponse] WARNING: No apiKey after re-authentication!');
|
|
1040
1115
|
}
|
|
1116
|
+
// Sync remoteAIClient reference to slashCommandHandler for /provider command
|
|
1117
|
+
this.slashCommandHandler.setRemoteAIClient(this.remoteAIClient);
|
|
1041
1118
|
// Retry the current operation
|
|
1042
1119
|
console.log('');
|
|
1043
1120
|
console.log(colors.info('Retrying with new authentication...'));
|
|
1044
1121
|
console.log('');
|
|
1045
1122
|
return this.generateRemoteResponse(thinkingTokens);
|
|
1046
1123
|
}
|
|
1047
|
-
//
|
|
1048
|
-
|
|
1124
|
+
// Distinguish error types: timeout vs other failures
|
|
1125
|
+
const isTimeout = error.message.includes('timeout') || error.message.includes('Timeout');
|
|
1126
|
+
const failureReason = isTimeout ? 'timeout' : 'failure';
|
|
1127
|
+
logger.debug(`[Session] Task failed: taskId=${this.currentTaskId}, error: ${error.message}, reason: ${failureReason}`);
|
|
1049
1128
|
if (this.remoteAIClient && this.currentTaskId) {
|
|
1050
|
-
await this.remoteAIClient.
|
|
1129
|
+
await this.remoteAIClient.failTask?.(this.currentTaskId, failureReason).catch(() => { });
|
|
1051
1130
|
}
|
|
1052
1131
|
console.log(colors.error(`Error: ${error.message}`));
|
|
1053
1132
|
return;
|
|
@@ -1080,33 +1159,54 @@ export class InteractiveSession {
|
|
|
1080
1159
|
}
|
|
1081
1160
|
else {
|
|
1082
1161
|
const toolDescription = this.getToolDescription(name, params);
|
|
1083
|
-
console.log('');
|
|
1084
1162
|
console.log(`${indent}${colors.textMuted(`${icons.loading} ${toolDescription}`)}`);
|
|
1085
1163
|
}
|
|
1086
1164
|
}
|
|
1087
1165
|
// Execute all tools in parallel
|
|
1088
|
-
const results = await toolRegistry.executeAll(preparedToolCalls.map(tc => ({ name: tc.name, params: tc.params })), this.executionMode);
|
|
1089
|
-
//
|
|
1166
|
+
const results = await toolRegistry.executeAll(preparedToolCalls.map((tc) => ({ name: tc.name, params: tc.params })), this.executionMode);
|
|
1167
|
+
// Create a map to store results by tool call index to maintain original order
|
|
1168
|
+
const resultsByIndex = new Map();
|
|
1169
|
+
const usedIndices = new Set();
|
|
1170
|
+
for (const result of results) {
|
|
1171
|
+
// Find the first unused original index in preparedToolCalls that matches the tool name
|
|
1172
|
+
const originalIndex = preparedToolCalls.findIndex((tc, idx) => tc.name === result.tool && !usedIndices.has(idx));
|
|
1173
|
+
if (originalIndex !== -1) {
|
|
1174
|
+
usedIndices.add(originalIndex);
|
|
1175
|
+
resultsByIndex.set(originalIndex, result);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
// Process results in the original tool_calls order (critical for Anthropic format APIs)
|
|
1090
1179
|
let hasError = false;
|
|
1091
|
-
for (
|
|
1092
|
-
const toolCall = preparedToolCalls
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
const
|
|
1180
|
+
for (let i = 0; i < preparedToolCalls.length; i++) {
|
|
1181
|
+
const toolCall = preparedToolCalls[i];
|
|
1182
|
+
const { name: tool, params } = toolCall;
|
|
1183
|
+
const resultData = resultsByIndex.get(i);
|
|
1184
|
+
const result = resultData?.result;
|
|
1185
|
+
const error = resultData?.error;
|
|
1096
1186
|
if (error) {
|
|
1097
|
-
// Clear the operation flag
|
|
1098
|
-
this._isOperationInProgress = false;
|
|
1099
1187
|
if (error === 'Operation cancelled by user') {
|
|
1188
|
+
// Notify backend to cancel the task
|
|
1189
|
+
if (this.remoteAIClient && this.currentTaskId) {
|
|
1190
|
+
await this.remoteAIClient.cancelTask?.(this.currentTaskId).catch(() => { });
|
|
1191
|
+
}
|
|
1192
|
+
// 清理 conversation 中未完成的 tool_call
|
|
1193
|
+
this.cleanupIncompleteToolCalls();
|
|
1194
|
+
this._isOperationInProgress = false;
|
|
1100
1195
|
return;
|
|
1101
1196
|
}
|
|
1102
1197
|
hasError = true;
|
|
1103
1198
|
console.log('');
|
|
1104
|
-
console.log(`${indent}${colors.error(`${icons.cross} Tool Error: ${error}`)}`);
|
|
1199
|
+
console.log(`${indent}${colors.error(`${icons.cross} Tool Error: ${tool} - ${error}`)}`);
|
|
1200
|
+
// Add detailed error info including tool name and params for AI understanding and correction
|
|
1105
1201
|
this.conversation.push({
|
|
1106
1202
|
role: 'tool',
|
|
1107
|
-
content: JSON.stringify({
|
|
1203
|
+
content: JSON.stringify({
|
|
1204
|
+
name: tool,
|
|
1205
|
+
parameters: params,
|
|
1206
|
+
error: error,
|
|
1207
|
+
}),
|
|
1108
1208
|
tool_call_id: toolCall.id,
|
|
1109
|
-
timestamp: Date.now()
|
|
1209
|
+
timestamp: Date.now(),
|
|
1110
1210
|
});
|
|
1111
1211
|
}
|
|
1112
1212
|
else {
|
|
@@ -1116,7 +1216,7 @@ export class InteractiveSession {
|
|
|
1116
1216
|
// Always show details for todo tools so users can see their task lists
|
|
1117
1217
|
const isTodoTool = tool === 'todo_write' || tool === 'todo_read';
|
|
1118
1218
|
// Special handling for edit tool with diff
|
|
1119
|
-
const isEditTool = tool === '
|
|
1219
|
+
const isEditTool = tool === 'Edit';
|
|
1120
1220
|
const hasDiff = isEditTool && result?.diff;
|
|
1121
1221
|
// Special handling for Write tool with file preview
|
|
1122
1222
|
const isWriteTool = tool === 'Write';
|
|
@@ -1144,7 +1244,10 @@ export class InteractiveSession {
|
|
|
1144
1244
|
// Show edit result with diff
|
|
1145
1245
|
console.log('');
|
|
1146
1246
|
const diffOutput = renderDiff(result.diff);
|
|
1147
|
-
const indentedDiff = diffOutput
|
|
1247
|
+
const indentedDiff = diffOutput
|
|
1248
|
+
.split('\n')
|
|
1249
|
+
.map((line) => `${displayIndent} ${line}`)
|
|
1250
|
+
.join('\n');
|
|
1148
1251
|
console.log(`${indentedDiff}`);
|
|
1149
1252
|
}
|
|
1150
1253
|
else if (hasFilePreview) {
|
|
@@ -1164,7 +1267,8 @@ export class InteractiveSession {
|
|
|
1164
1267
|
// Special handling for task tool (subagent) - show friendly summary
|
|
1165
1268
|
console.log('');
|
|
1166
1269
|
const subagentType = params.subagent_type;
|
|
1167
|
-
const subagentName = params.description ||
|
|
1270
|
+
const subagentName = params.description ||
|
|
1271
|
+
(params.prompt ? params.prompt.substring(0, 50).replace(/\n/g, ' ') : 'Unknown task');
|
|
1168
1272
|
if (result?.success) {
|
|
1169
1273
|
console.log(`${displayIndent}${colors.success(`${icons.check} ${subagentType}: Completed`)}`);
|
|
1170
1274
|
console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
|
|
@@ -1247,6 +1351,27 @@ export class InteractiveSession {
|
|
|
1247
1351
|
}
|
|
1248
1352
|
}
|
|
1249
1353
|
}
|
|
1354
|
+
else if (tool === 'InvokeSkill') {
|
|
1355
|
+
// Special handling for InvokeSkill - show friendly summary
|
|
1356
|
+
console.log('');
|
|
1357
|
+
const skillName = params?.skillId || 'Unknown skill';
|
|
1358
|
+
const taskDesc = params?.taskDescription || '';
|
|
1359
|
+
if (result?.success) {
|
|
1360
|
+
console.log(`${displayIndent}${colors.success(`${icons.check} Skill: Completed`)}`);
|
|
1361
|
+
console.log(`${displayIndent}${colors.textDim(` Skill: ${skillName}`)}`);
|
|
1362
|
+
if (taskDesc) {
|
|
1363
|
+
const truncatedTask = taskDesc.length > 60 ? taskDesc.substring(0, 60) + '...' : taskDesc;
|
|
1364
|
+
console.log(`${displayIndent}${colors.textDim(` Task: ${truncatedTask}`)}`);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
else {
|
|
1368
|
+
console.log(`${displayIndent}${colors.error(`${icons.cross} Skill: Failed`)}`);
|
|
1369
|
+
console.log(`${displayIndent}${colors.textDim(` Skill: ${skillName}`)}`);
|
|
1370
|
+
if (result?.message) {
|
|
1371
|
+
console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1250
1375
|
else if (showToolDetails) {
|
|
1251
1376
|
console.log('');
|
|
1252
1377
|
console.log(`${displayIndent}${colors.success(`${icons.check} Tool Result:`)}`);
|
|
@@ -1261,7 +1386,10 @@ export class InteractiveSession {
|
|
|
1261
1386
|
const resultPreview = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
1262
1387
|
const truncatedPreview = resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
|
|
1263
1388
|
// Indent the preview
|
|
1264
|
-
const indentedPreview = truncatedPreview
|
|
1389
|
+
const indentedPreview = truncatedPreview
|
|
1390
|
+
.split('\n')
|
|
1391
|
+
.map((line) => `${displayIndent} ${line}`)
|
|
1392
|
+
.join('\n');
|
|
1265
1393
|
console.log(`${indentedPreview}`);
|
|
1266
1394
|
}
|
|
1267
1395
|
else {
|
|
@@ -1271,9 +1399,9 @@ export class InteractiveSession {
|
|
|
1271
1399
|
tool,
|
|
1272
1400
|
params,
|
|
1273
1401
|
result,
|
|
1274
|
-
timestamp: Date.now()
|
|
1402
|
+
timestamp: Date.now(),
|
|
1275
1403
|
};
|
|
1276
|
-
this.
|
|
1404
|
+
this.tool_calls.push(toolCallRecord);
|
|
1277
1405
|
// Record tool output to session manager
|
|
1278
1406
|
await this.sessionManager.addOutput({
|
|
1279
1407
|
role: 'tool',
|
|
@@ -1281,78 +1409,104 @@ export class InteractiveSession {
|
|
|
1281
1409
|
toolName: tool,
|
|
1282
1410
|
toolParams: params,
|
|
1283
1411
|
toolResult: result,
|
|
1284
|
-
timestamp: Date.now()
|
|
1412
|
+
timestamp: Date.now(),
|
|
1285
1413
|
});
|
|
1414
|
+
// Unified message format with tool name and params
|
|
1415
|
+
// Format: OpenAI-compatible tool result with plain text content
|
|
1286
1416
|
this.conversation.push({
|
|
1287
1417
|
role: 'tool',
|
|
1288
|
-
content: JSON.stringify(result),
|
|
1418
|
+
content: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
|
|
1289
1419
|
tool_call_id: toolCall.id,
|
|
1290
|
-
timestamp: Date.now()
|
|
1420
|
+
timestamp: Date.now(),
|
|
1291
1421
|
});
|
|
1292
1422
|
}
|
|
1293
1423
|
}
|
|
1294
1424
|
// Logic: Only skip returning results to main agent when user explicitly cancelled (ESC)
|
|
1295
1425
|
// For all other cases (success, failure, errors), always return results for further processing
|
|
1296
|
-
const guiSubagentCancelled = preparedToolCalls.some(tc => tc.name === 'task' &&
|
|
1426
|
+
const guiSubagentCancelled = preparedToolCalls.some((tc) => tc.name === 'task' &&
|
|
1427
|
+
tc.params?.subagent_type === 'gui-subagent' &&
|
|
1428
|
+
results.some((r) => r.tool === 'task' && r.result?.cancelled === true));
|
|
1297
1429
|
// If GUI agent was cancelled by user, don't continue generating response
|
|
1298
1430
|
// This avoids wasting API calls and tokens on cancelled tasks
|
|
1299
1431
|
if (guiSubagentCancelled) {
|
|
1300
|
-
console.log('');
|
|
1301
|
-
console.log(`${indent}${colors.textMuted('GUI task cancelled by user')}`);
|
|
1302
1432
|
this._isOperationInProgress = false;
|
|
1303
1433
|
return;
|
|
1304
1434
|
}
|
|
1305
|
-
//
|
|
1306
|
-
if (hasError) {
|
|
1307
|
-
this._isOperationInProgress = false;
|
|
1308
|
-
if (onComplete) {
|
|
1309
|
-
// Remote mode: callback handles error state (throws to mark task cancelled)
|
|
1310
|
-
throw new Error('Tool execution failed');
|
|
1311
|
-
}
|
|
1312
|
-
else {
|
|
1313
|
-
// Local mode: throw error to mark task as cancelled
|
|
1314
|
-
throw new Error('Tool execution failed');
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
// Continue based on mode
|
|
1435
|
+
// Continue based on mode - unified handling for both success and error cases
|
|
1318
1436
|
if (onComplete) {
|
|
1437
|
+
await this.checkAndCompressContext();
|
|
1319
1438
|
// Remote mode: use provided callback
|
|
1320
1439
|
await onComplete();
|
|
1321
1440
|
}
|
|
1322
1441
|
else {
|
|
1323
|
-
|
|
1442
|
+
await this.checkAndCompressContext();
|
|
1443
|
+
// Local mode: default behavior - continue with generateResponse
|
|
1324
1444
|
await this.generateResponse();
|
|
1325
1445
|
}
|
|
1326
1446
|
}
|
|
1447
|
+
/**
|
|
1448
|
+
* Clean up incomplete tool calls from conversation after cancellation
|
|
1449
|
+
* This removes assistant messages with tool_calls that don't have corresponding tool_results
|
|
1450
|
+
*/
|
|
1451
|
+
async cleanupIncompleteToolCalls() {
|
|
1452
|
+
// 从后往前找到包含 tool_calls 的 assistant 消息
|
|
1453
|
+
for (let i = this.conversation.length - 1; i >= 0; i--) {
|
|
1454
|
+
const msg = this.conversation[i];
|
|
1455
|
+
if (msg.role === 'assistant' && msg.tool_calls?.length) {
|
|
1456
|
+
// 收集所有 tool_call IDs
|
|
1457
|
+
const allToolCallIds = new Set(msg.tool_calls.map((tc) => tc.id));
|
|
1458
|
+
// 找出哪些 tool_call IDs 已经有对应的 tool_result
|
|
1459
|
+
const completedToolCallIds = new Set();
|
|
1460
|
+
for (let k = i + 1; k < this.conversation.length; k++) {
|
|
1461
|
+
const resultMsg = this.conversation[k];
|
|
1462
|
+
if (resultMsg.role === 'tool' && resultMsg.tool_call_id) {
|
|
1463
|
+
completedToolCallIds.add(resultMsg.tool_call_id);
|
|
1464
|
+
}
|
|
1465
|
+
else if (resultMsg.role === 'user' || resultMsg.role === 'assistant') {
|
|
1466
|
+
break; // 遇到下一个角色消息就停止
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
// 找出未完成的 tool_call IDs
|
|
1470
|
+
const incompleteToolCallIds = [...allToolCallIds].filter(id => !completedToolCallIds.has(id));
|
|
1471
|
+
// 如果所有 tool_call 都已完成,不需要清理
|
|
1472
|
+
if (incompleteToolCallIds.length === 0) {
|
|
1473
|
+
break;
|
|
1474
|
+
}
|
|
1475
|
+
// 只移除未完成的 tool_call,不移除已完成的 tool_result
|
|
1476
|
+
msg.tool_calls = msg.tool_calls.filter((tc) => !incompleteToolCallIds.includes(tc.id));
|
|
1477
|
+
break;
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1327
1481
|
/**
|
|
1328
1482
|
* Get user-friendly description for tool
|
|
1329
1483
|
*/
|
|
1330
1484
|
getToolDescription(toolName, params) {
|
|
1331
1485
|
const descriptions = {
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1486
|
+
Read: (p) => `Read file: ${this.truncatePath(p.filePath)}`,
|
|
1487
|
+
Write: (p) => `Write file: ${this.truncatePath(p.filePath)}`,
|
|
1488
|
+
Grep: (p) => `Search text: "${p.pattern}"`,
|
|
1489
|
+
Bash: (p) => `Execute command: ${this.truncateCommand(p.command)}`,
|
|
1490
|
+
ListDirectory: (p) => `List directory: ${this.truncatePath(p.path || '.')}`,
|
|
1491
|
+
SearchFiles: (p) => `Search files: ${p.pattern}`,
|
|
1492
|
+
DeleteFile: (p) => `Delete file: ${this.truncatePath(p.filePath)}`,
|
|
1493
|
+
CreateDirectory: (p) => `Create directory: ${this.truncatePath(p.dirPath)}`,
|
|
1494
|
+
Edit: (p) => `Edit text: ${this.truncatePath(p.file_path)}`,
|
|
1495
|
+
web_search: (p) => `Web search: "${p.query}"`,
|
|
1496
|
+
todo_write: () => `Update todo list`,
|
|
1497
|
+
todo_read: () => `Read todo list`,
|
|
1498
|
+
task: (p) => `Launch subtask: ${p.description}`,
|
|
1499
|
+
ReadBashOutput: (p) => `Read task output: ${p.task_id}`,
|
|
1500
|
+
web_fetch: () => `Fetch web content`,
|
|
1501
|
+
ask_user_question: () => `Ask user`,
|
|
1502
|
+
save_memory: () => `Save memory`,
|
|
1503
|
+
exit_plan_mode: () => `Complete plan`,
|
|
1504
|
+
xml_escape: (p) => `XML escape: ${this.truncatePath(p.file_path)}`,
|
|
1505
|
+
image_read: (p) => `Read image: ${this.truncatePath(p.image_input)}`,
|
|
1352
1506
|
// 'Skill': (p) => `Execute skill: ${p.skill}`,
|
|
1353
1507
|
// 'ListSkills': () => `List available skills`,
|
|
1354
1508
|
// 'GetSkillDetails': (p) => `Get skill details: ${p.skill}`,
|
|
1355
|
-
|
|
1509
|
+
InvokeSkill: (p) => `Invoke skill: ${p.skillId} - ${this.truncatePath(p.taskDescription || '', 40)}`,
|
|
1356
1510
|
};
|
|
1357
1511
|
const getDescription = descriptions[toolName];
|
|
1358
1512
|
return getDescription ? getDescription(params) : `Execute tool: ${toolName}`;
|
|
@@ -1385,10 +1539,10 @@ export class InteractiveSession {
|
|
|
1385
1539
|
return `${indent}${colors.textMuted('No tasks')}`;
|
|
1386
1540
|
}
|
|
1387
1541
|
const statusConfig = {
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1542
|
+
pending: { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
|
|
1543
|
+
in_progress: { icon: icons.loading, color: colors.warning, label: 'In Progress' },
|
|
1544
|
+
completed: { icon: icons.success, color: colors.success, label: 'Completed' },
|
|
1545
|
+
failed: { icon: icons.error, color: colors.error, label: 'Failed' },
|
|
1392
1546
|
};
|
|
1393
1547
|
const lines = [];
|
|
1394
1548
|
for (const todo of todos) {
|
|
@@ -1501,7 +1655,155 @@ export class InteractiveSession {
|
|
|
1501
1655
|
return this.currentTaskId;
|
|
1502
1656
|
}
|
|
1503
1657
|
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Clean up stale temporary workspaces from previous sessions.
|
|
1660
|
+
* Called at startup to ensure no leftover temp files accumulate.
|
|
1661
|
+
*/
|
|
1662
|
+
async function cleanupStaleWorkspaces() {
|
|
1663
|
+
try {
|
|
1664
|
+
const configManager = getConfigManager();
|
|
1665
|
+
const workspacePath = configManager.getWorkspacePath();
|
|
1666
|
+
// Use default workspace path if workspacePath is empty or undefined
|
|
1667
|
+
const effectiveWorkspacePath = (workspacePath && workspacePath.trim())
|
|
1668
|
+
? workspacePath
|
|
1669
|
+
: os.homedir();
|
|
1670
|
+
const baseWorkspaceDir = path.join(effectiveWorkspacePath, '.xagent', 'workspace');
|
|
1671
|
+
// First, verify the workspace directory exists before attempting cleanup
|
|
1672
|
+
try {
|
|
1673
|
+
const stats = await fsPromises.stat(baseWorkspaceDir);
|
|
1674
|
+
if (!stats.isDirectory()) {
|
|
1675
|
+
return; // Not a directory, skip cleanup
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
catch {
|
|
1679
|
+
// Directory doesn't exist - nothing to clean
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1682
|
+
try {
|
|
1683
|
+
const entries = await fsPromises.readdir(baseWorkspaceDir, { withFileTypes: true });
|
|
1684
|
+
let cleanedCount = 0;
|
|
1685
|
+
for (const entry of entries) {
|
|
1686
|
+
if (entry.isDirectory()) {
|
|
1687
|
+
const fullPath = path.join(baseWorkspaceDir, entry.name);
|
|
1688
|
+
try {
|
|
1689
|
+
await fsPromises.rm(fullPath, { recursive: true, force: true });
|
|
1690
|
+
cleanedCount++;
|
|
1691
|
+
}
|
|
1692
|
+
catch {
|
|
1693
|
+
// Skip directories that can't be removed (in use or permission issues)
|
|
1694
|
+
}
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
if (cleanedCount > 0) {
|
|
1698
|
+
console.log(colors.textDim(`🧹 Cleaned up ${cleanedCount} stale workspace(s)`));
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
catch {
|
|
1702
|
+
// Can't read directory - skip cleanup
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
catch {
|
|
1706
|
+
// Config not available - skip cleanup
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
/**
|
|
1710
|
+
* Initialize built-in skills on first run.
|
|
1711
|
+
* Checks if user skills directory is empty or doesn't exist,
|
|
1712
|
+
* then copies all built-in skills including the protected find-skills.
|
|
1713
|
+
* @returns Number of skills initialized, or 0 if no initialization was needed.
|
|
1714
|
+
*/
|
|
1715
|
+
async function initializeSkillsOnDemand() {
|
|
1716
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
1717
|
+
const __dirname = path.dirname(__filename);
|
|
1718
|
+
// Get user skills directory (respects OS-specific paths)
|
|
1719
|
+
const configManager = getConfigManager();
|
|
1720
|
+
const userSkillsPath = configManager.getUserSkillsPath() || path.join(os.homedir(), '.xagent', 'skills');
|
|
1721
|
+
// Check if user skills directory exists and has skills
|
|
1722
|
+
let hasSkills = false;
|
|
1723
|
+
try {
|
|
1724
|
+
const entries = await fsPromises.readdir(userSkillsPath, { withFileTypes: true });
|
|
1725
|
+
hasSkills = entries.some(e => e.isDirectory());
|
|
1726
|
+
}
|
|
1727
|
+
catch {
|
|
1728
|
+
hasSkills = false;
|
|
1729
|
+
}
|
|
1730
|
+
// If skills already exist, skip initialization
|
|
1731
|
+
if (hasSkills) {
|
|
1732
|
+
return 0;
|
|
1733
|
+
}
|
|
1734
|
+
// Ensure user skills directory exists
|
|
1735
|
+
await fsPromises.mkdir(userSkillsPath, { recursive: true });
|
|
1736
|
+
// Define skill source directories
|
|
1737
|
+
const builtinSkillsDir = path.join(__dirname, '..', 'skills', 'skills');
|
|
1738
|
+
const findSkillsDir = path.join(__dirname, '..', 'find-skills');
|
|
1739
|
+
const skillsToInstall = [];
|
|
1740
|
+
// Add find-skills from root directory
|
|
1741
|
+
if (fs.existsSync(findSkillsDir) && fs.existsSync(path.join(findSkillsDir, 'SKILL.md'))) {
|
|
1742
|
+
skillsToInstall.push({ source: findSkillsDir, name: 'find-skills' });
|
|
1743
|
+
}
|
|
1744
|
+
// Add skills from skills/skills directory
|
|
1745
|
+
if (fs.existsSync(builtinSkillsDir)) {
|
|
1746
|
+
const entries = fs.readdirSync(builtinSkillsDir, { withFileTypes: true });
|
|
1747
|
+
for (const entry of entries) {
|
|
1748
|
+
if (entry.isDirectory()) {
|
|
1749
|
+
const skillPath = path.join(builtinSkillsDir, entry.name);
|
|
1750
|
+
if (fs.existsSync(path.join(skillPath, 'SKILL.md'))) {
|
|
1751
|
+
skillsToInstall.push({ source: skillPath, name: entry.name });
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
if (skillsToInstall.length === 0) {
|
|
1757
|
+
return 0;
|
|
1758
|
+
}
|
|
1759
|
+
// Create user skills directory (already done above, but ensure it exists)
|
|
1760
|
+
await fsPromises.mkdir(userSkillsPath, { recursive: true });
|
|
1761
|
+
// Copy all skills
|
|
1762
|
+
for (const { source, name } of skillsToInstall) {
|
|
1763
|
+
const destPath = path.join(userSkillsPath, name);
|
|
1764
|
+
if (!fs.existsSync(destPath)) {
|
|
1765
|
+
await copyDirectoryRecursiveAsync(source, destPath);
|
|
1766
|
+
}
|
|
1767
|
+
}
|
|
1768
|
+
return skillsToInstall.length;
|
|
1769
|
+
}
|
|
1770
|
+
// Synchronous version (kept for backwards compatibility)
|
|
1771
|
+
function copyDirectoryRecursive(src, dest) {
|
|
1772
|
+
if (!fs.existsSync(dest)) {
|
|
1773
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
1774
|
+
}
|
|
1775
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
1776
|
+
for (const entry of entries) {
|
|
1777
|
+
const srcPath = path.join(src, entry.name);
|
|
1778
|
+
const destPath = path.join(dest, entry.name);
|
|
1779
|
+
if (entry.isDirectory()) {
|
|
1780
|
+
copyDirectoryRecursive(srcPath, destPath);
|
|
1781
|
+
}
|
|
1782
|
+
else if (entry.isFile()) {
|
|
1783
|
+
fs.copyFileSync(srcPath, destPath);
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
// Asynchronous version for concurrent-safe initialization
|
|
1788
|
+
async function copyDirectoryRecursiveAsync(src, dest) {
|
|
1789
|
+
await fsPromises.mkdir(dest, { recursive: true });
|
|
1790
|
+
const entries = await fsPromises.readdir(src, { withFileTypes: true });
|
|
1791
|
+
for (const entry of entries) {
|
|
1792
|
+
const srcPath = path.join(src, entry.name);
|
|
1793
|
+
const destPath = path.join(dest, entry.name);
|
|
1794
|
+
if (entry.isDirectory()) {
|
|
1795
|
+
await copyDirectoryRecursiveAsync(srcPath, destPath);
|
|
1796
|
+
}
|
|
1797
|
+
else if (entry.isFile()) {
|
|
1798
|
+
await fsPromises.copyFile(srcPath, destPath);
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1504
1802
|
export async function startInteractiveSession() {
|
|
1803
|
+
// Clean up any leftover temp workspaces from previous sessions
|
|
1804
|
+
await cleanupStaleWorkspaces();
|
|
1805
|
+
// Initialize built-in skills on first run (silent, returns count)
|
|
1806
|
+
const initializedCount = await initializeSkillsOnDemand();
|
|
1505
1807
|
const session = new InteractiveSession();
|
|
1506
1808
|
// Flag to control shutdown
|
|
1507
1809
|
session._isShuttingDown = false;
|
|
@@ -1537,6 +1839,15 @@ export async function startInteractiveSession() {
|
|
|
1537
1839
|
// Force exit
|
|
1538
1840
|
process.exit(0);
|
|
1539
1841
|
});
|
|
1842
|
+
await session.start(initializedCount);
|
|
1843
|
+
// Check for updates on startup
|
|
1844
|
+
try {
|
|
1845
|
+
const { checkUpdatesOnStartup } = await import('./update.js');
|
|
1846
|
+
await checkUpdatesOnStartup();
|
|
1847
|
+
}
|
|
1848
|
+
catch (error) {
|
|
1849
|
+
// Silently ignore update check failures
|
|
1850
|
+
}
|
|
1540
1851
|
await session.start();
|
|
1541
1852
|
}
|
|
1542
1853
|
// Singleton session instance for access from other modules
|