@tangle-network/agent-eval 0.128.2 → 0.130.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +279 -0
- package/README.md +19 -0
- package/dist/active-curriculum-C4mk67HP.js +214 -0
- package/dist/active-curriculum-C4mk67HP.js.map +1 -0
- package/dist/adversarial-smnADNFS.d.ts +21 -0
- package/dist/adversarial-smnADNFS.d.ts.map +1 -0
- package/dist/analyst/index.d.ts +83 -2932
- package/dist/analyst/index.d.ts.map +1 -0
- package/dist/analyst/index.js +319 -364
- package/dist/analyst/index.js.map +1 -1
- package/dist/analyst-BkTS3C58.d.ts +89 -0
- package/dist/analyst-BkTS3C58.d.ts.map +1 -0
- package/dist/analyst-LsnNpSkm.js +152 -0
- package/dist/analyst-LsnNpSkm.js.map +1 -0
- package/dist/analyze-runs-C1CavBMk.js +1067 -0
- package/dist/analyze-runs-C1CavBMk.js.map +1 -0
- package/dist/analyze-runs-FsgYCinh.d.ts +72 -0
- package/dist/analyze-runs-FsgYCinh.d.ts.map +1 -0
- package/dist/attribute-vocabulary-DLJ6303h.d.ts +54 -0
- package/dist/attribute-vocabulary-DLJ6303h.d.ts.map +1 -0
- package/dist/authenticity/index.d.ts +81 -79
- package/dist/authenticity/index.d.ts.map +1 -0
- package/dist/authenticity/index.js +209 -193
- package/dist/authenticity/index.js.map +1 -1
- package/dist/baseline-HsBvw_dk.js +550 -0
- package/dist/baseline-HsBvw_dk.js.map +1 -0
- package/dist/baseline-hG3K85h4.d.ts +125 -0
- package/dist/baseline-hG3K85h4.d.ts.map +1 -0
- package/dist/belief-state/index.d.ts +448 -1205
- package/dist/belief-state/index.d.ts.map +1 -0
- package/dist/belief-state/index.js +1617 -1710
- package/dist/belief-state/index.js.map +1 -1
- package/dist/benchmarks/index.d.ts +2 -894
- package/dist/benchmarks/index.js +2 -59
- package/dist/benchmarks-DviOvUNr.js +754 -0
- package/dist/benchmarks-DviOvUNr.js.map +1 -0
- package/dist/builder-eval/index.d.ts +150 -662
- package/dist/builder-eval/index.d.ts.map +1 -0
- package/dist/builder-eval/index.js +356 -345
- package/dist/builder-eval/index.js.map +1 -1
- package/dist/calibration-CNWWA6K8.js +94 -0
- package/dist/calibration-CNWWA6K8.js.map +1 -0
- package/dist/campaign/index.d.ts +5 -6390
- package/dist/campaign/index.js +3 -212
- package/dist/campaign-CBKZvQ1H.js +3885 -0
- package/dist/campaign-CBKZvQ1H.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +164 -174
- package/dist/cli.js.map +1 -1
- package/dist/client-C97NMzqi.d.ts +581 -0
- package/dist/client-C97NMzqi.d.ts.map +1 -0
- package/dist/client-CYzbdJOZ.js +637 -0
- package/dist/client-CYzbdJOZ.js.map +1 -0
- package/dist/code-agent-session-BjkMTQ7H.js +1390 -0
- package/dist/code-agent-session-BjkMTQ7H.js.map +1 -0
- package/dist/code-agent-session-DqqgOJaz.d.ts +143 -0
- package/dist/code-agent-session-DqqgOJaz.d.ts.map +1 -0
- package/dist/concurrency-DIxRZF_J.js +85 -0
- package/dist/concurrency-DIxRZF_J.js.map +1 -0
- package/dist/contract/index.d.ts +645 -5605
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/index.js +1730 -1937
- package/dist/contract/index.js.map +1 -1
- package/dist/control.d.ts +3 -1030
- package/dist/control.js +2 -32
- package/dist/cost-ledger-DIgQUFZZ.js +801 -0
- package/dist/cost-ledger-DIgQUFZZ.js.map +1 -0
- package/dist/cost-ledger-Dye6jCgg.d.ts +236 -0
- package/dist/cost-ledger-Dye6jCgg.d.ts.map +1 -0
- package/dist/dataset-BvtnC8Dc.d.ts +115 -0
- package/dist/dataset-BvtnC8Dc.d.ts.map +1 -0
- package/dist/default-registry-C-vFCSEc.js +2579 -0
- package/dist/default-registry-C-vFCSEc.js.map +1 -0
- package/dist/default-registry-CNPo-Vsb.d.ts +540 -0
- package/dist/default-registry-CNPo-Vsb.d.ts.map +1 -0
- package/dist/emitter-CPBAhxum.js +266 -0
- package/dist/emitter-CPBAhxum.js.map +1 -0
- package/dist/emitter-DGQGoLyj.d.ts +113 -0
- package/dist/emitter-DGQGoLyj.d.ts.map +1 -0
- package/dist/errors-8YnH8WlF.js +64 -0
- package/dist/errors-8YnH8WlF.js.map +1 -0
- package/dist/errors-CEk209JS.d.ts +76 -0
- package/dist/errors-CEk209JS.d.ts.map +1 -0
- package/dist/eval-campaign-DEm6c8ru.js +349 -0
- package/dist/eval-campaign-DEm6c8ru.js.map +1 -0
- package/dist/execution-tracks-CpgFPpS5.js +93 -0
- package/dist/execution-tracks-CpgFPpS5.js.map +1 -0
- package/dist/exporters-q9iL-2Jf.js +148 -0
- package/dist/exporters-q9iL-2Jf.js.map +1 -0
- package/dist/extract-usage-BrQ8mCLX.js +155 -0
- package/dist/extract-usage-BrQ8mCLX.js.map +1 -0
- package/dist/failure-cluster-CqcvCcdR.d.ts +59 -0
- package/dist/failure-cluster-CqcvCcdR.d.ts.map +1 -0
- package/dist/feedback-trajectory-CVaeREXV.d.ts +340 -0
- package/dist/feedback-trajectory-CVaeREXV.d.ts.map +1 -0
- package/dist/fuzz.d.ts +320 -646
- package/dist/fuzz.d.ts.map +1 -0
- package/dist/fuzz.js +670 -617
- package/dist/fuzz.js.map +1 -1
- package/dist/hf-dataset-DBJXXoY1.js +763 -0
- package/dist/hf-dataset-DBJXXoY1.js.map +1 -0
- package/dist/hosted/index.d.ts +11 -831
- package/dist/hosted/index.d.ts.map +1 -0
- package/dist/hosted/index.js +2 -37
- package/dist/index-2JJSA6-r2.d.ts +926 -0
- package/dist/index-2JJSA6-r2.d.ts.map +1 -0
- package/dist/index-6N0aYmpW.d.ts +217 -0
- package/dist/index-6N0aYmpW.d.ts.map +1 -0
- package/dist/index-BAvgST_9.d.ts +131 -0
- package/dist/index-BAvgST_9.d.ts.map +1 -0
- package/dist/index-BvnJuTGD.d.ts +68 -0
- package/dist/index-BvnJuTGD.d.ts.map +1 -0
- package/dist/index-C61Wi7yg.d.ts +547 -0
- package/dist/index-C61Wi7yg.d.ts.map +1 -0
- package/dist/index-CAPUUKaM.d.ts +335 -0
- package/dist/index-CAPUUKaM.d.ts.map +1 -0
- package/dist/index-DE5fb3EC.d.ts +2244 -0
- package/dist/index-DE5fb3EC.d.ts.map +1 -0
- package/dist/index-DSC51roc.d.ts +102 -0
- package/dist/index-DSC51roc.d.ts.map +1 -0
- package/dist/index.d.ts +3776 -15120
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11185 -11191
- package/dist/index.js.map +1 -1
- package/dist/integrity-BzRbCHzi.js +424 -0
- package/dist/integrity-BzRbCHzi.js.map +1 -0
- package/dist/integrity-rmVhXWA7.d.ts +61 -0
- package/dist/integrity-rmVhXWA7.d.ts.map +1 -0
- package/dist/judge-calibration-DFtEMlde.d.ts +146 -0
- package/dist/judge-calibration-DFtEMlde.d.ts.map +1 -0
- package/dist/ledger-core/index.d.ts +2 -0
- package/dist/ledger-core/index.js +2 -0
- package/dist/ledger-core-DtZz1RG0.js +388 -0
- package/dist/ledger-core-DtZz1RG0.js.map +1 -0
- package/dist/llm-client--GR4JbZE.js +687 -0
- package/dist/llm-client--GR4JbZE.js.map +1 -0
- package/dist/llm-client-B_nIBlYo.d.ts +290 -0
- package/dist/llm-client-B_nIBlYo.d.ts.map +1 -0
- package/dist/matrix/index.d.ts +3 -155
- package/dist/matrix/index.js +2 -8
- package/dist/matrix-BzQnu2S6.js +270 -0
- package/dist/matrix-BzQnu2S6.js.map +1 -0
- package/dist/meta-eval/index.d.ts +4 -1027
- package/dist/meta-eval/index.js +393 -390
- package/dist/meta-eval/index.js.map +1 -1
- package/dist/metrics-C9YY1OcL.js +239 -0
- package/dist/metrics-C9YY1OcL.js.map +1 -0
- package/dist/mint-yN2M2eh0.js +201 -0
- package/dist/mint-yN2M2eh0.js.map +1 -0
- package/dist/multi-layer-verifier-BHY1gWAc.d.ts +138 -0
- package/dist/multi-layer-verifier-BHY1gWAc.d.ts.map +1 -0
- package/dist/multishot/index.d.ts +273 -481
- package/dist/multishot/index.d.ts.map +1 -0
- package/dist/multishot/index.js +595 -548
- package/dist/multishot/index.js.map +1 -1
- package/dist/off-policy-DvgzvtIx.js +220 -0
- package/dist/off-policy-DvgzvtIx.js.map +1 -0
- package/dist/off-policy-mskQw8Mb.d.ts +153 -0
- package/dist/off-policy-mskQw8Mb.d.ts.map +1 -0
- package/dist/openapi.json +1 -1
- package/dist/opencode-sqlite-BGrHeDu3.js +318 -0
- package/dist/opencode-sqlite-BGrHeDu3.js.map +1 -0
- package/dist/outcome-store-BYHIuO0e.d.ts +65 -0
- package/dist/outcome-store-BYHIuO0e.d.ts.map +1 -0
- package/dist/outcome-store-ChBKlTd_.js +75 -0
- package/dist/outcome-store-ChBKlTd_.js.map +1 -0
- package/dist/paired-arms-D9D0wXj2.js +260 -0
- package/dist/paired-arms-D9D0wXj2.js.map +1 -0
- package/dist/pipelines/index.d.ts +95 -532
- package/dist/pipelines/index.d.ts.map +1 -0
- package/dist/pipelines/index.js +497 -478
- package/dist/pipelines/index.js.map +1 -1
- package/dist/pre-registration-DakwTRXk.js +96 -0
- package/dist/pre-registration-DakwTRXk.js.map +1 -0
- package/dist/propose-review-control-LhIWGzHl.js +1458 -0
- package/dist/propose-review-control-LhIWGzHl.js.map +1 -0
- package/dist/query-CJ_DX8vl.d.ts +28 -0
- package/dist/query-CJ_DX8vl.d.ts.map +1 -0
- package/dist/query-Di7eEQ79.js +83 -0
- package/dist/query-Di7eEQ79.js.map +1 -0
- package/dist/raw-provider-sink-BQd7mzyT.js +201 -0
- package/dist/raw-provider-sink-BQd7mzyT.js.map +1 -0
- package/dist/raw-provider-sink-BU29Sh8h.d.ts +134 -0
- package/dist/raw-provider-sink-BU29Sh8h.d.ts.map +1 -0
- package/dist/redact-7Aq1ukl-.js +107 -0
- package/dist/redact-7Aq1ukl-.js.map +1 -0
- package/dist/release-report-Cz9NKH39.js +603 -0
- package/dist/release-report-Cz9NKH39.js.map +1 -0
- package/dist/release-report-mvB2G4_J.d.ts +244 -0
- package/dist/release-report-mvB2G4_J.d.ts.map +1 -0
- package/dist/replay-GnyotH0J.js +1741 -0
- package/dist/replay-GnyotH0J.js.map +1 -0
- package/dist/replay-OoidtG1E.d.ts +749 -0
- package/dist/replay-OoidtG1E.d.ts.map +1 -0
- package/dist/reporting.d.ts +6 -1298
- package/dist/reporting.js +6 -50
- package/dist/researcher-CwTdwXG1.d.ts +314 -0
- package/dist/researcher-CwTdwXG1.d.ts.map +1 -0
- package/dist/reward-hacking-eAnOsynk.d.ts +224 -0
- package/dist/reward-hacking-eAnOsynk.d.ts.map +1 -0
- package/dist/reward-hacking-qipEpKvY.js +596 -0
- package/dist/reward-hacking-qipEpKvY.js.map +1 -0
- package/dist/reward-nw2xZGZG.js +137 -0
- package/dist/reward-nw2xZGZG.js.map +1 -0
- package/dist/rl.d.ts +916 -3596
- package/dist/rl.d.ts.map +1 -0
- package/dist/rl.js +2362 -1751
- package/dist/rl.js.map +1 -1
- package/dist/rolldown-runtime-8H4AJuhK.js +14 -0
- package/dist/rollout/index.d.ts +3 -1048
- package/dist/rollout/index.js +8 -110
- package/dist/rollout-BOYjemfR.js +624 -0
- package/dist/rollout-BOYjemfR.js.map +1 -0
- package/dist/rubric-predictive-validity-B3xmbmS1.js +141 -0
- package/dist/rubric-predictive-validity-B3xmbmS1.js.map +1 -0
- package/dist/rubric-predictive-validity-Ku_clp_1.d.ts +74 -0
- package/dist/rubric-predictive-validity-Ku_clp_1.d.ts.map +1 -0
- package/dist/run-evidence-ClRX_8A9.d.ts +225 -0
- package/dist/run-evidence-ClRX_8A9.d.ts.map +1 -0
- package/dist/run-record-BuoE80Dq.js +467 -0
- package/dist/run-record-BuoE80Dq.js.map +1 -0
- package/dist/run-record-CnZu_gjl.d.ts +357 -0
- package/dist/run-record-CnZu_gjl.d.ts.map +1 -0
- package/dist/run-score-iEEAWiBY.js +41 -0
- package/dist/run-score-iEEAWiBY.js.map +1 -0
- package/dist/runtime-trajectory-1gyaTOoC.js +93 -0
- package/dist/runtime-trajectory-1gyaTOoC.js.map +1 -0
- package/dist/runtime-trajectory-BvSZcCHD.d.ts +50 -0
- package/dist/runtime-trajectory-BvSZcCHD.d.ts.map +1 -0
- package/dist/schema-BtVldJ3T.d.ts +206 -0
- package/dist/schema-BtVldJ3T.d.ts.map +1 -0
- package/dist/schema-C6DW4ZHR.js +821 -0
- package/dist/schema-C6DW4ZHR.js.map +1 -0
- package/dist/schema-CRhEY1SO.js +69 -0
- package/dist/schema-CRhEY1SO.js.map +1 -0
- package/dist/schema-Cef2cFmb.d.ts +408 -0
- package/dist/schema-Cef2cFmb.d.ts.map +1 -0
- package/dist/semantic-concept-judge-B6cWNJ2K.js +725 -0
- package/dist/semantic-concept-judge-B6cWNJ2K.js.map +1 -0
- package/dist/sequential-Br0mAPHA.js +148 -0
- package/dist/sequential-Br0mAPHA.js.map +1 -0
- package/dist/sequential-CYwq6Ff_.d.ts +141 -0
- package/dist/sequential-CYwq6Ff_.d.ts.map +1 -0
- package/dist/series-convergence-CjO2QdRW.js +43 -0
- package/dist/series-convergence-CjO2QdRW.js.map +1 -0
- package/dist/series-convergence-ofsqPWhs.d.ts +35 -0
- package/dist/series-convergence-ofsqPWhs.d.ts.map +1 -0
- package/dist/server-m5D9cvnG.js +1040 -0
- package/dist/server-m5D9cvnG.js.map +1 -0
- package/dist/skill-usage-C_pXm7lP.d.ts +534 -0
- package/dist/skill-usage-C_pXm7lP.d.ts.map +1 -0
- package/dist/skillopt-optimization-method-B7wX7XkF.d.ts +1739 -0
- package/dist/skillopt-optimization-method-B7wX7XkF.d.ts.map +1 -0
- package/dist/skillopt-optimization-method-D4ODwFVV.js +7712 -0
- package/dist/skillopt-optimization-method-D4ODwFVV.js.map +1 -0
- package/dist/statistics-Cmj6nynr.d.ts +514 -0
- package/dist/statistics-Cmj6nynr.d.ts.map +1 -0
- package/dist/statistics-CnnxdpOg.js +1437 -0
- package/dist/statistics-CnnxdpOg.js.map +1 -0
- package/dist/store-CT9YIIve.d.ts +117 -0
- package/dist/store-CT9YIIve.d.ts.map +1 -0
- package/dist/store-CxJry_cs.d.ts +229 -0
- package/dist/store-CxJry_cs.d.ts.map +1 -0
- package/dist/storyboard/index.d.ts +84 -203
- package/dist/storyboard/index.d.ts.map +1 -0
- package/dist/storyboard/index.js +609 -542
- package/dist/storyboard/index.js.map +1 -1
- package/dist/summary-report-BNs5nmXI.js +862 -0
- package/dist/summary-report-BNs5nmXI.js.map +1 -0
- package/dist/summary-report-Cj9gdw4i.d.ts +407 -0
- package/dist/summary-report-Cj9gdw4i.d.ts.map +1 -0
- package/dist/supervisor-run/index.d.ts +2 -849
- package/dist/supervisor-run/index.js +2 -64
- package/dist/supervisor-run-_lnTLM3z.js +1679 -0
- package/dist/supervisor-run-_lnTLM3z.js.map +1 -0
- package/dist/task-failure-attributes-CQZlB3et.js +311 -0
- package/dist/task-failure-attributes-CQZlB3et.js.map +1 -0
- package/dist/test-graded-scenario-BsqWLmPt.js +318 -0
- package/dist/test-graded-scenario-BsqWLmPt.js.map +1 -0
- package/dist/test-graded-scenario-D1TaI2va.d.ts +141 -0
- package/dist/test-graded-scenario-D1TaI2va.d.ts.map +1 -0
- package/dist/tools-BmuN627J.js +1085 -0
- package/dist/tools-BmuN627J.js.map +1 -0
- package/dist/trace-attributes.d.ts +2 -52
- package/dist/trace-attributes.js +131 -61
- package/dist/trace-attributes.js.map +1 -1
- package/dist/traces.d.ts +12 -2365
- package/dist/traces.js +12 -251
- package/dist/trajectory-D_7rLrvE.js +56 -0
- package/dist/trajectory-D_7rLrvE.js.map +1 -0
- package/dist/types-DGsxbAEd.d.ts +387 -0
- package/dist/types-DGsxbAEd.d.ts.map +1 -0
- package/dist/types-k9tZGKUg.d.ts +640 -0
- package/dist/types-k9tZGKUg.d.ts.map +1 -0
- package/dist/verdict-Dps8_okt.d.ts +37 -0
- package/dist/verdict-Dps8_okt.d.ts.map +1 -0
- package/dist/wire/index.d.ts +702 -1174
- package/dist/wire/index.d.ts.map +1 -0
- package/dist/wire/index.js +2 -81
- package/docs/feature-guide.md +1 -1
- package/docs/rollout.md +116 -2
- package/package.json +18 -10
- package/dist/benchmarks/index.js.map +0 -1
- package/dist/campaign/index.js.map +0 -1
- package/dist/chunk-2JX3CFMB.js +0 -695
- package/dist/chunk-2JX3CFMB.js.map +0 -1
- package/dist/chunk-2MKQIFS4.js +0 -183
- package/dist/chunk-2MKQIFS4.js.map +0 -1
- package/dist/chunk-3RF76KTD.js +0 -84
- package/dist/chunk-3RF76KTD.js.map +0 -1
- package/dist/chunk-5DTSBUL2.js +0 -159
- package/dist/chunk-5DTSBUL2.js.map +0 -1
- package/dist/chunk-7ZZMD7UK.js +0 -386
- package/dist/chunk-7ZZMD7UK.js.map +0 -1
- package/dist/chunk-BOD4O7OF.js +0 -40
- package/dist/chunk-BOD4O7OF.js.map +0 -1
- package/dist/chunk-BYT7ELPS.js +0 -1553
- package/dist/chunk-BYT7ELPS.js.map +0 -1
- package/dist/chunk-DJKY2TSY.js +0 -2428
- package/dist/chunk-DJKY2TSY.js.map +0 -1
- package/dist/chunk-DPUHNQLN.js +0 -232
- package/dist/chunk-DPUHNQLN.js.map +0 -1
- package/dist/chunk-DRYIUNWY.js +0 -622
- package/dist/chunk-DRYIUNWY.js.map +0 -1
- package/dist/chunk-EJGRPCO3.js +0 -617
- package/dist/chunk-EJGRPCO3.js.map +0 -1
- package/dist/chunk-EOSZT7PL.js +0 -2001
- package/dist/chunk-EOSZT7PL.js.map +0 -1
- package/dist/chunk-EZJEIH2R.js +0 -1559
- package/dist/chunk-EZJEIH2R.js.map +0 -1
- package/dist/chunk-GGE4NNQT.js +0 -65
- package/dist/chunk-GGE4NNQT.js.map +0 -1
- package/dist/chunk-HHWE3POT.js +0 -94
- package/dist/chunk-HHWE3POT.js.map +0 -1
- package/dist/chunk-IHQDPH7D.js +0 -171
- package/dist/chunk-IHQDPH7D.js.map +0 -1
- package/dist/chunk-JHCHEVET.js +0 -274
- package/dist/chunk-JHCHEVET.js.map +0 -1
- package/dist/chunk-K4DBDHLK.js +0 -158
- package/dist/chunk-K4DBDHLK.js.map +0 -1
- package/dist/chunk-K6N6XJJX.js +0 -306
- package/dist/chunk-K6N6XJJX.js.map +0 -1
- package/dist/chunk-MA6HLL3S.js +0 -65
- package/dist/chunk-MA6HLL3S.js.map +0 -1
- package/dist/chunk-MAZ26DC7.js +0 -99
- package/dist/chunk-MAZ26DC7.js.map +0 -1
- package/dist/chunk-MHELPNRP.js +0 -1212
- package/dist/chunk-MHELPNRP.js.map +0 -1
- package/dist/chunk-NACAGYSY.js +0 -1040
- package/dist/chunk-NACAGYSY.js.map +0 -1
- package/dist/chunk-NKAGIDE2.js +0 -7633
- package/dist/chunk-NKAGIDE2.js.map +0 -1
- package/dist/chunk-NPCTHQIO.js +0 -91
- package/dist/chunk-NPCTHQIO.js.map +0 -1
- package/dist/chunk-NYLOYM6N.js +0 -332
- package/dist/chunk-NYLOYM6N.js.map +0 -1
- package/dist/chunk-ONWEPEDO.js +0 -57
- package/dist/chunk-ONWEPEDO.js.map +0 -1
- package/dist/chunk-P5W7RQKK.js +0 -576
- package/dist/chunk-P5W7RQKK.js.map +0 -1
- package/dist/chunk-P6FYH6K4.js +0 -1161
- package/dist/chunk-P6FYH6K4.js.map +0 -1
- package/dist/chunk-PBE2LOSS.js +0 -669
- package/dist/chunk-PBE2LOSS.js.map +0 -1
- package/dist/chunk-PC4UYEBM.js +0 -166
- package/dist/chunk-PC4UYEBM.js.map +0 -1
- package/dist/chunk-PXE2VKMX.js +0 -140
- package/dist/chunk-PXE2VKMX.js.map +0 -1
- package/dist/chunk-PZ5AY32C.js +0 -10
- package/dist/chunk-PZ5AY32C.js.map +0 -1
- package/dist/chunk-RZTMDUO7.js +0 -49
- package/dist/chunk-RZTMDUO7.js.map +0 -1
- package/dist/chunk-S5YLIBFX.js +0 -136
- package/dist/chunk-S5YLIBFX.js.map +0 -1
- package/dist/chunk-SZLVEKMJ.js +0 -1446
- package/dist/chunk-SZLVEKMJ.js.map +0 -1
- package/dist/chunk-T4SQEITX.js +0 -95
- package/dist/chunk-T4SQEITX.js.map +0 -1
- package/dist/chunk-TBL77AUT.js +0 -355
- package/dist/chunk-TBL77AUT.js.map +0 -1
- package/dist/chunk-TSN7JT6D.js +0 -1646
- package/dist/chunk-TSN7JT6D.js.map +0 -1
- package/dist/chunk-TT4KNT67.js +0 -124
- package/dist/chunk-TT4KNT67.js.map +0 -1
- package/dist/chunk-UB2LOJ6Q.js +0 -4461
- package/dist/chunk-UB2LOJ6Q.js.map +0 -1
- package/dist/chunk-UWZZKKU7.js +0 -237
- package/dist/chunk-UWZZKKU7.js.map +0 -1
- package/dist/chunk-VBQ3CRKH.js +0 -291
- package/dist/chunk-VBQ3CRKH.js.map +0 -1
- package/dist/chunk-VGRCHJON.js +0 -163
- package/dist/chunk-VGRCHJON.js.map +0 -1
- package/dist/chunk-VI2UW6B6.js +0 -162
- package/dist/chunk-VI2UW6B6.js.map +0 -1
- package/dist/chunk-VLOATJQ2.js +0 -908
- package/dist/chunk-VLOATJQ2.js.map +0 -1
- package/dist/chunk-VQMK5FMP.js +0 -247
- package/dist/chunk-VQMK5FMP.js.map +0 -1
- package/dist/chunk-VZSRQ272.js +0 -149
- package/dist/chunk-VZSRQ272.js.map +0 -1
- package/dist/chunk-WGXIEX7P.js +0 -116
- package/dist/chunk-WGXIEX7P.js.map +0 -1
- package/dist/chunk-WS3NZZQQ.js +0 -929
- package/dist/chunk-WS3NZZQQ.js.map +0 -1
- package/dist/chunk-XDWDC2MP.js +0 -695
- package/dist/chunk-XDWDC2MP.js.map +0 -1
- package/dist/chunk-XPRT64IE.js +0 -766
- package/dist/chunk-XPRT64IE.js.map +0 -1
- package/dist/chunk-YJBNWCAA.js +0 -1056
- package/dist/chunk-YJBNWCAA.js.map +0 -1
- package/dist/chunk-ZET2UAYW.js +0 -89
- package/dist/chunk-ZET2UAYW.js.map +0 -1
- package/dist/chunk-ZUUWPZCV.js +0 -752
- package/dist/chunk-ZUUWPZCV.js.map +0 -1
- package/dist/control.js.map +0 -1
- package/dist/hosted/index.js.map +0 -1
- package/dist/matrix/index.js.map +0 -1
- package/dist/reporting.js.map +0 -1
- package/dist/rollout/index.js.map +0 -1
- package/dist/run-campaign-ISHFZ7FJ.js +0 -17
- package/dist/run-campaign-ISHFZ7FJ.js.map +0 -1
- package/dist/supervisor-run/index.js.map +0 -1
- package/dist/traces.js.map +0 -1
- package/dist/wire/index.js.map +0 -1
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
//#region src/judge-calibration.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Judge calibration — measure judge quality against human gold + bias.
|
|
4
|
+
*
|
|
5
|
+
* Workflow:
|
|
6
|
+
* 1. Build a golden set: {itemId, humanScore}[].
|
|
7
|
+
* 2. Run candidate judges; each produces {itemId, score}.
|
|
8
|
+
* 3. `calibrateJudge(golden, candidate)` reports κ + Pearson + MAE.
|
|
9
|
+
* 4. `calibrateJudgeContinuous(golden, candidate)` adds quadratic-weighted
|
|
10
|
+
* κ over the un-rounded [0,1] scores plus ICC(2,1), Pearson, Spearman,
|
|
11
|
+
* and bootstrap CIs — use this for fine-grained judges where rounding
|
|
12
|
+
* to int discards information (e.g. 0.78 vs 0.81 both round to 1 and
|
|
13
|
+
* look "perfectly agreed" to integer κ).
|
|
14
|
+
* 5. Run bias probes (positional, verbosity, self-preference) to
|
|
15
|
+
* detect systematic score inflation.
|
|
16
|
+
* 6. For N≥2 judges on the same items, `continuousAgreement(scores)`
|
|
17
|
+
* reports ICC(2,1) + κ_w + Pearson + Spearman with bootstrap CIs.
|
|
18
|
+
*
|
|
19
|
+
* Returns actionable diagnostics, not a single number. Consumers then
|
|
20
|
+
* decide whether to trust the judge, retrain it, or add a tie-breaker.
|
|
21
|
+
*/
|
|
22
|
+
interface GoldenItem {
|
|
23
|
+
itemId: string;
|
|
24
|
+
humanScore: number;
|
|
25
|
+
/** Optional group used for per-group bias audits (e.g. model-of-output family). */
|
|
26
|
+
group?: string;
|
|
27
|
+
}
|
|
28
|
+
interface CandidateScore {
|
|
29
|
+
itemId: string;
|
|
30
|
+
score: number;
|
|
31
|
+
/** Optional — enables positional-bias analysis (did order matter?). */
|
|
32
|
+
positionOfAInput?: 'first' | 'second';
|
|
33
|
+
}
|
|
34
|
+
interface CalibrationResult {
|
|
35
|
+
n: number;
|
|
36
|
+
pearson: number;
|
|
37
|
+
/** Cohen's κ with quadratic weights over integer-rounded scores. */
|
|
38
|
+
kappa: number;
|
|
39
|
+
/** Mean absolute error vs human. */
|
|
40
|
+
mae: number;
|
|
41
|
+
/** Worst-5 miscalibrations (largest |judge - human|). */
|
|
42
|
+
worstItems: Array<{
|
|
43
|
+
itemId: string;
|
|
44
|
+
judge: number;
|
|
45
|
+
human: number;
|
|
46
|
+
delta: number;
|
|
47
|
+
}>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Measure judge quality against human gold labels: computes Cohen's κ, Pearson correlation, and MAE over matched item ids.
|
|
51
|
+
*/
|
|
52
|
+
declare function calibrateJudge(golden: GoldenItem[], candidate: CandidateScore[]): CalibrationResult;
|
|
53
|
+
interface PositionalBiasResult {
|
|
54
|
+
/**
|
|
55
|
+
* Score delta (first-position - second-position) averaged across items
|
|
56
|
+
* presented in both positions. Non-zero = positional bias.
|
|
57
|
+
*/
|
|
58
|
+
avgDelta: number;
|
|
59
|
+
n: number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Feed the same items to the judge twice with A/B swapped and pass all
|
|
63
|
+
* results here. Items that don't appear in both positions are ignored.
|
|
64
|
+
*/
|
|
65
|
+
declare function positionalBias(scores: CandidateScore[]): PositionalBiasResult;
|
|
66
|
+
interface VerbosityBiasResult {
|
|
67
|
+
/** Pearson correlation between output length and score. Strong positive = verbosity bias. */
|
|
68
|
+
pearson: number;
|
|
69
|
+
n: number;
|
|
70
|
+
}
|
|
71
|
+
declare function verbosityBias(samples: Array<{
|
|
72
|
+
outputLen: number;
|
|
73
|
+
score: number;
|
|
74
|
+
}>): VerbosityBiasResult;
|
|
75
|
+
interface SelfPreferenceResult {
|
|
76
|
+
/** Mean judge score when judge's family matches output's family. */
|
|
77
|
+
inFamilyMean: number;
|
|
78
|
+
outOfFamilyMean: number;
|
|
79
|
+
deltaMean: number;
|
|
80
|
+
n: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Pass the same scenarios scored with judge-model X grading outputs from
|
|
84
|
+
* model X (in-family) and model Y (out-of-family). Non-zero delta
|
|
85
|
+
* indicates self-preference.
|
|
86
|
+
*/
|
|
87
|
+
declare function selfPreference(samples: Array<{
|
|
88
|
+
score: number;
|
|
89
|
+
inFamily: boolean;
|
|
90
|
+
}>): SelfPreferenceResult;
|
|
91
|
+
interface ContinuousAgreement {
|
|
92
|
+
/** Cohen's κ_w with quadratic weights, computed on raw [0,1] scores. */
|
|
93
|
+
weightedKappa: number;
|
|
94
|
+
/** ICC(2,1): two-way random effects, absolute agreement, single rater. */
|
|
95
|
+
icc: number;
|
|
96
|
+
/** Pearson product-moment correlation (averaged over rater pairs if N>2). */
|
|
97
|
+
pearson: number;
|
|
98
|
+
/** Spearman rank correlation (averaged over rater pairs if N>2). */
|
|
99
|
+
spearman: number;
|
|
100
|
+
/** 95% bootstrap percentile CIs over items. */
|
|
101
|
+
ci: {
|
|
102
|
+
icc: [number, number];
|
|
103
|
+
weightedKappa: [number, number];
|
|
104
|
+
};
|
|
105
|
+
/** Number of complete items (no NaN across raters). */
|
|
106
|
+
n: number;
|
|
107
|
+
/** Number of raters. */
|
|
108
|
+
raters: number;
|
|
109
|
+
}
|
|
110
|
+
interface ContinuousAgreementOptions {
|
|
111
|
+
/** Bootstrap iterations. Default 1000. Set to 0 to skip CIs (CI = [NaN, NaN]). */
|
|
112
|
+
bootstrap?: number;
|
|
113
|
+
/** κ weighting scheme. Default 'quadratic'. */
|
|
114
|
+
weights?: 'linear' | 'quadratic';
|
|
115
|
+
/** PRNG seed for reproducible bootstrap. Default 0xC0FFEE. */
|
|
116
|
+
seed?: number;
|
|
117
|
+
/** Confidence level for percentile CI. Default 0.95. */
|
|
118
|
+
ciLevel?: number;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Inter-rater agreement on continuous (typically [0,1]) scores.
|
|
122
|
+
*
|
|
123
|
+
* `scores` has shape [n_items][n_raters]. Rows with any non-finite entry
|
|
124
|
+
* are dropped. Returns NaN metrics if fewer than 2 raters or 2 complete
|
|
125
|
+
* items remain.
|
|
126
|
+
*/
|
|
127
|
+
declare function continuousAgreement(scores: number[][], opts?: ContinuousAgreementOptions): ContinuousAgreement;
|
|
128
|
+
interface ContinuousCalibrationResult extends CalibrationResult {
|
|
129
|
+
/** Cohen's κ_w computed on raw (un-rounded) scores. */
|
|
130
|
+
weightedKappaContinuous: number;
|
|
131
|
+
/** ICC(2,1) treating golden + candidate as two raters. */
|
|
132
|
+
icc: number;
|
|
133
|
+
spearman: number;
|
|
134
|
+
ci: {
|
|
135
|
+
icc: [number, number];
|
|
136
|
+
weightedKappa: [number, number];
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Extends `calibrateJudge` with continuous-value agreement metrics while
|
|
141
|
+
* retaining its base calibration summary.
|
|
142
|
+
*/
|
|
143
|
+
declare function calibrateJudgeContinuous(golden: GoldenItem[], candidate: CandidateScore[], opts?: ContinuousAgreementOptions): ContinuousCalibrationResult;
|
|
144
|
+
//#endregion
|
|
145
|
+
export { ContinuousCalibrationResult as a, SelfPreferenceResult as c, calibrateJudgeContinuous as d, continuousAgreement as f, verbosityBias as h, ContinuousAgreementOptions as i, VerbosityBiasResult as l, selfPreference as m, CandidateScore as n, GoldenItem as o, positionalBias as p, ContinuousAgreement as r, PositionalBiasResult as s, CalibrationResult as t, calibrateJudge as u };
|
|
146
|
+
//# sourceMappingURL=judge-calibration-DFtEMlde.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"judge-calibration-DFtEMlde.d.ts","names":[],"sources":["../src/judge-calibration.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;UAuBiB;EACf;EACA;;EAEA;;UAGe;EACf;EACA;;EAEA;;UAGe;EACf;EACA;;EAEA;;EAEA;;EAEA,YAAY;IAAQ;IAAgB;IAAe;IAAe;;;;;;iBAMpD,eACd,QAAQ,cACR,WAAW,mBACV;UA0Bc;;;;;EAKf;EACA;;;;;;iBAOc,eAAe,QAAQ,mBAAmB;UAgBzC;;EAEf;EACA;;iBAGc,cACd,SAAS;EAAQ;EAAmB;KACnC;UAYc;;EAEf;EACA;EACA;EACA;;;;;;;iBAQc,eACd,SAAS;EAAQ;EAAe;KAC/B;UA8Ec;;EAEf;;EAEA;;EAEA;;EAEA;;EAEA;IACE;IACA;;;EAGF;;EAEA;;UAGe;;EAEf;;EAEA;;EAEA;;EAEA;;;;;;;;;iBAUc,oBACd,oBACA,OAAM,6BACL;UAqEc,oCAAoC;;EAEnD;;EAEA;EACA;EACA;IACE;IACA;;;;;;;iBAQY,yBACd,QAAQ,cACR,WAAW,kBACX,OAAM,6BACL"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { C as AtomicFileLockUnavailable, S as AtomicFileLockOwner, T as tryAcquireAtomicFileLock, _ as withLedgerFileLock, a as LedgerEventBase, b as AtomicFileLockError, c as LedgerLineContext, d as canonicalString, f as hashCanonical, g as tryWithLedgerFileLock, h as appendLedgerLine, i as LedgerEntryOf, l as LedgerProjector, m as LedgerFileContext, n as LedgerAppendResult, o as LedgerHash, p as FileLockResult, r as LedgerChainFields, s as LedgerJournalCodec, t as FileLedgerJournal, u as LedgerReplayResult, v as AtomicFileLock, w as probeAtomicFileLock, x as AtomicFileLockOptions, y as AtomicFileLockAcquisition } from "../index-BAvgST_9.js";
|
|
2
|
+
export { type AtomicFileLock, type AtomicFileLockAcquisition, AtomicFileLockError, type AtomicFileLockOptions, type AtomicFileLockOwner, type AtomicFileLockUnavailable, FileLedgerJournal, type FileLockResult, type LedgerAppendResult, type LedgerChainFields, type LedgerEntryOf, type LedgerEventBase, type LedgerFileContext, type LedgerHash, type LedgerJournalCodec, type LedgerLineContext, type LedgerProjector, type LedgerReplayResult, appendLedgerLine, canonicalString, hashCanonical, probeAtomicFileLock, tryAcquireAtomicFileLock, tryWithLedgerFileLock, withLedgerFileLock };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as tryWithLedgerFileLock, c as probeAtomicFileLock, i as appendLedgerLine, l as tryAcquireAtomicFileLock, n as canonicalString, o as withLedgerFileLock, r as hashCanonical, s as AtomicFileLockError, t as FileLedgerJournal } from "../ledger-core-DtZz1RG0.js";
|
|
2
|
+
export { AtomicFileLockError, FileLedgerJournal, appendLedgerLine, canonicalString, hashCanonical, probeAtomicFileLock, tryAcquireAtomicFileLock, tryWithLedgerFileLock, withLedgerFileLock };
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import { t as canonicalize } from "./pre-registration-DakwTRXk.js";
|
|
2
|
+
import { t as Mutex } from "./concurrency-DIxRZF_J.js";
|
|
3
|
+
import { closeSync, constants, existsSync, fsyncSync, linkSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
6
|
+
import { hostname } from "node:os";
|
|
7
|
+
//#region src/ledger-core/atomic-file-lock.ts
|
|
8
|
+
/** Crash-safe filesystem lock shared by campaign persistence and run exclusion. */
|
|
9
|
+
var AtomicFileLockError = class extends Error {
|
|
10
|
+
name = "AtomicFileLockError";
|
|
11
|
+
};
|
|
12
|
+
/** Report whether another lock path prevents acquisition without modifying it. */
|
|
13
|
+
function probeAtomicFileLock(options) {
|
|
14
|
+
if (existsSync(recoveryPath(options.lockPath))) return {
|
|
15
|
+
acquired: false,
|
|
16
|
+
reason: "recovery"
|
|
17
|
+
};
|
|
18
|
+
const state = ownerState(options.lockPath);
|
|
19
|
+
if (state.state === "held") return {
|
|
20
|
+
acquired: false,
|
|
21
|
+
reason: "held",
|
|
22
|
+
holder: state.owner
|
|
23
|
+
};
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Try to acquire a complete, uniquely-owned lock inode.
|
|
28
|
+
*
|
|
29
|
+
* A hard link publishes fully-written owner metadata atomically. Stale-owner
|
|
30
|
+
* removal is serialized so one reclaimer cannot delete a new owner's lock.
|
|
31
|
+
*/
|
|
32
|
+
function tryAcquireAtomicFileLock(options) {
|
|
33
|
+
const pid = options.pid ?? process.pid;
|
|
34
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) throw new AtomicFileLockError("atomic file lock pid must be a positive integer");
|
|
35
|
+
const owner = {
|
|
36
|
+
pid,
|
|
37
|
+
host: hostname(),
|
|
38
|
+
nonce: randomUUID()
|
|
39
|
+
};
|
|
40
|
+
for (let attempt = 0; attempt < 8; attempt += 1) {
|
|
41
|
+
if (existsSync(recoveryPath(options.lockPath))) return {
|
|
42
|
+
acquired: false,
|
|
43
|
+
reason: "recovery"
|
|
44
|
+
};
|
|
45
|
+
if (tryLinkOwner(options.lockPath, owner, `acquire.${attempt}`)) return acquired(options.lockPath, owner);
|
|
46
|
+
const holder = ownerState(options.lockPath);
|
|
47
|
+
if (holder.state === "missing") continue;
|
|
48
|
+
if (holder.state === "held") return {
|
|
49
|
+
acquired: false,
|
|
50
|
+
reason: "held",
|
|
51
|
+
holder: holder.owner
|
|
52
|
+
};
|
|
53
|
+
const reclaimPath = recoveryPath(options.lockPath);
|
|
54
|
+
if (!tryLinkOwner(reclaimPath, owner, `reclaim.${attempt}`)) return {
|
|
55
|
+
acquired: false,
|
|
56
|
+
reason: "recovery"
|
|
57
|
+
};
|
|
58
|
+
try {
|
|
59
|
+
const current = ownerState(options.lockPath);
|
|
60
|
+
if (current.state === "held") return {
|
|
61
|
+
acquired: false,
|
|
62
|
+
reason: "held",
|
|
63
|
+
holder: current.owner
|
|
64
|
+
};
|
|
65
|
+
if (current.state === "stale") {
|
|
66
|
+
const tombstone = `${options.lockPath}.stale.${owner.nonce}.${attempt}`;
|
|
67
|
+
try {
|
|
68
|
+
renameSync(options.lockPath, tombstone);
|
|
69
|
+
unlinkSync(tombstone);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (!isMissing(error)) throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (tryLinkOwner(options.lockPath, owner, `recovered.${attempt}`)) return acquired(options.lockPath, owner);
|
|
75
|
+
} finally {
|
|
76
|
+
releaseOwnedPath(reclaimPath, owner);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
throw new AtomicFileLockError(`could not acquire atomic file lock ${options.lockPath}`);
|
|
80
|
+
}
|
|
81
|
+
function acquired(lockPath, owner) {
|
|
82
|
+
return {
|
|
83
|
+
acquired: true,
|
|
84
|
+
lock: {
|
|
85
|
+
owner,
|
|
86
|
+
release: () => releaseOwnedPath(lockPath, owner)
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function tryLinkOwner(lockPath, owner, suffix) {
|
|
91
|
+
const ownerPath = `${lockPath}.${owner.pid}.${owner.nonce}.${suffix}.owner`;
|
|
92
|
+
const descriptor = openSync(ownerPath, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY, 384);
|
|
93
|
+
try {
|
|
94
|
+
writeFileSync(descriptor, `${canonicalOwner(owner)}\n`, "utf8");
|
|
95
|
+
fsyncSync(descriptor);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
closeSync(descriptor);
|
|
98
|
+
unlinkIfExists(ownerPath);
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
closeSync(descriptor);
|
|
102
|
+
try {
|
|
103
|
+
linkSync(ownerPath, lockPath);
|
|
104
|
+
return true;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
if (!isAlreadyExists(error)) throw error;
|
|
107
|
+
return false;
|
|
108
|
+
} finally {
|
|
109
|
+
unlinkIfExists(ownerPath);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function ownerState(lockPath) {
|
|
113
|
+
const owner = readOwner(lockPath);
|
|
114
|
+
if (!owner) return { state: "missing" };
|
|
115
|
+
if (owner.host !== hostname()) return {
|
|
116
|
+
state: "held",
|
|
117
|
+
owner
|
|
118
|
+
};
|
|
119
|
+
try {
|
|
120
|
+
process.kill(owner.pid, 0);
|
|
121
|
+
return {
|
|
122
|
+
state: "held",
|
|
123
|
+
owner
|
|
124
|
+
};
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (isNoSuchProcess(error)) return {
|
|
127
|
+
state: "stale",
|
|
128
|
+
owner
|
|
129
|
+
};
|
|
130
|
+
return {
|
|
131
|
+
state: "held",
|
|
132
|
+
owner
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function readOwner(lockPath) {
|
|
137
|
+
let contents;
|
|
138
|
+
try {
|
|
139
|
+
contents = readFileSync(lockPath, "utf8").trim();
|
|
140
|
+
} catch (error) {
|
|
141
|
+
if (isMissing(error)) return void 0;
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
let value;
|
|
145
|
+
try {
|
|
146
|
+
value = JSON.parse(contents);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
throw invalidOwner(lockPath, error);
|
|
149
|
+
}
|
|
150
|
+
if (!isOwner(value)) throw invalidOwner(lockPath);
|
|
151
|
+
return value;
|
|
152
|
+
}
|
|
153
|
+
function isOwner(value) {
|
|
154
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
155
|
+
const record = value;
|
|
156
|
+
return Object.keys(record).length === 3 && Number.isSafeInteger(record.pid) && Number(record.pid) > 0 && typeof record.host === "string" && record.host.trim().length > 0 && typeof record.nonce === "string" && record.nonce.trim().length > 0;
|
|
157
|
+
}
|
|
158
|
+
function invalidOwner(lockPath, cause) {
|
|
159
|
+
const error = new AtomicFileLockError(`atomic file lock has an invalid owner (${lockPath}); refusing unsafe recovery`);
|
|
160
|
+
if (cause !== void 0) Object.defineProperty(error, "cause", { value: cause });
|
|
161
|
+
return error;
|
|
162
|
+
}
|
|
163
|
+
function releaseOwnedPath(lockPath, owner) {
|
|
164
|
+
const current = readOwner(lockPath);
|
|
165
|
+
if (!current) return;
|
|
166
|
+
if (canonicalOwner(current) !== canonicalOwner(owner)) throw new AtomicFileLockError(`atomic file lock owner changed before release (${lockPath})`);
|
|
167
|
+
try {
|
|
168
|
+
unlinkSync(lockPath);
|
|
169
|
+
} catch (error) {
|
|
170
|
+
if (!isMissing(error)) throw error;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function canonicalOwner(owner) {
|
|
174
|
+
return JSON.stringify({
|
|
175
|
+
host: owner.host,
|
|
176
|
+
nonce: owner.nonce,
|
|
177
|
+
pid: owner.pid
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
function recoveryPath(lockPath) {
|
|
181
|
+
return `${lockPath}.reclaim`;
|
|
182
|
+
}
|
|
183
|
+
function isAlreadyExists(error) {
|
|
184
|
+
return error instanceof Error && "code" in error && error.code === "EEXIST";
|
|
185
|
+
}
|
|
186
|
+
function isMissing(error) {
|
|
187
|
+
return error instanceof Error && "code" in error && error.code === "ENOENT";
|
|
188
|
+
}
|
|
189
|
+
function isNoSuchProcess(error) {
|
|
190
|
+
return error instanceof Error && "code" in error && error.code === "ESRCH";
|
|
191
|
+
}
|
|
192
|
+
function unlinkIfExists(path) {
|
|
193
|
+
try {
|
|
194
|
+
unlinkSync(path);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (!isMissing(error)) throw error;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/ledger-core/journal-file.ts
|
|
201
|
+
/** Filesystem durability and cross-process exclusion for append-only journal files. */
|
|
202
|
+
/** Append one already-serialized line; the write is fsynced (file and directory)
|
|
203
|
+
* before returning so an acknowledged append survives a crash. */
|
|
204
|
+
function appendLedgerLine(path, line, context) {
|
|
205
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
206
|
+
const fd = openSync(path, constants.O_CREAT | constants.O_WRONLY | constants.O_APPEND, 384);
|
|
207
|
+
try {
|
|
208
|
+
writeAll(fd, Buffer.from(line, "utf8"), context);
|
|
209
|
+
fsyncSync(fd);
|
|
210
|
+
} finally {
|
|
211
|
+
closeSync(fd);
|
|
212
|
+
}
|
|
213
|
+
fsyncDirectory(dirname(path));
|
|
214
|
+
}
|
|
215
|
+
function withLedgerFileLock(path, context, run) {
|
|
216
|
+
const result = tryWithLedgerFileLock(path, context, run);
|
|
217
|
+
if (!result.acquired) throw context.integrityError(`${context.subject} lock is held (${path})`);
|
|
218
|
+
return result.value;
|
|
219
|
+
}
|
|
220
|
+
function tryWithLedgerFileLock(path, context, run) {
|
|
221
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
222
|
+
const lockPath = `${path}.lock`;
|
|
223
|
+
try {
|
|
224
|
+
const acquisition = tryAcquireAtomicFileLock({ lockPath });
|
|
225
|
+
if (!acquisition.acquired) return { acquired: false };
|
|
226
|
+
try {
|
|
227
|
+
return {
|
|
228
|
+
acquired: true,
|
|
229
|
+
value: run()
|
|
230
|
+
};
|
|
231
|
+
} finally {
|
|
232
|
+
acquisition.lock.release();
|
|
233
|
+
}
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (error instanceof AtomicFileLockError) throw context.integrityError(error.message, { cause: error });
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function writeAll(fd, bytes, context) {
|
|
240
|
+
let offset = 0;
|
|
241
|
+
while (offset < bytes.byteLength) {
|
|
242
|
+
const written = writeSync(fd, bytes, offset, bytes.byteLength - offset);
|
|
243
|
+
if (written <= 0) throw context.integrityError("filesystem wrote zero bytes");
|
|
244
|
+
offset += written;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function fsyncDirectory(path) {
|
|
248
|
+
const fd = openSync(path, constants.O_RDONLY);
|
|
249
|
+
try {
|
|
250
|
+
fsyncSync(fd);
|
|
251
|
+
} finally {
|
|
252
|
+
closeSync(fd);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
//#endregion
|
|
256
|
+
//#region src/ledger-core/journal.ts
|
|
257
|
+
/**
|
|
258
|
+
* Generic durable append-only journal with a SHA-256 hash chain.
|
|
259
|
+
*
|
|
260
|
+
* The file format is canonical JSONL: every row is the canonical JSON encoding
|
|
261
|
+
* (object keys sorted recursively) of one entry, and every entry hashes the
|
|
262
|
+
* previous entry's hash so the log is tamper-evident. Appends are serialized
|
|
263
|
+
* in-process per path and across processes via a lock file, fsynced before
|
|
264
|
+
* acknowledgement, and idempotent by `eventId`. A malformed, non-canonical,
|
|
265
|
+
* truncated, reordered, or conflicting log fails loudly; a bad row is never
|
|
266
|
+
* skipped.
|
|
267
|
+
*
|
|
268
|
+
* Domain vocabulary lives entirely in the consumer's codec: entry schema
|
|
269
|
+
* validation, the constant header fields stamped into every entry, the error
|
|
270
|
+
* taxonomy, and the state machine replayed over verified entries.
|
|
271
|
+
*/
|
|
272
|
+
/** Canonical JSON encoding: object keys sorted recursively. This is the byte
|
|
273
|
+
* form that is hashed and stored, so identical values always encode identically. */
|
|
274
|
+
function canonicalString(value) {
|
|
275
|
+
return JSON.stringify(canonicalize(value));
|
|
276
|
+
}
|
|
277
|
+
function hashCanonical(value) {
|
|
278
|
+
return `sha256:${createHash("sha256").update(canonicalString(value)).digest("hex")}`;
|
|
279
|
+
}
|
|
280
|
+
const journalMutexes = /* @__PURE__ */ new Map();
|
|
281
|
+
function mutexFor(path) {
|
|
282
|
+
const existing = journalMutexes.get(path);
|
|
283
|
+
if (existing) return existing;
|
|
284
|
+
const mutex = new Mutex();
|
|
285
|
+
journalMutexes.set(path, mutex);
|
|
286
|
+
return mutex;
|
|
287
|
+
}
|
|
288
|
+
/** Durable filesystem journal. Construction performs no I/O; `append` and
|
|
289
|
+
* `replay` validate the complete existing file under the locks. */
|
|
290
|
+
var FileLedgerJournal = class {
|
|
291
|
+
path;
|
|
292
|
+
codec;
|
|
293
|
+
mutex;
|
|
294
|
+
constructor(path, codec) {
|
|
295
|
+
this.path = resolve(path);
|
|
296
|
+
this.codec = codec;
|
|
297
|
+
this.mutex = mutexFor(this.path);
|
|
298
|
+
}
|
|
299
|
+
async replay() {
|
|
300
|
+
return this.mutex.runExclusive(() => withLedgerFileLock(this.path, this.codec, () => this.replayLocked()));
|
|
301
|
+
}
|
|
302
|
+
async append(event) {
|
|
303
|
+
return this.mutex.runExclusive(() => withLedgerFileLock(this.path, this.codec, () => {
|
|
304
|
+
const before = this.replayLocked();
|
|
305
|
+
const existing = before.entries.find((entry) => entry.event.eventId === event.eventId);
|
|
306
|
+
if (existing) {
|
|
307
|
+
if (canonicalString(existing.event) !== canonicalString(event)) throw this.codec.conflictError(`eventId ${event.eventId} already exists with different content`);
|
|
308
|
+
return {
|
|
309
|
+
entry: existing,
|
|
310
|
+
appended: false,
|
|
311
|
+
projection: before.projection
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
const material = {
|
|
315
|
+
...this.codec.header,
|
|
316
|
+
sequence: before.entries.length,
|
|
317
|
+
previousHash: before.entries.at(-1)?.entryHash ?? null,
|
|
318
|
+
event
|
|
319
|
+
};
|
|
320
|
+
const entry = {
|
|
321
|
+
...material,
|
|
322
|
+
entryHash: hashCanonical(material)
|
|
323
|
+
};
|
|
324
|
+
const projection = this.project([...before.entries, entry]);
|
|
325
|
+
appendLedgerLine(this.path, `${canonicalString(entry)}\n`, this.codec);
|
|
326
|
+
return {
|
|
327
|
+
entry,
|
|
328
|
+
appended: true,
|
|
329
|
+
projection
|
|
330
|
+
};
|
|
331
|
+
}));
|
|
332
|
+
}
|
|
333
|
+
replayLocked() {
|
|
334
|
+
const entries = this.readEntries();
|
|
335
|
+
return {
|
|
336
|
+
entries,
|
|
337
|
+
projection: this.project(entries)
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
readEntries() {
|
|
341
|
+
if (!existsSync(this.path)) return [];
|
|
342
|
+
const text = readFileSync(this.path, "utf8");
|
|
343
|
+
if (text.length === 0) return [];
|
|
344
|
+
if (!text.endsWith("\n")) throw this.codec.integrityError(`${this.codec.subject} ${this.path} has a truncated final record (missing newline)`);
|
|
345
|
+
const lines = text.slice(0, -1).split("\n");
|
|
346
|
+
const entries = [];
|
|
347
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
348
|
+
const line = lines[index];
|
|
349
|
+
if (line.length === 0) throw this.codec.integrityError(`${this.codec.subject} ${this.path} has a blank row at line ${index + 1}`);
|
|
350
|
+
let raw;
|
|
351
|
+
try {
|
|
352
|
+
raw = JSON.parse(line);
|
|
353
|
+
} catch (error) {
|
|
354
|
+
throw this.codec.integrityError(`${this.codec.subject} ${this.path} has invalid JSON at line ${index + 1}`, { cause: error });
|
|
355
|
+
}
|
|
356
|
+
const entry = this.codec.parseEntry(raw, {
|
|
357
|
+
path: this.path,
|
|
358
|
+
line: index + 1
|
|
359
|
+
});
|
|
360
|
+
if (line !== canonicalString(entry)) throw this.codec.integrityError(`${this.codec.subject} ${this.path} has non-canonical bytes at line ${index + 1}`);
|
|
361
|
+
entries.push(entry);
|
|
362
|
+
}
|
|
363
|
+
return entries;
|
|
364
|
+
}
|
|
365
|
+
project(entries) {
|
|
366
|
+
const projector = this.codec.createProjector();
|
|
367
|
+
const eventIds = /* @__PURE__ */ new Set();
|
|
368
|
+
let expectedPrevious = null;
|
|
369
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
370
|
+
const entry = entries[index];
|
|
371
|
+
this.codec.checkEntryHeader(entry, index);
|
|
372
|
+
if (entry.sequence !== index) throw this.codec.integrityError(`entry ${entry.event.eventId} has sequence ${entry.sequence}, expected ${index}`);
|
|
373
|
+
if (entry.previousHash !== expectedPrevious) throw this.codec.integrityError(`entry ${entry.event.eventId} does not extend the previous hash`);
|
|
374
|
+
const { entryHash: _entryHash, ...material } = entry;
|
|
375
|
+
const expectedHash = hashCanonical(material);
|
|
376
|
+
if (entry.entryHash !== expectedHash) throw this.codec.integrityError(`entry ${entry.event.eventId} hash mismatch: expected ${expectedHash}, got ${entry.entryHash}`);
|
|
377
|
+
expectedPrevious = entry.entryHash;
|
|
378
|
+
if (eventIds.has(entry.event.eventId)) throw this.codec.integrityError(`duplicate eventId ${entry.event.eventId} in durable ledger`);
|
|
379
|
+
eventIds.add(entry.event.eventId);
|
|
380
|
+
projector.apply(entry, index);
|
|
381
|
+
}
|
|
382
|
+
return projector.finish(entries);
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
//#endregion
|
|
386
|
+
export { tryWithLedgerFileLock as a, probeAtomicFileLock as c, appendLedgerLine as i, tryAcquireAtomicFileLock as l, canonicalString as n, withLedgerFileLock as o, hashCanonical as r, AtomicFileLockError as s, FileLedgerJournal as t };
|
|
387
|
+
|
|
388
|
+
//# sourceMappingURL=ledger-core-DtZz1RG0.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ledger-core-DtZz1RG0.js","names":[],"sources":["../src/ledger-core/atomic-file-lock.ts","../src/ledger-core/journal-file.ts","../src/ledger-core/journal.ts"],"sourcesContent":["/** Crash-safe filesystem lock shared by campaign persistence and run exclusion. */\n\nimport { randomUUID } from 'node:crypto'\nimport {\n closeSync,\n constants,\n existsSync,\n fsyncSync,\n linkSync,\n openSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs'\nimport { hostname } from 'node:os'\n\nexport interface AtomicFileLockOwner {\n readonly pid: number\n readonly host: string\n readonly nonce: string\n}\n\nexport interface AtomicFileLock {\n readonly owner: AtomicFileLockOwner\n release(): void\n}\n\nexport type AtomicFileLockUnavailable =\n | { readonly acquired: false; readonly reason: 'held'; readonly holder: AtomicFileLockOwner }\n | { readonly acquired: false; readonly reason: 'recovery' }\n\nexport type AtomicFileLockAcquisition =\n | { readonly acquired: true; readonly lock: AtomicFileLock }\n | AtomicFileLockUnavailable\n\nexport interface AtomicFileLockOptions {\n readonly lockPath: string\n readonly pid?: number\n}\n\nexport class AtomicFileLockError extends Error {\n override readonly name = 'AtomicFileLockError'\n}\n\ntype OwnerState =\n | { readonly state: 'missing' }\n | { readonly state: 'stale'; readonly owner: AtomicFileLockOwner }\n | { readonly state: 'held'; readonly owner: AtomicFileLockOwner }\n\n/** Report whether another lock path prevents acquisition without modifying it. */\nexport function probeAtomicFileLock(\n options: AtomicFileLockOptions,\n): AtomicFileLockUnavailable | null {\n if (existsSync(recoveryPath(options.lockPath))) return { acquired: false, reason: 'recovery' }\n const state = ownerState(options.lockPath)\n if (state.state === 'held') {\n return { acquired: false, reason: 'held', holder: state.owner }\n }\n return null\n}\n\n/**\n * Try to acquire a complete, uniquely-owned lock inode.\n *\n * A hard link publishes fully-written owner metadata atomically. Stale-owner\n * removal is serialized so one reclaimer cannot delete a new owner's lock.\n */\nexport function tryAcquireAtomicFileLock(\n options: AtomicFileLockOptions,\n): AtomicFileLockAcquisition {\n const pid = options.pid ?? process.pid\n if (!Number.isSafeInteger(pid) || pid <= 0) {\n throw new AtomicFileLockError('atomic file lock pid must be a positive integer')\n }\n const owner: AtomicFileLockOwner = { pid, host: hostname(), nonce: randomUUID() }\n for (let attempt = 0; attempt < 8; attempt += 1) {\n if (existsSync(recoveryPath(options.lockPath))) {\n return { acquired: false, reason: 'recovery' }\n }\n if (tryLinkOwner(options.lockPath, owner, `acquire.${attempt}`)) {\n return acquired(options.lockPath, owner)\n }\n\n const holder = ownerState(options.lockPath)\n if (holder.state === 'missing') continue\n if (holder.state === 'held') {\n return { acquired: false, reason: 'held', holder: holder.owner }\n }\n\n const reclaimPath = recoveryPath(options.lockPath)\n if (!tryLinkOwner(reclaimPath, owner, `reclaim.${attempt}`)) {\n return { acquired: false, reason: 'recovery' }\n }\n try {\n const current = ownerState(options.lockPath)\n if (current.state === 'held') {\n return { acquired: false, reason: 'held', holder: current.owner }\n }\n if (current.state === 'stale') {\n const tombstone = `${options.lockPath}.stale.${owner.nonce}.${attempt}`\n try {\n renameSync(options.lockPath, tombstone)\n unlinkSync(tombstone)\n } catch (error) {\n if (!isMissing(error)) throw error\n }\n }\n if (tryLinkOwner(options.lockPath, owner, `recovered.${attempt}`)) {\n return acquired(options.lockPath, owner)\n }\n } finally {\n releaseOwnedPath(reclaimPath, owner)\n }\n }\n\n throw new AtomicFileLockError(`could not acquire atomic file lock ${options.lockPath}`)\n}\n\nfunction acquired(lockPath: string, owner: AtomicFileLockOwner): AtomicFileLockAcquisition {\n return {\n acquired: true,\n lock: {\n owner,\n release: () => releaseOwnedPath(lockPath, owner),\n },\n }\n}\n\nfunction tryLinkOwner(lockPath: string, owner: AtomicFileLockOwner, suffix: string): boolean {\n const ownerPath = `${lockPath}.${owner.pid}.${owner.nonce}.${suffix}.owner`\n const descriptor = openSync(\n ownerPath,\n constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY,\n 0o600,\n )\n try {\n writeFileSync(descriptor, `${canonicalOwner(owner)}\\n`, 'utf8')\n fsyncSync(descriptor)\n } catch (error) {\n closeSync(descriptor)\n unlinkIfExists(ownerPath)\n throw error\n }\n closeSync(descriptor)\n\n try {\n linkSync(ownerPath, lockPath)\n return true\n } catch (error) {\n if (!isAlreadyExists(error)) throw error\n return false\n } finally {\n unlinkIfExists(ownerPath)\n }\n}\n\nfunction ownerState(lockPath: string): OwnerState {\n const owner = readOwner(lockPath)\n if (!owner) return { state: 'missing' }\n if (owner.host !== hostname()) return { state: 'held', owner }\n try {\n process.kill(owner.pid, 0)\n return { state: 'held', owner }\n } catch (error) {\n if (isNoSuchProcess(error)) return { state: 'stale', owner }\n return { state: 'held', owner }\n }\n}\n\nfunction readOwner(lockPath: string): AtomicFileLockOwner | undefined {\n let contents: string\n try {\n contents = readFileSync(lockPath, 'utf8').trim()\n } catch (error) {\n if (isMissing(error)) return undefined\n throw error\n }\n\n let value: unknown\n try {\n value = JSON.parse(contents)\n } catch (error) {\n throw invalidOwner(lockPath, error)\n }\n if (!isOwner(value)) throw invalidOwner(lockPath)\n return value\n}\n\nfunction isOwner(value: unknown): value is AtomicFileLockOwner {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false\n const record = value as Record<string, unknown>\n return (\n Object.keys(record).length === 3 &&\n Number.isSafeInteger(record.pid) &&\n Number(record.pid) > 0 &&\n typeof record.host === 'string' &&\n record.host.trim().length > 0 &&\n typeof record.nonce === 'string' &&\n record.nonce.trim().length > 0\n )\n}\n\nfunction invalidOwner(lockPath: string, cause?: unknown): AtomicFileLockError {\n const error = new AtomicFileLockError(\n `atomic file lock has an invalid owner (${lockPath}); refusing unsafe recovery`,\n )\n if (cause !== undefined) Object.defineProperty(error, 'cause', { value: cause })\n return error\n}\n\nfunction releaseOwnedPath(lockPath: string, owner: AtomicFileLockOwner): void {\n const current = readOwner(lockPath)\n if (!current) return\n if (canonicalOwner(current) !== canonicalOwner(owner)) {\n throw new AtomicFileLockError(`atomic file lock owner changed before release (${lockPath})`)\n }\n try {\n unlinkSync(lockPath)\n } catch (error) {\n if (!isMissing(error)) throw error\n }\n}\n\nfunction canonicalOwner(owner: AtomicFileLockOwner): string {\n return JSON.stringify({ host: owner.host, nonce: owner.nonce, pid: owner.pid })\n}\n\nfunction recoveryPath(lockPath: string): string {\n return `${lockPath}.reclaim`\n}\n\nfunction isAlreadyExists(error: unknown): boolean {\n return error instanceof Error && 'code' in error && error.code === 'EEXIST'\n}\n\nfunction isMissing(error: unknown): boolean {\n return error instanceof Error && 'code' in error && error.code === 'ENOENT'\n}\n\nfunction isNoSuchProcess(error: unknown): boolean {\n return error instanceof Error && 'code' in error && error.code === 'ESRCH'\n}\n\nfunction unlinkIfExists(path: string): void {\n try {\n unlinkSync(path)\n } catch (error) {\n if (!isMissing(error)) throw error\n }\n}\n","/** Filesystem durability and cross-process exclusion for append-only journal files. */\n\nimport { closeSync, constants, fsyncSync, mkdirSync, openSync, writeSync } from 'node:fs'\nimport { dirname } from 'node:path'\nimport { AtomicFileLockError, tryAcquireAtomicFileLock } from './atomic-file-lock'\n\n/** How a journal binding reports file-layer faults in its own error taxonomy. */\nexport interface LedgerFileContext {\n /** Noun used in error messages, e.g. 'search ledger'. */\n subject: string\n integrityError(message: string, options?: { cause?: unknown }): Error\n}\n\n/** Append one already-serialized line; the write is fsynced (file and directory)\n * before returning so an acknowledged append survives a crash. */\nexport function appendLedgerLine(path: string, line: string, context: LedgerFileContext): void {\n mkdirSync(dirname(path), { recursive: true })\n const fd = openSync(path, constants.O_CREAT | constants.O_WRONLY | constants.O_APPEND, 0o600)\n try {\n writeAll(fd, Buffer.from(line, 'utf8'), context)\n fsyncSync(fd)\n } finally {\n closeSync(fd)\n }\n fsyncDirectory(dirname(path))\n}\n\nexport function withLedgerFileLock<T>(path: string, context: LedgerFileContext, run: () => T): T {\n const result = tryWithLedgerFileLock(path, context, run)\n if (!result.acquired) {\n throw context.integrityError(`${context.subject} lock is held (${path})`)\n }\n return result.value\n}\n\nexport type FileLockResult<T> = { acquired: true; value: T } | { acquired: false }\n\nexport function tryWithLedgerFileLock<T>(\n path: string,\n context: LedgerFileContext,\n run: () => T,\n): FileLockResult<T> {\n mkdirSync(dirname(path), { recursive: true })\n const lockPath = `${path}.lock`\n try {\n const acquisition = tryAcquireAtomicFileLock({ lockPath })\n if (!acquisition.acquired) return { acquired: false }\n try {\n return { acquired: true, value: run() }\n } finally {\n acquisition.lock.release()\n }\n } catch (error) {\n if (error instanceof AtomicFileLockError) {\n throw context.integrityError(error.message, { cause: error })\n }\n throw error\n }\n}\n\nfunction writeAll(fd: number, bytes: Buffer, context: LedgerFileContext): void {\n let offset = 0\n while (offset < bytes.byteLength) {\n const written = writeSync(fd, bytes, offset, bytes.byteLength - offset)\n if (written <= 0) throw context.integrityError('filesystem wrote zero bytes')\n offset += written\n }\n}\n\nfunction fsyncDirectory(path: string): void {\n const fd = openSync(path, constants.O_RDONLY)\n try {\n fsyncSync(fd)\n } finally {\n closeSync(fd)\n }\n}\n","/**\n * Generic durable append-only journal with a SHA-256 hash chain.\n *\n * The file format is canonical JSONL: every row is the canonical JSON encoding\n * (object keys sorted recursively) of one entry, and every entry hashes the\n * previous entry's hash so the log is tamper-evident. Appends are serialized\n * in-process per path and across processes via a lock file, fsynced before\n * acknowledgement, and idempotent by `eventId`. A malformed, non-canonical,\n * truncated, reordered, or conflicting log fails loudly; a bad row is never\n * skipped.\n *\n * Domain vocabulary lives entirely in the consumer's codec: entry schema\n * validation, the constant header fields stamped into every entry, the error\n * taxonomy, and the state machine replayed over verified entries.\n */\n\nimport { createHash } from 'node:crypto'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { Mutex } from '../concurrency'\nimport { canonicalize } from '../pre-registration'\nimport { appendLedgerLine, type LedgerFileContext, withLedgerFileLock } from './journal-file'\n\nexport type LedgerHash = `sha256:${string}`\n\n/** Canonical JSON encoding: object keys sorted recursively. This is the byte\n * form that is hashed and stored, so identical values always encode identically. */\nexport function canonicalString(value: unknown): string {\n return JSON.stringify(canonicalize(value))\n}\n\nexport function hashCanonical(value: unknown): LedgerHash {\n return `sha256:${createHash('sha256').update(canonicalString(value)).digest('hex')}`\n}\n\n/** Minimum shape of a journal event: the idempotency key. */\nexport interface LedgerEventBase {\n eventId: string\n}\n\n/** Chain envelope every entry carries in addition to the journal's constant\n * header fields. `entryHash` covers the header, sequence, previous hash, and\n * event, so any rewrite of history invalidates every later entry. */\nexport interface LedgerChainFields<Event extends LedgerEventBase> {\n sequence: number\n previousHash: LedgerHash | null\n event: Event\n entryHash: LedgerHash\n}\n\nexport type LedgerEntryOf<Header extends object, Event extends LedgerEventBase> = Header &\n LedgerChainFields<Event>\n\nexport interface LedgerLineContext {\n path: string\n line: number\n}\n\n/** Domain state machine replayed over chain-verified entries in order.\n * `apply` throws (via the codec's error taxonomy) on an invalid transition;\n * `finish` builds the consumer's projection once every entry was applied. */\nexport interface LedgerProjector<Entry, Projection> {\n apply(entry: Entry, index: number): void\n finish(entries: Entry[]): Projection\n}\n\nexport interface LedgerJournalCodec<\n Header extends object,\n Event extends LedgerEventBase,\n Projection,\n> extends LedgerFileContext {\n /** Constant fields stamped into every entry and covered by its hash. */\n header: Header\n conflictError(message: string): Error\n /** Validate one parsed JSON row into a typed entry. Must reject malformed\n * rows and non-canonical event encodings with the binding's own errors;\n * the journal itself verifies the stored bytes and the hash chain. */\n parseEntry(raw: unknown, context: LedgerLineContext): LedgerEntryOf<Header, Event>\n /** Reject an entry whose constant header fields do not match this journal. */\n checkEntryHeader(entry: LedgerEntryOf<Header, Event>, index: number): void\n createProjector(): LedgerProjector<LedgerEntryOf<Header, Event>, Projection>\n}\n\nexport interface LedgerReplayResult<Entry, Projection> {\n entries: Entry[]\n projection: Projection\n}\n\nexport interface LedgerAppendResult<Entry, Projection> {\n entry: Entry\n /** False when the exact event was already durably present. */\n appended: boolean\n projection: Projection\n}\n\n// One async mutex per resolved journal path so concurrent appends from a single\n// process queue up instead of failing on the held cross-process lock file.\nconst journalMutexes = new Map<string, Mutex>()\n\nfunction mutexFor(path: string): Mutex {\n const existing = journalMutexes.get(path)\n if (existing) return existing\n const mutex = new Mutex()\n journalMutexes.set(path, mutex)\n return mutex\n}\n\n/** Durable filesystem journal. Construction performs no I/O; `append` and\n * `replay` validate the complete existing file under the locks. */\nexport class FileLedgerJournal<Header extends object, Event extends LedgerEventBase, Projection> {\n readonly path: string\n private readonly codec: LedgerJournalCodec<Header, Event, Projection>\n private readonly mutex: Mutex\n\n constructor(path: string, codec: LedgerJournalCodec<Header, Event, Projection>) {\n this.path = resolve(path)\n this.codec = codec\n this.mutex = mutexFor(this.path)\n }\n\n async replay(): Promise<LedgerReplayResult<LedgerEntryOf<Header, Event>, Projection>> {\n return this.mutex.runExclusive(() =>\n withLedgerFileLock(this.path, this.codec, () => this.replayLocked()),\n )\n }\n\n async append(\n event: Event,\n ): Promise<LedgerAppendResult<LedgerEntryOf<Header, Event>, Projection>> {\n return this.mutex.runExclusive(() =>\n withLedgerFileLock(this.path, this.codec, () => {\n const before = this.replayLocked()\n const existing = before.entries.find((entry) => entry.event.eventId === event.eventId)\n if (existing) {\n if (canonicalString(existing.event) !== canonicalString(event)) {\n throw this.codec.conflictError(\n `eventId ${event.eventId} already exists with different content`,\n )\n }\n return { entry: existing, appended: false, projection: before.projection }\n }\n\n const material = {\n ...this.codec.header,\n sequence: before.entries.length,\n previousHash: before.entries.at(-1)?.entryHash ?? null,\n event,\n }\n const entry = { ...material, entryHash: hashCanonical(material) }\n\n // Apply the state transition before spending an append: an entry the\n // projector rejects must never reach the durable file.\n const projection = this.project([...before.entries, entry])\n appendLedgerLine(this.path, `${canonicalString(entry)}\\n`, this.codec)\n return { entry, appended: true, projection }\n }),\n )\n }\n\n private replayLocked(): LedgerReplayResult<LedgerEntryOf<Header, Event>, Projection> {\n const entries = this.readEntries()\n return { entries, projection: this.project(entries) }\n }\n\n private readEntries(): LedgerEntryOf<Header, Event>[] {\n if (!existsSync(this.path)) return []\n const text = readFileSync(this.path, 'utf8')\n if (text.length === 0) return []\n if (!text.endsWith('\\n')) {\n throw this.codec.integrityError(\n `${this.codec.subject} ${this.path} has a truncated final record (missing newline)`,\n )\n }\n\n const lines = text.slice(0, -1).split('\\n')\n const entries: LedgerEntryOf<Header, Event>[] = []\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index]!\n if (line.length === 0) {\n throw this.codec.integrityError(\n `${this.codec.subject} ${this.path} has a blank row at line ${index + 1}`,\n )\n }\n let raw: unknown\n try {\n raw = JSON.parse(line)\n } catch (error) {\n throw this.codec.integrityError(\n `${this.codec.subject} ${this.path} has invalid JSON at line ${index + 1}`,\n { cause: error },\n )\n }\n const entry = this.codec.parseEntry(raw, { path: this.path, line: index + 1 })\n if (line !== canonicalString(entry)) {\n throw this.codec.integrityError(\n `${this.codec.subject} ${this.path} has non-canonical bytes at line ${index + 1}`,\n )\n }\n entries.push(entry)\n }\n return entries\n }\n\n private project(entries: LedgerEntryOf<Header, Event>[]): Projection {\n const projector = this.codec.createProjector()\n const eventIds = new Set<string>()\n let expectedPrevious: LedgerHash | null = null\n for (let index = 0; index < entries.length; index += 1) {\n const entry = entries[index]!\n this.codec.checkEntryHeader(entry, index)\n if (entry.sequence !== index) {\n throw this.codec.integrityError(\n `entry ${entry.event.eventId} has sequence ${entry.sequence}, expected ${index}`,\n )\n }\n if (entry.previousHash !== expectedPrevious) {\n throw this.codec.integrityError(\n `entry ${entry.event.eventId} does not extend the previous hash`,\n )\n }\n const { entryHash: _entryHash, ...material } = entry\n const expectedHash = hashCanonical(material)\n if (entry.entryHash !== expectedHash) {\n throw this.codec.integrityError(\n `entry ${entry.event.eventId} hash mismatch: expected ${expectedHash}, got ${entry.entryHash}`,\n )\n }\n expectedPrevious = entry.entryHash\n if (eventIds.has(entry.event.eventId)) {\n throw this.codec.integrityError(\n `duplicate eventId ${entry.event.eventId} in durable ledger`,\n )\n }\n eventIds.add(entry.event.eventId)\n projector.apply(entry, index)\n }\n return projector.finish(entries)\n }\n}\n"],"mappings":"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"}
|