import {
  SandboxHarness,
  runTestGradedScenario
} from "../chunk-K6N6XJJX.js";
import {
  pearsonR,
  spearmanR
} from "../chunk-PJQFMIOX.js";
import {
  judgeSpans
} from "../chunk-ZET2UAYW.js";
import {
  TraceEmitter
} from "../chunk-VQMK5FMP.js";
import "../chunk-MA6HLL3S.js";
import "../chunk-ONWEPEDO.js";
import "../chunk-PZ5AY32C.js";

// src/builder-eval/builder-session.ts
var BuilderSession = class {
  store;
  builderEmitter;
  projectId;
  chatId;
  builderRunId;
  lastBuildRunId;
  defaultDriver;
  constructor(store, init, driver) {
    this.store = store;
    this.projectId = init.projectId;
    this.chatId = init.chatId ?? cryptoId();
    this.defaultDriver = driver;
    this.builderEmitter = new TraceEmitter(store);
  }
  /** Start the builder (L0) run for this chat. Returns the runId. */
  async startChat(scenarioId = `${this.projectId}/chat`) {
    await this.builderEmitter.startRun({
      scenarioId,
      projectId: this.projectId,
      chatId: this.chatId,
      layer: "builder"
    });
    this.builderRunId = this.builderEmitter.runId;
    return this.builderRunId;
  }
  /** The emitter for builder-level spans (edits, LLM calls, tool invocations). */
  get emitter() {
    if (!this.builderRunId) throw new Error("BuilderSession.emitter: call startChat() first");
    return this.builderEmitter;
  }
  /**
   * Ship the project's generated app: run the sandbox harness as a child
   * Run (`layer='app-build'`). Returns the build result + runId.
   */
  async ship(options) {
    if (!this.builderRunId) throw new Error("BuilderSession.ship: call startChat() first");
    const buildEmitter = new TraceEmitter(this.store);
    await buildEmitter.startRun({
      scenarioId: options.scenarioId ?? `${this.projectId}/build`,
      projectId: this.projectId,
      chatId: this.chatId,
      parentRunId: this.builderRunId,
      layer: "app-build"
    });
    const harness = new SandboxHarness(options.driver ?? this.defaultDriver);
    const result = await harness.run(options.harness, buildEmitter);
    await buildEmitter.endRun({
      pass: result.passed,
      score: result.score,
      failureClass: result.passed ? "success" : "sandbox_failure"
    });
    this.lastBuildRunId = buildEmitter.runId;
    return { runId: buildEmitter.runId, result };
  }
  /**
   * Run a domain scenario against the just-built app as a grandchild Run
   * (`layer='app-runtime'`). The `ship` call must precede this so the
   * parent is set correctly; if no build exists yet the session attaches
   * directly to the builder run (useful for prototypes).
   */
  async runAppScenario(options) {
    const parentRunId = this.lastBuildRunId ?? this.builderRunId;
    if (!parentRunId)
      throw new Error("BuilderSession.runAppScenario: call startChat() + ship() first");
    const { scenario, driver } = options;
    const result = await runTestGradedScenario(scenario, this.store, {
      driver: driver ?? this.defaultDriver,
      provenance: { codeSha: void 0, promptSha: void 0, modelFingerprint: void 0 }
    });
    await this.store.updateRun(result.runId, {
      parentRunId,
      projectId: this.projectId,
      chatId: this.chatId,
      layer: "app-runtime"
    });
    return result;
  }
  /** Record an end-of-chat meta score (judge verdict on whether the builder
   *  served the user's intent). Accepts a numeric score + optional rationale. */
  async recordMetaScore(score, rationale) {
    if (!this.builderRunId)
      throw new Error("BuilderSession.recordMetaScore: call startChat() first");
    await this.builderEmitter.recordJudge({
      judgeId: "builder-meta",
      targetSpanId: this.builderRunId,
      // attach to the builder run itself
      dimension: "user_intent_satisfaction",
      score,
      rationale,
      name: "builder-meta"
    });
  }
  /** Close the builder Run with a final outcome. */
  async endChat(outcome) {
    await this.builderEmitter.endRun({
      pass: outcome.pass,
      score: outcome.score,
      notes: outcome.notes
    });
  }
  /**
   * Inline app-runtime run — for cases where the "scenario" isn't a
   * SWE-bench-style test suite but a live agent interaction (LLM chat,
   * domain flow). Returns an emitter bound to a fresh Run in the
   * `app-runtime` layer; caller emits spans inside and calls
   * `.endRun()` with the final verdict.
   */
  async startAppRuntime(scenarioId) {
    const parentRunId = this.lastBuildRunId ?? this.builderRunId;
    if (!parentRunId)
      throw new Error(
        "BuilderSession.startAppRuntime: call startChat() + (optionally) ship() first"
      );
    const emitter = new TraceEmitter(this.store);
    await emitter.startRun({
      scenarioId,
      projectId: this.projectId,
      chatId: this.chatId,
      parentRunId,
      layer: "app-runtime"
    });
    return emitter;
  }
  /**
   * Lightweight "ship marker" — record an app-build Run with a caller-
   * provided verdict. Use when there isn't a sandbox harness to run but
   * you still want to mark the build state at publish time.
   */
  async recordShipMarker(args) {
    if (!this.builderRunId)
      throw new Error("BuilderSession.recordShipMarker: call startChat() first");
    const emitter = new TraceEmitter(this.store);
    await emitter.startRun({
      scenarioId: args.scenarioId ?? `${this.projectId}/ship`,
      projectId: this.projectId,
      chatId: this.chatId,
      parentRunId: this.builderRunId,
      layer: "app-build"
    });
    await emitter.endRun({
      pass: args.pass,
      score: args.score,
      failureClass: args.pass ? "success" : "sandbox_failure",
      notes: args.notes
    });
    this.lastBuildRunId = emitter.runId;
    return emitter.runId;
  }
  get lastBuildRunIdValue() {
    return this.lastBuildRunId;
  }
  get builderRunIdValue() {
    return this.builderRunId;
  }
};
async function resumeBuilderSession(store, projectId) {
  const runs = await store.listRuns({ projectId });
  const chatRuns = runs.filter((r) => r.layer === "builder").sort((a, b) => b.startedAt - a.startedAt);
  const buildRuns = runs.filter((r) => r.layer === "app-build").sort((a, b) => b.startedAt - a.startedAt);
  const appRuntimeRuns = runs.filter((r) => r.layer === "app-runtime").sort((a, b) => b.startedAt - a.startedAt);
  return {
    projectId,
    chatRuns,
    lastBuilderRun: chatRuns[0],
    lastBuildRun: buildRuns[0],
    lastAppRuntimeRuns: appRuntimeRuns
  };
}
function cryptoId() {
  if (typeof globalThis.crypto?.randomUUID === "function") return globalThis.crypto.randomUUID();
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
}

