@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.
- package/README.md +72 -1
- package/dist/documents.d.ts +66 -0
- package/dist/documents.d.ts.map +1 -0
- package/dist/documents.js +98 -0
- package/dist/documents.js.map +1 -0
- package/dist/entries.d.ts +13 -0
- package/dist/entries.d.ts.map +1 -0
- package/dist/entries.js +18 -0
- package/dist/entries.js.map +1 -0
- package/dist/env/index.d.ts +131 -0
- package/dist/env/index.d.ts.map +1 -0
- package/dist/env/index.js +45 -0
- package/dist/env/index.js.map +1 -0
- package/dist/env/node.d.ts +45 -0
- package/dist/env/node.d.ts.map +1 -0
- package/dist/env/node.js +908 -0
- package/dist/env/node.js.map +1 -0
- package/dist/env/utils/adaptive-publisher.d.ts +24 -0
- package/dist/env/utils/adaptive-publisher.d.ts.map +1 -0
- package/dist/env/utils/adaptive-publisher.js +79 -0
- package/dist/env/utils/adaptive-publisher.js.map +1 -0
- package/dist/env/utils/output-capture.d.ts +31 -0
- package/dist/env/utils/output-capture.d.ts.map +1 -0
- package/dist/env/utils/output-capture.js +238 -0
- package/dist/env/utils/output-capture.js.map +1 -0
- package/dist/env/utils/truncate.d.ts +71 -0
- package/dist/env/utils/truncate.d.ts.map +1 -0
- package/dist/env/utils/truncate.js +294 -0
- package/dist/env/utils/truncate.js.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +24 -0
- package/dist/errors.js.map +1 -0
- package/dist/harness/config.d.ts +20 -0
- package/dist/harness/config.d.ts.map +1 -0
- package/dist/harness/config.js +18 -0
- package/dist/harness/config.js.map +1 -0
- package/dist/harness/context.d.ts +27 -0
- package/dist/harness/context.d.ts.map +1 -0
- package/dist/harness/context.js +109 -0
- package/dist/harness/context.js.map +1 -0
- package/dist/harness/generation.d.ts +36 -0
- package/dist/harness/generation.d.ts.map +1 -0
- package/dist/harness/generation.js +266 -0
- package/dist/harness/generation.js.map +1 -0
- package/dist/harness/harness.d.ts +10 -0
- package/dist/harness/harness.d.ts.map +1 -0
- package/dist/harness/harness.js +266 -0
- package/dist/harness/harness.js.map +1 -0
- package/dist/harness/live.d.ts +40 -0
- package/dist/harness/live.d.ts.map +1 -0
- package/dist/harness/live.js +41 -0
- package/dist/harness/live.js.map +1 -0
- package/dist/harness/prompt.d.ts +24 -0
- package/dist/harness/prompt.d.ts.map +1 -0
- package/dist/harness/prompt.js +88 -0
- package/dist/harness/prompt.js.map +1 -0
- package/dist/harness/registry.d.ts +7 -0
- package/dist/harness/registry.d.ts.map +1 -0
- package/dist/harness/registry.js +309 -0
- package/dist/harness/registry.js.map +1 -0
- package/dist/harness/scheduler.d.ts +63 -0
- package/dist/harness/scheduler.d.ts.map +1 -0
- package/dist/harness/scheduler.js +605 -0
- package/dist/harness/scheduler.js.map +1 -0
- package/dist/harness/submissions.d.ts +24 -0
- package/dist/harness/submissions.d.ts.map +1 -0
- package/dist/harness/submissions.js +133 -0
- package/dist/harness/submissions.js.map +1 -0
- package/dist/harness/types.d.ts +358 -0
- package/dist/harness/types.d.ts.map +1 -0
- package/dist/harness/types.js +2 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/harness/util.d.ts +14 -0
- package/dist/harness/util.d.ts.map +1 -0
- package/dist/harness/util.js +55 -0
- package/dist/harness/util.js.map +1 -0
- package/dist/ids.d.ts +6 -0
- package/dist/ids.d.ts.map +1 -0
- package/dist/ids.js +9 -0
- package/dist/ids.js.map +1 -0
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/session/forks.d.ts +10 -0
- package/dist/session/forks.d.ts.map +1 -0
- package/dist/session/forks.js +42 -0
- package/dist/session/forks.js.map +1 -0
- package/dist/session/observation.d.ts +28 -0
- package/dist/session/observation.d.ts.map +1 -0
- package/dist/session/observation.js +280 -0
- package/dist/session/observation.js.map +1 -0
- package/dist/session/session.d.ts +59 -0
- package/dist/session/session.d.ts.map +1 -0
- package/dist/session/session.js +380 -0
- package/dist/session/session.js.map +1 -0
- package/dist/session/transaction.d.ts +105 -0
- package/dist/session/transaction.d.ts.map +1 -0
- package/dist/session/transaction.js +718 -0
- package/dist/session/transaction.js.map +1 -0
- package/dist/storage/jsonl/index.d.ts +2 -0
- package/dist/storage/jsonl/index.d.ts.map +1 -0
- package/dist/storage/jsonl/index.js +2 -0
- package/dist/storage/jsonl/index.js.map +1 -0
- package/dist/storage/jsonl/node.d.ts +6 -0
- package/dist/storage/jsonl/node.d.ts.map +1 -0
- package/dist/storage/jsonl/node.js +8 -0
- package/dist/storage/jsonl/node.js.map +1 -0
- package/dist/storage/jsonl/storage.d.ts +115 -0
- package/dist/storage/jsonl/storage.d.ts.map +1 -0
- package/dist/storage/jsonl/storage.js +681 -0
- package/dist/storage/jsonl/storage.js.map +1 -0
- package/dist/storage/memory.d.ts +60 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +681 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/storage/sqlite/database.d.ts +30 -0
- package/dist/storage/sqlite/database.d.ts.map +1 -0
- package/dist/storage/sqlite/database.js +2 -0
- package/dist/storage/sqlite/database.js.map +1 -0
- package/dist/storage/sqlite/index.d.ts +4 -0
- package/dist/storage/sqlite/index.d.ts.map +1 -0
- package/dist/storage/sqlite/index.js +3 -0
- package/dist/storage/sqlite/index.js.map +1 -0
- package/dist/storage/sqlite/migrations.d.ts +11 -0
- package/dist/storage/sqlite/migrations.d.ts.map +1 -0
- package/dist/storage/sqlite/migrations.js +111 -0
- package/dist/storage/sqlite/migrations.js.map +1 -0
- package/dist/storage/sqlite/node.d.ts +25 -0
- package/dist/storage/sqlite/node.d.ts.map +1 -0
- package/dist/storage/sqlite/node.js +97 -0
- package/dist/storage/sqlite/node.js.map +1 -0
- package/dist/storage/sqlite/storage.d.ts +51 -0
- package/dist/storage/sqlite/storage.d.ts.map +1 -0
- package/dist/storage/sqlite/storage.js +628 -0
- package/dist/storage/sqlite/storage.js.map +1 -0
- package/dist/tasks.d.ts +6 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +5 -0
- package/dist/tasks.js.map +1 -0
- package/dist/testing/assertions.d.ts +5 -0
- package/dist/testing/assertions.d.ts.map +1 -0
- package/dist/testing/assertions.js +25 -0
- package/dist/testing/assertions.js.map +1 -0
- package/dist/testing/index.d.ts +6 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +5 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/runner.d.ts +10 -0
- package/dist/testing/runner.d.ts.map +1 -0
- package/dist/testing/runner.js +14 -0
- package/dist/testing/runner.js.map +1 -0
- package/dist/testing/storage-benchmark.d.ts +41 -0
- package/dist/testing/storage-benchmark.d.ts.map +1 -0
- package/dist/testing/storage-benchmark.js +351 -0
- package/dist/testing/storage-benchmark.js.map +1 -0
- package/dist/testing/storage-conformance.d.ts +4 -0
- package/dist/testing/storage-conformance.d.ts.map +1 -0
- package/dist/testing/storage-conformance.js +1122 -0
- package/dist/testing/storage-conformance.js.map +1 -0
- package/dist/testing/types.d.ts +19 -0
- package/dist/testing/types.d.ts.map +1 -0
- package/dist/testing/types.js +2 -0
- package/dist/testing/types.js.map +1 -0
- package/dist/types.d.ts +525 -78
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/docs/chord-delta-findings.md +1 -1
- package/docs/pico-v5-chord-usage.md +90 -102
- package/docs/pico-v5-handoff.md +493 -253
- package/docs/pico-v5.md +1901 -848
- package/package.json +47 -5
- package/dist/memory-storage.d.ts +0 -37
- package/dist/memory-storage.d.ts.map +0 -1
- package/dist/memory-storage.js +0 -322
- 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 {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
export type
|
|
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:
|
|
9
|
-
/**
|
|
10
|
-
export type
|
|
11
|
-
readonly
|
|
12
|
-
|
|
13
|
-
readonly
|
|
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:
|
|
197
|
+
readonly id: ConversationId;
|
|
18
198
|
/** Fork source and inclusive parent entry through which history is inherited. */
|
|
19
199
|
readonly parent?: {
|
|
20
|
-
readonly conversationId:
|
|
21
|
-
readonly at:
|
|
200
|
+
readonly conversationId: ConversationId;
|
|
201
|
+
readonly at: EntryId;
|
|
22
202
|
};
|
|
23
|
-
/** Creator edge used for
|
|
203
|
+
/** Creator edge used for attribution, subtree abort, and subtree idle waits. */
|
|
24
204
|
readonly owner?: {
|
|
25
|
-
readonly conversationId:
|
|
26
|
-
readonly taskId:
|
|
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:
|
|
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:
|
|
44
|
-
readonly conversationId:
|
|
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,
|
|
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?:
|
|
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?:
|
|
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?:
|
|
241
|
+
readonly head?: EntryId | "self";
|
|
62
242
|
};
|
|
63
|
-
/**
|
|
64
|
-
type
|
|
65
|
-
readonly
|
|
66
|
-
|
|
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
|
|
71
|
-
export type
|
|
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
|
|
278
|
+
/** Added to the transcript and owned by an active run. */
|
|
80
279
|
readonly status: "placed";
|
|
81
|
-
|
|
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
|
|
285
|
+
/** Successfully answered user input. */
|
|
88
286
|
readonly status: "done";
|
|
89
|
-
|
|
90
|
-
readonly
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
163
|
-
readonly conversationId:
|
|
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
|
|
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:
|
|
194
|
-
/**
|
|
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:
|
|
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:
|
|
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:
|
|
480
|
+
readonly conversationId: ConversationId;
|
|
243
481
|
/** Oldest entry ID that may be returned. */
|
|
244
|
-
readonly minEntryId?:
|
|
482
|
+
readonly minEntryId?: EntryId;
|
|
245
483
|
/** Newest entry ID that may be returned. */
|
|
246
|
-
readonly maxEntryId?:
|
|
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?:
|
|
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
|
-
/**
|
|
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: "
|
|
268
|
-
readonly value:
|
|
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;
|
|
706
|
+
* immutable conversation/entry creation, document record consistency, and detached values;
|
|
707
|
+
* Session serializes commits.
|
|
276
708
|
*/
|
|
277
709
|
export interface Storage {
|
|
278
|
-
/**
|
|
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
|
|
281
|
-
mintId():
|
|
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:
|
|
717
|
+
conversation(id: ConversationId, context: Context): Promise<ConversationRecord | undefined>;
|
|
284
718
|
/** Scan conversations in ascending ID order. */
|
|
285
|
-
scanConversations(cursor: Cursor | undefined,
|
|
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:
|
|
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:
|
|
296
|
-
readonly head:
|
|
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,
|
|
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:
|
|
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,
|
|
304
|
-
/** Look up the latest complete record for one admitted
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
|
|
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
|
}
|