import { readFile } from "node:fs/promises";
import path from "node:path";
import { LAB_REPRESENTATIVE_AGENT } from "../../agents/identity.js";
import { compactModelRoute, currentModelFromRun, resolveModelRoute } from "../model-routing/index.js";
import { promptOpenCodeSessionWithModelRoute } from "../model-call/index.js";
import { appendEvent } from "../observability/index.js";
import { getRun, runDir } from "../run-state/index.js";
import { appendTeamConversationEntry, loadTeamRuntime, updateTeamMemberStatus } from "../team-mode/index.js";
import { sessionToolsForTeamMember } from "../../tools/executors/delegation.js";
import { appendDeliveryAttempt, listAgentMessages, patchAgentMessage } from "./store.js";
import { getAgentThread } from "../agent-thread/index.js";
import { planContextInjection, renderContextAttachments } from "../context-ledger/index.js";
export async function deliverQueuedAgentMessages(store, runId, ctx, input = {}) {
    const now = input.now ?? new Date().toISOString();
    const messages = (await listAgentMessages(store, runId))
        .filter((message) => message.status === "queued")
        .filter((message) => !input.messageIds?.length || input.messageIds.includes(message.message_id))
        .sort((left, right) => left.created_at.localeCompare(right.created_at));
    const result = {
        delivered: 0,
        mailbox_only: 0,
        failed: 0,
        skipped: 0,
        messages: [],
    };
    const max = Math.max(1, input.maxDeliveries ?? 3);
    for (const message of messages) {
        if (result.delivered + result.mailbox_only + result.failed >= max) {
            result.skipped += 1;
            result.messages.push(outcome(message, "skipped"));
            continue;
        }
        const delivered = await deliverOne(store, runId, ctx, message, {
            now,
            parentSessionId: input.parentSessionId,
        });
        if (delivered.mode === "live_prompt_async")
            result.delivered += 1;
        else if (delivered.mode === "mailbox_only")
            result.mailbox_only += 1;
        else if (delivered.mode === "failed")
            result.failed += 1;
        else
            result.skipped += 1;
        result.messages.push(delivered);
    }
    return result;
}
async function deliverOne(store, runId, ctx, message, input) {
    if (message.next_delivery_after && message.next_delivery_after > input.now) {
        await appendDeliveryAttempt(store, runId, {
            message_id: message.message_id,
            attempt: message.delivery_attempts,
            status: "queued",
            metadata: { reason: "retry_delay_not_elapsed", next_delivery_after: message.next_delivery_after },
        });
        return outcome(message, "skipped");
    }
    const runtime = await loadTeamRuntime(store, runId);
    const member = runtime?.members.find((candidate) => candidate.name === message.to);
    const thread = await getAgentThread(store, runId, message.to);
    const sessionId = message.target_session_id ?? thread?.opencode_session_id ?? member?.session_id;
    const terminal = thread?.status === "completed" || thread?.status === "failed" || thread?.status === "cancelled";
    if (terminal) {
        const reason = `recipient thread is ${thread.status}`;
        await patchAgentMessage(store, runId, message.message_id, {
            status: "failed",
            error: reason,
            completed_at: input.now,
        });
        await logDelivery(store, runId, message, "failed", {
            status: "failed",
            target_session_id: sessionId,
            error: reason,
        });
        return outcome(message, "failed", { session_id: sessionId, error: reason });
    }
    if (!sessionId) {
        await patchAgentMessage(store, runId, message.message_id, {
            status: "mailbox_only",
            error: "recipient has no OpenCode session id",
        });
        await logDelivery(store, runId, message, "mailbox_only", {
            status: "mailbox_only",
            error: "missing_recipient_session",
        });
        return outcome(message, "mailbox_only", { error: "recipient has no OpenCode session id" });
    }
    const status = thread?.status ?? member?.status;
    const busy = status === "active" || status === "launching" || status === "running";
    if (busy && message.priority !== "urgent") {
        await patchAgentMessage(store, runId, message.message_id, {
            status: "mailbox_only",
            target_session_id: sessionId,
        });
        await logDelivery(store, runId, message, "mailbox_only", {
            status: "mailbox_only",
            target_session_id: sessionId,
            metadata: { recipient_status: status },
        });
        return outcome(message, "mailbox_only", { session_id: sessionId });
    }
    if (!member) {
        const reason = "recipient team member not found";
        await patchAgentMessage(store, runId, message.message_id, {
            status: "failed",
            target_session_id: sessionId,
            error: reason,
            completed_at: input.now,
        });
        await logDelivery(store, runId, message, "failed", {
            status: "failed",
            target_session_id: sessionId,
            error: reason,
        });
        return outcome(message, "failed", { session_id: sessionId, error: reason });
    }
    const sessionTools = sessionToolsForTeamMember(member);
    const openCodeAgent = member.resolved_agent ?? member.opencode_agent ?? LAB_REPRESENTATIVE_AGENT;
    const run = await getRun(store, runId);
    const modelRoute = resolveModelRoute(store.config, openCodeAgent, run ? currentModelFromRun(run) : undefined);
    const promptBody = await renderDeliveryPrompt(store, runId, message);
    try {
        await patchAgentMessage(store, runId, message.message_id, {
            status: "delivering",
            target_session_id: sessionId,
        });
        await promptOpenCodeSessionWithModelRoute(ctx, sessionId, {
            agent: openCodeAgent,
            tools: sessionTools,
            parts: [{
                    type: "text",
                    text: promptBody,
                    metadata: {
                        seed_mode: "agent_thread_message",
                        seed_message_id: message.message_id,
                        seed_from_thread: message.from,
                        seed_to_thread: message.to,
                        seed_context_refs: message.context_refs,
                    },
                }],
        }, ctx.directory, true, modelRoute);
        await patchAgentMessage(store, runId, message.message_id, {
            status: "delivered",
            target_session_id: sessionId,
            delivered_at: input.now,
            delivery_attempts: message.delivery_attempts + 1,
        });
        await updateTeamMemberStatus(store, runId, member.name, "active");
        await logDelivery(store, runId, message, "live_prompt_async", {
            status: "delivered",
            target_session_id: sessionId,
            metadata: {
                opencode_agent: openCodeAgent,
                model_route: compactModelRoute(modelRoute),
                session_tools: sessionTools,
                allowed_tools: member.tool_policy?.allowed_tools ?? [],
                denied_tools: member.tool_policy?.denied_tools ?? [],
            },
        });
        await appendEvent(store, runId, {
            type: "agent_message_delivered",
            actor: message.from,
            session_id: input.parentSessionId,
            summary: `${message.from} -> ${message.to}: ${message.summary.slice(0, 140)}`,
            metadata: {
                message_id: message.message_id,
                from: message.from,
                to: message.to,
                target_session_id: sessionId,
                prompt_ref: message.prompt_ref,
                tool_manifest_ref: message.tool_manifest_ref,
                context_refs: message.context_refs,
                context_attachments: message.context_attachments,
                delivery_mode: "live_prompt_async",
                session_tools: sessionTools,
            },
        });
        return outcome(message, "live_prompt_async", { session_id: sessionId });
    }
    catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        const attempts = message.delivery_attempts + 1;
        const terminal = attempts >= store.config.message_bus.max_delivery_attempts;
        const nextDelivery = new Date(Date.parse(input.now) + store.config.message_bus.retry_delay_ms).toISOString();
        await patchAgentMessage(store, runId, message.message_id, {
            status: terminal ? "failed" : "queued",
            target_session_id: sessionId,
            delivery_attempts: attempts,
            next_delivery_after: terminal ? undefined : nextDelivery,
            error: errorMessage,
            completed_at: terminal ? input.now : undefined,
        });
        await logDelivery(store, runId, message, "failed", {
            status: terminal ? "failed" : "queued",
            target_session_id: sessionId,
            error: errorMessage,
            metadata: { attempt: attempts, next_delivery_after: terminal ? undefined : nextDelivery },
        });
        return outcome(message, "failed", { session_id: sessionId, error: errorMessage });
    }
}
async function renderDeliveryPrompt(store, runId, message) {
    const body = message.body_ref ? await readBodyRef(store, runId, message.body_ref) : "";
    const contextPlan = await contextPlanForMessage(store, runId, message);
    const contextBlock = renderContextAttachments(contextPlan);
    return `[SEED agent-thread message]
Message: ${message.message_id}
From: ${message.from}
To: ${message.to}
Kind: ${message.kind}
Correlation: ${message.correlation_id}

Read your SEED inbox/message context, act only within your agent-thread contract, acknowledge consumed messages, and report result/blocker with evidence.

Prompt/body reference: ${message.body_ref ?? message.prompt_ref ?? "(none)"}
Context refs: ${message.context_refs.length ? message.context_refs.join(", ") : "none"}

${contextBlock}

${body}`.trim();
}
async function contextPlanForMessage(store, runId, message) {
    if (message.context_attachments.length > 0) {
        return {
            turn_kind: "message_delivery",
            run_id: runId,
            actor: message.from,
            target_agent: message.to,
            target_thread_id: message.to,
            attachments: message.context_attachments,
            inline_text: "",
            injected_chars: message.context_attachments.reduce((total, item) => total + item.char_count, 0),
            warnings: [],
        };
    }
    return planContextInjection(store, {
        turn_kind: "message_delivery",
        run_id: runId,
        actor: message.from,
        target_agent: message.to,
        target_thread_id: message.to,
        bundle_ids: message.context_refs,
    });
}
async function readBodyRef(store, runId, bodyRef) {
    try {
        return await readFile(path.join(runDir(store, runId), bodyRef), "utf8");
    }
    catch {
        return "";
    }
}
async function logDelivery(store, runId, message, mode, input) {
    await appendDeliveryAttempt(store, runId, {
        message_id: message.message_id,
        attempt: message.delivery_attempts + 1,
        status: input.status === "live_prompt_async" ? "delivered" : input.status,
        target_session_id: input.target_session_id,
        error: input.error,
        metadata: {
            from: message.from,
            to: message.to,
            prompt_ref: message.prompt_ref,
            tool_manifest_ref: message.tool_manifest_ref,
            context_refs: message.context_refs,
            context_attachments: message.context_attachments,
            delivery_mode: mode,
            ...(input.metadata ?? {}),
        },
    });
    await appendTeamConversationEntry(store, runId, {
        type: "message_delivery",
        from: message.from,
        to: message.to,
        message_id: message.message_id,
        child_session_id: input.target_session_id,
        prompt_ref: message.prompt_ref,
        status: mode,
        metadata: {
            prompt_ref: message.prompt_ref,
            tool_manifest_ref: message.tool_manifest_ref,
            context_refs: message.context_refs,
            context_attachments: message.context_attachments,
            delivery_mode: mode,
            error: input.error,
            ...(input.metadata ?? {}),
        },
    });
}
function outcome(message, mode, patch = {}) {
    return {
        message_id: message.message_id,
        from: message.from,
        to: message.to,
        mode,
        prompt_ref: message.prompt_ref,
        tool_manifest_ref: message.tool_manifest_ref,
        ...patch,
    };
}
//# sourceMappingURL=delivery.js.map