import path from "node:path";
import { appendEvent, readTraceSummary } from "../observability/index.js";
import { listBackgroundNotifications, listBackgroundTasks } from "../background-state/index.js";
import { listAgentThreads } from "../agent-thread/index.js";
import { listAgentMessages } from "../message-bus/index.js";
import { getRun, listTasks, runDir } from "../run-state/index.js";
import { listSubplans, requiredSubplansBlockingFinal } from "../subplan/index.js";
import { compactTeamMeetingSummary, listTeamInbox, listTeamMeetings, listTeamTasks, loadTeamRuntime, unresolvedTeamTaskConflicts } from "../team-mode/index.js";
import { evaluateRunBudget, refreshRunBudgetEvaluation } from "../run-budget/index.js";
import { listProfessorMemory } from "../professor-memory/index.js";
import { nowIso, writeJsonFile, writeTextFile } from "../../shared/index.js";
import { LAB_REPRESENTATIVE_AGENT } from "../../agents/identity.js";
export function continuationRoot(store, runId) {
    return path.join(runDir(store, runId), "continuation");
}
export function continuationSnapshotPath(store, runId) {
    return path.join(continuationRoot(store, runId), "context.json");
}
export function continuationMarkdownPath(store, runId) {
    return path.join(continuationRoot(store, runId), "context.md");
}
export async function buildContinuationSnapshot(store, runId, input) {
    const run = await getRun(store, runId);
    if (!run)
        throw new Error(`SEED run not found: ${runId}`);
    const [tasks, backgrounds, backgroundNotifications, trace, teamRuntime, subplans, blockingSubplans, budgetState, professorMemory, agentThreads, agentMessages] = await Promise.all([
        listTasks(store, runId),
        listBackgroundTasks(store, runId),
        listBackgroundNotifications(store, runId, { status: "pending" }),
        readTraceSummary(store, runId),
        loadTeamRuntime(store, runId),
        listSubplans(store, runId),
        requiredSubplansBlockingFinal(store, runId),
        refreshRunBudgetEvaluation(store, runId),
        listProfessorMemory(store, { reusableOnly: true }),
        listAgentThreads(store, runId),
        listAgentMessages(store, runId),
    ]);
    const budgetEvaluation = await evaluateRunBudget(store, budgetState);
    const teamTasks = teamRuntime ? await listTeamTasks(store, runId) : [];
    const activeTeamMeetings = teamRuntime ? await listTeamMeetings(store, runId, { status: "open" }) : [];
    const unreadEntries = teamRuntime
        ? await Promise.all(teamRuntime.members.map(async (member) => [
            member.name,
            (await listTeamInbox(store, runId, member.name, { includeAcknowledged: false })).length,
        ]))
        : [];
    const unreadByMember = Object.fromEntries(unreadEntries);
    const openStatuses = new Set(["pending", "in_progress", "blocked", "review"]);
    const openTeamStatuses = new Set(["pending", "claimed", "in_progress", "blocked", "review"]);
    const openTasks = tasks.filter((task) => openStatuses.has(task.status)).sort(compareTaskPriority);
    const blockedTasks = tasks.filter((task) => task.status === "blocked");
    const reviewTasks = tasks.filter((task) => task.status === "review");
    const openTeamTasks = teamTasks.filter((task) => openTeamStatuses.has(task.status)).sort(compareTeamTaskPriority);
    const blockedTeamTasks = teamTasks.filter((task) => task.status === "blocked");
    const teamConflicts = unresolvedTeamTaskConflicts(teamTasks);
    const activeBackgrounds = backgrounds.filter((task) => task.status === "running");
    const staleBackgrounds = activeBackgrounds.filter((task) => task.stale_at);
    const terminalUnnotified = backgrounds.filter((task) => isTerminalBackground(task) && !task.notified_at);
    const recentTerminal = backgrounds.filter(isTerminalBackground).slice(-5);
    const decisions = pendingDecisions({
        blockedTasks,
        blockedTeamTasks,
        failedBackgrounds: backgrounds.filter((task) => task.status === "failed"),
        handoffEvents: trace.events.filter((event) => event.type === "professor_handoff_requested" || event.type === "professor_handoff_required"),
    });
    return {
        version: 1,
        run_id: runId,
        generated_at: nowIso(),
        reason: input.reason,
        session_id: input.sessionId,
        run: {
            run_id: run.run_id,
            status: run.status,
            run_mode: run.run_mode,
            professor_goal: run.professor_goal,
            active_agent: run.active_agent,
            root_session_id: run.root_session_id,
            final_artifacts: run.final_artifacts,
        },
        tasks: {
            open: openTasks,
            blocked: blockedTasks,
            review: reviewTasks,
            completed_count: tasks.filter((task) => task.status === "completed").length,
        },
        team: {
            runtime: teamRuntime,
            unread_by_member: unreadByMember,
            active_meetings: activeTeamMeetings,
            open_tasks: openTeamTasks,
            blocked_tasks: blockedTeamTasks,
            conflicts: teamConflicts,
        },
        subplans: {
            open: subplans.filter((subplan) => subplan.status !== "accepted" && subplan.status !== "cancelled"),
            blocking_final: blockingSubplans,
            accepted_count: subplans.filter((subplan) => subplan.status === "accepted").length,
            rework_count: subplans.filter((subplan) => subplan.status === "rework").length,
        },
        agent_threads: {
            active: agentThreads.filter((thread) => thread.status === "active" || thread.status === "idle" || thread.status === "waiting_on_mailbox"),
            blocked: agentThreads.filter((thread) => thread.status === "blocked" || thread.status === "failed"),
            helper_counts_by_thread: Object.fromEntries(agentThreads.map((thread) => [thread.thread_id, thread.helper_counts])),
        },
        message_bus: {
            queued_count: agentMessages.filter((message) => message.status === "queued" || message.status === "delivering").length,
            failed_count: agentMessages.filter((message) => message.status === "failed").length,
            blocked_deliveries: agentMessages.filter((message) => message.status === "failed" || message.status === "mailbox_only").slice(-10),
        },
        backgrounds: {
            active: activeBackgrounds,
            stale: staleBackgrounds,
            notifications: backgroundNotifications,
            terminal_unnotified: terminalUnnotified,
            recent_terminal: recentTerminal,
        },
        budget: {
            status: budgetEvaluation.status,
            warnings: budgetEvaluation.warnings,
            blocking_reason: budgetEvaluation.blocking_reason,
            model_calls: `${budgetState.usage.model_calls}/${budgetState.limits.max_model_calls}`,
            child_sessions: `${budgetState.usage.child_sessions}/${budgetState.limits.max_child_sessions}`,
            context_injected_chars: budgetState.usage.context.injected_chars_total,
            context_duplicate_bundle_refs: Object.entries(budgetState.usage.context.duplicate_bundle_injections).filter(([, count]) => count > 1).length,
        },
        professor_memory_refs: professorMemory.map((item) => `.seed-agent/memory/items/${item.memory_id}.json`).slice(-12),
        pending_decisions: decisions,
        next_actions: nextActions({
            decisions,
            openTasks,
            blockedTasks,
            reviewTasks,
            unreadByMember,
            openTeamTasks,
            blockedTeamTasks,
            activeBackgrounds,
            staleBackgrounds,
            backgroundNotifications,
            terminalUnnotified,
            budgetStatus: budgetEvaluation.status,
            budgetWarnings: budgetEvaluation.warnings,
            budgetBlockingReason: budgetEvaluation.blocking_reason,
        }),
        evidence_refs: unique([
            ...tasks.flatMap((task) => task.evidence_refs),
            ...teamTasks.flatMap((task) => task.evidence_refs),
            ...run.final_artifacts,
        ]).slice(-20),
        recent_events: trace.events.slice(-store.config.continuation.recent_event_limit),
    };
}
export async function writeContinuationSnapshot(store, runId, input) {
    const snapshot = await buildContinuationSnapshot(store, runId, input);
    const markdown = renderContinuationMarkdown(snapshot, store.config.continuation.context_max_chars);
    const json_path = continuationSnapshotPath(store, runId);
    const markdown_path = continuationMarkdownPath(store, runId);
    await writeJsonFile(json_path, snapshot);
    await writeTextFile(markdown_path, `${markdown}\n`);
    if (input.recordEvent !== false) {
        await appendEvent(store, runId, {
            type: "continuation_snapshot_written",
            actor: store.config.default_run_agent ?? LAB_REPRESENTATIVE_AGENT,
            session_id: input.sessionId,
            summary: `Continuation snapshot refreshed for ${input.reason}`,
            metadata: { reason: input.reason, markdown_path, json_path },
        });
    }
    return { snapshot, markdown, markdown_path, json_path };
}
export function renderContinuationContext(snapshot, maxChars) {
    return `<system-reminder>\n${renderContinuationMarkdown(snapshot, maxChars)}\n</system-reminder>`;
}
export function renderContinuationMarkdown(snapshot, maxChars) {
    const lines = [
        "# SEED continuation context",
        "",
        `Run: ${snapshot.run.run_id} (${snapshot.run.status})`,
        `Run mode: ${snapshot.run.run_mode}`,
        `Goal: ${snapshot.run.professor_goal}`,
        `Active agent: ${snapshot.run.active_agent}`,
        `Reason: ${snapshot.reason}`,
        "",
        "## Open SEED tasks",
        ...taskLines(snapshot.tasks.open),
        "",
        "## Next actions",
        ...nextActionLines(snapshot.next_actions),
        "",
        "## Blockers and review",
        ...blockerLines(snapshot),
        "",
        "## Team runtime",
        ...teamLines(snapshot),
        "",
        "## Sub-plans",
        ...subplanLines(snapshot),
        "",
        "## Agent threads and messages",
        ...agentThreadLines(snapshot),
        "",
        "## Budget and memory",
        ...budgetAndMemoryLines(snapshot),
        "",
        "## Background work",
        ...backgroundLines(snapshot),
        "",
        "## Pending decisions",
        ...decisionLines(snapshot),
        "",
        "## Evidence refs",
        ...(snapshot.evidence_refs.length ? snapshot.evidence_refs.map((ref) => `- ${ref}`) : ["- none"]),
        "",
        "## Recent events",
        ...eventLines(snapshot),
        "",
        "Continuation rule: continue from this state, preserve existing task/background/team IDs, and do not restart completed or delegated work unless the user explicitly asks.",
    ];
    return truncate(lines.join("\n"), maxChars);
}
function agentThreadLines(snapshot) {
    const active = snapshot.agent_threads.active.slice(0, 12).map((thread) => {
        const helpers = snapshot.agent_threads.helper_counts_by_thread[thread.thread_id];
        const helperText = helpers ? ` helpers:${helpers.running}/${helpers.completed}/${helpers.failed}` : "";
        return `- thread ${thread.thread_id} ${thread.status} role:${thread.role_label ?? thread.role}${helperText} session:${thread.opencode_session_id ?? "none"}`;
    });
    const blocked = snapshot.agent_threads.blocked.slice(0, 8).map((thread) => `- blocked thread ${thread.thread_id} ${thread.status}`);
    const blockedDeliveries = snapshot.message_bus.blocked_deliveries.slice(0, 8).map((message) => (`- message ${message.message_id} ${message.status} ${message.from}->${message.to}: ${message.summary}${message.error ? ` error:${message.error}` : ""}`));
    return [
        `- message queue: queued=${snapshot.message_bus.queued_count} failed=${snapshot.message_bus.failed_count}`,
        ...(active.length ? active : ["- active threads: none"]),
        ...(blocked.length ? blocked : ["- blocked threads: none"]),
        ...(blockedDeliveries.length ? blockedDeliveries : ["- blocked deliveries: none"]),
    ];
}
function subplanLines(snapshot) {
    const summary = `- accepted:${snapshot.subplans.accepted_count} rework:${snapshot.subplans.rework_count} blocking_final:${snapshot.subplans.blocking_final.length}`;
    if (!snapshot.subplans.open.length)
        return [summary, "- open: none"];
    return [
        summary,
        ...snapshot.subplans.open.slice(0, 12).map((subplan) => {
            const owner = subplan.owner_member ? ` owner:${subplan.owner_member}` : "";
            const verifier = subplan.verifier_member ? ` verifier:${subplan.verifier_member}` : "";
            const required = subplan.required_for_final ? " required" : "";
            return `- ${subplan.subplan_id} ${subplan.status}${required}${owner}${verifier}: ${subplan.title}`;
        }),
    ];
}
function budgetAndMemoryLines(snapshot) {
    return [
        `- budget: ${snapshot.budget.status} model_calls:${snapshot.budget.model_calls} child_sessions:${snapshot.budget.child_sessions}`,
        `- context: injected_chars:${snapshot.budget.context_injected_chars} duplicate_refs:${snapshot.budget.context_duplicate_bundle_refs}`,
        ...(snapshot.budget.blocking_reason ? [`- budget blocker: ${snapshot.budget.blocking_reason}`] : []),
        ...(snapshot.budget.warnings.length ? snapshot.budget.warnings.map((warning) => `- budget warning: ${warning}`) : ["- budget warning: none"]),
        ...(snapshot.professor_memory_refs.length ? snapshot.professor_memory_refs.map((ref) => `- professor memory: ${ref}`) : ["- professor memory: none"]),
    ];
}
function taskLines(tasks) {
    if (!tasks.length)
        return ["- none"];
    return tasks.slice(0, 12).map((task) => {
        const blocker = task.blocker ? ` blocker:${task.blocker}` : "";
        const evidence = task.evidence_refs.length ? ` evidence:${task.evidence_refs.join(",")}` : "";
        return `- ${task.task_id} ${task.status} p${task.priority} ${task.owner_agent}: ${task.title}${blocker}${evidence}`;
    });
}
function nextActionLines(actions) {
    if (!actions.length)
        return ["- none"];
    return actions.slice(0, 12).map((action) => {
        const related = action.related_id ? ` ${action.related_id}` : "";
        const reason = action.reason ? ` (${action.reason})` : "";
        return `- p${action.priority} ${action.category}${related}: ${action.summary}${reason}`;
    });
}
function blockerLines(snapshot) {
    const lines = [];
    for (const task of snapshot.tasks.blocked.slice(0, 8)) {
        lines.push(`- blocked ${task.task_id}: ${task.title}${task.blocker ? ` (${task.blocker})` : ""}`);
    }
    for (const task of snapshot.tasks.review.slice(0, 8)) {
        lines.push(`- review ${task.task_id}: ${task.title}`);
    }
    for (const task of snapshot.team.blocked_tasks.slice(0, 8)) {
        lines.push(`- team blocked ${task.team_task_id}: ${task.title}${task.blocker ? ` (${task.blocker})` : ""}`);
    }
    return lines.length ? lines : ["- none"];
}
function teamLines(snapshot) {
    if (!snapshot.team.runtime)
        return ["- runtime: not initialized"];
    const unread = Object.entries(snapshot.team.unread_by_member)
        .filter(([, count]) => count > 0)
        .map(([member, count]) => `${member}:${count}`);
    const openTasks = snapshot.team.open_tasks.slice(0, 10).map((task) => (`- team task ${task.team_task_id} ${task.status} owner:${task.owner_agent ?? "unclaimed"} p${task.priority}: ${task.title}`));
    const conflicts = snapshot.team.conflicts.slice(0, 8).map((conflict) => (`- conflict ${conflict.conflict_id} ${conflict.kind} actor:${conflict.actor} expected:${conflict.expected_revision} actual:${conflict.actual_revision}`));
    return [
        `- status: ${snapshot.team.runtime.status}`,
        `- members: ${snapshot.team.runtime.members.map((member) => `${member.name}:${member.status}`).join(", ") || "none"}`,
        `- unread: ${unread.join(", ") || "none"}`,
        "- active meetings:",
        ...compactTeamMeetingSummary(snapshot.team.active_meetings),
        ...(conflicts.length ? conflicts : ["- conflicts: none"]),
        ...(openTasks.length ? openTasks : ["- open team tasks: none"]),
    ];
}
function backgroundLines(snapshot) {
    const notifications = snapshot.backgrounds.notifications.slice(0, 8).map((notification) => {
        const owner = notification.target_agent ?? "background";
        return `notification ${notification.notification_id} ${notification.status} ${notification.background_status} ${notification.background_id} ${owner}: ${notification.title}`;
    });
    const active = snapshot.backgrounds.active.slice(0, 8).map(formatBackground);
    const stale = snapshot.backgrounds.stale.slice(0, 8).map((task) => `${formatBackground(task)} stale:${task.stale_reason ?? task.stale_at}`);
    const terminal = snapshot.backgrounds.terminal_unnotified.slice(0, 8).map((task) => `${formatBackground(task)} unnotified`);
    const recent = snapshot.backgrounds.recent_terminal.slice(0, 5).map((task) => `${formatBackground(task)} recent`);
    const lines = [...notifications, ...stale, ...active, ...terminal, ...recent];
    return lines.length ? lines.map((line) => `- ${line}`) : ["- none"];
}
function decisionLines(snapshot) {
    if (!snapshot.pending_decisions.length)
        return ["- none"];
    return snapshot.pending_decisions.slice(0, 12).map((decision) => {
        const id = decision.related_id ? ` ${decision.related_id}` : "";
        const reason = decision.reason ? ` (${decision.reason})` : "";
        return `- ${decision.source}${id}: ${decision.summary}${reason}`;
    });
}
function eventLines(snapshot) {
    if (!snapshot.recent_events.length)
        return ["- none"];
    return snapshot.recent_events.map((event) => `- ${event.timestamp} ${event.type}: ${event.summary}`);
}
function formatBackground(task) {
    const session = task.session_id ? ` session:${task.session_id}` : "";
    const latest = task.last_message ? ` latest:${task.last_message.slice(0, 140)}` : "";
    return `${task.background_id} ${task.status} ${task.target_agent ?? task.kind}${session}${latest}`;
}
function compareTaskPriority(left, right) {
    return left.priority - right.priority || right.updated_at.localeCompare(left.updated_at);
}
function compareTeamTaskPriority(left, right) {
    return left.priority - right.priority || right.updated_at.localeCompare(left.updated_at);
}
function isTerminalBackground(task) {
    return task.status === "completed" || task.status === "failed" || task.status === "cancelled";
}
function pendingDecisions(input) {
    return [
        ...input.handoffEvents.slice(-8).map((event) => ({
            source: "professor_handoff",
            summary: event.summary,
            reason: typeof event.metadata?.reason === "string" ? event.metadata.reason : undefined,
            related_id: event.related_task_id,
            created_at: event.timestamp,
        })),
        ...input.blockedTasks.slice(0, 8).map((task) => ({
            source: "blocked_task",
            summary: task.title,
            reason: task.blocker,
            related_id: task.task_id,
            created_at: task.updated_at,
        })),
        ...input.blockedTeamTasks.slice(0, 8).map((task) => ({
            source: "blocked_team_task",
            summary: task.title,
            reason: task.blocker,
            related_id: task.team_task_id,
            created_at: task.updated_at,
        })),
        ...input.failedBackgrounds.slice(-5).map((task) => ({
            source: "failed_background",
            summary: task.title ?? task.background_id,
            reason: task.error ?? task.last_message,
            related_id: task.background_id,
            created_at: task.updated_at,
        })),
    ];
}
function nextActions(input) {
    const actions = [];
    if (input.budgetStatus === "blocked" || input.budgetStatus === "warning") {
        actions.push({
            action_id: `budget:${input.budgetStatus}`,
            priority: input.budgetStatus === "blocked" ? 1 : 2,
            category: "review_budget",
            summary: input.budgetStatus === "blocked" ? "Run budget is blocking new delegation." : "Run budget warning is active.",
            reason: input.budgetBlockingReason ?? input.budgetWarnings.join("; "),
        });
    }
    for (const task of input.staleBackgrounds.slice(0, 4)) {
        actions.push({
            action_id: `background:${task.background_id}:stale`,
            priority: 2,
            category: "checkpoint_background",
            summary: `${task.target_agent ?? task.kind} background is stale; checkpoint, follow up, cancel, or relaunch.`,
            related_id: task.background_id,
            reason: task.stale_reason ?? task.last_message,
        });
    }
    for (const decision of input.decisions.slice(0, 6)) {
        actions.push({
            action_id: `decision:${decision.source}:${decision.related_id ?? decision.summary}`,
            priority: decision.source === "professor_handoff" ? 1 : 2,
            category: "professor_decision",
            summary: decision.summary,
            related_id: decision.related_id,
            reason: decision.reason,
        });
    }
    const notifiedBackgroundIds = new Set();
    for (const notification of input.backgroundNotifications.slice(0, 4)) {
        notifiedBackgroundIds.add(notification.background_id);
        actions.push({
            action_id: `background-notification:${notification.notification_id}`,
            priority: notification.severity === "error" ? 1 : 2,
            category: "review_background",
            summary: `${notification.title}; ${notification.next_action === "review_output" ? "review output." : "retry or hand off."}`,
            related_id: notification.background_id,
            reason: notification.body,
        });
    }
    for (const task of input.terminalUnnotified.filter((task) => !notifiedBackgroundIds.has(task.background_id)).slice(0, 4)) {
        actions.push({
            action_id: `background:${task.background_id}:review`,
            priority: task.status === "failed" ? 1 : 2,
            category: "review_background",
            summary: `${task.target_agent ?? task.kind} background ${task.status}; review output and decide follow-up.`,
            related_id: task.background_id,
            reason: task.error ?? task.last_message,
        });
    }
    for (const task of input.blockedTasks.slice(0, 4)) {
        actions.push({
            action_id: `task:${task.task_id}:unblock`,
            priority: 2,
            category: "unblock_task",
            summary: task.title,
            related_id: task.task_id,
            reason: task.blocker,
        });
    }
    for (const task of input.blockedTeamTasks.slice(0, 4)) {
        actions.push({
            action_id: `team-task:${task.team_task_id}:unblock`,
            priority: 2,
            category: "unblock_task",
            summary: task.title,
            related_id: task.team_task_id,
            reason: task.blocker,
        });
    }
    for (const task of input.reviewTasks.slice(0, 4)) {
        actions.push({
            action_id: `task:${task.task_id}:review`,
            priority: 3,
            category: "review_task",
            summary: task.title,
            related_id: task.task_id,
        });
    }
    for (const [member, count] of Object.entries(input.unreadByMember).filter(([, count]) => count > 0).slice(0, 4)) {
        actions.push({
            action_id: `team-inbox:${member}`,
            priority: 3,
            category: "read_team_inbox",
            summary: `Read ${count} unread team message(s) for ${member}.`,
            related_id: member,
        });
    }
    for (const task of input.activeBackgrounds.slice(0, 4)) {
        actions.push({
            action_id: `background:${task.background_id}:checkpoint`,
            priority: 4,
            category: "checkpoint_background",
            summary: `${task.target_agent ?? task.kind} is still running; checkpoint before assuming progress.`,
            related_id: task.background_id,
            reason: task.last_message,
        });
    }
    for (const task of input.openTasks.filter((task) => task.status !== "blocked" && task.status !== "review").slice(0, 6)) {
        actions.push({
            action_id: `task:${task.task_id}:continue`,
            priority: 5,
            category: "continue_task",
            summary: task.title,
            related_id: task.task_id,
        });
    }
    for (const task of input.openTeamTasks.filter((task) => task.status !== "blocked").slice(0, 6)) {
        actions.push({
            action_id: `team-task:${task.team_task_id}:continue`,
            priority: task.owner_agent ? 5 : 4,
            category: task.owner_agent ? "continue_team_task" : "claim_team_task",
            summary: task.title,
            related_id: task.team_task_id,
            reason: task.owner_agent ? `owner:${task.owner_agent}` : "unclaimed",
        });
    }
    return dedupeActions(actions).sort((left, right) => left.priority - right.priority || left.action_id.localeCompare(right.action_id)).slice(0, 16);
}
function dedupeActions(actions) {
    const seen = new Set();
    const uniqueActions = [];
    for (const action of actions) {
        if (seen.has(action.action_id))
            continue;
        seen.add(action.action_id);
        uniqueActions.push(action);
    }
    return uniqueActions;
}
function unique(values) {
    return [...new Set(values.filter(Boolean))];
}
function truncate(value, maxChars) {
    if (value.length <= maxChars)
        return value;
    return `${value.slice(0, Math.max(0, maxChars - 80))}\n\n[truncated: continuation context exceeded ${maxChars} characters]`;
}
//# sourceMappingURL=store.js.map