@morlay/session-rdb 0.0.11 → 0.0.13
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 +0 -16
- package/dist/artifact.d.mts +64 -0
- package/dist/artifact.mjs +3 -0
- package/dist/import-D2vUnUZe.mjs +438 -0
- package/dist/import.d.mts +24 -0
- package/dist/import.mjs +2 -0
- package/dist/index-uUvn6gYp.d.mts +111 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.mjs +466 -0
- package/dist/invariant.d.mts +7 -0
- package/dist/invariant.mjs +8 -0
- package/dist/magic-string.es-BWtG0AyA.mjs +1017 -0
- package/dist/schema-B-8G4lWc.mjs +852 -0
- package/dist/schema-CFXZURX7.d.mts +116 -0
- package/dist/sqlite-DIXY-dFZ.mjs +356 -0
- package/dist/storage.d.mts +44 -0
- package/dist/storage.mjs +3 -0
- package/dist/testing.d.mts +31 -0
- package/dist/testing.mjs +16051 -0
- package/package.json +39 -22
- package/src/adapters/ddl.ts +77 -0
- package/src/adapters/index.ts +4 -0
- package/src/adapters/to-postgres.ts +91 -0
- package/src/adapters/to-sqlite.ts +79 -0
- package/src/artifact.ts +4 -0
- package/src/backend.ts +112 -0
- package/src/branch.ts +508 -0
- package/src/entities/events.ts +27 -0
- package/src/entities/index.ts +30 -0
- package/src/entities/persistence-state.ts +10 -0
- package/src/entities/schema-meta.ts +9 -0
- package/src/entities/session-events.ts +29 -0
- package/src/entities/sessions.ts +20 -0
- package/src/entities/types.ts +48 -0
- package/src/import.ts +270 -0
- package/src/index.ts +547 -0
- package/src/invariant.ts +15 -0
- package/src/log.ts +456 -0
- package/src/migrate.ts +137 -0
- package/src/postgres.ts +369 -0
- package/src/schema.ts +234 -0
- package/src/sqlite.ts +412 -0
- package/src/storage.ts +5 -0
- package/src/testing/contract.ts +562 -0
- package/src/testing/coordinator-contract.ts +1723 -0
- package/src/testing/helpers.ts +20 -0
- package/src/testing.ts +12 -0
- package/src/write-guard.ts +27 -0
- package/lib/index.d.mts +0 -558
- package/lib/index.d.mts.map +0 -1
- package/lib/index.mjs +0 -2176
- package/lib/index.mjs.map +0 -1
- package/lib/invariant.d.mts +0 -15
- package/lib/invariant.d.mts.map +0 -1
- package/lib/invariant.mjs +0 -21
- package/lib/invariant.mjs.map +0 -1
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
import { SettingsProvider, type SettingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
3
|
+
|
|
4
|
+
export class EmptySettings extends SettingsProvider {
|
|
5
|
+
constructor(ctx: Context) {
|
|
6
|
+
super(ctx);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
get writable(): boolean {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
protected async load(): Promise<Record<string, unknown>> {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
protected persist(_ns: SettingsNamespace, _section: Record<string, unknown>): Promise<void> {
|
|
18
|
+
return Promise.resolve();
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/testing.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// 测试支撑(跨包测试共享):helpers / contract / coordinator-contract。
|
|
2
|
+
// 仅供 vitest 测试引用;不进入运行时 bundle。
|
|
3
|
+
export { EmptySettings } from "./testing/helpers.ts";
|
|
4
|
+
export type { ContractBackend } from "./testing/contract.ts";
|
|
5
|
+
export {
|
|
6
|
+
appendLog,
|
|
7
|
+
meta,
|
|
8
|
+
oneTurnLog,
|
|
9
|
+
runPersistenceContract,
|
|
10
|
+
} from "./testing/contract.ts";
|
|
11
|
+
export type { CoordinatorFixture } from "./testing/coordinator-contract.ts";
|
|
12
|
+
export { runCoordinatorContract } from "./testing/coordinator-contract.ts";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SessionId } from "@deepseek-ai/dsh-session";
|
|
2
|
+
|
|
3
|
+
export class WriteGuard {
|
|
4
|
+
private readonly headSeqs = new Map<SessionId, number>();
|
|
5
|
+
|
|
6
|
+
confirmHead(id: SessionId, head: number): void {
|
|
7
|
+
this.headSeqs.set(id, head);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
assertNoConcurrentWriter(id: SessionId, storedHead: number): void {
|
|
11
|
+
const known = this.headSeqs.get(id);
|
|
12
|
+
if (known === undefined) {
|
|
13
|
+
if (storedHead !== -1) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
`session "${id}" has a persisted log this instance has not read; another writer may own it — load the session first`,
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (known !== storedHead) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`session "${id}" was modified by another writer (stored head ${storedHead}, this instance last confirmed head ${known}); ` +
|
|
23
|
+
"concurrent writers on one session are not supported",
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
package/lib/index.d.mts
DELETED
|
@@ -1,558 +0,0 @@
|
|
|
1
|
-
import { Context } from "@deepseek-ai/cordis";
|
|
2
|
-
import z from "@deepseek-ai/schemastery";
|
|
3
|
-
import { PersistenceBackend, SessionLocation, SessionPersistence, SessionPersistenceRevision, SessionPersistenceSnapshot, StoredPrefix, StoredSuffix } from "@deepseek-ai/dsh-session-persistence";
|
|
4
|
-
import { Session, SessionEvent, SessionHeader, SessionId } from "@deepseek-ai/dsh-session";
|
|
5
|
-
import { BranchAnchorMode, BranchBoundary, ForkFromOptions, SessionBranch, SessionBranchProvider } from "@morlay/session-branch";
|
|
6
|
-
//#region src/backend.d.ts
|
|
7
|
-
/**
|
|
8
|
-
* 与方言无关的 `t_sessions` 行投影。SQLite / PostgreSQL 的 drizzle
|
|
9
|
-
* `InferSelectModel` 均结构兼容(各自多出的自增主键列不影响赋值)。
|
|
10
|
-
*/
|
|
11
|
-
interface SessionRow {
|
|
12
|
-
fSessionId: string;
|
|
13
|
-
/** Empty string means no head (fresh session or rewound to empty). */
|
|
14
|
-
fHeadEventId: string;
|
|
15
|
-
fHeadSequence: number;
|
|
16
|
-
fVersion: number;
|
|
17
|
-
fCreatedAt: number;
|
|
18
|
-
fCwd: string | null;
|
|
19
|
-
fParentSession: string | null;
|
|
20
|
-
fSeedLength: number | null;
|
|
21
|
-
fOrigin: string | null;
|
|
22
|
-
fDelegationDepth: number | null;
|
|
23
|
-
/** Stable identity assigned when this log is materialized. */
|
|
24
|
-
fIncarnation: string;
|
|
25
|
-
/** Monotonic log-change token incremented in each mutating transaction. */
|
|
26
|
-
fRevision: number;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* 一次事件插入的完整列值(`t_events` 一行),由存储层按事件构造、后端落库。
|
|
30
|
-
*/
|
|
31
|
-
interface EventInsert {
|
|
32
|
-
fEventId: string;
|
|
33
|
-
fParentId: string;
|
|
34
|
-
fKind: string;
|
|
35
|
-
fRole: string;
|
|
36
|
-
fName: string;
|
|
37
|
-
fActionId: string;
|
|
38
|
-
fEncoding: string;
|
|
39
|
-
fData: string;
|
|
40
|
-
fCreatedAt: number;
|
|
41
|
-
fOriginalSeq: number;
|
|
42
|
-
fSourceEventSeqs: string | null;
|
|
43
|
-
fSurfaceOp: string | null;
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* 一个 joined `t_session_events` + `t_events` 行:按 session 本地 `f_sequence`
|
|
47
|
-
* 寻址的持久化事件(`f_data` 为 JSON 文本,surface 列为 JSON 文本或 null)。
|
|
48
|
-
*/
|
|
49
|
-
interface EventRow {
|
|
50
|
-
/** `t_session_events.f_sequence` — the dense persisted seq. */
|
|
51
|
-
fSequence: number;
|
|
52
|
-
/** `t_events.f_original_seq` — the upstream seq before delta filtering. */
|
|
53
|
-
fOriginalSeq: number;
|
|
54
|
-
/** `t_events.f_kind` — the upstream `SessionEvent.type`. */
|
|
55
|
-
fKind: string;
|
|
56
|
-
/** `t_events.f_created_at` — the upstream `SessionEvent.time`. */
|
|
57
|
-
fCreatedAt: number;
|
|
58
|
-
/** `t_events.f_data` — JSON-encoded event data. */
|
|
59
|
-
fData: string;
|
|
60
|
-
/** JSON-encoded `number[]` — the event's sourceEventSeqs (upstream seqs), or null. */
|
|
61
|
-
fSourceEventSeqs: string | null;
|
|
62
|
-
/** JSON-encoded `SurfaceOp` — how the event entered the surface, or null. */
|
|
63
|
-
fSurfaceOp: string | null;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* 事务内可用的数据访问原语。后端保证这些调用落在同一个数据库事务里
|
|
67
|
-
* (SQLite 单连接隐式满足;PostgreSQL 绑定到 drizzle 的事务句柄)。
|
|
68
|
-
*/
|
|
69
|
-
interface BackendTx {
|
|
70
|
-
/** Insert-or-replace the session's metadata row (initial head cursor). */
|
|
71
|
-
upsertSession(meta: SessionHeader, incarnation: string): Promise<void>;
|
|
72
|
-
/** Fetch the head cursor; the caller materialized the row first. */
|
|
73
|
-
getHead(id: SessionId): Promise<Pick<SessionRow, "fHeadEventId" | "fHeadSequence">>;
|
|
74
|
-
/**
|
|
75
|
-
* Insert event rows in ONE multi-row INSERT. Callers pass non-empty arrays;
|
|
76
|
-
* the implementation may no-op on an empty input.
|
|
77
|
-
*/
|
|
78
|
-
insertEvents(events: EventInsert[]): Promise<void>;
|
|
79
|
-
/**
|
|
80
|
-
* Insert session↔event bridge rows in ONE multi-row INSERT. Callers pass
|
|
81
|
-
* non-empty arrays; the implementation may no-op on an empty input.
|
|
82
|
-
*/
|
|
83
|
-
insertBridges(rows: Array<{
|
|
84
|
-
fSessionId: SessionId;
|
|
85
|
-
fEventId: string;
|
|
86
|
-
fSequence: number;
|
|
87
|
-
}>): Promise<void>;
|
|
88
|
-
/** Move the head cursor forward. */
|
|
89
|
-
updateHead(id: SessionId, headEventId: string, headSequence: number): Promise<void>;
|
|
90
|
-
/** Increment the session's revision by one. */
|
|
91
|
-
bumpRevision(id: SessionId): Promise<void>;
|
|
92
|
-
/** Delete bridge rows with `f_sequence >= fromSequence` (torn-tail truncate). */
|
|
93
|
-
deleteBridgeTail(id: SessionId, fromSequence: number): Promise<void>;
|
|
94
|
-
/** The bridge row just below `sequence` (the surviving head anchor), if any. */
|
|
95
|
-
getPrevBridge(id: SessionId, sequence: number): Promise<{
|
|
96
|
-
fEventId: string;
|
|
97
|
-
fSequence: number;
|
|
98
|
-
} | undefined>;
|
|
99
|
-
/** The highest bridge row (the physical tail anchor), if any. */
|
|
100
|
-
getLastBridge(id: SessionId): Promise<{
|
|
101
|
-
fEventId: string;
|
|
102
|
-
fSequence: number;
|
|
103
|
-
} | undefined>;
|
|
104
|
-
/**
|
|
105
|
-
* Update an event's mutable columns in place (used by the per-session
|
|
106
|
-
* provenance cleanse, which rewrites surface/provenance JSON into the dense
|
|
107
|
-
* seq space). Undefined fields are left untouched; null clears the column.
|
|
108
|
-
*/
|
|
109
|
-
updateEventFields(id: SessionId, sequence: number, fields: {
|
|
110
|
-
fSourceEventSeqs?: string | null;
|
|
111
|
-
fSurfaceOp?: string | null;
|
|
112
|
-
fData?: string;
|
|
113
|
-
}): Promise<void>;
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* 存储后端:连接生命周期 + store 身份 + 事务外读取。`storeIdentity` 仅在
|
|
117
|
-
* {@link open} 完成后有效。
|
|
118
|
-
*/
|
|
119
|
-
interface Backend {
|
|
120
|
-
readonly kind: "sqlite" | "postgres";
|
|
121
|
-
/** Source-qualified store identity (revision 前缀),open 后可用。 */
|
|
122
|
-
readonly storeIdentity: string;
|
|
123
|
-
/** 连接 + 建表 + 版本/身份校验 + 读取 store id;失败时抛错(不迁移)。 */
|
|
124
|
-
open(): Promise<void>;
|
|
125
|
-
/** Fetch a session's row, or undefined if absent. */
|
|
126
|
-
getSession(id: SessionId): Promise<SessionRow | undefined>;
|
|
127
|
-
/** Lightweight two-column upstream→persisted seq map source. */
|
|
128
|
-
getSeqMapRows(id: SessionId): Promise<Array<{
|
|
129
|
-
fSequence: number;
|
|
130
|
-
fOriginalSeq: number;
|
|
131
|
-
fKind: string;
|
|
132
|
-
}>>;
|
|
133
|
-
/** Joined event rows for one session, dense seq ascending (optionally from a seq). */
|
|
134
|
-
getEventRows(id: SessionId, fromSequence?: number): Promise<EventRow[]>;
|
|
135
|
-
/** All materialized sessions' rows. */
|
|
136
|
-
listSessions(): Promise<SessionRow[]>;
|
|
137
|
-
/** Run `fn` inside one durable transaction. */
|
|
138
|
-
transaction<T>(fn: (tx: BackendTx) => Promise<T>): Promise<T>;
|
|
139
|
-
/** Close the connection (awaited by the coordinator's dispose, post-drain). */
|
|
140
|
-
close(): Promise<void>;
|
|
141
|
-
}
|
|
142
|
-
//#endregion
|
|
143
|
-
//#region src/write-guard.d.ts
|
|
144
|
-
/**
|
|
145
|
-
* The write-authority state machine for one backend instance. Not part of the
|
|
146
|
-
* {@link Backend} seam: it guards the orchestration layer's own invariants and
|
|
147
|
-
* lives entirely in memory.
|
|
148
|
-
*/
|
|
149
|
-
declare class WriteGuard {
|
|
150
|
-
/**
|
|
151
|
-
* Last CONFIRMED dense head per session — the head this instance itself
|
|
152
|
-
* wrote or observed via `loadStored`. `-1` records a confirmed absence (no
|
|
153
|
-
* row). `undefined` (absent from the map) means this instance never read or
|
|
154
|
-
* wrote the session.
|
|
155
|
-
*/
|
|
156
|
-
private readonly headSeqs;
|
|
157
|
-
/**
|
|
158
|
-
* Upstream seqs of delta events dropped per session. Mirrors `headSeqs` in
|
|
159
|
-
* shape: the concurrent-writer guarantee limits each session to one writer,
|
|
160
|
-
* so this instance is the only authority for its dropped seqs.
|
|
161
|
-
*/
|
|
162
|
-
private readonly filteredSeqs;
|
|
163
|
-
/**
|
|
164
|
-
* Record a head this instance actually observed or wrote.
|
|
165
|
-
* @param id - the session id.
|
|
166
|
-
* @param head - the confirmed dense head, or `-1` for a confirmed absence
|
|
167
|
-
* (a fresh session this instance has read about — a later append to a
|
|
168
|
-
* session that meanwhile got a row must reject).
|
|
169
|
-
*/
|
|
170
|
-
confirmHead(id: SessionId, head: number): void;
|
|
171
|
-
/**
|
|
172
|
-
* Fail loud when the on-disk head no longer matches this instance's last
|
|
173
|
-
* confirmed head for the session. `undefined` (never read/written here) is
|
|
174
|
-
* only acceptable for a session with NO row: a row written by someone else
|
|
175
|
-
* means this instance's coordinator cursor is not the log's authority.
|
|
176
|
-
* @param id - the session id.
|
|
177
|
-
* @param storedHead - the on-disk head cursor, read inside the append
|
|
178
|
-
* transaction before any re-numbering happens.
|
|
179
|
-
*/
|
|
180
|
-
assertNoConcurrentWriter(id: SessionId, storedHead: number): void;
|
|
181
|
-
/**
|
|
182
|
-
* Record the upstream seqs of events dropped for a session (delta events and
|
|
183
|
-
* ignorable events), so a later batch's `assistant/message` can prune
|
|
184
|
-
* `sourceEventSeqs` references to events that never got a persisted row.
|
|
185
|
-
* @param id - the session id.
|
|
186
|
-
* @param seqs - the dropped events' upstream seqs (pure-delta batches included).
|
|
187
|
-
*/
|
|
188
|
-
noteDropped(id: SessionId, seqs: Iterable<number>): void;
|
|
189
|
-
/**
|
|
190
|
-
* Prune `sourceEventSeqs` references that hit this session's dropped-delta
|
|
191
|
-
* seq set. `undefined`-like state (no dropped seqs recorded for the session)
|
|
192
|
-
* leaves the list untouched, matching the write path's "no known drops →
|
|
193
|
-
* keep verbatim" semantics (repair closers, which never carry provenance,
|
|
194
|
-
* call through the identity path).
|
|
195
|
-
*
|
|
196
|
-
* The predicate is THIS INSTANCE's view (see
|
|
197
|
-
* {@link pruneSourceEventSeqs}): only seqs it knows were dropped are
|
|
198
|
-
* pruned — references to rows persisted by another instance (e.g. a resume
|
|
199
|
-
* seed segment) must survive, so the disk-wide view used by the one-shot
|
|
200
|
-
* repair script is not applicable here.
|
|
201
|
-
* @param id - the session id.
|
|
202
|
-
* @param refs - the event's `sourceEventSeqs` (upstream seqs).
|
|
203
|
-
* @returns the pruned list; identical content when nothing was dropped.
|
|
204
|
-
*/
|
|
205
|
-
pruneRefs(id: SessionId, refs: readonly number[]): number[];
|
|
206
|
-
}
|
|
207
|
-
//#endregion
|
|
208
|
-
//#region src/schema.d.ts
|
|
209
|
-
/**
|
|
210
|
-
* The on-disk schema version. Bumped only on a breaking change to the table
|
|
211
|
-
* layout; orthogonal to a session's own `version` (which versions the EVENT
|
|
212
|
-
* vocabulary, stored per session in the `t_sessions` row).
|
|
213
|
-
*/
|
|
214
|
-
declare const SCHEMA_VERSION = 1;
|
|
215
|
-
/**
|
|
216
|
-
* Event types whose CONTENT is not persisted: the backend drops these rows
|
|
217
|
-
* entirely and re-numbers the surviving events to a dense persisted seq.
|
|
218
|
-
* Mirrors the persistence proposal's "ephemeral events never enter the
|
|
219
|
-
* canonical log" split.
|
|
220
|
-
*/
|
|
221
|
-
declare const EPHEMERAL_EVENT_TYPES: readonly ["assistant/chunk"];
|
|
222
|
-
/**
|
|
223
|
-
* Journal modes the backend will run under. `wal` is the default and the
|
|
224
|
-
* durability model the persistence ADR records; the rollback-journal modes
|
|
225
|
-
* (`delete`/`truncate`/`persist`) exist for filesystems where WAL's
|
|
226
|
-
* shared-memory files do not work (network mounts). `memory`/`off` are
|
|
227
|
-
* excluded: dropping journal durability silently contradicts what this
|
|
228
|
-
* backend promises.
|
|
229
|
-
*/
|
|
230
|
-
type JournalMode = "wal" | "delete" | "truncate" | "persist";
|
|
231
|
-
//#endregion
|
|
232
|
-
//#region src/branch.d.ts
|
|
233
|
-
/**
|
|
234
|
-
* 定位 `atSeq` 锚定的闭合 `turn/end` 边界 seq。见
|
|
235
|
-
* {@link SessionBranchProvider.readBranchPrefix} 的锚定语义说明。
|
|
236
|
-
* @param events - 完整(或前缀)事件列表,seq 连续。
|
|
237
|
-
* @param atSeq - 锚定 seq(inclusive);省略取最后闭合轮次。
|
|
238
|
-
* @param mode - `"after"`(默认)或 `"before"`。
|
|
239
|
-
* @returns 边界事件 seq;`"before"` 模式下 atSeq 之前无闭合轮次时返回 -1
|
|
240
|
-
* (空前缀)。
|
|
241
|
-
*/
|
|
242
|
-
declare function locateTurnEnd(events: readonly SessionEvent[], atSeq?: number, mode?: BranchAnchorMode): number;
|
|
243
|
-
/**
|
|
244
|
-
* live rewind 所需的「live 会话/agent」最小访问面。上游 `Session` 是
|
|
245
|
-
* append-only、缓存增量式对象,无公开截断方法;`PersistenceCoordinator`
|
|
246
|
-
* 的 `states` 是私有 Map。这些是**运行时最小侵入**(不改上游源码,仅在
|
|
247
|
-
* 编排层操作编译后对象字段),由 `SessionBranchRdb` 注入。
|
|
248
|
-
*/
|
|
249
|
-
interface LiveSessionHooks {
|
|
250
|
-
/** 查当前 live 会话(`ctx.sessions.get`)。 */
|
|
251
|
-
getSession(id: SessionId): Session | undefined;
|
|
252
|
-
/** 查当前 live agent(`ctx.agents?.get`,agents 服务可选)。 */
|
|
253
|
-
getAgent(id: SessionId): LiveAgentLike | undefined;
|
|
254
|
-
/** 立即落盘 live 会话的 write-behind 缓冲(`ctx.sessions.flush`)。 */
|
|
255
|
-
flush(session: Session): Promise<boolean>;
|
|
256
|
-
/**
|
|
257
|
-
* 截断后同步 coordinator 的会话内存状态:把 `states` 条目的 `cursor`
|
|
258
|
-
* 对齐到新的尾部(= boundary + 1),使下一次 append 的 seq 连续性校验
|
|
259
|
-
* 通过。**不能**删除 `states` 条目——live 会话的下一次 append 若走
|
|
260
|
-
* `adopt` 会经 `SessionStore.prepare` 构造 detached session,与 store 中
|
|
261
|
-
* 的 live entry 冲突。
|
|
262
|
-
*/
|
|
263
|
-
setCoordinatorCursor(id: SessionId, cursor: number): void;
|
|
264
|
-
/**
|
|
265
|
-
* 截断后同步 coordinator 的 ownerless 状态(cold 分支):把 `states` 条目
|
|
266
|
-
* 的 `cursor` 对齐到新尾部。与 {@link setCoordinatorCursor} 的区别是
|
|
267
|
-
* **不要求条目已存在**——cold 会话可能从未被 adopt(无 states 条目),
|
|
268
|
-
* 此时创建 ownerless 条目(meta 来自截断后快照),使下一次 append 走
|
|
269
|
-
* 标准路径而非 `adopt`(adopt 会经 `prepareCore` 补 closers 撤销截断)。
|
|
270
|
-
*/
|
|
271
|
-
setCoordinatorState(id: SessionId, cursor: number, meta: SessionHeader): void;
|
|
272
|
-
}
|
|
273
|
-
/** agent 的最小 live 形态:持有同一会话 + 可重置请求头日志标记。 */
|
|
274
|
-
interface LiveAgentLike {
|
|
275
|
-
session: Session;
|
|
276
|
-
/** 编译后字段:是否已 append 过首个 `request/header`(截断后需重置)。 */
|
|
277
|
-
requestHeaderLogged?: boolean;
|
|
278
|
-
}
|
|
279
|
-
/**
|
|
280
|
-
* RDB 分支数据层实现(`SessionBranchProvider`)。与 `SessionPersistenceRdb`
|
|
281
|
-
* 共享同一数据库连接(`Backend`)与 coordinator 写路径。
|
|
282
|
-
*/
|
|
283
|
-
declare class SessionBranchRdbProvider implements SessionBranchProvider {
|
|
284
|
-
private readonly persistence;
|
|
285
|
-
/** live 会话/agent 访问面(rewind 支持 live session 时使用)。 */
|
|
286
|
-
private readonly live;
|
|
287
|
-
readonly name = "session-rdb";
|
|
288
|
-
constructor(persistence: SessionPersistenceRdb,
|
|
289
|
-
/** live 会话/agent 访问面(rewind 支持 live session 时使用)。 */
|
|
290
|
-
live?: LiveSessionHooks);
|
|
291
|
-
readBranchPrefix(id: SessionId, atSeq?: number, mode?: BranchAnchorMode, signal?: AbortSignal): Promise<BranchBoundary>;
|
|
292
|
-
/**
|
|
293
|
-
* 读取会话的**原始**事件(不含 coordinator 补记的合成 closers)。
|
|
294
|
-
*
|
|
295
|
-
* `persistence.inspect` 走 coordinator 的 `prepareCore`,会给未闭合 log
|
|
296
|
-
* 补 `interruptedTurnClosers`(合成 step/end + turn/end)——对普通读取
|
|
297
|
-
* (客户端历史)这是正确的逻辑视图,但对分支编排是误导:未闭合轮次被
|
|
298
|
-
* closers 掩盖成闭合,编辑未闭合轮次的 user 消息会走错边界。这里用
|
|
299
|
-
* `loadStored`(backend hook,scanRows 只做 torn-tail 切割、不补 closers)
|
|
300
|
-
* 读原始事件,使未闭合状态真实可见。
|
|
301
|
-
*/
|
|
302
|
-
readRawEvents(id: SessionId, signal?: AbortSignal): Promise<{
|
|
303
|
-
meta: SessionHeader;
|
|
304
|
-
events: readonly SessionEvent[];
|
|
305
|
-
}>;
|
|
306
|
-
forkFrom(sourceId: SessionId, options?: ForkFromOptions, signal?: AbortSignal): Promise<SessionId>;
|
|
307
|
-
rewind(id: SessionId, toBoundary: number, signal?: AbortSignal): Promise<SessionPersistenceSnapshot>;
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* RDB 的 `ctx.sessionBranch` 服务:组合 {@link SessionBranchRdbProvider} 的
|
|
311
|
-
* 数据层原语与共享版本树投影。插件把本类注册为 `sessionBranch` 服务后,
|
|
312
|
-
* 编排层即可通过统一服务面完成 rewind / retry / fork。
|
|
313
|
-
*/
|
|
314
|
-
declare class SessionBranchRdb extends SessionBranch {
|
|
315
|
-
static inject: string[];
|
|
316
|
-
constructor(ctx: import("@deepseek-ai/cordis").Context);
|
|
317
|
-
private readonly provider;
|
|
318
|
-
readBranchPrefix(id: SessionId, atSeq?: number, mode?: BranchAnchorMode, signal?: AbortSignal): Promise<BranchBoundary>;
|
|
319
|
-
/**
|
|
320
|
-
* 读取会话的**原始**事件(不含 coordinator 补记的合成 closers)。
|
|
321
|
-
* 编排层用它识别未闭合轮次(inspect 会把未闭合 log 补成闭合)。
|
|
322
|
-
*/
|
|
323
|
-
readRawEvents(id: SessionId, signal?: AbortSignal): Promise<{
|
|
324
|
-
meta: SessionHeader;
|
|
325
|
-
events: readonly SessionEvent[];
|
|
326
|
-
}>;
|
|
327
|
-
forkFrom(sourceId: SessionId, options?: ForkFromOptions, signal?: AbortSignal): Promise<SessionId>;
|
|
328
|
-
rewind(id: SessionId, toBoundary: number, signal?: AbortSignal): Promise<SessionPersistenceSnapshot>;
|
|
329
|
-
/**
|
|
330
|
-
* 清洗一个 session 的 surface/provenance 坐标到稠密空间(持久化层透传)。
|
|
331
|
-
* 旧数据(rewind 前写入)的 sourceEventSeqs / surfaceOp / shadowedRange
|
|
332
|
-
* 是上游坐标,读取时每次都要对齐;清洗一次性写回稠密坐标,之后读取无需
|
|
333
|
-
* 对齐。返回实际变更的事件数。
|
|
334
|
-
*/
|
|
335
|
-
cleanseSession(sessionId: SessionId, signal?: AbortSignal): Promise<{
|
|
336
|
-
changed: number;
|
|
337
|
-
}>;
|
|
338
|
-
/**
|
|
339
|
-
* 同步 live 会话的 coordinator 内存 cursor,跳过 ignorable 占位事件。
|
|
340
|
-
* 编排层在 rewind 后把 ignorable 版本效果 push 进 live log(不发布、不
|
|
341
|
-
* 进缓冲)——它占用一个上游 seq,但 coordinator 的 cursor(rewind 设到
|
|
342
|
-
* 截断后长度)看不见它;不跳过的话,flush 的 `appendLiveBatch` 会以
|
|
343
|
-
* `e.seq >= cursor` 过滤掉 cursor 之前的事件(manualTurn 永远不落盘),
|
|
344
|
-
* 或 `appendCore` 的 seq 连续性校验错位。这里把 cursor 从当前值起跳过
|
|
345
|
-
* 连续的 ignorable 事件,对齐到下一个待持久化事件的 seq。
|
|
346
|
-
*/
|
|
347
|
-
syncLiveCursor(sessionId: SessionId): void;
|
|
348
|
-
timeline(sessionId: SessionId, signal?: AbortSignal): Promise<import("@morlay/session-branch").BranchTimeline>;
|
|
349
|
-
}
|
|
350
|
-
//#endregion
|
|
351
|
-
//#region src/index.d.ts
|
|
352
|
-
/**
|
|
353
|
-
* 同包分支 provider(rewind / forkFrom)访问 `SessionPersistenceRdb` 内部
|
|
354
|
-
* 能力的窄接口。避免把 backend / writeGuard 暴露成公开 API,同时让
|
|
355
|
-
* {@link SessionBranchRdbProvider} 与持久化后端共享同一连接与写路径。
|
|
356
|
-
* @internal
|
|
357
|
-
*/
|
|
358
|
-
interface SessionPersistenceRdbInternals {
|
|
359
|
-
readonly backend: Backend;
|
|
360
|
-
readonly writeGuard: WriteGuard;
|
|
361
|
-
create(meta: SessionHeader): Promise<void>;
|
|
362
|
-
append(id: SessionId, events: readonly SessionEvent[]): Promise<void>;
|
|
363
|
-
load(id: SessionId): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
|
|
364
|
-
inspect(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionInspection>;
|
|
365
|
-
readFrom(id: SessionId, fromSeq: number, signal?: AbortSignal): Promise<{
|
|
366
|
-
meta: SessionHeader;
|
|
367
|
-
events: SessionEvent[];
|
|
368
|
-
}>;
|
|
369
|
-
listSnapshots(signal?: AbortSignal): Promise<SessionPersistenceSnapshot[]>;
|
|
370
|
-
readStoredRevision(id: SessionId, signal?: AbortSignal): Promise<import("@deepseek-ai/dsh-session-persistence").SessionPersistenceRevision | undefined>;
|
|
371
|
-
}
|
|
372
|
-
/**
|
|
373
|
-
* Plugin configuration — a discriminated union on `type`. The SQLite arm keeps
|
|
374
|
-
* the file path plus the SQLite-specific pragmas; the PostgreSQL arm takes a
|
|
375
|
-
* `node-postgres` connection string.
|
|
376
|
-
*/
|
|
377
|
-
type Config = {
|
|
378
|
-
type: "sqlite";
|
|
379
|
-
/**
|
|
380
|
-
* Filesystem path to the SQLite database file. The special value `:memory:`
|
|
381
|
-
* opens an in-process database (tests). On filesystems with POSIX modes,
|
|
382
|
-
* missing directories and databases are created owner-only; existing path
|
|
383
|
-
* modes are preserved.
|
|
384
|
-
*/
|
|
385
|
-
path: string;
|
|
386
|
-
/**
|
|
387
|
-
* SQLite `journal_mode` pragma. `wal` (the default) is the recorded
|
|
388
|
-
* durability model; pick a rollback-journal mode (`delete`/`truncate`/
|
|
389
|
-
* `persist`) on filesystems where WAL's shared-memory files do not work
|
|
390
|
-
* (network mounts). See {@link JournalMode}.
|
|
391
|
-
*/
|
|
392
|
-
journalMode?: JournalMode;
|
|
393
|
-
/**
|
|
394
|
-
* Milliseconds to wait for a contended write lock before failing. SQLite
|
|
395
|
-
* fails immediately by default, so a second process sharing this database
|
|
396
|
-
* would lose every append that meets an in-flight commit; a nonzero wait
|
|
397
|
-
* turns the contention window into a queue. `0` restores fail-fast.
|
|
398
|
-
*/
|
|
399
|
-
busyTimeout?: number;
|
|
400
|
-
} | {
|
|
401
|
-
type: "postgres";
|
|
402
|
-
/**
|
|
403
|
-
* `node-postgres` connection string (e.g.
|
|
404
|
-
* `postgres://user:pass@host:5432/db`). The database must be reachable;
|
|
405
|
-
* the backend creates its tables and identity on first open.
|
|
406
|
-
*/
|
|
407
|
-
connectionString: string;
|
|
408
|
-
};
|
|
409
|
-
/**
|
|
410
|
-
* The persistence backend. Load as a plugin; it registers as
|
|
411
|
-
* `ctx.sessionPersistence` and (via the coordinator) installs the write-path
|
|
412
|
-
* listeners. Its torn-tail marker is the persisted seq to delete from.
|
|
413
|
-
*
|
|
414
|
-
* Configuration resolution: `$DSH_HOME/settings.yaml` 的
|
|
415
|
-
* `session-rdb` namespace(settings 服务)覆盖 cordis 层 entry
|
|
416
|
-
* config,见 {@link installSettingsSection}。
|
|
417
|
-
*/
|
|
418
|
-
declare class SessionPersistenceRdb extends SessionPersistence implements PersistenceBackend<number> {
|
|
419
|
-
config: Config;
|
|
420
|
-
static inject: string[];
|
|
421
|
-
static Config: z<Config>;
|
|
422
|
-
/** settings namespace:`$DSH_HOME/settings.yaml` 的 `session-rdb` section。 */
|
|
423
|
-
static readonly settingsNs: import("@deepseek-ai/dsh-settings").SettingsNamespace;
|
|
424
|
-
/**
|
|
425
|
-
* Backend label for the coordinator's dispose diagnostics. Intentionally
|
|
426
|
-
* shadows cordis `Service.name` (set to `'sessionPersistence'` by the base);
|
|
427
|
-
* see the JSONL backend for why this does not affect service resolution.
|
|
428
|
-
*/
|
|
429
|
-
readonly name = "session-rdb";
|
|
430
|
-
/** One RDB database holds every session; there is no per-session raw artifact. */
|
|
431
|
-
readonly supportsRawArtifacts = false;
|
|
432
|
-
private readonly backend;
|
|
433
|
-
private storeIdentity;
|
|
434
|
-
private readonly ready;
|
|
435
|
-
private readonly coordinator;
|
|
436
|
-
/**
|
|
437
|
-
* Write-authority state: the confirmed dense head per session (concurrent-
|
|
438
|
-
* writer detection) and the dropped delta seqs per session (provenance
|
|
439
|
-
* pruning). See {@link WriteGuard} for the timing contract.
|
|
440
|
-
*/
|
|
441
|
-
private readonly writeGuard;
|
|
442
|
-
constructor(ctx: Context, config: Config,
|
|
443
|
-
/**
|
|
444
|
-
* @internal Test injection: use a pre-built backend (e.g. a drizzle PG
|
|
445
|
-
* instance over an in-memory pglite) instead of {@link createBackend}.
|
|
446
|
-
*/
|
|
447
|
-
injectedBackend?: Backend);
|
|
448
|
-
private init;
|
|
449
|
-
/** The backend has one database, not an independent local artifact per session. */
|
|
450
|
-
locate(_meta: SessionHeader): SessionLocation | undefined;
|
|
451
|
-
create(meta: SessionHeader): Promise<void>;
|
|
452
|
-
append(id: SessionId, events: readonly SessionEvent[]): Promise<void>;
|
|
453
|
-
load(id: SessionId): Promise<{
|
|
454
|
-
meta: SessionHeader;
|
|
455
|
-
events: readonly SessionEvent[];
|
|
456
|
-
}>;
|
|
457
|
-
inspect(id: SessionId, signal?: AbortSignal): Promise<{
|
|
458
|
-
meta: SessionHeader;
|
|
459
|
-
events: readonly SessionEvent[];
|
|
460
|
-
}>;
|
|
461
|
-
readFrom(id: SessionId, fromSeq: number, signal?: AbortSignal): Promise<{
|
|
462
|
-
meta: SessionHeader;
|
|
463
|
-
events: SessionEvent[];
|
|
464
|
-
}>;
|
|
465
|
-
/** Read a stored prefix by id (ids are globally unique — no scope to scan). */
|
|
466
|
-
loadStored(id: SessionId, signal?: AbortSignal): Promise<StoredPrefix<number> | undefined>;
|
|
467
|
-
/**
|
|
468
|
-
* Seek-capable suffix read: the backend selects `f_sequence >= fromSeq`
|
|
469
|
-
* directly, so the read scales with the suffix, not the log. Provenance
|
|
470
|
-
* remapping still needs every row's upstream seq, so a lightweight
|
|
471
|
-
* two-column map is read alongside. Torn rows past the preserved region are
|
|
472
|
-
* dropped, never repaired (non-mutating read).
|
|
473
|
-
*/
|
|
474
|
-
loadStoredFrom(id: SessionId, fromSeq: number, signal?: AbortSignal): Promise<StoredSuffix | undefined>;
|
|
475
|
-
/**
|
|
476
|
-
* Read a session's row + ordered events into a {@link StoredPrefix}. The
|
|
477
|
-
* torn-tail marker is the persisted seq from which a never-committed tail
|
|
478
|
-
* must be deleted (`scanRows` already returns it as `number | undefined`).
|
|
479
|
-
* Records the confirmed dense head (or confirmed absence) so a later
|
|
480
|
-
* `appendBatch` can detect a second writer that advanced the log.
|
|
481
|
-
*/
|
|
482
|
-
private readPrefix;
|
|
483
|
-
/**
|
|
484
|
-
* Read the current source-qualified revision for one stored session without
|
|
485
|
-
* loading its event log. Returns `undefined` when the identity is absent.
|
|
486
|
-
* The representation matches {@link loadStored}'s `revision` and
|
|
487
|
-
* {@link listSnapshots} — the coordinator compares them with `===`.
|
|
488
|
-
*/
|
|
489
|
-
readStoredRevision(id: SessionId, signal?: AbortSignal): Promise<SessionPersistenceRevision | undefined>;
|
|
490
|
-
/**
|
|
491
|
-
* Shared read pipeline: session row → meta, event rows → preserved prefix.
|
|
492
|
-
* A whole-log read (`fromSeq` absent) builds the seq map from the same rows;
|
|
493
|
-
* a suffix read keeps the backend's lightweight two-column seq-map source so
|
|
494
|
-
* the query still scales with the suffix, not the log.
|
|
495
|
-
*/
|
|
496
|
-
private readLog;
|
|
497
|
-
/**
|
|
498
|
-
* Durably append a batch in ONE transaction: materialize the sessions row (if
|
|
499
|
-
* lazy) and INSERT every persisted event (plus its bridge row), or roll back
|
|
500
|
-
* entirely. Delta events and events the writer marked `ignorable` are dropped
|
|
501
|
-
* and the surviving events are re-numbered densely from the session's head
|
|
502
|
-
* cursor; a batch that contains only dropped events is a no-op (no row
|
|
503
|
-
* materialization, no revision bump). Dropped events' upstream seqs are
|
|
504
|
-
* recorded per session so a later batch's surface provenance can prune
|
|
505
|
-
* references to them (see {@link surfaceBindings}).
|
|
506
|
-
* The transaction is the atomicity + durability boundary, so a mid-batch
|
|
507
|
-
* failure (a UNIQUE violation on a duplicated seq) leaves the stored log
|
|
508
|
-
* untouched.
|
|
509
|
-
*
|
|
510
|
-
* SQLite acquires the write lock up front (`BEGIN IMMEDIATE`, queued behind
|
|
511
|
-
* `busy_timeout`); PostgreSQL relies on the transaction's row locks and the
|
|
512
|
-
* `UNIQUE (f_session_id, f_sequence)` constraint to reject a colliding batch.
|
|
513
|
-
* Either way {@link assertNoConcurrentWriter} rejects a second writer before
|
|
514
|
-
* re-numbering — a session has exactly one writer per log, and a second
|
|
515
|
-
* writer fails loud instead of corrupting the log.
|
|
516
|
-
*
|
|
517
|
-
* The row upsert runs UNCONDITIONALLY, not only when `!isMaterialized`: a
|
|
518
|
-
* delta-only batch leaves the coordinator's materialized flag true while no
|
|
519
|
-
* row exists, so the flag cannot be trusted as the row's existence signal.
|
|
520
|
-
* The upsert keeps an existing row's head cursor (only header columns are
|
|
521
|
-
* refreshed on conflict), so a fresh row still starts at the initial head.
|
|
522
|
-
*/
|
|
523
|
-
appendBatch(meta: SessionHeader, events: readonly SessionEvent[], _isMaterialized: boolean): Promise<void>;
|
|
524
|
-
/**
|
|
525
|
-
* Make a crash repair durable in ONE transaction: DELETE the torn tail (from
|
|
526
|
-
* `tornMarker`), rewind the head cursor to the last surviving event, INSERT
|
|
527
|
-
* the synthetic `closers`, and bump the revision once. After COMMIT the
|
|
528
|
-
* stored rows == the balanced log.
|
|
529
|
-
*/
|
|
530
|
-
commitRepair(meta: SessionHeader, tornMarker: number | undefined, closers: readonly SessionEvent[]): Promise<void>;
|
|
531
|
-
/**
|
|
532
|
-
* 一次性清洗一个 session 的 surface/provenance 坐标到稠密空间并写回。
|
|
533
|
-
*
|
|
534
|
-
* 旧数据(rewind 前的代码写入)的 `sourceEventSeqs`、`surfaceOp` replace
|
|
535
|
-
* range 与 compaction `shadowedRange` 是上游坐标,读取时每次都要做坐标
|
|
536
|
-
* 解析对齐;清洗用与读取完全相同的解析(稠密优先 + 上游映射 + 剪枝 +
|
|
537
|
-
* replace provenance 补全)把它们重写为稠密坐标,之后读取无需再对齐
|
|
538
|
-
* (对已清洗数据解析恒为恒等)。torn tail 片段不参与(读取路径也不会
|
|
539
|
-
* 保留它们)。返回实际变更的事件数。
|
|
540
|
-
*/
|
|
541
|
-
cleanseSession(id: SessionId, signal?: AbortSignal): Promise<{
|
|
542
|
-
changed: number;
|
|
543
|
-
}>;
|
|
544
|
-
/** List all materialized sessions' metadata (every row is a materialized session). */
|
|
545
|
-
list(signal?: AbortSignal): Promise<SessionHeader[]>;
|
|
546
|
-
/** List metadata with a source-qualified monotonic revision per session. */
|
|
547
|
-
listSnapshots(signal?: AbortSignal): Promise<SessionPersistenceSnapshot[]>;
|
|
548
|
-
/** Close the database connection (awaited by the coordinator's dispose, post-drain). */
|
|
549
|
-
close(): Promise<void>;
|
|
550
|
-
/**
|
|
551
|
-
* 同包分支 provider 的内部访问面(rewind / forkFrom 共享后端与写路径)。
|
|
552
|
-
* @internal 仅供 `SessionBranchRdbProvider` 使用;不是公开 API。
|
|
553
|
-
*/
|
|
554
|
-
internals(): SessionPersistenceRdbInternals;
|
|
555
|
-
}
|
|
556
|
-
//#endregion
|
|
557
|
-
export { Config, EPHEMERAL_EVENT_TYPES, SCHEMA_VERSION, SessionBranchRdb, SessionBranchRdbProvider, SessionPersistenceRdb, SessionPersistenceRdb as default, SessionPersistenceRdbInternals, locateTurnEnd };
|
|
558
|
-
//# sourceMappingURL=index.d.mts.map
|
package/lib/index.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/backend.ts","../src/write-guard.ts","../src/schema.ts","../src/branch.ts","../src/index.ts"],"mappings":"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"}
|