dsh-plugin-dev-kb 1.6.0 → 1.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (622) hide show
  1. package/CHANGELOG.md +186 -141
  2. package/README.en.md +172 -155
  3. package/README.md +183 -163
  4. package/kb/INDEX.md +236 -234
  5. package/kb/README.md +88 -83
  6. package/kb/extra/AGENTS.md +77 -77
  7. package/kb/extra/cli-help.md +15 -7
  8. package/kb/extra/cli-help.zh.md +15 -7
  9. package/kb/extra/cookbook/adding-a-plugin-preview.md +18 -25
  10. package/kb/extra/cookbook/adding-a-plugin-preview.zh.md +18 -25
  11. package/kb/extra/cookbook/reviewing-persistence-type-changes.md +1 -1
  12. package/kb/extra/cookbook/reviewing-persistence-type-changes.zh.md +1 -1
  13. package/kb/extra/cookbook/reviewing-ui-design.md +166 -0
  14. package/kb/extra/cookbook/reviewing-ui-design.zh.md +166 -0
  15. package/kb/extra/deepseek-llm-api-wire-extensions.md +162 -162
  16. package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +162 -162
  17. package/kb/extra/defensive-patterns/conversation-and-session.md +2 -2
  18. package/kb/extra/defensive-patterns/conversation-and-session.zh.md +2 -2
  19. package/kb/extra/defensive-patterns/general.md +2 -0
  20. package/kb/extra/defensive-patterns/general.zh.md +2 -0
  21. package/kb/extra/defensive-patterns/package-surface.md +233 -231
  22. package/kb/extra/defensive-patterns/package-surface.zh.md +233 -231
  23. package/kb/extra/defensive-patterns/remote-and-api.md +117 -115
  24. package/kb/extra/defensive-patterns/remote-and-api.zh.md +117 -115
  25. package/kb/extra/development.md +221 -214
  26. package/kb/extra/development.zh.md +228 -221
  27. package/kb/extra/event-producer-consumer.md +133 -110
  28. package/kb/extra/event-producer-consumer.zh.md +135 -112
  29. package/kb/extra/i18n/README.md +2 -0
  30. package/kb/extra/i18n/README.zh.md +2 -0
  31. package/kb/extra/module-graph.md +1961 -1776
  32. package/kb/extra/module-graph.zh.md +1963 -1778
  33. package/kb/extra/persistence-changes/2026-10-13-liveshare-share-viewer-principal.md +60 -0
  34. package/kb/extra/persistence-changes/2026-10-13-liveshare-share-viewer-principal.zh.md +60 -0
  35. package/kb/extra/persistence-changes/2026-10-19-session-team-mode.md +48 -0
  36. package/kb/extra/persistence-changes/2026-10-19-session-team-mode.zh.md +48 -0
  37. package/kb/extra/persistence-changes/2026-10-22-action-card-status.md +60 -0
  38. package/kb/extra/persistence-changes/2026-10-22-action-card-status.zh.md +60 -0
  39. package/kb/extra/persistence-changes/2026-10-25-skill-catalog-incomplete.md +60 -0
  40. package/kb/extra/persistence-changes/2026-10-25-skill-catalog-incomplete.zh.md +60 -0
  41. package/kb/extra/persistence-changes/2026-10-27-retry-control-input.md +60 -0
  42. package/kb/extra/persistence-changes/2026-10-27-retry-control-input.zh.md +60 -0
  43. package/kb/extra/persistence-changes/2026-10-27-subagent-settlement-attribution.md +60 -0
  44. package/kb/extra/persistence-changes/2026-10-27-subagent-settlement-attribution.zh.md +60 -0
  45. package/kb/extra/persistence-changes/2026-11-02-subagent-catalog-names.md +48 -0
  46. package/kb/extra/persistence-changes/2026-11-02-subagent-catalog-names.zh.md +48 -0
  47. package/kb/extra/persistence-changes/2026-11-02-team-task-owner-names.md +48 -0
  48. package/kb/extra/persistence-changes/2026-11-02-team-task-owner-names.zh.md +48 -0
  49. package/kb/extra/persistence-changes/2026-11-05-timeline-priority-notice.md +60 -0
  50. package/kb/extra/persistence-changes/2026-11-05-timeline-priority-notice.zh.md +60 -0
  51. package/kb/extra/persistence-changes/README.md +92 -90
  52. package/kb/extra/persistence-changes/README.zh.md +92 -90
  53. package/kb/extra/persistence-changes/historical-formats/README.md +75 -75
  54. package/kb/extra/persistence-changes/historical-formats/README.zh.md +75 -75
  55. package/kb/extra/persistence-changes/releases/README.md +103 -103
  56. package/kb/extra/persistence-changes/releases/README.zh.md +103 -103
  57. package/kb/extra/rescope.md +55 -53
  58. package/kb/extra/rescope.zh.md +55 -53
  59. package/kb/extra/subsystems/agent-team.md +38 -75
  60. package/kb/extra/subsystems/agent-team.zh.md +38 -75
  61. package/kb/extra/subsystems/attachment.md +36 -13
  62. package/kb/extra/subsystems/attachment.zh.md +36 -13
  63. package/kb/extra/subsystems/boot.md +631 -447
  64. package/kb/extra/subsystems/boot.zh.md +631 -447
  65. package/kb/extra/subsystems/computer-use.md +179 -59
  66. package/kb/extra/subsystems/computer-use.zh.md +179 -59
  67. package/kb/extra/subsystems/deliverables.md +2 -0
  68. package/kb/extra/subsystems/deliverables.zh.md +2 -0
  69. package/kb/extra/subsystems/extensions.md +387 -384
  70. package/kb/extra/subsystems/extensions.zh.md +387 -384
  71. package/kb/extra/subsystems/feedback.md +418 -400
  72. package/kb/extra/subsystems/feedback.zh.md +418 -400
  73. package/kb/extra/subsystems/forge.md +2169 -0
  74. package/kb/extra/subsystems/forge.zh.md +2171 -0
  75. package/kb/extra/subsystems/liveshare.md +167 -0
  76. package/kb/extra/subsystems/liveshare.zh.md +167 -0
  77. package/kb/extra/subsystems/mcp.md +237 -139
  78. package/kb/extra/subsystems/mcp.zh.md +237 -139
  79. package/kb/extra/subsystems/office-kit.md +122 -0
  80. package/kb/extra/subsystems/office-kit.zh.md +122 -0
  81. package/kb/extra/subsystems/working-directory.md +95 -64
  82. package/kb/extra/subsystems/working-directory.zh.md +95 -64
  83. package/kb/extra/subsystems/worktrees.md +187 -76
  84. package/kb/extra/subsystems/worktrees.zh.md +187 -76
  85. package/kb/extra/testing.md +1 -1
  86. package/kb/extra/testing.zh.md +1 -1
  87. package/kb/extra/ui-design-review-cases.md +445 -0
  88. package/kb/extra/ui-design-review-cases.zh.md +445 -0
  89. package/kb/extra/ui-radius.md +3 -1
  90. package/kb/extra/ui-radius.zh.md +3 -1
  91. package/kb/extra/upgrade-guide/v0.2.1-alpha.1/python-ptc-sandbox/guide.md +21 -21
  92. package/kb/extra/upgrade-guide/v0.2.1-alpha.1/python-ptc-sandbox/guide.zh.md +21 -21
  93. package/kb/extra/upgrade-guide/v0.2.1-alpha.1/team-direct-inbox/guide.md +1 -1
  94. package/kb/extra/upgrade-guide/v0.2.1-alpha.1/team-direct-inbox/guide.zh.md +1 -1
  95. package/kb/extra/upgrade-guide/v0.2.1-alpha.2/cordis-timer-wrappers/guide.md +24 -0
  96. package/kb/extra/upgrade-guide/v0.2.1-alpha.2/cordis-timer-wrappers/guide.zh.md +24 -0
  97. package/kb/extra/upgrade-guide/v0.2.1-alpha.2/plugin-manager-view-fields/guide.md +25 -0
  98. package/kb/extra/upgrade-guide/v0.2.1-alpha.2/plugin-manager-view-fields/guide.zh.md +25 -0
  99. package/kb/extra/upgrade-guide/v0.2.1-alpha.4/model-switch-compaction/guide.md +30 -0
  100. package/kb/extra/upgrade-guide/v0.2.1-alpha.4/model-switch-compaction/guide.zh.md +30 -0
  101. package/kb/extra/upgrade-guide/v0.2.1-alpha.4/session-model-selection/guide.md +28 -0
  102. package/kb/extra/upgrade-guide/v0.2.1-alpha.4/session-model-selection/guide.zh.md +28 -0
  103. package/kb/extra/upgrade-guide/v0.2.1-rc.1/agent-instructions-chain-budgets/guide.md +24 -0
  104. package/kb/extra/upgrade-guide/v0.2.1-rc.1/agent-instructions-chain-budgets/guide.zh.md +24 -0
  105. package/kb/extra/upgrade-guide/v0.2.1-rc.1/subagent-control-tools/guide.md +23 -0
  106. package/kb/extra/upgrade-guide/v0.2.1-rc.1/subagent-control-tools/guide.zh.md +23 -0
  107. package/kb/extra/upgrade-guide/v0.2.1-rc.2/agent-runtime-owner/guide.md +20 -0
  108. package/kb/extra/upgrade-guide/v0.2.1-rc.2/agent-runtime-owner/guide.zh.md +20 -0
  109. package/kb/extra/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.md +31 -0
  110. package/kb/extra/upgrade-guide/v0.2.1-rc.2/agent-team-subagents/guide.zh.md +31 -0
  111. package/kb/extra/upgrade-guide/v0.2.1-rc.2/approval-projection-dependency/guide.md +34 -0
  112. package/kb/extra/upgrade-guide/v0.2.1-rc.2/approval-projection-dependency/guide.zh.md +34 -0
  113. package/kb/extra/upgrade-guide/v0.2.1-rc.2/auto-review-shutdown/guide.md +20 -0
  114. package/kb/extra/upgrade-guide/v0.2.1-rc.2/auto-review-shutdown/guide.zh.md +20 -0
  115. package/kb/extra/upgrade-guide/v0.2.1-rc.2/auto-review-user-sources/guide.md +18 -0
  116. package/kb/extra/upgrade-guide/v0.2.1-rc.2/auto-review-user-sources/guide.zh.md +18 -0
  117. package/kb/extra/upgrade-guide/v0.2.1-rc.2/built-in-japanese/guide.md +18 -0
  118. package/kb/extra/upgrade-guide/v0.2.1-rc.2/built-in-japanese/guide.zh.md +18 -0
  119. package/kb/extra/upgrade-guide/v0.2.1-rc.2/conditional-tools/guide.md +25 -0
  120. package/kb/extra/upgrade-guide/v0.2.1-rc.2/conditional-tools/guide.zh.md +25 -0
  121. package/kb/extra/upgrade-guide/v0.2.1-rc.2/context-menu-services/guide.md +23 -0
  122. package/kb/extra/upgrade-guide/v0.2.1-rc.2/context-menu-services/guide.zh.md +23 -0
  123. package/kb/extra/upgrade-guide/v0.2.1-rc.2/cua-consent-storage/guide.md +22 -0
  124. package/kb/extra/upgrade-guide/v0.2.1-rc.2/cua-consent-storage/guide.zh.md +22 -0
  125. package/kb/extra/upgrade-guide/v0.2.1-rc.2/cua-preset-scope/guide.md +18 -0
  126. package/kb/extra/upgrade-guide/v0.2.1-rc.2/cua-preset-scope/guide.zh.md +18 -0
  127. package/kb/extra/upgrade-guide/v0.2.1-rc.2/cua-relative-coordinates/guide.md +17 -0
  128. package/kb/extra/upgrade-guide/v0.2.1-rc.2/cua-relative-coordinates/guide.zh.md +17 -0
  129. package/kb/extra/upgrade-guide/v0.2.1-rc.2/file-drag-callbacks/guide.md +19 -0
  130. package/kb/extra/upgrade-guide/v0.2.1-rc.2/file-drag-callbacks/guide.zh.md +19 -0
  131. package/kb/extra/upgrade-guide/v0.2.1-rc.2/headless-policy-composition/guide.md +27 -0
  132. package/kb/extra/upgrade-guide/v0.2.1-rc.2/headless-policy-composition/guide.zh.md +27 -0
  133. package/kb/extra/upgrade-guide/v0.2.1-rc.2/model-badge-package-removal/guide.md +18 -0
  134. package/kb/extra/upgrade-guide/v0.2.1-rc.2/model-badge-package-removal/guide.zh.md +18 -0
  135. package/kb/extra/upgrade-guide/v0.2.1-rc.2/new-session-blank-reuse/guide.md +22 -0
  136. package/kb/extra/upgrade-guide/v0.2.1-rc.2/new-session-blank-reuse/guide.zh.md +22 -0
  137. package/kb/extra/upgrade-guide/v0.2.1-rc.2/node-24/guide.md +17 -0
  138. package/kb/extra/upgrade-guide/v0.2.1-rc.2/node-24/guide.zh.md +17 -0
  139. package/kb/extra/upgrade-guide/v0.2.1-rc.2/official-bundle-lists/guide.md +18 -0
  140. package/kb/extra/upgrade-guide/v0.2.1-rc.2/official-bundle-lists/guide.zh.md +18 -0
  141. package/kb/extra/upgrade-guide/v0.2.1-rc.2/persistent-terminal-controls/guide.md +18 -0
  142. package/kb/extra/upgrade-guide/v0.2.1-rc.2/persistent-terminal-controls/guide.zh.md +18 -0
  143. package/kb/extra/upgrade-guide/v0.2.1-rc.2/pi-ai-default-capacities/guide.md +18 -0
  144. package/kb/extra/upgrade-guide/v0.2.1-rc.2/pi-ai-default-capacities/guide.zh.md +18 -0
  145. package/kb/extra/upgrade-guide/v0.2.1-rc.2/pi-ai-model-catalog/guide.md +53 -0
  146. package/kb/extra/upgrade-guide/v0.2.1-rc.2/pi-ai-model-catalog/guide.zh.md +53 -0
  147. package/kb/extra/upgrade-guide/v0.2.1-rc.2/pi-ai-protocol-replay/guide.md +30 -0
  148. package/kb/extra/upgrade-guide/v0.2.1-rc.2/pi-ai-protocol-replay/guide.zh.md +30 -0
  149. package/kb/extra/upgrade-guide/v0.2.1-rc.2/ptc-script-format/guide.md +20 -0
  150. package/kb/extra/upgrade-guide/v0.2.1-rc.2/ptc-script-format/guide.zh.md +20 -0
  151. package/kb/extra/upgrade-guide/v0.2.1-rc.2/sdk-desktop-context/guide.md +18 -0
  152. package/kb/extra/upgrade-guide/v0.2.1-rc.2/sdk-desktop-context/guide.zh.md +18 -0
  153. package/kb/extra/upgrade-guide/v0.2.1-rc.2/sdk-minimal-checkpoints/guide.md +18 -0
  154. package/kb/extra/upgrade-guide/v0.2.1-rc.2/sdk-minimal-checkpoints/guide.zh.md +18 -0
  155. package/kb/extra/upgrade-guide/v0.2.1-rc.2/session-projection-handles/guide.md +24 -0
  156. package/kb/extra/upgrade-guide/v0.2.1-rc.2/session-projection-handles/guide.zh.md +24 -0
  157. package/kb/extra/upgrade-guide/v0.2.1-rc.2/session-store-writes/guide.md +25 -0
  158. package/kb/extra/upgrade-guide/v0.2.1-rc.2/session-store-writes/guide.zh.md +25 -0
  159. package/kb/extra/upgrade-guide/v0.2.1-rc.2/session-team-mode/guide.md +22 -0
  160. package/kb/extra/upgrade-guide/v0.2.1-rc.2/session-team-mode/guide.zh.md +22 -0
  161. package/kb/extra/upgrade-guide/v0.2.1-rc.2/subagent-activation-options/guide.md +18 -0
  162. package/kb/extra/upgrade-guide/v0.2.1-rc.2/subagent-activation-options/guide.zh.md +18 -0
  163. package/kb/extra/upgrade-guide/v0.2.1-rc.2/subagent-service-methods/guide.md +19 -0
  164. package/kb/extra/upgrade-guide/v0.2.1-rc.2/subagent-service-methods/guide.zh.md +19 -0
  165. package/kb/extra/upgrade-guide/v0.2.1-rc.2/subagent-user-controls/guide.md +26 -0
  166. package/kb/extra/upgrade-guide/v0.2.1-rc.2/subagent-user-controls/guide.zh.md +26 -0
  167. package/kb/extra/upgrade-guide/v0.2.1-rc.2/tool-executor-plugins/guide.md +35 -0
  168. package/kb/extra/upgrade-guide/v0.2.1-rc.2/tool-executor-plugins/guide.zh.md +35 -0
  169. package/kb/extra/upgrade-guide/v0.2.1-rc.2/web-fetch-private-destinations/guide.md +21 -0
  170. package/kb/extra/upgrade-guide/v0.2.1-rc.2/web-fetch-private-destinations/guide.zh.md +21 -0
  171. package/kb/extra/upgrade-guide/v0.2.1-rc.2/webserver-index-peer/guide.md +17 -0
  172. package/kb/extra/upgrade-guide/v0.2.1-rc.2/webserver-index-peer/guide.zh.md +17 -0
  173. package/kb/extra/upgrade-guide/v0.2.1-rc.3/attachment-read-access/guide.md +23 -0
  174. package/kb/extra/upgrade-guide/v0.2.1-rc.3/attachment-read-access/guide.zh.md +23 -0
  175. package/kb/extra/upgrade-guide/v0.2.1-rc.3/computer-use-helper-permissions/guide.md +21 -0
  176. package/kb/extra/upgrade-guide/v0.2.1-rc.3/computer-use-helper-permissions/guide.zh.md +21 -0
  177. package/kb/extra/upgrade-guide/v0.2.1-rc.3/cua-bundle-id-consent/guide.md +20 -0
  178. package/kb/extra/upgrade-guide/v0.2.1-rc.3/cua-bundle-id-consent/guide.zh.md +20 -0
  179. package/kb/extra/upgrade-guide/v0.2.1-rc.3/generated-worktree-names/guide.md +20 -0
  180. package/kb/extra/upgrade-guide/v0.2.1-rc.3/generated-worktree-names/guide.zh.md +20 -0
  181. package/kb/extra/upgrade-guide/v0.2.1-rc.3/inbox-only-agent-wait/guide.md +19 -0
  182. package/kb/extra/upgrade-guide/v0.2.1-rc.3/inbox-only-agent-wait/guide.zh.md +19 -0
  183. package/kb/extra/upgrade-guide/v0.2.1-rc.3/independent-session-forks/guide.md +23 -0
  184. package/kb/extra/upgrade-guide/v0.2.1-rc.3/independent-session-forks/guide.zh.md +23 -0
  185. package/kb/extra/upgrade-guide/v0.2.1-rc.3/ptc-caller-cancellation/guide.md +20 -0
  186. package/kb/extra/upgrade-guide/v0.2.1-rc.3/ptc-caller-cancellation/guide.zh.md +20 -0
  187. package/kb/extra/upgrade-guide/v0.2.1-rc.3/session-follow-activation/guide.md +21 -0
  188. package/kb/extra/upgrade-guide/v0.2.1-rc.3/session-follow-activation/guide.zh.md +21 -0
  189. package/kb/extra/upgrade-guide/v0.2.1-rc.3/subagent-runtime-ownership/guide.md +21 -0
  190. package/kb/extra/upgrade-guide/v0.2.1-rc.3/subagent-runtime-ownership/guide.zh.md +21 -0
  191. package/kb/extra/upgrade-guide/v0.2.1-rc.3/team-mode-menu/guide.md +18 -0
  192. package/kb/extra/upgrade-guide/v0.2.1-rc.3/team-mode-menu/guide.zh.md +18 -0
  193. package/kb/extra/upgrade-guide/v0.2.1-rc.3/team-projection/guide.md +20 -0
  194. package/kb/extra/upgrade-guide/v0.2.1-rc.3/team-projection/guide.zh.md +20 -0
  195. package/kb/extra/upgrade-guide/v0.2.1-rc.3/web-file-downloads/guide.md +19 -0
  196. package/kb/extra/upgrade-guide/v0.2.1-rc.3/web-file-downloads/guide.zh.md +19 -0
  197. package/kb/extra/upgrade-guide/v0.2.1-rc.3/web-search-defaults/guide.md +18 -0
  198. package/kb/extra/upgrade-guide/v0.2.1-rc.3/web-search-defaults/guide.zh.md +18 -0
  199. package/kb/extra/upgrade-guide/v0.2.1-rc.4/model-listing-refresh/guide.md +22 -0
  200. package/kb/extra/upgrade-guide/v0.2.1-rc.4/model-listing-refresh/guide.zh.md +22 -0
  201. package/kb/extra/upgrade-guide/v0.2.1-rc.4/reasoning-body-props/guide.md +20 -0
  202. package/kb/extra/upgrade-guide/v0.2.1-rc.4/reasoning-body-props/guide.zh.md +20 -0
  203. package/kb/extra/upgrade-guide/v0.2.1-rc.4/sandbox-unsupported-policy-error/guide.md +18 -0
  204. package/kb/extra/upgrade-guide/v0.2.1-rc.4/sandbox-unsupported-policy-error/guide.zh.md +18 -0
  205. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/context-menu-target/guide.md +19 -0
  206. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/context-menu-target/guide.zh.md +19 -0
  207. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/cordis-loader-entry-trees/guide.md +24 -0
  208. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/cordis-loader-entry-trees/guide.zh.md +24 -0
  209. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/css-anchored-menus/guide.md +21 -0
  210. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/css-anchored-menus/guide.zh.md +21 -0
  211. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/cua-source-dev-helper/guide.md +19 -0
  212. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/cua-source-dev-helper/guide.zh.md +19 -0
  213. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/desktop-stable-channel/guide.md +18 -0
  214. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/desktop-stable-channel/guide.zh.md +18 -0
  215. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/forge-git-permissions/guide.md +21 -0
  216. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/forge-git-permissions/guide.zh.md +21 -0
  217. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/forge-worktree-entry/guide.md +29 -0
  218. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/forge-worktree-entry/guide.zh.md +29 -0
  219. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/fs-observation-policy-default-off/guide.md +29 -0
  220. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/fs-observation-policy-default-off/guide.zh.md +29 -0
  221. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/llm-error-builtin-codes/guide.md +18 -0
  222. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/llm-error-builtin-codes/guide.zh.md +18 -0
  223. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/llm-request-purpose-declarations/guide.md +29 -0
  224. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/llm-request-purpose-declarations/guide.zh.md +29 -0
  225. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/pi-ai-request-image-count/guide.md +18 -0
  226. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/pi-ai-request-image-count/guide.zh.md +18 -0
  227. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/projection-event-frames/guide.md +32 -0
  228. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/projection-event-frames/guide.zh.md +32 -0
  229. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/projection-released-checkpoint/guide.md +30 -0
  230. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/projection-released-checkpoint/guide.zh.md +30 -0
  231. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/projection-snapshot-scope/guide.md +19 -0
  232. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/projection-snapshot-scope/guide.zh.md +19 -0
  233. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/retained-ptc-timeout/guide.md +21 -0
  234. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/retained-ptc-timeout/guide.zh.md +21 -0
  235. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/schedule-agent-tools/guide.md +37 -0
  236. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/schedule-agent-tools/guide.zh.md +37 -0
  237. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/schedule-remote-host-tasks/guide.md +22 -0
  238. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/schedule-remote-host-tasks/guide.zh.md +22 -0
  239. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/sdk-run-commands/guide.md +24 -0
  240. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/sdk-run-commands/guide.zh.md +24 -0
  241. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/session-header-menu/guide.md +20 -0
  242. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/session-header-menu/guide.zh.md +20 -0
  243. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/subagent-explicit-reasoning/guide.md +20 -0
  244. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/subagent-explicit-reasoning/guide.zh.md +20 -0
  245. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/web-search-credential-modes/guide.md +17 -0
  246. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/web-search-credential-modes/guide.zh.md +17 -0
  247. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/web-search-default-provider/guide.md +19 -0
  248. package/kb/extra/upgrade-guide/v0.2.2-alpha.2/web-search-default-provider/guide.zh.md +19 -0
  249. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/chat-process-activity-context/guide.md +18 -0
  250. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/chat-process-activity-context/guide.zh.md +18 -0
  251. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/office-kit-package/guide.md +34 -0
  252. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/office-kit-package/guide.zh.md +34 -0
  253. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/office-operation-tools/guide.md +49 -0
  254. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/office-operation-tools/guide.zh.md +49 -0
  255. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/office-skill-resources/guide.md +36 -0
  256. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/office-skill-resources/guide.zh.md +36 -0
  257. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/subagent-names/guide.md +29 -0
  258. package/kb/extra/upgrade-guide/v0.2.2-alpha.3/subagent-names/guide.zh.md +29 -0
  259. package/kb/extra/upgrade-guide/v0.2.2-rc.1/account-notification-fields/guide.md +26 -0
  260. package/kb/extra/upgrade-guide/v0.2.2-rc.1/account-notification-fields/guide.zh.md +26 -0
  261. package/kb/extra/upgrade-guide/v0.2.2-rc.1/account-remote-provider/guide.md +22 -0
  262. package/kb/extra/upgrade-guide/v0.2.2-rc.1/account-remote-provider/guide.zh.md +22 -0
  263. package/kb/extra/upgrade-guide/v0.2.2-rc.1/archive-worktrees/guide.md +21 -0
  264. package/kb/extra/upgrade-guide/v0.2.2-rc.1/archive-worktrees/guide.zh.md +21 -0
  265. package/kb/extra/upgrade-guide/v0.2.2-rc.1/attachment-error-codes/guide.md +23 -0
  266. package/kb/extra/upgrade-guide/v0.2.2-rc.1/attachment-error-codes/guide.zh.md +23 -0
  267. package/kb/extra/upgrade-guide/v0.2.2-rc.1/cua-ax-read-limits/guide.md +20 -0
  268. package/kb/extra/upgrade-guide/v0.2.2-rc.1/cua-ax-read-limits/guide.zh.md +20 -0
  269. package/kb/extra/upgrade-guide/v0.2.2-rc.1/cua-hover-ax-only/guide.md +18 -0
  270. package/kb/extra/upgrade-guide/v0.2.2-rc.1/cua-hover-ax-only/guide.zh.md +18 -0
  271. package/kb/extra/upgrade-guide/v0.2.2-rc.1/cua-native-platform/guide.md +23 -0
  272. package/kb/extra/upgrade-guide/v0.2.2-rc.1/cua-native-platform/guide.zh.md +23 -0
  273. package/kb/extra/upgrade-guide/v0.2.2-rc.1/dsh-doc-skill/guide.md +35 -0
  274. package/kb/extra/upgrade-guide/v0.2.2-rc.1/dsh-doc-skill/guide.zh.md +35 -0
  275. package/kb/extra/upgrade-guide/v0.2.2-rc.1/forge-change-request-overview/guide.md +26 -0
  276. package/kb/extra/upgrade-guide/v0.2.2-rc.1/forge-change-request-overview/guide.zh.md +26 -0
  277. package/kb/extra/upgrade-guide/v0.2.2-rc.1/forge-pinned-diff/guide.md +26 -0
  278. package/kb/extra/upgrade-guide/v0.2.2-rc.1/forge-pinned-diff/guide.zh.md +26 -0
  279. package/kb/extra/upgrade-guide/v0.2.2-rc.1/forge-review-reads/guide.md +19 -0
  280. package/kb/extra/upgrade-guide/v0.2.2-rc.1/forge-review-reads/guide.zh.md +19 -0
  281. package/kb/extra/upgrade-guide/v0.2.2-rc.1/package-export-cleanup/guide.md +26 -0
  282. package/kb/extra/upgrade-guide/v0.2.2-rc.1/package-export-cleanup/guide.zh.md +26 -0
  283. package/kb/extra/upgrade-guide/v0.2.2-rc.1/plugin-update-method/guide.md +22 -0
  284. package/kb/extra/upgrade-guide/v0.2.2-rc.1/plugin-update-method/guide.zh.md +22 -0
  285. package/kb/extra/upgrade-guide/v0.2.2-rc.1/ptc-bootstrap-protocol/guide.md +25 -0
  286. package/kb/extra/upgrade-guide/v0.2.2-rc.1/ptc-bootstrap-protocol/guide.zh.md +25 -0
  287. package/kb/extra/upgrade-guide/v0.2.2-rc.1/ptc-inspect-output/guide.md +48 -0
  288. package/kb/extra/upgrade-guide/v0.2.2-rc.1/ptc-inspect-output/guide.zh.md +48 -0
  289. package/kb/extra/upgrade-guide/v0.2.2-rc.1/ptc-scoped-repl-default/guide.md +45 -0
  290. package/kb/extra/upgrade-guide/v0.2.2-rc.1/ptc-scoped-repl-default/guide.zh.md +45 -0
  291. package/kb/extra/upgrade-guide/v0.2.2-rc.1/runtime-context-reminders/guide.md +19 -0
  292. package/kb/extra/upgrade-guide/v0.2.2-rc.1/runtime-context-reminders/guide.zh.md +19 -0
  293. package/kb/extra/upgrade-guide/v0.2.2-rc.1/session-append-overloads/guide.md +17 -0
  294. package/kb/extra/upgrade-guide/v0.2.2-rc.1/session-append-overloads/guide.zh.md +17 -0
  295. package/kb/extra/upgrade-guide/v0.2.2-rc.1/subagent-interrupt-default/guide.md +27 -0
  296. package/kb/extra/upgrade-guide/v0.2.2-rc.1/subagent-interrupt-default/guide.zh.md +27 -0
  297. package/kb/extra/upgrade-guide/v0.2.2-rc.1/terminal-backend-cleanup-error/guide.md +20 -0
  298. package/kb/extra/upgrade-guide/v0.2.2-rc.1/terminal-backend-cleanup-error/guide.zh.md +20 -0
  299. package/kb/extra/upgrade-guide/v0.2.2-rc.1/terminal-bundle-sidebar-backend/guide.md +19 -0
  300. package/kb/extra/upgrade-guide/v0.2.2-rc.1/terminal-bundle-sidebar-backend/guide.zh.md +19 -0
  301. package/kb/extra/upgrade-guide/v0.2.2-rc.1/web-fetch-transport-error-codes/guide.md +18 -0
  302. package/kb/extra/upgrade-guide/v0.2.2-rc.1/web-fetch-transport-error-codes/guide.zh.md +18 -0
  303. package/kb/extra/upgrade-guide/v0.2.2-rc.1/web-search-billing/guide.md +41 -0
  304. package/kb/extra/upgrade-guide/v0.2.2-rc.1/web-search-billing/guide.zh.md +41 -0
  305. package/kb/extra/upgrade-guide/v0.2.2-rc.1/window-drag-regions/guide.md +21 -0
  306. package/kb/extra/upgrade-guide/v0.2.2-rc.1/window-drag-regions/guide.zh.md +21 -0
  307. package/kb/extra/upgrade-guide/v0.2.2-rc.2/client-module-metadata/guide.md +29 -0
  308. package/kb/extra/upgrade-guide/v0.2.2-rc.2/client-module-metadata/guide.zh.md +29 -0
  309. package/kb/extra/upgrade-guide/v0.2.2-rc.2/connection-page-peer/guide.md +25 -0
  310. package/kb/extra/upgrade-guide/v0.2.2-rc.2/connection-page-peer/guide.zh.md +25 -0
  311. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cordis-root-and-intercept-metadata/guide.md +31 -0
  312. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cordis-root-and-intercept-metadata/guide.zh.md +31 -0
  313. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cua-agents-without-preset/guide.md +24 -0
  314. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cua-agents-without-preset/guide.zh.md +24 -0
  315. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cua-macos-provider/guide.md +24 -0
  316. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cua-macos-provider/guide.zh.md +24 -0
  317. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cua-partial-observations/guide.md +19 -0
  318. package/kb/extra/upgrade-guide/v0.2.2-rc.2/cua-partial-observations/guide.zh.md +19 -0
  319. package/kb/extra/upgrade-guide/v0.2.2-rc.2/deepseek-files-timeout/guide.md +15 -0
  320. package/kb/extra/upgrade-guide/v0.2.2-rc.2/deepseek-files-timeout/guide.zh.md +15 -0
  321. package/kb/extra/upgrade-guide/v0.2.2-rc.2/experimental-tool-error-codes/guide.md +28 -0
  322. package/kb/extra/upgrade-guide/v0.2.2-rc.2/experimental-tool-error-codes/guide.zh.md +28 -0
  323. package/kb/extra/upgrade-guide/v0.2.2-rc.2/package-export-cleanup/guide.md +36 -0
  324. package/kb/extra/upgrade-guide/v0.2.2-rc.2/package-export-cleanup/guide.zh.md +36 -0
  325. package/kb/extra/upgrade-guide/v0.2.2-rc.2/python-ptc-removal/guide.md +22 -0
  326. package/kb/extra/upgrade-guide/v0.2.2-rc.2/python-ptc-removal/guide.zh.md +22 -0
  327. package/kb/extra/upgrade-guide/v0.2.2-rc.2/session-status-package/guide.md +31 -0
  328. package/kb/extra/upgrade-guide/v0.2.2-rc.2/session-status-package/guide.zh.md +31 -0
  329. package/kb/extra/upgrade-guide/v0.2.2-rc.2/shared-search-filter/guide.md +18 -0
  330. package/kb/extra/upgrade-guide/v0.2.2-rc.2/shared-search-filter/guide.zh.md +18 -0
  331. package/kb/extra/upgrade-guide/v0.2.2-rc.2/shell-fs-tool-error-codes/guide.md +30 -0
  332. package/kb/extra/upgrade-guide/v0.2.2-rc.2/shell-fs-tool-error-codes/guide.zh.md +30 -0
  333. package/kb/extra/web-styling.md +57 -39
  334. package/kb/extra/web-styling.zh.md +57 -39
  335. package/kb/meta/search-index.json +4508 -3806
  336. package/kb/meta/source.json +18 -14
  337. package/kb/site/develop/cordis-tutorial/04-events.md +1 -1
  338. package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +5 -4
  339. package/kb/site/develop/framework/events.md +1 -1
  340. package/kb/site/develop/practice/dynamic-cordis.md +1 -1
  341. package/kb/site/develop/practice/llm-adapter.md +192 -192
  342. package/kb/site/en/develop/cordis-tutorial/04-events.md +1 -1
  343. package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +5 -4
  344. package/kb/site/en/develop/framework/events.md +1 -1
  345. package/kb/site/en/develop/practice/dynamic-cordis.md +1 -1
  346. package/kb/site/en/develop/practice/llm-adapter.md +192 -192
  347. package/kb/site/en/guide/desktop-uninstall-cleanup.md +63 -0
  348. package/kb/site/en/guide/network-proxy.md +1 -1
  349. package/kb/site/en/guide/plugin-market.md +153 -151
  350. package/kb/site/en/guide/providers.md +234 -230
  351. package/kb/site/en/guide/python-sdk.md +17 -2
  352. package/kb/site/en/guide/quickstart.md +35 -34
  353. package/kb/site/en/guide/schedule.md +45 -26
  354. package/kb/site/en/reference/agent-lifecycle.md +3 -1
  355. package/kb/site/en/reference/api-gateway.md +4 -4
  356. package/kb/site/en/reference/capability-seams.md +789 -719
  357. package/kb/site/en/reference/config-catalog.md +5432 -4806
  358. package/kb/site/en/reference/cookbook/adding-a-package.md +175 -173
  359. package/kb/site/en/reference/cookbook/adding-a-tool.md +105 -104
  360. package/kb/site/en/reference/cookbook/extension-cookbook.md +178 -135
  361. package/kb/site/en/reference/cordis-api/context.md +373 -369
  362. package/kb/site/en/reference/cordis-api/events.md +9 -9
  363. package/kb/site/en/reference/cordis-api/fiber.md +457 -380
  364. package/kb/site/en/reference/cordis-api/inherited.md +44 -43
  365. package/kb/site/en/reference/cordis-api/service.md +118 -107
  366. package/kb/site/en/reference/experimental-persistence-catalog.md +36 -20
  367. package/kb/site/en/reference/index.md +174 -168
  368. package/kb/site/en/reference/persistence-catalog.md +9453 -9146
  369. package/kb/site/en/reference/subsystems/client-modules.md +19 -11
  370. package/kb/site/en/reference/subsystems/compaction.md +288 -288
  371. package/kb/site/en/reference/subsystems/conversation.md +2 -0
  372. package/kb/site/en/reference/subsystems/core.md +1172 -1117
  373. package/kb/site/en/reference/subsystems/credentials.md +526 -497
  374. package/kb/site/en/reference/subsystems/filesystem.md +708 -534
  375. package/kb/site/en/reference/subsystems/index.md +82 -80
  376. package/kb/site/en/reference/subsystems/llm-streaming.md +1290 -1200
  377. package/kb/site/en/reference/subsystems/lsp.md +230 -206
  378. package/kb/site/en/reference/subsystems/persistence.md +451 -422
  379. package/kb/site/en/reference/subsystems/plan.md +91 -91
  380. package/kb/site/en/reference/subsystems/ptc-runtime.md +295 -254
  381. package/kb/site/en/reference/subsystems/sandbox.md +250 -225
  382. package/kb/site/en/reference/subsystems/schedule.md +194 -430
  383. package/kb/site/en/reference/subsystems/session-projection.md +186 -44
  384. package/kb/site/en/reference/subsystems/session-query.md +525 -515
  385. package/kb/site/en/reference/subsystems/session-reference.md +231 -228
  386. package/kb/site/en/reference/subsystems/session.md +1478 -1343
  387. package/kb/site/en/reference/subsystems/sidebar-right.md +165 -164
  388. package/kb/site/en/reference/subsystems/skills.md +356 -356
  389. package/kb/site/en/reference/subsystems/slots.md +235 -204
  390. package/kb/site/en/reference/subsystems/storage.md +26 -9
  391. package/kb/site/en/reference/subsystems/subagent.md +119 -102
  392. package/kb/site/en/reference/subsystems/system-prompt.md +13 -11
  393. package/kb/site/en/reference/subsystems/terminal.md +188 -188
  394. package/kb/site/en/reference/subsystems/token-meter.md +45 -0
  395. package/kb/site/en/reference/subsystems/tools.md +902 -751
  396. package/kb/site/en/reference/subsystems/typert.md +21 -11
  397. package/kb/site/en/reference/subsystems/user-questions.md +330 -311
  398. package/kb/site/en/reference/subsystems/web-client.md +161 -104
  399. package/kb/site/en/reference/subsystems/web-server.md +434 -306
  400. package/kb/site/en/reference/subsystems/web.md +268 -210
  401. package/kb/site/en/reference/subsystems/workflow.md +287 -282
  402. package/kb/site/en/reference/subsystems/workspace.md +743 -651
  403. package/kb/site/en/reference/tool-catalog.md +2867 -2829
  404. package/kb/site/en/reference/tool-execution-pipeline.md +5 -4
  405. package/kb/site/guide/desktop-uninstall-cleanup.md +63 -0
  406. package/kb/site/guide/network-proxy.md +1 -1
  407. package/kb/site/guide/plugin-market.md +153 -151
  408. package/kb/site/guide/providers.md +234 -230
  409. package/kb/site/guide/python-sdk.md +17 -2
  410. package/kb/site/guide/quickstart.md +35 -34
  411. package/kb/site/guide/schedule.md +45 -26
  412. package/kb/site/reference/agent-lifecycle.md +3 -1
  413. package/kb/site/reference/api-gateway.md +4 -4
  414. package/kb/site/reference/capability-seams.md +789 -719
  415. package/kb/site/reference/config-catalog.md +5432 -4806
  416. package/kb/site/reference/cookbook/adding-a-package.md +175 -173
  417. package/kb/site/reference/cookbook/adding-a-tool.md +107 -106
  418. package/kb/site/reference/cookbook/extension-cookbook.md +182 -139
  419. package/kb/site/reference/cordis-api/context.md +373 -369
  420. package/kb/site/reference/cordis-api/events.md +9 -9
  421. package/kb/site/reference/cordis-api/fiber.md +459 -380
  422. package/kb/site/reference/cordis-api/inherited.md +44 -43
  423. package/kb/site/reference/cordis-api/service.md +118 -107
  424. package/kb/site/reference/experimental-persistence-catalog.md +36 -20
  425. package/kb/site/reference/index.md +178 -172
  426. package/kb/site/reference/persistence-catalog.md +9453 -9146
  427. package/kb/site/reference/subsystems/client-modules.md +19 -11
  428. package/kb/site/reference/subsystems/compaction.md +288 -288
  429. package/kb/site/reference/subsystems/conversation.md +4 -0
  430. package/kb/site/reference/subsystems/core.md +1184 -1129
  431. package/kb/site/reference/subsystems/credentials.md +526 -497
  432. package/kb/site/reference/subsystems/filesystem.md +708 -534
  433. package/kb/site/reference/subsystems/index.md +82 -80
  434. package/kb/site/reference/subsystems/llm-streaming.md +1296 -1206
  435. package/kb/site/reference/subsystems/lsp.md +230 -206
  436. package/kb/site/reference/subsystems/persistence.md +451 -422
  437. package/kb/site/reference/subsystems/plan.md +91 -91
  438. package/kb/site/reference/subsystems/ptc-runtime.md +295 -254
  439. package/kb/site/reference/subsystems/sandbox.md +250 -225
  440. package/kb/site/reference/subsystems/schedule.md +197 -433
  441. package/kb/site/reference/subsystems/session-projection.md +186 -44
  442. package/kb/site/reference/subsystems/session-query.md +525 -515
  443. package/kb/site/reference/subsystems/session-reference.md +231 -228
  444. package/kb/site/reference/subsystems/session.md +1482 -1347
  445. package/kb/site/reference/subsystems/sidebar-right.md +165 -164
  446. package/kb/site/reference/subsystems/skills.md +356 -356
  447. package/kb/site/reference/subsystems/slots.md +239 -208
  448. package/kb/site/reference/subsystems/storage.md +26 -9
  449. package/kb/site/reference/subsystems/subagent.md +119 -102
  450. package/kb/site/reference/subsystems/system-prompt.md +13 -11
  451. package/kb/site/reference/subsystems/terminal.md +188 -188
  452. package/kb/site/reference/subsystems/token-meter.md +45 -0
  453. package/kb/site/reference/subsystems/tools.md +902 -751
  454. package/kb/site/reference/subsystems/typert.md +21 -11
  455. package/kb/site/reference/subsystems/user-questions.md +330 -311
  456. package/kb/site/reference/subsystems/web-client.md +161 -104
  457. package/kb/site/reference/subsystems/web-server.md +434 -306
  458. package/kb/site/reference/subsystems/web.md +268 -210
  459. package/kb/site/reference/subsystems/workflow.md +287 -282
  460. package/kb/site/reference/subsystems/workspace.md +743 -651
  461. package/kb/site/reference/tool-catalog.md +2872 -2835
  462. package/kb/site/reference/tool-execution-pipeline.md +4 -3
  463. package/lib/index.js +359 -244
  464. package/lib/kb-version.js +118 -0
  465. package/package.json +68 -66
  466. package/scripts/refresh-kb.mjs +153 -0
  467. package/scripts/selfcheck.mjs +18 -0
  468. package/scripts/smoke.mjs +77 -0
  469. package/skills/dsh-plugin-dev-kb.md +71 -69
  470. package/skills/dsh-plugin-starter.md +65 -0
  471. package/skills/dsh-upgrade-assistant.md +62 -0
  472. package/kb/extra/agent-lifecycle.md +0 -91
  473. package/kb/extra/agent-lifecycle.zh.md +0 -93
  474. package/kb/extra/api-gateway.md +0 -172
  475. package/kb/extra/api-gateway.zh.md +0 -178
  476. package/kb/extra/architecture.md +0 -165
  477. package/kb/extra/architecture.zh.md +0 -169
  478. package/kb/extra/capability-seams.md +0 -714
  479. package/kb/extra/capability-seams.zh.md +0 -716
  480. package/kb/extra/config-catalog.md +0 -4801
  481. package/kb/extra/config-catalog.zh.md +0 -4803
  482. package/kb/extra/cookbook/adding-a-package.md +0 -170
  483. package/kb/extra/cookbook/adding-a-package.zh.md +0 -170
  484. package/kb/extra/cookbook/adding-a-settings-card.md +0 -72
  485. package/kb/extra/cookbook/adding-a-settings-card.zh.md +0 -72
  486. package/kb/extra/cookbook/adding-a-tool.md +0 -101
  487. package/kb/extra/cookbook/adding-a-tool.zh.md +0 -103
  488. package/kb/extra/cookbook/adding-an-llm-adapter.md +0 -43
  489. package/kb/extra/cookbook/adding-an-llm-adapter.zh.md +0 -43
  490. package/kb/extra/cookbook/extension-cookbook.md +0 -132
  491. package/kb/extra/cookbook/extension-cookbook.zh.md +0 -136
  492. package/kb/extra/cordis-api/context.md +0 -364
  493. package/kb/extra/cordis-api/context.zh.md +0 -366
  494. package/kb/extra/cordis-api/events.md +0 -207
  495. package/kb/extra/cordis-api/events.zh.md +0 -209
  496. package/kb/extra/cordis-api/fiber.md +0 -375
  497. package/kb/extra/cordis-api/fiber.zh.md +0 -377
  498. package/kb/extra/cordis-api/inherited.md +0 -38
  499. package/kb/extra/cordis-api/registry.md +0 -152
  500. package/kb/extra/cordis-api/registry.zh.md +0 -154
  501. package/kb/extra/cordis-api/service.md +0 -102
  502. package/kb/extra/cordis-api/service.zh.md +0 -104
  503. package/kb/extra/cordis-tutorial/01-first-plugin.md +0 -95
  504. package/kb/extra/cordis-tutorial/01-first-plugin.zh.md +0 -95
  505. package/kb/extra/cordis-tutorial/02-lifecycle-and-effects.md +0 -98
  506. package/kb/extra/cordis-tutorial/02-lifecycle-and-effects.zh.md +0 -98
  507. package/kb/extra/cordis-tutorial/03-services.md +0 -98
  508. package/kb/extra/cordis-tutorial/03-services.zh.md +0 -98
  509. package/kb/extra/cordis-tutorial/04-events.md +0 -144
  510. package/kb/extra/cordis-tutorial/04-events.zh.md +0 -144
  511. package/kb/extra/cordis-tutorial/05-config.md +0 -111
  512. package/kb/extra/cordis-tutorial/05-config.zh.md +0 -111
  513. package/kb/extra/cordis-tutorial/06-composition-and-hmr.md +0 -113
  514. package/kb/extra/cordis-tutorial/06-composition-and-hmr.zh.md +0 -113
  515. package/kb/extra/cordis-tutorial/07-into-the-harness.md +0 -108
  516. package/kb/extra/cordis-tutorial/07-into-the-harness.zh.md +0 -108
  517. package/kb/extra/cordis-tutorial/index.md +0 -60
  518. package/kb/extra/cordis-tutorial/index.zh.md +0 -62
  519. package/kb/extra/experimental-persistence-catalog.md +0 -17
  520. package/kb/extra/experimental-persistence-catalog.zh.md +0 -17
  521. package/kb/extra/persistence-catalog.md +0 -9140
  522. package/kb/extra/persistence-catalog.zh.md +0 -9142
  523. package/kb/extra/subsystems/README.md +0 -76
  524. package/kb/extra/subsystems/README.zh.md +0 -76
  525. package/kb/extra/subsystems/approval.md +0 -170
  526. package/kb/extra/subsystems/approval.zh.md +0 -170
  527. package/kb/extra/subsystems/client-modules.md +0 -198
  528. package/kb/extra/subsystems/client-modules.zh.md +0 -198
  529. package/kb/extra/subsystems/client-resources.md +0 -92
  530. package/kb/extra/subsystems/client-resources.zh.md +0 -92
  531. package/kb/extra/subsystems/commands.md +0 -223
  532. package/kb/extra/subsystems/commands.zh.md +0 -223
  533. package/kb/extra/subsystems/compaction.md +0 -284
  534. package/kb/extra/subsystems/compaction.zh.md +0 -284
  535. package/kb/extra/subsystems/conversation.md +0 -301
  536. package/kb/extra/subsystems/conversation.zh.md +0 -303
  537. package/kb/extra/subsystems/core.md +0 -1113
  538. package/kb/extra/subsystems/core.zh.md +0 -1125
  539. package/kb/extra/subsystems/credentials.md +0 -493
  540. package/kb/extra/subsystems/credentials.zh.md +0 -493
  541. package/kb/extra/subsystems/filesystem.md +0 -530
  542. package/kb/extra/subsystems/filesystem.zh.md +0 -530
  543. package/kb/extra/subsystems/goal.md +0 -313
  544. package/kb/extra/subsystems/goal.zh.md +0 -313
  545. package/kb/extra/subsystems/jobs.md +0 -554
  546. package/kb/extra/subsystems/jobs.zh.md +0 -554
  547. package/kb/extra/subsystems/llm-streaming.md +0 -1196
  548. package/kb/extra/subsystems/llm-streaming.zh.md +0 -1202
  549. package/kb/extra/subsystems/lsp.md +0 -202
  550. package/kb/extra/subsystems/lsp.zh.md +0 -202
  551. package/kb/extra/subsystems/office-to-pdf.md +0 -87
  552. package/kb/extra/subsystems/office-to-pdf.zh.md +0 -87
  553. package/kb/extra/subsystems/permission-presets.md +0 -167
  554. package/kb/extra/subsystems/permission-presets.zh.md +0 -167
  555. package/kb/extra/subsystems/persistence.md +0 -418
  556. package/kb/extra/subsystems/persistence.zh.md +0 -418
  557. package/kb/extra/subsystems/plan.md +0 -87
  558. package/kb/extra/subsystems/plan.zh.md +0 -87
  559. package/kb/extra/subsystems/ptc-runtime.md +0 -250
  560. package/kb/extra/subsystems/ptc-runtime.zh.md +0 -250
  561. package/kb/extra/subsystems/sandbox.md +0 -221
  562. package/kb/extra/subsystems/sandbox.zh.md +0 -221
  563. package/kb/extra/subsystems/schedule.md +0 -550
  564. package/kb/extra/subsystems/schedule.zh.md +0 -550
  565. package/kb/extra/subsystems/scope.md +0 -59
  566. package/kb/extra/subsystems/scope.zh.md +0 -61
  567. package/kb/extra/subsystems/session-projection.md +0 -356
  568. package/kb/extra/subsystems/session-projection.zh.md +0 -356
  569. package/kb/extra/subsystems/session-query.md +0 -511
  570. package/kb/extra/subsystems/session-query.zh.md +0 -511
  571. package/kb/extra/subsystems/session-reference.md +0 -224
  572. package/kb/extra/subsystems/session-reference.zh.md +0 -224
  573. package/kb/extra/subsystems/session-telemetry.md +0 -212
  574. package/kb/extra/subsystems/session-telemetry.zh.md +0 -212
  575. package/kb/extra/subsystems/session-title.md +0 -213
  576. package/kb/extra/subsystems/session-title.zh.md +0 -213
  577. package/kb/extra/subsystems/session.md +0 -1339
  578. package/kb/extra/subsystems/session.zh.md +0 -1343
  579. package/kb/extra/subsystems/settings.md +0 -155
  580. package/kb/extra/subsystems/settings.zh.md +0 -155
  581. package/kb/extra/subsystems/shell.md +0 -314
  582. package/kb/extra/subsystems/shell.zh.md +0 -314
  583. package/kb/extra/subsystems/sidebar-right.md +0 -160
  584. package/kb/extra/subsystems/sidebar-right.zh.md +0 -160
  585. package/kb/extra/subsystems/skills.md +0 -352
  586. package/kb/extra/subsystems/skills.zh.md +0 -352
  587. package/kb/extra/subsystems/slots.md +0 -200
  588. package/kb/extra/subsystems/slots.zh.md +0 -204
  589. package/kb/extra/subsystems/spill.md +0 -124
  590. package/kb/extra/subsystems/spill.zh.md +0 -124
  591. package/kb/extra/subsystems/storage.md +0 -259
  592. package/kb/extra/subsystems/storage.zh.md +0 -259
  593. package/kb/extra/subsystems/subagent.md +0 -686
  594. package/kb/extra/subsystems/subagent.zh.md +0 -688
  595. package/kb/extra/subsystems/subprocess.md +0 -321
  596. package/kb/extra/subsystems/subprocess.zh.md +0 -321
  597. package/kb/extra/subsystems/system-prompt.md +0 -254
  598. package/kb/extra/subsystems/system-prompt.zh.md +0 -254
  599. package/kb/extra/subsystems/terminal.md +0 -184
  600. package/kb/extra/subsystems/terminal.zh.md +0 -184
  601. package/kb/extra/subsystems/token-meter.md +0 -106
  602. package/kb/extra/subsystems/token-meter.zh.md +0 -106
  603. package/kb/extra/subsystems/tools.md +0 -747
  604. package/kb/extra/subsystems/tools.zh.md +0 -747
  605. package/kb/extra/subsystems/typert.md +0 -690
  606. package/kb/extra/subsystems/typert.zh.md +0 -690
  607. package/kb/extra/subsystems/user-questions.md +0 -307
  608. package/kb/extra/subsystems/user-questions.zh.md +0 -307
  609. package/kb/extra/subsystems/web-client.md +0 -100
  610. package/kb/extra/subsystems/web-client.zh.md +0 -100
  611. package/kb/extra/subsystems/web-server.md +0 -302
  612. package/kb/extra/subsystems/web-server.zh.md +0 -302
  613. package/kb/extra/subsystems/web.md +0 -206
  614. package/kb/extra/subsystems/web.zh.md +0 -206
  615. package/kb/extra/subsystems/workflow.md +0 -278
  616. package/kb/extra/subsystems/workflow.zh.md +0 -278
  617. package/kb/extra/subsystems/workspace.md +0 -647
  618. package/kb/extra/subsystems/workspace.zh.md +0 -647
  619. package/kb/extra/tool-catalog.md +0 -2824
  620. package/kb/extra/tool-catalog.zh.md +0 -2832
  621. package/kb/extra/tool-execution-pipeline.md +0 -65
  622. package/kb/extra/tool-execution-pipeline.zh.md +0 -67
