@tangle-network/agent-eval 0.119.0 → 0.120.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 (55) hide show
  1. package/CHANGELOG.md +25 -3
  2. package/README.md +0 -26
  3. package/dist/analyst/index.d.ts +30 -39
  4. package/dist/analyst/index.js +9 -5
  5. package/dist/analyst/index.js.map +1 -1
  6. package/dist/benchmarks/index.d.ts +11 -5
  7. package/dist/benchmarks/index.js +7 -7
  8. package/dist/campaign/index.d.ts +121 -66
  9. package/dist/campaign/index.js +26 -10
  10. package/dist/{chunk-RLCJQ6VZ.js → chunk-32BZXMSO.js} +385 -120
  11. package/dist/chunk-32BZXMSO.js.map +1 -0
  12. package/dist/{chunk-XJ7JVCHB.js → chunk-3A246TSA.js} +2 -2
  13. package/dist/{chunk-LMJ2TGWJ.js → chunk-JM2SKQMS.js} +2 -2
  14. package/dist/{chunk-F6YUH3L4.js → chunk-JN2FCO5W.js} +14 -168
  15. package/dist/chunk-JN2FCO5W.js.map +1 -0
  16. package/dist/{chunk-D7AEXSM5.js → chunk-PICTDURQ.js} +2 -2
  17. package/dist/{chunk-6QIM2EAP.js → chunk-PXD6ZFNY.js} +99 -1
  18. package/dist/chunk-PXD6ZFNY.js.map +1 -0
  19. package/dist/{chunk-GERDAIAL.js → chunk-QWMPPZ3X.js} +3 -3
  20. package/dist/{chunk-7A4LIMMY.js → chunk-S5TT5R3L.js} +229 -97
  21. package/dist/chunk-S5TT5R3L.js.map +1 -0
  22. package/dist/{chunk-ZYHJNKI3.js → chunk-ULOKLHIQ.js} +42 -5
  23. package/dist/chunk-ULOKLHIQ.js.map +1 -0
  24. package/dist/{chunk-FIUFRXP3.js → chunk-WW2A73HW.js} +49 -91
  25. package/dist/chunk-WW2A73HW.js.map +1 -0
  26. package/dist/{chunk-PAHNGS65.js → chunk-XDIRG3TO.js} +175 -11
  27. package/dist/chunk-XDIRG3TO.js.map +1 -0
  28. package/dist/contract/index.d.ts +76 -43
  29. package/dist/contract/index.js +596 -32
  30. package/dist/contract/index.js.map +1 -1
  31. package/dist/index.d.ts +92 -75
  32. package/dist/index.js +38 -35
  33. package/dist/index.js.map +1 -1
  34. package/dist/openapi.json +1 -1
  35. package/dist/rl.d.ts +11 -5
  36. package/dist/{run-campaign-OBXSN5WK.js → run-campaign-HNFPJET4.js} +2 -2
  37. package/dist/traces.d.ts +39 -25
  38. package/dist/traces.js +3 -5
  39. package/docs/trace-analysis.md +6 -2
  40. package/package.json +2 -7
  41. package/dist/chunk-6QIM2EAP.js.map +0 -1
  42. package/dist/chunk-7A4LIMMY.js.map +0 -1
  43. package/dist/chunk-F6YUH3L4.js.map +0 -1
  44. package/dist/chunk-FIUFRXP3.js.map +0 -1
  45. package/dist/chunk-PAHNGS65.js.map +0 -1
  46. package/dist/chunk-RLCJQ6VZ.js.map +0 -1
  47. package/dist/chunk-ZYHJNKI3.js.map +0 -1
  48. package/dist/primeintellect/index.d.ts +0 -311
  49. package/dist/primeintellect/index.js +0 -348
  50. package/dist/primeintellect/index.js.map +0 -1
  51. /package/dist/{chunk-XJ7JVCHB.js.map → chunk-3A246TSA.js.map} +0 -0
  52. /package/dist/{chunk-LMJ2TGWJ.js.map → chunk-JM2SKQMS.js.map} +0 -0
  53. /package/dist/{chunk-D7AEXSM5.js.map → chunk-PICTDURQ.js.map} +0 -0
  54. /package/dist/{chunk-GERDAIAL.js.map → chunk-QWMPPZ3X.js.map} +0 -0
  55. /package/dist/{run-campaign-OBXSN5WK.js.map → run-campaign-HNFPJET4.js.map} +0 -0
