/** * In-memory event log per task. * * Each task accumulates StreamEvents in an append-only buffer. Readers poll * with an offset ("give me events from index N onward") so they can reconnect * mid-stream without replaying from scratch. * * The log is ephemeral — it lives for the duration of the Node process. Once a * run completes the events stay in memory for late reconnects but are garbage- * collected when the task is explicitly cleared. */ import type { StreamEvent } from "@/lib/types"; export interface EventEntry { /** Monotonically increasing index within this task's log. */ index: number; /** Epoch-ms when the event was appended. */ ts: number; event: StreamEvent; } interface TaskLog { entries: EventEntry[]; done: boolean; } const logs = new Map(); function ensureLog(taskId: string): TaskLog { let log = logs.get(taskId); if (!log) { log = { entries: [], done: false }; logs.set(taskId, log); } return log; } /** Append a StreamEvent to the task's log. */ export function appendEvent(taskId: string, event: StreamEvent): EventEntry { const log = ensureLog(taskId); const entry: EventEntry = { index: log.entries.length, ts: Date.now(), event, }; log.entries.push(entry); return entry; } /** Mark the log as complete — no more events will arrive. */ export function markDone(taskId: string): void { const log = logs.get(taskId); if (log) log.done = true; } /** Read events starting at `fromIndex`. Returns entries + whether the log is done. */ export function readEvents( taskId: string, fromIndex = 0, ): { entries: EventEntry[]; done: boolean } { const log = logs.get(taskId); if (!log) return { entries: [], done: false }; return { entries: log.entries.slice(fromIndex), done: log.done, }; } /** Whether the task's log has been marked done. */ export function isDone(taskId: string): boolean { return logs.get(taskId)?.done ?? false; } /** Total number of events in the log. */ export function eventCount(taskId: string): number { return logs.get(taskId)?.entries.length ?? 0; } /** Remove a task's log to free memory. */ export function clearLog(taskId: string): void { logs.delete(taskId); }