dsh-plugin-dev-kb 1.0.8 → 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 +11 -0
  2. package/README.en.md +6 -6
  3. package/README.md +6 -6
  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 +2 -2
  169. package/skills/dsh-plugin-dev-kb.md +8 -6
@@ -31,7 +31,19 @@ interface ContentBlockMap {
31
31
  }
32
32
  ```
33
33
 
34
- The block interfaces (full fields in source): `TextBlock` (`text`), `ReasoningBlock` (thinking, distinct from visible text), `ImageBlock` (a durable [image attachment](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/attachment.md)), `ToolCallBlock` (`id: CallId`, `name`, raw-JSON `arguments`), and `ToolResultBlock` (`toolCallId`, nested `content: ContentBlock[]`, `isError?`). `ContentBlock = ContentBlockMap[ContentBlockType]`. A new modality belongs in the merge-extensible map only when its adapter, UI, compaction, and durable replay paths honor it.
34
+ The block interfaces (full fields in source): `TextBlock` (`text`), `ReasoningBlock` (thinking, distinct from visible text), `ImageBlock` (a durable [image attachment](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/subsystems/attachment.md)), `ToolCallBlock` (`id: ToolCallId`, `name`, raw-JSON `arguments`), and `ToolResultBlock` (`toolCallId`, nested `content: ContentBlock[]`, `isError?`). `ContentBlock = ContentBlockMap[ContentBlockType]`. A new modality belongs in the merge-extensible map only when its adapter, UI, compaction, and durable replay paths honor it.
35
+
36
+ Image access belongs to request serialization rather than the durable attachment or deterministic request-image version. `resolveImageAttachmentAccess()` combines the attachment provider's optional host object path with a mapping supplied by the consumer for the current tool execution filesystem. The result is available only for that request and does not participate in `variantId`.
37
+
38
+ Source: [`packages/llm/llm/src/content.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm/src/content.ts)
39
+
40
+ ```ts type-equiv
41
+ /** Execution-world path that model tools can use to read one normalized attachment. */
42
+ interface ImageAttachmentAccess {
43
+ /** Absolute path to immutable normalized bytes; callers must treat it as read-only. */
44
+ readonlyPath: string
45
+ }
46
+ ```
35
47
 
