// engine/agent-handoff-store.ts — SPEC-639: handoff.json protocol for agent teams
import { readFile, writeFile, mkdir } from 'node:fs/promises';
import { join } from 'node:path';
function handoffPath(projectPath, specId) {
    return join(projectPath, '.planu', 'team', specId, 'handoff.json');
}
/**
 * SPEC-639: Initialize handoff.json for a spec team.
 * Creates the directory and empty handoff file if it does not exist.
 */
export async function initTeamHandoff(projectPath, specId, agentIds) {
    const filePath = handoffPath(projectPath, specId);
    await mkdir(join(filePath, '..'), { recursive: true });
    const now = new Date().toISOString();
    const agents = {};
    for (const agentId of agentIds) {
        agents[agentId] = {
            agentId,
            status: 'pending',
            filesCreated: [],
            filesModified: [],
            summary: '',
            updatedAt: now,
        };
    }
    const handoff = { specId, createdAt: now, updatedAt: now, agents };
    await writeFile(filePath, JSON.stringify(handoff, null, 2) + '\n', 'utf-8');
    return handoff;
}
/**
 * SPEC-639: Read the current handoff.json for a spec team.
 * Returns null if not initialized.
 */
export async function readTeamHandoff(projectPath, specId) {
    try {
        const raw = await readFile(handoffPath(projectPath, specId), 'utf-8');
        return JSON.parse(raw);
    }
    catch {
        return null;
    }
}
/**
 * SPEC-639: Write an agent's completion entry to handoff.json.
 * Called by a worker when it finishes (or fails).
 */
export async function writeAgentHandoffEntry(projectPath, specId, entry) {
    const current = await readTeamHandoff(projectPath, specId);
    const now = new Date().toISOString();
    const base = current ?? {
        specId,
        createdAt: now,
        updatedAt: now,
        agents: {},
    };
    base.agents[entry.agentId] = { ...entry, updatedAt: now };
    base.updatedAt = now;
    const filePath = handoffPath(projectPath, specId);
    await mkdir(join(filePath, '..'), { recursive: true });
    await writeFile(filePath, JSON.stringify(base, null, 2) + '\n', 'utf-8');
    // SPEC-691: fire-and-forget telemetry on agent completion
    if (entry.status === 'done' || entry.status === 'failed') {
        void import('./agent-telemetry.js')
            .then(({ appendTelemetryRecord }) => appendTelemetryRecord(projectPath, specId, {
            agentId: entry.agentId,
            role: entry.agentId.split('-')[0] ?? 'coder',
            startedAt: base.createdAt,
            completedAt: now,
            durationMs: Date.now() - new Date(base.createdAt).getTime(),
            tokensIn: 0,
            tokensOut: 0,
            model: 'unknown',
            costUsd: 0,
            status: entry.status,
        }))
            .catch(() => {
            /* best-effort */
        });
    }
    return base;
}
/**
 * SPEC-639: Return the list of agents that have failed and need re-queuing.
 */
export function getFailedAgents(handoff) {
    return Object.values(handoff.agents).filter((a) => a.status === 'failed');
}
/**
 * SPEC-639: Return true when all agents are done (none pending or failed).
 * QA may start when this returns true.
 */
export function allWorkersDone(handoff) {
    return Object.values(handoff.agents).every((a) => a.status === 'done' || a.status === 'failed');
}
/**
 * SPEC-639: Collect all files touched by workers for QA consumption.
 */
export function collectAllWorkerFiles(handoff) {
    const created = new Set();
    const modified = new Set();
    for (const entry of Object.values(handoff.agents)) {
        if (entry.status === 'done') {
            entry.filesCreated.forEach((f) => created.add(f));
            entry.filesModified.forEach((f) => modified.add(f));
        }
    }
    return { filesCreated: [...created], filesModified: [...modified] };
}
//# sourceMappingURL=agent-handoff-store.js.map