import {
  canonicalize
} from "./chunk-VSMTAMNK.js";
import {
  ValidationError
} from "./chunk-3BFEG2F6.js";

// src/completion-verifier.ts
function completionVerdict(input) {
  if (input.requirements.length === 0) {
    throw new Error(
      `completionVerdict: task '${input.taskId}' has no requirement checks \u2014 nothing to derive a verdict from`
    );
  }
  const satisfiedCount = input.requirements.filter((r) => r.satisfied).length;
  const completionRate = satisfiedCount / input.requirements.length;
  const fullyComplete = satisfiedCount === input.requirements.length;
  return {
    taskId: input.taskId,
    requirements: input.requirements,
    completionRate,
    fullyComplete,
    valid: fullyComplete,
    score: completionRate
  };
}
var STOPWORDS = /* @__PURE__ */ new Set([
  "the",
  "a",
  "an",
  "of",
  "for",
  "and",
  "or",
  "to",
  "in",
  "on",
  "with",
  "by"
]);
var REQUIREMENT_FORM_STOPWORDS = /* @__PURE__ */ new Set([
  "generated",
  "generate",
  "view",
  "render",
  "rendered",
  "persisted",
  "persist",
  "artifact",
  "file",
  "document",
  "note",
  "proposal",
  "deliverable",
  "output",
  "created",
  "create",
  "produce",
  "produced",
  "flag"
]);
var MATCH_THRESHOLD = 0.5;
var MIN_CONTENT_CHARS = 50;
function tokens(s, extraStop) {
  return new Set(
    s.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 1 && !STOPWORDS.has(t) && !extraStop?.has(t))
  );
}
function tokenRecall(requirementText, candidateText) {
  const req = tokens(requirementText, REQUIREMENT_FORM_STOPWORDS);
  if (req.size === 0) return 0;
  const cand = tokens(candidateText);
  let hit = 0;
  for (const t of req) if (cand.has(t)) hit++;
  return hit / req.size;
}
function artifactCandidates(req, reqIndex, artifacts) {
  const reqText = `${req.title} ${req.category ?? ""}`;
  const out = [];
  artifacts.forEach((a, i) => {
    if ((a.content ?? "").trim().length < MIN_CONTENT_CHARS) return;
    let score = tokenRecall(
      reqText,
      `${a.path ?? ""} ${a.kind} ${(a.content ?? "").slice(0, 4e3)}`
    );
    if (req.category && a.kind && req.category.toLowerCase() === a.kind.toLowerCase()) {
      score = Math.max(score, 1);
    }
    if (score < MATCH_THRESHOLD) return;
    out.push({
      reqIndex,
      itemKey: `artifact:${i}`,
      score,
      evidence: `artifact '${a.path ?? a.kind}' matched (token recall ${score.toFixed(2)})`,
      content: a.content ?? null
    });
  });
  return out;
}
function proposalCandidates(req, reqIndex, proposals) {
  const reqText = `${req.title} ${req.category ?? ""}`;
  const out = [];
  for (const p of proposals) {
    if (p.status !== "approved") continue;
    const body = (p.content ?? "").trim();
    if (body.length < MIN_CONTENT_CHARS) continue;
    const score = tokenRecall(reqText, `${p.title} ${body}`);
    if (score < MATCH_THRESHOLD) continue;
    out.push({
      reqIndex,
      itemKey: `proposal:${p.id}`,
      score,
      evidence: `approved proposal '${p.title}' matched (token recall ${score.toFixed(2)})`,
      content: body
    });
  }
  return out;
}
function toolCallCandidates(req, reqIndex, toolCalls) {
  const out = [];
  toolCalls.forEach((name, i) => {
    const score = tokenRecall(req.title, name);
    if (score < MATCH_THRESHOLD) return;
    out.push({
      reqIndex,
      itemKey: `tool:${i}`,
      score,
      evidence: `tool call '${name}' matched (token recall ${score.toFixed(2)})`,
      content: null
    });
  });
  return out;
}
async function verifyCompletion(gold, state, checkCorrectness) {
  if (gold.requirements.length === 0) {
    throw new Error(
      `verifyCompletion: task '${gold.taskId}' has no requirements \u2014 malformed gold spec`
    );
  }
  const candidates = [];
  gold.requirements.forEach((req, i) => {
    const by = req.satisfiedBy ?? "any";
    if (by === "artifact" || by === "any") {
      candidates.push(...artifactCandidates(req, i, state.artifacts));
    }
    if (by === "proposal" || by === "any") {
      candidates.push(...proposalCandidates(req, i, state.proposals));
    }
    if (by === "tool-call" || by === "any") {
      candidates.push(...toolCallCandidates(req, i, state.toolCalls));
    }
  });
  candidates.sort((a, b) => b.score - a.score);
  const assigned = /* @__PURE__ */ new Map();
  const itemTaken = /* @__PURE__ */ new Set();
  for (const c of candidates) {
    if (assigned.has(c.reqIndex) || itemTaken.has(c.itemKey)) continue;
    assigned.set(c.reqIndex, c);
    itemTaken.add(c.itemKey);
  }
  const requirements = [];
  for (let i = 0; i < gold.requirements.length; i++) {
    const req = gold.requirements[i];
    const match = assigned.get(i);
    const evidence = [];
    let correct = null;
    if (match) {
      evidence.push(match.evidence);
      if (match.content !== null) {
        const r = await checkCorrectness(req, match.content);
        correct = r.correct;
        evidence.push(`correctness: ${r.correct ? "pass" : "fail"} \u2014 ${r.reason}`);
      } else {
        evidence.push("correctness: not assessed \u2014 matched item carries no content");
      }
    } else {
      const by = req.satisfiedBy ?? "any";
      const kind = by === "any" ? "artifact/proposal/tool-call" : by;
      evidence.push(`no produced ${kind} matched this requirement`);
    }
    const structurallyPresent = match !== void 0;
    const satisfied = structurallyPresent && correct !== false;
    requirements.push({
      reqId: req.reqId,
      title: req.title,
      structurallyPresent,
      correct,
      satisfied,
      evidence
    });
  }
  return completionVerdict({ taskId: gold.taskId, requirements });
}
function parseCorrectnessResponse(raw) {
  const match = raw.match(/\{[\s\S]*\}/);
  if (!match) {
    throw new Error(`correctness checker: no JSON object in model response: ${raw.slice(0, 200)}`);
  }
  const parsed = JSON.parse(match[0]);
  if (typeof parsed.correct !== "boolean") {
    throw new Error(`correctness checker: 'correct' is not a boolean in: ${match[0].slice(0, 200)}`);
  }
  return { correct: parsed.correct, reason: typeof parsed.reason === "string" ? parsed.reason : "" };
}
function createLlmCorrectnessChecker(tc, opts = {}) {
  const model = opts.model ?? "claude-sonnet-4-6";
  const maxContentChars = opts.maxContentChars ?? 8e3;
  return async (requirement, content) => {
    const resp = await tc.chat({
      model,
      messages: [
        {
          role: "system",
          content: 'You verify whether a produced work artifact actually fulfils a stated requirement. Judge fulfilment only \u2014 is the deliverable substantively present and on-point \u2014 not polish. A plan to do it later, a vague gesture, or a description of what should be done does NOT fulfil a requirement; the artifact must BE the deliverable. Respond with a single JSON object: {"correct": boolean, "reason": string (<= 30 words)}.'
        },
        {
          role: "user",
          content: `Requirement: ${requirement.title}
${requirement.category ? `Category: ${requirement.category}
` : ""}
Produced artifact:
${content.slice(0, maxContentChars)}`
        }
      ],
      temperature: 0,
      maxTokens: 200
    });
    const raw = resp.choices?.[0]?.message?.content ?? "";
    return parseCorrectnessResponse(raw);
  };
}
var TITLE_STOPWORDS = /* @__PURE__ */ new Set([
  "the",
  "a",
  "an",
  "and",
  "or",
  "for",
  "to",
  "of",
  "in",
  "on",
  "with",
  "review",
  "update",
  "new",
  "proposed"
]);
function createTokenRecallChecker(opts = {}) {
  const minRecall = opts.minRecall ?? 0.5;
  const minLen = opts.minContentLength ?? 120;
  return async (requirement, content) => {
    const body = content.trim();
    if (body.length < minLen)
      return {
        correct: false,
        reason: `content too thin (${body.length} chars) to be the deliverable`
      };
    const titleTokens = requirement.title.toLowerCase().split(/[^a-z0-9]+/).filter((t) => t.length > 2 && !TITLE_STOPWORDS.has(t));
    if (titleTokens.length === 0)
      return {
        correct: true,
        reason: "requirement title has no significant tokens \u2014 structural match accepted"
      };
    const lower = body.toLowerCase();
    const hits = titleTokens.filter((t) => lower.includes(t)).length;
    const recall = hits / titleTokens.length;
    return recall >= minRecall ? {
      correct: true,
      reason: `content recalls ${hits}/${titleTokens.length} requirement tokens`
    } : {
      correct: false,
      reason: `content recalls only ${hits}/${titleTokens.length} requirement tokens`
    };
  };
}

