/**
 * Graph Router (ADR-030)
 *
 * Resolves classified capabilities into a stage-sequenced agent pipeline.
 * This is the core routing engine that replaces full-fleet dispatch with
 * targeted agent selection.
 *
 * Decision flow:
 *   1. Receive classified capabilities
 *   2. Resolve transitive dependencies
 *   3. Look up agents and repos from the graph
 *   4. Sequence agents by execution stage
 *   5. Enforce agent budget constraints
 *   6. Return a minimal, ordered pipeline
 */
import { getCapabilityByName, resolveDependencies } from './graph-loader.js';
import { normalizeAgents } from './weighted-resolver.js';
import { applyCompositions, formatCompositionExplanation } from './composition-engine.js';
const DEFAULT_BUDGET = {
    singleCapability: 5,
    multiCapability: 8,
    fullPipeline: 12,
    hardCeiling: 15,
};
// ============================================================================
// Router
// ============================================================================
/**
 * Route classified capabilities through the ecosystem graph to produce
 * a stage-sequenced agent pipeline.
 *
 * @param classification - Output from classifyCapabilities()
 * @param graph - Loaded ecosystem graph
 * @param budget - Optional agent budget overrides
 * @returns Ordered pipeline of agents grouped by stage
 */
export function routeCapabilities(classification, graph, budget) {
    const effectiveBudget = { ...DEFAULT_BUDGET, ...budget };
    // Step 1: Extract capability names from classification
    const classifiedNames = classification.capabilities.map(c => c.name);
    if (classifiedNames.length === 0) {
        return buildEmptyResult(classification);
    }
    // Step 2: Apply composition rules (ADR-031 Phase 3)
    let compositionResult;
    let expandedNames = classifiedNames;
    if (graph.compositions && graph.compositions.length > 0) {
        const validCaps = new Set(graph.capabilities.map(c => c.name));
        compositionResult = applyCompositions(classifiedNames, graph.compositions, validCaps, { agentBudget: effectiveBudget.hardCeiling });
        expandedNames = [...compositionResult.capabilities];
    }
    // Step 3: Resolve transitive dependencies
    const resolvedNames = resolveDependencies(graph, expandedNames);
    // Step 4: Look up all capability entries
    const entries = new Map();
    for (const name of resolvedNames) {
        const cap = getCapabilityByName(graph, name);
        if (cap) {
            entries.set(name, cap);
        }
    }
    // Step 5: Build agent list with deduplication and weighted selection
    const agentSet = new Map(); // key = agentName for dedup
    for (const [capName, cap] of entries) {
        const stage = cap.stage ?? inferStage(cap);
        const weightedAgents = normalizeAgents(cap.agents);
        // Sort by weight descending for selection priority
        const sorted = [...weightedAgents].sort((a, b) => b.weight - a.weight);
        for (const wa of sorted) {
            const repo = cap.repos[0] ?? 'unknown';
            // First occurrence wins (preserves capability with highest confidence)
            if (!agentSet.has(wa.name)) {
                agentSet.set(wa.name, {
                    agentName: wa.name,
                    capability: capName,
                    repo,
                    stage,
                    weight: wa.weight,
                    role: wa.role,
                });
            }
        }
    }
    // Step 6: Determine budget limit
    const capCount = classifiedNames.length;
    let maxAgents;
    if (capCount <= 1) {
        maxAgents = effectiveBudget.singleCapability;
    }
    else if (capCount <= 3) {
        maxAgents = effectiveBudget.multiCapability;
    }
    else {
        maxAgents = effectiveBudget.fullPipeline;
    }
    maxAgents = Math.min(maxAgents, effectiveBudget.hardCeiling);
    // Step 7: Prune if over budget (weight-aware: prefer primary > secondary > specialist)
    let agents = Array.from(agentSet.values());
    let pruned = false;
    if (agents.length > maxAgents) {
        agents = pruneAgents(agents, classification, maxAgents);
        pruned = true;
    }
    // Step 8: Group by stage in execution order
    const pipeline = buildPipeline(agents, graph.stages);
    // Step 9: Collect unique repos
    const repoSet = new Set();
    for (const agent of agents) {
        repoSet.add(agent.repo);
    }
    const repos = Array.from(repoSet);
    // Step 10: Build explanation
    const explanation = buildExplanation(classification, resolvedNames, classifiedNames, pipeline, agents.length, pruned, compositionResult);
    return {
        classification,
        resolvedCapabilities: resolvedNames,
        pipeline,
        totalAgents: agents.length,
        totalRepos: repos.length,
        repos,
        pruned,
        explanation,
        compositionResult,
    };
}
// ============================================================================
// Pruning
// ============================================================================
/**
 * Prune agents to fit within budget. Strategy:
 * 1. Rank agents by the confidence of the capability that selected them
 * 2. Ensure at least one agent per classified capability
 * 3. Fill remaining budget with highest-confidence agents
 */
