import {
  doublyRobust,
  inverseProbabilityWeighting,
  offPolicyEstimateAll,
  selfNormalizedImportanceWeighting
} from "./chunk-VGRCHJON.js";
import {
  FileSystemOutcomeStore,
  InMemoryOutcomeStore
} from "./chunk-3RF76KTD.js";
import {
  runEvalCampaign
} from "./chunk-DNVPOYUS.js";
import {
  detectRewardHacking,
  extractVerifiableReward,
  extractVerifiableRewardsFromRecords,
  filterDeterministicallyRewarded
} from "./chunk-ARU2PZFM.js";
import "./chunk-J7S4YM27.js";
import {
  rubricPredictiveValidity
} from "./chunk-X4UCIOTZ.js";
import {
  evaluateInterimReleaseConfidence
} from "./chunk-MAZ26DC7.js";
import "./chunk-DPZAEKA6.js";
import {
  benjaminiHochberg,
  wilcoxonSignedRank
} from "./chunk-PJQFMIOX.js";
import {
  observationsFromRunRecords,
  thompsonCurriculum,
  varianceBasedCurriculum
} from "./chunk-VZSRQ272.js";
import "./chunk-TT4KNT67.js";
import "./chunk-PC4UYEBM.js";
import "./chunk-VQMK5FMP.js";
import "./chunk-LOW3U7JZ.js";
import "./chunk-MA6HLL3S.js";
import "./chunk-GC4ATIKK.js";
import "./chunk-VSMTAMNK.js";
import {
  ValidationError
} from "./chunk-ONWEPEDO.js";
import "./chunk-PZ5AY32C.js";

// src/rl/adaptation-eval.ts
async function runAdaptationCurve(opts) {
  const ks = opts.ks ?? [0, 1, 2, 4, 8, 16];
  const reps = opts.reps ?? 3;
  const passThreshold = opts.passThreshold ?? 0.5;
  const sortedKs = [...ks].sort((a, b) => a - b);
  const points = [];
  for (const k of sortedKs) {
    const perScenario = [];
    const allScores = [];
    let totalPasses = 0;
    let totalAttempts = 0;
    for (const scenario of opts.scenarios) {
      const sid = scenario.scenarioId ?? `scenario-${opts.scenarios.indexOf(scenario)}`;
      const scores = [];
      let passes = 0;
      for (let r = 0; r < reps; r++) {
        const score = await opts.runner.run({ scenario, k, rep: r });
        scores.push(score);
        if (score >= passThreshold) passes++;
        allScores.push(score);
        if (score >= passThreshold) totalPasses++;
        totalAttempts++;
      }
      const meanS = scores.reduce((s, v) => s + v, 0) / scores.length;
      perScenario.push({ scenarioId: sid, meanScore: meanS, passes, total: scores.length });
    }
    const meanScore = allScores.reduce((s, v) => s + v, 0) / Math.max(1, allScores.length);
    const variance = allScores.length < 2 ? 0 : allScores.reduce((s, v) => s + (v - meanScore) ** 2, 0) / (allScores.length - 1);
    points.push({
      k,
      meanScore,
      passRate: totalPasses / Math.max(1, totalAttempts),
      std: Math.sqrt(variance),
      n: allScores.length,
      perScenario
    });
  }
  const firstPassK2 = points.find((p) => p.passRate >= passThreshold)?.k ?? null;
  const maxK = sortedKs[sortedKs.length - 1] ?? 1;
  let area = 0;
  for (let i = 1; i < points.length; i++) {
    const x1 = points[i - 1].k;
    const x2 = points[i].k;
    const y1 = points[i - 1].meanScore;
    const y2 = points[i].meanScore;
    area += (y1 + y2) / 2 * (x2 - x1);
  }
  const adaptationArea = maxK === 0 ? 0 : area / maxK;
  return { points, firstPassK: firstPassK2, adaptationArea };
}
function compareAdaptationCurves(a, b, opts = {}) {
  const conf = opts.confidence ?? 0.95;
  const resamples = opts.bootstrapResamples ?? 500;
  const rng = makeRng(opts.seed);
  const perK = [];
  for (const ap of a.points) {
    const bp = b.points.find((p) => p.k === ap.k);
    if (!bp) continue;
    const aMeans = ap.perScenario.map((s) => s.meanScore);
    const bMeans = bp.perScenario.map((s) => s.meanScore);
    const aCi = bootstrapMeanCi(aMeans, resamples, conf, rng);
    const bCi = bootstrapMeanCi(bMeans, resamples, conf, rng);
    perK.push({
      k: ap.k,
      deltaMean: ap.meanScore - bp.meanScore,
      aLow: aCi.low,
      aHigh: aCi.high,
      bLow: bCi.low,
      bHigh: bCi.high
    });
  }
  const areaDelta = a.adaptationArea - b.adaptationArea;
  const firstPassKDelta = a.firstPassK !== null && b.firstPassK !== null ? b.firstPassK - a.firstPassK : null;
  const meanDelta = perK.reduce((s, p) => s + p.deltaMean, 0) / Math.max(1, perK.length);
  let verdict;
  if (Math.abs(meanDelta) < 0.02 && Math.abs(areaDelta) < 0.02) verdict = "similar";
  else if (meanDelta > 0 && areaDelta > 0) verdict = "a_better";
  else if (meanDelta < 0 && areaDelta < 0) verdict = "b_better";
  else verdict = "similar";
  const rationale = `mean per-k delta=${meanDelta.toFixed(3)}, area delta=${areaDelta.toFixed(3)}` + (firstPassKDelta !== null ? `, first-pass-k delta=${firstPassKDelta}` : "");
  return { perK, areaDelta, firstPassKDelta, verdict, rationale };
}
function firstPassK(curve, threshold = 0.5) {
  return curve.points.find((p) => p.passRate >= threshold)?.k ?? null;
}
function makeRng(seed) {
  if (seed === void 0) return Math.random;
  let s = seed >>> 0;
  return () => {
    s = s + 1831565813 >>> 0;
    let t = s;
    t = Math.imul(t ^ t >>> 15, t | 1);
    t ^= t + Math.imul(t ^ t >>> 7, t | 61);
    return ((t ^ t >>> 14) >>> 0) / 4294967296;
  };
}
function bootstrapMeanCi(xs, resamples, confidence, rng) {
  if (xs.length < 2) return { low: xs[0] ?? 0, high: xs[0] ?? 0 };
  const samples = new Array(resamples);
  for (let b = 0; b < resamples; b++) {
    let sum = 0;
    for (let i = 0; i < xs.length; i++) sum += xs[Math.floor(rng() * xs.length)];
    samples[b] = sum / xs.length;
  }
  samples.sort((a, b) => a - b);
  const alpha = 1 - confidence;
  return {
    low: samples[Math.floor(alpha / 2 * resamples)],
    high: samples[Math.min(resamples - 1, Math.ceil((1 - alpha / 2) * resamples) - 1)]
  };
}

