import {
  buildEvidenceVector,
  composeGate,
  defaultProductionGate,
  detectScale,
  dimensionRegressions,
  evolutionaryProposer,
  heldoutSignificance,
  pairHoldout,
  paretoPolicy,
  paretoSignificanceGate,
  runEval
} from "../chunk-KKWJD5E6.js";
import {
  agentProfileHash,
  agentProfileId,
  agentProfileModelId,
  extractProducedState,
  verifyCompletion
} from "../chunk-HMA63UEO.js";
import {
  buildLoopProvenanceRecord,
  campaignBreakdown,
  campaignMeanComposite,
  countSentenceEdits,
  defaultRenderDiff,
  emitLoopProvenance,
  extractH2Sections,
  gepaProposer,
  heldOutGate,
  isProposedCandidate,
  labelTrustRank,
  loopProvenanceSpans,
  openAutoPr,
  provenanceRecordPath,
  provenanceSpansPath,
  renderAnalystEvidence,
  runImprovementLoop,
  runOptimization,
  surfaceContentHash,
  surfaceHash
} from "../chunk-7QTQKIDD.js";
import {
  assertRealBackend,
  fsCampaignStorage,
  inMemoryCampaignStorage,
  runCampaign,
  summarizeBackendIntegrity
} from "../chunk-2T4EZACH.js";
import {
  estimateCost,
  isModelPriced
} from "../chunk-VI2UW6B6.js";
import {
  AnalystRegistry,
  DEFAULT_TRACE_ANALYST_KINDS,
  createTraceAnalystKind
} from "../chunk-S4SYLDFX.js";
import "../chunk-5HRORJQY.js";
import {
  callLlm
} from "../chunk-CWNP4DV4.js";
import "../chunk-45EEMHTC.js";
import {
  eProcess,
  mulberry32,
  pairedBootstrap
} from "../chunk-56GC6VYO.js";
import {
  analyzeTraces
} from "../chunk-IZCEK2HR.js";
import "../chunk-GGE4NNQT.js";
import {
  OtlpFileTraceStore
} from "../chunk-YBIGNSCZ.js";
import "../chunk-PC4UYEBM.js";
import {
  validateRunRecord
} from "../chunk-LO6IOIJ2.js";
import {
  canonicalize
} from "../chunk-VSMTAMNK.js";
import {
  AgentEvalError
} from "../chunk-3BFEG2F6.js";
import "../chunk-PZ5AY32C.js";

// src/campaign/analyst-surface.ts
function surfaceToText(surface) {
  if (typeof surface === "string") return surface;
  throw new Error(
    `buildAnalystSurfaceDispatch: the analyst surface must be a string actorDescription, got a ${surface.kind}-tier surface (${surface.worktreeRef}). The analyst prompt is prompt-tier.`
  );
}
function buildAnalystSurfaceDispatch(opts) {
  const analyze = opts.analyze ?? analyzeTraces;
  return async (surface, scenario, _ctx) => {
    const actorDescription = surfaceToText(surface);
    const res = await analyze(
      { question: scenario.question },
      { ...opts.analystOptions, actorDescription, source: scenario.source }
    );
    return {
      answer: res.answer,
      findings: res.findings,
      actorPromptVersion: res.actorPromptVersion
    };
  };
}
function failureModeRecallJudge(opts = {}) {
  const recallWeight = opts.recallWeight ?? 0.5;
  return {
    name: "failure-mode-recall",
    dimensions: [
      { key: "recall", description: "fraction of ground-truth failure modes the analyst surfaced" },
      {
        key: "precision",
        description: "1 \u2212 share of findings that named a failure/tool/error absent from this corpus"
      }
    ],
    appliesTo: (s) => s.kind === "analyst-surface",
    score({ artifact, scenario }) {
      const modes = scenario.expectedFailureModes;
      if (modes.length === 0) {
        throw new Error(
          `failureModeRecallJudge: scenario '${scenario.id}' has no expectedFailureModes \u2014 refusing to score (a vacuous 1.0 would corrupt the comparison)`
        );
      }
      const hay = artifact.findings.join("\n").toLowerCase();
      const matched = modes.filter((m) => m.cues.some((c) => hay.includes(c.toLowerCase())));
      const recall = matched.length / modes.length;
      const forbidden = (scenario.forbiddenCues ?? []).map((c) => c.toLowerCase());
      let precision = 1;
      let hallucinated = 0;
      if (forbidden.length > 0) {
        const denom = Math.max(1, artifact.findings.length);
        hallucinated = artifact.findings.filter(
          (f) => forbidden.some((c) => f.toLowerCase().includes(c))
        ).length;
        precision = 1 - hallucinated / denom;
      }
      const composite = forbidden.length > 0 ? recallWeight * recall + (1 - recallWeight) * precision : recall;
      const missed = modes.filter((m) => !matched.includes(m)).map((m) => m.id);
      const notes = `matched ${matched.length}/${modes.length} failure modes` + (missed.length ? `; missed [${missed.join(", ")}]` : "") + (hallucinated ? `; ${hallucinated} out-of-corpus finding(s)` : "");
      return { dimensions: { recall, precision }, composite, notes };
    }
  };
}

// src/campaign/gates/sequential.ts
import { createHash } from "crypto";
function verifyManifestSync(m) {
  if (m.algo !== void 0 && m.algo !== "sha256-content") {
    throw new Error(`sequentialPairedGate: unrecognized manifest hash algo '${m.algo}'`);
  }
  const { contentHash, algo: _algo, ...rest } = m;
  void _algo;
  const bytes = JSON.stringify(canonicalize(rest));
  const hash = createHash("sha256").update(bytes, "utf8").digest("hex");
  return hash === contentHash;
}
function resolveConfig(opts) {
  const m = opts.preRegistration;
  let alpha;
  let maxN;
  let direction;
  let minEffect;
  if (m) {
    if (!verifyManifestSync(m)) {
      throw new Error(
        `sequentialPairedGate: pre-registration manifest '${m.id}' content hash mismatch (tampered)`
      );
    }
    if (opts.alpha !== void 0 && opts.alpha !== m.alpha) {
      throw new Error(
        `sequentialPairedGate: alpha ${opts.alpha} conflicts with pre-registered alpha ${m.alpha} \u2014 the registered statistic is the only one anytime validity covers`
      );
    }
    if (opts.maxN !== void 0 && opts.maxN !== m.preRegisteredN) {
      throw new Error(
        `sequentialPairedGate: maxN ${opts.maxN} conflicts with pre-registered N ${m.preRegisteredN}`
      );
    }
    alpha = m.alpha;
    maxN = m.preRegisteredN;
    direction = m.direction;
    minEffect = m.minEffect;
  } else {
    alpha = opts.alpha ?? 0.05;
    if (opts.maxN === void 0) {
      throw new Error(
        "sequentialPairedGate: maxN is required (or bind a preRegistration manifest whose preRegisteredN is the budget) \u2014 an unbounded stream has no pre-registered budget"
      );
    }
    maxN = opts.maxN;
    direction = "increase";
    minEffect = 0;
  }
  const scale = opts.scale ?? 1;
  if (!Number.isFinite(scale) || scale <= 0) {
    throw new Error(`sequentialPairedGate: scale must be > 0, got ${scale}`);
  }
  if (!Number.isInteger(maxN) || maxN < 1) {
    throw new Error(`sequentialPairedGate: maxN must be a positive integer, got ${maxN}`);
  }
  const minN = opts.minN ?? 5;
  if (!Number.isInteger(minN) || minN < 1 || minN > maxN) {
    throw new Error(
      `sequentialPairedGate: minN must be an integer in [1, maxN=${maxN}], got ${minN}`
    );
  }
  if (!Number.isFinite(minEffect) || minEffect < 0 || minEffect >= scale) {
    throw new Error(
      `sequentialPairedGate: minEffect must be in [0, scale=${scale}), got ${minEffect}`
    );
  }
  const nullMean = 0.5 + minEffect / (2 * scale);
  return {
    alpha,
    minN,
    maxN,
    maxBet: opts.maxBet ?? 0.5,
    scale,
    shuffleSeed: opts.shuffleSeed ?? 1337,
    direction,
    nullMean,
    minEffect
  };
}
function makeStream(cfg) {
  const proc = eProcess({ alpha: cfg.alpha, maxBet: cfg.maxBet, nullMean: cfg.nullMean });
  const threshold = 1 / cfg.alpha;
  let terminal;
  return {
    observe(delta) {
      if (terminal === "undecided-at-maxN") {
        throw new Error(
          `sequentialPairedGate: pre-registered maxN=${cfg.maxN} exhausted \u2014 extending the stream after seeing the result reopens optional stopping; start a NEW pre-registered test`
        );
      }
      if (!Number.isFinite(delta) || Math.abs(delta) > cfg.scale) {
        throw new Error(
          `sequentialPairedGate: delta ${delta} outside \xB1scale=${cfg.scale} \u2014 pass the judge's native scale explicitly (detectScale helps pick 1 vs 100)`
        );
      }
      const d = cfg.direction === "decrease" ? -delta : delta;
      const step = proc.update((d / cfg.scale + 1) / 2);
      if (terminal === void 0 && step.n >= cfg.minN && step.wealth >= threshold) {
        terminal = "promote";
      }
      if (terminal === "promote") {
        return {
          decision: "promote",
          eValue: step.wealth,
          n: step.n,
          reason: `e-value ${step.wealth.toFixed(2)} \u2265 1/\u03B1=${threshold.toFixed(2)} at n=${step.n} (minN=${cfg.minN}): the paired improvement exceeds ${cfg.minEffect} at anytime-valid level \u03B1=${cfg.alpha}`
        };
      }
      if (step.n >= cfg.maxN) {
        terminal = "undecided-at-maxN";
        return {
          decision: "undecided-at-maxN",
          eValue: step.wealth,
          n: step.n,
          reason: `undecided at pre-registered maxN=${cfg.maxN} (e-value ${step.wealth.toFixed(2)} < 1/\u03B1=${threshold.toFixed(2)}). This is NOT evidence of no effect \u2014 the effect may be real but smaller than this budget can detect; re-register with a larger N to test that`
        };
      }
      return {
        decision: "continue",
        eValue: step.wealth,
        n: step.n,
        reason: `e-value ${step.wealth.toFixed(2)} < 1/\u03B1=${threshold.toFixed(2)} at n=${step.n}/${cfg.maxN} \u2014 keep observing`
      };
    },
    state() {
      return { ...proc.state(), decision: terminal ?? "continue" };
    }
  };
}
function seededShuffle(items, seed) {
  const rng = mulberry32(seed);
  for (let i = items.length - 1; i > 0; i--) {
    const j = Math.floor(rng() * (i + 1));
    const tmp = items[i];
    items[i] = items[j];
    items[j] = tmp;
  }
  return items;
}
function sequentialPairedGate(options) {
  const cfg = resolveConfig(options);
  const name = options.name ?? "sequentialPairedGate";
  const manifest = options.preRegistration;
  const observeStream = makeStream(cfg);
  return {
    name,
    observe(delta) {
      return observeStream.observe(delta);
    },
    state() {
      return observeStream.state();
    },
    async decide(ctx) {
      if (!ctx.baselineJudgeScores) {
        throw new Error(
          `${name}: ctx.baselineJudgeScores is required \u2014 falling back to the candidate's own scores would compare the candidate against itself (delta 0, silent no-op)`
        );
      }
      const scenarioIds = new Set(ctx.scenarios.map((s) => s.id));
      const paired = pairHoldout(
        ctx.judgeScores,
        ctx.baselineJudgeScores,
        scenarioIds,
        (s) => s.composite
      );
      const deltas = paired.after.map((a, i) => a - paired.before[i]);
      seededShuffle(deltas, cfg.shuffleSeed);
      const stream = makeStream(cfg);
      let last;
      for (const d of deltas) {
        last = stream.observe(d);
        if (last.decision !== "continue") break;
      }
      const detail = {
        ...stream.state(),
        minN: cfg.minN,
        maxN: cfg.maxN,
        scale: cfg.scale,
        shuffleSeed: cfg.shuffleSeed,
        direction: cfg.direction,
        minEffect: cfg.minEffect,
        pairedN: deltas.length,
        preRegisteredId: manifest?.id,
        metric: manifest?.metric
      };
      const meanDelta = deltas.length === 0 ? void 0 : deltas.reduce((s, d) => s + d, 0) / deltas.length;
      if (last === void 0) {
        return {
          decision: "need_more_work",
          reasons: [`${name}: no paired holdout observations \u2014 nothing to test`],
          contributingGates: [{ name, passed: false, detail }]
        };
      }
      const decision = last.decision === "promote" ? "ship" : last.decision === "continue" ? "need_more_work" : "hold";
      return {
        decision,
        reasons: [`${name}: ${last.reason}`],
        contributingGates: [{ name, passed: decision === "ship", detail }],
        delta: meanDelta
      };
    }
  };
}
function sequentialDecide(options = {}) {
  const alpha = options.alpha ?? 0.05;
  const minN = options.minN ?? 5;
  const scale = options.scale ?? 1;
  if (!Number.isFinite(scale) || scale <= 0) {
    throw new Error(`sequentialDecide: scale must be > 0, got ${scale}`);
  }
  const proc = eProcess({ alpha, maxBet: options.maxBet ?? 0.5, nullMean: 0.5 });
  const threshold = 1 / alpha;
  let processedGenerations = 0;
  let reference;
  let stopped;
  const topCandidate = (record) => {
    if (record.candidates.length === 0) {
      throw new Error(
        `sequentialDecide: generation ${record.generationIndex} has no candidates \u2014 cannot extract a top candidate`
      );
    }
    return record.candidates.reduce((best, c) => c.composite > best.composite ? c : best);
  };
  const decide = ({ history }) => {
    if (stopped) return stopped;
    if (history.length === 0) return { stop: false };
    if (reference === void 0) {
      reference = new Map(
        topCandidate(history[0]).scenarios.map((s) => [s.scenarioId, s.composite])
      );
    }
    for (let g = Math.max(1, processedGenerations); g < history.length; g++) {
      const top = topCandidate(history[g]);
      const byScenario = new Map(top.scenarios.map((s) => [s.scenarioId, s.composite]));
      for (const [scenarioId, refComposite] of reference) {
        const candComposite = byScenario.get(scenarioId);
        if (candComposite === void 0) {
          throw new Error(
            `sequentialDecide: generation ${history[g].generationIndex} top candidate is missing scenario '${scenarioId}' \u2014 generations must score the same scenario set to pair`
          );
        }
        const delta = candComposite - refComposite;
        if (!Number.isFinite(delta) || Math.abs(delta) > scale) {
          throw new Error(
            `sequentialDecide: paired delta ${delta} outside \xB1scale=${scale} on scenario '${scenarioId}' \u2014 pass the composite scale explicitly`
          );
        }
        const step = proc.update((delta / scale + 1) / 2);
        if (step.n >= minN && step.wealth >= threshold) {
          stopped = {
            stop: true,
            reason: `sequential e-process decided at generation ${history[g].generationIndex}: e-value ${step.wealth.toFixed(2)} \u2265 1/\u03B1=${threshold.toFixed(2)} after n=${step.n} paired deltas vs the generation-0 incumbent \u2014 the improvement is real at \u03B1=${alpha}; stop exploring and promote via the gate`
          };
          processedGenerations = history.length;
          return stopped;
        }
      }
    }
    processedGenerations = history.length;
    return { stop: false };
  };
  decide.state = () => proc.state();
  return decide;
}

