dsh-llm-fallbacks 0.4.4 → 0.5.1

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.
@@ -0,0 +1,292 @@
1
+ /**
2
+ * Dispatch-seam role resolution + per-child record (plan
3
+ * role-based-subagent-adoption Task 1).
4
+ *
5
+ * The seam is the documented cordis service-read waterfall
6
+ * (`Events['internal/get']`, `@deepseek-ai/cordis` 4.0.2
7
+ * `lib/types/events.d.ts`): a listener wraps the `subagents` service VALUE on
8
+ * read and returns a prototype-delegating wrapper whose `start` /
9
+ * `startContinuable` resolve the dispatch role from the incoming request
10
+ * BEFORE delegating. The real service object is never mutated; readers of the
11
+ * `subagents` service transparently receive the wrapper.
12
+ *
13
+ * Role source (plan Global Constraints): the child request's prompt carries
14
+ * the Assignment header field `**Execute as**: <id>` — dsh role binding is
15
+ * prompt-only. {@link resolveDeclaredRoleFromAssignment} is the ONE pure
16
+ * resolution point; it canonicalizes exactly like the existing trimmed-id
17
+ * map (trim, drop ONE leading `@`, case-insensitive match against the
18
+ * declared trimmed ids) and returns the DECLARED RAW id. Undeclared, absent,
19
+ * `inherit`, or ambiguous declarations resolve to `undefined` = no role.
20
+ *
21
+ * The resolution reads the Assignment HEADER region only, never the whole
22
+ * prompt (Task 1 CF-2): the region ends at the first Assignment body marker,
23
+ * and when the prompt carries NO marker it is bounded to the LEADING HEADER
24
+ * BLOCK — the first paragraph plus every following paragraph that opens with
25
+ * header material, ending at the prompt's first prose paragraph. So a
26
+ * `**Execute as**: <id>` line quoted inside a body paragraph can never shape a
27
+ * resolution (it is outside the region) even though mstar's canonical dispatch
28
+ * text carries no body marker at all. Inside the region the engine's pinned
29
+ * ambiguity rule stands unchanged: the DISTINCT DECLARED ids must collapse to
30
+ * exactly one, else the dispatch is a no-op — never a first-occurrence pick.
31
+ *
32
+ * Per-child record: the wrapped start's RESULT carries the child session id —
33
+ * `start` resolves to a `SubagentRun` (`id` IS the published child session id
34
+ * for a local run) and `startContinuable` to a `ContinuableStart` (`childId`).
35
+ * The record `{ role, at, firstNoticePending }` is keyed by that id (Task 3
36
+ * reads it to emit the one role notice row). A result carrying only a
37
+ * `jobId` — a registry job id, NOT a session id — is never keyed: the child is
38
+ * correlated instead through the parent-owned `subagent/catalog` session event
39
+ * `{ childId, label }` joined on the delegating parent + request label.
40
+ *
41
+ * Persona delivery (plan Task 2): the SAME single resolution point merges the
42
+ * declared role's `roles.list[].persona` (trimmed, non-empty) into the
43
+ * request's NATIVE `persona` slot — the slot the runtime composes as the
44
+ * scoped `deployment:persona-prefix` system-prompt section on the child. The
45
+ * source is the RESOLVED ROLE only, so delivery is chain-independent: a
46
+ * `chain: []` role gets its persona exactly like a chained one (no routing
47
+ * state is read). Both native start surfaces are covered, with the gate each
48
+ * one's fail-loud contract needs (measured on the installed
49
+ * `@deepseek-ai/dsh-subagent` `0.1.5-rc.1`): the one-shot `start` REJECTS a
50
+ * request carrying `persona` for a provider whose
51
+ * `getProvider(name).capabilities.persona` is not `true` (`lib/index.js:3202-3227`,
52
+ * `assertCapabilities`), so the merge pre-checks that flag; the continuable
53
+ * surface is composed by the continuation manager, which applies
54
+ * `request.persona` unconditionally (`lib/index.js:1703-1705`) and is gated by
55
+ * the provider's `prepareContinuable` presence instead (`lib/index.js:3179-3183`).
56
+ * A gate miss skips the persona with ONE contained debug log — never a failed
57
+ * start. An explicit caller `persona` WINS (the slot is filled only when it is
58
+ * absent), and every skip path returns the caller's OWN request object, so the
59
+ * native call stays byte-identical.
60
+ *
61
+ * In-session notice row (plan Task 3): the SAME install point registers the
62
+ * `agent/pre-step` emitter (`./role-notice.ts`) over the record map it owns, so
63
+ * the role is announced once in the child's own session. Its per-agent
64
+ * `noticeEmitted` marker is exposed here for the caller's cleanup sites: the
65
+ * plugin dispose effect clears it, while `agent/disposed` deliberately KEEPS it
66
+ * (CF-6 — the marker is SESSION-stable and bounded, because a continuable
67
+ * child's durable session survives its activation).
68
+ *
69
+ * Durable role visibility (plan Task 3b): the SAME install point registers the
70
+ * host-side session projection unit (`./role-projection.ts`) that folds that
71
+ * notice row out of the child's own log, so the session-header badge can show
72
+ * the role for a settled child and after a host restart — a read of the SAME
73
+ * single write primitive, never a second one.
74
+ *
75
+ * Multi-fiber composition (CF-5): the record map and the notice marker are
76
+ * ROOT-shared, while only the `internal/get` wrapper (+ the catalog listener it
77
+ * feeds) is single-owner. Every applied fiber registers its OWN notice emitter
78
+ * and projection unit and reports its ownership structurally
79
+ * (`SubagentSeam.ownsWrapper`), so a disposing fiber cannot silently take the
80
+ * role surfaces down with it.
81
+ *
82
+ * Degrade-never-crash: an absent/reshaped service, an unexpected result
83
+ * shape, or any throwing bookkeeping degrades to the native path with at most
84
+ * ONE contained debug log — a dispatch is never affected.
85
+ *
86
+ * @module dsh-llm-fallbacks/subagents-seam
87
+ */
88
+ import type { Context } from '@deepseek-ai/cordis';
89
+ import { type FallbacksRole } from './config.ts';
90
+ /**
91
+ * The cordis service-read waterfall event the seam registers on. Referenced
92
+ * ONCE (registration) so a future cordis rename is a one-line change here.
93
+ */
94
+ export declare const SUBAGENT_SEAM_EVENT = "internal/get";
95
+ /** The service name the seam wraps (`ctx.subagents`). */
96
+ export declare const SUBAGENT_SEAM_SERVICE = "subagents";
97
+ /**
98
+ * One dispatch-resolved per-child record (Task 3 reads it to emit exactly one
99
+ * role notice row per child session).
100
+ */
101
+ export interface SubagentSeamRecord {
102
+ /**
103
+ * The DECLARED RAW role id resolved at the dispatch seam.
104
+ *
105
+ * Authoritative-role precedence (CF-10, documented rather than resolved by
106
+ * re-branding): a `startContinuable` resume re-keys the child and may resolve
107
+ * a DIFFERENT declared role, and this field is then rewritten while the
108
+ * durable notice row the child's session already carries keeps the FIRST role.
109
+ * The projection deliberately reads the notice row, so the durable role is the
110
+ * child's announced identity and this record is only the LIVE dispatch hint
111
+ * (what the next start / the resume fallback would use). The two can therefore
112
+ * disagree after a re-dispatch until the child is disposed; no consumer may
113
+ * treat the record as the durable role.
114
+ */
115
+ role: string;
116
+ /** When the record was written (epoch ms). */
117
+ at: number;
118
+ /**
119
+ * `true` until the child's role notice row has been emitted (Task 3). A
120
+ * later write for the SAME child session preserves an already-cleared
121
+ * marker, so a repeat dispatch/resume cannot produce a second notice row.
122
+ * The root-shared `noticeEmitted` set is the SESSION-stable half of that
123
+ * guarantee (it survives `agent/disposed`); this field is the per-record half
124
+ * that a resume rewrite preserves.
125
+ */
126
+ firstNoticePending: boolean;
127
+ /**
128
+ * `true` when the role DECLARES a persona and that persona was NOT delivered
129
+ * (Task 2's capability gate declined it, or no provider was registered to
130
+ * carry it). Absent when there was nothing to report: the persona was
131
+ * delivered, the caller set one (explicit intent wins), the role declares
132
+ * none, or no persona source is wired. Task 3 appends
133
+ * ` (persona not applied)` on `true` only.
134
+ *
135
+ * STICKY (Task 3 review Minor 4): a record rewrite (a `startContinuable`
136
+ * resume re-keys the child) can only ADD this verdict, never clear it. A
137
+ * resume recomputes the merge outcome on its own surface — and that surface
138
+ * legitimately returns `false` when it delivers the CALLER's persona or when
139
+ * its own merge degrades — but neither retroactively installs the role's
140
+ * DECLARED persona on the dispatch that already reported it missing. The
141
+ * child's one notice row is a claim about that dispatch, so the first `true`
142
+ * survives; a later `false` is simply not recorded.
143
+ */
144
+ personaNotApplied?: boolean;
145
+ }
146
+ /** One consumed prompt content block (`@deepseek-ai/dsh-llm` `ContentBlock` text members). */
147
+ export interface SubagentPromptBlockView {
148
+ readonly type: string;
149
+ readonly text?: string;
150
+ }
151
+ /**
152
+ * Structural view of the one-shot start request (consumed fields only): the
153
+ * `prompt` is the role-extraction source, `label` + `parent` are the
154
+ * catalog-correlation join key, and `persona` is the NATIVE per-child persona
155
+ * slot the role persona is merged into (plan Global Constraints: an explicit
156
+ * caller value wins). The wrapper forwards the request object itself, so every
157
+ * other field reaches the service unchanged.
158
+ */
159
+ export interface SubagentStartRequestView {
160
+ readonly prompt?: readonly SubagentPromptBlockView[];
161
+ readonly label?: string;
162
+ readonly parent?: {
163
+ readonly session?: {
164
+ readonly id?: string;
165
+ };
166
+ };
167
+ readonly persona?: string;
168
+ }
169
+ /**
170
+ * Structural view of the continuable start spec (consumed fields only):
171
+ * `childId` is the caller-reserved identity a resume already carries, used as
172
+ * the record fallback when the resume request has no Assignment header.
173
+ */
174
+ export interface ContinuableStartSpecView {
175
+ readonly provider: string;
176
+ readonly label?: string;
177
+ readonly childId?: string;
178
+ readonly request: SubagentStartRequestView;
179
+ }
180
+ /** Options for {@link installSubagentSeam}. */
181
+ export interface SubagentSeamOptions {
182
+ /**
183
+ * Live declared-role id map (trimmed id → DECLARED RAW id), read per start
184
+ * so a settings change is observed without a re-install.
185
+ */
186
+ roleIds: () => ReadonlyMap<string, string>;
187
+ /**
188
+ * Live declared roles (`roles.list`) — the persona source, read per start so
189
+ * a settings/persona edit is observed without a re-install. Absent ⇒ the
190
+ * seam only records roles (no persona delivery).
191
+ */
192
+ roles?: () => readonly FallbacksRole[];
193
+ /** Contained debug sink (at most one line per contained degrade). */
194
+ debug?: (message: string) => void;
195
+ }
196
+ /** The installed seam (Task 3 surface + explicit teardown). */
197
+ export interface SubagentSeam {
198
+ /**
199
+ * Per-child records keyed by the child SESSION id (the wrapped start's
200
+ * result id). ROOT-shared (CF-5): every fiber applied over the same root reads
201
+ * the same map. Mutable by design: Task 3 clears `firstNoticePending` after
202
+ * emitting the notice.
203
+ */
204
+ readonly records: Map<string, SubagentSeamRecord>;
205
+ /**
206
+ * Per-agent marker of children whose role notice row was already emitted
207
+ * (mirrors the runtime's `dispatchInjected`): the in-memory half of Task 3's
208
+ * once-per-child guarantee, also ROOT-shared. SESSION-stable (CF-6): it is
209
+ * deliberately NOT cleared on `agent/disposed` — a continuable child's durable
210
+ * session survives the activation, so clearing the marker would let a
211
+ * re-activation resume append a second notice row to the same log. It is
212
+ * bounded (`NOTICE_EMITTED_LIMIT`) and cleared only in the plugin dispose
213
+ * effect.
214
+ */
215
+ readonly noticeEmitted: Set<string>;
216
+ /**
217
+ * `true` when THIS install owns the root-scoped `internal/get` wrapper (plus
218
+ * the catalog-correlation listener it feeds) — the only part that must have a
219
+ * single owner. `false` on a later fiber over the same root, which still
220
+ * registers its OWN notice emitter and projection unit. The callers' dedupe
221
+ * branch reads THIS field (CF-5): a structured marker, never a thrown
222
+ * message.
223
+ */
224
+ readonly ownsWrapper: boolean;
225
+ /** Stop intercepting service reads (the owning fiber's teardown also does). */
226
+ dispose(): void;
227
+ }
228
+ /**
229
+ * Cap on un-correlated claims (a `jobId`-only result whose catalog event never
230
+ * arrives). Bounded insertion-order eviction — recency-ordered, because a
231
+ * re-claimed hot key is re-inserted (`beginCorrelation` deletes before it sets),
232
+ * so the busiest claim can never be the one evicted.
233
+ *
234
+ * @internal Exported for the bound test (mirrors `subagentSeamOf`): the value is
235
+ * not part of the plugin's public surface, but a test that hardcoded 64 could
236
+ * not tell the real bound from a stale copy.
237
+ */
238
+ export declare const PENDING_CORRELATION_LIMIT = 64;
239
+ /**
240
+ * PURE dispatch-role resolution (plan Global Constraints): read the Assignment
241
+ * header field `**Execute as**: <id>` from `prompt` and return the DECLARED
242
+ * RAW role id, or `undefined` when nothing resolves. Never throws.
243
+ *
244
+ * - absent / empty / body-quoted-only → `undefined`
245
+ * - `inherit` (the reserved "no specific role" id) → `undefined`
246
+ * - undeclared id → `undefined` (never invent a role)
247
+ * - several DISTINCT declared ids in the header region → `undefined`
248
+ * (ambiguous); repeats of the same declared id (e.g. `coder` and `@CODER`)
249
+ * still resolve
250
+ *
251
+ * @param prompt - the Assignment text carried by the child request's prompt.
252
+ * @param roleIds - the declared trimmed-id map (`trimmed id → declared raw id`).
253
+ */
254
+ export declare function resolveDeclaredRoleFromAssignment(prompt: string, roleIds: ReadonlyMap<string, string>): string | undefined;
255
+ /**
256
+ * PURE, chain-independent persona lookup (plan Task 2 / Global Constraints):
257
+ * the DECLARED `persona` of `roleId`, trimmed, and only when it is still
258
+ * non-empty after the trim — a blank persona is NO persona. `roleId` is the
259
+ * DECLARED RAW id the seam already resolved, so this is an exact id match, not
260
+ * a second canonicalization pass.
261
+ *
262
+ * Only `id` and `persona` are read: no `chain`, no `rootChain`, no routing
263
+ * state — a `chain: []` role's persona resolves exactly like a chained one's.
264
+ *
265
+ * @param roles - the live declared roles (`roles.list`).
266
+ * @param roleId - the DECLARED RAW role id resolved at the seam.
267
+ */
268
+ export declare function personaForRole(roles: readonly FallbacksRole[], roleId: string): string | undefined;
269
+ /**
270
+ * @internal Test seam (mirrors `chainHeads`): the seam installed on
271
+ * `ctx`, if any. Not part of the plugin's public surface — lets tests read and
272
+ * seed the per-child record map without reaching into the installation
273
+ * closure. `undefined` when no seam is installed.
274
+ */
275
+ export declare function subagentSeamOf(ctx: Context): SubagentSeam | undefined;
276
+ /**
277
+ * Install the dispatch seam on `ctx`: wrap every `subagents` service read and
278
+ * resolve + record the dispatch role per start, and register this fiber's role
279
+ * surfaces (the notice emitter + the projection unit).
280
+ *
281
+ * Never throws for a multi-fiber apply (CF-5): a fiber whose root already owns
282
+ * the `internal/get` wrapper gets a seam with `ownsWrapper: false` — it shares
283
+ * the root's record map / notice marker (so exactly one notice row is written
284
+ * per child) and still installs its OWN notice emitter and projection unit, so
285
+ * neither surface dies when another fiber of the same root disposes.
286
+ *
287
+ * Returns the seam (the shared record map Task 3 reads, the ownership marker,
288
+ * plus an idempotent `dispose` that withdraws THIS fiber's registrations). The
289
+ * `internal/get` listener is owned by this module; callers that rely on the
290
+ * fiber teardown alone may ignore `dispose`.
291
+ */
292
+ export declare function installSubagentSeam(ctx: Context, options: SubagentSeamOptions): SubagentSeam;
@@ -78,7 +78,7 @@ export declare const PRESETS: Record<PresetId, PresetDefinition>;
78
78
  * are the ordered walk before that last-resort fallback. An empty chain or
