@deepseek-ai/dsh-session-persistence-jsonl 0.1.2-alpha.5 → 0.1.3-alpha.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.
@@ -8,7 +8,7 @@
8
8
  * @module dsh-session-persistence-jsonl/format
9
9
  */
10
10
  import type { SessionEvent, SessionHeader, SessionId, SessionLogOffset as SessionLogOffsetType } from '@deepseek-ai/dsh-session';
11
- import { type SessionStorageMetadata } from '@deepseek-ai/dsh-session-persistence';
11
+ import type { SessionFormatRecovery } from '@deepseek-ai/dsh-session-format';
12
12
  /** Physical encoding selected for JSONL session artifacts. */
13
13
  export type JsonlCompression = 'zstd' | 'none';
14
14
  /**
@@ -18,9 +18,26 @@ export type JsonlCompression = 'zstd' | 'none';
18
18
  */
19
19
  export declare function logSuffix(compression: JsonlCompression): '.jsonl.zstd' | '.jsonl';
20
20
  /**
21
- * The private version-0 physical header stored as the first JSONL record.
22
- * Its optional numeric `seedLength` translates to logical lineage metadata
23
- * plus a separately carried exact inherited cut.
21
+ * Return the canonical filename for one immutable Session format generation.
22
+ * Version zero retains the original suffix-only name; every later generation
23
+ * carries a lowercase numeric `vN` component.
24
+ * @param version - non-negative safe Session format version.
25
+ * @param compression - configured JSONL artifact encoding.
26
+ * @returns the generation filename inside one Session directory.
27
+ */
28
+ export declare function generationLogFilename(version: number, compression: JsonlCompression): string;
29
+ /**
30
+ * Parse one canonical generation filename for the selected physical encoding.
31
+ * Noncanonical, temporary, uppercase, leading-zero, and version-zero-tagged names do
32
+ * not identify committed generations.
33
+ * @param filename - one entry from a Session directory.
34
+ * @param compression - configured JSONL artifact encoding.
35
+ * @returns its format version, or `undefined` when the name is not canonical.
36
+ */
37
+ export declare function parseGenerationLogFilename(filename: string, compression: JsonlCompression): number | undefined;
38
+ /**
39
+ * The current v2 physical header stored as the first JSONL record. The exact
40
+ * inherited cut lives on the last tagged `session/end-seed` event.
24
41
  */
