dsh-plugin-dev-kb 1.0.9 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.en.md +14 -8
  3. package/README.md +14 -8
  4. package/docs/banner.svg +25 -0
  5. package/kb/INDEX.md +8 -2
  6. package/kb/README.md +4 -4
  7. package/kb/extra/AGENTS.md +3 -3
  8. package/kb/extra/cookbook/adding-a-remote-api.md +197 -0
  9. package/kb/extra/cookbook/adding-a-remote-api.zh.md +197 -0
  10. package/kb/extra/cookbook/adding-a-session-format-version.md +109 -0
  11. package/kb/extra/cookbook/adding-a-session-format-version.zh.md +109 -0
  12. package/kb/extra/cookbook/adding-a-vendored-package.md +1 -1
  13. package/kb/extra/cookbook/adding-a-vendored-package.zh.md +1 -1
  14. package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.md +1 -1
  15. package/kb/extra/cookbook/responding-to-pr-review-on-a-stack.zh.md +1 -1
  16. package/kb/extra/deepseek-llm-api-wire-extensions.md +19 -15
  17. package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +19 -15
  18. package/kb/extra/development.md +13 -3
  19. package/kb/extra/development.zh.md +13 -3
  20. package/kb/extra/event-producer-consumer.md +49 -46
  21. package/kb/extra/event-producer-consumer.zh.md +52 -49
  22. package/kb/extra/i18n/README.md +2 -1
  23. package/kb/extra/i18n/README.zh.md +3 -2
  24. package/kb/extra/i18n/style-samples.md +2 -2
  25. package/kb/extra/i18n/translation-prompt.md +1 -1
  26. package/kb/extra/module-graph.md +493 -1002
  27. package/kb/extra/module-graph.zh.md +495 -1004
  28. package/kb/extra/rescope.md +1 -1
  29. package/kb/extra/rescope.zh.md +1 -1
  30. package/kb/extra/session-format-status.md +47 -0
  31. package/kb/extra/session-format-status.zh.md +47 -0
  32. package/kb/extra/subsystems/agent-team.md +3 -2
  33. package/kb/extra/subsystems/agent-team.zh.md +3 -2
  34. package/kb/extra/subsystems/attachment.md +157 -4
  35. package/kb/extra/subsystems/attachment.zh.md +157 -4
  36. package/kb/extra/subsystems/feedback.md +164 -30
  37. package/kb/extra/subsystems/feedback.zh.md +164 -30
  38. package/kb/extra/testing.md +9 -4
  39. package/kb/extra/testing.zh.md +9 -4
  40. package/kb/extra/web-styling.md +5 -0
  41. package/kb/extra/web-styling.zh.md +5 -0
  42. package/kb/meta/search-index.json +223 -151
  43. package/kb/meta/site-pages.txt +187 -181
  44. package/kb/meta/source.json +5 -5
  45. package/kb/meta/topics.md +5 -5
  46. package/kb/site/develop/basic/publish.md +1 -1
  47. package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +3 -2
  48. package/kb/site/develop/practice/llm-adapter.md +4 -3
  49. package/kb/site/en/develop/basic/publish.md +1 -1
  50. package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +3 -2
  51. package/kb/site/en/develop/practice/llm-adapter.md +4 -3
  52. package/kb/site/en/guide/network-proxy.md +87 -0
  53. package/kb/site/en/guide/providers.md +70 -17
  54. package/kb/site/en/guide/python-sdk.md +19 -3
  55. package/kb/site/en/guide/schedule.md +2 -0
  56. package/kb/site/en/reference/agent-lifecycle.md +17 -8
  57. package/kb/site/en/reference/api-gateway.md +2 -2
  58. package/kb/site/en/reference/capability-seams.md +24 -10
  59. package/kb/site/en/reference/config-catalog.md +322 -264
  60. package/kb/site/en/reference/cookbook/adding-a-package.md +1 -2
  61. package/kb/site/en/reference/cookbook/adding-a-settings-card.md +10 -8
  62. package/kb/site/en/reference/cookbook/adding-a-tool.md +1 -1
  63. package/kb/site/en/reference/cookbook/extension-cookbook.md +16 -13
  64. package/kb/site/en/reference/index.md +26 -9
  65. package/kb/site/en/reference/persistence-catalog.md +189 -95
  66. package/kb/site/en/reference/subsystems/approval.md +1 -1
  67. package/kb/site/en/reference/subsystems/client-modules.md +9 -0
  68. package/kb/site/en/reference/subsystems/client-resources.md +94 -0
  69. package/kb/site/en/reference/subsystems/code-runtime.md +7 -3
  70. package/kb/site/en/reference/subsystems/commands.md +25 -16
  71. package/kb/site/en/reference/subsystems/compaction.md +10 -10
  72. package/kb/site/en/reference/subsystems/conversation.md +11 -4
  73. package/kb/site/en/reference/subsystems/core.md +173 -29
  74. package/kb/site/en/reference/subsystems/credentials.md +5 -4
  75. package/kb/site/en/reference/subsystems/filesystem.md +15 -0
  76. package/kb/site/en/reference/subsystems/goal.md +38 -2
  77. package/kb/site/en/reference/subsystems/index.md +3 -1
  78. package/kb/site/en/reference/subsystems/invariants.md +1 -1
  79. package/kb/site/en/reference/subsystems/llm-streaming.md +43 -12
  80. package/kb/site/en/reference/subsystems/lsp.md +1 -1
  81. package/kb/site/en/reference/subsystems/permission-presets.md +5 -5
  82. package/kb/site/en/reference/subsystems/persistence.md +211 -199
  83. package/kb/site/en/reference/subsystems/plan.md +3 -3
  84. package/kb/site/en/reference/subsystems/sandbox.md +2 -0
  85. package/kb/site/en/reference/subsystems/schedule.md +9 -3
  86. package/kb/site/en/reference/subsystems/scope.md +1 -1
  87. package/kb/site/en/reference/subsystems/session-projection.md +50 -24
  88. package/kb/site/en/reference/subsystems/session-query.md +19 -13
  89. package/kb/site/en/reference/subsystems/session-reference.md +29 -1
  90. package/kb/site/en/reference/subsystems/session-telemetry.md +27 -10
  91. package/kb/site/en/reference/subsystems/session-title.md +6 -6
  92. package/kb/site/en/reference/subsystems/session.md +208 -103
  93. package/kb/site/en/reference/subsystems/settings.md +30 -12
  94. package/kb/site/en/reference/subsystems/shell.md +9 -6
  95. package/kb/site/en/reference/subsystems/sidebar-right.md +151 -0
  96. package/kb/site/en/reference/subsystems/skills.md +1 -1
  97. package/kb/site/en/reference/subsystems/slots.md +41 -32
  98. package/kb/site/en/reference/subsystems/spill.md +13 -6
  99. package/kb/site/en/reference/subsystems/storage.md +28 -5
  100. package/kb/site/en/reference/subsystems/subagent.md +58 -105
  101. package/kb/site/en/reference/subsystems/subprocess.md +25 -26
  102. package/kb/site/en/reference/subsystems/system-prompt.md +18 -3
  103. package/kb/site/en/reference/subsystems/token-meter.md +4 -3
  104. package/kb/site/en/reference/subsystems/tools.md +5 -5
  105. package/kb/site/en/reference/subsystems/typert.md +18 -18
  106. package/kb/site/en/reference/subsystems/web-client.md +1 -1
  107. package/kb/site/en/reference/subsystems/workspace.md +88 -9
  108. package/kb/site/en/reference/tool-catalog.md +56 -66
  109. package/kb/site/en/reference/tool-execution-pipeline.md +2 -2
  110. package/kb/site/guide/network-proxy.md +87 -0
  111. package/kb/site/guide/providers.md +70 -17
  112. package/kb/site/guide/python-sdk.md +19 -3
  113. package/kb/site/guide/schedule.md +2 -0
  114. package/kb/site/reference/agent-lifecycle.md +17 -8
  115. package/kb/site/reference/api-gateway.md +2 -2
  116. package/kb/site/reference/capability-seams.md +24 -10
  117. package/kb/site/reference/config-catalog.md +321 -263
  118. package/kb/site/reference/cookbook/adding-a-package.md +1 -2
  119. package/kb/site/reference/cookbook/adding-a-settings-card.md +10 -8
  120. package/kb/site/reference/cookbook/adding-a-tool.md +1 -1
  121. package/kb/site/reference/cookbook/extension-cookbook.md +16 -13
  122. package/kb/site/reference/index.md +26 -9
  123. package/kb/site/reference/persistence-catalog.md +190 -96
  124. package/kb/site/reference/subsystems/approval.md +1 -1
  125. package/kb/site/reference/subsystems/client-modules.md +9 -0
  126. package/kb/site/reference/subsystems/client-resources.md +94 -0
  127. package/kb/site/reference/subsystems/code-runtime.md +7 -3
  128. package/kb/site/reference/subsystems/commands.md +25 -16
  129. package/kb/site/reference/subsystems/compaction.md +10 -10
  130. package/kb/site/reference/subsystems/conversation.md +11 -4
  131. package/kb/site/reference/subsystems/core.md +173 -29
  132. package/kb/site/reference/subsystems/credentials.md +5 -4
  133. package/kb/site/reference/subsystems/filesystem.md +15 -0
  134. package/kb/site/reference/subsystems/goal.md +38 -2
  135. package/kb/site/reference/subsystems/index.md +14 -12
  136. package/kb/site/reference/subsystems/invariants.md +1 -1
  137. package/kb/site/reference/subsystems/llm-streaming.md +43 -12
  138. package/kb/site/reference/subsystems/lsp.md +1 -1
  139. package/kb/site/reference/subsystems/permission-presets.md +5 -5
  140. package/kb/site/reference/subsystems/persistence.md +210 -198
  141. package/kb/site/reference/subsystems/plan.md +3 -3
  142. package/kb/site/reference/subsystems/schedule.md +9 -3
  143. package/kb/site/reference/subsystems/scope.md +1 -1
  144. package/kb/site/reference/subsystems/session-projection.md +50 -24
  145. package/kb/site/reference/subsystems/session-query.md +19 -13
  146. package/kb/site/reference/subsystems/session-reference.md +29 -1
  147. package/kb/site/reference/subsystems/session-telemetry.md +27 -10
  148. package/kb/site/reference/subsystems/session-title.md +6 -6
  149. package/kb/site/reference/subsystems/session.md +208 -103
  150. package/kb/site/reference/subsystems/settings.md +30 -12
  151. package/kb/site/reference/subsystems/shell.md +9 -6
  152. package/kb/site/reference/subsystems/sidebar-right.md +151 -0
  153. package/kb/site/reference/subsystems/skills.md +1 -1
  154. package/kb/site/reference/subsystems/slots.md +41 -32
  155. package/kb/site/reference/subsystems/spill.md +13 -6
  156. package/kb/site/reference/subsystems/storage.md +28 -5
  157. package/kb/site/reference/subsystems/subagent.md +58 -105
  158. package/kb/site/reference/subsystems/subprocess.md +25 -26
  159. package/kb/site/reference/subsystems/system-prompt.md +18 -3
  160. package/kb/site/reference/subsystems/token-meter.md +4 -3
  161. package/kb/site/reference/subsystems/tools.md +5 -5
  162. package/kb/site/reference/subsystems/typert.md +18 -18
  163. package/kb/site/reference/subsystems/web-client.md +1 -1
  164. package/kb/site/reference/subsystems/workspace.md +88 -9
  165. package/kb/site/reference/tool-catalog.md +55 -65
  166. package/kb/site/reference/tool-execution-pipeline.md +2 -2
  167. package/lib/index.js +25 -44
  168. package/package.json +3 -2
  169. package/skills/dsh-plugin-dev-kb.md +4 -4
