@kr78/pi-coding-agent 17.2.10
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 +14861 -0
- package/README.md +35 -0
- package/dist/CHANGELOG-chtdxh1z.md +14861 -0
- package/dist/cli.js +20483 -0
- package/dist/template-c2hyaytt.js +1653 -0
- package/dist/template-f8wx9vfn.css +1355 -0
- package/dist/template-qat058wr.html +55 -0
- package/dist/tool-views.generated-jdfmzwmn.js +35 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +104 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/extensions/README.md +142 -0
- package/examples/extensions/api-demo.ts +78 -0
- package/examples/extensions/chalk-logger.ts +25 -0
- package/examples/extensions/hello.ts +31 -0
- package/examples/extensions/pirate.ts +43 -0
- package/examples/extensions/plan-mode.ts +549 -0
- package/examples/extensions/reload-runtime.ts +38 -0
- package/examples/extensions/thinking-note.ts +13 -0
- package/examples/extensions/tools.ts +145 -0
- package/examples/extensions/with-deps/index.ts +36 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +17 -0
- package/examples/hooks/README.md +56 -0
- package/examples/hooks/auto-commit-on-exit.ts +48 -0
- package/examples/hooks/confirm-destructive.ts +58 -0
- package/examples/hooks/custom-compaction.ts +115 -0
- package/examples/hooks/dirty-repo-guard.ts +51 -0
- package/examples/hooks/file-trigger.ts +40 -0
- package/examples/hooks/git-checkpoint.ts +52 -0
- package/examples/hooks/handoff.ts +149 -0
- package/examples/hooks/permission-gate.ts +33 -0
- package/examples/hooks/protected-paths.ts +29 -0
- package/examples/hooks/qna.ts +118 -0
- package/examples/hooks/status-line.ts +39 -0
- package/examples/sdk/01-minimal.ts +21 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +46 -0
- package/examples/sdk/04-skills.ts +43 -0
- package/examples/sdk/06-extensions.ts +82 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +35 -0
- package/examples/sdk/08-prompt-templates.ts +41 -0
- package/examples/sdk/08-slash-commands.ts +46 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
- package/examples/sdk/11-sessions.ts +47 -0
- package/examples/sdk/12-redis-sessions.ts +54 -0
- package/examples/sdk/13-sql-sessions.ts +61 -0
- package/examples/sdk/README.md +169 -0
- package/package.json +587 -0
- package/scripts/bench-guard.ts +71 -0
- package/scripts/bench-title-models.ts +332 -0
- package/scripts/build-binary.ts +117 -0
- package/scripts/bundle-dist.ts +123 -0
- package/scripts/compile-binary.ts +69 -0
- package/scripts/embed-mupdf-wasm.ts +67 -0
- package/scripts/format-prompts.ts +68 -0
- package/scripts/generate-aria-snapshot.ts +134 -0
- package/scripts/generate-docs-index.ts +58 -0
- package/scripts/generate-share-viewer.ts +34 -0
- package/scripts/legacy-pi-virtual-module.ts +205 -0
- package/scripts/measure-prompt-tokens.ts +63 -0
- package/scripts/omp +51 -0
- package/scripts/omp.ts +19 -0
- package/scripts/security-compare.ts +40 -0
- package/src/advisor/advise-tool.ts +234 -0
- package/src/advisor/config.ts +341 -0
- package/src/advisor/emission-guard.ts +172 -0
- package/src/advisor/index.ts +6 -0
- package/src/advisor/runtime.ts +1412 -0
- package/src/advisor/transcript-recorder.ts +215 -0
- package/src/advisor/watchdog.ts +135 -0
- package/src/async/index.ts +1 -0
- package/src/async/job-manager.ts +847 -0
- package/src/auto-thinking/classifier.ts +220 -0
- package/src/autolearn/controller.ts +152 -0
- package/src/autolearn/managed-skills.ts +255 -0
- package/src/autoresearch/command-resume.md +14 -0
- package/src/autoresearch/dashboard.ts +436 -0
- package/src/autoresearch/git.ts +331 -0
- package/src/autoresearch/helpers.ts +218 -0
- package/src/autoresearch/index.ts +541 -0
- package/src/autoresearch/prompt-setup.md +43 -0
- package/src/autoresearch/prompt.md +103 -0
- package/src/autoresearch/resume-message.md +10 -0
- package/src/autoresearch/state.ts +273 -0
- package/src/autoresearch/storage.ts +700 -0
- package/src/autoresearch/tools/init-experiment.ts +268 -0
- package/src/autoresearch/tools/log-experiment.ts +520 -0
- package/src/autoresearch/tools/run-experiment.ts +407 -0
- package/src/autoresearch/tools/update-notes.ts +109 -0
- package/src/autoresearch/types.ts +168 -0
- package/src/capability/context-file.ts +44 -0
- package/src/capability/extension-module.ts +34 -0
- package/src/capability/extension.ts +47 -0
- package/src/capability/fs.ts +117 -0
- package/src/capability/hook.ts +40 -0
- package/src/capability/index.ts +467 -0
- package/src/capability/instruction.ts +37 -0
- package/src/capability/mcp.ts +115 -0
- package/src/capability/prompt.ts +35 -0
- package/src/capability/rule-buckets.ts +66 -0
- package/src/capability/rule.ts +298 -0
- package/src/capability/settings.ts +34 -0
- package/src/capability/skill.ts +69 -0
- package/src/capability/slash-command.ts +40 -0
- package/src/capability/ssh.ts +41 -0
- package/src/capability/system-prompt.ts +34 -0
- package/src/capability/tool.ts +38 -0
- package/src/capability/types.ts +187 -0
- package/src/cleanse/agent.ts +226 -0
- package/src/cleanse/balance.ts +79 -0
- package/src/cleanse/checkers.ts +996 -0
- package/src/cleanse/index.ts +190 -0
- package/src/cleanse/loop.ts +51 -0
- package/src/cleanse/parsers.ts +726 -0
- package/src/cleanse/progress.ts +50 -0
- package/src/cleanse/prompts/assignment.md +47 -0
- package/src/cleanse/types.ts +72 -0
- package/src/cli/agents-cli.ts +138 -0
- package/src/cli/args.ts +368 -0
- package/src/cli/auth-broker-cli.ts +940 -0
- package/src/cli/auth-gateway-cli.ts +674 -0
- package/src/cli/bench-cli.ts +990 -0
- package/src/cli/browser-relay-cli.ts +119 -0
- package/src/cli/classify-install-target.ts +76 -0
- package/src/cli/claude-trace-cli.ts +795 -0
- package/src/cli/command-help.ts +107 -0
- package/src/cli/commands/init-xdg.ts +27 -0
- package/src/cli/completion-gen.ts +550 -0
- package/src/cli/config-cli.ts +459 -0
- package/src/cli/dry-balance-cli.ts +864 -0
- package/src/cli/extension-flags.ts +48 -0
- package/src/cli/file-processor.ts +132 -0
- package/src/cli/flag-tables.ts +367 -0
- package/src/cli/gallery-cli.ts +272 -0
- package/src/cli/gallery-fixtures/agentic.ts +420 -0
- package/src/cli/gallery-fixtures/codeintel.ts +187 -0
- package/src/cli/gallery-fixtures/edit.ts +254 -0
- package/src/cli/gallery-fixtures/fs.ts +245 -0
- package/src/cli/gallery-fixtures/index.ts +40 -0
- package/src/cli/gallery-fixtures/interaction.ts +46 -0
- package/src/cli/gallery-fixtures/memory.ts +81 -0
- package/src/cli/gallery-fixtures/misc.ts +177 -0
- package/src/cli/gallery-fixtures/search.ts +135 -0
- package/src/cli/gallery-fixtures/shell.ts +241 -0
- package/src/cli/gallery-fixtures/types.ts +57 -0
- package/src/cli/gallery-fixtures/web.ts +158 -0
- package/src/cli/gallery-screenshot.ts +279 -0
- package/src/cli/gc-cli.ts +1566 -0
- package/src/cli/grep-cli.ts +161 -0
- package/src/cli/grievances-cli.ts +256 -0
- package/src/cli/help-extra.ts +89 -0
- package/src/cli/initial-message.ts +58 -0
- package/src/cli/models-cli.ts +385 -0
- package/src/cli/plugin-cli.ts +996 -0
- package/src/cli/profile-alias.ts +369 -0
- package/src/cli/profile-bootstrap.ts +233 -0
- package/src/cli/read-cli.ts +99 -0
- package/src/cli/session-picker.ts +110 -0
- package/src/cli/setup-cli.ts +312 -0
- package/src/cli/setup-model-picker.ts +43 -0
- package/src/cli/shell-cli.ts +176 -0
- package/src/cli/ssh-cli.ts +179 -0
- package/src/cli/startup-cwd.ts +58 -0
- package/src/cli/stats-cli.ts +229 -0
- package/src/cli/thinking-levels.ts +7 -0
- package/src/cli/tiny-models-cli.ts +153 -0
- package/src/cli/ttsr-cli.ts +1013 -0
- package/src/cli/update-cli.ts +1184 -0
- package/src/cli/usage-cli.ts +1084 -0
- package/src/cli/usage-error.ts +7 -0
- package/src/cli/web-search-cli.ts +144 -0
- package/src/cli/worktree-cli.ts +311 -0
- package/src/cli-commands.ts +308 -0
- package/src/cli.ts +428 -0
- package/src/collab/crypto.ts +63 -0
- package/src/collab/display-name.ts +13 -0
- package/src/collab/guest.ts +764 -0
- package/src/collab/host.ts +692 -0
- package/src/collab/protocol.ts +296 -0
- package/src/collab/relay-client.ts +282 -0
- package/src/collab/replication-shrink.ts +111 -0
- package/src/commands/acp.ts +35 -0
- package/src/commands/agents.ts +58 -0
- package/src/commands/auth-broker.ts +100 -0
- package/src/commands/auth-gateway.ts +70 -0
- package/src/commands/bench.ts +64 -0
- package/src/commands/browser-relay.ts +53 -0
- package/src/commands/cleanse.ts +45 -0
- package/src/commands/commit.ts +47 -0
- package/src/commands/complete.ts +67 -0
- package/src/commands/completions.ts +61 -0
- package/src/commands/config.ts +52 -0
- package/src/commands/dry-balance.ts +43 -0
- package/src/commands/gallery.ts +61 -0
- package/src/commands/gc.ts +47 -0
- package/src/commands/grep.ts +49 -0
- package/src/commands/grievances.ts +52 -0
- package/src/commands/install.ts +107 -0
- package/src/commands/join.ts +40 -0
- package/src/commands/launch-help.ts +116 -0
- package/src/commands/launch.ts +34 -0
- package/src/commands/models.ts +61 -0
- package/src/commands/plugin.ts +79 -0
- package/src/commands/read.ts +39 -0
- package/src/commands/say.ts +146 -0
- package/src/commands/setup.ts +68 -0
- package/src/commands/share.ts +71 -0
- package/src/commands/shell.ts +30 -0
- package/src/commands/ssh.ts +61 -0
- package/src/commands/stats.ts +30 -0
- package/src/commands/tiny-models.ts +36 -0
- package/src/commands/token.ts +164 -0
- package/src/commands/ttsr.ts +125 -0
- package/src/commands/update.ts +34 -0
- package/src/commands/usage.ts +55 -0
- package/src/commands/web-search.ts +43 -0
- package/src/commands/worktree.ts +63 -0
- package/src/commit/agentic/agent.ts +322 -0
- package/src/commit/agentic/fallback.ts +96 -0
- package/src/commit/agentic/index.ts +383 -0
- package/src/commit/agentic/lock-files.ts +107 -0
- package/src/commit/agentic/prompts/analyze-file.md +22 -0
- package/src/commit/agentic/prompts/session-user.md +25 -0
- package/src/commit/agentic/prompts/split-confirm.md +1 -0
- package/src/commit/agentic/prompts/system.md +38 -0
- package/src/commit/agentic/state.ts +60 -0
- package/src/commit/agentic/tools/analyze-file.ts +148 -0
- package/src/commit/agentic/tools/git-file-diff.ts +191 -0
- package/src/commit/agentic/tools/git-hunk.ts +52 -0
- package/src/commit/agentic/tools/git-overview.ts +62 -0
- package/src/commit/agentic/tools/index.ts +54 -0
- package/src/commit/agentic/tools/propose-changelog.ts +147 -0
- package/src/commit/agentic/tools/propose-commit.ts +109 -0
- package/src/commit/agentic/tools/recent-commits.ts +81 -0
- package/src/commit/agentic/tools/schemas.ts +11 -0
- package/src/commit/agentic/tools/split-commit.ts +241 -0
- package/src/commit/agentic/topo-sort.ts +44 -0
- package/src/commit/agentic/trivial.ts +51 -0
- package/src/commit/agentic/validation.ts +183 -0
- package/src/commit/analysis/conventional.ts +64 -0
- package/src/commit/analysis/index.ts +4 -0
- package/src/commit/analysis/scope.ts +242 -0
- package/src/commit/analysis/summary.ts +107 -0
- package/src/commit/analysis/validation.ts +66 -0
- package/src/commit/changelog/detect.ts +40 -0
- package/src/commit/changelog/generate.ts +101 -0
- package/src/commit/changelog/index.ts +234 -0
- package/src/commit/changelog/parse.ts +44 -0
- package/src/commit/cli.ts +85 -0
- package/src/commit/git/diff.ts +148 -0
- package/src/commit/index.ts +5 -0
- package/src/commit/map-reduce/index.ts +69 -0
- package/src/commit/map-reduce/map-phase.ts +193 -0
- package/src/commit/map-reduce/reduce-phase.ts +49 -0
- package/src/commit/map-reduce/utils.ts +9 -0
- package/src/commit/message.ts +11 -0
- package/src/commit/model-selection.ts +95 -0
- package/src/commit/pipeline.ts +244 -0
- package/src/commit/prompts/analysis-system.md +148 -0
- package/src/commit/prompts/analysis-user.md +38 -0
- package/src/commit/prompts/changelog-system.md +50 -0
- package/src/commit/prompts/changelog-user.md +18 -0
- package/src/commit/prompts/file-observer-system.md +24 -0
- package/src/commit/prompts/file-observer-user.md +8 -0
- package/src/commit/prompts/reduce-system.md +50 -0
- package/src/commit/prompts/reduce-user.md +17 -0
- package/src/commit/prompts/summary-retry.md +3 -0
- package/src/commit/prompts/summary-system.md +38 -0
- package/src/commit/prompts/summary-user.md +13 -0
- package/src/commit/prompts/types-description.md +2 -0
- package/src/commit/shared-llm.ts +70 -0
- package/src/commit/types.ts +118 -0
- package/src/commit/utils/exclusions.ts +42 -0
- package/src/commit/utils.ts +58 -0
- package/src/config/api-key-resolver.ts +81 -0
- package/src/config/append-only-context-mode.ts +76 -0
- package/src/config/config-file.ts +347 -0
- package/src/config/inline-tool-descriptors-mode.ts +26 -0
- package/src/config/keybindings.ts +703 -0
- package/src/config/mcp-schema.json +252 -0
- package/src/config/model-discovery.ts +1077 -0
- package/src/config/model-registry.ts +2822 -0
- package/src/config/model-resolver.ts +2115 -0
- package/src/config/model-roles.ts +113 -0
- package/src/config/models-config-schema-bundle.ts +315 -0
- package/src/config/models-config-schema.ts +14 -0
- package/src/config/models-config.ts +130 -0
- package/src/config/prompt-templates.ts +205 -0
- package/src/config/provider-globals.ts +25 -0
- package/src/config/resolve-config-value.ts +95 -0
- package/src/config/service-tier.ts +146 -0
- package/src/config/settings-schema.ts +5887 -0
- package/src/config/settings.ts +2394 -0
- package/src/config.ts +242 -0
- package/src/cursor-bridge-tools.ts +81 -0
- package/src/cursor.ts +960 -0
- package/src/dap/client.ts +1043 -0
- package/src/dap/config.ts +480 -0
- package/src/dap/defaults.json +212 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1841 -0
- package/src/dap/types.ts +611 -0
- package/src/debug/index.ts +584 -0
- package/src/debug/log-formatting.ts +58 -0
- package/src/debug/log-viewer.ts +966 -0
- package/src/debug/profiler.ts +168 -0
- package/src/debug/protocol-probe.ts +267 -0
- package/src/debug/raw-sse-buffer.ts +421 -0
- package/src/debug/raw-sse.ts +312 -0
- package/src/debug/remote-debugger.ts +151 -0
- package/src/debug/report-bundle.ts +411 -0
- package/src/debug/system-info.ts +111 -0
- package/src/debug/terminal-info.ts +124 -0
- package/src/discovery/agent-plugin-format.ts +551 -0
- package/src/discovery/agent-plugins.ts +341 -0
- package/src/discovery/agents-md.ts +67 -0
- package/src/discovery/agents.ts +300 -0
- package/src/discovery/at-imports.ts +273 -0
- package/src/discovery/builtin-defaults.ts +39 -0
- package/src/discovery/builtin-rules/go-add-cleanup.md +33 -0
- package/src/discovery/builtin-rules/go-bench-loop.md +36 -0
- package/src/discovery/builtin-rules/go-exp-promoted.md +40 -0
- package/src/discovery/builtin-rules/go-ioutil.md +37 -0
- package/src/discovery/builtin-rules/go-join-hostport.md +30 -0
- package/src/discovery/builtin-rules/go-new-expr.md +44 -0
- package/src/discovery/builtin-rules/go-rand-v2.md +41 -0
- package/src/discovery/builtin-rules/go-range-int.md +45 -0
- package/src/discovery/builtin-rules/index.ts +74 -0
- package/src/discovery/builtin-rules/rs-box-leak.md +49 -0
- package/src/discovery/builtin-rules/rs-future-prelude.md +24 -0
- package/src/discovery/builtin-rules/rs-lazylock.md +52 -0
- package/src/discovery/builtin-rules/rs-match-ergonomics.md +68 -0
- package/src/discovery/builtin-rules/rs-parking-lot.md +45 -0
- package/src/discovery/builtin-rules/rs-result-type.md +20 -0
- package/src/discovery/builtin-rules/ts-bare-catch.md +39 -0
- package/src/discovery/builtin-rules/ts-import-type.md +43 -0
- package/src/discovery/builtin-rules/ts-no-any.md +66 -0
- package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +45 -0
- package/src/discovery/builtin-rules/ts-no-dynamic-import.md +40 -0
- package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +56 -0
- package/src/discovery/builtin-rules/ts-no-local-is-record.md +48 -0
- package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
- package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
- package/src/discovery/builtin-rules/ts-no-tiny-functions.md +51 -0
- package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +66 -0
- package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
- package/src/discovery/builtin-rules/ts-set-map.md +28 -0
- package/src/discovery/builtin.ts +945 -0
- package/src/discovery/claude-plugins.ts +640 -0
- package/src/discovery/claude.ts +591 -0
- package/src/discovery/cline.ts +83 -0
- package/src/discovery/codex.ts +553 -0
- package/src/discovery/contained-path.ts +78 -0
- package/src/discovery/cursor.ts +223 -0
- package/src/discovery/gemini.ts +386 -0
- package/src/discovery/github.ts +337 -0
- package/src/discovery/helpers.ts +1183 -0
- package/src/discovery/index.ts +82 -0
- package/src/discovery/mcp-json.ts +182 -0
- package/src/discovery/omp-extension-roots.ts +272 -0
- package/src/discovery/omp-plugins.ts +409 -0
- package/src/discovery/opencode.ts +441 -0
- package/src/discovery/plugin-dir-roots.ts +28 -0
- package/src/discovery/ssh.ts +153 -0
- package/src/discovery/substitute-plugin-root.ts +77 -0
- package/src/discovery/vscode.ts +106 -0
- package/src/discovery/windsurf.ts +149 -0
- package/src/edit/apply-patch/index.ts +87 -0
- package/src/edit/apply-patch/parser.ts +174 -0
- package/src/edit/diff.ts +1008 -0
- package/src/edit/edit-clipboard.ts +23 -0
- package/src/edit/file-snapshot-store.ts +148 -0
- package/src/edit/hashline/block-resolver.ts +33 -0
- package/src/edit/hashline/diff.ts +402 -0
- package/src/edit/hashline/execute.ts +313 -0
- package/src/edit/hashline/filesystem.ts +247 -0
- package/src/edit/hashline/index.ts +5 -0
- package/src/edit/hashline/noop-loop-guard.ts +99 -0
- package/src/edit/hashline/params.ts +12 -0
- package/src/edit/index.ts +699 -0
- package/src/edit/modes/apply-patch.lark +19 -0
- package/src/edit/modes/apply-patch.ts +53 -0
- package/src/edit/modes/patch.ts +1958 -0
- package/src/edit/modes/replace.ts +1191 -0
- package/src/edit/normalize.ts +345 -0
- package/src/edit/notebook.ts +242 -0
- package/src/edit/read-file.ts +25 -0
- package/src/edit/renderer.ts +998 -0
- package/src/edit/snapshot-details.ts +77 -0
- package/src/edit/streaming.ts +710 -0
- package/src/eval/agent-bridge.ts +224 -0
- package/src/eval/backend-helpers.ts +48 -0
- package/src/eval/backend.ts +71 -0
- package/src/eval/bridge-timeout.ts +65 -0
- package/src/eval/budget-bridge.ts +48 -0
- package/src/eval/completion-bridge.ts +212 -0
- package/src/eval/concurrency-bridge.ts +34 -0
- package/src/eval/executor-base.ts +562 -0
- package/src/eval/idle-timeout.ts +91 -0
- package/src/eval/index.ts +6 -0
- package/src/eval/jl/executor.ts +551 -0
- package/src/eval/jl/index.ts +54 -0
- package/src/eval/jl/kernel.ts +236 -0
- package/src/eval/jl/prelude.jl +736 -0
- package/src/eval/jl/prelude.ts +3 -0
- package/src/eval/jl/runner.jl +666 -0
- package/src/eval/jl/runtime.ts +118 -0
- package/src/eval/js/context-manager.ts +872 -0
- package/src/eval/js/executor.ts +178 -0
- package/src/eval/js/index.ts +41 -0
- package/src/eval/js/process-entry.ts +31 -0
- package/src/eval/js/shared/helpers.ts +170 -0
- package/src/eval/js/shared/indirect-eval.ts +30 -0
- package/src/eval/js/shared/local-module-loader.ts +364 -0
- package/src/eval/js/shared/prelude.ts +2 -0
- package/src/eval/js/shared/prelude.txt +294 -0
- package/src/eval/js/shared/rewrite-imports.ts +550 -0
- package/src/eval/js/shared/runtime.ts +590 -0
- package/src/eval/js/shared/types.ts +18 -0
- package/src/eval/js/tool-bridge.ts +163 -0
- package/src/eval/js/worker-core.ts +380 -0
- package/src/eval/js/worker-entry.ts +37 -0
- package/src/eval/js/worker-protocol.ts +47 -0
- package/src/eval/kernel-base.ts +569 -0
- package/src/eval/py/display.ts +71 -0
- package/src/eval/py/executor.ts +643 -0
- package/src/eval/py/index.ts +57 -0
- package/src/eval/py/kernel.ts +235 -0
- package/src/eval/py/prelude.py +673 -0
- package/src/eval/py/prelude.ts +3 -0
- package/src/eval/py/runner.py +1425 -0
- package/src/eval/py/runtime.ts +276 -0
- package/src/eval/py/spawn-options.ts +134 -0
- package/src/eval/py/tool-bridge.ts +201 -0
- package/src/eval/rb/executor.ts +511 -0
- package/src/eval/rb/index.ts +54 -0
- package/src/eval/rb/kernel.ts +231 -0
- package/src/eval/rb/prelude.rb +551 -0
- package/src/eval/rb/prelude.ts +3 -0
- package/src/eval/rb/runner.rb +581 -0
- package/src/eval/rb/runtime.ts +132 -0
- package/src/eval/runtime-env.ts +104 -0
- package/src/eval/session-id.ts +8 -0
- package/src/eval/types.ts +48 -0
- package/src/exa/index.ts +2 -0
- package/src/exa/mcp-client.ts +370 -0
- package/src/exa/types.ts +69 -0
- package/src/exec/bash-executor.ts +627 -0
- package/src/exec/direnv.ts +145 -0
- package/src/exec/exec.ts +53 -0
- package/src/exec/non-interactive-env.ts +118 -0
- package/src/export/custom-share.ts +65 -0
- package/src/export/html/args.ts +20 -0
- package/src/export/html/index.ts +320 -0
- package/src/export/html/share-loader.js +102 -0
- package/src/export/html/template.css +1355 -0
- package/src/export/html/template.html +55 -0
- package/src/export/html/template.js +1653 -0
- package/src/export/html/tool-views.generated.js +35 -0
- package/src/export/html/vendor/highlight.min.js +1213 -0
- package/src/export/html/vendor/marked.min.js +6 -0
- package/src/export/html/web-palette.ts +126 -0
- package/src/export/share.ts +686 -0
- package/src/export/ttsr.ts +590 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +54 -0
- package/src/extensibility/custom-commands/bundled/review/index.ts +698 -0
- package/src/extensibility/custom-commands/index.ts +2 -0
- package/src/extensibility/custom-commands/loader.ts +242 -0
- package/src/extensibility/custom-commands/types.ts +119 -0
- package/src/extensibility/custom-tools/index.ts +7 -0
- package/src/extensibility/custom-tools/loader.ts +301 -0
- package/src/extensibility/custom-tools/types.ts +287 -0
- package/src/extensibility/custom-tools/wrapper.ts +50 -0
- package/src/extensibility/extensions/compact-handler.ts +40 -0
- package/src/extensibility/extensions/get-commands-handler.ts +78 -0
- package/src/extensibility/extensions/index.ts +16 -0
- package/src/extensibility/extensions/load-errors.ts +13 -0
- package/src/extensibility/extensions/loader.ts +663 -0
- package/src/extensibility/extensions/managed-timers.ts +83 -0
- package/src/extensibility/extensions/model-api.ts +39 -0
- package/src/extensibility/extensions/runner.ts +1396 -0
- package/src/extensibility/extensions/types.ts +1603 -0
- package/src/extensibility/extensions/wrapper.ts +395 -0
- package/src/extensibility/hooks/index.ts +6 -0
- package/src/extensibility/hooks/loader.ts +243 -0
- package/src/extensibility/hooks/runner.ts +425 -0
- package/src/extensibility/hooks/tool-wrapper.ts +124 -0
- package/src/extensibility/hooks/types.ts +612 -0
- package/src/extensibility/legacy-pi-ai-shim.ts +161 -0
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +1460 -0
- package/src/extensibility/legacy-pi-tui-shim.ts +43 -0
- package/src/extensibility/legacy-typebox.ts +101 -0
- package/src/extensibility/plugins/bun-git-cache.ts +91 -0
- package/src/extensibility/plugins/doctor.ts +65 -0
- package/src/extensibility/plugins/git-url.ts +367 -0
- package/src/extensibility/plugins/index.ts +9 -0
- package/src/extensibility/plugins/installer.ts +201 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +2643 -0
- package/src/extensibility/plugins/legacy-pi-virtual-modules.d.ts +4 -0
- package/src/extensibility/plugins/loader.ts +535 -0
- package/src/extensibility/plugins/manager.ts +1142 -0
- package/src/extensibility/plugins/marketplace/cache.ts +136 -0
- package/src/extensibility/plugins/marketplace/fetcher.ts +316 -0
- package/src/extensibility/plugins/marketplace/index.ts +6 -0
- package/src/extensibility/plugins/marketplace/manager.ts +927 -0
- package/src/extensibility/plugins/marketplace/registry.ts +192 -0
- package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
- package/src/extensibility/plugins/marketplace/types.ts +192 -0
- package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
- package/src/extensibility/plugins/parser.ts +107 -0
- package/src/extensibility/plugins/runtime-config.ts +9 -0
- package/src/extensibility/plugins/types.ts +194 -0
- package/src/extensibility/session-handler-types.ts +21 -0
- package/src/extensibility/shared-events.ts +401 -0
- package/src/extensibility/skills.ts +531 -0
- package/src/extensibility/slash-commands.ts +131 -0
- package/src/extensibility/tool-event-input.ts +80 -0
- package/src/extensibility/tool-proxy.ts +28 -0
- package/src/extensibility/utils.ts +192 -0
- package/src/goals/index.ts +3 -0
- package/src/goals/runtime.ts +521 -0
- package/src/goals/state.ts +37 -0
- package/src/goals/tools/goal-tool.ts +251 -0
- package/src/hindsight/backend.ts +354 -0
- package/src/hindsight/bank.ts +156 -0
- package/src/hindsight/client.ts +680 -0
- package/src/hindsight/config.ts +193 -0
- package/src/hindsight/content.ts +266 -0
- package/src/hindsight/index.ts +8 -0
- package/src/hindsight/mental-models.ts +429 -0
- package/src/hindsight/seeds.json +32 -0
- package/src/hindsight/state.ts +530 -0
- package/src/hindsight/transcript.ts +71 -0
- package/src/index.ts +66 -0
- package/src/internal-urls/agent-protocol.ts +180 -0
- package/src/internal-urls/artifact-protocol.ts +151 -0
- package/src/internal-urls/docs-index.ts +102 -0
- package/src/internal-urls/filesystem-resource.ts +34 -0
- package/src/internal-urls/history-protocol.ts +198 -0
- package/src/internal-urls/index.ts +28 -0
- package/src/internal-urls/issue-pr-protocol.ts +594 -0
- package/src/internal-urls/json-query.ts +126 -0
- package/src/internal-urls/local-protocol.ts +471 -0
- package/src/internal-urls/mcp-protocol.ts +168 -0
- package/src/internal-urls/memory-protocol.ts +380 -0
- package/src/internal-urls/omp-protocol.ts +94 -0
- package/src/internal-urls/parse.ts +103 -0
- package/src/internal-urls/registry-helpers.ts +132 -0
- package/src/internal-urls/router.ts +153 -0
- package/src/internal-urls/rule-protocol.ts +45 -0
- package/src/internal-urls/security-protocol.ts +261 -0
- package/src/internal-urls/skill-protocol.ts +131 -0
- package/src/internal-urls/ssh-protocol.ts +368 -0
- package/src/internal-urls/types.ts +196 -0
- package/src/internal-urls/vault-protocol.ts +940 -0
- package/src/internal-urls/xd-protocol.ts +46 -0
- package/src/irc/bus.ts +380 -0
- package/src/jsonrpc/message-framing.ts +142 -0
- package/src/launch/broker.ts +1368 -0
- package/src/launch/client.ts +521 -0
- package/src/launch/ensure.ts +73 -0
- package/src/launch/paths.ts +14 -0
- package/src/launch/presence.ts +82 -0
- package/src/launch/protocol.ts +448 -0
- package/src/launch/spawn-options.ts +17 -0
- package/src/launch/terminal-output-worker-client.ts +53 -0
- package/src/launch/terminal-output-worker-protocol.ts +11 -0
- package/src/launch/terminal-output-worker.ts +23 -0
- package/src/launch/terminal-output.ts +46 -0
- package/src/lib/xai-http.ts +150 -0
- package/src/live/attestation.ts +91 -0
- package/src/live/controller.ts +517 -0
- package/src/live/prompts/agent-final-message.md +3 -0
- package/src/live/prompts/live-instructions.md +23 -0
- package/src/live/protocol.ts +233 -0
- package/src/live/transport.ts +422 -0
- package/src/live/visualizer.ts +221 -0
- package/src/live/voices.ts +18 -0
- package/src/lsp/client.ts +1465 -0
- package/src/lsp/clients/biome-client.ts +263 -0
- package/src/lsp/clients/index.ts +50 -0
- package/src/lsp/clients/lsp-linter-client.ts +85 -0
- package/src/lsp/clients/swiftlint-client.ts +120 -0
- package/src/lsp/config.ts +549 -0
- package/src/lsp/defaults.json +499 -0
- package/src/lsp/deferred-diagnostics.ts +66 -0
- package/src/lsp/diagnostics-ledger.ts +51 -0
- package/src/lsp/edits.ts +288 -0
- package/src/lsp/format-options.ts +119 -0
- package/src/lsp/index.ts +2821 -0
- package/src/lsp/lspmux.ts +233 -0
- package/src/lsp/mux/daemon.ts +348 -0
- package/src/lsp/mux/protocol.ts +96 -0
- package/src/lsp/mux/server.ts +797 -0
- package/src/lsp/render.ts +668 -0
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +479 -0
- package/src/lsp/utils.ts +747 -0
- package/src/main.ts +1799 -0
- package/src/markit/NOTICE +32 -0
- package/src/markit/converters/docx.ts +56 -0
- package/src/markit/converters/epub.ts +136 -0
- package/src/markit/converters/pdf/columns.ts +103 -0
- package/src/markit/converters/pdf/extract.ts +598 -0
- package/src/markit/converters/pdf/grid.ts +780 -0
- package/src/markit/converters/pdf/headers.ts +106 -0
- package/src/markit/converters/pdf/index.ts +146 -0
- package/src/markit/converters/pdf/render.ts +501 -0
- package/src/markit/converters/pdf/types.ts +84 -0
- package/src/markit/converters/pptx.ts +325 -0
- package/src/markit/converters/xlsx.ts +173 -0
- package/src/markit/index.ts +2 -0
- package/src/markit/registry.ts +59 -0
- package/src/markit/types.ts +35 -0
- package/src/mcp/client.ts +511 -0
- package/src/mcp/config-writer.ts +377 -0
- package/src/mcp/config.ts +385 -0
- package/src/mcp/index.ts +29 -0
- package/src/mcp/json-rpc.ts +122 -0
- package/src/mcp/loader.ts +125 -0
- package/src/mcp/manager.ts +1524 -0
- package/src/mcp/oauth-credentials.ts +104 -0
- package/src/mcp/oauth-discovery.ts +587 -0
- package/src/mcp/oauth-flow.ts +830 -0
- package/src/mcp/render.ts +214 -0
- package/src/mcp/request-id.ts +24 -0
- package/src/mcp/smithery-auth.ts +108 -0
- package/src/mcp/smithery-connect.ts +154 -0
- package/src/mcp/smithery-registry.ts +500 -0
- package/src/mcp/startup-events.ts +116 -0
- package/src/mcp/timeout.ts +59 -0
- package/src/mcp/tool-bridge.ts +691 -0
- package/src/mcp/tool-cache.ts +117 -0
- package/src/mcp/transports/header-policy.ts +95 -0
- package/src/mcp/transports/http.ts +508 -0
- package/src/mcp/transports/index.ts +7 -0
- package/src/mcp/transports/sse.ts +372 -0
- package/src/mcp/transports/stdio.ts +905 -0
- package/src/mcp/types.ts +467 -0
- package/src/memories/index.ts +1434 -0
- package/src/memories/storage.ts +578 -0
- package/src/memory-backend/index.ts +19 -0
- package/src/memory-backend/local-backend.ts +47 -0
- package/src/memory-backend/messages.ts +19 -0
- package/src/memory-backend/off-backend.ts +25 -0
- package/src/memory-backend/resolve.ts +25 -0
- package/src/memory-backend/runtime.ts +66 -0
- package/src/memory-backend/tool-names.ts +2 -0
- package/src/memory-backend/types.ts +166 -0
- package/src/mnemopi/backend.ts +629 -0
- package/src/mnemopi/config.ts +267 -0
- package/src/mnemopi/embed-client.ts +293 -0
- package/src/mnemopi/embed-protocol.ts +35 -0
- package/src/mnemopi/embed-worker.ts +114 -0
- package/src/mnemopi/index.ts +3 -0
- package/src/mnemopi/state.ts +940 -0
- package/src/modes/acp/acp-agent.ts +2595 -0
- package/src/modes/acp/acp-client-bridge.ts +154 -0
- package/src/modes/acp/acp-event-mapper.ts +1084 -0
- package/src/modes/acp/acp-mode.ts +48 -0
- package/src/modes/acp/index.ts +2 -0
- package/src/modes/acp/terminal-auth.ts +37 -0
- package/src/modes/components/advisor-config.ts +635 -0
- package/src/modes/components/advisor-message.ts +109 -0
- package/src/modes/components/agent-dashboard.ts +1252 -0
- package/src/modes/components/agent-hub-projection.ts +248 -0
- package/src/modes/components/agent-hub-renderer.ts +194 -0
- package/src/modes/components/agent-hub.ts +1126 -0
- package/src/modes/components/agent-transcript-viewer.ts +647 -0
- package/src/modes/components/ask-dialog.ts +1017 -0
- package/src/modes/components/assistant-message.ts +978 -0
- package/src/modes/components/background-tan-message.ts +36 -0
- package/src/modes/components/bash-execution.ts +233 -0
- package/src/modes/components/bordered-loader.ts +41 -0
- package/src/modes/components/btw-panel.ts +161 -0
- package/src/modes/components/cache-invalidation-marker.ts +110 -0
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/chat-transcript-builder.ts +518 -0
- package/src/modes/components/codex-reset-fireworks.ts +369 -0
- package/src/modes/components/collab-prompt-message.ts +32 -0
- package/src/modes/components/compaction-summary-message.ts +221 -0
- package/src/modes/components/copy-selector.ts +218 -0
- package/src/modes/components/countdown-timer.ts +75 -0
- package/src/modes/components/custom-editor.ts +1036 -0
- package/src/modes/components/custom-message.ts +70 -0
- package/src/modes/components/diff.ts +254 -0
- package/src/modes/components/dynamic-border.ts +37 -0
- package/src/modes/components/error-banner.ts +33 -0
- package/src/modes/components/eval-execution.ts +169 -0
- package/src/modes/components/execution-shared.ts +101 -0
- package/src/modes/components/extensions/extension-dashboard.ts +492 -0
- package/src/modes/components/extensions/extension-list.ts +507 -0
- package/src/modes/components/extensions/index.ts +9 -0
- package/src/modes/components/extensions/inspector-panel.ts +326 -0
- package/src/modes/components/extensions/state-manager.ts +648 -0
- package/src/modes/components/extensions/types.ts +186 -0
- package/src/modes/components/footer.ts +274 -0
- package/src/modes/components/history-search.ts +268 -0
- package/src/modes/components/hook-editor.ts +213 -0
- package/src/modes/components/hook-input.ts +87 -0
- package/src/modes/components/hook-message.ts +67 -0
- package/src/modes/components/hook-selector.ts +691 -0
- package/src/modes/components/index.ts +42 -0
- package/src/modes/components/keybinding-hints.ts +56 -0
- package/src/modes/components/late-diagnostics-message.ts +60 -0
- package/src/modes/components/login-dialog.ts +197 -0
- package/src/modes/components/logout-account-selector.ts +130 -0
- package/src/modes/components/mcp-add-wizard.ts +1413 -0
- package/src/modes/components/message-frame.ts +98 -0
- package/src/modes/components/model-browser.ts +888 -0
- package/src/modes/components/model-hub.ts +2014 -0
- package/src/modes/components/model-picker.ts +237 -0
- package/src/modes/components/move-overlay.ts +293 -0
- package/src/modes/components/oauth-selector.ts +474 -0
- package/src/modes/components/omfg-panel.ts +141 -0
- package/src/modes/components/overlay-box.ts +109 -0
- package/src/modes/components/pause-screen.ts +208 -0
- package/src/modes/components/plan-review-overlay.ts +1226 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/plugin-selector.ts +100 -0
- package/src/modes/components/plugin-settings.ts +745 -0
- package/src/modes/components/queue-mode-selector.ts +61 -0
- package/src/modes/components/read-tool-group.ts +856 -0
- package/src/modes/components/reset-usage-selector.ts +161 -0
- package/src/modes/components/segment-track.ts +89 -0
- package/src/modes/components/select-list-mouse-routing.ts +35 -0
- package/src/modes/components/selector-helpers.ts +129 -0
- package/src/modes/components/session-account-selector.ts +62 -0
- package/src/modes/components/session-selector.ts +1035 -0
- package/src/modes/components/settings-defs.ts +267 -0
- package/src/modes/components/settings-selector.ts +1445 -0
- package/src/modes/components/show-images-selector.ts +50 -0
- package/src/modes/components/skill-message.ts +110 -0
- package/src/modes/components/snapcompact-shape-preview-doc.md +14 -0
- package/src/modes/components/snapcompact-shape-preview.ts +192 -0
- package/src/modes/components/status-line/component.ts +1843 -0
- package/src/modes/components/status-line/context-thresholds.ts +86 -0
- package/src/modes/components/status-line/git-utils.ts +42 -0
- package/src/modes/components/status-line/index.ts +5 -0
- package/src/modes/components/status-line/presets.ts +106 -0
- package/src/modes/components/status-line/segments.ts +710 -0
- package/src/modes/components/status-line/separators.ts +55 -0
- package/src/modes/components/status-line/types.ts +159 -0
- package/src/modes/components/stripped-tool-calls-placeholder.ts +35 -0
- package/src/modes/components/theme-selector.ts +68 -0
- package/src/modes/components/thinking-selector.ts +57 -0
- package/src/modes/components/tiny-title-download-progress.ts +90 -0
- package/src/modes/components/tips.txt +26 -0
- package/src/modes/components/todo-reminder.ts +43 -0
- package/src/modes/components/tool-execution.ts +1467 -0
- package/src/modes/components/transcript-container.ts +527 -0
- package/src/modes/components/tree-selector.ts +1006 -0
- package/src/modes/components/ttsr-notification.ts +123 -0
- package/src/modes/components/usage-row.ts +52 -0
- package/src/modes/components/user-message-selector.ts +227 -0
- package/src/modes/components/user-message.ts +167 -0
- package/src/modes/components/visual-truncate.ts +63 -0
- package/src/modes/components/welcome.ts +578 -0
- package/src/modes/controllers/btw-controller.ts +246 -0
- package/src/modes/controllers/command-controller-shared.ts +109 -0
- package/src/modes/controllers/command-controller.ts +2024 -0
- package/src/modes/controllers/event-controller.ts +2173 -0
- package/src/modes/controllers/extension-ui-controller.ts +1243 -0
- package/src/modes/controllers/input-controller.ts +2037 -0
- package/src/modes/controllers/live-command-controller.ts +261 -0
- package/src/modes/controllers/mcp-command-controller.ts +2552 -0
- package/src/modes/controllers/omfg-controller.ts +287 -0
- package/src/modes/controllers/omfg-rule.ts +647 -0
- package/src/modes/controllers/selector-controller.ts +2058 -0
- package/src/modes/controllers/session-focus-controller.ts +117 -0
- package/src/modes/controllers/ssh-command-controller.ts +385 -0
- package/src/modes/controllers/streaming-reveal.ts +399 -0
- package/src/modes/controllers/tan-command-controller.ts +243 -0
- package/src/modes/controllers/todo-command-controller.ts +487 -0
- package/src/modes/controllers/tool-args-reveal.ts +591 -0
- package/src/modes/data/emojis.json +1 -0
- package/src/modes/emoji-autocomplete.ts +285 -0
- package/src/modes/github-ref-autocomplete.ts +75 -0
- package/src/modes/gradient-highlight.ts +99 -0
- package/src/modes/image-references.ts +137 -0
- package/src/modes/index.ts +10 -0
- package/src/modes/interactive-mode.ts +5055 -0
- package/src/modes/internal-url-autocomplete.ts +158 -0
- package/src/modes/loop-limit.ts +192 -0
- package/src/modes/magic-keyword-boundary.ts +23 -0
- package/src/modes/magic-keywords.ts +42 -0
- package/src/modes/markdown-prose.ts +247 -0
- package/src/modes/oauth-manual-input.ts +69 -0
- package/src/modes/orchestrate.ts +43 -0
- package/src/modes/print-mode.ts +282 -0
- package/src/modes/prompt-action-autocomplete.ts +322 -0
- package/src/modes/queue-input.ts +132 -0
- package/src/modes/rpc/host-tools.ts +204 -0
- package/src/modes/rpc/host-uris.ts +241 -0
- package/src/modes/rpc/rpc-client.ts +1213 -0
- package/src/modes/rpc/rpc-frame.ts +316 -0
- package/src/modes/rpc/rpc-input.ts +38 -0
- package/src/modes/rpc/rpc-messages.ts +127 -0
- package/src/modes/rpc/rpc-mode.ts +1519 -0
- package/src/modes/rpc/rpc-subagents.ts +265 -0
- package/src/modes/rpc/rpc-types.ts +544 -0
- package/src/modes/running-subagent-badge.ts +13 -0
- package/src/modes/runtime-init.ts +144 -0
- package/src/modes/session-observer-registry.ts +223 -0
- package/src/modes/session-teardown.ts +82 -0
- package/src/modes/setup-version.ts +11 -0
- package/src/modes/setup-wizard/index.ts +103 -0
- package/src/modes/setup-wizard/lazy.ts +16 -0
- package/src/modes/setup-wizard/scenes/glyph.ts +103 -0
- package/src/modes/setup-wizard/scenes/model.ts +132 -0
- package/src/modes/setup-wizard/scenes/outro.ts +35 -0
- package/src/modes/setup-wizard/scenes/providers.ts +105 -0
- package/src/modes/setup-wizard/scenes/sign-in.ts +312 -0
- package/src/modes/setup-wizard/scenes/splash.ts +201 -0
- package/src/modes/setup-wizard/scenes/theme.ts +330 -0
- package/src/modes/setup-wizard/scenes/types.ts +65 -0
- package/src/modes/setup-wizard/scenes/web-search.ts +153 -0
- package/src/modes/setup-wizard/startup-splash.ts +107 -0
- package/src/modes/setup-wizard/wizard-overlay.ts +335 -0
- package/src/modes/shared.ts +49 -0
- package/src/modes/skill-command.ts +91 -0
- package/src/modes/theme/dark.json +95 -0
- package/src/modes/theme/defaults/alabaster.json +93 -0
- package/src/modes/theme/defaults/amethyst.json +96 -0
- package/src/modes/theme/defaults/anthracite.json +93 -0
- package/src/modes/theme/defaults/basalt.json +91 -0
- package/src/modes/theme/defaults/birch.json +95 -0
- package/src/modes/theme/defaults/dark-abyss.json +91 -0
- package/src/modes/theme/defaults/dark-arctic.json +104 -0
- package/src/modes/theme/defaults/dark-aurora.json +95 -0
- package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
- package/src/modes/theme/defaults/dark-cavern.json +91 -0
- package/src/modes/theme/defaults/dark-copper.json +95 -0
- package/src/modes/theme/defaults/dark-cosmos.json +90 -0
- package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
- package/src/modes/theme/defaults/dark-dracula.json +98 -0
- package/src/modes/theme/defaults/dark-eclipse.json +91 -0
- package/src/modes/theme/defaults/dark-ember.json +95 -0
- package/src/modes/theme/defaults/dark-equinox.json +90 -0
- package/src/modes/theme/defaults/dark-forest.json +96 -0
- package/src/modes/theme/defaults/dark-github.json +105 -0
- package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
- package/src/modes/theme/defaults/dark-lavender.json +95 -0
- package/src/modes/theme/defaults/dark-lunar.json +89 -0
- package/src/modes/theme/defaults/dark-midnight.json +95 -0
- package/src/modes/theme/defaults/dark-monochrome.json +94 -0
- package/src/modes/theme/defaults/dark-monokai.json +98 -0
- package/src/modes/theme/defaults/dark-nebula.json +90 -0
- package/src/modes/theme/defaults/dark-nord.json +97 -0
- package/src/modes/theme/defaults/dark-ocean.json +101 -0
- package/src/modes/theme/defaults/dark-one.json +100 -0
- package/src/modes/theme/defaults/dark-poimandres.json +143 -0
- package/src/modes/theme/defaults/dark-rainforest.json +91 -0
- package/src/modes/theme/defaults/dark-reef.json +91 -0
- package/src/modes/theme/defaults/dark-retro.json +92 -0
- package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
- package/src/modes/theme/defaults/dark-sakura.json +95 -0
- package/src/modes/theme/defaults/dark-slate.json +95 -0
- package/src/modes/theme/defaults/dark-solarized.json +97 -0
- package/src/modes/theme/defaults/dark-solstice.json +90 -0
- package/src/modes/theme/defaults/dark-starfall.json +91 -0
- package/src/modes/theme/defaults/dark-sunset.json +99 -0
- package/src/modes/theme/defaults/dark-swamp.json +90 -0
- package/src/modes/theme/defaults/dark-synthwave.json +103 -0
- package/src/modes/theme/defaults/dark-taiga.json +91 -0
- package/src/modes/theme/defaults/dark-terminal.json +95 -0
- package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
- package/src/modes/theme/defaults/dark-tundra.json +91 -0
- package/src/modes/theme/defaults/dark-twilight.json +91 -0
- package/src/modes/theme/defaults/dark-volcanic.json +91 -0
- package/src/modes/theme/defaults/graphite.json +92 -0
- package/src/modes/theme/defaults/index.ts +199 -0
- package/src/modes/theme/defaults/light-arctic.json +107 -0
- package/src/modes/theme/defaults/light-aurora-day.json +91 -0
- package/src/modes/theme/defaults/light-canyon.json +91 -0
- package/src/modes/theme/defaults/light-catppuccin.json +106 -0
- package/src/modes/theme/defaults/light-cirrus.json +90 -0
- package/src/modes/theme/defaults/light-coral.json +95 -0
- package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
- package/src/modes/theme/defaults/light-dawn.json +90 -0
- package/src/modes/theme/defaults/light-dunes.json +91 -0
- package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
- package/src/modes/theme/defaults/light-forest.json +100 -0
- package/src/modes/theme/defaults/light-frost.json +95 -0
- package/src/modes/theme/defaults/light-github.json +115 -0
- package/src/modes/theme/defaults/light-glacier.json +91 -0
- package/src/modes/theme/defaults/light-gruvbox.json +108 -0
- package/src/modes/theme/defaults/light-haze.json +90 -0
- package/src/modes/theme/defaults/light-honeycomb.json +95 -0
- package/src/modes/theme/defaults/light-lagoon.json +91 -0
- package/src/modes/theme/defaults/light-lavender.json +95 -0
- package/src/modes/theme/defaults/light-meadow.json +91 -0
- package/src/modes/theme/defaults/light-mint.json +95 -0
- package/src/modes/theme/defaults/light-monochrome.json +101 -0
- package/src/modes/theme/defaults/light-ocean.json +99 -0
- package/src/modes/theme/defaults/light-one.json +99 -0
- package/src/modes/theme/defaults/light-opal.json +91 -0
- package/src/modes/theme/defaults/light-orchard.json +91 -0
- package/src/modes/theme/defaults/light-paper.json +95 -0
- package/src/modes/theme/defaults/light-poimandres.json +143 -0
- package/src/modes/theme/defaults/light-prism.json +90 -0
- package/src/modes/theme/defaults/light-retro.json +98 -0
- package/src/modes/theme/defaults/light-sand.json +95 -0
- package/src/modes/theme/defaults/light-savanna.json +91 -0
- package/src/modes/theme/defaults/light-solarized.json +102 -0
- package/src/modes/theme/defaults/light-soleil.json +90 -0
- package/src/modes/theme/defaults/light-sunset.json +99 -0
- package/src/modes/theme/defaults/light-synthwave.json +98 -0
- package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
- package/src/modes/theme/defaults/light-wetland.json +91 -0
- package/src/modes/theme/defaults/light-zenith.json +89 -0
- package/src/modes/theme/defaults/limestone.json +94 -0
- package/src/modes/theme/defaults/mahogany.json +97 -0
- package/src/modes/theme/defaults/marble.json +93 -0
- package/src/modes/theme/defaults/obsidian.json +91 -0
- package/src/modes/theme/defaults/onyx.json +91 -0
- package/src/modes/theme/defaults/pearl.json +93 -0
- package/src/modes/theme/defaults/porcelain.json +91 -0
- package/src/modes/theme/defaults/quartz.json +96 -0
- package/src/modes/theme/defaults/sandstone.json +95 -0
- package/src/modes/theme/defaults/titanium.json +90 -0
- package/src/modes/theme/light.json +93 -0
- package/src/modes/theme/mermaid-cache.ts +92 -0
- package/src/modes/theme/shimmer.ts +305 -0
- package/src/modes/theme/theme-schema.json +463 -0
- package/src/modes/theme/theme.ts +3171 -0
- package/src/modes/turn-budget.ts +31 -0
- package/src/modes/types.ts +494 -0
- package/src/modes/ultrathink.ts +42 -0
- package/src/modes/utils/context-usage.ts +518 -0
- package/src/modes/utils/copy-targets.ts +378 -0
- package/src/modes/utils/hotkeys-markdown.ts +65 -0
- package/src/modes/utils/interactive-context-helpers.ts +31 -0
- package/src/modes/utils/keybinding-matchers.ts +86 -0
- package/src/modes/utils/tools-markdown.ts +31 -0
- package/src/modes/utils/transcript-render-helpers.ts +253 -0
- package/src/modes/utils/ui-helpers.ts +976 -0
- package/src/modes/warp-events.ts +232 -0
- package/src/modes/workflow.ts +55 -0
- package/src/plan-mode/approved-plan.ts +194 -0
- package/src/plan-mode/model-transition.ts +51 -0
- package/src/plan-mode/plan-files.ts +40 -0
- package/src/plan-mode/plan-handoff.ts +37 -0
- package/src/plan-mode/plan-protection.ts +31 -0
- package/src/plan-mode/state.ts +6 -0
- package/src/priority.json +60 -0
- package/src/prompts/advisor/active-repo-watchdog.md +6 -0
- package/src/prompts/advisor/advise-tool.md +3 -0
- package/src/prompts/advisor/context-files.md +8 -0
- package/src/prompts/advisor/system.md +98 -0
- package/src/prompts/agents/designer.md +74 -0
- package/src/prompts/agents/frontmatter.md +12 -0
- package/src/prompts/agents/init.md +33 -0
- package/src/prompts/agents/librarian.md +119 -0
- package/src/prompts/agents/reviewer.md +139 -0
- package/src/prompts/agents/scout.md +58 -0
- package/src/prompts/agents/security-reviewer.md +75 -0
- package/src/prompts/agents/task.md +17 -0
- package/src/prompts/bench/cache-prefix-chunk.md +1 -0
- package/src/prompts/bench/cache-prefix.md +3 -0
- package/src/prompts/bench/cache-suffix.md +1 -0
- package/src/prompts/bench.md +6 -0
- package/src/prompts/ci-green-request.md +36 -0
- package/src/prompts/dry-balance-bench.md +8 -0
- package/src/prompts/goals/goal-budget-limit.md +16 -0
- package/src/prompts/goals/goal-continuation.md +28 -0
- package/src/prompts/goals/goal-mode-active.md +23 -0
- package/src/prompts/goals/goal-mode-context.md +4 -0
- package/src/prompts/goals/goal-todo-context.md +12 -0
- package/src/prompts/goals/guided-goal-interview.md +43 -0
- package/src/prompts/memories/consolidation.md +30 -0
- package/src/prompts/memories/consolidation_system.md +4 -0
- package/src/prompts/memories/read-path.md +17 -0
- package/src/prompts/memories/stage_one_input.md +6 -0
- package/src/prompts/memories/stage_one_system.md +21 -0
- package/src/prompts/review-custom-request.md +21 -0
- package/src/prompts/review-headless-request.md +16 -0
- package/src/prompts/review-request.md +68 -0
- package/src/prompts/security/scan-coordinator.md +7 -0
- package/src/prompts/security/scan-request.md +21 -0
- package/src/prompts/security/validate-request.md +8 -0
- package/src/prompts/session/launch-completion.md +1 -0
- package/src/prompts/skills/autoload.md +8 -0
- package/src/prompts/skills/user-invocation.md +11 -0
- package/src/prompts/steering/parent-irc.md +5 -0
- package/src/prompts/steering/user-interjection.md +6 -0
- package/src/prompts/system/active-repo-context.md +4 -0
- package/src/prompts/system/agent-creation-architect.md +50 -0
- package/src/prompts/system/agent-creation-user.md +6 -0
- package/src/prompts/system/auto-continue.md +1 -0
- package/src/prompts/system/auto-thinking-difficulty-local.md +14 -0
- package/src/prompts/system/auto-thinking-difficulty.md +14 -0
- package/src/prompts/system/autolearn-guidance-learn.md +1 -0
- package/src/prompts/system/autolearn-guidance.md +7 -0
- package/src/prompts/system/autolearn-nudge-autocontinue.md +5 -0
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/btw-user.md +8 -0
- package/src/prompts/system/commit-message-system.md +14 -0
- package/src/prompts/system/computer-safety.md +14 -0
- package/src/prompts/system/custom-system-prompt.md +64 -0
- package/src/prompts/system/eager-task.md +7 -0
- package/src/prompts/system/eager-todo.md +18 -0
- package/src/prompts/system/empty-stop-retry.md +4 -0
- package/src/prompts/system/gemini-tool-call-reminder.md +9 -0
- package/src/prompts/system/interrupted-thinking.md +7 -0
- package/src/prompts/system/irc-autoreply.md +6 -0
- package/src/prompts/system/irc-incoming.md +9 -0
- package/src/prompts/system/manual-continue.md +7 -0
- package/src/prompts/system/mcp-xdev-guidance.md +11 -0
- package/src/prompts/system/memory-consolidation-system.md +8 -0
- package/src/prompts/system/memory-extraction-system.md +26 -0
- package/src/prompts/system/mid-run-todo-nudge.md +3 -0
- package/src/prompts/system/omfg-user.md +50 -0
- package/src/prompts/system/orchestrate-notice.md +40 -0
- package/src/prompts/system/personalities/default.md +18 -0
- package/src/prompts/system/personalities/friendly.md +17 -0
- package/src/prompts/system/personalities/pragmatic.md +15 -0
- package/src/prompts/system/plan-mode-active.md +125 -0
- package/src/prompts/system/plan-mode-approved.md +22 -0
- package/src/prompts/system/plan-mode-compact-instructions.md +17 -0
- package/src/prompts/system/plan-mode-reference.md +10 -0
- package/src/prompts/system/plan-mode-subagent.md +33 -0
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
- package/src/prompts/system/plan-yolo-handoff.md +5 -0
- package/src/prompts/system/prewalk-checklist.md +7 -0
- package/src/prompts/system/prewalk-continue.md +1 -0
- package/src/prompts/system/prewalk-plan.md +13 -0
- package/src/prompts/system/project-prompt.md +61 -0
- package/src/prompts/system/recap-user.md +9 -0
- package/src/prompts/system/resolve-device-reminder.md +3 -0
- package/src/prompts/system/rewind-report.md +6 -0
- package/src/prompts/system/side-channel-no-tools.md +3 -0
- package/src/prompts/system/snapcompact-context-frames-note.md +1 -0
- package/src/prompts/system/snapcompact-context-stub.md +1 -0
- package/src/prompts/system/snapcompact-system-frames-note.md +1 -0
- package/src/prompts/system/snapcompact-system-stub.md +1 -0
- package/src/prompts/system/snapcompact-toolresult-note.md +1 -0
- package/src/prompts/system/speech-rewrite.md +15 -0
- package/src/prompts/system/subagent-async-pending.md +6 -0
- package/src/prompts/system/subagent-system-prompt.md +73 -0
- package/src/prompts/system/subagent-user-prompt.md +3 -0
- package/src/prompts/system/subagent-yield-reminder.md +23 -0
- package/src/prompts/system/system-prompt.md +263 -0
- package/src/prompts/system/tan-context-switch.md +17 -0
- package/src/prompts/system/task-label.md +23 -0
- package/src/prompts/system/thinking-loop-redirect.md +10 -0
- package/src/prompts/system/title-marker-instruction.md +1 -0
- package/src/prompts/system/title-system.md +16 -0
- package/src/prompts/system/tool-call-loop-redirect.md +8 -0
- package/src/prompts/system/ttsr-interrupt.md +7 -0
- package/src/prompts/system/ttsr-tool-reminder.md +5 -0
- package/src/prompts/system/ultrathink-notice.md +3 -0
- package/src/prompts/system/unexpected-stop-classifier.md +17 -0
- package/src/prompts/system/unexpected-stop-retry.md +4 -0
- package/src/prompts/system/vibe-mode-active.md +26 -0
- package/src/prompts/system/web-search.md +25 -0
- package/src/prompts/system/workflow-notice.md +112 -0
- package/src/prompts/system/xdev-mount-notice.md +20 -0
- package/src/prompts/tools/apply-patch.md +65 -0
- package/src/prompts/tools/ask.md +22 -0
- package/src/prompts/tools/ast-edit.md +11 -0
- package/src/prompts/tools/ast-grep.md +19 -0
- package/src/prompts/tools/async-result.md +8 -0
- package/src/prompts/tools/bash.md +23 -0
- package/src/prompts/tools/browser.md +29 -0
- package/src/prompts/tools/checkpoint.md +15 -0
- package/src/prompts/tools/computer.md +26 -0
- package/src/prompts/tools/debug.md +3 -0
- package/src/prompts/tools/eval.md +45 -0
- package/src/prompts/tools/github.md +22 -0
- package/src/prompts/tools/glob.md +16 -0
- package/src/prompts/tools/goal.md +11 -0
- package/src/prompts/tools/grep.md +13 -0
- package/src/prompts/tools/hub.md +34 -0
- package/src/prompts/tools/image-attachment-describe-system.md +8 -0
- package/src/prompts/tools/image-attachment-describe.md +10 -0
- package/src/prompts/tools/image-gen.md +7 -0
- package/src/prompts/tools/inspect-image-system.md +20 -0
- package/src/prompts/tools/inspect-image.md +22 -0
- package/src/prompts/tools/learn.md +7 -0
- package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
- package/src/prompts/tools/lsp.md +19 -0
- package/src/prompts/tools/manage-skill.md +9 -0
- package/src/prompts/tools/memory-edit.md +12 -0
- package/src/prompts/tools/patch.md +57 -0
- package/src/prompts/tools/read.md +27 -0
- package/src/prompts/tools/recall.md +7 -0
- package/src/prompts/tools/reflect.md +5 -0
- package/src/prompts/tools/replace.md +30 -0
- package/src/prompts/tools/retain.md +6 -0
- package/src/prompts/tools/rewind.md +14 -0
- package/src/prompts/tools/security-publish.md +1 -0
- package/src/prompts/tools/security-scan.md +1 -0
- package/src/prompts/tools/task-async-contract.md +1 -0
- package/src/prompts/tools/task-summary.md +20 -0
- package/src/prompts/tools/task.md +83 -0
- package/src/prompts/tools/todo.md +42 -0
- package/src/prompts/tools/vibe-kill.md +3 -0
- package/src/prompts/tools/vibe-list.md +3 -0
- package/src/prompts/tools/vibe-send.md +9 -0
- package/src/prompts/tools/vibe-spawn.md +10 -0
- package/src/prompts/tools/vibe-turn-result.md +19 -0
- package/src/prompts/tools/vibe-wait.md +8 -0
- package/src/prompts/tools/web-search.md +8 -0
- package/src/prompts/tools/write.md +14 -0
- package/src/registry/agent-lifecycle.ts +505 -0
- package/src/registry/agent-registry.ts +287 -0
- package/src/registry/persisted-agents.ts +423 -0
- package/src/sdk.ts +3820 -0
- package/src/secrets/index.ts +375 -0
- package/src/secrets/obfuscator.ts +2629 -0
- package/src/secrets/regex.ts +21 -0
- package/src/security/auth.ts +98 -0
- package/src/security/cloud.ts +686 -0
- package/src/security/comparison.ts +255 -0
- package/src/security/contracts/ids.ts +111 -0
- package/src/security/contracts/index.ts +4 -0
- package/src/security/contracts/schemas.ts +219 -0
- package/src/security/contracts/types.ts +254 -0
- package/src/security/contracts/validation.ts +69 -0
- package/src/security/coordinator.ts +708 -0
- package/src/security/importers/codex-security.ts +387 -0
- package/src/security/importers/index.ts +2 -0
- package/src/security/importers/sarif.ts +357 -0
- package/src/security/index.ts +13 -0
- package/src/security/preflight.ts +405 -0
- package/src/security/provenance.ts +106 -0
- package/src/security/publication.ts +326 -0
- package/src/security/remediation.ts +93 -0
- package/src/security/resource-output.ts +50 -0
- package/src/security/sarif.ts +78 -0
- package/src/security/store.ts +430 -0
- package/src/session/acp-permission-gate.ts +165 -0
- package/src/session/agent-session-events.ts +67 -0
- package/src/session/agent-session-types.ts +407 -0
- package/src/session/agent-session.ts +9000 -0
- package/src/session/agent-storage.ts +807 -0
- package/src/session/artifacts.ts +154 -0
- package/src/session/async-job-delivery.ts +82 -0
- package/src/session/auth-broker-config.ts +92 -0
- package/src/session/auth-storage.ts +25 -0
- package/src/session/bash-runner.ts +326 -0
- package/src/session/blob-store.ts +295 -0
- package/src/session/checkpoint-entries.ts +81 -0
- package/src/session/claude-session-store.ts +426 -0
- package/src/session/client-bridge.ts +85 -0
- package/src/session/codex-auto-reset.ts +673 -0
- package/src/session/codex-session-store.ts +673 -0
- package/src/session/compact-modes.ts +105 -0
- package/src/session/credential-pin.ts +93 -0
- package/src/session/eval-runner.ts +219 -0
- package/src/session/exit-diagnostics.ts +310 -0
- package/src/session/foreign-session-import.ts +52 -0
- package/src/session/foreign-session-jsonl.ts +29 -0
- package/src/session/foreign-session-store.ts +26 -0
- package/src/session/history-storage.ts +329 -0
- package/src/session/indexed-session-storage.ts +553 -0
- package/src/session/irc-bridge.ts +203 -0
- package/src/session/launch-completion.ts +37 -0
- package/src/session/messages.ts +1304 -0
- package/src/session/model-controls.ts +757 -0
- package/src/session/prewalk.ts +279 -0
- package/src/session/provider-image-budget.ts +86 -0
- package/src/session/queued-messages.ts +99 -0
- package/src/session/redis-session-storage.ts +257 -0
- package/src/session/retry-fallback-chains.ts +455 -0
- package/src/session/role-models.ts +85 -0
- package/src/session/session-advisors.ts +1896 -0
- package/src/session/session-context.ts +584 -0
- package/src/session/session-dump-format.ts +216 -0
- package/src/session/session-entries.ts +306 -0
- package/src/session/session-handoff.ts +325 -0
- package/src/session/session-history-format.ts +459 -0
- package/src/session/session-listing.ts +715 -0
- package/src/session/session-loader.ts +362 -0
- package/src/session/session-maintenance.ts +3115 -0
- package/src/session/session-manager.ts +2747 -0
- package/src/session/session-memory.ts +222 -0
- package/src/session/session-metadata.ts +53 -0
- package/src/session/session-migrations.ts +78 -0
- package/src/session/session-paths.ts +280 -0
- package/src/session/session-persistence.ts +293 -0
- package/src/session/session-provider-boundary.ts +306 -0
- package/src/session/session-stats.ts +349 -0
- package/src/session/session-storage.ts +774 -0
- package/src/session/session-title-slot.ts +141 -0
- package/src/session/session-tools.ts +1385 -0
- package/src/session/session-workspace.ts +53 -0
- package/src/session/settings-stream-fn.ts +79 -0
- package/src/session/shake-types.ts +43 -0
- package/src/session/snapcompact-inline.ts +545 -0
- package/src/session/snapcompact-savings-journal.ts +113 -0
- package/src/session/sql-session-storage.ts +374 -0
- package/src/session/stream-guards.ts +417 -0
- package/src/session/streaming-output.ts +1459 -0
- package/src/session/todo-tracker.ts +380 -0
- package/src/session/tool-choice-queue.ts +305 -0
- package/src/session/ttsr-coordinator.ts +496 -0
- package/src/session/turn-persistence.ts +142 -0
- package/src/session/turn-recovery.ts +2005 -0
- package/src/session/unexpected-stop-classifier.ts +141 -0
- package/src/session/yield-queue.ts +288 -0
- package/src/slash-commands/acp-builtins.ts +70 -0
- package/src/slash-commands/available-commands.ts +105 -0
- package/src/slash-commands/builtin-registry.ts +3149 -0
- package/src/slash-commands/helpers/active-oauth-account.ts +80 -0
- package/src/slash-commands/helpers/collab-qrcode.ts +28 -0
- package/src/slash-commands/helpers/context-report.ts +66 -0
- package/src/slash-commands/helpers/format.ts +46 -0
- package/src/slash-commands/helpers/logout.ts +108 -0
- package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
- package/src/slash-commands/helpers/mcp.ts +533 -0
- package/src/slash-commands/helpers/parse.ts +85 -0
- package/src/slash-commands/helpers/reset-usage.ts +68 -0
- package/src/slash-commands/helpers/security.ts +451 -0
- package/src/slash-commands/helpers/session-pin.ts +44 -0
- package/src/slash-commands/helpers/ssh.ts +196 -0
- package/src/slash-commands/helpers/stats-dashboard.ts +86 -0
- package/src/slash-commands/helpers/todo.ts +285 -0
- package/src/slash-commands/helpers/usage-report.ts +198 -0
- package/src/slash-commands/marketplace-install-parser.ts +99 -0
- package/src/slash-commands/types.ts +139 -0
- package/src/ssh/config-writer.ts +183 -0
- package/src/ssh/connection-manager.ts +667 -0
- package/src/ssh/file-transfer.ts +214 -0
- package/src/ssh/sshfs-mount.ts +163 -0
- package/src/ssh/utils.ts +51 -0
- package/src/startup-splash.ts +19 -0
- package/src/stt/asr-client.ts +401 -0
- package/src/stt/asr-protocol.ts +65 -0
- package/src/stt/asr-worker.ts +603 -0
- package/src/stt/downloader.ts +142 -0
- package/src/stt/endpointer.ts +259 -0
- package/src/stt/index.ts +6 -0
- package/src/stt/models.ts +150 -0
- package/src/stt/sherpa-runtime.ts +71 -0
- package/src/stt/stt-controller.ts +320 -0
- package/src/stt/submit-trigger.ts +74 -0
- package/src/subprocess/worker-client.ts +463 -0
- package/src/subprocess/worker-runtime.ts +494 -0
- package/src/system-prompt.ts +921 -0
- package/src/task/agents.ts +170 -0
- package/src/task/commands.ts +132 -0
- package/src/task/discovery.ts +145 -0
- package/src/task/executor.ts +3431 -0
- package/src/task/index.ts +1515 -0
- package/src/task/isolation-ownership.ts +106 -0
- package/src/task/isolation-runner.ts +444 -0
- package/src/task/label.ts +40 -0
- package/src/task/name-generator.ts +1577 -0
- package/src/task/omp-command.ts +26 -0
- package/src/task/output-manager.ts +115 -0
- package/src/task/parallel.ts +221 -0
- package/src/task/persisted-revive.ts +173 -0
- package/src/task/prewalk.ts +6 -0
- package/src/task/prompt-policy.ts +8 -0
- package/src/task/provider-concurrency.ts +100 -0
- package/src/task/read-only-policy.ts +27 -0
- package/src/task/render.ts +1830 -0
- package/src/task/renderer.ts +14 -0
- package/src/task/repair-args.ts +118 -0
- package/src/task/spawn-policy.ts +72 -0
- package/src/task/structured-subagent.ts +676 -0
- package/src/task/subprocess-tool-registry.ts +88 -0
- package/src/task/types.ts +555 -0
- package/src/task/worktree.ts +967 -0
- package/src/task/yield-assembly.ts +198 -0
- package/src/telemetry-export.ts +504 -0
- package/src/thinking.ts +377 -0
- package/src/tiny/device.ts +111 -0
- package/src/tiny/dtype.ts +101 -0
- package/src/tiny/message-preproc.ts +155 -0
- package/src/tiny/models.ts +268 -0
- package/src/tiny/text.ts +290 -0
- package/src/tiny/title-client.ts +460 -0
- package/src/tiny/title-protocol.ts +56 -0
- package/src/tiny/worker.ts +354 -0
- package/src/tools/acp-bridge.ts +125 -0
- package/src/tools/approval.ts +245 -0
- package/src/tools/ask.ts +1459 -0
- package/src/tools/ast-edit.ts +718 -0
- package/src/tools/ast-grep.ts +526 -0
- package/src/tools/auto-generated-guard.ts +335 -0
- package/src/tools/bash-interactive.ts +435 -0
- package/src/tools/bash-interceptor.ts +148 -0
- package/src/tools/bash-pty-selection.ts +14 -0
- package/src/tools/bash-skill-urls.ts +350 -0
- package/src/tools/bash.ts +1826 -0
- package/src/tools/browser/aria/aria-snapshot.bundle.txt +7 -0
- package/src/tools/browser/aria/aria-snapshot.ts +131 -0
- package/src/tools/browser/attach.ts +219 -0
- package/src/tools/browser/cmux/cmux-tab.ts +1531 -0
- package/src/tools/browser/cmux/rpc.ts +200 -0
- package/src/tools/browser/cmux/socket-client.ts +445 -0
- package/src/tools/browser/launch.ts +939 -0
- package/src/tools/browser/readable.ts +112 -0
- package/src/tools/browser/registry.ts +447 -0
- package/src/tools/browser/relay/bridge.ts +945 -0
- package/src/tools/browser/relay/daemon.ts +117 -0
- package/src/tools/browser/relay/extension-assets/background.js.txt +242 -0
- package/src/tools/browser/relay/extension-assets/manifest.json.txt +14 -0
- package/src/tools/browser/relay/extension-assets/options.html.txt +53 -0
- package/src/tools/browser/relay/extension-assets/options.js.txt +23 -0
- package/src/tools/browser/relay/kind.ts +41 -0
- package/src/tools/browser/relay/protocol.ts +54 -0
- package/src/tools/browser/relay/server.ts +141 -0
- package/src/tools/browser/render.ts +229 -0
- package/src/tools/browser/run-output.ts +76 -0
- package/src/tools/browser/shared-daemon.ts +139 -0
- package/src/tools/browser/tab-protocol.ts +123 -0
- package/src/tools/browser/tab-supervisor.ts +1077 -0
- package/src/tools/browser/tab-worker-entry.ts +29 -0
- package/src/tools/browser/tab-worker.ts +1982 -0
- package/src/tools/browser.ts +487 -0
- package/src/tools/builtin-names.ts +67 -0
- package/src/tools/checkpoint.ts +137 -0
- package/src/tools/computer/exposure.ts +14 -0
- package/src/tools/computer/protocol.ts +69 -0
- package/src/tools/computer/supervisor.ts +409 -0
- package/src/tools/computer/worker-entry.ts +39 -0
- package/src/tools/computer/worker.ts +745 -0
- package/src/tools/computer-renderer.ts +147 -0
- package/src/tools/computer.ts +222 -0
- package/src/tools/conflict-detect.ts +815 -0
- package/src/tools/context.ts +49 -0
- package/src/tools/debug.ts +1121 -0
- package/src/tools/default-renderer.ts +154 -0
- package/src/tools/essential-tools.ts +46 -0
- package/src/tools/eval-backends.ts +34 -0
- package/src/tools/eval-format/index.ts +24 -0
- package/src/tools/eval-format/javascript.ts +952 -0
- package/src/tools/eval-format/julia.ts +446 -0
- package/src/tools/eval-format/python.ts +544 -0
- package/src/tools/eval-format/ruby.ts +380 -0
- package/src/tools/eval-render.ts +784 -0
- package/src/tools/eval.ts +774 -0
- package/src/tools/fetch.ts +1889 -0
- package/src/tools/file-recorder.ts +35 -0
- package/src/tools/fs-cache-invalidation.ts +28 -0
- package/src/tools/gh-cache-invalidation.ts +175 -0
- package/src/tools/gh-format.ts +12 -0
- package/src/tools/gh-renderer.ts +484 -0
- package/src/tools/gh.ts +3958 -0
- package/src/tools/github-cache.ts +663 -0
- package/src/tools/glob.ts +691 -0
- package/src/tools/grep.ts +1920 -0
- package/src/tools/grouped-file-output.ts +210 -0
- package/src/tools/hub/index.ts +579 -0
- package/src/tools/hub/jobs.ts +714 -0
- package/src/tools/hub/launch.ts +690 -0
- package/src/tools/hub/messaging.ts +735 -0
- package/src/tools/hub/types.ts +117 -0
- package/src/tools/image-gen.ts +1689 -0
- package/src/tools/image-providers.ts +50 -0
- package/src/tools/index.ts +736 -0
- package/src/tools/inspect-image-renderer.ts +133 -0
- package/src/tools/inspect-image.ts +316 -0
- package/src/tools/json-tree.ts +260 -0
- package/src/tools/jtd-to-json-schema.ts +219 -0
- package/src/tools/jtd-to-typescript.ts +136 -0
- package/src/tools/jtd-utils.ts +102 -0
- package/src/tools/learn.ts +141 -0
- package/src/tools/list-limit.ts +40 -0
- package/src/tools/manage-skill.ts +102 -0
- package/src/tools/match-line-format.ts +20 -0
- package/src/tools/memory-edit.ts +61 -0
- package/src/tools/memory-recall.ts +102 -0
- package/src/tools/memory-reflect.ts +88 -0
- package/src/tools/memory-render.ts +211 -0
- package/src/tools/memory-retain.ts +89 -0
- package/src/tools/output-meta.ts +860 -0
- package/src/tools/output-schema-validator.ts +307 -0
- package/src/tools/path-utils.ts +1489 -0
- package/src/tools/plan-mode-guard.ts +155 -0
- package/src/tools/puppeteer/00_stealth_tampering.txt +44 -0
- package/src/tools/puppeteer/01_stealth_activity.txt +80 -0
- package/src/tools/puppeteer/02_stealth_hairline.txt +57 -0
- package/src/tools/puppeteer/03_stealth_botd.txt +380 -0
- package/src/tools/puppeteer/04_stealth_iframe.txt +174 -0
- package/src/tools/puppeteer/05_stealth_webgl.txt +233 -0
- package/src/tools/puppeteer/06_stealth_screen.txt +260 -0
- package/src/tools/puppeteer/07_stealth_fonts.txt +99 -0
- package/src/tools/puppeteer/08_stealth_audio.txt +63 -0
- package/src/tools/puppeteer/09_stealth_locale.txt +51 -0
- package/src/tools/puppeteer/10_stealth_plugins.txt +212 -0
- package/src/tools/puppeteer/11_stealth_hardware.txt +59 -0
- package/src/tools/puppeteer/12_stealth_codecs.txt +42 -0
- package/src/tools/puppeteer/13_stealth_worker.txt +235 -0
- package/src/tools/read.ts +3762 -0
- package/src/tools/render-utils.ts +926 -0
- package/src/tools/renderers.ts +133 -0
- package/src/tools/report-tool-issue.ts +568 -0
- package/src/tools/resolve.ts +423 -0
- package/src/tools/review.ts +103 -0
- package/src/tools/run-scope.ts +417 -0
- package/src/tools/security-scan.ts +287 -0
- package/src/tools/shell-tokenize.ts +213 -0
- package/src/tools/sqlite-reader.ts +884 -0
- package/src/tools/terminal-output.ts +141 -0
- package/src/tools/todo.ts +1226 -0
- package/src/tools/tool-errors.ts +62 -0
- package/src/tools/tool-result.ts +102 -0
- package/src/tools/tool-timeouts.ts +38 -0
- package/src/tools/tts.ts +266 -0
- package/src/tools/vibe.ts +608 -0
- package/src/tools/write.ts +1646 -0
- package/src/tools/xdev.ts +560 -0
- package/src/tools/yield.ts +486 -0
- package/src/tts/downloader.ts +64 -0
- package/src/tts/index.ts +10 -0
- package/src/tts/models.ts +137 -0
- package/src/tts/runtime.ts +21 -0
- package/src/tts/speakable.ts +392 -0
- package/src/tts/speech-enhancer.ts +206 -0
- package/src/tts/streaming-player.ts +120 -0
- package/src/tts/tts-client.ts +475 -0
- package/src/tts/tts-protocol.ts +69 -0
- package/src/tts/tts-worker.ts +434 -0
- package/src/tts/vocalizer.ts +419 -0
- package/src/tts/wav.ts +58 -0
- package/src/tui/code-cell.ts +268 -0
- package/src/tui/file-list.ts +55 -0
- package/src/tui/hyperlink.ts +178 -0
- package/src/tui/index.ts +13 -0
- package/src/tui/output-block.ts +268 -0
- package/src/tui/status-line.ts +54 -0
- package/src/tui/tree-list.ts +172 -0
- package/src/tui/types.ts +15 -0
- package/src/tui/utils.ts +103 -0
- package/src/tui/width-aware-text.ts +58 -0
- package/src/utils/active-repo-context.ts +143 -0
- package/src/utils/block-context.ts +312 -0
- package/src/utils/changelog.ts +373 -0
- package/src/utils/clipboard.ts +361 -0
- package/src/utils/command-args.ts +74 -0
- package/src/utils/commit-message-generator.ts +148 -0
- package/src/utils/cpuprofile.ts +235 -0
- package/src/utils/edit-mode.ts +61 -0
- package/src/utils/enhanced-paste.ts +230 -0
- package/src/utils/event-bus.ts +33 -0
- package/src/utils/external-editor.ts +80 -0
- package/src/utils/fetch-timeout.ts +10 -0
- package/src/utils/file-display-mode.ts +44 -0
- package/src/utils/file-mentions.ts +293 -0
- package/src/utils/git.ts +2472 -0
- package/src/utils/image-loading.ts +236 -0
- package/src/utils/image-resize.ts +420 -0
- package/src/utils/image-vision-fallback.ts +196 -0
- package/src/utils/inspect-image-mode.ts +39 -0
- package/src/utils/ipc.ts +38 -0
- package/src/utils/jj.ts +422 -0
- package/src/utils/lang-from-path.ts +251 -0
- package/src/utils/late-cleanup.ts +17 -0
- package/src/utils/local-date.ts +7 -0
- package/src/utils/mac-file-urls.applescript +37 -0
- package/src/utils/markit-cache.ts +166 -0
- package/src/utils/markit.ts +223 -0
- package/src/utils/mupdf-wasm-embed.ts +12 -0
- package/src/utils/open.ts +126 -0
- package/src/utils/profile-tree.ts +111 -0
- package/src/utils/prompt-path.ts +3 -0
- package/src/utils/qrcode.ts +535 -0
- package/src/utils/sample-profile.ts +437 -0
- package/src/utils/session-color.ts +142 -0
- package/src/utils/shell-snapshot-fn-env.sh +63 -0
- package/src/utils/shell-snapshot.ts +326 -0
- package/src/utils/sixel.ts +69 -0
- package/src/utils/thinking-display.ts +163 -0
- package/src/utils/title-generator.ts +597 -0
- package/src/utils/token-rate.ts +72 -0
- package/src/utils/tool-choice.ts +50 -0
- package/src/utils/tools-manager.ts +411 -0
- package/src/utils/turndown.ts +82 -0
- package/src/utils/zip.ts +1106 -0
- package/src/vibe/runtime.ts +1684 -0
- package/src/vibe/state.ts +4 -0
- package/src/web/kagi.ts +305 -0
- package/src/web/parallel.ts +354 -0
- package/src/web/scrapers/artifacthub.ts +207 -0
- package/src/web/scrapers/arxiv.ts +83 -0
- package/src/web/scrapers/aur.ts +162 -0
- package/src/web/scrapers/biorxiv.ts +133 -0
- package/src/web/scrapers/bluesky.ts +262 -0
- package/src/web/scrapers/brew.ts +172 -0
- package/src/web/scrapers/cheatsh.ts +68 -0
- package/src/web/scrapers/chocolatey.ts +196 -0
- package/src/web/scrapers/choosealicense.ts +95 -0
- package/src/web/scrapers/cisa-kev.ts +87 -0
- package/src/web/scrapers/clojars.ts +154 -0
- package/src/web/scrapers/coingecko.ts +177 -0
- package/src/web/scrapers/crates-io.ts +97 -0
- package/src/web/scrapers/crossref.ts +136 -0
- package/src/web/scrapers/devto.ts +147 -0
- package/src/web/scrapers/discogs.ts +306 -0
- package/src/web/scrapers/discourse.ts +197 -0
- package/src/web/scrapers/dockerhub.ts +138 -0
- package/src/web/scrapers/docs-rs.ts +663 -0
- package/src/web/scrapers/fdroid.ts +134 -0
- package/src/web/scrapers/firefox-addons.ts +191 -0
- package/src/web/scrapers/flathub.ts +223 -0
- package/src/web/scrapers/github-gist.ts +58 -0
- package/src/web/scrapers/github.ts +800 -0
- package/src/web/scrapers/gitlab.ts +401 -0
- package/src/web/scrapers/go-pkg.ts +266 -0
- package/src/web/scrapers/hackage.ts +129 -0
- package/src/web/scrapers/hackernews.ts +189 -0
- package/src/web/scrapers/hex.ts +105 -0
- package/src/web/scrapers/huggingface.ts +321 -0
- package/src/web/scrapers/iacr.ts +89 -0
- package/src/web/scrapers/index.ts +252 -0
- package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
- package/src/web/scrapers/lemmy.ts +203 -0
- package/src/web/scrapers/lobsters.ts +175 -0
- package/src/web/scrapers/mastodon.ts +292 -0
- package/src/web/scrapers/maven.ts +138 -0
- package/src/web/scrapers/mdn.ts +173 -0
- package/src/web/scrapers/metacpan.ts +222 -0
- package/src/web/scrapers/musicbrainz.ts +250 -0
- package/src/web/scrapers/npm.ts +98 -0
- package/src/web/scrapers/nuget.ts +183 -0
- package/src/web/scrapers/nvd.ts +222 -0
- package/src/web/scrapers/ollama.ts +239 -0
- package/src/web/scrapers/open-vsx.ts +106 -0
- package/src/web/scrapers/opencorporates.ts +292 -0
- package/src/web/scrapers/openlibrary.ts +336 -0
- package/src/web/scrapers/orcid.ts +286 -0
- package/src/web/scrapers/osv.ts +176 -0
- package/src/web/scrapers/packagist.ts +160 -0
- package/src/web/scrapers/pub-dev.ts +143 -0
- package/src/web/scrapers/pubmed.ts +211 -0
- package/src/web/scrapers/pypi.ts +112 -0
- package/src/web/scrapers/rawg.ts +110 -0
- package/src/web/scrapers/readthedocs.ts +121 -0
- package/src/web/scrapers/reddit.ts +95 -0
- package/src/web/scrapers/repology.ts +251 -0
- package/src/web/scrapers/rfc.ts +201 -0
- package/src/web/scrapers/rubygems.ts +103 -0
- package/src/web/scrapers/searchcode.ts +189 -0
- package/src/web/scrapers/sec-edgar.ts +261 -0
- package/src/web/scrapers/semantic-scholar.ts +171 -0
- package/src/web/scrapers/snapcraft.ts +187 -0
- package/src/web/scrapers/sourcegraph.ts +336 -0
- package/src/web/scrapers/spdx.ts +108 -0
- package/src/web/scrapers/spotify.ts +198 -0
- package/src/web/scrapers/stackoverflow.ts +120 -0
- package/src/web/scrapers/terraform.ts +277 -0
- package/src/web/scrapers/tldr.ts +47 -0
- package/src/web/scrapers/twitter.ts +94 -0
- package/src/web/scrapers/types.ts +354 -0
- package/src/web/scrapers/utils.ts +109 -0
- package/src/web/scrapers/vimeo.ts +133 -0
- package/src/web/scrapers/vscode-marketplace.ts +187 -0
- package/src/web/scrapers/w3c.ts +156 -0
- package/src/web/scrapers/wikidata.ts +344 -0
- package/src/web/scrapers/wikipedia.ts +84 -0
- package/src/web/scrapers/youtube.ts +325 -0
- package/src/web/search/index.ts +393 -0
- package/src/web/search/provider.ts +272 -0
- package/src/web/search/providers/anthropic.ts +406 -0
- package/src/web/search/providers/base.ts +112 -0
- package/src/web/search/providers/brave.ts +181 -0
- package/src/web/search/providers/browser-headers.ts +82 -0
- package/src/web/search/providers/browser-page.ts +125 -0
- package/src/web/search/providers/codex.ts +788 -0
- package/src/web/search/providers/duckduckgo.ts +382 -0
- package/src/web/search/providers/ecosia.ts +183 -0
- package/src/web/search/providers/exa.ts +513 -0
- package/src/web/search/providers/firecrawl.ts +213 -0
- package/src/web/search/providers/gemini.ts +634 -0
- package/src/web/search/providers/google.ts +195 -0
- package/src/web/search/providers/jina.ts +144 -0
- package/src/web/search/providers/kagi.ts +98 -0
- package/src/web/search/providers/kimi.ts +221 -0
- package/src/web/search/providers/mojeek.ts +220 -0
- package/src/web/search/providers/parallel.ts +186 -0
- package/src/web/search/providers/perplexity-auth.ts +142 -0
- package/src/web/search/providers/perplexity.ts +997 -0
- package/src/web/search/providers/public.ts +199 -0
- package/src/web/search/providers/searxng.ts +467 -0
- package/src/web/search/providers/startpage.ts +225 -0
- package/src/web/search/providers/synthetic.ts +126 -0
- package/src/web/search/providers/tavily.ts +244 -0
- package/src/web/search/providers/tinyfish.ts +166 -0
- package/src/web/search/providers/utils.ts +130 -0
- package/src/web/search/providers/xai.ts +372 -0
- package/src/web/search/providers/zai.ts +452 -0
- package/src/web/search/query.ts +850 -0
- package/src/web/search/render.ts +262 -0
- package/src/web/search/types.ts +518 -0
- package/src/web/search/utils.ts +17 -0
- package/src/workspace-tree.ts +326 -0
|
@@ -0,0 +1,2024 @@
|
|
|
1
|
+
import * as fs from "node:fs/promises";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { CompactionCancelledError, type CompactionOutcome } from "@oh-my-pi/pi-agent-core/compaction";
|
|
5
|
+
import {
|
|
6
|
+
getEnvApiKey,
|
|
7
|
+
getProviderDetails,
|
|
8
|
+
type ProviderDetails,
|
|
9
|
+
resolveUsedFraction,
|
|
10
|
+
type UsageLimit,
|
|
11
|
+
type UsageReport,
|
|
12
|
+
} from "@oh-my-pi/pi-ai";
|
|
13
|
+
import { Loader, Markdown, padding, Spacer, Text, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
14
|
+
import { formatDuration, Snowflake, sanitizeText } from "@oh-my-pi/pi-utils";
|
|
15
|
+
import { shouldEnableAppendOnlyContext } from "../../config/append-only-context-mode";
|
|
16
|
+
import { type BashResult, isPersistentShellCdCommand } from "../../exec/bash-executor";
|
|
17
|
+
import { type LoadedCustomShare, loadCustomShare } from "../../export/custom-share";
|
|
18
|
+
import { parseExportArgs } from "../../export/html/args";
|
|
19
|
+
import { shareSession } from "../../export/share";
|
|
20
|
+
import type { CompactOptions } from "../../extensibility/extensions/types";
|
|
21
|
+
import {
|
|
22
|
+
diffMentalModelContent,
|
|
23
|
+
type HindsightApi,
|
|
24
|
+
type HindsightSessionState,
|
|
25
|
+
loadHindsightConfig,
|
|
26
|
+
reloadMentalModelsForSession,
|
|
27
|
+
resolveSeedsForScope,
|
|
28
|
+
seedAlreadyExists,
|
|
29
|
+
summarizeMentalModel,
|
|
30
|
+
} from "../../hindsight";
|
|
31
|
+
import { memoryStatsUnavailableMessage, resolveMemoryBackend } from "../../memory-backend";
|
|
32
|
+
import { BashExecutionComponent } from "../../modes/components/bash-execution";
|
|
33
|
+
import { BorderedLoader } from "../../modes/components/bordered-loader";
|
|
34
|
+
import { DynamicBorder } from "../../modes/components/dynamic-border";
|
|
35
|
+
import { EvalExecutionComponent } from "../../modes/components/eval-execution";
|
|
36
|
+
import { MoveOverlay, type MoveOverlayResult } from "../../modes/components/move-overlay";
|
|
37
|
+
import { TranscriptBlock } from "../../modes/components/transcript-container";
|
|
38
|
+
import { getMarkdownTheme, getSymbolTheme, theme } from "../../modes/theme/theme";
|
|
39
|
+
import type { InteractiveModeContext } from "../../modes/types";
|
|
40
|
+
import { computeContextBreakdown, renderContextUsage } from "../../modes/utils/context-usage";
|
|
41
|
+
import { buildHotkeysMarkdown } from "../../modes/utils/hotkeys-markdown";
|
|
42
|
+
import { buildToolsMarkdown } from "../../modes/utils/tools-markdown";
|
|
43
|
+
import type { AsyncJobSnapshotItem } from "../../session/agent-session";
|
|
44
|
+
import type { AuthStorage, OAuthAccountIdentity } from "../../session/auth-storage";
|
|
45
|
+
import type { CompactMode } from "../../session/compact-modes";
|
|
46
|
+
import type { NewSessionOptions } from "../../session/session-entries";
|
|
47
|
+
import { formatShakeSummary, type ShakeMode, type ShakeResult } from "../../session/shake-types";
|
|
48
|
+
import { formatActiveAccountLabel, limitMatchesActiveAccount } from "../../slash-commands/helpers/active-oauth-account";
|
|
49
|
+
import { outputMeta } from "../../tools/output-meta";
|
|
50
|
+
import { resolveToCwd, stripOuterDoubleQuotes } from "../../tools/path-utils";
|
|
51
|
+
import { replaceTabs, truncateToWidth } from "../../tools/render-utils";
|
|
52
|
+
import {
|
|
53
|
+
getChangelogPath,
|
|
54
|
+
parseChangelog,
|
|
55
|
+
RECENT_CHANGELOG_ENTRY_LIMIT,
|
|
56
|
+
renderChangelogEntries,
|
|
57
|
+
} from "../../utils/changelog";
|
|
58
|
+
import { copyToClipboard } from "../../utils/clipboard";
|
|
59
|
+
import { openPath } from "../../utils/open";
|
|
60
|
+
import { setSessionTerminalTitle } from "../../utils/title-generator";
|
|
61
|
+
|
|
62
|
+
function showMarkdownPanel(ctx: InteractiveModeContext, title: string, markdown: string): void {
|
|
63
|
+
const block = new TranscriptBlock();
|
|
64
|
+
block.addChild(new DynamicBorder());
|
|
65
|
+
block.addChild(new Text(theme.bold(theme.fg("accent", title)), 1, 0));
|
|
66
|
+
block.addChild(new Spacer(1));
|
|
67
|
+
block.addChild(new Markdown(markdown.trim(), 1, 1, getMarkdownTheme()));
|
|
68
|
+
block.addChild(new DynamicBorder());
|
|
69
|
+
ctx.presentCommandOutput(block);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export class CommandController {
|
|
73
|
+
constructor(private readonly ctx: InteractiveModeContext) {}
|
|
74
|
+
|
|
75
|
+
openInBrowser(urlOrPath: string): void {
|
|
76
|
+
openPath(urlOrPath);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async handleExportCommand(text: string): Promise<void> {
|
|
80
|
+
try {
|
|
81
|
+
const { outputPath, useUserThemes } = parseExportArgs(text.slice("/export".length));
|
|
82
|
+
if (outputPath === "--copy" || outputPath === "clipboard" || outputPath === "copy") {
|
|
83
|
+
this.ctx.showWarning("Use /dump to copy the session to clipboard.");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const filePath = await this.ctx.session.exportToHtml(outputPath, useUserThemes);
|
|
88
|
+
this.ctx.showStatus(`Session exported to: ${filePath}`);
|
|
89
|
+
this.openInBrowser(filePath);
|
|
90
|
+
} catch (error: unknown) {
|
|
91
|
+
this.ctx.showError(`Failed to export session: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async handleDumpCommand(): Promise<void> {
|
|
96
|
+
try {
|
|
97
|
+
const formatted = this.ctx.session.formatSessionAsText();
|
|
98
|
+
if (!formatted) {
|
|
99
|
+
this.ctx.showError("No messages to dump yet.");
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
// Build the LLM request JSON sidecar first so its path (and a
|
|
103
|
+
// raw-context warning) can be appended to the copied transcript.
|
|
104
|
+
let sidecarPath: string | undefined;
|
|
105
|
+
let sidecarError: string | undefined;
|
|
106
|
+
try {
|
|
107
|
+
sidecarPath = await this.ctx.session.dumpLlmRequestToTmpDir();
|
|
108
|
+
} catch (error: unknown) {
|
|
109
|
+
sidecarError = error instanceof Error ? error.message : "Unknown error";
|
|
110
|
+
}
|
|
111
|
+
const doc = sidecarPath
|
|
112
|
+
? `${formatted}\n\n---\nLLM request JSON: ${sidecarPath}\nThis file persists on disk and may contain raw context/secrets — treat accordingly.`
|
|
113
|
+
: formatted;
|
|
114
|
+
await copyToClipboard(doc);
|
|
115
|
+
const statusParts = ["Session copied to clipboard"];
|
|
116
|
+
if (sidecarPath) statusParts.push(`LLM request JSON: ${sidecarPath}`);
|
|
117
|
+
if (sidecarError) statusParts.push(`LLM request JSON unavailable: ${sidecarError}`);
|
|
118
|
+
this.ctx.showStatus(statusParts.join("\n"));
|
|
119
|
+
} catch (error: unknown) {
|
|
120
|
+
this.ctx.showError(`Failed to copy session: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
handleAdvisorDumpCommand(isRaw = false) {
|
|
125
|
+
try {
|
|
126
|
+
const advisorHistory = this.ctx.session.formatAdvisorHistoryAsText({ compact: !isRaw });
|
|
127
|
+
if (advisorHistory === null) {
|
|
128
|
+
this.ctx.showError("Advisor is not active for this session.");
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (!advisorHistory) {
|
|
132
|
+
this.ctx.showError("Advisor has no history yet.");
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
copyToClipboard(advisorHistory);
|
|
136
|
+
this.ctx.showStatus("Advisor history copied to clipboard");
|
|
137
|
+
} catch (error: unknown) {
|
|
138
|
+
this.ctx.showError(
|
|
139
|
+
`Failed to copy advisor history: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async handleDebugTranscriptCommand(): Promise<void> {
|
|
145
|
+
try {
|
|
146
|
+
const width = Math.max(1, this.ctx.ui.terminal.columns);
|
|
147
|
+
const renderedLines = this.ctx.chatContainer.render(width).map(line => replaceTabs(Bun.stripANSI(line)));
|
|
148
|
+
const rendered = renderedLines.join("\n").trimEnd();
|
|
149
|
+
if (!rendered) {
|
|
150
|
+
this.ctx.showError("No messages to dump yet.");
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
const tmpPath = path.join(os.tmpdir(), `${Snowflake.next()}-tmp.txt`);
|
|
154
|
+
await Bun.write(tmpPath, `${rendered}\n`);
|
|
155
|
+
this.ctx.showStatus(`Debug transcript written to:\n${tmpPath}`);
|
|
156
|
+
} catch (error: unknown) {
|
|
157
|
+
this.ctx.showError(
|
|
158
|
+
`Failed to write debug transcript: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async handleShareCommand(): Promise<void> {
|
|
164
|
+
let customShare: LoadedCustomShare | null;
|
|
165
|
+
try {
|
|
166
|
+
customShare = await loadCustomShare();
|
|
167
|
+
} catch (err) {
|
|
168
|
+
this.ctx.showError(err instanceof Error ? err.message : String(err));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const loader = new BorderedLoader(this.ctx.ui, theme, "Sharing session...");
|
|
173
|
+
this.ctx.editorContainer.clear();
|
|
174
|
+
this.ctx.editorContainer.addChild(loader);
|
|
175
|
+
this.ctx.ui.setFocus(loader);
|
|
176
|
+
this.ctx.ui.requestRender();
|
|
177
|
+
|
|
178
|
+
const restoreEditor = () => {
|
|
179
|
+
loader.dispose();
|
|
180
|
+
this.ctx.editorContainer.clear();
|
|
181
|
+
this.ctx.editorContainer.addChild(this.ctx.editor);
|
|
182
|
+
this.ctx.ui.setFocus(this.ctx.editor);
|
|
183
|
+
};
|
|
184
|
+
loader.onAbort = () => {
|
|
185
|
+
restoreEditor();
|
|
186
|
+
this.ctx.showStatus("Share cancelled");
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
// Custom share scripts keep their legacy contract: they receive a path
|
|
190
|
+
// to a standalone HTML export. No fallback to the default flow on error.
|
|
191
|
+
if (customShare) {
|
|
192
|
+
const tmpFile = path.join(os.tmpdir(), `${Snowflake.next()}.html`);
|
|
193
|
+
try {
|
|
194
|
+
await this.ctx.session.exportToHtml(tmpFile);
|
|
195
|
+
const result = await customShare.fn(tmpFile);
|
|
196
|
+
if (loader.signal.aborted) return;
|
|
197
|
+
restoreEditor();
|
|
198
|
+
|
|
199
|
+
if (typeof result === "string") {
|
|
200
|
+
this.ctx.showStatus(`Share URL: ${result}`);
|
|
201
|
+
this.openInBrowser(result);
|
|
202
|
+
} else if (result) {
|
|
203
|
+
const parts: string[] = [];
|
|
204
|
+
if (result.url) parts.push(`Share URL: ${result.url}`);
|
|
205
|
+
if (result.message) parts.push(result.message);
|
|
206
|
+
if (parts.length > 0) this.ctx.showStatus(parts.join("\n"));
|
|
207
|
+
if (result.url) this.openInBrowser(result.url);
|
|
208
|
+
} else {
|
|
209
|
+
this.ctx.showStatus("Session shared");
|
|
210
|
+
}
|
|
211
|
+
} catch (err) {
|
|
212
|
+
if (!loader.signal.aborted) {
|
|
213
|
+
restoreEditor();
|
|
214
|
+
this.ctx.showError(`Custom share failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
215
|
+
}
|
|
216
|
+
} finally {
|
|
217
|
+
await fs.rm(tmpFile, { force: true }).catch(() => {});
|
|
218
|
+
}
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Default: encrypted snapshot to a secret gist (preferred) or the share
|
|
223
|
+
// server; the key rides in the link fragment and never leaves the client.
|
|
224
|
+
try {
|
|
225
|
+
const result = await shareSession(this.ctx.session.sessionManager, {
|
|
226
|
+
serverUrl: this.ctx.settings.get("share.serverUrl"),
|
|
227
|
+
store: this.ctx.settings.get("share.store"),
|
|
228
|
+
state: this.ctx.session.state,
|
|
229
|
+
obfuscator: this.ctx.settings.get("share.redactSecrets") ? this.ctx.session.obfuscator : undefined,
|
|
230
|
+
});
|
|
231
|
+
if (loader.signal.aborted) return;
|
|
232
|
+
restoreEditor();
|
|
233
|
+
|
|
234
|
+
const lines = [`Share URL: ${result.url}`];
|
|
235
|
+
if (result.gistUrl) lines.push(`Gist: ${result.gistUrl}`);
|
|
236
|
+
if (result.truncated) lines.push("Note: large content was trimmed to fit the share size limit.");
|
|
237
|
+
this.ctx.showStatus(lines.join("\n"));
|
|
238
|
+
this.openInBrowser(result.url);
|
|
239
|
+
} catch (error: unknown) {
|
|
240
|
+
if (!loader.signal.aborted) {
|
|
241
|
+
restoreEditor();
|
|
242
|
+
this.ctx.showError(`Failed to share session: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async handleSessionCommand(): Promise<void> {
|
|
248
|
+
const stats = this.ctx.session.getSessionStats();
|
|
249
|
+
const premiumRequests =
|
|
250
|
+
"premiumRequests" in stats && typeof stats.premiumRequests === "number"
|
|
251
|
+
? stats.premiumRequests
|
|
252
|
+
: this.ctx.session.sessionManager.getUsageStatistics().premiumRequests;
|
|
253
|
+
const normalizedPremiumRequests = Math.round((premiumRequests + Number.EPSILON) * 100) / 100;
|
|
254
|
+
|
|
255
|
+
let info = `${theme.bold("Session Info")}\n\n`;
|
|
256
|
+
info += `${theme.fg("dim", "File:")} ${stats.sessionFile ?? "In-memory"}\n`;
|
|
257
|
+
info += `${theme.fg("dim", "ID:")} ${stats.sessionId}\n\n`;
|
|
258
|
+
info += `\n${theme.bold("Provider")}\n`;
|
|
259
|
+
const model = this.ctx.session.model;
|
|
260
|
+
if (!model) {
|
|
261
|
+
info += `${theme.fg("dim", "No model selected")}\n`;
|
|
262
|
+
} else {
|
|
263
|
+
const authMode = resolveProviderAuthMode(this.ctx.session.modelRegistry.authStorage, model.provider);
|
|
264
|
+
const openaiWebsocketSetting = this.ctx.settings.get("providers.openaiWebsockets") ?? "auto";
|
|
265
|
+
const preferOpenAICodexWebsockets =
|
|
266
|
+
openaiWebsocketSetting === "on" ? true : openaiWebsocketSetting === "off" ? false : undefined;
|
|
267
|
+
const credentialSource = this.ctx.session.modelRegistry.authStorage.describeCredentialSource(
|
|
268
|
+
model.provider,
|
|
269
|
+
stats.sessionId,
|
|
270
|
+
);
|
|
271
|
+
const providerDetails = getProviderDetails({
|
|
272
|
+
model,
|
|
273
|
+
sessionId: stats.sessionId,
|
|
274
|
+
authMode,
|
|
275
|
+
credentialSource,
|
|
276
|
+
preferWebsockets: preferOpenAICodexWebsockets,
|
|
277
|
+
providerSessionState: this.ctx.session.providerSessionState,
|
|
278
|
+
});
|
|
279
|
+
info += renderProviderSection(providerDetails, theme);
|
|
280
|
+
}
|
|
281
|
+
info += `\n`;
|
|
282
|
+
info += `${theme.bold("Messages")}\n`;
|
|
283
|
+
info += `${theme.fg("dim", "User:")} ${stats.userMessages}\n`;
|
|
284
|
+
info += `${theme.fg("dim", "Assistant:")} ${stats.assistantMessages}\n`;
|
|
285
|
+
info += `${theme.fg("dim", "Tool Calls:")} ${stats.toolCalls}\n`;
|
|
286
|
+
info += `${theme.fg("dim", "Tool Results:")} ${stats.toolResults}\n`;
|
|
287
|
+
info += `${theme.fg("dim", "Total:")} ${stats.totalMessages}\n\n`;
|
|
288
|
+
// Append-only context
|
|
289
|
+
{
|
|
290
|
+
const setting = this.ctx.settings.get("provider.appendOnlyContext") ?? "auto";
|
|
291
|
+
const model = this.ctx.session.model;
|
|
292
|
+
const mode = shouldEnableAppendOnlyContext(setting, model);
|
|
293
|
+
const activeLabel = mode ? theme.fg("success", "active") : theme.fg("dim", "inactive");
|
|
294
|
+
const settingLabel = setting === "auto" ? `${setting} (${model?.provider ?? "?"})` : setting;
|
|
295
|
+
info += `${theme.fg("dim", "Append-Only:")} ${activeLabel} (setting: ${settingLabel})\n`;
|
|
296
|
+
}
|
|
297
|
+
info += `${theme.bold("Tokens")}\n`;
|
|
298
|
+
info += `${theme.fg("dim", "Input:")} ${stats.tokens.input.toLocaleString()}\n`;
|
|
299
|
+
info += `${theme.fg("dim", "Output:")} ${stats.tokens.output.toLocaleString()}\n`;
|
|
300
|
+
if (stats.tokens.cacheRead > 0) {
|
|
301
|
+
info += `${theme.fg("dim", "Cache Read:")} ${stats.tokens.cacheRead.toLocaleString()}\n`;
|
|
302
|
+
}
|
|
303
|
+
if (stats.tokens.cacheWrite > 0) {
|
|
304
|
+
info += `${theme.fg("dim", "Cache Write:")} ${stats.tokens.cacheWrite.toLocaleString()}\n`;
|
|
305
|
+
}
|
|
306
|
+
info += `${theme.fg("dim", "Total:")} ${stats.tokens.total.toLocaleString()}\n`;
|
|
307
|
+
|
|
308
|
+
if (stats.cost > 0 || normalizedPremiumRequests > 0) {
|
|
309
|
+
info += `\n${theme.bold("Cost")}\n`;
|
|
310
|
+
if (stats.cost > 0) {
|
|
311
|
+
info += `${theme.fg("dim", "Total:")} ${stats.cost.toFixed(4)}\n`;
|
|
312
|
+
}
|
|
313
|
+
if (normalizedPremiumRequests > 0) {
|
|
314
|
+
info += `${theme.fg("dim", "Premium Requests:")} ${normalizedPremiumRequests.toLocaleString()}\n`;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (this.ctx.lspServers && this.ctx.lspServers.length > 0) {
|
|
319
|
+
info += `\n${theme.bold("LSP Servers")}\n`;
|
|
320
|
+
for (const server of this.ctx.lspServers) {
|
|
321
|
+
const statusColor =
|
|
322
|
+
server.status === "ready"
|
|
323
|
+
? "success"
|
|
324
|
+
: server.status === "available"
|
|
325
|
+
? "dim"
|
|
326
|
+
: server.status === "connecting"
|
|
327
|
+
? "warning"
|
|
328
|
+
: "error";
|
|
329
|
+
const statusText =
|
|
330
|
+
server.status === "error" && server.error ? `${server.status}: ${server.error}` : server.status;
|
|
331
|
+
info += `${theme.fg("dim", `${server.name}:`)} ${theme.fg(statusColor, statusText)} ${theme.fg("dim", `(${server.fileTypes.join(", ")})`)}\n`;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (this.ctx.mcpManager) {
|
|
336
|
+
const mcpServers = this.ctx.mcpManager.getConnectedServers();
|
|
337
|
+
info += `\n${theme.bold("MCP Servers")}\n`;
|
|
338
|
+
if (mcpServers.length === 0) {
|
|
339
|
+
info += `${theme.fg("dim", "None connected")}\n`;
|
|
340
|
+
} else {
|
|
341
|
+
for (const name of mcpServers) {
|
|
342
|
+
const conn = this.ctx.mcpManager.getConnection(name);
|
|
343
|
+
const toolCount = conn?.tools?.length ?? 0;
|
|
344
|
+
info += `${theme.fg("dim", `${name}:`)} ${theme.fg("success", "connected")} ${theme.fg("dim", `(${toolCount} tools)`)}\n`;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
this.ctx.presentCommandOutput([new Spacer(1), new Text(info, 1, 0)]);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
static readonly #advisorStatusGlyph: Record<string, string> = {
|
|
353
|
+
running: "●",
|
|
354
|
+
paused: "○",
|
|
355
|
+
no_model: "○",
|
|
356
|
+
quota_exhausted: "✕",
|
|
357
|
+
error: "✕",
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
static readonly #advisorStatusLabel: Record<string, string> = {
|
|
361
|
+
running: "running",
|
|
362
|
+
paused: "off",
|
|
363
|
+
no_model: "no model",
|
|
364
|
+
quota_exhausted: "quota exhausted",
|
|
365
|
+
error: "error",
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
async handleAdvisorStatusCommand(): Promise<void> {
|
|
369
|
+
const stats = this.ctx.session.getAdvisorStats();
|
|
370
|
+
if (!stats.configured) {
|
|
371
|
+
this.ctx.presentCommandOutput([new Spacer(1), new Text("Advisor is disabled.", 1, 0)]);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
// Fetch live quota data (cached 5 min by the auth-gateway) so we can show
|
|
375
|
+
// real usage windows/reset timers per advisor provider. Non-fatal when absent.
|
|
376
|
+
const usageProvider = this.ctx.session as { fetchUsageReports?: () => Promise<UsageReport[] | null> };
|
|
377
|
+
let usageReports: UsageReport[] | null = null;
|
|
378
|
+
if (usageProvider.fetchUsageReports) {
|
|
379
|
+
try {
|
|
380
|
+
usageReports = await usageProvider.fetchUsageReports();
|
|
381
|
+
} catch {
|
|
382
|
+
// Network/auth failure is non-fatal — just skip the quota line.
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// Resolve the active OAuth identity for each advisor's provider so quota
|
|
386
|
+
// filtering matches the credential actually in use (not sibling accounts).
|
|
387
|
+
const resolveActiveAdvisorAccount = (provider: string, sessionId?: string): OAuthAccountIdentity | undefined =>
|
|
388
|
+
this.ctx.session.modelRegistry.authStorage.getOAuthAccountIdentity(
|
|
389
|
+
provider,
|
|
390
|
+
sessionId ?? this.ctx.session.sessionId,
|
|
391
|
+
);
|
|
392
|
+
const nowMs = Date.now();
|
|
393
|
+
// Roster view: show every configured advisor with its status, even when
|
|
394
|
+
// none are live (all paused/no-model). The old code returned a generic
|
|
395
|
+
// message that hid the per-advisor state the user needs to act on.
|
|
396
|
+
if (stats.advisors.length > 1 || (stats.configured && !stats.active)) {
|
|
397
|
+
let info = `${theme.bold("Advisor Status")} (${stats.advisors.length} advisors)\n`;
|
|
398
|
+
for (const a of stats.advisors) {
|
|
399
|
+
const glyph = CommandController.#advisorStatusGlyph[a.status] ?? "?";
|
|
400
|
+
const label = CommandController.#advisorStatusLabel[a.status] ?? a.status;
|
|
401
|
+
const color =
|
|
402
|
+
a.status === "running"
|
|
403
|
+
? "success"
|
|
404
|
+
: a.status === "quota_exhausted" || a.status === "error"
|
|
405
|
+
? "error"
|
|
406
|
+
: "dim";
|
|
407
|
+
info += `\n${theme.fg(color, glyph)} ${theme.bold(a.name)} ${theme.fg("dim", `[${label}]`)}\n`;
|
|
408
|
+
if (a.model) {
|
|
409
|
+
info += `${theme.fg("dim", "Model:")} ${a.model.provider}/${a.model.id}\n`;
|
|
410
|
+
}
|
|
411
|
+
if (a.model && usageReports) {
|
|
412
|
+
const quota = formatCompactQuota(
|
|
413
|
+
a.model.provider,
|
|
414
|
+
usageReports,
|
|
415
|
+
nowMs,
|
|
416
|
+
resolveActiveAdvisorAccount(a.model.provider, a.sessionId),
|
|
417
|
+
);
|
|
418
|
+
if (quota) info += `${theme.fg("dim", quota)}\n`;
|
|
419
|
+
}
|
|
420
|
+
if (a.status === "running" || a.status === "quota_exhausted") {
|
|
421
|
+
const ctx =
|
|
422
|
+
a.contextWindow > 0
|
|
423
|
+
? `${a.contextTokens.toLocaleString()} / ${a.contextWindow.toLocaleString()} (${Math.round((a.contextTokens / a.contextWindow) * 100)}%)`
|
|
424
|
+
: `${a.contextTokens.toLocaleString()}`;
|
|
425
|
+
info += `${theme.fg("dim", "Context:")} ${ctx}\n`;
|
|
426
|
+
info += `${theme.fg("dim", "Messages:")} ${a.messages.total.toLocaleString()}\n`;
|
|
427
|
+
info += `${theme.fg("dim", "Spend:")} ${a.tokens.input.toLocaleString()} in / ${a.tokens.output.toLocaleString()} out`;
|
|
428
|
+
if (a.cost > 0) info += `, $${a.cost.toFixed(4)}`;
|
|
429
|
+
info += "\n";
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
if (stats.active) {
|
|
433
|
+
info += `\n${theme.bold("Totals")}\n`;
|
|
434
|
+
info += `${theme.fg("dim", "Tokens:")} ${stats.tokens.total.toLocaleString()}\n`;
|
|
435
|
+
if (stats.cost > 0) info += `${theme.fg("dim", "Cost:")} $${stats.cost.toFixed(4)}\n`;
|
|
436
|
+
}
|
|
437
|
+
this.ctx.presentCommandOutput([new Spacer(1), new Text(info, 1, 0)]);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
// Single active advisor — detailed view.
|
|
441
|
+
const model = stats.model;
|
|
442
|
+
let info = `${theme.bold("Advisor Status")}\n\n`;
|
|
443
|
+
if (stats.advisors.length === 1) {
|
|
444
|
+
const a = stats.advisors[0];
|
|
445
|
+
const glyph = CommandController.#advisorStatusGlyph[a.status] ?? "?";
|
|
446
|
+
const label = CommandController.#advisorStatusLabel[a.status] ?? a.status;
|
|
447
|
+
info += `${theme.fg(a.status === "running" ? "success" : "error", glyph)} ${a.name} ${theme.fg("dim", `[${label}]`)}\n\n`;
|
|
448
|
+
}
|
|
449
|
+
if (model) {
|
|
450
|
+
info += `${theme.bold("Provider")}\n`;
|
|
451
|
+
info += `${theme.fg("dim", "Model:")} ${model.provider}/${model.id}\n`;
|
|
452
|
+
}
|
|
453
|
+
if (model && usageReports) {
|
|
454
|
+
const quota = formatCompactQuota(
|
|
455
|
+
model.provider,
|
|
456
|
+
usageReports,
|
|
457
|
+
nowMs,
|
|
458
|
+
resolveActiveAdvisorAccount(model.provider, stats.advisors[0]?.sessionId),
|
|
459
|
+
);
|
|
460
|
+
if (quota) {
|
|
461
|
+
info += `\n${theme.bold("Quota")}\n`;
|
|
462
|
+
info += `${theme.fg("dim", quota)}\n`;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
info += `\n${theme.bold("Messages")}\n`;
|
|
466
|
+
info += `${theme.fg("dim", "User:")} ${stats.messages.user.toLocaleString()}\n`;
|
|
467
|
+
info += `${theme.fg("dim", "Assistant:")} ${stats.messages.assistant.toLocaleString()}\n`;
|
|
468
|
+
info += `${theme.fg("dim", "Total:")} ${stats.messages.total.toLocaleString()}\n`;
|
|
469
|
+
info += `\n${theme.bold("Context")}\n`;
|
|
470
|
+
if (stats.contextWindow > 0) {
|
|
471
|
+
const percent = Math.round((stats.contextTokens / stats.contextWindow) * 100);
|
|
472
|
+
info += `${theme.fg("dim", "Tokens:")} ${stats.contextTokens.toLocaleString()} / ${stats.contextWindow.toLocaleString()} (${percent}%)\n`;
|
|
473
|
+
} else {
|
|
474
|
+
info += `${theme.fg("dim", "Tokens:")} ${stats.contextTokens.toLocaleString()}\n`;
|
|
475
|
+
}
|
|
476
|
+
info += `\n${theme.bold("Spend")}\n`;
|
|
477
|
+
info += `${theme.fg("dim", "Input:")} ${stats.tokens.input.toLocaleString()}\n`;
|
|
478
|
+
info += `${theme.fg("dim", "Output:")} ${stats.tokens.output.toLocaleString()}\n`;
|
|
479
|
+
if (stats.tokens.cacheRead > 0) {
|
|
480
|
+
info += `${theme.fg("dim", "Cache Read:")} ${stats.tokens.cacheRead.toLocaleString()}\n`;
|
|
481
|
+
}
|
|
482
|
+
if (stats.cost > 0) info += `${theme.fg("dim", "Cost:")} $${stats.cost.toFixed(4)}\n`;
|
|
483
|
+
this.ctx.presentCommandOutput([new Spacer(1), new Text(info, 1, 0)]);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
async handleJobsCommand(): Promise<void> {
|
|
487
|
+
const snapshot = this.ctx.session.getAsyncJobSnapshot({ recentLimit: 5 });
|
|
488
|
+
if (!snapshot) {
|
|
489
|
+
this.ctx.showWarning("Async background jobs are unavailable in this session.");
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const now = Date.now();
|
|
494
|
+
const lineWidth = Math.max(24, (this.ctx.ui.terminal.columns ?? 100) - 24);
|
|
495
|
+
let info = `${theme.bold("Background Jobs")}\n\n`;
|
|
496
|
+
info += `${theme.fg("dim", "Running:")} ${snapshot.running.length}\n`;
|
|
497
|
+
|
|
498
|
+
if (snapshot.running.length === 0 && snapshot.recent.length === 0) {
|
|
499
|
+
info += `\n${theme.fg("dim", "No async jobs yet.")}\n`;
|
|
500
|
+
this.ctx.presentCommandOutput([new Spacer(1), new Text(info, 1, 0)]);
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (snapshot.running.length > 0) {
|
|
505
|
+
info += `\n${theme.bold("Running Jobs")}\n`;
|
|
506
|
+
for (const job of snapshot.running) {
|
|
507
|
+
info += `${renderJobLine(job, now)}\n`;
|
|
508
|
+
info += ` ${theme.fg("dim", truncateJobLabel(job.label, lineWidth))}\n`;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (snapshot.recent.length > 0) {
|
|
513
|
+
info += `\n${theme.bold("Recent Jobs")}\n`;
|
|
514
|
+
for (const job of snapshot.recent) {
|
|
515
|
+
info += `${renderJobLine(job, now)}\n`;
|
|
516
|
+
info += ` ${theme.fg("dim", truncateJobLabel(job.label, lineWidth))}\n`;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
this.ctx.presentCommandOutput([new Spacer(1), new Text(info.trimEnd(), 1, 0)]);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
async handleUsageCommand(reports?: UsageReport[] | null): Promise<void> {
|
|
524
|
+
let usageReports = reports ?? null;
|
|
525
|
+
if (!usageReports) {
|
|
526
|
+
const provider = this.ctx.session as { fetchUsageReports?: () => Promise<UsageReport[] | null> };
|
|
527
|
+
if (!provider.fetchUsageReports) {
|
|
528
|
+
this.ctx.showWarning("Usage reporting is not configured for this session.");
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
try {
|
|
532
|
+
usageReports = await provider.fetchUsageReports();
|
|
533
|
+
} catch (error) {
|
|
534
|
+
this.ctx.showError(`Failed to fetch usage data: ${error instanceof Error ? error.message : String(error)}`);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
if (!usageReports || usageReports.length === 0) {
|
|
540
|
+
this.ctx.showWarning("No usage data available.");
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const availableWidth = Math.max(40, (this.ctx.ui.terminal.columns ?? 100) - 2);
|
|
545
|
+
const currentProvider = this.ctx.session.model?.provider;
|
|
546
|
+
const activeAccount = currentProvider
|
|
547
|
+
? this.ctx.session.modelRegistry.authStorage.getOAuthAccountIdentity(
|
|
548
|
+
currentProvider,
|
|
549
|
+
this.ctx.session.sessionId,
|
|
550
|
+
)
|
|
551
|
+
: undefined;
|
|
552
|
+
const usageModelSelectors = this.ctx.session.getUsageReportingModelSelectors(usageReports);
|
|
553
|
+
const output = renderUsageReports(
|
|
554
|
+
usageReports,
|
|
555
|
+
theme,
|
|
556
|
+
Date.now(),
|
|
557
|
+
availableWidth,
|
|
558
|
+
provider => (provider === currentProvider ? activeAccount : undefined),
|
|
559
|
+
usageModelSelectors,
|
|
560
|
+
);
|
|
561
|
+
this.ctx.presentCommandOutput([new Spacer(1), new Text(output, 1, 0)]);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
async handleChangelogCommand(showFull = false): Promise<void> {
|
|
565
|
+
const changelogPath = getChangelogPath();
|
|
566
|
+
const allEntries = await parseChangelog(changelogPath);
|
|
567
|
+
const entriesToShow = showFull ? allEntries : allEntries.slice(0, RECENT_CHANGELOG_ENTRY_LIMIT);
|
|
568
|
+
const changelogMarkdown =
|
|
569
|
+
entriesToShow.length > 0 ? renderChangelogEntries(entriesToShow).markdown : "No changelog entries found.";
|
|
570
|
+
const title = showFull ? "Full Changelog" : "Recent Changes";
|
|
571
|
+
const hint = showFull
|
|
572
|
+
? ""
|
|
573
|
+
: `\n\n${theme.fg("dim", "Use")} ${theme.bold("/changelog full")} ${theme.fg("dim", "to view the complete changelog.")}`;
|
|
574
|
+
|
|
575
|
+
const block = new TranscriptBlock();
|
|
576
|
+
block.addChild(new DynamicBorder());
|
|
577
|
+
block.addChild(new Text(theme.bold(theme.fg("accent", title)), 1, 0));
|
|
578
|
+
block.addChild(new Spacer(1));
|
|
579
|
+
block.addChild(new Markdown(changelogMarkdown + hint, 1, 1, getMarkdownTheme()));
|
|
580
|
+
block.addChild(new DynamicBorder());
|
|
581
|
+
this.ctx.presentCommandOutput(block);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
handleHotkeysCommand(): void {
|
|
585
|
+
const hotkeys = buildHotkeysMarkdown({ keybindings: this.ctx.keybindings });
|
|
586
|
+
showMarkdownPanel(this.ctx, "Keyboard Shortcuts", hotkeys);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
handleToolsCommand(): void {
|
|
590
|
+
const tools = buildToolsMarkdown({
|
|
591
|
+
tools: this.ctx.session.agent.state.tools,
|
|
592
|
+
xdevTools: this.ctx.session.getXdevToolEntries(),
|
|
593
|
+
});
|
|
594
|
+
showMarkdownPanel(this.ctx, "Available Tools", tools);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
handleContextCommand(): void {
|
|
598
|
+
const breakdown = computeContextBreakdown(this.ctx.session, { snapcompactSavings: true });
|
|
599
|
+
if (breakdown.contextWindow <= 0) {
|
|
600
|
+
this.ctx.showWarning("Context usage is unavailable: no model is selected for this session.");
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
const output = renderContextUsage(breakdown, theme);
|
|
604
|
+
const block = new TranscriptBlock();
|
|
605
|
+
block.addChild(new DynamicBorder());
|
|
606
|
+
block.addChild(new Text(theme.bold(theme.fg("accent", "Context Usage")), 1, 0));
|
|
607
|
+
block.addChild(new Spacer(1));
|
|
608
|
+
block.addChild(new Text(output, 1, 0));
|
|
609
|
+
block.addChild(new DynamicBorder());
|
|
610
|
+
this.ctx.presentCommandOutput(block);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
async handleMemoryCommand(text: string): Promise<void> {
|
|
614
|
+
const argumentText = text.slice(7).trim();
|
|
615
|
+
const action = argumentText.split(/\s+/, 1)[0]?.toLowerCase() || "view";
|
|
616
|
+
const agentDir = this.ctx.settings.getAgentDir();
|
|
617
|
+
const backend = await resolveMemoryBackend(this.ctx.settings);
|
|
618
|
+
|
|
619
|
+
if (action === "view") {
|
|
620
|
+
const payload = await backend.buildDeveloperInstructions(agentDir, this.ctx.settings, this.ctx.session);
|
|
621
|
+
if (!payload) {
|
|
622
|
+
this.ctx.showWarning("Memory payload is empty (memory backend off, disabled, or no memory available).");
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
const block = new TranscriptBlock();
|
|
626
|
+
block.addChild(new DynamicBorder());
|
|
627
|
+
block.addChild(new Text(theme.bold(theme.fg("accent", "Memory Injection Payload")), 1, 0));
|
|
628
|
+
block.addChild(new Spacer(1));
|
|
629
|
+
block.addChild(new Markdown(payload, 1, 1, getMarkdownTheme()));
|
|
630
|
+
block.addChild(new DynamicBorder());
|
|
631
|
+
this.ctx.presentCommandOutput(block);
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if (action === "reset" || action === "clear") {
|
|
636
|
+
try {
|
|
637
|
+
await backend.clear(agentDir, this.ctx.sessionManager.getCwd(), this.ctx.session);
|
|
638
|
+
await this.ctx.session.refreshBaseSystemPrompt();
|
|
639
|
+
this.ctx.showStatus("Memory data cleared and system prompt refreshed.");
|
|
640
|
+
} catch (error) {
|
|
641
|
+
this.ctx.showError(`Memory clear failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
642
|
+
}
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
if (action === "enqueue" || action === "rebuild") {
|
|
647
|
+
try {
|
|
648
|
+
await backend.enqueue(agentDir, this.ctx.sessionManager.getCwd(), this.ctx.session);
|
|
649
|
+
this.ctx.showStatus("Memory consolidation enqueued.");
|
|
650
|
+
} catch (error) {
|
|
651
|
+
this.ctx.showError(`Memory enqueue failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
652
|
+
}
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
if (action === "stats" || action === "diagnose") {
|
|
657
|
+
const hook = action === "stats" ? backend.stats : backend.diagnose;
|
|
658
|
+
try {
|
|
659
|
+
const payload = await hook?.(agentDir, this.ctx.sessionManager.getCwd(), this.ctx.session);
|
|
660
|
+
if (!payload) {
|
|
661
|
+
this.ctx.showWarning(memoryStatsUnavailableMessage(backend.id, action));
|
|
662
|
+
return;
|
|
663
|
+
}
|
|
664
|
+
showMarkdownPanel(this.ctx, `Memory ${action === "stats" ? "Stats" : "Diagnostics"}`, payload);
|
|
665
|
+
} catch (error) {
|
|
666
|
+
this.ctx.showError(`Memory ${action} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
667
|
+
}
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (action === "mm") {
|
|
672
|
+
await this.#handleMentalModelsSubcommand(argumentText);
|
|
673
|
+
return;
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
this.ctx.showError("Usage: /memory <view|stats|diagnose|clear|reset|enqueue|rebuild|mm ...>");
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
async #handleMentalModelsSubcommand(argumentText: string): Promise<void> {
|
|
680
|
+
// Parse: "mm <verb> [arg]"
|
|
681
|
+
const parts = argumentText.split(/\s+/).slice(1);
|
|
682
|
+
const verb = parts[0]?.toLowerCase() ?? "list";
|
|
683
|
+
const arg = parts[1];
|
|
684
|
+
|
|
685
|
+
const state = this.ctx.session.getHindsightSessionState();
|
|
686
|
+
const primary = state && !state.aliasOf ? state : undefined;
|
|
687
|
+
if (!primary) {
|
|
688
|
+
this.ctx.showError("Hindsight backend is not active for this session.");
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
if (!primary.config.mentalModelsEnabled) {
|
|
692
|
+
this.ctx.showError("Mental models are disabled (hindsight.mentalModelsEnabled = false).");
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
switch (verb) {
|
|
697
|
+
case "list":
|
|
698
|
+
await this.#mmList(primary);
|
|
699
|
+
return;
|
|
700
|
+
case "show":
|
|
701
|
+
if (!arg) return this.ctx.showError("Usage: /memory mm show <id>");
|
|
702
|
+
await this.#mmShow(primary, arg);
|
|
703
|
+
return;
|
|
704
|
+
case "refresh":
|
|
705
|
+
await this.#mmRefresh(primary, arg);
|
|
706
|
+
return;
|
|
707
|
+
case "history":
|
|
708
|
+
if (!arg) return this.ctx.showError("Usage: /memory mm history <id>");
|
|
709
|
+
await this.#mmHistory(primary, arg);
|
|
710
|
+
return;
|
|
711
|
+
case "seed":
|
|
712
|
+
await this.#mmSeed(primary);
|
|
713
|
+
return;
|
|
714
|
+
case "reload":
|
|
715
|
+
await this.#mmReload(primary);
|
|
716
|
+
return;
|
|
717
|
+
case "delete":
|
|
718
|
+
case "remove":
|
|
719
|
+
if (!arg) return this.ctx.showError("Usage: /memory mm delete <id>");
|
|
720
|
+
await this.#mmDelete(primary, arg);
|
|
721
|
+
return;
|
|
722
|
+
default:
|
|
723
|
+
this.ctx.showError("Usage: /memory mm <list|show|refresh|history|seed|reload|delete>");
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
async #mmList(state: HindsightSessionState): Promise<void> {
|
|
728
|
+
const client: HindsightApi = state.client;
|
|
729
|
+
try {
|
|
730
|
+
const response = await client.listMentalModels(state.bankId, { detail: "metadata" });
|
|
731
|
+
const items = response.items ?? [];
|
|
732
|
+
if (items.length === 0) {
|
|
733
|
+
this.ctx.showStatus(`No mental models on bank ${state.bankId}.`);
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
const lines = items
|
|
737
|
+
.slice()
|
|
738
|
+
.sort((a, b) => a.id.localeCompare(b.id))
|
|
739
|
+
.map(summarizeMentalModel);
|
|
740
|
+
showMarkdownPanel(this.ctx, `Mental Models — ${state.bankId}`, lines.join("\n"));
|
|
741
|
+
} catch (error) {
|
|
742
|
+
this.ctx.showError(`mm list failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
async #mmShow(state: HindsightSessionState, id: string): Promise<void> {
|
|
747
|
+
try {
|
|
748
|
+
const model = await state.client.getMentalModel(state.bankId, id, { detail: "content" });
|
|
749
|
+
if (!model) {
|
|
750
|
+
this.ctx.showError(`Mental model not found: ${id}`);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
const tags = model.tags && model.tags.length > 0 ? `\n_tags: ${model.tags.join(", ")}_` : "";
|
|
754
|
+
const refreshed = model.last_refreshed_at ? `\n_last refreshed: ${model.last_refreshed_at}_` : "";
|
|
755
|
+
const sourceQuery = model.source_query ? `\n\n**Source query:** ${model.source_query}` : "";
|
|
756
|
+
const content = (model.content ?? "_(empty — background reflect may still be running)_").trim();
|
|
757
|
+
showMarkdownPanel(
|
|
758
|
+
this.ctx,
|
|
759
|
+
model.name,
|
|
760
|
+
`**id:** \`${model.id}\`${tags}${refreshed}${sourceQuery}\n\n${content}`,
|
|
761
|
+
);
|
|
762
|
+
} catch (error) {
|
|
763
|
+
this.ctx.showError(`mm show failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
async #mmRefresh(state: HindsightSessionState, id: string | undefined): Promise<void> {
|
|
768
|
+
try {
|
|
769
|
+
if (id) {
|
|
770
|
+
// Single-model refresh is explicit operator intent: bypass the
|
|
771
|
+
// auto-refresh filter so curated/manual models can still be
|
|
772
|
+
// refreshed on demand.
|
|
773
|
+
await state.client.refreshMentalModel(state.bankId, id);
|
|
774
|
+
this.ctx.showStatus(`Refresh queued for mental model ${id}.`);
|
|
775
|
+
} else {
|
|
776
|
+
// Bulk refresh: only touch models that opted into automatic
|
|
777
|
+
// refresh via `trigger.refresh_after_consolidation`. Curated
|
|
778
|
+
// models are reviewed before publishing and must not be
|
|
779
|
+
// silently regenerated by a bank-wide refresh sweep. Reading
|
|
780
|
+
// `detail: "content"` here is required because the trigger
|
|
781
|
+
// field is excluded from `detail: "metadata"`.
|
|
782
|
+
const list = await state.client.listMentalModels(state.bankId, { detail: "content" });
|
|
783
|
+
const items = list.items ?? [];
|
|
784
|
+
if (items.length === 0) {
|
|
785
|
+
this.ctx.showStatus(`No mental models on bank ${state.bankId}.`);
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
const targets = items.filter(m => m.trigger?.refresh_after_consolidation === true);
|
|
789
|
+
const skipped = items.length - targets.length;
|
|
790
|
+
if (targets.length === 0) {
|
|
791
|
+
this.ctx.showStatus(
|
|
792
|
+
`No mental models opted into auto-refresh; ${skipped} curated model(s) left untouched. Pass an explicit id to refresh one of them.`,
|
|
793
|
+
);
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
let queued = 0;
|
|
797
|
+
for (const item of targets) {
|
|
798
|
+
try {
|
|
799
|
+
await state.client.refreshMentalModel(state.bankId, item.id);
|
|
800
|
+
queued++;
|
|
801
|
+
} catch (error) {
|
|
802
|
+
this.ctx.showWarning(
|
|
803
|
+
`Refresh failed for ${item.id}: ${error instanceof Error ? error.message : String(error)}`,
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
const skippedSuffix = skipped > 0 ? `; skipped ${skipped} curated model(s)` : "";
|
|
808
|
+
this.ctx.showStatus(
|
|
809
|
+
`Refresh queued for ${queued}/${targets.length} auto-refresh model(s)${skippedSuffix}.`,
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
// Reload the cache after a brief grace so the new content (if the refresh
|
|
813
|
+
// completes synchronously on the server) flows into the system prompt.
|
|
814
|
+
await Bun.sleep(500);
|
|
815
|
+
await reloadMentalModelsForSession(state.session);
|
|
816
|
+
} catch (error) {
|
|
817
|
+
this.ctx.showError(`mm refresh failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
async #mmHistory(state: HindsightSessionState, id: string): Promise<void> {
|
|
822
|
+
try {
|
|
823
|
+
const [model, history] = await Promise.all([
|
|
824
|
+
state.client.getMentalModel(state.bankId, id, { detail: "content" }),
|
|
825
|
+
state.client.getMentalModelHistory(state.bankId, id),
|
|
826
|
+
]);
|
|
827
|
+
if (!model) {
|
|
828
|
+
this.ctx.showError(`Mental model not found: ${id}`);
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
if (history.length === 0) {
|
|
832
|
+
this.ctx.showStatus(`No history recorded for ${id}.`);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
// History is most-recent first. Each entry stores the content BEFORE that
|
|
836
|
+
// change. To diff "what changed at entry N", compare entry N's
|
|
837
|
+
// previous_content (= state before that change) with entry N-1's
|
|
838
|
+
// previous_content (= state after that change, which was state before
|
|
839
|
+
// the next change). For the most recent change, compare against the
|
|
840
|
+
// model's CURRENT content.
|
|
841
|
+
const sections: string[] = [];
|
|
842
|
+
for (let i = 0; i < history.length; i++) {
|
|
843
|
+
const before = history[i].previous_content ?? "";
|
|
844
|
+
const after = i === 0 ? (model.content ?? "") : (history[i - 1].previous_content ?? "");
|
|
845
|
+
const diff = diffMentalModelContent(before, after);
|
|
846
|
+
sections.push(`### ${history[i].changed_at}\n\n\`\`\`diff\n${diff}\n\`\`\``);
|
|
847
|
+
}
|
|
848
|
+
showMarkdownPanel(this.ctx, `History — ${model.name}`, sections.join("\n\n"));
|
|
849
|
+
} catch (error) {
|
|
850
|
+
this.ctx.showError(`mm history failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
async #mmSeed(state: HindsightSessionState): Promise<void> {
|
|
855
|
+
try {
|
|
856
|
+
const config = loadHindsightConfig(this.ctx.settings);
|
|
857
|
+
const seeds = resolveSeedsForScope(
|
|
858
|
+
{
|
|
859
|
+
bankId: state.bankId,
|
|
860
|
+
retainTags: state.retainTags,
|
|
861
|
+
recallTags: state.recallTags,
|
|
862
|
+
recallTagsMatch: state.recallTagsMatch,
|
|
863
|
+
},
|
|
864
|
+
config.scoping,
|
|
865
|
+
);
|
|
866
|
+
if (seeds.length === 0) {
|
|
867
|
+
this.ctx.showStatus(`No built-in seeds apply to scoping=${config.scoping}.`);
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
const list = await state.client.listMentalModels(state.bankId, { detail: "metadata" });
|
|
871
|
+
const existing = list.items ?? [];
|
|
872
|
+
let created = 0;
|
|
873
|
+
let skipped = 0;
|
|
874
|
+
for (const seed of seeds) {
|
|
875
|
+
if (seedAlreadyExists(seed, existing)) {
|
|
876
|
+
skipped++;
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
try {
|
|
880
|
+
await state.client.createMentalModel(state.bankId, seed.name, seed.sourceQuery, {
|
|
881
|
+
id: seed.id,
|
|
882
|
+
tags: seed.tags.length > 0 ? seed.tags : undefined,
|
|
883
|
+
maxTokens: seed.maxTokens,
|
|
884
|
+
trigger: seed.trigger,
|
|
885
|
+
});
|
|
886
|
+
created++;
|
|
887
|
+
} catch (error) {
|
|
888
|
+
this.ctx.showWarning(
|
|
889
|
+
`Seed failed for ${seed.id}: ${error instanceof Error ? error.message : String(error)}`,
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
this.ctx.showStatus(`Seeded ${created} new mental model(s); ${skipped} already present.`);
|
|
894
|
+
} catch (error) {
|
|
895
|
+
this.ctx.showError(`mm seed failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
async #mmReload(state: HindsightSessionState): Promise<void> {
|
|
900
|
+
const ok = await reloadMentalModelsForSession(state.session);
|
|
901
|
+
if (ok) {
|
|
902
|
+
this.ctx.showStatus("Mental-model cache reloaded.");
|
|
903
|
+
} else {
|
|
904
|
+
this.ctx.showError("Reload failed (Hindsight backend not active or mental models disabled).");
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
async #mmDelete(state: HindsightSessionState, id: string): Promise<void> {
|
|
909
|
+
try {
|
|
910
|
+
const removed = await state.client.deleteMentalModel(state.bankId, id);
|
|
911
|
+
if (!removed) {
|
|
912
|
+
this.ctx.showError(`Mental model not found: ${id}`);
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
// Drop the cached snippet so the closing tag does not silently keep
|
|
916
|
+
// stale content in the system prompt until the next agent_end TTL.
|
|
917
|
+
await reloadMentalModelsForSession(state.session);
|
|
918
|
+
this.ctx.showStatus(`Deleted mental model ${id} from bank ${state.bankId}.`);
|
|
919
|
+
} catch (error) {
|
|
920
|
+
this.ctx.showError(`mm delete failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
async #runNewSessionFlow(options?: NewSessionOptions, label: string = "New session started"): Promise<void> {
|
|
925
|
+
this.ctx.clearTransientSessionUi();
|
|
926
|
+
|
|
927
|
+
if (this.ctx.session.isCompacting) {
|
|
928
|
+
this.ctx.session.abortCompaction();
|
|
929
|
+
while (this.ctx.session.isCompacting) {
|
|
930
|
+
await Bun.sleep(10);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
if (!(await this.ctx.session.newSession(options))) return;
|
|
934
|
+
this.ctx.resetObserverRegistry();
|
|
935
|
+
setSessionTerminalTitle(this.ctx.sessionManager.getSessionName(), this.ctx.sessionManager.getCwd());
|
|
936
|
+
|
|
937
|
+
this.ctx.statusLine.invalidate();
|
|
938
|
+
this.ctx.statusLine.resetActiveTime();
|
|
939
|
+
this.ctx.updateEditorBorderColor();
|
|
940
|
+
this.ctx.clearTransientSessionUi();
|
|
941
|
+
this.ctx.resetTranscript();
|
|
942
|
+
|
|
943
|
+
this.ctx.present([new Spacer(1), new Text(`${theme.fg("accent", `${theme.status.success} ${label}`)}`, 1, 1)]);
|
|
944
|
+
await this.ctx.reloadTodos();
|
|
945
|
+
this.ctx.ui.requestRender(true, { clearScrollback: true });
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
async handleClearCommand(): Promise<void> {
|
|
949
|
+
await this.#runNewSessionFlow();
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
async handleFreshCommand(): Promise<void> {
|
|
953
|
+
const result = this.ctx.session.freshSession();
|
|
954
|
+
if (!result) {
|
|
955
|
+
this.ctx.showWarning("Wait for the current response to finish or abort it before refreshing provider state.");
|
|
956
|
+
return;
|
|
957
|
+
}
|
|
958
|
+
const stateLabel = result.closedProviderSessions === 1 ? "provider state" : "provider states";
|
|
959
|
+
this.ctx.statusLine.invalidate();
|
|
960
|
+
this.ctx.ui.requestRender();
|
|
961
|
+
this.ctx.showStatus(`Fresh provider session started (${result.closedProviderSessions} ${stateLabel} pruned).`);
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
async handleResetContextCommand(): Promise<void> {
|
|
965
|
+
if (this.ctx.session.isCompacting) {
|
|
966
|
+
this.ctx.session.abortCompaction();
|
|
967
|
+
while (this.ctx.session.isCompacting) {
|
|
968
|
+
await Bun.sleep(10);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
const result = await this.ctx.session.resetSessionContext();
|
|
972
|
+
if (!result) {
|
|
973
|
+
this.ctx.showWarning("Wait for the current response to finish or abort it before resetting the context.");
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
// Drop the rendered transcript so the UI matches the now-empty model
|
|
977
|
+
// context (mirrors #runNewSessionFlow's teardown, minus the new session —
|
|
978
|
+
// the session id, title, and transcript file all survive).
|
|
979
|
+
this.ctx.clearTransientSessionUi();
|
|
980
|
+
this.ctx.resetTranscript();
|
|
981
|
+
this.ctx.statusLine.invalidate();
|
|
982
|
+
this.ctx.updateEditorBorderColor();
|
|
983
|
+
const noun = result.droppedCount === 1 ? "message" : "messages";
|
|
984
|
+
this.ctx.present([
|
|
985
|
+
new Spacer(1),
|
|
986
|
+
new Text(
|
|
987
|
+
`${theme.fg("accent", `${theme.status.success} Context reset — ${result.droppedCount} ${noun} dropped; session continues.`)}`,
|
|
988
|
+
1,
|
|
989
|
+
1,
|
|
990
|
+
),
|
|
991
|
+
]);
|
|
992
|
+
this.ctx.ui.requestRender(true, { clearScrollback: true });
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
async handleDropCommand(): Promise<void> {
|
|
996
|
+
if (!this.ctx.sessionManager.getSessionFile()) {
|
|
997
|
+
this.ctx.showError("Nothing to drop (in-memory session)");
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
await this.#runNewSessionFlow({ drop: true }, "Session dropped");
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
async handleForkCommand(): Promise<void> {
|
|
1004
|
+
if (this.ctx.session.isStreaming) {
|
|
1005
|
+
this.ctx.showWarning("Wait for the current response to finish or abort it before forking.");
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
if (this.ctx.loadingAnimation) {
|
|
1009
|
+
this.ctx.loadingAnimation.stop();
|
|
1010
|
+
this.ctx.loadingAnimation = undefined;
|
|
1011
|
+
}
|
|
1012
|
+
this.ctx.statusContainer.disposeChildren();
|
|
1013
|
+
|
|
1014
|
+
const success = await this.ctx.session.fork();
|
|
1015
|
+
if (!success) {
|
|
1016
|
+
this.ctx.showError("Fork failed (session not persisted or cancelled)");
|
|
1017
|
+
return;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
this.ctx.statusLine.invalidate();
|
|
1021
|
+
this.ctx.ui.requestRender();
|
|
1022
|
+
|
|
1023
|
+
const sessionFile = this.ctx.session.sessionFile;
|
|
1024
|
+
const shortPath = sessionFile ? sessionFile.split("/").pop() : "new session";
|
|
1025
|
+
this.ctx.present([
|
|
1026
|
+
new Spacer(1),
|
|
1027
|
+
new Text(`${theme.fg("accent", `${theme.status.success} Session forked to ${shortPath}`)}`, 1, 1),
|
|
1028
|
+
]);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/**
|
|
1032
|
+
* `/move` — relocate the current session to a different directory.
|
|
1033
|
+
*
|
|
1034
|
+
* With no `targetPath` (TUI only), opens an autocomplete overlay so the user
|
|
1035
|
+
* can pick or type a directory. With a `targetPath`, resolves it directly.
|
|
1036
|
+
* If the target directory does not exist, the user is asked whether to create
|
|
1037
|
+
* it. The active session file and artifacts are moved into the target
|
|
1038
|
+
* directory's session bucket so `/resume` from that directory can find it.
|
|
1039
|
+
*/
|
|
1040
|
+
async handleMoveCommand(targetPath?: string): Promise<void> {
|
|
1041
|
+
if (this.ctx.session.isStreaming) {
|
|
1042
|
+
this.ctx.showWarning("Wait for the current response to finish or abort it before moving.");
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
let input: string | undefined = targetPath?.trim() || undefined;
|
|
1047
|
+
|
|
1048
|
+
// No argument in TUI mode: open the path autocomplete overlay.
|
|
1049
|
+
if (!input) {
|
|
1050
|
+
const result = await this.ctx.showHookCustom<MoveOverlayResult | undefined>(
|
|
1051
|
+
(_tui, _theme, _keybindings, done) => new MoveOverlay(this.ctx.sessionManager.getCwd(), done),
|
|
1052
|
+
{ overlay: true },
|
|
1053
|
+
);
|
|
1054
|
+
if (!result) return; // cancelled
|
|
1055
|
+
input = result.directory;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
const unquoted = stripOuterDoubleQuotes(input);
|
|
1059
|
+
if (!unquoted) {
|
|
1060
|
+
this.ctx.showError("Usage: /move <path>");
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
const cwd = this.ctx.sessionManager.getCwd();
|
|
1065
|
+
const resolvedPath = resolveToCwd(unquoted, cwd);
|
|
1066
|
+
|
|
1067
|
+
// If the directory doesn't exist, offer to create it.
|
|
1068
|
+
let isDirectory: boolean;
|
|
1069
|
+
try {
|
|
1070
|
+
isDirectory = (await fs.stat(resolvedPath)).isDirectory();
|
|
1071
|
+
} catch {
|
|
1072
|
+
isDirectory = false;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
if (!isDirectory) {
|
|
1076
|
+
const parentDir = path.dirname(resolvedPath);
|
|
1077
|
+
let parentExists = false;
|
|
1078
|
+
try {
|
|
1079
|
+
parentExists = (await fs.stat(parentDir)).isDirectory();
|
|
1080
|
+
} catch {
|
|
1081
|
+
parentExists = false;
|
|
1082
|
+
}
|
|
1083
|
+
if (!parentExists) {
|
|
1084
|
+
this.ctx.showError(`Cannot create "${path.basename(resolvedPath)}": parent directory does not exist`);
|
|
1085
|
+
return;
|
|
1086
|
+
}
|
|
1087
|
+
const confirmed = await this.ctx.showHookConfirm(
|
|
1088
|
+
"Create directory?",
|
|
1089
|
+
`"${path.basename(resolvedPath)}" does not exist. Create it?`,
|
|
1090
|
+
);
|
|
1091
|
+
if (!confirmed) return;
|
|
1092
|
+
try {
|
|
1093
|
+
await fs.mkdir(resolvedPath, { recursive: true });
|
|
1094
|
+
} catch (err) {
|
|
1095
|
+
this.ctx.showError(`Failed to create directory: ${err instanceof Error ? err.message : String(err)}`);
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
try {
|
|
1100
|
+
await this.ctx.settings.flush();
|
|
1101
|
+
} catch (err) {
|
|
1102
|
+
this.ctx.showError(`Failed to save pending settings: ${err instanceof Error ? err.message : String(err)}`);
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
try {
|
|
1107
|
+
await this.ctx.session.moveSession(resolvedPath);
|
|
1108
|
+
} catch (err) {
|
|
1109
|
+
this.ctx.showError(`Move failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
await this.ctx.applyCwdChange(resolvedPath);
|
|
1113
|
+
|
|
1114
|
+
this.ctx.updateEditorBorderColor();
|
|
1115
|
+
await this.ctx.reloadTodos();
|
|
1116
|
+
this.ctx.ui.requestRender();
|
|
1117
|
+
|
|
1118
|
+
this.ctx.present([
|
|
1119
|
+
new Spacer(1),
|
|
1120
|
+
new Text(`${theme.fg("accent", `${theme.status.success} Moved to ${resolvedPath}`)}`, 1, 1),
|
|
1121
|
+
]);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
async handleRenameCommand(title: string): Promise<void> {
|
|
1125
|
+
try {
|
|
1126
|
+
const stored = await this.ctx.sessionManager.setSessionName(title, "user");
|
|
1127
|
+
if (!stored) {
|
|
1128
|
+
this.ctx.showError("Session name cannot be empty.");
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
const name = this.ctx.sessionManager.getSessionName()!;
|
|
1132
|
+
this.ctx.showStatus(`Session renamed to "${name}".`);
|
|
1133
|
+
} catch (err) {
|
|
1134
|
+
this.ctx.showError(`Rename failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
async handleBashCommand(command: string, excludeFromContext = false): Promise<void> {
|
|
1139
|
+
const isDeferred = this.ctx.session.isStreaming;
|
|
1140
|
+
const shouldPersistCwd = isPersistentShellCdCommand(command);
|
|
1141
|
+
if (isDeferred && shouldPersistCwd) {
|
|
1142
|
+
this.ctx.showWarning("Wait for the current response to finish or abort it before changing directories.");
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
this.ctx.bashComponent = new BashExecutionComponent(command, this.ctx.ui, excludeFromContext);
|
|
1147
|
+
|
|
1148
|
+
if (isDeferred) {
|
|
1149
|
+
this.ctx.pendingMessagesContainer.addChild(this.ctx.bashComponent);
|
|
1150
|
+
this.ctx.pendingBashComponents.push(this.ctx.bashComponent);
|
|
1151
|
+
} else {
|
|
1152
|
+
this.ctx.present(this.ctx.bashComponent);
|
|
1153
|
+
}
|
|
1154
|
+
this.ctx.ui.requestRender();
|
|
1155
|
+
|
|
1156
|
+
try {
|
|
1157
|
+
const result = await this.ctx.session.executeBash(
|
|
1158
|
+
command,
|
|
1159
|
+
chunk => {
|
|
1160
|
+
if (this.ctx.bashComponent) {
|
|
1161
|
+
this.ctx.bashComponent.appendOutput(chunk);
|
|
1162
|
+
}
|
|
1163
|
+
},
|
|
1164
|
+
{ excludeFromContext, useUserShell: true },
|
|
1165
|
+
);
|
|
1166
|
+
if (this.ctx.bashComponent) {
|
|
1167
|
+
const meta = outputMeta().truncationFromSummary(result, { direction: "tail" }).get();
|
|
1168
|
+
this.ctx.bashComponent.setComplete(result.exitCode, result.cancelled, {
|
|
1169
|
+
output: result.output,
|
|
1170
|
+
truncation: meta?.truncation,
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
try {
|
|
1174
|
+
if (shouldPersistCwd) await this.#applyBashResultCwd(result);
|
|
1175
|
+
} catch (error) {
|
|
1176
|
+
this.ctx.showError(
|
|
1177
|
+
`Bash command completed, but OMP failed to update its working directory: ${
|
|
1178
|
+
error instanceof Error ? error.message : "Unknown error"
|
|
1179
|
+
}`,
|
|
1180
|
+
);
|
|
1181
|
+
}
|
|
1182
|
+
} catch (error) {
|
|
1183
|
+
if (this.ctx.bashComponent) {
|
|
1184
|
+
this.ctx.bashComponent.setComplete(undefined, false);
|
|
1185
|
+
}
|
|
1186
|
+
this.ctx.showError(`Bash command failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
this.ctx.bashComponent = undefined;
|
|
1190
|
+
this.ctx.ui.requestRender();
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
async #moveInteractiveCwd(resolvedPath: string): Promise<void> {
|
|
1194
|
+
await this.ctx.sessionManager.moveTo(resolvedPath);
|
|
1195
|
+
await this.ctx.applyCwdChange(resolvedPath);
|
|
1196
|
+
this.ctx.updateEditorBorderColor();
|
|
1197
|
+
await this.ctx.reloadTodos();
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
async #applyBashResultCwd(result: BashResult): Promise<void> {
|
|
1201
|
+
if (result.cancelled || result.exitCode !== 0 || !result.workingDir) return;
|
|
1202
|
+
if (!path.isAbsolute(result.workingDir)) return;
|
|
1203
|
+
|
|
1204
|
+
const resolvedPath = path.resolve(result.workingDir);
|
|
1205
|
+
if (resolvedPath === path.resolve(this.ctx.sessionManager.getCwd())) return;
|
|
1206
|
+
|
|
1207
|
+
let isDirectory = false;
|
|
1208
|
+
try {
|
|
1209
|
+
isDirectory = (await fs.stat(resolvedPath)).isDirectory();
|
|
1210
|
+
} catch {
|
|
1211
|
+
isDirectory = false;
|
|
1212
|
+
}
|
|
1213
|
+
if (!isDirectory) return;
|
|
1214
|
+
|
|
1215
|
+
await this.#moveInteractiveCwd(resolvedPath);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
async handlePythonCommand(code: string, excludeFromContext = false): Promise<void> {
|
|
1219
|
+
const isDeferred = this.ctx.session.isStreaming;
|
|
1220
|
+
this.ctx.pythonComponent = new EvalExecutionComponent(code, this.ctx.ui, excludeFromContext);
|
|
1221
|
+
|
|
1222
|
+
if (isDeferred) {
|
|
1223
|
+
this.ctx.pendingMessagesContainer.addChild(this.ctx.pythonComponent);
|
|
1224
|
+
this.ctx.pendingPythonComponents.push(this.ctx.pythonComponent);
|
|
1225
|
+
} else {
|
|
1226
|
+
this.ctx.present(this.ctx.pythonComponent);
|
|
1227
|
+
}
|
|
1228
|
+
this.ctx.ui.requestRender();
|
|
1229
|
+
|
|
1230
|
+
try {
|
|
1231
|
+
const result = await this.ctx.session.executePython(
|
|
1232
|
+
code,
|
|
1233
|
+
chunk => {
|
|
1234
|
+
if (this.ctx.pythonComponent) {
|
|
1235
|
+
this.ctx.pythonComponent.appendOutput(chunk);
|
|
1236
|
+
}
|
|
1237
|
+
},
|
|
1238
|
+
{ excludeFromContext },
|
|
1239
|
+
);
|
|
1240
|
+
|
|
1241
|
+
if (this.ctx.pythonComponent) {
|
|
1242
|
+
const meta = outputMeta().truncationFromSummary(result, { direction: "tail" }).get();
|
|
1243
|
+
this.ctx.pythonComponent.setComplete(result.exitCode, result.cancelled, {
|
|
1244
|
+
output: result.output,
|
|
1245
|
+
truncation: meta?.truncation,
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
} catch (error) {
|
|
1249
|
+
if (this.ctx.pythonComponent) {
|
|
1250
|
+
this.ctx.pythonComponent.setComplete(undefined, false);
|
|
1251
|
+
}
|
|
1252
|
+
this.ctx.showError(`Python execution failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
this.ctx.pythonComponent = undefined;
|
|
1256
|
+
this.ctx.ui.requestRender();
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
async handleCompactCommand(
|
|
1260
|
+
customInstructions?: string,
|
|
1261
|
+
mode?: CompactMode,
|
|
1262
|
+
beforeFlush?: (outcome: CompactionOutcome) => void | Promise<void>,
|
|
1263
|
+
internalGuidance?: string,
|
|
1264
|
+
): Promise<CompactionOutcome> {
|
|
1265
|
+
const entries = this.ctx.sessionManager.getEntries();
|
|
1266
|
+
const messageCount = entries.filter(e => e.type === "message").length;
|
|
1267
|
+
|
|
1268
|
+
if (messageCount < 2) {
|
|
1269
|
+
this.ctx.showWarning("Nothing to compact (no messages yet)");
|
|
1270
|
+
return "ok";
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
// `internalGuidance` is a private summarizer directive (plan-mode
|
|
1274
|
+
// "Approve and compact context") that MUST stay off the public
|
|
1275
|
+
// `customInstructions` channel of the `session_before_compact` extension
|
|
1276
|
+
// hook — extensions treat that field as user focus and would otherwise
|
|
1277
|
+
// bias the summary toward the plan boilerplate (issue #4359). Ride it
|
|
1278
|
+
// through as a CompactOptions field instead.
|
|
1279
|
+
if (internalGuidance) {
|
|
1280
|
+
return this.executeCompaction({ internalGuidance, ...(mode ? { mode } : {}) }, false, beforeFlush, mode);
|
|
1281
|
+
}
|
|
1282
|
+
return this.executeCompaction(customInstructions, false, beforeFlush, mode);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
/**
|
|
1286
|
+
* TUI handler for `/shake`. `elide` drops heavy structural content and
|
|
1287
|
+
* `images` strips image blocks. Rebuilds the chat and reports counts.
|
|
1288
|
+
*/
|
|
1289
|
+
async handleShakeCommand(mode: ShakeMode): Promise<void> {
|
|
1290
|
+
let result: ShakeResult;
|
|
1291
|
+
try {
|
|
1292
|
+
result = await this.ctx.session.shake(mode);
|
|
1293
|
+
} catch (error) {
|
|
1294
|
+
this.ctx.showError(`Shake failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
const dropped = result.toolResultsDropped + result.blocksDropped + (result.imagesDropped ?? 0);
|
|
1299
|
+
if (dropped === 0) {
|
|
1300
|
+
this.ctx.showStatus("Nothing to shake.");
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
this.ctx.rebuildChatFromMessages();
|
|
1304
|
+
this.ctx.statusLine.invalidate();
|
|
1305
|
+
this.ctx.ui.requestRender();
|
|
1306
|
+
this.ctx.showStatus(formatShakeSummary(result));
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
async executeCompaction(
|
|
1310
|
+
customInstructionsOrOptions?: string | CompactOptions,
|
|
1311
|
+
isAuto = false,
|
|
1312
|
+
beforeFlush?: (outcome: CompactionOutcome) => void | Promise<void>,
|
|
1313
|
+
mode?: CompactMode,
|
|
1314
|
+
): Promise<CompactionOutcome> {
|
|
1315
|
+
if (this.ctx.loadingAnimation) {
|
|
1316
|
+
this.ctx.loadingAnimation.stop();
|
|
1317
|
+
this.ctx.loadingAnimation = undefined;
|
|
1318
|
+
}
|
|
1319
|
+
this.ctx.statusContainer.disposeChildren();
|
|
1320
|
+
|
|
1321
|
+
const label = isAuto ? "Auto-compacting context... (esc to cancel)" : "Compacting context... (esc to cancel)";
|
|
1322
|
+
const compactingLoader = new Loader(
|
|
1323
|
+
this.ctx.ui,
|
|
1324
|
+
spinner => theme.fg("accent", spinner),
|
|
1325
|
+
text => theme.fg("muted", text),
|
|
1326
|
+
label,
|
|
1327
|
+
getSymbolTheme().spinnerFrames,
|
|
1328
|
+
);
|
|
1329
|
+
this.ctx.statusContainer.addChild(compactingLoader);
|
|
1330
|
+
this.ctx.ui.requestRender();
|
|
1331
|
+
|
|
1332
|
+
let outcome: CompactionOutcome = "ok";
|
|
1333
|
+
try {
|
|
1334
|
+
const instructions = typeof customInstructionsOrOptions === "string" ? customInstructionsOrOptions : undefined;
|
|
1335
|
+
const baseOptions =
|
|
1336
|
+
customInstructionsOrOptions && typeof customInstructionsOrOptions === "object"
|
|
1337
|
+
? customInstructionsOrOptions
|
|
1338
|
+
: undefined;
|
|
1339
|
+
// The slash path passes `mode` positionally; the extension path carries
|
|
1340
|
+
// it inside the options object. Either source wins over no mode.
|
|
1341
|
+
const effectiveMode = mode ?? baseOptions?.mode;
|
|
1342
|
+
const options =
|
|
1343
|
+
baseOptions || effectiveMode
|
|
1344
|
+
? { ...baseOptions, ...(effectiveMode ? { mode: effectiveMode } : {}) }
|
|
1345
|
+
: undefined;
|
|
1346
|
+
await this.ctx.session.compact(instructions, options);
|
|
1347
|
+
|
|
1348
|
+
compactingLoader.stop();
|
|
1349
|
+
this.ctx.statusContainer.disposeChildren();
|
|
1350
|
+
this.ctx.rebuildChatFromMessages({ reuseSettledComponents: true });
|
|
1351
|
+
|
|
1352
|
+
this.ctx.statusLine.invalidate();
|
|
1353
|
+
// Same as the auto-compaction rebuild: a collapsed transcript is an
|
|
1354
|
+
// intentional replacement, so drop the stale pre-compaction scrollback
|
|
1355
|
+
// instead of repainting the shrunken frame below it. With collapse
|
|
1356
|
+
// disabled the full history stays inline and scrollback is kept.
|
|
1357
|
+
if (this.ctx.settings.get("display.collapseCompacted")) {
|
|
1358
|
+
this.ctx.ui.requestRender(true, { clearScrollback: true });
|
|
1359
|
+
} else {
|
|
1360
|
+
this.ctx.ui.requestRender();
|
|
1361
|
+
}
|
|
1362
|
+
} catch (error) {
|
|
1363
|
+
if (error instanceof CompactionCancelledError) {
|
|
1364
|
+
outcome = "cancelled";
|
|
1365
|
+
this.ctx.showError("Compaction cancelled");
|
|
1366
|
+
} else {
|
|
1367
|
+
outcome = "failed";
|
|
1368
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1369
|
+
this.ctx.showError(`Compaction failed: ${message}`);
|
|
1370
|
+
}
|
|
1371
|
+
} finally {
|
|
1372
|
+
compactingLoader.stop();
|
|
1373
|
+
this.ctx.statusContainer.disposeChildren();
|
|
1374
|
+
}
|
|
1375
|
+
// Run the caller's pre-flush hook (e.g. the plan-approval model transition)
|
|
1376
|
+
// before queued user input is dispatched, so any turn queued during
|
|
1377
|
+
// compaction executes on the post-compaction model rather than the model
|
|
1378
|
+
// compaction itself ran on.
|
|
1379
|
+
if (beforeFlush) await beforeFlush(outcome);
|
|
1380
|
+
await this.ctx.flushCompactionQueue({ willRetry: false });
|
|
1381
|
+
return outcome;
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
async handleHandoffCommand(customInstructions?: string): Promise<void> {
|
|
1385
|
+
if (this.ctx.session.isStreaming) {
|
|
1386
|
+
this.ctx.showWarning("Wait for the current response to finish or abort it before handing off.");
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
const entries = this.ctx.sessionManager.getEntries();
|
|
1391
|
+
const messageCount = entries.filter(e => e.type === "message").length;
|
|
1392
|
+
|
|
1393
|
+
if (messageCount < 2) {
|
|
1394
|
+
this.ctx.showWarning("Nothing to hand off (no messages yet)");
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
if (this.ctx.loadingAnimation) {
|
|
1399
|
+
this.ctx.loadingAnimation.stop();
|
|
1400
|
+
this.ctx.loadingAnimation = undefined;
|
|
1401
|
+
}
|
|
1402
|
+
this.ctx.statusContainer.disposeChildren();
|
|
1403
|
+
|
|
1404
|
+
const handoffLoader = new Loader(
|
|
1405
|
+
this.ctx.ui,
|
|
1406
|
+
spinner => theme.fg("accent", spinner),
|
|
1407
|
+
text => theme.fg("muted", text),
|
|
1408
|
+
"Generating handoff… (esc to cancel)",
|
|
1409
|
+
getSymbolTheme().spinnerFrames,
|
|
1410
|
+
);
|
|
1411
|
+
this.ctx.statusContainer.addChild(handoffLoader);
|
|
1412
|
+
this.ctx.ui.requestRender();
|
|
1413
|
+
|
|
1414
|
+
try {
|
|
1415
|
+
// Handoff generation runs as a oneshot request; the new session is shown after it completes.
|
|
1416
|
+
const result = await this.ctx.session.handoff(customInstructions);
|
|
1417
|
+
|
|
1418
|
+
if (!result) {
|
|
1419
|
+
this.ctx.showError("Handoff cancelled");
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
// Rebuild chat from the new session (which now contains the handoff document).
|
|
1424
|
+
this.ctx.clearTransientSessionUi();
|
|
1425
|
+
this.ctx.renderInitialMessages();
|
|
1426
|
+
this.ctx.statusLine.invalidate();
|
|
1427
|
+
this.ctx.updateEditorBorderColor();
|
|
1428
|
+
await this.ctx.reloadTodos();
|
|
1429
|
+
|
|
1430
|
+
this.ctx.present([
|
|
1431
|
+
new Spacer(1),
|
|
1432
|
+
new Text(`${theme.fg("accent", `${theme.status.success} New session started with handoff context`)}`, 1, 1),
|
|
1433
|
+
]);
|
|
1434
|
+
if (result.savedPath) {
|
|
1435
|
+
this.ctx.showStatus(`Handoff document saved to: ${result.savedPath}`);
|
|
1436
|
+
}
|
|
1437
|
+
} catch (error) {
|
|
1438
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1439
|
+
if (message === "Handoff cancelled" || (error instanceof Error && error.name === "AbortError")) {
|
|
1440
|
+
this.ctx.showError("Handoff cancelled");
|
|
1441
|
+
} else {
|
|
1442
|
+
this.ctx.showError(`Handoff failed: ${message}`);
|
|
1443
|
+
}
|
|
1444
|
+
} finally {
|
|
1445
|
+
handoffLoader.stop();
|
|
1446
|
+
this.ctx.statusContainer.disposeChildren();
|
|
1447
|
+
}
|
|
1448
|
+
this.ctx.ui.requestRender(true, { clearScrollback: true });
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
const BAR_WIDTH_MAX = 24;
|
|
1453
|
+
const COLUMN_WIDTH_MIN = 4;
|
|
1454
|
+
|
|
1455
|
+
function renderJobLine(job: AsyncJobSnapshotItem, now: number): string {
|
|
1456
|
+
const duration = formatDuration(Math.max(0, now - job.startTime));
|
|
1457
|
+
const status = formatJobStatus(job.status);
|
|
1458
|
+
return `${theme.fg("dim", job.id)} ${theme.fg("dim", `[${job.type}]`)} ${status} ${theme.fg("dim", `(${duration})`)}`;
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
function formatJobStatus(status: AsyncJobSnapshotItem["status"]): string {
|
|
1462
|
+
if (status === "running") return theme.fg("warning", "running");
|
|
1463
|
+
if (status === "completed") return theme.fg("success", "completed");
|
|
1464
|
+
if (status === "cancelled") return theme.fg("dim", "cancelled");
|
|
1465
|
+
return theme.fg("error", "failed");
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
function truncateJobLabel(label: string, maxWidth: number): string {
|
|
1469
|
+
if (visibleWidth(label) <= maxWidth) return label;
|
|
1470
|
+
if (maxWidth <= 1) return "…";
|
|
1471
|
+
|
|
1472
|
+
let out = "";
|
|
1473
|
+
for (const char of label) {
|
|
1474
|
+
const next = `${out}${char}`;
|
|
1475
|
+
if (visibleWidth(`${next}…`) > maxWidth) break;
|
|
1476
|
+
out = next;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
return `${out}…`;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
function formatProviderName(provider: string): string {
|
|
1483
|
+
return provider
|
|
1484
|
+
.split(/[-_]/g)
|
|
1485
|
+
.map(part => (part ? part[0].toUpperCase() + part.slice(1) : ""))
|
|
1486
|
+
.join(" ");
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
function formatNumber(value: number, maxFractionDigits = 1): string {
|
|
1490
|
+
return new Intl.NumberFormat("en-US", { maximumFractionDigits: maxFractionDigits }).format(value);
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
function resolveProviderAuthMode(authStorage: AuthStorage, provider: string): string {
|
|
1494
|
+
if (authStorage.hasOAuth(provider)) {
|
|
1495
|
+
return "oauth";
|
|
1496
|
+
}
|
|
1497
|
+
if (authStorage.has(provider)) {
|
|
1498
|
+
return "api key";
|
|
1499
|
+
}
|
|
1500
|
+
if (getEnvApiKey(provider)) {
|
|
1501
|
+
return "env api key";
|
|
1502
|
+
}
|
|
1503
|
+
if (authStorage.hasAuth(provider)) {
|
|
1504
|
+
return "runtime/fallback";
|
|
1505
|
+
}
|
|
1506
|
+
return "unknown";
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
export function renderProviderSection(details: ProviderDetails, uiTheme: Pick<typeof theme, "fg">): string {
|
|
1510
|
+
const lines: string[] = [];
|
|
1511
|
+
lines.push(`${uiTheme.fg("dim", "Name:")} ${details.provider}`);
|
|
1512
|
+
for (const field of details.fields) {
|
|
1513
|
+
lines.push(`${uiTheme.fg("dim", `${field.label}:`)} ${field.value}`);
|
|
1514
|
+
}
|
|
1515
|
+
return `${lines.join("\n")}\n`;
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
function resolveProviderUsageTotal(reports: UsageReport[]): number {
|
|
1519
|
+
return reports
|
|
1520
|
+
.flatMap(report => report.limits)
|
|
1521
|
+
.map(limit => resolveUsedFraction(limit) ?? 0)
|
|
1522
|
+
.reduce((sum, value) => sum + value, 0);
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
function formatLimitTitle(limit: UsageLimit): string {
|
|
1526
|
+
const tier = limit.scope.tier;
|
|
1527
|
+
if (tier && !limit.label.toLowerCase().includes(tier.toLowerCase())) {
|
|
1528
|
+
return `${limit.label} (${tier})`;
|
|
1529
|
+
}
|
|
1530
|
+
return limit.label;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
function formatWindowSuffix(label: string, windowLabel: string, uiTheme: typeof theme): string {
|
|
1534
|
+
const normalizedLabel = label.toLowerCase();
|
|
1535
|
+
const normalizedWindow = windowLabel.toLowerCase();
|
|
1536
|
+
if (normalizedWindow === "quota window") return "";
|
|
1537
|
+
if (normalizedLabel.includes(normalizedWindow)) return "";
|
|
1538
|
+
return uiTheme.fg("dim", `(${windowLabel})`);
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
/** ` (org)` suffix when the report is org-attributed — two subscriptions can share one email. */
|
|
1542
|
+
function orgSuffix(report: UsageReport): string {
|
|
1543
|
+
const orgName = report.metadata?.orgName;
|
|
1544
|
+
const orgId = report.metadata?.orgId;
|
|
1545
|
+
const org = typeof orgName === "string" && orgName ? orgName : typeof orgId === "string" ? orgId : undefined;
|
|
1546
|
+
return org ? ` (${org})` : "";
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
function formatAccountLabel(limit: UsageLimit, report: UsageReport, index: number): string {
|
|
1550
|
+
const email = report.metadata?.email;
|
|
1551
|
+
if (typeof email === "string" && email) return `${email}${orgSuffix(report)}`;
|
|
1552
|
+
const accountId =
|
|
1553
|
+
typeof report.metadata?.accountId === "string" && report.metadata.accountId
|
|
1554
|
+
? report.metadata.accountId
|
|
1555
|
+
: limit.scope.accountId || undefined;
|
|
1556
|
+
if (accountId) return `${accountId}${orgSuffix(report)}`;
|
|
1557
|
+
const projectId =
|
|
1558
|
+
typeof report.metadata?.projectId === "string" && report.metadata.projectId
|
|
1559
|
+
? report.metadata.projectId
|
|
1560
|
+
: limit.scope.projectId || undefined;
|
|
1561
|
+
if (projectId) return projectId;
|
|
1562
|
+
return `account ${index + 1}`;
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
function formatUnlimitedReportLabel(report: UsageReport, index: number): string {
|
|
1566
|
+
const email = report.metadata?.email;
|
|
1567
|
+
if (typeof email === "string" && email) return `${email}${orgSuffix(report)}`;
|
|
1568
|
+
const accountId = report.metadata?.accountId;
|
|
1569
|
+
if (typeof accountId === "string" && accountId) return `${accountId}${orgSuffix(report)}`;
|
|
1570
|
+
const projectId = report.metadata?.projectId;
|
|
1571
|
+
if (typeof projectId === "string" && projectId) return projectId;
|
|
1572
|
+
return `account ${index + 1}`;
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
function formatResetShort(limit: UsageLimit, nowMs: number): string | undefined {
|
|
1576
|
+
const resetsAt = limit.window?.resetsAt;
|
|
1577
|
+
if (resetsAt === undefined) return undefined;
|
|
1578
|
+
// Codex returns the prior window's reset_at until a new request opens a fresh window —
|
|
1579
|
+
// rendering a negative delta is meaningless, so drop the suffix in that case.
|
|
1580
|
+
if (resetsAt <= nowMs) return undefined;
|
|
1581
|
+
return formatDuration(resetsAt - nowMs);
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
function formatAccountHeaderRow(
|
|
1585
|
+
limits: UsageLimit[],
|
|
1586
|
+
reports: UsageReport[],
|
|
1587
|
+
nowMs: number,
|
|
1588
|
+
columnWidth: number,
|
|
1589
|
+
uiTheme: typeof theme,
|
|
1590
|
+
activeAccount?: OAuthAccountIdentity,
|
|
1591
|
+
): string[] {
|
|
1592
|
+
const parts = limits.map((limit, index) => {
|
|
1593
|
+
const reset = formatResetShort(limit, nowMs);
|
|
1594
|
+
const report = reports[index];
|
|
1595
|
+
const active = report !== undefined && limitMatchesActiveAccount(report, limit, activeAccount);
|
|
1596
|
+
const label = formatAccountLabel(limit, report, index);
|
|
1597
|
+
return {
|
|
1598
|
+
label: active ? `● ${label}` : label,
|
|
1599
|
+
suffix: reset ? `(${reset})` : "",
|
|
1600
|
+
active,
|
|
1601
|
+
};
|
|
1602
|
+
});
|
|
1603
|
+
const maxSuffixWidth = parts.reduce((max, p) => Math.max(max, visibleWidth(p.suffix)), 0);
|
|
1604
|
+
const gap = maxSuffixWidth > 0 ? 1 : 0;
|
|
1605
|
+
const prefixBudget = columnWidth - maxSuffixWidth - gap;
|
|
1606
|
+
|
|
1607
|
+
// If suffix can't share the cell with at least `x…`, fall back to whole-label truncation.
|
|
1608
|
+
if (prefixBudget < 2) {
|
|
1609
|
+
return parts.map(p => {
|
|
1610
|
+
const full = p.suffix ? `${p.label} ${p.suffix}` : p.label;
|
|
1611
|
+
const cell = padColumn(truncateJobLabel(full, columnWidth), columnWidth);
|
|
1612
|
+
return p.active ? uiTheme.fg("accent", cell) : cell;
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
return parts.map(p => {
|
|
1617
|
+
const prefix = truncateJobLabel(p.label, prefixBudget);
|
|
1618
|
+
const prefixCell = prefix + " ".repeat(prefixBudget - visibleWidth(prefix));
|
|
1619
|
+
const styledPrefix = p.active ? uiTheme.fg("accent", prefixCell) : prefixCell;
|
|
1620
|
+
if (!p.suffix) return styledPrefix + " ".repeat(maxSuffixWidth + gap);
|
|
1621
|
+
const suffixPad = " ".repeat(maxSuffixWidth - visibleWidth(p.suffix));
|
|
1622
|
+
return `${styledPrefix} ${suffixPad}${uiTheme.fg("dim", p.suffix)}`;
|
|
1623
|
+
});
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
function padColumn(text: string, width: number): string {
|
|
1627
|
+
const visible = visibleWidth(text);
|
|
1628
|
+
if (visible >= width) return text;
|
|
1629
|
+
return `${text}${padding(width - visible)}`;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
type AggregateDisplayStatus = NonNullable<UsageLimit["status"]> | "neutral";
|
|
1633
|
+
|
|
1634
|
+
function isUsedOnlyAbsoluteAmount(limit: UsageLimit): boolean {
|
|
1635
|
+
const amount = limit.amount;
|
|
1636
|
+
return (
|
|
1637
|
+
amount.unit !== "percent" &&
|
|
1638
|
+
amount.unit !== "unknown" &&
|
|
1639
|
+
amount.used !== undefined &&
|
|
1640
|
+
Number.isFinite(amount.used) &&
|
|
1641
|
+
amount.limit === undefined &&
|
|
1642
|
+
amount.remaining === undefined &&
|
|
1643
|
+
resolveUsedFraction(limit) === undefined
|
|
1644
|
+
);
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
function resolveAggregateStatus(limits: UsageLimit[]): AggregateDisplayStatus {
|
|
1648
|
+
const hasOk = limits.some(limit => limit.status === "ok");
|
|
1649
|
+
const hasWarning = limits.some(limit => limit.status === "warning");
|
|
1650
|
+
const hasExhausted = limits.some(limit => limit.status === "exhausted");
|
|
1651
|
+
if (!hasOk && !hasWarning && !hasExhausted) {
|
|
1652
|
+
return limits.length > 0 && limits.every(isUsedOnlyAbsoluteAmount) ? "neutral" : "unknown";
|
|
1653
|
+
}
|
|
1654
|
+
if (hasOk) {
|
|
1655
|
+
return hasWarning || hasExhausted ? "warning" : "ok";
|
|
1656
|
+
}
|
|
1657
|
+
if (hasWarning) return "warning";
|
|
1658
|
+
return "exhausted";
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
function formatAggregateAmount(limits: UsageLimit[]): string {
|
|
1662
|
+
const fractions = limits
|
|
1663
|
+
.map(limit => resolveUsedFraction(limit))
|
|
1664
|
+
.filter((value): value is number => value !== undefined);
|
|
1665
|
+
if (fractions.length === limits.length && fractions.length > 0) {
|
|
1666
|
+
const sum = fractions.reduce((total, value) => total + value, 0);
|
|
1667
|
+
const avgRemaining = Math.max(0, ((limits.length - sum) / limits.length) * 100);
|
|
1668
|
+
return `${formatNumber(avgRemaining)}% free`;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
const amounts = limits
|
|
1672
|
+
.map(limit => limit.amount)
|
|
1673
|
+
.filter(amount => amount.used !== undefined && amount.limit !== undefined && amount.limit > 0);
|
|
1674
|
+
if (amounts.length === limits.length && amounts.length > 0) {
|
|
1675
|
+
const totalUsed = amounts.reduce((sum, amount) => sum + (amount.used ?? 0), 0);
|
|
1676
|
+
const totalLimit = amounts.reduce((sum, amount) => sum + (amount.limit ?? 0), 0);
|
|
1677
|
+
const remainingPct = totalLimit > 0 ? Math.max(0, 100 - (totalUsed / totalLimit) * 100) : 0;
|
|
1678
|
+
return `${formatNumber(remainingPct)}% free`;
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
if (limits.length > 0 && limits.every(isUsedOnlyAbsoluteAmount)) return "";
|
|
1682
|
+
|
|
1683
|
+
// Count unique accounts from limit scopes — not limits.length.
|
|
1684
|
+
const uniqueAccountIds = new Set(
|
|
1685
|
+
limits.map(limit => limit.scope.accountId).filter((id): id is string => typeof id === "string" && id.length > 0),
|
|
1686
|
+
);
|
|
1687
|
+
if (uniqueAccountIds.size > 0) return `${uniqueAccountIds.size} ${uniqueAccountIds.size === 1 ? "acct" : "accts"}`;
|
|
1688
|
+
// No account IDs available — keep the pre-existing fallback so providers
|
|
1689
|
+
// that don't populate scope.accountId still show a summary.
|
|
1690
|
+
return `${limits.length} accts`;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
function resolveResetRange(limits: UsageLimit[], nowMs: number): string | null {
|
|
1694
|
+
const windows = limits
|
|
1695
|
+
.map(limit => limit.window)
|
|
1696
|
+
.filter(
|
|
1697
|
+
(window): window is NonNullable<UsageLimit["window"]> =>
|
|
1698
|
+
window?.resetsAt !== undefined && Number.isFinite(window.resetsAt) && window.resetsAt > nowMs,
|
|
1699
|
+
);
|
|
1700
|
+
if (windows.length === 0) return null;
|
|
1701
|
+
// Use the shared verb when every contributing window agrees (e.g. all "tick");
|
|
1702
|
+
// mixed or absent labels fall back to the generic "resets".
|
|
1703
|
+
const labels = new Set(windows.map(window => window.resetLabel ?? "resets"));
|
|
1704
|
+
const verb = labels.size === 1 ? [...labels][0]! : "resets";
|
|
1705
|
+
const offsets = windows.map(window => window.resetsAt! - nowMs);
|
|
1706
|
+
const minReset = Math.min(...offsets);
|
|
1707
|
+
const maxReset = Math.max(...offsets);
|
|
1708
|
+
if (maxReset - minReset > 60_000) {
|
|
1709
|
+
return `${verb} in ${formatDuration(minReset)}–${formatDuration(maxReset)}`;
|
|
1710
|
+
}
|
|
1711
|
+
return `${verb} in ${formatDuration(minReset)}`;
|
|
1712
|
+
}
|
|
1713
|
+
/**
|
|
1714
|
+
* Compact one-line quota summary for a single advisor's provider.
|
|
1715
|
+
* Returns `null` when the provider has no usage data.
|
|
1716
|
+
* When `activeAccount` is provided, only limits matching that credential
|
|
1717
|
+
* are shown (mirrors `renderUsageReports`'s account-stickiness filtering).
|
|
1718
|
+
* Example output: `Quota: 7d window · 67% used · resets in 3.2d`
|
|
1719
|
+
*/
|
|
1720
|
+
export function formatCompactQuota(
|
|
1721
|
+
provider: string,
|
|
1722
|
+
reports: UsageReport[],
|
|
1723
|
+
nowMs: number,
|
|
1724
|
+
activeAccount?: OAuthAccountIdentity,
|
|
1725
|
+
): string | null {
|
|
1726
|
+
const providerReports = reports.filter(r => r.provider === provider);
|
|
1727
|
+
if (providerReports.length === 0) return null;
|
|
1728
|
+
// Group limits by window id so we show BOTH the 5-hour and 7-day windows
|
|
1729
|
+
// (or any other distinct windows the provider exposes). Within each window,
|
|
1730
|
+
// pick the highest used fraction across accounts — that's the most pressing.
|
|
1731
|
+
const byWindow = new Map<string, { limit: UsageLimit; fraction: number }>();
|
|
1732
|
+
for (const report of providerReports) {
|
|
1733
|
+
for (const limit of report.limits) {
|
|
1734
|
+
// Skip limits that belong to a different credential than the one
|
|
1735
|
+
// the advisor is actually using, so we don't alarm the user with
|
|
1736
|
+
// an exhausted account that isn't theirs.
|
|
1737
|
+
if (activeAccount && !limitMatchesActiveAccount(report, limit, activeAccount)) continue;
|
|
1738
|
+
const fraction = resolveUsedFraction(limit);
|
|
1739
|
+
if (fraction === undefined) continue;
|
|
1740
|
+
const key = limit.window?.id ?? limit.scope.windowId ?? "—";
|
|
1741
|
+
const existing = byWindow.get(key);
|
|
1742
|
+
if (!existing || fraction > existing.fraction) byWindow.set(key, { limit, fraction });
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
if (byWindow.size === 0) return null;
|
|
1746
|
+
// Sort windows by urgency (highest fraction first) so the most pressing
|
|
1747
|
+
// quota is always the first thing the user sees.
|
|
1748
|
+
const entries = [...byWindow.values()].sort((a, b) => b.fraction - a.fraction);
|
|
1749
|
+
const lines: string[] = [];
|
|
1750
|
+
for (const { limit, fraction } of entries) {
|
|
1751
|
+
const pct = Math.round(fraction * 100);
|
|
1752
|
+
const windowLabel = limit.window?.label ?? limit.scope.windowId ?? "—";
|
|
1753
|
+
// Include the limit label (account/tier) when it carries identity beyond
|
|
1754
|
+
// the window name, so the user can tell which credential's quota is shown.
|
|
1755
|
+
const identity = limit.label.trim();
|
|
1756
|
+
const header = identity && identity !== windowLabel ? `${windowLabel} (${identity})` : windowLabel;
|
|
1757
|
+
const parts = [`${header}: ${pct}% used`];
|
|
1758
|
+
const reset = resolveResetRange([limit], nowMs);
|
|
1759
|
+
if (reset) parts.push(reset);
|
|
1760
|
+
lines.push(parts.join(" · "));
|
|
1761
|
+
}
|
|
1762
|
+
return `Quota: ${lines.join(" │ ")}`;
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
function resolveStatusIcon(status: AggregateDisplayStatus, uiTheme: typeof theme): string {
|
|
1766
|
+
if (status === "neutral") return uiTheme.fg("dim", uiTheme.status.info);
|
|
1767
|
+
if (status === "exhausted") return uiTheme.fg("error", uiTheme.status.error);
|
|
1768
|
+
if (status === "warning") return uiTheme.fg("warning", uiTheme.status.warning);
|
|
1769
|
+
if (status === "ok") return uiTheme.fg("success", uiTheme.status.success);
|
|
1770
|
+
return uiTheme.fg("dim", uiTheme.status.pending);
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
function resolveStatusColor(status: UsageLimit["status"]): "success" | "warning" | "error" | "dim" {
|
|
1774
|
+
if (status === "exhausted") return "error";
|
|
1775
|
+
if (status === "warning") return "warning";
|
|
1776
|
+
if (status === "ok") return "success";
|
|
1777
|
+
return "dim";
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
function renderUsageBar(limit: UsageLimit, uiTheme: typeof theme, barWidth: number): string {
|
|
1781
|
+
const usedAmount = limit.amount.used;
|
|
1782
|
+
if (usedAmount !== undefined && isUsedOnlyAbsoluteAmount(limit)) {
|
|
1783
|
+
const used =
|
|
1784
|
+
limit.amount.unit === "usd"
|
|
1785
|
+
? `$${usedAmount.toFixed(2)}`
|
|
1786
|
+
: `${formatNumber(usedAmount, 2)} ${limit.amount.unit}`;
|
|
1787
|
+
return uiTheme.fg("dim", truncateJobLabel(`${used} used`, barWidth));
|
|
1788
|
+
}
|
|
1789
|
+
const fraction = resolveUsedFraction(limit);
|
|
1790
|
+
if (fraction === undefined) {
|
|
1791
|
+
return uiTheme.fg("dim", "·".repeat(barWidth));
|
|
1792
|
+
}
|
|
1793
|
+
const clamped = Math.min(Math.max(fraction, 0), 1);
|
|
1794
|
+
const exact = clamped * barWidth;
|
|
1795
|
+
const fullCells = Math.floor(exact);
|
|
1796
|
+
const remainder = exact - fullCells;
|
|
1797
|
+
let partial = "";
|
|
1798
|
+
if (remainder >= 2 / 3) partial = "▓";
|
|
1799
|
+
else if (remainder >= 1 / 3) partial = "▒";
|
|
1800
|
+
const leading = "█".repeat(fullCells) + partial;
|
|
1801
|
+
const empty = "░".repeat(Math.max(0, barWidth - fullCells - (partial ? 1 : 0)));
|
|
1802
|
+
const color = resolveStatusColor(limit.status);
|
|
1803
|
+
return `${uiTheme.fg(color, leading)}${uiTheme.fg("dim", empty)}`;
|
|
1804
|
+
}
|
|
1805
|
+
|
|
1806
|
+
/**
|
|
1807
|
+
* Pick a per-account column width so the columns and trailing amount fit in `available`.
|
|
1808
|
+
* Falls back to the minimum when the terminal is too narrow rather than wrapping.
|
|
1809
|
+
*/
|
|
1810
|
+
function resolveColumnWidth(count: number, available: number, trailing: number): number {
|
|
1811
|
+
if (count <= 0) return BAR_WIDTH_MAX;
|
|
1812
|
+
const indent = 2;
|
|
1813
|
+
const gaps = count - 1;
|
|
1814
|
+
const spaceForBars = available - indent - gaps - (trailing > 0 ? trailing + 1 : 0);
|
|
1815
|
+
const ideal = Math.floor(spaceForBars / count);
|
|
1816
|
+
if (ideal < COLUMN_WIDTH_MIN) return COLUMN_WIDTH_MIN;
|
|
1817
|
+
return ideal;
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
export function renderUsageReports(
|
|
1821
|
+
reports: UsageReport[],
|
|
1822
|
+
uiTheme: typeof theme,
|
|
1823
|
+
nowMs: number,
|
|
1824
|
+
availableWidth: number,
|
|
1825
|
+
resolveActiveAccount?: (provider: string) => OAuthAccountIdentity | undefined,
|
|
1826
|
+
usageModelSelectors: readonly string[] = [],
|
|
1827
|
+
): string {
|
|
1828
|
+
const lines: string[] = [];
|
|
1829
|
+
const latestFetchedAt = Math.max(...reports.map(report => report.fetchedAt ?? 0));
|
|
1830
|
+
const headerSuffix = latestFetchedAt ? ` (${formatDuration(nowMs - latestFetchedAt)} ago)` : "";
|
|
1831
|
+
lines.push(uiTheme.bold(uiTheme.fg("accent", `Usage${headerSuffix}`)));
|
|
1832
|
+
const grouped = new Map<string, UsageReport[]>();
|
|
1833
|
+
for (const report of reports) {
|
|
1834
|
+
const list = grouped.get(report.provider) ?? [];
|
|
1835
|
+
list.push(report);
|
|
1836
|
+
grouped.set(report.provider, list);
|
|
1837
|
+
}
|
|
1838
|
+
const providerEntries = Array.from(grouped.entries())
|
|
1839
|
+
.map(([provider, providerReports]) => ({
|
|
1840
|
+
provider,
|
|
1841
|
+
providerReports,
|
|
1842
|
+
totalUsage: resolveProviderUsageTotal(providerReports),
|
|
1843
|
+
}))
|
|
1844
|
+
.sort((a, b) => {
|
|
1845
|
+
if (a.totalUsage !== b.totalUsage) return a.totalUsage - b.totalUsage;
|
|
1846
|
+
return a.provider.localeCompare(b.provider);
|
|
1847
|
+
});
|
|
1848
|
+
|
|
1849
|
+
for (const { provider, providerReports } of providerEntries) {
|
|
1850
|
+
lines.push("");
|
|
1851
|
+
const providerName = formatProviderName(provider);
|
|
1852
|
+
const activeAccount = resolveActiveAccount?.(provider);
|
|
1853
|
+
|
|
1854
|
+
const limitGroups = new Map<
|
|
1855
|
+
string,
|
|
1856
|
+
{ label: string; windowLabel: string; limits: UsageLimit[]; reports: UsageReport[] }
|
|
1857
|
+
>();
|
|
1858
|
+
for (const report of providerReports) {
|
|
1859
|
+
for (const limit of report.limits) {
|
|
1860
|
+
const windowId = limit.window?.id ?? limit.scope.windowId ?? "default";
|
|
1861
|
+
const key = `${formatLimitTitle(limit)}|${windowId}`;
|
|
1862
|
+
const windowLabel = limit.window?.label ?? windowId;
|
|
1863
|
+
const entry = limitGroups.get(key) ?? {
|
|
1864
|
+
label: formatLimitTitle(limit),
|
|
1865
|
+
windowLabel,
|
|
1866
|
+
limits: [],
|
|
1867
|
+
reports: [],
|
|
1868
|
+
};
|
|
1869
|
+
entry.limits.push(limit);
|
|
1870
|
+
entry.reports.push(report);
|
|
1871
|
+
limitGroups.set(key, entry);
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
lines.push(uiTheme.bold(uiTheme.fg("accent", providerName)));
|
|
1876
|
+
const activeAccountLabel = formatActiveAccountLabel(activeAccount);
|
|
1877
|
+
if (activeAccountLabel) {
|
|
1878
|
+
lines.push(` ${uiTheme.fg("accent", "in use by this session:")} ${activeAccountLabel}`);
|
|
1879
|
+
}
|
|
1880
|
+
const reportingModels = usageModelSelectors.filter(selector => selector.startsWith(`${provider}/`));
|
|
1881
|
+
if (reportingModels.length > 0) {
|
|
1882
|
+
lines.push(` ${uiTheme.fg("accent", "Models with usage data")}`);
|
|
1883
|
+
for (const selector of reportingModels) {
|
|
1884
|
+
lines.push(` ${replaceTabs(truncateToWidth(sanitizeText(selector), availableWidth - 4))}`);
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
// Provider-wide disclaimers (e.g. "OMP-observed spend only") render once
|
|
1889
|
+
// above the per-account sections instead of duplicating onto every limit.
|
|
1890
|
+
const providerNotes = [...new Set(providerReports.flatMap(report => report.notes ?? []))];
|
|
1891
|
+
if (providerNotes.length > 0) {
|
|
1892
|
+
lines.push(
|
|
1893
|
+
` ${uiTheme.fg("dim", replaceTabs(truncateToWidth(sanitizeText(providerNotes.map(n => n.replace(/[\r\n]+/g, " ")).join(" • ")), 110)))}`.trimEnd(),
|
|
1894
|
+
);
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
const resetAccountLines: string[] = [];
|
|
1898
|
+
for (const report of providerReports) {
|
|
1899
|
+
const count = report.resetCredits?.availableCount ?? 0;
|
|
1900
|
+
if (count <= 0) continue;
|
|
1901
|
+
const label =
|
|
1902
|
+
typeof report.metadata?.email === "string" && report.metadata.email
|
|
1903
|
+
? report.metadata.email
|
|
1904
|
+
: typeof report.metadata?.accountId === "string" && report.metadata.accountId
|
|
1905
|
+
? report.metadata.accountId
|
|
1906
|
+
: "account";
|
|
1907
|
+
const isActive =
|
|
1908
|
+
!!activeAccount &&
|
|
1909
|
+
((!!activeAccount.accountId && activeAccount.accountId === report.metadata?.accountId) ||
|
|
1910
|
+
(!!activeAccount.email && activeAccount.email === report.metadata?.email));
|
|
1911
|
+
resetAccountLines.push(
|
|
1912
|
+
` • ${label}: ${count} saved reset${count === 1 ? "" : "s"}${isActive ? " (active)" : ""}`,
|
|
1913
|
+
);
|
|
1914
|
+
const credits = report.resetCredits?.credits;
|
|
1915
|
+
if (credits) {
|
|
1916
|
+
for (const credit of credits) {
|
|
1917
|
+
if (credit.expiresAt) {
|
|
1918
|
+
const expiryMs = Date.parse(credit.expiresAt);
|
|
1919
|
+
if (!Number.isNaN(expiryMs)) {
|
|
1920
|
+
const remaining = expiryMs - nowMs;
|
|
1921
|
+
const expiryDate = credit.expiresAt.slice(0, 10);
|
|
1922
|
+
if (remaining > 0) {
|
|
1923
|
+
resetAccountLines.push(` expires in ${formatDuration(remaining)} (${expiryDate})`);
|
|
1924
|
+
} else {
|
|
1925
|
+
resetAccountLines.push(` expired (${expiryDate})`);
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
if (resetAccountLines.length > 0) {
|
|
1933
|
+
lines.push(
|
|
1934
|
+
` ${uiTheme.fg("accent", "Saved rate-limit resets")} ${uiTheme.fg("dim", "(/usage reset to spend)")}`,
|
|
1935
|
+
);
|
|
1936
|
+
for (const line of resetAccountLines) lines.push(uiTheme.fg("dim", line));
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
// Order account columns ONCE per provider (worst-first), then apply that
|
|
1940
|
+
// same order to every window group. Sorting each group independently by
|
|
1941
|
+
// its own used fraction (issue #6067) desynchronized the columns: an
|
|
1942
|
+
// account exhausted on its 5h window but light on the weekly window would
|
|
1943
|
+
// land in different column positions on each row, so the positional
|
|
1944
|
+
// `account N` labels denoted different credentials per row and an
|
|
1945
|
+
// exhausted limit appeared under a sibling that still had quota.
|
|
1946
|
+
const accountRank = new Map<UsageReport, number>();
|
|
1947
|
+
providerReports.forEach((report, position) => {
|
|
1948
|
+
const worst = report.limits.reduce((max, limit) => {
|
|
1949
|
+
const fraction = resolveUsedFraction(limit) ?? -1;
|
|
1950
|
+
return fraction > max ? fraction : max;
|
|
1951
|
+
}, -1);
|
|
1952
|
+
// Encode worst-first primary key with the stable position as tiebreak
|
|
1953
|
+
// so accounts tied on pressure keep their discovery order.
|
|
1954
|
+
accountRank.set(report, -worst * 1000 + position);
|
|
1955
|
+
});
|
|
1956
|
+
|
|
1957
|
+
const renderableGroups = Array.from(limitGroups.values()).map(group => {
|
|
1958
|
+
const entries = group.limits.map((limit, index) => ({
|
|
1959
|
+
limit,
|
|
1960
|
+
report: group.reports[index],
|
|
1961
|
+
index,
|
|
1962
|
+
}));
|
|
1963
|
+
entries.sort((a, b) => {
|
|
1964
|
+
const aRank = accountRank.get(a.report) ?? a.index;
|
|
1965
|
+
const bRank = accountRank.get(b.report) ?? b.index;
|
|
1966
|
+
if (aRank !== bRank) return aRank - bRank;
|
|
1967
|
+
return a.index - b.index;
|
|
1968
|
+
});
|
|
1969
|
+
const sortedLimits = entries.map(entry => entry.limit);
|
|
1970
|
+
const sortedReports = entries.map(entry => entry.report);
|
|
1971
|
+
return { group, sortedLimits, sortedReports, amountText: formatAggregateAmount(sortedLimits) };
|
|
1972
|
+
});
|
|
1973
|
+
|
|
1974
|
+
const sectionCount = renderableGroups.reduce((max, g) => Math.max(max, g.sortedLimits.length), 0);
|
|
1975
|
+
const sectionTrailing = renderableGroups.reduce((max, g) => Math.max(max, visibleWidth(g.amountText)), 0);
|
|
1976
|
+
const sectionColumnWidth = resolveColumnWidth(sectionCount, availableWidth, sectionTrailing);
|
|
1977
|
+
const sectionBarWidth = Math.min(sectionColumnWidth, BAR_WIDTH_MAX);
|
|
1978
|
+
|
|
1979
|
+
for (const { group, sortedLimits, sortedReports, amountText } of renderableGroups) {
|
|
1980
|
+
const status = resolveAggregateStatus(sortedLimits);
|
|
1981
|
+
const statusIcon = resolveStatusIcon(status, uiTheme);
|
|
1982
|
+
|
|
1983
|
+
const windowSuffix = formatWindowSuffix(group.label, group.windowLabel, uiTheme);
|
|
1984
|
+
lines.push(`${statusIcon} ${uiTheme.bold(group.label)} ${windowSuffix}`.trim());
|
|
1985
|
+
const accountLabels = formatAccountHeaderRow(
|
|
1986
|
+
sortedLimits,
|
|
1987
|
+
sortedReports,
|
|
1988
|
+
nowMs,
|
|
1989
|
+
sectionColumnWidth,
|
|
1990
|
+
uiTheme,
|
|
1991
|
+
activeAccount,
|
|
1992
|
+
);
|
|
1993
|
+
lines.push(` ${accountLabels.join(" ")}`.trimEnd());
|
|
1994
|
+
const bars = sortedLimits.map(limit =>
|
|
1995
|
+
padColumn(renderUsageBar(limit, uiTheme, sectionBarWidth), sectionColumnWidth),
|
|
1996
|
+
);
|
|
1997
|
+
lines.push(` ${bars.join(" ")} ${amountText}`.trimEnd());
|
|
1998
|
+
const resetText = sortedLimits.length <= 1 ? resolveResetRange(sortedLimits, nowMs) : null;
|
|
1999
|
+
if (resetText) {
|
|
2000
|
+
lines.push(` ${uiTheme.fg("dim", resetText)}`.trimEnd());
|
|
2001
|
+
}
|
|
2002
|
+
const notes = [...new Set(sortedLimits.flatMap(limit => limit.notes ?? []))];
|
|
2003
|
+
if (notes.length > 0) {
|
|
2004
|
+
lines.push(
|
|
2005
|
+
` ${uiTheme.fg("dim", replaceTabs(truncateToWidth(sanitizeText(notes.map(n => n.replace(/[\r\n]+/g, " ")).join(" • ")), 110)))}`.trimEnd(),
|
|
2006
|
+
);
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
// Render accounts with no rate limits (e.g. business/enterprise plans).
|
|
2011
|
+
const unlimitedReports = providerReports.filter(report => report.limits.length === 0);
|
|
2012
|
+
for (const report of unlimitedReports) {
|
|
2013
|
+
const label = formatUnlimitedReportLabel(report, 0);
|
|
2014
|
+
const tier = report.metadata?.planType;
|
|
2015
|
+
const tierSuffix = typeof tier === "string" && tier ? ` ${uiTheme.fg("dim", `(${tier})`)}` : "";
|
|
2016
|
+
lines.push(
|
|
2017
|
+
`${uiTheme.fg("success", uiTheme.status.success)} ${label}${tierSuffix} ${uiTheme.fg("dim", "-- no limits")}`,
|
|
2018
|
+
);
|
|
2019
|
+
}
|
|
2020
|
+
// No per-provider footer; global header shows last check.
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2023
|
+
return lines.join("\n");
|
|
2024
|
+
}
|