dsh-plugin-guide 0.3.3 → 0.3.5

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 (102) hide show
  1. package/README.es.md +5 -1
  2. package/README.hi.md +5 -1
  3. package/README.md +5 -1
  4. package/README.pt.md +5 -1
  5. package/README.zh.md +5 -1
  6. package/dist/dsh-plugin-dev.js +3 -3
  7. package/guide/plugin-dev-guide.md +2 -2
  8. package/guide/quick-reference.es.md +2 -2
  9. package/guide/quick-reference.hi.md +1 -1
  10. package/guide/quick-reference.md +2 -2
  11. package/guide/quick-reference.pt.md +2 -2
  12. package/guide/quick-reference.zh-CN.md +2 -2
  13. package/package.json +5 -4
  14. package/references/official-docs/AGENTS.md +2 -2
  15. package/references/official-docs/SNAPSHOT.md +3 -3
  16. package/references/official-docs/THIRD_PARTY_NOTICES.md +4 -6
  17. package/references/official-docs/docs/agent-lifecycle.i18n.yaml +2 -2
  18. package/references/official-docs/docs/agent-lifecycle.md +5 -3
  19. package/references/official-docs/docs/agent-lifecycle.zh.md +5 -3
  20. package/references/official-docs/docs/architecture.i18n.yaml +2 -2
  21. package/references/official-docs/docs/architecture.md +7 -3
  22. package/references/official-docs/docs/architecture.zh.md +7 -3
  23. package/references/official-docs/docs/capability-seams.i18n.yaml +2 -2
  24. package/references/official-docs/docs/capability-seams.md +5 -0
  25. package/references/official-docs/docs/capability-seams.zh.md +5 -0
  26. package/references/official-docs/docs/config-catalog.i18n.yaml +2 -2
  27. package/references/official-docs/docs/config-catalog.md +15 -21
  28. package/references/official-docs/docs/config-catalog.zh.md +15 -21
  29. package/references/official-docs/docs/cookbook/extension-cookbook.i18n.yaml +2 -2
  30. package/references/official-docs/docs/cookbook/extension-cookbook.md +12 -10
  31. package/references/official-docs/docs/cookbook/extension-cookbook.zh.md +12 -10
  32. package/references/official-docs/docs/deepseek-llm-api-wire-extensions.i18n.yaml +2 -2
  33. package/references/official-docs/docs/deepseek-llm-api-wire-extensions.md +16 -12
  34. package/references/official-docs/docs/deepseek-llm-api-wire-extensions.zh.md +16 -12
  35. package/references/official-docs/docs/event-producer-consumer.i18n.yaml +2 -2
  36. package/references/official-docs/docs/event-producer-consumer.md +28 -27
  37. package/references/official-docs/docs/event-producer-consumer.zh.md +32 -31
  38. package/references/official-docs/docs/module-graph.i18n.yaml +2 -2
  39. package/references/official-docs/docs/module-graph.md +36 -14
  40. package/references/official-docs/docs/module-graph.zh.md +36 -14
  41. package/references/official-docs/docs/persistence-catalog.i18n.yaml +2 -2
  42. package/references/official-docs/docs/persistence-catalog.md +49 -39
  43. package/references/official-docs/docs/persistence-catalog.zh.md +39 -29
  44. package/references/official-docs/docs/subsystems/attachment.i18n.yaml +2 -2
  45. package/references/official-docs/docs/subsystems/attachment.md +157 -4
  46. package/references/official-docs/docs/subsystems/attachment.zh.md +157 -4
  47. package/references/official-docs/docs/subsystems/commands.i18n.yaml +2 -2
  48. package/references/official-docs/docs/subsystems/commands.md +24 -15
  49. package/references/official-docs/docs/subsystems/commands.zh.md +24 -15
  50. package/references/official-docs/docs/subsystems/conversation.i18n.yaml +2 -2
  51. package/references/official-docs/docs/subsystems/conversation.md +4 -4
  52. package/references/official-docs/docs/subsystems/conversation.zh.md +4 -4
  53. package/references/official-docs/docs/subsystems/core.i18n.yaml +2 -2
  54. package/references/official-docs/docs/subsystems/core.md +62 -1
  55. package/references/official-docs/docs/subsystems/core.zh.md +62 -1
  56. package/references/official-docs/docs/subsystems/llm-streaming.i18n.yaml +2 -2
  57. package/references/official-docs/docs/subsystems/llm-streaming.md +23 -2
  58. package/references/official-docs/docs/subsystems/llm-streaming.zh.md +23 -2
  59. package/references/official-docs/docs/subsystems/persistence.i18n.yaml +2 -2
  60. package/references/official-docs/docs/subsystems/persistence.md +10 -10
  61. package/references/official-docs/docs/subsystems/persistence.zh.md +10 -10
  62. package/references/official-docs/docs/subsystems/session-projection.i18n.yaml +2 -2
  63. package/references/official-docs/docs/subsystems/session-projection.md +19 -0
  64. package/references/official-docs/docs/subsystems/session-projection.zh.md +19 -0
  65. package/references/official-docs/docs/subsystems/session-reference.i18n.yaml +2 -2
  66. package/references/official-docs/docs/subsystems/session-reference.md +25 -1
  67. package/references/official-docs/docs/subsystems/session-reference.zh.md +25 -1
  68. package/references/official-docs/docs/subsystems/session-telemetry.i18n.yaml +2 -2
  69. package/references/official-docs/docs/subsystems/session-telemetry.md +6 -5
  70. package/references/official-docs/docs/subsystems/session-telemetry.zh.md +6 -5
  71. package/references/official-docs/docs/subsystems/session.i18n.yaml +2 -2
  72. package/references/official-docs/docs/subsystems/session.md +52 -40
  73. package/references/official-docs/docs/subsystems/session.zh.md +52 -40
  74. package/references/official-docs/docs/subsystems/web-client.i18n.yaml +2 -2
  75. package/references/official-docs/docs/subsystems/web-client.md +1 -1
  76. package/references/official-docs/docs/subsystems/web-client.zh.md +1 -1
  77. package/references/official-docs/docs/subsystems/workspace.i18n.yaml +2 -2
  78. package/references/official-docs/docs/subsystems/workspace.md +8 -8
  79. package/references/official-docs/docs/subsystems/workspace.zh.md +8 -8
  80. package/references/official-docs/docs/testing.i18n.yaml +2 -2
  81. package/references/official-docs/docs/testing.md +2 -2
  82. package/references/official-docs/docs/testing.zh.md +2 -2
  83. package/references/official-docs/docs/user/guide/network-proxy.i18n.yaml +6 -0
  84. package/references/official-docs/docs/user/guide/network-proxy.md +85 -0
  85. package/references/official-docs/docs/user/guide/network-proxy.zh.md +85 -0
  86. package/references/official-docs/docs/user/guide/providers.i18n.yaml +2 -2
  87. package/references/official-docs/docs/user/guide/providers.md +70 -17
  88. package/references/official-docs/docs/user/guide/providers.zh.md +70 -17
  89. package/references/official-docs/docs/web-styling.i18n.yaml +2 -2
  90. package/references/official-docs/docs/web-styling.md +1 -0
  91. package/references/official-docs/docs/web-styling.zh.md +1 -0
  92. package/references/official-docs/website-docs.ts +8 -0
  93. package/templates/js/README.es.md +1 -1
  94. package/templates/js/README.hi.md +1 -1
  95. package/templates/js/README.md +1 -1
  96. package/templates/js/README.pt.md +1 -1
  97. package/templates/js/README.zh.md +1 -1
  98. package/templates/ts/README.es.md +1 -1
  99. package/templates/ts/README.hi.md +1 -1
  100. package/templates/ts/README.md +1 -1
  101. package/templates/ts/README.pt.md +1 -1
  102. package/templates/ts/README.zh.md +1 -1
