/**
 * Delivery Run Coordinator — `uap agent` + `uap deploy` integration
 *
 * Makes a delivery run a first-class citizen of the multi-agent coordination
 * layer:
 *
 *  - registers an ephemeral agent for the run (visible in `uap agent status`)
 *  - announces work intent on the project root (overlap detection warns
 *    other agents — and this run — about concurrent edits)
 *  - heartbeats every loop turn so the run never shows as stale
 *  - marks work complete and deregisters when the loop finishes
 *  - on success, can queue a commit of the applied files into the deploy
 *    batcher (`uap deploy status` / `uap deploy flush`)
 *
 * Everything is fail-soft via lazy imports: a missing/locked coordination DB
 * degrades to a no-op coordinator rather than blocking delivery.
 */
const NOOP = {
    agentId: null,
    overlapWarnings: [],
    onIteration: () => undefined,
    finish: async () => undefined,
    queueDeploy: async () => null,
};
/** All files the loop applied across its iteration history, deduplicated. */
export function collectAppliedFiles(result) {
    const files = new Set();
    for (const record of result.history) {
        for (const f of record.filesApplied)
            files.add(f);
    }
    return [...files];
}
/**
 * Register this delivery run with the coordination service and announce its
 * work. Returns a no-op coordinator when the coordination layer is
 * unavailable — delivery must never fail because coordination did.
 */
export async function createRunCoordinator(options) {
    let registeredId = null;
    let registeredService = null;
    try {
        const { CoordinationService } = await import('../coordination/service.js');
        const service = new CoordinationService(options.dbPath ? { dbPath: options.dbPath } : {});
        const agentId = service.register(`uap-deliver-${options.modelId}`, [
            'delivery',
            'convergence-loop',
        ]);
        registeredId = agentId;
        registeredService = service;
        service.updateStatus(agentId, 'active', options.instruction.slice(0, 200));
        const { overlaps } = service.announceWork(agentId, options.projectRoot, 'editing', {
            description: `uap deliver: ${options.instruction.slice(0, 200)}`,
            estimatedMinutes: options.estimatedMinutes ?? 15,
        });
        const overlapWarnings = overlaps.map((o) => {
            const who = o.agents.map((a) => `${a.name} (${a.intentType})`).join(', ');
            return `${o.resource}: ${who} — ${o.conflictRisk} risk. ${o.suggestion}`;
        });
        // A single loop turn (model call + full gate run) routinely exceeds the
        // stale-agent cutoff, so heartbeat on a timer too — not just per turn —
        // or concurrent cleanup would mark this run stale mid-turn. unref() so
        // the timer never keeps the process alive.
        const heartbeatTimer = setInterval(() => {
            try {
                service.heartbeat(agentId);
            }
            catch {
                // Heartbeat is best-effort
            }
        }, 30_000);
        heartbeatTimer.unref?.();
        return {
            agentId,
            overlapWarnings,
            onIteration(record) {
                try {
                    service.heartbeat(agentId);
                    service.updateStatus(agentId, 'active', `turn ${record.turn}: ${Math.round(record.score * 100)}% of gates`);
                }
                catch {
                    // Heartbeat is best-effort
                }
            },
            async finish(result) {
                clearInterval(heartbeatTimer);
                try {
                    service.completeWork(agentId, options.projectRoot);
                    service.updateStatus(agentId, 'completed', result.success ? `delivered in ${result.turns} turn(s)` : 'not delivered');
                    service.deregister(agentId);
                }
                catch {
                    // Cleanup is best-effort
                }
            },
            async queueDeploy(result, message) {
                if (!result.success)
                    return null;
                try {
                    const { DeployBatcher } = await import('../coordination/deploy-batcher.js');
                    const batcher = new DeployBatcher(options.dbPath ? { dbPath: options.dbPath } : {});
                    const files = collectAppliedFiles(result);
                    if (files.length === 0)
                        return null;
                    return await batcher.queue(agentId, 'commit', options.projectRoot, {
                        message,
                        files,
                        cwd: options.projectRoot,
                        source: 'uap-deliver',
                    });
                }
                catch {
                    return null;
                }
            },
        };
    }
    catch {
        // If registration succeeded but a later step threw, don't leak an
        // 'active' phantom agent into the registry.
        if (registeredId && registeredService) {
            try {
                registeredService.deregister(registeredId);
            }
            catch {
                // best-effort
            }
        }
        return NOOP;
    }
}
//# sourceMappingURL=run-coordinator.js.map