dsh-plugin-dev-kb 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.en.md +14 -8
  3. package/README.md +14 -8
  4. package/docs/banner.svg +25 -0
  5. package/kb/INDEX.md +6 -2
  6. package/kb/README.md +4 -4
  7. package/kb/extra/AGENTS.md +2 -2
  8. package/kb/extra/cookbook/adding-a-session-format-version.md +109 -0
  9. package/kb/extra/cookbook/adding-a-session-format-version.zh.md +109 -0
  10. package/kb/extra/cookbook/adding-a-vendored-package.md +1 -1
  11. package/kb/extra/cookbook/adding-a-vendored-package.zh.md +1 -1
  12. package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.md +1 -1
  13. package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.zh.md +1 -1
  14. package/kb/extra/deepseek-llm-api-wire-extensions.md +2 -2
  15. package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +2 -2
  16. package/kb/extra/development.md +13 -3
  17. package/kb/extra/development.zh.md +13 -3
  18. package/kb/extra/event-producer-consumer.md +33 -31
  19. package/kb/extra/event-producer-consumer.zh.md +34 -32
  20. package/kb/extra/i18n/README.md +2 -1
  21. package/kb/extra/i18n/README.zh.md +3 -2
  22. package/kb/extra/i18n/translation-prompt.md +1 -1
  23. package/kb/extra/module-graph.md +62 -24
  24. package/kb/extra/module-graph.zh.md +62 -24
  25. package/kb/extra/rescope.md +1 -1
  26. package/kb/extra/rescope.zh.md +1 -1
  27. package/kb/extra/session-format-status.md +47 -0
  28. package/kb/extra/session-format-status.zh.md +47 -0
  29. package/kb/extra/subsystems/agent-team.md +1 -1
  30. package/kb/extra/subsystems/agent-team.zh.md +1 -1
  31. package/kb/extra/subsystems/feedback.md +164 -30
  32. package/kb/extra/subsystems/feedback.zh.md +164 -30
  33. package/kb/extra/testing.md +4 -3
  34. package/kb/extra/testing.zh.md +4 -3
  35. package/kb/extra/web-styling.md +1 -0
  36. package/kb/extra/web-styling.zh.md +1 -0
  37. package/kb/meta/search-index.json +151 -103
  38. package/kb/meta/site-pages.txt +188 -184
  39. package/kb/meta/source.json +5 -5
  40. package/kb/meta/topics.md +2 -2
  41. package/kb/site/develop/basic/publish.md +1 -1
  42. package/kb/site/en/develop/basic/publish.md +1 -1
  43. package/kb/site/en/guide/providers.md +1 -1
  44. package/kb/site/en/guide/python-sdk.md +19 -3
  45. package/kb/site/en/reference/agent-lifecycle.md +12 -5
  46. package/kb/site/en/reference/capability-seams.md +10 -3
  47. package/kb/site/en/reference/config-catalog.md +173 -42
  48. package/kb/site/en/reference/cookbook/adding-a-tool.md +1 -1
  49. package/kb/site/en/reference/cookbook/extension-cookbook.md +1 -1
  50. package/kb/site/en/reference/index.md +18 -8
  51. package/kb/site/en/reference/persistence-catalog.md +121 -48
  52. package/kb/site/en/reference/subsystems/approval.md +1 -1
  53. package/kb/site/en/reference/subsystems/client-modules.md +9 -0
  54. package/kb/site/en/reference/subsystems/client-resources.md +94 -0
  55. package/kb/site/en/reference/subsystems/compaction.md +1 -1
  56. package/kb/site/en/reference/subsystems/core.md +79 -16
  57. package/kb/site/en/reference/subsystems/filesystem.md +15 -0
  58. package/kb/site/en/reference/subsystems/goal.md +37 -1
  59. package/kb/site/en/reference/subsystems/index.md +2 -0
  60. package/kb/site/en/reference/subsystems/invariants.md +1 -1
  61. package/kb/site/en/reference/subsystems/llm-streaming.md +20 -10
  62. package/kb/site/en/reference/subsystems/lsp.md +1 -1
  63. package/kb/site/en/reference/subsystems/persistence.md +36 -15
  64. package/kb/site/en/reference/subsystems/schedule.md +1 -1
  65. package/kb/site/en/reference/subsystems/scope.md +1 -1
  66. package/kb/site/en/reference/subsystems/session-reference.md +4 -0
  67. package/kb/site/en/reference/subsystems/session-telemetry.md +22 -6
  68. package/kb/site/en/reference/subsystems/session.md +64 -42
  69. package/kb/site/en/reference/subsystems/shell.md +9 -6
  70. package/kb/site/en/reference/subsystems/sidebar-right.md +151 -0
  71. package/kb/site/en/reference/subsystems/slots.md +41 -33
  72. package/kb/site/en/reference/subsystems/spill.md +13 -6
  73. package/kb/site/en/reference/subsystems/subagent.md +20 -13
  74. package/kb/site/en/reference/subsystems/subprocess.md +25 -26
  75. package/kb/site/en/reference/subsystems/system-prompt.md +3 -1
  76. package/kb/site/en/reference/subsystems/token-meter.md +2 -1
  77. package/kb/site/en/reference/subsystems/tools.md +5 -5
  78. package/kb/site/en/reference/subsystems/workspace.md +80 -1
  79. package/kb/site/en/reference/tool-catalog.md +46 -2
  80. package/kb/site/en/reference/tool-execution-pipeline.md +2 -2
  81. package/kb/site/guide/providers.md +1 -1
  82. package/kb/site/guide/python-sdk.md +19 -3
  83. package/kb/site/reference/agent-lifecycle.md +12 -5
  84. package/kb/site/reference/capability-seams.md +10 -3
  85. package/kb/site/reference/config-catalog.md +173 -42
  86. package/kb/site/reference/cookbook/adding-a-tool.md +1 -1
  87. package/kb/site/reference/cookbook/extension-cookbook.md +1 -1
  88. package/kb/site/reference/index.md +18 -8
  89. package/kb/site/reference/persistence-catalog.md +127 -54
  90. package/kb/site/reference/subsystems/approval.md +1 -1
  91. package/kb/site/reference/subsystems/client-modules.md +9 -0
  92. package/kb/site/reference/subsystems/client-resources.md +94 -0
  93. package/kb/site/reference/subsystems/compaction.md +1 -1
  94. package/kb/site/reference/subsystems/core.md +79 -16
  95. package/kb/site/reference/subsystems/filesystem.md +15 -0
  96. package/kb/site/reference/subsystems/goal.md +37 -1
  97. package/kb/site/reference/subsystems/index.md +14 -12
  98. package/kb/site/reference/subsystems/invariants.md +1 -1
  99. package/kb/site/reference/subsystems/llm-streaming.md +20 -10
  100. package/kb/site/reference/subsystems/lsp.md +1 -1
  101. package/kb/site/reference/subsystems/persistence.md +36 -15
  102. package/kb/site/reference/subsystems/schedule.md +1 -1
  103. package/kb/site/reference/subsystems/scope.md +1 -1
  104. package/kb/site/reference/subsystems/session-reference.md +4 -0
  105. package/kb/site/reference/subsystems/session-telemetry.md +22 -6
  106. package/kb/site/reference/subsystems/session.md +64 -42
  107. package/kb/site/reference/subsystems/shell.md +9 -6
  108. package/kb/site/reference/subsystems/sidebar-right.md +151 -0
  109. package/kb/site/reference/subsystems/slots.md +41 -33
  110. package/kb/site/reference/subsystems/spill.md +13 -6
  111. package/kb/site/reference/subsystems/subagent.md +20 -13
  112. package/kb/site/reference/subsystems/subprocess.md +25 -26
  113. package/kb/site/reference/subsystems/system-prompt.md +3 -1
  114. package/kb/site/reference/subsystems/token-meter.md +2 -1
  115. package/kb/site/reference/subsystems/tools.md +5 -5
  116. package/kb/site/reference/subsystems/workspace.md +80 -1
  117. package/kb/site/reference/tool-catalog.md +46 -2
  118. package/kb/site/reference/tool-execution-pipeline.md +2 -2
  119. package/lib/index.js +25 -44
  120. package/package.json +3 -2
  121. package/skills/dsh-plugin-dev-kb.md +3 -3