// src/campaign/labeled-store/fs-adapter.ts
import { createHash as createHash2 } from "crypto";
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
import { join } from "path";
var LabeledScenarioStoreError = class extends Error {
  constructor(code, message) {
    super(message);
    this.code = code;
    this.name = "LabeledScenarioStoreError";
  }
  code;
};
var FsLabeledScenarioStore = class {
  constructor(options) {
    this.options = options;
    if (!existsSync(options.root)) mkdirSync(options.root, { recursive: true });
    this.now = options.now ?? Date.now;
  }
  options;
  now;
  rateLimits = /* @__PURE__ */ new Map();
  async observe(write) {
    this.assertProvenance(write);
    this.assertRateLimit(write);
    const record = this.toRecord(write);
    const path = this.pathForSource(write.source);
    const line = `${JSON.stringify(record)}
`;
    appendLine(path, line);
  }
  async sample(args) {
    if (!args.split) {
      throw new LabeledScenarioStoreError(
        "split_required",
        "sample() requires an explicit `split` (train | test) \u2014 substrate refuses ambiguous reads"
      );
    }
    if (!args.capturedBefore) {
      throw new LabeledScenarioStoreError(
        "capturedBefore_required",
        "sample() requires an explicit `capturedBefore` timestamp for temporal-split discipline"
      );
    }
    const all = [];
    for (const source of ALL_SOURCES) {
      if (args.split === "train" && source === "production-trace") {
        const explicit = sourceFilterContains(args.filter?.source, "production-trace");
        if (!explicit) continue;
      }
      const path = this.pathForSource(source);
      if (!existsSync(path)) continue;
      const lines = readFileSync(path, "utf8").split("\n").filter(Boolean);
      for (const line of lines) {
        let record;
        try {
          record = JSON.parse(line);
        } catch {
          continue;
        }
        if (!matchesFilter(record, args, source)) continue;
        all.push(record);
      }
    }
    all.sort((a, b) => {
      if (a.capturedAt !== b.capturedAt) return a.capturedAt.localeCompare(b.capturedAt);
      return a.recordHash.localeCompare(b.recordHash);
    });
    return all.slice(0, args.count);
  }
  async size() {
    const bySource = {};
    const byTrust = {
      unverified: 0,
      "verified-signal": 0,
      "human-rated": 0
    };
    let total = 0;
    for (const source of ALL_SOURCES) {
      const path = this.pathForSource(source);
      if (!existsSync(path)) {
        bySource[source] = 0;
        continue;
      }
      const lines = readFileSync(path, "utf8").split("\n").filter(Boolean);
      bySource[source] = lines.length;
      total += lines.length;
      for (const line of lines) {
        let trust = "unverified";
        try {
          trust = JSON.parse(line).labelTrust ?? "unverified";
        } catch {
        }
        byTrust[trust] += 1;
      }
    }
    return { train: total, test: total, bySource, byTrust };
  }
  assertProvenance(write) {
    if (!write.source) {
      throw new LabeledScenarioStoreError(
        "missing_source",
        "LabeledScenarioWrite requires `source`"
      );
    }
    if (!write.sourceVersionHash || write.sourceVersionHash.length === 0) {
      throw new LabeledScenarioStoreError(
        "missing_source_version",
        "LabeledScenarioWrite requires `sourceVersionHash` (git sha or substrate version)"
      );
    }
    if (!write.capturedAt) {
      throw new LabeledScenarioStoreError(
        "missing_captured_at",
        "LabeledScenarioWrite requires `capturedAt` ISO timestamp"
      );
    }
    if (!write.redactionStatus) {
      throw new LabeledScenarioStoreError(
        "missing_redaction_status",
        "LabeledScenarioWrite requires explicit `redactionStatus` \u2014 raw / redacted-pii / redacted-secrets / fully-redacted"
      );
    }
    if (!ALL_SOURCES.includes(write.source)) {
      throw new LabeledScenarioStoreError(
        "unknown_source",
        `LabeledScenarioWrite.source must be one of: ${ALL_SOURCES.join(", ")}`
      );
    }
  }
  assertRateLimit(write) {
    const cap = this.options.maxWritesPerMinutePerBucket;
    if (!cap || !write.rateLimitBucket) return;
    const now = this.now();
    const windowMs = 6e4;
    let state = this.rateLimits.get(write.rateLimitBucket);
    if (!state || now - state.windowStartMs >= windowMs) {
      state = { bucket: write.rateLimitBucket, windowStartMs: now, count: 0 };
      this.rateLimits.set(write.rateLimitBucket, state);
    }
    if (state.count >= cap) {
      throw new LabeledScenarioStoreError(
        "rate_limit_exceeded",
        `LabeledScenarioStore: bucket ${write.rateLimitBucket} exceeded ${cap} writes/min`
      );
    }
    state.count += 1;
  }
  toRecord(write) {
    const recordHash = sha256(
      JSON.stringify({
        id: write.scenario.id,
        src: write.source,
        at: write.capturedAt,
        ver: write.sourceVersionHash
      })
    );
    return {
      ...write,
      recordHash,
      split: "train"
    };
  }
  pathForSource(source) {
    return join(this.options.root, `${source}.jsonl`);
  }
};
var ALL_SOURCES = [
  "production-trace",
  "eval-run",
  "manual",
  "red-team",
  "synthetic"
];
function sourceFilterContains(filter, needle) {
  if (!filter) return false;
  if (Array.isArray(filter)) return filter.includes(needle);
  return filter === needle;
}
function matchesFilter(record, args, source) {
  if (args.split === "train" && record.capturedAt >= args.capturedBefore) return false;
  if (args.split === "test" && record.capturedAt < args.capturedBefore) return false;
  const f = args.filter;
  if (!f) return true;
  if (f.kind && record.scenario.kind !== f.kind) return false;
  if (f.source) {
    const sources = Array.isArray(f.source) ? f.source : [f.source];
    if (!sources.includes(source)) return false;
  }
  if (f.minComposite !== void 0 || f.maxComposite !== void 0) {
    const composites = Object.values(record.judgeScores).map((s) => s.composite);
    const max = composites.length === 0 ? 0 : Math.max(...composites);
    if (f.minComposite !== void 0 && max < f.minComposite) return false;
    if (f.maxComposite !== void 0 && max > f.maxComposite) return false;
  }
  if (f.minTrust !== void 0 && labelTrustRank(record.labelTrust) < labelTrustRank(f.minTrust)) {
    return false;
  }
  return true;
}
function sha256(input) {
  return createHash2("sha256").update(input).digest("hex").slice(0, 16);
}
function appendLine(path, line) {
  if (existsSync(path)) {
    const existing = readFileSync(path, "utf8");
    writeFileSync(path, existing + line);
  } else {
    writeFileSync(path, line);
  }
}