// src/builder-eval/correlation.ts
function correlateLayers(reports) {
  const completeProjects = reports.filter((r) => r.complete).length;
  return {
    metaVsBuild: pairwise(
      reports,
      (r) => r.metaScore,
      (r) => r.buildScore
    ),
    metaVsRuntime: pairwise(
      reports,
      (r) => r.metaScore,
      (r) => r.runtimeScore
    ),
    buildVsRuntime: pairwise(
      reports,
      (r) => r.buildScore,
      (r) => r.runtimeScore
    ),
    completeProjects
  };
}
function pairwise(reports, a, b) {
  const xs = [];
  const ys = [];
  for (const r of reports) {
    const x = a(r);
    const y = b(r);
    if (x !== null && y !== null && Number.isFinite(x) && Number.isFinite(y)) {
      xs.push(x);
      ys.push(y);
    }
  }
  if (xs.length < 2) return void 0;
  return {
    n: xs.length,
    pearson: pearsonR(xs, ys),
    spearman: spearmanR(xs, ys)
  };
}

// src/builder-eval/project-registry.ts
var ProjectRegistry = class {
  constructor(store) {
    this.store = store;
  }
  store;
  async listProjects() {
    const runs = await this.store.listRuns();
    const byProject = /* @__PURE__ */ new Map();
    for (const r of runs) {
      if (!r.projectId) continue;
      const arr = byProject.get(r.projectId) ?? [];
      arr.push(r);
      byProject.set(r.projectId, arr);
    }
    const summaries = [];
    for (const [projectId, projectRuns] of byProject) {
      const sorted = projectRuns.slice().sort((a, b) => b.startedAt - a.startedAt);
      const chats = projectRuns.filter((r) => r.layer === "builder");
      const builds = projectRuns.filter((r) => r.layer === "app-build");
      const runtimes = projectRuns.filter((r) => r.layer === "app-runtime");
      const latest = sorted[0];
      if (!latest) continue;
      summaries.push({
        projectId,
        chatCount: chats.length,
        buildCount: builds.length,
        appRuntimeCount: runtimes.length,
        lastActivityAt: latest.startedAt,
        latestChatId: chats[0]?.chatId,
        latestOutcome: latest.outcome ? { pass: latest.outcome.pass ?? false, score: latest.outcome.score } : void 0
      });
    }
    return summaries.sort((a, b) => b.lastActivityAt - a.lastActivityAt);
  }
  async projectTimeline(projectId) {
    const runs = await this.store.listRuns({ projectId });
    const ordered = runs.slice().sort((a, b) => a.startedAt - b.startedAt);
    return ordered.map((run) => ({
      run,
      layerBucket: run.layer === "builder" ? "chat" : run.layer === "app-build" ? "build" : run.layer === "app-runtime" ? "runtime" : "other"
    }));
  }
  async projectChats(projectId) {
    const builderRuns = (await this.store.listRuns({ projectId, layer: "builder" })).sort(
      (a, b) => b.startedAt - a.startedAt
    );
    const childrenFor = async (runId) => this.store.listRuns({ parentRunId: runId });
    const out = [];
    for (const run of builderRuns) {
      const spans = await this.store.spans({ runId: run.runId });
      const children = await childrenFor(run.runId);
      const build = children.find((c) => c.layer === "app-build");
      const runtime = [];
      if (build) {
        const grands = await childrenFor(build.runId);
        for (const g of grands) if (g.layer === "app-runtime") runtime.push(g.runId);
      }
      for (const c of children) if (c.layer === "app-runtime") runtime.push(c.runId);
      out.push({
        chatId: run.chatId ?? run.runId,
        projectId,
        builderRunId: run.runId,
        startedAt: run.startedAt,
        endedAt: run.endedAt,
        status: run.status,
        outcome: run.outcome,
        llmTurns: spans.filter((s) => s.kind === "llm").length,
        toolCalls: spans.filter((s) => s.kind === "tool").length,
        buildRunId: build?.runId,
        appRuntimeRunIds: runtime
      });
    }
    return out;
  }
};

