import type { SessionEvent } from '../types/session.js'; /** * Idea #1 from IDEAS.md — Stateful Session Recovery. * * `SessionRecovery` is the read-side companion to the in-flight * marker mechanism. When the agent loop is running, it writes an * `in_flight_start` event at the current point in the log. On * clean shutdown, a matching `in_flight_end` follows. Provider and * tool events may legitimately appear after the start marker, so * recovery finds the latest lifecycle boundary rather than assuming * the marker is literally the last JSONL record. * * Detection is diagnostic only. Normal session resume reconstructs the * conversation by replaying persisted JSONL events; it must not blindly * re-execute tool calls after a crash. `recover()` exposes the tail after the * last checkpoint so callers can explain which persisted work was in flight. * * Concurrency: pure read; no writes. Safe to call from multiple * processes simultaneously. */ export interface StaleSession { sessionId: string; /** Path to the JSONL log. */ path: string; /** Last event ts (the in_flight_start timestamp). */ lastEventTs: string; /** Context the agent was working on when it died. */ context: string; /** Total events in the log. */ eventCount: number; } export interface RecoveryPlan { sessionId: string; /** True if the session is stale (has a dangling in_flight_start). */ stale: boolean; /** The last `checkpoint` event before the un-replayed work, or null. */ lastCheckpoint: SessionEvent | null; /** All persisted events after the last checkpoint (diagnostic in-flight tail). */ pendingEvents: SessionEvent[]; /** The dangling in_flight_start event, if any. */ inFlightStart: SessionEvent | null; /** Free-form context the agent was working on, if any. */ context: string | null; } /** * Result of `SessionRecovery.recover(sessionId)`. Distinct from * `StaleSession`: a session is "stale" if its latest lifecycle * boundary is an open marker, but a "recovery plan" can also be generated for * clean sessions whose last checkpoint is older than the * conversation history (e.g. a user-initiated "rewind to last * good state" flow). This is a diagnostic plan, not authorization to replay * external side effects. */ export declare class SessionRecovery { private readonly dir; private static readonly MAX_PENDING_EVENTS; private static readonly MAX_PENDING_BYTES; /** * Scan a session log and return a `StaleSession` if and only if the newest * lifecycle boundary is an `in_flight_start` without a later * `in_flight_end`/`session_end`. Ordinary provider/tool events after the * marker do not make the session clean. Returns `null` when: * - the log does not exist; * - the log is empty; * - the latest lifecycle boundary is `in_flight_end` or `session_end`; * - there is no lifecycle boundary (legacy/pre-marker log). * * The reverse scanner is chunked and line-aware. It can cross arbitrarily * large JSONL records (for example a large tool result) without imposing a * fixed tail-size correctness limit. Clean logs normally return after the * first chunk; stale logs continue counting lines so `eventCount` remains * the documented total rather than a tail-only approximation. */ detectStale(sessionId: string): Promise; /** * Generate a recovery plan for a session. The plan describes * the persisted tail after the last checkpoint, plus the dangling in-flight * marker if present. SessionStore.resume() independently reconstructs state * from the journal and does not re-run these events as commands. * * Returns a non-null plan for ANY session that has at least * one event after a checkpoint (or, for legacy sessions, at * least one event). Pure read; no mutation. */ recover(sessionId: string): Promise; /** * List every stale session in a directory. Returns an array * (possibly empty) sorted by `lastEventTs` descending — most * recent crash first. */ listResumable(): Promise; private filePath; constructor(dir: string); } //# sourceMappingURL=session-recovery.d.ts.map