// src/campaign/proposers/fapo.ts
var FAPO_LEVELS = ["prompt", "parameter", "structural"];
var MAX_FINDING_DEPTH = 16;
var UNSAFE_JSON_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
function fapoProposer(opts) {
  const proposers = levelProposers(opts);
  const allowed = allowedLevels(opts, proposers);
  const plateauWindow = opts.plateauWindow ?? 3;
  const minDistinctStrategies = opts.minDistinctStrategies ?? 3;
  const minImprovement = opts.minImprovement ?? 0;
  const proposalsPerCycle = opts.proposalsPerCycle ?? 1;
  if (allowed.length === 0) {
    throw new Error("fapoProposer: at least one allowed level must have a proposer");
  }
  if (plateauWindow < 1) throw new Error("fapoProposer: plateauWindow must be >= 1");
  if (minDistinctStrategies < 1) {
    throw new Error("fapoProposer: minDistinctStrategies must be >= 1");
  }
  if (proposalsPerCycle < 1) throw new Error("fapoProposer: proposalsPerCycle must be >= 1");
  return {
    kind: "fapo",
    async propose(ctx) {
      const state = policyState(ctx.history, ctx.findings, {
        plateauWindow,
        minDistinctStrategies,
        minImprovement
      });
      const decision = chooseLevel({
        allowed,
        available: allowed.filter((level) => !state.exhausted[level]),
        proposers,
        state,
        promptFirst: opts.promptFirst ?? true,
        parameterBeforeStructural: opts.parameterBeforeStructural ?? true
      });
      if (!decision) return [];
      const proposer = proposers[decision.level];
      if (!proposer) {
        throw new Error(`fapoProposer: selected ${decision.level} but no proposer is configured`);
      }
      const requested = Math.min(ctx.populationSize, proposalsPerCycle);
      const raw = await proposer.propose({ ...ctx, populationSize: requested });
      const wrapped = raw.map((candidate) => normalizeProposal(candidate)).map((candidate) => wrapProposal(candidate, decision.level, decision.reason));
      const reviewed = [];
      for (const candidate of wrapped) {
        const review = opts.reviewCandidate ? await opts.reviewCandidate({
          level: decision.level,
          candidate,
          context: ctx,
          reason: decision.reason
        }) : { verdict: "pass" };
        if (review.verdict === "fail") continue;
        const warn = review.verdict === "warn" ? renderReviewWarnings(review) : "";
        reviewed.push(
          warn ? { ...candidate, rationale: `${candidate.rationale}
${warn}` } : candidate
        );
      }
      if (wrapped.length > 0 && reviewed.length === 0) {
        throw new Error(
          `fapoProposer: reviewer blocked every ${decision.level} candidate; refusing to evaluate an unreviewed variant`
        );
      }
      return reviewed;
    },
    decide({ history }) {
      const last = history[history.length - 1];
      if (last && last.candidates.length === 0) {
        return { stop: true, reason: "FAPO produced no scoped candidate in the prior generation" };
      }
      const state = policyState(history, [], {
        plateauWindow,
        minDistinctStrategies,
        minImprovement
      });
      const everyAllowedLevelExhausted = allowed.every((level) => state.exhausted[level]);
      return everyAllowedLevelExhausted ? { stop: true, reason: `all FAPO levels exhausted (${allowed.join(", ")})` } : { stop: false };
    }
  };
}
function levelProposers(opts) {
  return {
    ...opts.proposers ?? {},
    ...opts.promptProposer ? { prompt: opts.promptProposer } : {},
    ...opts.parameterProposer ? { parameter: opts.parameterProposer } : {},
    ...opts.structuralProposer ? { structural: opts.structuralProposer } : {}
  };
}
function allowedLevels(opts, proposers) {
  const explicit = opts.scope?.allowedLevels;
  const forbidden = new Set(opts.scope?.forbiddenLevels ?? []);
  return FAPO_LEVELS.filter((level) => {
    if (!proposers[level]) return false;
    if (forbidden.has(level)) return false;
    return explicit ? explicit.includes(level) : true;
  });
}
function policyState(history, findings, opts) {
  const attempts = extractAttempts(history);
  return {
    attempts,
    exhausted: {
      prompt: isLevelExhausted("prompt", attempts, opts),
      parameter: isLevelExhausted("parameter", attempts, opts),
      structural: isLevelExhausted("structural", attempts, opts)
    },
    signals: extractFapoAttributionSignals(findings)
  };
}
function extractAttempts(history) {
  const attempts = [];
  for (const generation of history) {
    for (const candidate of generation.candidates) {
      const level = candidateLevel(candidate);
      if (!level) continue;
      attempts.push({
        level,
        composite: candidate.composite,
        strategy: candidateStrategy(candidate)
      });
    }
  }
  return attempts;
}
function candidateLevel(candidate) {
  const label = candidate.label ?? "";
  const rationale = candidate.rationale ?? "";
  return parseLevel(label) ?? parseLevel(rationale);
}
function parseLevel(text) {
  const match = /\bfapo:(prompt|parameter|structural)\b/.exec(text);
  return match ? match[1] : null;
}
function candidateStrategy(candidate) {
  const label = candidate.label ?? candidate.rationale ?? candidate.surfaceHash;
  return label.replace(/\bfapo:(prompt|parameter|structural):?/g, "").trim() || candidate.surfaceHash;
}
function isLevelExhausted(level, attempts, opts) {
  const own = attempts.filter((attempt) => attempt.level === level);
  if (own.length < opts.plateauWindow) return false;
  const distinctStrategies = new Set(own.map((attempt) => attempt.strategy)).size;
  if (distinctStrategies < opts.minDistinctStrategies) return false;
  let best = Number.NEGATIVE_INFINITY;
  let nonImproving = 0;
  for (const attempt of own) {
    if (attempt.composite > best + opts.minImprovement) {
      best = attempt.composite;
      nonImproving = 0;
    } else {
      nonImproving += 1;
    }
  }
  return nonImproving >= opts.plateauWindow;
}
function chooseLevel(args) {
  const { available, state } = args;
  if (available.length === 0) return null;
  const triedPrompt = state.attempts.some((attempt) => attempt.level === "prompt");
  if (args.promptFirst && available.includes("prompt") && !triedPrompt) {
    return {
      level: "prompt",
      reason: "prompt-first policy: no prompt-level variant has been tried yet"
    };
  }
  const supported = available.filter((level) => state.signals.counts[level] > 0);
  if (supported.includes("prompt")) {
    return {
      level: "prompt",
      reason: `attribution has ${state.signals.counts.prompt} prompt-addressable failure(s)`
    };
  }
  const strongest = supported.sort(
    (a, b) => state.signals.counts[b] - state.signals.counts[a] || levelRank(a) - levelRank(b)
  )[0];
  if (strongest) {
    if (strongest === "structural" && args.parameterBeforeStructural && available.includes("parameter") && args.proposers.parameter) {
      return {
        level: "parameter",
        reason: "attribution indicates a non-prompt bottleneck; trying parameter/config edits before structural edits"
      };
    }
    return {
      level: strongest,
      reason: `attribution has ${state.signals.counts[strongest]} ${strongest}-addressable failure(s)`
    };
  }
  if (state.attempts.length === 0) {
    const first = available.slice().sort((a, b) => levelRank(a) - levelRank(b))[0];
    return { level: first, reason: "no attribution yet; starting at the cheapest allowed level" };
  }
  return null;
}
function levelRank(level) {
  return level === "prompt" ? 0 : level === "parameter" ? 1 : 2;
}
function normalizeProposal(value) {
  return isProposedCandidate(value) ? value : { surface: value, label: "candidate", rationale: "bare candidate from level proposer" };
}
function wrapProposal(candidate, level, reason) {
  const label = candidate.label ? `fapo:${level}:${candidate.label}` : `fapo:${level}`;
  return {
    ...candidate,
    label,
    rationale: [
      `fapo:${level} selected by reviewed escalation policy`,
      `Reason: ${reason}`,
      candidate.rationale ? `Level proposer rationale: ${candidate.rationale}` : ""
    ].filter(Boolean).join("\n")
  };
}
function renderReviewWarnings(review) {
  const issues = review.issues?.filter((issue) => issue.severity === "warn") ?? [];
  if (issues.length === 0) return "";
  return `Reviewer warnings:
${issues.map((issue) => `- ${issue.checkName}: ${issue.description}`).join("\n")}`;
}
function extractFapoAttributionSignals(findings) {
  const signals = {
    counts: { prompt: 0, parameter: 0, structural: 0 },
    clusters: []
  };
  const seen = /* @__PURE__ */ new WeakSet();
  for (const finding of findings) {
    collectFinding(signals, finding, seen, 0);
  }
  return signals;
}
function collectFinding(signals, finding, seen, depth) {
  if (depth > MAX_FINDING_DEPTH) return;
  if (!finding) return;
  if (typeof finding === "string") {
    const level = inferLevelFromText(finding);
    if (level) addCluster(signals, { label: finding, level, count: 1 });
    return;
  }
  if (Array.isArray(finding)) {
    if (seen.has(finding)) return;
    seen.add(finding);
    for (const item of finding) collectFinding(signals, item, seen, depth + 1);
    return;
  }
  if (typeof finding !== "object") return;
  if (seen.has(finding)) return;
  seen.add(finding);
  const obj = finding;
  collectLevelPartition(signals, obj.level_partition ?? obj.levelPartition, seen, depth + 1);
  collectCounts(signals, obj);
  collectClusters(signals, obj.clusters, true, seen, depth + 1);
  const explicit = parseFindingLevel(obj.level ?? obj.optimization_level ?? obj.optimizationLevel);
  const text = textFields(obj);
  const inferred = explicit ?? inferLevelFromText(text);
  if (inferred) {
    addCluster(signals, {
      label: text || inferred,
      level: inferred,
      count: numberField(obj.count) ?? 1,
      confidence: confidenceField(obj.confidence),
      suggestedFix: stringField(obj.suggested_fix ?? obj.suggestedFix ?? obj.recommended_action),
      caseIds: stringArrayField(obj.case_ids ?? obj.caseIds)
    });
  }
}
function collectLevelPartition(signals, raw, seen, depth) {
  if (!raw || typeof raw !== "object") return;
  if (seen.has(raw)) return;
  seen.add(raw);
  const partition = raw;
  for (const level of FAPO_LEVELS) {
    const bucket = partition[level];
    if (!bucket || typeof bucket !== "object") continue;
    const obj = bucket;
    const count = numberField(obj.count) ?? 0;
    if (count > 0) signals.counts[level] += count;
    collectClusters(signals, obj.clusters, false, seen, depth + 1);
  }
}
function collectCounts(signals, obj) {
  const prompt = numberField(obj.prompt_addressable ?? obj.promptAddressable);
  const structural = numberField(obj.structural_addressable ?? obj.structuralAddressable);
  const tool = numberField(obj.tool_addressable ?? obj.toolAddressable);
  if (prompt) signals.counts.prompt += prompt;
  if (structural) signals.counts.structural += structural;
  if (tool) signals.counts.structural += tool;
}
function collectClusters(signals, raw, countClusters, seen, depth) {
  if (!Array.isArray(raw)) return;
  if (seen.has(raw)) return;
  seen.add(raw);
  for (const item of raw) {
    if (countClusters) {
      collectFinding(signals, item, seen, depth + 1);
      continue;
    }
    const cluster = parseCluster(item);
    if (cluster) signals.clusters.push(cluster);
  }
}
function addCluster(signals, cluster) {
  signals.counts[cluster.level] += Math.max(1, cluster.count);
  signals.clusters.push(cluster);
}
function parseCluster(raw) {
  if (!raw || typeof raw !== "object") return null;
  const obj = raw;
  const text = textFields(obj);
  const level = parseFindingLevel(obj.level ?? obj.optimization_level ?? obj.optimizationLevel) ?? inferLevelFromText(text);
  if (!level) return null;
  return {
    label: text || level,
    level,
    count: numberField(obj.count) ?? 1,
    confidence: confidenceField(obj.confidence),
    suggestedFix: stringField(obj.suggested_fix ?? obj.suggestedFix ?? obj.recommended_action),
    caseIds: stringArrayField(obj.case_ids ?? obj.caseIds)
  };
}
function parseFindingLevel(raw) {
  if (typeof raw !== "string") return null;
  const lower = raw.toLowerCase();
  if (lower === "prompt" || lower === "parameter" || lower === "structural") return lower;
  if (lower === "chain" || lower === "tool" || lower === "code") return "structural";
  if (lower === "config" || lower === "params") return "parameter";
  return null;
}
function inferLevelFromText(text) {
  const lower = text.toLowerCase();
  if (/\b(retrieval_k|temperature|top_p|max_tokens|max_completion_tokens|reasoning_effort|config|parameter)\b/.test(
    lower
  )) {
    return "parameter";
  }
  if (/\b(retriev\w*|search\w*|bm25|hop|evidence|cascade|tool|node|chain|state|structur\w*|topology|import)\b/.test(
    lower
  )) {
    return "structural";
  }
  if (/\b(format\w*|verbose|brevity|abstain\w*|reasoning|instruction|prompt|output|answer)\b/.test(
    lower
  )) {
    return "prompt";
  }
  return null;
}
function textFields(obj) {
  return [
    obj.label,
    obj.claim,
    obj.recommended_action,
    obj.suggested_fix,
    obj.suggestedFix,
    obj.message,
    obj.text,
    obj.heuristic,
    obj.area
  ].filter((value) => typeof value === "string" && value.trim().length > 0).join(" ");
}
function numberField(value) {
  return typeof value === "number" && Number.isFinite(value) ? value : null;
}
function stringField(value) {
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
}
function confidenceField(value) {
  return value === "high" || value === "medium" || value === "low" ? value : void 0;
}
function stringArrayField(value) {
  return Array.isArray(value) ? value.filter((item) => typeof item === "string") : void 0;
}
function parameterSweepProposer(opts) {
  if (opts.candidates.length === 0) {
    throw new Error("parameterSweepProposer: candidates must not be empty");
  }
  return {
    kind: "parameter-sweep",
    async propose(ctx) {
      if (typeof ctx.currentSurface !== "string") {
        throw new Error(
          "parameterSweepProposer: currentSurface must be a JSON string config surface"
        );
      }
      const parse = opts.parse ?? parseJsonObject;
      const stringify = opts.stringify ?? ((config) => JSON.stringify(config, null, 2));
      const current = parse(ctx.currentSurface);
      const currentCanonical = canonicalJson(current);
      const tried = triedLabels(ctx.history);
      const out = [];
      for (const candidate of opts.candidates) {
        if (tried.has(candidate.label)) continue;
        const next = applyParameterCandidate(current, candidate);
        const surface = stringify(next);
        if (surface === ctx.currentSurface || canonicalJson(next) === currentCanonical) continue;
        out.push({ surface, label: candidate.label, rationale: candidate.rationale });
        if (out.length >= ctx.populationSize) break;
      }
      return out;
    }
  };
}
function parseJsonObject(surface) {
  let parsed;
  try {
    parsed = JSON.parse(surface);
  } catch (error) {
    throw new Error(
      `parameterSweepProposer: currentSurface must be valid JSON object config (${error instanceof Error ? error.message : String(error)})`
    );
  }
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
    throw new Error("parameterSweepProposer: JSON surface must parse to an object");
  }
  return parsed;
}
function triedLabels(history) {
  const tried = /* @__PURE__ */ new Set();
  for (const generation of history) {
    for (const candidate of generation.candidates) {
      if (!candidate.label) continue;
      tried.add(candidate.label);
      tried.add(candidateStrategy(candidate));
    }
  }
  return tried;
}
function applyParameterCandidate(current, candidate) {
  const next = cloneJsonObject(current);
  if (candidate.patch) deepMerge(next, candidate.patch);
  for (const change of candidate.changes ?? []) {
    setPath(
      next,
      typeof change.path === "string" ? change.path.split(".") : [...change.path],
      change.value
    );
  }
  return next;
}
function cloneJsonObject(value) {
  return JSON.parse(JSON.stringify(value));
}
function deepMerge(target, patch) {
  for (const [key, value] of Object.entries(patch)) {
    assertSafeJsonKey(key);
    if (isPlainObject(value) && isPlainObject(target[key])) {
      deepMerge(target[key], value);
    } else {
      target[key] = value;
    }
  }
}
function setPath(target, path, value) {
  if (path.length === 0) throw new Error("parameterSweepProposer: change path must not be empty");
  for (const part of path) assertSafeJsonKey(part);
  let cursor = target;
  for (const part of path.slice(0, -1)) {
    const existing = cursor[part];
    if (!isPlainObject(existing)) {
      cursor[part] = {};
    }
    cursor = cursor[part];
  }
  cursor[path[path.length - 1]] = value;
}
function assertSafeJsonKey(key) {
  if (!key.trim()) throw new Error("parameterSweepProposer: change path contains an empty key");
  if (UNSAFE_JSON_KEYS.has(key)) {
    throw new Error(`parameterSweepProposer: unsafe JSON key "${key}" is not allowed`);
  }
}
function canonicalJson(value) {
  if (Array.isArray(value)) return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
  if (isPlainObject(value)) {
    return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(",")}}`;
  }
  return JSON.stringify(value);
}
function isPlainObject(value) {
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
  const proto = Object.getPrototypeOf(value);
  return proto === Object.prototype || proto === null;
}

// src/campaign/skill-patch.ts
function applySkillPatch(surface, patch) {
  let lines = surface.split("\n");
  let applied = 0;
  const rejected = [];
  const findLine = (anchor) => lines.findIndex((l) => l.includes(anchor));
  for (const op of patch.ops) {
    if (op.op === "add") {
      if (typeof op.text !== "string" || op.text.trim() === "") {
        rejected.push({ op, reason: "empty add text" });
        continue;
      }
      const insert = op.text.split("\n");
      if (op.after === void 0 || op.after === "") {
        lines = [...lines, ...insert];
        applied++;
        continue;
      }
      const idx = findLine(op.after);
      if (idx === -1) {
        rejected.push({ op, reason: `add anchor not found: ${truncate(op.after)}` });
        continue;
      }
      lines = [...lines.slice(0, idx + 1), ...insert, ...lines.slice(idx + 1)];
      applied++;
    } else if (op.op === "delete") {
      const idx = findLine(op.anchor);
      if (idx === -1) {
        rejected.push({ op, reason: `delete anchor not found: ${truncate(op.anchor)}` });
        continue;
      }
      lines = [...lines.slice(0, idx), ...lines.slice(idx + 1)];
      applied++;
    } else {
      const idx = findLine(op.anchor);
      if (idx === -1) {
        rejected.push({ op, reason: `replace anchor not found: ${truncate(op.anchor)}` });
        continue;
      }
      if (typeof op.text !== "string") {
        rejected.push({ op, reason: "replace text missing" });
        continue;
      }
      lines = [...lines.slice(0, idx), ...op.text.split("\n"), ...lines.slice(idx + 1)];
      applied++;
    }
  }
  return { surface: lines.join("\n"), applied, rejected };
}
function patchEditCount(patch) {
  return patch.ops.length;
}
function truncate(s, max = 48) {
  return s.length <= max ? s : `${s.slice(0, max)}\u2026`;
}

// src/campaign/proposers/skill-opt.ts
var SKILLOPT_SYSTEM = 'You are a SkillOpt optimizer. You improve ONE skill document by proposing BOUNDED, anchored edits \u2014 never a full rewrite. Output ONLY a JSON object of shape {"patches":[{"label":string,"rationale":string,"ops":[op,...]}]} where each op is one of: {"op":"add","after":<exact substring of an existing line, or omit to append>,"text":<new line(s)>}, {"op":"delete","anchor":<exact substring of the line to remove>}, {"op":"replace","anchor":<exact substring of the line to replace>,"text":<replacement line(s)>}. Anchors MUST be verbatim substrings of lines that exist in the document. No prose outside JSON.';
function skillOptProposer(opts) {
  const evidenceK = opts.evidenceK ?? 3;
  const defaultBudget = opts.editBudget ?? 3;
  async function proposePatches(args) {
    const userPrompt = buildPatchPrompt({
      target: opts.target,
      surface: args.surface,
      evidence: args.evidence,
      editBudget: args.editBudget,
      rejectedBuffer: args.rejectedBuffer,
      metaNote: args.metaNote,
      findingsNote: args.findingsNote,
      count: args.count
    });
    const result = await callLlm(
      {
        model: opts.model,
        messages: [
          { role: "system", content: SKILLOPT_SYSTEM },
          { role: "user", content: userPrompt }
        ],
        jsonMode: true,
        temperature: opts.temperature ?? 0.6,
        maxTokens: opts.maxTokens ?? 4e3
      },
      opts.llm
    );
    return parseSkillPatchResponse(result.content, args.count, args.editBudget);
  }
  return {
    kind: "skill-opt",
    proposePatches,
    async propose(ctx) {
      if (typeof ctx.currentSurface !== "string") {
        throw new Error(
          "skillOptProposer: surface must be a string skill document (got a CodeSurface). SkillOpt patches text."
        );
      }
      const surface = ctx.currentSurface;
      const patches = await proposePatches({
        surface,
        evidence: evidenceFromHistory(ctx, evidenceK),
        editBudget: defaultBudget,
        rejectedBuffer: [],
        findingsNote: renderAnalystEvidence(ctx.findings, ctx.report) ?? void 0,
        count: ctx.populationSize,
        signal: ctx.signal
      });
      const out = [];
      const seen = /* @__PURE__ */ new Set();
      for (const patch of patches) {
        const { surface: candidate, applied } = applySkillPatch(surface, patch);
        if (applied === 0 || candidate === surface || seen.has(candidate)) continue;
        seen.add(candidate);
        out.push({ surface: candidate, label: patch.label, rationale: patch.rationale });
        if (out.length >= ctx.populationSize) break;
      }
      return out;
    }
  };
}
function evidenceFromHistory(ctx, k) {
  const last = ctx.history.at(-1);
  if (!last || last.candidates.length === 0) return { weakScenarios: [], weakDimensions: [] };
  const best = [...last.candidates].sort((a, b) => b.composite - a.composite)[0];
  if (!best) return { weakScenarios: [], weakDimensions: [] };
  const weakScenarios = [...best.scenarios].sort((a, b) => a.composite - b.composite).slice(0, k);
  const weakDimensions = Object.entries(best.dimensions).sort((a, b) => a[1] - b[1]).slice(0, k).map(([dimension, score]) => ({ dimension, score }));
  return { weakScenarios, weakDimensions };
}
function buildPatchPrompt(args) {
  const lines = [
    `Skill document governs: ${args.target}.`,
    "",
    "Current skill document:",
    "```",
    args.surface,
    "```",
    "",
    `Propose ${args.count} candidate patch(es). Each patch is a SMALL bundle of`,
    `at most ${args.editBudget} op(s). Anchors must be verbatim substrings of`,
    "existing lines. Prefer adding a specific missing rule or sharpening a vague",
    "one over deleting; never rewrite the whole document."
  ];
  if (args.evidence.weakScenarios.length > 0) {
    lines.push(
      "",
      "Weakest scenarios (patch to fix these):",
      ...args.evidence.weakScenarios.map((s) => `- ${s.scenarioId} (${s.composite.toFixed(2)})`)
    );
  }
  if (args.evidence.weakDimensions.length > 0) {
    lines.push(
      "",
      "Weakest dimensions (what to improve):",
      ...args.evidence.weakDimensions.map((d) => `- ${d.dimension} (${d.score.toFixed(2)})`)
    );
  }
  if (args.rejectedBuffer.length > 0) {
    lines.push(
      "",
      "Already tried and REJECTED (do not repeat or restate these edits):",
      ...args.rejectedBuffer.map((e) => `- ${e.label}: ${e.rationale} \u2014 ${e.reason}`)
    );
  }
  if (args.findingsNote) {
    lines.push("", args.findingsNote);
  }
  if (args.metaNote) {
    lines.push("", `Strategy note from prior epochs: ${args.metaNote}`);
  }
  return lines.join("\n");
}
var SkillPatchParseError = class extends Error {
  constructor(message) {
    super(message);
    this.name = "SkillPatchParseError";
  }
};
function parseSkillPatchResponse(raw, maxPatches, editBudget) {
  let text = raw.trim();
  if (text.startsWith("```")) text = text.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "");
  const start = text.indexOf("{");
  const end = text.lastIndexOf("}");
  if (start < 0 || end <= start) {
    throw new SkillPatchParseError(
      `parseSkillPatchResponse: response was not valid JSON (no object found): ${snippet(raw)}`
    );
  }
  let parsed;
  try {
    parsed = JSON.parse(text.slice(start, end + 1));
  } catch (err) {
    throw new SkillPatchParseError(
      `parseSkillPatchResponse: response was not valid JSON (${err instanceof Error ? err.message : String(err)}): ${snippet(raw)}`
    );
  }
  const rawPatches = Array.isArray(parsed.patches) ? parsed.patches : [];
  const out = [];
  for (const rp of rawPatches) {
    if (typeof rp !== "object" || rp === null) continue;
    const obj = rp;
    const ops = Array.isArray(obj.ops) ? obj.ops.map(normalizeOp).filter(isOp) : [];
    if (ops.length === 0) continue;
    out.push({
      label: typeof obj.label === "string" ? obj.label : "patch",
      rationale: typeof obj.rationale === "string" ? obj.rationale : "",
      ops: ops.slice(0, editBudget)
    });
    if (out.length >= maxPatches) break;
  }
  return out;
}
function normalizeOp(raw) {
  if (typeof raw !== "object" || raw === null) return null;
  const o = raw;
  if (o.op === "add") {
    if (typeof o.text !== "string") return null;
    const op = { op: "add", text: o.text };
    if (typeof o.after === "string") op.after = o.after;
    return op;
  }
  if (o.op === "delete") {
    if (typeof o.anchor !== "string") return null;
    return { op: "delete", anchor: o.anchor };
  }
  if (o.op === "replace") {
    if (typeof o.anchor !== "string" || typeof o.text !== "string") return null;
    return { op: "replace", anchor: o.anchor, text: o.text };
  }
  return null;
}
function isOp(op) {
  return op !== null;
}
function snippet(s, max = 120) {
  const t = s.trim().replace(/\s+/g, " ");
  return t.length <= max ? t : `${t.slice(0, max)}\u2026`;
}

