dsh-plugin-dev-kb 1.0.8 → 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.
Files changed (188) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.en.md +6 -6
  3. package/README.md +6 -6
  4. package/kb/INDEX.md +21 -5
  5. package/kb/README.md +11 -10
  6. package/kb/extra/AGENTS.md +4 -4
  7. package/kb/extra/cookbook/adding-a-remote-api.md +197 -0
  8. package/kb/extra/cookbook/adding-a-remote-api.zh.md +197 -0
  9. package/kb/extra/cookbook/adding-a-vendored-package.md +2 -2
  10. package/kb/extra/cookbook/adding-a-vendored-package.zh.md +2 -2
  11. package/kb/extra/deepseek-llm-api-wire-extensions.md +163 -0
  12. package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +163 -0
  13. package/kb/extra/development.md +8 -14
  14. package/kb/extra/development.zh.md +8 -14
  15. package/kb/extra/event-producer-consumer.md +55 -48
  16. package/kb/extra/event-producer-consumer.zh.md +58 -51
  17. package/kb/extra/glossary.md +1 -1
  18. package/kb/extra/glossary.zh.md +1 -1
  19. package/kb/extra/graph-atlas.md +0 -2
  20. package/kb/extra/graph-atlas.zh.md +0 -2
  21. package/kb/extra/i18n/README.md +4 -4
  22. package/kb/extra/i18n/README.zh.md +4 -4
  23. package/kb/extra/i18n/style-samples.md +2 -2
  24. package/kb/extra/module-graph.md +646 -926
  25. package/kb/extra/module-graph.zh.md +648 -928
  26. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.md +2 -2
  27. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.zh.md +2 -2
  28. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.md +2 -2
  29. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +2 -2
  30. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.md +2 -2
  31. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.zh.md +2 -2
  32. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +1 -1
  33. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +1 -1
  34. package/kb/extra/rescope.md +2 -2
  35. package/kb/extra/rescope.zh.md +2 -2
  36. package/kb/extra/subsystems/agent-team.md +28 -4
  37. package/kb/extra/subsystems/agent-team.zh.md +28 -4
  38. package/kb/extra/subsystems/attachment.md +168 -7
  39. package/kb/extra/subsystems/attachment.zh.md +168 -7
  40. package/kb/extra/subsystems/extensions.md +18 -0
  41. package/kb/extra/subsystems/extensions.zh.md +18 -0
  42. package/kb/extra/subsystems/feedback.md +4 -4
  43. package/kb/extra/subsystems/feedback.zh.md +4 -4
  44. package/kb/extra/subsystems/todo.md +32 -0
  45. package/kb/extra/subsystems/todo.zh.md +32 -0
  46. package/kb/extra/subsystems/webhook.md +70 -0
  47. package/kb/extra/subsystems/webhook.zh.md +70 -0
  48. package/kb/extra/testing.md +15 -10
  49. package/kb/extra/testing.zh.md +13 -8
  50. package/kb/extra/web-styling.md +4 -0
  51. package/kb/extra/web-styling.zh.md +4 -0
  52. package/kb/meta/search-index.json +309 -177
  53. package/kb/meta/site-pages.txt +183 -167
  54. package/kb/meta/source.json +5 -5
  55. package/kb/meta/topics.md +14 -6
  56. package/kb/site/develop/basic/publish.md +2 -2
  57. package/kb/site/develop/basic/tool.md +1 -1
  58. package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +5 -4
  59. package/kb/site/develop/framework/events.md +1 -1
  60. package/kb/site/develop/practice/dynamic-cordis.md +17 -0
  61. package/kb/site/develop/practice/llm-adapter.md +4 -3
  62. package/kb/site/en/develop/basic/publish.md +2 -2
  63. package/kb/site/en/develop/basic/tool.md +1 -1
  64. package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +5 -4
  65. package/kb/site/en/develop/framework/events.md +1 -1
  66. package/kb/site/en/develop/practice/dynamic-cordis.md +17 -0
  67. package/kb/site/en/develop/practice/llm-adapter.md +4 -3
  68. package/kb/site/en/guide/github-review.md +104 -0
  69. package/kb/site/en/guide/mcp-memory.md +103 -0
  70. package/kb/site/en/guide/network-proxy.md +87 -0
  71. package/kb/site/en/guide/providers.md +70 -17
  72. package/kb/site/en/guide/python-sdk.md +80 -34
  73. package/kb/site/en/guide/schedule.md +23 -0
  74. package/kb/site/en/reference/agent-lifecycle.md +6 -4
  75. package/kb/{extra → site/en/reference}/api-gateway.md +12 -10
  76. package/kb/site/en/reference/capability-seams.md +128 -73
  77. package/kb/site/en/reference/config-catalog.md +481 -360
  78. package/kb/site/en/reference/cookbook/adding-a-package.md +3 -4
  79. package/kb/site/en/reference/cookbook/adding-a-settings-card.md +12 -10
  80. package/kb/site/en/reference/cookbook/adding-a-tool.md +11 -4
  81. package/kb/site/en/reference/cookbook/adding-an-llm-adapter.md +1 -1
  82. package/kb/site/en/reference/cookbook/extension-cookbook.md +20 -17
  83. package/kb/site/en/reference/cordis-api/inherited.md +1 -1
  84. package/kb/site/en/reference/cordis-primer.md +2 -1
  85. package/kb/site/en/reference/index.md +30 -11
  86. package/kb/site/en/reference/persistence-catalog.md +148 -80
  87. package/kb/site/en/reference/subsystems/approval.md +10 -10
  88. package/kb/site/en/reference/subsystems/client-modules.md +58 -16
  89. package/kb/site/en/reference/subsystems/code-runtime.md +10 -6
  90. package/kb/site/en/reference/subsystems/commands.md +25 -16
  91. package/kb/site/en/reference/subsystems/compaction.md +11 -11
  92. package/kb/site/en/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +50 -24
  93. package/kb/site/en/reference/subsystems/core.md +156 -17
  94. package/kb/site/en/reference/subsystems/credentials.md +44 -3
  95. package/kb/site/en/reference/subsystems/filesystem.md +12 -2
  96. package/kb/site/en/reference/subsystems/goal.md +1 -1
  97. package/kb/site/en/reference/subsystems/index.md +7 -2
  98. package/kb/site/en/reference/subsystems/jobs.md +1 -1
  99. package/kb/site/en/reference/subsystems/llm-streaming.md +154 -12
  100. package/kb/site/en/reference/subsystems/permission-presets.md +6 -6
  101. package/kb/site/en/reference/subsystems/persistence.md +185 -175
  102. package/kb/site/en/reference/subsystems/plan.md +2 -2
  103. package/kb/site/en/reference/subsystems/sandbox.md +2 -0
  104. package/kb/site/en/reference/subsystems/schedule.md +9 -3
  105. package/kb/site/en/reference/subsystems/session-projection.md +115 -48
  106. package/kb/site/en/reference/subsystems/session-query.md +28 -14
  107. package/kb/site/en/reference/subsystems/session-reference.md +53 -8
  108. package/kb/site/en/reference/subsystems/session-telemetry.md +8 -8
  109. package/kb/site/en/reference/subsystems/session-title.md +6 -6
  110. package/kb/site/en/reference/subsystems/session.md +401 -99
  111. package/kb/site/en/reference/subsystems/settings.md +101 -6
  112. package/kb/site/en/reference/subsystems/skills.md +23 -0
  113. package/kb/site/en/reference/subsystems/slots.md +178 -0
  114. package/kb/site/en/reference/subsystems/spill.md +2 -2
  115. package/kb/site/en/reference/subsystems/storage.md +34 -3
  116. package/kb/site/en/reference/subsystems/subagent.md +122 -109
  117. package/kb/site/en/reference/subsystems/system-prompt.md +17 -4
  118. package/kb/site/en/reference/subsystems/token-meter.md +27 -12
  119. package/kb/site/en/reference/subsystems/tools.md +39 -39
  120. package/kb/site/en/reference/subsystems/typert.md +62 -55
  121. package/kb/site/en/reference/subsystems/user-questions.md +33 -33
  122. package/kb/site/en/reference/subsystems/web-client.md +98 -0
  123. package/kb/site/en/reference/subsystems/web-server.md +11 -5
  124. package/kb/site/en/reference/subsystems/web.md +7 -1
  125. package/kb/site/en/reference/subsystems/workspace.md +102 -9
  126. package/kb/site/en/reference/tool-catalog.md +86 -82
  127. package/kb/site/en/reference/tool-execution-pipeline.md +1 -1
  128. package/kb/site/guide/github-review.md +104 -0
  129. package/kb/site/guide/mcp-memory.md +103 -0
  130. package/kb/site/guide/network-proxy.md +87 -0
  131. package/kb/site/guide/providers.md +70 -17
  132. package/kb/site/guide/python-sdk.md +87 -41
  133. package/kb/site/guide/schedule.md +23 -0
  134. package/kb/site/reference/agent-lifecycle.md +6 -4
  135. package/kb/{extra/api-gateway.zh.md → site/reference/api-gateway.md} +12 -10
  136. package/kb/site/reference/capability-seams.md +128 -73
  137. package/kb/site/reference/config-catalog.md +481 -360
  138. package/kb/site/reference/cookbook/adding-a-package.md +3 -4
  139. package/kb/site/reference/cookbook/adding-a-settings-card.md +12 -10
  140. package/kb/site/reference/cookbook/adding-a-tool.md +11 -4
  141. package/kb/site/reference/cookbook/adding-an-llm-adapter.md +1 -1
  142. package/kb/site/reference/cookbook/extension-cookbook.md +20 -17
  143. package/kb/site/reference/cordis-api/inherited.md +1 -1
  144. package/kb/site/reference/cordis-primer.md +2 -1
  145. package/kb/site/reference/index.md +30 -11
  146. package/kb/site/reference/persistence-catalog.md +148 -80
  147. package/kb/site/reference/subsystems/approval.md +10 -10
  148. package/kb/site/reference/subsystems/client-modules.md +58 -16
  149. package/kb/site/reference/subsystems/code-runtime.md +10 -6
  150. package/kb/site/reference/subsystems/commands.md +25 -16
  151. package/kb/site/reference/subsystems/compaction.md +11 -11
  152. package/kb/site/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +50 -24
  153. package/kb/site/reference/subsystems/core.md +156 -17
  154. package/kb/site/reference/subsystems/credentials.md +44 -3
  155. package/kb/site/reference/subsystems/filesystem.md +12 -2
  156. package/kb/site/reference/subsystems/goal.md +1 -1
  157. package/kb/site/reference/subsystems/index.md +7 -2
  158. package/kb/site/reference/subsystems/jobs.md +1 -1
  159. package/kb/site/reference/subsystems/llm-streaming.md +154 -12
  160. package/kb/site/reference/subsystems/permission-presets.md +5 -5
  161. package/kb/site/reference/subsystems/persistence.md +184 -174
  162. package/kb/site/reference/subsystems/plan.md +2 -2
  163. package/kb/site/reference/subsystems/schedule.md +9 -3
  164. package/kb/site/reference/subsystems/session-projection.md +115 -48
  165. package/kb/site/reference/subsystems/session-query.md +28 -14
  166. package/kb/site/reference/subsystems/session-reference.md +53 -8
  167. package/kb/site/reference/subsystems/session-telemetry.md +8 -8
  168. package/kb/site/reference/subsystems/session-title.md +6 -6
  169. package/kb/site/reference/subsystems/session.md +401 -99
  170. package/kb/site/reference/subsystems/settings.md +101 -6
  171. package/kb/site/reference/subsystems/skills.md +23 -0
  172. package/kb/site/reference/subsystems/slots.md +178 -0
  173. package/kb/site/reference/subsystems/spill.md +2 -2
  174. package/kb/site/reference/subsystems/storage.md +34 -3
  175. package/kb/site/reference/subsystems/subagent.md +122 -109
  176. package/kb/site/reference/subsystems/system-prompt.md +17 -4
  177. package/kb/site/reference/subsystems/token-meter.md +27 -12
  178. package/kb/site/reference/subsystems/tools.md +39 -39
  179. package/kb/site/reference/subsystems/typert.md +62 -55
  180. package/kb/site/reference/subsystems/user-questions.md +33 -33
  181. package/kb/site/reference/subsystems/web-client.md +98 -0
  182. package/kb/site/reference/subsystems/web-server.md +11 -5
  183. package/kb/site/reference/subsystems/web.md +7 -1
  184. package/kb/site/reference/subsystems/workspace.md +102 -9
  185. package/kb/site/reference/tool-catalog.md +85 -81
  186. package/kb/site/reference/tool-execution-pipeline.md +1 -1
  187. package/package.json +2 -2
  188. package/skills/dsh-plugin-dev-kb.md +8 -6