function pruneAgents(agents, classification, maxAgents) {
    // Build confidence map from classification
    const confidenceMap = new Map();
    for (const c of classification.capabilities) {
        confidenceMap.set(c.name, c.confidence);
    }
    // Ensure at least one agent per classified capability
    const guaranteedAgents = new Map();
    for (const agent of agents) {
        if (!guaranteedAgents.has(agent.capability)) {
            guaranteedAgents.set(agent.capability, agent);
        }
    }
    const guaranteed = Array.from(guaranteedAgents.values());
    if (guaranteed.length >= maxAgents) {
        // Even one-per-capability exceeds budget — take top N by confidence
        return guaranteed
            .sort((a, b) => (confidenceMap.get(b.capability) ?? 0) - (confidenceMap.get(a.capability) ?? 0))
            .slice(0, maxAgents);
    }
    // Fill remaining slots — sort by: role priority (primary > secondary > specialist),
    // then by weight descending, then by capability confidence descending
    const guaranteedNames = new Set(guaranteed.map(a => a.agentName));
    const rolePriority = { primary: 3, secondary: 2, specialist: 1 };
    const remaining = agents
        .filter(a => !guaranteedNames.has(a.agentName))
        .sort((a, b) => {
        const roleDiff = (rolePriority[b.role] ?? 0) - (rolePriority[a.role] ?? 0);
        if (roleDiff !== 0)
            return roleDiff;
        const weightDiff = b.weight - a.weight;
        if (Math.abs(weightDiff) > 0.001)
            return weightDiff;
        return (confidenceMap.get(b.capability) ?? 0) - (confidenceMap.get(a.capability) ?? 0);
    });
    const remainingBudget = maxAgents - guaranteed.length;
    return [...guaranteed, ...remaining.slice(0, remainingBudget)];
}
// ============================================================================
// Pipeline Building
// ============================================================================
/**
 * Group agents by stage and order according to the graph's stage sequence.
 */
function buildPipeline(agents, stages) {
    const stageMap = new Map();
    for (const agent of agents) {
        const existing = stageMap.get(agent.stage);
        if (existing) {
            existing.push(agent);
        }
        else {
            stageMap.set(agent.stage, [agent]);
        }
    }
    // Return stages in order, only including non-empty ones
    const pipeline = [];
    for (const stage of stages) {
        const stageAgents = stageMap.get(stage);
        if (stageAgents && stageAgents.length > 0) {
            pipeline.push({ stage, agents: stageAgents });
        }
    }
    return pipeline;
}
/**
 * Infer a default stage for capabilities that don't have one assigned.
 */