// src/campaign/presets/run-skill-opt.ts
async function runSkillOpt(opts) {
  if (opts.trainScenarios.length === 0) throw new Error("runSkillOpt: trainScenarios is empty");
  if (opts.holdoutScenarios.length === 0) throw new Error("runSkillOpt: holdoutScenarios is empty");
  if (!opts.judges || opts.judges.length === 0) {
    throw new Error(
      "runSkillOpt: at least one judge is required \u2014 scoring (and therefore acceptance) is meaningless without one, and would report a silent zero lift."
    );
  }
  const holdoutIds = new Set(opts.holdoutScenarios.map((s) => s.id));
  const overlap = opts.trainScenarios.filter((s) => holdoutIds.has(s.id)).map((s) => s.id);
  if (overlap.length > 0) {
    throw new Error(
      `runSkillOpt: trainScenarios and holdoutScenarios must be disjoint (overlap: [${overlap.join(
        ", "
      )}]) \u2014 a shared scenario leaks the held-out acceptance axis into the proposal evidence.`
    );
  }
  const patchesPerEpoch = opts.patchesPerEpoch ?? 2;
  const initialBudget = opts.editBudget ?? 3;
  const minImprovement = opts.minImprovement ?? 0;
  if (minImprovement < 0) {
    throw new Error(
      "runSkillOpt: minImprovement must be >= 0 \u2014 a negative threshold would accept held-out regressions, breaking the monotonic-lift contract."
    );
  }
  const patience = opts.patience ?? opts.maxEpochs;
  const budgetAnneal = opts.budgetAnneal ?? true;
  const rejectedBufferSize = opts.rejectedBufferSize ?? 12;
  const slowMetaEvery = opts.slowMetaEvery ?? 2;
  let totalCostUsd = 0;
  const scoreHoldout = async (surface, tag) => {
    const campaign = await runScoringCampaign(opts, opts.holdoutScenarios, surface, tag);
    totalCostUsd += campaign.aggregates.totalCostUsd;
    return campaignMeanComposite(campaign);
  };
  const evidenceK = opts.evidenceK ?? 3;
  const trainEvidence = async (surface, tag) => {
    const campaign = await runScoringCampaign(opts, opts.trainScenarios, surface, tag);
    totalCostUsd += campaign.aggregates.totalCostUsd;
    return toEvidence(campaign, evidenceK);
  };
  let current = opts.baselineSurface;
  let currentEvidence = await trainEvidence(current, "baseline-train");
  const baselineHoldout = await scoreHoldout(current, "baseline-holdout");
  let currentHoldout = baselineHoldout;
  const buffer = [];
  const acceptedEdits = [];
  const rejectedAll = [];
  const history = [];
  let budget = initialBudget;
  let sinceAccept = 0;
  let metaNote;
  let epochsRun = 0;
  for (let epoch = 0; epoch < opts.maxEpochs; epoch++) {
    epochsRun++;
    const patches = await opts.proposer.proposePatches({
      surface: current,
      evidence: currentEvidence,
      editBudget: budget,
      rejectedBuffer: buffer,
      metaNote,
      count: patchesPerEpoch,
      signal: opts.signal ?? new AbortController().signal
    });
    let accepted = null;
    const rejectedThisEpoch = [];
    for (let i = 0; i < patches.length; i++) {
      const patch = patches[i];
      const { surface: candidate, applied } = applySkillPatch(current, patch);
      if (applied === 0 || candidate === current) {
        rejectedThisEpoch.push({
          label: patch.label,
          rationale: patch.rationale,
          reason: "no-op (unanchored or zero-change)"
        });
        continue;
      }
      const candidateHoldout = await scoreHoldout(candidate, `epoch-${epoch}-cand-${i}-holdout`);
      if (candidateHoldout > currentHoldout + minImprovement) {
        accepted = {
          epoch,
          label: patch.label,
          rationale: patch.rationale,
          holdoutDelta: candidateHoldout - currentHoldout
        };
        current = candidate;
        currentHoldout = candidateHoldout;
        currentEvidence = await trainEvidence(current, `epoch-${epoch}-train`);
        break;
      }
      rejectedThisEpoch.push({
        label: patch.label,
        rationale: patch.rationale,
        reason: `held-out ${candidateHoldout.toFixed(3)} \u2264 current ${currentHoldout.toFixed(3)}`
      });
    }
    if (accepted) {
      acceptedEdits.push(accepted);
      sinceAccept = 0;
    } else {
      sinceAccept++;
      if (budgetAnneal && sinceAccept >= 2 && budget > 1) budget--;
    }
    for (const r of rejectedThisEpoch) {
      buffer.push(r);
      rejectedAll.push(r);
    }
    while (buffer.length > rejectedBufferSize) buffer.shift();
    if (slowMetaEvery > 0 && (epoch + 1) % slowMetaEvery === 0) {
      metaNote = buildMetaNote(acceptedEdits, buffer);
    }
    history.push({
      epoch,
      editBudget: budget,
      proposed: patches.length,
      accepted,
      rejected: rejectedThisEpoch,
      holdoutComposite: currentHoldout
    });
    if (sinceAccept >= patience) break;
  }
  return {
    winnerSurface: current,
    baselineHoldoutComposite: baselineHoldout,
    winnerHoldoutComposite: currentHoldout,
    lift: currentHoldout - baselineHoldout,
    acceptedEdits,
    rejectedEdits: rejectedAll,
    epochsRun,
    history,
    totalCostUsd
  };
}
function runScoringCampaign(opts, scenarios, surface, tag) {
  return runCampaign({
    ...opts,
    scenarios,
    dispatch: (scenario, ctx) => opts.dispatchWithSurface(surface, scenario, ctx),
    runDir: `${opts.runDir}/${tag}`
  });
}
function toEvidence(campaign, k) {
  const { dimensions, scenarios } = campaignBreakdown(campaign);
  const weakScenarios = [...scenarios].sort((a, b) => a.composite - b.composite).slice(0, k);
  const weakDimensions = Object.entries(dimensions).sort((a, b) => a[1] - b[1]).slice(0, k).map(([dimension, score]) => ({ dimension, score }));
  return { weakScenarios, weakDimensions };
}
function buildMetaNote(accepted, rejected) {
  const parts = [];
  if (accepted.length > 0) {
    parts.push(
      `Edits that improved held-out so far: ${accepted.map((a) => `"${a.label}" (+${a.holdoutDelta.toFixed(3)})`).join("; ")}. Build on these.`
    );
  }
  if (rejected.length > 0) {
    const labels = [...new Set(rejected.map((r) => r.label))].slice(0, 5);
    parts.push(`Dead ends to avoid: ${labels.join(", ")}. Try a different anchor or rule.`);
  }
  parts.push("Keep edits small and anchored to existing lines.");
  return parts.join(" ");
}

