dsh-llm-fallbacks 0.7.0 → 0.8.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.
- package/README.md +27 -27
- package/README.zh-CN.md +25 -25
- package/dist/chain-walk.d.ts +63 -77
- package/dist/chains.d.ts +32 -101
- package/dist/client/FallbacksCard.d.ts +27 -80
- package/dist/client/fallbacks-store.d.ts +71 -225
- package/dist/client/index.d.ts +0 -1
- package/dist/client/index.js +1256 -1926
- package/dist/client/locales.d.ts +59 -105
- package/dist/commands.d.ts +68 -103
- package/dist/config.d.ts +152 -105
- package/dist/gateway.d.ts +63 -109
- package/dist/index.d.ts +3 -25
- package/dist/index.js +552 -775
- package/dist/role-resolution.d.ts +14 -43
- package/dist/schema.d.ts +6 -5
- package/dist/seeds.d.ts +5 -7
- package/dist/state.d.ts +0 -42
- package/dist/subagents-seam.d.ts +23 -45
- package/dist/time-slots.d.ts +21 -24
- package/dist/tui-settings.d.ts +5 -8
- package/dist/virtual-adapter.d.ts +63 -108
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +1 -1
- package/dist/client/GeneralFallbacksRow.d.ts +0 -19
- package/dist/roles.d.ts +0 -89
- package/dist/route-allowlist.d.ts +0 -43
- package/dist/subagent-policy.d.ts +0 -137
|
@@ -1,54 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Virtual `FallbacksChain
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* dsh-TUI (web and TUI share the adapter catalog).
|
|
2
|
+
* Virtual `FallbacksChain` LLM adapter (named virtual chains, plan
|
|
3
|
+
* named-virtual-chains T2): one provider carrying N model rows — ONE per
|
|
4
|
+
* named chain, the row id BEING the chain id (picker selection is
|
|
5
|
+
* `FallbacksChain/<chain id>`), without patching dsh or dsh-TUI.
|
|
7
6
|
*
|
|
8
|
-
* Wiring
|
|
7
|
+
* Wiring: `installFallbacksAdapter` installs ONE conditional
|
|
9
8
|
* `ctx.inject(['llm'])` child — absent `llm` service (test harness) is a
|
|
10
9
|
* clean no-op, and fiber unload ⇒ the child's disposer unregisters the
|
|
11
10
|
* route. Registration is an idempotent transition-reconcile on COMMITTED
|
|
12
|
-
* config snapshots: register when
|
|
13
|
-
* (`isFallbackActive`
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* all-day). The registration gate is `isFallbackActive` content presence
|
|
20
|
-
* (`rootChain` / `timeSlots` / declared roles): a slot-row edit that flips
|
|
21
|
-
* that gate (e.g. removing the only slot while `rootChain` and roles are
|
|
22
|
-
* empty) intentionally churns registration — the content-presence contract
|
|
23
|
-
* (plan fallbacks-web-ux-alignment T2), not churn to design away; do NOT
|
|
24
|
-
* re-narrow the condition to ignore `timeSlots`. Conformance-only chain
|
|
25
|
-
* edits still never churn (conformance gates the delegate, not the row).
|
|
11
|
+
* config snapshots: register when `chains` goes empty → non-empty
|
|
12
|
+
* (`isFallbackActive` = `chains.length > 0`), unregister on the reverse,
|
|
13
|
+
* driven by the settings `onChange` hook (the returned reconcile thunk,
|
|
14
|
+
* wired by `apply()`) plus child activation. A chain with empty or
|
|
15
|
+
* non-conforming `entries` still shows its row (conformance gates the
|
|
16
|
+
* delegate via {@link effectiveHeadOf}, not the row). One
|
|
17
|
+
* `llm.registerAdapter([FALLBACKS_PROVIDER], adapter)` call serves all rows.
|
|
26
18
|
*
|
|
27
|
-
* Adapter behavior
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* the
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
19
|
+
* Adapter behavior: `listModels` advertises one row per chain;
|
|
20
|
+
* `stream()` WALKS the selected chain (cooldown / failed-set / switch cap /
|
|
21
|
+
* recovery bookkeeping) in `src/chain-walk.ts` — the walk is injected at
|
|
22
|
+
* install and owns the pre-attempt guards. `resolveModel` /
|
|
23
|
+
* `imageRequestPricing` resolve the chain FROM the `model` argument and
|
|
24
|
+
* follow its effective head (permissive default otherwise — never throw).
|
|
25
|
+
* `providerRetryPolicy` returns `undefined` (documented degradation): the
|
|
26
|
+
* host captures the policy ONCE per provider at registration, so the virtual
|
|
27
|
+
* route cannot mirror a per-chain head's policy — the host default applies.
|
|
36
28
|
*
|
|
37
|
-
|
|
29
|
+
@module dsh-llm-fallbacks/virtual-adapter
|
|
38
30
|
*/
|
|
39
31
|
import type { Context } from '@deepseek-ai/cordis';
|
|
40
32
|
import { LlmAdapter, type GenerateOptions, type LlmImageRequestPricing, type LlmModelInfo, type LlmResolvedModelInfo, type LlmRuntime, type ResolvedRetryPolicy, type StreamChunk } from '@deepseek-ai/dsh-llm';
|
|
41
|
-
import { type FallbacksConfig } from './config.ts';
|
|
42
|
-
import type { PolicyRoute } from './subagent-policy.ts';
|
|
33
|
+
import { type FallbacksConfig, type NamedChain } from './config.ts';
|
|
43
34
|
/** Provider route of the virtual adapter (exact string, spec lock). */
|
|
44
35
|
export declare const FALLBACKS_PROVIDER = "FallbacksChain";
|
|
45
|
-
/**
|
|
46
|
-
* Model id of the virtual catalog row (exact string, spec lock). "Auto" is
|
|
47
|
-
* the hardcoded picker id (not i18n — user decision 2026-08-18). The
|
|
48
|
-
* catalog `name` the host picker renders is dynamic — see
|
|
49
|
-
* {@link pickerDisplayName} (`Auto: <model>[<slot>]`).
|
|
50
|
-
*/
|
|
51
|
-
export declare const FALLBACKS_CHAIN_MODEL = "Auto";
|
|
52
36
|
/** One dispatchable exact head: `provider/model`. Wildcards are never seeds (P3). */
|
|
53
37
|
export interface EffectiveHead {
|
|
54
38
|
provider: string;
|
|
@@ -57,50 +41,32 @@ export interface EffectiveHead {
|
|
|
57
41
|
/**
|
|
58
42
|
* The FIRST DISPATCHABLE exact head of a chain — the single definition of
|
|
59
43
|
* "effective head" (F-001) shared by this module's metadata paths
|
|
60
|
-
* (`pickerDisplayName` / `resolveModel` / `
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* (`injectRoute` in `src/subagents-seam.ts`). Walks the SAME chain
|
|
64
|
-
* `resolveEffectiveChain` produces, skipping entries that can never
|
|
44
|
+
* (`pickerDisplayName` / `resolveModel` / `imageRequestPricing`) and the
|
|
45
|
+
* walk's head resolution (`resolveWalk` in `src/chain-walk.ts`). Walks the
|
|
46
|
+
* SAME chain `resolveEffectiveChain` produces, skipping entries that can never
|
|
65
47
|
* be dispatched: malformed selectors (config-warning path), `provider/*`
|
|
66
48
|
* `provider/*` wildcards (no real pair), and self-routes back to
|
|
67
49
|
* `FallbacksChain/*` (the P1 recursion guard). `undefined` when the chain
|
|
68
50
|
* is empty or no entry is dispatchable.
|
|
69
|
-
*
|
|
70
|
-
* `allowedModels` (spec D1) additionally restricts the result to the host
|
|
71
|
-
* subagent allowlist: a PLUGIN-originated route is only ever attempted on a
|
|
72
|
-
* route the host authorizes, so the seam's mount gate and a plugin-originated
|
|
73
|
-
* walk's start pass the enabled policy's list and an entry it excludes is
|
|
74
|
-
* skipped like any other non-dispatchable entry. An empty intersection is
|
|
75
|
-
* `undefined` — the caller declines the mount / refuses the walk.
|
|
76
51
|
*/
|
|
77
|
-
export declare function firstDispatchableExactHead(chain: readonly string[]
|
|
52
|
+
export declare function firstDispatchableExactHead(chain: readonly string[]): EffectiveHead | undefined;
|
|
78
53
|
/**
|
|
79
|
-
* The effective chain head at `now
|
|
80
|
-
* of the SAME `resolveEffectiveChain` the
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* entry is dispatchable (wildcard,
|
|
84
|
-
* malformed selector).
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* conforming (exactly one official model — Flash or Pro)
|
|
88
|
-
* for a successful delegate —
|
|
89
|
-
* a legacy multi-model (or empty) all-day chain earns no primary
|
|
90
|
-
* semantics even though the picker row is visible. `resolveModel` then
|
|
91
|
-
* falls back to the permissive default. The delegate side of that gate is the
|
|
92
|
-
* chain walk's ROOT-origin rule (`UNDISPATCHABLE_EFFECTIVE_HEAD`); a subagent
|
|
93
|
-
* walks its role chain instead, so the all-day shape never gates a child.
|
|
54
|
+
* The effective chain head of ONE named chain at `now`: the first
|
|
55
|
+
* DISPATCHABLE exact head of the SAME `resolveEffectiveChain` the walk uses —
|
|
56
|
+
* thin chain+clock entry into the shared {@link firstDispatchableExactHead}.
|
|
57
|
+
* `undefined` when the entries are empty or non-conforming (the tail must be
|
|
58
|
+
* exactly one official model), or no entry is dispatchable (wildcard,
|
|
59
|
+
* self-route to `FallbacksChain/*`, malformed selector). `resolveModel` then
|
|
60
|
+
* falls back to the permissive default; the delegate side of the conformance
|
|
61
|
+
* gate is the chain walk's `UNDISPATCHABLE_EFFECTIVE_HEAD`.
|
|
94
62
|
*/
|
|
95
|
-
export declare function effectiveHeadOf(
|
|
63
|
+
export declare function effectiveHeadOf(chain: NamedChain, now: Date, tz?: string): EffectiveHead | undefined;
|
|
96
64
|
/**
|
|
97
|
-
* Host picker label:
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* there is no dispatchable head. Slot label from {@link resolveSlotState}.
|
|
101
|
-
* @param modelDisplayName - catalog/resolved name; falls back to the id.
|
|
65
|
+
* Host picker label for one chain: `` `${chain.label}: <head>[<slot>]` ``.
|
|
66
|
+
* Bare `chain.label` when there is no dispatchable head. The label is
|
|
67
|
+
* user-editable and display-only; the model id is the chain id.
|
|
102
68
|
*/
|
|
103
|
-
export declare function pickerDisplayName(
|
|
69
|
+
export declare function pickerDisplayName(chain: NamedChain, now?: Date, modelDisplayName?: string, tz?: string): string;
|
|
104
70
|
/**
|
|
105
71
|
* The virtual adapter (P1/P3). `stream()` hands the call to the injected walk
|
|
106
72
|
* (see `src/chain-walk.ts`) — registration metadata stays here.
|
|
@@ -115,50 +81,39 @@ export declare class FallbacksChainAdapter extends LlmAdapter {
|
|
|
115
81
|
id: string;
|
|
116
82
|
name: string;
|
|
117
83
|
};
|
|
118
|
-
/** Advisory catalog:
|
|
84
|
+
/** Advisory catalog: ONE row per named chain; `name` is the live picker label. */
|
|
119
85
|
listModels(provider: string): Promise<readonly LlmModelInfo[]>;
|
|
120
86
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
87
|
+
* Model-scoped metadata: resolve the chain FROM the `model` argument (the
|
|
88
|
+
* chain id) and follow its effective head — modalities/context-window/
|
|
89
|
+
* reasoning follow the head, the truthful answer for the CURRENT head.
|
|
90
|
+
* Permissive default for an unknown chain id or an unresolvable head;
|
|
91
|
+
* never throws.
|
|
124
92
|
*/
|
|
125
93
|
resolveModel(provider: string, model: string, signal?: AbortSignal): Promise<LlmResolvedModelInfo>;
|
|
126
94
|
/**
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* (e.g. a user's `llm-deepseek.retryPolicy`, including `mode: 'always'`)
|
|
133
|
-
* keeps retries behaving exactly as they do on the head `stream()`
|
|
134
|
-
* dispatches. Never throws: an unresolvable head, a vanished `llm`, or an
|
|
135
|
-
* unregistered head provider degrades to `undefined` — the host's own
|
|
136
|
-
* default. That last arm matters: the runtime's lookup THROWS `NO_ADAPTER`
|
|
137
|
-
* for an unknown provider, and this call happens inside the plugin's
|
|
138
|
-
* `registerAdapter`, so an escaping throw would take the whole virtual
|
|
139
|
-
* route down instead of just degrading its policy.
|
|
95
|
+
* DOCUMENTED DEGRADATION (named virtual chains): the adapter stays ONE
|
|
96
|
+
* `FallbacksChain` provider carrying N chain rows, but the host captures
|
|
97
|
+
* `adapter.providerRetryPolicy(provider)` ONCE per provider at
|
|
98
|
+
* registration, so the virtual route can no longer mirror its head's
|
|
99
|
+
* retry policy. It returns `undefined` — the host's own default applies.
|
|
140
100
|
*/
|
|
141
101
|
providerRetryPolicy(_provider: string): ResolvedRetryPolicy | undefined;
|
|
142
102
|
/**
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
* (including `undefined`). Never throws: an unresolvable head, a
|
|
149
|
-
* vanished `llm`, or a throwing delegate degrades to `undefined` — the
|
|
150
|
-
* token meter's neutral estimate.
|
|
103
|
+
* Image pricing, model-scoped: resolve the chain FROM the `model`
|
|
104
|
+
* argument and delegate through the runtime lookup for its effective head
|
|
105
|
+
* (including `undefined`). Never throws: an unknown chain id, an
|
|
106
|
+
* unresolvable head, a vanished `llm`, or a throwing delegate degrades to
|
|
107
|
+
* `undefined` — the token meter's neutral estimate.
|
|
151
108
|
*/
|
|
152
|
-
imageRequestPricing(_provider: string,
|
|
109
|
+
imageRequestPricing(_provider: string, model: string): LlmImageRequestPricing | undefined;
|
|
153
110
|
/**
|
|
154
|
-
* The virtual route's entry point
|
|
155
|
-
* WALKED (`src/chain-walk.ts`) — the walk owns the
|
|
156
|
-
* (`
|
|
157
|
-
* `LLM_UNAVAILABLE`, thrown synchronously
|
|
158
|
-
* normalizes them exactly like an adapter
|
|
159
|
-
*
|
|
160
|
-
* root's all-day chain, so a root-only gate here would refuse a child that
|
|
161
|
-
* has a perfectly walkable role chain.
|
|
111
|
+
* The virtual route's entry point: `options.model` IS the chain id and the
|
|
112
|
+
* selected chain is WALKED (`src/chain-walk.ts`) — the walk owns the
|
|
113
|
+
* pre-attempt guards (`UNKNOWN_CHAIN` / `EMPTY_EFFECTIVE_CHAIN` /
|
|
114
|
+
* `UNDISPATCHABLE_EFFECTIVE_HEAD` / `LLM_UNAVAILABLE`, thrown synchronously
|
|
115
|
+
* before any chunk, so the runtime normalizes them exactly like an adapter
|
|
116
|
+
* throw).
|
|
162
117
|
*/
|
|
163
118
|
stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
|
|
164
119
|
}
|
package/locale/en.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
3
|
"title": "Fallbacks",
|
|
4
|
-
"description": "
|
|
4
|
+
"description": "Named fallback chains for model requests: on failure the walk switches to the next model in the chain so agent steps keep running; the main agent must select a virtual chain in the model picker for fallback to apply.",
|
|
5
5
|
"detail": "docs/DETAIL.md"
|
|
6
6
|
}
|
|
7
7
|
}
|
package/locale/zh.json
CHANGED
package/package.json
CHANGED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
3
|
-
import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-ui-slots';
|
|
4
|
-
import type { FallbacksSettingsController, FallbacksSettingsState } from './fallbacks-store.ts';
|
|
5
|
-
/** Injected dependencies of {@link GeneralFallbacksRow} (slot `inject`). */
|
|
6
|
-
export interface GeneralFallbacksRowInjected {
|
|
7
|
-
/** The shared controller (loaded on first mount, refreshed on pushed invalidations). */
|
|
8
|
-
controller: FallbacksSettingsController;
|
|
9
|
-
/** uSES subscription hook bound to the store (inject face — advisor pattern). */
|
|
10
|
-
useSnapshot: SnapshotSelectorHook<FallbacksSettingsState>;
|
|
11
|
-
}
|
|
12
|
-
/** Props delivered by the slot outlet: runtime share + locale seat + inject face. */
|
|
13
|
-
export type GeneralFallbacksRowProps = PropsRuntime<'settings.general.item'> & PropsLocale<'fallbacks'> & GeneralFallbacksRowInjected;
|
|
14
|
-
/**
|
|
15
|
-
* Render the Fallbacks status row.
|
|
16
|
-
* @param props - composed slot props.
|
|
17
|
-
* @returns the row element tree.
|
|
18
|
-
*/
|
|
19
|
-
export declare function GeneralFallbacksRow({ controller, useSnapshot, t }: GeneralFallbacksRowProps): ReactNode;
|
package/dist/roles.d.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Role resolution for fallback chains (spec §7.1; plan fallbacks-role-runtime
|
|
3
|
-
* Task 1).
|
|
4
|
-
*
|
|
5
|
-
* This module is the RULES stage. Dispatch-time role resolution
|
|
6
|
-
* (plan fallbacks-role-automatch; plan dsh-148 added the dispatch stage) is
|
|
7
|
-
* the three-stage resolver in `role-resolution.ts` — explicit
|
|
8
|
-
* (`session.header.agentPreset`) → dispatch-declared role (the seam record
|
|
9
|
-
* resolved from `**Execute as**`) → this rules stage; `AgentLike` carries the
|
|
10
|
-
* additive `session.header.agentPreset?` carrier for that explicit stage.
|
|
11
|
-
*
|
|
12
|
-
* Precedence (first hit wins):
|
|
13
|
-
* 1. the first `rules` entry whose specified provider/model patterns match
|
|
14
|
-
* the agent — rules are SUBAGENT-ONLY (PR #62 feedback): a root-origin
|
|
15
|
-
* agent never matches rules and resolves straight to the built-in
|
|
16
|
-
* `'inherit'` (→ `rootChain`). The legacy per-rule `origin` field (a
|
|
17
|
-
* pre-feedback config may still carry `origin: root|subagent`) is
|
|
18
|
-
* IGNORED at match time — every rule applies to subagents regardless
|
|
19
|
-
* of the stored origin value;
|
|
20
|
-
* 2. the built-in `'inherit'` role (no-rule-match default, spec §7.1 / D4).
|
|
21
|
-
*
|
|
22
|
-
* A matched rule must target a declared role id (`roleIds`, derived from
|
|
23
|
-
* `config.roles.list`) or the built-in `'inherit'` — an undeclared target
|
|
24
|
-
* warns and falls back to `'inherit'` (defensive; startup validation already
|
|
25
|
-
* flagged the reference, spec §7.1 / AC-4).
|
|
26
|
-
*
|
|
27
|
-
* Role ids are canonicalized by trim (qc2 F-001, client-canonical trim
|
|
28
|
-
* alignment — `validateFallbacksConfig` trims ids and rule references, so
|
|
29
|
-
* the runtime must too): `roleIds` maps a TRIMMED id to the DECLARED RAW
|
|
30
|
-
* id, membership compares `rule.role.trim()`, and a matched rule returns
|
|
31
|
-
* the declared raw id — so roleDef lookups in `chains.ts` / `commands.ts`
|
|
32
|
-
* match the stored `roles.list[].id` exactly and a padded YAML id
|
|
33
|
-
* (`' coder '`) with a trimmed rule reference (`'coder'`) resolves to the
|
|
34
|
-
* same role instead of silently degrading to `'inherit'`.
|
|
35
|
-
*
|
|
36
|
-
* A missing agent origin is treated as `'root'` — and root agents never
|
|
37
|
-
* match rules (rules are subagent-only, PR #62 feedback). Origin is read
|
|
38
|
-
* from `session.header.origin` — a native `SessionHeader` field the store folds
|
|
39
|
-
* from `CreateSessionOptions.meta.origin` (`packages/core/session/src/
|
|
40
|
-
* index.ts:884`); subagent children set it via `childSessionMeta`
|
|
41
|
-
* (`packages/subagent/subagent/src/child-agent.ts:115`), root agents carry
|
|
42
|
-
* none. A subagent `persona` is NOT readable at the decision point:
|
|
43
|
-
* `AgentOptions` is provider/model/maxTokens only, and persona is installed
|
|
44
|
-
* as a scoped system-prompt section in the child's creation window — see
|
|
45
|
-
* `.mstar/iterations/iter-20260811-fallbacks-mount-only/guides/
|
|
46
|
-
* role-and-model-selection-exploration.md` (Role section).
|
|
47
|
-
*
|
|
48
|
-
* @module dsh-llm-fallbacks/roles
|
|
49
|
-
*/
|
|
50
|
-
import { type FallbacksRoleRule } from './config.ts';
|
|
51
|
-
export type { FallbacksRoleRule } from './config.ts';
|
|
52
|
-
/** Agent origins (spec §3) — an agent property, not a rule constraint:
|
|
53
|
-
* rules are subagent-only (PR #62 feedback). */
|
|
54
|
-
export type Origin = 'root' | 'subagent';
|
|
55
|
-
/** Loose agent shape sufficient for role resolution (spec §3 / brief). */
|
|
56
|
-
export interface AgentLike {
|
|
57
|
-
options?: {
|
|
58
|
-
provider?: string;
|
|
59
|
-
model?: string;
|
|
60
|
-
};
|
|
61
|
-
session?: {
|
|
62
|
-
header?: {
|
|
63
|
-
origin?: Origin;
|
|
64
|
-
/** Dispatch-time explicit role carrier (plan fallbacks-role-automatch Task 2). */
|
|
65
|
-
agentPreset?: string;
|
|
66
|
-
};
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Resolve the fallback-chain role for an agent: first matching rule (in
|
|
71
|
-
* listed order) → the built-in `'inherit'` role. A rule targeting an id
|
|
72
|
-
* outside the declared-id set ∪ {'inherit'} warns once and resolves to
|
|
73
|
-
* `'inherit'` (the defensive path — `validateFallbacksConfig` already
|
|
74
|
-
* warned at startup, spec §7.1).
|
|
75
|
-
*
|
|
76
|
-
* `roleIds` is the canonical trimmed-id map built by the wiring
|
|
77
|
-
* (`trimmed id → declared raw id`, see `src/index.ts` apply()): a matched
|
|
78
|
-
* rule returns the declared RAW id so the roleDef lookups in
|
|
79
|
-
* `chains.ts` / `commands.ts` find the stored `roles.list[]` entry exactly
|
|
80
|
-
* (qc2 F-001 — no silent rule inertness on padded YAML ids).
|
|
81
|
-
*
|
|
82
|
-
* `warn` defaults to `console.warn` (direct-call compatibility); the
|
|
83
|
-
* decision path injects the cordis `logger.warn` so every plugin warning
|
|
84
|
-
* flows through the plugin log namespace (qc2 F-002 / qc3 S-3).
|
|
85
|
-
*/
|
|
86
|
-
export declare function resolveRole(agent: AgentLike, rules: FallbacksRoleRule[], roleIds: ReadonlyMap<string, string>, warn?: (message: string) => void, servedRoute?: {
|
|
87
|
-
provider: string;
|
|
88
|
-
model: string;
|
|
89
|
-
}): string;
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Route identity and allowlist intersection on resolved candidates (plan
|
|
3
|
-
* dsh-012-subagent-routing T2; spec D1 ∩ D2).
|
|
4
|
-
*
|
|
5
|
-
* Pure, host-free module: plain route data in, results out. Route identity is
|
|
6
|
-
* the EXACT provider string plus the EXACT model string — no aliasing, no
|
|
7
|
-
* case-folding, no catalog-id substitution (spec Terms). Intersections run on
|
|
8
|
-
* RESOLVED candidates (concrete provider+model after the chain candidate
|
|
9
|
-
* pipeline); this module never resolves chains or expands wildcards — the
|
|
10
|
-
* caller passes already-resolved candidates in.
|
|
11
|
-
*
|
|
12
|
-
* Fail-closed direction: an empty allowlist contains nothing and an empty
|
|
13
|
-
* intersection yields `undefined` — the caller must skip the
|
|
14
|
-
* plugin-originated route rather than fall back to an unproven one (spec D1).
|
|
15
|
-
* Never throws.
|
|
16
|
-
*
|
|
17
|
-
* @module dsh-llm-fallbacks/route-allowlist
|
|
18
|
-
*/
|
|
19
|
-
import type { PolicyRoute } from './subagent-policy.ts';
|
|
20
|
-
/** One resolved chain candidate: a selector anchored to a concrete model id. */
|
|
21
|
-
export type ResolvedCandidate = {
|
|
22
|
-
readonly provider: string;
|
|
23
|
-
readonly model?: string;
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* Normalize resolved chain candidates into detached plain routes. Candidates
|
|
27
|
-
* without a concrete model id are dropped (they are not routes); every
|
|
28
|
-
* surviving entry is a fresh object, so the result never aliases the
|
|
29
|
-
* caller's candidate list.
|
|
30
|
-
*/
|
|
31
|
-
export declare function resolvedRoutes(candidates: readonly ResolvedCandidate[]): PolicyRoute[];
|
|
32
|
-
/**
|
|
33
|
-
* Whether the route is on the allowlist under exact route identity: both the
|
|
34
|
-
* provider and the model strings must equal an allowed route exactly.
|
|
35
|
-
*/
|
|
36
|
-
export declare function routeOnAllowlist(route: PolicyRoute, allowedModels: readonly PolicyRoute[]): boolean;
|
|
37
|
-
/**
|
|
38
|
-
* The first resolved candidate that is on the allowlist, preserving the
|
|
39
|
-
* candidate order (the chain's first in-allowlist entry wins), or `undefined`
|
|
40
|
-
* when the intersection is empty — the caller then skips the inject / switch
|
|
41
|
-
* and the host seed stands (spec D1 ∩ D2).
|
|
42
|
-
*/
|
|
43
|
-
export declare function firstAllowedCandidate(resolved: readonly PolicyRoute[], allowedModels: readonly PolicyRoute[]): PolicyRoute | undefined;
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Subagent routing policy reader (plan dsh-012-subagent-routing T1; spec D1).
|
|
3
|
-
*
|
|
4
|
-
* Pure, host-free module: plain data in, results out. It resolves the
|
|
5
|
-
* effective subagent model-selection policy from two snapshots the wiring
|
|
6
|
-
* layer extracts from the host:
|
|
7
|
-
*
|
|
8
|
-
* 1. the session's `subagent/model-selection-policy` event payload, read
|
|
9
|
-
* STRUCTURALLY — the upstream reader is not published from
|
|
10
|
-
* `@deepseek-ai/dsh-tool-subagent` and throws on malformed payloads,
|
|
11
|
-
* which is incompatible with the fail-closed no-throw requirement;
|
|
12
|
-
* 2. the host's live subagent model-selection settings value (the
|
|
13
|
-
* `subagent-model-selection-settings` settings namespace), typed via a
|
|
14
|
-
* type-only peer import that is erased at emit. rc.1 made the settings
|
|
15
|
-
* service's own snapshot reader private, so the wiring reads the settings
|
|
16
|
-
* form value and narrows it structurally here.
|
|
17
|
-
*
|
|
18
|
-
* The session event wins over settings (ADR read order). Anything present but
|
|
19
|
-
* unreadable yields `'unprovable'` (fail-closed): callers must not emit a
|
|
20
|
-
* plugin-originated route they cannot prove is allowed. This module never
|
|
21
|
-
* throws.
|
|
22
|
-
*
|
|
23
|
-
* @module dsh-llm-fallbacks/subagent-policy
|
|
24
|
-
*/
|
|
25
|
-
import type { SubagentModelSelectionSettings } from '@deepseek-ai/dsh-tool-subagent/model-selection-settings';
|
|
26
|
-
declare module '@deepseek-ai/dsh-session/types' {
|
|
27
|
-
interface SessionEventMap {
|
|
28
|
-
/**
|
|
29
|
-
* Host-recorded route policy for a model-selectable subagent session.
|
|
30
|
-
* The payload shape mirrors upstream exactly, so a future published
|
|
31
|
-
* upstream augmentation merges identically. Read structurally by this
|
|
32
|
-
* plugin; never written here.
|
|
33
|
-
*/
|
|
34
|
-
'subagent/model-selection-policy': {
|
|
35
|
-
/** Exact routes this session may select explicitly for a child. */
|
|
36
|
-
allowedModels: {
|
|
37
|
-
provider: string;
|
|
38
|
-
model: string;
|
|
39
|
-
}[];
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
/** One exact child LLM route (structural twin of upstream `AllowedModelRoute`). */
|
|
44
|
-
export type PolicyRoute = {
|
|
45
|
-
readonly provider: string;
|
|
46
|
-
readonly model: string;
|
|
47
|
-
};
|
|
48
|
-
/**
|
|
49
|
-
* Effective subagent routing policy for one session.
|
|
50
|
-
*
|
|
51
|
-
* - `disabled`: host policy off or absent — selection matches pre-policy
|
|
52
|
-
* behavior.
|
|
53
|
-
* - `enabled`: an allowlist is proven; plugin-originated routes must stay
|
|
54
|
-
* inside it.
|
|
55
|
-
* - `unprovable`: the policy is on but unreadable (malformed event payload,
|
|
56
|
-
* or enabled settings with a malformed route list) — fail-closed; no
|
|
57
|
-
* plugin-originated route may be emitted.
|
|
58
|
-
*/
|
|
59
|
-
export type EffectivePolicy = {
|
|
60
|
-
readonly state: 'disabled';
|
|
61
|
-
} | {
|
|
62
|
-
readonly state: 'enabled';
|
|
63
|
-
readonly allowedModels: readonly PolicyRoute[];
|
|
64
|
-
} | {
|
|
65
|
-
readonly state: 'unprovable';
|
|
66
|
-
};
|
|
67
|
-
/** Outcome of the structural session-event read. */
|
|
68
|
-
export type SessionPolicyEventRead = {
|
|
69
|
-
readonly ok: true;
|
|
70
|
-
readonly allowedModels: PolicyRoute[];
|
|
71
|
-
} | {
|
|
72
|
-
readonly ok: false;
|
|
73
|
-
readonly present: true;
|
|
74
|
-
} | {
|
|
75
|
-
readonly ok: false;
|
|
76
|
-
readonly present: false;
|
|
77
|
-
};
|
|
78
|
-
/** Settings snapshot consumed by the reader — plain structural data, no host objects. */
|
|
79
|
-
export type PolicySettings = {
|
|
80
|
-
readonly enabled: boolean;
|
|
81
|
-
readonly allowedModels: readonly PolicyRoute[];
|
|
82
|
-
};
|
|
83
|
-
/** Assignability guard: fails typecheck if the peer's settings shape drifts from the reader's snapshot contract. */
|
|
84
|
-
type AssertSettingsShape<T extends PolicySettings> = T;
|
|
85
|
-
/**
|
|
86
|
-
* The host settings value's shape, re-exported from the published peer
|
|
87
|
-
* subpath (type-only). Wiring passes the settings form's namespace value in as
|
|
88
|
-
* plain data; if upstream drifts, this alias fails typecheck instead of
|
|
89
|
-
* drifting silently at runtime (plan ADR).
|
|
90
|
-
*/
|
|
91
|
-
export type PeerSettingsSnapshot = AssertSettingsShape<SubagentModelSelectionSettings>;
|
|
92
|
-
/**
|
|
93
|
-
* Narrow a host settings value into the reader's snapshot contract.
|
|
94
|
-
* Reads STRUCTURALLY like {@link readSessionPolicyEvent} — rc.1 publishes no
|
|
95
|
-
* settings value reader — so a malformed value is reported, never raised; the
|
|
96
|
-
* wiring decides its failure face (warn + fail-closed).
|
|
97
|
-
*
|
|
98
|
-
* @param value - untrusted settings-form value for the policy namespace.
|
|
99
|
-
* @returns a detached snapshot, or undefined when the value is not one.
|
|
100
|
-
*/
|
|
101
|
-
export declare function policySettingsFrom(value: unknown): PolicySettings | undefined;
|
|
102
|
-
/**
|
|
103
|
-
* Structurally read the session's `subagent/model-selection-policy` event.
|
|
104
|
-
* Never throws: a present-but-malformed payload is reported, not raised.
|
|
105
|
-
*
|
|
106
|
-
* Recency (qc fix wave S-asym — deliberate asymmetry, both sides mirror
|
|
107
|
-
* upstream): this read takes the FIRST occurrence, exactly like the upstream
|
|
108
|
-
* reader's `.find` — the host writes the policy event at most once per
|
|
109
|
-
* session (its ensure-guard checks the reader before appending), so a second
|
|
110
|
-
* occurrence would already be off-contract. The re-emittable
|
|
111
|
-
* `model/selection` log is the opposite: `authorized-route.ts`
|
|
112
|
-
* `lastModelSelection` scans backwards because upstream's selection
|
|
113
|
-
* projection folds to the newest entry.
|
|
114
|
-
*
|
|
115
|
-
* @param events - plain event descriptors (`type` + optional `data`) captured
|
|
116
|
-
* from the session by the wiring layer.
|
|
117
|
-
* @returns the detached route list when the event is present and valid;
|
|
118
|
-
* otherwise a presence flag distinguishing "no policy event" (pure
|
|
119
|
-
* inheritance path) from "policy event unreadable" (fail-closed).
|
|
120
|
-
*/
|
|
121
|
-
export declare function readSessionPolicyEvent(events: readonly {
|
|
122
|
-
type: string;
|
|
123
|
-
data?: unknown;
|
|
124
|
-
}[]): SessionPolicyEventRead;
|
|
125
|
-
/**
|
|
126
|
-
* Resolve the effective policy from the session-event read and the settings
|
|
127
|
-
* snapshot. The session event wins over settings (ADR read order).
|
|
128
|
-
* Fail-closed: a present-but-malformed event, or enabled settings with a
|
|
129
|
-
* malformed route list, yields `'unprovable'`. Never throws.
|
|
130
|
-
*
|
|
131
|
-
* @param event - result of {@link readSessionPolicyEvent}.
|
|
132
|
-
* @param settings - host settings value narrowed by
|
|
133
|
-
* {@link policySettingsFrom}, or undefined when the host composes no such
|
|
134
|
-
* settings entry.
|
|
135
|
-
*/
|
|
136
|
-
export declare function effectivePolicy(event: SessionPolicyEventRead, settings: PolicySettings | undefined): EffectivePolicy;
|
|
137
|
-
export {};
|