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
@@ -24,8 +24,8 @@ interface UserMessage extends Message {
24
24
  /**
25
25
  * The merge-extensible, append-only source of truth for an agent interaction.
26
26
  * Message history is derived from this log. Every event is lossless JSON and
27
- * sequence numbers stay contiguous, including raw chunks, so persistence can
28
- * store the canonical log verbatim.
27
+ * sequence numbers stay contiguous. Assistant attempt events embed their exact
28
+ * compact raw streams so persistence stores one durable settlement per attempt.
29
29
  */
30
30
  interface SessionEventMap {
31
31
  /**
@@ -56,8 +56,6 @@ interface SessionEventMap {
56
56
  * project their `content` verbatim; `source` tells them apart.
57
57
  */
58
58
  'user/message': UserMessage
59
- /** Raw stream chunk — token-level replay fidelity. */
60
- 'assistant/chunk': { turn: number; step: number; chunk: StreamChunk }
61
59
  /**
62
60
  * Assembled assistant message for one step (derived history uses this).
63
61
  * Carries the step's `usage` when the adapter reported token accounting, so
@@ -68,7 +66,21 @@ interface SessionEventMap {
68
66
  * marker distinguishes that prefix without re-deriving interruption from turn
69
67
  * boundaries. An aborted turn with no such event streamed no visible content.
70
68
  */
71
- 'assistant/message': { turn: number; step: number; message: AssistantMessage; usage?: TokenUsage; interrupted?: true }
69
+ 'assistant/message': {
70
+ turn: number
71
+ step: number
72
+ message: AssistantMessage
73
+ /** Exact timed model stream, compacted without joining delta boundaries. */
74
+ stream: AssistantStreamRecord[]
75
+ usage?: TokenUsage
76
+ interrupted?: true
77
+ }
78
+ /**
79
+ * One model attempt that committed no surface message. The embedded stream
80
+ * preserves a failed, retried, cancelled, or stream-error attempt that
81
+ * reached settlement without fabricating model-visible history.
82
+ */
83
+ 'assistant/attempt': { turn: number; step: number; stream: AssistantStreamRecord[] }
72
84
  /**
73
85
  * The model requested one tool invocation: `name` with the raw `arguments`
74
86
  * JSON string exactly as the model produced it (unparsed). `callId` pairs the
@@ -112,12 +124,12 @@ interface SessionEventMap {
112
124
  * Marks the end of a constructor seed. Events before it have smaller seq
113
125
  * values and came from the seed (resume, fork, or replay); this lifecycle
114
126
  * produced none of them. This log-only event is the durable projection of
115
- * {@link Session.firstLiveSeq}. Its payload is empty — position and `time`
116
- * carry the meaning.
127
+ * {@link Session.firstLiveSeq}.
117
128
  *
118
- * Locate the LAST one in stored history. A seed already ending in one is not
119
- * re-marked, so reopening an untouched session does not grow its log per
120
- * pickup and the event need not be at the current `firstLiveSeq`.
129
+ * A fresh fork child owns one `{ inherited: true }` marker at its exact
130
+ * inherited-prefix cut, even when that prefix ends in an ancestor marker.
131
+ * The last tagged marker is the current Session's cut; untagged markers keep
132
+ * ordinary restore and replay lifecycle boundaries.
121
133
  *
122
134
  * `Session`'s constructor is the only legitimate writer. The invariant
123
135
  * companion deliberately constrains nothing here, so a plugin appending one
@@ -130,7 +142,7 @@ interface SessionEventMap {
130
142
  * writers — a concurrently live session holds its own boundary elsewhere,
131
143
  * so tolerating concurrent writers needs a signal beyond the log.
132
144
  */
133
- 'session/end-seed': Record<string, never>
145
+ 'session/end-seed': { inherited?: true }
134
146
  }
135
147
  ```
136
148
 
@@ -182,6 +194,28 @@ interface RequestContext {
182
194
 
183
195
  基于 `type` 的真正可辨识联合(而非独立的 `type`/`data` 联合),因此 `switch (event.type)` 能直接收窄 `event.data`,无需类型断言。`seq` 是日志中的单调递增位置(`seq = log.length`);`time` 为 epoch 毫秒。
184
196
 
197
+ ```ts type-equiv
198
+ /** Sequence number of one existing event in a Session log. */
199
+ type SessionSeq = BrandedNumber<'SessionSeq'>
200
+ ```
201
+
202
+ ```ts type-equiv
203
+ /** A Session log gap, prefix length, or read offset, which may equal the event count. */
204
+ type SessionLogOffset = BrandedNumber<'SessionLogOffset'>
205
+ ```
206
+
207
+ ```ts type-equiv
208
+ /** Inclusive Session event watermark, or `-1` before any event exists. */
209
+ type SessionSeqCursor = SessionSeq | -1
210
+ ```
211
+
212
+ ```ts type-equiv
213
+ /** One existing Session event position, or explicit absence. */
214
+ type OptionalSessionSeq = SessionSeq | null
215
+ ```
216
+
217
+ `SessionSeq(value)` 与 `SessionLogOffset(value)` 只接纳非负安全整数,并拒绝负零。它们仅添加编译期品牌,不改变序列化后的数值;算术会返回普通 `number`,调用方必须按结果的预期角色通过对应构造器重新接纳。
218
+
185
219
  ```ts type-equiv
186
220
  /**
187
221
  * One immutable entry in the session log.
@@ -192,7 +226,7 @@ interface RequestContext {
192
226
  * The {@link sourceEventSeqs} and {@link surfaceOp} fields are conditional:
193
227
  * they only exist on {@link SurfaceEventType} variants (`user/message`,
194
228
  * `assistant/message`, `tool/result`).
195
- * Non-surface events (boundary markers, chunks, usage, errors) never carry
229
+ * Non-surface events (boundary markers, attempts, errors) never carry
196
230
  * surface metadata — the compiler enforces this at `Session.append()`
197
231
  * call sites.
198
232
  */
@@ -200,20 +234,28 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
200
234
  [K in SessionEventType]: {
201
235
  type: K
202
236
  /** Monotonic sequence number within the session. */
203
- seq: number
237
+ seq: SessionSeq
204
238
  /** Unix epoch milliseconds. */
205
239
  time: number
206
240
  data: SessionEventMap[K]
241
+ /**
242
+ * Marks an event a reader may safely skip when it does not recognize
243
+ * `type`. Absent means required: a reader meeting an unrecognized type
244
+ * without this marker MUST refuse to reconstruct the session instead of
245
+ * silently dropping the event, because an unrecognized required event may
246
+ * change how the rest of the log is interpreted. A writer sets `true` only
247
+ * on purely informational records whose loss cannot affect reconstruction;
248
+ * defaulting to required means a forgotten marker over-refuses (an
249
+ * inconvenience) rather than silently resuming a gutted session.
250
+ */
251
+ ignorable?: true
207
252
  } & (K extends SurfaceEventType ? {
208
253
  /**
209
- * Seq numbers of earlier events that this event cites as sources
210
- * (e.g. the `assistant/chunk` seqs that built an `assistant/message`,
211
- * or the surface nodes shadowed by a compaction replace node). An
212
- * `assistant/message` may carry a present empty array for a known empty
213
- * provider stream; when the field is absent, the event does not record which
214
- * earlier events produced the message.
254
+ * Seq numbers of earlier events that this event cites as sources, such as
255
+ * the surface nodes shadowed by a compaction replacement. A v2
256
+ * `assistant/message` embeds its provider stream and cannot carry this field.
215
257
  */
216
- sourceEventSeqs?: number[]
258
+ sourceEventSeqs?: SessionSeq[]
217
259
  /** How this event entered the surface; absent for non-surface events. */
218
260
  surfaceOp?: SurfaceOp
219
261
  } : object)
@@ -222,7 +264,7 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
222
264
 
223
265
  `SessionEventType = keyof SessionEventMap`。由于 `SessionEventMap` 可通过合并扩展,对 `SessionEvent` 的 switch 语句禁止使用 `assertNever`:插件添加的变体是合法的未知值;处理已知 case 后在 `default` 中放行。
224
266
 
225
- 对于 `assistant/message`,存在的 `sourceEventSeqs: []` 表示提供方流已知且完整地为空;旧格式或外部事件缺少该字段时,没有记录这条消息由哪些早期事件产生。agent loop 会为每次成功的模型调用写入该字段;其他 surface 事件只要包含该字段,其列表就必须非空。
267
+ V2 `assistant/message` 嵌入 provider stream,不能携带 `sourceEventSeqs`。User tool surface event 可以在 provenance 或 replacement operation 需要时引用完整且非空的唯一较早 event 集合。
226
268
 
227
269
  <a id="surface-types"></a>
228
270
 
@@ -236,7 +278,8 @@ type SessionEvent<T extends SessionEventType = SessionEventType> = {
236
278
  /**
237
279
  * The subset of {@link SessionEventType} values whose events produce LLM
238
280
  * messages and are eligible to appear on the ordered surface. Only these
239
- * event types may carry {@link SurfaceOp} and {@link SessionEvent.sourceEventSeqs}.
281
+ * event types may carry {@link SurfaceOp}; user and tool events may also cite
282
+ * earlier sources through {@link SessionEvent.sourceEventSeqs}.
240
283
  */
241
284
  type SurfaceEventType =
242
285
  | 'user/message'
@@ -262,7 +305,7 @@ type SurfaceEventType =
262
305
  */
263
306
  type SurfaceOp =
264
307
  | 'append'
265
- | { op: 'replace'; start: number; end: number }
308
+ | { op: 'replace'; start: SessionSeq; end: SessionSeq }
266
309
  ```
267
310
 
268
311
  `'append'` 是常规的尾部追加路径。`replace` 会遮蔽从 `start` 到 `end`(含两端)的 surface 条目(两者都必须是有效的 surface seq;`start === end` 时仅替换单个条目),并在原位置插入新事件。
@@ -274,21 +317,20 @@ type SurfaceOp =
274
317
  * Surface placement and cited source-event seqs for {@link Session.append}. Required on
275
318
  * message-producing events and forbidden on log-only events.
276
319
  */
277
- interface SurfaceIntent {
320
+ type SurfaceIntent<T extends SurfaceEventType = SurfaceEventType> = {
278
321
  surfaceOp: SurfaceOp
279
- /**
280
- * Complete set of known source-event seqs. `assistant/message` may use a
281
- * present empty array for a known empty provider stream; when the field is
282
- * absent, the event does not record which earlier events produced the message.
283
- * Other surface events require a non-empty set when this field is present.
284
- */
285
- sourceEventSeqs?: number[]
286
- }
322
+ } & (T extends 'assistant/message' ? {
323
+ /** V2 Assistant messages embed their provider stream instead of citing source events. */
324
+ sourceEventSeqs?: never
325
+ } : {
326
+ /** Complete non-empty set of known earlier source-event seqs. */
327
+ sourceEventSeqs?: SessionSeq[]
328
+ })
287
329
  ```
288
330
 
289
331
  对 `SurfaceEventType` 事件必填:每个产生消息的事件都必须声明它如何加入 surface(派生模型历史的唯一来源)。面向人类的 transcript(文本记录)是另一个投影,读取的是日志中追加来源的事件,因为 surface 会有意遮蔽替换所概括的范围(见 [dsh-session](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/core/session/README.zh.md) 的 `isAppendSurfaceEvent`)。非 surface 类型在编译期拒绝此参数。
290
332
 
291
- 只有 `assistant/message` 可以携带存在但为空的 `sourceEventSeqs`;字段不存在时,该事件没有记录这条消息由哪些早期事件产生,但提供方仍可能发出过分片。
333
+ `assistant/message` 不能携带 `sourceEventSeqs`;它的 `stream` 拥有精确 provider 证据。其他 surface event 不引用较早 event 时省略该字段,需要引用时使用完整非空 list。
292
334
 
293
335
  ### `SessionSurface`:实时只读 surface 投影
294
336
 
@@ -300,7 +342,7 @@ interface SurfaceIntent {
300
342
  /** Readonly live projection of the message-producing session events. */
301
343
  interface SessionSurface {
302
344
  /** Current surface event sequences in model-visible order. */
303
- readonly nodes: readonly number[]
345
+ readonly nodes: readonly SessionSeq[]
304
346
  /** Monotonic count of committed positional replacements. */
305
347
  readonly replaceGeneration: number
306
348
  }
@@ -314,13 +356,13 @@ interface SessionSurface {
314
356
  /** One replacement operation observed while folding a session surface. */
315
357
  interface SurfaceFoldReplacement {
316
358
  /** Seq of the event that replaced the prior surface range. */
317
- seq: number
359
+ seq: SessionSeq
318
360
  /** Declared inclusive start seq of the replaced surface range. */
319
- start: number
361
+ start: SessionSeq
320
362
  /** Declared inclusive end seq of the replaced surface range. */
321
- end: number
363
+ end: SessionSeq
322
364
  /** Actual surface entries removed by the operation, in surface order. */
323
- shadowedSeqs: number[]
365
+ shadowedSeqs: SessionSeq[]
324
366
  }
325
367
  ```
326
368
 
@@ -328,7 +370,7 @@ interface SurfaceFoldReplacement {
328
370
  /** Complete result of replaying the surface operations in a session log. */
329
371
  interface SurfaceFoldResult {
330
372
  /** Current surface event sequences in model-visible order. */
331
- nodes: number[]
373
+ nodes: SessionSeq[]
332
374
  /** Replacement operations in event order. */
333
375
  replacements: SurfaceFoldReplacement[]
334
376
  }
@@ -352,24 +394,27 @@ declare class Session {
352
394
  get surface(): SessionSurface;
353
395
  /**
354
396
  * Detached, deep-frozen creation metadata (format version, cwd, lineage,
355
- * seed boundary). Supplied by the store via `ctx.sessions.create()`. When a
397
+ * and whether fork history exists). Supplied by the store via `ctx.sessions.create()`. When a
356
398
  * `Session` is created without a store-owned header, a minimal header is
357
399
  * synthesized (stamped with the current {@link SESSION_FORMAT_VERSION}) so
358
400
  * `session.header` is always present. Kept out of the event log — it is a
359
401
  * storage concern, not replayable conversation state.
360
402
  */
361
403
  readonly header: SessionHeader;
404
+ /** Number of leading events inherited from this Session's fork parent. */
405
+ readonly inheritedEventCount: SessionLogOffset;
362
406
  /** The session identity, derived from its durable header's single copy. */
363
407
  get id(): SessionId;
364
408
  /**
365
409
  * The first seq appended IN THIS PROCESS: the length of the constructor
366
410
  * seed (0 without one). Events with smaller seq values entered through
367
411
  * construction — replay, fork, or resume — and were never published on the
368
- * `session/event` firehose (constructor seeds do not emit), so consumers
369
- * that replay the log as a publication substitute (telemetry adoption)
370
- * start here. Distinct from `header.seedLength`, the DURABLE fork-lineage
371
- * boundary: a resumed session's constructor seed is its full stored log,
372
- * while its header keeps the original fork value this field is the
412
+ * `session/event` firehose (constructor seeds do not emit). This offset marks
413
+ * the constructor-input boundary for lifecycle ownership and persistence
414
+ * adoption; consumers that need complete canonical history still start at
415
+ * seq 0. Distinct from {@link inheritedEventCount}, the DURABLE
416
+ * fork-lineage cut: a resumed session's constructor seed is its full stored
417
+ * log, while the inherited count keeps the original fork value — this field is the
373
418
  * in-process construction fact.
374
419
  *
375
420
  * Not persisted itself: a seeded session projects it into the log as the
@@ -382,17 +427,23 @@ declare class Session {
382
427
  * When this lifecycle appends the marker, it occupies this seq before the
383
428
  * store attaches and therefore does not publish either. Otherwise this seq
384
429
  * holds an ordinary published write.
385
- */
386
- readonly firstLiveSeq: number;
430
+ */
431
+ readonly firstLiveSeq: SessionLogOffset;
387
432
  /**
388
433
  * Create a detached session by validating and snapshotting borrowed seed
389
434
  * events and storage metadata.
390
435
  * @param id - session identity.
391
436
  * @param seed - optional borrowed replay or fork events.
392
437
  * @param header - optional borrowed storage metadata.
438
+ * @param inheritedEventCount - exact fork-inherited prefix length for a seeded header.
393
439
  * @returns a detached session.
394
440
  */
395
- static create(id: SessionId, seed?: readonly SessionEvent[], header?: SessionHeader): Session;
441
+ static create(
442
+ id: SessionId,
443
+ seed?: readonly SessionEvent[],
444
+ header?: SessionHeader,
445
+ inheritedEventCount?: SessionLogOffset,
446
+ ): Session;
396
447
  /**
397
448
  * Restore a detached session by taking ownership of fresh persistence values.
398
449
  * The storage format, event envelopes, sequence continuity, surface transitions,
@@ -400,18 +451,46 @@ declare class Session {
400
451
  * @param id - restored session identity.
401
452
  * @param seed - fresh detached events whose ownership is transferred.
402
453
  * @param header - fresh detached metadata whose ownership is transferred.
454
+ * @param inheritedEventCount - exact fork-inherited prefix length decoded from storage.
403
455
  * @returns a restored detached session.
404
456
  */
405
- static fromRestore(id: SessionId, seed: readonly SessionEvent[], header: SessionHeader): Session;
457
+ static fromRestore(
458
+ id: SessionId,
459
+ seed: readonly SessionEvent[],
460
+ header: SessionHeader,
461
+ inheritedEventCount: SessionLogOffset,
462
+ ): Session;
463
+ /**
464
+ * Return the immutable event stored at one exact sequence number.
465
+ * @param seq - event sequence number.
466
+ * @returns the accepted event, or undefined when the log does not contain it.
467
+ */
468
+ eventAt(seq: SessionSeq): SessionEvent | undefined;
469
+ /**
470
+ * Materialize an immutable snapshot of a half-open event sequence range.
471
+ * A full current snapshot is reused until the next append; every previously
472
+ * returned snapshot remains stable after later appends.
473
+ * @param fromSeq - non-negative inclusive sequence number; defaults to the log start.
474
+ * @param toSeqExclusive - non-negative exclusive sequence number; defaults to the current end.
475
+ * @returns a frozen array of the selected deeply frozen events.
476
+ */
477
+ snapshotEvents(
478
+ fromSeq: SessionLogOffset = SessionLogOffset(0),
479
+ toSeqExclusive: SessionLogOffset = this.seq,
480
+ ): readonly SessionEvent[];
481
+ /**
482
+ * Return this Session's events after its fork-inherited prefix.
483
+ * @returns a fresh array containing child-owned events in log order.
484
+ */
485
+ ownEvents(): readonly SessionEvent[];
406
486
  /**
407
- * An immutable snapshot of the append-only event log. The snapshot is reused
408
- * until the next append; a previously returned array does not grow later.
409
- * Events and their nested data are deep-frozen at acceptance, so neither a
410
- * cast nor ordinary JavaScript can rewrite durable history.
487
+ * Whether one existing event position is outside the fork-inherited prefix.
488
+ * @param seq - event position in this Session.
489
+ * @returns true when the event belongs to this Session rather than its parent.
411
490
  */
412
- get events(): readonly SessionEvent[];
491
+ isOwnSeq(seq: SessionSeq): boolean;
413
492
  /** The next event's sequence number — always the log length (the `seq = log.length` contiguity contract). */
414
- get seq(): number;
493
+ get seq(): SessionLogOffset;
415
494
  /**
416
495
  * Append one typed event to the log and synchronously notify observers via
417
496
  * the store-owned, module-private publication hooks. The hot path never blocks
@@ -429,7 +508,8 @@ declare class Session {
429
508
  * declare how it joins the surface, the sole source of derived model
430
509
  * history) and
431
510
  * rejected by the compiler for non-surface types like `turn/start` or
432
- * `assistant/chunk`.
511
+ * `assistant/attempt`. Assistant messages embed their exact provider
512
+ * stream and cannot cite top-level source events.
433
513
  * @returns the logged event — its assigned `seq`/`time` plus the SNAPSHOT of
434
514
  * `data` that entered the log, so reading `event.data` back sees the logged
435
515
  * value, never the caller's still-mutable input.
@@ -439,7 +519,7 @@ declare class Session {
439
519
  * Map/Set/Date/class instance), or when the candidate violates the
440
520
  * canonical surface contract (marker shape and eligibility, unique
441
521
  * earlier source-event references, positional replacement validity, and complete
442
- * shadowed-node coverage). One recursive pass reads, validates, and
522
+ * shadowed-node coverage). One iterative pass reads, validates, and
443
523
  * copies each nested value once, so a stateful getter cannot supply one value
444
524
  * to validation and another to storage. The event log is the durable source
445
525
  * of truth, so a bad event fails at the append site rather than later during
@@ -450,13 +530,13 @@ declare class Session {
450
530
  append<T extends SessionEventType>(
451
531
  type: T,
452
532
  data: SessionEventMap[T],
453
- ...opts: T extends SurfaceEventType ? [opts: SurfaceIntent] : []
533
+ ...opts: T extends SurfaceEventType ? [opts: SurfaceIntent<T>] : []
454
534
  ): SessionEvent<T>;
455
535
  /**
456
536
  * The {@link EpochHeader} in force after the log's last header event — the
457
537
  * header the NEXT request will be compared against — or undefined before
458
538
  * the first `request/header` snapshot. The live, incrementally-maintained
459
- * form of `foldRequestHeader(session.events)`: each header event is folded
539
+ * form of `foldRequestHeader(session.snapshotEvents())`: each header event is folded
460
540
  * once, when first seen, so a per-step read costs O(new events).
461
541
  * @returns the folded header, or undefined when no header event exists yet.
462
542
  */
@@ -501,17 +581,17 @@ declare class Session {
501
581
  `Session.deriveMessages()` 将事件日志投影为模型看到的 `Message[]`。它是缓存的(每个 surface 节点在首次出现时投影一次;surface 重写触发重建)且冻结的(每次调用返回一个新数组,引用共享的深冻结消息,因此通过投影修改已记录的历史在类型上不可表达)。`deriveEventMessage(event)` 是折叠所应用的逐节点纯函数,公开暴露以便外部重建器和开发不变式检查能以完全相同的规则投影日志前缀,不会与缓存产生分歧。投影规则:
502
582
 
503
583
  - `user/message` → 一条携带确切 `content` 的 user 消息;可选 envelope 仅作为日志中的展示元数据保留。
504
- - `assistant/message` → 一条 assistant 消息,包含生成它的提供方和模型,以及可选的适配器私有回放状态。原始 `assistant/chunk` 事件属于回放/UI 数据,在派生时会被**跳过**(组装后的消息才是权威)。**内容为空的** `assistant/message` 也会跳过:因 max-tokens 而截断且无内容的步骤仍会记录一条 `assistant/message` 来保存用量、提供方和模型,但无内容的 assistant 轮次不得进入提供方 transcript(文本记录)。
584
+ - `assistant/message` → 一条 assistant 消息,包含生成它的提供方和模型,以及可选的适配器私有回放状态。其嵌入式紧凑 stream 是回放、usage 与 UI 证据,而不是第二条 message。**内容为空的** `assistant/message` 也会跳过:因 max-tokens 而截断且无内容的步骤仍会记录一条 `assistant/message` 来保存 stream、usage、提供方和模型,但无内容的 assistant 轮次不得进入提供方 transcript(文本记录)。
505
585
  - `tool/result` → 一条携带 `tool-result` 块的 user 消息。
506
586
  - `user/message`(注入上下文,即非 `user` 来源)→ 按时间顺序在相应位置生成一条 user-role 消息,并原样承载其 `content`;其类型化 source 标明生产方,并携带所有生产方专用数据。
507
587
 
508
- 其余所有事件(`turn/*`、`step/*`、插件所属的 `llm/retry`)均为结构信息,不会投影为消息。token 记账读取每个步骤的 `assistant/chunk { type: 'usage' }` 记录;如果没有用量分片,则将 `assistant/message.usage` 作为已提交步骤的后备。失败的模型请求尝试没有 assistant 消息,因此其用量分片是持久化的记账记录。由于这一尚未发布的格式有意不提供兼容性承诺,seed/load 校验会拒绝没有提供方/模型的请求头和 assistant 消息,而不会猜测历史数据应走的提供方路由。
588
+ 其余所有事件(`turn/*`、`step/*`、`assistant/attempt`、插件所属的 `llm/retry`)均为结构信息,不会投影为消息。token 记账会展开每个 `assistant/message` `assistant/attempt` 的嵌入式 stream,message 顶层 `usage` 存在时仍是已提交 message 的权威。失败的模型请求 attempt 因此可以保留提供方 usage,而无需虚构 assistant message。当前逻辑校验会拒绝没有提供方/模型的 request header 和 assistant 消息,而不会猜测路由;受支持的历史表示会在当前 Session 存在前,由其相邻格式迁移边归一化并校验。
509
589
 
510
590
  ## 活跃会话 fork API
511
591
 
512
592
  `ctx.sessions.create(id, { seed, meta })` 是底层的回放/fork 原语。对于普通的活跃会话 fork,`SessionStore` 暴露一个策略 API:
513
593
 
514
- - `fork(source, boundary?, childSessionId?)` 接受一个活跃的 `Session` 对象或活跃的 `SessionId`,选取到 `boundary` seq(含)为止的源事件(默认为当前最后一个事件),要求所选前缀结束时没有开放轮次,然后创建一个活跃的子会话,包含深克隆的种子事件和子会话元数据(`parentSession`、`seedLength` 及继承的 `cwd`)。
594
+ - `fork(source, boundary?, childSessionId?)` 接受一个活跃的 `Session` 对象或活跃的 `SessionId`,选取到 `SessionSeq` boundary(含)为止的源事件(默认为当前最后一个事件),要求所选前缀结束时没有开放轮次,然后创建一个活跃的子会话,包含深克隆的 seed event、`parentSession`、`isSeeded: true`、精确 `inheritedEventCount` 及继承的 `cwd`。
515
595
 
516
596
  显式 `boundary` 允许调用者从任意稳定的轮次间位置 fork,包括之前的 `turn/end` 或更晚的独立纯日志事件,即使源会话有更新的事件或正在进行的轮次。API 拒绝结束于开放轮次内的前缀,而不是静默截断。更广泛的执行关系健全性检查留在既有的 `dsh-invariants` 插件和持久化修复路径中,不在 `fork()` 中重复。`dsh-subagent-fork-in-process` 保留其已完成前缀截断逻辑,因为工具调用时的委托通常在父轮次仍然打开时启动;普通的会话分支应显式指定请求的 boundary。
517
597
 
@@ -545,8 +625,10 @@ interface TurnEndReasonMap {
545
625
  /** At least one step reached its output-token ceiling, even if a plugin continued the turn. */
546
626
  'max-tokens': { kind: 'max-tokens' }
547
627
  /**
548
- * A persistence backend closed a crash-orphaned turn on reload. The loop never
549
- * emits this marker, and the events recorded before the crash remain intact.
628
+ * A crash-orphaned turn was closed after the fact: agent-loop resume appends
629
+ * this closer for a stored log whose last turn never ended, and session-query
630
+ * synthesizes it on cold reads. The loop never emits this marker live, and
631
+ * the events recorded before the crash remain intact.
550
632
  */
551
633
  interrupted: { kind: 'interrupted' }
552
634
  }
@@ -562,9 +644,9 @@ interface TurnEndReasonMap {
562
644
 
563
645
  ## 种子结束边界:`session/end-seed`
564
646
 
565
- 带种子的会话(恢复、fork 或回放)紧接构造种子之后追加这个仅日志事件,作为自己的第一次实时写入。在它之前的事件具有更小的 seq,且来自种子。它是 `firstLiveSeq` 的持久投影:该字段为持有对象的消费方回答本生命周期的写入从哪里开始,该事件则为只持有存储字节的消费方回答同一问题。payload 为空,因此位置与 `time` 承载全部含义,且不产生任何消息。`Session` 的构造函数是唯一合法的写入方。
647
+ fork constructor 要求 seed 等于 inherited prefix,并在精确持久 cut 追加 `session/end-seed { inherited: true }`。restore 会保留该 tagged marker,并且只在完整 stored seed 尚未以 marker 结尾时追加普通 `session/end-seed {}`。两种形式都只进入 log 且不产生 message;`Session` constructor 是唯一合法 writer。
566
648
 
567
- 显式传入的空种子会在 seq 0 写入 `session/end-seed`,从而把从空日志恢复的会话与全新会话区分开来。种子本身已以 `session/end-seed` 结尾时不会重复标记,因此重新打开一个未被改动的会话不会每次拾起都增长日志。应定位存储历史中的最后一条 `session/end-seed`,而不是假定 `firstLiveSeq` 处一定有一条:在一次没有产生工作的拾起之后,该事件的 seq 会小于下一个生命周期的 `firstLiveSeq`。
649
+ 对于 fork lineage,定位 payload 携带 `inherited: true` 的最后一个 marker;v2 decoding 只在 `SessionHeader.isSeeded` true 时要求该 marker,并从其 seq 推导 `inheritedEventCount`。对于 lifecycle ownership,定位任一形式的最后一个 `session/end-seed`。重新打开已经以任一 marker 结尾的 seed 时,不会再追加普通 marker。
568
650
 
569
651
  它之所以必要,是因为种子历史与实时工作在字节层面完全相同,这会让任何拥有独立开/闭括号的插件失效:一个未配对的 `compaction/start`,无论写入方是在压缩中途崩溃、还是此刻正在压缩,读起来都一样。在 `session/end-seed` 之前的开启标记来自构造种子,并且属于一个已结束的生命周期,无论结束原因为何(崩溃、进程接替,或从仍在运行的父会话 fork 出来),因此其所有方可以视之为已死。这只覆盖*本*会话继承的括号:另一个并发存活的会话可能在同一段历史上持有开放括号,而它自己的边界在别处,因此容忍并发写入方还需要日志之外的存活信号。核心写入该边界但不从中读取任何内容——括号的词汇表仍归其所属插件,这也正是崩溃修复只关闭轮次/步骤/工具边界而从不处理 `compaction/*` 的原因。
570
652
 
@@ -580,7 +662,7 @@ interface TurnEndReasonMap {
580
662
 
581
663
  ## 持久性约定
582
664
 
583
- 持久化后端依赖的约定如下:持久日志无损保存每个事件,**包括** `assistant/chunk`;`seq` 必须连续,因此不能从规范日志中过滤分片。后端可以为事件批次选择自己的存储编码,只要 `load` 返回与追加时完全一致的事件即可(JSONL 后端默认启用的打包分片行就是此类编码;见 [persistence.md](./persistence.md))。所有 `event.data` 都必须可序列化为 JSON;`Session.append` 会从源头强制这一要求(遇到不可序列化数据时抛出),因此错误事件绝不会进入日志,`session.events` 始终与后端可持久化的内容一致。新增会携带不可序列化数据、破坏核心执行嵌套或违反事件所有方声明关系的事件类型,都会构成磁盘格式的破坏性变更。
665
+ 持久化后端依赖的约定如下:持久日志无损保存每个事件,每个 Assistant attempt 都是一个 `assistant/message` 或 `assistant/attempt`,其嵌入式紧凑 stream 会保留原始带时间 chunk。`seq` 在这些 settlement 与所有交错事件之间保持连续。后端可以为事件批次选择自己的存储 framing,只要句柄的 `read()` 返回与追加时完全一致的事件即可;当前 JSONL v2 每个事件写一行(见 [persistence.md](./persistence.md))。所有 `event.data` 都必须可序列化为 JSON;`Session.append` 会从源头强制这一要求(遇到不可序列化数据时抛出),因此错误事件绝不会进入日志,`session.snapshotEvents()` 始终与后端可持久化的内容一致。新增会携带不可序列化数据、破坏核心执行嵌套或违反事件所有方声明关系的事件类型,都会构成磁盘格式的破坏性变更。
584
666
 
585
667
  消费此约定的后端见 [persistence.md](./persistence.md)。
586
668
 
@@ -618,7 +700,7 @@ resolveAgent(sessionId: SessionId): Promise<ApiSessionAgentResult>
618
700
  * @param signal - optional caller cancellation for persistence reads.
619
701
  * @returns the current attached state or persisted header and event prefix.
620
702
  */
621
- inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<{ meta: SessionHeader; events: SessionEvent[] }>
703
+ inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<SessionInspection>
622
704
 
623
705
  /**
624
706
  * Read all visible Session rows without resuming an Agent.
@@ -667,7 +749,7 @@ inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<{ meta: SessionH
667
749
  * @param request - path after best-effort Session workspace resolution.
668
750
  * @param signal - caller lifetime; abort terminates the native command.
669
751
  * @returns confirmation after the native opener accepts the path.
670
- * @throws TypertRemoteFailure when the request is invalid, cancelled, or the opener fails.
752
+ * @throws RemoteError when the request is invalid, cancelled, or the opener fails.
671
753
  */
672
754
  @Remote('openWorkspacePath') async openWorkspacePath( request: SessionOpenWorkspacePathRequest, signal: AbortSignal, ): Promise<SessionOpenWorkspacePathValue>
673
755
 
@@ -726,7 +808,8 @@ inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<{ meta: SessionH
726
808
  * Follow one Session log from its opening or resume cursor.
727
809
  * @param request - durable address and last committed sequence already held by the caller.
728
810
  * @param signal - cancellation owned by the Remote stream carrier.
729
- * @returns a complete opening snapshot followed by gap-free event frames.
811
+ * @returns a complete opening snapshot followed by gap-free durable event
812
+ * frames and optional cursorless assistant-stream frames.
730
813
  */
731
814
  @Remote({ mode: 'stream' }) follow(request: SessionFollowRequest, signal: AbortSignal): AsyncIterable<SessionFollowFrame>
732
815
 
@@ -738,7 +821,7 @@ inspect( sessionId: SessionId, signal?: AbortSignal, ): Promise<{ meta: SessionH
738
821
  @Remote({ mode: 'stream' }) control(signal: AbortSignal): AsyncIterable<SessionControlFrame>
739
822
  ```
740
823
 
741
- Types: [SessionHeader](./persistence.md) · [SessionId](./core.md) · [SessionSearchRequest](./session-query.md)
824
+ Types: [SessionId](./core.md) · [SessionInspection](./persistence.md) · [SessionSearchRequest](./session-query.md)
742
825
 
743
826
  Source: [`packages/api/session-controller/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/api/session-controller/src/index.ts)
744
827
 
@@ -748,7 +831,7 @@ Source: [`packages/api/session-controller/src/index.ts`](https://github.com/deep
748
831
 
749
832
  In-memory session store (`ctx.sessions`).
750
833
 
751
- Persistence is intentionally not implemented here — persistence plugins subscribe to `session/event` and flush on `session/flush` / dispose.
834
+ Persistence is intentionally not implemented here — the agent lifecycle attaches a session-log writer to each published session's write handle; a session published outside that lifecycle persists nothing.
752
835
 
753
836
  ```ts cordis-catalog
754
837
  /**
@@ -871,7 +954,7 @@ list(): Session[]
871
954
  * `SessionStore`'s id policy.
872
955
  * @returns The created live child session.
873
956
  */
874
- fork(source: SessionForkSource, boundary?: number, childSessionId?: SessionId): Session
957
+ fork(source: SessionForkSource, boundary?: SessionSeq, childSessionId?: SessionId): Session
875
958
  ```
876
959
 
877
960
  Types: [CreateSessionOptions](./persistence.md) · [PrepareSessionOptions](./persistence.md) · [SessionId](./core.md)
@@ -200,8 +200,22 @@ prepareDocument(): Promise<string | undefined>
200
200
  * @param schema - schemastery schema resolving this namespace's value.
201
201
  * @param options - composition `base` layer and effect timing.
202
202
  * @returns the owner scope for reads, observation, and updates.
203
+ * @throws {TypeError} when `ns` is not a lowercase hyphenated identifier.
203
204
  */
204
- register<T>(ns: SettingsNamespace, schema: z<T>, options?: SettingsRegisterOptions<T>): SettingsScope<T>
205
+ register<const Namespace extends string, T>( ns: Namespace & SettingsNamespaceInput<Namespace>, schema: z<T>, options?: SettingsRegisterOptions<T>, ): SettingsScope<T>
206
+
207
+ /**
208
+ * Attach one optional-settings consumer to this provider. The consumer
209
+ * registers its composition entry as the base layer while this provider is
210
+ * present, then falls back to that entry if the provider detaches.
211
+ * @param owner - consumer context whose unload suppresses fallback work.
212
+ * @param ns - consumer-owned settings namespace.
213
+ * @param schema - schema resolving the namespace.
214
+ * @param entry - composition entry used as the base and fallback value.
215
+ * @param hooks - source sink, change notification, and optional validation.
216
+ * @throws {TypeError} when `ns` is not a lowercase hyphenated identifier.
217
+ */
218
+ installSection<const Namespace extends string, T>( owner: Context, ns: Namespace & SettingsNamespaceInput<Namespace>, schema: z<T>, entry: T, hooks: SettingsSectionHooks<T>, ): void
205
219
 
206
220
  /**
207
221
  * Describe every registered namespace for configuration surfaces, including
@@ -216,8 +230,9 @@ describe(options?: SettingsDescribeOptions): SettingsDescriptor[]
216
230
  * Read one registered namespace's resolved value.
217
231
  * @param ns - the namespace to read.
218
232
  * @returns the resolved value, or `undefined` while unregistered.
233
+ * @throws {TypeError} when `ns` is not a lowercase hyphenated identifier.
219
234
  */
220
- get(ns: SettingsNamespace): unknown
235
+ get<const Namespace extends string>(ns: Namespace & SettingsNamespaceInput<Namespace>): unknown
221
236
 
222
237
  /**
223
238
  * Merge a patch into one registered namespace's user layer, validate the
@@ -229,8 +244,9 @@ get(ns: SettingsNamespace): unknown
229
244
  * @param patch - plain-object patch over the user section.
230
245
  * @param expectedRevision - the descriptor `revision` the caller read; a
231
246
  * namespace that moved past it rejects with {@link SettingsConflictError}.
247
+ * @throws {TypeError} when `ns` is not a lowercase hyphenated identifier.
232
248
  */
233
- async update(ns: SettingsNamespace, patch: object, expectedRevision?: number): Promise<void>
249
+ async update<const Namespace extends string>( ns: Namespace & SettingsNamespaceInput<Namespace>, patch: object, expectedRevision?: number, ): Promise<void>
234
250
 
235
251
  /**
236
252
  * Replace one registered namespace's user section wholesale, validate,
@@ -241,8 +257,9 @@ async update(ns: SettingsNamespace, patch: object, expectedRevision?: number): P
241
257
  * @param section - the complete next user section.
242
258
  * @param expectedRevision - the descriptor `revision` the caller read; a
243
259
  * namespace that moved past it rejects with {@link SettingsConflictError}.
260
+ * @throws {TypeError} when `ns` is not a lowercase hyphenated identifier.
244
261
  */
245
- async replace(ns: SettingsNamespace, section: object, expectedRevision?: number): Promise<void>
262
+ async replace<const Namespace extends string>( ns: Namespace & SettingsNamespaceInput<Namespace>, section: object, expectedRevision?: number, ): Promise<void>
246
263
 
247
264
  /**
248
265
  * Apply path-addressed edits to one registered namespace's user section,
@@ -255,8 +272,9 @@ async replace(ns: SettingsNamespace, section: object, expectedRevision?: number)
255
272
  * @param ops - ordered path edits; later ops observe earlier ones.
256
273
  * @param expectedRevision - the descriptor `revision` the caller read; a
257
274
  * namespace that moved past it rejects with {@link SettingsConflictError}.
275
+ * @throws {TypeError} when `ns` is not a lowercase hyphenated identifier.
258
276
  */
259
- async mutate(ns: SettingsNamespace, ops: readonly SettingsPathOp[], expectedRevision?: number): Promise<void>
277
+ async mutate<const Namespace extends string>( ns: Namespace & SettingsNamespaceInput<Namespace>, ops: readonly SettingsPathOp[], expectedRevision?: number, ): Promise<void>
260
278
  ```
261
279
 
262
280
  Source: [`packages/settings/settings/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/settings/settings/src/index.ts)
@@ -265,14 +283,14 @@ Source: [`packages/settings/settings/src/index.ts`](https://github.com/deepseek-
265
283
 
266
284
  ### `ctx.settingsController` — `SettingsController`
267
285
 
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.
286
+ 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
287
 
270
288
  ```ts cordis-catalog
271
289
  /**
272
290
  * Describe every registered namespace for a configuration page: redacted
273
291
  * layered values plus the serialized schema the page renders its form from.
274
292
  * @returns provider writability, local-document presence, and one view per namespace.
275
- * @throws TypertRemoteFailure when no settings provider is mounted.
293
+ * @throws RemoteError when no settings provider is mounted.
276
294
  */
277
295
  @Remote describe(): SettingsDescribeValue
278
296
 
@@ -288,7 +306,7 @@ Host service backing the generated `ctx.remote.settings` namespace. Every remote
288
306
  * @param patch - fields to merge into the user section.
289
307
  * @param expectedRevision - revision the caller read; `undefined` writes unconditionally.
290
308
  * @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.
309
+ * @throws RemoteError when the request is invalid, no provider is mounted, or the provider refuses the write.
292
310
  */
293
311
  @Remote update( ns: string, patch: Record<string, JsonValue>, expectedRevision: number | undefined, ): Promise<SettingsNamespaceView>
294
312
 
@@ -298,7 +316,7 @@ Host service backing the generated `ctx.remote.settings` namespace. Every remote
298
316
  * @param section - complete replacement user section.
299
317
  * @param expectedRevision - revision the caller read; `undefined` writes unconditionally.
300
318
  * @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.
319
+ * @throws RemoteError when the request is invalid, no provider is mounted, or the provider refuses the write.
302
320
  */
303
321
  @Remote replace( ns: string, section: Record<string, JsonValue>, expectedRevision: number | undefined, ): Promise<SettingsNamespaceView>
304
322
 
@@ -310,7 +328,7 @@ Host service backing the generated `ctx.remote.settings` namespace. Every remote
310
328
  * @param ops - the edits to apply, in order.
311
329
  * @param expectedRevision - revision the caller read; `undefined` writes unconditionally.
312
330
  * @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.
331
+ * @throws RemoteError when the request is invalid, no provider is mounted, or the provider refuses the write.
314
332
  */
315
333
  @Remote async mutate( ns: string, ops: SettingsPathOpView[], expectedRevision: number | undefined, ): Promise<SettingsNamespaceView>
316
334
 
@@ -318,7 +336,7 @@ Host service backing the generated `ctx.remote.settings` namespace. Every remote
318
336
  * Materialize the provider-owned settings document and open it in a native text editor.
319
337
  * @param signal - caller lifetime; abort terminates preparation or the native command.
320
338
  * @returns confirmation after the native opener accepts the document.
321
- * @throws TypertRemoteFailure when no document exists, preparation fails, or opening fails.
339
+ * @throws RemoteError when no document exists, preparation fails, or opening fails.
322
340
  */
323
341
  @Remote async openSettingsDocument(signal: AbortSignal): Promise<SettingsDocumentOpenValue>
324
342
 
@@ -327,7 +345,7 @@ Host service backing the generated `ctx.remote.settings` namespace. Every remote
327
345
  * @param agentPreset - preset id resolved against Host-owned roots.
328
346
  * @param signal - caller lifetime; abort terminates the native command.
329
347
  * @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.
348
+ * @throws RemoteError when the preset is missing, read-only, invalid, or cannot be opened.
331
349
  */
332
350
  @Remote async openAgentPresetDirectory( agentPreset: string, signal: AbortSignal, ): Promise<AgentPresetDirectoryOpenValue>
333
351
  ```