@earendil-works/pi-durable 0.87.0 → 0.99.0

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.
Files changed (177) hide show
  1. package/README.md +72 -1
  2. package/dist/documents.d.ts +66 -0
  3. package/dist/documents.d.ts.map +1 -0
  4. package/dist/documents.js +98 -0
  5. package/dist/documents.js.map +1 -0
  6. package/dist/entries.d.ts +13 -0
  7. package/dist/entries.d.ts.map +1 -0
  8. package/dist/entries.js +18 -0
  9. package/dist/entries.js.map +1 -0
  10. package/dist/env/index.d.ts +131 -0
  11. package/dist/env/index.d.ts.map +1 -0
  12. package/dist/env/index.js +45 -0
  13. package/dist/env/index.js.map +1 -0
  14. package/dist/env/node.d.ts +45 -0
  15. package/dist/env/node.d.ts.map +1 -0
  16. package/dist/env/node.js +908 -0
  17. package/dist/env/node.js.map +1 -0
  18. package/dist/env/utils/adaptive-publisher.d.ts +24 -0
  19. package/dist/env/utils/adaptive-publisher.d.ts.map +1 -0
  20. package/dist/env/utils/adaptive-publisher.js +79 -0
  21. package/dist/env/utils/adaptive-publisher.js.map +1 -0
  22. package/dist/env/utils/output-capture.d.ts +31 -0
  23. package/dist/env/utils/output-capture.d.ts.map +1 -0
  24. package/dist/env/utils/output-capture.js +238 -0
  25. package/dist/env/utils/output-capture.js.map +1 -0
  26. package/dist/env/utils/truncate.d.ts +71 -0
  27. package/dist/env/utils/truncate.d.ts.map +1 -0
  28. package/dist/env/utils/truncate.js +294 -0
  29. package/dist/env/utils/truncate.js.map +1 -0
  30. package/dist/errors.d.ts +15 -0
  31. package/dist/errors.d.ts.map +1 -0
  32. package/dist/errors.js +24 -0
  33. package/dist/errors.js.map +1 -0
  34. package/dist/harness/config.d.ts +20 -0
  35. package/dist/harness/config.d.ts.map +1 -0
  36. package/dist/harness/config.js +18 -0
  37. package/dist/harness/config.js.map +1 -0
  38. package/dist/harness/context.d.ts +27 -0
  39. package/dist/harness/context.d.ts.map +1 -0
  40. package/dist/harness/context.js +109 -0
  41. package/dist/harness/context.js.map +1 -0
  42. package/dist/harness/generation.d.ts +36 -0
  43. package/dist/harness/generation.d.ts.map +1 -0
  44. package/dist/harness/generation.js +266 -0
  45. package/dist/harness/generation.js.map +1 -0
  46. package/dist/harness/harness.d.ts +10 -0
  47. package/dist/harness/harness.d.ts.map +1 -0
  48. package/dist/harness/harness.js +266 -0
  49. package/dist/harness/harness.js.map +1 -0
  50. package/dist/harness/live.d.ts +40 -0
  51. package/dist/harness/live.d.ts.map +1 -0
  52. package/dist/harness/live.js +41 -0
  53. package/dist/harness/live.js.map +1 -0
  54. package/dist/harness/prompt.d.ts +24 -0
  55. package/dist/harness/prompt.d.ts.map +1 -0
  56. package/dist/harness/prompt.js +88 -0
  57. package/dist/harness/prompt.js.map +1 -0
  58. package/dist/harness/registry.d.ts +7 -0
  59. package/dist/harness/registry.d.ts.map +1 -0
  60. package/dist/harness/registry.js +309 -0
  61. package/dist/harness/registry.js.map +1 -0
  62. package/dist/harness/scheduler.d.ts +63 -0
  63. package/dist/harness/scheduler.d.ts.map +1 -0
  64. package/dist/harness/scheduler.js +605 -0
  65. package/dist/harness/scheduler.js.map +1 -0
  66. package/dist/harness/submissions.d.ts +24 -0
  67. package/dist/harness/submissions.d.ts.map +1 -0
  68. package/dist/harness/submissions.js +133 -0
  69. package/dist/harness/submissions.js.map +1 -0
  70. package/dist/harness/types.d.ts +358 -0
  71. package/dist/harness/types.d.ts.map +1 -0
  72. package/dist/harness/types.js +2 -0
  73. package/dist/harness/types.js.map +1 -0
  74. package/dist/harness/util.d.ts +14 -0
  75. package/dist/harness/util.d.ts.map +1 -0
  76. package/dist/harness/util.js +55 -0
  77. package/dist/harness/util.js.map +1 -0
  78. package/dist/ids.d.ts +6 -0
  79. package/dist/ids.d.ts.map +1 -0
  80. package/dist/ids.js +9 -0
  81. package/dist/ids.js.map +1 -0
  82. package/dist/index.d.ts +13 -2
  83. package/dist/index.d.ts.map +1 -1
  84. package/dist/index.js +11 -1
  85. package/dist/index.js.map +1 -1
  86. package/dist/session/forks.d.ts +10 -0
  87. package/dist/session/forks.d.ts.map +1 -0
  88. package/dist/session/forks.js +42 -0
  89. package/dist/session/forks.js.map +1 -0
  90. package/dist/session/observation.d.ts +28 -0
  91. package/dist/session/observation.d.ts.map +1 -0
  92. package/dist/session/observation.js +280 -0
  93. package/dist/session/observation.js.map +1 -0
  94. package/dist/session/session.d.ts +59 -0
  95. package/dist/session/session.d.ts.map +1 -0
  96. package/dist/session/session.js +380 -0
  97. package/dist/session/session.js.map +1 -0
  98. package/dist/session/transaction.d.ts +105 -0
  99. package/dist/session/transaction.d.ts.map +1 -0
  100. package/dist/session/transaction.js +718 -0
  101. package/dist/session/transaction.js.map +1 -0
  102. package/dist/storage/jsonl/index.d.ts +2 -0
  103. package/dist/storage/jsonl/index.d.ts.map +1 -0
  104. package/dist/storage/jsonl/index.js +2 -0
  105. package/dist/storage/jsonl/index.js.map +1 -0
  106. package/dist/storage/jsonl/node.d.ts +6 -0
  107. package/dist/storage/jsonl/node.d.ts.map +1 -0
  108. package/dist/storage/jsonl/node.js +8 -0
  109. package/dist/storage/jsonl/node.js.map +1 -0
  110. package/dist/storage/jsonl/storage.d.ts +115 -0
  111. package/dist/storage/jsonl/storage.d.ts.map +1 -0
  112. package/dist/storage/jsonl/storage.js +681 -0
  113. package/dist/storage/jsonl/storage.js.map +1 -0
  114. package/dist/storage/memory.d.ts +60 -0
  115. package/dist/storage/memory.d.ts.map +1 -0
  116. package/dist/storage/memory.js +681 -0
  117. package/dist/storage/memory.js.map +1 -0
  118. package/dist/storage/sqlite/database.d.ts +30 -0
  119. package/dist/storage/sqlite/database.d.ts.map +1 -0
  120. package/dist/storage/sqlite/database.js +2 -0
  121. package/dist/storage/sqlite/database.js.map +1 -0
  122. package/dist/storage/sqlite/index.d.ts +4 -0
  123. package/dist/storage/sqlite/index.d.ts.map +1 -0
  124. package/dist/storage/sqlite/index.js +3 -0
  125. package/dist/storage/sqlite/index.js.map +1 -0
  126. package/dist/storage/sqlite/migrations.d.ts +11 -0
  127. package/dist/storage/sqlite/migrations.d.ts.map +1 -0
  128. package/dist/storage/sqlite/migrations.js +111 -0
  129. package/dist/storage/sqlite/migrations.js.map +1 -0
  130. package/dist/storage/sqlite/node.d.ts +25 -0
  131. package/dist/storage/sqlite/node.d.ts.map +1 -0
  132. package/dist/storage/sqlite/node.js +97 -0
  133. package/dist/storage/sqlite/node.js.map +1 -0
  134. package/dist/storage/sqlite/storage.d.ts +51 -0
  135. package/dist/storage/sqlite/storage.d.ts.map +1 -0
  136. package/dist/storage/sqlite/storage.js +628 -0
  137. package/dist/storage/sqlite/storage.js.map +1 -0
  138. package/dist/tasks.d.ts +6 -0
  139. package/dist/tasks.d.ts.map +1 -0
  140. package/dist/tasks.js +5 -0
  141. package/dist/tasks.js.map +1 -0
  142. package/dist/testing/assertions.d.ts +5 -0
  143. package/dist/testing/assertions.d.ts.map +1 -0
  144. package/dist/testing/assertions.js +25 -0
  145. package/dist/testing/assertions.js.map +1 -0
  146. package/dist/testing/index.d.ts +6 -0
  147. package/dist/testing/index.d.ts.map +1 -0
  148. package/dist/testing/index.js +5 -0
  149. package/dist/testing/index.js.map +1 -0
  150. package/dist/testing/runner.d.ts +10 -0
  151. package/dist/testing/runner.d.ts.map +1 -0
  152. package/dist/testing/runner.js +14 -0
  153. package/dist/testing/runner.js.map +1 -0
  154. package/dist/testing/storage-benchmark.d.ts +41 -0
  155. package/dist/testing/storage-benchmark.d.ts.map +1 -0
  156. package/dist/testing/storage-benchmark.js +351 -0
  157. package/dist/testing/storage-benchmark.js.map +1 -0
  158. package/dist/testing/storage-conformance.d.ts +4 -0
  159. package/dist/testing/storage-conformance.d.ts.map +1 -0
  160. package/dist/testing/storage-conformance.js +1122 -0
  161. package/dist/testing/storage-conformance.js.map +1 -0
  162. package/dist/testing/types.d.ts +19 -0
  163. package/dist/testing/types.d.ts.map +1 -0
  164. package/dist/testing/types.js +2 -0
  165. package/dist/testing/types.js.map +1 -0
  166. package/dist/types.d.ts +525 -78
  167. package/dist/types.d.ts.map +1 -1
  168. package/dist/types.js.map +1 -1
  169. package/docs/chord-delta-findings.md +1 -1
  170. package/docs/pico-v5-chord-usage.md +90 -102
  171. package/docs/pico-v5-handoff.md +493 -253
  172. package/docs/pico-v5.md +1901 -848
  173. package/package.json +47 -5
  174. package/dist/memory-storage.d.ts +0 -37
  175. package/dist/memory-storage.d.ts.map +0 -1
  176. package/dist/memory-storage.js +0 -322
  177. package/dist/memory-storage.js.map +0 -1
