@tangle-network/agent-eval 0.216.1 → 0.218.1

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 (532) hide show
  1. package/CHANGELOG.md +137 -57
  2. package/README.md +2 -2
  3. package/dist/adapters/http.d.ts +2 -2
  4. package/dist/{agent-profile-VFcQncbW.d.ts → agent-profile-DTt8uGP9.d.ts} +4 -17
  5. package/dist/agent-profile-DTt8uGP9.d.ts.map +1 -0
  6. package/dist/{agent-profile-cell-BK8mlqlq.js → agent-profile-cell-B1V1gUZs.js} +4 -53
  7. package/dist/agent-profile-cell-B1V1gUZs.js.map +1 -0
  8. package/dist/{agent-profile-cell-BNp-Js9F.d.ts → agent-profile-cell-BcpFbNd5.d.ts} +2 -22
  9. package/dist/agent-profile-cell-BcpFbNd5.d.ts.map +1 -0
  10. package/dist/{agreement-irr-BEWLIF3_.js → agreement-irr-BhAqjluA.js} +5 -5
  11. package/dist/agreement-irr-BhAqjluA.js.map +1 -0
  12. package/dist/analyst/index.d.ts +36 -169
  13. package/dist/analyst/index.d.ts.map +1 -1
  14. package/dist/analyst/index.js +17 -192
  15. package/dist/analyst/index.js.map +1 -1
  16. package/dist/{analyze-runs-D68fd-su.d.ts → analyze-runs-DjlZOVXx.d.ts} +5 -5
  17. package/dist/{analyze-runs-D68fd-su.d.ts.map → analyze-runs-DjlZOVXx.d.ts.map} +1 -1
  18. package/dist/{analyze-runs-C84irm4i.js → analyze-runs-DqPJaJ4X.js} +225 -21
  19. package/dist/analyze-runs-DqPJaJ4X.js.map +1 -0
  20. package/dist/authenticity/index.d.ts +0 -23
  21. package/dist/authenticity/index.d.ts.map +1 -1
  22. package/dist/authenticity/index.js +1 -34
  23. package/dist/authenticity/index.js.map +1 -1
  24. package/dist/{benchmark-command-By0vh8q7.js → benchmark-command-BupdHRyf.js} +1225 -661
  25. package/dist/benchmark-command-BupdHRyf.js.map +1 -0
  26. package/dist/benchmarks/index.d.ts +5 -6
  27. package/dist/benchmarks/index.d.ts.map +1 -1
  28. package/dist/benchmarks/index.js +4 -4
  29. package/dist/benchmarks/index.js.map +1 -1
  30. package/dist/builder-eval/index.d.ts +3 -3
  31. package/dist/builder-eval/index.js +2 -2
  32. package/dist/builder-eval/index.js.map +1 -1
  33. package/dist/campaign/index.d.ts +7 -7
  34. package/dist/campaign/index.js +15 -15
  35. package/dist/{campaign-BudZ70zo.js → campaign-CGvUTnmc.js} +324 -800
  36. package/dist/campaign-CGvUTnmc.js.map +1 -0
  37. package/dist/{campaign-evidence-Clrn4hBV.js → campaign-evidence-DunzcJGv.js} +10 -18
  38. package/dist/{campaign-evidence-Clrn4hBV.js.map → campaign-evidence-DunzcJGv.js.map} +1 -1
  39. package/dist/{canonical-B2RqLofv.js → canonical-Dq2hOQ8L.js} +2 -2
  40. package/dist/{canonical-B2RqLofv.js.map → canonical-Dq2hOQ8L.js.map} +1 -1
  41. package/dist/cell-spend-DEWR3nj-.js +90 -0
  42. package/dist/cell-spend-DEWR3nj-.js.map +1 -0
  43. package/dist/{chat-client-l-MVDlDk.js → chat-client-B2pXlPdx.js} +2 -2
  44. package/dist/{chat-client-l-MVDlDk.js.map → chat-client-B2pXlPdx.js.map} +1 -1
  45. package/dist/{chat-json-call-puMArmtW.js → chat-json-call-Dm9c-b41.js} +3 -2
  46. package/dist/chat-json-call-Dm9c-b41.js.map +1 -0
  47. package/dist/{claim-q4HQhK1G.js → claim-BPVuyHuy.js} +3 -3
  48. package/dist/{claim-q4HQhK1G.js.map → claim-BPVuyHuy.js.map} +1 -1
  49. package/dist/cli.js +113 -12
  50. package/dist/cli.js.map +1 -1
  51. package/dist/{client-BzL9niqi.d.ts → client-hwo3cJjR.d.ts} +3 -16
  52. package/dist/client-hwo3cJjR.d.ts.map +1 -0
  53. package/dist/{cluster-bootstrap-qcUWYFl-.js → cluster-bootstrap-DEwwhl0C.js} +3 -3
  54. package/dist/{cluster-bootstrap-qcUWYFl-.js.map → cluster-bootstrap-DEwwhl0C.js.map} +1 -1
  55. package/dist/{produced-state-BsDmnwxc.js → completion-verifier-DbLVf3i8.js} +9 -64
  56. package/dist/completion-verifier-DbLVf3i8.js.map +1 -0
  57. package/dist/contract/index.d.ts +12 -12
  58. package/dist/contract/index.js +107 -403
  59. package/dist/contract/index.js.map +1 -1
  60. package/dist/{cost-ledger-BP8V4kUQ.js → cost-ledger-D0352Z4d.js} +4 -8
  61. package/dist/cost-ledger-D0352Z4d.js.map +1 -0
  62. package/dist/dataset-DQqhOCPt.d.ts.map +1 -1
  63. package/dist/{default-registry-COhk9g36.js → default-registry-BJ-Pq6yL.js} +314 -285
  64. package/dist/default-registry-BJ-Pq6yL.js.map +1 -0
  65. package/dist/{default-registry-DzHiGruW.d.ts → default-registry-DXgLmoDI.d.ts} +7 -50
  66. package/dist/default-registry-DXgLmoDI.d.ts.map +1 -0
  67. package/dist/{define-agent-eval-CNTPzIkp.js → define-agent-eval-BGdFKlrc.js} +170 -110
  68. package/dist/define-agent-eval-BGdFKlrc.js.map +1 -0
  69. package/dist/{define-agent-eval-WSck0CZJ.d.ts → define-agent-eval-BlH10zEw.d.ts} +8 -8
  70. package/dist/define-agent-eval-BlH10zEw.d.ts.map +1 -0
  71. package/dist/{descriptive-DboxPZrG.js → descriptive-CVNy48rR.js} +2 -2
  72. package/dist/{descriptive-DboxPZrG.js.map → descriptive-CVNy48rR.js.map} +1 -1
  73. package/dist/diagnosis/index.d.ts +4 -14
  74. package/dist/diagnosis/index.d.ts.map +1 -1
  75. package/dist/diagnosis/index.js +67 -28
  76. package/dist/diagnosis/index.js.map +1 -1
  77. package/dist/{dspy-rlm-engine-LRCu61m5.js → dspy-rlm-engine-CSMwudLO.js} +5 -10
  78. package/dist/dspy-rlm-engine-CSMwudLO.js.map +1 -0
  79. package/dist/{emitter-HcCmFQl_.d.ts → emitter-DABYYTcH.d.ts} +5 -5
  80. package/dist/emitter-DABYYTcH.d.ts.map +1 -0
  81. package/dist/emitter-DbMXUIVP.js.map +1 -1
  82. package/dist/{engine-3A_4jOlI.d.ts → engine-BvZ0R-36.d.ts} +17 -21
  83. package/dist/engine-BvZ0R-36.d.ts.map +1 -0
  84. package/dist/{equal-terms-BtdXayCJ.js → equal-terms-DbPGcVtI.js} +2 -2
  85. package/dist/equal-terms-DbPGcVtI.js.map +1 -0
  86. package/dist/{errors-DVZiqS7K.js → errors-BcOs5MKW.js} +2 -8
  87. package/dist/{errors-DVZiqS7K.js.map → errors-BcOs5MKW.js.map} +1 -1
  88. package/dist/eval-campaign-BUMIxKTE.js +393 -0
  89. package/dist/eval-campaign-BUMIxKTE.js.map +1 -0
  90. package/dist/{evaluation-6Yoi74gM.d.ts → evaluation-BOZ38vOH.d.ts} +7 -7
  91. package/dist/evaluation-BOZ38vOH.d.ts.map +1 -0
  92. package/dist/evaluation.d.ts +2 -2
  93. package/dist/evaluation.js +1 -1
  94. package/dist/evaluation.js.map +1 -1
  95. package/dist/{exact-types-ClugMJfI.d.ts → exact-types-CflbJ9f3.d.ts} +2 -2
  96. package/dist/{exact-types-ClugMJfI.d.ts.map → exact-types-CflbJ9f3.d.ts.map} +1 -1
  97. package/dist/{exec-ChJ6d5if.js → exec-CpbtOXVt.js} +2 -24
  98. package/dist/{exec-ChJ6d5if.js.map → exec-CpbtOXVt.js.map} +1 -1
  99. package/dist/execution-tracks-CpgFPpS5.js.map +1 -1
  100. package/dist/experiment/index.d.ts +9 -112
  101. package/dist/experiment/index.d.ts.map +1 -1
  102. package/dist/experiment/index.js +258 -311
  103. package/dist/experiment/index.js.map +1 -1
  104. package/dist/{exporters-X4xwla8A.js → exporters-DWvM16LE.js} +3 -3
  105. package/dist/exporters-DWvM16LE.js.map +1 -0
  106. package/dist/{exporters-ChTnR90l.d.ts → exporters-Dk3oEbK_.d.ts} +2 -4
  107. package/dist/{exporters-ChTnR90l.d.ts.map → exporters-Dk3oEbK_.d.ts.map} +1 -1
  108. package/dist/{external-optimizer-process-BjMGUuds.js → external-optimizer-process-C3Wm7Vhn.js} +4 -3
  109. package/dist/external-optimizer-process-C3Wm7Vhn.js.map +1 -0
  110. package/dist/{external-optimizer-subprocess-BqW0hHcI.js → external-optimizer-subprocess-B5gdLRZv.js} +9 -11
  111. package/dist/external-optimizer-subprocess-B5gdLRZv.js.map +1 -0
  112. package/dist/{extract-usage-BrnzRJ6H.js → extract-usage-BZLU01kR.js} +2 -23
  113. package/dist/{extract-usage-BrnzRJ6H.js.map → extract-usage-BZLU01kR.js.map} +1 -1
  114. package/dist/{failure-cluster-CbgEDcFJ.d.ts → failure-cluster-DN0t63Z0.d.ts} +4 -43
  115. package/dist/failure-cluster-DN0t63Z0.d.ts.map +1 -0
  116. package/dist/{failure-taxonomy-CjabTATj.js → failure-taxonomy-Bx5oWqaW.js} +2 -19
  117. package/dist/{failure-taxonomy-CjabTATj.js.map → failure-taxonomy-Bx5oWqaW.js.map} +1 -1
  118. package/dist/{feedback-trajectory-C4EG0nNH.d.ts → feedback-trajectory-BWmhRBGu.d.ts} +7 -9
  119. package/dist/feedback-trajectory-BWmhRBGu.d.ts.map +1 -0
  120. package/dist/{semantic-concept-judge-BQF8te0T.js → findings-store-BtTzPL0d.js} +9 -249
  121. package/dist/findings-store-BtTzPL0d.js.map +1 -0
  122. package/dist/fuzz.d.ts +19 -62
  123. package/dist/fuzz.d.ts.map +1 -1
  124. package/dist/fuzz.js +62 -108
  125. package/dist/fuzz.js.map +1 -1
  126. package/dist/{hf-dataset-BR2yhuNn.js → hf-dataset-CKxIJQba.js} +4 -24
  127. package/dist/hf-dataset-CKxIJQba.js.map +1 -0
  128. package/dist/hosted/index.d.ts +2 -2
  129. package/dist/hosted/index.js +2 -2
  130. package/dist/{index-CyszSg4O.d.ts → index-B16FNdW5.d.ts} +145 -360
  131. package/dist/index-B16FNdW5.d.ts.map +1 -0
  132. package/dist/{index-DV-jSzxF.d.ts → index-BLGqZ81N.d.ts} +10 -15
  133. package/dist/{index-DV-jSzxF.d.ts.map → index-BLGqZ81N.d.ts.map} +1 -1
  134. package/dist/{index-BO41eGj5.d.ts → index-CX-GAUpw.d.ts} +3 -7
  135. package/dist/index-CX-GAUpw.d.ts.map +1 -0
  136. package/dist/{index-DHKEAv3s.d.ts → index-CalAsXMR.d.ts} +8 -68
  137. package/dist/index-CalAsXMR.d.ts.map +1 -0
  138. package/dist/{index-BFRO6chs.d.ts → index-CiRwLv2R.d.ts} +5 -69
  139. package/dist/index-CiRwLv2R.d.ts.map +1 -0
  140. package/dist/{index-DPcngtwt.d.ts → index-DbMOXYnP.d.ts} +11 -36
  141. package/dist/index-DbMOXYnP.d.ts.map +1 -0
  142. package/dist/index.d.ts +211 -218
  143. package/dist/index.d.ts.map +1 -1
  144. package/dist/index.js +999 -1714
  145. package/dist/index.js.map +1 -1
  146. package/dist/{integrity-dLiDe2kD.js → integrity-C8oUeB0g.js} +326 -203
  147. package/dist/integrity-C8oUeB0g.js.map +1 -0
  148. package/dist/{integrity-BXOsAb3i.js → integrity-W4CADcFc.js} +3 -7
  149. package/dist/{integrity-BXOsAb3i.js.map → integrity-W4CADcFc.js.map} +1 -1
  150. package/dist/{integrity-DAMEDFaC.d.ts → integrity-_qgZLA5p.d.ts} +4 -6
  151. package/dist/{integrity-DAMEDFaC.d.ts.map → integrity-_qgZLA5p.d.ts.map} +1 -1
  152. package/dist/{internal-Epl-YBZW.js → internal-BTK91Bqt.js} +3 -3
  153. package/dist/{internal-Epl-YBZW.js.map → internal-BTK91Bqt.js.map} +1 -1
  154. package/dist/{interval-CELpye1n.js → interval-Dq9gt7jx.js} +3 -3
  155. package/dist/{interval-CELpye1n.js.map → interval-Dq9gt7jx.js.map} +1 -1
  156. package/dist/{journal-DfRz9kps.js → journal-C7om8Gum.js} +157 -147
  157. package/dist/journal-C7om8Gum.js.map +1 -0
  158. package/dist/json-BhdsJaOQ.js +111 -0
  159. package/dist/json-BhdsJaOQ.js.map +1 -0
  160. package/dist/{judge-calibration-BpCy5XQl.js → judge-calibration-BkXMIWA4.js} +3 -3
  161. package/dist/{judge-calibration-BpCy5XQl.js.map → judge-calibration-BkXMIWA4.js.map} +1 -1
  162. package/dist/{judge-gate-iY9s4L44.js → judge-gate-Co5tuh57.js} +4 -27
  163. package/dist/{judge-gate-iY9s4L44.js.map → judge-gate-Co5tuh57.js.map} +1 -1
  164. package/dist/{judge-gate-CE7CQAgt.d.ts → judge-gate-DsL0yFcb.d.ts} +8 -9
  165. package/dist/judge-gate-DsL0yFcb.d.ts.map +1 -0
  166. package/dist/{kind-factory-BobI5Jy3.js → kind-factory-Gy4fXixd.js} +88 -17
  167. package/dist/kind-factory-Gy4fXixd.js.map +1 -0
  168. package/dist/ledger-core/index.d.ts +2 -2
  169. package/dist/ledger-core/index.js +4 -4
  170. package/dist/ledger-core/lock-holder.d.ts +2 -0
  171. package/dist/ledger-core/lock-holder.js +181 -0
  172. package/dist/ledger-core/lock-holder.js.map +1 -0
  173. package/dist/{ledger-core-Gq7yp8lP.js → ledger-core-CAxUdDk0.js} +5 -84
  174. package/dist/ledger-core-CAxUdDk0.js.map +1 -0
  175. package/dist/{llm-client-CyBAoYiX.js → llm-client-_Y6ic-tf.js} +243 -355
  176. package/dist/llm-client-_Y6ic-tf.js.map +1 -0
  177. package/dist/{llm-judge-CG12SQJq.js → llm-judge-Djxh-Uhs.js} +662 -482
  178. package/dist/llm-judge-Djxh-Uhs.js.map +1 -0
  179. package/dist/lock-holder-DaJbfpci.d.ts +86 -0
  180. package/dist/lock-holder-DaJbfpci.d.ts.map +1 -0
  181. package/dist/matrix/index.d.ts +3 -3
  182. package/dist/matrix/index.js +3 -2
  183. package/dist/{matrix-Dm0BQW7U.js → matrix-B8l-Nsxd.js} +3 -88
  184. package/dist/matrix-B8l-Nsxd.js.map +1 -0
  185. package/dist/{matrix-DSrSxjjy.d.ts → matrix-CCSBna4q.d.ts} +9 -18
  186. package/dist/matrix-CCSBna4q.d.ts.map +1 -0
  187. package/dist/{mean-DHoMiudD.js → mean-BbA_JCzW.js} +39 -5
  188. package/dist/mean-BbA_JCzW.js.map +1 -0
  189. package/dist/meta-eval/index.d.ts +5 -5
  190. package/dist/meta-eval/index.js +10 -10
  191. package/dist/metrics-BKaPBtIG.js.map +1 -1
  192. package/dist/{mint-DNU8QPG4.d.ts → mint-BRZXuwRi.d.ts} +4 -4
  193. package/dist/{mint-DNU8QPG4.d.ts.map → mint-BRZXuwRi.d.ts.map} +1 -1
  194. package/dist/{mint-QaD5cA0u.js → mint-CbdwjTPW.js} +4 -4
  195. package/dist/{mint-QaD5cA0u.js.map → mint-CbdwjTPW.js.map} +1 -1
  196. package/dist/{multi-layer-verifier-BUaQ4C17.d.ts → multi-layer-verifier-DEgMUk6v.d.ts} +2 -2
  197. package/dist/{multi-layer-verifier-BUaQ4C17.d.ts.map → multi-layer-verifier-DEgMUk6v.d.ts.map} +1 -1
  198. package/dist/{multiplicity-DQUFcbW4.js → multiplicity-Sg0U7h8j.js} +2 -2
  199. package/dist/{multiplicity-DQUFcbW4.js.map → multiplicity-Sg0U7h8j.js.map} +1 -1
  200. package/dist/multishot/golden/index.d.ts +1 -23
  201. package/dist/multishot/golden/index.d.ts.map +1 -1
  202. package/dist/multishot/golden/index.js +3 -3
  203. package/dist/multishot/golden/index.js.map +1 -1
  204. package/dist/multishot/index.d.ts +3 -12
  205. package/dist/multishot/index.d.ts.map +1 -1
  206. package/dist/multishot/index.js +4 -3
  207. package/dist/multishot/index.js.map +1 -1
  208. package/dist/openapi.json +114 -10
  209. package/dist/{otel-spans-B9tUXRHF.js → otel-spans-CdN909k1.js} +3 -3
  210. package/dist/{otel-spans-B9tUXRHF.js.map → otel-spans-CdN909k1.js.map} +1 -1
  211. package/dist/{paired-arms-CwMGJsrS.js → paired-arms-Ct4LTKc1.js} +32 -9
  212. package/dist/paired-arms-Ct4LTKc1.js.map +1 -0
  213. package/dist/{paired-binary-CvpFHfKZ.js → paired-binary-B_nejA44.js} +3 -36
  214. package/dist/paired-binary-B_nejA44.js.map +1 -0
  215. package/dist/{paired-promotion-decision-DPKz8ZnF.d.ts → paired-promotion-decision-CQMJ5xGN.d.ts} +2 -34
  216. package/dist/paired-promotion-decision-CQMJ5xGN.d.ts.map +1 -0
  217. package/dist/{paired-promotion-decision-DsAPYGCm.js → paired-promotion-decision-D0SHb-ZL.js} +3 -3
  218. package/dist/{paired-promotion-decision-DsAPYGCm.js.map → paired-promotion-decision-D0SHb-ZL.js.map} +1 -1
  219. package/dist/{pareto-Cm6ndrWN.js → pareto-CTv8vozZ.js} +11 -11
  220. package/dist/pareto-CTv8vozZ.js.map +1 -0
  221. package/dist/pipelines/index.d.ts +16 -19
  222. package/dist/pipelines/index.d.ts.map +1 -1
  223. package/dist/pipelines/index.js +4 -68
  224. package/dist/pipelines/index.js.map +1 -1
  225. package/dist/{power-and-mde-Hj2ubb4B.js → power-and-mde-N5nU4_Ly.js} +3 -3
  226. package/dist/{power-and-mde-Hj2ubb4B.js.map → power-and-mde-N5nU4_Ly.js.map} +1 -1
  227. package/dist/{prime-protocol-CHKgA3a6.js → prime-protocol-BFgGrEMP.js} +3 -3
  228. package/dist/prime-protocol-BFgGrEMP.js.map +1 -0
  229. package/dist/{prime-protocol-HiB9HX7E.d.ts → prime-protocol-BWG2DQIk.d.ts} +3 -21
  230. package/dist/prime-protocol-BWG2DQIk.d.ts.map +1 -0
  231. package/dist/profile-cell.d.ts +2 -2
  232. package/dist/profile-cell.js +2 -2
  233. package/dist/{promotion-gate-BawrdPYr.d.ts → promotion-gate-CxEHRsZW.d.ts} +7 -8
  234. package/dist/{promotion-gate-BawrdPYr.d.ts.map → promotion-gate-CxEHRsZW.d.ts.map} +1 -1
  235. package/dist/{promotion-policy-KO57gbyV.js → promotion-policy-6A8osGr_.js} +4 -4
  236. package/dist/{promotion-policy-KO57gbyV.js.map → promotion-policy-6A8osGr_.js.map} +1 -1
  237. package/dist/{proposal-findings-CY1xKn_a.js → proposal-findings-n-5WBbEx.js} +3 -3
  238. package/dist/proposal-findings-n-5WBbEx.js.map +1 -0
  239. package/dist/{query-BUCZCK7F.js → query-B_RZ36an.js} +5 -5
  240. package/dist/query-B_RZ36an.js.map +1 -0
  241. package/dist/{query-DK1nWcmp.d.ts → query-Cmv_U8vf.d.ts} +4 -24
  242. package/dist/{query-DK1nWcmp.d.ts.map → query-Cmv_U8vf.d.ts.map} +1 -1
  243. package/dist/{random-ByUSnV9w.js → random-DDDcC7YU.js} +2 -2
  244. package/dist/{random-ByUSnV9w.js.map → random-DDDcC7YU.js.map} +1 -1
  245. package/dist/{raw-provider-sink-DoHz4JNG.js → raw-provider-sink-75w4FWBj.js} +2 -2
  246. package/dist/{raw-provider-sink-DoHz4JNG.js.map → raw-provider-sink-75w4FWBj.js.map} +1 -1
  247. package/dist/{redact-mtCCcT8z.d.ts → redact-DXdlFb_U.d.ts} +2 -3
  248. package/dist/redact-DXdlFb_U.d.ts.map +1 -0
  249. package/dist/{redact-B5xXKqQ3.js → redact-mtkTiJVk.js} +2 -7
  250. package/dist/{redact-B5xXKqQ3.js.map → redact-mtkTiJVk.js.map} +1 -1
  251. package/dist/{registry-D7_hm2fK.d.ts → registry-ssNoHzwD.d.ts} +4 -4
  252. package/dist/{registry-D7_hm2fK.d.ts.map → registry-ssNoHzwD.d.ts.map} +1 -1
  253. package/dist/{release-confidence-CpuP65S5.js → release-confidence-CYIFH-fp.js} +6 -11
  254. package/dist/release-confidence-CYIFH-fp.js.map +1 -0
  255. package/dist/{report-command-SC9nqb4M.js → render-Cs34y0iX.js} +625 -587
  256. package/dist/render-Cs34y0iX.js.map +1 -0
  257. package/dist/reporting.d.ts +6 -6
  258. package/dist/reporting.js +8 -8
  259. package/dist/{researcher-CEcfku-T.d.ts → researcher-GU1tV4dn.d.ts} +9 -9
  260. package/dist/{researcher-CEcfku-T.d.ts.map → researcher-GU1tV4dn.d.ts.map} +1 -1
  261. package/dist/{reward-CpCmAM8L.d.ts → reward-DrZ71vnf.d.ts} +3 -26
  262. package/dist/reward-DrZ71vnf.d.ts.map +1 -0
  263. package/dist/{reward-hacking-E0d-WVcP.js → reward-hacking-ByuETaIb.js} +3 -3
  264. package/dist/{reward-hacking-E0d-WVcP.js.map → reward-hacking-ByuETaIb.js.map} +1 -1
  265. package/dist/{reward-hacking-RVdZ6Kkt.d.ts → reward-hacking-E52wq86n.d.ts} +5 -18
  266. package/dist/reward-hacking-E52wq86n.d.ts.map +1 -0
  267. package/dist/reward-nw2xZGZG.js.map +1 -1
  268. package/dist/rl.d.ts +117 -295
  269. package/dist/rl.d.ts.map +1 -1
  270. package/dist/rl.js +27 -747
  271. package/dist/rl.js.map +1 -1
  272. package/dist/rollout/index.d.ts +5 -5
  273. package/dist/rollout/index.js +7 -8
  274. package/dist/{rollout-DZV1t2Wg.js → rollout-Dd1ucT_M.js} +88 -13
  275. package/dist/rollout-Dd1ucT_M.js.map +1 -0
  276. package/dist/{rubric-predictive-validity-DaFW70y9.js → rubric-predictive-validity-D3_oI72N.js} +3 -3
  277. package/dist/{rubric-predictive-validity-DaFW70y9.js.map → rubric-predictive-validity-D3_oI72N.js.map} +1 -1
  278. package/dist/{rubric-predictive-validity-C03EC7ZE.d.ts → rubric-predictive-validity-DiwT8PbL.d.ts} +2 -2
  279. package/dist/{rubric-predictive-validity-C03EC7ZE.d.ts.map → rubric-predictive-validity-DiwT8PbL.d.ts.map} +1 -1
  280. package/dist/{run-record-C1E24G2A.d.ts → run-record-CaxbLq6Y.d.ts} +4 -10
  281. package/dist/run-record-CaxbLq6Y.d.ts.map +1 -0
  282. package/dist/{run-record-C5e7FjhJ.js → run-record-CysKLB93.js} +3 -3
  283. package/dist/run-record-CysKLB93.js.map +1 -0
  284. package/dist/{run-record-Boc5IUTp.js → run-record-DhEJgoPz.js} +4 -4
  285. package/dist/{run-record-Boc5IUTp.js.map → run-record-DhEJgoPz.js.map} +1 -1
  286. package/dist/sandbox-harness-BlSOu4LX.d.ts.map +1 -1
  287. package/dist/{schema-CSf6qWgZ.js → schema-Bt2vLFm3.js} +2 -14
  288. package/dist/schema-Bt2vLFm3.js.map +1 -0
  289. package/dist/schema-C-ziyJD7.d.ts.map +1 -1
  290. package/dist/{schema-BuiujZ-g.js → schema-CvhfLjtZ.js} +78 -48
  291. package/dist/schema-CvhfLjtZ.js.map +1 -0
  292. package/dist/{schema-2g7trUwH.d.ts → schema-D6EVKzTB.d.ts} +4 -5
  293. package/dist/schema-D6EVKzTB.d.ts.map +1 -0
  294. package/dist/search/index.d.ts +3 -31
  295. package/dist/search/index.d.ts.map +1 -1
  296. package/dist/search/index.js +4 -4
  297. package/dist/{search-kernel-DyNEa6IU.d.ts → search-kernel-p9JB3SmL.d.ts} +10 -27
  298. package/dist/search-kernel-p9JB3SmL.d.ts.map +1 -0
  299. package/dist/{search-ledger-CVUGLDZp.js → search-ledger-ClpN4I3e.js} +7 -7
  300. package/dist/{search-ledger-CVUGLDZp.js.map → search-ledger-ClpN4I3e.js.map} +1 -1
  301. package/dist/{search-ledger-file-DDQUgvCg.js → search-ledger-file-Bd5Tioq6.js} +4 -4
  302. package/dist/search-ledger-file-Bd5Tioq6.js.map +1 -0
  303. package/dist/{search-shipper-BOSD46qv.js → search-shipper-Bg2HW3Uv.js} +4 -4
  304. package/dist/search-shipper-Bg2HW3Uv.js.map +1 -0
  305. package/dist/{search-summary-BQCSWf1g.js → search-summary-CrBuvVnf.js} +5 -5
  306. package/dist/{search-summary-BQCSWf1g.js.map → search-summary-CrBuvVnf.js.map} +1 -1
  307. package/dist/{sequential-CzK5DarL.js → sequential-BVv1sjsw.js} +2 -32
  308. package/dist/{sequential-CzK5DarL.js.map → sequential-BVv1sjsw.js.map} +1 -1
  309. package/dist/{sequential-Bf_pbE50.js → sequential-CimQrbjv.js} +5 -5
  310. package/dist/sequential-CimQrbjv.js.map +1 -0
  311. package/dist/{sequential-BhsrMupG.d.ts → sequential-KwGspfeR.d.ts} +2 -9
  312. package/dist/{sequential-BhsrMupG.d.ts.map → sequential-KwGspfeR.d.ts.map} +1 -1
  313. package/dist/{sequential-eprocess-DM_qENCl.js → sequential-eprocess-DodI6RoF.js} +2 -2
  314. package/dist/{sequential-eprocess-DM_qENCl.js.map → sequential-eprocess-DodI6RoF.js.map} +1 -1
  315. package/dist/{server-e9oWO_bN.js → server-C6M0qR8h.js} +101 -101
  316. package/dist/server-C6M0qR8h.js.map +1 -0
  317. package/dist/{single-run-lock-CV9Eyd_q.js → single-run-lock-Dekd8_Wv.js} +7 -7
  318. package/dist/single-run-lock-Dekd8_Wv.js.map +1 -0
  319. package/dist/{skillopt-optimization-method-BiwA8nfX.js → skillopt-optimization-method-5dlex7MJ.js} +1386 -1184
  320. package/dist/skillopt-optimization-method-5dlex7MJ.js.map +1 -0
  321. package/dist/{spans-BHAUXMGN.d.ts → spans-CVngGij3.d.ts} +2 -2
  322. package/dist/{spans-BHAUXMGN.d.ts.map → spans-CVngGij3.d.ts.map} +1 -1
  323. package/dist/{statistical-heldout-CYpGaoDH.d.ts → statistical-heldout-BHfLZHrm.d.ts} +8 -67
  324. package/dist/statistical-heldout-BHfLZHrm.d.ts.map +1 -0
  325. package/dist/{statistical-heldout-42NlCNDe.js → statistical-heldout-Bksv92Th.js} +3 -3
  326. package/dist/statistical-heldout-Bksv92Th.js.map +1 -0
  327. package/dist/{steps-Bg5-KVx1.d.ts → steps-DlepJPwD.d.ts} +2 -56
  328. package/dist/steps-DlepJPwD.d.ts.map +1 -0
  329. package/dist/{store-C4avFjIi.d.ts → store-B3nWRy3B.d.ts} +2 -2
  330. package/dist/{store-C4avFjIi.d.ts.map → store-B3nWRy3B.d.ts.map} +1 -1
  331. package/dist/{store-otlp-DJ9dtA5x.js → store-otlp-BteDAbHK.js} +3 -3
  332. package/dist/{store-otlp-DJ9dtA5x.js.map → store-otlp-BteDAbHK.js.map} +1 -1
  333. package/dist/{store-tool-spans-CIX5arAG.js → store-tool-spans-D5l_OfTJ.js} +8 -8
  334. package/dist/store-tool-spans-D5l_OfTJ.js.map +1 -0
  335. package/dist/{store-tool-spans-DdCSPa7C.d.ts → store-tool-spans-DZS0HWVf.d.ts} +9 -18
  336. package/dist/store-tool-spans-DZS0HWVf.d.ts.map +1 -0
  337. package/dist/storyboard/index.d.ts +4 -11
  338. package/dist/storyboard/index.d.ts.map +1 -1
  339. package/dist/storyboard/index.js +1 -16
  340. package/dist/storyboard/index.js.map +1 -1
  341. package/dist/{summary-report-V_JACR4Y.js → summary-report-BsXJoYX5.js} +88 -10
  342. package/dist/summary-report-BsXJoYX5.js.map +1 -0
  343. package/dist/{summary-report-bdFDXq0b.d.ts → summary-report-CkbRcSuu.d.ts} +25 -19
  344. package/dist/summary-report-CkbRcSuu.d.ts.map +1 -0
  345. package/dist/supervisor-run/index.d.ts +60 -28
  346. package/dist/supervisor-run/index.d.ts.map +1 -1
  347. package/dist/supervisor-run/index.js +4 -458
  348. package/dist/supervisor-run/index.js.map +1 -1
  349. package/dist/{systemone-BomhHr2c.d.ts → systemone-BMzDJb1i.d.ts} +5 -5
  350. package/dist/{systemone-BomhHr2c.d.ts.map → systemone-BMzDJb1i.d.ts.map} +1 -1
  351. package/dist/systemone-protocol.js +1 -1
  352. package/dist/{systemone-review-CUVcQJu6.d.ts → systemone-review-CwE6JGIp.d.ts} +2 -2
  353. package/dist/{systemone-review-CUVcQJu6.d.ts.map → systemone-review-CwE6JGIp.d.ts.map} +1 -1
  354. package/dist/{systemone-review-D7sXwsqq.js → systemone-review-DzdOJ8I6.js} +3 -3
  355. package/dist/{systemone-review-D7sXwsqq.js.map → systemone-review-DzdOJ8I6.js.map} +1 -1
  356. package/dist/systemone.d.ts +5 -5
  357. package/dist/systemone.js +5 -5
  358. package/dist/systemone.js.map +1 -1
  359. package/dist/{task-failure-attributes-hEmASjix.js → task-failure-attributes-BMsblaDh.js} +3 -3
  360. package/dist/task-failure-attributes-BMsblaDh.js.map +1 -0
  361. package/dist/{terminal-record-DZyQZ3pz.js → terminal-record-D4Rv-j1W.js} +244 -196
  362. package/dist/terminal-record-D4Rv-j1W.js.map +1 -0
  363. package/dist/{tool-groups-C3NEcdCM.d.ts → tool-groups-DyNz2JZ_.d.ts} +3 -3
  364. package/dist/tool-groups-DyNz2JZ_.d.ts.map +1 -0
  365. package/dist/{tool-waste-BKgvd-TP.d.ts → tool-waste-CkjzWmeY.d.ts} +25 -28
  366. package/dist/tool-waste-CkjzWmeY.d.ts.map +1 -0
  367. package/dist/{tool-waste-BlesqTqW.js → tool-waste-DLsjS_p-.js} +5 -5
  368. package/dist/{tool-waste-BlesqTqW.js.map → tool-waste-DLsjS_p-.js.map} +1 -1
  369. package/dist/{trace-questions-CM2CAocU.d.ts → trace-questions-xxwgsuMi.d.ts} +4 -9
  370. package/dist/{trace-questions-CM2CAocU.d.ts.map → trace-questions-xxwgsuMi.d.ts.map} +1 -1
  371. package/dist/trace-repair/index.d.ts +14 -211
  372. package/dist/trace-repair/index.d.ts.map +1 -1
  373. package/dist/trace-repair/index.js +865 -981
  374. package/dist/trace-repair/index.js.map +1 -1
  375. package/dist/traces.d.ts +18 -46
  376. package/dist/traces.d.ts.map +1 -1
  377. package/dist/traces.js +13 -13
  378. package/dist/traces.js.map +1 -1
  379. package/dist/trajectory-replay/index.d.ts +7 -118
  380. package/dist/trajectory-replay/index.d.ts.map +1 -1
  381. package/dist/trajectory-replay/index.js +921 -869
  382. package/dist/trajectory-replay/index.js.map +1 -1
  383. package/dist/{tree-C1XYeXvy.js → tree-B5e1uAeL.js} +132 -21
  384. package/dist/tree-B5e1uAeL.js.map +1 -0
  385. package/dist/{types-Cbe2fWBa.d.ts → types-BKCzzmit.d.ts} +3 -3
  386. package/dist/{types-Cbe2fWBa.d.ts.map → types-BKCzzmit.d.ts.map} +1 -1
  387. package/dist/{types-B3jzCp0p.js → types-BXgASBCL.js} +2 -2
  388. package/dist/{types-B3jzCp0p.js.map → types-BXgASBCL.js.map} +1 -1
  389. package/dist/{types-C1tnkEgM.d.ts → types-CW8ewATZ.d.ts} +4 -113
  390. package/dist/types-CW8ewATZ.d.ts.map +1 -0
  391. package/dist/{types-BqkVmHfv.d.ts → types-CmXY0PQf.d.ts} +2 -4
  392. package/dist/types-CmXY0PQf.d.ts.map +1 -0
  393. package/dist/{types-C02qtnG8.d.ts → types-D-mgRzlh.d.ts} +7 -28
  394. package/dist/types-D-mgRzlh.d.ts.map +1 -0
  395. package/dist/{types-IXD2HJzu.js → types-DqFMNTgK.js} +2 -4
  396. package/dist/types-DqFMNTgK.js.map +1 -0
  397. package/dist/{types-Cq8mSbq8.d.ts → types-DtkO1G9L.d.ts} +7 -9
  398. package/dist/{types-Cq8mSbq8.d.ts.map → types-DtkO1G9L.d.ts.map} +1 -1
  399. package/dist/{verdict-Dl0tiT1X.js → verdict-BGQmxQ8A.js} +3 -3
  400. package/dist/verdict-BGQmxQ8A.js.map +1 -0
  401. package/dist/{verdict-E4eRNf7-.d.ts → verdict-C24NELtN.d.ts} +2 -15
  402. package/dist/{verdict-E4eRNf7-.d.ts.map → verdict-C24NELtN.d.ts.map} +1 -1
  403. package/dist/{verdict-cache-Bm8EKL2s.js → verdict-cache-CL_7tePD.js} +2 -2
  404. package/dist/verdict-cache-CL_7tePD.js.map +1 -0
  405. package/dist/wire/index.d.ts +31 -410
  406. package/dist/wire/index.d.ts.map +1 -1
  407. package/dist/wire/index.js +2 -2
  408. package/docs/changes/jev-evidence-review.md +1 -1
  409. package/docs/changes/jev-product-adoption.md +1 -1
  410. package/docs/changes/jev-recorded-evidence.md +1 -1
  411. package/docs/design/statistics-decisions.md +18 -18
  412. package/docs/design.md +1 -1
  413. package/docs/eval-surface-map.md +1 -0
  414. package/docs/evaluation-integrity.md +1 -1
  415. package/docs/profile-json-identity.md +1 -2
  416. package/docs/proofs/campaign-attempt-retention-20261001/README.md +2 -2
  417. package/docs/public-api.md +158 -538
  418. package/docs/rollout.md +1 -1
  419. package/docs/trace-repair-admission.md +1 -1
  420. package/docs/wire-protocol.md +1 -1
  421. package/package.json +7 -2
  422. package/dist/active-curriculum-B_Lc9kfO.js +0 -61
  423. package/dist/active-curriculum-B_Lc9kfO.js.map +0 -1
  424. package/dist/agent-profile-VFcQncbW.d.ts.map +0 -1
  425. package/dist/agent-profile-cell-BK8mlqlq.js.map +0 -1
  426. package/dist/agent-profile-cell-BNp-Js9F.d.ts.map +0 -1
  427. package/dist/agreement-irr-BEWLIF3_.js.map +0 -1
  428. package/dist/analyze-runs-C84irm4i.js.map +0 -1
  429. package/dist/baseline-Udi_PLuT.js +0 -259
  430. package/dist/baseline-Udi_PLuT.js.map +0 -1
  431. package/dist/benchmark-CXmVVHH2.js +0 -562
  432. package/dist/benchmark-CXmVVHH2.js.map +0 -1
  433. package/dist/benchmark-command-By0vh8q7.js.map +0 -1
  434. package/dist/campaign-BudZ70zo.js.map +0 -1
  435. package/dist/chat-json-call-puMArmtW.js.map +0 -1
  436. package/dist/claude-jsonl-dZir12m6.js +0 -86
  437. package/dist/claude-jsonl-dZir12m6.js.map +0 -1
  438. package/dist/client-BzL9niqi.d.ts.map +0 -1
  439. package/dist/cost-ledger-BP8V4kUQ.js.map +0 -1
  440. package/dist/counterfactual-CPaWN99-.d.ts +0 -60
  441. package/dist/counterfactual-CPaWN99-.d.ts.map +0 -1
  442. package/dist/counterfactual-Dn3veueY.js +0 -98
  443. package/dist/counterfactual-Dn3veueY.js.map +0 -1
  444. package/dist/default-registry-COhk9g36.js.map +0 -1
  445. package/dist/default-registry-DzHiGruW.d.ts.map +0 -1
  446. package/dist/define-agent-eval-CNTPzIkp.js.map +0 -1
  447. package/dist/define-agent-eval-WSck0CZJ.d.ts.map +0 -1
  448. package/dist/dspy-rlm-engine-LRCu61m5.js.map +0 -1
  449. package/dist/effect-sizes-BhzEpXMc.js +0 -82
  450. package/dist/effect-sizes-BhzEpXMc.js.map +0 -1
  451. package/dist/emitter-HcCmFQl_.d.ts.map +0 -1
  452. package/dist/engine-3A_4jOlI.d.ts.map +0 -1
  453. package/dist/equal-terms-BtdXayCJ.js.map +0 -1
  454. package/dist/eval-campaign-90jeaCAI.js +0 -343
  455. package/dist/eval-campaign-90jeaCAI.js.map +0 -1
  456. package/dist/evaluation-6Yoi74gM.d.ts.map +0 -1
  457. package/dist/exporters-X4xwla8A.js.map +0 -1
  458. package/dist/external-optimizer-process-BjMGUuds.js.map +0 -1
  459. package/dist/external-optimizer-subprocess-BqW0hHcI.js.map +0 -1
  460. package/dist/failure-cluster-CbgEDcFJ.d.ts.map +0 -1
  461. package/dist/feedback-trajectory-C4EG0nNH.d.ts.map +0 -1
  462. package/dist/hf-dataset-BR2yhuNn.js.map +0 -1
  463. package/dist/index-BFRO6chs.d.ts.map +0 -1
  464. package/dist/index-BO41eGj5.d.ts.map +0 -1
  465. package/dist/index-CyszSg4O.d.ts.map +0 -1
  466. package/dist/index-DHKEAv3s.d.ts.map +0 -1
  467. package/dist/index-DPcngtwt.d.ts.map +0 -1
  468. package/dist/integrity-dLiDe2kD.js.map +0 -1
  469. package/dist/journal-DfRz9kps.js.map +0 -1
  470. package/dist/judge-gate-CE7CQAgt.d.ts.map +0 -1
  471. package/dist/kind-factory-BobI5Jy3.js.map +0 -1
  472. package/dist/ledger-core-Gq7yp8lP.js.map +0 -1
  473. package/dist/llm-client-CyBAoYiX.js.map +0 -1
  474. package/dist/llm-judge-CG12SQJq.js.map +0 -1
  475. package/dist/matrix-DSrSxjjy.d.ts.map +0 -1
  476. package/dist/matrix-Dm0BQW7U.js.map +0 -1
  477. package/dist/mean-DHoMiudD.js.map +0 -1
  478. package/dist/normal-DsJZtrJc.js +0 -25
  479. package/dist/normal-DsJZtrJc.js.map +0 -1
  480. package/dist/paired-arms-CwMGJsrS.js.map +0 -1
  481. package/dist/paired-binary-CvpFHfKZ.js.map +0 -1
  482. package/dist/paired-promotion-decision-DPKz8ZnF.d.ts.map +0 -1
  483. package/dist/pareto-Cm6ndrWN.js.map +0 -1
  484. package/dist/prime-protocol-CHKgA3a6.js.map +0 -1
  485. package/dist/prime-protocol-HiB9HX7E.d.ts.map +0 -1
  486. package/dist/produced-state-BsDmnwxc.js.map +0 -1
  487. package/dist/proposal-findings-CY1xKn_a.js.map +0 -1
  488. package/dist/query-BUCZCK7F.js.map +0 -1
  489. package/dist/redact-mtCCcT8z.d.ts.map +0 -1
  490. package/dist/release-confidence-CpuP65S5.js.map +0 -1
  491. package/dist/report-command-SC9nqb4M.js.map +0 -1
  492. package/dist/reward-CpCmAM8L.d.ts.map +0 -1
  493. package/dist/reward-hacking-RVdZ6Kkt.d.ts.map +0 -1
  494. package/dist/rollout-DZV1t2Wg.js.map +0 -1
  495. package/dist/run-record-C1E24G2A.d.ts.map +0 -1
  496. package/dist/run-record-C5e7FjhJ.js.map +0 -1
  497. package/dist/schema-2g7trUwH.d.ts.map +0 -1
  498. package/dist/schema-BuiujZ-g.js.map +0 -1
  499. package/dist/schema-CSf6qWgZ.js.map +0 -1
  500. package/dist/search-kernel-DyNEa6IU.d.ts.map +0 -1
  501. package/dist/search-ledger-file-DDQUgvCg.js.map +0 -1
  502. package/dist/search-shipper-BOSD46qv.js.map +0 -1
  503. package/dist/semantic-concept-judge-BQF8te0T.js.map +0 -1
  504. package/dist/sequential-Bf_pbE50.js.map +0 -1
  505. package/dist/server-e9oWO_bN.js.map +0 -1
  506. package/dist/single-run-lock-CV9Eyd_q.js.map +0 -1
  507. package/dist/skillopt-optimization-method-BiwA8nfX.js.map +0 -1
  508. package/dist/statistical-heldout-42NlCNDe.js.map +0 -1
  509. package/dist/statistical-heldout-CYpGaoDH.d.ts.map +0 -1
  510. package/dist/steps-Bg5-KVx1.d.ts.map +0 -1
  511. package/dist/store-tool-spans-CIX5arAG.js.map +0 -1
  512. package/dist/store-tool-spans-DdCSPa7C.d.ts.map +0 -1
  513. package/dist/student-t-DVPW7CdL.js +0 -39
  514. package/dist/student-t-DVPW7CdL.js.map +0 -1
  515. package/dist/summary-report-V_JACR4Y.js.map +0 -1
  516. package/dist/summary-report-bdFDXq0b.d.ts.map +0 -1
  517. package/dist/task-failure-attributes-hEmASjix.js.map +0 -1
  518. package/dist/terminal-record-DZyQZ3pz.js.map +0 -1
  519. package/dist/tool-groups-C3NEcdCM.d.ts.map +0 -1
  520. package/dist/tool-waste-BKgvd-TP.d.ts.map +0 -1
  521. package/dist/trajectory-Y1fmdlw7.d.ts +0 -24
  522. package/dist/trajectory-Y1fmdlw7.d.ts.map +0 -1
  523. package/dist/tree-C1XYeXvy.js.map +0 -1
  524. package/dist/types-BqkVmHfv.d.ts.map +0 -1
  525. package/dist/types-C02qtnG8.d.ts.map +0 -1
  526. package/dist/types-C1tnkEgM.d.ts.map +0 -1
  527. package/dist/types-IXD2HJzu.js.map +0 -1
  528. package/dist/usage-receipt-Cx3xNeyB.js +0 -76
  529. package/dist/usage-receipt-Cx3xNeyB.js.map +0 -1
  530. package/dist/verdict-Dl0tiT1X.js.map +0 -1
  531. package/dist/verdict-cache-Bm8EKL2s.js.map +0 -1
  532. package/docs/design/self-improvement-evidence-audit.md +0 -263