@@ -1,1347 +1,1482 @@
1
- ---
2
- editSource: "docs/subsystems/session.zh.md"
3
- rawMarkdownPath: "reference/subsystems/session.md"
4
- outline: [2,3]
5
- ---
6
-
7
- # 会话
8
-
9
- [dsh-session](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/core/session) 的内存事件溯源模型。`Session` 是一份由类型化 `SessionEvent` 组成的**仅追加日志**,是 agent(智能体)完整交互历史的唯一真源。LLM(大语言模型)消息历史从日志*派生*而来,从不单独存储;回放即从同一组事件重新派生。日志如何实现**持久化**(持久化 seam、后端、崩溃恢复)是兄弟文档 [persistence.md](./persistence.md) 的关注点。
10
-
11
- 源码:[`packages/core/session/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
12
-
13
- ## `SessionEventMap`:事件词汇
14
-
15
- 仅追加的事件类型。可通过声明合并扩展:插件通过 declaration merging 声明额外的事件类型。例如[压缩(compaction) seam](./compaction.md) 添加了 `compaction/start` / `compaction/summary` / `compaction/end`,`@deepseek-ai/dsh-hook-protocol` 为钩子桥接添加了仅记录日志的 `hook/invoked` / `hook/result` 记录。与 `compaction/*` 一样,这些都不是 `SurfaceEventType`(没有 `surfaceOp`)。生成的[持久化日志事件目录](../persistence-catalog.md)列举了所有成员(核心与合并扩展的),包含其 payload、surface 标记与声明位置。
16
-
17
- ```ts type-equiv
18
- /** A user-role specialization of the shared message representation. */
19
- interface UserMessage extends MessageBase {
20
- readonly role: 'user'
21
- }
22
- ```
23
-
24
- ```ts type-equiv
25
- /**
26
- * The merge-extensible, append-only source of truth for an agent interaction.
27
- * Message history is derived from this log. Every event is lossless JSON and
28
- * sequence numbers stay contiguous. Assistant attempt events embed their exact
29
- * compact raw streams so persistence stores one durable settlement per attempt.
30
- */
31
- interface SessionEventMap {
32
- /**
33
- * Opens turn `turn` before the loop claims queued input or runs pre-step.
34
- * Rejection, empty input, cancellation, or failure may close it with no
35
- * step; otherwise the following identified `user/message` event or batch
36
- * records the messages entering the step.
37
- */
38
- 'turn/start': { turn: number }
39
- /**
40
- * Closes turn `turn` with the {@link TurnEndReason} that ended it. A turn
41
- * with no entered step has no `step/start` or `step/end`. The loop does not await a
42
- * flush at turn boundaries: `dsh-session-checkpoint-policy` owns the
43
- * per-request durability checkpoint, and consumers that read storage after
44
- * `whenIdle()` flush themselves. Success commits the turn; rejection is
45
- * reported live and does not prevent later work.
46
- */
47
- 'turn/end': { turn: number; reason: TurnEndReason }
48
- /** Opens step `step` of turn `turn` — one model call plus the tool executions it requested. */
49
- 'step/start': { turn: number; step: number }
50
- /** Closes step `step` of turn `turn`. */
51
- 'step/end': { turn: number; step: number }
52
- /**
53
- * A user-role message on the model-visible surface: a direct human prompt
54
- * (the queued message claimed for this turn), a synthetic `agent.inject()`
55
- * context (file-change notices, subdir AGENTS.md, skill content, cron
56
- * notifications, …), or an entered goal continuation round. All three
57
- * project their `content` verbatim; `source` tells them apart.
58
- */
59
- 'user/message': UserMessage
60
- /** An incremental agent session change admitted at the named turn and step. */
61
- 'developer/message': {
62
- turn: number
63
- step: number
64
- message: DeveloperMessage
65
- /** Earlier request/header defining every tool addition; required exactly when additions are present. */
66
- headerSeq?: SessionSeq
67
- }
68
- /**
69
- * The rendered system prompt on the model-visible surface. The loop appends
70
- * the first one as surface node 0 before the step's first `user/message`.
71
- * A prepared in-history route can append nonempty changes in a continuing
72
- * series. An incapable route or new series normalizes text to the first system
73
- * node. Normalization empties nonempty later nodes, then rewrites the head if
74
- * needed, through logged per-node replacements. An empty rendering always
75
- * clears all active system nodes, leaving no older instructions model-visible.
76
- * Empty later nodes are dormant and project to no message; an empty head with
77
- * no active later node records "no system prompt". Restored nonempty text follows
78
- * the same route and series rule; empty nodes never restore older text.
79
- */
80
- 'system/message': { turn: number; step: number; message: SystemMessage }
81
- /**
82
- * Assembled assistant message for one step (derived history uses this).
83
- * Carries the step's `usage` when the adapter reported token accounting, so
84
- * the model output and its accounting travel together (there is no separate
85
- * usage record). `usage` is absent when the adapter reported none. A turn
86
- * cancelled mid-stream finalizes its delivered text/reasoning prefix as this
87
- * event with `interrupted: true`; undispatched tool calls are absent. The
88
- * marker distinguishes that prefix without re-deriving interruption from turn
89
- * boundaries. An aborted turn with no such event streamed no visible content.
90
- */
91
- 'assistant/message': {
92
- turn: number
93
- step: number
94
- message: AssistantMessage
95
- /** Exact timed model stream, compacted without joining delta boundaries. */
96
- stream: AssistantStreamRecord[]
97
- usage?: TokenUsage
98
- interrupted?: true
99
- }
100
- /**
101
- * One model attempt that committed no surface message. The embedded stream
102
- * preserves a failed, retried, cancelled, or stream-error attempt that
103
- * reached settlement without fabricating model-visible history.
104
- */
105
- 'assistant/attempt': { turn: number; step: number; stream: AssistantStreamRecord[] }
106
- /**
107
- * The model requested one tool invocation: `name` with the raw `arguments`
108
- * JSON string exactly as the model produced it (unparsed). `callId` pairs the
109
- * call with its `tool/result`.
110
- */
111
- 'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string }
112
- /**
113
- * A completed tool call's model-facing result, optional internal failure
114
- * identity and user-facing reason, and optional tool-private `meta`
115
- * presentation payload. The reason remains outside the model-facing message.
116
- * `meta` is
117
- * opaque to the core (the producing tool owns its shape and reads it back in
118
- * `presentResult`) but MUST be JSON-serializable: `Session.append`
119
- * runtime-validates all event data with `isJsonValue`, so a non-serializable
120
- * `meta` is rejected at the source, and the durable log reproduces the
121
- * identical card on replay. Absent
122
- * unless the tool attaches one (e.g. `dsh-tool-fs` carries its result-time
123
- * contextual diff here).
124
- */
125
- 'tool/result': {
126
- turn: number
127
- step: number
128
- message: ToolResultMessage
129
- /**
130
- * Optional failure identity and raw user-facing reason, outside model content;
131
- * allowed only when the message has `isError: true`.
132
- */
133
- error?: { name: string; code: string; reason?: string }
134
- meta?: JsonValue
135
- }
136
- /**
137
- * Full header for the next request, appended inside its step before dispatch.
138
- * It is log-only; the latest snapshot reconstructs the request header.
139
- */
140
- 'request/header': {
141
- header: EpochHeader
142
- reason: RequestHeaderReason
143
- /** This request begins a distinct model-message series, independently of the header reason. */
144
- startsSeries?: true
145
- }
146
- /**
147
- * Route metadata for the next request, logged only when the route, capacity,
148
- * or system prompt update mode changes. It does not participate in request
149
- * reconstruction or header equality. Prompt admission uses the bound prepared
150
- * call's capability, not this snapshot from an earlier request.
151
- */
152
- 'request/context': RequestContext
153
- /**
154
- * Separates inherited or restored history from later lifecycle-owned work.
155
- * This log-only marker need not be at {@link Session.firstLiveSeq}: a fork
156
- * seed can already contain its tagged marker and child-owned synthetic
157
- * closers before construction.
158
- *
159
- * A fresh fork child owns one `{ inherited: true }` marker at its exact
160
- * inherited-prefix cut, even when that prefix ends in an ancestor marker.
161
- * `buildForkSeed` appends that marker before any synthetic closers; the
162
- * `Session` constructor supplies it when given only the inherited prefix.
163
- * The last tagged marker is the current Session's cut; untagged markers
164
- * keep ordinary restore and replay lifecycle boundaries.
165
- *
166
- * Only the `Session` constructor and `buildForkSeed` may create this marker.
167
- * Session append does not reject other writers, so a plugin appending one
168
- * would silently classify every live bracket before it as seed history.
169
- *
170
- * An owner of a standalone open/close bracket (`compaction/start` …
171
- * `compaction/end`) reads it because seed history and live work are otherwise
172
- * byte-identical: an unmatched opening marker before this event belongs to
173
- * an ended lifecycle, whatever ended it. NOT a liveness signal about other
174
- * writers — a concurrently live session holds its own boundary elsewhere,
175
- * so tolerating concurrent writers needs a signal beyond the log.
176
- */
177
- 'session/end-seed': { inherited?: true }
178
- }
179
- ```
180
-
181
- `UserMessage` 是普通提示词、注入上下文、steering(中途引导)与实时收件箱事件共享的带标识且冻结的 user-role 值。事件包装层只会增加事件本地的位置或结果事实;条目待处理期间,loop 只额外附加驱动器自有的路由状态。
182
-
183
- <a id="the-request-header-event-requestheader"></a>
184
-
185
- ### 请求头事件:`request/header`
186
-
187
- 请求信封(即 `EpochHeader`:调用配置 + 适配器所提供默认值的标记 + 已组装的工具 schema)会作为会话状态写入日志,因此每个对话请求都是日志的纯函数(见可重建性 Agent Note)。渲染后的系统提示词不属于请求头:它是派生历史,即 surface 第 0 号节点上的 `system/message` 事件以及任何后续的历史内系统节点([决策](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-02-system-prompt-as-surface-node.zh.md)),因此提示词变更替换或追加一个系统节点,而请求头保持不变。带有 reason `'initial'` 或 `'resume'` 的完整 `request/header` 快照记录每个 agent loop 实例的边界;请求变化时会追加 reason 为 `'change'` 的快照;未变的信封显式开启消息序列或跟随 surface 替换时,会追加 reason 为 `'series'` 的快照。如果 `'initial'`、`'resume'` 或 `'change'` 快照所属请求同时开启序列,它会携带 `startsSeries: true`;reason `'series'` 已记录这一事实。普通的仅追加后续 Turn,以及同一模型消息序列内的后续 Step 与重试沿用最新快照。`foldRequestHeader(events)` 通过选择最新快照重建请求头。该事件不是 `SurfaceEventType`,不产生 LLM 消息。
188
-
189
- ```ts type-equiv
190
- /**
191
- * Logged request state outside derived history: call config and tools. The
192
- * system prompt is derived history — surface node 0, a `system/message` event.
193
- * The latest full `request/header` snapshot reconstructs the header; canonical
194
- * empty optional fields are absent.
195
- */
196
- interface EpochHeader {
197
- /** The conversation's call configuration (provider, model, reasoning effort, and sampling scalars). */
198
- config: LlmCallConfig
199
- /** Effective config fields materialized from the exact adapter rather than proposed by a caller. */
200
- adapterDefaults?: LlmCallConfigAdapterDefaults
201
- /** Assembled tool schemas; absent for a tool-less request. */
202
- tools?: ToolSchema[]
203
- /** Retired request text; system prompts belong to system/message events.
204
- * @persistenceReserved
205
- */
206
- system?: never
207
- }
208
- ```
209
-
210
- 当前事件接纳要求 `request/header.header` 为规范形式:禁止任何 `system` 字段,必须省略 `tools: []` 与 `adapterDefaults: {}`。仅含空白的系统消息内容、`config.stop: []` 与嵌套扩展保持不变。seed、append 与当前持久化读取拒绝非规范 header,而不会静默规范化;[V3 信封决策](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-06-v3-canonical-session-envelopes.zh.md)负责历史转换。包含旧版 `request/header-delta` 事件或完整快照原因为 `fallback` 的旧版 v0 日志,会被拒绝,而不会以不完整方式回放。
211
-
212
- ### 路由容量事件:`request/context`
213
-
214
- 请求所解析到的路由的上下文元数据是独立的已记录状态,在同一步骤内紧随 `request/header` 追加,且仅在提供方、模型、容量或 `systemPromptUpdate` 模式与上一条记录不同时追加。它保持在 `EpochHeader` 之外,因为该类型是 `headerEquals` 逐字段比较的重建约定。容量与更新模式描述的是路由,不是请求输入,把它们折叠进去会让一次路由变化被登记为请求信封的 `change`,也会把适配器元数据拉进 loop 的请求重建。与 `request/header` 一样,它不是 `SurfaceEventType`,也不产生 LLM 消息。`session.requestContext()` 以增量方式归并最新一条记录;agent loop 在决定变化后的系统提示词是替换最新的系统节点还是追加到已缓存历史之后时,读取该记录的 `systemPromptUpdate`([决策规则](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent-loop/README.zh.md#understand-the-implementation))。适配器不公布容量的路由会以缺失 `contextWindow` 的形式记录,因此新记录可以清除较早路由的容量;未声明更新模式的路由同样会清除较早路由的 `systemPromptUpdate`。
215
-
216
- ```ts type-equiv
217
- /** Registration-bound metadata for one resolved model route. */
218
- interface RequestContext {
219
- /** Registered provider route the metadata belongs to. */
220
- provider: string
221
- /** Provider-owned model id the metadata belongs to. */
222
- model: string
223
- /** Maximum combined request and response context in tokens, when advertised. */
224
- contextWindow?: number
225
- /** `'in-history'` when the route reads the latest `system` message at any position as the effective system prompt. */
226
- systemPromptUpdate?: SystemPromptUpdate
227
- }
228
- ```
229
-
230
- ## `SessionEvent<T>`:一条日志条目
231
-
232
- 基于 `type` 的真正可辨识联合(而非独立的 `type`/`data` 联合),因此 `switch (event.type)` 能直接收窄 `event.data`,无需类型断言。`seq` 是日志中的单调递增位置(`seq = log.length`);`time` 为 epoch 毫秒。
233
-
234
- ```ts type-equiv
235
- /** Sequence number of one existing event in a Session log. */
236
- type SessionSeq = BrandedNumber<'SessionSeq'>
237
- ```
238
-
239
- ```ts type-equiv
240
- /** A Session log gap, prefix length, or read offset, which may equal the event count. */
241
- type SessionLogOffset = BrandedNumber<'SessionLogOffset'>
242
- ```
243
-
244
- ```ts type-equiv
245
- /** Inclusive Session event watermark, or `-1` before any event exists. */
246
- type SessionSeqCursor = SessionSeq | -1
247
- ```
248
-
249
- ```ts type-equiv
250
- /** One existing Session event position, or explicit absence. */
251
- type OptionalSessionSeq = SessionSeq | null
252
- ```
253
-
254
- `SessionSeq(value)` 与 `SessionLogOffset(value)` 只接纳非负安全整数,并拒绝负零。它们仅添加编译期品牌,不改变序列化后的数值;算术会返回普通 `number`,调用方必须按结果的预期角色通过对应构造器重新接纳。
255
-
256
- ```ts type-equiv
257
- /**
258
- * One immutable entry in the session log.
259
- *
260
- * A proper discriminated union over `type` (not independent `type`/`data`
261
- * unions), so `switch (event.type)` narrows `event.data` without casts.
262
- *
263
- * The {@link sourceEventSeqs} and {@link surfaceOp} fields are conditional:
264
- * they only exist on {@link SurfaceEventType} variants (`system/message`, `user/message`,
265
- * `assistant/message`, `tool/result`).
266
- * Non-surface events (boundary markers, attempts, errors) never carry
267
- * surface metadata — the compiler enforces this at `Session.append()`
268
- * call sites.
269
- */
270
- type SessionEvent<T extends SessionEventType = SessionEventType> = {
271
- [K in SessionEventType]: {
272
- type: K
273
- /** Monotonic sequence number within the session. */
274
- seq: SessionSeq
275
- /** Unix epoch milliseconds. */
276
- time: number
277
- data: SessionEventMap[K]
278
- /**
279
- * Marks an event a reader may safely skip when it does not recognize
280
- * `type`. Absent means required: a reader meeting an unrecognized type
281
- * without this marker MUST refuse to reconstruct the session instead of
282
- * silently dropping the event, because an unrecognized required event may
283
- * change how the rest of the log is interpreted. A writer sets `true` only
284
- * on purely informational records whose loss cannot affect reconstruction;
285
- * defaulting to required means a forgotten marker over-refuses (an
286
- * inconvenience) rather than silently resuming a gutted session.
287
- */
288
- ignorable?: true
289
- } & (K extends SurfaceEventType ? SurfaceIntent<K> : {
290
- surfaceOp?: never
291
- sourceEventSeqs?: never
292
- })
293
- }[T]
294
- ```
295
-
296
- `SessionEventType = keyof SessionEventMap`。由于 `SessionEventMap` 可通过合并扩展,对 `SessionEvent` 的 switch 语句禁止使用 `assertNever`:插件添加的变体是合法的未知值;处理已知 case 后在 `default` 中放行。
297
-
298
- 每个 surface 事件都要求 `surfaceOp`;已知仅日志事件禁止两个 surface 元数据字段。原生未知或已退役的可忽略信封保持不透明。`assistant/message` 嵌入其提供方 stream,并禁止 `sourceEventSeqs`。System、user 与 tool surface 事件可以在来源归属或替换覆盖需要时引用完整、非空且唯一的较早事件集合。`tool/result` 仅在消息带有 `isError: true` 时可以携带 `data.error`;失败结果的失败身份仍可省略。
299
-
300
- <a id="surface-types"></a>
301
-
302
- ## Surface 类型
303
-
304
- 产生消息的类型(`SurfaceEventType`:`system/message`、`developer/message`、`user/message`、`assistant/message`、`tool/result`)携带 surface 元数据,用来声明它们如何加入有序的派生 surface。`system/message` 承载渲染后的系统提示词:循环把第一条追加为 surface 第 0 号节点,并在提示词变化时恰好替换最新的系统节点,或在历史内路由上追加一条新的;surface 折叠拒绝任何其他覆盖第 0 号节点 `system/message` 的替换,而后续系统节点是普通历史,压缩替换可以遮蔽它。见 [session surface Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-06-18-session-surface.zh.md)。
305
-
306
- ### `SurfaceEventType`:事件类型中产生消息的子集
307
-
308
- ```ts type-equiv
309
- /**
310
- * The subset of {@link SessionEventType} values whose events produce LLM
311
- * messages and are eligible to appear on the ordered surface. Only these
312
- * event types may carry {@link SurfaceOp}; system, user, and tool events may also cite
313
- * earlier sources through {@link SessionEvent.sourceEventSeqs}.
314
- */
315
- type SurfaceEventType =
316
- | 'system/message'
317
- | 'developer/message'
318
- | 'user/message'
319
- | 'assistant/message'
320
- | 'tool/result'
321
- ```
322
-
323
- ### `SurfaceOp`:事件如何进入 surface
324
-
325
- ```ts type-equiv
326
- /**
327
- * How a session event entered the ordered surface. Only valid on
328
- * {@link SurfaceEventType} events.
329
- *
330
- * - `'append'`: added to the tail — normal path for user/assistant/tool
331
- * messages.
332
- * - `{ op: 'replace', startSeq, endSeq }`: replaces surface nodes from `startSeq`
333
- * (inclusive) through `endSeq` (inclusive) with this node. Both must exist as
334
- * surface nodes in the current surface. `startSeq === endSeq` replaces a single
335
- * node. The node's {@link SessionEvent.sourceEventSeqs} must include every
336
- * shadowed surface node. Used by compaction; any surface-replacing producer
337
- * may use it.
338
- */
339
- type SurfaceOp =
340
- | 'append'
341
- | { op: 'replace'; startSeq: SessionSeq; endSeq: SessionSeq }
342
- ```
343
-
344
- `'append'` 是常规的尾部追加路径。`replace` 恰好包含 `op`、`startSeq` 和 `endSeq`,不接受别名或额外键。它遮蔽这两个当前 surface 事件序号之间的闭区间,并在原位置插入新事件;相同端点仅替换一个条目。端点必须早于替换事件,但它们的相对顺序按 surface 顺序而非数值序号顺序确定。
345
-
346
- ### `SurfaceIntent`:`session.append()` 的参数
347
-
348
- ```ts type-equiv
349
- /**
350
- * Surface placement and cited source-event seqs for {@link Session.append}. Required on
351
- * message-producing events and forbidden on log-only events.
352
- */
353
- type SurfaceIntent<T extends SurfaceEventType = SurfaceEventType> = {
354
- surfaceOp: SurfaceOp
355
- } & (T extends 'assistant/message' ? {
356
- /** Assistant messages embed their provider stream instead of citing source events. */
357
- sourceEventSeqs?: never
358
- } : {
359
- /** Complete non-empty set of known earlier source-event seqs. */
360
- sourceEventSeqs?: SessionSeq[]
361
- })
362
- ```
363
-
364
- 对 `SurfaceEventType` 事件必填:每个产生消息的事件都必须声明它如何加入 surface(派生模型历史的唯一来源)。面向人类的 transcript(文本记录)是另一个投影,读取的是日志中追加来源的事件,因为 surface 会有意遮蔽替换所概括的范围(见 [dsh-session](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/README.zh.md) 的 `isAppendSurfaceEvent`)。非 surface 类型在编译期拒绝此参数。
365
-
366
- `assistant/message` 不能携带 `sourceEventSeqs`;它的 `stream` 拥有精确 provider 证据。其他 surface event 不引用较早 event 时省略该字段,需要引用时使用完整非空 list。
367
-
368
- <a id="plugin-owned-message-projections"></a>
369
- ### 插件拥有的消息投影
370
-
371
- 修改内容的插件用 `@messageProjection` 标记事件声明,并通过 `ctx.sessions.registerMessageProjection()` 注册纯处理器。Session 在提交前通过处理器校验完整决策,应用其不可变消息更新,并推进 `contentGeneration`。缺少处理器时拒绝操作,包括恢复和独立折叠,卸载已经使用的处理器后也会拒绝读取缓存。独立读取器将处理器显式传给 `foldSurface(events, projections)`,并通过 `deriveEventMessage()` 应用 `projectedMessages`。当前格式目录为离线读取器装配第一方处理器。[图片省略插件](./compaction.md#image-offload)拥有图片专用的事件及解释逻辑。
372
-
373
- ```ts type-equiv
374
- /** Readonly history immediately before a message-projection event. */
375
- interface SessionMessageProjectionContext {
376
- /** Current message-producing event sequences in model-visible order. */
377
- nodes: readonly SessionSeq[]
378
- /** Contiguous event window; entries at or beyond the candidate seq are not committed inputs. */
379
- events: readonly SessionEvent[]
380
- /** Absolute sequence of the window's first event. */
381
- baseSeq: SessionLogOffset
382
- /** Previously projected messages keyed by their original event sequences. */
383
- messages: ReadonlyMap<SessionSeq, Message>
384
- }
385
- ```
386
-
387
- ```ts type-equiv
388
- /** Pure interpretation of one plugin-owned event that changes existing message content. */
389
- interface SessionMessageProjection<T extends SessionEventType = SessionEventType> {
390
- /** Event interpreted by this definition; declare it with `@messageProjection` in SessionEventMap. */
391
- type: T
392
- /**
393
- * Validate the complete durable decision before returning any updates. Preserve
394
- * message identities and publish immutable copies without mutating the input.
395
- * @param event - candidate event, not yet applied to the supplied history.
396
- * @param context - history preceding this decision.
397
- * @returns changed current messages keyed by their original sequences.
398
- * @throws when the durable decision cannot be applied to this history.
399
- */
400
- project(event: SessionEvent<T>, context: SessionMessageProjectionContext): ReadonlyMap<SessionSeq, Message>
401
- }
402
- ```
403
-
404
- ### `SessionSurface`:实时只读 surface 投影
405
-
406
- `Session.surface` 返回会话稳定的 `SessionSurface` 视图。同一个增量管理器在提交前校验追加候选事件,并根据已提交事件推进该投影;调用方可以观察成员关系和替换代次,但不能调用校验。
407
-
408
- `SurfaceManager(log, baseSeq?, projections?)` 也可以折叠一个连续的已加载窗口,其第一个事件的绝对序号为 `baseSeq`。每个事件在该绝对序号空间中仍保持连续;如果替换跨过窗口头部,由于其声明的范围并不存在,该替换会失败。
409
-
410
- ```ts type-equiv
411
- /** Readonly live projection of the message-producing session events. */
412
- interface SessionSurface {
413
- /** Current surface event sequences in model-visible order. */
414
- readonly nodes: readonly SessionSeq[]
415
- /** Monotonic count of committed positional replacements. */
416
- readonly replaceGeneration: number
417
- /** Monotonic count of committed replacements and plugin-owned message changes. */
418
- readonly contentGeneration: number
419
- }
420
- ```
421
-
422
- ### `SurfaceFoldReplacement` 与 `SurfaceFoldResult`:完整的 surface 回放
423
-
424
- `foldSurface(events, projections)` 返回一份独立的当前事件 seq 列表,以及每个声明的替换范围实际遮蔽的 seq。实时管理器复用同一套状态转换,但不保留替换历史。每提交一次替换,其 `replaceGeneration` 就递增一次,使增量消费方能够区分纯尾部增长与重写。
425
-
426
- ```ts type-equiv
427
- /** One replacement operation observed while folding a session surface. */
428
- interface SurfaceFoldReplacement {
429
- /** Seq of the event that replaced the prior surface range. */
430
- seq: SessionSeq
431
- /** Declared inclusive start seq of the replaced surface range. */
432
- start: SessionSeq
433
- /** Declared inclusive end seq of the replaced surface range. */
434
- end: SessionSeq
435
- /** Actual surface entries removed by the operation, in surface order. */
436
- shadowedSeqs: SessionSeq[]
437
- }
438
- ```
439
-
440
- ```ts type-equiv
441
- /** Complete result of replaying the surface operations in a session log. */
442
- interface SurfaceFoldResult {
443
- /** Current surface event sequences in model-visible order. */
444
- nodes: SessionSeq[]
445
- /** Replacement operations in event order. */
446
- replacements: SurfaceFoldReplacement[]
447
- /** Immutable projected messages, keyed by their original event sequences. */
448
- projectedMessages: ReadonlyMap<SessionSeq, Message>
449
- }
450
- ```
451
-
452
- ## `Session` 公共 API
453
-
454
- 去除方法体的声明与源码中的普通类保持同步,覆盖其脱离态工厂、状态访问器、append 方法和历史投影。存储操作仍由生成的 [`ctx.sessions` 小节](#ctxsessions--sessionstore)记录。
455
-
456
- ```ts public-api
457
- /**
458
- * An event-sourced session: an append-only log of {@link SessionEvent}s.
459
- *
460
- * Plain class (not a Service) — create live instances via
461
- * `ctx.sessions.create()` and detached instances via {@link create}.
462
- * Seeding with an existing event log replays/forks a session.
463
- * @typert object
464
- */
465
- declare class Session {
466
- /** The ordered surface over this session's event log. */
467
- get surface(): SessionSurface;
468
- /**
469
- * Detached, deep-frozen creation metadata (format version, cwd, lineage,
470
- * and whether fork history exists). Supplied by the store via `ctx.sessions.create()`. When a
471
- * `Session` is created without a store-owned header, a minimal header is
472
- * synthesized (stamped with the current {@link SESSION_FORMAT_VERSION}) so
473
- * `session.header` is always present. Kept out of the event log — it is a
474
- * storage concern, not replayable conversation state.
475
- */
476
- readonly header: SessionHeader;
477
- /** Number of leading events inherited from this Session's fork parent. */
478
- readonly inheritedEventCount: SessionLogOffset;
479
- /** The session identity, derived from its durable header's single copy. */
480
- get id(): SessionId;
481
- /**
482
- * The constructor seed length (0 without one), before any marker appended
483
- * during construction. Seed events never publish on `session/event`. A
484
- * marker appended before the store attaches occupies this seq without
485
- * publishing either; otherwise this seq is available for the next append.
486
- *
487
- * This in-process offset is not persisted. A fork seed can already contain
488
- * the child's inherited marker and synthetic closers, so its child-owned
489
- * history starts at {@link inheritedEventCount}, before this offset. A
490
- * resumed Session's seed contains its full stored log, while its inherited
491
- * count keeps the durable fork cut. Consumers needing complete canonical
492
- * history start at seq 0.
493
- */
494
- readonly firstLiveSeq: SessionLogOffset;
495
- /**
496
- * First event produced for this object lifecycle. A new fork includes its
497
- * child-owned seed marker and closers; a restored Session starts after its
498
- * complete stored prefix. This in-process capture offset is not persisted.
499
- */
500
- readonly firstLifecycleSeq: SessionLogOffset;
501
-
502
- /**
503
- * Create a detached session by validating and snapshotting borrowed seed
504
- * events and storage metadata.
505
- * @param id - session identity.
506
- * @param seed - optional borrowed replay or fork events.
507
- * @param header - optional borrowed storage metadata.
508
- * @param inheritedEventCount - exact fork-inherited prefix length for a seeded header.
509
- * @param projections - pure interpreters for plugin-owned message changes.
510
- * @returns a detached session.
511
- * @throws when a seed event requires a missing message interpreter or fails validation.
512
- */
513
- static create(
514
- id: SessionId,
515
- seed?: readonly SessionEvent[],
516
- header?: SessionHeader,
517
- inheritedEventCount?: SessionLogOffset,
518
- projections?: readonly SessionMessageProjection[],
519
- ): Session;
520
- /**
521
- * Restore a detached session by adopting an independently owned or deeply frozen seed.
522
- * Runtime-required event fields, event envelopes, sequence continuity, surface
523
- * transitions, and header fields are validated without copying or freezing events.
524
- * Embedded Assistant streams remain opaque until a stream consumer or storage
525
- * verifier reads them.
526
- * @param id - restored session identity.
527
- * @param seed - independently owned or deeply frozen events.
528
- * @param header - independently owned storage metadata.
529
- * @param inheritedEventCount - exact fork-inherited prefix length decoded from storage.
530
- * @param eventState - aliasing state carried from the operation that produced the seed.
531
- * @param projections - pure interpreters for plugin-owned message changes.
532
- * @returns a restored detached session.
533
- * @throws when a seed event requires a missing message interpreter or fails validation.
534
- */
535
- static fromRestore(
536
- id: SessionId,
537
- seed: readonly SessionEvent[],
538
- header: SessionHeader,
539
- inheritedEventCount: SessionLogOffset,
540
- eventState: SessionSeedEventState,
541
- projections?: readonly SessionMessageProjection[],
542
- ): Session;
543
- /**
544
- * Return the immutable event stored at one exact sequence number.
545
- * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited.
546
- * See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md).
547
- * @param seq - event sequence number.
548
- * @returns the accepted event, or undefined when the log does not contain it.
549
- */
550
- eventAt(seq: SessionSeq): SessionEvent | undefined;
551
- /**
552
- * Materialize an immutable snapshot of a half-open event sequence range.
553
- * A full current snapshot is reused until the next append; every previously
554
- * returned snapshot remains stable after later appends.
555
- * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited.
556
- * See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md).
557
- * @param fromSeq - non-negative inclusive sequence number; defaults to the log start.
558
- * @param toSeqExclusive - non-negative exclusive sequence number; defaults to the current end.
559
- * @returns a frozen array of the selected deeply frozen events.
560
- */
561
- snapshotEvents(
562
- fromSeq: SessionLogOffset = SessionLogOffset(0),
563
- toSeqExclusive: SessionLogOffset = this.seq,
564
- ): readonly SessionEvent[];
565
- /**
566
- * Return this Session's events after its fork-inherited prefix.
567
- * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited.
568
- * See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md).
569
- * @returns a fresh array containing child-owned events in log order.
570
- */
571
- ownEvents(): readonly SessionEvent[];
572
- /**
573
- * Whether one existing event position is outside the fork-inherited prefix.
574
- * @param seq - event position in this Session.
575
- * @returns true when the event belongs to this Session rather than its parent.
576
- */
577
- isOwnSeq(seq: SessionSeq): boolean;
578
- /** The next event's sequence number — always the log length (the `seq = log.length` contiguity contract). */
579
- get seq(): SessionLogOffset;
580
- /**
581
- * Append one typed event to the log and synchronously notify observers via
582
- * the store-owned, module-private publication hooks. The hot path never blocks
583
- * on I/O — persistence plugins buffer asynchronously. Once the event enters
584
- * the log, the append is committed: observer failures are logged and
585
- * contained per listener, so they do not change the return value or prevent
586
- * later listeners from observing the same accepted event.
587
- *
588
- * @param type - The event type (key of {@link SessionEventMap}).
589
- * @param data - The event payload; must be JSON-serializable.
590
- * @param opts - Surface metadata: `surfaceOp` controls how the event enters
591
- * the ordered surface; `sourceEventSeqs` lists the seq numbers of earlier
592
- * events this one derives from. REQUIRED for
593
- * {@link SurfaceEventType} events (every message-producing event must
594
- * declare how it joins the surface, the sole source of derived model
595
- * history) and
596
- * rejected by the compiler for non-surface types like `turn/start` or
597
- * `assistant/attempt`. Assistant messages embed their exact provider
598
- * stream and cannot cite top-level source events.
599
- * @returns the logged event — its assigned `seq`/`time` plus the SNAPSHOT of
600
- * `data` that entered the log, so reading `event.data` back sees the logged
601
- * value, never the caller's still-mutable input.
602
- * @throws if `data` or surface metadata is not losslessly JSON-serializable
603
- * (BigInt, function, symbol, undefined, negative zero, non-finite number,
604
- * circular reference, sparse array, or an exotic object such as
605
- * Map/Set/Date/class instance), or when the candidate violates the
606
- * request-header empty-field or tool-error consistency rules, or the
607
- * canonical surface contract (marker shape and eligibility, unique
608
- * earlier source-event references, positional replacement validity, and complete
609
- * shadowed-node coverage). One iterative pass reads, validates, and
610
- * copies each nested value once, so a stateful getter cannot supply one value
611
- * to validation and another to storage. The event log is the durable source
612
- * of truth, so a bad event fails at the append site rather than later during
613
- * a backend flush. A synchronous internal dispatch validation failure or an
614
- * append reentered while this acceptance/publication boundary is open also
615
- * rejects before the log changes.
616
- */
617
- append<T extends SessionEventType>(
618
- type: T,
619
- data: SessionEventMap[T],
620
- ...opts: T extends SurfaceEventType ? [opts: SurfaceIntent<T>] : []
621
- ): SessionEvent<T>;
622
- /**
623
- * The {@link EpochHeader} in force after the log's last header event — the
624
- * header the NEXT request will be compared against — or undefined before
625
- * the first `request/header` snapshot. The live, incrementally-maintained
626
- * form of `foldRequestHeader(session.snapshotEvents())`: each header event is folded
627
- * once, when first seen, so a per-step read costs O(new events).
628
- * @returns the folded header, or undefined when no header event exists yet.
629
- */
630
- requestHeader(): EpochHeader | undefined;
631
- /**
632
- * Return the latest resolved route metadata, or `undefined` before the first
633
- * `request/context` event. Each event is folded once.
634
- * @returns the latest immutable route metadata.
635
- */
636
- requestContext(): RequestContext | undefined;
637
- /**
638
- * Fold unseen committed events into capability-independent tool history.
639
- * Initial access reconstructs inherited history; later reads consume only new events.
640
- * @returns an immutable snapshot for LLM request projection, including historical addition definitions.
641
- */
642
- toolHistory(): ToolHistory;
643
- /**
644
- * Derive the LLM message history by walking the ordered sequences of
645
- * message-producing events maintained by `surfaceOp` markers. The
646
- * surface is the single source of derived history: every message-producing
647
- * append records its `surfaceOp`, so a raw event with no marker (a chunk, a
648
- * turn boundary) is correctly absent, and a compaction `replace` deletes the
649
- * shadowed nodes from the derivation. The projection rules are
650
- * {@link deriveEventMessage}, with logged message projections applied
651
- * without changing node membership or message identity.
652
- *
653
- * CACHED: pure tail growth costs O(new nodes); a replacement or message projection
654
- * ({@link SessionSurface.contentGeneration}) rebuilds. The returned array is
655
- * a fresh snapshot per call (later appends never grow an array a caller
656
- * already holds); the `Message` objects in it are SHARED and **deep-frozen**.
657
- * Unchanged content reuses frozen event data; projected blocks are frozen
658
- * derived copies. Consumers cannot mutate the log through either form.
659
- * @returns a fresh array of the shared, frozen derived history.
660
- */
661
- deriveMessages(): Message[];
662
- /**
663
- * Project one event with all committed message projections applied.
664
- * The original durable event remains unchanged.
665
- * @param event - the event to project.
666
- * @returns the derived message, or null when the event produces none.
667
- */
668
- deriveEventMessage(event: SessionEvent): Message | null;
669
- }
670
- ```
671
-
672
- ## 派生历史:`deriveMessages()` 与 `deriveEventMessage()`
673
-
674
- `Session.deriveMessages()` 将事件日志投影为模型看到的 `Message[]`。它是缓存的(每个 surface 节点在首次出现时投影一次;surface 重写触发重建)且冻结的(每次调用返回一个新数组,引用共享的深冻结消息,因此通过投影修改已记录的历史在类型上不可表达)。`deriveEventMessage(event)` 是折叠所应用的逐节点纯函数,公开暴露以便外部重建器能以完全相同的规则投影日志前缀,不会与缓存产生分歧。投影规则:
675
-
676
- - `user/message` → 一条携带确切 `content` 的 user 消息;可选 envelope 仅作为日志中的展示元数据保留。
677
- - `assistant/message` → 一条 assistant 消息,包含生成它的提供方和模型,以及可选的适配器私有回放状态。其嵌入式紧凑 stream 是回放、usage 与 UI 证据,而不是第二条 message。**内容为空的** `assistant/message` 也会跳过:因 max-tokens 而截断且无内容的步骤仍会记录一条 `assistant/message` 来保存 stream、usage、提供方和模型,但无内容的 assistant 轮次不得进入提供方 transcript(文本记录)。
678
- - `tool/result` → 一条一等 tool-role 消息,携带结果内容与工具调用标识。
679
- - `user/message`(注入上下文,即非 `user` 来源)→ 按时间顺序在相应位置生成一条 user-role 消息,并原样承载其 `content`;其类型化 source 标明生产方,并携带所有生产方专用数据。
680
-
681
- 其余所有事件(`turn/*`、`step/*`、`assistant/attempt`、插件所属的 `llm/retry`)均为结构信息,不会投影为消息。token 记账会展开每个 `assistant/message` 或 `assistant/attempt` 的嵌入式 stream,message 顶层 `usage` 存在时仍是已提交 message 的权威。失败的模型请求 attempt 因此可以保留提供方 usage,而无需虚构 assistant message。当前逻辑校验会拒绝没有提供方/模型的 request header 和 assistant 消息,而不会猜测路由;受支持的历史表示会在当前 Session 存在前,由其相邻格式迁移边归一化并校验。
682
-
683
- ## 活跃会话 fork API
684
-
685
- `ctx.sessions.create(id, { seed, meta })` 是底层的回放/fork 原语。对于普通的活跃会话 fork,`SessionStore` 暴露一个策略 API:
686
-
687
- - `fork(source, boundary?, childSessionId?)` 接受一个活跃的 `Session` 对象或活跃的 `SessionId`,选取到 `SessionSeq` boundary(含)为止的源事件(默认为当前最后一个事件),要求所选前缀结束时没有开放轮次,然后创建一个活跃的子会话,包含深克隆的 seed event、`parentSession`、`isSeeded: true`、精确 `inheritedEventCount` 及继承的 `cwd`。
688
-
689
- 显式 `boundary` 允许调用者从任意稳定的轮次间位置 fork,包括之前的 `turn/end` 或更晚的独立纯日志事件,即使源会话有更新的事件或正在进行的轮次。API 拒绝结束于开放轮次内的前缀,而不是静默截断。`dsh-subagent-fork-in-process` 保留其已完成前缀截断逻辑,因为工具调用时的委托通常在父轮次仍然打开时启动;普通的会话分支应显式指定请求的 boundary。
690
-
691
- <a id="why-a-turn-ended-turnendreasonmap"></a>
692
-
693
- ## 轮次的结束原因:`TurnEndReasonMap`
694
-
695
- `turn/start` 没有 trigger 字段。已进入的 `user/message` 批次记录进入每个步骤的内容,`llm/retry` 记录请求恢复,idle 注入则保持待处理,直到唤醒交付抵达后续 pre-step。实时轮次会保留停止驱动器的类型化 [`AgentCancelCause`](./core.md#the-agent-handle);只有在导入受支持的粗粒度取消记录且记录未保存调用方时,持久化才使用额外的 `{ kind: 'legacy' }` 原因。
696
-
697
- ```ts type-equiv
698
- /** Durable cancellation cause, including imports whose original coarse record carried no cause. */
699
- type TurnEndCancelCause = AgentCancelCause | { readonly kind: 'legacy' }
700
- ```
701
-
702
- ```ts type-equiv
703
- /**
704
- * Why a turn ended. Merge-extensible sum type.
705
- */
706
- interface TurnEndReasonMap {
707
- completed: { kind: 'completed' }
708
- /** A cancellation request interrupted the live turn. */
709
- aborted: { kind: 'aborted'; reason: TurnEndCancelCause }
710
-
711
- blocked: { kind: 'blocked' }
712
- /**
713
- * The turn failed. `error` is always a structured failure: the `LlmError`
714
- * facts verbatim, or `{ message: errorChain(error), code: 'UNKNOWN' }`
715
- * flattened from any other error.
716
- */
717
- error: { kind: 'error'; error: LlmFailure }
718
- /** At least one step reached its output-token ceiling, even if a plugin continued the turn. */
719
- 'max-tokens': { kind: 'max-tokens' }
720
- /**
721
- * A crash-orphaned turn was closed after the fact: agent-loop resume appends
722
- * this closer for a stored log whose last turn never ended, and session-query
723
- * synthesizes it on cold reads. The loop never emits this marker live, and
724
- * the events recorded before the crash remain intact.
725
- */
726
- interrupted: { kind: 'interrupted' }
727
- /**
728
- * Fork-seed construction closed a turn that was still open at the fork
729
- * boundary in the source session. Only fork seeds carry this marker — the
730
- * loop never emits it — and the source events before the boundary remain
731
- * intact in the child.
732
- */
733
- forked: { kind: 'forked' }
734
- }
735
- ```
736
-
737
- `max-tokens` 与模型调用中同名的 `FinishReason` 对应:只要轮次内有任何步骤以 `max-tokens` 结束,整个轮次就以 `max-tokens` 而不是 `completed` 结束(即使之后继续执行,截断事实仍优先),让消费方能够区分正常停止和截断停止。取消和错误仍是不同的结果。loop 不会实时发出 `interrupted` 或 `forked`:崩溃恢复合成 `interrupted`(见 [persistence.md](./persistence.md)),而 fork 种子构造合成 `forked`。该 map 可通过合并扩展。
738
-
739
- ## 执行封闭与独立事件
740
-
741
- 一个轮次包围一次模型循环执行,而不是整个会话日志。AgentLoop 只会在轮次内进入 pre-step 批次时记录注入的 `user/message` 事件;插件所属的纯日志事件仍可出现在 `turn/end` 与下一个 `turn/start` 之间,占用事件 seq 但不递增轮次编号。持久化会将每个连续且已接受的事件纳入有界持久化批次,而崩溃修复只关闭确实仍处于开放状态的尾部轮次。需要即时持久性屏障的生产方会显式等待 `ctx.sessions.flush(session)`。
742
-
743
- 可合并扩展事件的关系由声明它的插件拥有,因此核心不会仅因没有开放轮次就拒绝未知事件。见[独立事件决策](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/simplification/2026-07-28-remove-synthetic-log-only-turns.zh.md)。
744
-
745
- ## 种子结束边界:`session/end-seed`
746
-
747
- `buildForkSeed` 在精确的 inherited-prefix cut 追加 `session/end-seed { inherited: true }`,然后添加合成的 fork 结束事件。`Session` constructor 保留该预先构造的 marker,或在 seed 仅含 inherited prefix 时追加 marker。restore 保留 tagged marker,并且只在完整 stored seed 尚未以 marker 结尾时追加普通 `session/end-seed {}`。两种形式都只进入 log 且不产生 message;只有 constructor 和 `buildForkSeed` 可以创建它们。
748
-
749
- 对于 fork lineage,定位 payload 携带 `inherited: true` 的最后一个 marker;当前格式 decoding 只在 `SessionHeader.isSeeded` 为 true 时要求该 marker,并从其 seq 推导 `inheritedEventCount`。对于 lifecycle ownership,定位任一形式的最后一个 `session/end-seed`。重新打开已经以任一 marker 结尾的 seed 时,不会再追加普通 marker。
750
-
751
- 它之所以必要,是因为种子历史与实时工作在字节层面完全相同,这会让任何拥有独立开/闭括号的插件失效:一个未配对的 `compaction/start`,无论写入方是在压缩中途崩溃、还是此刻正在压缩,读起来都一样。在 `session/end-seed` 之前的开启标记来自构造种子,并且属于一个已结束的生命周期,无论结束原因为何(崩溃、进程接替,或从仍在运行的父会话 fork 出来),因此其所有方可以视之为已死。这只覆盖*本*会话继承的括号:另一个并发存活的会话可能在同一段历史上持有开放括号,而它自己的边界在别处,因此容忍并发写入方还需要日志之外的存活信号。核心写入该边界但不从中读取任何内容——括号的词汇表仍归其所属插件,这也正是崩溃修复只关闭轮次/步骤/工具边界而从不处理 `compaction/*` 的原因。
752
-
753
- 按真人活动排序 Session 的消费方会排除该边界:接手 Session 不算工作,因此按日志尾部排序会把每个打开过的 Session 顶到最前。
754
-
755
- ## 插件贡献的仅日志事件
756
-
757
- 插件可以通过 declaration merging 添加额外的 `SessionEventMap` 类型。这些是**仅日志**事件:不是 `SurfaceEventType`(不携带 `surfaceOp`,不参与派生历史)。事件所有方决定它们属于一个开放的执行轮次,还是可以独立位于轮次之间。生成的[持久化日志事件目录](../persistence-catalog.md)会列出每个核心或插件贡献的事件;压缩 seam 的 `compaction/*` 语义在 [compaction.md](./compaction.md) 中讨论。
758
-
759
- 如果同一个插件事件族中的多条事件要组装成一个 Web Client Conversation Node,该事件族中的每条 start、update、result、resource 或 interruption 事件都必须携带或独立推导出同一个稳定业务 id。此要求只约束需要关联的 Node 事件族,并不要求每条 Session 事件都有业务 id;Client 因此无须根据相邻关系猜测归属,也无须扫描历史。参见 [Conversation 子系统](./conversation.md)。
760
-
761
- 钩子桥接层的 `hook/invoked` / `hook/result` 对(来自 `@deepseek-ai/dsh-hook-protocol`)通过 `handlerId` 关联。`UserPromptSubmit`、`PreToolUse`、`PostToolUse` 与 `Stop` 在 loop 已打开的轮次内触发,因此其 `hook/*` 记录天然位于轮次之内。`SessionStart` 不生成 `hook/*` 记录,因为它在轮次 1 之前运行;其上下文会在 inbox 中保持待处理,直到唤醒交付打开一个轮次。
762
-
763
- ### 插件记录
764
-
765
- 实验性包在 `PluginRecordMap` 中声明插件自有的状态:`appendPluginRecord()` 根据每项声明约束名称与 payload 类型,并在 `plugin:` 命名空间中追加一条带 `ignorable: true` 标记的事件。`pluginRecordOf()` 读取已存记录,其 payload 为未知类型,不受当前声明约束。[实验性持久化目录](../experimental-persistence-catalog.md)列出当前声明。调用方限制、保留方式与读取路径见[包 README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/README.zh.md#write-experimental-plugin-records)。
766
-
767
- ```ts type-equiv
768
- /**
769
- * Event type of an experimental plugin record: `plugin:` followed by
770
- * slash-separated segments of lowercase letters, digits, `.`, `_`, and `-`,
771
- * each starting with a letter or digit. A record type is never a
772
- * {@link SessionEventMap} member.
773
- */
774
- type PluginRecordType = `plugin:${string}`
775
- ```
776
-
777
- ```ts type-equiv
778
- /**
779
- * Payloads written by experimental packages, keyed by their `plugin:` record
780
- * names. Packages augment this map to type writes and enter the current plugin
781
- * record catalog; these declarations do not enter {@link SessionEventMap} or
782
- * released persistence schemas. Stored records still require owner validation.
783
- */
784
- interface PluginRecordMap {}
785
- ```
786
-
787
- ```ts type-equiv
788
- /**
789
- * One committed experimental plugin record, read from the log by
790
- * `pluginRecordOf`. The record's owner validates `data` before use, because a
791
- * restored record carries whatever JSON an earlier build of its owner wrote,
792
- * or a V3 event that a format migration renamed into the `plugin:` namespace held.
793
- */
794
- interface PluginRecord {
795
- /** The record type, chosen by the owning plugin. */
796
- readonly type: PluginRecordType
797
- /** The record's position in the Session log. */
798
- readonly seq: SessionSeq
799
- /** Unix epoch milliseconds at append. */
800
- readonly time: number
801
- /** The JSON payload as committed. */
802
- readonly data: unknown
803
- }
804
- ```
805
-
806
- ## 持久性约定
807
-
808
- 持久化后端依赖的约定如下:持久日志无损保存每个事件,每个 Assistant attempt 都是一个 `assistant/message` 或 `assistant/attempt`,其嵌入式紧凑 stream 会保留原始带时间 chunk。`seq` 在这些 settlement 与所有交错事件之间保持连续。后端可以为事件批次选择自己的存储 framing,只要句柄的 `read()` 返回与追加时完全一致的事件即可;当前 JSONL 每个事件写一行(见 [persistence.md](./persistence.md))。所有 `event.data` 都必须可序列化为 JSON;`Session.append` 会从源头强制这一要求(遇到不可序列化数据时抛出),因此错误事件绝不会进入日志,`session.snapshotEvents()` 始终与后端可持久化的内容一致。新增会携带不可序列化数据、破坏核心执行嵌套或违反事件所有方声明关系的事件类型,都会构成磁盘格式的破坏性变更。
809
-
810
- 消费此约定的后端见 [persistence.md](./persistence.md)。
811
-
812
- ## Remote 目录与 workspace 打开
813
-
814
- `ModelCatalog` 是 `session/modelCatalog` 返回的 Host generation 模型目录:它携带部署默认值、可路由 provider id、成功的 provider 分组与相互隔离的 provider 失败。它不由某个 Session 派生,因此与 Session projection 分开保存。
815
-
816
- `SessionOpenWorkspacePathRequest` 携带绝对路径或已按 workspace 解析的 `path`。`SessionOpenWorkspacePathValue` 确认 Host 已接受原生交接。Session-aware Client 会在已知当前 Session cwd 时据此解析相对路径;controller 将路径原样交给打开器,并通过 Session Remote 错误词汇表报告无效请求、取消与打开器失败。 可选的 `action: "reveal"` 选择文件管理器导航;省略时使用默认应用打开。
817
-
818
- 可选的 `application` 指定文件关联应用,不修改系统默认值;`workspacePathApplications({ path })` 在校验 Host 路径后返回当前关联应用、名称、图标和默认项。
819
-
820
- <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
821
-
822
- <a id="cordis-surface"></a>
823
-
824
- ## Cordis API
825
-
826
- 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).
827
-
828
- <a id="ctxsessioncontroller--sessioncontroller"></a>
829
-
830
- ### `ctx.sessionController` — `SessionController`
831
-
832
- Host service backing the generated `ctx.remote.session` namespace.
833
-
834
- ```ts cordis-catalog
835
- /**
836
- * Resolve or resume one ordinary Session for another Host API domain.
837
- * @param sessionId - Session identity whose Agent owns the operation.
838
- * @returns the live Agent or the stable Session-domain failure.
839
- */
840
- resolveAgent(sessionId: SessionId): Promise<ApiSessionAgentResult>
841
-
842
- /**
843
- * Inspect one attached or persisted Session without activating its Agent.
844
- * @param sessionId - durable Session identity.
845
- * @param signal - optional caller cancellation for persistence reads.
846
- * @returns the current attached state or persisted header and event prefix.
847
- */
848
- inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<SessionInspection>
849
-
850
- /**
851
- * Read all visible Session rows without resuming an Agent.
852
- * @param _request - reserved empty list request.
853
- * @param signal - cancellation for persistence reads and summary generation.
854
- * @returns visible Session summaries ordered by activity.
855
- */
856
- @Remote('list') async list(_request: SessionListRequest, signal: AbortSignal): Promise<SessionListValue>
857
-
858
- /**
859
- * Search visible Session content without resuming an Agent. A non-owner
860
- * receives only the hits whose Session `allows()` admits; `hasMore`
861
- * describes the bounded result before that filtering.
862
- * @param request - literal message-content query.
863
- * @param signal - cancellation for list and search reads.
864
- * @returns authorized bounded Session search results.
865
- */
866
- @Remote('search') async search(request: SessionSearchRequest, signal: AbortSignal): Promise<SessionSearchValue>
867
-
868
- /**
869
- * Create one ordinary Session for a new conversation, reusing the named blank Session while it is reusable.
870
- * @param request - optional blank Session to reuse, location, and Agent preset.
871
- * @returns the created or reused Session identity and its preset when configured.
872
- */
873
- @Remote('create') create(request: SessionCreateRequest): Promise<SessionCreateValue>
874
-
875
- /**
876
- * Select one Session-local model after explicitly resuming the Session; save the default in the background.
877
- * @param request - Session identity and requested model selection.
878
- * @returns the normalized selection installed for the Session, without waiting for default persistence.
879
- */
880
- @Remote('selectModel') selectModel(request: SessionSelectModelRequest): Promise<SessionSelectModelValue>
881
-
882
- /**
883
- * Select the first available account model after login when no provider API key is configured.
884
- * @returns after saving the first available model or retaining the existing default.
885
- */
886
- @Remote async initializeDefaultModel(): Promise<void>
887
-
888
- /**
889
- * Describe every currently routable model for Host-generation selectors.
890
- * @returns provider-grouped models, the deployment default, and isolated provider failures.
891
- */
892
- @Remote('modelCatalog') modelCatalog(): Promise<ModelCatalog>
893
-
894
- /**
895
- * Report whether this deployment can hand a Session workspace path to a native desktop.
896
- * @returns true when the matching open operation is available.
897
- */
898
- @Remote canOpenWorkspacePath(): boolean
899
-
900
- /**
901
- * Describe the serving desktop for authenticated file-action routes.
902
- * @returns Host name, configured availability, and platform-specific file-manager behavior.
903
- */
904
- workspaceDesktop(): { name: string; available: boolean; fileManager: 'finder' | 'explorer' | 'directory' | null }
905
-
906
- /**
907
- * Verify one path through the composed filesystem and open it on the Host desktop.
908
- * @param request - path after best-effort Session workspace resolution.
909
- * @param signal - caller lifetime; abort terminates the native command.
910
- * @returns confirmation after the native opener accepts the path.
911
- * @throws RemoteError when the request is invalid, has no verified Host mapping, is cancelled, or the opener fails.
912
- */
913
- @Remote('openWorkspacePath') async openWorkspacePath( request: SessionOpenWorkspacePathRequest, signal: AbortSignal, ): Promise<SessionOpenWorkspacePathValue>
914
-
915
- /**
916
- * Query current file handlers on the serving desktop without activating an Agent.
917
- * @param request - file path in Host filesystem syntax.
918
- * @param signal - caller lifetime, propagated to filesystem and desktop queries.
919
- * @returns OS application names, icons, and default selection; empty when desktop opening is unavailable.
920
- * @throws RemoteError when the path is invalid, the query is cancelled, or native discovery fails.
921
- */
922
- @Remote('workspacePathApplications') async workspacePathApplications( request: { readonly path: string }, signal: AbortSignal, ): Promise<readonly SessionWorkspacePathApplication[]>
923
-
924
- /**
925
- * Rename one Session after explicitly resuming it.
926
- * @param request - Session identity and proposed title.
927
- * @returns the accepted title and durable event sequence.
928
- */
929
- @Remote('rename') rename(request: SessionRenameRequest): Promise<SessionRenameValue>
930
-
931
- /**
932
- * Fork one cold-readable exact event prefix into a new Session. An omitted
933
- * boundary selects the latest completed-turn prefix; an open cut receives
934
- * synthetic fork closers.
935
- * @param request - source Session, optional exact inclusive event boundary, and migration preflight choice.
936
- * @returns the new Session identity.
937
- */
938
- @Remote('fork') async fork(request: SessionForkRequest): Promise<SessionForkValue>
939
-
940
- /**
941
- * Admit one prompt after explicitly resuming its Session.
942
- * @param request - Session identity, prompt content, source metadata, and delivery mode.
943
- * @param signal - caller cancellation before prompt admission begins.
944
- * @returns acknowledgement that the Agent accepted the prompt.
945
- */
946
- @Remote('prompt') async prompt(request: SessionPromptRequest, signal: AbortSignal): Promise<SessionPromptValue>
947
-
948
- /**
949
- * Read one image proven reachable from the addressed Session log.
950
- * @param request - Session and attachment identities used for authorization.
951
- * @returns the durable attachment reference and base64-encoded bytes.
952
- */
953
- @Remote('attachment') attachment(request: SessionAttachmentRequest): Promise<SessionAttachmentValue>
954
-
955
- /**
956
- * Mutate one still-pending queue occurrence, resuming a cold Agent first.
957
- * @param request - Session, queue item, and requested mutation.
958
- * @returns acknowledgement that the queue mutation was applied.
959
- */
960
- @Remote('updateQueue') updateQueue(request: SessionUpdateQueueRequest): Promise<SessionUpdateQueueValue>
961
-
962
- /**
963
- * Cancel one active Agent turn without dropping its pending inbox.
964
- * @param request - Session whose active Agent turn is cancelled.
965
- * @returns acknowledgement that cancellation was requested.
966
- */
967
- @Remote('cancel') cancel(request: SessionCancelRequest): SessionCancelValue
968
-
969
- /**
970
- * Read one cold-safe, message-aligned Session history page.
971
- * @param request - durable address, backward cursor, and page budget.
972
- * @param signal - cancellation for persistence reads.
973
- * @returns one chronological page.
974
- */
975
- @Remote({ name: 'page', resources: { session: addressedSession } }) page(request: SessionPageRequest, signal: AbortSignal): Promise<SessionPage>
976
-
977
- /**
978
- * Follow one Session log from its opening or resume cursor.
979
- * @param request - durable address and last committed sequence already held by the caller.
980
- * @param signal - cancellation owned by the Remote stream carrier.
981
- * @returns a complete opening snapshot followed by gap-free durable event
982
- * frames and optional cursorless assistant-stream frames.
983
- */
984
- @Remote({ mode: 'stream', resources: { session: addressedSession } }) follow(request: SessionFollowRequest, signal: AbortSignal): AsyncIterable<SessionFollowFrame>
985
-
986
- /**
987
- * Read exact projections, returning cached hints only when migration is required.
988
- * @param request - Session whose current values are required.
989
- * @param signal - cancellation for the Session read.
990
- * @returns a sequenced baseline, cached hints when migration is deferred, or null when absent.
991
- */
992
- @Remote('projections') async projections(request: SessionProjectionsRequest, signal: AbortSignal): Promise<SessionProjectionsValue>
993
-
994
- /**
995
- * Stream a complete live-control baseline followed by replacement frames.
996
- * @param signal - cancellation owned by the Remote stream carrier.
997
- * @returns one complete baseline followed by live replacement frames.
998
- */
999
- @Remote({ mode: 'stream' }) async *control(signal: AbortSignal): AsyncIterable<SessionControlFrame>
1000
- ```
1001
-
1002
- Types: [SessionId](./core.md) · [SessionInspection](./persistence.md) · [SessionSearchRequest](./session-query.md)
1003
-
1004
- Source: [`packages/api/session-controller/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/index.ts)
1005
-
1006
- <a id="ctxsessions--sessionstore"></a>
1007
-
1008
- ### `ctx.sessions` — `SessionStore`
1009
-
1010
- In-memory session store (`ctx.sessions`).
1011
-
1012
- Persistence is intentionally not implemented here — the agent lifecycle attaches a session-log writer to each published session's write handle; a session published outside that lifecycle persists nothing.
1013
-
1014
- ```ts cordis-catalog
1015
- /**
1016
- * Register one event interpreter for live creation, restore, and fork.
1017
- * Disposing the contribution makes sessions that used it refuse further derivation.
1018
- * @param projection - pure definition owned by the event's plugin.
1019
- * @returns the fiber-owned disposer.
1020
- * @throws when another definition already owns this event type.
1021
- */
1022
- registerMessageProjection(projection: SessionMessageProjection): () => Promise<void>
1023
-
1024
- /**
1025
- * Create a session owned by the calling fiber: disposing that fiber stops
1026
- * event notification and removes the session from the store. `options.seed`
1027
- * populates the session with a copy of those events (replay/fork);
1028
- * `options.meta` attaches creation metadata (validated absolute `cwd`, seed
1029
- * and parent lineage, and delegation depth) as the immutable
1030
- * {@link SessionHeader} (the store fills `version`/`id`/`createdAt`).
1031
- *
1032
- * For an agent whose session must be torn down IN ORDER with its loop (so the
1033
- * loop's final events are published before the store attachment ends), do NOT use this
1034
- * — fold the session lifecycle into the agent's own effect via
1035
- * {@link prepare} + {@link enter} + {@link announce} (see
1036
- * `dsh-agent-loop`'s creation transaction).
1037
- *
1038
- * @param id - the session id; omitted, the store mints `session-<n>`.
1039
- * @param options - seed events and/or creation metadata for the header.
1040
- * @returns the live session, already entered and announced.
1041
- * @throws if a session with `id` already exists, metadata is not a plain
1042
- * lossless-JSON record with valid scalar fields, or `meta.cwd` is a
1043
- * non-absolute path (storage backends key directories off it).
1044
- */
1045
- create(id?: SessionId, options?: CreateSessionOptions): Session
1046
-
1047
- /**
1048
- * Build a session WITHOUT entering it into the store — validate the id/cwd and
1049
- * construct the {@link Session} (with its immutable {@link SessionHeader}).
1050
- * Pairs with {@link enter} + {@link announce}: a caller that owns a composite
1051
- * `ctx.effect` (the agent factory) folds the session lifecycle into that ONE
1052
- * effect so a fiber unload tears the session + agent down as a single ORDERED
1053
- * chain rather than as racing sibling effects — which would remove the publication hooks
1054
- * before the driver's closing events commit, dropping them.
1055
- *
1056
- * @param id - the session id; omitted, the store mints `session-<n>`.
1057
- * @param options - seed events and/or creation metadata for the header. With
1058
- * `eventState`, every seed event is either independently owned or any
1059
- * shared value is deeply frozen; {@link Session.fromRestore} validates and
1060
- * adopts those values without copying or freezing them.
1061
- * @returns the constructed session, NOT yet in the store.
1062
- * @throws if a session with `id` already exists, metadata is not a plain
1063
- * lossless-JSON record with valid scalar fields, or `meta.cwd` is a
1064
- * non-absolute path.
1065
- */
1066
- prepare(id?: SessionId, options?: PrepareSessionOptions): Session
1067
-
1068
- /**
1069
- * Enter a {@link prepare}d session into the store: install the module-private
1070
- * append publication hooks and add it to the store. Returns the DETACH
1071
- * disposer (hooks + store removal). Does NOT emit `session/created` —
1072
- * the caller yields this disposer inside its effect and THEN calls
1073
- * {@link announce}, so a throwing `session/created` listener rolls the attach
1074
- * back instead of leaking it.
1075
- *
1076
- * Re-checks the id for a duplicate: `prepare` and `enter` are public
1077
- * cross-package primitives and a caller may interleave arbitrary work (or
1078
- * another create) between them, so a stale prepared session must NOT overwrite
1079
- * a live store entry of the same id — its detach disposer would later delete
1080
- * the REAL session. The {@link create} convenience and the agent factory call
1081
- * the two back-to-back so they never trip this, but the public API cannot
1082
- * assume that.
1083
- *
1084
- * @param session - a {@link prepare}d session not yet in the store.
1085
- * @returns the detach disposer (publication hooks + store removal). When called from
1086
- * a synchronous `session/created` listener, removal and disposal wait until
1087
- * that creation dispatch unwinds.
1088
- * @throws if a session with this id is already in the store.
1089
- */
1090
- enter(session: Session): () => void
1091
-
1092
- /** Emit `session/created` exactly once for an {@link enter}ed session (with
1093
- * the carrier {@link enter} captured). Separate from {@link enter} so the
1094
- * caller can yield the detach disposer first (rollback safety — see
1095
- * {@link enter}).
1096
- * @param session - the entered session to announce to listeners.
1097
- * @throws if the session is not live or its announcement already began,
1098
- * including a reentrant call from a creation listener. */
1099
- announce(session: Session): void
1100
-
1101
- /**
1102
- * Dispatch the awaited `session/flush` durability checkpoint for `session`,
1103
- * with the carrier captured at {@link enter}. THE flush entry point: the
1104
- * store owns the carrier, so callers (the checkpoint policy's per-request
1105
- * barrier, goal-round-driver's idle checkpoint, teardown drains, and consumers
1106
- * that flush themselves before reading storage) must come through here
1107
- * rather than dispatch a raw `ctx.parallel('session/flush', …)` — one owner
1108
- * and one spelling.
1109
- * @param session - the session whose buffered events must reach durable storage.
1110
- * @returns whether at least one durability listener participated, after every
1111
- * listener has settled successfully.
1112
- * @throws the first registered listener failure after every listener settles.
1113
- */
1114
- async flush(session: Session): Promise<boolean>
1115
-
1116
- /**
1117
- * Look up a live session.
1118
- * @param id - the session id to look up.
1119
- * @returns the session, or undefined when no live session has that id.
1120
- */
1121
- get(id: SessionId): Session | undefined
1122
-
1123
- /**
1124
- * All live sessions, in creation order.
1125
- * @returns a fresh array; mutating it does not affect the store.
1126
- */
1127
- list(): Session[]
1128
-
1129
- /**
1130
- * Create a live child session from an exact prefix of a live source.
1131
- * `boundary` is an inclusive source event seq; omitted means the source's
1132
- * current last event. An open tail receives synthetic tool results and
1133
- * step/turn closers with the forked cause. Closed steps and turns remain
1134
- * unchanged, including any failed tool calls already missing results.
1135
- * `inheritedEventCount` counts only copied source events, excluding these closers.
1136
- *
1137
- * @param source - Live source session object or id.
1138
- * @param boundary - Inclusive source event seq to fork through; omitted means
1139
- * the source's current last event, and omitted on an empty source forks an
1140
- * empty child.
1141
- * @param childSessionId - Optional child session id; omitted delegates to
1142
- * `SessionStore`'s id policy.
1143
- * @returns The created live child session.
1144
- */
1145
- fork(source: SessionForkSource, boundary?: SessionSeq, childSessionId?: SessionId): Session
1146
- ```
1147
-
1148
- Types: [CreateSessionOptions](./persistence.md) · [PrepareSessionOptions](./persistence.md) · [SessionId](./core.md)
1149
-
1150
- Source: [`packages/core/session/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/index.ts)
1151
-
1152
- <a id="api-session-events"></a>
1153
-
1154
- ### `api-session/*` events
1155
-
1156
- <a id="api-sessionactivity--emit"></a>
1157
-
1158
- #### `api-session/activity` — emit
1159
-
1160
- One user-authored durable message advanced Session list activity.
1161
-
1162
- ```ts cordis-catalog
1163
- /**
1164
- * One user-authored durable message advanced Session list activity.
1165
- * @mode emit
1166
- * @param sessionId - addressed Session identity.
1167
- * @param updatedAt - durable message time used for list ordering.
1168
- */
1169
- 'api-session/activity'(sessionId: SessionId, updatedAt: number): void
1170
- ```
1171
-
1172
- Types: [SessionId](./core.md)
1173
-
1174
- Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
1175
-
1176
- <a id="api-sessionadded--emit"></a>
1177
-
1178
- #### `api-session/added` — emit
1179
-
1180
- A Session became visible or its Agent was created or disposed. Consumers upsert the summary and replace its current running and availability state.
1181
-
1182
- ```ts cordis-catalog
1183
- /**
1184
- * A Session became visible or its Agent was created or disposed.
1185
- * Consumers upsert the summary and replace its current running and availability state.
1186
- * @mode emit
1187
- * @param summary - current list row for the Session.
1188
- */
1189
- 'api-session/added'(summary: SessionSummary): void
1190
- ```
1191
-
1192
- Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
1193
-
1194
- <a id="api-sessionerror--emit"></a>
1195
-
1196
- #### `api-session/error` — emit
1197
-
1198
- One Agent failed outside a durable turn position.
1199
-
1200
- ```ts cordis-catalog
1201
- /**
1202
- * One Agent failed outside a durable turn position.
1203
- * @mode emit
1204
- * @param sessionId - Agent and Session identity.
1205
- * @param message - user-safe failure chain.
1206
- */
1207
- 'api-session/error'(sessionId: SessionId, message: string): void
1208
- ```
1209
-
1210
- Types: [SessionId](./core.md)
1211
-
1212
- Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
1213
-
1214
- <a id="api-sessionremoved--emit"></a>
1215
-
1216
- #### `api-session/removed` — emit
1217
-
1218
- A Session left the live Host registry.
1219
-
1220
- ```ts cordis-catalog
1221
- /**
1222
- * A Session left the live Host registry.
1223
- * @mode emit
1224
- * @param sessionId - removed Session identity.
1225
- */
1226
- 'api-session/removed'(sessionId: SessionId): void
1227
- ```
1228
-
1229
- Types: [SessionId](./core.md)
1230
-
1231
- Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
1232
-
1233
- <a id="api-sessionstatus--emit"></a>
1234
-
1235
- #### `api-session/status` — emit
1236
-
1237
- One Agent changed running state.
1238
-
1239
- ```ts cordis-catalog
1240
- /**
1241
- * One Agent changed running state.
1242
- * @mode emit
1243
- * @param sessionId - Agent and Session identity.
1244
- * @param running - whether the Agent is running.
1245
- */
1246
- 'api-session/status'(sessionId: SessionId, running: boolean): void
1247
- ```
1248
-
1249
- Types: [SessionId](./core.md)
1250
-
1251
- Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
1252
-
1253
- <a id="session-events"></a>
1254
-
1255
- ### `session/*` events
1256
-
1257
- <a id="sessioncreated--emit"></a>
1258
-
1259
- #### `session/created` — emit
1260
-
1261
- Creation announcement during session publication. A synchronous throw vetoes and rolls back with a paired disposal; detach requested during dispatch is deferred. A returned-promise rejection is logged but cannot retroactively veto this synchronous boundary. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only sessions entered through that agent's context.
1262
-
1263
- ```ts cordis-catalog
1264
- /**
1265
- * Creation announcement during session publication. A synchronous throw vetoes and rolls
1266
- * back with a paired disposal; detach requested during dispatch is deferred.
1267
- * A returned-promise rejection is logged but cannot retroactively veto this
1268
- * synchronous boundary.
1269
- * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners
1270
- * receive only sessions entered through that agent's context.
1271
- * @param session - the session just entered and announced.
1272
- * @mode emit
1273
- */
1274
- 'session/created'(this: Scoped<Session>, session: Session): void
1275
- ```
1276
-
1277
- Types: [Scoped](./scope.md)
1278
-
1279
- Source: [`packages/core/session/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/index.ts)
1280
-
1281
- <a id="sessiondisposed--emit"></a>
1282
-
1283
- #### `session/disposed` — emit
1284
-
1285
- Emitted once when an announced session leaves the store, including publication rollback, but never for an entry whose creation announcement did not begin. Listener failures are logged and contained. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`) reuses the owner scope.
1286
-
1287
- ```ts cordis-catalog
1288
- /**
1289
- * Emitted once when an announced session leaves the store, including
1290
- * publication rollback, but never for an entry whose creation announcement
1291
- * did not begin. Listener failures are logged and contained.
1292
- * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`) reuses the owner scope.
1293
- * @param session - the session that is no longer live in the store.
1294
- * @mode emit
1295
- */
1296
- 'session/disposed'(this: Scoped<Session>, session: Session): void
1297
- ```
1298
-
1299
- Types: [Scoped](./scope.md)
1300
-
1301
- Source: [`packages/core/session/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/index.ts)
1302
-
1303
- <a id="sessionevent--emit"></a>
1304
-
1305
- #### `session/event` — emit
1306
-
1307
- Post-commit, fire-and-forget append feed. The listener snapshot resolves before the log push, but callbacks run after it; observer failures are logged and contained without making the committed append fail. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only events from sessions entered through that agent's context.
1308
-
1309
- ```ts cordis-catalog
1310
- /**
1311
- * Post-commit, fire-and-forget append feed. The listener snapshot resolves
1312
- * before the log push, but callbacks run after it; observer failures are
1313
- * logged and contained without making the committed append fail.
1314
- * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners
1315
- * receive only events from sessions entered through that agent's context.
1316
- * @param session - the session whose log grew.
1317
- * @param event - the appended event, exactly as recorded.
1318
- * @mode emit
1319
- */
1320
- 'session/event'(this: Scoped<Session>, session: Session, event: SessionEvent): void
1321
- ```
1322
-
1323
- Types: [Scoped](./scope.md)
1324
-
1325
- Source: [`packages/core/session/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/index.ts)
1326
-
1327
- <a id="sessionflush--parallel"></a>
1328
-
1329
- #### `session/flush` — parallel
1330
-
1331
- Awaited parallel durability checkpoint: every listener runs and the caller awaits all of them, with no waterfall veto. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`) reuses the session's owner scope.
1332
-
1333
- ```ts cordis-catalog
1334
- /**
1335
- * Awaited parallel durability checkpoint: every listener runs and the
1336
- * caller awaits all of them, with no waterfall veto. Scope-filtered dispatch
1337
- * (`@deepseek-ai/dsh-scope`) reuses the session's owner scope.
1338
- * @param session - the session whose buffered events must reach durable storage.
1339
- * @mode parallel
1340
- */
1341
- 'session/flush'(this: Scoped<Session>, session: Session): Promise<void> | void
1342
- ```
1343
-
1344
- Types: [Scoped](./scope.md)
1345
-
1346
- Source: [`packages/core/session/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/index.ts)
1347
- <!-- END GENERATED cordis-surface -->
1
+ ---
2
+ editSource: "docs/subsystems/session.zh.md"
3
+ rawMarkdownPath: "reference/subsystems/session.md"
4
+ outline: [2,3]
5
+ ---
6
+
7
+ # 会话
8
+
9
+ [dsh-session](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/core/session) 的内存事件溯源模型。`Session` 是一份由类型化 `SessionEvent` 组成的**仅追加日志**,是 agent(智能体)完整交互历史的唯一真源。LLM(大语言模型)消息历史从日志*派生*而来,从不单独存储;回放即从同一组事件重新派生。日志如何实现**持久化**(持久化 seam、后端、崩溃恢复)是兄弟文档 [persistence.md](./persistence.md) 的关注点。
10
+
11
+ 源码:[`packages/core/session/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/types.ts)
12
+
13
+ ## `SessionEventMap`:事件词汇
14
+
15
+ 仅追加的事件类型。可通过声明合并扩展:插件通过 declaration merging 声明额外的事件类型。例如[压缩(compaction) seam](./compaction.md) 添加了 `compaction/start` / `compaction/summary` / `compaction/end`,`@deepseek-ai/dsh-hook-protocol` 为钩子桥接添加了仅记录日志的 `hook/invoked` / `hook/result` 记录。与 `compaction/*` 一样,这些都不是 `SurfaceEventType`(没有 `surfaceOp`)。生成的[持久化日志事件目录](../persistence-catalog.md)列举了所有成员(核心与合并扩展的),包含其 payload、surface 标记与声明位置。
16
+
17
+ ```ts type-equiv
18
+ /** A user-role specialization of the shared message representation. */
19
+ interface UserMessage extends MessageBase {
20
+ readonly role: 'user'
21
+ }
22
+ ```
23
+
24
+ ```ts type-equiv
25
+ /**
26
+ * The merge-extensible, append-only source of truth for an agent interaction.
27
+ * Message history is derived from this log. Every event is lossless JSON and
28
+ * sequence numbers stay contiguous. Assistant attempt events embed their exact
29
+ * compact raw streams so persistence stores one durable settlement per attempt.
30
+ */
31
+ interface SessionEventMap {
32
+ /**
33
+ * Opens turn `turn` before the loop claims queued input or runs pre-step.
34
+ * Rejection, empty input, cancellation, or failure may close it with no
35
+ * step; otherwise the following identified `user/message` event or batch
36
+ * records the messages entering the step.
37
+ */
38
+ 'turn/start': { turn: number }
39
+ /**
40
+ * Closes turn `turn` with the {@link TurnEndReason} that ended it. A turn
41
+ * with no entered step has no `step/start` or `step/end`. The loop does not await a
42
+ * flush at turn boundaries: `dsh-session-checkpoint-policy` owns the
43
+ * per-request durability checkpoint, and consumers that read storage after
44
+ * `whenIdle()` flush themselves. Success commits the turn; rejection is
45
+ * reported live and does not prevent later work.
46
+ */
47
+ 'turn/end': { turn: number; reason: TurnEndReason }
48
+ /** Opens step `step` of turn `turn` — one model call plus the tool executions it requested. */
49
+ 'step/start': { turn: number; step: number }
50
+ /** Closes step `step` of turn `turn`. */
51
+ 'step/end': { turn: number; step: number }
52
+ /**
53
+ * A user-role message on the model-visible surface: a direct human prompt
54
+ * (the queued message claimed for this turn), a synthetic `agent.inject()`
55
+ * context (file-change notices, subdir AGENTS.md, skill content, cron
56
+ * notifications, …), or an entered goal continuation round. All three
57
+ * project their `content` verbatim; `source` tells them apart.
58
+ */
59
+ 'user/message': UserMessage
60
+ /** An incremental agent session change admitted at the named turn and step. */
61
+ 'developer/message': {
62
+ turn: number
63
+ step: number
64
+ message: DeveloperMessage
65
+ /** Earlier request/header defining every tool addition; required exactly when additions are present. */
66
+ headerSeq?: SessionSeq
67
+ }
68
+ /**
69
+ * The rendered system prompt on the model-visible surface. The loop appends
70
+ * the first one as surface node 0 before the step's first `user/message`.
71
+ * A prepared in-history route can append nonempty changes in a continuing
72
+ * series. An incapable route, or a new series whose effective text differs,
73
+ * normalizes text to the first system node; a series start whose latest
74
+ * surviving node already holds the rendering leaves it in place.
75
+ * Normalization empties nonempty later nodes, then rewrites the head if
76
+ * needed, through logged per-node replacements. An empty rendering always
77
+ * clears all active system nodes, leaving no older instructions model-visible.
78
+ * Empty later nodes are dormant and project to no message; an empty head with
79
+ * no active later node records "no system prompt". Restored nonempty text follows
80
+ * the same route and series rule; empty nodes never restore older text.
81
+ */
82
+ 'system/message': { turn: number; step: number; message: SystemMessage }
83
+ /**
84
+ * Assembled assistant message for one step (derived history uses this).
85
+ * Carries the step's `usage` when the adapter reported token accounting, so
86
+ * the model output and its accounting travel together (there is no separate
87
+ * usage record). `usage` is absent when the adapter reported none. A turn
88
+ * cancelled mid-stream finalizes its delivered text/reasoning prefix as this
89
+ * event with `interrupted: true`; undispatched tool calls are absent. The
90
+ * marker distinguishes that prefix without re-deriving interruption from turn
91
+ * boundaries. An aborted turn with no such event streamed no visible content.
92
+ */
93
+ 'assistant/message': {
94
+ turn: number
95
+ step: number
96
+ message: AssistantMessage
97
+ /** Exact timed model stream, compacted without joining delta boundaries. */
98
+ stream: AssistantStreamRecord[]
99
+ usage?: TokenUsage
100
+ interrupted?: true
101
+ }
102
+ /**
103
+ * One model attempt that committed no surface message. The embedded stream
104
+ * preserves a failed, retried, cancelled, or stream-error attempt that
105
+ * reached settlement without fabricating model-visible history.
106
+ */
107
+ 'assistant/attempt': { turn: number; step: number; stream: AssistantStreamRecord[] }
108
+ /**
109
+ * The model requested one tool invocation: `name` with the raw `arguments`
110
+ * JSON string exactly as the model produced it (unparsed). `callId` pairs the
111
+ * call with its `tool/result`.
112
+ */
113
+ 'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string }
114
+ /**
115
+ * A completed tool call's model-facing result, optional internal failure
116
+ * identity and user-facing reason, and optional tool-private `meta`
117
+ * presentation payload. The reason remains outside the model-facing message.
118
+ * `meta` is
119
+ * opaque to the core (the producing tool owns its shape and reads it back in
120
+ * `presentResult`) but MUST be JSON-serializable: `Session.append`
121
+ * runtime-validates all event data with `isJsonValue`, so a non-serializable
122
+ * `meta` is rejected at the source, and the durable log reproduces the
123
+ * identical card on replay. Absent
124
+ * unless the tool attaches one (e.g. `dsh-tool-fs` carries its result-time
125
+ * contextual diff here).
126
+ */
127
+ 'tool/result': {
128
+ turn: number
129
+ step: number
130
+ message: ToolResultMessage
131
+ /**
132
+ * Optional failure identity and raw user-facing reason, outside model content;
133
+ * allowed only when the message has `isError: true`.
134
+ */
135
+ error?: { name: string; code: string; reason?: string }
136
+ meta?: JsonValue
137
+ }
138
+ /**
139
+ * Full header for the next request, appended inside its step before dispatch.
140
+ * It is log-only; the latest snapshot reconstructs the request header.
141
+ */
142
+ 'request/header': {
143
+ header: EpochHeader
144
+ reason: RequestHeaderReason
145
+ /** This request begins a distinct model-message series, independently of the header reason. */
146
+ startsSeries?: true
147
+ }
148
+ /**
149
+ * Route metadata for the next request, logged only when the route, capacity,
150
+ * or system prompt update mode changes. It does not participate in request
151
+ * reconstruction or header equality. Prompt admission uses the bound prepared
152
+ * call's capability, not this snapshot from an earlier request.
153
+ */
154
+ 'request/context': RequestContext
155
+ /**
156
+ * Separates inherited or restored history from later lifecycle-owned work.
157
+ * This log-only marker need not be at {@link Session.firstLiveSeq}: a fork
158
+ * seed can already contain its tagged marker and child-owned synthetic
159
+ * closers before construction.
160
+ *
161
+ * A fresh fork child owns one `{ inherited: true }` marker at its exact
162
+ * inherited-prefix cut, even when that prefix ends in an ancestor marker.
163
+ * `buildForkSeed` appends that marker before any synthetic closers; the
164
+ * `Session` constructor supplies it when given only the inherited prefix.
165
+ * The last tagged marker is the current Session's cut; untagged markers
166
+ * keep ordinary restore and replay lifecycle boundaries.
167
+ *
168
+ * Only the `Session` constructor and `buildForkSeed` may create this marker.
169
+ * Session append does not reject other writers, so a plugin appending one
170
+ * would silently classify every live bracket before it as seed history.
171
+ *
172
+ * An owner of a standalone open/close bracket (`compaction/start` …
173
+ * `compaction/end`) reads it because seed history and live work are otherwise
174
+ * byte-identical: an unmatched opening marker before this event belongs to
175
+ * an ended lifecycle, whatever ended it. NOT a liveness signal about other
176
+ * writers — a concurrently live session holds its own boundary elsewhere,
177
+ * so tolerating concurrent writers needs a signal beyond the log.
178
+ */
179
+ 'session/end-seed': { inherited?: true }
180
+ }
181
+ ```
182
+
183
+ `UserMessage` 是普通提示词、注入上下文、steering(中途引导)与实时收件箱事件共享的带标识且冻结的 user-role 值。事件包装层只会增加事件本地的位置或结果事实;条目待处理期间,loop 只额外附加驱动器自有的路由状态。
184
+
185
+ <a id="the-request-header-event-requestheader"></a>
186
+
187
+ ### 请求头事件:`request/header`
188
+
189
+ 请求信封(即 `EpochHeader`:调用配置 + 适配器所提供默认值的标记 + 已组装的工具 schema)会作为会话状态写入日志,因此每个对话请求都是日志的纯函数(见可重建性 Agent Note)。渲染后的系统提示词不属于请求头:它是派生历史,即 surface 第 0 号节点上的 `system/message` 事件以及任何后续的历史内系统节点([决策](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-02-system-prompt-as-surface-node.zh.md)),因此提示词变更替换或追加一个系统节点,而请求头保持不变。带有 reason `'initial'` 或 `'resume'` 的完整 `request/header` 快照记录每个 agent loop 实例的边界;请求变化时会追加 reason 为 `'change'` 的快照;未变的信封显式开启消息序列或跟随 surface 替换时,会追加 reason 为 `'series'` 的快照。如果 `'initial'`、`'resume'` 或 `'change'` 快照所属请求同时开启序列,它会携带 `startsSeries: true`;reason `'series'` 已记录这一事实。普通的仅追加后续 Turn,以及同一模型消息序列内的后续 Step 与重试沿用最新快照。`foldRequestHeader(events)` 通过选择最新快照重建请求头。该事件不是 `SurfaceEventType`,不产生 LLM 消息。
190
+
191
+ ```ts type-equiv
192
+ /**
193
+ * Logged request state outside derived history: call config and tools. The
194
+ * system prompt is derived history — surface node 0, a `system/message` event.
195
+ * The latest full `request/header` snapshot reconstructs the header; canonical
196
+ * empty optional fields are absent.
197
+ */
198
+ interface EpochHeader {
199
+ /** The conversation's call configuration (provider, model, reasoning effort, and sampling scalars). */
200
+ config: LlmCallConfig
201
+ /** Effective config fields materialized from the exact adapter rather than proposed by a caller. */
202
+ adapterDefaults?: LlmCallConfigAdapterDefaults
203
+ /** Assembled tool schemas; absent for a tool-less request. */
204
+ tools?: ToolSchema[]
205
+ /** Retired request text; system prompts belong to system/message events.
206
+ * @persistenceReserved
207
+ */
208
+ system?: never
209
+ }
210
+ ```
211
+
212
+ 当前事件接纳要求 `request/header.header` 为规范形式:禁止任何 `system` 字段,必须省略 `tools: []` 与 `adapterDefaults: {}`。仅含空白的系统消息内容、`config.stop: []` 与嵌套扩展保持不变。seed、append 与当前持久化读取拒绝非规范 header,而不会静默规范化;[V3 信封决策](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-06-v3-canonical-session-envelopes.zh.md)负责历史转换。包含旧版 `request/header-delta` 事件或完整快照原因为 `fallback` 的旧版 v0 日志,会被拒绝,而不会以不完整方式回放。
213
+
214
+ ### 路由容量事件:`request/context`
215
+
216
+ 请求所解析到的路由的上下文元数据是独立的已记录状态,在同一步骤内紧随 `request/header` 追加,且仅在提供方、模型、容量或 `systemPromptUpdate` 模式与上一条记录不同时追加。它保持在 `EpochHeader` 之外,因为该类型是 `headerEquals` 逐字段比较的重建约定。容量与更新模式描述的是路由,不是请求输入,把它们折叠进去会让一次路由变化被登记为请求信封的 `change`,也会把适配器元数据拉进 loop 的请求重建。与 `request/header` 一样,它不是 `SurfaceEventType`,也不产生 LLM 消息。`session.requestContext()` 以增量方式归并最新一条记录;agent loop 在决定变化后的系统提示词是替换最新的系统节点还是追加到已缓存历史之后时,读取该记录的 `systemPromptUpdate`([决策规则](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent-loop/README.zh.md#understand-the-implementation))。适配器不公布容量的路由会以缺失 `contextWindow` 的形式记录,因此新记录可以清除较早路由的容量;未声明更新模式的路由同样会清除较早路由的 `systemPromptUpdate`。
217
+
218
+ ```ts type-equiv
219
+ /** Registration-bound metadata for one resolved model route. */
220
+ interface RequestContext {
221
+ /** Registered provider route the metadata belongs to. */
222
+ provider: string
223
+ /** Provider-owned model id the metadata belongs to. */
224
+ model: string
225
+ /** Maximum combined request and response context in tokens, when advertised. */
226
+ contextWindow?: number
227
+ /** `'in-history'` when the route reads the latest `system` message at any position as the effective system prompt. */
228
+ systemPromptUpdate?: SystemPromptUpdate
229
+ }
230
+ ```
231
+
232
+ ## `SessionEvent<T>`:一条日志条目
233
+
234
+ 基于 `type` 的真正可辨识联合(而非独立的 `type`/`data` 联合),因此 `switch (event.type)` 能直接收窄 `event.data`,无需类型断言。`seq` 是日志中的单调递增位置(`seq = log.length`);`time` 为 epoch 毫秒。
235
+
236
+ ```ts type-equiv
237
+ /** Sequence number of one existing event in a Session log. */
238
+ type SessionSeq = BrandedNumber<'SessionSeq'>
239
+ ```
240
+
241
+ ```ts type-equiv
242
+ /** A Session log gap, prefix length, or read offset, which may equal the event count. */
243
+ type SessionLogOffset = BrandedNumber<'SessionLogOffset'>
244
+ ```
245
+
246
+ ```ts type-equiv
247
+ /** Inclusive Session event watermark, or `-1` before any event exists. */
248
+ type SessionSeqCursor = SessionSeq | -1
249
+ ```
250
+
251
+ ```ts type-equiv
252
+ /** One existing Session event position, or explicit absence. */
253
+ type OptionalSessionSeq = SessionSeq | null
254
+ ```
255
+
256
+ `SessionSeq(value)` 与 `SessionLogOffset(value)` 只接纳非负安全整数,并拒绝负零。它们仅添加编译期品牌,不改变序列化后的数值;算术会返回普通 `number`,调用方必须按结果的预期角色通过对应构造器重新接纳。
257
+
258
+ ```ts type-equiv
259
+ /**
260
+ * One immutable entry in the session log.
261
+ *
262
+ * A proper discriminated union over `type` (not independent `type`/`data`
263
+ * unions), so `switch (event.type)` narrows `event.data` without casts.
264
+ *
265
+ * The {@link sourceEventSeqs} and {@link surfaceOp} fields are conditional:
266
+ * they only exist on {@link SurfaceEventType} variants (`system/message`, `user/message`,
267
+ * `assistant/message`, `tool/result`).
268
+ * Non-surface events (boundary markers, attempts, errors) never carry
269
+ * surface metadata — the compiler enforces this at `Session.append()`
270
+ * call sites.
271
+ */
272
+ type SessionEvent<T extends SessionEventType = SessionEventType> = {
273
+ [K in SessionEventType]: {
274
+ type: K
275
+ /** Monotonic sequence number within the session. */
276
+ seq: SessionSeq
277
+ /** Unix epoch milliseconds. */
278
+ time: number
279
+ data: SessionEventMap[K]
280
+ /**
281
+ * Marks an event a reader may safely skip when it does not recognize
282
+ * `type`. Absent means required: a reader meeting an unrecognized type
283
+ * without this marker MUST refuse to reconstruct the session instead of
284
+ * silently dropping the event, because an unrecognized required event may
285
+ * change how the rest of the log is interpreted. A writer sets `true` only
286
+ * on purely informational records whose loss cannot affect reconstruction;
287
+ * defaulting to required means a forgotten marker over-refuses (an
288
+ * inconvenience) rather than silently resuming a gutted session.
289
+ */
290
+ ignorable?: true
291
+ } & (K extends SurfaceEventType ? SurfaceIntent<K> : {
292
+ surfaceOp?: never
293
+ sourceEventSeqs?: never
294
+ })
295
+ }[T]
296
+ ```
297
+
298
+ `SessionEventType = keyof SessionEventMap`。由于 `SessionEventMap` 可通过合并扩展,对 `SessionEvent` 的 switch 语句禁止使用 `assertNever`:插件添加的变体是合法的未知值;处理已知 case 后在 `default` 中放行。
299
+
300
+ 每个 surface 事件都要求 `surfaceOp`;已知仅日志事件禁止两个 surface 元数据字段。原生未知或已退役的可忽略信封保持不透明。`assistant/message` 嵌入其提供方 stream,并禁止 `sourceEventSeqs`。System、user 与 tool surface 事件可以在来源归属或替换覆盖需要时引用完整、非空且唯一的较早事件集合。`tool/result` 仅在消息带有 `isError: true` 时可以携带 `data.error`;失败结果的失败身份仍可省略。
301
+
302
+ <a id="surface-types"></a>
303
+
304
+ ## Surface 类型
305
+
306
+ 产生消息的类型(`SurfaceEventType`:`system/message`、`developer/message`、`user/message`、`assistant/message`、`tool/result`)携带 surface 元数据,用来声明它们如何加入有序的派生 surface。`system/message` 承载渲染后的系统提示词:循环把第一条追加为 surface 第 0 号节点,并在提示词变化时恰好替换最新的系统节点,或在历史内路由上追加一条新的;surface 折叠拒绝任何其他覆盖第 0 号节点 `system/message` 的替换,而后续系统节点是普通历史,压缩替换可以遮蔽它。见 [session surface Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-06-18-session-surface.zh.md)。
307
+
308
+ ### `SurfaceEventType`:事件类型中产生消息的子集
309
+
310
+ ```ts type-equiv
311
+ /**
312
+ * The subset of {@link SessionEventType} values whose events produce LLM
313
+ * messages and are eligible to appear on the ordered surface. Only these
314
+ * event types may carry {@link SurfaceOp}; system, user, and tool events may also cite
315
+ * earlier sources through {@link SessionEvent.sourceEventSeqs}.
316
+ */
317
+ type SurfaceEventType =
318
+ | 'system/message'
319
+ | 'developer/message'
320
+ | 'user/message'
321
+ | 'assistant/message'
322
+ | 'tool/result'
323
+ ```
324
+
325
+ ### `SurfaceOp`:事件如何进入 surface
326
+
327
+ ```ts type-equiv
328
+ /**
329
+ * How a session event entered the ordered surface. Only valid on
330
+ * {@link SurfaceEventType} events.
331
+ *
332
+ * - `'append'`: added to the tail — normal path for user/assistant/tool
333
+ * messages.
334
+ * - `{ op: 'replace', startSeq, endSeq }`: replaces surface nodes from `startSeq`
335
+ * (inclusive) through `endSeq` (inclusive) with this node. Both must exist as
336
+ * surface nodes in the current surface. `startSeq === endSeq` replaces a single
337
+ * node. The node's {@link SessionEvent.sourceEventSeqs} must include every
338
+ * shadowed surface node. Used by compaction; any surface-replacing producer
339
+ * may use it.
340
+ */
341
+ type SurfaceOp =
342
+ | 'append'
343
+ | { op: 'replace'; startSeq: SessionSeq; endSeq: SessionSeq }
344
+ ```
345
+
346
+ `'append'` 是常规的尾部追加路径。`replace` 恰好包含 `op`、`startSeq` 和 `endSeq`,不接受别名或额外键。它遮蔽这两个当前 surface 事件序号之间的闭区间,并在原位置插入新事件;相同端点仅替换一个条目。端点必须早于替换事件,但它们的相对顺序按 surface 顺序而非数值序号顺序确定。
347
+
348
+ ### `SurfaceIntent`:`session.append()` 的参数
349
+
350
+ ```ts type-equiv
351
+ /**
352
+ * Surface placement and cited source-event seqs for {@link Session.append}. Required on
353
+ * message-producing events and forbidden on log-only events.
354
+ */
355
+ type SurfaceIntent<T extends SurfaceEventType = SurfaceEventType> = {
356
+ surfaceOp: SurfaceOp
357
+ } & (T extends 'assistant/message' ? {
358
+ /** Assistant messages embed their provider stream instead of citing source events. */
359
+ sourceEventSeqs?: never
360
+ } : {
361
+ /** Complete non-empty set of known earlier source-event seqs. */
362
+ sourceEventSeqs?: SessionSeq[]
363
+ })
364
+ ```
365
+
366
+ 对 `SurfaceEventType` 事件必填:每个产生消息的事件都必须声明它如何加入 surface(派生模型历史的唯一来源)。面向人类的 transcript(文本记录)是另一个投影,读取的是日志中追加来源的事件,因为 surface 会有意遮蔽替换所概括的范围(见 [dsh-session](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/README.zh.md) 的 `isAppendSurfaceEvent`)。非 surface 类型在编译期拒绝此参数。
367
+
368
+ `assistant/message` 不能携带 `sourceEventSeqs`;它的 `stream` 拥有精确 provider 证据。其他 surface event 不引用较早 event 时省略该字段,需要引用时使用完整非空 list。
369
+
370
+ <a id="plugin-owned-message-projections"></a>
371
+ ### 插件拥有的消息投影
372
+
373
+ 修改内容的插件用 `@messageProjection` 标记事件声明,并通过 `ctx.sessions.registerMessageProjection()` 注册纯处理器。Session 在提交前通过处理器校验完整决策,应用其不可变消息更新,并推进 `contentGeneration`。缺少处理器时拒绝操作,包括恢复和独立折叠,卸载已经使用的处理器后也会拒绝读取缓存。独立读取器将处理器显式传给 `foldSurface(events, projections)`,并通过 `deriveEventMessage()` 应用 `projectedMessages`。当前格式目录为离线读取器装配第一方处理器。[图片省略插件](./compaction.md#image-offload)拥有图片专用的事件及解释逻辑。
374
+
375
+ ```ts type-equiv
376
+ /** Readonly history immediately before a message-projection event. */
377
+ interface SessionMessageProjectionContext {
378
+ /** Current message-producing event sequences in model-visible order. */
379
+ nodes: readonly SessionSeq[]
380
+ /** Contiguous event window; entries at or beyond the candidate seq are not committed inputs. */
381
+ events: readonly SessionEvent[]
382
+ /** Absolute sequence of the window's first event. */
383
+ baseSeq: SessionLogOffset
384
+ /** Previously projected messages keyed by their original event sequences. */
385
+ messages: ReadonlyMap<SessionSeq, Message>
386
+ }
387
+ ```
388
+
389
+ ```ts type-equiv
390
+ /** Pure interpretation of one plugin-owned event that changes existing message content. */
391
+ interface SessionMessageProjection<T extends SessionEventType = SessionEventType> {
392
+ /** Event interpreted by this definition; declare it with `@messageProjection` in SessionEventMap. */
393
+ type: T
394
+ /**
395
+ * Validate the complete durable decision before returning any updates. Preserve
396
+ * message identities and publish immutable copies without mutating the input.
397
+ * @param event - candidate event, not yet applied to the supplied history.
398
+ * @param context - history preceding this decision.
399
+ * @returns changed current messages keyed by their original sequences.
400
+ * @throws when the durable decision cannot be applied to this history.
401
+ */
402
+ project(event: SessionEvent<T>, context: SessionMessageProjectionContext): ReadonlyMap<SessionSeq, Message>
403
+ }
404
+ ```
405
+
406
+ ### `SessionSurface`:实时只读 surface 投影
407
+
408
+ `Session.surface` 返回会话稳定的 `SessionSurface` 视图。同一个增量管理器在提交前校验追加候选事件,并根据已提交事件推进该投影;调用方可以观察成员关系和替换代次,但不能调用校验。
409
+
410
+ `SurfaceManager(log, baseSeq?, projections?)` 也可以折叠一个连续的已加载窗口,其第一个事件的绝对序号为 `baseSeq`。每个事件在该绝对序号空间中仍保持连续;如果替换跨过窗口头部,由于其声明的范围并不存在,该替换会失败。
411
+
412
+ ```ts type-equiv
413
+ /** Readonly live projection of the message-producing session events. */
414
+ interface SessionSurface {
415
+ /** Current surface event sequences in model-visible order. */
416
+ readonly nodes: readonly SessionSeq[]
417
+ /** Monotonic count of committed positional replacements. */
418
+ readonly replaceGeneration: number
419
+ /** Monotonic count of committed replacements and plugin-owned message changes. */
420
+ readonly contentGeneration: number
421
+ }
422
+ ```
423
+
424
+ ### `SurfaceFoldReplacement` 与 `SurfaceFoldResult`:完整的 surface 回放
425
+
426
+ `foldSurface(events, projections)` 返回一份独立的当前事件 seq 列表,以及每个声明的替换范围实际遮蔽的 seq。实时管理器复用同一套状态转换,但不保留替换历史。每提交一次替换,其 `replaceGeneration` 就递增一次,使增量消费方能够区分纯尾部增长与重写。
427
+
428
+ ```ts type-equiv
429
+ /** One replacement operation observed while folding a session surface. */
430
+ interface SurfaceFoldReplacement {
431
+ /** Seq of the event that replaced the prior surface range. */
432
+ seq: SessionSeq
433
+ /** Declared inclusive start seq of the replaced surface range. */
434
+ start: SessionSeq
435
+ /** Declared inclusive end seq of the replaced surface range. */
436
+ end: SessionSeq
437
+ /** Actual surface entries removed by the operation, in surface order. */
438
+ shadowedSeqs: SessionSeq[]
439
+ }
440
+ ```
441
+
442
+ ```ts type-equiv
443
+ /** Complete result of replaying the surface operations in a session log. */
444
+ interface SurfaceFoldResult {
445
+ /** Current surface event sequences in model-visible order. */
446
+ nodes: SessionSeq[]
447
+ /** Replacement operations in event order. */
448
+ replacements: SurfaceFoldReplacement[]
449
+ /** Immutable projected messages, keyed by their original event sequences. */
450
+ projectedMessages: ReadonlyMap<SessionSeq, Message>
451
+ }
452
+ ```
453
+
454
+ ## `Session` 公共 API
455
+
456
+ ### `SessionEventInput`:一个候选事件
457
+
458
+ ```ts type-equiv
459
+ /** One positional event call proposed for append, without assigned sequence or time. */
460
+ type SessionEventInput = {
461
+ [K in SessionEventType]: readonly [type: K, data: SessionEventMap[K], ...opts: K extends SurfaceEventType ? [opts: SurfaceIntent<K>] : []]
462
+ }[SessionEventType]
463
+ ```
464
+
465
+ 数组中的每个成员将事件类型与对应载荷配对,且仅对产生消息的事件要求surface意图。
466
+
467
+ 去除方法体的声明与源码中的普通类保持同步,覆盖其脱离态工厂、状态访问器、append 方法和历史投影。存储操作仍由生成的 [`ctx.sessions` 小节](#ctxsessions--sessionstore)记录。
468
+
469
+ ```ts public-api
470
+ /**
471
+ * An event-sourced session: an append-only log of {@link SessionEvent}s.
472
+ *
473
+ * Plain class (not a Service) — create live instances via
474
+ * `ctx.sessions.create()` and detached instances via {@link create}.
475
+ * Seeding with an existing event log replays/forks a session.
476
+ * @typert object
477
+ */
478
+ declare class Session {
479
+ /** The ordered surface over this session's event log. */
480
+ get surface(): SessionSurface;
481
+ /**
482
+ * Detached, deep-frozen creation metadata (format version, cwd, lineage,
483
+ * and whether fork history exists). Supplied by the store via `ctx.sessions.create()`. When a
484
+ * `Session` is created without a store-owned header, a minimal header is
485
+ * synthesized (stamped with the current {@link SESSION_FORMAT_VERSION}) so
486
+ * `session.header` is always present. Kept out of the event log — it is a
487
+ * storage concern, not replayable conversation state.
488
+ */
489
+ readonly header: SessionHeader;
490
+ /** Number of leading events inherited from this Session's fork parent. */
491
+ readonly inheritedEventCount: SessionLogOffset;
492
+ /** The session identity, derived from its durable header's single copy. */
493
+ get id(): SessionId;
494
+ /**
495
+ * The constructor seed length (0 without one), before any marker appended
496
+ * during construction. Seed events never publish on `session/event`. A
497
+ * marker appended before the store attaches occupies this seq without
498
+ * publishing either; otherwise this seq is available for the next append.
499
+ *
500
+ * This in-process offset is not persisted. A fork seed can already contain
501
+ * the child's inherited marker and synthetic closers, so its child-owned
502
+ * history starts at {@link inheritedEventCount}, before this offset. A
503
+ * resumed Session's seed contains its full stored log, while its inherited
504
+ * count keeps the durable fork cut. Consumers needing complete canonical
505
+ * history start at seq 0.
506
+ */
507
+ readonly firstLiveSeq: SessionLogOffset;
508
+ /**
509
+ * First event produced for this object lifecycle. A new fork includes its
510
+ * child-owned seed marker and closers; a restored Session starts after its
511
+ * complete stored prefix. This in-process capture offset is not persisted.
512
+ */
513
+ readonly firstLifecycleSeq: SessionLogOffset;
514
+
515
+ /**
516
+ * Create a detached session by validating and snapshotting borrowed seed
517
+ * events and storage metadata.
518
+ * @param id - session identity.
519
+ * @param seed - optional borrowed replay or fork events.
520
+ * @param header - optional borrowed storage metadata.
521
+ * @param inheritedEventCount - exact fork-inherited prefix length for a seeded header.
522
+ * @param projections - pure interpreters for plugin-owned message changes.
523
+ * @returns a detached session.
524
+ * @throws when a seed event requires a missing message interpreter or fails validation.
525
+ */
526
+ static create(
527
+ id: SessionId,
528
+ seed?: readonly SessionEvent[],
529
+ header?: SessionHeader,
530
+ inheritedEventCount?: SessionLogOffset,
531
+ projections?: readonly SessionMessageProjection[],
532
+ ): Session;
533
+ /**
534
+ * Restore a detached session by adopting an independently owned or deeply frozen seed.
535
+ * Runtime-required event fields, event envelopes, sequence continuity, surface
536
+ * transitions, and header fields are validated without copying or freezing events.
537
+ * Embedded Assistant streams remain opaque until a stream consumer or storage
538
+ * verifier reads them.
539
+ * @param id - restored session identity.
540
+ * @param seed - independently owned or deeply frozen events.
541
+ * @param header - independently owned storage metadata.
542
+ * @param inheritedEventCount - exact fork-inherited prefix length decoded from storage.
543
+ * @param eventState - aliasing state carried from the operation that produced the seed.
544
+ * @param projections - pure interpreters for plugin-owned message changes.
545
+ * @returns a restored detached session.
546
+ * @throws when a seed event requires a missing message interpreter or fails validation.
547
+ */
548
+ static fromRestore(
549
+ id: SessionId,
550
+ seed: readonly SessionEvent[],
551
+ header: SessionHeader,
552
+ inheritedEventCount: SessionLogOffset,
553
+ eventState: SessionSeedEventState,
554
+ projections?: readonly SessionMessageProjection[],
555
+ ): Session;
556
+ /**
557
+ * Return the immutable event stored at one exact sequence number.
558
+ * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited.
559
+ * See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md).
560
+ * @param seq - event sequence number.
561
+ * @returns the accepted event, or undefined when the log does not contain it.
562
+ */
563
+ eventAt(seq: SessionSeq): SessionEvent | undefined;
564
+ /**
565
+ * Materialize an immutable snapshot of a half-open event sequence range.
566
+ * A full current snapshot is reused until the next append; every previously
567
+ * returned snapshot remains stable after later appends.
568
+ * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited.
569
+ * See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md).
570
+ * @param fromSeq - non-negative inclusive sequence number; defaults to the log start.
571
+ * @param toSeqExclusive - non-negative exclusive sequence number; defaults to the current end.
572
+ * @returns a frozen array of the selected deeply frozen events.
573
+ */
574
+ snapshotEvents(
575
+ fromSeq: SessionLogOffset = SessionLogOffset(0),
576
+ toSeqExclusive: SessionLogOffset = this.seq,
577
+ ): readonly SessionEvent[];
578
+ /**
579
+ * Return this Session's events after its fork-inherited prefix.
580
+ * @deprecated Existing logic may remain unmigrated for now, but new calls are prohibited.
581
+ * See the [Agent Note](../../../../.agents/notes/implemented/architecture/2026-09-09-deprecate-synchronous-session-event-reads.md).
582
+ * @returns a fresh array containing child-owned events in log order.
583
+ */
584
+ ownEvents(): readonly SessionEvent[];
585
+ /**
586
+ * Whether one existing event position is outside the fork-inherited prefix.
587
+ * @param seq - event position in this Session.
588
+ * @returns true when the event belongs to this Session rather than its parent.
589
+ */
590
+ isOwnSeq(seq: SessionSeq): boolean;
591
+ /** The next event's sequence number — always the log length (the `seq = log.length` contiguity contract). */
592
+ get seq(): SessionLogOffset;
593
+ /** Current event while synchronously notifying a batch's observers. */
594
+ get currentBatchPublication(): SessionEvent | undefined;
595
+ /**
596
+ * Append consecutive events together after validating every candidate and dispatch.
597
+ * Observers read the complete committed log and final message surface.
598
+ * Listener registration changes during publication take effect on the next append.
599
+ * Persistence listeners retain their own durability rules.
600
+ * @param inputs - event payloads and surface intents in commit order. An empty array records no events.
601
+ * @returns immutable committed events in the same order.
602
+ * @throws if the Session cannot accept writes, including an empty append, or any candidate or synchronous dispatch validation fails.
603
+ */
604
+ append(inputs: readonly SessionEventInput[]): readonly SessionEvent[];
605
+ /**
606
+ * Append one typed event to the log and synchronously notify observers via
607
+ * the store-owned, module-private publication hooks. The hot path never blocks
608
+ * on I/O — persistence plugins buffer asynchronously. Once the event enters
609
+ * the log, the append is committed: observer failures are logged and
610
+ * contained per listener, so they do not change the return value or prevent
611
+ * later listeners from observing the same accepted event.
612
+ * Independent Sessions append without notifying store observers. Once a store
613
+ * prepares or enters the instance, writes require a completed creation
614
+ * announcement and remain disabled after removal.
615
+ *
616
+ * @param type - The event type (key of {@link SessionEventMap}).
617
+ * @param data - The event payload; must be JSON-serializable.
618
+ * @param opts - Surface metadata: `surfaceOp` controls how the event enters
619
+ * the ordered surface; `sourceEventSeqs` lists the seq numbers of earlier
620
+ * events this one derives from. REQUIRED for
621
+ * {@link SurfaceEventType} events (every message-producing event must
622
+ * declare how it joins the surface, the sole source of derived model
623
+ * history) and
624
+ * rejected by the compiler for non-surface types like `turn/start` or
625
+ * `assistant/attempt`. Assistant messages embed their exact provider
626
+ * stream and cannot cite top-level source events.
627
+ * @returns the logged event — its assigned `seq`/`time` plus the SNAPSHOT of
628
+ * `data` that entered the log, so reading `event.data` back sees the logged
629
+ * value, never the caller's still-mutable input.
630
+ * @throws if a store-owned Session is unentered or its creation announcement
631
+ * has not completed, or if `data` or surface
632
+ * metadata is not losslessly JSON-serializable
633
+ * (BigInt, function, symbol, undefined, negative zero, non-finite number,
634
+ * circular reference, sparse array, or an exotic object such as
635
+ * Map/Set/Date/class instance), or when the candidate violates the
636
+ * request-header empty-field or tool-error consistency rules, or the
637
+ * canonical surface contract (marker shape and eligibility, unique
638
+ * earlier source-event references, positional replacement validity, and complete
639
+ * shadowed-node coverage). One iterative pass reads, validates, and
640
+ * copies each nested value once, so a stateful getter cannot supply one value
641
+ * to validation and another to storage. The event log is the durable source
642
+ * of truth, so a bad event fails at the append site rather than later during
643
+ * a backend flush. A synchronous internal dispatch validation failure or an
644
+ * append reentered while this acceptance/publication boundary is open also
645
+ * rejects before the log changes.
646
+ */
647
+ append<T extends SessionEventType>(
648
+ type: T,
649
+ data: SessionEventMap[T],
650
+ ...opts: T extends SurfaceEventType ? [opts: SurfaceIntent<T>] : []
651
+ ): SessionEvent<T>;
652
+ /**
653
+ * The {@link EpochHeader} in force after the log's last header event — the
654
+ * header the NEXT request will be compared against — or undefined before
655
+ * the first `request/header` snapshot. The live, incrementally-maintained
656
+ * form of `foldRequestHeader(session.snapshotEvents())`: each header event is folded
657
+ * once, when first seen, so a per-step read costs O(new events).
658
+ * @returns the folded header, or undefined when no header event exists yet.
659
+ */
660
+ requestHeader(): EpochHeader | undefined;
661
+ /**
662
+ * Return the latest resolved route metadata, or `undefined` before the first
663
+ * `request/context` event. Each event is folded once.
664
+ * @returns the latest immutable route metadata.
665
+ */
666
+ requestContext(): RequestContext | undefined;
667
+ /**
668
+ * Fold unseen committed events into capability-independent tool history.
669
+ * Initial access reconstructs inherited history; later reads consume only new events.
670
+ * @returns an immutable snapshot for LLM request projection, including historical addition definitions.
671
+ */
672
+ toolHistory(): ToolHistory;
673
+ /**
674
+ * Derive the LLM message history by walking the ordered sequences of
675
+ * message-producing events maintained by `surfaceOp` markers. The
676
+ * surface is the single source of derived history: every message-producing
677
+ * append records its `surfaceOp`, so a raw event with no marker (a chunk, a
678
+ * turn boundary) is correctly absent, and a compaction `replace` deletes the
679
+ * shadowed nodes from the derivation. The projection rules are
680
+ * {@link deriveEventMessage}, with logged message projections applied
681
+ * without changing node membership or message identity.
682
+ *
683
+ * CACHED: pure tail growth costs O(new nodes); a replacement or message projection
684
+ * ({@link SessionSurface.contentGeneration}) rebuilds. The returned array is
685
+ * a fresh snapshot per call (later appends never grow an array a caller
686
+ * already holds); the `Message` objects in it are SHARED and **deep-frozen**.
687
+ * Unchanged content reuses frozen event data; projected blocks are frozen
688
+ * derived copies. Consumers cannot mutate the log through either form.
689
+ * @returns a fresh array of the shared, frozen derived history.
690
+ */
691
+ deriveMessages(): Message[];
692
+ /**
693
+ * Project one event with all committed message projections applied.
694
+ * The original durable event remains unchanged.
695
+ * @param event - the event to project.
696
+ * @returns the derived message, or null when the event produces none.
697
+ */
698
+ deriveEventMessage(event: SessionEvent): Message | null;
699
+ }
700
+ ```
701
+
702
+ ## 派生历史:`deriveMessages()` 与 `deriveEventMessage()`
703
+
704
+ `Session.deriveMessages()` 将事件日志投影为模型看到的 `Message[]`。它是缓存的(每个 surface 节点在首次出现时投影一次;surface 重写触发重建)且冻结的(每次调用返回一个新数组,引用共享的深冻结消息,因此通过投影修改已记录的历史在类型上不可表达)。`deriveEventMessage(event)` 是折叠所应用的逐节点纯函数,公开暴露以便外部重建器能以完全相同的规则投影日志前缀,不会与缓存产生分歧。投影规则:
705
+
706
+ - `user/message` → 一条携带确切 `content` 的 user 消息;可选 envelope 仅作为日志中的展示元数据保留。
707
+ - `assistant/message` → 一条 assistant 消息,包含生成它的提供方和模型,以及可选的适配器私有回放状态。其嵌入式紧凑 stream 是回放、usage 与 UI 证据,而不是第二条 message。**内容为空的** `assistant/message` 也会跳过:因 max-tokens 而截断且无内容的步骤仍会记录一条 `assistant/message` 来保存 stream、usage、提供方和模型,但无内容的 assistant 轮次不得进入提供方 transcript(文本记录)。
708
+ - `tool/result` → 一条一等 tool-role 消息,携带结果内容与工具调用标识。
709
+ - `user/message`(注入上下文,即非 `user` 来源)→ 按时间顺序在相应位置生成一条 user-role 消息,并原样承载其 `content`;其类型化 source 标明生产方,并携带所有生产方专用数据。
710
+
711
+ 其余所有事件(`turn/*`、`step/*`、`assistant/attempt`、插件所属的 `llm/retry`)均为结构信息,不会投影为消息。token 记账会展开每个 `assistant/message` 或 `assistant/attempt` 的嵌入式 stream,message 顶层 `usage` 存在时仍是已提交 message 的权威。失败的模型请求 attempt 因此可以保留提供方 usage,而无需虚构 assistant message。当前逻辑校验会拒绝没有提供方/模型的 request header 和 assistant 消息,而不会猜测路由;受支持的历史表示会在当前 Session 存在前,由其相邻格式迁移边归一化并校验。
712
+
713
+ ## 活跃会话 fork API
714
+
715
+ `ctx.sessions.create(id, { seed, meta })` 是底层的回放/fork 原语。对于普通的活跃会话 fork,`SessionStore` 暴露一个策略 API:
716
+
717
+ - `fork(source, boundary?, childSessionId?)` 接受一个活跃的 `Session` 对象或活跃的 `SessionId`,选取到 `SessionSeq` boundary(含)为止的源事件(默认为当前最后一个事件),闭合任何开放尾部,然后创建一个独立的活跃会话,包含深克隆的 seed event、`isSeeded: true`、精确 `inheritedEventCount` 及继承的 `cwd`,不含 `parentSession`。
718
+
719
+ 显式 `boundary` 允许调用者从任意稳定的轮次间位置 fork,包括之前的 `turn/end` 或更晚的独立纯日志事件,即使源会话有更新的事件或正在进行的轮次。API 拒绝结束于开放轮次内的前缀,而不是静默截断。`dsh-subagent-fork-in-process` 保留其已完成前缀截断逻辑,因为工具调用时的委托通常在父轮次仍然打开时启动;普通的会话分支应显式指定请求的 boundary。
720
+
721
+ <a id="why-a-turn-ended-turnendreasonmap"></a>
722
+
723
+ ## 轮次的结束原因:`TurnEndReasonMap`
724
+
725
+ `turn/start` 没有 trigger 字段。已进入的 `user/message` 批次记录进入每个步骤的内容,`llm/retry` 记录请求恢复,idle 注入则保持待处理,直到唤醒交付抵达后续 pre-step。实时轮次会保留停止驱动器的类型化 [`AgentCancelCause`](./core.md#the-agent-handle);只有在导入受支持的粗粒度取消记录且记录未保存调用方时,持久化才使用额外的 `{ kind: 'legacy' }` 原因。
726
+
727
+ ```ts type-equiv
728
+ /** Durable cancellation cause, including imports whose original coarse record carried no cause. */
729
+ type TurnEndCancelCause = AgentCancelCause | { readonly kind: 'legacy' }
730
+ ```
731
+
732
+ ```ts type-equiv
733
+ /**
734
+ * Why a turn ended. Merge-extensible sum type.
735
+ */
736
+ interface TurnEndReasonMap {
737
+ completed: { kind: 'completed' }
738
+ /** A cancellation request interrupted the live turn. */
739
+ aborted: { kind: 'aborted'; reason: TurnEndCancelCause }
740
+
741
+ blocked: { kind: 'blocked' }
742
+ /**
743
+ * The turn failed. `error` is always a structured failure: the `LlmError`
744
+ * facts verbatim, or `{ message: errorChain(error), code }` flattened from
745
+ * any other error, where `code` is `TOOL_ACTIVATION_FAILED` for a failed
746
+ * tool condition and `UNKNOWN` otherwise.
747
+ */
748
+ error: { kind: 'error'; error: LlmFailure }
749
+ /** At least one step reached its output-token ceiling, even if a plugin continued the turn. */
750
+ 'max-tokens': { kind: 'max-tokens' }
751
+ /**
752
+ * A crash-orphaned turn was closed after the fact: agent-loop resume appends
753
+ * this closer for a stored log whose last turn never ended, and session-query
754
+ * synthesizes it on cold reads. The loop never emits this marker live, and
755
+ * the events recorded before the crash remain intact.
756
+ */
757
+ interrupted: { kind: 'interrupted' }
758
+ /**
759
+ * Fork-seed construction closed a turn that was still open at the fork
760
+ * boundary in the source session. Only fork seeds carry this marker — the
761
+ * loop never emits it — and the source events before the boundary remain
762
+ * intact in the child.
763
+ */
764
+ forked: { kind: 'forked' }
765
+ }
766
+ ```
767
+
768
+ `max-tokens` 与模型调用中同名的 `FinishReason` 对应:只要轮次内有任何步骤以 `max-tokens` 结束,整个轮次就以 `max-tokens` 而不是 `completed` 结束(即使之后继续执行,截断事实仍优先),让消费方能够区分正常停止和截断停止。取消和错误仍是不同的结果。loop 不会实时发出 `interrupted` 或 `forked`:崩溃恢复合成 `interrupted`(见 [persistence.md](./persistence.md)),而 fork 种子构造合成 `forked`。该 map 可通过合并扩展。
769
+
770
+ ## 执行封闭与独立事件
771
+
772
+ 一个轮次包围一次模型循环执行,而不是整个会话日志。AgentLoop 只会在轮次内进入 pre-step 批次时记录注入的 `user/message` 事件;插件所属的纯日志事件仍可出现在 `turn/end` 与下一个 `turn/start` 之间,占用事件 seq 但不递增轮次编号。持久化会将每个连续且已接受的事件纳入有界持久化批次,而崩溃修复只关闭确实仍处于开放状态的尾部轮次。需要即时持久性屏障的生产方会显式等待 `ctx.sessions.flush(session)`。
773
+
774
+ 可合并扩展事件的关系由声明它的插件拥有,因此核心不会仅因没有开放轮次就拒绝未知事件。见[独立事件决策](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/simplification/2026-07-28-remove-synthetic-log-only-turns.zh.md)。
775
+
776
+ ## 种子结束边界:`session/end-seed`
777
+
778
+ `buildForkSeed` 在精确的 inherited-prefix cut 追加 `session/end-seed { inherited: true }`,然后添加合成的 fork 结束事件。`Session` constructor 保留该预先构造的 marker,或在 seed 仅含 inherited prefix 时追加 marker。restore 保留 tagged marker,并且只在完整 stored seed 尚未以 marker 结尾时追加普通 `session/end-seed {}`。两种形式都只进入 log 且不产生 message;只有 constructor 和 `buildForkSeed` 可以创建它们。
779
+
780
+ 对于 fork lineage,定位 payload 携带 `inherited: true` 的最后一个 marker;当前格式 decoding 只在 `SessionHeader.isSeeded` 为 true 时要求该 marker,并从其 seq 推导 `inheritedEventCount`。对于 lifecycle ownership,定位任一形式的最后一个 `session/end-seed`。重新打开已经以任一 marker 结尾的 seed 时,不会再追加普通 marker。
781
+
782
+ 它之所以必要,是因为种子历史与实时工作在字节层面完全相同,这会让任何拥有独立开/闭括号的插件失效:一个未配对的 `compaction/start`,无论写入方是在压缩中途崩溃、还是此刻正在压缩,读起来都一样。在 `session/end-seed` 之前的开启标记来自构造种子,并且属于一个已结束的生命周期,无论结束原因为何(崩溃、进程接替,或从仍在运行的父会话 fork 出来),因此其所有方可以视之为已死。这只覆盖*本*会话继承的括号:另一个并发存活的会话可能在同一段历史上持有开放括号,而它自己的边界在别处,因此容忍并发写入方还需要日志之外的存活信号。核心写入该边界但不从中读取任何内容——括号的词汇表仍归其所属插件,这也正是崩溃修复只关闭轮次/步骤/工具边界而从不处理 `compaction/*` 的原因。
783
+
784
+ 按真人活动排序 Session 的消费方会排除该边界:接手 Session 不算工作,因此按日志尾部排序会把每个打开过的 Session 顶到最前。
785
+
786
+ ## 插件贡献的仅日志事件
787
+
788
+ 插件可以通过 declaration merging 添加额外的 `SessionEventMap` 类型。这些是**仅日志**事件:不是 `SurfaceEventType`(不携带 `surfaceOp`,不参与派生历史)。事件所有方决定它们属于一个开放的执行轮次,还是可以独立位于轮次之间。生成的[持久化日志事件目录](../persistence-catalog.md)会列出每个核心或插件贡献的事件;压缩 seam 的 `compaction/*` 语义在 [compaction.md](./compaction.md) 中讨论。
789
+
790
+ 如果同一个插件事件族中的多条事件要组装成一个 Web Client Conversation Node,该事件族中的每条 start、update、result、resource 或 interruption 事件都必须携带或独立推导出同一个稳定业务 id。此要求只约束需要关联的 Node 事件族,并不要求每条 Session 事件都有业务 id;Client 因此无须根据相邻关系猜测归属,也无须扫描历史。参见 [Conversation 子系统](./conversation.md)。
791
+
792
+ 钩子桥接层的 `hook/invoked` / `hook/result` 对(来自 `@deepseek-ai/dsh-hook-protocol`)通过 `handlerId` 关联。`UserPromptSubmit`、`PreToolUse`、`PostToolUse` 与 `Stop` 在 loop 已打开的轮次内触发,因此其 `hook/*` 记录天然位于轮次之内。`SessionStart` 不生成 `hook/*` 记录,因为它在轮次 1 之前运行;其上下文会在 inbox 中保持待处理,直到唤醒交付打开一个轮次。
793
+
794
+ ### 插件记录
795
+
796
+ 实验性包在 `PluginRecordMap` 中声明插件自有的状态:`appendPluginRecord()` 根据每项声明约束名称与 payload 类型,并在 `plugin:` 命名空间中追加一条带 `ignorable: true` 标记的事件。`pluginRecordOf()` 读取已存记录,其 payload 为未知类型,不受当前声明约束。[实验性持久化目录](../experimental-persistence-catalog.md)列出当前声明。调用方限制、保留方式与读取路径见[包 README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/README.zh.md#write-experimental-plugin-records)。
797
+
798
+ ```ts type-equiv
799
+ /**
800
+ * Event type of an experimental plugin record: `plugin:` followed by
801
+ * slash-separated segments of lowercase letters, digits, `.`, `_`, and `-`,
802
+ * each starting with a letter or digit. A record type is never a
803
+ * {@link SessionEventMap} member.
804
+ */
805
+ type PluginRecordType = `plugin:${string}`
806
+ ```
807
+
808
+ ```ts type-equiv
809
+ /**
810
+ * Payloads written by experimental packages, keyed by their `plugin:` record
811
+ * names. Packages augment this map to type writes and enter the current plugin
812
+ * record catalog; these declarations do not enter {@link SessionEventMap} or
813
+ * released persistence schemas. Stored records still require owner validation.
814
+ */
815
+ interface PluginRecordMap {}
816
+ ```
817
+
818
+ ```ts type-equiv
819
+ /**
820
+ * One committed experimental plugin record, read from the log by
821
+ * `pluginRecordOf`. The record's owner validates `data` before use, because a
822
+ * restored record carries whatever JSON an earlier build of its owner wrote,
823
+ * or a V3 event that a format migration renamed into the `plugin:` namespace held.
824
+ */
825
+ interface PluginRecord {
826
+ /** The record type, chosen by the owning plugin. */
827
+ readonly type: PluginRecordType
828
+ /** The record's position in the Session log. */
829
+ readonly seq: SessionSeq
830
+ /** Unix epoch milliseconds at append. */
831
+ readonly time: number
832
+ /** The JSON payload as committed. */
833
+ readonly data: unknown
834
+ }
835
+ ```
836
+
837
+ ## 持久性约定
838
+
839
+ 持久化后端依赖的约定如下:持久日志无损保存每个事件,每个 Assistant attempt 都是一个 `assistant/message` 或 `assistant/attempt`,其嵌入式紧凑 stream 会保留原始带时间 chunk。`seq` 在这些 settlement 与所有交错事件之间保持连续。后端可以为事件批次选择自己的存储 framing,只要句柄的 `read()` 返回与追加时完全一致的事件即可;当前 JSONL 每个事件写一行(见 [persistence.md](./persistence.md))。所有 `event.data` 都必须可序列化为 JSON;`Session.append` 会从源头强制这一要求(遇到不可序列化数据时抛出),因此错误事件绝不会进入日志,`session.snapshotEvents()` 始终与后端可持久化的内容一致。新增会携带不可序列化数据、破坏核心执行嵌套或违反事件所有方声明关系的事件类型,都会构成磁盘格式的破坏性变更。
840
+
841
+ 消费此约定的后端见 [persistence.md](./persistence.md)。
842
+
843
+ ## Remote 目录与 workspace 打开
844
+
845
+ `ModelCatalog` 是 `session/modelCatalog` 返回的 Host generation 模型目录:它携带部署默认值、可路由 provider id、成功的 provider 分组与相互隔离的 provider 失败。它不由某个 Session 派生,因此与 Session projection 分开保存。
846
+
847
+ `SessionOpenWorkspacePathRequest` 携带绝对路径或已按 workspace 解析的 `path`。`SessionOpenWorkspacePathValue` 确认 Host 已接受原生交接。Session-aware Client 会在已知当前 Session cwd 时据此解析相对路径;controller 将路径原样交给打开器,并通过 Session Remote 错误词汇表报告无效请求、取消与打开器失败。 可选的 `action: "reveal"` 选择文件管理器导航;省略时使用默认应用打开。
848
+
849
+ 可选的 `application` 指定文件关联应用,不修改系统默认值;`workspacePathApplications({ path })` 在校验 Host 路径后返回当前关联应用、名称、图标和默认项。
850
+
851
+ ## Session 日志上传排除条件
852
+
853
+ `ctx.sessionLogDeepSeek.registerExclusion()` 注册归调用方所有的判断函数,输入为 `SessionLogUploadRequest`。任意条件匹配时,在读取 Session 事件前省略日志贡献与接受回调。没有条件匹配时保留普通上传资格判断。[包 README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/session/session-log-deepseek/README.zh.md#upload-exclusions) 定义失败与释放行为。
854
+
855
+ ```ts type-equiv
856
+ /** Request identity inspected before preparing a Session-log contribution. */
857
+ interface SessionLogUploadRequest {
858
+ /** Live Session whose canonical log would be contributed. */
859
+ readonly sessionId: SessionId
860
+ /** Auxiliary request classification; absent for ordinary conversation requests. */
861
+ readonly purpose?: string
862
+ }
863
+ ```
864
+
865
+ ```ts type-equiv
866
+ /**
867
+ * Synchronous, side-effect-free upload exclusion owned by its registering plugin.
868
+ * @param request - request identity; the predicate must not mutate it.
869
+ * @returns `true` to omit the log field and acceptance callback, or `false` to keep checking eligibility.
870
+ */
871
+ type SessionLogUploadExclusion = (request: SessionLogUploadRequest) => boolean
872
+ ```
873
+
874
+ <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
875
+
876
+ <a id="cordis-surface"></a>
877
+
878
+ ## Cordis API
879
+
880
+ 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).
881
+
882
+ <a id="ctxsessioncontroller--sessioncontroller"></a>
883
+
884
+ ### `ctx.sessionController` — `SessionController`
885
+
886
+ Host service backing the generated `ctx.remote.session` namespace.
887
+
888
+ ```ts cordis-catalog
889
+ /**
890
+ * Resolve or resume one ordinary Session for another Host API domain.
891
+ * @param sessionId - Session identity whose Agent owns the operation.
892
+ * @returns the live Agent or the stable Session-domain failure.
893
+ */
894
+ resolveAgent(sessionId: SessionId): Promise<ApiSessionAgentResult>
895
+
896
+ /**
897
+ * Read all visible Session rows without resuming an Agent.
898
+ * @param _request - reserved empty list request.
899
+ * @param signal - cancellation for persistence reads and summary generation.
900
+ * @returns visible Session summaries ordered by activity.
901
+ */
902
+ @Remote('list') async list(_request: SessionListRequest, signal: AbortSignal): Promise<SessionListValue>
903
+
904
+ /**
905
+ * Search visible Session content without resuming an Agent. A non-owner
906
+ * receives only the hits whose Session `allows()` admits; `hasMore`
907
+ * describes the bounded result before that filtering.
908
+ * @param request - literal message-content query.
909
+ * @param signal - cancellation for list and search reads.
910
+ * @returns authorized bounded Session search results.
911
+ */
912
+ @Remote('search') async search(request: SessionSearchRequest, signal: AbortSignal): Promise<SessionSearchValue>
913
+
914
+ /**
915
+ * Create one ordinary Session for a new conversation, reusing the named blank Session while it is reusable.
916
+ * @param request - optional blank Session to reuse, location, and Agent preset.
917
+ * @returns the created or reused Session identity and its preset when configured.
918
+ */
919
+ @Remote('create') create(request: SessionCreateRequest): Promise<SessionCreateValue>
920
+
921
+ /**
922
+ * Select one Session-local model after explicitly resuming the Session; save the default in the background.
923
+ * @param request - Session identity and requested model selection.
924
+ * @returns the normalized selection installed for the Session, without waiting for default persistence.
925
+ */
926
+ @Remote('selectModel') selectModel(request: SessionSelectModelRequest): Promise<SessionSelectModelValue>
927
+
928
+ /**
929
+ * Select the first available account model after login when no provider API key is configured.
930
+ * @returns after saving the first available model or retaining the existing default.
931
+ */
932
+ @Remote async initializeDefaultModel(): Promise<void>
933
+
934
+ /**
935
+ * Describe every currently routable model for Host-generation selectors.
936
+ * @returns provider-grouped models, the deployment default, and isolated provider failures.
937
+ */
938
+ @Remote('modelCatalog') modelCatalog(): Promise<ModelCatalog>
939
+
940
+ /**
941
+ * Report whether this deployment can hand a Session workspace path to a native desktop.
942
+ * @returns true when the matching open operation is available.
943
+ */
944
+ @Remote canOpenWorkspacePath(): boolean
945
+
946
+ /**
947
+ * Describe the serving desktop for authenticated file-action routes.
948
+ * @returns Host name, configured availability, and platform-specific file-manager behavior.
949
+ */
950
+ workspaceDesktop(): { name: string; available: boolean; fileManager: 'finder' | 'explorer' | 'directory' | null }
951
+
952
+ /**
953
+ * Verify one path through the composed filesystem and open it on the Host desktop.
954
+ * @param request - path after best-effort Session workspace resolution.
955
+ * @param signal - caller lifetime; abort terminates the native command.
956
+ * @returns confirmation after the native opener accepts the path.
957
+ * @throws RemoteError when the request is invalid, has no verified Host mapping, is cancelled, or the opener fails.
958
+ */
959
+ @Remote('openWorkspacePath') async openWorkspacePath( request: SessionOpenWorkspacePathRequest, signal: AbortSignal, ): Promise<SessionOpenWorkspacePathValue>
960
+
961
+ /**
962
+ * Query current file handlers on the serving desktop without activating an Agent.
963
+ * @param request - file path in Host filesystem syntax.
964
+ * @param signal - caller lifetime, propagated to filesystem and desktop queries.
965
+ * @returns OS application names, icons, and default selection; empty when desktop opening is unavailable.
966
+ * @throws RemoteError when the path is invalid, the query is cancelled, or native discovery fails.
967
+ */
968
+ @Remote('workspacePathApplications') async workspacePathApplications( request: { readonly path: string }, signal: AbortSignal, ): Promise<readonly SessionWorkspacePathApplication[]>
969
+
970
+ /**
971
+ * Rename one Session after explicitly resuming it.
972
+ * @param request - Session identity and proposed title.
973
+ * @returns the accepted title and durable event sequence.
974
+ */
975
+ @Remote('rename') rename(request: SessionRenameRequest): Promise<SessionRenameValue>
976
+
977
+ /**
978
+ * Fork one cold-readable exact event prefix into a new Session. An omitted
979
+ * boundary selects the latest completed-turn prefix; an open cut receives
980
+ * synthetic fork closers.
981
+ * @param request - source Session, optional exact inclusive event boundary, and migration preflight choice.
982
+ * @returns the new Session identity.
983
+ */
984
+ @Remote('fork') async fork(request: SessionForkRequest): Promise<SessionForkValue>
985
+
986
+ /**
987
+ * Admit one prompt after explicitly resuming its Session.
988
+ * @param request - Session identity, prompt content, source metadata, and delivery mode.
989
+ * @param signal - caller cancellation before prompt admission begins.
990
+ * @returns acknowledgement that the Agent accepted the prompt.
991
+ */
992
+ @Remote('prompt') async prompt(request: SessionPromptRequest, signal: AbortSignal): Promise<SessionPromptValue>
993
+
994
+ /**
995
+ * Read one image from the attachment store after gateway authorization.
996
+ * @param request - image reference and gateway permission scope; Session existence is not checked.
997
+ * @returns the durable attachment reference and base64-encoded bytes.
998
+ */
999
+ @Remote('attachment') attachment(request: SessionAttachmentRequest): Promise<SessionAttachmentValue>
1000
+
1001
+ /**
1002
+ * Mutate one still-pending queue occurrence, resuming a cold Agent first.
1003
+ * @param request - Session, queue item, and requested mutation.
1004
+ * @returns acknowledgement that the queue mutation was applied.
1005
+ */
1006
+ @Remote('updateQueue') updateQueue(request: SessionUpdateQueueRequest): Promise<SessionUpdateQueueValue>
1007
+
1008
+ /**
1009
+ * Cancel one active Agent turn without dropping its pending inbox.
1010
+ * @param request - Session whose active Agent turn is cancelled.
1011
+ * @returns acknowledgement that cancellation was requested.
1012
+ */
1013
+ @Remote('cancel') cancel(request: SessionCancelRequest): SessionCancelValue
1014
+
1015
+ /**
1016
+ * Read one cold-safe, message-aligned Session history page.
1017
+ * @param request - durable address, optional fixed cut, backward cursor, and page budget.
1018
+ * @param signal - cancellation for persistence reads.
1019
+ * @returns one chronological page with its inclusive cursor.
1020
+ */
1021
+ @Remote events(request: SessionPageRequest, signal: AbortSignal): Promise<SessionEventsPage>
1022
+
1023
+ /**
1024
+ * Subscribe to Session events without reading a snapshot.
1025
+ * @param request - Session address.
1026
+ * @param signal - subscription cancellation.
1027
+ * @returns readiness and subsequent events, without a snapshot.
1028
+ */
1029
+ @Remote({ mode: 'stream' }) observeEvent(request: SessionObserveEventRequest, signal: AbortSignal): AsyncIterable<SessionEventFrame>
1030
+
1031
+ /**
1032
+ * Subscribe to Session projection changes.
1033
+ * @param request - target Session.
1034
+ * @param signal - subscription cancellation.
1035
+ * @returns readiness followed by one projection change frame per committed event.
1036
+ */
1037
+ @Remote({ mode: 'stream' }) observeProjections(request: SessionProjectionsRequest, signal: AbortSignal): AsyncIterable<SessionProjectionFrame>
1038
+
1039
+ /**
1040
+ * Read passive Session state.
1041
+ * @param request - target Session.
1042
+ * @param signal - read cancellation.
1043
+ * @returns current passive state.
1044
+ */
1045
+ @Remote state(request: SessionStateRequest, signal: AbortSignal): Promise<SessionState>
1046
+
1047
+ /**
1048
+ * Subscribe to complete Session state replacements.
1049
+ * @param request - target Session.
1050
+ * @param signal - subscription cancellation.
1051
+ * @returns complete passive state replacements.
1052
+ */
1053
+ @Remote({ mode: 'stream' }) observeState(request: SessionStateRequest, signal: AbortSignal): AsyncIterable<SessionState>
1054
+
1055
+ /**
1056
+ * Wait for Session quiescence or pending input with bounded assistant output.
1057
+ * @param request - Session, wait bounds and output window.
1058
+ * @param signal - caller cancellation.
1059
+ * @returns stopping state and bounded assistant output.
1060
+ */
1061
+ @Remote wait(request: SessionWaitRequest, signal: AbortSignal): Promise<SessionWaitValue>
1062
+
1063
+ /**
1064
+ * Follow one Session log from its opening or resume cursor.
1065
+ * @param request - durable address and last committed sequence already held by the caller.
1066
+ * @param signal - cancellation owned by the Remote stream carrier.
1067
+ * @returns a complete opening snapshot followed by gap-free durable event
1068
+ * frames and optional cursorless assistant-stream frames.
1069
+ */
1070
+ @Remote({ mode: 'stream' }) follow(request: SessionFollowRequest, signal: AbortSignal): AsyncIterable<SessionFollowFrame>
1071
+
1072
+ /**
1073
+ * Read exact projections, returning cached hints only when migration is required.
1074
+ * @param request - Session whose current values are required.
1075
+ * @param signal - cancellation for the Session read.
1076
+ * @returns a sequenced baseline, cached hints when migration is deferred, or null when absent.
1077
+ */
1078
+ @Remote('projections') projections(request: SessionProjectionsRequest, signal: AbortSignal): Promise<SessionProjectionsValue>
1079
+
1080
+ /**
1081
+ * Stream a complete live-control baseline followed by replacement frames.
1082
+ * @param signal - cancellation owned by the Remote stream carrier.
1083
+ * @returns one complete baseline followed by live replacement frames.
1084
+ */
1085
+ @Remote({ mode: 'stream' }) async *control(signal: AbortSignal): AsyncIterable<SessionControlFrame>
1086
+ ```
1087
+
1088
+ Types: [SessionId](./core.md) · [SessionSearchRequest](./session-query.md)
1089
+
1090
+ Source: [`packages/api/session-controller/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/index.ts)
1091
+
1092
+ <a id="ctxsessionlogdeepseek--sessionlogdeepseekservice"></a>
1093
+
1094
+ ### `ctx.sessionLogDeepSeek` — `SessionLogDeepSeekService`
1095
+
1096
+ Incremental Session-log contributor with caller-owned upload exclusions.
1097
+
1098
+ ```ts cordis-catalog
1099
+ /**
1100
+ * Exclude matching requests before reading or preparing their log contribution.
1101
+ * Any matching rule suppresses both the field and its acceptance callback. With
1102
+ * no matching rules, normal upload eligibility applies. A throwing rule fails
1103
+ * this contribution; the request-extension registry omits it and reports the error.
1104
+ * @param exclude - synchronous, side-effect-free predicate; `true` skips this upload.
1105
+ * @returns effect disposer that removes this registration when called or when the caller unloads.
1106
+ */
1107
+ registerExclusion(exclude: SessionLogUploadExclusion): () => Promise<void>
1108
+ ```
1109
+
1110
+ Source: [`packages/session/session-log-deepseek/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/session/session-log-deepseek/src/index.ts)
1111
+
1112
+ <a id="ctxsessions--sessionstore"></a>
1113
+
1114
+ ### `ctx.sessions` — `SessionStore`
1115
+
1116
+ In-memory session store (`ctx.sessions`).
1117
+
1118
+ Persistence is intentionally not implemented here — the agent lifecycle attaches a session-log writer to each published session's write handle; a session published outside that lifecycle persists nothing.
1119
+
1120
+ ```ts cordis-catalog
1121
+ /**
1122
+ * Register one event interpreter for live creation, restore, and fork.
1123
+ * Disposing the contribution makes sessions that used it refuse further derivation.
1124
+ * @param projection - pure definition owned by the event's plugin.
1125
+ * @returns the fiber-owned disposer.
1126
+ * @throws when another definition already owns this event type.
1127
+ */
1128
+ registerMessageProjection(projection: SessionMessageProjection): () => Promise<void>
1129
+
1130
+ /**
1131
+ * Create a session owned by the calling fiber: disposing that fiber stops
1132
+ * event notification and removes the session from the store. `options.seed`
1133
+ * populates the session with a copy of those events (replay/fork);
1134
+ * `options.meta` attaches creation metadata (validated absolute `cwd`, seed
1135
+ * and parent lineage, and delegation depth) as the immutable
1136
+ * {@link SessionHeader} (the store fills `version`/`id`/`createdAt`).
1137
+ *
1138
+ * For an agent whose session must be torn down IN ORDER with its loop (so the
1139
+ * loop's final events are published before the store attachment ends), do NOT use this
1140
+ * — fold the session lifecycle into the agent's own effect via
1141
+ * {@link prepare} + {@link enter} + {@link announce} (see
1142
+ * `dsh-agent-loop`'s creation transaction).
1143
+ *
1144
+ * @param id - the session id; omitted, the store mints `session-<n>`.
1145
+ * @param options - seed events and/or creation metadata for the header.
1146
+ * @returns the live session, already entered and announced.
1147
+ * @throws if a session with `id` already exists, metadata is not a plain
1148
+ * lossless-JSON record with valid scalar fields, or `meta.cwd` is a
1149
+ * non-absolute path (storage backends key directories off it).
1150
+ */
1151
+ create(id?: SessionId, options?: CreateSessionOptions): Session
1152
+
1153
+ /**
1154
+ * Build a session WITHOUT entering it into the store — validate the id/cwd and
1155
+ * construct the {@link Session} (with its immutable {@link SessionHeader}).
1156
+ * Pairs with {@link enter} + {@link announce}: a caller that owns a composite
1157
+ * `ctx.effect` (the agent factory) folds the session lifecycle into that ONE
1158
+ * effect so a fiber unload tears the session + agent down as a single ORDERED
1159
+ * chain rather than as racing sibling effects — which would remove the publication hooks
1160
+ * before the driver's closing events commit, dropping them.
1161
+ *
1162
+ * @param id - the session id; omitted, the store mints `session-<n>`.
1163
+ * @param options - seed events and/or creation metadata for the header. With
1164
+ * `eventState`, every seed event is either independently owned or any
1165
+ * shared value is deeply frozen; {@link Session.fromRestore} validates and
1166
+ * adopts those values without copying or freezing them.
1167
+ * @returns the store-owned session, not yet entered and read-only until announced.
1168
+ * @throws if a session with `id` already exists, metadata is not a plain
1169
+ * lossless-JSON record with valid scalar fields, or `meta.cwd` is a
1170
+ * non-absolute path.
1171
+ */
1172
+ prepare(id?: SessionId, options?: PrepareSessionOptions): Session
1173
+
1174
+ /**
1175
+ * Enter a session into the store: install the module-private
1176
+ * append publication hooks and add it to the store. Writes remain disabled
1177
+ * until {@link announce} completes successfully. Returns the DETACH
1178
+ * disposer (hooks + store removal). Does NOT emit `session/created` —
1179
+ * the caller yields this disposer inside its effect and THEN calls
1180
+ * {@link announce}, so a throwing `session/created` listener rolls the attach
1181
+ * back instead of leaking it.
1182
+ *
1183
+ * Re-checks the id for a duplicate: `prepare` and `enter` are public
1184
+ * cross-package primitives and a caller may interleave arbitrary work (or
1185
+ * another create) between them, so a stale prepared session must NOT overwrite
1186
+ * a live store entry of the same id — its detach disposer would later delete
1187
+ * the REAL session. The {@link create} convenience and the agent factory call
1188
+ * the two back-to-back so they never trip this, but the public API cannot
1189
+ * assume that.
1190
+ *
1191
+ * @param session - an unentered session; independent instances become store-owned.
1192
+ * @returns the detach disposer (publication hooks + store removal). When called from
1193
+ * a synchronous `session/created` listener, removal and disposal wait until
1194
+ * that creation dispatch unwinds.
1195
+ * @throws if a session with this id is already in the store.
1196
+ */
1197
+ enter(session: Session): () => void
1198
+
1199
+ /** Emit `session/created` exactly once for an {@link enter}ed session (with
1200
+ * the carrier {@link enter} captured). Separate from {@link enter} so the
1201
+ * caller can yield the detach disposer first (rollback safety — see
1202
+ * {@link enter}). The Session remains read-only throughout synchronous
1203
+ * dispatch; successful completion enables appends. A failed announcement
1204
+ * leaves it read-only and cannot be retried.
1205
+ * @param session - the entered session to announce to listeners.
1206
+ * @throws if the session is not live or its announcement already began,
1207
+ * including a reentrant call from a creation listener. */
1208
+ announce(session: Session): void
1209
+
1210
+ /**
1211
+ * Dispatch the awaited `session/flush` durability checkpoint for `session`,
1212
+ * with the carrier captured at {@link enter}. THE flush entry point: the
1213
+ * store owns the carrier, so callers (the checkpoint policy's per-request
1214
+ * barrier, goal-round-driver's idle checkpoint, teardown drains, and consumers
1215
+ * that flush themselves before reading storage) must come through here
1216
+ * rather than dispatch a raw `ctx.parallel('session/flush', …)` — one owner
1217
+ * and one spelling.
1218
+ * @param session - the session whose buffered events must reach durable storage.
1219
+ * @returns whether at least one durability listener participated, after every
1220
+ * listener has settled successfully.
1221
+ * @throws the first registered listener failure after every listener settles.
1222
+ */
1223
+ async flush(session: Session): Promise<boolean>
1224
+
1225
+ /**
1226
+ * Look up a live session.
1227
+ * @param id - the session id to look up.
1228
+ * @returns the session, or undefined when no live session has that id.
1229
+ */
1230
+ get(id: SessionId): Session | undefined
1231
+
1232
+ /**
1233
+ * All live sessions, in creation order.
1234
+ * @returns a fresh array; mutating it does not affect the store.
1235
+ */
1236
+ list(): Session[]
1237
+
1238
+ /**
1239
+ * Create an independent live session from an exact prefix of a live source.
1240
+ * `boundary` is an inclusive source event seq; omitted means the source's
1241
+ * current last event. An open tail receives synthetic tool results and
1242
+ * step/turn closers with the forked cause. Closed steps and turns remain
1243
+ * unchanged, including any failed tool calls already missing results.
1244
+ * `inheritedEventCount` counts only copied source events, excluding these closers.
1245
+ *
1246
+ * @param source - Live source session object or id.
1247
+ * @param boundary - Inclusive source event seq to fork through; omitted means
1248
+ * the source's current last event, and omitted on an empty source forks an
1249
+ * empty child.
1250
+ * @param childSessionId - Optional child session id; omitted delegates to
1251
+ * `SessionStore`'s id policy.
1252
+ * @returns The created live child session.
1253
+ */
1254
+ fork(source: SessionForkSource, boundary?: SessionSeq, childSessionId?: SessionId): Session
1255
+ ```
1256
+
1257
+ Types: [CreateSessionOptions](./persistence.md) · [PrepareSessionOptions](./persistence.md) · [SessionId](./core.md)
1258
+
1259
+ Source: [`packages/core/session/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/index.ts)
1260
+
1261
+ <a id="api-session-events"></a>
1262
+
1263
+ ### `api-session/*` events
1264
+
1265
+ <a id="api-sessionactivity--emit"></a>
1266
+
1267
+ #### `api-session/activity` — emit
1268
+
1269
+ One user-authored durable message advanced Session list activity.
1270
+
1271
+ ```ts cordis-catalog
1272
+ /**
1273
+ * One user-authored durable message advanced Session list activity.
1274
+ * @mode emit
1275
+ * @param sessionId - addressed Session identity.
1276
+ * @param updatedAt - durable message time used for list ordering.
1277
+ */
1278
+ 'api-session/activity'(sessionId: SessionId, updatedAt: number): void
1279
+ ```
1280
+
1281
+ Types: [SessionId](./core.md)
1282
+
1283
+ Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
1284
+
1285
+ <a id="api-sessionadded--emit"></a>
1286
+
1287
+ #### `api-session/added` — emit
1288
+
1289
+ A Session became visible or its Agent was created or disposed. Consumers upsert the summary and replace its current running and availability state.
1290
+
1291
+ ```ts cordis-catalog
1292
+ /**
1293
+ * A Session became visible or its Agent was created or disposed.
1294
+ * Consumers upsert the summary and replace its current running and availability state.
1295
+ * @mode emit
1296
+ * @param summary - current list row for the Session.
1297
+ */
1298
+ 'api-session/added'(summary: SessionSummary): void
1299
+ ```
1300
+
1301
+ Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
1302
+
1303
+ <a id="api-sessionerror--emit"></a>
1304
+
1305
+ #### `api-session/error` — emit
1306
+
1307
+ One Agent failed outside a durable turn position.
1308
+
1309
+ ```ts cordis-catalog
1310
+ /**
1311
+ * One Agent failed outside a durable turn position.
1312
+ * @mode emit
1313
+ * @param sessionId - Agent and Session identity.
1314
+ * @param message - user-safe failure chain.
1315
+ */
1316
+ 'api-session/error'(sessionId: SessionId, message: string): void
1317
+ ```
1318
+
1319
+ Types: [SessionId](./core.md)
1320
+
1321
+ Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
1322
+
1323
+ <a id="api-sessionremoved--emit"></a>
1324
+
1325
+ #### `api-session/removed` — emit
1326
+
1327
+ A Session left the live Host registry.
1328
+
1329
+ ```ts cordis-catalog
1330
+ /**
1331
+ * A Session left the live Host registry.
1332
+ * @mode emit
1333
+ * @param sessionId - removed Session identity.
1334
+ */
1335
+ 'api-session/removed'(sessionId: SessionId): void
1336
+ ```
1337
+
1338
+ Types: [SessionId](./core.md)
1339
+
1340
+ Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
1341
+
1342
+ <a id="api-sessionstatus--emit"></a>
1343
+
1344
+ #### `api-session/status` — emit
1345
+
1346
+ One Agent changed running state.
1347
+
1348
+ ```ts cordis-catalog
1349
+ /**
1350
+ * One Agent changed running state.
1351
+ * @mode emit
1352
+ * @param sessionId - Agent and Session identity.
1353
+ * @param running - whether the Agent is running.
1354
+ */
1355
+ 'api-session/status'(sessionId: SessionId, running: boolean): void
1356
+ ```
1357
+
1358
+ Types: [SessionId](./core.md)
1359
+
1360
+ Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
1361
+
1362
+ <a id="session-events"></a>
1363
+
1364
+ ### `session/*` events
1365
+
1366
+ <a id="sessioncreated--emit"></a>
1367
+
1368
+ #### `session/created` — emit
1369
+
1370
+ Read-only creation announcement after store entry. New events reject until every synchronous listener has returned successfully. A synchronous throw vetoes creation and rolls back with a paired disposal; detach requested during dispatch is deferred. A returned-promise rejection is logged but cannot retroactively veto this synchronous boundary. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only sessions entered through that agent's context.
1371
+
1372
+ ```ts cordis-catalog
1373
+ /**
1374
+ * Read-only creation announcement after store entry.
1375
+ * New events reject until every synchronous listener has returned successfully.
1376
+ * A synchronous throw vetoes creation and rolls back with a paired disposal;
1377
+ * detach requested during dispatch is deferred.
1378
+ * A returned-promise rejection is logged but cannot retroactively veto this
1379
+ * synchronous boundary.
1380
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners
1381
+ * receive only sessions entered through that agent's context.
1382
+ * @param session - the session just entered and announced.
1383
+ * @mode emit
1384
+ */
1385
+ 'session/created'(this: Scoped<Session>, session: Session): void
1386
+ ```
1387
+
1388
+ Types: [Scoped](./scope.md)
1389
+
1390
+ Source: [`packages/core/session/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/index.ts)
1391
+
1392
+ <a id="sessiondisposed--emit"></a>
1393
+
1394
+ #### `session/disposed` — emit
1395
+
1396
+ Emitted once when an announced session leaves the store, including publication rollback, but never for an entry whose creation announcement did not begin. Listener failures are logged and contained. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`) reuses the owner scope.
1397
+
1398
+ ```ts cordis-catalog
1399
+ /**
1400
+ * Emitted once when an announced session leaves the store, including
1401
+ * publication rollback, but never for an entry whose creation announcement
1402
+ * did not begin. Listener failures are logged and contained.
1403
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`) reuses the owner scope.
1404
+ * @param session - the session that is no longer live in the store.
1405
+ * @mode emit
1406
+ */
1407
+ 'session/disposed'(this: Scoped<Session>, session: Session): void
1408
+ ```
1409
+
1410
+ Types: [Scoped](./scope.md)
1411
+
1412
+ Source: [`packages/core/session/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/index.ts)
1413
+
1414
+ <a id="sessionevent--emit"></a>
1415
+
1416
+ #### `session/event` — emit
1417
+
1418
+ Post-commit, fire-and-forget append feed. The listener snapshot resolves before the log push, but callbacks run after it; observer failures are logged and contained without making the committed append fail. Array append commits every member before publishing them in order; `session.seq` and `session.surface` already include the complete batch. `session.currentBatchPublication` identifies the member being delivered. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only events from sessions entered through that agent's context.
1419
+
1420
+ ```ts cordis-catalog
1421
+ /**
1422
+ * Post-commit, fire-and-forget append feed. The listener snapshot resolves
1423
+ * before the log push, but callbacks run after it; observer failures are
1424
+ * logged and contained without making the committed append fail.
1425
+ * Array append commits every member before publishing them in order;
1426
+ * `session.seq` and `session.surface` already include the complete batch.
1427
+ * `session.currentBatchPublication` identifies the member being delivered.
1428
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners
1429
+ * receive only events from sessions entered through that agent's context.
1430
+ * @param session - the session whose log grew.
1431
+ * @param event - the appended event, exactly as recorded.
1432
+ * @mode emit
1433
+ */
1434
+ 'session/event'(this: Scoped<Session>, session: Session, event: SessionEvent): void
1435
+ ```
1436
+
1437
+ Types: [Scoped](./scope.md)
1438
+
1439
+ Source: [`packages/core/session/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/index.ts)
1440
+
1441
+ <a id="sessionflush--parallel"></a>
1442
+
1443
+ #### `session/flush` — parallel
1444
+
1445
+ Awaited parallel durability checkpoint: every listener runs and the caller awaits all of them, with no waterfall veto. Scope-filtered dispatch (`@deepseek-ai/dsh-scope`) reuses the session's owner scope.
1446
+
1447
+ ```ts cordis-catalog
1448
+ /**
1449
+ * Awaited parallel durability checkpoint: every listener runs and the
1450
+ * caller awaits all of them, with no waterfall veto. Scope-filtered dispatch
1451
+ * (`@deepseek-ai/dsh-scope`) reuses the session's owner scope.
1452
+ * @param session - the session whose buffered events must reach durable storage.
1453
+ * @mode parallel
1454
+ */
1455
+ 'session/flush'(this: Scoped<Session>, session: Session): Promise<void> | void
1456
+ ```
1457
+
1458
+ Types: [Scoped](./scope.md)
1459
+
1460
+ Source: [`packages/core/session/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/index.ts)
1461
+
1462
+ <a id="timeline-events"></a>
1463
+
1464
+ ### `timeline/*` events
1465
+
1466
+ <a id="timelinepriority--emit"></a>
1467
+
1468
+ #### `timeline/priority` — emit
1469
+
1470
+ A saved user edit entered or left P0. Model task patches do not emit it.
1471
+
1472
+ ```ts cordis-catalog
1473
+ /**
1474
+ * A saved user edit entered or left P0. Model task patches do not emit it.
1475
+ * @mode emit
1476
+ * @param session - the session that owns the task.
1477
+ */
1478
+ 'timeline/priority'(session: Session): void
1479
+ ```
1480
+
1481
+ Source: [`packages/experimental/timeline/src/storage.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/experimental/timeline/src/storage.ts)
1482
+ <!-- END GENERATED cordis-surface -->