@@ -0,0 +1,94 @@
1
+ ---
2
+ editSource: "docs/subsystems/client-resources.zh.md"
3
+ outline: [2,3]
4
+ ---
5
+
6
+ # 客户端资源
7
+
8
+ 客户端资源模型把一个地址变成任何 Web Client 组件都能读的活数据。[`dsh-client-resources`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/resources/README.zh.md) 提供 `ctx.resources` 服务与 `useResource` 全局标准 hook;拥有某类内容的包为它的**协议**注册一个**提供方**,组件按**地址**读取该内容的当前状态,而无需引用拥有者的运行时。右侧 Sidebar 的 tab 是这个模型的第一个消费方([右侧 Sidebar](./sidebar-right.md));决策记录见 [客户端资源模型 Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-05-client-resource-model.zh.md)。
9
+
10
+ 本页是面向开发者的参考:地址怎么写、提供方怎么注册、资源怎么读、状态与失败各是什么意思、模型怎样持有与释放一份资源。
11
+
12
+ ## 地址
13
+
14
+ 资源地址是 `dsh-resource://<type>/…` 形式的 URL。host 命名协议,必须是 `ResourceProtocolMap` 的键;路径归协议自己,由其拥有者逐段做百分号编码。需要作用域的协议把作用域放进路径:`file` 协议的地址形如 `dsh-resource://file/session/<sessionId>/<path>`,其中 path 可以相对工作区根,也可以是保留前导斜杠的绝对路径,用 [`dsh-util-workspace-path`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/util/workspace-path/README.zh.md) 的 `fileAddressFor(sessionId, cwd, path)` 构造、`parseFileAddress(address)` 读回。模型本身只读 scheme 与 host:`protocolOf(address)` 对 `dsh-resource://` URL 返回小写 host,对其它任何字串返回 `undefined`。其它 scheme 下的地址——Sidebar 的 `sidebar://guide`——不指向资源,读作 `none`。
15
+
16
+ | 地址 | 协议键 | 读作 |
17
+ |---|---|---|
18
+ | `dsh-resource://file/session/s1/notes/a.md` | `file` | 会话 `s1` 工作区根下 `notes/a.md` 的元数据(`file` 提供方已注册时) |
19
+ | `dsh-resource://file/absolute/home/me/notes.md` | `file` | 可解析,但没有授权 Session,以 `workspace-file/unknown-workspace` 失败;不借用当前或 Tab Session |
20
+ | `DSH-RESOURCE://File/session/s1/a` | `file` | 另一份记录:地址按字符串比较,`openResource` 只接受 `fileAddressFor` 生成的规范小写拼写 |
21
+ | `sidebar://guide` | — | `none`:导航地址 |
22
+ | `/home/me/notes.md` | — | `none`:不是 URL |
23
+
24
+ ## 注册提供方
25
+
26
+ 协议拥有者在 `ResourceProtocolMap` 上声明其值类型,并在自己的 `ctx.effect` 里注册一个提供方,使协议与插件同寿([提供协议](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/resources/README.zh.md#provide-a-protocol))。`open(address, { signal })` 返回一条 `RemoteResult` 帧流——首帧是当前状态,之后每次变化一帧——并且必须在 `signal` 中止时停下。失败是携带 `RemoteFailure` 的 `ok: false` 帧;流里抛出是编程错误,不会被捕获。
27
+
28
+ ```ts ignore-check
29
+ import type { Context } from '@deepseek-ai/cordis'
30
+ import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol'
31
+ import type {} from '@deepseek-ai/dsh-client-resources/client'
32
+
33
+ interface NoteView { readonly title: string; readonly updatedAt: string }
34
+
35
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
36
+ interface ResourceProtocolMap { note: NoteView }
37
+ }
38
+
39
+ export const inject = ['resources', 'remote']
40
+
41
+ export function apply(ctx: Context): void {
42
+ ctx.effect(() => ctx.resources.register<'note'>({
43
+ protocol: 'note',
44
+ async *open(address, { signal }): AsyncIterable<RemoteResult<NoteView>> {
45
+ const id = new URL(address).pathname.slice(1)
46
+ yield await ctx.remote.notes.read(id, signal)
47
+ for await (const change of ctx.remote.notes.follow(id, signal)) yield change
48
+ },
49
+ }), 'my-notes: note resource provider')
50
+ }
51
+ ```
52
+
53
+ 一个协议恰有一个提供方;第二次注册抛错。注册时若该协议的地址已被持有,则立刻打开它们的流;提供方 dispose 时结束这些流,地址读作 `none` 直到提供方回来。
54
+
55
+ ## 读取资源
56
+
57
+ 每个 slot 组件不论作用域都在 props 上收到 `useResource`([Slots](./slots.md))。`useResource<P>(address)` 以类型参数命名协议,返回该地址的当前快照;订阅就是持有资源的方式,另一个持有者让资源存活时,新挂载的组件立刻读到最新值而不重开流([读取资源](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/resources/README.zh.md#read-a-resource))。
58
+
59
+ | `status` | 含义 | `value` | `failure` |
60
+ |---|---|---|---|
61
+ | `none` | 地址的协议没有注册提供方,或地址不是资源地址 | `undefined` | `undefined` |
62
+ | `loading` | 提供方的流已打开、尚未产出 | `undefined` | `undefined` |
63
+ | `live` | 最新一帧成功 | 最新的 `ok` 值 | `undefined` |
64
+ | `failed` | 最新一帧报告了失败 | 保留的上一个 `ok` 值 | 该帧的 `RemoteFailure` |
65
+
66
+ ```tsx ignore-check
67
+ import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
68
+ import type {} from '@deepseek-ai/dsh-api-workspace-files/client'
69
+
70
+ type Props = PropsRuntime<'sidebar.right.pane.tab'>
71
+
72
+ export function FileHeader({ useTabInfo, useResource, t }: Props) {
73
+ const { tab } = useTabInfo()
74
+ const meta = useResource<'file'>(tab.contentId)
75
+ if (meta.status === 'failed') return <p role="alert">{t('failed', { code: meta.failure.code })}</p>
76
+ return (
77
+ <header>
78
+ {tab.title}
79
+ </header>
80
+ )
81
+ }
82
+ ```
83
+
84
+ `failed` 由消费方自己呈现:模型把最后一个值留在失败旁,正文可以带提示显示旧内容而不是一片空白,下一个 `ok` 帧会清除失败。模型本身不产生任何用户可见文案。
85
+
86
+ ## 持有与释放
87
+
88
+ 资源有持有者就存活:一个订阅中的 `useResource`,或一次钉住。`ctx.resources.pin(address, signal)` 在不订阅的情况下让资源保持打开直到 `signal` 中止,已中止的信号什么也不钉;右侧 Sidebar 在每条打开的 tab 记录存续期内钉住其地址,因此切 tab 卸载正文不关流。第一个持有者打开提供方的流;最后一个释放时中止它、丢弃值,并把快照回到 `loading`(有提供方)或 `none`(没有)。提供方在这次释放之后产出的帧被丢弃,迭代器被归还。`ctx.resources.source(address)` 是 hook 背后的裸 observable,按地址引用稳定,供 React 之外的调用方使用;只读它的快照不算持有([生命周期](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/resources/README.zh.md#lifecycle))。
89
+
90
+ 流只推元数据不推内容。`file` 提供方的值是 `WorkspaceFileStat { absolutePath, version, bytes? }`:首帧来自 Host 的 `stat`,后续观察更新版本。消费方自己经 Workspace Files Remote 命名空间读取内容;Preview 按 tab 独立刷新([`dsh-api-workspace-files`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/workspace-files/README.zh.md))。
91
+
92
+ ## 限制
93
+
94
+ 记录在页面存续期内保留:地址的记录在最后一个持有者离开后仍留着,不持有流也不持有值,因此内存随读过的不同地址数增长。忽略 `signal` 的提供方会一直跑到它的下一帧。失败类型是 Remote 面的 `RemoteFailure`,来源不是 Remote 调用的提供方得自己铸一个。拼错的协议或畸形的地址读作 `none`,没有别的诊断。
@@ -11,7 +11,7 @@ outline: [2,3]
11
11
 
12
12
  ## `compaction/*` 会话事件
13
13
 
14
- 压缩通过声明合并为 [`SessionEventMap`](./session.md) 扩展三种事件类型。三者都**仅写入日志**——它们记录锁、摘要、选中范围、被遮蔽事件 seq、token 数以及模型调用,绝不进入 surface。这里有意不扩展 `SurfaceEventType`(只有产生消息的事件才到达模型),因此摘要本身承载在另一条带有 `surfaceOp: { op: 'replace', start, end }` 的 `user/message` 上——这是摘要压缩执行的唯一 surface 变更。[Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.zh.md) 负责复用 `user/message` 的决策依据。
14
+ 压缩通过声明合并为 [`SessionEventMap`](./session.md) 扩展三种事件类型。三者都**仅写入日志**——它们记录锁、摘要、选中范围、被遮蔽事件 seq、token 数以及模型调用,绝不进入 surface。这里有意不扩展 `SurfaceEventType`(只有产生消息的事件才到达模型),因此摘要本身承载在另一条带有 `surfaceOp: { op: 'replace', startSeq, endSeq }` 的 `user/message` 上——这是摘要压缩执行的唯一 surface 变更。[Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-06-18-compaction-capability-seam.zh.md) 负责复用 `user/message` 的决策依据。
15
15
 
16
16
  | 事件 | 载荷 | 作用 |
17
17
  |---|---|---|
@@ -51,9 +51,9 @@ interface AgentHandle {
51
51
  }
52
52
  ```
53
53
 
54
- `CreateAgentOptions` 携带共享标识以及新 agent 发布前所需的一切:会话元数据(`meta`——已校验的 `cwd`、fork 谱系、`isSeeded` 标记、来源分类、委派深度与 `agentPreset`)、同级字段 `inheritedEventCount` 所表示的精确 fork cut、可选的 `seed` 回放前缀、按 agent 的 `AgentOptions`、仅创建期有效的取消 `signal`,以及 `setup`。`ResumeAgentOptions` 是持久标识的对应项:`resumeSessionId`、`agentOptions`、`signal` 与 `setup`。`setup` 回调(`AgentSetup`)在两个 id 都尚未发布时组装 agent 的作用域世界——凡经 `agentCtx` 注册的内容都先于 `agent/created` 与第一次提示词组装存在——并可返回一个在发布前一刻调用的同步 commit;setup 拒绝、commit 抛出或所有者 dispose(资源释放)都会回滚事务,两个 id 均不发布。
54
+ `CreateAgentOptions` 携带共享标识以及新 agent 发布前所需的一切:可选的存活 `parentAgent`、会话元数据(`meta`——已校验的 `cwd`、fork 谱系、`isSeeded` 标记、来源分类、委派深度与 `agentPreset`)、同级字段 `inheritedEventCount` 所表示的精确 fork cut、可选的 `seed` 回放前缀、按 agent 的 `AgentOptions`、仅创建期有效的取消 `signal`,以及 `setup`。`ResumeAgentOptions` 是持久标识的对应项:`resumeSessionId`、`parentAgent`、`agentOptions`、`signal` 与 `setup`。`setup` 回调(`AgentSetup`)在两个 id 均未发布时接收 `(agentCtx, agent)`:上下文拥有作用域注册,显式 Agent 提供确切的子 Session,Context 无需反向属性。凡经 `agentCtx` 注册的内容都先于 `agent/created` 与第一次提示词组装存在。Setup 可以返回在发布前一刻调用的同步 commit;setup 拒绝、commit 抛出或所有者 dispose(资源释放)都会回滚事务,两个 id 均不发布。
55
55
 
56
- `AgentFactory` 是注册表背后的创建接口:循环经 `ctx.agents.setFactory()` 注册其工厂,因此消费方使用 `ctx.agents` 时无需依赖具体循环包。确切的 `create`/`resume` 签名及回滚约定见下方[生成区块](#ctxagents--agentregistry)。
56
+ `AgentFactory` 是注册表背后的创建接口:循环经 `ctx.agents.setFactory()` 注册其工厂,因此消费方使用 `ctx.agents` 时无需依赖具体循环包。运行时子 Agent 的创建方设置 `options.parentAgent`;注册表把 options 与调用方 Context 传给工厂,不从其中一项推导另一项。确切的 `create`/`resume` 签名及回滚约定见下方[生成区块](#ctxagents--agentregistry)。
57
57
 
58
58
  <a id="the-agent-handle"></a>
59
59
 
@@ -72,7 +72,7 @@ interface Agent {
72
72
  readonly options: AgentOptions
73
73
  /** The live session this agent drives; its log is the durable source of truth. */
74
74
  readonly session: Session
75
- /** The agent-owned projection of durable pending work. */
75
+ /** Agent-owned access to durable pending work. */
76
76
  readonly inbox: Inbox
77
77
  /** The current lifecycle state, mirrored on every `agent/status` transition. */
78
78
  readonly status: AgentStatus
@@ -213,16 +213,73 @@ interface AgentOptions {
213
213
  }
214
214
  ```
215
215
 
216
- 在 `agent/request` 之后,分发要求 `provider` 与 `model` 都存在。显式 `reasoningEffort` 会为该路由的首次请求提供初始值;确切模型解析会校验该值,省略时则允许填入适配器默认值。提供 `maxTokens` 时,它必须是正安全整数,并限制每次对话模型请求的输出;省略时,系统会在写入请求 header 前填入确切模型的适配器默认值,否则提供方行为保持不变。agent 作用域的 `deployment:persona` 提示词段落可以遮蔽全局默认 persona。
216
+ 在 `agent/request` 之后,分发要求 `provider` 与 `model` 都存在。显式 `reasoningEffort` 会为该路由的首次请求提供初始值;确切模型解析会校验该值,省略时则允许填入适配器默认值。提供 `maxTokens` 时,它必须是正安全整数,并限制每次对话模型请求的输出;省略时,系统会在写入请求 header 前填入确切模型的适配器默认值,否则提供方行为保持不变。agent 作用域的 `deployment:persona-prefix` 提示词段落可以遮蔽全局默认 persona。
217
217
 
218
218
  inbox 即投递词汇——agent 以持久投影形式拥有的两条有序待处理消息列表:
219
219
 
220
+ ```ts type-equiv
221
+ /** Agent-owned access to pending work; concrete storage belongs to the driver. */
222
+ interface Inbox {
223
+ /** Prompts awaiting individual turns. */
224
+ readonly nextTurn: readonly UserMessage[]
225
+ /** Input awaiting the next step boundary. */
226
+ readonly nextStep: readonly UserMessage[]
227
+
228
+ /** Durably cancel all pending input, clearing next-step before next-turn. */
229
+ clear(): void
230
+
231
+ /**
232
+ * Append one message to a pending list.
233
+ * @param target - pending list to extend.
234
+ * @param message - message to append.
235
+ */
236
+ append(target: InboxTarget, message: UserMessage): void
237
+
238
+ /**
239
+ * Prepend one message to a pending list.
240
+ * @param target - pending list to extend.
241
+ * @param message - message to prepend.
242
+ */
243
+ prepend(target: InboxTarget, message: UserMessage): void
244
+
245
+ /**
246
+ * Replace one pending message in place.
247
+ * @param messageId - identity of the pending message to replace.
248
+ * @param newMessage - replacement message.
249
+ * @returns whether the message was still pending.
250
+ */
251
+ replace(messageId: MessageId, newMessage: UserMessage): boolean
252
+
253
+ /**
254
+ * Remove one pending message.
255
+ * @param messageId - identity of the pending message to remove.
256
+ * @returns whether the message was still pending.
257
+ */
258
+ remove(messageId: MessageId): boolean
259
+
260
+ /**
261
+ * Apply standard splice semantics and durably record the normalized result.
262
+ * @param target - pending list to mutate.
263
+ * @param start - splice position.
264
+ * @param deleteCount - maximum number of messages to remove.
265
+ * @param inserted - messages to insert at the resolved position.
266
+ * @returns messages removed by the splice.
267
+ */
268
+ splice(
269
+ target: InboxTarget,
270
+ start: number,
271
+ deleteCount: number,
272
+ inserted: UserMessage[],
273
+ ): UserMessage[]
274
+ }
275
+ ```
276
+
220
277
  ```ts type-equiv
221
278
  /** One of the two ordered pending-message lists owned by an agent. */
222
279
  type InboxTarget = 'next-turn' | 'next-step'
223
280
  ```
224
281
 
225
- 每个待处理入队项就是其 `UserMessage`;`MessageId` 是唯一标识。`Inbox.append`、`prepend`、`replace`、`remove`、`clear`、`splice` `claim` 会记录规范化的持久 `agent/inbox/spliced` 变更,并拒绝重复的待处理 id。`replace(messageId, newMessage)` 与 `remove(messageId)` 通过 `MessageId` 跨两份列表定位待处理消息;替换可以改变标识,并先将旧消息作为 discarded 发布,再将新消息作为 inserted 发布。普通删除和 `clear()` 都表示取消。`claim(target)` 通过纯删除 splice 移除拟进入步骤的批次——全部 `next-step` 输入,外加轮次边界上的一条 `next-turn` 消息——且不发出 discarded 通知;循环另行逐条发出 claimed 通知。UI 投影等整体队列消费方通过持久 splice 重建 `nextTurn` `nextStep`,而跟踪单条消息的消费方使用精确的 `agent/inbox/inserted`、`claimed` 与 `discarded` 通知。
282
+ 每个待处理入队项就是其 `UserMessage`;`MessageId` 是唯一标识。结构化 `Inbox` 方法会记录规范化的持久 `agent/inbox/spliced` 变更,并拒绝重复的待处理 id。`replace(messageId, newMessage)` 与 `remove(messageId)` 通过 `MessageId` 跨两份列表定位待处理消息;替换可以改变标识,并先将旧消息作为 discarded 发布,再将新消息作为 inserted 发布。普通删除和 `clear()` 都表示取消。在步骤边界,dsh-agent-loop 包内部的 `ReactLoopInbox` 会通过纯删除 splice 移除拟进入步骤的批次——全部 `next-step` 输入,外加轮次边界上的一条 `next-turn` 消息——且不发出 discarded 通知,随后逐条发出 claimed 通知。仅供循环使用的待处理检测与领取操作不属于 `Agent.inbox`。每个 `ReactLoopInbox` 构造函数都从其 agent 作用域贡献标准 `inbox` 投影;注册表通过引用计数在多个 agent 之间共享该定义,其 cell 是唯一 live 状态,同一份折叠也服务于冷消费方。该 fold 会拒绝不安全或越界的 splice 坐标,以及跨两份列表重复的标识,并通过事件 seq 指出格式错误的持久历史。跟踪单条消息的消费方使用精确的 `agent/inbox/inserted`、`claimed` 与 `discarded` 通知。
226
283
 
227
284
  取消:
228
285
 
@@ -301,7 +358,7 @@ type SessionStartSource = 'startup' | 'resume' | 'clear' | 'compact'
301
358
 
302
359
  `Session` 是一份类型化 `SessionEvent` 的**仅追加日志**——唯一的真源。LLM 消息历史从日志*派生*(`deriveMessages()`),而非单独存储。每个条目携带单调的 `seq`、`time` 与按 `type` 判别的 `data` payload;surface 变体还可以在 `sourceEventSeqs` 中列出被引用的较早事件,并携带 `surfaceOp`。
303
360
 
304
- `SessionEvent` 信封的确切条件字段、十二种核心事件变体(`turn/start`、`turn/end`、`step/start`、`step/end`、`user/message`、`assistant/message`、`assistant/attempt`、`tool/call`、`tool/result`、`request/header`、`request/context`、`session/end-seed`)、`deriveMessages()` 投影规则、`TurnEndReason` 原因以及执行封闭和独立事件规则都在 **[session.md](./session.md)** 中。日志如何持久化——`SessionPersistence` 接口、JSONL provider、`session/flush` 检查点、崩溃恢复与 `SessionHeader`——则在 **[persistence.md](./persistence.md)** 中。
361
+ `SessionEvent` 信封的确切条件字段、十三种核心事件变体(`turn/start`、`turn/end`、`step/start`、`step/end`、`user/message`、`system/message`、`assistant/message`、`assistant/attempt`、`tool/call`、`tool/result`、`request/header`、`request/context`、`session/end-seed`)、`deriveMessages()` 投影规则、`TurnEndReason` 原因以及执行封闭和独立事件规则都在 **[session.md](./session.md)** 中。日志如何持久化——`SessionPersistence` 接口、JSONL provider、`session/flush` 检查点、崩溃恢复与 `SessionHeader`——则在 **[persistence.md](./persistence.md)** 中。
305
362
 
306
363
  ## `ToolDefinition`
307
364
 
@@ -419,7 +476,7 @@ async create(id: SessionId, options: AgentOptions = {}, meta: Pick<SessionHeader
419
476
  /**
420
477
  * Create an owned agent on a caller-supplied session id.
421
478
  * @param ownerCtx - caller context that structurally owns the lifecycle.
422
- * @param options - identities, session seed/metadata, loop options, setup, and cancellation.
479
+ * @param options - identities, optional live parent, session seed/metadata, loop options, setup, and cancellation.
423
480
  * @returns the published handle.
424
481
  */
425
482
  async createAgent(ownerCtx: Context, options: CreateAgentOptions): Promise<AgentHandle>
@@ -427,7 +484,7 @@ async createAgent(ownerCtx: Context, options: CreateAgentOptions): Promise<Agent
427
484
  /**
428
485
  * Resume an owned agent from the configured persistence service.
429
486
  * @param ownerCtx - caller context that owns load, setup, and the live lifecycle.
430
- * @param options - persisted identity, loop options, setup, and cancellation.
487
+ * @param options - persisted identity, optional live parent, loop options, setup, and cancellation.
431
488
  * @returns the published handle.
432
489
  */
433
490
  async resume(ownerCtx: Context, options: ResumeAgentOptions): Promise<AgentHandle>
@@ -692,7 +749,8 @@ Initiator methods provide same-process causal attribution only. Ambient presence
692
749
  * Read the Agent that initiated the inherited asynchronous driver chain.
693
750
  * Use this optional form for logging, tracing, metrics, or host attribution
694
751
  * that also supports agentless calls. When a parent creates a child, setup
695
- * reports the causal parent while `agentCtx.agent` identifies the child.
752
+ * reports the causal parent while the setup callback's Agent parameter
753
+ * identifies the child.
696
754
  * @returns the inherited Agent, or `undefined` outside an initiator boundary
697
755
  * and inside an explicit clearing boundary.
698
756
  * @throws when this service instance has been disposed.
@@ -757,7 +815,7 @@ setFactory(factory: AgentFactory): () => void
757
815
  * agent): this constructs the agent and its session. Rejects if no factory is
758
816
  * registered or creation/setup fails. The resolved {@link AgentHandle} lets
759
817
  * the owner tear down exactly this agent.
760
- * @param options - shared identity, session seed/metadata, and agent options.
818
+ * @param options - shared identity, optional live parent, session seed/metadata, and agent options.
761
819
  * @returns the handle after setup, rollback-covered publication, and loop start complete.
762
820
  */
763
821
  async create(options: CreateAgentOptions): Promise<AgentHandle>
@@ -766,7 +824,7 @@ async create(options: CreateAgentOptions): Promise<AgentHandle>
766
824
  * Load a persisted session and resume an agent on it through the registered
767
825
  * factory. Rejects if no factory is registered; the factory rejects if
768
826
  * session persistence is not configured or persistence/setup fails.
769
- * @param options - persisted identity, configuration, and optional setup.
827
+ * @param options - persisted identity, optional live parent, configuration, and setup.
770
828
  * @returns the handle after setup, rollback-covered publication, and loop start complete.
771
829
  */
772
830
  async resume(options: ResumeAgentOptions): Promise<AgentHandle>
@@ -778,7 +836,8 @@ async resume(options: ResumeAgentOptions): Promise<AgentHandle>
778
836
  * (`scopeTarget(agent, agent)`): the subject is the agent in hand, so the
779
837
  * emits are scope-filtered regardless of which context invoked `register`
780
838
  * (calling through `agent.ctx` scopes EFFECTS; dispatch scoping always
781
- * requires passing the carrier). Returns the disposer.
839
+ * requires passing the carrier). The entry is a runtime root; factory-backed
840
+ * creation uses `options.parentAgent` for child ownership. Returns the disposer.
782
841
  * @param agent - the already-constructed agent to record in the store.
783
842
  * @returns the EXACT Cordis effect disposer (single-shot; a repeat call
784
843
  * returns undefined without awaiting an in-flight teardown). Exact
@@ -798,7 +857,7 @@ register(agent: Agent): () => void
798
857
  * returned detach closure into its pre-installed composite teardown before
799
858
  * calling {@link announce}. Ordinary callers use {@link register}.
800
859
  * @param agent - the prepared, unpublished agent.
801
- * @param owner - live agent whose scoped context created this agent, or
860
+ * @param owner - explicitly supplied live runtime owner, or
802
861
  * undefined for a top-level runtime root. This is runtime ownership, not
803
862
  * the resumed session's durable parent lineage.
804
863
  * @returns an idempotent closure that removes this exact entry and emits
@@ -1043,14 +1102,18 @@ Source: [`packages/core/agent/src/runtime-types.ts`](https://github.com/deepseek
1043
1102
 
1044
1103
  #### `agent/request` — waterfall
1045
1104
 
1046
- Replace the frozen call configuration. `await next()` yields the config the machine would use (agent options on the first request, the logged header afterwards); return a replacement to switch. Model-visible content must use logged channels; this waterfall cannot mutate messages.
1105
+ Replace the frozen call configuration. `await next()` yields the config the machine would use (agent options on the first request, the logged header afterwards); return a replacement to switch. On step admission, this runs after assembly and `step/start`, before the system prompt and accepted user batch are committed. Cancellation here or during subsequent `prepareCall()` resolution commits neither. The prepared call capability governs prompt admission. Model-visible content must use logged channels; this waterfall cannot mutate messages.
1047
1106
 
1048
1107
  ```ts cordis-catalog
1049
1108
  /**
1050
1109
  * Replace the frozen call configuration. `await next()` yields the config
1051
1110
  * the machine would use (agent options on the first request, the logged
1052
- * header afterwards); return a replacement to switch. Model-visible
1053
- * content must use logged channels; this waterfall cannot mutate messages.
1111
+ * header afterwards); return a replacement to switch. On step admission,
1112
+ * this runs after assembly and `step/start`, before the system prompt and
1113
+ * accepted user batch are committed. Cancellation here or during subsequent
1114
+ * `prepareCall()` resolution commits neither. The prepared call capability
1115
+ * governs prompt admission. Model-visible content must use logged channels;
1116
+ * this waterfall cannot mutate messages.
1054
1117
  * @param payload.agent - the agent making the model call.
1055
1118
  * @param payload.turn - the open turn number.
1056
1119
  * @param payload.step - the step whose request this is.
@@ -399,6 +399,21 @@ abstract streamText(target: FsTarget, signal?: AbortSignal): Promise<AsyncIterab
399
399
  */
400
400
  abstract readBytes(target: FsTarget, signal: AbortSignal | undefined, maxBytes: number): Promise<Uint8Array>
401
401
 
402
+ /**
403
+ * Read one byte window of the regular file as raw bytes with no decoding or
404
+ * binary rejection: the bytes at `[offset, offset + length)`, shorter when
405
+ * the file ends inside the window and empty when `offset` lies at or past
406
+ * its end. The window is the bound here, not the file: a backend transfers
407
+ * at most `length` bytes of content beyond the prefix it skips to reach
408
+ * `offset` and never buffers the whole file, so the caller's cap on `length`
409
+ * is the guard against unbounded buffering.
410
+ * @param target - the resolved target to read.
411
+ * @param range - `offset`, the 0-based first byte, and `length`, the largest byte count; both non-negative integers.
412
+ * @param signal - aborts the read.
413
+ * @returns the window's bytes, at most `length` long.
414
+ */
415
+ abstract readByteRange(target: FsTarget, range: { offset: number; length: number }, signal?: AbortSignal): Promise<Uint8Array>
416
+
402
417
  /**
403
418
  * List direct children of a directory in stable name order. Returns resolved
404
419
  * child targets plus cheap metadata only; never reads file contents.
@@ -72,6 +72,25 @@ interface GoalView extends GoalSnapshot {
72
72
  }
73
73
  ```
74
74
 
75
+ 服务还会在不改变持久状态的情况下发布进程本地 activation 边沿;客户端消费该事件获得实时状态。
76
+
77
+ ```ts type-equiv
78
+ /** Live process-local activation update forwarded to UI clients. */
79
+ interface GoalActivationChanged {
80
+ /** Session whose live goal activation changed. */
81
+ readonly sessionId: SessionId
82
+ /** Current exact activation, absent when no goal is current. */
83
+ readonly goal?: {
84
+ /** Exact current goal identity. */
85
+ readonly id: GoalId
86
+ /** Exact current goal revision. */
87
+ readonly revision: number
88
+ /** Current process-local continuation state. */
89
+ readonly activation: GoalActivation
90
+ }
91
+ }
92
+ ```
93
+
75
94
  ## 持久变更
76
95
 
77
96
  每次变更都是持久的 `goal/change` 会话事件,其载荷要么是变更后的完整快照,要么是清除墓碑。严格折叠与持久投影只从这些事件派生生命周期状态;inbox 变更不会影响 goal 状态。
@@ -168,7 +187,7 @@ Goal service (`ctx.goals`) backed exclusively by the owning session log.
168
187
  * @returns a fresh view or `undefined` when no goal is current.
169
188
  * @throws {@link GoalError} when the agent is not the registry's live instance.
170
189
  */
171
- get(agent: Agent): GoalView | undefined
190
+ @Remote('get') get(agent: Agent): GoalView | undefined
172
191
 
173
192
  /**
174
193
  * Remove process-local continuation authority without changing durable goal
@@ -256,6 +275,23 @@ Source: [`packages/goal/goal/src/index.ts`](https://github.com/deepseek-ai/deeps
256
275
 
257
276
  ### `goal/*` events
258
277
 
278
+ <a id="goalactivation-changed--emit"></a>
279
+
280
+ #### `goal/activation-changed` — emit
281
+
282
+ Process-local goal activation changed for one session.
283
+
284
+ ```ts cordis-catalog
285
+ /**
286
+ * Process-local goal activation changed for one session.
287
+ * @mode emit
288
+ * @param payload - session id and the exact current goal activation, or no goal after a clear.
289
+ */
290
+ 'goal/activation-changed'(payload: GoalActivationChanged): void
291
+ ```
292
+
293
+ Source: [`packages/goal/goal/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/goal/goal/src/types.ts)
294
+
259
295
  <a id="goalchanged--emit"></a>
260
296
 
261
297
  #### `goal/changed` — emit
@@ -9,21 +9,21 @@ outline: [2,3]
9
9
 
10
10
  | 页面 | 负责内容 |
11
11
  |---|---|
12
- | [core.md](./core.md) | `packages/core` 如何控制 agent loop(智能体循环):逐包的循环说明、agent 创建与所有权(`AgentHandle`)、`Agent` 句柄的投递/取消/拦截约定,以及全仓通用类型模式(`…Map → 派生联合`、品牌化 id) |
12
+ | [core.md](./core.md) | `packages/core` 如何控制 agent loop(智能体循环):逐包的循环说明、agent 创建与所有权(`AgentHandle`)、`Agent` 句柄的投递/取消/拦截约定,以及全仓通用类型模式(`…Map → derived-union`、品牌化 id) |
13
13
  | [llm-streaming.md](./llm-streaming.md) | `packages/llm` 的对话类型——`Message`/`ContentBlock`、组装完成的模型请求、`StreamChunk` wire protocol 和适配器约定(adapter contract)、`BlockAssembler`,以及 `LlmAdapter` 提供方约定 |
14
14
  | [token-meter.md](./token-meter.md) | 不可变的标量与位置回放度量,附带已消费日志修订号 |
15
15
  | [scope.md](./scope.md) | 作用域注册标识、dispatch 载体,以及拥有的 `Scope` 上下文 |
16
16
  | [typert.md](./typert.md) | 远程调用描述符、lookup/Context 声明、Typert 注册表,以及 Host Gateway/Client API 边界 |
17
17
  | [goal.md](./goal.md) | 持久 goal 标识、生命周期快照、激活、变更记录与 Round 归属 |
18
- | [schedule.md](./schedule.md) | 仅限 Session 内的提醒记录、持久转换、活动视图与普通对话交付 |
19
- | [todo.md](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/todo.zh.md) | todo 包的整列表条目类型、持久事件所有权、投影和开放轮次不变量 |
18
+ | [schedule.md](./schedule.md) | 仅限会话内的提醒记录、持久转换、活动视图与普通对话交付 |
19
+ | [todo.md](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/todo.zh.md) | todo 包的整列表条目类型、持久事件所有权、投影和未结束轮次不变式 |
20
20
  | [commands.md](./commands.md) | 人类命令注册表服务:定义、适配器发现、直接调用、结果与解析视图 |
21
21
  | [session.md](./session.md) | 完整的 `SessionEventMap` 变体目录、`TurnEndReason`、`deriveMessages()`、执行封闭与独立事件 |
22
- | [persistence.md](./persistence.md) | 持久性 seam:`SessionPersistence`、JSONL provider、`session/flush`、崩溃恢复、`SessionHeader` |
22
+ | [persistence.md](./persistence.md) | 持久性 seam:`SessionPersistence`、JSONL 提供方、`session/flush`、崩溃恢复、`SessionHeader` |
23
23
  | [settings.md](./settings.md) | 用户设置 seam:`SettingsNamespace` 注册、分层解析(默认值 → 组合 `base` → 用户文档)、owner scope、热提交 |
24
24
  | [credentials.md](./credentials.md) | 凭据 seam:配置中的 `CredentialRef` 引用(绝不含值)、按操作解析、对 UI 安全的 `CredentialInfo`、提供方来源层 |
25
25
  | [session-query.md](./session-query.md) | 逻辑记录、有界精确事件读取、关系追踪、语义筛选器/文档与全文检索结果页 |
26
- | [feedback.md](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/feedback.zh.md) | 绑定生命周期的逐消息反馈记录、乐观版本、伴随记录持久化与 Host Remote 契约 |
26
+ | [feedback.md](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/feedback.zh.md) | 绑定生命周期的逐消息反馈记录、乐观版本、伴随记录持久化与 Host Remote 约定 |
27
27
  | [session-title.md](./session-title.md) | 持久标题快照、被引用的来源消息 seq 与异步提供方约定 |
28
28
  | [session-reference.md](./session-reference.md) | 结构化跨会话引用:`SessionReferenceInput`/`Candidate`、prepared 消息上下文、稳定错误分类 |
29
29
  | [system-prompt.md](./system-prompt.md) | 逐次组装的上下文、工具提供方结果、提示词段落与协作式组装 |
@@ -36,7 +36,7 @@ outline: [2,3]
36
36
  | [terminal.md](./terminal.md) | 持久化终端 ID、后端/会话约定、发送就绪状态、有界读取与 owner 可见快照 |
37
37
  | [sandbox.md](./sandbox.md) | 每会话策略解析与进程约束 seam:文件效果模式、执行/提供方策略、`ConfinedArgv`、强制执行与故障关闭错误 |
38
38
  | [code-runtime.md](./code-runtime.md) | 代码执行 seam:`CodeRunRequest`/`Result`、绑定命名空间、捕获日志、`CodeRunFailure` 分类体系 |
39
- | [extensions.md](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/extensions.zh.md) | 带版本的动态 Cordis Plugin 与 Package、Host/Client 激活、审批、运行时检查和生命周期撤销 |
39
+ | [extensions.md](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/extensions.zh.md) | 带版本的动态 Cordis 插件与包、Host/Client 激活、审批、运行时检查和生命周期清理 |
40
40
  | [filesystem.md](./filesystem.md) | 文件系统 seam:`FsTarget`、读/写/编辑结果、观测到的文件状态、`FsErrorCode` |
41
41
  | [lsp.md](./lsp.md) | LSP 导航 seam:`LspQueryRequest`/`Result`、`LspProvider`/`Service`、四种操作、`LspError` |
42
42
  | [skills.md](./skills.md) | skill(技能)服务:发现优先级、`SkillSummary`/`SkillDefinition`、会话前缀目录、面向模型的 `skill` 加载 |
@@ -51,14 +51,16 @@ outline: [2,3]
51
51
  | [plan.md](./plan.md) | 计划模式:仅记日志的 `plan/mode` 状态、待定选择的冲刷、`PlanModeConfig`、`exit_plan_mode` 审阅流程 |
52
52
  | [invariants.md](./invariants.md) | 运行时不变式注册表:选择配置 `Config`、`InvariantInstaller`/`InvariantFailure`、空配套插件约定 |
53
53
  | [web-server.md](./web-server.md) | HTTP 载体:`WebRouteKind`/`WebRoute`、匹配顺序、可认领的回退席位、index 渲染挂接点 |
54
- | [webhook.md](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/webhook.zh.md) | 通过身份验证的提供方交付、任意程序化规则,以及 fire-and-forget 的 Workspace Session 创建 |
54
+ | [webhook.md](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/webhook.zh.md) | 通过身份验证的提供方交付、任意程序化规则,以及发起 Workspace 会话创建后不等待结果 |
55
55
  | [storage.md](./storage.md) | 存储子系统:后端约定(`StorageBackend`)、`StorageForms`、`DomainSpec`/`Domain`、`domain/changed` |
56
56
  | [workspace.md](./workspace.md) | 工作区注册表:`Workspace`/`WorkspaceId`、注册与解析、与会话 `cwd` 的关系 |
57
- | [web-client.md](./web-client.md) | 浏览器架构:启动、Remote 通信、配对的 Client model、UI adapter、Conversation 组装、Slots 与重连语义 |
58
- | [client-modules.md](./client-modules.md) | Web 插件表:`dsh.client` 声明、`WebBootGraph` 线上组合、bundle 路由与 index 转换 |
59
- | [slots.md](./slots.md) | 类型化 Web UI 组合:声明所有权、cardinality 与 scope、框架与功能注入、props 推导及当前层级 |
60
- | [conversation.md](./conversation.md) | target-neutral Session event 组装:Context identity、Location data、replay 路径、view builder 与 target 自有 render node |
57
+ | [web-client.md](./web-client.md) | 浏览器架构:启动、Remote 通信、配对的 Client model、UI 适配器、Conversation 组装、slot 与重连语义 |
58
+ | [client-modules.md](./client-modules.md) | Web 插件表:`dsh.client` 声明、`WebBootGraph` 协议格式组合、bundle 路由与 index 挂接点 |
59
+ | [slots.md](./slots.md) | 类型化 Web UI 组合:声明所有权、cardinality 与 scope、框架与功能注入、props 推导及已交付的层级结构 |
60
+ | [client-resources.md](./client-resources.md) | 客户端资源模型:`dsh-resource://<type>/…` 地址、协议提供方与 `ResourceProtocolMap`、`useResource` 全局钩子及其状态、钉住与释放 |
61
+ | [sidebar-right.md](./sidebar-right.md) | 右侧 Sidebar:资源地址与导航地址、tab 类型注册与路由、`ctx.sidebarRight` 导航服务、pane-tab slot 与 owner props、资源模型及 Workspace Files 服务 |
62
+ | [conversation.md](./conversation.md) | 目标无关的会话事件组装:上下文标识、位置数据、回放路径、视图构建器与目标自有的渲染节点 |
61
63
  | [session-projection.md](./session-projection.md) | 投影 seam:`SessionProjectionMap`、纯函数 `ProjectionDefinition` 单元、`ProjectionSnapshot` 的一致切面、变更馈送 |
62
- | [session-telemetry.md](./session-telemetry.md) | 对外会话上报能力 seam:`SessionTelemetryRecord`/`SessionTelemetrySeverity`、`SessionTelemetrySink` 约定和 `session-telemetry/record` 脱敏 waterfall |
64
+ | [session-telemetry.md](./session-telemetry.md) | 对外会话上报能力 seam:`SessionTelemetryRecord`/`SessionTelemetrySeverity`、`SessionTelemetrySink` 约定和 `session-telemetry/record` 脱敏 waterfall(瀑布式事件) |
63
65
 
64
66
  > 这些页面上的类型声明及其 JSDoc 与源码等价,并由 `pnpm run verify-type-equiv` 检查漂移(见 [development.md](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/development.zh.md#documenting-types-verbatim-ts-type-equiv))。普通块保留完整声明;`public-api` 块保留去除实现体的公开 class 声明。Cordis 服务与事件使用每页生成的 **Cordis API** 小节。
@@ -5,7 +5,7 @@ outline: [2,3]
5
5
 
6
6
  # 运行时不变式
7
7
 
8
- [dsh-invariants](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/runtime-diagnostics/invariants) 是面向包自有运行时不变式检查的可配置注册表服务(`ctx.invariants`)。它是一个 support 组的包,不是三包能力 seam,也不属于 agent loop(智能体循环)主干:注册表拥有选择逻辑、名称保留、子 fiber 生命周期和归因到包的失败,而每个工作区包发布一个 `./invariant` 配套插件,以自己确切的 npm 包名注册检查。检查可以断言什么(权威事件流或可变数据,绝不是服务或方法是否存在)是 [AGENTS.md](https://github.com/deepseek-ai/deepseek-harness/blob/master/AGENTS.md#conventions) 中的运行时不变式约定;注册表设计由[不变式服务 Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-07-19-package-owned-invariant-service.zh.md)规定。
8
+ [dsh-invariants](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/runtime-diagnostics/invariants) 是面向包自有运行时不变式检查的可配置注册表服务(`ctx.invariants`)。它是一个 support 组的包,不是三包能力 seam,也不属于 agent loop(智能体循环)主干:注册表拥有选择逻辑、名称保留、子 fiber 生命周期和归因到包的失败,而每个工作区包发布一个 `./invariant` 配套插件,以自己确切的 npm 包名注册检查。检查可以断言什么(权威事件流或可变数据,绝不是服务或方法是否存在)是 [AGENTS.md](https://github.com/deepseek-ai/deepseek-harness/blob/master/AGENTS.md#conventions) 中的运行时不变式约定。
9
9
 
10
10
  源码:[`packages/runtime-diagnostics/invariants/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/runtime-diagnostics/invariants/src/index.ts)
11
11
 
@@ -228,7 +228,7 @@ type StreamChunk =
228
228
 
229
229
  `snapshot()` 返回分离且不可变的 stream。`expandAssistantStream()` 会严格检查 record key、成员数、index、时间戳、tool-call identity 与无损 JSON,再重建精确的带时间 chunk 序列。Session 日志会把该 stream 嵌入作为 surface result 的 `assistant/message`,或嵌入没有 surface message 的 `assistant/attempt`。
230
230
 
231
- 进程本地 `agent/assistant-stream` frame 承载实时呈现。持久回放、遥测、token 记账与历史 UI 组装会展开嵌入式 settlement,而不会把 live frame 当作持久事实。
231
+ 进程本地 `agent/assistant-stream` frame 承载实时呈现。持久回放与恢复校验仍会展开内嵌 settlement;遥测、token 记账与 Host 折叠直接读取紧凑记录。记录级读取器(`assistantStreamFirstTokenTime`、`assistantStreamHasVisibleContent`、`assistantStreamHasVisibleText`、`lastAssistantStreamChunk`、`assistantStreamChunks`、`joinAssistantStreamText`、`assembleAssistantStream` 以及按 run `runFirstTokenTime` 与 `runFirstVisibleTime`)以提前退出在一次扫描内回答消费方问题,因此大历史每次结算的代价为 O(records) 而非 O(members) 展开([折叠决策](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-06-embedded-stream-record-readers.zh.md))。`expandAssistantStream()` 仍是持久边界读取记录与需要每个成员的消费方的校验路径。
232
232
 
233
233
  <a id="llmfailure"></a>
234
234
 
@@ -300,7 +300,7 @@ interface LlmImageRequestPricing {
300
300
  - **一次适配器调用就是一次提供方尝试。** 适配器禁用库重试。agent 层恢复会打开另一个持久、带编号的轮次;直接调用 `ctx.llm.stream()` 的调用方仍然只尝试一次。
301
301
  - **提供方停顿在传输层受到时限约束。** 两个已交付的远程适配器都暴露正数且有限的 `streamIdleTimeoutMs`,默认五分钟。watchdog 只在 iterator `next()` 尚未完成时启动,整个请求使用同一个稳定 signal,把自身到期映射为 `TIMEOUT`,并把更早发生的调用方中止保留为 `ABORTED`。
302
302
  - **上下文溢出只有一个规范 code。** 两个 DeepSeek 适配器都通过 `isContextWindowExceededError()` 对提供方的显式细节分类并暴露 `CONTEXT_WINDOW_EXCEEDED`,无论失败以抛出的 HTTP `LlmError` 还是带内 finish error 到达。消费方按 code 路由,绝不依赖提供方文本。
303
- - **空 completion 是可重试错误,而不是静默的成功结果。** 两个适配器都把没有携带任何内容块的终止性 `stop` 结束映射为携带规范 `EMPTY_RESPONSE` code 的 `finish {kind:'error'}`,`dsh-llm-retry` 默认会重试它;详见[空模型响应可重试](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/bug-fix/2026-07-24-empty-model-response-is-retryable.zh.md)。
303
+ - **空 completion 是可重试错误,而不是静默的成功结果。** 两个适配器都把没有携带任何内容块的终止性 `stop` 结束映射为携带规范 `EMPTY_RESPONSE` code 的 `finish {kind:'error'}`,`dsh-llm-retry` 默认会重试它。
304
304
  - **每个提供方 HTTP 请求都携带应用归属头。** 适配器发送 `attributionHeaders()`(见下文)作为 `User-Agent` 基线,并通过协议级测试加以证明。
305
305
  - **回放状态归适配器所有;其切分是共享词汇。** 成功的 `finish` 可以携带一个 `ReplayEnvelope`:不透明的响应级元数据,加上与发射块序列对齐的可选逐块条目。对齐关系是 harness 的词汇——组装丢弃某个块时,同一位置的条目一并丢弃,因此存储的元数据始终描述存储的内容。循环把裁剪后的数据与组装后的 assistant 消息一起存储。后续请求中,仅当历史提供方与目标提供方当前注册到完全相同的适配器实例时,`LlmRuntime` 才会传递该状态。该适配器负责校验状态并拥有所有跨模型或跨提供方转换;其他适配器只会收到提供方无关的内容以及提供方/模型字段,不会收到私有状态。持久化内容保持权威:读取适配器无法使用的已存状态只会把这一条消息降级为提供方无关转换并带出诊断,而不是让请求失败。
306
306
 
@@ -501,6 +501,8 @@ interface LlmConfigurableProvider {
501
501
  * from outside.
502
502
  */
503
503
  declared?: boolean
504
+ /** Configuration diagnostic for repair; unaffected models may remain serviceable. */
505
+ error?: string
504
506
  }
505
507
  ```
506
508
 
@@ -520,7 +522,7 @@ interface LlmModelInfo {
520
522
  }
521
523
  ```
522
524
 
523
- 对正确性敏感的元数据与参考目录分开解析,并归服务该确切路由的适配器所有。上下文容量、适配器调用默认值和推理选项共用同一个确切模型结果,消费方因而无需重复执行权威模型解析。
525
+ 对正确性敏感的元数据与参考目录分开解析,并归服务该确切路由的适配器所有。上下文容量、适配器调用默认值、推理选项和系统提示词更新模式共用同一个确切模型结果,消费方因而无需重复执行权威模型解析。`SystemPromptUpdate` 只有一个值 `'in-history'`:模型把 `messages` 中任意位置最新的 `system` 消息读作完整的有效系统提示词,因此 agent loop 可以把变化后的提示词追加到已缓存历史之后,而不是改写第 0 条消息([决策规则](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent-loop/README.zh.md#understand-the-implementation));模式缺失表示只读取开头的 system 消息,`normalizeModelInfo` 以 `INVALID_MODEL_INFO` 拒绝任何其他值。
524
526
 
525
527
  ```ts type-equiv
526
528
  /** Provider-owned context capacity for one exact provider/model route. */
@@ -571,6 +573,8 @@ interface LlmResolvedModelInfo extends LlmModelInfo {
571
573
  defaultMaxTokens?: number
572
574
  /** Adapter-owned selectable reasoning levels when exposed. */
573
575
  reasoning?: LlmModelReasoningInfo
576
+ /** Declared mid-conversation system prompt handling; absent means only a leading system message is read. */
577
+ systemPromptUpdate?: SystemPromptUpdate
574
578
  }
575
579
  ```
576
580
 
@@ -583,12 +587,16 @@ interface GenerateOptions {
583
587
  /** Adapter-owned reasoning effort selected for this exact model. */
584
588
  reasoningEffort?: ReasoningEffortId
585
589
  /**
586
- * Ordered conversation messages, exactly as the provider sees them (after
587
- * the `system` slot). A loop-built request assembles them as
588
- * the derived history (dsh-agent-loop); a hand-built one-shot passes any list.
590
+ * Ordered conversation messages, exactly as the provider sees them. A
591
+ * loop-built request passes the derived history (dsh-agent-loop), whose
592
+ * leading system-role message carries the system prompt; a hand-built
593
+ * one-shot passes any list.
589
594
  */
590
595
  messages: Message[]
591
- /** System prompt text (adapters map to the provider's system slot). */
596
+ /**
597
+ * System prompt text for one-shot callers; adapters map it to the provider's
598
+ * system slot ahead of `messages`. Loop-built requests leave it undefined.
599
+ */
592
600
  system?: string
593
601
  /** Tool schemas (adapters map to the provider's `tools` field). */
594
602
  tools?: ToolSchema[]
@@ -702,11 +710,11 @@ interface LlmDiscoveredModel {
702
710
 
703
711
  ### 请求信封:`LlmCallConfig` 与记录的 header
704
712
 
705
- 循环从已记录状态构建每个请求。`EpochHeader` 记录调用配置,标记由适配器默认值提供的字段,并通过完整的 `request/header` 快照记录渲染后的提示词以及权威返回工具顺序(由 `toolOrder` 配置;未配置时按字典序)。结合派生历史,请求便可由会话日志重建。见 [session.md](./session.md#the-request-header-event-requestheader) 与[可重建性 Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.zh.md)。
713
+ 循环从已记录状态构建每个请求。`EpochHeader` 记录调用配置,标记由适配器默认值提供的字段,并通过完整的 `request/header` 快照记录权威返回工具顺序(由 `toolOrder` 配置;未配置时按字典序)。渲染后的提示词是派生历史——surface 第 0 号节点上的 `system/message`,加上 `in-history` 路由追加的任何后续系统节点——因此请求头与派生历史共同使请求可由会话日志重建。见 [session.md](./session.md#the-request-header-event-requestheader) 与[可重建性 Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.zh.md)。
706
714
 
707
- `agent/request` 接收冻结的调用配置种子,并可返回替代值以切换提供方、模型、推理强度或采样参数。waterfall(瀑布式事件)开始前,循环会移除标记为适配器默认值的值,使确切模型准备过程填入所选路由的当前值;未带标记的显式设置仍保留在提议中。waterfall 结束后,准备过程会在轮次信号控制下拒绝显式指定但不受支持的推理强度 ID(不自动调整),并记录生效配置以及由适配器默认值提供的字段。准备完成的调用直至分派完成始终持有同一项适配器注册。到达 `llm/stream` 的请求会被深度冻结,因此变更会抛异常;请求还携带进程本地循环标识,使观察者不会把单独记录的冻结辅助调用误认成对话请求。
715
+ `agent/request` 接收冻结的调用配置种子,并可返回替代值以切换提供方、模型、推理强度或采样参数。waterfall(瀑布式事件)开始前,循环会移除标记为适配器默认值的值,使确切模型准备过程填入所选路由的当前值;未带标记的显式设置仍保留在提议中。waterfall 结束后,准备过程会在轮次信号控制下拒绝显式指定但不受支持的推理强度 ID(不自动调整),并记录生效配置以及由适配器默认值提供的字段。步骤准入时,该 waterfall 与准备过程在组装和 `step/start` 之后、系统提示词与已接纳用户批次提交之前运行;在任一阶段取消都不会提交这两者。已准备调用的能力决定提示词协调,调用直至分派完成始终持有同一项适配器注册。到达 `llm/stream` 的请求会被深度冻结,因此变更会抛异常;请求还携带进程本地循环标识,使观察者不会把单独记录的冻结辅助调用误认成对话请求。
708
716
 
709
- 在协议中,循环构建的请求先读取 `system` slot(渲染后的提示词组装),再读取派生历史。已记录的请求快照会以最新的 `user/message`(轮次首步)或上一步的工具结果(后续步骤)结尾。开发不变式针对每个循环构建的请求精确重算此等式。
717
+ 在协议中,循环构建的请求只有派生历史:渲染后的提示词作为开头的 `system` 角色消息(surface 第 0 号节点,即一个 `system/message` 事件)传输,并且当已准备调用声明 `systemPromptUpdate: 'in-history'` 时,变化后的非空提示词可以作为后续的 `system` 角色消息跟在已缓存历史之后,由模型读作有效提示词;请求的 `system` 字段不设置——`GenerateOptions.system` 服务于标题提供方等直接单次调用方。空渲染文本使派生历史不包含任何系统消息,即使先前请求保留了多个提示词版本。已记录的请求会以最新的 `user/message`(轮次首步)或上一步的工具结果(后续步骤)结尾。开发不变式针对每个循环构建的请求精确重算此等式,并拒绝携带 `system` 字段的循环请求。
710
718
 
711
719
  FIXME(call-config-shape):重新审视其余哪些字段出于缓存目的确实属于 epoch 层级(`model` 和模型持有的推理强度已明确属于;采样标量目前出于谨慎保留在此)。
712
720
 
@@ -759,6 +767,8 @@ interface PreparedLlmCall {
759
767
  readonly context?: LlmModelContext
760
768
  /** Exact model modalities captured with the adapter dispatch generation. */
761
769
  readonly inputModalities?: readonly ModelModality[]
770
+ /** Exact model system prompt update mode captured with the adapter dispatch generation. */
771
+ readonly systemPromptUpdate?: SystemPromptUpdate
762
772
  /** Config fields materialized by the captured adapter rather than proposed by the caller. */
763
773
  readonly adapterDefaults: LlmCallConfigAdapterDefaults
764
774
  /**
@@ -5,7 +5,7 @@ outline: [2,3]
5
5
 
6
6
  # LSP 导航
7
7
 
8
- LSP seam 是一个[能力 seam](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-07-15-lsp-capability-seam.zh.md):它在单一 `ctx.lsp` 服务上公开语义代码导航,并拆分到多个包:Service Definition([dsh-lsp](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/lsp/lsp),`ctx.lsp` + 提供方注册表)、通用 Service Provider([dsh-lsp-stdio](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/lsp/lsp-stdio),经过配置的 stdio 语言服务器宿主)和 Consumer([dsh-tool-lsp](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/lsp/tool-lsp),即 `lsp` 工具 schema)。LSP 是**一项可选能力**,不属于 agent loop(智能体循环)主干,因此其词汇定义在此而非 [core.md](./core.md) 中。更换提供方不会改变模型请求导航的方式。
8
+ LSP seam 是一个[能力 seam](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/glossary.zh.md#capability-seam):它在单一 `ctx.lsp` 服务上公开语义代码导航,并拆分到多个包:Service Definition([dsh-lsp](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/lsp/lsp),`ctx.lsp` + 提供方注册表)、通用 Service Provider([dsh-lsp-stdio](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/lsp/lsp-stdio),经过配置的 stdio 语言服务器宿主)和 Consumer([dsh-tool-lsp](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/lsp/tool-lsp),即 `lsp` 工具 schema)。LSP 是**一项可选能力**,不属于 agent loop(智能体循环)主干,因此其词汇定义在此而非 [core.md](./core.md) 中。更换提供方不会改变模型请求导航的方式。
9
9
 
10
10
  源文件:[`packages/lsp/lsp/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/lsp/lsp/src/types.ts)
11
11