pi-agent-zh 17.1.8

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.
Files changed (1519) hide show
  1. package/CHANGELOG.md +14459 -0
  2. package/README.md +35 -0
  3. package/dist/CHANGELOG-vt8ene9g.md +14459 -0
  4. package/dist/cli.js +22079 -0
  5. package/dist/template-dys3vk5b.js +1671 -0
  6. package/dist/template-f8wx9vfn.css +1355 -0
  7. package/dist/template-qat058wr.html +55 -0
  8. package/dist/tool-views.generated-jdfmzwmn.js +35 -0
  9. package/examples/README.md +21 -0
  10. package/examples/custom-tools/README.md +104 -0
  11. package/examples/custom-tools/hello/index.ts +20 -0
  12. package/examples/extensions/README.md +142 -0
  13. package/examples/extensions/api-demo.ts +79 -0
  14. package/examples/extensions/chalk-logger.ts +25 -0
  15. package/examples/extensions/hello.ts +31 -0
  16. package/examples/extensions/pirate.ts +43 -0
  17. package/examples/extensions/plan-mode.ts +549 -0
  18. package/examples/extensions/reload-runtime.ts +38 -0
  19. package/examples/extensions/thinking-note.ts +13 -0
  20. package/examples/extensions/tools.ts +145 -0
  21. package/examples/extensions/with-deps/index.ts +36 -0
  22. package/examples/extensions/with-deps/package-lock.json +31 -0
  23. package/examples/extensions/with-deps/package.json +17 -0
  24. package/examples/hooks/README.md +56 -0
  25. package/examples/hooks/auto-commit-on-exit.ts +48 -0
  26. package/examples/hooks/confirm-destructive.ts +58 -0
  27. package/examples/hooks/custom-compaction.ts +115 -0
  28. package/examples/hooks/dirty-repo-guard.ts +51 -0
  29. package/examples/hooks/file-trigger.ts +40 -0
  30. package/examples/hooks/git-checkpoint.ts +52 -0
  31. package/examples/hooks/handoff.ts +149 -0
  32. package/examples/hooks/permission-gate.ts +33 -0
  33. package/examples/hooks/protected-paths.ts +29 -0
  34. package/examples/hooks/qna.ts +118 -0
  35. package/examples/hooks/status-line.ts +39 -0
  36. package/examples/sdk/01-minimal.ts +21 -0
  37. package/examples/sdk/02-custom-model.ts +49 -0
  38. package/examples/sdk/03-custom-prompt.ts +46 -0
  39. package/examples/sdk/04-skills.ts +43 -0
  40. package/examples/sdk/06-extensions.ts +82 -0
  41. package/examples/sdk/06-hooks.ts +61 -0
  42. package/examples/sdk/07-context-files.ts +35 -0
  43. package/examples/sdk/08-prompt-templates.ts +41 -0
  44. package/examples/sdk/08-slash-commands.ts +46 -0
  45. package/examples/sdk/09-api-keys-and-oauth.ts +54 -0
  46. package/examples/sdk/11-sessions.ts +47 -0
  47. package/examples/sdk/12-redis-sessions.ts +54 -0
  48. package/examples/sdk/13-sql-sessions.ts +61 -0
  49. package/examples/sdk/README.md +169 -0
  50. package/package.json +606 -0
  51. package/scripts/bench-guard.ts +71 -0
  52. package/scripts/bench-title-models.ts +332 -0
  53. package/scripts/build-binary.ts +117 -0
  54. package/scripts/bundle-dist.ts +133 -0
  55. package/scripts/compile-binary.ts +69 -0
  56. package/scripts/embed-mupdf-wasm.ts +67 -0
  57. package/scripts/format-prompts.ts +68 -0
  58. package/scripts/generate-aria-snapshot.ts +134 -0
  59. package/scripts/generate-docs-index.ts +58 -0
  60. package/scripts/generate-share-viewer.ts +34 -0
  61. package/scripts/legacy-pi-virtual-module.ts +195 -0
  62. package/scripts/measure-prompt-tokens.ts +63 -0
  63. package/scripts/omp +42 -0
  64. package/scripts/omp.ts +19 -0
  65. package/src/advisor/advise-tool.ts +216 -0
  66. package/src/advisor/config.ts +341 -0
  67. package/src/advisor/emission-guard.ts +172 -0
  68. package/src/advisor/index.ts +6 -0
  69. package/src/advisor/runtime.ts +1232 -0
  70. package/src/advisor/transcript-recorder.ts +159 -0
  71. package/src/advisor/watchdog.ts +135 -0
  72. package/src/async/index.ts +1 -0
  73. package/src/async/job-manager.ts +821 -0
  74. package/src/auto-thinking/classifier.ts +182 -0
  75. package/src/autolearn/controller.ts +152 -0
  76. package/src/autolearn/managed-skills.ts +255 -0
  77. package/src/autoresearch/command-resume.md +14 -0
  78. package/src/autoresearch/dashboard.ts +436 -0
  79. package/src/autoresearch/git.ts +331 -0
  80. package/src/autoresearch/helpers.ts +218 -0
  81. package/src/autoresearch/index.ts +541 -0
  82. package/src/autoresearch/prompt-setup.md +43 -0
  83. package/src/autoresearch/prompt.md +103 -0
  84. package/src/autoresearch/resume-message.md +10 -0
  85. package/src/autoresearch/state.ts +273 -0
  86. package/src/autoresearch/storage.ts +700 -0
  87. package/src/autoresearch/tools/init-experiment.ts +269 -0
  88. package/src/autoresearch/tools/log-experiment.ts +521 -0
  89. package/src/autoresearch/tools/run-experiment.ts +407 -0
  90. package/src/autoresearch/tools/update-notes.ts +109 -0
  91. package/src/autoresearch/types.ts +168 -0
  92. package/src/capability/context-file.ts +44 -0
  93. package/src/capability/extension-module.ts +34 -0
  94. package/src/capability/extension.ts +47 -0
  95. package/src/capability/fs.ts +117 -0
  96. package/src/capability/hook.ts +40 -0
  97. package/src/capability/index.ts +467 -0
  98. package/src/capability/instruction.ts +37 -0
  99. package/src/capability/mcp.ts +97 -0
  100. package/src/capability/prompt.ts +35 -0
  101. package/src/capability/rule-buckets.ts +66 -0
  102. package/src/capability/rule.ts +298 -0
  103. package/src/capability/settings.ts +34 -0
  104. package/src/capability/skill.ts +63 -0
  105. package/src/capability/slash-command.ts +40 -0
  106. package/src/capability/ssh.ts +41 -0
  107. package/src/capability/system-prompt.ts +34 -0
  108. package/src/capability/tool.ts +38 -0
  109. package/src/capability/types.ts +187 -0
  110. package/src/cleanse/agent.ts +226 -0
  111. package/src/cleanse/balance.ts +79 -0
  112. package/src/cleanse/checkers.ts +996 -0
  113. package/src/cleanse/index.ts +190 -0
  114. package/src/cleanse/loop.ts +51 -0
  115. package/src/cleanse/parsers.ts +726 -0
  116. package/src/cleanse/progress.ts +50 -0
  117. package/src/cleanse/prompts/assignment.md +47 -0
  118. package/src/cleanse/types.ts +72 -0
  119. package/src/cli/agents-cli.ts +138 -0
  120. package/src/cli/args.ts +420 -0
  121. package/src/cli/auth-broker-cli.ts +940 -0
  122. package/src/cli/auth-gateway-cli.ts +674 -0
  123. package/src/cli/bench-cli.ts +990 -0
  124. package/src/cli/classify-install-target.ts +76 -0
  125. package/src/cli/claude-trace-cli.ts +795 -0
  126. package/src/cli/commands/init-xdg.ts +27 -0
  127. package/src/cli/completion-gen.ts +550 -0
  128. package/src/cli/config-cli.ts +459 -0
  129. package/src/cli/dry-balance-cli.ts +864 -0
  130. package/src/cli/extension-flags.ts +48 -0
  131. package/src/cli/file-processor.ts +132 -0
  132. package/src/cli/flag-tables.ts +352 -0
  133. package/src/cli/gallery-cli.ts +272 -0
  134. package/src/cli/gallery-fixtures/agentic.ts +420 -0
  135. package/src/cli/gallery-fixtures/codeintel.ts +187 -0
  136. package/src/cli/gallery-fixtures/edit.ts +254 -0
  137. package/src/cli/gallery-fixtures/fs.ts +220 -0
  138. package/src/cli/gallery-fixtures/index.ts +40 -0
  139. package/src/cli/gallery-fixtures/interaction.ts +46 -0
  140. package/src/cli/gallery-fixtures/memory.ts +81 -0
  141. package/src/cli/gallery-fixtures/misc.ts +177 -0
  142. package/src/cli/gallery-fixtures/search.ts +135 -0
  143. package/src/cli/gallery-fixtures/shell.ts +241 -0
  144. package/src/cli/gallery-fixtures/types.ts +57 -0
  145. package/src/cli/gallery-fixtures/web.ts +158 -0
  146. package/src/cli/gallery-screenshot.ts +279 -0
  147. package/src/cli/gc-cli.ts +946 -0
  148. package/src/cli/grep-cli.ts +161 -0
  149. package/src/cli/grievances-cli.ts +256 -0
  150. package/src/cli/initial-message.ts +58 -0
  151. package/src/cli/models-cli.ts +388 -0
  152. package/src/cli/plugin-cli.ts +996 -0
  153. package/src/cli/profile-alias.ts +369 -0
  154. package/src/cli/profile-bootstrap.ts +233 -0
  155. package/src/cli/read-cli.ts +99 -0
  156. package/src/cli/session-picker.ts +93 -0
  157. package/src/cli/setup-cli.ts +320 -0
  158. package/src/cli/setup-model-picker.ts +43 -0
  159. package/src/cli/shell-cli.ts +176 -0
  160. package/src/cli/ssh-cli.ts +179 -0
  161. package/src/cli/startup-cwd.ts +58 -0
  162. package/src/cli/stats-cli.ts +229 -0
  163. package/src/cli/tiny-models-cli.ts +153 -0
  164. package/src/cli/ttsr-cli.ts +995 -0
  165. package/src/cli/update-cli.ts +1219 -0
  166. package/src/cli/usage-cli.ts +1084 -0
  167. package/src/cli/usage-error.ts +7 -0
  168. package/src/cli/web-search-cli.ts +144 -0
  169. package/src/cli/worktree-cli.ts +311 -0
  170. package/src/cli-commands.ts +166 -0
  171. package/src/cli.ts +407 -0
  172. package/src/collab/crypto.ts +63 -0
  173. package/src/collab/display-name.ts +13 -0
  174. package/src/collab/guest.ts +731 -0
  175. package/src/collab/host.ts +691 -0
  176. package/src/collab/protocol.ts +296 -0
  177. package/src/collab/relay-client.ts +282 -0
  178. package/src/collab/replication-shrink.ts +111 -0
  179. package/src/commands/acp.ts +33 -0
  180. package/src/commands/agents.ts +57 -0
  181. package/src/commands/auth-broker.ts +99 -0
  182. package/src/commands/auth-gateway.ts +69 -0
  183. package/src/commands/bench.ts +65 -0
  184. package/src/commands/cleanse.ts +45 -0
  185. package/src/commands/commit.ts +46 -0
  186. package/src/commands/complete.ts +66 -0
  187. package/src/commands/completions.ts +60 -0
  188. package/src/commands/config.ts +51 -0
  189. package/src/commands/dry-balance.ts +43 -0
  190. package/src/commands/gallery.ts +60 -0
  191. package/src/commands/gc.ts +46 -0
  192. package/src/commands/grep.ts +48 -0
  193. package/src/commands/grievances.ts +51 -0
  194. package/src/commands/install.ts +107 -0
  195. package/src/commands/join.ts +39 -0
  196. package/src/commands/launch.ts +212 -0
  197. package/src/commands/models.ts +60 -0
  198. package/src/commands/plugin.ts +78 -0
  199. package/src/commands/read.ts +38 -0
  200. package/src/commands/say.ts +145 -0
  201. package/src/commands/setup.ts +67 -0
  202. package/src/commands/shell.ts +29 -0
  203. package/src/commands/ssh.ts +60 -0
  204. package/src/commands/stats.ts +29 -0
  205. package/src/commands/tiny-models.ts +36 -0
  206. package/src/commands/token.ts +164 -0
  207. package/src/commands/ttsr.ts +125 -0
  208. package/src/commands/update.ts +33 -0
  209. package/src/commands/usage.ts +54 -0
  210. package/src/commands/web-search.ts +42 -0
  211. package/src/commands/worktree.ts +62 -0
  212. package/src/commit/agentic/agent.ts +322 -0
  213. package/src/commit/agentic/fallback.ts +96 -0
  214. package/src/commit/agentic/index.ts +382 -0
  215. package/src/commit/agentic/lock-files.ts +107 -0
  216. package/src/commit/agentic/prompts/analyze-file.md +22 -0
  217. package/src/commit/agentic/prompts/session-user.md +25 -0
  218. package/src/commit/agentic/prompts/split-confirm.md +1 -0
  219. package/src/commit/agentic/prompts/system.md +38 -0
  220. package/src/commit/agentic/state.ts +60 -0
  221. package/src/commit/agentic/tools/analyze-file.ts +148 -0
  222. package/src/commit/agentic/tools/git-file-diff.ts +191 -0
  223. package/src/commit/agentic/tools/git-hunk.ts +52 -0
  224. package/src/commit/agentic/tools/git-overview.ts +62 -0
  225. package/src/commit/agentic/tools/index.ts +54 -0
  226. package/src/commit/agentic/tools/propose-changelog.ts +147 -0
  227. package/src/commit/agentic/tools/propose-commit.ts +109 -0
  228. package/src/commit/agentic/tools/recent-commits.ts +81 -0
  229. package/src/commit/agentic/tools/schemas.ts +11 -0
  230. package/src/commit/agentic/tools/split-commit.ts +241 -0
  231. package/src/commit/agentic/topo-sort.ts +44 -0
  232. package/src/commit/agentic/trivial.ts +51 -0
  233. package/src/commit/agentic/validation.ts +183 -0
  234. package/src/commit/analysis/conventional.ts +64 -0
  235. package/src/commit/analysis/index.ts +4 -0
  236. package/src/commit/analysis/scope.ts +242 -0
  237. package/src/commit/analysis/summary.ts +107 -0
  238. package/src/commit/analysis/validation.ts +66 -0
  239. package/src/commit/changelog/detect.ts +40 -0
  240. package/src/commit/changelog/generate.ts +101 -0
  241. package/src/commit/changelog/index.ts +234 -0
  242. package/src/commit/changelog/parse.ts +44 -0
  243. package/src/commit/cli.ts +85 -0
  244. package/src/commit/git/diff.ts +148 -0
  245. package/src/commit/index.ts +5 -0
  246. package/src/commit/map-reduce/index.ts +69 -0
  247. package/src/commit/map-reduce/map-phase.ts +193 -0
  248. package/src/commit/map-reduce/reduce-phase.ts +49 -0
  249. package/src/commit/map-reduce/utils.ts +9 -0
  250. package/src/commit/message.ts +11 -0
  251. package/src/commit/model-selection.ts +95 -0
  252. package/src/commit/pipeline.ts +243 -0
  253. package/src/commit/prompts/analysis-system.md +148 -0
  254. package/src/commit/prompts/analysis-user.md +38 -0
  255. package/src/commit/prompts/changelog-system.md +50 -0
  256. package/src/commit/prompts/changelog-user.md +18 -0
  257. package/src/commit/prompts/file-observer-system.md +24 -0
  258. package/src/commit/prompts/file-observer-user.md +8 -0
  259. package/src/commit/prompts/reduce-system.md +50 -0
  260. package/src/commit/prompts/reduce-user.md +17 -0
  261. package/src/commit/prompts/summary-retry.md +3 -0
  262. package/src/commit/prompts/summary-system.md +38 -0
  263. package/src/commit/prompts/summary-user.md +13 -0
  264. package/src/commit/prompts/types-description.md +2 -0
  265. package/src/commit/shared-llm.ts +70 -0
  266. package/src/commit/types.ts +118 -0
  267. package/src/commit/utils/exclusions.ts +42 -0
  268. package/src/commit/utils.ts +58 -0
  269. package/src/config/api-key-resolver.ts +81 -0
  270. package/src/config/append-only-context-mode.ts +76 -0
  271. package/src/config/config-file.ts +347 -0
  272. package/src/config/file-lock.ts +164 -0
  273. package/src/config/inline-tool-descriptors-mode.ts +26 -0
  274. package/src/config/keybindings.ts +688 -0
  275. package/src/config/mcp-schema.json +247 -0
  276. package/src/config/model-discovery.ts +999 -0
  277. package/src/config/model-registry.ts +2725 -0
  278. package/src/config/model-resolver.ts +2068 -0
  279. package/src/config/model-roles.ts +113 -0
  280. package/src/config/models-config-schema-bundle.ts +313 -0
  281. package/src/config/models-config-schema.ts +14 -0
  282. package/src/config/models-config.ts +130 -0
  283. package/src/config/prompt-templates.ts +205 -0
  284. package/src/config/provider-globals.ts +25 -0
  285. package/src/config/resolve-config-value.ts +94 -0
  286. package/src/config/service-tier.ts +141 -0
  287. package/src/config/settings-schema.ts +5751 -0
  288. package/src/config/settings.ts +2369 -0
  289. package/src/config.ts +242 -0
  290. package/src/cursor.ts +513 -0
  291. package/src/dap/client.ts +1043 -0
  292. package/src/dap/config.ts +480 -0
  293. package/src/dap/defaults.json +212 -0
  294. package/src/dap/index.ts +4 -0
  295. package/src/dap/session.ts +1841 -0
  296. package/src/dap/types.ts +611 -0
  297. package/src/debug/index.ts +584 -0
  298. package/src/debug/log-formatting.ts +58 -0
  299. package/src/debug/log-viewer.ts +966 -0
  300. package/src/debug/profiler.ts +168 -0
  301. package/src/debug/protocol-probe.ts +267 -0
  302. package/src/debug/raw-sse-buffer.ts +421 -0
  303. package/src/debug/raw-sse.ts +312 -0
  304. package/src/debug/remote-debugger.ts +151 -0
  305. package/src/debug/report-bundle.ts +411 -0
  306. package/src/debug/system-info.ts +111 -0
  307. package/src/debug/terminal-info.ts +124 -0
  308. package/src/discovery/agents-md.ts +67 -0
  309. package/src/discovery/agents.ts +230 -0
  310. package/src/discovery/at-imports.ts +273 -0
  311. package/src/discovery/builtin-defaults.ts +39 -0
  312. package/src/discovery/builtin-rules/go-add-cleanup.md +33 -0
  313. package/src/discovery/builtin-rules/go-bench-loop.md +36 -0
  314. package/src/discovery/builtin-rules/go-exp-promoted.md +40 -0
  315. package/src/discovery/builtin-rules/go-ioutil.md +37 -0
  316. package/src/discovery/builtin-rules/go-join-hostport.md +30 -0
  317. package/src/discovery/builtin-rules/go-new-expr.md +44 -0
  318. package/src/discovery/builtin-rules/go-rand-v2.md +41 -0
  319. package/src/discovery/builtin-rules/go-range-int.md +45 -0
  320. package/src/discovery/builtin-rules/index.ts +74 -0
  321. package/src/discovery/builtin-rules/rs-box-leak.md +49 -0
  322. package/src/discovery/builtin-rules/rs-future-prelude.md +24 -0
  323. package/src/discovery/builtin-rules/rs-lazylock.md +52 -0
  324. package/src/discovery/builtin-rules/rs-match-ergonomics.md +68 -0
  325. package/src/discovery/builtin-rules/rs-parking-lot.md +45 -0
  326. package/src/discovery/builtin-rules/rs-result-type.md +20 -0
  327. package/src/discovery/builtin-rules/ts-bare-catch.md +39 -0
  328. package/src/discovery/builtin-rules/ts-import-type.md +43 -0
  329. package/src/discovery/builtin-rules/ts-no-any.md +66 -0
  330. package/src/discovery/builtin-rules/ts-no-deprecated-leftovers.md +45 -0
  331. package/src/discovery/builtin-rules/ts-no-dynamic-import.md +40 -0
  332. package/src/discovery/builtin-rules/ts-no-inline-cast-access.md +55 -0
  333. package/src/discovery/builtin-rules/ts-no-local-is-record.md +48 -0
  334. package/src/discovery/builtin-rules/ts-no-return-type.md +45 -0
  335. package/src/discovery/builtin-rules/ts-no-test-timers.md +55 -0
  336. package/src/discovery/builtin-rules/ts-no-tiny-functions.md +51 -0
  337. package/src/discovery/builtin-rules/ts-promise-with-resolvers.md +66 -0
  338. package/src/discovery/builtin-rules/ts-redundant-clear-guard.md +75 -0
  339. package/src/discovery/builtin-rules/ts-set-map.md +28 -0
  340. package/src/discovery/builtin.ts +935 -0
  341. package/src/discovery/claude-plugins.ts +624 -0
  342. package/src/discovery/claude.ts +588 -0
  343. package/src/discovery/cline.ts +83 -0
  344. package/src/discovery/codex.ts +540 -0
  345. package/src/discovery/cursor.ts +220 -0
  346. package/src/discovery/gemini.ts +383 -0
  347. package/src/discovery/github.ts +337 -0
  348. package/src/discovery/helpers.ts +1159 -0
  349. package/src/discovery/index.ts +81 -0
  350. package/src/discovery/mcp-json.ts +172 -0
  351. package/src/discovery/omp-extension-roots.ts +213 -0
  352. package/src/discovery/omp-plugins.ts +387 -0
  353. package/src/discovery/opencode.ts +441 -0
  354. package/src/discovery/plugin-dir-roots.ts +28 -0
  355. package/src/discovery/ssh.ts +153 -0
  356. package/src/discovery/substitute-plugin-root.ts +77 -0
  357. package/src/discovery/vscode.ts +105 -0
  358. package/src/discovery/windsurf.ts +147 -0
  359. package/src/edit/apply-patch/index.ts +87 -0
  360. package/src/edit/apply-patch/parser.ts +174 -0
  361. package/src/edit/diff.ts +999 -0
  362. package/src/edit/file-snapshot-store.ts +148 -0
  363. package/src/edit/hashline/block-resolver.ts +33 -0
  364. package/src/edit/hashline/diff.ts +356 -0
  365. package/src/edit/hashline/execute.ts +280 -0
  366. package/src/edit/hashline/filesystem.ts +223 -0
  367. package/src/edit/hashline/index.ts +5 -0
  368. package/src/edit/hashline/noop-loop-guard.ts +99 -0
  369. package/src/edit/hashline/params.ts +12 -0
  370. package/src/edit/index.ts +676 -0
  371. package/src/edit/modes/apply-patch.lark +19 -0
  372. package/src/edit/modes/apply-patch.ts +53 -0
  373. package/src/edit/modes/patch.ts +1958 -0
  374. package/src/edit/modes/replace.ts +1137 -0
  375. package/src/edit/normalize.ts +345 -0
  376. package/src/edit/notebook.ts +242 -0
  377. package/src/edit/read-file.ts +25 -0
  378. package/src/edit/renderer.ts +998 -0
  379. package/src/edit/snapshot-details.ts +77 -0
  380. package/src/edit/streaming.ts +698 -0
  381. package/src/eval/agent-bridge.ts +224 -0
  382. package/src/eval/backend-helpers.ts +48 -0
  383. package/src/eval/backend.ts +71 -0
  384. package/src/eval/bridge-timeout.ts +62 -0
  385. package/src/eval/budget-bridge.ts +48 -0
  386. package/src/eval/completion-bridge.ts +211 -0
  387. package/src/eval/concurrency-bridge.ts +34 -0
  388. package/src/eval/executor-base.ts +504 -0
  389. package/src/eval/idle-timeout.ts +91 -0
  390. package/src/eval/index.ts +6 -0
  391. package/src/eval/jl/executor.ts +540 -0
  392. package/src/eval/jl/index.ts +54 -0
  393. package/src/eval/jl/kernel.ts +236 -0
  394. package/src/eval/jl/prelude.jl +736 -0
  395. package/src/eval/jl/prelude.ts +3 -0
  396. package/src/eval/jl/runner.jl +666 -0
  397. package/src/eval/jl/runtime.ts +118 -0
  398. package/src/eval/js/context-manager.ts +701 -0
  399. package/src/eval/js/executor.ts +175 -0
  400. package/src/eval/js/index.ts +40 -0
  401. package/src/eval/js/process-entry.ts +31 -0
  402. package/src/eval/js/shared/helpers.ts +170 -0
  403. package/src/eval/js/shared/indirect-eval.ts +30 -0
  404. package/src/eval/js/shared/local-module-loader.ts +364 -0
  405. package/src/eval/js/shared/prelude.ts +2 -0
  406. package/src/eval/js/shared/prelude.txt +294 -0
  407. package/src/eval/js/shared/rewrite-imports.ts +550 -0
  408. package/src/eval/js/shared/runtime.ts +590 -0
  409. package/src/eval/js/shared/types.ts +18 -0
  410. package/src/eval/js/tool-bridge.ts +163 -0
  411. package/src/eval/js/worker-core.ts +380 -0
  412. package/src/eval/js/worker-entry.ts +37 -0
  413. package/src/eval/js/worker-protocol.ts +47 -0
  414. package/src/eval/kernel-base.ts +569 -0
  415. package/src/eval/py/display.ts +71 -0
  416. package/src/eval/py/executor.ts +603 -0
  417. package/src/eval/py/index.ts +57 -0
  418. package/src/eval/py/kernel.ts +234 -0
  419. package/src/eval/py/prelude.py +672 -0
  420. package/src/eval/py/prelude.ts +3 -0
  421. package/src/eval/py/runner.py +1388 -0
  422. package/src/eval/py/runtime.ts +276 -0
  423. package/src/eval/py/spawn-options.ts +139 -0
  424. package/src/eval/py/tool-bridge.ts +181 -0
  425. package/src/eval/rb/executor.ts +504 -0
  426. package/src/eval/rb/index.ts +54 -0
  427. package/src/eval/rb/kernel.ts +231 -0
  428. package/src/eval/rb/prelude.rb +552 -0
  429. package/src/eval/rb/prelude.ts +3 -0
  430. package/src/eval/rb/runner.rb +581 -0
  431. package/src/eval/rb/runtime.ts +132 -0
  432. package/src/eval/runtime-env.ts +104 -0
  433. package/src/eval/session-id.ts +8 -0
  434. package/src/eval/types.ts +48 -0
  435. package/src/exa/index.ts +2 -0
  436. package/src/exa/mcp-client.ts +370 -0
  437. package/src/exa/types.ts +69 -0
  438. package/src/exec/bash-executor.ts +627 -0
  439. package/src/exec/direnv.ts +145 -0
  440. package/src/exec/exec.ts +53 -0
  441. package/src/exec/non-interactive-env.ts +118 -0
  442. package/src/export/custom-share.ts +65 -0
  443. package/src/export/html/args.ts +20 -0
  444. package/src/export/html/index.ts +313 -0
  445. package/src/export/html/share-loader.js +102 -0
  446. package/src/export/html/template.css +1355 -0
  447. package/src/export/html/template.html +55 -0
  448. package/src/export/html/template.js +1671 -0
  449. package/src/export/html/tool-views.generated.js +35 -0
  450. package/src/export/html/vendor/highlight.min.js +1213 -0
  451. package/src/export/html/vendor/marked.min.js +6 -0
  452. package/src/export/html/web-palette.ts +126 -0
  453. package/src/export/share.ts +682 -0
  454. package/src/export/ttsr.ts +590 -0
  455. package/src/extensibility/custom-commands/bundled/ci-green/index.ts +54 -0
  456. package/src/extensibility/custom-commands/bundled/review/index.ts +698 -0
  457. package/src/extensibility/custom-commands/index.ts +2 -0
  458. package/src/extensibility/custom-commands/loader.ts +242 -0
  459. package/src/extensibility/custom-commands/types.ts +119 -0
  460. package/src/extensibility/custom-tools/index.ts +7 -0
  461. package/src/extensibility/custom-tools/loader.ts +301 -0
  462. package/src/extensibility/custom-tools/types.ts +286 -0
  463. package/src/extensibility/custom-tools/wrapper.ts +50 -0
  464. package/src/extensibility/extensions/compact-handler.ts +40 -0
  465. package/src/extensibility/extensions/get-commands-handler.ts +78 -0
  466. package/src/extensibility/extensions/index.ts +16 -0
  467. package/src/extensibility/extensions/load-errors.ts +13 -0
  468. package/src/extensibility/extensions/loader.ts +624 -0
  469. package/src/extensibility/extensions/managed-timers.ts +83 -0
  470. package/src/extensibility/extensions/model-api.ts +39 -0
  471. package/src/extensibility/extensions/runner.ts +1209 -0
  472. package/src/extensibility/extensions/types.ts +1526 -0
  473. package/src/extensibility/extensions/wrapper.ts +377 -0
  474. package/src/extensibility/hooks/index.ts +6 -0
  475. package/src/extensibility/hooks/loader.ts +244 -0
  476. package/src/extensibility/hooks/runner.ts +425 -0
  477. package/src/extensibility/hooks/tool-wrapper.ts +124 -0
  478. package/src/extensibility/hooks/types.ts +612 -0
  479. package/src/extensibility/legacy-pi-ai-shim.ts +140 -0
  480. package/src/extensibility/legacy-pi-coding-agent-shim.ts +1384 -0
  481. package/src/extensibility/legacy-pi-tui-shim.ts +43 -0
  482. package/src/extensibility/plugins/bun-git-cache.ts +91 -0
  483. package/src/extensibility/plugins/doctor.ts +65 -0
  484. package/src/extensibility/plugins/git-url.ts +367 -0
  485. package/src/extensibility/plugins/index.ts +9 -0
  486. package/src/extensibility/plugins/installer.ts +201 -0
  487. package/src/extensibility/plugins/legacy-pi-compat.ts +2532 -0
  488. package/src/extensibility/plugins/legacy-pi-virtual-modules.d.ts +4 -0
  489. package/src/extensibility/plugins/loader.ts +535 -0
  490. package/src/extensibility/plugins/manager.ts +1142 -0
  491. package/src/extensibility/plugins/marketplace/cache.ts +136 -0
  492. package/src/extensibility/plugins/marketplace/fetcher.ts +316 -0
  493. package/src/extensibility/plugins/marketplace/index.ts +6 -0
  494. package/src/extensibility/plugins/marketplace/manager.ts +926 -0
  495. package/src/extensibility/plugins/marketplace/registry.ts +196 -0
  496. package/src/extensibility/plugins/marketplace/source-resolver.ts +147 -0
  497. package/src/extensibility/plugins/marketplace/types.ts +192 -0
  498. package/src/extensibility/plugins/marketplace-auto-update.ts +49 -0
  499. package/src/extensibility/plugins/parser.ts +107 -0
  500. package/src/extensibility/plugins/runtime-config.ts +9 -0
  501. package/src/extensibility/plugins/types.ts +194 -0
  502. package/src/extensibility/session-handler-types.ts +21 -0
  503. package/src/extensibility/shared-events.ts +401 -0
  504. package/src/extensibility/skills.ts +511 -0
  505. package/src/extensibility/slash-commands.ts +131 -0
  506. package/src/extensibility/tool-event-input.ts +80 -0
  507. package/src/extensibility/tool-proxy.ts +28 -0
  508. package/src/extensibility/typebox.ts +958 -0
  509. package/src/extensibility/utils.ts +184 -0
  510. package/src/goals/index.ts +3 -0
  511. package/src/goals/runtime.ts +521 -0
  512. package/src/goals/state.ts +37 -0
  513. package/src/goals/tools/goal-tool.ts +251 -0
  514. package/src/hindsight/backend.ts +354 -0
  515. package/src/hindsight/bank.ts +156 -0
  516. package/src/hindsight/client.ts +680 -0
  517. package/src/hindsight/config.ts +193 -0
  518. package/src/hindsight/content.ts +266 -0
  519. package/src/hindsight/index.ts +8 -0
  520. package/src/hindsight/mental-models.ts +429 -0
  521. package/src/hindsight/seeds.json +32 -0
  522. package/src/hindsight/state.ts +550 -0
  523. package/src/hindsight/transcript.ts +71 -0
  524. package/src/index.ts +66 -0
  525. package/src/internal-urls/agent-protocol.ts +180 -0
  526. package/src/internal-urls/artifact-protocol.ts +151 -0
  527. package/src/internal-urls/docs-index.ts +102 -0
  528. package/src/internal-urls/filesystem-resource.ts +34 -0
  529. package/src/internal-urls/history-protocol.ts +198 -0
  530. package/src/internal-urls/index.ts +27 -0
  531. package/src/internal-urls/issue-pr-protocol.ts +594 -0
  532. package/src/internal-urls/json-query.ts +126 -0
  533. package/src/internal-urls/local-protocol.ts +470 -0
  534. package/src/internal-urls/mcp-protocol.ts +165 -0
  535. package/src/internal-urls/memory-protocol.ts +347 -0
  536. package/src/internal-urls/omp-protocol.ts +94 -0
  537. package/src/internal-urls/parse.ts +103 -0
  538. package/src/internal-urls/registry-helpers.ts +132 -0
  539. package/src/internal-urls/router.ts +150 -0
  540. package/src/internal-urls/rule-protocol.ts +45 -0
  541. package/src/internal-urls/skill-protocol.ts +117 -0
  542. package/src/internal-urls/ssh-protocol.ts +368 -0
  543. package/src/internal-urls/types.ts +196 -0
  544. package/src/internal-urls/vault-protocol.ts +940 -0
  545. package/src/internal-urls/xd-protocol.ts +46 -0
  546. package/src/irc/bus.ts +380 -0
  547. package/src/jsonrpc/message-framing.ts +142 -0
  548. package/src/launch/broker.ts +1134 -0
  549. package/src/launch/client.ts +350 -0
  550. package/src/launch/paths.ts +17 -0
  551. package/src/launch/presence.ts +82 -0
  552. package/src/launch/protocol.ts +396 -0
  553. package/src/launch/spawn-options.ts +17 -0
  554. package/src/launch/terminal-output.ts +46 -0
  555. package/src/lib/xai-http.ts +150 -0
  556. package/src/live/attestation.ts +91 -0
  557. package/src/live/controller.ts +517 -0
  558. package/src/live/prompts/agent-final-message.md +3 -0
  559. package/src/live/prompts/live-instructions.md +23 -0
  560. package/src/live/protocol.ts +233 -0
  561. package/src/live/transport.ts +422 -0
  562. package/src/live/visualizer.ts +214 -0
  563. package/src/live/voices.ts +18 -0
  564. package/src/lsp/client.ts +1429 -0
  565. package/src/lsp/clients/biome-client.ts +263 -0
  566. package/src/lsp/clients/index.ts +50 -0
  567. package/src/lsp/clients/lsp-linter-client.ts +85 -0
  568. package/src/lsp/clients/swiftlint-client.ts +120 -0
  569. package/src/lsp/config.ts +549 -0
  570. package/src/lsp/defaults.json +499 -0
  571. package/src/lsp/deferred-diagnostics.ts +66 -0
  572. package/src/lsp/diagnostics-ledger.ts +51 -0
  573. package/src/lsp/edits.ts +288 -0
  574. package/src/lsp/format-options.ts +119 -0
  575. package/src/lsp/index.ts +2766 -0
  576. package/src/lsp/lspmux.ts +233 -0
  577. package/src/lsp/render.ts +668 -0
  578. package/src/lsp/startup-events.ts +13 -0
  579. package/src/lsp/types.ts +453 -0
  580. package/src/lsp/utils.ts +718 -0
  581. package/src/main.ts +1647 -0
  582. package/src/markit/NOTICE +32 -0
  583. package/src/markit/converters/docx.ts +56 -0
  584. package/src/markit/converters/epub.ts +136 -0
  585. package/src/markit/converters/mammoth.d.ts +24 -0
  586. package/src/markit/converters/pdf/columns.ts +103 -0
  587. package/src/markit/converters/pdf/extract.ts +598 -0
  588. package/src/markit/converters/pdf/grid.ts +780 -0
  589. package/src/markit/converters/pdf/headers.ts +106 -0
  590. package/src/markit/converters/pdf/index.ts +146 -0
  591. package/src/markit/converters/pdf/render.ts +501 -0
  592. package/src/markit/converters/pdf/types.ts +84 -0
  593. package/src/markit/converters/pptx.ts +325 -0
  594. package/src/markit/converters/xlsx.ts +173 -0
  595. package/src/markit/index.ts +2 -0
  596. package/src/markit/registry.ts +59 -0
  597. package/src/markit/types.ts +35 -0
  598. package/src/mcp/client.ts +511 -0
  599. package/src/mcp/config-writer.ts +377 -0
  600. package/src/mcp/config.ts +381 -0
  601. package/src/mcp/index.ts +29 -0
  602. package/src/mcp/json-rpc.ts +122 -0
  603. package/src/mcp/loader.ts +125 -0
  604. package/src/mcp/manager.ts +1419 -0
  605. package/src/mcp/oauth-credentials.ts +104 -0
  606. package/src/mcp/oauth-discovery.ts +587 -0
  607. package/src/mcp/oauth-flow.ts +820 -0
  608. package/src/mcp/render.ts +214 -0
  609. package/src/mcp/smithery-auth.ts +108 -0
  610. package/src/mcp/smithery-connect.ts +154 -0
  611. package/src/mcp/smithery-registry.ts +500 -0
  612. package/src/mcp/startup-events.ts +116 -0
  613. package/src/mcp/timeout.ts +59 -0
  614. package/src/mcp/tool-bridge.ts +691 -0
  615. package/src/mcp/tool-cache.ts +117 -0
  616. package/src/mcp/transports/http.ts +523 -0
  617. package/src/mcp/transports/index.ts +7 -0
  618. package/src/mcp/transports/sse.ts +377 -0
  619. package/src/mcp/transports/stdio.ts +903 -0
  620. package/src/mcp/types.ts +437 -0
  621. package/src/memories/index.ts +1434 -0
  622. package/src/memories/storage.ts +578 -0
  623. package/src/memory-backend/index.ts +18 -0
  624. package/src/memory-backend/local-backend.ts +47 -0
  625. package/src/memory-backend/off-backend.ts +25 -0
  626. package/src/memory-backend/resolve.ts +25 -0
  627. package/src/memory-backend/runtime.ts +66 -0
  628. package/src/memory-backend/tool-names.ts +2 -0
  629. package/src/memory-backend/types.ts +166 -0
  630. package/src/mnemopi/backend.ts +629 -0
  631. package/src/mnemopi/config.ts +267 -0
  632. package/src/mnemopi/embed-client.ts +246 -0
  633. package/src/mnemopi/embed-protocol.ts +35 -0
  634. package/src/mnemopi/embed-worker.ts +114 -0
  635. package/src/mnemopi/index.ts +3 -0
  636. package/src/mnemopi/state.ts +906 -0
  637. package/src/modes/acp/acp-agent.ts +2577 -0
  638. package/src/modes/acp/acp-client-bridge.ts +154 -0
  639. package/src/modes/acp/acp-event-mapper.ts +1084 -0
  640. package/src/modes/acp/acp-mode.ts +48 -0
  641. package/src/modes/acp/index.ts +2 -0
  642. package/src/modes/acp/terminal-auth.ts +37 -0
  643. package/src/modes/components/advisor-config.ts +635 -0
  644. package/src/modes/components/advisor-message.ts +109 -0
  645. package/src/modes/components/agent-dashboard.ts +1252 -0
  646. package/src/modes/components/agent-hub.ts +648 -0
  647. package/src/modes/components/agent-transcript-viewer.ts +649 -0
  648. package/src/modes/components/ask-dialog.ts +975 -0
  649. package/src/modes/components/assistant-message.ts +972 -0
  650. package/src/modes/components/background-tan-message.ts +36 -0
  651. package/src/modes/components/bash-execution.ts +233 -0
  652. package/src/modes/components/bordered-loader.ts +41 -0
  653. package/src/modes/components/btw-panel.ts +124 -0
  654. package/src/modes/components/cache-invalidation-marker.ts +110 -0
  655. package/src/modes/components/chat-block.ts +111 -0
  656. package/src/modes/components/chat-transcript-builder.ts +495 -0
  657. package/src/modes/components/collab-prompt-message.ts +32 -0
  658. package/src/modes/components/compaction-summary-message.ts +221 -0
  659. package/src/modes/components/copy-selector.ts +218 -0
  660. package/src/modes/components/countdown-timer.ts +75 -0
  661. package/src/modes/components/custom-editor.ts +1027 -0
  662. package/src/modes/components/custom-message.ts +70 -0
  663. package/src/modes/components/diff.ts +254 -0
  664. package/src/modes/components/dynamic-border.ts +37 -0
  665. package/src/modes/components/error-banner.ts +33 -0
  666. package/src/modes/components/eval-execution.ts +169 -0
  667. package/src/modes/components/execution-shared.ts +101 -0
  668. package/src/modes/components/extensions/extension-dashboard.ts +492 -0
  669. package/src/modes/components/extensions/extension-list.ts +507 -0
  670. package/src/modes/components/extensions/index.ts +9 -0
  671. package/src/modes/components/extensions/inspector-panel.ts +321 -0
  672. package/src/modes/components/extensions/state-manager.ts +648 -0
  673. package/src/modes/components/extensions/types.ts +186 -0
  674. package/src/modes/components/footer.ts +276 -0
  675. package/src/modes/components/history-search.ts +268 -0
  676. package/src/modes/components/hook-editor.ts +213 -0
  677. package/src/modes/components/hook-input.ts +87 -0
  678. package/src/modes/components/hook-message.ts +67 -0
  679. package/src/modes/components/hook-selector.ts +691 -0
  680. package/src/modes/components/index.ts +42 -0
  681. package/src/modes/components/keybinding-hints.ts +65 -0
  682. package/src/modes/components/late-diagnostics-message.ts +60 -0
  683. package/src/modes/components/login-dialog.ts +197 -0
  684. package/src/modes/components/logout-account-selector.ts +130 -0
  685. package/src/modes/components/mcp-add-wizard.ts +1413 -0
  686. package/src/modes/components/message-frame.ts +98 -0
  687. package/src/modes/components/model-browser.ts +872 -0
  688. package/src/modes/components/model-hub.ts +2011 -0
  689. package/src/modes/components/model-picker.ts +233 -0
  690. package/src/modes/components/move-overlay.ts +293 -0
  691. package/src/modes/components/oauth-selector.ts +474 -0
  692. package/src/modes/components/omfg-panel.ts +141 -0
  693. package/src/modes/components/overlay-box.ts +109 -0
  694. package/src/modes/components/pause-screen.ts +208 -0
  695. package/src/modes/components/plan-review-overlay.ts +1226 -0
  696. package/src/modes/components/plan-toc.ts +138 -0
  697. package/src/modes/components/plugin-selector.ts +100 -0
  698. package/src/modes/components/plugin-settings.ts +745 -0
  699. package/src/modes/components/queue-mode-selector.ts +61 -0
  700. package/src/modes/components/read-tool-group.ts +678 -0
  701. package/src/modes/components/reset-usage-selector.ts +161 -0
  702. package/src/modes/components/segment-track.ts +89 -0
  703. package/src/modes/components/select-list-mouse-routing.ts +35 -0
  704. package/src/modes/components/selector-helpers.ts +129 -0
  705. package/src/modes/components/session-account-selector.ts +62 -0
  706. package/src/modes/components/session-selector.ts +1019 -0
  707. package/src/modes/components/settings-defs.ts +267 -0
  708. package/src/modes/components/settings-selector.ts +1445 -0
  709. package/src/modes/components/show-images-selector.ts +50 -0
  710. package/src/modes/components/skill-message.ts +110 -0
  711. package/src/modes/components/snapcompact-shape-preview-doc.md +14 -0
  712. package/src/modes/components/snapcompact-shape-preview.ts +192 -0
  713. package/src/modes/components/status-line/component.ts +1516 -0
  714. package/src/modes/components/status-line/context-thresholds.ts +86 -0
  715. package/src/modes/components/status-line/git-utils.ts +42 -0
  716. package/src/modes/components/status-line/index.ts +5 -0
  717. package/src/modes/components/status-line/presets.ts +106 -0
  718. package/src/modes/components/status-line/segments.ts +710 -0
  719. package/src/modes/components/status-line/separators.ts +55 -0
  720. package/src/modes/components/status-line/types.ts +159 -0
  721. package/src/modes/components/theme-selector.ts +68 -0
  722. package/src/modes/components/thinking-selector.ts +57 -0
  723. package/src/modes/components/tiny-title-download-progress.ts +90 -0
  724. package/src/modes/components/tips.txt +26 -0
  725. package/src/modes/components/todo-reminder.ts +43 -0
  726. package/src/modes/components/tool-execution.ts +1356 -0
  727. package/src/modes/components/transcript-container.ts +527 -0
  728. package/src/modes/components/tree-selector.ts +1008 -0
  729. package/src/modes/components/ttsr-notification.ts +123 -0
  730. package/src/modes/components/usage-row.ts +47 -0
  731. package/src/modes/components/user-message-selector.ts +227 -0
  732. package/src/modes/components/user-message.ts +167 -0
  733. package/src/modes/components/visual-truncate.ts +63 -0
  734. package/src/modes/components/welcome.ts +578 -0
  735. package/src/modes/controllers/btw-controller.ts +198 -0
  736. package/src/modes/controllers/command-controller-shared.ts +109 -0
  737. package/src/modes/controllers/command-controller.ts +1993 -0
  738. package/src/modes/controllers/event-controller.ts +1781 -0
  739. package/src/modes/controllers/extension-ui-controller.ts +1242 -0
  740. package/src/modes/controllers/input-controller.ts +2004 -0
  741. package/src/modes/controllers/live-command-controller.ts +260 -0
  742. package/src/modes/controllers/mcp-command-controller.ts +2403 -0
  743. package/src/modes/controllers/omfg-controller.ts +287 -0
  744. package/src/modes/controllers/omfg-rule.ts +647 -0
  745. package/src/modes/controllers/selector-controller.ts +1968 -0
  746. package/src/modes/controllers/session-focus-controller.ts +117 -0
  747. package/src/modes/controllers/ssh-command-controller.ts +385 -0
  748. package/src/modes/controllers/streaming-reveal.ts +399 -0
  749. package/src/modes/controllers/tan-command-controller.ts +229 -0
  750. package/src/modes/controllers/todo-command-controller.ts +487 -0
  751. package/src/modes/controllers/tool-args-reveal.ts +591 -0
  752. package/src/modes/data/emojis.json +1 -0
  753. package/src/modes/emoji-autocomplete.ts +285 -0
  754. package/src/modes/github-ref-autocomplete.ts +75 -0
  755. package/src/modes/gradient-highlight.ts +99 -0
  756. package/src/modes/image-references.ts +137 -0
  757. package/src/modes/index.ts +10 -0
  758. package/src/modes/interactive-mode.ts +4957 -0
  759. package/src/modes/internal-url-autocomplete.ts +158 -0
  760. package/src/modes/loop-limit.ts +192 -0
  761. package/src/modes/magic-keyword-boundary.ts +23 -0
  762. package/src/modes/magic-keywords.ts +42 -0
  763. package/src/modes/markdown-prose.ts +247 -0
  764. package/src/modes/oauth-manual-input.ts +69 -0
  765. package/src/modes/orchestrate.ts +43 -0
  766. package/src/modes/print-mode.ts +265 -0
  767. package/src/modes/prompt-action-autocomplete.ts +322 -0
  768. package/src/modes/queue-input.ts +132 -0
  769. package/src/modes/rpc/host-tools.ts +204 -0
  770. package/src/modes/rpc/host-uris.ts +235 -0
  771. package/src/modes/rpc/rpc-client.ts +1195 -0
  772. package/src/modes/rpc/rpc-frame.ts +316 -0
  773. package/src/modes/rpc/rpc-input.ts +38 -0
  774. package/src/modes/rpc/rpc-messages.ts +127 -0
  775. package/src/modes/rpc/rpc-mode.ts +1504 -0
  776. package/src/modes/rpc/rpc-subagents.ts +265 -0
  777. package/src/modes/rpc/rpc-types.ts +533 -0
  778. package/src/modes/running-subagent-badge.ts +13 -0
  779. package/src/modes/runtime-init.ts +144 -0
  780. package/src/modes/session-observer-registry.ts +218 -0
  781. package/src/modes/session-teardown.ts +82 -0
  782. package/src/modes/setup-version.ts +11 -0
  783. package/src/modes/setup-wizard/index.ts +103 -0
  784. package/src/modes/setup-wizard/lazy.ts +16 -0
  785. package/src/modes/setup-wizard/scenes/glyph.ts +103 -0
  786. package/src/modes/setup-wizard/scenes/model.ts +132 -0
  787. package/src/modes/setup-wizard/scenes/outro.ts +35 -0
  788. package/src/modes/setup-wizard/scenes/providers.ts +105 -0
  789. package/src/modes/setup-wizard/scenes/sign-in.ts +312 -0
  790. package/src/modes/setup-wizard/scenes/splash.ts +201 -0
  791. package/src/modes/setup-wizard/scenes/theme.ts +330 -0
  792. package/src/modes/setup-wizard/scenes/types.ts +65 -0
  793. package/src/modes/setup-wizard/scenes/web-search.ts +153 -0
  794. package/src/modes/setup-wizard/startup-splash.ts +107 -0
  795. package/src/modes/setup-wizard/wizard-overlay.ts +335 -0
  796. package/src/modes/shared.ts +49 -0
  797. package/src/modes/skill-command.ts +91 -0
  798. package/src/modes/theme/dark.json +95 -0
  799. package/src/modes/theme/defaults/alabaster.json +93 -0
  800. package/src/modes/theme/defaults/amethyst.json +96 -0
  801. package/src/modes/theme/defaults/anthracite.json +93 -0
  802. package/src/modes/theme/defaults/basalt.json +91 -0
  803. package/src/modes/theme/defaults/birch.json +95 -0
  804. package/src/modes/theme/defaults/dark-abyss.json +91 -0
  805. package/src/modes/theme/defaults/dark-arctic.json +104 -0
  806. package/src/modes/theme/defaults/dark-aurora.json +95 -0
  807. package/src/modes/theme/defaults/dark-catppuccin.json +107 -0
  808. package/src/modes/theme/defaults/dark-cavern.json +91 -0
  809. package/src/modes/theme/defaults/dark-copper.json +95 -0
  810. package/src/modes/theme/defaults/dark-cosmos.json +90 -0
  811. package/src/modes/theme/defaults/dark-cyberpunk.json +102 -0
  812. package/src/modes/theme/defaults/dark-dracula.json +98 -0
  813. package/src/modes/theme/defaults/dark-eclipse.json +91 -0
  814. package/src/modes/theme/defaults/dark-ember.json +95 -0
  815. package/src/modes/theme/defaults/dark-equinox.json +90 -0
  816. package/src/modes/theme/defaults/dark-forest.json +96 -0
  817. package/src/modes/theme/defaults/dark-github.json +105 -0
  818. package/src/modes/theme/defaults/dark-gruvbox.json +112 -0
  819. package/src/modes/theme/defaults/dark-lavender.json +95 -0
  820. package/src/modes/theme/defaults/dark-lunar.json +89 -0
  821. package/src/modes/theme/defaults/dark-midnight.json +95 -0
  822. package/src/modes/theme/defaults/dark-monochrome.json +94 -0
  823. package/src/modes/theme/defaults/dark-monokai.json +98 -0
  824. package/src/modes/theme/defaults/dark-nebula.json +90 -0
  825. package/src/modes/theme/defaults/dark-nord.json +97 -0
  826. package/src/modes/theme/defaults/dark-ocean.json +101 -0
  827. package/src/modes/theme/defaults/dark-one.json +100 -0
  828. package/src/modes/theme/defaults/dark-poimandres.json +143 -0
  829. package/src/modes/theme/defaults/dark-rainforest.json +91 -0
  830. package/src/modes/theme/defaults/dark-reef.json +91 -0
  831. package/src/modes/theme/defaults/dark-retro.json +92 -0
  832. package/src/modes/theme/defaults/dark-rose-pine.json +96 -0
  833. package/src/modes/theme/defaults/dark-sakura.json +95 -0
  834. package/src/modes/theme/defaults/dark-slate.json +95 -0
  835. package/src/modes/theme/defaults/dark-solarized.json +97 -0
  836. package/src/modes/theme/defaults/dark-solstice.json +90 -0
  837. package/src/modes/theme/defaults/dark-starfall.json +91 -0
  838. package/src/modes/theme/defaults/dark-sunset.json +99 -0
  839. package/src/modes/theme/defaults/dark-swamp.json +90 -0
  840. package/src/modes/theme/defaults/dark-synthwave.json +103 -0
  841. package/src/modes/theme/defaults/dark-taiga.json +91 -0
  842. package/src/modes/theme/defaults/dark-terminal.json +95 -0
  843. package/src/modes/theme/defaults/dark-tokyo-night.json +101 -0
  844. package/src/modes/theme/defaults/dark-tundra.json +91 -0
  845. package/src/modes/theme/defaults/dark-twilight.json +91 -0
  846. package/src/modes/theme/defaults/dark-volcanic.json +91 -0
  847. package/src/modes/theme/defaults/graphite.json +92 -0
  848. package/src/modes/theme/defaults/index.ts +199 -0
  849. package/src/modes/theme/defaults/light-arctic.json +107 -0
  850. package/src/modes/theme/defaults/light-aurora-day.json +91 -0
  851. package/src/modes/theme/defaults/light-canyon.json +91 -0
  852. package/src/modes/theme/defaults/light-catppuccin.json +106 -0
  853. package/src/modes/theme/defaults/light-cirrus.json +90 -0
  854. package/src/modes/theme/defaults/light-coral.json +95 -0
  855. package/src/modes/theme/defaults/light-cyberpunk.json +96 -0
  856. package/src/modes/theme/defaults/light-dawn.json +90 -0
  857. package/src/modes/theme/defaults/light-dunes.json +91 -0
  858. package/src/modes/theme/defaults/light-eucalyptus.json +95 -0
  859. package/src/modes/theme/defaults/light-forest.json +100 -0
  860. package/src/modes/theme/defaults/light-frost.json +95 -0
  861. package/src/modes/theme/defaults/light-github.json +115 -0
  862. package/src/modes/theme/defaults/light-glacier.json +91 -0
  863. package/src/modes/theme/defaults/light-gruvbox.json +108 -0
  864. package/src/modes/theme/defaults/light-haze.json +90 -0
  865. package/src/modes/theme/defaults/light-honeycomb.json +95 -0
  866. package/src/modes/theme/defaults/light-lagoon.json +91 -0
  867. package/src/modes/theme/defaults/light-lavender.json +95 -0
  868. package/src/modes/theme/defaults/light-meadow.json +91 -0
  869. package/src/modes/theme/defaults/light-mint.json +95 -0
  870. package/src/modes/theme/defaults/light-monochrome.json +101 -0
  871. package/src/modes/theme/defaults/light-ocean.json +99 -0
  872. package/src/modes/theme/defaults/light-one.json +99 -0
  873. package/src/modes/theme/defaults/light-opal.json +91 -0
  874. package/src/modes/theme/defaults/light-orchard.json +91 -0
  875. package/src/modes/theme/defaults/light-paper.json +95 -0
  876. package/src/modes/theme/defaults/light-poimandres.json +143 -0
  877. package/src/modes/theme/defaults/light-prism.json +90 -0
  878. package/src/modes/theme/defaults/light-retro.json +98 -0
  879. package/src/modes/theme/defaults/light-sand.json +95 -0
  880. package/src/modes/theme/defaults/light-savanna.json +91 -0
  881. package/src/modes/theme/defaults/light-solarized.json +102 -0
  882. package/src/modes/theme/defaults/light-soleil.json +90 -0
  883. package/src/modes/theme/defaults/light-sunset.json +99 -0
  884. package/src/modes/theme/defaults/light-synthwave.json +98 -0
  885. package/src/modes/theme/defaults/light-tokyo-night.json +111 -0
  886. package/src/modes/theme/defaults/light-wetland.json +91 -0
  887. package/src/modes/theme/defaults/light-zenith.json +89 -0
  888. package/src/modes/theme/defaults/limestone.json +94 -0
  889. package/src/modes/theme/defaults/mahogany.json +97 -0
  890. package/src/modes/theme/defaults/marble.json +93 -0
  891. package/src/modes/theme/defaults/obsidian.json +91 -0
  892. package/src/modes/theme/defaults/onyx.json +91 -0
  893. package/src/modes/theme/defaults/pearl.json +93 -0
  894. package/src/modes/theme/defaults/porcelain.json +91 -0
  895. package/src/modes/theme/defaults/quartz.json +96 -0
  896. package/src/modes/theme/defaults/sandstone.json +95 -0
  897. package/src/modes/theme/defaults/titanium.json +90 -0
  898. package/src/modes/theme/light.json +93 -0
  899. package/src/modes/theme/mermaid-cache.ts +92 -0
  900. package/src/modes/theme/shimmer.ts +305 -0
  901. package/src/modes/theme/theme-schema.json +463 -0
  902. package/src/modes/theme/theme.ts +3019 -0
  903. package/src/modes/turn-budget.ts +31 -0
  904. package/src/modes/types.ts +479 -0
  905. package/src/modes/ultrathink.ts +42 -0
  906. package/src/modes/utils/context-usage.ts +518 -0
  907. package/src/modes/utils/copy-targets.ts +378 -0
  908. package/src/modes/utils/hotkeys-markdown.ts +63 -0
  909. package/src/modes/utils/interactive-context-helpers.ts +30 -0
  910. package/src/modes/utils/keybinding-matchers.ts +86 -0
  911. package/src/modes/utils/tools-markdown.ts +31 -0
  912. package/src/modes/utils/transcript-render-helpers.ts +253 -0
  913. package/src/modes/utils/ui-helpers.ts +963 -0
  914. package/src/modes/warp-events.ts +232 -0
  915. package/src/modes/workflow.ts +49 -0
  916. package/src/plan-mode/approved-plan.ts +194 -0
  917. package/src/plan-mode/model-transition.ts +51 -0
  918. package/src/plan-mode/plan-files.ts +40 -0
  919. package/src/plan-mode/plan-handoff.ts +37 -0
  920. package/src/plan-mode/plan-protection.ts +31 -0
  921. package/src/plan-mode/state.ts +6 -0
  922. package/src/priority.json +60 -0
  923. package/src/prompts/advisor/active-repo-watchdog.md +6 -0
  924. package/src/prompts/advisor/advise-tool.md +3 -0
  925. package/src/prompts/advisor/context-files.md +8 -0
  926. package/src/prompts/advisor/system.md +98 -0
  927. package/src/prompts/agents/designer.md +74 -0
  928. package/src/prompts/agents/frontmatter.md +12 -0
  929. package/src/prompts/agents/init.md +33 -0
  930. package/src/prompts/agents/librarian.md +119 -0
  931. package/src/prompts/agents/reviewer.md +139 -0
  932. package/src/prompts/agents/scout.md +58 -0
  933. package/src/prompts/agents/task.md +17 -0
  934. package/src/prompts/bench/cache-prefix-chunk.md +1 -0
  935. package/src/prompts/bench/cache-prefix.md +3 -0
  936. package/src/prompts/bench/cache-suffix.md +1 -0
  937. package/src/prompts/bench.md +6 -0
  938. package/src/prompts/ci-green-request.md +36 -0
  939. package/src/prompts/dry-balance-bench.md +8 -0
  940. package/src/prompts/goals/goal-budget-limit.md +16 -0
  941. package/src/prompts/goals/goal-continuation.md +28 -0
  942. package/src/prompts/goals/goal-mode-active.md +23 -0
  943. package/src/prompts/goals/goal-mode-context.md +4 -0
  944. package/src/prompts/goals/goal-todo-context.md +12 -0
  945. package/src/prompts/goals/guided-goal-interview.md +43 -0
  946. package/src/prompts/memories/consolidation.md +30 -0
  947. package/src/prompts/memories/consolidation_system.md +4 -0
  948. package/src/prompts/memories/read-path.md +17 -0
  949. package/src/prompts/memories/stage_one_input.md +6 -0
  950. package/src/prompts/memories/stage_one_system.md +21 -0
  951. package/src/prompts/review-custom-request.md +21 -0
  952. package/src/prompts/review-headless-request.md +16 -0
  953. package/src/prompts/review-request.md +68 -0
  954. package/src/prompts/skills/autoload.md +8 -0
  955. package/src/prompts/skills/user-invocation.md +11 -0
  956. package/src/prompts/steering/parent-irc.md +5 -0
  957. package/src/prompts/steering/user-interjection.md +6 -0
  958. package/src/prompts/system/active-repo-context.md +4 -0
  959. package/src/prompts/system/agent-creation-architect.md +50 -0
  960. package/src/prompts/system/agent-creation-user.md +6 -0
  961. package/src/prompts/system/auto-continue.md +1 -0
  962. package/src/prompts/system/auto-thinking-difficulty-local.md +14 -0
  963. package/src/prompts/system/auto-thinking-difficulty.md +12 -0
  964. package/src/prompts/system/autolearn-guidance-learn.md +1 -0
  965. package/src/prompts/system/autolearn-guidance.md +7 -0
  966. package/src/prompts/system/autolearn-nudge-autocontinue.md +5 -0
  967. package/src/prompts/system/background-tan-dispatch.md +8 -0
  968. package/src/prompts/system/btw-user.md +8 -0
  969. package/src/prompts/system/commit-message-system.md +14 -0
  970. package/src/prompts/system/computer-safety.md +14 -0
  971. package/src/prompts/system/custom-system-prompt.md +64 -0
  972. package/src/prompts/system/eager-task.md +7 -0
  973. package/src/prompts/system/eager-todo.md +18 -0
  974. package/src/prompts/system/empty-stop-retry.md +4 -0
  975. package/src/prompts/system/gemini-tool-call-reminder.md +9 -0
  976. package/src/prompts/system/interrupted-thinking.md +7 -0
  977. package/src/prompts/system/irc-autoreply.md +6 -0
  978. package/src/prompts/system/irc-incoming.md +9 -0
  979. package/src/prompts/system/manual-continue.md +7 -0
  980. package/src/prompts/system/mcp-xdev-guidance.md +11 -0
  981. package/src/prompts/system/memory-consolidation-system.md +8 -0
  982. package/src/prompts/system/memory-extraction-system.md +26 -0
  983. package/src/prompts/system/mid-run-todo-nudge.md +3 -0
  984. package/src/prompts/system/omfg-user.md +50 -0
  985. package/src/prompts/system/orchestrate-notice.md +40 -0
  986. package/src/prompts/system/personalities/default.md +18 -0
  987. package/src/prompts/system/personalities/friendly.md +17 -0
  988. package/src/prompts/system/personalities/pragmatic.md +15 -0
  989. package/src/prompts/system/plan-mode-active.md +125 -0
  990. package/src/prompts/system/plan-mode-approved.md +22 -0
  991. package/src/prompts/system/plan-mode-compact-instructions.md +17 -0
  992. package/src/prompts/system/plan-mode-reference.md +10 -0
  993. package/src/prompts/system/plan-mode-subagent.md +33 -0
  994. package/src/prompts/system/plan-mode-tool-decision-reminder.md +9 -0
  995. package/src/prompts/system/plan-yolo-handoff.md +5 -0
  996. package/src/prompts/system/prewalk-checklist.md +7 -0
  997. package/src/prompts/system/prewalk-continue.md +1 -0
  998. package/src/prompts/system/prewalk-plan.md +13 -0
  999. package/src/prompts/system/project-prompt.md +61 -0
  1000. package/src/prompts/system/recap-user.md +9 -0
  1001. package/src/prompts/system/resolve-device-reminder.md +3 -0
  1002. package/src/prompts/system/rewind-report.md +6 -0
  1003. package/src/prompts/system/side-channel-no-tools.md +3 -0
  1004. package/src/prompts/system/snapcompact-context-frames-note.md +1 -0
  1005. package/src/prompts/system/snapcompact-context-stub.md +1 -0
  1006. package/src/prompts/system/snapcompact-system-frames-note.md +1 -0
  1007. package/src/prompts/system/snapcompact-system-stub.md +1 -0
  1008. package/src/prompts/system/snapcompact-toolresult-note.md +1 -0
  1009. package/src/prompts/system/speech-rewrite.md +15 -0
  1010. package/src/prompts/system/subagent-async-pending.md +6 -0
  1011. package/src/prompts/system/subagent-system-prompt.md +73 -0
  1012. package/src/prompts/system/subagent-user-prompt.md +3 -0
  1013. package/src/prompts/system/subagent-yield-reminder.md +23 -0
  1014. package/src/prompts/system/system-prompt.md +283 -0
  1015. package/src/prompts/system/tan-context-switch.md +17 -0
  1016. package/src/prompts/system/task-label.md +23 -0
  1017. package/src/prompts/system/thinking-loop-redirect.md +10 -0
  1018. package/src/prompts/system/title-marker-instruction.md +1 -0
  1019. package/src/prompts/system/title-system.md +16 -0
  1020. package/src/prompts/system/tool-call-loop-redirect.md +8 -0
  1021. package/src/prompts/system/ttsr-interrupt.md +7 -0
  1022. package/src/prompts/system/ttsr-tool-reminder.md +5 -0
  1023. package/src/prompts/system/ultrathink-notice.md +3 -0
  1024. package/src/prompts/system/unexpected-stop-classifier.md +17 -0
  1025. package/src/prompts/system/unexpected-stop-retry.md +4 -0
  1026. package/src/prompts/system/vibe-mode-active.md +26 -0
  1027. package/src/prompts/system/web-search.md +25 -0
  1028. package/src/prompts/system/workflow-notice.md +112 -0
  1029. package/src/prompts/system/xdev-mount-notice.md +20 -0
  1030. package/src/prompts/tools/apply-patch.md +65 -0
  1031. package/src/prompts/tools/ask.md +22 -0
  1032. package/src/prompts/tools/ast-edit.md +11 -0
  1033. package/src/prompts/tools/ast-grep.md +19 -0
  1034. package/src/prompts/tools/async-result.md +8 -0
  1035. package/src/prompts/tools/bash.md +25 -0
  1036. package/src/prompts/tools/browser.md +28 -0
  1037. package/src/prompts/tools/checkpoint.md +15 -0
  1038. package/src/prompts/tools/computer.md +26 -0
  1039. package/src/prompts/tools/debug.md +3 -0
  1040. package/src/prompts/tools/eval.md +45 -0
  1041. package/src/prompts/tools/github.md +22 -0
  1042. package/src/prompts/tools/glob.md +15 -0
  1043. package/src/prompts/tools/goal.md +11 -0
  1044. package/src/prompts/tools/grep.md +12 -0
  1045. package/src/prompts/tools/hub.md +34 -0
  1046. package/src/prompts/tools/image-attachment-describe-system.md +8 -0
  1047. package/src/prompts/tools/image-attachment-describe.md +10 -0
  1048. package/src/prompts/tools/image-gen.md +7 -0
  1049. package/src/prompts/tools/inspect-image-system.md +20 -0
  1050. package/src/prompts/tools/inspect-image.md +22 -0
  1051. package/src/prompts/tools/learn.md +7 -0
  1052. package/src/prompts/tools/lsp-late-diagnostic.md +8 -0
  1053. package/src/prompts/tools/lsp.md +19 -0
  1054. package/src/prompts/tools/manage-skill.md +9 -0
  1055. package/src/prompts/tools/memory-edit.md +12 -0
  1056. package/src/prompts/tools/patch.md +57 -0
  1057. package/src/prompts/tools/read.md +27 -0
  1058. package/src/prompts/tools/recall.md +7 -0
  1059. package/src/prompts/tools/reflect.md +5 -0
  1060. package/src/prompts/tools/replace.md +29 -0
  1061. package/src/prompts/tools/retain.md +6 -0
  1062. package/src/prompts/tools/rewind.md +14 -0
  1063. package/src/prompts/tools/task-async-contract.md +1 -0
  1064. package/src/prompts/tools/task-summary.md +20 -0
  1065. package/src/prompts/tools/task.md +83 -0
  1066. package/src/prompts/tools/todo.md +42 -0
  1067. package/src/prompts/tools/vibe-kill.md +3 -0
  1068. package/src/prompts/tools/vibe-list.md +3 -0
  1069. package/src/prompts/tools/vibe-send.md +9 -0
  1070. package/src/prompts/tools/vibe-spawn.md +10 -0
  1071. package/src/prompts/tools/vibe-turn-result.md +19 -0
  1072. package/src/prompts/tools/vibe-wait.md +8 -0
  1073. package/src/prompts/tools/web-search.md +8 -0
  1074. package/src/prompts/tools/write.md +14 -0
  1075. package/src/registry/agent-lifecycle.ts +455 -0
  1076. package/src/registry/agent-registry.ts +217 -0
  1077. package/src/registry/persisted-agents.ts +98 -0
  1078. package/src/sdk.ts +3553 -0
  1079. package/src/secrets/index.ts +246 -0
  1080. package/src/secrets/obfuscator.ts +2574 -0
  1081. package/src/secrets/regex.ts +21 -0
  1082. package/src/session/acp-permission-gate.ts +165 -0
  1083. package/src/session/agent-session-events.ts +66 -0
  1084. package/src/session/agent-session-types.ts +346 -0
  1085. package/src/session/agent-session.ts +8406 -0
  1086. package/src/session/agent-storage.ts +805 -0
  1087. package/src/session/artifacts.ts +154 -0
  1088. package/src/session/async-job-delivery.ts +82 -0
  1089. package/src/session/auth-broker-config.ts +92 -0
  1090. package/src/session/auth-storage.ts +25 -0
  1091. package/src/session/bash-runner.ts +326 -0
  1092. package/src/session/blob-store.ts +295 -0
  1093. package/src/session/checkpoint-entries.ts +81 -0
  1094. package/src/session/client-bridge.ts +85 -0
  1095. package/src/session/codex-auto-reset.ts +202 -0
  1096. package/src/session/compact-modes.ts +105 -0
  1097. package/src/session/eval-runner.ts +217 -0
  1098. package/src/session/exit-diagnostics.ts +310 -0
  1099. package/src/session/history-storage.ts +327 -0
  1100. package/src/session/indexed-session-storage.ts +544 -0
  1101. package/src/session/irc-bridge.ts +203 -0
  1102. package/src/session/messages.ts +1280 -0
  1103. package/src/session/model-controls.ts +750 -0
  1104. package/src/session/prewalk.ts +257 -0
  1105. package/src/session/provider-image-budget.ts +86 -0
  1106. package/src/session/queued-messages.ts +99 -0
  1107. package/src/session/redis-session-storage.ts +257 -0
  1108. package/src/session/retry-fallback-chains.ts +455 -0
  1109. package/src/session/role-models.ts +85 -0
  1110. package/src/session/session-advisors.ts +1754 -0
  1111. package/src/session/session-context.ts +554 -0
  1112. package/src/session/session-dump-format.ts +216 -0
  1113. package/src/session/session-entries.ts +260 -0
  1114. package/src/session/session-handoff.ts +308 -0
  1115. package/src/session/session-history-format.ts +459 -0
  1116. package/src/session/session-listing.ts +715 -0
  1117. package/src/session/session-loader.ts +280 -0
  1118. package/src/session/session-maintenance.ts +2983 -0
  1119. package/src/session/session-manager.ts +2557 -0
  1120. package/src/session/session-memory.ts +222 -0
  1121. package/src/session/session-metadata.ts +53 -0
  1122. package/src/session/session-migrations.ts +78 -0
  1123. package/src/session/session-paths.ts +222 -0
  1124. package/src/session/session-persistence.ts +293 -0
  1125. package/src/session/session-provider-boundary.ts +306 -0
  1126. package/src/session/session-stats.ts +293 -0
  1127. package/src/session/session-storage.ts +734 -0
  1128. package/src/session/session-title-slot.ts +141 -0
  1129. package/src/session/session-tools.ts +1226 -0
  1130. package/src/session/session-workspace.ts +53 -0
  1131. package/src/session/settings-stream-fn.ts +74 -0
  1132. package/src/session/shake-types.ts +43 -0
  1133. package/src/session/snapcompact-inline.ts +545 -0
  1134. package/src/session/snapcompact-savings-journal.ts +113 -0
  1135. package/src/session/sql-session-storage.ts +374 -0
  1136. package/src/session/stream-guards.ts +417 -0
  1137. package/src/session/streaming-output.ts +1459 -0
  1138. package/src/session/todo-tracker.ts +380 -0
  1139. package/src/session/tool-choice-queue.ts +305 -0
  1140. package/src/session/ttsr-coordinator.ts +496 -0
  1141. package/src/session/turn-persistence.ts +142 -0
  1142. package/src/session/turn-recovery.ts +1728 -0
  1143. package/src/session/unexpected-stop-classifier.ts +132 -0
  1144. package/src/session/yield-queue.ts +183 -0
  1145. package/src/slash-commands/acp-builtins.ts +70 -0
  1146. package/src/slash-commands/available-commands.ts +105 -0
  1147. package/src/slash-commands/builtin-registry.ts +2982 -0
  1148. package/src/slash-commands/helpers/active-oauth-account.ts +80 -0
  1149. package/src/slash-commands/helpers/collab-qrcode.ts +28 -0
  1150. package/src/slash-commands/helpers/context-report.ts +66 -0
  1151. package/src/slash-commands/helpers/format.ts +46 -0
  1152. package/src/slash-commands/helpers/logout.ts +108 -0
  1153. package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
  1154. package/src/slash-commands/helpers/mcp.ts +533 -0
  1155. package/src/slash-commands/helpers/parse.ts +85 -0
  1156. package/src/slash-commands/helpers/reset-usage.ts +66 -0
  1157. package/src/slash-commands/helpers/session-pin.ts +44 -0
  1158. package/src/slash-commands/helpers/ssh.ts +196 -0
  1159. package/src/slash-commands/helpers/stats-dashboard.ts +85 -0
  1160. package/src/slash-commands/helpers/todo.ts +285 -0
  1161. package/src/slash-commands/helpers/usage-report.ts +198 -0
  1162. package/src/slash-commands/marketplace-install-parser.ts +99 -0
  1163. package/src/slash-commands/types.ts +139 -0
  1164. package/src/ssh/config-writer.ts +183 -0
  1165. package/src/ssh/connection-manager.ts +667 -0
  1166. package/src/ssh/file-transfer.ts +214 -0
  1167. package/src/ssh/sshfs-mount.ts +163 -0
  1168. package/src/ssh/utils.ts +51 -0
  1169. package/src/startup-splash.ts +19 -0
  1170. package/src/stt/asr-client.ts +401 -0
  1171. package/src/stt/asr-protocol.ts +65 -0
  1172. package/src/stt/asr-worker.ts +603 -0
  1173. package/src/stt/downloader.ts +142 -0
  1174. package/src/stt/endpointer.ts +259 -0
  1175. package/src/stt/index.ts +6 -0
  1176. package/src/stt/models.ts +150 -0
  1177. package/src/stt/sherpa-runtime.ts +71 -0
  1178. package/src/stt/stt-controller.ts +320 -0
  1179. package/src/stt/submit-trigger.ts +74 -0
  1180. package/src/subprocess/worker-client.ts +463 -0
  1181. package/src/subprocess/worker-runtime.ts +494 -0
  1182. package/src/system-prompt.ts +894 -0
  1183. package/src/task/agents.ts +168 -0
  1184. package/src/task/commands.ts +132 -0
  1185. package/src/task/discovery.ts +145 -0
  1186. package/src/task/executor.ts +3129 -0
  1187. package/src/task/index.ts +1519 -0
  1188. package/src/task/isolation-ownership.ts +106 -0
  1189. package/src/task/isolation-runner.ts +413 -0
  1190. package/src/task/label.ts +40 -0
  1191. package/src/task/name-generator.ts +1577 -0
  1192. package/src/task/omp-command.ts +26 -0
  1193. package/src/task/output-manager.ts +115 -0
  1194. package/src/task/parallel.ts +221 -0
  1195. package/src/task/persisted-revive.ts +141 -0
  1196. package/src/task/prewalk.ts +6 -0
  1197. package/src/task/prompt-policy.ts +8 -0
  1198. package/src/task/provider-concurrency.ts +100 -0
  1199. package/src/task/render.ts +1814 -0
  1200. package/src/task/renderer.ts +14 -0
  1201. package/src/task/repair-args.ts +118 -0
  1202. package/src/task/spawn-policy.ts +58 -0
  1203. package/src/task/structured-subagent.ts +649 -0
  1204. package/src/task/subprocess-tool-registry.ts +88 -0
  1205. package/src/task/types.ts +551 -0
  1206. package/src/task/worktree.ts +967 -0
  1207. package/src/task/yield-assembly.ts +198 -0
  1208. package/src/telemetry-export.ts +500 -0
  1209. package/src/thinking.ts +364 -0
  1210. package/src/tiny/device.ts +111 -0
  1211. package/src/tiny/dtype.ts +101 -0
  1212. package/src/tiny/message-preproc.ts +155 -0
  1213. package/src/tiny/models.ts +268 -0
  1214. package/src/tiny/text.ts +278 -0
  1215. package/src/tiny/title-client.ts +460 -0
  1216. package/src/tiny/title-protocol.ts +56 -0
  1217. package/src/tiny/worker.ts +354 -0
  1218. package/src/tools/acp-bridge.ts +81 -0
  1219. package/src/tools/approval.ts +245 -0
  1220. package/src/tools/ask.ts +1459 -0
  1221. package/src/tools/ast-edit.ts +718 -0
  1222. package/src/tools/ast-grep.ts +520 -0
  1223. package/src/tools/auto-generated-guard.ts +335 -0
  1224. package/src/tools/bash-interactive.ts +435 -0
  1225. package/src/tools/bash-interceptor.ts +67 -0
  1226. package/src/tools/bash-pty-selection.ts +14 -0
  1227. package/src/tools/bash-skill-urls.ts +335 -0
  1228. package/src/tools/bash.ts +1762 -0
  1229. package/src/tools/browser/aria/aria-snapshot.bundle.txt +7 -0
  1230. package/src/tools/browser/aria/aria-snapshot.ts +131 -0
  1231. package/src/tools/browser/attach.ts +194 -0
  1232. package/src/tools/browser/cmux/cmux-tab.ts +1400 -0
  1233. package/src/tools/browser/cmux/rpc.ts +206 -0
  1234. package/src/tools/browser/cmux/socket-client.ts +445 -0
  1235. package/src/tools/browser/launch.ts +788 -0
  1236. package/src/tools/browser/readable.ts +112 -0
  1237. package/src/tools/browser/registry.ts +287 -0
  1238. package/src/tools/browser/render.ts +226 -0
  1239. package/src/tools/browser/run-cancellation.ts +131 -0
  1240. package/src/tools/browser/run-output.ts +76 -0
  1241. package/src/tools/browser/tab-protocol.ts +115 -0
  1242. package/src/tools/browser/tab-supervisor.ts +1040 -0
  1243. package/src/tools/browser/tab-worker-entry.ts +29 -0
  1244. package/src/tools/browser/tab-worker.ts +1842 -0
  1245. package/src/tools/browser.ts +457 -0
  1246. package/src/tools/builtin-names.ts +66 -0
  1247. package/src/tools/checkpoint.ts +150 -0
  1248. package/src/tools/computer/exposure.ts +38 -0
  1249. package/src/tools/computer/protocol.ts +28 -0
  1250. package/src/tools/computer/supervisor.ts +306 -0
  1251. package/src/tools/computer/worker-entry.ts +34 -0
  1252. package/src/tools/computer/worker.ts +131 -0
  1253. package/src/tools/computer-renderer.ts +108 -0
  1254. package/src/tools/computer.ts +516 -0
  1255. package/src/tools/conflict-detect.ts +815 -0
  1256. package/src/tools/context.ts +46 -0
  1257. package/src/tools/debug.ts +1121 -0
  1258. package/src/tools/default-renderer.ts +139 -0
  1259. package/src/tools/essential-tools.ts +46 -0
  1260. package/src/tools/eval-backends.ts +34 -0
  1261. package/src/tools/eval-format/index.ts +24 -0
  1262. package/src/tools/eval-format/javascript.ts +952 -0
  1263. package/src/tools/eval-format/julia.ts +446 -0
  1264. package/src/tools/eval-format/python.ts +544 -0
  1265. package/src/tools/eval-format/ruby.ts +380 -0
  1266. package/src/tools/eval-render.ts +784 -0
  1267. package/src/tools/eval.ts +774 -0
  1268. package/src/tools/fetch.ts +1889 -0
  1269. package/src/tools/file-recorder.ts +35 -0
  1270. package/src/tools/fs-cache-invalidation.ts +28 -0
  1271. package/src/tools/gh-cache-invalidation.ts +175 -0
  1272. package/src/tools/gh-format.ts +12 -0
  1273. package/src/tools/gh-renderer.ts +484 -0
  1274. package/src/tools/gh.ts +3959 -0
  1275. package/src/tools/github-cache.ts +663 -0
  1276. package/src/tools/glob.ts +682 -0
  1277. package/src/tools/grep.ts +1858 -0
  1278. package/src/tools/grouped-file-output.ts +210 -0
  1279. package/src/tools/hub/index.ts +579 -0
  1280. package/src/tools/hub/jobs.ts +714 -0
  1281. package/src/tools/hub/launch.ts +579 -0
  1282. package/src/tools/hub/messaging.ts +735 -0
  1283. package/src/tools/hub/types.ts +117 -0
  1284. package/src/tools/image-gen.ts +1689 -0
  1285. package/src/tools/image-providers.ts +50 -0
  1286. package/src/tools/index.ts +691 -0
  1287. package/src/tools/inspect-image-renderer.ts +133 -0
  1288. package/src/tools/inspect-image.ts +290 -0
  1289. package/src/tools/json-tree.ts +260 -0
  1290. package/src/tools/jtd-to-json-schema.ts +219 -0
  1291. package/src/tools/jtd-to-typescript.ts +136 -0
  1292. package/src/tools/jtd-utils.ts +102 -0
  1293. package/src/tools/learn.ts +141 -0
  1294. package/src/tools/list-limit.ts +40 -0
  1295. package/src/tools/manage-skill.ts +102 -0
  1296. package/src/tools/match-line-format.ts +20 -0
  1297. package/src/tools/memory-edit.ts +61 -0
  1298. package/src/tools/memory-recall.ts +102 -0
  1299. package/src/tools/memory-reflect.ts +88 -0
  1300. package/src/tools/memory-render.ts +211 -0
  1301. package/src/tools/memory-retain.ts +89 -0
  1302. package/src/tools/output-meta.ts +845 -0
  1303. package/src/tools/output-schema-validator.ts +307 -0
  1304. package/src/tools/path-utils.ts +1381 -0
  1305. package/src/tools/plan-mode-guard.ts +155 -0
  1306. package/src/tools/puppeteer/00_stealth_tampering.txt +44 -0
  1307. package/src/tools/puppeteer/01_stealth_activity.txt +80 -0
  1308. package/src/tools/puppeteer/02_stealth_hairline.txt +57 -0
  1309. package/src/tools/puppeteer/03_stealth_botd.txt +380 -0
  1310. package/src/tools/puppeteer/04_stealth_iframe.txt +174 -0
  1311. package/src/tools/puppeteer/05_stealth_webgl.txt +233 -0
  1312. package/src/tools/puppeteer/06_stealth_screen.txt +260 -0
  1313. package/src/tools/puppeteer/07_stealth_fonts.txt +99 -0
  1314. package/src/tools/puppeteer/08_stealth_audio.txt +63 -0
  1315. package/src/tools/puppeteer/09_stealth_locale.txt +51 -0
  1316. package/src/tools/puppeteer/10_stealth_plugins.txt +212 -0
  1317. package/src/tools/puppeteer/11_stealth_hardware.txt +59 -0
  1318. package/src/tools/puppeteer/12_stealth_codecs.txt +42 -0
  1319. package/src/tools/puppeteer/13_stealth_worker.txt +235 -0
  1320. package/src/tools/read.ts +3742 -0
  1321. package/src/tools/render-utils.ts +924 -0
  1322. package/src/tools/renderers.ts +133 -0
  1323. package/src/tools/report-tool-issue.ts +568 -0
  1324. package/src/tools/resolve.ts +423 -0
  1325. package/src/tools/review.ts +103 -0
  1326. package/src/tools/shell-tokenize.ts +83 -0
  1327. package/src/tools/sqlite-reader.ts +884 -0
  1328. package/src/tools/terminal-output.ts +141 -0
  1329. package/src/tools/todo.ts +1226 -0
  1330. package/src/tools/tool-errors.ts +62 -0
  1331. package/src/tools/tool-result.ts +102 -0
  1332. package/src/tools/tool-timeouts.ts +37 -0
  1333. package/src/tools/tts.ts +266 -0
  1334. package/src/tools/vibe.ts +607 -0
  1335. package/src/tools/write.ts +1640 -0
  1336. package/src/tools/xdev.ts +508 -0
  1337. package/src/tools/yield.ts +486 -0
  1338. package/src/tts/downloader.ts +64 -0
  1339. package/src/tts/index.ts +10 -0
  1340. package/src/tts/models.ts +137 -0
  1341. package/src/tts/runtime.ts +21 -0
  1342. package/src/tts/speakable.ts +392 -0
  1343. package/src/tts/speech-enhancer.ts +206 -0
  1344. package/src/tts/streaming-player.ts +120 -0
  1345. package/src/tts/tts-client.ts +475 -0
  1346. package/src/tts/tts-protocol.ts +69 -0
  1347. package/src/tts/tts-worker.ts +434 -0
  1348. package/src/tts/vocalizer.ts +419 -0
  1349. package/src/tts/wav.ts +58 -0
  1350. package/src/tui/code-cell.ts +268 -0
  1351. package/src/tui/file-list.ts +55 -0
  1352. package/src/tui/hyperlink.ts +178 -0
  1353. package/src/tui/index.ts +13 -0
  1354. package/src/tui/output-block.ts +268 -0
  1355. package/src/tui/status-line.ts +54 -0
  1356. package/src/tui/tree-list.ts +172 -0
  1357. package/src/tui/types.ts +15 -0
  1358. package/src/tui/utils.ts +103 -0
  1359. package/src/tui/width-aware-text.ts +58 -0
  1360. package/src/utils/active-repo-context.ts +143 -0
  1361. package/src/utils/block-context.ts +312 -0
  1362. package/src/utils/changelog.ts +232 -0
  1363. package/src/utils/clipboard.ts +327 -0
  1364. package/src/utils/command-args.ts +76 -0
  1365. package/src/utils/commit-message-generator.ts +148 -0
  1366. package/src/utils/cpuprofile.ts +235 -0
  1367. package/src/utils/edit-mode.ts +61 -0
  1368. package/src/utils/enhanced-paste.ts +230 -0
  1369. package/src/utils/event-bus.ts +33 -0
  1370. package/src/utils/external-editor.ts +78 -0
  1371. package/src/utils/fetch-timeout.ts +10 -0
  1372. package/src/utils/file-display-mode.ts +44 -0
  1373. package/src/utils/file-mentions.ts +293 -0
  1374. package/src/utils/git.ts +2425 -0
  1375. package/src/utils/image-loading.ts +231 -0
  1376. package/src/utils/image-resize.ts +420 -0
  1377. package/src/utils/image-vision-fallback.ts +196 -0
  1378. package/src/utils/inspect-image-mode.ts +39 -0
  1379. package/src/utils/ipc.ts +38 -0
  1380. package/src/utils/jj.ts +416 -0
  1381. package/src/utils/lang-from-path.ts +251 -0
  1382. package/src/utils/local-date.ts +7 -0
  1383. package/src/utils/mac-file-urls.applescript +37 -0
  1384. package/src/utils/markit-cache.ts +166 -0
  1385. package/src/utils/markit.ts +223 -0
  1386. package/src/utils/mupdf-wasm-embed.ts +12 -0
  1387. package/src/utils/open.ts +126 -0
  1388. package/src/utils/profile-tree.ts +111 -0
  1389. package/src/utils/prompt-path.ts +3 -0
  1390. package/src/utils/qrcode.ts +535 -0
  1391. package/src/utils/sample-profile.ts +437 -0
  1392. package/src/utils/session-color.ts +142 -0
  1393. package/src/utils/shell-snapshot-fn-env.sh +63 -0
  1394. package/src/utils/shell-snapshot.ts +316 -0
  1395. package/src/utils/sixel.ts +69 -0
  1396. package/src/utils/thinking-display.ts +163 -0
  1397. package/src/utils/title-generator.ts +597 -0
  1398. package/src/utils/token-rate.ts +72 -0
  1399. package/src/utils/tool-choice.ts +63 -0
  1400. package/src/utils/tools-manager.ts +411 -0
  1401. package/src/utils/turndown.ts +83 -0
  1402. package/src/utils/zip.ts +1106 -0
  1403. package/src/vibe/runtime.ts +1540 -0
  1404. package/src/vibe/state.ts +4 -0
  1405. package/src/web/kagi.ts +304 -0
  1406. package/src/web/parallel.ts +354 -0
  1407. package/src/web/scrapers/artifacthub.ts +207 -0
  1408. package/src/web/scrapers/arxiv.ts +83 -0
  1409. package/src/web/scrapers/aur.ts +162 -0
  1410. package/src/web/scrapers/biorxiv.ts +133 -0
  1411. package/src/web/scrapers/bluesky.ts +262 -0
  1412. package/src/web/scrapers/brew.ts +172 -0
  1413. package/src/web/scrapers/cheatsh.ts +68 -0
  1414. package/src/web/scrapers/chocolatey.ts +196 -0
  1415. package/src/web/scrapers/choosealicense.ts +95 -0
  1416. package/src/web/scrapers/cisa-kev.ts +87 -0
  1417. package/src/web/scrapers/clojars.ts +154 -0
  1418. package/src/web/scrapers/coingecko.ts +177 -0
  1419. package/src/web/scrapers/crates-io.ts +97 -0
  1420. package/src/web/scrapers/crossref.ts +136 -0
  1421. package/src/web/scrapers/devto.ts +147 -0
  1422. package/src/web/scrapers/discogs.ts +306 -0
  1423. package/src/web/scrapers/discourse.ts +197 -0
  1424. package/src/web/scrapers/dockerhub.ts +138 -0
  1425. package/src/web/scrapers/docs-rs.ts +663 -0
  1426. package/src/web/scrapers/fdroid.ts +134 -0
  1427. package/src/web/scrapers/firefox-addons.ts +191 -0
  1428. package/src/web/scrapers/flathub.ts +223 -0
  1429. package/src/web/scrapers/github-gist.ts +58 -0
  1430. package/src/web/scrapers/github.ts +800 -0
  1431. package/src/web/scrapers/gitlab.ts +401 -0
  1432. package/src/web/scrapers/go-pkg.ts +266 -0
  1433. package/src/web/scrapers/hackage.ts +140 -0
  1434. package/src/web/scrapers/hackernews.ts +189 -0
  1435. package/src/web/scrapers/hex.ts +105 -0
  1436. package/src/web/scrapers/huggingface.ts +321 -0
  1437. package/src/web/scrapers/iacr.ts +89 -0
  1438. package/src/web/scrapers/index.ts +252 -0
  1439. package/src/web/scrapers/jetbrains-marketplace.ts +159 -0
  1440. package/src/web/scrapers/lemmy.ts +203 -0
  1441. package/src/web/scrapers/lobsters.ts +175 -0
  1442. package/src/web/scrapers/mastodon.ts +292 -0
  1443. package/src/web/scrapers/maven.ts +138 -0
  1444. package/src/web/scrapers/mdn.ts +173 -0
  1445. package/src/web/scrapers/metacpan.ts +222 -0
  1446. package/src/web/scrapers/musicbrainz.ts +250 -0
  1447. package/src/web/scrapers/npm.ts +98 -0
  1448. package/src/web/scrapers/nuget.ts +183 -0
  1449. package/src/web/scrapers/nvd.ts +222 -0
  1450. package/src/web/scrapers/ollama.ts +239 -0
  1451. package/src/web/scrapers/open-vsx.ts +106 -0
  1452. package/src/web/scrapers/opencorporates.ts +292 -0
  1453. package/src/web/scrapers/openlibrary.ts +336 -0
  1454. package/src/web/scrapers/orcid.ts +286 -0
  1455. package/src/web/scrapers/osv.ts +176 -0
  1456. package/src/web/scrapers/packagist.ts +160 -0
  1457. package/src/web/scrapers/pub-dev.ts +143 -0
  1458. package/src/web/scrapers/pubmed.ts +211 -0
  1459. package/src/web/scrapers/pypi.ts +112 -0
  1460. package/src/web/scrapers/rawg.ts +110 -0
  1461. package/src/web/scrapers/readthedocs.ts +120 -0
  1462. package/src/web/scrapers/reddit.ts +95 -0
  1463. package/src/web/scrapers/repology.ts +251 -0
  1464. package/src/web/scrapers/rfc.ts +201 -0
  1465. package/src/web/scrapers/rubygems.ts +103 -0
  1466. package/src/web/scrapers/searchcode.ts +189 -0
  1467. package/src/web/scrapers/sec-edgar.ts +261 -0
  1468. package/src/web/scrapers/semantic-scholar.ts +171 -0
  1469. package/src/web/scrapers/snapcraft.ts +187 -0
  1470. package/src/web/scrapers/sourcegraph.ts +336 -0
  1471. package/src/web/scrapers/spdx.ts +108 -0
  1472. package/src/web/scrapers/spotify.ts +198 -0
  1473. package/src/web/scrapers/stackoverflow.ts +120 -0
  1474. package/src/web/scrapers/terraform.ts +277 -0
  1475. package/src/web/scrapers/tldr.ts +47 -0
  1476. package/src/web/scrapers/twitter.ts +94 -0
  1477. package/src/web/scrapers/types.ts +354 -0
  1478. package/src/web/scrapers/utils.ts +109 -0
  1479. package/src/web/scrapers/vimeo.ts +133 -0
  1480. package/src/web/scrapers/vscode-marketplace.ts +187 -0
  1481. package/src/web/scrapers/w3c.ts +156 -0
  1482. package/src/web/scrapers/wikidata.ts +344 -0
  1483. package/src/web/scrapers/wikipedia.ts +84 -0
  1484. package/src/web/scrapers/youtube.ts +325 -0
  1485. package/src/web/search/index.ts +376 -0
  1486. package/src/web/search/provider.ts +272 -0
  1487. package/src/web/search/providers/anthropic.ts +401 -0
  1488. package/src/web/search/providers/base.ts +110 -0
  1489. package/src/web/search/providers/brave.ts +179 -0
  1490. package/src/web/search/providers/browser-headers.ts +109 -0
  1491. package/src/web/search/providers/browser-page.ts +123 -0
  1492. package/src/web/search/providers/codex.ts +749 -0
  1493. package/src/web/search/providers/duckduckgo.ts +213 -0
  1494. package/src/web/search/providers/ecosia.ts +182 -0
  1495. package/src/web/search/providers/exa.ts +511 -0
  1496. package/src/web/search/providers/firecrawl.ts +211 -0
  1497. package/src/web/search/providers/gemini.ts +628 -0
  1498. package/src/web/search/providers/google.ts +194 -0
  1499. package/src/web/search/providers/jina.ts +134 -0
  1500. package/src/web/search/providers/kagi.ts +95 -0
  1501. package/src/web/search/providers/kimi.ts +217 -0
  1502. package/src/web/search/providers/mojeek.ts +219 -0
  1503. package/src/web/search/providers/parallel.ts +182 -0
  1504. package/src/web/search/providers/perplexity-auth.ts +142 -0
  1505. package/src/web/search/providers/perplexity.ts +994 -0
  1506. package/src/web/search/providers/public.ts +199 -0
  1507. package/src/web/search/providers/searxng.ts +461 -0
  1508. package/src/web/search/providers/startpage.ts +219 -0
  1509. package/src/web/search/providers/synthetic.ts +120 -0
  1510. package/src/web/search/providers/tavily.ts +242 -0
  1511. package/src/web/search/providers/tinyfish.ts +164 -0
  1512. package/src/web/search/providers/utils.ts +128 -0
  1513. package/src/web/search/providers/xai.ts +372 -0
  1514. package/src/web/search/providers/zai.ts +437 -0
  1515. package/src/web/search/query.ts +850 -0
  1516. package/src/web/search/render.ts +262 -0
  1517. package/src/web/search/types.ts +507 -0
  1518. package/src/web/search/utils.ts +17 -0
  1519. package/src/workspace-tree.ts +326 -0
