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,566 +0,0 @@
1
- /** Providers: StreamFn adapters (ADR-003 seam). Errors cross as stopReason "error", never throws.
2
- *
3
- * Wire protocols supported:
4
- * - OpenAI-compatible /chat/completions (JSON + SSE streaming), with tools
5
- * - Anthropic /messages (x-api-key), with tools
6
- * - Any custom base URL + key (ROVECODE_BASE_URL/ROVECODE_API_KEY or explicit config)
7
- * Model catalogs are FETCHED from the endpoint (/v1/models) — never hard-coded (pi pattern).
8
- * Error-turn shaping (abort vs error; HTTP status + Retry-After side-channel, port #23) lives in stream-errors.ts.
9
- * Message lowering (harness parts → wire content, incl. port #34 image blocks) lives in wire-messages.ts. */
10
-
11
- import type { StreamFn, Message, AssistantTurn, StreamEvent, ModelRef, StopReason } from "../core/types.ts";
12
- import { partsText } from "../core/loop.ts";
13
- import { applyAnthropicCacheBoundaries } from "./cache.ts";
14
- import { normalizeUsage } from "../core/usage.ts";
15
- import { BUILTIN_PROVIDERS, buildSnapshot, pickDefault } from "./provider-config.ts";
16
- import { failedTurn, fetchFirstByte, httpErrorTurn } from "./stream-errors.ts";
17
- import { supportsImages } from "./catalog.ts";
18
- import { profileWire } from "./profiles.ts";
19
- import { anthropicThinking, thinkingBudget, thinkingPlan, type AnthropicThinkingShape } from "./thinking.ts";
20
- import { toOpenAiMessages, toAnthropicMessages, toOpenAiToolSchemas, asToolSchema, type WireOptions } from "./wire-messages.ts";
21
- import { sseLines } from "./sse.ts";
22
- import { oauthStream, resolveOAuthProviderConfig, type OAuthSeamDeps } from "./oauth/seam.ts";
23
- import { openaiResponsesStream } from "./responses.ts"; // the /responses wire (aion port #75)
24
- import { openAiWire } from "./wire-select.ts"; // chat vs responses, per call
25
-
26
- export { toOpenAiMessages, toAnthropicMessages, toOpenAiToolSchemas } from "./wire-messages.ts";
27
-
28
- /** port #34: image parts go on the wire as image blocks unless the models.dev catalog says the
29
- * model has no image input (then wire-messages.ts substitutes a text placeholder); an unknown
30
- * model is given the image (catalog.ts supportsImages). */
31
- const wireOptions = (model: ModelRef): WireOptions => ({ vision: supportsImages(model) !== false });
32
-
33
- export interface ProviderConfig {
34
- id: string; // provider id, e.g. "kaesra"
35
- baseUrl: string; // e.g. https://api.kaesra.tech/v1
36
- apiKey: string;
37
- protocol: "openai" | "anthropic";
38
- defaultModel?: string;
39
- /** extra request headers (proxies, org ids) — providers.json `headers`; the protocol's own auth headers win.
40
- * Also the OAuth providers' own headers (GitHub Copilot's editor headers, OpenAI's account id) */
41
- headers?: Record<string, string>;
42
- /** apiKey is a stored OAuth access token — providerStream/providerStreaming refresh it per request (oauth/seam.ts) */
43
- oauth?: true;
44
- /** pin the OpenAI wire for every request (the OpenAI OAuth token sets "responses" — a ChatGPT token is accepted by
45
- * the Codex Responses backend only); unset = ROVECODE_OPENAI_WIRE, else the catalog rule (wire-select.ts) */
46
- wire?: "responses" | "chat";
47
- }
48
-
49
- /** What a wire adapter needs: endpoint root, key, optional extra headers (ProviderConfig.headers). */
50
- export interface AdapterOptions {
51
- baseUrl: string;
52
- apiKey: string;
53
- headers?: Record<string, string>;
54
- }
55
-
56
- export interface ModelCatalogEntry {
57
- id: string;
58
- ownedBy?: string;
59
- type?: string;
60
- }
61
-
62
- // ---------- catalog (fetched, cached) ----------
63
-
64
- const catalogCache = new Map<string, { models: ModelCatalogEntry[]; fetchedAt: number }>();
65
- const CATALOG_TTL_MS = 5 * 60_000;
66
-
67
- export async function fetchModels(cfg: ProviderConfig, force = false): Promise<ModelCatalogEntry[]> {
68
- const hit = catalogCache.get(cfg.id);
69
- if (!force && hit && Date.now() - hit.fetchedAt < CATALOG_TTL_MS) return hit.models;
70
- const url = cfg.baseUrl.replace(/\/$/, "") + "/models";
71
- let models: ModelCatalogEntry[] = [];
72
- try {
73
- const res = await fetch(url, { headers: authHeaders(cfg) });
74
- if (res.ok) {
75
- const json = (await res.json()) as { data?: { id: string; owned_by?: string; type?: string }[] };
76
- models = (json.data ?? []).map((m) => ({ id: m.id, ownedBy: m.owned_by, type: m.type }));
77
- }
78
- } catch { /* errors are empty catalog — provider seam never throws */ }
79
- catalogCache.set(cfg.id, { models, fetchedAt: Date.now() });
80
- return models;
81
- }
82
-
83
- function authHeaders(cfg: ProviderConfig): Record<string, string> {
84
- return {
85
- ...(cfg.headers ?? {}),
86
- ...(cfg.protocol === "anthropic"
87
- ? { "x-api-key": cfg.apiKey, "anthropic-version": "2023-06-01" }
88
- : { authorization: `Bearer ${cfg.apiKey}` }),
89
- };
90
- }
91
-
92
- // ---------- factories ----------
93
-
94
- const optsOf = (cfg: ProviderConfig): AdapterOptions => ({ baseUrl: cfg.baseUrl, apiKey: cfg.apiKey, ...(cfg.headers !== undefined ? { headers: cfg.headers } : {}) });
95
-
96
- /** `oauth`: the seam's injectable clock/fetch/store/provider list (tests drive a fake endpoint); production passes nothing */
97
- export function providerStream(cfg: ProviderConfig, oauth: OAuthSeamDeps = {}): StreamFn {
98
- if (cfg.oauth) return oauthStream(cfg, openAiWire(cfg, openaiCompatStream, openaiResponsesStream), oauth); // a stored OAuth token: refresh-once-if-expired, then the wire picked per call
99
- return cfg.protocol === "anthropic" ? anthropicStream(optsOf(cfg)) : openAiWire(cfg, openaiCompatStream, openaiResponsesStream)(optsOf(cfg));
100
- }
101
-
102
- /** The streaming twin of providerStream: text_delta as the model writes, the same final turn.
103
- * This is what every surface gets by default — the one-shot JSON adapters above are the fallback.
104
- * The ONE builder the CLI's SSE sites use, so an OAuth config's headers (GitHub Copilot's editor
105
- * headers) reach the streaming request too; a headerless `openaiCompatStreaming({ baseUrl, apiKey })`
106
- * at a call site is the bug test/unit/auth-login-sse.test.ts pins against. */
107
- export function providerStreaming(cfg: ProviderConfig, oauth: OAuthSeamDeps = {}): StreamFn {
108
- if (cfg.oauth) return oauthStream(cfg, openAiWire(cfg, openaiCompatStreaming, openaiResponsesStream), oauth); // the TUI's default path: a long session outlives a 30-minute Copilot token, so it refreshes here too
109
- return cfg.protocol === "anthropic" ? anthropicStreaming(optsOf(cfg)) : openAiWire(cfg, openaiCompatStreaming, openaiResponsesStream)(optsOf(cfg)); // the Responses wire is SSE-only, so it is the same adapter on both factories
110
- }
111
-
112
- /** Streaming is ON unless ROVECODE_STREAM says otherwise: `off`/`json`/`0`/`false`/`none` fall back to
113
- * the one-shot JSON adapters (a proxy with no SSE route, a recording harness that wants one body).
114
- * `sse` is still accepted — it used to be the opt-IN spelling and some scripts still set it. */
115
- export function wantsStreaming(env: { ROVECODE_STREAM?: string | undefined }): boolean {
116
- const v = (env.ROVECODE_STREAM ?? "").trim().toLowerCase();
117
- return !(v === "off" || v === "json" || v === "0" || v === "false" || v === "none");
118
- }
119
-
120
- /** Streaming requests ask for an UNCOMPRESSED body. Measured against the Anthropic endpoint: with the
121
- * default `accept-encoding: gzip, deflate, br`, the decompressor holds the whole SSE stream and every
122
- * event lands in one burst at the end (first text delta at 11.1 s of an 11.1 s response); with
123
- * `identity` the same prompt starts writing at 1.4 s. Streaming is the point of these two adapters, so
124
- * the extra bytes are the right trade — the JSON adapters below keep compression. */
125
- const SSE_HEADERS = { "accept-encoding": "identity" } as const;
126
-
127
- /** A proxy that ignores `stream: true` and answers with one JSON body would otherwise leave the SSE
128
- * reader with nothing to parse and the turn empty. Content-type decides: an explicit application/json
129
- * is read whole through the same parser the one-shot adapters use (no deltas — there is nothing to
130
- * stream), anything else is treated as an event stream. */
131
- const isJsonBody = (res: Response): boolean => (res.headers.get("content-type") ?? "").includes("application/json");
132
-
133
- /** Thinking, per protocol.
134
- *
135
- * Anthropic has TWO request shapes and no model exposes both reliably — measured against the live
136
- * API on 2026-09-03:
137
- *
138
- * model output_config.effort thinking.type=enabled
139
- * claude-opus-5 200 400 "not supported for this model"
140
- * claude-sonnet-5 200 400
141
- * claude-opus-4-5 200 200
142
- * claude-sonnet-4-5 400 "does not support…" 200 → [thinking, text]
143
- * claude-haiku-4-5 400 200
144
- *
145
- * So the shape is a property of the model, and guessing wrong is a hard 400, not a downgrade. There
146
- * is no capability field to read (the /models list carries ids only), and a hard-coded table would
147
- * rot with the next release — this file's rule is that catalogs are fetched, never hard-coded. So:
148
- * try the newer shape, and when the endpoint says that shape is unsupported, flip and retry ONCE,
149
- * remembering the answer per model id for the rest of the process. One wasted round trip the first
150
- * time a model is used, never again.
151
- *
152
- * `off` is not "send nothing": opus-5 thinks by DEFAULT (measured — a bare request streams
153
- * thinking_delta), so off has to say so with thinking.type=disabled.
154
- *
155
- * Extended thinking and tool use: the thinking blocks are NOT echoed back on the next turn, and
156
- * measurement says they need not be — a tool_result turn that omits them answers 200 on both shapes
157
- * and both model generations. */
158
- // the dial itself (budgets, both Anthropic shapes, every OpenAI-compatible dialect) lives in thinking.ts;
159
- // re-exported so the adapters' callers and tests keep one import
160
- export { anthropicThinking, thinkingBudget, type AnthropicThinkingShape };
161
-
162
- /** what the endpoint said when the shape was wrong — both spellings it uses. The message must ALSO name
163
- * the dial (thinking/effort/budget): "not supported for this model" alone is how the API refuses other
164
- * things too (a tool feature, an image), and flipping the shape on those would cost every later request
165
- * a wasted round trip. */
166
- const WRONG_SHAPE = /not supported for this model|does not support the effort parameter/i;
167
- const ABOUT_THINKING = /thinking|effort|budget_tokens|output_config/i;
168
- const wrongShape = (text: string): boolean => WRONG_SHAPE.test(text) && ABOUT_THINKING.test(text);
169
-
170
- /** per provider+model, learned from a 400. Process-lifetime: a model's shape does not change under us.
171
- * Keyed with the provider so a gateway that fronts the same model id differently cannot poison the
172
- * direct endpoint's memory (or the other way round). Exported for tests only. */
173
- export const shapeByModel = new Map<string, AnthropicThinkingShape>();
174
- export const shapeKey = (model: ModelRef): string => `${model.provider}/${model.model}`;
175
-
176
- /** the answer needs room BESIDE the thinking budget — never less than the caller asked for */
177
- export function anthropicMaxTokens(model: ModelRef): number {
178
- const base = model.maxTokens ?? 8192; // buildDef normally sets maxTokens from the catalog; 8192 is the floor every current Claude accepts
179
- const budget = thinkingBudget(model.effort);
180
- return budget === null ? base : Math.max(base, budget + 4096);
181
- }
182
-
183
- /** the shape the next request for this model will use (for /effort and `model show` to say the truth) */
184
- export function anthropicShapeFor(model: ModelRef): AnthropicThinkingShape { return shapeByModel.get(shapeKey(model)) ?? "effort"; }
185
-
186
- /** POST to Anthropic, learning the model's thinking shape from a wrong-shape 400 and retrying ONCE.
187
- * `build` is called per attempt because the shape changes the body. A failure that is NOT about the
188
- * shape is returned as-is — the adapters turn it into an error turn (httpErrorTurn). Bounded: at most
189
- * two sends per request, and the flipped shape is remembered only when the retry was not the same
190
- * refusal — two wrong-shape 400s in a row (a model that takes neither) leave no memory, so the next
191
- * request does not oscillate between two guaranteed failures. */
192
- async function anthropicPost(url: string, headers: Record<string, string>, model: ModelRef, build: (extra: Record<string, unknown>) => unknown, signal: AbortSignal | undefined): Promise<Response> {
193
- const wanted = model.effort;
194
- const key = shapeKey(model);
195
- let shape: AnthropicThinkingShape = shapeByModel.get(key) ?? "effort";
196
- const send = (): Promise<Response> => fetchFirstByte(url, { method: "POST", headers, body: JSON.stringify(build(anthropicThinking(wanted, shape))), ...(signal ? { signal } : {}) });
197
- const res = await send();
198
- // nothing to learn when the request carried no level (auto/off/unset use one shape-free field), or when it worked
199
- if (res.ok || wanted === undefined || wanted === "auto" || wanted === "off" || res.status !== 400) return res;
200
- const text = await res.clone().text().catch(() => "");
201
- if (!wrongShape(text)) return res;
202
- shape = shape === "effort" ? "budget" : "effort";
203
- const retry = await send();
204
- if (retry.status === 400 && wrongShape(await retry.clone().text().catch(() => ""))) shapeByModel.delete(key);
205
- else shapeByModel.set(key, shape);
206
- return retry;
207
- }
208
-
209
- /** OpenAI-compatible endpoints name the dial in a dozen vocabularies (thinking.ts): the plan's fields are
210
- * spread into the body after the profile's own wire fields (profiles.ts), so the dial wins a clash. */
211
- function reasoningEffort(model: ModelRef): Record<string, unknown> {
212
- return thinkingPlan(model, "openai").fields;
213
- }
214
-
215
- export function openaiCompatStream(opts: AdapterOptions): StreamFn {
216
- return async function* (model: ModelRef, messages: Message[], options?: { signal?: AbortSignal; tools?: unknown[] }): AsyncGenerator<StreamEvent> {
217
- let turn: AssistantTurn;
218
- try {
219
- const res = await fetchFirstByte(opts.baseUrl.replace(/\/$/, "") + "/chat/completions", {
220
- method: "POST",
221
- headers: { ...(opts.headers ?? {}), "content-type": "application/json", authorization: `Bearer ${opts.apiKey}` },
222
- body: JSON.stringify({
223
- model: model.model,
224
- messages: toOpenAiMessages(messages, wireOptions(model)),
225
- ...(options?.tools?.length ? { tools: toOpenAiToolSchemas(options.tools) } : {}),
226
- stream: false,
227
- ...profileWire(model, false), // per-family endpoint fields (providers/profiles.ts); the effort word below wins a clash
228
- ...reasoningEffort(model),
229
- ...(model.maxTokens ? { max_tokens: model.maxTokens } : {}),
230
- }),
231
- signal: options?.signal,
232
- });
233
- if (!res.ok) {
234
- turn = await httpErrorTurn(res); // status + Retry-After recorded for withRetry (stream-errors.ts)
235
- } else {
236
- turn = parseOpenAiResponse(await res.json());
237
- }
238
- } catch (e) {
239
- turn = failedTurn(e, options?.signal);
240
- }
241
- yield { type: "turn", turn };
242
- };
243
- }
244
-
245
- /** Streaming variant: emits text_delta events as they arrive, then the final turn. */
246
- export function openaiCompatStreaming(opts: AdapterOptions): StreamFn {
247
- return async function* (model: ModelRef, messages: Message[], options?: { signal?: AbortSignal; tools?: unknown[] }): AsyncGenerator<StreamEvent> {
248
- let turn: AssistantTurn;
249
- let buffer = "";
250
- const toolArgs = new Map<number, { id: string; name: string; args: string }>();
251
- try {
252
- const res = await fetchFirstByte(opts.baseUrl.replace(/\/$/, "") + "/chat/completions", {
253
- method: "POST",
254
- headers: { ...(opts.headers ?? {}), ...SSE_HEADERS, "content-type": "application/json", authorization: `Bearer ${opts.apiKey}` },
255
- body: JSON.stringify({
256
- model: model.model,
257
- messages: toOpenAiMessages(messages, wireOptions(model)),
258
- ...(options?.tools?.length ? { tools: toOpenAiToolSchemas(options.tools) } : {}),
259
- stream: true,
260
- // ask for the final usage chunk — without it most OpenAI-compat SSE streams omit usage
261
- stream_options: { include_usage: true },
262
- ...profileWire(model, true), // per-family endpoint fields, streaming variant (tool_stream for GLM)
263
- ...reasoningEffort(model),
264
- ...(model.maxTokens ? { max_tokens: model.maxTokens } : {}),
265
- }),
266
- signal: options?.signal,
267
- });
268
- if (!res.ok || !res.body) {
269
- turn = await httpErrorTurn(res); // status + Retry-After recorded for withRetry (stream-errors.ts)
270
- yield { type: "turn", turn };
271
- return;
272
- }
273
- if (isJsonBody(res)) { yield { type: "turn", turn: parseOpenAiResponse(await res.json()) }; return; }
274
- let finish: StopReason = "end_turn";
275
- let usage: AssistantTurn["usage"] = { input: 0, output: 0 };
276
- for await (const line of sseLines(res.body)) {
277
- const ev = JSON.parse(line) as {
278
- choices?: { delta?: { content?: string | null; reasoning_content?: string | null; tool_calls?: { index?: number; id?: string; function?: { name?: string; arguments?: string } }[] }; finish_reason?: string | null }[];
279
- usage?: unknown;
280
- };
281
- const c = ev.choices?.[0];
282
- if (c?.delta?.content) { buffer += c.delta.content; yield { type: "text_delta", text: c.delta.content }; }
283
- // reasoning slices (GLM / DeepSeek-style `reasoning_content`): surfaced exactly like Anthropic's
284
- // thinking_delta — the live status line counts them, the turn's parts never carry them
285
- if (c?.delta?.reasoning_content) yield { type: "reasoning_delta", text: c.delta.reasoning_content };
286
- for (const tc of c?.delta?.tool_calls ?? []) {
287
- const idx = tc.index ?? 0;
288
- const cur = toolArgs.get(idx) ?? { id: tc.id ?? `tc${idx}`, name: tc.function?.name ?? "", args: "" };
289
- if (tc.id) cur.id = tc.id;
290
- if (tc.function?.name) cur.name += tc.function.name;
291
- if (tc.function?.arguments) cur.args += tc.function.arguments;
292
- toolArgs.set(idx, cur);
293
- }
294
- if (c?.finish_reason) finish = c.finish_reason === "tool_calls" ? "tool_use" : c.finish_reason === "length" ? "length" : "end_turn";
295
- if (ev.usage) {
296
- // same normalization as the JSON adapters (parseOpenAiResponse/parseAnthropicResponse):
297
- // cached_tokens subtracted from the inclusive prompt count, cacheRead/Write carried
298
- const u = normalizeUsage(ev.usage);
299
- usage = { input: u.input, output: u.output, cacheRead: u.cacheRead || undefined, cacheWrite: u.cacheWrite || undefined };
300
- }
301
- }
302
- const parts: AssistantTurn["parts"] = [];
303
- if (buffer) parts.push({ kind: "text", text: buffer });
304
- for (const [, tc] of [...toolArgs].sort((a, b) => a[0] - b[0])) {
305
- let args: unknown = {};
306
- try { args = JSON.parse(tc.args || "{}"); } catch { args = { _raw: tc.args }; }
307
- parts.push({ kind: "tool_call", id: tc.id, tool: tc.name, args });
308
- }
309
- turn = { parts, stopReason: finish, usage };
310
- } catch (e) {
311
- turn = failedTurn(e, options?.signal, buffer); // mid-stream abort: the deltas already streamed survive as the turn's text
312
- }
313
- yield { type: "turn", turn };
314
- };
315
- }
316
-
317
- /** Streaming variant of the Anthropic adapter: emits text_delta as the model writes, then the final
318
- * turn. The Messages SSE stream is a sequence of numbered content blocks — `content_block_start`
319
- * opens one (text or tool_use), `content_block_delta` carries `text_delta` for prose and
320
- * `input_json_delta` for a tool call's arguments (streamed as JSON text, one fragment at a time),
321
- * `content_block_stop` closes it. Usage arrives in two halves: the input side on `message_start`,
322
- * the output count on the final `message_delta` — they are merged so /cost sees the same shape the
323
- * JSON adapter produces. Block ORDER is preserved: parts are emitted by block index, so a text
324
- * block before a tool_use stays before it. */
325
- export function anthropicStreaming(opts: AdapterOptions): StreamFn {
326
- return async function* (model: ModelRef, messages: Message[], options?: { signal?: AbortSignal; tools?: unknown[] }): AsyncGenerator<StreamEvent> {
327
- let turn: AssistantTurn;
328
- let buffer = "";
329
- /** open + finished blocks by wire index; `json` accumulates an input_json_delta */
330
- const blocks = new Map<number, { kind: "text"; text: string } | { kind: "tool"; id: string; name: string; json: string } | { kind: "thinking" }>();
331
- try {
332
- const system = messages.filter((m) => m.role === "system").map((m) => partsText(m.parts)).join("\n");
333
- const rest: Record<string, unknown> = {};
334
- if (options?.tools?.length) rest.tools = options.tools.map((t) => { const sc = asToolSchema(t); return { name: sc.name, description: sc.description, input_schema: sc.args }; });
335
- if (system) rest.system = system;
336
- const body = (extra: Record<string, unknown>): Record<string, unknown> => ({
337
- model: model.model,
338
- max_tokens: anthropicMaxTokens(model),
339
- messages: toAnthropicMessages(messages, wireOptions(model)),
340
- stream: true,
341
- ...rest,
342
- ...extra,
343
- });
344
- const res = await anthropicPost(
345
- opts.baseUrl.replace(/\/$/, "") + "/messages",
346
- { ...(opts.headers ?? {}), ...SSE_HEADERS, "content-type": "application/json", "x-api-key": opts.apiKey, "anthropic-version": "2023-06-01" },
347
- model,
348
- (extra) => applyAnthropicCacheBoundaries(body(extra)),
349
- options?.signal,
350
- );
351
- if (!res.ok || !res.body) {
352
- turn = await httpErrorTurn(res); // status + Retry-After recorded for withRetry (stream-errors.ts)
353
- yield { type: "turn", turn };
354
- return;
355
- }
356
- if (isJsonBody(res)) { yield { type: "turn", turn: parseAnthropicResponse(await res.json()) }; return; }
357
- let stop: StopReason = "end_turn";
358
- let usage: AssistantTurn["usage"] = { input: 0, output: 0 };
359
- for await (const line of sseLines(res.body)) {
360
- const ev = JSON.parse(line) as {
361
- type?: string;
362
- index?: number;
363
- message?: { usage?: unknown };
364
- content_block?: { type?: string; id?: string; name?: string };
365
- delta?: { type?: string; text?: string; partial_json?: string; thinking?: string; stop_reason?: string | null };
366
- usage?: unknown;
367
- error?: { type?: string; message?: string };
368
- };
369
- switch (ev.type) {
370
- case "message_start": {
371
- const u = normalizeUsage(ev.message?.usage);
372
- usage = { input: u.input, output: u.output, cacheRead: u.cacheRead || undefined, cacheWrite: u.cacheWrite || undefined };
373
- break;
374
- }
375
- case "content_block_start": {
376
- const i = ev.index ?? 0;
377
- if (ev.content_block?.type === "tool_use") blocks.set(i, { kind: "tool", id: ev.content_block.id ?? `tc${i}`, name: ev.content_block.name ?? "unknown", json: "" });
378
- else if (ev.content_block?.type === "thinking" || ev.content_block?.type === "redacted_thinking") blocks.set(i, { kind: "thinking" });
379
- else blocks.set(i, { kind: "text", text: "" });
380
- break;
381
- }
382
- case "content_block_delta": {
383
- const i = ev.index ?? 0;
384
- const b = blocks.get(i);
385
- if (ev.delta?.type === "text_delta" && ev.delta.text !== undefined) {
386
- buffer += ev.delta.text;
387
- if (b?.kind === "text") b.text += ev.delta.text;
388
- else blocks.set(i, { kind: "text", text: ev.delta.text }); // a delta without its start
389
- yield { type: "text_delta", text: ev.delta.text };
390
- } else if (ev.delta?.type === "input_json_delta" && ev.delta.partial_json !== undefined && b?.kind === "tool") {
391
- b.json += ev.delta.partial_json;
392
- } else if (ev.delta?.type === "thinking_delta" && ev.delta.thinking !== undefined) {
393
- // the model is reasoning: surface the slice so the TUI can prove work is happening, keep
394
- // nothing — thinking is not the answer (signature_delta carries no tokens and is skipped)
395
- yield { type: "reasoning_delta", text: ev.delta.thinking };
396
- }
397
- break;
398
- }
399
- case "message_delta": {
400
- if (ev.delta?.stop_reason === "max_tokens") stop = "length";
401
- // the output count only exists here; the input side stays as message_start reported it
402
- if (ev.usage !== undefined) {
403
- const u = normalizeUsage(ev.usage);
404
- usage = { ...usage, output: u.output || usage.output, cacheRead: usage.cacheRead ?? (u.cacheRead || undefined), cacheWrite: usage.cacheWrite ?? (u.cacheWrite || undefined) };
405
- }
406
- break;
407
- }
408
- // a mid-stream `error` event ends the turn with what was already streamed
409
- case "error":
410
- throw new Error(ev.error?.message ?? "anthropic stream error");
411
- default:
412
- break;
413
- }
414
- }
415
- const parts: AssistantTurn["parts"] = [];
416
- for (const [, b] of [...blocks].sort((a, z) => a[0] - z[0])) {
417
- if (b.kind === "thinking") continue; // reasoning never becomes a part
418
- if (b.kind === "text") { if (b.text) parts.push({ kind: "text", text: b.text }); continue; }
419
- let args: unknown = {};
420
- try { args = JSON.parse(b.json || "{}"); } catch { args = { _raw: b.json }; }
421
- parts.push({ kind: "tool_call", id: b.id, tool: b.name, args });
422
- stop = "tool_use";
423
- }
424
- turn = { parts, stopReason: stop, usage };
425
- } catch (e) {
426
- turn = failedTurn(e, options?.signal, buffer); // mid-stream abort: the deltas already streamed survive as the turn's text
427
- }
428
- yield { type: "turn", turn };
429
- };
430
- }
431
-
432
- /** Anthropic Messages protocol adapter. */
433
- export function anthropicStream(opts: AdapterOptions): StreamFn {
434
- return async function* (model: ModelRef, messages: Message[], options?: { signal?: AbortSignal; tools?: unknown[] }): AsyncGenerator<StreamEvent> {
435
- let turn: AssistantTurn;
436
- try {
437
- const system = messages.filter((m) => m.role === "system").map((m) => partsText(m.parts)).join("\n");
438
- const rest: Record<string, unknown> = {};
439
- if (options?.tools?.length) rest.tools = options.tools.map((t) => { const sc = asToolSchema(t); return { name: sc.name, description: sc.description, input_schema: sc.args }; });
440
- if (system) rest.system = system;
441
- const body = (extra: Record<string, unknown>): Record<string, unknown> => ({
442
- model: model.model,
443
- max_tokens: anthropicMaxTokens(model),
444
- messages: toAnthropicMessages(messages, wireOptions(model)),
445
- ...rest,
446
- ...extra,
447
- });
448
- // port #5: place prompt-cache breakpoints on the stable prefix (hermes pattern)
449
- const res = await anthropicPost(
450
- opts.baseUrl.replace(/\/$/, "") + "/messages",
451
- { ...(opts.headers ?? {}), "content-type": "application/json", "x-api-key": opts.apiKey, "anthropic-version": "2023-06-01" },
452
- model,
453
- (extra) => applyAnthropicCacheBoundaries(body(extra)),
454
- options?.signal,
455
- );
456
- if (!res.ok) {
457
- turn = await httpErrorTurn(res); // status + Retry-After recorded for withRetry (stream-errors.ts)
458
- } else {
459
- turn = parseAnthropicResponse(await res.json());
460
- }
461
- } catch (e) {
462
- turn = failedTurn(e, options?.signal);
463
- }
464
- yield { type: "turn", turn };
465
- };
466
- }
467
-
468
- // ---------- parsing ----------
469
-
470
- function parseOpenAiResponse(json: unknown): AssistantTurn {
471
- const j = json as {
472
- choices: { message: { content: string | null; tool_calls?: { id: string; function: { name: string; arguments: string } }[] }; finish_reason: string | null }[];
473
- usage?: { prompt_tokens: number; completion_tokens: number };
474
- };
475
- const c = j.choices?.[0];
476
- const parts: AssistantTurn["parts"] = [];
477
- if (c?.message.content) parts.push({ kind: "text", text: c.message.content });
478
- for (const tc of c?.message.tool_calls ?? []) {
479
- let args: unknown = {};
480
- try { args = JSON.parse(tc.function.arguments || "{}"); } catch { args = { _raw: tc.function.arguments }; }
481
- parts.push({ kind: "tool_call", id: tc.id, tool: tc.function.name, args });
482
- }
483
- const stop = (c?.message.tool_calls?.length ?? 0) > 0
484
- ? "tool_use"
485
- : c?.finish_reason === "length" ? "length" : "end_turn";
486
- const u = normalizeUsage(j.usage);
487
- return { parts, stopReason: stop, usage: { input: u.input, output: u.output, cacheRead: u.cacheRead || undefined, cacheWrite: u.cacheWrite || undefined } };
488
- }
489
-
490
- function parseAnthropicResponse(json: unknown): AssistantTurn {
491
- const j = json as {
492
- content: { type: string; text?: string; id?: string; name?: string; input?: unknown }[];
493
- stop_reason: string | null;
494
- usage: { input_tokens: number; output_tokens: number };
495
- };
496
- const parts: AssistantTurn["parts"] = [];
497
- for (const b of j.content ?? []) {
498
- if (b.type === "text" && b.text) parts.push({ kind: "text", text: b.text });
499
- if (b.type === "tool_use" && b.id) parts.push({ kind: "tool_call", id: b.id, tool: b.name ?? "unknown", args: b.input ?? {} });
500
- }
501
- const stop = (j.content ?? []).some((b) => b.type === "tool_use") ? "tool_use" : j.stop_reason === "max_tokens" ? "length" : "end_turn";
502
- const u = normalizeUsage(j.usage);
503
- return { parts, stopReason: stop, usage: { input: u.input, output: u.output, cacheRead: u.cacheRead || undefined, cacheWrite: u.cacheWrite || undefined } };
504
- }
505
-
506
- // ---------- SSE: the line reader lives in sse.ts (shared with any future wire that must not import this file) ----------
507
-
508
- // ---------- mock (test seam) ----------
509
-
510
- export interface MockScript { turns: AssistantTurn[] }
511
-
512
- export function mockStream(script: MockScript): StreamFn {
513
- let i = 0;
514
- return async function* (_model: ModelRef, _messages: Message[]): AsyncGenerator<StreamEvent> {
515
- const turn = script.turns[Math.min(i, script.turns.length - 1)]!;
516
- i++;
517
- yield { type: "turn", turn };
518
- };
519
- }
520
-
521
- export function textTurn(text: string): AssistantTurn {
522
- return { parts: [{ kind: "text", text }], stopReason: "end_turn", usage: { input: 0, output: 1 } };
523
- }
524
-
525
- export function toolTurn(calls: { id: string; tool: string; args: unknown }[]): AssistantTurn {
526
- return { parts: calls.map((c) => ({ kind: "tool_call" as const, ...c })), stopReason: "tool_use", usage: { input: 0, output: 1 } };
527
- }
528
-
529
- // ---------- registry: named providers from env/config ----------
530
- // The provider table and the providers.json merge live in provider-config.ts (data) and the live
531
- // dispatcher in registry.ts. resolveProvider() is the ONE-SHOT default lookup the CLI uses at boot.
532
-
533
- /** Resolve the default provider config: ROVECODE_BASE_URL/ROVECODE_API_KEY override, else the
534
- * providers.json `default` selector (when that provider has a key), else stored credential
535
- * (`rovecode auth set`, port #37) in provider order, else a named env key, else a stored OAuth
536
- * token (`rovecode auth login`, oauth/seam.ts — "a stored token is used when no API key is set"), else null.
537
- *
538
- * Precedence follows opencode provider.ts @ ebece6e: stored api keys are merged AFTER env
539
- * (provider.ts:1578-1602, later mergeProvider patch wins) so a stored credential beats a
540
- * named env key; the explicit pair keeps its documented "always wins" rank, like opencode's
541
- * config source re-applied last (provider.ts:1643-1651). provider-config.ts pickDefault is
542
- * the single implementation; the live registry shares it. */
543
- export function resolveProvider(overrides: { baseUrl?: string; apiKey?: string; id?: string; protocol?: "openai" | "anthropic"; defaultModel?: string } = {}): ProviderConfig | null {
544
- const base = overrides.baseUrl ?? process.env.ROVECODE_BASE_URL;
545
- const key = overrides.apiKey ?? process.env.ROVECODE_API_KEY ?? process.env.OPENAI_API_KEY;
546
- if (base && key) {
547
- const looksAnthropic = overrides.protocol === "anthropic" || base.includes("anthropic.com");
548
- const defaultModel = overrides.defaultModel ?? process.env.ROVECODE_MODEL;
549
- return { id: overrides.id ?? "custom", baseUrl: base, apiKey: key, protocol: looksAnthropic ? "anthropic" : "openai", ...(defaultModel !== undefined ? { defaultModel } : {}) };
550
- }
551
- const pick = pickDefault(buildSnapshot(process.cwd()));
552
- if (pick === null) return resolveOAuthProviderConfig(); // a stored OAuth token is used when no API key is set; else null
553
- const p = pick.provider;
554
- return {
555
- id: p.id, baseUrl: p.baseUrl, apiKey: p.apiKey ?? "", protocol: p.protocol,
556
- ...(pick.model !== undefined ? { defaultModel: pick.model } : {}),
557
- ...(p.headers !== undefined ? { headers: p.headers } : {}),
558
- };
559
- }
560
-
561
- export function listBuiltinProviders(): { id: string; baseUrl: string; protocol: string; envKey: string; configured: boolean }[] {
562
- return BUILTIN_PROVIDERS.map((p) => {
563
- const envKey = p.keyEnv ?? `${p.id.toUpperCase()}_API_KEY`;
564
- return { id: p.id, baseUrl: p.baseUrl, protocol: p.protocol, envKey, configured: Boolean(process.env[envKey]) };
565
- });
566
- }