"use strict";
/**
 * DA-MED-NEW-1 (P1H-01b): Manifest-integrity scanner rule class.
 *
 * Customer-facing scanner rules that detect the same defect class that
 * caused Claude Code's subagent loader to silently drop 19/20 agents on
 * 2026-04-21: agent manifests with missing required fields, invalid
 * field values, or name/filename mismatches. Each finding is HIGH
 * severity.
 *
 * Rule IDs:
 *   - MANI-001: Missing required field (name, description, tools, role)
 *   - MANI-002: Invalid field type (e.g. tools is a string when an array
 *     is required, platform is not a recognised enum member)
 *   - MANI-003: Name / filename mismatch (`foo.md` has `name: bar` in
 *     front-matter, a source of silent drops when the loader indexes
 *     by filename and dispatches by name)
 *
 * @connexum/ai-governance
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.evaluateManifest = evaluateManifest;
exports.evaluateManifests = evaluateManifests;
const log_1 = require("../util/log");
const log = (0, log_1.createLogger)('manifest-integrity');
/** Required front-matter fields for a Claude Code `.claude/agents/*.md` file. */
const REQUIRED_FIELDS = [
    'name',
    'description',
    'tools',
    'role',
];
/** Permitted enum values for the `platform` field, when present. */
const PLATFORM_ENUM = [
    'Claude', 'OpenAI', 'Gemini', 'DeepSeek', 'Microsoft', 'Amazon', 'Meta', 'Other',
];
function filenameNoExt(file) {
    const base = file.split('/').pop() ?? file;
    const dot = base.lastIndexOf('.');
    return dot > 0 ? base.slice(0, dot) : base;
}
/**
 * Run the three manifest-integrity rules against a single parsed manifest.
 * Returns every finding; callers can filter by severity or rule ID.
 */
function evaluateManifest(input) {
    const findings = [];
    const { file, manifest } = input;
    // MANI-001: the manifest itself is missing
    if (!manifest || typeof manifest !== 'object') {
        findings.push({
            ruleId: 'MANI-001',
            severity: 'HIGH',
            field: null,
            file,
            message: 'Manifest is empty or not a valid object. Front-matter may have failed to parse.',
            remediation: 'Add a YAML front-matter block at the top of the file with `name`, ' +
                '`description`, `tools`, and `role` fields. Without a valid manifest, ' +
                'the subagent loader may silently drop this agent.',
        });
        return findings;
    }
    // MANI-001: required field presence
    for (const field of REQUIRED_FIELDS) {
        const v = manifest[field];
        if (v === undefined || v === null || v === '') {
            findings.push({
                ruleId: 'MANI-001',
                severity: 'HIGH',
                field,
                file,
                message: `Required manifest field "${field}" is missing or empty.`,
                remediation: `Add the "${field}" field to the front-matter. Loaders that ` +
                    'index by presence of required fields silently skip agents when ' +
                    'a required field is absent, resulting in attribution drift.',
            });
        }
    }
    // MANI-002: invalid field types
    const nameVal = manifest.name;
    if (nameVal !== undefined && (typeof nameVal !== 'string' || String(nameVal).length === 0)) {
        findings.push({
            ruleId: 'MANI-002',
            severity: 'HIGH',
            field: 'name',
            file,
            message: `Field "name" must be a non-empty string, got ${typeof nameVal}.`,
            remediation: 'Set `name: "<agent-name>"` in the front-matter as a plain string.',
        });
    }
    const descVal = manifest.description;
    if (descVal !== undefined && typeof descVal !== 'string') {
        findings.push({
            ruleId: 'MANI-002',
            severity: 'HIGH',
            field: 'description',
            file,
            message: `Field "description" must be a string, got ${typeof descVal}.`,
            remediation: 'Quote the description as a single-line string.',
        });
    }
    const toolsVal = manifest.tools;
    if (toolsVal !== undefined && !Array.isArray(toolsVal) && typeof toolsVal !== 'string') {
        findings.push({
            ruleId: 'MANI-002',
            severity: 'HIGH',
            field: 'tools',
            file,
            message: `Field "tools" must be an array or comma-separated string, got ${typeof toolsVal}.`,
            remediation: 'Use either `tools: [Read, Edit]` or `tools: "Read, Edit"`.',
        });
    }
    const platformVal = manifest.platform;
    if (platformVal !== undefined) {
        if (typeof platformVal !== 'string' || !PLATFORM_ENUM.includes(platformVal)) {
            findings.push({
                ruleId: 'MANI-002',
                severity: 'HIGH',
                field: 'platform',
                file,
                message: `Field "platform" must be one of: ${PLATFORM_ENUM.join(', ')}.`,
                remediation: 'Set the platform enum explicitly. An unrecognised platform ' +
                    'leaves registry identity ambiguous and can route attribution ' +
                    'to the wrong provider.',
            });
        }
    }
    // MANI-003: name / filename mismatch
    const fileBase = filenameNoExt(file);
    if (typeof nameVal === 'string' && nameVal.length > 0 && fileBase.length > 0) {
        if (nameVal !== fileBase) {
            findings.push({
                ruleId: 'MANI-003',
                severity: 'HIGH',
                field: 'name',
                file,
                message: `Manifest name "${nameVal}" does not match filename "${fileBase}". ` +
                    'Loaders that index by filename and dispatch by name will silently ' +
                    'drop this agent.',
                remediation: `Rename the file to "${nameVal}.md" OR change the manifest name ` +
                    `field to "${fileBase}". The two must be identical so the loader ` +
                    'can resolve the same identity on index and dispatch.',
            });
        }
    }
    log.debug(`Evaluated manifest ${file}: ${findings.length} finding(s)`);
    return findings;
}
/**
 * Convenience: evaluate many manifests and return a flat list of findings.
 */
function evaluateManifests(inputs) {
    return inputs.flatMap((i) => evaluateManifest(i));
}
//# sourceMappingURL=manifest-integrity.js.map