79
79
  * a chain whose tail is not an official model (including the retired V4
80
80
  * ids) keeps slot rows inert and refuses the virtual-row
81
- * override/delegate; the v0.2.2 failure walk over the raw chain stays
81
+ * delegate; the v0.2.2 failure walk over the raw chain stays
82
82
  * verbatim.
83
83
  */
84
84
  export declare function isAllDayConforming(chain: readonly string[]): boolean;
@@ -92,7 +92,8 @@ export declare function isAllDayConforming(chain: readonly string[]): boolean;
92
92
  * P6 (qc1 F-001): without a conforming all-day the slot rows are inert, so
93
93
  * this IS the raw `rootChain` — the gate lives in {@link resolveSlotState},
94
94
  * the single source every slot surface (this, the 分时切换 log, the
95
- * `/fallbacks` strip, select-is-primary, the virtual adapter delegate) reads.
95
+ * `/fallbacks` strip, the virtual adapter delegate, its served-route anchor)
96
+ * reads.
96
97
  */
97
98
  export declare function resolveEffectiveChain(config: FallbacksConfig, now: Date, tz: string): string[];
98
99
  /**
@@ -14,7 +14,7 @@
14
14
  * reconcile thunk, wired by `apply()`) plus child activation. The row is
15
15
  * visible whenever the plugin is enabled — a non-conforming all-day chain
16
16
  * does NOT hide it (PR #62 feedback); conformance still gates a
17
- * successful override/delegate (`effectiveHeadOf` below refuses a
17
+ * successful delegate (`effectiveHeadOf` below refuses a
18
18
  * non-conforming all-day). The condition deliberately ignores `timeSlots`
19
19
  * and conformance, so slot-row edits and chain edits never churn
20
20
  * registration.
@@ -26,12 +26,14 @@
26
26
  * `agent/request-error`, where the existing engine walks from there).
27
27
  * `resolveModel` proxies the current effective head's metadata when
28
28
  * resolvable (modalities/context-window/reasoning follow the head) with a
29
- * permissive default otherwise — never throws.
29
+ * permissive default otherwise — never throws. `providerRetryPolicy` and
30
+ * `imageRequestPricing` mirror the same head for the host-captured retry
31
+ * policy and the token meter — also never throwing.
30
32
  *
31
33
  * @module dsh-llm-fallbacks/virtual-adapter
32
34
  */
