@tangle-network/agent-eval 0.215.1 → 0.216.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/dist/adapters/http.d.ts +3 -3
- package/dist/{agent-profile-DmzL8KaJ.d.ts → agent-profile-VFcQncbW.d.ts} +5 -5
- package/dist/{agent-profile-DmzL8KaJ.d.ts.map → agent-profile-VFcQncbW.d.ts.map} +1 -1
- package/dist/{agent-profile-cell-BYc0bxsL.js → agent-profile-cell-BK8mlqlq.js} +3 -3
- package/dist/{agent-profile-cell-BYc0bxsL.js.map → agent-profile-cell-BK8mlqlq.js.map} +1 -1
- package/dist/agreement-irr-BEWLIF3_.js +186 -0
- package/dist/agreement-irr-BEWLIF3_.js.map +1 -0
- package/dist/analyst/index.d.ts +487 -14
- package/dist/analyst/index.d.ts.map +1 -1
- package/dist/analyst/index.js +1547 -12
- package/dist/analyst/index.js.map +1 -1
- package/dist/{analyze-runs-3xpby3p2.js → analyze-runs-C84irm4i.js} +608 -604
- package/dist/analyze-runs-C84irm4i.js.map +1 -0
- package/dist/{analyze-runs-BhFLOztS.d.ts → analyze-runs-D68fd-su.d.ts} +17 -15
- package/dist/analyze-runs-D68fd-su.d.ts.map +1 -0
- package/dist/{baseline-Dm8966du.js → baseline-Udi_PLuT.js} +5 -5
- package/dist/{baseline-Dm8966du.js.map → baseline-Udi_PLuT.js.map} +1 -1
- package/dist/{benchmark-BywPeAUo.js → benchmark-CXmVVHH2.js} +2 -2
- package/dist/{benchmark-BywPeAUo.js.map → benchmark-CXmVVHH2.js.map} +1 -1
- package/dist/{benchmark-command-CgpGi7vY.js → benchmark-command-By0vh8q7.js} +71 -70
- package/dist/benchmark-command-By0vh8q7.js.map +1 -0
- package/dist/benchmarks/index.d.ts +5 -5
- package/dist/benchmarks/index.js +3 -3
- package/dist/builder-eval/index.js +2 -2
- package/dist/campaign/index.d.ts +7 -7
- package/dist/campaign/index.js +14 -14
- package/dist/{campaign-CpnsCgfz.js → campaign-BudZ70zo.js} +245 -247
- package/dist/campaign-BudZ70zo.js.map +1 -0
- package/dist/{campaign-evidence-OcUciD-N.js → campaign-evidence-Clrn4hBV.js} +8 -8
- package/dist/campaign-evidence-Clrn4hBV.js.map +1 -0
- package/dist/{canonical-BsZVgBhW.js → canonical-B2RqLofv.js} +2 -2
- package/dist/{canonical-BsZVgBhW.js.map → canonical-B2RqLofv.js.map} +1 -1
- package/dist/{chat-client-DthPOYGd.js → chat-client-l-MVDlDk.js} +2 -2
- package/dist/{chat-client-DthPOYGd.js.map → chat-client-l-MVDlDk.js.map} +1 -1
- package/dist/{chat-json-call-BrKsyspg.js → chat-json-call-puMArmtW.js} +2 -2
- package/dist/{chat-json-call-BrKsyspg.js.map → chat-json-call-puMArmtW.js.map} +1 -1
- package/dist/{claim-BXcZw-jo.js → claim-q4HQhK1G.js} +4 -4
- package/dist/claim-q4HQhK1G.js.map +1 -0
- package/dist/cli.js +9 -9
- package/dist/cli.js.map +1 -1
- package/dist/cluster-bootstrap-qcUWYFl-.js +66 -0
- package/dist/cluster-bootstrap-qcUWYFl-.js.map +1 -0
- package/dist/contract/index.d.ts +11 -11
- package/dist/contract/index.js +1023 -1079
- package/dist/contract/index.js.map +1 -1
- package/dist/{cost-ledger-D2FnJPBF.js → cost-ledger-BP8V4kUQ.js} +835 -816
- package/dist/cost-ledger-BP8V4kUQ.js.map +1 -0
- package/dist/{cost-ledger-CO7UyEcQ.d.ts → cost-ledger-Jk62OBfF.d.ts} +12 -47
- package/dist/cost-ledger-Jk62OBfF.d.ts.map +1 -0
- package/dist/{counterfactual-DtMWvn7D.js → counterfactual-Dn3veueY.js} +2 -2
- package/dist/{counterfactual-DtMWvn7D.js.map → counterfactual-Dn3veueY.js.map} +1 -1
- package/dist/{default-registry-KP4-Tolz.js → default-registry-COhk9g36.js} +744 -759
- package/dist/default-registry-COhk9g36.js.map +1 -0
- package/dist/{default-registry-B4B7GMvI.d.ts → default-registry-DzHiGruW.d.ts} +6 -6
- package/dist/{default-registry-B4B7GMvI.d.ts.map → default-registry-DzHiGruW.d.ts.map} +1 -1
- package/dist/{define-agent-eval-BdfnqXeP.js → define-agent-eval-CNTPzIkp.js} +10 -10
- package/dist/define-agent-eval-CNTPzIkp.js.map +1 -0
- package/dist/{define-agent-eval-DfXvNho5.d.ts → define-agent-eval-WSck0CZJ.d.ts} +8 -8
- package/dist/{define-agent-eval-DfXvNho5.d.ts.map → define-agent-eval-WSck0CZJ.d.ts.map} +1 -1
- package/dist/{descriptive-CnU8Yq29.js → descriptive-DboxPZrG.js} +2 -2
- package/dist/{descriptive-CnU8Yq29.js.map → descriptive-DboxPZrG.js.map} +1 -1
- package/dist/diagnosis/index.d.ts +3 -3
- package/dist/diagnosis/index.js +4 -4
- package/dist/{dspy-rlm-engine-DZfHM7kT.js → dspy-rlm-engine-LRCu61m5.js} +3 -3
- package/dist/{dspy-rlm-engine-DZfHM7kT.js.map → dspy-rlm-engine-LRCu61m5.js.map} +1 -1
- package/dist/{effect-sizes-DiH8MGOH.js → effect-sizes-BhzEpXMc.js} +2 -2
- package/dist/{effect-sizes-DiH8MGOH.js.map → effect-sizes-BhzEpXMc.js.map} +1 -1
- package/dist/{engine-C37HS2kr.d.ts → engine-3A_4jOlI.d.ts} +5 -5
- package/dist/{engine-C37HS2kr.d.ts.map → engine-3A_4jOlI.d.ts.map} +1 -1
- package/dist/{equal-terms-Blx9Zk5I.js → equal-terms-BtdXayCJ.js} +2 -2
- package/dist/{equal-terms-Blx9Zk5I.js.map → equal-terms-BtdXayCJ.js.map} +1 -1
- package/dist/{errors-Dngq5h35.js → errors-DVZiqS7K.js} +6 -2
- package/dist/{errors-Dngq5h35.js.map → errors-DVZiqS7K.js.map} +1 -1
- package/dist/{eval-campaign-4byTtqrf.js → eval-campaign-90jeaCAI.js} +6 -6
- package/dist/{eval-campaign-4byTtqrf.js.map → eval-campaign-90jeaCAI.js.map} +1 -1
- package/dist/evaluation-6Yoi74gM.d.ts +75 -0
- package/dist/evaluation-6Yoi74gM.d.ts.map +1 -0
- package/dist/evaluation.d.ts +2 -74
- package/dist/evaluation.js +1 -1
- package/dist/{exact-types-Dlb73wN4.d.ts → exact-types-ClugMJfI.d.ts} +2 -2
- package/dist/{exact-types-Dlb73wN4.d.ts.map → exact-types-ClugMJfI.d.ts.map} +1 -1
- package/dist/experiment/index.d.ts +404 -3
- package/dist/experiment/index.d.ts.map +1 -1
- package/dist/experiment/index.js +313 -19
- package/dist/experiment/index.js.map +1 -1
- package/dist/{external-optimizer-process-GngofQML.js → external-optimizer-process-BjMGUuds.js} +3 -3
- package/dist/{external-optimizer-process-GngofQML.js.map → external-optimizer-process-BjMGUuds.js.map} +1 -1
- package/dist/{external-optimizer-subprocess-BUxifb8g.js → external-optimizer-subprocess-BqW0hHcI.js} +791 -532
- package/dist/external-optimizer-subprocess-BqW0hHcI.js.map +1 -0
- package/dist/{feedback-trajectory-BLN_6fEs.d.ts → feedback-trajectory-C4EG0nNH.d.ts} +5 -8
- package/dist/feedback-trajectory-C4EG0nNH.d.ts.map +1 -0
- package/dist/fuzz.d.ts +1 -1
- package/dist/fuzz.js +3 -3
- package/dist/hosted/index.js +1 -1
- package/dist/{index-CPJJr98o.d.ts → index-BFRO6chs.d.ts} +3 -2
- package/dist/index-BFRO6chs.d.ts.map +1 -0
- package/dist/{index-Ch9VxCpK.d.ts → index-BO41eGj5.d.ts} +2 -2
- package/dist/{index-Ch9VxCpK.d.ts.map → index-BO41eGj5.d.ts.map} +1 -1
- package/dist/{index-B3WBm953.d.ts → index-CyszSg4O.d.ts} +165 -162
- package/dist/index-CyszSg4O.d.ts.map +1 -0
- package/dist/{index-B7m2uzJL.d.ts → index-DPcngtwt.d.ts} +30 -17
- package/dist/index-DPcngtwt.d.ts.map +1 -0
- package/dist/{index-tYLUvk_w.d.ts → index-DV-jSzxF.d.ts} +9 -9
- package/dist/{index-tYLUvk_w.d.ts.map → index-DV-jSzxF.d.ts.map} +1 -1
- package/dist/index.d.ts +1969 -1871
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +162 -201
- package/dist/index.js.map +1 -1
- package/dist/{integrity-BvnVARVl.js → integrity-BXOsAb3i.js} +2 -2
- package/dist/{integrity-BvnVARVl.js.map → integrity-BXOsAb3i.js.map} +1 -1
- package/dist/{integrity-CCAxGVK6.d.ts → integrity-DAMEDFaC.d.ts} +2 -2
- package/dist/{integrity-CCAxGVK6.d.ts.map → integrity-DAMEDFaC.d.ts.map} +1 -1
- package/dist/{integrity-BV1ZDf42.js → integrity-dLiDe2kD.js} +2 -5
- package/dist/integrity-dLiDe2kD.js.map +1 -0
- package/dist/{internal-CRHwS9iz.js → internal-Epl-YBZW.js} +3 -3
- package/dist/{internal-CRHwS9iz.js.map → internal-Epl-YBZW.js.map} +1 -1
- package/dist/interval-CELpye1n.js +219 -0
- package/dist/interval-CELpye1n.js.map +1 -0
- package/dist/interval-ohuOJurX.d.ts +47 -0
- package/dist/interval-ohuOJurX.d.ts.map +1 -0
- package/dist/{journal-DvKCvjty.js → journal-DfRz9kps.js} +2 -2
- package/dist/{journal-DvKCvjty.js.map → journal-DfRz9kps.js.map} +1 -1
- package/dist/{judge-calibration-N44V04Se.js → judge-calibration-BpCy5XQl.js} +3 -3
- package/dist/{judge-calibration-N44V04Se.js.map → judge-calibration-BpCy5XQl.js.map} +1 -1
- package/dist/{judge-gate-DNcUrRFA.d.ts → judge-gate-CE7CQAgt.d.ts} +3 -3
- package/dist/{judge-gate-DNcUrRFA.d.ts.map → judge-gate-CE7CQAgt.d.ts.map} +1 -1
- package/dist/{judge-gate-B1qYnkpi.js → judge-gate-iY9s4L44.js} +3 -3
- package/dist/judge-gate-iY9s4L44.js.map +1 -0
- package/dist/{kind-factory-AaEhUADH.js → kind-factory-BobI5Jy3.js} +5 -5
- package/dist/{kind-factory-AaEhUADH.js.map → kind-factory-BobI5Jy3.js.map} +1 -1
- package/dist/ledger-core/index.js +3 -3
- package/dist/{ledger-core-B2hahzrt.js → ledger-core-Gq7yp8lP.js} +4 -4
- package/dist/{ledger-core-B2hahzrt.js.map → ledger-core-Gq7yp8lP.js.map} +1 -1
- package/dist/{llm-client-DQveoxSM.js → llm-client-CyBAoYiX.js} +192 -219
- package/dist/llm-client-CyBAoYiX.js.map +1 -0
- package/dist/{llm-judge-Cd5s5Ei6.js → llm-judge-CG12SQJq.js} +499 -921
- package/dist/llm-judge-CG12SQJq.js.map +1 -0
- package/dist/matrix/index.d.ts +2 -2
- package/dist/matrix/index.js +1 -1
- package/dist/{matrix-DXVAJMLD.d.ts → matrix-DSrSxjjy.d.ts} +4 -4
- package/dist/{matrix-DXVAJMLD.d.ts.map → matrix-DSrSxjjy.d.ts.map} +1 -1
- package/dist/{matrix-_BV9-Lk5.js → matrix-Dm0BQW7U.js} +7 -11
- package/dist/matrix-Dm0BQW7U.js.map +1 -0
- package/dist/{paired-tests-jbwPdSz5.js → mean-DHoMiudD.js} +11 -5
- package/dist/mean-DHoMiudD.js.map +1 -0
- package/dist/meta-eval/index.d.ts +3 -3
- package/dist/meta-eval/index.js +9 -70
- package/dist/meta-eval/index.js.map +1 -1
- package/dist/mint-DNU8QPG4.d.ts +81 -0
- package/dist/mint-DNU8QPG4.d.ts.map +1 -0
- package/dist/{mint-Csor1BLJ.js → mint-QaD5cA0u.js} +3 -3
- package/dist/{mint-Csor1BLJ.js.map → mint-QaD5cA0u.js.map} +1 -1
- package/dist/{multiplicity-D9cjmStq.js → multiplicity-DQUFcbW4.js} +2 -2
- package/dist/{multiplicity-D9cjmStq.js.map → multiplicity-DQUFcbW4.js.map} +1 -1
- package/dist/multishot/golden/index.d.ts +1 -1
- package/dist/multishot/golden/index.d.ts.map +1 -1
- package/dist/multishot/golden/index.js +3 -6
- package/dist/multishot/golden/index.js.map +1 -1
- package/dist/multishot/index.d.ts +2 -2
- package/dist/multishot/index.js +1 -1
- package/dist/nonnegative-number-DvdMxg_f.js +9 -0
- package/dist/nonnegative-number-DvdMxg_f.js.map +1 -0
- package/dist/openapi.json +1 -1
- package/dist/{otel-spans-C8YIJWy1.js → otel-spans-B9tUXRHF.js} +3 -3
- package/dist/{otel-spans-C8YIJWy1.js.map → otel-spans-B9tUXRHF.js.map} +1 -1
- package/dist/{paired-arms-DzKn0TfY.js → paired-arms-CwMGJsrS.js} +5 -5
- package/dist/{paired-arms-DzKn0TfY.js.map → paired-arms-CwMGJsrS.js.map} +1 -1
- package/dist/{paired-binary-Q36bhn2R.js → paired-binary-CvpFHfKZ.js} +2 -2
- package/dist/{paired-binary-Q36bhn2R.js.map → paired-binary-CvpFHfKZ.js.map} +1 -1
- package/dist/{paired-promotion-decision-BN6IQTiu.js → paired-promotion-decision-DsAPYGCm.js} +3 -3
- package/dist/{paired-promotion-decision-BN6IQTiu.js.map → paired-promotion-decision-DsAPYGCm.js.map} +1 -1
- package/dist/{search-kernel-BztlERLv.js → pareto-Cm6ndrWN.js} +883 -861
- package/dist/pareto-Cm6ndrWN.js.map +1 -0
- package/dist/pipelines/index.d.ts +2 -2
- package/dist/pipelines/index.js +3 -3
- package/dist/{power-and-mde-fK5Qocwo.js → power-and-mde-Hj2ubb4B.js} +2 -2
- package/dist/{power-and-mde-fK5Qocwo.js.map → power-and-mde-Hj2ubb4B.js.map} +1 -1
- package/dist/predicate-FdKmElA_.js +38 -0
- package/dist/predicate-FdKmElA_.js.map +1 -0
- package/dist/{prime-protocol-DcAK9pC8.js → prime-protocol-CHKgA3a6.js} +2 -2
- package/dist/{prime-protocol-DcAK9pC8.js.map → prime-protocol-CHKgA3a6.js.map} +1 -1
- package/dist/{prime-protocol-BHia9vDz.d.ts → prime-protocol-HiB9HX7E.d.ts} +3 -3
- package/dist/{prime-protocol-BHia9vDz.d.ts.map → prime-protocol-HiB9HX7E.d.ts.map} +1 -1
- package/dist/{produced-state-BGp4BX8t.js → produced-state-BsDmnwxc.js} +8 -8
- package/dist/{produced-state-BGp4BX8t.js.map → produced-state-BsDmnwxc.js.map} +1 -1
- package/dist/profile-cell.js +1 -1
- package/dist/{release-confidence-BK2QCB5M.d.ts → promotion-gate-BawrdPYr.d.ts} +102 -102
- package/dist/promotion-gate-BawrdPYr.d.ts.map +1 -0
- package/dist/{promotion-policy-CWW35UWq.js → promotion-policy-KO57gbyV.js} +4 -4
- package/dist/{promotion-policy-CWW35UWq.js.map → promotion-policy-KO57gbyV.js.map} +1 -1
- package/dist/{proposal-findings-CXblsskH.js → proposal-findings-CY1xKn_a.js} +2 -36
- package/dist/proposal-findings-CY1xKn_a.js.map +1 -0
- package/dist/{query-B1D1qp2Z.js → query-BUCZCK7F.js} +3 -3
- package/dist/{query-B1D1qp2Z.js.map → query-BUCZCK7F.js.map} +1 -1
- package/dist/{random-Dn5fPWkt.js → random-ByUSnV9w.js} +2 -2
- package/dist/{random-Dn5fPWkt.js.map → random-ByUSnV9w.js.map} +1 -1
- package/dist/{raw-provider-sink-rB2DYxr6.js → raw-provider-sink-DoHz4JNG.js} +2 -2
- package/dist/{raw-provider-sink-rB2DYxr6.js.map → raw-provider-sink-DoHz4JNG.js.map} +1 -1
- package/dist/{redact-Dk1ZdJEm.js → redact-B5xXKqQ3.js} +97 -98
- package/dist/redact-B5xXKqQ3.js.map +1 -0
- package/dist/{redact-Cveoq6fJ.d.ts → redact-mtCCcT8z.d.ts} +4 -20
- package/dist/redact-mtCCcT8z.d.ts.map +1 -0
- package/dist/{registry-CMYIaDgS.d.ts → registry-D7_hm2fK.d.ts} +11 -13
- package/dist/registry-D7_hm2fK.d.ts.map +1 -0
- package/dist/{release-confidence-Dbe9AS_X.js → release-confidence-CpuP65S5.js} +5 -5
- package/dist/{release-confidence-Dbe9AS_X.js.map → release-confidence-CpuP65S5.js.map} +1 -1
- package/dist/{report-command-CtIIsQKP.js → report-command-SC9nqb4M.js} +128 -177
- package/dist/report-command-SC9nqb4M.js.map +1 -0
- package/dist/reporting-html-Kvlc8oMt.js +100 -0
- package/dist/reporting-html-Kvlc8oMt.js.map +1 -0
- package/dist/reporting.d.ts +3 -3
- package/dist/reporting.js +6 -6
- package/dist/{researcher-f6mZhh58.d.ts → researcher-CEcfku-T.d.ts} +5 -5
- package/dist/{researcher-f6mZhh58.d.ts.map → researcher-CEcfku-T.d.ts.map} +1 -1
- package/dist/reward-CpCmAM8L.d.ts +78 -0
- package/dist/reward-CpCmAM8L.d.ts.map +1 -0
- package/dist/{reward-hacking-BFcQgkux.js → reward-hacking-E0d-WVcP.js} +2 -2
- package/dist/{reward-hacking-BFcQgkux.js.map → reward-hacking-E0d-WVcP.js.map} +1 -1
- package/dist/{reward-hacking-DHA7c1l8.d.ts → reward-hacking-RVdZ6Kkt.d.ts} +2 -2
- package/dist/{reward-hacking-DHA7c1l8.d.ts.map → reward-hacking-RVdZ6Kkt.d.ts.map} +1 -1
- package/dist/rl.d.ts +8 -8
- package/dist/rl.js +14 -14
- package/dist/rollout/index.d.ts +3 -2
- package/dist/rollout/index.js +2 -2
- package/dist/{rollout-D9bKKBnZ.js → rollout-DZV1t2Wg.js} +2 -2
- package/dist/{rollout-D9bKKBnZ.js.map → rollout-DZV1t2Wg.js.map} +1 -1
- package/dist/round-decimal-COrKglMh.js +10 -0
- package/dist/round-decimal-COrKglMh.js.map +1 -0
- package/dist/{rubric-predictive-validity-2SgHxoa5.d.ts → rubric-predictive-validity-C03EC7ZE.d.ts} +2 -2
- package/dist/{rubric-predictive-validity-2SgHxoa5.d.ts.map → rubric-predictive-validity-C03EC7ZE.d.ts.map} +1 -1
- package/dist/{rubric-predictive-validity-BsgkBfT0.js → rubric-predictive-validity-DaFW70y9.js} +3 -3
- package/dist/{rubric-predictive-validity-BsgkBfT0.js.map → rubric-predictive-validity-DaFW70y9.js.map} +1 -1
- package/dist/{run-record-DlTZ_lqe.js → run-record-Boc5IUTp.js} +4 -4
- package/dist/{run-record-DlTZ_lqe.js.map → run-record-Boc5IUTp.js.map} +1 -1
- package/dist/{run-record-D1Hw-Jml.d.ts → run-record-C1E24G2A.d.ts} +2 -2
- package/dist/{run-record-D1Hw-Jml.d.ts.map → run-record-C1E24G2A.d.ts.map} +1 -1
- package/dist/{run-record-ES07wfz9.js → run-record-C5e7FjhJ.js} +3 -5
- package/dist/run-record-C5e7FjhJ.js.map +1 -0
- package/dist/search/index.d.ts +46 -32
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +3 -3
- package/dist/{search-kernel-BqyBZ9Wm.d.ts → search-kernel-DyNEa6IU.d.ts} +14 -10
- package/dist/search-kernel-DyNEa6IU.d.ts.map +1 -0
- package/dist/{search-ledger-d7V8o9Lk.js → search-ledger-CVUGLDZp.js} +189 -199
- package/dist/search-ledger-CVUGLDZp.js.map +1 -0
- package/dist/{search-ledger-file-BqOo-bVo.js → search-ledger-file-DDQUgvCg.js} +4 -4
- package/dist/{search-ledger-file-BqOo-bVo.js.map → search-ledger-file-DDQUgvCg.js.map} +1 -1
- package/dist/search-ledger-types-p2xKfiDJ.d.ts.map +1 -1
- package/dist/{search-shipper-er4pK5_Y.js → search-shipper-BOSD46qv.js} +3 -3
- package/dist/{search-shipper-er4pK5_Y.js.map → search-shipper-BOSD46qv.js.map} +1 -1
- package/dist/{search-summary-CBvz3u_S.js → search-summary-BQCSWf1g.js} +5 -5
- package/dist/{search-summary-CBvz3u_S.js.map → search-summary-BQCSWf1g.js.map} +1 -1
- package/dist/{semantic-concept-judge-BPqbwIVs.js → semantic-concept-judge-BQF8te0T.js} +12 -14
- package/dist/semantic-concept-judge-BQF8te0T.js.map +1 -0
- package/dist/{sequential-D-1PPCmM.js → sequential-Bf_pbE50.js} +5 -5
- package/dist/{sequential-D-1PPCmM.js.map → sequential-Bf_pbE50.js.map} +1 -1
- package/dist/{sequential-eprocess-D1jKoihe.js → sequential-eprocess-DM_qENCl.js} +2 -2
- package/dist/{sequential-eprocess-D1jKoihe.js.map → sequential-eprocess-DM_qENCl.js.map} +1 -1
- package/dist/{server-DbcC7xMl.js → server-e9oWO_bN.js} +4 -4
- package/dist/{server-DbcC7xMl.js.map → server-e9oWO_bN.js.map} +1 -1
- package/dist/{single-run-lock-fVu3MZWH.js → single-run-lock-CV9Eyd_q.js} +2 -2
- package/dist/{single-run-lock-fVu3MZWH.js.map → single-run-lock-CV9Eyd_q.js.map} +1 -1
- package/dist/{skillopt-optimization-method-Dcepr6QL.js → skillopt-optimization-method-BiwA8nfX.js} +427 -12
- package/dist/skillopt-optimization-method-BiwA8nfX.js.map +1 -0
- package/dist/{spans-D9_ehFuk.d.ts → spans-BHAUXMGN.d.ts} +2 -2
- package/dist/{spans-D9_ehFuk.d.ts.map → spans-BHAUXMGN.d.ts.map} +1 -1
- package/dist/{statistical-heldout-1Gbmex7v.js → statistical-heldout-42NlCNDe.js} +3 -3
- package/dist/{statistical-heldout-1Gbmex7v.js.map → statistical-heldout-42NlCNDe.js.map} +1 -1
- package/dist/{statistical-heldout-CJTj2EoS.d.ts → statistical-heldout-CYpGaoDH.d.ts} +3 -3
- package/dist/{statistical-heldout-CJTj2EoS.d.ts.map → statistical-heldout-CYpGaoDH.d.ts.map} +1 -1
- package/dist/{store-otlp-DWVhUgEj.js → store-otlp-DJ9dtA5x.js} +135 -132
- package/dist/store-otlp-DJ9dtA5x.js.map +1 -0
- package/dist/{store-tool-spans-BNICrhsb.js → store-tool-spans-CIX5arAG.js} +7 -7
- package/dist/{store-tool-spans-BNICrhsb.js.map → store-tool-spans-CIX5arAG.js.map} +1 -1
- package/dist/{store-tool-spans-BNzls3OD.d.ts → store-tool-spans-DdCSPa7C.d.ts} +162 -161
- package/dist/store-tool-spans-DdCSPa7C.d.ts.map +1 -0
- package/dist/storyboard/index.d.ts.map +1 -1
- package/dist/storyboard/index.js +20 -3
- package/dist/storyboard/index.js.map +1 -1
- package/dist/{student-t-BQeKJx2N.js → student-t-DVPW7CdL.js} +2 -2
- package/dist/{student-t-BQeKJx2N.js.map → student-t-DVPW7CdL.js.map} +1 -1
- package/dist/{summary-report-DtdRucM0.js → summary-report-V_JACR4Y.js} +526 -614
- package/dist/summary-report-V_JACR4Y.js.map +1 -0
- package/dist/{summary-report-C-CtItCN.d.ts → summary-report-bdFDXq0b.d.ts} +10 -5
- package/dist/summary-report-bdFDXq0b.d.ts.map +1 -0
- package/dist/supervisor-run/index.d.ts +154 -150
- package/dist/supervisor-run/index.d.ts.map +1 -1
- package/dist/supervisor-run/index.js +65 -3
- package/dist/supervisor-run/index.js.map +1 -1
- package/dist/systemone-BomhHr2c.d.ts +135 -0
- package/dist/systemone-BomhHr2c.d.ts.map +1 -0
- package/dist/systemone-protocol.js +1 -1
- package/dist/{systemone-review-C4dZgrgN.d.ts → systemone-review-CUVcQJu6.d.ts} +2 -2
- package/dist/{systemone-review-C4dZgrgN.d.ts.map → systemone-review-CUVcQJu6.d.ts.map} +1 -1
- package/dist/{systemone-review-DTClwgmE.js → systemone-review-D7sXwsqq.js} +3 -3
- package/dist/{systemone-review-DTClwgmE.js.map → systemone-review-D7sXwsqq.js.map} +1 -1
- package/dist/systemone.d.ts +4 -134
- package/dist/systemone.js +4 -4
- package/dist/{task-failure-attributes-DngaLdE4.js → task-failure-attributes-hEmASjix.js} +2 -2
- package/dist/{task-failure-attributes-DngaLdE4.js.map → task-failure-attributes-hEmASjix.js.map} +1 -1
- package/dist/{terminal-record-BHocwFw1.js → terminal-record-DZyQZ3pz.js} +14 -10
- package/dist/terminal-record-DZyQZ3pz.js.map +1 -0
- package/dist/{tool-groups-BKRrtqy4.d.ts → tool-groups-C3NEcdCM.d.ts} +3 -3
- package/dist/tool-groups-C3NEcdCM.d.ts.map +1 -0
- package/dist/{tool-waste-Wi4g6Ekb.d.ts → tool-waste-BKgvd-TP.d.ts} +30 -30
- package/dist/tool-waste-BKgvd-TP.d.ts.map +1 -0
- package/dist/{tool-waste-BYt5rdt8.js → tool-waste-BlesqTqW.js} +5 -158
- package/dist/tool-waste-BlesqTqW.js.map +1 -0
- package/dist/{trace-questions-CLx8vt6t.d.ts → trace-questions-CM2CAocU.d.ts} +4 -4
- package/dist/{trace-questions-CLx8vt6t.d.ts.map → trace-questions-CM2CAocU.d.ts.map} +1 -1
- package/dist/trace-repair/index.d.ts +3 -3
- package/dist/trace-repair/index.d.ts.map +1 -1
- package/dist/trace-repair/index.js +7 -13
- package/dist/trace-repair/index.js.map +1 -1
- package/dist/traces.d.ts +76 -58
- package/dist/traces.d.ts.map +1 -1
- package/dist/traces.js +9 -9
- package/dist/trajectory-replay/index.js +3 -3
- package/dist/{tree-rXLW3M_4.js → tree-C1XYeXvy.js} +2277 -2339
- package/dist/tree-C1XYeXvy.js.map +1 -0
- package/dist/{types-DjOMwGY_.d.ts → types-BqkVmHfv.d.ts} +7 -1
- package/dist/types-BqkVmHfv.d.ts.map +1 -0
- package/dist/{types-DBZ7keha.d.ts → types-C02qtnG8.d.ts} +4 -46
- package/dist/types-C02qtnG8.d.ts.map +1 -0
- package/dist/{types-DHb5TI36.d.ts → types-C1tnkEgM.d.ts} +347 -6
- package/dist/types-C1tnkEgM.d.ts.map +1 -0
- package/dist/{types-DinHtRJp.d.ts → types-Cbe2fWBa.d.ts} +3 -3
- package/dist/{types-DinHtRJp.d.ts.map → types-Cbe2fWBa.d.ts.map} +1 -1
- package/dist/{types-CGEmVYOk.d.ts → types-Cq8mSbq8.d.ts} +78 -72
- package/dist/types-Cq8mSbq8.d.ts.map +1 -0
- package/dist/{types-CW5b90JH.js → types-IXD2HJzu.js} +16 -3
- package/dist/types-IXD2HJzu.js.map +1 -0
- package/dist/{verdict-DhPoXApk.js → verdict-Dl0tiT1X.js} +2 -2
- package/dist/{verdict-DhPoXApk.js.map → verdict-Dl0tiT1X.js.map} +1 -1
- package/dist/{verdict-cache-BQEsVy9n.js → verdict-cache-Bm8EKL2s.js} +2 -2
- package/dist/{verdict-cache-BQEsVy9n.js.map → verdict-cache-Bm8EKL2s.js.map} +1 -1
- package/dist/wire/index.d.ts +3 -3
- package/dist/wire/index.js +1 -1
- package/docs/concepts.md +1 -0
- package/docs/design/mlbenchmarks-book-review.md +1 -1
- package/docs/design/mlbenchmarks-review/probes.mts +23 -31
- package/docs/experiment.md +1 -1
- package/docs/finding-layers.md +69 -0
- package/package.json +1 -1
- package/dist/analyze-runs-3xpby3p2.js.map +0 -1
- package/dist/analyze-runs-BhFLOztS.d.ts.map +0 -1
- package/dist/ast-DJ6Bc7ab.js +0 -558
- package/dist/ast-DJ6Bc7ab.js.map +0 -1
- package/dist/ast-hI-vjW6J.d.ts +0 -457
- package/dist/ast-hI-vjW6J.d.ts.map +0 -1
- package/dist/benchmark-command-CgpGi7vY.js.map +0 -1
- package/dist/campaign-CpnsCgfz.js.map +0 -1
- package/dist/campaign-evidence-OcUciD-N.js.map +0 -1
- package/dist/claim-BXcZw-jo.js.map +0 -1
- package/dist/cost-ledger-CO7UyEcQ.d.ts.map +0 -1
- package/dist/cost-ledger-D2FnJPBF.js.map +0 -1
- package/dist/default-registry-KP4-Tolz.js.map +0 -1
- package/dist/define-agent-eval-BdfnqXeP.js.map +0 -1
- package/dist/evaluation.d.ts.map +0 -1
- package/dist/external-optimizer-subprocess-BUxifb8g.js.map +0 -1
- package/dist/feedback-trajectory-BLN_6fEs.d.ts.map +0 -1
- package/dist/index-B3WBm953.d.ts.map +0 -1
- package/dist/index-B7m2uzJL.d.ts.map +0 -1
- package/dist/index-CPJJr98o.d.ts.map +0 -1
- package/dist/integrity-BV1ZDf42.js.map +0 -1
- package/dist/judge-gate-B1qYnkpi.js.map +0 -1
- package/dist/llm-client-DQveoxSM.js.map +0 -1
- package/dist/llm-judge-Cd5s5Ei6.js.map +0 -1
- package/dist/matrix-_BV9-Lk5.js.map +0 -1
- package/dist/mint-DCGoBYhB.d.ts +0 -155
- package/dist/mint-DCGoBYhB.d.ts.map +0 -1
- package/dist/paired-tests-jbwPdSz5.js.map +0 -1
- package/dist/proposal-findings-CXblsskH.js.map +0 -1
- package/dist/redact-Cveoq6fJ.d.ts.map +0 -1
- package/dist/redact-Dk1ZdJEm.js.map +0 -1
- package/dist/registry-CMYIaDgS.d.ts.map +0 -1
- package/dist/release-confidence-BK2QCB5M.d.ts.map +0 -1
- package/dist/report-command-CtIIsQKP.js.map +0 -1
- package/dist/run-record-ES07wfz9.js.map +0 -1
- package/dist/search-kernel-BqyBZ9Wm.d.ts.map +0 -1
- package/dist/search-kernel-BztlERLv.js.map +0 -1
- package/dist/search-ledger-d7V8o9Lk.js.map +0 -1
- package/dist/semantic-concept-judge-BPqbwIVs.js.map +0 -1
- package/dist/skillopt-optimization-method-Dcepr6QL.js.map +0 -1
- package/dist/store-otlp-DWVhUgEj.js.map +0 -1
- package/dist/store-tool-spans-BNzls3OD.d.ts.map +0 -1
- package/dist/summary-report-C-CtItCN.d.ts.map +0 -1
- package/dist/summary-report-DtdRucM0.js.map +0 -1
- package/dist/systemone.d.ts.map +0 -1
- package/dist/terminal-record-BHocwFw1.js.map +0 -1
- package/dist/tool-groups-BKRrtqy4.d.ts.map +0 -1
- package/dist/tool-waste-BYt5rdt8.js.map +0 -1
- package/dist/tool-waste-Wi4g6Ekb.d.ts.map +0 -1
- package/dist/tree-rXLW3M_4.js.map +0 -1
- package/dist/types-CGEmVYOk.d.ts.map +0 -1
- package/dist/types-CW5b90JH.js.map +0 -1
- package/dist/types-DBZ7keha.d.ts.map +0 -1
- package/dist/types-DHb5TI36.d.ts.map +0 -1
- package/dist/types-DjOMwGY_.d.ts.map +0 -1
|
@@ -1,835 +1,882 @@
|
|
|
1
|
-
import { s as ValidationError } from "./errors-
|
|
1
|
+
import { s as ValidationError } from "./errors-DVZiqS7K.js";
|
|
2
2
|
import { a as resolveModelPricing, i as isModelPriced, n as estimateCost } from "./metrics-BKaPBtIG.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
//#region src/cost-ledger.ts
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
//#region src/cost-ledger/validation.ts
|
|
5
|
+
/** Validate cost and usage primitives without admitting work or consulting pricing. */
|
|
6
|
+
function toError(value) {
|
|
7
|
+
return value instanceof Error ? value : new Error(String(value));
|
|
8
|
+
}
|
|
9
|
+
function validationMessage(cause) {
|
|
10
|
+
return cause instanceof z.ZodError ? z.prettifyError(cause) : toError(cause).message;
|
|
11
|
+
}
|
|
12
|
+
function assertUsage(usage) {
|
|
13
|
+
assertTokenCount(usage.inputTokens, "inputTokens");
|
|
14
|
+
assertTokenCount(usage.outputTokens, "outputTokens");
|
|
15
|
+
if (usage.reasoningTokens !== void 0) {
|
|
16
|
+
assertTokenCount(usage.reasoningTokens, "reasoningTokens");
|
|
17
|
+
if (usage.reasoningTokens > usage.outputTokens) throw new ValidationError("CostLedger: reasoningTokens must not exceed outputTokens");
|
|
8
18
|
}
|
|
19
|
+
if (usage.cachedTokens !== void 0) assertTokenCount(usage.cachedTokens, "cachedTokens");
|
|
20
|
+
if (usage.cacheWriteTokens !== void 0) assertTokenCount(usage.cacheWriteTokens, "cacheWriteTokens");
|
|
21
|
+
}
|
|
22
|
+
function assertTokenCount(value, name) {
|
|
23
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) throw new ValidationError(`CostLedger: ${name} must be a non-negative integer, got ${String(value)}`);
|
|
24
|
+
}
|
|
25
|
+
function assertNonNegative(value, name) {
|
|
26
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) throw new ValidationError(`CostLedger: ${name} must be a non-negative finite number, got ${String(value)}`);
|
|
27
|
+
}
|
|
28
|
+
function assertTimeout(value, name) {
|
|
29
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0 || value > 2147483647) throw new ValidationError(`CostLedger: ${name} must be a non-negative safe integer no greater than 2147483647, got ${String(value)}`);
|
|
30
|
+
}
|
|
31
|
+
function assertString(value, name) {
|
|
32
|
+
if (typeof value !== "string" || value.trim().length === 0) throw new ValidationError(`CostLedger: ${name} must be a non-empty string`);
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/cost-ledger/pricing.ts
|
|
36
|
+
/** Model-price lookup and caller-supplied token pricing, including receipt snapshots. */
|
|
37
|
+
/** Return the canonical pricing-table key, or null when the model is unpriced. */
|
|
38
|
+
function modelPriceKey(model) {
|
|
39
|
+
return isModelPriced(model) ? model : null;
|
|
40
|
+
}
|
|
41
|
+
function costForUsage(model, usage) {
|
|
42
|
+
assertUsage(usage);
|
|
43
|
+
if (!resolveModelPricing(model)) return {
|
|
44
|
+
costUsd: 0,
|
|
45
|
+
costUnknown: true
|
|
46
|
+
};
|
|
47
|
+
return {
|
|
48
|
+
costUsd: estimateCost(usage.inputTokens + (usage.cachedTokens ?? 0) + (usage.cacheWriteTokens ?? 0), usage.outputTokens, model),
|
|
49
|
+
costUnknown: false
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/** Price token counts with caller-supplied per-million rates. */
|
|
53
|
+
function costForTokenPricing(pricing, usage) {
|
|
54
|
+
assertTokenCount(usage.inputTokens, "usage.inputTokens");
|
|
55
|
+
assertTokenCount(usage.outputTokens, "usage.outputTokens");
|
|
56
|
+
if (usage.cachedTokens !== void 0) assertTokenCount(usage.cachedTokens, "usage.cachedTokens");
|
|
57
|
+
if (usage.cacheWriteTokens !== void 0) assertTokenCount(usage.cacheWriteTokens, "usage.cacheWriteTokens");
|
|
58
|
+
assertNonNegative(pricing.inputUsdPerMillion, "pricing.inputUsdPerMillion");
|
|
59
|
+
if (pricing.cachedInputUsdPerMillion !== void 0) assertNonNegative(pricing.cachedInputUsdPerMillion, "pricing.cachedInputUsdPerMillion");
|
|
60
|
+
if (pricing.cacheWriteUsdPerMillion !== void 0) assertNonNegative(pricing.cacheWriteUsdPerMillion, "pricing.cacheWriteUsdPerMillion");
|
|
61
|
+
assertNonNegative(pricing.outputUsdPerMillion, "pricing.outputUsdPerMillion");
|
|
62
|
+
const cachedInputRate = pricing.cachedInputUsdPerMillion ?? pricing.inputUsdPerMillion;
|
|
63
|
+
const cacheWriteRate = pricing.cacheWriteUsdPerMillion ?? pricing.inputUsdPerMillion;
|
|
64
|
+
return usage.inputTokens / 1e6 * pricing.inputUsdPerMillion + (usage.cachedTokens ?? 0) / 1e6 * cachedInputRate + (usage.cacheWriteTokens ?? 0) / 1e6 * cacheWriteRate + usage.outputTokens / 1e6 * pricing.outputUsdPerMillion;
|
|
65
|
+
}
|
|
66
|
+
function costFromPricing(usage, pricing) {
|
|
67
|
+
const cachedInputRate = pricing.cachedInputUsdPerThousand ?? pricing.inputUsdPerThousand;
|
|
68
|
+
const cacheWriteRate = pricing.cacheWriteUsdPerThousand ?? pricing.inputUsdPerThousand;
|
|
69
|
+
return usage.inputTokens / 1e3 * pricing.inputUsdPerThousand + (usage.cachedTokens ?? 0) / 1e3 * cachedInputRate + (usage.cacheWriteTokens ?? 0) / 1e3 * cacheWriteRate + usage.outputTokens / 1e3 * pricing.outputUsdPerThousand;
|
|
70
|
+
}
|
|
71
|
+
function pricingSnapshot(pricing) {
|
|
72
|
+
costForTokenPricing(pricing, {
|
|
73
|
+
inputTokens: 0,
|
|
74
|
+
outputTokens: 0
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
inputUsdPerThousand: pricing.inputUsdPerMillion / 1e3,
|
|
78
|
+
...pricing.cachedInputUsdPerMillion === void 0 ? {} : { cachedInputUsdPerThousand: pricing.cachedInputUsdPerMillion / 1e3 },
|
|
79
|
+
...pricing.cacheWriteUsdPerMillion === void 0 ? {} : { cacheWriteUsdPerThousand: pricing.cacheWriteUsdPerMillion / 1e3 },
|
|
80
|
+
outputUsdPerThousand: pricing.outputUsdPerMillion / 1e3
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/cost-ledger/receipts.ts
|
|
85
|
+
/** Create, validate, filter, and detach the receipts stored by a cost ledger. */
|
|
86
|
+
const NonEmptyString = z.string().refine((value) => value.trim().length > 0, "must be non-empty");
|
|
87
|
+
const TokenCount = z.number().int().nonnegative().finite();
|
|
88
|
+
const NonNegative = z.number().nonnegative().finite();
|
|
89
|
+
const Tags = z.record(NonEmptyString, z.string());
|
|
90
|
+
const CostPricingSchema = z.strictObject({
|
|
91
|
+
inputUsdPerThousand: NonNegative,
|
|
92
|
+
cachedInputUsdPerThousand: NonNegative.optional(),
|
|
93
|
+
cacheWriteUsdPerThousand: NonNegative.optional(),
|
|
94
|
+
outputUsdPerThousand: NonNegative
|
|
95
|
+
});
|
|
96
|
+
const CostCallBaseShape = {
|
|
97
|
+
callId: NonEmptyString,
|
|
98
|
+
channel: NonEmptyString,
|
|
99
|
+
phase: NonEmptyString,
|
|
100
|
+
actor: NonEmptyString,
|
|
101
|
+
model: NonEmptyString,
|
|
102
|
+
maximumCostUsd: NonNegative.optional(),
|
|
103
|
+
tags: Tags.optional(),
|
|
104
|
+
timestamp: NonNegative
|
|
9
105
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
106
|
+
const PendingCostCallSchema = z.strictObject({
|
|
107
|
+
status: z.literal("pending"),
|
|
108
|
+
...CostCallBaseShape
|
|
109
|
+
});
|
|
110
|
+
const CostReceiptBaseShape = {
|
|
111
|
+
status: z.literal("settled"),
|
|
112
|
+
...CostCallBaseShape,
|
|
113
|
+
inputTokens: TokenCount,
|
|
114
|
+
outputTokens: TokenCount,
|
|
115
|
+
cachedTokens: TokenCount.optional(),
|
|
116
|
+
costUsd: NonNegative,
|
|
117
|
+
costUnknown: z.boolean(),
|
|
118
|
+
usageUnknown: z.boolean().default(false),
|
|
119
|
+
pricing: CostPricingSchema.optional(),
|
|
120
|
+
actualCostUsd: NonNegative.optional(),
|
|
121
|
+
estimatedCostUsd: NonNegative.optional(),
|
|
122
|
+
error: NonEmptyString.optional()
|
|
15
123
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
124
|
+
const CostReceiptSchema = z.strictObject({
|
|
125
|
+
...CostReceiptBaseShape,
|
|
126
|
+
reasoningTokens: TokenCount.optional(),
|
|
127
|
+
cacheWriteTokens: TokenCount.optional()
|
|
128
|
+
}).superRefine((receipt, ctx) => validateCostReceipt(receipt, ctx));
|
|
129
|
+
function validateCostReceipt(receipt, ctx) {
|
|
130
|
+
if (receipt.reasoningTokens !== void 0 && receipt.reasoningTokens > receipt.outputTokens) ctx.addIssue({
|
|
131
|
+
code: "custom",
|
|
132
|
+
message: "reasoningTokens must not exceed outputTokens"
|
|
133
|
+
});
|
|
134
|
+
if (receipt.actualCostUsd !== void 0 && receipt.estimatedCostUsd !== void 0) ctx.addIssue({
|
|
135
|
+
code: "custom",
|
|
136
|
+
message: "actual and external estimated costs cannot both be present"
|
|
137
|
+
});
|
|
138
|
+
if (receipt.actualCostUsd !== void 0) {
|
|
139
|
+
if (receipt.costUnknown || receipt.costUsd !== receipt.actualCostUsd) ctx.addIssue({
|
|
140
|
+
code: "custom",
|
|
141
|
+
message: "actual cost must be known and equal costUsd"
|
|
142
|
+
});
|
|
143
|
+
if (receipt.pricing !== void 0) ctx.addIssue({
|
|
144
|
+
code: "custom",
|
|
145
|
+
message: "actual cost must not include estimated pricing"
|
|
146
|
+
});
|
|
147
|
+
return;
|
|
23
148
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
149
|
+
if (receipt.estimatedCostUsd !== void 0) {
|
|
150
|
+
if (receipt.costUnknown || receipt.costUsd !== receipt.estimatedCostUsd) ctx.addIssue({
|
|
151
|
+
code: "custom",
|
|
152
|
+
message: "external estimate must be known and equal costUsd"
|
|
153
|
+
});
|
|
154
|
+
if (receipt.pricing !== void 0) ctx.addIssue({
|
|
155
|
+
code: "custom",
|
|
156
|
+
message: "external estimate must not include token pricing"
|
|
157
|
+
});
|
|
158
|
+
return;
|
|
32
159
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
160
|
+
if (receipt.costUnknown) {
|
|
161
|
+
if (receipt.costUsd !== 0) ctx.addIssue({
|
|
162
|
+
code: "custom",
|
|
163
|
+
message: "unknown cost must have costUsd 0"
|
|
164
|
+
});
|
|
165
|
+
if (receipt.pricing !== void 0) ctx.addIssue({
|
|
166
|
+
code: "custom",
|
|
167
|
+
message: "unknown cost must not include estimated pricing"
|
|
168
|
+
});
|
|
169
|
+
return;
|
|
43
170
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
costCeilingUsd;
|
|
55
|
-
persistence;
|
|
56
|
-
constructor(input) {
|
|
57
|
-
const options = typeof input === "number" ? { costCeilingUsd: input } : input ?? {};
|
|
58
|
-
this.persistence = options.persistence;
|
|
59
|
-
if (options.costCeilingUsd !== void 0) assertNonNegative(options.costCeilingUsd, "costCeilingUsd");
|
|
60
|
-
let persistedCostCeilingUsd;
|
|
61
|
-
if (this.persistence) {
|
|
62
|
-
let stored;
|
|
63
|
-
try {
|
|
64
|
-
stored = this.persistence.read();
|
|
65
|
-
} catch (cause) {
|
|
66
|
-
throw new CostLedgerPersistenceError(cause);
|
|
67
|
-
}
|
|
68
|
-
assertString(stored.revision, "persistence revision");
|
|
69
|
-
this.revision = stored.revision;
|
|
70
|
-
const restored = parseEvents(stored.events);
|
|
71
|
-
for (const record of restored.records) this.records.set(record.callId, record);
|
|
72
|
-
this.completedTasks = restored.completedTasks;
|
|
73
|
-
persistedCostCeilingUsd = restored.costCeilingUsd;
|
|
74
|
-
}
|
|
75
|
-
if (options.costCeilingUsd !== void 0 && persistedCostCeilingUsd !== void 0 && options.costCeilingUsd !== persistedCostCeilingUsd) throw new ValidationError(`CostLedger: requested cost ceiling ${options.costCeilingUsd} does not match persisted ceiling ${persistedCostCeilingUsd}`);
|
|
76
|
-
this.costCeilingUsd = persistedCostCeilingUsd ?? options.costCeilingUsd;
|
|
77
|
-
this.costLimitPersisted = !this.persistence || this.costCeilingUsd === void 0 || persistedCostCeilingUsd !== void 0;
|
|
78
|
-
if (this.costCeilingUsd !== void 0 && [...this.records.values()].some((record) => record.status === "pending" && record.maximumCostUsd === void 0)) throw new ValidationError("CostLedger: capped event log contains an unbounded pending call");
|
|
79
|
-
if (options.receipts?.length) {
|
|
80
|
-
const imported = options.receipts.map((receipt, index) => parseImportedReceipt(receipt, `imported receipt ${index + 1}`));
|
|
81
|
-
this.ensureCostLimitPersisted();
|
|
82
|
-
for (const receipt of imported) this.appendRecord(receipt);
|
|
83
|
-
}
|
|
171
|
+
if (receipt.usageUnknown) ctx.addIssue({
|
|
172
|
+
code: "custom",
|
|
173
|
+
message: "known estimated cost requires known usage"
|
|
174
|
+
});
|
|
175
|
+
if (!receipt.pricing) {
|
|
176
|
+
ctx.addIssue({
|
|
177
|
+
code: "custom",
|
|
178
|
+
message: "known estimated cost requires a pricing snapshot"
|
|
179
|
+
});
|
|
180
|
+
return;
|
|
84
181
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
error: toError(error)
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
try {
|
|
154
|
-
return await this.execute(input, pending);
|
|
155
|
-
} catch (error) {
|
|
156
|
-
return {
|
|
157
|
-
succeeded: false,
|
|
158
|
-
callId: pending.callId,
|
|
159
|
-
error: toError(error)
|
|
160
|
-
};
|
|
161
|
-
} finally {
|
|
162
|
-
if (!this.lateCallIds.has(pending.callId)) this.releaseActiveCall(pending.callId);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
/** Wait until every call started by this ledger has produced a durable outcome. */
|
|
166
|
-
async waitForIdle(options = {}) {
|
|
167
|
-
const hasActiveMatch = () => [...this.activeCallIds].some((callId) => {
|
|
168
|
-
const record = this.records.get(callId);
|
|
169
|
-
return record !== void 0 && matches(record, options.filter);
|
|
170
|
-
});
|
|
171
|
-
if (!hasActiveMatch()) return true;
|
|
172
|
-
const timeoutMs = options.timeoutMs ?? 5e3;
|
|
173
|
-
assertTimeout(timeoutMs, "waitForIdle.timeoutMs");
|
|
174
|
-
return new Promise((resolve) => {
|
|
175
|
-
let timer;
|
|
176
|
-
const finish = (settled) => {
|
|
177
|
-
this.idleWaiters.delete(onIdle);
|
|
178
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
179
|
-
resolve(settled);
|
|
180
|
-
};
|
|
181
|
-
const onIdle = () => {
|
|
182
|
-
if (!hasActiveMatch()) finish(true);
|
|
183
|
-
};
|
|
184
|
-
this.idleWaiters.add(onIdle);
|
|
185
|
-
timer = setTimeout(() => finish(false), timeoutMs);
|
|
186
|
-
onIdle();
|
|
187
|
-
});
|
|
182
|
+
const expected = costFromPricing(receipt, receipt.pricing);
|
|
183
|
+
if (!costAmountsMatch(receipt.costUsd, expected)) ctx.addIssue({
|
|
184
|
+
code: "custom",
|
|
185
|
+
message: `estimated cost ${receipt.costUsd} does not match pricing snapshot ${expected}`
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
function costAmountsMatch(left, right) {
|
|
189
|
+
const scale = Math.max(1, Math.abs(left), Math.abs(right));
|
|
190
|
+
return Math.abs(left - right) <= Number.EPSILON * scale * 8;
|
|
191
|
+
}
|
|
192
|
+
function buildReceipt(pending, observed, failed = false, error) {
|
|
193
|
+
assertUsage(observed);
|
|
194
|
+
assertString(observed.model, "receipt.model");
|
|
195
|
+
if ([
|
|
196
|
+
observed.actualCostUsd,
|
|
197
|
+
observed.estimatedCostUsd,
|
|
198
|
+
observed.customTokenPricing
|
|
199
|
+
].filter((value) => value !== void 0).length > 1) throw new ValidationError("CostLedger: a receipt can have only one of actualCostUsd, estimatedCostUsd, or customTokenPricing");
|
|
200
|
+
const customPricingSnapshot = observed.customTokenPricing === void 0 ? void 0 : pricingSnapshot(observed.customTokenPricing);
|
|
201
|
+
const estimated = customPricingSnapshot ? {
|
|
202
|
+
costUsd: costFromPricing(observed, customPricingSnapshot),
|
|
203
|
+
costUnknown: false
|
|
204
|
+
} : costForUsage(observed.model, observed);
|
|
205
|
+
const hasActual = observed.actualCostUsd !== void 0;
|
|
206
|
+
const hasExternalEstimate = observed.estimatedCostUsd !== void 0;
|
|
207
|
+
if (hasActual && observed.costUnknown === true) throw new ValidationError("CostLedger: a receipt cannot have both actualCostUsd and costUnknown=true");
|
|
208
|
+
if (hasActual) assertNonNegative(observed.actualCostUsd, "actualCostUsd");
|
|
209
|
+
if (hasExternalEstimate) assertNonNegative(observed.estimatedCostUsd, "estimatedCostUsd");
|
|
210
|
+
if (hasExternalEstimate && observed.costUnknown === true) throw new ValidationError("CostLedger: a receipt cannot have both estimatedCostUsd and costUnknown=true");
|
|
211
|
+
const usageUnknown = observed.usageUnknown === true;
|
|
212
|
+
const costUnknown = observed.costUnknown === true || !hasActual && !hasExternalEstimate && (usageUnknown || estimated.costUnknown);
|
|
213
|
+
const resolvedModelPricing = !customPricingSnapshot && !hasActual && !hasExternalEstimate && !costUnknown ? resolveModelPricing(observed.model) : null;
|
|
214
|
+
const receiptPricing = !hasActual && !hasExternalEstimate && !costUnknown ? customPricingSnapshot ?? (resolvedModelPricing ? {
|
|
215
|
+
inputUsdPerThousand: resolvedModelPricing.input,
|
|
216
|
+
outputUsdPerThousand: resolvedModelPricing.output
|
|
217
|
+
} : void 0) : void 0;
|
|
218
|
+
return parseReceipt({
|
|
219
|
+
status: "settled",
|
|
220
|
+
callId: pending.callId,
|
|
221
|
+
channel: pending.channel,
|
|
222
|
+
phase: pending.phase,
|
|
223
|
+
actor: pending.actor,
|
|
224
|
+
model: observed.model,
|
|
225
|
+
inputTokens: observed.inputTokens,
|
|
226
|
+
outputTokens: observed.outputTokens,
|
|
227
|
+
...observed.reasoningTokens === void 0 ? {} : { reasoningTokens: observed.reasoningTokens },
|
|
228
|
+
...observed.cachedTokens === void 0 ? {} : { cachedTokens: observed.cachedTokens },
|
|
229
|
+
...observed.cacheWriteTokens === void 0 ? {} : { cacheWriteTokens: observed.cacheWriteTokens },
|
|
230
|
+
costUsd: costUnknown ? 0 : hasActual ? observed.actualCostUsd : hasExternalEstimate ? observed.estimatedCostUsd : estimated.costUsd,
|
|
231
|
+
costUnknown,
|
|
232
|
+
usageUnknown,
|
|
233
|
+
...receiptPricing ? { pricing: receiptPricing } : {},
|
|
234
|
+
...hasActual ? { actualCostUsd: observed.actualCostUsd } : {},
|
|
235
|
+
...hasExternalEstimate ? { estimatedCostUsd: observed.estimatedCostUsd } : {},
|
|
236
|
+
...pending.maximumCostUsd === void 0 ? {} : { maximumCostUsd: pending.maximumCostUsd },
|
|
237
|
+
...failed ? { error: error ?? "paid-call-failed" } : {},
|
|
238
|
+
...pending.tags ? { tags: { ...pending.tags } } : {},
|
|
239
|
+
timestamp: pending.timestamp
|
|
240
|
+
}, "provider receipt");
|
|
241
|
+
}
|
|
242
|
+
function parseReceipt(value, path) {
|
|
243
|
+
try {
|
|
244
|
+
return CostReceiptSchema.parse(value);
|
|
245
|
+
} catch (cause) {
|
|
246
|
+
throw new ValidationError(`CostLedger: invalid ${path}: ${validationMessage(cause)}`, { cause });
|
|
188
247
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
248
|
+
}
|
|
249
|
+
function parseImportedReceipt(value, path) {
|
|
250
|
+
if (typeof value !== "object" || value === null) return parseReceipt(value, path);
|
|
251
|
+
const candidate = { ...value };
|
|
252
|
+
if (candidate.status === "settled" && candidate.actualCostUsd === void 0 && candidate.estimatedCostUsd === void 0 && candidate.costUnknown === false && candidate.pricing === void 0 && typeof candidate.model === "string") {
|
|
253
|
+
const pricing = resolveModelPricing(candidate.model);
|
|
254
|
+
if (pricing) candidate.pricing = {
|
|
255
|
+
inputUsdPerThousand: pricing.input,
|
|
256
|
+
outputUsdPerThousand: pricing.output
|
|
257
|
+
};
|
|
196
258
|
}
|
|
197
|
-
|
|
198
|
-
|
|
259
|
+
return parseReceipt(candidate, path);
|
|
260
|
+
}
|
|
261
|
+
function validateAttribution(input) {
|
|
262
|
+
assertString(input.channel, "channel");
|
|
263
|
+
assertString(input.phase, "phase");
|
|
264
|
+
assertString(input.actor, "actor");
|
|
265
|
+
if (input.model !== void 0) assertString(input.model, "model");
|
|
266
|
+
if (input.tags !== void 0) {
|
|
267
|
+
const parsed = Tags.safeParse(input.tags);
|
|
268
|
+
if (!parsed.success) throw new ValidationError(`CostLedger: invalid tags: ${parsed.error.message}`);
|
|
199
269
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
270
|
+
}
|
|
271
|
+
function matches(record, filter) {
|
|
272
|
+
if (!filter) return true;
|
|
273
|
+
if (filter.channel !== void 0 && record.channel !== filter.channel) return false;
|
|
274
|
+
if (filter.phase !== void 0 && record.phase !== filter.phase) return false;
|
|
275
|
+
return Object.entries(filter.tags ?? {}).every(([key, value]) => record.tags?.[key] === value);
|
|
276
|
+
}
|
|
277
|
+
function cloneRecord(record) {
|
|
278
|
+
if (record.status === "settled") return cloneReceipt(record);
|
|
279
|
+
return clonePendingCall(record);
|
|
280
|
+
}
|
|
281
|
+
function clonePendingCall(record) {
|
|
282
|
+
const { tags, ...rest } = record;
|
|
283
|
+
return {
|
|
284
|
+
...rest,
|
|
285
|
+
...tags ? { tags: { ...tags } } : {}
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
function cloneReceipt(receipt) {
|
|
289
|
+
const { pricing, tags, ...rest } = receipt;
|
|
290
|
+
return {
|
|
291
|
+
...rest,
|
|
292
|
+
...tags ? { tags: { ...tags } } : {},
|
|
293
|
+
...pricing ? { pricing: { ...pricing } } : {}
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
//#endregion
|
|
297
|
+
//#region src/cost-ledger/errors.ts
|
|
298
|
+
/** Typed cost-accounting failures retain detached receipt evidence. */
|
|
299
|
+
var CostCeilingReachedError = class extends ValidationError {
|
|
300
|
+
constructor(ceilingUsd, committedUsd, requestedUsd, phase, actor) {
|
|
301
|
+
super(`CostLedger: reserving ${requestedUsd} for '${actor}' during '${phase}' would exceed ceiling ${ceilingUsd} with ${committedUsd} already committed`);
|
|
206
302
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const unpriced = /* @__PURE__ */ new Set();
|
|
213
|
-
const incompleteReasons = pending.map((record) => `call '${record.callId}' for '${record.actor}' is pending`);
|
|
214
|
-
let inputTokens = 0;
|
|
215
|
-
let outputTokens = 0;
|
|
216
|
-
let reasoningTokens = 0;
|
|
217
|
-
let cachedTokens = 0;
|
|
218
|
-
let cacheWriteTokens = 0;
|
|
219
|
-
let totalCostUsd = 0;
|
|
220
|
-
for (const receipt of receipts) {
|
|
221
|
-
inputTokens += receipt.inputTokens;
|
|
222
|
-
outputTokens += receipt.outputTokens;
|
|
223
|
-
reasoningTokens += receipt.reasoningTokens ?? 0;
|
|
224
|
-
cachedTokens += receipt.cachedTokens ?? 0;
|
|
225
|
-
cacheWriteTokens += receipt.cacheWriteTokens ?? 0;
|
|
226
|
-
totalCostUsd += receipt.costUsd;
|
|
227
|
-
if (receipt.costUnknown) {
|
|
228
|
-
unpriced.add(receipt.model);
|
|
229
|
-
incompleteReasons.push(receipt.error ?? `cost unknown for '${receipt.actor}' using '${receipt.model}'`);
|
|
230
|
-
}
|
|
231
|
-
if (receipt.usageUnknown) incompleteReasons.push(`token usage unknown for '${receipt.actor}' using '${receipt.model}'`);
|
|
232
|
-
if (receipt.maximumCostUsd !== void 0 && receipt.costUsd > receipt.maximumCostUsd) incompleteReasons.push(`'${receipt.actor}' charged ${receipt.costUsd}, exceeding its enforced maximum ${receipt.maximumCostUsd}`);
|
|
233
|
-
const rollup = byChannel.get(receipt.channel) ?? emptyRollup(receipt.channel);
|
|
234
|
-
rollup.calls += 1;
|
|
235
|
-
rollup.inputTokens += receipt.inputTokens;
|
|
236
|
-
rollup.outputTokens += receipt.outputTokens;
|
|
237
|
-
rollup.reasoningTokens = (rollup.reasoningTokens ?? 0) + (receipt.reasoningTokens ?? 0);
|
|
238
|
-
rollup.cachedTokens += receipt.cachedTokens ?? 0;
|
|
239
|
-
rollup.cacheWriteTokens = (rollup.cacheWriteTokens ?? 0) + (receipt.cacheWriteTokens ?? 0);
|
|
240
|
-
rollup.costUsd += receipt.costUsd;
|
|
241
|
-
if (receipt.costUnknown) rollup.unpricedCalls += 1;
|
|
242
|
-
if (receipt.usageUnknown) rollup.unknownUsageCalls += 1;
|
|
243
|
-
byChannel.set(receipt.channel, rollup);
|
|
244
|
-
}
|
|
245
|
-
return {
|
|
246
|
-
totalCalls: receipts.length,
|
|
247
|
-
pendingCalls: pending.length,
|
|
248
|
-
unresolvedCalls: pending.filter((record) => !this.activeCallIds.has(record.callId) || this.lateCallIds.has(record.callId)).length,
|
|
249
|
-
reservedCostUsd: pending.reduce((sum, record) => sum + (record.maximumCostUsd ?? 0), 0),
|
|
250
|
-
inputTokens,
|
|
251
|
-
outputTokens,
|
|
252
|
-
reasoningTokens,
|
|
253
|
-
cachedTokens,
|
|
254
|
-
cacheWriteTokens,
|
|
255
|
-
totalCostUsd,
|
|
256
|
-
costProvenance: pending.length > 0 || receipts.some((receipt) => receipt.costUnknown) ? {
|
|
257
|
-
kind: "uncaptured",
|
|
258
|
-
usd: null
|
|
259
|
-
} : receipts.every((receipt) => receipt.actualCostUsd !== void 0) ? {
|
|
260
|
-
kind: "observed",
|
|
261
|
-
usd: totalCostUsd
|
|
262
|
-
} : {
|
|
263
|
-
kind: "estimated",
|
|
264
|
-
usd: totalCostUsd
|
|
265
|
-
},
|
|
266
|
-
byChannel: [...byChannel.values()].sort((a, b) => a.channel.localeCompare(b.channel)),
|
|
267
|
-
unpricedModels: [...unpriced].sort(),
|
|
268
|
-
fullyPriced: unpriced.size === 0,
|
|
269
|
-
usageComplete: pending.length === 0 && receipts.every((receipt) => !receipt.usageUnknown),
|
|
270
|
-
accountingComplete: incompleteReasons.length === 0,
|
|
271
|
-
incompleteReasons: [...new Set(incompleteReasons)]
|
|
272
|
-
};
|
|
303
|
+
};
|
|
304
|
+
var CostAccountingIncompleteError = class extends ValidationError {};
|
|
305
|
+
var CostReservationExceededError = class extends ValidationError {
|
|
306
|
+
constructor(actor, actualUsd, maximumUsd) {
|
|
307
|
+
super(`CostLedger: '${actor}' charged ${actualUsd}, exceeding its enforced maximum ${maximumUsd}`);
|
|
273
308
|
}
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
this.completedTasks += count;
|
|
309
|
+
};
|
|
310
|
+
var CostCallConflictError = class extends ValidationError {
|
|
311
|
+
callId;
|
|
312
|
+
receipt;
|
|
313
|
+
constructor(message, options = {}) {
|
|
314
|
+
super(message, options.cause === void 0 ? void 0 : { cause: options.cause });
|
|
315
|
+
this.callId = options.callId;
|
|
316
|
+
this.receipt = options.receipt ? cloneReceipt(options.receipt) : void 0;
|
|
283
317
|
}
|
|
284
|
-
|
|
285
|
-
|
|
318
|
+
};
|
|
319
|
+
var CostLedgerPersistenceError = class extends ValidationError {
|
|
320
|
+
callId;
|
|
321
|
+
receipt;
|
|
322
|
+
constructor(cause, callId, receipt) {
|
|
323
|
+
super(`CostLedger: failed to persist${callId ? ` call '${callId}'` : ""}: ${toError(cause).message}`, { cause });
|
|
324
|
+
this.callId = callId;
|
|
325
|
+
this.receipt = receipt ? cloneReceipt(receipt) : void 0;
|
|
286
326
|
}
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (settled.kind === "aborted") {
|
|
298
|
-
this.lateCallIds.add(pending.callId);
|
|
299
|
-
this.captureLateOutcome(input, pending, operation);
|
|
300
|
-
return paidFailure(pending.callId, abortError(signal));
|
|
301
|
-
}
|
|
302
|
-
if (settled.kind === "error") {
|
|
303
|
-
let observed;
|
|
304
|
-
try {
|
|
305
|
-
observed = input.receiptFromError?.(settled.error);
|
|
306
|
-
} catch (receiptError) {
|
|
307
|
-
return this.captureFailure(pending, settled.error, receiptError);
|
|
308
|
-
}
|
|
309
|
-
return this.commitOutcome(pending, settled.error, observed ?? unknownReceipt(pending.model));
|
|
310
|
-
}
|
|
311
|
-
try {
|
|
312
|
-
const receipt = this.commitReceipt(pending, input.receipt(settled.value));
|
|
313
|
-
if (receipt.maximumCostUsd !== void 0 && receipt.costUsd > receipt.maximumCostUsd) return paidFailure(pending.callId, new CostReservationExceededError(pending.actor, receipt.costUsd, receipt.maximumCostUsd), receipt);
|
|
314
|
-
return {
|
|
315
|
-
succeeded: true,
|
|
316
|
-
callId: pending.callId,
|
|
317
|
-
value: settled.value,
|
|
318
|
-
receipt
|
|
319
|
-
};
|
|
320
|
-
} catch (receiptError) {
|
|
321
|
-
if (receiptError instanceof CostLedgerPersistenceError || receiptError instanceof CostCallConflictError) return paidFailure(pending.callId, receiptError);
|
|
322
|
-
return this.captureFailure(pending, receiptError, receiptError);
|
|
323
|
-
}
|
|
327
|
+
};
|
|
328
|
+
var CostReceiptCaptureError = class extends ValidationError {
|
|
329
|
+
callId;
|
|
330
|
+
receipt;
|
|
331
|
+
receiptError;
|
|
332
|
+
constructor(callId, cause, receiptError, receipt) {
|
|
333
|
+
super(`CostLedger: could not capture the provider receipt for call '${callId}'`, { cause });
|
|
334
|
+
this.callId = callId;
|
|
335
|
+
this.receipt = receipt ? cloneReceipt(receipt) : void 0;
|
|
336
|
+
this.receiptError = toError(receiptError);
|
|
324
337
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
338
|
+
};
|
|
339
|
+
//#endregion
|
|
340
|
+
//#region src/cost-ledger/journal.ts
|
|
341
|
+
/** Parse the append-only journal and enforce immutable pending-to-settled transitions. */
|
|
342
|
+
const CostLedgerEventSchema = z.union([
|
|
343
|
+
z.strictObject({
|
|
344
|
+
version: z.literal(1),
|
|
345
|
+
record: PendingCostCallSchema
|
|
346
|
+
}),
|
|
347
|
+
z.strictObject({
|
|
348
|
+
version: z.literal(2),
|
|
349
|
+
record: CostReceiptSchema
|
|
350
|
+
}),
|
|
351
|
+
z.strictObject({
|
|
352
|
+
version: z.literal(1),
|
|
353
|
+
completedTasks: TokenCount
|
|
354
|
+
}),
|
|
355
|
+
z.strictObject({
|
|
356
|
+
version: z.literal(1),
|
|
357
|
+
costCeilingUsd: NonNegative
|
|
358
|
+
})
|
|
359
|
+
]);
|
|
360
|
+
function parseEvents(serialized) {
|
|
361
|
+
const records = /* @__PURE__ */ new Map();
|
|
362
|
+
let completedTasks = 0;
|
|
363
|
+
let costCeilingUsd;
|
|
364
|
+
let lineNumber = 0;
|
|
365
|
+
try {
|
|
366
|
+
for (const line of completeJournalPrefix(serialized).split("\n")) {
|
|
367
|
+
if (!line.trim()) continue;
|
|
368
|
+
lineNumber += 1;
|
|
369
|
+
const event = CostLedgerEventSchema.parse(JSON.parse(line));
|
|
370
|
+
if ("record" in event) {
|
|
371
|
+
const record = event.record;
|
|
372
|
+
validateTransition(records, record);
|
|
373
|
+
records.set(record.callId, cloneRecord(record));
|
|
374
|
+
} else if ("completedTasks" in event) {
|
|
375
|
+
completedTasks += event.completedTasks;
|
|
376
|
+
if (!Number.isSafeInteger(completedTasks)) throw new ValidationError("CostLedger: completed task count exceeds safe integer range");
|
|
377
|
+
} else {
|
|
378
|
+
if (costCeilingUsd !== void 0) throw new ValidationError("CostLedger: duplicate persisted cost ceiling");
|
|
379
|
+
costCeilingUsd = event.costCeilingUsd;
|
|
340
380
|
}
|
|
341
|
-
}).finally(() => {
|
|
342
|
-
this.lateCallIds.delete(pending.callId);
|
|
343
|
-
this.releaseActiveCall(pending.callId);
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
releaseActiveCall(callId) {
|
|
347
|
-
this.activeCallIds.delete(callId);
|
|
348
|
-
for (const resolve of [...this.idleWaiters]) resolve();
|
|
349
|
-
}
|
|
350
|
-
async waitForReservationRelease(signal) {
|
|
351
|
-
if (signal?.aborted) throw abortError(signal);
|
|
352
|
-
await new Promise((resolve, reject) => {
|
|
353
|
-
let finished = false;
|
|
354
|
-
const finish = (error) => {
|
|
355
|
-
if (finished) return;
|
|
356
|
-
finished = true;
|
|
357
|
-
this.idleWaiters.delete(onRelease);
|
|
358
|
-
signal?.removeEventListener("abort", onAbort);
|
|
359
|
-
if (error) reject(error);
|
|
360
|
-
else resolve();
|
|
361
|
-
};
|
|
362
|
-
const onRelease = () => finish();
|
|
363
|
-
const onAbort = () => finish(abortError(signal));
|
|
364
|
-
this.idleWaiters.add(onRelease);
|
|
365
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
366
|
-
if (this.activeCallIds.size === 0) onRelease();
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
commitOutcome(pending, error, observed) {
|
|
370
|
-
try {
|
|
371
|
-
const receipt = this.commitReceipt(pending, observed, true);
|
|
372
|
-
return paidFailure(pending.callId, error, receipt);
|
|
373
|
-
} catch (receiptError) {
|
|
374
|
-
if (receiptError instanceof CostLedgerPersistenceError || receiptError instanceof CostCallConflictError) return paidFailure(pending.callId, receiptError);
|
|
375
|
-
return this.captureFailure(pending, error, receiptError);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
captureFailure(pending, cause, receiptError) {
|
|
379
|
-
try {
|
|
380
|
-
const receipt = this.commitReceipt(pending, unknownReceipt(pending.model), true);
|
|
381
|
-
return paidFailure(pending.callId, new CostReceiptCaptureError(pending.callId, cause, receiptError, receipt), receipt);
|
|
382
|
-
} catch (error) {
|
|
383
|
-
const typed = error instanceof Error ? error : toError(error);
|
|
384
|
-
return paidFailure(pending.callId, typed);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
commitReceipt(pending, observed, failed = false, error) {
|
|
388
|
-
const receipt = buildReceipt(pending, observed, failed, error);
|
|
389
|
-
if (this.records.get(pending.callId)?.status !== "pending") throw new CostCallConflictError(`CostLedger: call '${pending.callId}' is not pending`);
|
|
390
|
-
this.appendRecord(receipt);
|
|
391
|
-
return cloneReceipt(receipt);
|
|
392
|
-
}
|
|
393
|
-
resolveMaximum(maximum) {
|
|
394
|
-
if (!maximum) {
|
|
395
|
-
if (this.costCeilingUsd !== void 0) throw new CostAccountingIncompleteError("CostLedger: capped paid calls require a hard maximumCharge before execution");
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
if ("externallyEnforcedMaximumUsd" in maximum) {
|
|
399
|
-
assertNonNegative(maximum.externallyEnforcedMaximumUsd, "maximumCharge.externallyEnforcedMaximumUsd");
|
|
400
|
-
return maximum.externallyEnforcedMaximumUsd;
|
|
401
|
-
}
|
|
402
|
-
if ("customTokenPricing" in maximum) return costForTokenPricing(maximum.customTokenPricing, maximum);
|
|
403
|
-
const priced = costForUsage(maximum.model, maximum);
|
|
404
|
-
if (priced.costUnknown) {
|
|
405
|
-
if (this.costCeilingUsd !== void 0) throw new CostAccountingIncompleteError(`CostLedger: cannot reserve unpriced model '${maximum.model}' in a capped run`);
|
|
406
|
-
return;
|
|
407
|
-
}
|
|
408
|
-
return priced.costUsd;
|
|
409
|
-
}
|
|
410
|
-
/** Compute admission totals in record order without building reporting rollups. */
|
|
411
|
-
admissionState() {
|
|
412
|
-
let unresolvedCalls = 0;
|
|
413
|
-
let reservedCostUsd = 0;
|
|
414
|
-
let committedCostUsd = 0;
|
|
415
|
-
let budgetBreached = false;
|
|
416
|
-
for (const record of this.records.values()) if (record.status === "pending") {
|
|
417
|
-
if (!this.activeCallIds.has(record.callId) || this.lateCallIds.has(record.callId)) unresolvedCalls += 1;
|
|
418
|
-
reservedCostUsd += record.maximumCostUsd ?? 0;
|
|
419
|
-
} else {
|
|
420
|
-
if (record.maximumCostUsd !== void 0 && record.costUsd > record.maximumCostUsd) budgetBreached = true;
|
|
421
|
-
committedCostUsd += record.costUnknown ? record.maximumCostUsd ?? record.costUsd : record.costUsd;
|
|
422
381
|
}
|
|
423
382
|
return {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
budgetBreached
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
appendRecord(record) {
|
|
431
|
-
const callId = record.callId;
|
|
432
|
-
const receipt = record.status === "settled" ? record : void 0;
|
|
433
|
-
validateTransition(this.records, record);
|
|
434
|
-
const event = record.status === "settled" ? {
|
|
435
|
-
version: 2,
|
|
436
|
-
record: cloneReceipt(record)
|
|
437
|
-
} : {
|
|
438
|
-
version: 1,
|
|
439
|
-
record: clonePendingCall(record)
|
|
383
|
+
records: [...records.values()],
|
|
384
|
+
completedTasks,
|
|
385
|
+
...costCeilingUsd === void 0 ? {} : { costCeilingUsd }
|
|
440
386
|
};
|
|
441
|
-
|
|
442
|
-
|
|
387
|
+
} catch (cause) {
|
|
388
|
+
throw new ValidationError(`CostLedger: invalid persisted event ${lineNumber || 1}: ${validationMessage(cause)}`, { cause });
|
|
443
389
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
390
|
+
}
|
|
391
|
+
function completeJournalPrefix(serialized) {
|
|
392
|
+
if (!serialized || serialized.endsWith("\n")) return serialized;
|
|
393
|
+
const finalNewline = serialized.lastIndexOf("\n");
|
|
394
|
+
const tail = serialized.slice(finalNewline + 1);
|
|
395
|
+
try {
|
|
396
|
+
JSON.parse(tail);
|
|
397
|
+
} catch {
|
|
398
|
+
return finalNewline < 0 ? "" : serialized.slice(0, finalNewline + 1);
|
|
451
399
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
400
|
+
throw new ValidationError("CostLedger: complete final persisted event is missing its newline terminator");
|
|
401
|
+
}
|
|
402
|
+
function validateTransition(records, record) {
|
|
403
|
+
const current = records.get(record.callId);
|
|
404
|
+
if (!current) return;
|
|
405
|
+
if (record.status === "pending" || current.status === "settled") throw new CostCallConflictError(`CostLedger: duplicate callId '${record.callId}'`);
|
|
406
|
+
if (!sameAttribution(current, record)) throw new ValidationError(`CostLedger: receipt attribution changed for call '${record.callId}'`);
|
|
407
|
+
}
|
|
408
|
+
function sameAttribution(before, after) {
|
|
409
|
+
return before.channel === after.channel && before.phase === after.phase && before.actor === after.actor && before.maximumCostUsd === after.maximumCostUsd && before.timestamp === after.timestamp && sameTags(before.tags, after.tags);
|
|
410
|
+
}
|
|
411
|
+
function sameTags(left, right) {
|
|
412
|
+
const leftEntries = Object.entries(left ?? {}).sort(([a], [b]) => a.localeCompare(b));
|
|
413
|
+
const rightEntries = Object.entries(right ?? {}).sort(([a], [b]) => a.localeCompare(b));
|
|
414
|
+
return JSON.stringify(leftEntries) === JSON.stringify(rightEntries);
|
|
415
|
+
}
|
|
416
|
+
//#endregion
|
|
417
|
+
//#region src/cost-ledger/summary.ts
|
|
418
|
+
function summarizeCostLedger(records, activeCallIds, lateCallIds) {
|
|
419
|
+
const pending = records.filter((record) => record.status === "pending");
|
|
420
|
+
const receipts = records.filter((record) => record.status === "settled");
|
|
421
|
+
const byChannel = /* @__PURE__ */ new Map();
|
|
422
|
+
const unpriced = /* @__PURE__ */ new Set();
|
|
423
|
+
const incompleteReasons = pending.map((record) => `call '${record.callId}' for '${record.actor}' is pending`);
|
|
424
|
+
let inputTokens = 0;
|
|
425
|
+
let outputTokens = 0;
|
|
426
|
+
let reasoningTokens = 0;
|
|
427
|
+
let cachedTokens = 0;
|
|
428
|
+
let cacheWriteTokens = 0;
|
|
429
|
+
let totalCostUsd = 0;
|
|
430
|
+
for (const receipt of receipts) {
|
|
431
|
+
inputTokens += receipt.inputTokens;
|
|
432
|
+
outputTokens += receipt.outputTokens;
|
|
433
|
+
reasoningTokens += receipt.reasoningTokens ?? 0;
|
|
434
|
+
cachedTokens += receipt.cachedTokens ?? 0;
|
|
435
|
+
cacheWriteTokens += receipt.cacheWriteTokens ?? 0;
|
|
436
|
+
totalCostUsd += receipt.costUsd;
|
|
437
|
+
if (receipt.costUnknown) {
|
|
438
|
+
unpriced.add(receipt.model);
|
|
439
|
+
incompleteReasons.push(receipt.error ?? `cost unknown for '${receipt.actor}' using '${receipt.model}'`);
|
|
466
440
|
}
|
|
441
|
+
if (receipt.usageUnknown) incompleteReasons.push(`token usage unknown for '${receipt.actor}' using '${receipt.model}'`);
|
|
442
|
+
if (receipt.maximumCostUsd !== void 0 && receipt.costUsd > receipt.maximumCostUsd) incompleteReasons.push(`'${receipt.actor}' charged ${receipt.costUsd}, exceeding its enforced maximum ${receipt.maximumCostUsd}`);
|
|
443
|
+
const rollup = byChannel.get(receipt.channel) ?? emptyRollup(receipt.channel);
|
|
444
|
+
rollup.calls += 1;
|
|
445
|
+
rollup.inputTokens += receipt.inputTokens;
|
|
446
|
+
rollup.outputTokens += receipt.outputTokens;
|
|
447
|
+
rollup.reasoningTokens = (rollup.reasoningTokens ?? 0) + (receipt.reasoningTokens ?? 0);
|
|
448
|
+
rollup.cachedTokens += receipt.cachedTokens ?? 0;
|
|
449
|
+
rollup.cacheWriteTokens = (rollup.cacheWriteTokens ?? 0) + (receipt.cacheWriteTokens ?? 0);
|
|
450
|
+
rollup.costUsd += receipt.costUsd;
|
|
451
|
+
if (receipt.costUnknown) rollup.unpricedCalls += 1;
|
|
452
|
+
if (receipt.usageUnknown) rollup.unknownUsageCalls += 1;
|
|
453
|
+
byChannel.set(receipt.channel, rollup);
|
|
467
454
|
}
|
|
468
|
-
};
|
|
469
|
-
/** Return the canonical pricing-table key, or null when the model is unpriced. */
|
|
470
|
-
function modelPriceKey(model) {
|
|
471
|
-
return isModelPriced(model) ? model : null;
|
|
472
|
-
}
|
|
473
|
-
function costForUsage(model, usage) {
|
|
474
|
-
assertUsage(usage);
|
|
475
|
-
if (!resolveModelPricing(model)) return {
|
|
476
|
-
costUsd: 0,
|
|
477
|
-
costUnknown: true
|
|
478
|
-
};
|
|
479
455
|
return {
|
|
480
|
-
|
|
481
|
-
|
|
456
|
+
totalCalls: receipts.length,
|
|
457
|
+
pendingCalls: pending.length,
|
|
458
|
+
unresolvedCalls: pending.filter((record) => !activeCallIds.has(record.callId) || lateCallIds.has(record.callId)).length,
|
|
459
|
+
reservedCostUsd: pending.reduce((sum, record) => sum + (record.maximumCostUsd ?? 0), 0),
|
|
460
|
+
inputTokens,
|
|
461
|
+
outputTokens,
|
|
462
|
+
reasoningTokens,
|
|
463
|
+
cachedTokens,
|
|
464
|
+
cacheWriteTokens,
|
|
465
|
+
totalCostUsd,
|
|
466
|
+
costProvenance: pending.length > 0 || receipts.some((receipt) => receipt.costUnknown) ? {
|
|
467
|
+
kind: "uncaptured",
|
|
468
|
+
usd: null
|
|
469
|
+
} : receipts.every((receipt) => receipt.actualCostUsd !== void 0) ? {
|
|
470
|
+
kind: "observed",
|
|
471
|
+
usd: totalCostUsd
|
|
472
|
+
} : {
|
|
473
|
+
kind: "estimated",
|
|
474
|
+
usd: totalCostUsd
|
|
475
|
+
},
|
|
476
|
+
byChannel: [...byChannel.values()].sort((a, b) => a.channel.localeCompare(b.channel)),
|
|
477
|
+
unpricedModels: [...unpriced].sort(),
|
|
478
|
+
fullyPriced: unpriced.size === 0,
|
|
479
|
+
usageComplete: pending.length === 0 && receipts.every((receipt) => !receipt.usageUnknown),
|
|
480
|
+
accountingComplete: incompleteReasons.length === 0,
|
|
481
|
+
incompleteReasons: [...new Set(incompleteReasons)]
|
|
482
482
|
};
|
|
483
483
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
}
|
|
498
|
-
async function settle(promise, signal) {
|
|
499
|
-
return await new Promise((resolve) => {
|
|
500
|
-
let done = false;
|
|
501
|
-
const finish = (value) => {
|
|
502
|
-
if (done) return;
|
|
503
|
-
done = true;
|
|
504
|
-
signal.removeEventListener("abort", onAbort);
|
|
505
|
-
resolve(value);
|
|
506
|
-
};
|
|
507
|
-
const onAbort = () => finish({ kind: "aborted" });
|
|
508
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
509
|
-
if (signal.aborted) onAbort();
|
|
510
|
-
promise.then((value) => finish({
|
|
511
|
-
kind: "value",
|
|
512
|
-
value
|
|
513
|
-
}), (error) => finish({
|
|
514
|
-
kind: "error",
|
|
515
|
-
error: toError(error)
|
|
516
|
-
}));
|
|
517
|
-
});
|
|
518
|
-
}
|
|
519
|
-
function buildReceipt(pending, observed, failed = false, error) {
|
|
520
|
-
assertUsage(observed);
|
|
521
|
-
assertString(observed.model, "receipt.model");
|
|
522
|
-
if ([
|
|
523
|
-
observed.actualCostUsd,
|
|
524
|
-
observed.estimatedCostUsd,
|
|
525
|
-
observed.customTokenPricing
|
|
526
|
-
].filter((value) => value !== void 0).length > 1) throw new ValidationError("CostLedger: a receipt can have only one of actualCostUsd, estimatedCostUsd, or customTokenPricing");
|
|
527
|
-
const customPricingSnapshot = observed.customTokenPricing === void 0 ? void 0 : pricingSnapshot(observed.customTokenPricing);
|
|
528
|
-
const estimated = customPricingSnapshot ? {
|
|
529
|
-
costUsd: costFromPricing(observed, customPricingSnapshot),
|
|
530
|
-
costUnknown: false
|
|
531
|
-
} : costForUsage(observed.model, observed);
|
|
532
|
-
const hasActual = observed.actualCostUsd !== void 0;
|
|
533
|
-
const hasExternalEstimate = observed.estimatedCostUsd !== void 0;
|
|
534
|
-
if (hasActual && observed.costUnknown === true) throw new ValidationError("CostLedger: a receipt cannot have both actualCostUsd and costUnknown=true");
|
|
535
|
-
if (hasActual) assertNonNegative(observed.actualCostUsd, "actualCostUsd");
|
|
536
|
-
if (hasExternalEstimate) assertNonNegative(observed.estimatedCostUsd, "estimatedCostUsd");
|
|
537
|
-
if (hasExternalEstimate && observed.costUnknown === true) throw new ValidationError("CostLedger: a receipt cannot have both estimatedCostUsd and costUnknown=true");
|
|
538
|
-
const usageUnknown = observed.usageUnknown === true;
|
|
539
|
-
const costUnknown = observed.costUnknown === true || !hasActual && !hasExternalEstimate && (usageUnknown || estimated.costUnknown);
|
|
540
|
-
const resolvedModelPricing = !customPricingSnapshot && !hasActual && !hasExternalEstimate && !costUnknown ? resolveModelPricing(observed.model) : null;
|
|
541
|
-
const receiptPricing = !hasActual && !hasExternalEstimate && !costUnknown ? customPricingSnapshot ?? (resolvedModelPricing ? {
|
|
542
|
-
inputUsdPerThousand: resolvedModelPricing.input,
|
|
543
|
-
outputUsdPerThousand: resolvedModelPricing.output
|
|
544
|
-
} : void 0) : void 0;
|
|
545
|
-
return parseReceipt({
|
|
546
|
-
status: "settled",
|
|
547
|
-
callId: pending.callId,
|
|
548
|
-
channel: pending.channel,
|
|
549
|
-
phase: pending.phase,
|
|
550
|
-
actor: pending.actor,
|
|
551
|
-
model: observed.model,
|
|
552
|
-
inputTokens: observed.inputTokens,
|
|
553
|
-
outputTokens: observed.outputTokens,
|
|
554
|
-
...observed.reasoningTokens === void 0 ? {} : { reasoningTokens: observed.reasoningTokens },
|
|
555
|
-
...observed.cachedTokens === void 0 ? {} : { cachedTokens: observed.cachedTokens },
|
|
556
|
-
...observed.cacheWriteTokens === void 0 ? {} : { cacheWriteTokens: observed.cacheWriteTokens },
|
|
557
|
-
costUsd: costUnknown ? 0 : hasActual ? observed.actualCostUsd : hasExternalEstimate ? observed.estimatedCostUsd : estimated.costUsd,
|
|
558
|
-
costUnknown,
|
|
559
|
-
usageUnknown,
|
|
560
|
-
...receiptPricing ? { pricing: receiptPricing } : {},
|
|
561
|
-
...hasActual ? { actualCostUsd: observed.actualCostUsd } : {},
|
|
562
|
-
...hasExternalEstimate ? { estimatedCostUsd: observed.estimatedCostUsd } : {},
|
|
563
|
-
...pending.maximumCostUsd === void 0 ? {} : { maximumCostUsd: pending.maximumCostUsd },
|
|
564
|
-
...failed ? { error: error ?? "paid-call-failed" } : {},
|
|
565
|
-
...pending.tags ? { tags: { ...pending.tags } } : {},
|
|
566
|
-
timestamp: pending.timestamp
|
|
567
|
-
}, "provider receipt");
|
|
484
|
+
function emptyRollup(channel) {
|
|
485
|
+
return {
|
|
486
|
+
channel,
|
|
487
|
+
calls: 0,
|
|
488
|
+
inputTokens: 0,
|
|
489
|
+
outputTokens: 0,
|
|
490
|
+
reasoningTokens: 0,
|
|
491
|
+
cachedTokens: 0,
|
|
492
|
+
cacheWriteTokens: 0,
|
|
493
|
+
costUsd: 0,
|
|
494
|
+
unpricedCalls: 0,
|
|
495
|
+
unknownUsageCalls: 0
|
|
496
|
+
};
|
|
568
497
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
};
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
498
|
+
//#endregion
|
|
499
|
+
//#region src/cost-ledger.ts
|
|
500
|
+
/** Run-wide paid-call admission, durable call state, receipts, and summaries. */
|
|
501
|
+
var CostLedger = class {
|
|
502
|
+
records = /* @__PURE__ */ new Map();
|
|
503
|
+
activeCallIds = /* @__PURE__ */ new Set();
|
|
504
|
+
lateCallIds = /* @__PURE__ */ new Set();
|
|
505
|
+
idleWaiters = /* @__PURE__ */ new Set();
|
|
506
|
+
completedTasks = 0;
|
|
507
|
+
revision = "memory";
|
|
508
|
+
costLimitPersisted = false;
|
|
509
|
+
costCeilingUsd;
|
|
510
|
+
persistence;
|
|
511
|
+
constructor(input) {
|
|
512
|
+
const options = typeof input === "number" ? { costCeilingUsd: input } : input ?? {};
|
|
513
|
+
this.persistence = options.persistence;
|
|
514
|
+
if (options.costCeilingUsd !== void 0) assertNonNegative(options.costCeilingUsd, "costCeilingUsd");
|
|
515
|
+
let persistedCostCeilingUsd;
|
|
516
|
+
if (this.persistence) {
|
|
517
|
+
let stored;
|
|
518
|
+
try {
|
|
519
|
+
stored = this.persistence.read();
|
|
520
|
+
} catch (cause) {
|
|
521
|
+
throw new CostLedgerPersistenceError(cause);
|
|
522
|
+
}
|
|
523
|
+
assertString(stored.revision, "persistence revision");
|
|
524
|
+
this.revision = stored.revision;
|
|
525
|
+
const restored = parseEvents(stored.events);
|
|
526
|
+
for (const record of restored.records) this.records.set(record.callId, record);
|
|
527
|
+
this.completedTasks = restored.completedTasks;
|
|
528
|
+
persistedCostCeilingUsd = restored.costCeilingUsd;
|
|
529
|
+
}
|
|
530
|
+
if (options.costCeilingUsd !== void 0 && persistedCostCeilingUsd !== void 0 && options.costCeilingUsd !== persistedCostCeilingUsd) throw new ValidationError(`CostLedger: requested cost ceiling ${options.costCeilingUsd} does not match persisted ceiling ${persistedCostCeilingUsd}`);
|
|
531
|
+
this.costCeilingUsd = persistedCostCeilingUsd ?? options.costCeilingUsd;
|
|
532
|
+
this.costLimitPersisted = !this.persistence || this.costCeilingUsd === void 0 || persistedCostCeilingUsd !== void 0;
|
|
533
|
+
if (this.costCeilingUsd !== void 0 && [...this.records.values()].some((record) => record.status === "pending" && record.maximumCostUsd === void 0)) throw new ValidationError("CostLedger: capped event log contains an unbounded pending call");
|
|
534
|
+
if (options.receipts?.length) {
|
|
535
|
+
const imported = options.receipts.map((receipt, index) => parseImportedReceipt(receipt, `imported receipt ${index + 1}`));
|
|
536
|
+
this.ensureCostLimitPersisted();
|
|
537
|
+
for (const receipt of imported) this.appendRecord(receipt);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
async runPaidCall(input) {
|
|
541
|
+
let callId;
|
|
542
|
+
let pending;
|
|
543
|
+
try {
|
|
544
|
+
callId = resolveCallId(input.callId);
|
|
545
|
+
validateAttribution(input);
|
|
546
|
+
if (input.signal?.aborted) return {
|
|
547
|
+
succeeded: false,
|
|
548
|
+
callId,
|
|
549
|
+
error: abortError(input.signal)
|
|
550
|
+
};
|
|
551
|
+
this.ensureCostLimitPersisted(callId);
|
|
552
|
+
const maximumCostUsd = this.resolveMaximum(input.maximumCharge);
|
|
553
|
+
const existing = this.records.get(callId);
|
|
554
|
+
if (existing) return {
|
|
555
|
+
succeeded: false,
|
|
556
|
+
callId,
|
|
557
|
+
error: new CostCallConflictError(existing.status === "pending" ? `CostLedger: callId '${callId}' is unresolved and must be reconciled before retry` : `CostLedger: callId '${callId}' already exists`, { callId })
|
|
558
|
+
};
|
|
559
|
+
if (!pending) {
|
|
560
|
+
while (true) {
|
|
561
|
+
if (this.records.has(callId)) return {
|
|
562
|
+
succeeded: false,
|
|
563
|
+
callId,
|
|
564
|
+
error: new CostCallConflictError(`CostLedger: callId '${callId}' already exists`)
|
|
565
|
+
};
|
|
566
|
+
const admission = this.admissionState();
|
|
567
|
+
if (admission.unresolvedCalls > 0) return {
|
|
568
|
+
succeeded: false,
|
|
569
|
+
callId,
|
|
570
|
+
error: new CostAccountingIncompleteError(`CostLedger: ${admission.unresolvedCalls} unresolved call(s) must be reconciled before new paid work`)
|
|
571
|
+
};
|
|
572
|
+
if (this.costCeilingUsd === void 0) break;
|
|
573
|
+
if (admission.budgetBreached) return {
|
|
574
|
+
succeeded: false,
|
|
575
|
+
callId,
|
|
576
|
+
error: new CostAccountingIncompleteError(`CostLedger: a settled call exceeded its enforced maximum; refusing paid call '${input.actor}' during '${input.phase}'`)
|
|
577
|
+
};
|
|
578
|
+
const { committedCostUsd } = admission;
|
|
579
|
+
if (committedCostUsd + maximumCostUsd > this.costCeilingUsd) return {
|
|
580
|
+
succeeded: false,
|
|
581
|
+
callId,
|
|
582
|
+
error: new CostCeilingReachedError(this.costCeilingUsd, committedCostUsd, maximumCostUsd, input.phase, input.actor)
|
|
583
|
+
};
|
|
584
|
+
if (committedCostUsd + admission.reservedCostUsd + maximumCostUsd <= this.costCeilingUsd) break;
|
|
585
|
+
await this.waitForReservationRelease(input.signal);
|
|
586
|
+
}
|
|
587
|
+
pending = {
|
|
588
|
+
status: "pending",
|
|
589
|
+
callId,
|
|
590
|
+
channel: input.channel,
|
|
591
|
+
phase: input.phase,
|
|
592
|
+
actor: input.actor,
|
|
593
|
+
model: pendingModel(input),
|
|
594
|
+
...maximumCostUsd === void 0 ? {} : { maximumCostUsd },
|
|
595
|
+
...input.tags ? { tags: { ...input.tags } } : {},
|
|
596
|
+
timestamp: Date.now()
|
|
597
|
+
};
|
|
598
|
+
this.appendRecord(pending);
|
|
599
|
+
this.activeCallIds.add(callId);
|
|
600
|
+
}
|
|
601
|
+
} catch (error) {
|
|
602
|
+
return {
|
|
603
|
+
succeeded: false,
|
|
604
|
+
...callId ? { callId } : {},
|
|
605
|
+
error: toError(error)
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
try {
|
|
609
|
+
return await this.execute(input, pending);
|
|
610
|
+
} catch (error) {
|
|
611
|
+
return {
|
|
612
|
+
succeeded: false,
|
|
613
|
+
callId: pending.callId,
|
|
614
|
+
error: toError(error)
|
|
615
|
+
};
|
|
616
|
+
} finally {
|
|
617
|
+
if (!this.lateCallIds.has(pending.callId)) this.releaseActiveCall(pending.callId);
|
|
618
|
+
}
|
|
631
619
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
620
|
+
/** Wait until every call started by this ledger has produced a durable outcome. */
|
|
621
|
+
async waitForIdle(options = {}) {
|
|
622
|
+
const hasActiveMatch = () => [...this.activeCallIds].some((callId) => {
|
|
623
|
+
const record = this.records.get(callId);
|
|
624
|
+
return record !== void 0 && matches(record, options.filter);
|
|
636
625
|
});
|
|
637
|
-
if (
|
|
638
|
-
|
|
639
|
-
|
|
626
|
+
if (!hasActiveMatch()) return true;
|
|
627
|
+
const timeoutMs = options.timeoutMs ?? 5e3;
|
|
628
|
+
assertTimeout(timeoutMs, "waitForIdle.timeoutMs");
|
|
629
|
+
return new Promise((resolve) => {
|
|
630
|
+
let timer;
|
|
631
|
+
const finish = (settled) => {
|
|
632
|
+
this.idleWaiters.delete(onIdle);
|
|
633
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
634
|
+
resolve(settled);
|
|
635
|
+
};
|
|
636
|
+
const onIdle = () => {
|
|
637
|
+
if (!hasActiveMatch()) finish(true);
|
|
638
|
+
};
|
|
639
|
+
this.idleWaiters.add(onIdle);
|
|
640
|
+
timer = setTimeout(() => finish(false), timeoutMs);
|
|
641
|
+
onIdle();
|
|
640
642
|
});
|
|
641
|
-
return;
|
|
642
643
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
});
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
644
|
+
/** Settle a call left pending by a crashed process after reconciling with the provider. */
|
|
645
|
+
reconcile(callId, observed, options = {}) {
|
|
646
|
+
const pending = this.records.get(callId);
|
|
647
|
+
if (pending?.status !== "pending") throw new CostCallConflictError(`CostLedger: no pending call '${callId}'`);
|
|
648
|
+
if (this.activeCallIds.has(callId)) throw new CostCallConflictError(`CostLedger: call '${callId}' is still active`);
|
|
649
|
+
this.ensureCostLimitPersisted(callId);
|
|
650
|
+
return this.commitReceipt(pending, observed, options.failed === true || options.error !== void 0, options.error);
|
|
651
|
+
}
|
|
652
|
+
list(filter) {
|
|
653
|
+
return [...this.records.values()].filter((record) => record.status === "settled").filter((receipt) => matches(receipt, filter)).map(cloneReceipt);
|
|
654
|
+
}
|
|
655
|
+
/** Read pending calls without exposing mutable ledger state. */
|
|
656
|
+
listPending(filter) {
|
|
657
|
+
return [...this.records.values()].filter((record) => record.status === "pending").filter((record) => matches(record, filter)).map((record) => ({
|
|
658
|
+
...clonePendingCall(record),
|
|
659
|
+
state: this.lateCallIds.has(record.callId) ? "late" : this.activeCallIds.has(record.callId) ? "active" : "interrupted"
|
|
660
|
+
}));
|
|
661
|
+
}
|
|
662
|
+
summary(filter) {
|
|
663
|
+
return summarizeCostLedger([...this.records.values()].filter((record) => matches(record, filter)), this.activeCallIds, this.lateCallIds);
|
|
664
|
+
}
|
|
665
|
+
markCompleted(count = 1) {
|
|
666
|
+
if (!Number.isInteger(count) || count < 0) throw new ValidationError(`CostLedger.markCompleted: count must be a non-negative integer, got ${count}`);
|
|
667
|
+
if (count === 0) return;
|
|
668
|
+
this.ensureCostLimitPersisted();
|
|
669
|
+
this.appendEvent({
|
|
670
|
+
version: 1,
|
|
671
|
+
completedTasks: count
|
|
651
672
|
});
|
|
652
|
-
|
|
673
|
+
this.completedTasks += count;
|
|
653
674
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
675
|
+
costPerCompletedTask() {
|
|
676
|
+
return this.completedTasks === 0 ? null : this.summary().totalCostUsd / this.completedTasks;
|
|
677
|
+
}
|
|
678
|
+
async execute(input, pending) {
|
|
679
|
+
const signal = input.signal ?? new AbortController().signal;
|
|
680
|
+
if (signal.aborted) return this.commitOutcome(pending, abortError(signal), {
|
|
681
|
+
model: pending.model,
|
|
682
|
+
inputTokens: 0,
|
|
683
|
+
outputTokens: 0,
|
|
684
|
+
actualCostUsd: 0
|
|
662
685
|
});
|
|
663
|
-
|
|
686
|
+
const operation = Promise.resolve().then(() => input.execute(signal, pending.callId));
|
|
687
|
+
const settled = await settle(operation, signal);
|
|
688
|
+
if (settled.kind === "aborted") {
|
|
689
|
+
this.lateCallIds.add(pending.callId);
|
|
690
|
+
this.captureLateOutcome(input, pending, operation);
|
|
691
|
+
return paidFailure(pending.callId, abortError(signal));
|
|
692
|
+
}
|
|
693
|
+
if (settled.kind === "error") {
|
|
694
|
+
let observed;
|
|
695
|
+
try {
|
|
696
|
+
observed = input.receiptFromError?.(settled.error);
|
|
697
|
+
} catch (receiptError) {
|
|
698
|
+
return this.captureFailure(pending, settled.error, receiptError);
|
|
699
|
+
}
|
|
700
|
+
return this.commitOutcome(pending, settled.error, observed ?? unknownReceipt(pending.model));
|
|
701
|
+
}
|
|
702
|
+
try {
|
|
703
|
+
const receipt = this.commitReceipt(pending, input.receipt(settled.value));
|
|
704
|
+
if (receipt.maximumCostUsd !== void 0 && receipt.costUsd > receipt.maximumCostUsd) return paidFailure(pending.callId, new CostReservationExceededError(pending.actor, receipt.costUsd, receipt.maximumCostUsd), receipt);
|
|
705
|
+
return {
|
|
706
|
+
succeeded: true,
|
|
707
|
+
callId: pending.callId,
|
|
708
|
+
value: settled.value,
|
|
709
|
+
receipt
|
|
710
|
+
};
|
|
711
|
+
} catch (receiptError) {
|
|
712
|
+
if (receiptError instanceof CostLedgerPersistenceError || receiptError instanceof CostCallConflictError) return paidFailure(pending.callId, receiptError);
|
|
713
|
+
return this.captureFailure(pending, receiptError, receiptError);
|
|
714
|
+
}
|
|
664
715
|
}
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
z.strictObject({
|
|
681
|
-
version: z.literal(2),
|
|
682
|
-
record: CostReceiptSchema
|
|
683
|
-
}),
|
|
684
|
-
z.strictObject({
|
|
685
|
-
version: z.literal(1),
|
|
686
|
-
completedTasks: TokenCount
|
|
687
|
-
}),
|
|
688
|
-
z.strictObject({
|
|
689
|
-
version: z.literal(1),
|
|
690
|
-
costCeilingUsd: NonNegative
|
|
691
|
-
})
|
|
692
|
-
]);
|
|
693
|
-
function parseEvents(serialized) {
|
|
694
|
-
const records = /* @__PURE__ */ new Map();
|
|
695
|
-
let completedTasks = 0;
|
|
696
|
-
let costCeilingUsd;
|
|
697
|
-
let lineNumber = 0;
|
|
698
|
-
try {
|
|
699
|
-
for (const line of completeJournalPrefix(serialized).split("\n")) {
|
|
700
|
-
if (!line.trim()) continue;
|
|
701
|
-
lineNumber += 1;
|
|
702
|
-
const event = CostLedgerEventSchema.parse(JSON.parse(line));
|
|
703
|
-
if ("record" in event) {
|
|
704
|
-
const record = event.record;
|
|
705
|
-
validateTransition(records, record);
|
|
706
|
-
records.set(record.callId, cloneRecord(record));
|
|
707
|
-
} else if ("completedTasks" in event) {
|
|
708
|
-
completedTasks += event.completedTasks;
|
|
709
|
-
if (!Number.isSafeInteger(completedTasks)) throw new ValidationError("CostLedger: completed task count exceeds safe integer range");
|
|
710
|
-
} else {
|
|
711
|
-
if (costCeilingUsd !== void 0) throw new ValidationError("CostLedger: duplicate persisted cost ceiling");
|
|
712
|
-
costCeilingUsd = event.costCeilingUsd;
|
|
716
|
+
captureLateOutcome(input, pending, operation) {
|
|
717
|
+
operation.then((value) => {
|
|
718
|
+
if (this.records.get(pending.callId)?.status !== "pending") return;
|
|
719
|
+
try {
|
|
720
|
+
this.commitReceipt(pending, input.receipt(value));
|
|
721
|
+
} catch {}
|
|
722
|
+
}, (cause) => {
|
|
723
|
+
if (this.records.get(pending.callId)?.status !== "pending") return;
|
|
724
|
+
const error = toError(cause);
|
|
725
|
+
try {
|
|
726
|
+
const observed = input.receiptFromError?.(error);
|
|
727
|
+
this.commitReceipt(pending, observed ?? unknownReceipt(pending.model), true);
|
|
728
|
+
} catch (receiptError) {
|
|
729
|
+
if (receiptError instanceof CostLedgerPersistenceError || receiptError instanceof CostCallConflictError) return;
|
|
730
|
+
this.captureFailure(pending, error, receiptError);
|
|
713
731
|
}
|
|
732
|
+
}).finally(() => {
|
|
733
|
+
this.lateCallIds.delete(pending.callId);
|
|
734
|
+
this.releaseActiveCall(pending.callId);
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
releaseActiveCall(callId) {
|
|
738
|
+
this.activeCallIds.delete(callId);
|
|
739
|
+
for (const resolve of [...this.idleWaiters]) resolve();
|
|
740
|
+
}
|
|
741
|
+
async waitForReservationRelease(signal) {
|
|
742
|
+
if (signal?.aborted) throw abortError(signal);
|
|
743
|
+
await new Promise((resolve, reject) => {
|
|
744
|
+
let finished = false;
|
|
745
|
+
const finish = (error) => {
|
|
746
|
+
if (finished) return;
|
|
747
|
+
finished = true;
|
|
748
|
+
this.idleWaiters.delete(onRelease);
|
|
749
|
+
signal?.removeEventListener("abort", onAbort);
|
|
750
|
+
if (error) reject(error);
|
|
751
|
+
else resolve();
|
|
752
|
+
};
|
|
753
|
+
const onRelease = () => finish();
|
|
754
|
+
const onAbort = () => finish(abortError(signal));
|
|
755
|
+
this.idleWaiters.add(onRelease);
|
|
756
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
757
|
+
if (this.activeCallIds.size === 0) onRelease();
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
commitOutcome(pending, error, observed) {
|
|
761
|
+
try {
|
|
762
|
+
const receipt = this.commitReceipt(pending, observed, true);
|
|
763
|
+
return paidFailure(pending.callId, error, receipt);
|
|
764
|
+
} catch (receiptError) {
|
|
765
|
+
if (receiptError instanceof CostLedgerPersistenceError || receiptError instanceof CostCallConflictError) return paidFailure(pending.callId, receiptError);
|
|
766
|
+
return this.captureFailure(pending, error, receiptError);
|
|
714
767
|
}
|
|
715
|
-
return {
|
|
716
|
-
records: [...records.values()],
|
|
717
|
-
completedTasks,
|
|
718
|
-
...costCeilingUsd === void 0 ? {} : { costCeilingUsd }
|
|
719
|
-
};
|
|
720
|
-
} catch (cause) {
|
|
721
|
-
throw new ValidationError(`CostLedger: invalid persisted event ${lineNumber || 1}: ${validationMessage(cause)}`, { cause });
|
|
722
768
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
return finalNewline < 0 ? "" : serialized.slice(0, finalNewline + 1);
|
|
769
|
+
captureFailure(pending, cause, receiptError) {
|
|
770
|
+
try {
|
|
771
|
+
const receipt = this.commitReceipt(pending, unknownReceipt(pending.model), true);
|
|
772
|
+
return paidFailure(pending.callId, new CostReceiptCaptureError(pending.callId, cause, receiptError, receipt), receipt);
|
|
773
|
+
} catch (error) {
|
|
774
|
+
const typed = error instanceof Error ? error : toError(error);
|
|
775
|
+
return paidFailure(pending.callId, typed);
|
|
776
|
+
}
|
|
732
777
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
if (record.status === "pending" || current.status === "settled") throw new CostCallConflictError(`CostLedger: duplicate callId '${record.callId}'`);
|
|
739
|
-
if (!sameAttribution(current, record)) throw new ValidationError(`CostLedger: receipt attribution changed for call '${record.callId}'`);
|
|
740
|
-
}
|
|
741
|
-
function sameAttribution(before, after) {
|
|
742
|
-
return before.channel === after.channel && before.phase === after.phase && before.actor === after.actor && before.maximumCostUsd === after.maximumCostUsd && before.timestamp === after.timestamp && sameTags(before.tags, after.tags);
|
|
743
|
-
}
|
|
744
|
-
function sameTags(left, right) {
|
|
745
|
-
const leftEntries = Object.entries(left ?? {}).sort(([a], [b]) => a.localeCompare(b));
|
|
746
|
-
const rightEntries = Object.entries(right ?? {}).sort(([a], [b]) => a.localeCompare(b));
|
|
747
|
-
return JSON.stringify(leftEntries) === JSON.stringify(rightEntries);
|
|
748
|
-
}
|
|
749
|
-
function parseReceipt(value, path) {
|
|
750
|
-
try {
|
|
751
|
-
return CostReceiptSchema.parse(value);
|
|
752
|
-
} catch (cause) {
|
|
753
|
-
throw new ValidationError(`CostLedger: invalid ${path}: ${validationMessage(cause)}`, { cause });
|
|
778
|
+
commitReceipt(pending, observed, failed = false, error) {
|
|
779
|
+
const receipt = buildReceipt(pending, observed, failed, error);
|
|
780
|
+
if (this.records.get(pending.callId)?.status !== "pending") throw new CostCallConflictError(`CostLedger: call '${pending.callId}' is not pending`);
|
|
781
|
+
this.appendRecord(receipt);
|
|
782
|
+
return cloneReceipt(receipt);
|
|
754
783
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
784
|
+
resolveMaximum(maximum) {
|
|
785
|
+
if (!maximum) {
|
|
786
|
+
if (this.costCeilingUsd !== void 0) throw new CostAccountingIncompleteError("CostLedger: capped paid calls require a hard maximumCharge before execution");
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
if ("externallyEnforcedMaximumUsd" in maximum) {
|
|
790
|
+
assertNonNegative(maximum.externallyEnforcedMaximumUsd, "maximumCharge.externallyEnforcedMaximumUsd");
|
|
791
|
+
return maximum.externallyEnforcedMaximumUsd;
|
|
792
|
+
}
|
|
793
|
+
if ("customTokenPricing" in maximum) return costForTokenPricing(maximum.customTokenPricing, maximum);
|
|
794
|
+
const priced = costForUsage(maximum.model, maximum);
|
|
795
|
+
if (priced.costUnknown) {
|
|
796
|
+
if (this.costCeilingUsd !== void 0) throw new CostAccountingIncompleteError(`CostLedger: cannot reserve unpriced model '${maximum.model}' in a capped run`);
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
return priced.costUsd;
|
|
800
|
+
}
|
|
801
|
+
/** Compute admission totals in record order without building reporting rollups. */
|
|
802
|
+
admissionState() {
|
|
803
|
+
let unresolvedCalls = 0;
|
|
804
|
+
let reservedCostUsd = 0;
|
|
805
|
+
let committedCostUsd = 0;
|
|
806
|
+
let budgetBreached = false;
|
|
807
|
+
for (const record of this.records.values()) if (record.status === "pending") {
|
|
808
|
+
if (!this.activeCallIds.has(record.callId) || this.lateCallIds.has(record.callId)) unresolvedCalls += 1;
|
|
809
|
+
reservedCostUsd += record.maximumCostUsd ?? 0;
|
|
810
|
+
} else {
|
|
811
|
+
if (record.maximumCostUsd !== void 0 && record.costUsd > record.maximumCostUsd) budgetBreached = true;
|
|
812
|
+
committedCostUsd += record.costUnknown ? record.maximumCostUsd ?? record.costUsd : record.costUsd;
|
|
813
|
+
}
|
|
814
|
+
return {
|
|
815
|
+
unresolvedCalls,
|
|
816
|
+
reservedCostUsd,
|
|
817
|
+
committedCostUsd,
|
|
818
|
+
budgetBreached
|
|
764
819
|
};
|
|
765
820
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
821
|
+
appendRecord(record) {
|
|
822
|
+
const callId = record.callId;
|
|
823
|
+
const receipt = record.status === "settled" ? record : void 0;
|
|
824
|
+
validateTransition(this.records, record);
|
|
825
|
+
const event = record.status === "settled" ? {
|
|
826
|
+
version: 2,
|
|
827
|
+
record: cloneReceipt(record)
|
|
828
|
+
} : {
|
|
829
|
+
version: 1,
|
|
830
|
+
record: clonePendingCall(record)
|
|
831
|
+
};
|
|
832
|
+
this.appendEvent(event, callId, receipt);
|
|
833
|
+
this.records.set(callId, cloneRecord(record));
|
|
776
834
|
}
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
835
|
+
ensureCostLimitPersisted(callId) {
|
|
836
|
+
if (this.costLimitPersisted || this.costCeilingUsd === void 0) return;
|
|
837
|
+
this.appendEvent({
|
|
838
|
+
version: 1,
|
|
839
|
+
costCeilingUsd: this.costCeilingUsd
|
|
840
|
+
}, callId);
|
|
841
|
+
this.costLimitPersisted = true;
|
|
842
|
+
}
|
|
843
|
+
appendEvent(event, callId, receipt) {
|
|
844
|
+
try {
|
|
845
|
+
if (this.persistence) {
|
|
846
|
+
const nextRevision = this.persistence.append(this.revision, `${JSON.stringify(event)}\n`);
|
|
847
|
+
if (nextRevision === void 0) throw new CostCallConflictError(`CostLedger: persisted revision changed while writing call '${callId}'`, {
|
|
848
|
+
callId,
|
|
849
|
+
receipt
|
|
850
|
+
});
|
|
851
|
+
assertString(nextRevision, "persistence revision");
|
|
852
|
+
this.revision = nextRevision;
|
|
853
|
+
}
|
|
854
|
+
} catch (cause) {
|
|
855
|
+
if (cause instanceof CostCallConflictError) throw cause;
|
|
856
|
+
throw new CostLedgerPersistenceError(cause, callId, receipt);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
async function settle(promise, signal) {
|
|
861
|
+
return await new Promise((resolve) => {
|
|
862
|
+
let done = false;
|
|
863
|
+
const finish = (value) => {
|
|
864
|
+
if (done) return;
|
|
865
|
+
done = true;
|
|
866
|
+
signal.removeEventListener("abort", onAbort);
|
|
867
|
+
resolve(value);
|
|
868
|
+
};
|
|
869
|
+
const onAbort = () => finish({ kind: "aborted" });
|
|
870
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
871
|
+
if (signal.aborted) onAbort();
|
|
872
|
+
promise.then((value) => finish({
|
|
873
|
+
kind: "value",
|
|
874
|
+
value
|
|
875
|
+
}), (error) => finish({
|
|
876
|
+
kind: "error",
|
|
877
|
+
error: toError(error)
|
|
878
|
+
}));
|
|
812
879
|
});
|
|
813
|
-
return {
|
|
814
|
-
inputUsdPerThousand: pricing.inputUsdPerMillion / 1e3,
|
|
815
|
-
...pricing.cachedInputUsdPerMillion === void 0 ? {} : { cachedInputUsdPerThousand: pricing.cachedInputUsdPerMillion / 1e3 },
|
|
816
|
-
...pricing.cacheWriteUsdPerMillion === void 0 ? {} : { cacheWriteUsdPerThousand: pricing.cacheWriteUsdPerMillion / 1e3 },
|
|
817
|
-
outputUsdPerThousand: pricing.outputUsdPerMillion / 1e3
|
|
818
|
-
};
|
|
819
|
-
}
|
|
820
|
-
function emptyRollup(channel) {
|
|
821
|
-
return {
|
|
822
|
-
channel,
|
|
823
|
-
calls: 0,
|
|
824
|
-
inputTokens: 0,
|
|
825
|
-
outputTokens: 0,
|
|
826
|
-
reasoningTokens: 0,
|
|
827
|
-
cachedTokens: 0,
|
|
828
|
-
cacheWriteTokens: 0,
|
|
829
|
-
costUsd: 0,
|
|
830
|
-
unpricedCalls: 0,
|
|
831
|
-
unknownUsageCalls: 0
|
|
832
|
-
};
|
|
833
880
|
}
|
|
834
881
|
function pendingModel(input) {
|
|
835
882
|
if (input.model) return input.model;
|
|
@@ -860,9 +907,6 @@ function abortError(signal) {
|
|
|
860
907
|
error.name = "AbortError";
|
|
861
908
|
return error;
|
|
862
909
|
}
|
|
863
|
-
function toError(value) {
|
|
864
|
-
return value instanceof Error ? value : new Error(String(value));
|
|
865
|
-
}
|
|
866
910
|
function paidFailure(callId, error, receipt) {
|
|
867
911
|
return {
|
|
868
912
|
succeeded: false,
|
|
@@ -871,32 +915,7 @@ function paidFailure(callId, error, receipt) {
|
|
|
871
915
|
...receipt ? { receipt: cloneReceipt(receipt) } : {}
|
|
872
916
|
};
|
|
873
917
|
}
|
|
874
|
-
function validationMessage(cause) {
|
|
875
|
-
return cause instanceof z.ZodError ? z.prettifyError(cause) : toError(cause).message;
|
|
876
|
-
}
|
|
877
|
-
function assertUsage(usage) {
|
|
878
|
-
assertTokenCount(usage.inputTokens, "inputTokens");
|
|
879
|
-
assertTokenCount(usage.outputTokens, "outputTokens");
|
|
880
|
-
if (usage.reasoningTokens !== void 0) {
|
|
881
|
-
assertTokenCount(usage.reasoningTokens, "reasoningTokens");
|
|
882
|
-
if (usage.reasoningTokens > usage.outputTokens) throw new ValidationError("CostLedger: reasoningTokens must not exceed outputTokens");
|
|
883
|
-
}
|
|
884
|
-
if (usage.cachedTokens !== void 0) assertTokenCount(usage.cachedTokens, "cachedTokens");
|
|
885
|
-
if (usage.cacheWriteTokens !== void 0) assertTokenCount(usage.cacheWriteTokens, "cacheWriteTokens");
|
|
886
|
-
}
|
|
887
|
-
function assertTokenCount(value, name) {
|
|
888
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) throw new ValidationError(`CostLedger: ${name} must be a non-negative integer, got ${String(value)}`);
|
|
889
|
-
}
|
|
890
|
-
function assertNonNegative(value, name) {
|
|
891
|
-
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) throw new ValidationError(`CostLedger: ${name} must be a non-negative finite number, got ${String(value)}`);
|
|
892
|
-
}
|
|
893
|
-
function assertTimeout(value, name) {
|
|
894
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0 || value > 2147483647) throw new ValidationError(`CostLedger: ${name} must be a non-negative safe integer no greater than 2147483647, got ${String(value)}`);
|
|
895
|
-
}
|
|
896
|
-
function assertString(value, name) {
|
|
897
|
-
if (typeof value !== "string" || value.trim().length === 0) throw new ValidationError(`CostLedger: ${name} must be a non-empty string`);
|
|
898
|
-
}
|
|
899
918
|
//#endregion
|
|
900
|
-
export { CostLedgerPersistenceError as a, costForTokenPricing as c,
|
|
919
|
+
export { CostLedgerPersistenceError as a, costForTokenPricing as c, CostCeilingReachedError as i, costForUsage as l, CostAccountingIncompleteError as n, CostReceiptCaptureError as o, CostCallConflictError as r, CostReservationExceededError as s, CostLedger as t, modelPriceKey as u };
|
|
901
920
|
|
|
902
|
-
//# sourceMappingURL=cost-ledger-
|
|
921
|
+
//# sourceMappingURL=cost-ledger-BP8V4kUQ.js.map
|