import {
  attributeCounterfactuals,
  runCounterfactual
} from "./chunk-REVYNR6C.js";
import {
  buildTrajectory
} from "./chunk-RZTMDUO7.js";
import {
  makeFinding
} from "./chunk-45EEMHTC.js";
import {
  confidenceInterval
} from "./chunk-HRGUJTER.js";
import {
  validateRunRecord
} from "./chunk-LO6IOIJ2.js";
import "./chunk-TVVP3ZZQ.js";
import "./chunk-VSMTAMNK.js";
import {
  ValidationError
} from "./chunk-3BFEG2F6.js";
import "./chunk-PZ5AY32C.js";

// src/diagnose/causal-sweep.ts
function stepRefOf(step) {
  return {
    index: step.index,
    spanId: step.span.spanId,
    kind: step.span.kind,
    name: step.span.name
  };
}
var DEFAULT_CI_SEED = 24301;
function defaultMutations(step) {
  if (step.span.kind === "tool") {
    return [{ kind: "swap-tool-result", at: step.index, newResult: null }];
  }
  if (step.span.kind === "llm") {
    return [{ kind: "truncate-after", at: step.index }];
  }
  return [];
}
async function causalSweep(opts) {
  if (!Number.isInteger(opts.reps) || opts.reps < 2) {
    throw new ValidationError(
      `causalSweep: reps must be an integer >= 2 (got ${opts.reps}) \u2014 a single-intervention delta is one stochastic draw, not a measurement`
    );
  }
  if (!Number.isInteger(opts.budget) || opts.budget < 1) {
    throw new ValidationError(`causalSweep: budget must be an integer >= 1 (got ${opts.budget})`);
  }
  const originalRun = await opts.store.getRun(opts.runId);
  if (!originalRun) throw new ValidationError(`causalSweep: run ${opts.runId} not found`);
  const originalScore = originalRun.outcome?.score;
  if (typeof originalScore !== "number" || !Number.isFinite(originalScore)) {
    throw new ValidationError(
      `causalSweep: run ${opts.runId} has no numeric outcome.score \u2014 deltas have no baseline`
    );
  }
  const trajectory = await buildTrajectory(opts.store, opts.runId);
  const candidates = resolveCandidates(trajectory, opts.candidateSteps);
  const mutationsFor = opts.mutationsPerStep ?? defaultMutations;
  const cells = [];
  for (const step of candidates) {
    const mutations = mutationsFor(step);
    for (const m of mutations) {
      if (m.at !== step.index) {
        throw new ValidationError(
          `causalSweep: mutationsPerStep returned a mutation targeting at=${m.at} for step index=${step.index} \u2014 mutations must target the step they were asked for`
        );
      }
      cells.push({ step, mutation: m });
    }
  }
  const responsibilities = [];
  const allResults = [];
  const uncoveredIndices = /* @__PURE__ */ new Set();
  let replaysUsed = 0;
  let halted = false;
  for (const cell of cells) {
    if (halted || replaysUsed + opts.reps > opts.budget) {
      halted = true;
      uncoveredIndices.add(cell.step.index);
      continue;
    }
    const deltas = [];
    const cfRunIds = [];
    for (let rep = 0; rep < opts.reps; rep++) {
      const result = await runCounterfactual(opts.store, opts.runId, cell.mutation, opts.runner);
      replaysUsed++;
      const d = result.delta.deltaScore;
      if (typeof d !== "number" || !Number.isFinite(d)) {
        throw new ValidationError(
          `causalSweep: counterfactual replay for step ${cell.step.index} (${cell.mutation.kind}) rep ${rep} produced no numeric score \u2014 the runner must endRun with a numeric outcome.score`
        );
      }
      deltas.push(d);
      cfRunIds.push(result.counterfactualRunId);
      allResults.push(result);
    }
    const ci = confidenceInterval(deltas, opts.ciConfidence ?? 0.95, {
      seed: opts.ciSeed ?? DEFAULT_CI_SEED
    });
    responsibilities.push({
      stepRef: stepRefOf(cell.step),
      mutationKind: cell.mutation.kind,
      meanEffect: ci.mean,
      ci,
      ciExcludesZero: ci.lower > 0 || ci.upper < 0,
      reps: opts.reps,
      deltas,
      counterfactualRunIds: cfRunIds
    });
  }
  responsibilities.sort((a, b) => Math.abs(b.meanEffect) - Math.abs(a.meanEffect));
  const uncovered = candidates.filter((s) => uncoveredIndices.has(s.index)).map(stepRefOf);
  return {
    runId: opts.runId,
    originalScore,
    steps: responsibilities,
    byMutationKind: attributeCounterfactuals(allResults),
    replaysUsed,
    budget: opts.budget,
    uncovered
  };
}
function resolveCandidates(trajectory, indices) {
  if (indices === void 0) {
    return trajectory.steps.filter((s) => s.span.kind === "llm" || s.span.kind === "tool");
  }
  return indices.map((i) => {
    const step = trajectory.steps[i];
    if (!step) {
      throw new ValidationError(
        `causalSweep: candidateSteps index ${i} out of range [0, ${trajectory.steps.length})`
      );
    }
    return step;
  });
}