package/dist/types.d.ts CHANGED
@@ -1,35 +1,215 @@
1
- import type { Context, JsonValue } from "@earendil-works/chord";
2
- import type { Message } from "@earendil-works/pi-ai";
3
- /** Session-global identifier shared by every durable record table. */
4
- export type Id = number;
5
- /** Monotonic sequence assigned to one atomic storage commit. */
6
- export type Seq = number;
1
+ import type { AttachedReplicatedState, Context, Draft, JsonValue } from "@earendil-works/chord";
2
+ import type { Op } from "@earendil-works/chord/delta";
3
+ import type { Message, Models } from "@earendil-works/pi-ai";
4
+ import type { ContextView, RegistrySnapshot } from "./harness/types.ts";
5
+ /** JSON object used as the root of every durable document. */
6
+ export type JsonObject = {
7
+ [key: string]: JsonValue;
8
+ };
9
+ declare const idBrand: unique symbol;
10
+ /** Erased nominal number identifying one durable record kind. */
11
+ export type Id<Kind extends string, Type = unknown> = number & {
12
+ readonly [idBrand]: {
13
+ readonly kind: Kind;
14
+ readonly type: Type;
15
+ };
16
+ };
17
+ export type ConversationId = Id<"conversation">;
18
+ export type EntryId = Id<"entry">;
19
+ export type TaskId<Result = unknown> = Id<"task", Result>;
20
+ export type SubmissionId = Id<"submission">;
21
+ export type DocumentId = Id<"document">;
22
+ declare const seqBrand: unique symbol;
23
+ /** Strictly increasing sequence assigned to one atomic storage commit; gaps are permitted. */
24
+ export type Seq = number & {
25
+ readonly [seqBrand]: "sequence";
26
+ };
7
27
  /** The root conversation always uses this reserved ID. */