function inferStage(_cap) {
    return 'build';
}
// ============================================================================
// Explanation
// ============================================================================
function buildExplanation(classification, resolvedNames, classifiedNames, pipeline, totalAgents, pruned, compositionResult) {
    const lines = [];
    lines.push('Capability Graph Routing:');
    // Classification
    lines.push(`  Classified: [${classifiedNames.join(', ')}]`);
    for (const c of classification.capabilities) {
        lines.push(`    ${c.name}: ${c.matchType} match (${(c.confidence * 100).toFixed(0)}%) via [${c.matchedTokens.join(', ')}]`);
    }
    // Composition rules (ADR-031 Phase 3)
    if (compositionResult && compositionResult.firedRules.length > 0) {
        lines.push(formatCompositionExplanation(compositionResult));
    }
    // Dependencies
    const addedDeps = resolvedNames.filter(n => !classifiedNames.includes(n));
    if (addedDeps.length > 0) {
        lines.push(`  Dependencies added: [${addedDeps.join(', ')}]`);
    }
    // Pipeline
    lines.push(`  Pipeline (${totalAgents} agents)${pruned ? ' [PRUNED]' : ''}:`);
    for (const group of pipeline) {
        const agentNames = group.agents.map(a => a.agentName).join(', ');
        lines.push(`    ${group.stage}: ${agentNames}`);
    }
    return lines.join('\n');
}
// ============================================================================
// Empty Result
// ============================================================================
function buildEmptyResult(classification) {
    return {
        classification,
        resolvedCapabilities: [],
        pipeline: [],
        totalAgents: 0,
        totalRepos: 0,
        repos: [],
        pruned: false,
        explanation: 'Capability Graph Routing:\n  No capabilities matched. Falling back to full-fleet dispatch.',
    };
}
/**
 * Comprehensive mapping from graph agent display names to domain/agent pairs.
 * Maintained manually; must stay in sync with AGENT_DOMAINS in agents.ts.
 */
