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,108 +0,0 @@
1
- /** How wrong our token estimate is for a given model, measured rather than assumed.
2
- *
3
- * rovecode budgets with two estimators and neither is the tokenizer that decides: `estimateTokens`
4
- * (chars/4) drives compaction, `countTokens` (o200k, OpenAI's tokenizer) drives the reports. For an
5
- * OpenAI model o200k is the truth by construction. For everyone else it is a stand-in, and the size of
6
- * the error is not a detail — compaction fires on the estimate, so an estimate that reads low compacts
7
- * too late and the provider rejects the request that follows.
8
- *
9
- * The numbers below were measured with `bun scripts/measure-tokenizer.ts`, which sends real samples to
10
- * Anthropic's own `/v1/messages/count_tokens` and divides. Rerun it to check them; that is the point of
11
- * keeping the script. Measured 2026-09-05 over twenty samples — the system prompt, the tool schemas, a
12
- * TypeScript file, English prose, Turkish prose, a JSON tool result, and fourteen market documentation
13
- * bodies:
14
- *
15
- * claude-opus-5, claude-sonnet-5 chars/4 1.33–1.81× o200k 1.43–1.79×
16
- * claude-haiku-4-5 chars/4 1.00–1.46× o200k 1.08–1.29×
17
- *
18
- * The market documents were added after nimbus-24, independently measuring 19 skill bodies, found
19
- * ratios above the ceiling the first six samples produced. They were right, and the reason matters:
20
- * markdown documentation — headings, bullets, fenced code and tables in one file — tokenizes worse
21
- * than either prose or code alone, and it is precisely what lands in a window when a model opens a
22
- * skill. Their worst case (mcp-builder, 1.792) reproduces here at 1.791. A sample set that omits the
23
- * commonest content is not conservative; it is wrong in the expensive direction.
24
- *
25
- * Two things that table settles. The Claude 5 models share one tokenizer — Opus 5 and Sonnet 5 returned
26
- * identical counts on every sample — so the factor belongs to a generation, not to a model name.
27
- * And 4.5 is a different, older one: applying the 5-generation figure to Haiku would over-correct by two
28
- * fifths, which wastes window rather than overflowing it, but is still a wrong number.
29
- *
30
- * Each factor is the **maximum** ratio across the samples, not the mean. The two errors are not
31
- * symmetric: a budget that reads high merely leaves some window unused, while one that reads low sends
32
- * a request the provider refuses and loses the turn. Rounded up to two decimals, and rounded up rather
33
- * than to nearest, for the same reason.
34
- *
35
- * A model nobody has measured gets 1 and says so. A guessed multiplier is worse than none: it moves the
36
- * budget by an amount whose provenance no one can explain, and hides the fact that the number is unknown. */
37
-
38
- export interface TokenScale {
39
- /** multiply an o200k estimate (`countTokens`) by this to approximate what the provider will count;
40
- * 1 when unmeasured */
41
- scale: number;
42
- /** the same for the OTHER estimator, `estimateTokens` (chars/4), which is what compaction and context
43
- * assembly measure with. It is a different approximation with a different error, so it needs its own
44
- * number: on Claude 4.5 the o200k figure is 1.29x while chars/4 needs 1.46x, and using the first to
45
- * size a budget the second is compared against under-corrects by an eighth — which is the direction
46
- * that overflows the window. Measured in the same run, from the same samples. */
47
- charScale: number;
48
- /** true when the number came from a measurement rather than from the default */
49
- measured: boolean;
50
- /** one line for the reader: where the number is from, or that there is none */
51
- note: string;
52
- }
53
-
54
- const UNMEASURED: TokenScale = {
55
- scale: 1,
56
- charScale: 1,
57
- measured: false,
58
- note: "no measurement for this model — the estimate is used as-is; bun scripts/measure-tokenizer.ts measures it",
59
- };
60
-
61
- /** o200k is OpenAI's own tokenizer, so `scale` is exactly 1 — but chars/4 is nobody's tokenizer, and
62
- * the budget is compared against chars/4. Left at 1 because it has not been measured against an
63
- * OpenAI model, and an unmeasured number is what this file refuses to invent. */
64
- const EXACT: TokenScale = {
65
- scale: 1,
66
- charScale: 1,
67
- measured: true,
68
- note: "o200k is this vendor's own tokenizer — the estimate is exact",
69
- };
70
-
71
- interface Row { provider: string; match: RegExp; scale: number; charScale: number; note: string }
72
-
73
- const MEASURED: Row[] = [
74
- {
75
- provider: "anthropic",
76
- // the 5 generation: opus-5, sonnet-5, fable-5.x, and the dated snapshots of each
77
- match: /(opus-5|sonnet-5|fable-5|mythos-5)/,
78
- scale: 1.80,
79
- charScale: 1.82,
80
- note: "measured 2026-09-05 against /v1/messages/count_tokens over 20 samples — o200k reads up to 1.79× low on Claude 5",
81
- },
82
- {
83
- provider: "anthropic",
84
- match: /(haiku-4-5|opus-4-5|sonnet-4-6)/,
85
- scale: 1.29,
86
- charScale: 1.46,
87
- note: "measured 2026-09-05 against /v1/messages/count_tokens over 20 samples — o200k reads up to 1.29× low on Claude 4.5/4.6",
88
- },
89
- ];
90
-
91
- /** OpenAI's own models are counted with OpenAI's own tokenizer; there is nothing to correct. */
92
- const EXACT_PROVIDERS = new Set(["openai"]);
93
-
94
- /** The scale for a model, by provider and model id. Unknown models get 1 with a note saying so —
95
- * never a factor borrowed from a neighbouring model, which would be a guess dressed as a measurement. */
96
- export function tokenScaleFor(ref: { provider: string; model: string }): TokenScale {
97
- const provider = ref.provider.toLowerCase();
98
- const model = ref.model.toLowerCase();
99
- if (EXACT_PROVIDERS.has(provider)) return EXACT;
100
- const row = MEASURED.find((r) => r.provider === provider && r.match.test(model));
101
- return row ? { scale: row.scale, charScale: row.charScale, measured: true, note: row.note } : UNMEASURED;
102
- }
103
-
104
- /** An estimate corrected towards what the provider will count. Rounds up: a token of slack costs
105
- * nothing, a token of shortfall is a rejected request. */
106
- export function scaleEstimate(tokens: number, ref: { provider: string; model: string }): number {
107
- return Math.ceil(tokens * tokenScaleFor(ref).scale);
108
- }
package/src/core/tools.ts DELETED
@@ -1,309 +0,0 @@
1
- /** Tool pipeline: validate → revise (extension hooks) → policy → [pre_tool hook] → approve (the
2
- * ApprovalFn chain: execpolicy → [approval hook] → human, composed in cli/runtime.ts buildCfg) →
3
- * execute → [post_tool hook] (ADR-005; hooks v2 = port #29).
4
- * Tool calls are recorded BEFORE execution; truncated responses fail calls unexecuted. */
5
-
6
- import type {
7
- Tool, ToolContext, ToolOutput, PermissionRule, PermissionDecision,
8
- ApprovalRequest, ToolCallPart, RunEvent,
9
- } from "./types.ts";
10
- import type { ToolGuard } from "./guardrails.ts";
11
- import { formatIssues, validateArgs } from "./validate.ts";
12
- import { cloneForHook, type HookCtx, type HookRunner } from "./hooks.ts";
13
- import { EXTERNAL_ACTION, externalResource, outsidePrompt, outsideWorkspace, toolPath } from "./workspace.ts"; // the workspace boundary (--add-dir roots)
14
-
15
- export interface ExtensionHooks {
16
- /** May revise args; returns revised args (omp revision gate). */
17
- reviseToolArgs?: (tool: string, args: unknown) => Promise<unknown>;
18
- onToolResult?: (tool: string, args: unknown, out: ToolOutput) => Promise<void>;
19
- /** port #29 typed hook set (core/hooks.ts; a HookRunner satisfies this seam): pre_tool / post_tool
20
- * ride dispatch at the seams below, timeout-bounded + isolated by the runner; the approval hook
21
- * rides the ApprovalFn chain instead (HookRunner.approver — after execpolicy, before the human); the
22
- * loop taps the run-level hooks (pre_run / compaction / post_run / on_event) through observer(). */
23
- run?: HookRunner["run"];
24
- observer?: HookRunner["observer"];
25
- }
26
-
27
- /** Output of a tool_call that never executed because the run aborted (port
28
- * #21): a queued sibling in an aborted batch, or an approval answered after
29
- * the abort. Same text loop.ts synthesizes for calls a batch never delivered
30
- * (opencode session/processor.ts:587; codex normalize.rs:51-67). */
31
- export const ABORTED_TOOL_RESULT = "Tool execution aborted";
32
- /** port #29: the small ctx hooks receive — cwd, session, run; no registry/store handles */
33
- const hookCtx = (c: ToolContext): HookCtx =>
34
- ({ cwd: c.cwd, sessionId: c.sessionId, ...(c.runId !== undefined ? { runId: c.runId } : {}) });
35
-
36
- /** Deny-by-default wildcard rules, last match wins (opencode permission.ts:126). */
37
- export function evaluatePermissions(rules: PermissionRule[], action: string, resource: string): PermissionDecision {
38
- let decision: PermissionDecision = { effect: "deny", reason: `no rule allows ${action}` };
39
- for (const r of rules) {
40
- if (matchesGlob(r.action, action) && matchesGlob(r.resource, resource)) {
41
- decision = r.effect === "allow" ? { effect: "allow" }
42
- : r.effect === "deny" ? { effect: "deny", reason: `denied by rule ${r.action} ${r.resource}` }
43
- : { effect: "prompt", prompt: `permission required for ${action} ${resource}` };
44
- }
45
- }
46
- return decision;
47
- }
48
-
49
- function matchesGlob(pattern: string, value: string): boolean {
50
- if (pattern === "*") return true;
51
- const rx = new RegExp("^" + pattern.split("*").map(escapeRx).join(".*") + "$");
52
- return rx.test(value);
53
- }
54
- function escapeRx(s: string): string { return s.replace(/[.+^${}()|[\]\\]/g, "\\$&"); }
55
-
56
- export class ToolRegistry {
57
- private tools = new Map<string, Tool>();
58
- private approvalCache = new Map<string, "once" | "always">();
59
-
60
- register(...tools: Tool[]): void { for (const t of tools) this.tools.set(t.schema.name, t); }
61
- list(): Tool[] { return [...this.tools.values()]; }
62
-
63
- /** ADR-005 ladder. Emits events; never lets tool exceptions escape as control flow. */
64
- async dispatch(
65
- call: ToolCallPart,
66
- ctx: ToolContext,
67
- hooks: ExtensionHooks | undefined,
68
- rules: PermissionRule[],
69
- approve: ((req: ApprovalRequest) => Promise<"once" | "always" | "deny">) | undefined,
70
- emit: (e: RunEvent) => void,
71
- guard?: ToolGuard,
72
- ): Promise<ToolOutput> {
73
- const t0 = Date.now();
74
- const tool = this.tools.get(call.tool);
75
- if (!tool) {
76
- emit({ type: "tool_call_failed", callId: call.id, reason: "not_found", detail: `unknown tool ${call.tool}` });
77
- return { ok: false, output: `Error: unknown tool '${call.tool}'` };
78
- }
79
-
80
- // 1. extension revision
81
- let args = call.args;
82
- if (hooks?.reviseToolArgs) args = await hooks.reviseToolArgs(call.tool, args);
83
-
84
- // 1a. validate against the tool's OWN published schema (ADR-005's first step; core/validate.ts).
85
- // After revision, so a hook that repairs args is judged on what it produced; before the guard and
86
- // policy, because a call that cannot execute should not consume a loop-guard slot, an approval
87
- // card, or the human's attention. The failure shape matches the others here: a tool_call_failed
88
- // event and an ok:false result the model reads and corrects on the next turn.
89
- const issues = validateArgs(tool.schema.args, args);
90
- if (issues.length > 0) {
91
- const detail = formatIssues(call.tool, issues);
92
- emit({ type: "tool_call_failed", callId: call.id, reason: "invalid_args", detail });
93
- return { ok: false, output: detail };
94
- }
95
-
96
- // 1b. loop guard (port #4, hermes): stub repeated identical calls BEFORE the user is
97
- // prompted for them; warn notes ride along on the result
98
- let warnNote: string | undefined;
99
- if (guard) {
100
- const verdict = guard.checkCall(call.tool, args);
101
- if (verdict.action === "stub") {
102
- const note = verdict.note ?? "call blocked by loop guard: identical call repeated too often";
103
- emit({ type: "tool_execution_start", callId: call.id, tool: call.tool, args });
104
- emit({ type: "tool_execution_end", callId: call.id, ok: false, output: note, durationMs: 0 });
105
- return { ok: false, output: note };
106
- }
107
- if (verdict.action === "warn") warnNote = verdict.note;
108
- }
109
-
110
- // 2. policy (deny-default)
111
- const resource = describeResource(tool, args, ctx.cwd);
112
- let decision = evaluatePermissions(rules, actionFor(tool), resource);
113
- // 2a. workspace boundary (core/workspace.ts): a path-declared tool whose CANONICAL resource lies outside the cwd
114
- // is judged a second time under `file.external` with resource `<real containing dir>\*`. Only when some rule
115
- // speaks of that action at all (the gated set says `file.external * prompt`; yolo's `* *` allows it; a bare
116
- // rule list from a test never mentions it → byte-identical behaviour). deny > prompt > allow: a deny names the
117
- // boundary; a prompt REPLACES the primary card with the one that says what happened — the path, the cwd, the
118
- // roots that exist and the remedy — and "always" on it is remembered for the DIRECTORY, not the one file. A
119
- // `--add-dir` root arrives as `allow file.external <root>\*`, so nothing under it ever reaches this card.
120
- let external: string | undefined;
121
- if (decision.effect !== "deny" && declaresPath(tool) && rules.some((r) => matchesGlob(r.action, EXTERNAL_ACTION)) && outsideWorkspace(ctx.cwd, resource)) {
122
- const ext = externalResource(resource);
123
- const verdict = evaluatePermissions(rules, EXTERNAL_ACTION, ext);
124
- if (verdict.effect === "deny") decision = { effect: "deny", reason: `${resource} is outside the workspace ${ctx.cwd} (${verdict.reason})` };
125
- else if (verdict.effect === "prompt") { decision = { effect: "prompt", prompt: outsidePrompt(resource, ctx.cwd, rules) }; external = ext; }
126
- }
127
- if (decision.effect === "deny") {
128
- emit({ type: "tool_call_failed", callId: call.id, reason: "permission_denied", detail: decision.reason });
129
- return { ok: false, output: `Permission denied: ${decision.reason}` };
130
- }
131
-
132
- // 2b. pre_tool hook (port #29) — AFTER policy: the rule deny above is never un-denied and hooks
133
- // never see rule-rejected calls; a hook deny applies in every mode incl. yolo (the user's own
134
- // stricter layer) and takes the policy-deny failure shape with the hook's reason. Hooks see a COPY
135
- // of the args (hooks.ts cloneForHook): mutating it cannot re-aim what policy just evaluated
136
- const veto = await hooks?.run?.("pre_tool", hookCtx(ctx), { id: call.id, tool: call.tool, args: cloneForHook(args) });
137
- if (veto) {
138
- emit({ type: "tool_call_failed", callId: call.id, reason: "permission_denied", detail: veto.deny });
139
- return { ok: false, output: `Permission denied by hook: ${veto.deny}` };
140
- }
141
-
142
- // 3. approval — always resolved against the REVISED args (omp wrapper.ts:205-247). The approver
143
- // IS the chain: execpolicy refinement → approval hook → human (cli/runtime.ts buildCfg), so a
144
- // hook is consulted only where the human would be — never ahead of a forbidden-argv hard stop
145
- if (decision.effect === "prompt") {
146
- // "always" is remembered by what the RULES are about — action + resource — not by the whole
147
- // argument blob. Keyed on the args, an "always" on `write {path, content}` never matched again:
148
- // the next write to the same file carries different content, so the cache missed and the card
149
- // came back. The pair below is the same identity evaluatePermissions just decided on, so
150
- // "always" now means what the card says: this action, on this file / this command / this host.
151
- const key = external !== undefined ? `${EXTERNAL_ACTION}|${external}` : approvalKey(actionFor(tool), resource, tool.schema.name, args);
152
- const cached = this.approvalCache.get(key);
153
- if (!cached) {
154
- if (!approve) {
155
- emit({ type: "tool_call_failed", callId: call.id, reason: "permission_denied", detail: "approval required but no approver connected" });
156
- return { ok: false, output: "Permission denied: approval required, no approver available" };
157
- }
158
- const verdict = await approve({ tool: call.tool, args, revisedArgs: args, reason: decision.prompt, ...(external !== undefined ? { external } : {}) });
159
- if (verdict === "deny") {
160
- emit({ type: "tool_call_failed", callId: call.id, reason: "permission_denied", detail: "user denied" });
161
- return { ok: false, output: "Permission denied by user" };
162
- }
163
- // "once" means once: only "always" verdicts persist across calls
164
- if (verdict === "always") this.approvalCache.set(key, verdict);
165
- }
166
- }
167
-
168
- // 3b. abort re-check (port #21 LOW-1): the approver may answer long after
169
- // the run aborted — the loop has already synthesized ABORTED for this call
170
- // and returned, so executing now would run the tool detached from any run.
171
- // Nothing above touches the workspace; this is the last gate before the
172
- // tool's side effect. (An "always" verdict is still cached: it is the
173
- // user's decision about the tool+args, not about this run.)
174
- if (ctx.signal.aborted) return { ok: false, output: ABORTED_TOOL_RESULT };
175
-
176
- // 4. execute with typed error capture; ctx.onUpdate is wired here so a
177
- // tool's progress notes (MCP onprogress, LSP/checkpoint updates) become
178
- // real tool_execution_update events for ALL tools (port #3 LOW-6)
179
- emit({ type: "tool_execution_start", callId: call.id, tool: call.tool, args });
180
- let out: ToolOutput;
181
- try {
182
- out = await tool.execute(args, { ...ctx, onUpdate: (note) => emit({ type: "tool_execution_update", callId: call.id, note }) });
183
- } catch (e) {
184
- out = { ok: false, output: `Error: ${e instanceof Error ? e.message : String(e)}` };
185
- }
186
- // 4b. loop guard result pass: byte-identical duplicate results become stubs.
187
- // out.ok is threaded through so FAILED results are never stubbed (hermes
188
- // keeps errors verbatim) even when the text dodges the string sniff.
189
- if (guard) {
190
- const r = guard.checkResult(call.tool, args, out.output, out.ok);
191
- if (r.deduped) out = { ...out, output: r.output };
192
- }
193
- if (warnNote) out = { ...out, output: `${out.output}\n\n[loop-guard] ${warnNote}` };
194
- // port #29: post_tool may annotate/replace what the model will see (growth-bounded in the runner);
195
- // tool_execution_end below carries the final text, like the guard's stub/warn rewrites above. The
196
- // hook gets a copy of the result: only its RETURNED {output} counts (an assignment dodges no bound)
197
- const ann = await hooks?.run?.("post_tool", hookCtx(ctx), { id: call.id, tool: call.tool, args: cloneForHook(args) }, { ...out });
198
- if (ann?.output !== undefined) out = { ...out, output: ann.output };
199
- emit({ type: "tool_execution_end", callId: call.id, ok: out.ok, output: out.output, durationMs: Date.now() - t0 });
200
- if (hooks?.onToolResult) await hooks.onToolResult(call.tool, args, out).catch(() => {});
201
- return out;
202
- }
203
-
204
- /** Batch executor: concurrent for parallel-safe tools, sequential otherwise (pi executionMode). */
205
- async dispatchBatch(
206
- calls: ToolCallPart[],
207
- ctx: ToolContext,
208
- hooks: ExtensionHooks | undefined,
209
- rules: PermissionRule[],
210
- approve: ((req: ApprovalRequest) => Promise<"once" | "always" | "deny">) | undefined,
211
- emit: (e: RunEvent) => void,
212
- parallelEnabled: boolean,
213
- guard?: ToolGuard,
214
- ): Promise<Map<string, ToolOutput>> {
215
- const results = new Map<string, ToolOutput>();
216
- const run = async (c: ToolCallPart) => { results.set(c.id, await this.dispatch(c, ctx, hooks, rules, approve, emit, guard)); };
217
- if (!parallelEnabled || calls.some((c) => this.tools.get(c.tool)?.sequential !== false)) {
218
- for (const c of calls) {
219
- // port #21 MED-2: an abort that landed during the previous call must not
220
- // START the next one — nor prompt for it. It gets the aborted synthesis
221
- // HERE: the loop's batch-finally only synthesizes for a batch that never
222
- // settled, and a killed bash settles well inside the loop's grace.
223
- if (ctx.signal.aborted) { results.set(c.id, { ok: false, output: ABORTED_TOOL_RESULT }); continue; }
224
- await run(c);
225
- }
226
- } else {
227
- await Promise.all(calls.map(run));
228
- }
229
- return results;
230
- }
231
- }
232
-
233
- function actionFor(tool: Tool): string {
234
- switch (tool.kind) {
235
- case "read": return "file.read";
236
- case "write": return "file.write";
237
- case "execute": return "shell.exec";
238
- case "spawn": return "spawn";
239
- case "memory": return "memory.write";
240
- case "network": return "net.fetch"; // port #31: outbound requests; resource = URL host
241
- default: return `tool.${tool.schema.name}`;
242
- }
243
- }
244
-
245
- /** Resource for policy rules. An args key is only honored when the tool's
246
- * DECLARED schema has that property: args are not schema-validated before
247
- * policy, so a smuggled key ({query, path:"/tmp/x"} on recall, whose schema
248
- * has no `path`) must not re-aim a tool-targeted deny rule at another
249
- * resource. Policy runs pre-execute, so per-tool arg-stripping can't repair
250
- * this — the gate belongs here.
251
- * For a path-declared tool the resource is the path the tool will actually
252
- * touch: a missing/empty `path` defaults to ctx.cwd and a relative one
253
- * resolves against it (the tools' own resolvePath rule), so neither omitting
254
- * nor relativizing the arg can dodge a path-targeted rule (port #22 MED-4).
255
- * Consequently path resources are ALWAYS absolute, so a permission rule's
256
- * pattern must match the full absolute path (matchesGlob anchors it): an
257
- * absolute path/glob or `*` — a cwd-relative pattern such as
258
- * `deny file.write ".env*"` can never match via dispatch.
259
- * Command resources and the tool-name fallback are untouched. */
260
- /** does the tool's own schema declare a `path` argument? (the boundary applies to those tools only — a smuggled
261
- * `path` on a tool whose schema lacks it never reaches the check, and never reaches the tool's resolvePath either) */
262
- function declaresPath(tool: Tool): boolean {
263
- const props = tool.schema.args["properties"];
264
- return typeof props === "object" && props !== null && "path" in (props as Record<string, unknown>);
265
- }
266
-
267
- export function describeResource(tool: Tool, args: unknown, cwd: string): string {
268
- // a tool that declares its own mode wins: a path/command/url says WHAT is touched, but a tool
269
- // whose modes differ in what they may do has to be able to say WHICH mode (types.ts Tool.resource)
270
- if (typeof tool.resource === "function") return tool.resource(args);
271
- const props = tool.schema.args["properties"];
272
- const declared = (key: string): boolean =>
273
- typeof props === "object" && props !== null && key in (props as Record<string, unknown>);
274
- const a = args && typeof args === "object" ? (args as Record<string, unknown>) : undefined;
275
- if (declared("path")) {
276
- const p = a?.["path"];
277
- if (p === undefined || p === null || p === "") return cwd;
278
- return toolPath(cwd, String(p)); // the ONE spelling (core/workspace.ts): what the tool opens is what is judged here
279
- }
280
- if (a && declared("command") && "command" in a) return String(a["command"]);
281
- if (a && declared("url") && "url" in a) return hostOf(String(a["url"]));
282
- return tool.schema.name;
283
- }
284
-
285
- /** Policy resource for url-declared tools (port #31): the URL's hostname — no
286
- * scheme/port/credentials/path — so `allow net.fetch docs.example.com` and
287
- * `deny net.fetch *` read naturally. The host is CANONICAL (lowercased, trailing
288
- * dot stripped — the form the tool's own SSRF guard checks), so `evil.com.`
289
- * cannot dodge a `deny net.fetch evil.com` rule. The decision covers this host
290
- * only: web_fetch stops at a redirect to a different host and reports the
291
- * target URL, so that host comes back through this gate as its own call.
292
- * Unparseable URLs keep the raw string (the tool rejects them anyway), so `*`
293
- * rules still see a stable resource. */
294
- function hostOf(url: string): string {
295
- try { const h = new URL(url).hostname; return (h.endsWith(".") ? h.slice(0, -1) : h).toLowerCase() || url; } catch { return url; }
296
- }
297
-
298
- /** The identity an "always" verdict is remembered under.
299
- *
300
- * Where the card names a real target — a path, a shell command, a host — that pair IS the decision
301
- * the human made ("always allow writing THIS file"), and it is the same identity the rules evaluate.
302
- * Where the tool declares none of those, describeResource falls back to the tool NAME, and widening
303
- * to it would turn "always" on one `mcp_call` into "always" on every MCP call. So those keep the
304
- * exact-arguments key they always had: the narrow reading is the safe one when the card cannot say
305
- * what the decision is about. */
306
- function approvalKey(action: string, resource: string, toolName: string, args: unknown): string {
307
- const base = action + "|" + resource;
308
- return resource === toolName ? base + "|" + JSON.stringify(args) : base;
309
- }
package/src/core/trust.ts DELETED
@@ -1,104 +0,0 @@
1
- /** Project trust — ONE store and ONE predicate for every file a checkout can use to make rovecode EXECUTE something.
2
- *
3
- * A repository is data from the network. Berkay's 2026-09-04 decision put project MCP files and project plugins behind
4
- * a digest gate: nothing of theirs loads until this machine approved THIS content of THIS file. Three more project files
5
- * had walked in beside them without being asked — found in the 2026-09-07 survey — and this module is where the same
6
- * gate now covers all of them:
7
- * .rovecode/hooks.ts | hooks.js imported IN-PROCESS at boot with the person's privileges (core/hooks.ts) — the worst
8
- * of the five: no spawn to classify, no execpolicy to consult
9
- * .rovecode/sandbox.json the executor rung and the docker image every bash command runs in (core/sandbox-config.ts)
10
- * .rovecode/settings.json when it carries a COMMAND-BEARING key — verify (a shell string), lsp (an argv table),
11
- * notify_command (an argv) — core/settings.ts COMMAND_KEYS, enforced inside loadSettings
12
- * so no consumer can forget
13
- * .rovecode/mcp.json, .mcp.json servers (mcp/config.ts loadMcpConfig, since 2026-09-04)
14
- * .rovecode/plugins/<name> by folder digest (plugins/state.ts, since 2026-09-04)
15
- * Out of scope, on purpose: .rovecode/commands/*.md and .rovecode/agents/*.md are prompt text, not executables — a
16
- * different class of gate (prompt injection), not this one.
17
- *
18
- * The store is plugins/state.ts's `trusted` map in the USER home (~/.rovecode/plugins.json): absolute path → sha256 of
19
- * the bytes the person approved. In the user home so a repo cannot trust itself; a digest so a `git pull` that changes
20
- * the file asks again; one map so `rovecode trust` and `rovecode mcp trust` are two names for the same yes.
21
- *
22
- * Cost: loadSettings runs per boot and per run (verify, lsp) and the gate adds a stat + sha256 per call. Digests and the
23
- * state file are memoised on path + mtimeMs + size, so a hot call is two stats and a Map lookup (measured in
24
- * test/unit/project-trust.test.ts). A rewrite that keeps the size within the same mtime tick is not seen until the next
25
- * tick — acceptable for a file a human edits, and no test writes that fast without changing the size. */
26
-
27
- import { createHash } from "node:crypto";
28
- import { readFileSync, statSync } from "node:fs";
29
- import { loadState, saveState, statePath, trustKey, type PluginState } from "../plugins/state.ts";
30
-
31
- export type TrustStatus = "trusted" | "untrusted" | "absent";
32
-
33
- interface Stamp { mtimeMs: number; size: number }
34
- const same = (a: Stamp, b: Stamp): boolean => a.mtimeMs === b.mtimeMs && a.size === b.size;
35
- const stamp = (path: string): Stamp | undefined => { try { const s = statSync(path); return s.isFile() ? { mtimeMs: s.mtimeMs, size: s.size } : undefined; } catch { return undefined; } };
36
-
37
- const digests = new Map<string, Stamp & { digest: string }>();
38
- /** sha256 (hex) of a file's bytes — the trust key for a project file; undefined when absent or unreadable */
39
- export function fileDigest(file: string): string | undefined {
40
- const st = stamp(file);
41
- if (st === undefined) { digests.delete(file); return undefined; }
42
- const hit = digests.get(file);
43
- if (hit !== undefined && same(hit, st)) return hit.digest;
44
- let digest: string;
45
- try { digest = createHash("sha256").update(readFileSync(file)).digest("hex"); } catch { return undefined; }
46
- digests.set(file, { ...st, digest });
47
- return digest;
48
- }
49
-
50
- const states = new Map<string, Stamp & { state: PluginState }>();
51
- /** the trust store, memoised on the state file's stamp (an absent file is "nothing decided" and is not cached) */
52
- export function trustState(home: string): PluginState {
53
- const path = statePath(home);
54
- const st = stamp(path);
55
- if (st === undefined) { states.delete(path); return loadState(home); }
56
- const hit = states.get(path);
57
- if (hit !== undefined && same(hit, st)) return hit.state;
58
- const state = loadState(home);
59
- states.set(path, { ...st, state });
60
- return state;
61
- }
62
-
63
- /** the predicate the loaders take: is THIS content of THIS file approved on this machine */
64
- export function trustedPredicate(state: PluginState): (file: string, digest: string) => boolean {
65
- return (file, digest) => state.trusted[trustKey(file)] === digest;
66
- }
67
-
68
- export function fileTrustStatus(home: string, file: string): TrustStatus {
69
- const digest = fileDigest(file);
70
- if (digest === undefined) return "absent";
71
- return trustedPredicate(trustState(home))(file, digest) ? "trusted" : "untrusted";
72
- }
73
-
74
- /** the one question every gate asks; an absent file is not trusted (there is nothing to trust) */
75
- export function isTrustedFile(home: string, file: string): boolean { return fileTrustStatus(home, file) === "trusted"; }
76
-
77
- /** record the file's CURRENT bytes as approved; an absent file only loses any stale entry */
78
- export function trustFile(home: string, file: string): { ok: true; digest: string } | { ok: false; reason: string } {
79
- const state = loadState(home);
80
- const digest = fileDigest(file);
81
- if (digest === undefined) { delete state.trusted[trustKey(file)]; saveState(home, state); return { ok: false, reason: `${file}: no such file` }; }
82
- state.trusted[trustKey(file)] = digest;
83
- saveState(home, state);
84
- return { ok: true, digest };
85
- }
86
-
87
- export function untrustFile(home: string, file: string): boolean {
88
- const state = loadState(home);
89
- const had = trustKey(file) in state.trusted;
90
- delete state.trusted[trustKey(file)];
91
- saveState(home, state);
92
- return had;
93
- }
94
-
95
- /** The hint every untrusted-file note ends with. It names BOTH spellings on purpose: the CLI verb and
96
- * the TUI command do the same thing, and a note that names only `rovecode trust` tells a person sitting
97
- * in the TUI to quit their session to answer a question the TUI can ask. Telling someone to leave in
98
- * order to approve something is how a gate gets switched off instead of answered. */
99
- export const TRUST_HINT = "Review: rovecode trust show (in the TUI: /trust show) · approve: rovecode trust (or /trust)";
100
-
101
- /** the one line an untrusted project file gets: the file, what stays off and why, the review and the approval command */
102
- export function untrustedFileNote(file: string, what: string): string {
103
- return `${file}: not trusted on this machine — ${what}. ${TRUST_HINT}`;
104
- }