@wahack/pi-coding-agent 15.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10031 -0
- package/README.md +36 -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 +79 -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 +172 -0
- package/package.json +554 -0
- package/scripts/build-binary.ts +100 -0
- package/scripts/bundle-dist.ts +90 -0
- package/scripts/format-prompts.ts +68 -0
- package/scripts/generate-docs-index.ts +40 -0
- package/scripts/generate-template.ts +33 -0
- package/scripts/omp +42 -0
- package/scripts/omp.ts +19 -0
- package/src/async/index.ts +1 -0
- package/src/async/job-manager.ts +625 -0
- package/src/auto-thinking/classifier.ts +185 -0
- package/src/autoresearch/command-resume.md +14 -0
- package/src/autoresearch/dashboard.ts +436 -0
- package/src/autoresearch/git.ts +319 -0
- package/src/autoresearch/helpers.ts +218 -0
- package/src/autoresearch/index.ts +536 -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 +699 -0
- package/src/autoresearch/tools/init-experiment.ts +272 -0
- package/src/autoresearch/tools/log-experiment.ts +524 -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/bun-imports.d.ts +28 -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 +436 -0
- package/src/capability/instruction.ts +37 -0
- package/src/capability/mcp.ts +74 -0
- package/src/capability/prompt.ts +35 -0
- package/src/capability/rule-buckets.ts +66 -0
- package/src/capability/rule.ts +261 -0
- package/src/capability/settings.ts +34 -0
- package/src/capability/skill.ts +63 -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 +168 -0
- package/src/cli/agents-cli.ts +138 -0
- package/src/cli/args.ts +340 -0
- package/src/cli/auth-broker-cli.ts +895 -0
- package/src/cli/auth-gateway-cli.ts +611 -0
- package/src/cli/classify-install-target.ts +76 -0
- package/src/cli/claude-trace-cli.ts +795 -0
- package/src/cli/commands/init-xdg.ts +27 -0
- package/src/cli/completion-gen.ts +550 -0
- package/src/cli/config-cli.ts +418 -0
- package/src/cli/dry-balance-cli.ts +856 -0
- package/src/cli/extension-flags.ts +48 -0
- package/src/cli/file-processor.ts +133 -0
- package/src/cli/gallery-cli.ts +230 -0
- package/src/cli/gallery-fixtures/agentic.ts +407 -0
- package/src/cli/gallery-fixtures/codeintel.ts +187 -0
- package/src/cli/gallery-fixtures/edit.ts +194 -0
- package/src/cli/gallery-fixtures/fs.ts +220 -0
- package/src/cli/gallery-fixtures/index.ts +40 -0
- package/src/cli/gallery-fixtures/interaction.ts +49 -0
- package/src/cli/gallery-fixtures/memory.ts +81 -0
- package/src/cli/gallery-fixtures/misc.ts +250 -0
- package/src/cli/gallery-fixtures/search.ts +213 -0
- package/src/cli/gallery-fixtures/shell.ts +167 -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/grep-cli.ts +160 -0
- package/src/cli/grievances-cli.ts +256 -0
- package/src/cli/initial-message.ts +58 -0
- package/src/cli/list-models.ts +194 -0
- package/src/cli/plugin-cli.ts +996 -0
- package/src/cli/read-cli.ts +57 -0
- package/src/cli/session-picker.ts +79 -0
- package/src/cli/setup-cli.ts +231 -0
- package/src/cli/shell-cli.ts +176 -0
- package/src/cli/ssh-cli.ts +179 -0
- package/src/cli/startup-cwd.ts +68 -0
- package/src/cli/stats-cli.ts +238 -0
- package/src/cli/tiny-models-cli.ts +127 -0
- package/src/cli/update-cli.ts +611 -0
- package/src/cli/usage-cli.ts +603 -0
- package/src/cli/web-search-cli.ts +132 -0
- package/src/cli/worktree-cli.ts +291 -0
- package/src/cli-commands.ts +79 -0
- package/src/cli.ts +200 -0
- package/src/commands/acp.ts +24 -0
- package/src/commands/agents.ts +57 -0
- package/src/commands/auth-broker.ts +99 -0
- package/src/commands/auth-gateway.ts +69 -0
- package/src/commands/commit.ts +46 -0
- package/src/commands/complete.ts +66 -0
- package/src/commands/completions.ts +60 -0
- package/src/commands/config.ts +51 -0
- package/src/commands/dry-balance.ts +43 -0
- package/src/commands/gallery.ts +52 -0
- package/src/commands/grep.ts +48 -0
- package/src/commands/grievances.ts +51 -0
- package/src/commands/install.ts +107 -0
- package/src/commands/launch.ts +169 -0
- package/src/commands/plugin.ts +78 -0
- package/src/commands/read.ts +38 -0
- package/src/commands/setup.ts +67 -0
- package/src/commands/shell.ts +29 -0
- package/src/commands/ssh.ts +60 -0
- package/src/commands/stats.ts +29 -0
- package/src/commands/tiny-models.ts +36 -0
- package/src/commands/update.ts +21 -0
- package/src/commands/usage.ts +35 -0
- package/src/commands/web-search.ts +42 -0
- package/src/commands/worktree.ts +56 -0
- package/src/commit/agentic/agent.ts +317 -0
- package/src/commit/agentic/fallback.ts +96 -0
- package/src/commit/agentic/index.ts +355 -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 +146 -0
- package/src/commit/agentic/tools/git-file-diff.ts +191 -0
- package/src/commit/agentic/tools/git-hunk.ts +50 -0
- package/src/commit/agentic/tools/git-overview.ts +81 -0
- package/src/commit/agentic/tools/index.ts +54 -0
- package/src/commit/agentic/tools/propose-changelog.ts +144 -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 +23 -0
- package/src/commit/agentic/tools/split-commit.ts +245 -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 +105 -0
- package/src/commit/analysis/validation.ts +66 -0
- package/src/commit/changelog/detect.ts +40 -0
- package/src/commit/changelog/generate.ts +97 -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 +92 -0
- package/src/commit/pipeline.ts +243 -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 +77 -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 +60 -0
- package/src/config/append-only-context-mode.ts +31 -0
- package/src/config/config-file.ts +317 -0
- package/src/config/file-lock.ts +164 -0
- package/src/config/keybindings.ts +628 -0
- package/src/config/mcp-schema.json +230 -0
- package/src/config/model-discovery.ts +554 -0
- package/src/config/model-registry.ts +2090 -0
- package/src/config/model-resolver.ts +1502 -0
- package/src/config/model-roles.ts +74 -0
- package/src/config/models-config-schema.ts +226 -0
- package/src/config/models-config.ts +129 -0
- package/src/config/prompt-templates.ts +185 -0
- package/src/config/resolve-config-value.ts +94 -0
- package/src/config/settings-schema.ts +3530 -0
- package/src/config/settings.ts +1178 -0
- package/src/config.ts +242 -0
- package/src/cursor.ts +340 -0
- package/src/dap/client.ts +760 -0
- package/src/dap/config.ts +189 -0
- package/src/dap/defaults.json +212 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1441 -0
- package/src/dap/types.ts +610 -0
- package/src/debug/index.ts +515 -0
- package/src/debug/log-formatting.ts +58 -0
- package/src/debug/log-viewer.ts +908 -0
- package/src/debug/profiler.ts +162 -0
- package/src/debug/protocol-probe.ts +267 -0
- package/src/debug/raw-sse-buffer.ts +273 -0
- package/src/debug/raw-sse.ts +292 -0
- package/src/debug/report-bundle.ts +374 -0
- package/src/debug/system-info.ts +111 -0
- package/src/debug/terminal-info.ts +124 -0
- package/src/discovery/agents-md.ts +67 -0
- package/src/discovery/agents.ts +230 -0
- package/src/discovery/at-imports.ts +273 -0
- package/src/discovery/builtin-defaults.ts +39 -0
- package/src/discovery/builtin-rules/index.ts +54 -0
- package/src/discovery/builtin-rules/rs-box-leak.md +48 -0
- package/src/discovery/builtin-rules/rs-future-prelude.md +23 -0
- package/src/discovery/builtin-rules/rs-lazylock.md +51 -0
- package/src/discovery/builtin-rules/rs-match-ergonomics.md +67 -0
- package/src/discovery/builtin-rules/rs-parking-lot.md +44 -0
- package/src/discovery/builtin-rules/rs-result-type.md +19 -0
- package/src/discovery/builtin-rules/ts-bare-catch.md +38 -0
- package/src/discovery/builtin-rules/ts-import-type.md +42 -0
- package/src/discovery/builtin-rules/ts-no-any.md +56 -0
- package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +44 -0
- package/src/discovery/builtin-rules/ts-no-dynamic-import.md +39 -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 +65 -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 +906 -0
- package/src/discovery/claude-plugins.ts +386 -0
- package/src/discovery/claude.ts +584 -0
- package/src/discovery/cline.ts +83 -0
- package/src/discovery/codex.ts +522 -0
- package/src/discovery/cursor.ts +220 -0
- package/src/discovery/gemini.ts +383 -0
- package/src/discovery/github.ts +154 -0
- package/src/discovery/helpers.ts +1016 -0
- package/src/discovery/index.ts +81 -0
- package/src/discovery/mcp-json.ts +171 -0
- package/src/discovery/omp-extension-roots.ts +190 -0
- package/src/discovery/omp-plugins.ts +383 -0
- package/src/discovery/opencode.ts +398 -0
- package/src/discovery/plugin-dir-roots.ts +28 -0
- package/src/discovery/ssh.ts +153 -0
- package/src/discovery/substitute-plugin-root.ts +29 -0
- package/src/discovery/vscode.ts +105 -0
- package/src/discovery/windsurf.ts +147 -0
- package/src/edit/apply-patch/index.ts +87 -0
- package/src/edit/apply-patch/parser.ts +174 -0
- package/src/edit/diff.ts +999 -0
- package/src/edit/file-snapshot-store.ts +91 -0
- package/src/edit/hashline/block-resolver.ts +33 -0
- package/src/edit/hashline/diff.ts +290 -0
- package/src/edit/hashline/execute.ts +242 -0
- package/src/edit/hashline/filesystem.ts +130 -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 +18 -0
- package/src/edit/index.ts +571 -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 +1891 -0
- package/src/edit/modes/replace.ts +1137 -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 +769 -0
- package/src/edit/streaming.ts +517 -0
- package/src/eval/__tests__/agent-bridge.test.ts +708 -0
- package/src/eval/__tests__/bridge-timeout.test.ts +64 -0
- package/src/eval/__tests__/budget-bridge.test.ts +69 -0
- package/src/eval/__tests__/completion-bridge.test.ts +412 -0
- package/src/eval/__tests__/helpers-local-roots.test.ts +58 -0
- package/src/eval/__tests__/idle-timeout.test.ts +80 -0
- package/src/eval/__tests__/js-context-manager.test.ts +241 -0
- package/src/eval/__tests__/kernel-spawn.test.ts +103 -0
- package/src/eval/agent-bridge.ts +319 -0
- package/src/eval/backend.ts +71 -0
- package/src/eval/bridge-timeout.ts +44 -0
- package/src/eval/budget-bridge.ts +48 -0
- package/src/eval/completion-bridge.ts +207 -0
- package/src/eval/concurrency-bridge.ts +34 -0
- package/src/eval/idle-timeout.ts +91 -0
- package/src/eval/index.ts +4 -0
- package/src/eval/js/context-manager.ts +502 -0
- package/src/eval/js/executor.ts +173 -0
- package/src/eval/js/index.ts +51 -0
- package/src/eval/js/shared/helpers.ts +283 -0
- package/src/eval/js/shared/indirect-eval.ts +30 -0
- package/src/eval/js/shared/local-module-loader.ts +342 -0
- package/src/eval/js/shared/prelude.ts +2 -0
- package/src/eval/js/shared/prelude.txt +246 -0
- package/src/eval/js/shared/rewrite-imports.ts +532 -0
- package/src/eval/js/shared/runtime.ts +352 -0
- package/src/eval/js/shared/types.ts +18 -0
- package/src/eval/js/tool-bridge.ts +162 -0
- package/src/eval/js/worker-core.ts +132 -0
- package/src/eval/js/worker-entry.ts +30 -0
- package/src/eval/js/worker-protocol.ts +47 -0
- package/src/eval/py/__tests__/prelude.test.ts +19 -0
- package/src/eval/py/display.ts +71 -0
- package/src/eval/py/executor.ts +742 -0
- package/src/eval/py/index.ts +68 -0
- package/src/eval/py/kernel.ts +748 -0
- package/src/eval/py/prelude.py +658 -0
- package/src/eval/py/prelude.ts +3 -0
- package/src/eval/py/runner.py +1133 -0
- package/src/eval/py/runtime.ts +276 -0
- package/src/eval/py/spawn-options.ts +126 -0
- package/src/eval/py/tool-bridge.ts +182 -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 +419 -0
- package/src/exec/exec.ts +53 -0
- package/src/exec/non-interactive-env.ts +48 -0
- package/src/export/custom-share.ts +65 -0
- package/src/export/html/index.ts +164 -0
- package/src/export/html/template.css +1051 -0
- package/src/export/html/template.generated.ts +2 -0
- package/src/export/html/template.html +46 -0
- package/src/export/html/template.js +2271 -0
- package/src/export/html/template.macro.ts +25 -0
- package/src/export/html/vendor/highlight.min.js +1213 -0
- package/src/export/html/vendor/marked.min.js +6 -0
- package/src/export/ttsr.ts +583 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +54 -0
- package/src/extensibility/custom-commands/bundled/review/index.ts +489 -0
- package/src/extensibility/custom-commands/index.ts +2 -0
- package/src/extensibility/custom-commands/loader.ts +238 -0
- package/src/extensibility/custom-commands/types.ts +113 -0
- package/src/extensibility/custom-tools/index.ts +7 -0
- package/src/extensibility/custom-tools/loader.ts +269 -0
- package/src/extensibility/custom-tools/types.ts +270 -0
- package/src/extensibility/custom-tools/wrapper.ts +47 -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/loader.ts +572 -0
- package/src/extensibility/extensions/runner.ts +922 -0
- package/src/extensibility/extensions/types.ts +1322 -0
- package/src/extensibility/extensions/wrapper.ts +223 -0
- package/src/extensibility/hooks/index.ts +5 -0
- package/src/extensibility/hooks/loader.ts +257 -0
- package/src/extensibility/hooks/runner.ts +425 -0
- package/src/extensibility/hooks/tool-wrapper.ts +107 -0
- package/src/extensibility/hooks/types.ts +606 -0
- package/src/extensibility/legacy-pi-ai-shim.ts +24 -0
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +15 -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 +192 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +682 -0
- package/src/extensibility/plugins/loader.ts +313 -0
- package/src/extensibility/plugins/manager.ts +827 -0
- package/src/extensibility/plugins/marketplace/cache.ts +136 -0
- package/src/extensibility/plugins/marketplace/fetcher.ts +317 -0
- package/src/extensibility/plugins/marketplace/index.ts +6 -0
- package/src/extensibility/plugins/marketplace/manager.ts +770 -0
- package/src/extensibility/plugins/marketplace/registry.ts +196 -0
- package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
- package/src/extensibility/plugins/marketplace/types.ts +191 -0
- package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
- package/src/extensibility/plugins/parser.ts +105 -0
- package/src/extensibility/plugins/types.ts +194 -0
- package/src/extensibility/shared-events.ts +343 -0
- package/src/extensibility/skills.ts +312 -0
- package/src/extensibility/slash-commands.ts +227 -0
- package/src/extensibility/tool-proxy.ts +25 -0
- package/src/extensibility/typebox.ts +418 -0
- package/src/extensibility/utils.ts +44 -0
- package/src/goals/index.ts +3 -0
- package/src/goals/runtime.ts +528 -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 +598 -0
- package/src/hindsight/config.ts +175 -0
- package/src/hindsight/content.ts +210 -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 +488 -0
- package/src/hindsight/transcript.ts +71 -0
- package/src/index.ts +59 -0
- package/src/internal-urls/agent-protocol.ts +146 -0
- package/src/internal-urls/artifact-protocol.ts +107 -0
- package/src/internal-urls/docs-index.generated.ts +106 -0
- package/src/internal-urls/history-protocol.ts +113 -0
- package/src/internal-urls/index.ts +25 -0
- package/src/internal-urls/issue-pr-protocol.ts +584 -0
- package/src/internal-urls/json-query.ts +126 -0
- package/src/internal-urls/local-protocol.ts +287 -0
- package/src/internal-urls/mcp-protocol.ts +151 -0
- package/src/internal-urls/memory-protocol.ts +169 -0
- package/src/internal-urls/omp-protocol.ts +93 -0
- package/src/internal-urls/parse.ts +72 -0
- package/src/internal-urls/registry-helpers.ts +25 -0
- package/src/internal-urls/router.ts +105 -0
- package/src/internal-urls/rule-protocol.ts +45 -0
- package/src/internal-urls/skill-protocol.ts +96 -0
- package/src/internal-urls/types.ts +152 -0
- package/src/internal-urls/vault-protocol.ts +936 -0
- package/src/irc/bus.ts +292 -0
- package/src/lib/xai-http.ts +124 -0
- package/src/lsp/client.ts +1193 -0
- package/src/lsp/clients/biome-client.ts +264 -0
- package/src/lsp/clients/index.ts +50 -0
- package/src/lsp/clients/lsp-linter-client.ts +93 -0
- package/src/lsp/clients/swiftlint-client.ts +120 -0
- package/src/lsp/config.ts +502 -0
- package/src/lsp/defaults.json +493 -0
- package/src/lsp/diagnostics-ledger.ts +51 -0
- package/src/lsp/edits.ts +267 -0
- package/src/lsp/index.ts +2477 -0
- package/src/lsp/lspmux.ts +233 -0
- package/src/lsp/render.ts +694 -0
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +455 -0
- package/src/lsp/utils.ts +718 -0
- package/src/main.ts +1325 -0
- package/src/mcp/client.ts +484 -0
- package/src/mcp/config-writer.ts +225 -0
- package/src/mcp/config.ts +365 -0
- package/src/mcp/index.ts +29 -0
- package/src/mcp/json-rpc.ts +122 -0
- package/src/mcp/loader.ts +124 -0
- package/src/mcp/manager.ts +1275 -0
- package/src/mcp/oauth-discovery.ts +442 -0
- package/src/mcp/oauth-flow.ts +442 -0
- package/src/mcp/render.ts +132 -0
- package/src/mcp/smithery-auth.ts +104 -0
- package/src/mcp/smithery-connect.ts +145 -0
- package/src/mcp/smithery-registry.ts +477 -0
- package/src/mcp/timeout.ts +59 -0
- package/src/mcp/tool-bridge.ts +426 -0
- package/src/mcp/tool-cache.ts +117 -0
- package/src/mcp/transports/http.ts +519 -0
- package/src/mcp/transports/index.ts +6 -0
- package/src/mcp/transports/stdio.ts +528 -0
- package/src/mcp/types.ts +423 -0
- package/src/memories/index.ts +1150 -0
- package/src/memories/storage.ts +577 -0
- package/src/memory-backend/index.ts +18 -0
- package/src/memory-backend/local-backend.ts +39 -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/types.ts +166 -0
- package/src/mnemopi/backend.ts +547 -0
- package/src/mnemopi/config.ts +160 -0
- package/src/mnemopi/index.ts +3 -0
- package/src/mnemopi/state.ts +584 -0
- package/src/modes/acp/acp-agent.ts +2407 -0
- package/src/modes/acp/acp-client-bridge.ts +154 -0
- package/src/modes/acp/acp-event-mapper.ts +929 -0
- package/src/modes/acp/acp-mode.ts +23 -0
- package/src/modes/acp/index.ts +2 -0
- package/src/modes/acp/terminal-auth.ts +37 -0
- package/src/modes/components/agent-dashboard.ts +1206 -0
- package/src/modes/components/agent-hub.ts +1071 -0
- package/src/modes/components/assistant-message.ts +307 -0
- package/src/modes/components/bash-execution.ts +220 -0
- package/src/modes/components/bordered-loader.ts +41 -0
- package/src/modes/components/branch-summary-message.ts +45 -0
- package/src/modes/components/btw-panel.ts +104 -0
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/compaction-summary-message.ts +87 -0
- package/src/modes/components/copy-selector.ts +206 -0
- package/src/modes/components/countdown-timer.ts +75 -0
- package/src/modes/components/custom-editor.ts +398 -0
- package/src/modes/components/custom-message.ts +63 -0
- package/src/modes/components/diff.ts +277 -0
- package/src/modes/components/dynamic-border.ts +34 -0
- package/src/modes/components/error-banner.ts +33 -0
- package/src/modes/components/eval-execution.ts +158 -0
- package/src/modes/components/execution-shared.ts +101 -0
- package/src/modes/components/extensions/extension-dashboard.ts +399 -0
- package/src/modes/components/extensions/extension-list.ts +502 -0
- package/src/modes/components/extensions/index.ts +9 -0
- package/src/modes/components/extensions/inspector-panel.ts +317 -0
- package/src/modes/components/extensions/state-manager.ts +627 -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 +280 -0
- package/src/modes/components/hook-editor.ts +167 -0
- package/src/modes/components/hook-input.ts +87 -0
- package/src/modes/components/hook-message.ts +66 -0
- package/src/modes/components/hook-selector.ts +660 -0
- package/src/modes/components/index.ts +38 -0
- package/src/modes/components/keybinding-hints.ts +65 -0
- package/src/modes/components/late-diagnostics-message.ts +60 -0
- package/src/modes/components/login-dialog.ts +164 -0
- package/src/modes/components/mcp-add-wizard.ts +1340 -0
- package/src/modes/components/message-frame.ts +88 -0
- package/src/modes/components/model-selector.ts +1271 -0
- package/src/modes/components/oauth-selector.ts +368 -0
- package/src/modes/components/omfg-panel.ts +141 -0
- package/src/modes/components/overlay-box.ts +108 -0
- package/src/modes/components/plan-review-overlay.ts +820 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/plugin-selector.ts +95 -0
- package/src/modes/components/plugin-settings.ts +722 -0
- package/src/modes/components/queue-mode-selector.ts +56 -0
- package/src/modes/components/read-tool-group.ts +670 -0
- package/src/modes/components/segment-track.ts +52 -0
- package/src/modes/components/session-selector.ts +625 -0
- package/src/modes/components/settings-defs.ts +189 -0
- package/src/modes/components/settings-selector.ts +651 -0
- package/src/modes/components/show-images-selector.ts +45 -0
- package/src/modes/components/skill-message.ts +89 -0
- package/src/modes/components/status-line/component.ts +869 -0
- package/src/modes/components/status-line/context-thresholds.ts +79 -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 +584 -0
- package/src/modes/components/status-line/separators.ts +55 -0
- package/src/modes/components/status-line/token-rate.ts +66 -0
- package/src/modes/components/status-line/types.ts +108 -0
- package/src/modes/components/theme-selector.ts +63 -0
- package/src/modes/components/thinking-selector.ts +52 -0
- package/src/modes/components/tiny-title-download-progress.ts +90 -0
- package/src/modes/components/tips.txt +19 -0
- package/src/modes/components/todo-reminder.ts +38 -0
- package/src/modes/components/tool-execution.ts +1024 -0
- package/src/modes/components/transcript-container.ts +608 -0
- package/src/modes/components/tree-selector.ts +978 -0
- package/src/modes/components/ttsr-notification.ts +122 -0
- package/src/modes/components/user-message-selector.ts +227 -0
- package/src/modes/components/user-message.ts +66 -0
- package/src/modes/components/visual-truncate.ts +63 -0
- package/src/modes/components/welcome.ts +493 -0
- package/src/modes/controllers/btw-controller.ts +105 -0
- package/src/modes/controllers/command-controller-shared.ts +109 -0
- package/src/modes/controllers/command-controller.ts +1566 -0
- package/src/modes/controllers/event-controller.ts +1054 -0
- package/src/modes/controllers/extension-ui-controller.ts +886 -0
- package/src/modes/controllers/input-controller.ts +1073 -0
- package/src/modes/controllers/mcp-command-controller.ts +2017 -0
- package/src/modes/controllers/omfg-controller.ts +283 -0
- package/src/modes/controllers/omfg-rule.ts +647 -0
- package/src/modes/controllers/selector-controller.ts +1108 -0
- package/src/modes/controllers/ssh-command-controller.ts +384 -0
- package/src/modes/controllers/streaming-reveal.ts +279 -0
- package/src/modes/controllers/tan-command-controller.ts +173 -0
- package/src/modes/controllers/todo-command-controller.ts +485 -0
- package/src/modes/data/emojis.json +1 -0
- package/src/modes/emoji-autocomplete.ts +285 -0
- package/src/modes/gradient-highlight.ts +87 -0
- package/src/modes/image-references.ts +117 -0
- package/src/modes/index.ts +17 -0
- package/src/modes/interactive-mode.ts +3370 -0
- package/src/modes/internal-url-autocomplete.ts +143 -0
- package/src/modes/loop-limit.ts +140 -0
- package/src/modes/magic-keywords.ts +20 -0
- package/src/modes/markdown-prose.ts +247 -0
- package/src/modes/oauth-manual-input.ts +69 -0
- package/src/modes/orchestrate.ts +42 -0
- package/src/modes/print-mode.ts +126 -0
- package/src/modes/prompt-action-autocomplete.ts +260 -0
- package/src/modes/rpc/host-tools.ts +186 -0
- package/src/modes/rpc/host-uris.ts +235 -0
- package/src/modes/rpc/rpc-client.ts +963 -0
- package/src/modes/rpc/rpc-mode.ts +947 -0
- package/src/modes/rpc/rpc-subagents.ts +265 -0
- package/src/modes/rpc/rpc-types.ts +458 -0
- package/src/modes/runtime-init.ts +116 -0
- package/src/modes/session-observer-registry.ts +146 -0
- package/src/modes/setup-version.ts +11 -0
- package/src/modes/setup-wizard/index.ts +99 -0
- package/src/modes/setup-wizard/lazy.ts +16 -0
- package/src/modes/setup-wizard/scenes/glyph.ts +96 -0
- package/src/modes/setup-wizard/scenes/outro.ts +35 -0
- package/src/modes/setup-wizard/scenes/providers.ts +69 -0
- package/src/modes/setup-wizard/scenes/sign-in.ts +205 -0
- package/src/modes/setup-wizard/scenes/splash.ts +201 -0
- package/src/modes/setup-wizard/scenes/theme.ts +299 -0
- package/src/modes/setup-wizard/scenes/types.ts +48 -0
- package/src/modes/setup-wizard/scenes/web-search.ts +129 -0
- package/src/modes/setup-wizard/wizard-overlay.ts +275 -0
- package/src/modes/shared.ts +47 -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 +142 -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 +142 -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 +29 -0
- package/src/modes/theme/shimmer.ts +235 -0
- package/src/modes/theme/theme-schema.json +459 -0
- package/src/modes/theme/theme.ts +2676 -0
- package/src/modes/turn-budget.ts +31 -0
- package/src/modes/types.ts +359 -0
- package/src/modes/ultrathink.ts +41 -0
- package/src/modes/utils/context-usage.ts +339 -0
- package/src/modes/utils/copy-targets.ts +360 -0
- package/src/modes/utils/hotkeys-markdown.ts +61 -0
- package/src/modes/utils/keybinding-matchers.ts +51 -0
- package/src/modes/utils/tools-markdown.ts +27 -0
- package/src/modes/utils/ui-helpers.ts +801 -0
- package/src/modes/workflow.ts +42 -0
- package/src/plan-mode/approved-plan.ts +186 -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 +41 -0
- package/src/prompts/agents/designer.md +66 -0
- package/src/prompts/agents/explore.md +58 -0
- package/src/prompts/agents/frontmatter.md +11 -0
- package/src/prompts/agents/init.md +33 -0
- package/src/prompts/agents/librarian.md +119 -0
- package/src/prompts/agents/oracle.md +55 -0
- package/src/prompts/agents/plan.md +48 -0
- package/src/prompts/agents/reviewer.md +140 -0
- package/src/prompts/agents/task.md +16 -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/memories/consolidation.md +30 -0
- package/src/prompts/memories/read-path.md +11 -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 +22 -0
- package/src/prompts/review-headless-request.md +16 -0
- package/src/prompts/review-request.md +69 -0
- package/src/prompts/steering/user-interjection.md +10 -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 +12 -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/custom-system-prompt.md +64 -0
- package/src/prompts/system/eager-todo.md +13 -0
- package/src/prompts/system/empty-stop-retry.md +6 -0
- package/src/prompts/system/irc-incoming.md +7 -0
- package/src/prompts/system/manual-continue.md +7 -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/omfg-user.md +50 -0
- package/src/prompts/system/orchestrate-notice.md +40 -0
- package/src/prompts/system/plan-mode-active.md +109 -0
- package/src/prompts/system/plan-mode-approved.md +25 -0
- package/src/prompts/system/plan-mode-compact-instructions.md +16 -0
- package/src/prompts/system/plan-mode-reference.md +11 -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/project-prompt.md +52 -0
- package/src/prompts/system/subagent-system-prompt.md +64 -0
- package/src/prompts/system/subagent-user-prompt.md +3 -0
- package/src/prompts/system/subagent-yield-reminder.md +12 -0
- package/src/prompts/system/system-prompt.md +258 -0
- package/src/prompts/system/tiny-title-system.md +8 -0
- package/src/prompts/system/title-system.md +16 -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/web-search.md +25 -0
- package/src/prompts/system/workflow-notice.md +70 -0
- package/src/prompts/tools/apply-patch.md +65 -0
- package/src/prompts/tools/ask.md +30 -0
- package/src/prompts/tools/ast-edit.md +39 -0
- package/src/prompts/tools/ast-grep.md +42 -0
- package/src/prompts/tools/async-result.md +8 -0
- package/src/prompts/tools/bash.md +46 -0
- package/src/prompts/tools/browser.md +73 -0
- package/src/prompts/tools/checkpoint.md +16 -0
- package/src/prompts/tools/debug.md +34 -0
- package/src/prompts/tools/eval.md +92 -0
- package/src/prompts/tools/find.md +36 -0
- package/src/prompts/tools/github.md +21 -0
- package/src/prompts/tools/goal.md +18 -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 +32 -0
- package/src/prompts/tools/irc.md +59 -0
- package/src/prompts/tools/job.md +19 -0
- package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
- package/src/prompts/tools/lsp.md +42 -0
- package/src/prompts/tools/memory-edit.md +8 -0
- package/src/prompts/tools/patch.md +70 -0
- package/src/prompts/tools/read.md +84 -0
- package/src/prompts/tools/recall.md +5 -0
- package/src/prompts/tools/reflect.md +5 -0
- package/src/prompts/tools/render-mermaid.md +9 -0
- package/src/prompts/tools/replace.md +30 -0
- package/src/prompts/tools/resolve.md +9 -0
- package/src/prompts/tools/retain.md +6 -0
- package/src/prompts/tools/rewind.md +13 -0
- package/src/prompts/tools/search-tool-bm25.md +32 -0
- package/src/prompts/tools/search.md +24 -0
- package/src/prompts/tools/ssh.md +31 -0
- package/src/prompts/tools/task-summary.md +17 -0
- package/src/prompts/tools/task.md +88 -0
- package/src/prompts/tools/todo.md +62 -0
- package/src/prompts/tools/web-search.md +10 -0
- package/src/prompts/tools/write.md +14 -0
- package/src/registry/agent-lifecycle.ts +218 -0
- package/src/registry/agent-registry.ts +151 -0
- package/src/sdk.ts +2558 -0
- package/src/secrets/index.ts +123 -0
- package/src/secrets/obfuscator.ts +298 -0
- package/src/secrets/regex.ts +21 -0
- package/src/session/agent-session.ts +10121 -0
- package/src/session/agent-storage.ts +455 -0
- package/src/session/artifacts.ts +135 -0
- package/src/session/auth-broker-config.ts +131 -0
- package/src/session/auth-storage.ts +29 -0
- package/src/session/blob-store.ts +255 -0
- package/src/session/client-bridge.ts +85 -0
- package/src/session/history-storage.ts +348 -0
- package/src/session/indexed-session-storage.ts +430 -0
- package/src/session/messages.ts +541 -0
- package/src/session/redis-session-storage.ts +170 -0
- package/src/session/session-dump-format.ts +209 -0
- package/src/session/session-history-format.ts +246 -0
- package/src/session/session-manager.ts +3676 -0
- package/src/session/session-storage.ts +529 -0
- package/src/session/shake-types.ts +43 -0
- package/src/session/sql-session-storage.ts +314 -0
- package/src/session/streaming-output.ts +1330 -0
- package/src/session/tool-choice-queue.ts +213 -0
- package/src/session/yield-queue.ts +173 -0
- package/src/slash-commands/acp-builtins.ts +70 -0
- package/src/slash-commands/builtin-registry.ts +1798 -0
- package/src/slash-commands/helpers/context-report.ts +39 -0
- package/src/slash-commands/helpers/format.ts +46 -0
- package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
- package/src/slash-commands/helpers/mcp.ts +532 -0
- package/src/slash-commands/helpers/parse.ts +85 -0
- package/src/slash-commands/helpers/ssh.ts +195 -0
- package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
- package/src/slash-commands/helpers/todo.ts +279 -0
- package/src/slash-commands/helpers/usage-report.ts +95 -0
- package/src/slash-commands/marketplace-install-parser.ts +99 -0
- package/src/slash-commands/types.ts +135 -0
- package/src/ssh/config-writer.ts +183 -0
- package/src/ssh/connection-manager.ts +509 -0
- package/src/ssh/ssh-executor.ts +189 -0
- package/src/ssh/sshfs-mount.ts +140 -0
- package/src/ssh/utils.ts +8 -0
- package/src/stt/downloader.ts +71 -0
- package/src/stt/index.ts +3 -0
- package/src/stt/recorder.ts +351 -0
- package/src/stt/setup.ts +52 -0
- package/src/stt/stt-controller.ts +160 -0
- package/src/stt/transcribe.py +70 -0
- package/src/stt/transcriber.ts +91 -0
- package/src/stubs/natives/index.ts +814 -0
- package/src/stubs/natives/package.json +7 -0
- package/src/stubs/tui/index.ts +282 -0
- package/src/stubs/tui/package.json +7 -0
- package/src/system-prompt.ts +611 -0
- package/src/task/agents.ts +167 -0
- package/src/task/commands.ts +132 -0
- package/src/task/discovery.ts +122 -0
- package/src/task/executor.ts +2133 -0
- package/src/task/index.ts +1419 -0
- package/src/task/name-generator.ts +1577 -0
- package/src/task/omp-command.ts +26 -0
- package/src/task/output-manager.ts +88 -0
- package/src/task/parallel.ts +116 -0
- package/src/task/render.ts +1381 -0
- package/src/task/repair-args.ts +129 -0
- package/src/task/subprocess-tool-registry.ts +88 -0
- package/src/task/types.ts +336 -0
- package/src/task/worktree.ts +514 -0
- package/src/telemetry-export.ts +144 -0
- package/src/thinking.ts +167 -0
- package/src/tiny/compiled-runtime.ts +179 -0
- package/src/tiny/device.ts +111 -0
- package/src/tiny/dtype.ts +101 -0
- package/src/tiny/models.ts +242 -0
- package/src/tiny/text.ts +165 -0
- package/src/tiny/title-client.ts +543 -0
- package/src/tiny/title-protocol.ts +56 -0
- package/src/tiny/worker.ts +568 -0
- package/src/tool-discovery/mode.ts +24 -0
- package/src/tool-discovery/tool-index.ts +256 -0
- package/src/tools/approval.ts +189 -0
- package/src/tools/archive-reader.ts +721 -0
- package/src/tools/ask.ts +928 -0
- package/src/tools/ast-edit.ts +642 -0
- package/src/tools/ast-grep.ts +452 -0
- package/src/tools/auto-generated-guard.ts +322 -0
- package/src/tools/bash-command-fixup.ts +37 -0
- package/src/tools/bash-interactive.ts +408 -0
- package/src/tools/bash-interceptor.ts +67 -0
- package/src/tools/bash-pty-selection.ts +14 -0
- package/src/tools/bash-skill-urls.ts +248 -0
- package/src/tools/bash.ts +1386 -0
- package/src/tools/browser/attach.ts +175 -0
- package/src/tools/browser/launch.ts +660 -0
- package/src/tools/browser/readable.ts +112 -0
- package/src/tools/browser/registry.ts +197 -0
- package/src/tools/browser/render.ts +216 -0
- package/src/tools/browser/tab-protocol.ts +105 -0
- package/src/tools/browser/tab-supervisor.ts +628 -0
- package/src/tools/browser/tab-worker-entry.ts +21 -0
- package/src/tools/browser/tab-worker.ts +1226 -0
- package/src/tools/browser.ts +343 -0
- package/src/tools/checkpoint.ts +136 -0
- package/src/tools/conflict-detect.ts +718 -0
- package/src/tools/context.ts +39 -0
- package/src/tools/debug.ts +1067 -0
- package/src/tools/eval-backends.ts +27 -0
- package/src/tools/eval-render.ts +752 -0
- package/src/tools/eval.ts +577 -0
- package/src/tools/fetch.ts +1926 -0
- package/src/tools/file-recorder.ts +35 -0
- package/src/tools/find.ts +609 -0
- package/src/tools/fs-cache-invalidation.ts +28 -0
- package/src/tools/gh-cache-invalidation.ts +255 -0
- package/src/tools/gh-format.ts +12 -0
- package/src/tools/gh-renderer.ts +481 -0
- package/src/tools/gh.ts +3720 -0
- package/src/tools/github-cache.ts +637 -0
- package/src/tools/grouped-file-output.ts +210 -0
- package/src/tools/image-gen.ts +1517 -0
- package/src/tools/index.ts +599 -0
- package/src/tools/inspect-image-renderer.ts +132 -0
- package/src/tools/inspect-image.ts +174 -0
- package/src/tools/irc.ts +723 -0
- package/src/tools/job.ts +557 -0
- package/src/tools/json-tree.ts +243 -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/list-limit.ts +40 -0
- package/src/tools/match-line-format.ts +20 -0
- package/src/tools/memory-edit.ts +59 -0
- package/src/tools/memory-recall.ts +100 -0
- package/src/tools/memory-reflect.ts +88 -0
- package/src/tools/memory-render.ts +202 -0
- package/src/tools/memory-retain.ts +91 -0
- package/src/tools/output-meta.ts +754 -0
- package/src/tools/output-schema-validator.ts +132 -0
- package/src/tools/path-utils.ts +1054 -0
- package/src/tools/plan-mode-guard.ts +108 -0
- package/src/tools/puppeteer/00_stealth_tampering.txt +63 -0
- package/src/tools/puppeteer/01_stealth_activity.txt +20 -0
- package/src/tools/puppeteer/02_stealth_hairline.txt +11 -0
- package/src/tools/puppeteer/03_stealth_botd.txt +384 -0
- package/src/tools/puppeteer/04_stealth_iframe.txt +81 -0
- package/src/tools/puppeteer/05_stealth_webgl.txt +75 -0
- package/src/tools/puppeteer/06_stealth_screen.txt +72 -0
- package/src/tools/puppeteer/07_stealth_fonts.txt +97 -0
- package/src/tools/puppeteer/08_stealth_audio.txt +51 -0
- package/src/tools/puppeteer/09_stealth_locale.txt +46 -0
- package/src/tools/puppeteer/10_stealth_plugins.txt +206 -0
- package/src/tools/puppeteer/11_stealth_hardware.txt +8 -0
- package/src/tools/puppeteer/12_stealth_codecs.txt +40 -0
- package/src/tools/puppeteer/13_stealth_worker.txt +74 -0
- package/src/tools/read.ts +2929 -0
- package/src/tools/render-mermaid.ts +69 -0
- package/src/tools/render-utils.ts +838 -0
- package/src/tools/renderers.ts +77 -0
- package/src/tools/report-tool-issue.ts +534 -0
- package/src/tools/resolve.ts +276 -0
- package/src/tools/review.ts +253 -0
- package/src/tools/search-tool-bm25.ts +351 -0
- package/src/tools/search.ts +1580 -0
- package/src/tools/sqlite-reader.ts +828 -0
- package/src/tools/ssh.ts +349 -0
- package/src/tools/todo.ts +982 -0
- package/src/tools/tool-errors.ts +62 -0
- package/src/tools/tool-result.ts +94 -0
- package/src/tools/tool-timeouts.ts +30 -0
- package/src/tools/tts.ts +133 -0
- package/src/tools/write.ts +1217 -0
- package/src/tools/yield.ts +269 -0
- package/src/tui/code-cell.ts +216 -0
- package/src/tui/file-list.ts +55 -0
- package/src/tui/hyperlink.ts +175 -0
- package/src/tui/index.ts +12 -0
- package/src/tui/output-block.ts +240 -0
- package/src/tui/status-line.ts +54 -0
- package/src/tui/tree-list.ts +84 -0
- package/src/tui/types.ts +15 -0
- package/src/tui/utils.ts +103 -0
- package/src/utils/block-context.ts +312 -0
- package/src/utils/changelog.ts +132 -0
- package/src/utils/clipboard.ts +193 -0
- package/src/utils/command-args.ts +76 -0
- package/src/utils/commit-message-generator.ts +151 -0
- package/src/utils/edit-mode.ts +41 -0
- package/src/utils/enhanced-paste.ts +230 -0
- package/src/utils/event-bus.ts +33 -0
- package/src/utils/external-editor.ts +65 -0
- package/src/utils/file-display-mode.ts +45 -0
- package/src/utils/file-mentions.ts +281 -0
- package/src/utils/git.ts +1833 -0
- package/src/utils/image-loading.ts +132 -0
- package/src/utils/image-resize.ts +309 -0
- package/src/utils/jj.ts +248 -0
- package/src/utils/lang-from-path.ts +239 -0
- package/src/utils/markit.ts +89 -0
- package/src/utils/open.ts +55 -0
- package/src/utils/session-color.ts +68 -0
- package/src/utils/shell-snapshot.ts +187 -0
- package/src/utils/sixel.ts +69 -0
- package/src/utils/title-generator.ts +373 -0
- package/src/utils/tool-choice.ts +33 -0
- package/src/utils/tools-manager.ts +363 -0
- package/src/web/kagi.ts +305 -0
- package/src/web/parallel.ts +353 -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 +653 -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 +704 -0
- package/src/web/scrapers/gitlab.ts +401 -0
- package/src/web/scrapers/go-pkg.ts +266 -0
- package/src/web/scrapers/hackage.ts +140 -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 +120 -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 +397 -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 +292 -0
- package/src/web/search/provider.ts +157 -0
- package/src/web/search/providers/anthropic.ts +318 -0
- package/src/web/search/providers/base.ts +89 -0
- package/src/web/search/providers/brave.ts +152 -0
- package/src/web/search/providers/codex.ts +591 -0
- package/src/web/search/providers/exa.ts +400 -0
- package/src/web/search/providers/gemini.ts +460 -0
- package/src/web/search/providers/jina.ts +111 -0
- package/src/web/search/providers/kagi.ts +86 -0
- package/src/web/search/providers/kimi.ts +196 -0
- package/src/web/search/providers/parallel.ts +225 -0
- package/src/web/search/providers/perplexity.ts +730 -0
- package/src/web/search/providers/searxng.ts +313 -0
- package/src/web/search/providers/synthetic.ts +114 -0
- package/src/web/search/providers/tavily.ts +176 -0
- package/src/web/search/providers/utils.ts +128 -0
- package/src/web/search/providers/zai.ts +333 -0
- package/src/web/search/render.ts +262 -0
- package/src/web/search/types.ts +482 -0
- package/src/web/search/utils.ts +17 -0
- package/src/workspace-tree.ts +286 -0
|
@@ -0,0 +1,1386 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import type {
|
|
3
|
+
AgentTool,
|
|
4
|
+
AgentToolContext,
|
|
5
|
+
AgentToolResult,
|
|
6
|
+
AgentToolUpdateCallback,
|
|
7
|
+
ToolApprovalDecision,
|
|
8
|
+
} from "@oh-my-pi/pi-agent-core";
|
|
9
|
+
import type { Component } from './stubs/tui/index.ts';
|
|
10
|
+
import { ImageProtocol, TERMINAL } from './stubs/tui/index.ts';
|
|
11
|
+
import { getProjectDir, isEnoent, logger, prompt } from "@oh-my-pi/pi-utils";
|
|
12
|
+
import * as z from "zod/v4";
|
|
13
|
+
import { type BashResult, executeBash } from "../exec/bash-executor";
|
|
14
|
+
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
15
|
+
import { InternalUrlRouter } from "../internal-urls";
|
|
16
|
+
import { truncateToVisualLines } from "../modes/components/visual-truncate";
|
|
17
|
+
import { highlightCode, type Theme } from "../modes/theme/theme";
|
|
18
|
+
import bashDescription from "../prompts/tools/bash.md" with { type: "text" };
|
|
19
|
+
import type { ClientBridgeTerminalExitStatus, ClientBridgeTerminalOutput } from "../session/client-bridge";
|
|
20
|
+
import { DEFAULT_MAX_BYTES, enforceInlineByteCap, streamTailUpdates, TailBuffer } from "../session/streaming-output";
|
|
21
|
+
import { renderStatusLine } from "../tui";
|
|
22
|
+
import { CachedOutputBlock, markFramedBlockComponent } from "../tui/output-block";
|
|
23
|
+
import { getSixelLineMask } from "../utils/sixel";
|
|
24
|
+
import type { ToolSession } from ".";
|
|
25
|
+
import { truncateForPrompt } from "./approval";
|
|
26
|
+
import { applyBashFixups } from "./bash-command-fixup";
|
|
27
|
+
import { type BashInteractiveResult, runInteractiveBashPty } from "./bash-interactive";
|
|
28
|
+
import { checkBashInterception } from "./bash-interceptor";
|
|
29
|
+
import { canUseInteractiveBashPty } from "./bash-pty-selection";
|
|
30
|
+
import { expandInternalUrls, type InternalUrlExpansionOptions } from "./bash-skill-urls";
|
|
31
|
+
import { invalidateGithubCacheForBashCommand } from "./gh-cache-invalidation";
|
|
32
|
+
import { formatStyledTruncationWarning, type OutputMeta, stripOutputNotice } from "./output-meta";
|
|
33
|
+
import { resolveToCwd } from "./path-utils";
|
|
34
|
+
import { capPreviewLines, formatToolWorkingDirectory, replaceTabs } from "./render-utils";
|
|
35
|
+
import { ToolAbortError, ToolError } from "./tool-errors";
|
|
36
|
+
import { toolResult } from "./tool-result";
|
|
37
|
+
import { clampTimeout, TOOL_TIMEOUTS } from "./tool-timeouts";
|
|
38
|
+
|
|
39
|
+
export const BASH_DEFAULT_PREVIEW_LINES = 10;
|
|
40
|
+
|
|
41
|
+
const BASH_ENV_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
42
|
+
const DEFAULT_AUTO_BACKGROUND_THRESHOLD_MS = 60_000;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Bash patterns flagged as safety critical for approval policy.
|
|
46
|
+
*
|
|
47
|
+
* Kept intentionally tight — the cost of a false negative is data loss or a compromised host,
|
|
48
|
+
* while false positives remain actionable through user policy control.
|
|
49
|
+
* New patterns should target shapes that are virtually never legitimate in automation.
|
|
50
|
+
*/
|
|
51
|
+
export const CRITICAL_BASH_PATTERNS = [
|
|
52
|
+
// Recursive destruction.
|
|
53
|
+
/\brm\s+-[a-z]*[rRfF][a-z]*\s+\//i, // rm -rf /, rm -fr /, rm -r /, rm -f /…
|
|
54
|
+
/\bsudo\s+rm\b/i, // any `sudo rm`.
|
|
55
|
+
/\bchmod\s+-R\s+[0-7]+\s+\//i, // `chmod -R 777 /`.
|
|
56
|
+
/\bchmod\s+-R\s+[ugoa+\-=rwxXst,]+\s+\//, // `chmod -R u+x /`, `chmod -R u+rwx,o+w /etc` (symbolic mode, root target).
|
|
57
|
+
/\bchown\s+-R\s+\S+\s+\//i, // `chown -R user /`.
|
|
58
|
+
|
|
59
|
+
// Fork bomb (a few common spacings).
|
|
60
|
+
/:\(\)\s*\{\s*:\s*\|\s*:/i,
|
|
61
|
+
|
|
62
|
+
// Disk / filesystem destruction.
|
|
63
|
+
/>\s*\/dev\/sd[a-z]/i, // write to disk device.
|
|
64
|
+
/\bmkfs(\.|\b)/i, // format filesystem.
|
|
65
|
+
/\bdd\s+if=.+of=\/dev\//i, // dd to a device.
|
|
66
|
+
/\bshred\s+\/dev\//i,
|
|
67
|
+
/\bcryptsetup\b/i,
|
|
68
|
+
|
|
69
|
+
// System-config destruction.
|
|
70
|
+
/>\s*\/etc\/(?:passwd|shadow|sudoers)\b/i,
|
|
71
|
+
/\btee\s+(?:-a\s+)?\/etc\/(?:passwd|shadow|sudoers)\b/i, // `tee /etc/passwd`, `tee -a /etc/sudoers`.
|
|
72
|
+
|
|
73
|
+
// Remote-fetch-then-execute (curl/wget piped to a shell or process-subbed).
|
|
74
|
+
/\b(?:curl|wget|fetch)\b[^|]*\|\s*(?:bash|sh|zsh|fish)\b/i,
|
|
75
|
+
// Process-sub variants — `bash <(curl …)`, `source <(curl …)`, `. <(curl …)`. `.` and `source` are
|
|
76
|
+
// anchored to a command boundary so `find . -name` and similar don't false-positive.
|
|
77
|
+
/(?:^|[\s;&|(])(?:bash|sh|zsh|source|\.)\s+<\(\s*(?:curl|wget|fetch)\b/i,
|
|
78
|
+
// `eval "$(curl …)"` / `eval $(curl …)` / `eval \`curl …\``.
|
|
79
|
+
/\beval\s+["'`]?\$\(\s*(?:curl|wget|fetch)\b|\beval\s+`\s*(?:curl|wget|fetch)\b/i,
|
|
80
|
+
|
|
81
|
+
// Process/host control.
|
|
82
|
+
/\bkill\s+-9\s+1\b/, // kill PID 1.
|
|
83
|
+
// Process/host control — must sit at command position so `npm run reboot-tests`
|
|
84
|
+
// or `echo 'shutdown the queue'` don't false-positive.
|
|
85
|
+
/(?:^|[\s;&|(])(?:shutdown|poweroff|reboot|halt)(?:\s|$|[;|&])/i,
|
|
86
|
+
/(?:^|[\s;&|(])init\s+0\b/i,
|
|
87
|
+
|
|
88
|
+
// Network-shell exfil.
|
|
89
|
+
/\bnc\b[^|;]*\s-[a-zA-Z]*[ec][a-zA-Z]*\s/i, // `nc -e` / `nc -c`.
|
|
90
|
+
] as const;
|
|
91
|
+
|
|
92
|
+
async function saveBashOriginalArtifact(session: ToolSession, originalText: string): Promise<string | undefined> {
|
|
93
|
+
try {
|
|
94
|
+
const alloc = await session.allocateOutputArtifact?.("bash-original");
|
|
95
|
+
if (!alloc?.path || !alloc.id) return undefined;
|
|
96
|
+
await Bun.write(alloc.path, originalText);
|
|
97
|
+
return alloc.id;
|
|
98
|
+
} catch {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const bashSchemaBase = z.object({
|
|
104
|
+
command: z.string().describe("command to execute"),
|
|
105
|
+
env: z.record(z.string().regex(BASH_ENV_NAME_PATTERN), z.string()).optional().describe("extra env vars"),
|
|
106
|
+
timeout: z.number().default(300).describe("timeout in seconds").optional(),
|
|
107
|
+
cwd: z.string().describe("working directory").optional(),
|
|
108
|
+
pty: z.boolean().describe("run in pty mode").optional(),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const bashSchemaWithAsync = bashSchemaBase.extend({
|
|
112
|
+
async: z.boolean().describe("run in background").optional(),
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
type BashToolSchema = typeof bashSchemaBase | typeof bashSchemaWithAsync;
|
|
116
|
+
|
|
117
|
+
export interface BashToolInput {
|
|
118
|
+
command: string;
|
|
119
|
+
env?: Record<string, string>;
|
|
120
|
+
timeout?: number;
|
|
121
|
+
cwd?: string;
|
|
122
|
+
|
|
123
|
+
async?: boolean;
|
|
124
|
+
pty?: boolean;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface BashToolDetails {
|
|
128
|
+
meta?: OutputMeta;
|
|
129
|
+
timeoutSeconds?: number;
|
|
130
|
+
requestedTimeoutSeconds?: number;
|
|
131
|
+
wallTimeMs?: number;
|
|
132
|
+
/** Exit code of a command that ran to completion but failed (non-zero). */
|
|
133
|
+
exitCode?: number;
|
|
134
|
+
terminalId?: string;
|
|
135
|
+
async?: {
|
|
136
|
+
state: "running" | "completed" | "failed";
|
|
137
|
+
jobId: string;
|
|
138
|
+
type: "bash";
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export interface BashToolOptions {}
|
|
143
|
+
|
|
144
|
+
type ManagedBashJobCompletion =
|
|
145
|
+
| {
|
|
146
|
+
kind: "completed";
|
|
147
|
+
result: AgentToolResult<BashToolDetails>;
|
|
148
|
+
}
|
|
149
|
+
| {
|
|
150
|
+
kind: "failed";
|
|
151
|
+
error: unknown;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
interface ManagedBashJobHandle {
|
|
155
|
+
jobId: string;
|
|
156
|
+
label: string;
|
|
157
|
+
completion: Promise<ManagedBashJobCompletion>;
|
|
158
|
+
getLatestText: () => string;
|
|
159
|
+
setBackgrounded: (backgrounded: boolean) => void;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function normalizeResultOutput(result: BashResult | BashInteractiveResult): string {
|
|
163
|
+
return result.output || "";
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function isInteractiveResult(result: BashResult | BashInteractiveResult): result is BashInteractiveResult {
|
|
167
|
+
return "timedOut" in result;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function normalizeBashEnv(env: Record<string, string> | undefined): Record<string, string> | undefined {
|
|
171
|
+
if (!env || Object.keys(env).length === 0) return undefined;
|
|
172
|
+
const normalized: Record<string, string> = {};
|
|
173
|
+
for (const [key, value] of Object.entries(env)) {
|
|
174
|
+
if (!BASH_ENV_NAME_PATTERN.test(key)) {
|
|
175
|
+
throw new ToolError(`Invalid bash env name: ${key}`);
|
|
176
|
+
}
|
|
177
|
+
normalized[key] = value;
|
|
178
|
+
}
|
|
179
|
+
return normalized;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function escapeBashEnvValueForDisplay(value: string): string {
|
|
183
|
+
return value
|
|
184
|
+
.replaceAll("\\", "\\\\")
|
|
185
|
+
.replaceAll("\n", "\\n")
|
|
186
|
+
.replaceAll("\r", "\\r")
|
|
187
|
+
.replaceAll("\t", "\\t")
|
|
188
|
+
.replaceAll('"', '\\"')
|
|
189
|
+
.replaceAll("$", "\\$")
|
|
190
|
+
.replaceAll("`", "\\`");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function formatBashEnvAssignments(env: Record<string, string> | undefined): string {
|
|
194
|
+
if (!env || Object.keys(env).length === 0) return "";
|
|
195
|
+
return Object.entries(env)
|
|
196
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
197
|
+
.map(([key, value]) => `${key}="${escapeBashEnvValueForDisplay(value)}"`)
|
|
198
|
+
.join(" ");
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function unescapePartialJsonString(value: string): string {
|
|
202
|
+
let output = "";
|
|
203
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
204
|
+
const char = value[index];
|
|
205
|
+
if (char !== "\\") {
|
|
206
|
+
output += char;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
const next = value[index + 1];
|
|
210
|
+
if (!next) {
|
|
211
|
+
output += "\\";
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
index += 1;
|
|
215
|
+
switch (next) {
|
|
216
|
+
case '"':
|
|
217
|
+
output += '"';
|
|
218
|
+
break;
|
|
219
|
+
case "\\":
|
|
220
|
+
output += "\\";
|
|
221
|
+
break;
|
|
222
|
+
case "/":
|
|
223
|
+
output += "/";
|
|
224
|
+
break;
|
|
225
|
+
case "b":
|
|
226
|
+
output += "\b";
|
|
227
|
+
break;
|
|
228
|
+
case "f":
|
|
229
|
+
output += "\f";
|
|
230
|
+
break;
|
|
231
|
+
case "n":
|
|
232
|
+
output += "\n";
|
|
233
|
+
break;
|
|
234
|
+
case "r":
|
|
235
|
+
output += "\r";
|
|
236
|
+
break;
|
|
237
|
+
case "t":
|
|
238
|
+
output += "\t";
|
|
239
|
+
break;
|
|
240
|
+
case "u": {
|
|
241
|
+
const hex = value.slice(index + 1, index + 5);
|
|
242
|
+
if (/^[0-9a-fA-F]{4}$/u.test(hex)) {
|
|
243
|
+
output += String.fromCharCode(Number.parseInt(hex, 16));
|
|
244
|
+
index += 4;
|
|
245
|
+
} else {
|
|
246
|
+
output += "\\u";
|
|
247
|
+
}
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
default:
|
|
251
|
+
output += next;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return output;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function extractPartialBashEnv(partialJson: string | undefined): Record<string, string> | undefined {
|
|
258
|
+
if (!partialJson) return undefined;
|
|
259
|
+
const envStart = partialJson.search(/"env"\s*:\s*\{/u);
|
|
260
|
+
if (envStart === -1) return undefined;
|
|
261
|
+
const objectStart = partialJson.indexOf("{", envStart);
|
|
262
|
+
if (objectStart === -1) return undefined;
|
|
263
|
+
const envBody = partialJson.slice(objectStart + 1);
|
|
264
|
+
const env: Record<string, string> = {};
|
|
265
|
+
const matcher = /"([A-Za-z_][A-Za-z0-9_]*)"\s*:\s*"((?:\\.|[^"\\])*)(?:"|$)/gu;
|
|
266
|
+
for (const match of envBody.matchAll(matcher)) {
|
|
267
|
+
env[match[1]!] = unescapePartialJsonString(match[2]!);
|
|
268
|
+
}
|
|
269
|
+
return Object.keys(env).length > 0 ? env : undefined;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function formatTimeoutClampNotice(requestedTimeoutSec: number, effectiveTimeoutSec: number): string | undefined {
|
|
273
|
+
return requestedTimeoutSec !== effectiveTimeoutSec
|
|
274
|
+
? `Timeout clamped to ${effectiveTimeoutSec}s (requested ${requestedTimeoutSec}s; allowed range ${TOOL_TIMEOUTS.bash.min}-${TOOL_TIMEOUTS.bash.max}s).`
|
|
275
|
+
: undefined;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function formatWallTimeSeconds(wallTimeMs: number): string {
|
|
279
|
+
return (wallTimeMs / 1000).toFixed(2);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function formatWallTimeNotice(wallTimeMs: number): string {
|
|
283
|
+
return `Wall time: ${formatWallTimeSeconds(wallTimeMs)} seconds`;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function formatExitCodeNotice(exitCode: number): string {
|
|
287
|
+
return `Command exited with code ${exitCode}`;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const RAW_OUTPUT_ARTIFACT_PREFIX = "[raw output: artifact://";
|
|
291
|
+
const RAW_OUTPUT_ARTIFACT_SUFFIX = "]";
|
|
292
|
+
|
|
293
|
+
function stripRawOutputArtifactNotice(text: string): { text: string; artifactId?: string } {
|
|
294
|
+
const trimmed = text.trimEnd();
|
|
295
|
+
const lineStart = trimmed.lastIndexOf("\n");
|
|
296
|
+
const candidateStart = lineStart === -1 ? 0 : lineStart + 1;
|
|
297
|
+
if (
|
|
298
|
+
!trimmed.startsWith(RAW_OUTPUT_ARTIFACT_PREFIX, candidateStart) ||
|
|
299
|
+
!trimmed.endsWith(RAW_OUTPUT_ARTIFACT_SUFFIX)
|
|
300
|
+
) {
|
|
301
|
+
return { text };
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const idStart = candidateStart + RAW_OUTPUT_ARTIFACT_PREFIX.length;
|
|
305
|
+
const idEnd = trimmed.length - RAW_OUTPUT_ARTIFACT_SUFFIX.length;
|
|
306
|
+
if (idStart === idEnd) return { text };
|
|
307
|
+
for (let i = idStart; i < idEnd; i++) {
|
|
308
|
+
const code = trimmed.charCodeAt(i);
|
|
309
|
+
if (code < 48 || code > 57) return { text };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const artifactId = trimmed.slice(idStart, idEnd);
|
|
313
|
+
return {
|
|
314
|
+
text: trimmed.slice(0, lineStart === -1 ? 0 : lineStart).trimEnd(),
|
|
315
|
+
artifactId,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Strip the trailing occurrence of `notice` (plus a single surrounding newline
|
|
321
|
+
* on each side) so the TUI can echo the value via a styled footer label
|
|
322
|
+
* instead of repeating it verbatim in the output pane. The notice is
|
|
323
|
+
* reconstructed from the same value the result was tagged with, so a literal
|
|
324
|
+
* sub-string match never strips a coincidental in-output token — only the
|
|
325
|
+
* exact line we appended in #buildCompletedResult.
|
|
326
|
+
*/
|
|
327
|
+
function stripTrailingNotice(text: string, notice: string): string {
|
|
328
|
+
const idx = text.lastIndexOf(notice);
|
|
329
|
+
if (idx === -1) return text;
|
|
330
|
+
let start = idx;
|
|
331
|
+
let end = idx + notice.length;
|
|
332
|
+
if (text[start - 1] === "\n") start -= 1;
|
|
333
|
+
if (text[end] === "\n") end += 1;
|
|
334
|
+
return (text.slice(0, start) + text.slice(end)).trimEnd();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function stripWallTimeNotice(text: string, wallTimeMs: number | undefined): string {
|
|
338
|
+
if (wallTimeMs === undefined) return text;
|
|
339
|
+
return stripTrailingNotice(text, formatWallTimeNotice(wallTimeMs));
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function stripExitCodeNotice(text: string, exitCode: number | undefined): string {
|
|
343
|
+
if (exitCode === undefined) return text;
|
|
344
|
+
return stripTrailingNotice(text, formatExitCodeNotice(exitCode));
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Bash tool implementation.
|
|
349
|
+
*
|
|
350
|
+
* Executes bash commands with optional timeout and working directory.
|
|
351
|
+
*/
|
|
352
|
+
export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
353
|
+
readonly name = "bash";
|
|
354
|
+
readonly approval = (args: unknown): ToolApprovalDecision => {
|
|
355
|
+
const rawCommand = (args as Partial<BashToolInput>).command;
|
|
356
|
+
const command = typeof rawCommand === "string" ? rawCommand : "";
|
|
357
|
+
if (command !== "" && CRITICAL_BASH_PATTERNS.some(pattern => pattern.test(command))) {
|
|
358
|
+
return { tier: "exec", override: true, reason: "Critical pattern detected" };
|
|
359
|
+
}
|
|
360
|
+
return "exec";
|
|
361
|
+
};
|
|
362
|
+
readonly formatApprovalDetails = (args: unknown): string[] => {
|
|
363
|
+
const rawCommand = (args as Partial<BashToolInput>).command;
|
|
364
|
+
const command = typeof rawCommand === "string" ? rawCommand : "(missing)";
|
|
365
|
+
return [`Command: ${truncateForPrompt(command)}`];
|
|
366
|
+
};
|
|
367
|
+
readonly label = "Bash";
|
|
368
|
+
readonly loadMode = "essential";
|
|
369
|
+
readonly description: string;
|
|
370
|
+
readonly parameters: BashToolSchema;
|
|
371
|
+
readonly concurrency = "exclusive";
|
|
372
|
+
readonly strict = true;
|
|
373
|
+
readonly #asyncEnabled: boolean;
|
|
374
|
+
readonly #autoBackgroundEnabled: boolean;
|
|
375
|
+
readonly #autoBackgroundThresholdMs: number;
|
|
376
|
+
|
|
377
|
+
constructor(private readonly session: ToolSession) {
|
|
378
|
+
this.#asyncEnabled = this.session.settings.get("async.enabled");
|
|
379
|
+
this.#autoBackgroundEnabled = this.session.settings.get("bash.autoBackground.enabled");
|
|
380
|
+
this.#autoBackgroundThresholdMs = Math.max(
|
|
381
|
+
0,
|
|
382
|
+
Math.floor(
|
|
383
|
+
this.session.settings.get("bash.autoBackground.thresholdMs") ?? DEFAULT_AUTO_BACKGROUND_THRESHOLD_MS,
|
|
384
|
+
),
|
|
385
|
+
);
|
|
386
|
+
this.parameters = this.#asyncEnabled ? bashSchemaWithAsync : bashSchemaBase;
|
|
387
|
+
this.description = prompt.render(bashDescription, {
|
|
388
|
+
asyncEnabled: this.#asyncEnabled,
|
|
389
|
+
autoBackgroundEnabled: this.#autoBackgroundEnabled,
|
|
390
|
+
autoBackgroundThresholdSeconds: Math.max(0, Math.floor(this.#autoBackgroundThresholdMs / 1000)),
|
|
391
|
+
hasAstGrep: this.session.settings.get("astGrep.enabled"),
|
|
392
|
+
hasAstEdit: this.session.settings.get("astEdit.enabled"),
|
|
393
|
+
hasSearch: this.session.settings.get("search.enabled"),
|
|
394
|
+
hasFind: this.session.settings.get("find.enabled"),
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
#formatResultOutput(result: BashResult | BashInteractiveResult): string {
|
|
399
|
+
const outputText = normalizeResultOutput(result);
|
|
400
|
+
return outputText || "(no output)";
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Throw for outcomes that are *not* a completed command: user/timeout
|
|
405
|
+
* aborts and a missing exit status. The foreground and bridge callers plus
|
|
406
|
+
* the async job manager rely on these throwing so cancellations surface as
|
|
407
|
+
* aborts and jobs are recorded as failed. A definite non-zero exit is a
|
|
408
|
+
* completed command that failed; #buildCompletedResult surfaces it as an
|
|
409
|
+
* error *result* (carrying execution details) rather than a throw.
|
|
410
|
+
*/
|
|
411
|
+
#throwIfUnfinished(result: BashResult | BashInteractiveResult, timeoutSec: number, outputText: string): void {
|
|
412
|
+
if (result.cancelled) {
|
|
413
|
+
// executeBash output already carries a `[Command cancelled]` notice from
|
|
414
|
+
// the sink; PTY/bridge interactive output does not, so annotate it here.
|
|
415
|
+
const out = normalizeResultOutput(result);
|
|
416
|
+
const annotated = isInteractiveResult(result) && out ? `${out}\n\n[Command aborted]` : out;
|
|
417
|
+
throw new ToolError(annotated || "Command aborted");
|
|
418
|
+
}
|
|
419
|
+
if (isInteractiveResult(result) && result.timedOut) {
|
|
420
|
+
const out = normalizeResultOutput(result);
|
|
421
|
+
throw new ToolError(
|
|
422
|
+
out
|
|
423
|
+
? `${out}\n\n[Command timed out after ${timeoutSec} seconds]`
|
|
424
|
+
: `Command timed out after ${timeoutSec} seconds`,
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
if (result.exitCode === undefined) {
|
|
428
|
+
throw new ToolError(`${outputText}\n\nCommand failed: missing exit status`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
async #buildCompletedResult(
|
|
433
|
+
result: BashResult | BashInteractiveResult,
|
|
434
|
+
timeoutSec: number,
|
|
435
|
+
options: {
|
|
436
|
+
requestedTimeoutSec?: number;
|
|
437
|
+
notices?: readonly string[];
|
|
438
|
+
terminalId?: string;
|
|
439
|
+
wallTimeMs?: number;
|
|
440
|
+
} = {},
|
|
441
|
+
): Promise<AgentToolResult<BashToolDetails>> {
|
|
442
|
+
const exitCode = result.exitCode;
|
|
443
|
+
const failedExit = exitCode !== undefined && exitCode !== 0;
|
|
444
|
+
|
|
445
|
+
const outputLines = [this.#formatResultOutput(result)];
|
|
446
|
+
const notices: string[] = [];
|
|
447
|
+
if (options.wallTimeMs !== undefined) {
|
|
448
|
+
notices.push(formatWallTimeNotice(options.wallTimeMs));
|
|
449
|
+
}
|
|
450
|
+
if (options.notices) {
|
|
451
|
+
for (const notice of options.notices) {
|
|
452
|
+
if (notice) notices.push(notice);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (notices.length > 0) outputLines.push("", ...notices);
|
|
456
|
+
if (failedExit) outputLines.push("", formatExitCodeNotice(exitCode));
|
|
457
|
+
const outputText = outputLines.join("\n");
|
|
458
|
+
|
|
459
|
+
// Aborts / timeouts / missing-status still propagate as thrown errors.
|
|
460
|
+
this.#throwIfUnfinished(result, timeoutSec, outputText);
|
|
461
|
+
|
|
462
|
+
const details: BashToolDetails = { timeoutSeconds: timeoutSec };
|
|
463
|
+
if (options.requestedTimeoutSec !== undefined && options.requestedTimeoutSec !== timeoutSec) {
|
|
464
|
+
details.requestedTimeoutSeconds = options.requestedTimeoutSec;
|
|
465
|
+
}
|
|
466
|
+
if (options.terminalId !== undefined) {
|
|
467
|
+
details.terminalId = options.terminalId;
|
|
468
|
+
}
|
|
469
|
+
if (options.wallTimeMs !== undefined) {
|
|
470
|
+
details.wallTimeMs = options.wallTimeMs;
|
|
471
|
+
}
|
|
472
|
+
if (failedExit) {
|
|
473
|
+
details.exitCode = exitCode;
|
|
474
|
+
}
|
|
475
|
+
// Final defense at the tool-result boundary: no bash path (client bridge,
|
|
476
|
+
// head-retention spill, minimizer miss) may emit more than
|
|
477
|
+
// ~DEFAULT_MAX_BYTES inline. No-op for already-bounded output.
|
|
478
|
+
const cappedOutputText = await enforceInlineByteCap(outputText, {
|
|
479
|
+
label: "bash output",
|
|
480
|
+
saveArtifact: full => saveBashOriginalArtifact(this.session, full),
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
const resultBuilder = toolResult(details)
|
|
484
|
+
.text(cappedOutputText)
|
|
485
|
+
.truncationFromSummary(result, { direction: "tail" });
|
|
486
|
+
if (failedExit) resultBuilder.error();
|
|
487
|
+
return resultBuilder.done();
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
#buildBackgroundStartResult(
|
|
491
|
+
jobId: string,
|
|
492
|
+
label: string,
|
|
493
|
+
previewText: string,
|
|
494
|
+
timeoutSec: number,
|
|
495
|
+
options: { requestedTimeoutSec?: number; notices?: readonly string[] } = {},
|
|
496
|
+
): AgentToolResult<BashToolDetails> {
|
|
497
|
+
const details: BashToolDetails = {
|
|
498
|
+
timeoutSeconds: timeoutSec,
|
|
499
|
+
async: { state: "running", jobId, type: "bash" },
|
|
500
|
+
};
|
|
501
|
+
if (options.requestedTimeoutSec !== undefined && options.requestedTimeoutSec !== timeoutSec) {
|
|
502
|
+
details.requestedTimeoutSeconds = options.requestedTimeoutSec;
|
|
503
|
+
}
|
|
504
|
+
const lines: string[] = [];
|
|
505
|
+
const trimmedPreview = previewText.trimEnd();
|
|
506
|
+
if (trimmedPreview.length > 0) {
|
|
507
|
+
lines.push(trimmedPreview, "");
|
|
508
|
+
}
|
|
509
|
+
if (options.notices?.length) {
|
|
510
|
+
lines.push(...options.notices, "");
|
|
511
|
+
}
|
|
512
|
+
lines.push(`Background job ${jobId} started: ${label}`);
|
|
513
|
+
lines.push("Result will be delivered automatically when complete.");
|
|
514
|
+
lines.push(
|
|
515
|
+
`You can use \`job\` to poll until complete, but prefer to continue with another task in the meanwhile if it's not blocking.`,
|
|
516
|
+
);
|
|
517
|
+
return {
|
|
518
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
519
|
+
details,
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
#extractTextResult(result: AgentToolResult<BashToolDetails>): string {
|
|
524
|
+
return result.content.find(block => block.type === "text")?.text ?? "";
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
#startManagedBashJob(options: {
|
|
528
|
+
command: string;
|
|
529
|
+
commandCwd: string;
|
|
530
|
+
timeoutMs: number;
|
|
531
|
+
timeoutSec: number;
|
|
532
|
+
requestedTimeoutSec?: number;
|
|
533
|
+
notices?: readonly string[];
|
|
534
|
+
|
|
535
|
+
resolvedEnv?: Record<string, string>;
|
|
536
|
+
onUpdate?: AgentToolUpdateCallback<BashToolDetails>;
|
|
537
|
+
startBackgrounded: boolean;
|
|
538
|
+
}): ManagedBashJobHandle {
|
|
539
|
+
const manager = this.session.asyncJobManager;
|
|
540
|
+
if (!manager) {
|
|
541
|
+
throw new ToolError("Background job manager unavailable for this session.");
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
const label = options.command.length > 120 ? `${options.command.slice(0, 117)}...` : options.command;
|
|
545
|
+
let latestText = "";
|
|
546
|
+
let backgrounded = options.startBackgrounded;
|
|
547
|
+
const completion = Promise.withResolvers<ManagedBashJobCompletion>();
|
|
548
|
+
|
|
549
|
+
const jobId = manager.register(
|
|
550
|
+
"bash",
|
|
551
|
+
label,
|
|
552
|
+
async ({ jobId, signal: runSignal, reportProgress }) => {
|
|
553
|
+
const { path: artifactPath, id: artifactId } = (await this.session.allocateOutputArtifact?.("bash")) ?? {};
|
|
554
|
+
const tailBuffer = new TailBuffer(DEFAULT_MAX_BYTES);
|
|
555
|
+
const wallTimeStart = performance.now();
|
|
556
|
+
try {
|
|
557
|
+
const result = await executeBash(options.command, {
|
|
558
|
+
cwd: options.commandCwd,
|
|
559
|
+
sessionKey: `${this.session.getSessionId?.() ?? ""}:async:${jobId}`,
|
|
560
|
+
timeout: options.timeoutMs,
|
|
561
|
+
signal: runSignal,
|
|
562
|
+
env: options.resolvedEnv,
|
|
563
|
+
artifactPath,
|
|
564
|
+
artifactId,
|
|
565
|
+
onChunk: chunk => {
|
|
566
|
+
tailBuffer.append(chunk);
|
|
567
|
+
latestText = tailBuffer.text();
|
|
568
|
+
void reportProgress(latestText, { async: { state: "running", jobId, type: "bash" } });
|
|
569
|
+
},
|
|
570
|
+
onMinimizedSave: originalText => saveBashOriginalArtifact(this.session, originalText),
|
|
571
|
+
});
|
|
572
|
+
const wallTimeMs = performance.now() - wallTimeStart;
|
|
573
|
+
const finalResult = await this.#buildCompletedResult(result, options.timeoutSec, {
|
|
574
|
+
requestedTimeoutSec: options.requestedTimeoutSec,
|
|
575
|
+
notices: options.notices ?? [],
|
|
576
|
+
wallTimeMs,
|
|
577
|
+
});
|
|
578
|
+
const finalText = this.#extractTextResult(finalResult);
|
|
579
|
+
latestText = finalText;
|
|
580
|
+
// Hand the detailed result to the foreground auto-background
|
|
581
|
+
// waiter (which renders it, footer included) before deciding
|
|
582
|
+
// the job's terminal state.
|
|
583
|
+
completion.resolve({ kind: "completed", result: finalResult });
|
|
584
|
+
if (finalResult.isError === true) {
|
|
585
|
+
// A non-zero exit is a completed command that failed. Re-enter
|
|
586
|
+
// the failure path so the job manager records it as failed and
|
|
587
|
+
// delivers the error text, matching prior throw-based behavior.
|
|
588
|
+
throw new ToolError(finalText);
|
|
589
|
+
}
|
|
590
|
+
await reportProgress(finalText, { async: { state: "completed", jobId, type: "bash" } });
|
|
591
|
+
return finalText;
|
|
592
|
+
} catch (error) {
|
|
593
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
594
|
+
latestText = message;
|
|
595
|
+
completion.resolve({ kind: "failed", error });
|
|
596
|
+
await reportProgress(message, { async: { state: "failed", jobId, type: "bash" } });
|
|
597
|
+
throw error;
|
|
598
|
+
}
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
ownerId: this.session.getAgentId?.() ?? undefined,
|
|
602
|
+
onProgress: async (text, details) => {
|
|
603
|
+
latestText = text;
|
|
604
|
+
await options.onUpdate?.({
|
|
605
|
+
content: [{ type: "text", text }],
|
|
606
|
+
details: backgrounded ? ((details ?? {}) as BashToolDetails) : {},
|
|
607
|
+
});
|
|
608
|
+
},
|
|
609
|
+
},
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
return {
|
|
613
|
+
jobId,
|
|
614
|
+
label,
|
|
615
|
+
completion: completion.promise,
|
|
616
|
+
getLatestText: () => latestText,
|
|
617
|
+
setBackgrounded: (nextBackgrounded: boolean) => {
|
|
618
|
+
backgrounded = nextBackgrounded;
|
|
619
|
+
},
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
async #waitForManagedBashJob(
|
|
624
|
+
job: ManagedBashJobHandle,
|
|
625
|
+
thresholdMs: number,
|
|
626
|
+
signal?: AbortSignal,
|
|
627
|
+
): Promise<ManagedBashJobCompletion | { kind: "running" } | { kind: "aborted" }> {
|
|
628
|
+
if (signal?.aborted) {
|
|
629
|
+
return { kind: "aborted" };
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const waiters: Array<Promise<ManagedBashJobCompletion | { kind: "running" } | { kind: "aborted" }>> = [
|
|
633
|
+
job.completion,
|
|
634
|
+
Bun.sleep(thresholdMs).then(() => ({ kind: "running" as const })),
|
|
635
|
+
];
|
|
636
|
+
|
|
637
|
+
if (!signal) {
|
|
638
|
+
return await Promise.race(waiters);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
const { promise: abortedPromise, resolve: resolveAborted } = Promise.withResolvers<{ kind: "aborted" }>();
|
|
642
|
+
const onAbort = () => resolveAborted({ kind: "aborted" });
|
|
643
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
644
|
+
waiters.push(abortedPromise);
|
|
645
|
+
try {
|
|
646
|
+
return await Promise.race(waiters);
|
|
647
|
+
} finally {
|
|
648
|
+
signal.removeEventListener("abort", onAbort);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
#resolveAutoBackgroundWaitMs(timeoutMs: number): number {
|
|
653
|
+
if (this.#autoBackgroundThresholdMs <= 0) return 0;
|
|
654
|
+
const timeoutBufferMs = 1_000;
|
|
655
|
+
return Math.max(0, Math.min(this.#autoBackgroundThresholdMs, timeoutMs - timeoutBufferMs));
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
async execute(
|
|
659
|
+
_toolCallId: string,
|
|
660
|
+
{
|
|
661
|
+
command: rawCommand,
|
|
662
|
+
env: rawEnv,
|
|
663
|
+
timeout: rawTimeout = 300,
|
|
664
|
+
cwd,
|
|
665
|
+
|
|
666
|
+
async: asyncRequested = false,
|
|
667
|
+
pty = false,
|
|
668
|
+
}: BashToolInput,
|
|
669
|
+
signal?: AbortSignal,
|
|
670
|
+
onUpdate?: AgentToolUpdateCallback<BashToolDetails>,
|
|
671
|
+
ctx?: AgentToolContext,
|
|
672
|
+
): Promise<AgentToolResult<BashToolDetails>> {
|
|
673
|
+
let command = rawCommand;
|
|
674
|
+
const env = normalizeBashEnv(rawEnv);
|
|
675
|
+
|
|
676
|
+
// Apply conservative bash fixups (strip trailing `| head|tail` and redundant
|
|
677
|
+
// `2>&1`). The helper is single-line only and refuses anything that could
|
|
678
|
+
// change semantics.
|
|
679
|
+
if (this.session.settings.get("bash.stripTrailingHeadTail")) {
|
|
680
|
+
const fixup = applyBashFixups(command);
|
|
681
|
+
if (fixup.stripped.length > 0) {
|
|
682
|
+
command = fixup.command;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// Extract leading `cd <path> && ...` into cwd when the model ignores the cwd parameter.
|
|
687
|
+
// Constrained to a single line so a `&&` that sits on a later line of a multiline
|
|
688
|
+
// script can't pull the entire script into the "cwd" capture.
|
|
689
|
+
if (!cwd) {
|
|
690
|
+
const cdMatch = command.match(/^cd[ \t]+((?:[^&\\\n\r]|\\.)+?)[ \t]*&&[ \t]*/);
|
|
691
|
+
// Skip extraction when the path needs shell expansion ($VAR, $(...),
|
|
692
|
+
// backticks) — resolveToCwd only expands `~`, so routing those through
|
|
693
|
+
// cwd would reject commands the shell itself handles fine.
|
|
694
|
+
if (cdMatch && !/[$`(]/.test(cdMatch[1])) {
|
|
695
|
+
cwd = cdMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
696
|
+
command = command.slice(cdMatch[0].length);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
if (asyncRequested && !this.#asyncEnabled) {
|
|
700
|
+
throw new ToolError("Async bash execution is disabled. Enable async.enabled to use async mode.");
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// Check both the original command and the cwd-normalized command so
|
|
704
|
+
// leading `cd ... &&` wrappers do not hide either shell-navigation rules
|
|
705
|
+
// or the dedicated-tool command that follows the directory change.
|
|
706
|
+
if (this.session.settings.get("bashInterceptor.enabled")) {
|
|
707
|
+
const rules = this.session.settings.getBashInterceptorRules();
|
|
708
|
+
const commandsToCheck = rawCommand === command ? [command] : [rawCommand, command];
|
|
709
|
+
for (const commandToCheck of commandsToCheck) {
|
|
710
|
+
const interception = checkBashInterception(commandToCheck, ctx?.toolNames ?? [], rules);
|
|
711
|
+
if (interception.block) {
|
|
712
|
+
throw new ToolError(interception.message ?? "Command blocked");
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
const internalUrlOptions: InternalUrlExpansionOptions = {
|
|
718
|
+
skills: this.session.skills ?? [],
|
|
719
|
+
internalRouter: InternalUrlRouter.instance(),
|
|
720
|
+
localOptions: {
|
|
721
|
+
getArtifactsDir: this.session.getArtifactsDir,
|
|
722
|
+
getSessionId: this.session.getSessionId,
|
|
723
|
+
},
|
|
724
|
+
};
|
|
725
|
+
command = await expandInternalUrls(command, { ...internalUrlOptions, ensureLocalParentDirs: true });
|
|
726
|
+
const resolvedEnv = env
|
|
727
|
+
? Object.fromEntries(
|
|
728
|
+
await Promise.all(
|
|
729
|
+
Object.entries(env).map(async ([key, value]) => [
|
|
730
|
+
key,
|
|
731
|
+
await expandInternalUrls(value, {
|
|
732
|
+
...internalUrlOptions,
|
|
733
|
+
ensureLocalParentDirs: true,
|
|
734
|
+
noEscape: true,
|
|
735
|
+
}),
|
|
736
|
+
]),
|
|
737
|
+
),
|
|
738
|
+
)
|
|
739
|
+
: undefined;
|
|
740
|
+
|
|
741
|
+
// Resolve protocol URLs (skill://, agent://, etc.) in extracted cwd.
|
|
742
|
+
if (cwd?.includes("://") || cwd?.includes("local:/")) {
|
|
743
|
+
cwd = await expandInternalUrls(cwd, { ...internalUrlOptions, noEscape: true });
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// Best-effort cache invalidation: drop github-cache rows for any issue/PR
|
|
747
|
+
// number touched by a mutating `gh` subcommand inside this bash call so
|
|
748
|
+
// subsequent issue:// / pr:// reads pick up the post-mutation state
|
|
749
|
+
// instead of the cached pre-mutation snapshot.
|
|
750
|
+
invalidateGithubCacheForBashCommand(command);
|
|
751
|
+
|
|
752
|
+
const commandCwd = cwd ? resolveToCwd(cwd, this.session.cwd) : this.session.cwd;
|
|
753
|
+
let cwdStat: fs.Stats;
|
|
754
|
+
try {
|
|
755
|
+
cwdStat = await fs.promises.stat(commandCwd);
|
|
756
|
+
} catch (err) {
|
|
757
|
+
if (isEnoent(err)) {
|
|
758
|
+
throw new ToolError(`Working directory does not exist: ${commandCwd}`);
|
|
759
|
+
}
|
|
760
|
+
throw err;
|
|
761
|
+
}
|
|
762
|
+
if (!cwdStat.isDirectory()) {
|
|
763
|
+
throw new ToolError(`Working directory is not a directory: ${commandCwd}`);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// Clamp to reasonable range: 1s - 3600s (1 hour)
|
|
767
|
+
const requestedTimeoutSec = rawTimeout;
|
|
768
|
+
const timeoutSec = clampTimeout("bash", requestedTimeoutSec);
|
|
769
|
+
const timeoutMs = timeoutSec * 1000;
|
|
770
|
+
const pendingNotices: string[] = [];
|
|
771
|
+
const timeoutClampNotice = formatTimeoutClampNotice(requestedTimeoutSec, timeoutSec);
|
|
772
|
+
if (timeoutClampNotice) pendingNotices.push(timeoutClampNotice);
|
|
773
|
+
|
|
774
|
+
if (asyncRequested) {
|
|
775
|
+
if (!this.session.asyncJobManager) {
|
|
776
|
+
throw new ToolError("Async job manager unavailable for this session.");
|
|
777
|
+
}
|
|
778
|
+
const job = this.#startManagedBashJob({
|
|
779
|
+
command,
|
|
780
|
+
commandCwd,
|
|
781
|
+
timeoutMs,
|
|
782
|
+
timeoutSec,
|
|
783
|
+
requestedTimeoutSec,
|
|
784
|
+
notices: pendingNotices,
|
|
785
|
+
|
|
786
|
+
resolvedEnv,
|
|
787
|
+
onUpdate,
|
|
788
|
+
startBackgrounded: true,
|
|
789
|
+
});
|
|
790
|
+
return this.#buildBackgroundStartResult(job.jobId, job.label, "", timeoutSec, {
|
|
791
|
+
requestedTimeoutSec,
|
|
792
|
+
notices: pendingNotices,
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// The client-bridge terminal provides a live terminal card in the editor;
|
|
797
|
+
// when available it wins over auto-backgrounding (both are opt-in, and
|
|
798
|
+
// auto-background would otherwise silently disable the terminal route).
|
|
799
|
+
const clientBridge = this.session.getClientBridge?.();
|
|
800
|
+
const bridgeTerminalAvailable = Boolean(
|
|
801
|
+
clientBridge?.capabilities.terminal && clientBridge.createTerminal && !pty,
|
|
802
|
+
);
|
|
803
|
+
|
|
804
|
+
const autoBgManager = this.session.asyncJobManager;
|
|
805
|
+
// At the running-job cap, fall through to direct foreground execution
|
|
806
|
+
// instead of failing every bash call until a slot frees up.
|
|
807
|
+
if (
|
|
808
|
+
this.#autoBackgroundEnabled &&
|
|
809
|
+
!pty &&
|
|
810
|
+
!bridgeTerminalAvailable &&
|
|
811
|
+
autoBgManager &&
|
|
812
|
+
!autoBgManager.atCapacity
|
|
813
|
+
) {
|
|
814
|
+
const autoBackgroundWaitMs = this.#resolveAutoBackgroundWaitMs(timeoutMs);
|
|
815
|
+
const startBackgrounded = autoBackgroundWaitMs === 0;
|
|
816
|
+
const job = this.#startManagedBashJob({
|
|
817
|
+
command,
|
|
818
|
+
commandCwd,
|
|
819
|
+
timeoutMs,
|
|
820
|
+
timeoutSec,
|
|
821
|
+
requestedTimeoutSec,
|
|
822
|
+
notices: pendingNotices,
|
|
823
|
+
|
|
824
|
+
resolvedEnv,
|
|
825
|
+
onUpdate,
|
|
826
|
+
startBackgrounded,
|
|
827
|
+
});
|
|
828
|
+
if (startBackgrounded) {
|
|
829
|
+
return this.#buildBackgroundStartResult(job.jobId, job.label, "", timeoutSec, {
|
|
830
|
+
requestedTimeoutSec,
|
|
831
|
+
notices: pendingNotices,
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
// Suppress the completion delivery up front so a job finishing while we
|
|
835
|
+
// foreground-wait cannot also be injected by the delivery loop. Lifted
|
|
836
|
+
// via resumeDeliveries() if we end up backgrounding after all.
|
|
837
|
+
autoBgManager.acknowledgeDeliveries([job.jobId]);
|
|
838
|
+
const waitResult = await this.#waitForManagedBashJob(job, autoBackgroundWaitMs, signal);
|
|
839
|
+
if (waitResult.kind === "completed") {
|
|
840
|
+
return waitResult.result;
|
|
841
|
+
}
|
|
842
|
+
if (waitResult.kind === "failed") {
|
|
843
|
+
throw waitResult.error;
|
|
844
|
+
}
|
|
845
|
+
if (waitResult.kind === "aborted") {
|
|
846
|
+
autoBgManager.cancel(job.jobId);
|
|
847
|
+
throw new ToolAbortError(job.getLatestText() || "Command aborted");
|
|
848
|
+
}
|
|
849
|
+
job.setBackgrounded(true);
|
|
850
|
+
autoBgManager.resumeDeliveries([job.jobId]);
|
|
851
|
+
return this.#buildBackgroundStartResult(job.jobId, job.label, job.getLatestText(), timeoutSec, {
|
|
852
|
+
requestedTimeoutSec,
|
|
853
|
+
notices: pendingNotices,
|
|
854
|
+
});
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// Route through the client terminal when the client advertises the terminal capability.
|
|
858
|
+
// Skip when pty=true (PTY needs the local terminal UI).
|
|
859
|
+
if (clientBridge?.capabilities.terminal && clientBridge.createTerminal && !pty) {
|
|
860
|
+
const bridgeWallTimeStart = performance.now();
|
|
861
|
+
const handle = await clientBridge.createTerminal({
|
|
862
|
+
command,
|
|
863
|
+
cwd: commandCwd,
|
|
864
|
+
env: resolvedEnv
|
|
865
|
+
? Object.entries(resolvedEnv).map(([name, value]) => ({ name, value: value as string }))
|
|
866
|
+
: undefined,
|
|
867
|
+
outputByteLimit: DEFAULT_MAX_BYTES,
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
// Emit partial update so the editor can embed the live terminal card.
|
|
871
|
+
onUpdate?.({ content: [], details: { terminalId: handle.terminalId } });
|
|
872
|
+
|
|
873
|
+
const exitPromise = handle.waitForExit();
|
|
874
|
+
let exitStatus!: ClientBridgeTerminalExitStatus;
|
|
875
|
+
|
|
876
|
+
type BridgeRaceResult =
|
|
877
|
+
| { kind: "exit"; status: ClientBridgeTerminalExitStatus }
|
|
878
|
+
| { kind: "poll" }
|
|
879
|
+
| { kind: "timeout" }
|
|
880
|
+
| { kind: "aborted" };
|
|
881
|
+
|
|
882
|
+
// Set up abort listener before entering the poll loop. The listener
|
|
883
|
+
// kicks off `handle.kill()` synchronously so a `session/cancel`
|
|
884
|
+
// arriving mid-poll terminates the remote command immediately,
|
|
885
|
+
// instead of waiting for the next `currentOutput()` to return.
|
|
886
|
+
const { promise: abortedP, resolve: resolveAborted } = Promise.withResolvers<void>();
|
|
887
|
+
let killStarted = false;
|
|
888
|
+
const fireKill = (): Promise<void> => {
|
|
889
|
+
if (killStarted) return Promise.resolve();
|
|
890
|
+
killStarted = true;
|
|
891
|
+
return handle.kill().catch((error: unknown) => {
|
|
892
|
+
logger.warn("ACP terminal kill failed", { terminalId: handle.terminalId, error });
|
|
893
|
+
});
|
|
894
|
+
};
|
|
895
|
+
const onAbortSignal = () => {
|
|
896
|
+
resolveAborted();
|
|
897
|
+
void fireKill();
|
|
898
|
+
};
|
|
899
|
+
signal?.addEventListener("abort", onAbortSignal, { once: true });
|
|
900
|
+
|
|
901
|
+
try {
|
|
902
|
+
try {
|
|
903
|
+
if (signal?.aborted) {
|
|
904
|
+
await fireKill();
|
|
905
|
+
throw new ToolAbortError("Command aborted");
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
const timeoutPromise = Bun.sleep(timeoutMs).then(() => ({ kind: "timeout" as const }));
|
|
909
|
+
// Poll until the process exits, times out, or the caller aborts.
|
|
910
|
+
for (;;) {
|
|
911
|
+
const racers: Array<Promise<BridgeRaceResult>> = [
|
|
912
|
+
exitPromise.then(s => ({ kind: "exit" as const, status: s })),
|
|
913
|
+
timeoutPromise,
|
|
914
|
+
Bun.sleep(250).then(() => ({ kind: "poll" as const })),
|
|
915
|
+
];
|
|
916
|
+
if (signal) {
|
|
917
|
+
racers.push(abortedP.then(() => ({ kind: "aborted" as const })));
|
|
918
|
+
}
|
|
919
|
+
const raced = await Promise.race(racers);
|
|
920
|
+
|
|
921
|
+
if (raced.kind === "aborted" || signal?.aborted) {
|
|
922
|
+
await fireKill();
|
|
923
|
+
throw new ToolAbortError("Command aborted");
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
if (raced.kind === "timeout") {
|
|
927
|
+
// Kill before reading final output so a slow `terminal/output`
|
|
928
|
+
// RPC cannot let a timed-out command keep running past the
|
|
929
|
+
// enforced timeout. The handle stays valid post-kill so the
|
|
930
|
+
// buffered output is still readable.
|
|
931
|
+
await fireKill();
|
|
932
|
+
let current = { output: "", truncated: false };
|
|
933
|
+
try {
|
|
934
|
+
current = await handle.currentOutput();
|
|
935
|
+
} catch (error) {
|
|
936
|
+
logger.warn("ACP terminal final output read failed", {
|
|
937
|
+
terminalId: handle.terminalId,
|
|
938
|
+
error,
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
const timedOutResult: BashInteractiveResult = {
|
|
942
|
+
output: current.output,
|
|
943
|
+
exitCode: undefined,
|
|
944
|
+
cancelled: false,
|
|
945
|
+
timedOut: true,
|
|
946
|
+
truncated: current.truncated,
|
|
947
|
+
totalLines: current.output.length > 0 ? current.output.split("\n").length : 0,
|
|
948
|
+
totalBytes: current.output.length,
|
|
949
|
+
outputLines: current.output.length > 0 ? current.output.split("\n").length : 0,
|
|
950
|
+
outputBytes: current.output.length,
|
|
951
|
+
};
|
|
952
|
+
return this.#buildCompletedResult(timedOutResult, timeoutSec, {
|
|
953
|
+
requestedTimeoutSec,
|
|
954
|
+
notices: pendingNotices,
|
|
955
|
+
terminalId: handle.terminalId,
|
|
956
|
+
wallTimeMs: performance.now() - bridgeWallTimeStart,
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
if (raced.kind === "exit") {
|
|
961
|
+
exitStatus = raced.status;
|
|
962
|
+
break;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// Poll tick: push current output so agent-loop transcript stays consistent.
|
|
966
|
+
// Race the read against abort so a stuck `terminal/output` RPC does not
|
|
967
|
+
// delay cancellation.
|
|
968
|
+
const pollOutput = await Promise.race([
|
|
969
|
+
handle.currentOutput(),
|
|
970
|
+
abortedP.then(() => undefined as ClientBridgeTerminalOutput | undefined),
|
|
971
|
+
]);
|
|
972
|
+
if (pollOutput === undefined) {
|
|
973
|
+
// Abort fired during the poll-tick read; let the next loop iteration
|
|
974
|
+
// observe `signal?.aborted` and exit via the abort branch.
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
onUpdate?.({
|
|
978
|
+
content: [{ type: "text", text: pollOutput.output }],
|
|
979
|
+
details: { terminalId: handle.terminalId },
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
} finally {
|
|
983
|
+
signal?.removeEventListener("abort", onAbortSignal);
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
// Fetch final output; the terminal is released in the outer finally.
|
|
987
|
+
const finalOutput = await handle.currentOutput();
|
|
988
|
+
|
|
989
|
+
// Map exit status: null exitCode with a signal → treat as signal kill (137).
|
|
990
|
+
const rawExitCode = exitStatus.exitCode;
|
|
991
|
+
const exitCode: number | undefined =
|
|
992
|
+
rawExitCode != null ? rawExitCode : exitStatus.signal ? 137 : undefined;
|
|
993
|
+
|
|
994
|
+
const outputText = finalOutput.output;
|
|
995
|
+
const outputByteLen = outputText.length;
|
|
996
|
+
const outputLineCount = outputText.length > 0 ? outputText.split("\n").length : 0;
|
|
997
|
+
|
|
998
|
+
const bridgeResult: BashResult = {
|
|
999
|
+
output: outputText,
|
|
1000
|
+
exitCode,
|
|
1001
|
+
cancelled: false,
|
|
1002
|
+
truncated: finalOutput.truncated,
|
|
1003
|
+
totalLines: outputLineCount,
|
|
1004
|
+
totalBytes: outputByteLen,
|
|
1005
|
+
outputLines: outputLineCount,
|
|
1006
|
+
outputBytes: outputByteLen,
|
|
1007
|
+
};
|
|
1008
|
+
|
|
1009
|
+
const bridgeNotices: string[] = [];
|
|
1010
|
+
if (finalOutput.truncated) bridgeNotices.push("(output truncated)");
|
|
1011
|
+
for (const notice of pendingNotices) bridgeNotices.push(notice);
|
|
1012
|
+
|
|
1013
|
+
return this.#buildCompletedResult(bridgeResult, timeoutSec, {
|
|
1014
|
+
requestedTimeoutSec,
|
|
1015
|
+
notices: bridgeNotices,
|
|
1016
|
+
terminalId: handle.terminalId,
|
|
1017
|
+
wallTimeMs: performance.now() - bridgeWallTimeStart,
|
|
1018
|
+
});
|
|
1019
|
+
} finally {
|
|
1020
|
+
try {
|
|
1021
|
+
await handle.release();
|
|
1022
|
+
} catch (error) {
|
|
1023
|
+
logger.warn("ACP terminal release failed", { terminalId: handle.terminalId, error });
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
// Track output for streaming updates (tail only)
|
|
1029
|
+
const tailBuffer = new TailBuffer(DEFAULT_MAX_BYTES);
|
|
1030
|
+
|
|
1031
|
+
// Allocate artifact for truncated output storage
|
|
1032
|
+
const { path: artifactPath, id: artifactId } = (await this.session.allocateOutputArtifact?.("bash")) ?? {};
|
|
1033
|
+
|
|
1034
|
+
const interactiveUi = canUseInteractiveBashPty(pty, ctx) ? ctx?.ui : undefined;
|
|
1035
|
+
if (pty && !interactiveUi) {
|
|
1036
|
+
pendingNotices.push("pty requested but unavailable in this environment; ran without a terminal");
|
|
1037
|
+
}
|
|
1038
|
+
const wallTimeStart = performance.now();
|
|
1039
|
+
const result: BashResult | BashInteractiveResult = interactiveUi
|
|
1040
|
+
? await runInteractiveBashPty(interactiveUi, {
|
|
1041
|
+
command,
|
|
1042
|
+
cwd: commandCwd,
|
|
1043
|
+
timeoutMs,
|
|
1044
|
+
signal,
|
|
1045
|
+
env: resolvedEnv,
|
|
1046
|
+
artifactPath,
|
|
1047
|
+
artifactId,
|
|
1048
|
+
})
|
|
1049
|
+
: await executeBash(command, {
|
|
1050
|
+
cwd: commandCwd,
|
|
1051
|
+
sessionKey: this.session.getSessionId?.() ?? undefined,
|
|
1052
|
+
timeout: timeoutMs,
|
|
1053
|
+
signal,
|
|
1054
|
+
env: resolvedEnv,
|
|
1055
|
+
artifactPath,
|
|
1056
|
+
artifactId,
|
|
1057
|
+
onChunk: streamTailUpdates(tailBuffer, onUpdate),
|
|
1058
|
+
onMinimizedSave: originalText => saveBashOriginalArtifact(this.session, originalText),
|
|
1059
|
+
});
|
|
1060
|
+
const wallTimeMs = performance.now() - wallTimeStart;
|
|
1061
|
+
if (result.cancelled) {
|
|
1062
|
+
const out = normalizeResultOutput(result);
|
|
1063
|
+
// PTY output carries no cancel/timeout notice of its own; annotate so
|
|
1064
|
+
// the model can tell an abort from a plain failure.
|
|
1065
|
+
const message = isInteractiveResult(result) && out ? `${out}\n\n[Command aborted]` : out || "Command aborted";
|
|
1066
|
+
if (signal?.aborted) {
|
|
1067
|
+
throw new ToolAbortError(message);
|
|
1068
|
+
}
|
|
1069
|
+
throw new ToolError(message);
|
|
1070
|
+
}
|
|
1071
|
+
if (isInteractiveResult(result) && result.timedOut) {
|
|
1072
|
+
const out = normalizeResultOutput(result);
|
|
1073
|
+
throw new ToolError(
|
|
1074
|
+
out
|
|
1075
|
+
? `${out}\n\n[Command timed out after ${timeoutSec} seconds]`
|
|
1076
|
+
: `Command timed out after ${timeoutSec} seconds`,
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
return this.#buildCompletedResult(result, timeoutSec, {
|
|
1080
|
+
requestedTimeoutSec,
|
|
1081
|
+
notices: pendingNotices,
|
|
1082
|
+
wallTimeMs,
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// =============================================================================
|
|
1088
|
+
// TUI Renderer
|
|
1089
|
+
// =============================================================================
|
|
1090
|
+
export interface BashRenderArgs {
|
|
1091
|
+
command?: string;
|
|
1092
|
+
env?: Record<string, string>;
|
|
1093
|
+
timeout?: number;
|
|
1094
|
+
cwd?: string;
|
|
1095
|
+
__partialJson?: string;
|
|
1096
|
+
[key: string]: unknown;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
export interface BashRenderContext {
|
|
1100
|
+
/** Raw output text */
|
|
1101
|
+
output?: string;
|
|
1102
|
+
/** Whether output came from artifact storage */
|
|
1103
|
+
isFullOutput?: boolean;
|
|
1104
|
+
/** Whether output is expanded */
|
|
1105
|
+
expanded?: boolean;
|
|
1106
|
+
/** Number of preview lines when collapsed */
|
|
1107
|
+
previewLines?: number;
|
|
1108
|
+
/** Timeout in seconds */
|
|
1109
|
+
timeout?: number;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
export interface ShellRendererConfig<TArgs> {
|
|
1113
|
+
resolveTitle: (args: TArgs | undefined, options: RenderResultOptions) => string;
|
|
1114
|
+
resolveCommand?: (args: TArgs | undefined) => string | undefined;
|
|
1115
|
+
resolveCwd?: (args: TArgs | undefined) => string | undefined;
|
|
1116
|
+
resolveEnv?: (args: TArgs | undefined) => Record<string, string> | undefined;
|
|
1117
|
+
showHeader?: boolean;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
function getPartialJson<TArgs>(args: TArgs | undefined): string | undefined {
|
|
1121
|
+
if (!args || typeof args !== "object" || !("__partialJson" in args)) return undefined;
|
|
1122
|
+
const value = (args as { __partialJson?: unknown }).__partialJson;
|
|
1123
|
+
return typeof value === "string" ? value : undefined;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
export function getBashEnvForDisplay(args: BashRenderArgs): Record<string, string> | undefined {
|
|
1127
|
+
// During streaming, partial-json parsing often does not surface env values until the object closes.
|
|
1128
|
+
// Recover them from the raw JSON buffer so the pending bash preview can show `NAME="..." cmd` immediately,
|
|
1129
|
+
// instead of rendering only the command and making the env assignment appear at the very end.
|
|
1130
|
+
const partialEnv = extractPartialBashEnv(args.__partialJson);
|
|
1131
|
+
if (partialEnv && args.env) return { ...partialEnv, ...args.env };
|
|
1132
|
+
return args.env ?? partialEnv;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
/**
|
|
1136
|
+
* Returns the bash command formatted for the result body: the dim `$ cd … &&`
|
|
1137
|
+
* prefix joined with syntax-highlighted command lines. The prefix is applied
|
|
1138
|
+
* only to the first line so multi-line commands display cleanly — terminals
|
|
1139
|
+
* reset SGR state at line boundaries, which made the previous single-string
|
|
1140
|
+
* `theme.fg("dim", ...)` form render only the first line as dim.
|
|
1141
|
+
*/
|
|
1142
|
+
export function formatBashCommandLines(args: BashRenderArgs, uiTheme: Theme): string[] {
|
|
1143
|
+
const command = replaceTabs(args.command || "…");
|
|
1144
|
+
const cwd = getProjectDir();
|
|
1145
|
+
const displayWorkdir = formatToolWorkingDirectory(args.cwd, cwd);
|
|
1146
|
+
const envAssignments = formatBashEnvAssignments(getBashEnvForDisplay(args));
|
|
1147
|
+
const prefixParts = ["$"];
|
|
1148
|
+
if (displayWorkdir) prefixParts.push(`cd ${displayWorkdir} &&`);
|
|
1149
|
+
if (envAssignments) prefixParts.push(envAssignments);
|
|
1150
|
+
const prefix = uiTheme.fg("dim", `${prefixParts.join(" ")} `);
|
|
1151
|
+
const highlightedLines = highlightCode(command, "bash");
|
|
1152
|
+
if (highlightedLines.length === 0) return [prefix.trimEnd()];
|
|
1153
|
+
return highlightedLines.map((line, i) => (i === 0 ? `${prefix}${line}` : line));
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
function toBashRenderArgs<TArgs>(args: TArgs | undefined, config: ShellRendererConfig<TArgs>): BashRenderArgs {
|
|
1157
|
+
return {
|
|
1158
|
+
command: config.resolveCommand?.(args),
|
|
1159
|
+
cwd: config.resolveCwd?.(args),
|
|
1160
|
+
env: config.resolveEnv?.(args),
|
|
1161
|
+
__partialJson: getPartialJson(args),
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
export function createShellRenderer<TArgs>(config: ShellRendererConfig<TArgs>) {
|
|
1166
|
+
return {
|
|
1167
|
+
renderCall(args: TArgs, options: RenderResultOptions, uiTheme: Theme): Component {
|
|
1168
|
+
const renderArgs = toBashRenderArgs(args, config);
|
|
1169
|
+
const cmdLines = formatBashCommandLines(renderArgs, uiTheme);
|
|
1170
|
+
const header =
|
|
1171
|
+
config.showHeader === false
|
|
1172
|
+
? undefined
|
|
1173
|
+
: renderStatusLine({ icon: "pending", title: config.resolveTitle(args, options) }, uiTheme);
|
|
1174
|
+
const outputBlock = new CachedOutputBlock();
|
|
1175
|
+
return markFramedBlockComponent({
|
|
1176
|
+
render: (width: number): readonly string[] =>
|
|
1177
|
+
outputBlock.render(
|
|
1178
|
+
{
|
|
1179
|
+
header,
|
|
1180
|
+
state: "pending",
|
|
1181
|
+
sections: [{ lines: capPreviewLines(cmdLines, uiTheme, { expanded: options.expanded }) }],
|
|
1182
|
+
width,
|
|
1183
|
+
},
|
|
1184
|
+
uiTheme,
|
|
1185
|
+
),
|
|
1186
|
+
invalidate: () => {
|
|
1187
|
+
outputBlock.invalidate();
|
|
1188
|
+
},
|
|
1189
|
+
});
|
|
1190
|
+
},
|
|
1191
|
+
|
|
1192
|
+
renderResult(
|
|
1193
|
+
result: {
|
|
1194
|
+
content: Array<{ type: string; text?: string }>;
|
|
1195
|
+
details?: BashToolDetails;
|
|
1196
|
+
isError?: boolean;
|
|
1197
|
+
},
|
|
1198
|
+
options: RenderResultOptions & { renderContext?: BashRenderContext },
|
|
1199
|
+
uiTheme: Theme,
|
|
1200
|
+
args?: TArgs,
|
|
1201
|
+
): Component {
|
|
1202
|
+
const renderArgs = toBashRenderArgs(args, config);
|
|
1203
|
+
const cmdLines = args ? formatBashCommandLines(renderArgs, uiTheme) : undefined;
|
|
1204
|
+
const isError = result.isError === true;
|
|
1205
|
+
const isPartial = options.isPartial === true;
|
|
1206
|
+
const success = !isPartial && !isError;
|
|
1207
|
+
const header =
|
|
1208
|
+
config.showHeader === false
|
|
1209
|
+
? undefined
|
|
1210
|
+
: renderStatusLine(
|
|
1211
|
+
success
|
|
1212
|
+
? {
|
|
1213
|
+
iconOverride: uiTheme.styledSymbol("tool.bash", "accent"),
|
|
1214
|
+
title: config.resolveTitle(args, options),
|
|
1215
|
+
}
|
|
1216
|
+
: {
|
|
1217
|
+
icon: isPartial ? "pending" : "error",
|
|
1218
|
+
title: config.resolveTitle(args, options),
|
|
1219
|
+
},
|
|
1220
|
+
uiTheme,
|
|
1221
|
+
);
|
|
1222
|
+
const details = result.details;
|
|
1223
|
+
const outputBlock = new CachedOutputBlock();
|
|
1224
|
+
|
|
1225
|
+
// Per-instance cache for the expensive inner lines computation. Mirrors
|
|
1226
|
+
// the eval-renderer pattern (`eval-render.ts:709-752`): without this,
|
|
1227
|
+
// every TUI repaint (one per keystroke when a long transcript is on
|
|
1228
|
+
// screen) re-runs `split` / `replaceTabs` / `truncateToVisualLines` over
|
|
1229
|
+
// the whole stored output for every bash row in scrollback. With a
|
|
1230
|
+
// 50KB-tail bash result times hundreds of rows, that re-rendering is
|
|
1231
|
+
// what pinned the main thread in issue #2081 and made keystrokes feel
|
|
1232
|
+
// like the CPU was at 100%. The cache key includes every render input
|
|
1233
|
+
// that materially affects the produced lines.
|
|
1234
|
+
let cachedWidth: number | undefined;
|
|
1235
|
+
let cachedPreviewLines: number | undefined;
|
|
1236
|
+
let cachedExpanded: boolean | undefined;
|
|
1237
|
+
let cachedRawOutput: string | undefined;
|
|
1238
|
+
let cachedIsPartial: boolean | undefined;
|
|
1239
|
+
let cachedLines: readonly string[] | undefined;
|
|
1240
|
+
|
|
1241
|
+
return markFramedBlockComponent({
|
|
1242
|
+
render: (width: number): readonly string[] => {
|
|
1243
|
+
// REACTIVE: read mutable options at render time
|
|
1244
|
+
const { renderContext } = options;
|
|
1245
|
+
const expanded = renderContext?.expanded ?? options.expanded;
|
|
1246
|
+
const previewLines = renderContext?.previewLines ?? BASH_DEFAULT_PREVIEW_LINES;
|
|
1247
|
+
|
|
1248
|
+
// Get output from context (preferred) or fall back to result content.
|
|
1249
|
+
// Strip the LLM-facing notice appended by wrappedExecute so we don't
|
|
1250
|
+
// double-print it alongside the styled warning line below.
|
|
1251
|
+
const rawOutput = renderContext?.output ?? result.content?.find(c => c.type === "text")?.text ?? "";
|
|
1252
|
+
|
|
1253
|
+
const isPartial = options.isPartial === true;
|
|
1254
|
+
|
|
1255
|
+
if (
|
|
1256
|
+
cachedLines !== undefined &&
|
|
1257
|
+
cachedWidth === width &&
|
|
1258
|
+
cachedPreviewLines === previewLines &&
|
|
1259
|
+
cachedExpanded === expanded &&
|
|
1260
|
+
cachedRawOutput === rawOutput &&
|
|
1261
|
+
cachedIsPartial === isPartial
|
|
1262
|
+
) {
|
|
1263
|
+
return cachedLines;
|
|
1264
|
+
}
|
|
1265
|
+
const strippedOutput = stripOutputNotice(rawOutput, details?.meta);
|
|
1266
|
+
const withoutExit = stripExitCodeNotice(strippedOutput, details?.exitCode);
|
|
1267
|
+
const withoutWall = stripWallTimeNotice(withoutExit, details?.wallTimeMs);
|
|
1268
|
+
const rawOutputArtifact = stripRawOutputArtifactNotice(withoutWall);
|
|
1269
|
+
const output = rawOutputArtifact.text;
|
|
1270
|
+
const displayOutput = output.trimEnd();
|
|
1271
|
+
const showingFullOutput = expanded && renderContext?.isFullOutput === true;
|
|
1272
|
+
|
|
1273
|
+
// Build truncation warning
|
|
1274
|
+
const timeoutSeconds = details?.timeoutSeconds ?? renderContext?.timeout;
|
|
1275
|
+
const requestedTimeoutSeconds = details?.requestedTimeoutSeconds;
|
|
1276
|
+
const wallTimeMs = details?.wallTimeMs;
|
|
1277
|
+
const statsParts: string[] = [];
|
|
1278
|
+
if (wallTimeMs !== undefined) {
|
|
1279
|
+
statsParts.push(`Wall: ${formatWallTimeSeconds(wallTimeMs)}s`);
|
|
1280
|
+
}
|
|
1281
|
+
if (typeof timeoutSeconds === "number") {
|
|
1282
|
+
statsParts.push(
|
|
1283
|
+
requestedTimeoutSeconds !== undefined && requestedTimeoutSeconds !== timeoutSeconds
|
|
1284
|
+
? `Timeout: ${timeoutSeconds}s (requested ${requestedTimeoutSeconds}s clamped)`
|
|
1285
|
+
: `Timeout: ${timeoutSeconds}s`,
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
if (rawOutputArtifact.artifactId) {
|
|
1289
|
+
statsParts.push(`Artifact: ${rawOutputArtifact.artifactId}`);
|
|
1290
|
+
}
|
|
1291
|
+
if (isError && typeof details?.exitCode === "number") {
|
|
1292
|
+
statsParts.push(`Exit: ${details.exitCode}`);
|
|
1293
|
+
}
|
|
1294
|
+
const timeoutLine =
|
|
1295
|
+
statsParts.length > 0
|
|
1296
|
+
? uiTheme.fg(
|
|
1297
|
+
"dim",
|
|
1298
|
+
`${uiTheme.format.bracketLeft}${statsParts.join(" | ")}${uiTheme.format.bracketRight}`,
|
|
1299
|
+
)
|
|
1300
|
+
: undefined;
|
|
1301
|
+
let warningLine: string | undefined;
|
|
1302
|
+
if (details?.meta?.truncation && !showingFullOutput) {
|
|
1303
|
+
warningLine = formatStyledTruncationWarning(details.meta, uiTheme) ?? undefined;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
const outputLines: string[] = [];
|
|
1307
|
+
const hasOutput = displayOutput.trim().length > 0;
|
|
1308
|
+
const rawOutputLines = displayOutput.split("\n");
|
|
1309
|
+
const sixelLineMask =
|
|
1310
|
+
TERMINAL.imageProtocol === ImageProtocol.Sixel ? getSixelLineMask(rawOutputLines) : undefined;
|
|
1311
|
+
const hasSixelOutput = sixelLineMask?.some(Boolean) ?? false;
|
|
1312
|
+
if (hasOutput) {
|
|
1313
|
+
if (hasSixelOutput) {
|
|
1314
|
+
outputLines.push(
|
|
1315
|
+
...rawOutputLines.map((line, index) =>
|
|
1316
|
+
sixelLineMask?.[index] ? line : uiTheme.fg("toolOutput", replaceTabs(line)),
|
|
1317
|
+
),
|
|
1318
|
+
);
|
|
1319
|
+
} else if (expanded) {
|
|
1320
|
+
outputLines.push(...rawOutputLines.map(line => uiTheme.fg("toolOutput", replaceTabs(line))));
|
|
1321
|
+
} else {
|
|
1322
|
+
const styledOutput = rawOutputLines
|
|
1323
|
+
.map(line => uiTheme.fg("toolOutput", replaceTabs(line)))
|
|
1324
|
+
.join("\n");
|
|
1325
|
+
const textContent = styledOutput;
|
|
1326
|
+
const result = truncateToVisualLines(textContent, previewLines, width);
|
|
1327
|
+
if (result.skippedCount > 0) {
|
|
1328
|
+
outputLines.push(
|
|
1329
|
+
uiTheme.fg(
|
|
1330
|
+
"dim",
|
|
1331
|
+
`… (${result.skippedCount} earlier lines, showing ${result.visualLines.length} of ${result.skippedCount + result.visualLines.length}) (ctrl+o to expand)`,
|
|
1332
|
+
),
|
|
1333
|
+
);
|
|
1334
|
+
}
|
|
1335
|
+
outputLines.push(...result.visualLines);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
if (timeoutLine) outputLines.push(timeoutLine);
|
|
1339
|
+
if (warningLine) outputLines.push(warningLine);
|
|
1340
|
+
|
|
1341
|
+
const framed = outputBlock.render(
|
|
1342
|
+
{
|
|
1343
|
+
header,
|
|
1344
|
+
state: isPartial ? "pending" : isError ? "error" : "success",
|
|
1345
|
+
sections: [
|
|
1346
|
+
{
|
|
1347
|
+
lines: isPartial ? capPreviewLines(cmdLines ?? [], uiTheme, { expanded }) : (cmdLines ?? []),
|
|
1348
|
+
},
|
|
1349
|
+
{ label: uiTheme.fg("toolTitle", "Output"), lines: outputLines },
|
|
1350
|
+
],
|
|
1351
|
+
width,
|
|
1352
|
+
},
|
|
1353
|
+
uiTheme,
|
|
1354
|
+
);
|
|
1355
|
+
|
|
1356
|
+
cachedWidth = width;
|
|
1357
|
+
cachedPreviewLines = previewLines;
|
|
1358
|
+
cachedExpanded = expanded;
|
|
1359
|
+
cachedRawOutput = rawOutput;
|
|
1360
|
+
cachedIsPartial = isPartial;
|
|
1361
|
+
cachedLines = framed;
|
|
1362
|
+
return framed;
|
|
1363
|
+
},
|
|
1364
|
+
invalidate: () => {
|
|
1365
|
+
outputBlock.invalidate();
|
|
1366
|
+
cachedLines = undefined;
|
|
1367
|
+
cachedWidth = undefined;
|
|
1368
|
+
cachedPreviewLines = undefined;
|
|
1369
|
+
cachedExpanded = undefined;
|
|
1370
|
+
cachedRawOutput = undefined;
|
|
1371
|
+
cachedIsPartial = undefined;
|
|
1372
|
+
},
|
|
1373
|
+
});
|
|
1374
|
+
},
|
|
1375
|
+
mergeCallAndResult: true,
|
|
1376
|
+
inline: true,
|
|
1377
|
+
};
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
export const bashToolRenderer = createShellRenderer<BashRenderArgs>({
|
|
1381
|
+
resolveTitle: () => "Bash",
|
|
1382
|
+
resolveCommand: args => args?.command,
|
|
1383
|
+
resolveCwd: args => args?.cwd,
|
|
1384
|
+
resolveEnv: args => args?.env,
|
|
1385
|
+
showHeader: false,
|
|
1386
|
+
});
|