// src/matrix/aggregation.ts
function flattenRuns(cells) {
  const rows = [];
  for (const { cell, runs } of cells) {
    for (const result of runs) rows.push({ cell, result });
  }
  return rows;
}
function quantile(sorted, q) {
  if (sorted.length === 0) return 0;
  if (sorted.length === 1) return sorted[0];
  const pos = (sorted.length - 1) * q;
  const lo = Math.floor(pos);
  const hi = Math.ceil(pos);
  if (lo === hi) return sorted[lo];
  const frac = pos - lo;
  return sorted[lo] * (1 - frac) + sorted[hi] * frac;
}
function summariseRows(rows, axisName, axisValue) {
  if (rows.length === 0) {
    return {
      axisName,
      axisValue,
      cells: 0,
      passRate: 0,
      meanScore: 0,
      p50Score: 0,
      p90Score: 0,
      totalCostUsd: 0,
      meanDurationMs: 0
    };
  }
  let pass = 0;
  let scoreSum = 0;
  let costSum = 0;
  let durSum = 0;
  const scores = [];
  for (const { result } of rows) {
    const errored = result.error !== void 0;
    const score = errored ? 0 : result.verdict.score;
    const valid = !errored && result.verdict.valid;
    if (valid) pass++;
    scoreSum += score;
    scores.push(score);
    costSum += result.costUsd;
    durSum += result.durationMs;
  }
  scores.sort((a, b) => a - b);
  return {
    axisName,
    axisValue,
    cells: rows.length,
    passRate: pass / rows.length,
    meanScore: scoreSum / rows.length,
    p50Score: quantile(scores, 0.5),
    p90Score: quantile(scores, 0.9),
    totalCostUsd: costSum,
    meanDurationMs: durSum / rows.length
  };
}
function bucketBy(rows, axisName, labelFor) {
  const buckets = /* @__PURE__ */ new Map();
  for (const row of rows) {
    const slot = row.cell.axes[axisName];
    if (!slot) continue;
    const id = slot.id;
    let arr = buckets.get(id);
    if (!arr) {
      arr = [];
      buckets.set(id, arr);
    }
    arr.push(row);
  }
  const out = {};
  for (const id of [...buckets.keys()].sort()) {
    out[id] = summariseRows(buckets.get(id), axisName, labelFor(id));
  }
  return out;
}
function buildByAxis(cells, axes, aggregateBy) {
  const rows = flattenRuns(cells);
  const byName = new Map(axes.map((a) => [a.name, a]));
  const byAxis = {};
  for (const name of aggregateBy) {
    const axis = byName.get(name);
    const labelFor = (id) => {
      if (!axis?.label) return id;
      const found = axis.values.find((v) => v.id === id);
      if (!found) return id;
      return axis.label(found.value, id);
    };
    byAxis[name] = bucketBy(rows, name, labelFor);
  }
  return byAxis;
}