33
35
  import type { Context } from '@deepseek-ai/cordis';
34
- import { LlmAdapter, type GenerateOptions, type LlmImageRequestPricing, type LlmModelInfo, type LlmResolvedModelInfo, type LlmRuntime, type StreamChunk } from '@deepseek-ai/dsh-llm';
36
+ import { LlmAdapter, type GenerateOptions, type LlmImageRequestPricing, type LlmModelInfo, type LlmResolvedModelInfo, type LlmRuntime, type ResolvedRetryPolicy, type StreamChunk } from '@deepseek-ai/dsh-llm';
35
37
  import type { FallbacksConfig } from './config.ts';
36
38
  /** Provider route of the virtual adapter (exact string, spec lock). */
37
39
  export declare const FALLBACKS_PROVIDER = "FallbacksChain";
@@ -62,15 +64,33 @@ export interface EffectiveHead {
62
64
  }
63
65
  /**
64
66
  * The FIRST DISPATCHABLE exact head of a chain — the single definition of
65
- * "effective head" (F-001) shared by the root select-is-primary override
66
- * (`src/index.ts`) and the virtual adapter's delegate paths. Walks the SAME
67
- * chain `resolveEffectiveChain` produces, skipping entries that can never
67
+ * "effective head" (F-001) shared by the virtual adapter's delegate paths and
68
+ * the routing engine's served-route anchor (`anchorServedRoute` in
69
+ * `src/index.ts`). Walks the SAME chain `resolveEffectiveChain` produces,
70
+ * skipping entries that can never
68
71
  * be dispatched: malformed selectors (config-warning path), `provider/*`
69
72
  * `provider/*` wildcards (no real pair), and self-routes back to
70
73
  * `FallbacksChain/*` (the P1 recursion guard). `undefined` when the chain
71
74
  * is empty or no entry is dispatchable.
72
75
  */