@@ -1,251 +1,107 @@
1
- import { t as mulberry32 } from "../random-ByUSnV9w.js";
2
- import { t as certificationEvidenceDigest } from "../verdict-Dl0tiT1X.js";
1
+ import { o as NotFoundError, s as ValidationError } from "../errors-BcOs5MKW.js";
2
+ import { t as mulberry32 } from "../random-DDDcC7YU.js";
3
+ import { t as certificationEvidenceDigest } from "../verdict-BGQmxQ8A.js";
3
4
  import { t as TraceEmitter } from "../emitter-DbMXUIVP.js";
4
- import { t as runCounterfactual } from "../counterfactual-Dn3veueY.js";
5
5
  import { n as InMemoryTraceStore } from "../store-BhNuLdJw.js";
6
6
  import { t as packageVersion } from "../package-version-D7lQHt_-.js";
7
- import { a as assertReplayableTrajectory, c as deriveFailureSignature, d as isRecordedTimeout, f as isSubmitAction, g as unreadableExitCount, h as parseRecordedReturncode, i as TIMEOUT_OBSERVATION_MARKER, l as finalRecordedOutcome, m as parseObservationOutput, n as FORMAT_ERROR_OBSERVATION_PREFIX, o as classifyObservation, p as isSubmitOnlyAction, r as SUBMIT_ACTION_SIGNATURE, s as decodeRecordedTurns, t as wrapActionForExec, u as isElidedField } from "../exec-ChJ6d5if.js";
7
+ import { t as buildTrajectory } from "../trajectory-D_7rLrvE.js";
8
+ import { a as classifyObservation, c as finalRecordedOutcome, d as isSubmitOnlyAction, f as parseObservationOutput, o as decodeRecordedTurns, p as parseRecordedReturncode, s as deriveFailureSignature, t as wrapActionForExec, u as isSubmitAction } from "../exec-CpbtOXVt.js";
8
9
  import { createHash } from "node:crypto";
