dsh-plugin-dev-kb 1.0.7 → 1.0.9

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 +16 -0
  2. package/README.en.md +144 -0
  3. package/README.md +21 -7
  4. package/kb/INDEX.md +19 -5
  5. package/kb/README.md +11 -10
  6. package/kb/extra/AGENTS.md +4 -4
  7. package/kb/extra/cookbook/adding-a-vendored-package.md +2 -2
  8. package/kb/extra/cookbook/adding-a-vendored-package.zh.md +2 -2
  9. package/kb/extra/deepseek-llm-api-wire-extensions.md +159 -0
  10. package/kb/extra/deepseek-llm-api-wire-extensions.zh.md +159 -0
  11. package/kb/extra/development.md +8 -14
  12. package/kb/extra/development.zh.md +8 -14
  13. package/kb/extra/event-producer-consumer.md +47 -41
  14. package/kb/extra/event-producer-consumer.zh.md +47 -41
  15. package/kb/extra/glossary.md +1 -1
  16. package/kb/extra/glossary.zh.md +1 -1
  17. package/kb/extra/graph-atlas.md +0 -2
  18. package/kb/extra/graph-atlas.zh.md +0 -2
  19. package/kb/extra/i18n/README.md +4 -4
  20. package/kb/extra/i18n/README.zh.md +4 -4
  21. package/kb/extra/module-graph.md +680 -413
  22. package/kb/extra/module-graph.zh.md +681 -414
  23. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.md +2 -2
  24. package/kb/extra/postmortem/0001-acp-default-export-drops-inject.zh.md +2 -2
  25. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.md +2 -2
  26. package/kb/extra/postmortem/0002-js-expression-disabled-filesystem-tools.zh.md +2 -2
  27. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.md +2 -2
  28. package/kb/extra/postmortem/0003-web-agent-gui-feedback-loop.zh.md +2 -2
  29. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.md +1 -1
  30. package/kb/extra/postmortem/0004-landlock-partial-notice-misclassified-child-failures.zh.md +1 -1
  31. package/kb/extra/rescope.md +2 -2
  32. package/kb/extra/rescope.zh.md +2 -2
  33. package/kb/extra/subsystems/agent-team.md +24 -1
  34. package/kb/extra/subsystems/agent-team.zh.md +24 -1
  35. package/kb/extra/subsystems/attachment.md +12 -4
  36. package/kb/extra/subsystems/attachment.zh.md +12 -4
  37. package/kb/extra/subsystems/extensions.md +18 -0
  38. package/kb/extra/subsystems/extensions.zh.md +18 -0
  39. package/kb/extra/subsystems/feedback.md +2 -2
  40. package/kb/extra/subsystems/feedback.zh.md +2 -2
  41. package/kb/extra/subsystems/todo.md +32 -0
  42. package/kb/extra/subsystems/todo.zh.md +32 -0
  43. package/kb/extra/subsystems/webhook.md +70 -0
  44. package/kb/extra/subsystems/webhook.zh.md +70 -0
  45. package/kb/extra/testing.md +11 -10
  46. package/kb/extra/testing.zh.md +8 -7
  47. package/kb/meta/search-index.json +269 -161
  48. package/kb/meta/site-pages.txt +182 -168
  49. package/kb/meta/source.json +5 -5
  50. package/kb/meta/topics.md +14 -6
  51. package/kb/site/develop/basic/publish.md +2 -2
  52. package/kb/site/develop/basic/tool.md +1 -1
  53. package/kb/site/develop/cordis-tutorial/07-into-the-harness.md +4 -4
  54. package/kb/site/develop/framework/events.md +1 -1
  55. package/kb/site/develop/practice/dynamic-cordis.md +17 -0
  56. package/kb/site/develop/practice/llm-adapter.md +3 -3
  57. package/kb/site/en/develop/basic/publish.md +2 -2
  58. package/kb/site/en/develop/basic/tool.md +1 -1
  59. package/kb/site/en/develop/cordis-tutorial/07-into-the-harness.md +4 -4
  60. package/kb/site/en/develop/framework/events.md +1 -1
  61. package/kb/site/en/develop/practice/dynamic-cordis.md +17 -0
  62. package/kb/site/en/develop/practice/llm-adapter.md +3 -3
  63. package/kb/site/en/guide/github-review.md +104 -0
  64. package/kb/site/en/guide/mcp-memory.md +103 -0
  65. package/kb/site/en/guide/python-sdk.md +80 -34
  66. package/kb/site/en/guide/schedule.md +21 -0
  67. package/kb/site/en/reference/agent-lifecycle.md +1 -1
  68. package/kb/{extra → site/en/reference}/api-gateway.md +11 -9
  69. package/kb/site/en/reference/capability-seams.md +115 -67
  70. package/kb/site/en/reference/config-catalog.md +358 -164
  71. package/kb/site/en/reference/cookbook/adding-a-package.md +2 -2
  72. package/kb/site/en/reference/cookbook/adding-a-settings-card.md +2 -2
  73. package/kb/site/en/reference/cookbook/adding-a-tool.md +11 -4
  74. package/kb/site/en/reference/cookbook/adding-an-llm-adapter.md +1 -1
  75. package/kb/site/en/reference/cookbook/extension-cookbook.md +6 -6
  76. package/kb/site/en/reference/cordis-api/inherited.md +1 -1
  77. package/kb/site/en/reference/cordis-primer.md +2 -1
  78. package/kb/site/en/reference/index.md +19 -7
  79. package/kb/site/en/reference/persistence-catalog.md +91 -44
  80. package/kb/site/en/reference/subsystems/approval.md +10 -10
  81. package/kb/site/en/reference/subsystems/client-modules.md +58 -16
  82. package/kb/site/en/reference/subsystems/code-runtime.md +3 -3
  83. package/kb/site/en/reference/subsystems/compaction.md +2 -2
  84. package/kb/site/en/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +43 -24
  85. package/kb/site/en/reference/subsystems/core.md +70 -12
  86. package/kb/site/en/reference/subsystems/credentials.md +43 -3
  87. package/kb/site/en/reference/subsystems/filesystem.md +12 -2
  88. package/kb/site/en/reference/subsystems/index.md +6 -1
  89. package/kb/site/en/reference/subsystems/jobs.md +1 -1
  90. package/kb/site/en/reference/subsystems/llm-streaming.md +132 -11
  91. package/kb/site/en/reference/subsystems/permission-presets.md +1 -1
  92. package/kb/site/en/reference/subsystems/persistence.md +22 -3
  93. package/kb/site/en/reference/subsystems/plan.md +1 -1
  94. package/kb/site/en/reference/subsystems/session-projection.md +74 -33
  95. package/kb/site/en/reference/subsystems/session-query.md +9 -1
  96. package/kb/site/en/reference/subsystems/session-reference.md +28 -7
  97. package/kb/site/en/reference/subsystems/session-telemetry.md +2 -3
  98. package/kb/site/en/reference/subsystems/session.md +260 -41
  99. package/kb/site/en/reference/subsystems/settings.md +78 -1
  100. package/kb/site/en/reference/subsystems/skills.md +23 -0
  101. package/kb/site/en/reference/subsystems/slots.md +177 -0
  102. package/kb/site/en/reference/subsystems/spill.md +2 -2
  103. package/kb/site/en/reference/subsystems/storage.md +9 -1
  104. package/kb/site/en/reference/subsystems/subagent.md +90 -23
  105. package/kb/site/en/reference/subsystems/system-prompt.md +4 -4
  106. package/kb/site/en/reference/subsystems/token-meter.md +25 -10
  107. package/kb/site/en/reference/subsystems/tools.md +39 -39
  108. package/kb/site/en/reference/subsystems/typert.md +44 -37
  109. package/kb/site/en/reference/subsystems/user-questions.md +33 -33
  110. package/kb/site/en/reference/subsystems/web-client.md +98 -0
  111. package/kb/site/en/reference/subsystems/web-server.md +11 -5
  112. package/kb/site/en/reference/subsystems/web.md +7 -1
  113. package/kb/site/en/reference/subsystems/workspace.md +95 -2
  114. package/kb/site/en/reference/tool-catalog.md +76 -18
  115. package/kb/site/en/reference/tool-execution-pipeline.md +1 -1
  116. package/kb/site/guide/github-review.md +104 -0
  117. package/kb/site/guide/mcp-memory.md +103 -0
  118. package/kb/site/guide/python-sdk.md +87 -41
  119. package/kb/site/guide/schedule.md +21 -0
  120. package/kb/site/reference/agent-lifecycle.md +1 -1
  121. package/kb/{extra/api-gateway.zh.md → site/reference/api-gateway.md} +11 -9
  122. package/kb/site/reference/capability-seams.md +115 -67
  123. package/kb/site/reference/config-catalog.md +357 -163
  124. package/kb/site/reference/cookbook/adding-a-package.md +2 -2
  125. package/kb/site/reference/cookbook/adding-a-settings-card.md +2 -2
  126. package/kb/site/reference/cookbook/adding-a-tool.md +11 -4
  127. package/kb/site/reference/cookbook/adding-an-llm-adapter.md +1 -1
  128. package/kb/site/reference/cookbook/extension-cookbook.md +6 -6
  129. package/kb/site/reference/cordis-api/inherited.md +1 -1
  130. package/kb/site/reference/cordis-primer.md +2 -1
  131. package/kb/site/reference/index.md +19 -7
  132. package/kb/site/reference/persistence-catalog.md +87 -40
  133. package/kb/site/reference/subsystems/approval.md +10 -10
  134. package/kb/site/reference/subsystems/client-modules.md +58 -16
  135. package/kb/site/reference/subsystems/code-runtime.md +3 -3
  136. package/kb/site/reference/subsystems/compaction.md +2 -2
  137. package/kb/site/reference/{cookbook/adding-a-conversation-node.md → subsystems/conversation.md} +43 -24
  138. package/kb/site/reference/subsystems/core.md +70 -12
  139. package/kb/site/reference/subsystems/credentials.md +43 -3
  140. package/kb/site/reference/subsystems/filesystem.md +12 -2
  141. package/kb/site/reference/subsystems/index.md +6 -1
  142. package/kb/site/reference/subsystems/jobs.md +1 -1
  143. package/kb/site/reference/subsystems/llm-streaming.md +132 -11
  144. package/kb/site/reference/subsystems/persistence.md +22 -3
  145. package/kb/site/reference/subsystems/plan.md +1 -1
  146. package/kb/site/reference/subsystems/session-projection.md +74 -33
  147. package/kb/site/reference/subsystems/session-query.md +9 -1
  148. package/kb/site/reference/subsystems/session-reference.md +28 -7
  149. package/kb/site/reference/subsystems/session-telemetry.md +2 -3
  150. package/kb/site/reference/subsystems/session.md +260 -41
  151. package/kb/site/reference/subsystems/settings.md +78 -1
  152. package/kb/site/reference/subsystems/skills.md +23 -0
  153. package/kb/site/reference/subsystems/slots.md +177 -0
  154. package/kb/site/reference/subsystems/spill.md +2 -2
  155. package/kb/site/reference/subsystems/storage.md +9 -1
  156. package/kb/site/reference/subsystems/subagent.md +90 -23
  157. package/kb/site/reference/subsystems/system-prompt.md +4 -4
  158. package/kb/site/reference/subsystems/token-meter.md +25 -10
  159. package/kb/site/reference/subsystems/tools.md +39 -39
  160. package/kb/site/reference/subsystems/typert.md +44 -37
  161. package/kb/site/reference/subsystems/user-questions.md +33 -33
  162. package/kb/site/reference/subsystems/web-client.md +98 -0
  163. package/kb/site/reference/subsystems/web-server.md +11 -5
  164. package/kb/site/reference/subsystems/web.md +7 -1
  165. package/kb/site/reference/subsystems/workspace.md +95 -2
  166. package/kb/site/reference/tool-catalog.md +76 -18
  167. package/kb/site/reference/tool-execution-pipeline.md +1 -1
  168. package/package.json +11 -3
  169. package/skills/dsh-plugin-dev-kb.md +8 -6
