@x1a0f3n9/dsh-session-persistence 0.1.2-alpha.6 → 0.1.5-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,105 @@
1
- /** Stable failures exposed by the session-persistence service. */
1
+ /**
2
+ * Stable failures exposed by the session-persistence service and its handles,
3
+ * including the format refusals shared by every backend: a stored log this
4
+ * build cannot faithfully interpret is refused, never misread, and the
5
+ * refusal points at the raw artifact when the backend keeps one per session.
6
+ * @module @x1a0f3n9/dsh-session-persistence/errors
7
+ */
2
8
  import type { SessionId } from '@x1a0f3n9/dsh-session';
3
- /** The requested Session identity has no materialized durable log. */
9
+ /** The requested Session identity has no durable log visible to this caller. */
4
10
  export declare class SessionPersistenceNotFoundError extends Error {
5
11
  readonly sessionId: SessionId;
6
12
  /** @param sessionId - absent durable Session identity. */
7
13
  constructor(sessionId: SessionId);
8
14
  }
15
+ /** `create` targeted a Session identity that already exists in this backend. */
16
+ export declare class SessionAlreadyExistsError extends Error {
17
+ readonly sessionId: SessionId;
18
+ /** @param sessionId - the occupied durable Session identity. */
19
+ constructor(sessionId: SessionId);
20
+ }
21
+ /** A write open found the session already bound to an active write handle. */
22
+ export declare class SessionAlreadyOwnedError extends Error {
23
+ readonly sessionId: SessionId;
24
+ /** @param sessionId - the session whose write ownership is taken. */
25
+ constructor(sessionId: SessionId);
26
+ }
27
+ /** A mutation (`append`/`flush`) was called on a read handle. */
28
+ export declare class SessionReadOnlyError extends Error {
29
+ readonly sessionId: SessionId;
30
+ /**
31
+ * @param sessionId - the session the read handle observes.
32
+ * @param operation - the refused mutating operation name.
33
+ */
34
+ constructor(sessionId: SessionId, operation: string);
35
+ }
36
+ /**
37
+ * A write handle's ownership is permanently gone: its lease expired, a renewal
38
+ * failed, or the durable ownership record no longer names this handle. The
39
+ * handle never re-acquires ownership — close it and reopen for write.
40
+ *
41
+ * Declared for the cross-process lease layer; the shipped in-process backends
42
+ * never throw it yet.
43
+ */
44
+ export declare class SessionOwnershipLostError extends Error {
45
+ readonly sessionId: SessionId;
46
+ /** @param sessionId - the session whose write ownership this handle lost. */
47
+ constructor(sessionId: SessionId);
48
+ }
49
+ /** An operation was called on a handle after `close()` was called. */
50
+ export declare class SessionHandleClosedError extends Error {
51
+ readonly sessionId: SessionId;
52
+ /**
53
+ * @param sessionId - the session the closed handle addressed.
54
+ * @param operation - the refused operation name.
55
+ */
56
+ constructor(sessionId: SessionId, operation: string);
57
+ }
58
+ /**
59
+ * A backend-resolved, per-session local artifact location. Carried only by
60
+ * refusal diagnostics ({@link SessionFormatUnsupportedError}) so a user can
61
+ * find the raw log a build refused to interpret; it is not a consumer-facing
62
+ * query — log access goes through a session handle's `read`.
63
+ */
64
+ export interface SessionLocation {
65
+ /** Backend-specific artifact kind, for example `jsonl`. */
66
+ readonly kind: string;
67
+ /** Absolute path to this session's backend-owned artifact. */
68
+ readonly path: string;
69
+ }
70
+ /** Durable session contents failed validation after a successful backend read. */
71
+ export declare class SessionPersistenceCorruptionError extends Error {
72
+ /**
73
+ * @param message - stable corruption context.
74
+ * @param options - original validation failure.
75
+ */
76
+ constructor(message: string, options: ErrorOptions);
77
+ }
78
+ /**
79
+ * The stored log is intact but this runtime cannot faithfully interpret it:
80
+ * the header carries an unsupported format version, or an event's type is
81
+ * unknown to this build. Distinct from {@link SessionPersistenceCorruptionError}
82
+ * — nothing is damaged; the raw log remains readable at {@link location} when
83
+ * the backend keeps one artifact per session.
84
+ */
85
+ export declare class SessionFormatUnsupportedError extends Error {
86
+ readonly location?: SessionLocation | undefined;
87
+ /**
88
+ * @param message - stable reason the log cannot be interpreted, already
89
+ * including the raw-log path when one exists.
90
+ * @param location - the backend's artifact location, when one exists.
91
+ */
92
+ constructor(message: string, location?: SessionLocation | undefined);
93
+ }
94
+ /**
95
+ * Direction-aware refusal text for a stored session whose format version this
96
+ * build does not read. Shared by load-time checks and by backends that must
97
+ * refuse BEFORE decoding version-dependent structure (a future format may not
98
+ * satisfy this build's structural checks at all, and the user must see
99
+ * "upgrade the harness", never "corrupt").
100
+ * @param id - the stored session id, for message context.
101
+ * @param version - the stored format version.
102
+ * @returns the stable refusal text, without a raw-log path suffix.
103
+ */
104
+ export declare function sessionFormatVersionRefusal(id: string, version: number): string;
9
105
  //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1,108 @@