8
- export declare const ROOT_CONVERSATION_ID: Id;
9
- /** JSON-safe error snapshot persisted instead of a runtime `Error` object. */
10
- export type StoredError = {
11
- readonly message: string;
12
- /** Optional structured diagnostic data for inspection or recovery. */
13
- readonly detail?: JsonValue;
28
+ export declare const ROOT_CONVERSATION_ID: ConversationId;
29
+ /** Conversation document that retains only its current state. */
30
+ export type LatestConversationSemantics = {
31
+ readonly scope: "conversation";
32
+ readonly history: "latest";
33
+ readonly fork: "current" | "initial";
34
+ };
35
+ /** Conversation document whose history remains addressable for as-of reads. */
36
+ export type RewindableConversationSemantics = {
37
+ readonly scope: "conversation";
38
+ readonly history: "rewindable";
39
+ readonly fork: "asOf" | "current" | "initial";
40
+ };
41
+ /** Ownership and lifetime of a document; only conversation documents declare history and fork behavior. */
42
+ export type DocumentSemantics = {
43
+ readonly scope: "session";
44
+ readonly history?: never;
45
+ readonly fork?: never;
46
+ } | LatestConversationSemantics | RewindableConversationSemantics | {
47
+ readonly scope: "task";
48
+ readonly history?: never;
49
+ readonly fork?: never;
50
+ };
51
+ /** Stored replay state supplied to a document's checkpoint predicate. */
52
+ export type CheckpointInfo = {
53
+ /** Deltas already stored after the newest base, excluding the change being evaluated. */
54
+ readonly deltasSinceBase: number;
55
+ };
56
+ /** Definition fields shared by singleton documents and document families. */
57
+ export type CommonDocDefinition<T extends JsonObject> = {
58
+ /** Stable persisted kind; part of the public protocol. */
59
+ readonly kind: string;
60
+ /** Positive integer version of the stored value shape. */
61
+ readonly version: number;
62
+ initial(): T;
63
+ migrate?(value: JsonObject, fromVersion: number): T;
64
+ /** Return true to store this ordinary change as a complete base instead of a delta. */
65
+ checkpointWhen?(value: Readonly<T>, ops: readonly Op[], info: CheckpointInfo): boolean;
66
+ };
67
+ /** Singleton document definition. */
68
+ export type DocDefinition<T extends JsonObject> = CommonDocDefinition<T> & DocumentSemantics;
69
+ /** Keyed document family definition; `initial(seed)` runs only when a member is absent. */
70
+ export type DocFamilyDefinition<T extends JsonObject, I extends JsonValue> = Omit<CommonDocDefinition<T>, "initial"> & DocumentSemantics & {
71
+ readonly family: true;
72
+ initial(seed: I): T;
73
+ };
74
+ declare const docType: unique symbol;
75
+ /** Typed singleton document token passed explicitly to typed access. */
76
+ export interface DocToken<T extends JsonObject, D extends DocDefinition<T>> {
77
+ readonly definition: D;
78
+ readonly [docType]?: T;
79
+ }
80
+ /** Typed document family token passed explicitly to typed access. */
81
+ export interface DocFamilyToken<T extends JsonObject, I extends JsonValue, D extends DocFamilyDefinition<T, I>> {
82
+ readonly definition: D;
83
+ readonly [docType]?: T;
84
+ }
85
+ export type SessionDocToken<T extends JsonObject> = DocToken<T, CommonDocDefinition<T> & {
86
+ readonly scope: "session";
87
+ }>;
88
+ export type ConversationDocToken<T extends JsonObject> = DocToken<T, CommonDocDefinition<T> & (LatestConversationSemantics | RewindableConversationSemantics)>;
89
+ export type RewindableConversationDocToken<T extends JsonObject> = DocToken<T, CommonDocDefinition<T> & RewindableConversationSemantics>;
90
+ export type TaskDocToken<T extends JsonObject> = DocToken<T, CommonDocDefinition<T> & {
91
+ readonly scope: "task";
92
+ }>;
93
+ export type SessionDocFamilyToken<T extends JsonObject, I extends JsonValue> = DocFamilyToken<T, I, DocFamilyDefinition<T, I> & {
94
+ readonly scope: "session";
95
+ }>;
96
+ export type ConversationDocFamilyToken<T extends JsonObject, I extends JsonValue> = DocFamilyToken<T, I, DocFamilyDefinition<T, I> & (LatestConversationSemantics | RewindableConversationSemantics)>;
97
+ export type RewindableConversationDocFamilyToken<T extends JsonObject, I extends JsonValue> = DocFamilyToken<T, I, DocFamilyDefinition<T, I> & RewindableConversationSemantics>;
98
+ export type TaskDocFamilyToken<T extends JsonObject, I extends JsonValue> = DocFamilyToken<T, I, DocFamilyDefinition<T, I> & {
99
+ readonly scope: "task";
100
+ }>;
101
+ /** Live task record reserved by one invocation. */
102
+ export type RunningTask<I, S, R> = TaskRecord<I, S, R> & {
103
+ readonly state: Extract<TaskState<S, R>, {
104
+ readonly status: "running";
105
+ }>;
106
+ };
107
+ /** Next state a task commits for itself: a replacement checkpoint or its terminal outcome. */
108
+ export type NextTaskState<S, R> = Extract<TaskState<S, R>, {
109
+ readonly status: "running" | "terminal";
110
+ }>;
111
+ /**
112
+ * Runs one checkpoint phase. It must commit a changed checkpoint or a terminal outcome through `runtime.commit()`;
113
+ * returning without durable progress faults the task.
114
+ */
115
+ export type PhaseHandler<I, P, S, R, H extends object> = (task: RunningTask<I, P, R>, runtime: TaskRuntime<I, S, R, H>, context: Context) => Promise<void>;
116
+ /**
117
+ * Operations of one task invocation. Every operation rejects after the invocation ends; watches acquired through it
118
+ * stop at invocation end. `_H` is the task's hook map, consumed once the runtime gains its hook runner.
119
+ */
120
+ export interface TaskRuntime<I, S, R, _H extends object> extends DocumentObserver, DocumentReader {
121
+ readonly taskId: TaskId<R>;
122
+ readonly conversationId: ConversationId;
123
+ /** Aborted when the run is signalled by `abortTask()` or the Harness closes. */
124
+ readonly signal: AbortSignal;
125
+ /** Registry snapshot of the current phase; refreshed at every phase boundary. */
126
+ readonly registry: RegistrySnapshot;
127
+ readonly models: Models;
128
+ /**
129
+ * Commit on the Session line after rereading the task. Rejects when the task is terminal, the invocation ended, the
130
+ * Harness is closing, or, in a run invocation, the task carries an abort mark. A returned state replaces the task's
131
+ * state in the same commit; returning nothing leaves it unchanged. `tx.createTask()` defaults to the task's
132
+ * conversation.
133
+ */
134
+ commit(change: (tx: Tx, current: RunningTask<I, S, R>) => NextTaskState<S, R> | undefined | Promise<NextTaskState<S, R> | undefined>, context: Context): Promise<void>;
135
+ /** Read a durable memo of this task. */
136
+ memo<T extends JsonValue>(name: string, context: Context): Promise<T | undefined>;
137
+ /** Store `candidate` unless a memo already exists; return the durable winner. */
138
+ memo<T extends JsonValue>(name: string, candidate: T, context: Context): Promise<T>;
139
+ /** Committed raw active transcript and model context, optionally cut off at the visible entry `at`. */
140
+ context(conversationId: ConversationId, context: Context, at?: EntryId): Promise<ContextView>;
141
+ /** The Harness clock. */
142
+ now(): number;
143
+ /** Forward a non-fatal failure to `HarnessOptions.onReport`. */
144
+ report(error: unknown): void;
145
+ /** Resolve once the Harness clock reaches `until`; rejects when the invocation or `context` is cancelled. */
146
+ sleep(until: number, context: Context): Promise<void>;
147
+ }
148
+ /** Executable durable state machine definition, registered in the registry by `name`. */
149
+ export type TaskDefinition<I, S extends {
150
+ phase: string;
151
+ }, R, H extends object> = {
152
+ /** Registered task kind persisted in `TaskRecord.kind`. */
153
+ readonly name: string;
154
+ /** Definition version persisted with live input and checkpoints. */
155
+ readonly version: number;
156
+ /** First durable checkpoint for a newly created task. */
157
+ initial(input: I): S;
158
+ /** Exhaustive phase map; each handler receives the task narrowed to its phase. */
159
+ readonly phases: {
160
+ readonly [P in S["phase"]]: PhaseHandler<I, Extract<S, {
161
+ phase: P;
162
+ }>, S, R, H>;
163
+ };
164
+ /** Runs in a fresh invocation after an abort mark and must commit a terminal outcome. */
165
+ abort(task: RunningTask<I, S, R>, runtime: TaskRuntime<I, S, R, H>, context: Context): Promise<void>;
166
+ /** Convert a record stored by any older supported version; runs at reservation. */
167
+ migrate?(input: JsonValue, checkpoint: JsonValue, fromVersion: number): {
168
+ input: I;
169
+ checkpoint: S;
170
+ };
171
+ readonly hooks?: H;
172
+ };
173
+ /** Typed executable task definition. */
174
+ export interface Task<I, S extends {
175
+ phase: string;
176
+ }, R, H extends object> {
177
+ readonly definition: TaskDefinition<I, S, R, H>;
178
+ }
179
+ /** Creation options for a durable task. */
180
+ export type TaskOptions = {
181
+ /** Owning conversation; required for Session commits that are not bound to a conversation. */
182
+ readonly conversationId?: ConversationId;
183
+ /** Tasks that must be terminal before ordinary execution may begin. */
184
+ readonly after?: readonly TaskId[];
185
+ /** Excluded from ordinary idle waits and conversation aborts. */
186
+ readonly background?: boolean;
187
+ };
188
+ /** Ownership selected explicitly whenever a conversation is created. */
189
+ export type ConversationOwnership = {
190
+ readonly kind: "ownerless";
191
+ } | {
192
+ readonly kind: "task";
193
+ readonly taskId: TaskId;
14
194
  };
15
195
  /** Immutable identity, history ancestry, and task ownership of a transcript scope. */
16
196
  export type ConversationRecord = {
17
- readonly id: Id;
197
+ readonly id: ConversationId;
18
198
  /** Fork source and inclusive parent entry through which history is inherited. */
19
199
  readonly parent?: {
20
- readonly conversationId: Id;
21
- readonly at: Id;
200
+ readonly conversationId: ConversationId;
201
+ readonly at: EntryId;
22
202
  };
23
- /** Creator edge used for authorization, subtree abort, and subtree idle waits. */
203
+ /** Creator edge used for attribution, subtree abort, and subtree idle waits. */
24
204
  readonly owner?: {
25
- readonly conversationId: Id;
26
- readonly taskId: Id;
205
+ readonly conversationId: ConversationId;
206
+ readonly taskId: TaskId;
27
207
  };
28
208
  };
29
209
  /** An immutable override of one visible entry's contribution to model context. */
30
210
  export type ContextEdit = {
31
211
  /** Entry whose model messages are omitted or replaced. */
32
- readonly target: Id;
212
+ readonly target: EntryId;
33
213
  } & ({
34
214
  readonly action: "omit";
35
215
  readonly messages?: never;
@@ -40,35 +220,54 @@ export type ContextEdit = {
40
220
  });
41
221
  /** Immutable transcript event with separate model-facing and application-facing payloads. */
42
222
  export type EntryRecord = {
43
- readonly id: Id;
44
- readonly conversationId: Id;
223
+ readonly id: EntryId;
224
+ readonly conversationId: ConversationId;
45
225
  /** Application-defined entry discriminator. */
46
226
  readonly kind: string;
47
227
  /** Messages contributed to model context; absent for display or bookkeeping entries. */
48
228
  readonly model?: readonly Message[];
49
- /** JSON payload consumed by views, plugins, or bookkeeping logic. */
229
+ /** JSON payload consumed by views, extensions, or bookkeeping logic. */
50
230
  readonly data?: JsonValue;
51
231
  /** First entry in the active context selected by this entry. */
52
- readonly head?: Id;
232
+ readonly head?: EntryId;
53
233
  /** Context-only overrides of earlier visible entries. */
54
234
  readonly edits?: readonly ContextEdit[];
55
235
  /** Task that appended this entry, when it was produced by durable work. */
56
- readonly byTaskId?: Id;
236
+ readonly byTaskId?: TaskId;
57
237
  };
58
238
  /** Entry content supplied before the Session assigns identity and task attribution. */
59
239
  export type EntryDraft = Omit<EntryRecord, "id" | "conversationId" | "byTaskId" | "head"> & {
60
240
  /** `"self"` starts active context at the newly assigned entry ID. */
61
- readonly head?: Id | "self";
241
+ readonly head?: EntryId | "self";
62
242
  };
63
- /** Identity fields shared by every input lifecycle state. */
64
- type InputBase = {
65
- readonly id: Id;
66
- readonly conversationId: Id;
243
+ /** Entry whose `data` has type `D`; `never` means the kind carries no data. */
244
+ export type TypedEntry<D extends JsonValue> = Omit<EntryRecord, "data"> & ([D] extends [never] ? {
245
+ readonly data?: never;
246
+ } : {
247
+ readonly data: D;
248
+ });
249
+ /** Entry content of a typed kind; the token supplies `kind`. */
250
+ export type TypedEntryDraft<D extends JsonValue> = Omit<EntryDraft, "kind" | "data"> & ([D] extends [never] ? {
251
+ readonly data?: never;
252
+ } : {
253
+ readonly data: D;
254
+ });
255
+ /** Typed entry kind with a narrowing guard. */
256
+ export interface Entry<D extends JsonValue = never> {
257
+ readonly kind: string;
258
+ is(entry: EntryRecord | undefined): entry is TypedEntry<D>;
259
+ }
260
+ /** Identity fields shared by every durable submission state. */
261
+ type SubmissionRecordBase = {
262
+ readonly id: SubmissionId;
263
+ readonly conversationId: ConversationId;
67
264
  /** Host-provided deduplication key, scoped to the conversation. */
68
265
  readonly requestId?: string;
69
266
  };
70
- /** Durable lifecycle of one admitted host input and the handle waiting for it. */
71
- export type Input = InputBase & ({
267
+ /** Durable lifecycle of one admitted user input or passive entry write. */
268
+ export type SubmissionRecord = (SubmissionRecordBase & {
269
+ readonly type: "input";
270
+ } & ({
72
271
  /** Admitted but not yet represented in the transcript. */
73
272
  readonly status: "queued";
74
273
  readonly entry?: never;
@@ -76,33 +275,67 @@ export type Input = InputBase & ({
76
275
  readonly reason?: never;
77
276
  readonly detail?: never;
78
277
  } | {
79
- /** Added to the transcript and owned by an active turn. */
278
+ /** Added to the transcript and owned by an active run. */
80
279
  readonly status: "placed";
81
- /** User transcript entry created when this input was placed. */
82
- readonly entry: Id;
280
+ readonly entry: EntryId;
83
281
  readonly answer?: never;
84
282
  readonly reason?: never;
85
283
  readonly detail?: never;
86
284
  } | {
87
- /** Successfully settled with a durable transcript result. */
285
+ /** Successfully answered user input. */
88
286
  readonly status: "done";
89
- /** User or passive-write transcript entry created when this input was placed. */
90
- readonly entry: Id;
91
- /** Assistant answer entry; absent for a completed passive write. */
92
- readonly answer?: Id;
287
+ readonly entry: EntryId;
288
+ readonly answer: EntryId;
93
289
  readonly reason?: never;
94
290
  readonly detail?: never;
95
291
  } | {
96
292
  /** Terminal input that can no longer receive an answer. */
97
293
  readonly status: "unanswered";
98
- /** Present when the input was placed before becoming unanswered. */
99
- readonly entry?: Id;
294
+ readonly entry?: EntryId;
100
295
  readonly answer?: never;
101
- /** Stable machine-readable explanation such as `"aborted"` or `"stale"`. */
102
296
  readonly reason: string;
103
- /** Optional structured diagnostic data. */
104
297
  readonly detail?: JsonValue;
105
- });
298
+ })) | (SubmissionRecordBase & {
299
+ readonly type: "write";
300
+ } & ({
301
+ /** Admitted but not yet appended to the transcript. */
302
+ readonly status: "queued";
303
+ readonly entry?: never;
304
+ readonly answer?: never;
305
+ readonly reason?: never;
306
+ readonly detail?: never;
307
+ } | {
308
+ /** Successfully appended passive entry. */
309
+ readonly status: "done";
310
+ readonly entry: EntryId;
311
+ readonly answer?: never;
312
+ readonly reason?: never;
313
+ readonly detail?: never;
314
+ } | {
315
+ /** Terminal passive write that could not be placed. */
316
+ readonly status: "unanswered";
317
+ readonly entry?: never;
318
+ readonly answer?: never;
319
+ readonly reason: string;
320
+ readonly detail?: JsonValue;
321
+ }));
322
+ /** Terminal status staged for a submission; identity, type, and entry come from its current record. */
323
+ export type SubmissionSettlement = {
324
+ readonly status: "done";
325
+ readonly answer: EntryId;
326
+ } | {
327
+ readonly status: "unanswered";
328
+ readonly reason: string;
329
+ readonly detail?: JsonValue;
330
+ };
331
+ /** Submission fields supplied before the Session assigns an ID. */
332
+ export type SubmissionCreate = SubmissionRecord extends infer Record ? Record extends SubmissionRecord ? Omit<Record, "id"> : never : never;
333
+ /** JSON-safe error snapshot persisted instead of a runtime `Error` object. */
334
+ export type TaskOutcomeError = {
335
+ readonly message: string;
336
+ /** Optional structured diagnostic data for inspection or recovery. */
337
+ readonly detail?: JsonValue;
338
+ };
106
339
  /** Durable reason and optional result recorded when a task becomes terminal. */
107
340
  export type TaskOutcome<R> = {
108
341
  readonly status: "completed";
@@ -113,7 +346,7 @@ export type TaskOutcome<R> = {
113
346
  /** Expected task or domain failure explicitly committed by its implementation. */
114
347
  | {
115
348
  readonly status: "failed";
116
- readonly error: StoredError;
349
+ readonly error: TaskOutcomeError;
117
350
  readonly result?: R;
118
351
  readonly reason?: never;
119
352
  }
@@ -134,7 +367,7 @@ export type TaskOutcome<R> = {
134
367
  /** Runtime-detected contract failure, such as an uncaught throw or no durable progress. */
135
368
  | {
136
369
  readonly status: "faulted";
137
- readonly error: StoredError;
370
+ readonly error: TaskOutcomeError;
138
371
  readonly result?: never;
139
372
  readonly reason?: never;
140
373
  };
@@ -158,9 +391,9 @@ export type TaskState<S, R> = {
158
391
  readonly outcome: TaskOutcome<R>;
159
392
  };
160
393
  /** Identity, definition, and scheduling fields shared by every task state. */
161
- type TaskRecordBase<I> = {
162
- readonly id: Id;
163
- readonly conversationId: Id;
394
+ type TaskRecordBase<I, R> = {
395
+ readonly id: TaskId<R>;
396
+ readonly conversationId: ConversationId;
164
397
  /** Registered task definition name. */
165
398
  readonly kind: string;
166
399
  /** Definition version used to migrate live input and checkpoints. */
@@ -168,14 +401,14 @@ type TaskRecordBase<I> = {
168
401
  /** Original task input retained while the task is live or terminal. */
169
402
  readonly input: I;
170
403
  /** Tasks that must be terminal before ordinary execution may begin. */
171
- readonly after: readonly Id[];
404
+ readonly after: readonly TaskId[];
172
405
  /** Whether this task is excluded from ordinary idle waits and conversation aborts. */
173
406
  readonly background: boolean;
174
407
  /** Durable abort mark checked before run-mode progress is committed. */
175
408
  readonly abortRequested: boolean;
176
409
  };
177
410
  /** Complete replacement record for one durable task state machine. */
178
- export type TaskRecord<I, S, R> = TaskRecordBase<I> & ({
411
+ export type TaskRecord<I, S, R> = TaskRecordBase<I, R> & ({
179
412
  readonly state: Extract<TaskState<S, R>, {
180
413
  readonly status: "pending" | "running";
181
414
  }>;
@@ -190,8 +423,8 @@ export type TaskRecord<I, S, R> = TaskRecordBase<I> & ({
190
423
  /** Persisted lifecycle record for one create-to-retire document incarnation. */
191
424
  export type DocumentRecord = {
192
425
  /** Unique incarnation ID; never reused when the same logical document is recreated. */
193
- readonly id: Id;
194
- /** Registered document kind. */
426
+ readonly id: DocumentId;
427
+ /** Stable document definition kind. */
195
428
  readonly kind: string;
196
429
  /** Family member key; absent for singleton documents. */
197
430
  readonly key?: string;
@@ -208,7 +441,7 @@ export type DocumentRecord = {
208
441
  } | ({
209
442
  readonly scope: {
210
443
  readonly kind: "conversation";
211
- readonly conversationId: Id;
444
+ readonly conversationId: ConversationId;
212
445
  };
213
446
  } & ({
214
447
  /** Retain only current state. */
@@ -223,7 +456,7 @@ export type DocumentRecord = {
223
456
  })) | {
224
457
  readonly scope: {
225
458
  readonly kind: "task";
226
- readonly taskId: Id;
459
+ readonly taskId: TaskId;
227
460
  };
228
461
  readonly history?: never;
229
462
  readonly fork?: never;
@@ -237,23 +470,71 @@ export type Page<T, C> = {
237
470
  };
238
471
  /** Backend-owned JSON continuation state that callers only round-trip to the same scan. */
239
472
  export type Cursor = Readonly<Record<string, JsonValue>>;
473
+ /** Optional filters for an ordered conversation scan. */
474
+ export type ConversationQuery = {
475
+ readonly ownerConversationId?: ConversationId;
476
+ readonly ownerTaskId?: TaskId;
477
+ };
240
478
  /** Inclusive ID bounds for a newest-first scan of one conversation's fork-aware history. */
241
479
  export type EntryQuery = {
242
- readonly conversationId: Id;
480
+ readonly conversationId: ConversationId;
243
481
  /** Oldest entry ID that may be returned. */
244
- readonly minEntryId?: Id;
482
+ readonly minEntryId?: EntryId;
245
483
  /** Newest entry ID that may be returned. */
246
- readonly maxEntryId?: Id;
484
+ readonly maxEntryId?: EntryId;
247
485
  };
248
486
  /** Optional filters for an ordered scan of durable task records. */
249
487
  export type TaskQuery = {
250
- readonly conversationId?: Id;
488
+ readonly conversationId?: ConversationId;
251
489
  readonly kind?: string;
252
490
  readonly status?: "pending" | "running" | "terminal";
253
491
  readonly abortRequested?: boolean;
254
492
  readonly background?: boolean;
255
493
  };
256
- /** One table mutation in an atomic storage commit; task and input writes replace whole records. */
494
+ /** Optional filters for an ordered scan of submission records. */
495
+ export type SubmissionQuery = {
496
+ readonly conversationId?: ConversationId;
497
+ readonly status?: SubmissionRecord["status"];
498
+ };
499
+ /** Current state or one historical commit sequence used for document membership and content reads. */
500
+ export type DocumentPoint = Seq | "current";
501
+ /** Exact logical identity of a singleton or one keyed family member. */
502
+ export type DocumentAddress = {
503
+ readonly kind: string;
504
+ readonly scope: DocumentRecord["scope"];
505
+ /** Absent selects the singleton; present selects one family member. */
506
+ readonly key?: string;
507
+ };
508
+ /** Ordered scan of document incarnations alive in one exact scope at one point. */
509
+ export type DocumentQuery = {
510
+ readonly scope: DocumentRecord["scope"];
511
+ readonly at: DocumentPoint;
512
+ readonly kind?: string;
513
+ };
514
+ /** Complete checkpoint or Chord operation batch selected by the owning Session. */
515
+ export type DocumentContent = {
516
+ readonly version: number;
517
+ readonly kind: "base";
518
+ readonly value: JsonObject;
519
+ } | {
520
+ readonly version: number;
521
+ readonly kind: "delta";
522
+ readonly ops: readonly Op[];
523
+ };
524
+ /** Exact persisted source selected for a definition-free document copy. */
525
+ export type DocumentCopySource = {
526
+ readonly id: DocumentId;
527
+ readonly at: DocumentPoint;
528
+ };
529
+ /** Detached materialized value and stored definition version at a selected point. */
530
+ export type StoredDocument = {
531
+ readonly record: DocumentRecord;
532
+ readonly version: number;
533
+ readonly value: JsonObject;
534
+ /** Deltas replayed after the selected base to materialize `value`. */
535
+ readonly deltasSinceBase: number;
536
+ };
537
+ /** One record or document mutation in an atomic storage commit. */
257
538
  export type StorageWrite = {
258
539
  readonly type: "conversation";
259
540
  readonly value: ConversationRecord;
@@ -264,27 +545,185 @@ export type StorageWrite = {
264
545
  readonly type: "task";
265
546
  readonly value: TaskRecord<JsonValue, JsonValue, JsonValue>;
266
547
  } | {
267
- readonly type: "input";
268
- readonly value: Input;
548
+ readonly type: "submission";
549
+ readonly value: SubmissionRecord;
550
+ } | {
551
+ readonly type: "document.create";
552
+ readonly record: DocumentCreate;
553
+ readonly content: Extract<DocumentContent, {
554
+ readonly kind: "base";
555
+ }>;
556
+ } | {
557
+ readonly type: "document.copy";
558
+ readonly record: DocumentCreate;
559
+ readonly source: DocumentCopySource;
560
+ } | {
561
+ readonly type: "document.change";
562
+ readonly id: DocumentId;
563
+ readonly content: DocumentContent;
564
+ } | {
565
+ readonly type: "document.retire";
566
+ readonly id: DocumentId;
567
+ };
568
+ /** Committed change of one document incarnation. */
569
+ export type DocumentCommitChange = {
570
+ readonly type: "document";
571
+ readonly record: DocumentRecord;
572
+ /** Conversation owning the document; task documents derive it from their task record. Undefined only for Session documents. */
573
+ readonly conversationId: ConversationId | undefined;
574
+ /** Definition version of `value`; absent when this commit retired the incarnation. */
575
+ readonly version: number | undefined;
576
+ /** Exact adopted immutable revision, or `null` when this commit retired the incarnation. */
577
+ readonly value: JsonObject | null;
578
+ /** Exact adopted operations for an ordinary update; empty for creation and retirement. */
579
+ readonly ops: readonly Op[];
580
+ } | {
581
+ /** Definition-free child initialization; consumers hydrate through state or watch acquisition. */
582
+ readonly type: "document.copy";
583
+ readonly record: DocumentRecord;
584
+ readonly conversationId: ConversationId;
585
+ readonly source: DocumentCopySource;
586
+ };
587
+ /** Complete table record committed without another publication copy. */
588
+ export type TableCommitChange = Extract<StorageWrite, {
589
+ readonly type: "conversation" | "entry" | "task" | "submission";
590
+ }>;
591
+ export type CommitChange = TableCommitChange | DocumentCommitChange;
592
+ /** Every immutable change from one successful Session commit. Change order is unspecified. */
593
+ export type CommitPublication = {
594
+ readonly seq: Seq;
595
+ readonly changes: readonly CommitChange[];
269
596
  };
597
+ /**
598
+ * Transaction surface of one Session commit callback.
599
+ * Table reads and creation results are trusted immutable values and may be shared with internal commit state.
600
+ */
601
+ export interface Tx {
602
+ conversation(id: ConversationId): Promise<ConversationRecord | undefined>;
603
+ entry(id: EntryId): Promise<EntryRecord | undefined>;
604
+ /** Undefined when the entry is absent or has another kind. */
605
+ entry<D extends JsonValue>(token: Entry<D>, id: EntryId): Promise<TypedEntry<D> | undefined>;
606
+ task(id: TaskId): Promise<TaskRecord<JsonValue, JsonValue, JsonValue> | undefined>;
607
+ scanConversations(query: ConversationQuery, limit: number, cursor?: Cursor): Promise<Page<ConversationRecord, Cursor>>;
608
+ scanEntries(query: EntryQuery, limit: number, cursor?: Cursor): Promise<Page<EntryRecord, Cursor>>;
609
+ scanTasks(query: TaskQuery, limit: number, cursor?: Cursor): Promise<Page<TaskRecord<JsonValue, JsonValue, JsonValue>, Cursor>>;
610
+ /** Create a conversation with explicitly selected ownership. */
611
+ createConversation(options: {
612
+ readonly ownership: ConversationOwnership;
613
+ }): Promise<ConversationRecord>;
614
+ /** Create a history fork at one concrete visible entry with explicitly selected ownership. */
615
+ forkConversation(parentConversationId: ConversationId, at: EntryId, options: {
616
+ readonly ownership: ConversationOwnership;
617
+ }): Promise<ConversationRecord>;
618
+ /** Returned records are Session-owned immutable values and may be shared with commit listeners. */
619
+ appendEntry(conversationId: ConversationId, value: EntryDraft): Promise<EntryRecord>;
620
+ /** The token supplies `kind` and types `data`. */
621
+ appendEntry<D extends JsonValue>(token: Entry<D>, conversationId: ConversationId, value: TypedEntryDraft<NoInfer<D>>): Promise<TypedEntry<D>>;
622
+ createTask<I, S extends {
623
+ phase: string;
624
+ }, R, H extends object>(task: Task<I, S, R, H>, input: I, options?: TaskOptions): Promise<TaskId<R>>;
625
+ /**
626
+ * Settle a queued or placed submission; only a placed input can be answered, and a settled submission stays
627
+ * unchanged. Resolved against this transaction's latest record of the submission, so it works after table writes.
628
+ * Run tasks settle the inputs they answer.
629
+ */
630
+ settleSubmission(id: SubmissionId, settlement: SubmissionSettlement): void;
631
+ doc<T extends JsonObject>(token: SessionDocToken<T>): Promise<Draft<T>>;
632
+ doc<T extends JsonObject>(token: ConversationDocToken<T>, conversationId: ConversationId): Promise<Draft<T>>;
633
+ doc<T extends JsonObject>(token: TaskDocToken<T>, taskId: TaskId): Promise<Draft<T>>;
634
+ doc<T extends JsonObject, I extends JsonValue>(token: SessionDocFamilyToken<T, I>, key: string, seed: I): Promise<Draft<T>>;
635
+ doc<T extends JsonObject, I extends JsonValue>(token: ConversationDocFamilyToken<T, I>, conversationId: ConversationId, key: string, seed: I): Promise<Draft<T>>;
636
+ doc<T extends JsonObject, I extends JsonValue>(token: TaskDocFamilyToken<T, I>, taskId: TaskId, key: string, seed: I): Promise<Draft<T>>;
637
+ retireDoc<T extends JsonObject>(token: SessionDocToken<T>): Promise<void>;
638
+ retireDoc<T extends JsonObject>(token: ConversationDocToken<T>, conversationId: ConversationId): Promise<void>;
639
+ retireDoc<T extends JsonObject>(token: TaskDocToken<T>, taskId: TaskId): Promise<void>;
640
+ retireDoc<T extends JsonObject, I extends JsonValue>(token: SessionDocFamilyToken<T, I>, key: string): Promise<void>;
641
+ retireDoc<T extends JsonObject, I extends JsonValue>(token: ConversationDocFamilyToken<T, I>, conversationId: ConversationId, key: string): Promise<void>;
642
+ retireDoc<T extends JsonObject, I extends JsonValue>(token: TaskDocFamilyToken<T, I>, taskId: TaskId, key: string): Promise<void>;
643
+ }
644
+ /** Disposable, read-only Chord state bound to one committed document incarnation. */
645
+ export type DocumentState<T extends JsonObject> = AttachedReplicatedState<Readonly<T> | null>;
646
+ /** Terminal result of one document watch. */
647
+ export type WatchEnd = {
648
+ readonly reason: "stopped" | "cancelled" | "session_closed" | "retired";
649
+ } | {
650
+ readonly reason: "listener_error";
651
+ readonly error: Error;
652
+ };
653
+ /** Serialized exact-frame observation of an immutable value with bounded pending delivery. */
654
+ export interface WatchHandle<T> {
655
+ /** Acquisition revision before start; latest delivered immutable revision afterward. */
656
+ readonly value: T;
657
+ /** Install the sole asynchronous listener. Never invokes it inline. */
658
+ start(listener: (value: T, ops: readonly Op[], context: Context) => Promise<void>): void;
659
+ /** Idempotently stop future callbacks and return this watch's terminal result. */
660
+ stop(): Promise<WatchEnd>;
661
+ /** Settle when the watch terminates; an already-running callback remains caller-owned. */
662
+ readonly closed: Promise<WatchEnd>;
663
+ }
664
+ export type DocumentWatch<T extends JsonObject> = WatchHandle<Readonly<T> | null>;
665
+ /** Committed document reads. */
666
+ export type DocumentReader = Pick<Session, "snapshot" | "snapshotAsOf">;
667
+ /** Non-creating document watch acquisition shared by Session and later invocation APIs. */
668
+ export interface DocumentObserver {
669
+ watchDoc<T extends JsonObject>(token: SessionDocToken<T>, context: Context): Promise<DocumentWatch<T> | undefined>;
670
+ watchDoc<T extends JsonObject>(token: ConversationDocToken<T>, conversationId: ConversationId, context: Context): Promise<DocumentWatch<T> | undefined>;
671
+ watchDoc<T extends JsonObject>(token: TaskDocToken<T>, taskId: TaskId, context: Context): Promise<DocumentWatch<T> | undefined>;
672
+ watchDoc<T extends JsonObject, I extends JsonValue>(token: SessionDocFamilyToken<T, I>, key: string, context: Context): Promise<DocumentWatch<T> | undefined>;
673
+ watchDoc<T extends JsonObject, I extends JsonValue>(token: ConversationDocFamilyToken<T, I>, conversationId: ConversationId, key: string, context: Context): Promise<DocumentWatch<T> | undefined>;
674
+ watchDoc<T extends JsonObject, I extends JsonValue>(token: TaskDocFamilyToken<T, I>, taskId: TaskId, key: string, context: Context): Promise<DocumentWatch<T> | undefined>;
675
+ }
676
+ /** Owner of one mutation line, its records, and its tracked documents. */
677
+ export interface Session extends DocumentObserver {
678
+ /** Run one atomic transaction on the Session mutation line. */
679
+ commit<T>(change: (tx: Tx) => T | Promise<T>, context: Context): Promise<T>;
680
+ /** Seal admission, settle admitted commits, then close storage. */
681
+ close(context: Context): Promise<void>;
682
+ /** Observe complete commits synchronously after adoption. The listener must not throw, block, or call Session APIs. */
683
+ subscribeCommits(listener: (publication: CommitPublication, context: Context) => void): () => void;
684
+ /** Observe close synchronously when it begins. The listener must not throw, block, or call Session APIs. */
685
+ subscribeClose(listener: () => void): () => void;
686
+ snapshot<T extends JsonObject>(token: SessionDocToken<T>, context: Context): Promise<Readonly<T> | undefined>;
687
+ snapshot<T extends JsonObject>(token: ConversationDocToken<T>, conversationId: ConversationId, context: Context): Promise<Readonly<T> | undefined>;
688
+ snapshot<T extends JsonObject>(token: TaskDocToken<T>, taskId: TaskId, context: Context): Promise<Readonly<T> | undefined>;
689
+ snapshot<T extends JsonObject, I extends JsonValue>(token: SessionDocFamilyToken<T, I>, key: string, context: Context): Promise<Readonly<T> | undefined>;
690
+ snapshot<T extends JsonObject, I extends JsonValue>(token: ConversationDocFamilyToken<T, I>, conversationId: ConversationId, key: string, context: Context): Promise<Readonly<T> | undefined>;
691
+ snapshot<T extends JsonObject, I extends JsonValue>(token: TaskDocFamilyToken<T, I>, taskId: TaskId, key: string, context: Context): Promise<Readonly<T> | undefined>;
692
+ documentState<T extends JsonObject>(token: SessionDocToken<T>, context: Context): Promise<DocumentState<T> | undefined>;
693
+ documentState<T extends JsonObject>(token: ConversationDocToken<T>, conversationId: ConversationId, context: Context): Promise<DocumentState<T> | undefined>;
694
+ documentState<T extends JsonObject>(token: TaskDocToken<T>, taskId: TaskId, context: Context): Promise<DocumentState<T> | undefined>;
695
+ documentState<T extends JsonObject, I extends JsonValue>(token: SessionDocFamilyToken<T, I>, key: string, context: Context): Promise<DocumentState<T> | undefined>;
696
+ documentState<T extends JsonObject, I extends JsonValue>(token: ConversationDocFamilyToken<T, I>, conversationId: ConversationId, key: string, context: Context): Promise<DocumentState<T> | undefined>;
697
+ documentState<T extends JsonObject, I extends JsonValue>(token: TaskDocFamilyToken<T, I>, taskId: TaskId, key: string, context: Context): Promise<DocumentState<T> | undefined>;
698
+ snapshotAsOf<T extends JsonObject>(token: RewindableConversationDocToken<T>, conversationId: ConversationId, at: EntryId, context: Context): Promise<Readonly<T> | undefined>;
699
+ snapshotAsOf<T extends JsonObject, I extends JsonValue>(token: RewindableConversationDocFamilyToken<T, I>, conversationId: ConversationId, key: string, at: EntryId, context: Context): Promise<Readonly<T> | undefined>;
700
+ }
270
701
  /**
271
702
  * Atomic persistence boundary for Session records.
272
703
  *
273
704
  * Storage trusts the owning Session to supply semantically valid records, references,
274
705
  * ancestry, and transitions. Implementations enforce atomicity, global ID ownership,
275
- * immutable conversation/entry creation, and detached values; Session serializes commits.
706
+ * immutable conversation/entry creation, document record consistency, and detached values;
707
+ * Session serializes commits.
276
708
  */
277
709
  export interface Storage {
278
- /** Atomically persist one batch and return the sequence assigned to that commit. */
710
+ /**
711
+ * Atomically persist one batch and return its sequence. Once resolved, later reads through this storage observe it.
712
+ */
279
713
  commit(writes: readonly StorageWrite[], context: Context): Promise<Seq>;
280
- /** Return a fresh candidate from the Session-global record ID namespace. */
281
- mintId(): Id;
714
+ /** Return a fresh branded candidate from the Session-global numeric ID namespace. */
715
+ mintId<I extends Id<string>>(): Promise<I>;
282
716
  /** Look up one conversation by exact ID. */
283
- conversation(id: Id, context: Context): Promise<ConversationRecord | undefined>;
717
+ conversation(id: ConversationId, context: Context): Promise<ConversationRecord | undefined>;
284
718
  /** Scan conversations in ascending ID order. */
285
- scanConversations(cursor: Cursor | undefined, limit: number, context: Context): Promise<Page<ConversationRecord, Cursor>>;
719
+ scanConversations(query: ConversationQuery, limit: number, cursor: Cursor | undefined, context: Context): Promise<Page<ConversationRecord, Cursor>>;
286
720
  /** Look up one global entry and the sequence of the commit that persisted it. */
287
- entry(id: Id, context: Context): Promise<{
721
+ entry(id: EntryId, context: Context): Promise<{
722
+ readonly entry: EntryRecord;
723
+ readonly commitSeq: Seq;
724
+ } | undefined>;
725
+ /** Look up one entry only when it is visible through the requested conversation's ancestry. */
726
+ entry(conversationId: ConversationId, id: EntryId, context: Context): Promise<{
288
727
  readonly entry: EntryRecord;
289
728
  readonly commitSeq: Seq;
290
729
  } | undefined>;
@@ -292,19 +731,27 @@ export interface Storage {
292
731
  * Return the newest visible entry with a `head` at or below the optional inclusive cutoff.
293
732
  * The returned entry is the marker; its `head` value is the range's actual lower bound.
294
733
  */
295
- findLatestHeadMarker(conversationId: Id, atOrBeforeEntryId: Id | undefined, context: Context): Promise<(EntryRecord & {
296
- readonly head: Id;
734
+ findLatestHeadMarker(conversationId: ConversationId, atOrBeforeEntryId: EntryId | undefined, context: Context): Promise<(EntryRecord & {
735
+ readonly head: EntryId;
297
736
  }) | undefined>;
298
737
  /** Scan the inclusive visible range newest-first, returning at most `limit` entries. */
299
- scanEntries(query: EntryQuery, cursor: Cursor | undefined, limit: number, context: Context): Promise<Page<EntryRecord, Cursor>>;
738
+ scanEntries(query: EntryQuery, limit: number, cursor: Cursor | undefined, context: Context): Promise<Page<EntryRecord, Cursor>>;
300
739
  /** Look up the latest complete record for one task. */
301
- task(id: Id, context: Context): Promise<TaskRecord<JsonValue, JsonValue, JsonValue> | undefined>;
740
+ task(id: TaskId, context: Context): Promise<TaskRecord<JsonValue, JsonValue, JsonValue> | undefined>;
302
741
  /** Scan task records matching every supplied filter. */
303
- scanTasks(query: TaskQuery, cursor: Cursor | undefined, limit: number, context: Context): Promise<Page<TaskRecord<JsonValue, JsonValue, JsonValue>, Cursor>>;
304
- /** Look up the latest complete record for one admitted input. */
305
- input(id: Id, context: Context): Promise<Input | undefined>;
306
- /** Find an input by its conversation-scoped host deduplication key. */
307
- inputByRequest(conversationId: Id, requestId: string, context: Context): Promise<Input | undefined>;
742
+ scanTasks(query: TaskQuery, limit: number, cursor: Cursor | undefined, context: Context): Promise<Page<TaskRecord<JsonValue, JsonValue, JsonValue>, Cursor>>;
743
+ /** Look up the latest complete record for one admitted submission. */
744
+ submission(id: SubmissionId, context: Context): Promise<SubmissionRecord | undefined>;
745
+ /** Scan submissions matching every supplied filter in ascending ID order. */
746
+ scanSubmissions(query: SubmissionQuery, limit: number, cursor: Cursor | undefined, context: Context): Promise<Page<SubmissionRecord, Cursor>>;
747
+ /** Find a submission by its conversation-scoped host deduplication key. */
748
+ submissionByRequest(conversationId: ConversationId, requestId: string, context: Context): Promise<SubmissionRecord | undefined>;
749
+ /** Resolve the incarnation occupying one exact logical address at the selected point. */
750
+ findDocument(address: DocumentAddress, at: DocumentPoint, context: Context): Promise<DocumentRecord | undefined>;
751
+ /** Materialize one specific incarnation by ID at the selected point without following a replacement at its address. */
752
+ document(id: DocumentId, at: DocumentPoint, context: Context): Promise<StoredDocument | undefined>;
753
+ /** Scan incarnations alive in one exact scope at the selected point. */
754
+ scanDocuments(query: DocumentQuery, limit: number, cursor: Cursor | undefined, context: Context): Promise<Page<DocumentRecord, Cursor>>;
308
755
  /** Release backend resources; all later operations must reject. */
309
756
  close(context: Context): Promise<void>;
310
757
  }