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,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
- }