// src/matrix/runner.ts
function cartesian(axes) {
  if (axes.length === 0) return [{ axes: {} }];
  for (const a of axes) if (a.values.length === 0) return [];
  const out = [];
  const idx = new Array(axes.length).fill(0);
  while (true) {
    const slot = {};
    for (let i2 = 0; i2 < axes.length; i2++) {
      const axis = axes[i2];
      const v = axis.values[idx[i2]];
      slot[axis.name] = { id: v.id, value: v.value };
    }
    out.push({ axes: slot });
    let i = 0;
    while (i < axes.length) {
      const next = idx[i] + 1;
      const axis = axes[i];
      if (next < axis.values.length) {
        idx[i] = next;
        break;
      }
      idx[i] = 0;
      i++;
    }
    if (i === axes.length) break;
  }
  return out;
}
function makeMatrixId() {
  const t = Date.now().toString(36);
  let r = "";
  for (let i = 0; i < 8; i++) r += Math.floor(Math.random() * 16).toString(16);
  return `mtx_${t}_${r}`;
}
function makeErrorResult(err) {
  const e = err;
  return {
    output: void 0,
    verdict: { valid: false, score: 0 },
    costUsd: 0,
    durationMs: 0,
    error: {
      message: typeof e?.message === "string" ? e.message : String(err),
      kind: typeof e?.name === "string" ? e.name : "Error"
    }
  };
}
async function runAgentMatrix(opts) {
  const startedAt = Date.now();
  const reps = Math.max(1, opts.reps ?? 1);
  const maxConcurrency = Math.max(1, opts.maxConcurrency ?? 4);
  const costCeiling = opts.costCeiling ?? Number.POSITIVE_INFINITY;
  const aggregateBy = opts.aggregateBy ?? opts.axes.map((a) => a.name);
  const base = cartesian(opts.axes);
  const filtered = opts.filter ? base.filter((c) => opts.filter(c)) : base;
  const filteredOut = base.length - filtered.length;
  const planned = [];
  for (let i = 0; i < filtered.length; i++) {
    for (let r = 0; r < reps; r++) {
      planned.push({
        axes: filtered[i].axes,
        rep: r,
        ordinal: i * reps + r
      });
    }
  }
  const cellRecords = [];
  let cumulativeCost = 0;
  let costCeilingReached = false;
  let runsExecuted = 0;
  let cellsUnscheduled = 0;
  const aborted = () => opts.signal?.aborted === true;
  let inFlight = 0;
  let cursor = 0;
  let resolveAll;
  const done = new Promise((res) => {
    resolveAll = res;
  });
  const pump = () => {
    while (inFlight < maxConcurrency && cursor < planned.length) {
      if (aborted() || costCeilingReached) {
        const left = planned.length - cursor;
        cellsUnscheduled += left;
        cursor = planned.length;
        break;
      }
      const cell = planned[cursor++];
      inFlight++;
      const record = { cell, runs: [] };
      cellRecords.push(record);
      const promise = (async () => {
        try {
          return await opts.runCell(cell);
        } catch (err) {
          return makeErrorResult(err);
        }
      })();
      promise.then((result) => {
        record.runs.push(result);
        runsExecuted++;
        cumulativeCost += result.costUsd;
        if (cumulativeCost >= costCeiling && !costCeilingReached) {
          costCeilingReached = true;
          console.warn("[matrix] cost ceiling reached");
        }
        try {
          opts.onCellComplete?.(cell, result);
        } catch {
        }
        inFlight--;
        if (cursor < planned.length) {
          pump();
        } else if (inFlight === 0) {
          resolveAll?.();
        }
      });
    }
    if (cursor >= planned.length && inFlight === 0) resolveAll?.();
  };
  const onAbort = () => {
    if (cursor < planned.length) {
      cellsUnscheduled += planned.length - cursor;
      cursor = planned.length;
    }
    if (inFlight === 0) resolveAll?.();
  };
  if (opts.signal) {
    if (opts.signal.aborted) {
      cellsUnscheduled = planned.length;
      cursor = planned.length;
      resolveAll?.();
    } else {
      opts.signal.addEventListener("abort", onAbort, { once: true });
    }
  }
  if (planned.length === 0) {
    resolveAll?.();
  } else {
    pump();
  }
  await done;
  if (opts.signal) opts.signal.removeEventListener("abort", onAbort);
  cellRecords.sort((a, b) => a.cell.ordinal - b.cell.ordinal);
  let pass = 0;
  let scoreSum = 0;
  let totalCost = 0;
  let runCount = 0;
  for (const { runs } of cellRecords) {
    for (const r of runs) {
      runCount++;
      const errored = r.error !== void 0;
      if (!errored && r.verdict.valid) pass++;
      scoreSum += errored ? 0 : r.verdict.score;
      totalCost += r.costUsd;
    }
  }
  const byAxis = buildByAxis(cellRecords, opts.axes, aggregateBy);
  return {
    cells: cellRecords,
    byAxis,
    summary: {
      totalCells: planned.length,
      runsExecuted,
      cellsSkipped: cellsUnscheduled + filteredOut * reps,
      overallPassRate: runCount === 0 ? 0 : pass / runCount,
      overallMeanScore: runCount === 0 ? 0 : scoreSum / runCount,
      totalCostUsd: totalCost,
      durationMs: Date.now() - startedAt
    },
    matrixId: makeMatrixId()
  };
}

export {
  runAgentMatrix
};
//# sourceMappingURL=chunk-JHCHEVET.js.map