rovecode 0.3.2 → 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 -45
  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 +38 -11
  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,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
- }