@tangle-network/agent-eval 0.120.2 → 0.120.3

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 (171) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/analyst/index.d.ts +3111 -0
  3. package/dist/analyst/index.js +403 -0
  4. package/dist/analyst/index.js.map +1 -0
  5. package/dist/authenticity/index.d.ts +161 -0
  6. package/dist/authenticity/index.js +215 -0
  7. package/dist/authenticity/index.js.map +1 -0
  8. package/dist/belief-state/index.d.ts +1301 -0
  9. package/dist/belief-state/index.js +2152 -0
  10. package/dist/belief-state/index.js.map +1 -0
  11. package/dist/benchmarks/index.d.ts +974 -0
  12. package/dist/benchmarks/index.js +60 -0
  13. package/dist/benchmarks/index.js.map +1 -0
  14. package/dist/builder-eval/index.d.ts +695 -0
  15. package/dist/builder-eval/index.js +366 -0
  16. package/dist/builder-eval/index.js.map +1 -0
  17. package/dist/campaign/index.d.ts +7454 -0
  18. package/dist/campaign/index.js +272 -0
  19. package/dist/campaign/index.js.map +1 -0
  20. package/dist/chunk-3CDFMEMO.js +3878 -0
  21. package/dist/chunk-3CDFMEMO.js.map +1 -0
  22. package/dist/chunk-3RF76KTD.js +84 -0
  23. package/dist/chunk-3RF76KTD.js.map +1 -0
  24. package/dist/chunk-3XH4Y2SS.js +750 -0
  25. package/dist/chunk-3XH4Y2SS.js.map +1 -0
  26. package/dist/chunk-3YYRZDON.js +45 -0
  27. package/dist/chunk-3YYRZDON.js.map +1 -0
  28. package/dist/chunk-5BYTIDZ7.js +550 -0
  29. package/dist/chunk-5BYTIDZ7.js.map +1 -0
  30. package/dist/chunk-5CVUPHJ4.js +2668 -0
  31. package/dist/chunk-5CVUPHJ4.js.map +1 -0
  32. package/dist/chunk-ARU2PZFM.js +312 -0
  33. package/dist/chunk-ARU2PZFM.js.map +1 -0
  34. package/dist/chunk-BOD4O7OF.js +40 -0
  35. package/dist/chunk-BOD4O7OF.js.map +1 -0
  36. package/dist/chunk-CVJP5TMD.js +766 -0
  37. package/dist/chunk-CVJP5TMD.js.map +1 -0
  38. package/dist/chunk-DPZAEKA6.js +880 -0
  39. package/dist/chunk-DPZAEKA6.js.map +1 -0
  40. package/dist/chunk-DTJ6QUQB.js +131 -0
  41. package/dist/chunk-DTJ6QUQB.js.map +1 -0
  42. package/dist/chunk-GGE4NNQT.js +65 -0
  43. package/dist/chunk-GGE4NNQT.js.map +1 -0
  44. package/dist/chunk-H5UD2323.js +286 -0
  45. package/dist/chunk-H5UD2323.js.map +1 -0
  46. package/dist/chunk-HHWE3POT.js +94 -0
  47. package/dist/chunk-HHWE3POT.js.map +1 -0
  48. package/dist/chunk-HKUCJ437.js +787 -0
  49. package/dist/chunk-HKUCJ437.js.map +1 -0
  50. package/dist/chunk-JHCHEVET.js +274 -0
  51. package/dist/chunk-JHCHEVET.js.map +1 -0
  52. package/dist/chunk-K4DBDHLK.js +158 -0
  53. package/dist/chunk-K4DBDHLK.js.map +1 -0
  54. package/dist/chunk-K6N6XJJX.js +306 -0
  55. package/dist/chunk-K6N6XJJX.js.map +1 -0
  56. package/dist/chunk-MA6HLL3S.js +65 -0
  57. package/dist/chunk-MA6HLL3S.js.map +1 -0
  58. package/dist/chunk-MAZ26DC7.js +99 -0
  59. package/dist/chunk-MAZ26DC7.js.map +1 -0
  60. package/dist/chunk-MOXWMGPC.js +577 -0
  61. package/dist/chunk-MOXWMGPC.js.map +1 -0
  62. package/dist/chunk-NJC7U437.js +626 -0
  63. package/dist/chunk-NJC7U437.js.map +1 -0
  64. package/dist/chunk-NMN4WGSJ.js +1030 -0
  65. package/dist/chunk-NMN4WGSJ.js.map +1 -0
  66. package/dist/chunk-NPCTHQIO.js +91 -0
  67. package/dist/chunk-NPCTHQIO.js.map +1 -0
  68. package/dist/chunk-ONWEPEDO.js +57 -0
  69. package/dist/chunk-ONWEPEDO.js.map +1 -0
  70. package/dist/chunk-OYZAPX5G.js +1526 -0
  71. package/dist/chunk-OYZAPX5G.js.map +1 -0
  72. package/dist/chunk-P5MGQ2FY.js +7958 -0
  73. package/dist/chunk-P5MGQ2FY.js.map +1 -0
  74. package/dist/chunk-PC4UYEBM.js +166 -0
  75. package/dist/chunk-PC4UYEBM.js.map +1 -0
  76. package/dist/chunk-PJQFMIOX.js +1182 -0
  77. package/dist/chunk-PJQFMIOX.js.map +1 -0
  78. package/dist/chunk-PXD6ZFNY.js +1107 -0
  79. package/dist/chunk-PXD6ZFNY.js.map +1 -0
  80. package/dist/chunk-PXE2VKMX.js +140 -0
  81. package/dist/chunk-PXE2VKMX.js.map +1 -0
  82. package/dist/chunk-PZ5AY32C.js +10 -0
  83. package/dist/chunk-PZ5AY32C.js.map +1 -0
  84. package/dist/chunk-QBRSJK47.js +622 -0
  85. package/dist/chunk-QBRSJK47.js.map +1 -0
  86. package/dist/chunk-S3UZOQ5Y.js +328 -0
  87. package/dist/chunk-S3UZOQ5Y.js.map +1 -0
  88. package/dist/chunk-SQQED7ZH.js +998 -0
  89. package/dist/chunk-SQQED7ZH.js.map +1 -0
  90. package/dist/chunk-SYV364BL.js +1266 -0
  91. package/dist/chunk-SYV364BL.js.map +1 -0
  92. package/dist/chunk-T4SQEITX.js +95 -0
  93. package/dist/chunk-T4SQEITX.js.map +1 -0
  94. package/dist/chunk-TT4KNT67.js +124 -0
  95. package/dist/chunk-TT4KNT67.js.map +1 -0
  96. package/dist/chunk-U5CHZ5M3.js +357 -0
  97. package/dist/chunk-U5CHZ5M3.js.map +1 -0
  98. package/dist/chunk-ULOKLHIQ.js +1937 -0
  99. package/dist/chunk-ULOKLHIQ.js.map +1 -0
  100. package/dist/chunk-VI2UW6B6.js +162 -0
  101. package/dist/chunk-VI2UW6B6.js.map +1 -0
  102. package/dist/chunk-VQMK5FMP.js +247 -0
  103. package/dist/chunk-VQMK5FMP.js.map +1 -0
  104. package/dist/chunk-VSMTAMNK.js +53 -0
  105. package/dist/chunk-VSMTAMNK.js.map +1 -0
  106. package/dist/chunk-VZSRQ272.js +149 -0
  107. package/dist/chunk-VZSRQ272.js.map +1 -0
  108. package/dist/chunk-WW2A73HW.js +159 -0
  109. package/dist/chunk-WW2A73HW.js.map +1 -0
  110. package/dist/chunk-X4UCIOTZ.js +136 -0
  111. package/dist/chunk-X4UCIOTZ.js.map +1 -0
  112. package/dist/chunk-XJYR7XFV.js +317 -0
  113. package/dist/chunk-XJYR7XFV.js.map +1 -0
  114. package/dist/chunk-ZET2UAYW.js +89 -0
  115. package/dist/chunk-ZET2UAYW.js.map +1 -0
  116. package/dist/chunk-ZMXDQ4K7.js +870 -0
  117. package/dist/chunk-ZMXDQ4K7.js.map +1 -0
  118. package/dist/chunk-ZZUXHH3R.js +99 -0
  119. package/dist/chunk-ZZUXHH3R.js.map +1 -0
  120. package/dist/cli.d.ts +1 -0
  121. package/dist/cli.js +112 -0
  122. package/dist/cli.js.map +1 -0
  123. package/dist/contract/index.d.ts +4972 -0
  124. package/dist/contract/index.js +1654 -0
  125. package/dist/contract/index.js.map +1 -0
  126. package/dist/control.d.ts +1013 -0
  127. package/dist/control.js +34 -0
  128. package/dist/control.js.map +1 -0
  129. package/dist/fuzz.d.ts +759 -0
  130. package/dist/fuzz.js +714 -0
  131. package/dist/fuzz.js.map +1 -0
  132. package/dist/hosted/index.d.ts +730 -0
  133. package/dist/hosted/index.js +14 -0
  134. package/dist/hosted/index.js.map +1 -0
  135. package/dist/index.d.ts +16780 -0
  136. package/dist/index.js +12168 -0
  137. package/dist/index.js.map +1 -0
  138. package/dist/matrix/index.d.ts +155 -0
  139. package/dist/matrix/index.js +8 -0
  140. package/dist/matrix/index.js.map +1 -0
  141. package/dist/meta-eval/index.d.ts +1030 -0
  142. package/dist/meta-eval/index.js +417 -0
  143. package/dist/meta-eval/index.js.map +1 -0
  144. package/dist/multishot/index.d.ts +579 -0
  145. package/dist/multishot/index.js +589 -0
  146. package/dist/multishot/index.js.map +1 -0
  147. package/dist/openapi.json +992 -0
  148. package/dist/pipelines/index.d.ts +567 -0
  149. package/dist/pipelines/index.js +515 -0
  150. package/dist/pipelines/index.js.map +1 -0
  151. package/dist/reporting.d.ts +1277 -0
  152. package/dist/reporting.js +48 -0
  153. package/dist/reporting.js.map +1 -0
  154. package/dist/rl.d.ts +4092 -0
  155. package/dist/rl.js +1724 -0
  156. package/dist/rl.js.map +1 -0
  157. package/dist/run-campaign-75RTPVV5.js +14 -0
  158. package/dist/run-campaign-75RTPVV5.js.map +1 -0
  159. package/dist/storyboard/index.d.ts +279 -0
  160. package/dist/storyboard/index.js +767 -0
  161. package/dist/storyboard/index.js.map +1 -0
  162. package/dist/trace-attributes.d.ts +52 -0
  163. package/dist/trace-attributes.js +62 -0
  164. package/dist/trace-attributes.js.map +1 -0
  165. package/dist/traces.d.ts +2343 -0
  166. package/dist/traces.js +249 -0
  167. package/dist/traces.js.map +1 -0
  168. package/dist/wire/index.d.ts +1252 -0
  169. package/dist/wire/index.js +81 -0
  170. package/dist/wire/index.js.map +1 -0
  171. package/package.json +1 -1
