import { createHash } from "node:crypto";
import { readFileSync, readdirSync, statSync } from "node:fs";
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
import { stableDigest } from "@best-agent/foundation";
const COMPLETION_GUARD_TOOL_NAMES = new Set(["shell", "write-workspace", "edit", "apply_patch"]);
const PROMPT_DELIVERABLE_QUOTED_PATH_RE = /[`'"]([^`'"\n]*(?:\/|\.)[^`'"\n]*)[`'"]/giu;
const PROMPT_DELIVERABLE_MARKDOWN_DEFINITION_PATH_RE = /(?:^|\n)\s*(?:[-*+]|\d+\.)\s+(?:\*\*)?`?((?:\/|\.)[A-Za-z0-9._@+=,~:/-]+|[A-Za-z0-9._@+=,-]+\.(?:c|comp|cpp|css|csv|fa|fasta|fastq|go|h|hpp|html|java|jpeg|jpg|js|json|jsx|log|md|out|pdf|pdb|png|py|r|rs|sh|sql|svg|toml|ts|tsx|tsv|txt|vcf|xml|yaml|yml))`?(?:\*\*)?\s*:/giu;
const PROMPT_DELIVERABLE_BARE_PATH_RE = new RegExp([
    String.raw `(^|[\s([{,:;])`,
    String.raw `(`,
    String.raw `(?:/[A-Za-z0-9._@+=,-]+(?:/[A-Za-z0-9._@+=,-]+)+)`,
    String.raw `|(?:(?:\.{1,2}/)?[A-Za-z0-9._@+=,-]+/[A-Za-z0-9._@+=,-]+(?:/[A-Za-z0-9._@+=,-]+)*)`,
    String.raw `|(?:[A-Za-z0-9._@+=,-]+\.(?:txt|json|csv|tsv|md|py|js|ts|tsx|jsx|rs|go|java|cpp|c|h|hpp|sh|toml|yaml|yml|xml|html|css|sql|r|fasta|fa|fastq|bed|vcf|pdb|sdf|mol2|png|jpg|jpeg|svg|pdf|out|log|answer|comp))`,
    String.raw `)`,
    String.raw `(?=$|[\s)\]},.;:!?*])`,
].join(""), "giu");
const PROMPT_SOURCE_PATH_CONTEXT_RE = /\b(?:for|about|of|from|in|inside|within|review|inspect|read|analy[sz]e)\s+$/iu;
const PROMPT_SOURCE_PREPOSITION_TAIL_RE = /\b(?:changed|changes|defined|implemented|located|found|present|contained)\s+in\s+$/iu;
const PROMPT_REPORT_ABOUT_SOURCE_RE = /\b(?:report|summary|review|analysis)\b[\s\S]{0,80}\b(?:about|of|for)\b[\s\S]{0,40}\bin\s+$/iu;
const PROMPT_DELIVERABLE_TARGET_CONTEXT_RE = /(?:\b(?:to|into|at|as|called|named|titled)\s*|\b(?:answer|result|solution|deliverable|submission|response|report|summary|output|file|path)[\s\S]{0,80}\b(?:in|to|into|at|as|called|named|titled)\s*|\b(?:path|file|output|report|summary)\s+(?:is|=|:)\s*)$/iu;
const PROMPT_DELIVERABLE_DIRECT_WRITE_RE = /\b(?:write|create|save|store|put|place|output|produce|generate|emit|record|dump)\s+(?:(?:me|us)\s+)?(?:(?:a|an|the)\s+)?(?:final\s+)?(?:answer|result|solution|deliverable|submission|response|report|summary|file|output)?\s*$/iu;
const PROMPT_DELIVERABLE_AFTER_RE = /^(?:[\s\S]{0,80}\b(?:should|must|needs to|has to|is required to|will)\s+(?:contain|include|hold|be|exist)\b|[\s\S]{0,80}\b(?:containing|contains)\b)/iu;
const PROMPT_DELIVERABLE_CHINESE_BEFORE_RE = /(?:写入|创建|保存|输出|生成|存到|放到|最终答案|答案|结果|提交|报告)[\s\S]{0,120}$/u;
const PROMPT_DELIVERABLE_CHINESE_AFTER_RE = /^[\s\S]{0,100}(?:应|应该|必须|需要)[\s\S]{0,60}(?:包含|存在|保存|写入)/u;
const PROMPT_DELIVERABLE_NEGATED_RE = /\b(?:do not|don't|dont|never|avoid)\s+(?:write|create|save|store|modify|touch)\b|不要|别/u;
const PROMPT_DELIVERABLE_CHANGE_CONTEXT_RE = /\b(?:update|updated|modify|modified|change|changed|edit|edited|fix|fixed|rewrite|replace|revise|patch)\b|更新|修改|修复/u;
const PROMPT_ARTIFACT_CREATE_OUTPUT_INTENT_RE = /\b(?:write|create|save|store|put|place|output|produce|generate|emit|record|dump)\b|写入|创建|保存|输出|生成|存到|放到/iu;
const PROMPT_EXAMPLE_PATH_CONTEXT_RE = /(?:\be\.g\.|\bfor example\b|\bexample(?:s)?\b)[\s\S]{0,80}$/iu;
const PROMPT_REFERENCE_FILE_CONTEXT_RE = /\b(?:against|versus|vs\.?|opponent(?:s)?|warrior(?:s)?)\b[\s\S]{0,100}$/iu;
const PROMPT_DIRECTORY_CONTEXT_RE = /\b(?:directory|folder|dir)\b|目录|文件夹/iu;
const PROMPT_FILE_CONTEXT_RE = /\b(?:file|answer|result|solution|output|deliverable|submission|report|summary|script|program|executable|binary)\b|文件|答案|结果|提交|报告/u;
const PROMPT_PATH_FILE_EXTENSION_RE = /\.[A-Za-z0-9][A-Za-z0-9._-]*$/u;
export function hasCompletionGuardTool(availableTools) {
    return availableTools.some((tool) => COMPLETION_GUARD_TOOL_NAMES.has(tool.name));
}
export function compileArtifactObligations(input) {
    const source = input.messages && input.messages.length > 0
        ? (findLatestUserMessage(input.messages) ?? input.objective)
        : input.objective;
    const intent = classifyArtifactIntent(source);
    if (intent === "none") {
        return [];
    }
    if (intent === "create-output" && input.artifactProducing !== true) {
        return [];
    }
    const obligations = [];
    const seen = new Set();
    for (const candidate of extractPromptDeliverablePaths(source, input.cwd)) {
        const obligationId = createArtifactObligationId("write-target", candidate.path);
        if (seen.has(obligationId)) {
            continue;
        }
        seen.add(obligationId);
        const initialDigest = candidate.requiresChange ? readArtifactDigest(candidate.path) : undefined;
        const verifier = initialDigest ? "changed" : "exists";
        const constraints = [
            ...(candidate.exclusiveDirectory
                ? [
                    {
                        kind: "exclusive-directory",
                        directory: candidate.exclusiveDirectory,
                        allowedSiblingBasenames: readAllowedSiblingBasenames(candidate.path),
                    },
                ]
                : []),
            ...(candidate.maxBytes
                ? [
                    {
                        kind: "max-bytes",
                        bytes: candidate.maxBytes.bytes,
                        inclusive: candidate.maxBytes.inclusive,
                    },
                ]
                : []),
        ];
        obligations.push({
            ...(constraints.length > 0 ? { constraints } : {}),
            ...(initialDigest ? { initialDigest } : {}),
            kind: initialDigest ? "modify-target" : "write-target",
            obligationId,
            required: true,
            target: candidate.path,
            verifier,
        });
    }
    return obligations;
}
export function collectArtifactObligationFailures(obligations, cwd, input = {}) {
    const failures = [];
    const existsCandidates = obligations.flatMap((obligation) => {
        if (!obligation.required || !obligation.target) {
            return [];
        }
        if (obligation.verifier !== "exists" && obligation.verifier !== "changed") {
            return [];
        }
        return [artifactObligationFailureCandidate(obligation, cwd)];
    });
    failures.push(...collectPromptDeliverableFailuresForCandidates(existsCandidates));
    const failedObligationIds = new Set(failures.map((failure) => failure.obligationId).filter(Boolean));
    for (const obligation of obligations) {
        if (!obligation.required ||
            !obligation.target ||
            failedObligationIds.has(obligation.obligationId)) {
            continue;
        }
        const candidate = artifactObligationFailureCandidate(obligation, cwd);
        switch (obligation.verifier) {
            case "exists":
            case "semantic-only": {
                const sizeLimit = findMaxBytesConstraint(obligation);
                if (sizeLimit) {
                    const currentSize = readArtifactByteSize(obligation.target);
                    if (currentSize !== undefined && violatesSizeLimit(currentSize, sizeLimit)) {
                        failures.push({
                            ...candidate,
                            actualBytes: currentSize,
                            maxBytes: sizeLimit,
                            reason: "too-large",
                        });
                    }
                }
                break;
            }
            case "changed": {
                const currentDigest = readArtifactDigest(obligation.target);
                if (!currentDigest || currentDigest === obligation.initialDigest) {
                    failures.push({ ...candidate, reason: "unchanged" });
                }
                else {
                    const sizeLimit = findMaxBytesConstraint(obligation);
                    if (sizeLimit) {
                        const currentSize = readArtifactByteSize(obligation.target);
                        if (currentSize !== undefined && violatesSizeLimit(currentSize, sizeLimit)) {
                            failures.push({
                                ...candidate,
                                actualBytes: currentSize,
                                maxBytes: sizeLimit,
                                reason: "too-large",
                            });
                        }
                    }
                }
                break;
            }
            case "command-zero-exit": {
                if (!hasCommandZeroExitEvidence(obligation, input.evidenceRecords ?? [], cwd)) {
                    failures.push({ ...candidate, reason: "evidence-missing" });
                }
                break;
            }
            case "evidence-present":
                if (!hasEvidenceForObligation(obligation, input.evidenceRecords ?? [], cwd)) {
                    failures.push({ ...candidate, reason: "evidence-missing" });
                }
                break;
            case "structured-output":
                if (!hasStructuredEvidenceForObligation(obligation, input.evidenceRecords ?? [], cwd)) {
                    failures.push({ ...candidate, reason: "evidence-missing" });
                }
                break;
        }
    }
    return failures;
}
export function collectPromptDeliverableFailures(messages, cwd) {
    for (const message of [...messages].reverse()) {
        if (message.role !== "user") {
            continue;
        }
        const intent = classifyArtifactIntent(message.content);
        if (intent !== "create-output") {
            continue;
        }
        const candidates = extractPromptDeliverablePaths(message.content, cwd);
        if (candidates.length > 0) {
            return collectPromptDeliverableFailuresForCandidates(candidates);
        }
    }
    return [];
}
export function displayArtifactObligationTarget(target, cwd) {
    if (cwd) {
        const relativePath = relative(cwd, target);
        if (relativePath && !relativePath.startsWith("..") && !isAbsolute(relativePath)) {
            return relativePath;
        }
    }
    return target;
}
function findLatestUserMessage(messages) {
    return [...messages].reverse().find((message) => message.role === "user")?.content;
}
function classifyArtifactIntent(message) {
    if (PROMPT_ARTIFACT_CREATE_OUTPUT_INTENT_RE.test(message)) {
        return "create-output";
    }
    return "none";
}
function createArtifactObligationId(kind, target) {
    return `obl_${stableDigest({ kind, target }).slice(0, 12)}`;
}
function artifactObligationFailureCandidate(obligation, cwd) {
    const exclusiveDir = findExclusiveDirectoryConstraint(obligation);
    return {
        ...(exclusiveDir ? { allowedSiblingBasenames: exclusiveDir.allowedSiblingBasenames } : {}),
        displayPath: displayArtifactObligationTarget(obligation.target ?? obligation.obligationId, cwd),
        ...(exclusiveDir ? { exclusiveDirectory: exclusiveDir.directory } : {}),
        obligationId: obligation.obligationId,
        path: obligation.target ?? obligation.obligationId,
    };
}
function collectPromptDeliverableFailuresForCandidates(candidates) {
    const failures = [];
    const seen = new Set();
    for (const candidate of candidates) {
        if (seen.has(candidate.path)) {
            continue;
        }
        seen.add(candidate.path);
        let stats;
        try {
            stats = statSync(candidate.path);
        }
        catch {
            failures.push({ ...candidate, reason: "missing" });
            continue;
        }
        if (!stats.isFile()) {
            failures.push({ ...candidate, reason: "missing" });
            continue;
        }
        if (isBlankPromptDeliverable(candidate.path, stats.size)) {
            failures.push({ ...candidate, reason: "blank" });
            continue;
        }
        if (candidate.maxBytes && violatesSizeLimit(stats.size, candidate.maxBytes)) {
            failures.push({
                ...candidate,
                actualBytes: stats.size,
                maxBytes: candidate.maxBytes,
                reason: "too-large",
            });
            continue;
        }
        const extraSibling = findUnexpectedDeliverableSibling(candidate);
        if (extraSibling) {
            failures.push({
                ...candidate,
                extraPath: extraSibling,
                reason: "extra-sibling",
            });
        }
    }
    return failures;
}
function findUnexpectedDeliverableSibling(candidate) {
    if (!candidate.exclusiveDirectory || !candidate.allowedSiblingBasenames) {
        return undefined;
    }
    const allowed = new Set(candidate.allowedSiblingBasenames);
    try {
        for (const entry of readdirSync(candidate.exclusiveDirectory)) {
            if (!allowed.has(entry)) {
                return join(candidate.exclusiveDirectory, entry);
            }
        }
    }
    catch { }
    return undefined;
}
function extractPromptDeliverablePaths(message, cwd) {
    const matches = [];
    for (const match of message.matchAll(PROMPT_DELIVERABLE_QUOTED_PATH_RE)) {
        const rawPath = match[1];
        if (!rawPath) {
            continue;
        }
        matches.push({
            index: (match.index ?? 0) + match[0].indexOf(rawPath),
            rawPath,
        });
    }
    for (const match of message.matchAll(PROMPT_DELIVERABLE_MARKDOWN_DEFINITION_PATH_RE)) {
        const rawPath = match[1];
        if (!rawPath) {
            continue;
        }
        matches.push({
            index: (match.index ?? 0) + match[0].indexOf(rawPath),
            rawPath,
        });
    }
    for (const match of message.matchAll(PROMPT_DELIVERABLE_BARE_PATH_RE)) {
        const rawPath = match[2];
        if (!rawPath) {
            continue;
        }
        matches.push({
            index: (match.index ?? 0) + (match[1]?.length ?? 0),
            rawPath,
        });
    }
    const candidates = [];
    const seen = new Set();
    for (const match of matches.sort((left, right) => left.index - right.index)) {
        const before = message.slice(Math.max(0, match.index - 160), match.index);
        const after = message.slice(match.index + match.rawPath.length, match.index + match.rawPath.length + 120);
        const beforeShort = before.slice(-160);
        const isNegatedOrReference = PROMPT_DELIVERABLE_NEGATED_RE.test(beforeShort) ||
            PROMPT_EXAMPLE_PATH_CONTEXT_RE.test(beforeShort) ||
            PROMPT_REFERENCE_FILE_CONTEXT_RE.test(beforeShort.slice(-140));
        if (isNegatedOrReference) {
            continue;
        }
        if (!hasPromptDeliverableContext(before, after) &&
            !hasPromptProjectFileDeliverableContext(match.rawPath, before, after)) {
            continue;
        }
        const candidate = normalizePromptDeliverablePath(match.rawPath, cwd, before, after);
        if (!candidate || seen.has(candidate.path)) {
            continue;
        }
        seen.add(candidate.path);
        const exclusiveDirectory = hasPromptSingleFileContext(before, after)
            ? dirname(candidate.path)
            : undefined;
        candidates.push({
            ...candidate,
            ...(exclusiveDirectory
                ? {
                    allowedSiblingBasenames: readAllowedSiblingBasenames(candidate.path),
                    exclusiveDirectory,
                }
                : {}),
            ...resolvePromptDeliverableSizeLimit(message, match.index, matches.length),
            requiresChange: hasPromptDeliverableChangeContext(before, after),
        });
    }
    return candidates;
}
function resolvePromptDeliverableSizeLimit(message, pathIndex, pathCount) {
    const nearby = message.slice(Math.max(0, pathIndex - 240), pathIndex + 360);
    const nearbyLimit = parsePromptSizeLimitBytes(nearby);
    if (nearbyLimit) {
        return { maxBytes: nearbyLimit };
    }
    if (pathCount === 1) {
        const globalLimit = parsePromptSizeLimitBytes(message);
        if (globalLimit) {
            return { maxBytes: globalLimit };
        }
    }
    return {};
}
function parsePromptSizeLimitBytes(text) {
    const match = text.match(/(?:(?<symbolOp><)|\b(?<wordOp>under|less\s+than|no\s+more\s+than|at\s+most))\s*(?<value>\d+(?:\.\d+)?)\s*(?<unit>bytes?|b|kib|kb|mib|mb)\b/iu);
    if (!match?.groups) {
        return undefined;
    }
    const value = Number(match.groups.value);
    if (!Number.isFinite(value) || value <= 0) {
        return undefined;
    }
    const unit = match.groups.unit.toLowerCase();
    let bytes;
    if (unit === "b" || unit.startsWith("byte")) {
        bytes = Math.floor(value);
    }
    else if (unit === "kb") {
        bytes = Math.floor(value * 1000);
    }
    else if (unit === "kib") {
        bytes = Math.floor(value * 1024);
    }
    else if (unit === "mb") {
        bytes = Math.floor(value * 1_000_000);
    }
    else if (unit === "mib") {
        bytes = Math.floor(value * 1024 * 1024);
    }
    else {
        return undefined;
    }
    const op = (match.groups.symbolOp ?? match.groups.wordOp ?? "").toLowerCase();
    return {
        bytes,
        inclusive: op === "no more than" || op === "at most",
    };
}
function hasPromptDeliverableContext(before, after) {
    const tail = before.slice(-160);
    if (PROMPT_DELIVERABLE_NEGATED_RE.test(before.slice(-100))) {
        return false;
    }
    if (PROMPT_EXAMPLE_PATH_CONTEXT_RE.test(before.slice(-100))) {
        return false;
    }
    if (PROMPT_REFERENCE_FILE_CONTEXT_RE.test(before.slice(-140))) {
        return false;
    }
    if (PROMPT_REPORT_ABOUT_SOURCE_RE.test(tail) || PROMPT_SOURCE_PREPOSITION_TAIL_RE.test(tail)) {
        return false;
    }
    const hasTargetContext = PROMPT_DELIVERABLE_TARGET_CONTEXT_RE.test(tail);
    if (!hasTargetContext && PROMPT_SOURCE_PATH_CONTEXT_RE.test(tail)) {
        return false;
    }
    return (hasTargetContext ||
        PROMPT_DELIVERABLE_DIRECT_WRITE_RE.test(tail) ||
        PROMPT_DELIVERABLE_AFTER_RE.test(after) ||
        hasPromptMarkdownDefinitionDeliverableContext(before, after) ||
        PROMPT_DELIVERABLE_CHINESE_BEFORE_RE.test(before) ||
        PROMPT_DELIVERABLE_CHINESE_AFTER_RE.test(after));
}
function hasPromptMarkdownDefinitionDeliverableContext(before, after) {
    return (/(?:^|\n)\s*(?:[-*+]|\d+\.)\s+(?:\*\*)?`?$/u.test(before) &&
        /^`?(?:\*\*)?\s*:\s*(?:a|an|the)?\s*(?:file|script|program|source|executable|binary)?\b/iu.test(after));
}
function hasPromptProjectFileDeliverableContext(rawPath, before, after) {
    if (basename(rawPath).toLowerCase() !== "pyproject.toml")
        return false;
    const context = `${before.slice(-180)} ${rawPath} ${after.slice(0, 100)}`;
    return (/\b(?:create|write|generate|include|add|provide)\b[\s\S]{0,120}\bpyproject\.toml\b/iu.test(context) || /\bpyproject\.toml\b[\s\S]{0,120}\b(?:must|should|needs to|has to)\b/iu.test(context));
}
function hasPromptDeliverableChangeContext(before, after) {
    return PROMPT_DELIVERABLE_CHANGE_CONTEXT_RE.test(`${before.slice(-120)} ${after.slice(0, 80)}`);
}
function hasPromptSingleFileContext(before, after) {
    return /\b(?:single|only)\s+(?:file|artifact|deliverable)\b/iu.test(`${before} ${after}`);
}
function normalizePromptDeliverablePath(rawPath, cwd, before, after) {
    if (rawPath.includes("<") || rawPath.includes(">")) {
        return undefined;
    }
    let candidate = rawPath
        .trim()
        .replace(/^\*+|\*+$/gu, "")
        .replace(/^[<([{]+/u, "")
        .replace(/[>\])},;:!?]+$/u, "");
    if (candidate.endsWith(".") && candidate.slice(0, -1).includes(".")) {
        candidate = candidate.slice(0, -1);
    }
    if (!candidate || candidate.includes("://") || candidate.includes("*") || /\s/u.test(candidate)) {
        return undefined;
    }
    if (!isLikelyPromptFilePath(candidate, before, after)) {
        return undefined;
    }
    const absolutePath = isAbsolute(candidate) ? resolve(candidate) : resolve(cwd, candidate);
    if (!isAbsolute(candidate)) {
        const workspaceRoot = resolve(cwd);
        const relativeToWorkspace = relative(workspaceRoot, absolutePath);
        if (relativeToWorkspace === "" ||
            relativeToWorkspace.startsWith("..") ||
            isAbsolute(relativeToWorkspace)) {
            return undefined;
        }
    }
    return {
        displayPath: isAbsolute(candidate) ? absolutePath : candidate,
        path: absolutePath,
    };
}
function isLikelyPromptFilePath(candidate, before, after) {
    const context = `${before.slice(-100)} ${after.slice(0, 100)}`;
    const baseName = candidate.split("/").pop() ?? candidate;
    const hasFileExtension = PROMPT_PATH_FILE_EXTENSION_RE.test(baseName);
    if (PROMPT_DIRECTORY_CONTEXT_RE.test(context) && !hasFileExtension) {
        return false;
    }
    return hasFileExtension || isAbsolute(candidate) || PROMPT_FILE_CONTEXT_RE.test(context);
}
function isBlankPromptDeliverable(path, byteSize) {
    if (byteSize === 0) {
        return true;
    }
    if (byteSize > 1_000_000) {
        return false;
    }
    try {
        return readFileSync(path, "utf8").trim().length === 0;
    }
    catch {
        return false;
    }
}
function readArtifactDigest(path) {
    try {
        const stats = statSync(path);
        if (!stats.isFile()) {
            return undefined;
        }
        return createHash("sha256").update(readFileSync(path)).digest("hex");
    }
    catch {
        return undefined;
    }
}
function readArtifactByteSize(path) {
    if (!path) {
        return undefined;
    }
    try {
        const stats = statSync(path);
        return stats.isFile() ? stats.size : undefined;
    }
    catch {
        return undefined;
    }
}
function violatesSizeLimit(size, limit) {
    if (typeof limit === "number") {
        return size > limit;
    }
    return limit.inclusive ? size > limit.bytes : size >= limit.bytes;
}
function getObligationConstraints(obligation) {
    return obligation.constraints ?? [];
}
function findMaxBytesConstraint(obligation) {
    for (const c of getObligationConstraints(obligation)) {
        if (c.kind === "max-bytes") {
            return { bytes: c.bytes, inclusive: c.inclusive };
        }
    }
    return undefined;
}
function findExclusiveDirectoryConstraint(obligation) {
    for (const c of getObligationConstraints(obligation)) {
        if (c.kind === "exclusive-directory") {
            return { directory: c.directory, allowedSiblingBasenames: c.allowedSiblingBasenames };
        }
    }
    return undefined;
}
function readAllowedSiblingBasenames(path) {
    const targetDirectory = dirname(path);
    const allowed = new Set([basename(path)]);
    try {
        for (const entry of readdirSync(targetDirectory)) {
            allowed.add(entry);
        }
    }
    catch { }
    return [...allowed];
}
function hasEvidenceForObligation(obligation, records, cwd) {
    const target = obligation.target ? resolve(cwd, obligation.target) : undefined;
    return records.some((record) => {
        if (target && record.relatedFilePaths?.some((path) => resolve(cwd, path) === target)) {
            return true;
        }
        return record.evidenceId === obligation.target;
    });
}
function hasStructuredEvidenceForObligation(obligation, records, cwd) {
    return records.some((record) => hasEvidenceForObligation(obligation, [record], cwd) &&
        (record.kind === "artifact-fact" ||
            record.payload.structuredOutput === true ||
            record.payload.structured === true));
}
function hasCommandZeroExitEvidence(obligation, records, cwd) {
    if (!obligation.target) {
        return false;
    }
    const target = normalizeCommandForEvidence(obligation.target);
    return records.some((record) => {
        if (record.kind !== "tool-outcome" || record.sourceToolName !== "shell") {
            return false;
        }
        const command = typeof record.payload.command === "string"
            ? record.payload.command
            : readStringRecordField(record.payload.envelope, "command");
        if (normalizeCommandForEvidence(command) !== target) {
            return false;
        }
        const evidencePayload = readRecordField(record.payload, "evidencePayload");
        const envelope = readRecordField(record.payload, "envelope");
        const envelopeEvidencePayload = readRecordField(envelope, "evidencePayload");
        const exitCode = readNumberRecordField(record.payload, "exitCode") ??
            readNumberRecordField(evidencePayload, "exitCode") ??
            readNumberRecordField(envelopeEvidencePayload, "exitCode");
        const signalCode = readStringRecordField(record.payload, "signalCode") ??
            readStringRecordField(evidencePayload, "signalCode") ??
            readStringRecordField(envelopeEvidencePayload, "signalCode");
        const timedOut = readBooleanRecordField(record.payload, "timedOut") ??
            readBooleanRecordField(evidencePayload, "timedOut") ??
            readBooleanRecordField(envelopeEvidencePayload, "timedOut");
        const cwdMatches = commandEvidenceCwdMatches(record.payload.cwd, cwd);
        return cwdMatches && exitCode === 0 && !signalCode && timedOut !== true;
    });
}
function normalizeCommandForEvidence(command) {
    return command?.trim() ?? "";
}
function commandEvidenceCwdMatches(recordCwd, cwd) {
    if (typeof recordCwd !== "string" || recordCwd.trim().length === 0) {
        return true;
    }
    return resolve(cwd, recordCwd) === resolve(cwd);
}
function readRecordField(value, key) {
    if (!value || typeof value !== "object" || Array.isArray(value)) {
        return undefined;
    }
    const field = value[key];
    return field && typeof field === "object" && !Array.isArray(field)
        ? field
        : undefined;
}
function readStringRecordField(value, key) {
    if (!value || typeof value !== "object" || Array.isArray(value)) {
        return undefined;
    }
    const field = value[key];
    return typeof field === "string" ? field : undefined;
}
function readNumberRecordField(value, key) {
    if (!value || typeof value !== "object" || Array.isArray(value)) {
        return undefined;
    }
    const field = value[key];
    return typeof field === "number" ? field : undefined;
}
function readBooleanRecordField(value, key) {
    if (!value || typeof value !== "object" || Array.isArray(value)) {
        return undefined;
    }
    const field = value[key];
    return typeof field === "boolean" ? field : undefined;
}
//# sourceMappingURL=artifact-obligations.js.map