@oh-my-pi/pi-coding-agent 17.0.9 → 17.1.1

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 (349) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +1 -1
  3. package/dist/cli.js +4685 -4614
  4. package/dist/types/async/job-manager.d.ts +37 -1
  5. package/dist/types/cli/args.d.ts +2 -0
  6. package/dist/types/cli/bench-cli.d.ts +41 -0
  7. package/dist/types/commands/bench.d.ts +15 -3
  8. package/dist/types/commands/launch.d.ts +4 -0
  9. package/dist/types/config/model-registry.d.ts +3 -1
  10. package/dist/types/config/models-config-schema.d.ts +18 -6
  11. package/dist/types/config/models-config.d.ts +14 -5
  12. package/dist/types/config/provider-globals.d.ts +2 -2
  13. package/dist/types/config/service-tier.d.ts +5 -1
  14. package/dist/types/config/settings-schema.d.ts +374 -47
  15. package/dist/types/config/settings.d.ts +7 -0
  16. package/dist/types/dap/client.d.ts +20 -0
  17. package/dist/types/exec/bash-executor.d.ts +38 -0
  18. package/dist/types/exec/direnv.d.ts +33 -0
  19. package/dist/types/export/html/args.d.ts +15 -0
  20. package/dist/types/export/html/index.d.ts +11 -26
  21. package/dist/types/export/html/web-palette.d.ts +55 -116
  22. package/dist/types/extensibility/extensions/loader.d.ts +3 -0
  23. package/dist/types/extensibility/extensions/types.d.ts +17 -2
  24. package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +3 -0
  25. package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +13 -0
  26. package/dist/types/live/attestation.d.ts +2 -0
  27. package/dist/types/live/controller.d.ts +48 -0
  28. package/dist/types/live/protocol.d.ts +85 -0
  29. package/dist/types/live/protocol.test.d.ts +1 -0
  30. package/dist/types/live/transport.d.ts +35 -0
  31. package/dist/types/live/visualizer.d.ts +30 -0
  32. package/dist/types/lsp/client.d.ts +16 -1
  33. package/dist/types/mcp/config-writer.test.d.ts +1 -0
  34. package/dist/types/mcp/oauth-discovery.d.ts +2 -0
  35. package/dist/types/mcp/smithery-auth.d.ts +1 -1
  36. package/dist/types/mcp/smithery-auth.test.d.ts +1 -0
  37. package/dist/types/memory-backend/tool-names.d.ts +2 -0
  38. package/dist/types/modes/components/assistant-message.d.ts +1 -0
  39. package/dist/types/modes/components/custom-message.d.ts +1 -1
  40. package/dist/types/modes/components/login-dialog.d.ts +2 -2
  41. package/dist/types/modes/components/message-frame.d.ts +8 -4
  42. package/dist/types/modes/components/plan-review-overlay.d.ts +26 -0
  43. package/dist/types/modes/components/session-account-selector.d.ts +11 -0
  44. package/dist/types/modes/components/settings-defs.d.ts +7 -1
  45. package/dist/types/modes/components/status-line/component.jj-cache.test.d.ts +1 -0
  46. package/dist/types/modes/components/tool-execution.d.ts +9 -1
  47. package/dist/types/modes/controllers/command-controller.d.ts +1 -1
  48. package/dist/types/modes/controllers/event-controller.d.ts +6 -1
  49. package/dist/types/modes/controllers/live-command-controller.d.ts +14 -0
  50. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  51. package/dist/types/modes/interactive-mode.d.ts +6 -0
  52. package/dist/types/modes/theme/theme.d.ts +1 -1
  53. package/dist/types/modes/types.d.ts +4 -1
  54. package/dist/types/modes/utils/context-usage.d.ts +12 -2
  55. package/dist/types/plan-mode/plan-files.d.ts +10 -0
  56. package/dist/types/registry/agent-lifecycle.d.ts +21 -9
  57. package/dist/types/registry/agent-registry.d.ts +11 -4
  58. package/dist/types/sdk.d.ts +15 -1
  59. package/dist/types/secrets/index.d.ts +15 -2
  60. package/dist/types/secrets/obfuscator.d.ts +113 -15
  61. package/dist/types/session/acp-permission-gate.d.ts +19 -0
  62. package/dist/types/session/agent-session-events.d.ts +84 -0
  63. package/dist/types/session/agent-session-types.d.ts +321 -0
  64. package/dist/types/session/agent-session.d.ts +135 -753
  65. package/dist/types/session/async-job-delivery.d.ts +28 -0
  66. package/dist/types/session/auth-storage.d.ts +1 -1
  67. package/dist/types/session/bash-runner.d.ts +75 -0
  68. package/dist/types/session/blob-store.d.ts +11 -1
  69. package/dist/types/session/blob-store.test.d.ts +1 -0
  70. package/dist/types/session/checkpoint-entries.d.ts +28 -0
  71. package/dist/types/session/eval-runner.d.ts +51 -0
  72. package/dist/types/session/irc-bridge.d.ts +46 -0
  73. package/dist/types/session/messages.d.ts +24 -0
  74. package/dist/types/session/model-controls.d.ts +180 -0
  75. package/dist/types/session/prewalk.d.ts +46 -0
  76. package/dist/types/session/queued-messages.d.ts +22 -0
  77. package/dist/types/session/redis-session-storage.d.ts +1 -0
  78. package/dist/types/session/retry-fallback-chains.d.ts +74 -0
  79. package/dist/types/session/role-models.d.ts +15 -0
  80. package/dist/types/session/session-advisors.d.ts +228 -0
  81. package/dist/types/session/session-entries.d.ts +8 -0
  82. package/dist/types/session/session-handoff.d.ts +70 -0
  83. package/dist/types/session/session-history-format.d.ts +6 -1
  84. package/dist/types/session/session-maintenance.d.ts +246 -0
  85. package/dist/types/session/session-manager.d.ts +41 -0
  86. package/dist/types/session/session-memory.d.ts +56 -0
  87. package/dist/types/session/session-provider-boundary.d.ts +62 -0
  88. package/dist/types/session/session-stats.d.ts +48 -0
  89. package/dist/types/session/session-tools.d.ts +158 -0
  90. package/dist/types/session/session-workspace.d.ts +32 -0
  91. package/dist/types/session/stream-guards.d.ts +44 -0
  92. package/dist/types/session/streaming-output.d.ts +8 -0
  93. package/dist/types/session/todo-tracker.d.ts +56 -0
  94. package/dist/types/session/ttsr-coordinator.d.ts +51 -0
  95. package/dist/types/session/turn-recovery.d.ts +225 -0
  96. package/dist/types/slash-commands/helpers/session-pin.d.ts +9 -0
  97. package/dist/types/stt/index.d.ts +0 -2
  98. package/dist/types/stt/stt-controller.d.ts +7 -0
  99. package/dist/types/system-prompt.d.ts +2 -0
  100. package/dist/types/task/executor.d.ts +4 -1
  101. package/dist/types/task/output-manager.d.ts +3 -1
  102. package/dist/types/tiny/title-client.d.ts +10 -0
  103. package/dist/types/tools/approval.d.ts +1 -0
  104. package/dist/types/tools/browser/launch.d.ts +5 -0
  105. package/dist/types/tools/builtin-names.d.ts +1 -1
  106. package/dist/types/tools/computer/protocol.d.ts +43 -0
  107. package/dist/types/tools/computer/supervisor.d.ts +32 -0
  108. package/dist/types/tools/computer/worker-entry.d.ts +1 -0
  109. package/dist/types/tools/computer/worker.d.ts +15 -0
  110. package/dist/types/tools/computer-renderer.d.ts +22 -0
  111. package/dist/types/tools/computer.d.ts +71 -0
  112. package/dist/types/tools/context.d.ts +2 -0
  113. package/dist/types/tools/default-renderer.d.ts +21 -0
  114. package/dist/types/tools/image-gen.d.ts +4 -4
  115. package/dist/types/tools/image-providers.d.ts +38 -0
  116. package/dist/types/tools/index.d.ts +7 -0
  117. package/dist/types/tools/path-utils.d.ts +8 -0
  118. package/dist/types/tools/todo.d.ts +10 -4
  119. package/dist/types/tools/xdev.d.ts +5 -1
  120. package/dist/types/tts/streaming-player.d.ts +10 -29
  121. package/dist/types/tts/vocalizer.d.ts +5 -0
  122. package/dist/types/utils/jj.d.ts +29 -0
  123. package/dist/types/utils/lang-from-path.d.ts +1 -0
  124. package/dist/types/utils/title-generator.d.ts +30 -0
  125. package/dist/types/utils/tools-manager.d.ts +1 -2
  126. package/dist/types/vibe/runtime.d.ts +36 -8
  127. package/dist/types/web/search/index.d.ts +1 -1
  128. package/dist/types/web/search/provider.d.ts +15 -5
  129. package/dist/types/web/search/types.d.ts +94 -0
  130. package/package.json +12 -12
  131. package/scripts/generate-share-viewer.ts +4 -6
  132. package/src/advisor/__tests__/advisor.test.ts +643 -0
  133. package/src/advisor/runtime.ts +144 -43
  134. package/src/async/job-manager.ts +90 -2
  135. package/src/cli/args.ts +3 -0
  136. package/src/cli/auth-gateway-cli.ts +18 -3
  137. package/src/cli/bench-cli.ts +395 -29
  138. package/src/cli/flag-tables.ts +3 -0
  139. package/src/cli/gc-cli.ts +2 -2
  140. package/src/cli/setup-cli.ts +2 -14
  141. package/src/cli/usage-cli.ts +10 -1
  142. package/src/cli.ts +8 -0
  143. package/src/collab/host.ts +3 -2
  144. package/src/commands/bench.ts +18 -4
  145. package/src/commands/launch.ts +4 -0
  146. package/src/config/model-registry.ts +16 -2
  147. package/src/config/models-config-schema.ts +15 -4
  148. package/src/config/provider-globals.ts +9 -9
  149. package/src/config/service-tier.ts +26 -1
  150. package/src/config/settings-schema.ts +270 -51
  151. package/src/config/settings.ts +81 -2
  152. package/src/dap/client.ts +37 -4
  153. package/src/edit/index.ts +49 -12
  154. package/src/eval/executor-base.ts +1 -0
  155. package/src/eval/js/executor.ts +2 -0
  156. package/src/eval/py/executor.ts +103 -33
  157. package/src/exec/bash-executor.ts +90 -6
  158. package/src/exec/direnv.ts +145 -0
  159. package/src/export/html/args.ts +20 -0
  160. package/src/export/html/index.ts +45 -47
  161. package/src/export/html/template.css +19 -1
  162. package/src/export/html/template.html +6 -0
  163. package/src/export/html/template.js +21 -0
  164. package/src/export/html/tool-views.generated.js +31 -31
  165. package/src/export/html/web-palette.ts +116 -132
  166. package/src/export/share.ts +249 -49
  167. package/src/extensibility/extensions/loader.ts +29 -1
  168. package/src/extensibility/extensions/runner.ts +6 -0
  169. package/src/extensibility/extensions/types.ts +33 -2
  170. package/src/extensibility/extensions/wrapper.ts +68 -12
  171. package/src/extensibility/legacy-pi-coding-agent-shim.ts +7 -1
  172. package/src/extensibility/plugins/legacy-pi-compat.ts +19 -1
  173. package/src/hindsight/state.ts +64 -6
  174. package/src/internal-urls/local-protocol.ts +2 -1
  175. package/src/internal-urls/memory-protocol.ts +2 -2
  176. package/src/internal-urls/skill-protocol.ts +2 -2
  177. package/src/internal-urls/ssh-protocol.ts +2 -1
  178. package/src/internal-urls/vault-protocol.ts +2 -1
  179. package/src/live/attestation.ts +91 -0
  180. package/src/live/controller.ts +517 -0
  181. package/src/live/prompts/agent-final-message.md +3 -0
  182. package/src/live/prompts/live-instructions.md +23 -0
  183. package/src/live/protocol.test.ts +140 -0
  184. package/src/live/protocol.ts +233 -0
  185. package/src/live/transport.ts +422 -0
  186. package/src/live/visualizer.ts +214 -0
  187. package/src/lsp/client.ts +29 -9
  188. package/src/lsp/index.ts +19 -4
  189. package/src/main.ts +8 -1
  190. package/src/mcp/config-writer.test.ts +43 -0
  191. package/src/mcp/config-writer.ts +109 -82
  192. package/src/mcp/oauth-discovery.ts +10 -2
  193. package/src/mcp/smithery-auth.test.ts +29 -0
  194. package/src/mcp/smithery-auth.ts +3 -2
  195. package/src/memories/index.ts +40 -20
  196. package/src/memory-backend/tool-names.ts +2 -0
  197. package/src/mnemopi/backend.ts +24 -7
  198. package/src/modes/acp/acp-agent.ts +56 -10
  199. package/src/modes/acp/acp-event-mapper.ts +8 -1
  200. package/src/modes/components/agent-hub.ts +1 -1
  201. package/src/modes/components/assistant-message.ts +7 -2
  202. package/src/modes/components/custom-message.ts +4 -1
  203. package/src/modes/components/login-dialog.ts +14 -4
  204. package/src/modes/components/message-frame.ts +14 -8
  205. package/src/modes/components/plan-review-overlay.ts +350 -35
  206. package/src/modes/components/session-account-selector.ts +62 -0
  207. package/src/modes/components/settings-defs.ts +23 -1
  208. package/src/modes/components/settings-selector.ts +169 -1
  209. package/src/modes/components/status-line/component.jj-cache.test.ts +229 -0
  210. package/src/modes/components/status-line/component.ts +123 -2
  211. package/src/modes/components/tool-execution.ts +28 -112
  212. package/src/modes/controllers/command-controller.ts +56 -15
  213. package/src/modes/controllers/event-controller.ts +111 -16
  214. package/src/modes/controllers/extension-ui-controller.test.ts +1 -0
  215. package/src/modes/controllers/extension-ui-controller.ts +20 -4
  216. package/src/modes/controllers/input-controller.ts +47 -39
  217. package/src/modes/controllers/live-command-controller.ts +255 -0
  218. package/src/modes/controllers/mcp-command-controller.ts +10 -1
  219. package/src/modes/controllers/omfg-controller.ts +44 -40
  220. package/src/modes/controllers/selector-controller.ts +80 -10
  221. package/src/modes/controllers/session-focus-controller.ts +6 -1
  222. package/src/modes/interactive-mode.ts +176 -38
  223. package/src/modes/runtime-init.ts +2 -0
  224. package/src/modes/setup-wizard/scenes/web-search.ts +17 -9
  225. package/src/modes/theme/theme.ts +1 -1
  226. package/src/modes/types.ts +4 -1
  227. package/src/modes/utils/context-usage.ts +23 -7
  228. package/src/modes/utils/ui-helpers.ts +10 -5
  229. package/src/plan-mode/plan-files.ts +40 -0
  230. package/src/prompts/bench/cache-prefix-chunk.md +1 -0
  231. package/src/prompts/bench/cache-prefix.md +3 -0
  232. package/src/prompts/bench/cache-suffix.md +1 -0
  233. package/src/prompts/system/computer-safety.md +14 -0
  234. package/src/prompts/system/custom-system-prompt.md +1 -1
  235. package/src/prompts/system/project-prompt.md +8 -1
  236. package/src/prompts/system/subagent-async-pending.md +6 -0
  237. package/src/prompts/system/system-prompt.md +1 -1
  238. package/src/prompts/system/workflow-notice.md +48 -40
  239. package/src/prompts/system/xdev-mount-notice.md +4 -0
  240. package/src/prompts/tools/computer.md +26 -0
  241. package/src/prompts/tools/todo.md +3 -1
  242. package/src/registry/agent-lifecycle.ts +116 -46
  243. package/src/registry/agent-registry.ts +35 -8
  244. package/src/registry/persisted-agents.ts +26 -2
  245. package/src/sdk.ts +215 -136
  246. package/src/secrets/index.ts +127 -4
  247. package/src/secrets/obfuscator.ts +2328 -105
  248. package/src/session/acp-permission-gate.ts +165 -0
  249. package/src/session/agent-session-error-log.test.ts +1 -1
  250. package/src/session/agent-session-events.ts +66 -0
  251. package/src/session/agent-session-types.ts +343 -0
  252. package/src/session/agent-session.ts +1940 -12218
  253. package/src/session/artifacts.ts +6 -5
  254. package/src/session/async-job-delivery.ts +74 -0
  255. package/src/session/auth-storage.ts +1 -0
  256. package/src/session/bash-runner.ts +326 -0
  257. package/src/session/blob-store.test.ts +56 -0
  258. package/src/session/blob-store.ts +18 -2
  259. package/src/session/checkpoint-entries.ts +81 -0
  260. package/src/session/eval-runner.ts +217 -0
  261. package/src/session/indexed-session-storage.ts +9 -2
  262. package/src/session/irc-bridge.ts +203 -0
  263. package/src/session/messages.ts +266 -1
  264. package/src/session/model-controls.ts +714 -0
  265. package/src/session/prewalk.ts +252 -0
  266. package/src/session/queued-messages.ts +93 -0
  267. package/src/session/redis-session-storage.ts +51 -11
  268. package/src/session/retry-fallback-chains.ts +455 -0
  269. package/src/session/role-models.ts +85 -0
  270. package/src/session/session-advisors.ts +1679 -0
  271. package/src/session/session-context.ts +6 -3
  272. package/src/session/session-entries.ts +8 -0
  273. package/src/session/session-handoff.ts +308 -0
  274. package/src/session/session-history-format.ts +20 -9
  275. package/src/session/session-loader.ts +0 -46
  276. package/src/session/session-maintenance.ts +2983 -0
  277. package/src/session/session-manager.ts +390 -30
  278. package/src/session/session-memory.ts +222 -0
  279. package/src/session/session-provider-boundary.ts +307 -0
  280. package/src/session/session-stats.ts +293 -0
  281. package/src/session/session-tools.ts +980 -0
  282. package/src/session/session-workspace.ts +53 -0
  283. package/src/session/stream-guards.ts +417 -0
  284. package/src/session/streaming-output.ts +52 -5
  285. package/src/session/todo-tracker.ts +380 -0
  286. package/src/session/ttsr-coordinator.ts +496 -0
  287. package/src/session/turn-recovery.ts +1629 -0
  288. package/src/slash-commands/builtin-registry.ts +257 -20
  289. package/src/slash-commands/helpers/session-pin.ts +44 -0
  290. package/src/slash-commands/helpers/usage-report.ts +22 -3
  291. package/src/stt/downloader.ts +0 -2
  292. package/src/stt/index.ts +0 -2
  293. package/src/stt/stt-controller.ts +57 -146
  294. package/src/system-prompt.ts +45 -17
  295. package/src/task/executor.ts +285 -70
  296. package/src/task/index.ts +98 -105
  297. package/src/task/output-manager.ts +25 -3
  298. package/src/task/persisted-revive.ts +4 -3
  299. package/src/task/structured-subagent.ts +1 -0
  300. package/src/tiny/title-client.ts +22 -0
  301. package/src/tools/approval.ts +55 -10
  302. package/src/tools/bash-interactive.ts +116 -86
  303. package/src/tools/bash-skill-urls.ts +28 -2
  304. package/src/tools/bash.ts +339 -123
  305. package/src/tools/browser/launch.ts +9 -1
  306. package/src/tools/browser/tab-supervisor.ts +8 -5
  307. package/src/tools/browser.ts +94 -47
  308. package/src/tools/builtin-names.ts +1 -0
  309. package/src/tools/computer/protocol.ts +28 -0
  310. package/src/tools/computer/supervisor.ts +258 -0
  311. package/src/tools/computer/worker-entry.ts +25 -0
  312. package/src/tools/computer/worker.ts +135 -0
  313. package/src/tools/computer-renderer.ts +108 -0
  314. package/src/tools/computer.ts +433 -0
  315. package/src/tools/context.ts +2 -0
  316. package/src/tools/default-renderer.ts +139 -0
  317. package/src/tools/essential-tools.ts +1 -0
  318. package/src/tools/image-gen.ts +20 -28
  319. package/src/tools/image-providers.ts +50 -0
  320. package/src/tools/index.ts +14 -0
  321. package/src/tools/path-utils.ts +49 -2
  322. package/src/tools/read.ts +156 -88
  323. package/src/tools/renderers.ts +9 -1
  324. package/src/tools/todo.ts +101 -11
  325. package/src/tools/vibe.ts +1 -2
  326. package/src/tools/write.ts +51 -1
  327. package/src/tools/xdev.ts +121 -29
  328. package/src/tts/streaming-player.ts +82 -283
  329. package/src/tts/vocalizer.ts +18 -1
  330. package/src/utils/clipboard.ts +1 -30
  331. package/src/utils/jj.ts +125 -4
  332. package/src/utils/lang-from-path.ts +7 -0
  333. package/src/utils/mac-file-urls.applescript +37 -0
  334. package/src/utils/title-generator.ts +137 -1
  335. package/src/utils/tool-choice.ts +14 -0
  336. package/src/utils/tools-manager.ts +1 -19
  337. package/src/vibe/runtime.ts +852 -72
  338. package/src/web/search/index.ts +3 -6
  339. package/src/web/search/provider.ts +31 -16
  340. package/src/web/search/providers/xai.ts +7 -1
  341. package/src/web/search/types.ts +3 -0
  342. package/dist/types/stt/recorder.d.ts +0 -30
  343. package/dist/types/stt/transcriber.d.ts +0 -14
  344. package/dist/types/stt/wav.d.ts +0 -29
  345. package/dist/types/tts/player.d.ts +0 -32
  346. package/src/stt/recorder.ts +0 -551
  347. package/src/stt/transcriber.ts +0 -60
  348. package/src/stt/wav.ts +0 -173
  349. package/src/tts/player.ts +0 -137