1
+ /**
2
+ * The per-session storage handle: one open channel onto a stored session's
3
+ * append-only event log, returned by `SessionPersistence.create`/`open`.
4
+ * @module @x1a0f3n9/dsh-session-persistence/handle
5
+ */
6
+ import type { SessionEvent, SessionHeader, SessionId, SessionLogOffset, SessionSeedEventState } from '@x1a0f3n9/dsh-session';
7
+ /**
8
+ * Log access granted by an open. `write` is read-write: the session's single
9
+ * mutator, which also reads its own log. `read` only observes — it never
10
+ * takes ownership and works while another handle or process holds `write`.
11
+ */
12
+ export type SessionAccess = 'read' | 'write';
13
+ /** Options for {@link SessionHandle.read}. */
14
+ export interface SessionHandleReadOptions {
15
+ /** Optional cancellation for backend read work. */
16
+ readonly signal?: AbortSignal;
17
+ }
18
+ /** One persistence event slice returned by {@link SessionHandle.read}. */
19
+ export interface SessionHandleReadResult {
20
+ /**
21
+ * Whether event values are exclusively owned or shared only after deep
22
+ * freezing. Slicing preserves the producer's state even when no events remain.
23
+ */
24
+ readonly eventState: SessionSeedEventState;
25
+ /** Event values in a caller-owned outer array. */
26
+ readonly events: readonly SessionEvent[];
27
+ }
28
+ /** Options for {@link SessionHandle.append}. */
29
+ export interface SessionHandleAppendOptions {
30
+ /** Optional cancellation observed before the write starts. */
31
+ readonly signal?: AbortSignal;
32
+ }
33
+ /** Options for {@link SessionHandle.flush}. */
34
+ export interface SessionHandleFlushOptions {
35
+ /** Optional cancellation observed before the barrier starts. */
36
+ readonly signal?: AbortSignal;
37
+ }
38
+ /**
39
+ * One open channel onto a stored session. A handle is single-owner state, not
40
+ * a shared service: `read` never backtracks below what this handle already
41
+ * observed, a `write` handle reads its own successful appends, and `close()`
42
+ * is the one teardown (idempotent, uncancellable; `Symbol.asyncDispose`
43
+ * delegates to it). Every operation on a closed handle rejects with
44
+ * `SessionHandleClosedError`.
45
+ *
46
+ * Freshness across handles: once an `append` or `flush` resolves on a write
47
+ * handle, every read STARTED afterwards on the same backend instance — on any
48
+ * handle, or through `stat`/`list` — observes at least that prefix.
49
+ * Reads concurrent with a mutation carry no ordering promise beyond the valid
50
+ * contiguous prefix.
51
+ */
52
+ export interface SessionHandle extends AsyncDisposable {
53
+ /** The stored session this handle addresses. */
54
+ readonly id: SessionId;
55
+ /** The immutable stored header, fixed at `create`/`open`. */
56
+ readonly header: SessionHeader;
57
+ /**
58
+ * Exact fork-inherited prefix length stored with the log; `0` when
59
+ * `header.isSeeded` is false. Storage metadata paired with the header for
60
+ * every body read, never part of the replayable event log.
61
+ */
62
+ readonly inheritedEventCount: SessionLogOffset;
63
+ /** Whether this handle may mutate the log. */
64
+ readonly access: SessionAccess;
65
+ /**
66
+ * Read a slice of the valid contiguous logical log. The slice is a legal log
67
+ * prefix segment: a torn physical tail is never returned, and repeated reads
68
+ * on this handle never observe an older state than a prior read.
69
+ * @param offset - first logical event seq to include; defaults to `0`.
70
+ * @param length - maximum number of events to return; defaults to the rest
71
+ * of the log. An offset at or past the end returns an empty list.
72
+ * @param options - optional cancellation.
73
+ * @returns the caller-owned outer slice plus the ownership state of its event values.
74
+ */
75
+ read(offset?: number, length?: number, options?: SessionHandleReadOptions): Promise<SessionHandleReadResult>;
76
+ /**
77
+ * Append a contiguous batch continuing the current logical end. The first
78
+ * event's `seq` MUST equal the stored next-seq. Ordinary appends never
79
+ * rewrite committed events; an explicit persistence `truncate` is the
80
+ * destructive exception. Persistence is best-effort: on resolution the batch is
81
+ * accepted, ordered, and visible to reads on this backend instance, but
82
+ * only a resolved {@link flush} promises it survives a crash — a backend
83
+ * may buffer or batch physical writes behind append. Rejects with
84
+ * `SessionReadOnlyError` on a read handle and `SessionOwnershipLostError`
85
+ * when write ownership is gone.
86
+ * @param events - the contiguous batch, in seq order.
87
+ * @param options - optional cancellation observed before the write starts.
88
+ */
89
+ append(events: readonly SessionEvent[], options?: SessionHandleAppendOptions): Promise<void>;
90
+ /**
91
+ * The durability barrier — the one operation that promises storage: on
92
+ * resolution every acknowledged append is durable and the session is
93
+ * materialized for other processes; an empty created session becomes
94
+ * durably listable here. Callers that must survive a crash flush; a backend
95
+ * whose `append` already persists on resolution treats this as
96
+ * materialize-if-needed. Rejects with `SessionReadOnlyError` on a read
97
+ * handle.
98
+ * @param options - optional cancellation observed before the barrier starts.
99
+ */
100
+ flush(options?: SessionHandleFlushOptions): Promise<void>;
101
+ /**
102
+ * Release the handle: a read handle frees local resources; a write handle
103
+ * completes pending durability and releases write ownership. Idempotent,
104
+ * asynchronous, and deliberately not cancellable.
105
+ */
106
+ close(): Promise<void>;
107
+ }
108
+ //# sourceMappingURL=handle.d.ts.map
@@ -1,243 +1,204 @@
1
1
  /**
2
2
  * Durable session-persistence Service Definition (`ctx.sessionPersistence`). Backends store
3
3
  * {@link SessionEvent}s as the event-sourced log and carry non-replayable
4
- * {@link SessionHeader} metadata separately.
4
+ * {@link SessionHeader} metadata separately; callers address one stored
5
+ * session through a {@link SessionHandle} obtained from `create`/`open`.
5
6
  * @module @x1a0f3n9/dsh-session-persistence
6
7
  */
