@deepseek-ai/dsh-session-projection-cache 0.1.6-alpha.1 → 0.1.7-alpha.1
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.i18n.yaml +2 -2
- package/README.md +2 -2
- package/README.zh.md +2 -2
- package/lib/index.js +71 -51
- package/lib/types/index.d.ts +27 -19
- package/lib/types/spec.d.ts +8 -6
- package/package.json +13 -13
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/session/session-projection-cache/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 0ba9c5a47fbf2006be1593cb1805adb5bf9fbc59
|
|
6
|
+
README.zh.md: d697b0520c0edf89572e9994b62e987111481a77
|
package/README.md
CHANGED
|
@@ -58,11 +58,11 @@ Three mandatory points always write: session creation persists the seed-derived
|
|
|
58
58
|
|
|
59
59
|
### Reading cached values
|
|
60
60
|
|
|
61
|
-
`cachedSnapshot(meta,
|
|
61
|
+
`cachedSnapshot(meta, keys?)` is the read-only face: it synchronously serves client values from the storage domain's in-memory tables with zero I/O. It accepts a record whose lifecycle identity (`formatVersion`, `createdAt`, `cwd`, `isSeeded`) matches the header and serves its version- and schema-matching keys as one block whose `asOfSeq` is the lowest watermark among the served rows. That watermark is the stored record's own: a header witnesses no inherited cut and cannot vouch that the row sequence is comparable with the log a consumer later opens, so the Session list labels the block `cached` and the client lets every value the connected Session later produces supersede it. Within one format generation the cut is fixed at fork time and distinguishes no lifecycle the other fields do not, and a viewed value never seeds a fold, so seeded (forked) sessions are served exactly like unseeded ones. `cachedPredecessorTitle(meta)` is the narrower listing-only exception across a Session-format edge: a structurally admitted predecessor record whose lifecycle matches may expose only a current-version-compatible `title` row, because title text is invariant across the adjacent edges. All other predecessor rows remain unavailable. `coldSnapshot(meta, inheritedEventCount, events)` is a fold face: it accepts the exact cut with a complete ordered log, skips the checkpointed prefix while folding, and refreshes the record without reading persistence itself.
|
|
62
62
|
|
|
63
63
|
### What the cache guarantees
|
|
64
64
|
|
|
65
|
-
The log leads and the cache follows: a live checkpoint flushes the session's buffered events durably before the cache row lands, so a crash can leave the cache behind the log but never ahead of it. Reads and writes share the storage domain's coherent in-memory state; the per-unit write chain mutates memory only after durability. Each version-stamped record must match the live unit schema and
|
|
65
|
+
The log leads and the cache follows: a live checkpoint flushes the session's buffered events durably before the cache row lands, so a crash can leave the cache behind the log but never ahead of it. Reads and writes share the storage domain's coherent in-memory state; the per-unit write chain mutates memory only after durability. Each version-stamped record must match the live unit schema and the lifecycle identity (`formatVersion`, `createdAt`, `cwd`, `isSeeded`); the fold faces (`hydratePrepared`, `coldSnapshot`, and checkpoint writes) also require the exact `inheritedEventCount`, so a row folded from another Session format generation or fork cut cannot seed the caller. The JSON backend stores each record at `<root>/session_projcache/sessions/<id>.json` in an owner-only directory tree. Domain read validation preserves every own JSON key in checkpoint values, including `__proto__` and `constructor` inside opaque metadata. It rejects values that cannot survive a lossless JSON round trip, using the same rules as checkpoint writes. Each projection then validates its hydration state with its own `stateSchema`; fields carrying opaque JSON need a validator that preserves their keys.
|
|
66
66
|
|
|
67
67
|
Upgrades never block startup or expose an unproven fold. Records stamped with a version in the spec's `compatibleVersions` remain structurally readable for a current checkpoint rewrite, but a missing or older `formatVersion` never matches a current Session and therefore cannot seed hydration. A lifecycle-matching predecessor title remains available only through the listing hint above because title text is invariant across the adjacent Session-format edges and its row still passes the current projection `stateVersion` and schema. Once the format matches, absent lineage fields decode as the unseeded lineage — exact for unseeded sessions, while a seeded caller fails the identity match and refolds cold. A stored record that still fails schema validation is moved aside as `<id>.json.bak.<stamp>` under the domain's `invalidRecords: 'backup-and-skip'` policy, logged with its cause, and rebuilt by the next checkpoint.
|
|
68
68
|
|
package/README.zh.md
CHANGED
|
@@ -58,11 +58,11 @@ kind: "package-reference"
|
|
|
58
58
|
|
|
59
59
|
### 读取缓存值
|
|
60
60
|
|
|
61
|
-
`cachedSnapshot(meta,
|
|
61
|
+
`cachedSnapshot(meta, keys?)` 是只读面:以零 I/O 从存储域的内存表同步提供客户端值。它接受生命周期身份(`formatVersion`、`createdAt`、`cwd`、`isSeeded`)与 header 匹配的记录,把其中版本和 schema 均匹配的 key 作为一个 block 提供,其 `asOfSeq` 是所服务各行中最低的水位。这个水位是存储记录自己的:header 作证不了 inherited cut,也作证不了行序号与消费者稍后打开的日志可比,因此 Session list 把该 block 标为 `cached`,客户端让建连后的 Session 产出的任何值覆盖它。在同一格式代内,cut 在 fork 时写死,不能区分其他字段区分不了的生命周期,而只读视图也从不播种 fold,所以 seeded(fork 出来的)会话与 unseeded 会话被同样地提供。`cachedPredecessorTitle(meta)` 是跨 Session 格式 edge 的更窄列表专用例外:生命周期匹配且已通过结构准入的 predecessor record 只能公开与当前版本兼容的 `title` row,因为 title 文本在相邻 edge 之间保持不变。其他 predecessor row 仍不可用。`coldSnapshot(meta, inheritedEventCount, events)` 是 fold 面:接受精确切点与完整有序日志,在折叠时跳过已检查点化的前缀,并在自身不读取持久化层的情况下刷新记录。
|
|
62
62
|
|
|
63
63
|
### 缓存保证什么
|
|
64
64
|
|
|
65
|
-
日志领先,缓存跟随:活会话检查点先把会话的缓冲事件持久化,然后才保存缓存记录。因此崩溃可能让缓存落后于日志,但绝不会让缓存领先。读取和写入共享存储域内一致的内存状态;逐单元写入链只在持久化成功后修改内存。每个带版本戳的记录必须匹配当前运行单元的 schema
|
|
65
|
+
日志领先,缓存跟随:活会话检查点先把会话的缓冲事件持久化,然后才保存缓存记录。因此崩溃可能让缓存落后于日志,但绝不会让缓存领先。读取和写入共享存储域内一致的内存状态;逐单元写入链只在持久化成功后修改内存。每个带版本戳的记录必须匹配当前运行单元的 schema 与生命周期身份(`formatVersion`、`createdAt`、`cwd`、`isSeeded`);fold 面(`hydratePrepared`、`coldSnapshot` 与检查点写入)还要求精确的 `inheritedEventCount`,因此从另一会话格式代或 fork 切点折叠出的行不能播种调用方。JSON 后端把每条记录存于仅所有者可访问的 `<root>/session_projcache/sessions/<id>.json` 目录树中。Domain 读取校验保留检查点值中的所有自有 JSON 键,包括不透明元数据中的 `__proto__` 与 `constructor`。它拒绝无法无损完成 JSON 往返的值,并与检查点写入使用相同规则。随后,每个 projection 用自己的 `stateSchema` 校验 hydration 状态;承载不透明 JSON 的字段需要使用保留其键的校验器。
|
|
66
66
|
|
|
67
67
|
升级绝不拖垮启动,也不会暴露未经证明的折叠结果。版本戳落在 spec `compatibleVersions` 集合内的记录仍可被结构化读取并等待当前检查点重写,但缺失或更旧的 `formatVersion` 绝不匹配当前 Session,因此不能作为 hydrate seed。生命周期匹配的 predecessor title 只能通过上述列表 hint 读取,因为 title 文本在相邻 Session format edge 之间保持不变,并且该 row 仍须通过当前 projection `stateVersion` 与 schema。格式匹配后,缺失的 lineage 字段解码为 unseeded lineage——对非 fork 会话精确无误,seeded 调用方则通不过身份比对、回落冷折叠。仍然通不过 schema 校验的存量记录会按域的 `invalidRecords: 'backup-and-skip'` 策略移出为 `<id>.json.bak.<时间戳>`、连同原因写入日志,并由下一次检查点重建。
|
|
68
68
|
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Service } from "@deepseek-ai/cordis";
|
|
2
2
|
import z from "@deepseek-ai/schemastery";
|
|
3
|
-
import { snapshotJsonValue } from "@deepseek-ai/dsh-util-values";
|
|
3
|
+
import { isJsonValue, snapshotJsonValue } from "@deepseek-ai/dsh-util-values";
|
|
4
4
|
import { SessionLogOffset, SessionSeq } from "@deepseek-ai/dsh-session";
|
|
5
5
|
import { z as z$1 } from "zod";
|
|
6
6
|
import { defineDomain, domainTable } from "@deepseek-ai/dsh-storage-domain";
|
|
@@ -19,15 +19,16 @@ import { defineDomain, domainTable } from "@deepseek-ai/dsh-storage-domain";
|
|
|
19
19
|
/**
|
|
20
20
|
* One persisted checkpoint row (the RFC's `(sessionId, key, ver, seq, val)`
|
|
21
21
|
* minus the two record keys). `val` is the unit's internal state — plain
|
|
22
|
-
* JSON by the unit contract
|
|
23
|
-
*
|
|
24
|
-
* how stale, and a `ver` mismatch
|
|
22
|
+
* JSON by the unit contract. Validation uses the same lossless JSON rules as
|
|
23
|
+
* writes and preserves every state key without cloning. A row is never wrong,
|
|
24
|
+
* only possibly stale: `seq` says exactly how stale, and a `ver` mismatch
|
|
25
|
+
* against the live unit's `stateVersion`
|
|
25
26
|
* discards it at read time (never a migration).
|
|
26
27
|
*/
|
|
27
28
|
const checkpointRow = z$1.object({
|
|
28
29
|
ver: z$1.number().int().nonnegative(),
|
|
29
30
|
seq: z$1.number().int().gte(-1).transform((value) => value === -1 ? -1 : SessionSeq(value)),
|
|
30
|
-
val: z$1.
|
|
31
|
+
val: z$1.custom(isJsonValue, { message: "checkpoint state must be losslessly JSON-serializable" })
|
|
31
32
|
});
|
|
32
33
|
/**
|
|
33
34
|
* The stored-log identity a record is bound to: the immutable header fields
|
|
@@ -172,22 +173,27 @@ var SessionProjectionCache = class extends Service {
|
|
|
172
173
|
}
|
|
173
174
|
/**
|
|
174
175
|
* The zero-I/O listing read: whole values viewed straight from the stored
|
|
175
|
-
* rows (version-matching keys only)
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
176
|
+
* rows (version-matching keys only) of the record bound to the caller's
|
|
177
|
+
* lifecycle. The header is the only identity witness a listing holds, so
|
|
178
|
+
* this face matches the lifecycle identity (`formatVersion`, `createdAt`,
|
|
179
|
+
* `cwd`, `isSeeded`) and not the inherited cut: within one format
|
|
180
|
+
* generation the cut is fixed at fork time, so it distinguishes no
|
|
181
|
+
* lifecycle the other fields do not, and a viewed value never seeds a fold.
|
|
182
|
+
* The view is as stale as the last durable checkpoint but never wrong and
|
|
183
|
+
* never from an unrelated log. Its `asOfSeq` is the lowest watermark among
|
|
184
|
+
* the served rows: the stored record's own position, which the header
|
|
185
|
+
* cannot relate to the log the caller later opens. The Session list
|
|
186
|
+
* therefore labels the block as cached, and the client lets every value the
|
|
187
|
+
* connected Session produces supersede it whatever this number says.
|
|
181
188
|
* @param meta - the listed session's header (identity witness; no log read).
|
|
182
|
-
* @param inheritedEventCount - exact inherited prefix length that completes
|
|
183
|
-
* the checkpoint identity.
|
|
184
189
|
* @param keys - optional projection keys required by the caller's audience.
|
|
185
|
-
* @returns the
|
|
186
|
-
*
|
|
190
|
+
* @returns the viewed block, or `undefined` when no usable row exists for
|
|
191
|
+
* this lifecycle at the current Session format.
|
|
187
192
|
*/
|
|
188
|
-
cachedSnapshot(meta,
|
|
189
|
-
const
|
|
190
|
-
|
|
193
|
+
cachedSnapshot(meta, keys) {
|
|
194
|
+
const expected = lifecycleIdentityOf(meta);
|
|
195
|
+
const record = this.requireTable().get(meta.id);
|
|
196
|
+
if (record === void 0 || !currentLifecycleMatches(record.identity, expected)) return void 0;
|
|
191
197
|
return this.viewRecord(record, keys);
|
|
192
198
|
}
|
|
193
199
|
/**
|
|
@@ -199,36 +205,33 @@ var SessionProjectionCache = class extends Service {
|
|
|
199
205
|
* fact from this Session. The registry still requires the current title
|
|
200
206
|
* projection's row version and schema. No other predecessor projection is
|
|
201
207
|
* exposed: format normalization can change their current meaning, and the
|
|
202
|
-
*
|
|
208
|
+
* {@link cachedSnapshot} / hydration paths continue to reject them.
|
|
203
209
|
* @param meta - authoritative listed Session header.
|
|
204
|
-
* @
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
* with the title unit. The sentinel avoids reusing a sequence that a
|
|
208
|
-
* cardinality-changing Session migration may have remapped.
|
|
210
|
+
* @returns a title-only block at the stored title row's watermark, or
|
|
211
|
+
* `undefined` when the record is current, newer, unrelated, missing, or
|
|
212
|
+
* incompatible with the title unit.
|
|
209
213
|
*/
|
|
210
|
-
cachedPredecessorTitle(meta
|
|
211
|
-
const expected =
|
|
214
|
+
cachedPredecessorTitle(meta) {
|
|
215
|
+
const expected = lifecycleIdentityOf(meta);
|
|
212
216
|
const record = this.requireTable().get(meta.id);
|
|
213
217
|
if (record === void 0 || !predecessorIdentityMatches(record.identity, expected)) return void 0;
|
|
214
|
-
|
|
215
|
-
return title === void 0 ? void 0 : {
|
|
216
|
-
...title,
|
|
217
|
-
asOfSeq: -1
|
|
218
|
-
};
|
|
218
|
+
return this.viewRecord(record, [PREDECESSOR_TITLE_KEY]);
|
|
219
219
|
}
|
|
220
|
-
/**
|
|
220
|
+
/**
|
|
221
|
+
* View selected wire rows as one block bound to the lowest served
|
|
222
|
+
* watermark: the seq every served value has folded through at least. The
|
|
223
|
+
* number is the record's own; whether a consumer may compare it with a
|
|
224
|
+
* live Session's seqs is decided by the face that serves the block, not
|
|
225
|
+
* here.
|
|
226
|
+
*/
|
|
221
227
|
viewRecord(record, keys) {
|
|
222
228
|
const values = this.ctx.sessionProjections.viewCheckpoint(record.rows, keys);
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
for (const key of servedKeys.slice(1)) {
|
|
228
|
-
const row = record.rows[key];
|
|
229
|
-
if (row.seq < asOfSeq) asOfSeq = row.seq;
|
|
229
|
+
let asOfSeq;
|
|
230
|
+
for (const [key, row] of Object.entries(record.rows)) {
|
|
231
|
+
if (!Object.hasOwn(values, key)) continue;
|
|
232
|
+
if (asOfSeq === void 0 || row.seq < asOfSeq) asOfSeq = row.seq;
|
|
230
233
|
}
|
|
231
|
-
return {
|
|
234
|
+
return asOfSeq === void 0 ? void 0 : {
|
|
232
235
|
asOfSeq,
|
|
233
236
|
values
|
|
234
237
|
};
|
|
@@ -357,26 +360,43 @@ var SessionProjectionCache = class extends Service {
|
|
|
357
360
|
return this.table;
|
|
358
361
|
}
|
|
359
362
|
};
|
|
360
|
-
/** Project a header onto the identity fields a
|
|
361
|
-
function
|
|
362
|
-
const cut = SessionLogOffset(inheritedEventCount);
|
|
363
|
-
if (!header.isSeeded && cut !== 0) throw new Error("unseeded projection-cache identity inherited event count must be 0");
|
|
363
|
+
/** Project a header onto the identity fields a header alone can witness. */
|
|
364
|
+
function lifecycleIdentityOf(header) {
|
|
364
365
|
return {
|
|
365
366
|
formatVersion: header.version,
|
|
366
367
|
createdAt: header.createdAt,
|
|
367
368
|
...header.cwd === void 0 ? {} : { cwd: header.cwd },
|
|
368
|
-
isSeeded: header.isSeeded
|
|
369
|
+
isSeeded: header.isSeeded
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
/** Project a header and its exact inherited cut onto the complete fold identity. */
|
|
373
|
+
function identityOf(header, inheritedEventCount) {
|
|
374
|
+
const cut = SessionLogOffset(inheritedEventCount);
|
|
375
|
+
if (!header.isSeeded && cut !== 0) throw new Error("unseeded projection-cache identity inherited event count must be 0");
|
|
376
|
+
return {
|
|
377
|
+
...lifecycleIdentityOf(header),
|
|
369
378
|
inheritedEventCount: cut
|
|
370
379
|
};
|
|
371
380
|
}
|
|
372
381
|
/**
|
|
373
|
-
* Whether a stored record
|
|
374
|
-
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
*
|
|
382
|
+
* Whether a stored record may seed the caller's fold: the current format
|
|
383
|
+
* generation, the same lifecycle, and the same inherited cut. A record folded
|
|
384
|
+
* under another cut encodes that cut in unit states (`schedule`,
|
|
385
|
+
* `subagentCatalog`, `permissions`) and would carry it into the continued
|
|
386
|
+
* fold and the next checkpoint. Absent lineage fields (records admitted via
|
|
387
|
+
* `compatibleVersions` predate them) read as the unseeded lineage: exact for
|
|
388
|
+
* an unseeded caller, while a seeded caller fails the match.
|
|
378
389
|
*/
|
|
379
390
|
function identityMatches(stored, expected) {
|
|
391
|
+
return currentLifecycleMatches(stored, expected) && (stored.inheritedEventCount ?? 0) === expected.inheritedEventCount;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Whether a stored record was folded from the caller's lifecycle at the
|
|
395
|
+
* current Session format. An absent format generation cannot prove the fold
|
|
396
|
+
* semantics and never matches. This is the whole identity a header-only
|
|
397
|
+
* reader can check, and the whole identity a view needs.
|
|
398
|
+
*/
|
|
399
|
+
function currentLifecycleMatches(stored, expected) {
|
|
380
400
|
return stored.formatVersion === expected.formatVersion && lifecycleIdentityMatches(stored, expected);
|
|
381
401
|
}
|
|
382
402
|
/** Match one predecessor cache record to the authoritative listed lifecycle. */
|
|
@@ -385,7 +405,7 @@ function predecessorIdentityMatches(stored, expected) {
|
|
|
385
405
|
}
|
|
386
406
|
/** Match the format-independent fields that distinguish one Session lifecycle. */
|
|
387
407
|
function lifecycleIdentityMatches(stored, expected) {
|
|
388
|
-
return stored.createdAt === expected.createdAt && stored.cwd === expected.cwd && (stored.isSeeded ?? false) === expected.isSeeded
|
|
408
|
+
return stored.createdAt === expected.createdAt && stored.cwd === expected.cwd && (stored.isSeeded ?? false) === expected.isSeeded;
|
|
389
409
|
}
|
|
390
410
|
//#endregion
|
|
391
411
|
export { Config, SessionProjectionCache, SessionProjectionCache as default, checkpointIdentity, checkpointRecord, checkpointRow, projectionCacheDomainSpec };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -74,20 +74,24 @@ export declare class SessionProjectionCache extends Service {
|
|
|
74
74
|
private recordFor;
|
|
75
75
|
/**
|
|
76
76
|
* The zero-I/O listing read: whole values viewed straight from the stored
|
|
77
|
-
* rows (version-matching keys only)
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
77
|
+
* rows (version-matching keys only) of the record bound to the caller's
|
|
78
|
+
* lifecycle. The header is the only identity witness a listing holds, so
|
|
79
|
+
* this face matches the lifecycle identity (`formatVersion`, `createdAt`,
|
|
80
|
+
* `cwd`, `isSeeded`) and not the inherited cut: within one format
|
|
81
|
+
* generation the cut is fixed at fork time, so it distinguishes no
|
|
82
|
+
* lifecycle the other fields do not, and a viewed value never seeds a fold.
|
|
83
|
+
* The view is as stale as the last durable checkpoint but never wrong and
|
|
84
|
+
* never from an unrelated log. Its `asOfSeq` is the lowest watermark among
|
|
85
|
+
* the served rows: the stored record's own position, which the header
|
|
86
|
+
* cannot relate to the log the caller later opens. The Session list
|
|
87
|
+
* therefore labels the block as cached, and the client lets every value the
|
|
88
|
+
* connected Session produces supersede it whatever this number says.
|
|
83
89
|
* @param meta - the listed session's header (identity witness; no log read).
|
|
84
|
-
* @param inheritedEventCount - exact inherited prefix length that completes
|
|
85
|
-
* the checkpoint identity.
|
|
86
90
|
* @param keys - optional projection keys required by the caller's audience.
|
|
87
|
-
* @returns the
|
|
88
|
-
*
|
|
91
|
+
* @returns the viewed block, or `undefined` when no usable row exists for
|
|
92
|
+
* this lifecycle at the current Session format.
|
|
89
93
|
*/
|
|
90
|
-
cachedSnapshot(meta: SessionHeader,
|
|
94
|
+
cachedSnapshot(meta: SessionHeader, keys?: readonly Extract<keyof SessionProjectionMap, string>[]): ProjectionSnapshot | undefined;
|
|
91
95
|
/**
|
|
92
96
|
* Read only a predecessor checkpoint's title as a zero-I/O listing hint.
|
|
93
97
|
*
|
|
@@ -97,16 +101,20 @@ export declare class SessionProjectionCache extends Service {
|
|
|
97
101
|
* fact from this Session. The registry still requires the current title
|
|
98
102
|
* projection's row version and schema. No other predecessor projection is
|
|
99
103
|
* exposed: format normalization can change their current meaning, and the
|
|
100
|
-
*
|
|
104
|
+
* {@link cachedSnapshot} / hydration paths continue to reject them.
|
|
101
105
|
* @param meta - authoritative listed Session header.
|
|
102
|
-
* @
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
* @returns a title-only block at the stored title row's watermark, or
|
|
107
|
+
* `undefined` when the record is current, newer, unrelated, missing, or
|
|
108
|
+
* incompatible with the title unit.
|
|
109
|
+
*/
|
|
110
|
+
cachedPredecessorTitle(meta: SessionHeader): ProjectionSnapshot | undefined;
|
|
111
|
+
/**
|
|
112
|
+
* View selected wire rows as one block bound to the lowest served
|
|
113
|
+
* watermark: the seq every served value has folded through at least. The
|
|
114
|
+
* number is the record's own; whether a consumer may compare it with a
|
|
115
|
+
* live Session's seqs is decided by the face that serves the block, not
|
|
116
|
+
* here.
|
|
107
117
|
*/
|
|
108
|
-
cachedPredecessorTitle(meta: SessionHeader, inheritedEventCount: SessionLogOffset): ProjectionSnapshot | undefined;
|
|
109
|
-
/** View selected wire rows and bind them to their lowest served watermark. */
|
|
110
118
|
private viewRecord;
|
|
111
119
|
/**
|
|
112
120
|
* Hydrate projection cells for an already-prepared Session without another
|
package/lib/types/spec.d.ts
CHANGED
|
@@ -10,20 +10,22 @@
|
|
|
10
10
|
* @module @deepseek-ai/dsh-session-projection-cache/src/spec
|
|
11
11
|
*/
|
|
12
12
|
import { z } from 'zod';
|
|
13
|
+
import type { JsonValue } from '@deepseek-ai/dsh-util-values';
|
|
13
14
|
import { SessionLogOffset, SessionSeq } from '@deepseek-ai/dsh-session';
|
|
14
15
|
import type { SessionId } from '@deepseek-ai/dsh-session';
|
|
15
16
|
/**
|
|
16
17
|
* One persisted checkpoint row (the RFC's `(sessionId, key, ver, seq, val)`
|
|
17
18
|
* minus the two record keys). `val` is the unit's internal state — plain
|
|
18
|
-
* JSON by the unit contract
|
|
19
|
-
*
|
|
20
|
-
* how stale, and a `ver` mismatch
|
|
19
|
+
* JSON by the unit contract. Validation uses the same lossless JSON rules as
|
|
20
|
+
* writes and preserves every state key without cloning. A row is never wrong,
|
|
21
|
+
* only possibly stale: `seq` says exactly how stale, and a `ver` mismatch
|
|
22
|
+
* against the live unit's `stateVersion`
|
|
21
23
|
* discards it at read time (never a migration).
|
|
22
24
|
*/
|
|
23
25
|
export declare const checkpointRow: z.ZodObject<{
|
|
24
26
|
ver: z.ZodNumber;
|
|
25
27
|
seq: z.ZodPipe<z.ZodNumber, z.ZodTransform<-1 | SessionSeq, number>>;
|
|
26
|
-
val: z.
|
|
28
|
+
val: z.ZodCustom<JsonValue, JsonValue>;
|
|
27
29
|
}, z.core.$strip>;
|
|
28
30
|
/**
|
|
29
31
|
* The stored-log identity a record is bound to: the immutable header fields
|
|
@@ -67,7 +69,7 @@ export declare const checkpointRecord: z.ZodObject<{
|
|
|
67
69
|
rows: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
68
70
|
ver: z.ZodNumber;
|
|
69
71
|
seq: z.ZodPipe<z.ZodNumber, z.ZodTransform<-1 | SessionSeq, number>>;
|
|
70
|
-
val: z.
|
|
72
|
+
val: z.ZodCustom<JsonValue, JsonValue>;
|
|
71
73
|
}, z.core.$strip>>;
|
|
72
74
|
}, z.core.$strip>;
|
|
73
75
|
/** One stored per-session checkpoint record, inferred from {@link checkpointRecord}. */
|
|
@@ -114,7 +116,7 @@ export declare const projectionCacheDomainSpec: {
|
|
|
114
116
|
rows: Record<string, {
|
|
115
117
|
ver: number;
|
|
116
118
|
seq: -1 | SessionSeq;
|
|
117
|
-
val:
|
|
119
|
+
val: JsonValue;
|
|
118
120
|
}>;
|
|
119
121
|
}>;
|
|
120
122
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-session-projection-cache",
|
|
3
3
|
"description": "Persisted projection cache (ctx.sessionProjectionCache): durable per-session checkpoint records on the session_projcache storage domain (per-record layout), throttled write-behind, and the cached listing read",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.7-alpha.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -28,21 +28,21 @@
|
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"zod": "^4.4.3",
|
|
31
|
-
"@deepseek-ai/dsh-util-values": "^0.1.
|
|
32
|
-
"@deepseek-ai/schemastery": "^3.18.
|
|
31
|
+
"@deepseek-ai/dsh-util-values": "^0.1.7-alpha.1",
|
|
32
|
+
"@deepseek-ai/schemastery": "^3.18.3"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
36
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
37
|
-
"@deepseek-ai/dsh-session-projection": "^0.1.
|
|
38
|
-
"@deepseek-ai/dsh-storage-domain": "^0.1.
|
|
35
|
+
"@deepseek-ai/cordis": "^4.0.3",
|
|
36
|
+
"@deepseek-ai/dsh-session": "^0.1.7-alpha.1",
|
|
37
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.7-alpha.1",
|
|
38
|
+
"@deepseek-ai/dsh-storage-domain": "^0.1.7-alpha.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@deepseek-ai/dsh-session": "^0.1.
|
|
42
|
-
"@deepseek-ai/dsh-session-projection": "^0.1.
|
|
43
|
-
"@deepseek-ai/
|
|
44
|
-
"@deepseek-ai/
|
|
45
|
-
"@deepseek-ai/dsh-storage-
|
|
46
|
-
"@deepseek-ai/dsh-storage-
|
|
41
|
+
"@deepseek-ai/dsh-session": "^0.1.7-alpha.1",
|
|
42
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.7-alpha.1",
|
|
43
|
+
"@deepseek-ai/dsh-storage": "^0.1.7-alpha.1",
|
|
44
|
+
"@deepseek-ai/cordis": "^4.0.3",
|
|
45
|
+
"@deepseek-ai/dsh-storage-json": "^0.1.7-alpha.1",
|
|
46
|
+
"@deepseek-ai/dsh-storage-domain": "^0.1.7-alpha.1"
|
|
47
47
|
}
|
|
48
48
|
}
|