@@ -32,6 +32,10 @@ function normalizeCaptureChunk(chunk: string): string {
32
32
  return sanitizeWithOptionalSixelPassthrough(normalized, sanitizeText);
33
33
  }
34
34
 
35
+ // Caps only the live xterm display backlog; OutputSink remains the bounded
36
+ // source of truth for the final captured output.
37
+ const MAX_LIVE_WRITE_QUEUE_CHUNKS = 512;
38
+
35
39
  // @xterm/headless is only needed once an interactive PTY session actually starts,
36
40
  // so it is loaded lazily (and memoized) instead of weighing down CLI startup.
37
41
  let xtermTerminalCtor: typeof XtermModule.Terminal | undefined;
@@ -142,9 +146,31 @@ class BashInteractiveOverlayComponent implements Component {
142
146
 
143
147
  appendOutput(chunk: string): void {
144
148
  this.#writeQueue.push(chunk);
149
+ this.#trimWriteQueue();
145
150
  this.#drainQueue();
146
151
  }
147
152
 
153
+ #trimWriteQueue(): void {
154
+ // Compact the consumed prefix first: the queue only self-resets on a
155
+ // full drain, which never happens while a fast producer keeps a
156
+ // backlog alive, so already-written chunks must be released here to
157
+ // keep the retained array itself bounded.
158
+ if (this.#writeOffset > 0) {
159
+ this.#writeQueue.splice(0, this.#writeOffset);
160
+ this.#writeOffset = 0;
161
+ }
162
+ const firstPending = this.#writing ? 1 : 0;
163
+ const overflow = this.#writeQueue.length - firstPending - MAX_LIVE_WRITE_QUEUE_CHUNKS;
164
+ if (overflow > 0) {
165
+ this.#writeQueue.splice(firstPending, overflow);
166
+ // Dropped chunks can split an in-flight DCS/OSC/APC string (e.g. a
167
+ // sixel payload) across the gap; a stray string terminator is a
168
+ // no-op in the ground state but resynchronizes the parser if the
169
+ // terminator was dropped.
170
+ this.#writeQueue[firstPending] = `\u001b\\${this.#writeQueue[firstPending]}`;
171
+ }
172
+ }
173
+
148
174
  #drainQueue(): void {