@@ -26,12 +26,13 @@ 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: 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.
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.
35
36
 
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`.
37
38
 
@@ -219,6 +220,16 @@ type StreamChunk =
219
220
  }
220
221
  ```
221
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 and restore validation still expand the embedded settlement; telemetry, token accounting, and Host folds read the compact records directly. Record-level readers (`assistantStreamFirstTokenTime`, `assistantStreamHasVisibleContent`, `assistantStreamHasVisibleText`, `lastAssistantStreamChunk`, `assistantStreamChunks`, `joinAssistantStreamText`, `assembleAssistantStream`, and the per-run `runFirstTokenTime` and `runFirstVisibleTime`) answer consumer questions in one pass over the records with early exit, so a large history costs O(records) per settlement instead of O(members) expansion ([fold decision](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-09-06-embedded-stream-record-readers.md)). `expandAssistantStream()` remains the validating path for records read at a durable boundary and for consumers that need every member.
232
+
222
233
  ## `LlmFailure`
223
234
 
224
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.
@@ -283,11 +294,11 @@ Every adapter MUST obey these, and every consumer may rely on them:
283
294
 
284
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.
285
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`.
286
- - **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.
287
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.
288
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`.
289
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.
290
- - **An empty completion is a retryable error, not a silent success.** Both adapters map a terminal `stop` finish that carried no content blocks to `finish {kind:'error'}` with the canonical `EMPTY_RESPONSE` code, and `dsh-llm-retry` retries it by default; see [empty model responses are retryable](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/bug-fix/2026-07-24-empty-model-response-is-retryable.md).
301
+ - **An empty completion is a retryable error, not a silent success.** Both adapters map a terminal `stop` finish that carried no content blocks to `finish {kind:'error'}` with the canonical `EMPTY_RESPONSE` code, and `dsh-llm-retry` retries it by default.
291
302
  - **Every provider HTTP request carries the app-attribution header.** Adapters send `attributionHeaders()` (below) - the `User-Agent` baseline - and prove it with a wire-level test.
