@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,1843 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
4
|
+
import type { AssistantMessage, UsageLimit, UsageReport } from "@oh-my-pi/pi-ai";
|
|
5
|
+
import { type Component, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
6
|
+
import { getProjectDir } from "@oh-my-pi/pi-utils";
|
|
7
|
+
import { settings } from "../../../config/settings";
|
|
8
|
+
import type { AgentSession } from "../../../session/agent-session";
|
|
9
|
+
import type { OAuthAccountIdentity } from "../../../session/auth-storage";
|
|
10
|
+
import { limitMatchesActiveAccount } from "../../../slash-commands/helpers/active-oauth-account";
|
|
11
|
+
import { type ActiveRepoContext, resolveActiveRepoContextSync } from "../../../utils/active-repo-context";
|
|
12
|
+
import * as git from "../../../utils/git";
|
|
13
|
+
import * as jj from "../../../utils/jj";
|
|
14
|
+
import { getSessionAccentAnsi, getSessionAccentHex } from "../../../utils/session-color";
|
|
15
|
+
import { calculateTokensPerSecond } from "../../../utils/token-rate";
|
|
16
|
+
import { sanitizeStatusText } from "../../shared";
|
|
17
|
+
import { theme } from "../../theme/theme";
|
|
18
|
+
import {
|
|
19
|
+
type CodexResetFireworksEvent,
|
|
20
|
+
type CodexResetUsageSnapshot,
|
|
21
|
+
detectCodexResetFireworks,
|
|
22
|
+
} from "../codex-reset-fireworks";
|
|
23
|
+
import { canReuseCachedPr, createPrCacheContext, isSamePrCacheContext, type PrCacheContext } from "./git-utils";
|
|
24
|
+
import { getPreset } from "./presets";
|
|
25
|
+
import { renderSegment, type SegmentContext } from "./segments";
|
|
26
|
+
import { getSeparator } from "./separators";
|
|
27
|
+
import type {
|
|
28
|
+
CollabStatus,
|
|
29
|
+
EffectiveStatusLineSettings,
|
|
30
|
+
StatusLineSegmentId,
|
|
31
|
+
StatusLineSegmentOptions,
|
|
32
|
+
StatusLineSettings,
|
|
33
|
+
} from "./types";
|
|
34
|
+
|
|
35
|
+
const JJ_REFRESH_TTL_MS = 5000;
|
|
36
|
+
const WATCHER_FAILURE_POLL_TTL_MS = 5000;
|
|
37
|
+
|
|
38
|
+
function normalizeCodexIdentityValue(value: unknown): string | undefined {
|
|
39
|
+
return typeof value === "string" && value.trim() ? value.trim().toLowerCase() : undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Fireworks are stateful, so their report match must be stricter than the
|
|
44
|
+
* status display's fallback matching: every known credential identifier must
|
|
45
|
+
* be present and equal or a workspace sibling can mutate this account's
|
|
46
|
+
* baseline.
|
|
47
|
+
*/
|
|
48
|
+
function codexReportMatchesExactIdentity(report: UsageReport, identity: OAuthAccountIdentity | undefined): boolean {
|
|
49
|
+
if (!identity) return false;
|
|
50
|
+
const accountId = normalizeCodexIdentityValue(identity.accountId);
|
|
51
|
+
const email = normalizeCodexIdentityValue(identity.email);
|
|
52
|
+
const projectId = normalizeCodexIdentityValue(identity.projectId);
|
|
53
|
+
const orgId = normalizeCodexIdentityValue(identity.orgId);
|
|
54
|
+
if (!accountId && !email && !projectId && !orgId) return false;
|
|
55
|
+
|
|
56
|
+
const metadata = report.metadata ?? {};
|
|
57
|
+
const reportAccountId =
|
|
58
|
+
normalizeCodexIdentityValue(metadata.accountId) ?? normalizeCodexIdentityValue(metadata.account_id);
|
|
59
|
+
const reportProjectId =
|
|
60
|
+
normalizeCodexIdentityValue(metadata.projectId) ?? normalizeCodexIdentityValue(metadata.project_id);
|
|
61
|
+
if (accountId && reportAccountId !== accountId) return false;
|
|
62
|
+
if (email && normalizeCodexIdentityValue(metadata.email) !== email) return false;
|
|
63
|
+
if (projectId && reportProjectId !== projectId) return false;
|
|
64
|
+
if (orgId && normalizeCodexIdentityValue(metadata.orgId) !== orgId) return false;
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
69
|
+
// Context-usage memo
|
|
70
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Cheap structural fingerprint of a message's tokenizable content. O(blocks) —
|
|
74
|
+
* only reads string `.length` and primitives, never copies or serializes.
|
|
75
|
+
* Detects in-place growth of the streaming tail (and other in-place mutations)
|
|
76
|
+
* so the cached `getContextUsage()` result is recomputed when — and only when —
|
|
77
|
+
* the numbers it depends on change.
|
|
78
|
+
*/
|
|
79
|
+
function messageFingerprint(msg: AgentMessage): string {
|
|
80
|
+
const role = (msg as { role?: string }).role ?? "";
|
|
81
|
+
const ts = (msg as { timestamp?: number }).timestamp ?? 0;
|
|
82
|
+
let textLen = 0;
|
|
83
|
+
let blocks = 0;
|
|
84
|
+
let images = 0;
|
|
85
|
+
if (role === "bashExecution") {
|
|
86
|
+
const b = msg as { command?: unknown; output?: unknown };
|
|
87
|
+
if (typeof b.command === "string") textLen += b.command.length;
|
|
88
|
+
if (typeof b.output === "string") textLen += b.output.length;
|
|
89
|
+
} else if (role === "user") {
|
|
90
|
+
const content = (msg as { content?: unknown }).content;
|
|
91
|
+
if (typeof content === "string") {
|
|
92
|
+
textLen += content.length;
|
|
93
|
+
} else if (Array.isArray(content)) {
|
|
94
|
+
blocks = content.length;
|
|
95
|
+
for (const block of content) {
|
|
96
|
+
if (block?.type === "text" && typeof block.text === "string") textLen += block.text.length;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
} else if (role === "assistant") {
|
|
100
|
+
const assistantMsg = msg as AssistantMessage;
|
|
101
|
+
const usageExt = assistantMsg.usage as unknown as { promptTokensDetails?: unknown };
|
|
102
|
+
const usageTotal = assistantMsg.usage?.totalTokens ?? 0;
|
|
103
|
+
const promptBuckets = usageExt?.promptTokensDetails ? 1 : 0;
|
|
104
|
+
const stopReason = assistantMsg.stopReason ?? "";
|
|
105
|
+
|
|
106
|
+
let signatureLen = 0;
|
|
107
|
+
let redactedLen = 0;
|
|
108
|
+
const msgExt = assistantMsg as unknown as {
|
|
109
|
+
thinkingSignature?: string;
|
|
110
|
+
textSignature?: string;
|
|
111
|
+
thoughtSignature?: string;
|
|
112
|
+
redactedThinking?: { data?: string };
|
|
113
|
+
};
|
|
114
|
+
const thinkingSignature = msgExt.thinkingSignature;
|
|
115
|
+
if (typeof thinkingSignature === "string") {
|
|
116
|
+
signatureLen += thinkingSignature.length;
|
|
117
|
+
}
|
|
118
|
+
const textSignature = msgExt.textSignature;
|
|
119
|
+
if (typeof textSignature === "string") {
|
|
120
|
+
signatureLen += textSignature.length;
|
|
121
|
+
}
|
|
122
|
+
const thoughtSignature = msgExt.thoughtSignature;
|
|
123
|
+
if (typeof thoughtSignature === "string") {
|
|
124
|
+
signatureLen += thoughtSignature.length;
|
|
125
|
+
}
|
|
126
|
+
const redactedData = msgExt.redactedThinking?.data;
|
|
127
|
+
if (typeof redactedData === "string") {
|
|
128
|
+
redactedLen += redactedData.length;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const content = (msg as { content?: unknown }).content;
|
|
132
|
+
if (Array.isArray(content)) {
|
|
133
|
+
blocks = content.length;
|
|
134
|
+
for (const block of content) {
|
|
135
|
+
if (!block || typeof block !== "object") continue;
|
|
136
|
+
const b = block as {
|
|
137
|
+
type?: string;
|
|
138
|
+
text?: string;
|
|
139
|
+
thinking?: string;
|
|
140
|
+
thinkingSignature?: string;
|
|
141
|
+
signature?: string;
|
|
142
|
+
textSignature?: string;
|
|
143
|
+
thoughtSignature?: string;
|
|
144
|
+
data?: string;
|
|
145
|
+
name?: string;
|
|
146
|
+
arguments?: unknown;
|
|
147
|
+
};
|
|
148
|
+
if (b.type === "text" && typeof b.text === "string") textLen += b.text.length;
|
|
149
|
+
else if (b.type === "thinking") {
|
|
150
|
+
if (typeof b.thinking === "string") textLen += b.thinking.length;
|
|
151
|
+
if (typeof b.thinkingSignature === "string") signatureLen += b.thinkingSignature.length;
|
|
152
|
+
if (typeof b.signature === "string") signatureLen += b.signature.length;
|
|
153
|
+
if (typeof b.textSignature === "string") signatureLen += b.textSignature.length;
|
|
154
|
+
if (typeof b.thoughtSignature === "string") signatureLen += b.thoughtSignature.length;
|
|
155
|
+
} else if (b.type === "redactedThinking" && typeof b.data === "string") {
|
|
156
|
+
redactedLen += b.data.length;
|
|
157
|
+
} else if (b.type === "toolCall") {
|
|
158
|
+
if (typeof b.name === "string") textLen += b.name.length;
|
|
159
|
+
if (b.arguments !== undefined) {
|
|
160
|
+
try {
|
|
161
|
+
textLen += JSON.stringify(b.arguments, (_key, value) =>
|
|
162
|
+
typeof value === "bigint" ? value.toString() : value,
|
|
163
|
+
).length;
|
|
164
|
+
} catch {
|
|
165
|
+
textLen += String(b.arguments).length;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return `${role}:${ts}:${textLen}:${blocks}:${images}:${signatureLen}:${redactedLen}:${usageTotal}:${promptBuckets}:${stopReason}`;
|
|
172
|
+
} else if (role === "toolResult" || role === "hookMessage") {
|
|
173
|
+
const content = (msg as { content?: unknown }).content;
|
|
174
|
+
if (typeof content === "string") {
|
|
175
|
+
textLen += content.length;
|
|
176
|
+
} else if (Array.isArray(content)) {
|
|
177
|
+
blocks = content.length;
|
|
178
|
+
for (const block of content) {
|
|
179
|
+
if (!block || typeof block !== "object") continue;
|
|
180
|
+
const b = block as { type?: string; text?: string };
|
|
181
|
+
if (b.type === "text" && typeof b.text === "string") textLen += b.text.length;
|
|
182
|
+
else if (b.type === "image") images++;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
} else if (role === "branchSummary" || role === "compactionSummary") {
|
|
186
|
+
const s = (msg as { summary?: unknown }).summary;
|
|
187
|
+
if (typeof s === "string") textLen += s.length;
|
|
188
|
+
}
|
|
189
|
+
return `${role}:${ts}:${textLen}:${blocks}:${images}`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
interface ContextUsageMemo {
|
|
193
|
+
messagesRef: readonly AgentMessage[];
|
|
194
|
+
length: number;
|
|
195
|
+
lastFingerprint: string | undefined;
|
|
196
|
+
modelContextWindow: number;
|
|
197
|
+
contextUsageRevision: number;
|
|
198
|
+
usedTokens: number;
|
|
199
|
+
contextWindow: number;
|
|
200
|
+
systemPromptRef: readonly string[] | undefined;
|
|
201
|
+
toolsRef: readonly any[] | undefined;
|
|
202
|
+
skillsRef: readonly any[] | undefined;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
interface ActiveRepoCache {
|
|
206
|
+
projectDir: string;
|
|
207
|
+
activeRepo: ActiveRepoContext | null;
|
|
208
|
+
effectiveGitCwd: string;
|
|
209
|
+
/** Project + worktree dir name when `projectDir` is a linked worktree, else null. */
|
|
210
|
+
worktree: WorktreeContext | null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
interface BranchResolveRequest {
|
|
214
|
+
id: number;
|
|
215
|
+
cwd: string;
|
|
216
|
+
controller: AbortController;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
interface JjResolveRequest {
|
|
220
|
+
id: number;
|
|
221
|
+
controller: AbortController;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
interface WorktreeContext {
|
|
225
|
+
/** Primary-checkout (project) name shown by the path segment. */
|
|
226
|
+
projectName: string;
|
|
227
|
+
/** Worktree directory name — suppressed from the path when it equals the branch. */
|
|
228
|
+
worktreeName: string;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Project + worktree-dir names when `cwd` is a linked git worktree, else null.
|
|
233
|
+
* The project name comes from the shared primary checkout; bare-repo worktrees
|
|
234
|
+
* resolve to the shared `foo.git` dir, so a trailing `.git` is stripped.
|
|
235
|
+
*/
|
|
236
|
+
function resolveWorktreeContext(cwd: string): WorktreeContext | null {
|
|
237
|
+
const worktree = git.repo.linkedWorktreeSync(cwd);
|
|
238
|
+
if (!worktree) return null;
|
|
239
|
+
const base = path.basename(worktree.primaryRoot);
|
|
240
|
+
const projectName = base.endsWith(".git") ? base.slice(0, -4) : base;
|
|
241
|
+
if (!projectName) return null;
|
|
242
|
+
return { projectName, worktreeName: path.basename(worktree.root) };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Per-{@link AgentSession} active-processing meter for the `time_spent`
|
|
247
|
+
* segment. `activeMs` is the union of every completed `agent_start`→
|
|
248
|
+
* `agent_end` window; `activeStartedAt` is the start timestamp of the
|
|
249
|
+
* currently-running window, or `null` when idle.
|
|
250
|
+
*
|
|
251
|
+
* `sessionFile` snapshots the loaded session-file path at meter-creation
|
|
252
|
+
* time. `AgentSession.switchSession` (/resume, /move, ACP fork, RPC
|
|
253
|
+
* `switch_session`, extension `switchSession`) mutates the loaded file
|
|
254
|
+
* under the same {@link AgentSession} ref, so the WeakMap key alone
|
|
255
|
+
* cannot tell two conversations apart. `#meter()` compares this snapshot
|
|
256
|
+
* against the live `session.sessionFile`, and a real-to-real change
|
|
257
|
+
* starts the meter fresh instead of crediting the new conversation with
|
|
258
|
+
* the previous one's accumulated active time. The undefined → real
|
|
259
|
+
* first-save transition does not reset, since the session identity has
|
|
260
|
+
* not changed.
|
|
261
|
+
*/
|
|
262
|
+
interface ActiveMeter {
|
|
263
|
+
activeMs: number;
|
|
264
|
+
activeStartedAt: number | null;
|
|
265
|
+
sessionFile: string | undefined;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const EMPTY_MESSAGES: readonly AgentMessage[] = [];
|
|
269
|
+
const STATUS_USAGE_START_DELAY_MS = 0;
|
|
270
|
+
const STATUS_USAGE_REFRESH_TIMEOUT_MS = 2_000;
|
|
271
|
+
|
|
272
|
+
function hasContextSegment(segments: readonly StatusLineSegmentId[]): boolean {
|
|
273
|
+
return segments.includes("context_pct") || segments.includes("context_total");
|
|
274
|
+
}
|
|
275
|
+
function hasGitSegment(segments: readonly StatusLineSegmentId[]): boolean {
|
|
276
|
+
return segments.includes("git");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function hasPrSegment(segments: readonly StatusLineSegmentId[]): boolean {
|
|
280
|
+
return segments.includes("pr");
|
|
281
|
+
}
|
|
282
|
+
function hasPathSegment(segments: readonly StatusLineSegmentId[]): boolean {
|
|
283
|
+
return segments.includes("path");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function hasGitBackedSegment(segments: readonly StatusLineSegmentId[]): boolean {
|
|
287
|
+
return hasGitSegment(segments) || hasPrSegment(segments);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
291
|
+
// StatusLineComponent
|
|
292
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
293
|
+
|
|
294
|
+
export class StatusLineComponent implements Component {
|
|
295
|
+
#settings: StatusLineSettings = {};
|
|
296
|
+
#effectiveSettings: EffectiveStatusLineSettings | undefined;
|
|
297
|
+
#cachedBranch: string | null | undefined = undefined;
|
|
298
|
+
#cachedBranchRepoId: string | null | undefined = undefined;
|
|
299
|
+
#cachedBranchCwd: string | undefined = undefined;
|
|
300
|
+
#cachedBranchHasGitRepository = false;
|
|
301
|
+
// In-flight reftable resolve slot. Ownership is the launch id, not the cwd:
|
|
302
|
+
// two live resolves can share a cwd string across an invalidation, and a
|
|
303
|
+
// stale one must never free (or poison) a slot it no longer owns.
|
|
304
|
+
#branchResolveSeq = 0;
|
|
305
|
+
#branchResolveActive: BranchResolveRequest | undefined = undefined;
|
|
306
|
+
// Bumped on every branch-cache reset (#invalidateGitCaches — a HEAD move or
|
|
307
|
+
// repo-context change). An in-flight reftable resolve captures this at
|
|
308
|
+
// launch; a mismatch on resolve means the cache was invalidated underneath
|
|
309
|
+
// it (a newer resolve superseded it), so its result is stale and must be
|
|
310
|
+
// dropped rather than overwrite the value the newer resolve committed.
|
|
311
|
+
// Mirrors #jjCacheGeneration / #getJjBranch in this file.
|
|
312
|
+
// Timestamp of the latest branch read; only bounds cache freshness when the
|
|
313
|
+
// HEAD watcher could not be installed.
|
|
314
|
+
#branchLastFetch: number | undefined = undefined;
|
|
315
|
+
// Bumped on every branch-cache reset (invalidateGitCaches — a HEAD move or
|
|
316
|
+
// repo-context change). An in-flight reftable resolve captures this at
|
|
317
|
+
// launch; a mismatch on resolve means the cache was invalidated underneath
|
|
318
|
+
// it (a newer resolve superseded it), so its result is stale and must be
|
|
319
|
+
// dropped rather than overwrite the value the newer resolve committed.
|
|
320
|
+
// Mirrors #jjCacheGeneration / #getJjBranch in this file.
|
|
321
|
+
#branchCacheGeneration = 0;
|
|
322
|
+
#gitWatcher: fs.FSWatcher | null = null;
|
|
323
|
+
#gitWatcherErrorListener: (() => void) | undefined = undefined;
|
|
324
|
+
#gitWatcherUnavailable = false;
|
|
325
|
+
#onBranchChange: (() => void) | null = null;
|
|
326
|
+
#disposed = false;
|
|
327
|
+
#autoCompactEnabled: boolean = true;
|
|
328
|
+
#hookStatuses: Map<string, string> = new Map();
|
|
329
|
+
#subagentCount: number = 0;
|
|
330
|
+
/**
|
|
331
|
+
* Active-processing accounting for the `time_spent` segment, keyed per
|
|
332
|
+
* {@link AgentSession} so the focus-controller mid-turn attach path
|
|
333
|
+
* cannot leak an unmatched synthesized `agent_start` from a subagent
|
|
334
|
+
* into the main session's meter.
|
|
335
|
+
*
|
|
336
|
+
* Each meter is `{ activeMs, activeStartedAt }`: `activeMs` is the union
|
|
337
|
+
* of every completed `agent_start`→`agent_end` window since
|
|
338
|
+
* {@link resetActiveTime} last reset it; `activeStartedAt` is the start
|
|
339
|
+
* timestamp of the currently-running window (or `null` when idle).
|
|
340
|
+
* `getActiveMs()` returns `activeMs + (now - activeStartedAt)` for the
|
|
341
|
+
* currently-attached session, so the counter ticks live during a turn
|
|
342
|
+
* and freezes the instant the agent yields.
|
|
343
|
+
*
|
|
344
|
+
* WeakMap so meters die with their session (e.g. a parked subagent
|
|
345
|
+
* dropped from the registry); the main session's meter survives focus
|
|
346
|
+
* round-trips because the same {@link AgentSession} ref is reused.
|
|
347
|
+
*/
|
|
348
|
+
#activeMeters: WeakMap<AgentSession, ActiveMeter> = new WeakMap();
|
|
349
|
+
#planModeStatus: { enabled: boolean; paused: boolean } | null = null;
|
|
350
|
+
#loopModeStatus: SegmentContext["loopMode"] = null;
|
|
351
|
+
#goalModeStatus: { enabled: boolean; paused: boolean } | null = null;
|
|
352
|
+
#vibeModeStatus: { enabled: boolean } | null = null;
|
|
353
|
+
/**
|
|
354
|
+
* Injected aggregator that returns the aggregate tok/s of this session's
|
|
355
|
+
* live vibe worker sessions, or null when no workers are streaming. Kept as
|
|
356
|
+
* a callback so the render layer doesn't import the heavy vibe/task
|
|
357
|
+
* dependency graph; interactive-mode wires it to VibeSessionRegistry.
|
|
358
|
+
*/
|
|
359
|
+
#vibeWorkerTokenRate: (() => number | null) | null = null;
|
|
360
|
+
#collabStatus: CollabStatus | null = null;
|
|
361
|
+
#focusedAgentId: string | undefined;
|
|
362
|
+
#activeRepoCache: ActiveRepoCache | undefined;
|
|
363
|
+
|
|
364
|
+
// Git status caching (1s TTL)
|
|
365
|
+
#cachedGitStatus: { staged: number; unstaged: number; untracked: number } | null = null;
|
|
366
|
+
#cachedGitStatusCwd: string | undefined = undefined;
|
|
367
|
+
#gitStatusLastFetch = 0;
|
|
368
|
+
#gitStatusInFlightCwd: string | undefined = undefined;
|
|
369
|
+
#jjRoot: string | null | undefined = undefined;
|
|
370
|
+
#jjRootCwd: string | undefined = undefined;
|
|
371
|
+
#cachedJjBranch: string | null = null;
|
|
372
|
+
#jjBranchLastFetch = 0;
|
|
373
|
+
#jjResolveSeq = 0;
|
|
374
|
+
#jjBranchActive: JjResolveRequest | undefined = undefined;
|
|
375
|
+
#cachedJjStatus: { staged: number; unstaged: number; untracked: number } | null = null;
|
|
376
|
+
#jjStatusLastFetch = 0;
|
|
377
|
+
#jjStatusActive: JjResolveRequest | undefined = undefined;
|
|
378
|
+
// Bumped on every jj-cache reset — a cwd switch (#jjRootFor) or a HEAD /
|
|
379
|
+
// bookmark move (#invalidateGitCaches). An in-flight jj query captures this
|
|
380
|
+
// at launch; a mismatch on resolve means the caches were reset underneath it
|
|
381
|
+
// (including a reset that re-resolves to the SAME root, which a root-equality
|
|
382
|
+
// check cannot detect), so the result is stale and must be dropped rather
|
|
383
|
+
// than poison the fresh cache or advance its throttle.
|
|
384
|
+
#jjCacheGeneration = 0;
|
|
385
|
+
|
|
386
|
+
// PR lookup caching (invalidated on branch/repo context changes)
|
|
387
|
+
#cachedPr: { number: number; url: string } | null | undefined = undefined;
|
|
388
|
+
#cachedPrContext: PrCacheContext | undefined = undefined;
|
|
389
|
+
#prLookupInFlight = false;
|
|
390
|
+
#defaultBranch?: string;
|
|
391
|
+
#defaultBranchCwd: string | undefined = undefined;
|
|
392
|
+
#lastTokensPerSecond: number | null = null;
|
|
393
|
+
#lastTokensPerSecondTimestamp: number | null = null;
|
|
394
|
+
|
|
395
|
+
// Provider usage caching (5-min TTL, OAuth/sub only)
|
|
396
|
+
#cachedUsage: {
|
|
397
|
+
tier?: string;
|
|
398
|
+
fiveHour?: { percent: number; resetMinutes?: number };
|
|
399
|
+
sevenDay?: { percent: number; resetHours?: number };
|
|
400
|
+
} | null = null;
|
|
401
|
+
#cachedUsageContextKey: string | null = null;
|
|
402
|
+
#usageFetchedAt = 0;
|
|
403
|
+
#usageInFlight = false;
|
|
404
|
+
#usageStartTimer: Timer | null = null;
|
|
405
|
+
// A timed-out request may still resolve. Its result remains eligible only
|
|
406
|
+
// until a newer request has applied.
|
|
407
|
+
#usageRefreshSequence = 0;
|
|
408
|
+
#latestAppliedUsageRefreshSequence = 0;
|
|
409
|
+
#codexResetSnapshots = new Map<string, CodexResetUsageSnapshot>();
|
|
410
|
+
#onCodexResetFireworks: ((event: CodexResetFireworksEvent) => void) | undefined;
|
|
411
|
+
// Context-usage memo. The status line redraws on every agent event, so the
|
|
412
|
+
// hot path must not recompute context tokens unless an input changed.
|
|
413
|
+
// `getContextUsage()` anchors on the last assistant's real prompt-token
|
|
414
|
+
// count (matching the provider and the `/context` panel), so a stable
|
|
415
|
+
// message list + model window yields a stable result we can return verbatim.
|
|
416
|
+
#contextUsageCache: ContextUsageMemo | undefined;
|
|
417
|
+
|
|
418
|
+
constructor(private session: AgentSession) {
|
|
419
|
+
this.#settings = {
|
|
420
|
+
preset: settings.get("statusLine.preset"),
|
|
421
|
+
leftSegments: settings.get("statusLine.leftSegments"),
|
|
422
|
+
rightSegments: settings.get("statusLine.rightSegments"),
|
|
423
|
+
separator: settings.get("statusLine.separator"),
|
|
424
|
+
showHookStatus: settings.get("statusLine.showHookStatus"),
|
|
425
|
+
segmentOptions: settings.getGroup("statusLine").segmentOptions,
|
|
426
|
+
sessionAccent: settings.get("statusLine.sessionAccent"),
|
|
427
|
+
transparent: settings.get("statusLine.transparent"),
|
|
428
|
+
compactThinkingLevel: settings.get("statusLine.compactThinkingLevel"),
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
#gitEnabled(): boolean {
|
|
432
|
+
return settings.get("git.enabled");
|
|
433
|
+
}
|
|
434
|
+
#hasGitBackedSegment(): boolean {
|
|
435
|
+
const effectiveSettings = this.#resolveSettings();
|
|
436
|
+
return (
|
|
437
|
+
hasGitBackedSegment(effectiveSettings.leftSegments) || hasGitBackedSegment(effectiveSettings.rightSegments)
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
#resolveActiveRepoCache(): ActiveRepoCache {
|
|
442
|
+
const projectDir = getProjectDir();
|
|
443
|
+
if (this.#activeRepoCache?.projectDir === projectDir) {
|
|
444
|
+
return this.#activeRepoCache;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const activeRepo = resolveActiveRepoContextSync(projectDir);
|
|
448
|
+
const effectiveGitCwd = activeRepo?.repoRoot ?? projectDir;
|
|
449
|
+
// Only collapse the bare-cwd case: a single-direct-child-repo context
|
|
450
|
+
// (activeRepo set) renders `<parent> ↳ <child>`, which we leave intact.
|
|
451
|
+
const worktree = activeRepo ? null : resolveWorktreeContext(effectiveGitCwd);
|
|
452
|
+
this.#activeRepoCache = { projectDir, activeRepo, effectiveGitCwd, worktree };
|
|
453
|
+
return this.#activeRepoCache;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* Re-point the status line at another session (focus proxy). Invalidate: model/context/usage all derive
|
|
458
|
+
* from it. `focusedAgentId` is the focused subagent id while the view is proxied, undefined for main.
|
|
459
|
+
*/
|
|
460
|
+
setSession(session: AgentSession, focusedAgentId?: string): void {
|
|
461
|
+
const sessionChanged = this.session !== session;
|
|
462
|
+
if (!sessionChanged && this.#focusedAgentId === focusedAgentId) return;
|
|
463
|
+
this.session = session;
|
|
464
|
+
this.#focusedAgentId = focusedAgentId;
|
|
465
|
+
if (sessionChanged) {
|
|
466
|
+
this.#invalidateSessionCaches();
|
|
467
|
+
this.#closeStaleActiveWindow();
|
|
468
|
+
}
|
|
469
|
+
this.invalidate();
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Drop a meter's in-flight window when the newly-attached session is no
|
|
474
|
+
* longer streaming. Handles the case where the focus controller
|
|
475
|
+
* synthesized an `agent_start` on a mid-turn attach but the matching
|
|
476
|
+
* real `agent_end` never reached us — the user detached before it
|
|
477
|
+
* fired, and re-focusing later (after the agent finished) would
|
|
478
|
+
* otherwise tick over the entire detached gap. Crediting that gap to
|
|
479
|
+
* `activeMs` would be wrong (the agent finished at some point we never
|
|
480
|
+
* observed), so the window is dropped rather than folded in.
|
|
481
|
+
*/
|
|
482
|
+
#closeStaleActiveWindow(): void {
|
|
483
|
+
const meter = this.#meter();
|
|
484
|
+
if (meter.activeStartedAt === null) return;
|
|
485
|
+
if (this.session.isStreaming) return;
|
|
486
|
+
meter.activeStartedAt = null;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
updateSettings(settings: StatusLineSettings): void {
|
|
490
|
+
this.#settings = settings;
|
|
491
|
+
this.#effectiveSettings = undefined;
|
|
492
|
+
if (this.#onBranchChange) this.#setupGitWatcher();
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
getEffectiveSettingsForTest(): EffectiveStatusLineSettings {
|
|
496
|
+
return this.#resolveSettings();
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
setAutoCompactEnabled(enabled: boolean): void {
|
|
500
|
+
this.#autoCompactEnabled = enabled;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
setSubagentCount(count: number): void {
|
|
504
|
+
this.#subagentCount = count;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* Compatibility shim for callers predating the simplified subagent badge.
|
|
509
|
+
* The status line now intentionally shows only the active count.
|
|
510
|
+
*/
|
|
511
|
+
setSubagentHubHint(_hint: string | undefined): void {}
|
|
512
|
+
|
|
513
|
+
/** Active subagent count as currently displayed (collab state mirroring). */
|
|
514
|
+
get subagentCount(): number {
|
|
515
|
+
return this.#subagentCount;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Reset the currently-attached session's active-time accumulators so
|
|
520
|
+
* the `time_spent` segment starts from zero. Called from `/clear`,
|
|
521
|
+
* fresh-session, and joined-collab paths; both the completed
|
|
522
|
+
* accumulator and any in-flight window are dropped, so a reset
|
|
523
|
+
* mid-turn ignores the running window (the matching `markActivityEnd`
|
|
524
|
+
* will see an idle meter and no-op).
|
|
525
|
+
*/
|
|
526
|
+
resetActiveTime(): void {
|
|
527
|
+
const meter = this.#meter();
|
|
528
|
+
meter.activeMs = 0;
|
|
529
|
+
meter.activeStartedAt = null;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Mark the currently-attached session as having started a unit of
|
|
534
|
+
* active processing. Idempotent: a second start while a window is
|
|
535
|
+
* already open is a no-op, so reentrant `agent_start` events (e.g.
|
|
536
|
+
* nested auto-compaction loops, focus-controller mid-turn attach onto
|
|
537
|
+
* an already-running window) do not double-count.
|
|
538
|
+
*/
|
|
539
|
+
markActivityStart(): void {
|
|
540
|
+
const meter = this.#meter();
|
|
541
|
+
if (meter.activeStartedAt !== null) return;
|
|
542
|
+
meter.activeStartedAt = Date.now();
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Close the currently-attached session's open active-processing
|
|
547
|
+
* window, folding its elapsed time into the accumulator. Idempotent
|
|
548
|
+
* when the meter is already idle so callers can fire it on every
|
|
549
|
+
* `agent_end` without guarding.
|
|
550
|
+
*/
|
|
551
|
+
markActivityEnd(): void {
|
|
552
|
+
const meter = this.#meter();
|
|
553
|
+
if (meter.activeStartedAt === null) return;
|
|
554
|
+
meter.activeMs += Math.max(0, Date.now() - meter.activeStartedAt);
|
|
555
|
+
meter.activeStartedAt = null;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Snapshot of total active-processing time for the currently-attached
|
|
560
|
+
* session, including any in-flight window. Exposed for the segment
|
|
561
|
+
* context builder; tests assert against this too.
|
|
562
|
+
*/
|
|
563
|
+
getActiveMs(): number {
|
|
564
|
+
const meter = this.#meter();
|
|
565
|
+
if (meter.activeStartedAt === null) return meter.activeMs;
|
|
566
|
+
return meter.activeMs + Math.max(0, Date.now() - meter.activeStartedAt);
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Return (lazily creating) the meter for the currently-attached
|
|
571
|
+
* session. Detects an in-place session-file swap under the same
|
|
572
|
+
* {@link AgentSession} ref (`switchSession` paths: `/resume`, `/move`,
|
|
573
|
+
* ACP fork/load, RPC `switch_session`, extension `switchSession`):
|
|
574
|
+
* a real-to-real change starts the meter fresh so the new
|
|
575
|
+
* conversation does not inherit the previous one's accumulated active
|
|
576
|
+
* time. The undefined → real first-save transition only refreshes the
|
|
577
|
+
* snapshot — the conversation identity has not changed.
|
|
578
|
+
*/
|
|
579
|
+
#meter(): ActiveMeter {
|
|
580
|
+
const currentFile = this.session.sessionFile;
|
|
581
|
+
let meter = this.#activeMeters.get(this.session);
|
|
582
|
+
if (meter) {
|
|
583
|
+
const switched =
|
|
584
|
+
currentFile !== undefined && meter.sessionFile !== undefined && meter.sessionFile !== currentFile;
|
|
585
|
+
if (switched) {
|
|
586
|
+
meter = undefined;
|
|
587
|
+
} else {
|
|
588
|
+
meter.sessionFile = currentFile;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
if (!meter) {
|
|
592
|
+
meter = { activeMs: 0, activeStartedAt: null, sessionFile: currentFile };
|
|
593
|
+
this.#activeMeters.set(this.session, meter);
|
|
594
|
+
}
|
|
595
|
+
return meter;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
setPlanModeStatus(status: { enabled: boolean; paused: boolean } | undefined): void {
|
|
599
|
+
this.#planModeStatus = status ?? null;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
setLoopModeStatus(status: NonNullable<SegmentContext["loopMode"]> | undefined): void {
|
|
603
|
+
this.#loopModeStatus = status ?? null;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
setGoalModeStatus(status: { enabled: boolean; paused: boolean } | undefined): void {
|
|
607
|
+
this.#goalModeStatus = status ?? null;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
setVibeModeStatus(status: { enabled: boolean } | undefined): void {
|
|
611
|
+
this.#vibeModeStatus = status ?? null;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Inject the aggregator that returns the aggregate tok/s of this session's
|
|
616
|
+
* live vibe worker sessions (null when no workers are streaming). Wired by
|
|
617
|
+
* interactive-mode, which owns the VibeSessionRegistry coupling, so the
|
|
618
|
+
* render layer stays off the heavy vibe/task dependency graph. Pass
|
|
619
|
+
* `undefined` to clear.
|
|
620
|
+
*/
|
|
621
|
+
setVibeWorkerTokenRateProvider(provider: (() => number | null) | undefined): void {
|
|
622
|
+
this.#vibeWorkerTokenRate = provider ?? null;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
setCollabStatus(status: CollabStatus | null): void {
|
|
626
|
+
this.#collabStatus = status;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/** Set the callback that presents detected Codex reset celebrations, or clear it with `undefined`. */
|
|
630
|
+
setCodexResetFireworksHandler(handler: ((event: CodexResetFireworksEvent) => void) | undefined): void {
|
|
631
|
+
this.#onCodexResetFireworks = handler;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
setHookStatus(key: string, text: string | undefined): void {
|
|
635
|
+
if (text === undefined) {
|
|
636
|
+
this.#hookStatuses.delete(key);
|
|
637
|
+
} else {
|
|
638
|
+
this.#hookStatuses.set(key, text);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
watchBranch(onBranchChange: () => void): void {
|
|
643
|
+
this.#onBranchChange = onBranchChange;
|
|
644
|
+
this.#setupGitWatcher();
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
#setupGitWatcher(): void {
|
|
648
|
+
this.#retireGitWatcher();
|
|
649
|
+
this.#gitWatcherUnavailable = false;
|
|
650
|
+
|
|
651
|
+
if (!this.#gitEnabled() || !this.#hasGitBackedSegment()) {
|
|
652
|
+
this.invalidateGitCaches();
|
|
653
|
+
return;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const { effectiveGitCwd } = this.#resolveActiveRepoCache();
|
|
657
|
+
const repository = git.repo.resolveSync(effectiveGitCwd);
|
|
658
|
+
if (!repository) {
|
|
659
|
+
// There is no path to watch yet. Cache the negative result only for the
|
|
660
|
+
// fallback poll interval so a later `git init` becomes visible without
|
|
661
|
+
// generic invalidations or a render-path probe on every paint.
|
|
662
|
+
this.#gitWatcherUnavailable = true;
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
const watchPath = git.repo.isReftableSync(repository)
|
|
667
|
+
? path.join(repository.gitDir, "reftable")
|
|
668
|
+
: repository.headPath;
|
|
669
|
+
|
|
670
|
+
try {
|
|
671
|
+
const watcher = fs.watch(watchPath, () => {
|
|
672
|
+
if (this.#disposed || this.#gitWatcher !== watcher) return;
|
|
673
|
+
this.invalidateGitCaches();
|
|
674
|
+
this.#onBranchChange?.();
|
|
675
|
+
});
|
|
676
|
+
const onError = () => {
|
|
677
|
+
if (this.#gitWatcher !== watcher) return;
|
|
678
|
+
this.#retireGitWatcher();
|
|
679
|
+
this.#gitWatcherUnavailable = true;
|
|
680
|
+
if (this.#disposed) return;
|
|
681
|
+
this.invalidateGitCaches();
|
|
682
|
+
this.#onBranchChange?.();
|
|
683
|
+
};
|
|
684
|
+
this.#gitWatcher = watcher;
|
|
685
|
+
this.#gitWatcherErrorListener = onError;
|
|
686
|
+
watcher.on("error", onError);
|
|
687
|
+
} catch {
|
|
688
|
+
this.#gitWatcherUnavailable = true;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
#retireGitWatcher(): void {
|
|
693
|
+
const watcher = this.#gitWatcher;
|
|
694
|
+
const onError = this.#gitWatcherErrorListener;
|
|
695
|
+
this.#gitWatcher = null;
|
|
696
|
+
this.#gitWatcherErrorListener = undefined;
|
|
697
|
+
if (!watcher) return;
|
|
698
|
+
if (onError) watcher.off("error", onError);
|
|
699
|
+
watcher.close();
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
dispose(): void {
|
|
703
|
+
this.#disposed = true;
|
|
704
|
+
this.#branchResolveActive?.controller.abort();
|
|
705
|
+
this.#branchResolveActive = undefined;
|
|
706
|
+
this.#resetJjRequests();
|
|
707
|
+
this.#onBranchChange = null;
|
|
708
|
+
this.#clearUsageStartTimer();
|
|
709
|
+
this.#onCodexResetFireworks = undefined;
|
|
710
|
+
this.#codexResetSnapshots.clear();
|
|
711
|
+
this.#retireGitWatcher();
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
#clearUsageStartTimer(): void {
|
|
715
|
+
if (!this.#usageStartTimer) return;
|
|
716
|
+
clearTimeout(this.#usageStartTimer);
|
|
717
|
+
this.#usageStartTimer = null;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
invalidate(): void {
|
|
721
|
+
// Generic repaint invalidation (theme change, message event, model
|
|
722
|
+
// switch, …). Must NOT abort or restart a live reftable HEAD/PR resolve:
|
|
723
|
+
// the render path self-invalidates via cwd/context cache-miss checks, so
|
|
724
|
+
// a generic paint only needs to re-render — not tear down in-flight VCS
|
|
725
|
+
// work. Aborting here would fan out a new git subprocess on every agent
|
|
726
|
+
// event, re-introducing the render-path spawn churn the async resolve
|
|
727
|
+
// was designed to avoid. Explicit Git/repository invalidation (watcher
|
|
728
|
+
// HEAD-move, cwd/repo switch) goes through {@link invalidateGitCaches}.
|
|
729
|
+
// A tool may open, close, or merge a PR without moving HEAD. Expire the
|
|
730
|
+
// settled PR context on ordinary activity while leaving HEAD work intact.
|
|
731
|
+
this.#cachedPrContext = undefined;
|
|
732
|
+
}
|
|
733
|
+
#invalidateSessionCaches(): void {
|
|
734
|
+
this.#clearUsageStartTimer();
|
|
735
|
+
this.#cachedUsage = null;
|
|
736
|
+
this.#usageFetchedAt = 0;
|
|
737
|
+
this.#usageInFlight = false;
|
|
738
|
+
this.#contextUsageCache = undefined;
|
|
739
|
+
this.#lastTokensPerSecond = null;
|
|
740
|
+
this.#lastTokensPerSecondTimestamp = null;
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Explicit Git/repository cache invalidation. Aborts any in-flight
|
|
745
|
+
* reftable HEAD/PR resolve, bumps the stale-result generation, and drops
|
|
746
|
+
* the branch/PR/jj caches so the next render refetches from disk. Called
|
|
747
|
+
* by the git watcher on a HEAD move and by {@link applyCwdChange} on a
|
|
748
|
+
* repo/cwd switch. Generic repaints use {@link invalidate} instead and
|
|
749
|
+
* must never reach this path.
|
|
750
|
+
*/
|
|
751
|
+
invalidateGitCaches(): void {
|
|
752
|
+
this.#cachedBranch = undefined;
|
|
753
|
+
this.#cachedBranchRepoId = undefined;
|
|
754
|
+
this.#cachedBranchCwd = undefined;
|
|
755
|
+
this.#cachedBranchHasGitRepository = false;
|
|
756
|
+
// Abort before releasing the in-flight slot. Releasing alone would allow
|
|
757
|
+
// repeated invalidations to fan out still-running git subprocesses.
|
|
758
|
+
this.#branchResolveActive?.controller.abort();
|
|
759
|
+
this.#branchResolveActive = undefined;
|
|
760
|
+
this.#branchLastFetch = undefined;
|
|
761
|
+
this.#branchCacheGeneration++;
|
|
762
|
+
this.#cachedPrContext = undefined;
|
|
763
|
+
// jj label/status share the git segment's lifecycle: a HEAD move (e.g. a
|
|
764
|
+
// colocated `jj new`/bookmark move) must drop the throttled jj caches too,
|
|
765
|
+
// mirroring #jjRootFor's per-cwd reset so the next render refetches.
|
|
766
|
+
this.#resetJjRequests();
|
|
767
|
+
this.#jjRoot = undefined;
|
|
768
|
+
this.#jjRootCwd = undefined;
|
|
769
|
+
this.#cachedJjBranch = null;
|
|
770
|
+
this.#jjBranchLastFetch = 0;
|
|
771
|
+
this.#cachedJjStatus = null;
|
|
772
|
+
this.#jjStatusLastFetch = 0;
|
|
773
|
+
this.#jjCacheGeneration++;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* Re-point the status line's VCS watcher and caches at a new cwd/repository.
|
|
778
|
+
* Atomically retires the old watcher/listeners, invalidates VCS caches and
|
|
779
|
+
* in-flight controllers, then runs watcher setup for the new cwd and requests
|
|
780
|
+
* a repaint. Called by {@link InteractiveMode.applyCwdChange} after the
|
|
781
|
+
* SessionManager's cwd has moved — the watcher ownership always follows the
|
|
782
|
+
* effective cwd/repo, so a stale watcher for the previous repo can never
|
|
783
|
+
* invalidate the new one. Generic repaints use {@link invalidate} and must
|
|
784
|
+
* never retire the watcher or abort a live resolve.
|
|
785
|
+
*/
|
|
786
|
+
applyCwdChange(): void {
|
|
787
|
+
this.#retireGitWatcher();
|
|
788
|
+
this.invalidateGitCaches();
|
|
789
|
+
this.#setupGitWatcher();
|
|
790
|
+
this.#onBranchChange?.();
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
#resetJjRequests(): void {
|
|
794
|
+
this.#jjBranchActive?.controller.abort();
|
|
795
|
+
this.#jjBranchActive = undefined;
|
|
796
|
+
this.#jjStatusActive?.controller.abort();
|
|
797
|
+
this.#jjStatusActive = undefined;
|
|
798
|
+
}
|
|
799
|
+
#getCurrentBranch(effectiveGitCwd?: string): string | null {
|
|
800
|
+
if (!this.#gitEnabled()) return null;
|
|
801
|
+
|
|
802
|
+
const gitCwd = effectiveGitCwd ?? this.#resolveActiveRepoCache().effectiveGitCwd;
|
|
803
|
+
const fallbackCacheExpired =
|
|
804
|
+
this.#gitWatcherUnavailable &&
|
|
805
|
+
(this.#branchLastFetch === undefined || Date.now() - this.#branchLastFetch >= WATCHER_FAILURE_POLL_TTL_MS);
|
|
806
|
+
if (this.#cachedBranch !== undefined && this.#cachedBranchCwd === gitCwd && !fallbackCacheExpired) {
|
|
807
|
+
return this.#cachedBranch;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
// A reftable repo resolves HEAD by spawning `git symbolic-ref` +
|
|
811
|
+
// `git rev-parse` — the unbounded spawn that froze the render path (F7).
|
|
812
|
+
// A non-reftable repo resolves HEAD with cheap sync filesystem reads, so
|
|
813
|
+
// only the reftable branch moves off the render path, mirroring
|
|
814
|
+
// #getGitStatus and #getJjBranch in this file.
|
|
815
|
+
const repository = git.repo.resolveSync(gitCwd);
|
|
816
|
+
if (repository && git.repo.isReftableSync(repository)) {
|
|
817
|
+
if (this.#branchResolveActive !== undefined) {
|
|
818
|
+
return this.#branchResolveActive.cwd === gitCwd && this.#cachedBranchCwd === gitCwd
|
|
819
|
+
? (this.#cachedBranch ?? null)
|
|
820
|
+
: null;
|
|
821
|
+
}
|
|
822
|
+
const request: BranchResolveRequest = {
|
|
823
|
+
id: ++this.#branchResolveSeq,
|
|
824
|
+
cwd: gitCwd,
|
|
825
|
+
controller: new AbortController(),
|
|
826
|
+
};
|
|
827
|
+
this.#branchResolveActive = request;
|
|
828
|
+
// Capture the cache generation at launch. invalidateGitCaches bumps it
|
|
829
|
+
// on a HEAD move and clears the in-flight slot, so a fresher resolve can
|
|
830
|
+
// start while this one is still pending. Without a generation check the
|
|
831
|
+
// older resolve would finish later, install its stale HEAD, and clear the
|
|
832
|
+
// slot — dropping the fresh result and freezing the status line on the
|
|
833
|
+
// pre-change branch. Mirrors #jjCacheGeneration / #getJjBranch.
|
|
834
|
+
const generation = this.#branchCacheGeneration;
|
|
835
|
+
(async () => {
|
|
836
|
+
let next: string | null = null;
|
|
837
|
+
let repoId: string | null = null;
|
|
838
|
+
try {
|
|
839
|
+
const headState = await git.head.resolve(gitCwd, request.controller.signal);
|
|
840
|
+
repoId = headState?.headPath ?? null;
|
|
841
|
+
next = !headState
|
|
842
|
+
? null
|
|
843
|
+
: headState.kind === "ref"
|
|
844
|
+
? (headState.branchName ?? headState.ref)
|
|
845
|
+
: "detached";
|
|
846
|
+
} catch {
|
|
847
|
+
next = null;
|
|
848
|
+
} finally {
|
|
849
|
+
// Release the slot only if this resolve still owns it: after an
|
|
850
|
+
// invalidation a fresher resolve may hold it, and freeing that
|
|
851
|
+
// slot here would let a third same-generation resolve launch and
|
|
852
|
+
// race the fresh one to the cache commit.
|
|
853
|
+
if (this.#branchResolveActive?.id === request.id) this.#branchResolveActive = undefined;
|
|
854
|
+
}
|
|
855
|
+
// Only the latest generation may update the cache; a mismatch means a
|
|
856
|
+
// newer resolve superseded this one (or the component disposed).
|
|
857
|
+
if (this.#branchCacheGeneration !== generation || this.#disposed) return;
|
|
858
|
+
const prev = this.#cachedBranchCwd === gitCwd ? this.#cachedBranch : undefined;
|
|
859
|
+
this.#cachedBranchCwd = gitCwd;
|
|
860
|
+
this.#cachedBranchRepoId = repoId;
|
|
861
|
+
this.#cachedBranchHasGitRepository = next === null;
|
|
862
|
+
this.#cachedBranch = next;
|
|
863
|
+
this.#branchLastFetch = Date.now();
|
|
864
|
+
if (prev !== next && this.#onBranchChange) this.#onBranchChange();
|
|
865
|
+
})();
|
|
866
|
+
return this.#cachedBranchCwd === gitCwd ? (this.#cachedBranch ?? null) : null;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Non-reftable: cheap sync filesystem read, safe on the render path.
|
|
870
|
+
const head = git.head.resolveSync(gitCwd);
|
|
871
|
+
const gitHeadPath = head?.headPath ?? null;
|
|
872
|
+
this.#cachedBranchCwd = gitCwd;
|
|
873
|
+
this.#cachedBranchRepoId = gitHeadPath;
|
|
874
|
+
this.#branchLastFetch = Date.now();
|
|
875
|
+
if (!head) {
|
|
876
|
+
this.#cachedBranch = null;
|
|
877
|
+
return null;
|
|
878
|
+
}
|
|
879
|
+
this.#cachedBranch = head.kind === "ref" ? (head.branchName ?? head.ref) : "detached";
|
|
880
|
+
return this.#cachedBranch ?? null;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
#isDefaultBranch(branch: string, effectiveGitCwd: string): boolean {
|
|
884
|
+
if (this.#defaultBranchCwd !== effectiveGitCwd) {
|
|
885
|
+
this.#defaultBranch = undefined;
|
|
886
|
+
this.#defaultBranchCwd = effectiveGitCwd;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
if (this.#defaultBranch === undefined) {
|
|
890
|
+
this.#defaultBranch = "main";
|
|
891
|
+
const lookupCwd = effectiveGitCwd;
|
|
892
|
+
(async () => {
|
|
893
|
+
const resolved = await git.branch.default(lookupCwd);
|
|
894
|
+
if (this.#disposed || this.#defaultBranchCwd !== lookupCwd) return;
|
|
895
|
+
if (resolved) {
|
|
896
|
+
this.#defaultBranch = resolved;
|
|
897
|
+
if (this.#onBranchChange) {
|
|
898
|
+
this.#onBranchChange();
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
})();
|
|
902
|
+
}
|
|
903
|
+
return branch === this.#defaultBranch;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
#getGitStatus(effectiveGitCwd?: string): { staged: number; unstaged: number; untracked: number } | null {
|
|
907
|
+
if (!this.#gitEnabled()) return null;
|
|
908
|
+
|
|
909
|
+
const gitCwd = effectiveGitCwd ?? this.#resolveActiveRepoCache().effectiveGitCwd;
|
|
910
|
+
if (this.#gitStatusInFlightCwd !== undefined) {
|
|
911
|
+
return this.#cachedGitStatusCwd === gitCwd ? this.#cachedGitStatus : null;
|
|
912
|
+
}
|
|
913
|
+
if (this.#cachedGitStatusCwd === gitCwd && Date.now() - this.#gitStatusLastFetch < 1000) {
|
|
914
|
+
return this.#cachedGitStatus;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
this.#gitStatusInFlightCwd = gitCwd;
|
|
918
|
+
|
|
919
|
+
(async () => {
|
|
920
|
+
let nextStatus: { staged: number; unstaged: number; untracked: number } | null = null;
|
|
921
|
+
try {
|
|
922
|
+
nextStatus = await git.status.summary(gitCwd);
|
|
923
|
+
} catch {
|
|
924
|
+
nextStatus = null;
|
|
925
|
+
} finally {
|
|
926
|
+
if (this.#gitStatusInFlightCwd === gitCwd) {
|
|
927
|
+
const prev = this.#cachedGitStatusCwd === gitCwd ? this.#cachedGitStatus : null;
|
|
928
|
+
this.#cachedGitStatus = nextStatus;
|
|
929
|
+
this.#cachedGitStatusCwd = gitCwd;
|
|
930
|
+
this.#gitStatusLastFetch = Date.now();
|
|
931
|
+
this.#gitStatusInFlightCwd = undefined;
|
|
932
|
+
if (!this.#disposed && this.#onBranchChange && JSON.stringify(prev) !== JSON.stringify(nextStatus)) {
|
|
933
|
+
this.#onBranchChange();
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
})();
|
|
938
|
+
|
|
939
|
+
return this.#cachedGitStatusCwd === gitCwd ? this.#cachedGitStatus : null;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
// Resolve (and cache per cwd) the jj workspace root, resetting both jj caches
|
|
943
|
+
// on a cwd change so a directory switch refetches label + status.
|
|
944
|
+
#jjRootFor(cwd: string): string | null {
|
|
945
|
+
if (this.#jjRoot === undefined || this.#jjRootCwd !== cwd) {
|
|
946
|
+
this.#jjRootCwd = cwd;
|
|
947
|
+
this.#jjRoot = jj.repo.rootSync(cwd);
|
|
948
|
+
this.#cachedJjBranch = null;
|
|
949
|
+
this.#jjBranchLastFetch = 0;
|
|
950
|
+
this.#cachedJjStatus = null;
|
|
951
|
+
this.#jjStatusLastFetch = 0;
|
|
952
|
+
this.#jjCacheGeneration++;
|
|
953
|
+
}
|
|
954
|
+
return this.#jjRoot;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// jj working-copy bookmark label (nearest bookmark, change-id fallback), shown
|
|
958
|
+
// in the `git` segment where git HEAD is detached/absent under jj. Throttled,
|
|
959
|
+
// cached, and repaints on resolve.
|
|
960
|
+
#getJjBranch(effectiveGitCwd?: string): string | null {
|
|
961
|
+
const cwd = effectiveGitCwd ?? this.#resolveActiveRepoCache().effectiveGitCwd;
|
|
962
|
+
const root = this.#jjRootFor(cwd);
|
|
963
|
+
if (!root) return null;
|
|
964
|
+
if (this.#jjBranchActive || Date.now() - this.#jjBranchLastFetch < JJ_REFRESH_TTL_MS) {
|
|
965
|
+
return this.#cachedJjBranch;
|
|
966
|
+
}
|
|
967
|
+
const request: JjResolveRequest = {
|
|
968
|
+
id: ++this.#jjResolveSeq,
|
|
969
|
+
controller: new AbortController(),
|
|
970
|
+
};
|
|
971
|
+
this.#jjBranchActive = request;
|
|
972
|
+
const generation = this.#jjCacheGeneration;
|
|
973
|
+
(async () => {
|
|
974
|
+
let next: string | null = null;
|
|
975
|
+
try {
|
|
976
|
+
next = await jj.workingCopy.label(root, {
|
|
977
|
+
signal: request.controller.signal,
|
|
978
|
+
timeoutMs: jj.JJ_COMMAND_TIMEOUT_MS,
|
|
979
|
+
});
|
|
980
|
+
} finally {
|
|
981
|
+
if (this.#jjBranchActive?.id === request.id) this.#jjBranchActive = undefined;
|
|
982
|
+
// Advance the throttle only if no reset raced this query; a reset
|
|
983
|
+
// leaves LastFetch at 0 so the current root refetches instead of
|
|
984
|
+
// being throttled on a superseded result.
|
|
985
|
+
if (this.#jjCacheGeneration === generation) this.#jjBranchLastFetch = Date.now();
|
|
986
|
+
}
|
|
987
|
+
// Drop a result whose caches were reset mid-flight — a repo switch OR a
|
|
988
|
+
// same-root HEAD/bookmark move — so a superseded label never lands in
|
|
989
|
+
// the live cache.
|
|
990
|
+
if (this.#jjCacheGeneration !== generation || this.#disposed) return;
|
|
991
|
+
const changed = next !== this.#cachedJjBranch;
|
|
992
|
+
this.#cachedJjBranch = next;
|
|
993
|
+
if (changed) this.#onBranchChange?.();
|
|
994
|
+
})();
|
|
995
|
+
return this.#cachedJjBranch;
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
// jj working-copy status counts (`@` vs its parent), used in place of git
|
|
999
|
+
// status in a jj repo where `git status` has no `.git` to read. Throttled,
|
|
1000
|
+
// cached, and repaints on resolve like #getJjBranch.
|
|
1001
|
+
#getJjStatus(effectiveGitCwd?: string): { staged: number; unstaged: number; untracked: number } | null {
|
|
1002
|
+
const cwd = effectiveGitCwd ?? this.#resolveActiveRepoCache().effectiveGitCwd;
|
|
1003
|
+
const root = this.#jjRootFor(cwd);
|
|
1004
|
+
if (!root) return null;
|
|
1005
|
+
if (this.#jjStatusActive || Date.now() - this.#jjStatusLastFetch < JJ_REFRESH_TTL_MS) {
|
|
1006
|
+
return this.#cachedJjStatus;
|
|
1007
|
+
}
|
|
1008
|
+
const request: JjResolveRequest = {
|
|
1009
|
+
id: ++this.#jjResolveSeq,
|
|
1010
|
+
controller: new AbortController(),
|
|
1011
|
+
};
|
|
1012
|
+
this.#jjStatusActive = request;
|
|
1013
|
+
const generation = this.#jjCacheGeneration;
|
|
1014
|
+
(async () => {
|
|
1015
|
+
let next: { staged: number; unstaged: number; untracked: number } | null = null;
|
|
1016
|
+
try {
|
|
1017
|
+
next = await jj.status.summary(root, {
|
|
1018
|
+
signal: request.controller.signal,
|
|
1019
|
+
timeoutMs: jj.JJ_COMMAND_TIMEOUT_MS,
|
|
1020
|
+
});
|
|
1021
|
+
} finally {
|
|
1022
|
+
if (this.#jjStatusActive?.id === request.id) this.#jjStatusActive = undefined;
|
|
1023
|
+
if (this.#jjCacheGeneration === generation) this.#jjStatusLastFetch = Date.now();
|
|
1024
|
+
}
|
|
1025
|
+
if (this.#jjCacheGeneration !== generation || this.#disposed) return;
|
|
1026
|
+
const prev = this.#cachedJjStatus;
|
|
1027
|
+
this.#cachedJjStatus = next;
|
|
1028
|
+
if (JSON.stringify(prev) !== JSON.stringify(next)) this.#onBranchChange?.();
|
|
1029
|
+
})();
|
|
1030
|
+
return this.#cachedJjStatus;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
#lookupPr(effectiveGitCwd?: string): { number: number; url: string } | null {
|
|
1034
|
+
if (!this.#gitEnabled()) return null;
|
|
1035
|
+
|
|
1036
|
+
const gitCwd = effectiveGitCwd ?? this.#resolveActiveRepoCache().effectiveGitCwd;
|
|
1037
|
+
const branch = this.#getCurrentBranch(gitCwd);
|
|
1038
|
+
const currentContext = branch ? createPrCacheContext(branch, this.#cachedBranchRepoId ?? null) : null;
|
|
1039
|
+
|
|
1040
|
+
if (canReuseCachedPr(this.#cachedPr, this.#cachedPrContext, currentContext)) {
|
|
1041
|
+
return this.#cachedPr ?? null;
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
const stalePr = this.#cachedPr;
|
|
1045
|
+
|
|
1046
|
+
if (!branch) {
|
|
1047
|
+
this.#cachedPr = null;
|
|
1048
|
+
this.#cachedPrContext = undefined;
|
|
1049
|
+
return null;
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
// Don't look up if detached, default branch, or already in flight.
|
|
1053
|
+
if (branch === "detached" || this.#isDefaultBranch(branch, gitCwd) || this.#prLookupInFlight) {
|
|
1054
|
+
return stalePr ?? null;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
this.#prLookupInFlight = true;
|
|
1058
|
+
const lookupContext = currentContext;
|
|
1059
|
+
const lookupCwd = gitCwd;
|
|
1060
|
+
|
|
1061
|
+
// Fire async lookup, keep stale value visible until resolved
|
|
1062
|
+
(async () => {
|
|
1063
|
+
// Helper: only write cache if branch/repo context hasn't changed since launch
|
|
1064
|
+
const setCachedPr = (value: { number: number; url: string } | null) => {
|
|
1065
|
+
const latestBranch = this.#getCurrentBranch(lookupCwd);
|
|
1066
|
+
const latestContext = latestBranch
|
|
1067
|
+
? createPrCacheContext(latestBranch, this.#cachedBranchRepoId ?? null)
|
|
1068
|
+
: undefined;
|
|
1069
|
+
if (lookupContext && isSamePrCacheContext(latestContext, lookupContext)) {
|
|
1070
|
+
this.#cachedPr = value;
|
|
1071
|
+
this.#cachedPrContext = lookupContext;
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
try {
|
|
1075
|
+
// Route through the shared `gh` helper so the child inherits
|
|
1076
|
+
// `GH_NON_INTERACTIVE_ENV` (disables terminal/keychain prompts) and
|
|
1077
|
+
// hard-terminates on the git command deadline instead of stalling
|
|
1078
|
+
// the status-line indefinitely (#4234). Requires `gh repo set-default`;
|
|
1079
|
+
// non-zero exit still falls through to the null cache below.
|
|
1080
|
+
const result = await git.github.run(
|
|
1081
|
+
lookupCwd,
|
|
1082
|
+
["pr", "view", "--json", "number,url"],
|
|
1083
|
+
AbortSignal.timeout(git.GIT_COMMAND_TIMEOUT_MS),
|
|
1084
|
+
);
|
|
1085
|
+
if (this.#disposed) return;
|
|
1086
|
+
if (result.exitCode !== 0) {
|
|
1087
|
+
setCachedPr(null);
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
const pr = JSON.parse(result.stdout) as { number: number; url: string };
|
|
1091
|
+
if (typeof pr.number === "number") {
|
|
1092
|
+
setCachedPr({ number: pr.number, url: pr.url });
|
|
1093
|
+
} else {
|
|
1094
|
+
setCachedPr(null);
|
|
1095
|
+
}
|
|
1096
|
+
} catch {
|
|
1097
|
+
if (this.#disposed) return;
|
|
1098
|
+
setCachedPr(null);
|
|
1099
|
+
} finally {
|
|
1100
|
+
this.#prLookupInFlight = false;
|
|
1101
|
+
if (!this.#disposed && this.#onBranchChange) {
|
|
1102
|
+
this.#onBranchChange();
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
})();
|
|
1106
|
+
|
|
1107
|
+
return stalePr ?? null;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
#getTokensPerSecond(): number | null {
|
|
1111
|
+
// Aggregate tok/s across the main session AND every live vibe worker.
|
|
1112
|
+
// In vibe mode the director is often idle while workers stream, so the
|
|
1113
|
+
// main session's own rate alone would show a stale/zero value while
|
|
1114
|
+
// parallel work is actively generating tokens.
|
|
1115
|
+
const workerRate = this.#getVibeWorkerTokensPerSecond();
|
|
1116
|
+
if (workerRate !== null) {
|
|
1117
|
+
// At least one worker is streaming — add the director's live rate
|
|
1118
|
+
// only when it is itself streaming (a finalized last-turn rate would
|
|
1119
|
+
// double-count and overstate throughput).
|
|
1120
|
+
const mainRate = this.session.isStreaming ? calculateTokensPerSecond(this.session.state.messages, true) : 0;
|
|
1121
|
+
return (mainRate ?? 0) + workerRate;
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// No workers streaming — fall back to the main session's own rate with
|
|
1125
|
+
// its sticky per-assistant-message cache so the badge doesn't flicker
|
|
1126
|
+
// off in the brief gap between stream end and the finalized message.
|
|
1127
|
+
return this.#getMainSessionTokensPerSecond();
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
/**
|
|
1131
|
+
* Main session's tok/s with sticky caching keyed on the last assistant
|
|
1132
|
+
* message timestamp. Preserves the pre-aggregation behavior when no vibe
|
|
1133
|
+
* workers are active.
|
|
1134
|
+
*/
|
|
1135
|
+
#getMainSessionTokensPerSecond(): number | null {
|
|
1136
|
+
let lastAssistantTimestamp: number | null = null;
|
|
1137
|
+
for (let i = this.session.state.messages.length - 1; i >= 0; i--) {
|
|
1138
|
+
const message = this.session.state.messages[i];
|
|
1139
|
+
if (message?.role === "assistant") {
|
|
1140
|
+
lastAssistantTimestamp = message.timestamp;
|
|
1141
|
+
break;
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
if (lastAssistantTimestamp === null) {
|
|
1146
|
+
this.#lastTokensPerSecond = null;
|
|
1147
|
+
this.#lastTokensPerSecondTimestamp = null;
|
|
1148
|
+
return null;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
const rate = calculateTokensPerSecond(this.session.state.messages, this.session.isStreaming);
|
|
1152
|
+
if (rate !== null) {
|
|
1153
|
+
this.#lastTokensPerSecond = rate;
|
|
1154
|
+
this.#lastTokensPerSecondTimestamp = lastAssistantTimestamp;
|
|
1155
|
+
return rate;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
if (this.#lastTokensPerSecondTimestamp === lastAssistantTimestamp) {
|
|
1159
|
+
return this.#lastTokensPerSecond;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
return null;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
/**
|
|
1166
|
+
* Aggregate tok/s across every live vibe worker session owned by this
|
|
1167
|
+
* session. Returns null when no workers are streaming (so the main
|
|
1168
|
+
* session's own rate shines through unchanged). The aggregation itself is
|
|
1169
|
+
* injected via {@link setVibeWorkerTokenRateProvider} to keep this render
|
|
1170
|
+
* layer off the heavy vibe/task dependency graph.
|
|
1171
|
+
*/
|
|
1172
|
+
#getVibeWorkerTokensPerSecond(): number | null {
|
|
1173
|
+
return this.#vibeWorkerTokenRate?.() ?? null;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
#formatUsageContextKey(activeProvider: string | undefined, identity: OAuthAccountIdentity | undefined): string {
|
|
1177
|
+
if (!activeProvider) return "";
|
|
1178
|
+
// orgId is part of the key: rotating between two same-email Anthropic
|
|
1179
|
+
// subscriptions must invalidate the cached usage immediately instead of
|
|
1180
|
+
// showing the previous org's quota for the rest of the cache TTL.
|
|
1181
|
+
return [
|
|
1182
|
+
activeProvider,
|
|
1183
|
+
identity?.accountId ?? "",
|
|
1184
|
+
identity?.email ?? "",
|
|
1185
|
+
identity?.projectId ?? "",
|
|
1186
|
+
identity?.orgId ?? "",
|
|
1187
|
+
].join("\0");
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
#getUsageContextKey(session: AgentSession): string {
|
|
1191
|
+
const activeProvider = session.state.model?.provider ?? session.model?.provider;
|
|
1192
|
+
const identity = activeProvider
|
|
1193
|
+
? session.modelRegistry?.authStorage?.getOAuthAccountIdentity(activeProvider, session.sessionId)
|
|
1194
|
+
: undefined;
|
|
1195
|
+
return this.#formatUsageContextKey(activeProvider, identity);
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
/**
|
|
1199
|
+
* Startup redraws only arm a short-delayed task; timeout releases the render
|
|
1200
|
+
* cadence while a late successful fetch can still refresh the cached segment.
|
|
1201
|
+
*/
|
|
1202
|
+
refreshUsageInBackground(): void {
|
|
1203
|
+
const now = Date.now();
|
|
1204
|
+
const session = this.session;
|
|
1205
|
+
const usageContextKey = this.#getUsageContextKey(session);
|
|
1206
|
+
if (this.#cachedUsageContextKey !== usageContextKey) {
|
|
1207
|
+
this.#cachedUsage = null;
|
|
1208
|
+
this.#usageFetchedAt = 0;
|
|
1209
|
+
this.#cachedUsageContextKey = usageContextKey;
|
|
1210
|
+
}
|
|
1211
|
+
if (this.#usageInFlight || this.#usageStartTimer) return;
|
|
1212
|
+
if (this.#usageFetchedAt > 0 && now - this.#usageFetchedAt < 5 * 60_000) return;
|
|
1213
|
+
const fetcher = (session as { fetchUsageReports?: (signal?: AbortSignal) => Promise<unknown> }).fetchUsageReports;
|
|
1214
|
+
if (typeof fetcher !== "function") return;
|
|
1215
|
+
this.#usageInFlight = true;
|
|
1216
|
+
this.#usageStartTimer = setTimeout(() => {
|
|
1217
|
+
this.#usageStartTimer = null;
|
|
1218
|
+
void this.#runUsageRefresh(session, fetcher);
|
|
1219
|
+
}, STATUS_USAGE_START_DELAY_MS);
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
async #runUsageRefresh(session: AgentSession, fetcher: (signal?: AbortSignal) => Promise<unknown>): Promise<void> {
|
|
1223
|
+
if (this.#disposed || this.session !== session) {
|
|
1224
|
+
this.#usageInFlight = false;
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
const sequence = ++this.#usageRefreshSequence;
|
|
1228
|
+
const signal = AbortSignal.timeout(STATUS_USAGE_REFRESH_TIMEOUT_MS);
|
|
1229
|
+
let reportsPromise: Promise<unknown> | undefined;
|
|
1230
|
+
try {
|
|
1231
|
+
reportsPromise = fetcher.call(session, signal);
|
|
1232
|
+
this.#applyUsageRefreshReports(
|
|
1233
|
+
session,
|
|
1234
|
+
await this.#raceUsageRefreshWithSignal(reportsPromise, signal),
|
|
1235
|
+
sequence,
|
|
1236
|
+
);
|
|
1237
|
+
} catch {
|
|
1238
|
+
if (this.session !== session) return;
|
|
1239
|
+
this.#usageFetchedAt = Date.now();
|
|
1240
|
+
if (signal.aborted && reportsPromise) {
|
|
1241
|
+
this.#observeLateUsageRefresh(session, reportsPromise, sequence);
|
|
1242
|
+
}
|
|
1243
|
+
} finally {
|
|
1244
|
+
if (this.session === session) this.#usageInFlight = false;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
#applyUsageRefreshReports(session: AgentSession, reports: unknown, sequence: number): void {
|
|
1249
|
+
if (this.#disposed || this.session !== session || sequence < this.#latestAppliedUsageRefreshSequence) {
|
|
1250
|
+
return;
|
|
1251
|
+
}
|
|
1252
|
+
this.#latestAppliedUsageRefreshSequence = sequence;
|
|
1253
|
+
const activeProvider = session.state.model?.provider ?? session.model?.provider;
|
|
1254
|
+
const activeIdentity =
|
|
1255
|
+
activeProvider && session.modelRegistry?.authStorage
|
|
1256
|
+
? session.modelRegistry.authStorage.getOAuthAccountIdentity(activeProvider, session.sessionId)
|
|
1257
|
+
: undefined;
|
|
1258
|
+
const normalized = this.#normalizeUsageReports(reports, activeProvider, activeIdentity);
|
|
1259
|
+
const resetSnapshot =
|
|
1260
|
+
activeProvider === "openai-codex" ? this.#normalizeCodexResetSnapshot(reports, activeIdentity) : null;
|
|
1261
|
+
this.#cachedUsage = normalized;
|
|
1262
|
+
this.#usageFetchedAt = Date.now();
|
|
1263
|
+
if (!resetSnapshot) return;
|
|
1264
|
+
const contextKey = this.#formatUsageContextKey(activeProvider, activeIdentity);
|
|
1265
|
+
const previous = this.#codexResetSnapshots.get(contextKey);
|
|
1266
|
+
this.#codexResetSnapshots.set(contextKey, resetSnapshot);
|
|
1267
|
+
if (!previous || !settings.get("tui.codexResetFireworks")) return;
|
|
1268
|
+
const event = detectCodexResetFireworks(previous, resetSnapshot);
|
|
1269
|
+
if (event) this.#onCodexResetFireworks?.(event);
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
#observeLateUsageRefresh(session: AgentSession, reportsPromise: Promise<unknown>, sequence: number): void {
|
|
1273
|
+
void reportsPromise
|
|
1274
|
+
.then(reports => {
|
|
1275
|
+
this.#applyUsageRefreshReports(session, reports, sequence);
|
|
1276
|
+
})
|
|
1277
|
+
.catch(() => {
|
|
1278
|
+
if (this.#disposed || this.session !== session || sequence < this.#latestAppliedUsageRefreshSequence) {
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
this.#usageFetchedAt = Date.now();
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
async #raceUsageRefreshWithSignal(promise: Promise<unknown>, signal: AbortSignal): Promise<unknown> {
|
|
1286
|
+
if (signal.aborted) throw signal.reason;
|
|
1287
|
+
const aborted = Promise.withResolvers<never>();
|
|
1288
|
+
const onAbort = () => aborted.reject(signal.reason);
|
|
1289
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1290
|
+
try {
|
|
1291
|
+
return await Promise.race([promise, aborted.promise]);
|
|
1292
|
+
} finally {
|
|
1293
|
+
signal.removeEventListener("abort", onAbort);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
#normalizeCodexResetSnapshot(
|
|
1298
|
+
reports: unknown,
|
|
1299
|
+
activeIdentity: OAuthAccountIdentity | undefined,
|
|
1300
|
+
): CodexResetUsageSnapshot | null {
|
|
1301
|
+
if (!Array.isArray(reports)) return null;
|
|
1302
|
+
let matchingReport: UsageReport | undefined;
|
|
1303
|
+
for (const report of reports) {
|
|
1304
|
+
if (!report || typeof report !== "object") continue;
|
|
1305
|
+
if (
|
|
1306
|
+
!("provider" in report) ||
|
|
1307
|
+
report.provider !== "openai-codex" ||
|
|
1308
|
+
!("limits" in report) ||
|
|
1309
|
+
!Array.isArray(report.limits)
|
|
1310
|
+
) {
|
|
1311
|
+
continue;
|
|
1312
|
+
}
|
|
1313
|
+
// The report boundary above validates the fields this extractor iterates;
|
|
1314
|
+
// optional metadata and credit fields are narrowed again before use.
|
|
1315
|
+
const usageReport = report as UsageReport;
|
|
1316
|
+
if (!codexReportMatchesExactIdentity(usageReport, activeIdentity)) continue;
|
|
1317
|
+
matchingReport = usageReport;
|
|
1318
|
+
break;
|
|
1319
|
+
}
|
|
1320
|
+
if (!matchingReport) return null;
|
|
1321
|
+
|
|
1322
|
+
const plan =
|
|
1323
|
+
typeof matchingReport.metadata?.planType === "string" && matchingReport.metadata.planType
|
|
1324
|
+
? matchingReport.metadata.planType
|
|
1325
|
+
: undefined;
|
|
1326
|
+
let sevenDay: CodexResetUsageSnapshot["sevenDay"];
|
|
1327
|
+
let sevenDayTier: string | undefined;
|
|
1328
|
+
for (const limit of matchingReport.limits) {
|
|
1329
|
+
if (!limit || typeof limit !== "object") continue;
|
|
1330
|
+
const candidate = limit as {
|
|
1331
|
+
scope?: { windowId?: string; tier?: string };
|
|
1332
|
+
window?: { resetsAt?: number };
|
|
1333
|
+
amount?: { usedFraction?: number };
|
|
1334
|
+
};
|
|
1335
|
+
const fraction = candidate.amount?.usedFraction;
|
|
1336
|
+
if (candidate.scope?.windowId !== "7d" || typeof fraction !== "number" || !Number.isFinite(fraction)) {
|
|
1337
|
+
continue;
|
|
1338
|
+
}
|
|
1339
|
+
const tier =
|
|
1340
|
+
typeof candidate.scope?.tier === "string" && candidate.scope.tier ? candidate.scope.tier : undefined;
|
|
1341
|
+
if (sevenDay && (sevenDayTier === undefined || tier)) continue;
|
|
1342
|
+
const resetsAt = candidate.window?.resetsAt;
|
|
1343
|
+
sevenDay = {
|
|
1344
|
+
percent: fraction * 100,
|
|
1345
|
+
resetsAt: typeof resetsAt === "number" && Number.isFinite(resetsAt) ? resetsAt : undefined,
|
|
1346
|
+
tier,
|
|
1347
|
+
plan,
|
|
1348
|
+
};
|
|
1349
|
+
sevenDayTier = tier;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
const fetchedAt = matchingReport.fetchedAt;
|
|
1353
|
+
const availableCount = matchingReport.resetCredits?.availableCount;
|
|
1354
|
+
const observedAt = typeof fetchedAt === "number" && Number.isFinite(fetchedAt) ? fetchedAt : undefined;
|
|
1355
|
+
const savedResets =
|
|
1356
|
+
typeof availableCount === "number" && Number.isFinite(availableCount)
|
|
1357
|
+
? Math.max(0, Math.trunc(availableCount))
|
|
1358
|
+
: undefined;
|
|
1359
|
+
if (!sevenDay && savedResets === undefined) return null;
|
|
1360
|
+
return { observedAt, sevenDay, savedResets };
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
#normalizeUsageReports(
|
|
1364
|
+
reports: unknown,
|
|
1365
|
+
activeProvider?: string,
|
|
1366
|
+
activeIdentity?: OAuthAccountIdentity,
|
|
1367
|
+
): {
|
|
1368
|
+
tier?: string;
|
|
1369
|
+
fiveHour?: { percent: number; resetMinutes?: number };
|
|
1370
|
+
sevenDay?: { percent: number; resetHours?: number };
|
|
1371
|
+
} | null {
|
|
1372
|
+
if (!Array.isArray(reports)) return null;
|
|
1373
|
+
let fiveHour: { percent: number; resetMinutes?: number } | undefined;
|
|
1374
|
+
let sevenDay: { percent: number; resetHours?: number } | undefined;
|
|
1375
|
+
let fiveHourTier: string | undefined;
|
|
1376
|
+
let sevenDayTier: string | undefined;
|
|
1377
|
+
const now = Date.now();
|
|
1378
|
+
for (const report of reports) {
|
|
1379
|
+
if (!report || typeof report !== "object") continue;
|
|
1380
|
+
const provider = (report as { provider?: unknown }).provider;
|
|
1381
|
+
if (activeProvider && provider !== activeProvider) continue;
|
|
1382
|
+
const limits = (report as { limits?: unknown }).limits;
|
|
1383
|
+
if (!Array.isArray(limits)) continue;
|
|
1384
|
+
const usageReport = report as UsageReport;
|
|
1385
|
+
for (const limit of limits) {
|
|
1386
|
+
if (!limit || typeof limit !== "object") continue;
|
|
1387
|
+
if (activeIdentity && !limitMatchesActiveAccount(usageReport, limit as UsageLimit, activeIdentity)) {
|
|
1388
|
+
continue;
|
|
1389
|
+
}
|
|
1390
|
+
const l = limit as {
|
|
1391
|
+
scope?: { windowId?: string; tier?: string };
|
|
1392
|
+
window?: { resetsAt?: number };
|
|
1393
|
+
amount?: { usedFraction?: number };
|
|
1394
|
+
};
|
|
1395
|
+
const fraction = l.amount?.usedFraction;
|
|
1396
|
+
if (typeof fraction !== "number") continue;
|
|
1397
|
+
const windowId = l.scope?.windowId;
|
|
1398
|
+
const tier = l.scope?.tier;
|
|
1399
|
+
const resetsAt = l.window?.resetsAt;
|
|
1400
|
+
// Accept tiered limits, but prefer untiered (backward compat with Anthropic).
|
|
1401
|
+
// An untiered limit always replaces a tiered one; among same-tieredness, first wins.
|
|
1402
|
+
if (windowId === "5h" && (!fiveHour || (fiveHourTier !== undefined && !tier))) {
|
|
1403
|
+
fiveHour = {
|
|
1404
|
+
percent: fraction * 100,
|
|
1405
|
+
resetMinutes:
|
|
1406
|
+
typeof resetsAt === "number" ? Math.max(0, Math.round((resetsAt - now) / 60_000)) : undefined,
|
|
1407
|
+
};
|
|
1408
|
+
fiveHourTier = tier || undefined;
|
|
1409
|
+
}
|
|
1410
|
+
if (windowId === "7d" && (!sevenDay || (sevenDayTier !== undefined && !tier))) {
|
|
1411
|
+
sevenDay = {
|
|
1412
|
+
percent: fraction * 100,
|
|
1413
|
+
resetHours:
|
|
1414
|
+
typeof resetsAt === "number" ? Math.max(0, Math.round((resetsAt - now) / 3_600_000)) : undefined,
|
|
1415
|
+
};
|
|
1416
|
+
sevenDayTier = tier || undefined;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
if (!fiveHour && !sevenDay) return null;
|
|
1421
|
+
// Single compact label; prefer the five-hour tier if displayed windows ever disagree.
|
|
1422
|
+
const effectiveTier = fiveHourTier ?? sevenDayTier;
|
|
1423
|
+
return { tier: effectiveTier, fiveHour, sevenDay };
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
/**
|
|
1427
|
+
* Used-tokens / context-window totals for the status-line context% segment,
|
|
1428
|
+
* memoized so the per-event redraw stays O(1) when nothing changed.
|
|
1429
|
+
*
|
|
1430
|
+
* The numerator comes from `session.getContextUsage()`, which anchors on the
|
|
1431
|
+
* last assistant's real prompt-token count — so the bar matches the provider
|
|
1432
|
+
* and the `/context` panel — and reports `null` while that count is unknown
|
|
1433
|
+
* (right after compaction, before the next response). Exposed (non-private)
|
|
1434
|
+
* for unit tests and the collab host's state broadcast.
|
|
1435
|
+
*/
|
|
1436
|
+
getCachedContextBreakdown(): { usedTokens: number; contextWindow: number } {
|
|
1437
|
+
const messages = this.session.messages ?? EMPTY_MESSAGES;
|
|
1438
|
+
const modelContextWindow = this.session.model?.contextWindow ?? 0;
|
|
1439
|
+
const length = messages.length;
|
|
1440
|
+
const lastFingerprint = length > 0 ? messageFingerprint(messages[length - 1]!) : undefined;
|
|
1441
|
+
// Bumps when the in-flight pending snapshot is set/cleared. Without it a
|
|
1442
|
+
// value computed mid-turn (estimate of the active tail) would survive after
|
|
1443
|
+
// the turn ends/aborts, since clearing the snapshot touches no message.
|
|
1444
|
+
const contextUsageRevision = this.session.contextUsageRevision ?? 0;
|
|
1445
|
+
|
|
1446
|
+
const systemPrompt = this.session.systemPrompt;
|
|
1447
|
+
const tools = this.session.agent?.state?.tools;
|
|
1448
|
+
const skills = this.session.skills;
|
|
1449
|
+
|
|
1450
|
+
const cache = this.#contextUsageCache;
|
|
1451
|
+
if (
|
|
1452
|
+
cache &&
|
|
1453
|
+
cache.messagesRef === messages &&
|
|
1454
|
+
cache.length === length &&
|
|
1455
|
+
cache.lastFingerprint === lastFingerprint &&
|
|
1456
|
+
cache.modelContextWindow === modelContextWindow &&
|
|
1457
|
+
cache.contextUsageRevision === contextUsageRevision &&
|
|
1458
|
+
cache.systemPromptRef === systemPrompt &&
|
|
1459
|
+
cache.toolsRef === tools &&
|
|
1460
|
+
cache.skillsRef === skills
|
|
1461
|
+
) {
|
|
1462
|
+
return { usedTokens: cache.usedTokens, contextWindow: cache.contextWindow };
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
const usage = this.session.getContextUsage();
|
|
1466
|
+
const usedTokens = usage?.tokens ?? 0;
|
|
1467
|
+
const contextWindow = usage?.contextWindow ?? modelContextWindow;
|
|
1468
|
+
this.#contextUsageCache = {
|
|
1469
|
+
messagesRef: messages,
|
|
1470
|
+
length,
|
|
1471
|
+
lastFingerprint,
|
|
1472
|
+
modelContextWindow,
|
|
1473
|
+
contextUsageRevision,
|
|
1474
|
+
usedTokens,
|
|
1475
|
+
contextWindow,
|
|
1476
|
+
systemPromptRef: systemPrompt,
|
|
1477
|
+
toolsRef: tools,
|
|
1478
|
+
skillsRef: skills,
|
|
1479
|
+
};
|
|
1480
|
+
return { usedTokens, contextWindow };
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
#buildSegmentContext(
|
|
1484
|
+
width: number,
|
|
1485
|
+
segmentOptions: StatusLineSettings["segmentOptions"],
|
|
1486
|
+
includePath: boolean,
|
|
1487
|
+
includeContext: boolean,
|
|
1488
|
+
includeGit: boolean,
|
|
1489
|
+
includePr: boolean,
|
|
1490
|
+
): SegmentContext {
|
|
1491
|
+
const state = this.session.state;
|
|
1492
|
+
|
|
1493
|
+
// Trigger background fetch (5-min TTL); render uses cached value
|
|
1494
|
+
this.refreshUsageInBackground();
|
|
1495
|
+
|
|
1496
|
+
// Get usage statistics
|
|
1497
|
+
const aggregateUsageStats = this.session.sessionManager?.getUsageStatistics() ?? {
|
|
1498
|
+
input: 0,
|
|
1499
|
+
output: 0,
|
|
1500
|
+
cacheRead: 0,
|
|
1501
|
+
cacheWrite: 0,
|
|
1502
|
+
totalTokens: 0,
|
|
1503
|
+
orchestrationInput: 0,
|
|
1504
|
+
orchestrationOutput: 0,
|
|
1505
|
+
orchestrationCacheRead: 0,
|
|
1506
|
+
premiumRequests: 0,
|
|
1507
|
+
cost: 0,
|
|
1508
|
+
};
|
|
1509
|
+
const usageStats = {
|
|
1510
|
+
...aggregateUsageStats,
|
|
1511
|
+
tokensPerSecond: this.#getTokensPerSecond(),
|
|
1512
|
+
};
|
|
1513
|
+
|
|
1514
|
+
let contextWindow = state.model?.contextWindow ?? this.session.model?.contextWindow ?? 0;
|
|
1515
|
+
let contextPercent: number | null = 0;
|
|
1516
|
+
let contextTokens = 0;
|
|
1517
|
+
if (includeContext) {
|
|
1518
|
+
const breakdown = this.getCachedContextBreakdown();
|
|
1519
|
+
contextTokens = breakdown.usedTokens;
|
|
1520
|
+
contextWindow = breakdown.contextWindow || contextWindow;
|
|
1521
|
+
contextPercent = contextWindow > 0 ? (breakdown.usedTokens / contextWindow) * 100 : null;
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
// Collab guest: context comes from the host's state frames — the local
|
|
1525
|
+
// replica does no accounting of its own.
|
|
1526
|
+
const collabState = this.#collabStatus?.stateOverride;
|
|
1527
|
+
if (collabState?.contextUsage) {
|
|
1528
|
+
contextWindow = collabState.contextUsage.contextWindow || contextWindow;
|
|
1529
|
+
contextTokens = collabState.contextUsage.tokens ?? contextTokens;
|
|
1530
|
+
contextPercent = collabState.contextUsage.percent ?? contextPercent;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
const shouldResolveActiveRepo = this.#gitEnabled() && (includePath || includeGit || includePr);
|
|
1534
|
+
const projectDir = getProjectDir();
|
|
1535
|
+
const activeRepoCache = shouldResolveActiveRepo
|
|
1536
|
+
? this.#resolveActiveRepoCache()
|
|
1537
|
+
: { projectDir, activeRepo: null, effectiveGitCwd: projectDir, worktree: null };
|
|
1538
|
+
let gitBranch = includeGit || includePr ? this.#getCurrentBranch(activeRepoCache.effectiveGitCwd) : null;
|
|
1539
|
+
// A jj repo has no git branch to read: git HEAD is detached (colocated) or
|
|
1540
|
+
// absent. A pending reftable resolve owns this cwd as an explicit Git repo,
|
|
1541
|
+
// so it must not be mistaken for an absent Git checkout and fall through to
|
|
1542
|
+
// an ancestor jj workspace.
|
|
1543
|
+
const gitHeadResolvePending = this.#branchResolveActive?.cwd === activeRepoCache.effectiveGitCwd;
|
|
1544
|
+
const gitHeadIsJjLike =
|
|
1545
|
+
!this.#cachedBranchHasGitRepository &&
|
|
1546
|
+
!gitHeadResolvePending &&
|
|
1547
|
+
(gitBranch === "detached" || gitBranch === null);
|
|
1548
|
+
if (includeGit && gitHeadIsJjLike) {
|
|
1549
|
+
gitBranch = this.#getJjBranch(activeRepoCache.effectiveGitCwd) ?? gitBranch;
|
|
1550
|
+
}
|
|
1551
|
+
const gitStatus = includeGit
|
|
1552
|
+
? ((gitHeadIsJjLike ? this.#getJjStatus(activeRepoCache.effectiveGitCwd) : null) ??
|
|
1553
|
+
this.#getGitStatus(activeRepoCache.effectiveGitCwd))
|
|
1554
|
+
: null;
|
|
1555
|
+
const gitPr = includePr ? this.#lookupPr(activeRepoCache.effectiveGitCwd) : null;
|
|
1556
|
+
return {
|
|
1557
|
+
session: this.session,
|
|
1558
|
+
focusedAgentId: this.#focusedAgentId,
|
|
1559
|
+
activeRepo: activeRepoCache.activeRepo,
|
|
1560
|
+
width,
|
|
1561
|
+
options: segmentOptions ?? {},
|
|
1562
|
+
compactThinkingLevel: this.#resolveSettings().compactThinkingLevel ?? false,
|
|
1563
|
+
planMode: this.#planModeStatus,
|
|
1564
|
+
loopMode: this.#loopModeStatus,
|
|
1565
|
+
prewalk:
|
|
1566
|
+
typeof this.session.getPrewalkState === "function" && this.session.getPrewalkState()
|
|
1567
|
+
? { enabled: true }
|
|
1568
|
+
: null,
|
|
1569
|
+
goalMode: this.#goalModeStatus,
|
|
1570
|
+
vibeMode: this.#vibeModeStatus,
|
|
1571
|
+
collab: this.#collabStatus,
|
|
1572
|
+
usageStats,
|
|
1573
|
+
contextPercent,
|
|
1574
|
+
contextTokens,
|
|
1575
|
+
contextWindow,
|
|
1576
|
+
autoCompactEnabled: this.#autoCompactEnabled,
|
|
1577
|
+
subagentCount: this.#subagentCount,
|
|
1578
|
+
activeMs: this.getActiveMs(),
|
|
1579
|
+
git: {
|
|
1580
|
+
branch: gitBranch,
|
|
1581
|
+
status: gitStatus,
|
|
1582
|
+
pr: gitPr,
|
|
1583
|
+
},
|
|
1584
|
+
worktree: activeRepoCache.worktree,
|
|
1585
|
+
usage: this.#cachedUsage,
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
#resolveSettings(): EffectiveStatusLineSettings {
|
|
1590
|
+
if (this.#effectiveSettings === undefined) {
|
|
1591
|
+
this.#effectiveSettings = this.#computeEffectiveSettings();
|
|
1592
|
+
}
|
|
1593
|
+
return this.#effectiveSettings;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
#computeEffectiveSettings(): EffectiveStatusLineSettings {
|
|
1597
|
+
const preset = this.#settings.preset ?? "default";
|
|
1598
|
+
const presetDef = getPreset(preset);
|
|
1599
|
+
const useCustomSegments = preset === "custom";
|
|
1600
|
+
const mergedSegmentOptions: StatusLineSettings["segmentOptions"] = {};
|
|
1601
|
+
|
|
1602
|
+
for (const [segment, options] of Object.entries(presetDef.segmentOptions ?? {})) {
|
|
1603
|
+
mergedSegmentOptions[segment as keyof StatusLineSegmentOptions] = { ...(options as Record<string, unknown>) };
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1606
|
+
for (const [segment, options] of Object.entries(this.#settings.segmentOptions ?? {})) {
|
|
1607
|
+
const current = mergedSegmentOptions[segment as keyof StatusLineSegmentOptions] ?? {};
|
|
1608
|
+
mergedSegmentOptions[segment as keyof StatusLineSegmentOptions] = {
|
|
1609
|
+
...(current as Record<string, unknown>),
|
|
1610
|
+
...(options as Record<string, unknown>),
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
const leftSegments = useCustomSegments
|
|
1615
|
+
? (this.#settings.leftSegments ?? presetDef.leftSegments)
|
|
1616
|
+
: presetDef.leftSegments;
|
|
1617
|
+
const rightSegments = useCustomSegments
|
|
1618
|
+
? (this.#settings.rightSegments ?? presetDef.rightSegments)
|
|
1619
|
+
: presetDef.rightSegments;
|
|
1620
|
+
|
|
1621
|
+
return {
|
|
1622
|
+
...this.#settings,
|
|
1623
|
+
leftSegments,
|
|
1624
|
+
rightSegments,
|
|
1625
|
+
separator: this.#settings.separator ?? presetDef.separator,
|
|
1626
|
+
segmentOptions: mergedSegmentOptions,
|
|
1627
|
+
};
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
#subagentBadgeText(): string | undefined {
|
|
1631
|
+
if (this.#subagentCount === 0) return undefined;
|
|
1632
|
+
const noun = this.#subagentCount === 1 ? "agent" : "agents";
|
|
1633
|
+
return theme.fg("statusLineSubagents", `${theme.icon.agents} ${this.#subagentCount} ${noun}`);
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
#buildStatusLine(width: number): string {
|
|
1637
|
+
const effectiveSettings = this.#resolveSettings();
|
|
1638
|
+
const includePath =
|
|
1639
|
+
hasPathSegment(effectiveSettings.leftSegments) || hasPathSegment(effectiveSettings.rightSegments);
|
|
1640
|
+
const includeContext =
|
|
1641
|
+
hasContextSegment(effectiveSettings.leftSegments) || hasContextSegment(effectiveSettings.rightSegments);
|
|
1642
|
+
const gitEnabled = this.#gitEnabled();
|
|
1643
|
+
const includeGit =
|
|
1644
|
+
gitEnabled &&
|
|
1645
|
+
(hasGitSegment(effectiveSettings.leftSegments) || hasGitSegment(effectiveSettings.rightSegments));
|
|
1646
|
+
const includePr =
|
|
1647
|
+
gitEnabled && (hasPrSegment(effectiveSettings.leftSegments) || hasPrSegment(effectiveSettings.rightSegments));
|
|
1648
|
+
const ctx = this.#buildSegmentContext(
|
|
1649
|
+
width,
|
|
1650
|
+
effectiveSettings.segmentOptions,
|
|
1651
|
+
includePath,
|
|
1652
|
+
includeContext,
|
|
1653
|
+
includeGit,
|
|
1654
|
+
includePr,
|
|
1655
|
+
);
|
|
1656
|
+
const separatorDef = getSeparator(effectiveSettings.separator ?? "powerline-thin", theme);
|
|
1657
|
+
|
|
1658
|
+
// `transparent` reuses the empty-string sentinel (`\x1b[49m`) so the bar
|
|
1659
|
+
// inherits the terminal's default background, matching custom themes that
|
|
1660
|
+
// set `statusLineBg: ""`. Powerline end caps need a contrasting fill to
|
|
1661
|
+
// bridge the bar into the surrounding terminal; without one they read as
|
|
1662
|
+
// stray glyphs, so the cap renderer drops them when the fill is empty.
|
|
1663
|
+
const TRANSPARENT_BG_ANSI = "\x1b[49m";
|
|
1664
|
+
const themeBgAnsi = theme.getBgAnsi("statusLineBg");
|
|
1665
|
+
const bgAnsi = effectiveSettings.transparent ? TRANSPARENT_BG_ANSI : themeBgAnsi;
|
|
1666
|
+
const transparentBg = bgAnsi === TRANSPARENT_BG_ANSI;
|
|
1667
|
+
const fgAnsi = theme.getFgAnsi("text");
|
|
1668
|
+
const sepAnsi = theme.getFgAnsi("statusLineSep");
|
|
1669
|
+
const subagentBadge = this.#subagentBadgeText();
|
|
1670
|
+
|
|
1671
|
+
// Collect visible segment contents
|
|
1672
|
+
const leftParts: string[] = [];
|
|
1673
|
+
const leftSegIds: StatusLineSegmentId[] = [];
|
|
1674
|
+
for (const segId of effectiveSettings.leftSegments) {
|
|
1675
|
+
if (subagentBadge && segId === "subagents") continue;
|
|
1676
|
+
const rendered = renderSegment(segId, ctx);
|
|
1677
|
+
if (rendered.visible && rendered.content) {
|
|
1678
|
+
leftParts.push(rendered.content);
|
|
1679
|
+
leftSegIds.push(segId);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
const rightParts: string[] = [];
|
|
1684
|
+
for (const segId of effectiveSettings.rightSegments) {
|
|
1685
|
+
if (subagentBadge && segId === "subagents") continue;
|
|
1686
|
+
const rendered = renderSegment(segId, ctx);
|
|
1687
|
+
if (rendered.visible && rendered.content) {
|
|
1688
|
+
rightParts.push(rendered.content);
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
const runningBackgroundJobs = this.session.getAsyncJobSnapshot()?.running.length ?? 0;
|
|
1693
|
+
if (runningBackgroundJobs > 0) {
|
|
1694
|
+
rightParts.unshift(theme.fg("statusLineSubagents", `${theme.icon.job} ${runningBackgroundJobs}`));
|
|
1695
|
+
}
|
|
1696
|
+
if (subagentBadge) {
|
|
1697
|
+
rightParts.unshift(subagentBadge);
|
|
1698
|
+
}
|
|
1699
|
+
const topFillWidth = Math.max(0, width);
|
|
1700
|
+
const left = [...leftParts];
|
|
1701
|
+
const right = [...rightParts];
|
|
1702
|
+
|
|
1703
|
+
const leftSepWidth = visibleWidth(separatorDef.left);
|
|
1704
|
+
const rightSepWidth = visibleWidth(separatorDef.right);
|
|
1705
|
+
// Transparent mode drops powerline caps (they need a bg fill to bridge),
|
|
1706
|
+
// so the width budget excludes them too.
|
|
1707
|
+
const leftCapWidth = separatorDef.endCaps && !transparentBg ? visibleWidth(separatorDef.endCaps.right) : 0;
|
|
1708
|
+
const rightCapWidth = separatorDef.endCaps && !transparentBg ? visibleWidth(separatorDef.endCaps.left) : 0;
|
|
1709
|
+
|
|
1710
|
+
const groupWidth = (parts: string[], capWidth: number, sepWidth: number): number => {
|
|
1711
|
+
if (parts.length === 0) return 0;
|
|
1712
|
+
const partsWidth = parts.reduce((sum, part) => sum + visibleWidth(part), 0);
|
|
1713
|
+
const sepTotal = Math.max(0, parts.length - 1) * (sepWidth + 2);
|
|
1714
|
+
return partsWidth + sepTotal + 2 + capWidth;
|
|
1715
|
+
};
|
|
1716
|
+
|
|
1717
|
+
let leftWidth = groupWidth(left, leftCapWidth, leftSepWidth);
|
|
1718
|
+
let rightWidth = groupWidth(right, rightCapWidth, rightSepWidth);
|
|
1719
|
+
const totalWidth = () => leftWidth + rightWidth + (left.length > 0 && right.length > 0 ? 1 : 0);
|
|
1720
|
+
|
|
1721
|
+
if (topFillWidth > 0) {
|
|
1722
|
+
while (totalWidth() > topFillWidth && right.length > 0) {
|
|
1723
|
+
right.pop();
|
|
1724
|
+
rightWidth = groupWidth(right, rightCapWidth, rightSepWidth);
|
|
1725
|
+
}
|
|
1726
|
+
// Shrink path before dropping left segments — path is the only elastic segment
|
|
1727
|
+
const pathIdx = leftSegIds.indexOf("path");
|
|
1728
|
+
if (pathIdx >= 0 && totalWidth() > topFillWidth) {
|
|
1729
|
+
const overflow = totalWidth() - topFillWidth;
|
|
1730
|
+
const currentPathVW = visibleWidth(left[pathIdx]);
|
|
1731
|
+
const minPathVW = 8; // icon + ellipsis + a few chars
|
|
1732
|
+
const shrinkable = currentPathVW - minPathVW;
|
|
1733
|
+
if (shrinkable > 0) {
|
|
1734
|
+
const shrinkBy = Math.min(shrinkable, overflow);
|
|
1735
|
+
const currentMaxLen = ctx.options.path?.maxLength ?? 40;
|
|
1736
|
+
let newMaxLen = Math.max(4, Math.min(currentMaxLen, currentPathVW) - shrinkBy);
|
|
1737
|
+
const pathCtx = (maxLen: number): SegmentContext => ({
|
|
1738
|
+
...ctx,
|
|
1739
|
+
options: { ...ctx.options, path: { ...ctx.options.path, maxLength: maxLen } },
|
|
1740
|
+
});
|
|
1741
|
+
let reRendered = renderSegment("path", pathCtx(newMaxLen));
|
|
1742
|
+
if (reRendered.visible && reRendered.content) {
|
|
1743
|
+
// maxLength governs path text, not icon prefix; iterate to compensate
|
|
1744
|
+
for (let i = 0; i < 8; i++) {
|
|
1745
|
+
const saved = currentPathVW - visibleWidth(reRendered.content);
|
|
1746
|
+
if (saved >= shrinkBy) break;
|
|
1747
|
+
const nextMaxLen = Math.max(4, newMaxLen - (shrinkBy - saved));
|
|
1748
|
+
if (nextMaxLen >= newMaxLen) break; // no progress or hit floor
|
|
1749
|
+
newMaxLen = nextMaxLen;
|
|
1750
|
+
const adjusted = renderSegment("path", pathCtx(newMaxLen));
|
|
1751
|
+
if (!adjusted.visible || !adjusted.content) break;
|
|
1752
|
+
reRendered = adjusted;
|
|
1753
|
+
}
|
|
1754
|
+
left[pathIdx] = reRendered.content;
|
|
1755
|
+
leftWidth = groupWidth(left, leftCapWidth, leftSepWidth);
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
const leftOverflowDropIndex = (): number => {
|
|
1760
|
+
// Preserve the current working directory as long as possible. The
|
|
1761
|
+
// previous right-to-left pop could collapse a normal-width bar to
|
|
1762
|
+
// just the model segment, hiding the path before less-critical left
|
|
1763
|
+
// segments such as model/mode/collab were removed.
|
|
1764
|
+
for (let i = leftSegIds.length - 1; i >= 0; i--) {
|
|
1765
|
+
if (leftSegIds[i] !== "path") return i;
|
|
1766
|
+
}
|
|
1767
|
+
return left.length - 1;
|
|
1768
|
+
};
|
|
1769
|
+
|
|
1770
|
+
while (totalWidth() > topFillWidth && left.length > 0) {
|
|
1771
|
+
const dropIdx = leftOverflowDropIndex();
|
|
1772
|
+
left.splice(dropIdx, 1);
|
|
1773
|
+
leftSegIds.splice(dropIdx, 1);
|
|
1774
|
+
leftWidth = groupWidth(left, leftCapWidth, leftSepWidth);
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
const renderGroup = (parts: string[], direction: "left" | "right"): string => {
|
|
1779
|
+
if (parts.length === 0) return "";
|
|
1780
|
+
const sep = direction === "left" ? separatorDef.left : separatorDef.right;
|
|
1781
|
+
const cap =
|
|
1782
|
+
separatorDef.endCaps && !transparentBg
|
|
1783
|
+
? direction === "left"
|
|
1784
|
+
? separatorDef.endCaps.right
|
|
1785
|
+
: separatorDef.endCaps.left
|
|
1786
|
+
: "";
|
|
1787
|
+
const capPrefix = separatorDef.endCaps?.useBgAsFg ? bgAnsi.replace("\x1b[48;", "\x1b[38;") : bgAnsi + sepAnsi;
|
|
1788
|
+
const capText = cap ? `${capPrefix}${this.#focusedAgentId ? "\x1b[22m" : ""}${cap}\x1b[0m` : "";
|
|
1789
|
+
|
|
1790
|
+
let content = bgAnsi + fgAnsi;
|
|
1791
|
+
content += ` ${parts.join(` ${sepAnsi}${sep}${fgAnsi} `)} `;
|
|
1792
|
+
content += "\x1b[0m";
|
|
1793
|
+
|
|
1794
|
+
if (capText) {
|
|
1795
|
+
return direction === "right" ? capText + content : content + capText;
|
|
1796
|
+
}
|
|
1797
|
+
return content;
|
|
1798
|
+
};
|
|
1799
|
+
|
|
1800
|
+
const leftGroup = renderGroup(left, "left");
|
|
1801
|
+
const rightGroup = renderGroup(right, "right");
|
|
1802
|
+
if (!leftGroup && !rightGroup) return "";
|
|
1803
|
+
|
|
1804
|
+
if (topFillWidth === 0 || left.length === 0 || right.length === 0) {
|
|
1805
|
+
return leftGroup + (leftGroup && rightGroup ? " " : "") + rightGroup;
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
const gapWidth = Math.max(1, topFillWidth - leftWidth - rightWidth);
|
|
1809
|
+
const sessionName =
|
|
1810
|
+
effectiveSettings.sessionAccent !== false ? this.session.sessionManager?.getSessionName() : undefined;
|
|
1811
|
+
const accentHex = sessionName
|
|
1812
|
+
? getSessionAccentHex(sessionName, theme.getMajorThemeColorHexes(), theme.accentSurfaceLuminance)
|
|
1813
|
+
: undefined;
|
|
1814
|
+
const gapColor = getSessionAccentAnsi(accentHex) ?? theme.getFgAnsi("border");
|
|
1815
|
+
const gapFill = `${gapColor}${theme.boxRound.horizontal.repeat(gapWidth)}\x1b[39m`;
|
|
1816
|
+
return leftGroup + gapFill + rightGroup;
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
getTopBorder(width: number): { content: string; width: number } {
|
|
1820
|
+
let content = this.#buildStatusLine(width);
|
|
1821
|
+
if (this.#focusedAgentId && content) {
|
|
1822
|
+
// Dim the whole bar while focus-proxied. Group/cap terminators emit full
|
|
1823
|
+
// `\x1b[0m` resets that would cancel faint mid-bar, so re-open it after each.
|
|
1824
|
+
content = `\x1b[2m${content.replaceAll("\x1b[0m", "\x1b[0m\x1b[2m")}\x1b[22m`;
|
|
1825
|
+
}
|
|
1826
|
+
return {
|
|
1827
|
+
content,
|
|
1828
|
+
width: visibleWidth(content),
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
render(width: number): readonly string[] {
|
|
1833
|
+
// Only render hook statuses - main status is in editor's top border
|
|
1834
|
+
const showHooks = this.#settings.showHookStatus ?? true;
|
|
1835
|
+
if (!showHooks || this.#hookStatuses.size === 0) {
|
|
1836
|
+
return [];
|
|
1837
|
+
}
|
|
1838
|
+
|
|
1839
|
+
return Array.from(this.#hookStatuses.entries())
|
|
1840
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
1841
|
+
.map(([, text]) => truncateToWidth(sanitizeStatusText(text), width));
|
|
1842
|
+
}
|
|
1843
|
+
}
|