dsh-plugin-dev-kb 1.0.9 → 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 (120) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.en.md +6 -6
  3. package/README.md +6 -6
  4. package/kb/INDEX.md +4 -2
  5. package/kb/README.md +4 -4
  6. package/kb/extra/AGENTS.md +1 -1
  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/deepseek-llm-api-wire-extensions.md +18 -14
  10. package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +18 -14
  11. package/kb/extra/event-producer-consumer.md +44 -43
  12. package/kb/extra/event-producer-consumer.zh.md +47 -46
  13. package/kb/extra/i18n/style-samples.md +2 -2
  14. package/kb/extra/module-graph.md +444 -991
  15. package/kb/extra/module-graph.zh.md +446 -993
  16. package/kb/extra/subsystems/agent-team.md +4 -3
  17. package/kb/extra/subsystems/agent-team.zh.md +4 -3
  18. package/kb/extra/subsystems/attachment.md +157 -4
  19. package/kb/extra/subsystems/attachment.zh.md +157 -4
  20. package/kb/extra/subsystems/feedback.md +2 -2
  21. package/kb/extra/subsystems/feedback.zh.md +2 -2
  22. package/kb/extra/testing.md +6 -2
  23. package/kb/extra/testing.zh.md +6 -2
  24. package/kb/extra/web-styling.md +4 -0
  25. package/kb/extra/web-styling.zh.md +4 -0
  26. package/kb/meta/search-index.json +135 -111
  27. package/kb/meta/site-pages.txt +184 -182
  28. package/kb/meta/source.json +5 -5
  29. package/kb/meta/topics.md +4 -4
  30. package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +3 -2
  31. package/kb/site/develop/practice/llm-adapter.md +4 -3
  32. package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +3 -2
  33. package/kb/site/en/develop/practice/llm-adapter.md +4 -3
  34. package/kb/site/en/guide/network-proxy.md +87 -0
  35. package/kb/site/en/guide/providers.md +70 -17
  36. package/kb/site/en/guide/schedule.md +2 -0
  37. package/kb/site/en/reference/agent-lifecycle.md +5 -3
  38. package/kb/site/en/reference/api-gateway.md +2 -2
  39. package/kb/site/en/reference/capability-seams.md +14 -7
  40. package/kb/site/en/reference/config-catalog.md +157 -230
  41. package/kb/site/en/reference/cookbook/adding-a-package.md +1 -2
  42. package/kb/site/en/reference/cookbook/adding-a-settings-card.md +10 -8
  43. package/kb/site/en/reference/cookbook/extension-cookbook.md +15 -12
  44. package/kb/site/en/reference/index.md +11 -4
  45. package/kb/site/en/reference/persistence-catalog.md +91 -70
  46. package/kb/site/en/reference/subsystems/code-runtime.md +7 -3
  47. package/kb/site/en/reference/subsystems/commands.md +25 -16
  48. package/kb/site/en/reference/subsystems/compaction.md +9 -9
  49. package/kb/site/en/reference/subsystems/conversation.md +11 -4
  50. package/kb/site/en/reference/subsystems/core.md +96 -15
  51. package/kb/site/en/reference/subsystems/credentials.md +5 -4
  52. package/kb/site/en/reference/subsystems/goal.md +1 -1
  53. package/kb/site/en/reference/subsystems/index.md +1 -1
  54. package/kb/site/en/reference/subsystems/llm-streaming.md +24 -3
  55. package/kb/site/en/reference/subsystems/permission-presets.md +5 -5
  56. package/kb/site/en/reference/subsystems/persistence.md +185 -194
  57. package/kb/site/en/reference/subsystems/plan.md +3 -3
  58. package/kb/site/en/reference/subsystems/sandbox.md +2 -0
  59. package/kb/site/en/reference/subsystems/schedule.md +9 -3
  60. package/kb/site/en/reference/subsystems/session-projection.md +50 -24
  61. package/kb/site/en/reference/subsystems/session-query.md +19 -13
  62. package/kb/site/en/reference/subsystems/session-reference.md +25 -1
  63. package/kb/site/en/reference/subsystems/session-telemetry.md +6 -5
  64. package/kb/site/en/reference/subsystems/session-title.md +6 -6
  65. package/kb/site/en/reference/subsystems/session.md +156 -73
  66. package/kb/site/en/reference/subsystems/settings.md +30 -12
  67. package/kb/site/en/reference/subsystems/skills.md +1 -1
  68. package/kb/site/en/reference/subsystems/slots.md +1 -0
  69. package/kb/site/en/reference/subsystems/storage.md +28 -5
  70. package/kb/site/en/reference/subsystems/subagent.md +43 -97
  71. package/kb/site/en/reference/subsystems/system-prompt.md +16 -3
  72. package/kb/site/en/reference/subsystems/token-meter.md +2 -2
  73. package/kb/site/en/reference/subsystems/typert.md +18 -18
  74. package/kb/site/en/reference/subsystems/web-client.md +1 -1
  75. package/kb/site/en/reference/subsystems/workspace.md +8 -8
  76. package/kb/site/en/reference/tool-catalog.md +10 -64
  77. package/kb/site/guide/network-proxy.md +87 -0
  78. package/kb/site/guide/providers.md +70 -17
  79. package/kb/site/guide/schedule.md +2 -0
  80. package/kb/site/reference/agent-lifecycle.md +5 -3
  81. package/kb/site/reference/api-gateway.md +2 -2
  82. package/kb/site/reference/capability-seams.md +14 -7
  83. package/kb/site/reference/config-catalog.md +156 -229
  84. package/kb/site/reference/cookbook/adding-a-package.md +1 -2
  85. package/kb/site/reference/cookbook/adding-a-settings-card.md +10 -8
  86. package/kb/site/reference/cookbook/extension-cookbook.md +15 -12
  87. package/kb/site/reference/index.md +11 -4
  88. package/kb/site/reference/persistence-catalog.md +91 -70
  89. package/kb/site/reference/subsystems/code-runtime.md +7 -3
  90. package/kb/site/reference/subsystems/commands.md +25 -16
  91. package/kb/site/reference/subsystems/compaction.md +9 -9
  92. package/kb/site/reference/subsystems/conversation.md +11 -4
  93. package/kb/site/reference/subsystems/core.md +96 -15
  94. package/kb/site/reference/subsystems/credentials.md +5 -4
  95. package/kb/site/reference/subsystems/goal.md +1 -1
  96. package/kb/site/reference/subsystems/index.md +1 -1
  97. package/kb/site/reference/subsystems/llm-streaming.md +24 -3
  98. package/kb/site/reference/subsystems/permission-presets.md +5 -5
  99. package/kb/site/reference/subsystems/persistence.md +184 -193
  100. package/kb/site/reference/subsystems/plan.md +3 -3
  101. package/kb/site/reference/subsystems/schedule.md +9 -3
  102. package/kb/site/reference/subsystems/session-projection.md +50 -24
  103. package/kb/site/reference/subsystems/session-query.md +19 -13
  104. package/kb/site/reference/subsystems/session-reference.md +25 -1
  105. package/kb/site/reference/subsystems/session-telemetry.md +6 -5
  106. package/kb/site/reference/subsystems/session-title.md +6 -6
  107. package/kb/site/reference/subsystems/session.md +156 -73
  108. package/kb/site/reference/subsystems/settings.md +30 -12
  109. package/kb/site/reference/subsystems/skills.md +1 -1
  110. package/kb/site/reference/subsystems/slots.md +1 -0
  111. package/kb/site/reference/subsystems/storage.md +28 -5
  112. package/kb/site/reference/subsystems/subagent.md +43 -97
  113. package/kb/site/reference/subsystems/system-prompt.md +16 -3
  114. package/kb/site/reference/subsystems/token-meter.md +2 -2
  115. package/kb/site/reference/subsystems/typert.md +18 -18
  116. package/kb/site/reference/subsystems/web-client.md +1 -1
  117. package/kb/site/reference/subsystems/workspace.md +8 -8
  118. package/kb/site/reference/tool-catalog.md +9 -63
  119. package/package.json +2 -2
  120. package/skills/dsh-plugin-dev-kb.md +4 -4
