rovecode 0.4.0-beta.2 → 0.4.0

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 (428) hide show
  1. package/README.md +67 -69
  2. package/THIRD_PARTY_NOTICES.md +0 -44
  3. package/bin/rovecode.ts +21 -0
  4. package/package.json +16 -37
  5. package/src/account/keys.ts +97 -0
  6. package/src/account/login.ts +158 -0
  7. package/src/account/provision.ts +47 -0
  8. package/src/account/store.ts +63 -0
  9. package/src/acp/server.ts +373 -0
  10. package/src/cli/account-cmd.ts +116 -0
  11. package/src/cli/connect.ts +244 -0
  12. package/src/cli/context-cmd.ts +199 -0
  13. package/src/cli/dispatch.ts +109 -0
  14. package/src/cli/doctor.ts +324 -0
  15. package/src/cli/export.ts +278 -0
  16. package/src/cli/help.ts +240 -0
  17. package/src/cli/is-tui-invocation.ts +8 -0
  18. package/src/cli/main.ts +599 -0
  19. package/src/cli/market-cmd.ts +658 -0
  20. package/src/cli/mcp-market-cmd.ts +299 -0
  21. package/src/cli/output.ts +382 -0
  22. package/src/cli/repl.ts +172 -0
  23. package/src/cli/resume.ts +32 -0
  24. package/src/cli/run-limits.ts +78 -0
  25. package/src/cli/runtime.ts +792 -0
  26. package/src/cli/setup.ts +187 -0
  27. package/src/cli/update-cmd.ts +78 -0
  28. package/src/cli/workflow-cmd.ts +100 -0
  29. package/src/coding/checkpoints.ts +270 -0
  30. package/src/coding/diff.ts +136 -0
  31. package/src/coding/files.ts +339 -0
  32. package/src/coding/hashline.ts +319 -0
  33. package/src/coding/lsp.ts +406 -0
  34. package/src/coding/repomap-cache.ts +99 -0
  35. package/src/coding/repomap-files.ts +110 -0
  36. package/src/coding/repomap.ts +392 -0
  37. package/src/core/compaction.ts +399 -0
  38. package/src/core/config.ts +289 -0
  39. package/src/core/context-report.ts +228 -0
  40. package/src/core/context.ts +60 -0
  41. package/src/core/count-remote.ts +107 -0
  42. package/src/core/execpolicy-rules.ts +196 -0
  43. package/src/core/execpolicy.ts +385 -0
  44. package/src/core/executor.ts +397 -0
  45. package/src/core/guardrails.ts +400 -0
  46. package/src/core/hooks.ts +398 -0
  47. package/src/core/images.ts +230 -0
  48. package/src/core/intro.ts +236 -0
  49. package/src/core/loop.ts +621 -0
  50. package/src/core/modes.ts +372 -0
  51. package/src/core/orchestrator.ts +207 -0
  52. package/src/core/reflection.ts +165 -0
  53. package/src/core/sandbox-config.ts +167 -0
  54. package/src/core/session-images.ts +73 -0
  55. package/src/core/session.ts +398 -0
  56. package/src/core/settings.ts +98 -0
  57. package/src/core/stuck-detector.ts +273 -0
  58. package/src/core/tasks.ts +374 -0
  59. package/src/core/token-scale.ts +108 -0
  60. package/src/core/tool-output-budget.ts +166 -0
  61. package/src/core/tools.ts +288 -0
  62. package/src/core/types.ts +330 -0
  63. package/src/core/update-check.ts +171 -0
  64. package/src/core/update.ts +158 -0
  65. package/src/core/usage.ts +204 -0
  66. package/src/core/validate.ts +121 -0
  67. package/src/core/verify-gate.ts +159 -0
  68. package/src/core/verify.ts +237 -0
  69. package/src/core/voice.ts +158 -0
  70. package/src/core/win-job.ts +183 -0
  71. package/src/design/audit.ts +797 -0
  72. package/src/design/direction.ts +190 -0
  73. package/src/design/rules.ts +157 -0
  74. package/src/eval/bench.ts +150 -0
  75. package/src/eval/gauntlet-runner.ts +218 -0
  76. package/src/eval/gauntlet.ts +226 -0
  77. package/src/eval/grader.ts +186 -0
  78. package/src/eval/record.ts +202 -0
  79. package/src/eval/redact.ts +141 -0
  80. package/src/eval/replay.ts +147 -0
  81. package/src/eval/trajectory.ts +373 -0
  82. package/src/index.ts +17 -0
  83. package/src/market/catalogs/mcp-docs.json +111 -0
  84. package/src/market/catalogs/plugins.json +111 -0
  85. package/src/market/catalogs/skills.json +478 -0
  86. package/src/market/clone.ts +72 -0
  87. package/src/market/context-cost.ts +121 -0
  88. package/src/market/digest.ts +106 -0
  89. package/src/market/index.ts +22 -0
  90. package/src/market/install.ts +578 -0
  91. package/src/market/manifest.ts +187 -0
  92. package/src/market/prereq.ts +145 -0
  93. package/src/market/registry.ts +363 -0
  94. package/src/market/resolve.ts +111 -0
  95. package/src/market/types.ts +236 -0
  96. package/src/market/validate.ts +227 -0
  97. package/src/mcp/client.ts +431 -0
  98. package/src/mcp/config.ts +239 -0
  99. package/src/mcp/local-package.ts +211 -0
  100. package/src/mcp/market-catalog.ts +84 -0
  101. package/src/mcp/market-install.ts +289 -0
  102. package/src/mcp/market.ts +0 -0
  103. package/src/mcp/tools.ts +131 -0
  104. package/src/mcp/trust.ts +49 -0
  105. package/src/memory/blocks.ts +175 -0
  106. package/src/memory/recall.ts +355 -0
  107. package/src/memory/store.ts +105 -0
  108. package/src/memory/tools.ts +99 -0
  109. package/src/plugins/cli.ts +123 -0
  110. package/src/plugins/discover.ts +108 -0
  111. package/src/plugins/index.ts +50 -0
  112. package/src/plugins/init.ts +140 -0
  113. package/src/plugins/install.ts +184 -0
  114. package/src/plugins/load.ts +149 -0
  115. package/src/plugins/manifest.ts +106 -0
  116. package/src/plugins/state.ts +83 -0
  117. package/src/providers/auth.ts +293 -0
  118. package/src/providers/cache.ts +223 -0
  119. package/src/providers/catalog-local.ts +160 -0
  120. package/src/providers/catalog.ts +408 -0
  121. package/src/providers/middleware-context.ts +86 -0
  122. package/src/providers/middleware.ts +373 -0
  123. package/src/providers/profile-glm53.ts +111 -0
  124. package/src/providers/profile-sonnet5-persona.ts +65 -0
  125. package/src/providers/profile-sonnet5-voice.ts +23 -0
  126. package/src/providers/profiles.ts +156 -0
  127. package/src/providers/provider-config.ts +311 -0
  128. package/src/providers/registry.ts +302 -0
  129. package/src/providers/response-validation.ts +80 -0
  130. package/src/providers/retry.ts +234 -0
  131. package/src/providers/router.ts +294 -0
  132. package/src/providers/sse.ts +26 -0
  133. package/src/providers/stream-errors.ts +117 -0
  134. package/src/providers/stream.ts +569 -0
  135. package/src/providers/thinking.ts +189 -0
  136. package/src/providers/wire-messages.ts +129 -0
  137. package/src/sdk/client.ts +225 -0
  138. package/src/sdk/index.ts +3 -0
  139. package/src/server/dashboard.ts +144 -0
  140. package/src/server/http.ts +343 -0
  141. package/src/server/openapi.ts +246 -0
  142. package/src/sextant/card-hits.ts +102 -0
  143. package/src/sextant/card-keys.ts +55 -0
  144. package/src/sextant/context-source.ts +157 -0
  145. package/src/sextant/draw-agents.ts +273 -0
  146. package/src/sextant/draw-code.ts +388 -0
  147. package/src/sextant/draw-context.ts +222 -0
  148. package/src/sextant/draw-frame.ts +164 -0
  149. package/src/sextant/draw-market.ts +573 -0
  150. package/src/sextant/draw-messages.ts +386 -0
  151. package/src/sextant/draw-pet.ts +230 -0
  152. package/src/sextant/draw-plan.ts +159 -0
  153. package/src/sextant/draw-tabs.ts +85 -0
  154. package/src/sextant/draw-util.ts +65 -0
  155. package/src/sextant/engine.ts +230 -0
  156. package/src/sextant/frame-hits.ts +25 -0
  157. package/src/sextant/frame.ts +101 -0
  158. package/src/sextant/git-status.ts +197 -0
  159. package/src/sextant/grid.ts +59 -0
  160. package/src/sextant/input.ts +119 -0
  161. package/src/sextant/keys.ts +488 -0
  162. package/src/sextant/layout.ts +86 -0
  163. package/src/sextant/local-commands.ts +156 -0
  164. package/src/sextant/market-source.ts +287 -0
  165. package/src/sextant/mentions.ts +141 -0
  166. package/src/sextant/message-hits.ts +26 -0
  167. package/src/sextant/model.ts +387 -0
  168. package/src/sextant/overlays.ts +451 -0
  169. package/src/sextant/panel-hits.ts +38 -0
  170. package/src/sextant/pet.ts +399 -0
  171. package/src/sextant/screen.ts +324 -0
  172. package/src/sextant/scroll-hits.ts +66 -0
  173. package/src/sextant/scrollbar.ts +82 -0
  174. package/src/sextant/selection.ts +123 -0
  175. package/src/sextant/sextant-bridge.ts +174 -0
  176. package/src/sextant/sextant-cards.ts +142 -0
  177. package/src/sextant/sextant-diff-base.ts +63 -0
  178. package/src/sextant/sextant-files.ts +154 -0
  179. package/src/sextant/sextant-frame-loop.ts +314 -0
  180. package/src/sextant/sextant-renderer.ts +478 -0
  181. package/src/sextant/sextant-repo.ts +131 -0
  182. package/src/sextant/theme.ts +66 -0
  183. package/src/sextant/tool-rows.ts +189 -0
  184. package/src/sextant/types.ts +473 -0
  185. package/src/skills/index.ts +306 -0
  186. package/src/skills/tools.ts +69 -0
  187. package/src/skills/versioned.ts +227 -0
  188. package/src/telemetry/otel.ts +353 -0
  189. package/src/telemetry/otlp.ts +68 -0
  190. package/src/tools/ask-user.ts +156 -0
  191. package/src/tools/design.ts +151 -0
  192. package/src/tools/evalcell.ts +338 -0
  193. package/src/tools/html-text.ts +139 -0
  194. package/src/tools/provider.ts +149 -0
  195. package/src/tools/task.ts +216 -0
  196. package/src/tools/todo.ts +320 -0
  197. package/src/tools/webfetch.ts +331 -0
  198. package/src/tui/app.ts +608 -0
  199. package/src/tui/attach.ts +127 -0
  200. package/src/tui/checkpoints-cmd.ts +70 -0
  201. package/src/tui/clipboard-image.ts +81 -0
  202. package/src/tui/commands.ts +277 -0
  203. package/src/tui/cost.ts +108 -0
  204. package/src/tui/info-cmd.ts +144 -0
  205. package/src/tui/mcp-cmd.ts +128 -0
  206. package/src/tui/modes-cmd.ts +45 -0
  207. package/src/tui/overlays.ts +97 -0
  208. package/src/tui/pi-renderer.ts +424 -0
  209. package/src/tui/providers-cmd.ts +366 -0
  210. package/src/tui/renderer.ts +101 -0
  211. package/src/tui/replay-marker.ts +29 -0
  212. package/src/tui/session-cmd.ts +146 -0
  213. package/src/tui/sextant-attach.ts +68 -0
  214. package/src/tui/sextant-io.ts +184 -0
  215. package/src/tui/sextant-smoke.ts +110 -0
  216. package/src/tui/smoke.ts +72 -0
  217. package/src/tui/theme.ts +59 -0
  218. package/src/tui/todo-label.ts +7 -0
  219. package/src/workflow/engine.ts +266 -0
  220. package/tsconfig.json +30 -0
  221. package/vendor/pi-tui/LICENSE +21 -0
  222. package/vendor/pi-tui/PATCHES.md +12 -0
  223. package/vendor/pi-tui/PROVENANCE.md +12 -0
  224. package/vendor/pi-tui/README.upstream.md +854 -0
  225. package/vendor/pi-tui/native/win32/prebuilds/win32-arm64/win32-console-mode.node +0 -0
  226. package/vendor/pi-tui/native/win32/prebuilds/win32-x64/win32-console-mode.node +0 -0
  227. package/vendor/pi-tui/src/alt-screen-search.ts +158 -0
  228. package/vendor/pi-tui/src/autocomplete.ts +827 -0
  229. package/vendor/pi-tui/src/components/alt-screen-flash.ts +52 -0
  230. package/vendor/pi-tui/src/components/box.ts +138 -0
  231. package/vendor/pi-tui/src/components/cancellable-loader.ts +41 -0
  232. package/vendor/pi-tui/src/components/editor.ts +2364 -0
  233. package/vendor/pi-tui/src/components/h-stack.ts +45 -0
  234. package/vendor/pi-tui/src/components/image.ts +128 -0
  235. package/vendor/pi-tui/src/components/input.ts +448 -0
  236. package/vendor/pi-tui/src/components/loader.ts +93 -0
  237. package/vendor/pi-tui/src/components/markdown.ts +1016 -0
  238. package/vendor/pi-tui/src/components/scroll-view.ts +217 -0
  239. package/vendor/pi-tui/src/components/select-list.ts +230 -0
  240. package/vendor/pi-tui/src/components/settings-list.ts +277 -0
  241. package/vendor/pi-tui/src/components/spacer.ts +29 -0
  242. package/vendor/pi-tui/src/components/stack.ts +155 -0
  243. package/vendor/pi-tui/src/components/text.ts +108 -0
  244. package/vendor/pi-tui/src/components/truncated-text.ts +66 -0
  245. package/vendor/pi-tui/src/components/v-stack.ts +34 -0
  246. package/vendor/pi-tui/src/editor-component.ts +75 -0
  247. package/vendor/pi-tui/src/fuzzy.ts +138 -0
  248. package/vendor/pi-tui/src/index.ts +149 -0
  249. package/vendor/pi-tui/src/keybindings.ts +321 -0
  250. package/vendor/pi-tui/src/keys.ts +1402 -0
  251. package/vendor/pi-tui/src/kill-ring.ts +47 -0
  252. package/vendor/pi-tui/src/latex.ts +1381 -0
  253. package/vendor/pi-tui/src/layout-node.ts +52 -0
  254. package/vendor/pi-tui/src/layout.ts +411 -0
  255. package/vendor/pi-tui/src/native-modifiers.ts +60 -0
  256. package/vendor/pi-tui/src/native-module-path.ts +32 -0
  257. package/vendor/pi-tui/src/stdin-buffer.ts +445 -0
  258. package/vendor/pi-tui/src/terminal-colors.ts +74 -0
  259. package/vendor/pi-tui/src/terminal-image.ts +701 -0
  260. package/vendor/pi-tui/src/terminal.ts +554 -0
  261. package/vendor/pi-tui/src/tui-alt-screen.ts +1379 -0
  262. package/vendor/pi-tui/src/tui-main-screen.ts +655 -0
  263. package/vendor/pi-tui/src/tui.ts +1264 -0
  264. package/vendor/pi-tui/src/undo-stack.ts +29 -0
  265. package/vendor/pi-tui/src/utils.ts +1327 -0
  266. package/vendor/pi-tui/src/word-navigation.ts +118 -0
  267. package/vendor/pi-tui/test/test-themes.ts +39 -0
  268. package/vendor/pi-tui/test/virtual-terminal.ts +219 -0
  269. package/CHANGELOG.md +0 -512
  270. package/bin/rovecode.js +0 -24
  271. package/dist/cli/app-dybnr56b.js +0 -2
  272. package/dist/cli/ask-user-p8hq4xgj.js +0 -2
  273. package/dist/cli/auth-login-ewpgw5sm.js +0 -2
  274. package/dist/cli/auth-m8p9grty.js +0 -2
  275. package/dist/cli/bench-xv3ypwev.js +0 -9
  276. package/dist/cli/catalog-737wb2s0.js +0 -2
  277. package/dist/cli/cli-arhg40m0.js +0 -2
  278. package/dist/cli/client-cf2pxx8q.js +0 -2
  279. package/dist/cli/commands-3p7e4xxs.js +0 -2
  280. package/dist/cli/connect-3q93d7cb.js +0 -2
  281. package/dist/cli/context-cmd-eqxmxhzq.js +0 -2
  282. package/dist/cli/context-report-hbw9zfes.js +0 -2
  283. package/dist/cli/count-remote-mby98cd0.js +0 -2
  284. package/dist/cli/design-122y0axd.js +0 -2
  285. package/dist/cli/dispatch-b4egzvvh.js +0 -2
  286. package/dist/cli/doctor-x4jkv72e.js +0 -3
  287. package/dist/cli/executor-ftvg6tsy.js +0 -2
  288. package/dist/cli/export-pdgdhkch.js +0 -2
  289. package/dist/cli/files-cez9a96p.js +0 -2
  290. package/dist/cli/gauntlet-r3xxaszc.js +0 -2
  291. package/dist/cli/gauntlet-runner-r515m7kk.js +0 -10
  292. package/dist/cli/gauntlet-wave3-bnkjk2v2.js +0 -5
  293. package/dist/cli/gauntlet-wave4-acs9s60q.js +0 -14
  294. package/dist/cli/hashline-ewg5hbe3.js +0 -2
  295. package/dist/cli/http-n0kehsk8.js +0 -5
  296. package/dist/cli/index-z5qt1s76.js +0 -2
  297. package/dist/cli/install-80mp63kx.js +0 -2
  298. package/dist/cli/loop-12twjcat.js +0 -2
  299. package/dist/cli/main-01pv9206.js +0 -4
  300. package/dist/cli/main-0jys2ccn.js +0 -3
  301. package/dist/cli/main-1ztz6fkj.js +0 -10
  302. package/dist/cli/main-23q7cmww.js +0 -9
  303. package/dist/cli/main-2rzbexn2.js +0 -3
  304. package/dist/cli/main-2wyax8k9.js +0 -9
  305. package/dist/cli/main-2yeveeve.js +0 -6
  306. package/dist/cli/main-2z3dek0b.js +0 -3
  307. package/dist/cli/main-2zgsknth.js +0 -3
  308. package/dist/cli/main-45ejth3a.js +0 -4
  309. package/dist/cli/main-45rn3trk.js +0 -22
  310. package/dist/cli/main-4p4e2w7x.js +0 -4
  311. package/dist/cli/main-4y0tnfpa.js +0 -16
  312. package/dist/cli/main-5py0rkmc.js +0 -4
  313. package/dist/cli/main-6dtqmbt6.js +0 -7
  314. package/dist/cli/main-6h9x282m.js +0 -4
  315. package/dist/cli/main-6vjeds42.js +0 -3
  316. package/dist/cli/main-78gq4bt9.js +0 -6
  317. package/dist/cli/main-7jd5vh3x.js +0 -4
  318. package/dist/cli/main-7kt6r53y.js +0 -4
  319. package/dist/cli/main-8c1tbazx.js +0 -58
  320. package/dist/cli/main-9a9rnh47.js +0 -19
  321. package/dist/cli/main-9ht36z12.js +0 -3
  322. package/dist/cli/main-a2yfvcy9.js +0 -7
  323. package/dist/cli/main-a3f51n0x.js +0 -5
  324. package/dist/cli/main-b8zq261k.js +0 -3
  325. package/dist/cli/main-bxtvnf6d.js +0 -13
  326. package/dist/cli/main-edxc3yzt.js +0 -4
  327. package/dist/cli/main-evgz4mp5.js +0 -21
  328. package/dist/cli/main-f33fc5je.js +0 -9
  329. package/dist/cli/main-fvnpq46y.js +0 -12
  330. package/dist/cli/main-gbbty4d4.js +0 -3
  331. package/dist/cli/main-gth53dnt.js +0 -25
  332. package/dist/cli/main-hqbz10aw.js +0 -9
  333. package/dist/cli/main-hrrvcfan.js +0 -38
  334. package/dist/cli/main-hzwtsb2m.js +0 -5
  335. package/dist/cli/main-j7ttv0sd.js +0 -34
  336. package/dist/cli/main-jak598k9.js +0 -5
  337. package/dist/cli/main-kba6zeyd.js +0 -6
  338. package/dist/cli/main-kwwsz6rq.js +0 -3
  339. package/dist/cli/main-m8vm17zq.js +0 -3
  340. package/dist/cli/main-mg4f96e1.js +0 -3
  341. package/dist/cli/main-mg9b20ac.js +0 -18
  342. package/dist/cli/main-mgb9ccnx.js +0 -3
  343. package/dist/cli/main-mjt2p7aj.js +0 -3
  344. package/dist/cli/main-n6qrdbmy.js +0 -3
  345. package/dist/cli/main-na7wse0x.js +0 -5
  346. package/dist/cli/main-nqveez48.js +0 -4
  347. package/dist/cli/main-ntqef02r.js +0 -10
  348. package/dist/cli/main-nvc3yjay.js +0 -136
  349. package/dist/cli/main-p0cfn6nr.js +0 -16
  350. package/dist/cli/main-qj2djy17.js +0 -19
  351. package/dist/cli/main-qsevpgsv.js +0 -3
  352. package/dist/cli/main-qvarybsp.js +0 -3
  353. package/dist/cli/main-rebtt91r.js +0 -5
  354. package/dist/cli/main-rpg7h8mb.js +0 -3
  355. package/dist/cli/main-rsy72qmw.js +0 -15
  356. package/dist/cli/main-rvetps99.js +0 -18
  357. package/dist/cli/main-s4bb0jav.js +0 -3
  358. package/dist/cli/main-s9v8k74e.js +0 -3
  359. package/dist/cli/main-tjvwmscs.js +0 -3
  360. package/dist/cli/main-tkgarpjj.js +0 -4
  361. package/dist/cli/main-v8y60bb2.js +0 -3
  362. package/dist/cli/main-vhrrq337.js +0 -3
  363. package/dist/cli/main-vp2dfb7s.js +0 -4
  364. package/dist/cli/main-vqbr22sz.js +0 -8
  365. package/dist/cli/main-vxnwe5xx.js +0 -18
  366. package/dist/cli/main-wgph00xf.js +0 -5
  367. package/dist/cli/main-wk2csfnj.js +0 -5
  368. package/dist/cli/main-wm997zjx.js +0 -3
  369. package/dist/cli/main-wpkyraxh.js +0 -3
  370. package/dist/cli/main-wqt32p5x.js +0 -4
  371. package/dist/cli/main-x9ct6y1a.js +0 -3
  372. package/dist/cli/main-xfekqh9m.js +0 -7
  373. package/dist/cli/main-xt9zc3n6.js +0 -7
  374. package/dist/cli/main-xx2z3zh5.js +0 -4
  375. package/dist/cli/main-y5c82rxr.js +0 -3
  376. package/dist/cli/main-yrjt2sqt.js +0 -14
  377. package/dist/cli/main-ys6zj3yr.js +0 -3
  378. package/dist/cli/main-ywbxshqc.js +0 -8
  379. package/dist/cli/main-z13755t8.js +0 -25
  380. package/dist/cli/main-zc7pyrbj.js +0 -4
  381. package/dist/cli/main.js +0 -279
  382. package/dist/cli/market-cmd-bm5xvn9f.js +0 -5
  383. package/dist/cli/mcp-login-bthtfpt7.js +0 -2
  384. package/dist/cli/mcp-market-cmd-mbeshfyd.js +0 -2
  385. package/dist/cli/notify-54v5z9dz.js +0 -2
  386. package/dist/cli/oauth-g5gme95c.js +0 -2
  387. package/dist/cli/output-satndjap.js +0 -16
  388. package/dist/cli/profiles-sfhpbq3m.js +0 -2
  389. package/dist/cli/provider-config-hv3xtdt4.js +0 -2
  390. package/dist/cli/provider-kwzq6g84.js +0 -2
  391. package/dist/cli/registry-fh0hdnyn.js +0 -2
  392. package/dist/cli/registry-y1y8e94r.js +0 -2
  393. package/dist/cli/repl-t4z03mqq.js +0 -11
  394. package/dist/cli/resume-fqt4chg8.js +0 -2
  395. package/dist/cli/run-flags-rysbag9t.js +0 -2
  396. package/dist/cli/runtime-j19fjbsa.js +0 -2
  397. package/dist/cli/sandbox-config-g4qxd7y5.js +0 -2
  398. package/dist/cli/server-r0b6bksk.js +0 -5
  399. package/dist/cli/session-arg-txmn5g4x.js +0 -2
  400. package/dist/cli/session-ed250d9j.js +0 -2
  401. package/dist/cli/sessions-cmd-adw7svfn.js +0 -7
  402. package/dist/cli/settings-y9rzcqx8.js +0 -2
  403. package/dist/cli/setup-jmbr11j0.js +0 -2
  404. package/dist/cli/sextant-smoke-tcth0vea.js +0 -5
  405. package/dist/cli/skills-cmd-zbdy99v6.js +0 -2
  406. package/dist/cli/smoke-1bg937kx.js +0 -8
  407. package/dist/cli/start-chat-p01cdks3.js +0 -12
  408. package/dist/cli/stream-4wmyaypz.js +0 -2
  409. package/dist/cli/task-eg4s093s.js +0 -2
  410. package/dist/cli/tasks-12v9rr9k.js +0 -2
  411. package/dist/cli/thinking-a5ngvqyh.js +0 -2
  412. package/dist/cli/todo-1wxpcecx.js +0 -2
  413. package/dist/cli/tools-2ftsya7w.js +0 -2
  414. package/dist/cli/tools-x1tj4fxm.js +0 -2
  415. package/dist/cli/trust-cmd-hccxehzb.js +0 -2
  416. package/dist/cli/update-check-ygt3vd7m.js +0 -2
  417. package/dist/cli/update-cmd-v23qhr8c.js +0 -2
  418. package/dist/cli/voice-g1gtck92.js +0 -2
  419. package/dist/cli/webfetch-0nnrjgb5.js +0 -2
  420. package/dist/cli/websearch-f0vr2p7d.js +0 -2
  421. package/dist/cli/workspace-9rq1w4ta.js +0 -2
  422. package/dist/lib/index.js +0 -62
  423. package/dist/lib/models-index.json +0 -1
  424. package/dist/lib/plugins.js +0 -6
  425. package/dist/lib/providers.js +0 -17
  426. package/dist/lib/public-api.js +0 -20
  427. package/dist/rovecode.exe +0 -4
  428. /package/{dist/cli → src/providers}/models-index.json +0 -0
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Evaluate-and-record: runs one gauntlet task through the scripted (offline) runner,
3
+ * captures every loop event into a versioned trajectory JSONL (eval P0-1), then grades
4
+ * the end state with patch/test-based graders (eval P0-2) and writes the result line.
5
+ *
6
+ * The existing deterministic gauntlet is NOT modified: runTask gains only an optional
7
+ * event sink (same loop, same scripted stream, same verdict), and the grader layer is
8
+ * additive — a recorded run can be graded without touching gauntlet.ts's own verify.
9
+ */
10
+
11
+ import { mkdtempSync, rmSync } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { join } from "node:path";
14
+ import { runTask } from "./gauntlet-runner.ts";
15
+ import type { GauntletTask, GauntletTranscript } from "./gauntlet.ts";
16
+ import { runGraders, gradersPassed, type GraderOutcome, type GraderSpec } from "./grader.ts";
17
+ import {
18
+ TrajectoryWriter,
19
+ snapshotFixture,
20
+ gitCommit,
21
+ makeRunId,
22
+ sha256Text,
23
+ portablize,
24
+ redactSafe,
25
+ TRAJECTORY_SCHEMA_VERSION,
26
+ type FailureTaxonomy,
27
+ type FixtureSpec,
28
+ type TrajectoryResultLine,
29
+ type TrajectoryStepLine,
30
+ type RecordedToolResult,
31
+ } from "./trajectory.ts";
32
+ import type { RunEvent, PermissionRule } from "../core/types.ts";
33
+
34
+ export interface EvalTaskSpec {
35
+ id: string;
36
+ category: string;
37
+ prompt: string;
38
+ task: GauntletTask;
39
+ graderSpecs: GraderSpec[];
40
+ model?: { provider: string; model: string } | null;
41
+ seed?: number;
42
+ commit?: string | null;
43
+ permissionRules?: PermissionRule[];
44
+ }
45
+
46
+ export interface RecordOptions {
47
+ /** output dir for the trajectory JSONL (created if missing) */
48
+ dir: string;
49
+ runId?: string;
50
+ guard?: Parameters<typeof runTask>[2];
51
+ }
52
+
53
+ export interface RecordOutcome {
54
+ runId: string;
55
+ path: string;
56
+ result: TrajectoryResultLine;
57
+ transcript: GauntletTranscript;
58
+ graderOutcomes: GraderOutcome[];
59
+ fixture: FixtureSpec;
60
+ }
61
+
62
+ const EVIDENCE_TOOLCALLS = 64;
63
+
64
+ /** Map a loop event onto trajectory step lines. `seq` orders the file; tool outputs get
65
+ * their replay-comparable sha here (normalized RAW output, redaction is lossy). */
66
+ class StepBuilder {
67
+ private seq = 0;
68
+ private turn = 0;
69
+
70
+ from(ev: RunEvent, workspace: string): TrajectoryStepLine[] {
71
+ switch (ev.type) {
72
+ case "run_start":
73
+ this.turn = 0;
74
+ return [this.step({ type: "user", text: ev.goal })];
75
+ case "turn_start":
76
+ this.turn = ev.turn;
77
+ return [];
78
+ case "tool_execution_start":
79
+ return [this.step({ type: "assistant", turn: this.turn, toolCalls: [{ id: ev.callId, tool: ev.tool, args: ev.args }] })];
80
+ case "tool_execution_end": {
81
+ const normalized = portablize(ev.output, workspace) as unknown as string;
82
+ const rec: RecordedToolResult = {
83
+ callId: ev.callId,
84
+ ok: ev.ok,
85
+ output: normalized,
86
+ outputSha: sha256Text(normalized),
87
+ };
88
+ return [this.step({ type: "tool", turn: this.turn, results: [rec] })];
89
+ }
90
+ case "tool_call_failed": {
91
+ const output = `${ev.reason}: ${ev.detail}`;
92
+ const rec: RecordedToolResult = { callId: ev.callId, ok: false, output, outputSha: sha256Text(portablize(output, workspace) as unknown as string) };
93
+ return [this.step({ type: "tool", turn: this.turn, results: [rec] })];
94
+ }
95
+ case "turn_end":
96
+ return [this.step({ type: "assistant", turn: ev.turn, stopReason: ev.stopReason })];
97
+ case "run_end":
98
+ return [this.step({ type: "assistant", stopReason: ev.status, text: ev.summary })];
99
+ default:
100
+ return [];
101
+ }
102
+ }
103
+
104
+ private step(fields: Record<string, unknown>): TrajectoryStepLine {
105
+ return { kind: "step", seq: this.seq++, ...fields } as TrajectoryStepLine;
106
+ }
107
+ }
108
+
109
+ export function classifyFailure(input: {
110
+ graderOutcomes?: readonly GraderOutcome[];
111
+ events?: readonly RunEvent[];
112
+ error?: unknown;
113
+ transcript?: Pick<GauntletTranscript, "finalText" | "events" | "toolCalls" | "recovered">;
114
+ }): { taxonomy: FailureTaxonomy; detail: string } {
115
+ if (input.error !== undefined) {
116
+ const msg = input.error instanceof Error ? input.error.message : String(input.error);
117
+ return { taxonomy: /timeout/i.test(msg) ? "timeout" : "provider-error", detail: msg };
118
+ }
119
+ const failed = (input.graderOutcomes ?? []).find((o) => !o.advisory && !o.pass);
120
+ if (failed) return { taxonomy: "verify-failed", detail: `${failed.name}: ${failed.detail.slice(0, 300)}` };
121
+ const events = input.events ?? [];
122
+ for (const ev of events) {
123
+ if (ev.type === "tool_call_failed" && ev.reason === "invalid_args") return { taxonomy: "invalid-args", detail: ev.detail.slice(0, 300) };
124
+ if (ev.type === "tool_call_failed" && ev.reason === "permission_denied") return { taxonomy: "permission-denied", detail: ev.detail.slice(0, 300) };
125
+ }
126
+ const guardBlocked = events.some((ev) => ev.type === "tool_execution_end" && ev.output.includes("loop guard: blocked"));
127
+ if (guardBlocked) return { taxonomy: "loop-guard", detail: "the tool guard stubbed repeated identical calls until the run ended" };
128
+ if (input.transcript && /error:/i.test(input.transcript.finalText)) {
129
+ return { taxonomy: "provider-error", detail: input.transcript.finalText.slice(0, 300) };
130
+ }
131
+ return { taxonomy: "unknown", detail: "run failed without a recognized signature" };
132
+ }
133
+
134
+ /** Run one task offline (scripted provider), record its trajectory, grade its end state. */
135
+ export async function evaluateAndRecord(spec: EvalTaskSpec, opts: RecordOptions): Promise<RecordOutcome> {
136
+ const runId = opts.runId ?? makeRunId(spec.id);
137
+ const ws = spec.task.setup ? spec.task.setup() : mkdtempSync(join(tmpdir(), "rovecode-eval-"));
138
+ const t0 = Date.now();
139
+ const builder = new StepBuilder();
140
+ const events: RunEvent[] = [];
141
+ let transcript: GauntletTranscript | null = null;
142
+ let error: unknown;
143
+ try {
144
+ const fixture = snapshotFixture(ws);
145
+ const writer = new TrajectoryWriter(opts.dir, runId, ws);
146
+ writer.writeHeader({
147
+ kind: "header",
148
+ schemaVersion: TRAJECTORY_SCHEMA_VERSION,
149
+ runId,
150
+ startedAt: t0,
151
+ task: { id: spec.id, category: spec.category, prompt: spec.prompt },
152
+ model: spec.model ?? null,
153
+ seed: spec.seed ?? 0,
154
+ commit: spec.commit !== undefined ? spec.commit : gitCommit(),
155
+ fixture,
156
+ ...(spec.permissionRules ? { permissionRules: spec.permissionRules } : {}),
157
+ env: { platform: process.platform, runtime: "bun" },
158
+ });
159
+ const onEvent = (ev: RunEvent): void => {
160
+ events.push(ev);
161
+ for (const step of builder.from(ev, ws)) writer.writeStep(step);
162
+ };
163
+ try {
164
+ transcript = await runTask(spec.task, ws, opts.guard ?? undefined, onEvent);
165
+ } catch (e) {
166
+ error = e;
167
+ }
168
+ const graderOutcomes = error === undefined
169
+ ? await runGraders(spec.graderSpecs, { workspace: ws, fixture, transcript: transcript ?? { toolCalls: [], events: [], finalText: "", recovered: false } })
170
+ : [];
171
+ const pass = error === undefined && transcript !== null && gradersPassed(graderOutcomes);
172
+ const failure = pass ? undefined : classifyFailure({ graderOutcomes, events, error, transcript: transcript ?? undefined });
173
+ const result: TrajectoryResultLine = {
174
+ kind: "result",
175
+ schemaVersion: TRAJECTORY_SCHEMA_VERSION,
176
+ runId,
177
+ outcome: pass ? "pass" : failure?.taxonomy === "timeout" || failure?.taxonomy === "provider-error" ? "error" : "fail",
178
+ durationMs: Date.now() - t0,
179
+ toolCallCount: transcript?.toolCalls.length ?? 0,
180
+ ...(transcript?.usage ? { usage: transcript.usage } : {}),
181
+ graderSpecs: spec.graderSpecs as unknown[],
182
+ graderResults: graderOutcomes.map((o) => ({ name: o.name, pass: o.pass, detail: redactSafe(o.detail).slice(0, 400) })),
183
+ evidence: {
184
+ finalText: transcript?.finalText,
185
+ ...(transcript ? { toolCalls: transcript.toolCalls.slice(0, EVIDENCE_TOOLCALLS).map((c) => ({ tool: c.tool, args: c.args })) } : {}),
186
+ ...(failure ? { notes: [`${failure.taxonomy}: ${failure.detail}`] } : {}),
187
+ },
188
+ ...(failure ? { failure } : {}),
189
+ };
190
+ writer.writeResult(result);
191
+ return {
192
+ runId,
193
+ path: writer.path,
194
+ result,
195
+ transcript: transcript ?? { toolCalls: [], events: [], finalText: "", recovered: false },
196
+ graderOutcomes,
197
+ fixture,
198
+ };
199
+ } finally {
200
+ rmSync(ws, { recursive: true, force: true });
201
+ }
202
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Secrets redaction for eval persistence (eval P0-5).
3
+ *
4
+ * Every string that lands in a trajectory JSONL — tool args, tool outputs, fixture
5
+ * contents, evidence — goes through redactSecrets/redactDeep FIRST. The patterns cover
6
+ * the credential shapes the harness actually handles (provider keys, Bearer/Basic auth
7
+ * headers, JWTs, PEM private keys) plus the generic `key = "value"` assignments that
8
+ * leak keys through tool output. Sensitive KEY names in structured args are redacted
9
+ * regardless of value shape, because a value like `Bearer whatever` is not always
10
+ * pattern-visible.
11
+ *
12
+ * Scope note (the research critique's premise, verified by
13
+ * test/unit/eval-redact.test.ts): src/telemetry/otel.ts records ids, sizes and
14
+ * outcomes only — it never carries args or output, so it needs no redaction pass and
15
+ * this module deliberately does NOT touch it. If that contract ever erodes, the test
16
+ * pins it red.
17
+ *
18
+ * Determinism matters more than recall here: replays compare hashes of redacted-
19
+ * normalized text, so the replacement is a stable literal ("[REDACTED:<kind>]") and
20
+ * redaction is idempotent — a second pass changes nothing.
21
+ */
22
+
23
+ export interface RedactionPattern {
24
+ name: string;
25
+ re: RegExp;
26
+ /** replacement for the whole match; $1… refer to capture groups */
27
+ replace?: string;
28
+ }
29
+
30
+ /** Ordered: a pattern consumes its match before later ones see it, so the more
31
+ * specific provider shapes come first (sk-ant-… would otherwise be eaten by the
32
+ * generic openai pattern), and the generic assignment pass runs last over text
33
+ * that may already carry [REDACTED:…] tokens (still idempotent). */
34
+ export const REDACTION_PATTERNS: readonly RedactionPattern[] = [
35
+ { name: "anthropic", re: /sk-ant-[A-Za-z0-9_-]{16,}/g },
36
+ { name: "openai", re: /sk-(?:proj-)?[A-Za-z0-9_-]{20,}/g },
37
+ { name: "aws-access-key", re: /AKIA[0-9A-Z]{16}/g },
38
+ { name: "github", re: /(?:gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{22,})/g },
39
+ { name: "google", re: /AIza[0-9A-Za-z_-]{35,}/g },
40
+ { name: "slack", re: /xox[bpars]-[A-Za-z0-9-]{10,}/g },
41
+ { name: "private-key", re: /-----BEGIN (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY(?: BLOCK)?-----[\s\S]*?-----END (?:RSA |EC |DSA |OPENSSH |PGP )?PRIVATE KEY(?: BLOCK)?-----/g },
42
+ { name: "jwt", re: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{5,}\b/g },
43
+ { name: "bearer", re: /\b(Bearer)\s+[A-Za-z0-9._~+/=-]{8,}/gi, replace: "$1 [REDACTED:bearer]" },
44
+ { name: "basic-auth", re: /\b(Basic)\s+[A-Za-z0-9+/=]{8,}/gi, replace: "$1 [REDACTED:basic-auth]" },
45
+ ];
46
+
47
+ /** The literal a redacted value becomes. Stable across runs so replay hashes match. */
48
+ export function redactionToken(kind: string): string {
49
+ return `[REDACTED:${kind}]`;
50
+ }
51
+
52
+ /** `key = "value"` / `password: value` — keep the key, lose the value. Done in code (not one
53
+ * regex) because quoted and bare values end differently, and String.replace cannot consume
54
+ * text beyond the match; an unterminated quote is left alone (redaction must not corrupt
55
+ * non-secret text it cannot bound). */
56
+ const ASSIGNMENT_RE = /\b(api[_-]?key|apikey|secret|access[_-]?token|auth[_-]?token|refresh[_-]?token|token|pass(?:word)?|passwd|pwd|client[_-]?secret)\b(\s*[:=]\s*)/gi;
57
+
58
+ function redactAssignments(text: string, hits: Map<string, number>): string {
59
+ let out = "";
60
+ let last = 0;
61
+ for (const m of text.matchAll(ASSIGNMENT_RE)) {
62
+ const start = m.index ?? 0;
63
+ const valueStart = start + m[0].length;
64
+ const q = text[valueStart];
65
+ let end = valueStart;
66
+ let replacement: string;
67
+ if (q === '"' || q === "'") {
68
+ const close = text.indexOf(q, valueStart + 1);
69
+ if (close === -1) continue; // unterminated: leave this one untouched
70
+ end = close + 1;
71
+ replacement = `${m[1]}${m[2]}${q}${redactionToken("assignment")}${q}`;
72
+ } else {
73
+ let j = valueStart;
74
+ while (j < text.length && !/[\s,;)\]}]/.test(text[j]!)) j++;
75
+ if (j === valueStart) continue; // separator with no value — nothing to hide
76
+ end = j;
77
+ replacement = `${m[1]}${m[2]}${redactionToken("assignment")}`;
78
+ }
79
+ out += text.slice(last, start) + replacement;
80
+ last = end;
81
+ hits.set("assignment", (hits.get("assignment") ?? 0) + 1);
82
+ }
83
+ return out + text.slice(last);
84
+ }
85
+
86
+ function applyPatterns(text: string, hits: Map<string, number>): string {
87
+ let out = text;
88
+ for (const p of REDACTION_PATTERNS) {
89
+ const replacement = p.replace;
90
+ out = out.replace(p.re, (...args: unknown[]) => {
91
+ hits.set(p.name, (hits.get(p.name) ?? 0) + 1);
92
+ if (replacement === undefined) return redactionToken(p.name);
93
+ // expand $1…$9 against the callback's capture groups (args[0] is the match)
94
+ return replacement.replace(/\$(\d)/g, (_, d: string) => {
95
+ const g = args[Number(d)];
96
+ return typeof g === "string" ? g : "";
97
+ });
98
+ });
99
+ }
100
+ return redactAssignments(out, hits);
101
+ }
102
+
103
+ /** Report form: the redacted text plus one count per pattern that hit. */
104
+ export function redactionReport(text: string): { text: string; matches: { kind: string; count: number }[] } {
105
+ const hits = new Map<string, number>();
106
+ const out = applyPatterns(text, hits);
107
+ return { text: out, matches: [...hits].map(([kind, count]) => ({ kind, count })) };
108
+ }
109
+
110
+ /** Plain-text form: just the redacted text. */
111
+ export function redactSecrets(text: string): string {
112
+ return redactionReport(text).text;
113
+ }
114
+
115
+ const SENSITIVE_KEY = /^(?:.*[-_])?(?:pass(?:word)?|passwd|pwd|secret|token|api[-_]?key|apikey|authorization|private[-_]?key|credentials?)$/i;
116
+
117
+ /** Deep form for structured payloads (tool args, fixture objects): every string value is
118
+ * pattern-redacted, and values under sensitive KEY names are replaced wholesale — a
119
+ * credential is still a credential when no pattern can name its shape. Cycles degrade to
120
+ * a marker instead of hanging; non-string leaves pass through untouched. */
121
+ export function redactDeep<T>(value: T, seen: Set<object> = new Set()): T {
122
+ if (typeof value === "string") return redactSecrets(value) as unknown as T;
123
+ if (value === null || typeof value !== "object") return value;
124
+ const obj = value as object;
125
+ if (seen.has(obj)) return "[circular]" as unknown as T;
126
+ seen.add(obj);
127
+ try {
128
+ if (Array.isArray(obj)) return obj.map((v) => redactDeep(v, seen)) as unknown as T;
129
+ const out: Record<string, unknown> = {};
130
+ for (const [k, v] of Object.entries(obj as Record<string, unknown>)) {
131
+ if (typeof v === "string" && SENSITIVE_KEY.test(k)) {
132
+ out[k] = redactionToken("key");
133
+ } else {
134
+ out[k] = redactDeep(v, seen);
135
+ }
136
+ }
137
+ return out as unknown as T;
138
+ } finally {
139
+ seen.delete(obj);
140
+ }
141
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Trajectory replay (eval P0-3): verify a recorded run WITHOUT a network or a model.
3
+ *
4
+ * The recorded trajectory carries a fixture snapshot (the pre-run workspace) and every
5
+ * tool call the model made. Replay rebuilds the workspace from the snapshot, re-executes
6
+ * the recorded calls through the REAL tool registry with the recorded permission rules,
7
+ * and compares, per call, the ok flag and the sha256 of the path-normalized RAW output
8
+ * against the recorded hashes. Then it re-runs the recorded grader specs and compares the
9
+ * composite verdict with the recorded outcome. Anything that drifted — a tampered call,
10
+ * a changed tool implementation, a nondeterministic command — is reported per step.
11
+ *
12
+ * Determinism guarantees this leans on: fixtures are files-on-disk, "<workspace>" tokens
13
+ * make recorded paths machine-independent (portablize/deportablize), and grader specs
14
+ * are workspace-relative. A tool whose output genuinely varies between machines (clocks,
15
+ * line endings in shell output) will honestly fail replay — that is the point.
16
+ */
17
+
18
+ import { mkdtempSync, rmSync } from "node:fs";
19
+ import { tmpdir } from "node:os";
20
+ import { join } from "node:path";
21
+ import { readTrajectory, materializeFixture, portablize, sha256Text, deportablize } from "./trajectory.ts";
22
+ import { runGraders, gradersPassed, GraderConfigError, type GraderOutcome, type GraderSpec } from "./grader.ts";
23
+ import { ToolRegistry } from "../core/tools.ts";
24
+ import type { PermissionRule, RunEvent, ToolCallPart } from "../core/types.ts";
25
+ import { readTool, editTool, writeTool, bashTool } from "../coding/hashline.ts";
26
+ import { globTool, grepTool, lsTool } from "../coding/files.ts";
27
+
28
+ export const REPLAY_ALLOW_ALL: PermissionRule[] = [{ action: "*", resource: "*", effect: "allow" }];
29
+
30
+ export interface StepComparison {
31
+ callId: string;
32
+ tool: string;
33
+ okMatch: boolean | null; // null = no recorded result to compare against
34
+ hashMatch: boolean | null; // null = recorded result had no hash
35
+ detail?: string;
36
+ }
37
+
38
+ export interface ReplayReport {
39
+ ok: boolean;
40
+ runId: string;
41
+ task: { id: string; category: string };
42
+ comparisons: StepComparison[];
43
+ graderOutcomes: GraderOutcome[];
44
+ /** replay's grader verdict vs the recorded outcome (null = nothing to compare) */
45
+ outcomeMatch: boolean | null;
46
+ mismatches: string[];
47
+ }
48
+
49
+ export interface ReplayOptions {
50
+ /** extra/override tools — defaults to the standard coding set */
51
+ registerTools?: (registry: ToolRegistry) => void;
52
+ /** keep the rebuilt workspace dir for debugging (default: removed) */
53
+ keepWorkspace?: boolean;
54
+ }
55
+
56
+ export async function replayTrajectory(path: string, opts?: ReplayOptions): Promise<ReplayReport> {
57
+ const t = readTrajectory(path);
58
+ const ws = mkdtempSync(join(tmpdir(), "rovecode-replay-"));
59
+ try {
60
+ materializeFixture(t.header.fixture, ws);
61
+
62
+ const registry = new ToolRegistry();
63
+ registry.register(readTool, editTool, writeTool, bashTool, globTool, grepTool, lsTool);
64
+ opts?.registerTools?.(registry);
65
+
66
+ const rules = t.header.permissionRules ?? REPLAY_ALLOW_ALL;
67
+ const recordedResults = new Map<string, { ok: boolean; outputSha?: string; output?: string }>();
68
+ for (const step of t.steps) {
69
+ for (const r of step.results ?? []) recordedResults.set(r.callId, { ok: r.ok, outputSha: r.outputSha, output: r.output });
70
+ }
71
+
72
+ const comparisons: StepComparison[] = [];
73
+ const mismatches: string[] = [];
74
+ const emit = (_ev: RunEvent): void => { /* replay is not recorded */ };
75
+
76
+ for (const step of t.steps) {
77
+ for (const call of step.toolCalls ?? []) {
78
+ const args = deportablize(call.args, ws);
79
+ const recorded = recordedResults.get(call.id);
80
+ const comparison: StepComparison = { callId: call.id, tool: call.tool, okMatch: null, hashMatch: null };
81
+ if (!recorded) {
82
+ comparison.detail = "no recorded result for this call";
83
+ mismatches.push(`${call.tool}(${call.id}): no recorded result`);
84
+ comparisons.push(comparison);
85
+ continue;
86
+ }
87
+ const part: ToolCallPart = { kind: "tool_call", id: call.id, tool: call.tool, args };
88
+ let out: { ok: boolean; output: string };
89
+ try {
90
+ out = await registry.dispatch(
91
+ part,
92
+ { sessionId: `replay-${t.header.runId}`, cwd: ws, signal: new AbortController().signal, permissions: { effect: "allow" } },
93
+ undefined,
94
+ rules,
95
+ undefined,
96
+ emit,
97
+ );
98
+ } catch (e) {
99
+ out = { ok: false, output: `replay dispatch threw: ${e instanceof Error ? e.message : String(e)}` };
100
+ }
101
+ comparison.okMatch = out.ok === recorded.ok;
102
+ const replaySha = sha256Text(portablize(out.output, ws) as unknown as string);
103
+ comparison.hashMatch = recorded.outputSha !== undefined ? replaySha === recorded.outputSha : null;
104
+ if (recorded.output !== undefined && comparison.hashMatch === false) {
105
+ comparison.detail = `output drifted — recorded: ${recorded.output.slice(0, 120)} | replayed: ${(portablize(out.output, ws) as unknown as string).slice(0, 120)}`;
106
+ }
107
+ if (comparison.okMatch === false) mismatches.push(`${call.tool}(${call.id}): ok ${out.ok} vs recorded ${recorded.ok}`);
108
+ if (comparison.hashMatch === false) mismatches.push(`${call.tool}(${call.id}): output hash drifted`);
109
+ comparisons.push(comparison);
110
+ }
111
+ }
112
+
113
+ let graderOutcomes: GraderOutcome[] = [];
114
+ let outcomeMatch: boolean | null = null;
115
+ const specs = (t.result?.graderSpecs ?? []) as GraderSpec[];
116
+ if (specs.length > 0) {
117
+ try {
118
+ graderOutcomes = await runGraders(specs, {
119
+ workspace: ws,
120
+ fixture: t.header.fixture,
121
+ transcript: { finalText: t.result?.evidence.finalText ?? "", toolCalls: [], events: [], recovered: true },
122
+ });
123
+ if (t.result) outcomeMatch = gradersPassed(graderOutcomes) === (t.result.outcome === "pass");
124
+ } catch (e) {
125
+ if (e instanceof GraderConfigError) {
126
+ mismatches.push(`grader config: ${e.message}`);
127
+ } else {
128
+ mismatches.push(`grader run failed: ${e instanceof Error ? e.message : String(e)}`);
129
+ }
130
+ }
131
+ }
132
+ if (outcomeMatch === false) mismatches.push("replayed graders disagree with the recorded outcome");
133
+
134
+ const stepMismatch = comparisons.some((c) => c.okMatch === false || c.hashMatch === false);
135
+ return {
136
+ ok: !stepMismatch && mismatches.length === 0 && outcomeMatch !== false,
137
+ runId: t.header.runId,
138
+ task: { id: t.header.task.id, category: t.header.task.category },
139
+ comparisons,
140
+ graderOutcomes,
141
+ outcomeMatch,
142
+ mismatches,
143
+ };
144
+ } finally {
145
+ if (!opts?.keepWorkspace) rmSync(ws, { recursive: true, force: true });
146
+ }
147
+ }