149
175
  if (this.#writing) return;
150
176
  if (this.#writeOffset >= this.#writeQueue.length) {
@@ -314,92 +340,96 @@ export async function runInteractiveBashPty(
314
340
  headBytes: resolveOutputSinkHeadBytes(settings),
315
341
  maxColumns: resolveOutputMaxColumns(settings),
316
342
  });
317
- const result = await ui.custom<BashInteractiveResult>(
318
- (tui, uiTheme, _keybindings, done) => {
319
- const session = new PtySession();
320
- const component = new BashInteractiveOverlayComponent(
321
- options.command,
322
- uiTheme,
323
- () => tui.terminal.rows,
324
- XtermTerminal,
325
- );
326
- component.setSession(session);
327
- let finished = false;
328
- const finalize = (run: PtyRunResult) => {
329
- if (finished) return;
330
- finished = true;
331
- component.setComplete({ exitCode: run.exitCode, cancelled: run.cancelled, timedOut: run.timedOut });
332
- tui.requestRender();
333
- void (async () => {
334
- await component.flushOutput();
335
- const summary = await sink.dump();
336
- done({
337
- exitCode: run.exitCode,
338
- cancelled: run.cancelled,
339
- timedOut: run.timedOut,
340
- ...summary,
341
- });
342
- })();
343
- };
344
- const cols = Math.max(20, tui.terminal.columns - 2);
345
- const rows = Math.max(5, tui.terminal.rows - 4);
346
- component.setHandlers(
347
- data => {
348
- try {
349
- session.write(data);
350
- } catch {
351
- // ignore writes after command exits
352
- }
353
- },
354
- () => {
355
- try {
356
- session.kill();
357
- } catch {
358
- // ignore
359
- }
360
- },
361
- () => {
362
- try {
363
- session.kill();
364
- } catch {
365
- // ignore
366
- }
367
- },
368
- );
369
- void session
370
- .start(
371
- {
372
- command: options.command,
373
- cwd: options.cwd,
374
- timeoutMs: options.timeoutMs,
375
- // Interactive PTY: inherit the user's environment (the Rust side
376
- // applies these as overrides), with a real TERM so editors,
377
- // pagers, and TUIs behave like a normal terminal.
378
- env: {
379
- TERM: "xterm-256color",
380
- ...options.env,
381
- },
382
- signal: options.signal,
383
- cols,
384
- rows,
385
- shell: resolvedShell,
343
+ try {
344
+ const result = await ui.custom<BashInteractiveResult>(
345
+ (tui, uiTheme, _keybindings, done) => {
346
+ const session = new PtySession();
347
+ const component = new BashInteractiveOverlayComponent(
348
+ options.command,
349
+ uiTheme,
350
+ () => tui.terminal.rows,
351
+ XtermTerminal,
352
+ );
353
+ component.setSession(session);
354
+ let finished = false;
355
+ const finalize = (run: PtyRunResult) => {
356
+ if (finished) return;
357
+ finished = true;
358
+ component.setComplete({ exitCode: run.exitCode, cancelled: run.cancelled, timedOut: run.timedOut });
359
+ tui.requestRender();
360
+ void (async () => {
361
+ await component.flushOutput();
362
+ const summary = await sink.dump();
363
+ done({
364
+ exitCode: run.exitCode,
365
+ cancelled: run.cancelled,
366
+ timedOut: run.timedOut,
367
+ ...summary,
368
+ });
369
+ })();
370
+ };
371
+ const cols = Math.max(20, tui.terminal.columns - 2);
372
+ const rows = Math.max(5, tui.terminal.rows - 4);
373
+ component.setHandlers(
374
+ data => {
375
+ try {
376
+ session.write(data);
377
+ } catch {
378
+ // ignore writes after command exits
379
+ }
386
380
  },
387
- (err, chunk) => {
388
- if (finished || err || !chunk) return;
389
- component.appendOutput(chunk);
390
- const normalizedChunk = normalizeCaptureChunk(chunk);
391
- sink.push(normalizedChunk);
392
- tui.requestRender();
381
+ () => {
382
+ try {
383
+ session.kill();
384
+ } catch {
385
+ // ignore
386
+ }
393
387
  },
394
- )
395
- .then(finalize)
396
- .catch(error => {
397
- sink.push(`PTY error: ${error instanceof Error ? error.message : String(error)}\n`);
398
- finalize({ exitCode: undefined, cancelled: false, timedOut: false });
399
- });
400
- return component;
401
- },
402
- { overlay: true },
403
- );
404
- return result;
388
+ () => {
389
+ try {
390
+ session.kill();
391
+ } catch {
392
+ // ignore
393
+ }
394
+ },
395
+ );
396
+ void session
397
+ .start(
398
+ {
399
+ command: options.command,
400
+ cwd: options.cwd,
401
+ timeoutMs: options.timeoutMs,
402
+ // Interactive PTY: inherit the user's environment (the Rust side
403
+ // applies these as overrides), with a real TERM so editors,
404
+ // pagers, and TUIs behave like a normal terminal.
405
+ env: {
406
+ TERM: "xterm-256color",
407
+ ...options.env,
408
+ },
409
+ signal: options.signal,
410
+ cols,
411
+ rows,
412
+ shell: resolvedShell,
413
+ },
414
+ (err, chunk) => {
415
+ if (finished || err || !chunk) return;
416
+ component.appendOutput(chunk);
417
+ const normalizedChunk = normalizeCaptureChunk(chunk);
418
+ sink.push(normalizedChunk);
419
+ tui.requestRender();
420
+ },
421
+ )
422
+ .then(finalize)
423
+ .catch(error => {
424
+ sink.push(`PTY error: ${error instanceof Error ? error.message : String(error)}\n`);
425
+ finalize({ exitCode: undefined, cancelled: false, timedOut: false });
426
+ });
427
+ return component;
428
+ },
429
+ { overlay: true },
430
+ );
431
+ return result;
432
+ } finally {
433
+ await sink.dispose();
434
+ }
405
435
  }
@@ -144,6 +144,8 @@ function unquoteToken(token: string): string {
144
144
  function isInsideShellQuote(command: string, index: number): boolean {
145
145
  type ShellQuote = "'" | '"' | undefined;
146
146
  interface CommandSubstitution {
147
+ /** `$(` … `)` tracks paren depth; `` ` `` … `` ` `` is a plain toggle. */
148
+ kind: "dollar" | "backtick";
147
149
  outerQuote: ShellQuote;
148
150
  depth: number;
149
151
  }
@@ -152,6 +154,19 @@ function isInsideShellQuote(command: string, index: number): boolean {
152
154
  const substitutions: CommandSubstitution[] = [];
153
155
  for (let i = 0; i < index; i++) {
154
156
  const char = command[i];
157
+ // Inside a backtick substitution nested in double quotes, bash treats `\"`
158
+ // as a quote delimiter for the inner command, not as an escaped literal.
159
+ if (
160
+ char === "\\" &&
161
+ command[i + 1] === '"' &&
162
+ quote !== "'" &&
163
+ substitutions.at(-1)?.kind === "backtick" &&
164
+ substitutions.at(-1)?.outerQuote === '"'
165
+ ) {
166
+ quote = quote === '"' ? undefined : '"';
167
+ i++;
168
+ continue;
169
+ }
155
170
  if (char === "\\" && quote !== "'") {
156
171
  i++;
157
172
  continue;
@@ -165,15 +180,26 @@ function isInsideShellQuote(command: string, index: number): boolean {
165
180
  continue;
166
181
  }
167
182
  if (char === "$" && command[i + 1] === "(" && quote !== "'") {
168
- substitutions.push({ outerQuote: quote, depth: 1 });
183
+ substitutions.push({ kind: "dollar", outerQuote: quote, depth: 1 });
169
184
  quote = undefined;
170
185
  i++;
171
186
  continue;
172
187
  }
188
+ if (char === "`" && quote !== "'") {
189
+ const top = substitutions.at(-1);
190
+ if (top?.kind === "backtick") {
191
+ substitutions.pop();
192
+ quote = top.outerQuote;
193
+ } else {
194
+ substitutions.push({ kind: "backtick", outerQuote: quote, depth: 0 });
195
+ quote = undefined;
196
+ }
197
+ continue;
198
+ }
173
199
  if (quote !== undefined) continue;
174
200
 
175
201
  const substitution = substitutions.at(-1);
176
- if (!substitution) continue;
202
+ if (substitution?.kind !== "dollar") continue;
177
203
  if (char === "(") {
178
204
  substitution.depth++;
179
205
  } else if (char === ")") {