dsh-plugin-dev-kb 1.0.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/README.en.md +6 -6
  3. package/README.md +6 -6
  4. package/kb/INDEX.md +21 -5
  5. package/kb/README.md +11 -10
  6. package/kb/extra/AGENTS.md +4 -4
  7. package/kb/extra/cookbook/adding-a-remote-api.md +197 -0
  8. package/kb/extra/cookbook/adding-a-remote-api.zh.md +197 -0
  9. package/kb/extra/cookbook/adding-a-vendored-package.md +2 -2
  10. package/kb/extra/cookbook/adding-a-vendored-package.zh.md +2 -2
  11. package/kb/extra/deepseek-llm-api-wire-extensions.md +163 -0
  12. package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +163 -0
  13. package/kb/extra/development.md +8 -14
  14. package/kb/extra/development.zh.md +8 -14
  15. package/kb/extra/event-producer-consumer.md +55 -48
  16. package/kb/extra/event-producer-consumer.zh.md +58 -51
  17. package/kb/extra/glossary.md +1 -1
  18. package/kb/extra/glossary.zh.md +1 -1
  19. package/kb/extra/graph-atlas.md +0 -2
  20. package/kb/extra/graph-atlas.zh.md +0 -2
  21. package/kb/extra/i18n/README.md +4 -4
  22. package/kb/extra/i18n/README.zh.md +4 -4
  23. package/kb/extra/i18n/style-samples.md +2 -2
  24. package/kb/extra/module-graph.md +646 -926
  25. package/kb/extra/module-graph.zh.md +648 -928
  26. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.md +2 -2
  27. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.zh.md +2 -2
  28. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.md +2 -2
  29. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +2 -2
  30. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.md +2 -2
  31. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.zh.md +2 -2
  32. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +1 -1
  33. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +1 -1
  34. package/kb/extra/rescope.md +2 -2
  35. package/kb/extra/rescope.zh.md +2 -2
  36. package/kb/extra/subsystems/agent-team.md +28 -4
  37. package/kb/extra/subsystems/agent-team.zh.md +28 -4
  38. package/kb/extra/subsystems/attachment.md +168 -7
  39. package/kb/extra/subsystems/attachment.zh.md +168 -7
  40. package/kb/extra/subsystems/extensions.md +18 -0
  41. package/kb/extra/subsystems/extensions.zh.md +18 -0
  42. package/kb/extra/subsystems/feedback.md +4 -4
  43. package/kb/extra/subsystems/feedback.zh.md +4 -4
  44. package/kb/extra/subsystems/todo.md +32 -0
  45. package/kb/extra/subsystems/todo.zh.md +32 -0
  46. package/kb/extra/subsystems/webhook.md +70 -0
  47. package/kb/extra/subsystems/webhook.zh.md +70 -0
  48. package/kb/extra/testing.md +15 -10
  49. package/kb/extra/testing.zh.md +13 -8
  50. package/kb/extra/web-styling.md +4 -0
  51. package/kb/extra/web-styling.zh.md +4 -0
  52. package/kb/meta/search-index.json +309 -177
  53. package/kb/meta/site-pages.txt +183 -167
  54. package/kb/meta/source.json +5 -5
  55. package/kb/meta/topics.md +14 -6
  56. package/kb/site/develop/basic/publish.md +2 -2
  57. package/kb/site/develop/basic/tool.md +1 -1
  58. package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +5 -4
  59. package/kb/site/develop/framework/events.md +1 -1
  60. package/kb/site/develop/practice/dynamic-cordis.md +17 -0
  61. package/kb/site/develop/practice/llm-adapter.md +4 -3
  62. package/kb/site/en/develop/basic/publish.md +2 -2
  63. package/kb/site/en/develop/basic/tool.md +1 -1
  64. package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +5 -4
  65. package/kb/site/en/develop/framework/events.md +1 -1
  66. package/kb/site/en/develop/practice/dynamic-cordis.md +17 -0
  67. package/kb/site/en/develop/practice/llm-adapter.md +4 -3
  68. package/kb/site/en/guide/github-review.md +104 -0
  69. package/kb/site/en/guide/mcp-memory.md +103 -0
  70. package/kb/site/en/guide/network-proxy.md +87 -0
  71. package/kb/site/en/guide/providers.md +70 -17
  72. package/kb/site/en/guide/python-sdk.md +80 -34
  73. package/kb/site/en/guide/schedule.md +23 -0
  74. package/kb/site/en/reference/agent-lifecycle.md +6 -4
  75. package/kb/{extra → site/en/reference}/api-gateway.md +12 -10
  76. package/kb/site/en/reference/capability-seams.md +128 -73
  77. package/kb/site/en/reference/config-catalog.md +481 -360
  78. package/kb/site/en/reference/cookbook/adding-a-package.md +3 -4
  79. package/kb/site/en/reference/cookbook/adding-a-settings-card.md +12 -10
  80. package/kb/site/en/reference/cookbook/adding-a-tool.md +11 -4
  81. package/kb/site/en/reference/cookbook/adding-an-llm-adapter.md +1 -1
  82. package/kb/site/en/reference/cookbook/extension-cookbook.md +20 -17
  83. package/kb/site/en/reference/cordis-api/inherited.md +1 -1
  84. package/kb/site/en/reference/cordis-primer.md +2 -1
  85. package/kb/site/en/reference/index.md +30 -11
  86. package/kb/site/en/reference/persistence-catalog.md +148 -80
  87. package/kb/site/en/reference/subsystems/approval.md +10 -10
  88. package/kb/site/en/reference/subsystems/client-modules.md +58 -16
  89. package/kb/site/en/reference/subsystems/code-runtime.md +10 -6
  90. package/kb/site/en/reference/subsystems/commands.md +25 -16
  91. package/kb/site/en/reference/subsystems/compaction.md +11 -11
  92. package/kb/site/en/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +50 -24
  93. package/kb/site/en/reference/subsystems/core.md +156 -17
  94. package/kb/site/en/reference/subsystems/credentials.md +44 -3
  95. package/kb/site/en/reference/subsystems/filesystem.md +12 -2
  96. package/kb/site/en/reference/subsystems/goal.md +1 -1
  97. package/kb/site/en/reference/subsystems/index.md +7 -2
  98. package/kb/site/en/reference/subsystems/jobs.md +1 -1
  99. package/kb/site/en/reference/subsystems/llm-streaming.md +154 -12
  100. package/kb/site/en/reference/subsystems/permission-presets.md +6 -6
  101. package/kb/site/en/reference/subsystems/persistence.md +185 -175
  102. package/kb/site/en/reference/subsystems/plan.md +2 -2
  103. package/kb/site/en/reference/subsystems/sandbox.md +2 -0
  104. package/kb/site/en/reference/subsystems/schedule.md +9 -3
  105. package/kb/site/en/reference/subsystems/session-projection.md +115 -48
  106. package/kb/site/en/reference/subsystems/session-query.md +28 -14
  107. package/kb/site/en/reference/subsystems/session-reference.md +53 -8
  108. package/kb/site/en/reference/subsystems/session-telemetry.md +8 -8
  109. package/kb/site/en/reference/subsystems/session-title.md +6 -6
  110. package/kb/site/en/reference/subsystems/session.md +401 -99
  111. package/kb/site/en/reference/subsystems/settings.md +101 -6
  112. package/kb/site/en/reference/subsystems/skills.md +23 -0
  113. package/kb/site/en/reference/subsystems/slots.md +178 -0
  114. package/kb/site/en/reference/subsystems/spill.md +2 -2
  115. package/kb/site/en/reference/subsystems/storage.md +34 -3
  116. package/kb/site/en/reference/subsystems/subagent.md +122 -109
  117. package/kb/site/en/reference/subsystems/system-prompt.md +17 -4
  118. package/kb/site/en/reference/subsystems/token-meter.md +27 -12
  119. package/kb/site/en/reference/subsystems/tools.md +39 -39
  120. package/kb/site/en/reference/subsystems/typert.md +62 -55
  121. package/kb/site/en/reference/subsystems/user-questions.md +33 -33
  122. package/kb/site/en/reference/subsystems/web-client.md +98 -0
  123. package/kb/site/en/reference/subsystems/web-server.md +11 -5
  124. package/kb/site/en/reference/subsystems/web.md +7 -1
  125. package/kb/site/en/reference/subsystems/workspace.md +102 -9
  126. package/kb/site/en/reference/tool-catalog.md +86 -82
  127. package/kb/site/en/reference/tool-execution-pipeline.md +1 -1
  128. package/kb/site/guide/github-review.md +104 -0
  129. package/kb/site/guide/mcp-memory.md +103 -0
  130. package/kb/site/guide/network-proxy.md +87 -0
  131. package/kb/site/guide/providers.md +70 -17
  132. package/kb/site/guide/python-sdk.md +87 -41
  133. package/kb/site/guide/schedule.md +23 -0
  134. package/kb/site/reference/agent-lifecycle.md +6 -4
  135. package/kb/{extra/api-gateway.zh.md → site/reference/api-gateway.md} +12 -10
  136. package/kb/site/reference/capability-seams.md +128 -73
  137. package/kb/site/reference/config-catalog.md +481 -360
  138. package/kb/site/reference/cookbook/adding-a-package.md +3 -4
  139. package/kb/site/reference/cookbook/adding-a-settings-card.md +12 -10
  140. package/kb/site/reference/cookbook/adding-a-tool.md +11 -4
  141. package/kb/site/reference/cookbook/adding-an-llm-adapter.md +1 -1
  142. package/kb/site/reference/cookbook/extension-cookbook.md +20 -17
  143. package/kb/site/reference/cordis-api/inherited.md +1 -1
  144. package/kb/site/reference/cordis-primer.md +2 -1
  145. package/kb/site/reference/index.md +30 -11
  146. package/kb/site/reference/persistence-catalog.md +148 -80
  147. package/kb/site/reference/subsystems/approval.md +10 -10
  148. package/kb/site/reference/subsystems/client-modules.md +58 -16
  149. package/kb/site/reference/subsystems/code-runtime.md +10 -6
  150. package/kb/site/reference/subsystems/commands.md +25 -16
  151. package/kb/site/reference/subsystems/compaction.md +11 -11
  152. package/kb/site/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +50 -24
  153. package/kb/site/reference/subsystems/core.md +156 -17
  154. package/kb/site/reference/subsystems/credentials.md +44 -3
  155. package/kb/site/reference/subsystems/filesystem.md +12 -2
  156. package/kb/site/reference/subsystems/goal.md +1 -1
  157. package/kb/site/reference/subsystems/index.md +7 -2
  158. package/kb/site/reference/subsystems/jobs.md +1 -1
  159. package/kb/site/reference/subsystems/llm-streaming.md +154 -12
  160. package/kb/site/reference/subsystems/permission-presets.md +5 -5
  161. package/kb/site/reference/subsystems/persistence.md +184 -174
  162. package/kb/site/reference/subsystems/plan.md +2 -2
  163. package/kb/site/reference/subsystems/schedule.md +9 -3
  164. package/kb/site/reference/subsystems/session-projection.md +115 -48
  165. package/kb/site/reference/subsystems/session-query.md +28 -14
  166. package/kb/site/reference/subsystems/session-reference.md +53 -8
  167. package/kb/site/reference/subsystems/session-telemetry.md +8 -8
  168. package/kb/site/reference/subsystems/session-title.md +6 -6
  169. package/kb/site/reference/subsystems/session.md +401 -99
  170. package/kb/site/reference/subsystems/settings.md +101 -6
  171. package/kb/site/reference/subsystems/skills.md +23 -0
  172. package/kb/site/reference/subsystems/slots.md +178 -0
  173. package/kb/site/reference/subsystems/spill.md +2 -2
  174. package/kb/site/reference/subsystems/storage.md +34 -3
  175. package/kb/site/reference/subsystems/subagent.md +122 -109
  176. package/kb/site/reference/subsystems/system-prompt.md +17 -4
  177. package/kb/site/reference/subsystems/token-meter.md +27 -12
  178. package/kb/site/reference/subsystems/tools.md +39 -39
  179. package/kb/site/reference/subsystems/typert.md +62 -55
  180. package/kb/site/reference/subsystems/user-questions.md +33 -33
  181. package/kb/site/reference/subsystems/web-client.md +98 -0
  182. package/kb/site/reference/subsystems/web-server.md +11 -5
  183. package/kb/site/reference/subsystems/web.md +7 -1
  184. package/kb/site/reference/subsystems/workspace.md +102 -9
  185. package/kb/site/reference/tool-catalog.md +85 -81
  186. package/kb/site/reference/tool-execution-pipeline.md +1 -1
  187. package/package.json +2 -2
  188. package/skills/dsh-plugin-dev-kb.md +8 -6
