@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.
- package/CHANGELOG.md +137 -57
- package/README.md +2 -2
- package/dist/adapters/http.d.ts +2 -2
- package/dist/{agent-profile-VFcQncbW.d.ts → agent-profile-DTt8uGP9.d.ts} +4 -17
- package/dist/agent-profile-DTt8uGP9.d.ts.map +1 -0
- package/dist/{agent-profile-cell-BK8mlqlq.js → agent-profile-cell-B1V1gUZs.js} +4 -53
- package/dist/agent-profile-cell-B1V1gUZs.js.map +1 -0
- package/dist/{agent-profile-cell-BNp-Js9F.d.ts → agent-profile-cell-BcpFbNd5.d.ts} +2 -22
- package/dist/agent-profile-cell-BcpFbNd5.d.ts.map +1 -0
- package/dist/{agreement-irr-BEWLIF3_.js → agreement-irr-BhAqjluA.js} +5 -5
- package/dist/agreement-irr-BhAqjluA.js.map +1 -0
- package/dist/analyst/index.d.ts +36 -169
- package/dist/analyst/index.d.ts.map +1 -1
- package/dist/analyst/index.js +17 -192
- package/dist/analyst/index.js.map +1 -1
- package/dist/{analyze-runs-D68fd-su.d.ts → analyze-runs-DjlZOVXx.d.ts} +5 -5
- package/dist/{analyze-runs-D68fd-su.d.ts.map → analyze-runs-DjlZOVXx.d.ts.map} +1 -1
- package/dist/{analyze-runs-C84irm4i.js → analyze-runs-DqPJaJ4X.js} +225 -21
- package/dist/analyze-runs-DqPJaJ4X.js.map +1 -0
- package/dist/authenticity/index.d.ts +0 -23
- package/dist/authenticity/index.d.ts.map +1 -1
- package/dist/authenticity/index.js +1 -34
- package/dist/authenticity/index.js.map +1 -1
- package/dist/{benchmark-command-By0vh8q7.js → benchmark-command-BupdHRyf.js} +1225 -661
- package/dist/benchmark-command-BupdHRyf.js.map +1 -0
- package/dist/benchmarks/index.d.ts +5 -6
- package/dist/benchmarks/index.d.ts.map +1 -1
- package/dist/benchmarks/index.js +4 -4
- package/dist/benchmarks/index.js.map +1 -1
- package/dist/builder-eval/index.d.ts +3 -3
- package/dist/builder-eval/index.js +2 -2
- package/dist/builder-eval/index.js.map +1 -1
- package/dist/campaign/index.d.ts +7 -7
- package/dist/campaign/index.js +15 -15
- package/dist/{campaign-BudZ70zo.js → campaign-CGvUTnmc.js} +324 -800
- package/dist/campaign-CGvUTnmc.js.map +1 -0
- package/dist/{campaign-evidence-Clrn4hBV.js → campaign-evidence-DunzcJGv.js} +10 -18
- package/dist/{campaign-evidence-Clrn4hBV.js.map → campaign-evidence-DunzcJGv.js.map} +1 -1
- package/dist/{canonical-B2RqLofv.js → canonical-Dq2hOQ8L.js} +2 -2
- package/dist/{canonical-B2RqLofv.js.map → canonical-Dq2hOQ8L.js.map} +1 -1
- package/dist/cell-spend-DEWR3nj-.js +90 -0
- package/dist/cell-spend-DEWR3nj-.js.map +1 -0
- package/dist/{chat-client-l-MVDlDk.js → chat-client-B2pXlPdx.js} +2 -2
- package/dist/{chat-client-l-MVDlDk.js.map → chat-client-B2pXlPdx.js.map} +1 -1
- package/dist/{chat-json-call-puMArmtW.js → chat-json-call-Dm9c-b41.js} +3 -2
- package/dist/chat-json-call-Dm9c-b41.js.map +1 -0
- package/dist/{claim-q4HQhK1G.js → claim-BPVuyHuy.js} +3 -3
- package/dist/{claim-q4HQhK1G.js.map → claim-BPVuyHuy.js.map} +1 -1
- package/dist/cli.js +113 -12
- package/dist/cli.js.map +1 -1
- package/dist/{client-BzL9niqi.d.ts → client-hwo3cJjR.d.ts} +3 -16
- package/dist/client-hwo3cJjR.d.ts.map +1 -0
- package/dist/{cluster-bootstrap-qcUWYFl-.js → cluster-bootstrap-DEwwhl0C.js} +3 -3
- package/dist/{cluster-bootstrap-qcUWYFl-.js.map → cluster-bootstrap-DEwwhl0C.js.map} +1 -1
- package/dist/{produced-state-BsDmnwxc.js → completion-verifier-DbLVf3i8.js} +9 -64
- package/dist/completion-verifier-DbLVf3i8.js.map +1 -0
- package/dist/contract/index.d.ts +12 -12
- package/dist/contract/index.js +107 -403
- package/dist/contract/index.js.map +1 -1
- package/dist/{cost-ledger-BP8V4kUQ.js → cost-ledger-D0352Z4d.js} +4 -8
- package/dist/cost-ledger-D0352Z4d.js.map +1 -0
- package/dist/dataset-DQqhOCPt.d.ts.map +1 -1
- package/dist/{default-registry-COhk9g36.js → default-registry-BJ-Pq6yL.js} +314 -285
- package/dist/default-registry-BJ-Pq6yL.js.map +1 -0
- package/dist/{default-registry-DzHiGruW.d.ts → default-registry-DXgLmoDI.d.ts} +7 -50
- package/dist/default-registry-DXgLmoDI.d.ts.map +1 -0
- package/dist/{define-agent-eval-CNTPzIkp.js → define-agent-eval-BGdFKlrc.js} +170 -110
- package/dist/define-agent-eval-BGdFKlrc.js.map +1 -0
- package/dist/{define-agent-eval-WSck0CZJ.d.ts → define-agent-eval-BlH10zEw.d.ts} +8 -8
- package/dist/define-agent-eval-BlH10zEw.d.ts.map +1 -0
- package/dist/{descriptive-DboxPZrG.js → descriptive-CVNy48rR.js} +2 -2
- package/dist/{descriptive-DboxPZrG.js.map → descriptive-CVNy48rR.js.map} +1 -1
- package/dist/diagnosis/index.d.ts +4 -14
- package/dist/diagnosis/index.d.ts.map +1 -1
- package/dist/diagnosis/index.js +67 -28
- package/dist/diagnosis/index.js.map +1 -1
- package/dist/{dspy-rlm-engine-LRCu61m5.js → dspy-rlm-engine-CSMwudLO.js} +5 -10
- package/dist/dspy-rlm-engine-CSMwudLO.js.map +1 -0
- package/dist/{emitter-HcCmFQl_.d.ts → emitter-DABYYTcH.d.ts} +5 -5
- package/dist/emitter-DABYYTcH.d.ts.map +1 -0
- package/dist/emitter-DbMXUIVP.js.map +1 -1
- package/dist/{engine-3A_4jOlI.d.ts → engine-BvZ0R-36.d.ts} +17 -21
- package/dist/engine-BvZ0R-36.d.ts.map +1 -0
- package/dist/{equal-terms-BtdXayCJ.js → equal-terms-DbPGcVtI.js} +2 -2
- package/dist/equal-terms-DbPGcVtI.js.map +1 -0
- package/dist/{errors-DVZiqS7K.js → errors-BcOs5MKW.js} +2 -8
- package/dist/{errors-DVZiqS7K.js.map → errors-BcOs5MKW.js.map} +1 -1
- package/dist/eval-campaign-BUMIxKTE.js +393 -0
- package/dist/eval-campaign-BUMIxKTE.js.map +1 -0
- package/dist/{evaluation-6Yoi74gM.d.ts → evaluation-BOZ38vOH.d.ts} +7 -7
- package/dist/evaluation-BOZ38vOH.d.ts.map +1 -0
- package/dist/evaluation.d.ts +2 -2
- package/dist/evaluation.js +1 -1
- package/dist/evaluation.js.map +1 -1
- package/dist/{exact-types-ClugMJfI.d.ts → exact-types-CflbJ9f3.d.ts} +2 -2
- package/dist/{exact-types-ClugMJfI.d.ts.map → exact-types-CflbJ9f3.d.ts.map} +1 -1
- package/dist/{exec-ChJ6d5if.js → exec-CpbtOXVt.js} +2 -24
- package/dist/{exec-ChJ6d5if.js.map → exec-CpbtOXVt.js.map} +1 -1
- package/dist/execution-tracks-CpgFPpS5.js.map +1 -1
- package/dist/experiment/index.d.ts +9 -112
- package/dist/experiment/index.d.ts.map +1 -1
- package/dist/experiment/index.js +258 -311
- package/dist/experiment/index.js.map +1 -1
- package/dist/{exporters-X4xwla8A.js → exporters-DWvM16LE.js} +3 -3
- package/dist/exporters-DWvM16LE.js.map +1 -0
- package/dist/{exporters-ChTnR90l.d.ts → exporters-Dk3oEbK_.d.ts} +2 -4
- package/dist/{exporters-ChTnR90l.d.ts.map → exporters-Dk3oEbK_.d.ts.map} +1 -1
- package/dist/{external-optimizer-process-BjMGUuds.js → external-optimizer-process-C3Wm7Vhn.js} +4 -3
- package/dist/external-optimizer-process-C3Wm7Vhn.js.map +1 -0
- package/dist/{external-optimizer-subprocess-BqW0hHcI.js → external-optimizer-subprocess-B5gdLRZv.js} +9 -11
- package/dist/external-optimizer-subprocess-B5gdLRZv.js.map +1 -0
- package/dist/{extract-usage-BrnzRJ6H.js → extract-usage-BZLU01kR.js} +2 -23
- package/dist/{extract-usage-BrnzRJ6H.js.map → extract-usage-BZLU01kR.js.map} +1 -1
- package/dist/{failure-cluster-CbgEDcFJ.d.ts → failure-cluster-DN0t63Z0.d.ts} +4 -43
- package/dist/failure-cluster-DN0t63Z0.d.ts.map +1 -0
- package/dist/{failure-taxonomy-CjabTATj.js → failure-taxonomy-Bx5oWqaW.js} +2 -19
- package/dist/{failure-taxonomy-CjabTATj.js.map → failure-taxonomy-Bx5oWqaW.js.map} +1 -1
- package/dist/{feedback-trajectory-C4EG0nNH.d.ts → feedback-trajectory-BWmhRBGu.d.ts} +7 -9
- package/dist/feedback-trajectory-BWmhRBGu.d.ts.map +1 -0
- package/dist/{semantic-concept-judge-BQF8te0T.js → findings-store-BtTzPL0d.js} +9 -249
- package/dist/findings-store-BtTzPL0d.js.map +1 -0
- package/dist/fuzz.d.ts +19 -62
- package/dist/fuzz.d.ts.map +1 -1
- package/dist/fuzz.js +62 -108
- package/dist/fuzz.js.map +1 -1
- package/dist/{hf-dataset-BR2yhuNn.js → hf-dataset-CKxIJQba.js} +4 -24
- package/dist/hf-dataset-CKxIJQba.js.map +1 -0
- package/dist/hosted/index.d.ts +2 -2
- package/dist/hosted/index.js +2 -2
- package/dist/{index-CyszSg4O.d.ts → index-B16FNdW5.d.ts} +145 -360
- package/dist/index-B16FNdW5.d.ts.map +1 -0
- package/dist/{index-DV-jSzxF.d.ts → index-BLGqZ81N.d.ts} +10 -15
- package/dist/{index-DV-jSzxF.d.ts.map → index-BLGqZ81N.d.ts.map} +1 -1
- package/dist/{index-BO41eGj5.d.ts → index-CX-GAUpw.d.ts} +3 -7
- package/dist/index-CX-GAUpw.d.ts.map +1 -0
- package/dist/{index-DHKEAv3s.d.ts → index-CalAsXMR.d.ts} +8 -68
- package/dist/index-CalAsXMR.d.ts.map +1 -0
- package/dist/{index-BFRO6chs.d.ts → index-CiRwLv2R.d.ts} +5 -69
- package/dist/index-CiRwLv2R.d.ts.map +1 -0
- package/dist/{index-DPcngtwt.d.ts → index-DbMOXYnP.d.ts} +11 -36
- package/dist/index-DbMOXYnP.d.ts.map +1 -0
- package/dist/index.d.ts +211 -218
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +999 -1714
- package/dist/index.js.map +1 -1
- package/dist/{integrity-dLiDe2kD.js → integrity-C8oUeB0g.js} +326 -203
- package/dist/integrity-C8oUeB0g.js.map +1 -0
- package/dist/{integrity-BXOsAb3i.js → integrity-W4CADcFc.js} +3 -7
- package/dist/{integrity-BXOsAb3i.js.map → integrity-W4CADcFc.js.map} +1 -1
- package/dist/{integrity-DAMEDFaC.d.ts → integrity-_qgZLA5p.d.ts} +4 -6
- package/dist/{integrity-DAMEDFaC.d.ts.map → integrity-_qgZLA5p.d.ts.map} +1 -1
- package/dist/{internal-Epl-YBZW.js → internal-BTK91Bqt.js} +3 -3
- package/dist/{internal-Epl-YBZW.js.map → internal-BTK91Bqt.js.map} +1 -1
- package/dist/{interval-CELpye1n.js → interval-Dq9gt7jx.js} +3 -3
- package/dist/{interval-CELpye1n.js.map → interval-Dq9gt7jx.js.map} +1 -1
- package/dist/{journal-DfRz9kps.js → journal-C7om8Gum.js} +157 -147
- package/dist/journal-C7om8Gum.js.map +1 -0
- package/dist/json-BhdsJaOQ.js +111 -0
- package/dist/json-BhdsJaOQ.js.map +1 -0
- package/dist/{judge-calibration-BpCy5XQl.js → judge-calibration-BkXMIWA4.js} +3 -3
- package/dist/{judge-calibration-BpCy5XQl.js.map → judge-calibration-BkXMIWA4.js.map} +1 -1
- package/dist/{judge-gate-iY9s4L44.js → judge-gate-Co5tuh57.js} +4 -27
- package/dist/{judge-gate-iY9s4L44.js.map → judge-gate-Co5tuh57.js.map} +1 -1
- package/dist/{judge-gate-CE7CQAgt.d.ts → judge-gate-DsL0yFcb.d.ts} +8 -9
- package/dist/judge-gate-DsL0yFcb.d.ts.map +1 -0
- package/dist/{kind-factory-BobI5Jy3.js → kind-factory-Gy4fXixd.js} +88 -17
- package/dist/kind-factory-Gy4fXixd.js.map +1 -0
- package/dist/ledger-core/index.d.ts +2 -2
- package/dist/ledger-core/index.js +4 -4
- package/dist/ledger-core/lock-holder.d.ts +2 -0
- package/dist/ledger-core/lock-holder.js +181 -0
- package/dist/ledger-core/lock-holder.js.map +1 -0
- package/dist/{ledger-core-Gq7yp8lP.js → ledger-core-CAxUdDk0.js} +5 -84
- package/dist/ledger-core-CAxUdDk0.js.map +1 -0
- package/dist/{llm-client-CyBAoYiX.js → llm-client-_Y6ic-tf.js} +243 -355
- package/dist/llm-client-_Y6ic-tf.js.map +1 -0
- package/dist/{llm-judge-CG12SQJq.js → llm-judge-Djxh-Uhs.js} +662 -482
- package/dist/llm-judge-Djxh-Uhs.js.map +1 -0
- package/dist/lock-holder-DaJbfpci.d.ts +86 -0
- package/dist/lock-holder-DaJbfpci.d.ts.map +1 -0
- package/dist/matrix/index.d.ts +3 -3
- package/dist/matrix/index.js +3 -2
- package/dist/{matrix-Dm0BQW7U.js → matrix-B8l-Nsxd.js} +3 -88
- package/dist/matrix-B8l-Nsxd.js.map +1 -0
- package/dist/{matrix-DSrSxjjy.d.ts → matrix-CCSBna4q.d.ts} +9 -18
- package/dist/matrix-CCSBna4q.d.ts.map +1 -0
- package/dist/{mean-DHoMiudD.js → mean-BbA_JCzW.js} +39 -5
- package/dist/mean-BbA_JCzW.js.map +1 -0
- package/dist/meta-eval/index.d.ts +5 -5
- package/dist/meta-eval/index.js +10 -10
- package/dist/metrics-BKaPBtIG.js.map +1 -1
- package/dist/{mint-DNU8QPG4.d.ts → mint-BRZXuwRi.d.ts} +4 -4
- package/dist/{mint-DNU8QPG4.d.ts.map → mint-BRZXuwRi.d.ts.map} +1 -1
- package/dist/{mint-QaD5cA0u.js → mint-CbdwjTPW.js} +4 -4
- package/dist/{mint-QaD5cA0u.js.map → mint-CbdwjTPW.js.map} +1 -1
- package/dist/{multi-layer-verifier-BUaQ4C17.d.ts → multi-layer-verifier-DEgMUk6v.d.ts} +2 -2
- package/dist/{multi-layer-verifier-BUaQ4C17.d.ts.map → multi-layer-verifier-DEgMUk6v.d.ts.map} +1 -1
- package/dist/{multiplicity-DQUFcbW4.js → multiplicity-Sg0U7h8j.js} +2 -2
- package/dist/{multiplicity-DQUFcbW4.js.map → multiplicity-Sg0U7h8j.js.map} +1 -1
- package/dist/multishot/golden/index.d.ts +1 -23
- package/dist/multishot/golden/index.d.ts.map +1 -1
- package/dist/multishot/golden/index.js +3 -3
- package/dist/multishot/golden/index.js.map +1 -1
- package/dist/multishot/index.d.ts +3 -12
- package/dist/multishot/index.d.ts.map +1 -1
- package/dist/multishot/index.js +4 -3
- package/dist/multishot/index.js.map +1 -1
- package/dist/openapi.json +114 -10
- package/dist/{otel-spans-B9tUXRHF.js → otel-spans-CdN909k1.js} +3 -3
- package/dist/{otel-spans-B9tUXRHF.js.map → otel-spans-CdN909k1.js.map} +1 -1
- package/dist/{paired-arms-CwMGJsrS.js → paired-arms-Ct4LTKc1.js} +32 -9
- package/dist/paired-arms-Ct4LTKc1.js.map +1 -0
- package/dist/{paired-binary-CvpFHfKZ.js → paired-binary-B_nejA44.js} +3 -36
- package/dist/paired-binary-B_nejA44.js.map +1 -0
- package/dist/{paired-promotion-decision-DPKz8ZnF.d.ts → paired-promotion-decision-CQMJ5xGN.d.ts} +2 -34
- package/dist/paired-promotion-decision-CQMJ5xGN.d.ts.map +1 -0
- package/dist/{paired-promotion-decision-DsAPYGCm.js → paired-promotion-decision-D0SHb-ZL.js} +3 -3
- package/dist/{paired-promotion-decision-DsAPYGCm.js.map → paired-promotion-decision-D0SHb-ZL.js.map} +1 -1
- package/dist/{pareto-Cm6ndrWN.js → pareto-CTv8vozZ.js} +11 -11
- package/dist/pareto-CTv8vozZ.js.map +1 -0
- package/dist/pipelines/index.d.ts +16 -19
- package/dist/pipelines/index.d.ts.map +1 -1
- package/dist/pipelines/index.js +4 -68
- package/dist/pipelines/index.js.map +1 -1
- package/dist/{power-and-mde-Hj2ubb4B.js → power-and-mde-N5nU4_Ly.js} +3 -3
- package/dist/{power-and-mde-Hj2ubb4B.js.map → power-and-mde-N5nU4_Ly.js.map} +1 -1
- package/dist/{prime-protocol-CHKgA3a6.js → prime-protocol-BFgGrEMP.js} +3 -3
- package/dist/prime-protocol-BFgGrEMP.js.map +1 -0
- package/dist/{prime-protocol-HiB9HX7E.d.ts → prime-protocol-BWG2DQIk.d.ts} +3 -21
- package/dist/prime-protocol-BWG2DQIk.d.ts.map +1 -0
- package/dist/profile-cell.d.ts +2 -2
- package/dist/profile-cell.js +2 -2
- package/dist/{promotion-gate-BawrdPYr.d.ts → promotion-gate-CxEHRsZW.d.ts} +7 -8
- package/dist/{promotion-gate-BawrdPYr.d.ts.map → promotion-gate-CxEHRsZW.d.ts.map} +1 -1
- package/dist/{promotion-policy-KO57gbyV.js → promotion-policy-6A8osGr_.js} +4 -4
- package/dist/{promotion-policy-KO57gbyV.js.map → promotion-policy-6A8osGr_.js.map} +1 -1
- package/dist/{proposal-findings-CY1xKn_a.js → proposal-findings-n-5WBbEx.js} +3 -3
- package/dist/proposal-findings-n-5WBbEx.js.map +1 -0
- package/dist/{query-BUCZCK7F.js → query-B_RZ36an.js} +5 -5
- package/dist/query-B_RZ36an.js.map +1 -0
- package/dist/{query-DK1nWcmp.d.ts → query-Cmv_U8vf.d.ts} +4 -24
- package/dist/{query-DK1nWcmp.d.ts.map → query-Cmv_U8vf.d.ts.map} +1 -1
- package/dist/{random-ByUSnV9w.js → random-DDDcC7YU.js} +2 -2
- package/dist/{random-ByUSnV9w.js.map → random-DDDcC7YU.js.map} +1 -1
- package/dist/{raw-provider-sink-DoHz4JNG.js → raw-provider-sink-75w4FWBj.js} +2 -2
- package/dist/{raw-provider-sink-DoHz4JNG.js.map → raw-provider-sink-75w4FWBj.js.map} +1 -1
- package/dist/{redact-mtCCcT8z.d.ts → redact-DXdlFb_U.d.ts} +2 -3
- package/dist/redact-DXdlFb_U.d.ts.map +1 -0
- package/dist/{redact-B5xXKqQ3.js → redact-mtkTiJVk.js} +2 -7
- package/dist/{redact-B5xXKqQ3.js.map → redact-mtkTiJVk.js.map} +1 -1
- package/dist/{registry-D7_hm2fK.d.ts → registry-ssNoHzwD.d.ts} +4 -4
- package/dist/{registry-D7_hm2fK.d.ts.map → registry-ssNoHzwD.d.ts.map} +1 -1
- package/dist/{release-confidence-CpuP65S5.js → release-confidence-CYIFH-fp.js} +6 -11
- package/dist/release-confidence-CYIFH-fp.js.map +1 -0
- package/dist/{report-command-SC9nqb4M.js → render-Cs34y0iX.js} +625 -587
- package/dist/render-Cs34y0iX.js.map +1 -0
- package/dist/reporting.d.ts +6 -6
- package/dist/reporting.js +8 -8
- package/dist/{researcher-CEcfku-T.d.ts → researcher-GU1tV4dn.d.ts} +9 -9
- package/dist/{researcher-CEcfku-T.d.ts.map → researcher-GU1tV4dn.d.ts.map} +1 -1
- package/dist/{reward-CpCmAM8L.d.ts → reward-DrZ71vnf.d.ts} +3 -26
- package/dist/reward-DrZ71vnf.d.ts.map +1 -0
- package/dist/{reward-hacking-E0d-WVcP.js → reward-hacking-ByuETaIb.js} +3 -3
- package/dist/{reward-hacking-E0d-WVcP.js.map → reward-hacking-ByuETaIb.js.map} +1 -1
- package/dist/{reward-hacking-RVdZ6Kkt.d.ts → reward-hacking-E52wq86n.d.ts} +5 -18
- package/dist/reward-hacking-E52wq86n.d.ts.map +1 -0
- package/dist/reward-nw2xZGZG.js.map +1 -1
- package/dist/rl.d.ts +117 -295
- package/dist/rl.d.ts.map +1 -1
- package/dist/rl.js +27 -747
- package/dist/rl.js.map +1 -1
- package/dist/rollout/index.d.ts +5 -5
- package/dist/rollout/index.js +7 -8
- package/dist/{rollout-DZV1t2Wg.js → rollout-Dd1ucT_M.js} +88 -13
- package/dist/rollout-Dd1ucT_M.js.map +1 -0
- package/dist/{rubric-predictive-validity-DaFW70y9.js → rubric-predictive-validity-D3_oI72N.js} +3 -3
- package/dist/{rubric-predictive-validity-DaFW70y9.js.map → rubric-predictive-validity-D3_oI72N.js.map} +1 -1
- package/dist/{rubric-predictive-validity-C03EC7ZE.d.ts → rubric-predictive-validity-DiwT8PbL.d.ts} +2 -2
- package/dist/{rubric-predictive-validity-C03EC7ZE.d.ts.map → rubric-predictive-validity-DiwT8PbL.d.ts.map} +1 -1
- package/dist/{run-record-C1E24G2A.d.ts → run-record-CaxbLq6Y.d.ts} +4 -10
- package/dist/run-record-CaxbLq6Y.d.ts.map +1 -0
- package/dist/{run-record-C5e7FjhJ.js → run-record-CysKLB93.js} +3 -3
- package/dist/run-record-CysKLB93.js.map +1 -0
- package/dist/{run-record-Boc5IUTp.js → run-record-DhEJgoPz.js} +4 -4
- package/dist/{run-record-Boc5IUTp.js.map → run-record-DhEJgoPz.js.map} +1 -1
- package/dist/sandbox-harness-BlSOu4LX.d.ts.map +1 -1
- package/dist/{schema-CSf6qWgZ.js → schema-Bt2vLFm3.js} +2 -14
- package/dist/schema-Bt2vLFm3.js.map +1 -0
- package/dist/schema-C-ziyJD7.d.ts.map +1 -1
- package/dist/{schema-BuiujZ-g.js → schema-CvhfLjtZ.js} +78 -48
- package/dist/schema-CvhfLjtZ.js.map +1 -0
- package/dist/{schema-2g7trUwH.d.ts → schema-D6EVKzTB.d.ts} +4 -5
- package/dist/schema-D6EVKzTB.d.ts.map +1 -0
- package/dist/search/index.d.ts +3 -31
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +4 -4
- package/dist/{search-kernel-DyNEa6IU.d.ts → search-kernel-p9JB3SmL.d.ts} +10 -27
- package/dist/search-kernel-p9JB3SmL.d.ts.map +1 -0
- package/dist/{search-ledger-CVUGLDZp.js → search-ledger-ClpN4I3e.js} +7 -7
- package/dist/{search-ledger-CVUGLDZp.js.map → search-ledger-ClpN4I3e.js.map} +1 -1
- package/dist/{search-ledger-file-DDQUgvCg.js → search-ledger-file-Bd5Tioq6.js} +4 -4
- package/dist/search-ledger-file-Bd5Tioq6.js.map +1 -0
- package/dist/{search-shipper-BOSD46qv.js → search-shipper-Bg2HW3Uv.js} +4 -4
- package/dist/search-shipper-Bg2HW3Uv.js.map +1 -0
- package/dist/{search-summary-BQCSWf1g.js → search-summary-CrBuvVnf.js} +5 -5
- package/dist/{search-summary-BQCSWf1g.js.map → search-summary-CrBuvVnf.js.map} +1 -1
- package/dist/{sequential-CzK5DarL.js → sequential-BVv1sjsw.js} +2 -32
- package/dist/{sequential-CzK5DarL.js.map → sequential-BVv1sjsw.js.map} +1 -1
- package/dist/{sequential-Bf_pbE50.js → sequential-CimQrbjv.js} +5 -5
- package/dist/sequential-CimQrbjv.js.map +1 -0
- package/dist/{sequential-BhsrMupG.d.ts → sequential-KwGspfeR.d.ts} +2 -9
- package/dist/{sequential-BhsrMupG.d.ts.map → sequential-KwGspfeR.d.ts.map} +1 -1
- package/dist/{sequential-eprocess-DM_qENCl.js → sequential-eprocess-DodI6RoF.js} +2 -2
- package/dist/{sequential-eprocess-DM_qENCl.js.map → sequential-eprocess-DodI6RoF.js.map} +1 -1
- package/dist/{server-e9oWO_bN.js → server-C6M0qR8h.js} +101 -101
- package/dist/server-C6M0qR8h.js.map +1 -0
- package/dist/{single-run-lock-CV9Eyd_q.js → single-run-lock-Dekd8_Wv.js} +7 -7
- package/dist/single-run-lock-Dekd8_Wv.js.map +1 -0
- package/dist/{skillopt-optimization-method-BiwA8nfX.js → skillopt-optimization-method-5dlex7MJ.js} +1386 -1184
- package/dist/skillopt-optimization-method-5dlex7MJ.js.map +1 -0
- package/dist/{spans-BHAUXMGN.d.ts → spans-CVngGij3.d.ts} +2 -2
- package/dist/{spans-BHAUXMGN.d.ts.map → spans-CVngGij3.d.ts.map} +1 -1
- package/dist/{statistical-heldout-CYpGaoDH.d.ts → statistical-heldout-BHfLZHrm.d.ts} +8 -67
- package/dist/statistical-heldout-BHfLZHrm.d.ts.map +1 -0
- package/dist/{statistical-heldout-42NlCNDe.js → statistical-heldout-Bksv92Th.js} +3 -3
- package/dist/statistical-heldout-Bksv92Th.js.map +1 -0
- package/dist/{steps-Bg5-KVx1.d.ts → steps-DlepJPwD.d.ts} +2 -56
- package/dist/steps-DlepJPwD.d.ts.map +1 -0
- package/dist/{store-C4avFjIi.d.ts → store-B3nWRy3B.d.ts} +2 -2
- package/dist/{store-C4avFjIi.d.ts.map → store-B3nWRy3B.d.ts.map} +1 -1
- package/dist/{store-otlp-DJ9dtA5x.js → store-otlp-BteDAbHK.js} +3 -3
- package/dist/{store-otlp-DJ9dtA5x.js.map → store-otlp-BteDAbHK.js.map} +1 -1
- package/dist/{store-tool-spans-CIX5arAG.js → store-tool-spans-D5l_OfTJ.js} +8 -8
- package/dist/store-tool-spans-D5l_OfTJ.js.map +1 -0
- package/dist/{store-tool-spans-DdCSPa7C.d.ts → store-tool-spans-DZS0HWVf.d.ts} +9 -18
- package/dist/store-tool-spans-DZS0HWVf.d.ts.map +1 -0
- package/dist/storyboard/index.d.ts +4 -11
- package/dist/storyboard/index.d.ts.map +1 -1
- package/dist/storyboard/index.js +1 -16
- package/dist/storyboard/index.js.map +1 -1
- package/dist/{summary-report-V_JACR4Y.js → summary-report-BsXJoYX5.js} +88 -10
- package/dist/summary-report-BsXJoYX5.js.map +1 -0
- package/dist/{summary-report-bdFDXq0b.d.ts → summary-report-CkbRcSuu.d.ts} +25 -19
- package/dist/summary-report-CkbRcSuu.d.ts.map +1 -0
- package/dist/supervisor-run/index.d.ts +60 -28
- package/dist/supervisor-run/index.d.ts.map +1 -1
- package/dist/supervisor-run/index.js +4 -458
- package/dist/supervisor-run/index.js.map +1 -1
- package/dist/{systemone-BomhHr2c.d.ts → systemone-BMzDJb1i.d.ts} +5 -5
- package/dist/{systemone-BomhHr2c.d.ts.map → systemone-BMzDJb1i.d.ts.map} +1 -1
- package/dist/systemone-protocol.js +1 -1
- package/dist/{systemone-review-CUVcQJu6.d.ts → systemone-review-CwE6JGIp.d.ts} +2 -2
- package/dist/{systemone-review-CUVcQJu6.d.ts.map → systemone-review-CwE6JGIp.d.ts.map} +1 -1
- package/dist/{systemone-review-D7sXwsqq.js → systemone-review-DzdOJ8I6.js} +3 -3
- package/dist/{systemone-review-D7sXwsqq.js.map → systemone-review-DzdOJ8I6.js.map} +1 -1
- package/dist/systemone.d.ts +5 -5
- package/dist/systemone.js +5 -5
- package/dist/systemone.js.map +1 -1
- package/dist/{task-failure-attributes-hEmASjix.js → task-failure-attributes-BMsblaDh.js} +3 -3
- package/dist/task-failure-attributes-BMsblaDh.js.map +1 -0
- package/dist/{terminal-record-DZyQZ3pz.js → terminal-record-D4Rv-j1W.js} +244 -196
- package/dist/terminal-record-D4Rv-j1W.js.map +1 -0
- package/dist/{tool-groups-C3NEcdCM.d.ts → tool-groups-DyNz2JZ_.d.ts} +3 -3
- package/dist/tool-groups-DyNz2JZ_.d.ts.map +1 -0
- package/dist/{tool-waste-BKgvd-TP.d.ts → tool-waste-CkjzWmeY.d.ts} +25 -28
- package/dist/tool-waste-CkjzWmeY.d.ts.map +1 -0
- package/dist/{tool-waste-BlesqTqW.js → tool-waste-DLsjS_p-.js} +5 -5
- package/dist/{tool-waste-BlesqTqW.js.map → tool-waste-DLsjS_p-.js.map} +1 -1
- package/dist/{trace-questions-CM2CAocU.d.ts → trace-questions-xxwgsuMi.d.ts} +4 -9
- package/dist/{trace-questions-CM2CAocU.d.ts.map → trace-questions-xxwgsuMi.d.ts.map} +1 -1
- package/dist/trace-repair/index.d.ts +14 -211
- package/dist/trace-repair/index.d.ts.map +1 -1
- package/dist/trace-repair/index.js +865 -981
- package/dist/trace-repair/index.js.map +1 -1
- package/dist/traces.d.ts +18 -46
- package/dist/traces.d.ts.map +1 -1
- package/dist/traces.js +13 -13
- package/dist/traces.js.map +1 -1
- package/dist/trajectory-replay/index.d.ts +7 -118
- package/dist/trajectory-replay/index.d.ts.map +1 -1
- package/dist/trajectory-replay/index.js +921 -869
- package/dist/trajectory-replay/index.js.map +1 -1
- package/dist/{tree-C1XYeXvy.js → tree-B5e1uAeL.js} +132 -21
- package/dist/tree-B5e1uAeL.js.map +1 -0
- package/dist/{types-Cbe2fWBa.d.ts → types-BKCzzmit.d.ts} +3 -3
- package/dist/{types-Cbe2fWBa.d.ts.map → types-BKCzzmit.d.ts.map} +1 -1
- package/dist/{types-B3jzCp0p.js → types-BXgASBCL.js} +2 -2
- package/dist/{types-B3jzCp0p.js.map → types-BXgASBCL.js.map} +1 -1
- package/dist/{types-C1tnkEgM.d.ts → types-CW8ewATZ.d.ts} +4 -113
- package/dist/types-CW8ewATZ.d.ts.map +1 -0
- package/dist/{types-BqkVmHfv.d.ts → types-CmXY0PQf.d.ts} +2 -4
- package/dist/types-CmXY0PQf.d.ts.map +1 -0
- package/dist/{types-C02qtnG8.d.ts → types-D-mgRzlh.d.ts} +7 -28
- package/dist/types-D-mgRzlh.d.ts.map +1 -0
- package/dist/{types-IXD2HJzu.js → types-DqFMNTgK.js} +2 -4
- package/dist/types-DqFMNTgK.js.map +1 -0
- package/dist/{types-Cq8mSbq8.d.ts → types-DtkO1G9L.d.ts} +7 -9
- package/dist/{types-Cq8mSbq8.d.ts.map → types-DtkO1G9L.d.ts.map} +1 -1
- package/dist/{verdict-Dl0tiT1X.js → verdict-BGQmxQ8A.js} +3 -3
- package/dist/verdict-BGQmxQ8A.js.map +1 -0
- package/dist/{verdict-E4eRNf7-.d.ts → verdict-C24NELtN.d.ts} +2 -15
- package/dist/{verdict-E4eRNf7-.d.ts.map → verdict-C24NELtN.d.ts.map} +1 -1
- package/dist/{verdict-cache-Bm8EKL2s.js → verdict-cache-CL_7tePD.js} +2 -2
- package/dist/verdict-cache-CL_7tePD.js.map +1 -0
- package/dist/wire/index.d.ts +31 -410
- package/dist/wire/index.d.ts.map +1 -1
- package/dist/wire/index.js +2 -2
- package/docs/changes/jev-evidence-review.md +1 -1
- package/docs/changes/jev-product-adoption.md +1 -1
- package/docs/changes/jev-recorded-evidence.md +1 -1
- package/docs/design/statistics-decisions.md +18 -18
- package/docs/design.md +1 -1
- package/docs/eval-surface-map.md +1 -0
- package/docs/evaluation-integrity.md +1 -1
- package/docs/profile-json-identity.md +1 -2
- package/docs/proofs/campaign-attempt-retention-20261001/README.md +2 -2
- package/docs/public-api.md +158 -538
- package/docs/rollout.md +1 -1
- package/docs/trace-repair-admission.md +1 -1
- package/docs/wire-protocol.md +1 -1
- package/package.json +7 -2
- package/dist/active-curriculum-B_Lc9kfO.js +0 -61
- package/dist/active-curriculum-B_Lc9kfO.js.map +0 -1
- package/dist/agent-profile-VFcQncbW.d.ts.map +0 -1
- package/dist/agent-profile-cell-BK8mlqlq.js.map +0 -1
- package/dist/agent-profile-cell-BNp-Js9F.d.ts.map +0 -1
- package/dist/agreement-irr-BEWLIF3_.js.map +0 -1
- package/dist/analyze-runs-C84irm4i.js.map +0 -1
- package/dist/baseline-Udi_PLuT.js +0 -259
- package/dist/baseline-Udi_PLuT.js.map +0 -1
- package/dist/benchmark-CXmVVHH2.js +0 -562
- package/dist/benchmark-CXmVVHH2.js.map +0 -1
- package/dist/benchmark-command-By0vh8q7.js.map +0 -1
- package/dist/campaign-BudZ70zo.js.map +0 -1
- package/dist/chat-json-call-puMArmtW.js.map +0 -1
- package/dist/claude-jsonl-dZir12m6.js +0 -86
- package/dist/claude-jsonl-dZir12m6.js.map +0 -1
- package/dist/client-BzL9niqi.d.ts.map +0 -1
- package/dist/cost-ledger-BP8V4kUQ.js.map +0 -1
- package/dist/counterfactual-CPaWN99-.d.ts +0 -60
- package/dist/counterfactual-CPaWN99-.d.ts.map +0 -1
- package/dist/counterfactual-Dn3veueY.js +0 -98
- package/dist/counterfactual-Dn3veueY.js.map +0 -1
- package/dist/default-registry-COhk9g36.js.map +0 -1
- package/dist/default-registry-DzHiGruW.d.ts.map +0 -1
- package/dist/define-agent-eval-CNTPzIkp.js.map +0 -1
- package/dist/define-agent-eval-WSck0CZJ.d.ts.map +0 -1
- package/dist/dspy-rlm-engine-LRCu61m5.js.map +0 -1
- package/dist/effect-sizes-BhzEpXMc.js +0 -82
- package/dist/effect-sizes-BhzEpXMc.js.map +0 -1
- package/dist/emitter-HcCmFQl_.d.ts.map +0 -1
- package/dist/engine-3A_4jOlI.d.ts.map +0 -1
- package/dist/equal-terms-BtdXayCJ.js.map +0 -1
- package/dist/eval-campaign-90jeaCAI.js +0 -343
- package/dist/eval-campaign-90jeaCAI.js.map +0 -1
- package/dist/evaluation-6Yoi74gM.d.ts.map +0 -1
- package/dist/exporters-X4xwla8A.js.map +0 -1
- package/dist/external-optimizer-process-BjMGUuds.js.map +0 -1
- package/dist/external-optimizer-subprocess-BqW0hHcI.js.map +0 -1
- package/dist/failure-cluster-CbgEDcFJ.d.ts.map +0 -1
- package/dist/feedback-trajectory-C4EG0nNH.d.ts.map +0 -1
- package/dist/hf-dataset-BR2yhuNn.js.map +0 -1
- package/dist/index-BFRO6chs.d.ts.map +0 -1
- package/dist/index-BO41eGj5.d.ts.map +0 -1
- package/dist/index-CyszSg4O.d.ts.map +0 -1
- package/dist/index-DHKEAv3s.d.ts.map +0 -1
- package/dist/index-DPcngtwt.d.ts.map +0 -1
- package/dist/integrity-dLiDe2kD.js.map +0 -1
- package/dist/journal-DfRz9kps.js.map +0 -1
- package/dist/judge-gate-CE7CQAgt.d.ts.map +0 -1
- package/dist/kind-factory-BobI5Jy3.js.map +0 -1
- package/dist/ledger-core-Gq7yp8lP.js.map +0 -1
- package/dist/llm-client-CyBAoYiX.js.map +0 -1
- package/dist/llm-judge-CG12SQJq.js.map +0 -1
- package/dist/matrix-DSrSxjjy.d.ts.map +0 -1
- package/dist/matrix-Dm0BQW7U.js.map +0 -1
- package/dist/mean-DHoMiudD.js.map +0 -1
- package/dist/normal-DsJZtrJc.js +0 -25
- package/dist/normal-DsJZtrJc.js.map +0 -1
- package/dist/paired-arms-CwMGJsrS.js.map +0 -1
- package/dist/paired-binary-CvpFHfKZ.js.map +0 -1
- package/dist/paired-promotion-decision-DPKz8ZnF.d.ts.map +0 -1
- package/dist/pareto-Cm6ndrWN.js.map +0 -1
- package/dist/prime-protocol-CHKgA3a6.js.map +0 -1
- package/dist/prime-protocol-HiB9HX7E.d.ts.map +0 -1
- package/dist/produced-state-BsDmnwxc.js.map +0 -1
- package/dist/proposal-findings-CY1xKn_a.js.map +0 -1
- package/dist/query-BUCZCK7F.js.map +0 -1
- package/dist/redact-mtCCcT8z.d.ts.map +0 -1
- package/dist/release-confidence-CpuP65S5.js.map +0 -1
- package/dist/report-command-SC9nqb4M.js.map +0 -1
- package/dist/reward-CpCmAM8L.d.ts.map +0 -1
- package/dist/reward-hacking-RVdZ6Kkt.d.ts.map +0 -1
- package/dist/rollout-DZV1t2Wg.js.map +0 -1
- package/dist/run-record-C1E24G2A.d.ts.map +0 -1
- package/dist/run-record-C5e7FjhJ.js.map +0 -1
- package/dist/schema-2g7trUwH.d.ts.map +0 -1
- package/dist/schema-BuiujZ-g.js.map +0 -1
- package/dist/schema-CSf6qWgZ.js.map +0 -1
- package/dist/search-kernel-DyNEa6IU.d.ts.map +0 -1
- package/dist/search-ledger-file-DDQUgvCg.js.map +0 -1
- package/dist/search-shipper-BOSD46qv.js.map +0 -1
- package/dist/semantic-concept-judge-BQF8te0T.js.map +0 -1
- package/dist/sequential-Bf_pbE50.js.map +0 -1
- package/dist/server-e9oWO_bN.js.map +0 -1
- package/dist/single-run-lock-CV9Eyd_q.js.map +0 -1
- package/dist/skillopt-optimization-method-BiwA8nfX.js.map +0 -1
- package/dist/statistical-heldout-42NlCNDe.js.map +0 -1
- package/dist/statistical-heldout-CYpGaoDH.d.ts.map +0 -1
- package/dist/steps-Bg5-KVx1.d.ts.map +0 -1
- package/dist/store-tool-spans-CIX5arAG.js.map +0 -1
- package/dist/store-tool-spans-DdCSPa7C.d.ts.map +0 -1
- package/dist/student-t-DVPW7CdL.js +0 -39
- package/dist/student-t-DVPW7CdL.js.map +0 -1
- package/dist/summary-report-V_JACR4Y.js.map +0 -1
- package/dist/summary-report-bdFDXq0b.d.ts.map +0 -1
- package/dist/task-failure-attributes-hEmASjix.js.map +0 -1
- package/dist/terminal-record-DZyQZ3pz.js.map +0 -1
- package/dist/tool-groups-C3NEcdCM.d.ts.map +0 -1
- package/dist/tool-waste-BKgvd-TP.d.ts.map +0 -1
- package/dist/trajectory-Y1fmdlw7.d.ts +0 -24
- package/dist/trajectory-Y1fmdlw7.d.ts.map +0 -1
- package/dist/tree-C1XYeXvy.js.map +0 -1
- package/dist/types-BqkVmHfv.d.ts.map +0 -1
- package/dist/types-C02qtnG8.d.ts.map +0 -1
- package/dist/types-C1tnkEgM.d.ts.map +0 -1
- package/dist/types-IXD2HJzu.js.map +0 -1
- package/dist/usage-receipt-Cx3xNeyB.js +0 -76
- package/dist/usage-receipt-Cx3xNeyB.js.map +0 -1
- package/dist/verdict-Dl0tiT1X.js.map +0 -1
- package/dist/verdict-cache-Bm8EKL2s.js.map +0 -1
- package/docs/design/self-improvement-evidence-audit.md +0 -263
|
@@ -1,251 +1,107 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as
|
|
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 {
|
|
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/
|
|
15
|
+
//#region src/counterfactual.ts
|
|
15
16
|
/**
|
|
16
|
-
*
|
|
17
|
+
* Counterfactual replay — "what would have happened if we'd changed
|
|
18
|
+
* exactly one thing at turn N?"
|
|
17
19
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
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
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
63
|
+
counterfactualRunId: cfEmitter.runId,
|
|
64
|
+
originalRunId,
|
|
65
|
+
mutation,
|
|
66
|
+
delta
|
|
42
67
|
};
|
|
43
68
|
}
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
73
|
+
...step,
|
|
74
|
+
span: {
|
|
75
|
+
...llm,
|
|
76
|
+
model: mutation.newModel
|
|
77
|
+
}
|
|
106
78
|
};
|
|
107
79
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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/
|
|
384
|
+
//#region src/trajectory-replay/verify.ts
|
|
529
385
|
/**
|
|
530
|
-
*
|
|
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
|
-
|
|
1120
|
-
|
|
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
|
-
|
|
1123
|
-
const
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
const
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
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
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
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
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
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
|
|
1187
|
-
const
|
|
1188
|
-
|
|
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
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
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
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
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
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
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
|
|
1474
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
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")
|
|
1508
|
-
for (const replayCase of enumeration.replayable) if (replayCase.submitGoldsSkipped > 0)
|
|
1509
|
-
|
|
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
|
-
|
|
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 {
|
|
2209
|
+
export { PREFIX_DIVERGENCE_TOLERANCE_PCT, classifyObservation, classifyVerdict, decodeRecordedTurns, dockerImagePreparer, finalRecordedOutcome, replayVerify, replayVerifyFinding, runReplayBatch, seededSample, verifyFindings };
|
|
2158
2210
|
|
|
2159
2211
|
//# sourceMappingURL=index.js.map
|