/**
 * RelayAdapter — high-level interface over the broker SDK.
 *
 * Wraps AgentRelayClient with auto-start, safe result patterns,
 * and convenience methods. Usable by any integration: dashboard,
 * MCP, ACP bridge, CLI, or custom tooling.
 *
 * Usage:
 *   import { RelayAdapter } from '@agent-relay/sdk';
 *
 *   const relay = new RelayAdapter({ cwd: projectRoot });
 *   await relay.start();
 *
 *   await relay.spawn({ name: 'Worker1', cli: 'claude', task: 'Build auth' });
 *   const agents = await relay.listAgents();
 *   await relay.sendMessage({ to: 'Worker1', text: 'status?' });
 *   await relay.release('Worker1');
 *
 *   await relay.shutdown();
 */
import { AgentRelayClient } from './client.js';
const WORKFLOW_BOOTSTRAP_TASK = 'You are connected to Agent Relay. Do not reply to this message and wait for relay messages and respond using Relaycast MCP tools.';
const WORKFLOW_CONVENTIONS = [
    'Messaging requirements:',
    '- When you receive `Relay message from <sender> ...`, reply using `mcp__relaycast__message_dm_send(to: "<sender>", text: "...")`.',
    '- Send `ACK: ...` when you receive a task.',
    '- Send `DONE: ...` when the task is complete.',
    '- Do not reply only in terminal text; send the response via mcp__relaycast__message_dm_send.',
    '- Use mcp__relaycast__message_inbox_check() and mcp__relaycast__agent_list() when context is missing.',
].join('\n');
function hasWorkflowConventions(task) {
    const lower = task.toLowerCase();
    return (lower.includes('mcp__relaycast__message_dm_send(') ||
        lower.includes('relay_send(') ||
        (lower.includes('ack:') && lower.includes('done:')));
}
function buildSpawnTask(task, includeWorkflowConventions) {
    const normalized = typeof task === 'string' ? task.trim() : '';
    if (!includeWorkflowConventions) {
        return normalized.length > 0 ? normalized : undefined;
    }
    if (normalized.length === 0) {
        return `${WORKFLOW_BOOTSTRAP_TASK}\n\n${WORKFLOW_CONVENTIONS}`;
    }
    if (hasWorkflowConventions(normalized)) {
        return normalized;
    }
    return `${normalized}\n\n${WORKFLOW_CONVENTIONS}`;
}
// ── Adapter ─────────────────────────────────────────────────────────
export class RelayAdapter {
    client = null;
    started = false;
    startPromise = null;
    spawnOpts;
    stderrListeners = new Set();
    pendingEventListeners = [];
    constructor(opts) {
        this.spawnOpts = {
            binaryPath: opts.binaryPath,
            channels: opts.channels ?? ['general'],
            cwd: opts.cwd,
            env: opts.env,
        };
    }
    ensureClient() {
        if (!this.client)
            throw new Error('RelayAdapter not started — call start() first');
        return this.client;
    }
    // ── Lifecycle ───────────────────────────────────────────────────
    /** Start the broker process. Idempotent — concurrent calls share one spawn. */
    async start() {
        if (this.started)
            return;
        if (this.startPromise)
            return this.startPromise;
        this.startPromise = this.doStart();
        try {
            await this.startPromise;
        }
        finally {
            this.startPromise = null;
        }
    }
    async doStart() {
        this.client = await AgentRelayClient.spawn({
            ...this.spawnOpts,
            onStderr: (line) => {
                for (const listener of this.stderrListeners) {
                    try {
                        listener(line);
                    }
                    catch {
                        /* ignore */
                    }
                }
            },
        });
        // Wire any event listeners that were registered before start()
        for (const listener of this.pendingEventListeners) {
            this.client.onEvent(listener);
        }
        this.pendingEventListeners.length = 0;
        this.started = true;
    }
    /** Shut down the broker and all spawned agents. */
    async shutdown() {
        await this.ensureClient().shutdown();
        this.client = null;
        this.started = false;
    }
    // ── Spawn / Release ─────────────────────────────────────────────
    /** Spawn an agent via the broker's PTY runtime. */
    async spawn(req) {
        await this.start();
        const client = this.ensureClient();
        try {
            const input = {
                name: req.name,
                cli: req.cli,
                task: buildSpawnTask(req.task, req.includeWorkflowConventions),
                channels: ['general'],
                model: req.model,
                cwd: req.cwd,
                team: req.team,
                shadowOf: req.shadowOf,
                shadowMode: req.shadowMode,
            };
            const result = await client.spawnPty(input);
            let pid;
            try {
                const agents = await client.listAgents();
                pid = agents.find((a) => a.name === req.name)?.pid;
            }
            catch {
                // Non-fatal
            }
            return { success: true, name: result.name, pid };
        }
        catch (err) {
            return { success: false, name: req.name, error: err?.message ?? String(err) };
        }
    }
    /** Release (stop) a spawned agent. */
    async release(name, reason) {
        await this.start();
        try {
            await this.ensureClient().release(name, reason);
            return { success: true, name };
        }
        catch (err) {
            return { success: false, name, error: err?.message ?? String(err) };
        }
    }
    // ── Query ───────────────────────────────────────────────────────
    /** List all agents managed by this broker instance. */
    async listAgents() {
        await this.start();
        const agents = await this.ensureClient().listAgents();
        return agents.map((a) => ({
            name: a.name,
            cli: undefined,
            pid: a.pid,
            channels: a.channels,
            parent: a.parent,
            runtime: a.runtime,
        }));
    }
    /** Check if a specific agent is spawned. */
    async hasAgent(name) {
        const agents = await this.listAgents();
        return agents.some((a) => a.name === name);
    }
    /** Get broker status (agent count, pending deliveries). */
    async getStatus() {
        await this.start();
        return this.ensureClient().getStatus();
    }
    // ── Messaging ───────────────────────────────────────────────────
    /** Send a message to an agent or channel. */
    async sendMessage(input) {
        await this.start();
        return this.ensureClient().sendMessage(input);
    }
    // ── PTY Input ───────────────────────────────────────────────────
    async sendInput(name, data) {
        await this.start();
        await this.ensureClient().sendInput(name, data);
    }
    async interruptAgent(name) {
        try {
            await this.sendInput(name, '\x1b\x1b');
            return true;
        }
        catch {
            return false;
        }
    }
    // ── Model ───────────────────────────────────────────────────────
    async setModel(name, model, opts) {
        await this.start();
        return this.ensureClient().setModel(name, model, opts);
    }
    // ── Metrics ─────────────────────────────────────────────────────
    async getMetrics(agent) {
        await this.start();
        return this.ensureClient().getMetrics(agent);
    }
    async getCrashInsights() {
        await this.start();
        return this.ensureClient().getCrashInsights();
    }
    // ── Events ──────────────────────────────────────────────────────
    onEvent(listener) {
        if (this.client) {
            return this.client.onEvent(listener);
        }
        // Queue listener — will be wired after start()
        this.pendingEventListeners.push(listener);
        return () => {
            const idx = this.pendingEventListeners.indexOf(listener);
            if (idx >= 0)
                this.pendingEventListeners.splice(idx, 1);
        };
    }
    onStderr(listener) {
        this.stderrListeners.add(listener);
        return () => {
            this.stderrListeners.delete(listener);
        };
    }
    // ── Underlying client (escape hatch) ────────────────────────────
    get raw() {
        return this.ensureClient();
    }
}
//# sourceMappingURL=relay-adapter.js.map