@tangle-network/agent-eval 0.105.0 → 0.106.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  recoverTruncatedJson
3
- } from "./chunk-ZKIL3VOD.js";
3
+ } from "./chunk-3E5KUXYZ.js";
4
4
  import {
5
5
  clamp01
6
6
  } from "./chunk-LOJ2QVCE.js";
@@ -858,4 +858,4 @@ export {
858
858
  agentProfileModelId,
859
859
  agentProfileHash
860
860
  };
861
- //# sourceMappingURL=chunk-BBQILVBH.js.map
861
+ //# sourceMappingURL=chunk-2HLM4SJJ.js.map
@@ -5,6 +5,9 @@ import {
5
5
  import {
6
6
  callLlm
7
7
  } from "./chunk-FUCQVFMU.js";
8
+ import {
9
+ pairedBootstrap
10
+ } from "./chunk-XMSYF4A7.js";
8
11
  import {
9
12
  DEFAULT_REDACTION_RULES
10
13
  } from "./chunk-GGE4NNQT.js";
@@ -1051,38 +1054,158 @@ function parseReflectionResponse(raw, maxProposals) {
1051
1054
  return out;
1052
1055
  }
1053
1056
 
1057
+ // src/campaign/gates/statistical-heldout.ts
1058
+ var TIE_WARN_FRACTION = 0.4;
1059
+ function pairHoldout(candidate, baseline, scenarioIds, select) {
1060
+ const cellValue = (byCell, cellId) => {
1061
+ const scores = byCell.get(cellId);
1062
+ if (!scores) return void 0;
1063
+ const vals = [];
1064
+ for (const s of Object.values(scores)) {
1065
+ const v = select(s);
1066
+ if (typeof v === "number" && Number.isFinite(v)) vals.push(v);
1067
+ }
1068
+ if (vals.length === 0) return void 0;
1069
+ return vals.reduce((a, b) => a + b, 0) / vals.length;
1070
+ };
1071
+ const inScope = (cellId) => scenarioIds.has(cellId.split(":")[0] ?? "");
1072
+ const candCells = [...candidate.keys()].filter(inScope).sort();
1073
+ const baseCells = [...baseline.keys()].filter(inScope).sort();
1074
+ if (candCells.length !== baseCells.length || candCells.some((c, i) => c !== baseCells[i])) {
1075
+ throw new Error(
1076
+ `pairHoldout: candidate/baseline holdout cells do not align \u2014 candidate=[${candCells.join(",")}] baseline=[${baseCells.join(",")}]. Both holdout campaigns must run the same scenarios with the same seed base.`
1077
+ );
1078
+ }
1079
+ const before = [];
1080
+ const after = [];
1081
+ const cellIds = [];
1082
+ for (const cellId of candCells) {
1083
+ const b = cellValue(baseline, cellId);
1084
+ const a = cellValue(candidate, cellId);
1085
+ if (b === void 0 || a === void 0) continue;
1086
+ before.push(b);
1087
+ after.push(a);
1088
+ cellIds.push(cellId);
1089
+ }
1090
+ return { before, after, cellIds };
1091
+ }
1092
+ function heldoutSignificance(paired, opts = {}) {
1093
+ const deltaThreshold = opts.deltaThreshold ?? 0;
1094
+ const minProductiveRuns = opts.minProductiveRuns ?? 3;
1095
+ const confidence = opts.confidence ?? 0.95;
1096
+ const resamples = opts.resamples ?? 2e3;
1097
+ const seed = opts.seed ?? 1337;
1098
+ const statistic = opts.statistic ?? "mean";
1099
+ const bootstrap = pairedBootstrap(paired.before, paired.after, {
1100
+ confidence,
1101
+ resamples,
1102
+ statistic,
1103
+ seed
1104
+ });
1105
+ const medianBootstrap = statistic === "median" ? bootstrap : pairedBootstrap(paired.before, paired.after, {
1106
+ confidence,
1107
+ resamples,
1108
+ statistic: "median",
1109
+ seed
1110
+ });
1111
+ const n = paired.before.length;
1112
+ let ties = 0;
1113
+ for (let i = 0; i < n; i += 1) {
1114
+ const after = paired.after[i] ?? 0;
1115
+ const before = paired.before[i] ?? 0;
1116
+ if (Math.abs(after - before) < 1e-9) ties += 1;
1117
+ }
1118
+ const tieFraction = n === 0 ? 0 : ties / n;
1119
+ const fewRuns = n < minProductiveRuns;
1120
+ const significant = !fewRuns && bootstrap.low > deltaThreshold;
1121
+ return { paired, bootstrap, medianBootstrap, tieFraction, n, significant, fewRuns };
1122
+ }
1123
+ function detectScale(values) {
1124
+ return values.some((v) => Math.abs(v) > 1.5) ? 100 : 1;
1125
+ }
1126
+ function dimensionRegressions(candidate, baseline, scenarioIds, criticalDimensions, opts = {}) {
1127
+ const out = [];
1128
+ for (const dim of criticalDimensions) {
1129
+ const paired = pairHoldout(candidate, baseline, scenarioIds, (s) => s.dimensions[dim]);
1130
+ if (paired.before.length === 0) continue;
1131
+ const tolerance = opts.tolerance ?? 0.05 * detectScale([...paired.before, ...paired.after]);
1132
+ const bootstrap = pairedBootstrap(paired.before, paired.after, {
1133
+ confidence: opts.confidence ?? 0.95,
1134
+ resamples: opts.resamples ?? 2e3,
1135
+ statistic: "median",
1136
+ seed: opts.seed ?? 1337
1137
+ });
1138
+ out.push({
1139
+ dimension: dim,
1140
+ bootstrap,
1141
+ regressed: bootstrap.low < -tolerance,
1142
+ tolerance,
1143
+ n: paired.before.length
1144
+ });
1145
+ }
1146
+ return out;
1147
+ }
1148
+
1054
1149
  // src/campaign/gates/heldout-gate.ts
1055
1150
  function heldOutGate(options) {
1056
1151
  const deltaThreshold = options.deltaThreshold ?? 0.5;
1152
+ const confidence = options.confidence ?? 0.95;
1153
+ const minProductiveRuns = options.minProductiveRuns ?? 3;
1154
+ const resamples = options.resamples ?? 2e3;
1155
+ const seed = options.bootstrapSeed ?? 1337;
1057
1156
  return {
1058
1157
  name: "heldOutGate",
1059
1158
  async decide(ctx) {
1159
+ if (!ctx.baselineJudgeScores) {
1160
+ throw new Error(
1161
+ "heldOutGate: ctx.baselineJudgeScores is required \u2014 comparing candidate scores against themselves would hide a missing baseline"
1162
+ );
1163
+ }
1060
1164
  const scenarioIds = new Set(options.scenarios.map((s) => s.id));
1061
- const baseline = meanForScenarios(ctx.baselineJudgeScores ?? ctx.judgeScores, scenarioIds);
1062
- const candidate = meanForScenarios(ctx.judgeScores, scenarioIds);
1063
- const delta = candidate - baseline;
1064
- const passed = delta >= deltaThreshold;
1165
+ const sig = heldoutSignificance(
1166
+ pairHoldout(ctx.judgeScores, ctx.baselineJudgeScores, scenarioIds, (s) => s.composite),
1167
+ {
1168
+ deltaThreshold,
1169
+ confidence,
1170
+ minProductiveRuns,
1171
+ resamples,
1172
+ seed
1173
+ }
1174
+ );
1175
+ const delta = sig.bootstrap.mean;
1176
+ const passed = sig.significant;
1177
+ const tieNote = sig.tieFraction >= TIE_WARN_FRACTION ? `, ${(sig.tieFraction * 100).toFixed(0)}% tied` : "";
1178
+ const ci = `${(sig.bootstrap.confidence * 100).toFixed(0)}% CI [${sig.bootstrap.low.toFixed(3)}, ${sig.bootstrap.high.toFixed(3)}]`;
1065
1179
  return {
1066
1180
  decision: passed ? "ship" : "hold",
1067
- reasons: passed ? [`held-out delta ${delta.toFixed(3)} \u2265 ${deltaThreshold}`] : [`held-out delta ${delta.toFixed(3)} < ${deltaThreshold}`],
1181
+ reasons: passed ? [
1182
+ `held-out mean \u0394 ${delta.toFixed(3)}, CI.low ${sig.bootstrap.low.toFixed(3)} > ${deltaThreshold} (${ci}, n=${sig.n}${tieNote})`
1183
+ ] : [
1184
+ sig.fewRuns ? `held-out: only ${sig.n} paired runs \u2014 too few to claim significance` : `held-out mean \u0394 ${delta.toFixed(3)}, CI.low ${sig.bootstrap.low.toFixed(3)} \u2264 ${deltaThreshold} (${ci}, n=${sig.n}${tieNote})`
1185
+ ],
1068
1186
  contributingGates: [
1069
- { name: "heldOutGate", passed, detail: { baseline, candidate, delta, deltaThreshold } }
1187
+ {
1188
+ name: "heldOutGate",
1189
+ passed,
1190
+ detail: {
1191
+ deltaMean: delta,
1192
+ deltaMedianDiagnostic: sig.medianBootstrap.median,
1193
+ tieFraction: sig.tieFraction,
1194
+ ciLow: sig.bootstrap.low,
1195
+ ciHigh: sig.bootstrap.high,
1196
+ confidence: sig.bootstrap.confidence,
1197
+ n: sig.n,
1198
+ deltaThreshold,
1199
+ fewRuns: sig.fewRuns,
1200
+ seed
1201
+ }
1202
+ }
1070
1203
  ],
1071
1204
  delta
1072
1205
  };
1073
1206
  }
1074
1207
  };
1075
1208
  }
1076
- function meanForScenarios(judgeScoresByCell, scenarioIds) {
1077
- const composites = [];
1078
- for (const [cellId, scores] of judgeScoresByCell) {
1079
- const scenarioId = cellId.split(":")[0] ?? "";
1080
- if (!scenarioIds.has(scenarioId)) continue;
1081
- const vals = Object.values(scores).map((s) => s.composite);
1082
- if (vals.length > 0) composites.push(vals.reduce((a, b) => a + b, 0) / vals.length);
1083
- }
1084
- return composites.length === 0 ? 0 : composites.reduce((a, b) => a + b, 0) / composites.length;
1085
- }
1086
1209
 
1087
1210
  // src/campaign/auto-pr.ts
1088
1211
  import { execSync } from "child_process";
@@ -2009,6 +2132,11 @@ export {
2009
2132
  buildReflectionPrompt,
2010
2133
  renderAnalystEvidence,
2011
2134
  parseReflectionResponse,
2135
+ TIE_WARN_FRACTION,
2136
+ pairHoldout,
2137
+ heldoutSignificance,
2138
+ detectScale,
2139
+ dimensionRegressions,
2012
2140
  heldOutGate,
2013
2141
  openAutoPr,
2014
2142
  campaignMeanComposite,
@@ -2029,4 +2157,4 @@ export {
2029
2157
  provenanceSpansPath,
2030
2158
  emitLoopProvenance
2031
2159
  };
2032
- //# sourceMappingURL=chunk-ZKIL3VOD.js.map
2160
+ //# sourceMappingURL=chunk-3E5KUXYZ.js.map