@@ -37,7 +37,7 @@ interface SessionReferenceInput {
37
37
  }
38
38
  ```
39
39
 
40
- `SessionReferenceCandidate` is host-facing discovery output. Its label uses the latest session title when present, while filtering still searches only session id and cwd and never transcript text.
40
+ `SessionReferenceCandidate` is host-facing discovery output. Its label uses the latest session title when present, and filtering searches that label alongside session id and cwd, never transcript text.
41
41
 
42
42
  ```ts type-equiv
43
43
  /** One host-facing candidate from exact session metadata. */
@@ -48,6 +48,12 @@ interface SessionReferenceCandidate {
48
48
  label: string
49
49
  /** Source session working directory, when recorded. */
50
50
  cwd?: string
51
+ /**
52
+ * True when {@link SessionReferenceCandidate.cwd} is recorded and equals the
53
+ * requesting agent's. Hosts that only surface a distinguishing location
54
+ * read this instead of comparing paths they never received.
55
+ */
56
+ sameWorkspace: boolean
51
57
  /** Source session creation time in Unix epoch milliseconds. */
52
58
  createdAt: number
53
59
  }
@@ -116,21 +122,32 @@ Host capability for cancellable file-reference discovery.
116
122
  * @returns deterministic path-only candidates.
117
123
  */
