dsh-plugin-dev-kb 1.0.7 → 1.0.9

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.
Files changed (169) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.en.md +144 -0
  3. package/README.md +21 -7
  4. package/kb/INDEX.md +19 -5
  5. package/kb/README.md +11 -10
  6. package/kb/extra/AGENTS.md +4 -4
  7. package/kb/extra/cookbook/adding-a-vendored-package.md +2 -2
  8. package/kb/extra/cookbook/adding-a-vendored-package.zh.md +2 -2
  9. package/kb/extra/deepseek-llm-api-wire-extensions.md +159 -0
  10. package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +159 -0
  11. package/kb/extra/development.md +8 -14
  12. package/kb/extra/development.zh.md +8 -14
  13. package/kb/extra/event-producer-consumer.md +47 -41
  14. package/kb/extra/event-producer-consumer.zh.md +47 -41
  15. package/kb/extra/glossary.md +1 -1
  16. package/kb/extra/glossary.zh.md +1 -1
  17. package/kb/extra/graph-atlas.md +0 -2
  18. package/kb/extra/graph-atlas.zh.md +0 -2
  19. package/kb/extra/i18n/README.md +4 -4
  20. package/kb/extra/i18n/README.zh.md +4 -4
  21. package/kb/extra/module-graph.md +680 -413
  22. package/kb/extra/module-graph.zh.md +681 -414
  23. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.md +2 -2
  24. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.zh.md +2 -2
  25. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.md +2 -2
  26. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +2 -2
  27. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.md +2 -2
  28. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.zh.md +2 -2
  29. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +1 -1
  30. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +1 -1
  31. package/kb/extra/rescope.md +2 -2
  32. package/kb/extra/rescope.zh.md +2 -2
  33. package/kb/extra/subsystems/agent-team.md +24 -1
  34. package/kb/extra/subsystems/agent-team.zh.md +24 -1
  35. package/kb/extra/subsystems/attachment.md +12 -4
  36. package/kb/extra/subsystems/attachment.zh.md +12 -4
  37. package/kb/extra/subsystems/extensions.md +18 -0
  38. package/kb/extra/subsystems/extensions.zh.md +18 -0
  39. package/kb/extra/subsystems/feedback.md +2 -2
  40. package/kb/extra/subsystems/feedback.zh.md +2 -2
  41. package/kb/extra/subsystems/todo.md +32 -0
  42. package/kb/extra/subsystems/todo.zh.md +32 -0
  43. package/kb/extra/subsystems/webhook.md +70 -0
  44. package/kb/extra/subsystems/webhook.zh.md +70 -0
  45. package/kb/extra/testing.md +11 -10
  46. package/kb/extra/testing.zh.md +8 -7
  47. package/kb/meta/search-index.json +269 -161
  48. package/kb/meta/site-pages.txt +182 -168
  49. package/kb/meta/source.json +5 -5
  50. package/kb/meta/topics.md +14 -6
  51. package/kb/site/develop/basic/publish.md +2 -2
  52. package/kb/site/develop/basic/tool.md +1 -1
  53. package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +4 -4
  54. package/kb/site/develop/framework/events.md +1 -1
  55. package/kb/site/develop/practice/dynamic-cordis.md +17 -0
  56. package/kb/site/develop/practice/llm-adapter.md +3 -3
  57. package/kb/site/en/develop/basic/publish.md +2 -2
  58. package/kb/site/en/develop/basic/tool.md +1 -1
  59. package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +4 -4
  60. package/kb/site/en/develop/framework/events.md +1 -1
  61. package/kb/site/en/develop/practice/dynamic-cordis.md +17 -0
  62. package/kb/site/en/develop/practice/llm-adapter.md +3 -3
  63. package/kb/site/en/guide/github-review.md +104 -0
  64. package/kb/site/en/guide/mcp-memory.md +103 -0
  65. package/kb/site/en/guide/python-sdk.md +80 -34
  66. package/kb/site/en/guide/schedule.md +21 -0
  67. package/kb/site/en/reference/agent-lifecycle.md +1 -1
  68. package/kb/{extra → site/en/reference}/api-gateway.md +11 -9
  69. package/kb/site/en/reference/capability-seams.md +115 -67
  70. package/kb/site/en/reference/config-catalog.md +358 -164
  71. package/kb/site/en/reference/cookbook/adding-a-package.md +2 -2
  72. package/kb/site/en/reference/cookbook/adding-a-settings-card.md +2 -2
  73. package/kb/site/en/reference/cookbook/adding-a-tool.md +11 -4
  74. package/kb/site/en/reference/cookbook/adding-an-llm-adapter.md +1 -1
  75. package/kb/site/en/reference/cookbook/extension-cookbook.md +6 -6
  76. package/kb/site/en/reference/cordis-api/inherited.md +1 -1
  77. package/kb/site/en/reference/cordis-primer.md +2 -1
  78. package/kb/site/en/reference/index.md +19 -7
  79. package/kb/site/en/reference/persistence-catalog.md +91 -44
  80. package/kb/site/en/reference/subsystems/approval.md +10 -10
  81. package/kb/site/en/reference/subsystems/client-modules.md +58 -16
  82. package/kb/site/en/reference/subsystems/code-runtime.md +3 -3
  83. package/kb/site/en/reference/subsystems/compaction.md +2 -2
  84. package/kb/site/en/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +43 -24
  85. package/kb/site/en/reference/subsystems/core.md +70 -12
  86. package/kb/site/en/reference/subsystems/credentials.md +43 -3
  87. package/kb/site/en/reference/subsystems/filesystem.md +12 -2
  88. package/kb/site/en/reference/subsystems/index.md +6 -1
  89. package/kb/site/en/reference/subsystems/jobs.md +1 -1
  90. package/kb/site/en/reference/subsystems/llm-streaming.md +132 -11
  91. package/kb/site/en/reference/subsystems/permission-presets.md +1 -1
  92. package/kb/site/en/reference/subsystems/persistence.md +22 -3
  93. package/kb/site/en/reference/subsystems/plan.md +1 -1
  94. package/kb/site/en/reference/subsystems/session-projection.md +74 -33
  95. package/kb/site/en/reference/subsystems/session-query.md +9 -1
  96. package/kb/site/en/reference/subsystems/session-reference.md +28 -7
  97. package/kb/site/en/reference/subsystems/session-telemetry.md +2 -3
  98. package/kb/site/en/reference/subsystems/session.md +260 -41
  99. package/kb/site/en/reference/subsystems/settings.md +78 -1
  100. package/kb/site/en/reference/subsystems/skills.md +23 -0
  101. package/kb/site/en/reference/subsystems/slots.md +177 -0
  102. package/kb/site/en/reference/subsystems/spill.md +2 -2
  103. package/kb/site/en/reference/subsystems/storage.md +9 -1
  104. package/kb/site/en/reference/subsystems/subagent.md +90 -23
  105. package/kb/site/en/reference/subsystems/system-prompt.md +4 -4
  106. package/kb/site/en/reference/subsystems/token-meter.md +25 -10
  107. package/kb/site/en/reference/subsystems/tools.md +39 -39
  108. package/kb/site/en/reference/subsystems/typert.md +44 -37
  109. package/kb/site/en/reference/subsystems/user-questions.md +33 -33
  110. package/kb/site/en/reference/subsystems/web-client.md +98 -0
  111. package/kb/site/en/reference/subsystems/web-server.md +11 -5
  112. package/kb/site/en/reference/subsystems/web.md +7 -1
  113. package/kb/site/en/reference/subsystems/workspace.md +95 -2
  114. package/kb/site/en/reference/tool-catalog.md +76 -18
  115. package/kb/site/en/reference/tool-execution-pipeline.md +1 -1
  116. package/kb/site/guide/github-review.md +104 -0
  117. package/kb/site/guide/mcp-memory.md +103 -0
  118. package/kb/site/guide/python-sdk.md +87 -41
  119. package/kb/site/guide/schedule.md +21 -0
  120. package/kb/site/reference/agent-lifecycle.md +1 -1
  121. package/kb/{extra/api-gateway.zh.md → site/reference/api-gateway.md} +11 -9
  122. package/kb/site/reference/capability-seams.md +115 -67
  123. package/kb/site/reference/config-catalog.md +357 -163
  124. package/kb/site/reference/cookbook/adding-a-package.md +2 -2
  125. package/kb/site/reference/cookbook/adding-a-settings-card.md +2 -2
  126. package/kb/site/reference/cookbook/adding-a-tool.md +11 -4
  127. package/kb/site/reference/cookbook/adding-an-llm-adapter.md +1 -1
  128. package/kb/site/reference/cookbook/extension-cookbook.md +6 -6
  129. package/kb/site/reference/cordis-api/inherited.md +1 -1
  130. package/kb/site/reference/cordis-primer.md +2 -1
  131. package/kb/site/reference/index.md +19 -7
  132. package/kb/site/reference/persistence-catalog.md +87 -40
  133. package/kb/site/reference/subsystems/approval.md +10 -10
  134. package/kb/site/reference/subsystems/client-modules.md +58 -16
  135. package/kb/site/reference/subsystems/code-runtime.md +3 -3
  136. package/kb/site/reference/subsystems/compaction.md +2 -2
  137. package/kb/site/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +43 -24
  138. package/kb/site/reference/subsystems/core.md +70 -12
  139. package/kb/site/reference/subsystems/credentials.md +43 -3
  140. package/kb/site/reference/subsystems/filesystem.md +12 -2
  141. package/kb/site/reference/subsystems/index.md +6 -1
  142. package/kb/site/reference/subsystems/jobs.md +1 -1
  143. package/kb/site/reference/subsystems/llm-streaming.md +132 -11
  144. package/kb/site/reference/subsystems/persistence.md +22 -3
  145. package/kb/site/reference/subsystems/plan.md +1 -1
  146. package/kb/site/reference/subsystems/session-projection.md +74 -33
  147. package/kb/site/reference/subsystems/session-query.md +9 -1
  148. package/kb/site/reference/subsystems/session-reference.md +28 -7
  149. package/kb/site/reference/subsystems/session-telemetry.md +2 -3
  150. package/kb/site/reference/subsystems/session.md +260 -41
  151. package/kb/site/reference/subsystems/settings.md +78 -1
  152. package/kb/site/reference/subsystems/skills.md +23 -0
  153. package/kb/site/reference/subsystems/slots.md +177 -0
  154. package/kb/site/reference/subsystems/spill.md +2 -2
  155. package/kb/site/reference/subsystems/storage.md +9 -1
  156. package/kb/site/reference/subsystems/subagent.md +90 -23
  157. package/kb/site/reference/subsystems/system-prompt.md +4 -4
  158. package/kb/site/reference/subsystems/token-meter.md +25 -10
  159. package/kb/site/reference/subsystems/tools.md +39 -39
  160. package/kb/site/reference/subsystems/typert.md +44 -37
  161. package/kb/site/reference/subsystems/user-questions.md +33 -33
  162. package/kb/site/reference/subsystems/web-client.md +98 -0
  163. package/kb/site/reference/subsystems/web-server.md +11 -5
  164. package/kb/site/reference/subsystems/web.md +7 -1
  165. package/kb/site/reference/subsystems/workspace.md +95 -2
  166. package/kb/site/reference/tool-catalog.md +76 -18
  167. package/kb/site/reference/tool-execution-pipeline.md +1 -1
  168. package/package.json +11 -3
  169. package/skills/dsh-plugin-dev-kb.md +8 -6
