/** * SnapshotStore — per-session persistent snapshot of HarnessDriver state. * * Pattern adapted from t3code's providerSnapshot/providerStatusCache. The * snapshot captures everything needed to resume a session after a runtime * crash or reconnect: session/turn state, accumulated tokens, last error. * * Today, killing the runtime mid-turn drops the live conversation. With * snapshots, the next driver constructor for the same sessionId reads * the snapshot and resumes from where it left off. * * Two implementations: * - FileSnapshotStore — persists JSON files to .memoire/studio/snapshots/ * - MemorySnapshotStore — for tests * * The store is injected into HarnessDriverConfig.snapshotStore. When not * provided, drivers behave exactly as before (no snapshotting, no resume). * This keeps the dependency optional and the rollout incremental. */ import type { HarnessId, SessionId, ThreadId, TurnId } from "../contracts/ids.js"; import type { SessionState, TurnState } from "../contracts/provider-runtime.js"; export interface HarnessSnapshot { readonly sessionId: SessionId; readonly harnessId: HarnessId; readonly threadId?: ThreadId; readonly sessionState: SessionState; readonly currentTurnId?: TurnId; readonly currentTurnState?: TurnState; readonly model?: string; readonly totalInputTokens: number; readonly totalOutputTokens: number; readonly totalReasoningTokens: number; readonly estimatedCostUsd: number; readonly lastError?: string; readonly lastEventAt: string; readonly lastEventSeq: number; readonly createdAt: string; readonly updatedAt: string; } export interface SnapshotStore { load(sessionId: SessionId): Promise; save(snapshot: HarnessSnapshot): Promise; list(): Promise; delete(sessionId: SessionId): Promise; prune(opts: { olderThanMs?: number; max?: number; }): Promise; } /** * In-memory implementation. Useful for tests, dry-runs, and the case where * crash-recovery isn't worth the disk I/O (very short sessions). */ export declare class MemorySnapshotStore implements SnapshotStore { private readonly map; load(sessionId: SessionId): Promise; save(snapshot: HarnessSnapshot): Promise; list(): Promise; delete(sessionId: SessionId): Promise; prune(opts: { olderThanMs?: number; max?: number; }): Promise; } /** * Disk-backed implementation. Each session gets its own JSON file under * /.memoire/studio/snapshots/.json. Writes are * atomic (write-to-temp + rename) so a crash mid-write doesn't corrupt. */ export declare class FileSnapshotStore implements SnapshotStore { private readonly dir; private ensureDirPromise; constructor(projectRoot: string); private ensureDir; private filePath; load(sessionId: SessionId): Promise; save(snapshot: HarnessSnapshot): Promise; list(): Promise; delete(sessionId: SessionId): Promise; prune(opts: { olderThanMs?: number; max?: number; }): Promise; } /** * Helper to construct a fresh snapshot from current driver state. Drivers * call this after each state-changing event and pass the result to * SnapshotStore.save(). */ export declare function buildSnapshot(input: { sessionId: SessionId; harnessId: HarnessId; threadId?: ThreadId; sessionState: SessionState; currentTurnId?: TurnId; currentTurnState?: TurnState; model?: string; totalInputTokens: number; totalOutputTokens: number; totalReasoningTokens: number; estimatedCostUsd: number; lastError?: string; lastEventSeq: number; createdAt: string; }): HarnessSnapshot;