rovecode 0.3.3 → 0.4.0-beta.3

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 (495) hide show
  1. package/CHANGELOG.md +527 -0
  2. package/README.md +46 -48
  3. package/bin/rovecode.js +24 -0
  4. package/dist/cli/app-j6gn14w3.js +2 -0
  5. package/dist/cli/ask-user-cwstt8fz.js +2 -0
  6. package/dist/cli/auth-login-9bbp9915.js +2 -0
  7. package/dist/cli/auth-m8p9grty.js +2 -0
  8. package/dist/cli/bench-16zqdms5.js +9 -0
  9. package/dist/cli/catalog-1xchffa4.js +2 -0
  10. package/dist/cli/cli-1n1zb64f.js +2 -0
  11. package/dist/cli/client-2t9gjkck.js +2 -0
  12. package/dist/cli/commands-exafvm2b.js +2 -0
  13. package/dist/cli/connect-6zde0kn3.js +2 -0
  14. package/dist/cli/context-cmd-5t43wgqt.js +2 -0
  15. package/dist/cli/context-report-kt01pw8y.js +2 -0
  16. package/dist/cli/count-remote-ap7x3vh6.js +2 -0
  17. package/dist/cli/design-ne5zszyh.js +2 -0
  18. package/dist/cli/dispatch-2r5myxye.js +2 -0
  19. package/dist/cli/doctor-ws4fh4tn.js +3 -0
  20. package/dist/cli/executor-bdrjn634.js +2 -0
  21. package/dist/cli/export-1mxb9g5p.js +2 -0
  22. package/dist/cli/files-g104xghh.js +2 -0
  23. package/dist/cli/gauntlet-07xrjpj7.js +2 -0
  24. package/dist/cli/gauntlet-runner-xvy64436.js +10 -0
  25. package/dist/cli/gauntlet-wave3-jm91yt5w.js +5 -0
  26. package/dist/cli/gauntlet-wave4-r13py7p1.js +14 -0
  27. package/dist/cli/hashline-znvrat11.js +2 -0
  28. package/dist/cli/http-xafw6fsh.js +143 -0
  29. package/dist/cli/index-1sgjm25y.js +2 -0
  30. package/dist/cli/init-g2m0tn4m.js +51 -0
  31. package/dist/cli/install-avaqjjqq.js +2 -0
  32. package/dist/cli/loop-mmpfft01.js +2 -0
  33. package/dist/cli/main-0904f6ps.js +5 -0
  34. package/dist/cli/main-0ab9fc26.js +9 -0
  35. package/dist/cli/main-0jys2ccn.js +3 -0
  36. package/dist/cli/main-0mtcdbs7.js +3 -0
  37. package/dist/cli/main-0z1w2zsg.js +3 -0
  38. package/dist/cli/main-1dchs7xv.js +18 -0
  39. package/dist/cli/main-1ereejm1.js +3 -0
  40. package/dist/cli/main-1k1kw6b5.js +3 -0
  41. package/dist/cli/main-27y4sm2k.js +38 -0
  42. package/dist/cli/main-2wwjex5j.js +58 -0
  43. package/dist/cli/main-2yeveeve.js +6 -0
  44. package/dist/cli/main-2yfck9b5.js +3 -0
  45. package/dist/cli/main-2zmzgkwh.js +3 -0
  46. package/dist/cli/main-351pz3z7.js +7 -0
  47. package/dist/cli/main-3gjqfh7a.js +6 -0
  48. package/dist/cli/main-3nf3kgve.js +3 -0
  49. package/dist/cli/main-3pjrb2hd.js +3 -0
  50. package/dist/cli/main-3rxcvgna.js +19 -0
  51. package/dist/cli/main-4b3jgy66.js +19 -0
  52. package/dist/cli/main-4wndhjdc.js +7 -0
  53. package/dist/cli/main-4xcmvxnk.js +3 -0
  54. package/dist/cli/main-5tbz0wbz.js +4 -0
  55. package/dist/cli/main-5ywnwthm.js +3 -0
  56. package/dist/cli/main-6b62vkz0.js +14 -0
  57. package/dist/cli/main-6dnk69vp.js +3 -0
  58. package/dist/cli/main-6genrmhs.js +136 -0
  59. package/dist/cli/main-73g7eff4.js +15 -0
  60. package/dist/cli/main-7c5thhjd.js +5 -0
  61. package/dist/cli/main-7rn6bqje.js +3 -0
  62. package/dist/cli/main-80haw7qk.js +4 -0
  63. package/dist/cli/main-875s60s2.js +4 -0
  64. package/dist/cli/main-8kjxbpw4.js +8 -0
  65. package/dist/cli/main-90ds1z4e.js +10 -0
  66. package/dist/cli/main-9etavkew.js +3 -0
  67. package/dist/cli/main-a9njrkk1.js +3 -0
  68. package/dist/cli/main-aecrjq2d.js +12 -0
  69. package/dist/cli/main-ck9asesq.js +9 -0
  70. package/dist/cli/main-cta9racd.js +4 -0
  71. package/dist/cli/main-ddv7j2ag.js +3 -0
  72. package/dist/cli/main-dfreez27.js +10 -0
  73. package/dist/cli/main-f7rw7des.js +3 -0
  74. package/dist/cli/main-ggcn7rd7.js +5 -0
  75. package/dist/cli/main-gzkmycnv.js +3 -0
  76. package/dist/cli/main-hq51jg8v.js +18 -0
  77. package/dist/cli/main-jft389w9.js +8 -0
  78. package/dist/cli/main-k1eqkg83.js +3 -0
  79. package/dist/cli/main-k2y8a2aw.js +9 -0
  80. package/dist/cli/main-kcpbykxz.js +4 -0
  81. package/dist/cli/main-kd488vje.js +22 -0
  82. package/dist/cli/main-kh32yvgk.js +5 -0
  83. package/dist/cli/main-kqxnqjnv.js +25 -0
  84. package/dist/cli/main-kyn0xnsg.js +3 -0
  85. package/dist/cli/main-m1kk6fp5.js +21 -0
  86. package/dist/cli/main-mv40pcr2.js +4 -0
  87. package/dist/cli/main-n0t3973w.js +3 -0
  88. package/dist/cli/main-nqveez48.js +4 -0
  89. package/dist/cli/main-pknhvrmj.js +3 -0
  90. package/dist/cli/main-pn1w7a7j.js +3 -0
  91. package/dist/cli/main-prxxs70n.js +4 -0
  92. package/dist/cli/main-q3vsesf9.js +3 -0
  93. package/dist/cli/main-qsevpgsv.js +3 -0
  94. package/dist/cli/main-rdgdw24b.js +25 -0
  95. package/dist/cli/main-rfth4tbm.js +16 -0
  96. package/dist/cli/main-rg0wn0xf.js +5 -0
  97. package/dist/cli/main-sdmxhtv8.js +4 -0
  98. package/dist/cli/main-skbp13js.js +18 -0
  99. package/dist/cli/main-t4xnd213.js +7 -0
  100. package/dist/cli/main-vqak588n.js +4 -0
  101. package/dist/cli/main-w2n1303f.js +9 -0
  102. package/dist/cli/main-wbrdspr2.js +5 -0
  103. package/dist/cli/main-wsrg79c1.js +7 -0
  104. package/dist/cli/main-x4r0fne4.js +5 -0
  105. package/dist/cli/main-xea2f3tn.js +6 -0
  106. package/dist/cli/main-xg704a3c.js +3 -0
  107. package/dist/cli/main-xvnrabfp.js +16 -0
  108. package/dist/cli/main-xy53xf0r.js +4 -0
  109. package/dist/cli/main-y1fqy60y.js +3 -0
  110. package/dist/cli/main-yn8cd281.js +34 -0
  111. package/dist/cli/main-yr0ksc0h.js +4 -0
  112. package/dist/cli/main-z2ex2vyf.js +4 -0
  113. package/dist/cli/main-z3aayzvq.js +3 -0
  114. package/dist/cli/main-zaqh35jg.js +3 -0
  115. package/dist/cli/main-zc2e8e46.js +4 -0
  116. package/dist/cli/main-zzrfw6cf.js +13 -0
  117. package/dist/cli/main.js +280 -0
  118. package/dist/cli/market-cmd-e14kmx9n.js +5 -0
  119. package/dist/cli/mcp-login-wq7ktdek.js +2 -0
  120. package/dist/cli/mcp-market-cmd-9mg3jecy.js +2 -0
  121. package/dist/cli/notify-b7qc0cjb.js +2 -0
  122. package/dist/cli/oauth-z8whcgfx.js +2 -0
  123. package/dist/cli/output-b3ewj3ps.js +16 -0
  124. package/dist/cli/profiles-6mr5he5e.js +2 -0
  125. package/dist/cli/provider-config-g7j42q8x.js +2 -0
  126. package/dist/cli/provider-jr1y8vvm.js +2 -0
  127. package/dist/cli/registry-s8yk86g0.js +2 -0
  128. package/dist/cli/registry-t6p8d4mn.js +2 -0
  129. package/dist/cli/repl-bajwe1mh.js +11 -0
  130. package/dist/cli/resume-rwn9nz7y.js +2 -0
  131. package/dist/cli/run-flags-nah7ndpt.js +2 -0
  132. package/dist/cli/runtime-n7gafzhb.js +2 -0
  133. package/dist/cli/sandbox-config-emdy18x4.js +2 -0
  134. package/dist/cli/server-b0nvs2bn.js +5 -0
  135. package/dist/cli/session-arg-y75wd4kj.js +2 -0
  136. package/dist/cli/session-j62evmjq.js +2 -0
  137. package/dist/cli/sessions-cmd-tsnwz0ns.js +7 -0
  138. package/dist/cli/settings-df10wfez.js +2 -0
  139. package/dist/cli/setup-jzvv72fg.js +2 -0
  140. package/dist/cli/sextant-smoke-37m81ke6.js +5 -0
  141. package/dist/cli/skills-cmd-gjxnxnhx.js +2 -0
  142. package/dist/cli/smoke-p7748apt.js +8 -0
  143. package/dist/cli/start-chat-s4st3mm0.js +12 -0
  144. package/dist/cli/stream-gmeyewds.js +2 -0
  145. package/dist/cli/task-gh0kkp3n.js +2 -0
  146. package/dist/cli/tasks-z1kfpe8e.js +2 -0
  147. package/dist/cli/thinking-0eqkrz6t.js +2 -0
  148. package/dist/cli/todo-5brcrt9m.js +2 -0
  149. package/dist/cli/tools-7pzm0vj9.js +2 -0
  150. package/dist/cli/tools-s635p6s8.js +2 -0
  151. package/dist/cli/trust-cmd-cjav8zgm.js +2 -0
  152. package/dist/cli/update-check-pt31bm2f.js +2 -0
  153. package/dist/cli/update-cmd-tk131s9t.js +2 -0
  154. package/dist/cli/voice-56nabd8d.js +2 -0
  155. package/dist/cli/webfetch-xd8q596m.js +2 -0
  156. package/dist/cli/websearch-5hkf98k1.js +2 -0
  157. package/dist/cli/workflow-cmd-cy3cvzjp.js +4 -0
  158. package/dist/cli/workspace-q10g5z3e.js +2 -0
  159. package/dist/lib/index.js +62 -0
  160. package/dist/lib/models-index.json +1 -0
  161. package/dist/lib/plugins.js +55 -0
  162. package/dist/lib/providers.js +17 -0
  163. package/dist/lib/public-api.js +20 -0
  164. package/dist/lib/sdk.js +360 -0
  165. package/package.json +84 -72
  166. package/bin/rovecode.ts +0 -21
  167. package/src/acp/server.ts +0 -374
  168. package/src/cli/auth-login.ts +0 -122
  169. package/src/cli/connect.ts +0 -244
  170. package/src/cli/context-cmd.ts +0 -199
  171. package/src/cli/dispatch.ts +0 -82
  172. package/src/cli/doctor.ts +0 -362
  173. package/src/cli/export.ts +0 -276
  174. package/src/cli/help.ts +0 -293
  175. package/src/cli/is-tui-invocation.ts +0 -8
  176. package/src/cli/main.ts +0 -583
  177. package/src/cli/market-cmd.ts +0 -658
  178. package/src/cli/mcp-login.ts +0 -141
  179. package/src/cli/mcp-market-cmd.ts +0 -302
  180. package/src/cli/output.ts +0 -382
  181. package/src/cli/repl.ts +0 -250
  182. package/src/cli/repomap-root.ts +0 -14
  183. package/src/cli/resume.ts +0 -57
  184. package/src/cli/run-flags.ts +0 -43
  185. package/src/cli/run-limits.ts +0 -78
  186. package/src/cli/runtime.ts +0 -931
  187. package/src/cli/session-arg.ts +0 -30
  188. package/src/cli/sessions-cmd.ts +0 -145
  189. package/src/cli/setup.ts +0 -153
  190. package/src/cli/skills-cmd.ts +0 -194
  191. package/src/cli/start-chat.ts +0 -65
  192. package/src/cli/trust-cmd.ts +0 -52
  193. package/src/coding/bash.ts +0 -148
  194. package/src/coding/checkpoints.ts +0 -327
  195. package/src/coding/diff.ts +0 -138
  196. package/src/coding/files.ts +0 -341
  197. package/src/coding/hashline.ts +0 -274
  198. package/src/coding/lsp-gate.ts +0 -254
  199. package/src/coding/lsp-servers.ts +0 -147
  200. package/src/coding/lsp.ts +0 -283
  201. package/src/coding/repomap-cache.ts +0 -99
  202. package/src/coding/repomap-files.ts +0 -192
  203. package/src/coding/repomap.ts +0 -481
  204. package/src/core/agents.ts +0 -255
  205. package/src/core/compaction.ts +0 -259
  206. package/src/core/config.ts +0 -289
  207. package/src/core/context-report.ts +0 -228
  208. package/src/core/context.ts +0 -60
  209. package/src/core/count-remote.ts +0 -107
  210. package/src/core/execpolicy-rules.ts +0 -196
  211. package/src/core/execpolicy.ts +0 -385
  212. package/src/core/executor.ts +0 -454
  213. package/src/core/guardrails.ts +0 -400
  214. package/src/core/hooks.ts +0 -411
  215. package/src/core/images.ts +0 -230
  216. package/src/core/intro.ts +0 -266
  217. package/src/core/loop.ts +0 -567
  218. package/src/core/modes.ts +0 -372
  219. package/src/core/orchestrator.ts +0 -245
  220. package/src/core/proc-group.ts +0 -48
  221. package/src/core/project-trust.ts +0 -98
  222. package/src/core/reflection.ts +0 -165
  223. package/src/core/sandbox-config.ts +0 -186
  224. package/src/core/session-id.ts +0 -24
  225. package/src/core/session-images.ts +0 -73
  226. package/src/core/session-ops.ts +0 -183
  227. package/src/core/session-text.ts +0 -29
  228. package/src/core/session.ts +0 -469
  229. package/src/core/settings.ts +0 -170
  230. package/src/core/tasks.ts +0 -646
  231. package/src/core/token-scale.ts +0 -108
  232. package/src/core/tools.ts +0 -309
  233. package/src/core/trust.ts +0 -104
  234. package/src/core/types.ts +0 -330
  235. package/src/core/update-check.ts +0 -171
  236. package/src/core/usage.ts +0 -204
  237. package/src/core/validate.ts +0 -121
  238. package/src/core/verify-gate.ts +0 -159
  239. package/src/core/verify.ts +0 -236
  240. package/src/core/voice.ts +0 -158
  241. package/src/core/win-job.ts +0 -183
  242. package/src/core/workspace.ts +0 -184
  243. package/src/design/audit.ts +0 -797
  244. package/src/design/direction.ts +0 -190
  245. package/src/design/rules.ts +0 -157
  246. package/src/eval/bench.ts +0 -150
  247. package/src/eval/gauntlet-runner.ts +0 -215
  248. package/src/eval/gauntlet-support.ts +0 -84
  249. package/src/eval/gauntlet-wave3.ts +0 -269
  250. package/src/eval/gauntlet-wave4.ts +0 -217
  251. package/src/eval/gauntlet.ts +0 -253
  252. package/src/index.ts +0 -17
  253. package/src/lanes/agy.ts +0 -95
  254. package/src/lanes/approval.ts +0 -24
  255. package/src/lanes/claude.ts +0 -129
  256. package/src/lanes/codex.ts +0 -127
  257. package/src/lanes/events.ts +0 -130
  258. package/src/lanes/job.ts +0 -142
  259. package/src/lanes/opencode.ts +0 -122
  260. package/src/lanes/process.ts +0 -184
  261. package/src/lanes/progress.ts +0 -183
  262. package/src/lanes/registry.ts +0 -178
  263. package/src/lanes/runner.ts +0 -124
  264. package/src/lanes/types.ts +0 -112
  265. package/src/market/catalogs/mcp-docs.json +0 -111
  266. package/src/market/catalogs/plugins.json +0 -111
  267. package/src/market/catalogs/skills.json +0 -478
  268. package/src/market/clone.ts +0 -72
  269. package/src/market/context-cost.ts +0 -121
  270. package/src/market/digest.ts +0 -106
  271. package/src/market/index.ts +0 -22
  272. package/src/market/install.ts +0 -578
  273. package/src/market/manifest.ts +0 -187
  274. package/src/market/prereq.ts +0 -145
  275. package/src/market/registry.ts +0 -363
  276. package/src/market/resolve.ts +0 -111
  277. package/src/market/types.ts +0 -236
  278. package/src/market/validate.ts +0 -227
  279. package/src/mcp/client.ts +0 -449
  280. package/src/mcp/config.ts +0 -252
  281. package/src/mcp/local-package.ts +0 -211
  282. package/src/mcp/market-catalog.ts +0 -84
  283. package/src/mcp/market-install.ts +0 -289
  284. package/src/mcp/market.ts +0 -362
  285. package/src/mcp/oauth.ts +0 -251
  286. package/src/mcp/prompts-resources.ts +0 -249
  287. package/src/mcp/shared.ts +0 -149
  288. package/src/mcp/status.ts +0 -67
  289. package/src/mcp/tools.ts +0 -275
  290. package/src/mcp/transport.ts +0 -122
  291. package/src/mcp/trust.ts +0 -25
  292. package/src/memory/blocks.ts +0 -278
  293. package/src/memory/recall.ts +0 -355
  294. package/src/memory/scope.ts +0 -182
  295. package/src/memory/store.ts +0 -105
  296. package/src/memory/tools.ts +0 -99
  297. package/src/plugins/cli.ts +0 -119
  298. package/src/plugins/discover.ts +0 -108
  299. package/src/plugins/index.ts +0 -50
  300. package/src/plugins/install.ts +0 -184
  301. package/src/plugins/load.ts +0 -124
  302. package/src/plugins/manifest.ts +0 -92
  303. package/src/plugins/state.ts +0 -83
  304. package/src/providers/auth.ts +0 -408
  305. package/src/providers/cache.ts +0 -223
  306. package/src/providers/catalog-local.ts +0 -160
  307. package/src/providers/catalog.ts +0 -421
  308. package/src/providers/middleware-context.ts +0 -86
  309. package/src/providers/middleware.ts +0 -373
  310. package/src/providers/model-list.ts +0 -23
  311. package/src/providers/oauth/common.ts +0 -105
  312. package/src/providers/oauth/device-code.ts +0 -107
  313. package/src/providers/oauth/github-copilot.ts +0 -146
  314. package/src/providers/oauth/loopback.ts +0 -158
  315. package/src/providers/oauth/openai.ts +0 -163
  316. package/src/providers/oauth/openrouter.ts +0 -89
  317. package/src/providers/oauth/pkce.ts +0 -45
  318. package/src/providers/oauth/registry.ts +0 -39
  319. package/src/providers/oauth/seam.ts +0 -89
  320. package/src/providers/profile-glm53.ts +0 -111
  321. package/src/providers/profile-sonnet5-persona.ts +0 -65
  322. package/src/providers/profile-sonnet5-voice.ts +0 -23
  323. package/src/providers/profiles.ts +0 -156
  324. package/src/providers/provider-config.ts +0 -311
  325. package/src/providers/registry.ts +0 -333
  326. package/src/providers/responses.ts +0 -209
  327. package/src/providers/retry.ts +0 -234
  328. package/src/providers/router.ts +0 -294
  329. package/src/providers/sse.ts +0 -26
  330. package/src/providers/stream-errors.ts +0 -117
  331. package/src/providers/stream.ts +0 -566
  332. package/src/providers/thinking.ts +0 -189
  333. package/src/providers/wire-messages.ts +0 -129
  334. package/src/providers/wire-responses.ts +0 -79
  335. package/src/providers/wire-select.ts +0 -53
  336. package/src/server/http.ts +0 -291
  337. package/src/server/openapi.ts +0 -246
  338. package/src/sextant/card-hits.ts +0 -102
  339. package/src/sextant/card-keys.ts +0 -55
  340. package/src/sextant/context-source.ts +0 -157
  341. package/src/sextant/crew-cards.ts +0 -350
  342. package/src/sextant/draw-agents.ts +0 -273
  343. package/src/sextant/draw-code.ts +0 -388
  344. package/src/sextant/draw-context.ts +0 -222
  345. package/src/sextant/draw-frame.ts +0 -164
  346. package/src/sextant/draw-market.ts +0 -573
  347. package/src/sextant/draw-messages.ts +0 -386
  348. package/src/sextant/draw-pet.ts +0 -230
  349. package/src/sextant/draw-plan.ts +0 -187
  350. package/src/sextant/draw-tabs.ts +0 -85
  351. package/src/sextant/draw-util.ts +0 -65
  352. package/src/sextant/draw-wizard.ts +0 -378
  353. package/src/sextant/engine.ts +0 -230
  354. package/src/sextant/frame-hits.ts +0 -25
  355. package/src/sextant/frame.ts +0 -101
  356. package/src/sextant/git-status.ts +0 -197
  357. package/src/sextant/grid.ts +0 -59
  358. package/src/sextant/input.ts +0 -119
  359. package/src/sextant/keys.ts +0 -521
  360. package/src/sextant/layout.ts +0 -86
  361. package/src/sextant/local-commands.ts +0 -169
  362. package/src/sextant/market-source.ts +0 -287
  363. package/src/sextant/mentions.ts +0 -200
  364. package/src/sextant/message-hits.ts +0 -26
  365. package/src/sextant/model.ts +0 -387
  366. package/src/sextant/overlays.ts +0 -456
  367. package/src/sextant/panel-hits.ts +0 -38
  368. package/src/sextant/pet.ts +0 -399
  369. package/src/sextant/screen.ts +0 -324
  370. package/src/sextant/scroll-hits.ts +0 -66
  371. package/src/sextant/scrollbar.ts +0 -82
  372. package/src/sextant/sextant-bridge.ts +0 -174
  373. package/src/sextant/sextant-cards.ts +0 -142
  374. package/src/sextant/sextant-diff-base.ts +0 -63
  375. package/src/sextant/sextant-files.ts +0 -154
  376. package/src/sextant/sextant-frame-loop.ts +0 -335
  377. package/src/sextant/sextant-renderer.ts +0 -574
  378. package/src/sextant/sextant-repo.ts +0 -140
  379. package/src/sextant/theme.ts +0 -66
  380. package/src/sextant/tool-rows.ts +0 -189
  381. package/src/sextant/types.ts +0 -493
  382. package/src/skills/index.ts +0 -387
  383. package/src/skills/pack.ts +0 -220
  384. package/src/skills/spec.ts +0 -162
  385. package/src/skills/tools.ts +0 -69
  386. package/src/skills/versioned.ts +0 -227
  387. package/src/telemetry/otel-export.ts +0 -122
  388. package/src/telemetry/otel-lanes.ts +0 -89
  389. package/src/telemetry/otel-logs.ts +0 -131
  390. package/src/telemetry/otel-metrics.ts +0 -136
  391. package/src/telemetry/otel.ts +0 -397
  392. package/src/telemetry/otlp.ts +0 -76
  393. package/src/tools/ask-user.ts +0 -156
  394. package/src/tools/bash-bg.ts +0 -94
  395. package/src/tools/bash-jobs.ts +0 -237
  396. package/src/tools/design.ts +0 -151
  397. package/src/tools/evalcell.ts +0 -338
  398. package/src/tools/html-text.ts +0 -139
  399. package/src/tools/provider.ts +0 -149
  400. package/src/tools/task.ts +0 -250
  401. package/src/tools/todo.ts +0 -320
  402. package/src/tools/webfetch.ts +0 -332
  403. package/src/tools/websearch.ts +0 -359
  404. package/src/tui/agents-cmd.ts +0 -41
  405. package/src/tui/app.ts +0 -749
  406. package/src/tui/attach.ts +0 -127
  407. package/src/tui/boot-notes.ts +0 -41
  408. package/src/tui/builtin-prompts.ts +0 -59
  409. package/src/tui/checkpoints-cmd.ts +0 -70
  410. package/src/tui/clipboard-image.ts +0 -81
  411. package/src/tui/clipboard.ts +0 -78
  412. package/src/tui/commands.ts +0 -283
  413. package/src/tui/config-view.ts +0 -53
  414. package/src/tui/context-cmds.ts +0 -282
  415. package/src/tui/cost.ts +0 -108
  416. package/src/tui/crash-guard.ts +0 -173
  417. package/src/tui/focus-terminal.ts +0 -34
  418. package/src/tui/git-cmds.ts +0 -273
  419. package/src/tui/git-plain.ts +0 -58
  420. package/src/tui/info-cmd.ts +0 -150
  421. package/src/tui/input-plain.ts +0 -76
  422. package/src/tui/mcp-cmd.ts +0 -128
  423. package/src/tui/memory-note.ts +0 -77
  424. package/src/tui/modes-cmd.ts +0 -45
  425. package/src/tui/notify-seq.ts +0 -100
  426. package/src/tui/notify.ts +0 -318
  427. package/src/tui/overlays.ts +0 -97
  428. package/src/tui/pi-renderer.ts +0 -428
  429. package/src/tui/providers-cmd.ts +0 -377
  430. package/src/tui/reasoning-view.ts +0 -56
  431. package/src/tui/renderer.ts +0 -128
  432. package/src/tui/replay-marker.ts +0 -29
  433. package/src/tui/session-cmd.ts +0 -148
  434. package/src/tui/session-manage.ts +0 -95
  435. package/src/tui/sextant-attach.ts +0 -102
  436. package/src/tui/sextant-io.ts +0 -202
  437. package/src/tui/sextant-smoke.ts +0 -110
  438. package/src/tui/shell-cmd.ts +0 -158
  439. package/src/tui/smoke.ts +0 -72
  440. package/src/tui/staged-terminal.ts +0 -50
  441. package/src/tui/startup.ts +0 -12
  442. package/src/tui/theme.ts +0 -59
  443. package/src/tui/todo-label.ts +0 -7
  444. package/src/tui/trust-card.ts +0 -107
  445. package/src/tui/tui-commands.ts +0 -87
  446. package/tsconfig.json +0 -30
  447. package/vendor/pi-tui/LICENSE +0 -21
  448. package/vendor/pi-tui/PATCHES.md +0 -12
  449. package/vendor/pi-tui/PROVENANCE.md +0 -12
  450. package/vendor/pi-tui/README.upstream.md +0 -854
  451. package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  452. package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  453. package/vendor/pi-tui/src/alt-screen-search.ts +0 -158
  454. package/vendor/pi-tui/src/autocomplete.ts +0 -827
  455. package/vendor/pi-tui/src/components/alt-screen-flash.ts +0 -52
  456. package/vendor/pi-tui/src/components/box.ts +0 -138
  457. package/vendor/pi-tui/src/components/cancellable-loader.ts +0 -41
  458. package/vendor/pi-tui/src/components/editor.ts +0 -2364
  459. package/vendor/pi-tui/src/components/h-stack.ts +0 -45
  460. package/vendor/pi-tui/src/components/image.ts +0 -128
  461. package/vendor/pi-tui/src/components/input.ts +0 -448
  462. package/vendor/pi-tui/src/components/loader.ts +0 -93
  463. package/vendor/pi-tui/src/components/markdown.ts +0 -1016
  464. package/vendor/pi-tui/src/components/scroll-view.ts +0 -217
  465. package/vendor/pi-tui/src/components/select-list.ts +0 -230
  466. package/vendor/pi-tui/src/components/settings-list.ts +0 -277
  467. package/vendor/pi-tui/src/components/spacer.ts +0 -29
  468. package/vendor/pi-tui/src/components/stack.ts +0 -155
  469. package/vendor/pi-tui/src/components/text.ts +0 -108
  470. package/vendor/pi-tui/src/components/truncated-text.ts +0 -66
  471. package/vendor/pi-tui/src/components/v-stack.ts +0 -34
  472. package/vendor/pi-tui/src/editor-component.ts +0 -75
  473. package/vendor/pi-tui/src/fuzzy.ts +0 -138
  474. package/vendor/pi-tui/src/index.ts +0 -149
  475. package/vendor/pi-tui/src/keybindings.ts +0 -321
  476. package/vendor/pi-tui/src/keys.ts +0 -1402
  477. package/vendor/pi-tui/src/kill-ring.ts +0 -47
  478. package/vendor/pi-tui/src/latex.ts +0 -1381
  479. package/vendor/pi-tui/src/layout-node.ts +0 -52
  480. package/vendor/pi-tui/src/layout.ts +0 -411
  481. package/vendor/pi-tui/src/native-modifiers.ts +0 -60
  482. package/vendor/pi-tui/src/native-module-path.ts +0 -32
  483. package/vendor/pi-tui/src/stdin-buffer.ts +0 -445
  484. package/vendor/pi-tui/src/terminal-colors.ts +0 -74
  485. package/vendor/pi-tui/src/terminal-image.ts +0 -701
  486. package/vendor/pi-tui/src/terminal.ts +0 -554
  487. package/vendor/pi-tui/src/tui-alt-screen.ts +0 -1379
  488. package/vendor/pi-tui/src/tui-main-screen.ts +0 -655
  489. package/vendor/pi-tui/src/tui.ts +0 -1264
  490. package/vendor/pi-tui/src/undo-stack.ts +0 -29
  491. package/vendor/pi-tui/src/utils.ts +0 -1327
  492. package/vendor/pi-tui/src/word-navigation.ts +0 -118
  493. package/vendor/pi-tui/test/test-themes.ts +0 -39
  494. package/vendor/pi-tui/test/virtual-terminal.ts +0 -219
  495. /package/{src/providers → dist/cli}/models-index.json +0 -0