@@ -37,11 +37,11 @@ interface CompactionResult {
37
37
  /** Human command that initiated this compaction, when it was manual. */
38
38
  sourceCommandId?: CommandId
39
39
  /** The seq of the appended `compaction/start` event. */
40
- startSeq: number
40
+ startSeq: SessionSeq
41
41
  /** The seq of the appended `compaction/summary` event. */
42
- summarySeq: number
42
+ summarySeq: SessionSeq
43
43
  /** The seq of the appended `compaction/end` event. */
44
- endSeq: number
44
+ endSeq: SessionSeq
45
45
  /** The summary content blocks produced by the backend. */
46
46
  summary: ContentBlock[]
47
47
  /**
@@ -52,9 +52,9 @@ interface CompactionResult {
52
52
  * can be GREATER than `end`. {@link CompactionResult.shadowedSeqs} is the
53
53
  * authoritative set of shadowed nodes, in surface order.
54
54
  */
55
- shadowedRange: { start: number; end: number }
55
+ shadowedRange: { start: SessionSeq; end: SessionSeq }
56
56
  /** The seqs of all shadowed surface nodes, in surface order. */
57
- shadowedSeqs: number[]
57
+ shadowedSeqs: SessionSeq[]
58
58
  /** Estimated token count of the shadowed content. */
59
59
  shadowedTokenCount: number
60
60
  }
