import {
  hashJson
} from "./chunk-VSMTAMNK.js";
import {
  ValidationError
} from "./chunk-QYJT52YW.js";

// src/agent-profile-cell.ts
var AgentProfileCellValidationError = class extends ValidationError {
  path;
  constructor(message, path = "") {
    super(path ? `${message} (at ${path})` : message);
    this.path = path;
  }
};
var SHA256_HEX = /^[0-9a-f]{64}$/;
var CELL_ID = /^agent-profile-cell:sha256:[0-9a-f]{64}$/;
async function buildAgentProfileCell(input) {
  const material = await normalizeAgentProfileCellInput(input);
  const cellId = `agent-profile-cell:sha256:${await hashJson(material)}`;
  return { ...material, cellId };
}
function agentProfileCellHashMaterial(cell) {
  const { cellId: _cellId, ...material } = cell;
  void _cellId;
  return normalizeAgentProfileCell(material);
}
async function verifyAgentProfileCell(cell) {
  validateAgentProfileCell(cell);
  return cell.cellId === `agent-profile-cell:sha256:${await hashJson(agentProfileCellHashMaterial(cell))}`;
}
function validateAgentProfileCell(input) {
  if (input === null || typeof input !== "object") {
    throw new AgentProfileCellValidationError("expected object");
  }
  const obj = input;
  expectLiteral(obj.schemaVersion, "agent-profile-cell/v1", "schemaVersion");
  if (typeof obj.cellId !== "string" || !CELL_ID.test(obj.cellId)) {
    throw new AgentProfileCellValidationError(
      "cellId must match agent-profile-cell:sha256:<64 lowercase hex chars>",
      "cellId"
    );
  }
  expectString(obj.profileId, "profileId");
  validateSource(obj.sourceProfile, "sourceProfile");
  if (obj.harness !== void 0) validateHarness(obj.harness, "harness");
  if (obj.model !== void 0) expectString(obj.model, "model");
  if (obj.promptHash !== void 0) expectString(obj.promptHash, "promptHash");
  if (obj.dimensions !== void 0) validateDimensions(obj.dimensions, "dimensions");
  return input;
}
function requireAgentProfileCell(record) {
  if (!record.agentProfile) {
    throw new AgentProfileCellValidationError(
      `run "${record.runId}" is missing agentProfile; profile-cell grouping requires explicit profile identity`,
      "agentProfile"
    );
  }
  return validateAgentProfileCell(record.agentProfile);
}
function agentProfileCellKey(record) {
  return requireAgentProfileCell(record).cellId;
}
async function assertRunAgentProfileCell(record) {
  const profile = requireAgentProfileCell(record);
  if (!await verifyAgentProfileCell(profile)) {
    throw new AgentProfileCellValidationError(
      `run "${record.runId}" has an agentProfile.cellId that does not match its content`,
      "agentProfile.cellId"
    );
  }
  if (profile.model !== void 0 && profile.model !== record.model) {
    throw new AgentProfileCellValidationError(
      `run "${record.runId}" agentProfile.model "${profile.model}" does not match model "${record.model}"`,
      "agentProfile.model"
    );
  }
  if (profile.promptHash !== void 0 && profile.promptHash !== record.promptHash) {
    throw new AgentProfileCellValidationError(
      `run "${record.runId}" agentProfile.promptHash "${profile.promptHash}" does not match promptHash "${record.promptHash}"`,
      "agentProfile.promptHash"
    );
  }
  return profile;
}
function groupRunsByAgentProfileCell(records) {
  const groups = /* @__PURE__ */ new Map();
  for (const record of records) {
    const key = agentProfileCellKey(record);
    const bucket = groups.get(key);
    if (bucket) bucket.push(record);
    else groups.set(key, [record]);
  }
  return groups;
}
async function normalizeAgentProfileCellInput(input) {
  return normalizeAgentProfileCell({
    schemaVersion: "agent-profile-cell/v1",
    profileId: input.profileId,
    sourceProfile: await normalizeSourceInput(input.sourceProfile),
    harness: input.harness,
    model: input.model,
    promptHash: input.promptHash,
    dimensions: input.dimensions
  });
}
function normalizeAgentProfileCell(input) {
  return compactObject({
    schemaVersion: "agent-profile-cell/v1",
    profileId: requireNonEmpty(input.profileId, "profileId"),
    sourceProfile: normalizeSource(input.sourceProfile),
    harness: input.harness ? normalizeHarness(input.harness, "harness") : void 0,
    model: optionalNonEmpty(input.model, "model"),
    promptHash: optionalNonEmpty(input.promptHash, "promptHash"),
    dimensions: input.dimensions ? nonEmptyRecord(normalizeDimensions(input.dimensions)) : void 0
  });
}
async function normalizeSourceInput(input) {
  const kind = requireNonEmpty(input.kind, "sourceProfile.kind");
  if (input.hash !== void 0 && input.profile !== void 0) {
    throw new AgentProfileCellValidationError(
      "sourceProfile must provide either hash or profile, not both",
      "sourceProfile"
    );
  }
  if (input.hash !== void 0) {
    return { kind, hash: requireSha256Hex(input.hash, "sourceProfile.hash") };
  }
  if (input.profile === void 0) {
    throw new AgentProfileCellValidationError(
      "sourceProfile must provide hash or profile",
      "sourceProfile"
    );
  }
  assertJson(input.profile, "sourceProfile.profile");
  return { kind, hash: await hashJson(input.profile) };
}
function normalizeSource(input) {
  return {
    kind: requireNonEmpty(input.kind, "sourceProfile.kind"),
    hash: requireSha256Hex(input.hash, "sourceProfile.hash")
  };
}
function normalizeHarness(input, path) {
  return compactObject({
    id: requireNonEmpty(input.id, `${path}.id`),
    version: optionalNonEmpty(input.version, `${path}.version`),
    hash: optionalNonEmpty(input.hash, `${path}.hash`)
  });
}
function normalizeDimensions(input) {
  const out = {};
  for (const key of Object.keys(input).sort()) {
    const value = input[key];
    requireNonEmpty(key, "dimensions.<key>");
    if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
      throw new AgentProfileCellValidationError(
        "expected primitive dimension value",
        `dimensions.${key}`
      );
    }
    if (typeof value === "number" && !Number.isFinite(value)) {
      throw new AgentProfileCellValidationError("expected finite number", `dimensions.${key}`);
    }
    out[key] = value;
  }
  return out;
}
function compactObject(input) {
  const out = {};
  for (const [key, value] of Object.entries(input)) {
    if (value !== void 0) out[key] = value;
  }
  return out;
}
function nonEmptyRecord(input) {
  return Object.keys(input).length > 0 ? input : void 0;
}
function validateSource(value, path) {
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
    throw new AgentProfileCellValidationError("expected object", path);
  }
  const rec = value;
  expectString(rec.kind, `${path}.kind`);
  requireSha256Hex(rec.hash, `${path}.hash`);
}
function validateHarness(value, path) {
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
    throw new AgentProfileCellValidationError("expected object", path);
  }
  const rec = value;
  expectString(rec.id, `${path}.id`);
  if (rec.version !== void 0) expectString(rec.version, `${path}.version`);
  if (rec.hash !== void 0) expectString(rec.hash, `${path}.hash`);
}
function validateDimensions(value, path) {
  if (value === null || typeof value !== "object" || Array.isArray(value)) {
    throw new AgentProfileCellValidationError("expected object", path);
  }
  normalizeDimensions(value);
}
function assertJson(value, path) {
  if (value === null) return;
  const type = typeof value;
  if (type === "string" || type === "boolean") return;
  if (type === "number") {
    if (!Number.isFinite(value)) {
      throw new AgentProfileCellValidationError("expected finite number", path);
    }
    return;
  }
  if (Array.isArray(value)) {
    value.forEach((item, index) => {
      assertJson(item, `${path}[${index}]`);
    });
    return;
  }
  if (type === "object") {
    for (const [key, nested] of Object.entries(value)) {
      requireNonEmpty(key, `${path}.<key>`);
      assertJson(nested, `${path}.${key}`);
    }
    return;
  }
  throw new AgentProfileCellValidationError("expected JSON-compatible value", path);
}
function expectLiteral(value, expected, path) {
  if (value !== expected) {
    throw new AgentProfileCellValidationError(`expected ${expected}`, path);
  }
}
function expectString(value, path) {
  if (typeof value !== "string" || value.length === 0) {
    throw new AgentProfileCellValidationError("expected non-empty string", path);
  }
}
function requireNonEmpty(value, path) {
  if (typeof value !== "string" || value.length === 0) {
    throw new AgentProfileCellValidationError("expected non-empty string", path);
  }
  return value;
}
function optionalNonEmpty(value, path) {
  if (value === void 0) return void 0;
  return requireNonEmpty(value, path);
}
function requireSha256Hex(value, path) {
  if (typeof value !== "string" || !SHA256_HEX.test(value)) {
    throw new AgentProfileCellValidationError("expected 64 lowercase sha256 hex chars", path);
  }
  return value;
}
var AGENT_PROFILE_KINDS = {
  /** A profile declared via `defineAgentProfile(...)` from
   *  `@tangle-network/sandbox`. The default kind for sandbox-hosted
   *  products (gtm-agent, blueprint-agent, sandbox, evals). */
  SANDBOX_AGENT_PROFILE: "sandbox-agent-profile"
};
function toAgentProfileJson(value) {
  let serialized;
  try {
    serialized = JSON.stringify(value);
  } catch (err) {
    throw new AgentProfileCellValidationError(
      `agent profile must be JSON-serializable: ${err instanceof Error ? err.message : String(err)}`,
      "sourceProfile.profile"
    );
  }
  if (serialized === void 0) {
    throw new AgentProfileCellValidationError(
      "agent profile must be JSON-serializable (got undefined after JSON.stringify)",
      "sourceProfile.profile"
    );
  }
  return JSON.parse(serialized);
}
async function buildSandboxAgentProfileCell(profile, input) {
  if (!profile || typeof profile !== "object") {
    throw new AgentProfileCellValidationError(
      "sandbox AgentProfile must be an object",
      "profile"
    );
  }
  if (typeof profile.name !== "string" || profile.name.length === 0) {
    throw new AgentProfileCellValidationError(
      "sandbox AgentProfile must have a non-empty `name`",
      "profile.name"
    );
  }
  if (typeof profile.version !== "string" || profile.version.length === 0) {
    throw new AgentProfileCellValidationError(
      "sandbox AgentProfile must have a non-empty `version`",
      "profile.version"
    );
  }
  return buildAgentProfileCell({
    ...input,
    profileId: `${profile.name}@${profile.version}`,
    sourceProfile: {
      kind: AGENT_PROFILE_KINDS.SANDBOX_AGENT_PROFILE,
      profile: toAgentProfileJson(profile)
    }
  });
}