@@ -0,0 +1,1654 @@
1
+ import {
2
+ fromClaudeCodeSession,
3
+ fromCodexSession,
4
+ fromKimiCodeSession,
5
+ fromOpenCodeSession,
6
+ fromPiSession,
7
+ fromPigraphSession,
8
+ parseCodeAgentJsonl
9
+ } from "../chunk-HKUCJ437.js";
10
+ import {
11
+ createHostedClient
12
+ } from "../chunk-ZZUXHH3R.js";
13
+ import {
14
+ analyzeRuns,
15
+ summarizeExecution
16
+ } from "../chunk-SQQED7ZH.js";
17
+ import {
18
+ REFERENCE_EQUIVALENCE_INPUT_LIMITS,
19
+ REFERENCE_EQUIVALENCE_JUDGE_VERSION,
20
+ buildEvidenceVector,
21
+ buildLoopProvenanceRecord,
22
+ campaignBreakdown,
23
+ campaignMeanComposite,
24
+ canonicalDigest,
25
+ composeGate,
26
+ createReferenceEquivalenceJudge,
27
+ defaultProductionGate,
28
+ emitLoopProvenance,
29
+ evolutionaryProposer,
30
+ gepaProposer,
31
+ heldOutGate,
32
+ loopProvenanceArgsFromResult,
33
+ pairArms,
34
+ paretoPolicy,
35
+ paretoSignificanceGate,
36
+ powerPreflight,
37
+ renderSurfaceDiff,
38
+ runEval,
39
+ runImprovementLoop,
40
+ runReferenceEquivalenceJudge,
41
+ surfaceContentHash,
42
+ surfaceHash,
43
+ verifyLoopProvenanceRecord
44
+ } from "../chunk-3CDFMEMO.js";
45
+ import {
46
+ assertCampaignSplitIdentity,
47
+ assertRealAgentReceipts,
48
+ campaignCoverage,
49
+ campaignSplitDigest,
50
+ createRunCostLedger,
51
+ fsCampaignStorage,
52
+ inMemoryCampaignStorage,
53
+ resolveRunDir,
54
+ runCampaign
55
+ } from "../chunk-SYV364BL.js";
56
+ import {
57
+ buildDefaultAnalystRegistry,
58
+ createChatClient
59
+ } from "../chunk-5BYTIDZ7.js";
60
+ import "../chunk-HHWE3POT.js";
61
+ import "../chunk-5CVUPHJ4.js";
62
+ import {
63
+ FileSystemOutcomeStore,
64
+ InMemoryOutcomeStore
65
+ } from "../chunk-3RF76KTD.js";
66
+ import "../chunk-ARU2PZFM.js";
67
+ import "../chunk-NJC7U437.js";
68
+ import "../chunk-DPZAEKA6.js";
69
+ import {
70
+ pairedBootstrap
71
+ } from "../chunk-PJQFMIOX.js";
72
+ import {
73
+ CostLedger
74
+ } from "../chunk-ZMXDQ4K7.js";
75
+ import "../chunk-VI2UW6B6.js";
76
+ import {
77
+ recordAggregateMeasurements,
78
+ summarizeExecutionMeasurements
79
+ } from "../chunk-H5UD2323.js";
80
+ import "../chunk-PXE2VKMX.js";
81
+ import "../chunk-ZET2UAYW.js";
82
+ import "../chunk-GGE4NNQT.js";
83
+ import "../chunk-PXD6ZFNY.js";
84
+ import "../chunk-PC4UYEBM.js";
85
+ import {
86
+ parseRunRecordSafe
87
+ } from "../chunk-S3UZOQ5Y.js";
88
+ import "../chunk-MA6HLL3S.js";
89
+ import "../chunk-XJYR7XFV.js";
90
+ import "../chunk-VSMTAMNK.js";
91
+ import "../chunk-ONWEPEDO.js";
92
+ import {
93
+ LLM_MODEL_ATTR_KEYS
94
+ } from "../chunk-K4DBDHLK.js";
95
+ import "../chunk-PZ5AY32C.js";
96
+
97
+ // src/contract/self-improve.ts
98
+ import { createHash } from "crypto";
99
+ var SelfImproveRunError = class extends Error {
100
+ cost;
101
+ receipts;
102
+ constructor(cause, ledger) {
103
+ const original = cause instanceof Error ? cause : new Error(String(cause));
104
+ super(original.message, { cause: original });
105
+ this.name = "SelfImproveRunError";
106
+ this.cost = ledger.summary();
107
+ this.receipts = ledger.list();
108
+ }
109
+ };
110
+ function splitTrainHoldout(scenarios, fraction) {
111
+ function hash(s) {
112
+ let h = 2166136261 >>> 0;
113
+ for (let i = 0; i < s.length; i++) {
114
+ h ^= s.charCodeAt(i);
115
+ h = Math.imul(h, 16777619) >>> 0;
116
+ }
117
+ return h;
118
+ }
119
+ const sorted = [...scenarios].sort((a, b) => hash(a.id) - hash(b.id));
120
+ const nHoldout = Math.max(1, Math.min(sorted.length - 1, Math.round(sorted.length * fraction)));
121
+ return {
122
+ holdout: sorted.slice(0, nHoldout),
123
+ train: sorted.slice(nHoldout)
124
+ };
125
+ }
126
+ function meanComposite(byScenario) {
127
+ const perScenario = {};
128
+ const values = [];
129
+ for (const [id, agg] of Object.entries(byScenario)) {
130
+ perScenario[id] = agg.meanComposite;
131
+ values.push(agg.meanComposite);
132
+ }
133
+ return {
134
+ compositeMean: values.length === 0 ? 0 : values.reduce((s, v) => s + v, 0) / values.length,
135
+ perScenario
136
+ };
137
+ }
138
+ async function selfImprove(opts) {
139
+ const startedAt = Date.now();
140
+ const requestedRunDir = opts.runDir ?? `mem://selfImprove-${startedAt}`;
141
+ const runDir = resolveRunDir(requestedRunDir);
142
+ const storage = opts.storage ?? (runDir.startsWith("mem://") ? inMemoryCampaignStorage() : fsCampaignStorage());
143
+ const costLedger = createRunCostLedger({
144
+ storage,
145
+ runDir,
146
+ costCeilingUsd: opts.budget?.dollars
147
+ });
148
+ try {
149
+ return await runSelfImprove(opts, costLedger, startedAt, runDir, storage);
150
+ } catch (error) {
151
+ throw new SelfImproveRunError(error, costLedger);
152
+ }
153
+ }
154
+ async function runSelfImprove(opts, costLedger, startedAt, runDir, storage) {
155
+ const budget = opts.budget ?? {};
156
+ const generations = budget.generations ?? 3;
157
+ const populationSize = budget.populationSize ?? 2;
158
+ const maxConcurrency = budget.maxConcurrency ?? 2;
159
+ const holdoutFraction = budget.holdoutFraction ?? 0.25;
160
+ const expectUsage = opts.expectUsage ?? "assert";
161
+ const explicitHoldout = budget.holdoutScenarios;
162
+ const { train, holdout } = explicitHoldout ? {
163
+ train: opts.scenarios.filter((s) => !explicitHoldout.some((h) => h.id === s.id)),
164
+ holdout: explicitHoldout
165
+ } : splitTrainHoldout(opts.scenarios, holdoutFraction);
166
+ if (train.length === 0) {
167
+ throw new Error(
168
+ "selfImprove: train split is empty. Reduce holdoutFraction or pass more scenarios."
169
+ );
170
+ }
171
+ if (holdout.length === 0) {
172
+ throw new Error("selfImprove: holdout split is empty. Pass more scenarios.");
173
+ }
174
+ const proposer = opts.proposer ?? gepaProposer({
175
+ llm: {
176
+ baseUrl: opts.llm?.baseUrl ?? "https://router.tangle.tools/v1",
177
+ apiKey: opts.llm?.apiKey ?? process.env.OPENAI_API_KEY ?? ""
178
+ },
179
+ model: opts.llm?.model ?? "anthropic/claude-sonnet-4.6",
180
+ target: opts.proposerTarget ?? "agent surface (system prompt or config) being optimized by selfImprove",
181
+ // Pass-through: when unset, gepaProposer falls back to its own
182
+ // domain-neutral engineering primitives.
183
+ mutationPrimitives: opts.mutationPrimitives
184
+ });
185
+ const gate = opts.gate ?? defaultProductionGate({
186
+ holdoutScenarios: holdout,
187
+ deltaThreshold: 0.05
188
+ });
189
+ if (opts.onProgress) {
190
+ opts.onProgress({ kind: "baseline.started", scenarios: opts.scenarios.length });
191
+ }
192
+ const result = await runImprovementLoop({
193
+ scenarios: train,
194
+ baselineSurface: opts.baselineSurface,
195
+ dispatchWithSurface: opts.agent,
196
+ proposer,
197
+ judges: [opts.judge],
198
+ populationSize,
199
+ maxGenerations: generations,
200
+ promoteTopK: budget.promoteTopK,
201
+ reps: budget.reps,
202
+ holdoutScenarios: holdout,
203
+ gate,
204
+ neutralize: opts.neutralize,
205
+ autoOnPromote: opts.autoOnPromote ?? "none",
206
+ ghOwner: opts.ghOwner,
207
+ ghRepo: opts.ghRepo,
208
+ storage,
209
+ runDir,
210
+ maxConcurrency,
211
+ cellPlacement: opts.cellPlacement,
212
+ dispatchTimeoutMs: opts.dispatchTimeoutMs,
213
+ costLedger,
214
+ expectUsage,
215
+ labeledStore: opts.labeledStore,
216
+ captureSource: opts.captureSource,
217
+ analyzeGeneration: opts.analyzeGeneration,
218
+ findings: opts.findings
219
+ });
220
+ const baseline = meanComposite(result.baselineOnHoldout.aggregates.byScenario);
221
+ const winnerStats = meanComposite(result.winnerOnHoldout.aggregates.byScenario);
222
+ let power;
223
+ const baselineHoldoutComposites = result.baselineOnHoldout.cells.filter((cell) => !cell.error).map((cell) => {
224
+ const scores = Object.values(cell.judgeScores);
225
+ return scores.length === 0 ? Number.NaN : scores.reduce((sum, s) => sum + s.composite, 0) / scores.length;
226
+ }).filter((v) => Number.isFinite(v));
227
+ if (baselineHoldoutComposites.length >= 3) {
228
+ power = powerPreflight({
229
+ baselineComposites: baselineHoldoutComposites,
230
+ sharedScorerChannel: true
231
+ });
232
+ if (opts.onProgress) {
233
+ opts.onProgress({
234
+ kind: "power.estimated",
235
+ n: power.n,
236
+ sd: power.sd,
237
+ mde: power.mde,
238
+ underpowered: power.underpowered
239
+ });
240
+ }
241
+ if (power.underpowered && generations > 0) {
242
+ console.warn(`[selfImprove] ${power.recommendation}`);
243
+ }
244
+ }
245
+ if (opts.onProgress) {
246
+ opts.onProgress({
247
+ kind: "baseline.completed",
248
+ compositeMean: baseline.compositeMean,
249
+ durationMs: Date.now() - startedAt
250
+ });
251
+ opts.onProgress({
252
+ kind: "gate.decided",
253
+ decision: result.gateResult.decision,
254
+ lift: winnerStats.compositeMean - baseline.compositeMean
255
+ });
256
+ }
257
+ const cost = result.cost;
258
+ const totalCost = cost.totalCostUsd;
259
+ const insight = await analyzeRuns({
260
+ runs: [
261
+ ...cellsToRunRecords(result.baselineCampaign.cells, "baseline", runDir, opts.baselineSurface),
262
+ ...cellsToRunRecords(result.winnerOnHoldout.cells, "winner", runDir, result.winnerSurface)
263
+ ],
264
+ baselineCandidateId: "baseline",
265
+ candidateCandidateId: "winner"
266
+ });
267
+ const durationMs = Date.now() - startedAt;
268
+ const { record: provenance } = await emitLoopProvenance({
269
+ ...loopProvenanceArgsFromResult({
270
+ runId: `${runDir}#${startedAt}`,
271
+ runDir,
272
+ timestamp: new Date(startedAt).toISOString(),
273
+ baselineSurface: opts.baselineSurface,
274
+ result,
275
+ costReceipts: costLedger.list(),
276
+ totalCostUsd: totalCost,
277
+ totalDurationMs: durationMs
278
+ }),
279
+ storage,
280
+ hostedClient: opts.hostedTenant ? createHostedClient(opts.hostedTenant) : void 0
281
+ });
282
+ if (opts.onProvenance) opts.onProvenance(provenance);
283
+ const summary = {
284
+ baseline,
285
+ winner: {
286
+ ...winnerStats,
287
+ surface: result.winnerSurface,
288
+ ...result.winnerLabel ? { label: result.winnerLabel } : {},
289
+ ...result.winnerRationale ? { rationale: result.winnerRationale } : {}
290
+ },
291
+ lift: winnerStats.compositeMean - baseline.compositeMean,
292
+ diff: result.promotedDiff,
293
+ provenance,
294
+ gateDecision: result.gateResult.decision,
295
+ generationsExplored: result.generations.length,
296
+ durationMs,
297
+ totalCostUsd: totalCost,
298
+ cost,
299
+ receipts: costLedger.list(),
300
+ insight,
301
+ ...power ? { power } : {},
302
+ raw: result
303
+ };
304
+ if (opts.hostedTenant) {
305
+ try {
306
+ await shipEvalRunToHosted(opts.hostedTenant, opts, summary, result, runDir);
307
+ } catch (err) {
308
+ const msg = err instanceof Error ? err.message : String(err);
309
+ console.warn(`[agent-eval] hosted ingest failed (continuing): ${msg}`);
310
+ }
311
+ }
312
+ return summary;
313
+ }
314
+ async function shipEvalRunToHosted(tenant, opts, summary, raw, runDir) {
315
+ const client = createHostedClient(tenant);
316
+ function snapshotFromCampaign(index, surface, campaign, durationMs) {
317
+ const cells = campaign.cells.map((cell) => {
318
+ const judgeScores = Object.values(cell.judgeScores);
319
+ const composite = judgeScores.length === 0 ? 0 : judgeScores.reduce((s, j) => s + j.composite, 0) / judgeScores.length;
320
+ return {
321
+ scenarioId: cell.scenarioId,
322
+ rep: cell.rep,
323
+ compositeMean: composite,
324
+ dimensions: Object.fromEntries(
325
+ Object.entries(cell.judgeScores).map(([name, score]) => [name, score.dimensions])
326
+ ),
327
+ errorMessage: cell.error ?? void 0
328
+ };
329
+ });
330
+ const compositeMean = cells.length === 0 ? 0 : cells.reduce((s, c) => s + c.compositeMean, 0) / cells.length;
331
+ return {
332
+ index,
333
+ surfaceHash: surfaceHash(surface),
334
+ surface,
335
+ cells,
336
+ compositeMean,
337
+ costUsd: campaign.aggregates.totalCostUsd,
338
+ durationMs
339
+ };
340
+ }
341
+ const generations = [];
342
+ generations.push(snapshotFromCampaign(0, opts.baselineSurface, raw.baselineCampaign, 0));
343
+ for (const gen of raw.generations) {
344
+ const winner = gen.surfaces.reduce(
345
+ (best, s) => s.campaign.aggregates.cellsExecuted > 0 && (best === void 0 || averageComposite(s.campaign) > averageComposite(best.campaign)) ? s : best,
346
+ gen.surfaces[0]
347
+ );
348
+ if (!winner) continue;
349
+ generations.push(
350
+ snapshotFromCampaign(gen.record.generationIndex + 1, winner.surface, winner.campaign, 0)
351
+ );
352
+ }
353
+ const event = {
354
+ runId: `${runDir}#${Date.now()}`,
355
+ runDir,
356
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
357
+ status: "finished",
358
+ labels: opts.hostedLabels ?? {},
359
+ baseline: generations[0],
360
+ generations,
361
+ gateDecision: summary.gateDecision,
362
+ holdoutLift: summary.lift,
363
+ totalCostUsd: summary.totalCostUsd,
364
+ totalDurationMs: summary.durationMs,
365
+ insightReport: summary.insight
366
+ };
367
+ await client.ingestEvalRun(event);
368
+ }
369
+ function averageComposite(campaign) {
370
+ const aggs = Object.values(campaign.aggregates.byScenario);
371
+ return aggs.length === 0 ? 0 : aggs.reduce((s, a) => s + a.meanComposite, 0) / aggs.length;
372
+ }
373
+ function hashString(s) {
374
+ let h = 2166136261 >>> 0;
375
+ for (let i = 0; i < s.length; i++) {
376
+ h ^= s.charCodeAt(i);
377
+ h = Math.imul(h, 16777619) >>> 0;
378
+ }
379
+ return h.toString(16).padStart(8, "0");
380
+ }
381
+ function cellsToRunRecords(cells, candidateId, runId, surface) {
382
+ const promptHash = surfaceContentHash(surface);
383
+ const configHash = `sha256:${createHash("sha256").update(candidateId).digest("hex")}`;
384
+ return cells.map((cell) => {
385
+ const perJudge = {};
386
+ const perDimMeanAccum = {};
387
+ let compositeSum = 0;
388
+ let compositeCount = 0;
389
+ for (const [judgeId, score] of Object.entries(cell.judgeScores)) {
390
+ perJudge[judgeId] = { ...score.dimensions };
391
+ for (const [dim, value] of Object.entries(score.dimensions)) {
392
+ if (!Number.isFinite(value)) continue;
393
+ const accum = perDimMeanAccum[dim] ?? { sum: 0, n: 0 };
394
+ accum.sum += value;
395
+ accum.n += 1;
396
+ perDimMeanAccum[dim] = accum;
397
+ }
398
+ if (Number.isFinite(score.composite)) {
399
+ compositeSum += score.composite;
400
+ compositeCount += 1;
401
+ }
402
+ }
403
+ const perDimMean = {};
404
+ for (const [dim, { sum, n }] of Object.entries(perDimMeanAccum)) {
405
+ perDimMean[dim] = n === 0 ? 0 : sum / n;
406
+ }
407
+ const composite = compositeCount === 0 ? 0 : compositeSum / compositeCount;
408
+ const judgeScores = {
409
+ perJudge,
410
+ perDimMean,
411
+ composite
412
+ };
413
+ return {
414
+ runId: `${runId}::${candidateId}::${cell.cellId}`,
415
+ experimentId: runId,
416
+ candidateId,
417
+ // Pair on (scenarioId, rep) — analyzeRuns pairs on (experimentId, seed).
418
+ // Synthesize a stable seed for that pairing.
419
+ seed: cell.rep * 1e6 + hashString(cell.scenarioId).slice(0, 6).split("").reduce((a, c) => a * 31 + c.charCodeAt(0) >>> 0, 0),
420
+ model: "campaign-cell",
421
+ promptHash,
422
+ configHash,
423
+ commitSha: "cell",
424
+ wallMs: cell.durationMs,
425
+ costUsd: cell.costUsd,
426
+ tokenUsage: {
427
+ input: cell.tokenUsage.input,
428
+ output: cell.tokenUsage.output,
429
+ ...cell.tokenUsage.reasoning === void 0 ? {} : { reasoning: cell.tokenUsage.reasoning },
430
+ ...cell.tokenUsage.cached === void 0 ? {} : { cached: cell.tokenUsage.cached },
431
+ ...cell.tokenUsage.cacheWrite === void 0 ? {} : { cacheWrite: cell.tokenUsage.cacheWrite }
432
+ },
433
+ outcome: {
434
+ holdoutScore: composite,
435
+ raw: {},
436
+ judgeScores
437
+ },
438
+ splitTag: "holdout",
439
+ ...cell.error ? { failureMode: cell.error } : {}
440
+ };
441
+ });
442
+ }
443
+
444
+ // src/contract/define-agent-eval.ts
445
+ function defineAgentEval(defaults) {
446
+ const defaultEvaluateOptions = evaluateDefaults(defaults);
447
+ return {
448
+ scenarios: defaults.scenarios,
449
+ baselineSurface: defaults.baselineSurface,
450
+ async evaluate(opts = {}) {
451
+ const { agent, judge, judges, runDir, scenarios, surface, ...campaignOpts } = opts;
452
+ const selectedAgent = agent ?? defaults.agent;
453
+ const selectedSurface = surface ?? defaults.baselineSurface;
454
+ const selectedRunDir = runDir ?? defaults.runDir ?? `mem://defineAgentEval-${Date.now()}`;
455
+ const selectedStorage = campaignOpts.storage ?? defaultEvaluateOptions.storage ?? (selectedRunDir.startsWith("mem://") ? inMemoryCampaignStorage() : void 0);
456
+ const evalOptions = {
457
+ ...defaultEvaluateOptions,
458
+ ...campaignOpts,
459
+ ...selectedStorage ? { storage: selectedStorage } : {},
460
+ runDir: selectedRunDir,
461
+ scenarios: scenarios ?? defaults.scenarios,
462
+ dispatch: (scenario, ctx) => selectedAgent(selectedSurface, scenario, ctx),
463
+ judges: evaluateJudges(judges, judge ?? defaults.judge)
464
+ };
465
+ if (evalOptions.reps !== void 0)
466
+ evalOptions.reps = requirePositiveInteger(evalOptions.reps, "reps");
467
+ return runEval(evalOptions);
468
+ },
469
+ async improve(opts = {}) {
470
+ const {
471
+ budget: budgetOverride,
472
+ hostedTenant: hostedTenantOverride,
473
+ llm: llmOverride,
474
+ ...topLevelOverrides
475
+ } = opts;
476
+ const merged = mergeDefined(defaults, topLevelOverrides);
477
+ const budget = mergeBudget(defaults.budget, budgetOverride);
478
+ const llm = mergeLlm(defaults.llm, llmOverride);
479
+ const hostedTenant = mergeHostedTenant(defaults.hostedTenant, hostedTenantOverride);
480
+ return selfImprove({
481
+ ...merged,
482
+ ...budget ? { budget } : {},
483
+ ...llm ? { llm } : {},
484
+ ...hostedTenant ? { hostedTenant } : {}
485
+ });
486
+ }
487
+ };
488
+ }
489
+ function evaluateDefaults(defaults) {
490
+ const out = {};
491
+ if (defaults.storage) out.storage = defaults.storage;
492
+ if (defaults.labeledStore) out.labeledStore = defaults.labeledStore;
493
+ if (defaults.captureSource) out.captureSource = defaults.captureSource;
494
+ if (defaults.cellPlacement) out.cellPlacement = defaults.cellPlacement;
495
+ if (defaults.expectUsage) out.expectUsage = defaults.expectUsage;
496
+ if (defaults.budget?.dollars !== void 0) out.costCeiling = defaults.budget.dollars;
497
+ if (defaults.budget?.maxConcurrency !== void 0)
498
+ out.maxConcurrency = defaults.budget.maxConcurrency;
499
+ if (defaults.budget?.reps !== void 0)
500
+ out.reps = requirePositiveInteger(defaults.budget.reps, "budget.reps");
501
+ return out;
502
+ }
503
+ function mergeBudget(defaults, overrides) {
504
+ const merged = mergeOptionalObject(defaults, overrides);
505
+ if (merged?.reps !== void 0) merged.reps = requirePositiveInteger(merged.reps, "budget.reps");
506
+ return merged;
507
+ }
508
+ function mergeLlm(defaults, overrides) {
509
+ return mergeOptionalObject(defaults, overrides);
510
+ }
511
+ function mergeHostedTenant(defaults, overrides) {
512
+ const merged = mergeOptionalObject(defaults, overrides);
513
+ if (!merged) return void 0;
514
+ if (!merged.endpoint?.trim() || !merged.apiKey?.trim() || !merged.tenantId?.trim()) {
515
+ throw new Error(
516
+ "defineAgentEval.improve: hostedTenant requires endpoint, apiKey, and tenantId after merging defaults and overrides"
517
+ );
518
+ }
519
+ return merged;
520
+ }
521
+ function mergeDefined(defaults, overrides) {
522
+ if (!overrides) return defaults;
523
+ const merged = { ...defaults };
524
+ for (const [key, value] of Object.entries(overrides)) {
525
+ if (value !== void 0) merged[key] = value;
526
+ }
527
+ return merged;
528
+ }
529
+ function mergeOptionalObject(defaults, overrides) {
530
+ if (!defaults && !overrides) return void 0;
531
+ return mergeDefined(defaults ?? {}, overrides);
532
+ }
533
+ function evaluateJudges(judges, defaultJudge) {
534
+ if (judges !== void 0) {
535
+ if (judges.length === 0) {
536
+ throw new Error("defineAgentEval.evaluate: judges must not be empty");
537
+ }
538
+ return judges;
539
+ }
540
+ return [defaultJudge];
541
+ }
542
+ function requirePositiveInteger(value, field) {
543
+ if (!Number.isInteger(value) || value < 1) {
544
+ throw new Error(`defineAgentEval: ${field} must be a positive integer`);
545
+ }
546
+ return value;
547
+ }
548
+
549
+ // src/contract/measured-comparison.ts
550
+ import {
551
+ agentImprovementMeasuredComparisonSchema
552
+ } from "@tangle-network/agent-interface";
553
+ function measuredComparisonFromSelfImproveResult(options) {
554
+ const { result } = options;
555
+ verifyLoopProvenanceRecord(result.provenance);
556
+ const power = result.power;
557
+ if (!power) throw new Error("agent improvement comparison requires heldout power analysis");
558
+ if (result.provenance.gate.reasons.length === 0) {
559
+ throw new Error("agent improvement comparison requires measured decision reasons");
560
+ }
561
+ const receiptLedger = new CostLedger({ receipts: result.receipts });
562
+ const receiptCost = receiptLedger.summary();
563
+ const receipts = receiptLedger.list();
564
+ assertRealAgentReceipts(receipts, { allowMixed: false });
565
+ const receiptsById = new Map(receipts.map((receipt) => [receipt.callId, receipt]));
566
+ const receiptIdsByCell = indexReceiptIdsByCell(receipts);
567
+ assertCompleteMeasuredCampaign(
568
+ result.raw.baselineOnHoldout,
569
+ "heldout baseline",
570
+ receiptsById,
571
+ receiptIdsByCell
572
+ );
573
+ assertCompleteMeasuredCampaign(
574
+ result.raw.winnerOnHoldout,
575
+ "heldout candidate",
576
+ receiptsById,
577
+ receiptIdsByCell
578
+ );
579
+ const pairs = pairMeasuredCells(
580
+ result.raw.baselineOnHoldout.cells,
581
+ result.raw.winnerOnHoldout.cells
582
+ );
583
+ const composite = measuredObjective(
584
+ {
585
+ kind: "objective",
586
+ name: "composite",
587
+ direction: "higher-is-better",
588
+ unit: "score"
589
+ },
590
+ pairs,
591
+ measuredComposite
592
+ );
593
+ assertMeasuredNumber(result.lift, composite.delta, "heldout lift");
594
+ assertMeasuredNumber(result.baseline.compositeMean, composite.baseline, "heldout baseline");
595
+ assertMeasuredNumber(result.winner.compositeMean, composite.candidate, "heldout candidate");
596
+ const baselineContentHash = surfaceContentHash(options.baselineSurface);
597
+ const candidateContentHash = surfaceContentHash(result.winner.surface);
598
+ const canonicalDiff = baselineContentHash === candidateContentHash ? "" : renderSurfaceDiff(result.raw.winnerSurface, options.baselineSurface);
599
+ assertMeasuredIdentity(result.gateDecision, result.raw.gateResult.decision, "raw decision");
600
+ assertMeasuredIdentity(
601
+ candidateContentHash,
602
+ surfaceContentHash(result.raw.winnerSurface),
603
+ "raw winner surface"
604
+ );
605
+ assertMeasuredIdentity(result.diff, canonicalDiff, "surface diff");
606
+ assertMeasuredIdentity(result.raw.promotedDiff, canonicalDiff, "raw surface diff");
607
+ if (result.gateDecision === "ship" && (baselineContentHash === candidateContentHash || result.diff.trim().length === 0)) {
608
+ throw new Error("a shipped improvement requires a changed surface and non-empty diff");
609
+ }
610
+ assertMeasuredIdentity(
611
+ canonicalDigest(power),
612
+ canonicalDigest(
613
+ powerPreflight({
614
+ baselineComposites: pairs.map(([cell]) => measuredComposite(cell)),
615
+ sharedScorerChannel: true
616
+ })
617
+ ),
618
+ "power analysis"
619
+ );
620
+ assertMeasuredNumber(power.n, composite.n, "power sample size");
621
+ assertMeasuredNumber(power.confidence, 0.95, "power confidence");
622
+ assertCompleteMeasuredCampaign(
623
+ result.raw.baselineCampaign,
624
+ "search baseline",
625
+ receiptsById,
626
+ receiptIdsByCell
627
+ );
628
+ assertGenerationMeasurements(result.raw.generations, receiptsById, receiptIdsByCell);
629
+ if (result.raw.neutralizedOnHoldout === void 0 !== (result.raw.neutralizedSurface === void 0)) {
630
+ throw new Error("neutralized surface and campaign must be supplied together");
631
+ }
632
+ if (result.raw.neutralizedOnHoldout) {
633
+ assertCompleteMeasuredCampaign(
634
+ result.raw.neutralizedOnHoldout,
635
+ "heldout neutralized",
636
+ receiptsById,
637
+ receiptIdsByCell
638
+ );
639
+ pairMeasuredCells(result.raw.baselineOnHoldout.cells, result.raw.neutralizedOnHoldout.cells);
640
+ }
641
+ const rebuiltProvenance = buildLoopProvenanceRecord(
642
+ loopProvenanceArgsFromResult({
643
+ runId: result.provenance.runId,
644
+ runDir: result.provenance.runDir,
645
+ timestamp: result.provenance.timestamp,
646
+ baselineSurface: options.baselineSurface,
647
+ result: result.raw,
648
+ costReceipts: receipts,
649
+ totalCostUsd: result.totalCostUsd,
650
+ totalDurationMs: result.durationMs
651
+ })
652
+ );
653
+ assertMeasuredIdentity(
654
+ result.provenance.recordDigest,
655
+ rebuiltProvenance.recordDigest,
656
+ "provenance record"
657
+ );
658
+ assertMeasuredIdentity(
659
+ options.benchmark.splitDigest,
660
+ rebuiltProvenance.evidence.holdout.splitDigest,
661
+ "benchmark heldout split"
662
+ );
663
+ const generationsExplored = result.raw.generations.length;
664
+ assertMeasuredNumber(result.generationsExplored, generationsExplored, "generation count");
665
+ assertMeasuredNumber(result.totalCostUsd, result.cost.totalCostUsd, "cost summary");
666
+ assertMeasuredIdentity(
667
+ canonicalDigest(result.cost),
668
+ canonicalDigest(result.raw.cost),
669
+ "raw cost summary"
670
+ );
671
+ if (!result.cost.accountingComplete) {
672
+ throw new Error(
673
+ `cost accounting is incomplete: ${result.cost.incompleteReasons.join("; ") || "unknown reason"}`
674
+ );
675
+ }
676
+ if (!receiptCost.accountingComplete) {
677
+ throw new Error(
678
+ `cost accounting is incomplete: ${receiptCost.incompleteReasons.join("; ") || "unknown reason"}`
679
+ );
680
+ }
681
+ assertMeasuredIdentity(
682
+ canonicalDigest(result.cost),
683
+ canonicalDigest(receiptCost),
684
+ "cost receipt summary"
685
+ );
686
+ assertMeasuredNumber(result.totalCostUsd, receiptCost.totalCostUsd, "cost receipts");
687
+ assertMeasuredOptional(result.winner.label, result.raw.winnerLabel, "raw winner label");
688
+ assertMeasuredOptional(
689
+ result.winner.rationale,
690
+ result.raw.winnerRationale,
691
+ "raw winner rationale"
692
+ );
693
+ return agentImprovementMeasuredComparisonSchema.parse({
694
+ schemaVersion: 1,
695
+ kind: "agent-improvement-measured-comparison",
696
+ benchmark: options.benchmark,
697
+ baselineProfileDigest: options.baselineProfileDigest,
698
+ candidateBundleDigest: options.candidateBundleDigest,
699
+ overall: {
700
+ name: "composite",
701
+ baseline: composite.baseline,
702
+ candidate: composite.candidate,
703
+ delta: composite.delta,
704
+ confidenceInterval: composite.confidenceInterval,
705
+ n: composite.n,
706
+ direction: "higher-is-better",
707
+ unit: "score"
708
+ },
709
+ objectives: measuredObjectives(pairs),
710
+ ...result.winner.label || result.winner.rationale ? {
711
+ candidate: {
712
+ ...result.winner.label ? { label: result.winner.label } : {},
713
+ ...result.winner.rationale ? { rationale: result.winner.rationale } : {}
714
+ }
715
+ } : {},
716
+ decision: {
717
+ outcome: result.gateDecision,
718
+ reasons: rebuiltProvenance.gate.reasons,
719
+ contributingChecks: rebuiltProvenance.gate.contributingGates.map((check) => ({
720
+ name: check.name,
721
+ passed: check.passed
722
+ }))
723
+ },
724
+ power: {
725
+ sufficient: power.scaleAssumed && !power.underpowered,
726
+ n: power.n,
727
+ minimumDetectableDelta: power.mde,
728
+ confidenceLevel: power.confidence,
729
+ scaleAssumed: power.scaleAssumed,
730
+ sharedScorerChannel: power.sharedChannelCaveat !== void 0,
731
+ reason: power.recommendation
732
+ },
733
+ provenance: {
734
+ kind: "agent-eval-loop",
735
+ schema: rebuiltProvenance.schema,
736
+ runId: rebuiltProvenance.runId,
737
+ recordDigest: rebuiltProvenance.recordDigest,
738
+ baselineContentHash,
739
+ candidateContentHash
740
+ },
741
+ diff: canonicalDiff,
742
+ evaluation: {
743
+ generationsExplored,
744
+ durationMs: result.durationMs,
745
+ totalCostUsd: result.totalCostUsd
746
+ }
747
+ });
748
+ }
749
+ function assertGenerationMeasurements(generations, receiptsById, receiptIdsByCell) {
750
+ for (const generation of generations) {
751
+ for (const measured of generation.surfaces) {
752
+ const candidate = generation.record.candidates.find(
753
+ (entry) => entry.surfaceHash === measured.surfaceHash
754
+ );
755
+ if (!candidate) {
756
+ throw new Error(
757
+ `generation ${generation.record.generationIndex} is missing candidate ${measured.surfaceHash}`
758
+ );
759
+ }
760
+ const composite = campaignMeanComposite(measured.campaign);
761
+ const breakdown = campaignBreakdown(measured.campaign);
762
+ const coverage = campaignCoverage(
763
+ measured.campaign.cells,
764
+ measured.campaign.scenarios,
765
+ measured.campaign.reps,
766
+ true
767
+ );
768
+ assertScorableMeasuredCells(
769
+ measured.campaign,
770
+ coverage.scorableCellIds,
771
+ receiptsById,
772
+ receiptIdsByCell
773
+ );
774
+ assertMeasuredNumber(
775
+ candidate.composite,
776
+ composite,
777
+ `candidate ${measured.surfaceHash} composite`
778
+ );
779
+ if (!candidate.coverage) {
780
+ throw new Error(`candidate ${measured.surfaceHash} does not report campaign coverage`);
781
+ }
782
+ assertMeasuredIdentity(
783
+ canonicalDigest(candidate.coverage),
784
+ canonicalDigest({
785
+ expectedCells: coverage.expectedCellIds.length,
786
+ scorableCells: coverage.scorableCellIds.length,
787
+ unscorableCells: coverage.unscorableCells
788
+ }),
789
+ `candidate ${measured.surfaceHash} coverage`
790
+ );
791
+ assertMeasuredIdentity(
792
+ String(candidate.eligibleForPromotion),
793
+ String(coverage.complete),
794
+ `candidate ${measured.surfaceHash} eligibility`
795
+ );
796
+ assertMeasuredIdentity(
797
+ canonicalDigest(candidate.dimensions),
798
+ canonicalDigest(breakdown.dimensions),
799
+ `candidate ${measured.surfaceHash} dimensions`
800
+ );
801
+ assertMeasuredIdentity(
802
+ canonicalDigest(candidate.scenarios),
803
+ canonicalDigest(breakdown.scenarios),
804
+ `candidate ${measured.surfaceHash} scenarios`
805
+ );
806
+ }
807
+ }
808
+ }
809
+ function assertCompleteMeasuredCampaign(campaign, name, receiptsById, receiptIdsByCell) {
810
+ assertCampaignSplitIdentity(campaign.scenarios, campaign.reps, campaign.splitDigest);
811
+ const coverage = campaignCoverage(campaign.cells, campaign.scenarios, campaign.reps, true);
812
+ if (!coverage.complete) {
813
+ throw new Error(
814
+ `${name} is incomplete (${coverage.scorableCellIds.length}/${coverage.expectedCellIds.length} designed cells scorable)`
815
+ );
816
+ }
817
+ assertScorableMeasuredCells(campaign, coverage.scorableCellIds, receiptsById, receiptIdsByCell);
818
+ }
819
+ function assertScorableMeasuredCells(campaign, scorableCellIds, receiptsById, receiptIdsByCell) {
820
+ const cellsById = new Map(campaign.cells.map((cell) => [cell.cellId, cell]));
821
+ for (const cellId of scorableCellIds) {
822
+ const cell = cellsById.get(cellId);
823
+ if (!cell) throw new Error(`measured campaign is missing scorable cell ${cellId}`);
824
+ assertMeasuredCell(cell, receiptsById, receiptIdsByCell, campaign.runDir);
825
+ }
826
+ }
827
+ function measuredObjectives(pairs) {
828
+ const qualityColumns = /* @__PURE__ */ new Map();
829
+ for (const [baseline, candidate] of pairs) {
830
+ for (const cell of [baseline, candidate]) {
831
+ for (const [objective, score] of Object.entries(cell.judgeScores)) {
832
+ if (score.failed) continue;
833
+ qualityColumns.set(`objective:${objective}`, {
834
+ kind: "objective",
835
+ name: objective,
836
+ direction: "higher-is-better",
837
+ unit: "score"
838
+ });
839
+ for (const name of Object.keys(score.dimensions)) {
840
+ qualityColumns.set(`dimension:${objective}:${name}`, {
841
+ kind: "dimension",
842
+ objective,
843
+ name,
844
+ direction: "higher-is-better",
845
+ unit: "score"
846
+ });
847
+ }
848
+ }
849
+ }
850
+ }
851
+ return [
852
+ ...[...qualityColumns.entries()].sort(([left], [right]) => left.localeCompare(right)).map(
853
+ ([, column]) => measuredObjective(column, pairs, (cell) => measuredQuality(cell, column))
854
+ ),
855
+ measuredCostObjective(pairs),
856
+ measuredObjective(
857
+ {
858
+ kind: "latency",
859
+ name: "latency",
860
+ direction: "lower-is-better",
861
+ unit: "milliseconds"
862
+ },
863
+ pairs,
864
+ (cell) => cell.durationMs
865
+ )
866
+ ];
867
+ }
868
+ function measuredCostObjective(pairs) {
869
+ const cells = pairs.flat();
870
+ for (const cell of cells) finiteMeasuredValue(cell.costUsd, "cost:cost");
871
+ return measuredObjective(
872
+ {
873
+ kind: "cost",
874
+ name: "cost",
875
+ direction: "lower-is-better",
876
+ unit: "usd"
877
+ },
878
+ pairs,
879
+ (cell) => cell.costUsd
880
+ );
881
+ }
882
+ function measuredComposite(cell) {
883
+ const values = Object.values(cell.judgeScores).filter((score) => !score.failed).map((score) => score.composite).filter(Number.isFinite);
884
+ if (values.length === 0) {
885
+ throw new Error(`heldout cell ${measuredCellKey(cell)} has no successful composite score`);
886
+ }
887
+ return measuredMean(values);
888
+ }
889
+ function pairMeasuredCells(baselineCells, candidateCells) {
890
+ const cells = [...baselineCells, ...candidateCells];
891
+ const errors = cells.filter((cell) => cell.error);
892
+ if (errors.length > 0) {
893
+ throw new Error(
894
+ `measured objectives cannot publish ${errors.length} errored heldout cells: ${errors.map((cell) => measuredCellKey(cell)).join(", ")}`
895
+ );
896
+ }
897
+ const rows = [
898
+ ...baselineCells.map((cell) => ({
899
+ pairKey: cell.scenarioId,
900
+ repKey: String(cell.rep),
901
+ arm: "baseline",
902
+ cell
903
+ })),
904
+ ...candidateCells.map((cell) => ({
905
+ pairKey: cell.scenarioId,
906
+ repKey: String(cell.rep),
907
+ arm: "candidate",
908
+ cell
909
+ }))
910
+ ];
911
+ const paired = pairArms(rows, { baselineArm: "baseline", treatmentArm: "candidate" });
912
+ if (paired.pairs.length === 0 || paired.unpairedBaseline.length > 0 || paired.unpairedTreatment.length > 0) {
913
+ throw new Error("measured objectives require the same non-empty paired heldout cells");
914
+ }
915
+ return paired.pairs.map((pair) => {
916
+ const baseline = pair.baseline.cell;
917
+ const candidate = pair.treatment.cell;
918
+ if (baseline.seed !== candidate.seed) {
919
+ throw new Error(`heldout cell ${baseline.cellId} does not share one paired seed`);
920
+ }
921
+ return [baseline, candidate];
922
+ });
923
+ }
924
+ function assertMeasuredCell(cell, receiptsById, receiptIdsByCell, runDir) {
925
+ if (!cell.scenarioId.trim() || !Number.isSafeInteger(cell.rep) || cell.rep < 0 || !Number.isSafeInteger(cell.seed) || cell.cellId !== measuredCellKey(cell)) {
926
+ throw new Error(`heldout cell '${cell.cellId}' has an invalid scenario/rep identity`);
927
+ }
928
+ nonnegativeMeasuredValue(cell.costUsd, `heldout cell ${cell.cellId} cost`);
929
+ nonnegativeMeasuredValue(cell.durationMs, `heldout cell ${cell.cellId} latency`);
930
+ if (!cell.tokenUsage) {
931
+ throw new Error(`heldout cell ${cell.cellId} does not report token usage`);
932
+ }
933
+ nonnegativeMeasuredInteger(cell.tokenUsage.input, `heldout cell ${cell.cellId} input tokens`);
934
+ nonnegativeMeasuredInteger(cell.tokenUsage.output, `heldout cell ${cell.cellId} output tokens`);
935
+ if (cell.tokenUsage.cached !== void 0) {
936
+ nonnegativeMeasuredInteger(cell.tokenUsage.cached, `heldout cell ${cell.cellId} cached tokens`);
937
+ }
938
+ if (!Array.isArray(cell.costCallIds)) {
939
+ throw new Error(`heldout cell ${cell.cellId} does not identify its cost receipts`);
940
+ }
941
+ if (new Set(cell.costCallIds).size !== cell.costCallIds.length) {
942
+ throw new Error(`heldout cell ${cell.cellId} repeats a cost receipt`);
943
+ }
944
+ const linkedReceipts = cell.costCallIds.map((callId) => {
945
+ const receipt = receiptsById.get(callId);
946
+ if (!receipt)
947
+ throw new Error(`heldout cell ${cell.cellId} references missing cost receipt ${callId}`);
948
+ if (receipt.tags?.runDir !== runDir || receipt.tags.cellId !== cell.cellId || receipt.tags.scenarioId !== cell.scenarioId || receipt.tags.rep !== String(cell.rep)) {
949
+ throw new Error(`heldout cell ${cell.cellId} references a cost receipt from another cell`);
950
+ }
951
+ return receipt;
952
+ });
953
+ assertMeasuredIdentity(
954
+ canonicalDigest([...cell.costCallIds].sort()),
955
+ canonicalDigest(
956
+ [
957
+ ...receiptIdsByCell.get(receiptCellKey(runDir, cell.cellId, cell.scenarioId, cell.rep)) ?? []
958
+ ].sort()
959
+ ),
960
+ `heldout cell ${cell.cellId} cost receipt IDs`
961
+ );
962
+ const agentReceipts = linkedReceipts.filter((receipt) => receipt.channel === "agent");
963
+ if (agentReceipts.some((receipt) => receipt.error)) {
964
+ throw new Error(`measured cell ${cell.cellId} links a failed agent receipt`);
965
+ }
966
+ assertRealAgentReceipts(agentReceipts, { allowMixed: false });
967
+ assertMeasuredNumber(
968
+ cell.costUsd,
969
+ agentReceipts.reduce((total, receipt) => total + receipt.costUsd, 0),
970
+ `heldout cell ${cell.cellId} cost receipts`
971
+ );
972
+ assertMeasuredNumber(
973
+ cell.tokenUsage.input,
974
+ agentReceipts.reduce((total, receipt) => total + receipt.inputTokens, 0),
975
+ `heldout cell ${cell.cellId} input receipts`
976
+ );
977
+ assertMeasuredNumber(
978
+ cell.tokenUsage.output,
979
+ agentReceipts.reduce((total, receipt) => total + receipt.outputTokens, 0),
980
+ `heldout cell ${cell.cellId} output receipts`
981
+ );
982
+ assertMeasuredNumber(
983
+ cell.tokenUsage.cached ?? 0,
984
+ agentReceipts.reduce((total, receipt) => total + (receipt.cachedTokens ?? 0), 0),
985
+ `heldout cell ${cell.cellId} cached receipts`
986
+ );
987
+ for (const [judge, score] of Object.entries(cell.judgeScores)) {
988
+ if (score.failed) {
989
+ throw new Error(`heldout cell ${cell.cellId} contains failed judge '${judge}'`);
990
+ }
991
+ finiteMeasuredValue(score.composite, `objective:${judge}`);
992
+ for (const [dimension, value] of Object.entries(score.dimensions)) {
993
+ finiteMeasuredValue(value, `dimension:${judge}:${dimension}`);
994
+ }
995
+ }
996
+ measuredComposite(cell);
997
+ }
998
+ function measuredObjective(identity, pairs, value) {
999
+ const key = measuredObjectiveKey(identity);
1000
+ const baseline = pairs.map(([cell]) => finiteMeasuredValue(value(cell), key));
1001
+ const candidate = pairs.map(([, cell]) => finiteMeasuredValue(value(cell), key));
1002
+ const interval = pairedBootstrap(baseline, candidate, {
1003
+ confidence: 0.95,
1004
+ resamples: 2e3,
1005
+ statistic: "mean",
1006
+ seed: measuredSeed(key)
1007
+ });
1008
+ const baselineMean = measuredMean(baseline);
1009
+ const candidateMean = measuredMean(candidate);
1010
+ return {
1011
+ ...identity,
1012
+ availability: "measured",
1013
+ baseline: baselineMean,
1014
+ candidate: candidateMean,
1015
+ delta: candidateMean - baselineMean,
1016
+ confidenceInterval: {
1017
+ level: interval.confidence,
1018
+ lower: interval.low,
1019
+ upper: interval.high,
1020
+ method: "paired-bootstrap",
1021
+ statistic: "mean",
1022
+ resamples: interval.resamples
1023
+ },
1024
+ n: interval.n
1025
+ };
1026
+ }
1027
+ function measuredQuality(cell, column) {
1028
+ const objective = column.kind === "objective" ? column.name : column.objective;
1029
+ const score = cell.judgeScores[objective];
1030
+ if (!score || score.failed) {
1031
+ throw new Error(
1032
+ `heldout cell ${measuredCellKey(cell)} is missing measured objective '${objective}'`
1033
+ );
1034
+ }
1035
+ const value = column.kind === "objective" ? score.composite : score.dimensions[column.name];
1036
+ if (value === void 0) {
1037
+ throw new Error(
1038
+ `heldout cell ${measuredCellKey(cell)} is missing '${objective}' dimension '${column.name}'`
1039
+ );
1040
+ }
1041
+ return finiteMeasuredValue(value, measuredObjectiveKey(column));
1042
+ }
1043
+ function measuredObjectiveKey(identity) {
1044
+ return identity.kind === "dimension" ? `${identity.kind}:${identity.objective}:${identity.name}` : `${identity.kind}:${identity.name}`;
1045
+ }
1046
+ function measuredCellKey(cell) {
1047
+ return `${cell.scenarioId}:${cell.rep}`;
1048
+ }
1049
+ function finiteMeasuredValue(value, name) {
1050
+ if (!Number.isFinite(value)) throw new Error(`measured objective '${name}' is not finite`);
1051
+ return value;
1052
+ }
1053
+ function nonnegativeMeasuredValue(value, name) {
1054
+ if (!Number.isFinite(value) || value < 0) throw new Error(`${name} must be non-negative`);
1055
+ return value;
1056
+ }
1057
+ function nonnegativeMeasuredInteger(value, name) {
1058
+ if (!Number.isSafeInteger(value) || value < 0) {
1059
+ throw new Error(`${name} must be a non-negative safe integer`);
1060
+ }
1061
+ return value;
1062
+ }
1063
+ function measuredMean(values) {
1064
+ if (values.length === 0) throw new Error("measured objective has no paired values");
1065
+ return values.reduce((sum, value) => sum + value, 0) / values.length;
1066
+ }
1067
+ function measuredSeed(value) {
1068
+ let seed = 2166136261;
1069
+ for (const byte of Buffer.from(value, "utf8")) {
1070
+ seed = Math.imul(seed ^ byte, 16777619) >>> 0;
1071
+ }
1072
+ return seed;
1073
+ }
1074
+ function assertMeasuredNumber(actual, expected, name) {
1075
+ const tolerance = Number.EPSILON * Math.max(1, Math.abs(actual), Math.abs(expected)) * 8;
1076
+ if (!Number.isFinite(actual) || !Number.isFinite(expected) || Math.abs(actual - expected) > tolerance) {
1077
+ throw new Error(`${name} does not agree across the measured comparison`);
1078
+ }
1079
+ }
1080
+ function assertMeasuredIdentity(actual, expected, name) {
1081
+ if (actual !== expected) throw new Error(`${name} does not agree across the measured comparison`);
1082
+ }
1083
+ function assertMeasuredOptional(actual, expected, name) {
1084
+ if (actual !== expected) throw new Error(`${name} does not agree across the measured comparison`);
1085
+ }
1086
+ function indexReceiptIdsByCell(receipts) {
1087
+ const idsByCell = /* @__PURE__ */ new Map();
1088
+ for (const receipt of receipts) {
1089
+ const tags = receipt.tags;
1090
+ if (!tags?.runDir || !tags.cellId || !tags.scenarioId || tags.rep === void 0) continue;
1091
+ const key = receiptCellKey(tags.runDir, tags.cellId, tags.scenarioId, tags.rep);
1092
+ const ids = idsByCell.get(key) ?? [];
1093
+ ids.push(receipt.callId);
1094
+ idsByCell.set(key, ids);
1095
+ }
1096
+ return idsByCell;
1097
+ }
1098
+ function receiptCellKey(runDir, cellId, scenarioId, rep) {
1099
+ return JSON.stringify([runDir, cellId, scenarioId, String(rep)]);
1100
+ }
1101
+
1102
+ // src/contract/eval-reporting-suite.ts
1103
+ import { mkdir, writeFile } from "fs/promises";
1104
+ import { dirname, join as join2 } from "path";
1105
+
1106
+ // src/contract/intake/run-record-dir.ts
1107
+ import { readdir, readFile, stat } from "fs/promises";
1108
+ import { join } from "path";
1109
+ var ANALYSIS_ARTIFACT = "analysis.json";
1110
+ function defaultInclude(fileName) {
1111
+ if (fileName === ANALYSIS_ARTIFACT) return false;
1112
+ return fileName.endsWith(".json") || fileName.endsWith(".jsonl");
1113
+ }
1114
+ async function fromRunRecordDir(path, options = {}) {
1115
+ const onInvalid = options.onInvalid ?? "throw";
1116
+ const include = options.include ?? defaultInclude;
1117
+ const stats = await stat(path);
1118
+ const filePaths = stats.isDirectory() ? await collectFiles(path, include, options.recursive ?? false) : [path];
1119
+ const runs = [];
1120
+ const rejected = [];
1121
+ for (const file of filePaths) {
1122
+ const raw = await parseRecordFile(file);
1123
+ for (const { index, value } of raw) {
1124
+ const parsed = parseRunRecordSafe(value);
1125
+ if (parsed.ok) {
1126
+ runs.push(parsed.value);
1127
+ continue;
1128
+ }
1129
+ const rejection = { file, index, reason: parsed.error.message };
1130
+ if (onInvalid === "throw") {
1131
+ throw new Error(
1132
+ `fromRunRecordDir: invalid RunRecord in '${file}' at index ${index}: ${parsed.error.message}`
1133
+ );
1134
+ }
1135
+ rejected.push(rejection);
1136
+ }
1137
+ }
1138
+ return { runs, rejected, files: filePaths };
1139
+ }
1140
+ async function parseRecordFile(file) {
1141
+ const text = await readFile(file, "utf8");
1142
+ const trimmed = text.trim();
1143
+ if (trimmed.length === 0) return [];
1144
+ if (trimmed.startsWith("[")) {
1145
+ const parsed = JSON.parse(trimmed);
1146
+ if (!Array.isArray(parsed)) {
1147
+ throw new Error(`fromRunRecordDir: file '${file}' did not parse to an array`);
1148
+ }
1149
+ return parsed.map((value, index) => ({ index, value }));
1150
+ }
1151
+ const out = [];
1152
+ const lines = trimmed.split("\n");
1153
+ for (let i = 0; i < lines.length; i++) {
1154
+ const line = lines[i].trim();
1155
+ if (line.length === 0) continue;
1156
+ try {
1157
+ out.push({ index: i, value: JSON.parse(line) });
1158
+ } catch (err) {
1159
+ throw new Error(
1160
+ `fromRunRecordDir: file '${file}' line ${i + 1} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`
1161
+ );
1162
+ }
1163
+ }
1164
+ return out;
1165
+ }
1166
+ async function collectFiles(dir, include, recursive) {
1167
+ const entries = await readdir(dir, { withFileTypes: true });
1168
+ const files = [];
1169
+ const subdirs = [];
1170
+ for (const entry of entries) {
1171
+ if (entry.isDirectory()) {
1172
+ if (recursive) subdirs.push(join(dir, entry.name));
1173
+ continue;
1174
+ }
1175
+ if (include(entry.name)) files.push(join(dir, entry.name));
1176
+ }
1177
+ files.sort();
1178
+ subdirs.sort();
1179
+ for (const sub of subdirs) {
1180
+ files.push(...await collectFiles(sub, include, recursive));
1181
+ }
1182
+ return files;
1183
+ }
1184
+
1185
+ // src/contract/eval-reporting-suite.ts
1186
+ var ANALYSIS_ARTIFACT2 = "analysis.json";
1187
+ async function evalReportingSuite(input, options = {}) {
1188
+ const fromPath = typeof input === "string";
1189
+ let runs;
1190
+ let files = [];
1191
+ let rejected = [];
1192
+ if (fromPath) {
1193
+ const loaded = await fromRunRecordDir(input, options.load);
1194
+ runs = loaded.runs;
1195
+ files = loaded.files;
1196
+ rejected = loaded.rejected;
1197
+ } else {
1198
+ runs = input;
1199
+ }
1200
+ if (runs.length === 0) {
1201
+ throw new Error(
1202
+ fromPath ? `evalReportingSuite: no RunRecords found at '${input}'` : "evalReportingSuite: no RunRecords to analyze"
1203
+ );
1204
+ }
1205
+ const report = await analyzeRuns({ ...options.analyze, runs });
1206
+ const result = {
1207
+ report,
1208
+ provenance: {
1209
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1210
+ runCount: runs.length,
1211
+ sourcePath: fromPath ? input : null,
1212
+ files,
1213
+ rejected
1214
+ },
1215
+ writtenTo: null
1216
+ };
1217
+ const target = resolveWriteTarget(options.write, fromPath ? input : null);
1218
+ if (target) {
1219
+ await mkdir(dirname(target), { recursive: true });
1220
+ await writeFile(target, `${JSON.stringify(result, null, 2)}
1221
+ `, "utf8");
1222
+ result.writtenTo = target;
1223
+ }
1224
+ return result;
1225
+ }
1226
+ function resolveWriteTarget(write, sourcePath) {
1227
+ if (!write) return null;
1228
+ if (typeof write === "string") {
1229
+ const looksLikeDir = write.endsWith("/") || !write.endsWith(".json") && !write.endsWith(".jsonl");
1230
+ return looksLikeDir ? join2(write, ANALYSIS_ARTIFACT2) : write;
1231
+ }
1232
+ if (sourcePath === null) {
1233
+ throw new Error(
1234
+ "evalReportingSuite: write:true needs a source path to anchor analysis.json \u2014 pass an explicit output path when analyzing in-memory records"
1235
+ );
1236
+ }
1237
+ const isFile = sourcePath.endsWith(".json") || sourcePath.endsWith(".jsonl");
1238
+ return isFile ? join2(dirname(sourcePath), ANALYSIS_ARTIFACT2) : join2(sourcePath, ANALYSIS_ARTIFACT2);
1239
+ }
1240
+
1241
+ // src/contract/diff.ts
1242
+ function keyForCell(cell) {
1243
+ return JSON.stringify([cell.scenarioId, cell.rep]);
1244
+ }
1245
+ function diffDimensions(before, after) {
1246
+ const out = {};
1247
+ const judges = /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]);
1248
+ for (const judge of judges) {
1249
+ const beforeDims = before[judge] ?? {};
1250
+ const afterDims = after[judge] ?? {};
1251
+ const dims = /* @__PURE__ */ new Set([...Object.keys(beforeDims), ...Object.keys(afterDims)]);
1252
+ const judgeOut = {};
1253
+ for (const dim of dims) {
1254
+ const rawBefore = beforeDims[dim];
1255
+ const rawAfter = afterDims[dim];
1256
+ const b = typeof rawBefore === "number" && Number.isFinite(rawBefore) ? rawBefore : null;
1257
+ const a = typeof rawAfter === "number" && Number.isFinite(rawAfter) ? rawAfter : null;
1258
+ judgeOut[dim] = {
1259
+ before: b,
1260
+ after: a,
1261
+ delta: b !== null && a !== null ? a - b : null
1262
+ };
1263
+ }
1264
+ out[judge] = judgeOut;
1265
+ }
1266
+ return out;
1267
+ }
1268
+ function diffGenerations(before, after) {
1269
+ const beforeMap = new Map(before.cells.map((c) => [keyForCell(c), c]));
1270
+ const afterMap = new Map(after.cells.map((c) => [keyForCell(c), c]));
1271
+ const matched = [];
1272
+ const removed = [];
1273
+ const added = [];
1274
+ for (const [key, beforeCell] of beforeMap) {
1275
+ const afterCell = afterMap.get(key);
1276
+ if (!afterCell) {
1277
+ removed.push(beforeCell);
1278
+ continue;
1279
+ }
1280
+ matched.push({
1281
+ scenarioId: beforeCell.scenarioId,
1282
+ rep: beforeCell.rep,
1283
+ compositeBefore: beforeCell.compositeMean,
1284
+ compositeAfter: afterCell.compositeMean,
1285
+ compositeDelta: afterCell.compositeMean - beforeCell.compositeMean,
1286
+ dimensions: diffDimensions(beforeCell.dimensions, afterCell.dimensions)
1287
+ });
1288
+ }
1289
+ for (const [key, afterCell] of afterMap) {
1290
+ if (!beforeMap.has(key)) added.push(afterCell);
1291
+ }
1292
+ return {
1293
+ beforeIndex: before.index,
1294
+ afterIndex: after.index,
1295
+ beforeSurfaceHash: before.surfaceHash,
1296
+ afterSurfaceHash: after.surfaceHash,
1297
+ surfaceChanged: before.surfaceHash !== after.surfaceHash,
1298
+ matched,
1299
+ removed,
1300
+ added,
1301
+ compositeBefore: before.compositeMean,
1302
+ compositeAfter: after.compositeMean,
1303
+ compositeDelta: after.compositeMean - before.compositeMean,
1304
+ costUsdBefore: before.costUsd,
1305
+ costUsdAfter: after.costUsd,
1306
+ costUsdDelta: after.costUsd - before.costUsd,
1307
+ durationMsBefore: before.durationMs,
1308
+ durationMsAfter: after.durationMs,
1309
+ durationMsDelta: after.durationMs - before.durationMs
1310
+ };
1311
+ }
1312
+ function winnerOf(run) {
1313
+ if (run.generations.length === 0) return null;
1314
+ let winner = run.generations[0];
1315
+ for (const gen of run.generations) {
1316
+ if (gen.index > winner.index) winner = gen;
1317
+ }
1318
+ return winner;
1319
+ }
1320
+ function diffRuns(before, after) {
1321
+ const beforeWinner = winnerOf(before);
1322
+ const afterWinner = winnerOf(after);
1323
+ const baselineDiff = before.baseline && after.baseline ? diffGenerations(before.baseline, after.baseline) : null;
1324
+ const winnersDiff = beforeWinner && afterWinner ? diffGenerations(beforeWinner, afterWinner) : null;
1325
+ const beforeLift = before.holdoutLift ?? null;
1326
+ const afterLift = after.holdoutLift ?? null;
1327
+ return {
1328
+ beforeRunId: before.runId,
1329
+ afterRunId: after.runId,
1330
+ beforeTimestamp: before.timestamp,
1331
+ afterTimestamp: after.timestamp,
1332
+ beforeGateDecision: before.gateDecision ?? null,
1333
+ afterGateDecision: after.gateDecision ?? null,
1334
+ beforeHoldoutLift: beforeLift,
1335
+ afterHoldoutLift: afterLift,
1336
+ holdoutLiftDelta: beforeLift !== null && afterLift !== null ? afterLift - beforeLift : null,
1337
+ beforeTotalCostUsd: before.totalCostUsd,
1338
+ afterTotalCostUsd: after.totalCostUsd,
1339
+ totalCostUsdDelta: after.totalCostUsd - before.totalCostUsd,
1340
+ beforeTotalDurationMs: before.totalDurationMs,
1341
+ afterTotalDurationMs: after.totalDurationMs,
1342
+ totalDurationMsDelta: after.totalDurationMs - before.totalDurationMs,
1343
+ baselineDiff,
1344
+ winnersDiff
1345
+ };
1346
+ }
1347
+ function diffRunBaselineToWinner(run) {
1348
+ if (!run.baseline) return null;
1349
+ const winner = winnerOf(run);
1350
+ if (!winner || winner.index === run.baseline.index) return null;
1351
+ return diffGenerations(run.baseline, winner);
1352
+ }
1353
+
1354
+ // src/contract/intake/agent-trace.ts
1355
+ function rangeLines(r) {
1356
+ return Math.max(0, r.end_line - r.start_line + 1);
1357
+ }
1358
+ function parseAgentTrace(records) {
1359
+ const acc = /* @__PURE__ */ new Map();
1360
+ for (const record of records) {
1361
+ const sha = record.vcs?.revision;
1362
+ if (!sha) continue;
1363
+ let a = acc.get(sha);
1364
+ if (!a) {
1365
+ a = {
1366
+ models: /* @__PURE__ */ new Set(),
1367
+ tools: /* @__PURE__ */ new Set(),
1368
+ files: /* @__PURE__ */ new Set(),
1369
+ conversationCount: 0,
1370
+ lineCount: 0,
1371
+ humanInvolved: false
1372
+ };
1373
+ acc.set(sha, a);
1374
+ }
1375
+ if (record.tool?.name) a.tools.add(record.tool.name);
1376
+ for (const file of record.files ?? []) {
1377
+ a.files.add(file.path);
1378
+ for (const conv of file.conversations ?? []) {
1379
+ a.conversationCount += 1;
1380
+ for (const range of conv.ranges ?? []) {
1381
+ const contributor = range.contributor ?? conv.contributor;
1382
+ a.lineCount += rangeLines(range);
1383
+ if (!contributor) continue;
1384
+ if (contributor.type === "human" || contributor.type === "mixed") {
1385
+ a.humanInvolved = true;
1386
+ }
1387
+ if ((contributor.type === "ai" || contributor.type === "mixed") && contributor.model_id) {
1388
+ a.models.add(contributor.model_id);
1389
+ }
1390
+ }
1391
+ }
1392
+ }
1393
+ }
1394
+ const index = /* @__PURE__ */ new Map();
1395
+ for (const [sha, a] of acc) {
1396
+ index.set(sha, {
1397
+ commitSha: sha,
1398
+ aiModels: [...a.models].sort(),
1399
+ tools: [...a.tools].sort(),
1400
+ conversationCount: a.conversationCount,
1401
+ fileCount: a.files.size,
1402
+ lineCount: a.lineCount,
1403
+ humanInvolved: a.humanInvolved
1404
+ });
1405
+ }
1406
+ return index;
1407
+ }
1408
+ function partitionRunsByAuthoringModel(runs, index) {
1409
+ const byModel = /* @__PURE__ */ new Map();
1410
+ const unattributed = [];
1411
+ for (const run of runs) {
1412
+ const provenance = index.get(run.commitSha);
1413
+ if (!provenance || provenance.aiModels.length === 0) {
1414
+ unattributed.push(run);
1415
+ continue;
1416
+ }
1417
+ for (const model of provenance.aiModels) {
1418
+ const cohort = byModel.get(model) ?? [];
1419
+ cohort.push(run);
1420
+ byModel.set(model, cohort);
1421
+ }
1422
+ }
1423
+ return { byModel, unattributed };
1424
+ }
1425
+
1426
+ // src/contract/intake/feedback-table.ts
1427
+ function fromFeedbackTable(opts) {
1428
+ const { ratings, meta = [], scale, emitRaterScores = true } = opts;
1429
+ const metaByRun = new Map(meta.map((m) => [m.runId, m]));
1430
+ const normalise = (rating) => {
1431
+ if (typeof rating === "boolean") return rating ? 1 : 0;
1432
+ if (!Number.isFinite(rating)) return Number.NaN;
1433
+ if (!scale) return rating;
1434
+ const { min, max } = scale;
1435
+ if (max === min) return rating;
1436
+ return (rating - min) / (max - min);
1437
+ };
1438
+ const byRun = /* @__PURE__ */ new Map();
1439
+ for (const row of ratings) {
1440
+ const list = byRun.get(row.runId) ?? [];
1441
+ list.push(row);
1442
+ byRun.set(row.runId, list);
1443
+ }
1444
+ const runs = [];
1445
+ const raterScores = [];
1446
+ for (const [runId, rowsForRun] of byRun) {
1447
+ const normalised = rowsForRun.map((r) => ({ rater: r.rater, score: normalise(r.rating) })).filter((r) => Number.isFinite(r.score));
1448
+ if (normalised.length === 0) continue;
1449
+ const meanScore = normalised.reduce((s, r) => s + r.score, 0) / normalised.length;
1450
+ const runMeta = metaByRun.get(runId) ?? { runId };
1451
+ const judgeScores = {
1452
+ perJudge: Object.fromEntries(normalised.map((r) => [r.rater, { rating: r.score }])),
1453
+ perDimMean: { rating: meanScore },
1454
+ composite: meanScore
1455
+ };
1456
+ const outcome = {
1457
+ // Feedback corpora ARE the holdout signal — score lands on
1458
+ // `holdoutScore` so downstream substrate primitives (`paretoChart`,
1459
+ // promotion gates) read it correctly by default.
1460
+ holdoutScore: meanScore,
1461
+ raw: Object.fromEntries(normalised.map((r) => [`rater:${r.rater}`, r.score])),
1462
+ judgeScores
1463
+ };
1464
+ runs.push({
1465
+ runId,
1466
+ experimentId: runMeta.experimentId ?? "feedback-corpus",
1467
+ candidateId: runMeta.candidateId ?? runId,
1468
+ seed: 0,
1469
+ model: runMeta.model ?? "unknown@unknown",
1470
+ promptHash: runMeta.promptHash ?? "sha256:unknown",
1471
+ configHash: runMeta.configHash ?? "sha256:unknown",
1472
+ commitSha: runMeta.commitSha ?? "unknown",
1473
+ wallMs: runMeta.wallMs ?? 0,
1474
+ costUsd: runMeta.costUsd ?? 0,
1475
+ tokenUsage: { input: 0, output: 0 },
1476
+ outcome,
1477
+ splitTag: runMeta.splitTag ?? "holdout"
1478
+ });
1479
+ if (emitRaterScores) {
1480
+ for (const r of normalised) raterScores.push({ runId, rater: r.rater, score: r.score });
1481
+ }
1482
+ }
1483
+ return { runs, raterScores };
1484
+ }
1485
+
1486
+ // src/contract/intake/otel-spans.ts
1487
+ var SCORE_KEYS = ["tangle.score", "eval.score", "score"];
1488
+ var MODEL_KEYS = ["tangle.model", ...LLM_MODEL_ATTR_KEYS, "model"];
1489
+ var PROMPT_HASH_KEYS = ["tangle.prompt_hash", "prompt.hash"];
1490
+ var CONFIG_HASH_KEYS = ["tangle.config_hash", "config.hash"];
1491
+ function fromOtelSpans(opts) {
1492
+ const { spans, defaultSplit = "holdout", experimentId = "otel-corpus" } = opts;
1493
+ const grouped = groupSpans(spans);
1494
+ const runs = [];
1495
+ for (const [groupKey, groupSpans2] of grouped) {
1496
+ const root = findRoot(groupSpans2);
1497
+ if (!root) continue;
1498
+ const measurements = summarizeExecutionMeasurements(
1499
+ groupSpans2.map((span) => ({
1500
+ id: span.spanId,
1501
+ ...span.parentSpanId ? { parentId: span.parentSpanId } : {},
1502
+ attributes: span.attributes,
1503
+ modelCall: isExplicitModelCall(span),
1504
+ aggregate: isExplicitAggregate(span)
1505
+ }))
1506
+ );
1507
+ const callSpanIds = new Set(measurements.callSpanIds);
1508
+ const callSpans = groupSpans2.filter((span) => callSpanIds.has(span.spanId));
1509
+ const wallMs = Math.max(0, (root.endTimeUnixNano - root.startTimeUnixNano) / 1e6);
1510
+ const model = readAttrString(callSpans, MODEL_KEYS) ?? readAttrString(groupSpans2, MODEL_KEYS) ?? "unknown@unknown";
1511
+ const capturedCost = (measurements.cost.complete ? measurements.cost.value : void 0) ?? measurements.aggregate?.costUsd;
1512
+ const costUsd = capturedCost ?? 0;
1513
+ const promptHash = readAttrString(groupSpans2, PROMPT_HASH_KEYS) ?? "sha256:unknown";
1514
+ const configHash = readAttrString(groupSpans2, CONFIG_HASH_KEYS) ?? "sha256:unknown";
1515
+ const score = readAttrNumber(groupSpans2, SCORE_KEYS);
1516
+ const rawNumeric = collectNumericAttrs(groupSpans2);
1517
+ rawNumeric.error_span_count = groupSpans2.filter((span) => span.status?.code === "ERROR").length;
1518
+ rawNumeric.llm_span_count = measurements.modelCallCount;
1519
+ if (measurements.cost.value !== void 0 && !measurements.cost.complete) {
1520
+ rawNumeric.partial_observed_cost_usd = measurements.cost.value;
1521
+ }
1522
+ recordAggregateMeasurements(rawNumeric, measurements.aggregate);
1523
+ const judgeScores = score !== void 0 ? {
1524
+ perJudge: { "otel-derived": { score } },
1525
+ perDimMean: { score },
1526
+ composite: score
1527
+ } : void 0;
1528
+ const errorSpan = groupSpans2.find((s) => s.status?.code === "ERROR");
1529
+ const outcome = {
1530
+ ...opts.defaultSplit === "search" ? { searchScore: score } : { holdoutScore: score },
1531
+ raw: rawNumeric,
1532
+ ...judgeScores ? { judgeScores } : {}
1533
+ };
1534
+ runs.push({
1535
+ runId: groupKey,
1536
+ experimentId,
1537
+ candidateId: root.attributes["tangle.candidateId"] ?? "otel-default",
1538
+ seed: 0,
1539
+ model,
1540
+ promptHash,
1541
+ configHash,
1542
+ commitSha: root.attributes["tangle.commit_sha"] ?? "unknown",
1543
+ wallMs,
1544
+ costUsd,
1545
+ costProvenance: capturedCost === void 0 ? { kind: "uncaptured", usd: null } : { kind: "observed", usd: capturedCost },
1546
+ tokenUsage: measurements.tokenUsage,
1547
+ outcome,
1548
+ splitTag: defaultSplit,
1549
+ ...errorSpan ? { failureMode: errorSpan.name } : {}
1550
+ });
1551
+ }
1552
+ return runs;
1553
+ }
1554
+ function isExplicitModelCall(span) {
1555
+ const kind = span.attributes["openinference.span.kind"];
1556
+ if (typeof kind === "string") return kind.toUpperCase() === "LLM";
1557
+ const spanType = span.attributes["span.type"];
1558
+ return typeof spanType === "string" && spanType.toLowerCase() === "llm_request" || span.name.toLowerCase().includes("llm") || readAttrString([span], MODEL_KEYS) !== void 0 || typeof span.attributes["gen_ai.operation.name"] === "string";
1559
+ }
1560
+ function isExplicitAggregate(span) {
1561
+ const kind = span.attributes["openinference.span.kind"];
1562
+ return typeof kind === "string" && kind.toUpperCase() !== "LLM";
1563
+ }
1564
+ function groupSpans(spans) {
1565
+ const m = /* @__PURE__ */ new Map();
1566
+ for (const span of spans) {
1567
+ const key = span["tangle.runId"] ?? span.traceId;
1568
+ const list = m.get(key) ?? [];
1569
+ list.push(span);
1570
+ m.set(key, list);
1571
+ }
1572
+ return m;
1573
+ }
1574
+ function findRoot(group) {
1575
+ return group.find((s) => !s.parentSpanId) ?? group[0];
1576
+ }
1577
+ function readAttrString(spans, keys) {
1578
+ for (const span of spans) {
1579
+ for (const key of keys) {
1580
+ const v = span.attributes[key];
1581
+ if (typeof v === "string" && v.length > 0) return v;
1582
+ }
1583
+ }
1584
+ return void 0;
1585
+ }
1586
+ function readAttrNumber(spans, keys) {
1587
+ for (const span of spans) {
1588
+ for (const key of keys) {
1589
+ const v = span.attributes[key];
1590
+ if (typeof v === "number" && Number.isFinite(v)) return v;
1591
+ if (typeof v === "string") {
1592
+ const parsed = Number(v);
1593
+ if (Number.isFinite(parsed)) return parsed;
1594
+ }
1595
+ }
1596
+ }
1597
+ return void 0;
1598
+ }
1599
+ function collectNumericAttrs(spans) {
1600
+ const raw = {};
1601
+ for (const span of spans) {
1602
+ for (const [k, v] of Object.entries(span.attributes)) {
1603
+ if (typeof v === "number" && Number.isFinite(v)) raw[k] = v;
1604
+ }
1605
+ }
1606
+ return raw;
1607
+ }
1608
+ export {
1609
+ FileSystemOutcomeStore,
1610
+ InMemoryOutcomeStore,
1611
+ REFERENCE_EQUIVALENCE_INPUT_LIMITS,
1612
+ REFERENCE_EQUIVALENCE_JUDGE_VERSION,
1613
+ SelfImproveRunError,
1614
+ analyzeRuns,
1615
+ buildDefaultAnalystRegistry,
1616
+ buildEvidenceVector,
1617
+ campaignSplitDigest,
1618
+ composeGate,
1619
+ createChatClient,
1620
+ createReferenceEquivalenceJudge,
1621
+ defaultProductionGate,
1622
+ defineAgentEval,
1623
+ diffGenerations,
1624
+ diffRunBaselineToWinner,
1625
+ diffRuns,
1626
+ evalReportingSuite,
1627
+ evolutionaryProposer,
1628
+ fromClaudeCodeSession,
1629
+ fromCodexSession,
1630
+ fromFeedbackTable,
1631
+ fromKimiCodeSession,
1632
+ fromOpenCodeSession,
1633
+ fromOtelSpans,
1634
+ fromPiSession,
1635
+ fromPigraphSession,
1636
+ fromRunRecordDir,
1637
+ fsCampaignStorage,
1638
+ gepaProposer,
1639
+ heldOutGate,
1640
+ inMemoryCampaignStorage,
1641
+ measuredComparisonFromSelfImproveResult,
1642
+ paretoPolicy,
1643
+ paretoSignificanceGate,
1644
+ parseAgentTrace,
1645
+ parseCodeAgentJsonl,
1646
+ partitionRunsByAuthoringModel,
1647
+ runCampaign,
1648
+ runEval,
1649
+ runImprovementLoop,
1650
+ runReferenceEquivalenceJudge,
1651
+ selfImprove,
1652
+ summarizeExecution
1653
+ };
1654
+ //# sourceMappingURL=index.js.map