@@ -98,9 +98,9 @@ The optional tool-result pruning service reports each durable content replacemen
98
98
  /** Cited source event and size accounting for one landed surface replacement. */
99
99
  interface PrunedEntry {
100
100
  /** Full-fidelity tool-result event shadowed by the replacement. */
101
- readonly originalSeq: number
101
+ readonly originalSeq: SessionSeq
102
102
  /** Newly appended pruned tool-result event. */
103
- readonly replacementSeq: number
103
+ readonly replacementSeq: SessionSeq
104
104
  /** Tool call shared by the original and replacement. */
105
105
  readonly callId: ToolCallId
106
106
  /** Original text size in Unicode code points. */
@@ -190,10 +190,10 @@ abstract compactNow( agent: ManualCompactAgentContext, signal: AbortSignal, sour
190
190
  * @throws when compaction is active or the range is missing, reversed, or unbalanced.
191
191
  * @returns the appended event seqs, summary, replaced range, and token accounting.
192
192
  */
193
- abstract compactRegion( start: number, end: number, agent: CompactionAgentContext, signal?: AbortSignal, ): Promise<CompactionResult>
193
+ abstract compactRegion( start: SessionSeq, end: SessionSeq, agent: CompactionAgentContext, signal?: AbortSignal, ): Promise<CompactionResult>
194
194
  ```
195
195
 
196
- Types: [CommandId](./commands.md)
196
+ Types: [CommandId](./commands.md) · [SessionSeq](./session.md)
197
197
 
198
198
  Source: [`packages/compaction/compaction/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/compaction/compaction/src/index.ts)
199
199
 
@@ -11,18 +11,24 @@ This page defines the data model and the extension path for a business-owned Con
11
11
 
12
12
  ## Data model and ownership
13
13
 
14
- 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.
14
+ 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.
15
15
 
16
16
  | Concept | Owner and purpose |
17
17
  |---|---|
18
- | 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. |
19
- | 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. |
18
+ | 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. |
19
+ | 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. |
20
20
  | Location | The engine-owned Session, Turn, or Step coordinates derived from durable boundary events. Definitions may publish typed data onto one Turn or Step. |
21
21
  | View Definition | A target package creates one incremental builder per Session and owns the final snapshot type for that target. |
22
22
  | View | A Slot entry such as Chat or Trajectory reads only its target snapshot and renders target-owned nodes. |
23
23
 
24
24
  Chat and Trajectory may recognize the same durable event family, but each keeps its own Definition State and final node payload. Shared target-neutral machinery is limited to identity routing, ordered replay, Location data, predecessor dependencies, and publication cadence.
25
25
 
26
+ ## Target activation
27
+
28
+ Each Session keeps a monotonic set of active targets. Creating or reading a target source does not activate it. The shell explicitly activates its persisted or newly selected View, while another consumer activates a target through its first source subscription. First activation creates that target's builder and calls `replace()` once from the current target-indexed Contexts. Later flushes call `apply()` for every active target, and unsubscription does not remove one.
29
+
30
+ The shell owns View selection and resolves the registered preferred View or Chat fallback before rendering when a binding is created or selected as current, and after View-roster changes. The assembler receives only the resolved target id and does not select Chat or another default target. A third-party View participates through the same selection and activation operations.
31
+
26
32
  ## Replayable event families
27
33
 
28
34
  Choose one stable business id before writing the Definition. Every event that contributes to the same Node must carry that id or derive it independently from its own payload; the client must never assign an update to “the latest unfinished” Context.
@@ -39,7 +45,7 @@ Use the producer-owned branded id type across the process boundary. Put the `Ses
39
45
 
40
46
  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.
41
47
 
42
- 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.
48
+ 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.
43
49
 
44
50
  ## Definition and typed Chat payload
45
51
 
@@ -250,5 +256,6 @@ Add focused tests that establish these outcomes:
250
256
  5. Repeated visible deltas preserve `context.key` and publish at most once per animation frame when requested.
251
257
  6. The keyed renderer consumes `node.data` and constrained Location hooks only; it does not scan the Session event window, Contexts, or Chat Nodes.
252
258
  7. Scalar and packed Assistant history produce the same final State, timing boundaries, and target snapshot, while one packed run remains one Match through replace, prepend, Location replay, and registry rebuild.
259
+ 8. Creating a target source performs no builder work; explicit selection or the first subscription performs one complete replacement, later updates reach every active target, and repeated activation performs no replacement.
253
260
 
254
261
  Use [`packages/client/ui-chat/src/client/conversation-nodes/assistant.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-chat/src/client/conversation-nodes/assistant.ts) for streaming and interruption, [`inbox.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-chat/src/client/conversation-nodes/inbox.ts) plus [`message.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/client/ui-chat/src/client/conversation-nodes/message.ts) for predecessor queries, and [`packages/client/ui-deliverables`](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/client/ui-deliverables) for a Definition that publishes Turn data without creating its own Node.
