@ai-agent-forge/agent-forge 0.85.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/CHANGELOG.md +5969 -0
- package/README.md +629 -0
- package/dist/artifacts/artifact-store.d.ts +51 -0
- package/dist/artifacts/artifact-store.d.ts.map +1 -0
- package/dist/artifacts/artifact-store.js +87 -0
- package/dist/artifacts/artifact-store.js.map +1 -0
- package/dist/artifacts/paged-read.d.ts +51 -0
- package/dist/artifacts/paged-read.d.ts.map +1 -0
- package/dist/artifacts/paged-read.js +55 -0
- package/dist/artifacts/paged-read.js.map +1 -0
- package/dist/bun/cli.d.ts +3 -0
- package/dist/bun/cli.d.ts.map +1 -0
- package/dist/bun/cli.js +11 -0
- package/dist/bun/cli.js.map +1 -0
- package/dist/bun/register-bedrock.d.ts +2 -0
- package/dist/bun/register-bedrock.d.ts.map +1 -0
- package/dist/bun/register-bedrock.js +4 -0
- package/dist/bun/register-bedrock.js.map +1 -0
- package/dist/bun/restore-sandbox-env.d.ts +17 -0
- package/dist/bun/restore-sandbox-env.d.ts.map +1 -0
- package/dist/bun/restore-sandbox-env.js +36 -0
- package/dist/bun/restore-sandbox-env.js.map +1 -0
- package/dist/bundle/.build-manifest.json +3566 -0
- package/dist/bundle/chunks/anthropic-messages-RJYU4TCY.js +6 -0
- package/dist/bundle/chunks/anthropic.js +75 -0
- package/dist/bundle/chunks/azure-openai-responses-LESTCUL7.js +2 -0
- package/dist/bundle/chunks/bedrock-converse-stream.js +78 -0
- package/dist/bundle/chunks/chunk-3QKHSPQL.js +8 -0
- package/dist/bundle/chunks/chunk-4L3WN2XY.js +2 -0
- package/dist/bundle/chunks/chunk-4NKZMFSZ.js +1579 -0
- package/dist/bundle/chunks/chunk-6CYCI3EJ.js +12 -0
- package/dist/bundle/chunks/chunk-6R7UI4IY.js +2 -0
- package/dist/bundle/chunks/chunk-7ZC4UXSL.js +2 -0
- package/dist/bundle/chunks/chunk-7ZY7IXZD.js +4 -0
- package/dist/bundle/chunks/chunk-DRQDKNXA.js +2 -0
- package/dist/bundle/chunks/chunk-DTD7JQ7Y.js +42 -0
- package/dist/bundle/chunks/chunk-EWXX34QG.js +2 -0
- package/dist/bundle/chunks/chunk-GMWCTUPB.js +2 -0
- package/dist/bundle/chunks/chunk-JUUC5AZY.js +2 -0
- package/dist/bundle/chunks/chunk-KQEGUM34.js +2 -0
- package/dist/bundle/chunks/chunk-L2ZPNLYD.js +2 -0
- package/dist/bundle/chunks/chunk-MDOTREMR.js +11 -0
- package/dist/bundle/chunks/chunk-NUHFSC37.js +25 -0
- package/dist/bundle/chunks/chunk-PGXDEOVV.js +2 -0
- package/dist/bundle/chunks/chunk-PJACZCIR.js +2 -0
- package/dist/bundle/chunks/chunk-PRRNXB7S.js +2 -0
- package/dist/bundle/chunks/chunk-SER2HCLE.js +38 -0
- package/dist/bundle/chunks/chunk-TEPMHNKQ.js +2 -0
- package/dist/bundle/chunks/chunk-V46XUTK3.js +2 -0
- package/dist/bundle/chunks/chunk-WGLBM2BP.js +2 -0
- package/dist/bundle/chunks/chunk-WW3LMV3F.js +2 -0
- package/dist/bundle/chunks/chunk-XDUWOHPL.js +6 -0
- package/dist/bundle/chunks/github-copilot.js +2 -0
- package/dist/bundle/chunks/google-generative-ai-NZ66X6K7.js +2 -0
- package/dist/bundle/chunks/google-vertex-2ZZOFBNQ.js +2 -0
- package/dist/bundle/chunks/https-proxy-agent-UDEHOBGS.js +13 -0
- package/dist/bundle/chunks/image-resize-worker.js +2 -0
- package/dist/bundle/chunks/kimi-coding.js +2 -0
- package/dist/bundle/chunks/lib-HGQMNYVT.js +9 -0
- package/dist/bundle/chunks/mistral-conversations-B6DZRMEK.js +5 -0
- package/dist/bundle/chunks/multipart-parser-SQ6GDJL7.js +3 -0
- package/dist/bundle/chunks/node-O3RIBJRH.js +16 -0
- package/dist/bundle/chunks/node-TWVVAGPO.js +2 -0
- package/dist/bundle/chunks/openai-codex-responses-UFH6O5K6.js +10 -0
- package/dist/bundle/chunks/openai-codex.js +75 -0
- package/dist/bundle/chunks/openai-completions-A55GHA4O.js +7 -0
- package/dist/bundle/chunks/openai-responses-TJLXRAXR.js +2 -0
- package/dist/bundle/chunks/openrouter-images-G3GT5BVT.js +2 -0
- package/dist/bundle/chunks/openrouter.js +75 -0
- package/dist/bundle/chunks/pi-messages-SLF355GF.js +8 -0
- package/dist/bundle/chunks/radius.js +75 -0
- package/dist/bundle/chunks/src-UK77SYZE.js +4 -0
- package/dist/bundle/chunks/xai.js +2 -0
- package/dist/bundle/cli.js +3 -0
- package/dist/bundle/client.js +2 -0
- package/dist/bundle/index.js +8 -0
- package/dist/bundle/rpc-entry.js +3 -0
- package/dist/capabilities/builtin.d.ts +46 -0
- package/dist/capabilities/builtin.d.ts.map +1 -0
- package/dist/capabilities/builtin.js +68 -0
- package/dist/capabilities/builtin.js.map +1 -0
- package/dist/cli/args.d.ts +102 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +712 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/auth-check.d.ts +19 -0
- package/dist/cli/auth-check.d.ts.map +1 -0
- package/dist/cli/auth-check.js +50 -0
- package/dist/cli/auth-check.js.map +1 -0
- package/dist/cli/auth-command.d.ts +24 -0
- package/dist/cli/auth-command.d.ts.map +1 -0
- package/dist/cli/auth-command.js +102 -0
- package/dist/cli/auth-command.js.map +1 -0
- package/dist/cli/config-selector.d.ts +23 -0
- package/dist/cli/config-selector.d.ts.map +1 -0
- package/dist/cli/config-selector.js +51 -0
- package/dist/cli/config-selector.js.map +1 -0
- package/dist/cli/credential-print.d.ts +13 -0
- package/dist/cli/credential-print.d.ts.map +1 -0
- package/dist/cli/credential-print.js +76 -0
- package/dist/cli/credential-print.js.map +1 -0
- package/dist/cli/experimental/auth.d.ts +16 -0
- package/dist/cli/experimental/auth.d.ts.map +1 -0
- package/dist/cli/experimental/auth.js +13 -0
- package/dist/cli/experimental/auth.js.map +1 -0
- package/dist/cli/experimental/cli.d.ts +6 -0
- package/dist/cli/experimental/cli.d.ts.map +1 -0
- package/dist/cli/experimental/cli.js +5 -0
- package/dist/cli/experimental/cli.js.map +1 -0
- package/dist/cli/experimental/command-options.d.ts +17 -0
- package/dist/cli/experimental/command-options.d.ts.map +1 -0
- package/dist/cli/experimental/command-options.js +35 -0
- package/dist/cli/experimental/command-options.js.map +1 -0
- package/dist/cli/experimental/command.d.ts +63 -0
- package/dist/cli/experimental/command.d.ts.map +1 -0
- package/dist/cli/experimental/command.js +130 -0
- package/dist/cli/experimental/command.js.map +1 -0
- package/dist/cli/experimental/commands/client.d.ts +13 -0
- package/dist/cli/experimental/commands/client.d.ts.map +1 -0
- package/dist/cli/experimental/commands/client.js +25 -0
- package/dist/cli/experimental/commands/client.js.map +1 -0
- package/dist/cli/experimental/commands/pi.d.ts +15 -0
- package/dist/cli/experimental/commands/pi.d.ts.map +1 -0
- package/dist/cli/experimental/commands/pi.js +26 -0
- package/dist/cli/experimental/commands/pi.js.map +1 -0
- package/dist/cli/experimental/commands/server.d.ts +13 -0
- package/dist/cli/experimental/commands/server.d.ts.map +1 -0
- package/dist/cli/experimental/commands/server.js +25 -0
- package/dist/cli/experimental/commands/server.js.map +1 -0
- package/dist/cli/experimental/transport-address.d.ts +10 -0
- package/dist/cli/experimental/transport-address.d.ts.map +1 -0
- package/dist/cli/experimental/transport-address.js +38 -0
- package/dist/cli/experimental/transport-address.js.map +1 -0
- package/dist/cli/file-processor.d.ts +15 -0
- package/dist/cli/file-processor.d.ts.map +1 -0
- package/dist/cli/file-processor.js +71 -0
- package/dist/cli/file-processor.js.map +1 -0
- package/dist/cli/initial-message.d.ts +18 -0
- package/dist/cli/initial-message.d.ts.map +1 -0
- package/dist/cli/initial-message.js +22 -0
- package/dist/cli/initial-message.js.map +1 -0
- package/dist/cli/list-models.d.ts +9 -0
- package/dist/cli/list-models.d.ts.map +1 -0
- package/dist/cli/list-models.js +98 -0
- package/dist/cli/list-models.js.map +1 -0
- package/dist/cli/plugin-command.d.ts +50 -0
- package/dist/cli/plugin-command.d.ts.map +1 -0
- package/dist/cli/plugin-command.js +85 -0
- package/dist/cli/plugin-command.js.map +1 -0
- package/dist/cli/plugin-manager-controller.d.ts +59 -0
- package/dist/cli/plugin-manager-controller.d.ts.map +1 -0
- package/dist/cli/plugin-manager-controller.js +183 -0
- package/dist/cli/plugin-manager-controller.js.map +1 -0
- package/dist/cli/plugin-selection-controller.d.ts +57 -0
- package/dist/cli/plugin-selection-controller.d.ts.map +1 -0
- package/dist/cli/plugin-selection-controller.js +159 -0
- package/dist/cli/plugin-selection-controller.js.map +1 -0
- package/dist/cli/serve.d.ts +39 -0
- package/dist/cli/serve.d.ts.map +1 -0
- package/dist/cli/serve.js +124 -0
- package/dist/cli/serve.js.map +1 -0
- package/dist/cli/session-picker.d.ts +30 -0
- package/dist/cli/session-picker.d.ts.map +1 -0
- package/dist/cli/session-picker.js +62 -0
- package/dist/cli/session-picker.js.map +1 -0
- package/dist/cli/startup-ui.d.ts +20 -0
- package/dist/cli/startup-ui.d.ts.map +1 -0
- package/dist/cli/startup-ui.js +185 -0
- package/dist/cli/startup-ui.js.map +1 -0
- package/dist/cli/trace-view.d.ts +86 -0
- package/dist/cli/trace-view.d.ts.map +1 -0
- package/dist/cli/trace-view.js +263 -0
- package/dist/cli/trace-view.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +19 -0
- package/dist/cli.js.map +1 -0
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +3 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/remote-session.d.ts +53 -0
- package/dist/client/remote-session.d.ts.map +1 -0
- package/dist/client/remote-session.js +340 -0
- package/dist/client/remote-session.js.map +1 -0
- package/dist/client/transcript.d.ts +12 -0
- package/dist/client/transcript.d.ts.map +1 -0
- package/dist/client/transcript.js +98 -0
- package/dist/client/transcript.js.map +1 -0
- package/dist/config.d.ts +95 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +459 -0
- package/dist/config.js.map +1 -0
- package/dist/core/agent-capability-contract.d.ts +322 -0
- package/dist/core/agent-capability-contract.d.ts.map +1 -0
- package/dist/core/agent-capability-contract.js +1154 -0
- package/dist/core/agent-capability-contract.js.map +1 -0
- package/dist/core/agent-session-bash.d.ts +51 -0
- package/dist/core/agent-session-bash.d.ts.map +1 -0
- package/dist/core/agent-session-bash.js +138 -0
- package/dist/core/agent-session-bash.js.map +1 -0
- package/dist/core/agent-session-compaction.d.ts +188 -0
- package/dist/core/agent-session-compaction.d.ts.map +1 -0
- package/dist/core/agent-session-compaction.js +1071 -0
- package/dist/core/agent-session-compaction.js.map +1 -0
- package/dist/core/agent-session-context.d.ts +43 -0
- package/dist/core/agent-session-context.d.ts.map +1 -0
- package/dist/core/agent-session-context.js +170 -0
- package/dist/core/agent-session-context.js.map +1 -0
- package/dist/core/agent-session-extensions.d.ts +30 -0
- package/dist/core/agent-session-extensions.d.ts.map +1 -0
- package/dist/core/agent-session-extensions.js +74 -0
- package/dist/core/agent-session-extensions.js.map +1 -0
- package/dist/core/agent-session-internal.d.ts +290 -0
- package/dist/core/agent-session-internal.d.ts.map +1 -0
- package/dist/core/agent-session-internal.js +40 -0
- package/dist/core/agent-session-internal.js.map +1 -0
- package/dist/core/agent-session-lifecycle.d.ts +242 -0
- package/dist/core/agent-session-lifecycle.d.ts.map +1 -0
- package/dist/core/agent-session-lifecycle.js +699 -0
- package/dist/core/agent-session-lifecycle.js.map +1 -0
- package/dist/core/agent-session-loop-runner.d.ts +52 -0
- package/dist/core/agent-session-loop-runner.d.ts.map +1 -0
- package/dist/core/agent-session-loop-runner.js +168 -0
- package/dist/core/agent-session-loop-runner.js.map +1 -0
- package/dist/core/agent-session-loop.d.ts +39 -0
- package/dist/core/agent-session-loop.d.ts.map +1 -0
- package/dist/core/agent-session-loop.js +155 -0
- package/dist/core/agent-session-loop.js.map +1 -0
- package/dist/core/agent-session-model.d.ts +49 -0
- package/dist/core/agent-session-model.d.ts.map +1 -0
- package/dist/core/agent-session-model.js +155 -0
- package/dist/core/agent-session-model.js.map +1 -0
- package/dist/core/agent-session-paths.d.ts +3 -0
- package/dist/core/agent-session-paths.d.ts.map +1 -0
- package/dist/core/agent-session-paths.js +14 -0
- package/dist/core/agent-session-paths.js.map +1 -0
- package/dist/core/agent-session-projection.d.ts +53 -0
- package/dist/core/agent-session-projection.d.ts.map +1 -0
- package/dist/core/agent-session-projection.js +195 -0
- package/dist/core/agent-session-projection.js.map +1 -0
- package/dist/core/agent-session-retry.d.ts +28 -0
- package/dist/core/agent-session-retry.d.ts.map +1 -0
- package/dist/core/agent-session-retry.js +141 -0
- package/dist/core/agent-session-retry.js.map +1 -0
- package/dist/core/agent-session-runtime.d.ts +181 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -0
- package/dist/core/agent-session-runtime.js +297 -0
- package/dist/core/agent-session-runtime.js.map +1 -0
- package/dist/core/agent-session-services.d.ts +281 -0
- package/dist/core/agent-session-services.d.ts.map +1 -0
- package/dist/core/agent-session-services.js +680 -0
- package/dist/core/agent-session-services.js.map +1 -0
- package/dist/core/agent-session-store.d.ts +47 -0
- package/dist/core/agent-session-store.d.ts.map +1 -0
- package/dist/core/agent-session-store.js +155 -0
- package/dist/core/agent-session-store.js.map +1 -0
- package/dist/core/agent-session-tools.d.ts +36 -0
- package/dist/core/agent-session-tools.d.ts.map +1 -0
- package/dist/core/agent-session-tools.js +305 -0
- package/dist/core/agent-session-tools.js.map +1 -0
- package/dist/core/agent-session.d.ts +1156 -0
- package/dist/core/agent-session.d.ts.map +1 -0
- package/dist/core/agent-session.js +2550 -0
- package/dist/core/agent-session.js.map +1 -0
- package/dist/core/auth-guidance.d.ts +5 -0
- package/dist/core/auth-guidance.d.ts.map +1 -0
- package/dist/core/auth-guidance.js +21 -0
- package/dist/core/auth-guidance.js.map +1 -0
- package/dist/core/auth-storage.d.ts +72 -0
- package/dist/core/auth-storage.d.ts.map +1 -0
- package/dist/core/auth-storage.js +426 -0
- package/dist/core/auth-storage.js.map +1 -0
- package/dist/core/bash-executor.d.ts +32 -0
- package/dist/core/bash-executor.d.ts.map +1 -0
- package/dist/core/bash-executor.js +111 -0
- package/dist/core/bash-executor.js.map +1 -0
- package/dist/core/cache-stats.d.ts +49 -0
- package/dist/core/cache-stats.d.ts.map +1 -0
- package/dist/core/cache-stats.js +101 -0
- package/dist/core/cache-stats.js.map +1 -0
- package/dist/core/compaction/branch-summarization.d.ts +88 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -0
- package/dist/core/compaction/branch-summarization.js +220 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -0
- package/dist/core/compaction/compaction.d.ts +178 -0
- package/dist/core/compaction/compaction.d.ts.map +1 -0
- package/dist/core/compaction/compaction.js +608 -0
- package/dist/core/compaction/compaction.js.map +1 -0
- package/dist/core/compaction/index.d.ts +7 -0
- package/dist/core/compaction/index.d.ts.map +1 -0
- package/dist/core/compaction/index.js +7 -0
- package/dist/core/compaction/index.js.map +1 -0
- package/dist/core/compaction/utils.d.ts +49 -0
- package/dist/core/compaction/utils.d.ts.map +1 -0
- package/dist/core/compaction/utils.js +170 -0
- package/dist/core/compaction/utils.js.map +1 -0
- package/dist/core/defaults.d.ts +4 -0
- package/dist/core/defaults.d.ts.map +1 -0
- package/dist/core/defaults.js +11 -0
- package/dist/core/defaults.js.map +1 -0
- package/dist/core/diagnostics.d.ts +15 -0
- package/dist/core/diagnostics.d.ts.map +1 -0
- package/dist/core/diagnostics.js +2 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/embedded-sdk.d.ts +96 -0
- package/dist/core/embedded-sdk.d.ts.map +1 -0
- package/dist/core/embedded-sdk.js +395 -0
- package/dist/core/embedded-sdk.js.map +1 -0
- package/dist/core/event-bus.d.ts +9 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/event-bus.js +25 -0
- package/dist/core/event-bus.js.map +1 -0
- package/dist/core/exec.d.ts +29 -0
- package/dist/core/exec.d.ts.map +1 -0
- package/dist/core/exec.js +75 -0
- package/dist/core/exec.js.map +1 -0
- package/dist/core/experimental.d.ts +2 -0
- package/dist/core/experimental.d.ts.map +1 -0
- package/dist/core/experimental.js +6 -0
- package/dist/core/experimental.js.map +1 -0
- package/dist/core/export-html/ansi-to-html.d.ts +22 -0
- package/dist/core/export-html/ansi-to-html.d.ts.map +1 -0
- package/dist/core/export-html/ansi-to-html.js +249 -0
- package/dist/core/export-html/ansi-to-html.js.map +1 -0
- package/dist/core/export-html/index.d.ts +37 -0
- package/dist/core/export-html/index.d.ts.map +1 -0
- package/dist/core/export-html/index.js +226 -0
- package/dist/core/export-html/index.js.map +1 -0
- package/dist/core/export-html/template.css +1066 -0
- package/dist/core/export-html/template.html +55 -0
- package/dist/core/export-html/template.js +1864 -0
- package/dist/core/export-html/tool-renderer.d.ts +34 -0
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -0
- package/dist/core/export-html/tool-renderer.js +108 -0
- package/dist/core/export-html/tool-renderer.js.map +1 -0
- package/dist/core/export-html/vendor/highlight.min.js +1213 -0
- package/dist/core/export-html/vendor/marked.min.js +78 -0
- package/dist/core/extensions/agent-composition.d.ts +67 -0
- package/dist/core/extensions/agent-composition.d.ts.map +1 -0
- package/dist/core/extensions/agent-composition.js +785 -0
- package/dist/core/extensions/agent-composition.js.map +1 -0
- package/dist/core/extensions/bootstrap-agent-loop.d.ts +203 -0
- package/dist/core/extensions/bootstrap-agent-loop.d.ts.map +1 -0
- package/dist/core/extensions/bootstrap-agent-loop.js +565 -0
- package/dist/core/extensions/bootstrap-agent-loop.js.map +1 -0
- package/dist/core/extensions/bootstrap-branch-summary-policy.d.ts +7 -0
- package/dist/core/extensions/bootstrap-branch-summary-policy.d.ts.map +1 -0
- package/dist/core/extensions/bootstrap-branch-summary-policy.js +28 -0
- package/dist/core/extensions/bootstrap-branch-summary-policy.js.map +1 -0
- package/dist/core/extensions/bootstrap-compaction-policy.d.ts +7 -0
- package/dist/core/extensions/bootstrap-compaction-policy.d.ts.map +1 -0
- package/dist/core/extensions/bootstrap-compaction-policy.js +81 -0
- package/dist/core/extensions/bootstrap-compaction-policy.js.map +1 -0
- package/dist/core/extensions/bootstrap-lifecycle.d.ts +26 -0
- package/dist/core/extensions/bootstrap-lifecycle.d.ts.map +1 -0
- package/dist/core/extensions/bootstrap-lifecycle.js +181 -0
- package/dist/core/extensions/bootstrap-lifecycle.js.map +1 -0
- package/dist/core/extensions/bootstrap-overflow-policy.d.ts +7 -0
- package/dist/core/extensions/bootstrap-overflow-policy.d.ts.map +1 -0
- package/dist/core/extensions/bootstrap-overflow-policy.js +29 -0
- package/dist/core/extensions/bootstrap-overflow-policy.js.map +1 -0
- package/dist/core/extensions/bootstrap-retry-policy.d.ts +7 -0
- package/dist/core/extensions/bootstrap-retry-policy.d.ts.map +1 -0
- package/dist/core/extensions/bootstrap-retry-policy.js +28 -0
- package/dist/core/extensions/bootstrap-retry-policy.js.map +1 -0
- package/dist/core/extensions/bootstrap-skills.d.ts +16 -0
- package/dist/core/extensions/bootstrap-skills.d.ts.map +1 -0
- package/dist/core/extensions/bootstrap-skills.js +119 -0
- package/dist/core/extensions/bootstrap-skills.js.map +1 -0
- package/dist/core/extensions/bootstrap-tools.d.ts +20 -0
- package/dist/core/extensions/bootstrap-tools.d.ts.map +1 -0
- package/dist/core/extensions/bootstrap-tools.js +35 -0
- package/dist/core/extensions/bootstrap-tools.js.map +1 -0
- package/dist/core/extensions/capability-exports.d.ts +5 -0
- package/dist/core/extensions/capability-exports.d.ts.map +1 -0
- package/dist/core/extensions/capability-exports.js +4 -0
- package/dist/core/extensions/capability-exports.js.map +1 -0
- package/dist/core/extensions/capability-plugin.d.ts +69 -0
- package/dist/core/extensions/capability-plugin.d.ts.map +1 -0
- package/dist/core/extensions/capability-plugin.js +485 -0
- package/dist/core/extensions/capability-plugin.js.map +1 -0
- package/dist/core/extensions/capability-runtime-events.d.ts +22 -0
- package/dist/core/extensions/capability-runtime-events.d.ts.map +1 -0
- package/dist/core/extensions/capability-runtime-events.js +381 -0
- package/dist/core/extensions/capability-runtime-events.js.map +1 -0
- package/dist/core/extensions/capability-runtime-guards.d.ts +93 -0
- package/dist/core/extensions/capability-runtime-guards.d.ts.map +1 -0
- package/dist/core/extensions/capability-runtime-guards.js +800 -0
- package/dist/core/extensions/capability-runtime-guards.js.map +1 -0
- package/dist/core/extensions/capability-runtime-inspect.d.ts +12 -0
- package/dist/core/extensions/capability-runtime-inspect.d.ts.map +1 -0
- package/dist/core/extensions/capability-runtime-inspect.js +106 -0
- package/dist/core/extensions/capability-runtime-inspect.js.map +1 -0
- package/dist/core/extensions/capability-runtime-manifest.d.ts +19 -0
- package/dist/core/extensions/capability-runtime-manifest.d.ts.map +1 -0
- package/dist/core/extensions/capability-runtime-manifest.js +218 -0
- package/dist/core/extensions/capability-runtime-manifest.js.map +1 -0
- package/dist/core/extensions/capability-runtime-operation.d.ts +26 -0
- package/dist/core/extensions/capability-runtime-operation.d.ts.map +1 -0
- package/dist/core/extensions/capability-runtime-operation.js +88 -0
- package/dist/core/extensions/capability-runtime-operation.js.map +1 -0
- package/dist/core/extensions/capability-runtime-snapshot.d.ts +11 -0
- package/dist/core/extensions/capability-runtime-snapshot.d.ts.map +1 -0
- package/dist/core/extensions/capability-runtime-snapshot.js +81 -0
- package/dist/core/extensions/capability-runtime-snapshot.js.map +1 -0
- package/dist/core/extensions/capability-runtime-store.d.ts +34 -0
- package/dist/core/extensions/capability-runtime-store.d.ts.map +1 -0
- package/dist/core/extensions/capability-runtime-store.js +153 -0
- package/dist/core/extensions/capability-runtime-store.js.map +1 -0
- package/dist/core/extensions/capability-runtime-tool.d.ts +10 -0
- package/dist/core/extensions/capability-runtime-tool.d.ts.map +1 -0
- package/dist/core/extensions/capability-runtime-tool.js +10 -0
- package/dist/core/extensions/capability-runtime-tool.js.map +1 -0
- package/dist/core/extensions/capability-runtime-types.d.ts +303 -0
- package/dist/core/extensions/capability-runtime-types.d.ts.map +1 -0
- package/dist/core/extensions/capability-runtime-types.js +2 -0
- package/dist/core/extensions/capability-runtime-types.js.map +1 -0
- package/dist/core/extensions/capability-runtime-validation.d.ts +35 -0
- package/dist/core/extensions/capability-runtime-validation.d.ts.map +1 -0
- package/dist/core/extensions/capability-runtime-validation.js +380 -0
- package/dist/core/extensions/capability-runtime-validation.js.map +1 -0
- package/dist/core/extensions/capability-runtime.d.ts +7 -0
- package/dist/core/extensions/capability-runtime.d.ts.map +1 -0
- package/dist/core/extensions/capability-runtime.js +2170 -0
- package/dist/core/extensions/capability-runtime.js.map +1 -0
- package/dist/core/extensions/compaction-memory-failure-mode.d.ts +43 -0
- package/dist/core/extensions/compaction-memory-failure-mode.d.ts.map +1 -0
- package/dist/core/extensions/compaction-memory-failure-mode.js +31 -0
- package/dist/core/extensions/compaction-memory-failure-mode.js.map +1 -0
- package/dist/core/extensions/context-strategy-binding.d.ts +12 -0
- package/dist/core/extensions/context-strategy-binding.d.ts.map +1 -0
- package/dist/core/extensions/context-strategy-binding.js +54 -0
- package/dist/core/extensions/context-strategy-binding.js.map +1 -0
- package/dist/core/extensions/index.d.ts +19 -0
- package/dist/core/extensions/index.d.ts.map +1 -0
- package/dist/core/extensions/index.js +18 -0
- package/dist/core/extensions/index.js.map +1 -0
- package/dist/core/extensions/lifecycle-catalog.d.ts +584 -0
- package/dist/core/extensions/lifecycle-catalog.d.ts.map +1 -0
- package/dist/core/extensions/lifecycle-catalog.js +1903 -0
- package/dist/core/extensions/lifecycle-catalog.js.map +1 -0
- package/dist/core/extensions/lifecycle-delivery-queue.d.ts +48 -0
- package/dist/core/extensions/lifecycle-delivery-queue.d.ts.map +1 -0
- package/dist/core/extensions/lifecycle-delivery-queue.js +164 -0
- package/dist/core/extensions/lifecycle-delivery-queue.js.map +1 -0
- package/dist/core/extensions/microcompact.d.ts +71 -0
- package/dist/core/extensions/microcompact.d.ts.map +1 -0
- package/dist/core/extensions/microcompact.js +123 -0
- package/dist/core/extensions/microcompact.js.map +1 -0
- package/dist/core/extensions/public-api.d.ts +10 -0
- package/dist/core/extensions/public-api.d.ts.map +1 -0
- package/dist/core/extensions/public-api.js +9 -0
- package/dist/core/extensions/public-api.js.map +1 -0
- package/dist/core/facade/coding-agent-facade.d.ts +338 -0
- package/dist/core/facade/coding-agent-facade.d.ts.map +1 -0
- package/dist/core/facade/coding-agent-facade.js +115 -0
- package/dist/core/facade/coding-agent-facade.js.map +1 -0
- package/dist/core/facade/coding-agent-runtime-facade.d.ts +115 -0
- package/dist/core/facade/coding-agent-runtime-facade.d.ts.map +1 -0
- package/dist/core/facade/coding-agent-runtime-facade.js +63 -0
- package/dist/core/facade/coding-agent-runtime-facade.js.map +1 -0
- package/dist/core/facade/index.d.ts +6 -0
- package/dist/core/facade/index.d.ts.map +1 -0
- package/dist/core/facade/index.js +3 -0
- package/dist/core/facade/index.js.map +1 -0
- package/dist/core/footer-data-provider.d.ts +57 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -0
- package/dist/core/footer-data-provider.js +323 -0
- package/dist/core/footer-data-provider.js.map +1 -0
- package/dist/core/http-dispatcher.d.ts +22 -0
- package/dist/core/http-dispatcher.d.ts.map +1 -0
- package/dist/core/http-dispatcher.js +98 -0
- package/dist/core/http-dispatcher.js.map +1 -0
- package/dist/core/index.d.ts +19 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/input-source.d.ts +9 -0
- package/dist/core/input-source.d.ts.map +1 -0
- package/dist/core/input-source.js +2 -0
- package/dist/core/input-source.js.map +1 -0
- package/dist/core/keybindings.d.ts +442 -0
- package/dist/core/keybindings.d.ts.map +1 -0
- package/dist/core/keybindings.js +329 -0
- package/dist/core/keybindings.js.map +1 -0
- package/dist/core/logger.d.ts +107 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +246 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/messages.d.ts +85 -0
- package/dist/core/messages.d.ts.map +1 -0
- package/dist/core/messages.js +146 -0
- package/dist/core/messages.js.map +1 -0
- package/dist/core/model-config.d.ts +586 -0
- package/dist/core/model-config.d.ts.map +1 -0
- package/dist/core/model-config.js +258 -0
- package/dist/core/model-config.js.map +1 -0
- package/dist/core/model-registry.d.ts +49 -0
- package/dist/core/model-registry.d.ts.map +1 -0
- package/dist/core/model-registry.js +115 -0
- package/dist/core/model-registry.js.map +1 -0
- package/dist/core/model-resolver.d.ts +124 -0
- package/dist/core/model-resolver.d.ts.map +1 -0
- package/dist/core/model-resolver.js +590 -0
- package/dist/core/model-resolver.js.map +1 -0
- package/dist/core/model-runtime.d.ts +100 -0
- package/dist/core/model-runtime.d.ts.map +1 -0
- package/dist/core/model-runtime.js +598 -0
- package/dist/core/model-runtime.js.map +1 -0
- package/dist/core/models-store.d.ts +22 -0
- package/dist/core/models-store.d.ts.map +1 -0
- package/dist/core/models-store.js +115 -0
- package/dist/core/models-store.js.map +1 -0
- package/dist/core/output-guard.d.ts +7 -0
- package/dist/core/output-guard.d.ts.map +1 -0
- package/dist/core/output-guard.js +90 -0
- package/dist/core/output-guard.js.map +1 -0
- package/dist/core/package-install-host.d.ts +23 -0
- package/dist/core/package-install-host.d.ts.map +1 -0
- package/dist/core/package-install-host.js +202 -0
- package/dist/core/package-install-host.js.map +1 -0
- package/dist/core/package-manager.d.ts +93 -0
- package/dist/core/package-manager.d.ts.map +1 -0
- package/dist/core/package-manager.js +947 -0
- package/dist/core/package-manager.js.map +1 -0
- package/dist/core/package-process-runner.d.ts +4 -0
- package/dist/core/package-process-runner.d.ts.map +1 -0
- package/dist/core/package-process-runner.js +47 -0
- package/dist/core/package-process-runner.js.map +1 -0
- package/dist/core/package-resource-collection.d.ts +16 -0
- package/dist/core/package-resource-collection.d.ts.map +1 -0
- package/dist/core/package-resource-collection.js +300 -0
- package/dist/core/package-resource-collection.js.map +1 -0
- package/dist/core/package-resource-discovery.d.ts +33 -0
- package/dist/core/package-resource-discovery.d.ts.map +1 -0
- package/dist/core/package-resource-discovery.js +449 -0
- package/dist/core/package-resource-discovery.js.map +1 -0
- package/dist/core/package-source.d.ts +157 -0
- package/dist/core/package-source.d.ts.map +1 -0
- package/dist/core/package-source.js +179 -0
- package/dist/core/package-source.js.map +1 -0
- package/dist/core/pi-manifest.d.ts +7 -0
- package/dist/core/pi-manifest.d.ts.map +1 -0
- package/dist/core/pi-manifest.js +26 -0
- package/dist/core/pi-manifest.js.map +1 -0
- package/dist/core/prompt-templates.d.ts +54 -0
- package/dist/core/prompt-templates.d.ts.map +1 -0
- package/dist/core/prompt-templates.js +236 -0
- package/dist/core/prompt-templates.js.map +1 -0
- package/dist/core/provider-composer.d.ts +58 -0
- package/dist/core/provider-composer.d.ts.map +1 -0
- package/dist/core/provider-composer.js +403 -0
- package/dist/core/provider-composer.js.map +1 -0
- package/dist/core/radius.d.ts +2 -0
- package/dist/core/radius.d.ts.map +1 -0
- package/dist/core/radius.js +2 -0
- package/dist/core/radius.js.map +1 -0
- package/dist/core/remote-catalog-provider.d.ts +5 -0
- package/dist/core/remote-catalog-provider.d.ts.map +1 -0
- package/dist/core/remote-catalog-provider.js +121 -0
- package/dist/core/remote-catalog-provider.js.map +1 -0
- package/dist/core/resolve-config-value.d.ts +30 -0
- package/dist/core/resolve-config-value.d.ts.map +1 -0
- package/dist/core/resolve-config-value.js +247 -0
- package/dist/core/resolve-config-value.js.map +1 -0
- package/dist/core/resource-loader.d.ts +211 -0
- package/dist/core/resource-loader.d.ts.map +1 -0
- package/dist/core/resource-loader.js +687 -0
- package/dist/core/resource-loader.js.map +1 -0
- package/dist/core/runtime-credentials.d.ts +15 -0
- package/dist/core/runtime-credentials.d.ts.map +1 -0
- package/dist/core/runtime-credentials.js +39 -0
- package/dist/core/runtime-credentials.js.map +1 -0
- package/dist/core/sdk.d.ts +395 -0
- package/dist/core/sdk.d.ts.map +1 -0
- package/dist/core/sdk.js +1112 -0
- package/dist/core/sdk.js.map +1 -0
- package/dist/core/session-cwd.d.ts +19 -0
- package/dist/core/session-cwd.d.ts.map +1 -0
- package/dist/core/session-cwd.js +38 -0
- package/dist/core/session-cwd.js.map +1 -0
- package/dist/core/session-export.d.ts +7 -0
- package/dist/core/session-export.d.ts.map +1 -0
- package/dist/core/session-export.js +48 -0
- package/dist/core/session-export.js.map +1 -0
- package/dist/core/session-manager.d.ts +463 -0
- package/dist/core/session-manager.d.ts.map +1 -0
- package/dist/core/session-manager.js +1583 -0
- package/dist/core/session-manager.js.map +1 -0
- package/dist/core/session-start.d.ts +12 -0
- package/dist/core/session-start.d.ts.map +1 -0
- package/dist/core/session-start.js +2 -0
- package/dist/core/session-start.js.map +1 -0
- package/dist/core/session-storage.d.ts +130 -0
- package/dist/core/session-storage.d.ts.map +1 -0
- package/dist/core/session-storage.js +386 -0
- package/dist/core/session-storage.js.map +1 -0
- package/dist/core/settings-diagnostics.d.ts +9 -0
- package/dist/core/settings-diagnostics.d.ts.map +1 -0
- package/dist/core/settings-diagnostics.js +21 -0
- package/dist/core/settings-diagnostics.js.map +1 -0
- package/dist/core/settings-manager.d.ts +352 -0
- package/dist/core/settings-manager.d.ts.map +1 -0
- package/dist/core/settings-manager.js +987 -0
- package/dist/core/settings-manager.js.map +1 -0
- package/dist/core/skills.d.ts +60 -0
- package/dist/core/skills.d.ts.map +1 -0
- package/dist/core/skills.js +382 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/slash-commands.d.ts +15 -0
- package/dist/core/slash-commands.d.ts.map +1 -0
- package/dist/core/slash-commands.js +33 -0
- package/dist/core/slash-commands.js.map +1 -0
- package/dist/core/source-info.d.ts +18 -0
- package/dist/core/source-info.d.ts.map +1 -0
- package/dist/core/source-info.js +19 -0
- package/dist/core/source-info.js.map +1 -0
- package/dist/core/system-prompt.d.ts +8 -0
- package/dist/core/system-prompt.d.ts.map +1 -0
- package/dist/core/system-prompt.js +137 -0
- package/dist/core/system-prompt.js.map +1 -0
- package/dist/core/timings.d.ts +11 -0
- package/dist/core/timings.d.ts.map +1 -0
- package/dist/core/timings.js +46 -0
- package/dist/core/timings.js.map +1 -0
- package/dist/core/tools/bash.d.ts +87 -0
- package/dist/core/tools/bash.d.ts.map +1 -0
- package/dist/core/tools/bash.js +409 -0
- package/dist/core/tools/bash.js.map +1 -0
- package/dist/core/tools/edit-diff.d.ts +110 -0
- package/dist/core/tools/edit-diff.d.ts.map +1 -0
- package/dist/core/tools/edit-diff.js +425 -0
- package/dist/core/tools/edit-diff.js.map +1 -0
- package/dist/core/tools/edit.d.ts +55 -0
- package/dist/core/tools/edit.d.ts.map +1 -0
- package/dist/core/tools/edit.js +305 -0
- package/dist/core/tools/edit.js.map +1 -0
- package/dist/core/tools/file-mutation-queue.d.ts +6 -0
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
- package/dist/core/tools/file-mutation-queue.js +52 -0
- package/dist/core/tools/file-mutation-queue.js.map +1 -0
- package/dist/core/tools/find.d.ts +42 -0
- package/dist/core/tools/find.d.ts.map +1 -0
- package/dist/core/tools/find.js +303 -0
- package/dist/core/tools/find.js.map +1 -0
- package/dist/core/tools/grep.d.ts +41 -0
- package/dist/core/tools/grep.d.ts.map +1 -0
- package/dist/core/tools/grep.js +309 -0
- package/dist/core/tools/grep.js.map +1 -0
- package/dist/core/tools/index.d.ts +44 -0
- package/dist/core/tools/index.d.ts.map +1 -0
- package/dist/core/tools/index.js +129 -0
- package/dist/core/tools/index.js.map +1 -0
- package/dist/core/tools/ls.d.ts +41 -0
- package/dist/core/tools/ls.d.ts.map +1 -0
- package/dist/core/tools/ls.js +173 -0
- package/dist/core/tools/ls.js.map +1 -0
- package/dist/core/tools/output-accumulator.d.ts +52 -0
- package/dist/core/tools/output-accumulator.d.ts.map +1 -0
- package/dist/core/tools/output-accumulator.js +184 -0
- package/dist/core/tools/output-accumulator.js.map +1 -0
- package/dist/core/tools/path-utils.d.ts +10 -0
- package/dist/core/tools/path-utils.d.ts.map +1 -0
- package/dist/core/tools/path-utils.js +99 -0
- package/dist/core/tools/path-utils.js.map +1 -0
- package/dist/core/tools/powershell.d.ts +16 -0
- package/dist/core/tools/powershell.d.ts.map +1 -0
- package/dist/core/tools/powershell.js +39 -0
- package/dist/core/tools/powershell.js.map +1 -0
- package/dist/core/tools/read.d.ts +39 -0
- package/dist/core/tools/read.d.ts.map +1 -0
- package/dist/core/tools/read.js +282 -0
- package/dist/core/tools/read.js.map +1 -0
- package/dist/core/tools/render-utils.d.ts +24 -0
- package/dist/core/tools/render-utils.d.ts.map +1 -0
- package/dist/core/tools/render-utils.js +65 -0
- package/dist/core/tools/render-utils.js.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts +15 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.js +36 -0
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
- package/dist/core/tools/tool-definition.d.ts +123 -0
- package/dist/core/tools/tool-definition.d.ts.map +1 -0
- package/dist/core/tools/tool-definition.js +20 -0
- package/dist/core/tools/tool-definition.js.map +1 -0
- package/dist/core/tools/tool-search.d.ts +30 -0
- package/dist/core/tools/tool-search.d.ts.map +1 -0
- package/dist/core/tools/tool-search.js +99 -0
- package/dist/core/tools/tool-search.js.map +1 -0
- package/dist/core/tools/truncate.d.ts +70 -0
- package/dist/core/tools/truncate.d.ts.map +1 -0
- package/dist/core/tools/truncate.js +215 -0
- package/dist/core/tools/truncate.js.map +1 -0
- package/dist/core/tools/write.d.ts +30 -0
- package/dist/core/tools/write.d.ts.map +1 -0
- package/dist/core/tools/write.js +202 -0
- package/dist/core/tools/write.js.map +1 -0
- package/dist/core/trace-retention.d.ts +19 -0
- package/dist/core/trace-retention.d.ts.map +1 -0
- package/dist/core/trace-retention.js +91 -0
- package/dist/core/trace-retention.js.map +1 -0
- package/dist/core/usage-totals.d.ts +21 -0
- package/dist/core/usage-totals.d.ts.map +1 -0
- package/dist/core/usage-totals.js +53 -0
- package/dist/core/usage-totals.js.map +1 -0
- package/dist/durable/durable-service.d.ts +136 -0
- package/dist/durable/durable-service.d.ts.map +1 -0
- package/dist/durable/durable-service.js +710 -0
- package/dist/durable/durable-service.js.map +1 -0
- package/dist/e2e/stdio-launcher.d.ts +25 -0
- package/dist/e2e/stdio-launcher.d.ts.map +1 -0
- package/dist/e2e/stdio-launcher.js +244 -0
- package/dist/e2e/stdio-launcher.js.map +1 -0
- package/dist/host/agent-retry-scheduler.d.ts +65 -0
- package/dist/host/agent-retry-scheduler.d.ts.map +1 -0
- package/dist/host/agent-retry-scheduler.js +52 -0
- package/dist/host/agent-retry-scheduler.js.map +1 -0
- package/dist/host/auto-compaction.d.ts +56 -0
- package/dist/host/auto-compaction.d.ts.map +1 -0
- package/dist/host/auto-compaction.js +62 -0
- package/dist/host/auto-compaction.js.map +1 -0
- package/dist/host/branch-reload.d.ts +56 -0
- package/dist/host/branch-reload.d.ts.map +1 -0
- package/dist/host/branch-reload.js +39 -0
- package/dist/host/branch-reload.js.map +1 -0
- package/dist/host/channel-adapter.d.ts +25 -0
- package/dist/host/channel-adapter.d.ts.map +1 -0
- package/dist/host/channel-adapter.js +45 -0
- package/dist/host/channel-adapter.js.map +1 -0
- package/dist/host/checkpoint-commit.d.ts +74 -0
- package/dist/host/checkpoint-commit.d.ts.map +1 -0
- package/dist/host/checkpoint-commit.js +76 -0
- package/dist/host/checkpoint-commit.js.map +1 -0
- package/dist/host/manual-compaction.d.ts +43 -0
- package/dist/host/manual-compaction.d.ts.map +1 -0
- package/dist/host/manual-compaction.js +63 -0
- package/dist/host/manual-compaction.js.map +1 -0
- package/dist/host/output-projection.d.ts +61 -0
- package/dist/host/output-projection.d.ts.map +1 -0
- package/dist/host/output-projection.js +74 -0
- package/dist/host/output-projection.js.map +1 -0
- package/dist/host/output-reducer.d.ts +43 -0
- package/dist/host/output-reducer.d.ts.map +1 -0
- package/dist/host/output-reducer.js +77 -0
- package/dist/host/output-reducer.js.map +1 -0
- package/dist/host/output-serializer.d.ts +31 -0
- package/dist/host/output-serializer.d.ts.map +1 -0
- package/dist/host/output-serializer.js +120 -0
- package/dist/host/output-serializer.js.map +1 -0
- package/dist/host/overflow-recovery.d.ts +55 -0
- package/dist/host/overflow-recovery.d.ts.map +1 -0
- package/dist/host/overflow-recovery.js +89 -0
- package/dist/host/overflow-recovery.js.map +1 -0
- package/dist/host/retry-common.d.ts +29 -0
- package/dist/host/retry-common.d.ts.map +1 -0
- package/dist/host/retry-common.js +12 -0
- package/dist/host/retry-common.js.map +1 -0
- package/dist/host/session-event-translation.d.ts +26 -0
- package/dist/host/session-event-translation.d.ts.map +1 -0
- package/dist/host/session-event-translation.js +61 -0
- package/dist/host/session-event-translation.js.map +1 -0
- package/dist/host/summary-retry.d.ts +54 -0
- package/dist/host/summary-retry.d.ts.map +1 -0
- package/dist/host/summary-retry.js +66 -0
- package/dist/host/summary-retry.js.map +1 -0
- package/dist/index.d.ts +105 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +176 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +36 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +1630 -0
- package/dist/main.js.map +1 -0
- package/dist/migration/migration-tool.d.ts +56 -0
- package/dist/migration/migration-tool.d.ts.map +1 -0
- package/dist/migration/migration-tool.js +72 -0
- package/dist/migration/migration-tool.js.map +1 -0
- package/dist/migrations.d.ts +33 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +272 -0
- package/dist/migrations.js.map +1 -0
- package/dist/modes/index.d.ts +10 -0
- package/dist/modes/index.d.ts.map +1 -0
- package/dist/modes/index.js +8 -0
- package/dist/modes/index.js.map +1 -0
- package/dist/modes/interactive/assets/clankolas.png +0 -0
- package/dist/modes/interactive/components/armin.d.ts +34 -0
- package/dist/modes/interactive/components/armin.d.ts.map +1 -0
- package/dist/modes/interactive/components/armin.js +336 -0
- package/dist/modes/interactive/components/armin.js.map +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts +25 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/assistant-message.js +151 -0
- package/dist/modes/interactive/components/assistant-message.js.map +1 -0
- package/dist/modes/interactive/components/bash-execution.d.ts +34 -0
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -0
- package/dist/modes/interactive/components/bash-execution.js +176 -0
- package/dist/modes/interactive/components/bash-execution.js.map +1 -0
- package/dist/modes/interactive/components/bordered-loader.d.ts +16 -0
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -0
- package/dist/modes/interactive/components/bordered-loader.js +56 -0
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -0
- package/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/branch-summary-message.js +44 -0
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -0
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/compaction-summary-message.js +45 -0
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -0
- package/dist/modes/interactive/components/config-selector.d.ts +113 -0
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/config-selector.js +857 -0
- package/dist/modes/interactive/components/config-selector.js.map +1 -0
- package/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
- package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -0
- package/dist/modes/interactive/components/countdown-timer.js +33 -0
- package/dist/modes/interactive/components/countdown-timer.js.map +1 -0
- package/dist/modes/interactive/components/custom-editor.d.ts +19 -0
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -0
- package/dist/modes/interactive/components/custom-editor.js +71 -0
- package/dist/modes/interactive/components/custom-editor.js.map +1 -0
- package/dist/modes/interactive/components/custom-entry.d.ts +19 -0
- package/dist/modes/interactive/components/custom-entry.d.ts.map +1 -0
- package/dist/modes/interactive/components/custom-entry.js +52 -0
- package/dist/modes/interactive/components/custom-entry.js.map +1 -0
- package/dist/modes/interactive/components/custom-message.d.ts +22 -0
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/custom-message.js +87 -0
- package/dist/modes/interactive/components/custom-message.js.map +1 -0
- package/dist/modes/interactive/components/daxnuts.d.ts +23 -0
- package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -0
- package/dist/modes/interactive/components/daxnuts.js +141 -0
- package/dist/modes/interactive/components/daxnuts.js.map +1 -0
- package/dist/modes/interactive/components/dialog-editor.d.ts +21 -0
- package/dist/modes/interactive/components/dialog-editor.d.ts.map +1 -0
- package/dist/modes/interactive/components/dialog-editor.js +98 -0
- package/dist/modes/interactive/components/dialog-editor.js.map +1 -0
- package/dist/modes/interactive/components/dialog-input.d.ts +23 -0
- package/dist/modes/interactive/components/dialog-input.d.ts.map +1 -0
- package/dist/modes/interactive/components/dialog-input.js +61 -0
- package/dist/modes/interactive/components/dialog-input.js.map +1 -0
- package/dist/modes/interactive/components/dialog-selector.d.ts +26 -0
- package/dist/modes/interactive/components/dialog-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/dialog-selector.js +83 -0
- package/dist/modes/interactive/components/dialog-selector.js.map +1 -0
- package/dist/modes/interactive/components/diff.d.ts +12 -0
- package/dist/modes/interactive/components/diff.d.ts.map +1 -0
- package/dist/modes/interactive/components/diff.js +133 -0
- package/dist/modes/interactive/components/diff.js.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.js +21 -0
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -0
- package/dist/modes/interactive/components/first-time-setup.d.ts +25 -0
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -0
- package/dist/modes/interactive/components/first-time-setup.js +103 -0
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts +33 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -0
- package/dist/modes/interactive/components/footer.js +248 -0
- package/dist/modes/interactive/components/footer.js.map +1 -0
- package/dist/modes/interactive/components/index.d.ts +34 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -0
- package/dist/modes/interactive/components/index.js +35 -0
- package/dist/modes/interactive/components/index.js.map +1 -0
- package/dist/modes/interactive/components/keybinding-hints.d.ts +13 -0
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -0
- package/dist/modes/interactive/components/keybinding-hints.js +36 -0
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -0
- package/dist/modes/interactive/components/login-dialog.d.ts +52 -0
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -0
- package/dist/modes/interactive/components/login-dialog.js +188 -0
- package/dist/modes/interactive/components/login-dialog.js.map +1 -0
- package/dist/modes/interactive/components/markdown-transform.d.ts +3 -0
- package/dist/modes/interactive/components/markdown-transform.d.ts.map +1 -0
- package/dist/modes/interactive/components/markdown-transform.js +19 -0
- package/dist/modes/interactive/components/markdown-transform.js.map +1 -0
- package/dist/modes/interactive/components/mermaid.d.ts +11 -0
- package/dist/modes/interactive/components/mermaid.d.ts.map +1 -0
- package/dist/modes/interactive/components/mermaid.js +75 -0
- package/dist/modes/interactive/components/mermaid.js.map +1 -0
- package/dist/modes/interactive/components/model-selector.d.ts +67 -0
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/model-selector.js +381 -0
- package/dist/modes/interactive/components/model-selector.js.map +1 -0
- package/dist/modes/interactive/components/oauth-selector.d.ts +33 -0
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/oauth-selector.js +165 -0
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -0
- package/dist/modes/interactive/components/plugin-manager-entry.d.ts +62 -0
- package/dist/modes/interactive/components/plugin-manager-entry.d.ts.map +1 -0
- package/dist/modes/interactive/components/plugin-manager-entry.js +294 -0
- package/dist/modes/interactive/components/plugin-manager-entry.js.map +1 -0
- package/dist/modes/interactive/components/plugin-selection-entry.d.ts +82 -0
- package/dist/modes/interactive/components/plugin-selection-entry.d.ts.map +1 -0
- package/dist/modes/interactive/components/plugin-selection-entry.js +228 -0
- package/dist/modes/interactive/components/plugin-selection-entry.js.map +1 -0
- package/dist/modes/interactive/components/render-types.d.ts +26 -0
- package/dist/modes/interactive/components/render-types.d.ts.map +1 -0
- package/dist/modes/interactive/components/render-types.js +7 -0
- package/dist/modes/interactive/components/render-types.js.map +1 -0
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +46 -0
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/scoped-models-selector.js +328 -0
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -0
- package/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -0
- package/dist/modes/interactive/components/session-selector-search.js +158 -0
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -0
- package/dist/modes/interactive/components/session-selector.d.ts +143 -0
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/session-selector.js +1053 -0
- package/dist/modes/interactive/components/session-selector.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts +88 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/settings-selector.js +677 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -0
- package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
- package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
- package/dist/modes/interactive/components/settings-submenu.js +165 -0
- package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
- package/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/show-images-selector.js +39 -0
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -0
- package/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/skill-invocation-message.js +47 -0
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -0
- package/dist/modes/interactive/components/status-indicator.d.ts +34 -0
- package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -0
- package/dist/modes/interactive/components/status-indicator.js +60 -0
- package/dist/modes/interactive/components/status-indicator.js.map +1 -0
- package/dist/modes/interactive/components/theme-selector.d.ts +11 -0
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/theme-selector.js +50 -0
- package/dist/modes/interactive/components/theme-selector.js.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts +23 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.js +109 -0
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution.d.ts +59 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-execution.js +335 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -0
- package/dist/modes/interactive/components/tree-list.d.ts +67 -0
- package/dist/modes/interactive/components/tree-list.d.ts.map +1 -0
- package/dist/modes/interactive/components/tree-list.js +710 -0
- package/dist/modes/interactive/components/tree-list.js.map +1 -0
- package/dist/modes/interactive/components/tree-render.d.ts +17 -0
- package/dist/modes/interactive/components/tree-render.d.ts.map +1 -0
- package/dist/modes/interactive/components/tree-render.js +316 -0
- package/dist/modes/interactive/components/tree-render.js.map +1 -0
- package/dist/modes/interactive/components/tree-selector.d.ts +25 -0
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/tree-selector.js +235 -0
- package/dist/modes/interactive/components/tree-selector.js.map +1 -0
- package/dist/modes/interactive/components/tree-types.d.ts +35 -0
- package/dist/modes/interactive/components/tree-types.d.ts.map +1 -0
- package/dist/modes/interactive/components/tree-types.js +2 -0
- package/dist/modes/interactive/components/tree-types.js.map +1 -0
- package/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/user-message-selector.js +114 -0
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -0
- package/dist/modes/interactive/components/user-message.d.ts +16 -0
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/user-message.js +49 -0
- package/dist/modes/interactive/components/user-message.js.map +1 -0
- package/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
- package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -0
- package/dist/modes/interactive/components/visual-truncate.js +33 -0
- package/dist/modes/interactive/components/visual-truncate.js.map +1 -0
- package/dist/modes/interactive/external-editor.d.ts +12 -0
- package/dist/modes/interactive/external-editor.d.ts.map +1 -0
- package/dist/modes/interactive/external-editor.js +38 -0
- package/dist/modes/interactive/external-editor.js.map +1 -0
- package/dist/modes/interactive/interactive-auth.d.ts +90 -0
- package/dist/modes/interactive/interactive-auth.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-auth.js +492 -0
- package/dist/modes/interactive/interactive-auth.js.map +1 -0
- package/dist/modes/interactive/interactive-commands.d.ts +68 -0
- package/dist/modes/interactive/interactive-commands.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-commands.js +157 -0
- package/dist/modes/interactive/interactive-commands.js.map +1 -0
- package/dist/modes/interactive/interactive-dialog-ui.d.ts +73 -0
- package/dist/modes/interactive/interactive-dialog-ui.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-dialog-ui.js +189 -0
- package/dist/modes/interactive/interactive-dialog-ui.js.map +1 -0
- package/dist/modes/interactive/interactive-format.d.ts +65 -0
- package/dist/modes/interactive/interactive-format.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-format.js +268 -0
- package/dist/modes/interactive/interactive-format.js.map +1 -0
- package/dist/modes/interactive/interactive-helpers.d.ts +25 -0
- package/dist/modes/interactive/interactive-helpers.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-helpers.js +73 -0
- package/dist/modes/interactive/interactive-helpers.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +640 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-mode.js +4107 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -0
- package/dist/modes/interactive/interactive-selectors.d.ts +95 -0
- package/dist/modes/interactive/interactive-selectors.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-selectors.js +356 -0
- package/dist/modes/interactive/interactive-selectors.js.map +1 -0
- package/dist/modes/interactive/model-catalog-refresh.d.ts +11 -0
- package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
- package/dist/modes/interactive/model-catalog-refresh.js +35 -0
- package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
- package/dist/modes/interactive/model-search.d.ts +12 -0
- package/dist/modes/interactive/model-search.d.ts.map +1 -0
- package/dist/modes/interactive/model-search.js +15 -0
- package/dist/modes/interactive/model-search.js.map +1 -0
- package/dist/modes/interactive/theme/dark.json +90 -0
- package/dist/modes/interactive/theme/light.json +89 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +40 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -0
- package/dist/modes/interactive/theme/theme-controller.js +126 -0
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -0
- package/dist/modes/interactive/theme/theme-schema.json +352 -0
- package/dist/modes/interactive/theme/theme.d.ts +122 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -0
- package/dist/modes/interactive/theme/theme.js +1093 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -0
- package/dist/modes/json-event.d.ts +34 -0
- package/dist/modes/json-event.d.ts.map +1 -0
- package/dist/modes/json-event.js +29 -0
- package/dist/modes/json-event.js.map +1 -0
- package/dist/modes/print-mode.d.ts +32 -0
- package/dist/modes/print-mode.d.ts.map +1 -0
- package/dist/modes/print-mode.js +205 -0
- package/dist/modes/print-mode.js.map +1 -0
- package/dist/modes/rpc/jsonl.d.ts +17 -0
- package/dist/modes/rpc/jsonl.d.ts.map +1 -0
- package/dist/modes/rpc/jsonl.js +49 -0
- package/dist/modes/rpc/jsonl.js.map +1 -0
- package/dist/modes/rpc/rpc-client.d.ts +254 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-client.js +496 -0
- package/dist/modes/rpc/rpc-client.js.map +1 -0
- package/dist/modes/rpc/rpc-mode.d.ts +53 -0
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-mode.js +540 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -0
- package/dist/modes/rpc/rpc-types.d.ts +486 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-types.js +8 -0
- package/dist/modes/rpc/rpc-types.js.map +1 -0
- package/dist/package-manager-cli.d.ts +18 -0
- package/dist/package-manager-cli.d.ts.map +1 -0
- package/dist/package-manager-cli.js +944 -0
- package/dist/package-manager-cli.js.map +1 -0
- package/dist/plugins/ecosystem/active-bindings.d.ts +92 -0
- package/dist/plugins/ecosystem/active-bindings.d.ts.map +1 -0
- package/dist/plugins/ecosystem/active-bindings.js +198 -0
- package/dist/plugins/ecosystem/active-bindings.js.map +1 -0
- package/dist/plugins/ecosystem/bootstrap-factory-manifest.d.ts +32 -0
- package/dist/plugins/ecosystem/bootstrap-factory-manifest.d.ts.map +1 -0
- package/dist/plugins/ecosystem/bootstrap-factory-manifest.js +32 -0
- package/dist/plugins/ecosystem/bootstrap-factory-manifest.js.map +1 -0
- package/dist/plugins/ecosystem/catalog.d.ts +38 -0
- package/dist/plugins/ecosystem/catalog.d.ts.map +1 -0
- package/dist/plugins/ecosystem/catalog.js +90 -0
- package/dist/plugins/ecosystem/catalog.js.map +1 -0
- package/dist/plugins/ecosystem/cli.d.ts +102 -0
- package/dist/plugins/ecosystem/cli.d.ts.map +1 -0
- package/dist/plugins/ecosystem/cli.js +970 -0
- package/dist/plugins/ecosystem/cli.js.map +1 -0
- package/dist/plugins/ecosystem/default-active-bindings.d.ts +33 -0
- package/dist/plugins/ecosystem/default-active-bindings.d.ts.map +1 -0
- package/dist/plugins/ecosystem/default-active-bindings.js +105 -0
- package/dist/plugins/ecosystem/default-active-bindings.js.map +1 -0
- package/dist/plugins/ecosystem/default-plugin-market.d.ts +68 -0
- package/dist/plugins/ecosystem/default-plugin-market.d.ts.map +1 -0
- package/dist/plugins/ecosystem/default-plugin-market.js +146 -0
- package/dist/plugins/ecosystem/default-plugin-market.js.map +1 -0
- package/dist/plugins/ecosystem/distribution.d.ts +113 -0
- package/dist/plugins/ecosystem/distribution.d.ts.map +1 -0
- package/dist/plugins/ecosystem/distribution.js +371 -0
- package/dist/plugins/ecosystem/distribution.js.map +1 -0
- package/dist/plugins/ecosystem/index.d.ts +56 -0
- package/dist/plugins/ecosystem/index.d.ts.map +1 -0
- package/dist/plugins/ecosystem/index.js +31 -0
- package/dist/plugins/ecosystem/index.js.map +1 -0
- package/dist/plugins/ecosystem/installer.d.ts +130 -0
- package/dist/plugins/ecosystem/installer.d.ts.map +1 -0
- package/dist/plugins/ecosystem/installer.js +316 -0
- package/dist/plugins/ecosystem/installer.js.map +1 -0
- package/dist/plugins/ecosystem/node-disk-registry-source.d.ts +47 -0
- package/dist/plugins/ecosystem/node-disk-registry-source.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-disk-registry-source.js +208 -0
- package/dist/plugins/ecosystem/node-disk-registry-source.js.map +1 -0
- package/dist/plugins/ecosystem/node-distribution-host.d.ts +55 -0
- package/dist/plugins/ecosystem/node-distribution-host.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-distribution-host.js +423 -0
- package/dist/plugins/ecosystem/node-distribution-host.js.map +1 -0
- package/dist/plugins/ecosystem/node-local-package-source.d.ts +10 -0
- package/dist/plugins/ecosystem/node-local-package-source.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-local-package-source.js +97 -0
- package/dist/plugins/ecosystem/node-local-package-source.js.map +1 -0
- package/dist/plugins/ecosystem/node-npm-registry-source.d.ts +58 -0
- package/dist/plugins/ecosystem/node-npm-registry-source.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-npm-registry-source.js +326 -0
- package/dist/plugins/ecosystem/node-npm-registry-source.js.map +1 -0
- package/dist/plugins/ecosystem/node-package-install-host.d.ts +91 -0
- package/dist/plugins/ecosystem/node-package-install-host.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-package-install-host.js +857 -0
- package/dist/plugins/ecosystem/node-package-install-host.js.map +1 -0
- package/dist/plugins/ecosystem/node-package-install-persistence.d.ts +34 -0
- package/dist/plugins/ecosystem/node-package-install-persistence.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-package-install-persistence.js +221 -0
- package/dist/plugins/ecosystem/node-package-install-persistence.js.map +1 -0
- package/dist/plugins/ecosystem/node-package-install-stack.d.ts +28 -0
- package/dist/plugins/ecosystem/node-package-install-stack.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-package-install-stack.js +65 -0
- package/dist/plugins/ecosystem/node-package-install-stack.js.map +1 -0
- package/dist/plugins/ecosystem/node-package-media.d.ts +14 -0
- package/dist/plugins/ecosystem/node-package-media.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-package-media.js +36 -0
- package/dist/plugins/ecosystem/node-package-media.js.map +1 -0
- package/dist/plugins/ecosystem/node-plugin-acquisition-host.d.ts +83 -0
- package/dist/plugins/ecosystem/node-plugin-acquisition-host.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-plugin-acquisition-host.js +423 -0
- package/dist/plugins/ecosystem/node-plugin-acquisition-host.js.map +1 -0
- package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts +48 -0
- package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-plugin-bootstrap.js +191 -0
- package/dist/plugins/ecosystem/node-plugin-bootstrap.js.map +1 -0
- package/dist/plugins/ecosystem/node-plugin-trust-root.d.ts +26 -0
- package/dist/plugins/ecosystem/node-plugin-trust-root.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-plugin-trust-root.js +120 -0
- package/dist/plugins/ecosystem/node-plugin-trust-root.js.map +1 -0
- package/dist/plugins/ecosystem/node-registry-source.d.ts +35 -0
- package/dist/plugins/ecosystem/node-registry-source.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-registry-source.js +286 -0
- package/dist/plugins/ecosystem/node-registry-source.js.map +1 -0
- package/dist/plugins/ecosystem/node-release-package-source.d.ts +47 -0
- package/dist/plugins/ecosystem/node-release-package-source.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-release-package-source.js +336 -0
- package/dist/plugins/ecosystem/node-release-package-source.js.map +1 -0
- package/dist/plugins/ecosystem/node-tar-package.d.ts +11 -0
- package/dist/plugins/ecosystem/node-tar-package.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-tar-package.js +294 -0
- package/dist/plugins/ecosystem/node-tar-package.js.map +1 -0
- package/dist/plugins/ecosystem/node-tar-writer.d.ts +16 -0
- package/dist/plugins/ecosystem/node-tar-writer.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-tar-writer.js +134 -0
- package/dist/plugins/ecosystem/node-tar-writer.js.map +1 -0
- package/dist/plugins/ecosystem/node-url-package-source.d.ts +31 -0
- package/dist/plugins/ecosystem/node-url-package-source.d.ts.map +1 -0
- package/dist/plugins/ecosystem/node-url-package-source.js +159 -0
- package/dist/plugins/ecosystem/node-url-package-source.js.map +1 -0
- package/dist/plugins/ecosystem/plugin-acquisition.d.ts +88 -0
- package/dist/plugins/ecosystem/plugin-acquisition.d.ts.map +1 -0
- package/dist/plugins/ecosystem/plugin-acquisition.js +197 -0
- package/dist/plugins/ecosystem/plugin-acquisition.js.map +1 -0
- package/dist/plugins/ecosystem/plugin-bootstrap.d.ts +87 -0
- package/dist/plugins/ecosystem/plugin-bootstrap.d.ts.map +1 -0
- package/dist/plugins/ecosystem/plugin-bootstrap.js +134 -0
- package/dist/plugins/ecosystem/plugin-bootstrap.js.map +1 -0
- package/dist/plugins/ecosystem/registry-cli-adapter.d.ts +8 -0
- package/dist/plugins/ecosystem/registry-cli-adapter.d.ts.map +1 -0
- package/dist/plugins/ecosystem/registry-cli-adapter.js +16 -0
- package/dist/plugins/ecosystem/registry-cli-adapter.js.map +1 -0
- package/dist/plugins/ecosystem/registry.d.ts +121 -0
- package/dist/plugins/ecosystem/registry.d.ts.map +1 -0
- package/dist/plugins/ecosystem/registry.js +324 -0
- package/dist/plugins/ecosystem/registry.js.map +1 -0
- package/dist/plugins/ecosystem/resolver.d.ts +32 -0
- package/dist/plugins/ecosystem/resolver.d.ts.map +1 -0
- package/dist/plugins/ecosystem/resolver.js +325 -0
- package/dist/plugins/ecosystem/resolver.js.map +1 -0
- package/dist/plugins/ecosystem/selection-store.d.ts +66 -0
- package/dist/plugins/ecosystem/selection-store.d.ts.map +1 -0
- package/dist/plugins/ecosystem/selection-store.js +241 -0
- package/dist/plugins/ecosystem/selection-store.js.map +1 -0
- package/dist/plugins/ecosystem/selection.d.ts +54 -0
- package/dist/plugins/ecosystem/selection.d.ts.map +1 -0
- package/dist/plugins/ecosystem/selection.js +116 -0
- package/dist/plugins/ecosystem/selection.js.map +1 -0
- package/dist/plugins/ecosystem/state.d.ts +96 -0
- package/dist/plugins/ecosystem/state.d.ts.map +1 -0
- package/dist/plugins/ecosystem/state.js +346 -0
- package/dist/plugins/ecosystem/state.js.map +1 -0
- package/dist/plugins/ecosystem/types.d.ts +90 -0
- package/dist/plugins/ecosystem/types.d.ts.map +1 -0
- package/dist/plugins/ecosystem/types.js +8 -0
- package/dist/plugins/ecosystem/types.js.map +1 -0
- package/dist/plugins/ecosystem/updates.d.ts +30 -0
- package/dist/plugins/ecosystem/updates.d.ts.map +1 -0
- package/dist/plugins/ecosystem/updates.js +148 -0
- package/dist/plugins/ecosystem/updates.js.map +1 -0
- package/dist/plugins/integrity-adapter.d.ts +58 -0
- package/dist/plugins/integrity-adapter.d.ts.map +1 -0
- package/dist/plugins/integrity-adapter.js +70 -0
- package/dist/plugins/integrity-adapter.js.map +1 -0
- package/dist/plugins/mcp-http.d.ts +78 -0
- package/dist/plugins/mcp-http.d.ts.map +1 -0
- package/dist/plugins/mcp-http.js +176 -0
- package/dist/plugins/mcp-http.js.map +1 -0
- package/dist/plugins/observability-types.d.ts +9 -0
- package/dist/plugins/observability-types.d.ts.map +1 -0
- package/dist/plugins/observability-types.js +9 -0
- package/dist/plugins/observability-types.js.map +1 -0
- package/dist/plugins/plugin-host.d.ts +46 -0
- package/dist/plugins/plugin-host.d.ts.map +1 -0
- package/dist/plugins/plugin-host.js +80 -0
- package/dist/plugins/plugin-host.js.map +1 -0
- package/dist/plugins/plugin-supervisor.d.ts +77 -0
- package/dist/plugins/plugin-supervisor.d.ts.map +1 -0
- package/dist/plugins/plugin-supervisor.js +82 -0
- package/dist/plugins/plugin-supervisor.js.map +1 -0
- package/dist/plugins/subagent-task/runner-entry.d.ts +35 -0
- package/dist/plugins/subagent-task/runner-entry.d.ts.map +1 -0
- package/dist/plugins/subagent-task/runner-entry.js +39 -0
- package/dist/plugins/subagent-task/runner-entry.js.map +1 -0
- package/dist/plugins/subagent-task/session-factory.d.ts +57 -0
- package/dist/plugins/subagent-task/session-factory.d.ts.map +1 -0
- package/dist/plugins/subagent-task/session-factory.js +51 -0
- package/dist/plugins/subagent-task/session-factory.js.map +1 -0
- package/dist/profile/child-facade.d.ts +44 -0
- package/dist/profile/child-facade.d.ts.map +1 -0
- package/dist/profile/child-facade.js +56 -0
- package/dist/profile/child-facade.js.map +1 -0
- package/dist/profile/dependency-wiring.d.ts +48 -0
- package/dist/profile/dependency-wiring.d.ts.map +1 -0
- package/dist/profile/dependency-wiring.js +56 -0
- package/dist/profile/dependency-wiring.js.map +1 -0
- package/dist/profile/domain-recovery.d.ts +60 -0
- package/dist/profile/domain-recovery.d.ts.map +1 -0
- package/dist/profile/domain-recovery.js +94 -0
- package/dist/profile/domain-recovery.js.map +1 -0
- package/dist/profile/first-roles.d.ts +30 -0
- package/dist/profile/first-roles.d.ts.map +1 -0
- package/dist/profile/first-roles.js +74 -0
- package/dist/profile/first-roles.js.map +1 -0
- package/dist/profile/goal-plugin.d.ts +38 -0
- package/dist/profile/goal-plugin.d.ts.map +1 -0
- package/dist/profile/goal-plugin.js +113 -0
- package/dist/profile/goal-plugin.js.map +1 -0
- package/dist/profile/identity.d.ts +58 -0
- package/dist/profile/identity.d.ts.map +1 -0
- package/dist/profile/identity.js +100 -0
- package/dist/profile/identity.js.map +1 -0
- package/dist/profile/lock.d.ts +59 -0
- package/dist/profile/lock.d.ts.map +1 -0
- package/dist/profile/lock.js +59 -0
- package/dist/profile/lock.js.map +1 -0
- package/dist/profile/plan-plugin.d.ts +44 -0
- package/dist/profile/plan-plugin.d.ts.map +1 -0
- package/dist/profile/plan-plugin.js +144 -0
- package/dist/profile/plan-plugin.js.map +1 -0
- package/dist/profile/plugin-profiles.d.ts +56 -0
- package/dist/profile/plugin-profiles.d.ts.map +1 -0
- package/dist/profile/plugin-profiles.js +192 -0
- package/dist/profile/plugin-profiles.js.map +1 -0
- package/dist/profile/policy-manifest.d.ts +53 -0
- package/dist/profile/policy-manifest.d.ts.map +1 -0
- package/dist/profile/policy-manifest.js +52 -0
- package/dist/profile/policy-manifest.js.map +1 -0
- package/dist/profile/profile-assembly.d.ts +74 -0
- package/dist/profile/profile-assembly.d.ts.map +1 -0
- package/dist/profile/profile-assembly.js +90 -0
- package/dist/profile/profile-assembly.js.map +1 -0
- package/dist/profile/profile-migration.d.ts +56 -0
- package/dist/profile/profile-migration.d.ts.map +1 -0
- package/dist/profile/profile-migration.js +60 -0
- package/dist/profile/profile-migration.js.map +1 -0
- package/dist/profile/profile-registry.d.ts +58 -0
- package/dist/profile/profile-registry.d.ts.map +1 -0
- package/dist/profile/profile-registry.js +74 -0
- package/dist/profile/profile-registry.js.map +1 -0
- package/dist/profile/role-assembly.d.ts +47 -0
- package/dist/profile/role-assembly.d.ts.map +1 -0
- package/dist/profile/role-assembly.js +31 -0
- package/dist/profile/role-assembly.js.map +1 -0
- package/dist/profile/role-catalog.d.ts +44 -0
- package/dist/profile/role-catalog.d.ts.map +1 -0
- package/dist/profile/role-catalog.js +130 -0
- package/dist/profile/role-catalog.js.map +1 -0
- package/dist/profile/role-orchestrator.d.ts +123 -0
- package/dist/profile/role-orchestrator.d.ts.map +1 -0
- package/dist/profile/role-orchestrator.js +194 -0
- package/dist/profile/role-orchestrator.js.map +1 -0
- package/dist/profile/role-runner.d.ts +99 -0
- package/dist/profile/role-runner.d.ts.map +1 -0
- package/dist/profile/role-runner.js +78 -0
- package/dist/profile/role-runner.js.map +1 -0
- package/dist/profile/root-facade.d.ts +36 -0
- package/dist/profile/root-facade.d.ts.map +1 -0
- package/dist/profile/root-facade.js +48 -0
- package/dist/profile/root-facade.js.map +1 -0
- package/dist/profile/skill-discovery-wiring.d.ts +58 -0
- package/dist/profile/skill-discovery-wiring.d.ts.map +1 -0
- package/dist/profile/skill-discovery-wiring.js +75 -0
- package/dist/profile/skill-discovery-wiring.js.map +1 -0
- package/dist/profile/state-store.d.ts +62 -0
- package/dist/profile/state-store.d.ts.map +1 -0
- package/dist/profile/state-store.js +140 -0
- package/dist/profile/state-store.js.map +1 -0
- package/dist/profile/todo-plugin.d.ts +37 -0
- package/dist/profile/todo-plugin.d.ts.map +1 -0
- package/dist/profile/todo-plugin.js +132 -0
- package/dist/profile/todo-plugin.js.map +1 -0
- package/dist/profiles/builtin-suites.d.ts +142 -0
- package/dist/profiles/builtin-suites.d.ts.map +1 -0
- package/dist/profiles/builtin-suites.js +395 -0
- package/dist/profiles/builtin-suites.js.map +1 -0
- package/dist/profiles/resolved-agent-profile.d.ts +83 -0
- package/dist/profiles/resolved-agent-profile.d.ts.map +1 -0
- package/dist/profiles/resolved-agent-profile.js +75 -0
- package/dist/profiles/resolved-agent-profile.js.map +1 -0
- package/dist/profiles/suite-lifecycle.d.ts +158 -0
- package/dist/profiles/suite-lifecycle.d.ts.map +1 -0
- package/dist/profiles/suite-lifecycle.js +447 -0
- package/dist/profiles/suite-lifecycle.js.map +1 -0
- package/dist/profiles/suite-loader.d.ts +410 -0
- package/dist/profiles/suite-loader.d.ts.map +1 -0
- package/dist/profiles/suite-loader.js +823 -0
- package/dist/profiles/suite-loader.js.map +1 -0
- package/dist/remote/server-assembly.d.ts +230 -0
- package/dist/remote/server-assembly.d.ts.map +1 -0
- package/dist/remote/server-assembly.js +1189 -0
- package/dist/remote/server-assembly.js.map +1 -0
- package/dist/remote/server-entry.d.ts +112 -0
- package/dist/remote/server-entry.d.ts.map +1 -0
- package/dist/remote/server-entry.js +239 -0
- package/dist/remote/server-entry.js.map +1 -0
- package/dist/remote/session-source.d.ts +101 -0
- package/dist/remote/session-source.d.ts.map +1 -0
- package/dist/remote/session-source.js +173 -0
- package/dist/remote/session-source.js.map +1 -0
- package/dist/rpc-entry.d.ts +3 -0
- package/dist/rpc-entry.d.ts.map +1 -0
- package/dist/rpc-entry.js +11 -0
- package/dist/rpc-entry.js.map +1 -0
- package/dist/server/create-harness.d.ts +26 -0
- package/dist/server/create-harness.d.ts.map +1 -0
- package/dist/server/create-harness.js +106 -0
- package/dist/server/create-harness.js.map +1 -0
- package/dist/share/session-share.d.ts +98 -0
- package/dist/share/session-share.d.ts.map +1 -0
- package/dist/share/session-share.js +89 -0
- package/dist/share/session-share.js.map +1 -0
- package/dist/share/share-viewer.d.ts +43 -0
- package/dist/share/share-viewer.d.ts.map +1 -0
- package/dist/share/share-viewer.js +103 -0
- package/dist/share/share-viewer.js.map +1 -0
- package/dist/testing/index.d.ts +4 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +6 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/mock-ai-server.d.ts +77 -0
- package/dist/testing/mock-ai-server.d.ts.map +1 -0
- package/dist/testing/mock-ai-server.js +69 -0
- package/dist/testing/mock-ai-server.js.map +1 -0
- package/dist/testing/plugin-testkit.d.ts +148 -0
- package/dist/testing/plugin-testkit.d.ts.map +1 -0
- package/dist/testing/plugin-testkit.js +493 -0
- package/dist/testing/plugin-testkit.js.map +1 -0
- package/dist/testing/runtime-delivery-queue.d.ts +47 -0
- package/dist/testing/runtime-delivery-queue.d.ts.map +1 -0
- package/dist/testing/runtime-delivery-queue.js +158 -0
- package/dist/testing/runtime-delivery-queue.js.map +1 -0
- package/dist/testing/session-plugin-testkit.d.ts +144 -0
- package/dist/testing/session-plugin-testkit.d.ts.map +1 -0
- package/dist/testing/session-plugin-testkit.js +444 -0
- package/dist/testing/session-plugin-testkit.js.map +1 -0
- package/dist/utils/abort.d.ts +5 -0
- package/dist/utils/abort.d.ts.map +1 -0
- package/dist/utils/abort.js +53 -0
- package/dist/utils/abort.js.map +1 -0
- package/dist/utils/agent-forge-user-agent.d.ts +2 -0
- package/dist/utils/agent-forge-user-agent.d.ts.map +1 -0
- package/dist/utils/agent-forge-user-agent.js +5 -0
- package/dist/utils/agent-forge-user-agent.js.map +1 -0
- package/dist/utils/ansi.d.ts +2 -0
- package/dist/utils/ansi.d.ts.map +1 -0
- package/dist/utils/ansi.js +53 -0
- package/dist/utils/ansi.js.map +1 -0
- package/dist/utils/changelog.d.ts +22 -0
- package/dist/utils/changelog.d.ts.map +1 -0
- package/dist/utils/changelog.js +165 -0
- package/dist/utils/changelog.js.map +1 -0
- package/dist/utils/child-process.d.ts +18 -0
- package/dist/utils/child-process.d.ts.map +1 -0
- package/dist/utils/child-process.js +107 -0
- package/dist/utils/child-process.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts +11 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -0
- package/dist/utils/clipboard-image.js +246 -0
- package/dist/utils/clipboard-image.js.map +1 -0
- package/dist/utils/clipboard-native.d.ts +11 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -0
- package/dist/utils/clipboard-native.js +22 -0
- package/dist/utils/clipboard-native.js.map +1 -0
- package/dist/utils/clipboard.d.ts +4 -0
- package/dist/utils/clipboard.d.ts.map +1 -0
- package/dist/utils/clipboard.js +161 -0
- package/dist/utils/clipboard.js.map +1 -0
- package/dist/utils/deprecation.d.ts +4 -0
- package/dist/utils/deprecation.d.ts.map +1 -0
- package/dist/utils/deprecation.js +13 -0
- package/dist/utils/deprecation.js.map +1 -0
- package/dist/utils/exif-orientation.d.ts +5 -0
- package/dist/utils/exif-orientation.d.ts.map +1 -0
- package/dist/utils/exif-orientation.js +158 -0
- package/dist/utils/exif-orientation.js.map +1 -0
- package/dist/utils/frontmatter.d.ts +8 -0
- package/dist/utils/frontmatter.d.ts.map +1 -0
- package/dist/utils/frontmatter.js +27 -0
- package/dist/utils/frontmatter.js.map +1 -0
- package/dist/utils/fs-watch.d.ts +5 -0
- package/dist/utils/fs-watch.d.ts.map +1 -0
- package/dist/utils/fs-watch.js +25 -0
- package/dist/utils/fs-watch.js.map +1 -0
- package/dist/utils/git.d.ts +26 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +201 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/html.d.ts +7 -0
- package/dist/utils/html.d.ts.map +1 -0
- package/dist/utils/html.js +40 -0
- package/dist/utils/html.js.map +1 -0
- package/dist/utils/image-convert.d.ts +10 -0
- package/dist/utils/image-convert.d.ts.map +1 -0
- package/dist/utils/image-convert.js +45 -0
- package/dist/utils/image-convert.js.map +1 -0
- package/dist/utils/image-process.d.ts +18 -0
- package/dist/utils/image-process.d.ts.map +1 -0
- package/dist/utils/image-process.js +84 -0
- package/dist/utils/image-process.js.map +1 -0
- package/dist/utils/image-resize-core.d.ts +30 -0
- package/dist/utils/image-resize-core.d.ts.map +1 -0
- package/dist/utils/image-resize-core.js +125 -0
- package/dist/utils/image-resize-core.js.map +1 -0
- package/dist/utils/image-resize-worker.d.ts +2 -0
- package/dist/utils/image-resize-worker.d.ts.map +1 -0
- package/dist/utils/image-resize-worker.js +31 -0
- package/dist/utils/image-resize-worker.js.map +1 -0
- package/dist/utils/image-resize.d.ts +16 -0
- package/dist/utils/image-resize.d.ts.map +1 -0
- package/dist/utils/image-resize.js +100 -0
- package/dist/utils/image-resize.js.map +1 -0
- package/dist/utils/json.d.ts +3 -0
- package/dist/utils/json.d.ts.map +1 -0
- package/dist/utils/json.js +8 -0
- package/dist/utils/json.js.map +1 -0
- package/dist/utils/management-http.d.ts +25 -0
- package/dist/utils/management-http.d.ts.map +1 -0
- package/dist/utils/management-http.js +54 -0
- package/dist/utils/management-http.js.map +1 -0
- package/dist/utils/mime.d.ts +3 -0
- package/dist/utils/mime.d.ts.map +1 -0
- package/dist/utils/mime.js +115 -0
- package/dist/utils/mime.js.map +1 -0
- package/dist/utils/open-browser.d.ts +9 -0
- package/dist/utils/open-browser.d.ts.map +1 -0
- package/dist/utils/open-browser.js +22 -0
- package/dist/utils/open-browser.js.map +1 -0
- package/dist/utils/paths.d.ts +34 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +114 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/photon.d.ts +21 -0
- package/dist/utils/photon.d.ts.map +1 -0
- package/dist/utils/photon.js +124 -0
- package/dist/utils/photon.js.map +1 -0
- package/dist/utils/shell.d.ts +34 -0
- package/dist/utils/shell.d.ts.map +1 -0
- package/dist/utils/shell.js +216 -0
- package/dist/utils/shell.js.map +1 -0
- package/dist/utils/sleep.d.ts +5 -0
- package/dist/utils/sleep.d.ts.map +1 -0
- package/dist/utils/sleep.js +17 -0
- package/dist/utils/sleep.js.map +1 -0
- package/dist/utils/sync-sleep.d.ts +2 -0
- package/dist/utils/sync-sleep.d.ts.map +1 -0
- package/dist/utils/sync-sleep.js +11 -0
- package/dist/utils/sync-sleep.js.map +1 -0
- package/dist/utils/syntax-highlight.d.ts +13 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -0
- package/dist/utils/syntax-highlight.js +177 -0
- package/dist/utils/syntax-highlight.js.map +1 -0
- package/dist/utils/text.d.ts +8 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +9 -0
- package/dist/utils/text.js.map +1 -0
- package/dist/utils/tool-result-images.d.ts +19 -0
- package/dist/utils/tool-result-images.d.ts.map +1 -0
- package/dist/utils/tool-result-images.js +45 -0
- package/dist/utils/tool-result-images.js.map +1 -0
- package/dist/utils/tools-manager.d.ts +24 -0
- package/dist/utils/tools-manager.d.ts.map +1 -0
- package/dist/utils/tools-manager.js +437 -0
- package/dist/utils/tools-manager.js.map +1 -0
- package/dist/utils/version-check.d.ts +21 -0
- package/dist/utils/version-check.d.ts.map +1 -0
- package/dist/utils/version-check.js +86 -0
- package/dist/utils/version-check.js.map +1 -0
- package/dist/utils/windows-self-update.d.ts +3 -0
- package/dist/utils/windows-self-update.d.ts.map +1 -0
- package/dist/utils/windows-self-update.js +77 -0
- package/dist/utils/windows-self-update.js.map +1 -0
- package/docs/compaction.md +348 -0
- package/docs/containerization.md +114 -0
- package/docs/development.md +71 -0
- package/docs/docs.json +148 -0
- package/docs/environment-variables.md +96 -0
- package/docs/images/exy.png +0 -0
- package/docs/images/interactive-mode.png +0 -0
- package/docs/images/tree-view.png +0 -0
- package/docs/index.md +76 -0
- package/docs/json.md +98 -0
- package/docs/keybindings.md +245 -0
- package/docs/llama-cpp.md +101 -0
- package/docs/models.md +576 -0
- package/docs/packages.md +205 -0
- package/docs/prompt-templates.md +96 -0
- package/docs/providers.md +317 -0
- package/docs/quickstart.md +167 -0
- package/docs/rpc.md +1487 -0
- package/docs/sdk.md +1146 -0
- package/docs/security.md +24 -0
- package/docs/session-format.md +438 -0
- package/docs/sessions.md +145 -0
- package/docs/settings.md +355 -0
- package/docs/shell-aliases.md +13 -0
- package/docs/skills.md +231 -0
- package/docs/terminal-setup.md +192 -0
- package/docs/termux.md +127 -0
- package/docs/themes.md +320 -0
- package/docs/tmux.md +63 -0
- package/docs/tui.md +478 -0
- package/docs/usage.md +291 -0
- package/docs/windows.md +39 -0
- package/examples/README.md +21 -0
- package/examples/extensions/README.md +163 -0
- package/examples/extensions/bash-spawn-hook/index.ts +37 -0
- package/examples/extensions/bash-spawn-hook/plugin.json +10 -0
- package/examples/extensions/bookmark/index.ts +67 -0
- package/examples/extensions/bookmark/plugin.json +11 -0
- package/examples/extensions/built-in-tool-renderer/index.ts +262 -0
- package/examples/extensions/built-in-tool-renderer/plugin.json +10 -0
- package/examples/extensions/claude-rules/index.ts +119 -0
- package/examples/extensions/claude-rules/plugin.json +8 -0
- package/examples/extensions/cli-bridge/README.md +27 -0
- package/examples/extensions/cli-bridge/index.ts +320 -0
- package/examples/extensions/cli-bridge/plugin.json +10 -0
- package/examples/extensions/dynamic-resources/SKILL.md +8 -0
- package/examples/extensions/dynamic-resources/dynamic.json +79 -0
- package/examples/extensions/dynamic-resources/dynamic.md +5 -0
- package/examples/extensions/dynamic-resources/index.ts +53 -0
- package/examples/extensions/dynamic-resources/plugin.json +8 -0
- package/examples/extensions/dynamic-tools/index.ts +102 -0
- package/examples/extensions/dynamic-tools/plugin.json +14 -0
- package/examples/extensions/event-bus/index.ts +75 -0
- package/examples/extensions/event-bus/plugin.json +13 -0
- package/examples/extensions/file-trigger/index.ts +65 -0
- package/examples/extensions/file-trigger/plugin.json +13 -0
- package/examples/extensions/goal/README.md +16 -0
- package/examples/extensions/goal/index.ts +134 -0
- package/examples/extensions/goal/plugin.json +12 -0
- package/examples/extensions/hello/index.ts +29 -0
- package/examples/extensions/hello/plugin.json +10 -0
- package/examples/extensions/kimi-deferred-tools/index.ts +93 -0
- package/examples/extensions/kimi-deferred-tools/plugin.json +11 -0
- package/examples/extensions/mcp-stdio/README.md +19 -0
- package/examples/extensions/mcp-stdio/index.d.ts +64 -0
- package/examples/extensions/mcp-stdio/index.d.ts.map +1 -0
- package/examples/extensions/mcp-stdio/index.js +292 -0
- package/examples/extensions/mcp-stdio/index.js.map +1 -0
- package/examples/extensions/mcp-stdio/index.ts +381 -0
- package/examples/extensions/mcp-stdio/plugin.json +10 -0
- package/examples/extensions/memory-backend/index.ts +120 -0
- package/examples/extensions/memory-backend/plugin.json +10 -0
- package/examples/extensions/minimal-mode/index.ts +447 -0
- package/examples/extensions/minimal-mode/plugin.json +10 -0
- package/examples/extensions/multi-agent/README.md +7 -0
- package/examples/extensions/multi-agent/index.ts +592 -0
- package/examples/extensions/multi-agent/plugin.json +13 -0
- package/examples/extensions/notify/index.ts +70 -0
- package/examples/extensions/notify/plugin.json +8 -0
- package/examples/extensions/permission-gate/index.ts +55 -0
- package/examples/extensions/permission-gate/plugin.json +11 -0
- package/examples/extensions/pirate/index.ts +75 -0
- package/examples/extensions/pirate/plugin.json +8 -0
- package/examples/extensions/plan/README.md +16 -0
- package/examples/extensions/plan/index.ts +136 -0
- package/examples/extensions/plan/plugin.json +12 -0
- package/examples/extensions/prompt-customizer/index.ts +117 -0
- package/examples/extensions/prompt-customizer/plugin.json +8 -0
- package/examples/extensions/protected-paths/index.ts +44 -0
- package/examples/extensions/protected-paths/plugin.json +11 -0
- package/examples/extensions/provider-attribution/README.md +12 -0
- package/examples/extensions/provider-attribution/index.ts +86 -0
- package/examples/extensions/provider-attribution/plugin.json +10 -0
- package/examples/extensions/send-user-message/index.ts +118 -0
- package/examples/extensions/send-user-message/plugin.json +11 -0
- package/examples/extensions/session-name/index.ts +31 -0
- package/examples/extensions/session-name/plugin.json +11 -0
- package/examples/extensions/session-share/README.md +15 -0
- package/examples/extensions/session-share/index.ts +170 -0
- package/examples/extensions/session-share/plugin.json +15 -0
- package/examples/extensions/structured-output/index.ts +68 -0
- package/examples/extensions/structured-output/plugin.json +10 -0
- package/examples/extensions/subagent/README.md +177 -0
- package/examples/extensions/subagent/agents/planner.md +37 -0
- package/examples/extensions/subagent/agents/reviewer.md +35 -0
- package/examples/extensions/subagent/agents/scout.md +50 -0
- package/examples/extensions/subagent/agents/worker.md +24 -0
- package/examples/extensions/subagent/agents.ts +157 -0
- package/examples/extensions/subagent/index.ts +1057 -0
- package/examples/extensions/subagent/plugin.json +13 -0
- package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
- package/examples/extensions/subagent/prompts/implement.md +10 -0
- package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
- package/examples/extensions/suite-manager/README.md +45 -0
- package/examples/extensions/suite-manager/index.ts +182 -0
- package/examples/extensions/suite-manager/plugin.json +13 -0
- package/examples/extensions/timed-confirm/index.ts +93 -0
- package/examples/extensions/timed-confirm/plugin.json +13 -0
- package/examples/extensions/todo-set/README.md +18 -0
- package/examples/extensions/todo-set/index.ts +133 -0
- package/examples/extensions/todo-set/plugin.json +12 -0
- package/examples/extensions/tool-override/index.ts +159 -0
- package/examples/extensions/tool-override/plugin.json +11 -0
- package/examples/extensions/trigger-compact/index.ts +76 -0
- package/examples/extensions/trigger-compact/plugin.json +14 -0
- package/examples/extensions/truncated-tool/index.ts +204 -0
- package/examples/extensions/truncated-tool/plugin.json +10 -0
- package/examples/extensions/with-deps/index.ts +42 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +22 -0
- package/examples/extensions/with-deps/plugin.json +10 -0
- package/examples/remote-client.ts +66 -0
- package/examples/rpc-extension-ui.ts +641 -0
- package/examples/sdk/01-minimal.ts +26 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +70 -0
- package/examples/sdk/04-skills.ts +55 -0
- package/examples/sdk/05-tools.ts +48 -0
- package/examples/sdk/07-context-files.ts +42 -0
- package/examples/sdk/08-prompt-templates.ts +51 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +34 -0
- package/examples/sdk/10-settings.ts +53 -0
- package/examples/sdk/11-sessions.ts +52 -0
- package/examples/sdk/12-full-control.ts +72 -0
- package/examples/sdk/13-session-runtime.ts +71 -0
- package/examples/sdk/14-custom-session-storage.ts +132 -0
- package/examples/sdk/README.md +139 -0
- package/package.json +111 -0
|
@@ -0,0 +1,4107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interactive mode for the coding agent.
|
|
3
|
+
* Handles TUI rendering and user interaction, delegating business logic to AgentSession.
|
|
4
|
+
*/
|
|
5
|
+
import * as crypto from "node:crypto";
|
|
6
|
+
import * as fs from "node:fs";
|
|
7
|
+
import * as os from "node:os";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import { contentText } from "@agent-forge/ai";
|
|
10
|
+
import * as TuiLayouts from "@agent-forge/tui";
|
|
11
|
+
import { CombinedAutocompleteProvider, Container, getCapabilities, hyperlink, Markdown, ProcessTerminal, Spacer, setCapabilityOverrides, setKeybindings, Text, TruncatedText, TuiAltScreen, TuiMainScreen, visibleWidth, } from "@agent-forge/tui";
|
|
12
|
+
import chalk from "chalk";
|
|
13
|
+
import { spawn } from "child_process";
|
|
14
|
+
import { APP_NAME, APP_TITLE, getAgentDir, getDebugLogPath, VERSION } from "../../config.js";
|
|
15
|
+
import { parseSkillBlock } from "../../core/agent-session.js";
|
|
16
|
+
import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
|
|
17
|
+
import { CACHE_TTL_MS, collectCacheMisses, computeCacheWaste, detectCacheMiss, } from "../../core/cache-stats.js";
|
|
18
|
+
import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "../../core/defaults.js";
|
|
19
|
+
import { createCodingAgentFacade, createCodingAgentRuntimeFacade, } from "../../core/facade/index.js";
|
|
20
|
+
import { FooterDataProvider } from "../../core/footer-data-provider.js";
|
|
21
|
+
import { configureHttpDispatcher, formatHttpIdleTimeoutMs } from "../../core/http-dispatcher.js";
|
|
22
|
+
import { KeybindingsManager } from "../../core/keybindings.js";
|
|
23
|
+
import { createCompactionSummaryMessage } from "../../core/messages.js";
|
|
24
|
+
import { DefaultPackageManager } from "../../core/package-manager.js";
|
|
25
|
+
import { formatMissingSessionCwdPrompt, MissingSessionCwdError } from "../../core/session-cwd.js";
|
|
26
|
+
import { SessionManager, sessionEntryToContextMessages } from "../../core/session-manager.js";
|
|
27
|
+
import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
|
|
28
|
+
import { getUsageCostBreakdown } from "../../core/usage-totals.js";
|
|
29
|
+
import { listSuiteIds, resolveSuiteId, SUITE_ENV_VAR, SUITE_SESSION_ENTRY_TYPE } from "../../profiles/suite-loader.js";
|
|
30
|
+
import { getChangelogPath, getNewEntries, normalizeChangelogLinks, parseChangelog } from "../../utils/changelog.js";
|
|
31
|
+
import { copyToClipboard, readClipboardText } from "../../utils/clipboard.js";
|
|
32
|
+
import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
|
|
33
|
+
import { parseGitUrl } from "../../utils/git.js";
|
|
34
|
+
import { openBrowser } from "../../utils/open-browser.js";
|
|
35
|
+
import { killTrackedDetachedChildren } from "../../utils/shell.js";
|
|
36
|
+
import { loadAllHighlightLanguages } from "../../utils/syntax-highlight.js";
|
|
37
|
+
import { ensureTool } from "../../utils/tools-manager.js";
|
|
38
|
+
import { checkForNewVersion } from "../../utils/version-check.js";
|
|
39
|
+
import { ArminComponent } from "./components/armin.js";
|
|
40
|
+
import { AssistantMessageComponent } from "./components/assistant-message.js";
|
|
41
|
+
import { BashExecutionComponent } from "./components/bash-execution.js";
|
|
42
|
+
import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
|
|
43
|
+
import { CompactionSummaryMessageComponent } from "./components/compaction-summary-message.js";
|
|
44
|
+
import { CustomEditor } from "./components/custom-editor.js";
|
|
45
|
+
import { CustomMessageComponent } from "./components/custom-message.js";
|
|
46
|
+
import { DaxnutsComponent } from "./components/daxnuts.js";
|
|
47
|
+
import { DynamicBorder } from "./components/dynamic-border.js";
|
|
48
|
+
import { FooterComponent, formatTokens } from "./components/footer.js";
|
|
49
|
+
import { formatKeyText, keyDisplayText, keyHint, keyText, rawKeyHint } from "./components/keybinding-hints.js";
|
|
50
|
+
import { createMermaidMarkdownTransformer } from "./components/mermaid.js";
|
|
51
|
+
import { readSessionSuiteIdFromFile, SessionSelectorComponent } from "./components/session-selector.js";
|
|
52
|
+
import { SettingsSelectorComponent } from "./components/settings-selector.js";
|
|
53
|
+
import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
|
|
54
|
+
import { BranchSummaryStatusIndicator, CompactionStatusIndicator, IdleStatus, RetryStatusIndicator, WorkingStatusIndicator, } from "./components/status-indicator.js";
|
|
55
|
+
import { ToolExecutionComponent } from "./components/tool-execution.js";
|
|
56
|
+
import { TreeSelectorComponent } from "./components/tree-selector.js";
|
|
57
|
+
import { UserMessageComponent } from "./components/user-message.js";
|
|
58
|
+
import { editInExternalEditor } from "./external-editor.js";
|
|
59
|
+
import * as authFlow from "./interactive-auth.js";
|
|
60
|
+
import { COMMAND_DISPATCH_ENTRIES, matchCommandDispatchEntry } from "./interactive-commands.js";
|
|
61
|
+
import { createInteractionHostV1, hideDialogEditor, hideDialogInput, hideDialogSelector, isExpandable, showDialogConfirm, showDialogEditor, showDialogInput, showDialogNotify, showDialogSelector, } from "./interactive-dialog-ui.js";
|
|
62
|
+
import { buildScopeGroups, findSourceInfoForPath, formatContextPath, formatDiagnostics, formatDisplayPath, formatMetricsSnapshot, formatPathWithSource, formatScopeGroups, getCompactPackageSourceLabel, getCompactPathLabel, getDisplaySourceInfo, getScopeGroup, getShortPath, isPackageSource, } from "./interactive-format.js";
|
|
63
|
+
import { createFuzzyAutocompleteItems, formatLoginProviderCompletionDescription, getLoginProviderCompletionOptions, getLoginProviderSearchText, isAnthropicSubscriptionAuthKey, isDeadTerminalError, quoteIfNeeded, } from "./interactive-helpers.js";
|
|
64
|
+
import * as selectorsFlow from "./interactive-selectors.js";
|
|
65
|
+
import { refreshModelCatalogs } from "./model-catalog-refresh.js";
|
|
66
|
+
import { getModelSearchText } from "./model-search.js";
|
|
67
|
+
import { getAvailableThemes, getEditorTheme, getMarkdownTheme, loadThemeFromPath, onThemeChange, setRegisteredThemes, stopThemeWatcher, Theme, theme, } from "./theme/theme.js";
|
|
68
|
+
import { InteractiveThemeController } from "./theme/theme-controller.js";
|
|
69
|
+
// Re-exported for existing consumers of the interactive mode module; the
|
|
70
|
+
// implementation moved to ./interactive-dialog-ui.ts with the extension UI block.
|
|
71
|
+
export { mapLegacyDialogResultToOutcome } from "./interactive-dialog-ui.js";
|
|
72
|
+
/**
|
|
73
|
+
* Rebuilds real Theme instances from the resource loader's ThemeMetaV1
|
|
74
|
+
* projection for the theme registry (B-S2b v1). Registration points
|
|
75
|
+
* (constructor, runtime session start, reload) need loadable instances —
|
|
76
|
+
* setRegisteredThemes stores what it is given and getThemeByName returns the
|
|
77
|
+
* registered instance first — while the loader surface now projects only
|
|
78
|
+
* metadata. Re-loading is synchronous and deterministic per sourcePath;
|
|
79
|
+
* theme names come from the same JSON the loader parsed, so meta.name ===
|
|
80
|
+
* Theme.name and the registry keeps its own name-filter/dedupe semantics.
|
|
81
|
+
*
|
|
82
|
+
* Contract: a meta without a sourcePath (or one whose file no longer parses)
|
|
83
|
+
* throws — themesOverride host inputs must always carry a loadable
|
|
84
|
+
* sourcePath. This adds one extra parse per theme at registration time
|
|
85
|
+
* (constructor / session start / reload, not per turn).
|
|
86
|
+
*/
|
|
87
|
+
export function buildRegisteredThemes(themes) {
|
|
88
|
+
return themes.map((meta) => loadThemeFromPath(meta.sourcePath));
|
|
89
|
+
}
|
|
90
|
+
class ExpandableText extends Text {
|
|
91
|
+
getCollapsedText;
|
|
92
|
+
getExpandedText;
|
|
93
|
+
constructor(getCollapsedText, getExpandedText, expanded = false, paddingX = 0, paddingY = 0) {
|
|
94
|
+
super(expanded ? getExpandedText() : getCollapsedText(), paddingX, paddingY);
|
|
95
|
+
this.getCollapsedText = getCollapsedText;
|
|
96
|
+
this.getExpandedText = getExpandedText;
|
|
97
|
+
}
|
|
98
|
+
setExpanded(expanded) {
|
|
99
|
+
this.setText(expanded ? this.getExpandedText() : this.getCollapsedText());
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function isCustomSessionEntry(item) {
|
|
103
|
+
return "type" in item && item.type === "custom";
|
|
104
|
+
}
|
|
105
|
+
function isCompactionCostNotice(item) {
|
|
106
|
+
return "type" in item && item.type === "compaction_cost";
|
|
107
|
+
}
|
|
108
|
+
// Empty-reply auto-continuation (#5, print-mode parity): sent at most once per
|
|
109
|
+
// submitted turn when the turn ends with an empty assistant reply and the
|
|
110
|
+
// model was neither aborted nor errored. The one-shot budget prevents loops.
|
|
111
|
+
const EMPTY_REPLY_RETRY_PROMPT = "Your previous reply was empty. Continue the user's task now.";
|
|
112
|
+
const ANTHROPIC_SUBSCRIPTION_AUTH_WARNING = "Anthropic subscription auth is active. Third-party harness usage draws from extra usage and is billed per token, not your Claude plan limits. Manage extra usage at https://claude.ai/settings/usage. Disable this warning in /settings.";
|
|
113
|
+
export function formatResumeCommand(session) {
|
|
114
|
+
if (!process.stdout.isTTY)
|
|
115
|
+
return undefined;
|
|
116
|
+
if (!session.isPersisted())
|
|
117
|
+
return undefined;
|
|
118
|
+
const sessionFile = session.getSessionFile();
|
|
119
|
+
if (!sessionFile || !fs.existsSync(sessionFile))
|
|
120
|
+
return undefined;
|
|
121
|
+
const args = [APP_NAME];
|
|
122
|
+
if (!session.usesDefaultSessionDir()) {
|
|
123
|
+
args.push("--session-dir", quoteIfNeeded(session.getSessionDir()));
|
|
124
|
+
}
|
|
125
|
+
args.push("--session", session.getSessionId());
|
|
126
|
+
return args.join(" ");
|
|
127
|
+
}
|
|
128
|
+
/** Composition root for selecting the interactive terminal renderer. */
|
|
129
|
+
export function createInteractiveTui(options) {
|
|
130
|
+
const terminal = options.terminal ?? new ProcessTerminal();
|
|
131
|
+
if (options.tuiMode === "fullscreen") {
|
|
132
|
+
const styleSearchMatch = (text) => theme.bg("searchMatchBg", theme.fg("searchMatchText", text));
|
|
133
|
+
return new TuiAltScreen(terminal, options.showHardwareCursor, options.logDirectory, {
|
|
134
|
+
searchMatchStyle: (text) => theme.underline(styleSearchMatch(text)),
|
|
135
|
+
searchCurrentMatchStyle: (text) => theme.bold(theme.inverse(styleSearchMatch(text))),
|
|
136
|
+
openUrl: openBrowser,
|
|
137
|
+
onRightClickPaste: options.onRightClickPaste,
|
|
138
|
+
copyOnSelect: options.fullscreenCopyOnSelect,
|
|
139
|
+
copySelection: async (text) => {
|
|
140
|
+
try {
|
|
141
|
+
await copyToClipboard(text);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return new TuiMainScreen(terminal, options.showHardwareCursor, options.logDirectory);
|
|
151
|
+
}
|
|
152
|
+
/** Stable reference for components while InteractiveMode replaces the active renderer. */
|
|
153
|
+
export function createInteractiveTuiReference(getTui) {
|
|
154
|
+
return new Proxy({}, {
|
|
155
|
+
get: (_target, property) => {
|
|
156
|
+
const tui = getTui();
|
|
157
|
+
const value = Reflect.get(tui, property, tui);
|
|
158
|
+
if (typeof value !== "function")
|
|
159
|
+
return value;
|
|
160
|
+
let methodTui = tui;
|
|
161
|
+
let method = value;
|
|
162
|
+
return (...args) => {
|
|
163
|
+
const currentTui = getTui();
|
|
164
|
+
if (currentTui !== methodTui) {
|
|
165
|
+
const currentMethod = Reflect.get(currentTui, property, currentTui);
|
|
166
|
+
if (typeof currentMethod !== "function") {
|
|
167
|
+
throw new TypeError(`TUI property ${String(property)} is not callable`);
|
|
168
|
+
}
|
|
169
|
+
methodTui = currentTui;
|
|
170
|
+
method = currentMethod;
|
|
171
|
+
}
|
|
172
|
+
return Reflect.apply(method, methodTui, args);
|
|
173
|
+
};
|
|
174
|
+
},
|
|
175
|
+
set: (_target, property, value) => {
|
|
176
|
+
const tui = getTui();
|
|
177
|
+
return Reflect.set(tui, property, value, tui);
|
|
178
|
+
},
|
|
179
|
+
has: (_target, property) => Reflect.has(getTui(), property),
|
|
180
|
+
getPrototypeOf: () => Reflect.getPrototypeOf(getTui()),
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
export class InteractiveMode {
|
|
184
|
+
runtimeHost;
|
|
185
|
+
renderer;
|
|
186
|
+
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
187
|
+
ui;
|
|
188
|
+
mainScreenRenderState;
|
|
189
|
+
loadedResourcesContainer;
|
|
190
|
+
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
191
|
+
chatContainer;
|
|
192
|
+
documentContainer;
|
|
193
|
+
transcriptScrollView;
|
|
194
|
+
fullscreenLayoutRoot;
|
|
195
|
+
pendingMessagesContainer;
|
|
196
|
+
statusContainer;
|
|
197
|
+
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
198
|
+
defaultEditor;
|
|
199
|
+
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
200
|
+
editor;
|
|
201
|
+
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
202
|
+
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
203
|
+
autocompleteProvider;
|
|
204
|
+
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
205
|
+
fdPath;
|
|
206
|
+
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
207
|
+
editorContainer;
|
|
208
|
+
activeSelectorToken;
|
|
209
|
+
activeSelectorDispose;
|
|
210
|
+
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
211
|
+
footer;
|
|
212
|
+
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
213
|
+
footerContainer;
|
|
214
|
+
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
215
|
+
footerDataProvider;
|
|
216
|
+
// Stored so the same manager can be injected into custom editors, selectors, and extension UI.
|
|
217
|
+
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
218
|
+
keybindings;
|
|
219
|
+
version;
|
|
220
|
+
isInitialized = false;
|
|
221
|
+
onInputCallback;
|
|
222
|
+
pendingUserInputs = [];
|
|
223
|
+
activeStatusIndicator = undefined;
|
|
224
|
+
idleStatus = new IdleStatus();
|
|
225
|
+
workingMessage = undefined;
|
|
226
|
+
workingVisible = true;
|
|
227
|
+
workingIndicatorOptions = undefined;
|
|
228
|
+
defaultWorkingMessage = "Working...";
|
|
229
|
+
defaultHiddenThinkingLabel = "Thinking...";
|
|
230
|
+
hiddenThinkingLabel = this.defaultHiddenThinkingLabel;
|
|
231
|
+
lastSigintTime = 0;
|
|
232
|
+
lastEscapeTime = 0;
|
|
233
|
+
changelogMarkdown = undefined;
|
|
234
|
+
startupNoticesShown = false;
|
|
235
|
+
anthropicSubscriptionWarningShown = false;
|
|
236
|
+
// Status line tracking (for mutating immediately-sequential status updates)
|
|
237
|
+
lastStatusSpacer = undefined;
|
|
238
|
+
lastStatusText = undefined;
|
|
239
|
+
managedToolStatusStarted = false;
|
|
240
|
+
// Streaming message tracking
|
|
241
|
+
streamingComponent = undefined;
|
|
242
|
+
streamingMessage = undefined;
|
|
243
|
+
// Tool execution tracking: toolCallId -> component
|
|
244
|
+
pendingTools = new Map();
|
|
245
|
+
// Tool output expansion state
|
|
246
|
+
/** Host-visible for ./interactive-dialog-ui.ts (S5 slice 2). */
|
|
247
|
+
toolOutputExpanded = false;
|
|
248
|
+
// Thinking block visibility state
|
|
249
|
+
hideThinkingBlock = false;
|
|
250
|
+
outputPad = 1;
|
|
251
|
+
mermaidMarkdownTransformer = createMermaidMarkdownTransformer({
|
|
252
|
+
getMode: () => this.settingsManager.getMermaidRenderingMode(),
|
|
253
|
+
theme,
|
|
254
|
+
});
|
|
255
|
+
// Skill commands: command name -> skill file path
|
|
256
|
+
skillCommands = new Map();
|
|
257
|
+
// Agent subscription unsubscribe function
|
|
258
|
+
unsubscribe;
|
|
259
|
+
signalCleanupHandlers = [];
|
|
260
|
+
// Track if editor is in bash mode (text starts with !)
|
|
261
|
+
isBashMode = false;
|
|
262
|
+
// Track current bash execution component
|
|
263
|
+
bashComponent = undefined;
|
|
264
|
+
// Track pending bash components (shown in pending area, moved to chat on submit)
|
|
265
|
+
pendingBashComponents = [];
|
|
266
|
+
// Auto-compaction state
|
|
267
|
+
autoCompactionEscapeHandler;
|
|
268
|
+
// Auto-retry state
|
|
269
|
+
retryEscapeHandler;
|
|
270
|
+
// Messages queued while compaction is running
|
|
271
|
+
compactionQueuedMessages = [];
|
|
272
|
+
// Shutdown state
|
|
273
|
+
shutdownRequested = false;
|
|
274
|
+
// Extension UI state: host-visible for ./interactive-dialog-ui.ts (S5 slice 2).
|
|
275
|
+
dialogSelector = undefined;
|
|
276
|
+
dialogInput = undefined;
|
|
277
|
+
// Settles the replaced dialog's still-pending promise when a concurrent
|
|
278
|
+
// request mounts a new dialog in the same slot (1A.8: request() settles).
|
|
279
|
+
dialogSelectorReplaced = undefined;
|
|
280
|
+
dialogInputReplaced = undefined;
|
|
281
|
+
dialogEditor = undefined;
|
|
282
|
+
// Layout spacer slots above/below the editor (fixed empty chrome; the legacy extension widget producer is retired).
|
|
283
|
+
widgetContainerAbove;
|
|
284
|
+
widgetContainerBelow;
|
|
285
|
+
// Header container that holds the built-in or custom header
|
|
286
|
+
headerContainer;
|
|
287
|
+
// Built-in header (logo + keybinding hints + changelog)
|
|
288
|
+
builtInHeader = undefined;
|
|
289
|
+
options;
|
|
290
|
+
onRightClickPaste = () => {
|
|
291
|
+
void this.handleRightClickPaste();
|
|
292
|
+
};
|
|
293
|
+
themeController;
|
|
294
|
+
// Convenience accessors
|
|
295
|
+
get session() {
|
|
296
|
+
return this.runtimeHost.session;
|
|
297
|
+
}
|
|
298
|
+
get agent() {
|
|
299
|
+
// 1B.7c: agent-loop control goes through the curated host surface.
|
|
300
|
+
return {
|
|
301
|
+
abort: () => this.session.host.abortAgent(),
|
|
302
|
+
signal: this.session.host.abortSignal,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Constructor-injected settings (D-047): the TUI reads settings from the
|
|
307
|
+
* host-provided manager, never from AgentSession. Internal collaboration
|
|
308
|
+
* surface; exposed for ./interactive-selectors.ts (S5 slice 5).
|
|
309
|
+
*/
|
|
310
|
+
get settingsManager() {
|
|
311
|
+
return this.options.settingsManager;
|
|
312
|
+
}
|
|
313
|
+
// Stable facade (阶段 1 slice 1): derived on each access; the facade and
|
|
314
|
+
// `this.session` always resolve to the same AgentSession, so session rebind
|
|
315
|
+
// needs no separate handling.
|
|
316
|
+
/** Internal collaboration surface; exposed for ./interactive-selectors.ts (S5 slice 5). */
|
|
317
|
+
get facade() {
|
|
318
|
+
return createCodingAgentFacade(this.session);
|
|
319
|
+
}
|
|
320
|
+
// Stable runtime facade (阶段 4 slice 3): derived on each access, same
|
|
321
|
+
// pattern as `facade`; runtime session rebinds are picked up automatically.
|
|
322
|
+
/** Internal collaboration surface; exposed for ./interactive-selectors.ts (S5 slice 5). */
|
|
323
|
+
get runtimeFacade() {
|
|
324
|
+
return createCodingAgentRuntimeFacade(this.runtimeHost);
|
|
325
|
+
}
|
|
326
|
+
constructor(runtimeHost, options) {
|
|
327
|
+
// Assign options first: the settingsManager getter reads the injected
|
|
328
|
+
// manager (D-047) and the constructor uses it before anything else.
|
|
329
|
+
this.options = { ...options };
|
|
330
|
+
this.runtimeHost = runtimeHost;
|
|
331
|
+
setCapabilityOverrides(this.settingsManager.getTerminalCapabilityOverrides());
|
|
332
|
+
const tuiMode = options.tuiMode ?? this.settingsManager.getTuiMode();
|
|
333
|
+
this.options.tuiMode = tuiMode;
|
|
334
|
+
this.runtimeFacade.setBeforeSessionInvalidate(() => {
|
|
335
|
+
this.resetDialogUI();
|
|
336
|
+
});
|
|
337
|
+
this.runtimeFacade.setRebindSession(async () => {
|
|
338
|
+
await this.rebindCurrentSession({ renderBeforeBind: true });
|
|
339
|
+
await this.themeController.applyFromSettings();
|
|
340
|
+
});
|
|
341
|
+
this.version = VERSION;
|
|
342
|
+
this.renderer = createInteractiveTui({
|
|
343
|
+
tuiMode,
|
|
344
|
+
showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
|
|
345
|
+
logDirectory: getAgentDir(),
|
|
346
|
+
onRightClickPaste: this.onRightClickPaste,
|
|
347
|
+
fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
|
|
348
|
+
});
|
|
349
|
+
this.ui = createInteractiveTuiReference(() => this.renderer);
|
|
350
|
+
this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
|
|
351
|
+
this.headerContainer = new Container();
|
|
352
|
+
this.loadedResourcesContainer = new Container();
|
|
353
|
+
this.chatContainer = new Container();
|
|
354
|
+
this.documentContainer = new Container();
|
|
355
|
+
this.documentContainer.addChild(this.headerContainer);
|
|
356
|
+
this.documentContainer.addChild(this.loadedResourcesContainer);
|
|
357
|
+
this.documentContainer.addChild(this.chatContainer);
|
|
358
|
+
this.pendingMessagesContainer = new Container();
|
|
359
|
+
this.statusContainer = new Container();
|
|
360
|
+
this.widgetContainerAbove = new Container();
|
|
361
|
+
// Fixed empty state (single spacer): matches the retired widget renderer's empty layout.
|
|
362
|
+
this.widgetContainerAbove.addChild(new Spacer(1));
|
|
363
|
+
this.widgetContainerBelow = new Container();
|
|
364
|
+
this.keybindings = KeybindingsManager.create();
|
|
365
|
+
setKeybindings(this.keybindings);
|
|
366
|
+
const editorPaddingX = this.settingsManager.getEditorPaddingX();
|
|
367
|
+
const autocompleteMaxVisible = this.settingsManager.getAutocompleteMaxVisible();
|
|
368
|
+
this.defaultEditor = new CustomEditor(this.ui, getEditorTheme(), this.keybindings, {
|
|
369
|
+
paddingX: editorPaddingX,
|
|
370
|
+
autocompleteMaxVisible,
|
|
371
|
+
});
|
|
372
|
+
this.editor = this.defaultEditor;
|
|
373
|
+
this.editorContainer = new Container();
|
|
374
|
+
this.editorContainer.addChild(this.editor);
|
|
375
|
+
this.footerDataProvider = new FooterDataProvider(this.facade.getCwd());
|
|
376
|
+
this.footer = new FooterComponent(this.session, this.footerDataProvider);
|
|
377
|
+
this.footer.setAutoCompactEnabled(this.facade.isAutoCompactionEnabled());
|
|
378
|
+
this.footerContainer = new Container();
|
|
379
|
+
this.footerContainer.addChild(this.footer);
|
|
380
|
+
// Load hide thinking block setting
|
|
381
|
+
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
382
|
+
this.outputPad = this.settingsManager.getOutputPad();
|
|
383
|
+
// Register themes from resource loader and initialize
|
|
384
|
+
setRegisteredThemes(buildRegisteredThemes(this.facade.getThemes().themes));
|
|
385
|
+
this.themeController = new InteractiveThemeController(this.ui, {
|
|
386
|
+
getSettingsManager: () => this.settingsManager,
|
|
387
|
+
showError: (message) => this.showError(message),
|
|
388
|
+
onChanged: () => this.updateEditorBorderColor(),
|
|
389
|
+
initialThemeSetting: options.initialThemeSetting,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
getAutocompleteSourceTag(sourceInfo) {
|
|
393
|
+
if (!sourceInfo) {
|
|
394
|
+
return undefined;
|
|
395
|
+
}
|
|
396
|
+
const scopePrefix = sourceInfo.scope === "user" ? "u" : sourceInfo.scope === "project" ? "p" : "t";
|
|
397
|
+
const source = sourceInfo.source.trim();
|
|
398
|
+
if (source === "auto" || source === "local" || source === "cli") {
|
|
399
|
+
return scopePrefix;
|
|
400
|
+
}
|
|
401
|
+
if (source.startsWith("npm:")) {
|
|
402
|
+
return `${scopePrefix}:${source}`;
|
|
403
|
+
}
|
|
404
|
+
const gitSource = parseGitUrl(source);
|
|
405
|
+
if (gitSource) {
|
|
406
|
+
const ref = gitSource.ref ? `@${gitSource.ref}` : "";
|
|
407
|
+
return `${scopePrefix}:git:${gitSource.host}/${gitSource.path}${ref}`;
|
|
408
|
+
}
|
|
409
|
+
return scopePrefix;
|
|
410
|
+
}
|
|
411
|
+
prefixAutocompleteDescription(description, sourceInfo) {
|
|
412
|
+
const sourceTag = this.getAutocompleteSourceTag(sourceInfo);
|
|
413
|
+
if (!sourceTag) {
|
|
414
|
+
return description;
|
|
415
|
+
}
|
|
416
|
+
return description ? `[${sourceTag}] ${description}` : `[${sourceTag}]`;
|
|
417
|
+
}
|
|
418
|
+
createBaseAutocompleteProvider() {
|
|
419
|
+
// Define commands for autocomplete
|
|
420
|
+
const slashCommands = BUILTIN_SLASH_COMMANDS.map((command) => ({
|
|
421
|
+
name: command.name,
|
|
422
|
+
description: command.description,
|
|
423
|
+
...(command.argumentHint && { argumentHint: command.argumentHint }),
|
|
424
|
+
}));
|
|
425
|
+
const modelCommand = slashCommands.find((command) => command.name === "model");
|
|
426
|
+
// v8 ignore next -- BUILTIN_SLASH_COMMANDS 恒含 model,find 必命中
|
|
427
|
+
if (modelCommand) {
|
|
428
|
+
modelCommand.getArgumentCompletions = (prefix) => {
|
|
429
|
+
const models = this.facade.getScopedModels().length > 0
|
|
430
|
+
? this.facade.getScopedModels().map((s) => s.model)
|
|
431
|
+
: this.facade.getAvailableModels();
|
|
432
|
+
if (models.length === 0)
|
|
433
|
+
return null;
|
|
434
|
+
// Create items with provider/id format
|
|
435
|
+
const items = models.map((m) => ({
|
|
436
|
+
id: m.id,
|
|
437
|
+
provider: m.provider,
|
|
438
|
+
name: m.name,
|
|
439
|
+
label: `${m.provider}/${m.id}`,
|
|
440
|
+
}));
|
|
441
|
+
return createFuzzyAutocompleteItems(items, prefix, getModelSearchText, (item) => ({
|
|
442
|
+
value: item.label,
|
|
443
|
+
label: item.id,
|
|
444
|
+
description: item.provider,
|
|
445
|
+
}));
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
const thinkingCommand = slashCommands.find((command) => command.name === "thinking");
|
|
449
|
+
// v8 ignore next -- BUILTIN_SLASH_COMMANDS 恒含 thinking,find 必命中
|
|
450
|
+
if (thinkingCommand) {
|
|
451
|
+
thinkingCommand.getArgumentCompletions = (prefix) => {
|
|
452
|
+
return createFuzzyAutocompleteItems(this.facade.getAvailableThinkingLevels(), prefix, (level) => level, (level) => ({
|
|
453
|
+
value: level,
|
|
454
|
+
label: level,
|
|
455
|
+
}));
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
const suiteCommand = slashCommands.find((command) => command.name === "suite");
|
|
459
|
+
// v8 ignore next -- BUILTIN_SLASH_COMMANDS 恒含 suite,find 必命中
|
|
460
|
+
if (suiteCommand) {
|
|
461
|
+
suiteCommand.getArgumentCompletions = (prefix) => {
|
|
462
|
+
return createFuzzyAutocompleteItems(listSuiteIds(getAgentDir()), prefix, (id) => id, (id) => ({
|
|
463
|
+
value: id,
|
|
464
|
+
label: id,
|
|
465
|
+
}));
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
const loginCommand = slashCommands.find((command) => command.name === "login");
|
|
469
|
+
// v8 ignore next -- BUILTIN_SLASH_COMMANDS 恒含 login,find 必命中
|
|
470
|
+
if (loginCommand) {
|
|
471
|
+
loginCommand.getArgumentCompletions = (prefix) => {
|
|
472
|
+
const providers = getLoginProviderCompletionOptions(this.getLoginProviderOptions());
|
|
473
|
+
return createFuzzyAutocompleteItems(providers, prefix, getLoginProviderSearchText, (provider) => ({
|
|
474
|
+
value: provider.id,
|
|
475
|
+
label: provider.id,
|
|
476
|
+
description: formatLoginProviderCompletionDescription(provider),
|
|
477
|
+
}));
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
const capabilityRegistrations = this.session.getCapabilityRuntime().listCommands();
|
|
481
|
+
const capabilityCommandNames = new Set(capabilityRegistrations.map(({ definition }) => definition.name));
|
|
482
|
+
// Conflicting command names are rejected by AgentSession rather than silently
|
|
483
|
+
// selecting a source, so do not offer either side as an executable completion.
|
|
484
|
+
const templateCommands = this.facade
|
|
485
|
+
.getPromptTemplates()
|
|
486
|
+
.filter((cmd) => !capabilityCommandNames.has(cmd.name))
|
|
487
|
+
.map((cmd) => ({
|
|
488
|
+
name: cmd.name,
|
|
489
|
+
description: this.prefixAutocompleteDescription(cmd.description, cmd.sourceInfo),
|
|
490
|
+
...(cmd.argumentHint && { argumentHint: cmd.argumentHint }),
|
|
491
|
+
}));
|
|
492
|
+
const builtinCommandNames = new Set(slashCommands.map((c) => c.name));
|
|
493
|
+
const resourceSkillCommandNames = new Set(this.facade.getSkills().skills.map((skill) => `skill:${skill.name}`));
|
|
494
|
+
const capabilityCommands = capabilityRegistrations
|
|
495
|
+
.filter(({ definition }) => !builtinCommandNames.has(definition.name) &&
|
|
496
|
+
!this.facade.getPromptTemplates().some((template) => template.name === definition.name) &&
|
|
497
|
+
!resourceSkillCommandNames.has(definition.name))
|
|
498
|
+
.map(({ definition }) => ({
|
|
499
|
+
name: definition.name,
|
|
500
|
+
description: definition.description,
|
|
501
|
+
...(definition.argumentHint && { argumentHint: definition.argumentHint }),
|
|
502
|
+
}));
|
|
503
|
+
// Build skill commands from session.skills (if enabled)
|
|
504
|
+
this.skillCommands.clear();
|
|
505
|
+
const skillCommandList = [];
|
|
506
|
+
if (this.settingsManager.getEnableSkillCommands()) {
|
|
507
|
+
for (const skill of this.facade.getSkills().skills) {
|
|
508
|
+
const commandName = `skill:${skill.name}`;
|
|
509
|
+
if (capabilityCommandNames.has(commandName))
|
|
510
|
+
continue;
|
|
511
|
+
this.skillCommands.set(commandName, skill.filePath);
|
|
512
|
+
skillCommandList.push({
|
|
513
|
+
name: commandName,
|
|
514
|
+
description: this.prefixAutocompleteDescription(skill.description, skill.sourceInfo),
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return new CombinedAutocompleteProvider([...slashCommands, ...templateCommands, ...capabilityCommands, ...skillCommandList], this.facade.getCwd(), this.fdPath);
|
|
519
|
+
}
|
|
520
|
+
setupAutocompleteProvider() {
|
|
521
|
+
const provider = this.createBaseAutocompleteProvider();
|
|
522
|
+
this.autocompleteProvider = provider;
|
|
523
|
+
this.defaultEditor.setAutocompleteProvider(provider);
|
|
524
|
+
if (this.editor !== this.defaultEditor) {
|
|
525
|
+
this.editor.setAutocompleteProvider?.(provider);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
showStartupNoticesIfNeeded() {
|
|
529
|
+
if (this.startupNoticesShown) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
this.startupNoticesShown = true;
|
|
533
|
+
if (!this.changelogMarkdown) {
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
if (this.chatContainer.children.length > 0) {
|
|
537
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
538
|
+
}
|
|
539
|
+
this.chatContainer.addChild(new DynamicBorder());
|
|
540
|
+
if (this.settingsManager.getCollapseChangelog()) {
|
|
541
|
+
const versionMatch = this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/);
|
|
542
|
+
const latestVersion = versionMatch ? versionMatch[1] : this.version;
|
|
543
|
+
const condensedText = `Updated to v${latestVersion}. Use ${theme.bold("/changelog")} to view full changelog.`;
|
|
544
|
+
this.chatContainer.addChild(new Text(condensedText, 1, 0));
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
|
|
548
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
549
|
+
this.chatContainer.addChild(new Markdown(this.changelogMarkdown.trim(), 1, 0, this.getMarkdownThemeWithSettings()));
|
|
550
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
551
|
+
}
|
|
552
|
+
this.chatContainer.addChild(new DynamicBorder());
|
|
553
|
+
}
|
|
554
|
+
mountInteractiveTui(tui, components) {
|
|
555
|
+
for (const component of components)
|
|
556
|
+
tui.addChild(component);
|
|
557
|
+
if (TuiLayouts.isViewportTUI(tui)) {
|
|
558
|
+
if (!this.fullscreenLayoutRoot)
|
|
559
|
+
throw new Error("Fullscreen layout is not initialized");
|
|
560
|
+
tui.setLayoutRoot(this.fullscreenLayoutRoot);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
stopInteractiveTui(fullscreenExitOutput) {
|
|
564
|
+
if (this.renderer.mode === "fullscreen" && fullscreenExitOutput === "transcript") {
|
|
565
|
+
while (this.renderer.hasOverlayEntries)
|
|
566
|
+
this.renderer.hideOverlay();
|
|
567
|
+
this.switchTuiMode("regular", false, false);
|
|
568
|
+
this.renderer.renderNow();
|
|
569
|
+
}
|
|
570
|
+
this.ui.stop({ preserveScreen: this.renderer.mode === "fullscreen" });
|
|
571
|
+
}
|
|
572
|
+
switchTuiMode(mode, restoreProgress = true, startRenderer = true) {
|
|
573
|
+
const previousUi = this.renderer;
|
|
574
|
+
if (mode === previousUi.mode)
|
|
575
|
+
return true;
|
|
576
|
+
if (previousUi.hasOverlayEntries)
|
|
577
|
+
return false;
|
|
578
|
+
const components = [...previousUi.children];
|
|
579
|
+
const focus = previousUi.getFocusedComponent();
|
|
580
|
+
const terminal = previousUi.terminal;
|
|
581
|
+
const showHardwareCursor = previousUi.getShowHardwareCursor();
|
|
582
|
+
const clearOnShrink = previousUi.getClearOnShrink();
|
|
583
|
+
const onDebug = previousUi.onDebug;
|
|
584
|
+
if (previousUi instanceof TuiMainScreen) {
|
|
585
|
+
this.mainScreenRenderState = previousUi.captureRenderState();
|
|
586
|
+
}
|
|
587
|
+
previousUi.stop({ preserveScreen: true });
|
|
588
|
+
previousUi.setFocus(null);
|
|
589
|
+
previousUi.clear();
|
|
590
|
+
if (TuiLayouts.isViewportTUI(previousUi))
|
|
591
|
+
previousUi.setLayoutRoot(undefined);
|
|
592
|
+
const nextUi = createInteractiveTui({
|
|
593
|
+
tuiMode: mode,
|
|
594
|
+
showHardwareCursor,
|
|
595
|
+
logDirectory: getAgentDir(),
|
|
596
|
+
terminal,
|
|
597
|
+
onRightClickPaste: this.onRightClickPaste,
|
|
598
|
+
fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
|
|
599
|
+
});
|
|
600
|
+
nextUi.setClearOnShrink(clearOnShrink);
|
|
601
|
+
nextUi.onDebug = onDebug;
|
|
602
|
+
if (nextUi instanceof TuiMainScreen && this.mainScreenRenderState) {
|
|
603
|
+
nextUi.restoreRenderState(this.mainScreenRenderState);
|
|
604
|
+
}
|
|
605
|
+
this.renderer = nextUi;
|
|
606
|
+
this.options.tuiMode = mode;
|
|
607
|
+
this.mountInteractiveTui(nextUi, components);
|
|
608
|
+
nextUi.invalidate();
|
|
609
|
+
nextUi.setFocus(focus);
|
|
610
|
+
if (!startRenderer)
|
|
611
|
+
return true;
|
|
612
|
+
nextUi.start();
|
|
613
|
+
this.themeController.rebindTui();
|
|
614
|
+
if (restoreProgress &&
|
|
615
|
+
this.settingsManager.getShowTerminalProgress() &&
|
|
616
|
+
(this.facade.isStreaming() || this.facade.isCompacting())) {
|
|
617
|
+
terminal.setProgress(true);
|
|
618
|
+
}
|
|
619
|
+
return true;
|
|
620
|
+
}
|
|
621
|
+
async init() {
|
|
622
|
+
if (this.isInitialized)
|
|
623
|
+
return;
|
|
624
|
+
this.registerSignalHandlers();
|
|
625
|
+
// Load changelog (only show new entries, skip for resumed sessions)
|
|
626
|
+
this.changelogMarkdown = this.getChangelogForDisplay();
|
|
627
|
+
if (this.facade.getScopedModels().length > 0 &&
|
|
628
|
+
(this.options.verbose || !this.settingsManager.getQuietStartup())) {
|
|
629
|
+
const modelList = this.facade
|
|
630
|
+
.getScopedModels()
|
|
631
|
+
.map((sm) => {
|
|
632
|
+
const thinkingStr = sm.thinkingLevel ? `:${sm.thinkingLevel}` : "";
|
|
633
|
+
return `${sm.model.id}${thinkingStr}`;
|
|
634
|
+
})
|
|
635
|
+
.join(", ");
|
|
636
|
+
const cycleKeys = this.keybindings.getKeys("app.model.cycleForward");
|
|
637
|
+
const cycleHint = cycleKeys.length > 0
|
|
638
|
+
? theme.fg("muted", ` (${formatKeyText(cycleKeys.join("/"), { capitalize: true })} to cycle)`)
|
|
639
|
+
: "";
|
|
640
|
+
console.log(theme.fg("dim", `Model scope: ${modelList}${cycleHint}`));
|
|
641
|
+
}
|
|
642
|
+
// Keep one component tree and remount it when changing renderers.
|
|
643
|
+
// Widget chrome is fixed empty; no initialization render needed.
|
|
644
|
+
this.transcriptScrollView = new TuiLayouts.ScrollView(this.documentContainer, {
|
|
645
|
+
follow: "end",
|
|
646
|
+
primary: true,
|
|
647
|
+
overscroll: "chain",
|
|
648
|
+
scrollbar: this.settingsManager.getFullscreenScrollbar(),
|
|
649
|
+
scrollbarStyle: (text) => theme.bg("scrollbarThumb", text),
|
|
650
|
+
});
|
|
651
|
+
const dock = new TuiLayouts.VStack([
|
|
652
|
+
{ component: this.pendingMessagesContainer, shrink: 1, minSize: 0 },
|
|
653
|
+
{ component: this.statusContainer, shrink: 1, minSize: 0 },
|
|
654
|
+
{ component: this.widgetContainerAbove, shrink: 1, minSize: 0 },
|
|
655
|
+
{ component: this.editorContainer, shrink: 1, minSize: 3 },
|
|
656
|
+
{ component: this.widgetContainerBelow, shrink: 1, minSize: 0 },
|
|
657
|
+
{ component: this.footerContainer, shrink: 1, minSize: 1 },
|
|
658
|
+
]);
|
|
659
|
+
this.fullscreenLayoutRoot = new TuiLayouts.VStack([
|
|
660
|
+
{ component: this.transcriptScrollView, basis: 0, grow: 1, shrink: 1, minSize: 1 },
|
|
661
|
+
{ component: dock, basis: "auto", grow: 0, shrink: 1, minSize: 1 },
|
|
662
|
+
]);
|
|
663
|
+
this.mountInteractiveTui(this.renderer, [
|
|
664
|
+
this.documentContainer,
|
|
665
|
+
this.pendingMessagesContainer,
|
|
666
|
+
this.statusContainer,
|
|
667
|
+
this.widgetContainerAbove,
|
|
668
|
+
this.editorContainer,
|
|
669
|
+
this.widgetContainerBelow,
|
|
670
|
+
this.footerContainer,
|
|
671
|
+
]);
|
|
672
|
+
// Accept text while startup completes, but only enable interrupt, exit, and submission feedback.
|
|
673
|
+
this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
|
|
674
|
+
this.defaultEditor.onCtrlD = () => this.handleCtrlD();
|
|
675
|
+
this.defaultEditor.onSubmit = (text) => this.handleStartupSubmit(text);
|
|
676
|
+
this.ui.setFocus(this.editor);
|
|
677
|
+
// Start the UI before initializing extensions so session_start handlers can use interactive dialogs
|
|
678
|
+
this.ui.start();
|
|
679
|
+
this.isInitialized = true;
|
|
680
|
+
await this.themeController.applyFromSettings();
|
|
681
|
+
// Add header with keybindings from config (unless silenced)
|
|
682
|
+
if (this.options.verbose || !this.settingsManager.getQuietStartup()) {
|
|
683
|
+
const logo = theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
|
|
684
|
+
// Build startup instructions using keybinding hint helpers
|
|
685
|
+
const hint = (keybinding, description) => keyHint(keybinding, description);
|
|
686
|
+
const expandedInstructions = [
|
|
687
|
+
hint("app.interrupt", "to interrupt"),
|
|
688
|
+
hint("app.clear", "to clear"),
|
|
689
|
+
rawKeyHint(`${keyText("app.clear")} twice`, "to exit"),
|
|
690
|
+
hint("app.exit", "to exit (empty)"),
|
|
691
|
+
hint("app.suspend", "to suspend"),
|
|
692
|
+
keyHint("tui.editor.deleteToLineEnd", "to delete to end"),
|
|
693
|
+
hint("app.thinking.cycle", "to cycle thinking level"),
|
|
694
|
+
rawKeyHint(`${keyText("app.model.cycleForward")}/${keyText("app.model.cycleBackward")}`, "to cycle models"),
|
|
695
|
+
hint("app.model.select", "to select model"),
|
|
696
|
+
hint("app.tools.expand", "to expand tools"),
|
|
697
|
+
hint("app.thinking.toggle", "to expand thinking"),
|
|
698
|
+
hint("app.editor.external", "for external editor"),
|
|
699
|
+
rawKeyHint("/", "for commands"),
|
|
700
|
+
rawKeyHint("!", "to run bash"),
|
|
701
|
+
rawKeyHint("!!", "to run bash (no context)"),
|
|
702
|
+
hint("app.message.followUp", "to queue follow-up"),
|
|
703
|
+
hint("app.message.dequeue", "to edit all queued messages"),
|
|
704
|
+
hint("app.clipboard.pasteImage", "to paste image (with text fallback)"),
|
|
705
|
+
rawKeyHint("drop files", "to attach"),
|
|
706
|
+
].join("\n");
|
|
707
|
+
const compactInstructions = [
|
|
708
|
+
hint("app.interrupt", "interrupt"),
|
|
709
|
+
rawKeyHint(`${keyText("app.clear")}/${keyText("app.exit")}`, "clear/exit"),
|
|
710
|
+
rawKeyHint("/", "commands"),
|
|
711
|
+
rawKeyHint("!", "bash"),
|
|
712
|
+
hint("app.tools.expand", "more"),
|
|
713
|
+
].join(theme.fg("muted", " · "));
|
|
714
|
+
const compactOnboarding = theme.fg("dim", `Press ${keyText("app.tools.expand")} to show full startup help and loaded resources.`);
|
|
715
|
+
const onboarding = theme.fg("dim", `Agent Forge can explain its own features and look up its docs. Ask it how to use or extend Agent Forge.`);
|
|
716
|
+
this.builtInHeader = new ExpandableText(() => `${logo}\n${compactInstructions}\n${compactOnboarding}\n\n${onboarding}`, () => `${logo}\n${expandedInstructions}\n\n${onboarding}`, this.getStartupExpansionState(), 1, 0);
|
|
717
|
+
// Setup UI layout
|
|
718
|
+
this.headerContainer.addChild(new Spacer(1));
|
|
719
|
+
this.headerContainer.addChild(this.builtInHeader);
|
|
720
|
+
this.headerContainer.addChild(new Spacer(1));
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
// Minimal header when silenced
|
|
724
|
+
this.builtInHeader = new Text("", 0, 0);
|
|
725
|
+
this.headerContainer.addChild(this.builtInHeader);
|
|
726
|
+
}
|
|
727
|
+
this.ui.requestRender();
|
|
728
|
+
// Ensure fd and rg are available after mounting the TUI (downloads if missing, adds to PATH via getBinDir)
|
|
729
|
+
// so slow downloads do not make startup appear frozen.
|
|
730
|
+
// Both are needed: fd for autocomplete, rg for grep tool and bash commands.
|
|
731
|
+
const [fdPath] = await Promise.all([
|
|
732
|
+
ensureTool("fd", (status) => this.showManagedToolStatus(status)),
|
|
733
|
+
ensureTool("rg", (status) => this.showManagedToolStatus(status)),
|
|
734
|
+
]);
|
|
735
|
+
this.fdPath = fdPath;
|
|
736
|
+
// Enable the remaining input handlers only after managed-tool setup completes.
|
|
737
|
+
this.setupKeyHandlers();
|
|
738
|
+
this.setupEditorSubmitHandler();
|
|
739
|
+
this.ui.requestRender();
|
|
740
|
+
// Initialize extensions first so resources are shown before messages
|
|
741
|
+
await this.rebindCurrentSession();
|
|
742
|
+
// Render initial messages AFTER showing loaded resources
|
|
743
|
+
this.renderInitialMessages();
|
|
744
|
+
// Cross-suite resume detection (设计 §10.1 最小可用版): a `-c`/resumed
|
|
745
|
+
// session may carry a binding from a different suite than the one the
|
|
746
|
+
// runtime just assembled it with. Record the fact; conversion is out of
|
|
747
|
+
// scope.
|
|
748
|
+
this.detectCrossSuiteResume();
|
|
749
|
+
// Set up theme file watcher
|
|
750
|
+
onThemeChange(() => {
|
|
751
|
+
this.ui.invalidate();
|
|
752
|
+
this.updateEditorBorderColor();
|
|
753
|
+
this.ui.requestRender();
|
|
754
|
+
});
|
|
755
|
+
// Set up git branch watcher (uses provider instead of footer)
|
|
756
|
+
this.footerDataProvider.onBranchChange(() => {
|
|
757
|
+
this.ui.requestRender();
|
|
758
|
+
});
|
|
759
|
+
// Initialize available provider count for footer display
|
|
760
|
+
await this.updateAvailableProviderCount();
|
|
761
|
+
// Unresolved startup suite id (设计 §9): with the TUI ready, let the user
|
|
762
|
+
// pick a suite before the first prompt; the session is then rebuilt with
|
|
763
|
+
// the selected suite. print/RPC never reach here (structured exit in main.ts).
|
|
764
|
+
if (this.options.pendingSuiteSelector) {
|
|
765
|
+
await this.showStartupSuiteSelector(this.options.pendingSuiteSelector);
|
|
766
|
+
}
|
|
767
|
+
// Flush the completed startup state before loading the remaining syntax grammars.
|
|
768
|
+
this.ui.renderNow();
|
|
769
|
+
void loadAllHighlightLanguages().then(() => {
|
|
770
|
+
if (!this.isInitialized)
|
|
771
|
+
return;
|
|
772
|
+
this.ui.invalidate();
|
|
773
|
+
this.ui.requestRender();
|
|
774
|
+
});
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Update terminal title with session name and cwd.
|
|
778
|
+
*/
|
|
779
|
+
updateTerminalTitle() {
|
|
780
|
+
const cwdBasename = path.basename(this.facade.getCwd());
|
|
781
|
+
const sessionName = this.facade.getSessionName();
|
|
782
|
+
if (sessionName) {
|
|
783
|
+
this.ui.terminal.setTitle(`${APP_TITLE} - ${sessionName} - ${cwdBasename}`);
|
|
784
|
+
}
|
|
785
|
+
else {
|
|
786
|
+
this.ui.terminal.setTitle(`${APP_TITLE} - ${cwdBasename}`);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
/**
|
|
790
|
+
* Run the interactive mode. This is the main entry point.
|
|
791
|
+
* Initializes the UI, shows warnings, processes initial messages, and starts the interactive loop.
|
|
792
|
+
*/
|
|
793
|
+
async run() {
|
|
794
|
+
await this.init();
|
|
795
|
+
if (!process.env.AGENT_FORGE_OFFLINE) {
|
|
796
|
+
const controller = new AbortController();
|
|
797
|
+
const timeout = setTimeout(() => controller.abort(), 15_000);
|
|
798
|
+
void refreshModelCatalogs(this.facade.getModelCatalog(), controller.signal)
|
|
799
|
+
.then(() => this.updateAvailableProviderCount())
|
|
800
|
+
.catch(() => { })
|
|
801
|
+
.finally(() => clearTimeout(timeout));
|
|
802
|
+
}
|
|
803
|
+
// Start version check asynchronously
|
|
804
|
+
checkForNewVersion(this.version).then((newRelease) => {
|
|
805
|
+
if (newRelease) {
|
|
806
|
+
this.showNewVersionNotification(newRelease);
|
|
807
|
+
}
|
|
808
|
+
});
|
|
809
|
+
// Start package update check asynchronously
|
|
810
|
+
this.checkForPackageUpdates()
|
|
811
|
+
.then((updates) => {
|
|
812
|
+
if (updates.length > 0) {
|
|
813
|
+
this.showPackageUpdateNotification(updates);
|
|
814
|
+
}
|
|
815
|
+
})
|
|
816
|
+
.finally(() => {
|
|
817
|
+
// On Windows, npm can overwrite the shared console title while checking
|
|
818
|
+
// extension package versions. Restore the product title after the startup check.
|
|
819
|
+
if (process.platform === "win32" && this.isInitialized) {
|
|
820
|
+
this.updateTerminalTitle();
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
// Check tmux keyboard setup asynchronously
|
|
824
|
+
this.checkTmuxKeyboardSetup().then((warning) => {
|
|
825
|
+
if (warning) {
|
|
826
|
+
this.showWarning(warning);
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
// Show startup warnings
|
|
830
|
+
const { migratedProviders, startupDiagnostics, modelFallbackMessage, initialMessage, initialImages, initialMessages, } = this.options;
|
|
831
|
+
for (const diagnostic of startupDiagnostics ?? []) {
|
|
832
|
+
if (diagnostic.type === "error") {
|
|
833
|
+
this.showError(diagnostic.message);
|
|
834
|
+
}
|
|
835
|
+
else if (diagnostic.type === "warning") {
|
|
836
|
+
this.showWarning(diagnostic.message);
|
|
837
|
+
}
|
|
838
|
+
else {
|
|
839
|
+
this.showStatus(diagnostic.message);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (migratedProviders && migratedProviders.length > 0) {
|
|
843
|
+
this.showWarning(`Migrated credentials to auth.json: ${migratedProviders.join(", ")}`);
|
|
844
|
+
}
|
|
845
|
+
const modelsJsonError = this.facade.getModelsError();
|
|
846
|
+
if (modelsJsonError) {
|
|
847
|
+
this.showError(`models.json error: ${modelsJsonError}`);
|
|
848
|
+
}
|
|
849
|
+
if (modelFallbackMessage) {
|
|
850
|
+
this.showWarning(modelFallbackMessage);
|
|
851
|
+
}
|
|
852
|
+
void this.maybeWarnAboutAnthropicSubscriptionAuth();
|
|
853
|
+
// Process initial messages
|
|
854
|
+
if (initialMessage) {
|
|
855
|
+
try {
|
|
856
|
+
await this.facade.prompt(initialMessage, { images: initialImages });
|
|
857
|
+
}
|
|
858
|
+
catch (error) {
|
|
859
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
860
|
+
this.showError(errorMessage);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
if (initialMessages) {
|
|
864
|
+
for (const message of initialMessages) {
|
|
865
|
+
try {
|
|
866
|
+
await this.facade.prompt(message);
|
|
867
|
+
}
|
|
868
|
+
catch (error) {
|
|
869
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
870
|
+
this.showError(errorMessage);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
// Main interactive loop
|
|
875
|
+
while (true) {
|
|
876
|
+
const userInput = await this.getUserInput();
|
|
877
|
+
try {
|
|
878
|
+
await this.promptWithEmptyReplyRetry(userInput);
|
|
879
|
+
}
|
|
880
|
+
catch (error) {
|
|
881
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error occurred";
|
|
882
|
+
this.showError(errorMessage);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* One-shot empty-reply auto-continuation (#5, print-mode parity): when a
|
|
888
|
+
* submitted turn finishes with an empty assistant text — and the model was
|
|
889
|
+
* neither aborted nor errored — nudge the model once to continue the task.
|
|
890
|
+
* The retry itself is never retried, so the budget is capped at one
|
|
891
|
+
* continuation per submission and this cannot loop.
|
|
892
|
+
*/
|
|
893
|
+
async promptWithEmptyReplyRetry(text) {
|
|
894
|
+
const seen = this.facade.getState().messages.length;
|
|
895
|
+
await this.facade.prompt(text);
|
|
896
|
+
const lastAssistant = [...this.facade.getState().messages.slice(seen)]
|
|
897
|
+
.reverse()
|
|
898
|
+
.find((m) => m.role === "assistant");
|
|
899
|
+
if (lastAssistant === undefined)
|
|
900
|
+
return;
|
|
901
|
+
if (lastAssistant.stopReason === "error" || lastAssistant.stopReason === "aborted")
|
|
902
|
+
return;
|
|
903
|
+
if (contentText(lastAssistant.content).trim().length > 0)
|
|
904
|
+
return;
|
|
905
|
+
this.showStatus("Previous reply was empty, retrying...");
|
|
906
|
+
await this.facade.prompt(EMPTY_REPLY_RETRY_PROMPT);
|
|
907
|
+
}
|
|
908
|
+
async checkForPackageUpdates() {
|
|
909
|
+
if (process.env.AGENT_FORGE_OFFLINE) {
|
|
910
|
+
return [];
|
|
911
|
+
}
|
|
912
|
+
try {
|
|
913
|
+
const packageManager = new DefaultPackageManager({
|
|
914
|
+
cwd: this.facade.getCwd(),
|
|
915
|
+
agentDir: getAgentDir(),
|
|
916
|
+
settingsManager: this.settingsManager,
|
|
917
|
+
});
|
|
918
|
+
const updates = await packageManager.checkForAvailableUpdates();
|
|
919
|
+
return updates.map((update) => update.displayName);
|
|
920
|
+
}
|
|
921
|
+
catch {
|
|
922
|
+
return [];
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
async checkTmuxKeyboardSetup() {
|
|
926
|
+
if (!process.env.TMUX)
|
|
927
|
+
return undefined;
|
|
928
|
+
const runTmuxShow = (option) => {
|
|
929
|
+
return new Promise((resolve) => {
|
|
930
|
+
const proc = spawn("tmux", ["show", "-gv", option], {
|
|
931
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
932
|
+
});
|
|
933
|
+
let stdout = "";
|
|
934
|
+
const timer = setTimeout(() => {
|
|
935
|
+
proc.kill();
|
|
936
|
+
resolve(undefined);
|
|
937
|
+
}, 2000);
|
|
938
|
+
proc.stdout?.on("data", (data) => {
|
|
939
|
+
stdout += data.toString();
|
|
940
|
+
});
|
|
941
|
+
proc.on("error", () => {
|
|
942
|
+
clearTimeout(timer);
|
|
943
|
+
resolve(undefined);
|
|
944
|
+
});
|
|
945
|
+
proc.on("close", (code) => {
|
|
946
|
+
clearTimeout(timer);
|
|
947
|
+
resolve(code === 0 ? stdout.trim() : undefined);
|
|
948
|
+
});
|
|
949
|
+
});
|
|
950
|
+
};
|
|
951
|
+
const [extendedKeys, extendedKeysFormat] = await Promise.all([
|
|
952
|
+
runTmuxShow("extended-keys"),
|
|
953
|
+
runTmuxShow("extended-keys-format"),
|
|
954
|
+
]);
|
|
955
|
+
// If we couldn't query tmux (timeout, sandbox, etc.), don't warn
|
|
956
|
+
if (extendedKeys === undefined)
|
|
957
|
+
return undefined;
|
|
958
|
+
if (extendedKeys !== "on" && extendedKeys !== "always") {
|
|
959
|
+
return "tmux extended-keys is off. Modified Enter keys may not work. Add `set -g extended-keys on` to ~/.tmux.conf and restart tmux.";
|
|
960
|
+
}
|
|
961
|
+
if (extendedKeysFormat === "xterm") {
|
|
962
|
+
return "tmux extended-keys-format is xterm. Agent Forge works best with csi-u. Add `set -g extended-keys-format csi-u` to ~/.tmux.conf and restart tmux.";
|
|
963
|
+
}
|
|
964
|
+
return undefined;
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Get changelog entries to display on startup.
|
|
968
|
+
* Only shows new entries since last seen version, skips for resumed sessions.
|
|
969
|
+
*/
|
|
970
|
+
getChangelogForDisplay() {
|
|
971
|
+
// Skip changelog for resumed/continued sessions (already have messages)
|
|
972
|
+
if (this.facade.getState().messages.length > 0) {
|
|
973
|
+
return undefined;
|
|
974
|
+
}
|
|
975
|
+
const lastVersion = this.settingsManager.getLastChangelogVersion();
|
|
976
|
+
const changelogPath = getChangelogPath();
|
|
977
|
+
const entries = parseChangelog(changelogPath);
|
|
978
|
+
if (!lastVersion) {
|
|
979
|
+
// Fresh install - record the version, don't show changelog
|
|
980
|
+
this.settingsManager.setLastChangelogVersion(VERSION);
|
|
981
|
+
return undefined;
|
|
982
|
+
}
|
|
983
|
+
const newEntries = getNewEntries(entries, lastVersion);
|
|
984
|
+
if (newEntries.length > 0) {
|
|
985
|
+
this.settingsManager.setLastChangelogVersion(VERSION);
|
|
986
|
+
return newEntries.map((e) => normalizeChangelogLinks(e.content, e)).join("\n\n");
|
|
987
|
+
}
|
|
988
|
+
return undefined;
|
|
989
|
+
}
|
|
990
|
+
getMarkdownThemeWithSettings() {
|
|
991
|
+
return {
|
|
992
|
+
...getMarkdownTheme(),
|
|
993
|
+
codeBlockIndent: this.settingsManager.getCodeBlockIndent(),
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
// =========================================================================
|
|
997
|
+
// Extension System
|
|
998
|
+
// =========================================================================
|
|
999
|
+
formatDisplayPath(p) {
|
|
1000
|
+
return formatDisplayPath(p);
|
|
1001
|
+
}
|
|
1002
|
+
formatContextPath(p) {
|
|
1003
|
+
return formatContextPath(p, this.facade.getCwd());
|
|
1004
|
+
}
|
|
1005
|
+
getStartupExpansionState() {
|
|
1006
|
+
return this.options.verbose || this.toolOutputExpanded;
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Get a short path relative to the package root for display.
|
|
1010
|
+
*/
|
|
1011
|
+
getShortPath(fullPath, sourceInfo) {
|
|
1012
|
+
return getShortPath(fullPath, sourceInfo);
|
|
1013
|
+
}
|
|
1014
|
+
getCompactPathLabel(resourcePath, sourceInfo) {
|
|
1015
|
+
return getCompactPathLabel(resourcePath, sourceInfo);
|
|
1016
|
+
}
|
|
1017
|
+
// The following formatting delegates have no in-class call sites (their
|
|
1018
|
+
// implementations moved to interactive-format.ts) but remain part of the
|
|
1019
|
+
// prototype contract, so they stay public instead of private.
|
|
1020
|
+
getCompactPackageSourceLabel(sourceInfo) {
|
|
1021
|
+
return getCompactPackageSourceLabel(sourceInfo);
|
|
1022
|
+
}
|
|
1023
|
+
getDisplaySourceInfo(sourceInfo) {
|
|
1024
|
+
return getDisplaySourceInfo(sourceInfo);
|
|
1025
|
+
}
|
|
1026
|
+
getScopeGroup(sourceInfo) {
|
|
1027
|
+
return getScopeGroup(sourceInfo);
|
|
1028
|
+
}
|
|
1029
|
+
isPackageSource(sourceInfo) {
|
|
1030
|
+
return isPackageSource(sourceInfo);
|
|
1031
|
+
}
|
|
1032
|
+
buildScopeGroups(items) {
|
|
1033
|
+
return buildScopeGroups(items);
|
|
1034
|
+
}
|
|
1035
|
+
formatScopeGroups(groups, options) {
|
|
1036
|
+
return formatScopeGroups(groups, options);
|
|
1037
|
+
}
|
|
1038
|
+
findSourceInfoForPath(p, sourceInfos) {
|
|
1039
|
+
return findSourceInfoForPath(p, sourceInfos);
|
|
1040
|
+
}
|
|
1041
|
+
formatPathWithSource(p, sourceInfo) {
|
|
1042
|
+
return formatPathWithSource(p, sourceInfo);
|
|
1043
|
+
}
|
|
1044
|
+
formatDiagnostics(diagnostics, sourceInfos) {
|
|
1045
|
+
return formatDiagnostics(diagnostics, sourceInfos);
|
|
1046
|
+
}
|
|
1047
|
+
showLoadedResources(options) {
|
|
1048
|
+
// Resource rendering is idempotent; chat clears no longer clear this separate container.
|
|
1049
|
+
this.loadedResourcesContainer.clear();
|
|
1050
|
+
const showListing = options?.force || this.options.verbose || !this.settingsManager.getQuietStartup();
|
|
1051
|
+
const showDiagnostics = showListing || options?.showDiagnosticsWhenQuiet === true;
|
|
1052
|
+
if (!showListing && !showDiagnostics) {
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
const sectionHeader = (name, color = "mdHeading") => theme.fg(color, `[${name}]`);
|
|
1056
|
+
const formatCompactList = (items, options) => {
|
|
1057
|
+
const labels = items.map((item) => item.trim()).filter((item) => item.length > 0);
|
|
1058
|
+
if (options?.sort !== false) {
|
|
1059
|
+
labels.sort((a, b) => a.localeCompare(b));
|
|
1060
|
+
}
|
|
1061
|
+
return theme.fg("dim", ` ${labels.join(", ")}`);
|
|
1062
|
+
};
|
|
1063
|
+
const addLoadedSection = (name, collapsedBody, expandedBody = collapsedBody, color = "mdHeading") => {
|
|
1064
|
+
const section = new ExpandableText(() => `${sectionHeader(name, color)}\n${collapsedBody}`, () => `${sectionHeader(name, color)}\n${expandedBody}`, this.getStartupExpansionState(), 0, 0);
|
|
1065
|
+
this.loadedResourcesContainer.addChild(section);
|
|
1066
|
+
this.loadedResourcesContainer.addChild(new Spacer(1));
|
|
1067
|
+
};
|
|
1068
|
+
const skillsResult = this.facade.getSkills();
|
|
1069
|
+
const promptsResult = this.facade.getPrompts();
|
|
1070
|
+
const themesResult = this.facade.getThemes();
|
|
1071
|
+
const sourceInfos = new Map();
|
|
1072
|
+
for (const skill of skillsResult.skills) {
|
|
1073
|
+
if (skill.sourceInfo) {
|
|
1074
|
+
sourceInfos.set(skill.filePath, skill.sourceInfo);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
for (const prompt of promptsResult.prompts) {
|
|
1078
|
+
if (prompt.sourceInfo) {
|
|
1079
|
+
sourceInfos.set(prompt.filePath, prompt.sourceInfo);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
for (const loadedTheme of themesResult.themes) {
|
|
1083
|
+
if (loadedTheme.sourcePath && loadedTheme.sourceInfo) {
|
|
1084
|
+
sourceInfos.set(loadedTheme.sourcePath, loadedTheme.sourceInfo);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
if (showListing) {
|
|
1088
|
+
const systemPromptSource = this.facade.getSystemPromptSource();
|
|
1089
|
+
const contextFiles = [
|
|
1090
|
+
...(systemPromptSource ? [systemPromptSource] : []),
|
|
1091
|
+
...this.facade.getAppendSystemPromptSources(),
|
|
1092
|
+
...this.facade.getAgentsFiles().agentsFiles,
|
|
1093
|
+
];
|
|
1094
|
+
if (contextFiles.length > 0) {
|
|
1095
|
+
this.loadedResourcesContainer.addChild(new Spacer(1));
|
|
1096
|
+
const contextList = contextFiles
|
|
1097
|
+
.map((f) => theme.fg("dim", ` ${this.formatDisplayPath(f.path)}`))
|
|
1098
|
+
.join("\n");
|
|
1099
|
+
const contextCompactList = formatCompactList(contextFiles.map((contextFile) => this.formatContextPath(contextFile.path)), { sort: false });
|
|
1100
|
+
addLoadedSection("Context", contextCompactList, contextList);
|
|
1101
|
+
}
|
|
1102
|
+
const skills = skillsResult.skills;
|
|
1103
|
+
if (skills.length > 0) {
|
|
1104
|
+
const groups = this.buildScopeGroups(skills.map((skill) => ({ path: skill.filePath, sourceInfo: skill.sourceInfo })));
|
|
1105
|
+
const skillList = this.formatScopeGroups(groups, {
|
|
1106
|
+
formatPath: (item) => this.formatDisplayPath(item.path),
|
|
1107
|
+
formatPackagePath: (item) => this.getShortPath(item.path, item.sourceInfo),
|
|
1108
|
+
});
|
|
1109
|
+
const skillCompactList = formatCompactList(skills.map((skill) => skill.name));
|
|
1110
|
+
addLoadedSection("Skills", skillCompactList, skillList);
|
|
1111
|
+
}
|
|
1112
|
+
const templates = this.facade.getPromptTemplates();
|
|
1113
|
+
if (templates.length > 0) {
|
|
1114
|
+
const groups = this.buildScopeGroups(templates.map((template) => ({ path: template.filePath, sourceInfo: template.sourceInfo })));
|
|
1115
|
+
const templateByPath = new Map(templates.map((t) => [t.filePath, t]));
|
|
1116
|
+
const templateList = this.formatScopeGroups(groups, {
|
|
1117
|
+
formatPath: (item) => {
|
|
1118
|
+
const template = templateByPath.get(item.path);
|
|
1119
|
+
// v8 ignore next -- templateByPath is built from the same templates as the groups
|
|
1120
|
+
return template ? `/${template.name}` : this.formatDisplayPath(item.path);
|
|
1121
|
+
},
|
|
1122
|
+
formatPackagePath: (item) => {
|
|
1123
|
+
const template = templateByPath.get(item.path);
|
|
1124
|
+
// v8 ignore next -- templateByPath is built from the same templates as the groups
|
|
1125
|
+
return template ? `/${template.name}` : this.formatDisplayPath(item.path);
|
|
1126
|
+
},
|
|
1127
|
+
});
|
|
1128
|
+
const promptCompactList = formatCompactList(templates.map((template) => `/${template.name}`));
|
|
1129
|
+
addLoadedSection("Prompts", promptCompactList, templateList);
|
|
1130
|
+
}
|
|
1131
|
+
// Show loaded themes (excluding built-in)
|
|
1132
|
+
const loadedThemes = themesResult.themes;
|
|
1133
|
+
const customThemes = loadedThemes.filter((t) => t.sourcePath);
|
|
1134
|
+
if (customThemes.length > 0) {
|
|
1135
|
+
const groups = this.buildScopeGroups(customThemes.map((loadedTheme) => ({
|
|
1136
|
+
path: loadedTheme.sourcePath,
|
|
1137
|
+
sourceInfo: loadedTheme.sourceInfo,
|
|
1138
|
+
})));
|
|
1139
|
+
const themeList = this.formatScopeGroups(groups, {
|
|
1140
|
+
formatPath: (item) => this.formatDisplayPath(item.path),
|
|
1141
|
+
formatPackagePath: (item) => this.getShortPath(item.path, item.sourceInfo),
|
|
1142
|
+
});
|
|
1143
|
+
const themeCompactList = formatCompactList(customThemes.map((loadedTheme) => loadedTheme.name ?? this.getCompactPathLabel(loadedTheme.sourcePath, loadedTheme.sourceInfo)));
|
|
1144
|
+
addLoadedSection("Themes", themeCompactList, themeList);
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
// v8 ignore next -- showDiagnostics 为假时上方已提前返回,此处恒为真
|
|
1148
|
+
if (showDiagnostics) {
|
|
1149
|
+
const skillDiagnostics = skillsResult.diagnostics;
|
|
1150
|
+
if (skillDiagnostics.length > 0) {
|
|
1151
|
+
const warningLines = this.formatDiagnostics(skillDiagnostics, sourceInfos);
|
|
1152
|
+
this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Skill conflicts]")}\n${warningLines}`, 0, 0));
|
|
1153
|
+
this.loadedResourcesContainer.addChild(new Spacer(1));
|
|
1154
|
+
}
|
|
1155
|
+
const promptDiagnostics = promptsResult.diagnostics;
|
|
1156
|
+
if (promptDiagnostics.length > 0) {
|
|
1157
|
+
const warningLines = this.formatDiagnostics(promptDiagnostics, sourceInfos);
|
|
1158
|
+
this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Prompt conflicts]")}\n${warningLines}`, 0, 0));
|
|
1159
|
+
this.loadedResourcesContainer.addChild(new Spacer(1));
|
|
1160
|
+
}
|
|
1161
|
+
const themeDiagnostics = themesResult.diagnostics;
|
|
1162
|
+
if (themeDiagnostics.length > 0) {
|
|
1163
|
+
const warningLines = this.formatDiagnostics(themeDiagnostics, sourceInfos);
|
|
1164
|
+
this.loadedResourcesContainer.addChild(new Text(`${theme.fg("warning", "[Theme conflicts]")}\n${warningLines}`, 0, 0));
|
|
1165
|
+
this.loadedResourcesContainer.addChild(new Spacer(1));
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Run the session-start boundary for the current session (capability
|
|
1171
|
+
* session.start@1 dispatch + resource discovery pass) and refresh the
|
|
1172
|
+
* startup surfaces that read session resources.
|
|
1173
|
+
*/
|
|
1174
|
+
async bindCurrentSessionExtensions() {
|
|
1175
|
+
await this.session.startSession();
|
|
1176
|
+
setRegisteredThemes(buildRegisteredThemes(this.facade.getThemes().themes));
|
|
1177
|
+
this.setupAutocompleteProvider();
|
|
1178
|
+
this.showLoadedResources({ force: false, showDiagnosticsWhenQuiet: true });
|
|
1179
|
+
this.showStartupNoticesIfNeeded();
|
|
1180
|
+
}
|
|
1181
|
+
applyFullscreenScrollbarSetting() {
|
|
1182
|
+
this.transcriptScrollView?.setScrollbar(this.settingsManager.getFullscreenScrollbar());
|
|
1183
|
+
}
|
|
1184
|
+
applyRuntimeSettings() {
|
|
1185
|
+
setCapabilityOverrides(this.settingsManager.getTerminalCapabilityOverrides());
|
|
1186
|
+
configureHttpDispatcher(this.settingsManager.getHttpIdleTimeoutMs());
|
|
1187
|
+
this.applyFullscreenScrollbarSetting();
|
|
1188
|
+
if (this.renderer instanceof TuiAltScreen) {
|
|
1189
|
+
this.renderer.setCopyOnSelect(this.settingsManager.getFullscreenCopyOnSelect());
|
|
1190
|
+
}
|
|
1191
|
+
this.footer.setSession(this.session);
|
|
1192
|
+
this.footer.setAutoCompactEnabled(this.facade.isAutoCompactionEnabled());
|
|
1193
|
+
this.footerDataProvider.setCwd(this.facade.getCwd());
|
|
1194
|
+
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
1195
|
+
this.outputPad = this.settingsManager.getOutputPad();
|
|
1196
|
+
this.ui.setShowHardwareCursor(this.settingsManager.getShowHardwareCursor());
|
|
1197
|
+
const clearOnShrink = this.settingsManager.getClearOnShrink();
|
|
1198
|
+
this.ui.setClearOnShrink(clearOnShrink);
|
|
1199
|
+
if (!clearOnShrink && !this.activeStatusIndicator) {
|
|
1200
|
+
this.statusContainer.clear();
|
|
1201
|
+
}
|
|
1202
|
+
const editorPaddingX = this.settingsManager.getEditorPaddingX();
|
|
1203
|
+
const autocompleteMaxVisible = this.settingsManager.getAutocompleteMaxVisible();
|
|
1204
|
+
this.defaultEditor.setPaddingX(editorPaddingX);
|
|
1205
|
+
this.defaultEditor.setAutocompleteMaxVisible(autocompleteMaxVisible);
|
|
1206
|
+
if (this.editor !== this.defaultEditor) {
|
|
1207
|
+
this.editor.setPaddingX?.(editorPaddingX);
|
|
1208
|
+
this.editor.setAutocompleteMaxVisible?.(autocompleteMaxVisible);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
async rebindCurrentSession(options = {}) {
|
|
1212
|
+
const session = this.session;
|
|
1213
|
+
this.unsubscribe?.();
|
|
1214
|
+
this.unsubscribe = undefined;
|
|
1215
|
+
this.applyRuntimeSettings();
|
|
1216
|
+
if (options.renderBeforeBind) {
|
|
1217
|
+
this.renderCurrentSessionState();
|
|
1218
|
+
this.subscribeToAgent();
|
|
1219
|
+
}
|
|
1220
|
+
await this.bindCurrentSessionExtensions();
|
|
1221
|
+
if (this.session !== session) {
|
|
1222
|
+
return;
|
|
1223
|
+
}
|
|
1224
|
+
if (!options.renderBeforeBind) {
|
|
1225
|
+
this.subscribeToAgent();
|
|
1226
|
+
}
|
|
1227
|
+
await this.updateAvailableProviderCount();
|
|
1228
|
+
this.updateEditorBorderColor();
|
|
1229
|
+
this.updateTerminalTitle();
|
|
1230
|
+
}
|
|
1231
|
+
async handleFatalRuntimeError(prefix, error) {
|
|
1232
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1233
|
+
this.showError(`${prefix}: ${message}`);
|
|
1234
|
+
stopThemeWatcher();
|
|
1235
|
+
this.stop("transcript");
|
|
1236
|
+
process.exit(1);
|
|
1237
|
+
}
|
|
1238
|
+
renderCurrentSessionState() {
|
|
1239
|
+
this.loadedResourcesContainer.clear();
|
|
1240
|
+
this.chatContainer.clear();
|
|
1241
|
+
this.pendingMessagesContainer.clear();
|
|
1242
|
+
this.compactionQueuedMessages = [];
|
|
1243
|
+
this.streamingComponent = undefined;
|
|
1244
|
+
this.streamingMessage = undefined;
|
|
1245
|
+
this.pendingTools.clear();
|
|
1246
|
+
this.renderInitialMessages();
|
|
1247
|
+
}
|
|
1248
|
+
/**
|
|
1249
|
+
* Get a registered tool definition by name (for custom rendering).
|
|
1250
|
+
*/
|
|
1251
|
+
getRegisteredToolDefinition(toolName) {
|
|
1252
|
+
return this.facade.getToolDefinition(toolName);
|
|
1253
|
+
}
|
|
1254
|
+
getMarkdownTransformers() {
|
|
1255
|
+
return [this.mermaidMarkdownTransformer];
|
|
1256
|
+
}
|
|
1257
|
+
showStatusIndicator(indicator) {
|
|
1258
|
+
this.activeStatusIndicator?.dispose();
|
|
1259
|
+
this.activeStatusIndicator = indicator;
|
|
1260
|
+
this.statusContainer.clear();
|
|
1261
|
+
this.statusContainer.addChild(indicator);
|
|
1262
|
+
}
|
|
1263
|
+
clearStatusIndicator(kind) {
|
|
1264
|
+
if (kind && this.activeStatusIndicator?.kind !== kind) {
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
const hadActiveStatusIndicator = this.activeStatusIndicator !== undefined;
|
|
1268
|
+
this.activeStatusIndicator?.dispose();
|
|
1269
|
+
this.activeStatusIndicator = undefined;
|
|
1270
|
+
this.statusContainer.clear();
|
|
1271
|
+
if (hadActiveStatusIndicator && this.options.tuiMode === "regular" && this.ui.getClearOnShrink()) {
|
|
1272
|
+
this.statusContainer.addChild(this.idleStatus);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
showWorkingStatusIndicator() {
|
|
1276
|
+
this.showStatusIndicator(new WorkingStatusIndicator(this.ui, this.workingMessage ?? this.defaultWorkingMessage, this.workingIndicatorOptions));
|
|
1277
|
+
}
|
|
1278
|
+
setWorkingVisible(visible) {
|
|
1279
|
+
this.workingVisible = visible;
|
|
1280
|
+
if (!visible) {
|
|
1281
|
+
this.clearStatusIndicator("working");
|
|
1282
|
+
this.ui.requestRender();
|
|
1283
|
+
return;
|
|
1284
|
+
}
|
|
1285
|
+
if (this.facade.isStreaming() && this.activeStatusIndicator?.kind !== "working") {
|
|
1286
|
+
this.showWorkingStatusIndicator();
|
|
1287
|
+
}
|
|
1288
|
+
this.ui.requestRender();
|
|
1289
|
+
}
|
|
1290
|
+
setWorkingIndicator(options) {
|
|
1291
|
+
this.workingIndicatorOptions = options;
|
|
1292
|
+
if (this.activeStatusIndicator?.kind === "working") {
|
|
1293
|
+
this.activeStatusIndicator.setIndicator(options);
|
|
1294
|
+
}
|
|
1295
|
+
this.ui.requestRender();
|
|
1296
|
+
}
|
|
1297
|
+
setHiddenThinkingLabel(label) {
|
|
1298
|
+
this.hiddenThinkingLabel = label ?? this.defaultHiddenThinkingLabel;
|
|
1299
|
+
for (const child of this.chatContainer.children) {
|
|
1300
|
+
if (child instanceof AssistantMessageComponent) {
|
|
1301
|
+
child.setHiddenThinkingLabel(this.hiddenThinkingLabel);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
if (this.streamingComponent) {
|
|
1305
|
+
this.streamingComponent.setHiddenThinkingLabel(this.hiddenThinkingLabel);
|
|
1306
|
+
}
|
|
1307
|
+
this.ui.requestRender();
|
|
1308
|
+
}
|
|
1309
|
+
/**
|
|
1310
|
+
* Set the working/loading message shown during streaming. Host entry wired
|
|
1311
|
+
* through ./interactive-dialog-ui.ts; restores the default when undefined.
|
|
1312
|
+
*/
|
|
1313
|
+
setWorkingMessage(message) {
|
|
1314
|
+
this.workingMessage = message;
|
|
1315
|
+
if (this.activeStatusIndicator?.kind === "working") {
|
|
1316
|
+
this.activeStatusIndicator.setMessage(message ?? this.defaultWorkingMessage);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
resetDialogUI() {
|
|
1320
|
+
if (this.dialogSelector) {
|
|
1321
|
+
this.hideDialogSelector();
|
|
1322
|
+
}
|
|
1323
|
+
if (this.dialogInput) {
|
|
1324
|
+
this.hideDialogInput();
|
|
1325
|
+
}
|
|
1326
|
+
if (this.dialogEditor) {
|
|
1327
|
+
this.hideDialogEditor();
|
|
1328
|
+
}
|
|
1329
|
+
this.ui.hideOverlay();
|
|
1330
|
+
this.footer.invalidate();
|
|
1331
|
+
this.setupAutocompleteProvider();
|
|
1332
|
+
this.updateTerminalTitle();
|
|
1333
|
+
this.workingMessage = undefined;
|
|
1334
|
+
this.workingVisible = true;
|
|
1335
|
+
this.setWorkingIndicator();
|
|
1336
|
+
if (this.activeStatusIndicator?.kind === "working") {
|
|
1337
|
+
this.activeStatusIndicator.setMessage(`${this.defaultWorkingMessage} (${keyText("app.interrupt")} to interrupt)`);
|
|
1338
|
+
}
|
|
1339
|
+
this.setHiddenThinkingLabel();
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Apply a theme request from the extension UI host (implemented by
|
|
1343
|
+
* ./interactive-dialog-ui.ts), keeping the theme controller and settings
|
|
1344
|
+
* persistence on the mode.
|
|
1345
|
+
*/
|
|
1346
|
+
applyExtensionTheme(themeOrName) {
|
|
1347
|
+
if (themeOrName instanceof Theme) {
|
|
1348
|
+
return this.themeController.setThemeInstance(themeOrName);
|
|
1349
|
+
}
|
|
1350
|
+
const result = this.themeController.setThemeName(themeOrName);
|
|
1351
|
+
if (result.success) {
|
|
1352
|
+
if (this.settingsManager.getTheme() !== themeOrName) {
|
|
1353
|
+
this.settingsManager.setTheme(themeOrName);
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
return result;
|
|
1357
|
+
}
|
|
1358
|
+
/**
|
|
1359
|
+
* Interaction host for the versioned capability surface (1A.8): maps the
|
|
1360
|
+
* frozen InteractionHostV1 contract onto the legacy TUI dialogs. Cancellation
|
|
1361
|
+
* rides the existing dialog signal support; notify reuses the notify channel.
|
|
1362
|
+
*/
|
|
1363
|
+
createInteractionHostV1() {
|
|
1364
|
+
return createInteractionHostV1(this);
|
|
1365
|
+
}
|
|
1366
|
+
/**
|
|
1367
|
+
* Show a selector for extensions.
|
|
1368
|
+
*/
|
|
1369
|
+
showDialogSelector(title, options, opts) {
|
|
1370
|
+
return showDialogSelector(this, title, options, opts);
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Hide the extension selector.
|
|
1374
|
+
*/
|
|
1375
|
+
hideDialogSelector() {
|
|
1376
|
+
hideDialogSelector(this);
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Show a confirmation dialog for extensions.
|
|
1380
|
+
*/
|
|
1381
|
+
async showDialogConfirm(title, message, opts) {
|
|
1382
|
+
return showDialogConfirm(this, title, message, opts);
|
|
1383
|
+
}
|
|
1384
|
+
async promptForMissingSessionCwd(error) {
|
|
1385
|
+
const confirmed = await this.showDialogConfirm("Session cwd not found", formatMissingSessionCwdPrompt(error.issue));
|
|
1386
|
+
return confirmed ? error.issue.fallbackCwd : undefined;
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* Show a text input for extensions.
|
|
1390
|
+
*/
|
|
1391
|
+
showDialogInput(title, placeholder, opts) {
|
|
1392
|
+
return showDialogInput(this, title, placeholder, opts);
|
|
1393
|
+
}
|
|
1394
|
+
/**
|
|
1395
|
+
* Hide the extension input.
|
|
1396
|
+
*/
|
|
1397
|
+
hideDialogInput() {
|
|
1398
|
+
hideDialogInput(this);
|
|
1399
|
+
}
|
|
1400
|
+
/**
|
|
1401
|
+
* Show a multi-line editor for extensions (with Ctrl+G support).
|
|
1402
|
+
*/
|
|
1403
|
+
showDialogEditor(title, prefill) {
|
|
1404
|
+
return showDialogEditor(this, title, prefill);
|
|
1405
|
+
}
|
|
1406
|
+
/**
|
|
1407
|
+
* External editor command for extension editors. Host entry for
|
|
1408
|
+
* ./interactive-dialog-ui.ts, keeping settings access on the mode.
|
|
1409
|
+
*/
|
|
1410
|
+
getExternalEditorCommand() {
|
|
1411
|
+
return this.settingsManager.getExternalEditorCommand();
|
|
1412
|
+
}
|
|
1413
|
+
/**
|
|
1414
|
+
* Hide the extension editor.
|
|
1415
|
+
*/
|
|
1416
|
+
hideDialogEditor() {
|
|
1417
|
+
hideDialogEditor(this);
|
|
1418
|
+
}
|
|
1419
|
+
/**
|
|
1420
|
+
* Show a notification for extensions.
|
|
1421
|
+
*/
|
|
1422
|
+
showDialogNotify(message, type) {
|
|
1423
|
+
showDialogNotify(this, message, type);
|
|
1424
|
+
}
|
|
1425
|
+
// =========================================================================
|
|
1426
|
+
// Key Handlers
|
|
1427
|
+
// =========================================================================
|
|
1428
|
+
setupKeyHandlers() {
|
|
1429
|
+
// Set up handlers on defaultEditor - they use this.editor for text access
|
|
1430
|
+
// so they work correctly regardless of which editor is active
|
|
1431
|
+
this.defaultEditor.onEscape = () => {
|
|
1432
|
+
if (this.facade.isStreaming()) {
|
|
1433
|
+
this.restoreQueuedMessagesToEditor({ abort: true });
|
|
1434
|
+
}
|
|
1435
|
+
else if (this.facade.isBashRunning()) {
|
|
1436
|
+
this.facade.abortBash();
|
|
1437
|
+
}
|
|
1438
|
+
else if (this.isBashMode) {
|
|
1439
|
+
this.editor.setText("");
|
|
1440
|
+
this.isBashMode = false;
|
|
1441
|
+
this.updateEditorBorderColor();
|
|
1442
|
+
}
|
|
1443
|
+
else if (!this.editor.getText().trim()) {
|
|
1444
|
+
// Double-escape with empty editor triggers /tree, /fork, or nothing based on setting
|
|
1445
|
+
const action = this.settingsManager.getDoubleEscapeAction();
|
|
1446
|
+
if (action !== "none") {
|
|
1447
|
+
const now = Date.now();
|
|
1448
|
+
if (now - this.lastEscapeTime < 500) {
|
|
1449
|
+
if (action === "tree") {
|
|
1450
|
+
this.showTreeSelector();
|
|
1451
|
+
}
|
|
1452
|
+
else {
|
|
1453
|
+
this.showUserMessageSelector();
|
|
1454
|
+
}
|
|
1455
|
+
this.lastEscapeTime = 0;
|
|
1456
|
+
}
|
|
1457
|
+
else {
|
|
1458
|
+
this.lastEscapeTime = now;
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
};
|
|
1463
|
+
// Register app action handlers
|
|
1464
|
+
this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
|
|
1465
|
+
this.defaultEditor.onCtrlD = () => this.handleCtrlD();
|
|
1466
|
+
this.defaultEditor.onAction("app.suspend", () => this.handleCtrlZ());
|
|
1467
|
+
this.defaultEditor.onAction("app.thinking.cycle", () => this.cycleThinkingLevel());
|
|
1468
|
+
this.defaultEditor.onAction("app.model.cycleForward", () => this.cycleModel("forward"));
|
|
1469
|
+
this.defaultEditor.onAction("app.model.cycleBackward", () => this.cycleModel("backward"));
|
|
1470
|
+
// Global debug handler on TUI (works regardless of focus)
|
|
1471
|
+
this.ui.onDebug = () => this.handleDebugCommand();
|
|
1472
|
+
this.defaultEditor.onAction("app.model.select", () => this.showModelSelector());
|
|
1473
|
+
this.defaultEditor.onAction("app.tools.expand", () => this.toggleToolOutputExpansion());
|
|
1474
|
+
this.defaultEditor.onAction("app.thinking.toggle", () => this.toggleThinkingBlockVisibility());
|
|
1475
|
+
this.defaultEditor.onAction("app.editor.external", () => void this.handleOpenExternalEditor());
|
|
1476
|
+
this.defaultEditor.onAction("app.message.copy", () => void this.handleCopyCommand({ flashConfirmation: true, preferSelection: true }));
|
|
1477
|
+
this.defaultEditor.onAction("app.message.followUp", () => this.handleFollowUp());
|
|
1478
|
+
this.defaultEditor.onAction("app.message.dequeue", () => this.handleDequeue());
|
|
1479
|
+
this.defaultEditor.onAction("app.session.new", () => this.handleClearCommand());
|
|
1480
|
+
this.defaultEditor.onAction("app.session.tree", () => this.showTreeSelector());
|
|
1481
|
+
this.defaultEditor.onAction("app.session.fork", () => this.showUserMessageSelector());
|
|
1482
|
+
this.defaultEditor.onAction("app.session.resume", () => this.showSessionSelector());
|
|
1483
|
+
this.defaultEditor.onChange = (text) => {
|
|
1484
|
+
const wasBashMode = this.isBashMode;
|
|
1485
|
+
this.isBashMode = text.trimStart().startsWith("!");
|
|
1486
|
+
if (wasBashMode !== this.isBashMode) {
|
|
1487
|
+
this.updateEditorBorderColor();
|
|
1488
|
+
}
|
|
1489
|
+
};
|
|
1490
|
+
// Handle clipboard paste (triggered on Ctrl+V). Images are attached by path;
|
|
1491
|
+
// otherwise, paste plain text from the system clipboard.
|
|
1492
|
+
this.defaultEditor.onPasteImage = () => {
|
|
1493
|
+
void this.handleClipboardPaste();
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
async handleRightClickPaste() {
|
|
1497
|
+
const target = this.renderer.getFocusedComponent();
|
|
1498
|
+
const handleInput = target?.handleInput;
|
|
1499
|
+
if (!target || !handleInput)
|
|
1500
|
+
return;
|
|
1501
|
+
try {
|
|
1502
|
+
const text = await readClipboardText();
|
|
1503
|
+
if (!text || this.renderer.getFocusedComponent() !== target)
|
|
1504
|
+
return;
|
|
1505
|
+
handleInput.call(target, `\x1b[200~${text}\x1b[201~`);
|
|
1506
|
+
this.ui.requestRender();
|
|
1507
|
+
}
|
|
1508
|
+
catch {
|
|
1509
|
+
// Silently ignore clipboard errors (may not have permission, etc.)
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
async handleClipboardPaste() {
|
|
1513
|
+
try {
|
|
1514
|
+
const image = await readClipboardImage();
|
|
1515
|
+
if (image) {
|
|
1516
|
+
const tmpDir = os.tmpdir();
|
|
1517
|
+
const ext = extensionForImageMimeType(image.mimeType) ?? "png";
|
|
1518
|
+
const fileName = `agent-forge-clipboard-${crypto.randomUUID()}.${ext}`;
|
|
1519
|
+
const filePath = path.join(tmpDir, fileName);
|
|
1520
|
+
fs.writeFileSync(filePath, Buffer.from(image.bytes));
|
|
1521
|
+
this.editor.insertTextAtCursor?.(filePath);
|
|
1522
|
+
this.ui.requestRender();
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
const text = await readClipboardText();
|
|
1526
|
+
if (text) {
|
|
1527
|
+
this.editor.insertTextAtCursor?.(text);
|
|
1528
|
+
this.ui.requestRender();
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
catch {
|
|
1532
|
+
// Silently ignore clipboard errors (may not have permission, etc.)
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
// Startup-time submissions of plain prompts are queued; the main loop
|
|
1536
|
+
// dequeues and submits them automatically once startup completes.
|
|
1537
|
+
// Commands ("/", "!") and empty input keep the pre-existing behavior:
|
|
1538
|
+
// commands need the post-startup dispatch context, so they are restored
|
|
1539
|
+
// into the editor for the user to submit again after startup.
|
|
1540
|
+
handleStartupSubmit(text) {
|
|
1541
|
+
if (!text.trim())
|
|
1542
|
+
return;
|
|
1543
|
+
if (text.startsWith("/") || text.startsWith("!")) {
|
|
1544
|
+
this.editor.setText(text);
|
|
1545
|
+
this.showStatus("Startup in progress — command restored to the editor, submit again once startup completes");
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
this.pendingUserInputs.push(text);
|
|
1549
|
+
this.showStatus("Startup in progress — input queued and will run when startup completes");
|
|
1550
|
+
}
|
|
1551
|
+
setupEditorSubmitHandler() {
|
|
1552
|
+
this.defaultEditor.onSubmit = async (text) => {
|
|
1553
|
+
text = text.trim();
|
|
1554
|
+
if (!text)
|
|
1555
|
+
return;
|
|
1556
|
+
// Handle /commands through the dispatch table (interactive-commands.ts)
|
|
1557
|
+
const dispatched = this.dispatchEditorCommand(text);
|
|
1558
|
+
if (dispatched) {
|
|
1559
|
+
if (dispatched.pending)
|
|
1560
|
+
await dispatched.pending;
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
// Handle bash command (! for normal, !! for excluded from context)
|
|
1564
|
+
if (text.startsWith("!")) {
|
|
1565
|
+
const isExcluded = text.startsWith("!!");
|
|
1566
|
+
const command = isExcluded ? text.slice(2).trim() : text.slice(1).trim();
|
|
1567
|
+
if (command) {
|
|
1568
|
+
if (this.facade.isBashRunning()) {
|
|
1569
|
+
this.showWarning("A bash command is already running. Press Esc to cancel it first.");
|
|
1570
|
+
this.editor.setText(text);
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
this.editor.addToHistory?.(text);
|
|
1574
|
+
await this.handleBashCommand(command, isExcluded);
|
|
1575
|
+
this.isBashMode = false;
|
|
1576
|
+
this.updateEditorBorderColor();
|
|
1577
|
+
return;
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
// Queue input during compaction
|
|
1581
|
+
if (this.facade.isCompacting()) {
|
|
1582
|
+
this.queueCompactionMessage(text, "steer");
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
// If streaming, use prompt() with steer behavior
|
|
1586
|
+
// This handles extension commands (execute immediately), prompt template expansion, and queueing
|
|
1587
|
+
if (this.facade.isStreaming()) {
|
|
1588
|
+
this.editor.addToHistory?.(text);
|
|
1589
|
+
this.editor.setText("");
|
|
1590
|
+
await this.facade.prompt(text, { streamingBehavior: "steer" });
|
|
1591
|
+
this.updatePendingMessagesDisplay();
|
|
1592
|
+
this.ui.requestRender();
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
// Normal message submission
|
|
1596
|
+
// First, move any pending bash components to chat
|
|
1597
|
+
this.flushPendingBashComponents();
|
|
1598
|
+
if (this.onInputCallback) {
|
|
1599
|
+
this.onInputCallback(text);
|
|
1600
|
+
}
|
|
1601
|
+
else {
|
|
1602
|
+
this.pendingUserInputs.push(text);
|
|
1603
|
+
}
|
|
1604
|
+
this.editor.addToHistory?.(text);
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1607
|
+
/**
|
|
1608
|
+
* Run a /command through COMMAND_DISPATCH_ENTRIES. Returns undefined when
|
|
1609
|
+
* text is not a registered command; otherwise the command has been fully
|
|
1610
|
+
* dispatched (handler invoked, editor cleared in the original order) and
|
|
1611
|
+
* pending carries the async completion, if any.
|
|
1612
|
+
*/
|
|
1613
|
+
dispatchEditorCommand(text) {
|
|
1614
|
+
const match = matchCommandDispatchEntry(COMMAND_DISPATCH_ENTRIES, text);
|
|
1615
|
+
if (!match)
|
|
1616
|
+
return undefined;
|
|
1617
|
+
const { entry, arg } = match;
|
|
1618
|
+
if (entry.clearEditorBeforeRun)
|
|
1619
|
+
this.setEditorText("");
|
|
1620
|
+
const result = entry.run(this, text, arg);
|
|
1621
|
+
if (!(result instanceof Promise)) {
|
|
1622
|
+
if (!entry.clearEditorBeforeRun)
|
|
1623
|
+
this.setEditorText("");
|
|
1624
|
+
return { pending: undefined };
|
|
1625
|
+
}
|
|
1626
|
+
return {
|
|
1627
|
+
pending: entry.clearEditorBeforeRun ? result : result.then(() => this.setEditorText("")),
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1630
|
+
setEditorText(text) {
|
|
1631
|
+
this.editor.setText(text);
|
|
1632
|
+
}
|
|
1633
|
+
subscribeToAgent() {
|
|
1634
|
+
this.unsubscribe = this.facade.subscribe(async (event) => {
|
|
1635
|
+
await this.handleEvent(event);
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
async handleEvent(event) {
|
|
1639
|
+
if (!this.isInitialized) {
|
|
1640
|
+
await this.init();
|
|
1641
|
+
}
|
|
1642
|
+
this.footer.invalidate();
|
|
1643
|
+
switch (event.type) {
|
|
1644
|
+
case "agent_start":
|
|
1645
|
+
this.pendingTools.clear();
|
|
1646
|
+
// Restore main escape handler if retry handler is still active
|
|
1647
|
+
// (retry success event fires later, but we need main handler now)
|
|
1648
|
+
if (this.retryEscapeHandler) {
|
|
1649
|
+
this.defaultEditor.onEscape = this.retryEscapeHandler;
|
|
1650
|
+
this.retryEscapeHandler = undefined;
|
|
1651
|
+
}
|
|
1652
|
+
break;
|
|
1653
|
+
case "turn_start":
|
|
1654
|
+
if (this.settingsManager.getShowTerminalProgress()) {
|
|
1655
|
+
this.ui.terminal.setProgress(true);
|
|
1656
|
+
}
|
|
1657
|
+
if (this.workingVisible) {
|
|
1658
|
+
if (this.activeStatusIndicator?.kind !== "working") {
|
|
1659
|
+
this.showWorkingStatusIndicator();
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
else {
|
|
1663
|
+
this.clearStatusIndicator();
|
|
1664
|
+
}
|
|
1665
|
+
this.ui.requestRender();
|
|
1666
|
+
break;
|
|
1667
|
+
case "queue_update":
|
|
1668
|
+
this.updatePendingMessagesDisplay();
|
|
1669
|
+
this.ui.requestRender();
|
|
1670
|
+
break;
|
|
1671
|
+
case "entry_appended":
|
|
1672
|
+
// Custom entries have no renderer source since the legacy extension
|
|
1673
|
+
// surface was removed; nothing to append to the chat.
|
|
1674
|
+
break;
|
|
1675
|
+
case "session_info_changed":
|
|
1676
|
+
this.updateTerminalTitle();
|
|
1677
|
+
this.footer.invalidate();
|
|
1678
|
+
this.ui.requestRender();
|
|
1679
|
+
break;
|
|
1680
|
+
case "thinking_level_changed":
|
|
1681
|
+
this.footer.invalidate();
|
|
1682
|
+
this.updateEditorBorderColor();
|
|
1683
|
+
break;
|
|
1684
|
+
case "message_start":
|
|
1685
|
+
if (event.message.role === "custom") {
|
|
1686
|
+
this.addMessageToChat(event.message);
|
|
1687
|
+
this.ui.requestRender();
|
|
1688
|
+
}
|
|
1689
|
+
else if (event.message.role === "user") {
|
|
1690
|
+
this.addMessageToChat(event.message);
|
|
1691
|
+
this.updatePendingMessagesDisplay();
|
|
1692
|
+
this.ui.requestRender();
|
|
1693
|
+
}
|
|
1694
|
+
else if (event.message.role === "assistant") {
|
|
1695
|
+
this.streamingComponent = new AssistantMessageComponent(undefined, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.getMarkdownTransformers());
|
|
1696
|
+
this.streamingMessage = event.message;
|
|
1697
|
+
this.chatContainer.addChild(this.streamingComponent);
|
|
1698
|
+
this.streamingComponent.updateContent(this.streamingMessage, true);
|
|
1699
|
+
this.ui.requestRender();
|
|
1700
|
+
}
|
|
1701
|
+
break;
|
|
1702
|
+
case "message_update":
|
|
1703
|
+
if (this.streamingComponent && event.message.role === "assistant") {
|
|
1704
|
+
this.streamingMessage = event.message;
|
|
1705
|
+
this.streamingComponent.updateContent(this.streamingMessage, true);
|
|
1706
|
+
for (const content of this.streamingMessage.content) {
|
|
1707
|
+
if (content.type === "toolCall") {
|
|
1708
|
+
if (!this.pendingTools.has(content.id)) {
|
|
1709
|
+
const component = new ToolExecutionComponent(content.name, content.id, content.arguments, {
|
|
1710
|
+
showImages: this.settingsManager.getShowImages(),
|
|
1711
|
+
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
1712
|
+
}, this.getRegisteredToolDefinition(content.name), this.ui, this.facade.getCwd());
|
|
1713
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
1714
|
+
this.chatContainer.addChild(component);
|
|
1715
|
+
this.pendingTools.set(content.id, component);
|
|
1716
|
+
}
|
|
1717
|
+
else {
|
|
1718
|
+
const component = this.pendingTools.get(content.id);
|
|
1719
|
+
// v8 ignore next -- has() 已确认键存在,get() 恒返回组件
|
|
1720
|
+
if (component) {
|
|
1721
|
+
component.updateArgs(content.arguments);
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
this.ui.requestRender();
|
|
1727
|
+
}
|
|
1728
|
+
break;
|
|
1729
|
+
case "message_end":
|
|
1730
|
+
if (event.message.role === "user")
|
|
1731
|
+
break;
|
|
1732
|
+
if (this.streamingComponent && event.message.role === "assistant") {
|
|
1733
|
+
this.streamingMessage = event.message;
|
|
1734
|
+
let errorMessage;
|
|
1735
|
+
if (this.streamingMessage.stopReason === "aborted") {
|
|
1736
|
+
const retryAttempt = this.facade.getRetryAttempt();
|
|
1737
|
+
errorMessage =
|
|
1738
|
+
retryAttempt > 0
|
|
1739
|
+
? `Aborted after ${retryAttempt} retry attempt${retryAttempt > 1 ? "s" : ""}`
|
|
1740
|
+
: "Operation aborted";
|
|
1741
|
+
this.streamingMessage.errorMessage = errorMessage;
|
|
1742
|
+
}
|
|
1743
|
+
this.streamingComponent.updateContent(this.streamingMessage, false);
|
|
1744
|
+
if (this.streamingMessage.stopReason === "aborted" || this.streamingMessage.stopReason === "error") {
|
|
1745
|
+
if (!errorMessage) {
|
|
1746
|
+
errorMessage = this.streamingMessage.errorMessage || "Error";
|
|
1747
|
+
}
|
|
1748
|
+
for (const [, component] of this.pendingTools.entries()) {
|
|
1749
|
+
component.updateResult({
|
|
1750
|
+
content: [{ type: "text", text: errorMessage }],
|
|
1751
|
+
isError: true,
|
|
1752
|
+
});
|
|
1753
|
+
}
|
|
1754
|
+
this.pendingTools.clear();
|
|
1755
|
+
}
|
|
1756
|
+
else {
|
|
1757
|
+
// Args are now complete - trigger diff computation for edit tools
|
|
1758
|
+
for (const [, component] of this.pendingTools.entries()) {
|
|
1759
|
+
component.setArgsComplete();
|
|
1760
|
+
}
|
|
1761
|
+
this.maybeShowAssistantDiagnostics(this.streamingMessage);
|
|
1762
|
+
this.maybeShowCacheMissNotice(this.streamingMessage);
|
|
1763
|
+
}
|
|
1764
|
+
this.streamingComponent = undefined;
|
|
1765
|
+
this.streamingMessage = undefined;
|
|
1766
|
+
this.footer.invalidate();
|
|
1767
|
+
}
|
|
1768
|
+
this.ui.requestRender();
|
|
1769
|
+
break;
|
|
1770
|
+
case "bash_execution_update":
|
|
1771
|
+
// The bash execution callback handles TUI output rendering.
|
|
1772
|
+
break;
|
|
1773
|
+
case "tool_execution_start": {
|
|
1774
|
+
let component = this.pendingTools.get(event.toolCallId);
|
|
1775
|
+
if (!component) {
|
|
1776
|
+
component = new ToolExecutionComponent(event.toolName, event.toolCallId, event.args, {
|
|
1777
|
+
showImages: this.settingsManager.getShowImages(),
|
|
1778
|
+
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
1779
|
+
}, this.getRegisteredToolDefinition(event.toolName), this.ui, this.facade.getCwd());
|
|
1780
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
1781
|
+
this.chatContainer.addChild(component);
|
|
1782
|
+
this.pendingTools.set(event.toolCallId, component);
|
|
1783
|
+
}
|
|
1784
|
+
component.markExecutionStarted();
|
|
1785
|
+
this.ui.requestRender();
|
|
1786
|
+
break;
|
|
1787
|
+
}
|
|
1788
|
+
case "tool_execution_update": {
|
|
1789
|
+
const component = this.pendingTools.get(event.toolCallId);
|
|
1790
|
+
if (component) {
|
|
1791
|
+
component.updateResult({ ...event.partialResult, isError: false }, true);
|
|
1792
|
+
this.ui.requestRender();
|
|
1793
|
+
}
|
|
1794
|
+
break;
|
|
1795
|
+
}
|
|
1796
|
+
case "tool_execution_end": {
|
|
1797
|
+
const component = this.pendingTools.get(event.toolCallId);
|
|
1798
|
+
if (component) {
|
|
1799
|
+
component.updateResult({ ...event.result, isError: event.isError });
|
|
1800
|
+
this.pendingTools.delete(event.toolCallId);
|
|
1801
|
+
this.ui.requestRender();
|
|
1802
|
+
}
|
|
1803
|
+
break;
|
|
1804
|
+
}
|
|
1805
|
+
case "capability_command_result": {
|
|
1806
|
+
const links = event.result.links?.map((link) => `${link.label}: ${link.url}`) ?? [];
|
|
1807
|
+
const message = [event.result.message, ...links].filter(Boolean).join("\n");
|
|
1808
|
+
if (event.result.status === "error")
|
|
1809
|
+
this.showError(message);
|
|
1810
|
+
else
|
|
1811
|
+
this.showStatus(message);
|
|
1812
|
+
this.ui.requestRender();
|
|
1813
|
+
break;
|
|
1814
|
+
}
|
|
1815
|
+
case "agent_end":
|
|
1816
|
+
if (this.settingsManager.getShowTerminalProgress()) {
|
|
1817
|
+
this.ui.terminal.setProgress(false);
|
|
1818
|
+
}
|
|
1819
|
+
this.clearStatusIndicator("working");
|
|
1820
|
+
if (this.streamingComponent) {
|
|
1821
|
+
this.chatContainer.removeChild(this.streamingComponent);
|
|
1822
|
+
this.streamingComponent = undefined;
|
|
1823
|
+
this.streamingMessage = undefined;
|
|
1824
|
+
}
|
|
1825
|
+
this.pendingTools.clear();
|
|
1826
|
+
this.ui.requestRender();
|
|
1827
|
+
break;
|
|
1828
|
+
case "agent_settled":
|
|
1829
|
+
await this.checkShutdownRequested();
|
|
1830
|
+
break;
|
|
1831
|
+
case "compaction_start": {
|
|
1832
|
+
if (this.settingsManager.getShowTerminalProgress()) {
|
|
1833
|
+
this.ui.terminal.setProgress(true);
|
|
1834
|
+
}
|
|
1835
|
+
// Keep editor active; submissions are queued during compaction.
|
|
1836
|
+
this.autoCompactionEscapeHandler = this.defaultEditor.onEscape;
|
|
1837
|
+
this.defaultEditor.onEscape = () => {
|
|
1838
|
+
this.facade.abortCompaction();
|
|
1839
|
+
};
|
|
1840
|
+
this.showStatusIndicator(new CompactionStatusIndicator(this.ui, event.reason));
|
|
1841
|
+
this.ui.requestRender();
|
|
1842
|
+
break;
|
|
1843
|
+
}
|
|
1844
|
+
case "compaction_end": {
|
|
1845
|
+
if (this.settingsManager.getShowTerminalProgress()) {
|
|
1846
|
+
this.ui.terminal.setProgress(false);
|
|
1847
|
+
}
|
|
1848
|
+
if (this.autoCompactionEscapeHandler) {
|
|
1849
|
+
this.defaultEditor.onEscape = this.autoCompactionEscapeHandler;
|
|
1850
|
+
this.autoCompactionEscapeHandler = undefined;
|
|
1851
|
+
}
|
|
1852
|
+
this.clearStatusIndicator("compaction");
|
|
1853
|
+
if (event.aborted) {
|
|
1854
|
+
if (event.reason === "manual") {
|
|
1855
|
+
this.showError("Compaction cancelled");
|
|
1856
|
+
}
|
|
1857
|
+
else {
|
|
1858
|
+
this.showStatus("Auto-compaction cancelled");
|
|
1859
|
+
}
|
|
1860
|
+
}
|
|
1861
|
+
else if (event.result) {
|
|
1862
|
+
const entries = this.facade.getContextEntries();
|
|
1863
|
+
if (entries[0]?.type !== "compaction") {
|
|
1864
|
+
throw new Error("Completed compaction is missing from the session context");
|
|
1865
|
+
}
|
|
1866
|
+
this.chatContainer.clear();
|
|
1867
|
+
// The latest compaction is prepended for model context; append it below at its chronological position.
|
|
1868
|
+
this.renderSessionEntries(entries.slice(1));
|
|
1869
|
+
this.addMessageToChat(createCompactionSummaryMessage(event.result.summary, event.result.tokensBefore, new Date().toISOString()));
|
|
1870
|
+
if (event.result.usage) {
|
|
1871
|
+
this.addCompactionCostNotice({
|
|
1872
|
+
type: "compaction_cost",
|
|
1873
|
+
kind: "compaction",
|
|
1874
|
+
usage: event.result.usage,
|
|
1875
|
+
});
|
|
1876
|
+
}
|
|
1877
|
+
this.footer.invalidate();
|
|
1878
|
+
}
|
|
1879
|
+
else if (event.errorMessage) {
|
|
1880
|
+
if (event.reason === "manual") {
|
|
1881
|
+
this.showError(event.errorMessage);
|
|
1882
|
+
}
|
|
1883
|
+
else {
|
|
1884
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
1885
|
+
this.chatContainer.addChild(new Text(theme.fg("error", event.errorMessage), 1, 0));
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
void this.flushCompactionQueue({ willRetry: event.willRetry });
|
|
1889
|
+
this.ui.requestRender();
|
|
1890
|
+
break;
|
|
1891
|
+
}
|
|
1892
|
+
case "auto_retry_start": {
|
|
1893
|
+
// Set up escape to abort retry
|
|
1894
|
+
this.retryEscapeHandler = this.defaultEditor.onEscape;
|
|
1895
|
+
this.defaultEditor.onEscape = () => {
|
|
1896
|
+
this.facade.abortRetry();
|
|
1897
|
+
};
|
|
1898
|
+
this.showStatusIndicator(new RetryStatusIndicator(this.ui, event.attempt, event.maxAttempts, event.delayMs));
|
|
1899
|
+
this.ui.requestRender();
|
|
1900
|
+
break;
|
|
1901
|
+
}
|
|
1902
|
+
case "auto_retry_end": {
|
|
1903
|
+
// Restore escape handler
|
|
1904
|
+
if (this.retryEscapeHandler) {
|
|
1905
|
+
this.defaultEditor.onEscape = this.retryEscapeHandler;
|
|
1906
|
+
this.retryEscapeHandler = undefined;
|
|
1907
|
+
}
|
|
1908
|
+
this.clearStatusIndicator("retry");
|
|
1909
|
+
// Show error only on final failure (success shows normal response)
|
|
1910
|
+
if (!event.success) {
|
|
1911
|
+
this.showError(`Retry failed after ${event.attempt} attempts: ${event.finalError || "Unknown error"}`);
|
|
1912
|
+
}
|
|
1913
|
+
this.ui.requestRender();
|
|
1914
|
+
break;
|
|
1915
|
+
}
|
|
1916
|
+
case "summarization_retry_scheduled": {
|
|
1917
|
+
this.showError(event.errorMessage);
|
|
1918
|
+
this.showStatusIndicator(new RetryStatusIndicator(this.ui, event.attempt, event.maxAttempts, event.delayMs));
|
|
1919
|
+
this.ui.requestRender();
|
|
1920
|
+
break;
|
|
1921
|
+
}
|
|
1922
|
+
case "summarization_retry_attempt_start": {
|
|
1923
|
+
this.clearStatusIndicator("retry");
|
|
1924
|
+
if (event.source === "branchSummary") {
|
|
1925
|
+
this.showStatusIndicator(new BranchSummaryStatusIndicator(this.ui));
|
|
1926
|
+
}
|
|
1927
|
+
else {
|
|
1928
|
+
this.showStatusIndicator(new CompactionStatusIndicator(this.ui, event.reason));
|
|
1929
|
+
}
|
|
1930
|
+
this.ui.requestRender();
|
|
1931
|
+
break;
|
|
1932
|
+
}
|
|
1933
|
+
case "summarization_retry_finished": {
|
|
1934
|
+
this.clearStatusIndicator("retry");
|
|
1935
|
+
this.ui.requestRender();
|
|
1936
|
+
break;
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
/** Extract text content from a user message */
|
|
1941
|
+
getUserMessageText(message) {
|
|
1942
|
+
if (message.role !== "user")
|
|
1943
|
+
return "";
|
|
1944
|
+
const textBlocks = typeof message.content === "string"
|
|
1945
|
+
? [{ type: "text", text: message.content }]
|
|
1946
|
+
: message.content.filter((c) => c.type === "text");
|
|
1947
|
+
return textBlocks.map((c) => c.text).join("");
|
|
1948
|
+
}
|
|
1949
|
+
/** Show a managed-tool status update in the chat. */
|
|
1950
|
+
showManagedToolStatus(status) {
|
|
1951
|
+
if (!this.managedToolStatusStarted) {
|
|
1952
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
1953
|
+
this.managedToolStatusStarted = true;
|
|
1954
|
+
}
|
|
1955
|
+
const message = status.type === "warning" ? `Warning: ${status.message}` : status.message;
|
|
1956
|
+
const color = status.type === "warning" ? "warning" : "dim";
|
|
1957
|
+
this.chatContainer.addChild(new Text(theme.fg(color, message), 1, 0));
|
|
1958
|
+
this.lastStatusSpacer = undefined;
|
|
1959
|
+
this.lastStatusText = undefined;
|
|
1960
|
+
this.ui.requestRender();
|
|
1961
|
+
}
|
|
1962
|
+
/**
|
|
1963
|
+
* Show a status message in the chat.
|
|
1964
|
+
*
|
|
1965
|
+
* If multiple status messages are emitted back-to-back (without anything else being added to the chat),
|
|
1966
|
+
* we update the previous status line instead of appending new ones to avoid log spam.
|
|
1967
|
+
*/
|
|
1968
|
+
showStatus(message) {
|
|
1969
|
+
const children = this.chatContainer.children;
|
|
1970
|
+
const last = children.length > 0 ? children[children.length - 1] : undefined;
|
|
1971
|
+
const secondLast = children.length > 1 ? children[children.length - 2] : undefined;
|
|
1972
|
+
if (last && secondLast && last === this.lastStatusText && secondLast === this.lastStatusSpacer) {
|
|
1973
|
+
this.lastStatusText.setText(theme.fg("dim", message));
|
|
1974
|
+
this.ui.requestRender();
|
|
1975
|
+
return;
|
|
1976
|
+
}
|
|
1977
|
+
const spacer = new Spacer(1);
|
|
1978
|
+
const text = new Text(theme.fg("dim", message), 1, 0);
|
|
1979
|
+
this.chatContainer.addChild(spacer);
|
|
1980
|
+
this.chatContainer.addChild(text);
|
|
1981
|
+
this.lastStatusSpacer = spacer;
|
|
1982
|
+
this.lastStatusText = text;
|
|
1983
|
+
this.ui.requestRender();
|
|
1984
|
+
}
|
|
1985
|
+
addMessageToChat(message, options) {
|
|
1986
|
+
switch (message.role) {
|
|
1987
|
+
case "bashExecution": {
|
|
1988
|
+
const component = new BashExecutionComponent(message.command, this.ui, message.excludeFromContext);
|
|
1989
|
+
if (message.output) {
|
|
1990
|
+
component.appendOutput(message.output);
|
|
1991
|
+
}
|
|
1992
|
+
component.setComplete(message.exitCode, message.cancelled, message.truncated ? { truncated: true } : undefined, message.fullOutputPath);
|
|
1993
|
+
this.chatContainer.addChild(component);
|
|
1994
|
+
break;
|
|
1995
|
+
}
|
|
1996
|
+
case "custom": {
|
|
1997
|
+
if (message.display) {
|
|
1998
|
+
const renderer = undefined;
|
|
1999
|
+
const component = new CustomMessageComponent(message, renderer, this.getMarkdownThemeWithSettings(), this.outputPad);
|
|
2000
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
2001
|
+
this.chatContainer.addChild(component);
|
|
2002
|
+
}
|
|
2003
|
+
break;
|
|
2004
|
+
}
|
|
2005
|
+
case "compactionSummary": {
|
|
2006
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2007
|
+
const component = new CompactionSummaryMessageComponent(message, this.getMarkdownThemeWithSettings());
|
|
2008
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
2009
|
+
this.chatContainer.addChild(component);
|
|
2010
|
+
break;
|
|
2011
|
+
}
|
|
2012
|
+
case "branchSummary": {
|
|
2013
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2014
|
+
const component = new BranchSummaryMessageComponent(message, this.getMarkdownThemeWithSettings());
|
|
2015
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
2016
|
+
this.chatContainer.addChild(component);
|
|
2017
|
+
break;
|
|
2018
|
+
}
|
|
2019
|
+
case "user": {
|
|
2020
|
+
const textContent = this.getUserMessageText(message);
|
|
2021
|
+
if (textContent) {
|
|
2022
|
+
if (this.chatContainer.children.length > 0) {
|
|
2023
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2024
|
+
}
|
|
2025
|
+
const skillBlock = parseSkillBlock(textContent);
|
|
2026
|
+
if (skillBlock) {
|
|
2027
|
+
// Render skill block (collapsible)
|
|
2028
|
+
const component = new SkillInvocationMessageComponent(skillBlock, this.getMarkdownThemeWithSettings());
|
|
2029
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
2030
|
+
this.chatContainer.addChild(component);
|
|
2031
|
+
// Render user message separately if present
|
|
2032
|
+
if (skillBlock.userMessage) {
|
|
2033
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2034
|
+
const userComponent = new UserMessageComponent(skillBlock.userMessage, this.getMarkdownThemeWithSettings(), this.outputPad, this.getMarkdownTransformers());
|
|
2035
|
+
this.chatContainer.addChild(userComponent);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
else {
|
|
2039
|
+
const userComponent = new UserMessageComponent(textContent, this.getMarkdownThemeWithSettings(), this.outputPad, this.getMarkdownTransformers());
|
|
2040
|
+
this.chatContainer.addChild(userComponent);
|
|
2041
|
+
}
|
|
2042
|
+
if (options?.populateHistory) {
|
|
2043
|
+
this.editor.addToHistory?.(textContent);
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
break;
|
|
2047
|
+
}
|
|
2048
|
+
case "assistant": {
|
|
2049
|
+
const assistantComponent = new AssistantMessageComponent(message, this.hideThinkingBlock, this.getMarkdownThemeWithSettings(), this.hiddenThinkingLabel, this.outputPad, this.getMarkdownTransformers());
|
|
2050
|
+
this.chatContainer.addChild(assistantComponent);
|
|
2051
|
+
break;
|
|
2052
|
+
}
|
|
2053
|
+
case "toolResult": {
|
|
2054
|
+
// Tool results are rendered inline with tool calls, handled separately
|
|
2055
|
+
break;
|
|
2056
|
+
}
|
|
2057
|
+
default: {
|
|
2058
|
+
// v8 ignore next -- exhaustive over AgentMessage roles
|
|
2059
|
+
const _exhaustive = message;
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
renderSessionItems(items, options = {}) {
|
|
2064
|
+
this.pendingTools.clear();
|
|
2065
|
+
const renderedPendingTools = new Map();
|
|
2066
|
+
// Cache-miss notices are not persisted; re-derive them from the full entry
|
|
2067
|
+
// list and re-inject them after the assistant messages that paid for them.
|
|
2068
|
+
const cacheMisses = this.settingsManager.getShowCacheMissNotices()
|
|
2069
|
+
? collectCacheMisses(this.facade.getEntries(), this.facade.getModelPriceSource())
|
|
2070
|
+
: new Map();
|
|
2071
|
+
if (options.updateFooter) {
|
|
2072
|
+
this.footer.invalidate();
|
|
2073
|
+
this.updateEditorBorderColor();
|
|
2074
|
+
}
|
|
2075
|
+
for (const item of items) {
|
|
2076
|
+
if (isCustomSessionEntry(item)) {
|
|
2077
|
+
// No entry renderer source since the legacy extension surface was
|
|
2078
|
+
// removed; previously renderer-less entries were skipped as well.
|
|
2079
|
+
continue;
|
|
2080
|
+
}
|
|
2081
|
+
if (isCompactionCostNotice(item)) {
|
|
2082
|
+
this.addCompactionCostNotice(item);
|
|
2083
|
+
continue;
|
|
2084
|
+
}
|
|
2085
|
+
const message = item;
|
|
2086
|
+
// Assistant messages need special handling for tool calls
|
|
2087
|
+
if (message.role === "assistant") {
|
|
2088
|
+
this.addMessageToChat(message);
|
|
2089
|
+
// Render tool call components
|
|
2090
|
+
for (const content of message.content) {
|
|
2091
|
+
if (content.type === "toolCall") {
|
|
2092
|
+
const component = new ToolExecutionComponent(content.name, content.id, content.arguments, {
|
|
2093
|
+
showImages: this.settingsManager.getShowImages(),
|
|
2094
|
+
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
2095
|
+
}, this.getRegisteredToolDefinition(content.name), this.ui, this.facade.getCwd());
|
|
2096
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
2097
|
+
this.chatContainer.addChild(component);
|
|
2098
|
+
if (message.stopReason === "aborted" || message.stopReason === "error") {
|
|
2099
|
+
let errorMessage;
|
|
2100
|
+
if (message.stopReason === "aborted") {
|
|
2101
|
+
const retryAttempt = this.facade.getRetryAttempt();
|
|
2102
|
+
errorMessage =
|
|
2103
|
+
retryAttempt > 0
|
|
2104
|
+
? `Aborted after ${retryAttempt} retry attempt${retryAttempt > 1 ? "s" : ""}`
|
|
2105
|
+
: "Operation aborted";
|
|
2106
|
+
}
|
|
2107
|
+
else {
|
|
2108
|
+
errorMessage = message.errorMessage || "Error";
|
|
2109
|
+
}
|
|
2110
|
+
component.updateResult({ content: [{ type: "text", text: errorMessage }], isError: true });
|
|
2111
|
+
}
|
|
2112
|
+
else {
|
|
2113
|
+
renderedPendingTools.set(content.id, component);
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
if (message.stopReason !== "aborted" && message.stopReason !== "error") {
|
|
2118
|
+
this.maybeShowAssistantDiagnostics(message);
|
|
2119
|
+
const miss = cacheMisses.get(message);
|
|
2120
|
+
if (miss)
|
|
2121
|
+
this.addCacheMissNotice(miss);
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
else if (message.role === "toolResult") {
|
|
2125
|
+
// Match tool results to pending tool components
|
|
2126
|
+
const component = renderedPendingTools.get(message.toolCallId);
|
|
2127
|
+
if (component) {
|
|
2128
|
+
component.updateResult(message);
|
|
2129
|
+
renderedPendingTools.delete(message.toolCallId);
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
else {
|
|
2133
|
+
// All other messages use standard rendering
|
|
2134
|
+
this.addMessageToChat(message, options);
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
for (const [toolCallId, component] of renderedPendingTools) {
|
|
2138
|
+
this.pendingTools.set(toolCallId, component);
|
|
2139
|
+
}
|
|
2140
|
+
this.ui.requestRender();
|
|
2141
|
+
}
|
|
2142
|
+
/**
|
|
2143
|
+
* Render session entries to chat. Used for initial load and rebuild after compaction.
|
|
2144
|
+
* @param entries Compaction-aware session entries to render
|
|
2145
|
+
* @param options.updateFooter Update footer state
|
|
2146
|
+
* @param options.populateHistory Add user messages to editor history
|
|
2147
|
+
*/
|
|
2148
|
+
renderSessionEntries(entries, options = {}) {
|
|
2149
|
+
const items = entries.flatMap((entry) => {
|
|
2150
|
+
if (entry.type === "custom") {
|
|
2151
|
+
return [entry];
|
|
2152
|
+
}
|
|
2153
|
+
const messages = sessionEntryToContextMessages(entry);
|
|
2154
|
+
if ((entry.type === "compaction" || entry.type === "branch_summary") && entry.usage && messages.length > 0) {
|
|
2155
|
+
return [...messages, { type: "compaction_cost", kind: entry.type, usage: entry.usage }];
|
|
2156
|
+
}
|
|
2157
|
+
return messages;
|
|
2158
|
+
});
|
|
2159
|
+
this.renderSessionItems(items, options);
|
|
2160
|
+
}
|
|
2161
|
+
/**
|
|
2162
|
+
* Render billing usage for a compaction or branch summary. The notice is derived
|
|
2163
|
+
* from persisted summary usage and is not stored as a separate session entry.
|
|
2164
|
+
*/
|
|
2165
|
+
addCompactionCostNotice(notice) {
|
|
2166
|
+
if (!this.settingsManager.getShowCacheMissNotices())
|
|
2167
|
+
return;
|
|
2168
|
+
const { usage } = notice;
|
|
2169
|
+
const tokens = usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
|
|
2170
|
+
const cost = usage.cost.total >= 0.01 ? ` (~$${usage.cost.total.toFixed(2)})` : "";
|
|
2171
|
+
const label = notice.kind === "compaction" ? "Compaction" : "Branch summary";
|
|
2172
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2173
|
+
this.chatContainer.addChild(new Text(theme.fg("warning", `${label}: ${formatTokens(tokens)} tokens billed${cost}`), 1, 0));
|
|
2174
|
+
}
|
|
2175
|
+
maybeShowAssistantDiagnostics(message) {
|
|
2176
|
+
if (!this.settingsManager.getShowCacheMissNotices())
|
|
2177
|
+
return;
|
|
2178
|
+
for (const diagnostic of message.diagnostics ?? []) {
|
|
2179
|
+
if (diagnostic.type !== "anthropic_input_transformations")
|
|
2180
|
+
continue;
|
|
2181
|
+
const transformations = diagnostic.details?.transformations;
|
|
2182
|
+
if (!Array.isArray(transformations))
|
|
2183
|
+
continue;
|
|
2184
|
+
const dropped = transformations.flatMap((transformation) => {
|
|
2185
|
+
if (typeof transformation !== "object" || transformation === null)
|
|
2186
|
+
return [];
|
|
2187
|
+
const details = transformation;
|
|
2188
|
+
if (details.type !== "thinking_dropped")
|
|
2189
|
+
return [];
|
|
2190
|
+
const reason = typeof details.reason === "string" ? details.reason : "unknown reason";
|
|
2191
|
+
const location = typeof details.path === "string" ? ` at ${details.path}` : "";
|
|
2192
|
+
return [`${reason}${location}`];
|
|
2193
|
+
});
|
|
2194
|
+
if (dropped.length === 0)
|
|
2195
|
+
continue;
|
|
2196
|
+
const noun = dropped.length === 1 ? "thinking block" : `${dropped.length} thinking blocks`;
|
|
2197
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2198
|
+
this.chatContainer.addChild(new Text(theme.fg("warning", `Anthropic dropped ${noun}: ${dropped.join("; ")}`), 1, 0));
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
/**
|
|
2202
|
+
* Show a transcript notice when a completed assistant message paid for a
|
|
2203
|
+
* significant cache miss. Only states observable facts: the miss itself,
|
|
2204
|
+
* a model switch, or an idle gap past the cache TTL.
|
|
2205
|
+
*/
|
|
2206
|
+
maybeShowCacheMissNotice(message) {
|
|
2207
|
+
if (!this.settingsManager.getShowCacheMissNotices())
|
|
2208
|
+
return;
|
|
2209
|
+
// Entries don't contain `message` yet: message_end fires before persistence.
|
|
2210
|
+
const miss = detectCacheMiss(this.facade.getEntries(), message, this.facade.getModelPriceSource());
|
|
2211
|
+
if (miss)
|
|
2212
|
+
this.addCacheMissNotice(miss);
|
|
2213
|
+
}
|
|
2214
|
+
addCacheMissNotice(miss) {
|
|
2215
|
+
if (miss.missedTokens < 20_000 && miss.missedCost < 0.1)
|
|
2216
|
+
return;
|
|
2217
|
+
const cost = miss.missedCost >= 0.01 ? ` (~$${miss.missedCost.toFixed(2)})` : "";
|
|
2218
|
+
const reBilled = `${formatTokens(miss.missedTokens)} tokens re-billed${cost}`;
|
|
2219
|
+
let label = "Cache miss";
|
|
2220
|
+
if (miss.modelChanged) {
|
|
2221
|
+
label = "Cache miss after model switch";
|
|
2222
|
+
}
|
|
2223
|
+
else if (miss.idleMs >= CACHE_TTL_MS) {
|
|
2224
|
+
label = `Cache miss after ${Math.round(miss.idleMs / 60_000)}m idle`;
|
|
2225
|
+
}
|
|
2226
|
+
const text = theme.fg("warning", `${label}: ${reBilled}`);
|
|
2227
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2228
|
+
this.chatContainer.addChild(new Text(text, 1, 0));
|
|
2229
|
+
}
|
|
2230
|
+
renderInitialMessages() {
|
|
2231
|
+
const entries = this.facade.getContextEntries();
|
|
2232
|
+
this.renderSessionEntries(entries, {
|
|
2233
|
+
updateFooter: true,
|
|
2234
|
+
populateHistory: true,
|
|
2235
|
+
});
|
|
2236
|
+
// Show compaction info if session was compacted
|
|
2237
|
+
const allEntries = this.facade.getEntries();
|
|
2238
|
+
const compactionCount = allEntries.filter((e) => e.type === "compaction").length;
|
|
2239
|
+
if (compactionCount > 0) {
|
|
2240
|
+
const times = compactionCount === 1 ? "1 time" : `${compactionCount} times`;
|
|
2241
|
+
this.showStatus(`Session compacted ${times}`);
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
async getUserInput() {
|
|
2245
|
+
const queuedInput = this.pendingUserInputs.shift();
|
|
2246
|
+
if (queuedInput !== undefined) {
|
|
2247
|
+
return queuedInput;
|
|
2248
|
+
}
|
|
2249
|
+
return new Promise((resolve) => {
|
|
2250
|
+
this.onInputCallback = (text) => {
|
|
2251
|
+
this.onInputCallback = undefined;
|
|
2252
|
+
resolve(text);
|
|
2253
|
+
};
|
|
2254
|
+
});
|
|
2255
|
+
}
|
|
2256
|
+
rebuildChatFromMessages() {
|
|
2257
|
+
this.chatContainer.clear();
|
|
2258
|
+
this.renderSessionEntries(this.facade.getContextEntries());
|
|
2259
|
+
}
|
|
2260
|
+
// =========================================================================
|
|
2261
|
+
// Key handlers
|
|
2262
|
+
// =========================================================================
|
|
2263
|
+
handleCtrlC() {
|
|
2264
|
+
const now = Date.now();
|
|
2265
|
+
if (now - this.lastSigintTime < 500) {
|
|
2266
|
+
void this.shutdown();
|
|
2267
|
+
}
|
|
2268
|
+
else {
|
|
2269
|
+
this.clearEditor();
|
|
2270
|
+
this.lastSigintTime = now;
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
handleCtrlD() {
|
|
2274
|
+
// Only called when editor is empty (enforced by CustomEditor)
|
|
2275
|
+
void this.shutdown();
|
|
2276
|
+
}
|
|
2277
|
+
/**
|
|
2278
|
+
* Gracefully shutdown the agent.
|
|
2279
|
+
* Stops the TUI before emitting shutdown events so extension UI cleanup cannot
|
|
2280
|
+
* repaint the final frame while the process is exiting.
|
|
2281
|
+
*/
|
|
2282
|
+
isShuttingDown = false;
|
|
2283
|
+
async shutdown(options) {
|
|
2284
|
+
if (this.isShuttingDown)
|
|
2285
|
+
return;
|
|
2286
|
+
this.isShuttingDown = true;
|
|
2287
|
+
// Keep signal handlers registered until terminal cleanup has completed.
|
|
2288
|
+
// `signal-exit` checks the listener list during the same SIGTERM/SIGHUP
|
|
2289
|
+
// dispatch and re-sends the signal if only its own listeners remain.
|
|
2290
|
+
if (options?.fromSignal) {
|
|
2291
|
+
// Signal-triggered shutdown (SIGTERM/SIGHUP). Emit extension cleanup
|
|
2292
|
+
// (session_shutdown) BEFORE touching the terminal. Extension teardown
|
|
2293
|
+
// such as removing sockets does not write to the tty, so it must not be
|
|
2294
|
+
// skipped if a later terminal-restore write fails on a dead or stalled
|
|
2295
|
+
// terminal. If the terminal is gone, the restore writes below emit EIO,
|
|
2296
|
+
// which the stdout/stderr error handler turns into emergencyTerminalExit;
|
|
2297
|
+
// the render loop is already idle, so this cannot hot-spin (see #4144).
|
|
2298
|
+
await this.runtimeFacade.dispose();
|
|
2299
|
+
this.themeController.disableAutoSync();
|
|
2300
|
+
await this.ui.terminal.drainInput(1000);
|
|
2301
|
+
this.stop();
|
|
2302
|
+
process.exit(0);
|
|
2303
|
+
}
|
|
2304
|
+
// Interactive quit (Ctrl+D, Ctrl+C, /quit, extension shutdown()). Stop the
|
|
2305
|
+
// TUI before emitting shutdown events so extension UI cleanup cannot repaint
|
|
2306
|
+
// the final frame while the process is exiting.
|
|
2307
|
+
// Drain any in-flight Kitty key release events before stopping.
|
|
2308
|
+
// This prevents escape sequences from leaking to the parent shell over slow SSH.
|
|
2309
|
+
this.themeController.disableAutoSync();
|
|
2310
|
+
await this.ui.terminal.drainInput(1000);
|
|
2311
|
+
this.stop();
|
|
2312
|
+
await this.runtimeFacade.dispose();
|
|
2313
|
+
const resumeCommand = formatResumeCommand(this.runtimeFacade);
|
|
2314
|
+
if (resumeCommand) {
|
|
2315
|
+
process.stdout.write(`${chalk.dim("To resume this session:")} ${resumeCommand}\n`);
|
|
2316
|
+
}
|
|
2317
|
+
process.exit(0);
|
|
2318
|
+
}
|
|
2319
|
+
emergencyTerminalExit() {
|
|
2320
|
+
this.isShuttingDown = true;
|
|
2321
|
+
this.unregisterSignalHandlers();
|
|
2322
|
+
killTrackedDetachedChildren();
|
|
2323
|
+
// The terminal is gone. Do not run normal shutdown because TUI and
|
|
2324
|
+
// extension cleanup can write restore sequences and re-trigger EIO.
|
|
2325
|
+
process.exit(129);
|
|
2326
|
+
}
|
|
2327
|
+
/**
|
|
2328
|
+
* Last-resort handler for uncaught exceptions. The TUI puts stdin into raw
|
|
2329
|
+
* mode and hides the cursor; without this handler, an uncaught throw from
|
|
2330
|
+
* anywhere (e.g. an extension's async `ChildProcess.on("exit")` callback)
|
|
2331
|
+
* tears down the process while leaving the terminal in raw mode with no
|
|
2332
|
+
* cursor, requiring `stty sane && reset` to recover.
|
|
2333
|
+
*
|
|
2334
|
+
* Unlike emergencyTerminalExit, the terminal is still alive here, so we
|
|
2335
|
+
* call ui.stop() to restore cooked mode, the cursor, and disable bracketed
|
|
2336
|
+
* paste / Kitty / modifyOtherKeys sequences.
|
|
2337
|
+
*/
|
|
2338
|
+
uncaughtCrash(error) {
|
|
2339
|
+
if (this.isShuttingDown) {
|
|
2340
|
+
process.exit(1);
|
|
2341
|
+
}
|
|
2342
|
+
this.isShuttingDown = true;
|
|
2343
|
+
try {
|
|
2344
|
+
this.unregisterSignalHandlers();
|
|
2345
|
+
}
|
|
2346
|
+
catch { }
|
|
2347
|
+
try {
|
|
2348
|
+
killTrackedDetachedChildren();
|
|
2349
|
+
}
|
|
2350
|
+
catch { }
|
|
2351
|
+
try {
|
|
2352
|
+
this.ui.stop();
|
|
2353
|
+
}
|
|
2354
|
+
catch { }
|
|
2355
|
+
console.error(`${APP_NAME} exiting due to uncaughtException:`);
|
|
2356
|
+
console.error(error);
|
|
2357
|
+
process.exit(1);
|
|
2358
|
+
}
|
|
2359
|
+
/**
|
|
2360
|
+
* Check if shutdown was requested and perform shutdown if so.
|
|
2361
|
+
*/
|
|
2362
|
+
async checkShutdownRequested() {
|
|
2363
|
+
if (!this.shutdownRequested)
|
|
2364
|
+
return;
|
|
2365
|
+
await this.shutdown();
|
|
2366
|
+
}
|
|
2367
|
+
registerSignalHandlers() {
|
|
2368
|
+
this.unregisterSignalHandlers();
|
|
2369
|
+
const signals = ["SIGTERM"];
|
|
2370
|
+
if (process.platform !== "win32") {
|
|
2371
|
+
signals.push("SIGHUP");
|
|
2372
|
+
}
|
|
2373
|
+
for (const signal of signals) {
|
|
2374
|
+
const handler = () => {
|
|
2375
|
+
// SIGHUP no longer hard-exits: graceful shutdown emits session_shutdown
|
|
2376
|
+
// first, then attempts terminal restore. A genuinely dead terminal
|
|
2377
|
+
// surfaces as an EIO on the restore writes, which the stdout/stderr
|
|
2378
|
+
// error handler converts into emergencyTerminalExit (see #4144, #5080).
|
|
2379
|
+
killTrackedDetachedChildren();
|
|
2380
|
+
void this.shutdown({ fromSignal: true });
|
|
2381
|
+
};
|
|
2382
|
+
process.prependListener(signal, handler);
|
|
2383
|
+
this.signalCleanupHandlers.push(() => process.off(signal, handler));
|
|
2384
|
+
}
|
|
2385
|
+
const terminalErrorHandler = (error) => {
|
|
2386
|
+
if (isDeadTerminalError(error)) {
|
|
2387
|
+
this.emergencyTerminalExit();
|
|
2388
|
+
}
|
|
2389
|
+
throw error;
|
|
2390
|
+
};
|
|
2391
|
+
process.stdout.on("error", terminalErrorHandler);
|
|
2392
|
+
process.stderr.on("error", terminalErrorHandler);
|
|
2393
|
+
this.signalCleanupHandlers.push(() => process.stdout.off("error", terminalErrorHandler));
|
|
2394
|
+
this.signalCleanupHandlers.push(() => process.stderr.off("error", terminalErrorHandler));
|
|
2395
|
+
// Restore the terminal before the process dies on any uncaught throw.
|
|
2396
|
+
// Without this, an unhandled exception from extension code (or anywhere
|
|
2397
|
+
// in the application) leaves the terminal in raw mode with no cursor.
|
|
2398
|
+
const uncaughtExceptionHandler = (error) => this.uncaughtCrash(error);
|
|
2399
|
+
process.prependListener("uncaughtException", uncaughtExceptionHandler);
|
|
2400
|
+
this.signalCleanupHandlers.push(() => process.off("uncaughtException", uncaughtExceptionHandler));
|
|
2401
|
+
}
|
|
2402
|
+
unregisterSignalHandlers() {
|
|
2403
|
+
for (const cleanup of this.signalCleanupHandlers) {
|
|
2404
|
+
cleanup();
|
|
2405
|
+
}
|
|
2406
|
+
this.signalCleanupHandlers = [];
|
|
2407
|
+
}
|
|
2408
|
+
handleCtrlZ() {
|
|
2409
|
+
if (process.platform === "win32") {
|
|
2410
|
+
this.showStatus("Suspend to background is not supported on Windows");
|
|
2411
|
+
return;
|
|
2412
|
+
}
|
|
2413
|
+
// Keep the event loop alive while suspended. Without this, stopping the TUI
|
|
2414
|
+
// can leave Node with no ref'ed handles, causing the process to exit on fg
|
|
2415
|
+
// before the SIGCONT handler gets a chance to restore the terminal.
|
|
2416
|
+
const suspendKeepAlive = setInterval(() => { }, 2 ** 30);
|
|
2417
|
+
// Ignore SIGINT while suspended so Ctrl+C in the terminal does not
|
|
2418
|
+
// kill the backgrounded process. The handler is removed on resume.
|
|
2419
|
+
const ignoreSigint = () => { };
|
|
2420
|
+
process.on("SIGINT", ignoreSigint);
|
|
2421
|
+
// Set up handler to restore TUI when resumed
|
|
2422
|
+
process.once("SIGCONT", () => {
|
|
2423
|
+
clearInterval(suspendKeepAlive);
|
|
2424
|
+
process.removeListener("SIGINT", ignoreSigint);
|
|
2425
|
+
this.ui.start();
|
|
2426
|
+
this.ui.requestRender(true);
|
|
2427
|
+
});
|
|
2428
|
+
try {
|
|
2429
|
+
// Stop the TUI (restore terminal to normal mode)
|
|
2430
|
+
this.ui.stop();
|
|
2431
|
+
// Send SIGTSTP to process group (pid=0 means all processes in group)
|
|
2432
|
+
process.kill(0, "SIGTSTP");
|
|
2433
|
+
}
|
|
2434
|
+
catch (error) {
|
|
2435
|
+
clearInterval(suspendKeepAlive);
|
|
2436
|
+
process.removeListener("SIGINT", ignoreSigint);
|
|
2437
|
+
throw error;
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
async handleFollowUp() {
|
|
2441
|
+
const text = (this.editor.getExpandedText?.() ?? this.editor.getText()).trim();
|
|
2442
|
+
if (!text)
|
|
2443
|
+
return;
|
|
2444
|
+
// Queue input during compaction (extension commands execute immediately)
|
|
2445
|
+
if (this.facade.isCompacting()) {
|
|
2446
|
+
this.queueCompactionMessage(text, "followUp");
|
|
2447
|
+
return;
|
|
2448
|
+
}
|
|
2449
|
+
// Alt+Enter queues a follow-up message (waits until agent finishes)
|
|
2450
|
+
// This handles extension commands (execute immediately), prompt template expansion, and queueing
|
|
2451
|
+
if (this.facade.isStreaming()) {
|
|
2452
|
+
this.editor.addToHistory?.(text);
|
|
2453
|
+
this.editor.setText("");
|
|
2454
|
+
await this.facade.prompt(text, { streamingBehavior: "followUp" });
|
|
2455
|
+
this.updatePendingMessagesDisplay();
|
|
2456
|
+
this.ui.requestRender();
|
|
2457
|
+
}
|
|
2458
|
+
// If not streaming, Alt+Enter acts like regular Enter (trigger onSubmit)
|
|
2459
|
+
else if (this.editor.onSubmit) {
|
|
2460
|
+
this.editor.setText("");
|
|
2461
|
+
this.editor.onSubmit(text);
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2464
|
+
handleDequeue() {
|
|
2465
|
+
const restored = this.restoreQueuedMessagesToEditor();
|
|
2466
|
+
if (restored === 0) {
|
|
2467
|
+
this.showStatus("No queued messages to restore");
|
|
2468
|
+
}
|
|
2469
|
+
else {
|
|
2470
|
+
this.showStatus(`Restored ${restored} queued message${restored > 1 ? "s" : ""} to editor`);
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
/** Host-visible for ./interactive-auth.ts (S5 slice 3). */
|
|
2474
|
+
updateEditorBorderColor() {
|
|
2475
|
+
if (this.isBashMode) {
|
|
2476
|
+
this.editor.borderColor = theme.getBashModeBorderColor();
|
|
2477
|
+
}
|
|
2478
|
+
else {
|
|
2479
|
+
const level = this.facade.getState().thinkingLevel || "off";
|
|
2480
|
+
this.editor.borderColor = theme.getThinkingBorderColor(level);
|
|
2481
|
+
}
|
|
2482
|
+
this.ui.requestRender();
|
|
2483
|
+
}
|
|
2484
|
+
cycleThinkingLevel() {
|
|
2485
|
+
const newLevel = this.facade.cycleThinkingLevel();
|
|
2486
|
+
if (newLevel === undefined) {
|
|
2487
|
+
this.showStatus("Current model does not support thinking");
|
|
2488
|
+
}
|
|
2489
|
+
else {
|
|
2490
|
+
this.footer.invalidate();
|
|
2491
|
+
this.updateEditorBorderColor();
|
|
2492
|
+
this.showStatus(`Thinking level: ${newLevel}`);
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
async cycleModel(direction) {
|
|
2496
|
+
try {
|
|
2497
|
+
const result = await this.facade.cycleModel(direction);
|
|
2498
|
+
if (result === undefined) {
|
|
2499
|
+
const msg = this.facade.getScopedModels().length > 0 ? "Only one model in scope" : "Only one model available";
|
|
2500
|
+
this.showStatus(msg);
|
|
2501
|
+
}
|
|
2502
|
+
else {
|
|
2503
|
+
this.footer.invalidate();
|
|
2504
|
+
this.updateEditorBorderColor();
|
|
2505
|
+
const thinkingStr = result.model.reasoning && result.thinkingLevel !== "off" ? ` (thinking: ${result.thinkingLevel})` : "";
|
|
2506
|
+
this.showStatus(`Switched to ${result.model.name || result.model.id}${thinkingStr}`);
|
|
2507
|
+
void this.maybeWarnAboutAnthropicSubscriptionAuth(result.model);
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
catch (error) {
|
|
2511
|
+
this.showError(error instanceof Error ? error.message : String(error));
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
toggleToolOutputExpansion() {
|
|
2515
|
+
this.setToolsExpanded(!this.toolOutputExpanded);
|
|
2516
|
+
}
|
|
2517
|
+
setToolsExpanded(expanded) {
|
|
2518
|
+
if (expanded === this.toolOutputExpanded)
|
|
2519
|
+
return;
|
|
2520
|
+
this.toolOutputExpanded = expanded;
|
|
2521
|
+
const activeHeader = this.builtInHeader;
|
|
2522
|
+
if (isExpandable(activeHeader)) {
|
|
2523
|
+
activeHeader.setExpanded(expanded);
|
|
2524
|
+
}
|
|
2525
|
+
for (const container of [this.loadedResourcesContainer, this.chatContainer]) {
|
|
2526
|
+
for (const child of container.children) {
|
|
2527
|
+
if (isExpandable(child)) {
|
|
2528
|
+
child.setExpanded(expanded);
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
}
|
|
2532
|
+
this.showStatus(`Tool output: ${expanded ? "expanded" : "collapsed"}`);
|
|
2533
|
+
}
|
|
2534
|
+
/** Update rendered assistant messages without rebuilding live tool components. */
|
|
2535
|
+
updateThinkingBlockVisibility() {
|
|
2536
|
+
for (const child of this.chatContainer.children) {
|
|
2537
|
+
if (child instanceof AssistantMessageComponent) {
|
|
2538
|
+
child.setHideThinkingBlock(this.hideThinkingBlock);
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
this.ui.requestRender();
|
|
2542
|
+
}
|
|
2543
|
+
toggleThinkingBlockVisibility() {
|
|
2544
|
+
this.hideThinkingBlock = !this.hideThinkingBlock;
|
|
2545
|
+
this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock);
|
|
2546
|
+
this.updateThinkingBlockVisibility();
|
|
2547
|
+
this.showStatus(`Thinking blocks: ${this.hideThinkingBlock ? "hidden" : "visible"}`);
|
|
2548
|
+
}
|
|
2549
|
+
async handleOpenExternalEditor() {
|
|
2550
|
+
const editorCmd = this.settingsManager.getExternalEditorCommand();
|
|
2551
|
+
const content = this.editor.getExpandedText?.() ?? this.editor.getText();
|
|
2552
|
+
this.ui.stop();
|
|
2553
|
+
try {
|
|
2554
|
+
const result = await editInExternalEditor({
|
|
2555
|
+
command: editorCmd,
|
|
2556
|
+
content,
|
|
2557
|
+
});
|
|
2558
|
+
if (result.status === "complete") {
|
|
2559
|
+
this.editor.setText(result.content);
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
finally {
|
|
2563
|
+
this.ui.start();
|
|
2564
|
+
this.ui.requestRender(true);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
// =========================================================================
|
|
2568
|
+
// UI helpers
|
|
2569
|
+
// =========================================================================
|
|
2570
|
+
clearEditor() {
|
|
2571
|
+
this.editor.setText("");
|
|
2572
|
+
this.ui.requestRender();
|
|
2573
|
+
}
|
|
2574
|
+
showError(errorMessage) {
|
|
2575
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2576
|
+
this.chatContainer.addChild(new Text(theme.fg("error", `Error: ${errorMessage}`), this.outputPad, 0));
|
|
2577
|
+
this.ui.requestRender();
|
|
2578
|
+
}
|
|
2579
|
+
showWarning(warningMessage) {
|
|
2580
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2581
|
+
this.chatContainer.addChild(new Text(theme.fg("warning", `Warning: ${warningMessage}`), 1, 0));
|
|
2582
|
+
this.ui.requestRender();
|
|
2583
|
+
}
|
|
2584
|
+
showNewVersionNotification(release) {
|
|
2585
|
+
const action = theme.fg("accent", `${APP_NAME} update`);
|
|
2586
|
+
const updateInstruction = theme.fg("muted", `New version ${release.version} is available. Run `) + action;
|
|
2587
|
+
const changelogUrl = process.env.AGENT_FORGE_CHANGELOG_URL;
|
|
2588
|
+
const changelogLine = changelogUrl
|
|
2589
|
+
? theme.fg("muted", "Changelog: ") +
|
|
2590
|
+
(getCapabilities().hyperlinks
|
|
2591
|
+
? hyperlink(theme.fg("accent", changelogUrl), changelogUrl)
|
|
2592
|
+
: theme.fg("accent", changelogUrl))
|
|
2593
|
+
: theme.fg("muted", "Changelog URL is not configured.");
|
|
2594
|
+
const note = release.note?.trim();
|
|
2595
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2596
|
+
this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
|
|
2597
|
+
this.chatContainer.addChild(new Text(`${theme.bold(theme.fg("warning", "Update Available"))}\n${updateInstruction}`, 1, 0));
|
|
2598
|
+
if (note) {
|
|
2599
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2600
|
+
this.chatContainer.addChild(new Markdown(note, 1, 0, this.getMarkdownThemeWithSettings(), {
|
|
2601
|
+
color: (text) => theme.fg("muted", text),
|
|
2602
|
+
}));
|
|
2603
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2604
|
+
}
|
|
2605
|
+
this.chatContainer.addChild(new Text(changelogLine, 1, 0));
|
|
2606
|
+
this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
|
|
2607
|
+
this.ui.requestRender();
|
|
2608
|
+
}
|
|
2609
|
+
showPackageUpdateNotification(packages) {
|
|
2610
|
+
const action = theme.fg("accent", `${APP_NAME} update --extensions`);
|
|
2611
|
+
const updateInstruction = theme.fg("muted", "Package updates are available. Run ") + action;
|
|
2612
|
+
const packageLines = packages.map((pkg) => `- ${pkg}`).join("\n");
|
|
2613
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
2614
|
+
this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
|
|
2615
|
+
this.chatContainer.addChild(new Text(`${theme.bold(theme.fg("warning", "Package Updates Available"))}\n${updateInstruction}\n${theme.fg("muted", "Packages:")}\n${packageLines}`, 1, 0));
|
|
2616
|
+
this.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text)));
|
|
2617
|
+
this.ui.requestRender();
|
|
2618
|
+
}
|
|
2619
|
+
/**
|
|
2620
|
+
* Get all queued messages (read-only).
|
|
2621
|
+
* Combines session queue and compaction queue.
|
|
2622
|
+
*/
|
|
2623
|
+
getAllQueuedMessages() {
|
|
2624
|
+
return {
|
|
2625
|
+
steering: [
|
|
2626
|
+
...this.facade.getSteeringMessages(),
|
|
2627
|
+
...this.compactionQueuedMessages.filter((msg) => msg.mode === "steer").map((msg) => msg.text),
|
|
2628
|
+
],
|
|
2629
|
+
followUp: [
|
|
2630
|
+
...this.facade.getFollowUpMessages(),
|
|
2631
|
+
...this.compactionQueuedMessages.filter((msg) => msg.mode === "followUp").map((msg) => msg.text),
|
|
2632
|
+
],
|
|
2633
|
+
};
|
|
2634
|
+
}
|
|
2635
|
+
/**
|
|
2636
|
+
* Clear all queued messages and return their contents.
|
|
2637
|
+
* Clears both session queue and compaction queue.
|
|
2638
|
+
*/
|
|
2639
|
+
clearAllQueues() {
|
|
2640
|
+
const { steering, followUp } = this.facade.clearQueue();
|
|
2641
|
+
const compactionSteering = this.compactionQueuedMessages
|
|
2642
|
+
.filter((msg) => msg.mode === "steer")
|
|
2643
|
+
.map((msg) => msg.text);
|
|
2644
|
+
const compactionFollowUp = this.compactionQueuedMessages
|
|
2645
|
+
.filter((msg) => msg.mode === "followUp")
|
|
2646
|
+
.map((msg) => msg.text);
|
|
2647
|
+
this.compactionQueuedMessages = [];
|
|
2648
|
+
return {
|
|
2649
|
+
steering: [...steering, ...compactionSteering],
|
|
2650
|
+
followUp: [...followUp, ...compactionFollowUp],
|
|
2651
|
+
};
|
|
2652
|
+
}
|
|
2653
|
+
updatePendingMessagesDisplay() {
|
|
2654
|
+
this.pendingMessagesContainer.clear();
|
|
2655
|
+
const { steering: steeringMessages, followUp: followUpMessages } = this.getAllQueuedMessages();
|
|
2656
|
+
if (steeringMessages.length > 0 || followUpMessages.length > 0) {
|
|
2657
|
+
this.pendingMessagesContainer.addChild(new Spacer(1));
|
|
2658
|
+
for (const message of steeringMessages) {
|
|
2659
|
+
const text = theme.fg("dim", `Steering: ${message}`);
|
|
2660
|
+
this.pendingMessagesContainer.addChild(new TruncatedText(text, 1, 0));
|
|
2661
|
+
}
|
|
2662
|
+
for (const message of followUpMessages) {
|
|
2663
|
+
const text = theme.fg("dim", `Follow-up: ${message}`);
|
|
2664
|
+
this.pendingMessagesContainer.addChild(new TruncatedText(text, 1, 0));
|
|
2665
|
+
}
|
|
2666
|
+
const dequeueHint = this.getAppKeyDisplay("app.message.dequeue");
|
|
2667
|
+
const hintText = theme.fg("dim", `↳ ${dequeueHint} to edit all queued messages`);
|
|
2668
|
+
this.pendingMessagesContainer.addChild(new TruncatedText(hintText, 1, 0));
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
restoreQueuedMessagesToEditor(options) {
|
|
2672
|
+
const { steering, followUp } = this.clearAllQueues();
|
|
2673
|
+
const allQueued = [...steering, ...followUp];
|
|
2674
|
+
if (allQueued.length === 0) {
|
|
2675
|
+
this.updatePendingMessagesDisplay();
|
|
2676
|
+
if (options?.abort) {
|
|
2677
|
+
this.agent.abort();
|
|
2678
|
+
}
|
|
2679
|
+
return 0;
|
|
2680
|
+
}
|
|
2681
|
+
const queuedText = allQueued.join("\n\n");
|
|
2682
|
+
const currentText = options?.currentText ?? this.editor.getText();
|
|
2683
|
+
const combinedText = [queuedText, currentText].filter((t) => t.trim()).join("\n\n");
|
|
2684
|
+
this.editor.setText(combinedText);
|
|
2685
|
+
this.updatePendingMessagesDisplay();
|
|
2686
|
+
if (options?.abort) {
|
|
2687
|
+
this.agent.abort();
|
|
2688
|
+
}
|
|
2689
|
+
return allQueued.length;
|
|
2690
|
+
}
|
|
2691
|
+
queueCompactionMessage(text, mode) {
|
|
2692
|
+
this.compactionQueuedMessages.push({ text, mode });
|
|
2693
|
+
this.editor.addToHistory?.(text);
|
|
2694
|
+
this.editor.setText("");
|
|
2695
|
+
this.updatePendingMessagesDisplay();
|
|
2696
|
+
this.showStatus("Queued message for after compaction");
|
|
2697
|
+
}
|
|
2698
|
+
async flushCompactionQueue(options) {
|
|
2699
|
+
if (this.compactionQueuedMessages.length === 0) {
|
|
2700
|
+
return;
|
|
2701
|
+
}
|
|
2702
|
+
const queuedMessages = [...this.compactionQueuedMessages];
|
|
2703
|
+
this.compactionQueuedMessages = [];
|
|
2704
|
+
this.updatePendingMessagesDisplay();
|
|
2705
|
+
const restoreQueue = (error) => {
|
|
2706
|
+
this.facade.clearQueue();
|
|
2707
|
+
this.compactionQueuedMessages = queuedMessages;
|
|
2708
|
+
this.updatePendingMessagesDisplay();
|
|
2709
|
+
this.showError(`Failed to send queued message${queuedMessages.length > 1 ? "s" : ""}: ${error instanceof Error ? error.message : String(error)}`);
|
|
2710
|
+
};
|
|
2711
|
+
try {
|
|
2712
|
+
if (options?.willRetry) {
|
|
2713
|
+
// When retry is pending, queue messages for the retry turn
|
|
2714
|
+
for (const message of queuedMessages) {
|
|
2715
|
+
if (message.mode === "followUp") {
|
|
2716
|
+
await this.facade.followUp(message.text);
|
|
2717
|
+
}
|
|
2718
|
+
else {
|
|
2719
|
+
await this.facade.steer(message.text);
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
this.updatePendingMessagesDisplay();
|
|
2723
|
+
return;
|
|
2724
|
+
}
|
|
2725
|
+
const [firstPrompt, ...rest] = queuedMessages;
|
|
2726
|
+
// Start a prompt when idle, or queue it into a run still finishing compaction.
|
|
2727
|
+
const promptPromise = this.facade
|
|
2728
|
+
.prompt(firstPrompt.text, { streamingBehavior: firstPrompt.mode })
|
|
2729
|
+
.catch((error) => {
|
|
2730
|
+
restoreQueue(error);
|
|
2731
|
+
});
|
|
2732
|
+
// Queue remaining messages
|
|
2733
|
+
for (const message of rest) {
|
|
2734
|
+
if (message.mode === "followUp") {
|
|
2735
|
+
await this.facade.followUp(message.text);
|
|
2736
|
+
}
|
|
2737
|
+
else {
|
|
2738
|
+
await this.facade.steer(message.text);
|
|
2739
|
+
}
|
|
2740
|
+
}
|
|
2741
|
+
this.updatePendingMessagesDisplay();
|
|
2742
|
+
void promptPromise;
|
|
2743
|
+
}
|
|
2744
|
+
catch (error) {
|
|
2745
|
+
restoreQueue(error);
|
|
2746
|
+
}
|
|
2747
|
+
}
|
|
2748
|
+
/** Move pending bash components from pending area to chat */
|
|
2749
|
+
flushPendingBashComponents() {
|
|
2750
|
+
for (const component of this.pendingBashComponents) {
|
|
2751
|
+
this.pendingMessagesContainer.removeChild(component);
|
|
2752
|
+
this.chatContainer.addChild(component);
|
|
2753
|
+
}
|
|
2754
|
+
this.pendingBashComponents = [];
|
|
2755
|
+
}
|
|
2756
|
+
// =========================================================================
|
|
2757
|
+
// Selectors
|
|
2758
|
+
// Suite/thinking/model/scoped-models/user-message-fork/clone selectors are
|
|
2759
|
+
// implemented in ./interactive-selectors.ts (S5 slice 5) behind the
|
|
2760
|
+
// SelectorsHost interface; same-name delegates below keep the command
|
|
2761
|
+
// dispatch and prototype-level tests bound to these names. The selector
|
|
2762
|
+
// infrastructure (showSelector/disposeActiveSelector), the settings/tree/
|
|
2763
|
+
// session selectors, and resume stay on the mode: the settings and tree
|
|
2764
|
+
// selectors assemble from broad private UI state, and the session/resume
|
|
2765
|
+
// path is the documented session-boundary carve-out (SessionManager
|
|
2766
|
+
// manager-level writes plus per-entry display reads).
|
|
2767
|
+
// =========================================================================
|
|
2768
|
+
disposeActiveSelector() {
|
|
2769
|
+
const dispose = this.activeSelectorDispose;
|
|
2770
|
+
this.activeSelectorToken = undefined;
|
|
2771
|
+
this.activeSelectorDispose = undefined;
|
|
2772
|
+
dispose?.();
|
|
2773
|
+
}
|
|
2774
|
+
/**
|
|
2775
|
+
* Shows a selector component in place of the editor.
|
|
2776
|
+
* @param create Factory that receives a `done` callback and returns the component and focus target
|
|
2777
|
+
*/
|
|
2778
|
+
showSelector(create) {
|
|
2779
|
+
const token = {};
|
|
2780
|
+
let dispose;
|
|
2781
|
+
const done = () => {
|
|
2782
|
+
dispose?.();
|
|
2783
|
+
if (this.activeSelectorToken !== token)
|
|
2784
|
+
return;
|
|
2785
|
+
this.activeSelectorToken = undefined;
|
|
2786
|
+
this.activeSelectorDispose = undefined;
|
|
2787
|
+
this.editorContainer.clear();
|
|
2788
|
+
this.editorContainer.addChild(this.editor);
|
|
2789
|
+
this.ui.setFocus(this.editor);
|
|
2790
|
+
};
|
|
2791
|
+
const created = create(done);
|
|
2792
|
+
dispose = created.dispose;
|
|
2793
|
+
this.disposeActiveSelector();
|
|
2794
|
+
this.activeSelectorToken = token;
|
|
2795
|
+
this.activeSelectorDispose = dispose;
|
|
2796
|
+
this.editorContainer.clear();
|
|
2797
|
+
this.editorContainer.addChild(created.component);
|
|
2798
|
+
this.ui.setFocus(created.focus);
|
|
2799
|
+
this.ui.requestRender();
|
|
2800
|
+
}
|
|
2801
|
+
showSettingsSelector() {
|
|
2802
|
+
this.showSelector((done) => {
|
|
2803
|
+
let selector;
|
|
2804
|
+
const defaultProvider = this.settingsManager.getDefaultProvider();
|
|
2805
|
+
const defaultModelId = this.settingsManager.getDefaultModel();
|
|
2806
|
+
const defaultModel = defaultProvider && defaultModelId ? `${defaultProvider}/${defaultModelId}` : "not set";
|
|
2807
|
+
selector = new SettingsSelectorComponent({
|
|
2808
|
+
autoCompact: this.facade.isAutoCompactionEnabled(),
|
|
2809
|
+
defaultModel,
|
|
2810
|
+
currentModel: this.facade.getModel(),
|
|
2811
|
+
availableDefaultModels: this.facade.getAvailableModels(),
|
|
2812
|
+
showImages: this.settingsManager.getShowImages(),
|
|
2813
|
+
imageWidthCells: this.settingsManager.getImageWidthCells(),
|
|
2814
|
+
autoResizeImages: this.settingsManager.getImageAutoResize(),
|
|
2815
|
+
blockImages: this.settingsManager.getBlockImages(),
|
|
2816
|
+
enableSkillCommands: this.settingsManager.getEnableSkillCommands(),
|
|
2817
|
+
steeringMode: this.facade.getSteeringMode(),
|
|
2818
|
+
followUpMode: this.facade.getFollowUpMode(),
|
|
2819
|
+
transport: this.settingsManager.getTransport(),
|
|
2820
|
+
httpIdleTimeoutMs: this.settingsManager.getHttpIdleTimeoutMs(),
|
|
2821
|
+
thinkingLevel: this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL,
|
|
2822
|
+
availableThinkingLevels: [...THINKING_LEVEL_OPTIONS],
|
|
2823
|
+
modelThinkingLevels: this.settingsManager.getAllModelThinkingLevels(),
|
|
2824
|
+
currentTheme: this.themeController.getThemeSelection() || "dark",
|
|
2825
|
+
terminalTheme: this.themeController.getTerminalTheme(),
|
|
2826
|
+
availableThemes: getAvailableThemes(),
|
|
2827
|
+
hideThinkingBlock: this.hideThinkingBlock,
|
|
2828
|
+
mermaidRenderingMode: this.settingsManager.getMermaidRenderingMode(),
|
|
2829
|
+
collapseChangelog: this.settingsManager.getCollapseChangelog(),
|
|
2830
|
+
doubleEscapeAction: this.settingsManager.getDoubleEscapeAction(),
|
|
2831
|
+
treeFilterMode: this.settingsManager.getTreeFilterMode(),
|
|
2832
|
+
showHardwareCursor: this.settingsManager.getShowHardwareCursor(),
|
|
2833
|
+
showCacheMissNotices: this.settingsManager.getShowCacheMissNotices(),
|
|
2834
|
+
editorPaddingX: this.settingsManager.getEditorPaddingX(),
|
|
2835
|
+
outputPad: this.settingsManager.getOutputPad(),
|
|
2836
|
+
autocompleteMaxVisible: this.settingsManager.getAutocompleteMaxVisible(),
|
|
2837
|
+
quietStartup: this.settingsManager.getQuietStartup(),
|
|
2838
|
+
clearOnShrink: this.settingsManager.getClearOnShrink(),
|
|
2839
|
+
showTerminalProgress: this.settingsManager.getShowTerminalProgress(),
|
|
2840
|
+
tuiMode: this.ui.mode,
|
|
2841
|
+
fullscreenExitOutput: this.settingsManager.getFullscreenExitOutput(),
|
|
2842
|
+
fullscreenScrollbar: this.settingsManager.getFullscreenScrollbar(),
|
|
2843
|
+
fullscreenCopyOnSelect: this.settingsManager.getFullscreenCopyOnSelect(),
|
|
2844
|
+
warnings: this.settingsManager.getWarnings(),
|
|
2845
|
+
}, {
|
|
2846
|
+
onAutoCompactChange: (enabled) => {
|
|
2847
|
+
this.facade.setAutoCompactionEnabled(enabled);
|
|
2848
|
+
this.footer.setAutoCompactEnabled(enabled);
|
|
2849
|
+
},
|
|
2850
|
+
onShowImagesChange: (enabled) => {
|
|
2851
|
+
this.settingsManager.setShowImages(enabled);
|
|
2852
|
+
for (const child of this.chatContainer.children) {
|
|
2853
|
+
if (child instanceof ToolExecutionComponent) {
|
|
2854
|
+
child.setShowImages(enabled);
|
|
2855
|
+
}
|
|
2856
|
+
}
|
|
2857
|
+
},
|
|
2858
|
+
onImageWidthCellsChange: (width) => {
|
|
2859
|
+
this.settingsManager.setImageWidthCells(width);
|
|
2860
|
+
for (const child of this.chatContainer.children) {
|
|
2861
|
+
if (child instanceof ToolExecutionComponent) {
|
|
2862
|
+
child.setImageWidthCells(width);
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
},
|
|
2866
|
+
onAutoResizeImagesChange: (enabled) => {
|
|
2867
|
+
this.settingsManager.setImageAutoResize(enabled);
|
|
2868
|
+
},
|
|
2869
|
+
onBlockImagesChange: (blocked) => {
|
|
2870
|
+
this.settingsManager.setBlockImages(blocked);
|
|
2871
|
+
},
|
|
2872
|
+
onEnableSkillCommandsChange: (enabled) => {
|
|
2873
|
+
this.settingsManager.setEnableSkillCommands(enabled);
|
|
2874
|
+
this.setupAutocompleteProvider();
|
|
2875
|
+
},
|
|
2876
|
+
onSteeringModeChange: (mode) => {
|
|
2877
|
+
this.facade.setSteeringMode(mode);
|
|
2878
|
+
},
|
|
2879
|
+
onFollowUpModeChange: (mode) => {
|
|
2880
|
+
this.facade.setFollowUpMode(mode);
|
|
2881
|
+
},
|
|
2882
|
+
onTransportChange: (transport) => {
|
|
2883
|
+
this.settingsManager.setTransport(transport);
|
|
2884
|
+
this.session.host.setTransport(transport);
|
|
2885
|
+
},
|
|
2886
|
+
onHttpIdleTimeoutMsChange: (timeoutMs) => {
|
|
2887
|
+
this.settingsManager.setHttpIdleTimeoutMs(timeoutMs);
|
|
2888
|
+
configureHttpDispatcher(timeoutMs);
|
|
2889
|
+
this.showStatus(`HTTP idle timeout: ${formatHttpIdleTimeoutMs(timeoutMs)}`);
|
|
2890
|
+
},
|
|
2891
|
+
onModelThinkingLevelChange: (provider, modelId, level) => {
|
|
2892
|
+
this.settingsManager.setModelThinkingLevel(provider, modelId, level);
|
|
2893
|
+
// If the override is for the current model, apply it to the session too
|
|
2894
|
+
const current = this.facade.getModel();
|
|
2895
|
+
if (current && current.provider === provider && current.id === modelId) {
|
|
2896
|
+
this.facade.setThinkingLevel(level);
|
|
2897
|
+
this.footer.invalidate();
|
|
2898
|
+
this.updateEditorBorderColor();
|
|
2899
|
+
}
|
|
2900
|
+
},
|
|
2901
|
+
onModelThinkingLevelRemove: (provider, modelId) => {
|
|
2902
|
+
this.settingsManager.removeModelThinkingLevel(provider, modelId);
|
|
2903
|
+
// If the override was for the current model, revert to global default
|
|
2904
|
+
const current = this.facade.getModel();
|
|
2905
|
+
if (current && current.provider === provider && current.id === modelId) {
|
|
2906
|
+
const globalDefault = this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
|
|
2907
|
+
this.facade.setThinkingLevel(globalDefault);
|
|
2908
|
+
this.footer.invalidate();
|
|
2909
|
+
this.updateEditorBorderColor();
|
|
2910
|
+
}
|
|
2911
|
+
},
|
|
2912
|
+
onThemeChange: (themeSetting) => {
|
|
2913
|
+
this.settingsManager.setTheme(themeSetting);
|
|
2914
|
+
void this.themeController.setThemeSetting(themeSetting);
|
|
2915
|
+
},
|
|
2916
|
+
onThemePreview: (themeName) => this.themeController.preview(themeName),
|
|
2917
|
+
onHideThinkingBlockChange: (hidden) => {
|
|
2918
|
+
this.hideThinkingBlock = hidden;
|
|
2919
|
+
this.settingsManager.setHideThinkingBlock(hidden);
|
|
2920
|
+
this.updateThinkingBlockVisibility();
|
|
2921
|
+
},
|
|
2922
|
+
onMermaidRenderingModeChange: (mode) => {
|
|
2923
|
+
this.settingsManager.setMermaidRenderingMode(mode);
|
|
2924
|
+
this.chatContainer.invalidate();
|
|
2925
|
+
this.ui.requestRender();
|
|
2926
|
+
},
|
|
2927
|
+
onShowCacheMissNoticesChange: (shown) => {
|
|
2928
|
+
this.settingsManager.setShowCacheMissNotices(shown);
|
|
2929
|
+
this.rebuildChatFromMessages();
|
|
2930
|
+
},
|
|
2931
|
+
onCollapseChangelogChange: (collapsed) => {
|
|
2932
|
+
this.settingsManager.setCollapseChangelog(collapsed);
|
|
2933
|
+
},
|
|
2934
|
+
onQuietStartupChange: (enabled) => {
|
|
2935
|
+
this.settingsManager.setQuietStartup(enabled);
|
|
2936
|
+
},
|
|
2937
|
+
onDoubleEscapeActionChange: (action) => {
|
|
2938
|
+
this.settingsManager.setDoubleEscapeAction(action);
|
|
2939
|
+
},
|
|
2940
|
+
onTreeFilterModeChange: (mode) => {
|
|
2941
|
+
this.settingsManager.setTreeFilterMode(mode);
|
|
2942
|
+
},
|
|
2943
|
+
onShowHardwareCursorChange: (enabled) => {
|
|
2944
|
+
this.settingsManager.setShowHardwareCursor(enabled);
|
|
2945
|
+
this.ui.setShowHardwareCursor(enabled);
|
|
2946
|
+
},
|
|
2947
|
+
onEditorPaddingXChange: (padding) => {
|
|
2948
|
+
this.settingsManager.setEditorPaddingX(padding);
|
|
2949
|
+
this.defaultEditor.setPaddingX(padding);
|
|
2950
|
+
if (this.editor !== this.defaultEditor && this.editor.setPaddingX !== undefined) {
|
|
2951
|
+
this.editor.setPaddingX(padding);
|
|
2952
|
+
}
|
|
2953
|
+
},
|
|
2954
|
+
onOutputPadChange: (padding) => {
|
|
2955
|
+
this.settingsManager.setOutputPad(padding);
|
|
2956
|
+
this.outputPad = padding;
|
|
2957
|
+
if (this.streamingComponent || this.facade.isStreaming()) {
|
|
2958
|
+
for (const child of this.chatContainer.children) {
|
|
2959
|
+
if (child instanceof AssistantMessageComponent ||
|
|
2960
|
+
child instanceof CustomMessageComponent ||
|
|
2961
|
+
child instanceof UserMessageComponent) {
|
|
2962
|
+
child.setOutputPad(padding);
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
if (this.streamingComponent) {
|
|
2966
|
+
this.streamingComponent.setOutputPad(padding);
|
|
2967
|
+
}
|
|
2968
|
+
this.ui.requestRender();
|
|
2969
|
+
return;
|
|
2970
|
+
}
|
|
2971
|
+
this.rebuildChatFromMessages();
|
|
2972
|
+
},
|
|
2973
|
+
onAutocompleteMaxVisibleChange: (maxVisible) => {
|
|
2974
|
+
this.settingsManager.setAutocompleteMaxVisible(maxVisible);
|
|
2975
|
+
this.defaultEditor.setAutocompleteMaxVisible(maxVisible);
|
|
2976
|
+
if (this.editor !== this.defaultEditor && this.editor.setAutocompleteMaxVisible !== undefined) {
|
|
2977
|
+
this.editor.setAutocompleteMaxVisible(maxVisible);
|
|
2978
|
+
}
|
|
2979
|
+
},
|
|
2980
|
+
onClearOnShrinkChange: (enabled) => {
|
|
2981
|
+
this.settingsManager.setClearOnShrink(enabled);
|
|
2982
|
+
this.ui.setClearOnShrink(enabled);
|
|
2983
|
+
if (!enabled && !this.activeStatusIndicator) {
|
|
2984
|
+
this.statusContainer.clear();
|
|
2985
|
+
}
|
|
2986
|
+
},
|
|
2987
|
+
onShowTerminalProgressChange: (enabled) => {
|
|
2988
|
+
this.settingsManager.setShowTerminalProgress(enabled);
|
|
2989
|
+
},
|
|
2990
|
+
onTuiModeChange: (mode) => {
|
|
2991
|
+
if (!this.switchTuiMode(mode)) {
|
|
2992
|
+
selector?.getSettingsList().updateValue("tui-mode", this.ui.mode);
|
|
2993
|
+
this.showStatus("Close active overlays before changing TUI mode");
|
|
2994
|
+
return;
|
|
2995
|
+
}
|
|
2996
|
+
this.settingsManager.setTuiMode(mode);
|
|
2997
|
+
if (!this.activeStatusIndicator)
|
|
2998
|
+
this.statusContainer.clear();
|
|
2999
|
+
this.showStatus(`TUI mode: ${mode}`);
|
|
3000
|
+
},
|
|
3001
|
+
onFullscreenExitOutputChange: (output) => {
|
|
3002
|
+
this.settingsManager.setFullscreenExitOutput(output);
|
|
3003
|
+
},
|
|
3004
|
+
onFullscreenScrollbarChange: (mode) => {
|
|
3005
|
+
this.settingsManager.setFullscreenScrollbar(mode);
|
|
3006
|
+
this.applyFullscreenScrollbarSetting();
|
|
3007
|
+
},
|
|
3008
|
+
onFullscreenCopyOnSelectChange: (enabled) => {
|
|
3009
|
+
this.settingsManager.setFullscreenCopyOnSelect(enabled);
|
|
3010
|
+
if (this.renderer instanceof TuiAltScreen)
|
|
3011
|
+
this.renderer.setCopyOnSelect(enabled);
|
|
3012
|
+
},
|
|
3013
|
+
onWarningsChange: (warnings) => {
|
|
3014
|
+
this.settingsManager.setWarnings(warnings);
|
|
3015
|
+
},
|
|
3016
|
+
onCancel: () => {
|
|
3017
|
+
done();
|
|
3018
|
+
this.ui.requestRender();
|
|
3019
|
+
},
|
|
3020
|
+
});
|
|
3021
|
+
return { component: selector, focus: selector.getSettingsList() };
|
|
3022
|
+
});
|
|
3023
|
+
}
|
|
3024
|
+
/** /thinking command entry; implementation in ./interactive-selectors.ts (S5 slice 5). */
|
|
3025
|
+
handleThinkingCommand(searchTerm) {
|
|
3026
|
+
selectorsFlow.handleThinkingCommand(this, searchTerm);
|
|
3027
|
+
}
|
|
3028
|
+
/** Thinking level apply; implementation in ./interactive-selectors.ts (S5 slice 5). */
|
|
3029
|
+
selectThinkingLevel(level, persist) {
|
|
3030
|
+
selectorsFlow.selectThinkingLevel(this, level, persist);
|
|
3031
|
+
}
|
|
3032
|
+
/** Thinking level picker; implementation in ./interactive-selectors.ts (S5 slice 5). */
|
|
3033
|
+
showThinkingSelector() {
|
|
3034
|
+
selectorsFlow.showThinkingSelector(this);
|
|
3035
|
+
}
|
|
3036
|
+
/**
|
|
3037
|
+
* Current session's suite id, read from the session custom entry recorded at
|
|
3038
|
+
* creation (设计 §10.1). Never inferred from settings or env; undefined when
|
|
3039
|
+
* the session carries no suite entry (e.g. resumed legacy sessions).
|
|
3040
|
+
* Internal collaboration surface; exposed for ./interactive-selectors.ts
|
|
3041
|
+
* (S5 slice 5) while the read stays on the mode (session-boundary carve-out).
|
|
3042
|
+
*/
|
|
3043
|
+
getCurrentSessionSuiteId() {
|
|
3044
|
+
const entries = this.facade.getEntries();
|
|
3045
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
3046
|
+
const entry = entries[i];
|
|
3047
|
+
if (entry.type !== "custom" || entry.customType !== SUITE_SESSION_ENTRY_TYPE)
|
|
3048
|
+
continue;
|
|
3049
|
+
const suiteId = entry.data?.suiteId;
|
|
3050
|
+
if (typeof suiteId === "string" && suiteId.trim().length > 0)
|
|
3051
|
+
return suiteId;
|
|
3052
|
+
}
|
|
3053
|
+
return undefined;
|
|
3054
|
+
}
|
|
3055
|
+
sessionSuiteIdCache;
|
|
3056
|
+
/** Host-visible for ./interactive-selectors.ts (S5 slice 5): narrow session read. */
|
|
3057
|
+
getAvailableThinkingLevels() {
|
|
3058
|
+
return this.facade.getAvailableThinkingLevels();
|
|
3059
|
+
}
|
|
3060
|
+
/** Host-visible for ./interactive-selectors.ts (S5 slice 5): narrow session read. */
|
|
3061
|
+
getUserMessagesForForking() {
|
|
3062
|
+
return this.facade.getUserMessagesForForking();
|
|
3063
|
+
}
|
|
3064
|
+
/**
|
|
3065
|
+
* Bound suite id of a listed session (设计 §10.1), read from the session
|
|
3066
|
+
* file's binding entry and cached per path — the selector refilters the
|
|
3067
|
+
* list on every keystroke.
|
|
3068
|
+
*/
|
|
3069
|
+
readSessionSuiteId(sessionPath) {
|
|
3070
|
+
this.sessionSuiteIdCache ??= new Map();
|
|
3071
|
+
if (this.sessionSuiteIdCache.has(sessionPath))
|
|
3072
|
+
return this.sessionSuiteIdCache.get(sessionPath);
|
|
3073
|
+
const suiteId = readSessionSuiteIdFromFile(sessionPath);
|
|
3074
|
+
this.sessionSuiteIdCache.set(sessionPath, suiteId);
|
|
3075
|
+
return suiteId;
|
|
3076
|
+
}
|
|
3077
|
+
/** /suite command entry; implementation in ./interactive-selectors.ts (S5 slice 5). */
|
|
3078
|
+
handleSuiteCommand(suiteId) {
|
|
3079
|
+
selectorsFlow.handleSuiteCommand(this, suiteId);
|
|
3080
|
+
}
|
|
3081
|
+
/** Suite list overlay; implementation in ./interactive-selectors.ts (S5 slice 5). */
|
|
3082
|
+
showSuiteSelector(availableSuites) {
|
|
3083
|
+
selectorsFlow.showSuiteSelector(this, availableSuites);
|
|
3084
|
+
}
|
|
3085
|
+
/**
|
|
3086
|
+
* Shared suite list overlay (设计 §9); implementation in
|
|
3087
|
+
* ./interactive-selectors.ts (S5 slice 5).
|
|
3088
|
+
*/
|
|
3089
|
+
openSuiteListSelector(callbacks) {
|
|
3090
|
+
selectorsFlow.openSuiteListSelector(this, callbacks);
|
|
3091
|
+
}
|
|
3092
|
+
/**
|
|
3093
|
+
* Interactive recovery for an unresolved startup suite id (设计 §9): pick a
|
|
3094
|
+
* suite, persist it as the project setting, then rebuild the session with
|
|
3095
|
+
* the selected suite via the same /new assembly path. Cancelling keeps the
|
|
3096
|
+
* fallback assembly the runtime started with.
|
|
3097
|
+
*/
|
|
3098
|
+
async showStartupSuiteSelector(pending) {
|
|
3099
|
+
const selected = await new Promise((resolve) => {
|
|
3100
|
+
this.openSuiteListSelector({
|
|
3101
|
+
availableSuites: pending.available,
|
|
3102
|
+
note: `Suite "${pending.requestedId}" was not found. The selected suite applies to the new session.`,
|
|
3103
|
+
onSelect: (suiteId) => resolve(suiteId),
|
|
3104
|
+
onCancel: () => resolve(undefined),
|
|
3105
|
+
});
|
|
3106
|
+
});
|
|
3107
|
+
if (selected === undefined) {
|
|
3108
|
+
this.showWarning(`Suite "${pending.requestedId}" was not found — continuing without a suite. Use /suite to choose one.`);
|
|
3109
|
+
return;
|
|
3110
|
+
}
|
|
3111
|
+
this.settingsManager.setProjectSuite(selected);
|
|
3112
|
+
try {
|
|
3113
|
+
await this.runtimeFacade.newSession();
|
|
3114
|
+
this.showStatus(`Suite set to ${selected} — new session started`);
|
|
3115
|
+
}
|
|
3116
|
+
catch (error) {
|
|
3117
|
+
await this.handleFatalRuntimeError("Failed to create session", error);
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
/** /model command entry; implementation in ./interactive-selectors.ts (S5 slice 5). */
|
|
3121
|
+
async handleModelCommand(searchTerm) {
|
|
3122
|
+
await selectorsFlow.handleModelCommand(this, searchTerm);
|
|
3123
|
+
}
|
|
3124
|
+
/** Exact model lookup; implementation in ./interactive-selectors.ts (S5 slice 5). */
|
|
3125
|
+
async findExactModelMatch(searchTerm) {
|
|
3126
|
+
return selectorsFlow.findExactModelMatch(this, searchTerm);
|
|
3127
|
+
}
|
|
3128
|
+
/**
|
|
3129
|
+
* Update the footer's available provider count from the current snapshot without
|
|
3130
|
+
* refreshing catalogs. Host-visible for ./interactive-auth.ts (S5 slice 3).
|
|
3131
|
+
*/
|
|
3132
|
+
updateAvailableProviderCount() {
|
|
3133
|
+
const models = this.facade.getScopedModels().length > 0
|
|
3134
|
+
? this.facade.getScopedModels().map((scoped) => scoped.model)
|
|
3135
|
+
: this.facade.getAvailableModels();
|
|
3136
|
+
const uniqueProviders = new Set(models.map((model) => model.provider));
|
|
3137
|
+
this.footerDataProvider.setAvailableProviderCount(uniqueProviders.size);
|
|
3138
|
+
}
|
|
3139
|
+
/** Host-visible for ./interactive-auth.ts (S5 slice 3). */
|
|
3140
|
+
async maybeWarnAboutAnthropicSubscriptionAuth(model = this.facade.getModel()) {
|
|
3141
|
+
if (this.settingsManager.getWarnings().anthropicExtraUsage === false) {
|
|
3142
|
+
return;
|
|
3143
|
+
}
|
|
3144
|
+
if (this.anthropicSubscriptionWarningShown) {
|
|
3145
|
+
return;
|
|
3146
|
+
}
|
|
3147
|
+
if (!model || model.provider !== "anthropic") {
|
|
3148
|
+
return;
|
|
3149
|
+
}
|
|
3150
|
+
try {
|
|
3151
|
+
if ((await this.facade.checkProviderAuth("anthropic"))?.type === "oauth") {
|
|
3152
|
+
this.anthropicSubscriptionWarningShown = true;
|
|
3153
|
+
this.showWarning(ANTHROPIC_SUBSCRIPTION_AUTH_WARNING);
|
|
3154
|
+
return;
|
|
3155
|
+
}
|
|
3156
|
+
const apiKey = (await this.facade.getProviderAuth(model.provider))?.auth.apiKey;
|
|
3157
|
+
if (!isAnthropicSubscriptionAuthKey(apiKey)) {
|
|
3158
|
+
return;
|
|
3159
|
+
}
|
|
3160
|
+
this.anthropicSubscriptionWarningShown = true;
|
|
3161
|
+
this.showWarning(ANTHROPIC_SUBSCRIPTION_AUTH_WARNING);
|
|
3162
|
+
}
|
|
3163
|
+
catch {
|
|
3164
|
+
// Ignore auth lookup failures for warning-only checks.
|
|
3165
|
+
}
|
|
3166
|
+
}
|
|
3167
|
+
/** Model picker; implementation in ./interactive-selectors.ts (S5 slice 5). */
|
|
3168
|
+
showModelSelector(initialSearchInput) {
|
|
3169
|
+
selectorsFlow.showModelSelector(this, initialSearchInput);
|
|
3170
|
+
}
|
|
3171
|
+
/** Scoped-models picker; implementation in ./interactive-selectors.ts (S5 slice 5). */
|
|
3172
|
+
showModelsSelector() {
|
|
3173
|
+
selectorsFlow.showModelsSelector(this);
|
|
3174
|
+
}
|
|
3175
|
+
/** Fork picker; implementation in ./interactive-selectors.ts (S5 slice 5). */
|
|
3176
|
+
showUserMessageSelector() {
|
|
3177
|
+
selectorsFlow.showUserMessageSelector(this);
|
|
3178
|
+
}
|
|
3179
|
+
/** /clone command entry; implementation in ./interactive-selectors.ts (S5 slice 5). */
|
|
3180
|
+
async handleCloneCommand() {
|
|
3181
|
+
await selectorsFlow.handleCloneCommand(this);
|
|
3182
|
+
}
|
|
3183
|
+
showTreeSelector(initialSelectedId) {
|
|
3184
|
+
const tree = this.facade.getTree();
|
|
3185
|
+
const realLeafId = this.facade.getLeafId();
|
|
3186
|
+
const initialFilterMode = this.settingsManager.getTreeFilterMode();
|
|
3187
|
+
if (tree.length === 0) {
|
|
3188
|
+
this.showStatus("No entries in session");
|
|
3189
|
+
return;
|
|
3190
|
+
}
|
|
3191
|
+
this.showSelector((done) => {
|
|
3192
|
+
const selector = new TreeSelectorComponent(tree, realLeafId, this.ui.terminal.rows, async (entryId) => {
|
|
3193
|
+
// Selecting the current leaf is a no-op (already there)
|
|
3194
|
+
if (entryId === this.facade.getLeafId()) {
|
|
3195
|
+
done();
|
|
3196
|
+
this.showStatus("Already at this point");
|
|
3197
|
+
return;
|
|
3198
|
+
}
|
|
3199
|
+
// Ask about summarization
|
|
3200
|
+
done(); // Close selector first
|
|
3201
|
+
// Loop until user makes a complete choice or cancels to tree
|
|
3202
|
+
let wantsSummary = false;
|
|
3203
|
+
let customInstructions;
|
|
3204
|
+
// Check if we should skip the prompt (user preference to always default to no summary)
|
|
3205
|
+
if (!this.settingsManager.getBranchSummarySkipPrompt()) {
|
|
3206
|
+
while (true) {
|
|
3207
|
+
const summaryChoice = await this.showDialogSelector("Summarize branch?", [
|
|
3208
|
+
"No summary",
|
|
3209
|
+
"Summarize",
|
|
3210
|
+
"Summarize with custom prompt",
|
|
3211
|
+
]);
|
|
3212
|
+
if (summaryChoice === undefined) {
|
|
3213
|
+
// User pressed escape - re-show tree selector with same selection
|
|
3214
|
+
this.showTreeSelector(entryId);
|
|
3215
|
+
return;
|
|
3216
|
+
}
|
|
3217
|
+
wantsSummary = summaryChoice !== "No summary";
|
|
3218
|
+
if (summaryChoice === "Summarize with custom prompt") {
|
|
3219
|
+
customInstructions = await this.showDialogEditor("Custom summarization instructions");
|
|
3220
|
+
if (customInstructions === undefined) {
|
|
3221
|
+
// User cancelled - loop back to summary selector
|
|
3222
|
+
continue;
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
// User made a complete choice
|
|
3226
|
+
break;
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
// The user committed to navigating: stop the active response first.
|
|
3230
|
+
if (this.facade.isStreaming()) {
|
|
3231
|
+
this.restoreQueuedMessagesToEditor();
|
|
3232
|
+
await this.facade.abort();
|
|
3233
|
+
}
|
|
3234
|
+
// Set up escape handler and status indicator if summarizing
|
|
3235
|
+
let showingSummaryIndicator = false;
|
|
3236
|
+
const originalOnEscape = this.defaultEditor.onEscape;
|
|
3237
|
+
if (wantsSummary) {
|
|
3238
|
+
this.defaultEditor.onEscape = () => {
|
|
3239
|
+
this.facade.abortBranchSummary();
|
|
3240
|
+
};
|
|
3241
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3242
|
+
this.showStatusIndicator(new BranchSummaryStatusIndicator(this.ui));
|
|
3243
|
+
showingSummaryIndicator = true;
|
|
3244
|
+
this.ui.requestRender();
|
|
3245
|
+
}
|
|
3246
|
+
try {
|
|
3247
|
+
const result = await this.facade.navigateTree(entryId, {
|
|
3248
|
+
summarize: wantsSummary,
|
|
3249
|
+
customInstructions,
|
|
3250
|
+
});
|
|
3251
|
+
if (result.aborted) {
|
|
3252
|
+
// Summarization aborted - re-show tree selector with same selection
|
|
3253
|
+
this.showStatus("Branch summarization cancelled");
|
|
3254
|
+
this.showTreeSelector(entryId);
|
|
3255
|
+
return;
|
|
3256
|
+
}
|
|
3257
|
+
if (result.cancelled) {
|
|
3258
|
+
this.showStatus("Navigation cancelled");
|
|
3259
|
+
return;
|
|
3260
|
+
}
|
|
3261
|
+
// Update UI
|
|
3262
|
+
this.chatContainer.clear();
|
|
3263
|
+
this.renderInitialMessages();
|
|
3264
|
+
if (result.editorText && !this.editor.getText().trim()) {
|
|
3265
|
+
this.editor.setText(result.editorText);
|
|
3266
|
+
}
|
|
3267
|
+
this.showStatus("Navigated to selected point");
|
|
3268
|
+
void this.flushCompactionQueue({ willRetry: false });
|
|
3269
|
+
}
|
|
3270
|
+
catch (error) {
|
|
3271
|
+
this.showError(error instanceof Error ? error.message : String(error));
|
|
3272
|
+
}
|
|
3273
|
+
finally {
|
|
3274
|
+
if (showingSummaryIndicator) {
|
|
3275
|
+
this.clearStatusIndicator("branchSummary");
|
|
3276
|
+
}
|
|
3277
|
+
this.defaultEditor.onEscape = originalOnEscape;
|
|
3278
|
+
}
|
|
3279
|
+
}, () => {
|
|
3280
|
+
done();
|
|
3281
|
+
this.ui.requestRender();
|
|
3282
|
+
}, (entryId, label) => {
|
|
3283
|
+
this.facade.appendLabelChange(entryId, label);
|
|
3284
|
+
this.ui.requestRender();
|
|
3285
|
+
}, initialSelectedId, initialFilterMode);
|
|
3286
|
+
selector.onCopy = async (text) => {
|
|
3287
|
+
if (!text) {
|
|
3288
|
+
this.showError("Selected entry has no text to copy");
|
|
3289
|
+
return;
|
|
3290
|
+
}
|
|
3291
|
+
try {
|
|
3292
|
+
await copyToClipboard(text);
|
|
3293
|
+
this.showStatus("Copied selected message to clipboard");
|
|
3294
|
+
}
|
|
3295
|
+
catch (error) {
|
|
3296
|
+
this.showError(error instanceof Error ? error.message : String(error));
|
|
3297
|
+
}
|
|
3298
|
+
};
|
|
3299
|
+
return { component: selector, focus: selector };
|
|
3300
|
+
});
|
|
3301
|
+
}
|
|
3302
|
+
showSessionSelector() {
|
|
3303
|
+
this.showSelector((done) => {
|
|
3304
|
+
const selector = new SessionSelectorComponent((onProgress) => this.runtimeFacade.listSessions(onProgress), (onProgress) => this.runtimeFacade.listAllSessions(onProgress), async (sessionPath) => {
|
|
3305
|
+
done();
|
|
3306
|
+
await this.handleResumeSession(sessionPath);
|
|
3307
|
+
}, () => {
|
|
3308
|
+
done();
|
|
3309
|
+
this.ui.requestRender();
|
|
3310
|
+
}, () => {
|
|
3311
|
+
void this.shutdown();
|
|
3312
|
+
}, () => this.ui.requestRender(), {
|
|
3313
|
+
renameSession: async (sessionFilePath, nextName) => {
|
|
3314
|
+
const next = (nextName ?? "").trim();
|
|
3315
|
+
if (!next)
|
|
3316
|
+
return;
|
|
3317
|
+
const mgr = SessionManager.open(sessionFilePath);
|
|
3318
|
+
mgr.appendSessionInfo(next);
|
|
3319
|
+
},
|
|
3320
|
+
showRenameHint: true,
|
|
3321
|
+
keybindings: this.keybindings,
|
|
3322
|
+
// 会话选择器默认只列当前方案的会话;跨方案会话仅在显式
|
|
3323
|
+
// "show all" 后可见(设计 §10.1)。
|
|
3324
|
+
suiteFilter: {
|
|
3325
|
+
currentSuiteId: this.getCurrentSessionSuiteId(),
|
|
3326
|
+
getSessionSuiteId: (session) => this.readSessionSuiteId(session.path),
|
|
3327
|
+
},
|
|
3328
|
+
}, this.runtimeFacade.getSessionFile());
|
|
3329
|
+
return { component: selector, focus: selector };
|
|
3330
|
+
});
|
|
3331
|
+
}
|
|
3332
|
+
async handleResumeSession(sessionPath, options) {
|
|
3333
|
+
this.clearStatusIndicator();
|
|
3334
|
+
try {
|
|
3335
|
+
const result = await this.runtimeFacade.switchSession(sessionPath, {
|
|
3336
|
+
withSession: options?.withSession,
|
|
3337
|
+
});
|
|
3338
|
+
if (result.cancelled) {
|
|
3339
|
+
return result;
|
|
3340
|
+
}
|
|
3341
|
+
this.showStatus("Resumed session");
|
|
3342
|
+
this.detectCrossSuiteResume();
|
|
3343
|
+
return result;
|
|
3344
|
+
}
|
|
3345
|
+
catch (error) {
|
|
3346
|
+
if (error instanceof MissingSessionCwdError) {
|
|
3347
|
+
const selectedCwd = await this.promptForMissingSessionCwd(error);
|
|
3348
|
+
if (!selectedCwd) {
|
|
3349
|
+
this.showStatus("Resume cancelled");
|
|
3350
|
+
return { cancelled: true };
|
|
3351
|
+
}
|
|
3352
|
+
const result = await this.runtimeFacade.switchSession(sessionPath, {
|
|
3353
|
+
cwdOverride: selectedCwd,
|
|
3354
|
+
withSession: options?.withSession,
|
|
3355
|
+
});
|
|
3356
|
+
if (result.cancelled) {
|
|
3357
|
+
return result;
|
|
3358
|
+
}
|
|
3359
|
+
this.showStatus("Resumed session in current cwd");
|
|
3360
|
+
this.detectCrossSuiteResume();
|
|
3361
|
+
return result;
|
|
3362
|
+
}
|
|
3363
|
+
return this.handleFatalRuntimeError("Failed to resume session", error);
|
|
3364
|
+
}
|
|
3365
|
+
}
|
|
3366
|
+
/**
|
|
3367
|
+
* Cross-suite resume detection (设计 §10.1 最小可用版): compare the loaded
|
|
3368
|
+
* session's original suite binding with the suite resolved from the current
|
|
3369
|
+
* settings/env — the id the resumed session was actually reassembled with
|
|
3370
|
+
* (assembly happens at session creation, so reassembly is the existing
|
|
3371
|
+
* behavior). On a mismatch, record the fact for later debugging: an info
|
|
3372
|
+
* status line plus a `suite_mismatch` custom entry on the session. No
|
|
3373
|
+
* context serialization or conversion is attempted.
|
|
3374
|
+
*/
|
|
3375
|
+
detectCrossSuiteResume() {
|
|
3376
|
+
const originalSuiteId = this.getCurrentSessionSuiteId();
|
|
3377
|
+
// Pre-suite (legacy) sessions carry no binding; nothing to compare.
|
|
3378
|
+
if (originalSuiteId === undefined)
|
|
3379
|
+
return;
|
|
3380
|
+
const currentSuiteId = resolveSuiteId({
|
|
3381
|
+
envSuite: process.env[SUITE_ENV_VAR],
|
|
3382
|
+
settings: this.settingsManager,
|
|
3383
|
+
agentDir: getAgentDir(),
|
|
3384
|
+
});
|
|
3385
|
+
if (currentSuiteId === originalSuiteId)
|
|
3386
|
+
return;
|
|
3387
|
+
this.showStatus(`Cross-suite compatible mode: session created with "${originalSuiteId}", assembling with "${currentSuiteId}"`);
|
|
3388
|
+
this.facade.recordSuiteMismatch(originalSuiteId, currentSuiteId);
|
|
3389
|
+
}
|
|
3390
|
+
// =========================================================================
|
|
3391
|
+
// Login/authentication (implementation in ./interactive-auth.ts, S5 slice 3)
|
|
3392
|
+
// Host-visible narrow accessors plus same-name delegates; prototype-level
|
|
3393
|
+
// tests and the command dispatch bind to these names.
|
|
3394
|
+
// =========================================================================
|
|
3395
|
+
/**
|
|
3396
|
+
* Host-visible for ./interactive-auth.ts (D-048): narrow credential/provider
|
|
3397
|
+
* auth projection of the session's ModelRuntime through the stable facade.
|
|
3398
|
+
* The full runtime never crosses the host boundary (D-047 裁决 3's
|
|
3399
|
+
* credential-management semantics stay open).
|
|
3400
|
+
*/
|
|
3401
|
+
getModelAuthRuntime() {
|
|
3402
|
+
return this.facade.getModelAuthRuntime();
|
|
3403
|
+
}
|
|
3404
|
+
/** Host-visible for ./interactive-auth.ts (D-048): available model snapshot. */
|
|
3405
|
+
getAvailableModels() {
|
|
3406
|
+
return this.facade.getAvailableModels();
|
|
3407
|
+
}
|
|
3408
|
+
/** Host-visible for ./interactive-selectors.ts (D-048): narrow catalog-refresh projection. */
|
|
3409
|
+
getModelCatalog() {
|
|
3410
|
+
return this.facade.getModelCatalog();
|
|
3411
|
+
}
|
|
3412
|
+
/** Host-visible for ./interactive-auth.ts (S5 slice 3): opaque session identity. */
|
|
3413
|
+
getSessionIdentity() {
|
|
3414
|
+
return this.session;
|
|
3415
|
+
}
|
|
3416
|
+
/** Host-visible for ./interactive-auth.ts (S5 slice 3). */
|
|
3417
|
+
isSameSessionIdentity(identity) {
|
|
3418
|
+
return this.session === identity;
|
|
3419
|
+
}
|
|
3420
|
+
/** Host-visible for ./interactive-auth.ts (S5 slice 3): stable facade read. */
|
|
3421
|
+
getSelectedModel() {
|
|
3422
|
+
return this.facade.getModel();
|
|
3423
|
+
}
|
|
3424
|
+
/** Host-visible for ./interactive-auth.ts (S5 slice 3): stable facade write. */
|
|
3425
|
+
async setSelectedModel(model) {
|
|
3426
|
+
await this.facade.setModel(model, { persist: true });
|
|
3427
|
+
}
|
|
3428
|
+
/** Host-visible for ./interactive-auth.ts (S5 slice 3). */
|
|
3429
|
+
invalidateFooter() {
|
|
3430
|
+
this.footer.invalidate();
|
|
3431
|
+
}
|
|
3432
|
+
/** Host-visible for ./interactive-auth.ts (S5 slice 3); delegates to the selector area. */
|
|
3433
|
+
showAuthSelector(create) {
|
|
3434
|
+
this.showSelector(create);
|
|
3435
|
+
}
|
|
3436
|
+
/** Login provider options; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3437
|
+
getLoginProviderOptions(authType) {
|
|
3438
|
+
return authFlow.getLoginProviderOptions(this, authType);
|
|
3439
|
+
}
|
|
3440
|
+
/** Logout provider options; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3441
|
+
async getLogoutProviderOptions() {
|
|
3442
|
+
return authFlow.getLogoutProviderOptions(this);
|
|
3443
|
+
}
|
|
3444
|
+
/** Login provider lookup; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3445
|
+
findLoginProviderOptions(providerRef) {
|
|
3446
|
+
return authFlow.findLoginProviderOptions(this, providerRef);
|
|
3447
|
+
}
|
|
3448
|
+
/** /login command entry; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3449
|
+
async handleLoginCommand(providerRef) {
|
|
3450
|
+
await authFlow.handleLoginCommand(this, providerRef);
|
|
3451
|
+
}
|
|
3452
|
+
/** Login branch by auth type; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3453
|
+
async startProviderLogin(providerOption) {
|
|
3454
|
+
await authFlow.startProviderLogin(this, providerOption);
|
|
3455
|
+
}
|
|
3456
|
+
/** Auth type selector; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3457
|
+
showLoginAuthTypeSelector(providerOptions) {
|
|
3458
|
+
authFlow.showLoginAuthTypeSelector(this, providerOptions);
|
|
3459
|
+
}
|
|
3460
|
+
/** Login provider selector; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3461
|
+
showLoginProviderSelector(authType, initialSearchInput) {
|
|
3462
|
+
authFlow.showLoginProviderSelector(this, authType, initialSearchInput);
|
|
3463
|
+
}
|
|
3464
|
+
/** Login/logout selector entry; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3465
|
+
async showOAuthSelector(mode) {
|
|
3466
|
+
await authFlow.showOAuthSelector(this, mode);
|
|
3467
|
+
}
|
|
3468
|
+
/** Post-login completion; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3469
|
+
async completeProviderAuthentication(providerId, providerName, authType, previousModel) {
|
|
3470
|
+
await authFlow.completeProviderAuthentication(this, providerId, providerName, authType, previousModel);
|
|
3471
|
+
}
|
|
3472
|
+
/** Ambient auth notice; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3473
|
+
showAmbientAuthDialog(providerOption) {
|
|
3474
|
+
authFlow.showAmbientAuthDialog(this, providerOption);
|
|
3475
|
+
}
|
|
3476
|
+
/** API key login dialog; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3477
|
+
async showApiKeyLoginDialog(providerId, providerName) {
|
|
3478
|
+
await authFlow.showApiKeyLoginDialog(this, providerId, providerName);
|
|
3479
|
+
}
|
|
3480
|
+
/** Auth select prompt; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3481
|
+
showAuthSelect(dialog, prompt) {
|
|
3482
|
+
return authFlow.showAuthSelect(this, dialog, prompt);
|
|
3483
|
+
}
|
|
3484
|
+
/** Auth prompt plumbing; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3485
|
+
async showAuthPrompt(dialog, prompt) {
|
|
3486
|
+
return authFlow.showAuthPrompt(this, dialog, prompt);
|
|
3487
|
+
}
|
|
3488
|
+
/** Auth event rendering; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3489
|
+
notifyAuthDialog(dialog, event) {
|
|
3490
|
+
authFlow.notifyAuthDialog(this, dialog, event);
|
|
3491
|
+
}
|
|
3492
|
+
/** Provider login call; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3493
|
+
async loginProvider(dialog, providerId, method) {
|
|
3494
|
+
await authFlow.loginProvider(this, dialog, providerId, method);
|
|
3495
|
+
}
|
|
3496
|
+
/** OAuth login dialog; implementation in ./interactive-auth.ts (S5 slice 3). */
|
|
3497
|
+
async showLoginDialog(providerId, providerName) {
|
|
3498
|
+
await authFlow.showLoginDialog(this, providerId, providerName);
|
|
3499
|
+
}
|
|
3500
|
+
// =========================================================================
|
|
3501
|
+
// Command handlers
|
|
3502
|
+
// =========================================================================
|
|
3503
|
+
async handleReloadCommand() {
|
|
3504
|
+
if (this.facade.isStreaming()) {
|
|
3505
|
+
this.showWarning("Wait for the current response to finish before reloading.");
|
|
3506
|
+
return;
|
|
3507
|
+
}
|
|
3508
|
+
if (this.facade.isCompacting()) {
|
|
3509
|
+
this.showWarning("Wait for compaction to finish before reloading.");
|
|
3510
|
+
return;
|
|
3511
|
+
}
|
|
3512
|
+
this.resetDialogUI();
|
|
3513
|
+
const reloadBox = new Container();
|
|
3514
|
+
const borderColor = (s) => theme.fg("border", s);
|
|
3515
|
+
reloadBox.addChild(new DynamicBorder(borderColor));
|
|
3516
|
+
reloadBox.addChild(new Spacer(1));
|
|
3517
|
+
reloadBox.addChild(new Text(theme.fg("muted", "Reloading keybindings, skills, prompts, themes, and context files..."), 1, 0));
|
|
3518
|
+
reloadBox.addChild(new Spacer(1));
|
|
3519
|
+
reloadBox.addChild(new DynamicBorder(borderColor));
|
|
3520
|
+
const previousEditor = this.editor;
|
|
3521
|
+
this.editorContainer.clear();
|
|
3522
|
+
this.editorContainer.addChild(reloadBox);
|
|
3523
|
+
this.ui.setFocus(reloadBox);
|
|
3524
|
+
this.ui.requestRender(true);
|
|
3525
|
+
await new Promise((resolve) => process.nextTick(resolve));
|
|
3526
|
+
const dismissReloadBox = (editor) => {
|
|
3527
|
+
this.editorContainer.clear();
|
|
3528
|
+
this.editorContainer.addChild(editor);
|
|
3529
|
+
this.ui.setFocus(editor);
|
|
3530
|
+
this.ui.requestRender();
|
|
3531
|
+
};
|
|
3532
|
+
let chatRestoredBeforeSessionStart = false;
|
|
3533
|
+
let reloadBoxDismissed = false;
|
|
3534
|
+
const restoreChatBeforeSessionStart = () => {
|
|
3535
|
+
if (chatRestoredBeforeSessionStart) {
|
|
3536
|
+
return;
|
|
3537
|
+
}
|
|
3538
|
+
this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
|
|
3539
|
+
this.outputPad = this.settingsManager.getOutputPad();
|
|
3540
|
+
this.rebuildChatFromMessages();
|
|
3541
|
+
chatRestoredBeforeSessionStart = true;
|
|
3542
|
+
};
|
|
3543
|
+
try {
|
|
3544
|
+
await this.facade.reload({ beforeSessionStart: restoreChatBeforeSessionStart });
|
|
3545
|
+
restoreChatBeforeSessionStart();
|
|
3546
|
+
this.keybindings.reload();
|
|
3547
|
+
const activeHeader = this.builtInHeader;
|
|
3548
|
+
if (isExpandable(activeHeader)) {
|
|
3549
|
+
activeHeader.setExpanded(this.toolOutputExpanded);
|
|
3550
|
+
}
|
|
3551
|
+
setRegisteredThemes(buildRegisteredThemes(this.facade.getThemes().themes));
|
|
3552
|
+
this.applyRuntimeSettings();
|
|
3553
|
+
await this.themeController.applyFromSettings();
|
|
3554
|
+
this.setupAutocompleteProvider();
|
|
3555
|
+
this.showLoadedResources({
|
|
3556
|
+
force: false,
|
|
3557
|
+
showDiagnosticsWhenQuiet: true,
|
|
3558
|
+
});
|
|
3559
|
+
const modelsJsonError = this.facade.getModelsError();
|
|
3560
|
+
if (modelsJsonError) {
|
|
3561
|
+
this.showError(`models.json error: ${modelsJsonError}`);
|
|
3562
|
+
}
|
|
3563
|
+
this.showStatus("Reloaded keybindings, skills, prompts, themes, and context files");
|
|
3564
|
+
dismissReloadBox(this.editor);
|
|
3565
|
+
reloadBoxDismissed = true;
|
|
3566
|
+
}
|
|
3567
|
+
catch (error) {
|
|
3568
|
+
// v8 ignore next -- reloadBoxDismissed 置位后 catch 前无可抛出点,恒走 dismiss 分支
|
|
3569
|
+
if (!reloadBoxDismissed) {
|
|
3570
|
+
dismissReloadBox(previousEditor);
|
|
3571
|
+
}
|
|
3572
|
+
this.showError(`Reload failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
async handleExportCommand(text) {
|
|
3576
|
+
const outputPath = this.getPathCommandArgument(text, "/export");
|
|
3577
|
+
try {
|
|
3578
|
+
if (outputPath?.endsWith(".jsonl")) {
|
|
3579
|
+
const filePath = this.facade.exportToJsonl(outputPath);
|
|
3580
|
+
this.showStatus(`Session exported to: ${filePath}`);
|
|
3581
|
+
}
|
|
3582
|
+
else {
|
|
3583
|
+
const filePath = await this.facade.exportToHtml(outputPath, {
|
|
3584
|
+
themeName: theme.name,
|
|
3585
|
+
});
|
|
3586
|
+
this.showStatus(`Session exported to: ${filePath}`);
|
|
3587
|
+
}
|
|
3588
|
+
}
|
|
3589
|
+
catch (error) {
|
|
3590
|
+
this.showError(`Failed to export session: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
getPathCommandArgument(text, command) {
|
|
3594
|
+
if (text === command) {
|
|
3595
|
+
return undefined;
|
|
3596
|
+
}
|
|
3597
|
+
if (!text.startsWith(`${command} `)) {
|
|
3598
|
+
return undefined;
|
|
3599
|
+
}
|
|
3600
|
+
const argsString = text.slice(command.length + 1).trimStart();
|
|
3601
|
+
if (!argsString) {
|
|
3602
|
+
return undefined;
|
|
3603
|
+
}
|
|
3604
|
+
const firstChar = argsString[0];
|
|
3605
|
+
if (firstChar === '"' || firstChar === "'") {
|
|
3606
|
+
const closingQuoteIndex = argsString.indexOf(firstChar, 1);
|
|
3607
|
+
if (closingQuoteIndex < 0) {
|
|
3608
|
+
return undefined;
|
|
3609
|
+
}
|
|
3610
|
+
return argsString.slice(1, closingQuoteIndex);
|
|
3611
|
+
}
|
|
3612
|
+
const firstWhitespaceIndex = argsString.search(/\s/);
|
|
3613
|
+
if (firstWhitespaceIndex < 0) {
|
|
3614
|
+
return argsString;
|
|
3615
|
+
}
|
|
3616
|
+
return argsString.slice(0, firstWhitespaceIndex);
|
|
3617
|
+
}
|
|
3618
|
+
async handleImportCommand(text) {
|
|
3619
|
+
const inputPath = this.getPathCommandArgument(text, "/import");
|
|
3620
|
+
if (!inputPath) {
|
|
3621
|
+
this.showError("Usage: /import <path.jsonl>");
|
|
3622
|
+
return;
|
|
3623
|
+
}
|
|
3624
|
+
const confirmed = await this.showDialogConfirm("Import session", `Replace current session with ${inputPath}?`);
|
|
3625
|
+
if (!confirmed) {
|
|
3626
|
+
this.showStatus("Import cancelled");
|
|
3627
|
+
return;
|
|
3628
|
+
}
|
|
3629
|
+
try {
|
|
3630
|
+
this.clearStatusIndicator();
|
|
3631
|
+
const result = await this.runtimeFacade.importFromJsonl(inputPath);
|
|
3632
|
+
if (result.cancelled) {
|
|
3633
|
+
this.showStatus("Import cancelled");
|
|
3634
|
+
return;
|
|
3635
|
+
}
|
|
3636
|
+
this.showStatus(`Session imported from: ${inputPath}`);
|
|
3637
|
+
}
|
|
3638
|
+
catch (error) {
|
|
3639
|
+
if (error instanceof MissingSessionCwdError) {
|
|
3640
|
+
const selectedCwd = await this.promptForMissingSessionCwd(error);
|
|
3641
|
+
if (!selectedCwd) {
|
|
3642
|
+
this.showStatus("Import cancelled");
|
|
3643
|
+
return;
|
|
3644
|
+
}
|
|
3645
|
+
const result = await this.runtimeFacade.importFromJsonl(inputPath, selectedCwd);
|
|
3646
|
+
if (result.cancelled) {
|
|
3647
|
+
this.showStatus("Import cancelled");
|
|
3648
|
+
return;
|
|
3649
|
+
}
|
|
3650
|
+
this.showStatus(`Session imported from: ${inputPath}`);
|
|
3651
|
+
return;
|
|
3652
|
+
}
|
|
3653
|
+
if (error instanceof SessionImportFileNotFoundError) {
|
|
3654
|
+
this.showError(`Failed to import session: ${error.message}`);
|
|
3655
|
+
return;
|
|
3656
|
+
}
|
|
3657
|
+
await this.handleFatalRuntimeError("Failed to import session", error);
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
async handleCopyCommand(options = {}) {
|
|
3661
|
+
if (options.preferSelection &&
|
|
3662
|
+
this.ui instanceof TuiAltScreen &&
|
|
3663
|
+
!this.ui.getCopyOnSelect() &&
|
|
3664
|
+
this.ui.hasActiveSelection()) {
|
|
3665
|
+
await this.ui.copyActiveSelectionToClipboard();
|
|
3666
|
+
return;
|
|
3667
|
+
}
|
|
3668
|
+
const text = this.facade.getLastAssistantText();
|
|
3669
|
+
if (!text) {
|
|
3670
|
+
this.showError("No agent messages to copy yet.");
|
|
3671
|
+
return;
|
|
3672
|
+
}
|
|
3673
|
+
try {
|
|
3674
|
+
await copyToClipboard(text);
|
|
3675
|
+
if (options.flashConfirmation && this.ui instanceof TuiAltScreen) {
|
|
3676
|
+
this.ui.flash("Copied!");
|
|
3677
|
+
}
|
|
3678
|
+
else {
|
|
3679
|
+
this.showStatus("Copied last agent message to clipboard");
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3682
|
+
catch (error) {
|
|
3683
|
+
this.showError(error instanceof Error ? error.message : String(error));
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
handleNameCommand(text) {
|
|
3687
|
+
const name = text.replace(/^\/name\s*/, "").trim();
|
|
3688
|
+
if (!name) {
|
|
3689
|
+
const currentName = this.facade.getSessionName();
|
|
3690
|
+
if (currentName) {
|
|
3691
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3692
|
+
this.chatContainer.addChild(new Text(theme.fg("dim", `Session name: ${currentName}`), 1, 0));
|
|
3693
|
+
}
|
|
3694
|
+
else {
|
|
3695
|
+
this.showWarning("Usage: /name <name>");
|
|
3696
|
+
}
|
|
3697
|
+
this.ui.requestRender();
|
|
3698
|
+
return;
|
|
3699
|
+
}
|
|
3700
|
+
this.facade.setSessionName(name);
|
|
3701
|
+
const sessionName = this.facade.getSessionName();
|
|
3702
|
+
if (sessionName !== name) {
|
|
3703
|
+
this.showWarning(`Session name was normalized from ${JSON.stringify(name)} to ${JSON.stringify(sessionName)}`);
|
|
3704
|
+
}
|
|
3705
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3706
|
+
this.chatContainer.addChild(new Text(theme.fg("dim", `Session name set: ${sessionName ?? name}`), 1, 0));
|
|
3707
|
+
this.ui.requestRender();
|
|
3708
|
+
}
|
|
3709
|
+
handleSessionCommand() {
|
|
3710
|
+
const stats = this.facade.getSessionStats();
|
|
3711
|
+
const sessionName = this.facade.getSessionName();
|
|
3712
|
+
const entries = this.facade.getEntries();
|
|
3713
|
+
const cacheWaste = computeCacheWaste(entries, this.facade.getModelPriceSource());
|
|
3714
|
+
// Cost/token totals per provider/model actually used (e.g. OpenRouter `auto`
|
|
3715
|
+
// resolves to a concrete responseModel). Usage without model attribution is
|
|
3716
|
+
// grouped separately so the breakdown reconciles with the session total.
|
|
3717
|
+
const usageBreakdown = getUsageCostBreakdown(entries);
|
|
3718
|
+
let info = `${theme.bold("Session Info")}\n\n`;
|
|
3719
|
+
if (sessionName) {
|
|
3720
|
+
info += `${theme.fg("dim", "Name:")} ${sessionName}\n`;
|
|
3721
|
+
}
|
|
3722
|
+
info += `${theme.fg("dim", "File:")} ${stats.sessionFile ?? "In-memory"}\n`;
|
|
3723
|
+
info += `${theme.fg("dim", "ID:")} ${stats.sessionId}\n\n`;
|
|
3724
|
+
info += `${theme.bold("Messages")}\n`;
|
|
3725
|
+
info += `${theme.fg("dim", "Total:")} ${stats.totalMessages}\n`;
|
|
3726
|
+
info += `${theme.fg("dim", "User:")} ${stats.userMessages}\n`;
|
|
3727
|
+
info += `${theme.fg("dim", "Assistant:")} ${stats.assistantMessages}\n`;
|
|
3728
|
+
info += `${theme.fg("dim", "Tools:")} ${stats.toolCalls} calls, ${stats.toolResults} results\n\n`;
|
|
3729
|
+
info += `${theme.bold("Tokens")}\n`;
|
|
3730
|
+
// "Input" is the full prompt volume. With cache activity, split it into
|
|
3731
|
+
// cached (served from cache) vs uncached (everything else) - the only
|
|
3732
|
+
// provider-independent split. Cache writes, where reported, are a detail
|
|
3733
|
+
// of the uncached portion.
|
|
3734
|
+
const { input, cacheRead, cacheWrite } = stats.tokens;
|
|
3735
|
+
const promptTokens = input + cacheRead + cacheWrite;
|
|
3736
|
+
info += `${theme.fg("dim", "Input:")} ${promptTokens.toLocaleString()}\n`;
|
|
3737
|
+
if (promptTokens > 0 && (cacheRead > 0 || cacheWrite > 0)) {
|
|
3738
|
+
const hitRate = theme.fg("dim", `(${((cacheRead / promptTokens) * 100).toFixed(1)}%)`);
|
|
3739
|
+
info += ` ${theme.fg("dim", "Cached:")} ${cacheRead.toLocaleString()} ${hitRate}\n`;
|
|
3740
|
+
const written = cacheWrite > 0 ? ` ${theme.fg("dim", `(${cacheWrite.toLocaleString()} written to cache)`)}` : "";
|
|
3741
|
+
info += ` ${theme.fg("dim", "Uncached:")} ${(input + cacheWrite).toLocaleString()}${written}\n`;
|
|
3742
|
+
}
|
|
3743
|
+
info += `${theme.fg("dim", "Output:")} ${stats.tokens.output.toLocaleString()}\n`;
|
|
3744
|
+
info += `${theme.fg("dim", "Total:")} ${stats.tokens.total.toLocaleString()}\n`;
|
|
3745
|
+
// 双维度统计(用户裁决 2026-09-25):总计之外按供应商/模型分桶——缓存读/写、
|
|
3746
|
+
// 命中率、费用逐桶可见。此前该拆分门控在 cost>0 且多桶才渲染,单价为 0 的
|
|
3747
|
+
// 自定义供应商(如自建代理)永远看不到。
|
|
3748
|
+
if (usageBreakdown.length > 0) {
|
|
3749
|
+
info += `\n${theme.bold("By Provider/Model")}\n`;
|
|
3750
|
+
for (const entry of usageBreakdown) {
|
|
3751
|
+
const bucket = entry.totals;
|
|
3752
|
+
const promptBucket = bucket.input + bucket.cacheRead + bucket.cacheWrite;
|
|
3753
|
+
let line = ` ${theme.bold(entry.key)}`;
|
|
3754
|
+
if (promptBucket > 0) {
|
|
3755
|
+
line += `\n ${theme.fg("dim", "Input:")} ${promptBucket.toLocaleString()}`;
|
|
3756
|
+
if (bucket.cacheRead > 0 || bucket.cacheWrite > 0) {
|
|
3757
|
+
const rate = promptBucket > 0 ? ((bucket.cacheRead / promptBucket) * 100).toFixed(1) : "0.0";
|
|
3758
|
+
line += ` ${theme.fg("dim", `(cache ${rate}%, W ${bucket.cacheWrite.toLocaleString()})`)}`;
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3761
|
+
if (bucket.output > 0)
|
|
3762
|
+
line += `\n ${theme.fg("dim", "Output:")} ${bucket.output.toLocaleString()}`;
|
|
3763
|
+
if (entry.cost > 0)
|
|
3764
|
+
line += ` ${theme.fg("dim", `$${entry.cost.toFixed(3)}`)}`;
|
|
3765
|
+
info += `${line}\n`;
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
if (stats.cost > 0 || cacheWaste.missedTokens > 0) {
|
|
3769
|
+
info += `\n${theme.bold("Cost")}\n`;
|
|
3770
|
+
info += `${theme.fg("dim", "Total:")} $${stats.cost.toFixed(3)}`;
|
|
3771
|
+
if (usageBreakdown.length > 1) {
|
|
3772
|
+
for (const entry of usageBreakdown) {
|
|
3773
|
+
info += `\n ${theme.fg("dim", `${entry.key}:`)} $${entry.cost.toFixed(3)} ${theme.fg("dim", `(${formatTokens(entry.tokens)} tokens)`)}`;
|
|
3774
|
+
}
|
|
3775
|
+
}
|
|
3776
|
+
if (cacheWaste.missedTokens > 0) {
|
|
3777
|
+
const missLabel = cacheWaste.missCount === 1 ? "1 miss" : `${cacheWaste.missCount} misses`;
|
|
3778
|
+
const detail = `${cacheWaste.missedTokens.toLocaleString()} tokens, ${missLabel}`;
|
|
3779
|
+
info +=
|
|
3780
|
+
cacheWaste.missedCost >= 0.0001
|
|
3781
|
+
? `\n${theme.fg("dim", "Cache Re-billed:")} $${cacheWaste.missedCost.toFixed(3)} ${theme.fg("dim", `(${detail})`)}`
|
|
3782
|
+
: `\n${theme.fg("dim", "Cache Re-billed:")} ${detail}`;
|
|
3783
|
+
}
|
|
3784
|
+
}
|
|
3785
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3786
|
+
this.chatContainer.addChild(new Text(info, 1, 0));
|
|
3787
|
+
this.ui.requestRender();
|
|
3788
|
+
}
|
|
3789
|
+
handleMetricsCommand(turnsArg) {
|
|
3790
|
+
const provider = this.options.getMetrics;
|
|
3791
|
+
if (!provider) {
|
|
3792
|
+
this.showWarning("Metrics are not available: the observability plugin is not mounted in this session");
|
|
3793
|
+
return;
|
|
3794
|
+
}
|
|
3795
|
+
let windowTurns;
|
|
3796
|
+
if (turnsArg !== undefined) {
|
|
3797
|
+
const parsed = Number.parseInt(turnsArg, 10);
|
|
3798
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1) {
|
|
3799
|
+
this.showWarning(`Invalid turns: ${turnsArg}. Usage: /metrics [turns]`);
|
|
3800
|
+
return;
|
|
3801
|
+
}
|
|
3802
|
+
windowTurns = parsed;
|
|
3803
|
+
}
|
|
3804
|
+
let snapshot;
|
|
3805
|
+
try {
|
|
3806
|
+
const result = provider({ windowTurns });
|
|
3807
|
+
if (result === undefined) {
|
|
3808
|
+
this.showWarning("Metrics are not available: the observability plugin is not mounted in this session");
|
|
3809
|
+
return;
|
|
3810
|
+
}
|
|
3811
|
+
snapshot = result;
|
|
3812
|
+
}
|
|
3813
|
+
catch (error) {
|
|
3814
|
+
this.showError(error instanceof Error ? error.message : String(error));
|
|
3815
|
+
return;
|
|
3816
|
+
}
|
|
3817
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3818
|
+
this.chatContainer.addChild(new Text(formatMetricsSnapshot(snapshot), 1, 0));
|
|
3819
|
+
this.ui.requestRender();
|
|
3820
|
+
}
|
|
3821
|
+
handleChangelogCommand() {
|
|
3822
|
+
const changelogPath = getChangelogPath();
|
|
3823
|
+
const allEntries = parseChangelog(changelogPath);
|
|
3824
|
+
const changelogMarkdown = allEntries.length > 0
|
|
3825
|
+
? allEntries
|
|
3826
|
+
.reverse()
|
|
3827
|
+
.map((e) => normalizeChangelogLinks(e.content, e))
|
|
3828
|
+
.join("\n\n")
|
|
3829
|
+
: "No changelog entries found.";
|
|
3830
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3831
|
+
this.chatContainer.addChild(new DynamicBorder());
|
|
3832
|
+
this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "What's New")), 1, 0));
|
|
3833
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3834
|
+
this.chatContainer.addChild(new Markdown(changelogMarkdown, 1, 1, this.getMarkdownThemeWithSettings()));
|
|
3835
|
+
this.chatContainer.addChild(new DynamicBorder());
|
|
3836
|
+
this.ui.requestRender();
|
|
3837
|
+
}
|
|
3838
|
+
/**
|
|
3839
|
+
* Get capitalized display string for an app keybinding action.
|
|
3840
|
+
*/
|
|
3841
|
+
getAppKeyDisplay(action) {
|
|
3842
|
+
return keyDisplayText(action);
|
|
3843
|
+
}
|
|
3844
|
+
/**
|
|
3845
|
+
* Get capitalized display string for an editor keybinding action.
|
|
3846
|
+
*/
|
|
3847
|
+
getEditorKeyDisplay(action) {
|
|
3848
|
+
return keyDisplayText(action);
|
|
3849
|
+
}
|
|
3850
|
+
handleHelpCommand() {
|
|
3851
|
+
const section = (title, rows) => {
|
|
3852
|
+
if (rows.length === 0)
|
|
3853
|
+
return "";
|
|
3854
|
+
let table = `\n**${title}**\n| Command | Description |\n|---------|-------------|\n`;
|
|
3855
|
+
for (const row of rows) {
|
|
3856
|
+
const command = row.hint ? `\`/${row.name} ${row.hint}\`` : `\`/${row.name}\``;
|
|
3857
|
+
table += `| ${command} | ${row.description ?? ""} |\n`;
|
|
3858
|
+
}
|
|
3859
|
+
return table;
|
|
3860
|
+
};
|
|
3861
|
+
const builtinRows = BUILTIN_SLASH_COMMANDS.map((command) => ({
|
|
3862
|
+
name: command.name,
|
|
3863
|
+
hint: command.argumentHint,
|
|
3864
|
+
description: command.description,
|
|
3865
|
+
}));
|
|
3866
|
+
const promptRows = this.facade.getPromptTemplates().map((template) => ({
|
|
3867
|
+
name: template.name,
|
|
3868
|
+
hint: template.argumentHint,
|
|
3869
|
+
description: template.description,
|
|
3870
|
+
}));
|
|
3871
|
+
const skillRows = this.facade.getSkills().skills.map((skill) => ({
|
|
3872
|
+
name: `skill:${skill.name}`,
|
|
3873
|
+
description: skill.description,
|
|
3874
|
+
}));
|
|
3875
|
+
const help = section("Built-in commands", builtinRows) +
|
|
3876
|
+
section("Prompt templates", promptRows) +
|
|
3877
|
+
section("Skills", skillRows) +
|
|
3878
|
+
"\nType `/` for autocomplete. Use `/hotkeys` for keyboard shortcuts.";
|
|
3879
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3880
|
+
this.chatContainer.addChild(new DynamicBorder());
|
|
3881
|
+
this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "Available Commands")), 1, 0));
|
|
3882
|
+
this.chatContainer.addChild(new Markdown(help.trim(), 1, 1, this.getMarkdownThemeWithSettings()));
|
|
3883
|
+
this.chatContainer.addChild(new DynamicBorder());
|
|
3884
|
+
this.ui.requestRender();
|
|
3885
|
+
}
|
|
3886
|
+
handleHotkeysCommand() {
|
|
3887
|
+
// Navigation keybindings
|
|
3888
|
+
const cursorUp = this.getEditorKeyDisplay("tui.editor.cursorUp");
|
|
3889
|
+
const cursorDown = this.getEditorKeyDisplay("tui.editor.cursorDown");
|
|
3890
|
+
const cursorLeft = this.getEditorKeyDisplay("tui.editor.cursorLeft");
|
|
3891
|
+
const cursorRight = this.getEditorKeyDisplay("tui.editor.cursorRight");
|
|
3892
|
+
const cursorWordLeft = this.getEditorKeyDisplay("tui.editor.cursorWordLeft");
|
|
3893
|
+
const cursorWordRight = this.getEditorKeyDisplay("tui.editor.cursorWordRight");
|
|
3894
|
+
const cursorLineStart = this.getEditorKeyDisplay("tui.editor.cursorLineStart");
|
|
3895
|
+
const cursorLineEnd = this.getEditorKeyDisplay("tui.editor.cursorLineEnd");
|
|
3896
|
+
const jumpForward = this.getEditorKeyDisplay("tui.editor.jumpForward");
|
|
3897
|
+
const jumpBackward = this.getEditorKeyDisplay("tui.editor.jumpBackward");
|
|
3898
|
+
const pageUp = this.getEditorKeyDisplay("tui.editor.pageUp");
|
|
3899
|
+
const pageDown = this.getEditorKeyDisplay("tui.editor.pageDown");
|
|
3900
|
+
// Editing keybindings
|
|
3901
|
+
const submit = this.getEditorKeyDisplay("tui.input.submit");
|
|
3902
|
+
const newLine = this.getEditorKeyDisplay("tui.input.newLine");
|
|
3903
|
+
const deleteWordBackward = this.getEditorKeyDisplay("tui.editor.deleteWordBackward");
|
|
3904
|
+
const deleteWordForward = this.getEditorKeyDisplay("tui.editor.deleteWordForward");
|
|
3905
|
+
const deleteToLineStart = this.getEditorKeyDisplay("tui.editor.deleteToLineStart");
|
|
3906
|
+
const deleteToLineEnd = this.getEditorKeyDisplay("tui.editor.deleteToLineEnd");
|
|
3907
|
+
const yank = this.getEditorKeyDisplay("tui.editor.yank");
|
|
3908
|
+
const yankPop = this.getEditorKeyDisplay("tui.editor.yankPop");
|
|
3909
|
+
const undo = this.getEditorKeyDisplay("tui.editor.undo");
|
|
3910
|
+
const tab = this.getEditorKeyDisplay("tui.input.tab");
|
|
3911
|
+
// App keybindings
|
|
3912
|
+
const interrupt = this.getAppKeyDisplay("app.interrupt");
|
|
3913
|
+
const clear = this.getAppKeyDisplay("app.clear");
|
|
3914
|
+
const exit = this.getAppKeyDisplay("app.exit");
|
|
3915
|
+
const suspend = this.getAppKeyDisplay("app.suspend");
|
|
3916
|
+
const cycleThinkingLevel = this.getAppKeyDisplay("app.thinking.cycle");
|
|
3917
|
+
const cycleModelForward = this.getAppKeyDisplay("app.model.cycleForward");
|
|
3918
|
+
const selectModel = this.getAppKeyDisplay("app.model.select");
|
|
3919
|
+
const expandTools = this.getAppKeyDisplay("app.tools.expand");
|
|
3920
|
+
const toggleThinking = this.getAppKeyDisplay("app.thinking.toggle");
|
|
3921
|
+
const externalEditor = this.getAppKeyDisplay("app.editor.external");
|
|
3922
|
+
const cycleModelBackward = this.getAppKeyDisplay("app.model.cycleBackward");
|
|
3923
|
+
const copyMessage = this.getAppKeyDisplay("app.message.copy");
|
|
3924
|
+
const followUp = this.getAppKeyDisplay("app.message.followUp");
|
|
3925
|
+
const dequeue = this.getAppKeyDisplay("app.message.dequeue");
|
|
3926
|
+
const pasteImage = this.getAppKeyDisplay("app.clipboard.pasteImage");
|
|
3927
|
+
const hotkeys = `
|
|
3928
|
+
**Navigation**
|
|
3929
|
+
| Key | Action |
|
|
3930
|
+
|-----|--------|
|
|
3931
|
+
| \`${cursorUp}\` / \`${cursorDown}\` / \`${cursorLeft}\` / \`${cursorRight}\` | Move cursor / browse history |
|
|
3932
|
+
| \`${cursorWordLeft}\` / \`${cursorWordRight}\` | Move by word |
|
|
3933
|
+
| \`${cursorLineStart}\` | Start of line |
|
|
3934
|
+
| \`${cursorLineEnd}\` | End of line |
|
|
3935
|
+
| \`${jumpForward}\` | Jump forward to character |
|
|
3936
|
+
| \`${jumpBackward}\` | Jump backward to character |
|
|
3937
|
+
| \`${pageUp}\` / \`${pageDown}\` | Scroll by page |
|
|
3938
|
+
|
|
3939
|
+
**Editing**
|
|
3940
|
+
| Key | Action |
|
|
3941
|
+
|-----|--------|
|
|
3942
|
+
| \`${submit}\` | Send message |
|
|
3943
|
+
| \`${newLine}\` | New line${process.platform === "win32" ? " (Ctrl+Enter on Windows Terminal)" : ""} |
|
|
3944
|
+
| \`${deleteWordBackward}\` | Delete word backwards |
|
|
3945
|
+
| \`${deleteWordForward}\` | Delete word forwards |
|
|
3946
|
+
| \`${deleteToLineStart}\` | Delete to start of line |
|
|
3947
|
+
| \`${deleteToLineEnd}\` | Delete to end of line |
|
|
3948
|
+
| \`${yank}\` | Paste the most-recently-deleted text |
|
|
3949
|
+
| \`${yankPop}\` | Cycle through the deleted text after pasting |
|
|
3950
|
+
| \`${undo}\` | Undo |
|
|
3951
|
+
|
|
3952
|
+
**Other**
|
|
3953
|
+
| Key | Action |
|
|
3954
|
+
|-----|--------|
|
|
3955
|
+
| \`${tab}\` | Path completion / accept autocomplete |
|
|
3956
|
+
| \`${interrupt}\` | Cancel autocomplete / abort streaming |
|
|
3957
|
+
| \`${clear}\` | Clear editor (first) / exit (second) |
|
|
3958
|
+
| \`${exit}\` | Exit (when editor is empty) |
|
|
3959
|
+
| \`${suspend}\` | Suspend to background |
|
|
3960
|
+
| \`${cycleThinkingLevel}\` | Cycle thinking level |
|
|
3961
|
+
| \`${cycleModelForward}\` / \`${cycleModelBackward}\` | Cycle models |
|
|
3962
|
+
| \`${selectModel}\` | Open model selector |
|
|
3963
|
+
| \`${expandTools}\` | Toggle tool output expansion |
|
|
3964
|
+
| \`${toggleThinking}\` | Toggle thinking block visibility |
|
|
3965
|
+
| \`${externalEditor}\` | Edit message in external editor |
|
|
3966
|
+
| \`${copyMessage}\` | Copy last assistant message |
|
|
3967
|
+
| \`${followUp}\` | Queue follow-up message |
|
|
3968
|
+
| \`${dequeue}\` | Restore queued messages |
|
|
3969
|
+
| \`${pasteImage}\` | Paste image or text from clipboard |
|
|
3970
|
+
| \`/\` | Slash commands |
|
|
3971
|
+
| \`!\` | Run bash command |
|
|
3972
|
+
| \`!!\` | Run bash command (excluded from context) |
|
|
3973
|
+
`;
|
|
3974
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3975
|
+
this.chatContainer.addChild(new DynamicBorder());
|
|
3976
|
+
this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", "Keyboard Shortcuts")), 1, 0));
|
|
3977
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3978
|
+
this.chatContainer.addChild(new Markdown(hotkeys.trim(), 1, 1, this.getMarkdownThemeWithSettings()));
|
|
3979
|
+
this.chatContainer.addChild(new DynamicBorder());
|
|
3980
|
+
this.ui.requestRender();
|
|
3981
|
+
}
|
|
3982
|
+
async handleClearCommand() {
|
|
3983
|
+
this.clearStatusIndicator();
|
|
3984
|
+
try {
|
|
3985
|
+
const result = await this.runtimeFacade.newSession();
|
|
3986
|
+
if (result.cancelled) {
|
|
3987
|
+
return;
|
|
3988
|
+
}
|
|
3989
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3990
|
+
this.chatContainer.addChild(new Text(`${theme.fg("accent", "✓ New session started")}`, 1, 1));
|
|
3991
|
+
this.ui.requestRender();
|
|
3992
|
+
}
|
|
3993
|
+
catch (error) {
|
|
3994
|
+
await this.handleFatalRuntimeError("Failed to create session", error);
|
|
3995
|
+
}
|
|
3996
|
+
}
|
|
3997
|
+
handleDebugCommand() {
|
|
3998
|
+
const width = this.ui.terminal.columns;
|
|
3999
|
+
const height = this.ui.terminal.rows;
|
|
4000
|
+
const allLines = this.ui.render(width);
|
|
4001
|
+
const debugLogPath = getDebugLogPath();
|
|
4002
|
+
const debugData = [
|
|
4003
|
+
`Debug output at ${new Date().toISOString()}`,
|
|
4004
|
+
`Terminal: ${width}x${height}`,
|
|
4005
|
+
`Total lines: ${allLines.length}`,
|
|
4006
|
+
"",
|
|
4007
|
+
"=== All rendered lines with visible widths ===",
|
|
4008
|
+
...allLines.map((line, idx) => {
|
|
4009
|
+
const vw = visibleWidth(line);
|
|
4010
|
+
const escaped = JSON.stringify(line);
|
|
4011
|
+
return `[${idx}] (w=${vw}) ${escaped}`;
|
|
4012
|
+
}),
|
|
4013
|
+
"",
|
|
4014
|
+
"=== Agent messages (JSONL) ===",
|
|
4015
|
+
...this.facade
|
|
4016
|
+
.getSessionAPI()
|
|
4017
|
+
.getMessages()
|
|
4018
|
+
.map((msg) => JSON.stringify(msg)),
|
|
4019
|
+
"",
|
|
4020
|
+
].join("\n");
|
|
4021
|
+
fs.mkdirSync(path.dirname(debugLogPath), { recursive: true });
|
|
4022
|
+
fs.writeFileSync(debugLogPath, debugData);
|
|
4023
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
4024
|
+
this.chatContainer.addChild(new Text(`${theme.fg("accent", "✓ Debug log written")}\n${theme.fg("muted", debugLogPath)}`, 1, 1));
|
|
4025
|
+
this.ui.requestRender();
|
|
4026
|
+
}
|
|
4027
|
+
handleArminSaysHi() {
|
|
4028
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
4029
|
+
this.chatContainer.addChild(new ArminComponent(this.ui));
|
|
4030
|
+
this.ui.requestRender();
|
|
4031
|
+
}
|
|
4032
|
+
handleDaxnuts() {
|
|
4033
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
4034
|
+
this.chatContainer.addChild(new DaxnutsComponent(this.ui));
|
|
4035
|
+
this.ui.requestRender();
|
|
4036
|
+
}
|
|
4037
|
+
/** Host-visible for ./interactive-auth.ts (S5 slice 3). */
|
|
4038
|
+
checkDaxnutsEasterEgg(model) {
|
|
4039
|
+
if (model.provider === "opencode" && model.id.toLowerCase().includes("kimi-k2.5")) {
|
|
4040
|
+
this.handleDaxnuts();
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
4043
|
+
async handleBashCommand(command, excludeFromContext = false) {
|
|
4044
|
+
// Dispatch the user.bash lifecycle facts, then execute locally.
|
|
4045
|
+
this.facade.emitUserBash(excludeFromContext);
|
|
4046
|
+
// Normal execution path
|
|
4047
|
+
const isDeferred = this.facade.isStreaming();
|
|
4048
|
+
this.bashComponent = new BashExecutionComponent(command, this.ui, excludeFromContext);
|
|
4049
|
+
if (isDeferred) {
|
|
4050
|
+
// Show in pending area when agent is streaming
|
|
4051
|
+
this.pendingMessagesContainer.addChild(this.bashComponent);
|
|
4052
|
+
this.pendingBashComponents.push(this.bashComponent);
|
|
4053
|
+
}
|
|
4054
|
+
else {
|
|
4055
|
+
// Show in chat immediately when agent is idle
|
|
4056
|
+
this.chatContainer.addChild(this.bashComponent);
|
|
4057
|
+
}
|
|
4058
|
+
this.ui.requestRender();
|
|
4059
|
+
try {
|
|
4060
|
+
const result = await this.facade.executeBash(command, (chunk) => {
|
|
4061
|
+
if (this.bashComponent) {
|
|
4062
|
+
this.bashComponent.appendOutput(chunk);
|
|
4063
|
+
this.ui.requestRender();
|
|
4064
|
+
}
|
|
4065
|
+
}, { excludeFromContext });
|
|
4066
|
+
if (this.bashComponent) {
|
|
4067
|
+
this.bashComponent.setComplete(result.exitCode, result.cancelled, result.truncated ? { truncated: true, content: result.output } : undefined, result.fullOutputPath);
|
|
4068
|
+
}
|
|
4069
|
+
}
|
|
4070
|
+
catch (error) {
|
|
4071
|
+
if (this.bashComponent) {
|
|
4072
|
+
this.bashComponent.setComplete(undefined, false);
|
|
4073
|
+
}
|
|
4074
|
+
this.showError(`Bash command failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
4075
|
+
}
|
|
4076
|
+
this.bashComponent = undefined;
|
|
4077
|
+
this.ui.requestRender();
|
|
4078
|
+
}
|
|
4079
|
+
async handleCompactCommand(customInstructions) {
|
|
4080
|
+
this.clearStatusIndicator();
|
|
4081
|
+
try {
|
|
4082
|
+
await this.facade.compact(customInstructions);
|
|
4083
|
+
}
|
|
4084
|
+
catch {
|
|
4085
|
+
// Ignore, will be emitted as an event
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
stop(fullscreenExitOutput = this.settingsManager.getFullscreenExitOutput()) {
|
|
4089
|
+
this.disposeActiveSelector();
|
|
4090
|
+
if (this.settingsManager.getShowTerminalProgress()) {
|
|
4091
|
+
this.ui.terminal.setProgress(false);
|
|
4092
|
+
}
|
|
4093
|
+
this.clearStatusIndicator();
|
|
4094
|
+
this.themeController.disableAutoSync();
|
|
4095
|
+
this.footer.dispose();
|
|
4096
|
+
this.footerDataProvider.dispose();
|
|
4097
|
+
if (this.unsubscribe) {
|
|
4098
|
+
this.unsubscribe();
|
|
4099
|
+
}
|
|
4100
|
+
if (this.isInitialized) {
|
|
4101
|
+
this.stopInteractiveTui(fullscreenExitOutput);
|
|
4102
|
+
this.isInitialized = false;
|
|
4103
|
+
}
|
|
4104
|
+
this.unregisterSignalHandlers();
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
//# sourceMappingURL=interactive-mode.js.map
|