@@ -26,12 +26,25 @@ interface ContentBlockMap {
26
26
  'text': TextBlock
27
27
  'reasoning': ReasoningBlock
28
28
  'image': ImageBlock
29
+ 'file': FileBlock
29
30
  'tool-call': ToolCallBlock
30
31
  'tool-result': ToolResultBlock
31
32
  }
32
33
  ```
33
34
 
34
- The block interfaces (full fields in source): `TextBlock` (`text`), `ReasoningBlock` (thinking, distinct from visible text), `ImageBlock` (a durable [image attachment](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/attachment.md)), `ToolCallBlock` (`id: CallId`, `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.
35
+ The block interfaces (full fields in source): `TextBlock` (`text`), `ReasoningBlock` (thinking, distinct from visible text), `ImageBlock` (a durable [image attachment](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/attachment.md)), `FileBlock` (a durable verbatim [file attachment](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/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.
36
+
37
+ 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`.
38
+
39
+ Source: [`packages/llm/llm/src/content.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm/src/content.ts)
40
+
41
+ ```ts type-equiv
42
+ /** Execution-world path that model tools can use to read one normalized attachment. */
43
+ interface ImageAttachmentAccess {
44
+ /** Absolute path to immutable normalized bytes; callers must treat it as read-only. */
45
+ readonlyPath: string
46
+ }
47
+ ```
35
48
 
36
49
  Source: [`packages/llm/llm/src/message.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm/src/message.ts)
37
50
 
@@ -196,7 +209,7 @@ type StreamChunk =
196
209
  | { type: 'block-start'; index: number; blockType: ContentBlockType }
197
210
  | { type: 'text-delta'; index: number; text: string }
198
211
  | { type: 'reasoning-delta'; index: number; text: string }
199
- | { type: 'tool-call-delta'; index: number; id: CallId; name?: string; argumentsDelta: string }
212
+ | { type: 'tool-call-delta'; index: number; id: ToolCallId; name?: string; argumentsDelta: string }
200
213
  | { type: 'block-end'; index: number; block: ContentBlock }
201
214
  | { type: 'usage'; usage: TokenUsage }
202
215
  | {
@@ -207,6 +220,16 @@ type StreamChunk =
207
220
  }
208
221
  ```
209
222
 
223
+ <a id="compact-assistant-streams"></a>
224
+
225
+ ## Compact Assistant streams
226
+
227
+ `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.
228
+
229
+ `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.
230
+
231
+ 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.
232
+
210
233
  ## `LlmFailure`
211
234
 
212
235
  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.
@@ -227,13 +250,51 @@ interface LlmFailure {
227
250
  }
228
251
  ```
229
252
 
253
+ ## Request-image pricing
254
+
255
+ An adapter whose provider charges visual tokens for request images declares per-route pricing by overriding `LlmAdapter.imageRequestPricing`, and `ctx.llm.imageRequestPricing(provider, model)` resolves it synchronously for consumers. The token meter resolves the routed model's pricing on every measurement so compaction pressure, retention, and range selection price image history as the routed request actually sends it; the DeepSeek adapter reproduces its own request projection (per-model pixel budget, oldest-first offload) and prices retained images with the published v4 vision accounting, while provider usage remains the authoritative anchor for completed requests.
256
+
257
+ ```ts type-equiv
258
+ /**
259
+ * Request price of one ordered image occurrence under one exact model route's
260
+ * request projection. Every occurrence resolves to the pair the wire actually
261
+ * carries: provider visual tokens for a retained image, plus the model-visible
262
+ * text sent with or instead of it (request-preview handle, offload placeholder,
263
+ * or text-only substitution). The caller prices `text` with its own text
264
+ * estimator so provider pricing never fixes a text tokenization.
265
+ */
266
+ interface LlmImageRequestPrice {
267
+ /** Provider visual tokens for the retained request image; 0 when only text represents this occurrence. */
268
+ visualTokens: number
269
+ /** Model-visible text sent for this occurrence, to be priced by the caller's text estimator. */
270
+ text: string
271
+ }
272
+ ```
273
+
274
+ ```ts type-equiv
275
+ /**
276
+ * Provider-side request-image pricing for one exact model route. Implemented
277
+ * by adapters whose provider charges visual tokens; consumers (the token
278
+ * meter) resolve it synchronously per measurement, so implementations must not
279
+ * perform I/O.
280
+ */
281
+ interface LlmImageRequestPricing {
282
+ /**
283
+ * Price every image occurrence of one request projection.
284
+ * @param images - durable image references in request order, one entry per occurrence.
285
+ * @returns one price per occurrence, aligned by index with `images`.
286
+ */
287
+ priceImages(images: readonly ImageAttachmentRef[]): readonly LlmImageRequestPrice[]
288
+ }
289
+ ```
290
+
230
291
  ## The adapter contract
231
292
 
232
293
  Every adapter MUST obey these, and every consumer may rely on them:
233
294
 
234
295
  - **`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.
235
296
  - **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`.
236
- - **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.
297
+ - **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.
237
298
  - **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.
238
299
  - **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`.
239
300
  - **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.
@@ -269,7 +330,7 @@ interface AppIdentity {
269
330
 
270
331
  ## `TokenUsage`
271
332
 
272
- Per-call token accounting. Counts are **disjoint**: `inputTokens` is uncached input only; cached input is reported separately, and billed input is the sum of the three. Adapters whose providers fold cache hits into a single prompt total (DeepSeek's `prompt_tokens`) subtract them back out. `reasoningTokens`, when present, is informational detail already included in `outputTokens`; totals must not add it again.
333
+ Per-call token accounting. Counts are **disjoint**: `inputTokens` is uncached input only; cached input is reported separately, and billed input is the sum of the three. Adapters whose providers fold cache hits into a single prompt total (DeepSeek's `prompt_tokens`) subtract them back out. Optional `totalTokens` is an exact aggregate prompt-plus-output count preserved from the provider or reconstructed from authoritative aggregate counters; adapters omit it when unavailable or inconsistent. `reasoningTokens`, when present, is informational detail already included in `outputTokens`; totals must not add it again.
273
334
 
274
335
  ```ts type-equiv
275
336
  /**
@@ -283,6 +344,14 @@ Per-call token accounting. Counts are **disjoint**: `inputTokens` is uncached in
283
344
  interface TokenUsage {
284
345
  inputTokens: number
285
346
  outputTokens: number
347
+ /**
348
+ * Exact full-call total including aggregate prompt and output tokens.
349
+ *
350
+ * Adapters preserve a provider total or derive it from authoritative
351
+ * aggregate prompt/output counters; they omit it when unavailable or
352
+ * inconsistent.
353
+ */
354
+ totalTokens?: number
286
355
  cacheReadTokens?: number
287
356
  cacheWriteTokens?: number
288
357
  reasoningTokens?: number
@@ -356,7 +425,7 @@ One model call is a fully-assembled `GenerateOptions`. The adapter answers with
356
425
 
357
426
  Source: [`packages/llm/llm/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm/src/types.ts)
358
427
 
359
- Provider and model discovery uses small provider-neutral descriptors. A model catalog is advisory: routing still keys on a registered provider, and an adapter may accept unlisted model ids.
428
+ Provider and model discovery uses small provider-neutral descriptors. A model catalog is advisory: routing still keys on a registered provider.
360
429
 
361
430
  Registering an adapter returns a handle: the disposer, plus the atomic route replacement a plugin whose route set is user-configurable needs.
362
431
 
@@ -604,8 +673,6 @@ interface LlmModelDiscoveryRequest {
604
673
  api?: string
605
674
  /** Credential for this interrogation alone; the harness never stores it. */
606
675
  apiKey?: string
607
- /** Caller cancellation; implementations must settle promptly after it aborts. */
608
- signal?: AbortSignal
609
676
  }
610
677
  ```
611
678
 
@@ -665,6 +732,12 @@ interface LlmCallConfigAdapterDefaults {
665
732
  }
666
733
  ```
667
734
 
735
+ ## Official DeepSeek request extensions
736
+
737
+ `ctx.deepseekLlmApiExtensions` is the provider-specific registry for additive top-level fields on `deepseek-official` requests. Contributor plugins use `register(field, provider)` to claim one field; the adapter calls `prepare(request)` after serializing its base body and merges the returned fields before HTTP. The prepared `accept()` transaction runs after 2xx, so a contributor can commit delivery state without treating a transport or provider rejection as acceptance. Preparation, collision, and acceptance failures use `REQUEST_EXTENSION` and fail the model request.
738
+
739
+ The [wire reference](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/deepseek-llm-api-wire-extensions.md) defines the exact request headers, extension transaction, field versions, and receiver obligations. The shipped composition registers [`dsh_session_log`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/session/session-log-deepseek/README.md) as a lossless incremental canonical-log suffix and [`dsh_plugin_packages`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/plugin-package-inventory-deepseek/README.md) as the complete active Loader-backed package set. These fields remain outside model messages and are absent from the pi-ai adapter path.
740
+
668
741
  ## Service and provider contracts
669
742
 
670
743
  `LlmAdapter` is the provider contract: subclass, implement `stream()`, and register one adapter instance with `ctx.llm.registerAdapter(providers, adapter)`. `GenerateOptions.provider` selects the registered adapter; `GenerateOptions.model` is passed to that adapter and need not be registered at lifecycle start. Duplicate provider routes fail atomically. Optional `providerRetryPolicy()` is captured per route with normal defaults, while `providerInfo()` and asynchronous `listModels()` feed `LlmRuntime.listProviders()` / `listModels()` with detached selector metadata. That catalog is advisory rather than a request whitelist: the adapter remains authoritative and may accept unlisted model ids. One asynchronous `resolveModel()` query returns exact model identity plus optional correctness-sensitive context capacity, an adapter-configured `defaultMaxTokens`, and ordered model-owned reasoning ids with an optional deployment default; absent fields mean unavailable metadata or provider-owned behavior, not invalid catalog membership. The resolver receives optional cancellation and must settle promptly after abort. `LlmRuntime.resolveModelInfo()` validates and detaches the aggregate. At the final adapter boundary, `resolveCallConfig()` materializes the output default only when `maxTokens` is absent and validates and materializes reasoning, so direct calls cannot bypass either configured behavior; direct dispatch captures one registration before awaiting that resolution. The agent loop instead uses `prepareCall()` to keep the same registration across model resolution, durable header logging, and dispatch, retain detached context metadata from that exact lookup, and report which config fields the adapter defaulted. Adapter lookup happens at the terminal continuation of the `llm/stream` waterfall, so a listener may short-circuit the call or route a mutable one-shot request before lookup. AgentLoop observes a request attempt once the outer waterfall returns a stream handle; that limited boundary does not prove a lazy terminal adapter was constructed or began provider I/O. The `block-start` / `block-end` `index` correlation and the assembler together mean an adapter only has to emit well-formed chunks — block reassembly is not each adapter's problem. [architecture.md](../index.md#turn-flow) shows where `ctx.llm.stream()` and the `llm/stream` waterfall sit in one turn.
@@ -713,6 +786,16 @@ declare abstract class LlmAdapter {
713
786
  * @returns a resolved policy, or `undefined` to use the normal defaults.
714
787
  */
715
788
  providerRetryPolicy(_provider: string): ResolvedRetryPolicy | undefined;
789
+ /**
790
+ * Resolve provider-side request-image pricing for one exact model route.
791
+ * The default declares none, so consumers fall back to their own neutral
792
+ * estimate. Implementations must answer synchronously without I/O; the
793
+ * token meter resolves this per measurement.
794
+ * @param _provider - a route passed to `registerAdapter()` for this instance.
795
+ * @param _model - exact model id passed to {@link GenerateOptions.model}.
796
+ * @returns route-owned image pricing, or `undefined` when the route declares none.
797
+ */
798
+ imageRequestPricing(_provider: string, _model: string): LlmImageRequestPricing | undefined;
716
799
  /**
717
800
  * List models this adapter can currently advertise for one owned provider.
718
801
  * The result is advisory: an adapter may accept unlisted model ids, and
@@ -764,6 +847,33 @@ declare abstract class LlmAdapter {
764
847
 
765
848
  Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — the language sides differ only in locale-specific paired document paths. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
766
849
 
850
+ <a id="ctxdeepseekllmapiextensions--deepseekllmapiextensionregistry"></a>
851
+
852
+ ### `ctx.deepseekLlmApiExtensions` — `DeepSeekLlmApiExtensionRegistry`
853
+
854
+ Registry of independently owned top-level fields for official DeepSeek requests.
855
+
856
+ ```ts cordis-catalog
857
+ /**
858
+ * Register the sole provider of one top-level request field. Registration is effect-scoped.
859
+ * @param field - declaration-merged field owned by the provider.
860
+ * @param provider - request-time field preparation and optional acceptance behavior.
861
+ * @returns disposer that releases the field.
862
+ */
863
+ register<K extends keyof DeepSeekLlmApiExtensionMap>( field: K, provider: DeepSeekLlmApiExtensionProvider<DeepSeekLlmApiExtensionMap[K]>, ): () => Promise<void>
864
+
865
+ /**
866
+ * Prepare every currently registered field from one immutable base request.
867
+ * Preparation failures reject before HTTP dispatch. Field values are cloned and frozen;
868
+ * providers retain no mutable alias to the outgoing request.
869
+ * @param request - exact serialized request facts before extension fields.
870
+ * @returns detached fields and their idempotent joint acceptance transaction.
871
+ */
872
+ async prepare(request: DeepSeekLlmApiExtensionRequest): Promise<PreparedDeepSeekLlmApiExtensions>
873
+ ```
874
+
875
+ Source: [`packages/llm/deepseek-llm-api-extensions/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/deepseek-llm-api-extensions/src/index.ts)
876
+
767
877
  <a id="ctxllm--llmruntime"></a>
768
878
 
769
879
  ### `ctx.llm` — `LlmRuntime`
@@ -785,7 +895,7 @@ registerAdapter(providers: string[], adapter: LlmAdapter): AdapterRegistrationHa
785
895
  * Describe provider routes with a registered adapter.
786
896
  * @returns detached provider metadata in registration order.
787
897
  */
788
- listProviders(): LlmProviderInfo[]
898
+ @Remote listProviders(): LlmProviderInfo[]
789
899
 
790
900
  /**
791
901
  * Declare provider routes an adapter plugin can activate through
@@ -801,7 +911,7 @@ registerConfigurableProviders(entries: readonly LlmConfigurableProvider[]): Dire
801
911
  * List every declared configurable provider, registered or dormant.
802
912
  * @returns detached directory entries in declaration order.
803
913
  */
804
- listConfigurableProviders(): LlmConfigurableProvider[]
914
+ @Remote listConfigurableProviders(): LlmConfigurableProvider[]
805
915
 
806
916
  /**
807
917
  * Offer to interrogate provider endpoints on behalf of the settings
@@ -810,10 +920,10 @@ listConfigurableProviders(): LlmConfigurableProvider[]
810
920
  * directory, and because a provider being *added* has no route to name yet.
811
921
  * Disposed with the fiber.
812
922
  * @param settingsNs - the namespace whose profiles this discovery serves.
813
- * @param discover - interrogates one endpoint; must honor `request.signal`.
923
+ * @param discover - interrogates one endpoint and must honor the supplied signal.
814
924
  * @returns the disposer that withdraws the offer.
815
925
  */
816
- registerModelDiscovery( settingsNs: string, discover: (request: LlmModelDiscoveryRequest) => Promise<readonly LlmDiscoveredModel[]>, ): () => void
926
+ registerModelDiscovery( settingsNs: string, discover: ( request: LlmModelDiscoveryRequest, signal?: AbortSignal, ) => Promise<readonly LlmDiscoveredModel[]>, ): () => void
817
927
 
818
928
  /**
819
929
  * Interrogate one provider endpoint for the models it advertises. The
@@ -822,9 +932,20 @@ registerModelDiscovery( settingsNs: string, discover: (request: LlmModelDiscover
822
932
  * candidate metadata a surface may offer for adoption.
823
933
  * @param settingsNs - namespace whose registered discovery serves this draft.
824
934
  * @param request - the endpoint, protocol, and one-shot credential to use.
935
+ * @param signal - caller cancellation.
825
936
  * @returns the advertised models, deduplicated in endpoint order.
826
937
  */
827
- async discoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, ): Promise<LlmDiscoveredModel[]>
938
+ async discoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, signal?: AbortSignal, ): Promise<LlmDiscoveredModel[]>
939
+
940
+ /**
941
+ * Remote adapter for one draft provider interrogation.
942
+ * @param settingsNs - namespace whose registered discovery serves this draft.
943
+ * @param request - endpoint, protocol, and one-shot credential to use.
944
+ * @param signal - caller cancellation supplied by the Remote carrier.
945
+ * @returns advertised models in endpoint order.
946
+ * @throws RemoteError with `llm/model-discovery-rejected` when discovery refuses or fails.
947
+ */
948
+ @Remote('discoverModels') async remoteDiscoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, signal: AbortSignal, ): Promise<LlmDiscoveredModel[]>
828
949
 
829
950
  /**
830
951
  * Resolve the retry policy captured when one provider route was registered.
@@ -833,6 +954,25 @@ async discoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, ):
833
954
  */
834
955
  providerRetryPolicy(provider: string): ResolvedRetryPolicy
835
956
 
957
+ /**
958
+ * Resolve provider-side request-image pricing for one exact route, or
959
+ * `undefined` when the provider is unregistered or declares none. Unknown
960
+ * providers degrade to `undefined` rather than throwing because callers
961
+ * price durable history whose route may no longer be mounted.
962
+ * @param provider - provider route named by a request header.
963
+ * @param model - exact model id named by the same header.
964
+ * @returns the owning adapter's image pricing for the route, when declared.
965
+ */
966
+ imageRequestPricing(provider: string, model: string): LlmImageRequestPricing | undefined
967
+
968
+ /**
969
+ * Resolve the exact text one durable file occurrence contributes to every
970
+ * provider request in the current execution environment.
971
+ * @param ref - durable verbatim file reference from model history.
972
+ * @returns the same deterministic handle text used at adapter dispatch.
973
+ */
974
+ fileRequestText(ref: FileAttachmentRef): string
975
+
836
976
  /**
837
977
  * Discover models advertised by one registered provider. Catalog membership
838
978
  * is advisory and never changes routing or request validation.
@@ -888,6 +1028,8 @@ async prepareCall(config: LlmCallConfig, signal?: AbortSignal): Promise<Prepared
888
1028
  stream(options: GenerateOptions): AsyncIterable<StreamChunk>
889
1029
  ```
890
1030
 
1031
+ Types: [FileAttachmentRef](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/attachment.md)
1032
+
891
1033
  Source: [`packages/llm/llm/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm/src/index.ts)
892
1034
 
893
1035
  <a id="llm-events"></a>
@@ -48,7 +48,7 @@ The service requires a confining `ctx.shell` executor and `ctx.approval`, and mi
48
48
 
49
49
  ## Current preset and the derived `custom`
50
50
 
51
- `current(events)` derives the effective preset from the knobs, not from its own event alone: it folds the session's effective sandbox mode (falling back to the executor's configured mode) and effective approval policy (falling back to the approval service config, then `ask`), prefers a still-matching recorded selection, then the first matching table entry in declaration order, and otherwise returns `CUSTOM_PRESET` (`'custom'`). `custom` is derived-only: clients may display it as the current value, but it is never a switch target or an event payload.
51
+ `current(session)` derives the effective preset from the optionally registered `permissions` projection. The unit folds the session's sandbox mode, approval policy, and recorded selection; values absent within that state fall back to the executor's configured mode and the approval service config, then `ask`. A missing registry or projection key fails explicitly. The service prefers a still-matching selection, then the first matching table entry in declaration order, and otherwise returns `CUSTOM_PRESET` (`'custom'`). `custom` is derived-only: clients may display it as the current value, but it is never a switch target or an event payload.
52
52
 
53
53
  `names` lists the switchable presets in table declaration order; `optionOf(name)` builds the option a client renders for a table key (label falls back to the key) or for `custom`, and throws for any other name.
54
54
 
@@ -66,9 +66,9 @@ interface PresetOption {
66
66
 
67
67
  ## Switching and the `permission/preset` event
68
68
 
69
- `set(session, name)` resolves the preset (unknown names throw), appends a log-only `permission/preset` event unless `name` is already the effective preset, then writes each knob through its own setter — `setSandboxMode` from [dsh-sandbox-policy](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/sandbox/sandbox-policy) and `setApprovalPolicy` from [dsh-user-approval](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/interaction/user-approval) — only when that knob's effective value changes. The selection event precedes the knob events in the same turn, and re-selecting the effective preset appends nothing at all.
69
+ `set(session, name)` resolves the preset (unknown names throw), appends a log-only `permission/preset` event unless `name` is already the effective preset, then writes each knob through its own setter — `setSandboxMode` from [dsh-sandbox-policy](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/sandbox/sandbox-policy) and `setApprovalPolicy` from [dsh-user-approval](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/interaction/user-approval) — only when that knob's effective value changes. The selection event precedes the knob events in the same turn, and re-selecting the effective preset appends nothing.
70
70
 
71
- `permission/preset` is durable, log-only user intent: it stays out of the model transcript (the knob events own the model-visible consequences through their consumers), and it exists so `current()` can preserve WHICH preset the user chose when two presets share a bundle; `effectivePermissionPreset(events)` folds the last one, and replay needs no catch-up state. The complete event declaration is in the [persistence log event catalog](../persistence-catalog.md); the method signatures are in the generated [service catalog](#ctxpermissionpresets--permissionpresetservice).
71
+ `permission/preset` is durable, log-only user intent: it stays out of the model transcript (the knob events own the model-visible consequences through their consumers), and it exists so `current()` can preserve WHICH preset the user chose when two presets share a bundle. The `permissions` projection folds that selection with both knob events and retains the `session/end-seed` boundary used to distinguish a restored empty seed from a fresh session; replay needs no catch-up state or raw-log rescan. The complete event declaration is in the [persistence log event catalog](../persistence-catalog.md); the method signatures are in the generated [service catalog](#ctxpermissionpresets--permissionpresetservice).
72
72
 
73
73
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
74
74
 
@@ -89,10 +89,10 @@ Owns the deployment's permission presets and their write path. Requires a confin
89
89
  * Resolve the preset matching the effective knob values. A still-matching
90
90
  * last selection wins shared-bundle ties; otherwise the first table match
91
91
  * wins, or {@link CUSTOM_PRESET} when no entry matches.
92
- * @param events - the session's events in log order.
92
+ * @param session - the session whose knob state is read.
93
93
  * @returns the effective preset name, or `custom` when nothing matches.
94
94
  */
95
- current(events: readonly SessionEvent[]): string
95
+ current(session: Session): string
96
96
 
97
97
  /**
98
98
  * Build the whole select value for one folded knob state: every table
@@ -128,7 +128,7 @@ optionOf(name: string): PresetOption
128
128
  set(session: Session, name: string): void
129
129
  ```
130
130
 
131
- Types: [Session](./session.md) · [SessionEvent](./session.md)
131
+ Types: [Session](./session.md)
132
132
 
133
133
  Source: [`packages/interaction/permission-presets/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/interaction/permission-presets/src/index.ts)
134
134
  <!-- END GENERATED cordis-surface -->