import {
  buildTrajectory
} from "./chunk-RZTMDUO7.js";
import {
  TraceEmitter
} from "./chunk-TVVP3ZZQ.js";
import {
  NotFoundError,
  ValidationError
} from "./chunk-3BFEG2F6.js";

// src/counterfactual.ts
async function runCounterfactual(store, originalRunId, mutation, runner) {
  const originalRun = await store.getRun(originalRunId);
  if (!originalRun) throw new NotFoundError(`counterfactual: run ${originalRunId} not found`);
  const trajectory = await buildTrajectory(store, originalRunId);
  if (mutation.at < 0 || mutation.at >= trajectory.steps.length) {
    throw new ValidationError(
      `counterfactual: mutation.at=${mutation.at} out of range [0, ${trajectory.steps.length})`
    );
  }
  const targetStep = trajectory.steps[mutation.at];
  const mutatedStep = applyMutation(targetStep, mutation);
  const cfEmitter = new TraceEmitter(store);
  await cfEmitter.startRun({
    scenarioId: originalRun.scenarioId,
    variantId: originalRun.variantId ? `${originalRun.variantId}+cf:${mutation.kind}@${mutation.at}` : `cf:${mutation.kind}@${mutation.at}`,
    projectId: originalRun.projectId,
    parentRunId: originalRunId,
    layer: "meta",
    tags: { counterfactual: "true", mutationKind: mutation.kind, mutationAt: String(mutation.at) }
  });
  await runner.executeFrom(
    {
      originalRunId,
      originalTrajectory: trajectory,
      prefix: trajectory.steps.slice(0, mutation.at),
      mutation,
      mutatedStep
    },
    cfEmitter
  );
  const counterfactual = await store.getRun(cfEmitter.runId);
  const delta = {
    originalOutcomeScore: originalRun.outcome?.score ?? null,
    counterfactualOutcomeScore: counterfactual?.outcome?.score ?? null,
    deltaScore: originalRun.outcome?.score !== void 0 && counterfactual?.outcome?.score !== void 0 ? counterfactual.outcome.score - originalRun.outcome.score : null
  };
  return { counterfactualRunId: cfEmitter.runId, originalRunId, mutation, delta };
}
function applyMutation(step, mutation) {
  if (mutation.kind === "swap-model" && step.span.kind === "llm") {
    const llm = step.span;
    return { ...step, span: { ...llm, model: mutation.newModel } };
  }
  if (mutation.kind === "swap-tool-result" && step.span.kind === "tool") {
    const tool = step.span;
    return { ...step, span: { ...tool, result: mutation.newResult } };
  }
  if (mutation.kind === "inject-system-message" && step.span.kind === "llm") {
    const llm = step.span;
    return {
      ...step,
      span: {
        ...llm,
        messages: [{ role: "system", content: mutation.content }, ...llm.messages]
      }
    };
  }
  if (mutation.kind === "custom") return mutation.apply(step);
  return step;
}
function attributeCounterfactuals(results) {
  const grouped = /* @__PURE__ */ new Map();
  for (const r of results) {
    const arr = grouped.get(r.mutation.kind) ?? [];
    arr.push(r);
    grouped.set(r.mutation.kind, arr);
  }
  const out = [];
  for (const [kind, items] of grouped) {
    const deltas = items.map((i) => i.delta.deltaScore).filter((d) => typeof d === "number");
    if (deltas.length === 0) continue;
    const meanAbs = deltas.reduce((a, b) => a + Math.abs(b), 0) / deltas.length;
    const meanSigned = deltas.reduce((a, b) => a + b, 0) / deltas.length;
    out.push({
      mutationKind: kind,
      n: deltas.length,
      meanAbsDelta: meanAbs,
      meanSignedDelta: meanSigned
    });
  }
  return out.sort((a, b) => b.meanAbsDelta - a.meanAbsDelta);
}

export {
  runCounterfactual,
  attributeCounterfactuals
};
//# sourceMappingURL=chunk-REVYNR6C.js.map