@@ -8,7 +8,7 @@ Source: [`packages/interaction/commands/src/index.ts`](../../packages/interactio
8
8
 
9
9
  ## Input metadata
10
10
 
11
- The service exposes one optional unstructured-input descriptor: a hint plus an image-acceptance flag. Command availability follows plugin composition: every adapter consuming the registry sees every effective definition.
11
+ The service exposes one optional unstructured-input descriptor: a hint plus an attachment-acceptance flag. Command availability follows plugin composition: every adapter consuming the registry sees every effective definition.
12
12
 
13
13
  ```ts type-equiv
14
14
  /** Immutable metadata for a command's optional unstructured input. */
@@ -16,13 +16,13 @@ interface CommandInputDescriptor {
16
16
  /** Placeholder shown before the user supplies free-form input. */
17
17
  readonly hint: string
18
18
  /**
19
- * Whether composer image attachments may accompany an invocation. Absent or
20
- * false = the executor rejects an invocation carrying images and capable
19
+ * Whether composer attachments may accompany an invocation. Absent or
20
+ * false = the executor rejects an invocation carrying attachments and capable
21
21
  * composers refuse the submission before dispatch. A declaring command's
22
22
  * handler receives the admitted durable blocks and owns every further
23
23
  * grammar decision, including rejecting sub-commands that cannot use them.
24
24
  */
25
- readonly images?: boolean
25
+ readonly attachments?: boolean
26
26
  }
27
27
  ```
28
28
 
@@ -64,13 +64,13 @@ interface CommandInvocation {
64
64
  /** Exact text following the registered command name, including separator whitespace. */
65
65
  readonly rawInput: string
66
66
  /**
67
- * Durably admitted image blocks accompanying this invocation, in submission
68
- * order; empty unless the definition declares `input.images`. The handler
67
+ * Durably admitted image and file blocks accompanying this invocation, in submission
68
+ * order; empty unless the definition declares `input.attachments`. The handler
69
69
  * owns their model-visible use — the registry never schedules them itself —
70
70
  * and a handler whose grammar cannot use them in this invocation returns an
71
71
  * error so the dispatching composer retains the originals.
72
72
  */
73
- readonly attachments: readonly ImageBlock[]
73
+ readonly attachments: readonly (ImageBlock | FileBlock)[]
74
74
  /** Cancellation signal owned by the dispatching UI request. */
75
75
  readonly signal: AbortSignal
76
76
  }
@@ -138,6 +138,13 @@ Human-command registry. Plain-context definitions are global; definitions regist
138
138
  */
139
139
  register(definition: CommandDefinition): () => void
140
140
 
141
+ /**
142
+ * Register the sole authority that resolves staged file receipts for command submissions.
143
+ * @param resolver - Session-aware receipt resolver.
144
+ * @returns disposer that removes this exact resolver.
145
+ */
146
+ registerFileReceiptResolver(resolver: CommandFileReceiptResolver): () => void
147
+
141
148
  /**
142
149
  * List the effective immutable command descriptors for one agent.
143
150
  * @param agent - exact receiving agent and scoped-layer key.
@@ -166,23 +173,25 @@ find(agent: Agent, name: string): CommandDefinition | undefined
166
173
  * handler-failure path is contained so the handler's own error stays the
167
174
  * reported failure.
168
175
  *
169
- * Image admission is enforced here, not in the composer: images sent to a
170
- * command that does not declare `input.images`, an absent attachment store,
171
- * and an exceeded attachment limit each settle as an error result before
172
- * the handler runs, and a rejected batch publishes no durable object.
176
+ * Attachment admission is enforced here, not in the composer: attachments sent to a
177
+ * command that does not declare `input.attachments`, an absent attachment store,
178
+ * and an exceeded image limit each settle as an error result before
179
+ * the handler runs. Validation rejection starts no attachment writes;
180
+ * a storage failure can leave only unreachable content-addressed objects
181
+ * for deferred collection.
173
182
  *
174
183
  * @param agent - exact receiving agent.
175
184
  * @param line - complete slash-command line.
176
- * @param images - base64-encoded composer images accompanying the line, in
177
- * submission order; empty for a plain invocation.
185
+ * @param submittedAttachments - encoded images and staged file receipts accompanying the line,
186
+ * in submission order; empty for a plain invocation.
178
187
  * @param signal - cancellation signal owned by the UI request.
179
188
  * @returns the settled execution (result + lifecycle pairing id), or
180
189
  * `undefined` when syntax or name does not resolve.
181
190
  */
182
- @Remote async execute( agent: Agent, line: string, images: readonly EncodedImageAttachment[], signal: AbortSignal, ): Promise<CommandExecution | undefined>
191
+ @Remote async execute( agent: Agent, line: string, submittedAttachments: readonly CommandSubmitAttachment[], signal: AbortSignal, ): Promise<CommandExecution | undefined>
183
192
  ```
184
193
 
185
- Types: [Agent](core.md) · [EncodedImageAttachment](attachment.md)
194
+ Types: [Agent](core.md)
186
195
 
187
196
  Source: [`packages/interaction/commands/src/index.ts`](../../packages/interaction/commands/src/index.ts)
188
197
 
@@ -8,7 +8,7 @@
8
8
 
9
9
  ## 输入元数据
10
10
 
11
- 该服务公开一个可选的非结构化输入描述符:提示文本加图片接受标志。命令的可用性由插件组合决定:每个消费注册表的适配器都会看到全部生效定义。
11
+ 该服务公开一个可选的非结构化输入描述符:提示文本加附件接受标志。命令的可用性由插件组合决定:每个消费注册表的适配器都会看到全部生效定义。
12
12
 
13
13
  ```ts type-equiv
14
14
  /** Immutable metadata for a command's optional unstructured input. */
@@ -16,13 +16,13 @@ interface CommandInputDescriptor {
16
16
  /** Placeholder shown before the user supplies free-form input. */
17
17
  readonly hint: string
18
18
  /**
19
- * Whether composer image attachments may accompany an invocation. Absent or
20
- * false = the executor rejects an invocation carrying images and capable
19
+ * Whether composer attachments may accompany an invocation. Absent or
20
+ * false = the executor rejects an invocation carrying attachments and capable
21
21
  * composers refuse the submission before dispatch. A declaring command's
22
22
  * handler receives the admitted durable blocks and owns every further
23
23
  * grammar decision, including rejecting sub-commands that cannot use them.
24
24
  */
25
- readonly images?: boolean
25
+ readonly attachments?: boolean
26
26
  }
27
27
  ```
28
28
 
@@ -64,13 +64,13 @@ interface CommandInvocation {
64
64
  /** Exact text following the registered command name, including separator whitespace. */
65
65
  readonly rawInput: string
66
66
  /**
67
- * Durably admitted image blocks accompanying this invocation, in submission
68
- * order; empty unless the definition declares `input.images`. The handler
67
+ * Durably admitted image and file blocks accompanying this invocation, in submission
68
+ * order; empty unless the definition declares `input.attachments`. The handler
69
69
  * owns their model-visible use — the registry never schedules them itself —
70
70
  * and a handler whose grammar cannot use them in this invocation returns an
71
71
  * error so the dispatching composer retains the originals.
72
72
  */
73
- readonly attachments: readonly ImageBlock[]
73
+ readonly attachments: readonly (ImageBlock | FileBlock)[]
74
74
  /** Cancellation signal owned by the dispatching UI request. */
75
75
  readonly signal: AbortSignal
76
76
  }
@@ -138,6 +138,13 @@ Human-command registry. Plain-context definitions are global; definitions regist
138
138
  */
139
139
  register(definition: CommandDefinition): () => void
140
140
 
141
+ /**
142
+ * Register the sole authority that resolves staged file receipts for command submissions.
143
+ * @param resolver - Session-aware receipt resolver.
144
+ * @returns disposer that removes this exact resolver.
145
+ */
146
+ registerFileReceiptResolver(resolver: CommandFileReceiptResolver): () => void
147
+
141
148
  /**
142
149
  * List the effective immutable command descriptors for one agent.
143
150
  * @param agent - exact receiving agent and scoped-layer key.
@@ -166,23 +173,25 @@ find(agent: Agent, name: string): CommandDefinition | undefined
166
173
  * handler-failure path is contained so the handler's own error stays the
167
174
  * reported failure.
168
175
  *
169
- * Image admission is enforced here, not in the composer: images sent to a
170
- * command that does not declare `input.images`, an absent attachment store,
171
- * and an exceeded attachment limit each settle as an error result before
172
- * the handler runs, and a rejected batch publishes no durable object.
176
+ * Attachment admission is enforced here, not in the composer: attachments sent to a
177
+ * command that does not declare `input.attachments`, an absent attachment store,
178
+ * and an exceeded image limit each settle as an error result before
179
+ * the handler runs. Validation rejection starts no attachment writes;
180
+ * a storage failure can leave only unreachable content-addressed objects
181
+ * for deferred collection.
173
182
  *
174
183
  * @param agent - exact receiving agent.
175
184
  * @param line - complete slash-command line.
176
- * @param images - base64-encoded composer images accompanying the line, in
177
- * submission order; empty for a plain invocation.
185
+ * @param submittedAttachments - encoded images and staged file receipts accompanying the line,
186
+ * in submission order; empty for a plain invocation.
178
187
  * @param signal - cancellation signal owned by the UI request.
179
188
  * @returns the settled execution (result + lifecycle pairing id), or
180
189
  * `undefined` when syntax or name does not resolve.
181
190
  */
182
- @Remote async execute( agent: Agent, line: string, images: readonly EncodedImageAttachment[], signal: AbortSignal, ): Promise<CommandExecution | undefined>
191
+ @Remote async execute( agent: Agent, line: string, submittedAttachments: readonly CommandSubmitAttachment[], signal: AbortSignal, ): Promise<CommandExecution | undefined>
183
192
  ```
184
193
 
185
- Types: [Agent](core.zh.md) · [EncodedImageAttachment](attachment.zh.md)
194
+ Types: [Agent](core.zh.md)
186
195
 
187
196
  Source: [`packages/interaction/commands/src/index.ts`](../../packages/interaction/commands/src/index.ts)
188
197
 
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write docs/subsystems/conversation.md
5
- conversation.md: df1476537b95690ae2055f367e8586653b99a9a9
6
- conversation.zh.md: 784f52975cbb829d8914ef630aa1041693e1de62
5
+ conversation.md: 61e76e1b057be7b47150bd623b3e04400b50e394
6
+ conversation.zh.md: 5b54625fcfe9ac451e79a42c387a5cf0836ee39a
@@ -8,12 +8,12 @@ This page defines the data model and the extension path for a business-owned Con
8
8
 
9
9
  ## Data model and ownership
10
10
 
11
- The Session Controller owns the contiguous loaded logical-event window. Each `SessionEventLikeEntry` is either `{ type: 'event', event: SessionEvent }` or `{ type: 'chunks', event: ChunkRowEvent }`; both inner events expose `type`, `seq`, `time`, and `data`. `ui-conversation` passes these entries to the assembler without opening a second history stream, converting records, or expanding packed members. One `ConversationNodeAssembler` per Session applies every registered Definition and publishes an independent source for each registered view target.
11
+ The Session Controller owns the contiguous loaded logical-event window. Each `SessionEventLikeEntry` is either `{ type: 'event', event: SessionEvent }` for one durable event or `{ type: 'transient', event: AssistantLiveChunkEvent }` for one Client-only `assistant/live-chunk` presentation. Both inner events expose `type`, `seq`, `time`, and `data`. `ui-conversation` passes these entries to the assembler without opening a second history stream. One `ConversationNodeAssembler` per Session applies every registered Definition and publishes an independent source for each registered view target.
12
12
 
13
13
  | Concept | Owner and purpose |
14
14
  |---|---|
15
- | Event Definition | A business package matches one standard event or packed Assistant run at a time, correlates it by stable `(kind, id)`, folds deterministic State, and optionally materializes one target node. |
16
- | Context | The engine-owned ordered Matches and current State for one `(kind, id)`. A packed run occupies one update Match; update-only evidence may remain pending until pagination supplies its unique scalar start. |
15
+ | Event Definition | A business package matches one durable or Client-only transient event at a time, correlates it by stable `(kind, id)`, folds deterministic State, and optionally materializes one target node. |
16
+ | Context | The engine-owned ordered Matches and current State for one `(kind, id)`. A transient event occupies one update Match; update-only evidence may remain pending until pagination supplies its unique durable start. |
17
17
  | Location | The engine-owned Session, Turn, or Step coordinates derived from durable boundary events. Definitions may publish typed data onto one Turn or Step. |
18
18
  | View Definition | A target package creates one incremental builder per Session and owns the final snapshot type for that target. |
19
19
  | View | A Slot entry such as Chat or Trajectory reads only its target snapshot and renders target-owned nodes. |
@@ -42,7 +42,7 @@ Use the producer-owned branded id type across the process boundary. Put the `Ses
42
42
 
43
43
  Incremental events are supported. Prefer whole-value checkpoints when the producer can emit them cheaply, because they remain useful when the start is outside the loaded window. Each delta must carry the stable id and produce deterministic State when replayed in ascending log `seq`; it must not depend on live-only memory. If the current history window contains only updates, the assembler keeps a pending Context and builds no State until an older page supplies the start. If the product must render before the start is loaded, a terminal or checkpoint event must carry enough whole fallback state for the Definition to build that result directly; do not recover it by scanning unrelated events.
44
44
 
45
- Historical runs of consecutive same-block `assistant/chunk` deltas arrive as `chunkrow/text-chunks`, `chunkrow/reasoning-chunks`, or `chunkrow/tool-call-chunks`. Their top-level `seq` and `time` identify the first logical member, and their `data` retains each fragment and timestamp gap. These Client-only events can only be updates; `start()` receives a standard `SessionEvent`. A Definition that consumes Assistant deltas handles the relevant packed tags in the same `match()` and `update()` methods, while other Definitions return `null` without expanding the run.
45
+ Live Assistant deltas arrive as Client-only `assistant/live-chunk` updates. Reconnect baselines expand the active process-local compact stream into the same transient events, while durable `assistant/message` and `assistant/attempt` events embed complete compact streams for history replay. Transient events can only be updates; `start()` receives a standard `SessionEvent`. A Definition that consumes Assistant output handles live chunks and durable settlements in the same `match()` and `update()` methods, while unrelated Definitions return `null` without expanding a stream.
46
46
 
47
47
  ## Definition and typed Chat payload
48
48
 
@@ -8,12 +8,12 @@ Conversation 是 Client `SessionEventLikeEntry` window 与浏览器 view 之间
8
8
 
9
9
  ## 数据模型与所有权
10
10
 
11
- Session Controller 拥有连续的已加载逻辑 event window。每个 `SessionEventLikeEntry` 都是 `{ type: 'event', event: SessionEvent }` `{ type: 'chunks', event: ChunkRowEvent }`;两种内部 event 都公开 `type`、`seq`、`time` 与 `data`。`ui-conversation` 把这些 entry 直接交给 assembler,不另开 history stream、不转换 record,也不展开 packed member。每个 Session 对应一个 `ConversationNodeAssembler`,它应用所有已注册 Definition,并为每个已注册 view target 发布独立 source。
11
+ Session Controller 拥有连续的已加载逻辑 event window。每个 `SessionEventLikeEntry` 要么是表示一个持久事件的 `{ type: 'event', event: SessionEvent }`,要么是表示一个 Client-only `assistant/live-chunk` 呈现的 `{ type: 'transient', event: AssistantLiveChunkEvent }`;两种内部 event 都公开 `type`、`seq`、`time` 与 `data`。`ui-conversation` 把这些 entry 直接交给 assembler,不另开 history stream。每个 Session 对应一个 `ConversationNodeAssembler`,它应用所有已注册 Definition,并为每个已注册 view target 发布独立 source。
12
12
 
13
13
  | 概念 | Owner 与用途 |
14
14
  |---|---|
15
- | Event Definition | 业务包一次匹配一条标准 event 或一个 packed Assistant run,以稳定 `(kind, id)` 关联输入、折叠确定性 State,并可选择 materialize 一个 target node。 |
16
- | Context | Engine 为一个 `(kind, id)` 拥有的有序 Match 与当前 State。一个 packed run 只占一个 update Match;只有 update 的证据可以保持 pending,直到分页补齐其唯一 scalar start。 |
15
+ | Event Definition | 业务包一次匹配一个持久 event Client-only 瞬态 event,以稳定 `(kind, id)` 关联输入、折叠确定性 State,并可选择 materialize 一个 target node。 |
16
+ | Context | Engine 为一个 `(kind, id)` 拥有的有序 Match 与当前 State。一个瞬态 event 只占一个 update Match;只有 update 的证据可以保持 pending,直到分页补齐其唯一持久 start。 |
17
17
  | Location | Engine 根据持久 boundary event 推导的 Session、Turn 或 Step 坐标。Definition 可以向一个 Turn 或 Step 发布类型化数据。 |
18
18
  | View Definition | Target 包为每个 Session 创建一个增量 builder,并拥有该 target 的最终 snapshot 类型。 |
19
19
  | View | Chat 或 Trajectory 等 Slot entry 只读取自身 target snapshot,并渲染 target 自有 node。 |
@@ -42,7 +42,7 @@ shell 拥有 View 选择,并在 binding 创建、被选为 current 或 View ro
42
42
 
43
43
  系统支持增量事件。如果生产方能以较低成本发出 whole-value checkpoint,应优先采用,因为 start 位于已加载窗口之外时它仍可直接使用。每条 delta 都必须携带稳定 id,并且按照日志 `seq` 升序回放时能够确定性地产生 State;它不能依赖只存在于实时内存中的状态。如果当前历史窗口只有 update,Assembler 会保留一个 pending Context,并在更早分页补齐 start 前不构造 State。如果产品必须在 start 尚未加载时渲染,terminal 或 checkpoint 事件就必须携带足够的完整 fallback 状态,让 Definition 能直接构造结果;不要通过扫描无关事件恢复它。
44
44
 
45
- 连续且属于同一 block 的历史 `assistant/chunk` delta 会以 `chunkrow/text-chunks`、`chunkrow/reasoning-chunks` `chunkrow/tool-call-chunks` 到达。顶层 `seq` 与 `time` 表示首个逻辑成员,`data` 保留每个 fragment timestamp gap。这些 Client-only event 只能充当 update;`start()` 只接收标准 `SessionEvent`。消费 Assistant delta Definition 在同一组 `match()` 与 `update()` 方法里处理相关 packed tag,其他 Definition 直接返回 `null`,无需展开该 run
45
+ 实时 Assistant delta 作为 Client-only `assistant/live-chunk` update 到达。重连 baseline 会把活跃的进程内紧凑 stream 展开为相同的瞬态 event,持久 `assistant/message` 与 `assistant/attempt` event 则嵌入完整紧凑 stream 供历史回放。瞬态 event 只能充当 update;`start()` 只接收标准 `SessionEvent`。消费 Assistant 输出的 Definition 在同一组 `match()` 与 `update()` 方法里处理 live chunk 与持久 settlement,其他 Definition 直接返回 `null`,无需展开 stream
46
46
 
47
47
  ## Definition 与类型化 Chat payload
48
48
 
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write docs/subsystems/core.md
5
- core.md: 170268351454643544d5ab20e3d4b6a8fd8c6c69
6
- core.zh.md: 048f7ed5f2136bb583c0308635e5cddde2b96785
5
+ core.md: 2f907062e89b9af12cd1b47d52d195866df62254
6
+ core.zh.md: a5649032b81cf4adf49253f74c46c46beb800969
@@ -152,6 +152,44 @@ interface Agent {
152
152
  type AgentStatus = 'idle' | 'running'
153
153
  ```
154
154
 
155
+ ```ts type-equiv
156
+ /** One process-local live assistant streaming publication. */
157
+ type AssistantStreamFrame =
158
+ | {
159
+ readonly type: 'start'
160
+ readonly attemptId: LlmAttemptId
161
+ /** Monotone within one attached Agent lifecycle; replacement restarts at 1. */
162
+ readonly revision: number
163
+ readonly turn: number
164
+ readonly step: number
165
+ }
166
+ | {
167
+ readonly type: 'chunk'
168
+ readonly attemptId: LlmAttemptId
169
+ readonly revision: number
170
+ /** Dense zero-based position within the attempt. */
171
+ readonly index: number
172
+ /** Safe-integer timestamp reused by the durable embedded stream. */
173
+ readonly time: number
174
+ readonly chunk: StreamChunk
175
+ }
176
+ | {
177
+ readonly type: 'end'
178
+ readonly attemptId: LlmAttemptId
179
+ readonly revision: number
180
+ /** Number of chunk frames emitted by this attempt. */
181
+ readonly index: number
182
+ /** Durable settlement committed before this notification, or live abandonment without one. */
183
+ readonly outcome:
184
+ | {
185
+ readonly kind: 'committed'
186
+ readonly eventType: 'assistant/message' | 'assistant/attempt'
187
+ readonly seq: SessionSeq
188
+ }
189
+ | { readonly kind: 'abandoned' }
190
+ }
191
+ ```
192
+
155
193
  `running` describes the driver-wide drain interval and may span consecutive queued turns; it does not prove a turn is still open. Disposal removes the agent from the registry and emits `agent/disposed`; it is not a terminal status value. `followup()` returns no handle: its `MessageId` identifies durable inbox insertion, claim, and discard facts, not a later assistant output or turn ending. `whenIdle()` observes the whole agent, so callers may call a receipt-to-idle interval a run only when they explicitly own that interval ([decision](../../.agents/notes/implemented/architecture/2026-07-30-followup-enqueue-and-owned-runs.md)).
156
194
 
157
195
  ```ts type-equiv
@@ -252,7 +290,7 @@ type SessionStartSource = 'startup' | 'resume' | 'clear' | 'compact'
252
290
 
253
291
  A `Session` is an **append-only log** of typed `SessionEvent`s — the single source of truth. The LLM message history is *derived* from the log (`deriveMessages()`), not stored separately. Every entry carries a monotonic `seq`, a `time`, and a `type`-discriminated `data` payload; surface variants may also list cited earlier events in `sourceEventSeqs` and carry a `surfaceOp`.
254
292
 
255
- The `SessionEvent` envelope's exact conditional fields, the twelve core event variants (`turn/start`, `turn/end`, `step/start`, `step/end`, `user/message`, `assistant/chunk`, `assistant/message`, `tool/call`, `tool/result`, `request/header`, `request/context`, `session/end-seed`), the `deriveMessages()` projection rules, the `TurnEndReason` reasons, and the execution-enclosure and standalone-event rules are on **[session.md](session.md)**. How the log is made durable — the `SessionPersistence` interface, JSONL provider, `session/flush` checkpoint, crash recovery, and `SessionHeader` — is on **[persistence.md](persistence.md)**.
293
+ The `SessionEvent` envelope's exact conditional fields, the twelve core event variants (`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`), the `deriveMessages()` projection rules, the `TurnEndReason` reasons, and the execution-enclosure and standalone-event rules are on **[session.md](session.md)**. How the log is made durable — the `SessionPersistence` interface, JSONL provider, `session/flush` checkpoint, crash recovery, and `SessionHeader` — is on **[persistence.md](persistence.md)**.
256
294
 
257
295
  ## `ToolDefinition`
258
296
 
@@ -804,6 +842,29 @@ Source: [`packages/core/agent/src/index.ts`](../../packages/core/agent/src/index
804
842
 
805
843
  ### `agent/*` events
806
844
 
845
+ <a id="agentassistant-stream--emit"></a>
846
+
847
+ #### `agent/assistant-stream` — emit
848
+
849
+ Process-local assistant-stream publication. Chunk frames are transient; the loop appends one final v2 `assistant/message` or `assistant/attempt` with the same stream before a committed end frame.
850
+
851
+ ```ts cordis-catalog
852
+ /**
853
+ * Process-local assistant-stream publication. Chunk frames are transient;
854
+ * the loop appends one final v2 `assistant/message` or `assistant/attempt`
855
+ * with the same stream before a committed end frame.
856
+ * @param payload.agent - the agent whose attempt produced the frame.
857
+ * @param payload.frame - one ordered start, chunk, or end publication.
858
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
859
+ * @mode emit
860
+ */
861
+ 'agent/assistant-stream'(this: Scoped<Agent>, payload: { agent: Agent; frame: AssistantStreamFrame }): void
862
+ ```
863
+
864
+ Types: [Scoped](scope.md)
865
+
866
+ Source: [`packages/core/agent/src/runtime-types.ts`](../../packages/core/agent/src/runtime-types.ts)
867
+
807
868
  <a id="agentcreated--emit"></a>
808
869
 
809
870
  #### `agent/created` — emit
@@ -156,6 +156,44 @@ interface Agent {
156
156
  type AgentStatus = 'idle' | 'running'
157
157
  ```
158
158
 
159
+ ```ts type-equiv
160
+ /** One process-local live assistant streaming publication. */
161
+ type AssistantStreamFrame =
162
+ | {
163
+ readonly type: 'start'
164
+ readonly attemptId: LlmAttemptId
165
+ /** Monotone within one attached Agent lifecycle; replacement restarts at 1. */
166
+ readonly revision: number
167
+ readonly turn: number
168
+ readonly step: number
169
+ }
170
+ | {
171
+ readonly type: 'chunk'
172
+ readonly attemptId: LlmAttemptId
173
+ readonly revision: number
174
+ /** Dense zero-based position within the attempt. */
175
+ readonly index: number
176
+ /** Safe-integer timestamp reused by the durable embedded stream. */
177
+ readonly time: number
178
+ readonly chunk: StreamChunk
179
+ }
180
+ | {
181
+ readonly type: 'end'
182
+ readonly attemptId: LlmAttemptId
183
+ readonly revision: number
184
+ /** Number of chunk frames emitted by this attempt. */
185
+ readonly index: number
186
+ /** Durable settlement committed before this notification, or live abandonment without one. */
187
+ readonly outcome:
188
+ | {
189
+ readonly kind: 'committed'
190
+ readonly eventType: 'assistant/message' | 'assistant/attempt'
191
+ readonly seq: SessionSeq
192
+ }
193
+ | { readonly kind: 'abandoned' }
194
+ }
195
+ ```
196
+
159
197
  `running` 描述整个驱动器的排空区间,可能跨越连续的排队轮次;它不能证明某个轮次仍然打开。dispose 会把 agent 从注册表移除并发出 `agent/disposed`;它不是一个终态 status 值。`followup()` 不返回句柄:其 `MessageId` 标识的是持久的 inbox 插入、认领与丢弃事实,而非之后的助手输出或轮次结束。`whenIdle()` 观察的是整个 agent,因此只有当调用方明确拥有从回执到空闲的这段区间时,才能把它称为一次 run([决策](../../.agents/notes/implemented/architecture/2026-07-30-followup-enqueue-and-owned-runs.zh.md))。
160
198
 
161
199
  ```ts type-equiv
@@ -260,7 +298,7 @@ type SessionStartSource = 'startup' | 'resume' | 'clear' | 'compact'
260
298
 
261
299
  `Session` 是一份类型化 `SessionEvent` 的**仅追加日志**——唯一的真源。LLM 消息历史从日志*派生*(`deriveMessages()`),而非单独存储。每个条目携带单调的 `seq`、`time` 与按 `type` 判别的 `data` payload;surface 变体还可以在 `sourceEventSeqs` 中列出被引用的较早事件,并携带 `surfaceOp`。
262
300
 
263
- `SessionEvent` 信封的确切条件字段、十二种核心事件变体(`turn/start`、`turn/end`、`step/start`、`step/end`、`user/message`、`assistant/chunk`、`assistant/message`、`tool/call`、`tool/result`、`request/header`、`request/context`、`session/end-seed`)、`deriveMessages()` 投影规则、`TurnEndReason` 原因以及执行封闭和独立事件规则都在 **[session.md](session.zh.md)** 中。日志如何持久化——`SessionPersistence` 接口、JSONL provider、`session/flush` 检查点、崩溃恢复与 `SessionHeader`——则在 **[persistence.md](persistence.zh.md)** 中。
301
+ `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.zh.md)** 中。日志如何持久化——`SessionPersistence` 接口、JSONL provider、`session/flush` 检查点、崩溃恢复与 `SessionHeader`——则在 **[persistence.md](persistence.zh.md)** 中。
264
302
 
