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,397 +0,0 @@
1
- /** PORT #39 — OpenTelemetry span export over the port-#29 hook seam (ADR-010: spans exportable as
2
- * OTel; per-step token/latency/cost accounting). A consumer of core/hooks.ts, nothing more: it is a
3
- * HookSet attached with `hooks.add(set, "otel")`, so the loop/tools/runner stay untouched.
4
- *
5
- * Naming: every span, event and attribute is `rovecode.*` and the resource service.name / scope name is
6
- * `rovecode`. Ported on 2026-09-07 from the upstream harness's telemetry layer, where the same shapes
7
- * carried that harness's own prefix; collectors keyed on those names must be re-pointed (no dual emission).
8
- *
9
- * Shape — ONE trace per run: `rovecode.run` (pre_run → post_run) ⊃ `rovecode.turn` (turn_start → turn_end,
10
- * one per model iteration) ⊃ `rovecode.tool` (pre_tool → post_tool; parent = the turn that ISSUED the
11
- * call). The loop yields turn_end BEFORE it executes the turn's calls (core/loop.ts:232 then :295),
12
- * so a tool span starts after its parent turn ended — OTLP permits a child to outlive its parent and
13
- * the run span brackets everything; keeping the turn = the model step keeps its latency honest.
14
- * Per-step accounting reads the ONE source of usage, the session store (Message.usage/origin — the
15
- * tui/cost.ts + cli/output.ts idiom): each turn span carries the tokens/cost of the assistant message
16
- * it appended, the run span the sums since the run began; latency is the span itself. Compactions
17
- * and calls that never reached pre_tool (policy deny, unknown tool, truncated) become span EVENTS on
18
- * the current turn (`rovecode.compaction`, `rovecode.tool_call_failed`) — a zero-length "step" is noise as a
19
- * span. Attribute policy follows pi (telemetry/README.md:387-389): ids, sizes and outcomes only —
20
- * never the goal, tool args/output, headers or credentials (output_bytes, not output).
21
- *
22
- * Export — hand-encoded OTLP/HTTP JSON (ids as hex, int64 as decimal strings, enums as integers)
23
- * through ONE exporter for three signals (otel-export.ts, port #82): the run's trace is POSTed ONCE at
24
- * post_run to <base>/v1/traces (ROVECODE_OTEL_ENDPOINT with or without the suffix; ROVECODE_OTEL_HEADERS
25
- * "k=v,k2=v2" ride along), then — same run, same order — the CUMULATIVE metrics to <base>/v1/metrics
26
- * when anything was recorded (otel-metrics.ts: rovecode.tokens by type, rovecode.request.duration histogram
27
- * per turn, rovecode.request.retries from the provider retry tap, rovecode.cost_usd when priced; totals since
28
- * construction, never reset) and the run's approval log records to <base>/v1/logs when there were any
29
- * (otel-logs.ts: the EXISTING `approval` member records each card and returns void — the human is still
30
- * asked; allow/deny are observed from the tool events, a card still pending at post_run is `unanswered`).
31
- * Every external lane (TaskInfo.kind "external", observed through TaskManager.subscribe via observeTasks)
32
- * is ONE `rovecode.lane` root span in its own trace, POSTed when the task settles (otel-lanes.ts). Each POST
33
- * is fire-and-forget on a 5s REF'D timer, tracked so flush() and session_close await the outstanding
34
- * ones; post_run never blocks the run and the export path never throws. A failed export is ONE bounded
35
- * warning: direct consumers pass onWarning; the runtime wiring has no write access to hooks.warnings, so
36
- * the set remembers the failure and raises it from its NEXT lifecycle hook (pre_run of the following run,
37
- * or session_close), which the runner's isolation records as exactly one note ("otel: … hook threw: OTLP
38
- * export to … failed …") that every surface already streams. The raise happens after that hook's own
39
- * work. A collector answering 404/405/501 on one signal switches THAT signal off after one note (the
40
- * others continue); 5xx/timeout/network stay per-export warnings.
41
- *
42
- * ZERO OVERHEAD OFF — cli/runtime.ts calls createOtelHooks ONLY when ROVECODE_OTEL_ENDPOINT is set; off,
43
- * no set is attached, the runner's tap short-circuits (hooks.ts:272), TaskManager.subscribe is never
44
- * called by telemetry and the retry tap is one null check. otelDebug.constructed is the spy the off-path
45
- * test pins ("exporter never constructed"); otelDebug.lanesObserved the same for observeTasks.
46
- *
47
- * Run boundaries (fix-wave 4, #39): post_run is the export trigger, and core/loop.ts fires it for a
48
- * run whose CONSUMER closed the generator (serve disconnect, ACP cancel, TUI Esc: hooks.ts
49
- * observer.close) with status "stopped" — cancelled runs export and release their RunState;
50
- * session_close still drains a leftover state (a generator dropped without .return()) as "stopped"
51
- * before its flush. Tool spans are keyed per issuing turn, so a call id a provider reuses across
52
- * turns (the SSE adapter's `tc<idx>` fallback, providers/stream.ts) is one span PER TURN.
53
- * `rovecode.tool_calls` counts ISSUED calls — dispatched (spans) plus never-dispatched (tool_call_failed
54
- * events) — the same count as `rovecode run --output json` toolCalls (cli/output.ts keys per issuing turn
55
- * too, LOW-B), with ONE residual: a run aborted while a call waited between its pre_tool hook and its
56
- * execution (tools.ts:141 returns without an event) has that call as a span but no toolCalls entry —
57
- * the hook side saw pre_tool, the event side saw nothing. A guard-stubbed call (tool events without a
58
- * pre_tool) carries rovecode.failure_reason=loop_guard. An endpoint that is not an absolute http(s) URL
59
- * (`http://`, `host:4318`) disables export with ONE note instead of a 5 s stall per run against host "v1".
60
- *
61
- * Sources (pi @ 853a80d, MIT — naming/shape reference, no code copied; header credit only):
62
- * - packages/agent/src/harness/telemetry.ts:235-256 `pi.harness.run` (outcome attribute; status error
63
- * when the run fails), :327-352 `pi.harness.turn` ("one assistant response and its tool batch",
64
- * parent run), :399-451 `pi.harness.tool` (parents turn|run; `pi.tool.name`, `pi.tool.call_id`,
65
- * `pi.tool.is_error`; status error when execution returns an error) → rovecode.run / rovecode.turn /
66
- * rovecode.tool with rovecode.status, rovecode.turn, rovecode.tool, rovecode.call_id, rovecode.ok.
67
- * - :94-103 `pi.ai.usage.{input,output,cache_read,cache_write}_tokens` + `.cost`, :88-92
68
- * `pi.ai.response.stop_reason`, :55-64 `pi.ai.provider`/`pi.ai.model`, :194 `pi.session.id` →
69
- * rovecode.tokens.{input,output,cacheRead,cacheWrite} (NormalizedUsage spelling), rovecode.cost_usd,
70
- * rovecode.stop_reason, rovecode.model.provider/model, rovecode.session_id.
71
- * Deviations: pi ships no exporter (telemetry/README.md:11 — adapter-owned) and records no timestamps
72
- * (memory.ts:203-218); rovecode ships the OTLP/HTTP exporter and wall-clock ns times. The metric and log
73
- * families' pattern sources are credited in otel-metrics.ts / otel-logs.ts. */
74
-
75
- import type { HookCtx, HookSet, HookToolCall, RunResult } from "../core/hooks.ts";
76
- import type { ApprovalRequest, Message, RunEvent, ToolOutput } from "../core/types.ts";
77
- import { costUsd, type PricingRow } from "../core/usage.ts";
78
- import { ModelCatalog } from "../providers/catalog.ts";
79
- import type { RetryNote } from "../providers/retry.ts";
80
- import { classifyStreamError } from "../providers/router.ts";
81
- import { createOtlpExporter, hex, validEndpoint } from "./otel-export.ts";
82
- import { observeLanes, type LaneSource } from "./otel-lanes.ts";
83
- import { argsFingerprint, createApprovalLedger, encodeLogsRequest, type ApprovalIds } from "./otel-logs.ts";
84
- import { createMetrics, DURATION_BOUNDS, type MetricAttrs } from "./otel-metrics.ts";
85
- import { bool, dbl, encodeTraceRequest, int, str, type OtelSpan, type OtlpValue } from "./otlp.ts";
86
-
87
- // wire types + encoder live in otlp.ts (pure), the pipe in otel-export.ts; re-exported so this stays the module consumers import
88
- export { encodeTraceRequest, unixNano, type OtelSpan, type OtlpKeyValue, type OtlpSpan, type OtlpTraceRequest, type OtlpValue } from "./otlp.ts";
89
- export { DEFAULT_EXPORT_TIMEOUT_MS, OTLP_TRACES_PATH, baseOf, normalizeEndpoint, signalUrl, validEndpoint, type OtlpSignal } from "./otel-export.ts";
90
- export type { OtlpMetricsRequest } from "./otel-metrics.ts";
91
- export type { OtlpLogsRequest } from "./otel-logs.ts";
92
-
93
- /** test spy: exporters constructed / TaskManagers observed in this process — the off-path bar is "never" */
94
- export const otelDebug = { constructed: 0, lanesObserved: 0 };
95
-
96
- /** ModelCatalog.lookup's shape (cli/output.ts PricingSource); tests inject fixed pricing */
97
- export interface PricingSource { lookup(provider: string, model: string): { pricing?: PricingRow } | undefined }
98
-
99
- export interface OtelOptions {
100
- /** collector base URL (…/v1/traces is appended) or the full traces URL */
101
- endpoint: string;
102
- headers?: Record<string, string>;
103
- fetch?: typeof fetch;
104
- /** wall clock in ms (fractions kept); default performance.timeOrigin + performance.now() */
105
- now?: () => number;
106
- /** resource service.name; default "rovecode" */
107
- serviceName?: string;
108
- /** live view of the session store — usage/origin per assistant message */
109
- messages: () => Message[];
110
- /** per-message pricing (Message.origin → row); default: a ModelCatalog built at the first export */
111
- pricing?: PricingSource;
112
- timeoutMs?: number;
113
- /** export failures for direct consumers; absent → raised through the runner (header) */
114
- onWarning?: (note: string) => void;
115
- /** the lane seam (#47 external agentic-CLI lanes): is this `task start` agent value an external lane's adapter
116
- * id? The approval record carries it as rovecode.lane. Default: nothing is a lane — a plain child agent must
117
- * never be tagged as one — until the runtime injects src/lanes/types.ts isAdapterId (a follow-up outside this
118
- * module, so neither side waits on the other to typecheck). */
119
- isLane?: (agent: unknown) => agent is string;
120
- }
121
- export interface OtelHooks extends HookSet {
122
- flush(): Promise<void>;
123
- /** runs recorded but not yet exported — 0 once every run reached post_run (diagnostic seam) */
124
- openRuns(): number;
125
- /** #82: one `rovecode.lane` root span per external lane that ran (TaskManager.subscribe); returns the unsubscribe */
126
- observeTasks(tasks: LaneSource): () => void;
127
- /** #82: the provider retry tap (boot-provider.ts onRetry) → rovecode.request.retries */
128
- recordRetry(note: RetryNote): void;
129
- }
130
-
131
- // ---------- per-run state ----------
132
-
133
- interface RunState {
134
- run: OtelSpan; spans: OtelSpan[]; turn?: OtelSpan; lastTurn?: OtelSpan;
135
- /** tool spans by `<issuing turn spanId>:<callId>` — a reused id is a new span in a new turn */
136
- tools: Map<string, OtelSpan>;
137
- /** #82: spans opened by pre_tool that have not executed yet → their args fingerprint (approval correlation) */
138
- awaiting: Map<OtelSpan, string | null>;
139
- /** issued calls: dispatched (spans) + never-dispatched (tool_call_failed events) */
140
- calls: number;
141
- /** store length at pre_run (run totals) and at the last turn_end (per-turn usage) */
142
- baseline: number; seen: number;
143
- }
144
-
145
- const OK: OtelSpan["status"] = { code: 1 };
146
- const ERROR = (message: string): OtelSpan["status"] => ({ code: 2, message });
147
- const TOKEN_TYPES = ["input", "output", "cacheRead", "cacheWrite"] as const;
148
-
149
- // ---------- env / endpoint / headers ----------
150
-
151
- /** null unless ROVECODE_OTEL_ENDPOINT is set (blank = unset) — the runtime constructs nothing on null */
152
- export function otelOptionsFromEnv(env: Record<string, string | undefined> = process.env): Pick<OtelOptions, "endpoint" | "headers"> | null {
153
- const endpoint = (env["ROVECODE_OTEL_ENDPOINT"] ?? "").trim();
154
- return endpoint ? { endpoint, headers: parseOtelHeaders(env["ROVECODE_OTEL_HEADERS"]) } : null;
155
- }
156
-
157
- /** "k=v,k2=v2" (OTEL_EXPORTER_OTLP_HEADERS shape): first "=" splits, so values may contain "=";
158
- * blank or key-less entries are skipped; values are taken raw (no percent-decoding). */
159
- export function parseOtelHeaders(text: string | undefined): Record<string, string> {
160
- const out: Record<string, string> = {};
161
- for (const part of (text ?? "").split(",")) {
162
- const eq = part.indexOf("=");
163
- if (eq <= 0) continue;
164
- const key = part.slice(0, eq).trim();
165
- if (key) out[key] = part.slice(eq + 1).trim();
166
- }
167
- return out;
168
- }
169
-
170
- // ---------- the hook set ----------
171
-
172
- export function createOtelHooks(opts: OtelOptions): OtelHooks {
173
- otelDebug.constructed++;
174
- const now = opts.now ?? defaultNow;
175
- const service = opts.serviceName ?? "rovecode";
176
- const isLane = opts.isLane ?? ((_agent: unknown): _agent is string => false);
177
- let pricing: PricingSource | undefined = opts.pricing;
178
- const runs = new Map<string, RunState>();
179
- let failed = 0, lastFailure = "";
180
- const warn = (note: string): void => {
181
- if (opts.onWarning) { opts.onWarning(note); return; }
182
- failed++; lastFailure = note;
183
- };
184
- const exporter = createOtlpExporter({ endpoint: opts.endpoint, headers: opts.headers, fetch: opts.fetch, timeoutMs: opts.timeoutMs, warn });
185
- const metrics = createMetrics(now()); // the cumulative window starts at construction (never reset)
186
- const ledger = createApprovalLedger();
187
- const unobserve: (() => void)[] = [];
188
- // ONE note at construction (raised by the first lifecycle hook), then no POSTs
189
- if (!validEndpoint(opts.endpoint)) warn(`ROVECODE_OTEL_ENDPOINT ${JSON.stringify(opts.endpoint)} is not an absolute http(s) URL — OTel export disabled`);
190
-
191
- const stateOf = (ctx: HookCtx): RunState | undefined => (ctx.runId === undefined ? undefined : runs.get(ctx.runId));
192
- const attach = (st: RunState, name: string, parent: OtelSpan): OtelSpan => {
193
- const s: OtelSpan = { traceId: st.run.traceId, spanId: hex(8), parentSpanId: parent.spanId, name, start: now(), attrs: new Map(), events: [], status: { code: 0 } };
194
- st.spans.push(s);
195
- return s;
196
- };
197
- const end = (s: OtelSpan, status: OtelSpan["status"]): void => { if (s.end === undefined) { s.end = now(); s.status = status; } };
198
- const event = (s: OtelSpan, name: string, attrs: [string, OtlpValue][]): void => { s.events.push({ name, time: now(), attrs: new Map(attrs) }); };
199
- const strAttr = (s: OtelSpan, key: string): string | undefined => { const v = s.attrs.get(key); return v && "stringValue" in v ? v.stringValue : undefined; };
200
- /** a call's parent = the turn that issued it (turn_end precedes the turn's tool events — header) */
201
- const issuer = (st: RunState): OtelSpan => st.lastTurn ?? st.run;
202
- const toolKey = (st: RunState, callId: string): string => `${issuer(st).spanId}:${callId}`;
203
- /** the tool span for a call — opened by whichever of pre_tool / tool_execution_start arrives first;
204
- * a span the START event has to open never saw pre_tool: the loop-guard stub path (tools.ts:88-93) */
205
- const openTool = (st: RunState, callId: string, tool?: string, fromStart = false): OtelSpan => {
206
- const key = toolKey(st, callId);
207
- let s = st.tools.get(key);
208
- if (!s) {
209
- s = attach(st, "rovecode.tool", issuer(st));
210
- s.attrs.set("rovecode.call_id", str(callId));
211
- if (fromStart) s.attrs.set("rovecode.failure_reason", str("loop_guard"));
212
- st.tools.set(key, s);
213
- st.calls++;
214
- }
215
- if (tool !== undefined && !s.attrs.has("rovecode.tool")) s.attrs.set("rovecode.tool", str(tool));
216
- return s;
217
- };
218
- const settleTool = (s: OtelSpan, ok: boolean, output: string): void => {
219
- if (s.end !== undefined) return; // post_tool already settled it; tool_execution_end only adds duration
220
- s.attrs.set("rovecode.ok", bool(ok));
221
- s.attrs.set("rovecode.output_bytes", int(Buffer.byteLength(output, "utf8")));
222
- end(s, ok ? OK : ERROR(`tool ${strAttr(s, "rovecode.tool") ?? "call"} failed`));
223
- };
224
- /** tokens summed + cost priced PER MESSAGE at Message.origin (cli/output.ts summarize): any
225
- * usage-bearing message without catalog pricing → cost unknown → attribute omitted, never 0 */
226
- const account = (s: OtelSpan, msgs: Message[]) => {
227
- const u = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
228
- let cost: number | null = 0;
229
- for (const m of msgs) {
230
- if (!m.usage) continue;
231
- const n = { input: m.usage.input, output: m.usage.output, cacheRead: m.usage.cacheRead ?? 0, cacheWrite: m.usage.cacheWrite ?? 0 };
232
- u.input += n.input; u.output += n.output; u.cacheRead += n.cacheRead; u.cacheWrite += n.cacheWrite;
233
- if (cost === null || (n.input === 0 && n.output === 0 && n.cacheRead === 0 && n.cacheWrite === 0)) continue;
234
- const row = m.origin ? (pricing ??= new ModelCatalog()).lookup(m.origin.provider, m.origin.model)?.pricing : undefined;
235
- const c = row ? costUsd(n, row) : undefined;
236
- cost = c === undefined ? null : cost + c;
237
- }
238
- for (const k of TOKEN_TYPES) s.attrs.set(`rovecode.tokens.${k}`, int(u[k]));
239
- if (cost !== null) s.attrs.set("rovecode.cost_usd", dbl(cost));
240
- const served = msgs.at(-1)?.origin; // the model that SERVED (router fallback may differ from the request)
241
- if (served) { s.attrs.set("rovecode.model.provider", str(served.provider)); s.attrs.set("rovecode.model.model", str(served.model)); }
242
- return { u, cost, served };
243
- };
244
- const modelAttrs = (m: { provider: string; model: string } | undefined): MetricAttrs => (m ? [["rovecode.model.provider", str(m.provider)], ["rovecode.model.model", str(m.model)]] : []);
245
- /** #82 metrics: the turn's usage/cost into the cumulative Sums, its latency into the duration histogram */
246
- const feedTurn = (t: OtelSpan, acc: ReturnType<typeof account>, stopReason: string): void => {
247
- const model = modelAttrs(acc.served);
248
- for (const k of TOKEN_TYPES) metrics.addSum("rovecode.tokens", "{token}", [["rovecode.token.type", str(k)], ...model], acc.u[k]);
249
- if (acc.cost !== null) metrics.addSum("rovecode.cost_usd", "USD", model, acc.cost, true);
250
- metrics.recordHistogram("rovecode.request.duration", "ms", DURATION_BOUNDS, [...model, ["rovecode.stop_reason", str(stopReason)]], (t.end ?? now()) - t.start);
251
- };
252
- // --- #82 approval correlation (otel-logs.ts header): pre_tool ran before the card, so the call's span is "awaiting" ---
253
- const awaitingFor = (tool: string): number => { let n = 0; for (const st of runs.values()) for (const s of st.awaiting.keys()) if (strAttr(s, "rovecode.tool") === tool) n++; return n; };
254
- /** the span executed (allow) or was refused (deny): answer the oldest matching card; ids only for a lone candidate */
255
- const settleApproval = (st: RunState, s: OtelSpan, decision: "allow" | "deny"): void => {
256
- const fp = st.awaiting.get(s);
257
- if (fp === undefined) return; // not awaiting: already resolved, or opened by the start event (loop guard)
258
- const tool = strAttr(s, "rovecode.tool");
259
- if (tool === undefined || ledger.pendingFor(tool) === 0) { st.awaiting.delete(s); return; }
260
- const lone = awaitingFor(tool) === 1; // this span is the ONLY same-tool span still awaiting execution
261
- st.awaiting.delete(s);
262
- const ids: ApprovalIds | undefined = lone ? { traceId: s.traceId, spanId: s.spanId, callId: strAttr(s, "rovecode.call_id") ?? "" } : undefined;
263
- ledger.resolve(tool, fp, decision, now(), ids);
264
- };
265
-
266
- // --- export ---
267
- /** deferred failure → thrown from a lifecycle hook → one runner warning (header); no-op with onWarning */
268
- const raise = (): void => {
269
- if (failed === 0) return;
270
- const n = failed; failed = 0;
271
- throw new Error(n === 1 ? lastFailure : `${lastFailure} (${n} exports failed)`);
272
- };
273
- const postMetrics = (): void => { if (metrics.dirty) exporter.post("metrics", JSON.stringify(metrics.encode(service, now()))); };
274
- const postLogs = (): void => { const recs = ledger.take(); if (recs.length > 0) exporter.post("logs", JSON.stringify(encodeLogsRequest(service, recs))); };
275
- /** the run's end: open turn/tool spans close here (status UNSET), totals + status land on the run
276
- * span, ONE traces POST — then metrics (if anything was recorded) and the run's approval records */
277
- const finish = (st: RunState, status: RunResult["status"]): void => {
278
- const t = now();
279
- for (const s of st.spans) if (s.end === undefined && s !== st.run) s.end = t;
280
- st.run.attrs.set("rovecode.status", str(status));
281
- st.run.attrs.set("rovecode.turns", int(st.spans.filter((s) => s.name === "rovecode.turn").length));
282
- st.run.attrs.set("rovecode.tool_calls", int(st.calls));
283
- account(st.run, opts.messages().slice(st.baseline).filter((m) => m.role === "assistant"));
284
- st.run.end = t;
285
- st.run.status = status === "done" || status === "stopped" ? OK : ERROR(status); // budget/error did not complete
286
- exporter.post("traces", JSON.stringify(encodeTraceRequest(service, st.spans)));
287
- ledger.drain(t); // a card still up when the run ended → unanswered (tools.ts:141: no event follows)
288
- postMetrics(); postLogs();
289
- };
290
-
291
- return {
292
- flush: () => exporter.flush(),
293
- openRuns: () => runs.size,
294
- observeTasks(tasks) {
295
- otelDebug.lanesObserved++;
296
- const off = observeLanes(tasks, (s) => exporter.post("traces", JSON.stringify(encodeTraceRequest(service, [s]))), now);
297
- unobserve.push(off);
298
- return off;
299
- },
300
- recordRetry(note) {
301
- const status = classifyStreamError(note.reason).status; // the status only — never the reason text
302
- metrics.addSum("rovecode.request.retries", "{retry}", [...modelAttrs(note.model), ...(status !== undefined ? [["rovecode.retry.status", int(status)] as const] : [])], 1);
303
- },
304
- pre_run(ctx) {
305
- if (ctx.runId === undefined) return;
306
- const run: OtelSpan = { traceId: hex(16), spanId: hex(8), name: "rovecode.run", start: now(), attrs: new Map(), events: [], status: { code: 0 } };
307
- run.attrs.set("rovecode.session_id", str(ctx.sessionId)); run.attrs.set("rovecode.run_id", str(ctx.runId));
308
- const len = opts.messages().length;
309
- runs.set(ctx.runId, { run, spans: [run], tools: new Map(), awaiting: new Map(), calls: 0, baseline: len, seen: len });
310
- raise();
311
- },
312
- on_event(ctx, ev: RunEvent) {
313
- const st = stateOf(ctx);
314
- if (!st) return;
315
- switch (ev.type) {
316
- case "turn_start": {
317
- if (st.turn) end(st.turn, st.turn.status); // defensive: the loop never nests turns
318
- st.turn = st.lastTurn = attach(st, "rovecode.turn", st.run);
319
- st.turn.attrs.set("rovecode.turn", int(ev.turn));
320
- break;
321
- }
322
- case "turn_end": {
323
- const t = st.turn ?? st.lastTurn;
324
- if (!t) break;
325
- t.attrs.set("rovecode.stop_reason", str(ev.stopReason));
326
- const msgs = opts.messages();
327
- const acc = account(t, msgs.slice(st.seen).filter((m) => m.role === "assistant"));
328
- st.seen = msgs.length;
329
- end(t, ev.stopReason === "error" ? ERROR("error") : OK);
330
- feedTurn(t, acc, ev.stopReason);
331
- st.turn = undefined;
332
- break;
333
- }
334
- case "tool_execution_start": settleApproval(st, openTool(st, ev.callId, ev.tool, true), "allow"); break;
335
- case "tool_execution_end": {
336
- const s = openTool(st, ev.callId);
337
- st.awaiting.delete(s);
338
- s.attrs.set("rovecode.duration_ms", int(ev.durationMs)); // the dispatcher's own measure (tools.ts:170)
339
- settleTool(s, ev.ok, ev.output);
340
- break;
341
- }
342
- case "tool_call_failed": {
343
- const s = st.tools.get(toolKey(st, ev.callId));
344
- if (s) {
345
- if (ev.reason === "permission_denied") settleApproval(st, s, "deny"); else st.awaiting.delete(s);
346
- s.attrs.set("rovecode.ok", bool(false)); s.attrs.set("rovecode.failure_reason", str(ev.reason)); end(s, ERROR(ev.reason));
347
- } else { st.calls++; event(st.turn ?? st.lastTurn ?? st.run, "rovecode.tool_call_failed", [["rovecode.call_id", str(ev.callId)], ["rovecode.failure_reason", str(ev.reason)]]); }
348
- break;
349
- }
350
- case "compaction":
351
- event(st.turn ?? st.run, "rovecode.compaction", [
352
- ["rovecode.compaction.strategy", str(ev.strategy)], ...(ev.trigger ? [["rovecode.compaction.trigger", str(ev.trigger)] as [string, OtlpValue]] : []),
353
- ["rovecode.compaction.tokens_before", int(ev.tokensBefore)], ["rovecode.compaction.tokens_after", int(ev.tokensAfter)],
354
- ]);
355
- break;
356
- default: break; // run_start/run_end ride pre_run/post_run; deltas, progress notes and steers are not spans
357
- }
358
- },
359
- pre_tool(ctx, call: HookToolCall) {
360
- const st = stateOf(ctx);
361
- if (!st) return;
362
- const s = openTool(st, call.id, call.tool);
363
- if (s.end === undefined && !s.attrs.has("rovecode.ok")) st.awaiting.set(s, argsFingerprint(call.args)); // awaiting execution until an event says otherwise
364
- },
365
- post_tool(ctx, call: HookToolCall, result: ToolOutput) {
366
- const st = stateOf(ctx);
367
- if (!st) return;
368
- const s = openTool(st, call.id, call.tool);
369
- settleApproval(st, s, "allow"); // the tool ran, so its card (if any) was allowed — post_tool precedes the buffered start event
370
- settleTool(s, result.ok, result.output);
371
- },
372
- /** #82: records the card and returns void — the human (or the next set) still decides; the decision is observed from the events */
373
- approval(ctx, req: ApprovalRequest) {
374
- const args = req.revisedArgs;
375
- const agent = req.tool === "task" && typeof args === "object" && args !== null ? (args as Record<string, unknown>)["agent"] : undefined;
376
- const fp = argsFingerprint(req.args);
377
- const known = fp !== null && [...runs.values()].some((st) => [...st.awaiting].some(([s, f]) => f === fp && strAttr(s, "rovecode.tool") === req.tool));
378
- ledger.request({ tool: req.tool, sessionId: ctx.sessionId, ...(isLane(agent) ? { lane: agent } : {}), fp: known ? fp : null, at: now() });
379
- },
380
- post_run(ctx, result: RunResult) {
381
- const st = stateOf(ctx);
382
- if (!st) return;
383
- runs.delete(ctx.runId!);
384
- finish(st, result.status);
385
- },
386
- async session_close() {
387
- // belt and braces (#39 MED-1): a run whose generator was dropped without .return() never reached
388
- // post_run — export what it recorded as "stopped" (open spans close UNSET) instead of leaking it
389
- for (const [id, st] of runs) { runs.delete(id); finish(st, "stopped"); }
390
- for (const off of unobserve.splice(0)) off();
391
- ledger.drain(now()); postMetrics(); postLogs(); // leftovers: retries after the last run, cards no run closed
392
- await exporter.flush(); raise();
393
- },
394
- };
395
- }
396
-
397
- const defaultNow = (): number => performance.timeOrigin + performance.now();
@@ -1,76 +0,0 @@
1
- /** OTLP/HTTP JSON wire shapes + encoder for the port-#39 exporter (telemetry/otel.ts): the
2
- * ExportTraceServiceRequest subset rovecode emits, the mutable in-memory span record it fills while a
3
- * run is live, and the OTLP/JSON mapping rules — ids as hex, fixed64 times and int64 attributes as
4
- * decimal strings, enums as integers. Pure: no I/O, no clock, no state. Split out of otel.ts at the
5
- * 400-line cap (fix-wave 4); otel.ts re-exports everything here, so its public surface is unchanged.
6
- * `kv`, `resourceAttrs` and `scopeOf` are exported so the metrics (otel-metrics.ts) and logs (otel-logs.ts)
7
- * encoders share ONE resource/attribute encoding with the traces below. */
8
-
9
- import pkg from "../../package.json";
10
-
11
- // ---------- wire types (OTLP/HTTP JSON, ExportTraceServiceRequest) ----------
12
-
13
- export type OtlpValue = { stringValue: string } | { intValue: string } | { doubleValue: number } | { boolValue: boolean };
14
- export interface OtlpKeyValue { key: string; value: OtlpValue }
15
- export interface OtlpSpan {
16
- traceId: string; spanId: string; parentSpanId?: string; name: string; kind: number;
17
- startTimeUnixNano: string; endTimeUnixNano: string;
18
- attributes: OtlpKeyValue[];
19
- events?: { name: string; timeUnixNano: string; attributes: OtlpKeyValue[] }[];
20
- status: { code: number; message?: string };
21
- }
22
- export interface OtlpTraceRequest {
23
- resourceSpans: {
24
- resource: { attributes: OtlpKeyValue[] };
25
- scopeSpans: { scope: { name: string; version?: string }; spans: OtlpSpan[] }[];
26
- }[];
27
- }
28
-
29
- /** one recorded span; mutable until its run is exported (events arrive out of order — otel.ts header) */
30
- export interface OtelSpan {
31
- traceId: string; spanId: string; parentSpanId?: string; name: string;
32
- start: number; end?: number;
33
- attrs: Map<string, OtlpValue>;
34
- events: { name: string; time: number; attrs: Map<string, OtlpValue> }[];
35
- status: { code: 0 | 1 | 2; message?: string };
36
- }
37
-
38
- export const str = (v: string): OtlpValue => ({ stringValue: v });
39
- export const int = (v: number): OtlpValue => ({ intValue: String(Math.trunc(v)) });
40
- export const dbl = (v: number): OtlpValue => ({ doubleValue: v });
41
- export const bool = (v: boolean): OtlpValue => ({ boolValue: v });
42
-
43
- // ---------- encoding ----------
44
-
45
- /** attribute map/tuples → the OTLP KeyValue list (shared by the three signal encoders) */
46
- export const kv = (m: Iterable<[string, OtlpValue]>): OtlpKeyValue[] => [...m].map(([key, value]) => ({ key, value }));
47
- /** the ONE resource every signal carries: service.name (default "rovecode") + service.version */
48
- export const resourceAttrs = (serviceName: string): OtlpKeyValue[] => kv([["service.name", str(serviceName)], ["service.version", str(pkg.version)]]);
49
- /** the ONE instrumentation scope every signal carries */
50
- export const scopeOf = (): { name: string; version: string } => ({ name: "rovecode", version: pkg.version });
51
-
52
- /** OTLP/JSON: ids hex (already), fixed64 times + int64 attrs as decimal strings, enums as integers */
53
- export function encodeTraceRequest(serviceName: string, spans: readonly OtelSpan[]): OtlpTraceRequest {
54
- return {
55
- resourceSpans: [{
56
- resource: { attributes: resourceAttrs(serviceName) },
57
- scopeSpans: [{
58
- scope: scopeOf(),
59
- spans: spans.map((s) => ({
60
- traceId: s.traceId, spanId: s.spanId, ...(s.parentSpanId ? { parentSpanId: s.parentSpanId } : {}),
61
- name: s.name, kind: 1, // SPAN_KIND_INTERNAL
62
- startTimeUnixNano: unixNano(s.start), endTimeUnixNano: unixNano(s.end ?? s.start),
63
- attributes: kv(s.attrs),
64
- ...(s.events.length > 0 ? { events: s.events.map((e) => ({ name: e.name, timeUnixNano: unixNano(e.time), attributes: kv(e.attrs) })) } : {}),
65
- status: s.status.message !== undefined ? { code: s.status.code, message: s.status.message } : { code: s.status.code },
66
- })),
67
- }],
68
- }],
69
- };
70
- }
71
-
72
- /** ms since epoch (fractions allowed) → exact unix-nano decimal string (no float rounding at 1e18) */
73
- export function unixNano(ms: number): string {
74
- const whole = Math.floor(ms);
75
- return (BigInt(whole) * 1_000_000n + BigInt(Math.round((ms - whole) * 1e6))).toString();
76
- }
@@ -1,156 +0,0 @@
1
- /** PORT #33 — ask_user: one clarifying question per call, answered by the interactive surface.
2
- *
3
- * The model asks a question (optional option labels + free text); the surface renders it and
4
- * the user's answer returns as the tool result. Surfaces without a human (run/serve/acp) never
5
- * bind an asker, so the tool fails CLOSED with an actionable error instead of hanging.
6
- *
7
- * Sources (research/source_snapshots):
8
- * - opencode-2026 (MIT, ebece6e) packages/opencode/src/tool/question.ts:22-40 — the tool
9
- * contract: prompts with option labels, answers folded back into the tool output as the
10
- * selected label text (:30-36); packages/schema/src/v1/question.ts:15-28 — Option/Prompt
11
- * shape incl. `custom` (typed answer allowed, default true); question.txt usage notes
12
- * (recommended option first, no catch-all "Other" — the surface adds the free-text entry);
13
- * packages/opencode/src/question/index.ts:29-33 — a dismissed question is an ERROR the
14
- * model sees ("The user dismissed this question"), never a silent empty answer.
15
- * - gemini-cli (Apache-2.0, 0bd1d43) packages/cli/src/config/config.ts:794-803 — headless
16
- * behavior: with no human present ask_user is excluded and ASK_USER decisions translate to
17
- * DENY. rovecode keeps the tool registered on every surface (one registry for all of them) and
18
- * fails closed at execute time with a message that tells the model what to do instead.
19
- * Deviations: ONE question per call (sequential: true) instead of a batch of up to 4; options
20
- * are plain strings — the harness renders into 80 columns, not a web panel. No code copied.
21
- */
22
-
23
- import type { Tool, ToolContext, ToolOutput } from "../core/types.ts";
24
-
25
- /** What the surface must render: the question, optional option labels, and whether a typed
26
- * answer is accepted (default true — opencode `custom`, schema/v1/question.ts:26-28). */
27
- export interface QuestionPrompt {
28
- question: string;
29
- options?: string[];
30
- allowFreeText?: boolean;
31
- }
32
-
33
- /** What the surface hands back: a picked option (index, label) or typed text. */
34
- export interface QuestionAnswer {
35
- choice?: number;
36
- label?: string;
37
- text?: string;
38
- }
39
-
40
- /** The surface seam. Resolves null when the user declines; MUST honor `signal` (dismiss on
41
- * abort) — the tool races it regardless, so a deaf surface cannot pin the run. */
42
- export type AskFn = (q: QuestionPrompt, signal: AbortSignal) => Promise<QuestionAnswer | null>;
43
-
44
- export const MAX_OPTIONS = 8;
45
- export const MAX_OPTION_CHARS = 80;
46
- export const MAX_QUESTION_CHARS = 2000;
47
-
48
- export const ASK_USER_UNAVAILABLE =
49
- "ask_user unavailable: no interactive user in this session (headless run/serve/acp) — proceed with your best judgment or stop";
50
- export const ASK_USER_ABORTED = "ask_user aborted";
51
- export const ASK_USER_DECLINED = "user declined to answer";
52
-
53
- /** Validate + normalize args. A string return is the error message (never throws). */
54
- export function parseQuestion(args: unknown): QuestionPrompt | string {
55
- const a = (args && typeof args === "object" ? args : {}) as Record<string, unknown>;
56
- const question = typeof a.question === "string" ? a.question.trim() : "";
57
- if (question === "") return "question must be a non-empty string";
58
- if (question.length > MAX_QUESTION_CHARS) return `question too long (${question.length} chars, max ${MAX_QUESTION_CHARS})`;
59
- let options: string[] | undefined;
60
- if (a.options !== undefined && a.options !== null) {
61
- if (!Array.isArray(a.options)) return "options must be an array of strings";
62
- if (a.options.length > MAX_OPTIONS) return `too many options (${a.options.length}, max ${MAX_OPTIONS})`;
63
- options = [];
64
- for (const o of a.options) {
65
- if (typeof o !== "string" || o.trim() === "") return "every option must be a non-empty string";
66
- const label = o.trim();
67
- if (label.length > MAX_OPTION_CHARS) return `option too long (${label.length} chars, max ${MAX_OPTION_CHARS}): ${label.slice(0, 40)}…`;
68
- options.push(label);
69
- }
70
- if (options.length === 0) options = undefined;
71
- }
72
- if (a.allowFreeText !== undefined && typeof a.allowFreeText !== "boolean") return "allowFreeText must be a boolean";
73
- const allowFreeText = a.allowFreeText !== false;
74
- if (!options && !allowFreeText) return "nothing to answer with: provide options or allow free text";
75
- return { question, ...(options ? { options } : {}), allowFreeText };
76
- }
77
-
78
- /** Fold the surface's answer into the model-facing result; garbage from a surface is an error. */
79
- function renderAnswer(a: QuestionAnswer, q: QuestionPrompt): ToolOutput {
80
- const text = typeof a.text === "string" ? a.text.trim() : "";
81
- if (text !== "") return { ok: true, output: `answer: ${text}`, data: { text } };
82
- const options = q.options ?? [];
83
- if (typeof a.choice === "number" && Number.isInteger(a.choice) && a.choice >= 0 && a.choice < options.length) {
84
- const label = options[a.choice]!;
85
- return { ok: true, output: `answer: ${label}`, data: { choice: a.choice, label } };
86
- }
87
- return { ok: false, output: "ask_user failed: the surface returned an unusable answer" };
88
- }
89
-
90
- /** Build the tool over a GETTER for the asker, so a surface can bind/unbind after registration
91
- * (runtime.setAskUser — the setBlockStore late-binding idiom). */
92
- export function askUserTool(getAsk: () => AskFn | undefined): Tool {
93
- return {
94
- schema: {
95
- name: "ask_user",
96
- description:
97
- "Ask the user ONE clarifying question and wait for the answer. Use it when instructions are " +
98
- "ambiguous, a decision has real consequences, or you must pick between approaches — do not guess. " +
99
- `Give up to ${MAX_OPTIONS} short options (≤${MAX_OPTION_CHARS} chars each); the user may also type a ` +
100
- "free-text answer unless allowFreeText is false. If you recommend an option, list it first and append " +
101
- "\"(recommended)\"; never add an \"Other\" option — the free-text entry covers it. The result is " +
102
- "`answer: <chosen option or typed text>`. A declined or aborted question, or a session with no " +
103
- "interactive user (headless run/serve/acp), returns an error: then proceed with your best judgment or stop.",
104
- args: {
105
- type: "object",
106
- properties: {
107
- question: { type: "string", description: "the complete question — clear, specific, one decision" },
108
- options: {
109
- type: "array", items: { type: "string" }, maxItems: MAX_OPTIONS,
110
- description: `up to ${MAX_OPTIONS} answer choices, ≤${MAX_OPTION_CHARS} chars each (recommended one first)`,
111
- },
112
- allowFreeText: { type: "boolean", description: "also accept a typed free-form answer (default true)" },
113
- },
114
- required: ["question"],
115
- },
116
- },
117
- // kind "read" → action file.read. Asking the human must never itself need approval: the gated
118
- // default rules (runtime.ts buildCfg) allow file.read, prompt file.write/shell.exec/spawn/
119
- // tool.mcp_call, and deny-default everything else — a "custom" kind would map to
120
- // `tool.ask_user`, which no rule allows (denied), and plan mode's blanket `tool.* deny`
121
- // (modes.ts planModeRules) would lock the question tool out of exactly the mode where
122
- // clarifying questions matter most, while file.read is re-allowed there. Non-mutating, so no
123
- // checkpoint (MUTATING_KINDS = write/execute). With no `path`/`command` arg the policy
124
- // resource is the tool name, so `file.read ask_user` can still be targeted precisely
125
- // (same reasoning as memory/recall.ts:282-288).
126
- kind: "read",
127
- sequential: true, // one question at a time — never interleaved with sibling tool output
128
- async execute(args: unknown, ctx: ToolContext): Promise<ToolOutput> {
129
- const prompt = parseQuestion(args);
130
- if (typeof prompt === "string") return { ok: false, output: `ask_user failed: ${prompt}` };
131
- const ask = getAsk();
132
- if (!ask) return { ok: false, output: ASK_USER_UNAVAILABLE }; // headless: fail closed, say why
133
- if (ctx.signal.aborted) return { ok: false, output: ASK_USER_ABORTED };
134
- let onAbort: (() => void) | undefined;
135
- const aborted = new Promise<"aborted">((res) => {
136
- onAbort = () => res("aborted");
137
- ctx.signal.addEventListener("abort", onAbort, { once: true });
138
- });
139
- try {
140
- // RACED, not merely awaited: a surface that ignores the signal must not pin the run.
141
- // The executor form invokes ask() synchronously and turns a sync throw into a rejection.
142
- const asked = new Promise<QuestionAnswer | null>((res) => res(ask(prompt, ctx.signal)));
143
- const winner = await Promise.race([asked.then((answer) => ({ answer })), aborted]);
144
- // an answer landing in the same tick as the abort is moot — the run is going away
145
- if (winner === "aborted" || ctx.signal.aborted) return { ok: false, output: ASK_USER_ABORTED };
146
- if (winner.answer === null) return { ok: false, output: ASK_USER_DECLINED };
147
- return renderAnswer(winner.answer, prompt);
148
- } catch (e) {
149
- // never throw across the tool seam (ADR-005): surface failures become typed output
150
- return { ok: false, output: `ask_user failed: ${e instanceof Error ? e.message : String(e)}` };
151
- } finally {
152
- if (onAbort) ctx.signal.removeEventListener("abort", onAbort);
153
- }
154
- },
155
- };
156
- }