import path from "node:path";
import { nowIso, readJsonFile, sanitizeText, withFileLock, writeJsonFile } from "../../shared/index.js";
import { runDir } from "../run-state/store.js";
export function backgroundTasksPath(store, runId) {
    return path.join(runDir(store, runId), "background_tasks.json");
}
export function backgroundNotificationsPath(store, runId) {
    return path.join(runDir(store, runId), "background_notifications.json");
}
export async function listBackgroundTasks(store, runId) {
    return (await readJsonFile(backgroundTasksPath(store, runId))) ?? [];
}
export async function listBackgroundNotifications(store, runId, options = {}) {
    const notifications = (await readJsonFile(backgroundNotificationsPath(store, runId))) ?? [];
    return notifications
        .filter((notification) => !options.status || notification.status === options.status)
        .filter((notification) => !options.parentSessionId || !notification.parent_session_id || notification.parent_session_id === options.parentSessionId);
}
export async function getBackgroundTask(store, runId, backgroundId) {
    return (await listBackgroundTasks(store, runId)).find((task) => task.background_id === backgroundId);
}
export async function upsertBackgroundTask(store, runId, input) {
    const file = backgroundTasksPath(store, runId);
    const task = await withFileLock(file, async () => {
        const tasks = (await readJsonFile(file)) ?? [];
        const index = tasks.findIndex((task) => task.background_id === input.background_id);
        const timestamp = nowIso();
        const current = index >= 0 ? tasks[index] : undefined;
        const status = input.status;
        const terminal = isTerminalStatus(status);
        const next = {
            ...current,
            ...input,
            run_id: runId,
            updated_at: input.updated_at ?? timestamp,
            last_heartbeat_at: input.last_heartbeat_at ?? current?.last_heartbeat_at ?? timestamp,
            stale_after_minutes: input.stale_after_minutes ?? current?.stale_after_minutes,
            stale_at: input.stale_at ?? current?.stale_at,
            stale_reason: input.stale_reason ? sanitizeText(input.stale_reason).slice(0, 1000) : current?.stale_reason,
            completed_at: input.completed_at ?? current?.completed_at ?? (terminal ? timestamp : undefined),
            checkpoint_count: input.checkpoint_count ?? current?.checkpoint_count ?? 0,
            output_tail: sanitizeTail(input.output_tail ?? current?.output_tail ?? []),
        };
        if (index >= 0)
            tasks[index] = next;
        else
            tasks.push(next);
        await writeJsonFile(file, tasks);
        return next;
    });
    if (isTerminalStatus(task.status))
        await upsertBackgroundTerminalNotification(store, runId, task);
    return task;
}
export async function updateBackgroundTask(store, runId, backgroundId, patch) {
    const file = backgroundTasksPath(store, runId);
    const task = await withFileLock(file, async () => {
        const tasks = (await readJsonFile(file)) ?? [];
        const index = tasks.findIndex((task) => task.background_id === backgroundId);
        if (index < 0)
            return undefined;
        const current = tasks[index];
        const timestamp = nowIso();
        const { checkpoint_delta, output_tail, last_message, error, ...rest } = patch;
        const status = rest.status ?? current.status;
        const terminal = status === "completed" || status === "failed" || status === "cancelled";
        const next = {
            ...current,
            ...rest,
            status,
            output_tail: output_tail ? sanitizeTail(output_tail) : current.output_tail,
            last_message: last_message ? sanitizeText(last_message).slice(0, 1000) : current.last_message,
            error: error ? sanitizeText(error).slice(0, 1000) : current.error,
            checkpoint_count: current.checkpoint_count + (checkpoint_delta ?? 0),
            updated_at: timestamp,
            last_heartbeat_at: rest.last_heartbeat_at ?? (status === "running" ? timestamp : current.last_heartbeat_at),
            completed_at: terminal ? (current.completed_at ?? timestamp) : rest.completed_at ?? current.completed_at,
        };
        tasks[index] = next;
        await writeJsonFile(file, tasks);
        return next;
    });
    if (task && isTerminalStatus(task.status))
        await upsertBackgroundTerminalNotification(store, runId, task);
    return task;
}
export async function markStaleBackgroundTasks(store, runId, input = {}) {
    const file = backgroundTasksPath(store, runId);
    const timestamp = input.now ?? nowIso();
    const staleAfterMinutes = Math.max(0.001, input.staleAfterMinutes ?? store.config.budget.stale_worker_minutes);
    const staleAfterMs = staleAfterMinutes * 60_000;
    return withFileLock(file, async () => {
        const tasks = (await readJsonFile(file)) ?? [];
        const marked = [];
        const next = tasks.map((task) => {
            if (task.status !== "running" || task.stale_at)
                return task;
            const heartbeat = Math.max(safeTime(task.last_heartbeat_at), safeTime(task.updated_at), safeTime(task.started_at));
            if (safeTime(timestamp) - heartbeat < staleAfterMs)
                return task;
            const stale = {
                ...task,
                updated_at: timestamp,
                stale_after_minutes: staleAfterMinutes,
                stale_at: timestamp,
                stale_reason: sanitizeText(input.reason ?? `No heartbeat for ${staleAfterMinutes} minute(s).`).slice(0, 1000),
                last_message: sanitizeText(`Stale worker: no heartbeat for ${staleAfterMinutes} minute(s).`).slice(0, 1000),
            };
            marked.push(stale);
            return stale;
        });
        if (marked.length)
            await writeJsonFile(file, next);
        return marked;
    });
}
export async function markBackgroundTaskNotified(store, runId, backgroundId) {
    const task = await updateBackgroundTask(store, runId, backgroundId, { notified_at: nowIso() });
    await markBackgroundNotificationsDeliveredForBackgrounds(store, runId, [backgroundId]);
    return task;
}
export async function upsertBackgroundTerminalNotification(store, runId, task) {
    const backgroundStatus = task.status;
    if (!isTerminalStatus(backgroundStatus))
        return undefined;
    const file = backgroundNotificationsPath(store, runId);
    return withFileLock(file, async () => {
        const notifications = (await readJsonFile(file)) ?? [];
        const timestamp = nowIso();
        const index = notifications.findIndex((notification) => notification.background_id === task.background_id && notification.background_status === task.status);
        const current = index >= 0 ? notifications[index] : undefined;
        const next = {
            notification_id: current?.notification_id ?? `${task.background_id}-${task.status}`,
            run_id: runId,
            background_id: task.background_id,
            parent_session_id: task.parent_session_id,
            session_id: task.session_id,
            agent_call_id: task.agent_call_id,
            target_agent: task.target_agent,
            task_id: task.task_id,
            background_status: backgroundStatus,
            status: current?.status ?? "pending",
            severity: notificationSeverity(backgroundStatus),
            title: sanitizeText(`${task.target_agent ?? task.kind} background ${backgroundStatus}`),
            body: backgroundNotificationBody(task),
            next_action: backgroundStatus === "completed" ? "review_output" : "retry_or_handoff",
            created_at: current?.created_at ?? timestamp,
            updated_at: timestamp,
            delivered_at: current?.delivered_at,
            dismissed_at: current?.dismissed_at,
        };
        if (index >= 0)
            notifications[index] = next;
        else
            notifications.push(next);
        await writeJsonFile(file, notifications);
        return next;
    });
}
export async function markBackgroundNotificationDelivered(store, runId, notificationId) {
    const file = backgroundNotificationsPath(store, runId);
    return withFileLock(file, async () => {
        const notifications = (await readJsonFile(file)) ?? [];
        const index = notifications.findIndex((notification) => notification.notification_id === notificationId);
        if (index < 0)
            return undefined;
        const current = notifications[index];
        const timestamp = nowIso();
        const next = {
            ...current,
            status: "delivered",
            delivered_at: current.delivered_at ?? timestamp,
            updated_at: timestamp,
        };
        notifications[index] = next;
        await writeJsonFile(file, notifications);
        return next;
    });
}
export async function markBackgroundNotificationsDeliveredForBackgrounds(store, runId, backgroundIds) {
    const ids = new Set(backgroundIds);
    const file = backgroundNotificationsPath(store, runId);
    return withFileLock(file, async () => {
        const notifications = (await readJsonFile(file)) ?? [];
        const timestamp = nowIso();
        const delivered = [];
        const next = notifications.map((notification) => {
            if (!ids.has(notification.background_id) || notification.status !== "pending")
                return notification;
            const updated = {
                ...notification,
                status: "delivered",
                delivered_at: notification.delivered_at ?? timestamp,
                updated_at: timestamp,
            };
            delivered.push(updated);
            return updated;
        });
        await writeJsonFile(file, next);
        return delivered;
    });
}
function sanitizeTail(lines) {
    return lines.map((line) => sanitizeText(line).slice(0, 4000)).filter(Boolean).slice(-80);
}
function isTerminalStatus(status) {
    return status === "completed" || status === "failed" || status === "cancelled";
}
function notificationSeverity(status) {
    if (status === "completed")
        return "info";
    if (status === "cancelled")
        return "warning";
    return "error";
}
function backgroundNotificationBody(task) {
    const latest = task.error ?? task.last_message ?? task.output_tail.at(-1);
    const owner = task.target_agent ?? task.kind;
    const action = task.status === "completed" ? "Review output and attach evidence if useful." : "Review output, then retry, cancel, or hand off the blocker.";
    return sanitizeText([
        `${owner} background task ${task.background_id} is ${task.status}.`,
        task.session_id ? `Child session: ${task.session_id}.` : undefined,
        latest ? `Latest: ${latest}` : undefined,
        `Next action: ${action}`,
    ].filter((line) => Boolean(line)).join("\n")).slice(0, 2000);
}
function safeTime(value) {
    const parsed = value ? Date.parse(value) : 0;
    return Number.isFinite(parsed) ? parsed : 0;
}
//# sourceMappingURL=store.js.map