292
303
  - **Replay state is adapter-owned; its split is shared.** A successful `finish` may carry a `ReplayEnvelope`: opaque response-level metadata plus optional per-block entries aligned with the emitted block sequence. The alignment is the harness's vocabulary — when assembly drops a block it drops the entry at the same position, so stored metadata always describes stored content. The loop stores the pruned envelope with the assembled assistant message. On a later request, `LlmRuntime` passes the state only when the historical provider and target provider are currently registered to the exact same adapter instance. That adapter validates the state and owns any cross-model or cross-provider conversion; other adapters receive the provider-neutral content plus provider/model fields without the private state. Durable content stays authoritative: a stored state the reading adapter cannot use degrades that one message to provider-neutral conversion with a diagnostic instead of failing the request.
293
304
 
@@ -484,6 +495,8 @@ interface LlmConfigurableProvider {
484
495
  * from outside.
485
496
  */
486
497
  declared?: boolean
498
+ /** Configuration diagnostic for repair; unaffected models may remain serviceable. */
499
+ error?: string
487
500
  }
488
501
  ```
489
502
 
@@ -503,7 +516,7 @@ interface LlmModelInfo {
503
516
  }
504
517
  ```
505
518
 
506
- Correctness-sensitive metadata is resolved separately from the advisory catalog and is owned by the adapter serving the exact route. Context capacity, adapter call defaults, and reasoning choices share one exact-model result so consumers do not repeat authoritative model resolution.
519
+ Correctness-sensitive metadata is resolved separately from the advisory catalog and is owned by the adapter serving the exact route. Context capacity, adapter call defaults, reasoning choices, and the system prompt update mode share one exact-model result so consumers do not repeat authoritative model resolution. `SystemPromptUpdate` has the single value `'in-history'`: the model reads the latest `system` message at any position of `messages` as the complete effective system prompt, so the agent loop can append a changed prompt after the cached history instead of rewriting message 0 ([decision rule](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent-loop/README.md#understand-the-implementation)); an absent mode means only a leading system message is read, and `normalizeModelInfo` rejects any other value with `INVALID_MODEL_INFO`.
507
520
 
508
521
  ```ts type-equiv
509
522
  /** Provider-owned context capacity for one exact provider/model route. */
@@ -554,6 +567,8 @@ interface LlmResolvedModelInfo extends LlmModelInfo {
554
567
  defaultMaxTokens?: number
555
568
  /** Adapter-owned selectable reasoning levels when exposed. */
556
569
  reasoning?: LlmModelReasoningInfo
570
+ /** Declared mid-conversation system prompt handling; absent means only a leading system message is read. */
571
+ systemPromptUpdate?: SystemPromptUpdate
557
572
  }
558
573
  ```
559
574
 
@@ -566,12 +581,16 @@ interface GenerateOptions {
566
581
  /** Adapter-owned reasoning effort selected for this exact model. */
567
582
  reasoningEffort?: ReasoningEffortId
568
583
  /**
569
- * Ordered conversation messages, exactly as the provider sees them (after
570
- * the `system` slot). A loop-built request assembles them as
571
- * the derived history (dsh-agent-loop); a hand-built one-shot passes any list.
584
+ * Ordered conversation messages, exactly as the provider sees them. A
585
+ * loop-built request passes the derived history (dsh-agent-loop), whose
586
+ * leading system-role message carries the system prompt; a hand-built
587
+ * one-shot passes any list.
572
588
  */
573
589
  messages: Message[]
574
- /** System prompt text (adapters map to the provider's system slot). */
590
+ /**
591
+ * System prompt text for one-shot callers; adapters map it to the provider's
592
+ * system slot ahead of `messages`. Loop-built requests leave it undefined.
593
+ */
575
594
  system?: string
576
595
  /** Tool schemas (adapters map to the provider's `tools` field). */
577
596
  tools?: ToolSchema[]
@@ -685,11 +704,11 @@ interface LlmDiscoveredModel {
685
704
 
686
705
  ### The request envelope: `LlmCallConfig` and the logged header
687
706
 
688
- The loop builds each request from logged state. `EpochHeader` records call config, marks the fields supplied by adapter defaults, and records the rendered prompt and authoritative returned tool order (configured by `toolOrder`, or lexicographic when unset) through full `request/header` snapshots. Together with derived history, this makes the request reconstructable from the session log. See [session.md](./session.md#the-request-header-event-requestheader) and the [reconstructability Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.md).
707
+ The loop builds each request from logged state. `EpochHeader` records call config, marks the fields supplied by adapter defaults, and records the authoritative returned tool order (configured by `toolOrder`, or lexicographic when unset) through full `request/header` snapshots. The rendered prompt is derived history — the `system/message` at surface node 0, plus any later system node an `in-history` route appended — so the header and the derived history together make the request reconstructable from the session log. See [session.md](./session.md#the-request-header-event-requestheader) and the [reconstructability Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.md).
689
708
 
690
- `agent/request` receives a frozen call-config seed and may return a replacement to switch provider, model, reasoning effort, or sampling. Before the waterfall, the loop removes values marked as adapter defaults so exact-model preparation materializes the selected route's current values; unmarked explicit settings remain in the proposal. After the waterfall, preparation rejects unsupported explicit effort ids without clamping and logs the effective config plus the fields supplied by adapter defaults under the turn signal. The prepared call keeps one adapter registration through dispatch. Requests reaching `llm/stream` are deep-frozen, so mutation throws, and carry a process-local loop identity so observers do not confuse separately logged frozen auxiliary calls with conversation requests.
709
+ `agent/request` receives a frozen call-config seed and may return a replacement to switch provider, model, reasoning effort, or sampling. Before the waterfall, the loop removes values marked as adapter defaults so exact-model preparation materializes the selected route's current values; unmarked explicit settings remain in the proposal. After the waterfall, preparation rejects unsupported explicit effort ids without clamping and logs the effective config plus the fields supplied by adapter defaults under the turn signal. On step admission, this waterfall and preparation run after assembly and `step/start` but before the system prompt and accepted user batch are committed; cancellation during either commits neither. The prepared capability governs prompt reconciliation, and the call keeps one adapter registration through dispatch. Requests reaching `llm/stream` are deep-frozen, so mutation throws, and carry a process-local loop identity so observers do not confuse separately logged frozen auxiliary calls with conversation requests.
691
710
 
692
- On the wire, a loop-built request reads the `system` slot (the rendered prompt assembly) followed by the derived history. The logged request snapshot ends with the newest `user/message` on a turn's first step and the previous step's tool results on later steps. The dev invariant recomputes exactly this equation against every loop-built request.
711
+ On the wire, a loop-built request is the derived history alone: the rendered prompt travels as the leading `system`-role message (surface node 0, a `system/message` event) and, when the prepared call declares `systemPromptUpdate: 'in-history'`, a non-empty changed prompt may follow the cached history as a later `system`-role message that the model reads as the effective prompt; the request's `system` field is unset — `GenerateOptions.system` serves direct one-shot callers such as title providers. An empty rendering leaves no system messages in derived history, even when earlier requests retained several prompt versions. The logged request ends with the newest `user/message` on a turn's first step and the previous step's tool results on later steps. The dev invariant recomputes exactly this equation against every loop-built request and rejects a loop request carrying a `system` field.
693
712
 
694
713
  FIXME(call-config-shape): revisit which remaining fields are genuinely epoch-level for cache purposes (`model` and the model-owned reasoning effort are explicit; the sampling scalars sit here out of caution).
695
714
 
@@ -742,6 +761,8 @@ interface PreparedLlmCall {
742
761
  readonly context?: LlmModelContext
743
762
  /** Exact model modalities captured with the adapter dispatch generation. */
744
763
  readonly inputModalities?: readonly ModelModality[]
764
+ /** Exact model system prompt update mode captured with the adapter dispatch generation. */
765
+ readonly systemPromptUpdate?: SystemPromptUpdate
745
766
  /** Config fields materialized by the captured adapter rather than proposed by the caller. */
746
767
  readonly adapterDefaults: LlmCallConfigAdapterDefaults
747
768
  /**
@@ -932,7 +953,7 @@ async discoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, sig
932
953
  * @param request - endpoint, protocol, and one-shot credential to use.
933
954
  * @param signal - caller cancellation supplied by the Remote carrier.
934
955
  * @returns advertised models in endpoint order.
935
- * @throws TypertRemoteFailure with `model-discovery-failed` when discovery refuses or fails.
956
+ * @throws RemoteError with `llm/model-discovery-rejected` when discovery refuses or fails.
936
957
  */
937
958
  @Remote('discoverModels') async remoteDiscoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, signal: AbortSignal, ): Promise<LlmDiscoveredModel[]>
938
959
 
@@ -954,6 +975,14 @@ providerRetryPolicy(provider: string): ResolvedRetryPolicy
954
975
  */
955
976
  imageRequestPricing(provider: string, model: string): LlmImageRequestPricing | undefined
956
977
 
978
+ /**
979
+ * Resolve the exact text one durable file occurrence contributes to every
980
+ * provider request in the current execution environment.
981
+ * @param ref - durable verbatim file reference from model history.
982
+ * @returns the same deterministic handle text used at adapter dispatch.
983
+ */
984
+ fileRequestText(ref: FileAttachmentRef): string
985
+
957
986
  /**
958
987
  * Discover models advertised by one registered provider. Catalog membership
959
988
  * is advisory and never changes routing or request validation.
@@ -1009,6 +1038,8 @@ async prepareCall(config: LlmCallConfig, signal?: AbortSignal): Promise<Prepared
1009
1038
  stream(options: GenerateOptions): AsyncIterable<StreamChunk>
1010
1039
  ```
1011
1040
 
1041
+ Types: [FileAttachmentRef](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/attachment.md)
1042
+
1012
1043
  Source: [`packages/llm/llm/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm/src/index.ts)
1013
1044
 
1014
1045
  <a id="llm-events"></a>
@@ -5,7 +5,7 @@ outline: [2,3]
5
5
 
6
6
  # LSP navigation
7
7
 
8
- The LSP seam — a [capability seam](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-07-15-lsp-capability-seam.md) exposing semantic code navigation on one `ctx.lsp` service, split across packages: Service Definition ([dsh-lsp](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/lsp/lsp), `ctx.lsp` + the provider registry), a generic Service Provider ([dsh-lsp-stdio](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/lsp/lsp-stdio), a configured stdio language-server host), and Consumer ([dsh-tool-lsp](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/lsp/tool-lsp), the `lsp` tool schema). LSP is **one optional capability**, not part of the agent-loop spine — so its vocabulary lives here, not in [core.md](./core.md). A provider swap does not change how the model asks for navigation.
8
+ The LSP seam — a [capability seam](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/glossary.md#capability-seam) exposing semantic code navigation on one `ctx.lsp` service, split across packages: Service Definition ([dsh-lsp](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/lsp/lsp), `ctx.lsp` + the provider registry), a generic Service Provider ([dsh-lsp-stdio](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/lsp/lsp-stdio), a configured stdio language-server host), and Consumer ([dsh-tool-lsp](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/lsp/tool-lsp), the `lsp` tool schema). LSP is **one optional capability**, not part of the agent-loop spine — so its vocabulary lives here, not in [core.md](./core.md). A provider swap does not change how the model asks for navigation.
9
9
 
10
10
  Source: [`packages/lsp/lsp/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/lsp/lsp/src/types.ts)
11
11
 
@@ -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
 
@@ -68,7 +68,7 @@ interface PresetOption {
68
68
 
69
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 -->