@@ -13,12 +13,15 @@ import {
13
13
  import {
14
14
  analyzeRuns,
15
15
  summarizeExecution
16
- } from "../chunk-XJ7JVCHB.js";
16
+ } from "../chunk-3A246TSA.js";
17
17
  import {
18
18
  REFERENCE_EQUIVALENCE_INPUT_LIMITS,
19
19
  REFERENCE_EQUIVALENCE_JUDGE_VERSION,
20
20
  buildEvidenceVector,
21
+ buildLoopProvenanceRecord,
22
+ campaignBreakdown,
21
23
  campaignMeanComposite,
24
+ canonicalDigest,
22
25
  composeGate,
23
26
  createReferenceEquivalenceJudge,
24
27
  defaultProductionGate,
@@ -26,28 +29,36 @@ import {
26
29
  evolutionaryProposer,
27
30
  gepaProposer,
28
31
  heldOutGate,
32
+ loopProvenanceArgsFromResult,
33
+ pairArms,
29
34
  paretoPolicy,
30
35
  paretoSignificanceGate,
31
36
  powerPreflight,
37
+ renderSurfaceDiff,
32
38
  runEval,
33
39
  runImprovementLoop,
34
40
  runReferenceEquivalenceJudge,
35
41
  surfaceContentHash,
36
- surfaceHash
37
- } from "../chunk-RLCJQ6VZ.js";
42
+ surfaceHash,
43
+ verifyLoopProvenanceRecord
44
+ } from "../chunk-32BZXMSO.js";
38
45
  import {
46
+ assertCampaignSplitIdentity,
47
+ assertRealAgentReceipts,
48
+ campaignCoverage,
49
+ campaignSplitDigest,
39
50
  createRunCostLedger,
40
51
  fsCampaignStorage,
41
52
  inMemoryCampaignStorage,
42
53
  resolveRunDir,
43
54
  runCampaign
44
- } from "../chunk-PAHNGS65.js";
55
+ } from "../chunk-XDIRG3TO.js";
45
56
  import {
46
57
  buildDefaultAnalystRegistry,
47
58
  createChatClient
48
- } from "../chunk-GERDAIAL.js";
59
+ } from "../chunk-QWMPPZ3X.js";
49
60
  import "../chunk-HHWE3POT.js";