265
303
  ## `ToolDefinition`
266
304
 
@@ -814,6 +852,29 @@ Source: [`packages/core/agent/src/index.ts`](../../packages/core/agent/src/index
814
852
 
815
853
  ### `agent/*` events
816
854
 
855
+ <a id="agentassistant-stream--emit"></a>
856
+
857
+ #### `agent/assistant-stream` — emit
858
+
859
+ Process-local assistant-stream publication. Chunk frames are transient; the loop appends one final v2 `assistant/message` or `assistant/attempt` with the same stream before a committed end frame.
860
+
861
+ ```ts cordis-catalog
862
+ /**
863
+ * Process-local assistant-stream publication. Chunk frames are transient;
864
+ * the loop appends one final v2 `assistant/message` or `assistant/attempt`
865
+ * with the same stream before a committed end frame.
866
+ * @param payload.agent - the agent whose attempt produced the frame.
867
+ * @param payload.frame - one ordered start, chunk, or end publication.
868
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
869
+ * @mode emit
870
+ */
871
+ 'agent/assistant-stream'(this: Scoped<Agent>, payload: { agent: Agent; frame: AssistantStreamFrame }): void
872
+ ```
873
+
874
+ Types: [Scoped](scope.zh.md)
875
+
876
+ Source: [`packages/core/agent/src/runtime-types.ts`](../../packages/core/agent/src/runtime-types.ts)
877
+
817
878
  <a id="agentcreated--emit"></a>
818
879
 
819
880
  #### `agent/created` — emit
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write docs/subsystems/llm-streaming.md
5
- llm-streaming.md: 6867ae292d77474bcedc1466ae0ce6b1fc1c92d3
6
- llm-streaming.zh.md: b75e24f2e9010b4fb08c035f14bc4e91dc3971ef
5
+ llm-streaming.md: e88741c6bb63b819a314f74fc425c89b865b5906
6
+ llm-streaming.zh.md: 4bc7fc6124e77c7e3bbbc474bff9bc79fbd8eb27
@@ -23,12 +23,13 @@ interface ContentBlockMap {
23
23
  'text': TextBlock
24
24
  'reasoning': ReasoningBlock
25
25
  'image': ImageBlock
26
+ 'file': FileBlock
26
27
  'tool-call': ToolCallBlock
27
28
  'tool-result': ToolResultBlock
28
29
  }
29
30
  ```
30
31
 
31
- The block interfaces (full fields in source): `TextBlock` (`text`), `ReasoningBlock` (thinking, distinct from visible text), `ImageBlock` (a durable [image attachment](attachment.md)), `ToolCallBlock` (`id: ToolCallId`, `name`, raw-JSON `arguments`), and `ToolResultBlock` (`toolCallId`, nested `content: ContentBlock[]`, `isError?`). `ContentBlock = ContentBlockMap[ContentBlockType]`. A new modality belongs in the merge-extensible map only when its adapter, UI, compaction, and durable replay paths honor it.
32
+ The block interfaces (full fields in source): `TextBlock` (`text`), `ReasoningBlock` (thinking, distinct from visible text), `ImageBlock` (a durable [image attachment](attachment.md)), `FileBlock` (a durable verbatim [file attachment](attachment.md) that request assembly projects to handle text for every route), `ToolCallBlock` (`id: ToolCallId`, `name`, raw-JSON `arguments`), and `ToolResultBlock` (`toolCallId`, nested `content: ContentBlock[]`, `isError?`). `ContentBlock = ContentBlockMap[ContentBlockType]`. A new modality belongs in the merge-extensible map only when its adapter, UI, compaction, and durable replay paths honor it.
32
33
 
33
34
  Image access belongs to request serialization rather than the durable attachment or deterministic request-image version. `resolveImageAttachmentAccess()` combines the attachment provider's optional host object path with a mapping supplied by the consumer for the current tool execution filesystem. The result is available only for that request and does not participate in `variantId`.
34
35
 
@@ -216,6 +217,16 @@ type StreamChunk =
216
217
  }
217
218
  ```
218
219
 
220
+ <a id="compact-assistant-streams"></a>
221
+
222
+ ## Compact Assistant streams
223
+
224
+ `AssistantStreamAccumulator` pairs each `StreamChunk` with its original safe-integer timestamp and produces `AssistantStreamRecord[]`. Consecutive text, reasoning, or tool-argument deltas for the same block become one record with `time0`, exact timestamp gaps, and one array entry per original delta; every other chunk stays a timestamped raw record. This representation removes repeated event envelopes without joining token boundaries or dropping terminal, usage, block, failure, or replay facts.
225
+
226
+ `snapshot()` returns a detached immutable stream. `expandAssistantStream()` strictly checks record keys, member counts, indexes, timestamps, tool-call identity, and lossless JSON before recreating the exact timed chunk sequence. The Session log embeds this stream in `assistant/message` for a surface result or `assistant/attempt` for an attempt with no surface message.
227
+
228
+ Process-local `agent/assistant-stream` frames carry live presentation. Durable replay, telemetry, token accounting, and historical UI assembly expand the embedded settlement instead of treating live frames as persisted facts.
229
+
219
230
  ## `LlmFailure`
220
231
 
221
232
  Every thrown or in-band final-adapter failure normalizes to one serializable provider-neutral payload. `providerRetryAfterMs` is a validated positive delay requested by the provider, not a retry decision; `ProviderRequestId` is an opaque branded string for diagnostics.
@@ -280,7 +291,7 @@ Every adapter MUST obey these, and every consumer may rely on them:
280
291
 
281
292
  - **`usage` before `finish`, nothing after `finish`.** Defer both to the provider's end-of-stream marker so a trailing usage-only chunk can't violate the ordering.
282
293
  - **Tool-call `arguments` stay raw JSON strings end-to-end.** Partial fragments stream via `argumentsDelta`; a provider that hands back parsed objects re-stringifies at `block-end`.
283
- - **Two sanctioned error paths, one `LlmFailure` type.** A failure may either THROW from `stream()` (transport/protocol errors) **or** end the stream with `finish {kind:'error'|'aborted', failure}` (provider in-band errors, for adapters that can't throw mid-stream). `LlmError.failure` carries the same `LlmFailure`. After the call selects its adapter, the stream preserves the exact thrown `Error` object and associates immutable facts plus the serving registration's immutable retry policy with that call; the agent loop closes the failed step and offers the error, facts, immutable prior-retried facts, serving policy, and turn signal to `agent/request-error`. A handling listener returns `{ kind: 'retry' }` after its awaited repair; absent recovery the structured failure becomes the turn error, and no normal assistant message or tool side effect is committed for that attempt.
294
+ - **Two sanctioned error paths, one `LlmFailure` type.** A failure may either THROW from `stream()` (transport/protocol errors) **or** end the stream with `finish {kind:'error'|'aborted', failure}` (provider in-band errors, for adapters that can't throw mid-stream). `LlmError.failure` carries the same `LlmFailure`. After the call selects its adapter, the stream preserves the exact thrown `Error` object and associates immutable facts plus the serving registration's immutable retry policy with that call; the agent loop commits the attempt stream as `assistant/attempt`, closes the failed step, and offers the error, facts, immutable prior-retried facts, serving policy, and turn signal to `agent/request-error`. A handling listener returns `{ kind: 'retry' }` after its awaited repair; absent recovery the structured failure becomes the turn error, and no surface Assistant message or tool side effect is committed for that attempt.
284
295
  - **One adapter call is one provider attempt.** Adapters disable library retries. Agent-level recovery opens another durable numbered turn; direct `ctx.llm.stream()` callers remain single-attempt.
285
296
  - **Provider stalls are bounded at the transport.** Both shipping remote adapters expose positive finite `streamIdleTimeoutMs` with a five-minute default. The watchdog arms only while iterator `next()` is outstanding, uses one stable signal for the whole request, maps its own expiry to `TIMEOUT`, and keeps an earlier caller abort as `ABORTED`.
286
297
  - **Context overflow has one canonical code.** Both DeepSeek adapters classify explicit provider detail through `isContextWindowExceededError()` and surface `CONTEXT_WINDOW_EXCEEDED`, whether the failure arrives as a thrown HTTP `LlmError` or an in-band finish error. Consumers route on the code, never provider text.
@@ -951,6 +962,14 @@ providerRetryPolicy(provider: string): ResolvedRetryPolicy
951
962
  */
952
963
  imageRequestPricing(provider: string, model: string): LlmImageRequestPricing | undefined
953
964
 
965
+ /**
966
+ * Resolve the exact text one durable file occurrence contributes to every
967
+ * provider request in the current execution environment.
968
+ * @param ref - durable verbatim file reference from model history.
969
+ * @returns the same deterministic handle text used at adapter dispatch.
970
+ */
971
+ fileRequestText(ref: FileAttachmentRef): string
972
+
954
973
  /**
955
974
  * Discover models advertised by one registered provider. Catalog membership
956
975
  * is advisory and never changes routing or request validation.
@@ -1006,6 +1025,8 @@ async prepareCall(config: LlmCallConfig, signal?: AbortSignal): Promise<Prepared
1006
1025
  stream(options: GenerateOptions): AsyncIterable<StreamChunk>
1007
1026
  ```
1008
1027
 
1028
+ Types: [FileAttachmentRef](attachment.md)
1029
+
1009
1030
  Source: [`packages/llm/llm/src/index.ts`](../../packages/llm/llm/src/index.ts)
1010
1031
 
1011
1032
  <a id="llm-events"></a>
@@ -23,12 +23,13 @@ interface ContentBlockMap {
23
23
  'text': TextBlock
24
24
  'reasoning': ReasoningBlock
25
25
  'image': ImageBlock
26
+ 'file': FileBlock
26
27
  'tool-call': ToolCallBlock
27
28
  'tool-result': ToolResultBlock
28
29
  }
29
30
  ```
30
31
 
31
- 各块接口(完整字段见源码):`TextBlock`(`text`)、`ReasoningBlock`(thinking,区别于可见文本)、`ImageBlock`(一个持久的[图片附件](attachment.zh.md))、`ToolCallBlock`(`id: ToolCallId`、`name`、原始 JSON `arguments`),以及 `ToolResultBlock`(`toolCallId`、嵌套 `content: ContentBlock[]`、`isError?`)。`ContentBlock = ContentBlockMap[ContentBlockType]`。仅当适配器、UI、压缩(compaction)和持久回放路径均支持某种新模态时,才将其纳入可合并扩展的 map。
32
+ 各块接口(完整字段见源码):`TextBlock`(`text`)、`ReasoningBlock`(thinking,区别于可见文本)、`ImageBlock`(一个持久的[图片附件](attachment.zh.md))、`FileBlock`(一个持久的原样[文件附件](attachment.zh.md),请求组装对每条路由都把它投影为 handle 文本)、`ToolCallBlock`(`id: ToolCallId`、`name`、原始 JSON `arguments`),以及 `ToolResultBlock`(`toolCallId`、嵌套 `content: ContentBlock[]`、`isError?`)。`ContentBlock = ContentBlockMap[ContentBlockType]`。仅当适配器、UI、压缩(compaction)和持久回放路径均支持某种新模态时,才将其纳入可合并扩展的 map。
32
33
 
33
34
  图片访问方式属于请求序列化,不属于持久附件或确定性请求图片版本。`resolveImageAttachmentAccess()` 把附件提供方可选的宿主对象路径,与消费方为当前工具执行文件系统提供的映射组合起来。结果只适用于本次请求,不参与 `variantId`。
34
35
 
@@ -216,6 +217,16 @@ type StreamChunk =
216
217
  }
217
218
  ```
218
219
 
220
+ <a id="compact-assistant-streams"></a>
221
+
222
+ ## 紧凑 Assistant stream
223
+
224
+ `AssistantStreamAccumulator` 把每个 `StreamChunk` 与其原始安全整数时间戳配对,并生成 `AssistantStreamRecord[]`。同一 block 的连续 text、reasoning 或 tool argument delta 会变成一个 record,使用 `time0`、精确时间戳间隔和每个原始 delta 对应的一个数组成员;其他 chunk 保留为带时间戳的 raw record。该表示会移除重复 event envelope,但不会合并 token 边界,也不会丢弃 terminal、usage、block、failure 或 replay 事实。
225
+
226
+ `snapshot()` 返回分离且不可变的 stream。`expandAssistantStream()` 会严格检查 record key、成员数、index、时间戳、tool-call identity 与无损 JSON,再重建精确的带时间 chunk 序列。Session 日志会把该 stream 嵌入作为 surface result 的 `assistant/message`,或嵌入没有 surface message 的 `assistant/attempt`。
227
+
228
+ 进程本地 `agent/assistant-stream` frame 承载实时呈现。持久回放、遥测、token 记账与历史 UI 组装会展开嵌入式 settlement,而不会把 live frame 当作持久事实。
229
+
219
230
  <a id="llmfailure"></a>
220
231
 
221
232
  ## `LlmFailure`
@@ -282,7 +293,7 @@ interface LlmImageRequestPricing {
282
293
 
283
294
  - **`usage` 在 `finish` 之前,`finish` 之后不再有任何分片。** 将两者都推迟到提供方的流结束标记,这样尾部的 usage-only 分片就不会违反顺序。
284
295
  - **工具调用的 `arguments` 全程保持原始 JSON 字符串。** 部分片段通过 `argumentsDelta` 流式传输;如果提供方返回的是已解析的对象,适配器在 `block-end` 时重新序列化为字符串。
285
- - **两条受支持的错误路径,共用一个 `LlmFailure` 类型。** 失败可以从 `stream()` 抛出(传输/协议错误),**或者**以 `finish {kind:'error'|'aborted', failure}` 结束流(无法在流中途抛异常的适配器用它表示提供方带内错误)。`LlmError.failure` 携带同一个 `LlmFailure`。调用选定适配器后,流会保留被抛出的确切 `Error` 对象,并将不可变事实以及实际服务注册所对应的不可变重试策略关联到该调用;agent loop(智能体循环)关闭失败步骤,再把错误、事实、不可变的先前已重试失败事实、实际服务策略和轮次信号提供给 `agent/request-error`。处理该错误的 listener 在其 await 的修复完成后返回 `{ kind: 'retry' }`;若未恢复,结构化失败会成为轮次错误,并且该次尝试不会提交正常 assistant 消息或工具副作用。
296
+ - **两条受支持的错误路径,共用一个 `LlmFailure` 类型。** 失败可以从 `stream()` 抛出(传输/协议错误),**或者**以 `finish {kind:'error'|'aborted', failure}` 结束流(无法在流中途抛异常的适配器用它表示提供方带内错误)。`LlmError.failure` 携带同一个 `LlmFailure`。调用选定适配器后,流会保留被抛出的确切 `Error` 对象,并将不可变事实以及实际服务注册所对应的不可变重试策略关联到该调用;agent loop(智能体循环)先把 attempt stream 提交为 `assistant/attempt`,再关闭失败步骤,并把错误、事实、不可变的先前已重试失败事实、实际服务策略和轮次信号提供给 `agent/request-error`。处理该错误的 listener 在其 await 的修复完成后返回 `{ kind: 'retry' }`;若未恢复,结构化失败会成为轮次错误,并且该次 attempt 不会提交 surface Assistant message 或工具副作用。
286
297
  - **一次适配器调用就是一次提供方尝试。** 适配器禁用库重试。agent 层恢复会打开另一个持久、带编号的轮次;直接调用 `ctx.llm.stream()` 的调用方仍然只尝试一次。
287
298
  - **提供方停顿在传输层受到时限约束。** 两个已交付的远程适配器都暴露正数且有限的 `streamIdleTimeoutMs`,默认五分钟。watchdog 只在 iterator `next()` 尚未完成时启动,整个请求使用同一个稳定 signal,把自身到期映射为 `TIMEOUT`,并把更早发生的调用方中止保留为 `ABORTED`。
288
299
  - **上下文溢出只有一个规范 code。** 两个 DeepSeek 适配器都通过 `isContextWindowExceededError()` 对提供方的显式细节分类并暴露 `CONTEXT_WINDOW_EXCEEDED`,无论失败以抛出的 HTTP `LlmError` 还是带内 finish error 到达。消费方按 code 路由,绝不依赖提供方文本。
@@ -957,6 +968,14 @@ providerRetryPolicy(provider: string): ResolvedRetryPolicy
957
968
  */
958
969
  imageRequestPricing(provider: string, model: string): LlmImageRequestPricing | undefined
959
970
 
971
+ /**
972
+ * Resolve the exact text one durable file occurrence contributes to every
973
+ * provider request in the current execution environment.
974
+ * @param ref - durable verbatim file reference from model history.
975
+ * @returns the same deterministic handle text used at adapter dispatch.
976
+ */
977
+ fileRequestText(ref: FileAttachmentRef): string
978
+
960
979
  /**
961
980
  * Discover models advertised by one registered provider. Catalog membership
962
981
  * is advisory and never changes routing or request validation.
@@ -1012,6 +1031,8 @@ async prepareCall(config: LlmCallConfig, signal?: AbortSignal): Promise<Prepared
1012
1031
  stream(options: GenerateOptions): AsyncIterable<StreamChunk>
1013
1032
  ```
1014
1033
 
1034
+ Types: [FileAttachmentRef](attachment.zh.md)
1035
+
1015
1036
  Source: [`packages/llm/llm/src/index.ts`](../../packages/llm/llm/src/index.ts)
1016
1037
 
1017
1038
  <a id="llm-events"></a>
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write docs/subsystems/persistence.md
5
- persistence.md: 4f6069b8a926f8fe24d2271650d45dc8377fa355
6
- persistence.zh.md: 6f8430868a001d0da4755579d2e667097d254ac6
5
+ persistence.md: 7cce80f43591610c1e0667d480971dbd8f3cd4f7
6
+ persistence.zh.md: 73169ec2f3a58f06a7355922b0e0f7e4e82944c7