@@ -37,13 +37,17 @@ interface ResolvedCredential {
37
37
  `describe(ref)` 在绝不暴露值的前提下回应配置界面:引用当前是否可解析、来自哪一层、`set` 当前能否成功。本地提供方把由当前进程环境供值的引用报告为 `writable: false`——那样的写入会表面成功而解析持续返回遮蔽值,因此 seam 直接拒绝,界面也得以提前把该引用渲染为只读。
38
38
 
39
39
  ```ts type-equiv
40
- /** Source and writability facts for one reference, safe for configuration UIs — never the value. */
40
+ /**
41
+ * Source and writability facts for one reference, safe for configuration UIs —
42
+ * never the value. The view has no slot a value could ride in, which is what
43
+ * lets the whole read half cross the Remote wire.
44
+ */
41
45
  interface CredentialInfo {
42
- /** Whether {@link CredentialProvider.resolve} would currently return a value. */
46
+ /** Whether resolving the reference would currently return a value. */
43
47
  configured: boolean
44
48
  /** Source layer currently supplying the value; absent while unconfigured. */
45
49
  source?: string
46
- /** Whether {@link CredentialProvider.set} would currently succeed for this reference. */
50
+ /** Whether the active provider can write this reference. */
47
51
  writable: boolean
48
52
  }
49
53
  ```
@@ -215,6 +219,42 @@ abstract deleteRecord(key: CredentialKey): Promise<void>
215
219
 
216
220
  Source: [`packages/credentials/credentials/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/credentials/credentials/src/index.ts)
217
221
 
222
+ <a id="ctxcredentialscontroller--credentialscontroller"></a>
223
+
224
+ ### `ctx.credentialsController` — `CredentialsController`
225
+
226
+ Host service backing the generated `ctx.remote.credentials` namespace. It carries every wire obligation the credential seam itself does not: the batch fan-out bound, the field-by-field view projection, the reference-grammar guard, and the refusal mapping. Secret values cross in one direction only — no method here returns one.
227
+
228
+ ```ts cordis-catalog
229
+ /**
230
+ * Describe several references for one configuration surface. Batched because
231
+ * a settings page describes every reference its rows name at once, and one
232
+ * round trip keeps those rows from settling separately.
233
+ * @param refs - reference names, at most {@link MAX_DESCRIBE_REFS}; a name outside the grammar rejects the whole call as `bad-request`.
234
+ * @returns one view per requested name, keyed by that name.
235
+ * @throws TypertRemoteFailure when the request is invalid or no credential provider is mounted.
236
+ */
237
+ @Remote async describe(refs: string[]): Promise<Record<string, CredentialInfo>>
238
+
239
+ /**
240
+ * Store one value from a configuration surface. The value crosses the wire in
241
+ * this direction only: no read path returns it.
242
+ * @param ref - reference name to store under.
243
+ * @param value - the non-empty secret value.
244
+ * @throws TypertRemoteFailure when the request is invalid, no provider is mounted, or the provider refuses the write.
245
+ */
246
+ @Remote async set(ref: string, value: string): Promise<void>
247
+
248
+ /**
249
+ * Remove one reference from a configuration surface.
250
+ * @param ref - reference name to remove.
251
+ * @throws TypertRemoteFailure when the request is invalid, no provider is mounted, or the provider refuses the write.
252
+ */
253
+ @Remote async unset(ref: string): Promise<void>
254
+ ```
255
+
256
+ Source: [`packages/api/settings-controller/src/credentials.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/settings-controller/src/credentials.ts)
257
+
218
258
  <a id="authorization-events"></a>
219
259
 
220
260
  ### `authorization/*` events
@@ -15,7 +15,7 @@ outline: [2,3]
15
15
 
16
16
  每个操作首先将用户提供的路径解析为不透明的后端目标。消费方可以显示 `displayPath`,但禁止解析 `targetKey`(一个品牌化的不透明 id),也不得假设它是本地绝对路径。
17
17
 
18
- 与文件系统共享执行世界的消费方通过提供方获取跨能力坐标,而不是解释该身份:`processPath(target)` 返回子进程可以打开的规范化绝对路径,`fileUrl(target)` 返回采用提供方平台语法的 `file:` URI,`contains(parent, child)` 则检查规范化身份相等或后代包含关系。
18
+ 与文件系统共享执行世界的消费方通过提供方获取跨能力坐标,而不是解释该身份:`processPath(target)` 返回子进程可以打开的规范化绝对路径;`processPathFromHostPath(hostPath)` 只在该执行世界共享相应宿主文件时映射其绝对路径;`fileUrl(target)` 返回采用提供方平台语法的 `file:` URI;`contains(parent, child)` 检查规范化身份相等或后代包含关系。
19
19
 
20
20
  ```ts type-equiv
21
21
  /**
@@ -278,7 +278,7 @@ type FsErrorCode =
278
278
 
279
279
  ## 服务与插件
280
280
 
281
- `FileSystem`(`ctx.fs`,abstract)拥有提供方原语:`resolve`、`processPath`、`fileUrl`、`contains`、`stat`、`lstat`、`readText`、`streamText`、`readBytes`、`listDir`、`writeText` 与 `editText`。`dsh-fs-observation-policy` **不注册服务**——它是一个通过 `fs/*` 事件门禁添加策略的插件:根据未见/缺失/存在状态对写入与编辑意图 waterfall 作出决策,并记录 `FsObservation` 值。执行器是 `dsh-tool-fs`:它通过 `ctx.fs` 读取/写入/编辑,分发 waterfall,并 emit 记录事件。下方生成的 [`ctx.fs` 小节](#ctxfs--filesystem-abstract-seam) 展示确切的 `ctx.fs` 签名。
281
+ `FileSystem`(`ctx.fs`,abstract)拥有提供方原语:`resolve`、`processPath`、`processPathFromHostPath`、`fileUrl`、`contains`、`stat`、`lstat`、`readText`、`streamText`、`readBytes`、`listDir`、`writeText` 与 `editText`。`dsh-fs-observation-policy` **不注册服务**。它通过 `fs/*` 事件门禁添加策略,根据未见、缺失或存在状态对写入与编辑意图 waterfall 作出决策,并记录 `FsObservation` 值。执行器是 `dsh-tool-fs`:它通过 `ctx.fs` 读取、写入或编辑,分发 waterfall,并 emit 记录事件。下方生成的 [`ctx.fs` 小节](#ctxfs--filesystem-abstract-seam) 展示确切的 `ctx.fs` 签名。
282
282
 
283
283
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
284
284
 
@@ -316,6 +316,16 @@ abstract resolve(path: string, opts?: { cwd?: string; signal?: AbortSignal }): P
316
316
  */
317
317
  abstract processPath(target: FsTarget): string
318
318
 
319
+ /**
320
+ * Map an absolute path from the harness host into this filesystem's
321
+ * execution world when both paths identify the same file. The base provider
322
+ * exposes no mapping; host-backed or explicitly shared backends override it.
323
+ * @param hostPath - absolute path in the harness host filesystem.
324
+ * @returns the process path for the same file, or undefined when this
325
+ * execution world cannot read that host file.
326
+ */
327
+ processPathFromHostPath(hostPath: string): string | undefined
328
+
319
329
  /**
320
330
  * Return the canonical `file:` URI for a target in this filesystem's
321
331
  * execution world. Backends own URI encoding because the host platform may
@@ -16,8 +16,9 @@ outline: [2,3]
16
16
  | [typert.md](./typert.md) | 远程调用描述符、lookup/Context 声明、Typert 注册表,以及 Host Gateway/Client API 边界 |
17
17
  | [goal.md](./goal.md) | 持久 goal 标识、生命周期快照、激活、变更记录与 Round 归属 |
18
18
  | [schedule.md](./schedule.md) | 仅限 Session 内的提醒记录、持久转换、活动视图与普通对话交付 |
19
+ | [todo.md](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/todo.zh.md) | todo 包的整列表条目类型、持久事件所有权、投影和开放轮次不变量 |
19
20
  | [commands.md](./commands.md) | 人类命令注册表服务:定义、适配器发现、直接调用、结果与解析视图 |
20
- | [session.md](./session.md) | 完整的 `SessionEventMap` 变体目录、`TurnTrigger`/`TurnEndReason`、`deriveMessages()`、执行封闭与独立事件 |
21
+ | [session.md](./session.md) | 完整的 `SessionEventMap` 变体目录、`TurnEndReason`、`deriveMessages()`、执行封闭与独立事件 |
21
22
  | [persistence.md](./persistence.md) | 持久性 seam:`SessionPersistence`、JSONL + SQLite 后端、`session/flush`、崩溃恢复、`SessionHeader` |
22
23
  | [settings.md](./settings.md) | 用户设置 seam:`SettingsNamespace` 注册、分层解析(默认值 → 组合 `base` → 用户文档)、owner scope、热提交 |
23
24
  | [credentials.md](./credentials.md) | 凭据 seam:配置中的 `CredentialRef` 引用(绝不含值)、按操作解析、对 UI 安全的 `CredentialInfo`、提供方来源层 |
@@ -50,9 +51,13 @@ outline: [2,3]
50
51
  | [plan.md](./plan.md) | 计划模式:仅记日志的 `plan/mode` 状态、待定选择的冲刷、`PlanModeConfig`、`exit_plan_mode` 审阅流程 |
51
52
  | [invariants.md](./invariants.md) | 运行时不变式注册表:选择配置 `Config`、`InvariantInstaller`/`InvariantFailure`、空配套插件约定 |
52
53
  | [web-server.md](./web-server.md) | HTTP 载体:`WebRouteKind`/`WebRoute`、匹配顺序、可认领的回退席位、index 渲染挂接点 |
54
+ | [webhook.md](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/webhook.zh.md) | 通过身份验证的提供方交付、任意程序化规则,以及 fire-and-forget 的 Workspace Session 创建 |
53
55
  | [storage.md](./storage.md) | 存储子系统:后端约定(`StorageBackend`)、`StorageForms`、`DomainSpec`/`Domain`、`domain/changed` |
54
56
  | [workspace.md](./workspace.md) | 工作区注册表:`Workspace`/`WorkspaceId`、注册与解析、与会话 `cwd` 的关系 |
57
+ | [web-client.md](./web-client.md) | 浏览器架构:启动、Remote 通信、配对的 Client model、UI adapter、Conversation 组装、Slots 与重连语义 |
55
58
  | [client-modules.md](./client-modules.md) | Web 插件表:`dsh.client` 声明、`WebBootGraph` 线上组合、bundle 路由与 index 转换 |
59
+ | [slots.md](./slots.md) | 类型化 Web UI 组合:声明所有权、cardinality 与 scope、框架与功能注入、props 推导及当前层级 |
60
+ | [conversation.md](./conversation.md) | target-neutral Session event 组装:Context identity、Location data、replay 路径、view builder 与 target 自有 render node |
56
61
  | [session-projection.md](./session-projection.md) | 投影 seam:`SessionProjectionMap`、纯函数 `ProjectionDefinition` 单元、`ProjectionSnapshot` 的一致切面、变更馈送 |
57
62
  | [session-telemetry.md](./session-telemetry.md) | 对外会话上报能力 seam:`SessionTelemetryRecord`/`SessionTelemetrySeverity`、`SessionTelemetrySink` 约定和 `session-telemetry/record` 脱敏 waterfall |
58
63
 
@@ -157,7 +157,7 @@ interface JobRead {
157
157
 
158
158
  ## 服务行为
159
159
 
160
- 抽象的 [`JobRegistry`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/jobs/jobs/src/index.ts) Service Definition 规定原子 `start`、限定调用方作用域的 `get` 和 `list`、`read`、`kill`、有界 `wait`、故障隔离的 `onJobDone` 与 `onJobsChanged` 监听器,以及 `attachController` 何时可用;[`LocalJobRegistry`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/jobs/jobs-local/src/index.ts) 是其进程局部 Service Provider。授权会比较拥有者会话;拥有者清理与准入会使用确切的已注册 `Agent` 实例。本地 Service Provider 的 `maxConcurrentJobsPerOwner` 配置必须是正的安全整数,默认值为 `10`;它按确切 owner 统计 `running` 与 `stopping` 记录,所有无 owner 任务共享一个服务级桶,并在生产方终止结算后释放容量。Service Definition 约定见 [`dsh-jobs`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/jobs/jobs/README.zh.md),注册表生命周期与准入策略见 [`dsh-jobs-local`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/jobs/jobs-local/README.zh.md),面向模型的 Consumer 见 [`dsh-tool-jobs`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/jobs/tool-jobs/README.zh.md)。
160
+ 抽象的 [`JobRegistry`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/jobs/jobs/src/index.ts) Service Definition 规定原子 `start`、限定调用方作用域的 `get` 和 `list`、`read`、`kill`、有界 `wait`、故障隔离的 `onJobDone` 与 `onJobsChanged` 监听器,以及 `attachController`;[`LocalJobRegistry`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/jobs/jobs-local/src/index.ts) 是其进程局部 Service Provider。授权会比较拥有者会话;拥有者清理与准入会使用确切的已注册 `Agent` 实例。本地 Service Provider 的 `maxConcurrentJobsPerOwner` 配置必须是正的安全整数,默认值为 `10`;它按确切 owner 统计 `running` 与 `stopping` 记录,所有无 owner 任务共享一个服务级桶,并在生产方终止结算后释放容量。Service Definition 约定见 [`dsh-jobs`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/jobs/jobs/README.zh.md),注册表生命周期与准入策略见 [`dsh-jobs-local`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/jobs/jobs-local/README.zh.md),面向模型的 Consumer 见 [`dsh-tool-jobs`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/jobs/tool-jobs/README.zh.md)。
161
161
 
162
162
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
163
163
 
@@ -31,7 +31,19 @@ interface ContentBlockMap {
31
31
  }
32
32
  ```
33
33
 
34
- 各块接口(完整字段见源码):`TextBlock`(`text`)、`ReasoningBlock`(thinking,区别于可见文本)、`ImageBlock`(一个持久的[图片附件](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/attachment.zh.md))、`ToolCallBlock`(`id: CallId`、`name`、原始 JSON `arguments`),以及 `ToolResultBlock`(`toolCallId`、嵌套 `content: ContentBlock[]`、`isError?`)。`ContentBlock = ContentBlockMap[ContentBlockType]`。仅当适配器、UI、压缩(compaction)和持久回放路径均支持某种新模态时,才将其纳入可合并扩展的 map。
34
+ 各块接口(完整字段见源码):`TextBlock`(`text`)、`ReasoningBlock`(thinking,区别于可见文本)、`ImageBlock`(一个持久的[图片附件](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/attachment.zh.md))、`ToolCallBlock`(`id: ToolCallId`、`name`、原始 JSON `arguments`),以及 `ToolResultBlock`(`toolCallId`、嵌套 `content: ContentBlock[]`、`isError?`)。`ContentBlock = ContentBlockMap[ContentBlockType]`。仅当适配器、UI、压缩(compaction)和持久回放路径均支持某种新模态时,才将其纳入可合并扩展的 map。
35
+
36
+ 图片访问方式属于请求序列化,不属于持久附件或确定性请求图片版本。`resolveImageAttachmentAccess()` 把附件提供方可选的宿主对象路径,与消费方为当前工具执行文件系统提供的映射组合起来。结果只适用于本次请求,不参与 `variantId`。
37
+
38
+ 源码:[`packages/llm/llm/src/content.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm/src/content.ts)
39
+
40
+ ```ts type-equiv
41
+ /** Execution-world path that model tools can use to read one normalized attachment. */
42
+ interface ImageAttachmentAccess {
43
+ /** Absolute path to immutable normalized bytes; callers must treat it as read-only. */
44
+ readonlyPath: string
45
+ }
46
+ ```
35
47
 
36
48
  源码:[`packages/llm/llm/src/message.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm/src/message.ts)
