/** * EventJournal — append-only per-session event log. * * Every ProviderRuntimeEvent emitted by a HarnessDriver is appended to * /.memoire/studio/events/.jsonl as a single JSON * line with the event's monotonic seq as the index. * * Unlocks: * - Audit trails: replay every action a session performed * - Offline playback: re-render a finished session without keeping it * warm in memory * - Crash forensics: see exactly the last events before a runtime kill * - Replay-from-cursor: a UI client that lost connection mid-session * can request `replay(sessionId, fromSeq)` and stream the tail * * Two implementations: * - FileEventJournal — appends JSONL files to disk * - MemoryEventJournal — for tests * * The journal is injected into HarnessDriverConfig.eventJournal. When * absent, drivers behave exactly as before (no journal writes). Like * snapshots, the integration is opt-in. */ import type { SessionId } from "../contracts/ids.js"; import type { ProviderRuntimeEvent } from "../contracts/provider-runtime.js"; export interface EventJournal { append(sessionId: SessionId, event: ProviderRuntimeEvent): Promise; /** Returns events for the session in seq order, optionally from a cursor. */ replay(sessionId: SessionId, fromSeq?: number): AsyncIterable; /** Returns the list of session IDs with persisted journals. */ list(): Promise; /** Removes the journal for a single session. */ delete(sessionId: SessionId): Promise; /** Removes journals matching the prune criteria. Returns the count removed. */ prune(opts: { olderThanMs?: number; max?: number; }): Promise; } /** * In-memory implementation. Useful for tests and dry-runs. */ export declare class MemoryEventJournal implements EventJournal { private readonly buffers; private readonly mtimes; append(sessionId: SessionId, event: ProviderRuntimeEvent): Promise; replay(sessionId: SessionId, fromSeq?: number): AsyncIterable; list(): Promise; delete(sessionId: SessionId): Promise; prune(opts: { olderThanMs?: number; max?: number; }): Promise; } /** * Disk-backed implementation. Each session gets its own JSONL file under * /.memoire/studio/events/.jsonl. Appends are * serialized per session via an inflight promise chain so out-of-order * concurrent writes can't interleave bytes. */ export declare class FileEventJournal implements EventJournal { private readonly dir; private ensureDirPromise; private readonly inflight; constructor(projectRoot: string); private ensureDir; private filePath; append(sessionId: SessionId, event: ProviderRuntimeEvent): Promise; replay(sessionId: SessionId, fromSeq?: number): AsyncIterable; list(): Promise; delete(sessionId: SessionId): Promise; prune(opts: { olderThanMs?: number; max?: number; }): Promise; } /** * Helper: collect a journal replay into an array. Convenient for tests. */ export declare function collectReplay(journal: EventJournal, sessionId: SessionId, fromSeq?: number): Promise;