9
10
  import { appendFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
10
11
  import { join } from "node:path";
11
12
  import { tmpdir } from "node:os";
12
13
  import { execFile } from "node:child_process";
13
14
  import { promisify } from "node:util";
14
- //#region src/trajectory-replay/corpus.ts
15
+ //#region src/counterfactual.ts
15
16
  /**
16
- * Corpus enumeration for batch replay verification.
17
+ * Counterfactual replay — "what would have happened if we'd changed
18
+ * exactly one thing at turn N?"
17
19
  *
18
- * A labeled trajectory corpus is a gold label file (array of trajectory
19
- * entries with `incorrect_stages[].incorrect_step_ids`) plus a prepared
20
- * directory:
21
- * <prepared>/normalized/<traj_id>/steps.json normalized steps
22
- * <prepared>/normalized/<traj_id>/task.md optional task statement
23
- * <prepared>/extracted/<traj_id>/swe_raw/** raw mini-SWE trajectory
20
+ * The framework does NOT drive the agent — it sets up the replay
21
+ * context (prior spans, prior state, mutation spec) and records the
22
+ * resulting divergence. Consumers supply an `executeFrom(ctx)` callback
23
+ * that runs their agent starting from turn N with the mutation applied.
24
24
  *
25
- * Only trajectories that record their environment are replayable: the raw
26
- * trajectory must carry `info.docker_config.base_image`. Every excluded case
27
- * is returned with a machine-readable reason — the batch report surfaces the
28
- * full exclusion table, never a silently shrunk denominator.
25
+ * Counterfactual runs are recorded as a new Run with `layer='meta'` and
26
+ * `parentRunId = originalRunId`, so downstream diff + correlation
27
+ * pipelines see them natively.
29
28
  */
30
- /** Parses `name=<labelsPath>::<preparedDir>` (paths may contain `=`, not `::`). */
31
- function parseCorpusFlag(value) {
32
- const eq = value.indexOf("=");
33
- if (eq <= 0) throw new Error(`--corpus must be name=<labels>::<prepared>, got: ${value}`);
34
- const name = value.slice(0, eq);
35
- const rest = value.slice(eq + 1);
36
- const sep = rest.indexOf("::");
37
- if (sep <= 0 || sep === rest.length - 2) throw new Error(`--corpus must be name=<labels>::<prepared>, got: ${value}`);
29
+ async function runCounterfactual(store, originalRunId, mutation, runner) {
30
+ const originalRun = await store.getRun(originalRunId);
31
+ if (!originalRun) throw new NotFoundError(`counterfactual: run ${originalRunId} not found`);
32
+ const trajectory = await buildTrajectory(store, originalRunId);
33
+ if (mutation.at < 0 || mutation.at >= trajectory.steps.length) throw new ValidationError(`counterfactual: mutation.at=${mutation.at} out of range [0, ${trajectory.steps.length})`);
34
+ const targetStep = trajectory.steps[mutation.at];
35
+ const mutatedStep = applyMutation(targetStep, mutation);
36
+ const cfEmitter = new TraceEmitter(store);
37
+ await cfEmitter.startRun({
38
+ scenarioId: originalRun.scenarioId,
39
+ variantId: originalRun.variantId ? `${originalRun.variantId}+cf:${mutation.kind}@${mutation.at}` : `cf:${mutation.kind}@${mutation.at}`,
40
+ projectId: originalRun.projectId,
41
+ parentRunId: originalRunId,
42
+ layer: "meta",
43
+ tags: {
44
+ counterfactual: "true",
45
+ mutationKind: mutation.kind,
46
+ mutationAt: String(mutation.at)
47
+ }
48
+ });
49
+ await runner.executeFrom({
50
+ originalRunId,
51
+ originalTrajectory: trajectory,
52
+ prefix: trajectory.steps.slice(0, mutation.at),
53
+ mutation,
54
+ mutatedStep
55
+ }, cfEmitter);
56
+ const counterfactual = await store.getRun(cfEmitter.runId);
57
+ const delta = {
58
+ originalOutcomeScore: originalRun.outcome?.score ?? null,
59
+ counterfactualOutcomeScore: counterfactual?.outcome?.score ?? null,
60
+ deltaScore: originalRun.outcome?.score !== void 0 && counterfactual?.outcome?.score !== void 0 ? counterfactual.outcome.score - originalRun.outcome.score : null
61
+ };
38
62
  return {
39
- name,
40
- labelsPath: rest.slice(0, sep),
41
- preparedDir: rest.slice(sep + 2)
63
+ counterfactualRunId: cfEmitter.runId,
64
+ originalRunId,
65
+ mutation,
66
+ delta
42
67
  };
43
68
  }
44
- function readLabelEntries(labelsPath) {
45
- const parsed = JSON.parse(readFileSync(labelsPath, "utf8"));
46
- if (!Array.isArray(parsed)) throw new Error(`${labelsPath} is not a JSON array of label entries`);
47
- for (const entry of parsed) if (typeof entry.traj_id !== "string") throw new Error(`${labelsPath} has an entry without a string traj_id`);
48
- return parsed;
49
- }
50
- function goldIncorrectSteps(entry) {
51
- const ids = /* @__PURE__ */ new Set();
52
- for (const stage of entry.incorrect_stages ?? []) for (const id of stage.incorrect_step_ids ?? []) ids.add(id);
53
- return [...ids].sort((a, b) => a - b);
54
- }
55
- function findRawTrajFiles(sweRawDir) {
56
- if (!existsSync(sweRawDir)) return [];
57
- return readdirSync(sweRawDir, {
58
- recursive: true,
59
- encoding: "utf8"
60
- }).filter((relative) => relative.endsWith(".traj.json")).map((relative) => join(sweRawDir, relative)).sort();
61
- }
62
- /** First non-empty output line of the trajectory's first bare `pwd` step. */
63
- function cwdFromPwdObservation(steps) {
64
- for (const step of steps) {
65
- if (step.action.trim() !== "pwd") continue;
66
- const line = parseObservationOutput(step.observation).split("\n").map((l) => l.trim()).find((l) => l.startsWith("/"));
67
- if (line) return line;
68
- }
69
- return null;
70
- }
71
- function taskStatementOf(preparedDir, trajId, raw) {
72
- const taskPath = join(preparedDir, "normalized", trajId, "task.md");
73
- if (existsSync(taskPath)) {
74
- const text = readFileSync(taskPath, "utf8").trim();
75
- if (text.length > 0) return text;
76
- }
77
- const userMessage = raw.messages?.find((m) => m.role === "user");
78
- if (typeof userMessage?.content === "string" && userMessage.content.trim().length > 0) return userMessage.content.trim();
79
- return null;
80
- }
81
- /**
82
- * Resolves the replay resources for one trajectory, independent of gold
83
- * labels — the finding wire uses this with a finding-supplied step instead.
84
- */
85
- function resolveCaseResources(corpus, trajId) {
86
- const sweRawDir = join(corpus.preparedDir, "extracted", trajId, "swe_raw");
87
- const rawFiles = findRawTrajFiles(sweRawDir);
88
- if (rawFiles.length === 0) return {
89
- resolved: false,
90
- reason: "no-swe-raw-trajectory",
91
- detail: sweRawDir
92
- };
93
- if (rawFiles.length > 1) return {
94
- resolved: false,
95
- reason: "ambiguous-swe-raw-trajectory",
96
- detail: rawFiles.join(", ")
97
- };
98
- let raw;
99
- try {
100
- raw = JSON.parse(readFileSync(rawFiles[0], "utf8"));
101
- } catch (err) {
69
+ function applyMutation(step, mutation) {
70
+ if (mutation.kind === "swap-model" && step.span.kind === "llm") {
71
+ const llm = step.span;
102
72
  return {
103
- resolved: false,
104
- reason: "unreadable-raw-trajectory",
105
- detail: `${rawFiles[0]}: ${err instanceof Error ? err.message : String(err)}`
73
+ ...step,
74
+ span: {
75
+ ...llm,
76
+ model: mutation.newModel
77
+ }
106
78
  };
107
79
  }
108
- const dockerConfig = raw.info?.docker_config;
109
- const image = dockerConfig?.base_image;
110
- if (typeof image !== "string" || image.length === 0) return {
111
- resolved: false,
112
- reason: "no-docker-image",
113
- detail: rawFiles[0]
114
- };
115
- const stepsPath = join(corpus.preparedDir, "normalized", trajId, "steps.json");
116
- if (!existsSync(stepsPath)) return {
117
- resolved: false,
118
- reason: "missing-steps-json",
119
- detail: stepsPath
120
- };
121
- const steps = JSON.parse(readFileSync(stepsPath, "utf8"));
122
- const runConfigCwd = raw.info?.config?.environment?.cwd;
123
- const dockerCwd = dockerConfig?.cwd;
124
- let cwd;
125
- let cwdSource;
126
- if (typeof runConfigCwd === "string" && runConfigCwd.length > 0) {
127
- cwd = runConfigCwd;
128
- cwdSource = "run-config";
129
- } else if (typeof dockerCwd === "string" && dockerCwd.length > 0) {
130
- cwd = dockerCwd;
131
- cwdSource = "docker-config";
132
- } else {
133
- const observed = cwdFromPwdObservation(steps);
134
- if (!observed) return {
135
- resolved: false,
136
- reason: "cwd-underivable"
80
+ if (mutation.kind === "swap-tool-result" && step.span.kind === "tool") {
81
+ const tool = step.span;
82
+ return {
83
+ ...step,
84
+ span: {
85
+ ...tool,
86
+ result: mutation.newResult
87
+ }
137
88
  };
138
- cwd = observed;
139
- cwdSource = "pwd-observation";
140
89
  }
141
- const recordedTimeout = raw.info?.config?.environment?.timeout;
142
- return {
143
- resolved: true,
144
- resources: {
145
- corpus: corpus.name,
146
- trajId,
147
- stepsPath,
148
- steps,
149
- taskStatement: taskStatementOf(corpus.preparedDir, trajId, raw),
150
- image,
151
- cwd,
152
- cwdSource,
153
- recordedStepTimeoutMs: typeof recordedTimeout === "number" && recordedTimeout > 0 ? recordedTimeout * 1e3 : null
154
- }
155
- };
156
- }
157
- /**
158
- * Replayable = a raw trajectory with a recorded image AND at least one gold
159
- * incorrect step that is a real mid-trajectory action. Gold steps whose action
160
- * is the submit command are skipped when choosing k (counted per case); a case
161
- * whose golds are ALL submit steps is excluded as gold-only-submit-step.
162
- * Exclusion reasons are reported in resolution order:
163
- * raw trajectory → image → gold labels → steps.json → k range → cwd.
164
- */
165
- function enumerateReplayableCases(corpora) {
166
- const replayable = [];
167
- const excluded = [];
168
- let labelEntryCount = 0;
169
- for (const corpus of corpora) for (const entry of readLabelEntries(corpus.labelsPath)) {
170
- labelEntryCount += 1;
171
- const trajId = entry.traj_id;
172
- const resolution = resolveCaseResources(corpus, trajId);
173
- if (!resolution.resolved) {
174
- excluded.push({
175
- corpus: corpus.name,
176
- trajId,
177
- reason: resolution.reason,
178
- detail: resolution.detail
179
- });
180
- continue;
181
- }
182
- const gold = goldIncorrectSteps(entry);
183
- if (gold.length === 0) {
184
- excluded.push({
185
- corpus: corpus.name,
186
- trajId,
187
- reason: "no-gold-incorrect-step"
188
- });
189
- continue;
190
- }
191
- const resources = resolution.resources;
192
- let submitGoldsSkipped = 0;
193
- let target = null;
194
- let missingGoldId = null;
195
- for (const goldId of gold) {
196
- const step = resources.steps.find((s) => s.step_id === goldId);
197
- if (!step) {
198
- missingGoldId = goldId;
199
- break;
200
- }
201
- if (step.action.trim() === "submit" || isSubmitOnlyAction(step.action)) {
202
- submitGoldsSkipped += 1;
203
- continue;
90
+ if (mutation.kind === "inject-system-message" && step.span.kind === "llm") {
91
+ const llm = step.span;
92
+ return {
93
+ ...step,
94
+ span: {
95
+ ...llm,
96
+ messages: [{
97
+ role: "system",
98
+ content: mutation.content
99
+ }, ...llm.messages]
204
100
  }
205
- target = step;
206
- break;
207
- }
208
- if (missingGoldId !== null) {
209
- excluded.push({
210
- corpus: corpus.name,
211
- trajId,
212
- reason: "gold-step-outside-steps",
213
- detail: `k=${missingGoldId}, steps=${resources.steps.length}`
214
- });
215
- continue;
216
- }
217
- if (!target) {
218
- excluded.push({
219
- corpus: corpus.name,
220
- trajId,
221
- reason: "gold-only-submit-step",
222
- detail: `${submitGoldsSkipped} gold step(s), all submit commands`
223
- });
224
- continue;
225
- }
226
- const recordedReturncodeAtK = parseRecordedReturncode(target.observation);
227
- if (recordedReturncodeAtK === null) {
228
- excluded.push({
229
- corpus: corpus.name,
230
- trajId,
231
- reason: "no-recorded-returncode-at-k",
232
- detail: `k=${target.step_id}; arm A cannot confirm execution without a recorded returncode`
233
- });
234
- continue;
235
- }
236
- replayable.push({
237
- ...resources,
238
- goldIncorrectSteps: gold,
239
- k: target.step_id,
240
- submitGoldsSkipped,
241
- recordedReturncodeAtK
242
- });
101
+ };
243
102
  }
244
- return {
245
- replayable,
246
- excluded,
247
- labelEntryCount
248
- };
103
+ if (mutation.kind === "custom") return mutation.apply(step);
104
+ return step;
249
105
  }
250
106
  //#endregion
251
107
  //#region src/trajectory-replay/fix.ts
@@ -525,129 +381,9 @@ async function runFixLoop(caller, input, executor, options) {
525
381
  };
526
382
  }
527
383
  //#endregion
528
- //#region src/trajectory-replay/image-preparer.ts
384
+ //#region src/trajectory-replay/verify.ts
529
385
  /**
530
- * Replay-ready image derivation.
531
- *
532
- * A recorded trajectory names the image it ran in, but the image is not always
533
- * runnable as-is: sandbox platforms pin customer commands to a non-root
534
- * identity, so a root-owned working tree must be chowned before the replay can
535
- * write to it. `ImagePreparer` is that step, injectable so a consumer whose
536
- * images are already replay-ready supplies its own no-op or none at all.
537
- */
538
- const execFileAsync = promisify(execFile);
539
- function derivedImageTag(image, cwd) {
540
- return `ctb-replay:${createHash("sha256").update(`${image}\n${cwd}`).digest("hex").slice(0, 12)}-uid1000`;
541
- }
542
- /**
543
- * Pulls the base image when absent and builds `FROM <base>; RUN chown -R
544
- * 1000:1000 <cwd>` tagged by content hash, so repeated batches reuse both
545
- * the pull and the build. cwd `/` skips the chown (never chown -R /) and
546
- * replays on the base image directly.
547
- */
548
- function dockerImagePreparer(options = {}) {
549
- const pullTimeoutMs = options.pullTimeoutMs ?? 12e5;
550
- const buildTimeoutMs = options.buildTimeoutMs ?? 9e5;
551
- const imageExists = async (tag) => {
552
- try {
553
- await execFileAsync("docker", [
554
- "image",
555
- "inspect",
556
- tag
557
- ], { maxBuffer: 8388608 });
558
- return true;
559
- } catch {
560
- return false;
561
- }
562
- };
563
- const errorTail = (err) => {
564
- return (err && typeof err === "object" && "stderr" in err && typeof err.stderr === "string" && err.stderr.length > 0 ? err.stderr : err instanceof Error ? err.message : String(err)).trim().split("\n").slice(-3).join(" | ").slice(0, 400);
565
- };
566
- return { async ensure(image, cwd) {
567
- if (cwd === "/") {
568
- if (await imageExists(image)) return {
569
- succeeded: true,
570
- value: {
571
- derivedImage: image,
572
- pulled: false,
573
- built: false
574
- }
575
- };
576
- try {
577
- await execFileAsync("docker", ["pull", image], {
578
- timeout: pullTimeoutMs,
579
- maxBuffer: 33554432
580
- });
581
- } catch (err) {
582
- return {
583
- succeeded: false,
584
- error: `pull ${image}: ${errorTail(err)}`
585
- };
586
- }
587
- return {
588
- succeeded: true,
589
- value: {
590
- derivedImage: image,
591
- pulled: true,
592
- built: false
593
- }
594
- };
595
- }
596
- const derived = derivedImageTag(image, cwd);
597
- if (await imageExists(derived)) return {
598
- succeeded: true,
599
- value: {
600
- derivedImage: derived,
601
- pulled: false,
602
- built: false
603
- }
604
- };
605
- let pulled = false;
606
- if (!await imageExists(image)) try {
607
- await execFileAsync("docker", ["pull", image], {
608
- timeout: pullTimeoutMs,
609
- maxBuffer: 33554432
610
- });
611
- pulled = true;
612
- } catch (err) {
613
- return {
614
- succeeded: false,
615
- error: `pull ${image}: ${errorTail(err)}`
616
- };
617
- }
618
- const contextDir = mkdtempSync(join(tmpdir(), "ctb-replay-image-"));
619
- const quotedCwd = `'${cwd.replaceAll("'", `'\\''`)}'`;
620
- writeFileSync(join(contextDir, "Dockerfile"), `FROM ${image}\nRUN chown -R 1000:1000 ${quotedCwd}\n`);
621
- try {
622
- await execFileAsync("docker", [
623
- "build",
624
- "-t",
625
- derived,
626
- contextDir
627
- ], {
628
- timeout: buildTimeoutMs,
629
- maxBuffer: 33554432
630
- });
631
- } catch (err) {
632
- return {
633
- succeeded: false,
634
- error: `build ${derived} from ${image}: ${errorTail(err)}`
635
- };
636
- }
637
- return {
638
- succeeded: true,
639
- value: {
640
- derivedImage: derived,
641
- pulled,
642
- built: true
643
- }
644
- };
645
- } };
646
- }
647
- //#endregion
648
- //#region src/trajectory-replay/verify.ts
649
- /**
650
- * Execution-replay verification for recorded shell trajectories.
386
+ * Execution-replay verification for recorded shell trajectories.
651
387
  *
652
388
  * Turns a cited claim ("step k is the error-critical step") into an executed
653
389
  * proof: replay steps 1..k-1 inside the trajectory's own image, then
@@ -1037,33 +773,8 @@ function renderReport(verdict, target, armA, armB) {
1037
773
  return lines.join("\n");
1038
774
  }
1039
775
  //#endregion
1040
- //#region src/trajectory-replay/batch.ts
1041
- /**
1042
- * Batch replay verification across gold-labeled trajectory corpora.
1043
- *
1044
- * For every replayable case (a raw trajectory with a recorded image AND at
1045
- * least one gold incorrect step) the batch:
1046
- * 1. derives the replay-ready image through the injected `ImagePreparer`,
1047
- * 2. replays the prefix and runs arm A — the recorded gold step k, and
1048
- * 3. optionally generates a corrected command with one LLM call and runs
1049
- * arm B in its own fresh session.
1050
- *
1051
- * Headline metrics:
1052
- * replayability rate — fraction of replayable cases where the prefix
1053
- * replays within the divergence tolerance AND arm A reproduces a
1054
- * recorded failure at k; a zero exit without an error signature is not
1055
- * evidence of failure;
1056
- * prefix fidelity — executed prefix steps and the share of them that did
1057
- * not confirm the recording, split by kind. A corpus whose recordings
1058
- * carry no returncodes shows up here as unknown-expectation steps, never
1059
- * as a clean replay;
1060
- * fix-flip rate — fraction of arm-B-executed cases where the failure
1061
- * vanished (exit 0, signature absent).
1062
- *
1063
- * Image pulls and execs run strictly serially: pulls contend on disk and
1064
- * registry bandwidth, and serial cases keep wall-time attribution per case
1065
- * honest. Pull failures are report rows, never silent skips.
1066
- */
776
+ //#region src/trajectory-replay/batch-fix.ts
777
+ /** Arm B and the counterfactual fix phase of a replay batch. */
1067
778
  function seededSample(items, size, seed) {
1068
779
  const pool = [...items];
1069
780
  const random = mulberry32(seed);
@@ -1116,366 +827,793 @@ async function executeArmB(replayCase, fixCommand, backend, signature, stepTimeo
1116
827
  stderr: exec.stderr
1117
828
  };
1118
829
  }
1119
- function rate(numerator, denominator) {
1120
- return denominator > 0 ? numerator / denominator : null;
830
+ /** A fix outcome with nothing measured; callers name what they did measure. */
831
+ function fixResult(measured) {
832
+ return {
833
+ attempted: true,
834
+ sampledOut: false,
835
+ command: null,
836
+ llmError: null,
837
+ usage: null,
838
+ armBExit: null,
839
+ armBPrefixExecuted: null,
840
+ armBPrefixDivergences: null,
841
+ armBPrefixDivergencePct: null,
842
+ failureVanished: null,
843
+ armBError: null,
844
+ attempts: null,
845
+ flippedAtAttempt: null,
846
+ ...measured
847
+ };
1121
848
  }
1122
- async function runReplayBatch(options) {
1123
- const onProgress = options.onProgress ?? (() => {});
1124
- const enumeration = enumerateReplayableCases(options.corpora);
1125
- let selected = enumeration.replayable;
1126
- if (options.caseFilter) selected = selected.filter((c) => c.trajId.includes(options.caseFilter));
1127
- if (options.caseLimit !== void 0) selected = selected.slice(0, options.caseLimit);
1128
- onProgress(`enumerated ${enumeration.labelEntryCount} label entries → ${enumeration.replayable.length} replayable, ${enumeration.excluded.length} excluded; executing ${selected.length}`);
1129
- const preparer = options.preparer ?? dockerImagePreparer();
1130
- const backendFactory = options.backendFactory;
1131
- mkdirSync(options.out, { recursive: true });
1132
- const progressPath = join(options.out, "cases.jsonl");
1133
- const rows = [];
1134
- const pullFailures = [];
1135
- const verdictByTraj = /* @__PURE__ */ new Map();
1136
- for (const [index, replayCase] of selected.entries()) {
1137
- const caseStart = Date.now();
1138
- const label = `[${index + 1}/${selected.length}] ${replayCase.corpus}/${replayCase.trajId}`;
1139
- onProgress(`${label}: preparing image ${replayCase.image}`);
1140
- const preparation = await preparer.ensure(replayCase.image, replayCase.cwd);
1141
- const base = {
1142
- corpus: replayCase.corpus,
1143
- trajId: replayCase.trajId,
1144
- image: replayCase.image,
1145
- cwd: replayCase.cwd,
1146
- cwdSource: replayCase.cwdSource,
1147
- k: replayCase.k,
1148
- stepCount: replayCase.steps.length,
1149
- goldIncorrectSteps: replayCase.goldIncorrectSteps,
1150
- submitGoldsSkipped: replayCase.submitGoldsSkipped,
1151
- recordedReturncodeAtK: replayCase.recordedReturncodeAtK
849
+ function runArmB(run, fixCase, command, label) {
850
+ const { options } = run;
851
+ return executeArmB(fixCase.replayCase, command, options.backendFactory(fixCase.row.derivedImage), fixCase.signature, fixCase.stepTimeoutMs, options.prefixLimit, (message) => run.onProgress(`${label}: ${message}`));
852
+ }
853
+ /** The fix summary a finished loop reports: the flipping attempt, else the last executed one, else why it stopped. */
854
+ function loopFixResult(result, usage) {
855
+ const summary = (result.flippedAtAttempt !== null ? result.attempts.find((a) => a.attempt === result.flippedAtAttempt) : null) ?? [...result.attempts].reverse().find((a) => a.executed) ?? null;
856
+ const lastRecord = result.attempts.at(-1) ?? null;
857
+ if (summary) return fixResult({
858
+ command: summary.command,
859
+ usage,
860
+ armBExit: summary.exitCode,
861
+ armBPrefixExecuted: summary.prefixExecuted,
862
+ armBPrefixDivergences: summary.prefixDivergences,
863
+ armBPrefixDivergencePct: summary.prefixDivergencePct,
864
+ failureVanished: summary.failureVanished,
865
+ attempts: result.attempts,
866
+ flippedAtAttempt: result.flippedAtAttempt
867
+ });
868
+ if (result.aborted) return fixResult({
869
+ command: lastRecord?.command ?? null,
870
+ usage,
871
+ armBError: lastRecord?.armBError ?? "sandbox error",
872
+ attempts: result.attempts
873
+ });
874
+ return fixResult({
875
+ llmError: lastRecord?.llmError ?? "no attempt produced a runnable fix",
876
+ usage,
877
+ attempts: result.attempts
878
+ });
879
+ }
880
+ /** Iterative fix: failed arms feed their real output into up to `fixAttempts` prompts. */
881
+ async function fixByLoop(run, fixCase, caller, tally) {
882
+ const { options, onProgress } = run;
883
+ const { row, label, caseOut } = fixCase;
884
+ const fixAttempts = options.fixAttempts ?? 3;
885
+ onProgress(`${label}: fix loop (budget ${fixAttempts} attempts)`);
886
+ const result = await runFixLoop(caller, fixCase.promptInput, async (command, attempt) => {
887
+ onProgress(`${label}: attempt ${attempt} arm B — ${command.split("\n")[0].slice(0, 120)}`);
888
+ const armB = await runArmB(run, fixCase, command, `${label}: attempt ${attempt}`);
889
+ mkdirSync(caseOut, { recursive: true });
890
+ writeFileSync(join(caseOut, `armB-attempt${attempt}-result.json`), `${JSON.stringify({
891
+ command,
892
+ ...armB
893
+ }, null, 2)}\n`);
894
+ writeFileSync(join(caseOut, "armB-result.json"), `${JSON.stringify({
895
+ command,
896
+ attempt,
897
+ ...armB
898
+ }, null, 2)}\n`);
899
+ return armB;
900
+ }, {
901
+ maxAttempts: fixAttempts,
902
+ onProgress: (message) => onProgress(`${label}: ${message}`)
903
+ });
904
+ tally.calls += result.llmCalls;
905
+ tally.failures += result.llmFailures;
906
+ tally.promptTokens += result.promptTokens;
907
+ tally.completionTokens += result.completionTokens;
908
+ tally.callsWithoutUsage += result.callsWithoutUsage;
909
+ const usage = result.promptTokens + result.completionTokens > 0 ? {
910
+ promptTokens: result.promptTokens,
911
+ completionTokens: result.completionTokens
912
+ } : null;
913
+ onProgress(`${label}: loop done — flipped=${result.flipped}` + (result.flippedAtAttempt !== null ? ` at attempt ${result.flippedAtAttempt}` : "") + ` (${result.llmCalls} calls, ${result.attempts.filter((a) => a.executed).length} arms)`);
914
+ return {
915
+ ...row,
916
+ fix: loopFixResult(result, usage)
917
+ };
918
+ }
919
+ /** One-shot fix: a single generated command, one arm B. */
920
+ async function fixByOneShot(run, fixCase, caller, tally) {
921
+ const { onProgress } = run;
922
+ const { row, label, caseOut } = fixCase;
923
+ onProgress(`${label}: generating corrected command`);
924
+ tally.calls += 1;
925
+ const generated = await generateFixCommand(caller, fixCase.promptInput);
926
+ if (!generated.succeeded) {
927
+ tally.failures += 1;
928
+ onProgress(`${label}: LLM failed — ${generated.error.slice(0, 200)}`);
929
+ return {
930
+ ...row,
931
+ fix: fixResult({ llmError: generated.error })
1152
932
  };
1153
- if (!preparation.succeeded) {
1154
- pullFailures.push({
1155
- corpus: replayCase.corpus,
1156
- trajId: replayCase.trajId,
1157
- image: replayCase.image,
1158
- error: preparation.error
933
+ }
934
+ if (generated.value.usage === null || generated.value.usage === void 0) tally.callsWithoutUsage += 1;
935
+ tally.promptTokens += generated.value.usage?.promptTokens ?? 0;
936
+ tally.completionTokens += generated.value.usage?.completionTokens ?? 0;
937
+ onProgress(`${label}: arm B — ${generated.value.command.split("\n")[0].slice(0, 120)}`);
938
+ try {
939
+ const armB = await runArmB(run, fixCase, generated.value.command, label);
940
+ const fix = fixResult({
941
+ command: generated.value.command,
942
+ usage: generated.value.usage,
943
+ armBExit: armB.exitCode,
944
+ armBPrefixExecuted: armB.prefixExecuted,
945
+ armBPrefixDivergences: armB.prefixDivergences,
946
+ armBPrefixDivergencePct: armB.prefixDivergencePct,
947
+ failureVanished: armB.failureVanished,
948
+ flippedAtAttempt: armB.failureVanished ? 1 : null
949
+ });
950
+ mkdirSync(caseOut, { recursive: true });
951
+ writeFileSync(join(caseOut, "armB-result.json"), `${JSON.stringify({
952
+ command: generated.value.command,
953
+ ...armB
954
+ }, null, 2)}\n`);
955
+ onProgress(`${label}: armB exit=${armB.exitCode} failureVanished=${armB.failureVanished}`);
956
+ return {
957
+ ...row,
958
+ fix
959
+ };
960
+ } catch (err) {
961
+ const message = err instanceof Error ? err.message : String(err);
962
+ onProgress(`${label}: arm B error — ${message.slice(0, 200)}`);
963
+ return {
964
+ ...row,
965
+ fix: fixResult({
966
+ command: generated.value.command,
967
+ usage: generated.value.usage,
968
+ armBError: message.slice(0, 500)
969
+ })
970
+ };
971
+ }
972
+ }
973
+ /** Phase 2: counterfactual fixes for the cases where arm A reproduced, capped and seeded-sampled. */
974
+ async function runFixPhase(run, selected, rows, verdicts) {
975
+ const { options, onProgress } = run;
976
+ const caller = options.fixCaller;
977
+ if (!caller) throw new Error(`trajectory-replay: fix=${options.fix} requires a fixCaller`);
978
+ const maxFixCases = options.maxFixCases ?? 30;
979
+ const seed = options.seed ?? 17;
980
+ const eligible = rows.filter((r) => r.status === "ok" && r.replayed);
981
+ const sampled = eligible.length > maxFixCases ? seededSample(eligible, maxFixCases, seed) : new Set(eligible);
982
+ if (eligible.length > maxFixCases) onProgress(`fix phase: ${eligible.length} eligible > cap ${maxFixCases}; seeded sample (seed=${seed})`);
983
+ const tally = {
984
+ calls: 0,
985
+ failures: 0,
986
+ promptTokens: 0,
987
+ completionTokens: 0,
988
+ callsWithoutUsage: 0
989
+ };
990
+ for (const row of eligible) {
991
+ const index = rows.indexOf(row);
992
+ if (!sampled.has(row)) {
993
+ rows[index] = {
994
+ ...row,
995
+ fix: fixResult({
996
+ attempted: false,
997
+ sampledOut: true
998
+ })
999
+ };
1000
+ continue;
1001
+ }
1002
+ const replayCase = selected.find((c) => c.trajId === row.trajId && c.corpus === row.corpus);
1003
+ const fixCase = {
1004
+ row,
1005
+ replayCase,
1006
+ label: `fix ${row.corpus}/${row.trajId}`,
1007
+ signature: verdicts.get(row.trajId)?.signature ?? null,
1008
+ stepTimeoutMs: options.stepTimeoutMs ?? replayCase.recordedStepTimeoutMs ?? 12e4,
1009
+ caseOut: join(options.out, caseOutDirName(row)),
1010
+ promptInput: {
1011
+ taskStatement: replayCase.taskStatement,
1012
+ steps: replayCase.steps,
1013
+ k: replayCase.k
1014
+ }
1015
+ };
1016
+ rows[index] = options.fix === "loop" ? await fixByLoop(run, fixCase, caller, tally) : await fixByOneShot(run, fixCase, caller, tally);
1017
+ }
1018
+ return {
1019
+ model: options.fixModelLabel ?? "unknown",
1020
+ ...tally
1021
+ };
1022
+ }
1023
+ //#endregion
1024
+ //#region src/trajectory-replay/batch-report.ts
1025
+ function pct(value) {
1026
+ return value === null ? "—" : `${(value * 100).toFixed(1)}%`;
1027
+ }
1028
+ function renderBatchReport(report) {
1029
+ const lines = [];
1030
+ const { headline, totals } = report;
1031
+ lines.push("# Replay-verify batch report");
1032
+ lines.push("");
1033
+ lines.push(`Generated ${report.generatedAt}.`);
1034
+ lines.push("");
1035
+ lines.push("## Headline");
1036
+ lines.push("");
1037
+ const fidelity = headline.prefixFidelity;
1038
+ lines.push(`- **Replayability rate: ${pct(headline.replayabilityRate.value)}** (${headline.replayabilityRate.numerator}/${headline.replayabilityRate.denominator} replayable cases where the prefix replayed within ${fidelity.tolerancePct}% divergence AND arm A reproduced an observed failure at the gold step k).`);
1039
+ lines.push(`- Signature-strict rate: ${pct(headline.signatureStrictRate.value)} (${headline.signatureStrictRate.numerator}/${headline.signatureStrictRate.denominator}; additionally requires the recorded error substring in arm A output).`);
1040
+ lines.push(`- **Prefix divergence: ${fidelity.divergencePct === null ? "—" : `${fidelity.divergencePct}%`}** (${fidelity.divergentSteps}/${fidelity.executedSteps} executed prefix steps did not confirm the recording: ${fidelity.returncodeMismatches} returncode mismatches, ${fidelity.unknownExpectations} with no recorded returncode to check). ${fidelity.casesWithinTolerance}/${fidelity.casesExecuted} executed cases are within the ${fidelity.tolerancePct}% tolerance.`);
1041
+ if (headline.fixFlipRate) lines.push(`- **Fix-flip rate: ${pct(headline.fixFlipRate.value)}** (${headline.fixFlipRate.numerator}/${headline.fixFlipRate.denominator} arm-B-executed cases where the generated fix made the failure vanish).`);
1042
+ if (headline.fixFlipRateNonzeroRc) lines.push(`- Fix-flip rate on recorded-rc≠0 cases: ${pct(headline.fixFlipRateNonzeroRc.value)} (${headline.fixFlipRateNonzeroRc.numerator}/${headline.fixFlipRateNonzeroRc.denominator}; real recorded failures — a gold step recorded with rc 0 flips vacuously).`);
1043
+ if (headline.fixFlipAttempt1) lines.push(`- Fix-flip@1: ${pct(headline.fixFlipAttempt1.value)} (${headline.fixFlipAttempt1.numerator}/${headline.fixFlipAttempt1.denominator} cases whose attempt 1 executed — the one-shot-comparable number).`);
1044
+ if (headline.flipsByAttempt && Object.keys(headline.flipsByAttempt).length > 0) {
1045
+ const parts = Object.entries(headline.flipsByAttempt).sort((a, b) => Number(a[0]) - Number(b[0])).map(([attempt, count]) => `attempt ${attempt}: ${count}`);
1046
+ lines.push(`- Flips by attempt: ${parts.join(", ")}.`);
1047
+ }
1048
+ lines.push("");
1049
+ lines.push("## Enumeration");
1050
+ lines.push("");
1051
+ lines.push(`${totals.labelEntries} label entries across ${report.corpora.length} corpora → ${totals.replayable} replayable (SWE-style docker image + ≥1 gold incorrect step), ${totals.executed} executed.`);
1052
+ lines.push("");
1053
+ lines.push("| exclusion reason | count |");
1054
+ lines.push("| --- | --- |");
1055
+ for (const [reason, count] of Object.entries(totals.excludedByReason).sort((a, b) => b[1] - a[1])) lines.push(`| ${reason} | ${count} |`);
1056
+ const submitEntries = Object.entries(totals.submitGoldsByCorpus);
1057
+ if (submitEntries.length > 0) {
1058
+ lines.push("");
1059
+ lines.push("Submit-command golds are never counterfactual targets (a gold on the submit step marks a bad submit decision, not a failed command):");
1060
+ lines.push("");
1061
+ lines.push("| corpus | cases excluded (all golds = submit) | golds skipped within replayable cases |");
1062
+ lines.push("| --- | --- | --- |");
1063
+ for (const [corpus, stats] of submitEntries.sort((a, b) => a[0].localeCompare(b[0]))) lines.push(`| ${corpus} | ${stats.submitOnlyCases} | ${stats.goldsSkippedWithinReplayable} |`);
1064
+ }
1065
+ if (report.pullFailures.length > 0) {
1066
+ lines.push("");
1067
+ lines.push("## Image pull/build failures");
1068
+ lines.push("");
1069
+ lines.push("| corpus | trajectory | image | error |");
1070
+ lines.push("| --- | --- | --- | --- |");
1071
+ for (const failure of report.pullFailures) lines.push(`| ${failure.corpus} | ${failure.trajId} | \`${failure.image}\` | ${failure.error.replaceAll("|", "\\|")} |`);
1072
+ }
1073
+ lines.push("");
1074
+ lines.push("## Per-case results");
1075
+ lines.push("");
1076
+ lines.push("| corpus | trajectory | k | rc@k | prefix | confirmed | rc mismatch | unknown rc | div | div% | armA exit | rc match | sig match | replayed | fix | armB exit | armB div% | vanished | wall s |");
1077
+ lines.push(`| ${Array(19).fill("---").join(" | ")} |`);
1078
+ for (const row of report.cases) {
1079
+ const fix = row.fix;
1080
+ const fixCell = !fix ? "—" : fix.sampledOut ? "sampled-out" : fix.llmError ? "llm-failed" : fix.armBError ? "armB-error" : fix.attempts ? fix.flippedAtAttempt !== null ? `flip@${fix.flippedAtAttempt}` : `exhausted(${fix.attempts.length})` : "generated";
1081
+ lines.push([
1082
+ row.corpus,
1083
+ row.trajId.length > 48 ? `${row.trajId.slice(0, 45)}…` : row.trajId,
1084
+ row.k,
1085
+ row.recordedReturncodeAtK ?? "null",
1086
+ row.prefixExecuted ?? "—",
1087
+ row.prefixConfirmed ?? "—",
1088
+ row.prefixReturncodeMismatches ?? "—",
1089
+ row.prefixUnknownExpectations ?? "—",
1090
+ row.prefixDivergences ?? "—",
1091
+ row.prefixDivergencePct ?? "—",
1092
+ row.status === "ok" ? row.armAExit : row.status,
1093
+ row.armAReturncodeMatch ? "yes" : "no",
1094
+ row.armASignatureMatch ? "yes" : "no",
1095
+ row.replayed ? "**yes**" : "no",
1096
+ fixCell,
1097
+ fix?.armBExit ?? "—",
1098
+ fix?.armBPrefixDivergencePct ?? "—",
1099
+ fix?.failureVanished === null || fix === null ? "—" : fix.failureVanished ? "**yes**" : "no",
1100
+ (row.wallMs / 1e3).toFixed(1)
1101
+ ].join(" | "));
1102
+ }
1103
+ if (report.llm) {
1104
+ lines.push("");
1105
+ lines.push("## LLM fix generation");
1106
+ lines.push("");
1107
+ lines.push(`Model ${report.llm.model}: ${report.llm.calls} calls (${report.llm.failures} failed), ${report.llm.promptTokens} prompt + ${report.llm.completionTokens} completion tokens.`);
1108
+ }
1109
+ lines.push("");
1110
+ return lines.join("\n");
1111
+ }
1112
+ //#endregion
1113
+ //#region src/trajectory-replay/corpus.ts
1114
+ /**
1115
+ * Corpus enumeration for batch replay verification.
1116
+ *
1117
+ * A labeled trajectory corpus is a gold label file (array of trajectory
1118
+ * entries with `incorrect_stages[].incorrect_step_ids`) plus a prepared
1119
+ * directory:
1120
+ * <prepared>/normalized/<traj_id>/steps.json normalized steps
1121
+ * <prepared>/normalized/<traj_id>/task.md optional task statement
1122
+ * <prepared>/extracted/<traj_id>/swe_raw/** raw mini-SWE trajectory
1123
+ *
1124
+ * Only trajectories that record their environment are replayable: the raw
1125
+ * trajectory must carry `info.docker_config.base_image`. Every excluded case
1126
+ * is returned with a machine-readable reason — the batch report surfaces the
1127
+ * full exclusion table, never a silently shrunk denominator.
1128
+ */
1129
+ function readLabelEntries(labelsPath) {
1130
+ const parsed = JSON.parse(readFileSync(labelsPath, "utf8"));
1131
+ if (!Array.isArray(parsed)) throw new Error(`${labelsPath} is not a JSON array of label entries`);
1132
+ for (const entry of parsed) if (typeof entry.traj_id !== "string") throw new Error(`${labelsPath} has an entry without a string traj_id`);
1133
+ return parsed;
1134
+ }
1135
+ function goldIncorrectSteps(entry) {
1136
+ const ids = /* @__PURE__ */ new Set();
1137
+ for (const stage of entry.incorrect_stages ?? []) for (const id of stage.incorrect_step_ids ?? []) ids.add(id);
1138
+ return [...ids].sort((a, b) => a - b);
1139
+ }
1140
+ function findRawTrajFiles(sweRawDir) {
1141
+ if (!existsSync(sweRawDir)) return [];
1142
+ return readdirSync(sweRawDir, {
1143
+ recursive: true,
1144
+ encoding: "utf8"
1145
+ }).filter((relative) => relative.endsWith(".traj.json")).map((relative) => join(sweRawDir, relative)).sort();
1146
+ }
1147
+ /** First non-empty output line of the trajectory's first bare `pwd` step. */
1148
+ function cwdFromPwdObservation(steps) {
1149
+ for (const step of steps) {
1150
+ if (step.action.trim() !== "pwd") continue;
1151
+ const line = parseObservationOutput(step.observation).split("\n").map((l) => l.trim()).find((l) => l.startsWith("/"));
1152
+ if (line) return line;
1153
+ }
1154
+ return null;
1155
+ }
1156
+ function taskStatementOf(preparedDir, trajId, raw) {
1157
+ const taskPath = join(preparedDir, "normalized", trajId, "task.md");
1158
+ if (existsSync(taskPath)) {
1159
+ const text = readFileSync(taskPath, "utf8").trim();
1160
+ if (text.length > 0) return text;
1161
+ }
1162
+ const userMessage = raw.messages?.find((m) => m.role === "user");
1163
+ if (typeof userMessage?.content === "string" && userMessage.content.trim().length > 0) return userMessage.content.trim();
1164
+ return null;
1165
+ }
1166
+ /**
1167
+ * Resolves the replay resources for one trajectory, independent of gold
1168
+ * labels — the finding wire uses this with a finding-supplied step instead.
1169
+ */
1170
+ function resolveCaseResources(corpus, trajId) {
1171
+ const sweRawDir = join(corpus.preparedDir, "extracted", trajId, "swe_raw");
1172
+ const rawFiles = findRawTrajFiles(sweRawDir);
1173
+ if (rawFiles.length === 0) return {
1174
+ resolved: false,
1175
+ reason: "no-swe-raw-trajectory",
1176
+ detail: sweRawDir
1177
+ };
1178
+ if (rawFiles.length > 1) return {
1179
+ resolved: false,
1180
+ reason: "ambiguous-swe-raw-trajectory",
1181
+ detail: rawFiles.join(", ")
1182
+ };
1183
+ let raw;
1184
+ try {
1185
+ raw = JSON.parse(readFileSync(rawFiles[0], "utf8"));
1186
+ } catch (err) {
1187
+ return {
1188
+ resolved: false,
1189
+ reason: "unreadable-raw-trajectory",
1190
+ detail: `${rawFiles[0]}: ${err instanceof Error ? err.message : String(err)}`
1191
+ };
1192
+ }
1193
+ const dockerConfig = raw.info?.docker_config;
1194
+ const image = dockerConfig?.base_image;
1195
+ if (typeof image !== "string" || image.length === 0) return {
1196
+ resolved: false,
1197
+ reason: "no-docker-image",
1198
+ detail: rawFiles[0]
1199
+ };
1200
+ const stepsPath = join(corpus.preparedDir, "normalized", trajId, "steps.json");
1201
+ if (!existsSync(stepsPath)) return {
1202
+ resolved: false,
1203
+ reason: "missing-steps-json",
1204
+ detail: stepsPath
1205
+ };
1206
+ const steps = JSON.parse(readFileSync(stepsPath, "utf8"));
1207
+ const runConfigCwd = raw.info?.config?.environment?.cwd;
1208
+ const dockerCwd = dockerConfig?.cwd;
1209
+ let cwd;
1210
+ let cwdSource;
1211
+ if (typeof runConfigCwd === "string" && runConfigCwd.length > 0) {
1212
+ cwd = runConfigCwd;
1213
+ cwdSource = "run-config";
1214
+ } else if (typeof dockerCwd === "string" && dockerCwd.length > 0) {
1215
+ cwd = dockerCwd;
1216
+ cwdSource = "docker-config";
1217
+ } else {
1218
+ const observed = cwdFromPwdObservation(steps);
1219
+ if (!observed) return {
1220
+ resolved: false,
1221
+ reason: "cwd-underivable"
1222
+ };
1223
+ cwd = observed;
1224
+ cwdSource = "pwd-observation";
1225
+ }
1226
+ const recordedTimeout = raw.info?.config?.environment?.timeout;
1227
+ return {
1228
+ resolved: true,
1229
+ resources: {
1230
+ corpus: corpus.name,
1231
+ trajId,
1232
+ stepsPath,
1233
+ steps,
1234
+ taskStatement: taskStatementOf(corpus.preparedDir, trajId, raw),
1235
+ image,
1236
+ cwd,
1237
+ cwdSource,
1238
+ recordedStepTimeoutMs: typeof recordedTimeout === "number" && recordedTimeout > 0 ? recordedTimeout * 1e3 : null
1239
+ }
1240
+ };
1241
+ }
1242
+ /**
1243
+ * Replayable = a raw trajectory with a recorded image AND at least one gold
1244
+ * incorrect step that is a real mid-trajectory action. Gold steps whose action
1245
+ * is the submit command are skipped when choosing k (counted per case); a case
1246
+ * whose golds are ALL submit steps is excluded as gold-only-submit-step.
1247
+ * Exclusion reasons are reported in resolution order:
1248
+ * raw trajectory → image → gold labels → steps.json → k range → cwd.
1249
+ */
1250
+ function enumerateReplayableCases(corpora) {
1251
+ const replayable = [];
1252
+ const excluded = [];
1253
+ let labelEntryCount = 0;
1254
+ for (const corpus of corpora) for (const entry of readLabelEntries(corpus.labelsPath)) {
1255
+ labelEntryCount += 1;
1256
+ const trajId = entry.traj_id;
1257
+ const resolution = resolveCaseResources(corpus, trajId);
1258
+ if (!resolution.resolved) {
1259
+ excluded.push({
1260
+ corpus: corpus.name,
1261
+ trajId,
1262
+ reason: resolution.reason,
1263
+ detail: resolution.detail
1264
+ });
1265
+ continue;
1266
+ }
1267
+ const gold = goldIncorrectSteps(entry);
1268
+ if (gold.length === 0) {
1269
+ excluded.push({
1270
+ corpus: corpus.name,
1271
+ trajId,
1272
+ reason: "no-gold-incorrect-step"
1273
+ });
1274
+ continue;
1275
+ }
1276
+ const resources = resolution.resources;
1277
+ let submitGoldsSkipped = 0;
1278
+ let target = null;
1279
+ let missingGoldId = null;
1280
+ for (const goldId of gold) {
1281
+ const step = resources.steps.find((s) => s.step_id === goldId);
1282
+ if (!step) {
1283
+ missingGoldId = goldId;
1284
+ break;
1285
+ }
1286
+ if (step.action.trim() === "submit" || isSubmitOnlyAction(step.action)) {
1287
+ submitGoldsSkipped += 1;
1288
+ continue;
1289
+ }
1290
+ target = step;
1291
+ break;
1292
+ }
1293
+ if (missingGoldId !== null) {
1294
+ excluded.push({
1295
+ corpus: corpus.name,
1296
+ trajId,
1297
+ reason: "gold-step-outside-steps",
1298
+ detail: `k=${missingGoldId}, steps=${resources.steps.length}`
1299
+ });
1300
+ continue;
1301
+ }
1302
+ if (!target) {
1303
+ excluded.push({
1304
+ corpus: corpus.name,
1305
+ trajId,
1306
+ reason: "gold-only-submit-step",
1307
+ detail: `${submitGoldsSkipped} gold step(s), all submit commands`
1308
+ });
1309
+ continue;
1310
+ }
1311
+ const recordedReturncodeAtK = parseRecordedReturncode(target.observation);
1312
+ if (recordedReturncodeAtK === null) {
1313
+ excluded.push({
1314
+ corpus: corpus.name,
1315
+ trajId,
1316
+ reason: "no-recorded-returncode-at-k",
1317
+ detail: `k=${target.step_id}; arm A cannot confirm execution without a recorded returncode`
1318
+ });
1319
+ continue;
1320
+ }
1321
+ replayable.push({
1322
+ ...resources,
1323
+ goldIncorrectSteps: gold,
1324
+ k: target.step_id,
1325
+ submitGoldsSkipped,
1326
+ recordedReturncodeAtK
1327
+ });
1328
+ }
1329
+ return {
1330
+ replayable,
1331
+ excluded,
1332
+ labelEntryCount
1333
+ };
1334
+ }
1335
+ //#endregion
1336
+ //#region src/trajectory-replay/image-preparer.ts
1337
+ /**
1338
+ * Replay-ready image derivation.
1339
+ *
1340
+ * A recorded trajectory names the image it ran in, but the image is not always
1341
+ * runnable as-is: sandbox platforms pin customer commands to a non-root
1342
+ * identity, so a root-owned working tree must be chowned before the replay can
1343
+ * write to it. `ImagePreparer` is that step, injectable so a consumer whose
1344
+ * images are already replay-ready supplies its own no-op or none at all.
1345
+ */
1346
+ const execFileAsync = promisify(execFile);
1347
+ function derivedImageTag(image, cwd) {
1348
+ return `ctb-replay:${createHash("sha256").update(`${image}\n${cwd}`).digest("hex").slice(0, 12)}-uid1000`;
1349
+ }
1350
+ /**
1351
+ * Pulls the base image when absent and builds `FROM <base>; RUN chown -R
1352
+ * 1000:1000 <cwd>` tagged by content hash, so repeated batches reuse both
1353
+ * the pull and the build. cwd `/` skips the chown (never chown -R /) and
1354
+ * replays on the base image directly.
1355
+ */
1356
+ function dockerImagePreparer(options = {}) {
1357
+ const pullTimeoutMs = options.pullTimeoutMs ?? 12e5;
1358
+ const buildTimeoutMs = options.buildTimeoutMs ?? 9e5;
1359
+ const imageExists = async (tag) => {
1360
+ try {
1361
+ await execFileAsync("docker", [
1362
+ "image",
1363
+ "inspect",
1364
+ tag
1365
+ ], { maxBuffer: 8388608 });
1366
+ return true;
1367
+ } catch {
1368
+ return false;
1369
+ }
1370
+ };
1371
+ const errorTail = (err) => {
1372
+ return (err && typeof err === "object" && "stderr" in err && typeof err.stderr === "string" && err.stderr.length > 0 ? err.stderr : err instanceof Error ? err.message : String(err)).trim().split("\n").slice(-3).join(" | ").slice(0, 400);
1373
+ };
1374
+ return { async ensure(image, cwd) {
1375
+ if (cwd === "/") {
1376
+ if (await imageExists(image)) return {
1377
+ succeeded: true,
1378
+ value: {
1379
+ derivedImage: image,
1380
+ pulled: false,
1381
+ built: false
1382
+ }
1383
+ };
1384
+ try {
1385
+ await execFileAsync("docker", ["pull", image], {
1386
+ timeout: pullTimeoutMs,
1387
+ maxBuffer: 33554432
1388
+ });
1389
+ } catch (err) {
1390
+ return {
1391
+ succeeded: false,
1392
+ error: `pull ${image}: ${errorTail(err)}`
1393
+ };
1394
+ }
1395
+ return {
1396
+ succeeded: true,
1397
+ value: {
1398
+ derivedImage: image,
1399
+ pulled: true,
1400
+ built: false
1401
+ }
1402
+ };
1403
+ }
1404
+ const derived = derivedImageTag(image, cwd);
1405
+ if (await imageExists(derived)) return {
1406
+ succeeded: true,
1407
+ value: {
1408
+ derivedImage: derived,
1409
+ pulled: false,
1410
+ built: false
1411
+ }
1412
+ };
1413
+ let pulled = false;
1414
+ if (!await imageExists(image)) try {
1415
+ await execFileAsync("docker", ["pull", image], {
1416
+ timeout: pullTimeoutMs,
1417
+ maxBuffer: 33554432
1159
1418
  });
1160
- const row = {
1161
- ...base,
1162
- derivedImage: null,
1163
- signature: null,
1164
- status: "image-unavailable",
1165
- error: preparation.error,
1166
- imagePulled: false,
1167
- imageBuilt: false,
1168
- prefixExecuted: null,
1169
- prefixDivergences: null,
1170
- prefixDivergencePct: null,
1171
- prefixConfirmed: null,
1172
- prefixReturncodeMismatches: null,
1173
- prefixUnknownExpectations: null,
1174
- armAExit: null,
1175
- armAReturncodeMatch: false,
1176
- armASignatureMatch: false,
1177
- replayed: false,
1178
- fix: null,
1179
- wallMs: Date.now() - caseStart
1419
+ pulled = true;
1420
+ } catch (err) {
1421
+ return {
1422
+ succeeded: false,
1423
+ error: `pull ${image}: ${errorTail(err)}`
1180
1424
  };
1181
- rows.push(row);
1182
- appendFileSync(progressPath, `${JSON.stringify(row)}\n`);
1183
- onProgress(`${label}: image unavailable — ${preparation.error}`);
1184
- continue;
1185
1425
  }
1186
- const { derivedImage, pulled, built } = preparation.value;
1187
- const stepTimeoutMs = options.stepTimeoutMs ?? replayCase.recordedStepTimeoutMs ?? 12e4;
1188
- const caseOut = join(options.out, caseOutDirName(replayCase));
1189
- onProgress(`${label}: arm A on ${derivedImage} (k=${replayCase.k}, timeout ${stepTimeoutMs}ms)`);
1426
+ const contextDir = mkdtempSync(join(tmpdir(), "ctb-replay-image-"));
1427
+ const quotedCwd = `'${cwd.replaceAll("'", `'\\''`)}'`;
1428
+ writeFileSync(join(contextDir, "Dockerfile"), `FROM ${image}\nRUN chown -R 1000:1000 ${quotedCwd}\n`);
1190
1429
  try {
1191
- const verdict = await replayVerify({
1192
- stepsPath: replayCase.stepsPath,
1193
- image: derivedImage,
1194
- at: replayCase.k,
1195
- cwd: replayCase.cwd,
1196
- out: caseOut,
1197
- caseId: replayCase.trajId,
1198
- stepTimeoutMs,
1199
- prefixLimit: options.prefixLimit,
1200
- backend: backendFactory(derivedImage),
1201
- onProgress: (message) => onProgress(`${label}: ${message}`)
1430
+ await execFileAsync("docker", [
1431
+ "build",
1432
+ "-t",
1433
+ derived,
1434
+ contextDir
1435
+ ], {
1436
+ timeout: buildTimeoutMs,
1437
+ maxBuffer: 33554432
1202
1438
  });
1203
- verdictByTraj.set(replayCase.trajId, verdict);
1204
- const returncodeMatch = verdict.recordedReturncode !== null && verdict.armA.exitCode === verdict.recordedReturncode;
1205
- const row = {
1206
- ...base,
1207
- derivedImage,
1208
- signature: verdict.signature,
1209
- status: "ok",
1210
- error: null,
1211
- imagePulled: pulled,
1212
- imageBuilt: built,
1213
- prefixExecuted: verdict.prefixExecuted,
1214
- prefixDivergences: verdict.prefixDivergences.length,
1215
- prefixDivergencePct: verdict.prefixDivergencePct,
1216
- prefixConfirmed: verdict.prefixConfirmed,
1217
- prefixReturncodeMismatches: verdict.prefixReturncodeMismatches,
1218
- prefixUnknownExpectations: verdict.prefixUnknownExpectations,
1219
- armAExit: verdict.armA.exitCode,
1220
- armAReturncodeMatch: returncodeMatch,
1221
- armASignatureMatch: verdict.armA.failureSignatureMatch,
1222
- replayed: verdict.valid,
1223
- fix: null,
1224
- wallMs: Date.now() - caseStart
1225
- };
1226
- rows.push(row);
1227
- appendFileSync(progressPath, `${JSON.stringify(row)}\n`);
1228
- onProgress(`${label}: armA exit=${verdict.armA.exitCode} rcMatch=${returncodeMatch} divergences=${verdict.prefixDivergences.length}/${verdict.prefixExecuted} (${verdict.prefixDivergencePct}%, ${verdict.prefixUnknownExpectations} unknown-expectation)`);
1229
1439
  } catch (err) {
1230
- const message = err instanceof Error ? err.message : String(err);
1231
- const row = {
1232
- ...base,
1233
- derivedImage,
1234
- signature: null,
1235
- status: "replay-error",
1236
- error: message.slice(0, 500),
1237
- imagePulled: pulled,
1238
- imageBuilt: built,
1239
- prefixExecuted: null,
1240
- prefixDivergences: null,
1241
- prefixDivergencePct: null,
1242
- prefixConfirmed: null,
1243
- prefixReturncodeMismatches: null,
1244
- prefixUnknownExpectations: null,
1245
- armAExit: null,
1246
- armAReturncodeMatch: false,
1247
- armASignatureMatch: false,
1248
- replayed: false,
1249
- fix: null,
1250
- wallMs: Date.now() - caseStart
1440
+ return {
1441
+ succeeded: false,
1442
+ error: `build ${derived} from ${image}: ${errorTail(err)}`
1251
1443
  };
1252
- rows.push(row);
1253
- appendFileSync(progressPath, `${JSON.stringify(row)}\n`);
1254
- onProgress(`${label}: replay error — ${message.slice(0, 200)}`);
1255
1444
  }
1256
- }
1257
- let llm = null;
1258
- if (options.fix !== "none") {
1259
- const caller = options.fixCaller;
1260
- if (!caller) throw new Error(`trajectory-replay: fix=${options.fix} requires a fixCaller`);
1261
- const fixAttempts = options.fixAttempts ?? 3;
1262
- const maxFixCases = options.maxFixCases ?? 30;
1263
- const seed = options.seed ?? 17;
1264
- const eligible = rows.filter((r) => r.status === "ok" && r.replayed);
1265
- const sampled = eligible.length > maxFixCases ? seededSample(eligible, maxFixCases, seed) : new Set(eligible);
1266
- if (eligible.length > maxFixCases) onProgress(`fix phase: ${eligible.length} eligible > cap ${maxFixCases}; seeded sample (seed=${seed})`);
1267
- let calls = 0;
1268
- let failures = 0;
1269
- let promptTokens = 0;
1270
- let completionTokens = 0;
1271
- let callsWithoutUsage = 0;
1272
- for (const row of eligible) {
1273
- const index = rows.indexOf(row);
1274
- if (!sampled.has(row)) {
1275
- rows[index] = {
1276
- ...row,
1277
- fix: {
1278
- attempted: false,
1279
- sampledOut: true,
1280
- command: null,
1281
- llmError: null,
1282
- usage: null,
1283
- armBExit: null,
1284
- armBPrefixExecuted: null,
1285
- armBPrefixDivergences: null,
1286
- armBPrefixDivergencePct: null,
1287
- failureVanished: null,
1288
- armBError: null,
1289
- attempts: null,
1290
- flippedAtAttempt: null
1291
- }
1292
- };
1293
- continue;
1294
- }
1295
- const replayCase = selected.find((c) => c.trajId === row.trajId && c.corpus === row.corpus);
1296
- const label = `fix ${row.corpus}/${row.trajId}`;
1297
- const signature = verdictByTraj.get(row.trajId)?.signature ?? null;
1298
- const stepTimeoutMs = options.stepTimeoutMs ?? replayCase.recordedStepTimeoutMs ?? 12e4;
1299
- const promptInput = {
1300
- taskStatement: replayCase.taskStatement,
1301
- steps: replayCase.steps,
1302
- k: replayCase.k
1303
- };
1304
- const caseOut = join(options.out, caseOutDirName(row));
1305
- if (options.fix === "loop") {
1306
- onProgress(`${label}: fix loop (budget ${fixAttempts} attempts)`);
1307
- const result = await runFixLoop(caller, promptInput, async (command, attempt) => {
1308
- onProgress(`${label}: attempt ${attempt} arm B — ${command.split("\n")[0].slice(0, 120)}`);
1309
- const armB = await executeArmB(replayCase, command, backendFactory(row.derivedImage), signature, stepTimeoutMs, options.prefixLimit, (message) => onProgress(`${label}: attempt ${attempt}: ${message}`));
1310
- mkdirSync(caseOut, { recursive: true });
1311
- writeFileSync(join(caseOut, `armB-attempt${attempt}-result.json`), `${JSON.stringify({
1312
- command,
1313
- ...armB
1314
- }, null, 2)}\n`);
1315
- writeFileSync(join(caseOut, "armB-result.json"), `${JSON.stringify({
1316
- command,
1317
- attempt,
1318
- ...armB
1319
- }, null, 2)}\n`);
1320
- return armB;
1321
- }, {
1322
- maxAttempts: fixAttempts,
1323
- onProgress: (message) => onProgress(`${label}: ${message}`)
1324
- });
1325
- calls += result.llmCalls;
1326
- failures += result.llmFailures;
1327
- promptTokens += result.promptTokens;
1328
- completionTokens += result.completionTokens;
1329
- callsWithoutUsage += result.callsWithoutUsage;
1330
- const usage = result.promptTokens + result.completionTokens > 0 ? {
1331
- promptTokens: result.promptTokens,
1332
- completionTokens: result.completionTokens
1333
- } : null;
1334
- const summary = (result.flippedAtAttempt !== null ? result.attempts.find((a) => a.attempt === result.flippedAtAttempt) : null) ?? [...result.attempts].reverse().find((a) => a.executed) ?? null;
1335
- const lastRecord = result.attempts.at(-1) ?? null;
1336
- rows[index] = {
1337
- ...row,
1338
- fix: summary ? {
1339
- attempted: true,
1340
- sampledOut: false,
1341
- command: summary.command,
1342
- llmError: null,
1343
- usage,
1344
- armBExit: summary.exitCode,
1345
- armBPrefixExecuted: summary.prefixExecuted,
1346
- armBPrefixDivergences: summary.prefixDivergences,
1347
- armBPrefixDivergencePct: summary.prefixDivergencePct,
1348
- failureVanished: summary.failureVanished,
1349
- armBError: null,
1350
- attempts: result.attempts,
1351
- flippedAtAttempt: result.flippedAtAttempt
1352
- } : result.aborted ? {
1353
- attempted: true,
1354
- sampledOut: false,
1355
- command: lastRecord?.command ?? null,
1356
- llmError: null,
1357
- usage,
1358
- armBExit: null,
1359
- armBPrefixExecuted: null,
1360
- armBPrefixDivergences: null,
1361
- armBPrefixDivergencePct: null,
1362
- failureVanished: null,
1363
- armBError: lastRecord?.armBError ?? "sandbox error",
1364
- attempts: result.attempts,
1365
- flippedAtAttempt: null
1366
- } : {
1367
- attempted: true,
1368
- sampledOut: false,
1369
- command: null,
1370
- llmError: lastRecord?.llmError ?? "no attempt produced a runnable fix",
1371
- usage,
1372
- armBExit: null,
1373
- armBPrefixExecuted: null,
1374
- armBPrefixDivergences: null,
1375
- armBPrefixDivergencePct: null,
1376
- failureVanished: null,
1377
- armBError: null,
1378
- attempts: result.attempts,
1379
- flippedAtAttempt: null
1380
- }
1381
- };
1382
- onProgress(`${label}: loop done — flipped=${result.flipped}` + (result.flippedAtAttempt !== null ? ` at attempt ${result.flippedAtAttempt}` : "") + ` (${result.llmCalls} calls, ${result.attempts.filter((a) => a.executed).length} arms)`);
1383
- continue;
1384
- }
1385
- onProgress(`${label}: generating corrected command`);
1386
- calls += 1;
1387
- const generated = await generateFixCommand(caller, promptInput);
1388
- if (!generated.succeeded) {
1389
- failures += 1;
1390
- rows[index] = {
1391
- ...row,
1392
- fix: {
1393
- attempted: true,
1394
- sampledOut: false,
1395
- command: null,
1396
- llmError: generated.error,
1397
- usage: null,
1398
- armBExit: null,
1399
- armBPrefixExecuted: null,
1400
- armBPrefixDivergences: null,
1401
- armBPrefixDivergencePct: null,
1402
- failureVanished: null,
1403
- armBError: null,
1404
- attempts: null,
1405
- flippedAtAttempt: null
1406
- }
1407
- };
1408
- onProgress(`${label}: LLM failed — ${generated.error.slice(0, 200)}`);
1409
- continue;
1410
- }
1411
- if (generated.value.usage === null || generated.value.usage === void 0) callsWithoutUsage += 1;
1412
- promptTokens += generated.value.usage?.promptTokens ?? 0;
1413
- completionTokens += generated.value.usage?.completionTokens ?? 0;
1414
- onProgress(`${label}: arm B — ${generated.value.command.split("\n")[0].slice(0, 120)}`);
1415
- try {
1416
- const armB = await executeArmB(replayCase, generated.value.command, backendFactory(row.derivedImage), signature, stepTimeoutMs, options.prefixLimit, (message) => onProgress(`${label}: ${message}`));
1417
- rows[index] = {
1418
- ...row,
1419
- fix: {
1420
- attempted: true,
1421
- sampledOut: false,
1422
- command: generated.value.command,
1423
- llmError: null,
1424
- usage: generated.value.usage,
1425
- armBExit: armB.exitCode,
1426
- armBPrefixExecuted: armB.prefixExecuted,
1427
- armBPrefixDivergences: armB.prefixDivergences,
1428
- armBPrefixDivergencePct: armB.prefixDivergencePct,
1429
- failureVanished: armB.failureVanished,
1430
- armBError: null,
1431
- attempts: null,
1432
- flippedAtAttempt: armB.failureVanished ? 1 : null
1433
- }
1434
- };
1435
- mkdirSync(caseOut, { recursive: true });
1436
- writeFileSync(join(caseOut, "armB-result.json"), `${JSON.stringify({
1437
- command: generated.value.command,
1438
- ...armB
1439
- }, null, 2)}\n`);
1440
- onProgress(`${label}: armB exit=${armB.exitCode} failureVanished=${armB.failureVanished}`);
1441
- } catch (err) {
1442
- const message = err instanceof Error ? err.message : String(err);
1443
- rows[index] = {
1444
- ...row,
1445
- fix: {
1446
- attempted: true,
1447
- sampledOut: false,
1448
- command: generated.value.command,
1449
- llmError: null,
1450
- usage: generated.value.usage,
1451
- armBExit: null,
1452
- armBPrefixExecuted: null,
1453
- armBPrefixDivergences: null,
1454
- armBPrefixDivergencePct: null,
1455
- failureVanished: null,
1456
- armBError: message.slice(0, 500),
1457
- attempts: null,
1458
- flippedAtAttempt: null
1459
- }
1460
- };
1461
- onProgress(`${label}: arm B error — ${message.slice(0, 200)}`);
1445
+ return {
1446
+ succeeded: true,
1447
+ value: {
1448
+ derivedImage: derived,
1449
+ pulled,
1450
+ built: true
1462
1451
  }
1463
- }
1464
- llm = {
1465
- model: options.fixModelLabel ?? "unknown",
1466
- calls,
1467
- failures,
1468
- promptTokens,
1469
- completionTokens,
1470
- callsWithoutUsage
1471
1452
  };
1453
+ } };
1454
+ }
1455
+ //#endregion
1456
+ //#region src/trajectory-replay/batch.ts
1457
+ /**
1458
+ * Batch replay verification across gold-labeled trajectory corpora.
1459
+ *
1460
+ * For every replayable case (a raw trajectory with a recorded image AND at
1461
+ * least one gold incorrect step) the batch:
1462
+ * 1. derives the replay-ready image through the injected `ImagePreparer`,
1463
+ * 2. replays the prefix and runs arm A — the recorded gold step k, and
1464
+ * 3. optionally generates a corrected command with one LLM call and runs
1465
+ * arm B in its own fresh session.
1466
+ *
1467
+ * Headline metrics:
1468
+ * replayability rate — fraction of replayable cases where the prefix
1469
+ * replays within the divergence tolerance AND arm A reproduces a
1470
+ * recorded failure at k; a zero exit without an error signature is not
1471
+ * evidence of failure;
1472
+ * prefix fidelity — executed prefix steps and the share of them that did
1473
+ * not confirm the recording, split by kind. A corpus whose recordings
1474
+ * carry no returncodes shows up here as unknown-expectation steps, never
1475
+ * as a clean replay;
1476
+ * fix-flip rate — fraction of arm-B-executed cases where the failure
1477
+ * vanished (exit 0, signature absent).
1478
+ *
1479
+ * Image pulls and execs run strictly serially: pulls contend on disk and
1480
+ * registry bandwidth, and serial cases keep wall-time attribution per case
1481
+ * honest. Pull failures are report rows, never silent skips.
1482
+ */
1483
+ function rate(numerator, denominator) {
1484
+ return denominator > 0 ? numerator / denominator : null;
1485
+ }
1486
+ function caseIdentity(replayCase) {
1487
+ return {
1488
+ corpus: replayCase.corpus,
1489
+ trajId: replayCase.trajId,
1490
+ image: replayCase.image,
1491
+ cwd: replayCase.cwd,
1492
+ cwdSource: replayCase.cwdSource,
1493
+ k: replayCase.k,
1494
+ stepCount: replayCase.steps.length,
1495
+ goldIncorrectSteps: replayCase.goldIncorrectSteps,
1496
+ submitGoldsSkipped: replayCase.submitGoldsSkipped,
1497
+ recordedReturncodeAtK: replayCase.recordedReturncodeAtK
1498
+ };
1499
+ }
1500
+ /** The row of a case that never produced an arm A verdict. */
1501
+ function unreplayedRow(replayCase, caseStart, failure) {
1502
+ return {
1503
+ ...caseIdentity(replayCase),
1504
+ derivedImage: failure.derivedImage,
1505
+ signature: null,
1506
+ status: failure.status,
1507
+ error: failure.error,
1508
+ imagePulled: failure.imagePulled,
1509
+ imageBuilt: failure.imageBuilt,
1510
+ prefixExecuted: null,
1511
+ prefixDivergences: null,
1512
+ prefixDivergencePct: null,
1513
+ prefixConfirmed: null,
1514
+ prefixReturncodeMismatches: null,
1515
+ prefixUnknownExpectations: null,
1516
+ armAExit: null,
1517
+ armAReturncodeMatch: false,
1518
+ armASignatureMatch: false,
1519
+ replayed: false,
1520
+ fix: null,
1521
+ wallMs: Date.now() - caseStart
1522
+ };
1523
+ }
1524
+ function verdictRow(replayCase, caseStart, image, verdict, returncodeMatch) {
1525
+ return {
1526
+ ...caseIdentity(replayCase),
1527
+ derivedImage: image.derivedImage,
1528
+ signature: verdict.signature,
1529
+ status: "ok",
1530
+ error: null,
1531
+ imagePulled: image.pulled,
1532
+ imageBuilt: image.built,
1533
+ prefixExecuted: verdict.prefixExecuted,
1534
+ prefixDivergences: verdict.prefixDivergences.length,
1535
+ prefixDivergencePct: verdict.prefixDivergencePct,
1536
+ prefixConfirmed: verdict.prefixConfirmed,
1537
+ prefixReturncodeMismatches: verdict.prefixReturncodeMismatches,
1538
+ prefixUnknownExpectations: verdict.prefixUnknownExpectations,
1539
+ armAExit: verdict.armA.exitCode,
1540
+ armAReturncodeMatch: returncodeMatch,
1541
+ armASignatureMatch: verdict.armA.failureSignatureMatch,
1542
+ replayed: verdict.valid,
1543
+ fix: null,
1544
+ wallMs: Date.now() - caseStart
1545
+ };
1546
+ }
1547
+ /** Phase 1 for one case: prepare its image and run arm A. Every outcome is a row, never a skip. */
1548
+ async function replayArmA(run, replayCase, label, verdicts, pullFailures) {
1549
+ const { options, onProgress } = run;
1550
+ const caseStart = Date.now();
1551
+ onProgress(`${label}: preparing image ${replayCase.image}`);
1552
+ const preparation = await run.preparer.ensure(replayCase.image, replayCase.cwd);
1553
+ if (!preparation.succeeded) {
1554
+ pullFailures.push({
1555
+ corpus: replayCase.corpus,
1556
+ trajId: replayCase.trajId,
1557
+ image: replayCase.image,
1558
+ error: preparation.error
1559
+ });
1560
+ const row = unreplayedRow(replayCase, caseStart, {
1561
+ derivedImage: null,
1562
+ status: "image-unavailable",
1563
+ error: preparation.error,
1564
+ imagePulled: false,
1565
+ imageBuilt: false
1566
+ });
1567
+ appendFileSync(run.progressPath, `${JSON.stringify(row)}\n`);
1568
+ onProgress(`${label}: image unavailable — ${preparation.error}`);
1569
+ return row;
1472
1570
  }
1473
- const executedRows = rows.filter((r) => r.status === "ok");
1474
- const replayed = executedRows.filter((r) => r.replayed);
1571
+ const { derivedImage, pulled, built } = preparation.value;
1572
+ const stepTimeoutMs = options.stepTimeoutMs ?? replayCase.recordedStepTimeoutMs ?? 12e4;
1573
+ const caseOut = join(options.out, caseOutDirName(replayCase));
1574
+ onProgress(`${label}: arm A on ${derivedImage} (k=${replayCase.k}, timeout ${stepTimeoutMs}ms)`);
1575
+ try {
1576
+ const verdict = await replayVerify({
1577
+ stepsPath: replayCase.stepsPath,
1578
+ image: derivedImage,
1579
+ at: replayCase.k,
1580
+ cwd: replayCase.cwd,
1581
+ out: caseOut,
1582
+ caseId: replayCase.trajId,
1583
+ stepTimeoutMs,
1584
+ prefixLimit: options.prefixLimit,
1585
+ backend: options.backendFactory(derivedImage),
1586
+ onProgress: (message) => onProgress(`${label}: ${message}`)
1587
+ });
1588
+ verdicts.set(replayCase.trajId, verdict);
1589
+ const returncodeMatch = verdict.recordedReturncode !== null && verdict.armA.exitCode === verdict.recordedReturncode;
1590
+ const row = verdictRow(replayCase, caseStart, {
1591
+ derivedImage,
1592
+ pulled,
1593
+ built
1594
+ }, verdict, returncodeMatch);
1595
+ appendFileSync(run.progressPath, `${JSON.stringify(row)}\n`);
1596
+ onProgress(`${label}: armA exit=${verdict.armA.exitCode} rcMatch=${returncodeMatch} divergences=${verdict.prefixDivergences.length}/${verdict.prefixExecuted} (${verdict.prefixDivergencePct}%, ${verdict.prefixUnknownExpectations} unknown-expectation)`);
1597
+ return row;
1598
+ } catch (err) {
1599
+ const message = err instanceof Error ? err.message : String(err);
1600
+ const row = unreplayedRow(replayCase, caseStart, {
1601
+ derivedImage,
1602
+ status: "replay-error",
1603
+ error: message.slice(0, 500),
1604
+ imagePulled: pulled,
1605
+ imageBuilt: built
1606
+ });
1607
+ appendFileSync(run.progressPath, `${JSON.stringify(row)}\n`);
1608
+ onProgress(`${label}: replay error — ${message.slice(0, 200)}`);
1609
+ return row;
1610
+ }
1611
+ }
1612
+ function prefixFidelityOf(executedRows) {
1475
1613
  const sumOver = (pick) => executedRows.reduce((total, row) => total + (pick(row) ?? 0), 0);
1476
1614
  const executedSteps = sumOver((r) => r.prefixExecuted);
1477
1615
  const divergentSteps = sumOver((r) => r.prefixDivergences);
1478
- const prefixFidelity = {
1616
+ return {
1479
1617
  executedSteps,
1480
1618
  divergentSteps,
1481
1619
  returncodeMismatches: sumOver((r) => r.prefixReturncodeMismatches),
@@ -1485,7 +1623,9 @@ async function runReplayBatch(options) {
1485
1623
  casesWithinTolerance: executedRows.filter((r) => r.prefixDivergencePct !== null && r.prefixDivergencePct <= 10).length,
1486
1624
  casesExecuted: executedRows.length
1487
1625
  };
1488
- const signatureStrict = executedRows.filter((r) => r.replayed && r.armASignatureMatch);
1626
+ }
1627
+ /** Fix-flip headline rates over the cases whose arm B executed. */
1628
+ function fixFlipHeadline(rows, fix) {
1489
1629
  const armBExecuted = rows.filter((r) => r.fix?.attempted && r.fix.failureVanished !== null);
1490
1630
  const flipped = armBExecuted.filter((r) => r.fix.failureVanished === true);
1491
1631
  const armBNonzeroRc = armBExecuted.filter((r) => r.recordedReturncodeAtK !== null && r.recordedReturncodeAtK !== 0);
@@ -1497,16 +1637,43 @@ async function runReplayBatch(options) {
1497
1637
  const at = row.fix?.flippedAtAttempt;
1498
1638
  if (typeof at === "number") flipsByAttempt[String(at)] = (flipsByAttempt[String(at)] ?? 0) + 1;
1499
1639
  }
1500
- const excludedByReason = {};
1501
- for (const excluded of enumeration.excluded) excludedByReason[excluded.reason] = (excludedByReason[excluded.reason] ?? 0) + 1;
1502
- const submitGoldsByCorpus = {};
1503
- const submitEntry = (corpus) => submitGoldsByCorpus[corpus] ??= {
1640
+ return {
1641
+ fixFlipRate: fix !== "none" ? {
1642
+ numerator: flipped.length,
1643
+ denominator: armBExecuted.length,
1644
+ value: rate(flipped.length, armBExecuted.length)
1645
+ } : null,
1646
+ fixFlipRateNonzeroRc: fix !== "none" ? {
1647
+ numerator: flippedNonzeroRc.length,
1648
+ denominator: armBNonzeroRc.length,
1649
+ value: rate(flippedNonzeroRc.length, armBNonzeroRc.length)
1650
+ } : null,
1651
+ fixFlipAttempt1: fix === "loop" ? {
1652
+ numerator: flippedAt1.length,
1653
+ denominator: attempt1Executed.length,
1654
+ value: rate(flippedAt1.length, attempt1Executed.length)
1655
+ } : null,
1656
+ flipsByAttempt: fix === "loop" ? flipsByAttempt : null
1657
+ };
1658
+ }
1659
+ /** Per-corpus submit-gold accounting: cases dropped for pure-submit golds, and submit golds skipped inside replayable cases. */
1660
+ function submitGoldsOf(enumeration) {
1661
+ const byCorpus = {};
1662
+ const entry = (corpus) => byCorpus[corpus] ??= {
1504
1663
  submitOnlyCases: 0,
1505
1664
  goldsSkippedWithinReplayable: 0
1506
1665
  };
1507
- for (const excluded of enumeration.excluded) if (excluded.reason === "gold-only-submit-step") submitEntry(excluded.corpus).submitOnlyCases += 1;
1508
- for (const replayCase of enumeration.replayable) if (replayCase.submitGoldsSkipped > 0) submitEntry(replayCase.corpus).goldsSkippedWithinReplayable += replayCase.submitGoldsSkipped;
1509
- const report = {
1666
+ for (const excluded of enumeration.excluded) if (excluded.reason === "gold-only-submit-step") entry(excluded.corpus).submitOnlyCases += 1;
1667
+ for (const replayCase of enumeration.replayable) if (replayCase.submitGoldsSkipped > 0) entry(replayCase.corpus).goldsSkippedWithinReplayable += replayCase.submitGoldsSkipped;
1668
+ return byCorpus;
1669
+ }
1670
+ function buildBatchReport(options, enumeration, selected, rows, llm, pullFailures) {
1671
+ const executedRows = rows.filter((r) => r.status === "ok");
1672
+ const replayed = executedRows.filter((r) => r.replayed);
1673
+ const signatureStrict = executedRows.filter((r) => r.replayed && r.armASignatureMatch);
1674
+ const excludedByReason = {};
1675
+ for (const excluded of enumeration.excluded) excludedByReason[excluded.reason] = (excludedByReason[excluded.reason] ?? 0) + 1;
1676
+ return {
1510
1677
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1511
1678
  corpora: options.corpora.map((c) => ({
1512
1679
  name: c.name,
@@ -1518,7 +1685,7 @@ async function runReplayBatch(options) {
1518
1685
  replayable: enumeration.replayable.length,
1519
1686
  executed: selected.length,
1520
1687
  excludedByReason,
1521
- submitGoldsByCorpus
1688
+ submitGoldsByCorpus: submitGoldsOf(enumeration)
1522
1689
  },
1523
1690
  headline: {
1524
1691
  replayabilityRate: {
@@ -1531,120 +1698,42 @@ async function runReplayBatch(options) {
1531
1698
  denominator: selected.length,
1532
1699
  value: rate(signatureStrict.length, selected.length)
1533
1700
  },
1534
- prefixFidelity,
1535
- fixFlipRate: options.fix !== "none" ? {
1536
- numerator: flipped.length,
1537
- denominator: armBExecuted.length,
1538
- value: rate(flipped.length, armBExecuted.length)
1539
- } : null,
1540
- fixFlipRateNonzeroRc: options.fix !== "none" ? {
1541
- numerator: flippedNonzeroRc.length,
1542
- denominator: armBNonzeroRc.length,
1543
- value: rate(flippedNonzeroRc.length, armBNonzeroRc.length)
1544
- } : null,
1545
- fixFlipAttempt1: options.fix === "loop" ? {
1546
- numerator: flippedAt1.length,
1547
- denominator: attempt1Executed.length,
1548
- value: rate(flippedAt1.length, attempt1Executed.length)
1549
- } : null,
1550
- flipsByAttempt: options.fix === "loop" ? flipsByAttempt : null
1701
+ prefixFidelity: prefixFidelityOf(executedRows),
1702
+ ...fixFlipHeadline(rows, options.fix)
1551
1703
  },
1552
1704
  llm,
1553
1705
  excluded: enumeration.excluded,
1554
1706
  pullFailures,
1555
1707
  cases: rows
1556
1708
  };
1709
+ }
1710
+ async function runReplayBatch(options) {
1711
+ const onProgress = options.onProgress ?? (() => {});
1712
+ const enumeration = enumerateReplayableCases(options.corpora);
1713
+ let selected = enumeration.replayable;
1714
+ if (options.caseFilter) selected = selected.filter((c) => c.trajId.includes(options.caseFilter));
1715
+ if (options.caseLimit !== void 0) selected = selected.slice(0, options.caseLimit);
1716
+ onProgress(`enumerated ${enumeration.labelEntryCount} label entries → ${enumeration.replayable.length} replayable, ${enumeration.excluded.length} excluded; executing ${selected.length}`);
1717
+ mkdirSync(options.out, { recursive: true });
1718
+ const run = {
1719
+ options,
1720
+ onProgress,
1721
+ progressPath: join(options.out, "cases.jsonl"),
1722
+ preparer: options.preparer ?? dockerImagePreparer()
1723
+ };
1724
+ const rows = [];
1725
+ const pullFailures = [];
1726
+ const verdictByTraj = /* @__PURE__ */ new Map();
1727
+ for (const [index, replayCase] of selected.entries()) {
1728
+ const label = `[${index + 1}/${selected.length}] ${replayCase.corpus}/${replayCase.trajId}`;
1729
+ rows.push(await replayArmA(run, replayCase, label, verdictByTraj, pullFailures));
1730
+ }
1731
+ const llm = options.fix !== "none" ? await runFixPhase(run, selected, rows, verdictByTraj) : null;
1732
+ const report = buildBatchReport(options, enumeration, selected, rows, llm, pullFailures);
1557
1733
  writeFileSync(join(options.out, "batch-report.json"), `${JSON.stringify(report, null, 2)}\n`);
1558
1734
  writeFileSync(join(options.out, "batch-report.md"), renderBatchReport(report));
1559
1735
  return report;
1560
1736
  }
1561
- function pct(value) {
1562
- return value === null ? "—" : `${(value * 100).toFixed(1)}%`;
1563
- }
1564
- function renderBatchReport(report) {
1565
- const lines = [];
1566
- const { headline, totals } = report;
1567
- lines.push("# Replay-verify batch report");
1568
- lines.push("");
1569
- lines.push(`Generated ${report.generatedAt}.`);
1570
- lines.push("");
1571
- lines.push("## Headline");
1572
- lines.push("");
1573
- const fidelity = headline.prefixFidelity;
1574
- lines.push(`- **Replayability rate: ${pct(headline.replayabilityRate.value)}** (${headline.replayabilityRate.numerator}/${headline.replayabilityRate.denominator} replayable cases where the prefix replayed within ${fidelity.tolerancePct}% divergence AND arm A reproduced an observed failure at the gold step k).`);
1575
- lines.push(`- Signature-strict rate: ${pct(headline.signatureStrictRate.value)} (${headline.signatureStrictRate.numerator}/${headline.signatureStrictRate.denominator}; additionally requires the recorded error substring in arm A output).`);
1576
- lines.push(`- **Prefix divergence: ${fidelity.divergencePct === null ? "—" : `${fidelity.divergencePct}%`}** (${fidelity.divergentSteps}/${fidelity.executedSteps} executed prefix steps did not confirm the recording: ${fidelity.returncodeMismatches} returncode mismatches, ${fidelity.unknownExpectations} with no recorded returncode to check). ${fidelity.casesWithinTolerance}/${fidelity.casesExecuted} executed cases are within the ${fidelity.tolerancePct}% tolerance.`);
1577
- if (headline.fixFlipRate) lines.push(`- **Fix-flip rate: ${pct(headline.fixFlipRate.value)}** (${headline.fixFlipRate.numerator}/${headline.fixFlipRate.denominator} arm-B-executed cases where the generated fix made the failure vanish).`);
1578
- if (headline.fixFlipRateNonzeroRc) lines.push(`- Fix-flip rate on recorded-rc≠0 cases: ${pct(headline.fixFlipRateNonzeroRc.value)} (${headline.fixFlipRateNonzeroRc.numerator}/${headline.fixFlipRateNonzeroRc.denominator}; real recorded failures — a gold step recorded with rc 0 flips vacuously).`);
1579
- if (headline.fixFlipAttempt1) lines.push(`- Fix-flip@1: ${pct(headline.fixFlipAttempt1.value)} (${headline.fixFlipAttempt1.numerator}/${headline.fixFlipAttempt1.denominator} cases whose attempt 1 executed — the one-shot-comparable number).`);
1580
- if (headline.flipsByAttempt && Object.keys(headline.flipsByAttempt).length > 0) {
1581
- const parts = Object.entries(headline.flipsByAttempt).sort((a, b) => Number(a[0]) - Number(b[0])).map(([attempt, count]) => `attempt ${attempt}: ${count}`);
1582
- lines.push(`- Flips by attempt: ${parts.join(", ")}.`);
1583
- }
1584
- lines.push("");
1585
- lines.push("## Enumeration");
1586
- lines.push("");
1587
- lines.push(`${totals.labelEntries} label entries across ${report.corpora.length} corpora → ${totals.replayable} replayable (SWE-style docker image + ≥1 gold incorrect step), ${totals.executed} executed.`);
1588
- lines.push("");
1589
- lines.push("| exclusion reason | count |");
1590
- lines.push("| --- | --- |");
1591
- for (const [reason, count] of Object.entries(totals.excludedByReason).sort((a, b) => b[1] - a[1])) lines.push(`| ${reason} | ${count} |`);
1592
- const submitEntries = Object.entries(totals.submitGoldsByCorpus);
1593
- if (submitEntries.length > 0) {
1594
- lines.push("");
1595
- lines.push("Submit-command golds are never counterfactual targets (a gold on the submit step marks a bad submit decision, not a failed command):");
1596
- lines.push("");
1597
- lines.push("| corpus | cases excluded (all golds = submit) | golds skipped within replayable cases |");
1598
- lines.push("| --- | --- | --- |");
1599
- for (const [corpus, stats] of submitEntries.sort((a, b) => a[0].localeCompare(b[0]))) lines.push(`| ${corpus} | ${stats.submitOnlyCases} | ${stats.goldsSkippedWithinReplayable} |`);
1600
- }
1601
- if (report.pullFailures.length > 0) {
1602
- lines.push("");
1603
- lines.push("## Image pull/build failures");
1604
- lines.push("");
1605
- lines.push("| corpus | trajectory | image | error |");
1606
- lines.push("| --- | --- | --- | --- |");
1607
- for (const failure of report.pullFailures) lines.push(`| ${failure.corpus} | ${failure.trajId} | \`${failure.image}\` | ${failure.error.replaceAll("|", "\\|")} |`);
1608
- }
1609
- lines.push("");
1610
- lines.push("## Per-case results");
1611
- lines.push("");
1612
- lines.push("| corpus | trajectory | k | rc@k | prefix | confirmed | rc mismatch | unknown rc | div | div% | armA exit | rc match | sig match | replayed | fix | armB exit | armB div% | vanished | wall s |");
1613
- lines.push(`| ${Array(19).fill("---").join(" | ")} |`);
1614
- for (const row of report.cases) {
1615
- const fix = row.fix;
1616
- const fixCell = !fix ? "—" : fix.sampledOut ? "sampled-out" : fix.llmError ? "llm-failed" : fix.armBError ? "armB-error" : fix.attempts ? fix.flippedAtAttempt !== null ? `flip@${fix.flippedAtAttempt}` : `exhausted(${fix.attempts.length})` : "generated";
1617
- lines.push([
1618
- row.corpus,
1619
- row.trajId.length > 48 ? `${row.trajId.slice(0, 45)}…` : row.trajId,
1620
- row.k,
1621
- row.recordedReturncodeAtK ?? "null",
1622
- row.prefixExecuted ?? "—",
1623
- row.prefixConfirmed ?? "—",
1624
- row.prefixReturncodeMismatches ?? "—",
1625
- row.prefixUnknownExpectations ?? "—",
1626
- row.prefixDivergences ?? "—",
1627
- row.prefixDivergencePct ?? "—",
1628
- row.status === "ok" ? row.armAExit : row.status,
1629
- row.armAReturncodeMatch ? "yes" : "no",
1630
- row.armASignatureMatch ? "yes" : "no",
1631
- row.replayed ? "**yes**" : "no",
1632
- fixCell,
1633
- fix?.armBExit ?? "—",
1634
- fix?.armBPrefixDivergencePct ?? "—",
1635
- fix?.failureVanished === null || fix === null ? "—" : fix.failureVanished ? "**yes**" : "no",
1636
- (row.wallMs / 1e3).toFixed(1)
1637
- ].join(" | "));
1638
- }
1639
- if (report.llm) {
1640
- lines.push("");
1641
- lines.push("## LLM fix generation");
1642
- lines.push("");
1643
- lines.push(`Model ${report.llm.model}: ${report.llm.calls} calls (${report.llm.failures} failed), ${report.llm.promptTokens} prompt + ${report.llm.completionTokens} completion tokens.`);
1644
- }
1645
- lines.push("");
1646
- return lines.join("\n");
1647
- }
1648
1737
  //#endregion
1649
1738
  //#region src/trajectory-replay/wire.ts
1650
1739
  /**
@@ -2116,44 +2205,7 @@ async function verifyFindings(findings, options) {
2116
2205
  }, null, 2)}\n`);
2117
2206
  return run;
2118
2207
  }
2119
- function verdictCell(verification) {
2120
- switch (verification.verified) {
2121
- case "reproduced": return "**VERIFIED** — reproduced";
2122
- case "fix-flipped": return "**VERIFIED** — fix-flipped";
2123
- case "divergent": return "DIVERGENT — recorded failure did not reproduce";
2124
- case "not-replayable": return `UNVERIFIABLE — ${verification.reason ?? "no reason recorded"}`;
2125
- }
2126
- }
2127
- /** Markdown section an analysis report appends when finding verification ran. */
2128
- function renderVerifiedFindingsSection(run) {
2129
- const lines = ["## Verified findings (executed replay)", ""];
2130
- const total = run.verifications.length;
2131
- lines.push(`${total} finding(s) → ${run.counts.reproduced} reproduced, ${run.counts["fix-flipped"]} fix-flipped, ${run.counts.divergent} divergent, ${run.counts["not-replayable"]} not replayable (${run.executions} execution(s); findings accusing the same step share one proof).`);
2132
- lines.push("");
2133
- lines.push("| Finding | Subject | Step | Verdict | Receipt |");
2134
- lines.push("|---|---|---:|---|---|");
2135
- for (const verification of run.verifications) {
2136
- const shared = verification.deduplicated_with ? " (shared proof)" : "";
2137
- lines.push(`| \`${verification.finding_id ?? "—"}\` | \`${verification.subject ?? "—"}\` | ${verification.step ?? "—"} | ${verdictCell(verification)} | \`${verification.receipt}\`${shared} |`);
2138
- }
2139
- lines.push("");
2140
- lines.push("VERIFIED = the accused step was re-executed after replaying the trajectory prefix, and the recorded failure signature reproduced (fix-flipped: a corrected command additionally made it vanish). Each receipt directory carries receipt.json and, when executed, replay-verdict.json + report.md with real stdout/stderr.");
2141
- lines.push("");
2142
- return lines.join("\n");
2143
- }
2144
- /**
2145
- * Accepts the two shapes findings travel in: a bare JSON array of analyst
2146
- * findings, or an object with a `findings` array (e.g. an extracted
2147
- * `observations[n]` from a result.json).
2148
- */
2149
- function readFindingsFile(path) {
2150
- const parsed = JSON.parse(readFileSync(path, "utf8"));
2151
- const array = Array.isArray(parsed) ? parsed : parsed && typeof parsed === "object" && Array.isArray(parsed.findings) ? parsed.findings : null;
2152
- if (!array) throw new Error(`${path} is neither a findings array nor an object with a findings array`);
2153
- for (const entry of array) if (!entry || typeof entry !== "object") throw new Error(`${path}: every finding must be an object, got ${JSON.stringify(entry)}`);
2154
- return array;
2155
- }
2156
2208
  //#endregion
2157
- export { FORMAT_ERROR_OBSERVATION_PREFIX, PREFIX_DIVERGENCE_TOLERANCE_PCT, SUBMIT_ACTION_SIGNATURE, SandboxCounterfactualRunner, TIMEOUT_OBSERVATION_MARKER, assertReplayableTrajectory, buildFixPrompt, buildRetryFixPrompt, classifyObservation, classifyPrefixStep, classifyVerdict, clipText, countScriptCommands, decodeRecordedTurns, deriveFailureSignature, derivedImageTag, dockerImagePreparer, enumerateReplayableCases, extractFixCommand, finalRecordedOutcome, findingReplayStep, findingTrajectoryId, generateFixCommand, goldIncorrectSteps, ingestRecordedTrajectory, isElidedField, isRecordedTimeout, isSubmitAction, isSubmitOnlyAction, parseCorpusFlag, parseIncorrectStepsSubject, parseObservationOutput, parseRecordedReturncode, readFindingsFile, renderVerifiedFindingsSection, replayVerify, replayVerifyFinding, resolveCaseResources, resolveFindingInvocation, resolveFindingReplayability, runFixLoop, runReplayBatch, seededSample, summarizePrefixReplay, unreadableExitCount, verifyFindings, wrapActionForExec };
2209
+ export { PREFIX_DIVERGENCE_TOLERANCE_PCT, classifyObservation, classifyVerdict, decodeRecordedTurns, dockerImagePreparer, finalRecordedOutcome, replayVerify, replayVerifyFinding, runReplayBatch, seededSample, verifyFindings };
2158
2210
 
2159
2211
  //# sourceMappingURL=index.js.map