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,289 +0,0 @@
1
- /**
2
- * Project context inheritance (port #8): auto-import instruction files from
3
- * other coding-agent "harnesses" (Claude, Gemini, Cursor, Copilot, plain
4
- * AGENTS.md) so rovecode projects don't need to duplicate repo conventions.
5
- *
6
- * Pattern + precedence order modeled on oh-my-pi's context-file discovery
7
- * (research/source_snapshots/can1357-oh-my-pi):
8
- * - docs/context-files.md, "Other supported context conventions" and
9
- * "Load order and shadowing" tables — the provider-priority idea
10
- * (higher priority wins at a shared scope) and the per-tool path
11
- * conventions: AGENTS.md, CLAUDE.md / .claude/CLAUDE.md, GEMINI.md,
12
- * .cursor/rules/*.mdc + legacy .cursorrules, .github/copilot-instructions.md.
13
- * - packages/coding-agent/src/discovery/builtin.ts `getAncestorDirs` — the
14
- * cwd-upward ancestor walk with an optional inclusive stop directory.
15
- * - packages/coding-agent/src/discovery/cursor.ts — `.cursor/rules/*.mdc`
16
- * carries MDC frontmatter that must be separated from the rule body.
17
- *
18
- * Discovery + precedence (rovecode's documented spec):
19
- * - Ancestor walk: cwd UPWARD via dirname until parent === current (the
20
- * filesystem root). The walk additionally stops — INCLUSIVELY — at the
21
- * first directory containing `.git` (file or directory; worktrees use a
22
- * file), so a repository never inherits context from outside itself.
23
- * `opts.stopAt` bounds the walk at an explicit dir (also inclusive).
24
- * - Precedence: NEARER directories first (nearest wins); within one
25
- * directory, family order rovecode > agents > claude > gemini > cursor >
26
- * copilot (the harvest list in `buildCandidates`). Earlier position wins
27
- * dedupe and total-cap priority.
28
- * - Shadowing (dedupe by depth): the same relative path (e.g. `AGENTS.md`)
29
- * found in a nearer directory completely shadows the farther one — the
30
- * farther file is never read and never listed.
31
- * - Blank files (empty or whitespace-only, after MDC frontmatter stripping)
32
- * are skipped entirely: no section, no dedupe registration, no shadowing.
33
- * - Byte-identical content across surviving candidates is included once;
34
- * the earlier (higher-precedence) occurrence wins and later duplicates
35
- * never reach `sources` — unlike total-cap drops, which keep a stub.
36
- *
37
- * Budgets (rovecode-specific; OMP has no cap on context-file loading):
38
- * - `maxPerFileChars` caps each file's content. Truncation is fence-safe:
39
- * cut at the last newline inside the window and close an odd ``` fence
40
- * count so the following sections aren't swallowed by an open code block.
41
- * - `maxTotalChars` budgets the FULL rendered section string — the
42
- * `## From <path>` header included, so `text.length` never exceeds it.
43
- * - `maxFiles` bounds how many files are included; further existing
44
- * candidate files are counted in `skippedFiles` but never read.
45
- *
46
- * Freshness: callers snapshot the result once per runtime (cli/runtime.ts)
47
- * so the system prompt stays byte-stable for prompt caching (port #5).
48
- * Mid-session edits to config files are intentionally not picked up —
49
- * restart rovecode (a new runtime) to refresh.
50
- *
51
- * Remaining deliberate deviations from OMP: no provider-priority shadowing
52
- * table (content dedupe + path shadowing instead); `.cursor/rules/*.mdc`
53
- * harvested unconditionally as plain text (frontmatter stripped, never
54
- * parsed/acted on) and restricted to `*.mdc`; the character/file budgets are
55
- * rovecode's own.
56
- */
57
-
58
- import { readFileSync, readdirSync, existsSync } from "node:fs";
59
- import { join, dirname, resolve } from "node:path";
60
-
61
- export interface ContextSource {
62
- /** Display path relative to cwd, "/"-separated with one "../" segment per
63
- * ancestor level — used verbatim in the rendered "## From <path>" header. */
64
- path: string;
65
- family: "rovecode" | "agents" | "claude" | "gemini" | "cursor" | "copilot";
66
- chars: number;
67
- truncated: boolean;
68
- }
69
-
70
- export interface ProjectContext {
71
- text: string;
72
- sources: ContextSource[];
73
- /** Existing candidate files that were NOT read because the `maxFiles`
74
- * bound was already reached (surfaced in /status, never silent). */
75
- skippedFiles: number;
76
- }
77
-
78
- export interface LoadOptions {
79
- /** Per-file cap in characters. Default 8000. */
80
- maxPerFileChars?: number;
81
- /** Total cap on the rendered text (headers included). Default 24000. */
82
- maxTotalChars?: number;
83
- /** Max files included across the walk. Default 24. */
84
- maxFiles?: number;
85
- /** Inclusive upper bound for the ancestor walk (tests/embedders). */
86
- stopAt?: string;
87
- }
88
-
89
- const DEFAULT_MAX_PER_FILE_CHARS = 8000;
90
- const DEFAULT_MAX_TOTAL_CHARS = 24000;
91
- const DEFAULT_MAX_FILES = 24;
92
- const TRUNCATION_MARKER = "…[truncated]";
93
-
94
- type Family = ContextSource["family"];
95
-
96
- interface Candidate {
97
- /** Path relative to its directory, "/"-separated regardless of host OS. */
98
- relPath: string;
99
- family: Family;
100
- /** Strip a leading MDC frontmatter block before treating this as content. */
101
- mdc: boolean;
102
- }
103
-
104
- /** Harvest list for ONE directory, in family precedence order (first =
105
- * highest): rovecode > agents > claude > gemini > cursor > copilot. */
106
- function buildCandidates(dir: string): Candidate[] {
107
- const candidates: Candidate[] = [
108
- { relPath: ".rovecode/ROVECODE.md", family: "rovecode", mdc: false },
109
- { relPath: "ROVECODE.md", family: "rovecode", mdc: false },
110
- { relPath: "AGENTS.md", family: "agents", mdc: false },
111
- { relPath: "CLAUDE.md", family: "claude", mdc: false },
112
- { relPath: ".claude/CLAUDE.md", family: "claude", mdc: false },
113
- { relPath: "GEMINI.md", family: "gemini", mdc: false },
114
- { relPath: ".cursorrules", family: "cursor", mdc: false },
115
- ];
116
- for (const name of listCursorRuleFiles(dir)) {
117
- candidates.push({ relPath: `.cursor/rules/${name}`, family: "cursor", mdc: true });
118
- }
119
- candidates.push({ relPath: ".github/copilot-instructions.md", family: "copilot", mdc: false });
120
- return candidates;
121
- }
122
-
123
- /** cwd upward: dirname until parent === current (filesystem root), stopping
124
- * inclusively at `stopAt` or at the first dir containing `.git` (file or
125
- * directory — git worktrees use a file). OMP `getAncestorDirs` pattern. */
126
- function ancestorDirs(cwd: string, stopAt?: string): string[] {
127
- const dirs: string[] = [];
128
- const stop = stopAt === undefined ? null : resolve(stopAt);
129
- let current = resolve(cwd);
130
- for (;;) {
131
- dirs.push(current);
132
- if (stop !== null && current === stop) break;
133
- if (hasGitMarker(current)) break; // repo root — inclusive, never above
134
- const parent = dirname(current);
135
- if (parent === current) break; // filesystem root
136
- current = parent;
137
- }
138
- return dirs;
139
- }
140
-
141
- /** `.git` presence, or false on any fs error (permissions, bad path, …). */
142
- function hasGitMarker(dir: string): boolean {
143
- try {
144
- return existsSync(join(dir, ".git"));
145
- } catch {
146
- return false;
147
- }
148
- }
149
-
150
- /** `.cursor/rules/*.mdc`, sorted by filename. A missing/unreadable directory
151
- * yields no entries — silent skip, never a throw. */
152
- function listCursorRuleFiles(dir: string): string[] {
153
- try {
154
- const entries = readdirSync(join(dir, ".cursor", "rules"), { withFileTypes: true });
155
- return entries
156
- .filter((e) => e.isFile() && e.name.endsWith(".mdc"))
157
- .map((e) => e.name)
158
- .sort();
159
- } catch {
160
- return [];
161
- }
162
- }
163
-
164
- /** Read a file's content, or null on any failure (missing, unreadable, is a
165
- * directory, …) — every failure mode is a silent skip, never a throw. */
166
- function tryReadFile(absPath: string): string | null {
167
- try {
168
- return readFileSync(absPath, "utf8");
169
- } catch {
170
- return null;
171
- }
172
- }
173
-
174
- /** existsSync that can never throw (used only for `skippedFiles` counting). */
175
- function fileExists(absPath: string): boolean {
176
- try {
177
- return existsSync(absPath);
178
- } catch {
179
- return false;
180
- }
181
- }
182
-
183
- /** Strip a leading `---\n...\n---` MDC frontmatter block, if present. */
184
- function stripMdcFrontmatter(content: string): string {
185
- const lines = content.split("\n");
186
- if ((lines[0] ?? "").trim() !== "---") return content;
187
- let endIdx = -1;
188
- for (let i = 1; i < lines.length; i++) {
189
- if ((lines[i] ?? "").trim() === "---") {
190
- endIdx = i;
191
- break;
192
- }
193
- }
194
- if (endIdx === -1) return content;
195
- return lines.slice(endIdx + 1).join("\n").replace(/^\n+/, "");
196
- }
197
-
198
- /** Fence-safe truncation: cut at the last newline inside the window (whole
199
- * lines only; hard cut when the window has no interior newline), close an
200
- * odd ``` fence count so following sections aren't swallowed by an open
201
- * code block, then append the marker. */
202
- function truncateSafely(full: string, maxChars: number): string {
203
- const window = full.slice(0, maxChars);
204
- const nl = window.lastIndexOf("\n");
205
- const cut = nl > 0 ? window.slice(0, nl) : window;
206
- const fences = cut.split("\n").filter((l) => l.trimStart().startsWith("```")).length;
207
- return fences % 2 === 1 ? `${cut}${TRUNCATION_MARKER}\n\`\`\`` : cut + TRUNCATION_MARKER;
208
- }
209
-
210
- interface KeptFile {
211
- /** cwd-relative display path: "../" per ancestor level + relPath. */
212
- displayPath: string;
213
- family: Family;
214
- /** Content after per-file truncation (includes the marker when truncated). */
215
- content: string;
216
- truncated: boolean;
217
- }
218
-
219
- /**
220
- * Load and merge instruction files from every supported harness convention
221
- * found in `cwd` and its ancestors (see module doc for the walk, precedence,
222
- * shadowing, and budget rules). Deterministic: identical tree contents always
223
- * produce the same `text` and `sources`, in the same order.
224
- */
225
- export function loadProjectContext(cwd: string, opts?: LoadOptions): ProjectContext {
226
- const maxPerFileChars = opts?.maxPerFileChars ?? DEFAULT_MAX_PER_FILE_CHARS;
227
- const maxTotalChars = opts?.maxTotalChars ?? DEFAULT_MAX_TOTAL_CHARS;
228
- const maxFiles = opts?.maxFiles ?? DEFAULT_MAX_FILES;
229
-
230
- const seenContent = new Set<string>();
231
- const shadowed = new Set<string>(); // relPaths claimed by a nearer non-blank file
232
- const kept: KeptFile[] = [];
233
- let skippedFiles = 0;
234
-
235
- const dirs = ancestorDirs(cwd, opts?.stopAt);
236
- for (let depth = 0; depth < dirs.length; depth++) {
237
- const dir = dirs[depth]!;
238
- for (const candidate of buildCandidates(dir)) {
239
- if (shadowed.has(candidate.relPath)) continue; // nearest wins (dedupe by depth)
240
- const absPath = join(dir, candidate.relPath);
241
- if (kept.length >= maxFiles) {
242
- // file-count bound (HIGH-3): count existing candidates, never read them
243
- if (fileExists(absPath)) skippedFiles++;
244
- continue;
245
- }
246
- const raw = tryReadFile(absPath);
247
- if (raw === null) continue; // missing or unreadable — skip silently
248
-
249
- const full = candidate.mdc ? stripMdcFrontmatter(raw) : raw;
250
- if (full.trim() === "") continue; // blank: no section/dedupe/shadow (LOW-6)
251
- shadowed.add(candidate.relPath);
252
-
253
- if (seenContent.has(full)) continue; // byte-identical dupe: earlier one won
254
- seenContent.add(full);
255
-
256
- let content = full;
257
- let truncated = false;
258
- if (full.length > maxPerFileChars) {
259
- content = truncateSafely(full, maxPerFileChars);
260
- truncated = true;
261
- }
262
- kept.push({
263
- displayPath: "../".repeat(depth) + candidate.relPath,
264
- family: candidate.family, content, truncated,
265
- });
266
- }
267
- }
268
-
269
- // Total cap enforced in precedence order over the FULL section string —
270
- // header included, so `text.length <= maxTotalChars` always holds. A file
271
- // that would exceed the budget is dropped from `text` but stays listed in
272
- // `sources` as a chars:0/truncated:true stub so callers can see what was
273
- // cut, and why the surviving text is short.
274
- const sources: ContextSource[] = [];
275
- const sections: string[] = [];
276
- let total = 0;
277
- for (const file of kept) {
278
- const section = `\n\n## From ${file.displayPath}\n${file.content}`;
279
- if (total + section.length <= maxTotalChars) {
280
- sources.push({ path: file.displayPath, family: file.family, chars: file.content.length, truncated: file.truncated });
281
- sections.push(section);
282
- total += section.length;
283
- } else {
284
- sources.push({ path: file.displayPath, family: file.family, chars: 0, truncated: true });
285
- }
286
- }
287
-
288
- return { text: sections.join(""), sources, skippedFiles };
289
- }
@@ -1,228 +0,0 @@
1
- /** What is actually in the context window, item by item — and how far our arithmetic is from what
2
- * the provider says it charged for.
3
- *
4
- * Two numbers exist for every turn and they are NOT the same thing:
5
- * - the ESTIMATE: o200k over every part we are about to send. Ours, synchronous, available before
6
- * a request and for a session that never ran. An estimate for anything that is not an OpenAI
7
- * tokenizer — Anthropic's is not public, and its 4.7-generation tokenizer produces materially
8
- * more tokens for the same text, so the estimate reads LOW there.
9
- * - the REPORTED prompt: what the provider itself counted, i.e. `input + cacheRead + cacheWrite`
10
- * of a turn's usage. Cache reads and writes are part of the prompt the model saw; leaving them
11
- * out is the most common way a context meter reads far too low on an agentic session, where
12
- * almost the whole prompt is a cache read.
13
- * drift() compares them at the same point in the transcript, so "how wrong is our meter for this
14
- * provider" becomes a measured number instead of a belief. Beyond DRIFT_TOLERANCE it is worth
15
- * saying out loud: it means compaction fires at the wrong time.
16
- *
17
- * Nothing here reads the network or the disk, and nothing throws: a transcript with no usage at all
18
- * still produces a report, with `drift` simply absent. */
19
-
20
- import { partsTokenText } from "./loop.ts";
21
- import type { Message, MessagePart } from "./types.ts";
22
- import { tokenScaleFor } from "./token-scale.ts";
23
- import { contextHealth, costUsdTiered, countTokens, type NormalizedUsage, type PricingRow } from "./usage.ts";
24
- import { ratesFor } from "../providers/catalog.ts";
25
- import type { PriceTier } from "../providers/catalog-local.ts";
26
-
27
- /** past this the estimate is misleading enough to name — the compaction trigger reads the estimate */
28
- export const DRIFT_TOLERANCE = 0.05;
29
-
30
- /** the budget for a model whose window we do not know — the old flat value, kept as the fallback */
31
- export const DEFAULT_CONTEXT_BUDGET = 200_000;
32
- /** never plan for less history than this, however small the window says it is */
33
- export const MIN_CONTEXT_BUDGET = 32_000;
34
- /** room left beside the history for the system prompt, the tool schemas and the indexes */
35
- export const PROMPT_OVERHEAD_TOKENS = 24_000;
36
- /** assumed answer room when the catalog states no output limit */
37
- const ASSUMED_OUTPUT = 32_000;
38
-
39
- /** How much history a run may carry before compaction. A flat 200k spends a fifth of a 1M window and
40
- * overflows a 128k one, so it is derived: the window minus what the answer and the fixed prompt need.
41
- * An explicit override wins (ROVECODE_CONTEXT_BUDGET), an unknown window keeps the old default, and a
42
- * window too small to hold the floor gets a proportional share rather than a budget larger than itself. */
43
- export function contextBudgetFor(opts: { window?: number; maxOutput?: number; override?: number; scale?: number }): number {
44
- const { window, maxOutput, override } = opts;
45
- if (override !== undefined && Number.isFinite(override) && override > 0) return Math.floor(override);
46
- if (!window || !Number.isFinite(window) || window <= 0) return DEFAULT_CONTEXT_BUDGET;
47
- const reserve = (Number.isFinite(maxOutput) && (maxOutput ?? 0) > 0 ? (maxOutput as number) : ASSUMED_OUTPUT) + PROMPT_OVERHEAD_TOKENS;
48
- const room = window - reserve;
49
- const raw = room < MIN_CONTEXT_BUDGET ? Math.max(1, Math.floor(window * 0.6)) : Math.floor(room);
50
- // The budget is compared against an estimate, so a model whose tokenizer counts more than the
51
- // estimator must get a smaller budget — dividing here is exactly equivalent to inflating every
52
- // estimate at every call site, and there is one of it. See core/token-scale.ts for the measurements.
53
- const scale = opts.scale !== undefined && Number.isFinite(opts.scale) && opts.scale > 0 ? opts.scale : 1;
54
- return Math.max(1, Math.floor(raw / scale));
55
- }
56
-
57
- export interface ContextSlice {
58
- label: string;
59
- tokens: number;
60
- /** of the estimate, 0..1 — 0 when the estimate is 0 */
61
- share: number;
62
- /** what the reader should know about this row, when a number alone would mislead */
63
- note?: string;
64
- }
65
-
66
- export interface ContextDrift {
67
- /** our estimate of the prompt at the last turn that reported usage */
68
- estimated: number;
69
- /** what that turn says it was given: input + cacheRead + cacheWrite */
70
- reported: number;
71
- /** reported − estimated; positive means we are UNDER-counting the real window */
72
- delta: number;
73
- /** |delta| / reported, 0 when reported is 0 */
74
- fraction: number;
75
- beyondTolerance: boolean;
76
- }
77
-
78
- export interface ContextReport {
79
- model: { provider: string; model: string };
80
- /** the catalog's context window for the current model, when it knows one */
81
- window?: number;
82
- /** o200k over the whole transcript — what the next request would carry */
83
- estimated: number;
84
- /** the estimate corrected towards this model's own tokenizer; equals `estimated` when unmeasured.
85
- * The window rows below are computed from THIS, because it is the number the provider will use. */
86
- corrected: number;
87
- /** the correction that was applied, and where its number came from */
88
- scale: { factor: number; measured: boolean; note: string };
89
- slices: ContextSlice[];
90
- /** window − corrected, floored at 0; absent when the window is unknown */
91
- remaining?: number;
92
- /** corrected / window; absent when the window is unknown */
93
- fraction?: number;
94
- nearLimit?: boolean;
95
- drift?: ContextDrift;
96
- /** summed over every turn that reported usage */
97
- totals: NormalizedUsage;
98
- /** USD over the turns that could be priced, and how many could not */
99
- costUsd?: number;
100
- unpricedTurns: number;
101
- /** images carry tokens we do not estimate — say how many rather than pretend they are free */
102
- images: number;
103
- }
104
-
105
- export interface ReportInput {
106
- messages: readonly Message[];
107
- /** the current model, used for the window and for turns with no origin */
108
- current: { provider: string; model: string };
109
- /** window + pricing for a model, however the caller gets them (catalog, overlay, a test double) */
110
- lookup: (ref: { provider: string; model: string }) => { contextWindow?: number; pricing?: PricingRow; tier?: PriceTier } | undefined;
111
- /** the system prompt that will be sent, when the caller has it */
112
- system?: string;
113
- /** the serialized tool schemas that will be sent, when the caller has them */
114
- toolSchemas?: string;
115
- }
116
-
117
- const tokensOf = (text: string): number => (text ? countTokens(text) : 0);
118
-
119
- function partTokens(parts: readonly MessagePart[], kind: MessagePart["kind"]): number {
120
- const only = parts.filter((p) => p.kind === kind);
121
- return only.length === 0 ? 0 : tokensOf(partsTokenText(only as MessagePart[]));
122
- }
123
-
124
- /** Roles are grouped the way a reader thinks about them, not the way the wire does: what I sent, what
125
- * the model said, what the tools were asked, what they answered. Tool traffic dominates an agentic
126
- * session and hiding it inside "assistant" is what makes a context meter useless. */
127
- export function contextReport(input: ReportInput): ContextReport {
128
- const { messages, current, lookup } = input;
129
- const info = lookup(current);
130
-
131
- const userText = messages.filter((m) => m.role === "user");
132
- const assistantText = messages.filter((m) => m.role === "assistant");
133
- const systemMsgs = messages.filter((m) => m.role === "system");
134
-
135
- const slices: ContextSlice[] = [];
136
- const push = (label: string, tokens: number, note?: string) => {
137
- if (tokens > 0) slices.push({ label, tokens, share: 0, ...(note ? { note } : {}) });
138
- };
139
-
140
- push("system prompt", tokensOf(input.system ?? "") + partTokens(systemMsgs.flatMap((m) => m.parts), "text"));
141
- push("tool schemas", tokensOf(input.toolSchemas ?? ""), input.toolSchemas ? undefined : "not supplied");
142
- push("your messages", partTokens(userText.flatMap((m) => m.parts), "text"));
143
- push("assistant replies", partTokens(assistantText.flatMap((m) => m.parts), "text"));
144
- const allParts = messages.flatMap((m) => m.parts);
145
- push("tool calls", partTokens(allParts, "tool_call"));
146
- push("tool results", partTokens(allParts, "tool_result"));
147
-
148
- const estimated = slices.reduce((n, s) => n + s.tokens, 0);
149
- for (const s of slices) s.share = estimated > 0 ? s.tokens / estimated : 0;
150
-
151
- const totals: NormalizedUsage = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
152
- let cost = 0;
153
- let priced = 0;
154
- let unpricedTurns = 0;
155
- for (const m of messages) {
156
- const u = m.usage;
157
- if (!u) continue;
158
- const n: NormalizedUsage = { input: u.input, output: u.output, cacheRead: u.cacheRead ?? 0, cacheWrite: u.cacheWrite ?? 0 };
159
- totals.input += n.input; totals.output += n.output; totals.cacheRead += n.cacheRead; totals.cacheWrite += n.cacheWrite;
160
- if (n.input === 0 && n.output === 0 && n.cacheRead === 0 && n.cacheWrite === 0) continue;
161
- const info = lookup(m.origin ?? current);
162
- // The prompt this turn carried decides the rate on a tiered model: xAI and Google bill a prompt
163
- // over 200k at the upper rate, and xAI applies it to the whole request. Pricing a tiered turn at
164
- // the base rate printed roughly HALF the real cost, with no caveat saying so — the TUI's /cost had
165
- // this right and this report did not, which is the worst arrangement of the two.
166
- const c = info?.pricing
167
- ? costUsdTiered(n, ratesFor({ ...(m.origin ?? current), pricing: info.pricing, ...(info.tier ? { tier: info.tier } : {}) }, n.input + n.cacheRead + n.cacheWrite))
168
- : undefined;
169
- if (c === undefined) unpricedTurns += 1;
170
- else { cost += c; priced += 1; }
171
- }
172
-
173
- // The window rows are what a reader acts on, so they are computed from the corrected estimate: on
174
- // Claude 5 o200k reads up to 1.58x low, and a meter that says 60% of a window already at 95% is worse
175
- // than no meter. `estimated` stays raw beside it so the correction is visible, never silent.
176
- const sc = tokenScaleFor(current);
177
- const corrected = Math.ceil(estimated * sc.scale);
178
- const report: ContextReport = {
179
- model: current,
180
- estimated,
181
- corrected,
182
- scale: { factor: sc.scale, measured: sc.measured, note: sc.note },
183
- slices,
184
- totals,
185
- unpricedTurns,
186
- images: allParts.filter((p) => p.kind === "image").length,
187
- ...(priced > 0 ? { costUsd: cost } : {}),
188
- };
189
- if (info?.contextWindow) {
190
- const health = contextHealth(corrected, info.contextWindow);
191
- report.window = info.contextWindow;
192
- report.fraction = health.fraction;
193
- report.nearLimit = health.nearLimit;
194
- report.remaining = Math.max(0, info.contextWindow - corrected);
195
- }
196
- // the two rows the provider counted and the transcript never stored — see drift()
197
- const fixedTokens = (slices.find((x) => x.label === "system prompt")?.tokens ?? 0) + (slices.find((x) => x.label === "tool schemas")?.tokens ?? 0);
198
- const d = drift(messages, fixedTokens);
199
- if (d) report.drift = d;
200
- return report;
201
- }
202
-
203
- /** Our estimate against the provider's own count, measured at the last turn that reported one.
204
- * The comparison point matters: a turn's usage describes the prompt BEFORE that turn, so the
205
- * estimate is taken over everything up to it, exclusive. Returns undefined when no turn reported
206
- * a prompt (a fresh session, or a provider that sends no usage).
207
- *
208
- * `fixedTokens` is the system prompt plus the tool schemas. They must be included or the comparison
209
- * is not a comparison: neither is ever stored in a transcript — the system message is appended to the
210
- * wire payload and never to history, and tool schemas are a separate wire field entirely — while the
211
- * provider's `reported` count is of a request that always carried both. Leaving them out made every
212
- * session look like it drifted by roughly the size of the fixed prompt, which on a fresh session is
213
- * most of it, and did so even for OpenAI models where the estimator is exact by construction. That is
214
- * a false signal on the one line whose whole job is to say whether the meter can be trusted. */
215
- export function drift(messages: readonly Message[], fixedTokens = 0): ContextDrift | undefined {
216
- for (let i = messages.length - 1; i >= 0; i--) {
217
- const u = messages[i]?.usage;
218
- if (!u) continue;
219
- const reported = u.input + (u.cacheRead ?? 0) + (u.cacheWrite ?? 0);
220
- if (reported <= 0) continue;
221
- const before = messages.slice(0, i);
222
- const estimated = fixedTokens + (before.length === 0 ? 0 : tokensOf(before.map((m) => partsTokenText(m.parts)).join("\n")));
223
- const delta = reported - estimated;
224
- const fraction = reported > 0 ? Math.abs(delta) / reported : 0;
225
- return { estimated, reported, delta, fraction, beyondTolerance: fraction > DRIFT_TOLERANCE };
226
- }
227
- return undefined;
228
- }
@@ -1,60 +0,0 @@
1
- /** Token accounting + ordered context assembly (ADR-007). */
2
-
3
- export interface ContextChunk {
4
- name: string; // "system" | "skills" | "repo-map" | "files" | "history" | "reminder"
5
- text: string;
6
- /** drop first under pressure; history is compacted not dropped */
7
- priority: number; // higher = keep longer
8
- tokens: number;
9
- }
10
-
11
- /** Cheap estimator: ~4 chars/token, clamped. Accurate enough for budgeting;
12
- * providers report exact usage which we feed back via usage accounting. */
13
- export function estimateTokens(text: string): number {
14
- return Math.ceil(text.length / 4);
15
- }
16
-
17
- export interface AssemblyResult {
18
- chunks: ContextChunk[]; // kept, in prompt order
19
- dropped: ContextChunk[]; // non-history chunks evicted to fit budget (lowest priority first)
20
- totalTokens: number; // tokens of kept chunks
21
- overBudget: boolean; // true when even kept chunks exceed budget
22
- }
23
-
24
- /** Order: system > files > repo-map > skills > history > reminder (aider ChatChunks ordering).
25
- * History is never dropped here (compaction owns it); non-history chunks are
26
- * evicted lowest-priority-first until the budget holds — system last-dropped. */
27
- export function assembleContext(chunks: ContextChunk[], budgetTokens: number): AssemblyResult {
28
- const ordered = [...chunks].sort((a, b) => b.priority - a.priority);
29
- const nonHistory = ordered.filter((c) => c.name !== "history");
30
- const dropped: ContextChunk[] = [];
31
- const kept = [...nonHistory];
32
- const hist = ordered.find((c) => c.name === "history");
33
- const histTokens = hist?.tokens ?? 0;
34
- while (kept.length > 0 && kept.reduce((n, c) => n + c.tokens, 0) + histTokens > budgetTokens) {
35
- // evict lowest-priority kept chunk (tail after priority sort = system last)
36
- const victim = kept.pop()!;
37
- dropped.push(victim);
38
- }
39
- const total = kept.reduce((n, c) => n + c.tokens, 0) + histTokens;
40
- const chunksOut = [...kept]; // prompt order: high priority first
41
- if (hist) chunksOut.push(hist);
42
- return { chunks: chunksOut, dropped, totalTokens: total, overBudget: total > budgetTokens };
43
- }
44
-
45
- /** Head/tail summarization plan (aider history.py:41): keep recent tail under
46
- * half budget, summarize older head. Pure — callers do the LLM summarize. */
47
- export function planCompaction(history: Message4Plan[], budgetTokens: number): { keep: Message4Plan[]; summarize: Message4Plan[] } {
48
- const total = history.reduce((n, m) => n + m.tokens, 0);
49
- if (total <= budgetTokens * 0.8) return { keep: history, summarize: [] };
50
- const half = Math.floor(budgetTokens / 2);
51
- let acc = 0; let cut = history.length;
52
- for (let i = history.length - 1; i >= 0; i--) {
53
- acc += history[i]!.tokens;
54
- if (acc > half) { cut = i + 1; break; }
55
- cut = i;
56
- }
57
- return { keep: history.slice(cut), summarize: history.slice(0, cut) };
58
- }
59
-
60
- export interface Message4Plan { id: string; tokens: number; text: string }