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,160 +0,0 @@
1
- /** rovecode's own price table for models the models.dev snapshot does not carry — the API ALIASES a
2
- * vendor sells under (DeepSeek's `deepseek-chat` / `deepseek-reasoner` are aliases of a release; the
3
- * snapshot lists release ids), and xAI's retired grok-4 slugs, which still resolve but are served and
4
- * billed as grok-4.3. Without these a real run on those ids is "unpriced" in /cost and `rovecode model show`.
5
- *
6
- * Rules: an entry here applies ONLY when neither the live models.dev layer nor the snapshot has the model
7
- * (catalog.ts findIn) — a later snapshot entry wins by construction. Every entry names the vendor page the
8
- * numbers were read from and the day; "fetched" means the page was read on that day, "unverified" means the
9
- * page no longer documents the id and the row is the last known value. `rovecode model show` prints
10
- * "priced from rovecode's own table" so nobody mistakes them for models.dev's. Prices are USD per million
11
- * tokens. Output limits are given only where the vendor publishes one (the runtime caps at MAX_OUTPUT_CAP). */
12
-
13
- export interface LocalModel {
14
- context: number;
15
- output?: number;
16
- /** has a reasoning mode (thinking.ts sends the dial only when this is not false) */
17
- reasoning: boolean;
18
- toolCall: boolean;
19
- image?: boolean;
20
- cost: { input: number; output: number; cacheRead?: number; cacheWrite?: number };
21
- /** where the numbers were read — the reader's warrant */
22
- source: string;
23
- /** when, and how: "YYYY-MM-DD (fetched)" or "YYYY-MM-DD (unverified: …)" */
24
- checked: string;
25
- /** Take precedence over a models.dev entry for this model. Off by default and deliberately rare: the
26
- * overlay exists to fill gaps, and a snapshot that gains a model should normally win. Set it ONLY when
27
- * the vendor's own page was read and disagrees with the snapshot, and say so in `source` — a reader has
28
- * to be able to see which number rovecode chose and why. */
29
- override?: true;
30
- }
31
-
32
- /** A vendor whose price CHANGES WITH PROMPT SIZE. models.dev has no field for this, so it lives beside the
33
- * overlay and augments a row whatever its source — the snapshot keeps owning the base prices.
34
- *
35
- * `mode` is the part that is easy to get wrong and expensive to get wrong:
36
- * - "per-request": crossing the threshold reprices THE WHOLE REQUEST, every token, at the upper rate
37
- * (xAI). A 201k-token prompt costs 201k × the high rate, not 200k low + 1k high.
38
- * - "marginal": only the tokens past the threshold take the upper rate. NO vendor row uses this today —
39
- * it exists because the two shapes are not interchangeable and a cost function must branch on which one
40
- * it has, rather than assume. Do not classify a vendor as marginal without wording that says so. */
41
- export interface PriceTier {
42
- thresholdTokens: number;
43
- mode: "per-request" | "marginal";
44
- above: { input: number; output: number; cacheRead?: number; cacheWrite?: number };
45
- source: string;
46
- checked: string;
47
- }
48
-
49
- /** xAI, docs.x.ai/developers/migration/may-15-retirement (fetched 2026-09-04): grok-4-0709, grok-4-fast-reasoning,
50
- * grok-4-fast-non-reasoning, grok-4-1-fast-* and grok-3 were retired on 2026-05-15; the slugs still resolve and
51
- * are SERVED BY grok-4.3 — reasoning slugs at low effort, non-reasoning ones at none — and BILLED at grok-4.3
52
- * rates ($1.25 in / $2.50 out / $0.20 cached per 1M under 200k context; docs.x.ai/docs/models/grok-4.3, fetched
53
- * the same day: 1M context, configurable reasoning none|low|medium|high). So a request to `grok-4` costs this. */
54
- const GROK_43_REDIRECT = { context: 1_000_000, toolCall: true, image: true, cost: { input: 1.25, output: 2.5, cacheRead: 0.2 },
55
- source: "docs.x.ai May-15-2026 retirement: served by grok-4.3, billed at grok-4.3 rates (under 200k context)", checked: "2026-09-04 (fetched)" } as const;
56
-
57
- /** Tiered pricing, by provider and model id. Verified 2026-09-05 against the vendors' own pricing pages.
58
- * Nothing here changes a base price — `catalog.ts` attaches the tier to whatever row it resolved, so a
59
- * models.dev entry keeps its numbers and gains the threshold it has no field for. */
60
- export const PRICE_TIERS: Readonly<Record<string, Readonly<Record<string, PriceTier>>>> = {
61
- // xAI: "requests over 200,000 prompt tokens are billed at the higher rate FOR THE ENTIRE REQUEST"
62
- xai: {
63
- "grok-4.6": { thresholdTokens: 200_000, mode: "per-request", above: { input: 4, output: 12, cacheRead: 1 },
64
- source: "docs.x.ai/developers/pricing — over 200k prompt tokens the whole request bills at the high rate", checked: "2026-09-05 (fetched)" },
65
- "grok-4.3": { thresholdTokens: 200_000, mode: "per-request", above: { input: 2.5, output: 5, cacheRead: 0.4 },
66
- source: "docs.x.ai/developers/pricing — over 200k prompt tokens the whole request bills at the high rate", checked: "2026-09-05 (fetched)" },
67
- },
68
- // OpenAI, developers.openai.com/api/docs/models/gpt-5.4 and /gpt-5.5: "Prompts with >272K input tokens
69
- // are priced at 2x input and 1.5x output for the full session." The multipliers are ASYMMETRIC — input
70
- // doubles, output goes up by half — so these are written out rather than derived from one rate.
71
- // The -pro tiers are NOT given this: their own pages do not carry the sentence, and inventing a
72
- // threshold for a $30/1M model is the expensive kind of guess.
73
- openai: {
74
- "gpt-5.4": { thresholdTokens: 272_000, mode: "per-request", above: { input: 5, output: 22.5, cacheRead: 0.5 },
75
- source: "developers.openai.com/api/docs/models/gpt-5.4 — >272K input tokens: 2x input, 1.5x output for the full session", checked: "2026-09-05 (fetched)" },
76
- "gpt-5.5": { thresholdTokens: 272_000, mode: "per-request", above: { input: 10, output: 45, cacheRead: 1 },
77
- source: "developers.openai.com/api/docs/models/gpt-5.5 — >272K input tokens: 2x input, 1.5x output for the full session", checked: "2026-09-05 (fetched)" },
78
- },
79
- // Google prices by PROMPT SIZE, not by overflow: the page's own columns read "prompts <= 200k tokens"
80
- // and "prompts > 200k tokens" for BOTH input and output, so a 201k prompt bills every token — and that
81
- // request's output — at the upper rate. Same shape as xAI, which is why neither is "marginal".
82
- google: {
83
- "gemini-3.1-pro-preview": { thresholdTokens: 200_000, mode: "per-request", above: { input: 4, output: 18 },
84
- source: "ai.google.dev/gemini-api/docs/pricing — \"$4.00, prompts > 200k tokens\" / \"$18.00, prompts > 200k\"", checked: "2026-09-05 (fetched)" },
85
- "gemini-2.5-pro": { thresholdTokens: 200_000, mode: "per-request", above: { input: 2.5, output: 15 },
86
- source: "ai.google.dev/gemini-api/docs/pricing — \"$2.50, prompts > 200k tokens\" / \"$15.00, prompts > 200k\"", checked: "2026-09-05 (fetched)" },
87
- },
88
- } as const;
89
-
90
- /** A price that is scheduled to change, or a condition the number depends on. Not a tier and not an
91
- * override — just the sentence a reader needs so today's number is not mistaken for a permanent one.
92
- * Attached to a row whatever its source. */
93
- export const PRICE_NOTES: Readonly<Record<string, Readonly<Record<string, string>>>> = {
94
- google: {
95
- "gemini-3.8-flash": "promotional pricing through 2026-12-31: ai.google.dev/gemini-api/docs/pricing says it doubles to $1.50 in / $7.50 out starting 2027-01-01 (checked 2026-09-05)",
96
- },
97
- mistral: {
98
- // Checked against mistral.ai/pricing/api on 2026-09-05: the table publishes ONLY `-latest` aliases
99
- // (mistral-medium-latest $1.5/$7.5, mistral-small-latest $0.15/$0.6, mistral-large-latest $0.5/$1.5).
100
- // No dated id appears — not the snapshot's `mistral-medium-2604`, and not `mistral-medium-3-5` or
101
- // `ministral-8b-2512` either. The prices match, the ids are unverifiable, so nothing is renamed here.
102
- "mistral-medium-latest": "mistral.ai/pricing/api publishes only `-latest` aliases and NO maximum output for any model — the catalog's maxOutput equalling the context window is filler, not a vendor figure (checked 2026-09-05)",
103
- "mistral-medium-2604": "this dated id is not on mistral.ai/pricing/api, which lists only `-latest` aliases; the price matches mistral-medium-latest but the id itself is unverified (checked 2026-09-05)",
104
- },
105
- groq: {
106
- // Not "unverified" — UNPUBLISHED. Groq's docs table says "Contact Sales" for the Llama models and
107
- // console.groq.com/pricing is a 404, so any number here came from a third party, not from Groq.
108
- "llama-3.3-70b-versatile": "Groq does not publish a price for this model — its docs table says \"Contact Sales\" and there is no public pricing page; the catalog's figure is models.dev's, not the vendor's (checked 2026-09-05)",
109
- },
110
- anthropic: {
111
- // true of every Anthropic row; recorded on the one most runs use
112
- "claude-mythos-5-1": "invite-only access (platform.claude.com/docs/en/models/mythos-5-1/overview); outside the Batches 300k beta, which covers Opus 5/4.8/4.7/4.6 and Sonnet 5/4.6 only (checked 2026-09-05)",
113
- "claude-fable-5-1": "outside the Batches 300k beta, which covers Opus 5/4.8/4.7/4.6 and Sonnet 5/4.6 only (checked 2026-09-05)",
114
- "claude-opus-5": "thinking tokens bill as OUTPUT and count inside max_tokens; from Opus 4.5 / Sonnet 4.6 / Fable 5.x earlier thinking blocks are preserved and bill as INPUT on the next turn (platform.claude.com/docs, checked 2026-09-05)",
115
- },
116
- } as const;
117
-
118
- export const LOCAL_MODELS: Readonly<Record<string, Readonly<Record<string, LocalModel>>>> = {
119
- deepseek: {
120
- // api-docs.deepseek.com/quick_start/pricing, fetched 2026-09-05. These three rows OVERRIDE models.dev,
121
- // which carries roughly a third of the vendor's own peak numbers for the same ids (flash 0.14/0.28 vs
122
- // 0.44/1.32). The page is the seller; the snapshot is a mirror that has drifted, so the page wins here
123
- // and says so. Peak rates are used: the page's own off-peak wording is self-contradictory (it labels
124
- // "peak" 01:00-04:00 and 06:00-10:00 UTC, which are its cheap hours elsewhere), so rovecode quotes the
125
- // HIGHER of the two — an estimate that surprises nobody with a bigger bill. Do not build discount logic
126
- // on that wording until the page is unambiguous.
127
- "deepseek-v4-flash": { context: 1_000_000, output: 384_000, reasoning: true, toolCall: true, override: true,
128
- cost: { input: 0.44, output: 1.32, cacheRead: 0.014 },
129
- source: "api-docs.deepseek.com/quick_start/pricing (peak rate — off-peak is half, we do not model time-of-day; models.dev carries ~1/3 of these)", checked: "2026-09-05 (fetched)" },
130
- "deepseek-v4-pro": { context: 1_000_000, output: 384_000, reasoning: true, toolCall: true, override: true,
131
- cost: { input: 1.32, output: 3.96, cacheRead: 0.044 },
132
- source: "api-docs.deepseek.com/quick_start/pricing (peak rate — off-peak is half, we do not model time-of-day; models.dev carries ~1/3 of these)", checked: "2026-09-05 (fetched)" },
133
- "deepseek-v4-flash-vision-exp": { context: 1_000_000, output: 384_000, reasoning: true, toolCall: true, image: true, override: true,
134
- cost: { input: 0.44, output: 1.32, cacheRead: 0.014 },
135
- source: "api-docs.deepseek.com/quick_start/pricing (peak rate — off-peak is half, we do not model time-of-day; models.dev carries ~1/3 of these)", checked: "2026-09-05 (fetched)" },
136
- // The two API aliases. The pricing page lists only the v4-* ids, so nothing here is verified: the
137
- // numbers are the last ones DeepSeek published for them and are marked as such rather than being
138
- // quietly refreshed to a v4 price they may not be billed at.
139
- "deepseek-chat": { context: 128_000, output: 8_000, reasoning: true, toolCall: true, cost: { input: 0.28, output: 0.42, cacheRead: 0.028 }, source: "api-docs.deepseek.com pricing (V3.2 era)", checked: "2026-09-05 (unverified: the alias is not on the pricing page, which lists deepseek-v4-* only)" },
140
- "deepseek-reasoner": { context: 128_000, output: 64_000, reasoning: true, toolCall: true, cost: { input: 0.28, output: 0.42, cacheRead: 0.028 }, source: "api-docs.deepseek.com pricing (V3.2 era)", checked: "2026-09-05 (unverified: not on the pricing page; DeepSeek now exposes thinking as a MODE of the v4 models, not a separate id)" },
141
- },
142
- openai: {
143
- // developers.openai.com/api/docs/pricing, fetched 2026-09-05. Not in the snapshot at all, so this is a
144
- // gap fill, not an override.
145
- "gpt-6-astra": { context: 1_050_000, output: 128_000, reasoning: true, toolCall: true, image: true,
146
- cost: { input: 10, output: 50, cacheRead: 1 },
147
- source: "developers.openai.com/api/docs/pricing", checked: "2026-09-05 (fetched)" },
148
- },
149
- xai: {
150
- "grok-4": { ...GROK_43_REDIRECT, reasoning: true },
151
- "grok-4-0709": { ...GROK_43_REDIRECT, reasoning: true },
152
- "grok-4-fast": { ...GROK_43_REDIRECT, reasoning: true },
153
- "grok-4-fast-reasoning": { ...GROK_43_REDIRECT, reasoning: true },
154
- "grok-4-fast-non-reasoning": { ...GROK_43_REDIRECT, reasoning: false },
155
- "grok-4-1-fast-reasoning": { ...GROK_43_REDIRECT, reasoning: true },
156
- "grok-4-1-fast-non-reasoning": { ...GROK_43_REDIRECT, reasoning: false },
157
- "grok-3": { ...GROK_43_REDIRECT, reasoning: false },
158
- // grok-3-mini: neither on docs.x.ai/docs/models nor in the retirement list on 2026-09-04 — no row, stays unpriced
159
- },
160
- };
@@ -1,421 +0,0 @@
1
- /** Model catalog: capability/pricing lookups backed by the models.dev database (ADR: port #6).
2
- *
3
- * Source of truth is the offline snapshot bundled in @opencode-ai/models (generated from
4
- * https://models.dev/api.json). Live refresh against models.dev itself is entirely OPT-IN
5
- * (only when a `fetchFn` is supplied) and layers on top of — never replaces — the offline
6
- * data, so a fresh `new ModelCatalog()` works fully offline with zero network access. */
7
-
8
- import { readFileSync, writeFileSync, mkdirSync, statSync } from "node:fs";
9
- import { join } from "node:path";
10
- import type { ProviderMap, Model } from "@opencode-ai/models";
11
- import { LOCAL_MODELS, PRICE_NOTES, PRICE_TIERS, type LocalModel, type PriceTier } from "./catalog-local.ts";
12
-
13
- // Loaded lazily on the first lookup() so that importing catalog.ts costs nothing — the TUI paints its
14
- // first frame before buildDef() is called.
15
- //
16
- // It reads src/providers/models-index.json, not @opencode-ai/models/snapshot. Same shape, same source,
17
- // five fields per model instead of the whole models.dev record: the upstream snapshot is 4.26 MB and
18
- // parsing it costs 55 MB resident, which every session paid on its first status update to answer "what
19
- // is this model's window and price". The trimmed index is 1.1 MB. scripts/build-model-index.mjs
20
- // generates it and `--check` fails when it and the installed package disagree, so an upgrade cannot
21
- // leave the catalog quietly describing the previous release.
22
- let _snapshot: ProviderMap | null = null;
23
- function snapshotProviders(): ProviderMap {
24
- if (_snapshot === null) {
25
- // eslint-disable-next-line @typescript-eslint/no-require-imports
26
- _snapshot = (require("./models-index.json") as { providers: ProviderMap }).providers;
27
- }
28
- return _snapshot;
29
- }
30
-
31
- export interface ModelInfo {
32
- provider: string;
33
- model: string;
34
- contextWindow?: number;
35
- maxOutput?: number;
36
- pricing?: { inputPerMTok?: number; outputPerMTok?: number; cacheReadPerMTok?: number; cacheWritePerMTok?: number };
37
- supportsTools?: boolean;
38
- supportsReasoning?: boolean;
39
- /** where the numbers come from: models.dev (snapshot or live) or rovecode's own table (catalog-local.ts) for a
40
- * model the snapshot lacks — `rovecode model show` says which, so a local price is never mistaken for models.dev's */
41
- source?: "models.dev" | "local";
42
- /** local entries only: the vendor page the numbers were read from, and when */
43
- sourceNote?: string;
44
- /** this model's price changes past a prompt size (xAI, Gemini). Attached whatever the row's source —
45
- * models.dev has no field for it. A cost estimator that ignores it under-quotes a long prompt, and for
46
- * `mode: "per-request"` it under-quotes the WHOLE request, not just the overflow. */
47
- tier?: PriceTier;
48
- /** a scheduled change or a condition attached to the price ("doubles on 2027-01-01") */
49
- priceNote?: string;
50
- }
51
-
52
- export interface CatalogOptions {
53
- fetchFn?: typeof fetch | null; // null = offline only; default null (live fetch is OPT-IN)
54
- cacheDir?: string; // when live: cache api.json to <cacheDir>/models.json, ttl 24h
55
- /** the local overlay (default catalog-local.ts LOCAL_MODELS); tests inject their own */
56
- local?: Readonly<Record<string, Readonly<Record<string, LocalModel>>>>;
57
- }
58
-
59
- const CACHE_TTL_MS = 24 * 60 * 60_000;
60
- const CACHE_FILE = "models.json";
61
- const LIVE_URL = "https://models.dev/api.json";
62
-
63
- /**
64
- * rovecode provider id -> models.dev provider key. Derived by loading the offline snapshot
65
- * (node_modules/@opencode-ai/models/dist/snapshot.js) and inspecting Object.keys(providers)
66
- * directly rather than guessing:
67
- * - together -> "togetherai" and fireworks -> "fireworks-ai" (not the bare names)
68
- * - lmstudio -> "lmstudio" DOES exist in the snapshot (3 curated local models), so it maps
69
- * - everything else here is an exact 1:1 id match confirmed present in the snapshot
70
- *
71
- * Deliberately absent (verified NOT a key in the snapshot -> lookup() returns undefined):
72
- * kaesra (rovecode's own proxy brand, not a models.dev provider — its vendor-prefixed model ids
73
- * resolve through VENDOR_PREFIX_MAP below instead), ollama (only "ollama-cloud" exists, not
74
- * bare "ollama"), moondream, vllm.
75
- */
76
- const PROVIDER_MAP: Record<string, string> = {
77
- openai: "openai",
78
- anthropic: "anthropic",
79
- deepseek: "deepseek",
80
- groq: "groq",
81
- openrouter: "openrouter",
82
- lmstudio: "lmstudio",
83
- together: "togetherai",
84
- mistral: "mistral",
85
- cerebras: "cerebras",
86
- fireworks: "fireworks-ai",
87
- perplexity: "perplexity",
88
- xai: "xai",
89
- // not built-in providers, but the ids people give `rovecode provider add` for these vendors' own endpoints
90
- // (Google's OpenAI layer, Z.ai, Moonshot, Alibaba DashScope, MiniMax) — mapped so their models price and
91
- // carry the reasoning flag like the built-ins (2026-09-04: models.dev snapshot 0.0.64 has all five)
92
- google: "google",
93
- gemini: "google",
94
- zai: "zai",
95
- moonshot: "moonshotai",
96
- moonshotai: "moonshotai",
97
- alibaba: "alibaba",
98
- dashscope: "alibaba",
99
- minimax: "minimax",
100
- };
101
-
102
- /**
103
- * HuggingFace-style vendor prefix -> models.dev provider key, for aggregator providers
104
- * (kaesra, or any custom base URL) that serve models under "vendor/model" ids. Lets
105
- * lookup("kaesra", "zai-org/glm-5.3-flash") price against the zai snapshot entry.
106
- * Verified against Object.keys(snapshotProviders): "zai", "deepseek", "moonshotai" all
107
- * exist; there is NO bare "moonshot" key (only moonshotai/moonshotai-cn), hence the
108
- * identity mapping for moonshotai.
109
- */
110
- const VENDOR_PREFIX_MAP: Record<string, string> = {
111
- "zai-org": "zai",
112
- "deepseek-ai": "deepseek",
113
- "moonshotai": "moonshotai",
114
- };
115
-
116
- /** the tiered-price row for a model, if the vendor has one (case-insensitive, like every other lookup) */
117
- function tierFor(providerId: string, modelId: string): PriceTier | undefined {
118
- const table = PRICE_TIERS[providerId.toLowerCase()];
119
- if (!table) return undefined;
120
- const key = Object.keys(table).find((k) => k.toLowerCase() === modelId.toLowerCase());
121
- return key === undefined ? undefined : table[key];
122
- }
123
-
124
- function noteFor(providerId: string, modelId: string): string | undefined {
125
- const table = PRICE_NOTES[providerId.toLowerCase()];
126
- if (!table) return undefined;
127
- const key = Object.keys(table).find((k) => k.toLowerCase() === modelId.toLowerCase());
128
- return key === undefined ? undefined : table[key];
129
- }
130
-
131
- /** The four rates one request is billed at. Same field names as `ModelInfo.pricing`, so a caller can pass
132
- * either where a rate table is wanted. */
133
- export interface Rates { inputPerMTok?: number; outputPerMTok?: number; cacheReadPerMTok?: number; cacheWritePerMTok?: number }
134
-
135
- /** What `promptTokens` actually costs on this model, split the way the vendor bills it. PURE — no I/O, no
136
- * clock, no rounding: a cost function multiplies these and decides its own precision.
137
- *
138
- * Read `promptBase`/`promptAbove` as token counts to multiply by `base`/`above`; `request` is the rate
139
- * table for everything that is not the prompt (output, cache) on this request, because both vendors we
140
- * have select the OUTPUT price by prompt size too. For a flat model the split is trivial (everything in
141
- * `promptBase`) so a caller never needs to branch on whether a tier exists — only on whether it wants the
142
- * detail. */
143
- export interface RateBreakdown {
144
- mode: "flat" | "per-request" | "marginal";
145
- /** true when the prompt crossed a threshold and the upper rates are in play */
146
- tierApplied: boolean;
147
- thresholdTokens?: number;
148
- promptBase: number;
149
- promptAbove: number;
150
- base: Rates;
151
- above?: Rates;
152
- request: Rates;
153
- }
154
-
155
- /** The rate breakdown for a prompt of `promptTokens` on this model. A negative or non-finite count is
156
- * treated as 0 — an estimator must not be able to produce a negative bill. */
157
- export function ratesFor(info: ModelInfo, promptTokens: number): RateBreakdown {
158
- const base: Rates = { ...(info.pricing ?? {}) };
159
- const n = Number.isFinite(promptTokens) && promptTokens > 0 ? Math.floor(promptTokens) : 0;
160
- const t = info.tier;
161
- if (!t || n <= t.thresholdTokens) {
162
- return { mode: t ? t.mode : "flat", tierApplied: false, ...(t ? { thresholdTokens: t.thresholdTokens } : {}),
163
- promptBase: n, promptAbove: 0, base, ...(t ? { above: ratesOf(t) } : {}), request: base };
164
- }
165
- const above = ratesOf(t);
166
- return t.mode === "per-request"
167
- // every token of the prompt, and this request's output, at the upper rate
168
- ? { mode: "per-request", tierApplied: true, thresholdTokens: t.thresholdTokens, promptBase: 0, promptAbove: n, base, above, request: above }
169
- // only the overflow at the upper rate; output follows the upper table, which is the conservative read
170
- : { mode: "marginal", tierApplied: true, thresholdTokens: t.thresholdTokens, promptBase: t.thresholdTokens, promptAbove: n - t.thresholdTokens, base, above, request: above };
171
- }
172
-
173
- function ratesOf(t: PriceTier): Rates {
174
- return { inputPerMTok: t.above.input, outputPerMTok: t.above.output,
175
- ...(t.above.cacheRead !== undefined ? { cacheReadPerMTok: t.above.cacheRead } : {}),
176
- ...(t.above.cacheWrite !== undefined ? { cacheWritePerMTok: t.above.cacheWrite } : {}) };
177
- }
178
-
179
- /** The price lines a surface shows — one place, so `model show`, /cost and the market cannot drift apart.
180
- * Empty when the model is unpriced. */
181
- export function describePricing(info: ModelInfo): string[] {
182
- const p = info.pricing;
183
- if (!p || (p.inputPerMTok === undefined && p.outputPerMTok === undefined)) return [];
184
- const money = (n?: number): string => (n === undefined ? "—" : `$${n}`);
185
- const lines = [`${money(p.inputPerMTok)} in / ${money(p.outputPerMTok)} out per 1M${p.cacheReadPerMTok !== undefined ? ` · cache read ${money(p.cacheReadPerMTok)}` : ""}${p.cacheWritePerMTok !== undefined ? ` · cache write ${money(p.cacheWritePerMTok)}` : ""}`];
186
- if (info.tier) {
187
- const t = info.tier;
188
- lines.push(t.mode === "per-request"
189
- ? `over ${t.thresholdTokens.toLocaleString("en-US")} prompt tokens the WHOLE request bills at ${money(t.above.input)} in / ${money(t.above.output)} out`
190
- : `tokens past ${t.thresholdTokens.toLocaleString("en-US")} bill at ${money(t.above.input)} in / ${money(t.above.output)} out`);
191
- }
192
- if (info.priceNote) lines.push(info.priceNote);
193
- return lines;
194
- }
195
-
196
- /** a local overlay row in the snapshot's shape, so one toModelInfo serves both */
197
- function localAsModel(id: string, m: LocalModel): Model {
198
- return {
199
- id, name: id, description: `${m.source} — checked ${m.checked}`, attachment: false, reasoning: m.reasoning, tool_call: m.toolCall,
200
- release_date: m.checked, modalities: { input: m.image ? ["text", "image"] : ["text"], output: ["text"] },
201
- limit: { context: m.context, ...(m.output !== undefined ? { output: m.output } : {}) },
202
- cost: { input: m.cost.input, output: m.cost.output, ...(m.cost.cacheRead !== undefined ? { cache_read: m.cost.cacheRead } : {}), ...(m.cost.cacheWrite !== undefined ? { cache_write: m.cost.cacheWrite } : {}) },
203
- } as unknown as Model;
204
- }
205
-
206
- function isProviderMap(value: unknown): value is ProviderMap {
207
- if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
208
- for (const entry of Object.values(value as Record<string, unknown>)) {
209
- if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return false;
210
- const models = (entry as { models?: unknown }).models;
211
- if (typeof models !== "object" || models === null || Array.isArray(models)) return false;
212
- }
213
- return true;
214
- }
215
-
216
- /** exact match, then case-insensitive, then match after stripping a single leading "vendor/" prefix. */
217
- function findModelKey(models: Record<string, Model>, modelId: string): string | undefined {
218
- if (modelId in models) return modelId;
219
- const lower = modelId.toLowerCase();
220
- for (const key of Object.keys(models)) {
221
- if (key.toLowerCase() === lower) return key;
222
- }
223
- const slashIndex = modelId.indexOf("/");
224
- if (slashIndex === -1) return undefined;
225
- const stripped = modelId.slice(slashIndex + 1);
226
- if (stripped in models) return stripped;
227
- const strippedLower = stripped.toLowerCase();
228
- for (const key of Object.keys(models)) {
229
- if (key.toLowerCase() === strippedLower) return key;
230
- }
231
- return undefined;
232
- }
233
-
234
- function toModelInfo(providerId: string, modelId: string, model: Model): ModelInfo {
235
- const info: ModelInfo = { provider: providerId, model: modelId };
236
- if (model.limit?.context !== undefined) info.contextWindow = model.limit.context;
237
- if (model.limit?.output !== undefined) info.maxOutput = model.limit.output;
238
- const cost = model.cost;
239
- if (cost) {
240
- const pricing: NonNullable<ModelInfo["pricing"]> = {};
241
- if (cost.input !== undefined) pricing.inputPerMTok = cost.input;
242
- if (cost.output !== undefined) pricing.outputPerMTok = cost.output;
243
- if (cost.cache_read !== undefined) pricing.cacheReadPerMTok = cost.cache_read;
244
- if (cost.cache_write !== undefined) pricing.cacheWritePerMTok = cost.cache_write;
245
- if (Object.keys(pricing).length > 0) info.pricing = pricing;
246
- }
247
- if (model.tool_call !== undefined) info.supportsTools = model.tool_call;
248
- if (model.reasoning !== undefined) info.supportsReasoning = model.reasoning;
249
- return info;
250
- }
251
-
252
- export class ModelCatalog {
253
- private readonly fetchFn: typeof fetch | null;
254
- private readonly cacheDir: string | undefined;
255
- private readonly local: Readonly<Record<string, Readonly<Record<string, LocalModel>>>>;
256
- private liveProviders: ProviderMap | null = null;
257
- private triedDiskCache = false;
258
-
259
- constructor(opts: CatalogOptions = {}) {
260
- this.fetchFn = opts.fetchFn ?? null;
261
- this.cacheDir = opts.cacheDir;
262
- this.local = opts.local ?? LOCAL_MODELS;
263
- }
264
-
265
- /** offline snapshot first; live cache layered on top when enabled.
266
- * Resolution order: (1) the rovecode provider's own models.dev entry (PROVIDER_MAP), then
267
- * (2) the model id's vendor prefix (VENDOR_PREFIX_MAP) with the prefix stripped — the
268
- * path that makes aggregator providers like kaesra (default model zai-org/glm-5.3-flash)
269
- * priceable. The vendor hit reports the vendor as `provider`, naming the pricing source. */
270
- lookup(providerId: string, modelId: string): ModelInfo | undefined {
271
- const hit = this.resolve(providerId, modelId);
272
- if (!hit) return undefined;
273
- const info = toModelInfo(hit.as, hit.key, hit.model);
274
- info.source = hit.local ? "local" : "models.dev";
275
- if (hit.local) info.sourceNote = `${hit.local.source}, checked ${hit.local.checked}`;
276
- const tier = tierFor(providerId, modelId) ?? tierFor(providerId, hit.key);
277
- if (tier) info.tier = tier;
278
- const note = noteFor(providerId, modelId) ?? noteFor(providerId, hit.key);
279
- if (note) info.priceNote = note;
280
- return info;
281
- }
282
-
283
- /** port #34: does the model accept image input? models.dev `modalities.input` (every entry of
284
- * the bundled snapshot carries it — 7479/7479 at @opencode-ai/models 0.0.61). undefined = the
285
- * model is not in the catalog (custom base URLs, local servers, aggregator ids without a
286
- * vendor prefix): callers treat unknown as "send the image" — a wrong guess then fails loudly
287
- * at the provider (HTTP 400 → error turn) instead of silently turning the user's image into
288
- * text. Same resolution order as lookup(). */
289
- supportsImages(providerId: string, modelId: string): boolean | undefined {
290
- const hit = this.resolve(providerId, modelId);
291
- const input: unknown = hit?.model.modalities?.input;
292
- return Array.isArray(input) ? input.includes("image") : undefined;
293
- }
294
-
295
- /** models.dev `reasoning` for a model: true/false, undefined for a model the catalog does not know — the same
296
- * resolution as supportsImages(). Callers treat unknown as "may reason" (wire-select.ts: an unlisted
297
- * Responses-only id must not be sent to a wire that 404s it). */
298
- supportsReasoning(providerId: string, modelId: string): boolean | undefined {
299
- const hit = this.resolve(providerId, modelId);
300
- return typeof hit?.model.reasoning === "boolean" ? hit.model.reasoning : undefined;
301
- }
302
-
303
- /** The catalog entry behind lookup()/supportsImages(): provider key candidates in order, live
304
- * layer over snapshot. `as` = the provider name reported (the vendor for a prefix hit). */
305
- private resolve(providerId: string, modelId: string): { as: string; key: string; model: Model; local?: LocalModel } | undefined {
306
- this.loadDiskCacheOnce();
307
-
308
- const candidates: { key: string; as: string; model: string }[] = [];
309
- const direct = PROVIDER_MAP[providerId];
310
- if (direct) candidates.push({ key: direct, as: providerId, model: modelId });
311
- const slash = modelId.indexOf("/");
312
- if (slash > 0) {
313
- const vendorKey = VENDOR_PREFIX_MAP[modelId.slice(0, slash).toLowerCase()];
314
- if (vendorKey && vendorKey !== direct) candidates.push({ key: vendorKey, as: vendorKey, model: modelId.slice(slash + 1) });
315
- }
316
-
317
- for (const c of candidates) {
318
- const hit = this.findIn(c.key, c.model);
319
- if (hit) return { as: c.as, ...hit };
320
- }
321
- return undefined;
322
- }
323
-
324
- /** one provider key: live layer, then the snapshot, then rovecode's own table (catalog-local.ts). The
325
- * overlay does not shadow a models.dev entry, so a later snapshot that adds a model wins by construction
326
- * — EXCEPT for a row marked `override: true`, which is how a vendor page that disagrees with the
327
- * snapshot wins (today: DeepSeek's v4 family, where models.dev carries about a third of the published
328
- * price). An override says which page and which day in its `source`, and `model show` prints it. */
329
- private findIn(key: string, modelId: string): { key: string; model: Model; local?: LocalModel } | undefined {
330
- const overriding = this.local[key];
331
- if (overriding) {
332
- const found = findModelKey(overriding as unknown as Record<string, Model>, modelId);
333
- const row = found === undefined ? undefined : overriding[found];
334
- if (found !== undefined && row?.override === true) return { key: found, model: localAsModel(found, row), local: row };
335
- }
336
-
337
- const live = this.liveProviders?.[key];
338
- if (live) {
339
- const found = findModelKey(live.models, modelId);
340
- if (found !== undefined) {
341
- const model = live.models[found];
342
- if (model) return { key: found, model };
343
- }
344
- }
345
-
346
- const snap = snapshotProviders()[key];
347
- if (snap) {
348
- const found = findModelKey(snap.models, modelId);
349
- if (found !== undefined) {
350
- const model = snap.models[found];
351
- if (model) return { key: found, model };
352
- }
353
- }
354
-
355
- const table = this.local[key];
356
- if (!table) return undefined;
357
- const found = findModelKey(table as unknown as Record<string, Model>, modelId);
358
- if (found === undefined) return undefined;
359
- const local = table[found]!;
360
- return { key: found, model: localAsModel(found, local), local };
361
- }
362
-
363
- /** live fetch https://models.dev/api.json when fetchFn set; false on failure, never throws. */
364
- async refresh(): Promise<boolean> {
365
- if (!this.fetchFn) return false;
366
- try {
367
- const res = await this.fetchFn(LIVE_URL);
368
- if (!res.ok) return false;
369
- const json: unknown = await res.json();
370
- if (!isProviderMap(json)) return false;
371
- this.liveProviders = json;
372
- this.triedDiskCache = true;
373
- this.writeDiskCache(json);
374
- return true;
375
- } catch {
376
- return false;
377
- }
378
- }
379
-
380
- /** Load a still-fresh (<24h) on-disk cache at most once per instance. Corrupted/stale/missing
381
- * cache is silently ignored — offline snapshot remains the fallback. Only consulted when a
382
- * fetchFn is configured, matching "live fetch is OPT-IN" (a purely-offline catalog never
383
- * touches disk). */
384
- private loadDiskCacheOnce(): void {
385
- if (this.triedDiskCache) return;
386
- this.triedDiskCache = true;
387
- if (!this.fetchFn || !this.cacheDir) return;
388
- try {
389
- const path = join(this.cacheDir, CACHE_FILE);
390
- const age = Date.now() - statSync(path).mtimeMs;
391
- if (age > CACHE_TTL_MS) return;
392
- const parsed: unknown = JSON.parse(readFileSync(path, "utf8"));
393
- if (isProviderMap(parsed)) this.liveProviders = parsed;
394
- } catch {
395
- // missing file, unreadable, or malformed JSON/shape -> treated as "no cache"
396
- }
397
- }
398
-
399
- private writeDiskCache(data: ProviderMap): void {
400
- if (!this.cacheDir) return;
401
- try {
402
- mkdirSync(this.cacheDir, { recursive: true });
403
- writeFileSync(join(this.cacheDir, CACHE_FILE), JSON.stringify(data));
404
- } catch {
405
- // best-effort cache write; a failed write must not fail refresh()
406
- }
407
- }
408
- }
409
-
410
- let offlineCatalog: ModelCatalog | null = null;
411
-
412
- /** Port #34 adapter hook: image capability of a ModelRef from the offline snapshot (no fetchFn →
413
- * never touches network or disk). undefined = unknown model (see ModelCatalog.supportsImages). */
414
- export function supportsImages(ref: { provider: string; model: string }, catalog?: ModelCatalog): boolean | undefined {
415
- return (catalog ?? (offlineCatalog ??= new ModelCatalog())).supportsImages(ref.provider, ref.model);
416
- }
417
-
418
- /** the same hook for the reasoning flag (wire-select.ts): true/false from the offline snapshot, undefined = unknown model */
419
- export function supportsReasoning(ref: { provider: string; model: string }, catalog?: ModelCatalog): boolean | undefined {
420
- return (catalog ?? (offlineCatalog ??= new ModelCatalog())).supportsReasoning(ref.provider, ref.model);
421
- }