// orchestrator/result-collector.ts — Result collection and merging (SPEC-079 AC-05)
// Collects outputs from agents and merges them into a unified result.
import { parseTokenUsage } from './agent-spawner.js';
import { resolveConflict } from './conflict-resolver.js';
/** Default cost per token (USD) by model tier. */
const COST_PER_TOKEN = {
    haiku: 0.00000025,
    sonnet: 0.000003,
    opus: 0.000015,
};
/**
 * Collect results from all agents into task result entries.
 */
export function collectTaskResults(agents, assignments) {
    return assignments.map((assignment) => {
        const agent = agents.find((a) => a.agentId === assignment.agentId);
        const duration = agent?.completedAt
            ? new Date(agent.completedAt).getTime() - new Date(agent.startedAt).getTime()
            : 0;
        return {
            taskId: assignment.taskId,
            agentId: assignment.agentId,
            status: agent?.status === 'completed' ? 'completed' : 'failed',
            output: agent ? parseAgentOutput(agent.stdout) : null,
            duration,
        };
    });
}
/**
 * Parse structured output from an agent's stdout.
 * Tries to extract JSON, falls back to raw text.
 */
export function parseAgentOutput(stdout) {
    // Try to find a JSON block
    const jsonMatch = /```json\s*([\s\S]*?)```/u.exec(stdout);
    if (jsonMatch?.[1]) {
        try {
            return JSON.parse(jsonMatch[1]);
        }
        catch {
            // Not valid JSON, fall through
        }
    }
    // Try raw JSON
    const trimmed = stdout.trim();
    if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
        try {
            return JSON.parse(trimmed);
        }
        catch {
            // Not valid JSON
        }
    }
    return trimmed || null;
}
/**
 * Detect file conflicts between agents and resolve them.
 */
export function detectAndResolveConflicts(assignments, agents, strategy) {
    // Build file-to-agents map
    const fileAgents = new Map();
    for (const assignment of assignments) {
        for (const file of assignment.files) {
            const existing = fileAgents.get(file) ?? [];
            if (!existing.includes(assignment.agentId)) {
                existing.push(assignment.agentId);
                fileAgents.set(file, existing);
            }
        }
    }
    const conflicts = [];
    for (const [file, agentIds] of fileAgents.entries()) {
        if (agentIds.length <= 1) {
            continue;
        }
        // Only consider it a conflict if multiple agents actually completed
        const completedAgents = agentIds.filter((id) => agents.some((a) => a.agentId === id && a.status === 'completed'));
        if (completedAgents.length <= 1) {
            continue;
        }
        const record = resolveConflict(file, completedAgents, strategy);
        conflicts.push(record);
    }
    return conflicts;
}
/**
 * Calculate total cost for the orchestration run.
 */
export function calculateCost(agents, defaultTier) {
    const breakdown = {};
    let totalTokens = 0;
    let totalUsd = 0;
    for (const agent of agents) {
        const tokens = agent.tokensUsed ?? parseTokenUsage(agent.stdout);
        const rate = COST_PER_TOKEN[defaultTier] ?? COST_PER_TOKEN.sonnet ?? 0.000003;
        const usd = tokens * rate;
        breakdown[agent.agentId] = { tokens, usd };
        totalTokens += tokens;
        totalUsd += usd;
    }
    return { tokens: totalTokens, usd: totalUsd, breakdown };
}
/**
 * Build the final orchestration result.
 */
export function buildOrchestrationResult(sessionId, agents, assignments, conflictStrategy, startTime) {
    const taskResults = collectTaskResults(agents, assignments);
    const conflicts = detectAndResolveConflicts(assignments, agents, conflictStrategy);
    const cost = calculateCost(agents, 'sonnet');
    const outputs = {};
    for (const result of taskResults) {
        outputs[result.taskId] = result.output;
    }
    return {
        sessionId,
        agents,
        tasks: taskResults,
        outputs,
        conflicts,
        duration: Date.now() - startTime,
        totalCost: cost,
    };
}
//# sourceMappingURL=result-collector.js.map