import { type VersionedSnapshotStore } from "./snapshot-protocol.js"; export type TurnPreambleOutcome = { kind: "warm"; baseVersion: number; } | { kind: "hydrated"; baseVersion: number; storeBehindExpected: boolean; /** * Store-wins anomaly: the store's version was BELOW this worker's * local marker (a restore from an older backup, or store data loss). * The store still wins — we hydrate what it holds — but the caller * emits `session.snapshot_regressed` so the regression is visible. */ regressed?: { markerVersion: number; storeVersion: number; }; } | { kind: "already-committed"; version: number; result: unknown; } /** * Local dir was cleared (or never existed) and the store holds nothing: * getOrCreate decides fresh-create vs lossy replay exactly as today. * `lossy` marks the data-loss flavor (turns were committed but the store * lost them) for observability. */ | { kind: "fresh"; baseVersion: number; lossy: boolean; }; export interface TurnCommitOutcome { version: number; contentHash: string; /** Committed (compressed) tar size — feeds session persistence stats. */ sizeBytes?: number; /** Uncompressed tar-stream size — feeds the compression-ratio stat. */ rawSizeBytes?: number; /** A racing/prior attempt of this same turn committed first. */ alreadyCommitted: boolean; /** * The winning attempt's recorded result (from .ps-turn-commit.json in * the restored snapshot). Present only when alreadyCommitted and the * commit file was readable; the caller must return THIS result, not * its own body's (§3.2 restore-not-replay). */ storedResult?: unknown; /** * Store-wins: whether this commit actually advanced the store. * true — a new version (or an alreadyCommitted same-turnKey winner) landed. * false — the snapshot was NOT published: the turn was user-stopped, or a * discarded/foreign turn advanced the store off our base while we * ran (superseded). The sentinel is left dirty so the next turn * rehydrates the winner; the caller emits `snapshot_unpublished`. */ published: boolean; /** Why the snapshot was not published (only when `published` is false). */ unpublishedReason?: "stopped" | "superseded"; /** * For a `superseded` unpublish: the store coordinates of the writer that * won the base — carried from the `SnapshotConflictError` so the emitted * `snapshot_unpublished` event can name what superseded this turn (foreign * writer vs restore race vs the session's own discarded turn). Absent for a * `stopped` unpublish (no commit was attempted, so nothing was observed). */ observedStoreVersion?: number; observedStoreTurnKey?: string; } export interface TurnLifecycleContext { store: VersionedSnapshotStore; sessionStateDir: string; sessionId: string; /** * Transcript epoch this turn belongs to (session regeneration). 0 = * legacy. Scopes every store call to the epoch's CAS chain, and gates * the warm fast path: local files stamped by a different epoch are a * dead incarnation and are never trusted (resolved from the store or * discarded — never a throw, a throw would wedge the session). */ transcriptEpoch: number; /** Orchestration's last recorded snapshot version (validation input). */ expectedVersion: number; /** Deterministic per-turn key (orchestration-generated GUID). */ turnKey: string; /** Destroy the in-memory ManagedSession only — disk untouched. */ dropWarmSession: () => Promise; trace: (message: string) => void; } /** * Preamble (store-wins). Runs under the per-session run-turn lock, before * getOrCreate. ONE probe per turn is the reconcile oracle — never the * orchestration's `expectedVersion`, which goes stale the moment a stopped or * zombie turn advances the store the control plane discarded (the divergence * this protocol removes). On return the local dir is in exactly one state: * trusted at `baseVersion` (warm — local marker matches the store's * version+hash), restored at the stored version (hydrated — the store wins), * absent (fresh), or restored at this turn's committed version * (already-committed — the caller returns the stored result without a body). */ export declare function runTurnPreamble(ctx: TurnLifecycleContext): Promise; /** * Last-resort recovery when getOrCreate fails on missing/lost local state * DESPITE the preamble's resolution (e.g. the SDK refused to resume from * intact-looking files): restore the committed snapshot if one exists. * Returns the hydrated version, or null when the store holds nothing — * only then may the caller fall back to the lossy fresh replay, whose * store-deleting reset is a no-op against an empty store. */ export declare function attemptStoreRecovery(ctx: TurnLifecycleContext): Promise; /** * Postamble (c1–c4). Runs under the per-session run-turn lock after the * body produced `result`. Transient store failures retry in place; a CAS * conflict (foreign writer) always throws — the activity must fail loudly. * An `alreadyCommitted` CAS outcome (a racing duplicate of this same turn * won) triggers restore-not-replay: the winner's snapshot and result are * adopted, discarding this attempt's divergent local state. */ export declare function runTurnCommit(ctx: TurnLifecycleContext, baseVersion: number, result: unknown): Promise; //# sourceMappingURL=session-lifecycle.d.ts.map