25
42
  interface HeaderLine {
26
43
  type: 'session';
@@ -29,11 +46,17 @@ interface HeaderLine {
29
46
  createdAt: number;
30
47
  cwd?: string;
31
48
  parentSession?: SessionId;
32
- seedLength?: number;
49
+ isSeeded: boolean;
33
50
  origin?: 'subagent';
34
51
  delegationDepth: number;
35
52
  agentPreset?: string;
36
53
  }
54
+ /**
55
+ * Refuse policy fields that never belong to a released Session header.
56
+ * @param value - parsed physical header candidate.
57
+ * @returns nothing after successful validation.
58
+ */
59
+ export declare function assertNoRetiredHeaderFields(value: unknown): void;
37
60
  /**
38
61
  * Build the header line object from a {@link SessionHeader}.
39
62
  * @param header - the immutable session metadata to serialize.
@@ -82,26 +105,37 @@ export declare function projectDir(root: string, cwd: string | undefined): strin
82
105
  */
83
106
  export declare function sessionDir(root: string, cwd: string | undefined, id: SessionId): string;
84
107
  /**
85
- * The append-only event-log file path for a session.
108
+ * Build one immutable Session format generation path.
109
+ * @param root - the backend's session root directory.
110
+ * @param cwd - the session's project directory (`undefined` → `_no-cwd`).
111
+ * @param id - the session id, path-encoded via {@link encodeSegment} before filesystem use.
112
+ * @param version - physical Session format generation.
113
+ * @param compression - physical artifact encoding and filename suffix.
114
+ * @returns the selected generation's configured JSONL artifact path.
115
+ */
116
+ export declare function generationLogPath(root: string, cwd: string | undefined, id: SessionId, version: number, compression: JsonlCompression): string;
117
+ /**
118
+ * Build the current generation's append target path for a Session.
86
119
  * @param root - the backend's session root directory.
87
120
  * @param cwd - the session's project directory (`undefined` → `_no-cwd`).
88
121
  * @param id - the session id, path-encoded via {@link encodeSegment} before filesystem use.
89
122
  * @param compression - physical artifact encoding and filename suffix.
90
- * @returns the session's configured JSONL artifact path.
123
+ * @returns the current Session format generation path.
91
124
  */
92
125
  export declare function logPath(root: string, cwd: string | undefined, id: SessionId, compression: JsonlCompression): string;
93
126
  /**
94
- * Serialize an event batch as JSONL lines (no trailing newline). With
95
- * `packChunks` on, delta-chunk runs pack into `text-chunks` /
96
- * `reasoning-chunks` / `tool-call-chunks` storage rows; off writes one event
97
- * per line. Both modes range-encode provenance at the storage boundary.
98
- * Reading is layout-blind either way ({@link scanLog} always decodes rows),
99
- * so the switch changes only newly written bytes.
127
+ * Serialize a v2 event batch as JSONL lines (no trailing newline). Compact
128
+ * Assistant streams are nested event data; every event occupies one row.
100
129
  * @param events - the batch to serialize, in log order.
101
- * @param packChunks - whether to pack delta runs into storage rows.
102
130
  * @returns the batch's JSONL text; the writer adds the final newline.
103
131
  */
104
- export declare function eventLines(events: readonly SessionEvent[], packChunks: boolean): string;
132
+ export declare function eventLines(events: readonly SessionEvent[]): string;
133
+ /**
134
+ * Serialize one v2 event as one JSONL record without its trailing newline.
135
+ * @param event - current event to encode.
136
+ * @returns one physical JSON record.
137
+ */
138
+ export declare function eventLine(event: SessionEvent): string;
105
139
  interface SessionLogScan {
106
140
  meta: SessionHeader;
107
141
  inheritedEventCount: SessionLogOffsetType;
@@ -115,9 +149,10 @@ interface SessionLogScan {
115
149
  * copied because a decoder may reuse its output buffer after `write()` returns.
116
150
  */
117
151
  export declare class SessionLogScanner {
152
+ private readonly recovery;
118
153
  private readonly meta;
119
- private readonly inheritedEventCount;
120
- private readonly events;
154
+ private readonly restore;
155
+ private eventCount;
121
156
  private fragments;
122
157
  private fragmentBytes;
123
158
  private inputBytes;
@@ -129,7 +164,7 @@ export declare class SessionLogScanner {
129
164
  * Create an event scanner from exactly one newline-terminated header record.
130
165
  * @param headerRecord - the complete first JSONL record, including its newline.
131
166
  */
132
- constructor(headerRecord: Buffer);
167
+ constructor(headerRecord: Buffer, recovery?: SessionFormatRecovery);
133
168
  /**
134
169
  * Consume the next raw plaintext chunk, retaining only an incomplete final record.
135
170
  * @param chunk - bytes immediately following all previously supplied bytes.
@@ -161,18 +196,5 @@ export declare class SessionLogScanner {
161
196
  * @returns the header, preserved event prefix, and byte offset safe to append at.
162
197
  */
163
198
  export declare function scanLog(buffer: Buffer): SessionLogScan;
164
- /**
165
- * Parse just the header line of a log into logical metadata plus its exact
166
- * inherited cut, or `undefined` if it is missing/not a header.
167
- * @param firstLine - the first line of a log file (without its trailing newline).
168
- * @returns parsed storage metadata, or `undefined` for a malformed header.
169
- */
170
- export declare function parseHeader(firstLine: string): SessionStorageMetadata | undefined;
171
- /**
172
- * Parse only the logical header fields needed by lightweight listing.
173
- * @param firstLine - first JSONL line without its trailing newline.
174
- * @returns the logical Session header, or `undefined` for a malformed line.
175
- */
176
- export declare function parseHeaderMeta(firstLine: string): SessionHeader | undefined;
177
199
  export {};
178
200
  //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Durable whole-generation publication for JSONL Session artifacts.
3
+ *
4
+ * Format packages transform parsed JSON values. This module owns the physical
5
+ * encoding, exact source identity, immutable generation files, and exclusive
6
+ * current-generation publication for both configured JSONL suffixes.
7
+ * @module @deepseek-ai/dsh-session-persistence-jsonl/generation
8
+ */
9
+ import { type FileHandle } from 'node:fs/promises';
10
+ import type { SessionFormatArtifact, SessionFormatJsonValue, SessionFormatRestore } from '@deepseek-ai/dsh-session-format';
11
+ import type { JsonlCompression } from './format.ts';
12
+ import { publishNewFileWin32 } from './win32.ts';
13
+ /** Pure adapter between backend-owned JSONL framing and the format catalog. */
14
+ export interface JsonlGenerationFormatAdapter {
15
+ readonly currentVersion: number;
16
+ /** Create the single-pass codec and migration state for a historical header. */
17
+ createRestore(header: Record<string, unknown>): SessionFormatRestore;
18
+ /** Encode one current header record without materializing body rows. */
19
+ encodeHeader(header: SessionFormatArtifact['header'], inheritedEventCount: number): SessionFormatJsonValue;
20
+ /** Encode one current event record. */
21
+ encodeEvent(event: SessionFormatArtifact['events'][number]): SessionFormatJsonValue;
22
+ /** Classify a supported-version artifact that policy refuses to migrate. */
23
+ isUnsupportedMigrationError?(error: unknown): error is Error;
24
+ }
25
+ /** Inputs for preparing one historical generation and publishing its current successor later. */
26
+ export interface PrepareJsonlMigrationOptions {
27
+ /** Immutable generation selected by the backend resolver. */
28
+ readonly sourcePath: string;
29
+ /** Version selected from the source filename and independently checked against its header. */
30
+ readonly sourceVersion: number;
31
+ /** Canonical filename for `format.currentVersion` in the same Session directory. */
32
+ readonly currentPath: string;
33
+ readonly compression: JsonlCompression;
34
+ readonly format: JsonlGenerationFormatAdapter;
35
+ /** Validate one selected historical header's identity before any migration write. */
36
+ readonly validateHistoricalHeader?: (header: Readonly<Record<string, unknown>>) => void | Promise<void>;
37
+ /** Validate the staged file in an isolated worker before publication. */
38
+ readonly verifyCurrentFile: (path: string, compression: JsonlCompression, expectedId: string, expectedEventCount: number, expectedPrefix?: JsonlExpectedPrefix, signal?: AbortSignal) => Promise<JsonlVerifiedGeneration>;
39
+ readonly signal?: AbortSignal;
40
+ }
41
+ /** Small physical identity returned by an isolated generation verifier. */
42
+ export interface JsonlVerifiedGeneration {
43
+ readonly identity: JsonlPhysicalIdentity;
44
+ readonly bytes: number;
45
+ readonly digest: string;
46
+ }
47
+ /** Physical byte prefix already proven to be a valid complete generation. */
48
+ export interface JsonlExpectedPrefix {
49
+ readonly bytes: number;
50
+ readonly digest: string;
51
+ }
52
+ /** A historical source changed after its single decode and migration pass. */
53
+ export declare class JsonlGenerationSourceChangedError extends Error {
54
+ readonly path: string;
55
+ readonly name = "JsonlGenerationSourceChangedError";
56
+ /** @param path - historical generation whose revision changed. */
57
+ constructor(path: string);
58
+ }
59
+ /** Current logical state prepared independently from durable publication. */
60
+ export interface PreparedJsonlMigration {
61
+ readonly sourceIdentity: JsonlPhysicalIdentity;
62
+ readonly artifact: SessionFormatArtifact;
63
+ /** Encode, verify, and exclusively publish once; every call shares the same success or failure. */
64
+ publish(): Promise<JsonlPhysicalIdentity>;
65
+ }
66
+ /** A historical artifact is intact, but the format edge refuses its contents. */
67
+ export declare class JsonlGenerationUnsupportedMigrationError extends Error {
68
+ readonly fromVersion: number;
69
+ readonly reason: Error;
70
+ readonly name = "JsonlGenerationUnsupportedMigrationError";
71
+ /**
72
+ * @param fromVersion - unchanged source generation version.
73
+ * @param reason - format-edge refusal.
74
+ */
75
+ constructor(fromVersion: number, reason: Error);
76
+ }
77
+ /** A current-generation filename already names different or invalid bytes. */
78
+ export declare class JsonlGenerationTargetConflictError extends Error {
79
+ readonly path: string;
80
+ readonly reason: Error;
81
+ readonly name = "JsonlGenerationTargetConflictError";
82
+ /**
83
+ * @param path - immutable target that prevented exclusive publication.
84
+ * @param reason - why the existing target cannot be accepted.
85
+ */
86
+ constructor(path: string, reason: Error);
87
+ }
88
+ /** Stat identity captured together with exact generation bytes. */
89
+ export interface JsonlPhysicalIdentity {
90
+ readonly dev: bigint;
91
+ readonly ino: bigint;
92
+ readonly size: bigint;
93
+ readonly mtimeNs: bigint;
94
+ readonly ctimeNs: bigint;
95
+ }
96
+ /** Exact bytes of one stable file revision together with the stat identity that proved it stable. */
97
+ export interface StablePhysicalFile {
98
+ readonly bytes: Buffer;
99
+ readonly identity: JsonlPhysicalIdentity;
100
+ }
101
+ interface GenerationFileSystem {
102
+ open(path: string, flags: string, mode?: number): Promise<FileHandle>;
103
+ readFile(path: string, signal?: AbortSignal): Promise<Buffer>;
104
+ readdir(path: string): Promise<string[]>;
105
+ stat(path: string): Promise<JsonlPhysicalIdentity>;
106
+ lstat(path: string): Promise<{
107
+ isFile(): boolean;
108
+ isSymbolicLink(): boolean;
109
+ }>;
110
+ link(existingPath: string, newPath: string): Promise<void>;
111
+ rm(path: string): Promise<void>;
112
+ }
113
+ type GenerationBarrierPhase = 'before-source-check' | 'after-publication';
114
+ interface JsonlGenerationInternals {
115
+ readonly fs: GenerationFileSystem;
116
+ readonly randomToken: () => string;
117
+ readonly platform: NodeJS.Platform;
118
+ readonly publishNewWin32: typeof publishNewFileWin32;
119
+ readonly barrier: (phase: GenerationBarrierPhase, attempt: number) => void | Promise<void>;
120
+ }
121
+ /** Dependency overrides for an isolated generation runtime. */
122
+ export type JsonlGenerationRuntimeOverrides = Partial<Omit<JsonlGenerationInternals, 'fs'>> & {
123
+ readonly fs?: Partial<GenerationFileSystem>;
124
+ };
125
+ /** Bound generation operations used by production defaults and deterministic tests. */
126
+ export interface JsonlGenerationRuntime {
127
+ readStable(path: string, signal?: AbortSignal): Promise<StablePhysicalFile>;
128
+ prepare(options: PrepareJsonlMigrationOptions): Promise<PreparedJsonlMigration>;
129
+ verify(path: string, compression: JsonlCompression, expectedId: string, expectedEventCount: number, expectedPrefix?: JsonlExpectedPrefix): Promise<JsonlVerifiedGeneration>;
130
+ }
131
+ /**
132
+ * Read one stable revision of a JSONL file with a single retry. If an append
133
+ * overlaps both reads, return the second read's committed pre-read prefix
134
+ * instead of starving behind a continuous writer.
135
+ * @param path - the generation file to read.
136
+ * @param signal - optional cancellation for the stat/read work.
137
+ * @returns the stable bytes (or the committed prefix) and their stat identity.
138
+ */
139
+ export declare function readStableJsonlFile(path: string, signal?: AbortSignal): Promise<StablePhysicalFile>;
140
+ /**
141
+ * Read and validate one complete current generation for an isolated verifier.
142
+ * @param path - staged or competing current-generation path.
143
+ * @param compression - configured physical encoding.
144
+ * @param expectedId - Session identity expected in the header.
145
+ * @param expectedEventCount - exact logical event count expected after decoding.
146
+ * @param expectedPrefix - verified migration prefix; an append tail may be present and is not validated.
147
+ * @returns stable physical identity and digest for publication comparison.
148
+ */
149
+ export declare function verifyJsonlCurrentGeneration(path: string, compression: JsonlCompression, expectedId: string, expectedEventCount: number, expectedPrefix?: JsonlExpectedPrefix): Promise<JsonlVerifiedGeneration>;
150
+ /**
151
+ * Decode and migrate one historical generation without writing its successor.
152
+ * @param options - resolved source, current target, format adapter, and load cancellation.
153
+ * @returns the current artifact and an idempotent explicit publication operation.
154
+ */
155
+ export declare function prepareJsonlMigration(options: PrepareJsonlMigrationOptions): Promise<PreparedJsonlMigration>;
156
+ /**
157
+ * Create one generation runtime with fixed filesystem and publication dependencies.
158
+ * @param overrides - deterministic filesystem, platform, and race dependencies.
159
+ * @returns bound generation operations.
160
+ */
161
+ export declare function createJsonlGenerationRuntime(overrides?: JsonlGenerationRuntimeOverrides): JsonlGenerationRuntime;
162
+ export {};
163
+ //# sourceMappingURL=generation.d.ts.map
@@ -1,19 +1,21 @@
1
1
  /**
2
2
  * JSONL durable session-persistence backend. It stores a header and contiguous
3
- * events in one append-only file per session, and delegates orchestration to
4
- * {@link PersistenceCoordinator}. Its side-effect-free locator returns the
5
- * absolute per-session log target before materialization.
3
+ * events in immutable generation files under one directory per session and serves the handle-based
4
+ * `SessionPersistence` API: `create`/`open` return per-session handles, and
5
+ * every read validates the same fail-closed storage contract.
6
6
  * @module @deepseek-ai/dsh-session-persistence-jsonl
7
7
  */
8
8
  import { Context } from '@deepseek-ai/cordis';
9
9
  import z from '@deepseek-ai/schemastery';
10
- import { SessionPersistence, type BorrowedSessionSource, type PersistenceBackend, type SessionLocation, type SessionPersistenceSnapshot, type SessionEventSuffix, type SessionInspection, type SessionPersistenceRevision as PersistenceRevision, type SessionRawArtifact, type SessionStorageMetadata, type StoredPrefix } from '@deepseek-ai/dsh-session-persistence';
11
- import type { Session, SessionEvent, SessionId, SessionHeader, SessionLogOffset, SessionPreparation } from '@deepseek-ai/dsh-session';
10
+ import { SessionPersistence, type SessionAccess, type SessionHandle, type SessionHandleReadResult, type SessionPersistenceCreateOptions, type SessionPersistenceListOptions, type SessionPersistenceOpenOptions, type SessionPersistenceSnapshot, type SessionPersistenceStatOptions, type SessionPersistenceRevision as PersistenceRevision } from '@deepseek-ai/dsh-session-persistence';
11
+ import { JsonlSessionHandle } from './storage.ts';
12
+ import { SessionWriteLease } from './lease.ts';
13
+ import type { SessionEvent, SessionId, SessionHeader, SessionLogOffset as SessionLogOffsetType } from '@deepseek-ai/dsh-session';
12
14
  import { type JsonlCompression } from './format.ts';
13
15
  export type { JsonlCompression } from './format.ts';
14
16
  /** Loader schema for the JSONL artifact's physical encoding. */
15
17
  export declare const JsonlCompressionSchema: z<JsonlCompression>;
16
- /** Plugin config: where the JSONL backend keeps its session logs, and the packed-row write switch. */
18
+ /** Plugin config for the JSONL backend's root and physical encoding. */
17
19
  export interface Config {
18
20
  /**
19
21
  * Root directory for all session files. Required (no default): a default of
@@ -23,111 +25,185 @@ export interface Config {
23
25
  * readable directory; an absent root is created on first materialization.
24
26
  */
25
27
  root: string;
26
- /**
27
- * Write runs of consecutive `assistant/chunk` delta events as packed
28
- * `text-chunks`/`reasoning-chunks`/`tool-call-chunks` rows (lossless,
29
- * ~60% smaller logs measured on a real session). Defaults to true; false
30
- * keeps one `SessionEvent` per line for diagnostics. Reading packed rows is
31
- * unconditional: a log's layout never depends on this switch.
32
- */
33
- packChunks?: boolean;
34
28
  /** Physical encoding; defaults to checksummed Zstandard frames. */
35
29
  compression?: JsonlCompression;
36
- /** Maximum cold Session preparations retained for history-to-resume reuse. */
37
- preparedSessionCacheSize?: number;
38
- /** Fixed live-event coalescing window; not a backend completion deadline. */
39
- writeBatchMaxDelayMs?: number;
40
30
  }
41
- /** Opaque coordinator token for replacing bytes recovered from a torn frame. */
42
- interface JsonlTornMarker {
43
- truncateTo: number;
44
- recoveredEvents: SessionEvent[];
31
+ /** One stored event graph whose producer has established immutable sharing. */
32
+ interface FrozenStoredEvents extends SessionHandleReadResult {
33
+ readonly eventState: 'shared-frozen';
34
+ }
35
+ /** State shared by prepared historical and published current logs. */
36
+ interface StoredLogBase extends FrozenStoredEvents {
37
+ readonly meta: SessionHeader;
38
+ readonly tornTruncateTo: number | undefined;
39
+ /** Complete events recovered from the torn final frame; the write path rewrites them durably. */
40
+ readonly recoveredTail: SessionEvent[];
41
+ /** Exact fork-inherited prefix length stored in the header line. */
42
+ readonly inheritedEventCount: SessionLogOffsetType;
43
+ readonly revision: PersistenceRevision;
44
+ }
45
+ /** A decoded current generation that is already durable. */
46
+ interface CurrentStoredLog extends StoredLogBase {
47
+ readonly status: 'current';
45
48
  }
46
49
  /**
47
50
  * The JSONL persistence backend. Load as a plugin; it registers as
48
- * `ctx.sessionPersistence` and (via the coordinator) installs the write-path
49
- * listeners. Its torn-tail marker carries the byte offset and any events
50
- * recovered from an incomplete final Zstandard frame.
51
+ * `ctx.sessionPersistence`. Sessions materialize lazily: a created session is
52
+ * visible to this process immediately, reaches disk on its first append or
53
+ * flush, and never existed if the process crashes before that.
51
54
  */
52
- export declare class JsonlSessionPersistence extends SessionPersistence implements PersistenceBackend<JsonlTornMarker> {
55
+ declare class JsonlSessionPersistence extends SessionPersistence {
53
56
  config: Config;
54
- readonly supportsRawArtifacts = true;
55
- static inject: string[];
56
57
  static Config: z<Config>;
57
- /**
58
- * Backend label for coordinator diagnostics and effects. It shadows
59
- * `Service.name` without changing the service key captured by the base
60
- * constructor.
61
- */
58
+ /** Backend label for diagnostics and effects; shadows `Service.name` without changing the service key. */
62
59
  readonly name = "session-persistence-jsonl";
63
60
  private root;
64
- private packChunks;
65
61
  private compression;
66
- private coordinator;
67
62
  private rootEncodingCheck;
63
+ private readonly tracker;
64
+ private readonly generationFormat;
65
+ /**
66
+ * Bounded LRU of parsed, validated stored logs keyed by session id and
67
+ * guarded by the stat-derived revision, so an immediate cold-read handoff
68
+ * (observation then resume) parses the artifact once. Every local mutation
69
+ * for an id invalidates its entry; a foreign write misses through the
70
+ * revision guard.
71
+ */
72
+ private readonly coldLogMemo;
73
+ /** One joinable decode/migration operation per selected historical Session file revision. */
74
+ private readonly migrationPreparations;
68
75
  constructor(ctx: Context, config: Config);
69
- /** Resolve the absolute target path without touching the filesystem. */
70
- locate(meta: SessionHeader): SessionLocation;
71
- create(meta: SessionHeader, inheritedEventCount?: SessionLogOffset): Promise<void>;
72
- ensureMaterialized(session: Session): Promise<void>;
73
- append(id: SessionId, events: readonly SessionEvent[]): Promise<void>;
74
- prepare(id: SessionId, signal?: AbortSignal): Promise<SessionPreparation>;
75
- load(id: SessionId): Promise<SessionInspection>;
76
- inspect(id: SessionId, signal?: AbortSignal): Promise<SessionInspection>;
77
- borrowSession(id: SessionId, signal?: AbortSignal): Promise<BorrowedSessionSource>;
78
- readFrom(id: SessionId, fromSeq: SessionLogOffset, signal?: AbortSignal): Promise<SessionEventSuffix>;
79
- /** Read a stored prefix by id across all project directories when cwd is unknown. */
80
- loadStored(id: SessionId, signal?: AbortSignal): Promise<StoredPrefix<JsonlTornMarker> | undefined>;
81
- /**
82
- * Read one log's stat-derived revision without loading its event bytes.
83
- * Resolving an id with unknown cwd still scans the project directories.
84
- */
85
- readStoredRevision(id: SessionId, signal?: AbortSignal): Promise<PersistenceRevision | undefined>;
86
- /**
87
- * Read a session's stored artifact text verbatim: the durable file bytes
88
- * decoded from this backend's physical encoding (complete zstd frames
89
- * concatenated, or UTF-8 plaintext). The content is the exact JSONL text the
90
- * backend wrote — never a reconstruction from parsed events — so packed-
91
- * chunk rows, key order, and line breaks survive byte-for-byte. A torn
92
- * final frame is omitted, matching the committed-prefix semantics of every
93
- * other read.
94
- * @param id - the persisted session to read.
95
- * @param signal - optional cancellation for the stat/read/decode work.
96
- * @returns the raw artifact text plus the header parsed from its own first
97
- * line, or `undefined` when the session has no stored artifact.
76
+ /**
77
+ * Refusal-diagnostics hook: the absolute target path, without touching the filesystem.
78
+ * @param meta - the stored header naming the session and its cwd.
79
+ * @returns the artifact kind and absolute path.
98
80
  */
99
- readRaw(id: SessionId, signal?: AbortSignal): Promise<SessionRawArtifact | undefined>;
81
+ private locate;
100
82
  /**
101
- * Read a file's bytes under a revision-stable loop: a writer appending
102
- * between stat and readFile would yield a torn physical file, so retry
103
- * while the stat revision changes.
83
+ * Create a new stored session and take its write ownership. The session is
84
+ * visible to this process immediately; the physical artifact appears on the
85
+ * first append or flush.
86
+ * @param header - the immutable header to store; must be losslessly
87
+ * JSON-serializable with a non-negative safe-integer `createdAt`.
88
+ * @param options - optional cancellation.
89
+ * @returns the owned write handle.
90
+ */
91
+ create(header: SessionHeader, options?: SessionPersistenceCreateOptions): Promise<SessionHandle>;
92
+ /**
93
+ * Open an existing stored session for `read` or single-writer `write`.
94
+ * @param id - the stored session to open.
95
+ * @param access - `read` (no ownership) or `write` (atomic in-process claim).
96
+ * @param options - optional cancellation.
97
+ * @returns the open handle.
98
+ */
99
+ open(id: SessionId, access: SessionAccess, options?: SessionPersistenceOpenOptions): Promise<SessionHandle>;
100
+ /**
101
+ * Flush every active write handle in one durability barrier; see the seam
102
+ * contract.
103
+ * @returns resolution once every write handle active at the call has flushed.
104
+ */
105
+ flush(): Promise<void>;
106
+ /**
107
+ * Observe one stored session without reading its event log.
108
+ * @param id - the stored session to observe.
109
+ * @param options - optional cancellation.
110
+ * @returns the snapshot (`sizeBytes` carries the physical artifact size), or
111
+ * `undefined` when the session does not exist.
112
+ */
113
+ stat(id: SessionId, options?: SessionPersistenceStatOptions): Promise<SessionPersistenceSnapshot | undefined>;
114
+ /**
115
+ * List every stored session visible to this process: materialized artifacts
116
+ * plus this process's created-but-unmaterialized sessions.
117
+ * @param options - optional cancellation.
118
+ * @returns one snapshot per session, in no promised order.
119
+ */
120
+ list(options?: SessionPersistenceListOptions): Promise<readonly SessionPersistenceSnapshot[]>;
121
+ /** Resolve and read one stored log, refusing loudly when the artifact is absent. */
122
+ private requireStoredLog;
123
+ /** Probe the memo and otherwise decode one historical generation under backend cancellation. */
124
+ private loadStoredMigration;
125
+ /** Await shared preparation for one caller and abort it only after its last waiter leaves. */
126
+ private waitForPreparation;
127
+ /** Decode one historical generation without publishing a successor. */
128
+ private prepareStoredMigration;
129
+ /** Publish a prepared historical log before granting write access. */
130
+ private publishStoredMigration;
131
+ /** Translate generation-layer failures into the persistence seam's error vocabulary. */
132
+ private generationFailure;
133
+ /**
134
+ * Read, parse, and validate one stored log as the current logical prefix.
104
135
  * @param path - the artifact file to read.
105
- * @param signal - optional cancellation for the stat/read work.
106
- * @returns the stable bytes and the revision that matched both stats.
136
+ * @param expectedId - the session identity the artifact must carry.
137
+ * @param signal - optional cancellation for the stat/read/decode work.
138
+ * @returns the validated stored log with any torn-tail truncation point.
139
+ */
140
+ readStoredLog(path: string, expectedId: SessionId, signal?: AbortSignal): Promise<CurrentStoredLog>;
141
+ /** Decode and memoize one already-stable current physical snapshot. */
142
+ private decodeStoredLog;
143
+ /** Insert one parsed log into the bounded handoff cache. */
144
+ private memoizeStoredLog;
145
+ /**
146
+ * Resolve a session's current-generation log path.
147
+ * @param id - the stored session to locate.
148
+ * @param signal - optional cancellation for the directory scans.
149
+ * @returns the current artifact path, or `undefined` while only a historical generation exists.
150
+ */
151
+ resolveCurrentLog(id: SessionId, signal?: AbortSignal): Promise<string | undefined>;
152
+ /**
153
+ * Durably append one validated batch; lazily materializes on the first write.
154
+ * @param header - the session's stored header.
155
+ * @param events - the validated contiguous batch, in seq order.
156
+ * @param isMaterialized - whether the session already has a durable artifact.
157
+ * @param inheritedEventCount - the exact fork-inherited prefix length written into a materializing header line.
158
+ */
159
+ persistBatch(header: SessionHeader, events: readonly SessionEvent[], isMaterialized: boolean, inheritedEventCount: SessionLogOffsetType): Promise<void>;
160
+ /**
161
+ * Materialize a header-only artifact for an explicitly durable empty session.
162
+ * @param header - the session's stored header.
163
+ * @param inheritedEventCount - the exact fork-inherited prefix length written into the header line.
164
+ */
165
+ persistHeader(header: SessionHeader, inheritedEventCount: SessionLogOffsetType): Promise<void>;
166
+ /**
167
+ * Truncate a torn physical tail durably before this session's first new append.
168
+ * @param header - the session's stored header.
169
+ * @param truncateTo - the byte offset the artifact is truncated to.
170
+ */
171
+ truncateTornTail(header: SessionHeader, truncateTo: number): Promise<void>;
172
+ /**
173
+ * Whether this process still tracks a created-but-unmaterialized session.
174
+ * @param id - the session to test.
175
+ * @returns true while the pending entry exists.
176
+ */
177
+ hasPendingSession(id: SessionId): boolean;
178
+ /**
179
+ * Release one handle's backend bookkeeping on close.
180
+ * @param handle - the closing handle.
181
+ * @param materialized - whether the session reached durable storage.
107
182
  */
108
- private readStableFile;
183
+ releaseHandle(handle: JsonlSessionHandle, materialized: boolean): void;
109
184
  /**
110
- * Read a stored prefix and convert torn-tail state to the opaque marker the
111
- * coordinator can round-trip without knowing the physical encoding.
185
+ * Acquire the session directory's kernel write lock; the kernel holds it
186
+ * until the handle's close releases the descriptor, including on process death.
187
+ * @param id - the session the lock guards.
188
+ * @param cwd - header cwd used to derive the directory for a fresh session.
189
+ * @param dir - the resolved directory of an existing artifact, when known.
190
+ * @returns the held lock.
112
191
  */
113
- private readPrefix;
192
+ private acquireLease;
193
+ /**
194
+ * Acquire the cross-process write lock for a materializing created session,
195
+ * called by its handle immediately before the first log bytes publish.
196
+ * @param header - the session's stored header (its cwd derives the directory).
197
+ * @returns the held lock.
198
+ */
199
+ acquireWriteLease(header: SessionHeader): Promise<SessionWriteLease>;
114
200
  /** Decode complete frames and retain complete JSONL records from a torn final frame. */
115
201
  private readZstdPrefix;
116
- /** Durably append a batch, lazily materializing the file when not yet present. */
117
- appendBatch(storage: SessionStorageMetadata, events: readonly SessionEvent[], isMaterialized: boolean): Promise<void>;
118
- /** Materialize a header-only JSONL artifact for an explicitly durable empty session. */
119
- materializeHeader(storage: SessionStorageMetadata): Promise<void>;
120
- /**
121
- * Make a crash repair durable: truncate a torn tail, restore complete events
122
- * decoded from it, then append synthetic closers. Two fsync'd steps — the seam
123
- * does not require this to be atomic.
124
- */
125
- commitRepair(storage: SessionStorageMetadata, tornMarker: JsonlTornMarker | undefined, closers: readonly SessionEvent[]): Promise<void>;
126
- /** List valid unique stored sessions' metadata (header line only — no full-log parse). */
127
- list(signal?: AbortSignal): Promise<SessionHeader[]>;
128
- /** List metadata plus a stat-derived identity for each append-only log. */
129
- listSnapshots(signal?: AbortSignal): Promise<SessionPersistenceSnapshot[]>;
130
202
  private listArtifacts;
203
+ /** Read and translate one selected generation header without inspecting its body. */
204
+ private readGenerationHeader;
205
+ /** Convert format-catalog string identities to current branded Session metadata. */
206
+ private currentHeader;
131
207
  /** Atomically write the header line + first batch (temp-write, fsync, publish). */
132
208
  private materialize;
133
209
  private materializePosix;
@@ -157,12 +233,16 @@ export declare class JsonlSessionPersistence extends SessionPersistence implemen
157
233
  private readFirstLine;
158
234
  /** Read and validate only the independently compressed header frame. */
159
235
  private readFirstZstdLine;
160
- /** Find the unique physical log for an id across every project directory. */
236
+ /** Select the numerically highest canonical generation in one Session directory. */
237
+ private resolveGenerationInDirectory;
238
+ /** Find the unique authoritative generation for an id across project directories. */
161
239
  private findLog;
162
240
  /** Require an existing configured root to be a readable directory. */
163
241
  private assertUsableRoot;
164
242
  /** Reject metadata that does not identify the selected physical log. */
165
243
  private assertStoredIdentity;
244
+ /** Validate a supported historical header against the selected source path. */
245
+ private validateSourceIdentity;
166
246
  /**
167
247
  * Whether two path spellings resolve to the same physical file. This admits
168
248
  * case aliases on case-insensitive filesystems without weakening identity
@@ -178,11 +258,18 @@ export declare class JsonlSessionPersistence extends SessionPersistence implemen
178
258
  private checkRootEncoding;
179
259
  private rejectLegacyFlatArtifact;
180
260
  private rejectOppositeArtifact;
261
+ /** Return the highest canonical generation encoded with the other configured suffix. */
262
+ private findOppositeGenerationInDirectory;
181
263
  private oppositeCompression;
182
264
  private encodingMismatch;
183
265
  private legacyLayout;
184
266
  private exists;
185
267
  private assertLogParentAllowsAbsence;
186
268
  }
269
+ /**
270
+ * One open channel onto a JSONL-stored session: the shared storage-handle
271
+ * scaffolding over this backend's file primitives. Reads re-scan the artifact
272
+ * under the stable-read loop.
273
+ */
187
274
  export default JsonlSessionPersistence;
188
275
  //# sourceMappingURL=index.d.ts.map