73
76
  export declare function firstDispatchableExactHead(chain: readonly string[]): EffectiveHead | undefined;
77
+ /**
78
+ * The effective chain head at `now` (P1): the first DISPATCHABLE exact head
79
+ * of the SAME `resolveEffectiveChain` the routing engine uses — thin
80
+ * config+clock entry into the shared {@link firstDispatchableExactHead} (no
81
+ * divergent skip/walk rules). `undefined` when the chain is empty or no
82
+ * entry is dispatchable (wildcard, self-route to `FallbacksChain/*`,
83
+ * malformed selector).
84
+ *
85
+ * Conformance gate (PR #62 feedback): the all-day `rootChain` must be
86
+ * conforming (exactly one official model — Flash or Pro)
87
+ * for a successful delegate —
88
+ * a legacy multi-model (or empty) all-day chain earns no primary
89
+ * semantics even though the picker row is visible. `resolveModel` then
90
+ * falls back to the permissive default and `stream()` throws
91
+ * {@link UNDISPATCHABLE_HEAD_CODE}.
92
+ */
93
+ export declare function effectiveHeadOf(config: FallbacksConfig, now: Date): EffectiveHead | undefined;
74
94
  /**
75
95
  * Host picker label: `Auto: <displayName>[<slot>]`.
76
96
  * Display name first so the trigger stays readable and platforms stay
@@ -104,6 +124,22 @@ export declare class FallbacksChainAdapter extends LlmAdapter {
104
124
  * answer for the CURRENT head. Permissive default otherwise; never throws.
105
125
  */