7
8
  import { Context, Service } from '@deepseek-ai/cordis';
8
- import { SessionPreparation, SessionLogOffset } from '@x1a0f3n9/dsh-session';
9
- import type { Session, SessionEvent, SessionId, SessionHeader } from '@x1a0f3n9/dsh-session';
9
+ import type { SessionEvent, SessionHeader, SessionId, SessionLogOffset, SessionSeqCursor } from '@x1a0f3n9/dsh-session';
10
+ import type { SessionHandle, SessionAccess } from './handle.ts';
10
11
  import type { SessionPersistenceRevision } from './revision.ts';
11
12
  export type { SessionHeader } from '@x1a0f3n9/dsh-session';
12
13
  export { SessionPersistenceRevision } from './revision.ts';
13
- export { SessionPersistenceNotFoundError } from './errors.ts';
14
- /** Lightweight immutable source identity returned without loading a full log. */
14
+ export type { SessionAccess, SessionHandle, SessionHandleAppendOptions, SessionHandleFlushOptions, SessionHandleReadOptions, SessionHandleReadResult, } from './handle.ts';
15
+ export { SessionAlreadyExistsError, SessionAlreadyOwnedError, SessionFormatUnsupportedError, SessionHandleClosedError, SessionOwnershipLostError, SessionPersistenceCorruptionError, SessionPersistenceNotFoundError, SessionReadOnlyError, sessionFormatVersionRefusal, } from './errors.ts';
16
+ export type { SessionLocation } from './errors.ts';
17
+ export { assertContiguous, assertStoredId, assertVersion, materializeAppendBatch, materializeCreateHeader, validateStoredEvents, } from './storage-contract.ts';
18
+ /**
19
+ * Lightweight stored-session observation returned by {@link SessionPersistence.stat}
20
+ * and {@link SessionPersistence.list} without reading the full event log.
21
+ */
15
22
  export interface SessionPersistenceSnapshot {
16
- /** Detached metadata for one materialized session. */
17
- header: SessionHeader;
18
- /** Opaque source-qualified token that changes whenever this stored log changes. */
19
- revision: SessionPersistenceRevision;
23
+ /** Detached metadata for one stored session. */
24
+ readonly header: SessionHeader;
25
+ /** Opaque change token; see {@link SessionPersistence.stat}. */
26
+ readonly revision: SessionPersistenceRevision;
27
+ /** Logical event count, when the backend can provide it cheaply from metadata; otherwise absent. */
28
+ readonly eventCount?: number;
29
+ /** Physical artifact byte size, when the backend can provide it cheaply (JSONL); otherwise absent. */
30
+ readonly sizeBytes?: number;
20
31
  }