// src/diagnose/remediation.ts
var DIAGNOSE_ANALYST_ID = "diagnose-causal-sweep";
function severityFromEffect(responsibility) {
  if (!responsibility.ciExcludesZero) return "info";
  const magnitude = Math.abs(responsibility.meanEffect);
  if (magnitude >= 0.5) return "critical";
  if (magnitude >= 0.25) return "high";
  if (magnitude >= 0.1) return "medium";
  return "low";
}
function describeMutation(mutation) {
  switch (mutation.kind) {
    case "swap-model":
      return `use model '${mutation.newModel}' at step ${mutation.at}`;
    case "swap-tool-result":
      return `replace the tool result at step ${mutation.at} with ${JSON.stringify(mutation.newResult)}`;
    case "truncate-after":
      return `stop the run after step ${mutation.at}`;
    case "inject-system-message":
      return `inject system message at step ${mutation.at}: ${mutation.content}`;
    case "custom":
      return `${mutation.describe} (step ${mutation.at})`;
  }
}
function toAnalystFindings(report, repairs) {
  const repairByStep = /* @__PURE__ */ new Map();
  for (const r of repairs?.repairs ?? []) {
    if (!repairByStep.has(r.stepRef.spanId)) repairByStep.set(r.stepRef.spanId, r);
  }
  return report.steps.map((resp) => {
    const repair = repairByStep.get(resp.stepRef.spanId);
    const evidence = [
      {
        kind: "span",
        uri: `span://${resp.stepRef.spanId}`,
        excerpt: `step ${resp.stepRef.index} (${resp.stepRef.kind} '${resp.stepRef.name}') meanEffect=${resp.meanEffect.toFixed(4)} ci=[${resp.ci.lower.toFixed(4)}, ${resp.ci.upper.toFixed(4)}] reps=${resp.reps}`
      },
      {
        kind: "metric",
        uri: `metric://diagnose/${report.runId}/step/${resp.stepRef.index}/${resp.mutationKind}`,
        excerpt: `deltas=[${resp.deltas.map((d) => d.toFixed(4)).join(", ")}]`
      },
      ...resp.counterfactualRunIds.map((id) => ({ kind: "span", uri: `run://${id}` }))
    ];
    return makeFinding({
      analyst_id: DIAGNOSE_ANALYST_ID,
      severity: severityFromEffect(resp),
      area: "causal-attribution",
      claim: `step '${resp.stepRef.name}' (${resp.stepRef.kind}) is causally responsible for the run outcome under ${resp.mutationKind}`,
      rationale: resp.ciExcludesZero ? `mean effect ${resp.meanEffect.toFixed(4)} over ${resp.reps} counterfactual replays; CI [${resp.ci.lower.toFixed(4)}, ${resp.ci.upper.toFixed(4)}] excludes zero` : `mean effect ${resp.meanEffect.toFixed(4)} over ${resp.reps} counterfactual replays; CI [${resp.ci.lower.toFixed(4)}, ${resp.ci.upper.toFixed(4)}] includes zero \u2014 not distinguishable from noise`,
      evidence_refs: evidence,
      recommended_action: repair ? describeMutation(repair.mutation) : void 0,
      validation_plan: repair ? `replay-validated: ${repair.reps}/${repair.reps} reps scored >= ${repairs.flipThreshold} (mean ${repair.meanScore.toFixed(4)}, delta ${repair.deltaScore.toFixed(4)})` : void 0,
      confidence: repair ? 0.95 : resp.ciExcludesZero ? 0.85 : 0.3,
      subject: resp.stepRef.spanId,
      metadata: {
        stepRef: resp.stepRef,
        mutationKind: resp.mutationKind,
        meanEffect: resp.meanEffect,
        ci: resp.ci,
        deltas: resp.deltas,
        counterfactualRunIds: resp.counterfactualRunIds,
        ...repair ? { repair: { mutation: repair.mutation, meanScore: repair.meanScore } } : {}
      }
    });
  });
}
function toCorpusRecord(run, repair, opts = {}) {
  const record = {
    ...run,
    runId: `${run.runId}#repair:${repair.stepRef.spanId}`,
    outcome: {
      ...run.outcome,
      raw: {
        ...run.outcome.raw,
        diagnose_blamed_step_index: repair.stepRef.index,
        diagnose_repair_mean_score: repair.meanScore,
        diagnose_repair_delta_score: repair.deltaScore,
        diagnose_repair_reps: repair.reps
      }
    },
    prompt: opts.prompt,
    completion: opts.completion ?? describeMutation(repair.mutation)
  };
  validateRunRecord(record);
  return record;
}
function suggestInvariant(repair) {
  const { stepRef, mutation } = repair;
  const at = `step ${stepRef.index} (${stepRef.kind} '${stepRef.name}')`;
  switch (mutation.kind) {
    case "swap-tool-result":
      return {
        description: `the result of tool '${stepRef.name}' was causally responsible for the failure; a replaced result flipped the outcome (delta ${repair.deltaScore.toFixed(4)})`,
        never: `unvalidated result from tool '${stepRef.name}' flows downstream`,
        without: `result guard on tool '${stepRef.name}'`
      };
    case "swap-model":
      return {
        description: `swapping the model at ${at} to '${mutation.newModel}' flipped the outcome (delta ${repair.deltaScore.toFixed(4)})`,
        never: `llm span '${stepRef.name}' runs on a model other than '${mutation.newModel}'`
      };
    case "inject-system-message":
      return {
        description: `injecting a system message at ${at} flipped the outcome (delta ${repair.deltaScore.toFixed(4)})`,
        without: `system message present at '${stepRef.name}': ${mutation.content}`
      };
    case "truncate-after":
      return {
        description: `stopping after ${at} flipped the outcome (delta ${repair.deltaScore.toFixed(4)}) \u2014 continuation past this step caused the failure`,
        never: `spans execute after '${stepRef.name}' (index ${stepRef.index})`
      };
    case "custom":
      return {
        description: `${mutation.describe} at ${at} flipped the outcome (delta ${repair.deltaScore.toFixed(4)})`
      };
    default: {
      const exhausted = mutation;
      throw new ValidationError(
        `suggestInvariant: unknown mutation kind ${JSON.stringify(exhausted)}`
      );
    }
  }
}