const AGENT_NAME_TO_DOMAIN = new Map([
    // test-bench
    ['Benchmark Runner Agent', { domain: 'test-bench', agent: 'benchmark' }],
    ['Model Comparator Agent', { domain: 'test-bench', agent: 'compare' }],
    ['Regression Detection Agent', { domain: 'test-bench', agent: 'regression' }],
    ['Quality Scoring Agent', { domain: 'test-bench', agent: 'quality' }],
    ['Hallucination Detection Agent', { domain: 'test-bench', agent: 'hallucination' }],
    ['Faithfulness Verification Agent', { domain: 'test-bench', agent: 'faithfulness' }],
    ['Bias Detection Agent', { domain: 'test-bench', agent: 'bias' }],
    ['Prompt Sensitivity Agent', { domain: 'test-bench', agent: 'prompt-sensitivity' }],
    ['Output Consistency Agent', { domain: 'test-bench', agent: 'consistency' }],
    ['Stress Test Agent', { domain: 'test-bench', agent: 'stress' }],
    ['Red Team Agent', { domain: 'test-bench', agent: 'red-team' }],
    ['Adversarial Prompt Agent', { domain: 'test-bench', agent: 'adversarial' }],
    ['Golden Dataset Validator Agent', { domain: 'test-bench', agent: 'golden-dataset' }],
    ['Synthetic Data Generator Agent', { domain: 'test-bench', agent: 'synthetic-data' }],
    // observatory
    ['Telemetry Collector Agent', { domain: 'observatory', agent: 'telemetry' }],
    ['Usage Pattern Agent', { domain: 'observatory', agent: 'usage-patterns' }],
    ['Failure Classification Agent', { domain: 'observatory', agent: 'failures' }],
    ['Health Check Agent', { domain: 'observatory', agent: 'health-check' }],
    ['SLO / SLA Enforcement Agent', { domain: 'observatory', agent: 'slo' }],
    ['Post-Mortem Generator Agent', { domain: 'observatory', agent: 'post-mortem' }],
    ['Visualization Spec Agent', { domain: 'observatory', agent: 'visualization' }],
    // shield
    ['Prompt Injection Detection Agent', { domain: 'shield', agent: 'prompt-injection' }],
    ['PII Detection Agent', { domain: 'shield', agent: 'pii' }],
    ['Data Redaction Agent', { domain: 'shield', agent: 'redaction' }],
    ['Secrets Leakage Agent', { domain: 'shield', agent: 'secrets' }],
    ['Toxicity Detection Agent', { domain: 'shield', agent: 'toxicity' }],
    ['Safety Boundary Agent', { domain: 'shield', agent: 'safety-boundary' }],
    ['Content Moderation Agent', { domain: 'shield', agent: 'moderation' }],
    ['Model Abuse Detection Agent', { domain: 'shield', agent: 'abuse' }],
    ['Credential Exposure Agent', { domain: 'shield', agent: 'credential-exposure' }],
    // sentinel
    ['Anomaly Detection Agent', { domain: 'sentinel', agent: 'anomaly' }],
    ['Drift Detection Agent', { domain: 'sentinel', agent: 'drift' }],
    ['Alerting Agent', { domain: 'sentinel', agent: 'alert' }],
    ['Incident Correlation Agent', { domain: 'sentinel', agent: 'correlation' }],
    ['Root Cause Analysis Agent', { domain: 'sentinel', agent: 'rca' }],
    // memory-graph
    ['Conversation Memory Agent', { domain: 'memory-graph', agent: 'conversation' }],
    ['Prompt Lineage Agent', { domain: 'memory-graph', agent: 'lineage' }],
    ['Decision Memory Agent', { domain: 'memory-graph', agent: 'decisions' }],
    ['Knowledge Graph Builder Agent', { domain: 'memory-graph', agent: 'knowledge-graph' }],
    ['Memory Retrieval Agent', { domain: 'memory-graph', agent: 'retrieval' }],
    ['Long-Term Pattern Agent', { domain: 'memory-graph', agent: 'patterns' }],
    // latency-lens
    ['Latency Analysis Agent', { domain: 'latency-lens', agent: 'latency' }],
    ['Cold Start Mitigation Agent', { domain: 'latency-lens', agent: 'cold-start' }],
    // forge
    ['SDK Generator Agent', { domain: 'forge', agent: 'sdk' }],
    ['CLI Command Generator Agent', { domain: 'forge', agent: 'cli' }],
    ['API Translation Agent', { domain: 'forge', agent: 'api-translation' }],
    ['Version Compatibility Agent', { domain: 'forge', agent: 'version-compat' }],
    // edge
    ['Tool Invocation Agent', { domain: 'edge', agent: 'tool-invoke' }],
    ['Circuit Breaker Agent', { domain: 'edge', agent: 'circuit-breaker' }],
    ['Failover Agent', { domain: 'edge', agent: 'failover' }],
    ['Execution Guard Agent', { domain: 'edge', agent: 'execution-guard' }],
    ['Caching Strategy Agent', { domain: 'edge', agent: 'caching' }],
    // auto-optimizer
    ['Self-Optimizing Agent', { domain: 'auto-optimizer', agent: 'self-optimize' }],
    ['Token Optimization Agent', { domain: 'auto-optimizer', agent: 'token' }],
    ['Model Selection Agent', { domain: 'auto-optimizer', agent: 'model-select' }],
    // incident-manager
    ['Incident Escalation Agent', { domain: 'incident-manager', agent: 'escalation' }],
    ['Human-in-the-Loop Agent', { domain: 'incident-manager', agent: 'hitl' }],
    // orchestrator
    ['Workflow Orchestrator Agent', { domain: 'orchestrator', agent: 'workflow' }],
    ['Task Scheduler Agent', { domain: 'orchestrator', agent: 'scheduler' }],
    ['Dependency Resolver Agent', { domain: 'orchestrator', agent: 'dependencies' }],
    ['Retry & Recovery Agent', { domain: 'orchestrator', agent: 'retry' }],
    ['Parallelization Agent', { domain: 'orchestrator', agent: 'parallel' }],
    ['State Machine Agent', { domain: 'orchestrator', agent: 'state-machine' }],
    ['Swarm Coordinator Agent', { domain: 'orchestrator', agent: 'swarm' }],
    // costops
    ['Cost Attribution Agent', { domain: 'costops', agent: 'attribution' }],
    ['Cost Forecasting Agent', { domain: 'costops', agent: 'forecast' }],
    ['Budget Enforcement Agent', { domain: 'costops', agent: 'budget' }],
    ['ROI Estimation Agent', { domain: 'costops', agent: 'roi' }],
    ['Cost-Performance Tradeoff Agent', { domain: 'costops', agent: 'tradeoff' }],
    // governance-dashboard
    ['Governance Audit Agent', { domain: 'governance-dashboard', agent: 'audit' }],
    ['Change Impact Agent', { domain: 'governance-dashboard', agent: 'impact' }],
    ['Usage Oversight Agent', { domain: 'governance-dashboard', agent: 'oversight' }],
    // policy-engine
    ['Policy Enforcement Agent', { domain: 'policy-engine', agent: 'enforce' }],
    ['Constraint Solver Agent', { domain: 'policy-engine', agent: 'constraints' }],
    ['Approval Routing Agent', { domain: 'policy-engine', agent: 'approval' }],
    // registry
    ['Registry Indexing Agent', { domain: 'registry', agent: 'index' }],
    ['Agent Reputation Agent', { domain: 'registry', agent: 'reputation' }],
    ['Template Bootstrap Agent', { domain: 'registry', agent: 'bootstrap' }],
    // marketplace
    ['Marketplace Packaging Agent', { domain: 'marketplace', agent: 'package' }],
    ['Deprecation Agent', { domain: 'marketplace', agent: 'deprecation' }],
    // analytics-hub
    ['Consensus Agent', { domain: 'analytics-hub', agent: 'consensus' }],
    ['Strategic Recommendation Agent', { domain: 'analytics-hub', agent: 'recommendation' }],
    // config-manager (shared agent name)
    // schema-registry (shared agent name)
    // connector-hub
    ['ERP Integration Agent', { domain: 'connector-hub', agent: 'erp-surface' }],
    // copilot
    ['Planner Agent', { domain: 'copilot', agent: 'planner' }],
    ['Decomposer Agent', { domain: 'copilot', agent: 'decomposer' }],
    ['Objective Clarifier Agent', { domain: 'copilot', agent: 'clarifier' }],
    ['Intent Classifier Agent', { domain: 'copilot', agent: 'intent' }],
    ['Reflection Agent', { domain: 'copilot', agent: 'reflection' }],
    ['Meta-Reasoner Agent', { domain: 'copilot', agent: 'meta-reasoner' }],
    // simulator
    ['What-If Simulation Agent', { domain: 'simulator', agent: 'what-if' }],
    ['Scenario Generator Agent', { domain: 'simulator', agent: 'scenario' }],
    // benchmark-exchange
    ['Benchmark Publication Agent', { domain: 'benchmark-exchange', agent: 'publish' }],
    // inference-gateway
    ['Inference Routing Agent', { domain: 'inference-gateway', agent: 'route' }],
    // data-vault
    ['Data Access Control Agent', { domain: 'data-vault', agent: 'access-control' }],
    ['Dataset Anonymization Agent', { domain: 'data-vault', agent: 'anonymize' }],
    // research-lab
    ['Hypothesis Agent', { domain: 'research-lab', agent: 'hypothesis' }],
    ['Experimental Metric Agent', { domain: 'research-lab', agent: 'metrics' }],
    // platform
    ['Decision Agent', { domain: 'platform', agent: 'decision' }],
    ['Executive Summary Agent', { domain: 'platform', agent: 'executive-summary' }],
    ['Decision Memo Agent', { domain: 'platform', agent: 'decision-memo' }],
    ['Risk Scoring Agent', { domain: 'platform', agent: 'risk-score' }],
    // enterprise
    ['Enterprise Strategy Agent', { domain: 'costops', agent: 'roi' }],
]);
/**
 * Resolve a graph agent name to its domain/agent invocation reference.
 * Returns undefined for unknown agents.
 */
export function resolveAgentRef(agentName) {
    return AGENT_NAME_TO_DOMAIN.get(agentName);
}
/**
 * Convert a routing result into a flat list of domain/agent pairs
 * ready for dispatch via the existing executeAgentsInvokeCommand.
 *
 * Agents that cannot be mapped are logged and skipped.
 */
export function toDomainAgentList(result) {
    const refs = [];
    const seen = new Set();
    for (const group of result.pipeline) {
        for (const agent of group.agents) {
            const ref = resolveAgentRef(agent.agentName);
            if (ref) {
                const key = `${ref.domain}/${ref.agent}`;
                if (!seen.has(key)) {
                    seen.add(key);
                    refs.push(ref);
                }
            }
        }
    }
    return refs;
}
//# sourceMappingURL=graph-router.js.map