36
48
  Source: [`packages/llm/llm/src/message.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm/src/message.ts)
37
49
 
@@ -196,7 +208,7 @@ type StreamChunk =
196
208
  | { type: 'block-start'; index: number; blockType: ContentBlockType }
197
209
  | { type: 'text-delta'; index: number; text: string }
198
210
  | { type: 'reasoning-delta'; index: number; text: string }
199
- | { type: 'tool-call-delta'; index: number; id: CallId; name?: string; argumentsDelta: string }
211
+ | { type: 'tool-call-delta'; index: number; id: ToolCallId; name?: string; argumentsDelta: string }
200
212
  | { type: 'block-end'; index: number; block: ContentBlock }
201
213
  | { type: 'usage'; usage: TokenUsage }
202
214
  | {
@@ -227,6 +239,44 @@ interface LlmFailure {
227
239
  }
228
240
  ```
229
241
 
242
+ ## Request-image pricing
243
+
244
+ An adapter whose provider charges visual tokens for request images declares per-route pricing by overriding `LlmAdapter.imageRequestPricing`, and `ctx.llm.imageRequestPricing(provider, model)` resolves it synchronously for consumers. The token meter resolves the routed model's pricing on every measurement so compaction pressure, retention, and range selection price image history as the routed request actually sends it; the DeepSeek adapter reproduces its own request projection (per-model pixel budget, oldest-first offload) and prices retained images with the published v4 vision accounting, while provider usage remains the authoritative anchor for completed requests.
245
+
246
+ ```ts type-equiv
247
+ /**
248
+ * Request price of one ordered image occurrence under one exact model route's
249
+ * request projection. Every occurrence resolves to the pair the wire actually
250
+ * carries: provider visual tokens for a retained image, plus the model-visible
251
+ * text sent with or instead of it (request-preview handle, offload placeholder,
252
+ * or text-only substitution). The caller prices `text` with its own text
253
+ * estimator so provider pricing never fixes a text tokenization.
254
+ */
255
+ interface LlmImageRequestPrice {
256
+ /** Provider visual tokens for the retained request image; 0 when only text represents this occurrence. */
257
+ visualTokens: number
258
+ /** Model-visible text sent for this occurrence, to be priced by the caller's text estimator. */
259
+ text: string
260
+ }
261
+ ```
262
+
263
+ ```ts type-equiv
264
+ /**
265
+ * Provider-side request-image pricing for one exact model route. Implemented
266
+ * by adapters whose provider charges visual tokens; consumers (the token
267
+ * meter) resolve it synchronously per measurement, so implementations must not
268
+ * perform I/O.
269
+ */
270
+ interface LlmImageRequestPricing {
271
+ /**
272
+ * Price every image occurrence of one request projection.
273
+ * @param images - durable image references in request order, one entry per occurrence.
274
+ * @returns one price per occurrence, aligned by index with `images`.
275
+ */
276
+ priceImages(images: readonly ImageAttachmentRef[]): readonly LlmImageRequestPrice[]
277
+ }
278
+ ```
279
+
230
280
  ## The adapter contract
231
281
 
232
282
  Every adapter MUST obey these, and every consumer may rely on them:
@@ -269,7 +319,7 @@ interface AppIdentity {
269
319
 
270
320
  ## `TokenUsage`
271
321
 
272
- Per-call token accounting. Counts are **disjoint**: `inputTokens` is uncached input only; cached input is reported separately, and billed input is the sum of the three. Adapters whose providers fold cache hits into a single prompt total (DeepSeek's `prompt_tokens`) subtract them back out. `reasoningTokens`, when present, is informational detail already included in `outputTokens`; totals must not add it again.
322
+ Per-call token accounting. Counts are **disjoint**: `inputTokens` is uncached input only; cached input is reported separately, and billed input is the sum of the three. Adapters whose providers fold cache hits into a single prompt total (DeepSeek's `prompt_tokens`) subtract them back out. Optional `totalTokens` is an exact aggregate prompt-plus-output count preserved from the provider or reconstructed from authoritative aggregate counters; adapters omit it when unavailable or inconsistent. `reasoningTokens`, when present, is informational detail already included in `outputTokens`; totals must not add it again.
273
323
 
274
324
  ```ts type-equiv
275
325
  /**
@@ -283,6 +333,14 @@ Per-call token accounting. Counts are **disjoint**: `inputTokens` is uncached in
283
333
  interface TokenUsage {
284
334
  inputTokens: number
285
335
  outputTokens: number
336
+ /**
337
+ * Exact full-call total including aggregate prompt and output tokens.
338
+ *
339
+ * Adapters preserve a provider total or derive it from authoritative
340
+ * aggregate prompt/output counters; they omit it when unavailable or
341
+ * inconsistent.
342
+ */
343
+ totalTokens?: number
286
344
  cacheReadTokens?: number
287
345
  cacheWriteTokens?: number
288
346
  reasoningTokens?: number
@@ -356,7 +414,7 @@ One model call is a fully-assembled `GenerateOptions`. The adapter answers with
356
414
 
357
415
  Source: [`packages/llm/llm/src/types.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/llm/src/types.ts)
358
416
 
359
- Provider and model discovery uses small provider-neutral descriptors. A model catalog is advisory: routing still keys on a registered provider, and an adapter may accept unlisted model ids.
417
+ Provider and model discovery uses small provider-neutral descriptors. A model catalog is advisory: routing still keys on a registered provider.
360
418
 
361
419
  Registering an adapter returns a handle: the disposer, plus the atomic route replacement a plugin whose route set is user-configurable needs.
362
420
 
@@ -604,8 +662,6 @@ interface LlmModelDiscoveryRequest {
604
662
  api?: string
605
663
  /** Credential for this interrogation alone; the harness never stores it. */
606
664
  apiKey?: string
607
- /** Caller cancellation; implementations must settle promptly after it aborts. */
608
- signal?: AbortSignal
609
665
  }
610
666
  ```
611
667
 
@@ -665,6 +721,12 @@ interface LlmCallConfigAdapterDefaults {
665
721
  }
666
722
  ```
667
723
 
724
+ ## Official DeepSeek request extensions
725
+
726
+ `ctx.deepseekLlmApiExtensions` is the provider-specific registry for additive top-level fields on `deepseek-official` requests. Contributor plugins use `register(field, provider)` to claim one field; the adapter calls `prepare(request)` after serializing its base body and merges the returned fields before HTTP. The prepared `accept()` transaction runs after 2xx, so a contributor can commit delivery state without treating a transport or provider rejection as acceptance. Preparation, collision, and acceptance failures use `REQUEST_EXTENSION` and fail the model request.
727
+
728
+ The [wire reference](https://github.com/deepseek-ai/deepseek-harness/blob/master/docs/deepseek-llm-api-wire-extensions.md) defines the exact request headers, extension transaction, field versions, and receiver obligations. The shipped composition registers [`dsh_session_log`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/session/session-log-deepseek/README.md) as a lossless incremental canonical-log suffix and [`dsh_plugin_packages`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/plugin-package-inventory-deepseek/README.md) as the complete active Loader-backed package set. These fields remain outside model messages and are absent from the pi-ai adapter path.
729
+
668
730
  ## Service and provider contracts
669
731
 
670
732
  `LlmAdapter` is the provider contract: subclass, implement `stream()`, and register one adapter instance with `ctx.llm.registerAdapter(providers, adapter)`. `GenerateOptions.provider` selects the registered adapter; `GenerateOptions.model` is passed to that adapter and need not be registered at lifecycle start. Duplicate provider routes fail atomically. Optional `providerRetryPolicy()` is captured per route with normal defaults, while `providerInfo()` and asynchronous `listModels()` feed `LlmRuntime.listProviders()` / `listModels()` with detached selector metadata. That catalog is advisory rather than a request whitelist: the adapter remains authoritative and may accept unlisted model ids. One asynchronous `resolveModel()` query returns exact model identity plus optional correctness-sensitive context capacity, an adapter-configured `defaultMaxTokens`, and ordered model-owned reasoning ids with an optional deployment default; absent fields mean unavailable metadata or provider-owned behavior, not invalid catalog membership. The resolver receives optional cancellation and must settle promptly after abort. `LlmRuntime.resolveModelInfo()` validates and detaches the aggregate. At the final adapter boundary, `resolveCallConfig()` materializes the output default only when `maxTokens` is absent and validates and materializes reasoning, so direct calls cannot bypass either configured behavior; direct dispatch captures one registration before awaiting that resolution. The agent loop instead uses `prepareCall()` to keep the same registration across model resolution, durable header logging, and dispatch, retain detached context metadata from that exact lookup, and report which config fields the adapter defaulted. Adapter lookup happens at the terminal continuation of the `llm/stream` waterfall, so a listener may short-circuit the call or route a mutable one-shot request before lookup. AgentLoop observes a request attempt once the outer waterfall returns a stream handle; that limited boundary does not prove a lazy terminal adapter was constructed or began provider I/O. The `block-start` / `block-end` `index` correlation and the assembler together mean an adapter only has to emit well-formed chunks — block reassembly is not each adapter's problem. [architecture.md](../index.md#turn-flow) shows where `ctx.llm.stream()` and the `llm/stream` waterfall sit in one turn.
@@ -713,6 +775,16 @@ declare abstract class LlmAdapter {
713
775
  * @returns a resolved policy, or `undefined` to use the normal defaults.
714
776
  */
715
777
  providerRetryPolicy(_provider: string): ResolvedRetryPolicy | undefined;
778
+ /**
779
+ * Resolve provider-side request-image pricing for one exact model route.
780
+ * The default declares none, so consumers fall back to their own neutral
781
+ * estimate. Implementations must answer synchronously without I/O; the
782
+ * token meter resolves this per measurement.
783
+ * @param _provider - a route passed to `registerAdapter()` for this instance.
784
+ * @param _model - exact model id passed to {@link GenerateOptions.model}.
785
+ * @returns route-owned image pricing, or `undefined` when the route declares none.
786
+ */
787
+ imageRequestPricing(_provider: string, _model: string): LlmImageRequestPricing | undefined;
716
788
  /**
717
789
  * List models this adapter can currently advertise for one owned provider.
718
790
  * The result is advisory: an adapter may accept unlisted model ids, and
@@ -764,6 +836,33 @@ declare abstract class LlmAdapter {
764
836
 
765
837
  Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnpm run verify-cordis-catalog` in doc-sync; regenerate with `pnpm run gen-cordis-catalog`) — the language sides differ only in locale-specific paired document paths. Signature blocks use a `ts cordis-catalog` fence and keep the original source JSDoc; dispatch modes are defined in the [primer](../cordis-primer.md#dispatch-modes), and the framework-inherited `ctx` API lives in [cordis-api/inherited.md](../cordis-api/inherited.md).
766
838
 
839
+ <a id="ctxdeepseekllmapiextensions--deepseekllmapiextensionregistry"></a>
840
+
841
+ ### `ctx.deepseekLlmApiExtensions` — `DeepSeekLlmApiExtensionRegistry`
842
+
843
+ Registry of independently owned top-level fields for official DeepSeek requests.
844
+
845
+ ```ts cordis-catalog
846
+ /**
847
+ * Register the sole provider of one top-level request field. Registration is effect-scoped.
848
+ * @param field - declaration-merged field owned by the provider.
849
+ * @param provider - request-time field preparation and optional acceptance behavior.
850
+ * @returns disposer that releases the field.
851
+ */
852
+ register<K extends keyof DeepSeekLlmApiExtensionMap>( field: K, provider: DeepSeekLlmApiExtensionProvider<DeepSeekLlmApiExtensionMap[K]>, ): () => Promise<void>
853
+
854
+ /**
855
+ * Prepare every currently registered field from one immutable base request.
856
+ * Preparation failures reject before HTTP dispatch. Field values are cloned and frozen;
857
+ * providers retain no mutable alias to the outgoing request.
858
+ * @param request - exact serialized request facts before extension fields.
859
+ * @returns detached fields and their idempotent joint acceptance transaction.
860
+ */
861
+ async prepare(request: DeepSeekLlmApiExtensionRequest): Promise<PreparedDeepSeekLlmApiExtensions>
862
+ ```
863
+
864
+ Source: [`packages/llm/deepseek-llm-api-extensions/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/llm/deepseek-llm-api-extensions/src/index.ts)
865
+
767
866
  <a id="ctxllm--llmruntime"></a>
768
867
 
769
868
  ### `ctx.llm` — `LlmRuntime`
@@ -785,7 +884,7 @@ registerAdapter(providers: string[], adapter: LlmAdapter): AdapterRegistrationHa
785
884
  * Describe provider routes with a registered adapter.
786
885
  * @returns detached provider metadata in registration order.
787
886
  */
788
- listProviders(): LlmProviderInfo[]
887
+ @Remote listProviders(): LlmProviderInfo[]
789
888
 
790
889
  /**
791
890
  * Declare provider routes an adapter plugin can activate through
@@ -801,7 +900,7 @@ registerConfigurableProviders(entries: readonly LlmConfigurableProvider[]): Dire
801
900
  * List every declared configurable provider, registered or dormant.
802
901
  * @returns detached directory entries in declaration order.
803
902
  */
804
- listConfigurableProviders(): LlmConfigurableProvider[]
903
+ @Remote listConfigurableProviders(): LlmConfigurableProvider[]
805
904
 
806
905
  /**
807
906
  * Offer to interrogate provider endpoints on behalf of the settings
@@ -810,10 +909,10 @@ listConfigurableProviders(): LlmConfigurableProvider[]
810
909
  * directory, and because a provider being *added* has no route to name yet.
811
910
  * Disposed with the fiber.
812
911
  * @param settingsNs - the namespace whose profiles this discovery serves.
813
- * @param discover - interrogates one endpoint; must honor `request.signal`.
912
+ * @param discover - interrogates one endpoint and must honor the supplied signal.
814
913
  * @returns the disposer that withdraws the offer.
815
914
  */
816
- registerModelDiscovery( settingsNs: string, discover: (request: LlmModelDiscoveryRequest) => Promise<readonly LlmDiscoveredModel[]>, ): () => void
915
+ registerModelDiscovery( settingsNs: string, discover: ( request: LlmModelDiscoveryRequest, signal?: AbortSignal, ) => Promise<readonly LlmDiscoveredModel[]>, ): () => void
817
916
 
818
917
  /**
819
918
  * Interrogate one provider endpoint for the models it advertises. The
@@ -822,9 +921,20 @@ registerModelDiscovery( settingsNs: string, discover: (request: LlmModelDiscover
822
921
  * candidate metadata a surface may offer for adoption.
823
922
  * @param settingsNs - namespace whose registered discovery serves this draft.
824
923
  * @param request - the endpoint, protocol, and one-shot credential to use.
924
+ * @param signal - caller cancellation.
825
925
  * @returns the advertised models, deduplicated in endpoint order.
826
926
  */
827
- async discoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, ): Promise<LlmDiscoveredModel[]>
927
+ async discoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, signal?: AbortSignal, ): Promise<LlmDiscoveredModel[]>
928
+
929
+ /**
930
+ * Remote adapter for one draft provider interrogation.
931
+ * @param settingsNs - namespace whose registered discovery serves this draft.
932
+ * @param request - endpoint, protocol, and one-shot credential to use.
933
+ * @param signal - caller cancellation supplied by the Remote carrier.
934
+ * @returns advertised models in endpoint order.
935
+ * @throws TypertRemoteFailure with `model-discovery-failed` when discovery refuses or fails.
936
+ */
937
+ @Remote('discoverModels') async remoteDiscoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, signal: AbortSignal, ): Promise<LlmDiscoveredModel[]>
828
938
 
829
939
  /**
830
940
  * Resolve the retry policy captured when one provider route was registered.
@@ -833,6 +943,17 @@ async discoverModels( settingsNs: string, request: LlmModelDiscoveryRequest, ):
833
943
  */
834
944
  providerRetryPolicy(provider: string): ResolvedRetryPolicy
835
945
 
946
+ /**
947
+ * Resolve provider-side request-image pricing for one exact route, or
948
+ * `undefined` when the provider is unregistered or declares none. Unknown
949
+ * providers degrade to `undefined` rather than throwing because callers
950
+ * price durable history whose route may no longer be mounted.
951
+ * @param provider - provider route named by a request header.
952
+ * @param model - exact model id named by the same header.
953
+ * @returns the owning adapter's image pricing for the route, when declared.
954
+ */
955
+ imageRequestPricing(provider: string, model: string): LlmImageRequestPricing | undefined
956
+
836
957
  /**
837
958
  * Discover models advertised by one registered provider. Catalog membership
838
959
  * is advisory and never changes routing or request validation.
@@ -66,7 +66,7 @@ interface PresetOption {
66
66
 
67
67
  ## Switching and the `permission/preset` event
68
68
 
69
- `set(session, name)` resolves the preset (unknown names throw), appends a log-only `permission/preset` event unless `name` is already the effective preset, then writes each knob through its own setter — `setSandboxMode` from [dsh-sandbox-policy](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/sandbox/sandbox-policy) and `setApprovalPolicy` from [dsh-user-approval](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/interaction/user-approval) — only when that knob's effective value changes. The selection event precedes the knob events in the same turn, and re-selecting the effective preset appends nothing at all.
69
+ `set(session, name)` resolves the preset (unknown names throw), appends a log-only `permission/preset` event unless `name` is already the effective preset, then writes each knob through its own setter — `setSandboxMode` from [dsh-sandbox-policy](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/sandbox/sandbox-policy) and `setApprovalPolicy` from [dsh-user-approval](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/interaction/user-approval) — only when that knob's effective value changes. The selection event precedes the knob events in the same turn, and re-selecting the effective preset appends nothing.
70
70
 
71
71
  `permission/preset` is durable, log-only user intent: it stays out of the model transcript (the knob events own the model-visible consequences through their consumers), and it exists so `current()` can preserve WHICH preset the user chose when two presets share a bundle; `effectivePermissionPreset(events)` folds the last one, and replay needs no catch-up state. The complete event declaration is in the [persistence log event catalog](../persistence-catalog.md); the method signatures are in the generated [service catalog](#ctxpermissionpresets--permissionpresetservice).
72
72
 
@@ -94,7 +94,7 @@ interface SessionHeader {
94
94
 
95
95
  ## Format refusal — logs a build cannot faithfully read
96
96
 
97
- A backend refuses a log it cannot faithfully interpret with `SessionFormatUnsupportedError`, distinct from `SessionPersistenceCorruptionError` because nothing is damaged. A header `version` ahead of `SESSION_FORMAT_VERSION` names the direction ("written by a newer harness — upgrade the harness to open it"); one behind it states that this build ships no upgrade path. After legacy-shape normalization, an event type outside this build's generated vocabulary (`KNOWN_SESSION_EVENT_TYPES`, emitted by `gen-persistence-catalog`) refuses the same way unless the event's envelope carries `ignorable: true` — silently skipping an unrecognized required event could change how the rest of the log must be read. The message appends the raw log path when the backend keeps one artifact per session, so the refused text stays reachable. The JSONL backend refuses a foreign version straight from the raw header line, before validating today's header shape or decoding any event row — a structurally different future format still reports the upgrade direction, never "corrupt"; SQLite gates whole-file structure through its own `SCHEMA_VERSION` pragma first. Design rationale and the deferred upgrader chain live in the [session-log-version-mechanism note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/architecture/2026-08-10-session-log-version-mechanism.md).
97
+ A backend refuses a log it cannot faithfully interpret with `SessionFormatUnsupportedError`, distinct from `SessionPersistenceCorruptionError` because nothing is damaged. A header `version` ahead of `SESSION_FORMAT_VERSION` names the direction ("written by a newer harness — upgrade the harness to open it"); one behind it states that this build ships no upgrade path. After legacy-shape normalization, an event type outside this build's generated set (`KNOWN_SESSION_EVENT_TYPES`, emitted by `gen-persistence-catalog`) also refuses reconstruction because silently skipping it could change how the rest of the log must be read. The message appends the raw log path when the backend keeps one artifact per session, so the refused text stays reachable. The JSONL backend refuses a foreign version straight from the raw header line, before validating this format version's header fields or decoding any event row — a structurally different future format still reports the upgrade direction, never "corrupt"; SQLite gates whole-file structure through its own `SCHEMA_VERSION` pragma first. Design rationale and the deferred upgrader chain live in the [fail-closed event-vocabulary note](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/implemented/simplification/2026-08-25-fail-closed-session-event-vocabulary.md).
98
98
 
99
99
  ## `CreateSessionOptions` — seeding and metadata
100
100
 
@@ -236,7 +236,7 @@ interface SessionPersistenceSnapshot {
236
236
  All implement the same abstract `SessionPersistence` (locate/create/append/prepare/load/inspect/readFrom/list/listSnapshots over `SessionEvent`, with optional cancellation on observation methods) and pass the shared `runPersistenceContract` suite:
237
237
 
238
238
  - **[dsh-session-persistence-jsonl](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/session/session-persistence-jsonl)** — an append-only logical JSONL log per session, stored as checksummed concatenated Zstandard frames by default or raw lines by configuration, with crash-safe atomic writes, interrupted-turn recovery, and a read/replay path.
239
- - **[dsh-session-persistence-sqlite](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/session/session-persistence-sqlite)** — an opt-in `node:sqlite` backend using schema 17 to store exact same-block delta runs in bounded physical `text-chunks`, `reasoning-chunks`, and `tool-call-chunks` rows. It reconstructs the complete logical event stream before returning it, packs only newly durable batches, and rejects older schemas rather than migrating them.
239
+ - **[dsh-session-persistence-sqlite](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/session/session-persistence-sqlite)** — an opt-in `node:sqlite` backend using schema 19 to store exact same-block delta runs in bounded physical `text-chunks`, `reasoning-chunks`, and `tool-call-chunks` rows. It reconstructs the complete logical event stream before returning it, packs only newly durable batches, and rejects older schemas rather than migrating them.
240
240
 
241
241
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
242
242
 
@@ -288,6 +288,14 @@ readRaw(_id: SessionId, signal?: AbortSignal): Promise<SessionRawArtifact | unde
288
288
  */
289
289
  abstract create(meta: SessionHeader): Promise<void>
290
290
 
291
+ /**
292
+ * Ensure a live session has a durable header even when it has no events.
293
+ * Ordinary sessions remain lazily materialized; lifecycle frontends call
294
+ * this only when an empty session itself is a durable resumable resource.
295
+ * @param _session - exact live session whose registered header is materialized.
296
+ */
297
+ ensureMaterialized(_session: Session): Promise<void>
298
+
291
299
  /**
292
300
  * Durably persist a batch of events. Honors the append-only and contiguous-
293
301
  * seq contracts: the first event's `seq` MUST equal the stored next-seq
@@ -342,6 +350,17 @@ abstract load(id: SessionId): Promise<SessionInspection>
342
350
  */
343
351
  abstract inspect(id: SessionId, signal?: AbortSignal): Promise<SessionInspection>
344
352
 
353
+ /**
354
+ * Borrow one exact inspection while retaining any reusable prepared source.
355
+ * A cold observation must pin the exact prepared Session that a later
356
+ * {@link prepare} reserves. Implementations must not degrade this operation
357
+ * to a detached {@link inspect} result.
358
+ * @param id - persisted session to observe.
359
+ * @param signal - optional cancellation for preparation work.
360
+ * @returns a disposable immutable observation.
361
+ */
362
+ abstract borrowSession(id: SessionId, signal?: AbortSignal): Promise<BorrowedSessionSource>
363
+
345
364
  /**
346
365
  * Read the stored events from `fromSeq` onward — the read-from-seq
347
366
  * primitive for read models that resume from a watermark (e.g. a persisted
@@ -382,7 +401,7 @@ abstract list(signal?: AbortSignal): Promise<SessionHeader[]>
382
401
  abstract listSnapshots(signal?: AbortSignal): Promise<SessionPersistenceSnapshot[]>
383
402
  ```
384
403
 
385
- Types: [SessionEvent](./session.md) · [SessionId](./core.md)
404
+ Types: [Session](./session.md) · [SessionEvent](./session.md) · [SessionId](./core.md)
386
405
 
387
406
  Source: [`packages/session/session-persistence/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/session/session-persistence/src/index.ts)
388
407
  <!-- END GENERATED cordis-surface -->
@@ -29,7 +29,7 @@ interface PlanModeConfig {
29
29
  }
30
30
  ```
31
31
 
32
- A missing, blank, or non-string `section` and any unknown key fail at plugin load rather than being ignored. While plan mode is active, the exact `section` text renders as the `plan:policy` [system-prompt section](./system-prompt.md) at order 50; inactive plan mode contributes no text.
32
+ A missing, blank, or non-string `section` and any unknown key fail at plugin load rather than being ignored. While plan mode is active, the exact `section` text renders as the `plan:policy` [system-prompt section](./system-prompt.md) at first-party order 500; inactive plan mode contributes no text.
33
33
 
34
34
  ## The exit tool and the `/plan` command
35
35
 
@@ -5,7 +5,7 @@ outline: [2,3]
5
5
 
6
6
  # Session Projections
7
7
 
8
- The session-projection seam — a [capability seam](../capability-seams.md) through which domain host plugins serve whole current values of log-derived per-session state to client carriers: the Service Definition and registry ([dsh-session-projection](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/session/session-projection), `ctx.sessionProjections`), domain contributors (each registering one pure unit), and carriers ([dsh-host-apiproxy](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/host/apiproxy)'s history tail page and `session/projection` push frame). It is one optional capability, not part of the agent-loop spine. The framework drives, the domain computes: the registry subscribes to `session/event` once and folds every committed event through every unit; domains hold no subscriptions and clients never fold domain events — they receive finished values. Design authority: the [session-projection RFC](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md); drive/cache/feed contracts: the [package README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/session/session-projection/README.md).
8
+ The session-projection seam — a [capability seam](../capability-seams.md) through which domain host plugins serve whole current values of log-derived per-session state to client carriers: the Service Definition and registry ([dsh-session-projection](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/session/session-projection), `ctx.sessionProjections`), domain contributors (each registering one pure unit), and carriers ([dsh-session-controller](https://github.com/deepseek-ai/deepseek-harness/tree/master/packages/api/session-controller)'s history tail page and `session/projection` push frame). It is one optional capability, not part of the agent-loop spine. The framework drives, the domain computes: the registry subscribes to `session/event` once and folds every committed event through every unit; domains hold no subscriptions and clients never fold domain events — they receive finished values. Design authority: the [session-projection RFC](https://github.com/deepseek-ai/deepseek-harness/blob/master/.agents/notes/proposed/architecture/2026-07-27-session-projection-and-command-log.md); drive/cache/feed contracts: the [package README](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/session/session-projection/README.md).
9
9
 
10
10
  Source: [`packages/session/session-projection/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/session/session-projection/src/index.ts)
11
11
 
@@ -31,10 +31,11 @@ interface ProjectionDefinition<
31
31
  /** Validates persisted state before it seeds a fold. */
32
32
  stateSchema: ZodType<S>
33
33
  /**
34
- * State for the empty log.
34
+ * State for the empty log and its immutable Session metadata.
35
+ * @param header - immutable metadata for the Session being projected.
35
36
  * @returns the initial state.
36
37
  */
37
- init(): NoInfer<S>
38
+ init(header: SessionHeader): NoInfer<S>
38
39
  /**
39
40
  * Pure transition: previous state + one committed event → next state. A
40
41
  * unit uninterested in an event MUST return the same state reference — an
@@ -73,7 +74,7 @@ The whole-value event rule is load-bearing: a state-carrying log event carries t
73
74
  /**
74
75
  * One consistent read cut over every registered client-visible unit for one session.
75
76
  * `asOfSeq` is the shared watermark — the seq of the last event every value
76
- * reflects (`-1` for an empty log, mirroring `session/subscribed.lastSeq`).
77
+ * reflects (`-1` for an empty log).
77
78
  */
78
79
  interface ProjectionSnapshot {
79
80
  /** Seq of the last event the values reflect; -1 for an empty log. */
@@ -101,7 +102,7 @@ type ProjectionChangeListener = (
101
102
 
102
103
  ## The registry: `ctx.sessionProjections`
103
104
 
104
- `SessionProjectionRegistry` ([signatures](#ctxsessionprojections--sessionprojectionregistry)) owns the drive: one `session/event` subscription, eager `apply` over every registered unit, and per-session per-unit watermark cells. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Registration is an effect whose disposer rides the calling fiber: an unloaded domain plugin's key (with its cached cells) disappears from subsequent drives and snapshots, and clients read that as capability absence; duplicate keys throw. Domain plugins register under `ctx.inject(['sessionProjections'], …)` so headless assemblies without the registry stay unaffected.
105
+ `SessionProjectionRegistry` ([signatures](#ctxsessionprojections--sessionprojectionregistry)) owns the drive: one `session/event` subscription, eager `apply` over every registered unit, and per-session per-unit watermark cells. Cells build lazily — a unit registered after events flowed, or a session older than the registry, folds `init` over the in-memory log on first touch (event or read). Registration is an effect whose disposer rides the calling fiber: an unloaded domain plugin's key (with its cached cells) disappears from subsequent drives and snapshots, and clients read that as capability absence; a duplicate key with a different `stateVersion` throws, while same-version registrants share one unit and are counted. Domain plugins register under `ctx.inject(['sessionProjections'], …)` so headless assemblies without the registry stay unaffected.
105
106
 
106
107
  <!-- BEGIN GENERATED cordis-surface (gen-cordis-catalog.ts) — do not edit between markers -->
107
108
 
@@ -115,49 +116,63 @@ Generated from source by `scripts/gen-cordis-catalog.ts` (verified fresh by `pnp
115
116
 
116
117
  ### `ctx.sessionProjectionCache` — `SessionProjectionCache`
117
118
 
118
- The persisted projection cache service. Opens the `session_projcache` domain at init, checkpoints live sessions on a throttled write-behind (count/interval triggers from Config) plus two mandatory points — `turn/end` and session disposal (the live-to-cold moment) — and serves the cold-read ladder: cached row, persistence `readFrom` tail, registry `restore`, durable write-back. Every durable write is fail-soft: failures log a warning and the cache self-heals on the next write or cold read.
119
+ The persisted projection cache service. Opens the `session_projcache` domain at init, checkpoints live sessions on a throttled write-behind (count/interval triggers from Config) plus three mandatory points — session creation, `turn/end`, and session disposal (the live-to-cold moment) — and serves the cached rows for a session header. Every durable write is fail-soft: failures log a warning and the cache self-heals on the next write.
119
120
 
120
121
  ```ts cordis-catalog
121
122
  /**
122
123
  * The zero-I/O listing read: whole values viewed straight from the stored
123
- * rows (version-matching keys only), each cut carried with its watermark
124
- * so a client value store can seed under its higher-seq-wins rule — as
125
- * stale as the last durable checkpoint but never wrong, and never from an
124
+ * rows (version-matching keys only), each cut carried with its watermark so
125
+ * a client value store can seed under its higher-seq-wins rule — as stale
126
+ * as the last durable checkpoint but never wrong, and never from an
126
127
  * unrelated log (the caller's header is the identity witness). Fresher
127
- * paths (the history tail baseline, {@link coldSnapshot}) supersede these
128
- * values whenever a session is actually opened.
128
+ * paths (the history tail baseline) supersede these values whenever a
129
+ * session is actually opened.
129
130
  * @param meta - the listed session's header (identity witness; no log read).
131
+ * @param keys - optional projection keys required by the caller's audience.
130
132
  * @returns the cut (`asOfSeq` = lowest served-row watermark), or
131
133
  * `undefined` when no usable row exists for this lifecycle.
132
134
  */
133
- cachedSnapshot(meta: SessionHeader): ProjectionSnapshot | undefined
135
+ cachedSnapshot( meta: SessionHeader, keys?: readonly Extract<keyof SessionProjectionMap, string>[], ): ProjectionSnapshot | undefined
134
136
 
135
137
  /**
136
- * Durably checkpoint one live session NOW (both mandatory points call
138
+ * Hydrate projection cells for an already-prepared Session without another
139
+ * persistence read. The cache seeds matching rows; the supplied exact log
140
+ * advances every unit to the observation cut. No checkpoint is written
141
+ * because the logical observation may contain recovery events not yet durable.
142
+ * @param session - exact unpublished Session retained by persistence.
143
+ * @param meta - observed lifecycle header.
144
+ * @param events - exact logical event prefix represented by the observation.
145
+ * @returns all projection values at the event cut.
146
+ */
147
+ hydratePrepared( session: Session, meta: SessionHeader, events: readonly SessionEvent[], ): ProjectionSnapshot
148
+
149
+ /**
150
+ * Durably checkpoint one live session NOW (all mandatory points call
137
151
  * this; tests and carriers may too). The registry cut is snapshotted at
138
- * this boundary (states are live references), then the whole record is
139
- * replaced. NOT fail-soft — callers on the fail-soft paths contain it.
152
+ * this boundary (states are live references), then the session's record is
153
+ * replaced on the domain's write chain. NOT fail-soft — callers on the
154
+ * fail-soft paths contain it.
140
155
  * @param session - the live session to checkpoint.
141
156
  * @returns resolution after durability and event emission.
142
157
  */
143
158
  async write(session: Session): Promise<void>
144
159
 
145
160
  /**
146
- * Cold-read one persisted session's projections with zero full-log load:
147
- * cached rows + a persistence `readFrom` tail from the registry's restore
148
- * floor, refolded by the registry and written back (fail-soft) so the next
149
- * cold read starts closer. A cache row invalidated by a shrunk log
150
- * (crash-repair truncation) triggers one full re-read from seq 0 the
151
- * ladder's slow rung, still no crash. Rejects when the session has no
152
- * persisted log (`not found` from the persistence seam).
153
- * @param id - the persisted session to read.
154
- * @param signal - optional cancellation for the persistence reads.
155
- * @returns the snapshot cut at the stored log end.
161
+ * Cold-read one session's projections from its complete log. Each unit is
162
+ * seeded from the identity-checked cached rows the registry skips `apply`
163
+ * for the already-folded prefix (events at or below the row's `seq`) and
164
+ * the refreshed checkpoint is written back (fail-soft, fire-and-forget), so
165
+ * the first cold read creates the cache row and later ones seed from it.
166
+ * The caller supplies the complete log in seq order: this service never
167
+ * consults the persistence layer.
168
+ * @param meta - the stored session header (identity witness).
169
+ * @param events - the session's complete log, in seq order.
170
+ * @returns the projection cut at the log end.
156
171
  */
157
- async coldSnapshot(id: SessionId, signal?: AbortSignal): Promise<ProjectionSnapshot>
172
+ coldSnapshot(meta: SessionHeader, events: readonly SessionEvent[]): ProjectionSnapshot
158
173
  ```
159
174
 
160
- Types: [Session](./session.md) · [SessionHeader](./persistence.md) · [SessionId](./core.md)
175
+ Types: [Session](./session.md) · [SessionEvent](./session.md) · [SessionHeader](./persistence.md)
161
176
 
162
177
  Source: [`packages/session/session-projection-cache/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/session/session-projection-cache/src/index.ts)
163
178
 
@@ -195,7 +210,8 @@ register< K extends Exclude<keyof SessionProjectionStateMap, keyof SessionProjec
195
210
  onChanged(listener: ProjectionChangeListener): () => void
196
211
 
197
212
  /**
198
- * Read one unit's current host state without computing unrelated views.
213
+ * Read one unit's current host state after materializing every registered
214
+ * unit at the Session cursor. Unrelated wire views are not produced.
199
215
  * The returned value is live; callers must not mutate it.
200
216
  * @param session - the session whose state is read.
201
217
  * @param key - the registered unit key.
@@ -209,9 +225,20 @@ stateOf<K extends keyof SessionProjectionStateMap>( session: Session, key: K, ):
209
225
  * Fully synchronous — every value and `asOfSeq` reflect the same log
210
226
  * position. Each value passes its unit's `viewSchema` before leaving.
211
227
  * @param session - the session whose projection values are read.
212
- * @returns the snapshot; `values` is empty when no client-visible unit is registered.
228
+ * @param keys - optional client-visible outputs; state materialization remains complete.
229
+ * @returns the snapshot; `values` is empty when no selected client-visible unit is registered.
213
230
  */
214
- snapshot(session: Session): ProjectionSnapshot
231
+ snapshot( session: Session, keys?: readonly Extract<keyof SessionProjectionMap, string>[], ): ProjectionSnapshot
232
+
233
+ /**
234
+ * Read only already-materialized client-visible cells without folding history.
235
+ * Values may trail the live Session and are therefore hints, not a complete
236
+ * baseline. Missing cells are omitted.
237
+ * @param session - attached Session whose cached cells are inspected.
238
+ * @param keys - optional wire keys to view.
239
+ * @returns the lowest common cached cut, or `undefined` when no wire cell exists.
240
+ */
241
+ cachedSnapshot( session: Session, keys?: readonly Extract<keyof SessionProjectionMap, string>[], ): ProjectionSnapshot | undefined
215
242
 
216
243
  /**
217
244
  * State-level checkpoint of every persisted unit for one session, read
@@ -255,9 +282,10 @@ restoreFloor(checkpoint: ProjectionCheckpoint): number | undefined
255
282
  * fuller read path refolds it). The zero-I/O rung of the read ladder —
256
283
  * values are as stale as their rows, never wrong.
257
284
  * @param checkpoint - persisted rows for one session (possibly stale or empty).
285
+ * @param keys - optional wire keys to view.
258
286
  * @returns whole values per key with a usable row; empty when none.
259
287
  */
260
- viewCheckpoint(checkpoint: ProjectionCheckpoint): Partial<SessionProjectionMap>
288
+ viewCheckpoint( checkpoint: ProjectionCheckpoint, keys?: readonly Extract<keyof SessionProjectionMap, string>[], ): Partial<SessionProjectionMap>
261
289
 
262
290
  /**
263
291
  * Cold read: fold every persisted unit over a stored log suffix, seeding
@@ -277,14 +305,27 @@ viewCheckpoint(checkpoint: ProjectionCheckpoint): Partial<SessionProjectionMap>
277
305
  * @param checkpoint - persisted rows for one session (possibly stale or empty).
278
306
  * @param events - the stored events with `seq >= baseSeq`, in seq order.
279
307
  * @param baseSeq - the seq `events` starts at (its first event's seq when non-empty).
308
+ * @param header - immutable metadata for the Session being restored.
280
309
  * @returns the snapshot cut at the supplied log end (`asOfSeq` is the last
281
310
  * supplied event's seq, `baseSeq - 1` for an empty tail) plus the
282
311
  * refreshed checkpoint rows at that cut, ready for a durable write-back.
283
312
  */
284
- restore( checkpoint: ProjectionCheckpoint, events: readonly SessionEvent[], baseSeq: number, ): { snapshot: ProjectionSnapshot; checkpoint: ProjectionCheckpoint }
313
+ restore( checkpoint: ProjectionCheckpoint, events: readonly SessionEvent[], baseSeq: number, header: SessionHeader, ): { snapshot: ProjectionSnapshot; checkpoint: ProjectionCheckpoint }
314
+
315
+ /**
316
+ * Restore an exact cut and install its states on the supplied prepared Session.
317
+ * A later publication reuses these cells; ordinary live reads and event drive
318
+ * advance any constructor-owned suffix exactly once.
319
+ * @param session - exact prepared Session that owns the restored log prefix.
320
+ * @param checkpoint - persisted rows for this Session lifecycle.
321
+ * @param events - exact events at the observation cut.
322
+ * @param baseSeq - first supplied event sequence.
323
+ * @returns all projection values at the supplied cut.
324
+ */
325
+ hydrate( session: Session, checkpoint: ProjectionCheckpoint, events: readonly SessionEvent[], baseSeq: number, ): ProjectionSnapshot
285
326
  ```
286
327
 
287
- Types: [Session](./session.md) · [SessionEvent](./session.md)
328
+ Types: [Session](./session.md) · [SessionEvent](./session.md) · [SessionHeader](./persistence.md)
288
329
 
289
330
  Source: [`packages/session/session-projection/src/index.ts`](https://github.com/deepseek-ai/deepseek-harness/blob/master/packages/session/session-projection/src/index.ts)
290
331
  <!-- END GENERATED cordis-surface -->
@@ -141,7 +141,7 @@ interface SessionEventSearchDocument extends SessionEventRecord {
141
141
  }
142
142
  ```
143
143
 
144
- `ctx.sessionQuery.filterSessions(filters)` applies `SessionResultFilter` to the complete logical corpus; `ctx.sessionQuery.filterEvents(sessionId, filters)` returns matching documents in ascending seq order. Messages, reasoning, tool calls/results, blocked prompts, todos, and failure/status detail contribute semantic text; structural events and stream chunks do not.
144
+ `ctx.sessionQuery.filterSessions(filters)` applies `SessionResultFilter` to the complete logical corpus; `ctx.sessionQuery.filterEvents(sessionId, filters)` returns matching documents in ascending seq order. Messages, tool calls/results, todos, and failure/status detail contribute semantic text; reasoning blocks, blocked prompts, structural events, and stream chunks do not.
145
145
 
146
146
  ## Full-text search pages
147
147
 
@@ -376,6 +376,14 @@ Unified live-preferred session query service.
376
376
  Exact reads, filters, and traces are backend-independent concrete behavior. A backend implements full-text observation, reconciliation, ranking, cursor generations, and query execution on the same `ctx.sessionQuery` service.
377
377
 
378
378
  ```ts cordis-catalog
379
+ /**
380
+ * Observe one exact live or prepared Session without a persistence listing preflight.
381
+ * @param sessionId - logical Session identity.
382
+ * @param options - cancellation and projection selection for this read.
383
+ * @returns a caller-owned observation lease.
384
+ */
385
+ observeSession( sessionId: SessionId, options: SessionObservationOptions = {}, ): Promise<SessionObservation>
386
+
379
387
  /**
380
388
  * Search the live-preferred logical corpus and group by session.
381
389
  * @param request - query text, metadata filters, page size, and cursor.