// src/diagnose/repair.ts
async function prescribeRepair(opts) {
  const flipThreshold = opts.flipThreshold ?? 0.5;
  const repsToValidate = opts.repsToValidate ?? 3;
  if (!Number.isInteger(repsToValidate) || repsToValidate < 1) {
    throw new ValidationError(
      `prescribeRepair: repsToValidate must be an integer >= 1 (got ${repsToValidate})`
    );
  }
  const maxAttempts = opts.maxAttemptsPerStep ?? Number.POSITIVE_INFINITY;
  if (maxAttempts < 1) {
    throw new ValidationError(
      `prescribeRepair: maxAttemptsPerStep must be >= 1 (got ${opts.maxAttemptsPerStep})`
    );
  }
  if (opts.blamed.length === 0) {
    throw new ValidationError("prescribeRepair: blamed is empty \u2014 nothing to repair");
  }
  const originalRun = await opts.store.getRun(opts.runId);
  if (!originalRun) throw new ValidationError(`prescribeRepair: run ${opts.runId} not found`);
  const originalScore = originalRun.outcome?.score;
  if (typeof originalScore !== "number" || !Number.isFinite(originalScore)) {
    throw new ValidationError(
      `prescribeRepair: run ${opts.runId} has no numeric outcome.score \u2014 flips have no baseline`
    );
  }
  const trajectory = await buildTrajectory(opts.store, opts.runId);
  const repairs = [];
  const rejected = [];
  let replaysUsed = 0;
  for (const responsibility of opts.blamed) {
    const step = trajectory.steps[responsibility.stepRef.index];
    if (!step || step.span.spanId !== responsibility.stepRef.spanId) {
      throw new ValidationError(
        `prescribeRepair: blamed step index=${responsibility.stepRef.index} spanId=${responsibility.stepRef.spanId} does not match run ${opts.runId} \u2014 stale report?`
      );
    }
    const candidates = await opts.proposeFix(step, {
      runId: opts.runId,
      trajectory,
      originalScore,
      responsibility
    });
    const toTry = candidates.slice(0, maxAttempts);
    for (const mutation of toTry) {
      if (mutation.at !== step.index) {
        throw new ValidationError(
          `prescribeRepair: proposeFix returned a mutation targeting at=${mutation.at} for blamed step index=${step.index}`
        );
      }
      const scores = [];
      const cfRunIds = [];
      let failure;
      for (let rep = 0; rep < repsToValidate; rep++) {
        try {
          const result = await runCounterfactual(opts.store, opts.runId, mutation, opts.runner);
          replaysUsed++;
          const score = result.delta.counterfactualOutcomeScore;
          if (typeof score !== "number" || !Number.isFinite(score)) {
            failure = `validation rep ${rep} produced no numeric score \u2014 the runner must endRun with a numeric outcome.score`;
            break;
          }
          scores.push(score);
          cfRunIds.push(result.counterfactualRunId);
        } catch (err) {
          replaysUsed++;
          failure = err instanceof Error ? err.message : String(err);
          break;
        }
      }
      if (failure !== void 0) {
        rejected.push({
          stepRef: responsibility.stepRef,
          mutation,
          reason: "error",
          error: failure
        });
        continue;
      }
      const meanScore = scores.reduce((a, b) => a + b, 0) / scores.length;
      const everyRepFlipped = scores.every((s) => s >= flipThreshold);
      if (everyRepFlipped) {
        repairs.push({
          stepRef: responsibility.stepRef,
          mutation,
          validated: true,
          meanScore,
          deltaScore: meanScore - originalScore,
          reps: repsToValidate,
          counterfactualRunIds: cfRunIds
        });
        break;
      }
      rejected.push({
        stepRef: responsibility.stepRef,
        mutation,
        reason: "did-not-flip",
        deltaScore: meanScore - originalScore
      });
    }
  }
  return { runId: opts.runId, originalScore, flipThreshold, repairs, rejected, replaysUsed };
}
export {
  DIAGNOSE_ANALYST_ID,
  causalSweep,
  describeMutation,
  prescribeRepair,
  severityFromEffect,
  stepRefOf,
  suggestInvariant,
  toAnalystFindings,
  toCorpusRecord
};
//# sourceMappingURL=diagnose.js.map