// src/run-record.ts
var MANDATORY_TOP_LEVEL = [
  "runId",
  "experimentId",
  "candidateId",
  "seed",
  "model",
  "promptHash",
  "configHash",
  "commitSha",
  "wallMs",
  "costUsd",
  "tokenUsage",
  "outcome",
  "splitTag"
];
var SPLIT_TAGS = ["search", "dev", "holdout"];
var RunRecordValidationError = class extends ValidationError {
  path;
  constructor(message, path = "") {
    super(path ? `${message} (at ${path})` : message);
    this.path = path;
  }
};
function validateRunRecord(input) {
  if (input === null || typeof input !== "object") {
    throw new RunRecordValidationError("expected object");
  }
  const obj = input;
  for (const key of MANDATORY_TOP_LEVEL) {
    if (!(key in obj)) {
      throw new RunRecordValidationError(`missing mandatory field "${key}"`);
    }
  }
  expectString2(obj.runId, "runId");
  expectString2(obj.experimentId, "experimentId");
  expectString2(obj.candidateId, "candidateId");
  expectFiniteNumber(obj.seed, "seed");
  expectString2(obj.model, "model");
  expectString2(obj.promptHash, "promptHash");
  expectString2(obj.configHash, "configHash");
  expectString2(obj.commitSha, "commitSha");
  expectFiniteNumber(obj.wallMs, "wallMs");
  if (obj.queueMs !== void 0) expectFiniteNumber(obj.queueMs, "queueMs");
  expectFiniteNumber(obj.costUsd, "costUsd");
  if (!modelHasSnapshot(obj.model)) {
    throw new RunRecordValidationError(
      `model "${obj.model}" lacks a snapshot version (use 'name@YYYY-MM-DD' or 'name-YYYYMMDD')`,
      "model"
    );
  }
  const tu = obj.tokenUsage;
  if (tu === null || typeof tu !== "object") {
    throw new RunRecordValidationError("tokenUsage must be an object", "tokenUsage");
  }
  const tuRec = tu;
  expectFiniteNumber(tuRec.input, "tokenUsage.input");
  expectFiniteNumber(tuRec.output, "tokenUsage.output");
  if (tuRec.cached !== void 0) expectFiniteNumber(tuRec.cached, "tokenUsage.cached");
  if (obj.judgeMetadata !== void 0) {
    const jm = obj.judgeMetadata;
    if (jm === null || typeof jm !== "object") {
      throw new RunRecordValidationError("judgeMetadata must be an object", "judgeMetadata");
    }
    const jmRec = jm;
    expectString2(jmRec.model, "judgeMetadata.model");
    expectString2(jmRec.promptVersion, "judgeMetadata.promptVersion");
    expectFiniteNumber(jmRec.confidence, "judgeMetadata.confidence");
    if (typeof jmRec.fallback !== "boolean") {
      throw new RunRecordValidationError(
        "judgeMetadata.fallback must be boolean",
        "judgeMetadata.fallback"
      );
    }
  }
  const out = obj.outcome;
  if (out === null || typeof out !== "object") {
    throw new RunRecordValidationError("outcome must be an object", "outcome");
  }
  const outRec = out;
  if (outRec.searchScore !== void 0)
    expectFiniteNumber(outRec.searchScore, "outcome.searchScore");
  if (outRec.holdoutScore !== void 0)
    expectFiniteNumber(outRec.holdoutScore, "outcome.holdoutScore");
  if (outRec.searchScore === void 0 && outRec.holdoutScore === void 0) {
    throw new RunRecordValidationError(
      "outcome must define searchScore or holdoutScore (or both)",
      "outcome"
    );
  }
  const raw = outRec.raw;
  if (raw === null || typeof raw !== "object") {
    throw new RunRecordValidationError("outcome.raw must be an object", "outcome.raw");
  }
  for (const [k, v] of Object.entries(raw)) {
    expectFiniteNumber(v, `outcome.raw.${k}`);
  }
  if (outRec.judgeScores !== void 0) {
    validateJudgeScores(outRec.judgeScores, "outcome.judgeScores");
  }
  if (obj.failureMode !== void 0) expectString2(obj.failureMode, "failureMode");
  if (obj.agentProfile !== void 0) {
    try {
      const profile = validateAgentProfileCell(obj.agentProfile);
      if (profile.model !== void 0 && profile.model !== obj.model) {
        throw new RunRecordValidationError(
          `agentProfile.model "${profile.model}" does not match model "${obj.model}"`,
          "agentProfile.model"
        );
      }
      if (profile.promptHash !== void 0 && profile.promptHash !== obj.promptHash) {
        throw new RunRecordValidationError(
          `agentProfile.promptHash "${profile.promptHash}" does not match promptHash "${obj.promptHash}"`,
          "agentProfile.promptHash"
        );
      }
    } catch (error) {
      if (error instanceof RunRecordValidationError) throw error;
      if (error instanceof Error) {
        throw new RunRecordValidationError(error.message, "agentProfile");
      }
      throw error;
    }
  }
  if (typeof obj.splitTag !== "string" || !SPLIT_TAGS.includes(obj.splitTag)) {
    throw new RunRecordValidationError(
      `splitTag must be one of ${SPLIT_TAGS.join(", ")}, got ${String(obj.splitTag)}`,
      "splitTag"
    );
  }
  return input;
}
function isRunRecord(input) {
  try {
    validateRunRecord(input);
    return true;
  } catch {
    return false;
  }
}
function parseRunRecordSafe(input) {
  try {
    return { ok: true, value: validateRunRecord(input) };
  } catch (e) {
    if (e instanceof RunRecordValidationError) return { ok: false, error: e };
    throw e;
  }
}
function roundTripRunRecord(record) {
  const json = JSON.stringify(record);
  return validateRunRecord(JSON.parse(json));
}
function expectString2(value, path) {
  if (typeof value !== "string" || value.length === 0) {
    throw new RunRecordValidationError(`expected non-empty string`, path);
  }
}
function expectFiniteNumber(value, path) {
  if (typeof value !== "number" || !Number.isFinite(value)) {
    throw new RunRecordValidationError(`expected finite number`, path);
  }
}
function validateJudgeScores(value, path) {
  if (value === null || typeof value !== "object") {
    throw new RunRecordValidationError("judgeScores must be an object", path);
  }
  const rec = value;
  const perJudge = rec.perJudge;
  if (perJudge === null || typeof perJudge !== "object") {
    throw new RunRecordValidationError("perJudge must be an object", `${path}.perJudge`);
  }
  for (const [judgeId, dims] of Object.entries(perJudge)) {
    if (dims === null || typeof dims !== "object") {
      throw new RunRecordValidationError(
        "per-judge entry must be an object of dimension scores",
        `${path}.perJudge.${judgeId}`
      );
    }
    for (const [dim, score] of Object.entries(dims)) {
      expectFiniteNumber(score, `${path}.perJudge.${judgeId}.${dim}`);
    }
  }
  const perDimMean = rec.perDimMean;
  if (perDimMean === null || typeof perDimMean !== "object") {
    throw new RunRecordValidationError("perDimMean must be an object", `${path}.perDimMean`);
  }
  for (const [dim, mean] of Object.entries(perDimMean)) {
    expectFiniteNumber(mean, `${path}.perDimMean.${dim}`);
  }
  expectFiniteNumber(rec.composite, `${path}.composite`);
  if (rec.failedJudges !== void 0) {
    if (!Array.isArray(rec.failedJudges)) {
      throw new RunRecordValidationError(
        "failedJudges must be an array of strings",
        `${path}.failedJudges`
      );
    }
    for (let i = 0; i < rec.failedJudges.length; i++) {
      const id = rec.failedJudges[i];
      if (typeof id !== "string" || id.length === 0) {
        throw new RunRecordValidationError(
          "failedJudges entry must be a non-empty string",
          `${path}.failedJudges[${i}]`
        );
      }
    }
  }
  if (rec.notes !== void 0 && typeof rec.notes !== "string") {
    throw new RunRecordValidationError("notes must be a string", `${path}.notes`);
  }
}
function modelHasSnapshot(model) {
  if (model.includes("@")) return true;
  if (/-\d{8}$/.test(model)) return true;
  if (/-\d{4}-\d{2}-\d{2}$/.test(model)) return true;
  if (/:date-/.test(model)) return true;
  return false;
}

export {
  AgentProfileCellValidationError,
  buildAgentProfileCell,
  agentProfileCellHashMaterial,
  verifyAgentProfileCell,
  validateAgentProfileCell,
  requireAgentProfileCell,
  agentProfileCellKey,
  assertRunAgentProfileCell,
  groupRunsByAgentProfileCell,
  AGENT_PROFILE_KINDS,
  toAgentProfileJson,
  buildSandboxAgentProfileCell,
  RunRecordValidationError,
  validateRunRecord,
  isRunRecord,
  parseRunRecordSafe,
  roundTripRunRecord
};
//# sourceMappingURL=chunk-TSPOEDM3.js.map