dsh-plugin-dev-kb 1.0.9 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.en.md +6 -6
- package/README.md +6 -6
- package/kb/INDEX.md +4 -2
- package/kb/README.md +4 -4
- package/kb/extra/AGENTS.md +1 -1
- package/kb/extra/cookbook/adding-a-remote-api.md +197 -0
- package/kb/extra/cookbook/adding-a-remote-api.zh.md +197 -0
- package/kb/extra/deepseek-llm-api-wire-extensions.md +18 -14
- package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +18 -14
- package/kb/extra/event-producer-consumer.md +44 -43
- package/kb/extra/event-producer-consumer.zh.md +47 -46
- package/kb/extra/i18n/style-samples.md +2 -2
- package/kb/extra/module-graph.md +444 -991
- package/kb/extra/module-graph.zh.md +446 -993
- package/kb/extra/subsystems/agent-team.md +4 -3
- package/kb/extra/subsystems/agent-team.zh.md +4 -3
- package/kb/extra/subsystems/attachment.md +157 -4
- package/kb/extra/subsystems/attachment.zh.md +157 -4
- package/kb/extra/subsystems/feedback.md +2 -2
- package/kb/extra/subsystems/feedback.zh.md +2 -2
- package/kb/extra/testing.md +6 -2
- package/kb/extra/testing.zh.md +6 -2
- package/kb/extra/web-styling.md +4 -0
- package/kb/extra/web-styling.zh.md +4 -0
- package/kb/meta/search-index.json +135 -111
- package/kb/meta/site-pages.txt +184 -182
- package/kb/meta/source.json +5 -5
- package/kb/meta/topics.md +4 -4
- package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +3 -2
- package/kb/site/develop/practice/llm-adapter.md +4 -3
- package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +3 -2
- package/kb/site/en/develop/practice/llm-adapter.md +4 -3
- package/kb/site/en/guide/network-proxy.md +87 -0
- package/kb/site/en/guide/providers.md +70 -17
- package/kb/site/en/guide/schedule.md +2 -0
- package/kb/site/en/reference/agent-lifecycle.md +5 -3
- package/kb/site/en/reference/api-gateway.md +2 -2
- package/kb/site/en/reference/capability-seams.md +14 -7
- package/kb/site/en/reference/config-catalog.md +157 -230
- package/kb/site/en/reference/cookbook/adding-a-package.md +1 -2
- package/kb/site/en/reference/cookbook/adding-a-settings-card.md +10 -8
- package/kb/site/en/reference/cookbook/extension-cookbook.md +15 -12
- package/kb/site/en/reference/index.md +11 -4
- package/kb/site/en/reference/persistence-catalog.md +91 -70
- package/kb/site/en/reference/subsystems/code-runtime.md +7 -3
- package/kb/site/en/reference/subsystems/commands.md +25 -16
- package/kb/site/en/reference/subsystems/compaction.md +9 -9
- package/kb/site/en/reference/subsystems/conversation.md +11 -4
- package/kb/site/en/reference/subsystems/core.md +96 -15
- package/kb/site/en/reference/subsystems/credentials.md +5 -4
- package/kb/site/en/reference/subsystems/goal.md +1 -1
- package/kb/site/en/reference/subsystems/index.md +1 -1
- package/kb/site/en/reference/subsystems/llm-streaming.md +24 -3
- package/kb/site/en/reference/subsystems/permission-presets.md +5 -5
- package/kb/site/en/reference/subsystems/persistence.md +185 -194
- package/kb/site/en/reference/subsystems/plan.md +3 -3
- package/kb/site/en/reference/subsystems/sandbox.md +2 -0
- package/kb/site/en/reference/subsystems/schedule.md +9 -3
- package/kb/site/en/reference/subsystems/session-projection.md +50 -24
- package/kb/site/en/reference/subsystems/session-query.md +19 -13
- package/kb/site/en/reference/subsystems/session-reference.md +25 -1
- package/kb/site/en/reference/subsystems/session-telemetry.md +6 -5
- package/kb/site/en/reference/subsystems/session-title.md +6 -6
- package/kb/site/en/reference/subsystems/session.md +156 -73
- package/kb/site/en/reference/subsystems/settings.md +30 -12
- package/kb/site/en/reference/subsystems/skills.md +1 -1
- package/kb/site/en/reference/subsystems/slots.md +1 -0
- package/kb/site/en/reference/subsystems/storage.md +28 -5
- package/kb/site/en/reference/subsystems/subagent.md +43 -97
- package/kb/site/en/reference/subsystems/system-prompt.md +16 -3
- package/kb/site/en/reference/subsystems/token-meter.md +2 -2
- package/kb/site/en/reference/subsystems/typert.md +18 -18
- package/kb/site/en/reference/subsystems/web-client.md +1 -1
- package/kb/site/en/reference/subsystems/workspace.md +8 -8
- package/kb/site/en/reference/tool-catalog.md +10 -64
- package/kb/site/guide/network-proxy.md +87 -0
- package/kb/site/guide/providers.md +70 -17
- package/kb/site/guide/schedule.md +2 -0
- package/kb/site/reference/agent-lifecycle.md +5 -3
- package/kb/site/reference/api-gateway.md +2 -2
- package/kb/site/reference/capability-seams.md +14 -7
- package/kb/site/reference/config-catalog.md +156 -229
- package/kb/site/reference/cookbook/adding-a-package.md +1 -2
- package/kb/site/reference/cookbook/adding-a-settings-card.md +10 -8
- package/kb/site/reference/cookbook/extension-cookbook.md +15 -12
- package/kb/site/reference/index.md +11 -4
- package/kb/site/reference/persistence-catalog.md +91 -70
- package/kb/site/reference/subsystems/code-runtime.md +7 -3
- package/kb/site/reference/subsystems/commands.md +25 -16
- package/kb/site/reference/subsystems/compaction.md +9 -9
- package/kb/site/reference/subsystems/conversation.md +11 -4
- package/kb/site/reference/subsystems/core.md +96 -15
- package/kb/site/reference/subsystems/credentials.md +5 -4
- package/kb/site/reference/subsystems/goal.md +1 -1
- package/kb/site/reference/subsystems/index.md +1 -1
- package/kb/site/reference/subsystems/llm-streaming.md +24 -3
- package/kb/site/reference/subsystems/permission-presets.md +5 -5
- package/kb/site/reference/subsystems/persistence.md +184 -193
- package/kb/site/reference/subsystems/plan.md +3 -3
- package/kb/site/reference/subsystems/schedule.md +9 -3
- package/kb/site/reference/subsystems/session-projection.md +50 -24
- package/kb/site/reference/subsystems/session-query.md +19 -13
- package/kb/site/reference/subsystems/session-reference.md +25 -1
- package/kb/site/reference/subsystems/session-telemetry.md +6 -5
- package/kb/site/reference/subsystems/session-title.md +6 -6
- package/kb/site/reference/subsystems/session.md +156 -73
- package/kb/site/reference/subsystems/settings.md +30 -12
- package/kb/site/reference/subsystems/skills.md +1 -1
- package/kb/site/reference/subsystems/slots.md +1 -0
- package/kb/site/reference/subsystems/storage.md +28 -5
- package/kb/site/reference/subsystems/subagent.md +43 -97
- package/kb/site/reference/subsystems/system-prompt.md +16 -3
- package/kb/site/reference/subsystems/token-meter.md +2 -2
- package/kb/site/reference/subsystems/typert.md +18 -18
- package/kb/site/reference/subsystems/web-client.md +1 -1
- package/kb/site/reference/subsystems/workspace.md +8 -8
- package/kb/site/reference/tool-catalog.md +9 -63
- package/package.json +2 -2
- package/skills/dsh-plugin-dev-kb.md +4 -4
|
@@ -5,31 +5,114 @@ outline: [2,3]
|
|
|
5
5
|
|
|
6
6
|
# 会话持久化
|
|
7
7
|
|
|
8
|
-
事件日志的**持久性 seam**。[session.md](./session.md) 描述了内存中的 `Session`:仅追加的 `SessionEvent` 日志即为真源。本页描述如何使该日志持久化:抽象的 `SessionPersistence`
|
|
8
|
+
事件日志的**持久性 seam**。[session.md](./session.md) 描述了内存中的 `Session`:仅追加的 `SessionEvent` 日志即为真源。本页描述如何使该日志持久化:抽象的 `SessionPersistence` 服务、它的提供方模型与随产品交付的 JSONL 后端、flush 检查点、崩溃恢复,以及随日志一同存储的元数据头。日志承载的事件词汇在生成的[持久化日志事件目录](../persistence-catalog.md)中逐项列举。
|
|
9
9
|
|
|
10
|
-
该 seam 是一个[能力 seam](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-06-13-capability-seams.zh.md):一个抽象服务([dsh-session-persistence](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/session/session-persistence),`ctx.sessionPersistence`)在现有 `SessionEvent`
|
|
10
|
+
该 seam 是一个[能力 seam](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-06-13-capability-seams.zh.md):一个抽象服务([dsh-session-persistence](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/session/session-persistence),`ctx.sessionPersistence`)在现有 `SessionEvent` 上暴露 `create`/`open`/`stat`/`list`——**没有平行的持久化事件类型**——其中 `create` 与 `open` 返回逐会话的 `SessionHandle`(`read`/`append`/`flush`/`close`),它承载全部日志访问与单写者所有权。仓库随产品交付 [dsh-session-persistence-jsonl](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/session/session-persistence-jsonl) 作为其 provider;仓库外 provider 可以实现同一服务约定。见[基于句柄的持久化 Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-08-27-handle-based-session-persistence.zh.md)与 [session-persistence Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-06-14-session-persistence.zh.md)。
|
|
11
|
+
|
|
12
|
+
## `SessionHandle`——通向已存储会话的一条打开通道
|
|
13
|
+
|
|
14
|
+
每一次日志读写都经由句柄流动,绝不经由按 id 寻址的服务方法:句柄是跨进程写租约把守的那扇唯一的门。一种句柄类型同时服务两种访问——在 `read` 句柄上执行修改是运行时的 `SessionReadOnlyError`,而非类型层面的拆分——而进程内单写者所有权使得在已有活跃持有者时第二次 `open(id, 'write')` 以 `SessionAlreadyOwnedError` 拒绝。
|
|
15
|
+
|
|
16
|
+
```ts type-equiv
|
|
17
|
+
/**
|
|
18
|
+
* One open channel onto a stored session. A handle is single-owner state, not
|
|
19
|
+
* a shared service: `read` never backtracks below what this handle already
|
|
20
|
+
* observed, a `write` handle reads its own successful appends, and `close()`
|
|
21
|
+
* is the one teardown (idempotent, uncancellable; `Symbol.asyncDispose`
|
|
22
|
+
* delegates to it). Every operation on a closed handle rejects with
|
|
23
|
+
* `SessionHandleClosedError`.
|
|
24
|
+
*
|
|
25
|
+
* Freshness across handles: once an `append` or `flush` resolves on a write
|
|
26
|
+
* handle, every read STARTED afterwards on the same backend instance — on any
|
|
27
|
+
* handle, or through `stat`/`list` — observes at least that prefix.
|
|
28
|
+
* Reads concurrent with a mutation carry no ordering promise beyond the valid
|
|
29
|
+
* contiguous prefix.
|
|
30
|
+
*/
|
|
31
|
+
interface SessionHandle extends AsyncDisposable {
|
|
32
|
+
/** The stored session this handle addresses. */
|
|
33
|
+
readonly id: SessionId
|
|
34
|
+
/** The immutable stored header, fixed at `create`/`open`. */
|
|
35
|
+
readonly header: SessionHeader
|
|
36
|
+
/**
|
|
37
|
+
* Exact fork-inherited prefix length stored with the log; `0` when
|
|
38
|
+
* `header.isSeeded` is false. Storage metadata paired with the header for
|
|
39
|
+
* every body read, never part of the replayable event log.
|
|
40
|
+
*/
|
|
41
|
+
readonly inheritedEventCount: SessionLogOffset
|
|
42
|
+
/** Whether this handle may mutate the log. */
|
|
43
|
+
readonly access: SessionAccess
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Read a slice of the valid contiguous logical log. The slice is a legal log
|
|
47
|
+
* prefix segment: a torn physical tail is never returned, and repeated reads
|
|
48
|
+
* on this handle never observe an older state than a prior read.
|
|
49
|
+
* @param offset - first logical event seq to include; defaults to `0`.
|
|
50
|
+
* @param length - maximum number of events to return; defaults to the rest
|
|
51
|
+
* of the log. An offset at or past the end returns an empty list.
|
|
52
|
+
* @param options - optional cancellation.
|
|
53
|
+
* @returns the events with `seq >= offset`, at most `length` of them.
|
|
54
|
+
*/
|
|
55
|
+
read(offset?: number, length?: number, options?: SessionHandleReadOptions): Promise<readonly SessionEvent[]>
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Append a contiguous batch continuing the current logical end. The first
|
|
59
|
+
* event's `seq` MUST equal the stored next-seq; committed events are never
|
|
60
|
+
* rewritten. Persistence is best-effort: on resolution the batch is
|
|
61
|
+
* accepted, ordered, and visible to reads on this backend instance, but
|
|
62
|
+
* only a resolved {@link flush} promises it survives a crash — a backend
|
|
63
|
+
* may buffer or batch physical writes behind append. Rejects with
|
|
64
|
+
* `SessionReadOnlyError` on a read handle and `SessionOwnershipLostError`
|
|
65
|
+
* when write ownership is gone.
|
|
66
|
+
* @param events - the contiguous batch, in seq order.
|
|
67
|
+
* @param options - optional cancellation observed before the write starts.
|
|
68
|
+
*/
|
|
69
|
+
append(events: readonly SessionEvent[], options?: SessionHandleAppendOptions): Promise<void>
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* The durability barrier — the one operation that promises storage: on
|
|
73
|
+
* resolution every acknowledged append is durable and the session is
|
|
74
|
+
* materialized for other processes; an empty created session becomes
|
|
75
|
+
* durably listable here. Callers that must survive a crash flush; a backend
|
|
76
|
+
* whose `append` already persists on resolution treats this as
|
|
77
|
+
* materialize-if-needed. Rejects with `SessionReadOnlyError` on a read
|
|
78
|
+
* handle.
|
|
79
|
+
* @param options - optional cancellation observed before the barrier starts.
|
|
80
|
+
*/
|
|
81
|
+
flush(options?: SessionHandleFlushOptions): Promise<void>
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Release the handle: a read handle frees local resources; a write handle
|
|
85
|
+
* completes pending durability and releases write ownership. Idempotent,
|
|
86
|
+
* asynchronous, and deliberately not cancellable.
|
|
87
|
+
*/
|
|
88
|
+
close(): Promise<void>
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
已创建的会话自 `create` 完成之刻起即可在本进程内被观察到,而后端可以把物理实体化(纯粹的优化)推迟到第一次 `append` 或 `flush`;其他进程只能看到已实体化的会话,一个在崩溃前从未实体化的会话等于从未存在。
|
|
11
93
|
|
|
12
94
|
## flush 检查点
|
|
13
95
|
|
|
14
|
-
`session/event`
|
|
96
|
+
`session/event` 是一个*同步*通知;挂载的后端按会话 id 把它路由进活跃写句柄的有界 write-behind 窗口,而不阻塞生产方(后端一次性安装这些监听器,因为持久化已保证每个 id 只有一个活跃写句柄)。第一个待处理事件会开启固定的内部批处理窗口,后续事件会加入但不会重置其截止时间。窗口到期后会通过该会话的写句柄启动一次持久化 `append`;该次写入期间接纳的事件会获得自己的截止时间,并形成后续批次。`session/flush` 会取消等待并排空至完全停稳,因此循环仍将其用作在领取下一个普通轮次之前的顺序与错误观察检查点。后台写入被拒绝时会按序保留对应事件、暂停自动路径,并通过 logger 报告;下一次显式 flush 会重试,并向其调用方响亮地拒绝。`session/disposed` 会执行同样的最终排空并关闭句柄,而 `close()` 本身会经由仍然打开的存储排空已路由的缓冲,因此后端 teardown 的关闭清扫不丢任何数据。该窗口只限制有意的批处理等待,不限制事件循环调度或后端完成持久化的延迟([决策](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-08-08-bounded-session-persistence-write-batching.zh.md))。
|
|
15
97
|
|
|
16
98
|
## 崩溃恢复保留被中断的轮次
|
|
17
99
|
|
|
18
|
-
|
|
100
|
+
一个在轮次中途崩溃的日志以打开的 `turn/start` 而无 `turn/end` 结束。持久化**不会**截断或修复它:在长周期任务中,单个轮次可能非常庞大(许多步骤、大量工具输出),而这些事件在崩溃前已被持久追加。它返回物理上有效的连续日志;只有撕裂物理尾部——属于一次从未完成的 append——中不完整的碎片会被丢弃:从中恢复的完整记录(JSONL 后端会部分解码撕裂的 Zstandard 帧)由写路径在句柄的第一次新 append 之前持久重写。修复是读方的职责:resume(agent-loop)通过其写句柄读取已存储的日志,计算 `interruptedTurnClosers`——缺失的工具错误、任何未闭合的 `step/end`,以及一个合成的 `turn/end { reason: { kind: 'interrupted' } }`——并在发布 Session 之前把它们作为普通批次通过同一句柄追加。`interrupted` 是唯一一个不由循环发出的 `TurnEndReason`(见 [session.md](./session.md#why-a-turn-ended-turnendreasonmap))。
|
|
19
101
|
|
|
20
|
-
|
|
102
|
+
因此修复只在写所有权之下写入:活跃会话的写句柄由其生命周期所有者持有,故并发的 `open(id, 'write')` 会以 `SessionAlreadyOwnedError` 拒绝,而不是让修复与活跃轮次竞速。只读观察方(session-query)仅在内存中用同样的闭合事件配平被中断的冷日志,不回写任何内容。
|
|
21
103
|
|
|
22
|
-
`
|
|
104
|
+
只读观察即 `open(id, 'read')`:句柄提供经过验证的连续前缀切片,绝不返回撕裂尾部,且同一句柄上的重复读取绝不会观察到比先前读取更旧的状态。持久化侧不存在已准备 Session 缓存:session-query 拥有自己的冷读缓存,按 `stat().revision` 变更令牌为每个 id 缓存一个已配平的冷 Session,仅在令牌变化时重新读取。该生命周期由[基于句柄的持久化 Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-08-27-handle-based-session-persistence.zh.md)定义;[Session 准备阶段决策](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-08-05-session-preparation.zh.md)记录仍然保留的发布边界 `SessionPreparation`。
|
|
23
105
|
|
|
24
|
-
## `SessionLocation
|
|
106
|
+
## `SessionLocation`——拒绝诊断的产物目标
|
|
25
107
|
|
|
26
|
-
`
|
|
108
|
+
`SessionLocation` 不是面向消费者的查询:日志访问走会话句柄的 `read`。它仅作为拒绝诊断存在,使 `SessionFormatUnsupportedError` 能指出本构建拒绝解读的原始日志。JSONL 提供其项目/会话目录内 transcript(文本记录)的绝对路径;没有逐会话工件的后端则不提供。
|
|
27
109
|
|
|
28
110
|
```ts type-equiv
|
|
29
111
|
/**
|
|
30
|
-
* A backend-resolved, per-session local artifact location.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
112
|
+
* A backend-resolved, per-session local artifact location. Carried only by
|
|
113
|
+
* refusal diagnostics ({@link SessionFormatUnsupportedError}) so a user can
|
|
114
|
+
* find the raw log a build refused to interpret; it is not a consumer-facing
|
|
115
|
+
* query — log access goes through a session handle's `read`.
|
|
33
116
|
*/
|
|
34
117
|
interface SessionLocation {
|
|
35
118
|
/** Backend-specific artifact kind, for example `jsonl`. */
|
|
@@ -43,7 +126,7 @@ interface SessionLocation {
|
|
|
43
126
|
|
|
44
127
|
## `SessionHeader`:日志旁的元数据
|
|
45
128
|
|
|
46
|
-
|
|
129
|
+
每个会话的元数据与事件日志**分开**存储:header 携带格式版本、cwd 与 `isSeeded` 谱系 bit,含正文的存储值则在其旁边单独携带精确 inherited cut。二者都不进入 `SessionEventMap`,也不会到达 `deriveMessages()`。logical header 通过 `session.header` 附加,Session 则以 `inheritedEventCount` 暴露其 cut。
|
|
47
130
|
|
|
48
131
|
源码:[`packages/core/session/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
|
|
49
132
|
|
|
@@ -53,11 +136,10 @@ interface SessionLocation {
|
|
|
53
136
|
*/
|
|
54
137
|
interface SessionHeader {
|
|
55
138
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* (no migration — see the constant).
|
|
139
|
+
* Current logical format version, stamped from {@link SESSION_FORMAT_VERSION}.
|
|
140
|
+
* Historical physical headers are translated before entering this interface.
|
|
59
141
|
*/
|
|
60
|
-
readonly version:
|
|
142
|
+
readonly version: typeof SESSION_FORMAT_VERSION
|
|
61
143
|
/** The session's id (mirrors the {@link Session}'s id). */
|
|
62
144
|
readonly id: SessionId
|
|
63
145
|
/** Non-negative safe-integer Unix epoch milliseconds when the session was created. */
|
|
@@ -67,10 +149,10 @@ interface SessionHeader {
|
|
|
67
149
|
/** The session this one was forked from (seed lineage), if any. */
|
|
68
150
|
readonly parentSession?: SessionId
|
|
69
151
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
152
|
+
* Whether this Session contains a fork-inherited event prefix. The exact prefix
|
|
153
|
+
* length is Session state rather than ordinary header metadata.
|
|
72
154
|
*/
|
|
73
|
-
readonly
|
|
155
|
+
readonly isSeeded: boolean
|
|
74
156
|
/**
|
|
75
157
|
* Coarse product classification for a session created as a subagent child.
|
|
76
158
|
* This is presentation metadata, not proof that the child is continuable.
|
|
@@ -94,11 +176,11 @@ interface SessionHeader {
|
|
|
94
176
|
|
|
95
177
|
## 格式拒绝:本构建无法可靠读取的日志
|
|
96
178
|
|
|
97
|
-
后端用 `SessionFormatUnsupportedError` 拒绝无法可靠解读的日志,它与 `SessionPersistenceCorruptionError`
|
|
179
|
+
后端用 `SessionFormatUnsupportedError` 拒绝无法可靠解读的日志,它与 `SessionPersistenceCorruptionError` 区分,因为数据没有损坏。`stat` 与 `list` 会对最高规范 generation 分类,并在不读取或改变正文的前提下转换受支持的历史 header。`open` 会在按 id 串行化的区段内运行构建时静态确定的相邻迁移链,再返回句柄;每个源路径、字节与 inode 都保持不变,并且只排他发布最终的当前 generation。即使仍有较旧的可读 generation,最高的未来 generation 仍会导致拒绝。当前 v2 恢复会保留已安装扩展和带 `ignorable: true` 的未知事件;历史 v0/v1 迁移则会拒绝未知类型,即使它带有 ignorable 标记。后端为每个会话保留独立文件时,消息附上选定的原始日志路径。JSONL 后端把已发布 v0 或 v1 迁移到当前 v2,并在解读其版本专属字段或事件行前拒绝未来版本。仓库外后端必须在自己的物理格式入口提供等价的仅当前句柄值与方向感知拒绝。[已发布格式迁移决策](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-08-31-released-session-format-migrations.zh.md)负责迁移链与不可变发布规则。
|
|
98
180
|
|
|
99
181
|
## `CreateSessionOptions`:seed 与元数据
|
|
100
182
|
|
|
101
|
-
通过 store 创建 `Session` 时会接收 `seed`(初始回放或 fork
|
|
183
|
+
通过 store 创建 `Session` 时会接收 `seed`(初始回放或 fork 历史)、可选的精确 `inheritedEventCount` 与 `meta`(store 整合进 `SessionHeader` 的存储层字段)。store 填充 `version`/`id` 并为 `createdAt` 提供默认值;调用方可以提供已校验的绝对 `cwd`、`parentSession` 谱系、`isSeeded` 谱系标记、可选的粗粒度 `origin`、`delegationDepth`、用于组装该 agent(智能体)的 `agentPreset` 以及已有的 `createdAt`。seeded 创建必须显式提供与 inherited prefix 完全相等的 seed 和精确 cut;constructor 会先在该 cut 追加 child-owned tagged end-seed marker,setup 再添加 child-owned event。`origin: 'subagent'` 让产品导航能够隐藏重复的 child 行;它不证明描述符有效,也不证明 child 可以恢复。
|
|
102
184
|
|
|
103
185
|
```ts type-equiv
|
|
104
186
|
/**
|
|
@@ -110,14 +192,20 @@ interface CreateSessionOptions {
|
|
|
110
192
|
/** Initial replay or fork history supplied at construction. */
|
|
111
193
|
readonly seed?: readonly SessionEvent[]
|
|
112
194
|
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
195
|
+
* Exact fork-inherited prefix length when `meta.isSeeded` is true. In v2 the
|
|
196
|
+
* constructor seed is exactly this inherited prefix; the constructor
|
|
197
|
+
* appends the child-owned tagged marker at the cut.
|
|
198
|
+
*/
|
|
199
|
+
readonly inheritedEventCount?: SessionLogOffset
|
|
200
|
+
/**
|
|
201
|
+
* Storage metadata read once before publication. `isSeeded` marks fork
|
|
202
|
+
* lineage; supplying replay history alone does not make it inherited.
|
|
115
203
|
*/
|
|
116
204
|
readonly meta?: {
|
|
117
205
|
readonly cwd?: string
|
|
118
206
|
readonly parentSession?: SessionId
|
|
119
207
|
readonly createdAt?: number
|
|
120
|
-
readonly
|
|
208
|
+
readonly isSeeded?: boolean
|
|
121
209
|
readonly origin?: 'subagent'
|
|
122
210
|
readonly delegationDepth?: number
|
|
123
211
|
readonly agentPreset?: string
|
|
@@ -125,27 +213,11 @@ interface CreateSessionOptions {
|
|
|
125
213
|
}
|
|
126
214
|
```
|
|
127
215
|
|
|
128
|
-
因此,回放/fork 的调用方式为 `ctx.
|
|
129
|
-
|
|
130
|
-
## `SessionRawArtifact`——逐字存储工件文本
|
|
131
|
-
|
|
132
|
-
后端为单个会话自持的工件文本,与其持久化写入的字节逐字一致(按物理编码解码)。`readRaw` 返回它而不从解析后事件重建,因此后端特定的序列化(chunk 打包、键序、换行)得以保留。Consumer 须先检查 `supportsRawArtifacts`:`false` 表示后端不提供此能力(如 SQLite),而 `readRaw(...) === undefined` 表示受支持的后端没有该会话的已实体化工件。
|
|
133
|
-
|
|
134
|
-
```ts type-equiv
|
|
135
|
-
/** A backend's own raw artifact text for one session, verbatim. */
|
|
136
|
-
interface SessionRawArtifact {
|
|
137
|
-
/** The session header parsed from the artifact's own first line. */
|
|
138
|
-
readonly meta: SessionHeader
|
|
139
|
-
/** The artifact's base filename on disk, without any physical encoding suffix. */
|
|
140
|
-
readonly filename: string
|
|
141
|
-
/** The artifact's full text content, decoded from the backend's physical encoding. */
|
|
142
|
-
readonly content: string
|
|
143
|
-
}
|
|
144
|
-
```
|
|
216
|
+
因此,回放/fork 的调用方式为 `ctx.agents.create({ sessionId, seed, meta })`——fork 还会随 `meta.isSeeded: true` 提供 `inheritedEventCount`,且只有经 agent-loop 发布的会话才会持久化,且循环会在发布之前通过新会话的写句柄存储 seed;将一个*持久化*会话恢复为活跃 agent 的调用方式为 `ctx.agents.resume({ resumeSessionId })`。
|
|
145
217
|
|
|
146
218
|
## 准备与恢复所有权
|
|
147
219
|
|
|
148
|
-
`SessionStore.prepare()` 接收普通创建选项,或通过 `RestoredSessionOptions` 转移所有权的全新的持久化对象图。恢复分支会就地验证并冻结转移来的 header 与事件,因此调用方不得保留可变别名。`SessionPreparation` 随后持有该精确的未发布 Session,直至发布或回滚;dispose
|
|
220
|
+
`SessionStore.prepare()` 接收普通创建选项,或通过 `RestoredSessionOptions` 转移所有权的全新的持久化对象图。恢复分支会就地验证并冻结转移来的 header 与事件,因此调用方不得保留可变别名。`SessionPreparation` 随后持有该精确的未发布 Session,直至发布或回滚;dispose 是同步且幂等的。agent-loop 的 resume 通过该会话的写句柄读取已存储的日志,并在准备之前追加所需的 `interruptedTurnClosers`,以此构建这些对象图。
|
|
149
221
|
|
|
150
222
|
```ts type-equiv
|
|
151
223
|
/**
|
|
@@ -157,6 +229,8 @@ interface RestoredSessionOptions {
|
|
|
157
229
|
readonly seed: SessionEvent[]
|
|
158
230
|
/** Fresh detached storage metadata to validate and freeze in place. */
|
|
159
231
|
readonly meta: SessionHeader
|
|
232
|
+
/** Exact number of fork-inherited leading events decoded from storage. */
|
|
233
|
+
readonly inheritedEventCount: SessionLogOffset
|
|
160
234
|
/** Select the persistence ownership-transfer path. */
|
|
161
235
|
readonly seedSource: 'persistence'
|
|
162
236
|
}
|
|
@@ -199,19 +273,9 @@ declare class SessionPreparation implements Disposable {
|
|
|
199
273
|
}
|
|
200
274
|
```
|
|
201
275
|
|
|
202
|
-
```ts type-equiv
|
|
203
|
-
/** Immutable logical session prepared from persistence or a live owner. */
|
|
204
|
-
interface SessionInspection {
|
|
205
|
-
/** Validated immutable session metadata. */
|
|
206
|
-
readonly meta: SessionHeader
|
|
207
|
-
/** Validated contiguous logical event log. */
|
|
208
|
-
readonly events: readonly SessionEvent[]
|
|
209
|
-
}
|
|
210
|
-
```
|
|
211
|
-
|
|
212
276
|
## 轻量源修订号
|
|
213
277
|
|
|
214
|
-
|
|
278
|
+
派生读取模型的消费方会在加载完整事件日志之前比较一个低开销的不透明修订号。该修订号是来自 `stat`/`list` 的逐后端实例变更令牌:修订号相等可视为日志未变;不相等则不作任何承诺,且写所有权的变动绝不会改变修订号。session-query 以它为键管理冷读缓存;该令牌在 open、read 或 resume 中不起任何作用。
|
|
215
279
|
|
|
216
280
|
```ts type-equiv
|
|
217
281
|
/**
|
|
@@ -222,21 +286,29 @@ type SessionPersistenceRevision = Branded<'SessionPersistenceRevision'>
|
|
|
222
286
|
```
|
|
223
287
|
|
|
224
288
|
```ts type-equiv
|
|
225
|
-
/**
|
|
289
|
+
/**
|
|
290
|
+
* Lightweight stored-session observation returned by {@link SessionPersistence.stat}
|
|
291
|
+
* and {@link SessionPersistence.list} without reading the full event log.
|
|
292
|
+
*/
|
|
226
293
|
interface SessionPersistenceSnapshot {
|
|
227
|
-
/** Detached metadata for one
|
|
228
|
-
header: SessionHeader
|
|
229
|
-
/** Opaque
|
|
230
|
-
revision: SessionPersistenceRevision
|
|
294
|
+
/** Detached metadata for one stored session. */
|
|
295
|
+
readonly header: SessionHeader
|
|
296
|
+
/** Opaque change token; see {@link SessionPersistence.stat}. */
|
|
297
|
+
readonly revision: SessionPersistenceRevision
|
|
298
|
+
/** Logical event count, when the backend can provide it cheaply from metadata; otherwise absent. */
|
|
299
|
+
readonly eventCount?: number
|
|
300
|
+
/** Physical artifact byte size, when the backend can provide it cheaply (JSONL); otherwise absent. */
|
|
301
|
+
readonly sizeBytes?: number
|
|
231
302
|
}
|
|
232
303
|
```
|
|
233
304
|
|
|
305
|
+
可选的 `eventCount`/`sizeBytes` 字段仍是供明确需要它们的 consumer 使用的低成本 backend observation。Session 列表不借助这两个字段打开冷日志,只读取 header 与经过 identity 校验的 projection cache hint,因此 cache 或 Session format 升级不会把启动变成 body scan。
|
|
306
|
+
|
|
234
307
|
## 后端
|
|
235
308
|
|
|
236
|
-
|
|
309
|
+
随产品交付的 provider 实现抽象 `SessionPersistence` 约定(`create`/`open`/`stat`/`list`,逐会话 `SessionHandle` 承载 `read`/`append`/`flush`/`close`,全程可选支持取消),并通过共享的持久化契约套件:
|
|
237
310
|
|
|
238
|
-
- **[dsh-session-persistence-jsonl](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/session/session-persistence-jsonl)**——逐会话仅追加的逻辑 JSONL 日志,默认存储为带 checksum 的连续 Zstandard frame
|
|
239
|
-
- **[dsh-session-persistence-sqlite](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/session/session-persistence-sqlite)**:一个可选启用的 `node:sqlite` 后端,使用 schema 19 把同一分片块中字段完全匹配的 delta 连续段存为有界物理 `text-chunks`、`reasoning-chunks` 与 `tool-call-chunks` 行。它在返回前重建完整逻辑事件流,只打包新增的持久批次,并拒绝旧 schema,而不是执行迁移。
|
|
311
|
+
- **[dsh-session-persistence-jsonl](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/session/session-persistence-jsonl)**——逐会话仅追加的逻辑 JSONL 日志,默认存储为带 checksum 的连续 Zstandard frame,也可配置为原始行;具备崩溃安全的原子实体化、逐批 `fsync` 的 append,以及在第一次新 append 之前截断撕裂尾部。`stat`/`list` 携带 `sizeBytes` 与尽力而为的、由 `fs.stat` 派生的修订号。
|
|
240
312
|
|
|
241
313
|
<!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
|
|
242
314
|
|
|
@@ -250,158 +322,77 @@ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnp
|
|
|
250
322
|
|
|
251
323
|
### `ctx.sessionPersistence` — `SessionPersistence` (abstract seam)
|
|
252
324
|
|
|
253
|
-
Durable append-only session storage
|
|
254
|
-
|
|
255
|
-
```ts cordis-catalog
|
|
256
|
-
/**
|
|
257
|
-
* Resolve this backend's independent local artifact for a session without
|
|
258
|
-
* reading, creating, flushing, or otherwise materializing it. Backends such
|
|
259
|
-
* as SQLite that do not own one artifact per session return `undefined`.
|
|
260
|
-
* @param meta - the immutable session header whose artifact is requested.
|
|
261
|
-
* @returns the backend-specific absolute location, when one exists.
|
|
262
|
-
*/
|
|
263
|
-
abstract locate(meta: SessionHeader): SessionLocation | undefined
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Read a session's backend-owned artifact text verbatim — the exact durable
|
|
267
|
-
* bytes the backend wrote (decoded from its physical encoding, e.g. a
|
|
268
|
-
* decompressed JSONL). The returned `content` is the raw text, not a
|
|
269
|
-
* reconstruction from parsed events, so it preserves backend-specific
|
|
270
|
-
* serialization (chunk packing, key order, line breaks). Callers first test
|
|
271
|
-
* {@link supportsRawArtifacts}; `undefined` then means only that the requested
|
|
272
|
-
* session has no materialized artifact.
|
|
273
|
-
* @param _id - the persisted session to read (unused by the default: no
|
|
274
|
-
* per-session artifact).
|
|
275
|
-
* @param signal - optional cancellation for backend read work.
|
|
276
|
-
* @returns the raw artifact plus its parsed header, or `undefined` when the
|
|
277
|
-
* session is absent.
|
|
278
|
-
* @throws when this backend does not expose per-session raw artifacts.
|
|
279
|
-
*/
|
|
280
|
-
readRaw(_id: SessionId, signal?: AbortSignal): Promise<SessionRawArtifact | undefined>
|
|
281
|
-
|
|
282
|
-
/**
|
|
283
|
-
* Register a new session's metadata. A backend MAY defer the physical write
|
|
284
|
-
* until the first {@link append} (lazy materialization), in which case a
|
|
285
|
-
* created-but-never-appended session is absent from {@link list}
|
|
286
|
-
* — abandoned sessions leave nothing behind.
|
|
287
|
-
* @param meta - the immutable header (id, version, cwd, lineage) to record.
|
|
288
|
-
*/
|
|
289
|
-
abstract create(meta: SessionHeader): Promise<void>
|
|
290
|
-
|
|
291
|
-
/**
|
|
292
|
-
* Ensure a live session has a durable header even when it has no events.
|
|
293
|
-
* Ordinary sessions remain lazily materialized; lifecycle frontends call
|
|
294
|
-
* this only when an empty session itself is a durable resumable resource.
|
|
295
|
-
* @param _session - exact live session whose registered header is materialized.
|
|
296
|
-
*/
|
|
297
|
-
ensureMaterialized(_session: Session): Promise<void>
|
|
325
|
+
Durable append-only session storage addressed through per-session handles.
|
|
298
326
|
|
|
299
|
-
|
|
300
|
-
* Durably persist a batch of events. Honors the append-only and contiguous-
|
|
301
|
-
* seq contracts: the first event's `seq` MUST equal the stored next-seq
|
|
302
|
-
* (after `load` has durably closed any interrupted turn). Rejects non-JSON-
|
|
303
|
-
* serializable `event.data` with an error naming the offending event type.
|
|
304
|
-
* @param id - the session the batch belongs to.
|
|
305
|
-
* @param events - the contiguous batch to persist, in seq order.
|
|
306
|
-
*/
|
|
307
|
-
abstract append(id: SessionId, events: readonly SessionEvent[]): Promise<void>
|
|
327
|
+
Storage semantics shared by every backend: events are contiguous from seq 0 and never rewritten; a torn physical tail is never returned to a reader and is truncated by the write path before its first append; reads validate current-format records only and refuse unknown vocabulary fail-closed. `append` persists best-effort; `flush` — per handle or service-wide — is the durability barrier.
|
|
308
328
|
|
|
309
|
-
|
|
310
|
-
* Prepare the exact unpublished Session used by resume. Implementations may
|
|
311
|
-
* reuse object graphs retained by an earlier {@link inspect} after confirming
|
|
312
|
-
* their durable revision is still current; disposal releases an unpublished
|
|
313
|
-
* reservation. Revision retries require the durable log to remain unchanged
|
|
314
|
-
* for one read/check round trip; continuous external writers may delay completion.
|
|
315
|
-
* @param id - persisted session to prepare.
|
|
316
|
-
* @param signal - optional cancellation for preparation work.
|
|
317
|
-
* @returns one owned unpublished Session preparation.
|
|
318
|
-
*/
|
|
319
|
-
async prepare(id: SessionId, signal?: AbortSignal): Promise<SessionPreparation>
|
|
329
|
+
Visibility: a created session is observable through `stat`/`list`/`open` in this process from the moment `create` resolves, even while a backend defers physical materialization (a pure optimization); other processes see the session only once it materializes, and a session that never materialized before a crash never existed. `SessionHandle.flush` forces materialization.
|
|
320
330
|
|
|
321
|
-
|
|
322
|
-
* Load an immutable balanced logical view and commit any required cold
|
|
323
|
-
* recovery. A complete interrupted final turn is preserved and durably
|
|
324
|
-
* closed with missing tool errors plus any open step and turn boundaries;
|
|
325
|
-
* only a torn final record is discarded. Unknown versions and corruption in
|
|
326
|
-
* the committed prefix reject. Implementations MUST NOT crash-repair an
|
|
327
|
-
* identity still bound to a live Session: a balanced live log may return as a
|
|
328
|
-
* durable snapshot, while an open live turn rejects. Returned values may be
|
|
329
|
-
* shared with immutable live or prepared state and must not be mutated.
|
|
330
|
-
* Revision-based implementations may wait for one stable read/check round trip.
|
|
331
|
-
* @param id - the persisted session to reload.
|
|
332
|
-
* @returns the header and a log ending on a balanced `turn/end`.
|
|
333
|
-
*/
|
|
334
|
-
abstract load(id: SessionId): Promise<SessionInspection>
|
|
331
|
+
Freshness: once an `append` or `flush` resolves, reads started afterwards on this backend instance observe at least that prefix.
|
|
335
332
|
|
|
333
|
+
```ts cordis-catalog
|
|
336
334
|
/**
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
* implementations retain the exact cold unpublished Session for bounded
|
|
343
|
-
* reuse by a later {@link prepare}. A stale ready source is reloaded; a source
|
|
344
|
-
* already committing or reserved for resume remains exclusive, and inspection
|
|
345
|
-
* may borrow its immutable view. Callers borrow only the immutable header and
|
|
346
|
-
* log. Continuous external writers may delay revision convergence.
|
|
347
|
-
* @param id - the persisted session to inspect.
|
|
348
|
-
* @param signal - optional cancellation for queued and backend read work.
|
|
349
|
-
* @returns the validated header and current logical event log.
|
|
335
|
+
* Create a new stored session and take its write ownership.
|
|
336
|
+
* @param header - the immutable header (id, version, cwd, lineage) to store.
|
|
337
|
+
* @param options - optional cancellation.
|
|
338
|
+
* @returns a `write` handle owned by the caller; close it to release ownership.
|
|
339
|
+
* @throws {SessionAlreadyExistsError} when the id already exists.
|
|
350
340
|
*/
|
|
351
|
-
abstract
|
|
341
|
+
abstract create(header: SessionHeader, options?: SessionPersistenceCreateOptions): Promise<SessionHandle>
|
|
352
342
|
|
|
353
343
|
/**
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
* @param
|
|
360
|
-
* @
|
|
344
|
+
* Open an existing stored session.
|
|
345
|
+
*
|
|
346
|
+
* `read` never takes ownership and works while another handle (or process)
|
|
347
|
+
* holds write ownership. `write` atomically claims single-writer ownership;
|
|
348
|
+
* an existing active owner rejects.
|
|
349
|
+
* @param id - the stored session to open.
|
|
350
|
+
* @param access - `read` or `write`.
|
|
351
|
+
* @param options - optional cancellation.
|
|
352
|
+
* @returns the open handle.
|
|
353
|
+
* @throws {SessionPersistenceNotFoundError} when the session does not exist.
|
|
354
|
+
* @throws {SessionAlreadyOwnedError} for `write` when ownership is taken.
|
|
361
355
|
*/
|
|
362
|
-
abstract
|
|
356
|
+
abstract open(id: SessionId, access: SessionAccess, options?: SessionPersistenceOpenOptions): Promise<SessionHandle>
|
|
363
357
|
|
|
364
358
|
/**
|
|
365
|
-
*
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
* (SQLite) read only the suffix; sequential media (JSONL, both encodings)
|
|
375
|
-
* still parse the whole artifact and skip forward — the primitive bounds
|
|
376
|
-
* what is RETURNED and refolded, not every backend's physical read.
|
|
377
|
-
* @param id - the persisted session to read.
|
|
378
|
-
* @param fromSeq - first event seq to include; a non-negative safe integer.
|
|
379
|
-
* @param signal - optional cancellation for queued and backend read work.
|
|
380
|
-
* @returns the header and the stored events with `seq >= fromSeq`.
|
|
359
|
+
* Flush every active write handle owned by this service instance in one
|
|
360
|
+
* durability barrier: each handle's routed live events drain durably and
|
|
361
|
+
* its session materializes, exactly as that handle's own
|
|
362
|
+
* `SessionHandle.flush` would. Read handles buffer nothing and are
|
|
363
|
+
* untouched. A handle closed concurrently counts as flushed — close itself
|
|
364
|
+
* drains durably.
|
|
365
|
+
* @returns resolution once every write handle active at the call has flushed.
|
|
366
|
+
* @throws {AggregateError} naming each session whose flush failed; the
|
|
367
|
+
* remaining handles still flush.
|
|
381
368
|
*/
|
|
382
|
-
abstract
|
|
369
|
+
abstract flush(): Promise<void>
|
|
383
370
|
|
|
384
371
|
/**
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
*
|
|
372
|
+
* Observe one stored session without reading its event log or taking
|
|
373
|
+
* ownership.
|
|
374
|
+
*
|
|
375
|
+
* The snapshot's `revision` is an opaque change token comparable only
|
|
376
|
+
* against revisions from the same service instance and session id: equal
|
|
377
|
+
* revisions may be treated as an unchanged log; unequal revisions promise
|
|
378
|
+
* nothing. Write-ownership churn does not change a revision. It exists for
|
|
379
|
+
* derived read-model caches keyed off `stat`/`list`; it plays no part in
|
|
380
|
+
* open, read, or resume.
|
|
381
|
+
* @param id - the stored session to observe.
|
|
382
|
+
* @param options - optional cancellation.
|
|
383
|
+
* @returns the snapshot, or `undefined` when the session does not exist.
|
|
388
384
|
*/
|
|
389
|
-
abstract
|
|
385
|
+
abstract stat(id: SessionId, options?: SessionPersistenceStatOptions): Promise<SessionPersistenceSnapshot | undefined>
|
|
390
386
|
|
|
391
387
|
/**
|
|
392
|
-
* List
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
* successful mutating {@link load} repair changes the next listed revision.
|
|
396
|
-
* Revisions also distinguish independently backed stores so backend-local
|
|
397
|
-
* counters cannot compare equal across different persistence sources.
|
|
398
|
-
* @param signal - optional cancellation for backend snapshot-listing work.
|
|
399
|
-
* @returns one header and opaque revision per materialized session without loading full logs.
|
|
388
|
+
* List every stored session visible to this process, in no promised order.
|
|
389
|
+
* @param options - optional cancellation.
|
|
390
|
+
* @returns one snapshot per stored session.
|
|
400
391
|
*/
|
|
401
|
-
abstract
|
|
392
|
+
abstract list(options?: SessionPersistenceListOptions): Promise<readonly SessionPersistenceSnapshot[]>
|
|
402
393
|
```
|
|
403
394
|
|
|
404
|
-
Types: [
|
|
395
|
+
Types: [SessionId](./core.md)
|
|
405
396
|
|
|
406
397
|
Source: [`packages/session/session-persistence/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/session/session-persistence/src/index.ts)
|
|
407
398
|
<!-- END GENERATED cordis-surface -->
|
|
@@ -11,7 +11,7 @@ outline: [2,3]
|
|
|
11
11
|
|
|
12
12
|
## 已记录状态与恢复
|
|
13
13
|
|
|
14
|
-
`plan/mode`(`{ active: boolean }`)是仅记日志、整值替换的[会话事件](./session.md):持久且可回放,绝不进入模型 transcript
|
|
14
|
+
`plan/mode`(`{ active: boolean }`)是仅记日志、整值替换的[会话事件](./session.md):持久且可回放,绝不进入模型 transcript(文本记录)。可选注册的 `plan` 单元折叠已提交模式、命令结算结果和最近一次请求头记录的模式。`ctx.planMode` 通过 `stateOf()` 读取该状态;注册表、`plan` key 或 `turnBoundary` key 缺失时,第一次依赖它们的访问会失败。客户端只接收 `{ active, pending }`;恢复、fork 与压缩(compaction)都能从日志恢复两者。完整事件声明见[持久化日志事件目录](../persistence-catalog.md)。
|
|
15
15
|
|
|
16
16
|
## 待生效选择与 pre-step 追加
|
|
17
17
|
|
|
@@ -29,7 +29,7 @@ interface PlanModeConfig {
|
|
|
29
29
|
}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
`section` 缺失、为空白或不是字符串,以及任何未知键,都会在插件加载时失败,而不是被忽略。计划模式激活期间,确切的 `section` 文本以
|
|
32
|
+
`section` 缺失、为空白或不是字符串,以及任何未知键,都会在插件加载时失败,而不是被忽略。计划模式激活期间,确切的 `section` 文本以 order 50 渲染为 `plan:policy` [系统提示词段落](./system-prompt.md);未激活的计划模式不贡献任何文本。
|
|
33
33
|
|
|
34
34
|
## 退出工具与 `/plan` 命令
|
|
35
35
|
|
|
@@ -53,7 +53,7 @@ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnp
|
|
|
53
53
|
|
|
54
54
|
### `ctx.planMode` — `PlanModeController`
|
|
55
55
|
|
|
56
|
-
`ctx.planMode`: owns logged plan state, applies and narrates selected state at step start, the `plan:policy` section, the `/plan` command, and the stable exit tool.
|
|
56
|
+
`ctx.planMode`: owns logged plan state, applies and narrates selected state at step start, the `plan:policy` section, the `/plan` command, and the stable exit tool. Client carriers expose the projection's cropped `{ active, pending }` view.
|
|
57
57
|
|
|
58
58
|
```ts cordis-catalog
|
|
59
59
|
/**
|
|
@@ -5,7 +5,7 @@ outline: [2,3]
|
|
|
5
5
|
|
|
6
6
|
# 仅限 Session 内的 Schedule
|
|
7
7
|
|
|
8
|
-
Schedule 拥有持久提醒;这些提醒会作为普通的后续对话轮次返回原 live Session。[持久 Schedule Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-08-05-durable-web-schedule.zh.md)
|
|
8
|
+
Schedule 拥有持久提醒;这些提醒会作为普通的后续对话轮次返回原 live Session。[持久 Schedule Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-08-05-durable-web-schedule.zh.md) 负责持久化、生命周期与活动状态呈现,[对话式交付](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/simplification/2026-08-09-conversational-schedule-delivery.zh.md) 负责无回执边界,[显式时区边界](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/simplification/2026-08-09-explicit-schedule-time-zone.zh.md) 负责浏览器本地解释,[有界固定速率 Schedule](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/simplification/2026-08-09-bounded-fixed-rate-schedule.zh.md) 负责重复调度。本页记录 [`packages/schedule/schedule/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/schedule/schedule/src/types.ts) 中的持久数据形状和面向模型的数据形状;[包 README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/schedule/schedule/README.zh.md) 负责组合、工具行为与确切的提醒 framing。
|
|
9
9
|
|
|
10
10
|
## 持久记录
|
|
11
11
|
|
|
@@ -152,7 +152,7 @@ type ScheduleDispatchChange = OneShotScheduleDispatchChange | EveryScheduleDispa
|
|
|
152
152
|
type ScheduleChange = ScheduleCreateChange | ScheduleDeleteChange | ScheduleDispatchChange
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
严格 decoder 与 fold 会拒绝未知版本、额外字段、复用 id、不匹配的一次性提醒或 Every dispatch 形状,以及针对非活动记录的 delete 或 dispatch 转换。普通 Session 折叠完整事件流。fork
|
|
155
|
+
严格 decoder 与 fold 会拒绝未知版本、额外字段、复用 id、不匹配的一次性提醒或 Every dispatch 形状,以及针对非活动记录的 delete 或 dispatch 转换。普通 Session 折叠完整事件流。fork 只折叠精确 `inheritedEventCount` 位置及其后的事件,因此保留历史,但不会接管父 Session 的活动提醒。Projection 初始化会在不可变 header 旁接收该 cut,复用共享 transition,并持久化 cut、活动记录与已使用 id 历史,使缓存恢复继续保持严格回放。`schedule/change` 声明和源码位置也编入[持久化目录](../persistence-catalog.md#schedulechange--log-only)。
|
|
156
156
|
|
|
157
157
|
## 活动视图与管理
|
|
158
158
|
|
|
@@ -180,10 +180,16 @@ type ScheduleView = ScheduleRecord & {
|
|
|
180
180
|
|
|
181
181
|
生成的[工具目录](../tool-catalog.md#deepseek-aidsh-schedule)负责 `schedule_create`、`schedule_list` 和 `schedule_delete` 的参数与结果 schema。一条 Agent-scoped 队列将管理调用与到期工作串行化。每次读取或判断都会先等待共享的 Session 持久化 barrier;create 与实际执行的 delete 在追加后还会再次等待。barrier 失败会报告 `persistence_uncertain`,而不是猜测 eager write 是否已提交。其他稳定错误代码是 `invalid_prompt`、`invalid_selector`、`invalid_rule`、`invalid_time_zone`、`not_future`、`time_out_of_range`、`frequency_too_high`、`corrupt_schedule_log` 和 `internal_error`。
|
|
182
182
|
|
|
183
|
+
## 只读 Web 目录
|
|
184
|
+
|
|
185
|
+
可选 Session projection 注册表存在时,Schedule 会注册客户端可见的 `schedule` key,其值是完整的活动 `ScheduleRecord[]`。live、cache、history 与 detached 读取共用同一套 header-aware 严格 fold;畸形权威输入会使既有读取路径失败,而不会发布部分值。
|
|
186
|
+
|
|
187
|
+
shipped Web bundle 默认禁用 `ui-schedule`,显式 Schedule overlay 则把它与 Host 能力一同启用。[`dsh-client-ui-schedule`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-schedule/README.zh.md)拥有 header 交互,[`dsh-client-ui-workspace`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-workspace/README.zh.md)拥有列表行呈现,持久 Schedule Agent Note 拥有二者共享的活动状态边界。共享值只表示当前活动状态,绝不表示交付历史或回执;到期提醒仍通过下文所述的普通 Assistant 输出出现。
|
|
188
|
+
|
|
183
189
|
## Live 交付
|
|
184
190
|
|
|
185
191
|
进程内 owner 根据持久 fold 派生最早的 timer,并在每次有界等待后重新读取墙钟。cold Session 不执行任何工作;重新打开后会重建 timer,并使已经过去的目标进入 overdue 状态。到期的一次性提醒享有优先级,每次只进入一个后续轮次。当没有一次性提醒到期时,所有 overdue 的 Every 记录会组成上述单个批次。
|
|
186
192
|
|
|
187
193
|
到期工作会先等待 Agent 完全 idle 并认领 maintenance phase,再重新折叠状态、采样本次判断、将一个 `followup()` 排入队列,并追加对应的 dispatch 变更。它绝不会调用 `steer()`,也绝不会中断当前轮次。
|
|
188
194
|
|
|
189
|
-
获得准入的一次性提醒或固定速率批次会启动一个普通的后续轮次,且只通过普通对话 transcript(文本记录)出现;Schedule 不提供独立的持久 Web
|
|
195
|
+
获得准入的一次性提醒或固定速率批次会启动一个普通的后续轮次,且只通过普通对话 transcript(文本记录)出现;Schedule 不提供独立的持久 Web 回执。上面的只读活动目录绝不表示交付成功。如果 framing 构造或同步队列准入失败,则不会记录 dispatch,提醒仍保持活动。队列准入后、持久 dispatch 前的狭窄崩溃窗口可能使提醒内容在恢复后重复,因此该边界提供的是尽力而为的至少一次交付,而非恰好一次交付。
|