@@ -20,7 +20,7 @@ A turn flows through the six packages in one loop: the driver in [`agent-loop`](
20
20
  | `agent-loop/` | The concrete driver implementing the public `Agent` contract (`ctx.agentLoop`) | this page |
21
21
  | `scope/` | The scoped-registration primitive the registries and loop build per-agent scoping on | [scope.md](./scope.md) |
22
22
 
23
- `scope/` is the one non-service package: a dependency-free library (`createScope`/`scopeOf`/`scopeTarget`) that sits below `session/` and `system-prompt/` in the module graph precisely so they can consume it without a cycle. `agent-loop` is the one concrete implementation of the public `Agent` contract and lives here because it is the harness's default product loop; it runs each driver inside `ctx.agents.withInitiator()`. Extension plugins depend on `agent` — including when they need the initiating Agent — and never on `agent-loop` directly, so the loop stays swappable. The default composition that wires this spine into a runnable agent is [`examples/agent-spine-demo`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/examples/agent-spine-demo/README.md).
23
+ `scope/` is the one non-service package: a dependency-free library (`createScope`/`scopeOf`/`scopeTarget`) that sits below `session/` and `system-prompt/` in the module graph precisely so they can consume it without a cycle. `agent-loop` is the one concrete implementation of the public `Agent` contract and lives here because it is the harness's default product loop; it runs each driver inside `ctx.agents.withInitiator()`. Extension plugins depend on `agent` — including when they need the initiating Agent — and never on `agent-loop` directly, so the loop stays swappable. [`dsh-base`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/bundle/base/README.md) is the default product composition, while [`dsh-sdk-minimal`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/bundle/sdk-minimal/README.md) declares a smaller standalone tree.
24
24
 
25
25
  ## Creation and ownership
26
26
 
@@ -49,7 +49,7 @@ interface AgentHandle {
49
49
  }
50
50
  ```
51
51
 
52
- `CreateAgentOptions` carries the shared identity and everything a fresh agent needs before publication: session metadata (`meta` — validated `cwd`, fork lineage, seed boundary, origin classification, delegation depth), an optional `seed` replay prefix for forks, per-agent `AgentOptions`, a creation-only cancellation `signal`, and `setup`. `ResumeAgentOptions` is the persisted-identity counterpart: `resumeSessionId`, `agentOptions`, `signal`, and `setup`. The `setup` callback (`AgentSetup`) composes the agent's scoped world while both ids are still unpublished — everything registered through `agentCtx` exists before `agent/created` and the first prompt assembly — and may return a synchronous commit invoked immediately before publication; a setup rejection, commit throw, or owner disposal rolls the transaction back without publishing either id.
52
+ `CreateAgentOptions` carries the shared identity and everything a fresh agent needs before publication: session metadata (`meta` — validated `cwd`, fork lineage, the `isSeeded` marker, origin classification, delegation depth, and `agentPreset`), the exact fork cut in sibling field `inheritedEventCount`, an optional `seed` replay prefix, per-agent `AgentOptions`, a creation-only cancellation `signal`, and `setup`. `ResumeAgentOptions` is the persisted-identity counterpart: `resumeSessionId`, `agentOptions`, `signal`, and `setup`. The `setup` callback (`AgentSetup`) composes the agent's scoped world while both ids are still unpublished — everything registered through `agentCtx` exists before `agent/created` and the first prompt assembly — and may return a synchronous commit invoked immediately before publication; a setup rejection, commit throw, or owner disposal rolls the transaction back without publishing either id.
53
53
 
54
54
  `AgentFactory` is the creation interface behind the registry: the loop registers its factory via `ctx.agents.setFactory()`, so consumers use `ctx.agents` without depending on the concrete loop package. The exact `create`/`resume` signatures and rollback contracts are in the [generated section](#ctxagents--agentregistry) below.
55
55
 
@@ -155,6 +155,44 @@ interface Agent {
155
155
  type AgentStatus = 'idle' | 'running'
156
156
  ```
157
157
 
158
+ ```ts type-equiv
159
+ /** One process-local live assistant streaming publication. */
160
+ type AssistantStreamFrame =
161
+ | {
162
+ readonly type: 'start'
163
+ readonly attemptId: LlmAttemptId
164
+ /** Monotone within one attached Agent lifecycle; replacement restarts at 1. */
165
+ readonly revision: number
166
+ readonly turn: number
167
+ readonly step: number
168
+ }
169
+ | {
170
+ readonly type: 'chunk'
171
+ readonly attemptId: LlmAttemptId
172
+ readonly revision: number
173
+ /** Dense zero-based position within the attempt. */
174
+ readonly index: number
175
+ /** Safe-integer timestamp reused by the durable embedded stream. */
176
+ readonly time: number
177
+ readonly chunk: StreamChunk
178
+ }
179
+ | {
180
+ readonly type: 'end'
181
+ readonly attemptId: LlmAttemptId
182
+ readonly revision: number
183
+ /** Number of chunk frames emitted by this attempt. */
184
+ readonly index: number
185
+ /** Durable settlement committed before this notification, or live abandonment without one. */
186
+ readonly outcome:
187
+ | {
188
+ readonly kind: 'committed'
189
+ readonly eventType: 'assistant/message' | 'assistant/attempt'
190
+ readonly seq: SessionSeq
191
+ }
192
+ | { readonly kind: 'abandoned' }
193
+ }
194
+ ```
195
+
158
196
  `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](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-07-30-followup-enqueue-and-owned-runs.md)).
159
197
 
160
198
  ```ts type-equiv
@@ -255,7 +293,7 @@ type SessionStartSource = 'startup' | 'resume' | 'clear' | 'compact'
255
293
 
256
294
  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`.
257
295
 
258
- 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/SQLite backends, the `session/flush` checkpoint, crash recovery, and `SessionHeader` — is on **[persistence.md](./persistence.md)**.
296
+ 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)**.
259
297
 
260
298
  ## `ToolDefinition`
261
299
 
@@ -304,9 +342,9 @@ Two large discriminated unions are the ones consumers `switch` over most: **`Str
304
342
 
305
343
  ### Branded IDs
306
344
 
307
- IDs passed between packages are **branded** — structurally strings, but non-interchangeable at the type level (a `SessionId` cannot be passed where a `ToolCallId` is expected). Construction goes through a per-type factory; comparison, logging, and JSON behave as ordinary strings.
345
+ IDs passed between packages are **branded** — structurally strings, but non-interchangeable at the type level (a `SessionId` cannot be passed where a `ToolCallId` is expected). Construction uses the shared `brandString<T>()` helper or an owner-defined validating factory; comparison, logging, and JSON behave as ordinary strings.
308
346
 
309
- The `Branded<B>` primitive lives in its own type-only package, [dsh-brand](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/util/brand) (no runtime code, no harness-package dependency), so any package can brand the ids it owns without depending on an unrelated capability package.
347
+ The `Branded<B>` primitive and stateless constructor live in [dsh-brand](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/util/brand), which has no harness capability dependency. `brandString<T>()` applies a compile-time-only string brand.
310
348
 
311
349
  Source: [`packages/util/brand/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/util/brand/src/index.ts)
312
350
 
@@ -359,13 +397,14 @@ Concrete agent factory and driver service.
359
397
  /**
360
398
  * Create an agent and session under one caller-supplied identity, owned by
361
399
  * the accessing fiber. Constructor-driven config calls mint a fresh combined
362
- * id before entering this boundary.
400
+ * id before entering this boundary. When a persistence backend is mounted,
401
+ * the session's durable identity and any seed are stored before publication.
363
402
  * @param id - shared agent/session identity.
364
403
  * @param options - concrete loop options.
365
404
  * @param meta - optional fresh-session workspace metadata.
366
405
  * @returns the published running agent.
367
406
  */
368
- create(id: SessionId, options: AgentOptions = {}, meta: Pick<SessionHeader, 'cwd'> = {}): Agent
407
+ async create(id: SessionId, options: AgentOptions = {}, meta: Pick<SessionHeader, 'cwd'> = {}): Promise<Agent>
369
408
 
370
409
  /**
371
410
  * Create an owned agent on a caller-supplied session id.
@@ -413,6 +452,25 @@ async list(): Promise<AgentPreset[]>
413
452
  */
414
453
  @Remote('list') async remoteExportList(): Promise<AgentPresetRoster>
415
454
 
455
+ /**
456
+ * Every preset's composition as flattened plugin rows, for plugin-listing
457
+ * surfaces beside the roster's own picker.
458
+ *
459
+ * A preset with a live standing mount answers from its newest generation's
460
+ * Loader entries — the composition new sessions join — even when the file
461
+ * behind it has since been edited into an unreadable state: the mount is
462
+ * what sessions actually run, so the broken verdict only applies to a
463
+ * preset nothing composed. One never composed since boot answers from its
464
+ * file, with `!!js` disabled gates evaluated against the Loader context so
465
+ * both answers reflect the same host. Reading never mounts: an unmounted
466
+ * preset is parsed, not composed, so listing a preset's plugins cannot
467
+ * activate them early. A composition that stopped reading between
468
+ * discovery's health verdict and this read is reported broken with the
469
+ * raced reason rather than dropped.
470
+ * @returns one composition per roster preset, in roster order.
471
+ */
472
+ async compositionInventory(): Promise<AgentPresetComposition[]>
473
+
416
474
  /**
417
475
  * Resolve one preset by id.
418
476
  *
@@ -491,8 +549,8 @@ async read(id: string): Promise<string>
491
549
  * One preset's composition text with the roster row it belongs to.
492
550
  * @param agentPreset - the preset id.
493
551
  * @returns the composition beside its trust and published metadata.
494
- * @throws {TypertRemoteFailure} `bad-request` for an empty id, or
495
- * `agent-preset-not-found` when no configured root supplies it.
552
+ * @throws {RemoteError} `gateway/bad-request` for an empty id, or
553
+ * `agent-preset/not-found` when no configured root supplies it.
496
554
  */
497
555
  @Remote('read') async readDocument(agentPreset: string): Promise<AgentPresetDocument>
498
556
 
@@ -519,8 +577,8 @@ async copy(from: string, id: string, name?: string): Promise<void>
519
577
  * @param id - the new preset id.
520
578
  * @param name - the copy's optional display name.
521
579
  * @returns once the copy is stored.
522
- * @throws {TypertRemoteFailure} with the corresponding stable preset code
523
- * and details when the copy is refused.
580
+ * @throws {RemoteError} with the corresponding stable preset code and
581
+ * details when the copy is refused.
524
582
  */
525
583
  @Remote('copy') async remoteExportCopy(from: string, id: string, name?: string): Promise<void>
526
584
 
@@ -536,8 +594,8 @@ async remove(id: string): Promise<void>
536
594
  * Delete one preset through the Remote API.
537
595
  * @param id - the preset id.
538
596
  * @returns once the preset is deleted.
539
- * @throws {TypertRemoteFailure} with the corresponding stable preset code
540
- * and details when deletion is refused.
597
+ * @throws {RemoteError} with the corresponding stable preset code and
598
+ * details when deletion is refused.
541
599
  */
542
600
  @Remote('deletePreset') async remoteExportDelete(id: string): Promise<void>
543
601
 
@@ -588,8 +646,8 @@ async recompose(agentCtx: Context, id: string): Promise<AgentPreset>
588
646
  * @param agent - the session's live agent, resolved from the wire identity.
589
647
  * @param agentPreset - the preset to compose the agent from instead.
590
648
  * @returns the preset id that was recorded.
591
- * @throws {TypertRemoteFailure} with `bad-request`, `agent-preset-locked`,
592
- * `agent-preset-not-found`, or `agent-preset-invalid` when refused.
649
+ * @throws {RemoteError} with `gateway/bad-request`, `agent-preset/locked`,
650
+ * `agent-preset/not-found`, or `agent-preset/invalid` when refused.
593
651
  */
594
652
  @Remote('select') async select(agent: Agent, agentPreset: string): Promise<string>
595
653
 
@@ -787,6 +845,29 @@ Source: [`packages/core/agent/src/index.ts`](https://github.com/deepseek-ai/deep
787
845
 
788
846
  ### `agent/*` events
789
847
 
848
+ <a id="agentassistant-stream--emit"></a>
849
+
850
+ #### `agent/assistant-stream` — emit
851
+
852
+ 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.
853
+
854
+ ```ts cordis-catalog
855
+ /**
856
+ * Process-local assistant-stream publication. Chunk frames are transient;
857
+ * the loop appends one final v2 `assistant/message` or `assistant/attempt`
858
+ * with the same stream before a committed end frame.
859
+ * @param payload.agent - the agent whose attempt produced the frame.
860
+ * @param payload.frame - one ordered start, chunk, or end publication.
861
+ * Scope-filtered dispatch (`@deepseek-ai/dsh-scope`): agent-scoped listeners receive only that agent.
862
+ * @mode emit
863
+ */
864
+ 'agent/assistant-stream'(this: Scoped<Agent>, payload: { agent: Agent; frame: AssistantStreamFrame }): void
865
+ ```
866
+
867
+ Types: [Scoped](./scope.md)
868
+
869
+ Source: [`packages/core/agent/src/runtime-types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/agent/src/runtime-types.ts)
870
+
790
871
  <a id="agentcreated--emit"></a>
791
872
 
792
873
  #### `agent/created` — emit
@@ -230,9 +230,10 @@ Host service backing the generated `ctx.remote.credentials` namespace. It carrie
230
230
  * Describe several references for one configuration surface. Batched because
231
231
  * a settings page describes every reference its rows name at once, and one
232
232
  * round trip keeps those rows from settling separately.
233
- * @param refs - reference names, at most {@link MAX_DESCRIBE_REFS}; a name outside the grammar rejects the whole call as `bad-request`.
233
+ * @param refs - reference names, at most {@link MAX_DESCRIBE_REFS}; a name outside the grammar
234
+ * rejects the whole call as `gateway/bad-request`.
234
235
  * @returns one view per requested name, keyed by that name.
235
- * @throws TypertRemoteFailure when the request is invalid or no credential provider is mounted.
236
+ * @throws RemoteError when the request is invalid or no credential provider is mounted.
236
237
  */
237
238
  @Remote async describe(refs: string[]): Promise<Record<string, CredentialInfo>>
238
239
 
@@ -241,14 +242,14 @@ Host service backing the generated `ctx.remote.credentials` namespace. It carrie
241
242
  * this direction only: no read path returns it.
242
243
  * @param ref - reference name to store under.
243
244
  * @param value - the non-empty secret value.
244
- * @throws TypertRemoteFailure when the request is invalid, no provider is mounted, or the provider refuses the write.
245
+ * @throws RemoteError when the request is invalid, no provider is mounted, or the provider refuses the write.
245
246
  */
246
247
  @Remote async set(ref: string, value: string): Promise<void>
247
248
 
248
249
  /**
249
250
  * Remove one reference from a configuration surface.
250
251
  * @param ref - reference name to remove.
251
- * @throws TypertRemoteFailure when the request is invalid, no provider is mounted, or the provider refuses the write.
252
+ * @throws RemoteError when the request is invalid, no provider is mounted, or the provider refuses the write.
252
253
  */
253
254
  @Remote async unset(ref: string): Promise<void>
254
255
  ```
@@ -145,7 +145,7 @@ interface GoalChanged {
145
145
 
146
146
  ## Service behavior
147
147
 
148
- [`GoalService`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/goal/goal/src/index.ts) resolves creation defaults, folds strict replay from durable `goal/change` events, enforces exact-live-agent identity and compare-and-set mutations, and emits contained `goal/changed` notifications. The package [README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/goal/goal/README.md) defines the callable API and model-visible contract.
148
+ [`GoalService`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/goal/goal/src/index.ts) resolves creation defaults, reads strict replay from the optionally registered `goal` projection, enforces exact-live-agent identity and compare-and-set mutations, and emits contained `goal/changed` notifications. Its first dependent access fails if the projection registry or key is absent. The package [README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/goal/goal/README.md) defines the callable API and model-visible contract.
149
149
 
150
150
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
151
151
 
@@ -19,7 +19,7 @@ One page per subsystem of the DeepSeek Harness: what it is, the data structures
19
19
  | [todo.md](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/todo.md) | the todo package's whole-list item type, durable event ownership, projection, and open-turn invariant |
20
20
  | [commands.md](./commands.md) | the human-command registry service: definitions, adapter discovery, direct invocation, results, and parsing views |
21
21
  | [session.md](./session.md) | the full `SessionEventMap` variant catalog, `TurnEndReason`, `deriveMessages()`, execution enclosure, and standalone events |
22
- | [persistence.md](./persistence.md) | the durability seam: `SessionPersistence`, JSONL + SQLite backends, `session/flush`, crash recovery, `SessionHeader` |
22
+ | [persistence.md](./persistence.md) | the durability seam: `SessionPersistence`, the JSONL provider, `session/flush`, crash recovery, `SessionHeader` |
23
23
  | [settings.md](./settings.md) | the user-settings seam: `SettingsNamespace` registration, layered resolution (defaults → composition `base` → user document), owner scopes, hot commits |
24
24
  | [credentials.md](./credentials.md) | the credential seam: `CredentialRef` references (never values) in configuration, per-operation resolution, UI-safe `CredentialInfo`, provider source layers |
25
25
  | [session-query.md](./session-query.md) | logical records, bounded exact-event reads, relationship traces, semantic filters/documents, and full-text result pages |
@@ -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, telemetry, token accounting, and historical UI assembly expand the embedded settlement instead of treating live frames as persisted facts.
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,7 +294,7 @@ 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.
@@ -932,7 +943,7 @@ async discoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, sig
932
943
  * @param request - endpoint, protocol, and one-shot credential to use.
933
944
  * @param signal - caller cancellation supplied by the Remote carrier.
934
945
  * @returns advertised models in endpoint order.
935
- * @throws TypertRemoteFailure with `model-discovery-failed` when discovery refuses or fails.
946
+ * @throws RemoteError with `llm/model-discovery-rejected` when discovery refuses or fails.
936
947
  */
937
948
  @Remote('discoverModels') async remoteDiscoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, signal: AbortSignal, ): Promise<LlmDiscoveredModel[]>
938
949
 
@@ -954,6 +965,14 @@ providerRetryPolicy(provider: string): ResolvedRetryPolicy
954
965
  */
955
966
  imageRequestPricing(provider: string, model: string): LlmImageRequestPricing | undefined
956
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
+
957
976
  /**
958
977
  * Discover models advertised by one registered provider. Catalog membership
959
978
  * is advisory and never changes routing or request validation.
@@ -1009,6 +1028,8 @@ async prepareCall(config: LlmCallConfig, signal?: AbortSignal): Promise<Prepared
1009
1028
  stream(options: GenerateOptions): AsyncIterable<StreamChunk>
1010
1029
  ```
1011
1030
 
1031
+ Types: [FileAttachmentRef](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/attachment.md)
1032
+
1012
1033
  Source: [`packages/llm/llm/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm/src/index.ts)
1013
1034
 
1014
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
 
@@ -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 -->