// src/campaign/presets/compare-proposers.ts
async function compareProposers(opts) {
  return compareProposerEntries(opts);
}
async function compareProposerEntries(opts) {
  if (opts.proposers.length === 0) throw new Error("compareProposers: no proposers to compare");
  const seed = opts.seed ?? 42;
  const resamples = opts.resamples ?? 2e3;
  const confidence = opts.confidence ?? 0.95;
  const scoreOnHoldout = async (surface, tag) => {
    const campaign = await runCampaign({
      ...opts,
      scenarios: opts.holdoutScenarios,
      dispatch: (scenario, ctx) => opts.dispatchWithSurface(surface, scenario, ctx),
      runDir: `${opts.runDir}/${tag}`
    });
    const byScenario = {};
    for (const { scenarioId, composite } of campaignBreakdown(campaign).scenarios) {
      byScenario[scenarioId] = composite;
    }
    return byScenario;
  };
  const scenarioIds = [...new Set(opts.holdoutScenarios.map((s) => s.id))].sort();
  if (scenarioIds.length === 0) throw new Error("compareProposers: holdoutScenarios is empty");
  const align = (byScenario, label) => {
    const missing = scenarioIds.filter((id) => !(id in byScenario));
    if (missing.length > 0) {
      throw new Error(
        `compareProposers: ${label} produced no held-out score for scenario(s) [${missing.join(
          ", "
        )}] \u2014 a cell errored or its judges returned nothing. Refusing to fabricate a 0 (it would corrupt the lift comparison). Fix the dispatch/judge or drop the scenario.`
      );
    }
    return scenarioIds.map((id) => byScenario[id]);
  };
  const baselineArr = align(
    await scoreOnHoldout(opts.baselineSurface, "compare-baseline"),
    "baseline"
  );
  const winners = [];
  for (const proposer of opts.proposers) {
    const out = await proposer.optimize();
    const byScenario = await scoreOnHoldout(out.winnerSurface, `compare-${slug(proposer.name)}`);
    winners.push({
      name: proposer.name,
      winnerSurface: out.winnerSurface,
      costUsd: out.costUsd,
      durationMs: out.durationMs,
      arr: align(byScenario, `proposer "${proposer.name}"`)
    });
  }
  const scores = winners.map((w) => {
    const boot = pairedBootstrap(baselineArr, w.arr, {
      seed,
      resamples,
      confidence,
      statistic: "mean"
    });
    const score = {
      name: w.name,
      baselineComposite: mean(baselineArr),
      winnerComposite: mean(w.arr),
      lift: boot.mean,
      liftCi: { low: boot.low, high: boot.high },
      costUsd: w.costUsd,
      winnerSurface: w.winnerSurface,
      rank: 0
    };
    if (w.durationMs !== void 0) score.durationMs = w.durationMs;
    return score;
  });
  scores.sort((a, b) => b.lift - a.lift || a.costUsd - b.costUsd);
  scores.forEach((s, i) => {
    s.rank = i + 1;
  });
  const best = scores[0];
  const byName = new Map(winners.map((w) => [w.name, w]));
  const bestArr = byName.get(best.name).arr;
  const pairwise = scores.slice(1).map((other) => {
    const otherArr = byName.get(other.name).arr;
    const boot = pairedBootstrap(otherArr, bestArr, {
      seed,
      resamples,
      confidence,
      statistic: "mean"
    });
    const favored = boot.low > 0 ? best.name : boot.high < 0 ? other.name : "tie";
    return {
      a: best.name,
      b: other.name,
      deltaMean: boot.mean,
      low: boot.low,
      high: boot.high,
      favored
    };
  });
  return { scores, best, pairwise, holdoutScenarioIds: scenarioIds };
}
function mean(xs) {
  return xs.length === 0 ? 0 : xs.reduce((a, b) => a + b, 0) / xs.length;
}
function slug(name) {
  return name.replace(/[^a-z0-9]+/gi, "-").toLowerCase();
}
function gepaReflectionEntry(config, name = "gepa-reflection") {
  return gepaEntry(config, false, name);
}
function gepaParetoEntry(config, name = "gepa-pareto") {
  return gepaEntry(config, true, name);
}
function gepaEntry(config, combineParents, name) {
  return {
    name,
    async optimize() {
      const started = Date.now();
      const proposer = gepaProposer({
        llm: config.llm,
        model: config.model,
        target: config.target,
        combineParents,
        ...config.mutationPrimitives ? { mutationPrimitives: config.mutationPrimitives } : {}
      });
      const result = await runImprovementLoop({
        scenarios: config.trainScenarios,
        holdoutScenarios: config.holdoutScenarios,
        baselineSurface: config.baselineSurface,
        dispatchWithSurface: config.dispatchWithSurface,
        judges: config.judges,
        proposer,
        populationSize: config.populationSize ?? 2,
        maxGenerations: config.maxGenerations ?? 3,
        gate: defaultProductionGate({
          holdoutScenarios: config.holdoutScenarios,
          deltaThreshold: 0
        }),
        autoOnPromote: "none",
        runDir: `${config.runDir}/${slug(name)}-loop`,
        ...config.seed !== void 0 ? { seed: config.seed } : {},
        // Flow findings to the proposer's propose(). These reach runOptimization
        // unchanged (runImprovementLoop extends RunOptimizationOptions and
        // forwards {...opts}); ctx.findings/report/analyzeGeneration are consumed there.
        ...config.findings !== void 0 ? { findings: config.findings } : {},
        ...config.analyzeGeneration ? { analyzeGeneration: config.analyzeGeneration } : {},
        ...config.report !== void 0 ? { report: config.report } : {}
      });
      const costUsd = result.baselineCampaign.aggregates.totalCostUsd + result.generations.reduce(
        (sum, g) => sum + g.surfaces.reduce((s, sf) => s + sf.campaign.aggregates.totalCostUsd, 0),
        0
      );
      return { winnerSurface: result.winnerSurface, costUsd, durationMs: Date.now() - started };
    }
  };
}
function skillOptEntry(config, name = "skill-opt") {
  return {
    name,
    async optimize() {
      const started = Date.now();
      const proposer = skillOptProposer({
        llm: config.llm,
        model: config.model,
        target: config.target
      });
      const result = await runSkillOpt({
        baselineSurface: config.baselineSurface,
        dispatchWithSurface: config.dispatchWithSurface,
        judges: config.judges,
        proposer,
        trainScenarios: config.trainScenarios,
        holdoutScenarios: config.holdoutScenarios,
        maxEpochs: config.maxEpochs ?? 6,
        runDir: `${config.runDir}/${slug(name)}-loop`,
        ...config.seed !== void 0 ? { seed: config.seed } : {}
      });
      return {
        winnerSurface: result.winnerSurface,
        costUsd: result.totalCostUsd,
        durationMs: Date.now() - started
      };
    }
  };
}
function fapoEscalationEntry(config, name = "fapo-escalation") {
  return {
    name,
    async optimize() {
      const started = Date.now();
      const promptProposer = config.promptProposer ?? gepaProposer({
        llm: config.llm,
        model: config.model,
        target: config.target,
        combineParents: true,
        ...config.mutationPrimitives ? { mutationPrimitives: config.mutationPrimitives } : {}
      });
      const parameterProposer = config.parameterProposer ?? (config.parameterCandidates ? parameterSweepProposer({ candidates: config.parameterCandidates }) : void 0);
      const structuralProposer = config.structuralProposer;
      const proposer = fapoProposer({
        ...config.fapo ?? {},
        promptProposer,
        ...parameterProposer ? { parameterProposer } : {},
        ...structuralProposer ? { structuralProposer } : {}
      });
      const result = await runImprovementLoop({
        scenarios: config.trainScenarios,
        holdoutScenarios: config.holdoutScenarios,
        baselineSurface: config.baselineSurface,
        dispatchWithSurface: config.dispatchWithSurface,
        judges: config.judges,
        proposer,
        populationSize: config.populationSize ?? 2,
        maxGenerations: config.maxGenerations ?? 3,
        gate: defaultProductionGate({
          holdoutScenarios: config.holdoutScenarios,
          deltaThreshold: 0
        }),
        autoOnPromote: "none",
        runDir: `${config.runDir}/${slug(name)}-loop`,
        ...config.seed !== void 0 ? { seed: config.seed } : {},
        ...config.findings !== void 0 ? { findings: config.findings } : {},
        ...config.analyzeGeneration ? { analyzeGeneration: config.analyzeGeneration } : {},
        ...config.report !== void 0 ? { report: config.report } : {}
      });
      const costUsd = result.baselineCampaign.aggregates.totalCostUsd + result.generations.reduce(
        (sum, g) => sum + g.surfaces.reduce((s, sf) => s + sf.campaign.aggregates.totalCostUsd, 0),
        0
      );
      return { winnerSurface: result.winnerSurface, costUsd, durationMs: Date.now() - started };
    }
  };
}