37
49
 
@@ -196,7 +208,7 @@ type StreamChunk =
196
208
  | { type: 'block-start'; index: number; blockType: ContentBlockType }
197
209
  | { type: 'text-delta'; index: number; text: string }
198
210
  | { type: 'reasoning-delta'; index: number; text: string }
199
- | { type: 'tool-call-delta'; index: number; id: CallId; name?: string; argumentsDelta: string }
211
+ | { type: 'tool-call-delta'; index: number; id: ToolCallId; name?: string; argumentsDelta: string }
200
212
  | { type: 'block-end'; index: number; block: ContentBlock }
201
213
  | { type: 'usage'; usage: TokenUsage }
202
214
  | {
@@ -229,6 +241,44 @@ interface LlmFailure {
229
241
  }
230
242
  ```
231
243
 
244
+ ## 请求图片定价
245
+
246
+ 提供方对请求图片收取视觉 token 的适配器通过覆写 `LlmAdapter.imageRequestPricing` 声明按路由的定价,消费方经 `ctx.llm.imageRequestPricing(provider, model)` 同步解析。token 计量服务在每次计量时解析路由模型的定价,使 compaction 的压力、保留与选段都按路由请求实际发送的形式为图片历史计价;DeepSeek 适配器复现自身的请求投影(按模型的像素预算、最旧优先 offload),并用官方公布的 v4 视觉计量为保留图片定价,已完成请求仍以 provider usage 为权威锚点。
247
+
248
+ ```ts type-equiv
249
+ /**
250
+ * Request price of one ordered image occurrence under one exact model route's
251
+ * request projection. Every occurrence resolves to the pair the wire actually
252
+ * carries: provider visual tokens for a retained image, plus the model-visible
253
+ * text sent with or instead of it (request-preview handle, offload placeholder,
254
+ * or text-only substitution). The caller prices `text` with its own text
255
+ * estimator so provider pricing never fixes a text tokenization.
256
+ */
257
+ interface LlmImageRequestPrice {
258
+ /** Provider visual tokens for the retained request image; 0 when only text represents this occurrence. */
259
+ visualTokens: number
260
+ /** Model-visible text sent for this occurrence, to be priced by the caller's text estimator. */
261
+ text: string
262
+ }
263
+ ```
264
+
265
+ ```ts type-equiv
266
+ /**
267
+ * Provider-side request-image pricing for one exact model route. Implemented
268
+ * by adapters whose provider charges visual tokens; consumers (the token
269
+ * meter) resolve it synchronously per measurement, so implementations must not
270
+ * perform I/O.
271
+ */
272
+ interface LlmImageRequestPricing {
273
+ /**
274
+ * Price every image occurrence of one request projection.
275
+ * @param images - durable image references in request order, one entry per occurrence.
276
+ * @returns one price per occurrence, aligned by index with `images`.
277
+ */
278
+ priceImages(images: readonly ImageAttachmentRef[]): readonly LlmImageRequestPrice[]
279
+ }
280
+ ```
281
+
232
282
  ## 适配器约定
233
283
 
234
284
  每个适配器必须遵守以下规则,每个消费方可以依赖它们:
@@ -273,7 +323,7 @@ interface AppIdentity {
273
323
 
274
324
  ## `TokenUsage`
275
325
 
276
- 逐调用 token 记账。各计数**互不重叠**:`inputTokens` 只包含未缓存输入;缓存输入单独报告,计费输入是三者之和。若提供方把缓存命中折入单一提示词总数(如 DeepSeek 的 `prompt_tokens`),适配器会再将其扣除。`reasoningTokens` 存在时只是信息性细节,已经包含在 `outputTokens` 中;汇总时不得重复相加。
326
+ 逐调用 token 记账。各计数**互不重叠**:`inputTokens` 只包含未缓存输入;缓存输入单独报告,计费输入是三者之和。若提供方把缓存命中折入单一提示词总数(如 DeepSeek 的 `prompt_tokens`),适配器会再将其扣除。可选的 `totalTokens` 是精确的提示词与输出聚合计数,由适配器保留提供方原值或从权威聚合计数重建;不可用或不一致时省略。`reasoningTokens` 存在时只是信息性细节,已经包含在 `outputTokens` 中;汇总时不得重复相加。
277
327
 
278
328
  ```ts type-equiv
279
329
  /**
@@ -287,6 +337,14 @@ interface AppIdentity {
287
337
  interface TokenUsage {
288
338
  inputTokens: number
289
339
  outputTokens: number
340
+ /**
341
+ * Exact full-call total including aggregate prompt and output tokens.
342
+ *
343
+ * Adapters preserve a provider total or derive it from authoritative
344
+ * aggregate prompt/output counters; they omit it when unavailable or
345
+ * inconsistent.
346
+ */
347
+ totalTokens?: number
290
348
  cacheReadTokens?: number
291
349
  cacheWriteTokens?: number
292
350
  reasoningTokens?: number
@@ -362,7 +420,7 @@ declare class BlockAssembler {
362
420
 
363
421
  源码:[`packages/llm/llm/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm/src/types.ts)
364
422
 
365
- 提供方与模型发现使用小型、提供方无关的描述符。模型目录仅供参考:路由仍以已注册提供方为键,适配器也可以接受未列出的模型 id。
423
+ 提供方与模型发现使用小型、提供方无关的描述符。模型目录仅供参考:路由仍以已注册提供方为键。
366
424
 
367
425
  注册适配器会返回一个句柄:既是释放器,也带有原子的路由替换——路由集合由用户配置决定的插件正需要它。
368
426
 
@@ -610,8 +668,6 @@ interface LlmModelDiscoveryRequest {
610
668
  api?: string
611
669
  /** Credential for this interrogation alone; the harness never stores it. */
612
670
  apiKey?: string
613
- /** Caller cancellation; implementations must settle promptly after it aborts. */
614
- signal?: AbortSignal
615
671
  }
616
672
  ```
617
673
 
@@ -671,6 +727,12 @@ interface LlmCallConfigAdapterDefaults {
671
727
  }
672
728
  ```
673
729
 
730
+ ## DeepSeek 官方请求扩展
731
+
732
+ `ctx.deepseekLlmApiExtensions` 是用于向 `deepseek-official` 请求添加顶层字段的提供方特定注册表。贡献插件通过 `register(field, provider)` 认领一个字段;适配器在序列化基础正文后调用 `prepare(request)`,并在 HTTP 前合并返回字段。已准备的 `accept()` 事务会在 2xx 后运行,因此贡献方可以提交交付状态,而不会把传输失败或提供方拒绝当作接受。准备、冲突与接受失败会使用 `REQUEST_EXTENSION`,并使模型请求失败。
733
+
734
+ [协议参考](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/deepseek-llm-api-wire-extensions.zh.md)定义确切的请求标头、扩展事务、字段版本和接收方义务。随附组合会将 [`dsh_session_log`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/session/session-log-deepseek/README.zh.md) 注册为无损增量权威日志后缀,并将 [`dsh_plugin_packages`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/plugin-package-inventory-deepseek/README.zh.md) 注册为完整存活 Loader 包集合。这些字段仍位于模型消息之外,也不会进入 pi-ai 适配器路径。
735
+
674
736
  ## 服务与提供方约定
675
737
 
676
738
  `LlmAdapter` 是提供方约定:创建子类、实现 `stream()`,再用 `ctx.llm.registerAdapter(providers, adapter)` 注册一个适配器实例。`GenerateOptions.provider` 选择已注册适配器;`GenerateOptions.model` 会传给该适配器,无需在生命周期启动时注册。重复提供方路由会原子失败。可选的 `providerRetryPolicy()` 会按路由捕获并填入 normal 默认值,`providerInfo()` 与异步 `listModels()` 方法则为 `LlmRuntime.listProviders()` / `listModels()` 提供分离的 selector 元数据。该目录仅供参考,不是请求白名单:适配器仍是权威,并可接受未列出的模型 id。单次异步 `resolveModel()` 查询返回确切模型身份,以及可选的对正确性敏感的上下文容量、适配器配置的 `defaultMaxTokens`、由模型持有的有序推理强度 ID 和可选的部署默认值;字段缺失表示元数据不可用或保留提供方持有的行为,而不表示目录成员关系无效。解析器会接收可选的取消信号,并且必须在信号中止后迅速完成结算。`LlmRuntime.resolveModelInfo()` 会校验聚合结果并返回分离值。在最终适配器边界,`resolveCallConfig()` 仅在 `maxTokens` 缺失时填入输出默认值,并校验和填入推理强度,因此直接调用也无法绕过任何一项已配置行为;直接分派会在等待解析前捕获一项适配器注册。agent loop 则使用 `prepareCall()`,使模型解析、请求头持久记录和分派全程使用同一项注册,保留来自同一次查询的分离上下文元数据,并报告适配器填入的配置字段。适配器查找发生在 `llm/stream` waterfall 的终端 continuation,因此 listener 可以在查找前短路调用,或路由一个可变的一次性请求。AgentLoop 在外层 waterfall 返回流句柄时观察到一次请求尝试;这个有限边界不能证明惰性终端适配器已构造完成或开始提供方 I/O。`block-start` / `block-end` 的 `index` 关联与 assembler 共同意味着适配器只需 emit 格式正确的分片——块重组不是每个适配器各自的问题。`ctx.llm.stream()` 与 `llm/stream` waterfall 在一个轮次中的位置见 [architecture.md](../index.md#turn-flow)。
@@ -719,6 +781,16 @@ declare abstract class LlmAdapter {
719
781
  * @returns a resolved policy, or `undefined` to use the normal defaults.
720
782
  */
721
783
  providerRetryPolicy(_provider: string): ResolvedRetryPolicy | undefined;
784
+ /**
785
+ * Resolve provider-side request-image pricing for one exact model route.
786
+ * The default declares none, so consumers fall back to their own neutral
787
+ * estimate. Implementations must answer synchronously without I/O; the
788
+ * token meter resolves this per measurement.
789
+ * @param _provider - a route passed to `registerAdapter()` for this instance.
790
+ * @param _model - exact model id passed to {@link GenerateOptions.model}.
791
+ * @returns route-owned image pricing, or `undefined` when the route declares none.
792
+ */
793
+ imageRequestPricing(_provider: string, _model: string): LlmImageRequestPricing | undefined;
722
794
  /**
723
795
  * List models this adapter can currently advertise for one owned provider.
724
796
  * The result is advisory: an adapter may accept unlisted model ids, and
@@ -770,6 +842,33 @@ declare abstract class LlmAdapter {
770
842
 
771
843
  Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — the language sides differ only in locale-specific paired document paths. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
772
844
 
845
+ <a id="ctxdeepseekllmapiextensions--deepseekllmapiextensionregistry"></a>
846
+
847
+ ### `ctx.deepseekLlmApiExtensions` — `DeepSeekLlmApiExtensionRegistry`
848
+
849
+ Registry of independently owned top-level fields for official DeepSeek requests.
850
+
851
+ ```ts cordis-catalog
852
+ /**
853
+ * Register the sole provider of one top-level request field. Registration is effect-scoped.
854
+ * @param field - declaration-merged field owned by the provider.
855
+ * @param provider - request-time field preparation and optional acceptance behavior.
856
+ * @returns disposer that releases the field.
857
+ */
858
+ register<K extends keyof DeepSeekLlmApiExtensionMap>( field: K, provider: DeepSeekLlmApiExtensionProvider<DeepSeekLlmApiExtensionMap[K]>, ): () => Promise<void>
859
+
860
+ /**
861
+ * Prepare every currently registered field from one immutable base request.
862
+ * Preparation failures reject before HTTP dispatch. Field values are cloned and frozen;
863
+ * providers retain no mutable alias to the outgoing request.
864
+ * @param request - exact serialized request facts before extension fields.
865
+ * @returns detached fields and their idempotent joint acceptance transaction.
866
+ */
867
+ async prepare(request: DeepSeekLlmApiExtensionRequest): Promise<PreparedDeepSeekLlmApiExtensions>
868
+ ```
869
+
870
+ Source: [`packages/llm/deepseek-llm-api-extensions/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/deepseek-llm-api-extensions/src/index.ts)
871
+
773
872
  <a id="ctxllm--llmruntime"></a>
774
873
 
775
874
  ### `ctx.llm` — `LlmRuntime`
@@ -791,7 +890,7 @@ registerAdapter(providers: string[], adapter: LlmAdapter): AdapterRegistrationHa
791
890
  * Describe provider routes with a registered adapter.
792
891
  * @returns detached provider metadata in registration order.
793
892
  */
794
- listProviders(): LlmProviderInfo[]
893
+ @Remote listProviders(): LlmProviderInfo[]
795
894
 
796
895
  /**
797
896
  * Declare provider routes an adapter plugin can activate through
@@ -807,7 +906,7 @@ registerConfigurableProviders(entries: readonly LlmConfigurableProvider[]): Dire
807
906
  * List every declared configurable provider, registered or dormant.
808
907
  * @returns detached directory entries in declaration order.
809
908
  */
810
- listConfigurableProviders(): LlmConfigurableProvider[]
909
+ @Remote listConfigurableProviders(): LlmConfigurableProvider[]
811
910
 
812
911
  /**
813
912
  * Offer to interrogate provider endpoints on behalf of the settings
@@ -816,10 +915,10 @@ listConfigurableProviders(): LlmConfigurableProvider[]
816
915
  * directory, and because a provider being *added* has no route to name yet.
817
916
  * Disposed with the fiber.
818
917
  * @param settingsNs - the namespace whose profiles this discovery serves.
819
- * @param discover - interrogates one endpoint; must honor `request.signal`.
918
+ * @param discover - interrogates one endpoint and must honor the supplied signal.
820
919
  * @returns the disposer that withdraws the offer.
821
920
  */
822
- registerModelDiscovery( settingsNs: string, discover: (request: LlmModelDiscoveryRequest) => Promise<readonly LlmDiscoveredModel[]>, ): () => void
921
+ registerModelDiscovery( settingsNs: string, discover: ( request: LlmModelDiscoveryRequest, signal?: AbortSignal, ) => Promise<readonly LlmDiscoveredModel[]>, ): () => void
823
922
 
824
923
  /**
825
924
  * Interrogate one provider endpoint for the models it advertises. The
@@ -828,9 +927,20 @@ registerModelDiscovery( settingsNs: string, discover: (request: LlmModelDiscover
828
927
  * candidate metadata a surface may offer for adoption.
829
928
  * @param settingsNs - namespace whose registered discovery serves this draft.
830
929
  * @param request - the endpoint, protocol, and one-shot credential to use.
930
+ * @param signal - caller cancellation.
831
931
  * @returns the advertised models, deduplicated in endpoint order.
832
932
  */
833
- async discoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, ): Promise<LlmDiscoveredModel[]>
933
+ async discoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, signal?: AbortSignal, ): Promise<LlmDiscoveredModel[]>
934
+
935
+ /**
936
+ * Remote adapter for one draft provider interrogation.
937
+ * @param settingsNs - namespace whose registered discovery serves this draft.
938
+ * @param request - endpoint, protocol, and one-shot credential to use.
939
+ * @param signal - caller cancellation supplied by the Remote carrier.
940
+ * @returns advertised models in endpoint order.
941
+ * @throws TypertRemoteFailure with `model-discovery-failed` when discovery refuses or fails.
942
+ */
943
+ @Remote('discoverModels') async remoteDiscoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, signal: AbortSignal, ): Promise<LlmDiscoveredModel[]>
834
944
 
