@xagent-ai/cli 1.2.2 → 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 +624 -243
- 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 +67 -55
- 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 +84 -34
- 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 +797 -437
- 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 +948 -386
- 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/src/session.ts
CHANGED
|
@@ -4,16 +4,28 @@ import https from 'https';
|
|
|
4
4
|
import axios from 'axios';
|
|
5
5
|
import crypto from 'crypto';
|
|
6
6
|
import ora from 'ora';
|
|
7
|
-
import inquirer from 'inquirer';
|
|
8
7
|
import { createRequire } from 'module';
|
|
9
|
-
import
|
|
8
|
+
import path from 'path';
|
|
10
9
|
import { fileURLToPath } from 'url';
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import fsPromises from 'fs/promises';
|
|
12
|
+
import os from 'os';
|
|
11
13
|
|
|
12
14
|
const require = createRequire(import.meta.url);
|
|
13
15
|
const packageJson = require('../package.json');
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
import {
|
|
17
|
+
ExecutionMode,
|
|
18
|
+
ChatMessage,
|
|
19
|
+
ToolCall,
|
|
20
|
+
AuthType,
|
|
21
|
+
AuthConfig,
|
|
22
|
+
AgentConfig,
|
|
23
|
+
ToolCallItem,
|
|
24
|
+
} from './types.js';
|
|
25
|
+
import { createAIClient, type AIClientInterface } from './ai-client-factory.js';
|
|
26
|
+
import { detectThinkingKeywords, getThinkingTokens } from './ai-client/types.js';
|
|
27
|
+
import { TokenInvalidError } from './ai-client/types.js';
|
|
28
|
+
import { fetchDefaultModels } from './ai-client/providers/remote.js';
|
|
17
29
|
import { getConfigManager, ConfigManager } from './config.js';
|
|
18
30
|
import { AuthService, selectAuthType } from './auth.js';
|
|
19
31
|
import { getToolRegistry } from './tools.js';
|
|
@@ -25,10 +37,28 @@ import { getConversationManager, ConversationManager } from './conversation.js';
|
|
|
25
37
|
import { getSessionManager, SessionManager } from './session-manager.js';
|
|
26
38
|
import { SlashCommandHandler, parseInput, detectImageInput } from './slash-commands.js';
|
|
27
39
|
import { SystemPromptGenerator } from './system-prompt-generator.js';
|
|
28
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
theme,
|
|
42
|
+
icons,
|
|
43
|
+
colors,
|
|
44
|
+
styleHelpers,
|
|
45
|
+
renderMarkdown,
|
|
46
|
+
renderDiff,
|
|
47
|
+
renderLines,
|
|
48
|
+
TERMINAL_BG,
|
|
49
|
+
} from './theme.js';
|
|
29
50
|
import { getCancellationManager, CancellationManager } from './cancellation.js';
|
|
30
|
-
import {
|
|
51
|
+
import {
|
|
52
|
+
getContextCompressor,
|
|
53
|
+
ContextCompressor,
|
|
54
|
+
CompressionResult,
|
|
55
|
+
} from './context-compressor.js';
|
|
31
56
|
import { Logger, LogLevel, getLogger } from './logger.js';
|
|
57
|
+
import { ensureTtySane, setupEscKeyHandler } from './terminal.js';
|
|
58
|
+
|
|
59
|
+
// Type aliases for backward compatibility
|
|
60
|
+
type AIClient = AIClientInterface;
|
|
61
|
+
type RemoteAIClient = AIClientInterface;
|
|
32
62
|
|
|
33
63
|
const logger = getLogger();
|
|
34
64
|
|
|
@@ -39,7 +69,7 @@ export class InteractiveSession {
|
|
|
39
69
|
private aiClient: AIClient | null = null;
|
|
40
70
|
private remoteAIClient: RemoteAIClient | null = null;
|
|
41
71
|
private conversation: ChatMessage[] = [];
|
|
42
|
-
private
|
|
72
|
+
private tool_calls: ToolCall[] = [];
|
|
43
73
|
private executionMode: ExecutionMode;
|
|
44
74
|
private slashCommandHandler: SlashCommandHandler;
|
|
45
75
|
private configManager: ConfigManager;
|
|
@@ -47,7 +77,7 @@ export class InteractiveSession {
|
|
|
47
77
|
private memoryManager: MemoryManager;
|
|
48
78
|
private mcpManager: MCPManager;
|
|
49
79
|
private checkpointManager: CheckpointManager;
|
|
50
|
-
private currentAgent:
|
|
80
|
+
private currentAgent: AgentConfig | null = null;
|
|
51
81
|
private rl: readline.Interface;
|
|
52
82
|
private cancellationManager: CancellationManager;
|
|
53
83
|
private indentLevel: number;
|
|
@@ -57,67 +87,51 @@ export class InteractiveSession {
|
|
|
57
87
|
private taskCompleted: boolean = false;
|
|
58
88
|
private isFirstApiCall: boolean = true;
|
|
59
89
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
output: process.stdout
|
|
67
|
-
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
this.configManager = getConfigManager(process.cwd());
|
|
73
|
-
|
|
74
|
-
this.agentManager = getAgentManager(process.cwd());
|
|
75
|
-
|
|
76
|
-
this.memoryManager = getMemoryManager(process.cwd());
|
|
77
|
-
|
|
78
|
-
this.mcpManager = getMCPManager();
|
|
79
|
-
|
|
80
|
-
this.checkpointManager = getCheckpointManager(process.cwd());
|
|
81
|
-
|
|
82
|
-
this.conversationManager = getConversationManager();
|
|
83
|
-
|
|
84
|
-
this.sessionManager = getSessionManager(process.cwd());
|
|
85
|
-
|
|
86
|
-
this.slashCommandHandler = new SlashCommandHandler();
|
|
87
|
-
|
|
88
|
-
// Register /clear callback, clear local conversation when clearing dialogue
|
|
89
|
-
this.slashCommandHandler.setClearCallback(() => {
|
|
90
|
-
this.conversation = [];
|
|
91
|
-
this.toolCalls = [];
|
|
92
|
-
this.currentTaskId = null;
|
|
93
|
-
this.taskCompleted = false;
|
|
94
|
-
this.isFirstApiCall = true;
|
|
95
|
-
this.slashCommandHandler.setConversationHistory([]);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// Register MCP update callback, update system prompt
|
|
101
|
-
|
|
102
|
-
this.slashCommandHandler.setSystemPromptUpdateCallback(async () => {
|
|
103
|
-
|
|
104
|
-
await this.updateSystemPrompt();
|
|
90
|
+
constructor(indentLevel: number = 0) {
|
|
91
|
+
this.rl = readline.createInterface({
|
|
92
|
+
input: process.stdin,
|
|
93
|
+
output: process.stdout,
|
|
94
|
+
});
|
|
105
95
|
|
|
106
|
-
|
|
96
|
+
this.configManager = getConfigManager(process.cwd());
|
|
97
|
+
this.agentManager = getAgentManager(process.cwd());
|
|
98
|
+
this.memoryManager = getMemoryManager(process.cwd());
|
|
99
|
+
this.mcpManager = getMCPManager();
|
|
100
|
+
this.checkpointManager = getCheckpointManager(process.cwd());
|
|
101
|
+
this.conversationManager = getConversationManager();
|
|
102
|
+
this.sessionManager = getSessionManager(process.cwd());
|
|
103
|
+
this.slashCommandHandler = new SlashCommandHandler();
|
|
104
|
+
|
|
105
|
+
// Register /clear callback, clear local conversation when clearing dialogue
|
|
106
|
+
this.slashCommandHandler.setClearCallback(() => {
|
|
107
|
+
this.conversation = [];
|
|
108
|
+
this.tool_calls = [];
|
|
109
|
+
this.currentTaskId = null;
|
|
110
|
+
this.taskCompleted = false;
|
|
111
|
+
this.isFirstApiCall = true;
|
|
112
|
+
this.slashCommandHandler.setConversationHistory([]);
|
|
113
|
+
});
|
|
107
114
|
|
|
108
|
-
|
|
115
|
+
// Register MCP update callback, update system prompt
|
|
116
|
+
this.slashCommandHandler.setSystemPromptUpdateCallback(async () => {
|
|
117
|
+
await this.updateSystemPrompt();
|
|
118
|
+
});
|
|
109
119
|
|
|
110
|
-
|
|
120
|
+
// Register config update callback, update aiClient config when /auth changes config
|
|
121
|
+
this.slashCommandHandler.setConfigUpdateCallback(() => {
|
|
122
|
+
this.updateAiClientConfig();
|
|
123
|
+
});
|
|
111
124
|
|
|
112
|
-
|
|
125
|
+
this.executionMode = ExecutionMode.DEFAULT;
|
|
113
126
|
|
|
114
|
-
|
|
127
|
+
this.cancellationManager = getCancellationManager();
|
|
115
128
|
|
|
116
|
-
|
|
129
|
+
this.indentLevel = indentLevel;
|
|
117
130
|
|
|
118
|
-
|
|
131
|
+
this.indentString = ' '.repeat(indentLevel);
|
|
119
132
|
|
|
120
|
-
|
|
133
|
+
this.contextCompressor = getContextCompressor();
|
|
134
|
+
}
|
|
121
135
|
|
|
122
136
|
private getIndent(): string {
|
|
123
137
|
return this.indentString;
|
|
@@ -127,6 +141,34 @@ export class InteractiveSession {
|
|
|
127
141
|
this.aiClient = aiClient;
|
|
128
142
|
}
|
|
129
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Update aiClient config when /auth changes config (called from callback)
|
|
146
|
+
*/
|
|
147
|
+
updateAiClientConfig(): void {
|
|
148
|
+
const authConfig = this.configManager.getAuthConfig();
|
|
149
|
+
const isRemote = authConfig.type === AuthType.OAUTH_XAGENT;
|
|
150
|
+
|
|
151
|
+
if (isRemote) {
|
|
152
|
+
// Already in remote mode, no change needed
|
|
153
|
+
if (this.remoteAIClient !== null) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
// Switch to remote: clear local client, create remote client
|
|
157
|
+
this.aiClient = null;
|
|
158
|
+
this.remoteAIClient = createAIClient(authConfig);
|
|
159
|
+
} else {
|
|
160
|
+
// Already in local mode, no change needed
|
|
161
|
+
if (this.aiClient !== null) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// Switch to local: clear remote client, create local client
|
|
165
|
+
this.remoteAIClient = null;
|
|
166
|
+
this.aiClient = createAIClient(authConfig);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
this.slashCommandHandler.setRemoteAIClient(this.remoteAIClient);
|
|
170
|
+
}
|
|
171
|
+
|
|
130
172
|
setExecutionMode(mode: ExecutionMode): void {
|
|
131
173
|
this.executionMode = mode;
|
|
132
174
|
}
|
|
@@ -135,30 +177,88 @@ export class InteractiveSession {
|
|
|
135
177
|
* Update system prompt to reflect MCP changes (called after add/remove MCP)
|
|
136
178
|
*/
|
|
137
179
|
async updateSystemPrompt(): Promise<void> {
|
|
180
|
+
// Reload skills to pick up any newly added/removed skills
|
|
181
|
+
// First reset the SkillLoader to clear all cached skills
|
|
182
|
+
const { resetSkillLoader } = await import('./skill-loader.js');
|
|
183
|
+
resetSkillLoader();
|
|
184
|
+
|
|
185
|
+
// Then reload the skill invoker
|
|
186
|
+
const skillInvoker = (await import('./skill-invoker.js')).getSkillInvoker();
|
|
187
|
+
await skillInvoker.reload();
|
|
188
|
+
|
|
138
189
|
const toolRegistry = getToolRegistry();
|
|
139
|
-
const promptGenerator = new SystemPromptGenerator(
|
|
190
|
+
const promptGenerator = new SystemPromptGenerator(
|
|
191
|
+
toolRegistry,
|
|
192
|
+
this.executionMode,
|
|
193
|
+
undefined,
|
|
194
|
+
this.mcpManager
|
|
195
|
+
);
|
|
140
196
|
|
|
141
197
|
// Use the current agent's original system prompt as base
|
|
142
|
-
const baseSystemPrompt =
|
|
198
|
+
const baseSystemPrompt =
|
|
199
|
+
this.currentAgent?.systemPrompt || 'You are xAgent, an AI-powered CLI tool.';
|
|
143
200
|
const newSystemPrompt = await promptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
|
|
144
201
|
|
|
145
202
|
// Replace old system prompt with new one
|
|
146
|
-
this.conversation = this.conversation.filter(msg => msg.role !== 'system');
|
|
203
|
+
this.conversation = this.conversation.filter((msg) => msg.role !== 'system');
|
|
147
204
|
this.conversation.unshift({
|
|
148
205
|
role: 'system',
|
|
149
206
|
content: newSystemPrompt,
|
|
150
|
-
timestamp: Date.now()
|
|
207
|
+
timestamp: Date.now(),
|
|
151
208
|
});
|
|
152
209
|
|
|
153
210
|
// Sync to slashCommandHandler
|
|
154
211
|
this.slashCommandHandler.setConversationHistory(this.conversation);
|
|
155
212
|
}
|
|
156
213
|
|
|
157
|
-
|
|
214
|
+
/**
|
|
215
|
+
* Watch for skill updates from CLI and update system prompt accordingly
|
|
216
|
+
*/
|
|
217
|
+
private startSkillUpdateWatcher(): void {
|
|
218
|
+
const SKILL_STATE_FILE = '.skill-state.json';
|
|
219
|
+
const configManager = getConfigManager();
|
|
220
|
+
const userSkillsPath = configManager.getUserSkillsPath();
|
|
221
|
+
const stateFilePath = userSkillsPath
|
|
222
|
+
? path.join(userSkillsPath, SKILL_STATE_FILE)
|
|
223
|
+
: null;
|
|
224
|
+
|
|
225
|
+
if (!stateFilePath) {
|
|
226
|
+
return; // No user skills path configured
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let lastUpdateTime = 0;
|
|
230
|
+
|
|
231
|
+
// Check for updates every 2 seconds
|
|
232
|
+
const checkInterval = setInterval(async () => {
|
|
233
|
+
try {
|
|
234
|
+
const { existsSync, readFileSync } = await import('fs');
|
|
235
|
+
if (existsSync(stateFilePath)) {
|
|
236
|
+
const content = readFileSync(stateFilePath, 'utf-8');
|
|
237
|
+
const state = JSON.parse(content);
|
|
238
|
+
|
|
239
|
+
if (state.lastSkillUpdate && state.lastSkillUpdate > lastUpdateTime) {
|
|
240
|
+
lastUpdateTime = state.lastSkillUpdate;
|
|
241
|
+
|
|
242
|
+
// Update system prompt with new skills
|
|
243
|
+
await this.updateSystemPrompt();
|
|
244
|
+
|
|
245
|
+
console.log(colors.textMuted(' 🔄 Skills updated from CLI'));
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
} catch (error) {
|
|
249
|
+
// Silent fail - watcher is optional
|
|
250
|
+
}
|
|
251
|
+
}, 2000);
|
|
252
|
+
|
|
253
|
+
// Clean up on session end
|
|
254
|
+
(this as any)._skillWatcherInterval = checkInterval;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
setAgent(agent: AgentConfig): void {
|
|
158
258
|
this.currentAgent = agent;
|
|
159
259
|
}
|
|
160
260
|
|
|
161
|
-
async start(): Promise<void> {
|
|
261
|
+
async start(initializedCount: number = 0): Promise<void> {
|
|
162
262
|
// Set this session as the singleton for access from other modules
|
|
163
263
|
setSingletonSession(this);
|
|
164
264
|
|
|
@@ -169,29 +269,52 @@ export class InteractiveSession {
|
|
|
169
269
|
console.log('');
|
|
170
270
|
console.log(colors.gradient('╔════════════════════════════════════════════════════════════╗'));
|
|
171
271
|
console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
|
|
172
|
-
console.log(
|
|
173
|
-
|
|
272
|
+
console.log(
|
|
273
|
+
colors.gradient('║') +
|
|
274
|
+
' '.repeat(13) +
|
|
275
|
+
'🤖 ' +
|
|
276
|
+
colors.gradient('XAGENT CLI') +
|
|
277
|
+
' '.repeat(32) +
|
|
278
|
+
colors.gradient(' ║')
|
|
279
|
+
);
|
|
280
|
+
console.log(
|
|
281
|
+
colors.gradient('║') +
|
|
282
|
+
' '.repeat(16) +
|
|
283
|
+
colors.textMuted(`v${packageJson.version}`) +
|
|
284
|
+
' '.repeat(36) +
|
|
285
|
+
colors.gradient(' ║')
|
|
286
|
+
);
|
|
174
287
|
console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
|
|
175
288
|
console.log(colors.gradient('╚════════════════════════════════════════════════════════════╝'));
|
|
176
289
|
console.log(colors.textMuted(' AI-powered command-line assistant'));
|
|
290
|
+
|
|
291
|
+
// Show initialization message if skills were initialized
|
|
292
|
+
if (initializedCount > 0) {
|
|
293
|
+
console.log(colors.textMuted(` ✨ Initialized ${initializedCount} built-in skills`));
|
|
294
|
+
}
|
|
177
295
|
console.log('');
|
|
178
296
|
|
|
179
297
|
await this.initialize();
|
|
180
298
|
this.showWelcomeMessage();
|
|
181
299
|
|
|
300
|
+
// Start watching for skill updates from CLI
|
|
301
|
+
this.startSkillUpdateWatcher();
|
|
302
|
+
// Set up ESC key handler using the terminal module
|
|
303
|
+
// This avoids conflicts with readline and provides clean ESC detection
|
|
304
|
+
let escCleanup: (() => void) | undefined;
|
|
305
|
+
if (process.stdin.isTTY) {
|
|
306
|
+
escCleanup = setupEscKeyHandler(() => {
|
|
307
|
+
if ((this as any)._isOperationInProgress) {
|
|
308
|
+
// An operation is running, let it be cancelled
|
|
309
|
+
this.cancellationManager.cancel();
|
|
310
|
+
}
|
|
311
|
+
// No operation running, ignore ESC
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
182
315
|
// Track if an operation is in progress
|
|
183
316
|
(this as any)._isOperationInProgress = false;
|
|
184
317
|
|
|
185
|
-
// Listen for ESC cancellation - only cancel operations, don't exit the program
|
|
186
|
-
const cancelHandler = () => {
|
|
187
|
-
if ((this as any)._isOperationInProgress) {
|
|
188
|
-
// An operation is running, let it be cancelled
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
// No operation running, ignore ESC or show a message
|
|
192
|
-
};
|
|
193
|
-
this.cancellationManager.on('cancelled', cancelHandler);
|
|
194
|
-
|
|
195
318
|
this.promptLoop();
|
|
196
319
|
|
|
197
320
|
// Keep the promise pending until shutdown
|
|
@@ -204,17 +327,17 @@ export class InteractiveSession {
|
|
|
204
327
|
logger.debug('\n[SESSION] ========== initialize() 开始 ==========\n');
|
|
205
328
|
|
|
206
329
|
try {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
330
|
+
// Custom spinner for initialization (like Thinking...)
|
|
331
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
332
|
+
let frameIndex = 0;
|
|
333
|
+
const validatingText = colors.textMuted('Validating authentication...');
|
|
334
|
+
|
|
335
|
+
const spinnerInterval = setInterval(() => {
|
|
336
|
+
process.stdout.write(`\r${colors.primary(frames[frameIndex])} ${validatingText}`);
|
|
337
|
+
frameIndex = (frameIndex + 1) % frames.length;
|
|
338
|
+
}, 120);
|
|
216
339
|
logger.debug('[SESSION] 调用 configManager.load()...');
|
|
217
|
-
|
|
340
|
+
this.configManager.load();
|
|
218
341
|
|
|
219
342
|
logger.debug('[SESSION] Config loaded');
|
|
220
343
|
let authConfig = this.configManager.getAuthConfig();
|
|
@@ -224,13 +347,16 @@ export class InteractiveSession {
|
|
|
224
347
|
logger.debug('[SESSION] selectedAuthType (initial):', String(selectedAuthType));
|
|
225
348
|
logger.debug('[SESSION] AuthType.OAUTH_XAGENT:', String(AuthType.OAUTH_XAGENT));
|
|
226
349
|
logger.debug('[SESSION] AuthType.OPENAI_COMPATIBLE:', String(AuthType.OPENAI_COMPATIBLE));
|
|
227
|
-
logger.debug(
|
|
350
|
+
logger.debug(
|
|
351
|
+
'[SESSION] Will validate OAuth:',
|
|
352
|
+
String(!!(authConfig.apiKey && selectedAuthType === AuthType.OAUTH_XAGENT))
|
|
353
|
+
);
|
|
228
354
|
|
|
229
355
|
// Only validate OAuth tokens, skip validation for third-party API keys
|
|
230
356
|
if (authConfig.apiKey && selectedAuthType === AuthType.OAUTH_XAGENT) {
|
|
231
357
|
clearInterval(spinnerInterval);
|
|
232
358
|
process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear the line
|
|
233
|
-
|
|
359
|
+
|
|
234
360
|
const baseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
|
|
235
361
|
let isValid = await this.validateToken(baseUrl, authConfig.apiKey);
|
|
236
362
|
|
|
@@ -242,15 +368,14 @@ export class InteractiveSession {
|
|
|
242
368
|
process.stdout.write(`\r${colors.primary(frames[frameIndex])} ${refreshingText}`);
|
|
243
369
|
frameIndex = (frameIndex + 1) % frames.length;
|
|
244
370
|
}, 120);
|
|
245
|
-
|
|
371
|
+
|
|
246
372
|
const newToken = await this.refreshToken(baseUrl, authConfig.refreshToken);
|
|
247
373
|
clearInterval(refreshInterval);
|
|
248
374
|
process.stdout.write('\r' + ' '.repeat(50) + '\r');
|
|
249
375
|
|
|
250
376
|
if (newToken) {
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
await this.configManager.save('global');
|
|
377
|
+
this.configManager.set('apiKey', newToken);
|
|
378
|
+
this.configManager.save('global');
|
|
254
379
|
authConfig.apiKey = newToken;
|
|
255
380
|
isValid = true;
|
|
256
381
|
}
|
|
@@ -262,23 +387,21 @@ export class InteractiveSession {
|
|
|
262
387
|
console.log(colors.info('Please select an authentication method to continue.'));
|
|
263
388
|
console.log('');
|
|
264
389
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
await this.configManager.set('refreshToken', '');
|
|
269
|
-
await this.configManager.save('global');
|
|
390
|
+
this.configManager.set('apiKey', '');
|
|
391
|
+
this.configManager.set('refreshToken', '');
|
|
392
|
+
this.configManager.save('global');
|
|
270
393
|
|
|
271
|
-
|
|
394
|
+
this.configManager.load();
|
|
272
395
|
authConfig = this.configManager.getAuthConfig();
|
|
273
396
|
|
|
274
397
|
await this.setupAuthentication();
|
|
275
398
|
authConfig = this.configManager.getAuthConfig();
|
|
276
399
|
|
|
277
|
-
// Recreate readline interface after
|
|
400
|
+
// Recreate readline interface after interactive prompt
|
|
278
401
|
this.rl.close();
|
|
279
402
|
this.rl = readline.createInterface({
|
|
280
403
|
input: process.stdin,
|
|
281
|
-
output: process.stdout
|
|
404
|
+
output: process.stdout,
|
|
282
405
|
});
|
|
283
406
|
this.rl.on('close', () => {
|
|
284
407
|
// readline closed
|
|
@@ -293,11 +416,11 @@ export class InteractiveSession {
|
|
|
293
416
|
selectedAuthType = this.configManager.get('selectedAuthType');
|
|
294
417
|
logger.debug('[SESSION] selectedAuthType (after setup):', String(selectedAuthType));
|
|
295
418
|
|
|
296
|
-
// Recreate readline interface after
|
|
419
|
+
// Recreate readline interface after interactive prompt
|
|
297
420
|
this.rl.close();
|
|
298
421
|
this.rl = readline.createInterface({
|
|
299
422
|
input: process.stdin,
|
|
300
|
-
output: process.stdout
|
|
423
|
+
output: process.stdout,
|
|
301
424
|
});
|
|
302
425
|
this.rl.on('close', () => {
|
|
303
426
|
// readline closed
|
|
@@ -308,22 +431,46 @@ export class InteractiveSession {
|
|
|
308
431
|
}
|
|
309
432
|
// For OPENAI_COMPATIBLE with API key, skip validation and proceed directly
|
|
310
433
|
|
|
311
|
-
|
|
312
|
-
this.contextCompressor.setAIClient(this.aiClient);
|
|
313
|
-
|
|
314
|
-
// Initialize remote AI client for OAuth XAGENT mode
|
|
315
|
-
logger.debug('[SESSION] Final selectedAuthType:', String(selectedAuthType));
|
|
316
|
-
logger.debug('[SESSION] Creating RemoteAIClient?', String(selectedAuthType === AuthType.OAUTH_XAGENT));
|
|
434
|
+
// Initialize AI clients and set contextCompressor appropriately
|
|
317
435
|
if (selectedAuthType === AuthType.OAUTH_XAGENT) {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
436
|
+
// Remote mode: fetch default models if not set
|
|
437
|
+
const currentLlm = this.configManager.get('remote_llmModelName');
|
|
438
|
+
const currentVlm = this.configManager.get('remote_vlmModelName');
|
|
439
|
+
|
|
440
|
+
if (!currentLlm || !currentVlm) {
|
|
441
|
+
const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
|
|
442
|
+
|
|
443
|
+
try {
|
|
444
|
+
const defaults = await fetchDefaultModels(authConfig.apiKey || '', webBaseUrl);
|
|
445
|
+
|
|
446
|
+
if (!currentLlm && defaults.llm?.name) {
|
|
447
|
+
this.configManager.set('remote_llmModelName', defaults.llm.name);
|
|
448
|
+
}
|
|
449
|
+
if (!currentVlm && defaults.vlm?.name) {
|
|
450
|
+
this.configManager.set('remote_vlmModelName', defaults.vlm.name);
|
|
451
|
+
}
|
|
452
|
+
this.configManager.save('global');
|
|
453
|
+
} catch (error: any) {
|
|
454
|
+
logger.debug('[SESSION] Failed to fetch default models:', error.message);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Remote mode: create RemoteAIClient and use it for context compression
|
|
459
|
+
this.remoteAIClient = createAIClient(authConfig);
|
|
460
|
+
this.contextCompressor.setAIClient(this.remoteAIClient);
|
|
321
461
|
logger.debug('[DEBUG Initialize] RemoteAIClient created successfully');
|
|
322
462
|
} else {
|
|
463
|
+
// Local mode: create local AIClient
|
|
464
|
+
this.aiClient = createAIClient(authConfig);
|
|
465
|
+
this.contextCompressor.setAIClient(this.aiClient);
|
|
323
466
|
logger.debug('[DEBUG Initialize] RemoteAIClient NOT created (not OAuth XAGENT mode)');
|
|
324
467
|
}
|
|
325
468
|
|
|
326
|
-
|
|
469
|
+
// Sync remoteAIClient reference to slashCommandHandler for /provider command
|
|
470
|
+
this.slashCommandHandler.setRemoteAIClient(this.remoteAIClient);
|
|
471
|
+
|
|
472
|
+
this.executionMode =
|
|
473
|
+
this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
|
|
327
474
|
|
|
328
475
|
await this.agentManager.loadAgents();
|
|
329
476
|
await this.memoryManager.loadMemory();
|
|
@@ -350,11 +497,17 @@ export class InteractiveSession {
|
|
|
350
497
|
// Eagerly connect to MCP servers to get tool definitions
|
|
351
498
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
352
499
|
try {
|
|
353
|
-
console.log(
|
|
500
|
+
console.log(
|
|
501
|
+
`${colors.info(`${icons.brain} Connecting to ${Object.keys(mcpServers).length} MCP server(s)...`)}`
|
|
502
|
+
);
|
|
354
503
|
await this.mcpManager.connectAllServers();
|
|
355
|
-
const connectedCount = Array.from(this.mcpManager.getAllServers()).filter((s: any) =>
|
|
504
|
+
const connectedCount = Array.from(this.mcpManager.getAllServers()).filter((s: any) =>
|
|
505
|
+
s.isServerConnected()
|
|
506
|
+
).length;
|
|
356
507
|
const mcpTools = this.mcpManager.getToolDefinitions();
|
|
357
|
-
console.log(
|
|
508
|
+
console.log(
|
|
509
|
+
`${colors.success(`✓ ${connectedCount}/${Object.keys(mcpServers).length} MCP server(s) connected (${mcpTools.length} tools available)`)}`
|
|
510
|
+
);
|
|
358
511
|
|
|
359
512
|
// Register MCP tools with the tool registry (hide MCP origin from LLM)
|
|
360
513
|
const toolRegistry = getToolRegistry();
|
|
@@ -375,7 +528,7 @@ export class InteractiveSession {
|
|
|
375
528
|
await this.checkpointManager.initialize();
|
|
376
529
|
}
|
|
377
530
|
|
|
378
|
-
this.currentAgent = this.agentManager.getAgent('general-purpose');
|
|
531
|
+
this.currentAgent = this.agentManager.getAgent('general-purpose') ?? null;
|
|
379
532
|
|
|
380
533
|
console.log(colors.success('✔ Initialization complete'));
|
|
381
534
|
} catch (error: any) {
|
|
@@ -392,7 +545,7 @@ export class InteractiveSession {
|
|
|
392
545
|
private async validateToken(baseUrl: string, apiKey: string): Promise<boolean> {
|
|
393
546
|
logger.debug('[SESSION] validateToken called with baseUrl:', baseUrl);
|
|
394
547
|
logger.debug('[SESSION] apiKey exists:', apiKey ? 'yes' : 'no');
|
|
395
|
-
|
|
548
|
+
|
|
396
549
|
try {
|
|
397
550
|
// For OAuth XAGENT auth, use /api/auth/me endpoint
|
|
398
551
|
const url = `${baseUrl}/api/auth/me`;
|
|
@@ -402,11 +555,11 @@ export class InteractiveSession {
|
|
|
402
555
|
|
|
403
556
|
const response = await axios.get(url, {
|
|
404
557
|
headers: {
|
|
405
|
-
|
|
406
|
-
'Content-Type': 'application/json'
|
|
558
|
+
Authorization: `Bearer ${apiKey}`,
|
|
559
|
+
'Content-Type': 'application/json',
|
|
407
560
|
},
|
|
408
561
|
httpsAgent,
|
|
409
|
-
timeout: 10000
|
|
562
|
+
timeout: 10000,
|
|
410
563
|
});
|
|
411
564
|
|
|
412
565
|
logger.debug('[SESSION] Validation response status:', String(response.status));
|
|
@@ -428,10 +581,14 @@ export class InteractiveSession {
|
|
|
428
581
|
const url = `${baseUrl}/api/auth/refresh`;
|
|
429
582
|
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
430
583
|
|
|
431
|
-
const response = await axios.post(
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
584
|
+
const response = await axios.post(
|
|
585
|
+
url,
|
|
586
|
+
{ refreshToken },
|
|
587
|
+
{
|
|
588
|
+
httpsAgent,
|
|
589
|
+
timeout: 10000,
|
|
590
|
+
}
|
|
591
|
+
);
|
|
435
592
|
|
|
436
593
|
if (response.status === 200) {
|
|
437
594
|
const data = response.data as { token?: string; refreshToken?: string };
|
|
@@ -454,11 +611,15 @@ export class InteractiveSession {
|
|
|
454
611
|
const authType = await selectAuthType();
|
|
455
612
|
this.configManager.set('selectedAuthType', authType);
|
|
456
613
|
|
|
614
|
+
// Get xagentApiBaseUrl from config (respects XAGENT_BASE_URL env var)
|
|
615
|
+
const config = this.configManager.getAuthConfig();
|
|
616
|
+
|
|
457
617
|
const authService = new AuthService({
|
|
458
618
|
type: authType,
|
|
459
619
|
apiKey: '',
|
|
460
620
|
baseUrl: '',
|
|
461
|
-
modelName: ''
|
|
621
|
+
modelName: '',
|
|
622
|
+
xagentApiBaseUrl: config.xagentApiBaseUrl,
|
|
462
623
|
});
|
|
463
624
|
|
|
464
625
|
const success = await authService.authenticate();
|
|
@@ -472,17 +633,32 @@ export class InteractiveSession {
|
|
|
472
633
|
|
|
473
634
|
const authConfig = authService.getAuthConfig();
|
|
474
635
|
|
|
636
|
+
// Clear modelName for remote mode
|
|
637
|
+
if (authType === AuthType.OAUTH_XAGENT) {
|
|
638
|
+
authConfig.modelName = '';
|
|
639
|
+
}
|
|
640
|
+
|
|
475
641
|
// VLM configuration is optional - only show for non-OAuth (local) mode
|
|
476
642
|
// Remote mode uses backend VLM configuration
|
|
477
643
|
if (authType !== AuthType.OAUTH_XAGENT) {
|
|
478
644
|
console.log('');
|
|
479
645
|
console.log(colors.info(`${icons.info} VLM configuration is optional.`));
|
|
480
|
-
console.log(colors.info(`You can configure it later using the /
|
|
646
|
+
console.log(colors.info(`You can configure it later using the /model command if needed.`));
|
|
481
647
|
console.log('');
|
|
482
648
|
}
|
|
483
649
|
|
|
484
|
-
|
|
485
|
-
|
|
650
|
+
this.configManager.setAuthConfig(authConfig);
|
|
651
|
+
|
|
652
|
+
// Set default remote model settings if not already set
|
|
653
|
+
if (authType === AuthType.OAUTH_XAGENT) {
|
|
654
|
+
if (!this.configManager.get('remote_llmModelName')) {
|
|
655
|
+
this.configManager.set('remote_llmModelName', '');
|
|
656
|
+
}
|
|
657
|
+
if (!this.configManager.get('remote_vlmModelName')) {
|
|
658
|
+
this.configManager.set('remote_vlmModelName', '');
|
|
659
|
+
}
|
|
660
|
+
this.configManager.save('global');
|
|
661
|
+
}
|
|
486
662
|
}
|
|
487
663
|
|
|
488
664
|
private showWelcomeMessage(): void {
|
|
@@ -494,7 +670,8 @@ export class InteractiveSession {
|
|
|
494
670
|
|
|
495
671
|
if (language === 'zh') {
|
|
496
672
|
console.log(colors.primaryBright(`${icons.sparkles} Welcome to XAGENT CLI!`));
|
|
497
|
-
|
|
673
|
+
console.log(colors.textMuted('Type /help to see available commands'));
|
|
674
|
+
} else {
|
|
498
675
|
console.log(colors.primaryBright(`${icons.sparkles} Welcome to XAGENT CLI!`));
|
|
499
676
|
console.log(colors.textMuted('Type /help to see available commands'));
|
|
500
677
|
}
|
|
@@ -510,28 +687,28 @@ export class InteractiveSession {
|
|
|
510
687
|
[ExecutionMode.YOLO]: {
|
|
511
688
|
color: colors.error,
|
|
512
689
|
icon: icons.fire,
|
|
513
|
-
description: 'Execute commands without confirmation'
|
|
690
|
+
description: 'Execute commands without confirmation',
|
|
514
691
|
},
|
|
515
692
|
[ExecutionMode.ACCEPT_EDITS]: {
|
|
516
693
|
color: colors.warning,
|
|
517
694
|
icon: icons.check,
|
|
518
|
-
description: 'Accept all edits automatically'
|
|
695
|
+
description: 'Accept all edits automatically',
|
|
519
696
|
},
|
|
520
697
|
[ExecutionMode.PLAN]: {
|
|
521
698
|
color: colors.info,
|
|
522
699
|
icon: icons.brain,
|
|
523
|
-
description: 'Plan before executing'
|
|
700
|
+
description: 'Plan before executing',
|
|
524
701
|
},
|
|
525
702
|
[ExecutionMode.DEFAULT]: {
|
|
526
703
|
color: colors.success,
|
|
527
704
|
icon: icons.bolt,
|
|
528
|
-
description: 'Safe execution with confirmations'
|
|
705
|
+
description: 'Safe execution with confirmations',
|
|
529
706
|
},
|
|
530
707
|
[ExecutionMode.SMART]: {
|
|
531
708
|
color: colors.primaryBright,
|
|
532
709
|
icon: icons.sparkles,
|
|
533
|
-
description: 'Smart approval with intelligent security checks'
|
|
534
|
-
}
|
|
710
|
+
description: 'Smart approval with intelligent security checks',
|
|
711
|
+
},
|
|
535
712
|
};
|
|
536
713
|
|
|
537
714
|
const config = modeConfig[this.executionMode];
|
|
@@ -541,6 +718,28 @@ export class InteractiveSession {
|
|
|
541
718
|
console.log(` ${config.color(config.icon)} ${styleHelpers.text.bold(config.color(modeName))}`);
|
|
542
719
|
console.log(` ${colors.textDim(` ${config.description}`)}`);
|
|
543
720
|
console.log('');
|
|
721
|
+
|
|
722
|
+
this.showRemoteModelInfo();
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
private showRemoteModelInfo(): void {
|
|
726
|
+
const authConfig = this.configManager.getAuthConfig();
|
|
727
|
+
const isRemote = authConfig.type === AuthType.OAUTH_XAGENT;
|
|
728
|
+
|
|
729
|
+
if (isRemote) {
|
|
730
|
+
const llmModel = authConfig.remote_llmModelName || colors.textMuted('Not set');
|
|
731
|
+
const vlmModel = authConfig.remote_vlmModelName || colors.textMuted('Not set');
|
|
732
|
+
console.log(colors.textMuted(`${icons.brain} Remote Models:`));
|
|
733
|
+
console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('LLM:')} ${llmModel}`);
|
|
734
|
+
console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('VLM:')} ${vlmModel}`);
|
|
735
|
+
} else {
|
|
736
|
+
const modelName = authConfig.modelName || colors.textMuted('Not set');
|
|
737
|
+
const guiSubagentModel = this.configManager.get('guiSubagentModel') || colors.textMuted('Not set');
|
|
738
|
+
console.log(colors.textMuted(`${icons.brain} Local Models:`));
|
|
739
|
+
console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('LLM:')} ${modelName}`);
|
|
740
|
+
console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('VLM:')} ${guiSubagentModel}`);
|
|
741
|
+
}
|
|
742
|
+
console.log('');
|
|
544
743
|
}
|
|
545
744
|
|
|
546
745
|
private async promptLoop(): Promise<void> {
|
|
@@ -554,16 +753,13 @@ export class InteractiveSession {
|
|
|
554
753
|
this.rl.close();
|
|
555
754
|
}
|
|
556
755
|
|
|
557
|
-
//
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
}
|
|
561
|
-
process.stdin.resume();
|
|
562
|
-
readline.emitKeypressEvents(process.stdin);
|
|
756
|
+
// Ensure TTY is in proper state for input handling
|
|
757
|
+
// This handles any state left by @clack/prompts or other interactions
|
|
758
|
+
ensureTtySane();
|
|
563
759
|
|
|
564
760
|
this.rl = readline.createInterface({
|
|
565
761
|
input: process.stdin,
|
|
566
|
-
output: process.stdout
|
|
762
|
+
output: process.stdout,
|
|
567
763
|
});
|
|
568
764
|
|
|
569
765
|
const prompt = `${colors.primaryBright('❯')} `;
|
|
@@ -592,7 +788,8 @@ export class InteractiveSession {
|
|
|
592
788
|
if (trimmedInput.startsWith('/')) {
|
|
593
789
|
const handled = await this.slashCommandHandler.handleCommand(trimmedInput);
|
|
594
790
|
if (handled) {
|
|
595
|
-
this.executionMode =
|
|
791
|
+
this.executionMode =
|
|
792
|
+
this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
|
|
596
793
|
// Sync conversation history to slashCommandHandler
|
|
597
794
|
this.slashCommandHandler.setConversationHistory(this.conversation);
|
|
598
795
|
}
|
|
@@ -622,7 +819,9 @@ export class InteractiveSession {
|
|
|
622
819
|
}
|
|
623
820
|
|
|
624
821
|
console.log('');
|
|
625
|
-
console.log(
|
|
822
|
+
console.log(
|
|
823
|
+
colors.primaryBright(`${icons.robot} Using agent: ${agent.name || agent.agentType}`)
|
|
824
|
+
);
|
|
626
825
|
console.log(colors.border(icons.separator.repeat(40)));
|
|
627
826
|
console.log('');
|
|
628
827
|
|
|
@@ -630,11 +829,11 @@ export class InteractiveSession {
|
|
|
630
829
|
await this.processUserMessage(task, agent);
|
|
631
830
|
}
|
|
632
831
|
|
|
633
|
-
public async processUserMessage(message: string, agent?:
|
|
832
|
+
public async processUserMessage(message: string, agent?: AgentConfig): Promise<void> {
|
|
634
833
|
const inputs = parseInput(message);
|
|
635
|
-
const textInput = inputs.find(i => i.type === 'text');
|
|
636
|
-
const fileInputs = inputs.filter(i => i.type === 'file');
|
|
637
|
-
const commandInput = inputs.find(i => i.type === 'command');
|
|
834
|
+
const textInput = inputs.find((i) => i.type === 'text');
|
|
835
|
+
const fileInputs = inputs.filter((i) => i.type === 'file');
|
|
836
|
+
const commandInput = inputs.find((i) => i.type === 'command');
|
|
638
837
|
|
|
639
838
|
if (commandInput) {
|
|
640
839
|
await this.executeShellCommand(commandInput.content);
|
|
@@ -647,10 +846,16 @@ export class InteractiveSession {
|
|
|
647
846
|
const toolRegistry = getToolRegistry();
|
|
648
847
|
for (const fileInput of fileInputs) {
|
|
649
848
|
try {
|
|
650
|
-
const content = await toolRegistry.execute(
|
|
849
|
+
const content = await toolRegistry.execute(
|
|
850
|
+
'Read',
|
|
851
|
+
{ filePath: fileInput.content },
|
|
852
|
+
this.executionMode
|
|
853
|
+
);
|
|
651
854
|
userContent += `\n\n--- File: ${fileInput.content} ---\n${content}`;
|
|
652
855
|
} catch (error: any) {
|
|
653
|
-
console.log(
|
|
856
|
+
console.log(
|
|
857
|
+
chalk.yellow(`Warning: Failed to read file ${fileInput.content}: ${error.message}`)
|
|
858
|
+
);
|
|
654
859
|
}
|
|
655
860
|
}
|
|
656
861
|
}
|
|
@@ -660,7 +865,7 @@ export class InteractiveSession {
|
|
|
660
865
|
type: 'text' as const,
|
|
661
866
|
content: userContent,
|
|
662
867
|
rawInput: message,
|
|
663
|
-
timestamp: Date.now()
|
|
868
|
+
timestamp: Date.now(),
|
|
664
869
|
};
|
|
665
870
|
await this.sessionManager.addInput(sessionInput);
|
|
666
871
|
|
|
@@ -677,29 +882,23 @@ export class InteractiveSession {
|
|
|
677
882
|
const userMessage: ChatMessage = {
|
|
678
883
|
role: 'user',
|
|
679
884
|
content: userContent,
|
|
680
|
-
timestamp: Date.now()
|
|
885
|
+
timestamp: Date.now(),
|
|
681
886
|
};
|
|
682
887
|
|
|
683
|
-
// Save last user message for recovery after compression
|
|
684
|
-
const lastUserMessage = userMessage;
|
|
685
|
-
|
|
686
888
|
this.conversation.push(userMessage);
|
|
687
889
|
await this.conversationManager.addMessage(userMessage);
|
|
688
890
|
|
|
689
|
-
// Check if context compression is needed
|
|
690
|
-
await this.checkAndCompressContext(lastUserMessage);
|
|
691
|
-
|
|
692
891
|
// Use remote AI client if available (OAuth XAGENT mode)
|
|
693
892
|
const currentSelectedAuthType = this.configManager.get('selectedAuthType');
|
|
694
|
-
logger.debug(
|
|
695
|
-
|
|
696
|
-
|
|
893
|
+
logger.debug(
|
|
894
|
+
`[DEBUG] processUserMessage: remoteAIClient exists=${!!this.remoteAIClient}, selectedAuthType=${currentSelectedAuthType}`
|
|
895
|
+
);
|
|
697
896
|
|
|
698
897
|
if (this.remoteAIClient) {
|
|
699
|
-
logger.debug('[DEBUG
|
|
898
|
+
logger.debug('[DEBUG] Using generateRemoteResponse (remote mode)');
|
|
700
899
|
await this.generateRemoteResponse(thinkingTokens);
|
|
701
900
|
} else {
|
|
702
|
-
logger.debug('[DEBUG
|
|
901
|
+
logger.debug('[DEBUG] Using generateResponse (local mode)');
|
|
703
902
|
await this.generateResponse(thinkingTokens);
|
|
704
903
|
}
|
|
705
904
|
}
|
|
@@ -709,7 +908,9 @@ export class InteractiveSession {
|
|
|
709
908
|
const thinkingConfig = this.configManager.getThinkingConfig();
|
|
710
909
|
const displayMode = thinkingConfig.displayMode || 'compact';
|
|
711
910
|
|
|
712
|
-
const separator = icons.separator.repeat(
|
|
911
|
+
const separator = icons.separator.repeat(
|
|
912
|
+
Math.min(60, process.stdout.columns || 80) - indent.length
|
|
913
|
+
);
|
|
713
914
|
|
|
714
915
|
console.log('');
|
|
715
916
|
console.log(`${indent}${colors.border(separator)}`);
|
|
@@ -725,9 +926,10 @@ export class InteractiveSession {
|
|
|
725
926
|
case 'compact':
|
|
726
927
|
// Compact display, truncate partial content
|
|
727
928
|
const maxLength = 500;
|
|
728
|
-
const truncatedContent =
|
|
729
|
-
|
|
730
|
-
|
|
929
|
+
const truncatedContent =
|
|
930
|
+
reasoningContent.length > maxLength
|
|
931
|
+
? reasoningContent.substring(0, maxLength) + '... (truncated)'
|
|
932
|
+
: reasoningContent;
|
|
731
933
|
|
|
732
934
|
console.log(`${indent}${colors.textDim(`${icons.brain} Thinking Process:`)}`);
|
|
733
935
|
console.log('');
|
|
@@ -738,7 +940,9 @@ export class InteractiveSession {
|
|
|
738
940
|
case 'indicator':
|
|
739
941
|
// Show indicator only
|
|
740
942
|
console.log(`${indent}${colors.textDim(`${icons.brain} Thinking process completed`)}`);
|
|
741
|
-
console.log(
|
|
943
|
+
console.log(
|
|
944
|
+
`${indent}${colors.textDim(`[${reasoningContent.length} chars of reasoning]`)}`
|
|
945
|
+
);
|
|
742
946
|
break;
|
|
743
947
|
|
|
744
948
|
default:
|
|
@@ -754,70 +958,107 @@ export class InteractiveSession {
|
|
|
754
958
|
/**
|
|
755
959
|
* Check and compress conversation context
|
|
756
960
|
*/
|
|
757
|
-
private async checkAndCompressContext(
|
|
961
|
+
private async checkAndCompressContext(): Promise<void> {
|
|
758
962
|
const compressionConfig = this.configManager.getContextCompressionConfig();
|
|
759
963
|
|
|
760
964
|
if (!compressionConfig.enabled) {
|
|
761
965
|
return;
|
|
762
966
|
}
|
|
763
967
|
|
|
764
|
-
const
|
|
968
|
+
const indent = this.getIndent();
|
|
969
|
+
const currentTokens = this.contextCompressor.estimateContextTokens(this.conversation);
|
|
970
|
+
const currentMessages = this.conversation.length;
|
|
971
|
+
const { needsCompression, reason, tokenCount } = this.contextCompressor.needsCompression(
|
|
765
972
|
this.conversation,
|
|
766
973
|
compressionConfig
|
|
767
974
|
);
|
|
768
975
|
|
|
769
|
-
if (needsCompression) {
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
console.log(`${indent}${colors.warning(`${icons.brain} Context compression triggered: ${reason}`)}`);
|
|
976
|
+
if (!needsCompression) {
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
773
979
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
const result: CompressionResult = await this.contextCompressor.compressContext(
|
|
780
|
-
this.conversation,
|
|
781
|
-
enhancedSystemPrompt,
|
|
782
|
-
compressionConfig
|
|
783
|
-
);
|
|
980
|
+
// Extract threshold and contextWindow from reason
|
|
981
|
+
const thresholdMatch = reason.match(/budget\s*\((\d+)/);
|
|
982
|
+
const contextWindowMatch = reason.match(/contextWindow:\s*(\d+)/);
|
|
983
|
+
const threshold = thresholdMatch ? parseInt(thresholdMatch[1], 10) : 0;
|
|
984
|
+
const contextWindow = contextWindowMatch ? parseInt(contextWindowMatch[1], 10) : 0;
|
|
784
985
|
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
986
|
+
console.log('');
|
|
987
|
+
console.log(
|
|
988
|
+
`${indent}${colors.success(`${icons.sparkles} Compressing context (${currentMessages} msgs, ${tokenCount.toLocaleString()} > ${threshold.toLocaleString()}/${contextWindow.toLocaleString()} tokens, ${Math.round((tokenCount / contextWindow) * 100)}% of context window)...`)}`
|
|
989
|
+
);
|
|
789
990
|
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
const isTruncated = summaryContent.length > maxPreviewLength;
|
|
991
|
+
const toolRegistry = getToolRegistry();
|
|
992
|
+
const baseSystemPrompt = this.currentAgent?.systemPrompt || 'You are a helpful AI assistant.';
|
|
993
|
+
const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode);
|
|
994
|
+
const enhancedSystemPrompt =
|
|
995
|
+
await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
|
|
796
996
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
997
|
+
const result: CompressionResult = await this.contextCompressor.compressContext(
|
|
998
|
+
this.conversation,
|
|
999
|
+
enhancedSystemPrompt,
|
|
1000
|
+
compressionConfig
|
|
1001
|
+
);
|
|
800
1002
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
1003
|
+
if (result.wasCompressed) {
|
|
1004
|
+
this.conversation = result.compressedMessages;
|
|
1005
|
+
const reductionPercent = Math.round((1 - result.compressedSize / result.originalSize) * 100);
|
|
1006
|
+
console.log(
|
|
1007
|
+
`${indent}${colors.success(`${icons.success} Compressed ${result.originalMessageCount} → ${result.compressedMessageCount} messages (${reductionPercent}% smaller)`)}`
|
|
1008
|
+
);
|
|
1009
|
+
|
|
1010
|
+
// Summary is embedded in first user message, look for it
|
|
1011
|
+
// The format is: "[Conversation Summary - X messages compressed]\n\n${summary}"
|
|
1012
|
+
let summaryMessage: ChatMessage | undefined = result.compressedMessages.find(
|
|
1013
|
+
(m) => m.role === 'user' && m.content.includes('[Conversation Summary')
|
|
1014
|
+
);
|
|
1015
|
+
|
|
1016
|
+
if (summaryMessage) {
|
|
1017
|
+
// Extract summary content after the header
|
|
1018
|
+
const match = summaryMessage.content.match(/\[Conversation Summary.*?\]:\n\n(.+)/s);
|
|
1019
|
+
if (match) {
|
|
1020
|
+
summaryMessage = {
|
|
1021
|
+
role: 'assistant',
|
|
1022
|
+
content: match[1],
|
|
1023
|
+
timestamp: summaryMessage.timestamp,
|
|
1024
|
+
};
|
|
811
1025
|
}
|
|
1026
|
+
}
|
|
812
1027
|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
1028
|
+
if (summaryMessage && summaryMessage.content) {
|
|
1029
|
+
const maxPreviewLength = 800;
|
|
1030
|
+
let summaryContent = summaryMessage.content;
|
|
1031
|
+
const isTruncated = summaryContent.length > maxPreviewLength;
|
|
1032
|
+
|
|
1033
|
+
if (isTruncated) {
|
|
1034
|
+
summaryContent = summaryContent.substring(0, maxPreviewLength) + '\n...';
|
|
816
1035
|
}
|
|
817
1036
|
|
|
818
|
-
|
|
819
|
-
|
|
1037
|
+
console.log('');
|
|
1038
|
+
console.log(
|
|
1039
|
+
`${indent}${theme.predefinedStyles.title(`${icons.sparkles} Conversation Summary`)}`
|
|
1040
|
+
);
|
|
1041
|
+
const separator = icons.separator.repeat(
|
|
1042
|
+
Math.min(60, process.stdout.columns || 80) - indent.length * 2
|
|
1043
|
+
);
|
|
1044
|
+
console.log(`${indent}${colors.border(separator)}`);
|
|
1045
|
+
const renderedSummary = renderMarkdown(
|
|
1046
|
+
summaryContent,
|
|
1047
|
+
(process.stdout.columns || 80) - indent.length * 4
|
|
1048
|
+
);
|
|
1049
|
+
console.log(
|
|
1050
|
+
`${indent}${theme.predefinedStyles.dim(renderedSummary).replace(/^/gm, indent)}`
|
|
1051
|
+
);
|
|
1052
|
+
if (isTruncated) {
|
|
1053
|
+
console.log(
|
|
1054
|
+
`${indent}${colors.textMuted(`(... ${summaryMessage.content.length - maxPreviewLength} more chars hidden)`)}`
|
|
1055
|
+
);
|
|
1056
|
+
}
|
|
1057
|
+
console.log(`${indent}${colors.border(separator)}`);
|
|
820
1058
|
}
|
|
1059
|
+
|
|
1060
|
+
// Sync compressed conversation history to slashCommandHandler
|
|
1061
|
+
this.slashCommandHandler.setConversationHistory(this.conversation);
|
|
821
1062
|
}
|
|
822
1063
|
}
|
|
823
1064
|
|
|
@@ -826,7 +1067,9 @@ export class InteractiveSession {
|
|
|
826
1067
|
console.log('');
|
|
827
1068
|
console.log(`${indent}${colors.textMuted(`${icons.code} Executing:`)}`);
|
|
828
1069
|
console.log(`${indent}${colors.codeText(` $ ${command}`)}`);
|
|
829
|
-
console.log(
|
|
1070
|
+
console.log(
|
|
1071
|
+
`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`
|
|
1072
|
+
);
|
|
830
1073
|
console.log('');
|
|
831
1074
|
|
|
832
1075
|
const toolRegistry = getToolRegistry();
|
|
@@ -846,17 +1089,17 @@ export class InteractiveSession {
|
|
|
846
1089
|
tool: 'Bash',
|
|
847
1090
|
params: { command },
|
|
848
1091
|
result,
|
|
849
|
-
timestamp: Date.now()
|
|
1092
|
+
timestamp: Date.now(),
|
|
850
1093
|
};
|
|
851
1094
|
|
|
852
|
-
this.
|
|
1095
|
+
this.tool_calls.push(toolCall);
|
|
853
1096
|
|
|
854
1097
|
// Record command execution to session manager
|
|
855
1098
|
await this.sessionManager.addInput({
|
|
856
1099
|
type: 'command',
|
|
857
1100
|
content: command,
|
|
858
1101
|
rawInput: command,
|
|
859
|
-
timestamp: Date.now()
|
|
1102
|
+
timestamp: Date.now(),
|
|
860
1103
|
});
|
|
861
1104
|
|
|
862
1105
|
await this.sessionManager.addOutput({
|
|
@@ -865,7 +1108,7 @@ export class InteractiveSession {
|
|
|
865
1108
|
toolName: 'Bash',
|
|
866
1109
|
toolParams: { command },
|
|
867
1110
|
toolResult: result,
|
|
868
|
-
timestamp: Date.now()
|
|
1111
|
+
timestamp: Date.now(),
|
|
869
1112
|
});
|
|
870
1113
|
} catch (error: any) {
|
|
871
1114
|
console.log(`${indent}${colors.error(`Command execution failed: ${error.message}`)}`);
|
|
@@ -894,10 +1137,22 @@ export class InteractiveSession {
|
|
|
894
1137
|
*/
|
|
895
1138
|
private createRemoteCaller(taskId: string, status: 'begin' | 'continue') {
|
|
896
1139
|
const client = this.remoteAIClient!;
|
|
1140
|
+
|
|
1141
|
+
|
|
897
1142
|
return {
|
|
898
|
-
chatCompletion: (messages: ChatMessage[], options: any) =>
|
|
899
|
-
|
|
900
|
-
|
|
1143
|
+
chatCompletion: (messages: ChatMessage[], options: any) => {
|
|
1144
|
+
// Must fetch authConfig inside the closure, otherwise it captures stale config
|
|
1145
|
+
const authConfig = this.configManager.getAuthConfig();
|
|
1146
|
+
logger.debug(`[DEBUG] createRemoteCaller: llmModelName=${authConfig.remote_llmModelName}, vlmModelName=${authConfig.remote_vlmModelName}`);
|
|
1147
|
+
return client.chatCompletion(messages, {
|
|
1148
|
+
...options,
|
|
1149
|
+
taskId,
|
|
1150
|
+
status: options.isFirstApiCall ? 'begin' : 'continue',
|
|
1151
|
+
llmModelName: authConfig.remote_llmModelName,
|
|
1152
|
+
vlmModelName: authConfig.remote_vlmModelName
|
|
1153
|
+
});
|
|
1154
|
+
},
|
|
1155
|
+
isRemote: true,
|
|
901
1156
|
};
|
|
902
1157
|
}
|
|
903
1158
|
|
|
@@ -907,39 +1162,32 @@ export class InteractiveSession {
|
|
|
907
1162
|
private createLocalCaller() {
|
|
908
1163
|
const client = this.aiClient!;
|
|
909
1164
|
return {
|
|
910
|
-
chatCompletion: (messages: ChatMessage[], options: any) =>
|
|
1165
|
+
chatCompletion: (messages: ChatMessage[], options: any) =>
|
|
911
1166
|
client.chatCompletion(messages as any, options),
|
|
912
|
-
isRemote: false
|
|
1167
|
+
isRemote: false,
|
|
913
1168
|
};
|
|
914
1169
|
}
|
|
915
1170
|
|
|
916
|
-
private async generateResponse(
|
|
1171
|
+
private async generateResponse(
|
|
1172
|
+
thinkingTokens: number = 0,
|
|
1173
|
+
_customAIClient?: AIClient,
|
|
1174
|
+
existingTaskId?: string
|
|
1175
|
+
): Promise<void> {
|
|
917
1176
|
// Use existing taskId or create new one for this user interaction
|
|
918
1177
|
// If taskId already exists (e.g., from tool calls), reuse it
|
|
919
1178
|
const taskId = existingTaskId || this.currentTaskId || crypto.randomUUID();
|
|
920
1179
|
this.currentTaskId = taskId;
|
|
921
|
-
this.isFirstApiCall = true;
|
|
922
1180
|
|
|
923
|
-
//
|
|
924
|
-
|
|
1181
|
+
// isFirstApiCall is reset in generateRemoteResponse for new tasks
|
|
1182
|
+
// For continuation calls (existingTaskId provided), keep previous value
|
|
925
1183
|
|
|
926
|
-
// Use
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
// Custom client (used by remote mode) - pass taskId and status
|
|
932
|
-
chatCompletion = (messages: ChatMessage[], options: any) =>
|
|
933
|
-
customAIClient.chatCompletion(messages as any, { ...options, taskId, status });
|
|
934
|
-
isRemote = true;
|
|
935
|
-
} else {
|
|
936
|
-
// Use unified LLM Caller with taskId (automatically selects local or remote mode)
|
|
937
|
-
const caller = this.createLLMCaller(taskId, status);
|
|
938
|
-
chatCompletion = caller.chatCompletion;
|
|
939
|
-
isRemote = caller.isRemote;
|
|
940
|
-
}
|
|
1184
|
+
// Use unified LLM Caller with taskId (automatically selects local or remote mode)
|
|
1185
|
+
const status: 'begin' | 'continue' = this.isFirstApiCall ? 'begin' : 'continue';
|
|
1186
|
+
const caller = this.createLLMCaller(taskId, status);
|
|
1187
|
+
const chatCompletion = caller.chatCompletion;
|
|
1188
|
+
const isRemote = caller.isRemote;
|
|
941
1189
|
|
|
942
|
-
if (!isRemote && !this.aiClient
|
|
1190
|
+
if (!isRemote && !this.aiClient) {
|
|
943
1191
|
console.log(colors.error('AI client not initialized'));
|
|
944
1192
|
return;
|
|
945
1193
|
}
|
|
@@ -971,17 +1219,28 @@ export class InteractiveSession {
|
|
|
971
1219
|
const toolDefinitions = toolRegistry.getToolDefinitions();
|
|
972
1220
|
|
|
973
1221
|
// Available tools for this session
|
|
974
|
-
const availableTools =
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1222
|
+
const availableTools =
|
|
1223
|
+
this.executionMode !== ExecutionMode.DEFAULT && allowedToolNames.length > 0
|
|
1224
|
+
? toolDefinitions.filter(
|
|
1225
|
+
(tool): tool is { function: { name: string } } =>
|
|
1226
|
+
typeof tool.function?.name === 'string' &&
|
|
1227
|
+
allowedToolNames.includes(tool.function.name)
|
|
1228
|
+
)
|
|
1229
|
+
: toolDefinitions;
|
|
1230
|
+
|
|
1231
|
+
const baseSystemPrompt = this.currentAgent?.systemPrompt ?? '';
|
|
1232
|
+
const systemPromptGenerator = new SystemPromptGenerator(
|
|
1233
|
+
toolRegistry,
|
|
1234
|
+
this.executionMode,
|
|
1235
|
+
undefined,
|
|
1236
|
+
this.mcpManager
|
|
1237
|
+
);
|
|
1238
|
+
const enhancedSystemPrompt =
|
|
1239
|
+
await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
|
|
981
1240
|
|
|
982
1241
|
const messages: ChatMessage[] = [
|
|
983
1242
|
{ role: 'system', content: `${enhancedSystemPrompt}\n\n${memory}`, timestamp: Date.now() },
|
|
984
|
-
...this.conversation
|
|
1243
|
+
...this.conversation,
|
|
985
1244
|
];
|
|
986
1245
|
|
|
987
1246
|
const operationId = `ai-response-${Date.now()}`;
|
|
@@ -989,7 +1248,8 @@ export class InteractiveSession {
|
|
|
989
1248
|
chatCompletion(messages, {
|
|
990
1249
|
tools: availableTools,
|
|
991
1250
|
toolChoice: availableTools.length > 0 ? 'auto' : 'none',
|
|
992
|
-
thinkingTokens
|
|
1251
|
+
thinkingTokens,
|
|
1252
|
+
isFirstApiCall: this.isFirstApiCall,
|
|
993
1253
|
}),
|
|
994
1254
|
operationId
|
|
995
1255
|
);
|
|
@@ -1002,9 +1262,7 @@ export class InteractiveSession {
|
|
|
1002
1262
|
|
|
1003
1263
|
const assistantMessage = response.choices[0].message;
|
|
1004
1264
|
|
|
1005
|
-
const content = typeof assistantMessage.content === 'string'
|
|
1006
|
-
? assistantMessage.content
|
|
1007
|
-
: '';
|
|
1265
|
+
const content = typeof assistantMessage.content === 'string' ? assistantMessage.content : '';
|
|
1008
1266
|
const reasoningContent = assistantMessage.reasoning_content || '';
|
|
1009
1267
|
// Display reasoning content if available and thinking mode is enabled
|
|
1010
1268
|
if (reasoningContent && this.configManager.getThinkingConfig().enabled) {
|
|
@@ -1013,9 +1271,14 @@ export class InteractiveSession {
|
|
|
1013
1271
|
|
|
1014
1272
|
console.log('');
|
|
1015
1273
|
console.log(`${indent}${colors.primaryBright(`${icons.robot} Assistant:`)}`);
|
|
1016
|
-
console.log(
|
|
1274
|
+
console.log(
|
|
1275
|
+
`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`
|
|
1276
|
+
);
|
|
1017
1277
|
console.log('');
|
|
1018
|
-
const renderedContent = renderMarkdown(
|
|
1278
|
+
const renderedContent = renderMarkdown(
|
|
1279
|
+
content,
|
|
1280
|
+
(process.stdout.columns || 80) - indent.length * 2
|
|
1281
|
+
);
|
|
1019
1282
|
console.log(`${indent}${renderedContent.replace(/^/gm, indent)}`);
|
|
1020
1283
|
console.log('');
|
|
1021
1284
|
|
|
@@ -1023,8 +1286,8 @@ export class InteractiveSession {
|
|
|
1023
1286
|
role: 'assistant',
|
|
1024
1287
|
content,
|
|
1025
1288
|
timestamp: Date.now(),
|
|
1026
|
-
reasoningContent,
|
|
1027
|
-
|
|
1289
|
+
reasoning_content: reasoningContent,
|
|
1290
|
+
tool_calls: assistantMessage.tool_calls,
|
|
1028
1291
|
});
|
|
1029
1292
|
|
|
1030
1293
|
// Record output to session manager
|
|
@@ -1032,19 +1295,21 @@ export class InteractiveSession {
|
|
|
1032
1295
|
role: 'assistant',
|
|
1033
1296
|
content,
|
|
1034
1297
|
timestamp: Date.now(),
|
|
1035
|
-
reasoningContent,
|
|
1036
|
-
|
|
1298
|
+
reasoning_content: reasoningContent,
|
|
1299
|
+
tool_calls: assistantMessage.tool_calls,
|
|
1037
1300
|
});
|
|
1038
1301
|
|
|
1039
1302
|
if (assistantMessage.tool_calls) {
|
|
1040
|
-
await this.handleToolCalls(assistantMessage.tool_calls);
|
|
1303
|
+
await this.handleToolCalls(assistantMessage.tool_calls as unknown as import('./types.js').ToolCallItem[]);
|
|
1304
|
+
} else {
|
|
1305
|
+
await this.checkAndCompressContext();
|
|
1041
1306
|
}
|
|
1042
1307
|
|
|
1043
1308
|
if (this.checkpointManager.isEnabled()) {
|
|
1044
1309
|
await this.checkpointManager.createCheckpoint(
|
|
1045
1310
|
`Response generated at ${new Date().toLocaleString()}`,
|
|
1046
1311
|
[...this.conversation],
|
|
1047
|
-
[...this.
|
|
1312
|
+
[...this.tool_calls]
|
|
1048
1313
|
);
|
|
1049
1314
|
}
|
|
1050
1315
|
|
|
@@ -1058,17 +1323,22 @@ export class InteractiveSession {
|
|
|
1058
1323
|
(this as any)._isOperationInProgress = false;
|
|
1059
1324
|
|
|
1060
1325
|
if (error.message === 'Operation cancelled by user') {
|
|
1061
|
-
//
|
|
1326
|
+
// Notify backend to cancel the task
|
|
1062
1327
|
if (this.remoteAIClient && this.currentTaskId) {
|
|
1063
|
-
await this.remoteAIClient.cancelTask(this.currentTaskId).catch(() => {});
|
|
1328
|
+
await this.remoteAIClient.cancelTask?.(this.currentTaskId).catch(() => {});
|
|
1064
1329
|
}
|
|
1065
1330
|
return;
|
|
1066
1331
|
}
|
|
1067
1332
|
|
|
1068
|
-
//
|
|
1069
|
-
|
|
1333
|
+
// Distinguish error types: timeout vs other failures
|
|
1334
|
+
const isTimeout = error.message.includes('timeout') || error.message.includes('Timeout');
|
|
1335
|
+
const failureReason = isTimeout ? 'timeout' : 'failure';
|
|
1336
|
+
|
|
1337
|
+
logger.debug(
|
|
1338
|
+
`[Session] Task failed: taskId=${this.currentTaskId}, error: ${error.message}, reason: ${failureReason}`
|
|
1339
|
+
);
|
|
1070
1340
|
if (this.remoteAIClient && this.currentTaskId) {
|
|
1071
|
-
await this.remoteAIClient.
|
|
1341
|
+
await this.remoteAIClient.failTask?.(this.currentTaskId, failureReason).catch(() => {});
|
|
1072
1342
|
}
|
|
1073
1343
|
|
|
1074
1344
|
console.log(colors.error(`Error: ${error.message}`));
|
|
@@ -1082,20 +1352,24 @@ export class InteractiveSession {
|
|
|
1082
1352
|
* @param thinkingTokens - Optional thinking tokens config
|
|
1083
1353
|
* @param existingTaskId - Optional existing taskId to reuse (for tool call continuation)
|
|
1084
1354
|
*/
|
|
1085
|
-
private async generateRemoteResponse(
|
|
1355
|
+
private async generateRemoteResponse(
|
|
1356
|
+
thinkingTokens: number = 0,
|
|
1357
|
+
existingTaskId?: string
|
|
1358
|
+
): Promise<void> {
|
|
1086
1359
|
// Reuse existing taskId or create new one for this user interaction
|
|
1087
1360
|
const taskId = existingTaskId || crypto.randomUUID();
|
|
1088
1361
|
this.currentTaskId = taskId;
|
|
1089
|
-
logger.debug(
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
if (!existingTaskId) {
|
|
1093
|
-
this.isFirstApiCall = true;
|
|
1094
|
-
}
|
|
1362
|
+
logger.debug(
|
|
1363
|
+
`[Session] generateRemoteResponse: taskId=${taskId}, existingTaskId=${!!existingTaskId}`
|
|
1364
|
+
);
|
|
1095
1365
|
|
|
1096
|
-
//
|
|
1097
|
-
|
|
1098
|
-
|
|
1366
|
+
// Each new user message is a fresh task - always set isFirstApiCall = true
|
|
1367
|
+
// This ensures status is 'begin' for every user message
|
|
1368
|
+
this.isFirstApiCall = true;
|
|
1369
|
+
const status: 'begin' | 'continue' = 'begin';
|
|
1370
|
+
logger.debug(
|
|
1371
|
+
`[Session] Status for this call: ${status}, isFirstApiCall=${this.isFirstApiCall}`
|
|
1372
|
+
);
|
|
1099
1373
|
|
|
1100
1374
|
// Check if remote client is available
|
|
1101
1375
|
if (!this.remoteAIClient) {
|
|
@@ -1104,20 +1378,24 @@ export class InteractiveSession {
|
|
|
1104
1378
|
}
|
|
1105
1379
|
|
|
1106
1380
|
try {
|
|
1107
|
-
//
|
|
1108
|
-
|
|
1381
|
+
// Use unified generateResponse without passing customAIClient,
|
|
1382
|
+
// let createLLMCaller handle remote/local selection
|
|
1383
|
+
await this.generateResponse(thinkingTokens, undefined, taskId);
|
|
1109
1384
|
|
|
1110
1385
|
// Mark task as completed (发送 status: 'end')
|
|
1111
1386
|
logger.debug(`[Session] Task completed: taskId=${this.currentTaskId}`);
|
|
1112
|
-
if (this.currentTaskId) {
|
|
1113
|
-
await this.remoteAIClient.completeTask(this.currentTaskId);
|
|
1387
|
+
if (this.remoteAIClient && this.currentTaskId) {
|
|
1388
|
+
await this.remoteAIClient.completeTask?.(this.currentTaskId);
|
|
1114
1389
|
}
|
|
1115
|
-
|
|
1116
1390
|
} catch (error: any) {
|
|
1117
1391
|
// Clear the operation flag
|
|
1118
1392
|
(this as any)._isOperationInProgress = false;
|
|
1119
1393
|
|
|
1120
1394
|
if (error.message === 'Operation cancelled by user') {
|
|
1395
|
+
// Notify backend to cancel the task
|
|
1396
|
+
if (this.remoteAIClient && this.currentTaskId) {
|
|
1397
|
+
await this.remoteAIClient.cancelTask?.(this.currentTaskId).catch(() => {});
|
|
1398
|
+
}
|
|
1121
1399
|
return;
|
|
1122
1400
|
}
|
|
1123
1401
|
|
|
@@ -1129,28 +1407,29 @@ export class InteractiveSession {
|
|
|
1129
1407
|
console.log('');
|
|
1130
1408
|
|
|
1131
1409
|
// Clear invalid credentials and persist
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1410
|
+
this.configManager.set('apiKey', '');
|
|
1411
|
+
this.configManager.set('refreshToken', '');
|
|
1412
|
+
this.configManager.save('global');
|
|
1135
1413
|
|
|
1136
|
-
logger.debug(
|
|
1414
|
+
logger.debug(
|
|
1415
|
+
'[DEBUG generateRemoteResponse] Cleared invalid credentials, starting re-authentication...'
|
|
1416
|
+
);
|
|
1137
1417
|
|
|
1138
1418
|
// Re-authenticate
|
|
1139
1419
|
await this.setupAuthentication();
|
|
1140
1420
|
|
|
1141
1421
|
// Reload config to ensure we have the latest authConfig
|
|
1142
|
-
|
|
1143
|
-
await this.configManager.load();
|
|
1422
|
+
this.configManager.load();
|
|
1144
1423
|
const authConfig = this.configManager.getAuthConfig();
|
|
1145
1424
|
|
|
1146
1425
|
logger.debug('[DEBUG generateRemoteResponse] After re-auth:');
|
|
1147
1426
|
logger.debug(' - authConfig.apiKey exists:', !!authConfig.apiKey ? 'true' : 'false');
|
|
1148
1427
|
|
|
1149
|
-
// Recreate readline interface after
|
|
1428
|
+
// Recreate readline interface after interactive prompt
|
|
1150
1429
|
this.rl.close();
|
|
1151
1430
|
this.rl = readline.createInterface({
|
|
1152
1431
|
input: process.stdin,
|
|
1153
|
-
output: process.stdout
|
|
1432
|
+
output: process.stdout,
|
|
1154
1433
|
});
|
|
1155
1434
|
this.rl.on('close', () => {
|
|
1156
1435
|
logger.debug('DEBUG: readline interface closed');
|
|
@@ -1158,13 +1437,19 @@ export class InteractiveSession {
|
|
|
1158
1437
|
|
|
1159
1438
|
// Reinitialize RemoteAIClient with new token
|
|
1160
1439
|
if (authConfig.apiKey) {
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1440
|
+
logger.debug(
|
|
1441
|
+
'[DEBUG generateRemoteResponse] Reinitializing RemoteAIClient with new token'
|
|
1442
|
+
);
|
|
1443
|
+
this.remoteAIClient = createAIClient(authConfig);
|
|
1164
1444
|
} else {
|
|
1165
|
-
logger.debug(
|
|
1445
|
+
logger.debug(
|
|
1446
|
+
'[DEBUG generateRemoteResponse] WARNING: No apiKey after re-authentication!'
|
|
1447
|
+
);
|
|
1166
1448
|
}
|
|
1167
1449
|
|
|
1450
|
+
// Sync remoteAIClient reference to slashCommandHandler for /provider command
|
|
1451
|
+
this.slashCommandHandler.setRemoteAIClient(this.remoteAIClient);
|
|
1452
|
+
|
|
1168
1453
|
// Retry the current operation
|
|
1169
1454
|
console.log('');
|
|
1170
1455
|
console.log(colors.info('Retrying with new authentication...'));
|
|
@@ -1172,10 +1457,15 @@ export class InteractiveSession {
|
|
|
1172
1457
|
return this.generateRemoteResponse(thinkingTokens);
|
|
1173
1458
|
}
|
|
1174
1459
|
|
|
1175
|
-
//
|
|
1176
|
-
|
|
1460
|
+
// Distinguish error types: timeout vs other failures
|
|
1461
|
+
const isTimeout = error.message.includes('timeout') || error.message.includes('Timeout');
|
|
1462
|
+
const failureReason = isTimeout ? 'timeout' : 'failure';
|
|
1463
|
+
|
|
1464
|
+
logger.debug(
|
|
1465
|
+
`[Session] Task failed: taskId=${this.currentTaskId}, error: ${error.message}, reason: ${failureReason}`
|
|
1466
|
+
);
|
|
1177
1467
|
if (this.remoteAIClient && this.currentTaskId) {
|
|
1178
|
-
await this.remoteAIClient.
|
|
1468
|
+
await this.remoteAIClient.failTask?.(this.currentTaskId, failureReason).catch(() => {});
|
|
1179
1469
|
}
|
|
1180
1470
|
|
|
1181
1471
|
console.log(colors.error(`Error: ${error.message}`));
|
|
@@ -1183,7 +1473,10 @@ export class InteractiveSession {
|
|
|
1183
1473
|
}
|
|
1184
1474
|
}
|
|
1185
1475
|
|
|
1186
|
-
private async handleToolCalls(
|
|
1476
|
+
private async handleToolCalls(
|
|
1477
|
+
toolCalls: ToolCallItem[],
|
|
1478
|
+
onComplete?: () => Promise<void>
|
|
1479
|
+
): Promise<void> {
|
|
1187
1480
|
// Mark that tool execution is in progress
|
|
1188
1481
|
(this as any)._isOperationInProgress = true;
|
|
1189
1482
|
|
|
@@ -1219,20 +1512,45 @@ export class InteractiveSession {
|
|
|
1219
1512
|
|
|
1220
1513
|
// Execute all tools in parallel
|
|
1221
1514
|
const results = await toolRegistry.executeAll(
|
|
1222
|
-
preparedToolCalls.map(tc => ({ name: tc.name, params: tc.params })),
|
|
1515
|
+
preparedToolCalls.map((tc) => ({ name: tc.name, params: tc.params })),
|
|
1223
1516
|
this.executionMode
|
|
1224
1517
|
);
|
|
1225
1518
|
|
|
1226
|
-
//
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1519
|
+
// Create a map to store results by tool call index to maintain original order
|
|
1520
|
+
const resultsByIndex = new Map<number, { tool: string; result: any; error?: string }>();
|
|
1521
|
+
const usedIndices = new Set<number>();
|
|
1522
|
+
|
|
1523
|
+
for (const result of results) {
|
|
1524
|
+
// Find the first unused original index in preparedToolCalls that matches the tool name
|
|
1525
|
+
const originalIndex = preparedToolCalls.findIndex((tc, idx) =>
|
|
1526
|
+
tc.name === result.tool && !usedIndices.has(idx)
|
|
1527
|
+
);
|
|
1528
|
+
if (originalIndex !== -1) {
|
|
1529
|
+
usedIndices.add(originalIndex);
|
|
1530
|
+
resultsByIndex.set(originalIndex, result);
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1231
1533
|
|
|
1232
|
-
|
|
1534
|
+
// Process results in the original tool_calls order (critical for Anthropic format APIs)
|
|
1535
|
+
let hasError = false;
|
|
1536
|
+
for (let i = 0; i < preparedToolCalls.length; i++) {
|
|
1537
|
+
const toolCall = preparedToolCalls[i];
|
|
1538
|
+
const { name: tool, params } = toolCall;
|
|
1539
|
+
|
|
1540
|
+
const resultData = resultsByIndex.get(i);
|
|
1541
|
+
const result = resultData?.result;
|
|
1542
|
+
const error = resultData?.error;
|
|
1233
1543
|
|
|
1234
1544
|
if (error) {
|
|
1235
1545
|
if (error === 'Operation cancelled by user') {
|
|
1546
|
+
// Notify backend to cancel the task
|
|
1547
|
+
if (this.remoteAIClient && this.currentTaskId) {
|
|
1548
|
+
await this.remoteAIClient.cancelTask?.(this.currentTaskId).catch(() => {});
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
// 清理 conversation 中未完成的 tool_call
|
|
1552
|
+
this.cleanupIncompleteToolCalls();
|
|
1553
|
+
|
|
1236
1554
|
(this as any)._isOperationInProgress = false;
|
|
1237
1555
|
return;
|
|
1238
1556
|
}
|
|
@@ -1242,16 +1560,16 @@ export class InteractiveSession {
|
|
|
1242
1560
|
console.log('');
|
|
1243
1561
|
console.log(`${indent}${colors.error(`${icons.cross} Tool Error: ${tool} - ${error}`)}`);
|
|
1244
1562
|
|
|
1245
|
-
//
|
|
1563
|
+
// Add detailed error info including tool name and params for AI understanding and correction
|
|
1246
1564
|
this.conversation.push({
|
|
1247
1565
|
role: 'tool',
|
|
1248
1566
|
content: JSON.stringify({
|
|
1249
1567
|
name: tool,
|
|
1250
1568
|
parameters: params,
|
|
1251
|
-
error: error
|
|
1569
|
+
error: error,
|
|
1252
1570
|
}),
|
|
1253
1571
|
tool_call_id: toolCall.id,
|
|
1254
|
-
timestamp: Date.now()
|
|
1572
|
+
timestamp: Date.now(),
|
|
1255
1573
|
});
|
|
1256
1574
|
} else {
|
|
1257
1575
|
// Use correct indent for gui-subagent tasks
|
|
@@ -1294,7 +1612,10 @@ export class InteractiveSession {
|
|
|
1294
1612
|
// Show edit result with diff
|
|
1295
1613
|
console.log('');
|
|
1296
1614
|
const diffOutput = renderDiff(result.diff);
|
|
1297
|
-
const indentedDiff = diffOutput
|
|
1615
|
+
const indentedDiff = diffOutput
|
|
1616
|
+
.split('\n')
|
|
1617
|
+
.map((line) => `${displayIndent} ${line}`)
|
|
1618
|
+
.join('\n');
|
|
1298
1619
|
console.log(`${indentedDiff}`);
|
|
1299
1620
|
} else if (hasFilePreview) {
|
|
1300
1621
|
// Show new file content in diff-like style
|
|
@@ -1306,24 +1627,34 @@ export class InteractiveSession {
|
|
|
1306
1627
|
} else if (hasDeleteInfo) {
|
|
1307
1628
|
// Show DeleteFile result
|
|
1308
1629
|
console.log('');
|
|
1309
|
-
console.log(
|
|
1630
|
+
console.log(
|
|
1631
|
+
`${displayIndent}${colors.success(`${icons.check} Deleted: ${result.filePath}`)}`
|
|
1632
|
+
);
|
|
1310
1633
|
} else if (isTaskTool) {
|
|
1311
1634
|
// Special handling for task tool (subagent) - show friendly summary
|
|
1312
1635
|
console.log('');
|
|
1313
1636
|
const subagentType = params.subagent_type;
|
|
1314
|
-
const subagentName =
|
|
1637
|
+
const subagentName =
|
|
1638
|
+
params.description ||
|
|
1639
|
+
(params.prompt ? params.prompt.substring(0, 50).replace(/\n/g, ' ') : 'Unknown task');
|
|
1315
1640
|
|
|
1316
1641
|
if (result?.success) {
|
|
1317
|
-
console.log(
|
|
1642
|
+
console.log(
|
|
1643
|
+
`${displayIndent}${colors.success(`${icons.check} ${subagentType}: Completed`)}`
|
|
1644
|
+
);
|
|
1318
1645
|
console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
|
|
1319
1646
|
if (result.message) {
|
|
1320
1647
|
console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
|
|
1321
1648
|
}
|
|
1322
1649
|
} else if (result?.cancelled) {
|
|
1323
|
-
console.log(
|
|
1650
|
+
console.log(
|
|
1651
|
+
`${displayIndent}${colors.warning(`${icons.cross} ${subagentType}: Cancelled`)}`
|
|
1652
|
+
);
|
|
1324
1653
|
console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
|
|
1325
1654
|
} else {
|
|
1326
|
-
console.log(
|
|
1655
|
+
console.log(
|
|
1656
|
+
`${displayIndent}${colors.error(`${icons.cross} ${subagentType}: Failed`)}`
|
|
1657
|
+
);
|
|
1327
1658
|
console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
|
|
1328
1659
|
if (result?.message) {
|
|
1329
1660
|
console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
|
|
@@ -1375,7 +1706,9 @@ export class InteractiveSession {
|
|
|
1375
1706
|
}
|
|
1376
1707
|
|
|
1377
1708
|
if (result?.success !== false) {
|
|
1378
|
-
console.log(
|
|
1709
|
+
console.log(
|
|
1710
|
+
`${displayIndent}${colors.success(`${icons.check} ${serverName}: Success`)}`
|
|
1711
|
+
);
|
|
1379
1712
|
console.log(`${displayIndent}${colors.textDim(` Tool: ${toolDisplayName}`)}`);
|
|
1380
1713
|
if (summary) {
|
|
1381
1714
|
console.log(`${displayIndent}${colors.textDim(` ${summary}`)}`);
|
|
@@ -1384,7 +1717,9 @@ export class InteractiveSession {
|
|
|
1384
1717
|
console.log(`${displayIndent}${colors.error(`${icons.cross} ${serverName}: Failed`)}`);
|
|
1385
1718
|
console.log(`${displayIndent}${colors.textDim(` Tool: ${toolDisplayName}`)}`);
|
|
1386
1719
|
if (result?.message || result?.error) {
|
|
1387
|
-
console.log(
|
|
1720
|
+
console.log(
|
|
1721
|
+
`${displayIndent}${colors.textDim(` ${result?.message || result?.error}`)}`
|
|
1722
|
+
);
|
|
1388
1723
|
}
|
|
1389
1724
|
}
|
|
1390
1725
|
} else if (tool === 'InvokeSkill') {
|
|
@@ -1397,7 +1732,8 @@ export class InteractiveSession {
|
|
|
1397
1732
|
console.log(`${displayIndent}${colors.success(`${icons.check} Skill: Completed`)}`);
|
|
1398
1733
|
console.log(`${displayIndent}${colors.textDim(` Skill: ${skillName}`)}`);
|
|
1399
1734
|
if (taskDesc) {
|
|
1400
|
-
const truncatedTask =
|
|
1735
|
+
const truncatedTask =
|
|
1736
|
+
taskDesc.length > 60 ? taskDesc.substring(0, 60) + '...' : taskDesc;
|
|
1401
1737
|
console.log(`${displayIndent}${colors.textDim(` Task: ${truncatedTask}`)}`);
|
|
1402
1738
|
}
|
|
1403
1739
|
} else {
|
|
@@ -1413,13 +1749,20 @@ export class InteractiveSession {
|
|
|
1413
1749
|
console.log(`${displayIndent}${colors.textDim(JSON.stringify(result, null, 2))}`);
|
|
1414
1750
|
} else if (result && result.success === false) {
|
|
1415
1751
|
// GUI task or other tool failed
|
|
1416
|
-
console.log(
|
|
1752
|
+
console.log(
|
|
1753
|
+
`${displayIndent}${colors.error(`${icons.cross} ${result.message || 'Failed'}`)}`
|
|
1754
|
+
);
|
|
1417
1755
|
} else if (result) {
|
|
1418
1756
|
// Show brief preview by default (consistent with subagent behavior)
|
|
1419
|
-
const resultPreview =
|
|
1420
|
-
|
|
1757
|
+
const resultPreview =
|
|
1758
|
+
typeof result === 'string' ? result : JSON.stringify(result, null, 2);
|
|
1759
|
+
const truncatedPreview =
|
|
1760
|
+
resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
|
|
1421
1761
|
// Indent the preview
|
|
1422
|
-
const indentedPreview = truncatedPreview
|
|
1762
|
+
const indentedPreview = truncatedPreview
|
|
1763
|
+
.split('\n')
|
|
1764
|
+
.map((line) => `${displayIndent} ${line}`)
|
|
1765
|
+
.join('\n');
|
|
1423
1766
|
console.log(`${indentedPreview}`);
|
|
1424
1767
|
} else {
|
|
1425
1768
|
console.log(`${displayIndent}${colors.textDim('(no result)')}`);
|
|
@@ -1429,10 +1772,10 @@ export class InteractiveSession {
|
|
|
1429
1772
|
tool,
|
|
1430
1773
|
params,
|
|
1431
1774
|
result,
|
|
1432
|
-
timestamp: Date.now()
|
|
1775
|
+
timestamp: Date.now(),
|
|
1433
1776
|
};
|
|
1434
1777
|
|
|
1435
|
-
this.
|
|
1778
|
+
this.tool_calls.push(toolCallRecord);
|
|
1436
1779
|
|
|
1437
1780
|
// Record tool output to session manager
|
|
1438
1781
|
await this.sessionManager.addOutput({
|
|
@@ -1441,82 +1784,122 @@ export class InteractiveSession {
|
|
|
1441
1784
|
toolName: tool,
|
|
1442
1785
|
toolParams: params,
|
|
1443
1786
|
toolResult: result,
|
|
1444
|
-
timestamp: Date.now()
|
|
1787
|
+
timestamp: Date.now(),
|
|
1445
1788
|
});
|
|
1446
1789
|
|
|
1447
|
-
//
|
|
1790
|
+
// Unified message format with tool name and params
|
|
1791
|
+
// Format: OpenAI-compatible tool result with plain text content
|
|
1448
1792
|
this.conversation.push({
|
|
1449
1793
|
role: 'tool',
|
|
1450
|
-
content: JSON.stringify(
|
|
1451
|
-
name: tool,
|
|
1452
|
-
parameters: params,
|
|
1453
|
-
result: result
|
|
1454
|
-
}),
|
|
1794
|
+
content: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
|
|
1455
1795
|
tool_call_id: toolCall.id,
|
|
1456
|
-
timestamp: Date.now()
|
|
1796
|
+
timestamp: Date.now(),
|
|
1457
1797
|
});
|
|
1458
1798
|
}
|
|
1459
1799
|
}
|
|
1460
1800
|
|
|
1461
1801
|
// Logic: Only skip returning results to main agent when user explicitly cancelled (ESC)
|
|
1462
1802
|
// For all other cases (success, failure, errors), always return results for further processing
|
|
1463
|
-
const guiSubagentCancelled = preparedToolCalls.some(
|
|
1803
|
+
const guiSubagentCancelled = preparedToolCalls.some(
|
|
1804
|
+
(tc) =>
|
|
1805
|
+
tc.name === 'task' &&
|
|
1806
|
+
tc.params?.subagent_type === 'gui-subagent' &&
|
|
1807
|
+
results.some((r) => r.tool === 'task' && (r.result as any)?.cancelled === true)
|
|
1808
|
+
);
|
|
1464
1809
|
|
|
1465
1810
|
// If GUI agent was cancelled by user, don't continue generating response
|
|
1466
1811
|
// This avoids wasting API calls and tokens on cancelled tasks
|
|
1467
1812
|
if (guiSubagentCancelled) {
|
|
1468
|
-
console.log('');
|
|
1469
|
-
console.log(`${indent}${colors.textMuted('GUI task cancelled by user')}`);
|
|
1470
1813
|
(this as any)._isOperationInProgress = false;
|
|
1471
1814
|
return;
|
|
1472
1815
|
}
|
|
1473
1816
|
|
|
1474
|
-
//
|
|
1475
|
-
if (hasError) {
|
|
1476
|
-
(this as any)._isOperationInProgress = false;
|
|
1477
|
-
// 不再抛出异常,而是将错误结果返回给 AI,让 AI 决定如何处理
|
|
1478
|
-
// 这样可以避免工具错误导致程序退出
|
|
1479
|
-
}
|
|
1480
|
-
|
|
1481
|
-
// Continue based on mode - 统一处理,无论是否有错误
|
|
1817
|
+
// Continue based on mode - unified handling for both success and error cases
|
|
1482
1818
|
if (onComplete) {
|
|
1819
|
+
await this.checkAndCompressContext();
|
|
1483
1820
|
// Remote mode: use provided callback
|
|
1484
1821
|
await onComplete();
|
|
1485
1822
|
} else {
|
|
1823
|
+
await this.checkAndCompressContext();
|
|
1486
1824
|
// Local mode: default behavior - continue with generateResponse
|
|
1487
1825
|
await this.generateResponse();
|
|
1488
1826
|
}
|
|
1489
1827
|
}
|
|
1490
1828
|
|
|
1829
|
+
/**
|
|
1830
|
+
* Clean up incomplete tool calls from conversation after cancellation
|
|
1831
|
+
* This removes assistant messages with tool_calls that don't have corresponding tool_results
|
|
1832
|
+
*/
|
|
1833
|
+
private async cleanupIncompleteToolCalls(): Promise<void> {
|
|
1834
|
+
// 从后往前找到包含 tool_calls 的 assistant 消息
|
|
1835
|
+
for (let i = this.conversation.length - 1; i >= 0; i--) {
|
|
1836
|
+
const msg = this.conversation[i];
|
|
1837
|
+
|
|
1838
|
+
if (msg.role === 'assistant' && msg.tool_calls?.length) {
|
|
1839
|
+
// 收集所有 tool_call IDs
|
|
1840
|
+
const allToolCallIds = new Set(msg.tool_calls.map((tc: any) => tc.id));
|
|
1841
|
+
|
|
1842
|
+
// 找出哪些 tool_call IDs 已经有对应的 tool_result
|
|
1843
|
+
const completedToolCallIds = new Set<string>();
|
|
1844
|
+
for (let k = i + 1; k < this.conversation.length; k++) {
|
|
1845
|
+
const resultMsg = this.conversation[k];
|
|
1846
|
+
if (resultMsg.role === 'tool' && resultMsg.tool_call_id) {
|
|
1847
|
+
completedToolCallIds.add(resultMsg.tool_call_id);
|
|
1848
|
+
} else if (resultMsg.role === 'user' || resultMsg.role === 'assistant') {
|
|
1849
|
+
break; // 遇到下一个角色消息就停止
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
// 找出未完成的 tool_call IDs
|
|
1854
|
+
const incompleteToolCallIds = [...allToolCallIds].filter(
|
|
1855
|
+
id => !completedToolCallIds.has(id)
|
|
1856
|
+
);
|
|
1857
|
+
|
|
1858
|
+
// 如果所有 tool_call 都已完成,不需要清理
|
|
1859
|
+
if (incompleteToolCallIds.length === 0) {
|
|
1860
|
+
break;
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
// 只移除未完成的 tool_call,不移除已完成的 tool_result
|
|
1864
|
+
msg.tool_calls = msg.tool_calls.filter(
|
|
1865
|
+
(tc: any) => !incompleteToolCallIds.includes(tc.id)
|
|
1866
|
+
);
|
|
1867
|
+
|
|
1868
|
+
break;
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1491
1873
|
/**
|
|
1492
1874
|
* Get user-friendly description for tool
|
|
1493
1875
|
*/
|
|
1494
1876
|
private getToolDescription(toolName: string, params: any): string {
|
|
1495
1877
|
const descriptions: Record<string, (params: any) => string> = {
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1878
|
+
Read: (p) => `Read file: ${this.truncatePath(p.filePath)}`,
|
|
1879
|
+
Write: (p) => `Write file: ${this.truncatePath(p.filePath)}`,
|
|
1880
|
+
Grep: (p) => `Search text: "${p.pattern}"`,
|
|
1881
|
+
Bash: (p) => `Execute command: ${this.truncateCommand(p.command)}`,
|
|
1882
|
+
ListDirectory: (p) => `List directory: ${this.truncatePath(p.path || '.')}`,
|
|
1883
|
+
SearchFiles: (p) => `Search files: ${p.pattern}`,
|
|
1884
|
+
DeleteFile: (p) => `Delete file: ${this.truncatePath(p.filePath)}`,
|
|
1885
|
+
CreateDirectory: (p) => `Create directory: ${this.truncatePath(p.dirPath)}`,
|
|
1886
|
+
Edit: (p) => `Edit text: ${this.truncatePath(p.file_path)}`,
|
|
1887
|
+
web_search: (p) => `Web search: "${p.query}"`,
|
|
1888
|
+
todo_write: () => `Update todo list`,
|
|
1889
|
+
todo_read: () => `Read todo list`,
|
|
1890
|
+
task: (p) => `Launch subtask: ${p.description}`,
|
|
1891
|
+
ReadBashOutput: (p) => `Read task output: ${p.task_id}`,
|
|
1892
|
+
web_fetch: () => `Fetch web content`,
|
|
1893
|
+
ask_user_question: () => `Ask user`,
|
|
1894
|
+
save_memory: () => `Save memory`,
|
|
1895
|
+
exit_plan_mode: () => `Complete plan`,
|
|
1896
|
+
xml_escape: (p) => `XML escape: ${this.truncatePath(p.file_path)}`,
|
|
1897
|
+
image_read: (p) => `Read image: ${this.truncatePath(p.image_input)}`,
|
|
1516
1898
|
// 'Skill': (p) => `Execute skill: ${p.skill}`,
|
|
1517
1899
|
// 'ListSkills': () => `List available skills`,
|
|
1518
1900
|
// 'GetSkillDetails': (p) => `Get skill details: ${p.skill}`,
|
|
1519
|
-
|
|
1901
|
+
InvokeSkill: (p) =>
|
|
1902
|
+
`Invoke skill: ${p.skillId} - ${this.truncatePath(p.taskDescription || '', 40)}`,
|
|
1520
1903
|
};
|
|
1521
1904
|
|
|
1522
1905
|
const getDescription = descriptions[toolName];
|
|
@@ -1549,11 +1932,14 @@ export class InteractiveSession {
|
|
|
1549
1932
|
return `${indent}${colors.textMuted('No tasks')}`;
|
|
1550
1933
|
}
|
|
1551
1934
|
|
|
1552
|
-
const statusConfig: Record<
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1935
|
+
const statusConfig: Record<
|
|
1936
|
+
string,
|
|
1937
|
+
{ icon: string; color: (text: string) => string; label: string }
|
|
1938
|
+
> = {
|
|
1939
|
+
pending: { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
|
|
1940
|
+
in_progress: { icon: icons.loading, color: colors.warning, label: 'In Progress' },
|
|
1941
|
+
completed: { icon: icons.success, color: colors.success, label: 'Completed' },
|
|
1942
|
+
failed: { icon: icons.error, color: colors.error, label: 'Failed' },
|
|
1557
1943
|
};
|
|
1558
1944
|
|
|
1559
1945
|
const lines: string[] = [];
|
|
@@ -1592,18 +1978,18 @@ export class InteractiveSession {
|
|
|
1592
1978
|
// const messages = data as any[];
|
|
1593
1979
|
// const tools = extra as any[];
|
|
1594
1980
|
//
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1981
|
+
// // System prompt
|
|
1982
|
+
// const systemMsg = messages.find((m: any) => m.role === 'system');
|
|
1983
|
+
// console.log(colors.border(`${boxChar.vertical}`) + ' 🟫 SYSTEM: ' +
|
|
1984
|
+
// colors.textMuted(systemMsg?.content?.toString().substring(0, 50) || '(none)') + ' '.repeat(3) + colors.border(boxChar.vertical));
|
|
1985
|
+
//
|
|
1986
|
+
// // Messages count
|
|
1987
|
+
// console.log(colors.border(`${boxChar.vertical}`) + ' 💬 MESSAGES: ' +
|
|
1988
|
+
// colors.text(messages.length.toString()) + ' items' + ' '.repeat(40) + colors.border(boxChar.vertical));
|
|
1989
|
+
//
|
|
1990
|
+
// // Tools count
|
|
1991
|
+
// console.log(colors.border(`${boxChar.vertical}`) + ' 🔧 TOOLS: ' +
|
|
1992
|
+
// colors.text((tools?.length || 0).toString()) + '' + ' '.repeat(43) + colors.border(boxChar.vertical)); //
|
|
1607
1993
|
// // Show last 2 messages
|
|
1608
1994
|
// const recentMessages = messages.slice(-2);
|
|
1609
1995
|
// for (const msg of recentMessages) {
|
|
@@ -1627,8 +2013,8 @@ export class InteractiveSession {
|
|
|
1627
2013
|
// colors.text(`Prompt: ${response.usage?.prompt_tokens || '?'}, Completion: ${response.usage?.completion_tokens || '?'}`) +
|
|
1628
2014
|
// ' '.repeat(15) + colors.border(boxChar.vertical));
|
|
1629
2015
|
//
|
|
1630
|
-
|
|
1631
|
-
|
|
2016
|
+
// console.log(colors.border(`${boxChar.vertical}`) + ' 🔧 TOOL_CALLS: ' +
|
|
2017
|
+
// colors.text((message.tool_calls?.length || 0).toString()) + '' + ' '.repeat(37) + colors.border(boxChar.vertical));
|
|
1632
2018
|
//
|
|
1633
2019
|
// // Content preview
|
|
1634
2020
|
// const contentStr = typeof message.content === 'string'
|
|
@@ -1676,7 +2062,174 @@ export class InteractiveSession {
|
|
|
1676
2062
|
}
|
|
1677
2063
|
}
|
|
1678
2064
|
|
|
2065
|
+
/**
|
|
2066
|
+
* Clean up stale temporary workspaces from previous sessions.
|
|
2067
|
+
* Called at startup to ensure no leftover temp files accumulate.
|
|
2068
|
+
*/
|
|
2069
|
+
async function cleanupStaleWorkspaces(): Promise<void> {
|
|
2070
|
+
try {
|
|
2071
|
+
const configManager = getConfigManager();
|
|
2072
|
+
const workspacePath = configManager.getWorkspacePath();
|
|
2073
|
+
// Use default workspace path if workspacePath is empty or undefined
|
|
2074
|
+
const effectiveWorkspacePath = (workspacePath && workspacePath.trim())
|
|
2075
|
+
? workspacePath
|
|
2076
|
+
: os.homedir();
|
|
2077
|
+
const baseWorkspaceDir = path.join(effectiveWorkspacePath, '.xagent', 'workspace');
|
|
2078
|
+
|
|
2079
|
+
// First, verify the workspace directory exists before attempting cleanup
|
|
2080
|
+
try {
|
|
2081
|
+
const stats = await fsPromises.stat(baseWorkspaceDir);
|
|
2082
|
+
if (!stats.isDirectory()) {
|
|
2083
|
+
return; // Not a directory, skip cleanup
|
|
2084
|
+
}
|
|
2085
|
+
} catch {
|
|
2086
|
+
// Directory doesn't exist - nothing to clean
|
|
2087
|
+
return;
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
try {
|
|
2091
|
+
const entries = await fsPromises.readdir(baseWorkspaceDir, { withFileTypes: true });
|
|
2092
|
+
|
|
2093
|
+
let cleanedCount = 0;
|
|
2094
|
+
for (const entry of entries) {
|
|
2095
|
+
if (entry.isDirectory()) {
|
|
2096
|
+
const fullPath = path.join(baseWorkspaceDir, entry.name);
|
|
2097
|
+
try {
|
|
2098
|
+
await fsPromises.rm(fullPath, { recursive: true, force: true });
|
|
2099
|
+
cleanedCount++;
|
|
2100
|
+
} catch {
|
|
2101
|
+
// Skip directories that can't be removed (in use or permission issues)
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
if (cleanedCount > 0) {
|
|
2107
|
+
console.log(colors.textDim(`🧹 Cleaned up ${cleanedCount} stale workspace(s)`));
|
|
2108
|
+
}
|
|
2109
|
+
} catch {
|
|
2110
|
+
// Can't read directory - skip cleanup
|
|
2111
|
+
}
|
|
2112
|
+
} catch {
|
|
2113
|
+
// Config not available - skip cleanup
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
|
|
2117
|
+
/**
|
|
2118
|
+
* Initialize built-in skills on first run.
|
|
2119
|
+
* Checks if user skills directory is empty or doesn't exist,
|
|
2120
|
+
* then copies all built-in skills including the protected find-skills.
|
|
2121
|
+
* @returns Number of skills initialized, or 0 if no initialization was needed.
|
|
2122
|
+
*/
|
|
2123
|
+
async function initializeSkillsOnDemand(): Promise<number> {
|
|
2124
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
2125
|
+
const __dirname = path.dirname(__filename);
|
|
2126
|
+
|
|
2127
|
+
// Get user skills directory (respects OS-specific paths)
|
|
2128
|
+
const configManager = getConfigManager();
|
|
2129
|
+
const userSkillsPath = configManager.getUserSkillsPath() || path.join(os.homedir(), '.xagent', 'skills');
|
|
2130
|
+
|
|
2131
|
+
// Check if user skills directory exists and has skills
|
|
2132
|
+
let hasSkills = false;
|
|
2133
|
+
try {
|
|
2134
|
+
const entries = await fsPromises.readdir(userSkillsPath, { withFileTypes: true });
|
|
2135
|
+
hasSkills = entries.some(e => e.isDirectory());
|
|
2136
|
+
} catch {
|
|
2137
|
+
hasSkills = false;
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
// If skills already exist, skip initialization
|
|
2141
|
+
if (hasSkills) {
|
|
2142
|
+
return 0;
|
|
2143
|
+
}
|
|
2144
|
+
|
|
2145
|
+
// Ensure user skills directory exists
|
|
2146
|
+
await fsPromises.mkdir(userSkillsPath, { recursive: true });
|
|
2147
|
+
|
|
2148
|
+
// Define skill source directories
|
|
2149
|
+
const builtinSkillsDir = path.join(__dirname, '..', 'skills', 'skills');
|
|
2150
|
+
const findSkillsDir = path.join(__dirname, '..', 'find-skills');
|
|
2151
|
+
|
|
2152
|
+
const skillsToInstall: { source: string; name: string }[] = [];
|
|
2153
|
+
|
|
2154
|
+
// Add find-skills from root directory
|
|
2155
|
+
if (fs.existsSync(findSkillsDir) && fs.existsSync(path.join(findSkillsDir, 'SKILL.md'))) {
|
|
2156
|
+
skillsToInstall.push({ source: findSkillsDir, name: 'find-skills' });
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
// Add skills from skills/skills directory
|
|
2160
|
+
if (fs.existsSync(builtinSkillsDir)) {
|
|
2161
|
+
const entries = fs.readdirSync(builtinSkillsDir, { withFileTypes: true });
|
|
2162
|
+
for (const entry of entries) {
|
|
2163
|
+
if (entry.isDirectory()) {
|
|
2164
|
+
const skillPath = path.join(builtinSkillsDir, entry.name);
|
|
2165
|
+
if (fs.existsSync(path.join(skillPath, 'SKILL.md'))) {
|
|
2166
|
+
skillsToInstall.push({ source: skillPath, name: entry.name });
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
|
|
2172
|
+
if (skillsToInstall.length === 0) {
|
|
2173
|
+
return 0;
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
// Create user skills directory (already done above, but ensure it exists)
|
|
2177
|
+
await fsPromises.mkdir(userSkillsPath, { recursive: true });
|
|
2178
|
+
|
|
2179
|
+
// Copy all skills
|
|
2180
|
+
for (const { source, name } of skillsToInstall) {
|
|
2181
|
+
const destPath = path.join(userSkillsPath, name);
|
|
2182
|
+
if (!fs.existsSync(destPath)) {
|
|
2183
|
+
await copyDirectoryRecursiveAsync(source, destPath);
|
|
2184
|
+
}
|
|
2185
|
+
}
|
|
2186
|
+
|
|
2187
|
+
return skillsToInstall.length;
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
// Synchronous version (kept for backwards compatibility)
|
|
2191
|
+
function copyDirectoryRecursive(src: string, dest: string): void {
|
|
2192
|
+
if (!fs.existsSync(dest)) {
|
|
2193
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
2194
|
+
}
|
|
2195
|
+
|
|
2196
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
2197
|
+
for (const entry of entries) {
|
|
2198
|
+
const srcPath = path.join(src, entry.name);
|
|
2199
|
+
const destPath = path.join(dest, entry.name);
|
|
2200
|
+
|
|
2201
|
+
if (entry.isDirectory()) {
|
|
2202
|
+
copyDirectoryRecursive(srcPath, destPath);
|
|
2203
|
+
} else if (entry.isFile()) {
|
|
2204
|
+
fs.copyFileSync(srcPath, destPath);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
// Asynchronous version for concurrent-safe initialization
|
|
2210
|
+
async function copyDirectoryRecursiveAsync(src: string, dest: string): Promise<void> {
|
|
2211
|
+
await fsPromises.mkdir(dest, { recursive: true });
|
|
2212
|
+
const entries = await fsPromises.readdir(src, { withFileTypes: true });
|
|
2213
|
+
|
|
2214
|
+
for (const entry of entries) {
|
|
2215
|
+
const srcPath = path.join(src, entry.name);
|
|
2216
|
+
const destPath = path.join(dest, entry.name);
|
|
2217
|
+
|
|
2218
|
+
if (entry.isDirectory()) {
|
|
2219
|
+
await copyDirectoryRecursiveAsync(srcPath, destPath);
|
|
2220
|
+
} else if (entry.isFile()) {
|
|
2221
|
+
await fsPromises.copyFile(srcPath, destPath);
|
|
2222
|
+
}
|
|
2223
|
+
}
|
|
2224
|
+
}
|
|
2225
|
+
|
|
1679
2226
|
export async function startInteractiveSession(): Promise<void> {
|
|
2227
|
+
// Clean up any leftover temp workspaces from previous sessions
|
|
2228
|
+
await cleanupStaleWorkspaces();
|
|
2229
|
+
|
|
2230
|
+
// Initialize built-in skills on first run (silent, returns count)
|
|
2231
|
+
const initializedCount = await initializeSkillsOnDemand();
|
|
2232
|
+
|
|
1680
2233
|
const session = new InteractiveSession();
|
|
1681
2234
|
|
|
1682
2235
|
// Flag to control shutdown
|
|
@@ -1721,6 +2274,15 @@ export async function startInteractiveSession(): Promise<void> {
|
|
|
1721
2274
|
process.exit(0);
|
|
1722
2275
|
});
|
|
1723
2276
|
|
|
2277
|
+
await session.start(initializedCount);
|
|
2278
|
+
// Check for updates on startup
|
|
2279
|
+
try {
|
|
2280
|
+
const { checkUpdatesOnStartup } = await import('./update.js');
|
|
2281
|
+
await checkUpdatesOnStartup();
|
|
2282
|
+
} catch (error) {
|
|
2283
|
+
// Silently ignore update check failures
|
|
2284
|
+
}
|
|
2285
|
+
|
|
1724
2286
|
await session.start();
|
|
1725
2287
|
}
|
|
1726
2288
|
|