// src/campaign/presets/playback.ts
function makePlaybackDispatch(driver) {
  return async (profile, scenario, ctx) => {
    const events = await driver.run(scenario, { ...ctx, profile });
    return extractProducedState(events);
  };
}
async function scoreUserStory(story, state, checkCorrectness) {
  const verdict = await verifyCompletion(
    { taskId: story.id, requirements: story.requirements },
    state,
    checkCorrectness
  );
  return { ...verdict, title: story.title };
}
function userStoryScoreboard(verdicts) {
  const rows = [];
  for (const v of verdicts) {
    for (const r of v.requirements) {
      rows.push({
        storyId: v.taskId,
        storyTitle: v.title,
        reqId: r.reqId,
        reqTitle: r.title,
        status: r.satisfied ? "PASS" : "FAIL",
        evidence: r.evidence
      });
    }
  }
  return rows;
}
function scoreboardSummary(rows) {
  const byStory = /* @__PURE__ */ new Map();
  let passed = 0;
  for (const r of rows) {
    const s = byStory.get(r.storyId) ?? { total: 0, passed: 0 };
    s.total++;
    if (r.status === "PASS") {
      s.passed++;
      passed++;
    }
    byStory.set(r.storyId, s);
  }
  let storiesFullyComplete = 0;
  for (const s of byStory.values()) if (s.total > 0 && s.passed === s.total) storiesFullyComplete++;
  return {
    stories: byStory.size,
    storiesFullyComplete,
    requirements: rows.length,
    passed,
    failed: rows.length - passed,
    passRate: rows.length === 0 ? 0 : passed / rows.length
  };
}
function escapeCell(s) {
  return s.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
}
function truncate2(s, max) {
  return s.length <= max ? s : `${s.slice(0, Math.max(0, max - 1))}\u2026`;
}
function renderScoreboardMarkdown(rows, opts = {}) {
  const maxEv = opts.maxEvidenceChars ?? 160;
  const sum = scoreboardSummary(rows);
  const pct = (n) => `${Math.round(n * 100)}%`;
  const ev = (e) => escapeCell(truncate2(e.join("; "), maxEv)) || "\u2014";
  const out = [`# ${opts.title ?? "Product-flow playback scoreboard"}`, ""];
  if (opts.meta) {
    for (const [k, v] of Object.entries(opts.meta)) out.push(`- **${k}:** ${v}`);
    out.push("");
  }
  out.push(
    `**${sum.storiesFullyComplete}/${sum.stories}** user stories fully shipped \xB7 **${sum.passed}/${sum.requirements}** requirements passing (${pct(sum.passRate)}) \xB7 **${sum.failed}** open`,
    ""
  );
  const fails = rows.filter((r) => r.status === "FAIL");
  if (fails.length > 0) {
    out.push("## Open tickets", "", "| Story | Requirement | Evidence |", "| --- | --- | --- |");
    for (const r of fails) {
      out.push(`| ${escapeCell(r.storyTitle)} | ${escapeCell(r.reqTitle)} | ${ev(r.evidence)} |`);
    }
    out.push("");
  } else {
    out.push("_All requirements passing \u2014 no open tickets._", "");
  }
  out.push("## Per-story tick-off", "");
  for (const storyId of [...new Set(rows.map((r) => r.storyId))]) {
    const storyRows = rows.filter((r) => r.storyId === storyId);
    const passed = storyRows.filter((r) => r.status === "PASS").length;
    const mark = passed === storyRows.length ? "\u2705" : "\u26A0\uFE0F";
    out.push(
      `### ${escapeCell(storyRows[0].storyTitle)} \u2014 ${passed}/${storyRows.length} ${mark}`,
      "",
      "| Requirement | Status | Evidence |",
      "| --- | --- | --- |"
    );
    for (const r of storyRows) {
      out.push(
        `| ${escapeCell(r.reqTitle)} | ${r.status === "PASS" ? "\u2705 PASS" : "\u274C FAIL"} | ${ev(r.evidence)} |`
      );
    }
    out.push("");
  }
  return out.join("\n");
}