21
- /** Logical Session header paired with its exact inherited cut for body-bearing storage operations. */
32
+ /** Options for {@link SessionPersistence.create}. */
33
+ export interface SessionPersistenceCreateOptions {
34
+ /** Optional cancellation observed before backend work starts. */
35
+ readonly signal?: AbortSignal;
36
+ /**
37
+ * Exact fork-inherited prefix length. Required when `header.isSeeded` is
38
+ * true and must be omitted (or `0`) otherwise; the backend refuses a
39
+ * mismatch at create.
40
+ */
41
+ readonly inheritedEventCount?: SessionLogOffset;
42
+ }
43
+ /**
44
+ * Logical Session header paired with its exact inherited cut for body-bearing
45
+ * storage operations. `isSeeded` marks fork lineage on the header; the
46
+ * numeric cut travels beside it, never inside the replayable event log.
47
+ */
22
48
  export interface SessionStorageMetadata {
23
49
  /** Validated immutable Session header. */
24
50
  readonly meta: SessionHeader;
25
51
  /** Number of leading events inherited from the Session's fork parent. */
26
52
  readonly inheritedEventCount: SessionLogOffset;
27
53
  }
28
- /** Immutable logical session prepared from persistence or a live owner. */
54
+ /** Immutable logical session read: storage metadata plus the complete validated event log. */
29
55
  export interface SessionInspection extends SessionStorageMetadata {
30
- /** Validated contiguous logical event log. */
56
+ /** Contiguous validated events from seq 0. */
31
57
  readonly events: readonly SessionEvent[];
32
58
  }