// src/rl/compute-curves.ts
async function runComputeCurve(opts) {
  const points = [];
  for (const budget of opts.budgets) {
    const r = await opts.runAtBudget(budget);
    points.push({
      budgetId: budget.id,
      cost: budget.cost,
      score: r.score,
      samples: r.samples,
      std: r.std,
      metrics: r.metrics
    });
  }
  const sorted = [...points].sort((a, b) => a.cost - b.cost);
  const logSlope = sorted.length >= 2 ? fitLogSlope(sorted) : null;
  const best = points.reduce((a, b) => b.score > a.score ? b : a);
  return { candidateId: opts.candidateId, points: sorted, logSlope, best };
}
async function bestOfN(opts) {
  if (opts.n <= 0) throw new ValidationError("bestOfN: n must be > 0");
  const rollouts = [];
  const scores = [];
  for (let i = 0; i < opts.n; i++) {
    const r = await opts.sample(i);
    rollouts.push(r);
    scores.push(await opts.scoreFn(r));
  }
  let bestIndex = 0;
  for (let i = 1; i < scores.length; i++) if (scores[i] > scores[bestIndex]) bestIndex = i;
  const meanScore = scores.reduce((s, x) => s + x, 0) / scores.length;
  return {
    best: rollouts[bestIndex],
    bestScore: scores[bestIndex],
    scores,
    meanScore,
    bestIndex
  };
}
async function selfConsistency(opts) {
  if (opts.n <= 0) throw new ValidationError("selfConsistency: n must be > 0");
  const rollouts = [];
  const histogram = {};
  for (let i = 0; i < opts.n; i++) {
    const r = await opts.sample(i);
    rollouts.push(r);
    const key = opts.answerKey(r);
    histogram[key] = (histogram[key] ?? 0) + 1;
  }
  let answer = "";
  let max = -1;
  for (const [k, v] of Object.entries(histogram)) {
    if (v > max) {
      max = v;
      answer = k;
    }
  }
  const representative = rollouts.find((r) => opts.answerKey(r) === answer) ?? rollouts[0];
  return {
    answer,
    agreement: max / opts.n,
    histogram,
    representative,
    rollouts
  };
}
function paretoFrontier(points) {
  const onFrontier = [];
  for (const p of points) {
    const dominated = points.some(
      (q) => q !== p && q.cost <= p.cost && q.score >= p.score && (q.cost < p.cost || q.score > p.score)
    );
    if (!dominated) onFrontier.push(p);
  }
  return onFrontier.sort((a, b) => a.cost - b.cost);
}
function fitLogSlope(points) {
  const xs = points.map((p) => Math.log(Math.max(1e-12, p.cost)));
  const ys = points.map((p) => p.score);
  const n = xs.length;
  const mx = xs.reduce((s, x) => s + x, 0) / n;
  const my = ys.reduce((s, y) => s + y, 0) / n;
  let num = 0;
  let den = 0;
  for (let i = 0; i < n; i++) {
    num += (xs[i] - mx) * (ys[i] - my);
    den += (xs[i] - mx) ** 2;
  }
  return den === 0 ? 0 : num / den;
}

