/**
 * Agent Integrity Verifier
 *
 * Verifies consensus integrity in multi-agent systems.
 * Detects single outlier domination, low-entropy agreements,
 * and validates patch signing with Sigstore.
 *
 * @module agents/agent-integrity
 */
import { createHash } from "crypto";
// ============================================================================
// Consensus Analysis
// ============================================================================
/**
 * Calculate Shannon entropy of a distribution
 */
function calculateEntropy(distribution) {
    const total = distribution.reduce((sum, v) => sum + v, 0);
    if (total === 0)
        return 0;
    let entropy = 0;
    for (const count of distribution) {
        if (count > 0) {
            const p = count / total;
            entropy -= p * Math.log2(p);
        }
    }
    // Normalize to 0-100 scale based on max possible entropy
    const maxEntropy = Math.log2(distribution.length || 1);
    return maxEntropy > 0 ? (entropy / maxEntropy) * 100 : 0;
}
/**
 * Calculate domination score (how much one agent dominates)
 */
function calculateDominationScore(distribution) {
    const values = Object.values(distribution);
    const total = values.reduce((sum, v) => sum + v, 0);
    if (total === 0)
        return 0;
    const max = Math.max(...values);
    // Domination = max contribution as percentage of total, scaled to 0-100
    return (max / total) * 100;
}
/**
 * Detect outlier agents
 */
function detectOutliers(contributions) {
    if (contributions.length < 3)
        return [];
    const findings = contributions.map((c) => c.findings);
    const mean = findings.reduce((sum, v) => sum + v, 0) / findings.length;
    const stdDev = Math.sqrt(findings.reduce((sum, v) => sum + Math.pow(v - mean, 2), 0) / findings.length);
    // Agents more than 2 standard deviations from mean
    return contributions
        .filter((c) => Math.abs(c.findings - mean) > 2 * stdDev)
        .map((c) => c.agent);
}
/**
 * Analyze consensus health
 */
export function analyzeConsensus(contributions) {
    const findings = [];
    // Build distribution
    const distribution = {};
    for (const contrib of contributions) {
        distribution[contrib.agent] = contrib.findings;
    }
    // Calculate metrics
    const dominationScore = calculateDominationScore(distribution);
    const entropyScore = calculateEntropy(Object.values(distribution));
    const outlierAgents = detectOutliers(contributions);
    // Check for domination
    if (dominationScore > 70) {
        const dominant = contributions.reduce((max, c) => c.findings > (max?.findings || 0) ? c : max, contributions[0]);
        findings.push({
            type: "domination",
            severity: "high",
            description: `Single agent dominates consensus (${dominant.agent}: ${dominationScore.toFixed(1)}% of findings)`,
            evidence: `Agent ${dominant.agent} contributed ${dominant.findings} findings out of total`,
            affectedAgents: [dominant.agent],
            recommendation: "Review dominant agent's findings carefully and ensure other agents have sufficient coverage",
        });
    }
    // Check for low entropy (suspiciously uniform agreement)
    if (contributions.length >= 3 && entropyScore < 30) {
        findings.push({
            type: "low-entropy",
            severity: "medium",
            description: "Low diversity in agent findings (suspiciously uniform)",
            evidence: `Entropy score: ${entropyScore.toFixed(1)}/100 (expected >50 for healthy consensus)`,
            recommendation: "Verify agents are running independently and not sharing state",
        });
    }
    // Report outliers
    if (outlierAgents.length > 0) {
        findings.push({
            type: "outlier",
            severity: "medium",
            description: `${outlierAgents.length} agent(s) significantly deviate from consensus`,
            evidence: `Outlier agents: ${outlierAgents.join(", ")}`,
            affectedAgents: outlierAgents,
            recommendation: "Review outlier findings - may indicate bugs or unique perspectives",
        });
    }
    // Check timing patterns
    const timestamps = contributions.map((c) => new Date(c.timestamp).getTime());
    const timeDiffs = [];
    for (let i = 1; i < timestamps.length; i++) {
        timeDiffs.push(timestamps[i] - timestamps[i - 1]);
    }
    if (timeDiffs.length > 0) {
        const avgDiff = timeDiffs.reduce((sum, d) => sum + d, 0) / timeDiffs.length;
        const suspiciouslyFast = timeDiffs.filter((d) => d < 100).length;
        if (suspiciouslyFast > timeDiffs.length / 2) {
            findings.push({
                type: "timing-anomaly",
                severity: "medium",
                description: "Suspicious timing pattern - agents completed too quickly",
                evidence: `${suspiciouslyFast}/${timeDiffs.length} intervals under 100ms (avg: ${avgDiff.toFixed(0)}ms)`,
                recommendation: "Verify agents are running independently and not pre-cached",
            });
        }
    }
    return {
        totalAgents: contributions.length,
        activeAgents: contributions.filter((c) => c.findings > 0).length,
        findingsDistribution: distribution,
        dominationScore,
        entropyScore,
        outlierAgents,
        suspiciousPatterns: findings,
        isHealthy: findings.filter((f) => f.severity === "high" || f.severity === "critical").length === 0,
    };
}
// ============================================================================
// Hash Verification
// ============================================================================
/**
 * Calculate hash for a finding
 */