50
- import "../chunk-7A4LIMMY.js";
61
+ import "../chunk-S5TT5R3L.js";
51
62
  import {
52
63
  FileSystemOutcomeStore,
53
64
  InMemoryOutcomeStore
@@ -55,8 +66,12 @@ import {
55
66
  import "../chunk-ARU2PZFM.js";
56
67
  import "../chunk-NJC7U437.js";
57
68
  import "../chunk-DPZAEKA6.js";
58
- import "../chunk-PJQFMIOX.js";
59
- import "../chunk-JHOJHHU7.js";
69
+ import {
70
+ pairedBootstrap
71
+ } from "../chunk-PJQFMIOX.js";
72
+ import {
73
+ CostLedger
74
+ } from "../chunk-JHOJHHU7.js";
60
75
  import "../chunk-VI2UW6B6.js";
61
76
  import {
62
77
  recordAggregateMeasurements,
@@ -65,7 +80,7 @@ import {
65
80
  import "../chunk-PXE2VKMX.js";
66
81
  import "../chunk-ZET2UAYW.js";
67
82
  import "../chunk-GGE4NNQT.js";
68
- import "../chunk-6QIM2EAP.js";
83
+ import "../chunk-PXD6ZFNY.js";
69
84
  import "../chunk-PC4UYEBM.js";
70
85
  import {
71
86
  parseRunRecordSafe
@@ -249,29 +264,17 @@ async function runSelfImprove(opts, costLedger, startedAt, runDir, storage) {
249
264
  candidateCandidateId: "winner"
250
265
  });
251
266
  const durationMs = Date.now() - startedAt;
252
- const workerRecords = opts.collectWorkerRecords?.() ?? cellsToRunRecords(result.winnerOnHoldout.cells, "winner", runDir, result.winnerSurface);
253
267
  const { record: provenance } = await emitLoopProvenance({
254
- runId: `${runDir}#${startedAt}`,
255
- runDir,
256
- timestamp: new Date(startedAt).toISOString(),
257
- baselineSurface: opts.baselineSurface,
258
- winnerSurface: result.winnerSurface,
259
- winnerLabel: result.winnerLabel,
260
- winnerRationale: result.winnerRationale,
261
- diff: result.promotedDiff,
262
- baselineSearchComposite: campaignMeanComposite(result.baselineCampaign),
263
- generations: result.generations.map((g) => ({
264
- generationIndex: g.record.generationIndex,
265
- candidates: g.record.candidates,
266
- promoted: g.record.promoted,
267
- surfaces: g.surfaces.map((s) => ({ surfaceHash: s.surfaceHash, surface: s.surface }))
268
- })),
269
- gate: result.gateResult,
270
- baselineOnHoldout: result.baselineOnHoldout,
271
- winnerOnHoldout: result.winnerOnHoldout,
272
- workerRecords,
273
- totalCostUsd: totalCost,
274
- totalDurationMs: durationMs,
268
+ ...loopProvenanceArgsFromResult({
269
+ runId: `${runDir}#${startedAt}`,
270
+ runDir,
271
+ timestamp: new Date(startedAt).toISOString(),
272
+ baselineSurface: opts.baselineSurface,
273
+ result,
274
+ costReceipts: costLedger.list(),
275
+ totalCostUsd: totalCost,
276
+ totalDurationMs: durationMs
277
+ }),
275
278
  storage,
276
279
  hostedClient: opts.hostedTenant ? createHostedClient(opts.hostedTenant) : void 0
277
280
  });
@@ -419,7 +422,13 @@ function cellsToRunRecords(cells, candidateId, runId, surface) {
419
422
  commitSha: "cell",
420
423
  wallMs: cell.durationMs,
421
424
  costUsd: cell.costUsd,
422
- tokenUsage: { input: 0, output: 0 },
425
+ tokenUsage: {
426
+ input: cell.tokenUsage.input,
427
+ output: cell.tokenUsage.output,
428
+ ...cell.tokenUsage.reasoning === void 0 ? {} : { reasoning: cell.tokenUsage.reasoning },
429
+ ...cell.tokenUsage.cached === void 0 ? {} : { cached: cell.tokenUsage.cached },
430
+ ...cell.tokenUsage.cacheWrite === void 0 ? {} : { cacheWrite: cell.tokenUsage.cacheWrite }
431
+ },
423
432
  outcome: {
424
433
  holdoutScore: composite,
425
434
  raw: {},
@@ -536,6 +545,559 @@ function requirePositiveInteger(value, field) {
536
545
  return value;
537
546
  }
538
547
 
548
+ // src/contract/measured-comparison.ts
549
+ import {
550
+ agentImprovementMeasuredComparisonSchema
551
+ } from "@tangle-network/agent-interface";
552
+ function measuredComparisonFromSelfImproveResult(options) {
553
+ const { result } = options;
554
+ verifyLoopProvenanceRecord(result.provenance);
555
+ const power = result.power;
556
+ if (!power) throw new Error("agent improvement comparison requires heldout power analysis");
557
+ if (result.provenance.gate.reasons.length === 0) {
558
+ throw new Error("agent improvement comparison requires measured decision reasons");
559
+ }
560
+ const receiptLedger = new CostLedger({ receipts: result.receipts });
561
+ const receiptCost = receiptLedger.summary();
562
+ const receipts = receiptLedger.list();
563
+ assertRealAgentReceipts(receipts, { allowMixed: false });
564
+ const receiptsById = new Map(receipts.map((receipt) => [receipt.callId, receipt]));
565
+ const receiptIdsByCell = indexReceiptIdsByCell(receipts);
566
+ assertCompleteMeasuredCampaign(
567
+ result.raw.baselineOnHoldout,
568
+ "heldout baseline",
569
+ receiptsById,
570
+ receiptIdsByCell
571
+ );
572
+ assertCompleteMeasuredCampaign(
573
+ result.raw.winnerOnHoldout,
574
+ "heldout candidate",
575
+ receiptsById,
576
+ receiptIdsByCell
577
+ );
578
+ const pairs = pairMeasuredCells(
579
+ result.raw.baselineOnHoldout.cells,
580
+ result.raw.winnerOnHoldout.cells
581
+ );
582
+ const composite = measuredObjective(
583
+ {
584
+ kind: "objective",
585
+ name: "composite",
586
+ direction: "higher-is-better",
587
+ unit: "score"
588
+ },
589
+ pairs,
590
+ measuredComposite
591
+ );
592
+ assertMeasuredNumber(result.lift, composite.delta, "heldout lift");
593
+ assertMeasuredNumber(result.baseline.compositeMean, composite.baseline, "heldout baseline");
594
+ assertMeasuredNumber(result.winner.compositeMean, composite.candidate, "heldout candidate");
595
+ const baselineContentHash = surfaceContentHash(options.baselineSurface);
596
+ const candidateContentHash = surfaceContentHash(result.winner.surface);
597
+ const canonicalDiff = baselineContentHash === candidateContentHash ? "" : renderSurfaceDiff(result.raw.winnerSurface, options.baselineSurface);
598
+ assertMeasuredIdentity(result.gateDecision, result.raw.gateResult.decision, "raw decision");
599
+ assertMeasuredIdentity(
600
+ candidateContentHash,
601
+ surfaceContentHash(result.raw.winnerSurface),
602
+ "raw winner surface"
603
+ );
604
+ assertMeasuredIdentity(result.diff, canonicalDiff, "surface diff");
605
+ assertMeasuredIdentity(result.raw.promotedDiff, canonicalDiff, "raw surface diff");
606
+ if (result.gateDecision === "ship" && (baselineContentHash === candidateContentHash || result.diff.trim().length === 0)) {
607
+ throw new Error("a shipped improvement requires a changed surface and non-empty diff");
608
+ }
609
+ assertMeasuredIdentity(
610
+ canonicalDigest(power),
611
+ canonicalDigest(
612
+ powerPreflight({
613
+ baselineComposites: pairs.map(([cell]) => measuredComposite(cell)),
614
+ sharedScorerChannel: true
615
+ })
616
+ ),
617
+ "power analysis"
618
+ );
619
+ assertMeasuredNumber(power.n, composite.n, "power sample size");
620
+ assertMeasuredNumber(power.confidence, 0.95, "power confidence");
621
+ assertCompleteMeasuredCampaign(
622
+ result.raw.baselineCampaign,
623
+ "search baseline",
624
+ receiptsById,
625
+ receiptIdsByCell
626
+ );
627
+ assertGenerationMeasurements(result.raw.generations, receiptsById, receiptIdsByCell);
628
+ if (result.raw.neutralizedOnHoldout === void 0 !== (result.raw.neutralizedSurface === void 0)) {
629
+ throw new Error("neutralized surface and campaign must be supplied together");
630
+ }
631
+ if (result.raw.neutralizedOnHoldout) {
632
+ assertCompleteMeasuredCampaign(
633
+ result.raw.neutralizedOnHoldout,
634
+ "heldout neutralized",
635
+ receiptsById,
636
+ receiptIdsByCell
637
+ );
638
+ pairMeasuredCells(result.raw.baselineOnHoldout.cells, result.raw.neutralizedOnHoldout.cells);
639
+ }
640
+ const rebuiltProvenance = buildLoopProvenanceRecord(
641
+ loopProvenanceArgsFromResult({
642
+ runId: result.provenance.runId,
643
+ runDir: result.provenance.runDir,
644
+ timestamp: result.provenance.timestamp,
645
+ baselineSurface: options.baselineSurface,
646
+ result: result.raw,
647
+ costReceipts: receipts,
648
+ totalCostUsd: result.totalCostUsd,
649
+ totalDurationMs: result.durationMs
650
+ })
651
+ );
652
+ assertMeasuredIdentity(
653
+ result.provenance.recordDigest,
654
+ rebuiltProvenance.recordDigest,
655
+ "provenance record"
656
+ );
657
+ assertMeasuredIdentity(
658
+ options.benchmark.splitDigest,
659
+ rebuiltProvenance.evidence.holdout.splitDigest,
660
+ "benchmark heldout split"
661
+ );
662
+ const generationsExplored = result.raw.generations.length;
663
+ assertMeasuredNumber(result.generationsExplored, generationsExplored, "generation count");
664
+ assertMeasuredNumber(result.totalCostUsd, result.cost.totalCostUsd, "cost summary");
665
+ assertMeasuredIdentity(
666
+ canonicalDigest(result.cost),
667
+ canonicalDigest(result.raw.cost),
668
+ "raw cost summary"
669
+ );
670
+ if (!result.cost.accountingComplete) {
671
+ throw new Error(
672
+ `cost accounting is incomplete: ${result.cost.incompleteReasons.join("; ") || "unknown reason"}`
673
+ );
674
+ }
675
+ if (!receiptCost.accountingComplete) {
676
+ throw new Error(
677
+ `cost accounting is incomplete: ${receiptCost.incompleteReasons.join("; ") || "unknown reason"}`
678
+ );
679
+ }
680
+ assertMeasuredIdentity(
681
+ canonicalDigest(result.cost),
682
+ canonicalDigest(receiptCost),
683
+ "cost receipt summary"
684
+ );
685
+ assertMeasuredNumber(result.totalCostUsd, receiptCost.totalCostUsd, "cost receipts");
686
+ assertMeasuredOptional(result.winner.label, result.raw.winnerLabel, "raw winner label");
687
+ assertMeasuredOptional(
688
+ result.winner.rationale,
689
+ result.raw.winnerRationale,
690
+ "raw winner rationale"
691
+ );
692
+ return agentImprovementMeasuredComparisonSchema.parse({
693
+ schemaVersion: 1,
694
+ kind: "agent-improvement-measured-comparison",
695
+ benchmark: options.benchmark,
696
+ baselineProfileDigest: options.baselineProfileDigest,
697
+ candidateBundleDigest: options.candidateBundleDigest,
698
+ overall: {
699
+ name: "composite",
700
+ baseline: composite.baseline,
701
+ candidate: composite.candidate,
702
+ delta: composite.delta,
703
+ confidenceInterval: composite.confidenceInterval,
704
+ n: composite.n,
705
+ direction: "higher-is-better",
706
+ unit: "score"
707
+ },
708
+ objectives: measuredObjectives(pairs),
709
+ ...result.winner.label || result.winner.rationale ? {
710
+ candidate: {
711
+ ...result.winner.label ? { label: result.winner.label } : {},
712
+ ...result.winner.rationale ? { rationale: result.winner.rationale } : {}
713
+ }
714
+ } : {},
715
+ decision: {
716
+ outcome: result.gateDecision,
717
+ reasons: rebuiltProvenance.gate.reasons,
718
+ contributingChecks: rebuiltProvenance.gate.contributingGates.map((check) => ({
719
+ name: check.name,
720
+ passed: check.passed
721
+ }))
722
+ },
723
+ power: {
724
+ sufficient: power.scaleAssumed && !power.underpowered,
725
+ n: power.n,
726
+ minimumDetectableDelta: power.mde,
727
+ confidenceLevel: power.confidence,
728
+ scaleAssumed: power.scaleAssumed,
729
+ sharedScorerChannel: power.sharedChannelCaveat !== void 0,
730
+ reason: power.recommendation
731
+ },
732
+ provenance: {
733
+ kind: "agent-eval-loop",
734
+ schema: rebuiltProvenance.schema,
735
+ runId: rebuiltProvenance.runId,
736
+ recordDigest: rebuiltProvenance.recordDigest,
737
+ baselineContentHash,
738
+ candidateContentHash
739
+ },
740
+ diff: canonicalDiff,
741
+ evaluation: {
742
+ generationsExplored,
743
+ durationMs: result.durationMs,
744
+ totalCostUsd: result.totalCostUsd
745
+ }
746
+ });
747
+ }
748
+ function assertGenerationMeasurements(generations, receiptsById, receiptIdsByCell) {
749
+ for (const generation of generations) {
750
+ for (const measured of generation.surfaces) {
751
+ const candidate = generation.record.candidates.find(
752
+ (entry) => entry.surfaceHash === measured.surfaceHash
753
+ );
754
+ if (!candidate) {
755
+ throw new Error(
756
+ `generation ${generation.record.generationIndex} is missing candidate ${measured.surfaceHash}`
757
+ );
758
+ }
759
+ const composite = campaignMeanComposite(measured.campaign);
760
+ const breakdown = campaignBreakdown(measured.campaign);
761
+ const coverage = campaignCoverage(
762
+ measured.campaign.cells,
763
+ measured.campaign.scenarios,
764
+ measured.campaign.reps,
765
+ true
766
+ );
767
+ assertScorableMeasuredCells(
768
+ measured.campaign,
769
+ coverage.scorableCellIds,
770
+ receiptsById,
771
+ receiptIdsByCell
772
+ );
773
+ assertMeasuredNumber(
774
+ candidate.composite,
775
+ composite,
776
+ `candidate ${measured.surfaceHash} composite`
777
+ );
778
+ if (!candidate.coverage) {
779
+ throw new Error(`candidate ${measured.surfaceHash} does not report campaign coverage`);
780
+ }
781
+ assertMeasuredIdentity(
782
+ canonicalDigest(candidate.coverage),
783
+ canonicalDigest({
784
+ expectedCells: coverage.expectedCellIds.length,
785
+ scorableCells: coverage.scorableCellIds.length,
786
+ unscorableCells: coverage.unscorableCells
787
+ }),
788
+ `candidate ${measured.surfaceHash} coverage`
789
+ );
790
+ assertMeasuredIdentity(
791
+ String(candidate.eligibleForPromotion),
792
+ String(coverage.complete),
793
+ `candidate ${measured.surfaceHash} eligibility`
794
+ );
795
+ assertMeasuredIdentity(
796
+ canonicalDigest(candidate.dimensions),
797
+ canonicalDigest(breakdown.dimensions),
798
+ `candidate ${measured.surfaceHash} dimensions`
799
+ );
800
+ assertMeasuredIdentity(
801
+ canonicalDigest(candidate.scenarios),
802
+ canonicalDigest(breakdown.scenarios),
803
+ `candidate ${measured.surfaceHash} scenarios`
804
+ );
805
+ }
806
+ }
807
+ }
808
+ function assertCompleteMeasuredCampaign(campaign, name, receiptsById, receiptIdsByCell) {
809
+ assertCampaignSplitIdentity(campaign.scenarios, campaign.reps, campaign.splitDigest);
810
+ const coverage = campaignCoverage(campaign.cells, campaign.scenarios, campaign.reps, true);
811
+ if (!coverage.complete) {
812
+ throw new Error(
813
+ `${name} is incomplete (${coverage.scorableCellIds.length}/${coverage.expectedCellIds.length} designed cells scorable)`
814
+ );
815
+ }
816
+ assertScorableMeasuredCells(campaign, coverage.scorableCellIds, receiptsById, receiptIdsByCell);
817
+ }
818
+ function assertScorableMeasuredCells(campaign, scorableCellIds, receiptsById, receiptIdsByCell) {
819
+ const cellsById = new Map(campaign.cells.map((cell) => [cell.cellId, cell]));
820
+ for (const cellId of scorableCellIds) {
821
+ const cell = cellsById.get(cellId);
822
+ if (!cell) throw new Error(`measured campaign is missing scorable cell ${cellId}`);
823
+ assertMeasuredCell(cell, receiptsById, receiptIdsByCell, campaign.runDir);
824
+ }
825
+ }
826
+ function measuredObjectives(pairs) {
827
+ const qualityColumns = /* @__PURE__ */ new Map();
828
+ for (const [baseline, candidate] of pairs) {
829
+ for (const cell of [baseline, candidate]) {
830
+ for (const [objective, score] of Object.entries(cell.judgeScores)) {
831
+ if (score.failed) continue;
832
+ qualityColumns.set(`objective:${objective}`, {
833
+ kind: "objective",
834
+ name: objective,
835
+ direction: "higher-is-better",
836
+ unit: "score"
837
+ });
838
+ for (const name of Object.keys(score.dimensions)) {
839
+ qualityColumns.set(`dimension:${objective}:${name}`, {
840
+ kind: "dimension",
841
+ objective,
842
+ name,
843
+ direction: "higher-is-better",
844
+ unit: "score"
845
+ });
846
+ }
847
+ }
848
+ }
849
+ }
850
+ return [
851
+ ...[...qualityColumns.entries()].sort(([left], [right]) => left.localeCompare(right)).map(
852
+ ([, column]) => measuredObjective(column, pairs, (cell) => measuredQuality(cell, column))
853
+ ),
854
+ measuredCostObjective(pairs),
855
+ measuredObjective(
856
+ {
857
+ kind: "latency",
858
+ name: "latency",
859
+ direction: "lower-is-better",
860
+ unit: "milliseconds"
861
+ },
862
+ pairs,
863
+ (cell) => cell.durationMs
864
+ )
865
+ ];
866
+ }
867
+ function measuredCostObjective(pairs) {
868
+ const cells = pairs.flat();
869
+ for (const cell of cells) finiteMeasuredValue(cell.costUsd, "cost:cost");
870
+ return measuredObjective(
871
+ {
872
+ kind: "cost",
873
+ name: "cost",
874
+ direction: "lower-is-better",
875
+ unit: "usd"
876
+ },
877
+ pairs,
878
+ (cell) => cell.costUsd
879
+ );
880
+ }
881
+ function measuredComposite(cell) {
882
+ const values = Object.values(cell.judgeScores).filter((score) => !score.failed).map((score) => score.composite).filter(Number.isFinite);
883
+ if (values.length === 0) {
884
+ throw new Error(`heldout cell ${measuredCellKey(cell)} has no successful composite score`);
885
+ }
886
+ return measuredMean(values);
887
+ }
888
+ function pairMeasuredCells(baselineCells, candidateCells) {
889
+ const cells = [...baselineCells, ...candidateCells];
890
+ const errors = cells.filter((cell) => cell.error);
891
+ if (errors.length > 0) {
892
+ throw new Error(
893
+ `measured objectives cannot publish ${errors.length} errored heldout cells: ${errors.map((cell) => measuredCellKey(cell)).join(", ")}`
894
+ );
895
+ }
896
+ const rows = [
897
+ ...baselineCells.map((cell) => ({
898
+ pairKey: cell.scenarioId,
899
+ repKey: String(cell.rep),
900
+ arm: "baseline",
901
+ cell
902
+ })),
903
+ ...candidateCells.map((cell) => ({
904
+ pairKey: cell.scenarioId,
905
+ repKey: String(cell.rep),
906
+ arm: "candidate",
907
+ cell
908
+ }))
909
+ ];
910
+ const paired = pairArms(rows, { baselineArm: "baseline", treatmentArm: "candidate" });
911
+ if (paired.pairs.length === 0 || paired.unpairedBaseline.length > 0 || paired.unpairedTreatment.length > 0) {
912
+ throw new Error("measured objectives require the same non-empty paired heldout cells");
913
+ }
914
+ return paired.pairs.map((pair) => {
915
+ const baseline = pair.baseline.cell;
916
+ const candidate = pair.treatment.cell;
917
+ if (baseline.seed !== candidate.seed) {
918
+ throw new Error(`heldout cell ${baseline.cellId} does not share one paired seed`);
919
+ }
920
+ return [baseline, candidate];
921
+ });
922
+ }
923
+ function assertMeasuredCell(cell, receiptsById, receiptIdsByCell, runDir) {
924
+ if (!cell.scenarioId.trim() || !Number.isSafeInteger(cell.rep) || cell.rep < 0 || !Number.isSafeInteger(cell.seed) || cell.cellId !== measuredCellKey(cell)) {
925
+ throw new Error(`heldout cell '${cell.cellId}' has an invalid scenario/rep identity`);
926
+ }
927
+ nonnegativeMeasuredValue(cell.costUsd, `heldout cell ${cell.cellId} cost`);
928
+ nonnegativeMeasuredValue(cell.durationMs, `heldout cell ${cell.cellId} latency`);
929
+ if (!cell.tokenUsage) {
930
+ throw new Error(`heldout cell ${cell.cellId} does not report token usage`);
931
+ }
932
+ nonnegativeMeasuredInteger(cell.tokenUsage.input, `heldout cell ${cell.cellId} input tokens`);
933
+ nonnegativeMeasuredInteger(cell.tokenUsage.output, `heldout cell ${cell.cellId} output tokens`);
934
+ if (cell.tokenUsage.cached !== void 0) {
935
+ nonnegativeMeasuredInteger(cell.tokenUsage.cached, `heldout cell ${cell.cellId} cached tokens`);
936
+ }
937
+ if (!Array.isArray(cell.costCallIds)) {
938
+ throw new Error(`heldout cell ${cell.cellId} does not identify its cost receipts`);
939
+ }
940
+ if (new Set(cell.costCallIds).size !== cell.costCallIds.length) {
941
+ throw new Error(`heldout cell ${cell.cellId} repeats a cost receipt`);
942
+ }
943
+ const linkedReceipts = cell.costCallIds.map((callId) => {
944
+ const receipt = receiptsById.get(callId);
945
+ if (!receipt)
946
+ throw new Error(`heldout cell ${cell.cellId} references missing cost receipt ${callId}`);
947
+ if (receipt.tags?.runDir !== runDir || receipt.tags.cellId !== cell.cellId || receipt.tags.scenarioId !== cell.scenarioId || receipt.tags.rep !== String(cell.rep)) {
948
+ throw new Error(`heldout cell ${cell.cellId} references a cost receipt from another cell`);
949
+ }
950
+ return receipt;
951
+ });
952
+ assertMeasuredIdentity(
953
+ canonicalDigest([...cell.costCallIds].sort()),
954
+ canonicalDigest(
955
+ [
956
+ ...receiptIdsByCell.get(receiptCellKey(runDir, cell.cellId, cell.scenarioId, cell.rep)) ?? []
957
+ ].sort()
958
+ ),
959
+ `heldout cell ${cell.cellId} cost receipt IDs`
960
+ );
961
+ const agentReceipts = linkedReceipts.filter((receipt) => receipt.channel === "agent");
962
+ if (agentReceipts.some((receipt) => receipt.error)) {
963
+ throw new Error(`measured cell ${cell.cellId} links a failed agent receipt`);
964
+ }
965
+ assertRealAgentReceipts(agentReceipts, { allowMixed: false });
966
+ assertMeasuredNumber(
967
+ cell.costUsd,
968
+ agentReceipts.reduce((total, receipt) => total + receipt.costUsd, 0),
969
+ `heldout cell ${cell.cellId} cost receipts`
970
+ );
971
+ assertMeasuredNumber(
972
+ cell.tokenUsage.input,
973
+ agentReceipts.reduce((total, receipt) => total + receipt.inputTokens, 0),
974
+ `heldout cell ${cell.cellId} input receipts`
975
+ );
976
+ assertMeasuredNumber(
977
+ cell.tokenUsage.output,
978
+ agentReceipts.reduce((total, receipt) => total + receipt.outputTokens, 0),
979
+ `heldout cell ${cell.cellId} output receipts`
980
+ );
981
+ assertMeasuredNumber(
982
+ cell.tokenUsage.cached ?? 0,
983
+ agentReceipts.reduce((total, receipt) => total + (receipt.cachedTokens ?? 0), 0),
984
+ `heldout cell ${cell.cellId} cached receipts`
985
+ );
986
+ for (const [judge, score] of Object.entries(cell.judgeScores)) {
987
+ if (score.failed) {
988
+ throw new Error(`heldout cell ${cell.cellId} contains failed judge '${judge}'`);
989
+ }
990
+ finiteMeasuredValue(score.composite, `objective:${judge}`);
991
+ for (const [dimension, value] of Object.entries(score.dimensions)) {
992
+ finiteMeasuredValue(value, `dimension:${judge}:${dimension}`);
993
+ }
994
+ }
995
+ measuredComposite(cell);
996
+ }
997
+ function measuredObjective(identity, pairs, value) {
998
+ const key = measuredObjectiveKey(identity);
999
+ const baseline = pairs.map(([cell]) => finiteMeasuredValue(value(cell), key));
1000
+ const candidate = pairs.map(([, cell]) => finiteMeasuredValue(value(cell), key));
1001
+ const interval = pairedBootstrap(baseline, candidate, {
1002
+ confidence: 0.95,
1003
+ resamples: 2e3,
1004
+ statistic: "mean",
1005
+ seed: measuredSeed(key)
1006
+ });
1007
+ const baselineMean = measuredMean(baseline);
1008
+ const candidateMean = measuredMean(candidate);
1009
+ return {
1010
+ ...identity,
1011
+ availability: "measured",
1012
+ baseline: baselineMean,
1013
+ candidate: candidateMean,
1014
+ delta: candidateMean - baselineMean,
1015
+ confidenceInterval: {
1016
+ level: interval.confidence,
1017
+ lower: interval.low,
1018
+ upper: interval.high,
1019
+ method: "paired-bootstrap",
1020
+ statistic: "mean",
1021
+ resamples: interval.resamples
1022
+ },
1023
+ n: interval.n
1024
+ };
1025
+ }
1026
+ function measuredQuality(cell, column) {
1027
+ const objective = column.kind === "objective" ? column.name : column.objective;
1028
+ const score = cell.judgeScores[objective];
1029
+ if (!score || score.failed) {
1030
+ throw new Error(
1031
+ `heldout cell ${measuredCellKey(cell)} is missing measured objective '${objective}'`
1032
+ );
1033
+ }
1034
+ const value = column.kind === "objective" ? score.composite : score.dimensions[column.name];
1035
+ if (value === void 0) {
1036
+ throw new Error(
1037
+ `heldout cell ${measuredCellKey(cell)} is missing '${objective}' dimension '${column.name}'`
1038
+ );
1039
+ }
1040
+ return finiteMeasuredValue(value, measuredObjectiveKey(column));
1041
+ }
1042
+ function measuredObjectiveKey(identity) {
1043
+ return identity.kind === "dimension" ? `${identity.kind}:${identity.objective}:${identity.name}` : `${identity.kind}:${identity.name}`;
1044
+ }
1045
+ function measuredCellKey(cell) {
1046
+ return `${cell.scenarioId}:${cell.rep}`;
1047
+ }
1048
+ function finiteMeasuredValue(value, name) {
1049
+ if (!Number.isFinite(value)) throw new Error(`measured objective '${name}' is not finite`);
1050
+ return value;
1051
+ }
1052
+ function nonnegativeMeasuredValue(value, name) {
1053
+ if (!Number.isFinite(value) || value < 0) throw new Error(`${name} must be non-negative`);
1054
+ return value;
1055
+ }
1056
+ function nonnegativeMeasuredInteger(value, name) {
1057
+ if (!Number.isSafeInteger(value) || value < 0) {
1058
+ throw new Error(`${name} must be a non-negative safe integer`);
1059
+ }
1060
+ return value;
1061
+ }
1062
+ function measuredMean(values) {
1063
+ if (values.length === 0) throw new Error("measured objective has no paired values");
1064
+ return values.reduce((sum, value) => sum + value, 0) / values.length;
1065
+ }
1066
+ function measuredSeed(value) {
1067
+ let seed = 2166136261;
1068
+ for (const byte of Buffer.from(value, "utf8")) {
1069
+ seed = Math.imul(seed ^ byte, 16777619) >>> 0;
1070
+ }
1071
+ return seed;
1072
+ }
1073
+ function assertMeasuredNumber(actual, expected, name) {
1074
+ const tolerance = Number.EPSILON * Math.max(1, Math.abs(actual), Math.abs(expected)) * 8;
1075
+ if (!Number.isFinite(actual) || !Number.isFinite(expected) || Math.abs(actual - expected) > tolerance) {
1076
+ throw new Error(`${name} does not agree across the measured comparison`);
1077
+ }
1078
+ }
1079
+ function assertMeasuredIdentity(actual, expected, name) {
1080
+ if (actual !== expected) throw new Error(`${name} does not agree across the measured comparison`);
1081
+ }
1082
+ function assertMeasuredOptional(actual, expected, name) {
1083
+ if (actual !== expected) throw new Error(`${name} does not agree across the measured comparison`);
1084
+ }
1085
+ function indexReceiptIdsByCell(receipts) {
1086
+ const idsByCell = /* @__PURE__ */ new Map();
1087
+ for (const receipt of receipts) {
1088
+ const tags = receipt.tags;
1089
+ if (!tags?.runDir || !tags.cellId || !tags.scenarioId || tags.rep === void 0) continue;
1090
+ const key = receiptCellKey(tags.runDir, tags.cellId, tags.scenarioId, tags.rep);
1091
+ const ids = idsByCell.get(key) ?? [];
1092
+ ids.push(receipt.callId);
1093
+ idsByCell.set(key, ids);
1094
+ }
1095
+ return idsByCell;
1096
+ }
1097
+ function receiptCellKey(runDir, cellId, scenarioId, rep) {
1098
+ return JSON.stringify([runDir, cellId, scenarioId, String(rep)]);
1099
+ }
1100
+
539
1101
  // src/contract/eval-reporting-suite.ts
540
1102
  import { mkdir, writeFile } from "fs/promises";
541
1103
  import { dirname, join as join2 } from "path";
@@ -1051,6 +1613,7 @@ export {
1051
1613
  analyzeRuns,
1052
1614
  buildDefaultAnalystRegistry,
1053
1615
  buildEvidenceVector,
1616
+ campaignSplitDigest,
1054
1617
  composeGate,
1055
1618
  createChatClient,
1056
1619
  createReferenceEquivalenceJudge,
@@ -1074,6 +1637,7 @@ export {
1074
1637
  gepaProposer,
1075
1638
  heldOutGate,
1076
1639
  inMemoryCampaignStorage,
1640
+ measuredComparisonFromSelfImproveResult,
1077
1641
  paretoPolicy,
1078
1642
  paretoSignificanceGate,
1079
1643
  parseAgentTrace,