// src/campaign/presets/run-profile-matrix.ts
import { createHash as createHash3 } from "crypto";
import { join as join2 } from "path";
var ProfileMatrixError = class extends AgentEvalError {
  constructor(message) {
    super("profile_matrix", message);
  }
};
function sanitize(id) {
  return id.replace(/[^a-zA-Z0-9_-]/g, "_");
}
function sha(input) {
  return createHash3("sha256").update(JSON.stringify(input)).digest("hex");
}
function mean2(xs) {
  return xs.length === 0 ? 0 : xs.reduce((a, b) => a + b, 0) / xs.length;
}
function cellComposite(cell) {
  const composites = Object.values(cell.judgeScores).map((s) => s.composite);
  return composites.length === 0 ? 0 : mean2(composites);
}
function buildRunRecord(args) {
  const { cell, profile, profileHash, configHash, experimentId, splitTag, commitSha, matrixId } = args;
  const profileId = agentProfileId(profile);
  const model = agentProfileModelId(profile);
  const composite = cellComposite(cell);
  const raw = { composite };
  const perJudge = {};
  const dimAccum = {};
  const notes = [];
  for (const [judgeName, js] of Object.entries(cell.judgeScores)) {
    perJudge[judgeName] = { ...js.dimensions };
    for (const [dim, value] of Object.entries(js.dimensions)) {
      raw[`${judgeName}.${dim}`] = value;
      dimAccum[dim] ??= [];
      dimAccum[dim].push(value);
    }
    if (js.notes) notes.push(`${judgeName}: ${js.notes}`);
  }
  const perDimMean = {};
  for (const [dim, values] of Object.entries(dimAccum)) perDimMean[dim] = mean2(values);
  let costUsd = cell.costUsd;
  let costEstimated = false;
  if (costUsd === 0 && cell.tokenUsage.output > 0 && isModelPriced(model)) {
    costUsd = estimateCost(cell.tokenUsage.input, cell.tokenUsage.output, model);
    costEstimated = costUsd > 0;
  }
  raw.cost_usd = costUsd;
  raw.cost_estimated = costEstimated ? 1 : 0;
  raw.tokens_input = cell.tokenUsage.input;
  raw.tokens_output = cell.tokenUsage.output;
  if (typeof cell.tokenUsage.cached === "number") raw.tokens_cached = cell.tokenUsage.cached;
  raw.latency_ms = cell.durationMs;
  if (costUsd > 0) {
    raw.tokens_per_dollar = (cell.tokenUsage.input + cell.tokenUsage.output) / costUsd;
  }
  if (composite > 0.01) raw.cost_per_quality = costUsd / composite;
  const outcome = splitTag === "holdout" ? { holdoutScore: composite, raw } : { searchScore: composite, raw };
  if (Object.keys(perJudge).length > 0) {
    outcome.judgeScores = {
      perJudge,
      perDimMean,
      composite,
      ...notes.length > 0 ? { notes: notes.join(" | ") } : {}
    };
  }
  const record = {
    runId: `${matrixId}:${profileId}:${cell.cellId}`,
    experimentId,
    candidateId: profileId,
    seed: cell.seed,
    model,
    promptHash: profileHash,
    configHash,
    commitSha,
    wallMs: cell.durationMs,
    costUsd,
    tokenUsage: cell.tokenUsage,
    outcome,
    splitTag,
    scenarioId: cell.scenarioId,
    ...cell.error ? { failureMode: cell.error } : {}
  };
  if (args.corpusText && args.scenario) {
    try {
      const text = args.corpusText(cell.artifact, args.scenario);
      if (text && typeof text.prompt === "string" && typeof text.completion === "string") {
        record.prompt = text.prompt;
        record.completion = text.completion;
      }
    } catch {
    }
  }
  return record;
}
async function runProfileMatrix(opts) {
  if (opts.profiles.length === 0) throw new ProfileMatrixError("profiles must not be empty");
  if (opts.scenarios.length === 0) throw new ProfileMatrixError("scenarios must not be empty");
  const splitTag = opts.splitTag ?? "search";
  const seed = opts.seed ?? 42;
  const validate = opts.validate ?? true;
  const integrityMode = opts.integrity ?? "assert";
  const profileIds = opts.profiles.map(agentProfileId);
  const experimentId = opts.experimentId ?? `pm_${sha({ profileIds, scenarios: opts.scenarios.map((s) => s.id) }).slice(0, 16)}`;
  const matrixId = `mtx_${sha({ experimentId, profileIds, seed, splitTag }).slice(0, 16)}`;
  const scenarioById = new Map(opts.scenarios.map((s) => [s.id, s]));
  for (const profile of opts.profiles) {
    const profileHash = agentProfileHash(profile);
    const profileId = agentProfileId(profile);
    const model = agentProfileModelId(profile);
    try {
      validateRunRecord({
        runId: `${matrixId}:${profileId}:probe`,
        experimentId,
        candidateId: profileId,
        seed,
        model,
        promptHash: profileHash,
        configHash: profileHash,
        commitSha: opts.commitSha,
        wallMs: 0,
        costUsd: 0,
        tokenUsage: { input: 0, output: 0 },
        outcome: splitTag === "holdout" ? { holdoutScore: 0, raw: {} } : { searchScore: 0, raw: {} },
        splitTag
      });
    } catch (err) {
      throw new ProfileMatrixError(
        `profile '${profileId}' is not recordable: ${err instanceof Error ? err.message : String(err)}`
      );
    }
  }
  const records = [];
  const campaigns = {};
  const byProfile = {};
  for (const profile of opts.profiles) {
    const profileHash = agentProfileHash(profile);
    const profileId = agentProfileId(profile);
    const model = agentProfileModelId(profile);
    const configHash = sha({
      profile: profileHash,
      judges: (opts.judges ?? []).map((j) => j.name),
      seed,
      splitTag
    });
    const dispatch = (scenario, ctx) => opts.dispatch(profile, scenario, ctx);
    Object.defineProperty(dispatch, "name", { value: `profile_${sanitize(profileId)}` });
    const campaign = await runCampaign({
      scenarios: opts.scenarios,
      dispatch,
      judges: opts.judges,
      seed,
      reps: opts.reps,
      maxConcurrency: opts.maxConcurrency,
      costCeiling: opts.costCeiling,
      labeledStore: opts.labeledStore,
      captureSource: opts.captureSource,
      storage: opts.storage,
      now: opts.now,
      runDir: join2(opts.runDir, sanitize(profileId))
    });
    const profileRecords = [];
    for (const cell of campaign.cells) {
      const record = buildRunRecord({
        cell,
        profile,
        profileHash,
        configHash,
        experimentId,
        splitTag,
        commitSha: opts.commitSha,
        matrixId,
        scenario: scenarioById.get(cell.scenarioId),
        corpusText: opts.corpusText
      });
      if (validate) validateRunRecord(record);
      profileRecords.push(record);
      records.push(record);
    }
    const pricedTotalCostUsd = profileRecords.reduce((a, r) => a + r.costUsd, 0);
    campaigns[profileId] = {
      ...campaign,
      aggregates: { ...campaign.aggregates, totalCostUsd: pricedTotalCostUsd }
    };
    byProfile[profileId] = {
      profileId,
      profileHash,
      model,
      records: profileRecords.length,
      meanComposite: mean2(profileRecords.map(compositeOf)),
      totalCostUsd: pricedTotalCostUsd,
      integrity: summarizeBackendIntegrity(profileRecords)
    };
  }
  const integrity = summarizeBackendIntegrity(records);
  if (integrityMode === "assert") {
    assertRealBackend(records, { allowMixed: opts.allowMixed ?? true });
  } else if (integrityMode === "warn" && integrity.verdict !== "real") {
    console.warn(
      `[runProfileMatrix] backend integrity: ${integrity.verdict} \u2014 ${integrity.diagnosis}`
    );
  }
  const byScenario = rollup(records, (r) => r.scenarioId);
  const byPersona = opts.personaOf ? rollupByPersona(records, opts.scenarios, opts.personaOf) : void 0;
  return { matrixId, experimentId, records, byProfile, byScenario, byPersona, integrity, campaigns };
}
function compositeOf(r) {
  return r.outcome.holdoutScore ?? r.outcome.searchScore ?? 0;
}
function rollup(records, keyOf) {
  const groups = /* @__PURE__ */ new Map();
  for (const r of records) {
    const key = keyOf(r);
    if (key === void 0) continue;
    const arr = groups.get(key) ?? [];
    arr.push(compositeOf(r));
    groups.set(key, arr);
  }
  const out = {};
  for (const [key, xs] of groups) out[key] = { meanComposite: mean2(xs), n: xs.length };
  return out;
}
function rollupByPersona(records, scenarios, personaOf) {
  const personaByScenarioId = /* @__PURE__ */ new Map();
  for (const s of scenarios) personaByScenarioId.set(s.id, personaOf(s));
  return rollup(records, (r) => r.scenarioId ? personaByScenarioId.get(r.scenarioId) : void 0);
}

// src/campaign/proposers/_findings-text.ts
function findingToLesson(f) {
  if (typeof f === "string") return f.trim() || null;
  if (f && typeof f === "object") {
    const o = f;
    const cand = o.recommended_action ?? o.claim ?? o.lesson ?? o.text ?? o.message;
    if (typeof cand === "string" && cand.trim()) return cand.trim();
  }
  return null;
}
function normKey(s) {
  return s.toLowerCase().replace(/\s+/g, " ").replace(/[.;:!?\s]+$/, "").trim();
}
function surfaceToText2(surface) {
  if (typeof surface === "string") return surface;
  throw new Error(
    `curator proposer: surface must be a string prompt, got a ${surface.kind}-tier surface (${surface.worktreeRef}) \u2014 curation is prompt-tier`
  );
}
function extractBlockBody(text, startMarker, endMarker) {
  const start = text.indexOf(startMarker);
  const end = text.indexOf(endMarker);
  if (start === -1 || end === -1 || end < start) return "";
  return text.slice(start + startMarker.length, end);
}
function stripBlock(text, startMarker, endMarker) {
  const start = text.indexOf(startMarker);
  const end = text.indexOf(endMarker);
  if (start === -1 || end === -1 || end < start) return text.trimEnd();
  return (text.slice(0, start) + text.slice(end + endMarker.length)).trimEnd();
}

// src/campaign/proposers/ace.ts
var BLOCK_START = "<!-- BEGIN ace-playbook (auto-managed by aceProposer) -->";
var BLOCK_END = "<!-- END ace-playbook -->";
var DEFAULT_HEADING = "## Playbook (accumulated lessons \u2014 append-only)";
function parsePlaybook(surface) {
  const body = extractBlockBody(surface, BLOCK_START, BLOCK_END);
  const out = [];
  for (const raw of body.split("\n")) {
    const line = raw.trim();
    if (!line.startsWith("- ")) continue;
    const item = line.slice(2).trim();
    const tag = /^\[g(-?\d+)\]\s*(.*)$/.exec(item);
    if (tag) out.push({ gen: Number(tag[1]), text: tag[2].trim() });
    else out.push({ gen: -1, text: item });
  }
  return out;
}
function aceProposer(opts = {}) {
  const maxEntries = opts.maxEntries ?? 50;
  if (maxEntries < 1) throw new Error("aceProposer: maxEntries must be >= 1");
  const heading = opts.sectionHeading ?? DEFAULT_HEADING;
  return {
    kind: "ace",
    async propose(ctx) {
      const parent = surfaceToText2(ctx.currentSurface);
      const existing = parsePlaybook(parent);
      const seen = new Set(existing.map((b) => normKey(b.text)));
      const fresh = [];
      for (const f of ctx.findings ?? []) {
        const lesson = findingToLesson(f);
        if (!lesson) continue;
        const k = normKey(lesson);
        if (!k || seen.has(k)) continue;
        seen.add(k);
        fresh.push({ gen: ctx.generation, text: lesson });
      }
      if (fresh.length === 0) return [];
      const all = [...existing, ...fresh].slice(-maxEntries);
      const block = [
        BLOCK_START,
        heading,
        ...all.map((b) => `- [g${b.gen}] ${b.text}`),
        BLOCK_END
      ].join("\n");
      const base = stripBlock(parent, BLOCK_START, BLOCK_END);
      const surface = base ? `${base}

${block}` : block;
      return [
        {
          surface,
          label: `ace-playbook +${fresh.length}`,
          rationale: `appended ${fresh.length} new lesson(s) from gen ${ctx.generation} findings (playbook now ${all.length} bullet(s), append-only)`
        }
      ];
    }
  };
}

// src/campaign/proposers/halo.ts
import { execFile } from "child_process";
import { promisify } from "util";

// src/campaign/proposers/analysis-edit.ts
import { mkdtempSync, writeFileSync as writeFileSync2 } from "fs";
import { tmpdir } from "os";
import { join as join3 } from "path";
var APPLY_SYSTEM = "You apply a trace-analysis report to an agent instruction prompt. Output ONLY the full revised prompt \u2014 no preamble, no commentary, no code fences. Make the minimal edits that address the report findings; preserve everything else verbatim.";
function surfaceToPromptText(surface) {
  return typeof surface === "string" ? surface : JSON.stringify(surface);
}
function analysisEditProposer(opts) {
  return {
    kind: opts.kind,
    async propose(ctx) {
      const parent = surfaceToPromptText(ctx.currentSurface);
      const traces = await opts.resolveTraces(ctx) ?? "";
      if (!traces.trim()) throw new Error(opts.noTracesError);
      const dir = mkdtempSync(join3(tmpdir(), `${opts.kind}-proposer-`));
      const tracePath = join3(dir, "traces.jsonl");
      writeFileSync2(tracePath, traces.endsWith("\n") ? traces : `${traces}
`);
      const report = await opts.analyze(tracePath, ctx);
      const applied = await callLlm(
        {
          model: opts.applyModel,
          messages: [
            { role: "system", content: APPLY_SYSTEM },
            {
              role: "user",
              content: `CURRENT PROMPT:
${parent}

TRACE-ANALYSIS REPORT:
${report}

Return the full revised prompt.`
            }
          ]
        },
        { baseUrl: opts.baseUrl, apiKey: opts.apiKey, fetch: opts.fetchImpl }
      );
      const text = applied.content.trim();
      if (!text || text === parent) return [];
      return [{ surface: text, label: opts.label, rationale: opts.rationale(report) }];
    }
  };
}