export function hashFinding(finding) {
    const content = JSON.stringify({
        severity: finding.severity,
        category: finding.category,
        description: finding.description,
        evidence: finding.evidence,
    });
    return createHash("sha256").update(content).digest("hex");
}
/**
 * Verify a finding's hash chain
 */
export function verifyFindingChain(findingId, finding, verifications) {
    const originalHash = hashFinding(finding);
    const chain = {
        findingId,
        originalHash,
        verifications: [],
        isValid: true,
    };
    let previousHash = originalHash;
    for (const v of verifications) {
        const verificationData = JSON.stringify({
            previousHash,
            agent: v.verifying_agent,
            verdict: v.verdict,
            evidence: v.evidence,
        });
        const verificationHash = createHash("sha256").update(verificationData).digest("hex");
        chain.verifications.push({
            agent: v.verifying_agent,
            hash: verificationHash,
            timestamp: v.created_at,
            verdict: v.verdict,
        });
        previousHash = verificationHash;
    }
    return chain;
}
// ============================================================================
// Cross-Contamination Detection
// ============================================================================
/**
 * Detect findings that appear to be copied between agents
 */
export function detectCrossContamination(agentFindings) {
    const findings = [];
    // Build hash map of all findings
    const hashToAgents = {};
    for (const [agent, agentFindingList] of Object.entries(agentFindings)) {
        for (const finding of agentFindingList) {
            const hash = hashFinding({
                severity: finding.severity,
                category: finding.category,
                description: finding.description,
                evidence: finding.evidence,
            });
            if (!hashToAgents[hash]) {
                hashToAgents[hash] = [];
            }
            hashToAgents[hash].push(agent);
        }
    }
    // Find duplicate findings across different agents
    const duplicates = Object.entries(hashToAgents)
        .filter(([_, agents]) => agents.length > 1);
    if (duplicates.length > 5) {
        const affectedAgents = [...new Set(duplicates.flatMap(([_, agents]) => agents))];
        findings.push({
            type: "cross-contamination",
            severity: "high",
            description: `${duplicates.length} findings appear identical across multiple agents`,
            evidence: `Affected agents: ${affectedAgents.join(", ")}`,
            affectedAgents,
            recommendation: "Verify agents are running independently. Identical findings may indicate shared state or caching.",
        });
    }
    return findings;
}
// ============================================================================
// Main Integrity Check
// ============================================================================
/**
 * Run full integrity verification
 */
export async function runIntegrityCheck(contributions, agentFindings, options) {
    // Analyze consensus
    const consensus = analyzeConsensus(contributions);
    // Check cross-contamination
    const crossContamination = options?.checkCrossContamination
        ? detectCrossContamination(agentFindings)
        : [];
    // Verify hash chains
    const chains = [];
    let verified = 0;
    let failed = 0;
    if (options?.verifyHashes) {
        for (const [_, findingList] of Object.entries(agentFindings)) {
            for (const finding of findingList) {
                const chain = verifyFindingChain(finding.id, finding, finding.verifications);
                chains.push(chain);
                if (chain.isValid)
                    verified++;
                else
                    failed++;
            }
        }
    }
    // Determine overall health
    const criticalIssues = [
        ...consensus.suspiciousPatterns,
        ...crossContamination,
    ].filter((f) => f.severity === "critical").length;
    const highIssues = [
        ...consensus.suspiciousPatterns,
        ...crossContamination,
    ].filter((f) => f.severity === "high").length;
    let overallHealth = "healthy";
    if (criticalIssues > 0 || failed > 0) {
        overallHealth = "critical";
    }
    else if (highIssues > 0 || !consensus.isHealthy) {
        overallHealth = "warning";
    }
    // Generate recommendations
    const recommendations = [];
    if (!consensus.isHealthy) {
        recommendations.push("Consensus health issues detected. Review suspicious patterns.");
    }
    if (crossContamination.length > 0) {
        recommendations.push("Cross-contamination detected. Ensure agent isolation.");
    }
    if (failed > 0) {
        recommendations.push(`${failed} finding(s) failed hash verification. Check for tampering.`);
    }
    if (consensus.outlierAgents.length > 0) {
        recommendations.push(`Review outlier agents: ${consensus.outlierAgents.join(", ")}`);
    }
    return {
        consensus,
        crossContamination,
        hashVerification: {
            verified,
            failed,
            chains: chains.slice(0, 10), // Return first 10 chains only
        },
        overallHealth,
        recommendations,
    };
}
/**
 * Convert integrity findings to certification findings
 */
export function integrityToFindings(analysis, crossContamination) {
    const allFindings = [...analysis.suspiciousPatterns, ...crossContamination];
    return allFindings.map((f, i) => ({
        id: `int-${String(i + 1).padStart(3, "0")}`,
        severity: f.severity,
        category: "consensus-manipulation",
        description: f.description,
        evidence: f.evidence,
        confidence: 85,
        verifications: [],
        created_at: new Date().toISOString(),
        scanner_source: undefined,
    }));
}
//# sourceMappingURL=agent-integrity.js.map