// Planu — Agent Router Engine
// Pure routing engine — no side effects, no file I/O
// === Scope ranking ===
const SCOPE_RANK = {
    trivial: 0,
    feature: 1,
    'cross-module': 2,
    architectural: 3,
};
/**
 * Returns the numeric rank for a given scope string.
 * Unknown scopes default to 0 (trivial).
 */
function scopeRank(scope) {
    return SCOPE_RANK[scope] ?? 0;
}
// === Trigger matching ===
/**
 * Determines whether a registry entry should activate for the given spec.
 * An entry with empty triggers (no tags, no specTypes, no minScope) always matches.
 * When multiple trigger conditions are present, they are evaluated as OR:
 * the agent activates if ANY condition matches.
 * MinScope is an additional filter that applies independently (AND) on top of the OR match.
 */
function matchesTriggers(entry, spec) {
    const { triggers } = entry;
    if (!hasTriggerConstraints(triggers)) {
        return true;
    }
    // MinScope: if defined, it must be satisfied (hard gate — applied independently)
    if (triggers.minScope !== undefined) {
        if (scopeRank(spec.scope) < scopeRank(triggers.minScope)) {
            return false;
        }
        // If minScope is the ONLY constraint, it matches when satisfied
        /* c8 ignore next 4 */
        if ((triggers.tags === undefined || triggers.tags.length === 0) &&
            (triggers.specTypes === undefined || triggers.specTypes.length === 0)) {
            return true;
        }
    }
    // Tags + SpecType are evaluated as OR: any match is sufficient
    const tagList = triggers.tags ?? [];
    const typeList = triggers.specTypes ?? [];
    if (tagList.length > 0 && spec.tags.some((t) => tagList.includes(t))) {
        return true;
    }
    if (typeList.length > 0 && typeList.includes(spec.type)) {
        return true;
    }
    return false;
}
/**
 * Returns true when the triggers object has at least one constraint defined.
 */
function hasTriggerConstraints(triggers) {
    return ((triggers.tags !== undefined && triggers.tags.length > 0) ||
        (triggers.specTypes !== undefined && triggers.specTypes.length > 0) ||
        triggers.minScope !== undefined);
}
/**
 * Builds a human-readable string explaining why the agent was activated.
 */
function buildTriggerReason(entry, spec) {
    const { triggers } = entry;
    if (!hasTriggerConstraints(triggers)) {
        return `always active for phase "${entry.phase}"`;
    }
    const reasons = [];
    if (triggers.tags !== undefined && triggers.tags.length > 0) {
        const tagList = triggers.tags;
        const matched = spec.tags.filter((t) => tagList.includes(t));
        if (matched.length > 0) {
            reasons.push(`matched tags: ${matched.join(', ')}`);
        }
    }
    if (triggers.specTypes?.includes(spec.type) === true) {
        reasons.push(`spec type "${spec.type}" in [${triggers.specTypes.join(', ')}]`);
    }
    if (triggers.minScope !== undefined) {
        reasons.push(`scope "${spec.scope}" >= minScope "${triggers.minScope}"`);
    }
    /* c8 ignore next */
    return reasons.length > 0 ? reasons.join('; ') : `matched phase "${entry.phase}"`;
}
// === Public API ===
/**
 * Resolves which agents should dispatch for the given spec and phase.
 * Filters by: enabled, phase match, and trigger conditions.
 */
export function resolveAgents(spec, registry, phase) {
    const plans = [];
    for (const entry of registry) {
        if (!entry.enabled) {
            continue;
        }
        if (entry.phase !== phase) {
            continue;
        }
        if (!matchesTriggers(entry, spec)) {
            continue;
        }
        plans.push({
            agentId: entry.id,
            phase: entry.phase,
            model: entry.model,
            promptTemplatePath: entry.promptTemplate,
            triggerReason: buildTriggerReason(entry, spec),
        });
    }
    return plans;
}
//# sourceMappingURL=agent-router.js.map