@deepseek-ai/dsh-session 0.1.5-rc.2 → 0.1.6-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 +8 -6
- package/README.zh.md +14 -12
- package/lib/index.js +128 -41
- package/lib/types/index.d.ts +34 -13
- package/lib/types/index.js +56 -20
- package/lib/types/invariant.js +1 -0
- package/lib/types/known-event-types.d.ts +2 -0
- package/lib/types/known-event-types.js +5 -0
- package/lib/types/surface.d.ts +53 -12
- package/lib/types/surface.js +80 -23
- package/lib/types/types.d.ts +8 -2
- package/package.json +10 -10
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/core/session/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 8d07292287d7300b562751ad74de77397bd5b5db
|
|
6
|
+
README.zh.md: 28b6f1f02838420861a76409046c0e9a12bac1b5
|
package/README.md
CHANGED
|
@@ -49,13 +49,15 @@ session.deriveMessages() // the derived model history
|
|
|
49
49
|
|
|
50
50
|
Surface events (`system/message`, `user/message`, `assistant/message`, `tool/result`) require `surfaceOp` in both typed events and append input. A replacement uses exactly `{ op: 'replace', startSeq, endSeq }`, with inclusive `SessionSeq` endpoints in current surface order. An Assistant message embeds its exact compact provider stream and forbids `sourceEventSeqs`. Known log-only events forbid both metadata fields and never produce a message.
|
|
51
51
|
|
|
52
|
+
Plugins declare content-changing events with `@messageProjection` and register a pure definition through `ctx.sessions.registerMessageProjection()`. Session calls the definition before accepting an event and caches its immutable message updates. Missing definitions reject append and restore; unloading a used definition also blocks cached reads. Detached constructors and `foldSurface(events, projections)` require explicit definitions. Reconstructors pass the fold's `projectedMessages` to `deriveEventMessage()`; live instance methods apply the same projections automatically. [Plugin-owned message projections](../../../.agents/notes/implemented/architecture/2026-09-11-plugin-owned-message-projections.md) defines ownership and offline assembly.
|
|
53
|
+
|
|
52
54
|
Append, seed/restore, and event adoption/snapshot reject any `header.system` and exactly empty optional request-header fields (`tools: []`, `adapterDefaults: {}`) instead of normalizing input. Tool-result `data.error` is allowed only when `message.content[0].isError === true`; failure identity remains optional. Rejected appends do not change the log, derived state, or event feed. Adoption validates event-local metadata but not referenced history or replacement membership.
|
|
53
55
|
|
|
54
56
|
`system/message` holds the rendered system prompt: the first one is surface node 0, the prepared call capability governs admission, with a non-empty rendering consolidated at the first system node on an incapable route or appended after cached history inside a continuing `in-history` series; empty system nodes project to no message, so clearing the prompt requires logged empty replacements of all active system nodes, not just the latest; the surface fold rejects a replacement covering node 0 while it is a `system/message` unless the replacing event is itself a `system/message` over exactly that node, while later system nodes carry no protection and a compaction range may shadow them ([decision](../../../.agents/notes/implemented/architecture/2026-09-02-system-prompt-as-surface-node.md)).
|
|
55
57
|
|
|
56
58
|
### Read the log
|
|
57
59
|
|
|
58
|
-
`session.seq` reads the current log length without materializing an array, and `session.eventAt(seq)` reads one accepted, deeply frozen event by sequence number. `session.snapshotEvents(fromSeq?, toSeqExclusive?)` materializes a frozen, stable snapshot of a half-open range; a complete current snapshot is cached until the next append. Callers that only need a length
|
|
60
|
+
`session.seq` reads the current log length without materializing an array, and `session.eventAt(seq)` reads one accepted, deeply frozen event by sequence number. `session.snapshotEvents(fromSeq?, toSeqExclusive?)` materializes a frozen, stable snapshot of a half-open range; a complete current snapshot is cached until the next append. `eventAt()`, `snapshotEvents()`, and `ownEvents()` are deprecated: existing logic may remain unmigrated for now, but new production calls are prohibited. Repository test files may use these three readers under their scoped lint allowance ([policy](../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md)). Callers that only need a length use `seq`.
|
|
59
61
|
|
|
60
62
|
Session log positions use two numeric types. `SessionSeq` identifies an existing event or inclusive event watermark; `SessionLogOffset` identifies a gap, prefix length, or read boundary and may equal the event count. `SessionSeqCursor` adds the `-1` “no event yet” value, while `OptionalSessionSeq` uses `null` when absence is data. The constructors validate non-negative safe integers, and the brands disappear at runtime, so durable JSON and wire values remain ordinary numbers.
|
|
61
63
|
|
|
@@ -81,11 +83,11 @@ This section explains how the package realizes the behavior above; the observabl
|
|
|
81
83
|
|
|
82
84
|
### Design concept
|
|
83
85
|
|
|
84
|
-
The package is built on event sourcing: a `Session` is an append-only log of typed `SessionEvent`s, and everything else — model history, transcripts, telemetry, titles, persistence — derives from that stream. The surface is a derived projection: an incremental manager validates append candidates, advances the ordered view from committed events, and tracks
|
|
86
|
+
The package is built on event sourcing: a `Session` is an append-only log of typed `SessionEvent`s, and everything else — model history, transcripts, telemetry, titles, persistence — derives from that stream. The surface is a derived projection: an incremental manager validates append candidates, advances the ordered view from committed events, and tracks `replaceGeneration` for positional replacements and `contentGeneration` for replacements and plugin-owned message changes. Model-visible means logged: anything that reaches a model request must be reconstructable from the log. Each model attempt that reaches settlement commits one event: `assistant/message` carries the assembled model-visible message plus its compact timed stream, while `assistant/attempt` retains a failed, retried, cancelled, or stream-error attempt without adding model history. A hard process loss before settlement leaves no durable attempt stream.
|
|
85
87
|
|
|
86
88
|
### Request headers
|
|
87
89
|
|
|
88
|
-
`request/header` stores a full canonical snapshot of the non-history request envelope with reason `initial`, `resume`, `change`, or `series`. An explicit message-series start
|
|
90
|
+
`request/header` stores a full canonical snapshot of the non-history request envelope with reason `initial`, `resume`, `change`, or `series`. An explicit message-series start, a surface replacement, or a plugin-owned message change writes a `series` snapshot when the envelope is unchanged; a simultaneous change uses `startsSeries: true`. Same-series steps, retries, and ordinary later turns inherit the latest snapshot. `adapterDefaults` distinguishes values resolved by the adapter from explicit settings, and `foldRequestHeader()` selects the latest snapshot. This self-contained record supports partial-window rendering and exact reconstruction at the cost of growth per message series; the [reconstructable-requests Agent Note](../../../.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.md) owns the detail.
|
|
89
91
|
|
|
90
92
|
### Source map
|
|
91
93
|
|
|
@@ -105,7 +107,7 @@ Every append uses the shared iterative `snapshotJsonValue()` pass, which reads,
|
|
|
105
107
|
|
|
106
108
|
### Derived history
|
|
107
109
|
|
|
108
|
-
`deriveMessages()` caches
|
|
110
|
+
`deriveMessages()` caches deep-frozen projections and returns a fresh array per call. The four surface event types (`system/message`, `user/message`, `assistant/message`, `tool/result`) supply their recorded message identities and content; an empty-content system node projects to no message. Plugin-owned projections change derived content without mutating recorded messages. Replacements and projection decisions invalidate the cache. Embedded Assistant streams and `assistant/attempt` events remain replay and diagnostic data only.
|
|
109
111
|
|
|
110
112
|
### The request header
|
|
111
113
|
|
|
@@ -135,7 +137,7 @@ The package-level contract is enough for most consumers; read these when you nee
|
|
|
135
137
|
|
|
136
138
|
#### What the model sees
|
|
137
139
|
|
|
138
|
-
The model receives the complete messages from `system/message`, `user/message`, `assistant/message`, and `tool/result` surface entries
|
|
140
|
+
The model receives the complete messages from `system/message`, `user/message`, `assistant/message`, and `tool/result` surface entries with logged projections applied, the system prompt first. Identities, roles, sources, and unmodified blocks retain their original values; projections never mint identities. Direct prompts and injected context remain separate `user/message` events whose sources preserve their attribution. Embedded streams, `assistant/attempt`, boundaries, and other log-only facts add no message.
|
|
139
141
|
|
|
140
142
|
#### Token effect
|
|
141
143
|
|
|
@@ -181,7 +183,7 @@ Logging causes no invalidation, and exact reconstruction preserves request-prefi
|
|
|
181
183
|
These limits define when the session store needs special care. They are current package constraints, not a task backlog.
|
|
182
184
|
|
|
183
185
|
- **`fork()` cuts only at stable boundaries of live sessions** — the selected prefix must end outside an open turn and the source must be in the store; forking a persisted-but-unloaded session is excluded from the fork API.
|
|
184
|
-
- **`SESSION_FORMAT_VERSION` names the [current logical representation](../../../docs/session-format-status.md)** — the current reader rejects retired `header.system` and validates `system/message` payloads and protected-head rewrites. Historical headers and events belong to adjacent format packages; the adjacent migration chain converts supported history before constructing `Session`, and write
|
|
186
|
+
- **`SESSION_FORMAT_VERSION` names the [current logical representation](../../../docs/session-format-status.md)** — the current reader rejects retired `header.system` and validates `system/message` payloads and protected-head rewrites. Historical headers and events belong to adjacent format packages; the adjacent migration chain converts supported history before constructing `Session`, and the write-open path publishes only the current-format successor. Equal-version unknown events require the envelope's explicit `ignorable` marker, which does not promise safe structural migration ([mechanism](../../../.agents/notes/implemented/architecture/2026-08-31-released-session-format-migrations.md)).
|
|
185
187
|
- **`TurnEndReasonMap` omits the ACP-named `refusal` / `max_turn_requests` variants** — producer-gated: they land when an adapter or the loop first emits them.
|
|
186
188
|
- **No session tree beyond fork** — a pi-style entry tree over branched sessions is deferred unless a consumer needs more than boundary-based forking.
|
|
187
189
|
|
package/README.zh.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "面向用户与维护者的事件溯源会话日志与内存存储说明,用于构建、检查或扩展每个 agent
|
|
2
|
+
description: "面向用户与维护者的事件溯源会话日志与内存存储说明,用于构建、检查或扩展每个 agent(智能体)交互背后的持久记录。"
|
|
3
3
|
kind: "package-reference"
|
|
4
4
|
---
|
|
5
5
|
|
|
@@ -9,7 +9,7 @@ kind: "package-reference"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
`dsh-session`
|
|
12
|
+
`dsh-session` 在仅追加的会话日志中记录每个模型可见事实,并从该记录派生模型历史。消费方可以检查、回放、fork 和刷新会话,同时保留历史事件;压缩(compaction)会在活跃对话中隐藏被取代的条目,但不会删除它们。除非添加持久化后端,否则会话仅保留在内存中;持久性检查点会等待配置的后端。agent 需要可重建的会话记录时请选择本包;它本身不调用模型。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
@@ -47,7 +47,9 @@ session.append('user/message', { role: 'user', content: [{ type: 'text', text: '
|
|
|
47
47
|
session.deriveMessages() // the derived model history
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
表层事件(`system/message`、`user/message`、`assistant/message`、`tool/result`)在类型化事件与追加输入中都必须带有 `surfaceOp`。替换操作仅接受 `{ op: 'replace', startSeq, endSeq }`,端点为包含边界的 `SessionSeq`,按当前 surface 顺序解释。
|
|
50
|
+
表层事件(`system/message`、`user/message`、`assistant/message`、`tool/result`)在类型化事件与追加输入中都必须带有 `surfaceOp`。替换操作仅接受 `{ op: 'replace', startSeq, endSeq }`,端点为包含边界的 `SessionSeq`,按当前 surface 顺序解释。assistant 消息会嵌入精确、紧凑的提供方流,并禁止 `sourceEventSeqs`。已知仅日志事件禁止这两个元数据字段,且从不产生消息。
|
|
51
|
+
|
|
52
|
+
插件用 `@messageProjection` 声明修改内容的事件,并通过 `ctx.sessions.registerMessageProjection()` 注册纯处理器。Session 在接受事件前调用处理器,并缓存其不可变消息更新。缺少处理器时拒绝追加和恢复,卸载已经使用的处理器后也会拒绝读取缓存。独立构造函数和 `foldSurface(events, projections)` 必须显式接收处理器。重建函数将折叠结果的 `projectedMessages` 传给 `deriveEventMessage()`,实时实例方法自动应用相同的投影。[插件拥有消息投影](../../../.agents/notes/implemented/architecture/2026-09-11-plugin-owned-message-projections.zh.md)说明职责划分和离线装配。
|
|
51
53
|
|
|
52
54
|
追加、seed/restore 与事件 adoption/snapshot 会拒绝任何 `header.system` 及恰好为空的可选请求头字段(`tools: []`、`adapterDefaults: {}`),而不规范化输入。工具结果的 `data.error` 仅在 `message.content[0].isError === true` 时允许存在;失败标识仍是可选的。被拒绝的追加不会改变日志、派生状态或事件流。Adoption 校验事件局部元数据,但不校验所引用的历史或替换端点是否属于 surface。
|
|
53
55
|
|
|
@@ -55,19 +57,19 @@ session.deriveMessages() // the derived model history
|
|
|
55
57
|
|
|
56
58
|
### 读取日志
|
|
57
59
|
|
|
58
|
-
`session.seq` 无需物化数组即可读取当前日志长度,`session.eventAt(seq)` 按序列号读取单个已接受且深度冻结的事件。`session.snapshotEvents(fromSeq?, toSeqExclusive?)`
|
|
60
|
+
`session.seq` 无需物化数组即可读取当前日志长度,`session.eventAt(seq)` 按序列号读取单个已接受且深度冻结的事件。`session.snapshotEvents(fromSeq?, toSeqExclusive?)` 会物化半开区间的冻结稳定快照;当前完整快照会缓存到下一次追加。`eventAt()`、`snapshotEvents()` 和 `ownEvents()` 已弃用:现有逻辑可以暂不迁移,但禁止新增生产调用。仓库测试文件可以在限定范围的 lint 豁免下使用这三个读取方法([策略](../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.zh.md))。只需要长度的调用方使用 `seq`。
|
|
59
61
|
|
|
60
|
-
会话日志位置使用两种数字类型。`SessionSeq` 标识已有事件或包含端点的事件水位;`SessionLogOffset` 标识间隙、前缀长度或读取边界,并且可以等于事件数量。`SessionSeqCursor` 添加 `-1` 这个“尚无事件”值,`OptionalSessionSeq` 则在缺失本身属于数据时使用 `null
|
|
62
|
+
会话日志位置使用两种数字类型。`SessionSeq` 标识已有事件或包含端点的事件水位;`SessionLogOffset` 标识间隙、前缀长度或读取边界,并且可以等于事件数量。`SessionSeqCursor` 添加 `-1` 这个“尚无事件”值,`OptionalSessionSeq` 则在缺失本身属于数据时使用 `null`。构造函数会校验非负安全整数,brand 在运行时会被擦除,因此持久 JSON 与 wire 值仍是普通数字。
|
|
61
63
|
|
|
62
64
|
### 派生会话的 fork
|
|
63
65
|
|
|
64
66
|
`ctx.sessions.fork(source, boundary?, childSessionId?)` 选取截至 `boundary` 事件序号(含该事件)的源事件(默认:当前最后一个事件),要求所选前缀结束时没有开放轮次,再创建带谱系元数据的实时子会话。必须在轮次中途分支的工具时委派会裁剪到已完成前缀。
|
|
65
67
|
|
|
66
|
-
逻辑 `SessionHeader.isSeeded` 字段报告是否存在 fork 历史,而不公开位置整数。`Session.inheritedEventCount` 保留经过校验的精确 `SessionLogOffset`;`ownEvents()` 返回从该切点开始的事件,`isOwnSeq(seq)`
|
|
68
|
+
逻辑 `SessionHeader.isSeeded` 字段报告是否存在 fork 历史,而不公开位置整数。`Session.inheritedEventCount` 保留经过校验的精确 `SessionLogOffset`;`ownEvents()` 返回从该切点开始的事件,`isOwnSeq(seq)` 只接受已存在且由子会话拥有的位置。底层带 seed 构造必须显式提供 `seed` 与 `inheritedEventCount`,因为构造 seed 可以在继承前缀之后包含子会话自有的设置事件。
|
|
67
69
|
|
|
68
70
|
### 刷新持久状态
|
|
69
71
|
|
|
70
|
-
`ctx.sessions.flush(session)`
|
|
72
|
+
`ctx.sessions.flush(session)` 分发需等待完成的持久性检查点:每个持久化监听器都会刷新,调用在所有监听器结算后完成。需要立即持久性屏障的生产方应等待它,而不是假定延后写入已经排空。
|
|
71
73
|
|
|
72
74
|
-----
|
|
73
75
|
|
|
@@ -81,11 +83,11 @@ session.deriveMessages() // the derived model history
|
|
|
81
83
|
|
|
82
84
|
### 设计理念
|
|
83
85
|
|
|
84
|
-
该包建立在事件溯源之上:`Session` 是类型化 `SessionEvent` 的仅追加日志,其他一切——模型历史、transcript
|
|
86
|
+
该包建立在事件溯源之上:`Session` 是类型化 `SessionEvent` 的仅追加日志,其他一切——模型历史、transcript(文本记录)、遥测、标题、持久化——都从这条流派生。surface 是派生投影:一个增量管理器校验追加候选、根据已提交事件推进有序视图,通过 `replaceGeneration` 跟踪位置替换,通过 `contentGeneration` 跟踪位置替换和插件拥有的消息变更。模型可见即已记录:任何到达模型请求的内容都必须能从日志重建。每个完成结算的模型尝试都会提交一个事件:`assistant/message` 携带组装后的模型可见 message 及其紧凑带时间 stream,`assistant/attempt` 则保留失败、重试、取消或 stream error attempt,且不添加模型历史。如果进程在 settlement 前硬中断,则不会留下持久 attempt stream。
|
|
85
87
|
|
|
86
|
-
###
|
|
88
|
+
### 请求头
|
|
87
89
|
|
|
88
|
-
`request/header`
|
|
90
|
+
`request/header` 存储非历史请求封装的完整规范快照,原因为 `initial`、`resume`、`change` 或 `series`。显式消息序列起点、表层替换或插件拥有的消息变更会在请求封装不变时写入 `series` 快照;同时发生变化时使用 `startsSeries: true`。同一序列内的步骤、重试与普通后续轮次继承最新快照。`adapterDefaults` 区分由适配器解析的值与显式设置,`foldRequestHeader()` 选择最新快照。这种自包含记录以每个消息序列增加存储为代价,支持局部窗口渲染与精确重建;细节由[可重建请求 Agent Note](../../../.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.zh.md)负责。
|
|
89
91
|
|
|
90
92
|
### 源码地图
|
|
91
93
|
|
|
@@ -105,7 +107,7 @@ session.deriveMessages() // the derived model history
|
|
|
105
107
|
|
|
106
108
|
### 派生历史
|
|
107
109
|
|
|
108
|
-
`deriveMessages()`
|
|
110
|
+
`deriveMessages()` 缓存深度冻结的派生消息,每次调用返回新数组。四种 surface 事件类型(`system/message`、`user/message`、`assistant/message`、`tool/result`)提供记录的消息身份和内容,空内容的系统节点不派生消息。插件拥有的投影修改派生内容,不修改记录的消息。替换和投影决策使缓存失效。嵌入式 Assistant stream 与 `assistant/attempt` 事件只保留回放和诊断数据。
|
|
109
111
|
|
|
110
112
|
### 请求头
|
|
111
113
|
|
|
@@ -135,7 +137,7 @@ session.deriveMessages() // the derived model history
|
|
|
135
137
|
|
|
136
138
|
#### 模型看到什么
|
|
137
139
|
|
|
138
|
-
|
|
140
|
+
模型会接收 `system/message`、`user/message`、`assistant/message` 与 `tool/result` surface 条目中的消息,并应用日志中的投影,系统提示词在先。消息标识、角色、来源及未修改的内容块保持不变,投影不生成标识。直接提示词与注入上下文仍是独立的 `user/message` 事件,各事件的来源保留其出处。嵌入式 stream、`assistant/attempt`、边界与其他仅日志事实不添加消息。
|
|
139
141
|
|
|
140
142
|
#### Token 影响
|
|
141
143
|
|
package/lib/index.js
CHANGED
|
@@ -97,6 +97,7 @@ const KNOWN_SESSION_EVENT_TYPES = new Set([
|
|
|
97
97
|
"goal/change",
|
|
98
98
|
"hook/invoked",
|
|
99
99
|
"hook/result",
|
|
100
|
+
"image/offload",
|
|
100
101
|
"llm/retry",
|
|
101
102
|
"llm/retry-started",
|
|
102
103
|
"model/selection",
|
|
@@ -134,6 +135,8 @@ const KNOWN_SESSION_EVENT_TYPES = new Set([
|
|
|
134
135
|
"user/message",
|
|
135
136
|
"web/deepseek-search-llm-request"
|
|
136
137
|
]);
|
|
138
|
+
/** Event types whose model-visible effects require an explicit pure interpreter. */
|
|
139
|
+
const MESSAGE_PROJECTION_EVENT_TYPES = new Set(["image/offload"]);
|
|
137
140
|
//#endregion
|
|
138
141
|
//#region lib/types/surface.js
|
|
139
142
|
/**
|
|
@@ -196,17 +199,18 @@ function isReplacementSurfaceEvent(event) {
|
|
|
196
199
|
/**
|
|
197
200
|
* Project a single event into the LLM message it derives to, or null when it
|
|
198
201
|
* produces none — a non-surface event (attempt, boundary, log-only record) or an
|
|
199
|
-
* empty-content assistant/message (which exists only to host usage).
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* nested in the event wrapper and shared by delivery, durable history, and
|
|
205
|
-
* model requests.
|
|
202
|
+
* empty-content assistant/message (which exists only to host usage). A caller
|
|
203
|
+
* reconstructing model input supplies the same prefix's `projectedMessages`
|
|
204
|
+
* from {@link foldSurface}; without that map this function reads original
|
|
205
|
+
* event content. Session instance methods apply the live projection. Messages
|
|
206
|
+
* are immutable and unchanged content retains its durable identity.
|
|
206
207
|
* @param event - the event to project.
|
|
208
|
+
* @param projectedMessages - message projections from the same log prefix's surface fold.
|
|
207
209
|
* @returns the derived message, or null when the event produces none.
|
|
208
210
|
*/
|
|
209
|
-
function deriveEventMessage(event) {
|
|
211
|
+
function deriveEventMessage(event, projectedMessages) {
|
|
212
|
+
const projected = projectedMessages?.get(event.seq);
|
|
213
|
+
if (projected !== void 0) return projected;
|
|
210
214
|
switch (event.type) {
|
|
211
215
|
case "user/message": return event.data;
|
|
212
216
|
case "system/message":
|
|
@@ -251,7 +255,10 @@ function validateSessionEventData(event, subject) {
|
|
|
251
255
|
function createFoldState() {
|
|
252
256
|
return {
|
|
253
257
|
nodes: [],
|
|
254
|
-
replaceGeneration: 0
|
|
258
|
+
replaceGeneration: 0,
|
|
259
|
+
contentGeneration: 0,
|
|
260
|
+
projectedMessages: /* @__PURE__ */ new Map(),
|
|
261
|
+
projections: /* @__PURE__ */ new Set()
|
|
255
262
|
};
|
|
256
263
|
}
|
|
257
264
|
/** Whether a runtime value is a non-negative safe event sequence. */
|
|
@@ -280,7 +287,7 @@ function surfaceOpOf(event) {
|
|
|
280
287
|
return op;
|
|
281
288
|
}
|
|
282
289
|
/** Validate cited source-event seqs against prior log entries and the replacement range. */
|
|
283
|
-
function
|
|
290
|
+
function assertSourceEventReferences(event, shadowedSeqs) {
|
|
284
291
|
const raw = event.sourceEventSeqs;
|
|
285
292
|
if (event.type === "assistant/message" && raw !== void 0) throw new Error("assistant/message embeds its source stream and cannot carry sourceEventSeqs");
|
|
286
293
|
const sources = /* @__PURE__ */ new Set();
|
|
@@ -309,7 +316,7 @@ function assertProvenance(event, shadowedSeqs) {
|
|
|
309
316
|
function validateSurfaceMetadata(event) {
|
|
310
317
|
const op = surfaceOpOf(event);
|
|
311
318
|
if (op !== void 0 && op !== "append" && (op.startSeq >= event.seq || op.endSeq >= event.seq)) throw new Error(`surface replace at seq ${event.seq}: startSeq and endSeq must reference earlier events`);
|
|
312
|
-
if (op !== void 0)
|
|
319
|
+
if (op !== void 0) assertSourceEventReferences(event, []);
|
|
313
320
|
return op;
|
|
314
321
|
}
|
|
315
322
|
/** Locate one replacement range without mutating the current fold state. */
|
|
@@ -382,16 +389,28 @@ function assertSystemHeadRewrite(event, state, startIdx, shadowedSeqs, events, b
|
|
|
382
389
|
if (event.type !== "system/message" || shadowedSeqs.length !== 1) throw new Error("surface replace: node 0 holds the system prompt and may be rewritten only by a system/message over exactly that node");
|
|
383
390
|
}
|
|
384
391
|
/** Validate one event at its replay boundary and prepare its atomic fold transition. */
|
|
385
|
-
function planSurfaceEvent(state, event, expectedSeq, events, baseSeq) {
|
|
392
|
+
function planSurfaceEvent(state, event, expectedSeq, events, baseSeq, projections) {
|
|
386
393
|
if (event.seq !== expectedSeq) throw new Error(`session event seq ${event.seq} is not contiguous; expected ${expectedSeq}`);
|
|
387
394
|
const surfaceOp = validateSurfaceMetadata(event);
|
|
395
|
+
const projection = projections.find((item) => item.type === event.type);
|
|
396
|
+
if (projection !== void 0) return {
|
|
397
|
+
kind: "project",
|
|
398
|
+
projection,
|
|
399
|
+
messages: projection.project(event, {
|
|
400
|
+
nodes: state.nodes,
|
|
401
|
+
events,
|
|
402
|
+
baseSeq,
|
|
403
|
+
messages: state.projectedMessages
|
|
404
|
+
})
|
|
405
|
+
};
|
|
406
|
+
if (MESSAGE_PROJECTION_EVENT_TYPES.has(event.type)) throw new Error(`session event "${event.type}" requires a message projection; load its owning plugin or supply its projection definition`);
|
|
388
407
|
if (surfaceOp === void 0) return;
|
|
389
408
|
if (surfaceOp === "append") return {
|
|
390
409
|
kind: "append",
|
|
391
410
|
seq: event.seq
|
|
392
411
|
};
|
|
393
412
|
const range = replacementRange(state, surfaceOp);
|
|
394
|
-
|
|
413
|
+
assertSourceEventReferences(event, range.shadowedSeqs);
|
|
395
414
|
assertToolResultRewrite(event, range.shadowedSeqs, events, baseSeq);
|
|
396
415
|
assertSystemHeadRewrite(event, state, range.startIdx, range.shadowedSeqs, events, baseSeq);
|
|
397
416
|
return {
|
|
@@ -403,8 +422,8 @@ function planSurfaceEvent(state, event, expectedSeq, events, baseSeq) {
|
|
|
403
422
|
};
|
|
404
423
|
}
|
|
405
424
|
/** Apply one event and return replacement metadata only when one occurred. */
|
|
406
|
-
function applySurfaceEvent(state, event, expectedSeq, events, baseSeq) {
|
|
407
|
-
return applySurfacePlan(state, planSurfaceEvent(state, event, expectedSeq, events, baseSeq));
|
|
425
|
+
function applySurfaceEvent(state, event, expectedSeq, events, baseSeq, projections) {
|
|
426
|
+
return applySurfacePlan(state, planSurfaceEvent(state, event, expectedSeq, events, baseSeq, projections));
|
|
408
427
|
}
|
|
409
428
|
/** Commit one previously validated surface transition. */
|
|
410
429
|
function applySurfacePlan(state, plan) {
|
|
@@ -412,6 +431,11 @@ function applySurfacePlan(state, plan) {
|
|
|
412
431
|
else if (plan?.kind === "replace") {
|
|
413
432
|
state.nodes.splice(plan.startIdx, plan.endIdx - plan.startIdx + 1, plan.seq);
|
|
414
433
|
state.replaceGeneration += 1;
|
|
434
|
+
state.contentGeneration += 1;
|
|
435
|
+
} else if (plan?.kind === "project") {
|
|
436
|
+
for (const [seq, message] of plan.messages) state.projectedMessages.set(seq, message);
|
|
437
|
+
state.projections.add(plan.projection);
|
|
438
|
+
state.contentGeneration += 1;
|
|
415
439
|
}
|
|
416
440
|
if (plan?.kind !== "replace") return;
|
|
417
441
|
return {
|
|
@@ -424,25 +448,28 @@ function applySurfacePlan(state, plan) {
|
|
|
424
448
|
/**
|
|
425
449
|
* Replay a complete session log through the canonical surface fold.
|
|
426
450
|
* @param events - session events in contiguous seq order.
|
|
451
|
+
* @param projections - pure interpreters for plugin-owned message changes; required definitions must be supplied.
|
|
427
452
|
* @returns detached current sequences and replacement history.
|
|
428
|
-
* @throws when an event violates surface metadata, source
|
|
453
|
+
* @throws when an interpreter is missing or an event violates its projection, surface metadata, source attribution, or replacement rules.
|
|
429
454
|
*/
|
|
430
|
-
function foldSurface(events) {
|
|
455
|
+
function foldSurface(events, projections = []) {
|
|
431
456
|
const state = createFoldState();
|
|
432
457
|
const replacements = [];
|
|
433
458
|
for (const [index, event] of events.entries()) {
|
|
434
|
-
const replacement = applySurfaceEvent(state, event, SessionSeq(index), events, SessionLogOffset(0));
|
|
459
|
+
const replacement = applySurfaceEvent(state, event, SessionSeq(index), events, SessionLogOffset(0), projections);
|
|
435
460
|
if (replacement !== void 0) replacements.push(replacement);
|
|
436
461
|
}
|
|
437
462
|
return {
|
|
438
463
|
nodes: [...state.nodes],
|
|
439
|
-
replacements
|
|
464
|
+
replacements,
|
|
465
|
+
projectedMessages: new Map(state.projectedMessages)
|
|
440
466
|
};
|
|
441
467
|
}
|
|
442
468
|
/** Incremental ordered surface view and append-boundary validator. */
|
|
443
469
|
var SurfaceManager = class {
|
|
444
470
|
log;
|
|
445
471
|
baseSeq;
|
|
472
|
+
projections;
|
|
446
473
|
/** Shared transition state; replacement history is not retained. */
|
|
447
474
|
_state = createFoldState();
|
|
448
475
|
/** Last processed absolute seq. */
|
|
@@ -452,10 +479,12 @@ var SurfaceManager = class {
|
|
|
452
479
|
/**
|
|
453
480
|
* @param log - Contiguous complete log or loaded event window.
|
|
454
481
|
* @param baseSeq - Absolute sequence of the window's first event.
|
|
482
|
+
* @param projections - live borrowed definitions; removing a used definition invalidates further reads.
|
|
455
483
|
*/
|
|
456
|
-
constructor(log, baseSeq = SessionLogOffset(0)) {
|
|
484
|
+
constructor(log, baseSeq = SessionLogOffset(0), projections = []) {
|
|
457
485
|
this.log = log;
|
|
458
486
|
this.baseSeq = baseSeq;
|
|
487
|
+
this.projections = projections;
|
|
459
488
|
this._lastProcessedSeq = baseSeq === 0 ? -1 : SessionSeq(baseSeq - 1);
|
|
460
489
|
}
|
|
461
490
|
/**
|
|
@@ -463,21 +492,40 @@ var SurfaceManager = class {
|
|
|
463
492
|
* @param event - candidate event that has not entered the log yet.
|
|
464
493
|
*/
|
|
465
494
|
validateNext(event) {
|
|
495
|
+
this._assertProjections();
|
|
466
496
|
if (this._lastProcessedSeq < this.baseSeq + this.log.length - 1) this._processDelta();
|
|
467
497
|
const expectedSeq = SessionSeq(this.baseSeq + this.log.length);
|
|
468
498
|
this._pendingPlan = {
|
|
469
499
|
event,
|
|
470
500
|
expectedSeq,
|
|
471
|
-
plan: planSurfaceEvent(this._state, event, expectedSeq, this.log, this.baseSeq)
|
|
501
|
+
plan: planSurfaceEvent(this._state, event, expectedSeq, this.log, this.baseSeq, this.projections)
|
|
472
502
|
};
|
|
473
503
|
}
|
|
474
504
|
/** Monotonic count of folded positional replacements. */
|
|
475
505
|
get replaceGeneration() {
|
|
506
|
+
this._assertProjections();
|
|
476
507
|
if (this._lastProcessedSeq < this.baseSeq + this.log.length - 1) this._processDelta();
|
|
477
508
|
return this._state.replaceGeneration;
|
|
478
509
|
}
|
|
510
|
+
/** Monotonic count of committed changes to existing model-visible content. */
|
|
511
|
+
get contentGeneration() {
|
|
512
|
+
this._assertProjections();
|
|
513
|
+
if (this._lastProcessedSeq < this.baseSeq + this.log.length - 1) this._processDelta();
|
|
514
|
+
return this._state.contentGeneration;
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Project one message with every committed message projection applied.
|
|
518
|
+
* @param event - message-producing or log-only event.
|
|
519
|
+
* @returns its immutable projected message, or null when it produces none.
|
|
520
|
+
*/
|
|
521
|
+
deriveEventMessage(event) {
|
|
522
|
+
this._assertProjections();
|
|
523
|
+
if (this._lastProcessedSeq < this.baseSeq + this.log.length - 1) this._processDelta();
|
|
524
|
+
return deriveEventMessage(event, this._state.projectedMessages);
|
|
525
|
+
}
|
|
479
526
|
/** Surface event sequences in model-visible order. */
|
|
480
527
|
get nodes() {
|
|
528
|
+
this._assertProjections();
|
|
481
529
|
if (this._lastProcessedSeq < this.baseSeq + this.log.length - 1) this._processDelta();
|
|
482
530
|
return this._state.nodes;
|
|
483
531
|
}
|
|
@@ -489,11 +537,18 @@ var SurfaceManager = class {
|
|
|
489
537
|
const event = this.log[index];
|
|
490
538
|
const pending = this._pendingPlan;
|
|
491
539
|
if (pending?.event === event && pending.expectedSeq === seq) applySurfacePlan(this._state, pending.plan);
|
|
492
|
-
else applySurfaceEvent(this._state, event, SessionSeq(seq), this.log, this.baseSeq);
|
|
540
|
+
else applySurfaceEvent(this._state, event, SessionSeq(seq), this.log, this.baseSeq, this.projections);
|
|
493
541
|
if (pending !== void 0 && pending.expectedSeq <= seq) this._pendingPlan = void 0;
|
|
494
542
|
this._lastProcessedSeq = SessionSeq(seq);
|
|
495
543
|
}
|
|
496
544
|
}
|
|
545
|
+
/** Cached messages cannot outlive the definitions that interpreted their log. */
|
|
546
|
+
_assertProjections() {
|
|
547
|
+
const candidate = this._pendingPlan;
|
|
548
|
+
const pending = candidate !== void 0 && this.log[candidate.expectedSeq - this.baseSeq] === candidate.event ? candidate.plan : void 0;
|
|
549
|
+
const required = pending?.kind === "project" ? [...this._state.projections, pending.projection] : this._state.projections;
|
|
550
|
+
for (const projection of required) if (!this.projections.includes(projection)) throw new Error(`session message projection "${projection.type}" was removed or replaced; restore the session with its owning plugin`);
|
|
551
|
+
}
|
|
497
552
|
};
|
|
498
553
|
//#endregion
|
|
499
554
|
//#region lib/types/request-header.js
|
|
@@ -988,7 +1043,7 @@ const attachments = /* @__PURE__ */ new WeakMap();
|
|
|
988
1043
|
var Session = class Session {
|
|
989
1044
|
log = [];
|
|
990
1045
|
/** Single incremental owner of surface acceptance and projection state. */
|
|
991
|
-
surfaceManager
|
|
1046
|
+
surfaceManager;
|
|
992
1047
|
/** The ordered surface over this session's event log. */
|
|
993
1048
|
get surface() {
|
|
994
1049
|
return this.surfaceManager;
|
|
@@ -1039,10 +1094,12 @@ var Session = class Session {
|
|
|
1039
1094
|
* @param seed - optional borrowed replay or fork events.
|
|
1040
1095
|
* @param header - optional borrowed storage metadata.
|
|
1041
1096
|
* @param inheritedEventCount - exact fork-inherited prefix length for a seeded header.
|
|
1097
|
+
* @param projections - pure interpreters for plugin-owned message changes.
|
|
1042
1098
|
* @returns a detached session.
|
|
1099
|
+
* @throws when a seed event requires a missing message interpreter or fails validation.
|
|
1043
1100
|
*/
|
|
1044
|
-
static create(id, seed, header, inheritedEventCount) {
|
|
1045
|
-
return new Session(id, seed, header, "snapshot", inheritedEventCount);
|
|
1101
|
+
static create(id, seed, header, inheritedEventCount, projections) {
|
|
1102
|
+
return new Session(id, seed, header, "snapshot", inheritedEventCount, projections);
|
|
1046
1103
|
}
|
|
1047
1104
|
/**
|
|
1048
1105
|
* Restore a detached session by adopting an independently owned or deeply frozen seed.
|
|
@@ -1055,12 +1112,15 @@ var Session = class Session {
|
|
|
1055
1112
|
* @param header - independently owned storage metadata.
|
|
1056
1113
|
* @param inheritedEventCount - exact fork-inherited prefix length decoded from storage.
|
|
1057
1114
|
* @param eventState - aliasing state carried from the operation that produced the seed.
|
|
1115
|
+
* @param projections - pure interpreters for plugin-owned message changes.
|
|
1058
1116
|
* @returns a restored detached session.
|
|
1117
|
+
* @throws when a seed event requires a missing message interpreter or fails validation.
|
|
1059
1118
|
*/
|
|
1060
|
-
static fromRestore(id, seed, header, inheritedEventCount, eventState) {
|
|
1061
|
-
return new Session(id, seed, header, eventState, inheritedEventCount);
|
|
1119
|
+
static fromRestore(id, seed, header, inheritedEventCount, eventState, projections) {
|
|
1120
|
+
return new Session(id, seed, header, eventState, inheritedEventCount, projections);
|
|
1062
1121
|
}
|
|
1063
|
-
constructor(id, seed, header, mode = "snapshot", suppliedInheritedEventCount) {
|
|
1122
|
+
constructor(id, seed, header, mode = "snapshot", suppliedInheritedEventCount, projections = []) {
|
|
1123
|
+
this.surfaceManager = new SurfaceManager(this.log, SessionLogOffset(0), projections);
|
|
1064
1124
|
const restoredHeader = mode === "snapshot" ? void 0 : validateRestoredSessionHeader(id, header);
|
|
1065
1125
|
if (seed !== void 0) for (const [index, source] of seed.entries()) {
|
|
1066
1126
|
const snapshot = mode === "snapshot" ? snapshotJsonValue(source) : source;
|
|
@@ -1090,6 +1150,8 @@ var Session = class Session {
|
|
|
1090
1150
|
eventsSnapshot;
|
|
1091
1151
|
/**
|
|
1092
1152
|
* Return the immutable event stored at one exact sequence number.
|
|
1153
|
+
* @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited.
|
|
1154
|
+
* See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md).
|
|
1093
1155
|
* @param seq - event sequence number.
|
|
1094
1156
|
* @returns the accepted event, or undefined when the log does not contain it.
|
|
1095
1157
|
*/
|
|
@@ -1100,6 +1162,8 @@ var Session = class Session {
|
|
|
1100
1162
|
* Materialize an immutable snapshot of a half-open event sequence range.
|
|
1101
1163
|
* A full current snapshot is reused until the next append; every previously
|
|
1102
1164
|
* returned snapshot remains stable after later appends.
|
|
1165
|
+
* @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited.
|
|
1166
|
+
* See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md).
|
|
1103
1167
|
* @param fromSeq - non-negative inclusive sequence number; defaults to the log start.
|
|
1104
1168
|
* @param toSeqExclusive - non-negative exclusive sequence number; defaults to the current end.
|
|
1105
1169
|
* @returns a frozen array of the selected deeply frozen events.
|
|
@@ -1113,6 +1177,8 @@ var Session = class Session {
|
|
|
1113
1177
|
}
|
|
1114
1178
|
/**
|
|
1115
1179
|
* Return this Session's events after its fork-inherited prefix.
|
|
1180
|
+
* @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited.
|
|
1181
|
+
* See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md).
|
|
1116
1182
|
* @returns a fresh array containing child-owned events in log order.
|
|
1117
1183
|
*/
|
|
1118
1184
|
ownEvents() {
|
|
@@ -1246,7 +1312,7 @@ var Session = class Session {
|
|
|
1246
1312
|
derived = [];
|
|
1247
1313
|
/** Surface position (nodes projected) the cache has reached. */
|
|
1248
1314
|
derivedNodes = 0;
|
|
1249
|
-
/** {@link SurfaceManager.
|
|
1315
|
+
/** {@link SurfaceManager.contentGeneration} the cache was built under. */
|
|
1250
1316
|
derivedGeneration = 0;
|
|
1251
1317
|
/**
|
|
1252
1318
|
* Derive the LLM message history by walking the ordered sequences of
|
|
@@ -1255,21 +1321,21 @@ var Session = class Session {
|
|
|
1255
1321
|
* append records its `surfaceOp`, so a raw event with no marker (a chunk, a
|
|
1256
1322
|
* turn boundary) is correctly absent, and a compaction `replace` deletes the
|
|
1257
1323
|
* shadowed nodes from the derivation. The projection rules are
|
|
1258
|
-
* {@link deriveEventMessage},
|
|
1324
|
+
* {@link deriveEventMessage}, with logged message projections applied
|
|
1325
|
+
* without changing node membership or message identity.
|
|
1259
1326
|
*
|
|
1260
|
-
* CACHED:
|
|
1261
|
-
*
|
|
1262
|
-
* {@link SessionSurface.replaceGeneration}) rebuilds. The returned array is
|
|
1327
|
+
* CACHED: pure tail growth costs O(new nodes); a replacement or message projection
|
|
1328
|
+
* ({@link SessionSurface.contentGeneration}) rebuilds. The returned array is
|
|
1263
1329
|
* a fresh snapshot per call (later appends never grow an array a caller
|
|
1264
1330
|
* already holds); the `Message` objects in it are SHARED and **deep-frozen**.
|
|
1265
|
-
*
|
|
1266
|
-
*
|
|
1331
|
+
* Unchanged content reuses frozen event data; projected blocks are frozen
|
|
1332
|
+
* derived copies. Consumers cannot mutate the log through either form.
|
|
1267
1333
|
* @returns a fresh array of the shared, frozen derived history.
|
|
1268
1334
|
*/
|
|
1269
1335
|
deriveMessages() {
|
|
1270
1336
|
const surface = this.surface;
|
|
1271
1337
|
const nodes = surface.nodes;
|
|
1272
|
-
const generation = surface.
|
|
1338
|
+
const generation = surface.contentGeneration;
|
|
1273
1339
|
if (generation !== this.derivedGeneration) {
|
|
1274
1340
|
this.derived = [];
|
|
1275
1341
|
this.derivedNodes = 0;
|
|
@@ -1283,13 +1349,13 @@ var Session = class Session {
|
|
|
1283
1349
|
return [...this.derived];
|
|
1284
1350
|
}
|
|
1285
1351
|
/**
|
|
1286
|
-
*
|
|
1287
|
-
*
|
|
1352
|
+
* Project one event with all committed message projections applied.
|
|
1353
|
+
* The original durable event remains unchanged.
|
|
1288
1354
|
* @param event - the event to project.
|
|
1289
1355
|
* @returns the derived message, or null when the event produces none.
|
|
1290
1356
|
*/
|
|
1291
1357
|
deriveEventMessage(event) {
|
|
1292
|
-
return deriveEventMessage(event);
|
|
1358
|
+
return this.surfaceManager.deriveEventMessage(event);
|
|
1293
1359
|
}
|
|
1294
1360
|
};
|
|
1295
1361
|
/** Typed error for session fork rejections. */
|
|
@@ -1311,6 +1377,27 @@ var SessionForkError = class extends Error {
|
|
|
1311
1377
|
var SessionStore = class extends Service {
|
|
1312
1378
|
store = /* @__PURE__ */ new Map();
|
|
1313
1379
|
counter = 0;
|
|
1380
|
+
projections = [];
|
|
1381
|
+
/** Borrowed definitions for detached replay; contributions live until their registering fibers unload. */
|
|
1382
|
+
get messageProjections() {
|
|
1383
|
+
return this.projections;
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Register one event interpreter for live creation, restore, and fork.
|
|
1387
|
+
* Disposing the contribution makes sessions that used it refuse further derivation.
|
|
1388
|
+
* @param projection - pure definition owned by the event's plugin.
|
|
1389
|
+
* @returns the fiber-owned disposer.
|
|
1390
|
+
* @throws when another definition already owns this event type.
|
|
1391
|
+
*/
|
|
1392
|
+
registerMessageProjection(projection) {
|
|
1393
|
+
if (this.projections.some((item) => item.type === projection.type)) throw new Error(`session message projection "${projection.type}" is already registered`);
|
|
1394
|
+
return this.ctx.effect(() => {
|
|
1395
|
+
this.projections.push(projection);
|
|
1396
|
+
return () => {
|
|
1397
|
+
this.projections.splice(this.projections.indexOf(projection), 1);
|
|
1398
|
+
};
|
|
1399
|
+
}, "sessions.registerMessageProjection()");
|
|
1400
|
+
}
|
|
1314
1401
|
constructor(ctx) {
|
|
1315
1402
|
super(ctx, "sessions");
|
|
1316
1403
|
ctx.inject(["typert"], (typeCtx) => {
|
|
@@ -1382,7 +1469,7 @@ var SessionStore = class extends Service {
|
|
|
1382
1469
|
const { eventState } = options;
|
|
1383
1470
|
switch (eventState) {
|
|
1384
1471
|
case "detached":
|
|
1385
|
-
case "shared-frozen": return Session.fromRestore(sessionId, options.seed, options.meta, options.inheritedEventCount, eventState);
|
|
1472
|
+
case "shared-frozen": return Session.fromRestore(sessionId, options.seed, options.meta, options.inheritedEventCount, eventState, this.projections);
|
|
1386
1473
|
case void 0: break;
|
|
1387
1474
|
/* v8 ignore next -- closed-union exhaustiveness guard */
|
|
1388
1475
|
default: assertNever(eventState, "SessionStore.prepare event state");
|
|
@@ -1401,7 +1488,7 @@ var SessionStore = class extends Service {
|
|
|
1401
1488
|
...meta?.delegationDepth === void 0 ? {} : { delegationDepth: meta.delegationDepth },
|
|
1402
1489
|
...meta?.agentPreset === void 0 ? {} : { agentPreset: meta.agentPreset }
|
|
1403
1490
|
};
|
|
1404
|
-
return Session.create(sessionId, seed, header, options?.inheritedEventCount);
|
|
1491
|
+
return Session.create(sessionId, seed, header, options?.inheritedEventCount, this.projections);
|
|
1405
1492
|
}
|
|
1406
1493
|
/**
|
|
1407
1494
|
* Enter a {@link prepare}d session into the store: install the module-private
|