import { Buffer } from "node:buffer";
import path from "node:path";
import { appendJsonl, createId, ensureDir, nowIso, readJsonFile, sanitizeText, withFileLock, writeJsonFile, writeTextFile } from "../../shared/index.js";
import { runDir } from "../run-state/index.js";
export function messageBusRoot(store, runId) {
    return path.join(runDir(store, runId), "messages");
}
export function messageBodiesRoot(store, runId) {
    return path.join(messageBusRoot(store, runId), "bodies");
}
export function messageEnvelopesPath(store, runId) {
    return path.join(messageBusRoot(store, runId), "envelopes.json");
}
export function messageDeliveryPath(store, runId) {
    return path.join(messageBusRoot(store, runId), "delivery.jsonl");
}
export async function enqueueAgentMessage(store, runId, input) {
    if (!store.config.message_bus.enabled) {
        throw new Error("SEED message bus is disabled by message_bus.enabled=false");
    }
    const body = sanitizeText(input.body);
    if (Buffer.byteLength(body, "utf8") > store.config.message_bus.envelope_body_max_bytes) {
        throw new Error("agent message envelope body exceeds configured limit");
    }
    const messageId = input.message_id ?? createId("team_msg");
    const timestamp = nowIso();
    const bodyRef = `messages/bodies/${messageId}.md`;
    const contextRefs = stringList(input.context_refs);
    const contextAttachments = normalizeContextAttachments(input.context_attachments);
    if (contextAttachments.length > store.config.context_policy.max_context_attachments_per_message) {
        throw new Error("agent message envelope context attachments exceed configured limit");
    }
    const envelope = {
        version: 1,
        message_id: messageId,
        run_id: runId,
        from: sanitizeText(input.from).trim(),
        to: sanitizeText(input.to).trim(),
        kind: input.kind ?? "message",
        priority: input.priority ?? "normal",
        summary: sanitizeText(input.summary ?? body.slice(0, 200)).trim(),
        body_ref: bodyRef,
        prompt_ref: optionalText(input.prompt_ref),
        tool_manifest_ref: optionalText(input.tool_manifest_ref),
        context_refs: contextRefs,
        context_attachments: contextAttachments,
        parent_message_id: optionalText(input.parent_message_id),
        correlation_id: sanitizeText(input.correlation_id ?? messageId).trim(),
        causation_id: optionalText(input.causation_id),
        allowed_tools: stringList(input.allowed_tools),
        denied_tools: stringList(input.denied_tools),
        allowed_skills: stringList(input.allowed_skills),
        denied_skills: stringList(input.denied_skills),
        allowed_mcps: stringList(input.allowed_mcps),
        denied_mcps: stringList(input.denied_mcps),
        target_session_id: optionalText(input.target_session_id),
        background_id: optionalText(input.background_id),
        status: "queued",
        delivery_attempts: 0,
        created_at: timestamp,
        updated_at: timestamp,
        metadata: input.metadata ?? {},
    };
    if (!envelope.from)
        throw new Error("agent message envelope requires a sender");
    if (!envelope.to)
        throw new Error("agent message envelope requires a recipient");
    if (!envelope.summary)
        throw new Error("agent message envelope requires a summary");
    await ensureDir(messageBodiesRoot(store, runId));
    await writeTextFile(path.join(runDir(store, runId), bodyRef), `${body}\n`);
    const file = messageEnvelopesPath(store, runId);
    return withFileLock(file, async () => {
        const envelopes = (await readJsonFile(file)) ?? [];
        const queued = envelopes.filter((candidate) => candidate.status === "queued" || candidate.status === "delivering");
        if (queued.length >= store.config.message_bus.max_queued_messages) {
            throw new Error("agent message envelope queue is full");
        }
        if (envelopes.some((candidate) => candidate.message_id === envelope.message_id)) {
            throw new Error(`agent message envelope already exists: ${envelope.message_id}`);
        }
        await writeJsonFile(file, [...envelopes, envelope]);
        return envelope;
    });
}
export async function listAgentMessages(store, runId, filter = {}) {
    const messages = (await readJsonFile(messageEnvelopesPath(store, runId))) ?? [];
    return messages
        .filter((message) => !filter.status || message.status === filter.status)
        .filter((message) => !filter.to || message.to === filter.to)
        .filter((message) => !filter.from || message.from === filter.from)
        .sort((left, right) => left.created_at.localeCompare(right.created_at));
}
export async function getAgentMessage(store, runId, messageId) {
    return (await listAgentMessages(store, runId)).find((message) => message.message_id === messageId);
}
export async function queueStats(store, runId) {
    const messages = await listAgentMessages(store, runId);
    const stats = {
        created: 0,
        queued: 0,
        delivering: 0,
        delivered: 0,
        mailbox_only: 0,
        acknowledged: 0,
        acted_on: 0,
        failed: 0,
        expired: 0,
    };
    for (const message of messages)
        stats[message.status] += 1;
    return stats;
}
export async function patchAgentMessage(store, runId, messageId, patch) {
    const file = messageEnvelopesPath(store, runId);
    const timestamp = nowIso();
    return withFileLock(file, async () => {
        const messages = (await readJsonFile(file)) ?? [];
        const index = messages.findIndex((message) => message.message_id === messageId);
        if (index < 0)
            return undefined;
        const current = messages[index];
        const next = {
            ...current,
            ...withoutUndefined(patch),
            updated_at: timestamp,
        };
        messages[index] = next;
        await writeJsonFile(file, messages);
        return next;
    });
}
export async function appendDeliveryAttempt(store, runId, input) {
    await appendJsonl(messageDeliveryPath(store, runId), {
        version: 1,
        run_id: runId,
        at: input.at ?? nowIso(),
        ...input,
    });
}
function optionalText(value) {
    const text = sanitizeText(typeof value === "string" ? value : "").trim();
    return text || undefined;
}
function stringList(value) {
    if (!Array.isArray(value))
        return [];
    return value.map((item) => optionalText(item)).filter((item) => Boolean(item));
}
function normalizeContextAttachments(value) {
    if (!Array.isArray(value))
        return [];
    return value
        .filter((item) => typeof item === "object" && item !== null)
        .map((item) => ({
        bundle_id: optionalText(item.bundle_id) ?? "",
        category: item.category === "stable" || item.category === "warm" || item.category === "hot" || item.category === "heavy" || item.category === "memory"
            ? item.category
            : "warm",
        scope: item.scope === "run" || item.scope === "agent_thread" || item.scope === "message" || item.scope === "subplan" || item.scope === "artifact" || item.scope === "meeting"
            ? item.scope
            : "message",
        title: optionalText(item.title) ?? "context attachment",
        render_mode: item.render_mode === "inline" || item.render_mode === "digest" || item.render_mode === "ref"
            ? item.render_mode
            : "ref",
        digest: optionalText(item.digest) ?? "",
        inline_text: optionalText(item.inline_text),
        body_ref: optionalText(item.body_ref) ?? "",
        body_sha256: optionalText(item.body_sha256) ?? "",
        byte_length: typeof item.byte_length === "number" ? item.byte_length : 0,
        char_count: typeof item.char_count === "number" ? item.char_count : 0,
        reason: optionalText(item.reason),
    }))
        .filter((item) => Boolean(item.bundle_id));
}
function withoutUndefined(value) {
    return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
}
//# sourceMappingURL=store.js.map