/** * MaintenanceRunner — periodic background task that keeps the engine's * persistent state from rotting. * * Today, snapshots and event journals never get cleaned up. A long-lived * runtime accumulates files indefinitely. The maintenance runner is the * only mechanism that prunes them. * * Default schedule (configurable): * - Tick every 30 seconds * - Snapshots: prune entries older than 7 days * - Journals: prune entries older than 30 days * - Snapshots: cap at 5,000 sessions (FIFO eviction by updatedAt) * - Journals: cap at 5,000 sessions (FIFO eviction by mtime) * * The runner is intentionally simple: a plain setInterval, not Effect * yet. The Effect-based scheduling lands in commit 12 alongside the new * RPC surface; for now this just needs to work and be testable. * * Errors during a tick are caught and surfaced via the optional onError * callback. They never crash the runner — a failed prune in one tick * just gets retried on the next. */ import type { SnapshotStore } from "../snapshots/snapshot-store.js"; import type { EventJournal } from "../journal/event-journal.js"; export interface MaintenancePolicy { /** Tick interval in ms. Default 30,000 (30s). */ tickIntervalMs?: number; /** Drop snapshots older than this many ms. Default 7 days. */ snapshotMaxAgeMs?: number; /** Cap snapshot count. Default 5,000. */ snapshotMaxCount?: number; /** Drop event journals older than this many ms. Default 30 days. */ journalMaxAgeMs?: number; /** Cap journal count. Default 5,000. */ journalMaxCount?: number; } export interface MaintenanceTaskResult { readonly snapshotsPruned: number; readonly journalsPruned: number; readonly tickAt: string; readonly elapsedMs: number; } export interface MaintenanceRunnerConfig { readonly snapshotStore?: SnapshotStore; readonly eventJournal?: EventJournal; readonly policy?: MaintenancePolicy; readonly onError?: (error: unknown, context: { task: string; }) => void; readonly onTick?: (result: MaintenanceTaskResult) => void; /** Injectable scheduler — defaults to setInterval. Tests pass a manual ticker. */ readonly scheduler?: MaintenanceScheduler; } export interface MaintenanceScheduler { start(intervalMs: number, run: () => Promise): void; stop(): void; } export declare class MaintenanceRunner { private readonly config; private readonly policy; private readonly scheduler; private running; constructor(config: MaintenanceRunnerConfig); start(): void; stop(): void; isRunning(): boolean; /** * Run one maintenance pass synchronously. Useful for tests and for the * runtime's startup path (run-on-boot before the interval kicks in). */ tick(): Promise; } /** * Test scheduler: lets you call .fire() manually instead of waiting on * setInterval. The runner uses whatever scheduler you pass. */ export declare class ManualScheduler implements MaintenanceScheduler { private active; start(_intervalMs: number, run: () => Promise): void; stop(): void; fire(): Promise; }