118
124
  abstract list( agent: Agent, query: string, signal: AbortSignal, ): Promise<FileReferenceCandidate[]>
125
+ ```
126
+
127
+ Types: [Agent](./core.md)
128
+
129
+ Source: [`packages/context/file-reference/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/context/file-reference/src/index.ts)
130
+
131
+ <a id="ctxsessionfilereferences--sessionfilereferences"></a>
119
132
 
133
+ ### `ctx.sessionFileReferences` — `SessionFileReferences`
134
+
135
+ Host Remote adapter over the composed file-reference provider.
136
+
137
+ ```ts cordis-catalog
120
138
  /**
121
- * Remote face of {@link list}; the decorator cannot mark the abstract
122
- * member, so this concrete adapter carries the identical contract.
123
- * @param agent - target agent whose session cwd bounds discovery.
139
+ * List file and directory candidates for one Agent's working directory.
140
+ * @param agent - target Agent resolved from the Session identity on the wire.
124
141
  * @param query - path text following `@` or `@"`.
125
142
  * @param signal - caller cancellation.
126
- * @returns deterministic path-only candidates.
143
+ * @returns deterministic path-only candidates from the composed provider.
127
144
  */
128
- @Remote('list') remoteExportList( agent: Agent, query: string, signal: AbortSignal, ): Promise<FileReferenceCandidate[]>
145
+ @Remote list( agent: Agent, query: string, signal: AbortSignal, ): Promise<FileReferenceCandidate[]>
129
146
  ```
130
147
 
131
148
  Types: [Agent](./core.md)
132
149
 
133
- Source: [`packages/context/file-reference/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/context/file-reference/src/index.ts)
150
+ Source: [`packages/api/session-controller/src/file-references.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/file-references.ts)
134
151
 
135
152
  <a id="ctxsessionreferenceresolver--sessionreferenceresolver"></a>
136
153
 
@@ -141,6 +158,10 @@ Exact-read consumer that prepares immutable cross-session message context.
141
158
  ```ts cordis-catalog
142
159
  /**
143
160
  * List reference candidates, ranked by working-directory affinity.
161
+ *
162
+ * Discovery runs at keystroke rate, so a title only ever comes from a
163
+ * projection read: see {@link SessionReferenceResolver.projectedTitle} for
164
+ * which sessions can answer one and which fall back to their id.
144
165
  * @param agent - target agent; self is excluded and its cwd drives ranking.
145
166
  * @param query - optional case-insensitive session-id/cwd/title substring.
146
167
  * @param limit - optional positive result cap.
@@ -67,9 +67,8 @@ The seam's acknowledgement contract (owned by the [Service Definition README's s
67
67
  /**
68
68
  * Deployment-selected session-sharing policy disclosed by a mounted
69
69
  * {@link SessionTelemetryBackend} backend to human-facing acknowledgement surfaces (the
70
- * `/feedback` command's confirmation text). The seam owns the vocabulary so
71
- * any backend can disclose a policy without depending on the OTel package;
72
- * the values mirror the OTel backend's serialized `SessionTelemetryMode` choices.
70
+ * `/feedback` command's confirmation text). The Service Definition owns the
71
+ * vocabulary so consumers and backends do not depend on a specific provider.
73
72
  */
74
73
  type SessionTelemetrySharingStatus = 'full' | 'feedback-only' | 'disabled'
75
74
  ```
@@ -74,7 +74,7 @@ interface SessionEventMap {
74
74
  * JSON string exactly as the model produced it (unparsed). `callId` pairs the
75
75
  * call with its `tool/result`.
76
76
  */
77
- 'tool/call': { turn: number; step: number; callId: CallId; name: string; arguments: string }
77
+ 'tool/call': { turn: number; step: number; callId: ToolCallId; name: string; arguments: string }
78
78
  /**
79
79
  * A completed tool call's model-facing result, optional internal failure
80
80
  * identity, and optional tool-private `meta` presentation payload. `meta` is
@@ -93,13 +93,16 @@ interface SessionEventMap {
93
93
  error?: { name: string; code: string }
94
94
  meta?: JsonValue
95
95
  }
96
- /** Whole-list snapshot; latest write wins on replay. Log-only UI state; never derived history. */
97
- 'todo/write': { todos: TodoItem[] }
98
96
  /**
99
97
  * Full header for the next request, appended inside its step before dispatch.
100
98
  * It is log-only; the latest snapshot reconstructs the request header.
101
99
  */
102
- 'request/header': { header: EpochHeader; reason: RequestHeaderReason }
100
+ 'request/header': {
101
+ header: EpochHeader
102
+ reason: RequestHeaderReason
103
+ /** A changed header also begins a distinct model-message series. */
104
+ startsSeries?: true
105
+ }
103
106
  /**
104
107
  * Route metadata for the next request, logged only when the route or capacity
105
108
  * changes. It does not participate in request reconstruction or header equality.
@@ -133,34 +136,11 @@ interface SessionEventMap {
133
136
 
134
137
  `UserMessage` is the identified, frozen user-role value shared by ordinary prompts, injected context, steering, and live inbox events. Event wrappers add only event-local position or outcome facts; the loop adds only driver-owned routing state while an item remains pending.
135
138
 
136
- ### `TodoItem` — one todo-list entry
137
-
138
- The unit of the `todo/write` event's whole-list snapshot. Deliberately minimal — a `content` line and a three-state `status` (no id, priority, or `activeForm`): the list is replaced wholesale on every write, so entries need no stable identity. See the [todo_write Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-06-29-todo-write-tool.md).
139
-
140
- ```ts type-equiv
141
- /**
142
- * One entry in an agent's todo list — the unit of the `todo/write`
143
- * {@link SessionEventMap} event's whole-list snapshot.
144
- *
145
- * Deliberately minimal: a human-readable `content` line and a three-state
146
- * `status`. No id, priority, or `activeForm` — the list is replaced wholesale
147
- * on every write (last-write-wins), so entries need no stable identity. The
148
- * three statuses describe the complete portable lifecycle needed by model and
149
- * UI consumers.
150
- */
151
- interface TodoItem {
152
- /** What this task is — a short imperative line shown in the UI. */
153
- content: string
154
- /** Lifecycle state. `in_progress` marks a task being worked now; parallel work may mark several. */
155
- status: 'pending' | 'in_progress' | 'completed'
156
- }
157
- ```
158
-
159
139
  <a id="the-request-header-event-requestheader"></a>
160
140
 
161
141
  ### The request header event: `request/header`
162
142
 
163
- The request envelope — the `EpochHeader` (call config + markers for adapter-supplied defaults + rendered system prompt + assembled tool schemas) — is logged session state, so every conversation request is a pure function of the log (the reconstructability Agent Note). A full `request/header` snapshot with reason `'initial'` or `'resume'` records each loop-instance boundary; a later changed request records another full snapshot with reason `'change'`. `foldRequestHeader(events)` reconstructs the header by selecting the latest snapshot. The event is not a `SurfaceEventType`: it produces no LLM message.
143
+ The request envelope — the `EpochHeader` (call config + markers for adapter-supplied defaults + rendered system prompt + assembled tool schemas) — is logged session state, so every conversation request is a pure function of the log (the reconstructability Agent Note). A full `request/header` snapshot with reason `'initial'` or `'resume'` records each loop-instance boundary; a changed request appends a snapshot with reason `'change'`; and an unchanged envelope beginning an explicitly declared message series or following a surface replacement appends a snapshot with reason `'series'`. A changed snapshot carries `startsSeries: true` when that request also begins a series. Ordinary append-only later Turns, further Steps, and retries in the same model-message series inherit the latest snapshot. `foldRequestHeader(events)` reconstructs the header by selecting the latest snapshot. The event is not a `SurfaceEventType`: it produces no LLM message.
164
144
 
165
145
  ```ts type-equiv
166
146
  /**
@@ -224,17 +204,6 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
224
204
  /** Unix epoch milliseconds. */
225
205
  time: number
226
206
  data: SessionEventMap[K]
227
- /**
228
- * Marks an event a reader may safely skip when it does not recognize
229
- * `type`. Absent means required: a reader meeting an unrecognized type
230
- * without this marker MUST refuse to reconstruct the session instead of
231
- * silently dropping the event, because an unrecognized required event may
232
- * change how the rest of the log is interpreted. A writer sets `true` only
233
- * on purely informational records whose loss cannot affect reconstruction;
234
- * defaulting to required means a forgotten marker over-refuses (an
235
- * inconvenience) rather than silently resuming a gutted session.
236
- */
237
- ignorable?: true
238
207
  } & (K extends SurfaceEventType ? {
239
208
  /**
240
209
  * Seq numbers of earlier events that this event cites as sources
@@ -599,9 +568,9 @@ Consumers that order Sessions by human activity exclude this boundary: picking a
599
568
 
600
569
  ## Plugin-contributed log-only events
601
570
 
602
- A plugin may declaration-merge extra `SessionEventMap` types. These are **log-only**: NOT `SurfaceEventType`s (they carry no `surfaceOp` and contribute nothing to derived history). Their owner decides whether they belong to an open execution turn or may stand between turns, and enforces any relation in its own invariant companion. The generated [persistence log event catalog](../persistence-catalog.md) enumerates every core and plugin-contributed event with its payload, surface badge, and declaration site; the compaction seam's `compaction/*` semantics are discussed on [compaction.md](./compaction.md).
571
+ A plugin may declaration-merge extra `SessionEventMap` types. These are **log-only**: NOT `SurfaceEventType`s (they carry no `surfaceOp` and contribute nothing to derived history). Their owner decides whether they belong to an open execution turn or may stand between turns, and enforces any relation in its own invariant companion. The generated [persistence log event catalog](../persistence-catalog.md) enumerates every core and plugin-contributed event; the compaction seam's `compaction/*` semantics are discussed on [compaction.md](./compaction.md).
603
572
 
604
- When several events in one plugin-owned family assemble into one Web Client Conversation Node, every start, update, result, resource, or interruption event in that family carries or independently derives the same stable business id. This requirement applies to correlated Node families, not to every Session event; it lets the client group each event without guessing from adjacency or scanning history. See the [Conversation Node cookbook](../cookbook/adding-a-conversation-node.md).
573
+ When several events in one plugin-owned family assemble into one Web Client Conversation Node, every start, update, result, resource, or interruption event in that family carries or independently derives the same stable business id. This requirement applies to correlated Node families, not to every Session event; it lets the client group each event without guessing from adjacency or scanning history. See the [Conversation subsystem](./conversation.md).
605
574
 
606
575
  The hook bridges' `hook/invoked` / `hook/result` pairs (from `@deepseek-ai/dsh-hook-protocol`) correlate by `handlerId`. `UserPromptSubmit`, `PreToolUse`, `PostToolUse`, and `Stop` fire inside the loop's open turn, so their `hook/*` records are turn-enclosed by construction. `SessionStart` gets no `hook/*` record because it runs before turn 1; its context remains pending in the inbox until a waking delivery opens a turn (see [the hook-bridges Agent Note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/feature/2026-06-30-hook-bridges.md)).
607
576
 
@@ -611,6 +580,12 @@ What a persistence backend relies on: the durable log persists every event lossl
611
580
 
612
581
  The backends that consume this contract are on [persistence.md](./persistence.md).
613
582
 
583
+ ## Remote catalog and workspace opening
584
+
585
+ `ModelCatalog` is the Host-generation model directory returned by `session/modelCatalog`: it carries the deployment default, routable provider ids, successful provider groups, and isolated provider failures. It is not derived from one Session and remains separate from Session projections.
586
+
587
+ `SessionOpenWorkspacePathRequest` carries an absolute or workspace-resolved `path`. `SessionOpenWorkspacePathValue` confirms that the Host accepted the native handoff. A Session-aware Client resolves relative paths against its current Session cwd when known; the controller hands the path to the opener unchanged and reports invalid requests, cancellation, and opener failures through the Session Remote error vocabulary.
588
+
614
589
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
615
590
 
616
591
  <a id="cordis-surface"></a>
@@ -619,6 +594,150 @@ The backends that consume this contract are on [persistence.md](./persistence.md
619
594
 
620
595
  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).
621
596
 
597
+ <a id="ctxsessioncontroller--sessioncontroller"></a>
598
+
599
+ ### `ctx.sessionController` — `SessionController`
600
+
601
+ Host service backing the generated `ctx.remote.session` namespace.
602
+
603
+ ```ts cordis-catalog
604
+ /**
605
+ * Resolve or resume one ordinary Session for another Host API domain.
606
+ * @param sessionId - Session identity whose Agent owns the operation.
607
+ * @returns the live Agent or the stable Session-domain failure.
608
+ */
609
+ resolveAgent(sessionId: SessionId): Promise<ApiSessionAgentResult>
610
+
611
+ /**
612
+ * Inspect one attached or persisted Session without activating its Agent.
613
+ * @param sessionId - durable Session identity.
614
+ * @param signal - optional caller cancellation for persistence reads.
615
+ * @returns the current attached state or persisted header and event prefix.
616
+ */
617
+ inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<{ meta: SessionHeader; events: SessionEvent[] }>
618
+
619
+ /**
620
+ * Read all visible Session rows without resuming an Agent.
621
+ * @param _request - reserved empty list request.
622
+ * @param signal - cancellation for persistence reads.
623
+ * @returns visible Session summaries ordered by activity.
624
+ */
625
+ @Remote('list') async list(_request: SessionListRequest, signal: AbortSignal): Promise<SessionListValue>
626
+
627
+ /**
628
+ * Search visible Session content without resuming an Agent.
629
+ * @param request - literal message-content query.
630
+ * @param signal - cancellation for list and search reads.
631
+ * @returns authorized bounded Session search results.
632
+ */
633
+ @Remote('search') search(request: SessionSearchRequest, signal: AbortSignal): Promise<SessionSearchValue>
634
+
635
+ /**
636
+ * Create or idempotently adopt one ordinary Session.
637
+ * @param request - requested identity, location, and Agent preset.
638
+ * @returns the Session identity and resolved preset when configured.
639
+ */
640
+ @Remote('create') create(request: SessionCreateRequest): Promise<SessionCreateValue>
641
+
642
+ /**
643
+ * Select one Session-local model after explicitly resuming the Session.
644
+ * @param request - Session identity and requested model selection.
645
+ * @returns the normalized selection installed for the Session.
646
+ */
647
+ @Remote('selectModel') selectModel(request: SessionSelectModelRequest): Promise<SessionSelectModelValue>
648
+
649
+ /**
650
+ * Describe every currently routable model for Host-generation selectors.
651
+ * @returns provider-grouped models, the deployment default, and isolated provider failures.
652
+ */
653
+ @Remote('modelCatalog') modelCatalog(): Promise<ModelCatalog>
654
+
655
+ /**
656
+ * Report whether this deployment can hand a Session workspace path to a native desktop.
657
+ * @returns true when the matching open operation is available.
658
+ */
659
+ @Remote canOpenWorkspacePath(): boolean
660
+
661
+ /**
662
+ * Open one path prepared by a Session-aware caller on the Host desktop.
663
+ * @param request - path after best-effort Session workspace resolution.
664
+ * @param signal - caller lifetime; abort terminates the native command.
665
+ * @returns confirmation after the native opener accepts the path.
666
+ * @throws TypertRemoteFailure when the request is invalid, cancelled, or the opener fails.
667
+ */
668
+ @Remote('openWorkspacePath') async openWorkspacePath( request: SessionOpenWorkspacePathRequest, signal: AbortSignal, ): Promise<SessionOpenWorkspacePathValue>
669
+
670
+ /**
671
+ * Rename one Session after explicitly resuming it.
672
+ * @param request - Session identity and proposed title.
673
+ * @returns the accepted title and durable event sequence.
674
+ */
675
+ @Remote('rename') rename(request: SessionRenameRequest): Promise<SessionRenameValue>
676
+
677
+ /**
678
+ * Fork one cold-readable completed-turn prefix into a new Session.
679
+ * @param request - source Session and optional event anchor.
680
+ * @returns the new Session identity.
681
+ */
682
+ @Remote('fork') fork(request: SessionForkRequest): Promise<SessionForkValue>
683
+
684
+ /**
685
+ * Admit one prompt after explicitly resuming its Session.
686
+ * @param request - Session identity, prompt content, source metadata, and delivery mode.
687
+ * @param signal - caller cancellation before prompt admission begins.
688
+ * @returns acknowledgement that the Agent accepted the prompt.
689
+ */
690
+ @Remote('prompt') prompt(request: SessionPromptRequest, signal: AbortSignal): Promise<SessionPromptValue>
691
+
692
+ /**
693
+ * Read one image proven reachable from the addressed Session log.
694
+ * @param request - Session and attachment identities used for authorization.
695
+ * @returns the durable attachment reference and base64-encoded bytes.
696
+ */
697
+ @Remote('attachment') attachment(request: SessionAttachmentRequest): Promise<SessionAttachmentValue>
698
+
699
+ /**
700
+ * Mutate one still-pending queue occurrence on a live Agent.
701
+ * @param request - Session, queue item, and requested mutation.
702
+ * @returns acknowledgement that the queue mutation was applied.
703
+ */
704
+ @Remote('updateQueue') updateQueue(request: SessionUpdateQueueRequest): SessionUpdateQueueValue
705
+
706
+ /**
707
+ * Cancel one active Agent turn without dropping its pending inbox.
708
+ * @param request - Session whose active Agent turn is cancelled.
709
+ * @returns acknowledgement that cancellation was requested.
710
+ */
711
+ @Remote('cancel') cancel(request: SessionCancelRequest): SessionCancelValue
712
+
713
+ /**
714
+ * Read one cold-safe, message-aligned Session history page.
715
+ * @param request - durable address, backward cursor, and page budget.
716
+ * @param signal - cancellation for persistence reads.
717
+ * @returns one chronological page.
718
+ */
719
+ @Remote('page') page(request: SessionPageRequest, signal: AbortSignal): Promise<SessionPage>
720
+
721
+ /**
722
+ * Follow one Session log from its opening or resume cursor.
723
+ * @param request - durable address and last committed sequence already held by the caller.
724
+ * @param signal - cancellation owned by the Remote stream carrier.
725
+ * @returns a complete opening snapshot followed by gap-free event frames.
726
+ */
727
+ @Remote({ mode: 'stream' }) follow(request: SessionFollowRequest, signal: AbortSignal): AsyncIterable<SessionFollowFrame>
728
+
729
+ /**
730
+ * Stream a complete live-control baseline followed by replacement frames.
731
+ * @param signal - cancellation owned by the Remote stream carrier.
732
+ * @returns one complete baseline followed by live replacement frames.
733
+ */
734
+ @Remote({ mode: 'stream' }) control(signal: AbortSignal): AsyncIterable<SessionControlFrame>
735
+ ```
736
+
737
+ Types: [SessionHeader](./persistence.md) · [SessionId](./core.md) · [SessionSearchRequest](./session-query.md)
738
+
739
+ Source: [`packages/api/session-controller/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/index.ts)
740
+
622
741
  <a id="ctxsessions--sessionstore"></a>
623
742
 
624
743
  ### `ctx.sessions` — `SessionStore`
@@ -755,6 +874,106 @@ Types: [CreateSessionOptions](./persistence.md) · [PrepareSessionOptions](./per
755
874
 
756
875
  Source: [`packages/core/session/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/src/index.ts)
757
876
 
877
+ <a id="api-session-events"></a>
878
+
879
+ ### `api-session/*` events
880
+
881
+ <a id="api-sessionactivity--emit"></a>
882
+
883
+ #### `api-session/activity` — emit
884
+
885
+ One user-authored durable message advanced Session list activity.
886
+
887
+ ```ts cordis-catalog
888
+ /**
889
+ * One user-authored durable message advanced Session list activity.
890
+ * @mode emit
891
+ * @param sessionId - addressed Session identity.
892
+ * @param updatedAt - durable message time used for list ordering.
893
+ */
894
+ 'api-session/activity'(sessionId: SessionId, updatedAt: number): void
895
+ ```
896
+
897
+ Types: [SessionId](./core.md)
898
+
899
+ Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
900
+
901
+ <a id="api-sessionadded--emit"></a>
902
+
903
+ #### `api-session/added` — emit
904
+
905
+ A Session became visible to Session list consumers.
906
+
907
+ ```ts cordis-catalog
908
+ /**
909
+ * A Session became visible to Session list consumers.
910
+ * @mode emit
911
+ * @param summary - initial list row for the Session.
912
+ */
913
+ 'api-session/added'(summary: SessionSummary): void
914
+ ```
915
+
916
+ Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
917
+
918
+ <a id="api-sessionerror--emit"></a>
919
+
920
+ #### `api-session/error` — emit
921
+
922
+ One Agent failed outside a durable turn position.
923
+
924
+ ```ts cordis-catalog
925
+ /**
926
+ * One Agent failed outside a durable turn position.
927
+ * @mode emit
928
+ * @param sessionId - Agent and Session identity.
929
+ * @param message - user-safe failure chain.
930
+ */
931
+ 'api-session/error'(sessionId: SessionId, message: string): void
932
+ ```
933
+
934
+ Types: [SessionId](./core.md)
935
+
936
+ Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
937
+
938
+ <a id="api-sessionremoved--emit"></a>
939
+
940
+ #### `api-session/removed` — emit
941
+
942
+ A Session left the live Host registry.
943
+
944
+ ```ts cordis-catalog
945
+ /**
946
+ * A Session left the live Host registry.
947
+ * @mode emit
948
+ * @param sessionId - removed Session identity.
949
+ */
950
+ 'api-session/removed'(sessionId: SessionId): void
951
+ ```
952
+
953
+ Types: [SessionId](./core.md)
954
+
955
+ Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
956
+
957
+ <a id="api-sessionstatus--emit"></a>
958
+
959
+ #### `api-session/status` — emit
960
+
961
+ One Agent changed running state.
962
+
963
+ ```ts cordis-catalog
964
+ /**
965
+ * One Agent changed running state.
966
+ * @mode emit
967
+ * @param sessionId - Agent and Session identity.
968
+ * @param running - whether the Agent is running.
969
+ */
970
+ 'api-session/status'(sessionId: SessionId, running: boolean): void
971
+ ```
972
+
973
+ Types: [SessionId](./core.md)
974
+
975
+ Source: [`packages/api/session-controller/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/types.ts)
976
+
758
977
  <a id="session-events"></a>
759
978
 
760
979
  ### `session/*` events
@@ -54,7 +54,7 @@ interface SettingsRegisterOptions<T> {
54
54
 
55
55
  `validate` runs after the schema admits a value, so it sees defaults and the composition base exactly as the owner will. `dsh-llm-pi-ai` uses it to refuse a provider profile it could not serve at the write that produced it, rather than storing one that would disable every route in its namespace.
56
56
 
57
- `applies` is a UI hint, not a mechanism: a `restart` owner simply never watches, so its value is read once at construction and configuration surfaces can badge the pending change.
57
+ `applies` is a UI hint, not a mechanism: a `restart` owner never watches, so its value is read once at construction and configuration surfaces can badge the pending change.
58
58
 
59
59
  ```ts type-equiv
60
60
  /** When a namespace's changes take effect for its owner. */
@@ -164,6 +164,10 @@ Every committed change — an in-process write or an externally observed provide
164
164
  type SettingsUpdateSource = 'update' | 'provider'
165
165
  ```
166
166
 
167
+ ## Native document operations
168
+
169
+ `SettingsDocumentOpenValue` confirms that `settings/openSettingsDocument` prepared the provider-owned document and handed it to the native text editor. `AgentPresetDirectoryOpenValue` reports either a completed native handoff or the resolved user-preset directory when desktop opening is unavailable. Neither operation accepts a browser-selected Host path.
170
+
167
171
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
168
172
 
169
173
  <a id="cordis-surface"></a>
@@ -257,6 +261,79 @@ async mutate(ns: SettingsNamespace, ops: readonly SettingsPathOp[], expectedRevi
257
261
 
258
262
  Source: [`packages/settings/settings/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/settings/settings/src/index.ts)
259
263
 
264
+ <a id="ctxsettingscontroller--settingscontroller"></a>
265
+
266
+ ### `ctx.settingsController` — `SettingsController`
267
+
268
+ Host service backing the generated `ctx.remote.settings` namespace. Every remote read uses `redactSecrets: true`, so a `role('secret')` field cannot ride a response. Writes expose the settings service's merge, replacement, and path-addressed operations, and classify every provider refusal as `settings-conflict` or `settings-rejected` with the service's message.
269
+
270
+ ```ts cordis-catalog
271
+ /**
272
+ * Describe every registered namespace for a configuration page: redacted
273
+ * layered values plus the serialized schema the page renders its form from.
274
+ * @returns provider writability, local-document presence, and one view per namespace.
275
+ * @throws TypertRemoteFailure when no settings provider is mounted.
276
+ */
277
+ @Remote describe(): SettingsDescribeValue
278
+
279
+ /**
280
+ * Report whether this deployment can open an authored Agent preset directory natively.
281
+ * @returns true when the matching open operation is available.
282
+ */
283
+ @Remote canOpenAgentPresetDirectory(): boolean
284
+
285
+ /**
286
+ * Merge a patch into one namespace's stored user section.
287
+ * @param ns - namespace key to write.
288
+ * @param patch - fields to merge into the user section.
289
+ * @param expectedRevision - revision the caller read; `undefined` writes unconditionally.
290
+ * @returns the namespace's redacted view after the write.
291
+ * @throws TypertRemoteFailure when the request is invalid, no provider is mounted, or the provider refuses the write.
292
+ */
293
+ @Remote update( ns: string, patch: Record<string, JsonValue>, expectedRevision: number | undefined, ): Promise<SettingsNamespaceView>
294
+
295
+ /**
296
+ * Replace one namespace's stored user section wholesale.
297
+ * @param ns - namespace key to write.
298
+ * @param section - complete replacement user section.
299
+ * @param expectedRevision - revision the caller read; `undefined` writes unconditionally.
300
+ * @returns the namespace's redacted view after the write.
301
+ * @throws TypertRemoteFailure when the request is invalid, no provider is mounted, or the provider refuses the write.
302
+ */
303
+ @Remote replace( ns: string, section: Record<string, JsonValue>, expectedRevision: number | undefined, ): Promise<SettingsNamespaceView>
304
+
305
+ /**
306
+ * Apply path-addressed edits to one namespace's user section, resolved against
307
+ * the section as stored rather than against whatever the caller last read,
308
+ * then answer with that namespace's new redacted view.
309
+ * @param ns - namespace key to write.
310
+ * @param ops - the edits to apply, in order.
311
+ * @param expectedRevision - revision the caller read; `undefined` writes unconditionally.
312
+ * @returns the namespace's redacted view after the write.
313
+ * @throws TypertRemoteFailure when the request is invalid, no provider is mounted, or the provider refuses the write.
314
+ */
315
+ @Remote async mutate( ns: string, ops: SettingsPathOpView[], expectedRevision: number | undefined, ): Promise<SettingsNamespaceView>
316
+
317
+ /**
318
+ * Materialize the provider-owned settings document and open it in a native text editor.
319
+ * @param signal - caller lifetime; abort terminates preparation or the native command.
320
+ * @returns confirmation after the native opener accepts the document.
321
+ * @throws TypertRemoteFailure when no document exists, preparation fails, or opening fails.
322
+ */
323
+ @Remote async openSettingsDocument(signal: AbortSignal): Promise<SettingsDocumentOpenValue>
324
+
325
+ /**
326
+ * Open one user-authored Agent preset directory or return its path when no native opener exists.
327
+ * @param agentPreset - preset id resolved against Host-owned roots.
328
+ * @param signal - caller lifetime; abort terminates the native command.
329
+ * @returns an opened confirmation or the resolved directory for text display.
330
+ * @throws TypertRemoteFailure when the preset is missing, read-only, invalid, or cannot be opened.
331
+ */
332
+ @Remote async openAgentPresetDirectory( agentPreset: string, signal: AbortSignal, ): Promise<AgentPresetDirectoryOpenValue>
333
+ ```
334
+
335
+ Source: [`packages/api/settings-controller/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/settings-controller/src/index.ts)
336
+
260
337
  <a id="settings-events"></a>
261
338
 
262
339
  ### `settings/*` events
@@ -237,6 +237,10 @@ Before each later model step, the consumer applies exact tool visibility and dig
237
237
 
238
238
  The model-facing `skill({ name })` tool validates the kebab-case name, finds the summary in the invocation-neutral catalog, rejects it before loading unless `isModelInvocable` permits access, then rereads the complete definition for the calling agent cwd and rechecks the policy before returning content. It reports an unresolved skill as unknown or no longer available and returns a tool result containing `<skill_content name="...">`, `<skill_resources>`, and `<skill_instructions>`. `resourceBase` resolves explicitly referenced scripts, references, and assets only as needed; the loaded result does not enumerate a skill directory. Body-only edits therefore change later tool calls without producing catalog messages or rewriting earlier tool results.
239
239
 
240
+ ## Browser Session catalog
241
+
242
+ `SkillListRequest` addresses one Session by `sessionId`; `SkillListValue` returns the user-invocable entries with name, description, optional usage guidance, and model-invocation availability. `SessionSkillCatalog` reads the Session cwd and recorded preset without activating an Agent. A live Agent may supply its scoped registry, while a cold Session uses the preset's standing scope.
243
+
240
244
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
241
245
 
242
246
  <a id="cordis-surface"></a>
@@ -245,6 +249,25 @@ The model-facing `skill({ name })` tool validates the kebab-case name, finds the
245
249
 
246
250
  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).
247
251
 
252
+ <a id="ctxsessionskillcatalog--sessionskillcatalog"></a>
253
+
254
+ ### `ctx.sessionSkillCatalog` — `SessionSkillCatalog`
255
+
256
+ Host service backing `ctx.remote.skills` without activating a cold Agent.
257
+
258
+ ```ts cordis-catalog
259
+ /**
260
+ * List the user-invocable skills visible to one Session composition.
261
+ * @param request - Session identity whose cwd and preset select the catalog view.
262
+ * @param signal - caller lifetime carried by the Remote transport; admitted catalog reads retain their existing completion semantics.
263
+ * @returns user-invocable skill metadata without loading skill bodies.
264
+ * @throws TypertRemoteFailure when the Session cannot be inspected or no registry can serve it.
265
+ */
266
+ @Remote async list(request: SkillListRequest, signal: AbortSignal): Promise<SkillListValue>
267
+ ```
268
+
269
+ Source: [`packages/api/session-controller/src/skill-catalog.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/skill-catalog.ts)
270
+
248
271
  <a id="ctxskills--skillregistry"></a>
249
272
 
250
273
  ### `ctx.skills` — `SkillRegistry`