835
945
  /**
836
946
  * Resolve the retry policy captured when one provider route was registered.
@@ -839,6 +949,17 @@ async discoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, ):
839
949
  */
840
950
  providerRetryPolicy(provider: string): ResolvedRetryPolicy
841
951
 
952
+ /**
953
+ * Resolve provider-side request-image pricing for one exact route, or
954
+ * `undefined` when the provider is unregistered or declares none. Unknown
955
+ * providers degrade to `undefined` rather than throwing because callers
956
+ * price durable history whose route may no longer be mounted.
957
+ * @param provider - provider route named by a request header.
958
+ * @param model - exact model id named by the same header.
959
+ * @returns the owning adapter's image pricing for the route, when declared.
960
+ */
961
+ imageRequestPricing(provider: string, model: string): LlmImageRequestPricing | undefined
962
+
842
963
  /**
843
964
  * Discover models advertised by one registered provider. Catalog membership
844
965
  * is advisory and never changes routing or request validation.
@@ -94,7 +94,7 @@ interface SessionHeader {
94
94
 
95
95
  ## 格式拒绝:本构建无法可靠读取的日志
96
96
 
97
- 后端用 `SessionFormatUnsupportedError` 拒绝无法可靠解读的日志,它与 `SessionPersistenceCorruptionError` 区分,因为数据没有损坏。header 的 `version` 比 `SESSION_FORMAT_VERSION` 新时,消息说明方向("由更新的 harness 写入,请升级 harness 后打开");比它旧时说明本构建没有升级路径。经过 legacy 形状归一化后,本构建生成词汇表(`KNOWN_SESSION_EVENT_TYPES`,由 `gen-persistence-catalog` 生成)之外的事件类型同样被拒绝,除非该事件的信封带 `ignorable: true`:静默跳过一个不认识的必需事件可能改变日志其余部分的解读方式。后端为每个会话保留独立文件时,消息附上原始日志路径,被拒绝的文本仍然可读。JSONL 后端直接从原始 header 行拒绝外来版本,先于当前 header 形状校验和任何事件行解码,因此结构完全不同的未来格式仍会报告升级方向,绝不会报"损坏";SQLite 则先由自己的 `SCHEMA_VERSION` pragma 把关整个文件的结构。设计理由与推迟建设的升级器链见 [session-log 版本机制 Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-08-10-session-log-version-mechanism.zh.md)。
97
+ 后端用 `SessionFormatUnsupportedError` 拒绝无法可靠解读的日志,它与 `SessionPersistenceCorruptionError` 区分,因为数据没有损坏。header 的 `version` 比 `SESSION_FORMAT_VERSION` 新时,消息说明方向("由更新的 harness 写入,请升级 harness 后打开");比它旧时说明本构建没有升级路径。经过 legacy 形状归一化后,本构建生成集合(`KNOWN_SESSION_EVENT_TYPES`,由 `gen-persistence-catalog` 生成)之外的事件类型也会拒绝重建,因为静默跳过该事件可能改变日志其余部分的解读方式。后端为每个会话保留独立文件时,消息附上原始日志路径,被拒绝的文本仍然可读。JSONL 后端直接从原始 header 行拒绝外来版本,先于校验本格式版本的 header 字段和解码任何事件行,因此结构完全不同的未来格式仍会报告升级方向,绝不会报"损坏";SQLite 则先由自己的 `SCHEMA_VERSION` pragma 把关整个文件的结构。设计理由与推迟建设的升级器链见[事件词汇表显式拒绝 Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/simplification/2026-08-25-fail-closed-session-event-vocabulary.zh.md)。
98
98
 
99
99
  ## `CreateSessionOptions`:seed 与元数据
100
100
 
@@ -236,7 +236,7 @@ interface SessionPersistenceSnapshot {
236
236
  两者都实现同一个抽象 `SessionPersistence`(在 `SessionEvent` 上执行 locate/create/append/prepare/load/inspect/readFrom/list/listSnapshots,观察方法可选支持取消),并通过共享的 `runPersistenceContract` 套件:
237
237
 
238
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 17 把同一分片块中字段完全匹配的 delta 连续段存为有界物理 `text-chunks`、`reasoning-chunks` 与 `tool-call-chunks` 行。它在返回前重建完整逻辑事件流,只打包新增的持久批次,并拒绝旧 schema,而不是执行迁移。
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,而不是执行迁移。
240
240
 
241
241
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
242
242
 
@@ -288,6 +288,14 @@ readRaw(_id: SessionId, signal?: AbortSignal): Promise<SessionRawArtifact | unde
288
288
  */