@@ -0,0 +1,1762 @@
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 "@oh-my-pi/pi-tui";
10
+ import { ImageProtocol, TERMINAL } from "@oh-my-pi/pi-tui";
11
+ import { getProjectDir, isEnoent, logger, prompt } from "@oh-my-pi/pi-utils";
12
+ import { type } from "arktype";
13
+ import type { Settings } from "../config/settings";
14
+ import { applyDirenvPreflight, type BashResult, executeBash } from "../exec/bash-executor";
15
+ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
16
+ import { InternalUrlRouter } from "../internal-urls";
17
+ import { truncateToVisualLines } from "../modes/components/visual-truncate";
18
+ import { highlightCode, type Theme } from "../modes/theme/theme";
19
+ import bashDescription from "../prompts/tools/bash.md" with { type: "text" };
20
+ import type {
21
+ ClientBridgeTerminalExitStatus,
22
+ ClientBridgeTerminalHandle,
23
+ ClientBridgeTerminalOutput,
24
+ } from "../session/client-bridge";
25
+ import { DEFAULT_MAX_BYTES, enforceInlineByteCap, streamTailUpdates, TailBuffer } from "../session/streaming-output";
26
+ import { renderStatusLine } from "../tui";
27
+ import { CachedOutputBlock, markFramedBlockComponent, outputBlockContentWidth } from "../tui/output-block";
28
+ import { getSixelLineMask } from "../utils/sixel";
29
+ import type { ToolSession } from ".";
30
+ import { truncateForPrompt } from "./approval";
31
+ import { type BashInteractiveResult, runInteractiveBashPty } from "./bash-interactive";
32
+ import { checkBashInterception } from "./bash-interceptor";
33
+ import { canUseInteractiveBashPty } from "./bash-pty-selection";
34
+ import { expandInternalUrls, type InternalUrlExpansionOptions } from "./bash-skill-urls";
35
+ import { resolveEvalBackends } from "./eval-backends";
36
+ import { invalidateGithubCacheForBashCommand } from "./gh-cache-invalidation";
37
+ import {
38
+ formatStyledTruncationWarning,
39
+ type OutputMeta,
40
+ resolveInlineByteCapBudget,
41
+ stripOutputNotice,
42
+ stripRawOutputArtifactNotice,
43
+ } from "./output-meta";
44
+ import { resolveToCwd } from "./path-utils";
45
+ import {
46
+ capPreviewLines,
47
+ DEFAULT_TERMINAL_PREVIEW_LINES,
48
+ formatToolWorkingDirectory,
49
+ previewWindowRows,
50
+ replaceTabs,
51
+ } from "./render-utils";
52
+ import { tokenizeShellSegments } from "./shell-tokenize";
53
+ import { ToolAbortError, ToolError } from "./tool-errors";
54
+ import { toolResult } from "./tool-result";
55
+ import { clampTimeout, TOOL_TIMEOUTS } from "./tool-timeouts";
56
+
57
+ export const BASH_DEFAULT_PREVIEW_LINES = DEFAULT_TERMINAL_PREVIEW_LINES;
58
+
59
+ const BASH_ENV_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
60
+ const DEFAULT_AUTO_BACKGROUND_THRESHOLD_MS = 60_000;
61
+ const BASH_APPROVAL_SHELL_CONTROL_RE = /[\n\r;&|<>`$()]/u;
62
+ const BASH_PATTERN_APPROVAL_VALUES = new Set(["allow", "deny", "prompt"]);
63
+
64
+ /**
65
+ * Shape a shell command line for an ACP-conformant `terminal/create` request.
66
+ *
67
+ * ACP's `command` field is documented as the executable and `args` as its
68
+ * argv tail (see https://agentclientprotocol.com/protocol/v1/terminals), so a
69
+ * spec-conformant client `spawn(command, args)`s them directly — no implicit
70
+ * shell. A raw `bash` tool line ("git status && echo x | head") therefore has
71
+ * to be wrapped in an explicit shell invocation, otherwise the client tries
72
+ * to spawn the whole line as argv[0] and fails with `ENOENT` for anything
73
+ * containing a space, pipe, `&&`, redirect, or `$(...)`.
74
+ *
75
+ * The wrap reuses the same shell binary + args the local `bash-executor` would
76
+ * pick via `settings.getShellConfig()` — Git Bash / `bash.exe` on Windows
77
+ * (`cmd.exe /c` as the last-resort fallback when no bash exists on the host),
78
+ * `$SHELL` (bash/zsh) with the `sh` fallback on POSIX — so the ACP path
79
+ * preserves `bash` tool semantics (`$VAR`, `$(...)`, `source`, POSIX quoting,
80
+ * `-l`) wherever a POSIX shell is available. The agent host's shell path is
81
+ * used as a proxy for the client's, matching the near-universal ACP
82
+ * deployment shape of an editor spawning omp as a co-hosted subprocess.
83
+ */
84
+ export function wrapShellLineForClientTerminal(
85
+ line: string,
86
+ shellConfig: { shell: string; args: string[]; prefix?: string | undefined },
87
+ ): { command: string; args: string[] } {
88
+ const finalLine = shellConfig.prefix ? `${shellConfig.prefix} ${line}` : line;
89
+ return { command: shellConfig.shell, args: [...shellConfig.args, finalLine] };
90
+ }
91
+
92
+ /**
93
+ * Mirrors pi-shell's `uutils_env_disabled` gate for `PI_DISABLE_UUTILS_BUILTINS`:
94
+ * session shell env first, then process env; truthy = present and not "", "0",
95
+ * or "false". Controls whether the prompt advertises the in-process builtins.
96
+ */
97
+ function shellBuiltinsDisabled(settings: Settings): boolean {
98
+ const raw = settings.getShellConfig().env?.PI_DISABLE_UUTILS_BUILTINS ?? Bun.env.PI_DISABLE_UUTILS_BUILTINS;
99
+ return !!raw && raw !== "0" && raw.toLowerCase() !== "false";
100
+ }
101
+
102
+ /**
103
+ * Bash patterns flagged as safety critical for approval policy.
104
+ *
105
+ * Kept intentionally tight — the cost of a false negative is data loss or a compromised host,
106
+ * while false positives remain actionable through user policy control.
107
+ * New patterns should target shapes that are virtually never legitimate in automation.
108
+ */
109
+ export const CRITICAL_BASH_PATTERNS = [
110
+ // Recursive destruction.
111
+ /\brm\s+-[a-z]*[rRfF][a-z]*\s+\//i, // rm -rf /, rm -fr /, rm -r /, rm -f /…
112
+ /\bsudo\s+rm\b/i, // any `sudo rm`.
113
+ /\bchmod\s+-R\s+[0-7]+\s+\//i, // `chmod -R 777 /`.
114
+ /\bchmod\s+-R\s+[ugoa+\-=rwxXst,]+\s+\//, // `chmod -R u+x /`, `chmod -R u+rwx,o+w /etc` (symbolic mode, root target).
115
+ /\bchown\s+-R\s+\S+\s+\//i, // `chown -R user /`.
116
+
117
+ // Fork bomb (a few common spacings).
118
+ /:\(\)\s*\{\s*:\s*\|\s*:/i,
119
+
120
+ // Disk / filesystem destruction.
121
+ />\s*\/dev\/sd[a-z]/i, // write to disk device.
122
+ /\bmkfs(\.|\b)/i, // format filesystem.
123
+ /\bdd\s+if=.+of=\/dev\//i, // dd to a device.
124
+ /\bshred\s+\/dev\//i,
125
+ /\bcryptsetup\b/i,
126
+
127
+ // System-config destruction.
128
+ />\s*\/etc\/(?:passwd|shadow|sudoers)\b/i,
129
+ /\btee\s+(?:-a\s+)?\/etc\/(?:passwd|shadow|sudoers)\b/i, // `tee /etc/passwd`, `tee -a /etc/sudoers`.
130
+
131
+ // Remote-fetch-then-execute (curl/wget piped to a shell or process-subbed).
132
+ /\b(?:curl|wget|fetch)\b[^|]*\|\s*(?:bash|sh|zsh|fish)\b/i,
133
+ // Process-sub variants — `bash <(curl …)`, `source <(curl …)`, `. <(curl …)`. `.` and `source` are
134
+ // anchored to a command boundary so `find . -name` and similar don't false-positive.
135
+ /(?:^|[\s;&|(])(?:bash|sh|zsh|source|\.)\s+<\(\s*(?:curl|wget|fetch)\b/i,
136
+ // `eval "$(curl …)"` / `eval $(curl …)` / `eval \`curl …\``.
137
+ /\beval\s+["'`]?\$\(\s*(?:curl|wget|fetch)\b|\beval\s+`\s*(?:curl|wget|fetch)\b/i,
138
+
139
+ // Process/host control.
140
+ /\bkill\s+-9\s+1\b/, // kill PID 1.
141
+ // Process/host control — must sit at command position so `npm run reboot-tests`
142
+ // or `echo 'shutdown the queue'` don't false-positive.
143
+ /(?:^|[\s;&|(])(?:shutdown|poweroff|reboot|halt)(?:\s|$|[;|&])/i,
144
+ /(?:^|[\s;&|(])init\s+0\b/i,
145
+
146
+ // Network-shell exfil.
147
+ /\bnc\b[^|;]*\s-[a-zA-Z]*[ec][a-zA-Z]*\s/i, // `nc -e` / `nc -c`.
148
+ ] as const;
149
+
150
+ type BashPatternApproval = "allow" | "deny" | "prompt";
151
+
152
+ interface BashApprovalPatternRule {
153
+ match: string;
154
+ approval: BashPatternApproval;
155
+ }
156
+
157
+ function normalizeBashApprovalPattern(value: string): string {
158
+ return value.trim().replace(/\s+/gu, " ");
159
+ }
160
+
161
+ function bashApprovalPatternToRegExp(pattern: string): RegExp {
162
+ const escaped = normalizeBashApprovalPattern(pattern)
163
+ .split("*")
164
+ .map(part => part.replace(/[\\^$+?.()|[\]{}]/gu, "\\$&"))
165
+ .join(".*");
166
+ return new RegExp(`^${escaped}$`, "u");
167
+ }
168
+
169
+ function normalizeBashPatternApproval(value: unknown): BashPatternApproval | undefined {
170
+ if (typeof value !== "string") return undefined;
171
+ const normalized = value.trim().toLowerCase();
172
+ return BASH_PATTERN_APPROVAL_VALUES.has(normalized) ? (normalized as BashPatternApproval) : undefined;
173
+ }
174
+
175
+ function getBashApprovalPatternRules(value: unknown): BashApprovalPatternRule[] {
176
+ if (!Array.isArray(value)) return [];
177
+ return value
178
+ .map(item => {
179
+ if (!item || typeof item !== "object" || Array.isArray(item)) return undefined;
180
+ const record = item as Record<string, unknown>;
181
+ if (typeof record.match !== "string") return undefined;
182
+ const match = normalizeBashApprovalPattern(record.match);
183
+ const approval = normalizeBashPatternApproval(record.approval);
184
+ return match.length > 0 && approval ? { match, approval } : undefined;
185
+ })
186
+ .filter((rule): rule is BashApprovalPatternRule => !!rule);
187
+ }
188
+
189
+ function commandMatchesBashApprovalPattern(command: string, pattern: string): boolean {
190
+ const normalizedCommand = normalizeBashApprovalPattern(command);
191
+ if (normalizedCommand.length === 0) return false;
192
+ return bashApprovalPatternToRegExp(pattern).test(normalizedCommand);
193
+ }
194
+
195
+ // `deny`/`prompt` rules are matched per segment so a dangerous command buried in
196
+ // a compound line (`cd x && rm -rf /`, `sleep 1 & rm -rf /`) is still caught.
197
+ // Reuse the shared shell tokenizer so segmentation stays in one place and honors
198
+ // every command boundary (`;`, `&&`, `||`, `|`, `&`, subshells, newlines).
199
+ function bashCommandSegments(command: string): string[] {
200
+ return tokenizeShellSegments(command)
201
+ .map(segment => segment.join(" "))
202
+ .filter(segment => segment.length > 0);
203
+ }
204
+
205
+ // `deny`/`prompt` matching: the rule fires when its glob matches the whole
206
+ // command or any single segment of a compound command.
207
+ function commandSegmentMatchesBashApprovalPattern(command: string, pattern: string): boolean {
208
+ const regex = bashApprovalPatternToRegExp(pattern);
209
+ const normalizedCommand = normalizeBashApprovalPattern(command);
210
+ if (normalizedCommand.length === 0) return false;
211
+ if (regex.test(normalizedCommand)) return true;
212
+ return bashCommandSegments(command).some(segment => regex.test(segment));
213
+ }
214
+
215
+ // A rule "applies" to a command under approval-specific semantics: `allow` must
216
+ // vouch for the ENTIRE command and never rides a compound line (shell control
217
+ // syntax could smuggle an unsafe segment past a narrow allow), while `deny` and
218
+ // `prompt` fire on any matching segment so they mean what they appear to.
219
+ function bashApprovalRuleMatches(command: string, rule: BashApprovalPatternRule): boolean {
220
+ if (rule.approval === "allow") {
221
+ if (BASH_APPROVAL_SHELL_CONTROL_RE.test(command)) return false;
222
+ return commandMatchesBashApprovalPattern(command, rule.match);
223
+ }
224
+ return commandSegmentMatchesBashApprovalPattern(command, rule.match);
225
+ }
226
+
227
+ function findBashApprovalPatternRule(
228
+ command: string,
229
+ rules: readonly BashApprovalPatternRule[],
230
+ ): BashApprovalPatternRule | undefined {
231
+ return rules.find(rule => bashApprovalRuleMatches(command, rule));
232
+ }
233
+
234
+ async function saveBashOriginalArtifact(session: ToolSession, originalText: string): Promise<string | undefined> {
235
+ try {
236
+ const alloc = await session.allocateOutputArtifact?.("bash-original");
237
+ if (!alloc?.path || !alloc.id) return undefined;
238
+ await Bun.write(alloc.path, originalText);
239
+ return alloc.id;
240
+ } catch {
241
+ return undefined;
242
+ }
243
+ }
244
+
245
+ const BASH_TIMEOUT_DESCRIPTION = `timeout in seconds; 0 disables the command deadline; nonzero values are clamped to ${TOOL_TIMEOUTS.bash.min}-${TOOL_TIMEOUTS.bash.max}`;
246
+
247
+ const bashSchemaBase = type({
248
+ command: type("string").describe("command to execute"),
249
+ "env?": type({ "[string]": "string" }).describe("extra env vars"),
250
+ "timeout?": type("number").describe(BASH_TIMEOUT_DESCRIPTION),
251
+ "cwd?": type("string").describe("working directory"),
252
+ "pty?": type("boolean").describe("run in pty mode"),
253
+ });
254
+
255
+ const bashSchemaWithAsync = type({
256
+ command: "string",
257
+ "env?": { "[string]": "string" },
258
+ "timeout?": type("number").describe(BASH_TIMEOUT_DESCRIPTION),
259
+ "cwd?": "string",
260
+ "pty?": "boolean",
261
+ "async?": type("boolean").describe("run in background"),
262
+ });
263
+
264
+ type BashToolSchema = typeof bashSchemaBase | typeof bashSchemaWithAsync;
265
+
266
+ export interface BashToolInput {
267
+ command: string;
268
+ env?: Record<string, string>;
269
+ timeout?: number;
270
+ cwd?: string;
271
+
272
+ async?: boolean;
273
+ pty?: boolean;
274
+ }
275
+
276
+ export interface BashToolDetails {
277
+ meta?: OutputMeta;
278
+ timeoutSeconds?: number;
279
+ requestedTimeoutSeconds?: number;
280
+ timeoutDisabled?: boolean;
281
+ wallTimeMs?: number;
282
+ /** Exit code of a command that ran to completion but failed (non-zero). */
283
+ exitCode?: number;
284
+ /** True when the command was killed by its timeout deadline (not a failure). */
285
+ timedOut?: boolean;
286
+ terminalId?: string;
287
+ async?: {
288
+ state: "running" | "completed" | "failed";
289
+ jobId: string;
290
+ type: "bash";
291
+ };
292
+ }
293
+
294
+ export interface BashToolOptions {}
295
+
296
+ type ManagedBashJobCompletion =
297
+ | {
298
+ kind: "completed";
299
+ result: AgentToolResult<BashToolDetails>;
300
+ }
301
+ | {
302
+ kind: "failed";
303
+ error: unknown;
304
+ };
305
+
306
+ interface ManagedBashJobHandle {
307
+ jobId: string;
308
+ completion: Promise<ManagedBashJobCompletion>;
309
+ getLatestText: () => string;
310
+ stopUpdates: () => void;
311
+ }
312
+
313
+ function normalizeResultOutput(result: BashResult | BashInteractiveResult): string {
314
+ return result.output || "";
315
+ }
316
+
317
+ function normalizeBashEnv(env: Record<string, string> | undefined): Record<string, string> | undefined {
318
+ if (!env || Object.keys(env).length === 0) return undefined;
319
+ const normalized: Record<string, string> = {};
320
+ for (const [key, value] of Object.entries(env)) {
321
+ if (!BASH_ENV_NAME_PATTERN.test(key)) {
322
+ throw new ToolError(`Invalid bash env name: ${key}`);
323
+ }
324
+ normalized[key] = value;
325
+ }
326
+ return normalized;
327
+ }
328
+
329
+ function escapeBashEnvValueForDisplay(value: string): string {
330
+ return value
331
+ .replaceAll("\\", "\\\\")
332
+ .replaceAll("\n", "\\n")
333
+ .replaceAll("\r", "\\r")
334
+ .replaceAll("\t", "\\t")
335
+ .replaceAll('"', '\\"')
336
+ .replaceAll("$", "\\$")
337
+ .replaceAll("`", "\\`");
338
+ }
339
+
340
+ function formatBashEnvAssignments(env: Record<string, string> | undefined): string {
341
+ if (!env || Object.keys(env).length === 0) return "";
342
+ return Object.entries(env)
343
+ .sort(([a], [b]) => a.localeCompare(b))
344
+ .map(([key, value]) => `${key}="${escapeBashEnvValueForDisplay(value)}"`)
345
+ .join(" ");
346
+ }
347
+
348
+ function unescapePartialJsonString(value: string): string {
349
+ let output = "";
350
+ for (let index = 0; index < value.length; index += 1) {
351
+ const char = value[index];
352
+ if (char !== "\\") {
353
+ output += char;
354
+ continue;
355
+ }
356
+ const next = value[index + 1];
357
+ if (!next) {
358
+ output += "\\";
359
+ break;
360
+ }
361
+ index += 1;
362
+ switch (next) {
363
+ case '"':
364
+ output += '"';
365
+ break;
366
+ case "\\":
367
+ output += "\\";
368
+ break;
369
+ case "/":
370
+ output += "/";
371
+ break;
372
+ case "b":
373
+ output += "\b";
374
+ break;
375
+ case "f":
376
+ output += "\f";
377
+ break;
378
+ case "n":
379
+ output += "\n";
380
+ break;
381
+ case "r":
382
+ output += "\r";
383
+ break;
384
+ case "t":
385
+ output += "\t";
386
+ break;
387
+ case "u": {
388
+ const hex = value.slice(index + 1, index + 5);
389
+ if (/^[0-9a-fA-F]{4}$/u.test(hex)) {
390
+ output += String.fromCharCode(Number.parseInt(hex, 16));
391
+ index += 4;
392
+ } else {
393
+ output += "\\u";
394
+ }
395
+ break;
396
+ }
397
+ default:
398
+ output += next;
399
+ }
400
+ }
401
+ return output;
402
+ }
403
+
404
+ function extractPartialBashEnv(partialJson: string | undefined): Record<string, string> | undefined {
405
+ if (!partialJson) return undefined;
406
+ const envStart = partialJson.search(/"env"\s*:\s*\{/u);
407
+ if (envStart === -1) return undefined;
408
+ const objectStart = partialJson.indexOf("{", envStart);
409
+ if (objectStart === -1) return undefined;
410
+ const envBody = partialJson.slice(objectStart + 1);
411
+ const env: Record<string, string> = {};
412
+ const matcher = /"([A-Za-z_][A-Za-z0-9_]*)"\s*:\s*"((?:\\.|[^"\\])*)(?:"|$)/gu;
413
+ for (const match of envBody.matchAll(matcher)) {
414
+ env[match[1]!] = unescapePartialJsonString(match[2]!);
415
+ }
416
+ return Object.keys(env).length > 0 ? env : undefined;
417
+ }
418
+
419
+ function formatTimeoutClampNotice(
420
+ requestedTimeoutSec: number,
421
+ effectiveTimeoutSec: number,
422
+ maxTimeout: number,
423
+ ): string | undefined {
424
+ if (requestedTimeoutSec === effectiveTimeoutSec) return undefined;
425
+ const cappedByGlobal = maxTimeout > 0 && effectiveTimeoutSec === maxTimeout && maxTimeout < TOOL_TIMEOUTS.bash.max;
426
+ const limit = cappedByGlobal
427
+ ? `global tools.maxTimeout ceiling ${maxTimeout}s`
428
+ : `allowed range ${TOOL_TIMEOUTS.bash.min}-${TOOL_TIMEOUTS.bash.max}s`;
429
+ return `Timeout clamped to ${effectiveTimeoutSec}s (requested ${requestedTimeoutSec}s; ${limit}).`;
430
+ }
431
+
432
+ function formatWallTimeSeconds(wallTimeMs: number): string {
433
+ return (wallTimeMs / 1000).toFixed(2);
434
+ }
435
+
436
+ function formatWallTimeNotice(wallTimeMs: number): string {
437
+ return `Wall time: ${formatWallTimeSeconds(wallTimeMs)} seconds`;
438
+ }
439
+
440
+ function formatExitCodeNotice(exitCode: number): string {
441
+ return `Command exited with code ${exitCode}`;
442
+ }
443
+
444
+ function formatBackgroundNotice(jobId: string): string {
445
+ return `Backgrounded as job ${jobId}; result will be delivered automatically.`;
446
+ }
447
+
448
+ /**
449
+ * Strip the trailing occurrence of `notice` (plus a single surrounding newline
450
+ * on each side) so the TUI can echo the value via a styled footer label
451
+ * instead of repeating it verbatim in the output pane. The notice is
452
+ * reconstructed from the same value the result was tagged with, so a literal
453
+ * sub-string match never strips a coincidental in-output token — only the
454
+ * exact line we appended in #buildCompletedResult.
455
+ */
456
+ function stripTrailingNotice(text: string, notice: string): string {
457
+ const idx = text.lastIndexOf(notice);
458
+ if (idx === -1) return text;
459
+ let start = idx;
460
+ let end = idx + notice.length;
461
+ if (text[start - 1] === "\n") start -= 1;
462
+ if (text[end] === "\n") end += 1;
463
+ return (text.slice(0, start) + text.slice(end)).trimEnd();
464
+ }
465
+
466
+ function stripWallTimeNotice(text: string, wallTimeMs: number | undefined): string {
467
+ if (wallTimeMs === undefined) return text;
468
+ return stripTrailingNotice(text, formatWallTimeNotice(wallTimeMs));
469
+ }
470
+
471
+ function stripExitCodeNotice(text: string, exitCode: number | undefined): string {
472
+ if (exitCode === undefined) return text;
473
+ return stripTrailingNotice(text, formatExitCodeNotice(exitCode));
474
+ }
475
+
476
+ function stripBackgroundNotice(text: string, async: BashToolDetails["async"] | undefined): string {
477
+ if (async?.state !== "running") return text;
478
+ return stripTrailingNotice(text, formatBackgroundNotice(async.jobId));
479
+ }
480
+
481
+ /**
482
+ * Bash tool implementation.
483
+ *
484
+ * Executes bash commands with optional timeout and working directory.
485
+ */
486
+ export class BashTool implements AgentTool<typeof bashSchemaBase | typeof bashSchemaWithAsync, BashToolDetails> {
487
+ readonly name = "bash";
488
+ readonly approval = (args: unknown): ToolApprovalDecision => {
489
+ const rawCommand = (args as Partial<BashToolInput>).command;
490
+ const command = typeof rawCommand === "string" ? rawCommand : "";
491
+ const patternRules = getBashApprovalPatternRules(this.session.settings.get("bash.patterns"));
492
+ const patternRule = findBashApprovalPatternRule(command, patternRules);
493
+ if (patternRule?.approval === "deny") {
494
+ return {
495
+ tier: "exec",
496
+ override: true,
497
+ policy: "deny",
498
+ reason: `Blocked by bash pattern: ${patternRule.match}`,
499
+ };
500
+ }
501
+ if (command !== "" && CRITICAL_BASH_PATTERNS.some(pattern => pattern.test(command))) {
502
+ return { tier: "exec", override: true, reason: "Critical pattern detected" };
503
+ }
504
+ if (patternRule?.approval === "allow") return { tier: "write", policy: "allow" };
505
+ if (patternRule?.approval === "prompt") {
506
+ return {
507
+ tier: "exec",
508
+ override: true,
509
+ policy: "prompt",
510
+ reason: `Prompt required by bash pattern: ${patternRule.match}`,
511
+ };
512
+ }
513
+ return "exec";
514
+ };
515
+ readonly formatApprovalDetails = (args: unknown): string[] => {
516
+ const rawCommand = (args as Partial<BashToolInput>).command;
517
+ const command = typeof rawCommand === "string" ? rawCommand : "(missing)";
518
+ return [`Command: ${truncateForPrompt(command)}`];
519
+ };
520
+ readonly label = "Bash";
521
+ readonly loadMode = "essential";
522
+ get description(): string {
523
+ const evalBackends = resolveEvalBackends(this.session);
524
+ const isToolActive = (name: string, fallback: boolean): boolean => this.session.isToolActive?.(name) ?? fallback;
525
+ return prompt.render(bashDescription, {
526
+ asyncEnabled: this.#asyncEnabled,
527
+ autoBackgroundEnabled: this.#autoBackgroundEnabled,
528
+ autoBackgroundThresholdSeconds: Math.max(0, Math.floor(this.#autoBackgroundThresholdMs / 1000)),
529
+ hasAstGrep: isToolActive("ast_grep", this.session.settings.get("astGrep.enabled")),
530
+ hasAstEdit: isToolActive("ast_edit", this.session.settings.get("astEdit.enabled")),
531
+ hasGrep: isToolActive("grep", this.session.settings.get("grep.enabled")),
532
+ hasGlob: isToolActive("glob", this.session.settings.get("glob.enabled")),
533
+ hasRead: isToolActive("read", true),
534
+ hasLaunch: isToolActive("hub", this.session.settings.get("launch.enabled")),
535
+ hasEval: isToolActive(
536
+ "eval",
537
+ evalBackends.python || evalBackends.js || evalBackends.ruby || evalBackends.julia,
538
+ ),
539
+ hasShellBuiltins: !shellBuiltinsDisabled(this.session.settings),
540
+ isWindows: process.platform === "win32",
541
+ });
542
+ }
543
+ readonly parameters: BashToolSchema;
544
+ // Non-pty calls run alongside each other (the executor isolates overlapping
545
+ // runs on the same shell session); pty takes over the terminal UI and must
546
+ // run alone.
547
+ readonly concurrency = (args: Partial<BashToolInput>): "shared" | "exclusive" =>
548
+ args.pty === true ? "exclusive" : "shared";
549
+ readonly strict = true;
550
+ readonly #asyncEnabled: boolean;
551
+ readonly #autoBackgroundEnabled: boolean;
552
+ readonly #autoBackgroundThresholdMs: number;
553
+
554
+ constructor(private readonly session: ToolSession) {
555
+ this.#asyncEnabled = this.session.settings.get("async.enabled");
556
+ this.#autoBackgroundEnabled = this.session.settings.get("bash.autoBackground.enabled");
557
+ this.#autoBackgroundThresholdMs = Math.max(
558
+ 0,
559
+ Math.floor(
560
+ this.session.settings.get("bash.autoBackground.thresholdMs") ?? DEFAULT_AUTO_BACKGROUND_THRESHOLD_MS,
561
+ ),
562
+ );
563
+ this.parameters = this.#asyncEnabled ? bashSchemaWithAsync : bashSchemaBase;
564
+ }
565
+
566
+ #formatResultOutput(result: BashResult | BashInteractiveResult): string {
567
+ const outputText = normalizeResultOutput(result);
568
+ return outputText || "(no output)";
569
+ }
570
+
571
+ /**
572
+ * Throw for outcomes that are *not* a completed command: user aborts and a
573
+ * missing exit status. Timeouts are handled separately by
574
+ * #buildCompletedResult, which returns a non-throwing error result with
575
+ * details.timedOut=true so the renderer can show a warning border. The
576
+ * foreground and bridge callers plus the async job manager rely on these
577
+ * throwing so cancellations surface as aborts and jobs are recorded as
578
+ * failed. A definite non-zero exit is a completed command that failed;
579
+ * #buildCompletedResult surfaces it as an error *result* (carrying
580
+ * execution details) rather than a throw.
581
+ */
582
+ #throwIfUnfinished(
583
+ result: BashResult | BashInteractiveResult,
584
+ timeoutSec: number | undefined,
585
+ outputText: string,
586
+ ): void {
587
+ if (result.cancelled) {
588
+ // Local executor output already carries a leading `[Command cancelled]`
589
+ // notice from the sink; PTY/bridge output does not, so annotate only
590
+ // the latter.
591
+ const out = normalizeResultOutput(result);
592
+ const annotated = out.startsWith("[Command cancelled]") ? out : out ? `${out}\n\n[Command aborted]` : out;
593
+ throw new ToolError(annotated || "Command aborted");
594
+ }
595
+ if (result.timedOut === true) {
596
+ const out = normalizeResultOutput(result);
597
+ const message =
598
+ timeoutSec === undefined ? "Command timed out" : `Command timed out after ${timeoutSec} seconds`;
599
+ throw new ToolError(out ? `${out}\n\n[${message}]` : message);
600
+ }
601
+ if (result.exitCode === undefined) {
602
+ throw new ToolError(`${outputText}\n\nCommand failed: missing exit status`);
603
+ }
604
+ }
605
+
606
+ async #buildCompletedResult(
607
+ result: BashResult | BashInteractiveResult,
608
+ timeoutSec: number | undefined,
609
+ options: {
610
+ requestedTimeoutSec?: number;
611
+ notices?: readonly string[];
612
+ terminalId?: string;
613
+ wallTimeMs?: number;
614
+ } = {},
615
+ ): Promise<AgentToolResult<BashToolDetails>> {
616
+ const exitCode = result.exitCode;
617
+ const failedExit = exitCode !== undefined && exitCode !== 0;
618
+
619
+ const outputLines = [this.#formatResultOutput(result)];
620
+ const notices: string[] = [];
621
+ if (options.wallTimeMs !== undefined) {
622
+ notices.push(formatWallTimeNotice(options.wallTimeMs));
623
+ }
624
+ if (options.notices) {
625
+ for (const notice of options.notices) {
626
+ if (notice) notices.push(notice);
627
+ }
628
+ }
629
+ if (notices.length > 0) outputLines.push("", ...notices);
630
+ if (failedExit) outputLines.push("", formatExitCodeNotice(exitCode));
631
+ const outputText = outputLines.join("\n");
632
+
633
+ // Timeouts are not failures — the command ran its course. Return an error
634
+ // result (isError=true for the model) but flag timedOut so the renderer
635
+ // uses a warning border instead of error red. Both interactive and
636
+ // non-interactive results carry an explicit `timedOut` field from the
637
+ // executor/PTY layer.
638
+ const isTimeout = result.timedOut === true;
639
+
640
+ const details: BashToolDetails = {};
641
+ if (timeoutSec === undefined) {
642
+ details.timeoutDisabled = true;
643
+ } else {
644
+ details.timeoutSeconds = timeoutSec;
645
+ }
646
+ if (options.requestedTimeoutSec !== undefined && options.requestedTimeoutSec !== timeoutSec) {
647
+ details.requestedTimeoutSeconds = options.requestedTimeoutSec;
648
+ }
649
+ if (options.terminalId !== undefined) {
650
+ details.terminalId = options.terminalId;
651
+ }
652
+ if (options.wallTimeMs !== undefined) {
653
+ details.wallTimeMs = options.wallTimeMs;
654
+ }
655
+ if (failedExit) {
656
+ details.exitCode = exitCode;
657
+ }
658
+
659
+ // Final-defense inline cap config, shared by the timeout and normal
660
+ // completion paths. The sink already bounds inline bodies to the spill
661
+ // threshold, so with the notice slack this only fires on paths that
662
+ // bypass the sink (client-bridge terminals, minimizer misses). When the
663
+ // sink spilled, its artifact already holds the full raw stream — reuse
664
+ // that id instead of saving a second (already-truncated) copy, so the
665
+ // `[raw output: artifact://N]` footer and the truncation notice agree.
666
+ const inlineCap = {
667
+ maxBytes: resolveInlineByteCapBudget(this.session.settings),
668
+ saveArtifact: (full: string) => result.artifactId ?? saveBashOriginalArtifact(this.session, full),
669
+ };
670
+
671
+ if (isTimeout) {
672
+ details.timedOut = true;
673
+ const message =
674
+ timeoutSec === undefined ? "Command timed out" : `Command timed out after ${timeoutSec} seconds`;
675
+ // executeBash has already emitted this leading sink notice. PTY output
676
+ // has not, so provide the LLM-facing annotation exactly once.
677
+ if (!normalizeResultOutput(result).startsWith(`[${message}]\n`)) {
678
+ outputLines.push("", `[${message}]`);
679
+ }
680
+ const timeoutOutputText = await enforceInlineByteCap(outputLines.join("\n"), inlineCap);
681
+ return toolResult(details)
682
+ .text(timeoutOutputText)
683
+ .truncationFromSummary(result, { direction: "tail" })
684
+ .error()
685
+ .done();
686
+ }
687
+
688
+ // Non-timeout cancellations and missing exit status still propagate as thrown errors.
689
+ this.#throwIfUnfinished(result, timeoutSec, outputText);
690
+
691
+ // No-op for already-bounded output; see `inlineCap` above.
692
+ const cappedOutputText = await enforceInlineByteCap(outputText, inlineCap);
693
+
694
+ const resultBuilder = toolResult(details)
695
+ .text(cappedOutputText)
696
+ .truncationFromSummary(result, { direction: "tail" });
697
+ if (failedExit) resultBuilder.error();
698
+ return resultBuilder.done();
699
+ }
700
+
701
+ #buildBackgroundStartResult(
702
+ jobId: string,
703
+ previewText: string,
704
+ timeoutSec: number | undefined,
705
+ options: { requestedTimeoutSec?: number; notices?: readonly string[] } = {},
706
+ ): AgentToolResult<BashToolDetails> {
707
+ const details: BashToolDetails = {
708
+ async: { state: "running", jobId, type: "bash" },
709
+ };
710
+ if (timeoutSec === undefined) {
711
+ details.timeoutDisabled = true;
712
+ } else {
713
+ details.timeoutSeconds = timeoutSec;
714
+ }
715
+ if (options.requestedTimeoutSec !== undefined && options.requestedTimeoutSec !== timeoutSec) {
716
+ details.requestedTimeoutSeconds = options.requestedTimeoutSec;
717
+ }
718
+ const lines: string[] = [];
719
+ const trimmedPreview = previewText.trimEnd();
720
+ if (trimmedPreview.length > 0) {
721
+ lines.push(trimmedPreview, "");
722
+ }
723
+ if (options.notices?.length) {
724
+ lines.push(...options.notices, "");
725
+ }
726
+ lines.push(formatBackgroundNotice(jobId));
727
+ return {
728
+ content: [{ type: "text", text: lines.join("\n") }],
729
+ details,
730
+ };
731
+ }
732
+
733
+ #extractTextResult(result: AgentToolResult<BashToolDetails>): string {
734
+ return result.content.find(block => block.type === "text")?.text ?? "";
735
+ }
736
+
737
+ #startManagedBashJob(options: {
738
+ command: string;
739
+ commandCwd: string;
740
+ timeoutMs: number | undefined;
741
+ timeoutSec: number | undefined;
742
+ requestedTimeoutSec?: number;
743
+ notices?: readonly string[];
744
+
745
+ resolvedEnv?: Record<string, string>;
746
+ onUpdate?: AgentToolUpdateCallback<BashToolDetails>;
747
+ forwardUpdates: boolean;
748
+ }): ManagedBashJobHandle {
749
+ const manager = this.session.asyncJobManager;
750
+ if (!manager) {
751
+ throw new ToolError("Background job manager unavailable for this session.");
752
+ }
753
+
754
+ const label = options.command.length > 120 ? `${options.command.slice(0, 117)}...` : options.command;
755
+ let latestText = "";
756
+ let forwardUpdates = options.forwardUpdates;
757
+ const completion = Promise.withResolvers<ManagedBashJobCompletion>();
758
+
759
+ const jobId = manager.register(
760
+ "bash",
761
+ label,
762
+ async ({ jobId, signal: runSignal, reportProgress }) => {
763
+ const { path: artifactPath, id: artifactId } = (await this.session.allocateOutputArtifact?.("bash")) ?? {};
764
+ const tailBuffer = new TailBuffer(DEFAULT_MAX_BYTES);
765
+ const wallTimeStart = performance.now();
766
+ try {
767
+ const result = await executeBash(options.command, {
768
+ cwd: options.commandCwd,
769
+ sessionKey: `${this.session.getSessionId?.() ?? ""}:async:${jobId}`,
770
+ timeout: options.timeoutMs ?? 0,
771
+ signal: runSignal,
772
+ env: options.resolvedEnv,
773
+ artifactPath,
774
+ artifactId,
775
+ onChunk: chunk => {
776
+ tailBuffer.append(chunk);
777
+ latestText = tailBuffer.text();
778
+ void reportProgress(latestText, { async: { state: "running", jobId, type: "bash" } });
779
+ },
780
+ onMinimizedSave: originalText => saveBashOriginalArtifact(this.session, originalText),
781
+ });
782
+ const wallTimeMs = performance.now() - wallTimeStart;
783
+ const finalResult = await this.#buildCompletedResult(result, options.timeoutSec, {
784
+ requestedTimeoutSec: options.requestedTimeoutSec,
785
+ notices: options.notices ?? [],
786
+ wallTimeMs,
787
+ });
788
+ const finalText = this.#extractTextResult(finalResult);
789
+ latestText = finalText;
790
+ // Hand the detailed result to the foreground auto-background
791
+ // waiter (which renders it, footer included) before deciding
792
+ // the job's terminal state.
793
+ completion.resolve({ kind: "completed", result: finalResult });
794
+ if (finalResult.isError === true) {
795
+ // A non-zero exit is a completed command that failed. Re-enter
796
+ // the failure path so the job manager records it as failed and
797
+ // delivers the error text, matching prior throw-based behavior.
798
+ throw new ToolError(finalText);
799
+ }
800
+ await reportProgress(finalText, { async: { state: "completed", jobId, type: "bash" } });
801
+ return finalText;
802
+ } catch (error) {
803
+ const message = error instanceof Error ? error.message : String(error);
804
+ latestText = message;
805
+ completion.resolve({ kind: "failed", error });
806
+ await reportProgress(message, { async: { state: "failed", jobId, type: "bash" } });
807
+ throw error;
808
+ }
809
+ },
810
+ {
811
+ ownerId: this.session.getAgentId?.() ?? undefined,
812
+ onProgress: async text => {
813
+ latestText = text;
814
+ if (!forwardUpdates) return;
815
+ await options.onUpdate?.({
816
+ content: [{ type: "text", text }],
817
+ details: {},
818
+ });
819
+ },
820
+ },
821
+ );
822
+
823
+ return {
824
+ jobId,
825
+ completion: completion.promise,
826
+ getLatestText: () => latestText,
827
+ stopUpdates: () => {
828
+ forwardUpdates = false;
829
+ },
830
+ };
831
+ }
832
+
833
+ async #waitForManagedBashJob(
834
+ job: ManagedBashJobHandle,
835
+ thresholdMs: number,
836
+ signal?: AbortSignal,
837
+ steeringSignal?: AbortSignal,
838
+ ): Promise<ManagedBashJobCompletion | { kind: "running" } | { kind: "steer" } | { kind: "aborted" }> {
839
+ if (signal?.aborted) {
840
+ return { kind: "aborted" };
841
+ }
842
+ if (steeringSignal?.aborted) {
843
+ return { kind: "steer" };
844
+ }
845
+
846
+ const waiters: Array<
847
+ Promise<ManagedBashJobCompletion | { kind: "running" } | { kind: "steer" } | { kind: "aborted" }>
848
+ > = [job.completion, Bun.sleep(thresholdMs).then(() => ({ kind: "running" as const }))];
849
+
850
+ if (!signal && !steeringSignal) {
851
+ return await Promise.race(waiters);
852
+ }
853
+
854
+ const { promise: abortedPromise, resolve: resolveAborted } = Promise.withResolvers<{ kind: "aborted" }>();
855
+ const onAbort = () => resolveAborted({ kind: "aborted" });
856
+ const { promise: steerPromise, resolve: resolveSteer } = Promise.withResolvers<{ kind: "steer" }>();
857
+ const onSteer = () => resolveSteer({ kind: "steer" });
858
+ if (signal) {
859
+ signal.addEventListener("abort", onAbort, { once: true });
860
+ waiters.push(abortedPromise);
861
+ }
862
+ if (steeringSignal) {
863
+ steeringSignal.addEventListener("abort", onSteer, { once: true });
864
+ waiters.push(steerPromise);
865
+ }
866
+ try {
867
+ return await Promise.race(waiters);
868
+ } finally {
869
+ signal?.removeEventListener("abort", onAbort);
870
+ steeringSignal?.removeEventListener("abort", onSteer);
871
+ }
872
+ }
873
+
874
+ #resolveAutoBackgroundWaitMs(timeoutMs: number | undefined): number {
875
+ if (this.#autoBackgroundThresholdMs <= 0) return 0;
876
+ if (timeoutMs === undefined) return this.#autoBackgroundThresholdMs;
877
+ const timeoutBufferMs = 1_000;
878
+ return Math.max(0, Math.min(this.#autoBackgroundThresholdMs, timeoutMs - timeoutBufferMs));
879
+ }
880
+
881
+ async execute(
882
+ _toolCallId: string,
883
+ {
884
+ command: rawCommand,
885
+ env: rawEnv,
886
+ timeout: rawTimeout = 300,
887
+ cwd,
888
+
889
+ async: asyncRequested = false,
890
+ pty = false,
891
+ }: BashToolInput,
892
+ signal?: AbortSignal,
893
+ onUpdate?: AgentToolUpdateCallback<BashToolDetails>,
894
+ ctx?: AgentToolContext,
895
+ ): Promise<AgentToolResult<BashToolDetails>> {
896
+ let command = rawCommand;
897
+ const env = normalizeBashEnv(rawEnv);
898
+
899
+ // Extract leading `cd <path> && ...` into cwd when the model ignores the cwd parameter.
900
+ // Constrained to a single line so a `&&` that sits on a later line of a multiline
901
+ // script can't pull the entire script into the "cwd" capture.
902
+ if (!cwd) {
903
+ const cdMatch = command.match(/^cd[ \t]+((?:[^&\\\n\r]|\\.)+?)[ \t]*&&[ \t]*/);
904
+ // Skip extraction when the path needs shell expansion ($VAR, $(...),
905
+ // backticks) — resolveToCwd only expands `~`, so routing those through
906
+ // cwd would reject commands the shell itself handles fine.
907
+ if (cdMatch && !/[$`(]/.test(cdMatch[1])) {
908
+ cwd = cdMatch[1].trim().replace(/^["']|["']$/g, "");
909
+ command = command.slice(cdMatch[0].length);
910
+ }
911
+ }
912
+ if (asyncRequested && !this.#asyncEnabled) {
913
+ throw new ToolError("Async bash execution is disabled. Enable async.enabled to use async mode.");
914
+ }
915
+
916
+ // Check both the original command and the cwd-normalized command so
917
+ // leading `cd ... &&` wrappers do not hide either shell-navigation rules
918
+ // or the dedicated-tool command that follows the directory change.
919
+ if (this.session.settings.get("bashInterceptor.enabled")) {
920
+ const rules = this.session.settings.getBashInterceptorRules();
921
+ const commandsToCheck = rawCommand === command ? [command] : [rawCommand, command];
922
+ for (const commandToCheck of commandsToCheck) {
923
+ const interception = checkBashInterception(commandToCheck, ctx?.toolNames ?? [], rules);
924
+ if (interception.block) {
925
+ throw new ToolError(interception.message ?? "Command blocked");
926
+ }
927
+ }
928
+ }
929
+
930
+ const internalUrlOptions: InternalUrlExpansionOptions = {
931
+ skills: this.session.skills ?? [],
932
+ internalRouter: InternalUrlRouter.instance(),
933
+ cwd: this.session.cwd,
934
+ localOptions: {
935
+ getArtifactsDir: this.session.getArtifactsDir,
936
+ getSessionId: this.session.getSessionId,
937
+ },
938
+ };
939
+ command = await expandInternalUrls(command, { ...internalUrlOptions, ensureLocalParentDirs: true });
940
+ const resolvedEnv = env
941
+ ? Object.fromEntries(
942
+ await Promise.all(
943
+ Object.entries(env).map(async ([key, value]) => [
944
+ key,
945
+ await expandInternalUrls(value, {
946
+ ...internalUrlOptions,
947
+ ensureLocalParentDirs: true,
948
+ noEscape: true,
949
+ }),
950
+ ]),
951
+ ),
952
+ )
953
+ : undefined;
954
+
955
+ // Resolve protocol URLs (skill://, agent://, etc.) in extracted cwd.
956
+ if (cwd?.includes("://") || cwd?.includes("local:/")) {
957
+ cwd = await expandInternalUrls(cwd, { ...internalUrlOptions, noEscape: true });
958
+ }
959
+
960
+ // Best-effort cache invalidation: drop github-cache rows for any issue/PR
961
+ // number touched by a mutating `gh` subcommand inside this bash call so
962
+ // subsequent issue:// / pr:// reads pick up the post-mutation state
963
+ // instead of the cached pre-mutation snapshot.
964
+ invalidateGithubCacheForBashCommand(command);
965
+
966
+ const commandCwd = cwd ? resolveToCwd(cwd, this.session.cwd) : this.session.cwd;
967
+ let cwdStat: fs.Stats;
968
+ try {
969
+ cwdStat = await fs.promises.stat(commandCwd);
970
+ } catch (err) {
971
+ if (isEnoent(err)) {
972
+ throw new ToolError(`Working directory does not exist: ${commandCwd}`);
973
+ }
974
+ throw err;
975
+ }
976
+ if (!cwdStat.isDirectory()) {
977
+ throw new ToolError(`Working directory is not a directory: ${commandCwd}`);
978
+ }
979
+
980
+ // A timeout of 0 is an explicit long-running-command contract: the user
981
+ // must still cancel the call or job, but OMP does not impose a deadline.
982
+ const requestedTimeoutSec = rawTimeout;
983
+ const timeoutDisabled = requestedTimeoutSec === 0;
984
+ const maxTimeout = this.session.settings.get("tools.maxTimeout");
985
+ const timeoutSec = timeoutDisabled ? undefined : clampTimeout("bash", requestedTimeoutSec, maxTimeout);
986
+ const timeoutMs = timeoutSec === undefined ? undefined : timeoutSec * 1000;
987
+ const pendingNotices: string[] = [];
988
+ if (timeoutSec !== undefined) {
989
+ const timeoutClampNotice = formatTimeoutClampNotice(requestedTimeoutSec, timeoutSec, maxTimeout);
990
+ if (timeoutClampNotice) pendingNotices.push(timeoutClampNotice);
991
+ }
992
+
993
+ if (asyncRequested) {
994
+ if (!this.session.asyncJobManager) {
995
+ throw new ToolError("Async job manager unavailable for this session.");
996
+ }
997
+ const job = this.#startManagedBashJob({
998
+ command,
999
+ commandCwd,
1000
+ timeoutMs,
1001
+ timeoutSec,
1002
+ requestedTimeoutSec,
1003
+ notices: pendingNotices,
1004
+
1005
+ resolvedEnv,
1006
+ onUpdate,
1007
+ forwardUpdates: false,
1008
+ });
1009
+ return this.#buildBackgroundStartResult(job.jobId, "", timeoutSec, {
1010
+ requestedTimeoutSec,
1011
+ notices: pendingNotices,
1012
+ });
1013
+ }
1014
+
1015
+ // The client-bridge terminal provides a live terminal card in the editor;
1016
+ // when available it wins over auto-backgrounding (both are opt-in, and
1017
+ // auto-background would otherwise silently disable the terminal route).
1018
+ const clientBridge = this.session.getClientBridge?.();
1019
+ const bridgeTerminalAvailable = Boolean(
1020
+ clientBridge?.capabilities.terminal && clientBridge.createTerminal && !pty,
1021
+ );
1022
+
1023
+ const autoBgManager = this.session.asyncJobManager;
1024
+ // At the running-job cap, fall through to direct foreground execution
1025
+ // instead of failing every bash call until a slot frees up.
1026
+ if (
1027
+ this.#autoBackgroundEnabled &&
1028
+ !pty &&
1029
+ !bridgeTerminalAvailable &&
1030
+ autoBgManager &&
1031
+ !autoBgManager.atCapacity
1032
+ ) {
1033
+ const autoBackgroundWaitMs = this.#resolveAutoBackgroundWaitMs(timeoutMs);
1034
+ const startBackgrounded = autoBackgroundWaitMs === 0;
1035
+ const job = this.#startManagedBashJob({
1036
+ command,
1037
+ commandCwd,
1038
+ timeoutMs,
1039
+ timeoutSec,
1040
+ requestedTimeoutSec,
1041
+ notices: pendingNotices,
1042
+
1043
+ resolvedEnv,
1044
+ onUpdate,
1045
+ forwardUpdates: !startBackgrounded,
1046
+ });
1047
+ if (startBackgrounded) {
1048
+ return this.#buildBackgroundStartResult(job.jobId, "", timeoutSec, {
1049
+ requestedTimeoutSec,
1050
+ notices: pendingNotices,
1051
+ });
1052
+ }
1053
+ // Suppress the completion delivery up front so a job finishing while we
1054
+ // foreground-wait cannot also be injected by the delivery loop. Lifted
1055
+ // via resumeDeliveries() if we end up backgrounding after all.
1056
+ autoBgManager.acknowledgeDeliveries([job.jobId]);
1057
+ const waitResult = await this.#waitForManagedBashJob(
1058
+ job,
1059
+ autoBackgroundWaitMs,
1060
+ signal,
1061
+ ctx?.toolCall?.steeringSignal,
1062
+ );
1063
+ if (waitResult.kind === "completed") {
1064
+ return waitResult.result;
1065
+ }
1066
+ if (waitResult.kind === "failed") {
1067
+ throw waitResult.error;
1068
+ }
1069
+ if (waitResult.kind === "aborted") {
1070
+ autoBgManager.cancel(job.jobId);
1071
+ throw new ToolAbortError(job.getLatestText() || "Command aborted");
1072
+ }
1073
+ job.stopUpdates();
1074
+ autoBgManager.resumeDeliveries([job.jobId]);
1075
+ // "steer": a queued user/peer message arrived mid-wait — background
1076
+ // the command (it keeps running) so the message injects promptly.
1077
+ const notices =
1078
+ waitResult.kind === "steer"
1079
+ ? [...pendingNotices, "Backgrounded early to handle an incoming message; the command keeps running."]
1080
+ : pendingNotices;
1081
+ return this.#buildBackgroundStartResult(job.jobId, job.getLatestText(), timeoutSec, {
1082
+ requestedTimeoutSec,
1083
+ notices,
1084
+ });
1085
+ }
1086
+
1087
+ // Fold direnv/devenv env into (command, env) ONCE for the two backends
1088
+ // that bypass `executeBash` — the ACP client terminal and the PTY. The
1089
+ // `executeBash` branch below is intentionally excluded: it runs its own
1090
+ // preflight internally, so routing the pre-applied command there too
1091
+ // would double-apply the unset prefix and re-merge the env. No
1092
+ // `commandPrefix` here: ACP applies the shell prefix via
1093
+ // `wrapShellLineForClientTerminal`, and the PTY path never wrapped one.
1094
+ // `callerTimeoutMs` clamps the direnv load to a positive command timeout
1095
+ // (the backend's own timeout is installed only after this await), matching
1096
+ // the executeBash branch so a cold `.envrc` can't outlast a short call.
1097
+ const backendPreflight =
1098
+ (clientBridge?.capabilities.terminal && clientBridge.createTerminal && !pty) ||
1099
+ canUseInteractiveBashPty(pty, ctx)
1100
+ ? await applyDirenvPreflight(command, commandCwd, {
1101
+ callerEnv: resolvedEnv,
1102
+ signal,
1103
+ timeoutMs: this.session.settings.get("bash.direnvLoadTimeoutMs"),
1104
+ callerTimeoutMs: timeoutMs,
1105
+ direnvSetting: this.session.settings.get("bash.direnv"),
1106
+ })
1107
+ : undefined;
1108
+
1109
+ // Route through the client terminal when the client advertises the terminal capability.
1110
+ // Skip when pty=true (PTY needs the local terminal UI).
1111
+ if (clientBridge?.capabilities.terminal && clientBridge.createTerminal && !pty) {
1112
+ // Invariant (ACP terminal bridge): createTerminal has no signal in its
1113
+ // contract; allocation cannot be cancelled retroactively. Guard before
1114
+ // allocation. Shared timeout helper / pure AbortSignal fusion rejected:
1115
+ // we need explicit kill-before-read ordering and distinct abort vs
1116
+ // timeout result shapes. Per-route race retained for testability.
1117
+ if (signal?.aborted) {
1118
+ throw new ToolAbortError("Command aborted");
1119
+ }
1120
+
1121
+ const bridgeWallTimeStart = performance.now();
1122
+ const killGraceMs = 1000;
1123
+ const outputSnapshotGraceMs = 2000;
1124
+ // Cancellable timeout: a bare Bun.sleep(timeoutMs) would leave a live,
1125
+ // ref'd timer for the full command timeout after fast completions —
1126
+ // accumulating timers and delaying process shutdown in SDK/headless use.
1127
+ // `timeoutMs` is optional (#4642): without one, no timer is armed and
1128
+ // the promise simply never resolves.
1129
+ const { promise: timeoutPromise, resolve: resolveTimeout } = Promise.withResolvers<{
1130
+ kind: "timeout";
1131
+ }>();
1132
+ const timeoutTimer = timeoutMs ? setTimeout(() => resolveTimeout({ kind: "timeout" }), timeoutMs) : undefined;
1133
+ const { promise: abortedP, resolve: resolveAborted } = Promise.withResolvers<void>();
1134
+ let handle: ClientBridgeTerminalHandle | undefined;
1135
+ let killStarted = false;
1136
+ const fireKill = (): Promise<void> => {
1137
+ if (killStarted) return Promise.resolve();
1138
+ const currentHandle = handle;
1139
+ if (!currentHandle) return Promise.resolve();
1140
+ killStarted = true;
1141
+ return currentHandle.kill().catch((error: unknown) => {
1142
+ logger.warn("ACP terminal kill failed", { terminalId: currentHandle.terminalId, error });
1143
+ });
1144
+ };
1145
+ const cleanupLateCreate = (createP: Promise<ClientBridgeTerminalHandle>): void => {
1146
+ void createP
1147
+ .then(async lateHandle => {
1148
+ try {
1149
+ await lateHandle.kill();
1150
+ } catch (error) {
1151
+ logger.warn("ACP terminal kill failed", { terminalId: lateHandle.terminalId, error });
1152
+ }
1153
+ try {
1154
+ await lateHandle.release();
1155
+ } catch (error) {
1156
+ logger.warn("ACP terminal release failed", { terminalId: lateHandle.terminalId, error });
1157
+ }
1158
+ })
1159
+ .catch((error: unknown) => {
1160
+ logger.warn("ACP terminal create failed after cancellation", { error });
1161
+ });
1162
+ };
1163
+ const onAbortSignal = () => {
1164
+ resolveAborted();
1165
+ void fireKill();
1166
+ };
1167
+ signal?.addEventListener("abort", onAbortSignal, { once: true });
1168
+
1169
+ try {
1170
+ // direnv-transformed command (carries any `unset -v` prefix) + merged
1171
+ // env; falls back to the raw command/env when direnv is off/absent.
1172
+ const bridgeCommand = backendPreflight?.command ?? command;
1173
+ const bridgeEnv = backendPreflight?.env ?? resolvedEnv;
1174
+ const shellSpawn = wrapShellLineForClientTerminal(bridgeCommand, this.session.settings.getShellConfig());
1175
+ const createP = clientBridge.createTerminal({
1176
+ command: shellSpawn.command,
1177
+ args: shellSpawn.args,
1178
+ cwd: commandCwd,
1179
+ env: bridgeEnv
1180
+ ? Object.entries(bridgeEnv).map(([name, value]) => ({ name, value: value as string }))
1181
+ : undefined,
1182
+ outputByteLimit: DEFAULT_MAX_BYTES,
1183
+ });
1184
+ const createRaced = await Promise.race([
1185
+ createP.then(createdHandle => ({ kind: "created" as const, handle: createdHandle })),
1186
+ timeoutPromise,
1187
+ abortedP.then(() => ({ kind: "aborted" as const })),
1188
+ ]);
1189
+ if (createRaced.kind === "aborted" || signal?.aborted) {
1190
+ cleanupLateCreate(createP);
1191
+ throw new ToolAbortError("Command aborted");
1192
+ }
1193
+ if (createRaced.kind === "timeout") {
1194
+ cleanupLateCreate(createP);
1195
+ const timedOutResult: BashInteractiveResult = {
1196
+ output: "",
1197
+ exitCode: undefined,
1198
+ cancelled: false,
1199
+ timedOut: true,
1200
+ truncated: false,
1201
+ totalLines: 0,
1202
+ totalBytes: 0,
1203
+ outputLines: 0,
1204
+ outputBytes: 0,
1205
+ };
1206
+ this.#throwIfUnfinished(timedOutResult, timeoutSec, this.#formatResultOutput(timedOutResult));
1207
+ throw new ToolError("Command timed out");
1208
+ }
1209
+
1210
+ handle = createRaced.handle;
1211
+
1212
+ // Emit partial update so the editor can embed the live terminal card.
1213
+ onUpdate?.({ content: [], details: { terminalId: handle.terminalId } });
1214
+
1215
+ const exitPromise = handle.waitForExit();
1216
+ let exitStatus!: ClientBridgeTerminalExitStatus;
1217
+
1218
+ type BridgeRaceResult =
1219
+ | { kind: "exit"; status: ClientBridgeTerminalExitStatus }
1220
+ | { kind: "poll" }
1221
+ | { kind: "timeout" }
1222
+ | { kind: "aborted" };
1223
+
1224
+ const exitRacer = exitPromise.then(status => ({ kind: "exit" as const, status }));
1225
+ const abortRacer = abortedP.then(() => ({ kind: "aborted" as const }));
1226
+ const abortPollRacer = abortedP.then(() => undefined as ClientBridgeTerminalOutput | undefined);
1227
+ const timeoutPollRacer = timeoutPromise.then(() => undefined as ClientBridgeTerminalOutput | undefined);
1228
+ let lastPolledOutput: ClientBridgeTerminalOutput = { output: "", truncated: false };
1229
+
1230
+ // Poll until the process exits, times out, or the caller aborts.
1231
+ for (;;) {
1232
+ const racers: Array<Promise<BridgeRaceResult>> = [
1233
+ exitRacer,
1234
+ timeoutPromise,
1235
+ Bun.sleep(250).then(() => ({ kind: "poll" as const })),
1236
+ ];
1237
+ if (signal) {
1238
+ racers.push(abortRacer);
1239
+ }
1240
+ const raced = await Promise.race(racers);
1241
+
1242
+ if (raced.kind === "aborted" || signal?.aborted) {
1243
+ await Promise.race([fireKill(), Bun.sleep(killGraceMs)]);
1244
+ throw new ToolAbortError("Command aborted");
1245
+ }
1246
+
1247
+ if (raced.kind === "timeout") {
1248
+ // Kill before reading final output so a slow `terminal/output`
1249
+ // RPC cannot let a timed-out command keep running past the
1250
+ // enforced timeout. The handle stays valid post-kill so the
1251
+ // buffered output is still readable.
1252
+ await Promise.race([fireKill(), Bun.sleep(killGraceMs)]);
1253
+ let current = lastPolledOutput;
1254
+ try {
1255
+ current = await Promise.race([
1256
+ handle.currentOutput(),
1257
+ Bun.sleep(outputSnapshotGraceMs).then(() => lastPolledOutput),
1258
+ ]);
1259
+ } catch (error) {
1260
+ logger.warn("ACP terminal final output read failed", {
1261
+ terminalId: handle.terminalId,
1262
+ error,
1263
+ });
1264
+ }
1265
+ const timedOutResult: BashInteractiveResult = {
1266
+ output: current.output,
1267
+ exitCode: undefined,
1268
+ cancelled: false,
1269
+ timedOut: true,
1270
+ truncated: current.truncated,
1271
+ totalLines: current.output.length > 0 ? current.output.split("\n").length : 0,
1272
+ totalBytes: current.output.length,
1273
+ outputLines: current.output.length > 0 ? current.output.split("\n").length : 0,
1274
+ outputBytes: current.output.length,
1275
+ };
1276
+ this.#throwIfUnfinished(timedOutResult, timeoutSec, this.#formatResultOutput(timedOutResult));
1277
+ throw new ToolError("Command timed out");
1278
+ }
1279
+
1280
+ if (raced.kind === "exit") {
1281
+ exitStatus = raced.status;
1282
+ break;
1283
+ }
1284
+
1285
+ // Poll tick: push current output so agent-loop transcript stays consistent.
1286
+ // Race the read against abort/timeout so a stuck `terminal/output` RPC does
1287
+ // not delay cancellation or let the command outlive its deadline.
1288
+ const pollOutput = await Promise.race([handle.currentOutput(), abortPollRacer, timeoutPollRacer]);
1289
+ if (pollOutput === undefined) {
1290
+ // Abort or timeout fired during the poll-tick read; let the next loop
1291
+ // iteration exit via the matching abort/timeout branch.
1292
+ continue;
1293
+ }
1294
+ lastPolledOutput = pollOutput;
1295
+ onUpdate?.({
1296
+ content: [{ type: "text", text: pollOutput.output }],
1297
+ details: { terminalId: handle.terminalId },
1298
+ });
1299
+ }
1300
+
1301
+ // Fetch final output; the terminal is released in the outer finally.
1302
+ let finalOutput = lastPolledOutput;
1303
+ try {
1304
+ finalOutput = await Promise.race([
1305
+ handle.currentOutput(),
1306
+ Bun.sleep(outputSnapshotGraceMs).then(() => lastPolledOutput),
1307
+ ]);
1308
+ } catch (error) {
1309
+ logger.warn("ACP terminal final output read failed", {
1310
+ terminalId: handle.terminalId,
1311
+ error,
1312
+ });
1313
+ }
1314
+
1315
+ // Map exit status: null exitCode with a signal → treat as signal kill (137).
1316
+ const rawExitCode = exitStatus.exitCode;
1317
+ const exitCode: number | undefined =
1318
+ rawExitCode != null ? rawExitCode : exitStatus.signal ? 137 : undefined;
1319
+
1320
+ const outputText = finalOutput.output;
1321
+ const outputByteLen = outputText.length;
1322
+ const outputLineCount = outputText.length > 0 ? outputText.split("\n").length : 0;
1323
+
1324
+ const bridgeResult: BashResult = {
1325
+ output: outputText,
1326
+ exitCode,
1327
+ cancelled: false,
1328
+ truncated: finalOutput.truncated,
1329
+ totalLines: outputLineCount,
1330
+ totalBytes: outputByteLen,
1331
+ outputLines: outputLineCount,
1332
+ outputBytes: outputByteLen,
1333
+ };
1334
+
1335
+ const bridgeNotices: string[] = [];
1336
+ if (finalOutput.truncated) bridgeNotices.push("(output truncated)");
1337
+ for (const notice of pendingNotices) bridgeNotices.push(notice);
1338
+
1339
+ return this.#buildCompletedResult(bridgeResult, timeoutSec, {
1340
+ requestedTimeoutSec,
1341
+ notices: bridgeNotices,
1342
+ terminalId: handle.terminalId,
1343
+ wallTimeMs: performance.now() - bridgeWallTimeStart,
1344
+ });
1345
+ } finally {
1346
+ clearTimeout(timeoutTimer);
1347
+ signal?.removeEventListener("abort", onAbortSignal);
1348
+ if (handle) {
1349
+ const releaseHandle = handle;
1350
+ // Bound release like kill/output: a hung `terminal/release` RPC must not
1351
+ // keep the tool pending after the result is already decided.
1352
+ await Promise.race([
1353
+ releaseHandle.release().catch((error: unknown) => {
1354
+ logger.warn("ACP terminal release failed", { terminalId: releaseHandle.terminalId, error });
1355
+ }),
1356
+ Bun.sleep(killGraceMs),
1357
+ ]);
1358
+ }
1359
+ }
1360
+ }
1361
+
1362
+ // Track output for streaming updates (tail only)
1363
+ const tailBuffer = new TailBuffer(DEFAULT_MAX_BYTES);
1364
+
1365
+ // Allocate artifact for truncated output storage
1366
+ const { path: artifactPath, id: artifactId } = (await this.session.allocateOutputArtifact?.("bash")) ?? {};
1367
+
1368
+ const interactiveUi = canUseInteractiveBashPty(pty, ctx) ? ctx?.ui : undefined;
1369
+ if (pty && !interactiveUi) {
1370
+ pendingNotices.push("pty requested but unavailable in this environment; ran without a terminal");
1371
+ }
1372
+ const wallTimeStart = performance.now();
1373
+ const result: BashResult | BashInteractiveResult = interactiveUi
1374
+ ? await runInteractiveBashPty(interactiveUi, {
1375
+ // PTY bypasses executeBash, so feed it the direnv-transformed
1376
+ // command + merged env (backendPreflight is defined whenever this
1377
+ // branch runs, since both gate on canUseInteractiveBashPty).
1378
+ command: backendPreflight?.command ?? command,
1379
+ cwd: commandCwd,
1380
+ timeoutMs,
1381
+ signal,
1382
+ env: backendPreflight?.env ?? resolvedEnv,
1383
+ artifactPath,
1384
+ artifactId,
1385
+ })
1386
+ : // executeBash runs its OWN direnv preflight internally — pass the RAW
1387
+ // command + resolvedEnv here so the unset prefix / env merge is not
1388
+ // applied twice.
1389
+ await executeBash(command, {
1390
+ cwd: commandCwd,
1391
+ sessionKey: this.session.getSessionId?.() ?? undefined,
1392
+ timeout: timeoutMs ?? 0,
1393
+ signal,
1394
+ env: resolvedEnv,
1395
+ artifactPath,
1396
+ artifactId,
1397
+ onChunk: streamTailUpdates(tailBuffer, onUpdate),
1398
+ onMinimizedSave: originalText => saveBashOriginalArtifact(this.session, originalText),
1399
+ });
1400
+ const wallTimeMs = performance.now() - wallTimeStart;
1401
+ if (result.cancelled) {
1402
+ // A cancelled result is either a timeout (the command's deadline fired)
1403
+ // or a user/system abort. Timeouts are handled by #buildCompletedResult
1404
+ // which returns a non-throwing error result with details.timedOut=true
1405
+ // so the renderer can show a warning border instead of error red.
1406
+ // Both interactive and non-interactive results carry an explicit
1407
+ // `timedOut` field from the executor/PTY layer.
1408
+ const isTimeout = result.timedOut === true;
1409
+ if (!isTimeout) {
1410
+ const out = normalizeResultOutput(result);
1411
+ // The local executor already prepends `[Command cancelled]`; PTY
1412
+ // output does not, so preserve one cancellation notice in either case.
1413
+ const message = out.startsWith("[Command cancelled]")
1414
+ ? out
1415
+ : out
1416
+ ? `${out}\n\n[Command aborted]`
1417
+ : "Command aborted";
1418
+ if (signal?.aborted) {
1419
+ throw new ToolAbortError(message);
1420
+ }
1421
+ throw new ToolError(message);
1422
+ }
1423
+ }
1424
+ return this.#buildCompletedResult(result, timeoutSec, {
1425
+ requestedTimeoutSec,
1426
+ notices: pendingNotices,
1427
+ wallTimeMs,
1428
+ });
1429
+ }
1430
+ }
1431
+
1432
+ // =============================================================================
1433
+ // TUI Renderer
1434
+ // =============================================================================
1435
+ export interface BashRenderArgs {
1436
+ command?: string;
1437
+ env?: Record<string, string>;
1438
+ timeout?: number;
1439
+ cwd?: string;
1440
+ __partialJson?: string;
1441
+ [key: string]: unknown;
1442
+ }
1443
+
1444
+ export interface BashRenderContext {
1445
+ /** Raw output text */
1446
+ output?: string;
1447
+ /** Whether output came from artifact storage */
1448
+ isFullOutput?: boolean;
1449
+ /** Whether output is expanded */
1450
+ expanded?: boolean;
1451
+ /** Number of preview lines when collapsed */
1452
+ previewLines?: number;
1453
+ /** Timeout in seconds */
1454
+ timeout?: number;
1455
+ }
1456
+
1457
+ export interface ShellRendererConfig<TArgs> {
1458
+ resolveTitle: (args: TArgs | undefined, options: RenderResultOptions) => string;
1459
+ resolveCommand?: (args: TArgs | undefined) => string | undefined;
1460
+ resolveCwd?: (args: TArgs | undefined) => string | undefined;
1461
+ resolveEnv?: (args: TArgs | undefined) => Record<string, string> | undefined;
1462
+ showHeader?: boolean;
1463
+ }
1464
+
1465
+ function getPartialJson<TArgs>(args: TArgs | undefined): string | undefined {
1466
+ if (!args || typeof args !== "object" || !("__partialJson" in args)) return undefined;
1467
+ const value = (args as { __partialJson?: unknown }).__partialJson;
1468
+ return typeof value === "string" ? value : undefined;
1469
+ }
1470
+
1471
+ export function getBashEnvForDisplay(args: BashRenderArgs): Record<string, string> | undefined {
1472
+ // The parsed args don't always mirror the exact current stream prefix, so recover
1473
+ // env from the raw JSON buffer to surface `NAME="..." cmd` in the preview as it
1474
+ // streams rather than only once the args object finishes.
1475
+ const partialEnv = extractPartialBashEnv(args.__partialJson);
1476
+ if (partialEnv && args.env) return { ...partialEnv, ...args.env };
1477
+ return args.env ?? partialEnv;
1478
+ }
1479
+
1480
+ /**
1481
+ * Returns the bash command formatted for the result body: the dim `$ cd … &&`
1482
+ * prefix joined with syntax-highlighted command lines. The prefix is applied
1483
+ * only to the first line so multi-line commands display cleanly — terminals
1484
+ * reset SGR state at line boundaries, which made the previous single-string
1485
+ * `theme.fg("dim", ...)` form render only the first line as dim.
1486
+ */
1487
+ export function formatBashCommandLines(args: BashRenderArgs, uiTheme: Theme): string[] {
1488
+ const command = replaceTabs(args.command || "…");
1489
+ const cwd = getProjectDir();
1490
+ const displayWorkdir = formatToolWorkingDirectory(args.cwd, cwd);
1491
+ const envAssignments = formatBashEnvAssignments(getBashEnvForDisplay(args));
1492
+ const prefixParts = ["$"];
1493
+ if (displayWorkdir) prefixParts.push(`cd ${displayWorkdir} &&`);
1494
+ if (envAssignments) prefixParts.push(envAssignments);
1495
+ const prefix = uiTheme.fg("dim", `${prefixParts.join(" ")} `);
1496
+ const highlightedLines = highlightCode(command, "bash");
1497
+ if (highlightedLines.length === 0) return [prefix.trimEnd()];
1498
+ return highlightedLines.map((line, i) => (i === 0 ? `${prefix}${line}` : line));
1499
+ }
1500
+
1501
+ function toBashRenderArgs<TArgs>(args: TArgs | undefined, config: ShellRendererConfig<TArgs>): BashRenderArgs {
1502
+ return {
1503
+ command: config.resolveCommand?.(args),
1504
+ cwd: config.resolveCwd?.(args),
1505
+ env: config.resolveEnv?.(args),
1506
+ __partialJson: getPartialJson(args),
1507
+ };
1508
+ }
1509
+
1510
+ export function createShellRenderer<TArgs>(config: ShellRendererConfig<TArgs>) {
1511
+ return {
1512
+ renderCall(args: TArgs, options: RenderResultOptions, uiTheme: Theme): Component {
1513
+ const renderArgs = toBashRenderArgs(args, config);
1514
+ const cmdLines = formatBashCommandLines(renderArgs, uiTheme);
1515
+ const outputBlock = new CachedOutputBlock();
1516
+ return markFramedBlockComponent({
1517
+ render: (width: number): readonly string[] => {
1518
+ const header =
1519
+ config.showHeader === false
1520
+ ? undefined
1521
+ : renderStatusLine(
1522
+ {
1523
+ icon: options.spinnerFrame !== undefined ? "running" : "pending",
1524
+ spinnerFrame: options.spinnerFrame,
1525
+ title: config.resolveTitle(args, options),
1526
+ },
1527
+ uiTheme,
1528
+ );
1529
+ return outputBlock.render(
1530
+ {
1531
+ header,
1532
+ state: options.spinnerFrame !== undefined ? "running" : "pending",
1533
+ sections: [{ lines: capPreviewLines(cmdLines, uiTheme, { expanded: options.expanded }) }],
1534
+ width,
1535
+ },
1536
+ uiTheme,
1537
+ );
1538
+ },
1539
+ invalidate: () => {
1540
+ outputBlock.invalidate();
1541
+ },
1542
+ });
1543
+ },
1544
+
1545
+ renderResult(
1546
+ result: {
1547
+ content: Array<{ type: string; text?: string }>;
1548
+ details?: BashToolDetails;
1549
+ isError?: boolean;
1550
+ },
1551
+ options: RenderResultOptions & { renderContext?: BashRenderContext },
1552
+ uiTheme: Theme,
1553
+ args?: TArgs,
1554
+ ): Component {
1555
+ const renderArgs = toBashRenderArgs(args, config);
1556
+ const cmdLines = args ? formatBashCommandLines(renderArgs, uiTheme) : undefined;
1557
+ const isError = result.isError === true;
1558
+ const isPartial = options.isPartial === true;
1559
+ const success = !isPartial && !isError;
1560
+ const details = result.details;
1561
+ const isTimeout = details?.timedOut === true;
1562
+ const header =
1563
+ config.showHeader === false
1564
+ ? undefined
1565
+ : renderStatusLine(
1566
+ success
1567
+ ? {
1568
+ iconOverride: uiTheme.styledSymbol("tool.bash", "accent"),
1569
+ title: config.resolveTitle(args, options),
1570
+ }
1571
+ : {
1572
+ icon: isPartial ? "pending" : isTimeout ? "warning" : "error",
1573
+ title: config.resolveTitle(args, options),
1574
+ },
1575
+ uiTheme,
1576
+ );
1577
+ const outputBlock = new CachedOutputBlock();
1578
+
1579
+ // Per-instance cache for the expensive inner lines computation. Mirrors
1580
+ // the eval-renderer pattern (`eval-render.ts:709-752`): without this,
1581
+ // every TUI repaint (one per keystroke when a long transcript is on
1582
+ // screen) re-runs `split` / `replaceTabs` / `truncateToVisualLines` over
1583
+ // the whole stored output for every bash row in scrollback. With a
1584
+ // 50KB-tail bash result times hundreds of rows, that re-rendering is
1585
+ // what pinned the main thread in issue #2081 and made keystrokes feel
1586
+ // like the CPU was at 100%. The cache key includes every render input
1587
+ // that materially affects the produced lines.
1588
+ let cachedWidth: number | undefined;
1589
+ let cachedPreviewLines: number | undefined;
1590
+ let cachedExpanded: boolean | undefined;
1591
+ let cachedRawOutput: string | undefined;
1592
+ let cachedIsPartial: boolean | undefined;
1593
+ let cachedLines: readonly string[] | undefined;
1594
+ let cachedPreviewWindow: number | undefined;
1595
+
1596
+ return markFramedBlockComponent({
1597
+ render: (width: number): readonly string[] => {
1598
+ // REACTIVE: read mutable options at render time
1599
+ const { renderContext } = options;
1600
+ const expanded = renderContext?.expanded ?? options.expanded;
1601
+ const previewLines = renderContext?.previewLines ?? BASH_DEFAULT_PREVIEW_LINES;
1602
+
1603
+ // Get output from context (preferred) or fall back to result content.
1604
+ // Strip the LLM-facing notice appended by wrappedExecute so we don't
1605
+ // double-print it alongside the styled warning line below.
1606
+ const rawOutput = renderContext?.output ?? result.content?.find(c => c.type === "text")?.text ?? "";
1607
+
1608
+ const isPartial = options.isPartial === true;
1609
+ const previewWindow = previewWindowRows();
1610
+
1611
+ if (
1612
+ cachedLines !== undefined &&
1613
+ cachedWidth === width &&
1614
+ cachedPreviewLines === previewLines &&
1615
+ cachedExpanded === expanded &&
1616
+ cachedRawOutput === rawOutput &&
1617
+ cachedIsPartial === isPartial &&
1618
+ cachedPreviewWindow === previewWindow
1619
+ ) {
1620
+ return cachedLines;
1621
+ }
1622
+ const withoutBackground = stripBackgroundNotice(rawOutput, details?.async);
1623
+ const strippedOutput = stripOutputNotice(withoutBackground, details?.meta);
1624
+ const withoutExit = stripExitCodeNotice(strippedOutput, details?.exitCode);
1625
+ const withoutWall = stripWallTimeNotice(withoutExit, details?.wallTimeMs);
1626
+ const rawOutputArtifact = stripRawOutputArtifactNotice(withoutWall);
1627
+ const output = rawOutputArtifact.text;
1628
+ const displayOutput = output.trimEnd();
1629
+ const showingFullOutput = expanded && renderContext?.isFullOutput === true;
1630
+
1631
+ // Build truncation warning
1632
+ const timeoutDisabled = details?.timeoutDisabled === true || renderContext?.timeout === 0;
1633
+ const timeoutSeconds = timeoutDisabled ? undefined : (details?.timeoutSeconds ?? renderContext?.timeout);
1634
+ const requestedTimeoutSeconds = details?.requestedTimeoutSeconds;
1635
+ const wallTimeMs = details?.wallTimeMs;
1636
+ const statsParts: string[] = [];
1637
+ if (details?.async?.state === "running") {
1638
+ statsParts.push(`Backgrounded: ${details.async.jobId}`);
1639
+ }
1640
+ if (wallTimeMs !== undefined) {
1641
+ statsParts.push(`Wall: ${formatWallTimeSeconds(wallTimeMs)}s`);
1642
+ }
1643
+ if (timeoutDisabled) {
1644
+ statsParts.push("Timeout: disabled");
1645
+ }
1646
+ if (typeof timeoutSeconds === "number") {
1647
+ statsParts.push(
1648
+ requestedTimeoutSeconds !== undefined && requestedTimeoutSeconds !== timeoutSeconds
1649
+ ? `Timeout: ${timeoutSeconds}s (requested ${requestedTimeoutSeconds}s clamped)`
1650
+ : `Timeout: ${timeoutSeconds}s`,
1651
+ );
1652
+ }
1653
+ if (rawOutputArtifact.artifactId) {
1654
+ statsParts.push(`Artifact: ${rawOutputArtifact.artifactId}`);
1655
+ }
1656
+ if (isError && typeof details?.exitCode === "number") {
1657
+ statsParts.push(`Exit: ${details.exitCode}`);
1658
+ }
1659
+ const timeoutLine =
1660
+ statsParts.length > 0
1661
+ ? uiTheme.fg(
1662
+ "dim",
1663
+ `${uiTheme.format.bracketLeft}${statsParts.join(" | ")}${uiTheme.format.bracketRight}`,
1664
+ )
1665
+ : undefined;
1666
+ let warningLine: string | undefined;
1667
+ if (details?.meta?.truncation && !showingFullOutput) {
1668
+ warningLine = formatStyledTruncationWarning(details.meta, uiTheme) ?? undefined;
1669
+ }
1670
+
1671
+ const outputLines: string[] = [];
1672
+ const hasOutput = displayOutput.trim().length > 0;
1673
+ const rawOutputLines = displayOutput.split("\n");
1674
+ const sixelLineMask =
1675
+ TERMINAL.imageProtocol === ImageProtocol.Sixel ? getSixelLineMask(rawOutputLines) : undefined;
1676
+ const hasSixelOutput = sixelLineMask?.some(Boolean) ?? false;
1677
+ if (hasOutput) {
1678
+ if (hasSixelOutput) {
1679
+ outputLines.push(
1680
+ ...rawOutputLines.map((line, index) =>
1681
+ sixelLineMask?.[index] ? line : uiTheme.fg("toolOutput", replaceTabs(line)),
1682
+ ),
1683
+ );
1684
+ } else if (expanded) {
1685
+ outputLines.push(...rawOutputLines.map(line => uiTheme.fg("toolOutput", replaceTabs(line))));
1686
+ } else {
1687
+ const styledOutput = rawOutputLines
1688
+ .map(line => uiTheme.fg("toolOutput", replaceTabs(line)))
1689
+ .join("\n");
1690
+ const textContent = styledOutput;
1691
+ // Cap the collapsed/streaming output to a viewport-sized tail and
1692
+ // measure it at the box's INNER width. Otherwise a growing tail
1693
+ // window scrolls its (mutating) rows above the live-region window
1694
+ // and the engine re-commits a fresh snapshot every frame —
1695
+ // spraying duplicate "… ctrl+o to expand" banners into native
1696
+ // scrollback (the box never overflows the viewport now).
1697
+ const previewBudget = Math.min(previewLines, previewWindow);
1698
+ const result = truncateToVisualLines(textContent, previewBudget, outputBlockContentWidth(width));
1699
+ if (result.skippedCount > 0) {
1700
+ outputLines.push(
1701
+ uiTheme.fg(
1702
+ "dim",
1703
+ `… (${result.skippedCount} earlier lines, showing ${result.visualLines.length} of ${result.skippedCount + result.visualLines.length}) (ctrl+o to expand)`,
1704
+ ),
1705
+ );
1706
+ }
1707
+ outputLines.push(...result.visualLines);
1708
+ }
1709
+ }
1710
+ if (timeoutLine) outputLines.push(timeoutLine);
1711
+ if (warningLine) outputLines.push(warningLine);
1712
+
1713
+ const framed = outputBlock.render(
1714
+ {
1715
+ header,
1716
+ state: isPartial ? "pending" : isError ? (isTimeout ? "warning" : "error") : "success",
1717
+ sections: [
1718
+ {
1719
+ // Viewport-sized tail window in every state — streaming and final
1720
+ // render identically; only ctrl+o uncaps.
1721
+ lines: capPreviewLines(cmdLines ?? [], uiTheme, { expanded }),
1722
+ },
1723
+ { label: uiTheme.fg("toolTitle", "Output"), lines: outputLines },
1724
+ ],
1725
+ width,
1726
+ },
1727
+ uiTheme,
1728
+ );
1729
+
1730
+ cachedWidth = width;
1731
+ cachedPreviewLines = previewLines;
1732
+ cachedExpanded = expanded;
1733
+ cachedRawOutput = rawOutput;
1734
+ cachedIsPartial = isPartial;
1735
+ cachedPreviewWindow = previewWindow;
1736
+ cachedLines = framed;
1737
+ return framed;
1738
+ },
1739
+ invalidate: () => {
1740
+ outputBlock.invalidate();
1741
+ cachedLines = undefined;
1742
+ cachedWidth = undefined;
1743
+ cachedPreviewLines = undefined;
1744
+ cachedExpanded = undefined;
1745
+ cachedRawOutput = undefined;
1746
+ cachedIsPartial = undefined;
1747
+ cachedPreviewWindow = undefined;
1748
+ },
1749
+ });
1750
+ },
1751
+ mergeCallAndResult: true,
1752
+ inline: true,
1753
+ };
1754
+ }
1755
+
1756
+ export const bashToolRenderer = createShellRenderer<BashRenderArgs>({
1757
+ resolveTitle: () => "Bash",
1758
+ resolveCommand: args => args?.command,
1759
+ resolveCwd: args => args?.cwd,
1760
+ resolveEnv: args => args?.env,
1761
+ showHeader: false,
1762
+ });