rovecode 0.3.3 → 0.4.0-beta.2

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 (491) hide show
  1. package/CHANGELOG.md +512 -0
  2. package/README.md +33 -48
  3. package/bin/rovecode.js +24 -0
  4. package/dist/cli/app-dybnr56b.js +2 -0
  5. package/dist/cli/ask-user-p8hq4xgj.js +2 -0
  6. package/dist/cli/auth-login-ewpgw5sm.js +2 -0
  7. package/dist/cli/auth-m8p9grty.js +2 -0
  8. package/dist/cli/bench-xv3ypwev.js +9 -0
  9. package/dist/cli/catalog-737wb2s0.js +2 -0
  10. package/dist/cli/cli-arhg40m0.js +2 -0
  11. package/dist/cli/client-cf2pxx8q.js +2 -0
  12. package/dist/cli/commands-3p7e4xxs.js +2 -0
  13. package/dist/cli/connect-3q93d7cb.js +2 -0
  14. package/dist/cli/context-cmd-eqxmxhzq.js +2 -0
  15. package/dist/cli/context-report-hbw9zfes.js +2 -0
  16. package/dist/cli/count-remote-mby98cd0.js +2 -0
  17. package/dist/cli/design-122y0axd.js +2 -0
  18. package/dist/cli/dispatch-b4egzvvh.js +2 -0
  19. package/dist/cli/doctor-x4jkv72e.js +3 -0
  20. package/dist/cli/executor-ftvg6tsy.js +2 -0
  21. package/dist/cli/export-pdgdhkch.js +2 -0
  22. package/dist/cli/files-cez9a96p.js +2 -0
  23. package/dist/cli/gauntlet-r3xxaszc.js +2 -0
  24. package/dist/cli/gauntlet-runner-r515m7kk.js +10 -0
  25. package/dist/cli/gauntlet-wave3-bnkjk2v2.js +5 -0
  26. package/dist/cli/gauntlet-wave4-acs9s60q.js +14 -0
  27. package/dist/cli/hashline-ewg5hbe3.js +2 -0
  28. package/dist/cli/http-n0kehsk8.js +5 -0
  29. package/dist/cli/index-z5qt1s76.js +2 -0
  30. package/dist/cli/install-80mp63kx.js +2 -0
  31. package/dist/cli/loop-12twjcat.js +2 -0
  32. package/dist/cli/main-01pv9206.js +4 -0
  33. package/dist/cli/main-0jys2ccn.js +3 -0
  34. package/dist/cli/main-1ztz6fkj.js +10 -0
  35. package/dist/cli/main-23q7cmww.js +9 -0
  36. package/dist/cli/main-2rzbexn2.js +3 -0
  37. package/dist/cli/main-2wyax8k9.js +9 -0
  38. package/dist/cli/main-2yeveeve.js +6 -0
  39. package/dist/cli/main-2z3dek0b.js +3 -0
  40. package/dist/cli/main-2zgsknth.js +3 -0
  41. package/dist/cli/main-45ejth3a.js +4 -0
  42. package/dist/cli/main-45rn3trk.js +22 -0
  43. package/dist/cli/main-4p4e2w7x.js +4 -0
  44. package/dist/cli/main-4y0tnfpa.js +16 -0
  45. package/dist/cli/main-5py0rkmc.js +4 -0
  46. package/dist/cli/main-6dtqmbt6.js +7 -0
  47. package/dist/cli/main-6h9x282m.js +4 -0
  48. package/dist/cli/main-6vjeds42.js +3 -0
  49. package/dist/cli/main-78gq4bt9.js +6 -0
  50. package/dist/cli/main-7jd5vh3x.js +4 -0
  51. package/dist/cli/main-7kt6r53y.js +4 -0
  52. package/dist/cli/main-8c1tbazx.js +58 -0
  53. package/dist/cli/main-9a9rnh47.js +19 -0
  54. package/dist/cli/main-9ht36z12.js +3 -0
  55. package/dist/cli/main-a2yfvcy9.js +7 -0
  56. package/dist/cli/main-a3f51n0x.js +5 -0
  57. package/dist/cli/main-b8zq261k.js +3 -0
  58. package/dist/cli/main-bxtvnf6d.js +13 -0
  59. package/dist/cli/main-edxc3yzt.js +4 -0
  60. package/dist/cli/main-evgz4mp5.js +21 -0
  61. package/dist/cli/main-f33fc5je.js +9 -0
  62. package/dist/cli/main-fvnpq46y.js +12 -0
  63. package/dist/cli/main-gbbty4d4.js +3 -0
  64. package/dist/cli/main-gth53dnt.js +25 -0
  65. package/dist/cli/main-hqbz10aw.js +9 -0
  66. package/dist/cli/main-hrrvcfan.js +38 -0
  67. package/dist/cli/main-hzwtsb2m.js +5 -0
  68. package/dist/cli/main-j7ttv0sd.js +34 -0
  69. package/dist/cli/main-jak598k9.js +5 -0
  70. package/dist/cli/main-kba6zeyd.js +6 -0
  71. package/dist/cli/main-kwwsz6rq.js +3 -0
  72. package/dist/cli/main-m8vm17zq.js +3 -0
  73. package/dist/cli/main-mg4f96e1.js +3 -0
  74. package/dist/cli/main-mg9b20ac.js +18 -0
  75. package/dist/cli/main-mgb9ccnx.js +3 -0
  76. package/dist/cli/main-mjt2p7aj.js +3 -0
  77. package/dist/cli/main-n6qrdbmy.js +3 -0
  78. package/dist/cli/main-na7wse0x.js +5 -0
  79. package/dist/cli/main-nqveez48.js +4 -0
  80. package/dist/cli/main-ntqef02r.js +10 -0
  81. package/dist/cli/main-nvc3yjay.js +136 -0
  82. package/dist/cli/main-p0cfn6nr.js +16 -0
  83. package/dist/cli/main-qj2djy17.js +19 -0
  84. package/dist/cli/main-qsevpgsv.js +3 -0
  85. package/dist/cli/main-qvarybsp.js +3 -0
  86. package/dist/cli/main-rebtt91r.js +5 -0
  87. package/dist/cli/main-rpg7h8mb.js +3 -0
  88. package/dist/cli/main-rsy72qmw.js +15 -0
  89. package/dist/cli/main-rvetps99.js +18 -0
  90. package/dist/cli/main-s4bb0jav.js +3 -0
  91. package/dist/cli/main-s9v8k74e.js +3 -0
  92. package/dist/cli/main-tjvwmscs.js +3 -0
  93. package/dist/cli/main-tkgarpjj.js +4 -0
  94. package/dist/cli/main-v8y60bb2.js +3 -0
  95. package/dist/cli/main-vhrrq337.js +3 -0
  96. package/dist/cli/main-vp2dfb7s.js +4 -0
  97. package/dist/cli/main-vqbr22sz.js +8 -0
  98. package/dist/cli/main-vxnwe5xx.js +18 -0
  99. package/dist/cli/main-wgph00xf.js +5 -0
  100. package/dist/cli/main-wk2csfnj.js +5 -0
  101. package/dist/cli/main-wm997zjx.js +3 -0
  102. package/dist/cli/main-wpkyraxh.js +3 -0
  103. package/dist/cli/main-wqt32p5x.js +4 -0
  104. package/dist/cli/main-x9ct6y1a.js +3 -0
  105. package/dist/cli/main-xfekqh9m.js +7 -0
  106. package/dist/cli/main-xt9zc3n6.js +7 -0
  107. package/dist/cli/main-xx2z3zh5.js +4 -0
  108. package/dist/cli/main-y5c82rxr.js +3 -0
  109. package/dist/cli/main-yrjt2sqt.js +14 -0
  110. package/dist/cli/main-ys6zj3yr.js +3 -0
  111. package/dist/cli/main-ywbxshqc.js +8 -0
  112. package/dist/cli/main-z13755t8.js +25 -0
  113. package/dist/cli/main-zc7pyrbj.js +4 -0
  114. package/dist/cli/main.js +279 -0
  115. package/dist/cli/market-cmd-bm5xvn9f.js +5 -0
  116. package/dist/cli/mcp-login-bthtfpt7.js +2 -0
  117. package/dist/cli/mcp-market-cmd-mbeshfyd.js +2 -0
  118. package/dist/cli/notify-54v5z9dz.js +2 -0
  119. package/dist/cli/oauth-g5gme95c.js +2 -0
  120. package/dist/cli/output-satndjap.js +16 -0
  121. package/dist/cli/profiles-sfhpbq3m.js +2 -0
  122. package/dist/cli/provider-config-hv3xtdt4.js +2 -0
  123. package/dist/cli/provider-kwzq6g84.js +2 -0
  124. package/dist/cli/registry-fh0hdnyn.js +2 -0
  125. package/dist/cli/registry-y1y8e94r.js +2 -0
  126. package/dist/cli/repl-t4z03mqq.js +11 -0
  127. package/dist/cli/resume-fqt4chg8.js +2 -0
  128. package/dist/cli/run-flags-rysbag9t.js +2 -0
  129. package/dist/cli/runtime-j19fjbsa.js +2 -0
  130. package/dist/cli/sandbox-config-g4qxd7y5.js +2 -0
  131. package/dist/cli/server-r0b6bksk.js +5 -0
  132. package/dist/cli/session-arg-txmn5g4x.js +2 -0
  133. package/dist/cli/session-ed250d9j.js +2 -0
  134. package/dist/cli/sessions-cmd-adw7svfn.js +7 -0
  135. package/dist/cli/settings-y9rzcqx8.js +2 -0
  136. package/dist/cli/setup-jmbr11j0.js +2 -0
  137. package/dist/cli/sextant-smoke-tcth0vea.js +5 -0
  138. package/dist/cli/skills-cmd-zbdy99v6.js +2 -0
  139. package/dist/cli/smoke-1bg937kx.js +8 -0
  140. package/dist/cli/start-chat-p01cdks3.js +12 -0
  141. package/dist/cli/stream-4wmyaypz.js +2 -0
  142. package/dist/cli/task-eg4s093s.js +2 -0
  143. package/dist/cli/tasks-12v9rr9k.js +2 -0
  144. package/dist/cli/thinking-a5ngvqyh.js +2 -0
  145. package/dist/cli/todo-1wxpcecx.js +2 -0
  146. package/dist/cli/tools-2ftsya7w.js +2 -0
  147. package/dist/cli/tools-x1tj4fxm.js +2 -0
  148. package/dist/cli/trust-cmd-hccxehzb.js +2 -0
  149. package/dist/cli/update-check-ygt3vd7m.js +2 -0
  150. package/dist/cli/update-cmd-v23qhr8c.js +2 -0
  151. package/dist/cli/voice-g1gtck92.js +2 -0
  152. package/dist/cli/webfetch-0nnrjgb5.js +2 -0
  153. package/dist/cli/websearch-f0vr2p7d.js +2 -0
  154. package/dist/cli/workspace-9rq1w4ta.js +2 -0
  155. package/dist/lib/index.js +62 -0
  156. package/dist/lib/models-index.json +1 -0
  157. package/dist/lib/plugins.js +6 -0
  158. package/dist/lib/providers.js +17 -0
  159. package/dist/lib/public-api.js +20 -0
  160. package/dist/rovecode.exe +4 -0
  161. package/package.json +83 -72
  162. package/bin/rovecode.ts +0 -21
  163. package/src/acp/server.ts +0 -374
  164. package/src/cli/auth-login.ts +0 -122
  165. package/src/cli/connect.ts +0 -244
  166. package/src/cli/context-cmd.ts +0 -199
  167. package/src/cli/dispatch.ts +0 -82
  168. package/src/cli/doctor.ts +0 -362
  169. package/src/cli/export.ts +0 -276
  170. package/src/cli/help.ts +0 -293
  171. package/src/cli/is-tui-invocation.ts +0 -8
  172. package/src/cli/main.ts +0 -583
  173. package/src/cli/market-cmd.ts +0 -658
  174. package/src/cli/mcp-login.ts +0 -141
  175. package/src/cli/mcp-market-cmd.ts +0 -302
  176. package/src/cli/output.ts +0 -382
  177. package/src/cli/repl.ts +0 -250
  178. package/src/cli/repomap-root.ts +0 -14
  179. package/src/cli/resume.ts +0 -57
  180. package/src/cli/run-flags.ts +0 -43
  181. package/src/cli/run-limits.ts +0 -78
  182. package/src/cli/runtime.ts +0 -931
  183. package/src/cli/session-arg.ts +0 -30
  184. package/src/cli/sessions-cmd.ts +0 -145
  185. package/src/cli/setup.ts +0 -153
  186. package/src/cli/skills-cmd.ts +0 -194
  187. package/src/cli/start-chat.ts +0 -65
  188. package/src/cli/trust-cmd.ts +0 -52
  189. package/src/coding/bash.ts +0 -148
  190. package/src/coding/checkpoints.ts +0 -327
  191. package/src/coding/diff.ts +0 -138
  192. package/src/coding/files.ts +0 -341
  193. package/src/coding/hashline.ts +0 -274
  194. package/src/coding/lsp-gate.ts +0 -254
  195. package/src/coding/lsp-servers.ts +0 -147
  196. package/src/coding/lsp.ts +0 -283
  197. package/src/coding/repomap-cache.ts +0 -99
  198. package/src/coding/repomap-files.ts +0 -192
  199. package/src/coding/repomap.ts +0 -481
  200. package/src/core/agents.ts +0 -255
  201. package/src/core/compaction.ts +0 -259
  202. package/src/core/config.ts +0 -289
  203. package/src/core/context-report.ts +0 -228
  204. package/src/core/context.ts +0 -60
  205. package/src/core/count-remote.ts +0 -107
  206. package/src/core/execpolicy-rules.ts +0 -196
  207. package/src/core/execpolicy.ts +0 -385
  208. package/src/core/executor.ts +0 -454
  209. package/src/core/guardrails.ts +0 -400
  210. package/src/core/hooks.ts +0 -411
  211. package/src/core/images.ts +0 -230
  212. package/src/core/intro.ts +0 -266
  213. package/src/core/loop.ts +0 -567
  214. package/src/core/modes.ts +0 -372
  215. package/src/core/orchestrator.ts +0 -245
  216. package/src/core/proc-group.ts +0 -48
  217. package/src/core/project-trust.ts +0 -98
  218. package/src/core/reflection.ts +0 -165
  219. package/src/core/sandbox-config.ts +0 -186
  220. package/src/core/session-id.ts +0 -24
  221. package/src/core/session-images.ts +0 -73
  222. package/src/core/session-ops.ts +0 -183
  223. package/src/core/session-text.ts +0 -29
  224. package/src/core/session.ts +0 -469
  225. package/src/core/settings.ts +0 -170
  226. package/src/core/tasks.ts +0 -646
  227. package/src/core/token-scale.ts +0 -108
  228. package/src/core/tools.ts +0 -309
  229. package/src/core/trust.ts +0 -104
  230. package/src/core/types.ts +0 -330
  231. package/src/core/update-check.ts +0 -171
  232. package/src/core/usage.ts +0 -204
  233. package/src/core/validate.ts +0 -121
  234. package/src/core/verify-gate.ts +0 -159
  235. package/src/core/verify.ts +0 -236
  236. package/src/core/voice.ts +0 -158
  237. package/src/core/win-job.ts +0 -183
  238. package/src/core/workspace.ts +0 -184
  239. package/src/design/audit.ts +0 -797
  240. package/src/design/direction.ts +0 -190
  241. package/src/design/rules.ts +0 -157
  242. package/src/eval/bench.ts +0 -150
  243. package/src/eval/gauntlet-runner.ts +0 -215
  244. package/src/eval/gauntlet-support.ts +0 -84
  245. package/src/eval/gauntlet-wave3.ts +0 -269
  246. package/src/eval/gauntlet-wave4.ts +0 -217
  247. package/src/eval/gauntlet.ts +0 -253
  248. package/src/index.ts +0 -17
  249. package/src/lanes/agy.ts +0 -95
  250. package/src/lanes/approval.ts +0 -24
  251. package/src/lanes/claude.ts +0 -129
  252. package/src/lanes/codex.ts +0 -127
  253. package/src/lanes/events.ts +0 -130
  254. package/src/lanes/job.ts +0 -142
  255. package/src/lanes/opencode.ts +0 -122
  256. package/src/lanes/process.ts +0 -184
  257. package/src/lanes/progress.ts +0 -183
  258. package/src/lanes/registry.ts +0 -178
  259. package/src/lanes/runner.ts +0 -124
  260. package/src/lanes/types.ts +0 -112
  261. package/src/market/catalogs/mcp-docs.json +0 -111
  262. package/src/market/catalogs/plugins.json +0 -111
  263. package/src/market/catalogs/skills.json +0 -478
  264. package/src/market/clone.ts +0 -72
  265. package/src/market/context-cost.ts +0 -121
  266. package/src/market/digest.ts +0 -106
  267. package/src/market/index.ts +0 -22
  268. package/src/market/install.ts +0 -578
  269. package/src/market/manifest.ts +0 -187
  270. package/src/market/prereq.ts +0 -145
  271. package/src/market/registry.ts +0 -363
  272. package/src/market/resolve.ts +0 -111
  273. package/src/market/types.ts +0 -236
  274. package/src/market/validate.ts +0 -227
  275. package/src/mcp/client.ts +0 -449
  276. package/src/mcp/config.ts +0 -252
  277. package/src/mcp/local-package.ts +0 -211
  278. package/src/mcp/market-catalog.ts +0 -84
  279. package/src/mcp/market-install.ts +0 -289
  280. package/src/mcp/market.ts +0 -362
  281. package/src/mcp/oauth.ts +0 -251
  282. package/src/mcp/prompts-resources.ts +0 -249
  283. package/src/mcp/shared.ts +0 -149
  284. package/src/mcp/status.ts +0 -67
  285. package/src/mcp/tools.ts +0 -275
  286. package/src/mcp/transport.ts +0 -122
  287. package/src/mcp/trust.ts +0 -25
  288. package/src/memory/blocks.ts +0 -278
  289. package/src/memory/recall.ts +0 -355
  290. package/src/memory/scope.ts +0 -182
  291. package/src/memory/store.ts +0 -105
  292. package/src/memory/tools.ts +0 -99
  293. package/src/plugins/cli.ts +0 -119
  294. package/src/plugins/discover.ts +0 -108
  295. package/src/plugins/index.ts +0 -50
  296. package/src/plugins/install.ts +0 -184
  297. package/src/plugins/load.ts +0 -124
  298. package/src/plugins/manifest.ts +0 -92
  299. package/src/plugins/state.ts +0 -83
  300. package/src/providers/auth.ts +0 -408
  301. package/src/providers/cache.ts +0 -223
  302. package/src/providers/catalog-local.ts +0 -160
  303. package/src/providers/catalog.ts +0 -421
  304. package/src/providers/middleware-context.ts +0 -86
  305. package/src/providers/middleware.ts +0 -373
  306. package/src/providers/model-list.ts +0 -23
  307. package/src/providers/oauth/common.ts +0 -105
  308. package/src/providers/oauth/device-code.ts +0 -107
  309. package/src/providers/oauth/github-copilot.ts +0 -146
  310. package/src/providers/oauth/loopback.ts +0 -158
  311. package/src/providers/oauth/openai.ts +0 -163
  312. package/src/providers/oauth/openrouter.ts +0 -89
  313. package/src/providers/oauth/pkce.ts +0 -45
  314. package/src/providers/oauth/registry.ts +0 -39
  315. package/src/providers/oauth/seam.ts +0 -89
  316. package/src/providers/profile-glm53.ts +0 -111
  317. package/src/providers/profile-sonnet5-persona.ts +0 -65
  318. package/src/providers/profile-sonnet5-voice.ts +0 -23
  319. package/src/providers/profiles.ts +0 -156
  320. package/src/providers/provider-config.ts +0 -311
  321. package/src/providers/registry.ts +0 -333
  322. package/src/providers/responses.ts +0 -209
  323. package/src/providers/retry.ts +0 -234
  324. package/src/providers/router.ts +0 -294
  325. package/src/providers/sse.ts +0 -26
  326. package/src/providers/stream-errors.ts +0 -117
  327. package/src/providers/stream.ts +0 -566
  328. package/src/providers/thinking.ts +0 -189
  329. package/src/providers/wire-messages.ts +0 -129
  330. package/src/providers/wire-responses.ts +0 -79
  331. package/src/providers/wire-select.ts +0 -53
  332. package/src/server/http.ts +0 -291
  333. package/src/server/openapi.ts +0 -246
  334. package/src/sextant/card-hits.ts +0 -102
  335. package/src/sextant/card-keys.ts +0 -55
  336. package/src/sextant/context-source.ts +0 -157
  337. package/src/sextant/crew-cards.ts +0 -350
  338. package/src/sextant/draw-agents.ts +0 -273
  339. package/src/sextant/draw-code.ts +0 -388
  340. package/src/sextant/draw-context.ts +0 -222
  341. package/src/sextant/draw-frame.ts +0 -164
  342. package/src/sextant/draw-market.ts +0 -573
  343. package/src/sextant/draw-messages.ts +0 -386
  344. package/src/sextant/draw-pet.ts +0 -230
  345. package/src/sextant/draw-plan.ts +0 -187
  346. package/src/sextant/draw-tabs.ts +0 -85
  347. package/src/sextant/draw-util.ts +0 -65
  348. package/src/sextant/draw-wizard.ts +0 -378
  349. package/src/sextant/engine.ts +0 -230
  350. package/src/sextant/frame-hits.ts +0 -25
  351. package/src/sextant/frame.ts +0 -101
  352. package/src/sextant/git-status.ts +0 -197
  353. package/src/sextant/grid.ts +0 -59
  354. package/src/sextant/input.ts +0 -119
  355. package/src/sextant/keys.ts +0 -521
  356. package/src/sextant/layout.ts +0 -86
  357. package/src/sextant/local-commands.ts +0 -169
  358. package/src/sextant/market-source.ts +0 -287
  359. package/src/sextant/mentions.ts +0 -200
  360. package/src/sextant/message-hits.ts +0 -26
  361. package/src/sextant/model.ts +0 -387
  362. package/src/sextant/overlays.ts +0 -456
  363. package/src/sextant/panel-hits.ts +0 -38
  364. package/src/sextant/pet.ts +0 -399
  365. package/src/sextant/screen.ts +0 -324
  366. package/src/sextant/scroll-hits.ts +0 -66
  367. package/src/sextant/scrollbar.ts +0 -82
  368. package/src/sextant/sextant-bridge.ts +0 -174
  369. package/src/sextant/sextant-cards.ts +0 -142
  370. package/src/sextant/sextant-diff-base.ts +0 -63
  371. package/src/sextant/sextant-files.ts +0 -154
  372. package/src/sextant/sextant-frame-loop.ts +0 -335
  373. package/src/sextant/sextant-renderer.ts +0 -574
  374. package/src/sextant/sextant-repo.ts +0 -140
  375. package/src/sextant/theme.ts +0 -66
  376. package/src/sextant/tool-rows.ts +0 -189
  377. package/src/sextant/types.ts +0 -493
  378. package/src/skills/index.ts +0 -387
  379. package/src/skills/pack.ts +0 -220
  380. package/src/skills/spec.ts +0 -162
  381. package/src/skills/tools.ts +0 -69
  382. package/src/skills/versioned.ts +0 -227
  383. package/src/telemetry/otel-export.ts +0 -122
  384. package/src/telemetry/otel-lanes.ts +0 -89
  385. package/src/telemetry/otel-logs.ts +0 -131
  386. package/src/telemetry/otel-metrics.ts +0 -136
  387. package/src/telemetry/otel.ts +0 -397
  388. package/src/telemetry/otlp.ts +0 -76
  389. package/src/tools/ask-user.ts +0 -156
  390. package/src/tools/bash-bg.ts +0 -94
  391. package/src/tools/bash-jobs.ts +0 -237
  392. package/src/tools/design.ts +0 -151
  393. package/src/tools/evalcell.ts +0 -338
  394. package/src/tools/html-text.ts +0 -139
  395. package/src/tools/provider.ts +0 -149
  396. package/src/tools/task.ts +0 -250
  397. package/src/tools/todo.ts +0 -320
  398. package/src/tools/webfetch.ts +0 -332
  399. package/src/tools/websearch.ts +0 -359
  400. package/src/tui/agents-cmd.ts +0 -41
  401. package/src/tui/app.ts +0 -749
  402. package/src/tui/attach.ts +0 -127
  403. package/src/tui/boot-notes.ts +0 -41
  404. package/src/tui/builtin-prompts.ts +0 -59
  405. package/src/tui/checkpoints-cmd.ts +0 -70
  406. package/src/tui/clipboard-image.ts +0 -81
  407. package/src/tui/clipboard.ts +0 -78
  408. package/src/tui/commands.ts +0 -283
  409. package/src/tui/config-view.ts +0 -53
  410. package/src/tui/context-cmds.ts +0 -282
  411. package/src/tui/cost.ts +0 -108
  412. package/src/tui/crash-guard.ts +0 -173
  413. package/src/tui/focus-terminal.ts +0 -34
  414. package/src/tui/git-cmds.ts +0 -273
  415. package/src/tui/git-plain.ts +0 -58
  416. package/src/tui/info-cmd.ts +0 -150
  417. package/src/tui/input-plain.ts +0 -76
  418. package/src/tui/mcp-cmd.ts +0 -128
  419. package/src/tui/memory-note.ts +0 -77
  420. package/src/tui/modes-cmd.ts +0 -45
  421. package/src/tui/notify-seq.ts +0 -100
  422. package/src/tui/notify.ts +0 -318
  423. package/src/tui/overlays.ts +0 -97
  424. package/src/tui/pi-renderer.ts +0 -428
  425. package/src/tui/providers-cmd.ts +0 -377
  426. package/src/tui/reasoning-view.ts +0 -56
  427. package/src/tui/renderer.ts +0 -128
  428. package/src/tui/replay-marker.ts +0 -29
  429. package/src/tui/session-cmd.ts +0 -148
  430. package/src/tui/session-manage.ts +0 -95
  431. package/src/tui/sextant-attach.ts +0 -102
  432. package/src/tui/sextant-io.ts +0 -202
  433. package/src/tui/sextant-smoke.ts +0 -110
  434. package/src/tui/shell-cmd.ts +0 -158
  435. package/src/tui/smoke.ts +0 -72
  436. package/src/tui/staged-terminal.ts +0 -50
  437. package/src/tui/startup.ts +0 -12
  438. package/src/tui/theme.ts +0 -59
  439. package/src/tui/todo-label.ts +0 -7
  440. package/src/tui/trust-card.ts +0 -107
  441. package/src/tui/tui-commands.ts +0 -87
  442. package/tsconfig.json +0 -30
  443. package/vendor/pi-tui/LICENSE +0 -21
  444. package/vendor/pi-tui/PATCHES.md +0 -12
  445. package/vendor/pi-tui/PROVENANCE.md +0 -12
  446. package/vendor/pi-tui/README.upstream.md +0 -854
  447. package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  448. package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  449. package/vendor/pi-tui/src/alt-screen-search.ts +0 -158
  450. package/vendor/pi-tui/src/autocomplete.ts +0 -827
  451. package/vendor/pi-tui/src/components/alt-screen-flash.ts +0 -52
  452. package/vendor/pi-tui/src/components/box.ts +0 -138
  453. package/vendor/pi-tui/src/components/cancellable-loader.ts +0 -41
  454. package/vendor/pi-tui/src/components/editor.ts +0 -2364
  455. package/vendor/pi-tui/src/components/h-stack.ts +0 -45
  456. package/vendor/pi-tui/src/components/image.ts +0 -128
  457. package/vendor/pi-tui/src/components/input.ts +0 -448
  458. package/vendor/pi-tui/src/components/loader.ts +0 -93
  459. package/vendor/pi-tui/src/components/markdown.ts +0 -1016
  460. package/vendor/pi-tui/src/components/scroll-view.ts +0 -217
  461. package/vendor/pi-tui/src/components/select-list.ts +0 -230
  462. package/vendor/pi-tui/src/components/settings-list.ts +0 -277
  463. package/vendor/pi-tui/src/components/spacer.ts +0 -29
  464. package/vendor/pi-tui/src/components/stack.ts +0 -155
  465. package/vendor/pi-tui/src/components/text.ts +0 -108
  466. package/vendor/pi-tui/src/components/truncated-text.ts +0 -66
  467. package/vendor/pi-tui/src/components/v-stack.ts +0 -34
  468. package/vendor/pi-tui/src/editor-component.ts +0 -75
  469. package/vendor/pi-tui/src/fuzzy.ts +0 -138
  470. package/vendor/pi-tui/src/index.ts +0 -149
  471. package/vendor/pi-tui/src/keybindings.ts +0 -321
  472. package/vendor/pi-tui/src/keys.ts +0 -1402
  473. package/vendor/pi-tui/src/kill-ring.ts +0 -47
  474. package/vendor/pi-tui/src/latex.ts +0 -1381
  475. package/vendor/pi-tui/src/layout-node.ts +0 -52
  476. package/vendor/pi-tui/src/layout.ts +0 -411
  477. package/vendor/pi-tui/src/native-modifiers.ts +0 -60
  478. package/vendor/pi-tui/src/native-module-path.ts +0 -32
  479. package/vendor/pi-tui/src/stdin-buffer.ts +0 -445
  480. package/vendor/pi-tui/src/terminal-colors.ts +0 -74
  481. package/vendor/pi-tui/src/terminal-image.ts +0 -701
  482. package/vendor/pi-tui/src/terminal.ts +0 -554
  483. package/vendor/pi-tui/src/tui-alt-screen.ts +0 -1379
  484. package/vendor/pi-tui/src/tui-main-screen.ts +0 -655
  485. package/vendor/pi-tui/src/tui.ts +0 -1264
  486. package/vendor/pi-tui/src/undo-stack.ts +0 -29
  487. package/vendor/pi-tui/src/utils.ts +0 -1327
  488. package/vendor/pi-tui/src/word-navigation.ts +0 -118
  489. package/vendor/pi-tui/test/test-themes.ts +0 -39
  490. package/vendor/pi-tui/test/virtual-terminal.ts +0 -219
  491. /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
- }