import {
  confidenceInterval
} from "./chunk-S3SDD56V.js";
import {
  __require
} from "./chunk-NSBPE2FW.js";

// src/campaign/run-campaign.ts
import { createHash } from "crypto";
import { join } from "path";

// src/campaign/storage.ts
function fsCampaignStorage() {
  const { existsSync, mkdirSync, readFileSync, writeFileSync } = __require("fs");
  return {
    ensureDir(dir) {
      if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
    },
    exists(path) {
      return existsSync(path);
    },
    read(path) {
      try {
        return readFileSync(path, "utf8");
      } catch {
        return void 0;
      }
    },
    write(path, content) {
      writeFileSync(path, content);
    }
  };
}
function inMemoryCampaignStorage() {
  const files = /* @__PURE__ */ new Map();
  const dirs = /* @__PURE__ */ new Set();
  return {
    ensureDir(dir) {
      dirs.add(dir);
    },
    exists(path) {
      return files.has(path) || dirs.has(path);
    },
    read(path) {
      const value = files.get(path);
      if (value === void 0) return void 0;
      return typeof value === "string" ? value : new TextDecoder().decode(value);
    },
    write(path, content) {
      files.set(path, content);
    }
  };
}

// src/campaign/run-campaign.ts
async function runCampaign(opts) {
  const seed = opts.seed ?? 42;
  const reps = opts.reps ?? 1;
  const resumable = opts.resumable ?? true;
  const maxConcurrency = opts.maxConcurrency ?? 2;
  const now = opts.now ?? (() => /* @__PURE__ */ new Date());
  const judges = opts.judges ?? [];
  const storage = opts.storage ?? fsCampaignStorage();
  storage.ensureDir(opts.runDir);
  const manifestHash = computeManifestHash({
    scenarios: opts.scenarios,
    judges,
    dispatchRef: opts.dispatch.name || "anonymous",
    seed,
    reps
  });
  const startedAt = now();
  const cells = [];
  const artifactsByPath = {};
  const schedule = [];
  let cellIndex = 0;
  for (const scenario of opts.scenarios) {
    for (let rep = 0; rep < reps; rep++) {
      const cellId = `${scenario.id}:${rep}`;
      const cellSeed = seed + cellIndex;
      schedule.push({ scenario, rep, cellId, cellSeed });
      cellIndex += 1;
    }
  }
  let totalCostUsd = 0;
  let costCeilingReached = false;
  const abortController = new AbortController();
  const lanes = [];
  let nextIdx = 0;
  const cellsRef = cells;
  for (let i = 0; i < maxConcurrency; i++) {
    lanes.push(
      (async () => {
        while (true) {
          const myIdx = nextIdx++;
          if (myIdx >= schedule.length) return;
          const slot = schedule[myIdx];
          if (costCeilingReached) {
            cellsRef.push(skippedCell(slot, "cost_ceiling_reached"));
            continue;
          }
          const result = await executeCell({
            slot,
            opts,
            manifestHash,
            resumable,
            now,
            storage,
            buildTraceWriter: opts.buildTraceWriter ?? defaultBuildTraceWriter(storage),
            signal: abortController.signal
          });
          cellsRef.push(result.cell);
          totalCostUsd += result.cell.costUsd;
          Object.assign(artifactsByPath, result.artifactsByPath);
          if (opts.costCeiling !== void 0 && totalCostUsd >= opts.costCeiling) {
            costCeilingReached = true;
          }
          if (opts.labeledStore && opts.labeledStore !== "off" && !result.cell.error) {
            await captureToStore({
              store: opts.labeledStore,
              cell: result.cell,
              scenario: slot.scenario,
              opts,
              now
            }).catch((err) => {
              console.warn(
                `[runCampaign] capture failed for ${result.cell.cellId}: ${err instanceof Error ? err.message : String(err)}`
              );
            });
          }
        }
      })()
    );
  }
  await Promise.all(lanes);
  const endedAt = now();
  cellsRef.sort((a, b) => a.cellId.localeCompare(b.cellId));
  const aggregates = computeAggregates(
    cellsRef,
    judges,
    seed
  );
  return {
    manifestHash,
    seed,
    startedAt: startedAt.toISOString(),
    endedAt: endedAt.toISOString(),
    durationMs: endedAt.getTime() - startedAt.getTime(),
    cells: cellsRef,
    aggregates,
    runDir: opts.runDir,
    artifactsByPath,
    scenarios: opts.scenarios.map((s) => ({ id: s.id, kind: s.kind }))
  };
}
async function executeCell(args) {
  const storage = args.storage;
  const cellDir = join(args.opts.runDir, args.slot.cellId.replace(/[^a-zA-Z0-9_-]/g, "_"));
  storage.ensureDir(cellDir);
  const cachePath = join(cellDir, "cached-result.json");
  if (args.resumable) {
    const raw = storage.read(cachePath);
    if (raw !== void 0) {
      try {
        const cached = JSON.parse(raw);
        if (cached.cellId === args.slot.cellId) {
          return { cell: { ...cached, cached: true }, artifactsByPath: {} };
        }
      } catch {
      }
    }
  }
  const startMs = Date.now();
  const trace = args.buildTraceWriter(args.slot.cellId, cellDir);
  const artifactsByPath = {};
  const artifacts = {
    async write(path, content) {
      const fullPath = join(cellDir, path);
      storage.ensureDir(join(fullPath, ".."));
      storage.write(fullPath, content);
      artifactsByPath[`${args.slot.cellId}/${path}`] = fullPath;
      return fullPath;
    },
    async writeJson(path, value) {
      return artifacts.write(path, JSON.stringify(value, null, 2));
    }
  };
  let costSoFar = 0;
  const cost = {
    observe(amount, source) {
      costSoFar += amount;
      trace.span(`cost.${source}`, { amountUsd: amount }).end();
    },
    current() {
      return costSoFar;
    }
  };
  const placement = args.opts.cellPlacement?.({
    scenario: args.slot.scenario,
    rep: args.slot.rep
  });
  const ctx = {
    cellId: args.slot.cellId,
    rep: args.slot.rep,
    seed: args.slot.cellSeed,
    signal: args.signal,
    trace,
    artifacts,
    cost,
    placement
  };
  let artifact;
  let errorMessage;
  try {
    artifact = await args.opts.dispatch(args.slot.scenario, ctx);
  } catch (err) {
    errorMessage = err instanceof Error ? err.message : String(err);
  }
  const judgeScores = {};
  if (artifact !== void 0) {
    for (const judge of args.opts.judges ?? []) {
      if (judge.appliesTo && !judge.appliesTo(args.slot.scenario)) continue;
      try {
        judgeScores[judge.name] = await runJudgeCell(judge, {
          artifact,
          scenario: args.slot.scenario,
          signal: args.signal
        });
      } catch (err) {
        errorMessage = `judge '${judge.name}' failed: ${err instanceof Error ? err.message : String(err)}`;
        break;
      }
    }
  }
  await trace.flush();
  const cell = {
    cellId: args.slot.cellId,
    scenarioId: args.slot.scenario.id,
    rep: args.slot.rep,
    artifact: artifact ?? null,
    judgeScores,
    costUsd: costSoFar,
    durationMs: Date.now() - startMs,
    seed: args.slot.cellSeed,
    cached: false,
    error: errorMessage
  };
  if (!errorMessage && args.resumable) {
    storage.write(cachePath, JSON.stringify(cell));
  }
  return { cell, artifactsByPath };
}
async function runJudgeCell(judge, input) {
  return judge.score(input);
}
function defaultBuildTraceWriter(storage) {
  return (cellId, dir) => {
    const spans = [];
    return {
      span(name, attributes) {
        const startMs = Date.now();
        const record = { name, cellId, startMs, ...attributes ?? {} };
        const finish = {
          end(endAttrs) {
            record.durationMs = Date.now() - startMs;
            if (endAttrs) Object.assign(record, endAttrs);
            spans.push(record);
          },
          setAttribute(key, value) {
            record[key] = value;
          }
        };
        return finish;
      },
      async flush() {
        storage.write(join(dir, "spans.jsonl"), spans.map((s) => JSON.stringify(s)).join("\n"));
      }
    };
  };
}
function skippedCell(slot, reason) {
  return {
    cellId: slot.cellId,
    scenarioId: slot.scenario.id,
    rep: slot.rep,
    artifact: null,
    judgeScores: {},
    costUsd: 0,
    durationMs: 0,
    seed: slot.cellSeed,
    cached: false,
    error: `skipped: ${reason}`
  };
}
async function captureToStore(args) {
  await args.store.observe({
    scenario: args.scenario,
    artifact: args.cell.artifact,
    judgeScores: args.cell.judgeScores,
    source: args.opts.captureSource ?? "eval-run",
    sourceVersionHash: args.opts.captureSourceVersionHash ?? "unknown",
    capturedAt: args.now().toISOString(),
    redactionStatus: "raw"
  });
}
function computeManifestHash(input) {
  const canonical = {
    scenarios: input.scenarios.map((s) => ({ id: s.id, kind: s.kind })),
    judges: input.judges.map((j) => ({ name: j.name, dims: j.dimensions.map((d) => d.key) })),
    dispatch: input.dispatchRef,
    seed: input.seed,
    reps: input.reps
  };
  return createHash("sha256").update(JSON.stringify(canonical)).digest("hex");
}
function computeAggregates(cells, judges, seed) {
  const byJudge = {};
  for (const judge of judges) {
    const scores = [];
    for (const cell of cells) {
      const s = cell.judgeScores[judge.name];
      if (s !== void 0) scores.push(s.composite);
    }
    byJudge[judge.name] = aggregate(scores, seed);
  }
  const byScenario = {};
  const scenarioGroups = /* @__PURE__ */ new Map();
  for (const cell of cells) {
    const composites = Object.values(cell.judgeScores).map((s) => s.composite);
    if (composites.length === 0) continue;
    const mean = composites.reduce((a, b) => a + b, 0) / composites.length;
    const arr = scenarioGroups.get(cell.scenarioId) ?? [];
    arr.push(mean);
    scenarioGroups.set(cell.scenarioId, arr);
  }
  for (const [scenarioId, samples] of scenarioGroups) {
    const ag = aggregate(samples, seed);
    byScenario[scenarioId] = { meanComposite: ag.mean, ci95: ag.ci95, n: ag.n };
  }
  return {
    byJudge,
    byScenario,
    totalCostUsd: cells.reduce((a, c) => a + c.costUsd, 0),
    cellsExecuted: cells.filter((c) => !c.error).length,
    cellsSkipped: cells.filter((c) => c.error?.startsWith("skipped:")).length,
    cellsCached: cells.filter((c) => c.cached).length,
    cellsFailed: cells.filter((c) => c.error && !c.error.startsWith("skipped:")).length
  };
}
function aggregate(samples, seed) {
  const n = samples.length;
  if (n === 0) return { mean: 0, stdev: 0, ci95: [0, 0], n: 0 };
  const mean = samples.reduce((a, b) => a + b, 0) / n;
  const variance = samples.reduce((a, b) => a + (b - mean) ** 2, 0) / Math.max(1, n - 1);
  const stdev = Math.sqrt(variance);
  const ci = confidenceInterval(samples, 0.95, { seed, resamples: 1e3 });
  return { mean, stdev, ci95: [ci.lower, ci.upper], n };
}

export {
  fsCampaignStorage,
  inMemoryCampaignStorage,
  runCampaign
};
//# sourceMappingURL=chunk-74Y2EMNH.js.map