106
126
  resolveModel(provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
127
+ /**
128
+ * Route-accurate retry attribution: the virtual row declares no policy of
129
+ * its own — the virtual `provider` argument is intentionally ignored. The
130
+ * host captures `adapter.providerRetryPolicy(provider) ??
131
+ * resolveRetryPolicy(void 0, …)` ONCE, when a route is registered, so
132
+ * answering here with the SAME policy the concrete head route declared
133
+ * (e.g. a user's `llm-deepseek.retryPolicy`, including `mode: 'always'`)
134
+ * keeps retries behaving exactly as they do on the head `stream()`
135
+ * dispatches. Never throws: an unresolvable head, a vanished `llm`, or an
136
+ * unregistered head provider degrades to `undefined` — the host's own
137
+ * default. That last arm matters: the runtime's lookup THROWS `NO_ADAPTER`
138
+ * for an unknown provider, and this call happens inside the plugin's
139
+ * `registerAdapter`, so an escaping throw would take the whole virtual
140
+ * route down instead of just degrading its policy.
141
+ */
142
+ providerRetryPolicy(_provider: string): ResolvedRetryPolicy | undefined;
107
143
  /**
108
144
  * Route-accurate image pricing (0.1.2 adoption): the virtual row has no
109
145
  * pricing of its own — the virtual `provider`/`model` arguments are
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "dsh",
6
6
  "dsh-plugin"
7
7
  ],
8
- "version": "0.4.4",
8
+ "version": "0.5.1",
9
9
  "type": "module",
10
10
  "main": "./dist/index.js",
11
11
  "types": "./dist/index.d.ts",
@@ -47,9 +47,10 @@
47
47
  "build-client": "tsx scripts/build-client.ts",
48
48
  "release:prepare": "tsx scripts/prepare-release.ts",
49
49
  "release:validate": "tsx scripts/validate-release-version.ts",
50
- "repair:fallbacks-switch-logs": "tsx scripts/repair-fallbacks-switch-logs.ts",
50
+ "repair:session-logs": "tsx scripts/repair-session-logs.ts",
51
51
  "test": "vitest run",
52
- "typecheck": "tsc -p tsconfig.build.json --noEmit && tsc --noEmit",
52
+ "typecheck": "tsc -p tsconfig.build.json --noEmit && tsc --noEmit && pnpm run typecheck:scripts",
53
+ "typecheck:scripts": "tsc -p tsconfig.scripts.json",
53
54
  "prepare": "pnpm run build"
54
55
  },
55
56
  "engines": {
@@ -60,28 +61,29 @@
60
61
  },
61
62
  "peerDependencies": {
62
63
  "@deepseek-ai/cordis": "^4.0.2",
63
- "@deepseek-ai/dsh-agent": "^0.1.5-rc.1",
64
- "@deepseek-ai/dsh-api-gateway": "^0.1.5-rc.1",
65
- "@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.1",
66
- "@deepseek-ai/dsh-api-session-controller": "^0.1.5-rc.1",
67
- "@deepseek-ai/dsh-client-connection": "^0.1.5-rc.1",
68
- "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.1",
69
- "@deepseek-ai/dsh-client-store": "^0.1.5-rc.1",
70
- "@deepseek-ai/dsh-client-ui-chat": "^0.1.5-rc.1",
71
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-rc.1",
72
- "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-rc.1",
73
- "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.1",
74
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.1",
75
- "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.5-rc.1",
76
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.1",
77
- "@deepseek-ai/dsh-commands": "^0.1.5-rc.1",
78
- "@deepseek-ai/dsh-llm": "^0.1.5-rc.1",
79
- "@deepseek-ai/dsh-llm-retry": "^0.1.5-rc.1",
80
- "@deepseek-ai/dsh-session": "^0.1.5-rc.1",
81
- "@deepseek-ai/dsh-settings": "^0.1.5-rc.1",
82
- "@deepseek-ai/dsh-tool-subagent": "^0.1.5-rc.1",
83
- "@deepseek-ai/dsh-typert-protocol": "^0.1.5-rc.1",
84
- "@deepseek-ai/dsh-typert-registry": "^0.1.5-rc.1",
64
+ "@deepseek-ai/dsh-agent": "^0.1.5-rc.2",
65
+ "@deepseek-ai/dsh-api-gateway": "^0.1.5-rc.2",
66
+ "@deepseek-ai/dsh-api-remotes": "^0.1.5-rc.2",
67
+ "@deepseek-ai/dsh-api-session-controller": "^0.1.5-rc.2",
68
+ "@deepseek-ai/dsh-client-connection": "^0.1.5-rc.2",
69
+ "@deepseek-ai/dsh-client-locale": "^0.1.5-rc.2",
70
+ "@deepseek-ai/dsh-client-store": "^0.1.5-rc.2",
71
+ "@deepseek-ai/dsh-client-ui-chat": "^0.1.5-rc.2",
72
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.5-rc.2",
73
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.1.5-rc.2",
74
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.1.5-rc.2",
75
+ "@deepseek-ai/dsh-client-ui-settings": "^0.1.5-rc.2",
76
+ "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.5-rc.2",
77
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.5-rc.2",
78
+ "@deepseek-ai/dsh-commands": "^0.1.5-rc.2",
79
+ "@deepseek-ai/dsh-llm": "^0.1.5-rc.2",
80
+ "@deepseek-ai/dsh-llm-retry": "^0.1.5-rc.2",
81
+ "@deepseek-ai/dsh-session": "^0.1.5-rc.2",
82
+ "@deepseek-ai/dsh-session-projection": "^0.1.5-rc.2",
83
+ "@deepseek-ai/dsh-settings": "^0.1.5-rc.2",
84
+ "@deepseek-ai/dsh-tool-subagent": "^0.1.5-rc.2",
85
+ "@deepseek-ai/dsh-typert-protocol": "^0.1.5-rc.2",
86
+ "@deepseek-ai/dsh-typert-registry": "^0.1.5-rc.2",
85
87
  "@deepseek-ai/schemastery": "^3.18.2",
86
88
  "react": "^18.2.0"
87
89
  },
@@ -48,7 +48,7 @@ const OUT_FILE = 'index.js'
48
48
 
49
49
  /**
50
50
  * Frozen loader module table (dsh mechanism-guide): the plan-documented
51
- * composition = PLATFORM_MODULES (9 entries in 0.1.5-rc.1,
51
+ * composition = PLATFORM_MODULES (9 entries in 0.1.5-rc.2,
52
52
  * `web/src/platform.ts:8-16` — the old runtime `./client` exemption became
53
53
  * the `@deepseek-ai/dsh-client-store` platform module, so the store VALUE
54
54
  * import stays external and resolves from the host loader table) + the