"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_PROMPT_POLICY_RULE_IDS = exports.CANONICAL_POLICY_RULES = void 0;
exports.defineCanonicalPolicyRegistry = defineCanonicalPolicyRegistry;
exports.composeCanonicalPolicy = composeCanonicalPolicy;
function hasUnsafeControlCharacters(value) {
    for (let index = 0; index < value.length; index += 1) {
        const codePoint = value.charCodeAt(index);
        if ((codePoint >= 0 && codePoint <= 8) ||
            codePoint === 11 ||
            codePoint === 12 ||
            (codePoint >= 14 && codePoint <= 31) ||
            codePoint === 127) {
            return true;
        }
    }
    return false;
}
const POLICY_RULE_ID = /^policy\.[a-z0-9]+(?:[.-][a-z0-9]+)*$/;
const TASK_DELTA_ID = /^task\.[a-z0-9]+(?:[.-][a-z0-9]+)*$/;
const MAX_TASK_DELTA_INSTRUCTIONS = 16;
const MAX_TASK_DELTA_INSTRUCTION_BYTES = 512;
const MAX_TASK_DELTA_BYTES = 4096;
exports.CANONICAL_POLICY_RULES = defineCanonicalPolicyRegistry([
    {
        id: 'policy.action.authoritative-descriptor',
        text: 'Resolve callable commands and their posture from the authoritative command descriptor; unresolved guidance is non-callable.',
        appliesTo: ['callable-actions', 'all-prompts'],
        contractReferences: ['src/lib/output/command-descriptors.ts'],
    },
    {
        id: 'policy.action.explicit-authorization',
        text: 'A recommendation is evidence only; perform a write only through an explicitly selected write-capable command with its declared confirmation.',
        appliesTo: ['write-actions', 'all-prompts'],
        requires: ['policy.action.authoritative-descriptor'],
        contractReferences: ['src/lib/agent-context/workflow-actions.ts'],
    },
    {
        id: 'policy.context.selected-evidence',
        text: 'Keep selected-artifact context read-only and bounded; context evidence does not authorize a transition.',
        appliesTo: ['artifact-context', 'all-prompts'],
        requires: ['policy.action.explicit-authorization'],
        contractReferences: ['src/lib/agent-context/index.ts'],
    },
    {
        id: 'policy.mutation.atomic-boundary',
        text: "Preserve the command's declared atomic and precondition boundary; never infer successful mutation from prompt composition.",
        appliesTo: ['write-actions', 'all-prompts'],
        requires: ['policy.action.explicit-authorization'],
        contractReferences: ['src/lib/filesystem/atomic-write.ts'],
    },
    {
        id: 'policy.prompt.task-delta',
        text: 'Compose task-specific instructions as a bounded delta that references canonical rules without redefining them.',
        appliesTo: ['all-prompts'],
        requires: ['policy.context.selected-evidence', 'policy.mutation.atomic-boundary'],
    },
]);
exports.DEFAULT_PROMPT_POLICY_RULE_IDS = Object.freeze(['policy.prompt.task-delta']);
function defineCanonicalPolicyRegistry(rules) {
    const normalized = [];
    const seen = new Map();
    for (const rule of rules) {
        const current = normalizeRule(rule);
        const prior = seen.get(current.id);
        if (prior !== undefined) {
            const kind = sameRuleDefinition(prior, current) ? 'Duplicate' : 'Conflicting';
            throw new Error(`${kind} canonical policy rule definition for '${current.id}'.`);
        }
        seen.set(current.id, current);
        normalized.push(current);
    }
    validateDependencies(normalized, seen);
    return Object.freeze(normalized);
}
function composeCanonicalPolicy(options) {
    const registry = options.registry === undefined ? exports.CANONICAL_POLICY_RULES : defineCanonicalPolicyRegistry(options.registry);
    const byId = new Map(registry.map((rule) => [rule.id, rule]));
    const delta = normalizeTaskDelta(options.taskDelta ?? null, byId);
    const requestedIds = uniqueSorted([
        ...options.ruleIds.map((id) => normalizeRuleId(id, 'policy rule reference')),
        ...(delta?.ruleReferences ?? []),
    ]);
    const resolved = [];
    const emitted = new Set();
    const visit = (id) => {
        if (emitted.has(id))
            return;
        const rule = byId.get(id);
        if (rule === undefined) {
            throw new Error(`Unknown canonical policy rule reference '${id}'.`);
        }
        for (const dependencyId of rule.requires)
            visit(dependencyId);
        emitted.add(id);
        resolved.push(rule);
    };
    for (const id of requestedIds)
        visit(id);
    const taskDelta = delta === null
        ? null
        : Object.freeze({
            id: delta.id,
            instructions: delta.instructions,
        });
    const compactText = [
        ...resolved.map((rule) => `[${rule.id}] ${rule.text}`),
        ...(taskDelta === null ? [] : taskDelta.instructions.map((instruction) => `[${taskDelta.id}] ${instruction}`)),
    ].join('\n');
    return Object.freeze({
        ruleIds: Object.freeze(resolved.map((rule) => rule.id)),
        rules: Object.freeze(resolved),
        taskDelta,
        compactText,
    });
}
function normalizeRule(rule) {
    const id = normalizeRuleId(rule.id, 'canonical policy rule id');
    const text = requireBoundedText(rule.text, `${id} text`, 1024);
    const appliesTo = uniqueSorted(rule.appliesTo.map((value) => {
        if (!['all-prompts', 'artifact-context', 'callable-actions', 'write-actions'].includes(value)) {
            throw new Error(`Canonical policy rule '${id}' has invalid applicability '${value}'.`);
        }
        return value;
    }));
    if (appliesTo.length === 0) {
        throw new Error(`Canonical policy rule '${id}' must declare applicability.`);
    }
    return Object.freeze({
        id,
        text,
        appliesTo: Object.freeze(appliesTo),
        requires: Object.freeze(uniqueSorted((rule.requires ?? []).map((value) => normalizeRuleId(value, `${id} dependency`)))),
        contractReferences: Object.freeze(uniqueSorted((rule.contractReferences ?? []).map((value) => requireBoundedText(value, `${id} contract reference`, 512)))),
    });
}
function validateDependencies(rules, byId) {
    const completed = new Set();
    const active = new Set();
    const visit = (id) => {
        if (completed.has(id))
            return;
        if (active.has(id))
            throw new Error(`Canonical policy dependency cycle includes '${id}'.`);
        const rule = byId.get(id);
        if (rule === undefined)
            throw new Error(`Unknown canonical policy rule dependency '${id}'.`);
        active.add(id);
        for (const dependencyId of rule.requires)
            visit(dependencyId);
        active.delete(id);
        completed.add(id);
    };
    for (const rule of rules)
        visit(rule.id);
}
function normalizeTaskDelta(delta, byId) {
    if (delta === null)
        return null;
    const id = requireBoundedText(delta.id, 'task policy delta id', 128);
    if (!TASK_DELTA_ID.test(id)) {
        if (byId.has(id) || POLICY_RULE_ID.test(id)) {
            throw new Error(`Task policy delta '${id}' shadows a canonical policy rule id.`);
        }
        throw new Error(`Invalid task policy delta id '${id}'.`);
    }
    const ruleReferences = uniqueSorted((delta.ruleReferences ?? []).map((value) => normalizeRuleId(value, `${id} rule reference`)));
    const instructions = delta.instructions.map((value, index) => requireBoundedText(value, `${id} instruction ${index + 1}`, MAX_TASK_DELTA_INSTRUCTION_BYTES));
    if (instructions.length === 0 || instructions.length > MAX_TASK_DELTA_INSTRUCTIONS) {
        throw new Error(`Task policy delta '${id}' must contain 1-${MAX_TASK_DELTA_INSTRUCTIONS} instructions.`);
    }
    if (Buffer.byteLength(instructions.join('\n'), 'utf8') > MAX_TASK_DELTA_BYTES) {
        throw new Error(`Task policy delta '${id}' exceeds ${MAX_TASK_DELTA_BYTES} bytes.`);
    }
    return Object.freeze({
        id,
        ruleReferences: Object.freeze(ruleReferences),
        instructions: Object.freeze(instructions),
    });
}
function normalizeRuleId(value, label) {
    const id = requireBoundedText(value, label, 128);
    if (!POLICY_RULE_ID.test(id))
        throw new Error(`Invalid ${label} '${id}'.`);
    return id;
}
function requireBoundedText(value, label, maxBytes) {
    const normalized = value.trim();
    if (normalized.length === 0)
        throw new Error(`${label} must not be empty.`);
    if (hasUnsafeControlCharacters(normalized)) {
        throw new Error(`${label} must not contain terminal control characters.`);
    }
    if (Buffer.byteLength(normalized, 'utf8') > maxBytes) {
        throw new Error(`${label} exceeds ${maxBytes} bytes.`);
    }
    return normalized;
}
function uniqueSorted(values) {
    return [...new Set(values)].sort((left, right) => left.localeCompare(right));
}
function sameRuleDefinition(left, right) {
    return (left.text === right.text &&
        JSON.stringify(left.appliesTo) === JSON.stringify(right.appliesTo) &&
        JSON.stringify(left.requires) === JSON.stringify(right.requires) &&
        JSON.stringify(left.contractReferences) === JSON.stringify(right.contractReferences));
}
