@tangle-network/agent-eval 0.86.0 → 0.89.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.
Files changed (134) hide show
  1. package/dist/adapters/http.d.ts +3 -3
  2. package/dist/adapters/langchain.d.ts +3 -3
  3. package/dist/adapters/otel.d.ts +6 -6
  4. package/dist/adversarial-DIVcDoI_.d.ts +88 -0
  5. package/dist/analyst/index.d.ts +11 -10
  6. package/dist/analyst/index.js +13 -8
  7. package/dist/analyst/index.js.map +1 -1
  8. package/dist/analyze-runs-DwCEkpO_.d.ts +81 -0
  9. package/dist/belief-state/index.d.ts +4 -4
  10. package/dist/belief-state/index.js +1 -1
  11. package/dist/benchmarks/index.d.ts +3 -3
  12. package/dist/campaign/index.d.ts +165 -18
  13. package/dist/campaign/index.js +289 -14
  14. package/dist/campaign/index.js.map +1 -1
  15. package/dist/chunk-45EEMHTC.js +35 -0
  16. package/dist/chunk-45EEMHTC.js.map +1 -0
  17. package/dist/{chunk-FZWAFVAA.js → chunk-4FBZZIYD.js} +2 -2
  18. package/dist/{chunk-YV7J7X5N.js → chunk-5HRORJQY.js} +22 -12
  19. package/dist/chunk-5HRORJQY.js.map +1 -0
  20. package/dist/{chunk-OTYQPHPL.js → chunk-6SOJM3VR.js} +5 -5
  21. package/dist/chunk-BOD4O7OF.js +40 -0
  22. package/dist/chunk-BOD4O7OF.js.map +1 -0
  23. package/dist/{chunk-Z7VFTS2J.js → chunk-CY6U5S3X.js} +2 -2
  24. package/dist/{chunk-VIDQF3F5.js → chunk-D3V5B42D.js} +5 -34
  25. package/dist/chunk-D3V5B42D.js.map +1 -0
  26. package/dist/{chunk-YGYXHNAQ.js → chunk-FIUKOSWI.js} +21 -8
  27. package/dist/chunk-FIUKOSWI.js.map +1 -0
  28. package/dist/{chunk-WJL2NJXN.js → chunk-GSH6QNNS.js} +2 -2
  29. package/dist/{chunk-RBNA5AZT.js → chunk-L3JOU6XM.js} +2 -2
  30. package/dist/{chunk-IDVBLYCY.js → chunk-LMZQ2Z4U.js} +56 -2
  31. package/dist/{chunk-IDVBLYCY.js.map → chunk-LMZQ2Z4U.js.map} +1 -1
  32. package/dist/{chunk-VUINJM5M.js → chunk-QAY5UIJO.js} +2 -193
  33. package/dist/chunk-QAY5UIJO.js.map +1 -0
  34. package/dist/{chunk-P2J6SOXT.js → chunk-QG2OVF2D.js} +5 -3
  35. package/dist/{chunk-P2J6SOXT.js.map → chunk-QG2OVF2D.js.map} +1 -1
  36. package/dist/chunk-REVYNR6C.js +100 -0
  37. package/dist/chunk-REVYNR6C.js.map +1 -0
  38. package/dist/{chunk-ZZ2HOPME.js → chunk-TWS7AZEY.js} +2 -2
  39. package/dist/chunk-UHMJT4T7.js +200 -0
  40. package/dist/chunk-UHMJT4T7.js.map +1 -0
  41. package/dist/chunk-UMMZHCPB.js +190 -0
  42. package/dist/chunk-UMMZHCPB.js.map +1 -0
  43. package/dist/chunk-VZSRQ272.js +149 -0
  44. package/dist/chunk-VZSRQ272.js.map +1 -0
  45. package/dist/{chunk-L5G7OUKD.js → chunk-XY4DDNEG.js} +8 -190
  46. package/dist/chunk-XY4DDNEG.js.map +1 -0
  47. package/dist/chunk-Y47J2LJ3.js +859 -0
  48. package/dist/chunk-Y47J2LJ3.js.map +1 -0
  49. package/dist/{chunk-BABOZOSN.js → chunk-ZFIBGEOL.js} +3 -3
  50. package/dist/chunk-ZFIBGEOL.js.map +1 -0
  51. package/dist/{code-agent-session-BRXmavYv.d.ts → code-agent-session-BO8nCnv3.d.ts} +1 -1
  52. package/dist/contract/index.d.ts +24 -95
  53. package/dist/contract/index.js +16 -755
  54. package/dist/contract/index.js.map +1 -1
  55. package/dist/{control-GeE8OhpN.d.ts → control-_Qb7skHX.d.ts} +2 -2
  56. package/dist/control.d.ts +5 -5
  57. package/dist/corpus-BoR-041R.d.ts +560 -0
  58. package/dist/cost-ledger-DuSqlw5B.d.ts +113 -0
  59. package/dist/counterfactual-Dwibr5IW.d.ts +85 -0
  60. package/dist/{dataset-B2kL-fSM.d.ts → dataset-BbGkaN2I.d.ts} +1 -1
  61. package/dist/{registry-DrEQ3Luj.d.ts → default-registry-zoGHUQEH.d.ts} +29 -2
  62. package/dist/diagnose.d.ts +251 -0
  63. package/dist/diagnose.js +381 -0
  64. package/dist/diagnose.js.map +1 -0
  65. package/dist/{errors-Dwqw-T_m.d.ts → errors-CzMUYo7b.d.ts} +1 -1
  66. package/dist/{feedback-trajectory-B3rErRsh.d.ts → feedback-trajectory-D9OVLrg9.d.ts} +1 -1
  67. package/dist/fuzz.d.ts +484 -0
  68. package/dist/fuzz.js +613 -0
  69. package/dist/fuzz.js.map +1 -0
  70. package/dist/governance/index.d.ts +4 -4
  71. package/dist/hosted/index.d.ts +6 -6
  72. package/dist/{index-DE3RXAXD.d.ts → index-Bx3gZ8xl.d.ts} +1 -1
  73. package/dist/index.d.ts +717 -455
  74. package/dist/index.js +1590 -793
  75. package/dist/index.js.map +1 -1
  76. package/dist/{insight-report-3ADTfClO.d.ts → insight-report-BBwvOh6x.d.ts} +2 -2
  77. package/dist/{integrity-CJzrpUua.d.ts → integrity-VJ9A7aST.d.ts} +1 -1
  78. package/dist/{judge-calibration-DilmB3Ml.d.ts → judge-calibration-0p2QcWNE.d.ts} +1 -1
  79. package/dist/{kind-factory-CVecZZG_.d.ts → kind-factory-5b7xXXOr.d.ts} +2 -2
  80. package/dist/{llm-client-CuUg2Mn3.d.ts → llm-client-BeEcAokY.d.ts} +1 -1
  81. package/dist/matrix/index.d.ts +2 -2
  82. package/dist/meta-eval/index.d.ts +177 -3
  83. package/dist/meta-eval/index.js +260 -1
  84. package/dist/meta-eval/index.js.map +1 -1
  85. package/dist/{multi-layer-verifier-DlWCXuxL.d.ts → multi-layer-verifier-DUZXrPDA.d.ts} +7 -1
  86. package/dist/multishot/index.d.ts +25 -11
  87. package/dist/multishot/index.js +36 -7
  88. package/dist/multishot/index.js.map +1 -1
  89. package/dist/openapi.json +1 -1
  90. package/dist/pipelines/index.js +2 -2
  91. package/dist/{agent-profile-D0PBIWlV.d.ts → pre-registration-DELOEJ8v.d.ts} +144 -4
  92. package/dist/{provenance-DPpNIOJD.d.ts → provenance-LnqRT0sS.d.ts} +5 -5
  93. package/dist/{red-team-DW9Ca_tj.d.ts → red-team-BXHil6c8.d.ts} +1 -1
  94. package/dist/{release-report-hlNtD12q.d.ts → release-report-euXIV_Sk.d.ts} +3 -3
  95. package/dist/reporting.d.ts +8 -8
  96. package/dist/reporting.js +3 -3
  97. package/dist/{researcher-BLPHBbNV.d.ts → researcher-DE6Gpnb4.d.ts} +4 -4
  98. package/dist/rl.d.ts +194 -656
  99. package/dist/rl.js +236 -154
  100. package/dist/rl.js.map +1 -1
  101. package/dist/{rubric-predictive-validity-CnEl9Jc8.d.ts → rubric-predictive-validity-Cy_W-hWZ.d.ts} +1 -1
  102. package/dist/{run-campaign-4Y5V5CN3.js → run-campaign-RDGAM5KJ.js} +3 -3
  103. package/dist/{run-improvement-loop-CNqQckTj.d.ts → run-improvement-loop-5z_l5zDz.d.ts} +2 -2
  104. package/dist/{run-record-De9VarXR.d.ts → run-record-e7vj1uZQ.d.ts} +1 -1
  105. package/dist/{runtime-trajectory-BLRiaifm.d.ts → runtime-trajectory-BDgfGZSr.d.ts} +1 -1
  106. package/dist/{semantic-concept-judge-DIEgr_6v.d.ts → semantic-concept-judge-Dn8Z6KEG.d.ts} +5 -31
  107. package/dist/series-convergence-D5OWMBg6.d.ts +33 -0
  108. package/dist/{statistics-CnC1FMbx.d.ts → statistics-C7PozGrZ.d.ts} +71 -2
  109. package/dist/{summary-report-Db0dDSWP.d.ts → summary-report-DGmUucwQ.d.ts} +1 -1
  110. package/dist/traces.d.ts +3 -3
  111. package/dist/traces.js +8 -6
  112. package/dist/{types-Cu3u_x59.d.ts → types-2VVIL04s.d.ts} +2 -2
  113. package/dist/{types-D7lLRYe9.d.ts → types-BU-7W85F.d.ts} +21 -1
  114. package/dist/{types-CqPax19X.d.ts → types-mn5Aqk7x.d.ts} +1 -1
  115. package/dist/{verdict-CeEgtjyI.d.ts → verdict-C9MlYujm.d.ts} +3 -0
  116. package/dist/wire/index.d.ts +3 -3
  117. package/dist/workflow/index.d.ts +12 -11
  118. package/dist/workflow/index.js +1 -1
  119. package/package.json +11 -1
  120. package/dist/chunk-BABOZOSN.js.map +0 -1
  121. package/dist/chunk-L5G7OUKD.js.map +0 -1
  122. package/dist/chunk-SHTXZ4O2.js +0 -113
  123. package/dist/chunk-SHTXZ4O2.js.map +0 -1
  124. package/dist/chunk-VIDQF3F5.js.map +0 -1
  125. package/dist/chunk-VUINJM5M.js.map +0 -1
  126. package/dist/chunk-YGYXHNAQ.js.map +0 -1
  127. package/dist/chunk-YV7J7X5N.js.map +0 -1
  128. /package/dist/{chunk-FZWAFVAA.js.map → chunk-4FBZZIYD.js.map} +0 -0
  129. /package/dist/{chunk-OTYQPHPL.js.map → chunk-6SOJM3VR.js.map} +0 -0
  130. /package/dist/{chunk-Z7VFTS2J.js.map → chunk-CY6U5S3X.js.map} +0 -0
  131. /package/dist/{chunk-WJL2NJXN.js.map → chunk-GSH6QNNS.js.map} +0 -0
  132. /package/dist/{chunk-RBNA5AZT.js.map → chunk-L3JOU6XM.js.map} +0 -0
  133. /package/dist/{chunk-ZZ2HOPME.js.map → chunk-TWS7AZEY.js.map} +0 -0
  134. /package/dist/{run-campaign-4Y5V5CN3.js.map → run-campaign-RDGAM5KJ.js.map} +0 -0