// src/produced-state.ts
function artifactKind(mimeType) {
  if (!mimeType) return "file";
  if (mimeType.includes("json")) return "json";
  if (mimeType.startsWith("text/")) return "text";
  return "file";
}
function extractProducedState(events) {
  const artifacts = [];
  const proposals = [];
  const toolCalls = [];
  const seenTools = /* @__PURE__ */ new Set();
  for (const ev of events) {
    if (ev.type === "tool_call") {
      const name = ev.toolName;
      if (name && !seenTools.has(name)) {
        seenTools.add(name);
        toolCalls.push(name);
      }
    } else if (ev.type === "artifact") {
      const a = ev;
      artifacts.push({
        kind: artifactKind(a.mimeType),
        path: a.name ?? a.uri ?? a.artifactId,
        content: a.content ?? ""
      });
    } else if (ev.type === "proposal_created") {
      const p = ev;
      proposals.push({
        id: p.proposalId,
        title: p.title,
        status: p.status ?? "pending",
        ...p.content !== void 0 ? { content: p.content } : {}
      });
    }
  }
  return { artifacts, proposals, toolCalls };
}

// src/agent-profile.ts
import { createHash } from "crypto";
function agentProfileId(profile) {
  const label = pathSafeProfileLabel(agentProfileDisplayLabel(profile)) ?? "profile";
  return `${label}-${agentProfileHash(profile).slice(0, 16)}`;
}
function agentProfileModelId(profile) {
  const model = profile.model?.default?.trim();
  if (!model) {
    const label = agentProfileDisplayLabel(profile) ?? "unnamed profile";
    throw new ValidationError(
      `AgentProfile "${label}" has no model.default \u2014 cannot record eval run`
    );
  }
  return model;
}
function agentProfileDisplayLabel(profile) {
  return profile.name?.trim() || profile.version?.trim() || void 0;
}
function pathSafeProfileLabel(label) {
  const safe = label?.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
  return safe || void 0;
}
function compact(input) {
  const out = {};
  for (const [key, value] of Object.entries(input)) {
    if (value !== void 0) out[key] = value;
  }
  return out;
}
function agentProfileHash(profile) {
  const model = agentProfileModelId(profile);
  const behaviour = {
    ...profile,
    name: void 0,
    description: void 0,
    tags: profile.tags ? [...profile.tags].sort() : void 0,
    model: compact({ ...profile.model, default: model })
  };
  return createHash("sha256").update(JSON.stringify(canonicalize(behaviour))).digest("hex");
}

export {
  completionVerdict,
  verifyCompletion,
  parseCorrectnessResponse,
  createLlmCorrectnessChecker,
  createTokenRecallChecker,
  extractProducedState,
  agentProfileId,
  agentProfileModelId,
  agentProfileHash
};
//# sourceMappingURL=chunk-HMA63UEO.js.map