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,209 +0,0 @@
1
- /** OpenAI Responses API wire (aion port #75, brought over 2026-09-07) — the second OpenAI adapter beside stream.ts's
2
- * /chat/completions pair. ADR-003 seam: ONE StreamFn, errors cross as stopReason "error" / "aborted", never a throw.
3
- * Hand-rolled fetch + SSE exactly like stream.ts (no `openai` npm dependency). providers/wire-select.ts decides per
4
- * call which of the two wires a request takes; this module never imports stream.ts at runtime (it shares sse.ts,
5
- * stream-errors.ts, usage.ts, thinking.ts, catalog.ts and wire-responses.ts with it).
6
- *
7
- * Request: POST <baseUrl>/responses `{ model, instructions?, input, tools?, stream: true, store: false,
8
- * max_output_tokens?, reasoning? }`. `max_output_tokens` is max(cap, 16) — cap = model.maxTokens, the stream.ts rule
9
- * — and is OMITTED on the ChatGPT Codex backend (a URL containing chatgpt.com/backend-api; pi's codex path never
10
- * sends it), which also gets the `OpenAI-Beta: responses=experimental` + `originator: rovecode` headers.
11
- * `reasoning: { effort, summary: "auto" }` is the Responses spelling of rovecode's ONE thinking dial
12
- * (providers/thinking.ts thinkingPlan — the same dialect table stream.ts spreads as `reasoning_effort`; auto sends
13
- * nothing, a model the catalog or the dialect marks without a reasoning mode sends nothing).
14
- * NOT brought over: `include: ["reasoning.encrypted_content"]` and the reasoning item as a message part — rovecode
15
- * has no reasoning MessagePart to replay it from (wire-responses.ts header), so asking the provider for encrypted
16
- * content we would then discard is cost without product. Reasoning TEXT still streams live as reasoning_delta.
17
- * Pattern sources, MIT — earendil-works/pi packages/ai/src/api: openai-responses.ts:290-302 (store:false,
18
- * stream:true, max_output_tokens), :32-33 (the 16-token floor the API enforces), :323-332 (reasoning effort +
19
- * summary "auto"); openai-codex-responses.ts:549-556 (`instructions`), :45 + :633-639 (chatgpt.com/backend-api →
20
- * codex/responses), :1607-1609 + :1622 (chatgpt-account-id, originator, `OpenAI-Beta: responses=experimental`,
21
- * accept text/event-stream); openai-responses-shared.ts:440,:463 (output slots keyed by output_index), :597-668
22
- * (the deltas), :680-739 (output_item.done finalises a slot: message text authoritative, function_call arguments
23
- * parsed), :757-759 (a stream without a terminal event is an error), :762-792 (stop mapping), :592-594 (a tool call
24
- * overrides a completed stop), :742-755 (`<code>: <message>` for response.failed / error events).
25
- * Pattern source, Apache-2.0 at pattern level (no code copied) — openai/codex: codex-rs/codex-api/src/sse/
26
- * responses.rs:357-518 (the event set a client consumes; :843-845 fixtures pair `event:` and `data:` lines),
27
- * codex-rs/model-provider-info/src/lib.rs:40 (CHATGPT_CODEX_BASE_URL), codex-rs/core/src/client.rs:688 (an
28
- * `originator` header on every request — the value is `rovecode` here; codex's own OpenAI-Beta value is
29
- * websocket-only, so `responses=experimental` is pi's). NOTICE entry: THIRD_PARTY_NOTICES.md, `## openai/codex`.
30
- * NOT ported: `previous_response_id`, `prompt_cache_key` / `prompt_cache_retention`, `text.verbosity`,
31
- * `service_tier`, `parallel_tool_calls`, `tool_choice`, strict / grammar / custom tools, developer-role items,
32
- * the websocket and zstd transports, pi's retry loop (rovecode's withRetry + router classify the error turn as
33
- * for chat completions) and codex's error taxonomy. Selection is static — no runtime 404 → chat re-issue.
34
- *
35
- * Events (`data:` payloads through sse.ts; `event:` lines are ignored — `type` names the event):
36
- * `response.output_text.delta` / `response.refusal.delta` → text_delta (and the abort-salvage buffer);
37
- * `response.reasoning_summary_text.delta` / `response.reasoning_text.delta` → reasoning_delta — NEVER into the
38
- * text buffer, never a text part (`…summary_part.done` → one "\n\n" reasoning_delta);
39
- * `response.function_call_arguments.delta` accumulates per output_index (a tool_call_delta once the slot knows
40
- * its call id and name); `response.output_item.done` finalises the slot (a reasoning item finalises to nothing);
41
- * `response.completed` → tool_use when a tool_call part exists, else end_turn; `response.incomplete` → length for
42
- * max_output_tokens, else error naming the reason; `response.failed` / `error` → error "<code>: <message>"; no
43
- * terminal event → error. Usage: `response.usage` through core/usage.ts normalizeUsage (input_tokens −
44
- * input_tokens_details.cached_tokens, cacheRead; reasoning_tokens are not carried — TokenUsage has no field for
45
- * them and normalizeUsage does not expose them). Non-2xx → httpErrorTurn (status + Retry-After for withRetry / the
46
- * router); a thrown fetch or abort → failedTurn (an aborted turn keeps the streamed text, never a tool_call
47
- * fragment). No header, token or account id ever reaches an error text or an event (oauth/common.ts hygiene rule). */
48
-
49
- import type { AssistantTurn, Message, MessagePart, ModelRef, StopReason, StreamEvent, StreamFn, StreamOptions, TokenUsage } from "../core/types.ts";
50
- import { normalizeUsage } from "../core/usage.ts";
51
- import { supportsImages } from "./catalog.ts";
52
- import { sseLines } from "./sse.ts";
53
- import { failedTurn, fetchFirstByte, httpErrorTurn } from "./stream-errors.ts";
54
- import { thinkingPlan } from "./thinking.ts";
55
- import { toResponsesInput, toResponsesTools } from "./wire-responses.ts";
56
-
57
- /** the same opts as openaiCompatStreaming — oauth/seam.ts MakeStream is unchanged */
58
- export interface ResponsesAdapterOptions { baseUrl: string; apiKey: string; headers?: Record<string, string> }
59
-
60
- /** the API rejects max_output_tokens below 16 (pi openai-responses.ts:32-33) */
61
- export const RESPONSES_MIN_OUTPUT_TOKENS = 16;
62
- /** the ChatGPT Codex backend: no max_output_tokens, plus the two headers below (pi openai-codex-responses.ts) */
63
- export const CODEX_BACKEND = /chatgpt\.com\/backend-api/;
64
- export const CODEX_HEADERS: Readonly<Record<string, string>> = { "OpenAI-Beta": "responses=experimental", originator: "rovecode" };
65
- export const NO_TERMINAL_EVENT = "OpenAI Responses stream ended before a terminal response event";
66
-
67
- export interface ResponsesRequest { url: string; headers: Record<string, string>; body: Record<string, unknown> }
68
-
69
- /** the Responses spelling of the thinking dial: the word the OpenAI-compatible dialect would put in `reasoning_effort`
70
- * (or OpenRouter's `reasoning.effort`) becomes `reasoning: { effort, summary: "auto" }`; nothing when the dial sends
71
- * nothing (auto, a model without a reasoning mode, the catalog's `reasoning: false` stamp) */
72
- export function responsesReasoning(model: ModelRef): { reasoning?: { effort: string; summary: "auto" } } {
73
- const fields = thinkingPlan(model, "openai").fields;
74
- const nested = fields.reasoning as { effort?: unknown } | undefined;
75
- const word = typeof fields.reasoning_effort === "string" ? fields.reasoning_effort : typeof nested?.effort === "string" ? nested.effort : undefined;
76
- return word === undefined ? {} : { reasoning: { effort: word, summary: "auto" } };
77
- }
78
-
79
- /** URL, headers and body for one call (header rules); exported so the request shape is testable without fetch */
80
- export function responsesRequest(opts: ResponsesAdapterOptions, model: ModelRef, messages: Message[], options?: StreamOptions): ResponsesRequest {
81
- const url = opts.baseUrl.replace(/\/$/, "") + "/responses";
82
- const codex = CODEX_BACKEND.test(url);
83
- const { instructions, input } = toResponsesInput(messages, { vision: supportsImages(model) !== false, model }); // port #34 vision gate (stream.ts wireOptions rule)
84
- const cap = model.maxTokens;
85
- const body: Record<string, unknown> = {
86
- model: model.model,
87
- ...(instructions !== undefined ? { instructions } : {}),
88
- input,
89
- ...(options?.tools?.length ? { tools: toResponsesTools(options.tools) } : {}),
90
- stream: true,
91
- store: false,
92
- ...(cap && !codex ? { max_output_tokens: Math.max(cap, RESPONSES_MIN_OUTPUT_TOKENS) } : {}),
93
- ...responsesReasoning(model),
94
- };
95
- const headers: Record<string, string> = { "content-type": "application/json", accept: "text/event-stream", ...(codex ? CODEX_HEADERS : {}), ...opts.headers, authorization: `Bearer ${opts.apiKey}` };
96
- return { url, headers, body };
97
- }
98
-
99
- // ---------- SSE state ----------
100
-
101
- type Slot = { kind: "message"; text: string } | { kind: "function_call"; callId: string; name: string; args: string } | { kind: "reasoning" };
102
- interface Item { type?: string; id?: string; call_id?: string; name?: string; arguments?: string; content?: { type?: string; text?: string; refusal?: string }[]; summary?: { text?: string }[] }
103
- interface Event {
104
- type?: string; output_index?: number; delta?: string; arguments?: string; item?: Item; code?: string; message?: string;
105
- response?: { usage?: unknown; incomplete_details?: { reason?: string } | null; error?: { code?: string; message?: string } | null };
106
- }
107
-
108
- function newSlot(item: Item | undefined): Slot | undefined {
109
- if (item?.type === "message") return { kind: "message", text: "" };
110
- if (item?.type === "function_call") return { kind: "function_call", callId: item.call_id ?? "", name: item.name ?? "", args: item.arguments ?? "" };
111
- if (item?.type === "reasoning") return { kind: "reasoning" };
112
- return undefined;
113
- }
114
-
115
- /** the part an `output_item.done` item finalises to (pi shared.ts:680-739); undefined for an empty message and for a
116
- * reasoning item (no reasoning MessagePart here — header) */
117
- function finishedPart(item: Item, slot: Slot | undefined, index: number): MessagePart | undefined {
118
- if (item.type === "message") {
119
- const text = item.content ? item.content.map((c) => (c.type === "refusal" ? c.refusal ?? "" : c.text ?? "")).join("") : slot?.kind === "message" ? slot.text : "";
120
- return text ? { kind: "text", text } : undefined;
121
- }
122
- if (item.type === "function_call") {
123
- const fc = slot?.kind === "function_call" ? slot : undefined;
124
- const raw = item.arguments ?? fc?.args ?? "";
125
- let args: unknown = {};
126
- try { args = JSON.parse(raw || "{}"); } catch { args = { _raw: raw }; }
127
- return { kind: "tool_call", id: item.call_id || fc?.callId || item.id || `fc${index}`, tool: item.name || fc?.name || "unknown", args };
128
- }
129
- return undefined;
130
- }
131
-
132
- /** the same normalization the stream.ts adapters apply (cached share subtracted, zero fields dropped) */
133
- function turnUsage(raw: unknown): TokenUsage {
134
- const u = normalizeUsage(raw);
135
- return { input: u.input, output: u.output, cacheRead: u.cacheRead || undefined, cacheWrite: u.cacheWrite || undefined };
136
- }
137
-
138
- // ---------- the adapter ----------
139
-
140
- export function openaiResponsesStream(opts: ResponsesAdapterOptions): StreamFn {
141
- return async function* (model: ModelRef, messages: Message[], options?: StreamOptions): AsyncGenerator<StreamEvent> {
142
- let turn: AssistantTurn;
143
- let buffer = ""; // text deltas only — the abort salvage (failedTurn); reasoning deltas never land here
144
- try {
145
- const req = responsesRequest(opts, model, messages, options);
146
- const res = await fetchFirstByte(req.url, { method: "POST", headers: req.headers, body: JSON.stringify(req.body), signal: options?.signal });
147
- if (!res.ok || !res.body) {
148
- yield { type: "turn", turn: await httpErrorTurn(res) }; // status + Retry-After recorded for withRetry (stream-errors.ts)
149
- return;
150
- }
151
- const slots = new Map<number, Slot>();
152
- const parts = new Map<number, MessagePart>();
153
- let usage: TokenUsage = { input: 0, output: 0 };
154
- let terminal: { stop: StopReason; error?: string } | undefined;
155
- for await (const line of sseLines(res.body)) {
156
- const ev = JSON.parse(line) as Event;
157
- const idx = ev.output_index ?? 0;
158
- switch (ev.type) {
159
- case "response.output_item.added": { const s = newSlot(ev.item); if (s) slots.set(idx, s); break; }
160
- case "response.output_text.delta":
161
- case "response.refusal.delta": {
162
- if (!ev.delta) break;
163
- const s: Slot = slots.get(idx) ?? { kind: "message", text: "" };
164
- if (s.kind === "message") { s.text += ev.delta; slots.set(idx, s); }
165
- buffer += ev.delta;
166
- yield { type: "text_delta", text: ev.delta };
167
- break;
168
- }
169
- case "response.reasoning_summary_text.delta":
170
- case "response.reasoning_text.delta": if (ev.delta) yield { type: "reasoning_delta", text: ev.delta }; break;
171
- case "response.reasoning_summary_part.done": yield { type: "reasoning_delta", text: "\n\n" }; break;
172
- case "response.function_call_arguments.delta": {
173
- if (!ev.delta) break;
174
- const s: Slot = slots.get(idx) ?? { kind: "function_call", callId: "", name: "", args: "" };
175
- if (s.kind !== "function_call") break;
176
- s.args += ev.delta;
177
- slots.set(idx, s);
178
- if (s.callId && s.name) yield { type: "tool_call_delta", id: s.callId, tool: s.name, argsDelta: ev.delta };
179
- break;
180
- }
181
- case "response.function_call_arguments.done": { const s = slots.get(idx); if (s?.kind === "function_call" && typeof ev.arguments === "string") s.args = ev.arguments; break; }
182
- case "response.output_item.done": {
183
- if (ev.item) { const p = finishedPart(ev.item, slots.get(idx), idx); if (p) parts.set(idx, p); }
184
- slots.delete(idx);
185
- break;
186
- }
187
- case "response.completed":
188
- case "response.incomplete": {
189
- if (ev.response?.usage) usage = turnUsage(ev.response.usage);
190
- const reason = ev.response?.incomplete_details?.reason;
191
- terminal = ev.type === "response.completed" ? { stop: "end_turn" } : reason === "max_output_tokens" ? { stop: "length" } : { stop: "error", error: `Response incomplete: ${reason ?? "no reason given"}` };
192
- break;
193
- }
194
- case "response.failed": { const e = ev.response?.error; terminal = { stop: "error", error: `${e?.code || "unknown"}: ${e?.message || "no message"}` }; break; }
195
- case "error": terminal = { stop: "error", error: `${ev.code || "unknown"}: ${ev.message || "no message"}` }; break;
196
- default: break; // response.created / in_progress / content_part.* / *_part.added / *.done text echoes: nothing to do
197
- }
198
- }
199
- if (!terminal) terminal = { stop: "error", error: NO_TERMINAL_EVENT }; // pi shared.ts:757-759 — never end_turn
200
- const ordered = [...parts].sort((a, b) => a[0] - b[0]).map(([, p]) => p);
201
- turn = terminal.stop === "error"
202
- ? { parts: [], stopReason: "error", usage, error: terminal.error }
203
- : { parts: ordered, stopReason: terminal.stop === "end_turn" && ordered.some((p) => p.kind === "tool_call") ? "tool_use" : terminal.stop, usage };
204
- } catch (e) {
205
- turn = failedTurn(e, options?.signal, buffer); // mid-stream abort: the text already streamed survives, tool fragments do not
206
- }
207
- yield { type: "turn", turn };
208
- };
209
- }
@@ -1,234 +0,0 @@
1
- /** Same-model retry with exponential backoff (port #23). A StreamFn wrapper — NO second agent
2
- * loop (ADR-003): bounded re-invocations of the SAME (model, messages, options) inside one
3
- * stream invocation, the way the router makes one bounded pass over chain candidates. Wired
4
- * INSIDE the router (`router.wrap(withRetry(stream))`, cli/runtime.ts) so retries exhaust on
5
- * candidate N before the chain advances to N+1, and the router's notes stay one-per-ADVANCE.
6
- *
7
- * Source: gemini-cli (Apache-2.0 @0bd1d43 — research/source_snapshots/google-gemini-gemini-cli,
8
- * packages/core/src/utils):
9
- * - retryWithBackoff loop shape: attempt counter vs maxAttempts, delay doubling up to
10
- * maxDelayMs (retry.ts:296-310, :494-501, :517-524); defaults 10 attempts / 5s initial /
11
- * 30s max (retry.ts:20, :42-47).
12
- * - Retryable set: 429 and 5xx; "Explicitly do not retry 400" (retry.ts:193-199); transport
13
- * failures without a status are retryable (retry.ts:49-62, :174-189). Shared with the router
14
- * through classifyStreamError (router.ts) — one classifier, two consumers (gemini-cli's 499
15
- * is excluded there, documented deviation).
16
- * - Aborts pass through untouched (retry.ts:337-340); the backoff sleep itself is abortable
17
- * (delay.ts:22-48: timeout + abort listener, both cleared on settle).
18
- * - A server-suggested delay is a FLOOR on the next sleep (retry.ts:472-476
19
- * `Math.max(currentDelay, retryDelayMs)`); a suggestion beyond the cap is terminal — no wait,
20
- * immediate fallback (googleQuotaErrors.ts:120 MAX_RETRYABLE_DELAY_SECONDS, :286-289). Here
21
- * the suggestion is the HTTP Retry-After header (RFC 9110 §10.2.3: delay-seconds or
22
- * HTTP-date), OpenAI's retry-after-ms, or Anthropic's ratelimit-reset timestamps, all recorded
23
- * by stream-errors.ts; the cap is the per-invocation total budget and the run's own deadline.
24
- * - Deviations: FULL jitter — delay = U[0,1) × min(max, base·2ⁿ) (AWS "Exponential Backoff And
25
- * Jitter") instead of gemini-cli's ±30% around the current delay (retry.ts:494-495) or +20%
26
- * over the server floor (:478): fewer synchronized retries, and the schedule is pinnable with
27
- * an injected random. A total wall-clock cap per invocation (gemini-cli has none): with a
28
- * fallback chain every candidate pays the retry budget, so it is kept short. No content-based
29
- * retry (:314-328), no quota classification / fallback dialog (:342-420) — the router owns
30
- * model fallback. A thrown inner stream is folded into an error turn (never-throw) but NOT
31
- * retried: a seam-contract violation is a harness bug, not a provider outcome.
32
- * - Per-invocation state only: attempt counter and deadline live inside one generator run;
33
- * nothing is remembered across calls (unlike the router's sticky switch).
34
- * - IDEMPOTENCY (2026-09-04): a retry is taken only while NOTHING has been streamed to the consumer.
35
- * Once a text or reasoning delta has gone out, a failure ends the turn — with the partial text kept as
36
- * the turn's parts and an error that says so — because a re-drive would print a second answer under
37
- * the first (the router applies the same rule to its chain advance). Deltas from a failed attempt that
38
- * streamed nothing cannot exist, so the "pass through live" rule and this one never meet.
39
- *
40
- * Env (retryOptionsFromEnv, read once by cli/runtime.ts):
41
- * - ROVECODE_RETRY_MAX retries after the first attempt; 0 disables. Default 3 (→ 4 attempts;
42
- * upstream 10 attempts — a fallback chain multiplies attempts per candidate).
43
- * - ROVECODE_RETRY_BASE_MS cap of the first backoff in ms. Default 1000 (upstream 5000; full jitter
44
- * halves the expected wait — ~0.5 s, 1 s, 2 s, 4 s expected).
45
- * Fixed: max backoff 20 s, total budget 60 s per invocation, and never past StreamOptions.deadlineAt
46
- * (the run's --max-seconds clock). */
47
-
48
- import type { AssistantTurn, ModelRef, StreamEvent, StreamFn } from "../core/types.ts";
49
- import { classifyStreamError } from "./router.ts";
50
- import { httpErrorMeta, providerMessage } from "./stream-errors.ts";
51
-
52
- export const DEFAULT_MAX_RETRIES = 3;
53
- export const DEFAULT_BASE_MS = 1_000;
54
- export const DEFAULT_MAX_DELAY_MS = 20_000;
55
- export const DEFAULT_TOTAL_MS = 60_000;
56
-
57
- export interface RetryNote {
58
- model: ModelRef;
59
- /** Attempts made so far (the one that just failed); the retry about to happen is attempt+1. */
60
- attempt: number;
61
- /** attempts the policy allows in total (maxRetries + 1) — for "(2/4)" */
62
- maxAttempts: number;
63
- delayMs: number;
64
- /** The failed turn's error text, e.g. "HTTP 429: ...". */
65
- reason: string;
66
- /** the HTTP status when the failure was a response; undefined for a transport failure */
67
- status?: number;
68
- /** Parsed server wait hint (Retry-After / retry-after-ms / anthropic-ratelimit-*-reset) when the error turn carried one. */
69
- retryAfterMs?: number;
70
- }
71
-
72
- /** why the wrapper stopped retrying a retryable failure */
73
- export type GiveUpWhy = "attempts" | "budget" | "deadline";
74
- export interface GiveUpNote extends Omit<RetryNote, "delayMs"> { why: GiveUpWhy; delayMs?: number }
75
-
76
- export interface RetryOptions {
77
- /** Retries after the first attempt (ROVECODE_RETRY_MAX). 0 = never retry. */
78
- maxRetries?: number;
79
- /** Cap of the first backoff, doubling per attempt (ROVECODE_RETRY_BASE_MS). */
80
- baseMs?: number;
81
- /** Ceiling for the doubling backoff term. */
82
- maxDelayMs?: number;
83
- /** Wall-clock budget per invocation incl. sleeps; a wait that would end past it is not taken. */
84
- totalMs?: number;
85
- /** Retry visibility — a note-style callback, not a StreamEvent (grammar is shared/untouchable). */
86
- onRetry?: (note: RetryNote) => void;
87
- /** the last word: a retryable failure that will not be retried (attempts, budget or the run's deadline) */
88
- onGiveUp?: (note: GiveUpNote) => void;
89
- /** Test seams: abortable sleep, jitter source, clock (epoch ms — also anchors HTTP-date). */
90
- sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
91
- random?: () => number;
92
- now?: () => number;
93
- }
94
-
95
- /** Abortable sleep (delay.ts:22-48 shape) on a REF'D setTimeout — Bun unrefs AbortSignal.timeout
96
- * timers, so a sleep built on one could not hold the process/test runner — plus an abort
97
- * listener; whichever settles first clears the other. Resolves (never rejects) on abort: the
98
- * caller re-checks signal.aborted. */
99
- export function sleepMs(ms: number, signal?: AbortSignal): Promise<void> {
100
- return new Promise<void>((resolve) => {
101
- if (signal?.aborted) { resolve(); return; }
102
- const settle = (): void => { clearTimeout(timer); signal?.removeEventListener("abort", settle); resolve(); };
103
- const timer = setTimeout(settle, ms);
104
- signal?.addEventListener("abort", settle, { once: true });
105
- });
106
- }
107
-
108
- /** RFC 9110 §10.2.3 Retry-After: delay-seconds (non-negative; a fractional value is tolerated)
109
- * or an HTTP-date, converted to ms from `now` (a past date → 0). Unparseable → undefined. */
110
- export function parseRetryAfter(value: string | undefined, now: number): number | undefined {
111
- const v = value?.trim();
112
- if (!v) return undefined;
113
- if (/^\d+(?:\.\d+)?$/.test(v)) return Math.round(Number(v) * 1000);
114
- const at = Date.parse(v);
115
- return Number.isNaN(at) ? undefined : Math.max(0, at - now);
116
- }
117
-
118
- /** the server's wait hint for a failed turn, whichever header it used: the largest of Retry-After,
119
- * retry-after-ms and the Anthropic ratelimit reset timestamps (RFC 3339) — the longest wait is the
120
- * one that will actually clear the limit */
121
- export function serverWaitMs(turn: AssistantTurn, now: number): number | undefined {
122
- const meta = httpErrorMeta(turn);
123
- if (!meta) return undefined;
124
- const hints: number[] = [];
125
- const ra = parseRetryAfter(meta.retryAfter, now);
126
- if (ra !== undefined) hints.push(ra);
127
- if (meta.retryAfterMs !== undefined && /^\d+(?:\.\d+)?$/.test(meta.retryAfterMs.trim())) hints.push(Math.round(Number(meta.retryAfterMs)));
128
- if (meta.resetAt !== undefined) { const at = Date.parse(meta.resetAt); if (!Number.isNaN(at)) hints.push(Math.max(0, at - now)); }
129
- return hints.length ? Math.max(...hints) : undefined;
130
- }
131
-
132
- /** Env knobs (header). Blank/invalid/out-of-range values fall back to the defaults;
133
- * ROVECODE_RETRY_MAX=0 is honored (retry off). */
134
- export function retryOptionsFromEnv(env: Record<string, string | undefined> = process.env): RetryOptions {
135
- return { maxRetries: envInt(env.ROVECODE_RETRY_MAX, DEFAULT_MAX_RETRIES, 0), baseMs: envInt(env.ROVECODE_RETRY_BASE_MS, DEFAULT_BASE_MS, 1) };
136
- }
137
-
138
- function envInt(raw: string | undefined, dflt: number, min: number): number {
139
- if (raw === undefined || raw.trim() === "") return dflt;
140
- const n = Number(raw);
141
- return Number.isFinite(n) && n >= min ? Math.floor(n) : dflt;
142
- }
143
-
144
- const errorTurn = (error: string): AssistantTurn => ({ parts: [], stopReason: "error", usage: { input: 0, output: 0 }, error });
145
-
146
- // ---------- the human's words ----------
147
-
148
- /** one noun phrase per failure class — what the notice and the give-up line lead with */
149
- export function failureWord(status: number | undefined, error: string): string {
150
- if (status === 429) return "rate limited";
151
- if (status === 529 || status === 503) return "overloaded";
152
- if (status !== undefined && status >= 500) return `server error (HTTP ${status})`;
153
- if (status !== undefined) return `HTTP ${status}`;
154
- if (/^no response from /.test(error)) return "no response";
155
- return "connection failed";
156
- }
157
-
158
- const fmtSeconds = (ms: number): string => { const s = ms / 1000; return `${s >= 10 ? Math.round(s) : Math.round(s * 10) / 10} s`; };
159
-
160
- /** "anthropic: overloaded — retrying in 4 s (2/4)" */
161
- export function describeRetry(n: RetryNote): string {
162
- return `${n.model.provider}: ${failureWord(n.status, n.reason)} — retrying in ${fmtSeconds(n.delayMs)} (${n.attempt + 1}/${n.maxAttempts})`;
163
- }
164
-
165
- /** "anthropic: overloaded (HTTP 529) — gave up after 4 attempts: Overloaded" */
166
- export function describeGiveUp(n: GiveUpNote): string {
167
- const why = n.why === "attempts" ? `gave up after ${n.attempt} attempt${n.attempt === 1 ? "" : "s"}`
168
- : n.why === "deadline" ? `not retried: the run's time limit is closer than the ${fmtSeconds(n.delayMs ?? 0)} wait`
169
- : `not retried: the ${fmtSeconds(n.delayMs ?? 0)} wait would pass the retry budget`;
170
- const status = n.status !== undefined && !/HTTP/.test(failureWord(n.status, n.reason)) ? ` (HTTP ${n.status})` : "";
171
- const msg = providerMessage(n.reason);
172
- return `${n.model.provider}: ${failureWord(n.status, n.reason)}${status} — ${why}${msg ? `: ${msg}` : ""}`;
173
- }
174
-
175
- /** Wrap a StreamFn: a retryable failed turn (429 / 5xx / transport, never 400, never abort, never after
176
- * a delta went out) is re-driven against the SAME model with the same args after an abortable
177
- * full-jitter backoff, until it succeeds, a non-retryable outcome lands, or a cap (attempts / total
178
- * budget / the run's deadline) stops it — then the LAST turn is yielded untouched so the router sees
179
- * the genuine provider error. Never throws (ADR-003). */
180
- export function withRetry(inner: StreamFn, opts: RetryOptions = {}): StreamFn {
181
- const maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES;
182
- const baseMs = opts.baseMs ?? DEFAULT_BASE_MS;
183
- const maxDelayMs = opts.maxDelayMs ?? DEFAULT_MAX_DELAY_MS;
184
- const totalMs = opts.totalMs ?? DEFAULT_TOTAL_MS;
185
- const sleep = opts.sleep ?? sleepMs;
186
- const random = opts.random ?? Math.random;
187
- const now = opts.now ?? Date.now;
188
- return async function* (model, messages, options): AsyncGenerator<StreamEvent> {
189
- const startedAt = now();
190
- for (let attempt = 1; ; attempt++) {
191
- let turn: AssistantTurn | null = null;
192
- let streamed = ""; // every text delta this attempt let through — the answer the consumer has already seen
193
- try {
194
- for await (const ev of inner(model, messages, options)) {
195
- if (ev.type === "turn") turn = ev.turn;
196
- else { if (ev.type === "text_delta") streamed += ev.text; else if (ev.type === "reasoning_delta") streamed ||= " "; yield ev; } // deltas pass through live (header)
197
- }
198
- } catch (e) {
199
- yield { type: "turn", turn: errorTurn(e instanceof Error ? e.message : String(e)) }; // folded, not retried (header)
200
- return;
201
- }
202
- if (turn === null) { yield { type: "turn", turn: errorTurn("stream ended without a terminal turn") }; return; }
203
- const meta = httpErrorMeta(turn);
204
- const status = meta?.status ?? classifyStreamError(turn.error).status;
205
- // retry.ts:337-340: aborts never retry; 400/4xx-non-429 and non-error turns stand as they are
206
- if (turn.stopReason !== "error" || options?.signal?.aborted === true || !classifyStreamError(turn.error).retryable) {
207
- yield { type: "turn", turn };
208
- return;
209
- }
210
- if (streamed.length > 0) {
211
- // idempotency (header): part of the answer is on the screen — end the turn, keep that text as the
212
- // turn's parts (the loop stores it and the summary shows it above the error), say why no retry
213
- const kept = streamed.trim().length > 0 && turn.parts.length === 0 ? [{ kind: "text" as const, text: streamed }] : turn.parts;
214
- yield { type: "turn", turn: { ...turn, parts: kept, error: `${turn.error ?? "provider stream failed"} — the connection dropped after part of the answer had arrived; not retried, a retry would repeat it` } };
215
- return;
216
- }
217
- const note = { model, attempt, maxAttempts: maxRetries + 1, reason: turn.error ?? "error", ...(status !== undefined ? { status } : {}) };
218
- // retries off (ROVECODE_RETRY_MAX=0): nothing was ever going to be retried, so there is no giving up to announce
219
- if (attempt > maxRetries) { if (maxRetries > 0) opts.onGiveUp?.({ ...note, why: "attempts" }); yield { type: "turn", turn }; return; }
220
- const retryAfterMs = serverWaitMs(turn, now());
221
- const cap = Math.min(maxDelayMs, baseMs * 2 ** (attempt - 1));
222
- const delayMs = Math.max(Math.round(random() * cap), retryAfterMs ?? 0); // full jitter, server floor (retry.ts:476)
223
- const withHint = retryAfterMs !== undefined ? { retryAfterMs } : {};
224
- // budget: a wait that would end past the deadline is not taken — the failure surfaces now
225
- // and the router may advance at once (googleQuotaErrors.ts:286-289 shape)
226
- if (now() - startedAt + delayMs > totalMs) { opts.onGiveUp?.({ ...note, ...withHint, delayMs, why: "budget" }); yield { type: "turn", turn }; return; }
227
- // the run's own clock (--max-seconds): a wait that ends past it would only be cut off at the turn boundary
228
- if (options?.deadlineAt !== undefined && now() + delayMs > options.deadlineAt) { opts.onGiveUp?.({ ...note, ...withHint, delayMs, why: "deadline" }); yield { type: "turn", turn }; return; }
229
- opts.onRetry?.({ ...note, delayMs, ...withHint });
230
- await sleep(delayMs, options?.signal);
231
- if (options?.signal?.aborted) { yield { type: "turn", turn }; return; } // abort landed during backoff: last turn stands
232
- }
233
- };
234
- }