package/dist/rl.js CHANGED
@@ -1,22 +1,22 @@
1
+ import {
2
+ detectRewardHacking,
3
+ extractVerifiableReward,
4
+ extractVerifiableRewardsFromRecords,
5
+ filterDeterministicallyRewarded
6
+ } from "./chunk-5HRORJQY.js";
1
7
  import {
2
8
  doublyRobust,
3
9
  inverseProbabilityWeighting,
4
10
  offPolicyEstimateAll,
5
11
  selfNormalizedImportanceWeighting
6
12
  } from "./chunk-4DIJWVUT.js";
7
- import {
8
- detectRewardHacking,
9
- extractVerifiableReward,
10
- extractVerifiableRewardsFromRecords,
11
- filterDeterministicallyRewarded
12
- } from "./chunk-YV7J7X5N.js";
13
13
  import {
14
14
  FileSystemOutcomeStore,
15
15
  InMemoryOutcomeStore
16
16
  } from "./chunk-3RF76KTD.js";
17
17
  import {
18
18
  runEvalCampaign
19
- } from "./chunk-WJL2NJXN.js";
19
+ } from "./chunk-GSH6QNNS.js";
20
20
  import "./chunk-CVVHBFGN.js";
21
21
  import {
22
22
  rubricPredictiveValidity
@@ -24,11 +24,16 @@ import {
24
24
  import {
25
25
  evaluateInterimReleaseConfidence
26
26
  } from "./chunk-MAZ26DC7.js";
27
- import "./chunk-Z7VFTS2J.js";
27
+ import "./chunk-CY6U5S3X.js";
28
28
  import {
29
29
  benjaminiHochberg,
30
30
  wilcoxonSignedRank
31
- } from "./chunk-IDVBLYCY.js";
31
+ } from "./chunk-LMZQ2Z4U.js";
32
+ import {
33
+ observationsFromRunRecords,
34
+ thompsonCurriculum,
35
+ varianceBasedCurriculum
36
+ } from "./chunk-VZSRQ272.js";
32
37
  import "./chunk-SBCB6VZY.js";
33
38
  import "./chunk-PC4UYEBM.js";
34
39
  import "./chunk-KWRRMR3J.js";
@@ -612,149 +617,6 @@ function buildPairwiseFromCampaign(input) {
612
617
  return outcomes;
613
618
  }
614
619
 
615
- // src/rl/active-curriculum.ts
616
- function varianceBasedCurriculum(observations, candidateCells, opts) {
617
- const variancePrior = opts.variancePrior ?? 0.05;
618
- const floor = opts.floorPerCell ?? 1;
619
- const budget = opts.budget;
620
- const grouped = /* @__PURE__ */ new Map();
621
- for (const o of observations) {
622
- const k = `${o.variantId}::${o.scenarioId}`;
623
- const arr = grouped.get(k) ?? [];
624
- arr.push(o.score);
625
- grouped.set(k, arr);
626
- }
627
- const cellStats = candidateCells.map((c) => {
628
- const k = `${c.variantId}::${c.scenarioId}`;
629
- const samples = grouped.get(k) ?? [];
630
- const n = samples.length;
631
- const mean = n === 0 ? 0.5 : samples.reduce((s, v) => s + v, 0) / n;
632
- const variance = n < 2 ? variancePrior : samples.reduce((s, v) => s + (v - mean) ** 2, 0) / (n - 1) + variancePrior;
633
- const weight = Math.sqrt(variance) + 1 / Math.sqrt(Math.max(1, n));
634
- return { variantId: c.variantId, scenarioId: c.scenarioId, n, mean, variance, weight };
635
- });
636
- const floorTotal = floor * cellStats.length;
637
- if (floorTotal >= budget) {
638
- const each = Math.max(1, Math.floor(budget / Math.max(1, cellStats.length)));
639
- return cellStats.map((c) => ({
640
- variantId: c.variantId,
641
- scenarioId: c.scenarioId,
642
- count: each,
643
- reason: `floor allocation (budget tight; n=${c.n})`
644
- }));
645
- }
646
- const remaining = budget - floorTotal;
647
- const totalWeight = cellStats.reduce((s, c) => s + c.weight, 0);
648
- return cellStats.map((c) => {
649
- const proportional = totalWeight === 0 ? 0 : Math.round(c.weight / totalWeight * remaining);
650
- return {
651
- variantId: c.variantId,
652
- scenarioId: c.scenarioId,
653
- count: floor + proportional,
654
- reason: `variance ${c.variance.toFixed(3)} (n=${c.n}, mean=${c.mean.toFixed(3)})`
655
- };
656
- });
657
- }
658
- function thompsonCurriculum(observations, candidateCells, opts) {
659
- const threshold = opts.decisionThreshold ?? 0.5;
660
- const alpha0 = opts.priorAlpha ?? 1;
661
- const beta0 = opts.priorBeta ?? 1;
662
- const rng = makeRng(opts.seed);
663
- const grouped = /* @__PURE__ */ new Map();
664
- for (const o of observations) {
665
- const k = `${o.variantId}::${o.scenarioId}`;
666
- const cur = grouped.get(k) ?? { passes: 0, failures: 0 };
667
- const pass = o.pass ?? o.score >= threshold;
668
- if (pass) cur.passes += 1;
669
- else cur.failures += 1;
670
- grouped.set(k, cur);
671
- }
672
- const stats = candidateCells.map((c) => {
673
- const k = `${c.variantId}::${c.scenarioId}`;
674
- const cur = grouped.get(k) ?? { passes: 0, failures: 0 };
675
- const a = alpha0 + cur.passes;
676
- const b = beta0 + cur.failures;
677
- const sampled = sampleBeta(a, b, rng);
678
- const distance = Math.abs(sampled - threshold);
679
- const variance = a * b / ((a + b) ** 2 * (a + b + 1));
680
- const sigma = Math.max(0.05, Math.sqrt(variance));
681
- const weight = Math.exp(-((distance / sigma) ** 2));
682
- return {
683
- variantId: c.variantId,
684
- scenarioId: c.scenarioId,
685
- n: cur.passes + cur.failures,
686
- sampled,
687
- sigma,
688
- weight,
689
- a,
690
- b
691
- };
692
- });
693
- const totalWeight = stats.reduce((s, c) => s + c.weight, 0);
694
- return stats.map((c) => {
695
- const proportional = totalWeight === 0 ? 0 : Math.round(c.weight / totalWeight * opts.budget);
696
- return {
697
- variantId: c.variantId,
698
- scenarioId: c.scenarioId,
699
- count: Math.max(0, proportional),
700
- reason: `Beta(${c.a.toFixed(1)},${c.b.toFixed(1)}) sample=${c.sampled.toFixed(3)} (target ${threshold})`
701
- };
702
- });
703
- }
704
- function observationsFromRunRecords(runs, opts = {}) {
705
- const threshold = opts.passThreshold ?? 0.5;
706
- const useHoldout = opts.useHoldout ?? true;
707
- const out = [];
708
- for (const r of runs) {
709
- if (!r.scenarioId) continue;
710
- const score = useHoldout ? r.outcome.holdoutScore ?? r.outcome.searchScore : r.outcome.searchScore ?? r.outcome.holdoutScore;
711
- if (typeof score !== "number" || !Number.isFinite(score)) continue;
712
- out.push({
713
- variantId: r.candidateId,
714
- scenarioId: r.scenarioId,
715
- score,
716
- pass: score >= threshold
717
- });
718
- }
719
- return out;
720
- }
721
- function makeRng(seed) {
722
- if (seed === void 0) return Math.random;
723
- let s = seed >>> 0;
724
- return () => {
725
- s = s + 1831565813 >>> 0;
726
- let t = s;
727
- t = Math.imul(t ^ t >>> 15, t | 1);
728
- t ^= t + Math.imul(t ^ t >>> 7, t | 61);
729
- return ((t ^ t >>> 14) >>> 0) / 4294967296;
730
- };
731
- }
732
- function sampleBeta(alpha, beta, rng) {
733
- const a = Math.max(1, alpha);
734
- const b = Math.max(1, beta);
735
- const x = sampleGamma(a, rng);
736
- const y = sampleGamma(b, rng);
737
- return x / (x + y);
738
- }
739
- function sampleGamma(shape, rng) {
740
- const d = shape - 1 / 3;
741
- const c = 1 / Math.sqrt(9 * d);
742
- while (true) {
743
- let x;
744
- let v;
745
- do {
746
- const u1 = rng() || 1e-12;
747
- const u2 = rng() || 1e-12;
748
- x = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
749
- v = 1 + c * x;
750
- } while (v <= 0);
751
- v = v * v * v;
752
- const u = rng();
753
- if (u < 1 - 0.0331 * x ** 4) return d * v;
754
- if (Math.log(u) < 0.5 * x * x + d * (1 - v + Math.log(v))) return d * v;
755
- }
756
- }
757
-
758
620
  // src/rl/adaptation-eval.ts
759
621
  async function runAdaptationCurve(opts) {
760
622
  const ks = opts.ks ?? [0, 1, 2, 4, 8, 16];
@@ -809,7 +671,7 @@ async function runAdaptationCurve(opts) {
809
671
  function compareAdaptationCurves(a, b, opts = {}) {
810
672
  const conf = opts.confidence ?? 0.95;
811
673
  const resamples = opts.bootstrapResamples ?? 500;
812
- const rng = makeRng2(opts.seed);
674
+ const rng = makeRng(opts.seed);
813
675
  const perK = [];
814
676
  for (const ap of a.points) {
815
677
  const bp = b.points.find((p) => p.k === ap.k);
@@ -841,7 +703,7 @@ function compareAdaptationCurves(a, b, opts = {}) {
841
703
  function firstPassK(curve, threshold = 0.5) {
842
704
  return curve.points.find((p) => p.passRate >= threshold)?.k ?? null;
843
705
  }
844
- function makeRng2(seed) {
706
+ function makeRng(seed) {
845
707
  if (seed === void 0) return Math.random;
846
708
  let s = seed >>> 0;
847
709
  return () => {
@@ -1661,22 +1523,239 @@ function buildSummary(args) {
1661
1523
  }
1662
1524
  return lines.join(" | ");
1663
1525
  }
1526
+
1527
+ // src/rl/sim-fidelity.ts
1528
+ var ABSENT_CATEGORY = "(absent)";
1529
+ var DEFAULT_MIN_N_PER_FEATURE = 20;
1530
+ var DEFAULT_QUANTILE_BUCKETS = 4;
1531
+ var REPRESENTATIVE_MIN_FIDELITY = 0.8;
1532
+ var TOP_SHIFT_COUNT = 5;
1533
+ var defaultBehaviorFeatures = (record) => {
1534
+ const raw = record.outcome?.raw ?? {};
1535
+ const toolErrors = finiteOrNull(raw.tool_errors);
1536
+ const turnsAborted = finiteOrNull(raw.turns_aborted);
1537
+ const completion = record.completion;
1538
+ return {
1539
+ score: finiteOrNull(record.outcome?.holdoutScore) ?? finiteOrNull(record.outcome?.searchScore),
1540
+ failure_class: record.failureClass ?? null,
1541
+ wall_ms: finiteOrNull(record.wallMs),
1542
+ output_tokens: finiteOrNull(record.tokenUsage?.output),
1543
+ turn_count: finiteOrNull(raw.turns_completed) ?? finiteOrNull(raw.assistant_messages),
1544
+ tool_errors: toolErrors,
1545
+ tool_error_recovery: toolErrorRecovery(toolErrors, turnsAborted, record.failureClass),
1546
+ completion_length: typeof completion === "string" ? completion.length : null
1547
+ };
1548
+ };
1549
+ function toolErrorRecovery(toolErrors, turnsAborted, failureClass) {
1550
+ if (toolErrors === null) return null;
1551
+ if (toolErrors === 0) return "no-tool-errors";
1552
+ const failed = (turnsAborted ?? 0) > 0 || failureClass !== void 0 && failureClass !== "success";
1553
+ return failed ? "unrecovered" : "recovered";
1554
+ }
1555
+ function finiteOrNull(value) {
1556
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
1557
+ }
1558
+ function jsDivergence(p, q) {
1559
+ const keys = /* @__PURE__ */ new Set([...Object.keys(p), ...Object.keys(q)]);
1560
+ if (keys.size === 0) {
1561
+ throw new ValidationError("jsDivergence: both histograms are empty");
1562
+ }
1563
+ let pSum = 0;
1564
+ let qSum = 0;
1565
+ for (const key of keys) {
1566
+ const pv = p[key] ?? 0;
1567
+ const qv = q[key] ?? 0;
1568
+ if (!Number.isFinite(pv) || !Number.isFinite(qv) || pv < 0 || qv < 0) {
1569
+ throw new ValidationError(`jsDivergence: negative or non-finite count for category "${key}"`);
1570
+ }
1571
+ pSum += pv;
1572
+ qSum += qv;
1573
+ }
1574
+ if (pSum === 0 || qSum === 0) {
1575
+ throw new ValidationError("jsDivergence: a histogram with zero total mass has no distribution");
1576
+ }
1577
+ let divergence = 0;
1578
+ for (const key of keys) {
1579
+ const pp = (p[key] ?? 0) / pSum;
1580
+ const qp = (q[key] ?? 0) / qSum;
1581
+ const m = (pp + qp) / 2;
1582
+ if (pp > 0) divergence += 0.5 * pp * Math.log2(pp / m);
1583
+ if (qp > 0) divergence += 0.5 * qp * Math.log2(qp / m);
1584
+ }
1585
+ return Math.min(1, Math.max(0, divergence));
1586
+ }
1587
+ function quantileEdges(values, bucketCount = DEFAULT_QUANTILE_BUCKETS) {
1588
+ if (values.length === 0) {
1589
+ throw new ValidationError("quantileEdges: requires at least one value");
1590
+ }
1591
+ if (!Number.isInteger(bucketCount) || bucketCount < 2) {
1592
+ throw new ValidationError(
1593
+ `quantileEdges: bucketCount must be an integer >= 2, got ${bucketCount}`
1594
+ );
1595
+ }
1596
+ const sorted = [...values].sort((a, b) => a - b);
1597
+ const edges = [];
1598
+ for (let k = 1; k < bucketCount; k++) {
1599
+ const pos = k / bucketCount * (sorted.length - 1);
1600
+ const lo = sorted[Math.floor(pos)];
1601
+ const hi = sorted[Math.ceil(pos)];
1602
+ edges.push(lo + (pos - Math.floor(pos)) * (hi - lo));
1603
+ }
1604
+ return [...new Set(edges)];
1605
+ }
1606
+ function bucketLabel(value, edges) {
1607
+ let i = 0;
1608
+ while (i < edges.length && value >= edges[i]) i++;
1609
+ const lo = i === 0 ? "-inf" : String(edges[i - 1]);
1610
+ const hi = i === edges.length ? "+inf" : String(edges[i]);
1611
+ return `[${lo},${hi})`;
1612
+ }
1613
+ function simFidelityReport(simulated, production, opts = {}) {
1614
+ if (simulated.length === 0) {
1615
+ throw new ValidationError("simFidelityReport: simulated records are empty");
1616
+ }
1617
+ if (production.length === 0) {
1618
+ throw new ValidationError("simFidelityReport: production records are empty");
1619
+ }
1620
+ const extract = opts.features ?? defaultBehaviorFeatures;
1621
+ const minN = opts.minNPerFeature ?? DEFAULT_MIN_N_PER_FEATURE;
1622
+ const simMaps = simulated.map(extract);
1623
+ const prodMaps = production.map(extract);
1624
+ const featureNames = [];
1625
+ const seen = /* @__PURE__ */ new Set();
1626
+ for (const map of [...simMaps, ...prodMaps]) {
1627
+ for (const name of Object.keys(map)) {
1628
+ if (!seen.has(name)) {
1629
+ seen.add(name);
1630
+ featureNames.push(name);
1631
+ }
1632
+ }
1633
+ }
1634
+ const perDimension = [];
1635
+ const insufficientData = [];
1636
+ for (const feature of featureNames) {
1637
+ const simVals = simMaps.map((m) => m[feature] ?? null);
1638
+ const prodVals = prodMaps.map((m) => m[feature] ?? null);
1639
+ const nSim = simVals.filter((v) => v !== null).length;
1640
+ const nProd = prodVals.filter((v) => v !== null).length;
1641
+ if (nSim < minN || nProd < minN) {
1642
+ insufficientData.push(feature);
1643
+ continue;
1644
+ }
1645
+ const { sim, prod } = histograms(feature, simVals, prodVals);
1646
+ perDimension.push({
1647
+ feature,
1648
+ divergence: jsDivergence(sim, prod),
1649
+ topShifts: topShifts(sim, simVals.length, prod, prodVals.length),
1650
+ nSim,
1651
+ nProd
1652
+ });
1653
+ }
1654
+ if (perDimension.length === 0) {
1655
+ return { perDimension, fidelity: Number.NaN, insufficientData, verdict: "insufficient-data" };
1656
+ }
1657
+ const fidelity = 1 - perDimension.reduce((sum, d) => sum + d.divergence, 0) / perDimension.length;
1658
+ return {
1659
+ perDimension,
1660
+ fidelity,
1661
+ insufficientData,
1662
+ verdict: fidelity >= REPRESENTATIVE_MIN_FIDELITY ? "representative" : "skewed"
1663
+ };
1664
+ }
1665
+ function histograms(feature, simVals, prodVals) {
1666
+ const kinds = /* @__PURE__ */ new Set();
1667
+ for (const v of [...simVals, ...prodVals]) {
1668
+ if (v !== null) kinds.add(typeof v);
1669
+ }
1670
+ if (kinds.size > 1) {
1671
+ throw new ValidationError(
1672
+ `simFidelityReport: feature "${feature}" mixes string and number values \u2014 an extractor must return one kind per feature`
1673
+ );
1674
+ }
1675
+ let toCategory;
1676
+ if (kinds.has("number")) {
1677
+ const union = [];
1678
+ for (const v of [...simVals, ...prodVals]) {
1679
+ if (v !== null) union.push(v);
1680
+ }
1681
+ const edges = quantileEdges(union);
1682
+ toCategory = (v) => bucketLabel(v, edges);
1683
+ } else {
1684
+ toCategory = (v) => v;
1685
+ }
1686
+ const count = (vals) => {
1687
+ const hist = {};
1688
+ for (const v of vals) {
1689
+ const key = v === null ? ABSENT_CATEGORY : toCategory(v);
1690
+ hist[key] = (hist[key] ?? 0) + 1;
1691
+ }
1692
+ return hist;
1693
+ };
1694
+ return { sim: count(simVals), prod: count(prodVals) };
1695
+ }
1696
+ function topShifts(sim, simTotal, prod, prodTotal) {
1697
+ const keys = [.../* @__PURE__ */ new Set([...Object.keys(sim), ...Object.keys(prod)])];
1698
+ const shifts = keys.map((value) => ({
1699
+ value,
1700
+ pSim: (sim[value] ?? 0) / simTotal,
1701
+ pProd: (prod[value] ?? 0) / prodTotal
1702
+ }));
1703
+ shifts.sort((a, b) => {
1704
+ const delta = Math.abs(b.pSim - b.pProd) - Math.abs(a.pSim - a.pProd);
1705
+ return delta !== 0 ? delta : a.value.localeCompare(b.value);
1706
+ });
1707
+ return shifts.slice(0, TOP_SHIFT_COUNT);
1708
+ }
1709
+ function easyModeCheck(simulated, production, opts = {}) {
1710
+ if (simulated.length === 0) {
1711
+ throw new ValidationError("easyModeCheck: simulated records are empty");
1712
+ }
1713
+ if (production.length === 0) {
1714
+ throw new ValidationError("easyModeCheck: production records are empty");
1715
+ }
1716
+ const threshold = opts.passThreshold ?? 0.5;
1717
+ const tolerance = opts.inflationTolerance ?? 0.1;
1718
+ const passRate = (records, side) => {
1719
+ let passes = 0;
1720
+ for (const r of records) {
1721
+ const score = finiteOrNull(r.outcome?.holdoutScore) ?? finiteOrNull(r.outcome?.searchScore);
1722
+ if (score === null) {
1723
+ throw new ValidationError(
1724
+ `easyModeCheck: ${side} run "${r.runId}" carries neither holdoutScore nor searchScore`
1725
+ );
1726
+ }
1727
+ if (score >= threshold) passes++;
1728
+ }
1729
+ return passes / records.length;
1730
+ };
1731
+ const simPassRate = passRate(simulated, "simulated");
1732
+ const prodPassRate = passRate(production, "production");
1733
+ const gap = simPassRate - prodPassRate;
1734
+ return { simPassRate, prodPassRate, gap, inflated: gap > tolerance };
1735
+ }
1664
1736
  export {
1737
+ ABSENT_CATEGORY,
1738
+ DEFAULT_MIN_N_PER_FEATURE,
1739
+ DEFAULT_QUANTILE_BUCKETS,
1665
1740
  FileSystemOutcomeStore,
1666
1741
  InMemoryOutcomeStore,
1667
1742
  PredictiveValidityResearcher,
1743
+ REPRESENTATIVE_MIN_FIDELITY,
1668
1744
  adversarialScenarioSearch,
1669
1745
  appendToCorpus,
1670
1746
  applyEloUpdate,
1671
1747
  bestOfN,
1748
+ bucketLabel,
1672
1749
  buildDatasetFromCorpus,
1673
1750
  buildPairwiseFromCampaign,
1674
1751
  buildRlDataset,
1675
1752
  campaignToRunRecords,
1676
1753
  compareAdaptationCurves,
1677
1754
  datasheetToMarkdown,
1755
+ defaultBehaviorFeatures,
1678
1756
  detectRewardHacking,
1679
1757
  doublyRobust,
1758
+ easyModeCheck,
1680
1759
  extractPreferences,
1681
1760
  extractStepRewards,
1682
1761
  extractVerifiableReward,
@@ -1686,10 +1765,12 @@ export {
1686
1765
  fitBradleyTerry,
1687
1766
  injectIrrelevantClause,
1688
1767
  inverseProbabilityWeighting,
1768
+ jsDivergence,
1689
1769
  observationsFromRunRecords,
1690
1770
  offPolicyEstimateAll,
1691
1771
  paretoFrontier,
1692
1772
  prmTrainingPairs,
1773
+ quantileEdges,
1693
1774
  readCorpus,
1694
1775
  renameVariables,
1695
1776
  runAdaptationCurve,
@@ -1701,6 +1782,7 @@ export {
1701
1782
  selfConsistency,
1702
1783
  selfNormalizedImportanceWeighting,
1703
1784
  shuffleOrder,
1785
+ simFidelityReport,
1704
1786
  stepRewardsToJsonl,
1705
1787
  thompsonCurriculum,
1706
1788
  toAnthropicFormat,