// src/builder-eval/three-layer-eval.ts
async function scoreProject(store, projectId) {
  const allRuns = await store.listRuns({ projectId });
  const builder = latestByLayer(allRuns, "builder");
  const build = latestByLayer(allRuns, "app-build");
  const runtime = allRuns.filter((r) => r.layer === "app-runtime");
  const metaScore = builder ? await extractMetaScore(store, builder.runId) : null;
  const buildScore = build?.outcome?.score ?? null;
  const runtimeScores = runtime.map((r) => r.outcome?.score).filter((s) => typeof s === "number");
  const runtimeScore = runtimeScores.length > 0 ? runtimeScores.reduce((a, b) => a + b, 0) / runtimeScores.length : null;
  const runtimePassed = runtime.filter((r) => r.outcome?.pass === true).length;
  const runtimePassRate = runtime.length > 0 ? runtimePassed / runtime.length : null;
  const kind = runtime.length === 0 ? "scaffold-only" : "full";
  const complete = kind === "scaffold-only" ? metaScore !== null && buildScore !== null : metaScore !== null && buildScore !== null && runtimeScore !== null;
  return {
    projectId,
    kind,
    builderRunId: builder?.runId,
    metaScore,
    buildRunId: build?.runId,
    buildScore,
    appRuntimeRunIds: runtime.map((r) => r.runId),
    runtimeScore,
    runtimePassRate,
    complete
  };
}
async function scoreAllProjects(store) {
  const runs = await store.listRuns();
  const projectIds = [...new Set(runs.map((r) => r.projectId).filter((p) => !!p))];
  return Promise.all(projectIds.map((p) => scoreProject(store, p)));
}
function latestByLayer(runs, layer) {
  const filtered = runs.filter((r) => r.layer === layer).sort((a, b) => b.startedAt - a.startedAt);
  return filtered[0];
}
async function extractMetaScore(store, builderRunId) {
  const js = await judgeSpans(store, builderRunId);
  const meta = js.find(
    (s) => s.judgeId === "builder-meta" && s.dimension === "user_intent_satisfaction"
  );
  if (!meta) return null;
  if (meta.score >= 0 && meta.score <= 1) return meta.score;
  if (meta.score >= 0 && meta.score <= 10) return meta.score / 10;
  return null;
}
export {
  BuilderSession,
  ProjectRegistry,
  correlateLayers,
  resumeBuilderSession,
  scoreAllProjects,
  scoreProject
};
//# sourceMappingURL=index.js.map