@@ -1,16 +1,16 @@
1
- # 持久图片附件
1
+ # 持久附件
2
2
 
3
3
  [English](attachment.md) | 中文
4
4
 
5
- 附件 seam 将二进制图片的所有权与会话日志分离。生产方把经过校验的编码字节交给 [`ctx.attachments`](#ctxattachments--attachmentstore-abstract-seam);只有对象完成持久化后,该服务才会发布不可变的内容寻址引用。会话事件和模型可见的 `ImageBlock` 包含该引用及其元数据,绝不包含浏览器对象 URL、宿主临时路径、提供方 URL 或 base64 数据。
5
+ 附件 seam 将二进制图片和通用文件的所有权与会话日志分离。生产方把字节交给 [`ctx.attachments`](#ctxattachments--attachmentstore-abstract-seam);只有对象完成持久化后,该服务才会发布不可变的内容寻址引用。会话事件和模型可见的附件块包含该引用及其元数据,绝不包含浏览器对象 URL、宿主临时路径、提供方 URL 或 base64 数据。独立的 [`ctx.fileUploads`](#ctxfileuploads--fileuploads) 服务把浏览器文件传输与暂存凭证绑定到接收方 Agent。
6
6
 
7
- 未发送的浏览器草稿可以保留在内存中,原生客户端也可以将其暂存于操作系统临时存储。宿主接受用户消息后,会先把消息中的图片移到 `<DSH_HOME>/attachments/v1` 下,再追加用户事件。结构化模型图片输出遵循同样的先持久化、后追加事件规则。
7
+ 未发送的浏览器草稿可以保留在内存中,原生客户端也可以将其暂存于操作系统临时存储。浏览器通用文件取得暂存 prompt 凭证前会完成持久化。宿主接受用户消息后,会先把消息中的图片移到 `<DSH_HOME>/attachments/v1` 下,再追加用户事件。结构化模型图片输出遵循同样的先持久化、后追加事件规则。
8
8
 
9
9
  来源:[`packages/attachment/attachment/src/types.ts`](../../packages/attachment/attachment/src/types.ts)
10
10
 
11
11
  ## 标识与经过校验的元数据
12
12
 
13
- `AttachmentId` 是带类型标记的不透明字符串。本地后端目前生成 `sha256:<digest>`,但消费方既不能解析这种表示,也不能据此派生文件系统路径。
13
+ `AttachmentId` 是带类型标记的不透明字符串。本地后端目前生成 `sha256:<digest>`,但消费方既不能解析这种表示,也不能据此派生文件系统路径。消费方可以通过 `imageHostPath()` 询问附件提供方所持对象的位置,然后必须由当前执行文件系统判断模型工具能否读取该宿主路径。
14
14
 
15
15
  ```ts type-equiv
16
16
  /** Raster image formats accepted by the version-one attachment path. */
@@ -62,6 +62,38 @@ interface ImageAttachmentLimits {
62
62
 
63
63
  ## 提交与经校验读取的数据
64
64
 
65
+ ```ts type-equiv
66
+ /**
67
+ * Browser-submitted prompt content accepted by Host prompt endpoints; the
68
+ * accepting Host promotes image parts to durable references through
69
+ * `ctx.attachments.admitPromptContent()` before any message is created, so a wire caller can
70
+ * never cite an attachment it did not upload.
71
+ */
72
+ type PromptContentPart =
73
+ | { readonly type: 'text'; readonly text: string }
74
+ | {
75
+ readonly type: 'image'
76
+ readonly mediaType: ImageMediaType
77
+ readonly data: string
78
+ readonly name?: string
79
+ }
80
+ ```
81
+
82
+ ```ts type-equiv
83
+ /** Host prompt content whose file receipts are resolved and whose image bytes await admission. */
84
+ type AttachmentAdmissionPart =
85
+ | PromptContentPart
86
+ | { readonly type: 'file'; readonly attachment: FileAttachmentRef }
87
+ ```
88
+
89
+ ```ts type-equiv
90
+ /** Host-admitted prompt content with every attachment represented by its durable reference. */
91
+ type AdmittedPromptContentPart =
92
+ | { readonly type: 'text'; readonly text: string }
93
+ | { readonly type: 'image'; readonly attachment: ImageAttachmentRef }
94
+ | { readonly type: 'file'; readonly attachment: FileAttachmentRef }
95
+ ```
96
+
65
97
  ```ts type-equiv
66
98
  /** Base64-encoded image upload accompanying one wire request. */
67
99
  interface EncodedImageAttachment {
@@ -98,7 +130,7 @@ interface StoredImageAttachment {
98
130
  interface ImageRequestPolicy {
99
131
  /** Maximum width multiplied by height after aspect-preserving projection. */
100
132
  maxPixels: number
101
- /** Encoded-byte cap before base64 expansion or Files API upload. */
133
+ /** Encoded-byte target before base64 expansion or Files API upload; the smallest quality-ladder output is kept when no quality fits. */
102
134
  maxBytes: number
103
135
  }
104
136
  ```
@@ -125,7 +157,7 @@ interface RequestImageAttachment {
125
157
  }
126
158
  ```
127
159
 
128
- `saveImage()` 准备并原子提交提供方无关的规范化附件,然后直接返回 `ImageAttachmentRef`。`saveImages()` 在发布批次前为每个成员各准备一次经过验证的附件,因此校验拒绝不会留下部分对象,发布也不会重复解码或选择质量。`admitEncodedImages()` 是面向 base64 上传的 wire 入口,把张数、聚合字节和有序批量准入交给 `saveImages()`。`readImage()` 校验来自已授权会话路径的规范化附件。`readImageRequest()` 按确切路由的像素和字节预算派生并缓存请求版本;新条目在发布前完整解码,缓存命中只做有界元数据探测。调用方需要有序批次时,对单数方法使用 `Promise.all`。本地实现按需编码首选候选、合并相同请求身份的并发任务、允许每个等待方单独取消、没有等待方时停止共享任务,并通过实例级限流器限制全部变换,默认同时执行两项。该服务不规定保留策略:恢复和 fork 后的会话可能共享对象,因此基于引用的垃圾回收会延期实现,不与单个会话的删除绑定。
160
+ `saveImage()` 准备并原子提交提供方无关的规范化附件,然后直接返回 `ImageAttachmentRef`。`saveImages()` 在发布批次前为每个成员各准备一次经过验证的附件,因此校验拒绝不会留下部分对象,发布也不会重复解码或选择质量。`admitPromptContent()` 在文件凭证解析后接收完整且有序的 Host prompt,把 base64 图片上传替换为持久引用,并让持久文件引用原样通过。`admitEncodedImages()` 支持其他 wire 入口,把张数、聚合字节和有序批量准入交给 `saveImages()`。`admitEncodedFile()` 让编码协议适配器使用服务拥有的规范 base64 准入,`isAttachmentError()` 让这些适配器无需导入实现辅助函数即可识别稳定的附件错误。`readImage()` 校验来自已授权会话路径的规范化附件。`imageHostPath()` 只公开提供方所持对象的宿主位置,不判断当前工具执行环境能否读取它。`readImageRequest()` 按确切路由的像素和字节预算派生并缓存确定性请求版本。该版本包含编码字节和元数据,不包含执行环境路径。新条目在发布前完整解码,缓存命中只做有界元数据探测。调用方需要有序批次时,对单数方法使用 `Promise.all`。本地实现按需编码首选候选、合并相同请求身份的并发任务、允许每个等待方单独取消、没有等待方时停止共享任务,并通过实例级限流器限制全部变换,默认同时执行两项。该服务不规定保留策略:恢复和 fork 后的会话可能共享对象,因此基于引用的垃圾回收会延期实现,不与单个会话的删除绑定。
129
161
 
130
162
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
131
163
 
@@ -157,6 +189,30 @@ abstract validateImage(input: SaveImageAttachment): Promise<void>
157
189
  */
158
190
  async saveImages(inputs: readonly SaveImageAttachment[]): Promise<readonly ImageAttachmentRef[]>
159
191
 
192
+ /**
193
+ * Admit one Host prompt and replace each uploaded image with its durable reference.
194
+ * Text and durable file references pass through unchanged. A prompt without image parts performs no storage operation.
195
+ * @param content - prompt parts in message order after file receipt resolution.
196
+ * @returns admitted prompt parts in the same order as `content`.
197
+ * @throws AttachmentError when the image batch is refused.
198
+ */
199
+ async admitPromptContent( content: readonly AttachmentAdmissionPart[], ): Promise<AdmittedPromptContentPart[]>
200
+
201
+ /**
202
+ * Decode and durably commit one canonical base64 file upload.
203
+ * @param input - canonical base64 bytes and optional display name.
204
+ * @returns the durable content-addressed file reference.
205
+ * @throws AttachmentError when the encoding or storage operation is refused.
206
+ */
207
+ admitEncodedFile(input: EncodedFileAttachment): Promise<FileAttachmentRef>
208
+
209
+ /**
210
+ * Identify a failure emitted by this attachment capability by its stable code.
211
+ * @param error - value caught from an attachment operation.
212
+ * @returns whether the value is an attachment failure.
213
+ */
214
+ isAttachmentError(error: unknown): error is AttachmentError
215
+
160
216
  /**
161
217
  * Validate and durably commit one image before its owning session event is appended.
162
218
  * The returned reference describes the persisted normalized image. When
@@ -176,10 +232,55 @@ abstract saveImage(input: SaveImageAttachment): Promise<ImageAttachmentRef>
176
232
  */
177
233
  abstract readImage(ref: ImageAttachmentRef, signal?: AbortSignal): Promise<StoredImageAttachment>
178
234
 
235
+ /**
236
+ * Locate the provider-owned normalized object in the harness host filesystem.
237
+ * @param ref - durable normalized attachment reference.
238
+ * @returns an absolute host path, or undefined when this backend is not host-file-backed.
239
+ * @throws an AttachmentError when the durable reference is invalid.
240
+ */
241
+ imageHostPath(ref: ImageAttachmentRef): string | undefined
242
+
243
+ /**
244
+ * Durably commit one file byte-for-byte before its owning session event is
245
+ * appended. Files carry no admission limits: any byte content and length is
246
+ * accepted, and the stored object is the exact submitted bytes. Backends
247
+ * without verbatim file storage keep this default rejection.
248
+ * @param input - exact bytes and optional display name.
249
+ * @returns the durable content-addressed file reference.
250
+ */
251
+ saveFile(input: SaveFileAttachment): Promise<FileAttachmentRef>
252
+
253
+ /**
254
+ * Durably commit one file byte-for-byte from bounded chunks. Providers must
255
+ * apply backpressure and must not collect the complete file in memory.
256
+ * Backends without streamed verbatim storage keep this default rejection.
257
+ * @param input - ordered exact bytes, optional cancellation, and display name.
258
+ * @returns the durable content-addressed file reference.
259
+ */
260
+ saveFileStream(input: SaveFileStreamAttachment): Promise<FileAttachmentRef>
261
+
262
+ /**
263
+ * Read and verify one verbatim stored file as bounded chunks. Providers must
264
+ * not collect the complete file in memory. Backends without verbatim file
265
+ * reads keep this default rejection.
266
+ * @param ref - durable reference from the session log.
267
+ * @param signal - optional cancellation for backend reads and verification work.
268
+ * @returns exact file bytes in order; integrity failures reject the iteration.
269
+ */
270
+ async *readFileStream( ref: FileAttachmentRef, signal?: AbortSignal, ): AsyncIterable<Uint8Array>
271
+
272
+ /**
273
+ * Locate the verbatim stored file object in the harness host filesystem.
274
+ * @param ref - durable file reference.
275
+ * @returns an absolute host path, or undefined when this backend is not host-file-backed.
276
+ * @throws an AttachmentError when the durable reference is invalid.
277
+ */
278
+ fileHostPath(ref: FileAttachmentRef): string | undefined
279
+
179
280
  /**
180
281
  * Generate or read one deterministic model-request version from the stored normalized image.
181
282
  * @param ref - durable provider-independent normalized attachment reference.
182
- * @param policy - exact route pixel and encoded-byte budget.
283
+ * @param policy - exact route pixel budget and encoded-byte target; a target no ladder quality meets yields the smallest ladder output.
183
284
  * @param signal - optional cancellation.
184
285
  * @returns request bytes and the cache/upload identity covering every transform input.
185
286
  */
@@ -187,4 +288,64 @@ readImageRequest( ref: ImageAttachmentRef, policy: ImageRequestPolicy, signal?:
187
288
  ```
188
289
 
189
290
  Source: [`packages/attachment/attachment/src/index.ts`](../../packages/attachment/attachment/src/index.ts)
291
+
292
+ <a id="ctxfileuploads--fileuploads"></a>
293
+
294
+ ### `ctx.fileUploads` — `FileUploads`
295
+
296
+ Host service owning upload storage and Agent-scoped staged receipts.
297
+
298
+ ```ts cordis-catalog
299
+ /**
300
+ * Register the ordinary-Session resolver used when a raw upload addresses a cold Session.
301
+ * @param resolve - resolver that returns the exact live Agent or throws a Remote error.
302
+ * @returns disposer removing this resolver.
303
+ */
304
+ registerAgentResolver(resolve: AgentResolver): () => void
305
+
306
+ /**
307
+ * Persist one encoded upload and stage it under the Agent receiver selected by Typert.
308
+ * @param agent - receiving Agent resolved from the Remote Agent scope.
309
+ * @param request - canonical base64 bytes and optional display name.
310
+ * @param signal - caller cancellation before storage begins.
311
+ * @returns the staged receipt and durable file reference.
312
+ */
313
+ @Remote('upload') upload(agent: Agent, request: EncodedFileUploadRequest, signal: AbortSignal): Promise<FileUploadValue>
314
+
315
+ /**
316
+ * Persist raw chunks for one Session without aggregating the upload.
317
+ * @param request - Session identity, ordered bytes, cancellation, and optional display name.
318
+ * @returns the staged receipt and durable file reference.
319
+ */
320
+ async uploadStream(request: { readonly sessionId: SessionId readonly data: AsyncIterable<Uint8Array> readonly signal?: AbortSignal readonly name?: string }): Promise<FileUploadValue>
321
+
322
+ /**
323
+ * Resolve one staged receipt inside its receiving Agent scope.
324
+ * @param agent - receiving Agent.
325
+ * @param receiptId - opaque receipt minted for one completed upload.
326
+ * @returns durable file reference, or `undefined` for an unknown or foreign receipt.
327
+ */
328
+ resolve(agent: Agent, receiptId: FileUploadReceiptId): FileAttachmentRef | undefined
329
+
330
+ /**
331
+ * Bind receipts while one prompt enters an Agent inbox.
332
+ * Disposal restores every prior binding unless the caller commits successful delivery.
333
+ * @param agent - receiving Agent.
334
+ * @param receiptIds - distinct staged receipts referenced by the prompt.
335
+ * @param requestId - prompt identity later observed in queue or history.
336
+ * @returns binding kept after commit until queue or history observation retires its receipts.
337
+ */
338
+ bindPrompt( agent: Agent, receiptIds: readonly FileUploadReceiptId[], requestId: string, ): PromptFileBinding
339
+
340
+ /**
341
+ * Retire every receipt accepted by one removed queue occurrence.
342
+ * @param agent - receiving Agent.
343
+ * @param requestId - prompt identity carried by the queue occurrence.
344
+ */
345
+ retirePrompt(agent: Agent, requestId: string): void
346
+ ```
347
+
348
+ Types: [Agent](core.zh.md) · [SessionId](core.zh.md)
349
+
350
+ Source: [`packages/client/file-upload/src/index.ts`](../../packages/client/file-upload/src/index.ts)
190
351
  <!-- END GENERATED cordis-surface -->
@@ -256,6 +256,24 @@ Types: [Agent](core.md)
256
256
 
257
257
  Source: [`packages/extensions/cordis-host-runner/src/index.ts`](../../packages/extensions/cordis-host-runner/src/index.ts)
258
258
 
259
+ <a id="ctxinspector--inspectorservice"></a>
260
+
261
+ ### `ctx.inspector` — `InspectorService`
262
+
263
+ Shared Host/Client service façade over the realm's source publisher.
264
+
265
+ ```ts cordis-catalog
266
+ /**
267
+ * Publish one JSON observation without waiting for Worker delivery.
268
+ * @param topic - Domain-owned topic name.
269
+ * @param payload - JSON value validated before it reaches the carrier.
270
+ * @param monotonicMs - Source-clock timestamp; defaults to `performance.now()`.
271
+ */
272
+ publish(topic: string, payload: InspectorJsonValue, monotonicMs?: number): void
273
+ ```
274
+
275
+ Source: [`packages/experimental/inspector/src/index.ts`](../../packages/experimental/inspector/src/index.ts)
276
+
259
277
  <a id="cordis-events"></a>
260
278
 
261
279
  ### `cordis/*` events
@@ -256,6 +256,24 @@ Types: [Agent](core.zh.md)
256
256
 
257
257
  Source: [`packages/extensions/cordis-host-runner/src/index.ts`](../../packages/extensions/cordis-host-runner/src/index.ts)
258
258
 
259
+ <a id="ctxinspector--inspectorservice"></a>
260
+
261
+ ### `ctx.inspector` — `InspectorService`
262
+
263
+ Shared Host/Client service façade over the realm's source publisher.
264
+
265
+ ```ts cordis-catalog
266
+ /**
267
+ * Publish one JSON observation without waiting for Worker delivery.
268
+ * @param topic - Domain-owned topic name.
269
+ * @param payload - JSON value validated before it reaches the carrier.
270
+ * @param monotonicMs - Source-clock timestamp; defaults to `performance.now()`.
271
+ */
272
+ publish(topic: string, payload: InspectorJsonValue, monotonicMs?: number): void
273
+ ```
274
+
275
+ Source: [`packages/experimental/inspector/src/index.ts`](../../packages/experimental/inspector/src/index.ts)
276
+
259
277
  <a id="cordis-events"></a>
260
278
 
261
279
  ### `cordis/*` events
@@ -191,13 +191,13 @@ One Session sidecar row contains its header identity `{createdAt, cwd}` and feed
191
191
 
192
192
  ## Target and lifecycle authority
193
193
 
194
- `SessionPersistence.inspect()` supplies the target Session observation without publishing or resuming an Agent and without committing cold repair. A cold `listSnapshots()` preflight classifies definite absence; inspection failure for a catalogued Session propagates as infrastructure failure. `put` accepts only a non-empty, append-origin `assistant/message` with the requested `MessageId`; replacement-origin, usage-only empty, and non-assistant records are not feedback targets.
194
+ A live owner's in-memory log supplies the target Session observation directly; a cold target is read through a `SessionPersistence.open(id, 'read')` handle without publishing or resuming an Agent and without writing recovery. A `stat(id)` preflight classifies definite absence; a read failure for a Session `stat` confirmed propagates as infrastructure failure. `put` accepts only a non-empty, append-origin `assistant/message` with the requested `MessageId`; replacement-origin, usage-only empty, and non-assistant records are not feedback targets.
195
195
 
196
196
  The stored `{createdAt, cwd}` identity must match the inspected header. A mismatch is treated as absence: `list` returns no items, while `put` may replace the stale row with one bound to the current header identity. Forks use a new Session identity and receive no sidecar copy even when their seed contains the same messages.
197
197
 
198
198
  ## Persistence and Remote contract
199
199
 
200
- The service stores whole Session rows in the `message_feedback` storage domain through `ctx.storageDomain`. Before `put` commits a row that references a target message, a matching live target passes through the canonical `ctx.sessions.flush` checkpoint; both live and cold paths are then physically read from sequence zero through `SessionPersistence.readFrom`. The resulting observation is revalidated before the sidecar write, so the durable target log always precedes its sidecar commit. `maxNoteBytes` is required and bounds note text by UTF-8 bytes; the Web Host composition sets `8192`. The package publishes the Host `messageFeedback.list`, `messageFeedback.put`, and `messageFeedback.delete` unary Remote contract through `TypertRemoteService` and `@Remote`; the generated Cordis API below is the method-level authority.
200
+ The service stores whole Session rows in the `message_feedback` storage domain through `ctx.storageDomain`. Before `put` commits a row that references a target message, a matching live target passes through the canonical `ctx.sessions.flush` checkpoint; both live and cold paths are then physically read from sequence zero through a fresh read handle, which observes at least the flushed prefix by the seam's freshness guarantee. The resulting observation is revalidated before the sidecar write, so the durable target log always precedes its sidecar commit. `maxNoteBytes` is required and bounds note text by UTF-8 bytes; the Web Host composition sets `8192`. The package publishes the Host `messageFeedback.list`, `messageFeedback.put`, and `messageFeedback.delete` unary Remote contract through `TypertRemoteService` and `@Remote`; the generated Cordis API below is the method-level authority.
201
201
 
202
202
  Plugin disposal closes mutation admission, drains accepted per-Session queue work, and then closes the storage domain.
203
203
 
@@ -205,14 +205,14 @@ Plugin disposal closes mutation admission, drains accepted per-Session queue wor
205
205
 
206
206
  [`@deepseek-ai/dsh-client-ui-message-feedback`](../../packages/client/ui-message-feedback) is the browser consumer. `@deepseek-ai/dsh-api-remotes` mounts the generated `messageFeedback` contribution, so the plugin calls `ctx.remote.messageFeedback` and never touches the transport.
207
207
 
208
- The controls are the `feedback` entry (order 10) of the `conversation.chat.assistant-actions` list slot, which `ui-conversation` declares and renders inside the finalized assistant message's IconActions row. Reaching that render site required one plumbing change: `AssistantMessageNode` now carries the optional `messageId` from the `assistant/message` event. The field is absent on interruption-frozen partials, and the render site skips the slot when it is absent. The strip renders once per turn, on the closing assistant message: the Host accepts every append-origin step message as a target, but earlier steps of a multi-step turn render tool rows rather than a rateable body, so the UI exposes a narrower set than the Host contract allows.
208
+ The controls are the `feedback` entry (order 10) of the `conversation.chat.assistant-actions` list slot, which `ui-conversation` declares and renders inside the finalized assistant message's IconActions row. `AssistantMessageNode` carries the optional `messageId` from the `assistant/message` event. The field is absent on interruption-frozen partials, and the render site skips the slot when it is absent. The strip renders once per turn, on the closing assistant message: the Host accepts every append-origin step message as a target, but earlier steps of a multi-step turn render tool rows rather than a rateable body, so the UI exposes a narrower set than the Host contract allows.
209
209
 
210
210
  One `MessageFeedbackController` per Session backs every message control in that Session: a single `list` read seeds the whole transcript, deferred to first hover or focus rather than fired on mount. Each mutation sends the version that controller last observed as `ifVersion`; a `version-conflict` reply carries the authoritative item, so the controller reconciles from the reply instead of refetching. Mutations serialize per Session so a queued operation compares against the committed version. A `connection/reset` refreshes only Sessions already read.
211
211
 
212
212
  ## Boundaries and limitations
213
213
 
214
214
  - The mutation queue is process-local. Storage-domain has no cross-process conditional write, so multiple Host writers to one storage root have no compare-and-swap or lost-update guarantee.
215
- - Session persistence has no durable deletion API. The service does not treat `session/disposed` or `host/session-removed` as deletion and therefore performs no fake cascade; orphan sidecar rows may remain after out-of-band log removal.
215
+ - Session persistence has no durable deletion API. The service does not treat `session/disposed` or `api-session/removed` as deletion and therefore performs no fake cascade; orphan sidecar rows may remain after out-of-band log removal.
216
216
  - A request in the narrow interval after live detach but before the persistence catalog materializes the header can receive `session-not-found`; callers retry after retirement materialization.
217
217
  - Cold requests scan the complete Session snapshot catalog because persistence has no lookup-by-id metadata operation. One Session row also has no item-count or aggregate-byte cap; `maxNoteBytes` bounds only each note until a concrete consumer owns a row policy.
218
218
  - Header identity detects a reused id only when `{createdAt, cwd}` differs; a cloned log retaining the same header identity is indistinguishable by this contract.
@@ -191,13 +191,13 @@ type MessageFeedbackDeleteResult =
191
191
 
192
192
  ## 目标与生命周期权威
193
193
 
194
- `SessionPersistence.inspect()` 提供目标 Session 的观测,且不会发布或恢复 Agent,也不会提交 cold repair。cold 路径先由 `listSnapshots()` 预检明确不存在;已进入目录的 Session 若检查失败,会按基础设施故障原样传播。`put` 只接受具有指定 `MessageId` 的非空、append-origin `assistant/message`;replacement-origin、仅承载 usage 的空记录和非 assistant 记录都不是反馈目标。
194
+ live 持有者的内存日志直接提供目标 Session 的观测;cold 目标则通过 `SessionPersistence.open(id, 'read')` 句柄读取,既不会发布或恢复 Agent,也不会写入恢复内容。先由 `stat(id)` 预检明确不存在;`stat` 已确认存在的 Session 若读取失败,会按基础设施故障原样传播。`put` 只接受具有指定 `MessageId` 的非空、append-origin `assistant/message`;replacement-origin、仅承载 usage 的空记录和非 assistant 记录都不是反馈目标。
195
195
 
196
196
  存储的 `{createdAt, cwd}` 身份必须与检查所得 header 匹配。不匹配按不存在处理:`list` 返回空条目,`put` 则可用绑定当前 header 身份的新记录替换陈旧行。fork 使用新的 Session 身份,即使种子包含相同消息,也不获得伴随记录副本。
197
197
 
198
198
  ## 持久化与 Remote 约定
199
199
 
200
- 服务通过 `ctx.storageDomain` 在 `message_feedback` 存储域中保存完整 Session 行。`put` 提交引用目标消息的伴随记录前,身份匹配的 live 目标先经过权威 `ctx.sessions.flush` checkpoint;随后 live 与 cold 路径都会通过 `SessionPersistence.readFrom` 从序列零做物理复读。写入伴随记录前会再次校验所得观测,因此目标日志的持久提交始终先于其伴随记录。`maxNoteBytes` 为必填项,按 UTF-8 字节限制备注文本;Web Host 组合将其设为 `8192`。该包通过 `TypertRemoteService` 与 `@Remote` 发布 Host `messageFeedback.list`、`messageFeedback.put` 和 `messageFeedback.delete` 一元 Remote 约定;下方生成的 Cordis API 是方法级权威。
200
+ 服务通过 `ctx.storageDomain` 在 `message_feedback` 存储域中保存完整 Session 行。`put` 提交引用目标消息的伴随记录前,身份匹配的 live 目标先经过权威 `ctx.sessions.flush` checkpoint;随后 live 与 cold 路径都会通过一个新开的读句柄从序列零做物理复读,依据该 seam 的新鲜度保证,它至少能观察到已 flush 的前缀。写入伴随记录前会再次校验所得观测,因此目标日志的持久提交始终先于其伴随记录。`maxNoteBytes` 为必填项,按 UTF-8 字节限制备注文本;Web Host 组合将其设为 `8192`。该包通过 `TypertRemoteService` 与 `@Remote` 发布 Host `messageFeedback.list`、`messageFeedback.put` 和 `messageFeedback.delete` 一元 Remote 约定;下方生成的 Cordis API 是方法级权威。
201
201
 
202
202
  Plugin disposal 会先关闭变更接纳,排空已进入各 Session 队列的工作,然后才关闭 storage domain。
203
203
 
@@ -205,14 +205,14 @@ Plugin disposal 会先关闭变更接纳,排空已进入各 Session 队列的
205
205
 
206
206
  [`@deepseek-ai/dsh-client-ui-message-feedback`](../../packages/client/ui-message-feedback) 是浏览器侧消费方。`@deepseek-ai/dsh-api-remotes` 挂载生成的 `messageFeedback` 贡献,因此该插件调用 `ctx.remote.messageFeedback`,不接触传输层。
207
207
 
208
- 控件是 `conversation.chat.assistant-actions` list slot 的 `feedback` 条目(order 10),该 slot 由 `ui-conversation` 声明,并渲染在已定稿助手消息的 IconActions 行内。为抵达该渲染点需要一处管道改动:`AssistantMessageNode` 现在携带来自 `assistant/message` 事件的可选 `messageId`。被中断冻结的部分输出没有该字段,渲染点在字段缺失时跳过该 slot。该操作栏每个 Turn 渲染一次,位于收尾的助手消息上:Host 接受每条 append-origin 步骤消息作为目标,但多步骤 Turn 中较早的步骤渲染的是工具行而非可评分正文,因此 UI 暴露的范围比 Host 约定允许的更窄。
208
+ 控件是 `conversation.chat.assistant-actions` list slot 的 `feedback` 条目(order 10),该 slot 由 `ui-conversation` 声明,并渲染在已定稿助手消息的 IconActions 行内。`AssistantMessageNode` 携带来自 `assistant/message` 事件的可选 `messageId`。被中断冻结的部分输出没有该字段,渲染点在字段缺失时跳过该 slot。该操作栏每个 Turn 渲染一次,位于收尾的助手消息上:Host 接受每条 append-origin 步骤消息作为目标,但多步骤 Turn 中较早的步骤渲染的是工具行而非可评分正文,因此 UI 暴露的范围比 Host 约定允许的更窄。
209
209
 
210
210
  每个 Session 一个 `MessageFeedbackController`,支撑该 Session 内所有消息的控件:一次 `list` 读取即填充整段对话,且延迟到首次 hover 或 focus 才发起,而非挂载时触发。每次变更把该 controller 最后观察到的版本作为 `ifVersion` 发送;`version-conflict` 响应携带权威条目,controller 据此对账而不重新拉取。变更按 Session 串行,排队操作与已提交版本比较。`connection/reset` 只刷新已读取过的 Session。
211
211
 
212
212
  ## 边界与限制
213
213
 
214
214
  - 变更队列仅在进程内生效。storage-domain 没有跨进程条件写,因此多个 Host 写入同一存储根目录时,不提供 compare-and-swap 或防止丢失更新的保证。
215
- - Session persistence 没有持久删除接口。服务不把 `session/disposed` 或 `host/session-removed` 当作删除,因此不伪造级联;在带外移除日志后,孤儿伴随记录可能继续存在。
215
+ - Session persistence 没有持久删除接口。服务不把 `session/disposed` 或 `api-session/removed` 当作删除,因此不伪造级联;在带外移除日志后,孤儿伴随记录可能继续存在。
216
216
  - 请求若恰好落在 live detach 之后、persistence catalog 物化 header 之前的极短窗口,可能收到 `session-not-found`;调用方应在 retirement materialization 后重试。
217
217
  - 由于 persistence 没有按 id 读取元数据的操作,cold 请求会扫描完整的 Session snapshot 目录。单个 Session 行也没有条目数或聚合字节上限;在具体消费方拥有行策略之前,`maxNoteBytes` 只限制每条备注。
218
218
  - 只有 `{createdAt, cwd}` 不同时,header 身份才能识别复用的 id;本约定无法区分保留相同 header 身份的克隆日志。
@@ -0,0 +1,32 @@
1
+ # Todo
2
+
3
+ English | [中文](todo.zh.md)
4
+
5
+ The durable todo vocabulary owned by [`@deepseek-ai/dsh-tool-todo`](../../packages/todo/tool-todo/README.md). The model-facing tool replaces one agent session's whole list; the package also owns the event declaration, replay projection, and invariant companion. Tool behavior and configuration are on the [package README](../../packages/todo/tool-todo/README.md).
6
+
7
+ Source: [`packages/todo/tool-todo/src/types.ts`](../../packages/todo/tool-todo/src/types.ts)
8
+
9
+ ## `TodoItem` — one list entry
10
+
11
+ ```ts type-equiv
12
+ /**
13
+ * One entry in an agent's todo list — the unit of the `todo/write`
14
+ * whole-list snapshot declared by this package.
15
+ *
16
+ * Deliberately minimal: a human-readable `content` line and a three-state
17
+ * `status`. No id, priority, or `activeForm` — the list is replaced wholesale
18
+ * on every write (last-write-wins), so entries need no stable identity. The
19
+ * three statuses describe the complete portable lifecycle needed by model and
20
+ * UI consumers.
21
+ */
22
+ interface TodoItem {
23
+ /** What this task is — a short imperative line shown in the UI. */
24
+ content: string
25
+ /** Lifecycle state. `in_progress` marks a task being worked now; parallel work may mark several. */
26
+ status: 'pending' | 'in_progress' | 'completed'
27
+ }
28
+ ```
29
+
30
+ ## Durable event and invariant
31
+
32
+ The package declaration-merges `todo/write: { todos: TodoItem[] }` into `SessionEventMap`. The event is log-only and carries the complete replacement list; the generated [persistence catalog](../persistence-catalog.md#todowrite--log-only) records its declaration site. The package's invariant companion validates existing and newly announced sessions in one pass, then tracks committed turn boundaries incrementally so every live `todo/write` is checked before append without rescanning the log.
@@ -0,0 +1,32 @@
1
+ # Todo
2
+
3
+ [English](todo.md) | 中文
4
+
5
+ 本页记录 [`@deepseek-ai/dsh-tool-todo`](../../packages/todo/tool-todo/README.zh.md) 拥有的持久 todo 词汇。面向模型的工具会整体替换一个 agent(智能体)会话的列表;该包还拥有事件声明、回放投影和不变量配套插件。工具行为与配置见[包 README](../../packages/todo/tool-todo/README.zh.md)。
6
+
7
+ 源码:[`packages/todo/tool-todo/src/types.ts`](../../packages/todo/tool-todo/src/types.ts)
8
+
9
+ ## `TodoItem`:一条列表项
10
+
11
+ ```ts type-equiv
12
+ /**
13
+ * One entry in an agent's todo list — the unit of the `todo/write`
14
+ * whole-list snapshot declared by this package.
15
+ *
16
+ * Deliberately minimal: a human-readable `content` line and a three-state
17
+ * `status`. No id, priority, or `activeForm` — the list is replaced wholesale
18
+ * on every write (last-write-wins), so entries need no stable identity. The
19
+ * three statuses describe the complete portable lifecycle needed by model and
20
+ * UI consumers.
21
+ */
22
+ interface TodoItem {
23
+ /** What this task is — a short imperative line shown in the UI. */
24
+ content: string
25
+ /** Lifecycle state. `in_progress` marks a task being worked now; parallel work may mark several. */
26
+ status: 'pending' | 'in_progress' | 'completed'
27
+ }
28
+ ```
29
+
30
+ ## 持久事件与不变量
31
+
32
+ 该包通过声明合并把 `todo/write: { todos: TodoItem[] }` 加入 `SessionEventMap`。此事件仅写入日志,并携带完整替换列表;生成的[持久化目录](../persistence-catalog.zh.md#todowrite--log-only)会记录其声明位置。该包的不变量配套插件会单次遍历校验现有会话和新发布的会话,随后增量追踪已提交的轮次边界,使每个实时 `todo/write` 都能在追加前得到校验,而无需重新扫描日志。
@@ -0,0 +1,70 @@
1
+ # Webhook runtime
2
+
3
+ English | [中文](webhook.zh.md)
4
+
5
+ The Webhook subsystem turns authenticated external deliveries into optional ordinary root Sessions. Provider adapters own authentication and generic JSON intake; trusted programmatic rules own conditions and external calls; `ctx.webhookRuntime` owns callback lifetime plus Workspace-backed Session creation. The [implemented decision](../../.agents/notes/implemented/feature/2026-08-22-fire-and-forget-webhook-sessions.md) records why the runtime keeps no delivery or completion state.
6
+
7
+ ## Shared values
8
+
9
+ `WebhookRuleId`, `WebhookSourceId`, and `WebhookDeliveryId` are opaque strings. A delivery id is provenance only: the runtime neither stores nor deduplicates it.
10
+
11
+ `WebhookEventMap` is merge-extensible by provider kind. `WebhookEventOf<K>` selects a known provider event and otherwise admits generic lossless JSON, allowing an out-of-tree adapter without changing the runtime package.
12
+
13
+ `VerifiedWebhookDelivery<K>` contains `kind`, configured `source`, provider `deliveryId`, normalized `event`, and non-negative safe-integer `receivedAt`. The runtime validates, detaches, and freezes the entire value before dispatching it to more than one rule.
14
+
15
+ `WebhookRule<K>` contains a unique id, provider kind, and `run(delivery, signal)`. The callback may execute arbitrary trusted code. It returns `null` or one `WebhookSessionRequest`, and it must observe the signal for asynchronous work that should stop when the registration unloads.
16
+
17
+ `WebhookSessionRequest` requires an absolute `workspacePath`, title, text prompt, agent preset, and permission preset. Optional `model` names an explicit provider/model route plus optional output-token cap and uses that adapter's reasoning default. Omission snapshots the complete current deployment selection, including reasoning effort, until the first request records its durable header.
18
+
19
+ ## Fire-and-forget dispatch
20
+
21
+ `dispatch()` snapshots the matching rules, schedules each independently, and returns before any callback settles. Throws and rejections are contained per rule. Registration disposal removes the rule before aborting and draining its active calls, so no later delivery can enter code that is unloading.
22
+
23
+ The runtime has no queue, retry, deduplication, execution status, crash replay, Agent-status listener, or completion result. Repeated delivery may create repeated Sessions. The only active-operation table is private teardown bookkeeping and disappears with the process.
24
+
25
+ ## Session creation
26
+
27
+ A non-null result is snapshotted before asynchronous preflight. The runtime validates permission and agent presets, resolves or creates the canonical Workspace, creates an Agent whose Session cwd equals the Workspace path, mounts the selected agent preset before publication, and durably attaches the Session before applying permission, title, and the initial follow-up.
28
+
29
+ The follow-up is a normal durable user-role message with `source.kind: "webhook"` and provider/source/delivery/rule provenance. Its accepted inbox insertion commits the webhook operation. The runtime does not specially flush or wait for the turn; ordinary Session persistence and Agent lifecycle apply afterward.
30
+
31
+ Failed attachment disposes the new Agent before a prompt exists. A failure between attachment and prompt admission attempts Workspace detach and Agent disposal without replacing the original error. A Workspace automatically created during preflight remains because another concurrent caller may already use it.
32
+
33
+ ## GitHub adapter
34
+
35
+ `@deepseek-ai/dsh-webhook-github` registers an exact route on an injected WebServer, resolves its credential reference for each request, verifies the untouched `application/json` body before parsing, and returns `202` immediately after in-memory dispatch. Its normalized event guarantees a signed lossless-JSON object; rules validate the event-specific fields they consume.
36
+
37
+ The [GitHub review guide](../user/guide/github-review.md) mounts this route on an isolated second WebServer so exposing webhook ingress does not expose the browser API.
38
+
39
+ <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
40
+
41
+ <a id="cordis-surface"></a>
42
+
43
+ ## Cordis API
44
+
45
+ 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).
46
+
47
+ <a id="ctxwebhookruntime--webhookruntime"></a>
48
+
49
+ ### `ctx.webhookRuntime` — `WebhookRuntime`
50
+
51
+ Fire-and-forget rule runtime. Session creation is the only built-in action.
52
+
53
+ ```ts cordis-catalog
54
+ /**
55
+ * Register one trusted programmatic rule.
56
+ * @param rule - unique id, provider kind, and arbitrary callback.
57
+ * @returns awaitable effect disposer that aborts and drains this rule's active callbacks.
58
+ */
59
+ register<K extends string>(rule: WebhookRule<K>): () => Promise<void>
60
+
61
+ /**
62
+ * Start every currently matching rule and return before any callback settles.
63
+ * @param delivery - authenticated provider data; snapshotted before dispatch.
64
+ * @throws synchronously when the runtime is closing or the delivery is malformed.
65
+ */
66
+ dispatch<K extends string>(delivery: VerifiedWebhookDelivery<K>): void
67
+ ```
68
+
69
+ Source: [`packages/webhook/webhook/src/index.ts`](../../packages/webhook/webhook/src/index.ts)
70
+ <!-- END GENERATED cordis-surface -->
@@ -0,0 +1,70 @@
1
+ # Webhook runtime
2
+
3
+ [English](webhook.md) | 中文
4
+
5
+ Webhook 子系统会把已通过身份验证的外部交付转换为可选的普通根 Session。提供方适配器拥有身份验证与通用 JSON 接收;受信任的程序化规则拥有条件与外部调用;`ctx.webhookRuntime` 拥有回调生命周期以及基于 Workspace 的 Session 创建。[已实现决策](../../.agents/notes/implemented/feature/2026-08-22-fire-and-forget-webhook-sessions.zh.md)记录了 runtime 为何不保留交付或完成状态。
6
+
7
+ ## 共享值
8
+
9
+ `WebhookRuleId`、`WebhookSourceId` 与 `WebhookDeliveryId` 是不透明字符串。交付 id 仅用于来源信息:runtime 既不存储也不对它去重。
10
+
11
+ `WebhookEventMap` 可按提供方种类合并扩展。`WebhookEventOf<K>` 会选择已知提供方事件,否则接纳通用无损 JSON,从而让树外适配器无需修改 runtime 包。
12
+
13
+ `VerifiedWebhookDelivery<K>` 包含 `kind`、已配置 `source`、提供方 `deliveryId`、规范化 `event` 与非负安全整数 `receivedAt`。runtime 会先验证、分离并冻结完整值,再把它分发给多个规则。
14
+
15
+ `WebhookRule<K>` 包含唯一 id、提供方种类与 `run(delivery, signal)`。回调可以执行任意受信任代码。它返回 `null` 或一个 `WebhookSessionRequest`,并且异步工作若应在注册卸载时停止,就必须观察 signal。
16
+
17
+ `WebhookSessionRequest` 要求绝对 `workspacePath`、标题、文本提示词、agent preset 与 permission preset。可选 `model` 会指定明确的提供方/模型路由与可选输出 token 上限,并使用该适配器的默认推理强度。省略时会快照包含推理强度的完整当前部署选择,直到首个请求记录持久 header。
18
+
19
+ ## Fire-and-forget 分发
20
+
21
+ `dispatch()` 会快照匹配规则,彼此独立地调度每个规则,并在任何回调结算前返回。抛出与拒绝按规则分别被包含。注册 disposer 会先移除规则,再中止并排空活动调用,因此后续交付无法进入正在卸载的代码。
22
+
23
+ runtime 没有队列、重试、去重、执行状态、崩溃重放、Agent 状态监听器或完成结果。重复交付可能创建重复 Session。唯一的活动操作表是私有 teardown 记账,并随进程消失。
24
+
25
+ ## Session 创建
26
+
27
+ 非 `null` 结果会在异步预检前生成快照。runtime 会验证 permission 与 agent preset,解析或创建规范 Workspace,创建 Session cwd 等于 Workspace 路径的 Agent,在发布前挂载所选 agent preset,并在应用权限、标题与初始 follow-up 前持久附加 Session。
28
+
29
+ follow-up 是普通持久 user-role 消息,使用 `source.kind: "webhook"`,并携带提供方/来源/交付/规则来源信息。其 inbox 插入被接受时提交 webhook 操作。runtime 不执行特殊 flush,也不等待轮次;之后应用普通 Session persistence 与 Agent 生命周期。
30
+
31
+ 附加失败会在提示词出现前释放新 Agent。附加之后、提示词接纳之前的失败会尝试脱离 Workspace 并释放 Agent,且不会取代原始错误。预检期间自动创建的 Workspace 会保留,因为另一个并发调用者可能已经使用它。
32
+
33
+ ## GitHub 适配器
34
+
35
+ `@deepseek-ai/dsh-webhook-github` 在注入的 WebServer 上注册精确路由,为每次请求解析凭据引用,在解析前验证未改动的 `application/json` body,并在内存分发后立即返回 `202`。它的规范化事件保证为已签名的无损 JSON 对象;规则负责验证自己消费的事件特定字段。
36
+
37
+ [GitHub 评审指南](../user/guide/github-review.zh.md)把该路由挂载在隔离的第二个 WebServer 上,因此暴露 webhook 入口不会暴露浏览器 API。
38
+
39
+ <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
40
+
41
+ <a id="cordis-surface"></a>
42
+
43
+ ## Cordis API
44
+
45
+ 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.zh.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
46
+
47
+ <a id="ctxwebhookruntime--webhookruntime"></a>
48
+
49
+ ### `ctx.webhookRuntime` — `WebhookRuntime`
50
+
51
+ Fire-and-forget rule runtime. Session creation is the only built-in action.
52
+
53
+ ```ts cordis-catalog
54
+ /**
55
+ * Register one trusted programmatic rule.
56
+ * @param rule - unique id, provider kind, and arbitrary callback.
57
+ * @returns awaitable effect disposer that aborts and drains this rule's active callbacks.
58
+ */
59
+ register<K extends string>(rule: WebhookRule<K>): () => Promise<void>
60
+
61
+ /**
62
+ * Start every currently matching rule and return before any callback settles.
63
+ * @param delivery - authenticated provider data; snapshotted before dispatch.
64
+ * @throws synchronously when the runtime is closing or the delivery is malformed.
65
+ */
66
+ dispatch<K extends string>(delivery: VerifiedWebhookDelivery<K>): void
67
+ ```
68
+
69
+ Source: [`packages/webhook/webhook/src/index.ts`](../../packages/webhook/webhook/src/index.ts)
70
+ <!-- END GENERATED cordis-surface -->