289
289
  abstract create(meta: SessionHeader): Promise<void>
290
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>
298
+
291
299
  /**
292
300
  * Durably persist a batch of events. Honors the append-only and contiguous-
293
301
  * seq contracts: the first event's `seq` MUST equal the stored next-seq
@@ -342,6 +350,17 @@ abstract load(id: SessionId): Promise<SessionInspection>
342
350
  */
343
351
  abstract inspect(id: SessionId, signal?: AbortSignal): Promise<SessionInspection>
344
352
 
353
+ /**
354
+ * Borrow one exact inspection while retaining any reusable prepared source.
355
+ * A cold observation must pin the exact prepared Session that a later
356
+ * {@link prepare} reserves. Implementations must not degrade this operation
357
+ * to a detached {@link inspect} result.
358
+ * @param id - persisted session to observe.
359
+ * @param signal - optional cancellation for preparation work.
360
+ * @returns a disposable immutable observation.
361
+ */
362
+ abstract borrowSession(id: SessionId, signal?: AbortSignal): Promise<BorrowedSessionSource>
363
+
345
364
  /**
346
365
  * Read the stored events from `fromSeq` onward — the read-from-seq
347
366
  * primitive for read models that resume from a watermark (e.g. a persisted
@@ -382,7 +401,7 @@ abstract list(signal?: AbortSignal): Promise<SessionHeader[]>
382
401
  abstract listSnapshots(signal?: AbortSignal): Promise<SessionPersistenceSnapshot[]>
383
402
  ```
384
403
 
385
- Types: [SessionEvent](./session.md) · [SessionId](./core.md)
404
+ Types: [Session](./session.md) · [SessionEvent](./session.md) · [SessionId](./core.md)
386
405
 
387
406
  Source: [`packages/session/session-persistence/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/session/session-persistence/src/index.ts)
388
407
  <!-- END GENERATED cordis-surface -->
@@ -29,7 +29,7 @@ interface PlanModeConfig {
29
29
  }
30
30
  ```
31
31
 
32
- `section` 缺失、为空白或不是字符串,以及任何未知键,都会在插件加载时失败,而不是被忽略。计划模式激活期间,确切的 `section` 文本以 order 50 渲染为 `plan:policy` [系统提示词段落](./system-prompt.md);未激活的计划模式不贡献任何文本。
32
+ `section` 缺失、为空白或不是字符串,以及任何未知键,都会在插件加载时失败,而不是被忽略。计划模式激活期间,确切的 `section` 文本以 first-party 顺序 500 渲染为 `plan:policy` [系统提示词段落](./system-prompt.md);未激活的计划模式不贡献任何文本。
33
33
 
34
34
  ## 退出工具与 `/plan` 命令
35
35