// src/rl/contamination.ts
async function runContaminationProbe(input, opts = {}) {
  const fdr = opts.fdr ?? 0.05;
  const minMedianDrop = opts.minMedianDrop ?? 0.05;
  const floor = opts.scoreFloor ?? 0;
  if (!input.perturbed && !input.perturbation) {
    throw new ValidationError(
      "runContaminationProbe: must supply either `perturbed` or `perturbation`."
    );
  }
  const perturbed = input.perturbed ?? await Promise.all(input.originals.map((s) => input.perturbation.apply(s)));
  if (perturbed.length !== input.originals.length) {
    throw new ValidationError(
      `runContaminationProbe: perturbed length ${perturbed.length} \u2260 originals ${input.originals.length}`
    );
  }
  const origScores = await Promise.all(input.originals.map((s) => input.scoreFn(s)));
  const pertScores = await Promise.all(perturbed.map((s) => input.scoreFn(s)));
  const perScenario = input.originals.map((s, i) => ({
    scenarioId: input.scenarioId(s),
    originalScore: origScores[i],
    perturbedScore: pertScores[i],
    delta: pertScores[i] - origScores[i],
    qValue: NaN
  }));
  const valid = perScenario.filter((p) => p.originalScore >= floor && p.perturbedScore >= floor);
  if (valid.length < 4) {
    return {
      perScenario,
      pairedTest: { w: 0, p: 1 },
      medianDelta: 0,
      meanDelta: 0,
      contaminationSuspected: false,
      reason: `insufficient valid scenarios (n=${valid.length}, need \u2265 4)`,
      n: valid.length
    };
  }
  const origValid = valid.map((p) => p.originalScore);
  const pertValid = valid.map((p) => p.perturbedScore);
  const pairedTest = wilcoxonSignedRank(origValid, pertValid);
  const deltas = valid.map((p) => p.delta);
  const sortedDeltas = [...deltas].sort((a, b) => a - b);
  const median = sortedDeltas[Math.floor(sortedDeltas.length / 2)];
  const mean = deltas.reduce((s, d) => s + d, 0) / deltas.length;
  const pseudoP = valid.map((p) => Math.min(1, Math.max(1e-6, 1 - Math.abs(p.delta) / 1)));
  const { qValues } = benjaminiHochberg(pseudoP, fdr);
  for (let i = 0; i < valid.length; i++) {
    const v = valid[i];
    const idx = perScenario.findIndex((p) => p.scenarioId === v.scenarioId);
    if (idx >= 0) perScenario[idx].qValue = qValues[i];
  }
  const contaminationSuspected = pairedTest.p < fdr && median <= -minMedianDrop;
  const reason = contaminationSuspected ? `paired p=${pairedTest.p.toFixed(4)} < ${fdr} and median drop ${median.toFixed(4)} \u2265 ${minMedianDrop}` : pairedTest.p >= fdr ? `no significant difference (paired p=${pairedTest.p.toFixed(4)})` : `significant but small effect (median delta ${median.toFixed(4)})`;
  return {
    perScenario,
    pairedTest,
    medianDelta: median,
    meanDelta: mean,
    contaminationSuspected,
    reason,
    n: valid.length
  };
}
function renameVariables(identifiers, rename = (n, i) => `${n}_${(i % 26 + 10).toString(36)}`) {
  return {
    kind: "rename_variables",
    apply(scenario) {
      let prompt = scenario.prompt;
      identifiers.forEach((id, i) => {
        const replacement = rename(id, i);
        const re = new RegExp(`\\b${escapeRegex(id)}\\b`, "g");
        prompt = prompt.replace(re, replacement);
      });
      return { ...scenario, prompt };
    }
  };
}
function shuffleOrder(shuffleSection, seed) {
  let s = seed >>> 0;
  const rng = () => {
    s = s + 1831565813 >>> 0;
    let t = s;
    t = Math.imul(t ^ t >>> 15, t | 1);
    t ^= t + Math.imul(t ^ t >>> 7, t | 61);
    return ((t ^ t >>> 14) >>> 0) / 4294967296;
  };
  return {
    kind: "shuffle_order",
    apply(scenario) {
      const newPrompt = shuffleSection(scenario.prompt, rng);
      return { ...scenario, prompt: newPrompt };
    }
  };
}
function injectIrrelevantClause(clause, position = "prefix") {
  return {
    kind: "inject_irrelevant_clause",
    apply(scenario) {
      const prompt = position === "prefix" ? `${clause} ${scenario.prompt}` : `${scenario.prompt} ${clause}`;
      return { ...scenario, prompt };
    }
  };
}
function escapeRegex(s) {
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

// src/rl/corpus.ts
import { appendFileSync, existsSync, mkdirSync, readFileSync } from "fs";
import { dirname } from "path";

// src/rl/exporters.ts
async function toDpoRows(triples, lookups) {
  const out = [];
  for (const t of triples) {
    const [prompt, chosen, rejected] = await Promise.all([
      Promise.resolve(lookups.promptOf(t.chosenRunId)),
      Promise.resolve(lookups.completionOf(t.chosenRunId)),
      Promise.resolve(lookups.completionOf(t.rejectedRunId))
    ]);
    out.push({
      prompt,
      chosen,
      rejected,
      margin: t.marginScore,
      meta: {
        scenarioId: t.scenarioId,
        chosenVariantId: t.chosenVariantId,
        rejectedVariantId: t.rejectedVariantId,
        chosenRunId: t.chosenRunId,
        rejectedRunId: t.rejectedRunId,
        chosenModel: t.meta.chosenModel,
        rejectedModel: t.meta.rejectedModel
      }
    });
  }
  return out;
}
function toDpoJsonl(rows) {
  return rows.map((r) => JSON.stringify(r)).join("\n") + (rows.length > 0 ? "\n" : "");
}
async function toGrpoRows(runs, lookups) {
  const rewardOf2 = lookups.rewardOf ?? defaultReward;
  const grouped = /* @__PURE__ */ new Map();
  for (const r of runs) {
    const sid = r.scenarioId ?? r.experimentId;
    const arr = grouped.get(sid) ?? [];
    arr.push(r);
    grouped.set(sid, arr);
  }
  const rows = [];
  for (const [scenarioId, group] of grouped.entries()) {
    if (group.length === 0) continue;
    const prompt = await Promise.resolve(lookups.promptOf(group[0].runId));
    const completions = [];
    const rewards = [];
    const runIds = [];
    for (const r of group) {
      const reward2 = rewardOf2(r);
      if (reward2 === null) continue;
      const completion = await Promise.resolve(lookups.completionOf(r.runId));
      completions.push(completion);
      rewards.push(reward2);
      runIds.push(r.runId);
    }
    if (completions.length === 0) continue;
    rows.push({
      prompt,
      completions,
      rewards,
      runIds,
      meta: {
        scenarioId,
        n: completions.length,
        meanReward: rewards.reduce((s, x) => s + x, 0) / rewards.length
      }
    });
  }
  return rows;
}
function toGrpoJsonl(rows) {
  return rows.map((r) => JSON.stringify(r)).join("\n") + (rows.length > 0 ? "\n" : "");
}
async function toSftRows(runs, lookups) {
  const include = lookups.include ?? (() => true);
  const rows = [];
  for (const r of runs) {
    if (!include(r)) continue;
    const system = lookups.systemOf?.(r);
    const [prompt, completion] = await Promise.all([
      Promise.resolve(lookups.promptOf(r.runId)),
      Promise.resolve(lookups.completionOf(r.runId))
    ]);
    const messages = [];
    if (system) messages.push({ role: "system", content: system });
    messages.push({ role: "user", content: prompt });
    messages.push({ role: "assistant", content: completion });
    rows.push({
      messages,
      meta: {
        runId: r.runId,
        candidateId: r.candidateId,
        scenarioId: r.scenarioId,
        score: r.outcome.holdoutScore ?? r.outcome.searchScore,
        model: r.model
      }
    });
  }
  return rows;
}
function toSftJsonl(rows) {
  return rows.map((r) => JSON.stringify(r)).join("\n") + (rows.length > 0 ? "\n" : "");
}
async function toPrmRows(triples, lookups) {
  const rows = [];
  for (const t of triples) {
    const prompt = await Promise.resolve(lookups.promptOf(t.prefixRunId));
    const prefixSpanIds = lookups.prefixOf ? await Promise.resolve(lookups.prefixOf(t.prefixRunId, t.prefixStepIndex)) : [];
    const prefixStepText = [];
    for (const spanId of prefixSpanIds) {
      prefixStepText.push(await Promise.resolve(lookups.stepTextOf(t.prefixRunId, spanId)));
    }
    const chosenStep = await Promise.resolve(lookups.stepTextOf(t.prefixRunId, t.chosenSpanId));
    const rejectedStep = await Promise.resolve(
      lookups.stepTextOf(t.rejectedRunId, t.rejectedSpanId)
    );
    rows.push({
      prompt,
      prefixSpanIds,
      prefixStepText,
      chosenStep,
      rejectedStep,
      chosenReward: t.chosenReward,
      rejectedReward: t.rejectedReward,
      marginScore: t.marginScore,
      meta: {
        prefixRunId: t.prefixRunId,
        rejectedRunId: t.rejectedRunId,
        prefixStepIndex: t.prefixStepIndex
      }
    });
  }
  return rows;
}
function toPrmJsonl(rows) {
  return rows.map((r) => JSON.stringify(r)).join("\n") + (rows.length > 0 ? "\n" : "");
}
function stepRewardsToJsonl(stepRewards) {
  const rows = stepRewards.map((s) => ({
    runId: s.runId,
    spanId: s.spanId,
    stepIndex: s.stepIndex,
    reward: s.reward,
    determinism: s.determinism,
    weight: s.weight ?? 1
  }));
  return rows.map((r) => JSON.stringify(r)).join("\n") + (rows.length > 0 ? "\n" : "");
}
function defaultReward(run) {
  const v = run.outcome.holdoutScore ?? run.outcome.searchScore;
  return typeof v === "number" && Number.isFinite(v) ? v : null;
}

// src/rl/dataset.ts
function reward(r) {
  const v = r.outcome.holdoutScore ?? r.outcome.searchScore;
  return typeof v === "number" && Number.isFinite(v) ? v : null;
}
function distinct(xs) {
  return [...new Set(xs)].sort();
}
function computeRewardStats(values) {
  if (values.length === 0) return { n: 0, mean: 0, median: 0, min: 0, max: 0, std: 0 };
  const sorted = [...values].sort((a, b) => a - b);
  const n = sorted.length;
  const mean = sorted.reduce((s, x) => s + x, 0) / n;
  const mid = Math.floor(n / 2);
  const median = n % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
  const variance = sorted.reduce((s, x) => s + (x - mean) ** 2, 0) / n;
  return { n, mean, median, min: sorted[0], max: sorted[n - 1], std: Math.sqrt(variance) };
}
function computeStats(records) {
  const splits = { search: 0, dev: 0, holdout: 0 };
  let inTok = 0;
  let outTok = 0;
  let cost = 0;
  const rewards = [];
  for (const r of records) {
    splits[r.splitTag] = (splits[r.splitTag] ?? 0) + 1;
    inTok += r.tokenUsage.input;
    outTok += r.tokenUsage.output;
    cost += r.costUsd;
    const rw = reward(r);
    if (rw !== null) rewards.push(rw);
  }
  return {
    records: records.length,
    splits,
    reward: computeRewardStats(rewards),
    models: distinct(records.map((r) => r.model)),
    promptHashes: distinct(records.map((r) => r.promptHash)),
    commitShas: distinct(records.map((r) => r.commitSha)),
    totalTokens: { input: inTok, output: outTok },
    totalCostUsd: cost
  };
}
async function buildRlDataset(records, lookups, config, preferences) {
  if (records.length === 0) {
    throw new Error("buildRlDataset: no records \u2014 refusing to package an empty dataset");
  }
  const formats = config.formats ?? ["grpo", "sft"];
  const files = {};
  const rowCounts = {};
  if (formats.includes("grpo")) {
    const rows = await toGrpoRows(records, lookups);
    files["train.grpo.jsonl"] = toGrpoJsonl(rows);
    rowCounts.grpo = rows.length;
  }
  if (formats.includes("sft")) {
    const rows = await toSftRows(records, lookups);
    files["train.sft.jsonl"] = toSftJsonl(rows);
    rowCounts.sft = rows.length;
  }
  if (formats.includes("dpo")) {
    if (!preferences) {
      throw new Error("buildRlDataset: format 'dpo' requires `preferences` (triples + lookups)");
    }
    const rows = await toDpoRows(preferences.triples, preferences.lookups);
    files["train.dpo.jsonl"] = toDpoJsonl(rows);
    rowCounts.dpo = rows.length;
  }
  const manifest = {
    ...config,
    formats,
    rowCounts,
    stats: computeStats(records)
  };
  files["manifest.json"] = `${JSON.stringify(manifest, null, 2)}
`;
  files["DATASHEET.md"] = datasheetToMarkdown(manifest);
  return { manifest, files };
}
function pct(x) {
  return `${(x * 100).toFixed(1)}%`;
}
function datasheetToMarkdown(m) {
  const s = m.stats;
  const total = s.records || 1;
  const splitLines = ["search", "dev", "holdout"].map((k) => `  - \`${k}\`: ${s.splits[k]} (${pct(s.splits[k] / total)})`).join("\n");
  const deterministic = m.reward.kind === "deterministic";
  return [
    `# Dataset: ${m.name} \`v${m.version}\``,
    "",
    `**Domain:** ${m.domain} \xB7 **Created:** ${m.createdAtIso} \xB7 **License:** ${m.license}`,
    "",
    "## Reward provenance",
    `- **Kind:** ${m.reward.kind}${deterministic ? " \u2705 (decidable \u2014 not judge-noise)" : ""}`,
    `- **Source:** ${m.reward.source}`,
    `- **Description:** ${m.reward.description}`,
    "",
    "## Composition",
    `- **Records (trajectories):** ${s.records}`,
    `- **Formats:** ${m.formats.map((f) => `${f} (${m.rowCounts[f] ?? 0} rows)`).join(", ")}`,
    "- **Splits:**",
    splitLines,
    "",
    "## Reward distribution",
    `- n=${s.reward.n} \xB7 mean=${s.reward.mean.toFixed(3)} \xB7 median=${s.reward.median.toFixed(3)} \xB7 min=${s.reward.min.toFixed(3)} \xB7 max=${s.reward.max.toFixed(3)} \xB7 std=${s.reward.std.toFixed(3)}`,
    "",
    "## Provenance",
    `- **Models:** ${s.models.join(", ")}`,
    `- **Prompt/agent versions (sha256):** ${s.promptHashes.length} distinct`,
    `- **Commits:** ${s.commitShas.join(", ")}`,
    `- **Tokens:** ${s.totalTokens.input} in / ${s.totalTokens.output} out \xB7 **Cost:** $${s.totalCostUsd.toFixed(2)}`,
    "",
    "## Quality gates",
    `- Contamination probe: ${m.qualityGates?.contaminationProbe ?? "not-run"}`,
    `- Dedup: ${m.qualityGates?.dedup ? "yes" : "no"} \xB7 Verifiable-reward filter: ${m.qualityGates?.verifiableRewardFilter ? "yes" : "no"}`,
    "",
    "## Recommended uses",
    m.intendedUse,
    "",
    "## Out of scope",
    m.outOfScope,
    "",
    "## Limitations",
    m.limitations,
    "",
    "## Token rendering",
    "For RL/SFT training, tokenize with the per-model renderer (DeepSeek-V3 / Kimi-K2 / Qwen3) to preserve token identity and per-token loss masks across tool-call turns \u2014 see `renderers` (PrimeIntellect). The `messages` / `completions` here are the renderer input.",
    ""
  ].join("\n");
}

// src/rl/corpus.ts
function appendToCorpus(records, corpusPath) {
  mkdirSync(dirname(corpusPath), { recursive: true });
  const existing = existsSync(corpusPath) ? readCorpus(corpusPath) : [];
  const seen = new Set(existing.map((r) => r.runId));
  const lines = [];
  let appended = 0;
  let skipped = 0;
  for (const r of records) {
    if (seen.has(r.runId)) {
      skipped++;
      continue;
    }
    seen.add(r.runId);
    lines.push(JSON.stringify(r));
    appended++;
  }
  if (lines.length > 0) appendFileSync(corpusPath, `${lines.join("\n")}
`);
  return { appended, skipped, total: existing.length + appended };
}
function readCorpus(corpusPath) {
  if (!existsSync(corpusPath)) return [];
  const out = [];
  for (const line of readFileSync(corpusPath, "utf8").split("\n")) {
    if (line.trim()) out.push(JSON.parse(line));
  }
  return out;
}
function rewardOf(r) {
  const v = r.outcome.holdoutScore ?? r.outcome.searchScore;
  return typeof v === "number" && Number.isFinite(v) ? v : 0;
}
async function buildDatasetFromCorpus(corpusPath, config, opts = {}) {
  let records = readCorpus(corpusPath).filter(
    (r) => typeof r.prompt === "string" && typeof r.completion === "string"
  );
  if (opts.splits) records = records.filter((r) => opts.splits.includes(r.splitTag));
  if (opts.minScore != null) records = records.filter((r) => rewardOf(r) >= opts.minScore);
  const text = new Map(
    records.map((r) => [r.runId, { prompt: r.prompt, completion: r.completion }])
  );
  const lookups = {
    promptOf: (id) => text.get(id)?.prompt ?? "",
    completionOf: (id) => text.get(id)?.completion ?? ""
  };
  return buildRlDataset(records, lookups, config);
}

// src/rl/predictive-validity-researcher.ts
var PredictiveValidityResearcher = class {
  opts;
  lastReport = null;
  constructor(opts) {
    this.opts = opts;
  }
  async inspectFailures(runs) {
    const threshold = this.opts.failureThreshold ?? 0.5;
    const failures = [];
    const failingRuns = runs.filter((r) => {
      const score = r.outcome.holdoutScore ?? r.outcome.searchScore;
      return typeof score === "number" && score < threshold;
    });
    if (failingRuns.length === 0) return failures;
    const grouped = /* @__PURE__ */ new Map();
    for (const r of failingRuns) {
      const arr = grouped.get(r.candidateId) ?? [];
      arr.push(r);
      grouped.set(r.candidateId, arr);
    }
    for (const [candidateId, group] of grouped.entries()) {
      const meanScore = group.reduce((s, r) => {
        const x = r.outcome.holdoutScore ?? r.outcome.searchScore ?? 0;
        return s + x;
      }, 0) / group.length;
      failures.push({
        code: `low-score-${candidateId}`,
        description: `${candidateId} scored < ${threshold} on ${group.length} run(s) (mean ${meanScore.toFixed(3)})`,
        evidence: {
          runIds: group.slice(0, 8).map((r) => r.runId),
          samples: group.length
        }
      });
    }
    return failures;
  }
  async proposeChange(failures) {
    if (failures.length === 0) return [];
    if (this.lastReport === null) {
      return [
        {
          kind: "threshold",
          payload: { directive: "researcher.collect-more-outcomes" },
          rationale: "predictive-validity researcher has no prior report; cannot recommend rubric reweighting until at least one report exists"
        }
      ];
    }
    const decorativeThreshold = this.opts.decorativeThreshold ?? 0.4;
    const changes = [];
    for (const ranking of this.lastReport.ranked) {
      if (ranking.verdict === "load_bearing") continue;
      if (Math.abs(ranking.spearman) >= decorativeThreshold) continue;
      changes.push({
        kind: "reviewer_prompt",
        payload: {
          rubric: ranking.rubric,
          action: "down-weight",
          spearman: ranking.spearman,
          bestOutcome: ranking.bestOutcome
        },
        rationale: `predictive-validity Spearman=${ranking.spearman.toFixed(3)} vs ${ranking.bestOutcome} (decorative); recommend down-weighting`,
        expectedDelta: -Math.max(0, 0.05 - Math.abs(ranking.spearman))
      });
    }
    for (const ranking of this.lastReport.ranked.slice(0, 1)) {
      if (ranking.verdict !== "load_bearing") continue;
      changes.push({
        kind: "reviewer_prompt",
        payload: {
          rubric: ranking.rubric,
          action: "up-weight",
          spearman: ranking.spearman,
          bestOutcome: ranking.bestOutcome
        },
        rationale: `predictive-validity Spearman=${ranking.spearman.toFixed(3)} vs ${ranking.bestOutcome} (load-bearing); recommend up-weighting`,
        expectedDelta: Math.max(0, Math.abs(ranking.spearman) - 0.5) * 0.1
      });
    }
    return changes;
  }
  async applyChange(changes, baseline) {
    return {
      ...baseline,
      changes: [...baseline.changes, ...changes]
    };
  }
  async evaluateChange(plan) {
    const emptyGate = {
      promote: false,
      candidateId: plan.proposedCandidateId,
      baselineId: plan.baselineCandidateId,
      evidence: {
        productiveRuns: 0,
        medianPairedDelta: 0,
        pairedCI: { low: 0, high: 0 },
        pairedPValue: 1,
        searchScore: 0,
        holdoutScore: 0,
        overfitGap: 0,
        baselineOverfitGap: 0,
        medianCandidateCost: Number.NaN,
        medianBaselineCost: Number.NaN
      },
      reason: "predictive-validity researcher does not execute plans; the caller is expected to run the sweep and call rubricPredictiveValidity directly with the resulting RunRecord[].",
      rejectionCode: "few_runs"
    };
    return {
      plan,
      runs: [],
      gateDecision: emptyGate
    };
  }
  /**
   * Run the predictive-validity check explicitly against a fresh RunRecord
   * set. Updates the researcher's cached report so subsequent
   * `proposeChange` calls have evidence to draw from.
   */
  async runValidityCheck(runs) {
    const report = await rubricPredictiveValidity({
      runs,
      outcomes: this.opts.outcomes,
      outcomeMetrics: this.opts.outcomeMetrics,
      rubrics: this.opts.rubrics
    });
    if (this.opts.onReport) await this.opts.onReport(report);
    this.lastReport = report;
    return report;
  }
  /**
   * Force-feed a predictive-validity report into the researcher state —
   * useful when the consumer ran the report out-of-band and wants the
   * researcher's later proposals informed by it.
   */
  setReport(report) {
    this.lastReport = report;
  }
  getLastReport() {
    return this.lastReport;
  }
};

// src/rl/preferences.ts
var SPLIT_TAG_DEFAULT = "holdout";
var DEFAULT_REWARD = (run) => {
  const v = run.outcome.holdoutScore ?? run.outcome.searchScore;
  return typeof v === "number" && Number.isFinite(v) ? v : null;
};
function extractPreferences(runs, opts = {}) {
  const strategy = opts.strategy ?? "paired-by-scenario-and-seed";
  const minMargin = opts.minMargin ?? 0.05;
  const splitTag = opts.splitTag ?? SPLIT_TAG_DEFAULT;
  const rewardOf2 = opts.rewardOf ?? DEFAULT_REWARD;
  const filtered = runs.filter((r) => r.splitTag === splitTag);
  const scoredEntries = [];
  for (const run of filtered) {
    const s = rewardOf2(run);
    if (s === null) continue;
    scoredEntries.push({ run, score: s });
  }
  const pairs = [];
  let pairsBelowMargin = 0;
  let cellsSingleton = 0;
  let cellsInspected = 0;
  if (strategy === "paired-by-scenario-and-seed") {
    const groups = /* @__PURE__ */ new Map();
    for (const e of scoredEntries) {
      const sid = scenarioOf(e.run);
      const key = `${sid}::${e.run.seed}`;
      const arr = groups.get(key) ?? [];
      arr.push(e);
      groups.set(key, arr);
    }
    for (const [key, members] of groups.entries()) {
      cellsInspected++;
      if (members.length < 2) {
        cellsSingleton++;
        continue;
      }
      for (let i = 0; i < members.length; i++) {
        for (let j = i + 1; j < members.length; j++) {
          const a = members[i];
          const b = members[j];
          if (a.run.candidateId === b.run.candidateId) continue;
          const result = makePair(a, b, key.split("::")[0], minMargin);
          if (result.kind === "admit") pairs.push(result.pair);
          else pairsBelowMargin++;
        }
      }
    }
  } else if (strategy === "paired-by-scenario") {
    const byScenarioVariant = /* @__PURE__ */ new Map();
    for (const e of scoredEntries) {
      const sid = scenarioOf(e.run);
      let perScenario = byScenarioVariant.get(sid);
      if (!perScenario) {
        perScenario = /* @__PURE__ */ new Map();
        byScenarioVariant.set(sid, perScenario);
      }
      const cur = perScenario.get(e.run.candidateId);
      if (cur) {
        cur.sum += e.score;
        cur.n++;
      } else perScenario.set(e.run.candidateId, { run: e.run, sum: e.score, n: 1 });
    }
    for (const [sid, perVariant] of byScenarioVariant.entries()) {
      cellsInspected++;
      const arr = [...perVariant.entries()].map(([vid, agg]) => ({
        run: agg.run,
        score: agg.sum / agg.n,
        variantId: vid
      }));
      if (arr.length < 2) {
        cellsSingleton++;
        continue;
      }
      for (let i = 0; i < arr.length; i++) {
        for (let j = i + 1; j < arr.length; j++) {
          const result = makePair(arr[i], arr[j], sid, minMargin);
          if (result.kind === "admit") pairs.push(result.pair);
          else pairsBelowMargin++;
        }
      }
    }
  } else {
    const byScenario = /* @__PURE__ */ new Map();
    for (const e of scoredEntries) {
      const sid = scenarioOf(e.run);
      const arr = byScenario.get(sid) ?? [];
      arr.push(e);
      byScenario.set(sid, arr);
    }
    for (const [sid, arr] of byScenario.entries()) {
      cellsInspected++;
      if (arr.length < 2) {
        cellsSingleton++;
        continue;
      }
      const sorted = [...arr].sort((a, b) => a.score - b.score);
      const top = sorted[sorted.length - 1];
      const bot = sorted[0];
      if (top.run.candidateId === bot.run.candidateId) {
        cellsSingleton++;
        continue;
      }
      const result = makePair(bot, top, sid, minMargin);
      if (result.kind === "admit") pairs.push(result.pair);
      else pairsBelowMargin++;
    }
  }
  return { pairs, cellsInspected, pairsBelowMargin, cellsSingleton, strategy };
}
function toTRLFormat(triples, promptOf) {
  return triples.map((t) => ({
    prompt: promptOf(t.meta.chosenPromptHash),
    chosen: t.meta.chosenPromptHash,
    // caller substitutes the model output via the runId map
    rejected: t.meta.rejectedPromptHash
  }));
}
function toAnthropicFormat(triples) {
  return triples.map((t) => ({
    scenarioId: t.scenarioId,
    chosenRunId: t.chosenRunId,
    rejectedRunId: t.rejectedRunId,
    margin: t.marginScore
  }));
}
function makePair(a, b, scenarioId, minMargin) {
  const margin = Math.abs(a.score - b.score);
  if (margin < minMargin) return { kind: "reject" };
  const [chosen, rejected] = a.score > b.score ? [a, b] : [b, a];
  return {
    kind: "admit",
    pair: {
      scenarioId,
      chosenRunId: chosen.run.runId,
      rejectedRunId: rejected.run.runId,
      chosenVariantId: chosen.run.candidateId,
      rejectedVariantId: rejected.run.candidateId,
      marginScore: chosen.score - rejected.score,
      scores: { chosen: chosen.score, rejected: rejected.score },
      seed: chosen.run.seed === rejected.run.seed ? chosen.run.seed : void 0,
      meta: {
        chosenPromptHash: chosen.run.promptHash,
        rejectedPromptHash: rejected.run.promptHash,
        chosenConfigHash: chosen.run.configHash,
        rejectedConfigHash: rejected.run.configHash,
        chosenModel: chosen.run.model,
        rejectedModel: rejected.run.model
      }
    }
  };
}
function scenarioOf(run) {
  if (typeof run.scenarioId === "string" && run.scenarioId.length > 0) return run.scenarioId;
  const fromRaw = run.outcome.raw.scenario_id;
  if (typeof fromRaw === "number" && Number.isFinite(fromRaw)) return String(fromRaw);
  if (typeof fromRaw === "string") return fromRaw;
  return run.experimentId;
}

// src/rl/process-reward.ts
async function extractStepRewards(store, runId, opts) {
  const spans = await store.spans({ runId });
  const ordered = [...spans].sort((a, b) => a.startedAt - b.startedAt);
  const out = [];
  let idx = 0;
  for (const span of ordered) {
    if (opts.preFilter && !opts.preFilter(span)) continue;
    let scored = null;
    for (const s of opts.scorers) {
      if (!s.appliesTo.includes(span.kind)) continue;
      const r = await s.score(span);
      if (r) {
        scored = r;
        break;
      }
    }
    if (!scored) continue;
    out.push({
      spanId: span.spanId,
      runId,
      stepIndex: idx++,
      kind: span.kind,
      name: span.name,
      reward: scored.reward,
      determinism: scored.determinism,
      rationale: scored.rationale,
      weight: scored.weight
    });
  }
  return out;
}
function runwiseStepRewardSummary(stepRewards) {
  if (stepRewards.length === 0) {
    return {
      runId: "",
      totalSteps: 0,
      meanReward: 0,
      sumWeightedReward: 0,
      failureFraction: 0,
      worstStepDelta: 0,
      worstStepIndex: null
    };
  }
  const runId = stepRewards[0].runId;
  let sumW = 0;
  let sumWR = 0;
  let failures = 0;
  let worstDelta = 0;
  let worstIdx = null;
  let prev = stepRewards[0].reward;
  for (let i = 0; i < stepRewards.length; i++) {
    const s = stepRewards[i];
    const w = s.weight ?? 1;
    sumW += w;
    sumWR += w * s.reward;
    if (s.reward < 0.5) failures++;
    if (i > 0) {
      const delta = s.reward - prev;
      if (delta < worstDelta) {
        worstDelta = delta;
        worstIdx = i;
      }
      prev = s.reward;
    } else {
      prev = s.reward;
    }
  }
  return {
    runId,
    totalSteps: stepRewards.length,
    meanReward: sumW === 0 ? 0 : sumWR / sumW,
    sumWeightedReward: sumWR,
    failureFraction: failures / stepRewards.length,
    worstStepDelta: worstDelta,
    worstStepIndex: worstIdx
  };
}
function prmTrainingPairs(stepRewardsByRun, opts = {}) {
  const minMargin = opts.minMargin ?? 0.2;
  const minPrefix = opts.minPrefixLength ?? 1;
  const runs = [...stepRewardsByRun.entries()].map(([runId, steps]) => ({ runId, steps }));
  const triples = [];
  for (let i = 0; i < runs.length; i++) {
    for (let j = i + 1; j < runs.length; j++) {
      const a = runs[i];
      const b = runs[j];
      const minLen = Math.min(a.steps.length, b.steps.length);
      if (minLen < minPrefix + 1) continue;
      let divergenceIdx = -1;
      for (let k = 0; k < minLen; k++) {
        const sa = a.steps[k];
        const sb = b.steps[k];
        const structuralDivergence = sa.kind !== sb.kind || sa.name !== sb.name;
        const rewardGap = Math.abs(sa.reward - sb.reward);
        if (structuralDivergence || rewardGap >= minMargin) {
          divergenceIdx = k;
          break;
        }
      }
      if (divergenceIdx < 0) continue;
      if (divergenceIdx < minPrefix) continue;
      const aNext = a.steps[divergenceIdx];
      const bNext = b.steps[divergenceIdx];
      const margin = Math.abs(aNext.reward - bNext.reward);
      if (margin < minMargin) continue;
      const chosen = aNext.reward > bNext.reward ? aNext : bNext;
      const rejected = aNext.reward > bNext.reward ? bNext : aNext;
      const chosenRun = aNext.reward > bNext.reward ? a.runId : b.runId;
      const rejectedRun = aNext.reward > bNext.reward ? b.runId : a.runId;
      triples.push({
        prefixRunId: chosenRun,
        prefixStepIndex: divergenceIdx - 1,
        chosenSpanId: chosen.spanId,
        chosenReward: chosen.reward,
        rejectedSpanId: rejected.spanId,
        rejectedReward: rejected.reward,
        rejectedRunId: rejectedRun,
        marginScore: chosen.reward - rejected.reward
      });
    }
  }
  return triples;
}

// src/rl/rl-campaign.ts
async function runRLCampaign(opts) {
  const campaign = await runEvalCampaign(opts);
  const rewardSignals = extractVerifiableRewardsFromRecords(
    campaign.runs,
    opts.verifiableReward ?? {}
  );
  const preferences = extractPreferences(campaign.runs, {
    strategy: opts.preferences?.strategy ?? "paired-by-scenario-and-seed",
    minMargin: opts.preferences?.minMargin ?? 0.05,
    splitTag: opts.preferences?.splitTag ?? opts.splitTag ?? "holdout",
    rewardOf: opts.preferences?.rewardOf
  });
  let interimConfidence = null;
  if (opts.report?.comparator) {
    const comparator = opts.report.comparator;
    const deltaSeries = collectPairedDeltaSeries(campaign.runs, comparator);
    if (deltaSeries.some((s) => s.deltas.length > 0)) {
      interimConfidence = evaluateInterimReleaseConfidence({
        deltaSeries,
        alpha: opts.sequential?.alpha,
        bound: opts.sequential?.bound,
        rope: opts.sequential?.rope ?? opts.report?.rope
      });
    }
  }
  const rewardHacking = detectRewardHacking({
    runs: campaign.runs,
    verifiableRewardOptions: opts.verifiableReward
  });
  let predictiveValidity = null;
  if (opts.outcomeStore && opts.outcomeMetrics && opts.outcomeMetrics.length > 0) {
    predictiveValidity = await rubricPredictiveValidity({
      runs: campaign.runs,
      outcomes: opts.outcomeStore,
      outcomeMetrics: opts.outcomeMetrics
    });
  }
  const trainerRows = {};
  if (opts.trainerExport?.dpo) {
    trainerRows.dpo = await toDpoRows(preferences.pairs, opts.trainerExport.dpo);
  }
  if (opts.trainerExport?.grpo) {
    trainerRows.grpo = await toGrpoRows(campaign.runs, opts.trainerExport.grpo);
  }
  if (opts.trainerExport?.sft) {
    trainerRows.sft = await toSftRows(campaign.runs, opts.trainerExport.sft);
  }
  const summary = buildSummary({
    campaign,
    preferences,
    interimConfidence,
    rewardHacking,
    predictiveValidity
  });
  return {
    campaign,
    rewardSignals,
    preferences,
    interimConfidence,
    rewardHacking,
    predictiveValidity,
    trainerRows,
    summary,
    kind: "agent-eval-rl-campaign"
  };
}
function collectPairedDeltaSeries(runs, comparator) {
  const baseline = /* @__PURE__ */ new Map();
  for (const r of runs) {
    if (r.candidateId !== comparator) continue;
    const sid = r.scenarioId ?? r.experimentId;
    const score = r.outcome.holdoutScore ?? r.outcome.searchScore;
    if (typeof score !== "number" || !Number.isFinite(score)) continue;
    baseline.set(`${sid}::${r.seed}`, score);
  }
  const byCandidate = /* @__PURE__ */ new Map();
  for (const r of runs) {
    if (r.candidateId === comparator) continue;
    const sid = r.scenarioId ?? r.experimentId;
    const score = r.outcome.holdoutScore ?? r.outcome.searchScore;
    if (typeof score !== "number" || !Number.isFinite(score)) continue;
    const baseScore = baseline.get(`${sid}::${r.seed}`);
    if (typeof baseScore !== "number") continue;
    const arr = byCandidate.get(r.candidateId) ?? [];
    arr.push(score - baseScore);
    byCandidate.set(r.candidateId, arr);
  }
  return [...byCandidate.entries()].map(([candidateId, deltas]) => ({ candidateId, deltas }));
}
function buildSummary(args) {
  const c = args.campaign;
  const lines = [
    `${c.campaignId}: ${c.runs.length} successful runs / ${c.failedRuns.length} failed (fingerprint ${c.campaignFingerprint.slice(0, 12)}\u2026)`,
    `preferences: ${args.preferences.pairs.length} (${args.preferences.strategy}, ${args.preferences.pairsBelowMargin} below margin)`
  ];
  if (args.interimConfidence) {
    lines.push(
      `sequential verdict: ${args.interimConfidence.recommendation.decision}` + (args.interimConfidence.recommendation.candidateId ? ` ${args.interimConfidence.recommendation.candidateId}` : "")
    );
  }
  lines.push(
    `reward-hacking: ${args.rewardHacking.verdict} (${args.rewardHacking.findings.length} signals checked)`
  );
  if (args.predictiveValidity) {
    const top = args.predictiveValidity.ranked[0];
    lines.push(
      `top-rubric: ${top?.rubric ?? "none"} \u03C1=${(top?.spearman ?? 0).toFixed(2)} (${top?.verdict ?? "no data"})`
    );
  }
  return lines.join(" | ");
}

// src/rl/run-record-adapters.ts
function campaignToRunRecords(campaign, ctx) {
  const splitTag = ctx.splitTag ?? "search";
  const candidateId = ctx.candidateId ?? campaign.manifestHash;
  return campaign.cells.map((cell) => {
    const composites = Object.values(cell.judgeScores).map((s) => s.composite);
    const score = composites.length > 0 ? composites.reduce((a, b) => a + b, 0) / composites.length : 0;
    const raw = { rep: cell.rep, duration_ms: cell.durationMs };
    for (const judge of Object.values(cell.judgeScores)) {
      for (const [dim, value] of Object.entries(judge.dimensions)) {
        if (Number.isFinite(value)) raw[`dim.${dim}`] = value;
      }
    }
    if (typeof cell.generation === "number") raw.generation = cell.generation;
    const outcome = { raw };
    if (splitTag === "holdout") outcome.holdoutScore = score;
    else outcome.searchScore = score;
    return {
      runId: cell.cellId,
      experimentId: ctx.experimentId,
      candidateId,
      seed: cell.seed,
      model: ctx.model,
      promptHash: ctx.promptHash,
      configHash: ctx.configHash,
      commitSha: ctx.commitSha,
      wallMs: cell.durationMs,
      costUsd: Number.isFinite(cell.costUsd) ? cell.costUsd : ctx.defaultCostUsd ?? 0,
      tokenUsage: { input: 0, output: 0 },
      outcome,
      failureMode: cell.error ? "cell_error" : void 0,
      splitTag,
      scenarioId: cell.scenarioId
    };
  });
}
function verificationReportToRunRecord(report, ctx, opts = {}) {
  const splitTag = ctx.splitTag ?? "search";
  const runId = opts.runId ?? `run-${ctx.candidateId}-${ctx.experimentId}-${report.startedAt}`;
  const raw = {
    pass_count: report.passCount,
    fail_count: report.failCount,
    error_count: report.errorCount,
    skipped_count: report.skippedCount,
    duration_ms: report.durationMs,
    blended_score: report.blendedScore
  };
  for (const layer of report.layers) {
    if (typeof layer.score === "number") raw[`layer.${layer.layer}`] = layer.score;
    raw[`layer_${layer.layer}_pass`] = layer.status === "pass" ? 1 : 0;
    if (layer.diagnostics) {
      for (const [k, v] of Object.entries(layer.diagnostics)) {
        if (typeof v === "number" && Number.isFinite(v)) raw[`layer.${layer.layer}.${k}`] = v;
      }
    }
  }
  const firstFail = report.layers.find((l) => l.status === "fail" || l.status === "error");
  const outcome = { raw };
  if (splitTag === "holdout") outcome.holdoutScore = report.blendedScore;
  else outcome.searchScore = report.blendedScore;
  return {
    runId,
    experimentId: ctx.experimentId,
    candidateId: ctx.candidateId,
    seed: 0,
    model: ctx.model,
    promptHash: ctx.promptHash,
    configHash: ctx.configHash,
    commitSha: ctx.commitSha,
    wallMs: report.durationMs,
    costUsd: ctx.defaultCostUsd ?? 0,
    tokenUsage: { input: 0, output: 0 },
    outcome,
    failureMode: firstFail ? failureModeFromLayer(firstFail) : void 0,
    splitTag,
    scenarioId: ctx.scenarioId
  };
}
function failureModeFromLayer(layer) {
  if (layer.status === "error") return `layer_${layer.layer}_error`;
  if (layer.status === "fail") return `layer_${layer.layer}_fail`;
  if (layer.status === "timeout") return `layer_${layer.layer}_timeout`;
  return `layer_${layer.layer}_${layer.status}`;
}

// src/rl/sim-fidelity.ts
var ABSENT_CATEGORY = "(absent)";
var DEFAULT_MIN_N_PER_FEATURE = 20;
var DEFAULT_QUANTILE_BUCKETS = 4;
var REPRESENTATIVE_MIN_FIDELITY = 0.8;
var TOP_SHIFT_COUNT = 5;
var defaultBehaviorFeatures = (record) => {
  const raw = record.outcome?.raw ?? {};
  const toolErrors = finiteOrNull(raw.tool_errors);
  const turnsAborted = finiteOrNull(raw.turns_aborted);
  const completion = record.completion;
  return {
    score: finiteOrNull(record.outcome?.holdoutScore) ?? finiteOrNull(record.outcome?.searchScore),
    failure_class: record.failureClass ?? null,
    wall_ms: finiteOrNull(record.wallMs),
    output_tokens: finiteOrNull(record.tokenUsage?.output),
    turn_count: finiteOrNull(raw.turns_completed) ?? finiteOrNull(raw.assistant_messages),
    tool_errors: toolErrors,
    tool_error_recovery: toolErrorRecovery(toolErrors, turnsAborted, record.failureClass),
    completion_length: typeof completion === "string" ? completion.length : null
  };
};
function toolErrorRecovery(toolErrors, turnsAborted, failureClass) {
  if (toolErrors === null) return null;
  if (toolErrors === 0) return "no-tool-errors";
  const failed = (turnsAborted ?? 0) > 0 || failureClass !== void 0 && failureClass !== "success";
  return failed ? "unrecovered" : "recovered";
}
function finiteOrNull(value) {
  return typeof value === "number" && Number.isFinite(value) ? value : null;
}
function jsDivergence(p, q) {
  const keys = /* @__PURE__ */ new Set([...Object.keys(p), ...Object.keys(q)]);
  if (keys.size === 0) {
    throw new ValidationError("jsDivergence: both histograms are empty");
  }
  let pSum = 0;
  let qSum = 0;
  for (const key of keys) {
    const pv = p[key] ?? 0;
    const qv = q[key] ?? 0;
    if (!Number.isFinite(pv) || !Number.isFinite(qv) || pv < 0 || qv < 0) {
      throw new ValidationError(`jsDivergence: negative or non-finite count for category "${key}"`);
    }
    pSum += pv;
    qSum += qv;
  }
  if (pSum === 0 || qSum === 0) {
    throw new ValidationError("jsDivergence: a histogram with zero total mass has no distribution");
  }
  let divergence = 0;
  for (const key of keys) {
    const pp = (p[key] ?? 0) / pSum;
    const qp = (q[key] ?? 0) / qSum;
    const m = (pp + qp) / 2;
    if (pp > 0) divergence += 0.5 * pp * Math.log2(pp / m);
    if (qp > 0) divergence += 0.5 * qp * Math.log2(qp / m);
  }
  return Math.min(1, Math.max(0, divergence));
}
function quantileEdges(values, bucketCount = DEFAULT_QUANTILE_BUCKETS) {
  if (values.length === 0) {
    throw new ValidationError("quantileEdges: requires at least one value");
  }
  if (!Number.isInteger(bucketCount) || bucketCount < 2) {
    throw new ValidationError(
      `quantileEdges: bucketCount must be an integer >= 2, got ${bucketCount}`
    );
  }
  const sorted = [...values].sort((a, b) => a - b);
  const edges = [];
  for (let k = 1; k < bucketCount; k++) {
    const pos = k / bucketCount * (sorted.length - 1);
    const lo = sorted[Math.floor(pos)];
    const hi = sorted[Math.ceil(pos)];
    edges.push(lo + (pos - Math.floor(pos)) * (hi - lo));
  }
  return [...new Set(edges)];
}
function bucketLabel(value, edges) {
  let i = 0;
  while (i < edges.length && value >= edges[i]) i++;
  const lo = i === 0 ? "-inf" : String(edges[i - 1]);
  const hi = i === edges.length ? "+inf" : String(edges[i]);
  return `[${lo},${hi})`;
}
function simFidelityReport(simulated, production, opts = {}) {
  if (simulated.length === 0) {
    throw new ValidationError("simFidelityReport: simulated records are empty");
  }
  if (production.length === 0) {
    throw new ValidationError("simFidelityReport: production records are empty");
  }
  const extract = opts.features ?? defaultBehaviorFeatures;
  const minN = opts.minNPerFeature ?? DEFAULT_MIN_N_PER_FEATURE;
  const simMaps = simulated.map(extract);
  const prodMaps = production.map(extract);
  const featureNames = [];
  const seen = /* @__PURE__ */ new Set();
  for (const map of [...simMaps, ...prodMaps]) {
    for (const name of Object.keys(map)) {
      if (!seen.has(name)) {
        seen.add(name);
        featureNames.push(name);
      }
    }
  }
  const perDimension = [];
  const insufficientData = [];
  for (const feature of featureNames) {
    const simVals = simMaps.map((m) => m[feature] ?? null);
    const prodVals = prodMaps.map((m) => m[feature] ?? null);
    const nSim = simVals.filter((v) => v !== null).length;
    const nProd = prodVals.filter((v) => v !== null).length;
    if (nSim < minN || nProd < minN) {
      insufficientData.push(feature);
      continue;
    }
    const { sim, prod } = histograms(feature, simVals, prodVals);
    perDimension.push({
      feature,
      divergence: jsDivergence(sim, prod),
      topShifts: topShifts(sim, simVals.length, prod, prodVals.length),
      nSim,
      nProd
    });
  }
  if (perDimension.length === 0) {
    return { perDimension, fidelity: Number.NaN, insufficientData, verdict: "insufficient-data" };
  }
  const fidelity = 1 - perDimension.reduce((sum, d) => sum + d.divergence, 0) / perDimension.length;
  return {
    perDimension,
    fidelity,
    insufficientData,
    verdict: fidelity >= REPRESENTATIVE_MIN_FIDELITY ? "representative" : "skewed"
  };
}
function histograms(feature, simVals, prodVals) {
  const kinds = /* @__PURE__ */ new Set();
  for (const v of [...simVals, ...prodVals]) {
    if (v !== null) kinds.add(typeof v);
  }
  if (kinds.size > 1) {
    throw new ValidationError(
      `simFidelityReport: feature "${feature}" mixes string and number values \u2014 an extractor must return one kind per feature`
    );
  }
  let toCategory;
  if (kinds.has("number")) {
    const union = [];
    for (const v of [...simVals, ...prodVals]) {
      if (v !== null) union.push(v);
    }
    const edges = quantileEdges(union);
    toCategory = (v) => bucketLabel(v, edges);
  } else {
    toCategory = (v) => v;
  }
  const count = (vals) => {
    const hist = {};
    for (const v of vals) {
      const key = v === null ? ABSENT_CATEGORY : toCategory(v);
      hist[key] = (hist[key] ?? 0) + 1;
    }
    return hist;
  };
  return { sim: count(simVals), prod: count(prodVals) };
}
function topShifts(sim, simTotal, prod, prodTotal) {
  const keys = [.../* @__PURE__ */ new Set([...Object.keys(sim), ...Object.keys(prod)])];
  const shifts = keys.map((value) => ({
    value,
    pSim: (sim[value] ?? 0) / simTotal,
    pProd: (prod[value] ?? 0) / prodTotal
  }));
  shifts.sort((a, b) => {
    const delta = Math.abs(b.pSim - b.pProd) - Math.abs(a.pSim - a.pProd);
    return delta !== 0 ? delta : a.value.localeCompare(b.value);
  });
  return shifts.slice(0, TOP_SHIFT_COUNT);
}
function easyModeCheck(simulated, production, opts = {}) {
  if (simulated.length === 0) {
    throw new ValidationError("easyModeCheck: simulated records are empty");
  }
  if (production.length === 0) {
    throw new ValidationError("easyModeCheck: production records are empty");
  }
  const threshold = opts.passThreshold ?? 0.5;
  const tolerance = opts.inflationTolerance ?? 0.1;
  const passRate = (records, side) => {
    let passes = 0;
    for (const r of records) {
      const score = finiteOrNull(r.outcome?.holdoutScore) ?? finiteOrNull(r.outcome?.searchScore);
      if (score === null) {
        throw new ValidationError(
          `easyModeCheck: ${side} run "${r.runId}" carries neither holdoutScore nor searchScore`
        );
      }
      if (score >= threshold) passes++;
    }
    return passes / records.length;
  };
  const simPassRate = passRate(simulated, "simulated");
  const prodPassRate = passRate(production, "production");
  const gap = simPassRate - prodPassRate;
  return { simPassRate, prodPassRate, gap, inflated: gap > tolerance };
}

// src/rl/tournament.ts
function fitBradleyTerry(outcomes, opts = {}) {
  const tol = opts.tolerance ?? 1e-6;
  const maxIter = opts.maxIterations ?? 256;
  const smoothing = opts.smoothing ?? 0.1;
  const candidates = /* @__PURE__ */ new Set();
  for (const o of outcomes) {
    candidates.add(o.winner);
    candidates.add(o.loser);
  }
  const ids = [...candidates].sort();
  const idx = new Map(ids.map((id, i) => [id, i]));
  const n = ids.length;
  if (n === 0) return { ratings: [], iterations: 0, finalDelta: 0, converged: true };
  if (n === 1) {
    return {
      ratings: [{ candidateId: ids[0], strength: 1, logStrength: 0, n: 0, wins: 0 }],
      iterations: 0,
      finalDelta: 0,
      converged: true
    };
  }
  const W = Array.from({ length: n }, () => new Array(n).fill(0));
  const N = Array.from({ length: n }, () => new Array(n).fill(0));
  for (const o of outcomes) {
    const i = idx.get(o.winner);
    const j = idx.get(o.loser);
    const w = o.weight ?? 1;
    if (o.draw) {
      W[i][j] += 0.5 * w;
      W[j][i] += 0.5 * w;
    } else {
      W[i][j] += w;
    }
    N[i][j] += w;
    N[j][i] += w;
  }
  const winsTotal = new Array(n).fill(0);
  for (let i = 0; i < n; i++) {
    for (let j = 0; j < n; j++) winsTotal[i] += W[i][j];
    winsTotal[i] += smoothing;
  }
  const compsTotal = new Array(n).fill(0);
  for (let i = 0; i < n; i++) {
    for (let j = 0; j < n; j++) compsTotal[i] += N[i][j];
  }
  let theta = new Array(n).fill(1);
  let iter = 0;
  let delta = Infinity;
  for (; iter < maxIter; iter++) {
    const newTheta = new Array(n);
    for (let i = 0; i < n; i++) {
      let denom = 0;
      for (let j = 0; j < n; j++) {
        if (j === i) continue;
        if (N[i][j] === 0) continue;
        denom += N[i][j] / (theta[i] + theta[j]);
      }
      newTheta[i] = denom === 0 ? theta[i] : winsTotal[i] / denom;
    }
    let logSum = 0;
    for (let i = 0; i < n; i++) logSum += Math.log(Math.max(1e-300, newTheta[i]));
    const norm = Math.exp(logSum / n);
    for (let i = 0; i < n; i++) newTheta[i] = newTheta[i] / norm;
    delta = 0;
    for (let i = 0; i < n; i++) {
      const d = Math.abs(newTheta[i] - theta[i]) / Math.max(1e-12, theta[i]);
      if (d > delta) delta = d;
    }
    theta = newTheta;
    if (delta < tol) break;
  }
  const minLog = Math.min(...theta.map((t) => Math.log(Math.max(1e-300, t))));
  const ratings = ids.map((id, i) => ({
    candidateId: id,
    strength: theta[i],
    logStrength: Math.log(Math.max(1e-300, theta[i])) - minLog,
    n: compsTotal[i],
    wins: winsTotal[i] - smoothing
  }));
  return {
    ratings: ratings.sort((a, b) => b.strength - a.strength),
    iterations: iter,
    finalDelta: delta,
    converged: delta < tol
  };
}
function applyEloUpdate(ratings, outcome, opts = {}) {
  const defaultRating = opts.defaultRating ?? 1500;
  const k = opts.kFactor ?? 32;
  const rW = ratings.get(outcome.winner) ?? defaultRating;
  const rL = ratings.get(outcome.loser) ?? defaultRating;
  const expectedW = 1 / (1 + 10 ** ((rL - rW) / 400));
  const scoreW = outcome.draw ? 0.5 : 1;
  const scoreL = outcome.draw ? 0.5 : 0;
  const w = outcome.weight ?? 1;
  const winnerDelta = k * w * (scoreW - expectedW);
  const loserDelta = k * w * (scoreL - (1 - expectedW));
  ratings.set(outcome.winner, rW + winnerDelta);
  ratings.set(outcome.loser, rL + loserDelta);
  return { winnerDelta, loserDelta };
}
function buildPairwiseFromCampaign(input) {
  const drawMargin = input.drawMargin ?? 0;
  const byKey = /* @__PURE__ */ new Map();
  for (const r of input.runs) {
    const arr = byKey.get(r.matchKey) ?? [];
    arr.push({ candidateId: r.candidateId, score: r.score });
    byKey.set(r.matchKey, arr);
  }
  const outcomes = [];
  for (const arr of byKey.values()) {
    for (let i = 0; i < arr.length; i++) {
      for (let j = i + 1; j < arr.length; j++) {
        const a = arr[i];
        const b = arr[j];
        if (a.candidateId === b.candidateId) continue;
        const margin = Math.abs(a.score - b.score);
        if (margin <= drawMargin) {
          outcomes.push({ winner: a.candidateId, loser: b.candidateId, draw: true, weight: 1 });
        } else {
          const [winner, loser] = a.score > b.score ? [a, b] : [b, a];
          outcomes.push({ winner: winner.candidateId, loser: loser.candidateId, weight: margin });
        }
      }
    }
  }
  return outcomes;
}
export {
  ABSENT_CATEGORY,
  DEFAULT_MIN_N_PER_FEATURE,
  DEFAULT_QUANTILE_BUCKETS,
  FileSystemOutcomeStore,
  InMemoryOutcomeStore,
  PredictiveValidityResearcher,
  REPRESENTATIVE_MIN_FIDELITY,
  appendToCorpus,
  applyEloUpdate,
  bestOfN,
  bucketLabel,
  buildDatasetFromCorpus,
  buildPairwiseFromCampaign,
  buildRlDataset,
  campaignToRunRecords,
  compareAdaptationCurves,
  datasheetToMarkdown,
  defaultBehaviorFeatures,
  detectRewardHacking,
  doublyRobust,
  easyModeCheck,
  extractPreferences,
  extractStepRewards,
  extractVerifiableReward,
  extractVerifiableRewardsFromRecords,
  filterDeterministicallyRewarded,
  firstPassK,
  fitBradleyTerry,
  injectIrrelevantClause,
  inverseProbabilityWeighting,
  jsDivergence,
  observationsFromRunRecords,
  offPolicyEstimateAll,
  paretoFrontier,
  prmTrainingPairs,
  quantileEdges,
  readCorpus,
  renameVariables,
  runAdaptationCurve,
  runComputeCurve,
  runContaminationProbe,
  runEvalCampaign,
  runRLCampaign,
  runwiseStepRewardSummary,
  selfConsistency,
  selfNormalizedImportanceWeighting,
  shuffleOrder,
  simFidelityReport,
  stepRewardsToJsonl,
  thompsonCurriculum,
  toAnthropicFormat,
  toDpoJsonl,
  toDpoRows,
  toGrpoJsonl,
  toGrpoRows,
  toPrmJsonl,
  toPrmRows,
  toSftJsonl,
  toSftRows,
  toTRLFormat,
  varianceBasedCurriculum,
  verificationReportToRunRecord
};
//# sourceMappingURL=rl.js.map