@@ -1,454 +0,0 @@
1
- /** Executor sandbox ladder (PORT #10): ONE seam for shell execution with
2
- * selectable rungs — `direct` | `wsl` | `docker`.
3
- *
4
- * Pattern source: openai/codex, Apache-2.0, Copyright 2025 OpenAI (snapshot
5
- * research/source_snapshots/openai-codex @ 379d50be). Ported at pattern
6
- * level, no code copied:
7
- * - tiered execution selected per platform: SandboxType +
8
- * get_platform_sandbox(), codex-rs/sandboxing/src/manager.rs:37,62;
9
- * - availability PROBED at runtime by a trial spawn THROUGH the wrapper
10
- * itself — the probe argv is the rung's own run shape with `true` as the
11
- * command, exactly as codex probes bubblewrap by running `bwrap … /bin/true`
12
- * (codex-rs/sandboxing/src/bwrap.rs:74). Probing anything weaker lies:
13
- * `wsl.exe --status` exits 0 on a machine whose default distro has no bash
14
- * (e.g. docker-desktop), and `docker version` proves a daemon but not that
15
- * the image exists or contains bash;
16
- * - probes are TIME-BOUNDED at codex's own 500ms cap (bwrap.rs:36,67);
17
- * a probe that cannot answer in time IS unavailable right now;
18
- * - an explicitly requested but unprovidable tier is a HARD ERROR
19
- * (SandboxTransformError::{SeatbeltUnavailable,…}, manager.rs:203-222,410).
20
- * Deliberately NOT ported: codex's silent `unwrap_or(SandboxType::None)`
21
- * degrade (manager.rs:305) — codex recovers via its approval layer
22
- * (core/src/exec_policy.rs:753-771); rovecode has no compensator at this seam,
23
- * so per the bar an unavailable rung is a loud RungUnavailableError and
24
- * NEVER a silent fallback DOWN the ladder. The docker rung follows the
25
- * OpenHands runtime-boundary shape (execute inside a container with the
26
- * workspace mounted) — OpenHands is not snapshotted; provenance gap.
27
- *
28
- * Honesty note (matches README): every rung is DELEGATION, not a native
29
- * sandbox. `direct` is today's blocklist-only behavior; `wsl`/`docker`
30
- * isolate only as well as the wrapped runtime does. */
31
-
32
- import { existsSync } from "node:fs";
33
- import { createWinJob, type WinJob } from "./win-job.ts";
34
- import { groupSpawnOptions, killGroup } from "./proc-group.ts";
35
-
36
- // ---------- Ladder ----------
37
-
38
- export type Rung = "direct" | "wsl" | "docker";
39
-
40
- /** Ladder order, weakest isolation first. Order is informational only —
41
- * selection is always explicit, never walked automatically. */
42
- export const RUNGS: readonly Rung[] = ["direct", "wsl", "docker"];
43
-
44
- export interface ExecResult { code: number; text: string }
45
-
46
- export interface Executor {
47
- readonly rung: Rung;
48
- /** Run `cmd` through bash in `cwd`. Result matches today's bashTool
49
- * internals: exit code + stdout (+ stderr section), sliced to 10k chars.
50
- * `observe` (port #55) streams the bytes and the pid while the command is still running — a
51
- * background job's only way to be readable before it finishes. Omitted: nothing is installed. */
52
- run(cmd: string, cwd: string, signal?: AbortSignal, observe?: SpawnObserver): Promise<ExecResult>;
53
- }
54
-
55
- // ---------- Raw process running (injectable so tests cover probe paths) ----------
56
-
57
- export interface RawResult {
58
- code: number; stdout: string; stderr: string;
59
- /** Windows runs with a signal: "job" (Job Object tree kill + taskkill sweep)
60
- * or the fail-safe "taskkill-only" (no bun:ffi/kernel32, or not assignable) */
61
- treeKill?: "job" | "taskkill-only";
62
- }
63
-
64
- /** Taps on a running process (port #55). A foreground run needs neither: it awaits the whole result and
65
- * reads the text once. A BACKGROUND job needs both — its output has to be readable while the command is
66
- * still running, and its pid is what a `bash_kill` names. Optional everywhere, so a run that does not
67
- * observe is byte-identical to what it was: no tap installed, no per-chunk work. */
68
- export interface SpawnObserver {
69
- /** the launcher's pid, once, right after spawn */
70
- onSpawn?(pid: number): void;
71
- /** raw bytes as they arrive, per stream — decoding is the observer's business, not the runner's */
72
- onChunk?(stream: "stdout" | "stderr", bytes: Uint8Array): void;
73
- }
74
-
75
- export type SpawnRunner = (
76
- argv: readonly string[],
77
- opts: { cwd?: string; signal?: AbortSignal; observe?: SpawnObserver },
78
- ) => Promise<RawResult>;
79
-
80
- /** Post-abort grace (port #21 HIGH-1b): the runner waits this long for pipes +
81
- * exit, then settles with the bytes so far + ABORT_TRUNCATED_MARKER, code 143.
82
- * Only a process outside the job can hold a pipe end past the kill (the
83
- * taskkill-only fallback measured an msys `sleep` holding stdout — and the
84
- * runner promise — for its whole duration). REF'D timer, deliberately: Bun's
85
- * AbortSignal.timeout timers are not, and an idle loop never fires them. */
86
- export const ABORT_GRACE_MS = 500;
87
- export const ABORT_TRUNCATED_MARKER = "[output truncated: process tree terminated on abort]";
88
-
89
- /** Incremental pipe reader: bytes so far are readable at any moment, and the
90
- * read is cancellable (drops our end) when an orphan holds the other.
91
- * Decodes like Response.text(): UTF-8, leading BOM stripped. */
92
- function collect(stream: ReadableStream<Uint8Array>, tap?: (bytes: Uint8Array) => void): { done: Promise<void>; text(): string; cancel(): void } {
93
- const chunks: Uint8Array[] = [];
94
- const reader = stream.getReader();
95
- const done = (async () => {
96
- for (let c = await reader.read(); !c.done; c = await reader.read()) if (c.value) { chunks.push(c.value); tap?.(c.value); }
97
- })().catch(() => {});
98
- return { done, text: () => new TextDecoder().decode(Buffer.concat(chunks)), cancel: () => { reader.cancel().catch(() => {}); } };
99
- }
100
-
101
- /** Default runner: Bun.spawn, stdout/stderr piped. A spawn failure (missing
102
- * binary) is returned as code -1 with the message in stderr, so probes can
103
- * report "not installed" instead of crashing.
104
- * Windows abort = TREE kill (port #21). Bun's own signal handling
105
- * TerminateProcess-es only the DIRECT child, and `taskkill /T` walks live
106
- * parent links that msys2's exec breaks (win-job.ts) — measured with taskkill
107
- * alone, the `sleep` inside `a; sleep N; b`, `bash -c 'sleep N'` and
108
- * `sleep N & sleep N & wait` survived 3/3 and held stdout so the runner hung
109
- * 3/3. So on Windows the signal is NOT handed to Bun.spawn: the launcher goes
110
- * into a Job Object right after spawn; abort fires TerminateJobObject (every
111
- * descendant, connected or not) plus `taskkill /T /F` as a sweep; a box
112
- * without job objects keeps taskkill alone and says so in `treeKill`.
113
- * POSIX abort = GROUP kill (port #67, ported from the aion harness with the
114
- * lanes). Bun's signal passthrough SIGTERMs the DIRECT child only: the shell
115
- * never runs its next statement, but a forked grandchild (`sleep`/`npm` inside
116
- * a compound command, anything behind `&`) is orphaned, finishes on its own and
117
- * keeps stdout open while it does — which hung the runner, not just leaked the
118
- * process. So the signal is kept from Bun.spawn here too; the child is spawned
119
- * `detached` (its own session and process group, pgid = pid) and abort sends
120
- * SIGTERM to `-pid` — shell, grandchildren and backgrounded members alike —
121
- * then SIGTERM to the launcher as a fallback, then SIGKILL to the group if the
122
- * grace expires with a pipe still held. A run WITHOUT a signal is spawned
123
- * exactly as before on both platforms: nothing can cancel it, so nothing needs
124
- * a group. HONESTY, unchanged from the note this replaces: the reference box is
125
- * Windows-only, so the POSIX path is exercised here by its decision function
126
- * and its spawn options, not by a killed grandchild. What is measured is the
127
- * harness this came from; what is pinned here is that win32 options stay
128
- * byte-identical and that a pre-aborted signal never spawns on either path. */
129
- /** Which cancellation shape a spawn gets. Exported because it is the only part of the POSIX path a
130
- * Windows box can pin: `grouped` decides both the spawn options and the abort order, and a run with no
131
- * signal must come out false on BOTH platforms — an uncancellable run keeps the plain spawn it always had. */
132
- export function abortShape(platform: NodeJS.Platform, hasSignal: boolean): { treeKill: boolean; grouped: boolean } {
133
- if (!hasSignal) return { treeKill: false, grouped: false };
134
- return platform === "win32" ? { treeKill: true, grouped: false } : { treeKill: false, grouped: true };
135
- }
136
-
137
- export const bunRunner: SpawnRunner = async (argv, opts) => {
138
- const signal = opts.signal;
139
- const { treeKill, grouped } = abortShape(process.platform, signal !== undefined);
140
- // pre-aborted (G4 pin): never spawn at all — a kill would race a fast
141
- // command and lose; 143 matches the killed-at-spawn shape the pin measured.
142
- // #67 extends this to POSIX: a listener added to an ALREADY-aborted signal
143
- // never fires, so a spawn there would run to completion uncancelled — the
144
- // one case where "we handed it the signal" and "it can be stopped" differ.
145
- if ((treeKill || grouped) && signal?.aborted) return { code: 143, stdout: "", stderr: "aborted before spawn" };
146
- let onAbort: (() => void) | undefined;
147
- let job: WinJob | null = null;
148
- let killed = false;
149
- let graceTimer: ReturnType<typeof setTimeout> | undefined;
150
- try {
151
- const proc = Bun.spawn([...argv], {
152
- cwd: opts.cwd,
153
- signal: (treeKill || grouped) ? undefined : signal,
154
- stdout: "pipe", stderr: "pipe",
155
- // #67: `detached: true` on POSIX when there is something to cancel; EMPTY on win32 and empty for an
156
- // uncancellable run, so those two option objects are byte-identical to what they were
157
- ...(grouped ? groupSpawnOptions(process.platform) : {}),
158
- });
159
- opts.observe?.onSpawn?.(proc.pid);
160
- const ob = opts.observe;
161
- const out = collect(proc.stdout, ob?.onChunk ? (b) => ob.onChunk!("stdout", b) : undefined);
162
- const err = collect(proc.stderr, ob?.onChunk ? (b) => ob.onChunk!("stderr", b) : undefined);
163
- const finished = Promise.all([out.done, err.done, proc.exited]).then(() => "done" as const);
164
- let graceUp!: () => void;
165
- const grace = new Promise<"grace">((r) => { graceUp = () => r("grace"); });
166
- if (signal) {
167
- if (treeKill) { job = createWinJob(); if (job && !job.assign(proc.pid)) job = null; } // null → taskkill-only
168
- onAbort = () => {
169
- // a dead launcher may have left a child holding a pipe end (`sleep N & echo x`): the job
170
- // is still armed (the RUNNER has not settled) — terminate regardless; taskkill only a LIVE pid (reuse)
171
- const launcherAlive = proc.exitCode === null;
172
- killed = treeKill || grouped;
173
- if (treeKill) {
174
- job?.terminate();
175
- if (launcherAlive) { try { Bun.spawn(["taskkill", "/T", "/F", "/PID", String(proc.pid)], { stdout: "ignore", stderr: "ignore" }); } catch { /* best-effort */ } }
176
- }
177
- if (grouped) {
178
- // the GROUP first — the orphan holding stdout is in it and the launcher is not its only member
179
- killGroup(proc.pid, "SIGTERM");
180
- if (launcherAlive) { try { proc.kill("SIGTERM"); } catch { /* already gone */ } }
181
- }
182
- graceTimer = setTimeout(graceUp, ABORT_GRACE_MS);
183
- };
184
- signal.addEventListener("abort", onAbort, { once: true });
185
- void finished.then(() => { if (onAbort) signal.removeEventListener("abort", onAbort); }, () => {});
186
- }
187
- const won = await Promise.race([finished, grace]);
188
- // #67: the grace expired with a pipe still held — something in the group ignored SIGTERM. Escalate to
189
- // the group, not to the launcher: the launcher is usually already gone and is never what is holding on.
190
- if (won === "grace" && grouped) killGroup(proc.pid, "SIGKILL");
191
- if (won === "grace") { out.cancel(); err.cancel(); }
192
- const code = (killed || won === "grace") ? 143 : await proc.exited;
193
- let stderr = err.text();
194
- if (won === "grace") stderr += (stderr ? "\n" : "") + ABORT_TRUNCATED_MARKER;
195
- const r: RawResult = { code, stdout: out.text(), stderr };
196
- if (treeKill) r.treeKill = job ? "job" : "taskkill-only";
197
- return r;
198
- } catch (e) {
199
- return { code: -1, stdout: "", stderr: `spawn failed: ${e instanceof Error ? e.message : String(e)}` };
200
- } finally {
201
- clearTimeout(graceTimer);
202
- if (killed) job?.terminate(); else job?.release(); // both no-ops once the abort path closed the job
203
- // the signal is the RUN's (long-lived): drop this spawn's listener or a
204
- // multi-bash run accumulates one dead closure per command
205
- if (onAbort) signal?.removeEventListener("abort", onAbort);
206
- }
207
- };
208
-
209
- /** The cap assemble() slices a command's output to. Exported for the git commands (tui/git-cmds.ts):
210
- * a diff this long is a PREFIX, and both the draft prompt and the approval card must say so. */
211
- export const OUTPUT_CAP_CHARS = 10_000;
212
-
213
- /** Byte-compatible with hashline runOnce text assembly: stdout, then an
214
- * optional `\nstderr:\n…` section, sliced to 10k chars. */
215
- function assemble(stdout: string, stderr: string): string {
216
- return (stdout + (stderr ? `\nstderr:\n${stderr}` : "")).slice(0, OUTPUT_CAP_CHARS);
217
- }
218
-
219
- /** Byte-compatible with hashline bashBin(): on Windows prefer Git bash —
220
- * System32 bash.exe is the WSL relay and breaks on non-WSL machines. */
221
- let bashCache: string | null = null;
222
- function bashBin(): string {
223
- if (bashCache !== null) return bashCache;
224
- if (process.platform !== "win32") return (bashCache = "bash");
225
- const git = "C:/Program Files/Git/bin/bash.exe";
226
- bashCache = existsSync(git) ? git : "bash";
227
- return bashCache;
228
- }
229
-
230
- // ---------- Probes (availability is checked, never assumed) ----------
231
-
232
- export interface RungProbe { rung: Rung; available: boolean; detail: string }
233
-
234
- /** Probe deadline: 500ms, upstream's own bwrap cap (bwrap.rs:36,67). Known
235
- * tradeoff, accepted deliberately: a COLD `wsl.exe --exec bash -c true`
236
- * (utility-VM boot) measured 2842ms on the reference machine vs 197ms warm —
237
- * a cold probe times out and the rung reports unavailable with a detail that
238
- * says to warm it and retry. That is the contract: timeout ⇒ unavailable NOW,
239
- * never a probe that hangs the session. */
240
- export const PROBE_TIMEOUT_MS = 500;
241
-
242
- export interface ProbeOptions {
243
- /** docker rung: image the trial (and later every command) runs in */
244
- dockerImage?: string;
245
- /** probe deadline override (tests); defaults to PROBE_TIMEOUT_MS */
246
- timeoutMs?: number;
247
- }
248
-
249
- /** wsl.exe emits UTF-16LE; drop NULs before quoting output in a detail. */
250
- function probeText(s: string): string {
251
- return s.replace(/\u0000/g, "").trim().slice(0, 200);
252
- }
253
-
254
- /** One bounded trial spawn. The AbortSignal kills the trial process at the
255
- * deadline; a separate REF'D setTimeout resolves the race. Two reasons the
256
- * race must NOT wait on the signal's own 'abort' event: (a) the killed
257
- * wrapper's children can keep the stdout pipe open past the kill — measured
258
- * on Windows: a 300ms abort delivered exit 143 but the runner promise only
259
- * settled ~5s later when the orphaned grandchild released the pipe; (b) Bun's
260
- * AbortSignal.timeout timer is UNREF'D — on an otherwise idle event loop it
261
- * never fires at all (this hung the whole test run before it was caught). */
262
- async function trialSpawn(
263
- runner: SpawnRunner,
264
- argv: string[],
265
- timeoutMs: number,
266
- ): Promise<{ r: RawResult; timedOut: boolean }> {
267
- const deadline: RawResult = { code: -1, stdout: "", stderr: `probe timed out after ${timeoutMs}ms` };
268
- let timer: ReturnType<typeof setTimeout> | undefined;
269
- const onDeadline = new Promise<RawResult>((resolve) => { timer = setTimeout(() => resolve(deadline), timeoutMs); });
270
- try {
271
- const r = await Promise.race([runner(argv, { signal: AbortSignal.timeout(timeoutMs) }), onDeadline]);
272
- return { r, timedOut: r === deadline };
273
- } finally {
274
- clearTimeout(timer);
275
- }
276
- }
277
-
278
- /** Probe verdicts come from spawning `bash -c true` THROUGH the rung's own
279
- * wrapper (bwrap.rs:74 shape) — the exact failure a real command would hit
280
- * (missing wsl.exe, bash-less default distro, dead daemon, unpulled or
281
- * bash-less image) is the failure the probe reports. */
282
- export async function probeRung(
283
- rung: Rung,
284
- runner: SpawnRunner = bunRunner,
285
- platform: NodeJS.Platform = process.platform,
286
- opts: ProbeOptions = {},
287
- ): Promise<RungProbe> {
288
- const timeoutMs = opts.timeoutMs ?? PROBE_TIMEOUT_MS;
289
- switch (rung) {
290
- case "direct":
291
- return { rung, available: true, detail: "always available — today's in-process bash (blocklist only, NOT a sandbox)" };
292
- case "wsl": {
293
- if (platform !== "win32") {
294
- return { rung, available: false, detail: `wsl rung requires Windows wsl.exe (platform is ${platform})` };
295
- }
296
- // Trial spawn through the wrapper, NOT `wsl.exe --status`: --status exits
297
- // 0 whenever the subsystem is installed, even when the default distro has
298
- // no bash (docker-desktop) and every real command would exit 1.
299
- const shape = "wsl.exe --exec bash -c true";
300
- const { r, timedOut } = await trialSpawn(runner, ["wsl.exe", "--exec", "bash", "-c", "true"], timeoutMs);
301
- if (timedOut) {
302
- return { rung, available: false, detail: `wsl trial (${shape}) timed out after ${timeoutMs}ms — wrapper did not answer (a cold WSL utility-VM boot exceeds this cap); warm it with the same command and reconfigure` };
303
- }
304
- return r.code === 0
305
- ? { rung, available: true, detail: `wsl trial (${shape}) ok` }
306
- : { rung, available: false, detail: `wsl trial (${shape}) exited ${r.code}: ${probeText(r.stderr || r.stdout) || "no output"}` };
307
- }
308
- case "docker": {
309
- // Trial container run, NOT `docker version`: the version handshake proves
310
- // a daemon but not that the image is present or contains bash.
311
- const image = opts.dockerImage ?? DEFAULT_DOCKER_IMAGE;
312
- const shape = `docker run --rm ${image} bash -c true`;
313
- const { r, timedOut } = await trialSpawn(runner, ["docker", "run", "--rm", image, "bash", "-c", "true"], timeoutMs);
314
- if (timedOut) {
315
- return { rung, available: false, detail: `docker trial (${shape}) timed out after ${timeoutMs}ms — daemon wedged or pulling the image; pre-pull it and retry` };
316
- }
317
- return r.code === 0
318
- ? { rung, available: true, detail: `docker trial (${shape}) ok` }
319
- : { rung, available: false, detail: `docker trial (${shape}) exited ${r.code}: ${probeText(r.stderr || r.stdout) || "no output"}` };
320
- }
321
- }
322
- }
323
-
324
- /** Probe every rung (status surfaces; lets a caller CHOOSE, not fall). */
325
- export function probeLadder(
326
- runner: SpawnRunner = bunRunner,
327
- platform: NodeJS.Platform = process.platform,
328
- opts: ProbeOptions = {},
329
- ): Promise<RungProbe[]> {
330
- return Promise.all(RUNGS.map((r) => probeRung(r, runner, platform, opts)));
331
- }
332
-
333
- // ---------- Unavailable rung = loud error (never fall DOWN the ladder) ----------
334
-
335
- export class RungUnavailableError extends Error {
336
- readonly rung: Rung;
337
- readonly detail: string;
338
- constructor(rung: Rung, detail: string) {
339
- super(
340
- `executor rung '${rung}' is unavailable: ${detail}. ` +
341
- `Refusing to fall back down the ladder — pick an available rung explicitly ` +
342
- `(probeLadder() reports availability) or make '${rung}' usable and retry.`,
343
- );
344
- this.name = "RungUnavailableError";
345
- this.rung = rung;
346
- this.detail = detail;
347
- }
348
- }
349
-
350
- // ---------- Rung constructors ----------
351
-
352
- /** Must contain bash; override per-project via ExecutorOptions.dockerImage. */
353
- export const DEFAULT_DOCKER_IMAGE = "debian:stable-slim";
354
-
355
- export interface ExecutorOptions {
356
- runner?: SpawnRunner;
357
- /** docker rung: image the command runs in (must provide bash) */
358
- dockerImage?: string;
359
- /** test seam: platform used by probes (defaults to process.platform) */
360
- platform?: NodeJS.Platform;
361
- }
362
-
363
- function directExecutor(runner: SpawnRunner): Executor {
364
- return {
365
- rung: "direct",
366
- async run(cmd, cwd, signal, observe) {
367
- // Byte-compatible with hashline runOnce: same argv, cwd, signal, assembly.
368
- const r = await runner([bashBin(), "-c", cmd], { cwd, signal, ...(observe ? { observe } : {}) });
369
- return { code: r.code, text: assemble(r.stdout, r.stderr) };
370
- },
371
- };
372
- }
373
-
374
- function wslExecutor(runner: SpawnRunner): Executor {
375
- return {
376
- rung: "wsl",
377
- async run(cmd, cwd, signal, observe) {
378
- // --cd translates the Windows cwd into the distro mount; --exec runs
379
- // bash directly with argv boundaries intact (no double-shell quoting).
380
- const r = await runner(["wsl.exe", "--cd", cwd, "--exec", "bash", "-c", cmd], { cwd, signal, ...(observe ? { observe } : {}) });
381
- return { code: r.code, text: assemble(r.stdout, r.stderr) };
382
- },
383
- };
384
- }
385
-
386
- function dockerExecutor(runner: SpawnRunner, image: string): Executor {
387
- return {
388
- rung: "docker",
389
- async run(cmd, cwd, signal, observe) {
390
- // Workspace mounted read-write at /workspace; container removed after
391
- // the run. The container is the boundary (OpenHands runtime shape).
392
- const argv = ["docker", "run", "--rm", "-v", `${cwd}:/workspace`, "-w", "/workspace", image, "bash", "-c", cmd];
393
- const r = await runner(argv, { cwd, signal, ...(observe ? { observe } : {}) });
394
- return { code: r.code, text: assemble(r.stdout, r.stderr) };
395
- },
396
- };
397
- }
398
-
399
- /** Probe `rung`, then construct its executor. Unavailable → throws
400
- * RungUnavailableError. The requested rung is always the returned rung —
401
- * no substitution, in either direction. */
402
- export async function createExecutor(rung: Rung, opts: ExecutorOptions = {}): Promise<Executor> {
403
- const runner = opts.runner ?? bunRunner;
404
- const probe = await probeRung(rung, runner, opts.platform ?? process.platform, { dockerImage: opts.dockerImage });
405
- if (!probe.available) throw new RungUnavailableError(rung, probe.detail);
406
- switch (rung) {
407
- case "direct": return directExecutor(runner);
408
- case "wsl": return wslExecutor(runner);
409
- case "docker": return dockerExecutor(runner, opts.dockerImage ?? DEFAULT_DOCKER_IMAGE);
410
- }
411
- }
412
-
413
- // ---------- Session seam (what bashTool calls through) ----------
414
-
415
- /** The seam tracks the DESIRED rung, not just the installed executor: a
416
- * failed configure must never silently degrade to whatever was installed
417
- * before (or to lazy `direct`). Until the desire is met, getExecutor()
418
- * throws — the same loud RungUnavailableError contract as createExecutor. */
419
- let current: Executor | null = null;
420
- let desired: Rung = "direct";
421
- let lastConfigureFailure: string | null = null;
422
-
423
- /** Probe + install the session executor. The requested rung becomes the
424
- * seam's DESIRED rung before probing: if the probe fails, this throws AND
425
- * every later getExecutor() throws too, until a configure succeeds or
426
- * resetExecutor() restores the direct default. The seam never hands out a
427
- * rung other than the one last asked for. */
428
- export async function configureExecutor(rung: Rung, opts?: ExecutorOptions): Promise<Executor> {
429
- desired = rung;
430
- try {
431
- current = await createExecutor(rung, opts);
432
- lastConfigureFailure = null;
433
- return current;
434
- } catch (e) {
435
- lastConfigureFailure = e instanceof RungUnavailableError ? e.detail
436
- : e instanceof Error ? e.message : String(e);
437
- throw e;
438
- }
439
- }
440
-
441
- /** Current executor. Until configured, the `direct` rung (today's behavior,
442
- * the one rung that needs no probe). After a FAILED configure this throws
443
- * RungUnavailableError for the desired rung — no silent fallback. */
444
- export function getExecutor(): Executor {
445
- if (current?.rung === desired) return current;
446
- if (desired === "direct") return (current = directExecutor(bunRunner));
447
- throw new RungUnavailableError(
448
- desired,
449
- lastConfigureFailure ?? `configureExecutor('${desired}') has not succeeded; the seam refuses to substitute another rung`,
450
- );
451
- }
452
-
453
- /** Test seam: forget the configured executor and the desired rung. */
454
- export function resetExecutor(): void { current = null; desired = "direct"; lastConfigureFailure = null; }