// src/campaign/proposers/halo.ts
var execFileAsync = promisify(execFile);
var DEFAULT_ANALYSIS_PROMPT = "Diagnose the failures in these agent execution traces \u2014 hallucinated tool calls, redundant tool arguments, refusal loops, and semantic-correctness errors \u2014 and suggest concrete, generalizable fixes to the agent instructions.";
function haloProposer(opts) {
  const haloBin = opts.haloBin ?? "halo";
  const model = opts.model ?? "gpt-5.4-mini";
  return analysisEditProposer({
    kind: "halo",
    label: "halo",
    baseUrl: opts.baseUrl,
    apiKey: opts.apiKey,
    applyModel: opts.applyModel ?? model,
    fetchImpl: opts.fetchImpl,
    resolveTraces: opts.resolveTraces,
    noTracesError: "haloProposer: resolveTraces returned no OTLP traces \u2014 the halo engine has nothing to analyze",
    // HALO's real findings are preserved verbatim in the rationale (attribution).
    rationale: (findings) => `halo-engine findings:
${findings.slice(0, 800)}`,
    analyze: async (tracePath, ctx) => {
      const args = [
        tracePath,
        "-p",
        opts.analysisPrompt ?? DEFAULT_ANALYSIS_PROMPT,
        "-m",
        model,
        ...opts.maxDepth !== void 0 ? ["--max-depth", String(opts.maxDepth)] : [],
        ...opts.maxTurns !== void 0 ? ["--max-turns", String(opts.maxTurns)] : []
      ];
      let findings;
      try {
        const { stdout } = await execFileAsync(haloBin, args, {
          maxBuffer: 64 * 1024 * 1024,
          signal: ctx.signal,
          env: {
            ...process.env,
            ...opts.apiKey ? { OPENAI_API_KEY: opts.apiKey } : {},
            OPENAI_BASE_URL: opts.baseUrl
          }
        });
        findings = stdout.trim();
      } catch (e) {
        throw new Error(
          `haloProposer: halo-engine ('${haloBin}') failed \u2014 ${e instanceof Error ? e.message : String(e)}`
        );
      }
      if (!findings) throw new Error("haloProposer: halo-engine produced no findings");
      return findings;
    }
  });
}

// src/campaign/proposers/memory.ts
var BLOCK_START2 = "<!-- BEGIN curated-memory (auto-managed by memoryCurationProposer) -->";
var BLOCK_END2 = "<!-- END curated-memory -->";
var DEFAULT_HEADING2 = "## Learned from prior runs (curated memory)";
var DISTILL_SYSTEM = 'You compress raw trace-analysis findings into crisp, generalizable agent guidance. Output ONLY a JSON array of strings, each one imperative lesson the agent should follow (e.g. "Always fetch a resource before mutating it"). No prose outside the JSON. Deduplicate; keep the most actionable and general; drop case-specific noise.';
function extractExistingLessons(text) {
  return extractBlockBody(text, BLOCK_START2, BLOCK_END2).split("\n").map((l) => l.replace(/^\s*-\s+/, "").trim()).filter((l) => l && !l.startsWith("#"));
}
async function distillLessons(raw, distill) {
  const res = await callLlm(
    {
      model: distill.model,
      messages: [
        { role: "system", content: DISTILL_SYSTEM },
        { role: "user", content: `Findings:
${raw.map((r) => `- ${r}`).join("\n")}` }
      ]
    },
    { baseUrl: distill.baseUrl, apiKey: distill.apiKey, fetch: distill.fetchImpl }
  );
  try {
    const parsed = JSON.parse(res.content.trim());
    if (Array.isArray(parsed)) {
      const lessons = parsed.filter(
        (x) => typeof x === "string" && x.trim().length > 0
      );
      if (lessons.length > 0) return lessons;
    }
  } catch {
  }
  return raw;
}
function memoryCurationProposer(opts = {}) {
  const maxEntries = opts.maxEntries ?? 12;
  const heading = opts.sectionHeading ?? DEFAULT_HEADING2;
  return {
    kind: "memory-curation",
    async propose(ctx) {
      const parent = surfaceToText2(ctx.currentSurface);
      const fresh = [];
      for (const f of ctx.findings ?? []) {
        const l = findingToLesson(f);
        if (l) fresh.push(l);
      }
      const carried = extractExistingLessons(parent);
      if (fresh.length === 0 && carried.length === 0) return [];
      const distilled = opts.distill && fresh.length > 0 ? await distillLessons(fresh, opts.distill) : fresh;
      const byKey = /* @__PURE__ */ new Map();
      for (const l of carried) {
        const k = normKey(l);
        if (k) byKey.set(k, { text: l, count: 1 });
      }
      for (const l of distilled) {
        const k = normKey(l);
        if (!k) continue;
        const e = byKey.get(k);
        if (e) e.count += 1;
        else byKey.set(k, { text: l, count: 1 });
      }
      const ranked = [...byKey.values()].sort((a, b) => b.count - a.count || a.text.localeCompare(b.text)).slice(0, maxEntries);
      if (ranked.length === 0) return [];
      const block = [BLOCK_START2, heading, ...ranked.map((e) => `- ${e.text}`), BLOCK_END2].join(
        "\n"
      );
      const next = `${stripBlock(parent, BLOCK_START2, BLOCK_END2)}

${block}
`;
      if (next === parent) return [];
      return [
        {
          surface: next,
          label: "memory-curation",
          rationale: `curated ${ranked.length} lessons (from ${fresh.length} new finding(s) + ${carried.length} carried)`
        }
      ];
    }
  };
}

// src/campaign/proposers/trace-analyst.ts
import { ai } from "@ax-llm/ax";
function renderFindings(findings) {
  return findings.map((f, i) => {
    const action = f.recommended_action ? `
   FIX: ${f.recommended_action}` : "";
    const subject = f.subject ? ` (${f.subject})` : "";
    return `${i + 1}. [${f.severity}/${f.area}]${subject} ${f.claim}${action}`;
  }).join("\n");
}
function traceAnalystProposer(opts) {
  if (!opts.apiKey) throw new Error("traceAnalystProposer: apiKey is required");
  if (!opts.model) throw new Error("traceAnalystProposer: model is required");
  const kinds = opts.kinds ?? DEFAULT_TRACE_ANALYST_KINDS;
  const produceFindings = opts.analyze ?? (async (path, c) => {
    const aiService = ai({
      name: opts.provider ?? "openai",
      apiKey: opts.apiKey,
      apiURL: opts.baseUrl,
      config: { model: opts.model }
    });
    const registry = new AnalystRegistry();
    for (const spec of kinds) {
      registry.register(createTraceAnalystKind(spec, { ai: aiService, model: opts.model }));
    }
    const result = await registry.run(
      `trace-analyst-gen-${c.generation}`,
      { traceStore: new OtlpFileTraceStore({ path }) },
      { signal: c.signal }
    );
    return result.findings;
  });
  return analysisEditProposer({
    kind: "trace-analyst",
    label: "trace-analyst",
    baseUrl: opts.baseUrl,
    apiKey: opts.apiKey,
    applyModel: opts.applyModel ?? opts.model,
    fetchImpl: opts.fetchImpl,
    resolveTraces: opts.resolveTraces,
    noTracesError: "traceAnalystProposer: resolveTraces returned no OTLP traces \u2014 the analyst has nothing to read",
    rationale: (report) => `trace-analyst findings:
${report.slice(0, 800)}`,
    analyze: async (tracePath, ctx) => {
      let findings;
      try {
        findings = await produceFindings(tracePath, ctx);
      } catch (e) {
        throw new Error(
          `traceAnalystProposer: analyst engine failed \u2014 ${e instanceof Error ? e.message : String(e)}`
        );
      }
      if (findings.length === 0) {
        throw new Error("traceAnalystProposer: analyst engine produced no findings");
      }
      return renderFindings(findings);
    }
  });
}

// src/campaign/worktree/index.ts
import { execFileSync } from "child_process";
import { existsSync as existsSync2 } from "fs";
import { basename, isAbsolute, join as join4 } from "path";
var WorktreeAdapterError = class extends Error {
  constructor(message, cause) {
    super(message);
    this.cause = cause;
    this.name = "WorktreeAdapterError";
  }
  cause;
};
function defaultGit(args, cwd) {
  try {
    return execFileSync("git", args, { cwd, encoding: "utf8" }).trim();
  } catch (err) {
    const stderr = err && typeof err === "object" && "stderr" in err ? String(err.stderr) : "";
    throw new WorktreeAdapterError(`git ${args.join(" ")} failed: ${stderr || String(err)}`, err);
  }
}
function slug2(label) {
  return label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "candidate";
}
function gitWorktreeAdapter(opts) {
  const git = opts.git ?? defaultGit;
  const worktreeDir = opts.worktreeDir ?? join4(opts.repoRoot, ".worktrees");
  const branchPrefix = opts.branchPrefix ?? "improve";
  return {
    async create({ baseRef, label }) {
      const id = `${slug2(label)}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
      const branch = `${branchPrefix}/${id}`;
      const path = join4(worktreeDir, id);
      git(["worktree", "add", "-b", branch, path, baseRef], opts.repoRoot);
      return { path, branch, baseRef };
    },
    async finalize(worktree, summary) {
      const status = git(["status", "--porcelain"], worktree.path);
      if (status.length > 0) {
        git(["add", "-A"], worktree.path);
        git(["commit", "-m", summary], worktree.path);
      }
      return {
        kind: "code",
        worktreeRef: worktree.path,
        baseRef: worktree.baseRef,
        summary
      };
    },
    async discard(worktree) {
      git(["worktree", "remove", "--force", worktree.path], opts.repoRoot);
      git(["branch", "-D", worktree.branch], opts.repoRoot);
    }
  };
}
function resolveWorktreePath(surface, worktreeDir) {
  if (isAbsolute(surface.worktreeRef) && existsSync2(surface.worktreeRef)) return surface.worktreeRef;
  if (worktreeDir) return join4(worktreeDir, basename(surface.worktreeRef));
  return surface.worktreeRef;
}
export {
  FsLabeledScenarioStore,
  LabeledScenarioStoreError,
  ProfileMatrixError,
  SkillPatchParseError,
  WorktreeAdapterError,
  aceProposer,
  applySkillPatch,
  buildAnalystSurfaceDispatch,
  buildEvidenceVector,
  buildLoopProvenanceRecord,
  campaignBreakdown,
  campaignMeanComposite,
  compareProposers,
  composeGate,
  countSentenceEdits,
  defaultProductionGate,
  defaultRenderDiff,
  detectScale,
  dimensionRegressions,
  emitLoopProvenance,
  evolutionaryProposer,
  extractFapoAttributionSignals,
  extractH2Sections,
  failureModeRecallJudge,
  fapoEscalationEntry,
  fapoProposer,
  fsCampaignStorage,
  gepaParetoEntry,
  gepaProposer,
  gepaReflectionEntry,
  gitWorktreeAdapter,
  haloProposer,
  heldOutGate,
  heldoutSignificance,
  inMemoryCampaignStorage,
  isProposedCandidate,
  labelTrustRank,
  loopProvenanceSpans,
  makePlaybackDispatch,
  memoryCurationProposer,
  openAutoPr,
  pairHoldout,
  parameterSweepProposer,
  paretoPolicy,
  paretoSignificanceGate,
  parseSkillPatchResponse,
  patchEditCount,
  provenanceRecordPath,
  provenanceSpansPath,
  renderScoreboardMarkdown,
  resolveWorktreePath,
  runCampaign,
  runEval,
  runImprovementLoop,
  runOptimization,
  runProfileMatrix,
  runSkillOpt,
  scoreUserStory,
  scoreboardSummary,
  sequentialDecide,
  sequentialPairedGate,
  skillOptEntry,
  skillOptProposer,
  surfaceContentHash,
  surfaceHash,
  traceAnalystProposer,
  userStoryScoreboard
};
//# sourceMappingURL=index.js.map