@tangle-network/agent-eval 0.187.2 → 0.189.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +95 -0
- package/dist/adapters/http.d.ts +2 -2
- package/dist/{agent-profile-uAIQK3Nr.d.ts → agent-profile-DNV_U6QR.d.ts} +4 -4
- package/dist/{agent-profile-uAIQK3Nr.d.ts.map → agent-profile-DNV_U6QR.d.ts.map} +1 -1
- package/dist/analyst/index.d.ts +12 -12
- package/dist/analyst/index.d.ts.map +1 -1
- package/dist/analyst/index.js +7 -7
- package/dist/{analyze-runs-DapYZz02.d.ts → analyze-runs-BvK4-wSs.d.ts} +5 -5
- package/dist/{analyze-runs-DapYZz02.d.ts.map → analyze-runs-BvK4-wSs.d.ts.map} +1 -1
- package/dist/{analyze-runs-DCY7LMt4.js → analyze-runs-SCSWYpy0.js} +17 -9
- package/dist/analyze-runs-SCSWYpy0.js.map +1 -0
- package/dist/{benchmark-command-DGokT8zd.js → benchmark-command-BhhAYstU.js} +14 -14
- package/dist/benchmark-command-BhhAYstU.js.map +1 -0
- package/dist/benchmarks/index.d.ts +4 -4
- package/dist/benchmarks/index.js +3 -3
- package/dist/builder-eval/index.d.ts +3 -3
- package/dist/builder-eval/index.js +1 -1
- package/dist/campaign/index.d.ts +5 -5
- package/dist/campaign/index.js +7 -7
- package/dist/{campaign-DlxdTOvw.js → campaign-BWoSQPSU.js} +10 -10
- package/dist/{campaign-DlxdTOvw.js.map → campaign-BWoSQPSU.js.map} +1 -1
- package/dist/{campaign-evidence-IF1p0oDa.js → campaign-evidence-CBlGJ3kC.js} +5 -5
- package/dist/campaign-evidence-CBlGJ3kC.js.map +1 -0
- package/dist/{chat-client-DqPqWzVV.js → chat-client-C4dg7Ng_.js} +2 -2
- package/dist/{chat-client-DqPqWzVV.js.map → chat-client-C4dg7Ng_.js.map} +1 -1
- package/dist/{chat-json-call-C-DnDxEy.js → chat-json-call-JVGbfx95.js} +2 -2
- package/dist/{chat-json-call-C-DnDxEy.js.map → chat-json-call-JVGbfx95.js.map} +1 -1
- package/dist/cli.js +4 -4
- package/dist/{client-DCc7a8ZC.d.ts → client-BhEyAdfe.d.ts} +4 -4
- package/dist/{client-DCc7a8ZC.d.ts.map → client-BhEyAdfe.d.ts.map} +1 -1
- package/dist/{client-CXE-U1SA.js → client-QwJpsNYU.js} +5 -1
- package/dist/client-QwJpsNYU.js.map +1 -0
- package/dist/contract/index.d.ts +11 -11
- package/dist/contract/index.js +22 -13
- package/dist/contract/index.js.map +1 -1
- package/dist/{counterfactual-CLgrwhkY.d.ts → counterfactual-CPaWN99-.d.ts} +4 -4
- package/dist/{counterfactual-CLgrwhkY.d.ts.map → counterfactual-CPaWN99-.d.ts.map} +1 -1
- package/dist/{counterfactual-Bjq1mlUu.js → counterfactual-DtMWvn7D.js} +2 -2
- package/dist/{counterfactual-Bjq1mlUu.js.map → counterfactual-DtMWvn7D.js.map} +1 -1
- package/dist/deep-freeze-CurcOQg3.js +16 -0
- package/dist/deep-freeze-CurcOQg3.js.map +1 -0
- package/dist/{default-registry-DMxeb4GD.d.ts → default-registry-B5HrwrPM.d.ts} +6 -6
- package/dist/{default-registry-DMxeb4GD.d.ts.map → default-registry-B5HrwrPM.d.ts.map} +1 -1
- package/dist/{default-registry-Ca2IWH1X.js → default-registry-m2jhbG6Q.js} +4 -3
- package/dist/default-registry-m2jhbG6Q.js.map +1 -0
- package/dist/{define-agent-eval-C0EqJ9Gz.js → define-agent-eval-98sgUaC1.js} +8 -8
- package/dist/{define-agent-eval-C0EqJ9Gz.js.map → define-agent-eval-98sgUaC1.js.map} +1 -1
- package/dist/{define-agent-eval-CGBwPPaq.d.ts → define-agent-eval-q6890HU4.d.ts} +7 -7
- package/dist/{define-agent-eval-CGBwPPaq.d.ts.map → define-agent-eval-q6890HU4.d.ts.map} +1 -1
- package/dist/diagnosis/index.d.ts +38 -96
- package/dist/diagnosis/index.d.ts.map +1 -1
- package/dist/diagnosis/index.js +84 -135
- package/dist/diagnosis/index.js.map +1 -1
- package/dist/{dspy-rlm-engine-BE9lCzzL.js → dspy-rlm-engine-DLYttKBm.js} +3 -3
- package/dist/{dspy-rlm-engine-BE9lCzzL.js.map → dspy-rlm-engine-DLYttKBm.js.map} +1 -1
- package/dist/emitter-DbMXUIVP.js +334 -0
- package/dist/emitter-DbMXUIVP.js.map +1 -0
- package/dist/{emitter-Cs0egaFd.d.ts → emitter-HcCmFQl_.d.ts} +37 -6
- package/dist/emitter-HcCmFQl_.d.ts.map +1 -0
- package/dist/{engine-DNlKhIm0.d.ts → engine-BkLCnZQX.d.ts} +15 -5
- package/dist/engine-BkLCnZQX.d.ts.map +1 -0
- package/dist/{eval-campaign-XFHEpN86.js → eval-campaign-C0Q1Eeo_.js} +8 -5
- package/dist/{eval-campaign-XFHEpN86.js.map → eval-campaign-C0Q1Eeo_.js.map} +1 -1
- package/dist/evaluation.d.ts +2 -2
- package/dist/{exact-types-BEHN7tnq.d.ts → exact-types-CfpP2A4Z.d.ts} +2 -2
- package/dist/{exact-types-BEHN7tnq.d.ts.map → exact-types-CfpP2A4Z.d.ts.map} +1 -1
- package/dist/experiment/index.d.ts +3 -3
- package/dist/experiment/index.js +2 -3
- package/dist/experiment/index.js.map +1 -1
- package/dist/{external-optimizer-process-BbhvXTFx.js → external-optimizer-process-C3quNeKV.js} +3 -3
- package/dist/{external-optimizer-process-BbhvXTFx.js.map → external-optimizer-process-C3quNeKV.js.map} +1 -1
- package/dist/{external-optimizer-subprocess-DMgmqTB6.js → external-optimizer-subprocess-BDwgKLO1.js} +3 -3
- package/dist/{external-optimizer-subprocess-DMgmqTB6.js.map → external-optimizer-subprocess-BDwgKLO1.js.map} +1 -1
- package/dist/{failure-cluster-OldNRoAt.d.ts → failure-cluster-CbgEDcFJ.d.ts} +3 -3
- package/dist/{failure-cluster-OldNRoAt.d.ts.map → failure-cluster-CbgEDcFJ.d.ts.map} +1 -1
- package/dist/failure-taxonomy-CjabTATj.js +538 -0
- package/dist/failure-taxonomy-CjabTATj.js.map +1 -0
- package/dist/{feedback-trajectory-CytTR7u4.d.ts → feedback-trajectory-B3ZSkfAq.d.ts} +6 -6
- package/dist/{feedback-trajectory-CytTR7u4.d.ts.map → feedback-trajectory-B3ZSkfAq.d.ts.map} +1 -1
- package/dist/hosted/index.d.ts +2 -2
- package/dist/hosted/index.d.ts.map +1 -1
- package/dist/hosted/index.js +1 -1
- package/dist/{index-vYNHCV2G.d.ts → index-2e_MTznj.d.ts} +8 -8
- package/dist/{index-vYNHCV2G.d.ts.map → index-2e_MTznj.d.ts.map} +1 -1
- package/dist/{index-CKZa_rnt.d.ts → index-Bcc9PkOz.d.ts} +3 -3
- package/dist/{index-CKZa_rnt.d.ts.map → index-Bcc9PkOz.d.ts.map} +1 -1
- package/dist/{index-u0d1Jp4F.d.ts → index-BqDJ6fUK.d.ts} +45 -24
- package/dist/index-BqDJ6fUK.d.ts.map +1 -0
- package/dist/{index-BRsrfSHT.d.ts → index-CnJOPR_E.d.ts} +10 -10
- package/dist/{index-BRsrfSHT.d.ts.map → index-CnJOPR_E.d.ts.map} +1 -1
- package/dist/{index-YZrbnBSV.d.ts → index-Dk1dOUfm.d.ts} +11 -11
- package/dist/{index-YZrbnBSV.d.ts.map → index-Dk1dOUfm.d.ts.map} +1 -1
- package/dist/index.d.ts +384 -69
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1492 -217
- package/dist/index.js.map +1 -1
- package/dist/{insight-report-DpOo-3iL.d.ts → insight-report-CvPkEIcC.d.ts} +14 -7
- package/dist/insight-report-CvPkEIcC.d.ts.map +1 -0
- package/dist/{integrity-Cy9WHAtb.js → integrity-BFP4kjGj.js} +6 -1
- package/dist/integrity-BFP4kjGj.js.map +1 -0
- package/dist/{integrity-rGOfSUle.d.ts → integrity-CaeNeusS.d.ts} +4 -4
- package/dist/integrity-CaeNeusS.d.ts.map +1 -0
- package/dist/jev.d.ts +2 -2
- package/dist/jev.js +2 -1
- package/dist/jev.js.map +1 -1
- package/dist/{journal-BuRLm6ho.js → journal-CDP0Vhx_.js} +262 -115
- package/dist/journal-CDP0Vhx_.js.map +1 -0
- package/dist/{kind-factory-V79Hw0AU.js → kind-factory-BGwN0nE6.js} +17 -5
- package/dist/{kind-factory-V79Hw0AU.js.map → kind-factory-BGwN0nE6.js.map} +1 -1
- package/dist/ledger-core/index.d.ts +2 -2
- package/dist/ledger-core/index.js +1 -1
- package/dist/{llm-client-DDhZ29Vm.js → llm-client-DF9bPNYA.js} +2 -2
- package/dist/{llm-client-DDhZ29Vm.js.map → llm-client-DF9bPNYA.js.map} +1 -1
- package/dist/{llm-judge-BVso5x-u.js → llm-judge-M_l_WXw2.js} +26 -24
- package/dist/llm-judge-M_l_WXw2.js.map +1 -0
- package/dist/{matrix-CPKsRY8Y.d.ts → matrix-CNypzP-v.d.ts} +2 -2
- package/dist/{matrix-CPKsRY8Y.d.ts.map → matrix-CNypzP-v.d.ts.map} +1 -1
- package/dist/meta-eval/index.d.ts +4 -4
- package/dist/{mint-BI14xehJ.d.ts → mint-DsS38v5c.d.ts} +25 -5
- package/dist/{mint-BI14xehJ.d.ts.map → mint-DsS38v5c.d.ts.map} +1 -1
- package/dist/{mint-B4zgSAJW.js → mint-iCD-W43s.js} +30 -12
- package/dist/mint-iCD-W43s.js.map +1 -0
- package/dist/multishot/golden/index.d.ts +1 -1
- package/dist/multishot/index.d.ts +2 -2
- package/dist/openapi.json +1 -1
- package/dist/pipelines/index.d.ts +67 -12
- package/dist/pipelines/index.d.ts.map +1 -1
- package/dist/pipelines/index.js +178 -41
- package/dist/pipelines/index.js.map +1 -1
- package/dist/{prime-protocol-CDBcL9NM.d.ts → prime-protocol-Fwvctmhm.d.ts} +2 -2
- package/dist/{prime-protocol-CDBcL9NM.d.ts.map → prime-protocol-Fwvctmhm.d.ts.map} +1 -1
- package/dist/{produced-state-Cn9ECr4v.js → produced-state-BOY98IJO.js} +4 -4
- package/dist/{produced-state-Cn9ECr4v.js.map → produced-state-BOY98IJO.js.map} +1 -1
- package/dist/{query-D6W6MaGx.d.ts → query-DBUQhehH.d.ts} +3 -3
- package/dist/{query-D6W6MaGx.d.ts.map → query-DBUQhehH.d.ts.map} +1 -1
- package/dist/random-DGEyr3zG.d.ts +83 -0
- package/dist/random-DGEyr3zG.d.ts.map +1 -0
- package/dist/{raw-provider-sink-BQd7mzyT.js → raw-provider-sink-C96CcqeG.js} +19 -56
- package/dist/raw-provider-sink-C96CcqeG.js.map +1 -0
- package/dist/redact-DPoH1-OW.d.ts +158 -0
- package/dist/redact-DPoH1-OW.d.ts.map +1 -0
- package/dist/redact-DxwlUAvh.js +824 -0
- package/dist/redact-DxwlUAvh.js.map +1 -0
- package/dist/{registry-D5Ly3GRV.d.ts → registry-mAQVtuLx.d.ts} +4 -4
- package/dist/{registry-D5Ly3GRV.d.ts.map → registry-mAQVtuLx.d.ts.map} +1 -1
- package/dist/{release-confidence-CSp3MsU4.d.ts → release-confidence-CkoXFx9I.d.ts} +4 -4
- package/dist/{release-confidence-CSp3MsU4.d.ts.map → release-confidence-CkoXFx9I.d.ts.map} +1 -1
- package/dist/{release-confidence-RanRUXRW.js → release-confidence-XQm_xjhR.js} +3 -3
- package/dist/release-confidence-XQm_xjhR.js.map +1 -0
- package/dist/{report-command-CSTjzFGY.js → report-command-DKJ_yAIj.js} +21 -12
- package/dist/{report-command-CSTjzFGY.js.map → report-command-DKJ_yAIj.js.map} +1 -1
- package/dist/reporting.d.ts +3 -3
- package/dist/reporting.js +1 -1
- package/dist/{researcher-D7E92STT.d.ts → researcher-DojEMb7E.d.ts} +7 -7
- package/dist/{researcher-D7E92STT.d.ts.map → researcher-DojEMb7E.d.ts.map} +1 -1
- package/dist/{reward-hacking-BIQ2BWLG.d.ts → reward-hacking-Cyk-QHk9.d.ts} +2 -2
- package/dist/{reward-hacking-BIQ2BWLG.d.ts.map → reward-hacking-Cyk-QHk9.d.ts.map} +1 -1
- package/dist/rl.d.ts +11 -10
- package/dist/rl.d.ts.map +1 -1
- package/dist/rl.js +7 -6
- package/dist/rl.js.map +1 -1
- package/dist/rollout/index.d.ts +4 -4
- package/dist/rollout/index.js +2 -2
- package/dist/{rollout-C0Ifk3vK.js → rollout-B-XBReTC.js} +2 -2
- package/dist/{rollout-C0Ifk3vK.js.map → rollout-B-XBReTC.js.map} +1 -1
- package/dist/{rubric-predictive-validity-CTU8y8Cv.d.ts → rubric-predictive-validity-DIUwJJ9Y.d.ts} +2 -2
- package/dist/{rubric-predictive-validity-CTU8y8Cv.d.ts.map → rubric-predictive-validity-DIUwJJ9Y.d.ts.map} +1 -1
- package/dist/{run-record-tg_5H2IK.js → run-record-BRlM99rX.js} +41 -3
- package/dist/run-record-BRlM99rX.js.map +1 -0
- package/dist/{run-record-D9wS35fK.d.ts → run-record-DF-fuu31.d.ts} +59 -7
- package/dist/run-record-DF-fuu31.d.ts.map +1 -0
- package/dist/{run-record-CNNzWGpB.js → run-record-DnMFyg_g.js} +26 -13
- package/dist/run-record-DnMFyg_g.js.map +1 -0
- package/dist/{schema-CR5cpjQ3.d.ts → schema-2g7trUwH.d.ts} +17 -2
- package/dist/schema-2g7trUwH.d.ts.map +1 -0
- package/dist/{schema-BzWDXhOR.d.ts → schema-B5LtYvn9.d.ts} +7 -5
- package/dist/{schema-BzWDXhOR.d.ts.map → schema-B5LtYvn9.d.ts.map} +1 -1
- package/dist/schema-C1aaAxTf.js.map +1 -1
- package/dist/schema-CSf6qWgZ.js.map +1 -1
- package/dist/{semantic-concept-judge-Cnt0TWp0.js → semantic-concept-judge-Cm2K2wAo.js} +6 -6
- package/dist/{semantic-concept-judge-Cnt0TWp0.js.map → semantic-concept-judge-Cm2K2wAo.js.map} +1 -1
- package/dist/{series-convergence-BnMs_uAr.d.ts → series-convergence-DY7ha0kx.d.ts} +2 -2
- package/dist/{series-convergence-BnMs_uAr.d.ts.map → series-convergence-DY7ha0kx.d.ts.map} +1 -1
- package/dist/{server-DmpcFiVK.js → server-C8XvqCg8.js} +2 -2
- package/dist/{server-DmpcFiVK.js.map → server-C8XvqCg8.js.map} +1 -1
- package/dist/{skillopt-optimization-method-Cc9SVLqi.js → skillopt-optimization-method-BVSB676o.js} +7 -7
- package/dist/{skillopt-optimization-method-Cc9SVLqi.js.map → skillopt-optimization-method-BVSB676o.js.map} +1 -1
- package/dist/spans-W0o8pbEg.d.ts +60 -0
- package/dist/spans-W0o8pbEg.d.ts.map +1 -0
- package/dist/{statistical-heldout-DTmhTW8P.d.ts → statistical-heldout-plAHG82H.d.ts} +3 -3
- package/dist/{statistical-heldout-DTmhTW8P.d.ts.map → statistical-heldout-plAHG82H.d.ts.map} +1 -1
- package/dist/{store-BErPvYBr.d.ts → store-C4avFjIi.d.ts} +2 -2
- package/dist/{store-BErPvYBr.d.ts.map → store-C4avFjIi.d.ts.map} +1 -1
- package/dist/{store-otlp-2gYCy-v0.js → store-otlp-D0AV4cvB.js} +2 -2
- package/dist/{store-otlp-2gYCy-v0.js.map → store-otlp-D0AV4cvB.js.map} +1 -1
- package/dist/{store-tool-spans-B3bX9yqn.d.ts → store-tool-spans-8PAyhRqb.d.ts} +9 -54
- package/dist/store-tool-spans-8PAyhRqb.d.ts.map +1 -0
- package/dist/{store-tool-spans-B1e6Auc2.js → store-tool-spans-B_0mXvYl.js} +4 -4
- package/dist/{store-tool-spans-B1e6Auc2.js.map → store-tool-spans-B_0mXvYl.js.map} +1 -1
- package/dist/storyboard/index.d.ts +1 -1
- package/dist/{summary-report-Iy6kXo8x.d.ts → summary-report-DvvesgR_.d.ts} +6 -5
- package/dist/summary-report-DvvesgR_.d.ts.map +1 -0
- package/dist/supervisor-run/index.d.ts +2 -2
- package/dist/supervisor-run/index.d.ts.map +1 -1
- package/dist/supervisor-run/index.js +1 -1
- package/dist/{tool-groups-veQYPiTy.d.ts → tool-groups-W7MJobX_.d.ts} +3 -3
- package/dist/tool-groups-W7MJobX_.d.ts.map +1 -0
- package/dist/{tool-waste-nbHPAxei.js → tool-waste-CJDfxH45.js} +3 -537
- package/dist/tool-waste-CJDfxH45.js.map +1 -0
- package/dist/{tool-waste-D23I0zWm.d.ts → tool-waste-CP5DO383.d.ts} +4 -4
- package/dist/{tool-waste-D23I0zWm.d.ts.map → tool-waste-CP5DO383.d.ts.map} +1 -1
- package/dist/trace-repair/index.d.ts +2 -2
- package/dist/traces.d.ts +12 -11
- package/dist/traces.d.ts.map +1 -1
- package/dist/traces.js +9 -9
- package/dist/{trajectory-D7qrNvaN.d.ts → trajectory-Y1fmdlw7.d.ts} +3 -3
- package/dist/{trajectory-D7qrNvaN.d.ts.map → trajectory-Y1fmdlw7.d.ts.map} +1 -1
- package/dist/trajectory-replay/index.d.ts +3 -3
- package/dist/trajectory-replay/index.js +2 -2
- package/dist/{types-BjB2wZM9.d.ts → types-Bay1dh2o.d.ts} +6 -6
- package/dist/{types-BjB2wZM9.d.ts.map → types-Bay1dh2o.d.ts.map} +1 -1
- package/dist/{types-BTRcIqFr.d.ts → types-BvmWNjjs.d.ts} +4 -4
- package/dist/{types-BTRcIqFr.d.ts.map → types-BvmWNjjs.d.ts.map} +1 -1
- package/dist/{types-CBbLtr2J.d.ts → types-C0bWoSKC.d.ts} +10 -11
- package/dist/types-C0bWoSKC.d.ts.map +1 -0
- package/dist/{types-C8VBbiw6.js → types-CW5b90JH.js} +2 -15
- package/dist/types-CW5b90JH.js.map +1 -0
- package/dist/{types--18oHwVF.d.ts → types-Cvqy7asY.d.ts} +3 -3
- package/dist/{types--18oHwVF.d.ts.map → types-Cvqy7asY.d.ts.map} +1 -1
- package/dist/{types-Tp5WhYL9.d.ts → types-DRNlLbBR.d.ts} +2 -2
- package/dist/{types-Tp5WhYL9.d.ts.map → types-DRNlLbBR.d.ts.map} +1 -1
- package/dist/wire/index.d.ts +3 -3
- package/dist/wire/index.js +1 -1
- package/docs/code-agent-intake.md +1 -1
- package/docs/diagnosis.md +2 -3
- package/docs/eval-surface-map.md +1 -1
- package/docs/experiment.md +0 -1
- package/docs/insight-report.md +3 -2
- package/docs/public-api.md +2 -2
- package/docs/redaction.md +59 -0
- package/docs/rollout.md +5 -5
- package/docs/search-history-receipts.md +1 -0
- package/docs/trace-contracts.md +102 -0
- package/docs/verdicts.md +1 -1
- package/package.json +3 -2
- package/dist/analyze-runs-DCY7LMt4.js.map +0 -1
- package/dist/benchmark-command-DGokT8zd.js.map +0 -1
- package/dist/campaign-evidence-IF1p0oDa.js.map +0 -1
- package/dist/client-CXE-U1SA.js.map +0 -1
- package/dist/default-registry-Ca2IWH1X.js.map +0 -1
- package/dist/emitter-Cs0egaFd.d.ts.map +0 -1
- package/dist/emitter-DeQHiDMm.js +0 -257
- package/dist/emitter-DeQHiDMm.js.map +0 -1
- package/dist/engine-DNlKhIm0.d.ts.map +0 -1
- package/dist/experiment-tracker-BKEumQug.js +0 -280
- package/dist/experiment-tracker-BKEumQug.js.map +0 -1
- package/dist/experiment-tracker-URRGP9ek.d.ts +0 -237
- package/dist/experiment-tracker-URRGP9ek.d.ts.map +0 -1
- package/dist/index-u0d1Jp4F.d.ts.map +0 -1
- package/dist/insight-report-DpOo-3iL.d.ts.map +0 -1
- package/dist/integrity-Cy9WHAtb.js.map +0 -1
- package/dist/integrity-rGOfSUle.d.ts.map +0 -1
- package/dist/journal-BuRLm6ho.js.map +0 -1
- package/dist/llm-judge-BVso5x-u.js.map +0 -1
- package/dist/mint-B4zgSAJW.js.map +0 -1
- package/dist/raw-provider-sink-BQd7mzyT.js.map +0 -1
- package/dist/redact-7Aq1ukl-.js +0 -107
- package/dist/redact-7Aq1ukl-.js.map +0 -1
- package/dist/release-confidence-RanRUXRW.js.map +0 -1
- package/dist/run-record-CNNzWGpB.js.map +0 -1
- package/dist/run-record-D9wS35fK.d.ts.map +0 -1
- package/dist/run-record-tg_5H2IK.js.map +0 -1
- package/dist/schema-CR5cpjQ3.d.ts.map +0 -1
- package/dist/store-tool-spans-B3bX9yqn.d.ts.map +0 -1
- package/dist/summary-report-Iy6kXo8x.d.ts.map +0 -1
- package/dist/tool-groups-veQYPiTy.d.ts.map +0 -1
- package/dist/tool-waste-nbHPAxei.js.map +0 -1
- package/dist/types-C8VBbiw6.js.map +0 -1
- package/dist/types-CBbLtr2J.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
|
|
2
2
|
import { i as JudgeError, o as NotFoundError, r as ConfigError, s as ValidationError, t as AgentEvalError } from "./errors-Dngq5h35.js";
|
|
3
3
|
import { a as hashCanonical, r as canonicalString } from "./canonical-BsZVgBhW.js";
|
|
4
|
-
import { a as CODING_HARNESSES, c as agentProfileId, d as harnessAxisOf, i as verifyCompletion, l as agentProfileModelId, n as completionVerdict, o as HARNESS_NATIVE_MODEL, r as createLlmCorrectnessChecker, s as agentProfileHash, t as extractProducedState, u as expandProfileAxes } from "./produced-state-
|
|
4
|
+
import { a as CODING_HARNESSES, c as agentProfileId, d as harnessAxisOf, i as verifyCompletion, l as agentProfileModelId, n as completionVerdict, o as HARNESS_NATIVE_MODEL, r as createLlmCorrectnessChecker, s as agentProfileHash, t as extractProducedState, u as expandProfileAxes } from "./produced-state-BOY98IJO.js";
|
|
5
5
|
import { c as groupRunsByAgentProfileCell, d as validateAgentProfileCell, f as verifyAgentProfileCell, h as manifestContentDigest, i as agentProfileCellKey, m as hashJson, r as agentProfileCellHashMaterial, s as buildAgentProfileCell, t as AGENT_PROFILE_KINDS, u as toAgentProfileJson } from "./agent-profile-cell-DcFW4vOc.js";
|
|
6
6
|
import { t as mulberry32 } from "./random-Dn5fPWkt.js";
|
|
7
7
|
import { a as spearmanR, c as weightedMean, i as ranks, n as partialCredit, o as summarizeNumberSeries, r as pearsonR, s as weightedComposite, t as confidenceInterval } from "./descriptive-1V17A-qa.js";
|
|
8
8
|
import { n as calibrateJudgeContinuous, o as verbosityBias, r as continuousAgreement, t as calibrateJudge } from "./judge-calibration-DYmaBtJr.js";
|
|
9
|
-
import { a as budgetBreachView, c as
|
|
9
|
+
import { a as budgetBreachView, c as interRaterReliability, i as failureClusterView, n as computeToolUseMetrics, o as corpusInterRaterAgreement, r as judgeAgreementView, s as corpusInterRaterAgreementFromJudgeScores, t as toolWasteView } from "./tool-waste-CJDfxH45.js";
|
|
10
10
|
import { i as pairedCohensDz, n as cohensD, r as interpretCliffs, t as cliffsDelta } from "./effect-sizes-DiH8MGOH.js";
|
|
11
11
|
import { a as requiredSampleSize, c as holm, i as requiredPairedSampleSize, n as mcnemarRequiredN, o as benjaminiHochberg, r as pairedMde, s as bonferroni, t as mcnemarPower } from "./power-and-mde-BJOpt6pq.js";
|
|
12
12
|
import { _ as passAtK, a as MANN_WHITNEY_EXACT_MAX_STATES, c as mannWhitneyU, d as isBinaryOutcomeVector, f as mcnemar, g as pairedRiskDifferenceScore, h as pairedRiskDifferenceExact, i as DEFAULT_PERMUTATIONS, l as wilcoxonSignedRank, m as pairedRiskDifference, n as pairArms, o as MANN_WHITNEY_EXACT_MAX_WORK, p as pairedBinaryScale, r as pairRunRecords, s as WILCOXON_EXACT_MAX_N, t as comparePairedArms, v as wilson } from "./paired-arms-JSDcs2zx.js";
|
|
@@ -16,49 +16,50 @@ import { n as iqr, r as welchsTTest } from "./baseline-BC-eBZ7U.js";
|
|
|
16
16
|
import { i as pairedDeltaTest, n as pairedDecisionShape, r as minimumPairsForPairedDeltaTest, t as decidePairedPromotion } from "./paired-promotion-decision-6cn3bLc3.js";
|
|
17
17
|
import { a as isToolSpan, i as isLlmSpan, r as isJudgeSpan, t as FAILURE_CLASSES } from "./schema-CSf6qWgZ.js";
|
|
18
18
|
import { d as runsForScenario, r as argHash, s as judgeSpans } from "./query-DlV0pdx4.js";
|
|
19
|
-
import { r as checkCanaries, t as analyzeRuns } from "./analyze-runs-
|
|
19
|
+
import { r as checkCanaries, t as analyzeRuns } from "./analyze-runs-SCSWYpy0.js";
|
|
20
20
|
import { r as observedSplitScore, t as isRealnessGated } from "./reward-nw2xZGZG.js";
|
|
21
|
-
import { a as parseRunRecordSafe, c as
|
|
21
|
+
import { a as parseRunRecordSafe, c as runTaskScore, i as modelHasSnapshot, l as searchCellRunId, n as UNKNOWN_MODEL, o as roundTripRunRecord, r as isRunRecord, s as runCostFloorUsd, t as RunRecordValidationError, u as validateRunRecord } from "./run-record-BRlM99rX.js";
|
|
22
22
|
import { a as summaryTable, n as gainHistogram, r as paretoChart } from "./summary-report-BMvou2oZ.js";
|
|
23
23
|
import { n as contentHash, r as fileVerdictCache, t as canonicalJson } from "./verdict-cache-BQEsVy9n.js";
|
|
24
|
-
import { B as DEFAULT_RED_TEAM_CORPUS, H as redTeamReport, N as dominates, P as paretoFrontier, U as scoreRedTeamOutput, V as redTeamDataset, W as runCanaries, X as runCampaign, a as transientDispatchFailure, i as quotaExhaustedUntil, o as aggregateRunScore, s as clamp01, t as llmJudge } from "./llm-judge-
|
|
24
|
+
import { B as DEFAULT_RED_TEAM_CORPUS, H as redTeamReport, N as dominates, P as paretoFrontier, U as scoreRedTeamOutput, V as redTeamDataset, W as runCanaries, X as runCampaign, a as transientDispatchFailure, i as quotaExhaustedUntil, o as aggregateRunScore, s as clamp01, t as llmJudge } from "./llm-judge-M_l_WXw2.js";
|
|
25
25
|
import { a as resolveModelPricing, i as isModelPriced, n as estimateCost, r as estimateTokens, t as MODEL_PRICING } from "./metrics-DOMpkoeb.js";
|
|
26
26
|
import { a as CostLedgerPersistenceError, c as costForTokenPricing, i as CostLedger, l as costForUsage, n as CostCallConflictError, o as CostReceiptCaptureError, r as CostCeilingReachedError, s as CostReservationExceededError, t as CostAccountingIncompleteError, u as modelPriceKey } from "./cost-ledger-_IbkejCF.js";
|
|
27
|
-
import { D as parseReflectionResponse, E as buildReflectionPrompt, G as summarizeBackendIntegrity, U as BackendIntegrityError, W as assertRealBackend, c as ATTESTATION_ALGORITHM, l as attest, u as verifyAttestation, z as surfaceContentHash } from "./campaign-evidence-
|
|
28
|
-
import { n as REDACTION_VERSION, r as
|
|
29
|
-
import {
|
|
30
|
-
import { r as selfImprove, t as defineAgentEval } from "./define-agent-eval-C0EqJ9Gz.js";
|
|
27
|
+
import { D as parseReflectionResponse, E as buildReflectionPrompt, G as summarizeBackendIntegrity, U as BackendIntegrityError, W as assertRealBackend, c as ATTESTATION_ALGORITHM, l as attest, u as verifyAttestation, z as surfaceContentHash } from "./campaign-evidence-CBlGJ3kC.js";
|
|
28
|
+
import { d as redactForShare, f as redactText, n as REDACTION_VERSION, p as shareAllowed, r as assessShareSafety, u as redact } from "./redact-DxwlUAvh.js";
|
|
29
|
+
import { r as selfImprove, t as defineAgentEval } from "./define-agent-eval-98sgUaC1.js";
|
|
31
30
|
import { n as equivalenceVerdict, t as certificationEvidenceDigest } from "./verdict-DhPoXApk.js";
|
|
32
|
-
import { t as TraceEmitter } from "./emitter-
|
|
31
|
+
import { t as TraceEmitter } from "./emitter-DbMXUIVP.js";
|
|
33
32
|
import { t as buildTrajectory } from "./trajectory-D_7rLrvE.js";
|
|
34
|
-
import { t as runCounterfactual } from "./counterfactual-
|
|
35
|
-
import {
|
|
36
|
-
import { _ as completedAnalystReviewQuality, b as validateAnalystReviewDecisions, c as FAILURE_MODE_KIND_SPEC, g as assertUniqueFindingIds, h as analystRunDigest, i as DEFAULT_TRACE_ANALYST_KINDS, m as analystFindingDigest, n as AnalystRegistry, t as buildDefaultAnalystRegistry, v as readAnalystReview, y as snapshotAnalystRun } from "./default-registry-
|
|
33
|
+
import { t as runCounterfactual } from "./counterfactual-DtMWvn7D.js";
|
|
34
|
+
import { H as parseFindingSubject, W as DEFAULT_TRACE_ANALYST_OUTPUT_TOKENS, h as TRACE_ANALYST_TRUNCATION_MARKER_PREFIX, m as DEFAULT_TRACE_ANALYST_BUDGETS, t as createTraceAnalyst, u as createBoundedTraceAnalysisStore } from "./kind-factory-BGwN0nE6.js";
|
|
35
|
+
import { _ as completedAnalystReviewQuality, b as validateAnalystReviewDecisions, c as FAILURE_MODE_KIND_SPEC, g as assertUniqueFindingIds, h as analystRunDigest, i as DEFAULT_TRACE_ANALYST_KINDS, m as analystFindingDigest, n as AnalystRegistry, t as buildDefaultAnalystRegistry, v as readAnalystReview, y as snapshotAnalystRun } from "./default-registry-m2jhbG6Q.js";
|
|
37
36
|
import { OUTPUT_VALUE } from "./trace-attributes.js";
|
|
38
37
|
import { r as extractUsageFromSse, t as extractUsage } from "./extract-usage-BrQ8mCLX.js";
|
|
39
|
-
import { n as InMemoryRawProviderSink, r as NoopRawProviderSink, t as FileSystemRawProviderSink } from "./raw-provider-sink-
|
|
40
|
-
import { D as captureFetchToRawSink, S as exportRunAsOtlp, _ as tokenizeDomainWords, d as inferDomainKeywords, g as scoreTraceInsightReadiness, l as describeTraceInsightScope, n as toolSpansToTraceAnalysisStore, o as buildTraceInsightContext, s as buildTraceInsightPrompt, u as domainEvidencePattern, v as analyzeTraces } from "./store-tool-spans-
|
|
41
|
-
import { n as assertRunCaptured, t as RunIntegrityError } from "./integrity-
|
|
38
|
+
import { n as InMemoryRawProviderSink, r as NoopRawProviderSink, t as FileSystemRawProviderSink } from "./raw-provider-sink-C96CcqeG.js";
|
|
39
|
+
import { D as captureFetchToRawSink, S as exportRunAsOtlp, _ as tokenizeDomainWords, d as inferDomainKeywords, g as scoreTraceInsightReadiness, l as describeTraceInsightScope, n as toolSpansToTraceAnalysisStore, o as buildTraceInsightContext, s as buildTraceInsightPrompt, u as domainEvidencePattern, v as analyzeTraces } from "./store-tool-spans-B_0mXvYl.js";
|
|
40
|
+
import { n as assertRunCaptured, t as RunIntegrityError } from "./integrity-BFP4kjGj.js";
|
|
42
41
|
import { n as InMemoryTraceStore, t as FileSystemTraceStore } from "./store-DNe_Uv1Q.js";
|
|
43
42
|
import { t as packageVersion$1 } from "./package-version-D7lQHt_-.js";
|
|
43
|
+
import { a as classifyFailure, i as INFRA_FAILURE_BLAMES, o as classifyFailureReason, r as FAILURE_BLAME, s as failureBlame, t as DEFAULT_FAILURE_REASON_RULES } from "./failure-taxonomy-CjabTATj.js";
|
|
44
44
|
import { n as runBoundedProcess, t as DEFAULT_BOUNDED_PROCESS_TIMEOUT_MS } from "./bounded-process-BBZob7vl.js";
|
|
45
|
-
import { t as runEvalCampaign } from "./eval-campaign-
|
|
46
|
-
import
|
|
47
|
-
import "./
|
|
48
|
-
import { t as mintRolloutRows } from "./mint-B4zgSAJW.js";
|
|
45
|
+
import { t as runEvalCampaign } from "./eval-campaign-C0Q1Eeo_.js";
|
|
46
|
+
import "./rollout-B-XBReTC.js";
|
|
47
|
+
import { t as mintRolloutRows } from "./mint-iCD-W43s.js";
|
|
49
48
|
import { n as pairedEvalueSequence, t as evaluateInterimReleaseConfidence } from "./sequential-CzK5DarL.js";
|
|
50
|
-
import { S as judgeFamily, _ as assertServedModels, b as CrossFamilyError, d as stripFencedJson, f as ModelSubstitutionError, h as assertServedModel, i as backoffMs, l as isTransientLlmError, m as assertCrossFamilyServed, o as costReceiptFromLlm, p as ServedCrossFamilyError, r as LlmResponseError, s as costReceiptFromLlmError, t as LlmCallError, u as maximumChargeForLlmRequest, v as checkServedModel, x as assertCrossFamily, y as servedModelAcceptable } from "./llm-client-
|
|
51
|
-
import { n as paidJsonChat } from "./chat-json-call-
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
49
|
+
import { S as judgeFamily, _ as assertServedModels, b as CrossFamilyError, d as stripFencedJson, f as ModelSubstitutionError, h as assertServedModel, i as backoffMs, l as isTransientLlmError, m as assertCrossFamilyServed, o as costReceiptFromLlm, p as ServedCrossFamilyError, r as LlmResponseError, s as costReceiptFromLlmError, t as LlmCallError, u as maximumChargeForLlmRequest, v as checkServedModel, x as assertCrossFamily, y as servedModelAcceptable } from "./llm-client-DF9bPNYA.js";
|
|
50
|
+
import { n as paidJsonChat } from "./chat-json-call-JVGbfx95.js";
|
|
51
|
+
import { i as makeProposalFinding, n as computeFindingId, r as makeFinding } from "./types-CW5b90JH.js";
|
|
52
|
+
import { t as createDspyRlmTraceEngine } from "./dspy-rlm-engine-DLYttKBm.js";
|
|
53
|
+
import { a as diffFindings, n as runSemanticConceptJudge, o as createChatTraceEngine, r as FindingsStore, t as SEMANTIC_CONCEPT_JUDGE_VERSION } from "./semantic-concept-judge-Cm2K2wAo.js";
|
|
54
54
|
import { t as analyzeSeries } from "./series-convergence-CjO2QdRW.js";
|
|
55
|
-
import { i as otlpTextToTraceAnalysisStore, n as OtlpFileTraceStore } from "./store-otlp-
|
|
56
|
-
import { n as evaluateReleaseConfidence, r as bootstrapCi } from "./release-confidence-
|
|
57
|
-
import { t as createChatClient } from "./chat-client-
|
|
55
|
+
import { i as otlpTextToTraceAnalysisStore, n as OtlpFileTraceStore } from "./store-otlp-D0AV4cvB.js";
|
|
56
|
+
import { n as evaluateReleaseConfidence, r as bootstrapCi } from "./release-confidence-XQm_xjhR.js";
|
|
57
|
+
import { t as createChatClient } from "./chat-client-C4dg7Ng_.js";
|
|
58
58
|
import { createHash } from "node:crypto";
|
|
59
59
|
import { accessSync, appendFileSync, constants, cpSync, existsSync, mkdirSync, promises, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
60
60
|
import { basename, delimiter, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
|
|
61
61
|
import { spawnSync } from "node:child_process";
|
|
62
|
+
import { INPUT_TOKEN_ATTR_KEYS, MODEL_ATTR_KEYS, OUTPUT_TOKEN_ATTR_KEYS, SPAN_KINDS, TOOL_NAME_ATTR_KEYS, firstNumberAttr, firstStringAttr, resolveSpanKind } from "@tangle-network/agent-trace-contract";
|
|
62
63
|
//#region src/profile/baselines.ts
|
|
63
64
|
/**
|
|
64
65
|
* Strong, generically-useful baseline ROLES — the top zone of an `AgentProfile`
|
|
@@ -966,7 +967,7 @@ function stableHash(input) {
|
|
|
966
967
|
* malformed line never breaks the read. `diffScorecard` compares the latest
|
|
967
968
|
* entry of each cell against its predecessor with Cohen's d + Welch's t-test.
|
|
968
969
|
*/
|
|
969
|
-
function median(xs) {
|
|
970
|
+
function median$1(xs) {
|
|
970
971
|
if (xs.length === 0) return 0;
|
|
971
972
|
const sorted = [...xs].sort((a, b) => a - b);
|
|
972
973
|
const mid = Math.floor(sorted.length / 2);
|
|
@@ -1024,7 +1025,7 @@ function recordRuns(runs, opts) {
|
|
|
1024
1025
|
commitSha: opts.commitSha,
|
|
1025
1026
|
timestamp,
|
|
1026
1027
|
scores,
|
|
1027
|
-
composite: median(scores),
|
|
1028
|
+
composite: median$1(scores),
|
|
1028
1029
|
runIds: scored.map((s) => s.run.runId)
|
|
1029
1030
|
};
|
|
1030
1031
|
const perDimension = aggregatePerDimension(scenarioRuns);
|
|
@@ -1184,88 +1185,383 @@ function formatScorecardDiff(diff) {
|
|
|
1184
1185
|
return lines.join("\n");
|
|
1185
1186
|
}
|
|
1186
1187
|
//#endregion
|
|
1187
|
-
//#region src/trace-contracts.ts
|
|
1188
|
+
//#region src/trace-contracts/spans.ts
|
|
1189
|
+
const EVAL_KIND_TO_CONTRACT_KIND = /* @__PURE__ */ new Map([["judge", "EVALUATOR"], ["retrieval", "RETRIEVER"]]);
|
|
1190
|
+
/**
|
|
1191
|
+
* The span's contract kind: a declared kind when recognised, else inferred.
|
|
1192
|
+
* A top-level `kind` counts as a declaration only when it names a contract
|
|
1193
|
+
* kind; OTLP words such as `SPAN_KIND_INTERNAL` and the eval kinds `sandbox`
|
|
1194
|
+
* and `custom` fall through to the span-kind attribute and name signals.
|
|
1195
|
+
*/
|
|
1196
|
+
function contractSpanKind(span) {
|
|
1197
|
+
const raw = typeof span.kind === "string" ? span.kind : void 0;
|
|
1198
|
+
const mapped = raw === void 0 ? void 0 : EVAL_KIND_TO_CONTRACT_KIND.get(raw) ?? raw.toUpperCase();
|
|
1199
|
+
const declared = mapped !== void 0 && SPAN_KINDS.includes(mapped) ? mapped : void 0;
|
|
1200
|
+
return resolveSpanKind({
|
|
1201
|
+
...declared === void 0 ? {} : { kind: declared },
|
|
1202
|
+
name: span.name,
|
|
1203
|
+
attributes: span.attributes ?? {}
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
/**
|
|
1207
|
+
* Tool name: `span.toolName` (typed ToolSpan) → the tool-name attributes
|
|
1208
|
+
* (`gen_ai.tool.name`, `tool.name`, ..., `toolName`) → `span.name` when the
|
|
1209
|
+
* span is a TOOL span (otel-bridge's `ExportableSpan` drops `toolName`).
|
|
1210
|
+
*/
|
|
1211
|
+
function contractSpanToolName(span) {
|
|
1212
|
+
if (typeof span.toolName === "string") return span.toolName;
|
|
1213
|
+
const attributes = span.attributes ?? {};
|
|
1214
|
+
const fromAttr = firstStringAttr(attributes, TOOL_NAME_ATTR_KEYS) ?? firstStringAttr(attributes, ["toolName"]);
|
|
1215
|
+
if (fromAttr !== void 0) return fromAttr;
|
|
1216
|
+
if (typeof span.name === "string" && contractSpanKind(span) === "TOOL") return span.name;
|
|
1217
|
+
}
|
|
1218
|
+
function spanModel(span) {
|
|
1219
|
+
if (typeof span.model === "string" && span.model.length > 0) return span.model;
|
|
1220
|
+
return firstStringAttr(span.attributes ?? {}, MODEL_ATTR_KEYS);
|
|
1221
|
+
}
|
|
1222
|
+
/** Input plus output tokens, or `undefined` when either side is unrecorded. */
|
|
1223
|
+
function spanTotalTokens(span) {
|
|
1224
|
+
const attributes = span.attributes ?? {};
|
|
1225
|
+
const input = finite(span.inputTokens) ?? firstNumberAttr(attributes, INPUT_TOKEN_ATTR_KEYS);
|
|
1226
|
+
const output = finite(span.outputTokens) ?? firstNumberAttr(attributes, OUTPUT_TOKEN_ATTR_KEYS);
|
|
1227
|
+
return input === void 0 || output === void 0 ? void 0 : input + output;
|
|
1228
|
+
}
|
|
1229
|
+
const ARGUMENT_ATTR_KEYS = [
|
|
1230
|
+
"gen_ai.tool.call.arguments",
|
|
1231
|
+
"tool.arguments",
|
|
1232
|
+
"input.value"
|
|
1233
|
+
];
|
|
1234
|
+
/** The call's arguments: the typed `args` field, else the argument attributes.
|
|
1235
|
+
* JSON strings are parsed; an unparseable string is kept as a string. */
|
|
1236
|
+
function spanArguments(span) {
|
|
1237
|
+
if (span.argsCaptured === false || span.attributes?.["tool.args_captured"] === false) return {
|
|
1238
|
+
known: false,
|
|
1239
|
+
reason: "arguments were not captured"
|
|
1240
|
+
};
|
|
1241
|
+
if (span.args !== void 0) return {
|
|
1242
|
+
known: true,
|
|
1243
|
+
value: parseJsonString(span.args)
|
|
1244
|
+
};
|
|
1245
|
+
for (const key of ARGUMENT_ATTR_KEYS) {
|
|
1246
|
+
const value = span.attributes?.[key];
|
|
1247
|
+
if (value !== void 0 && value !== null) return {
|
|
1248
|
+
known: true,
|
|
1249
|
+
value: parseJsonString(value)
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
return {
|
|
1253
|
+
known: false,
|
|
1254
|
+
reason: "the span carries no argument evidence"
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
function parseJsonString(value) {
|
|
1258
|
+
if (typeof value !== "string") return value;
|
|
1259
|
+
try {
|
|
1260
|
+
return JSON.parse(value);
|
|
1261
|
+
} catch {
|
|
1262
|
+
return value;
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
function finite(value) {
|
|
1266
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
1267
|
+
}
|
|
1268
|
+
function isSpanError(span) {
|
|
1269
|
+
return typeof span.status === "string" && /^(?:error|status_code_error)$/i.test(span.status);
|
|
1270
|
+
}
|
|
1271
|
+
//#endregion
|
|
1272
|
+
//#region src/trace-contracts/predicates.ts
|
|
1188
1273
|
function isSerializedRegex(v) {
|
|
1189
1274
|
return typeof v === "object" && v !== null && typeof v.$regex === "string" && typeof v.flags === "string";
|
|
1190
1275
|
}
|
|
1276
|
+
function isOneOfMatcher(v) {
|
|
1277
|
+
return typeof v === "object" && v !== null && Array.isArray(v.oneOf);
|
|
1278
|
+
}
|
|
1279
|
+
function isRegexValue(v) {
|
|
1280
|
+
return v instanceof RegExp || isSerializedRegex(v);
|
|
1281
|
+
}
|
|
1191
1282
|
function matchText(actual, matcher) {
|
|
1192
1283
|
if (typeof actual !== "string") return false;
|
|
1193
1284
|
if (typeof matcher === "string") return actual === matcher;
|
|
1194
1285
|
if (matcher instanceof RegExp) return matcher.test(actual);
|
|
1286
|
+
if (isOneOfMatcher(matcher)) return matcher.oneOf.includes(actual);
|
|
1195
1287
|
return new RegExp(matcher.$regex, matcher.flags).test(actual);
|
|
1196
1288
|
}
|
|
1197
|
-
function
|
|
1198
|
-
if (typeof
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1289
|
+
function assertMatcher(m, where) {
|
|
1290
|
+
if (typeof m === "string" || m instanceof RegExp) return;
|
|
1291
|
+
if (isSerializedRegex(m)) {
|
|
1292
|
+
try {
|
|
1293
|
+
new RegExp(m.$regex, m.flags);
|
|
1294
|
+
} catch (error) {
|
|
1295
|
+
throw new ValidationError(`${where}: invalid regex /${m.$regex}/${m.flags}: ${String(error)}`);
|
|
1296
|
+
}
|
|
1297
|
+
return;
|
|
1298
|
+
}
|
|
1299
|
+
if (isOneOfMatcher(m)) {
|
|
1300
|
+
if (m.oneOf.length === 0 || !m.oneOf.every((x) => typeof x === "string")) throw new ValidationError(`${where}: "oneOf" must be a non-empty array of strings`);
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
throw new ValidationError(`${where}: matcher must be string | RegExp | SerializedRegex | { oneOf }`);
|
|
1202
1304
|
}
|
|
1203
1305
|
function assertPredicate(value, where) {
|
|
1204
|
-
if (value === null || typeof value !== "object") throw new ValidationError(`${where}: predicate must be an object, got ${typeof value}`);
|
|
1306
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new ValidationError(`${where}: predicate must be an object, got ${typeof value}`);
|
|
1205
1307
|
const p = value;
|
|
1206
|
-
for (const field of [
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1308
|
+
for (const field of [
|
|
1309
|
+
"name",
|
|
1310
|
+
"tool",
|
|
1311
|
+
"model"
|
|
1312
|
+
]) if (p[field] !== void 0) assertMatcher(p[field], `${where} "${field}"`);
|
|
1313
|
+
if (p.kind !== void 0 && !SPAN_KINDS.includes(p.kind)) throw new ValidationError(`${where}: "kind" must be one of ${SPAN_KINDS.join(", ")}`);
|
|
1210
1314
|
if (p.attr !== void 0 && (p.attr === null || typeof p.attr !== "object" || Array.isArray(p.attr))) throw new ValidationError(`${where}: "attr" must be a plain object`);
|
|
1315
|
+
for (const [key, expected] of Object.entries(p.attr ?? {})) if (isSerializedRegex(expected)) assertMatcher(expected, `${where} attr "${key}"`);
|
|
1316
|
+
if (p.not !== void 0) assertPredicate(p.not, `${where} "not"`);
|
|
1211
1317
|
if (p.requiresCustom && typeof p.custom !== "function") throw new ValidationError(`${where}: rule was built with a custom predicate function, which does not survive JSON serialization — re-attach \`custom\` after deserializing or drop the rule`);
|
|
1212
1318
|
const hasAttr = p.attr !== void 0 && Object.keys(p.attr).length > 0;
|
|
1213
|
-
if (p.name === void 0 && p.tool === void 0 && !hasAttr && typeof p.custom !== "function") throw new ValidationError(`${where}: empty predicate would match every span — specify name, tool, attr, or custom`);
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
if (
|
|
1219
|
-
if (
|
|
1220
|
-
if (
|
|
1319
|
+
if (p.name === void 0 && p.tool === void 0 && p.model === void 0 && p.kind === void 0 && p.not === void 0 && !hasAttr && typeof p.custom !== "function") throw new ValidationError(`${where}: empty predicate would match every span — specify name, tool, model, kind, attr, not, or custom`);
|
|
1320
|
+
}
|
|
1321
|
+
function predicateMatches(span, p) {
|
|
1322
|
+
if (p.kind !== void 0 && contractSpanKind(span) !== p.kind) return false;
|
|
1323
|
+
if (p.name !== void 0 && !matchText(span.name, p.name)) return false;
|
|
1324
|
+
if (p.tool !== void 0 && !matchText(contractSpanToolName(span), p.tool)) return false;
|
|
1325
|
+
if (p.model !== void 0 && !matchText(spanModel(span), p.model)) return false;
|
|
1326
|
+
if (p.attr !== void 0) for (const [key, expected] of Object.entries(p.attr)) {
|
|
1221
1327
|
const actual = span.attributes?.[key];
|
|
1222
|
-
if (
|
|
1328
|
+
if (isRegexValue(expected)) {
|
|
1223
1329
|
if (!matchText(typeof actual === "string" ? actual : void 0, expected)) return false;
|
|
1224
1330
|
} else if (actual !== expected) return false;
|
|
1225
1331
|
}
|
|
1226
|
-
if (
|
|
1332
|
+
if (p.not !== void 0 && predicateMatches(span, p.not)) return false;
|
|
1333
|
+
if (p.custom !== void 0 && !p.custom(span)) return false;
|
|
1227
1334
|
return true;
|
|
1228
1335
|
}
|
|
1336
|
+
function normalizeMatcher(m) {
|
|
1337
|
+
if (m instanceof RegExp) return {
|
|
1338
|
+
$regex: m.source,
|
|
1339
|
+
flags: m.flags
|
|
1340
|
+
};
|
|
1341
|
+
if (isOneOfMatcher(m)) return { oneOf: [...m.oneOf] };
|
|
1342
|
+
return m;
|
|
1343
|
+
}
|
|
1344
|
+
function normalizePredicate(p, where) {
|
|
1345
|
+
const kind = typeof p?.kind === "string" ? p.kind.toUpperCase() : p?.kind;
|
|
1346
|
+
const withKind = kind === void 0 ? p : {
|
|
1347
|
+
...p,
|
|
1348
|
+
kind
|
|
1349
|
+
};
|
|
1350
|
+
assertPredicate(withKind, where);
|
|
1351
|
+
const out = {};
|
|
1352
|
+
if (withKind.kind !== void 0) out.kind = withKind.kind;
|
|
1353
|
+
if (withKind.name !== void 0) out.name = normalizeMatcher(withKind.name);
|
|
1354
|
+
if (withKind.tool !== void 0) out.tool = normalizeMatcher(withKind.tool);
|
|
1355
|
+
if (withKind.model !== void 0) out.model = normalizeMatcher(withKind.model);
|
|
1356
|
+
if (withKind.attr !== void 0) out.attr = Object.fromEntries(Object.entries(withKind.attr).map(([k, v]) => [k, v instanceof RegExp ? normalizeMatcher(v) : v]));
|
|
1357
|
+
if (withKind.not !== void 0) out.not = normalizePredicate(withKind.not, `${where} "not"`);
|
|
1358
|
+
if (typeof withKind.custom === "function") {
|
|
1359
|
+
out.custom = withKind.custom;
|
|
1360
|
+
out.requiresCustom = true;
|
|
1361
|
+
}
|
|
1362
|
+
return out;
|
|
1363
|
+
}
|
|
1364
|
+
//#endregion
|
|
1365
|
+
//#region src/trace-contracts/types.ts
|
|
1366
|
+
const ORDER_MODES = [
|
|
1367
|
+
"start-order",
|
|
1368
|
+
"finish-before-start",
|
|
1369
|
+
"all-occurrences"
|
|
1370
|
+
];
|
|
1371
|
+
const RUN_STATUSES = [
|
|
1372
|
+
"running",
|
|
1373
|
+
"completed",
|
|
1374
|
+
"failed",
|
|
1375
|
+
"aborted"
|
|
1376
|
+
];
|
|
1377
|
+
const ARGUMENT_OCCURRENCES = [
|
|
1378
|
+
"first",
|
|
1379
|
+
"last",
|
|
1380
|
+
"any",
|
|
1381
|
+
"all"
|
|
1382
|
+
];
|
|
1383
|
+
const ARGUMENT_TYPES = [
|
|
1384
|
+
"string",
|
|
1385
|
+
"number",
|
|
1386
|
+
"boolean",
|
|
1387
|
+
"object",
|
|
1388
|
+
"array",
|
|
1389
|
+
"null"
|
|
1390
|
+
];
|
|
1391
|
+
//#endregion
|
|
1392
|
+
//#region src/trace-contracts/validate.ts
|
|
1393
|
+
function assertOrder(order, where) {
|
|
1394
|
+
if (order === void 0) return void 0;
|
|
1395
|
+
if (!ORDER_MODES.includes(order)) throw new ValidationError(`${where}: order must be one of ${ORDER_MODES.join(", ")}`);
|
|
1396
|
+
return order;
|
|
1397
|
+
}
|
|
1398
|
+
function assertCount(max, where) {
|
|
1399
|
+
if (typeof max !== "number" || !Number.isInteger(max) || max < 0) throw new ValidationError(`${where}: max must be a non-negative integer`);
|
|
1400
|
+
}
|
|
1401
|
+
function assertJsonPointer(pointer, where) {
|
|
1402
|
+
if (typeof pointer !== "string" || pointer !== "" && !pointer.startsWith("/")) throw new ValidationError(`${where}: pointer must be an RFC 6901 JSON Pointer ('' or '/a/b')`);
|
|
1403
|
+
if (/~(?![01])/.test(pointer)) throw new ValidationError(`${where}: pointer has an invalid escape; use ~0 for ~ and ~1 for /`);
|
|
1404
|
+
}
|
|
1405
|
+
function assertArgumentCheck(check, where) {
|
|
1406
|
+
const c = check;
|
|
1407
|
+
if (c === null || typeof c !== "object") throw new ValidationError(`${where}: check must be an object`);
|
|
1408
|
+
switch (c.op) {
|
|
1409
|
+
case "exists": return;
|
|
1410
|
+
case "equals":
|
|
1411
|
+
if (!("value" in c)) throw new ValidationError(`${where}: equals needs a value`);
|
|
1412
|
+
return;
|
|
1413
|
+
case "oneOf":
|
|
1414
|
+
if (!Array.isArray(c.values) || c.values.length === 0) throw new ValidationError(`${where}: oneOf needs a non-empty values array`);
|
|
1415
|
+
return;
|
|
1416
|
+
case "type":
|
|
1417
|
+
if (!ARGUMENT_TYPES.includes(c.type)) throw new ValidationError(`${where}: type must be one of ${ARGUMENT_TYPES.join(", ")}`);
|
|
1418
|
+
return;
|
|
1419
|
+
default: throw new ValidationError(`${where}: check.op must be exists, equals, oneOf, or type`);
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
function assertRule(rule, where) {
|
|
1423
|
+
switch (rule.kind) {
|
|
1424
|
+
case "always":
|
|
1425
|
+
case "never":
|
|
1426
|
+
case "eventually":
|
|
1427
|
+
assertPredicate(rule.p, `${where} (p)`);
|
|
1428
|
+
return;
|
|
1429
|
+
case "precedes":
|
|
1430
|
+
assertPredicate(rule.a, `${where} (a)`);
|
|
1431
|
+
assertPredicate(rule.b, `${where} (b)`);
|
|
1432
|
+
assertOrder(rule.order, where);
|
|
1433
|
+
return;
|
|
1434
|
+
case "neverUnless":
|
|
1435
|
+
assertPredicate(rule.p, `${where} (p)`);
|
|
1436
|
+
assertPredicate(rule.prior, `${where} (prior)`);
|
|
1437
|
+
assertOrder(rule.order, where);
|
|
1438
|
+
return;
|
|
1439
|
+
case "atMost":
|
|
1440
|
+
case "tokensAtMost":
|
|
1441
|
+
assertPredicate(rule.p, `${where} (p)`);
|
|
1442
|
+
assertCount(rule.max, where);
|
|
1443
|
+
return;
|
|
1444
|
+
case "run":
|
|
1445
|
+
if (rule.requireCompleted === void 0 && rule.allowedStatuses === void 0 && rule.maxDurationMs === void 0) throw new ValidationError(`${where}: run rule checks nothing`);
|
|
1446
|
+
if (rule.requireCompleted !== void 0 && typeof rule.requireCompleted !== "boolean") throw new ValidationError(`${where}: requireCompleted must be a boolean`);
|
|
1447
|
+
if (rule.allowedStatuses !== void 0) {
|
|
1448
|
+
if (!Array.isArray(rule.allowedStatuses) || rule.allowedStatuses.length === 0) throw new ValidationError(`${where}: allowedStatuses must be a non-empty array`);
|
|
1449
|
+
for (const status of rule.allowedStatuses) if (!RUN_STATUSES.includes(status)) throw new ValidationError(`${where}: unknown run status "${String(status)}" — expected one of ${RUN_STATUSES.join(", ")}`);
|
|
1450
|
+
}
|
|
1451
|
+
if (rule.maxDurationMs !== void 0 && (typeof rule.maxDurationMs !== "number" || !Number.isFinite(rule.maxDurationMs) || rule.maxDurationMs <= 0)) throw new ValidationError(`${where}: maxDurationMs must be a positive number`);
|
|
1452
|
+
return;
|
|
1453
|
+
case "argument":
|
|
1454
|
+
assertPredicate(rule.p, `${where} (p)`);
|
|
1455
|
+
assertJsonPointer(rule.pointer, where);
|
|
1456
|
+
assertArgumentCheck(rule.check, where);
|
|
1457
|
+
if (rule.occurrence !== void 0 && !ARGUMENT_OCCURRENCES.includes(rule.occurrence)) throw new ValidationError(`${where}: occurrence must be one of ${ARGUMENT_OCCURRENCES.join(", ")}`);
|
|
1458
|
+
return;
|
|
1459
|
+
default: throw new ValidationError(`${where}: unknown rule kind "${String(rule.kind)}"`);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
function assertRules(rules, where, allowEmpty = false) {
|
|
1463
|
+
if (!Array.isArray(rules) || rules.length === 0 && !allowEmpty) throw new ValidationError(`${where}: no rules — an empty contract would vacuously pass`);
|
|
1464
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1465
|
+
for (const rule of rules) {
|
|
1466
|
+
const ruleWhere = `${where} rule "${rule?.label ?? "<unlabeled>"}"`;
|
|
1467
|
+
if (typeof rule?.label !== "string" || rule.label.length === 0) throw new ValidationError(`${ruleWhere}: label must be a non-empty string`);
|
|
1468
|
+
if (seen.has(rule.label)) throw new ValidationError(`${ruleWhere}: duplicate label would collapse per-rule scores`);
|
|
1469
|
+
seen.add(rule.label);
|
|
1470
|
+
assertRule(rule, ruleWhere);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
function assertContract(contract, where) {
|
|
1474
|
+
if (typeof contract?.name !== "string" || contract.name.length === 0) throw new ValidationError(`${where}: contract.name must be a non-empty string`);
|
|
1475
|
+
const at = `${where} "${contract.name}"`;
|
|
1476
|
+
assertRules(contract.rules, at, Array.isArray(contract.alternatives) && contract.alternatives.length > 0);
|
|
1477
|
+
if (contract.scope !== void 0) assertPredicate(contract.scope?.root, `${at} scope.root`);
|
|
1478
|
+
if (contract.alternatives !== void 0) {
|
|
1479
|
+
if (!Array.isArray(contract.alternatives) || contract.alternatives.length === 0) throw new ValidationError(`${at}: alternatives must be a non-empty array`);
|
|
1480
|
+
const ids = /* @__PURE__ */ new Set();
|
|
1481
|
+
for (const alternative of contract.alternatives) {
|
|
1482
|
+
if (typeof alternative?.id !== "string" || alternative.id.length === 0) throw new ValidationError(`${at}: every alternative needs a non-empty id`);
|
|
1483
|
+
if (ids.has(alternative.id)) throw new ValidationError(`${at}: duplicate alternative id "${alternative.id}"`);
|
|
1484
|
+
ids.add(alternative.id);
|
|
1485
|
+
assertRules(alternative.rules, `${at} alternative "${alternative.id}"`);
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
//#endregion
|
|
1490
|
+
//#region src/trace-contracts/explain.ts
|
|
1229
1491
|
function describeMatcher(m) {
|
|
1230
1492
|
if (typeof m === "string") return m;
|
|
1231
1493
|
if (m instanceof RegExp) return `/${m.source}/${m.flags}`;
|
|
1494
|
+
if (isOneOfMatcher(m)) return `oneOf(${m.oneOf.join("|")})`;
|
|
1232
1495
|
return `/${m.$regex}/${m.flags}`;
|
|
1233
1496
|
}
|
|
1234
1497
|
function describePredicate(p) {
|
|
1235
1498
|
const parts = [];
|
|
1499
|
+
if (p.kind !== void 0) parts.push(`kind=${p.kind}`);
|
|
1236
1500
|
if (p.name !== void 0) parts.push(`name=${describeMatcher(p.name)}`);
|
|
1237
1501
|
if (p.tool !== void 0) parts.push(`tool=${describeMatcher(p.tool)}`);
|
|
1238
|
-
if (p.
|
|
1502
|
+
if (p.model !== void 0) parts.push(`model=${describeMatcher(p.model)}`);
|
|
1503
|
+
if (p.attr !== void 0) for (const [k, v] of Object.entries(p.attr)) parts.push(`attr.${k}=${isRegexValue(v) ? describeMatcher(v) : JSON.stringify(v)}`);
|
|
1504
|
+
if (p.not !== void 0) parts.push(`not(${describePredicate(p.not)})`);
|
|
1239
1505
|
if (typeof p.custom === "function") parts.push(`custom=${p.custom.name || "fn"}`);
|
|
1240
1506
|
return parts.join(",");
|
|
1241
1507
|
}
|
|
1242
|
-
function
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1508
|
+
function describeCheck(check) {
|
|
1509
|
+
switch (check.op) {
|
|
1510
|
+
case "exists": return "exists";
|
|
1511
|
+
case "equals": return `equals ${JSON.stringify(check.value)}`;
|
|
1512
|
+
case "oneOf": return `oneOf ${JSON.stringify(check.values)}`;
|
|
1513
|
+
case "type": return `type ${check.type}`;
|
|
1514
|
+
}
|
|
1247
1515
|
}
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1516
|
+
/** One line per rule, stating what it requires. */
|
|
1517
|
+
function describeRule(rule) {
|
|
1518
|
+
switch (rule.kind) {
|
|
1519
|
+
case "always": return `every span matches ${describePredicate(rule.p)}`;
|
|
1520
|
+
case "never": return `no span matches ${describePredicate(rule.p)}`;
|
|
1521
|
+
case "eventually": return `some span matches ${describePredicate(rule.p)}`;
|
|
1522
|
+
case "precedes": return `${describePredicate(rule.a)} and ${describePredicate(rule.b)} both occur, and the first comes before the second (${rule.order ?? "start-order"})`;
|
|
1523
|
+
case "neverUnless": return `every ${describePredicate(rule.p)} has an earlier ${describePredicate(rule.prior)} (${rule.order ?? "start-order"})`;
|
|
1524
|
+
case "atMost": return `at most ${rule.max} span(s) match ${describePredicate(rule.p)}`;
|
|
1525
|
+
case "tokensAtMost": return `spans matching ${describePredicate(rule.p)} use at most ${rule.max} tokens in total`;
|
|
1526
|
+
case "run": {
|
|
1527
|
+
const parts = [];
|
|
1528
|
+
if (rule.requireCompleted) parts.push("the run reached a terminal status");
|
|
1529
|
+
if (rule.allowedStatuses) parts.push(`its status is one of ${rule.allowedStatuses.join(", ")}`);
|
|
1530
|
+
if (rule.maxDurationMs !== void 0) parts.push(`it took at most ${rule.maxDurationMs} ms`);
|
|
1531
|
+
return parts.join("; ");
|
|
1532
|
+
}
|
|
1533
|
+
case "argument": return `${rule.occurrence ?? "all"} call(s) matching ${describePredicate(rule.p)} have argument ${rule.pointer || "(whole value)"} that ${describeCheck(rule.check)}`;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
/** Plain-language statement of everything a contract checks, one rule per line. */
|
|
1537
|
+
function explainTraceContract(contract) {
|
|
1538
|
+
assertContract(contract, "explainTraceContract");
|
|
1539
|
+
const lines = [`Contract "${contract.name}"${contract.description ? ` — ${contract.description}` : ""}`];
|
|
1540
|
+
if (contract.scope) lines.push(`Scope: only the span matching ${describePredicate(contract.scope.root)} and its descendants; exactly one span must match.`);
|
|
1541
|
+
lines.push("Every rule must pass:");
|
|
1542
|
+
for (const rule of contract.rules) lines.push(` - ${rule.label}: ${describeRule(rule)}`);
|
|
1543
|
+
if (contract.alternatives) {
|
|
1544
|
+
lines.push("And at least one alternative must pass completely:");
|
|
1545
|
+
for (const alternative of contract.alternatives) {
|
|
1546
|
+
lines.push(` ${alternative.id}:`);
|
|
1547
|
+
for (const rule of alternative.rules) lines.push(` - ${rule.label}: ${describeRule(rule)}`);
|
|
1548
|
+
}
|
|
1257
1549
|
}
|
|
1258
|
-
return
|
|
1550
|
+
return lines.join("\n");
|
|
1259
1551
|
}
|
|
1260
|
-
|
|
1552
|
+
//#endregion
|
|
1553
|
+
//#region src/trace-contracts/builder.ts
|
|
1554
|
+
var TraceContractBuilder = class TraceContractBuilder {
|
|
1261
1555
|
name;
|
|
1262
1556
|
rules = [];
|
|
1557
|
+
alternativeList = [];
|
|
1558
|
+
scopeRoot;
|
|
1263
1559
|
constructor(name) {
|
|
1264
1560
|
this.name = name;
|
|
1265
1561
|
}
|
|
1266
1562
|
/** Every span in the trace must satisfy `p`. */
|
|
1267
1563
|
always(p, label) {
|
|
1268
|
-
const np = normalizePredicate(p,
|
|
1564
|
+
const np = normalizePredicate(p, this.where("always"));
|
|
1269
1565
|
return this.add({
|
|
1270
1566
|
kind: "always",
|
|
1271
1567
|
label: label ?? `always(${describePredicate(np)})`,
|
|
@@ -1274,7 +1570,7 @@ var TraceContractBuilder = class {
|
|
|
1274
1570
|
}
|
|
1275
1571
|
/** No span in the trace may satisfy `p`. */
|
|
1276
1572
|
never(p, label) {
|
|
1277
|
-
const np = normalizePredicate(p,
|
|
1573
|
+
const np = normalizePredicate(p, this.where("never"));
|
|
1278
1574
|
return this.add({
|
|
1279
1575
|
kind: "never",
|
|
1280
1576
|
label: label ?? `never(${describePredicate(np)})`,
|
|
@@ -1283,41 +1579,114 @@ var TraceContractBuilder = class {
|
|
|
1283
1579
|
}
|
|
1284
1580
|
/** At least one span in the trace must satisfy `p`. */
|
|
1285
1581
|
eventually(p, label) {
|
|
1286
|
-
const np = normalizePredicate(p,
|
|
1582
|
+
const np = normalizePredicate(p, this.where("eventually"));
|
|
1287
1583
|
return this.add({
|
|
1288
1584
|
kind: "eventually",
|
|
1289
1585
|
label: label ?? `eventually(${describePredicate(np)})`,
|
|
1290
1586
|
p: np
|
|
1291
1587
|
});
|
|
1292
1588
|
}
|
|
1293
|
-
/**
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1589
|
+
/** Both `a` and `b` must occur, with `a` first under `options.order`
|
|
1590
|
+
* (default `start-order`). For "every `b` needs an earlier `a`, and a run
|
|
1591
|
+
* without `b` passes", use {@link neverUnless}. */
|
|
1592
|
+
precedes(a, b, label, options = {}) {
|
|
1593
|
+
const na = normalizePredicate(a, this.where("precedes (a)"));
|
|
1594
|
+
const nb = normalizePredicate(b, this.where("precedes (b)"));
|
|
1595
|
+
const order = assertOrder(options.order, this.where("precedes"));
|
|
1297
1596
|
return this.add({
|
|
1298
1597
|
kind: "precedes",
|
|
1299
1598
|
label: label ?? `precedes(${describePredicate(na)} -> ${describePredicate(nb)})`,
|
|
1300
1599
|
a: na,
|
|
1301
|
-
b: nb
|
|
1600
|
+
b: nb,
|
|
1601
|
+
...order === void 0 ? {} : { order }
|
|
1302
1602
|
});
|
|
1303
1603
|
}
|
|
1304
|
-
/** Every `p`-match
|
|
1305
|
-
neverUnless(p, prior, label) {
|
|
1306
|
-
const np = normalizePredicate(p,
|
|
1307
|
-
const nprior = normalizePredicate(prior,
|
|
1604
|
+
/** Every `p`-match needs an earlier `prior`-match; a run without `p` passes. */
|
|
1605
|
+
neverUnless(p, prior, label, options = {}) {
|
|
1606
|
+
const np = normalizePredicate(p, this.where("neverUnless (p)"));
|
|
1607
|
+
const nprior = normalizePredicate(prior, this.where("neverUnless (prior)"));
|
|
1608
|
+
const order = assertOrder(options.order, this.where("neverUnless"));
|
|
1308
1609
|
return this.add({
|
|
1309
1610
|
kind: "neverUnless",
|
|
1310
1611
|
label: label ?? `neverUnless(${describePredicate(np)} unless ${describePredicate(nprior)})`,
|
|
1311
1612
|
p: np,
|
|
1312
|
-
prior: nprior
|
|
1613
|
+
prior: nprior,
|
|
1614
|
+
...order === void 0 ? {} : { order }
|
|
1313
1615
|
});
|
|
1314
1616
|
}
|
|
1617
|
+
/** At most `max` spans may satisfy `p`. */
|
|
1618
|
+
atMost(p, max, label) {
|
|
1619
|
+
const np = normalizePredicate(p, this.where("atMost"));
|
|
1620
|
+
assertCount(max, this.where("atMost"));
|
|
1621
|
+
return this.add({
|
|
1622
|
+
kind: "atMost",
|
|
1623
|
+
label: label ?? `atMost(${describePredicate(np)}, ${max})`,
|
|
1624
|
+
p: np,
|
|
1625
|
+
max
|
|
1626
|
+
});
|
|
1627
|
+
}
|
|
1628
|
+
/** Spans satisfying `p` may use at most `max` input+output tokens in total. */
|
|
1629
|
+
tokensAtMost(p, max, label) {
|
|
1630
|
+
const np = normalizePredicate(p, this.where("tokensAtMost"));
|
|
1631
|
+
assertCount(max, this.where("tokensAtMost"));
|
|
1632
|
+
return this.add({
|
|
1633
|
+
kind: "tokensAtMost",
|
|
1634
|
+
label: label ?? `tokensAtMost(${describePredicate(np)}, ${max})`,
|
|
1635
|
+
p: np,
|
|
1636
|
+
max
|
|
1637
|
+
});
|
|
1638
|
+
}
|
|
1639
|
+
/** Terminal status and duration of the run. */
|
|
1640
|
+
run(checks, label = "run") {
|
|
1641
|
+
const rule = {
|
|
1642
|
+
kind: "run",
|
|
1643
|
+
label,
|
|
1644
|
+
...checks
|
|
1645
|
+
};
|
|
1646
|
+
assertRule(rule, this.where("run"));
|
|
1647
|
+
return this.add(rule);
|
|
1648
|
+
}
|
|
1649
|
+
/** A JSON Pointer check on the arguments of calls matching `p`. */
|
|
1650
|
+
argument(p, options, label) {
|
|
1651
|
+
const np = normalizePredicate(p, this.where("argument"));
|
|
1652
|
+
const rule = {
|
|
1653
|
+
kind: "argument",
|
|
1654
|
+
label: label ?? `argument(${describePredicate(np)}, ${options.pointer || "(whole value)"} ${describeCheck(options.check)})`,
|
|
1655
|
+
p: np,
|
|
1656
|
+
pointer: options.pointer,
|
|
1657
|
+
check: options.check,
|
|
1658
|
+
...options.occurrence === void 0 ? {} : { occurrence: options.occurrence }
|
|
1659
|
+
};
|
|
1660
|
+
assertRule(rule, this.where("argument"));
|
|
1661
|
+
return this.add(rule);
|
|
1662
|
+
}
|
|
1663
|
+
/** Evaluate only the subtree rooted at the one span matching `root`. */
|
|
1664
|
+
scope(root) {
|
|
1665
|
+
this.scopeRoot = normalizePredicate(root, this.where("scope"));
|
|
1666
|
+
return this;
|
|
1667
|
+
}
|
|
1668
|
+
/** A named alternate path; at least one alternative must pass completely. */
|
|
1669
|
+
alternative(id, define) {
|
|
1670
|
+
const branch = define(new TraceContractBuilder(`${this.name}/${id}`));
|
|
1671
|
+
if (branch.scopeRoot !== void 0 || branch.alternativeList.length > 0) throw new ValidationError(`${this.where("alternative")}: alternatives cannot nest scope or alternatives`);
|
|
1672
|
+
this.alternativeList.push({
|
|
1673
|
+
id,
|
|
1674
|
+
rules: [...branch.rules]
|
|
1675
|
+
});
|
|
1676
|
+
return this;
|
|
1677
|
+
}
|
|
1315
1678
|
build() {
|
|
1316
|
-
|
|
1317
|
-
return {
|
|
1679
|
+
const contract = {
|
|
1318
1680
|
name: this.name,
|
|
1319
|
-
|
|
1681
|
+
...this.scopeRoot === void 0 ? {} : { scope: { root: this.scopeRoot } },
|
|
1682
|
+
rules: [...this.rules],
|
|
1683
|
+
...this.alternativeList.length === 0 ? {} : { alternatives: [...this.alternativeList] }
|
|
1320
1684
|
};
|
|
1685
|
+
assertContract(contract, `traceContract("${this.name}").build()`);
|
|
1686
|
+
return contract;
|
|
1687
|
+
}
|
|
1688
|
+
where(op) {
|
|
1689
|
+
return `traceContract("${this.name}").${op}`;
|
|
1321
1690
|
}
|
|
1322
1691
|
add(rule) {
|
|
1323
1692
|
let label = rule.label;
|
|
@@ -1337,144 +1706,378 @@ function traceContract(name) {
|
|
|
1337
1706
|
if (typeof name !== "string" || name.length === 0) throw new ValidationError("traceContract: name must be a non-empty string");
|
|
1338
1707
|
return new TraceContractBuilder(name);
|
|
1339
1708
|
}
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
break;
|
|
1355
|
-
case "precedes":
|
|
1356
|
-
assertPredicate(rule.a, `${where} (a)`);
|
|
1357
|
-
assertPredicate(rule.b, `${where} (b)`);
|
|
1358
|
-
break;
|
|
1359
|
-
case "neverUnless":
|
|
1360
|
-
assertPredicate(rule.p, `${where} (p)`);
|
|
1361
|
-
assertPredicate(rule.prior, `${where} (prior)`);
|
|
1362
|
-
break;
|
|
1363
|
-
default: throw new ValidationError(`${where}: unknown rule kind "${String(rule.kind)}"`);
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1709
|
+
//#endregion
|
|
1710
|
+
//#region src/trace-contracts/rules.ts
|
|
1711
|
+
function spanRef(span, index) {
|
|
1712
|
+
return span.spanId ?? `#${index}`;
|
|
1713
|
+
}
|
|
1714
|
+
function indexed(spans, p) {
|
|
1715
|
+
const out = [];
|
|
1716
|
+
spans.forEach((span, i) => {
|
|
1717
|
+
if (predicateMatches(span, p)) out.push({
|
|
1718
|
+
span,
|
|
1719
|
+
ref: spanRef(span, i)
|
|
1720
|
+
});
|
|
1721
|
+
});
|
|
1722
|
+
return out;
|
|
1366
1723
|
}
|
|
1367
1724
|
/**
|
|
1368
|
-
*
|
|
1369
|
-
*
|
|
1370
|
-
*
|
|
1371
|
-
* order over a partially-stamped sequence, and guessing would corrupt
|
|
1372
|
-
* `precedes` / `neverUnless` verdicts.
|
|
1725
|
+
* Is guarded span `b` preceded by the guard spans under `order`? A span never
|
|
1726
|
+
* witnesses itself. Missing timestamps fail: the checker cannot prove an
|
|
1727
|
+
* order it cannot see.
|
|
1373
1728
|
*/
|
|
1374
|
-
function
|
|
1375
|
-
const
|
|
1376
|
-
if (
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1729
|
+
function witnessed(b, guards, order) {
|
|
1730
|
+
const bStart = finite(b.span.startedAt);
|
|
1731
|
+
if (bStart === void 0) return {
|
|
1732
|
+
ok: false,
|
|
1733
|
+
detail: `span ${b.ref} has no startedAt, so its order cannot be proven`
|
|
1734
|
+
};
|
|
1735
|
+
const candidates = guards.filter((a) => a.span !== b.span);
|
|
1736
|
+
if (candidates.length === 0) return {
|
|
1737
|
+
ok: false,
|
|
1738
|
+
detail: `span ${b.ref} has no earlier match`
|
|
1739
|
+
};
|
|
1740
|
+
const boundary = order === "start-order" ? "startedAt" : "endedAt";
|
|
1741
|
+
const untimed = candidates.find((a) => finite(a.span[boundary]) === void 0);
|
|
1742
|
+
if (order === "all-occurrences") {
|
|
1743
|
+
if (untimed) return {
|
|
1744
|
+
ok: false,
|
|
1745
|
+
detail: `span ${untimed.ref} has no endedAt, so its order cannot be proven`
|
|
1746
|
+
};
|
|
1747
|
+
const late = candidates.find((a) => a.span.endedAt > bStart);
|
|
1748
|
+
return late ? {
|
|
1749
|
+
ok: false,
|
|
1750
|
+
detail: `span ${late.ref} ends after span ${b.ref} starts`
|
|
1751
|
+
} : { ok: true };
|
|
1752
|
+
}
|
|
1753
|
+
if (candidates.some((a) => {
|
|
1754
|
+
const t = finite(a.span[boundary]);
|
|
1755
|
+
return t !== void 0 && (order === "start-order" ? t < bStart : t <= bStart);
|
|
1756
|
+
})) return { ok: true };
|
|
1757
|
+
if (untimed) return {
|
|
1758
|
+
ok: false,
|
|
1759
|
+
detail: `span ${untimed.ref} has no ${boundary}, so its order cannot be proven`
|
|
1760
|
+
};
|
|
1761
|
+
return {
|
|
1762
|
+
ok: false,
|
|
1763
|
+
detail: order === "start-order" ? `span ${b.ref} has no match that started before it` : `span ${b.ref} has no match that finished before it started`
|
|
1764
|
+
};
|
|
1382
1765
|
}
|
|
1383
|
-
function
|
|
1384
|
-
|
|
1766
|
+
function orderViolations(label, guards, guarded, order, describe) {
|
|
1767
|
+
const out = [];
|
|
1768
|
+
for (const b of guarded) {
|
|
1769
|
+
const w = witnessed(b, guards, order);
|
|
1770
|
+
if (!w.ok) out.push({
|
|
1771
|
+
rule: label,
|
|
1772
|
+
spanId: b.span.spanId,
|
|
1773
|
+
detail: `${describe}: ${w.detail}`
|
|
1774
|
+
});
|
|
1775
|
+
}
|
|
1776
|
+
return out;
|
|
1777
|
+
}
|
|
1778
|
+
/** RFC 6901 resolution. `found: false` when any reference token is absent. */
|
|
1779
|
+
function resolveJsonPointer(value, pointer) {
|
|
1780
|
+
if (pointer === "") return {
|
|
1781
|
+
found: true,
|
|
1782
|
+
value
|
|
1783
|
+
};
|
|
1784
|
+
let current = value;
|
|
1785
|
+
for (const raw of pointer.slice(1).split("/")) {
|
|
1786
|
+
const token = raw.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
1787
|
+
if (Array.isArray(current)) {
|
|
1788
|
+
if (!/^(?:0|[1-9]\d*)$/.test(token) || Number(token) >= current.length) return { found: false };
|
|
1789
|
+
current = current[Number(token)];
|
|
1790
|
+
} else if (current !== null && typeof current === "object" && Object.hasOwn(current, token)) current = current[token];
|
|
1791
|
+
else return { found: false };
|
|
1792
|
+
}
|
|
1793
|
+
return {
|
|
1794
|
+
found: true,
|
|
1795
|
+
value: current
|
|
1796
|
+
};
|
|
1385
1797
|
}
|
|
1386
|
-
function
|
|
1798
|
+
function jsonType(value) {
|
|
1799
|
+
if (value === null) return "null";
|
|
1800
|
+
if (Array.isArray(value)) return "array";
|
|
1801
|
+
const t = typeof value;
|
|
1802
|
+
return t === "string" || t === "number" || t === "boolean" || t === "object" ? t : "undefined";
|
|
1803
|
+
}
|
|
1804
|
+
function jsonEqual(a, b) {
|
|
1805
|
+
if (a === b) return true;
|
|
1806
|
+
const ta = jsonType(a);
|
|
1807
|
+
if (ta !== jsonType(b)) return false;
|
|
1808
|
+
if (ta === "array") {
|
|
1809
|
+
const x = a;
|
|
1810
|
+
const y = b;
|
|
1811
|
+
return x.length === y.length && x.every((v, i) => jsonEqual(v, y[i]));
|
|
1812
|
+
}
|
|
1813
|
+
if (ta === "object") {
|
|
1814
|
+
const x = a;
|
|
1815
|
+
const y = b;
|
|
1816
|
+
const keys = Object.keys(x);
|
|
1817
|
+
return keys.length === Object.keys(y).length && keys.every((k) => Object.hasOwn(y, k) && jsonEqual(x[k], y[k]));
|
|
1818
|
+
}
|
|
1819
|
+
return false;
|
|
1820
|
+
}
|
|
1821
|
+
/** Why `value` fails `check`, or `undefined` when it passes. Never echoes the
|
|
1822
|
+
* actual argument value: arguments can carry customer data. */
|
|
1823
|
+
function argumentFailure(evidence, pointer, check) {
|
|
1824
|
+
if (!evidence.known) return evidence.reason;
|
|
1825
|
+
const resolved = resolveJsonPointer(evidence.value, pointer);
|
|
1826
|
+
if (!resolved.found) return `argument ${pointer || "(whole value)"} is absent`;
|
|
1827
|
+
switch (check.op) {
|
|
1828
|
+
case "exists": return;
|
|
1829
|
+
case "equals": return jsonEqual(resolved.value, check.value) ? void 0 : `argument ${pointer || "(whole value)"} (a ${jsonType(resolved.value)}) does not equal the expected value`;
|
|
1830
|
+
case "oneOf": return check.values.some((v) => jsonEqual(resolved.value, v)) ? void 0 : `argument ${pointer || "(whole value)"} (a ${jsonType(resolved.value)}) is not one of the expected values`;
|
|
1831
|
+
case "type": return jsonType(resolved.value) === check.type ? void 0 : `argument ${pointer || "(whole value)"} is a ${jsonType(resolved.value)}, not a ${check.type}`;
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
function checkArgument(rule, spans) {
|
|
1835
|
+
const matches = indexed(spans, rule.p);
|
|
1836
|
+
if (matches.length === 0) return [{
|
|
1837
|
+
rule: rule.label,
|
|
1838
|
+
detail: `no span matches ${describePredicate(rule.p)}, so there is no argument evidence to check`
|
|
1839
|
+
}];
|
|
1840
|
+
const occurrence = rule.occurrence ?? "all";
|
|
1841
|
+
let selected = matches;
|
|
1842
|
+
if ((occurrence === "first" || occurrence === "last") && matches.length > 1) {
|
|
1843
|
+
const untimed = matches.find((m) => finite(m.span.startedAt) === void 0);
|
|
1844
|
+
if (untimed) return [{
|
|
1845
|
+
rule: rule.label,
|
|
1846
|
+
spanId: untimed.span.spanId,
|
|
1847
|
+
detail: `span ${untimed.ref} has no startedAt, so the ${occurrence} call cannot be chosen`
|
|
1848
|
+
}];
|
|
1849
|
+
const sorted = [...matches].sort((x, y) => x.span.startedAt - y.span.startedAt);
|
|
1850
|
+
selected = [occurrence === "first" ? sorted[0] : sorted[sorted.length - 1]];
|
|
1851
|
+
}
|
|
1852
|
+
const failures = selected.map((m) => ({
|
|
1853
|
+
m,
|
|
1854
|
+
why: argumentFailure(spanArguments(m.span), rule.pointer, rule.check)
|
|
1855
|
+
}));
|
|
1856
|
+
const failed = failures.filter((f) => f.why !== void 0);
|
|
1857
|
+
if (occurrence === "any" ? failed.length < failures.length : failed.length === 0) return [];
|
|
1858
|
+
return failed.map((f) => ({
|
|
1859
|
+
rule: rule.label,
|
|
1860
|
+
spanId: f.m.span.spanId,
|
|
1861
|
+
detail: `span ${f.m.ref}: ${f.why}`
|
|
1862
|
+
}));
|
|
1863
|
+
}
|
|
1864
|
+
function checkRun(rule, context) {
|
|
1865
|
+
if ("unknown" in context) return [{
|
|
1866
|
+
rule: rule.label,
|
|
1867
|
+
detail: `run record unavailable: ${context.unknown}`
|
|
1868
|
+
}];
|
|
1869
|
+
const run = context.run;
|
|
1387
1870
|
const out = [];
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1871
|
+
if (rule.requireCompleted && (run.status === void 0 || run.status === "running" || finite(run.endedAt) === void 0)) out.push({
|
|
1872
|
+
rule: rule.label,
|
|
1873
|
+
detail: `run did not reach a terminal status (status ${run.status ?? "unknown"})`
|
|
1874
|
+
});
|
|
1875
|
+
if (rule.allowedStatuses && !rule.allowedStatuses.includes(run.status ?? "")) out.push({
|
|
1876
|
+
rule: rule.label,
|
|
1877
|
+
detail: `run status ${run.status ?? "unknown"} is not one of ${rule.allowedStatuses.join(", ")}`
|
|
1878
|
+
});
|
|
1879
|
+
if (rule.maxDurationMs !== void 0) {
|
|
1880
|
+
const start = finite(run.startedAt);
|
|
1881
|
+
const end = finite(run.endedAt);
|
|
1882
|
+
if (start === void 0 || end === void 0) out.push({
|
|
1883
|
+
rule: rule.label,
|
|
1884
|
+
detail: "run duration is unknown: start or end time is missing"
|
|
1885
|
+
});
|
|
1886
|
+
else if (end - start > rule.maxDurationMs) out.push({
|
|
1887
|
+
rule: rule.label,
|
|
1888
|
+
detail: `run took ${end - start} ms, over ${rule.maxDurationMs} ms`
|
|
1395
1889
|
});
|
|
1396
|
-
if (matchSpan(span, args.guard)) guardSeen = true;
|
|
1397
1890
|
}
|
|
1398
1891
|
return out;
|
|
1399
1892
|
}
|
|
1400
|
-
function checkRule(rule,
|
|
1893
|
+
function checkRule(rule, ctx) {
|
|
1894
|
+
const { spans } = ctx;
|
|
1401
1895
|
switch (rule.kind) {
|
|
1402
|
-
case "always": {
|
|
1896
|
+
case "always": return spans.flatMap((span, i) => predicateMatches(span, rule.p) ? [] : [{
|
|
1897
|
+
rule: rule.label,
|
|
1898
|
+
spanId: span.spanId,
|
|
1899
|
+
detail: `span ${spanRef(span, i)} ("${span.name ?? ""}") fails always(${describePredicate(rule.p)})`
|
|
1900
|
+
}]);
|
|
1901
|
+
case "never": return indexed(spans, rule.p).map(({ span, ref }) => ({
|
|
1902
|
+
rule: rule.label,
|
|
1903
|
+
spanId: span.spanId,
|
|
1904
|
+
detail: `span ${ref} ("${span.name ?? ""}") matches never(${describePredicate(rule.p)})`
|
|
1905
|
+
}));
|
|
1906
|
+
case "eventually": return spans.some((span) => predicateMatches(span, rule.p)) ? [] : [{
|
|
1907
|
+
rule: rule.label,
|
|
1908
|
+
detail: `no span matches eventually(${describePredicate(rule.p)}) over ${spans.length} span(s)`
|
|
1909
|
+
}];
|
|
1910
|
+
case "precedes": {
|
|
1911
|
+
const as = indexed(spans, rule.a);
|
|
1912
|
+
const bs = indexed(spans, rule.b);
|
|
1403
1913
|
const out = [];
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
}
|
|
1411
|
-
}
|
|
1412
|
-
return out;
|
|
1914
|
+
if (as.length === 0) out.push({
|
|
1915
|
+
rule: rule.label,
|
|
1916
|
+
detail: `required predecessor ${describePredicate(rule.a)} never occurred`
|
|
1917
|
+
});
|
|
1918
|
+
if (bs.length === 0) out.push({
|
|
1919
|
+
rule: rule.label,
|
|
1920
|
+
detail: `required successor ${describePredicate(rule.b)} never occurred`
|
|
1921
|
+
});
|
|
1922
|
+
if (out.length > 0) return out;
|
|
1923
|
+
return orderViolations(rule.label, as, bs, rule.order ?? "start-order", `${describePredicate(rule.b)} is not preceded by ${describePredicate(rule.a)}`);
|
|
1413
1924
|
}
|
|
1414
|
-
case "
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
});
|
|
1423
|
-
}
|
|
1424
|
-
return out;
|
|
1925
|
+
case "neverUnless": return orderViolations(rule.label, indexed(spans, rule.prior), indexed(spans, rule.p), rule.order ?? "start-order", `${describePredicate(rule.p)} without an earlier ${describePredicate(rule.prior)}`);
|
|
1926
|
+
case "atMost": {
|
|
1927
|
+
const matches = indexed(spans, rule.p);
|
|
1928
|
+
return matches.length <= rule.max ? [] : [{
|
|
1929
|
+
rule: rule.label,
|
|
1930
|
+
spanId: matches[rule.max]?.span.spanId,
|
|
1931
|
+
detail: `${matches.length} span(s) match ${describePredicate(rule.p)}, over the limit of ${rule.max}`
|
|
1932
|
+
}];
|
|
1425
1933
|
}
|
|
1426
|
-
case "
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1934
|
+
case "tokensAtMost": {
|
|
1935
|
+
const matches = indexed(spans, rule.p);
|
|
1936
|
+
const unknown = matches.find((m) => spanTotalTokens(m.span) === void 0);
|
|
1937
|
+
if (unknown) return [{
|
|
1938
|
+
rule: rule.label,
|
|
1939
|
+
spanId: unknown.span.spanId,
|
|
1940
|
+
detail: `span ${unknown.ref} has no recorded input and output token counts, so the total is unknown`
|
|
1941
|
+
}];
|
|
1942
|
+
const total = matches.reduce((sum, m) => sum + spanTotalTokens(m.span), 0);
|
|
1943
|
+
return total <= rule.max ? [] : [{
|
|
1944
|
+
rule: rule.label,
|
|
1945
|
+
detail: `${total} tokens across ${matches.length} span(s) matching ${describePredicate(rule.p)}, over the limit of ${rule.max}`
|
|
1946
|
+
}];
|
|
1947
|
+
}
|
|
1948
|
+
case "run": return checkRun(rule, ctx.run);
|
|
1949
|
+
case "argument": return checkArgument(rule, spans);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
function executeRule(rule, ctx) {
|
|
1953
|
+
try {
|
|
1954
|
+
const violations = checkRule(rule, ctx);
|
|
1955
|
+
return {
|
|
1956
|
+
status: violations.length === 0 ? "pass" : "fail",
|
|
1957
|
+
violations
|
|
1958
|
+
};
|
|
1959
|
+
} catch (error) {
|
|
1960
|
+
return {
|
|
1961
|
+
status: "error",
|
|
1962
|
+
violations: [],
|
|
1963
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1964
|
+
};
|
|
1444
1965
|
}
|
|
1445
1966
|
}
|
|
1967
|
+
//#endregion
|
|
1968
|
+
//#region src/trace-contracts/evaluate.ts
|
|
1446
1969
|
/**
|
|
1447
|
-
*
|
|
1448
|
-
*
|
|
1449
|
-
*
|
|
1970
|
+
* A run derived from one span: the `run.status` attribute a run-anchor span
|
|
1971
|
+
* carries (store-to-otlp writes it), else failed on an error status, else
|
|
1972
|
+
* completed once the span ended.
|
|
1450
1973
|
*/
|
|
1451
|
-
function
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1974
|
+
function runOfSpan(span) {
|
|
1975
|
+
const declared = span.attributes?.["run.status"];
|
|
1976
|
+
return {
|
|
1977
|
+
status: typeof declared === "string" && RUN_STATUSES.includes(declared) ? declared : isSpanError(span) ? "failed" : finite(span.endedAt) !== void 0 ? "completed" : "running",
|
|
1978
|
+
...finite(span.startedAt) === void 0 ? {} : { startedAt: span.startedAt },
|
|
1979
|
+
...finite(span.endedAt) === void 0 ? {} : { endedAt: span.endedAt }
|
|
1980
|
+
};
|
|
1981
|
+
}
|
|
1982
|
+
function rootsOf(spans) {
|
|
1983
|
+
const ids = new Set(spans.map((s) => s.spanId).filter((id) => typeof id === "string"));
|
|
1984
|
+
return spans.filter((s) => !s.parentSpanId || !ids.has(s.parentSpanId));
|
|
1985
|
+
}
|
|
1986
|
+
function subtree(spans, root) {
|
|
1987
|
+
if (typeof root.spanId !== "string") return [root];
|
|
1988
|
+
const children = /* @__PURE__ */ new Map();
|
|
1989
|
+
for (const span of spans) if (typeof span.parentSpanId === "string") {
|
|
1990
|
+
const list = children.get(span.parentSpanId) ?? [];
|
|
1991
|
+
list.push(span);
|
|
1992
|
+
children.set(span.parentSpanId, list);
|
|
1993
|
+
}
|
|
1994
|
+
const members = /* @__PURE__ */ new Set([root]);
|
|
1995
|
+
const queue = [root];
|
|
1996
|
+
while (queue.length > 0) {
|
|
1997
|
+
const current = queue.shift();
|
|
1998
|
+
for (const child of typeof current.spanId === "string" && children.get(current.spanId) || []) if (!members.has(child)) {
|
|
1999
|
+
members.add(child);
|
|
2000
|
+
queue.push(child);
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
return spans.filter((s) => members.has(s));
|
|
2004
|
+
}
|
|
2005
|
+
function statusOf(outcomes) {
|
|
2006
|
+
if (outcomes.some((o) => o.status === "error")) return "error";
|
|
2007
|
+
return outcomes.every((o) => o.status === "pass") ? "pass" : "fail";
|
|
2008
|
+
}
|
|
2009
|
+
/**
|
|
2010
|
+
* Evaluate one contract over a span sequence. Pure and synchronous — works on
|
|
2011
|
+
* `Span[]` from a TraceStore, `ExportableSpan[]` from the otel-bridge
|
|
2012
|
+
* flattening, or any array satisfying `ContractSpan`. Throws
|
|
2013
|
+
* `ValidationError` for a malformed contract; a rule that cannot be evaluated
|
|
2014
|
+
* is reported as `error` in the verdict instead.
|
|
2015
|
+
*/
|
|
2016
|
+
function evaluateTraceContract(contract, spans, options = {}) {
|
|
2017
|
+
assertContract(contract, "evaluateTraceContract");
|
|
2018
|
+
if (!Array.isArray(spans)) throw new ValidationError(`evaluateTraceContract: spans must be an array, got ${typeof spans}`);
|
|
2019
|
+
let subject = spans;
|
|
2020
|
+
let run;
|
|
2021
|
+
let scopeError;
|
|
2022
|
+
if (contract.scope) {
|
|
2023
|
+
const roots = spans.filter((s) => predicateMatches(s, contract.scope.root));
|
|
2024
|
+
if (roots.length === 1) {
|
|
2025
|
+
subject = subtree(spans, roots[0]);
|
|
2026
|
+
run = { run: runOfSpan(roots[0]) };
|
|
2027
|
+
} else {
|
|
2028
|
+
scopeError = `scope root ${describePredicate(contract.scope.root)} matched ${roots.length} spans; exactly one is required`;
|
|
2029
|
+
run = { unknown: scopeError };
|
|
2030
|
+
}
|
|
2031
|
+
} else if (options.run) run = { run: options.run };
|
|
2032
|
+
else {
|
|
2033
|
+
const roots = rootsOf(spans);
|
|
2034
|
+
run = roots.length === 1 ? { run: runOfSpan(roots[0]) } : { unknown: `the trace has ${roots.length} root spans and no run record was passed` };
|
|
1460
2035
|
}
|
|
1461
|
-
const
|
|
2036
|
+
const ctx = {
|
|
2037
|
+
spans: subject,
|
|
2038
|
+
run
|
|
2039
|
+
};
|
|
2040
|
+
const execute = (rule) => scopeError === void 0 ? executeRule(rule, ctx) : {
|
|
2041
|
+
status: "error",
|
|
2042
|
+
violations: [],
|
|
2043
|
+
error: scopeError
|
|
2044
|
+
};
|
|
2045
|
+
const base = contract.rules.map(execute);
|
|
2046
|
+
const branches = (contract.alternatives ?? []).map((alternative) => ({
|
|
2047
|
+
alternative,
|
|
2048
|
+
outcomes: alternative.rules.map(execute)
|
|
2049
|
+
}));
|
|
2050
|
+
const passedBranch = branches.find((b) => statusOf(b.outcomes) === "pass");
|
|
2051
|
+
const baseStatus = statusOf(base);
|
|
2052
|
+
let status = baseStatus;
|
|
2053
|
+
if (baseStatus === "pass" && branches.length > 0 && !passedBranch) status = branches.some((b) => statusOf(b.outcomes) === "error") ? "error" : "fail";
|
|
2054
|
+
const ruleExecutions = [...contract.rules.map((rule, i) => execution(rule.label, void 0, base[i])), ...branches.flatMap((b) => b.alternative.rules.map((rule, i) => execution(rule.label, b.alternative.id, b.outcomes[i])))];
|
|
2055
|
+
const violations = base.flatMap((o) => o.violations);
|
|
2056
|
+
if (branches.length > 0 && !passedBranch) for (const b of branches) for (const o of b.outcomes) violations.push(...o.violations.map((v) => ({
|
|
2057
|
+
...v,
|
|
2058
|
+
rule: `${b.alternative.id}/${v.rule}`
|
|
2059
|
+
})));
|
|
2060
|
+
const errors = ruleExecutions.filter((e) => e.status === "error").map((e) => `${e.alternative ? `${e.alternative}/` : ""}${e.rule}: ${e.error}`);
|
|
2061
|
+
const scores = {};
|
|
2062
|
+
contract.rules.forEach((rule, i) => {
|
|
2063
|
+
const s = base[i].status;
|
|
2064
|
+
if (s !== "error") scores[rule.label] = s === "pass" ? 1 : 0;
|
|
2065
|
+
});
|
|
2066
|
+
if (branches.length > 0 && (passedBranch || status !== "error")) scores.anyOf = passedBranch ? 1 : 0;
|
|
2067
|
+
const total = contract.rules.length + (branches.length > 0 ? 1 : 0);
|
|
1462
2068
|
const passCount = Object.values(scores).filter((s) => s === 1).length;
|
|
1463
2069
|
const assumptions = [];
|
|
1464
|
-
|
|
1465
|
-
for (const rule of contract.rules) if ([
|
|
1466
|
-
rule.p,
|
|
1467
|
-
rule.a,
|
|
1468
|
-
rule.b,
|
|
1469
|
-
rule.prior
|
|
1470
|
-
].some((p) => p !== void 0 && typeof p.custom === "function")) assumptions.push(`rule '${rule.label}' rests on a custom predicate function`);
|
|
2070
|
+
for (const rule of [...contract.rules, ...branches.flatMap((b) => b.alternative.rules)]) if (rulePredicates(rule).some(usesCustom)) assumptions.push(`rule '${rule.label}' rests on a custom predicate function`);
|
|
1471
2071
|
return {
|
|
1472
2072
|
contract: contract.name,
|
|
1473
|
-
|
|
1474
|
-
|
|
2073
|
+
status,
|
|
2074
|
+
valid: status === "pass",
|
|
2075
|
+
score: passCount / total,
|
|
1475
2076
|
scores,
|
|
1476
2077
|
violations,
|
|
1477
|
-
|
|
2078
|
+
ruleExecutions,
|
|
2079
|
+
errors,
|
|
2080
|
+
notes: `${passCount}/${total} rules passed` + (errors.length > 0 ? `; ${errors.length} could not be evaluated` : ""),
|
|
1478
2081
|
certification: {
|
|
1479
2082
|
strategy: "invariant",
|
|
1480
2083
|
checker: {
|
|
@@ -1484,25 +2087,573 @@ function evaluateTraceContract(contract, spans) {
|
|
|
1484
2087
|
assumptions,
|
|
1485
2088
|
evidenceDigest: certificationEvidenceDigest({
|
|
1486
2089
|
contract: contract.name,
|
|
2090
|
+
status,
|
|
1487
2091
|
scores,
|
|
1488
|
-
violations
|
|
2092
|
+
violations,
|
|
2093
|
+
ruleExecutions
|
|
1489
2094
|
})
|
|
1490
2095
|
}
|
|
1491
2096
|
};
|
|
1492
2097
|
}
|
|
2098
|
+
function execution(rule, alternative, outcome) {
|
|
2099
|
+
return {
|
|
2100
|
+
rule,
|
|
2101
|
+
...alternative === void 0 ? {} : { alternative },
|
|
2102
|
+
status: outcome.status,
|
|
2103
|
+
violations: outcome.violations.length,
|
|
2104
|
+
...outcome.error === void 0 ? {} : { error: outcome.error }
|
|
2105
|
+
};
|
|
2106
|
+
}
|
|
2107
|
+
function rulePredicates(rule) {
|
|
2108
|
+
switch (rule.kind) {
|
|
2109
|
+
case "precedes": return [rule.a, rule.b];
|
|
2110
|
+
case "neverUnless": return [rule.p, rule.prior];
|
|
2111
|
+
case "run": return [];
|
|
2112
|
+
default: return [rule.p];
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
function usesCustom(p) {
|
|
2116
|
+
return typeof p.custom === "function" || p.not !== void 0 && usesCustom(p.not);
|
|
2117
|
+
}
|
|
1493
2118
|
/**
|
|
1494
2119
|
* Evaluate many contracts over one span sequence. Throws on an empty
|
|
1495
2120
|
* contract list — `allValid: true` over zero contracts is a silent pass.
|
|
1496
2121
|
*/
|
|
1497
|
-
function checkTraceContracts(spans, contracts) {
|
|
2122
|
+
function checkTraceContracts(spans, contracts, options = {}) {
|
|
1498
2123
|
if (contracts.length === 0) throw new ValidationError("checkTraceContracts: empty contract list would vacuously pass — supply at least one contract");
|
|
1499
|
-
const verdicts = contracts.map((c) => evaluateTraceContract(c, spans));
|
|
2124
|
+
const verdicts = contracts.map((c) => evaluateTraceContract(c, spans, options));
|
|
2125
|
+
const status = verdicts.some((v) => v.status === "error") ? "error" : verdicts.every((v) => v.status === "pass") ? "pass" : "fail";
|
|
1500
2126
|
return {
|
|
1501
2127
|
verdicts,
|
|
1502
|
-
|
|
2128
|
+
status,
|
|
2129
|
+
allValid: status === "pass"
|
|
1503
2130
|
};
|
|
1504
2131
|
}
|
|
1505
2132
|
//#endregion
|
|
2133
|
+
//#region src/trace-contracts/spec.ts
|
|
2134
|
+
/**
|
|
2135
|
+
* The declarative contract form: a JSON document that names tools, model
|
|
2136
|
+
* calls, and run limits, compiled onto the temporal operators.
|
|
2137
|
+
*
|
|
2138
|
+
* ```json
|
|
2139
|
+
* {
|
|
2140
|
+
* "name": "refund-agent",
|
|
2141
|
+
* "run": { "requireCompleted": true, "maxDurationMs": 120000 },
|
|
2142
|
+
* "tools": {
|
|
2143
|
+
* "required": ["lookup_order", "issue_refund"],
|
|
2144
|
+
* "forbidden": ["delete_account"],
|
|
2145
|
+
* "allowed": ["lookup_order", "issue_refund", "send_email"],
|
|
2146
|
+
* "maxCalls": 20,
|
|
2147
|
+
* "requiredOrder": ["lookup_order", "issue_refund"],
|
|
2148
|
+
* "arguments": [{ "tool": "issue_refund", "pointer": "/amount", "type": "number" }]
|
|
2149
|
+
* },
|
|
2150
|
+
* "llm": { "maxCalls": 10, "maxTotalTokens": 50000, "allowedModels": ["claude-sonnet-4-5"] },
|
|
2151
|
+
* "alternatives": { "anyOf": [{ "id": "cache-hit", "tools": { "required": ["read_cache"] } }] }
|
|
2152
|
+
* }
|
|
2153
|
+
* ```
|
|
2154
|
+
*
|
|
2155
|
+
* Parsing is strict: an unknown key anywhere is an error that names the
|
|
2156
|
+
* closest known key, so a typo never silently drops a check. Tool rules match
|
|
2157
|
+
* TOOL spans only and model rules match LLM spans only, so an LLM span that
|
|
2158
|
+
* shares a tool's name never satisfies a tool rule. `rules` accepts the
|
|
2159
|
+
* low-level operators for checks the declarative keys cannot state.
|
|
2160
|
+
*/
|
|
2161
|
+
const PATH_KEYS = [
|
|
2162
|
+
"run",
|
|
2163
|
+
"tools",
|
|
2164
|
+
"llm",
|
|
2165
|
+
"rules"
|
|
2166
|
+
];
|
|
2167
|
+
const SPEC_KEYS = [
|
|
2168
|
+
"name",
|
|
2169
|
+
"description",
|
|
2170
|
+
"scope",
|
|
2171
|
+
"alternatives",
|
|
2172
|
+
...PATH_KEYS
|
|
2173
|
+
];
|
|
2174
|
+
const ALTERNATIVE_KEYS = ["id", ...PATH_KEYS];
|
|
2175
|
+
const RUN_KEYS = [
|
|
2176
|
+
"requireCompleted",
|
|
2177
|
+
"allowedStatuses",
|
|
2178
|
+
"maxDurationMs"
|
|
2179
|
+
];
|
|
2180
|
+
const TOOLS_KEYS = [
|
|
2181
|
+
"required",
|
|
2182
|
+
"forbidden",
|
|
2183
|
+
"allowed",
|
|
2184
|
+
"maxCalls",
|
|
2185
|
+
"maxCallsPerTool",
|
|
2186
|
+
"requiredOrder",
|
|
2187
|
+
"orderMode",
|
|
2188
|
+
"arguments"
|
|
2189
|
+
];
|
|
2190
|
+
const LLM_KEYS = [
|
|
2191
|
+
"maxCalls",
|
|
2192
|
+
"maxTotalTokens",
|
|
2193
|
+
"allowedModels"
|
|
2194
|
+
];
|
|
2195
|
+
const ARGUMENT_KEYS = [
|
|
2196
|
+
"tool",
|
|
2197
|
+
"pointer",
|
|
2198
|
+
"occurrence",
|
|
2199
|
+
"exists",
|
|
2200
|
+
"equals",
|
|
2201
|
+
"oneOf",
|
|
2202
|
+
"type"
|
|
2203
|
+
];
|
|
2204
|
+
const ARGUMENT_CHECK_KEYS = [
|
|
2205
|
+
"exists",
|
|
2206
|
+
"equals",
|
|
2207
|
+
"oneOf",
|
|
2208
|
+
"type"
|
|
2209
|
+
];
|
|
2210
|
+
const PREDICATE_KEYS = [
|
|
2211
|
+
"name",
|
|
2212
|
+
"tool",
|
|
2213
|
+
"model",
|
|
2214
|
+
"kind",
|
|
2215
|
+
"attr",
|
|
2216
|
+
"not"
|
|
2217
|
+
];
|
|
2218
|
+
const MATCHER_KEYS = [
|
|
2219
|
+
"$regex",
|
|
2220
|
+
"flags",
|
|
2221
|
+
"oneOf"
|
|
2222
|
+
];
|
|
2223
|
+
const RULE_KEYS = {
|
|
2224
|
+
always: [
|
|
2225
|
+
"kind",
|
|
2226
|
+
"label",
|
|
2227
|
+
"p"
|
|
2228
|
+
],
|
|
2229
|
+
never: [
|
|
2230
|
+
"kind",
|
|
2231
|
+
"label",
|
|
2232
|
+
"p"
|
|
2233
|
+
],
|
|
2234
|
+
eventually: [
|
|
2235
|
+
"kind",
|
|
2236
|
+
"label",
|
|
2237
|
+
"p"
|
|
2238
|
+
],
|
|
2239
|
+
precedes: [
|
|
2240
|
+
"kind",
|
|
2241
|
+
"label",
|
|
2242
|
+
"a",
|
|
2243
|
+
"b",
|
|
2244
|
+
"order"
|
|
2245
|
+
],
|
|
2246
|
+
neverUnless: [
|
|
2247
|
+
"kind",
|
|
2248
|
+
"label",
|
|
2249
|
+
"p",
|
|
2250
|
+
"prior",
|
|
2251
|
+
"order"
|
|
2252
|
+
],
|
|
2253
|
+
atMost: [
|
|
2254
|
+
"kind",
|
|
2255
|
+
"label",
|
|
2256
|
+
"p",
|
|
2257
|
+
"max"
|
|
2258
|
+
],
|
|
2259
|
+
tokensAtMost: [
|
|
2260
|
+
"kind",
|
|
2261
|
+
"label",
|
|
2262
|
+
"p",
|
|
2263
|
+
"max"
|
|
2264
|
+
],
|
|
2265
|
+
run: [
|
|
2266
|
+
"kind",
|
|
2267
|
+
"label",
|
|
2268
|
+
...RUN_KEYS
|
|
2269
|
+
],
|
|
2270
|
+
argument: [
|
|
2271
|
+
"kind",
|
|
2272
|
+
"label",
|
|
2273
|
+
"p",
|
|
2274
|
+
"pointer",
|
|
2275
|
+
"check",
|
|
2276
|
+
"occurrence"
|
|
2277
|
+
]
|
|
2278
|
+
};
|
|
2279
|
+
/**
|
|
2280
|
+
* Parse and compile a declarative contract. Throws `ValidationError` naming
|
|
2281
|
+
* the path of the first problem: an unknown key, a wrong type, an unknown
|
|
2282
|
+
* status, or a contract that checks nothing.
|
|
2283
|
+
*/
|
|
2284
|
+
function compileTraceContractSpec(input) {
|
|
2285
|
+
const spec = parseSpec(input);
|
|
2286
|
+
const scope = spec.scope ? { root: normalizePredicate(spec.scope.root, "scope.root") } : void 0;
|
|
2287
|
+
const alternatives = spec.alternatives?.anyOf.map((alt) => ({
|
|
2288
|
+
id: alt.id,
|
|
2289
|
+
rules: compilePath(alt, "")
|
|
2290
|
+
}));
|
|
2291
|
+
const contract = {
|
|
2292
|
+
name: spec.name,
|
|
2293
|
+
...spec.description === void 0 ? {} : { description: spec.description },
|
|
2294
|
+
...scope === void 0 ? {} : { scope },
|
|
2295
|
+
rules: compilePath(spec, ""),
|
|
2296
|
+
...alternatives === void 0 ? {} : { alternatives }
|
|
2297
|
+
};
|
|
2298
|
+
assertContract(contract, `contract spec "${spec.name}"`);
|
|
2299
|
+
return contract;
|
|
2300
|
+
}
|
|
2301
|
+
const toolCall = (tool) => ({
|
|
2302
|
+
kind: "TOOL",
|
|
2303
|
+
tool
|
|
2304
|
+
});
|
|
2305
|
+
const ANY_TOOL = { kind: "TOOL" };
|
|
2306
|
+
const ANY_LLM = { kind: "LLM" };
|
|
2307
|
+
function compilePath(path, prefix) {
|
|
2308
|
+
const at = (key) => prefix ? `${prefix}.${key}` : key;
|
|
2309
|
+
const rules = [];
|
|
2310
|
+
if (path.run) rules.push({
|
|
2311
|
+
kind: "run",
|
|
2312
|
+
label: at("run"),
|
|
2313
|
+
...path.run
|
|
2314
|
+
});
|
|
2315
|
+
const tools = path.tools;
|
|
2316
|
+
if (tools) {
|
|
2317
|
+
for (const tool of tools.required ?? []) rules.push({
|
|
2318
|
+
kind: "eventually",
|
|
2319
|
+
label: at(`tools.required[${tool}]`),
|
|
2320
|
+
p: toolCall(tool)
|
|
2321
|
+
});
|
|
2322
|
+
for (const tool of tools.forbidden ?? []) rules.push({
|
|
2323
|
+
kind: "never",
|
|
2324
|
+
label: at(`tools.forbidden[${tool}]`),
|
|
2325
|
+
p: toolCall(tool)
|
|
2326
|
+
});
|
|
2327
|
+
if (tools.allowed) rules.push({
|
|
2328
|
+
kind: "never",
|
|
2329
|
+
label: at("tools.allowed"),
|
|
2330
|
+
p: {
|
|
2331
|
+
kind: "TOOL",
|
|
2332
|
+
not: { tool: { oneOf: [...tools.allowed] } }
|
|
2333
|
+
}
|
|
2334
|
+
});
|
|
2335
|
+
if (tools.maxCalls !== void 0) rules.push({
|
|
2336
|
+
kind: "atMost",
|
|
2337
|
+
label: at("tools.maxCalls"),
|
|
2338
|
+
p: ANY_TOOL,
|
|
2339
|
+
max: tools.maxCalls
|
|
2340
|
+
});
|
|
2341
|
+
for (const [tool, max] of Object.entries(tools.maxCallsPerTool ?? {})) rules.push({
|
|
2342
|
+
kind: "atMost",
|
|
2343
|
+
label: at(`tools.maxCallsPerTool[${tool}]`),
|
|
2344
|
+
p: toolCall(tool),
|
|
2345
|
+
max
|
|
2346
|
+
});
|
|
2347
|
+
const order = tools.requiredOrder ?? [];
|
|
2348
|
+
for (let i = 1; i < order.length; i++) rules.push({
|
|
2349
|
+
kind: "precedes",
|
|
2350
|
+
label: at(`tools.requiredOrder[${order[i - 1]}->${order[i]}]`),
|
|
2351
|
+
a: toolCall(order[i - 1]),
|
|
2352
|
+
b: toolCall(order[i]),
|
|
2353
|
+
...tools.orderMode === void 0 ? {} : { order: tools.orderMode }
|
|
2354
|
+
});
|
|
2355
|
+
if (order.length === 1) rules.push({
|
|
2356
|
+
kind: "eventually",
|
|
2357
|
+
label: at(`tools.requiredOrder[${order[0]}]`),
|
|
2358
|
+
p: toolCall(order[0])
|
|
2359
|
+
});
|
|
2360
|
+
tools.arguments?.forEach((arg, i) => {
|
|
2361
|
+
const check = argumentCheck(arg);
|
|
2362
|
+
rules.push({
|
|
2363
|
+
kind: "argument",
|
|
2364
|
+
label: at(`tools.arguments[${i}] ${arg.tool} ${arg.pointer || "(whole value)"} ${describeCheck(check)}`),
|
|
2365
|
+
p: toolCall(arg.tool),
|
|
2366
|
+
pointer: arg.pointer,
|
|
2367
|
+
check,
|
|
2368
|
+
...arg.occurrence === void 0 ? {} : { occurrence: arg.occurrence }
|
|
2369
|
+
});
|
|
2370
|
+
});
|
|
2371
|
+
}
|
|
2372
|
+
const llm = path.llm;
|
|
2373
|
+
if (llm) {
|
|
2374
|
+
if (llm.maxCalls !== void 0) rules.push({
|
|
2375
|
+
kind: "atMost",
|
|
2376
|
+
label: at("llm.maxCalls"),
|
|
2377
|
+
p: ANY_LLM,
|
|
2378
|
+
max: llm.maxCalls
|
|
2379
|
+
});
|
|
2380
|
+
if (llm.maxTotalTokens !== void 0) rules.push({
|
|
2381
|
+
kind: "tokensAtMost",
|
|
2382
|
+
label: at("llm.maxTotalTokens"),
|
|
2383
|
+
p: ANY_LLM,
|
|
2384
|
+
max: llm.maxTotalTokens
|
|
2385
|
+
});
|
|
2386
|
+
if (llm.allowedModels) rules.push({
|
|
2387
|
+
kind: "never",
|
|
2388
|
+
label: at("llm.allowedModels"),
|
|
2389
|
+
p: {
|
|
2390
|
+
kind: "LLM",
|
|
2391
|
+
not: { model: { oneOf: [...llm.allowedModels] } }
|
|
2392
|
+
}
|
|
2393
|
+
});
|
|
2394
|
+
}
|
|
2395
|
+
for (const rule of path.rules ?? []) rules.push({ ...rule });
|
|
2396
|
+
return rules;
|
|
2397
|
+
}
|
|
2398
|
+
function argumentCheck(arg) {
|
|
2399
|
+
if (arg.exists) return { op: "exists" };
|
|
2400
|
+
if ("equals" in arg) return {
|
|
2401
|
+
op: "equals",
|
|
2402
|
+
value: arg.equals
|
|
2403
|
+
};
|
|
2404
|
+
if (arg.oneOf) return {
|
|
2405
|
+
op: "oneOf",
|
|
2406
|
+
values: arg.oneOf
|
|
2407
|
+
};
|
|
2408
|
+
return {
|
|
2409
|
+
op: "type",
|
|
2410
|
+
type: arg.type
|
|
2411
|
+
};
|
|
2412
|
+
}
|
|
2413
|
+
function parseSpec(input) {
|
|
2414
|
+
const spec = object(input, "contract");
|
|
2415
|
+
strictKeys(spec, SPEC_KEYS, "contract");
|
|
2416
|
+
const name = nonEmptyString(spec.name, "name");
|
|
2417
|
+
if (spec.description !== void 0) nonEmptyString(spec.description, "description");
|
|
2418
|
+
if (spec.scope !== void 0) {
|
|
2419
|
+
const scope = object(spec.scope, "scope");
|
|
2420
|
+
strictKeys(scope, ["root"], "scope");
|
|
2421
|
+
predicate(scope.root, "scope.root");
|
|
2422
|
+
}
|
|
2423
|
+
parsePath(spec, "");
|
|
2424
|
+
if (spec.alternatives !== void 0) {
|
|
2425
|
+
const alternatives = object(spec.alternatives, "alternatives");
|
|
2426
|
+
strictKeys(alternatives, ["anyOf"], "alternatives");
|
|
2427
|
+
nonEmptyArray(alternatives.anyOf, "alternatives.anyOf").forEach((alt, i) => {
|
|
2428
|
+
const where = `alternatives.anyOf[${i}]`;
|
|
2429
|
+
const a = object(alt, where);
|
|
2430
|
+
strictKeys(a, ALTERNATIVE_KEYS, where);
|
|
2431
|
+
nonEmptyString(a.id, `${where}.id`);
|
|
2432
|
+
parsePath(a, where);
|
|
2433
|
+
});
|
|
2434
|
+
}
|
|
2435
|
+
return {
|
|
2436
|
+
...spec,
|
|
2437
|
+
name
|
|
2438
|
+
};
|
|
2439
|
+
}
|
|
2440
|
+
function parsePath(path, prefix) {
|
|
2441
|
+
const at = (key) => prefix ? `${prefix}.${key}` : key;
|
|
2442
|
+
if (path.run !== void 0) {
|
|
2443
|
+
const run = object(path.run, at("run"));
|
|
2444
|
+
strictKeys(run, RUN_KEYS, at("run"));
|
|
2445
|
+
if (Array.isArray(run.allowedStatuses)) run.allowedStatuses.forEach((status, i) => {
|
|
2446
|
+
oneOfValues(status, RUN_STATUSES, at(`run.allowedStatuses[${i}]`));
|
|
2447
|
+
});
|
|
2448
|
+
assertRule({
|
|
2449
|
+
kind: "run",
|
|
2450
|
+
label: "run",
|
|
2451
|
+
...run
|
|
2452
|
+
}, at("run"));
|
|
2453
|
+
}
|
|
2454
|
+
if (path.tools !== void 0) {
|
|
2455
|
+
const tools = object(path.tools, at("tools"));
|
|
2456
|
+
strictKeys(tools, TOOLS_KEYS, at("tools"));
|
|
2457
|
+
for (const key of [
|
|
2458
|
+
"required",
|
|
2459
|
+
"forbidden",
|
|
2460
|
+
"allowed",
|
|
2461
|
+
"requiredOrder"
|
|
2462
|
+
]) if (tools[key] !== void 0) toolNames(tools[key], at(`tools.${key}`));
|
|
2463
|
+
if (tools.maxCalls !== void 0) count(tools.maxCalls, at("tools.maxCalls"));
|
|
2464
|
+
if (tools.maxCallsPerTool !== void 0) {
|
|
2465
|
+
const perTool = object(tools.maxCallsPerTool, at("tools.maxCallsPerTool"));
|
|
2466
|
+
for (const [tool, max] of Object.entries(perTool)) {
|
|
2467
|
+
nonEmptyString(tool, at("tools.maxCallsPerTool key"));
|
|
2468
|
+
count(max, at(`tools.maxCallsPerTool.${tool}`));
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
if (tools.orderMode !== void 0) oneOfValues(tools.orderMode, ORDER_MODES, at("tools.orderMode"));
|
|
2472
|
+
if (tools.orderMode !== void 0 && tools.requiredOrder === void 0) throw new ValidationError(`${at("tools.orderMode")}: set only with requiredOrder`);
|
|
2473
|
+
if (tools.arguments !== void 0) nonEmptyArray(tools.arguments, at("tools.arguments")).forEach((arg, i) => {
|
|
2474
|
+
const where = at(`tools.arguments[${i}]`);
|
|
2475
|
+
const a = object(arg, where);
|
|
2476
|
+
strictKeys(a, ARGUMENT_KEYS, where);
|
|
2477
|
+
nonEmptyString(a.tool, `${where}.tool`);
|
|
2478
|
+
const checks = ARGUMENT_CHECK_KEYS.filter((k) => k in a);
|
|
2479
|
+
if (checks.length !== 1) throw new ValidationError(`${where}: set exactly one of ${ARGUMENT_CHECK_KEYS.join(", ")} (found ${checks.length})`);
|
|
2480
|
+
if (a.exists !== void 0 && a.exists !== true) throw new ValidationError(`${where}.exists: must be true`);
|
|
2481
|
+
if (a.oneOf !== void 0) nonEmptyArray(a.oneOf, `${where}.oneOf`);
|
|
2482
|
+
if (a.type !== void 0) oneOfValues(a.type, ARGUMENT_TYPES, `${where}.type`);
|
|
2483
|
+
if (a.occurrence !== void 0) oneOfValues(a.occurrence, ARGUMENT_OCCURRENCES, `${where}.occurrence`);
|
|
2484
|
+
assertRule({
|
|
2485
|
+
kind: "argument",
|
|
2486
|
+
label: "argument",
|
|
2487
|
+
p: toolCall(a.tool),
|
|
2488
|
+
pointer: a.pointer,
|
|
2489
|
+
check: argumentCheck(a)
|
|
2490
|
+
}, where);
|
|
2491
|
+
});
|
|
2492
|
+
}
|
|
2493
|
+
if (path.llm !== void 0) {
|
|
2494
|
+
const llm = object(path.llm, at("llm"));
|
|
2495
|
+
strictKeys(llm, LLM_KEYS, at("llm"));
|
|
2496
|
+
if (llm.maxCalls !== void 0) count(llm.maxCalls, at("llm.maxCalls"));
|
|
2497
|
+
if (llm.maxTotalTokens !== void 0) count(llm.maxTotalTokens, at("llm.maxTotalTokens"));
|
|
2498
|
+
if (llm.allowedModels !== void 0) toolNames(llm.allowedModels, at("llm.allowedModels"));
|
|
2499
|
+
}
|
|
2500
|
+
if (path.rules !== void 0) nonEmptyArray(path.rules, at("rules")).forEach((raw, i) => {
|
|
2501
|
+
const where = at(`rules[${i}]`);
|
|
2502
|
+
const rule = object(raw, where);
|
|
2503
|
+
const kind = oneOfValues(rule.kind, Object.keys(RULE_KEYS), `${where}.kind`);
|
|
2504
|
+
strictKeys(rule, RULE_KEYS[kind], where);
|
|
2505
|
+
for (const key of [
|
|
2506
|
+
"p",
|
|
2507
|
+
"a",
|
|
2508
|
+
"b",
|
|
2509
|
+
"prior"
|
|
2510
|
+
]) if (rule[key] !== void 0) predicate(rule[key], `${where}.${key}`);
|
|
2511
|
+
if (kind === "argument" && rule.check !== void 0) strictKeys(object(rule.check, `${where}.check`), [
|
|
2512
|
+
"op",
|
|
2513
|
+
"value",
|
|
2514
|
+
"values",
|
|
2515
|
+
"type"
|
|
2516
|
+
], `${where}.check`);
|
|
2517
|
+
assertRule(rule, where);
|
|
2518
|
+
});
|
|
2519
|
+
}
|
|
2520
|
+
function predicate(value, where) {
|
|
2521
|
+
const p = object(value, where);
|
|
2522
|
+
strictKeys(p, PREDICATE_KEYS, where);
|
|
2523
|
+
for (const key of [
|
|
2524
|
+
"name",
|
|
2525
|
+
"tool",
|
|
2526
|
+
"model"
|
|
2527
|
+
]) {
|
|
2528
|
+
const m = p[key];
|
|
2529
|
+
if (m !== null && typeof m === "object" && !Array.isArray(m)) strictKeys(m, MATCHER_KEYS, `${where}.${key}`);
|
|
2530
|
+
}
|
|
2531
|
+
if (p.kind !== void 0) oneOfValues(p.kind, SPAN_KINDS, `${where}.kind`);
|
|
2532
|
+
if (p.not !== void 0) predicate(p.not, `${where}.not`);
|
|
2533
|
+
}
|
|
2534
|
+
function strictKeys(value, allowed, where) {
|
|
2535
|
+
for (const key of Object.keys(value)) {
|
|
2536
|
+
if (allowed.includes(key)) continue;
|
|
2537
|
+
const hint = closest(key, allowed);
|
|
2538
|
+
throw new ValidationError(`${where}: unknown key "${key}"${hint ? ` — did you mean "${hint}"?` : ""} (allowed: ${allowed.join(", ")})`);
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
function object(value, where) {
|
|
2542
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new ValidationError(`${where}: must be an object`);
|
|
2543
|
+
return value;
|
|
2544
|
+
}
|
|
2545
|
+
function nonEmptyString(value, where) {
|
|
2546
|
+
if (typeof value !== "string" || value.trim().length === 0) throw new ValidationError(`${where}: must be a non-empty string`);
|
|
2547
|
+
return value;
|
|
2548
|
+
}
|
|
2549
|
+
function nonEmptyArray(value, where) {
|
|
2550
|
+
if (!Array.isArray(value) || value.length === 0) throw new ValidationError(`${where}: must be a non-empty array`);
|
|
2551
|
+
return value;
|
|
2552
|
+
}
|
|
2553
|
+
function toolNames(value, where) {
|
|
2554
|
+
const names = nonEmptyArray(value, where).map((v, i) => nonEmptyString(v, `${where}[${i}]`));
|
|
2555
|
+
const dup = names.find((n, i) => names.indexOf(n) !== i);
|
|
2556
|
+
if (dup !== void 0) throw new ValidationError(`${where}: "${dup}" is listed twice`);
|
|
2557
|
+
return names;
|
|
2558
|
+
}
|
|
2559
|
+
function count(value, where) {
|
|
2560
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) throw new ValidationError(`${where}: must be a non-negative integer`);
|
|
2561
|
+
}
|
|
2562
|
+
function oneOfValues(value, allowed, where) {
|
|
2563
|
+
if (typeof value === "string" && allowed.includes(value)) return value;
|
|
2564
|
+
const hint = typeof value === "string" ? closest(value, allowed) : void 0;
|
|
2565
|
+
throw new ValidationError(`${where}: ${JSON.stringify(value)} is not one of ${allowed.join(", ")}${hint ? ` — did you mean "${hint}"?` : ""}`);
|
|
2566
|
+
}
|
|
2567
|
+
/** The allowed word within edit distance 2 (or a case-insensitive match). */
|
|
2568
|
+
function closest(word, allowed) {
|
|
2569
|
+
let best;
|
|
2570
|
+
for (const candidate of allowed) {
|
|
2571
|
+
const d = candidate.toLowerCase() === word.toLowerCase() ? 0 : editDistance(word.toLowerCase(), candidate.toLowerCase());
|
|
2572
|
+
if (d <= 2 && (!best || d < best.d)) best = {
|
|
2573
|
+
word: candidate,
|
|
2574
|
+
d
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2577
|
+
return best?.word;
|
|
2578
|
+
}
|
|
2579
|
+
function editDistance(a, b) {
|
|
2580
|
+
let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
|
|
2581
|
+
for (let i = 1; i <= a.length; i++) {
|
|
2582
|
+
const cur = [i];
|
|
2583
|
+
for (let j = 1; j <= b.length; j++) cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
2584
|
+
prev = cur;
|
|
2585
|
+
}
|
|
2586
|
+
return prev[b.length];
|
|
2587
|
+
}
|
|
2588
|
+
/**
|
|
2589
|
+
* Find contradictions and likely mistakes in a declarative contract. Parses
|
|
2590
|
+
* strictly first, so a malformed spec throws instead of linting.
|
|
2591
|
+
*/
|
|
2592
|
+
function lintTraceContractSpec(input) {
|
|
2593
|
+
const spec = parseSpec(input);
|
|
2594
|
+
const findings = [];
|
|
2595
|
+
lintPath(spec, "", findings);
|
|
2596
|
+
for (const [i, alt] of (spec.alternatives?.anyOf ?? []).entries()) lintPath(alt, `alternatives.anyOf[${i}]`, findings);
|
|
2597
|
+
if (spec.alternatives?.anyOf.length === 1) findings.push({
|
|
2598
|
+
level: "warning",
|
|
2599
|
+
code: "alternatives.single",
|
|
2600
|
+
path: "alternatives.anyOf",
|
|
2601
|
+
message: "one alternative is the same as base checks; move its checks to the base or add another path"
|
|
2602
|
+
});
|
|
2603
|
+
const ids = spec.alternatives?.anyOf.map((a) => a.id) ?? [];
|
|
2604
|
+
const dupId = ids.find((id, i) => ids.indexOf(id) !== i);
|
|
2605
|
+
if (dupId !== void 0) findings.push({
|
|
2606
|
+
level: "error",
|
|
2607
|
+
code: "alternatives.duplicate-id",
|
|
2608
|
+
path: "alternatives.anyOf",
|
|
2609
|
+
message: `alternative id "${dupId}" is used twice`
|
|
2610
|
+
});
|
|
2611
|
+
return findings;
|
|
2612
|
+
}
|
|
2613
|
+
function lintPath(path, prefix, out) {
|
|
2614
|
+
const at = (key) => prefix ? `${prefix}.${key}` : key;
|
|
2615
|
+
const error = (code, key, message) => out.push({
|
|
2616
|
+
level: "error",
|
|
2617
|
+
code,
|
|
2618
|
+
path: at(key),
|
|
2619
|
+
message
|
|
2620
|
+
});
|
|
2621
|
+
const warn = (code, key, message) => out.push({
|
|
2622
|
+
level: "warning",
|
|
2623
|
+
code,
|
|
2624
|
+
path: at(key),
|
|
2625
|
+
message
|
|
2626
|
+
});
|
|
2627
|
+
const t = path.tools ?? {};
|
|
2628
|
+
const forbidden = new Set(t.forbidden ?? []);
|
|
2629
|
+
const allowed = t.allowed ? new Set(t.allowed) : void 0;
|
|
2630
|
+
const needed = [...t.required ?? [], ...t.requiredOrder ?? []];
|
|
2631
|
+
for (const tool of needed) {
|
|
2632
|
+
if (forbidden.has(tool)) error("tools.required-and-forbidden", "tools", `"${tool}" is both required and forbidden`);
|
|
2633
|
+
if (allowed && !allowed.has(tool)) error("tools.required-not-allowed", "tools.allowed", `"${tool}" is required but not in allowed`);
|
|
2634
|
+
if (t.maxCallsPerTool?.[tool] === 0) error("tools.required-capped-at-zero", "tools.maxCallsPerTool", `"${tool}" is required but capped at 0 calls`);
|
|
2635
|
+
}
|
|
2636
|
+
if (t.maxCalls === 0 && needed.length > 0) error("tools.required-but-no-calls", "tools.maxCalls", "tools are required but maxCalls is 0");
|
|
2637
|
+
const distinctNeeded = new Set(needed).size;
|
|
2638
|
+
if (t.maxCalls !== void 0 && t.maxCalls > 0 && distinctNeeded > t.maxCalls) error("tools.max-below-required", "tools.maxCalls", `${distinctNeeded} distinct tools are required but maxCalls is ${t.maxCalls}`);
|
|
2639
|
+
for (const tool of forbidden) {
|
|
2640
|
+
if (allowed?.has(tool)) error("tools.allowed-and-forbidden", "tools", `"${tool}" is both allowed and forbidden`);
|
|
2641
|
+
if (t.maxCallsPerTool?.[tool] !== void 0) warn("tools.cap-on-forbidden", "tools.maxCallsPerTool", `"${tool}" is forbidden, so its call cap never applies`);
|
|
2642
|
+
}
|
|
2643
|
+
for (const tool of Object.keys(t.maxCallsPerTool ?? {})) if (allowed && !allowed.has(tool)) warn("tools.cap-on-disallowed", "tools.maxCallsPerTool", `"${tool}" has a cap but is not in allowed`);
|
|
2644
|
+
for (const [i, arg] of (t.arguments ?? []).entries()) {
|
|
2645
|
+
if (forbidden.has(arg.tool)) error("tools.argument-on-forbidden", `tools.arguments[${i}]`, `"${arg.tool}" is forbidden, so its arguments can never be checked`);
|
|
2646
|
+
if (!needed.includes(arg.tool)) warn("tools.argument-without-required", `tools.arguments[${i}]`, `"${arg.tool}" is not required; a run that never calls it fails this check for missing evidence`);
|
|
2647
|
+
}
|
|
2648
|
+
const order = t.requiredOrder ?? [];
|
|
2649
|
+
const repeated = order.find((tool, i) => order.indexOf(tool) !== i);
|
|
2650
|
+
if (repeated !== void 0) error("tools.order-repeats", "tools.requiredOrder", `"${repeated}" appears twice, so the order contradicts itself`);
|
|
2651
|
+
if (order.length > 1 && t.orderMode === void 0) warn("tools.order-default-mode", "tools.orderMode", "start-order passes when calls overlap; set finish-before-start if each step must finish first");
|
|
2652
|
+
const run = path.run;
|
|
2653
|
+
if (run?.requireCompleted && run.allowedStatuses?.includes("running")) error("run.running-and-completed", "run", "requireCompleted contradicts allowedStatuses containing \"running\"");
|
|
2654
|
+
if (path.llm?.maxCalls === 0 && (path.llm.maxTotalTokens !== void 0 || path.llm.allowedModels)) warn("llm.no-calls", "llm", "maxCalls is 0, so the token and model limits never apply");
|
|
2655
|
+
}
|
|
2656
|
+
//#endregion
|
|
1506
2657
|
//#region src/active-learning.ts
|
|
1507
2658
|
async function proposeSynthesisTargets(dataset, traceStore, options = {}) {
|
|
1508
2659
|
const minPerBand = options.minPerBand ?? 5;
|
|
@@ -2108,7 +3259,7 @@ async function runAgentControlLoop(config) {
|
|
|
2108
3259
|
const started = Date.now();
|
|
2109
3260
|
const wallTimer = budget.maxWallMs ? setTimeout(() => controller.abort(/* @__PURE__ */ new Error("control runtime wall timeout")), budget.maxWallMs) : void 0;
|
|
2110
3261
|
const history = [];
|
|
2111
|
-
const emitter = config.store ? new TraceEmitter(config.store) : void 0;
|
|
3262
|
+
const emitter = config.store ? new TraceEmitter(config.store, { onCaptureError: () => void 0 }) : void 0;
|
|
2112
3263
|
let spentCostUsd = 0;
|
|
2113
3264
|
const runtimeErrors = [];
|
|
2114
3265
|
const repeatedDetector = repeatedActionDetector({ maxRepeated: config.stopPolicies?.maxRepeatedActions ?? 0 });
|
|
@@ -2117,7 +3268,7 @@ async function runAgentControlLoop(config) {
|
|
|
2117
3268
|
minScoreDelta: config.stopPolicies?.minScoreDelta ?? .001
|
|
2118
3269
|
});
|
|
2119
3270
|
try {
|
|
2120
|
-
if (emitter) await runTrace(runtimeErrors, 0, () => emitter.startRun({
|
|
3271
|
+
if (emitter) await runTrace(emitter, runtimeErrors, 0, () => emitter.startRun({
|
|
2121
3272
|
scenarioId: config.scenarioId ?? "agent-control-loop",
|
|
2122
3273
|
projectId: config.projectId,
|
|
2123
3274
|
variantId: config.variantId,
|
|
@@ -2322,7 +3473,7 @@ async function runAgentControlLoop(config) {
|
|
|
2322
3473
|
const evalsBefore = evals;
|
|
2323
3474
|
const scoreBefore = averageScore(evals);
|
|
2324
3475
|
const actionStarted = Date.now();
|
|
2325
|
-
const stepHandle = emitter ? await runTrace(runtimeErrors, stepIndex, () => emitter.tool({
|
|
3476
|
+
const stepHandle = emitter ? await runTrace(emitter, runtimeErrors, stepIndex, () => emitter.tool({
|
|
2326
3477
|
name: `control-step-${stepIndex}`,
|
|
2327
3478
|
toolName: "agent-control-action",
|
|
2328
3479
|
args: decision.action,
|
|
@@ -2360,7 +3511,7 @@ async function runAgentControlLoop(config) {
|
|
|
2360
3511
|
durationMs: Date.now() - actionStarted
|
|
2361
3512
|
};
|
|
2362
3513
|
if (actionFailure === "stop") {
|
|
2363
|
-
await runTrace(runtimeErrors, stepIndex, () => stepHandle?.fail(actionOutcome.error ?? "action failed"));
|
|
3514
|
+
await runTrace(emitter, runtimeErrors, stepIndex, () => stepHandle?.fail(actionOutcome.error ?? "action failed"));
|
|
2364
3515
|
const step = {
|
|
2365
3516
|
index: stepIndex,
|
|
2366
3517
|
decision,
|
|
@@ -2411,7 +3562,7 @@ async function runAgentControlLoop(config) {
|
|
|
2411
3562
|
endedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2412
3563
|
};
|
|
2413
3564
|
history.push(step);
|
|
2414
|
-
await runTrace(runtimeErrors, stepIndex, () => stepHandle?.fail(runtimeErrors[runtimeErrors.length - 1].message));
|
|
3565
|
+
await runTrace(emitter, runtimeErrors, stepIndex, () => stepHandle?.fail(runtimeErrors[runtimeErrors.length - 1].message));
|
|
2415
3566
|
await runOnStep(config.onStep, step, runtimeErrors);
|
|
2416
3567
|
return finish(emitter, {
|
|
2417
3568
|
intent: config.intent,
|
|
@@ -2452,7 +3603,7 @@ async function runAgentControlLoop(config) {
|
|
|
2452
3603
|
endedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2453
3604
|
};
|
|
2454
3605
|
history.push(step);
|
|
2455
|
-
await runTrace(runtimeErrors, stepIndex, () => stepHandle?.fail(runtimeErrors[runtimeErrors.length - 1].message));
|
|
3606
|
+
await runTrace(emitter, runtimeErrors, stepIndex, () => stepHandle?.fail(runtimeErrors[runtimeErrors.length - 1].message));
|
|
2456
3607
|
await runOnStep(config.onStep, step, runtimeErrors);
|
|
2457
3608
|
return finish(emitter, {
|
|
2458
3609
|
intent: config.intent,
|
|
@@ -2490,14 +3641,14 @@ async function runAgentControlLoop(config) {
|
|
|
2490
3641
|
endedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2491
3642
|
};
|
|
2492
3643
|
history.push(step);
|
|
2493
|
-
if (actionOutcome.ok) await runTrace(runtimeErrors, stepIndex, () => stepHandle?.end({ attributes: {
|
|
3644
|
+
if (actionOutcome.ok) await runTrace(emitter, runtimeErrors, stepIndex, () => stepHandle?.end({ attributes: {
|
|
2494
3645
|
actionCostUsd: actionOutcome.costUsd ?? null,
|
|
2495
3646
|
spentCostUsd,
|
|
2496
3647
|
scoreBefore: scoreBefore ?? null,
|
|
2497
3648
|
scoreAfter: scoreAfter ?? null,
|
|
2498
3649
|
noProgressStreak
|
|
2499
3650
|
} }));
|
|
2500
|
-
else await runTrace(runtimeErrors, stepIndex, () => stepHandle?.fail(actionOutcome.error ?? "action failed", { attributes: {
|
|
3651
|
+
else await runTrace(emitter, runtimeErrors, stepIndex, () => stepHandle?.fail(actionOutcome.error ?? "action failed", { attributes: {
|
|
2501
3652
|
spentCostUsd,
|
|
2502
3653
|
noProgressStreak
|
|
2503
3654
|
} }));
|
|
@@ -2703,7 +3854,7 @@ function budgetStopDecision(budget, spentCostUsd) {
|
|
|
2703
3854
|
async function recordCostBudget(emitter, budget, spentCostUsd, handle, runtimeErrors, stepIndex) {
|
|
2704
3855
|
if (!emitter || budget.maxCostUsd === void 0) return;
|
|
2705
3856
|
const maxCostUsd = budget.maxCostUsd;
|
|
2706
|
-
await runTrace(runtimeErrors, stepIndex, () => emitter.recordBudget({
|
|
3857
|
+
await runTrace(emitter, runtimeErrors, stepIndex, () => emitter.recordBudget({
|
|
2707
3858
|
dimension: "usd",
|
|
2708
3859
|
limit: maxCostUsd,
|
|
2709
3860
|
consumed: spentCostUsd,
|
|
@@ -2714,7 +3865,7 @@ async function recordCostBudget(emitter, budget, spentCostUsd, handle, runtimeEr
|
|
|
2714
3865
|
}
|
|
2715
3866
|
async function recordEvalSpans(emitter, evals, phase, runtimeErrors, stepIndex, targetSpanId) {
|
|
2716
3867
|
if (!emitter) return;
|
|
2717
|
-
for (const result of evals) await runTrace(runtimeErrors, stepIndex, () => emitter.recordJudge({
|
|
3868
|
+
for (const result of evals) await runTrace(emitter, runtimeErrors, stepIndex, () => emitter.recordJudge({
|
|
2718
3869
|
judgeId: result.objective ? "objective-validator" : "subjective-judge",
|
|
2719
3870
|
targetSpanId: targetSpanId ?? emitter.runId,
|
|
2720
3871
|
name: `control-eval/${result.id}`,
|
|
@@ -2738,13 +3889,24 @@ async function runOnStep(onStep, step, runtimeErrors) {
|
|
|
2738
3889
|
runtimeErrors.push(runtimeError("on-step", step.index, err));
|
|
2739
3890
|
}
|
|
2740
3891
|
}
|
|
2741
|
-
|
|
3892
|
+
/**
|
|
3893
|
+
* Run trace writes and record a `trace` runtime error when any of them was
|
|
3894
|
+
* dropped. The emitter never throws a failed store write; it counts it, so the
|
|
3895
|
+
* count is what this reads. A throw from building the write is recorded too.
|
|
3896
|
+
*/
|
|
3897
|
+
async function runTrace(emitter, runtimeErrors, stepIndex, write) {
|
|
3898
|
+
if (!emitter) return void 0;
|
|
3899
|
+
const droppedBefore = emitter.captureStats().dropped;
|
|
3900
|
+
let value;
|
|
2742
3901
|
try {
|
|
2743
|
-
|
|
3902
|
+
value = await write();
|
|
2744
3903
|
} catch (err) {
|
|
2745
3904
|
runtimeErrors.push(runtimeError("trace", stepIndex, err));
|
|
2746
3905
|
return;
|
|
2747
3906
|
}
|
|
3907
|
+
const after = emitter.captureStats();
|
|
3908
|
+
if (after.dropped > droppedBefore) runtimeErrors.push(runtimeError("trace", stepIndex, after.lastError ?? "trace write dropped"));
|
|
3909
|
+
return value;
|
|
2748
3910
|
}
|
|
2749
3911
|
function fingerprintState(state, policies) {
|
|
2750
3912
|
if (policies?.stateFingerprint) return policies.stateFingerprint(state);
|
|
@@ -2778,7 +3940,7 @@ function runtimeError(phase, stepIndex, err) {
|
|
|
2778
3940
|
};
|
|
2779
3941
|
}
|
|
2780
3942
|
async function finish(emitter, result) {
|
|
2781
|
-
await runTrace(result.runtimeErrors, result.steps.length, () => emitter?.endRun({
|
|
3943
|
+
await runTrace(emitter, result.runtimeErrors, result.steps.length, () => emitter?.endRun({
|
|
2782
3944
|
pass: result.pass,
|
|
2783
3945
|
score: result.score ?? averageScore(result.finalEvals),
|
|
2784
3946
|
failureClass: result.failureClass,
|
|
@@ -2836,6 +3998,119 @@ async function discoverPersonas(dir, opts = {}) {
|
|
|
2836
3998
|
return results;
|
|
2837
3999
|
}
|
|
2838
4000
|
//#endregion
|
|
4001
|
+
//#region src/improvement-verdict.ts
|
|
4002
|
+
const DEFAULTS = {
|
|
4003
|
+
keepThreshold: 5,
|
|
4004
|
+
regressionThreshold: 5,
|
|
4005
|
+
iqrUnstableAbove: 10,
|
|
4006
|
+
stddevUnstableAbove: Number.POSITIVE_INFINITY,
|
|
4007
|
+
minRepsForVerdict: 3
|
|
4008
|
+
};
|
|
4009
|
+
function resolveThresholds(t) {
|
|
4010
|
+
const r = {
|
|
4011
|
+
...DEFAULTS,
|
|
4012
|
+
...t ?? {}
|
|
4013
|
+
};
|
|
4014
|
+
if (r.keepThreshold < 0) throw new ValidationError(`improvement-verdict: keepThreshold must be >= 0, got ${r.keepThreshold}`);
|
|
4015
|
+
if (r.regressionThreshold < 0) throw new ValidationError(`improvement-verdict: regressionThreshold must be >= 0, got ${r.regressionThreshold}`);
|
|
4016
|
+
if (r.minRepsForVerdict < 1) throw new ValidationError(`improvement-verdict: minRepsForVerdict must be >= 1, got ${r.minRepsForVerdict}`);
|
|
4017
|
+
return r;
|
|
4018
|
+
}
|
|
4019
|
+
function median(sorted) {
|
|
4020
|
+
const n = sorted.length;
|
|
4021
|
+
if (n === 0) return 0;
|
|
4022
|
+
const mid = Math.floor(n / 2);
|
|
4023
|
+
return n % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
4024
|
+
}
|
|
4025
|
+
/** Population standard deviation (÷n). 0 for fewer than 2 values. */
|
|
4026
|
+
function stddev(values, mean) {
|
|
4027
|
+
if (values.length < 2) return 0;
|
|
4028
|
+
const variance = values.reduce((acc, v) => acc + (v - mean) ** 2, 0) / values.length;
|
|
4029
|
+
return Math.sqrt(variance);
|
|
4030
|
+
}
|
|
4031
|
+
/**
|
|
4032
|
+
* Compute the N-rep statistics for a set of reps. Pure — no I/O. The `stable`
|
|
4033
|
+
* flag is the trust gate the verdict depends on: a sample whose spread exceeds
|
|
4034
|
+
* the configured bounds can't distinguish a real delta from run-to-run noise.
|
|
4035
|
+
*/
|
|
4036
|
+
function computeExperimentStats(reps, thresholds) {
|
|
4037
|
+
const t = resolveThresholds(thresholds);
|
|
4038
|
+
const n = reps.length;
|
|
4039
|
+
if (n === 0) return {
|
|
4040
|
+
median: 0,
|
|
4041
|
+
mean: 0,
|
|
4042
|
+
min: 0,
|
|
4043
|
+
max: 0,
|
|
4044
|
+
iqr: 0,
|
|
4045
|
+
stddev: 0,
|
|
4046
|
+
passRate: null,
|
|
4047
|
+
n: 0,
|
|
4048
|
+
stable: false
|
|
4049
|
+
};
|
|
4050
|
+
const scores = reps.map((r) => {
|
|
4051
|
+
if (!Number.isFinite(r.score)) throw new ValidationError(`improvement-verdict: rep ${r.rep} has non-finite score ${r.score}`);
|
|
4052
|
+
return r.score;
|
|
4053
|
+
});
|
|
4054
|
+
const sorted = [...scores].sort((a, b) => a - b);
|
|
4055
|
+
const mean = scores.reduce((s, v) => s + v, 0) / n;
|
|
4056
|
+
const sd = stddev(scores, mean);
|
|
4057
|
+
const spread = iqr(scores);
|
|
4058
|
+
const rated = reps.filter((r) => typeof r.passed === "boolean");
|
|
4059
|
+
const passRate = rated.length === 0 ? null : rated.filter((r) => r.passed).length / rated.length;
|
|
4060
|
+
const stable = spread < t.iqrUnstableAbove && sd < t.stddevUnstableAbove;
|
|
4061
|
+
return {
|
|
4062
|
+
median: median(sorted),
|
|
4063
|
+
mean,
|
|
4064
|
+
min: sorted[0],
|
|
4065
|
+
max: sorted[n - 1],
|
|
4066
|
+
iqr: spread,
|
|
4067
|
+
stddev: sd,
|
|
4068
|
+
passRate,
|
|
4069
|
+
n,
|
|
4070
|
+
stable
|
|
4071
|
+
};
|
|
4072
|
+
}
|
|
4073
|
+
/**
|
|
4074
|
+
* Verdict for a candidate against its parent. Pure — operates on already-computed
|
|
4075
|
+
* stats. KEEP/REGRESSION require both sides to have `>= minRepsForVerdict` reps
|
|
4076
|
+
* AND the candidate to be `stable`; otherwise the result is NOISE (unstable) or
|
|
4077
|
+
* ITERATE (not enough reps / no parent).
|
|
4078
|
+
*/
|
|
4079
|
+
function improvementVerdict(candidate, parent, thresholds) {
|
|
4080
|
+
const t = resolveThresholds(thresholds);
|
|
4081
|
+
if (!parent) return {
|
|
4082
|
+
verdict: "ITERATE",
|
|
4083
|
+
medianDelta: null,
|
|
4084
|
+
reason: "no parent experiment to compare against"
|
|
4085
|
+
};
|
|
4086
|
+
if (candidate.n < t.minRepsForVerdict || parent.n < t.minRepsForVerdict) return {
|
|
4087
|
+
verdict: "ITERATE",
|
|
4088
|
+
medianDelta: null,
|
|
4089
|
+
reason: `need >= ${t.minRepsForVerdict} reps on both sides (candidate n=${candidate.n}, parent n=${parent.n})`
|
|
4090
|
+
};
|
|
4091
|
+
if (!candidate.stable) return {
|
|
4092
|
+
verdict: "NOISE",
|
|
4093
|
+
medianDelta: candidate.median - parent.median,
|
|
4094
|
+
reason: `candidate unstable (iqr=${candidate.iqr}, stddev=${candidate.stddev.toFixed(2)})`
|
|
4095
|
+
};
|
|
4096
|
+
const medianDelta = candidate.median - parent.median;
|
|
4097
|
+
if (medianDelta > t.keepThreshold) return {
|
|
4098
|
+
verdict: "KEEP",
|
|
4099
|
+
medianDelta,
|
|
4100
|
+
reason: `median +${medianDelta} > +${t.keepThreshold}`
|
|
4101
|
+
};
|
|
4102
|
+
if (medianDelta < -t.regressionThreshold) return {
|
|
4103
|
+
verdict: "REGRESSION",
|
|
4104
|
+
medianDelta,
|
|
4105
|
+
reason: `median ${medianDelta} < -${t.regressionThreshold}`
|
|
4106
|
+
};
|
|
4107
|
+
return {
|
|
4108
|
+
verdict: "NOISE",
|
|
4109
|
+
medianDelta,
|
|
4110
|
+
reason: `median delta ${medianDelta} inside noise band [-${t.regressionThreshold}, +${t.keepThreshold}]`
|
|
4111
|
+
};
|
|
4112
|
+
}
|
|
4113
|
+
//#endregion
|
|
2839
4114
|
//#region src/propose-review.ts
|
|
2840
4115
|
/**
|
|
2841
4116
|
* Propose / Verify / Review — the core multi-shot primitive.
|
|
@@ -6264,9 +7539,9 @@ function completeCostMedian(runs) {
|
|
|
6264
7539
|
if (runs.length === 0) return null;
|
|
6265
7540
|
const costs = [];
|
|
6266
7541
|
for (const run of runs) {
|
|
6267
|
-
const
|
|
6268
|
-
if (
|
|
6269
|
-
costs.push(
|
|
7542
|
+
const usd = run.costProvenance.usd;
|
|
7543
|
+
if (usd === null) return null;
|
|
7544
|
+
costs.push(usd);
|
|
6270
7545
|
}
|
|
6271
7546
|
return medianFinite(costs);
|
|
6272
7547
|
}
|
|
@@ -8311,6 +9586,6 @@ function rankRows(rows, weights) {
|
|
|
8311
9586
|
})).sort((a, b) => b.mean - a.mean);
|
|
8312
9587
|
}
|
|
8313
9588
|
//#endregion
|
|
8314
|
-
export { AGENT_PROFILE_KINDS, ATTESTATION_ALGORITHM, AgentEvalError, AnalystRegistry, BOOTSTRAP_GATE_MIN_N, BackendIntegrityError, BudgetBreachError, BudgetGuard, CODING_HARNESSES, ConfigError, CostAccountingIncompleteError, CostCallConflictError, CostCeilingReachedError, CostLedger, CostLedgerPersistenceError, CostReceiptCaptureError, CostReservationExceededError, CostTracker, CrossFamilyError, DECISION_PAIRED_DELTA_STATISTIC, DEFAULT_BOUNDED_PROCESS_TIMEOUT_MS, DEFAULT_FAILURE_REASON_RULES, DEFAULT_PERMUTATIONS,
|
|
9589
|
+
export { AGENT_PROFILE_KINDS, ATTESTATION_ALGORITHM, AgentEvalError, AnalystRegistry, BOOTSTRAP_GATE_MIN_N, BackendIntegrityError, BudgetBreachError, BudgetGuard, CODING_HARNESSES, ConfigError, CostAccountingIncompleteError, CostCallConflictError, CostCeilingReachedError, CostLedger, CostLedgerPersistenceError, CostReceiptCaptureError, CostReservationExceededError, CostTracker, CrossFamilyError, DECISION_PAIRED_DELTA_STATISTIC, DEFAULT_BOUNDED_PROCESS_TIMEOUT_MS, DEFAULT_FAILURE_REASON_RULES, DEFAULT_PERMUTATIONS, DEFAULT_RED_TEAM_CORPUS, DEFAULT_TRACE_ANALYST_BUDGETS, DEFAULT_TRACE_ANALYST_KINDS, DEFAULT_TRACE_ANALYST_OUTPUT_TOKENS, ERROR_COUNT_PATTERNS, EquivalenceProtocolError, FAILURE_BLAME, FAILURE_CLASSES, FAILURE_MODE_KIND_SPEC, FileSystemFeedbackTrajectoryStore, FileSystemRawProviderSink, FileSystemTraceStore, FindingsStore, HARNESS_NATIVE_MODEL, HeldOutGate, INFRA_FAILURE_BLAMES, InMemoryFeedbackTrajectoryStore, InMemoryRawProviderSink, InMemoryTraceStore, JudgeError, LOCAL_COMMAND_RUNNER_DEFAULT_CAP_MS, LlmCallError, LlmResponseError, MANN_WHITNEY_EXACT_MAX_STATES, MANN_WHITNEY_EXACT_MAX_WORK, MODEL_PRICING, ModelSubstitutionError, MultiLayerVerifier, NoopRawProviderSink, NotFoundError, OUTPUT_VALUE, OtlpFileTraceStore, POLICY_EDIT_AXES, POLICY_EDIT_CANDIDATE_RECORD_SCHEMA, POLICY_EDIT_TARGET_SURFACES, PairwiseSteeringOptimizer, PolicyEditValidationError, ProductClient, PromptRegistry, REDACTION_VERSION, RunIntegrityError, RunRecordValidationError, SEMANTIC_CONCEPT_JUDGE_VERSION, ServedCrossFamilyError, TRACE_ANALYST_TRUNCATION_MARKER_PREFIX, TraceEmitter, UNKNOWN_MODEL, VERIFICATION_STRATEGIES, VERIFICATION_STRATEGY_SOURCES, ValidationError, WILCOXON_EXACT_MAX_N, acquisitionPlansForKnowledgeGaps, admitPolicyEdit, agentProfileCellHashMaterial, agentProfileCellKey, agentProfileHash, agentProfileId, aggregateJudgeVerdicts, aggregateRunScore, analystFindingDigest, analystRunDigest, analystRunToFeedbackTrajectory, analystRunToReviewRequests, analyzeAntiSlop, analyzeRuns, analyzeSeries, analyzeTraces, applyPolicyEditToSurface, argHash, assertCapabilityHeadroom, assertCrossFamily, assertCrossFamilyServed, assertNoHiddenLeak, assertNoJudgeVerdict, assertProductBenchmarkRun, assertRealBackend, assertRunCaptured, assertServedModel, assertServedModels, assertSingleBackend, assessShareSafety, assignFeedbackSplit, attest, benjaminiHochberg, blendHeldout, blockingKnowledgeEval, bonferroni, bootstrapCi, budgetBreachView, buildAgentProfileCell, buildDefaultAnalystRegistry, buildEquivalenceRecord, buildReflectionPrompt, buildTraceInsightContext, buildTraceInsightPrompt, buildTrajectory, calibrateJudge, calibrateJudgeContinuous, canonicalJson, capabilityHeadroom, captureFetchToRawSink, certificationEvidenceDigest, checkCanaries, checkServedModel, checkTraceContracts, clamp01, classifyFailure, classifyFailureReason, cliffsDelta, cohensD, comparePairedArms, compileTraceContractSpec, completionVerdict, computeExperimentStats, computeFindingId, computePolicyEditId, computeToolUseMetrics, confidenceInterval, contentHash, continuousAgreement, controlRunToFeedbackTrajectory, corpusInterRaterAgreement, corpusInterRaterAgreementFromJudgeScores, costForTokenPricing, costForUsage, costReceiptFromLlm, costReceiptFromLlmError, createAntiSlopJudge, createBoundedTraceAnalysisStore, createChatClient, createChatTraceEngine, createDspyRlmTraceEngine, createFeedbackTrajectory, createLlmCorrectnessChecker, createLlmReviewer, createTraceAnalyst, decidePairedPromotion, defaultBlendWeights, defineAgentEval, defineEquivalenceCheck, deployGateLayer, describeTraceInsightScope, diffFindings, diffScorecard, discoverPersonas, domainEvidencePattern, dominates, eProcess, ensembleJudge, equivalenceVerdict, errorStreakDetector, estimateCost, estimateTokens, evaluateActionPolicy, evaluateInterimReleaseConfidence, evaluateOracles, evaluateReleaseConfidence, evaluateTraceContract, expandProfileAxes, explainTraceContract, exportProductBenchmark, exportProductBenchmarkRuns, exportRunAsOtlp, extractErrorCount, extractProducedState, extractUsage, extractUsageFromSse, failureBlame, failureClusterView, feedbackTrajectoriesToDatasetScenarios, feedbackTrajectoriesToOptimizerRows, feedbackTrajectoryToOptimizerRow, fileVerdictCache, formatScorecardDiff, gainHistogram, gateTreatmentApplied, gradeOnHidden, gradeSemanticStatus, groupRunsByAgentProfileCell, harnessAxisOf, hashContent, hashJson, hiddenGrade, holm, improvementVerdict, inMemoryReviewStore, inferDomainKeywords, interRaterReliability, interpretCliffs, iqr, isBinaryOutcomeVector, isJudgeSpan, isLlmSpan, isModelPriced, isPolicyEdit, isRunRecord, isToolSpan, isTransientLlmError, jsonShape, jsonlReviewStore, jsonlRunRecordBackend, judgeAgreementView, judgeFamily, judgeSpans, knowledgeReadinessTracePayload, leaderboard, lintTraceContractSpec, llmJudge, loadScorecard, localCommandRunner, makeFinding, makePolicyEdit, makePolicyEditCandidateRecord, makeProposalFinding, manifestContentDigest, mannWhitneyU, maximumChargeForLlmRequest, mcnemar, mcnemarPower, mcnemarRequiredN, minimumPairsForPairedDeltaTest, mintRolloutRows, modelHasSnapshot, modelPriceKey, mulberry32, notBlocked, objectiveEval, observeAll, otlpTextToTraceAnalysisStore, pairArms, pairRunRecords, pairedBinaryScale, pairedBootstrap, pairedCohensDz, pairedDeltaTest, pairedDeltaTieFraction, pairedEvalueSequence, pairedMde, pairedPromotionPower, pairedRiskDifference, pairedRiskDifferenceExact, pairedRiskDifferenceScore, pairedSignTest, pairedTTest, paretoChart, paretoFrontier, parseReflectionResponse, parseRunRecordSafe, partialCredit, partitionHeldOut, partitionTrainSelectionTest, passAtK, pearsonR, policyEditFromFinding, policyEditsFromFindings, preflightModels, productBenchmarkRepoIdentity, profile_exports as profile, projectRuntimeTrajectoryEvidence, proposeSynthesisTargets, quotaExhaustedUntil, ranks, readProductBenchmarkManifest, recordRuns, recordRunsToScorecard, redTeamDataset, redTeamReport, redact, redactForShare, redactText, regexMatches, renderPreferenceMemoryMarkdown, repeatedActionDetector, requiredPairedSampleSize, requiredPairsForPairedPromotion, requiredSampleSize, resolveModelPricing, resolveSeat, roundTripRunRecord, routeFields, runAgentControlLoop, runBoundedProcess, runCampaign, runCanaries, runCostFloorUsd, runCounterfactual, runEquivalenceCheck, runEvalCampaign, runIntentMatchJudge, runKeywordCoverageJudge, runKeywordCoverageJudgeUrl, runProposeReview, runProposeReviewAsControlLoop, runSemanticConceptJudge, runTaskScore, runsForScenario, scoreKnowledgeReadiness, scorePolicyEditReadiness, scoreRedTeamOutput, scoreTraceInsightReadiness, searchCellRunId, seatPresets, selfImprove, servedModelAcceptable, shareAllowed, spearmanR, stripFencedJson, subjectiveEval, summarizeBackendIntegrity, summarizeNumberSeries, summarizePreferenceMemory, summaryTable, textInSnapshot, toAgentProfileJson, tokenizeDomainWords, toolSpansToTraceAnalysisStore, toolWasteView, traceContract, transientDispatchFailure, urlContains, userQuestionsForKnowledgeGaps, validatePolicyEdit, validatePolicyEditCandidateRecord, validateRunRecord, verbosityBias, verifyAgentProfileCell, verifyAttestation, verifyCompletion, viteDeployRunner, weightedComposite, weightedMean, wilcoxonSignedRank, wilson, withAssignedFeedbackSplit, withHeldoutBlend, withJudgeRetry, wranglerDeployRunner };
|
|
8315
9590
|
|
|
8316
9591
|
//# sourceMappingURL=index.js.map
|