33
- /** Detached logical suffix returned by one explicit stored-log offset read. */
34
- export interface SessionEventSuffix extends SessionStorageMetadata {
35
- /** First requested log offset; {@link events} contains only seqs at or after it. */
36
- readonly fromSeq: SessionLogOffset;
37
- /** Valid contiguous stored events at or after {@link fromSeq}; not a complete Session log when the offset is nonzero. */
38
- readonly events: readonly SessionEvent[];
59
+ /** Options for {@link SessionPersistence.open}. */
60
+ export interface SessionPersistenceOpenOptions {
61
+ /** Optional cancellation observed before backend work starts. */
62
+ readonly signal?: AbortSignal;
39
63
  }
40
- /** A borrowed exact Session source returned from a cold materialization or concurrent live owner. */
41
- export type BorrowedSessionSource = Disposable & ({
42
- /** A reusable unpublished Session is pinned until this observation is disposed. */
43
- readonly source: 'prepared';
44
- /** Immutable header and logical event prefix observed together. */
45
- readonly inspection: SessionInspection;
46
- /** Durable revision represented by the prepared source. */
47
- readonly revision: SessionPersistenceRevision;
48
- /** Exact unpublished Session retained for a later {@link prepare}. */
49
- readonly preparedSession: Session;
50
- } | {
51
- /** A live Session won source resolution while the persistence read was starting. */
52
- readonly source: 'live';
53
- /** Immutable live header and event prefix observed together. */
54
- readonly inspection: SessionInspection;
55
- });
56
- /** A backend's own raw artifact text for one session, verbatim. */
57
- export interface SessionRawArtifact extends SessionStorageMetadata {
58
- /** The artifact's base filename on disk, without any physical encoding suffix. */
59
- readonly filename: string;
60
- /** The artifact's full text content, decoded from the backend's physical encoding. */
61
- readonly content: string;
64
+ /** Options for {@link SessionPersistence.stat}. */
65
+ export interface SessionPersistenceStatOptions {
66
+ /** Optional cancellation for backend metadata reads. */
67
+ readonly signal?: AbortSignal;
68
+ }
69
+ /** Options for {@link SessionPersistence.list}. */
70
+ export interface SessionPersistenceListOptions {
71
+ /** Optional cancellation for backend listing work. */
72
+ readonly signal?: AbortSignal;
73
+ }
74
+ /** Options for {@link SessionPersistence.readHistorySuffix}. */
75
+ export interface SessionHistorySuffixOptions {
76
+ /** Maximum append-surface user/assistant messages the suffix must cover. */
77
+ readonly maxMessages: number;
78
+ /** Exclusive upper bound of an older page; omit for the newest page. */
79
+ readonly beforeSeq?: number;
80
+ /** Inclusive newest seq the page may include; omit for the durable cursor. */
81
+ readonly throughSeq?: number;
82
+ /** Optional cancellation for backend suffix reads. */
83
+ readonly signal?: AbortSignal;
84
+ }
85
+ /**
86
+ * A contiguous tail of one stored log that covers one history page without
87
+ * restoring the whole artifact.
88
+ */
89
+ export interface SessionHistorySuffix {
90
+ /** Immutable stored header. */
91
+ readonly header: SessionHeader;
92
+ /** Exact inherited prefix length, or `0` when a seeded cut is not in the suffix. */
93
+ readonly inheritedEventCount: SessionLogOffset;
94
+ /** Contiguous events covering the requested page, possibly with interrupted-turn closers. */
95
+ readonly events: SessionEvent[];
96
+ /** Last logical seq in this view, including in-memory closers. */
97
+ readonly cursor: SessionSeqCursor;
62
98
  }
63
- export { DEFAULT_PREPARED_SESSION_CACHE_SIZE, DEFAULT_WRITE_BATCH_MAX_DELAY_MS, MAX_WRITE_BATCH_DELAY_MS, PersistenceCoordinator, SessionFormatUnsupportedError, SessionPersistenceCorruptionError, sessionFormatVersionRefusal, } from './coordinator.ts';
64
- export type { PersistenceBackend, PersistenceCoordinatorOptions, StoredPrefix, StoredSuffix, } from './coordinator.ts';
65
99
  declare module '@deepseek-ai/cordis' {
66
100
  interface Context {
67
101
  sessionPersistence: SessionPersistence;
68
102
  }
69
103
  }
70
104
  /**
71
- * A backend-resolved, per-session local artifact location. The path is an
72
- * absolute target path and can name an artifact that has not materialized yet.
73
- * Consumers must treat it as a location hint, never as an authorization token.
74
- */
75
- export interface SessionLocation {
76
- /** Backend-specific artifact kind, for example `jsonl`. */
77
- readonly kind: string;
78
- /** Absolute path to this session's backend-owned artifact. */
79
- readonly path: string;
80
- }
81
- /**
82
- * Durable append-only session storage. Implementations preserve contiguous,
83
- * losslessly JSON-serializable events; {@link append} resolves only after
84
- * durability, and {@link load} balances a complete interrupted tail without
85
- * rewriting committed events.
105
+ * Durable session storage addressed through per-session handles. Normal
106
+ * writes append to a contiguous log; an explicit destructive tail truncation
107
+ * may rewrite the retained prefix when a backend supports it.
108
+ *
109
+ * Storage semantics shared by every backend: events are contiguous from seq 0;
110
+ * a torn physical tail is never returned to a reader and
111
+ * is truncated by the write path before its first append; reads validate
112
+ * current-format records only and refuse unknown vocabulary fail-closed.
113
+ * `append` persists best-effort; `flush` — per handle or service-wide — is
114
+ * the durability barrier.
115
+ *
116
+ * Visibility: a created session is observable through `stat`/`list`/`open`
117
+ * in this process from the moment `create` resolves, even while a backend
118
+ * defers physical materialization (a pure optimization); other processes see
119
+ * the session only once it materializes, and a session that never
120
+ * materialized before a crash never existed. `SessionHandle.flush` forces
121
+ * materialization.
122
+ *
123
+ * Freshness: once an `append` or `flush` resolves, reads started afterwards
124
+ * on this backend instance observe at least that prefix.
86
125
  */
87
126
  export declare abstract class SessionPersistence extends Service {
88
127
  constructor(ctx: Context);
89
128
  /**
90
- * Resolve this backend's independent local artifact for a session without
91
- * reading, creating, flushing, or otherwise materializing it. A backend
92
- * that does not own one artifact per Session returns `undefined`.
93
- * @param meta - the immutable session header whose artifact is requested.
94
- * @returns the backend-specific absolute location, when one exists.
95
- */
96
- abstract locate(meta: SessionHeader): SessionLocation | undefined;
97
- /**
98
- * Whether this backend exposes one verbatim raw artifact per session.
99
- * A backend that declares `true` must override {@link readRaw}.
129
+ * Create a new stored session and take its write ownership.
130
+ * @param header - the immutable header (id, version, cwd, lineage) to store.
131
+ * @param options - optional cancellation.
132
+ * @returns a `write` handle owned by the caller; close it to release ownership.
133
+ * @throws {SessionAlreadyExistsError} when the id already exists.
100
134
  */
101
- abstract readonly supportsRawArtifacts: boolean;
135
+ abstract create(header: SessionHeader, options?: SessionPersistenceCreateOptions): Promise<SessionHandle>;
102
136
  /**
103
- * Read a session's backend-owned artifact text verbatim — the exact durable
104
- * bytes the backend wrote (decoded from its physical encoding, e.g. a
105
- * decompressed JSONL). The returned `content` is the raw text, not a
106
- * reconstruction from parsed events, so it preserves backend-specific
107
- * serialization (chunk packing, key order, line breaks). Callers first test
108
- * {@link supportsRawArtifacts}; `undefined` then means only that the requested
109
- * session has no materialized artifact.
110
- * @param _id - the persisted session to read (unused by the default: no
111
- * per-session artifact).
112
- * @param signal - optional cancellation for backend read work.
113
- * @returns the raw artifact plus its parsed header, or `undefined` when the
114
- * session is absent.
115
- * @throws when this backend does not expose per-session raw artifacts.
137
+ * Open an existing stored session.
138
+ *
139
+ * `read` never takes ownership and works while another handle (or process)
140
+ * holds write ownership. `write` atomically claims single-writer ownership;
141
+ * an existing active owner rejects.
142
+ * @param id - the stored session to open.
143
+ * @param access - `read` or `write`.
144
+ * @param options - optional cancellation.
145
+ * @returns the open handle.
146
+ * @throws {SessionPersistenceNotFoundError} when the session does not exist.
147
+ * @throws {SessionAlreadyOwnedError} for `write` when ownership is taken.
116
148
  */
117
- readRaw(_id: SessionId, signal?: AbortSignal): Promise<SessionRawArtifact | undefined>;
149
+ abstract open(id: SessionId, access: SessionAccess, options?: SessionPersistenceOpenOptions): Promise<SessionHandle>;
118
150
  /**
119
- * Register a new session's metadata. A backend MAY defer the physical write
120
- * until the first {@link append} (lazy materialization), in which case a
121
- * created-but-never-appended session is absent from {@link list}
122
- * — abandoned sessions leave nothing behind.
123
- * @param meta - the immutable header (id, version, cwd, lineage) to record.
124
- * @param inheritedEventCount - exact fork-inherited prefix length. Required
125
- * for a seeded header and omitted only for an unseeded header.
151
+ * Flush every active write handle owned by this service instance in one
152
+ * durability barrier: each handle's routed live events drain durably and
153
+ * its session materializes, exactly as that handle's own
154
+ * `SessionHandle.flush` would. Read handles buffer nothing and are
155
+ * untouched. A handle closed concurrently counts as flushed — close itself
156
+ * drains durably.
157
+ * @returns resolution once every write handle active at the call has flushed.
158
+ * @throws {AggregateError} naming each session whose flush failed; the
159
+ * remaining handles still flush.
126
160
  */
127
- abstract create(meta: SessionHeader, inheritedEventCount?: SessionLogOffset): Promise<void>;
161
+ abstract flush(): Promise<void>;
128
162
  /**
129
- * Ensure a live session has a durable header even when it has no events.
130
- * Ordinary sessions remain lazily materialized; lifecycle frontends call
131
- * this only when an empty session itself is a durable resumable resource.
132
- * @param _session - exact live session whose registered header is materialized.
163
+ * Observe one stored session without reading its event log or taking
164
+ * ownership.
165
+ *
166
+ * The snapshot's `revision` is an opaque change token comparable only
167
+ * against revisions from the same service instance and session id: equal
168
+ * revisions may be treated as an unchanged log; unequal revisions promise
169
+ * nothing. Write-ownership churn does not change a revision. It exists for
170
+ * derived read-model caches keyed off `stat`/`list`; it plays no part in
171
+ * open, read, or resume.
172
+ * @param id - the stored session to observe.
173
+ * @param options - optional cancellation.
174
+ * @returns the snapshot, or `undefined` when the session does not exist.
133
175
  */
134
- ensureMaterialized(_session: Session): Promise<void>;
176
+ abstract stat(id: SessionId, options?: SessionPersistenceStatOptions): Promise<SessionPersistenceSnapshot | undefined>;
135
177
  /**
136
- * Durably persist a batch of events. Honors the append-only and contiguous-
137
- * seq contracts: the first event's `seq` MUST equal the stored next-seq
138
- * (after `load` has durably closed any interrupted turn). Rejects non-JSON-
139
- * serializable `event.data` with an error naming the offending event type.
140
- * A seeded session's first materializing batch must reach its complete
141
- * inherited prefix.
142
- * @param id - the session the batch belongs to.
143
- * @param events - the contiguous batch to persist, in seq order.
178
+ * List every stored session visible to this process, in no promised order.
179
+ * @param options - optional cancellation.
180
+ * @returns one snapshot per stored session.
144
181
  */
145
- abstract append(id: SessionId, events: readonly SessionEvent[]): Promise<void>;
182
+ abstract list(options?: SessionPersistenceListOptions): Promise<readonly SessionPersistenceSnapshot[]>;
146
183
  /**
147
- * Physically replace a live session's durable log with an earlier prefix.
184
+ * Permanently replace one session's durable event log with an earlier prefix.
148
185
  * The caller must apply the matching in-memory truncation only after this
149
- * promise resolves. Backends without rewrite support fail loudly.
150
- * @param session - exact live Session being changed.
151
- * @param length - number of events to retain.
152
- */
153
- truncate(_session: Session, _length: SessionLogOffset): Promise<void>;
154
- /**
155
- * Prepare the exact unpublished Session used by resume. Implementations may
156
- * reuse object graphs retained by an earlier {@link inspect} after confirming
157
- * their durable revision is still current; disposal releases an unpublished
158
- * reservation. Revision retries require the durable log to remain unchanged
159
- * for one read/check round trip; continuous external writers may delay completion.
160
- * @param id - persisted session to prepare.
161
- * @param signal - optional cancellation for preparation work.
162
- * @returns one owned unpublished Session preparation.
163
- */
164
- prepare(id: SessionId, signal?: AbortSignal): Promise<SessionPreparation>;
165
- /**
166
- * Load an immutable balanced logical view and commit any required cold
167
- * recovery. A complete interrupted final turn is preserved and durably
168
- * closed with missing tool errors plus any open step and turn boundaries;
169
- * only a torn final record is discarded. Unknown versions and corruption in
170
- * the committed prefix reject. Implementations MUST NOT crash-repair an
171
- * identity still bound to a live Session: a balanced live log may return as a
172
- * durable snapshot, while an open live turn rejects. Returned values may be
173
- * shared with immutable live or prepared state and must not be mutated.
174
- * Revision-based implementations may wait for one stable read/check round trip.
175
- * @param id - the persisted session to reload.
176
- * @returns the header and a log ending on a balanced `turn/end`.
186
+ * promise resolves. Backends that do not support rewriting reject loudly.
187
+ * @param _id - the live session whose durable log is being rewritten.
188
+ * @param _length - number of events to retain.
189
+ * @returns resolution after the retained prefix is durable.
177
190
  */
178
- abstract load(id: SessionId): Promise<SessionInspection>;
191
+ truncate(_id: SessionId, _length: SessionLogOffset): Promise<void>;
179
192
  /**
180
- * Inspect an immutable logical session without committing recovery or
181
- * publishing it. A cold complete interrupted turn receives synthetic closers
182
- * in memory and a torn physical tail remains untouched. An already-live
183
- * Session instead yields its current immutable snapshot, which may contain an
184
- * open turn and its `session/end-seed` boundary. Coordinator-backed
185
- * implementations retain the exact cold unpublished Session for bounded
186
- * reuse by a later {@link prepare}. A stale ready source is reloaded; a source
187
- * already committing or reserved for resume remains exclusive, and inspection
188
- * may borrow its immutable view. Callers borrow only the immutable header and
189
- * log. Continuous external writers may delay revision convergence.
190
- * @param id - the persisted session to inspect.
191
- * @param signal - optional cancellation for queued and backend read work.
192
- * @returns the validated header and current logical event log.
193
- */
194
- abstract inspect(id: SessionId, signal?: AbortSignal): Promise<SessionInspection>;
195
- /**
196
- * Borrow one exact inspection while retaining any reusable prepared source.
197
- * A cold observation must pin the exact prepared Session that a later
198
- * {@link prepare} reserves. Implementations must not degrade this operation
199
- * to a detached {@link inspect} result.
200
- * @param id - persisted session to observe.
201
- * @param signal - optional cancellation for preparation work.
202
- * @returns a disposable immutable observation.
203
- */
204
- abstract borrowSession(id: SessionId, signal?: AbortSignal): Promise<BorrowedSessionSource>;
205
- /**
206
- * Read the stored events from `fromSeq` onward — the read-from-seq
207
- * primitive for read models that resume from a watermark (e.g. a persisted
208
- * projection cache folding only the tail past its checkpoint). Unlike
209
- * {@link inspect}, it is a detached physical suffix read: no preparation
210
- * cache, torn-tail truncation, synthetic closers, or coordinator-state
211
- * publication. Only events from the valid contiguous stored prefix are
212
- * returned, so a torn fragment never reaches the caller. `fromSeq` at or
213
- * beyond the stored prefix returns an empty event list (never an error).
214
- * A backend whose medium can seek by seq may read only the suffix;
215
- * sequential media such as JSONL still parse the whole artifact and skip
216
- * forward. The primitive bounds what is returned and refolded, not every
217
- * backend's physical read.
218
- * @param id - the persisted session to read.
219
- * @param fromSeq - first event offset to include.
220
- * @param signal - optional cancellation for queued and backend read work.
221
- * @returns storage metadata, the requested offset, and stored events with `seq >= fromSeq`.
222
- */
223
- abstract readFrom(id: SessionId, fromSeq: SessionLogOffset, signal?: AbortSignal): Promise<SessionEventSuffix>;
224
- /**
225
- * Lightweight listing from metadata, without a full-log parse.
226
- * @param signal - optional cancellation for backend listing work.
227
- * @returns one header per materialized session.
228
- */
229
- abstract list(signal?: AbortSignal): Promise<SessionHeader[]>;
230
- /**
231
- * List materialized sessions with cheap per-log change tokens.
232
- *
233
- * Repeated observations of an unchanged log return the same revision. A
234
- * successful mutating {@link load} repair changes the next listed revision.
235
- * Revisions also distinguish independently backed stores so backend-local
236
- * counters cannot compare equal across different persistence sources.
237
- * @param signal - optional cancellation for backend snapshot-listing work.
238
- * @returns one header and opaque revision per materialized session without loading full logs.
193
+ * Read a contiguous tail covering one history page without restoring the
194
+ * whole log. Backends that cannot cheaply decode a suffix return `undefined`
195
+ * so callers fall back to a full observation.
196
+ * @param _id - the stored session to read.
197
+ * @param _options - page bounds and cancellation.
198
+ * @returns the suffix, or `undefined` when this backend has no suffix reader
199
+ * or the stored generation must be migrated first.
239
200
  */
240
- abstract listSnapshots(signal?: AbortSignal): Promise<SessionPersistenceSnapshot[]>;
201
+ readHistorySuffix(_id: SessionId, _options: SessionHistorySuffixOptions): Promise<SessionHistorySuffix | undefined>;
241
202
  }
242
203
  export default SessionPersistence;
243
204
  //# sourceMappingURL=index.d.ts.map