dsh-llm-fallbacks 0.5.0 → 0.5.2
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 +5 -1
- package/README.zh-CN.md +5 -1
- package/dist/client/SubagentRoleBadge.d.ts +40 -15
- package/dist/client/fallbacks-store.d.ts +0 -53
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +68 -145
- package/dist/gateway.d.ts +2 -55
- package/dist/index.d.ts +0 -23
- package/dist/index.js +557 -66
- package/dist/role-notice.d.ts +156 -0
- package/dist/role-projection-key.d.ts +41 -0
- package/dist/role-projection.d.ts +151 -0
- package/dist/subagents-seam.d.ts +292 -0
- package/package.json +24 -23
- package/scripts/build-client.ts +1 -1
- package/scripts/repair-session-logs.ts +239 -57
- package/scripts/session-logs/publish.ts +79 -26
package/README.md
CHANGED
|
@@ -161,6 +161,8 @@ The original generation is never modified and never truncated; rollback is delet
|
|
|
161
161
|
|
|
162
162
|
**`--apply` refuses a revision it did not decode.** The digest of the bytes this run read is compared with the file before anything is staged, so a concurrent append is refused before the first write. If the source moves *after* a publication this run created, that successor is removed again; if it moves after accepting a pre-existing identical successor, the failure names that file and says to delete it — never "nothing was published".
|
|
163
163
|
|
|
164
|
+
**A closed report stream is not a store event.** Piping the report into a consumer that stops reading (`pnpm repair:session-logs -- | head -n 5`) closes stdout, and the next write would otherwise raise an unhandled `EPIPE`. Instead the tool stops writing to that stream (with no stack trace), **still finishes the run** — an `--apply` pass publishes every remaining successor — and exits with the exit code below, which always describes what happened to the **store**, never whether the reader stayed. A stream error that is not a closed pipe (a full disk, an I/O failure) is **not** swallowed: it still surfaces as an uncaught error.
|
|
165
|
+
|
|
164
166
|
**Exit codes**: `0` = every log loads (a session that opens with rows dropped under the strict policy is `ok-truncated` and still exits `0`); `1` = at least one log is still refused/unrepairable, a repair failed, a log was left unpublished (including one `--class` excluded), an input could not be inspected (a `skipped` path), or a stale `session.repair.*.jsonl.zstd.tmp` was found under the root; `2` = fatal (bad arguments, a missing or unreadable `--root`, `--apply` without a resolved catalog, a catalog below format v3, `--apply --drop-legacy-events` without `--backup`, or a runtime without `node:zlib` zstd).
|
|
165
167
|
|
|
166
168
|
### Lossy recovery for `fallbacks/switch` (opt-in)
|
|
@@ -186,7 +188,9 @@ The durable fix belongs upstream at the migration edges (the frozen V0→V1 edge
|
|
|
186
188
|
- **Chain as root primary from the picker**: when `enabled` is on, the host model picker (web and TUI alike) shows a virtual `FallbacksChain` / `Auto` row — selecting it uses the configured chain as the root primary (a conforming all-day head is required for the delegation to succeed); selecting a real model keeps fallback-only (see [FallbacksChain in the model picker](#fallbackschain-in-the-model-picker)).
|
|
187
189
|
- **Time slots**: optional `fallbacks.timeSlots` rows rotate the effective root chain by wall-clock windows in the config-level `tz` timezone (default `Asia/Shanghai`) — four frozen UTC+8 presets (`liang-peak` / `liang-valley` / `glm-peak` / `glm-valley`, windows are code constants, models-only edits) or custom `start`/`end`/`days` windows. The first matching row wins; the all-day row is always last. A slot change applies on the **next** root request and is logged as a **time-slot switch** — a routing seed, never a failure decision: it consumes no cooldown and does not count against `maxSwitchesPerStep`. Failure walks keep the **fallback switch** copy (see [Time-slot presets](#time-slot-presets)).
|
|
188
190
|
- **Dispatch-time role resolution**: on a subagent's first request its role is resolved in three stages — explicit (`agentPreset` matches a declared role id) → deterministic rules (matching either the recorded pair or the served head — see [FallbacksChain in the model picker](#fallbackschain-in-the-model-picker)) → LLM auto-match from the declared role taxonomy (`fallbacks.roleAutoMatch`, default `true`). The resolved role's chain-head model is injected into the first request and recorded via an explicit `role → model` log line (no durable `fallbacks/switch` event is written — issue #52 stop-write); set `roleAutoMatch: false` to disable the LLM auto-match stage (the explicit `agentPreset` stage still applies — with no explicit role this reproduces the previous rules-only behavior). The settings card always renders an **Enable role auto-match** switch (default `true`) to toggle it — the schema default applies even to legacy configs that never declared the key.
|
|
189
|
-
- **
|
|
191
|
+
- **Role persona on the child (chain-independent)**: when a subagent's dispatch declares a role this plugin knows (the `**Execute as**: <id>` field of its Assignment), that role's `persona` is installed on the child as its own persona — the role is the child's identity, not only a routing decision, and delivery does not depend on routing: a role with an empty `chain` gets its persona exactly like a chained one (the chain only picks the model). A persona the caller already set is never overwritten; a provider that cannot carry a persona skips it and the dispatch runs natively.
|
|
192
|
+
- **Role announced inside the subagent's own session**: a child dispatched with a declared role gets exactly one plugin notice row in its own session, naming the role it was dispatched as — `[role: <id>]`, with ` (persona not applied)` when the role declares a persona that was skipped — written once at the child's first non-empty pre-step, never for a root session and never for `inherit`/unresolved dispatches. The row is a durable transcript row, so the role stays readable in the child's session after it settles.
|
|
193
|
+
- **Subagent role badge**: when a subagent is **dispatched with a declared role** — the `**Execute as**: <id>` field of its Assignment, the same declaration that carries the persona, and the badge's ONLY source (policy on or off) — its session shows a compact role badge next to the session's agent-preset (mode) chip in the web session header; hover shows `role → latest request route` (the route of the newest recorded request, not the dispatch-time route). Only the DECLARED header is read, so a child whose role came from another stage (`agentPreset` match, a role rule, or the LLM auto-match) but which declares no Assignment header is still routed as that role yet shows **no** badge — the badge never reports a role inferred after the fact. `inherit`/unresolved sessions render no badge. The badge is durable: it is read from the child's own session record, so it is still shown for a **settled** subagent session and **after a host restart**.
|
|
190
194
|
- **Context-window-aware fallback**: `triggerCodes` accepts any dsh failure code, including `CONTEXT_WINDOW_EXCEEDED` — a request too big for the current model fails over to a candidate with a **larger** context window (candidates that cannot fit either are skipped), and because the route itself is healthy the switch is request-scoped: no cooldown, no half-open probe burned (see [Fallback triggers](#fallback-triggers-triggercodes)).
|
|
191
195
|
- **Cooldown and revert**: failed / switched-away models are not re-selected during cooldown; `revertPolicy: cooldown-expiry` returns to the primary model automatically.
|
|
192
196
|
- **Host subagent model policy (dsh 0.1.2)**: when the host `subagent-model-selection` policy is enabled, its allowlist is a hard constraint on every plugin-originated subagent route — an explicit authorized spawn route stays the chain head (role-inject skipped), inheritance inject heads and failure-switch targets are intersected with the effective allowlist, and an empty intersection skips the inject/switch (warn log + read-only card warning; no out-of-allowlist request is ever sent). A present-but-unreadable policy fails closed. Policy off/absent → inject and failure-switch selection exactly as 0.3.5. Override `reasoningEffort` follows the upstream routeChanged rule on every path (same route → keep; route change → drop unless explicit). See [Host subagent model policy](#host-subagent-model-policy-dsh-012).
|
package/README.zh-CN.md
CHANGED
|
@@ -161,6 +161,8 @@ pnpm repair:session-logs -- --apply # 发布修复后的后继
|
|
|
161
161
|
|
|
162
162
|
**`--apply` 拒绝发布它不是基于其解码的那一版。** 本次运行读取到的字节摘要会在暂存任何内容之前与文件比对,因此并发追加会在首次写入前被拒绝。若源文件在本次运行**创建**的后继发布之后发生变化,该后继会被移除;若是在接受一个已存在的相同后继之后发生变化,失败信息会指出该文件并说明需删除它以回滚——绝不会说 “nothing was published”。
|
|
163
163
|
|
|
164
|
+
**报告流被关闭不是存储事件。** 把报告接到提前停止读取的消费者(`pnpm repair:session-logs -- | head -n 5`)会关闭 stdout,此后的写入本会抛出未处理的 `EPIPE`。现在工具会停止向该流写入(不打印堆栈),**仍把整轮运行跑完**——`--apply` 会发布其余每一个后继世代——并以下面的退出码退出;该退出码始终描述**存储**发生了什么,而与读取方是否留下无关。非关闭管道造成的流错误(磁盘写满、I/O 失败)**不会**被吞掉:它仍会以未捕获错误暴露。
|
|
165
|
+
|
|
164
166
|
**退出码**:`0` = 每个日志都能加载(在严格策略下会丢行打开的会话报告为 `ok-truncated`,仍以 `0` 退出);`1` = 至少一个日志仍被拒绝/不可修复、修复失败、有日志未发布(包括被 `--class` 排除的)、某个输入无法被检查(`skipped` 路径),或发现残留的 `session.repair.*.jsonl.zstd.tmp`;`2` = 致命错误(参数非法、缺少或无法读取 `--root`、`--apply` 未解析出 catalog、catalog 低于格式 v3、`--apply --drop-legacy-events` 缺少 `--backup`,或运行时没有 `node:zlib` zstd)。
|
|
165
167
|
|
|
166
168
|
### fallbacks/switch 的有损恢复(opt-in)
|
|
@@ -186,7 +188,9 @@ pnpm repair:session-logs -- --drop-legacy-events --apply --backup # 此处必
|
|
|
186
188
|
- **选择器里把链当主模型**:`enabled` 开启时,宿主模型选择器(web 与 TUI 一致)出现虚拟 `FallbacksChain` / `Auto` 行——选中它即以配置的链作为 root 主模型(需要 all-day 链头合规才能成功委托);选真实模型则保持 fallback-only(见 [模型选择器中的 FallbacksChain](#模型选择器中的-fallbackschain))。
|
|
187
189
|
- **峰谷无忧(分时切换)**:可选的 `fallbacks.timeSlots` 行按墙钟窗口(配置级 `tz` 时区,默认 `Asia/Shanghai`)轮换 root 生效链——四个冻结的 UTC+8 预设(`liang-peak` / `liang-valley` / `glm-peak` / `glm-valley`,窗口为代码常量、仅模型链可编辑),或自定义 `start`/`end`/`days` 窗口。第一条命中的行生效;全时段行固定最后。时段切换在**下一个** root 请求生效,日志记为**分时切换**——路由种子而非失败决策:不消耗冷却、不计入 `maxSwitchesPerStep`。失败降级保留**降级切换**文案(见 [分时槽预设(分时切换)](#分时槽预设分时切换))。
|
|
188
190
|
- **派发时角色解析**:在 subagent 的首次请求上,其角色按三个阶段解析——显式(`agentPreset` 匹配已声明角色 id)→ 确定性规则(匹配记录 pair 或实际服务的链头——见 [模型选择器中的 FallbacksChain](#模型选择器中的-fallbackschain))→ LLM 自动匹配(从已声明角色体系中选择,`fallbacks.roleAutoMatch` 默认 `true`)。解析出的角色的链头模型注入首次请求,并以显式 `role → model` 日志行记录(不写 durable `fallbacks/switch` 事件——issue #52 停写);设 `roleAutoMatch: false` 仅关闭 LLM 自动匹配阶段(显式 `agentPreset` 阶段仍生效——无显式角色时即复现原有仅规则行为)。设置卡总是渲染「启用角色自动匹配」开关(默认 `true`)以切换之——即使是从未声明过该键的旧配置,schema 默认值同样生效。
|
|
189
|
-
-
|
|
191
|
+
- **角色人格注入到子代理(与链无关)**:当 subagent 的派发声明了本插件已知的角色(Assignment 的 `**Execute as**: <id>` 字段)时,该角色的 `persona` 会安装为子代理自身的人格——角色就是子代理的身份,而不只是路由决策;注入与路由无关:`chain` 为空的角色与带链角色的人格注入完全一致(链只负责选模型)。调用方已设置的人格永不覆盖;宿主 provider 无法承载人格时跳过注入,派发照常原生运行。
|
|
192
|
+
- **角色在子代理自己的会话中声明**:声明了角色的派发会让子代理自身的会话得到恰好一条插件 notice 行,写明它被派发为哪个角色——`[role: <id>]`,当角色声明了人格但被跳过时追加 ` (persona not applied)`——在子代理首个非空 pre-step 写入一次,root 会话以及 `inherit`/未解析角色的派发永不写入。该行是持久轨迹行,子代理结束后角色仍可在其会话中读到。
|
|
193
|
+
- **子代理角色徽标**:当 subagent 被**声明了角色地派发**——即其 Assignment 中的 `**Execute as**: <id>` 字段,也就是承载人格的同一处声明,并且是徽标的**唯一**来源(策略开或关皆同)——时,其会话在 Web 会话头部的标题旁显示一个紧凑的角色徽标;悬停显示 `role → 最近一次请求的路由`(最近一条已记录请求的路由,而非派发时的路由)。由于只读取被声明的头部字段,通过其他阶段解析出角色(`agentPreset` 匹配、role 规则或 LLM 自动匹配)但**未**在 Assignment 头部声明角色的子代理仍会按该角色路由,却**不**显示徽标——徽标绝不报告事后推断的角色。`inherit`/未解析角色的会话不显示徽标。徽标是持久的:它读取子代理自身的会话记录,因此**已结束的子代理会话**与**宿主重启之后**仍然显示。
|
|
190
194
|
- **上下文窗口感知降级**:`triggerCodes` 接受任意 dsh 失败码,包括 `CONTEXT_WINDOW_EXCEEDED`——请求超出当前模型上下文时,降级到上下文窗口**更大**的候选(装不下的候选被跳过);由于路由本身健康,该切换是请求级的:不冷却、不浪费半开探针(见 [降级触发码](#降级触发码triggercodes))。
|
|
191
195
|
- **冷却与回主**:被切离/失败的模型在冷却期内不再入选;`revertPolicy: cooldown-expiry` 冷却到期后自动回主模型。
|
|
192
196
|
- **宿主子代理模型策略(dsh 0.1.2)**:当宿主 `subagent-model-selection` 策略启用时,其允许列表对每个插件发起的 subagent 路由都是硬约束——显式授权的派发路由保持为链头(跳过角色注入),继承注入的链头与失败切换目标都与生效允许列表求交集,交集为空则跳过注入/切换(warn 日志 + 只读卡片警告;绝不发送允许列表之外的请求)。策略存在但不可读时 fail-closed。策略关闭/缺省时,注入与失败切换的选择与 0.3.5 完全一致。覆盖路径上的 `reasoningEffort` 遵循上游 routeChanged 规则(同路由 → 保留;跨路由 → 除非显式指定否则丢弃)。见 [宿主子代理模型策略](#宿主子代理模型策略dsh-012)。
|
|
@@ -1,22 +1,47 @@
|
|
|
1
|
-
import type { ReactNode } from 'react';
|
|
2
|
-
import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
3
|
-
import type { FallbacksSettingsController } from './fallbacks-store.ts';
|
|
4
1
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
2
|
+
* Session-header subagent role badge (plan subagent-role-badge T3; durable
|
|
3
|
+
* channel plan role-based-subagent-adoption Task 3b): a compact read-only pill
|
|
4
|
+
* in `conversation.session.header.actions` showing which Subagent role the
|
|
5
|
+
* viewed session's dispatch resolved, hovering as `role → latest request route`.
|
|
6
|
+
*
|
|
7
|
+
* Contract notes (verified 2026-09-09 against the harness checkout):
|
|
8
|
+
* - The slot is `kind: 'list'`, `scope: 'session'`, owner props an empty marker
|
|
9
|
+
* (`ConversationHeaderActionOwnerProps`, `children?: never`) — the header
|
|
10
|
+
* renders occupants with `renderSlot(..., {})`, so ALL data flows through the
|
|
11
|
+
* session standard kit.
|
|
12
|
+
* - Every `scope: 'session'` occupant receives the session standard kit
|
|
13
|
+
* (`useSession` + `useProjection`; ui-session `src/client/index.ts:112-119`,
|
|
14
|
+
* merged into the runtime share by ui-slots' `PropsRuntime`). The compiled
|
|
15
|
+
* peer types do not carry that merge into this plugin's typecheck program
|
|
16
|
+
* (ui-session is deliberately not a plugin peer), so both seats are read
|
|
17
|
+
* STRUCTURALLY and guarded — a version-skewed host without them renders
|
|
18
|
+
* nothing, never throws.
|
|
19
|
+
* - Data: the host's `fallbacksSubagentRole` projection (Task 3b), folded
|
|
20
|
+
* server-side out of the child's own durable log — the ONE role source. The
|
|
21
|
+
* session kit binds the hook to the VIEWED session (`binding.session.
|
|
22
|
+
* projections.faceOf(key)`), so no `sessionId` plumbing, no effect, no state,
|
|
23
|
+
* and no polling: a settled child's follow opening snapshot already carries
|
|
24
|
+
* the value, and a running child's change frame updates the pill. An absent
|
|
25
|
+
* key (host without the unit, or no notice row) reads as "no pill".
|
|
26
|
+
* - Hook order is render-independent (Task 3b L2 review C-1): the seat is a real
|
|
27
|
+
* hook, so BOTH projection reads are unconditional and only their derived
|
|
28
|
+
* values are guarded — a conditional second read would change the hook count
|
|
29
|
+
* on the very re-render that publishes a running child's role.
|
|
30
|
+
* - The hover route comes from the host's EXISTING `modelSelection` projection
|
|
31
|
+
* (`lastUsed` — the route of the LATEST recorded request), NOT from the
|
|
32
|
+
* dispatch-time route: it is a separate, richer fact, and the label says so.
|
|
33
|
+
* - Render-only discipline (C4 pattern, same as `ConversationFallbackSwitch`):
|
|
34
|
+
* the badge contributes a view; no message construction, no model-context
|
|
35
|
+
* injection. Degrade-never-crash: a missing/foreign projection value or an
|
|
36
|
+
* `inherit` role renders `null` — the actions row collapses cleanly.
|
|
10
37
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
/** Props delivered by the utilities slot outlet: runtime share + locale seat + inject face. */
|
|
16
|
-
export type SubagentRoleBadgeProps = PropsRuntime<'conversation.session.header.utilities'> & PropsLocale<'fallbacks'> & SubagentRoleBadgeInjected;
|
|
38
|
+
import type { ReactNode } from 'react';
|
|
39
|
+
import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
40
|
+
/** Props delivered by the actions slot outlet: runtime share + locale seat. */
|
|
41
|
+
export type SubagentRoleBadgeProps = PropsRuntime<'conversation.session.header.actions'> & PropsLocale<'fallbacks'>;
|
|
17
42
|
/**
|
|
18
43
|
* Render the viewed session's dispatch-resolved role badge.
|
|
19
|
-
* @param props - composed slot props (the
|
|
44
|
+
* @param props - composed slot props (the session-kit `useProjection` seat is
|
|
20
45
|
* read structurally — see the module docblock).
|
|
21
46
|
* @returns the badge element, or `null` when there is nothing to show.
|
|
22
47
|
*/
|
|
@@ -169,47 +169,6 @@ export type SubagentPolicyView = {
|
|
|
169
169
|
* throws on an old or misshapen field (old-payload tolerance).
|
|
170
170
|
*/
|
|
171
171
|
export declare function parseSubagentPolicyWire(value: unknown): SubagentPolicyView | undefined;
|
|
172
|
-
/**
|
|
173
|
-
* One dispatch-resolved subagent role record as the session-header badge
|
|
174
|
-
* reads it (plan subagent-role-badge T3) — a structural twin of the gateway
|
|
175
|
-
* wire type, parsed from unknown, never imported from host modules
|
|
176
|
-
* (`src/client/**` consumes the gateway channel only — the
|
|
177
|
-
* `SubagentPolicyView` precedent). `model` is optional on the wire (gateway
|
|
178
|
-
* version skew); the writer always sets it.
|
|
179
|
-
*/
|
|
180
|
-
export interface SubagentRoleView {
|
|
181
|
-
/** The dispatch-resolved role id (never `inherit` — the writer skips it). */
|
|
182
|
-
role: string;
|
|
183
|
-
/** The route the subagent actually runs (override target, else the seed). */
|
|
184
|
-
model?: {
|
|
185
|
-
provider: string;
|
|
186
|
-
model: string;
|
|
187
|
-
};
|
|
188
|
-
/** Record time, Unix epoch milliseconds. */
|
|
189
|
-
at: number;
|
|
190
|
-
}
|
|
191
|
-
/**
|
|
192
|
-
* Shape-guard one wire role record (`fallbacks/subagent-roles` readback).
|
|
193
|
-
* A non-object, a blank/non-string `role`, or a non-number `at` fails the
|
|
194
|
-
* WHOLE record (`undefined` → the badge renders nothing). A present-but-
|
|
195
|
-
* malformed `model` drops the FIELD while the record still parses (the
|
|
196
|
-
* `parseSeedsWire` `source` rule) — the badge then hovers the role alone
|
|
197
|
-
* instead of disappearing. Records are NORMALIZED to the known keys, so a
|
|
198
|
-
* wire record can never smuggle a non-string `model` past the guard's type.
|
|
199
|
-
*/
|
|
200
|
-
export declare function parseSubagentRoleRecord(value: unknown): SubagentRoleView | undefined;
|
|
201
|
-
/**
|
|
202
|
-
* Read this session's dispatch-resolved role record through the plugin
|
|
203
|
-
* gateway channel (plan subagent-role-badge T3): the batch-of-ids endpoint
|
|
204
|
-
* called with the one id the badge cares about. NEVER throws — a channel
|
|
205
|
-
* down, an old gateway without the endpoint (rpc failure), or a malformed
|
|
206
|
-
* payload all resolve `undefined` (the badge renders nothing —
|
|
207
|
-
* degrade-never-crash, the fail-closed shape of `parseSubagentPolicyWire`).
|
|
208
|
-
* @param rpc - the connection's generic RPC caller (`/api` channel).
|
|
209
|
-
* @param sessionId - the session whose record to read.
|
|
210
|
-
* @returns the parsed record, or `undefined` when absent/failed.
|
|
211
|
-
*/
|
|
212
|
-
export declare function fetchSubagentRoleRecord(rpc: ClientConnectionRpc, sessionId: string): Promise<SubagentRoleView | undefined>;
|
|
213
172
|
/**
|
|
214
173
|
* The provider dropdown's offer set (spec §2.5 D-4): catalog providers whose
|
|
215
174
|
* settings profile resolves in the describe namespaces — the Models page's
|
|
@@ -548,18 +507,6 @@ export declare class FallbacksSettingsController {
|
|
|
548
507
|
* merge cannot express). Same error handling as {@link save} (KD-G3).
|
|
549
508
|
*/
|
|
550
509
|
resetToDefaults(): Promise<void>;
|
|
551
|
-
/**
|
|
552
|
-
* Read one session's dispatch-resolved subagent role record through the
|
|
553
|
-
* gateway channel (plan subagent-role-badge T3 — the session-header badge's
|
|
554
|
-
* data face). Thin delegate over {@link fetchSubagentRoleRecord} on the
|
|
555
|
-
* SAME rpc caller the store rides: the batch-of-ids readback called with
|
|
556
|
-
* the one id the badge cares about. Never throws — a channel down, an old
|
|
557
|
-
* gateway without the endpoint, or a malformed payload all resolve
|
|
558
|
-
* `undefined` (the badge renders nothing).
|
|
559
|
-
* @param sessionId - the session whose record to read.
|
|
560
|
-
* @returns the parsed record, or `undefined` when absent/failed.
|
|
561
|
-
*/
|
|
562
|
-
fetchSubagentRole(sessionId: string): Promise<SubagentRoleView | undefined>;
|
|
563
510
|
/** Stop in-flight responses from publishing after plugin disposal. */
|
|
564
511
|
dispose(): void;
|
|
565
512
|
/**
|
package/dist/client/index.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ import type { Context as ClientContext } from '@deepseek-ai/cordis';
|
|
|
36
36
|
export type { FallbacksCardInjected, FallbacksCardProps } from './FallbacksCard.tsx';
|
|
37
37
|
export type { GeneralFallbacksRowInjected, GeneralFallbacksRowProps } from './GeneralFallbacksRow.tsx';
|
|
38
38
|
export type { ConversationFallbackSwitchProps, FallbacksSwitchChatData, } from './ConversationFallbackSwitch.tsx';
|
|
39
|
-
export type {
|
|
39
|
+
export type { SubagentRoleBadgeProps } from './SubagentRoleBadge.tsx';
|
|
40
40
|
export type { FallbacksSettingsState } from './fallbacks-store.ts';
|
|
41
41
|
export { FallbacksSettingsController, FALLBACKS_SETTINGS_NS } from './fallbacks-store.ts';
|
|
42
42
|
/**
|
package/dist/client/index.js
CHANGED
|
@@ -610,48 +610,6 @@ window.__ModuleLoader__.load({
|
|
|
610
610
|
return parseSubagentPolicyWire(value.subagentPolicy);
|
|
611
611
|
}
|
|
612
612
|
/**
|
|
613
|
-
* Shape-guard one wire role record (`fallbacks/subagent-roles` readback).
|
|
614
|
-
* A non-object, a blank/non-string `role`, or a non-number `at` fails the
|
|
615
|
-
* WHOLE record (`undefined` → the badge renders nothing). A present-but-
|
|
616
|
-
* malformed `model` drops the FIELD while the record still parses (the
|
|
617
|
-
* `parseSeedsWire` `source` rule) — the badge then hovers the role alone
|
|
618
|
-
* instead of disappearing. Records are NORMALIZED to the known keys, so a
|
|
619
|
-
* wire record can never smuggle a non-string `model` past the guard's type.
|
|
620
|
-
*/
|
|
621
|
-
function parseSubagentRoleRecord(value) {
|
|
622
|
-
if (!isRecord(value)) return void 0;
|
|
623
|
-
if (typeof value.role !== "string" || value.role.trim() === "") return void 0;
|
|
624
|
-
if (typeof value.at !== "number") return void 0;
|
|
625
|
-
const model = parsePolicyRoute(value.model);
|
|
626
|
-
return {
|
|
627
|
-
role: value.role,
|
|
628
|
-
...model === void 0 ? {} : { model },
|
|
629
|
-
at: value.at
|
|
630
|
-
};
|
|
631
|
-
}
|
|
632
|
-
/**
|
|
633
|
-
* Read this session's dispatch-resolved role record through the plugin
|
|
634
|
-
* gateway channel (plan subagent-role-badge T3): the batch-of-ids endpoint
|
|
635
|
-
* called with the one id the badge cares about. NEVER throws — a channel
|
|
636
|
-
* down, an old gateway without the endpoint (rpc failure), or a malformed
|
|
637
|
-
* payload all resolve `undefined` (the badge renders nothing —
|
|
638
|
-
* degrade-never-crash, the fail-closed shape of `parseSubagentPolicyWire`).
|
|
639
|
-
* @param rpc - the connection's generic RPC caller (`/api` channel).
|
|
640
|
-
* @param sessionId - the session whose record to read.
|
|
641
|
-
* @returns the parsed record, or `undefined` when absent/failed.
|
|
642
|
-
*/
|
|
643
|
-
async function fetchSubagentRoleRecord(rpc, sessionId) {
|
|
644
|
-
try {
|
|
645
|
-
const result = await rpc.call("/api", "fallbacks/subagent-roles", { args: { ids: [sessionId] } });
|
|
646
|
-
if (!result.ok) return void 0;
|
|
647
|
-
const value = result.value;
|
|
648
|
-
if (!isRecord(value)) return void 0;
|
|
649
|
-
return parseSubagentRoleRecord(value[sessionId]);
|
|
650
|
-
} catch {
|
|
651
|
-
return;
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
/**
|
|
655
613
|
* The provider dropdown's offer set (spec §2.5 D-4): catalog providers whose
|
|
656
614
|
* settings profile resolves in the describe namespaces — the Models page's
|
|
657
615
|
* `configured` predicate (`ui-models` store.ts): a provider is configured
|
|
@@ -1274,20 +1232,6 @@ window.__ModuleLoader__.load({
|
|
|
1274
1232
|
this.fail(error);
|
|
1275
1233
|
}
|
|
1276
1234
|
}
|
|
1277
|
-
/**
|
|
1278
|
-
* Read one session's dispatch-resolved subagent role record through the
|
|
1279
|
-
* gateway channel (plan subagent-role-badge T3 — the session-header badge's
|
|
1280
|
-
* data face). Thin delegate over {@link fetchSubagentRoleRecord} on the
|
|
1281
|
-
* SAME rpc caller the store rides: the batch-of-ids readback called with
|
|
1282
|
-
* the one id the badge cares about. Never throws — a channel down, an old
|
|
1283
|
-
* gateway without the endpoint, or a malformed payload all resolve
|
|
1284
|
-
* `undefined` (the badge renders nothing).
|
|
1285
|
-
* @param sessionId - the session whose record to read.
|
|
1286
|
-
* @returns the parsed record, or `undefined` when absent/failed.
|
|
1287
|
-
*/
|
|
1288
|
-
fetchSubagentRole(sessionId) {
|
|
1289
|
-
return fetchSubagentRoleRecord(this.rpc, sessionId);
|
|
1290
|
-
}
|
|
1291
1235
|
/** Stop in-flight responses from publishing after plugin disposal. */
|
|
1292
1236
|
dispose() {
|
|
1293
1237
|
this.readGeneration += 1;
|
|
@@ -1562,7 +1506,7 @@ window.__ModuleLoader__.load({
|
|
|
1562
1506
|
"chat.switch.summary": "{from} → {to}({reason})",
|
|
1563
1507
|
"chat.switch.summary.roleInject": "({reason})",
|
|
1564
1508
|
"chat.switch.roleMap": "{role} → {model}",
|
|
1565
|
-
"subagentRole.hover": "{role} → {model}",
|
|
1509
|
+
"subagentRole.hover": "{role} → {model}(最近一次请求的路由)",
|
|
1566
1510
|
"defaults.prefix": "默认值",
|
|
1567
1511
|
"save": "保存",
|
|
1568
1512
|
"save.saving": "保存中…",
|
|
@@ -1759,7 +1703,7 @@ window.__ModuleLoader__.load({
|
|
|
1759
1703
|
"chat.switch.summary": "{from} → {to} ({reason})",
|
|
1760
1704
|
"chat.switch.summary.roleInject": "({reason})",
|
|
1761
1705
|
"chat.switch.roleMap": "{role} → {model}",
|
|
1762
|
-
"subagentRole.hover": "{role} → {model}",
|
|
1706
|
+
"subagentRole.hover": "{role} → {model} (latest request route)",
|
|
1763
1707
|
"defaults.prefix": "Default",
|
|
1764
1708
|
"save": "Save",
|
|
1765
1709
|
"save.saving": "Saving…",
|
|
@@ -4531,6 +4475,40 @@ window.__ModuleLoader__.load({
|
|
|
4531
4475
|
});
|
|
4532
4476
|
}
|
|
4533
4477
|
//#endregion
|
|
4478
|
+
//#region src/role-projection-key.ts
|
|
4479
|
+
/**
|
|
4480
|
+
* The session-projection contract of the subagent role pill (plan
|
|
4481
|
+
* role-based-subagent-adoption Task 3b): the ONE key the host fold
|
|
4482
|
+
* (`./role-projection.ts`) publishes and the session-header badge reads.
|
|
4483
|
+
*
|
|
4484
|
+
* PURE module — no `@deepseek-ai/*` import, no host-only dependency — because
|
|
4485
|
+
* BOTH halves import it: the host through the seam's install point, the client
|
|
4486
|
+
* through `SubagentRoleBadge`. Splitting the key (and the value guard) out of
|
|
4487
|
+
* the host module is what keeps the client half free of the fold's host
|
|
4488
|
+
* imports, and a single shared literal is what keeps the two halves from
|
|
4489
|
+
* drifting into a silent "no pill".
|
|
4490
|
+
*
|
|
4491
|
+
* @module dsh-llm-fallbacks/role-projection-key
|
|
4492
|
+
*/
|
|
4493
|
+
/**
|
|
4494
|
+
* The projection key the role pill reads (`useProjection(ROLE_PROJECTION_KEY)`).
|
|
4495
|
+
* Lower-camel like the host's own session-scoped keys (`modelSelection`,
|
|
4496
|
+
* `sessionListMetadata`); the `fallbacks` prefix keeps a third-party key
|
|
4497
|
+
* unambiguous inside the shared `SessionProjectionMap`.
|
|
4498
|
+
*/
|
|
4499
|
+
const ROLE_PROJECTION_KEY = "fallbacksSubagentRole";
|
|
4500
|
+
/**
|
|
4501
|
+
* Read a projection value defensively (degrade-never-crash): only a non-blank
|
|
4502
|
+
* string is a role. `undefined` — the key is not registered on this host, or no
|
|
4503
|
+
* baseline/frame has carried it yet — and every foreign value read as "no
|
|
4504
|
+
* role", so the pill collapses to nothing instead of naming something wrong.
|
|
4505
|
+
*
|
|
4506
|
+
* @param value - the raw `useProjection('<key>')` value.
|
|
4507
|
+
*/
|
|
4508
|
+
function readRoleProjectionValue(value) {
|
|
4509
|
+
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
4510
|
+
}
|
|
4511
|
+
//#endregion
|
|
4534
4512
|
//#region \0dsh-css:/home/runner/work/dsh-llm-fallbacks/dsh-llm-fallbacks/src/client/SubagentRoleBadge.module.css.mjs
|
|
4535
4513
|
const css = "\n\n._35648278_badge {\n display: inline-block;\n max-width: 120px;\n padding: 0 8px;\n border-radius: 999px;\n font-size: 12px;\n line-height: 20px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n vertical-align: middle;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-primary);\n}\n";
|
|
4536
4514
|
const tagId = "dsh-llm-fallbacks/SubagentRoleBadge.module.css";
|
|
@@ -4545,101 +4523,47 @@ window.__ModuleLoader__.load({
|
|
|
4545
4523
|
//#endregion
|
|
4546
4524
|
//#region src/client/SubagentRoleBadge.tsx
|
|
4547
4525
|
/**
|
|
4548
|
-
*
|
|
4549
|
-
*
|
|
4550
|
-
*
|
|
4551
|
-
*
|
|
4552
|
-
*
|
|
4553
|
-
* Contract notes (verified 2026-09-09 against the harness checkout):
|
|
4554
|
-
* - The slot is `kind: 'list'`, `scope: 'session'`, owner props an empty
|
|
4555
|
-
* marker (`ConversationHeaderActionOwnerProps`, `children?: never`) — the
|
|
4556
|
-
* header renders occupants with `renderSlot(..., {})`, so ALL data flows
|
|
4557
|
-
* through this component's inject face and the session standard kit.
|
|
4558
|
-
* - Every `scope: 'session'` occupant receives the session standard kit
|
|
4559
|
-
* (`sessionId` + `useSession` + `useProjection`; ui-session
|
|
4560
|
-
* `src/client/index.ts:112-119`, merged into the runtime share by ui-slots'
|
|
4561
|
-
* `PropsRuntime`) — the fetch keys off the `sessionId` prop directly (mount
|
|
4562
|
-
* + whenever the prop changes; the slot machinery remounts the occupant per
|
|
4563
|
-
* viewed session — no manual session subscription). The compiled peer types
|
|
4564
|
-
* do not carry the ui-session standard-props merge into this plugin's
|
|
4565
|
-
* typecheck program (that package is deliberately not a plugin peer), so
|
|
4566
|
-
* the seat is read structurally and guarded — a version-skewed host without
|
|
4567
|
-
* it renders nothing, never throws.
|
|
4568
|
-
* - Data: `fallbacks/subagent-roles` gateway readbacks via the injected fetch
|
|
4569
|
-
* face ({@link fetchSubagentRoleRecord} — never throws): one on mount +
|
|
4570
|
-
* whenever the viewed session changes, then a BOUNDED delayed re-probe
|
|
4571
|
-
* while no record has landed (the record is written at the subagent's first
|
|
4572
|
-
* dispatch request, which can land after this header mounted).
|
|
4573
|
-
* - Render-only discipline (C4 pattern, same as `ConversationFallbackSwitch`):
|
|
4574
|
-
* the badge contributes a view; no message construction, no model-context
|
|
4575
|
-
* injection. Degrade-never-crash: a missing record, an `inherit` role, or
|
|
4576
|
-
* ANY readback error renders `null` — the utilities strip collapses cleanly.
|
|
4526
|
+
* The host's own durable model-selection projection key (`dsh-api-session-
|
|
4527
|
+
* controller`, merged into `SessionProjectionMap`): its client value carries
|
|
4528
|
+
* `lastUsed` — the route the LATEST recorded request ran on. Read through the
|
|
4529
|
+
* same structural seat as the role key.
|
|
4577
4530
|
*/
|
|
4531
|
+
const MODEL_SELECTION_PROJECTION_KEY = "modelSelection";
|
|
4578
4532
|
/**
|
|
4579
|
-
*
|
|
4580
|
-
*
|
|
4581
|
-
*
|
|
4582
|
-
* first record, a session switch, or unmount. The record is written once, at
|
|
4583
|
-
* the subagent's FIRST dispatch request — a header opened before that moment
|
|
4584
|
-
* would otherwise never see the badge until remount.
|
|
4533
|
+
* `provider/model` of the model-selection projection's `lastUsed`, or
|
|
4534
|
+
* `undefined` when the value/key/fields are absent or foreign — the badge then
|
|
4535
|
+
* hovers the role alone instead of rendering a wrong route.
|
|
4585
4536
|
*/
|
|
4586
|
-
|
|
4587
|
-
|
|
4537
|
+
function latestRequestRoute(value) {
|
|
4538
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
4539
|
+
const { lastUsed } = value;
|
|
4540
|
+
if (typeof lastUsed !== "object" || lastUsed === null) return void 0;
|
|
4541
|
+
const { provider, model } = lastUsed;
|
|
4542
|
+
if (typeof provider !== "string" || provider === "" || typeof model !== "string" || model === "") return void 0;
|
|
4543
|
+
return `${provider}/${model}`;
|
|
4544
|
+
}
|
|
4588
4545
|
/**
|
|
4589
4546
|
* Render the viewed session's dispatch-resolved role badge.
|
|
4590
|
-
* @param props - composed slot props (the
|
|
4547
|
+
* @param props - composed slot props (the session-kit `useProjection` seat is
|
|
4591
4548
|
* read structurally — see the module docblock).
|
|
4592
4549
|
* @returns the badge element, or `null` when there is nothing to show.
|
|
4593
4550
|
*/
|
|
4594
4551
|
function SubagentRoleBadge(props) {
|
|
4595
|
-
const { t
|
|
4596
|
-
const
|
|
4597
|
-
|
|
4598
|
-
const
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
sessionId,
|
|
4604
|
-
record: void 0
|
|
4605
|
-
});
|
|
4606
|
-
const record = stamped.sessionId === sessionId ? stamped.record : void 0;
|
|
4607
|
-
(0, react.useEffect)(() => {
|
|
4608
|
-
if (sessionId === void 0) return;
|
|
4609
|
-
let cancelled = false;
|
|
4610
|
-
let timer;
|
|
4611
|
-
const probe = (attempt) => {
|
|
4612
|
-
controller.fetchSubagentRole(sessionId).then((next) => {
|
|
4613
|
-
if (cancelled) return;
|
|
4614
|
-
if (next !== void 0) {
|
|
4615
|
-
setStamped({
|
|
4616
|
-
sessionId,
|
|
4617
|
-
record: next
|
|
4618
|
-
});
|
|
4619
|
-
return;
|
|
4620
|
-
}
|
|
4621
|
-
if (attempt >= ROLE_RECORD_PROBES) return;
|
|
4622
|
-
timer = setTimeout(() => {
|
|
4623
|
-
timer = void 0;
|
|
4624
|
-
probe(attempt + 1);
|
|
4625
|
-
}, ROLE_RECORD_PROBE_DELAY_MS);
|
|
4626
|
-
});
|
|
4627
|
-
};
|
|
4628
|
-
probe(0);
|
|
4629
|
-
return () => {
|
|
4630
|
-
cancelled = true;
|
|
4631
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
4632
|
-
};
|
|
4633
|
-
}, [controller, sessionId]);
|
|
4634
|
-
if (record === void 0 || record.role === "inherit") return null;
|
|
4635
|
-
const model = record.model === void 0 ? void 0 : `${record.model.provider}/${record.model.model}`;
|
|
4552
|
+
const { t } = props;
|
|
4553
|
+
const readProjection = props.useProjection;
|
|
4554
|
+
if (typeof readProjection !== "function") return null;
|
|
4555
|
+
const rawRole = readProjection(ROLE_PROJECTION_KEY);
|
|
4556
|
+
const rawModelSelection = readProjection(MODEL_SELECTION_PROJECTION_KEY);
|
|
4557
|
+
const role = readRoleProjectionValue(rawRole);
|
|
4558
|
+
if (role === void 0 || role === "inherit") return null;
|
|
4559
|
+
const model = latestRequestRoute(rawModelSelection);
|
|
4636
4560
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4637
4561
|
className: SubagentRoleBadge_module_css_default.badge,
|
|
4638
|
-
title: model === void 0 ?
|
|
4639
|
-
role
|
|
4562
|
+
title: model === void 0 ? role : t("subagentRole.hover", {
|
|
4563
|
+
role,
|
|
4640
4564
|
model
|
|
4641
4565
|
}),
|
|
4642
|
-
children:
|
|
4566
|
+
children: role
|
|
4643
4567
|
});
|
|
4644
4568
|
}
|
|
4645
4569
|
//#endregion
|
|
@@ -4759,13 +4683,12 @@ window.__ModuleLoader__.load({
|
|
|
4759
4683
|
locale: NS
|
|
4760
4684
|
}, ConversationFallbackSwitch);
|
|
4761
4685
|
});
|
|
4762
|
-
ctx.slots.inject("conversation.session.header.
|
|
4686
|
+
ctx.slots.inject("conversation.session.header.actions", function* () {
|
|
4763
4687
|
yield ctx.slots.register({
|
|
4764
|
-
name: "conversation.session.header.
|
|
4688
|
+
name: "conversation.session.header.actions",
|
|
4765
4689
|
id: "fallbacks-subagent-role",
|
|
4766
|
-
order: -
|
|
4767
|
-
locale: NS
|
|
4768
|
-
inject: () => ({ controller })
|
|
4690
|
+
order: -5,
|
|
4691
|
+
locale: NS
|
|
4769
4692
|
}, SubagentRoleBadge);
|
|
4770
4693
|
});
|
|
4771
4694
|
}
|
package/dist/gateway.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* T1 (plan llm-fallbacks-settings-gateway) + T3 (plan fallbacks-role-seeds) —
|
|
3
3
|
* host-side `fallbacks` config gateway: the `/api/fallbacks/get` +
|
|
4
4
|
* `/api/fallbacks/set` + `/api/fallbacks/reset` + `/api/fallbacks/revert-seed`
|
|
5
|
-
*
|
|
5
|
+
* endpoints.
|
|
6
6
|
*
|
|
7
7
|
* Transport: the typertGateway `/api` interceptor is the single host-wide RPC
|
|
8
8
|
* slot (a plugin must NOT `connection.rpc.intercept('/api')` again — it would
|
|
@@ -43,11 +43,6 @@
|
|
|
43
43
|
* `revert-seed` exposes revert-to-current-seed-default for one id — both
|
|
44
44
|
* delegate to the per-apply `FallbacksSeedManager` passed into the
|
|
45
45
|
* constructor (single point of truth, no copied manager logic).
|
|
46
|
-
* `subagent-roles` (plan subagent-role-badge T2) is the read-only badge
|
|
47
|
-
* readback: it projects the per-apply dispatch-resolved role-record map
|
|
48
|
-
* (same T1 map the inject block writes, via the optional snapshot fn) for a
|
|
49
|
-
* batch of session ids — unknown ids are omitted, an unwired or throwing
|
|
50
|
-
* snapshot degrades to `{}`, never an error.
|
|
51
46
|
*
|
|
52
47
|
* The settings service is OPTIONAL (no settings service → the bridge source
|
|
53
48
|
* stays the entry, `get` still works; `set`/`reset` fail with a clear
|
|
@@ -131,29 +126,6 @@ export type SubagentPolicySnapshotFn = () => {
|
|
|
131
126
|
reason: string;
|
|
132
127
|
};
|
|
133
128
|
};
|
|
134
|
-
/**
|
|
135
|
-
* One dispatch-resolved subagent role record on the wire (plan
|
|
136
|
-
* subagent-role-badge T2): the role the subagent's dispatch resolved and the
|
|
137
|
-
* route it actually runs after the inject decision. `model` is optional on
|
|
138
|
-
* the WIRE type only — the writer (T1) always sets it, but a record without
|
|
139
|
-
* one still reads back as `{ role, at }` instead of failing the readback.
|
|
140
|
-
*/
|
|
141
|
-
export type SubagentRoleRecord = {
|
|
142
|
-
role: string;
|
|
143
|
-
model?: {
|
|
144
|
-
provider: string;
|
|
145
|
-
model: string;
|
|
146
|
-
};
|
|
147
|
-
at: number;
|
|
148
|
-
};
|
|
149
|
-
/**
|
|
150
|
-
* Live role-record snapshot the gateway reads through (plan
|
|
151
|
-
* subagent-role-badge T2) — the `SubagentPolicySnapshotFn` pattern: built in
|
|
152
|
-
* `apply()` over the SAME T1 map the dispatch inject block writes, no second
|
|
153
|
-
* record source. Optional: constructors that omit it make the
|
|
154
|
-
* `/api/fallbacks/subagent-roles` endpoint return `{}`.
|
|
155
|
-
*/
|
|
156
|
-
export type SubagentRolesSnapshotFn = () => ReadonlyMap<string, SubagentRoleRecord>;
|
|
157
129
|
/**
|
|
158
130
|
* The live configuration source for the gateway (guide §7 — the same bridge
|
|
159
131
|
* shape the runtime reads through). `source()` returns the live composed
|
|
@@ -212,12 +184,6 @@ export declare class FallbacksConfigGateway extends TypertRemoteService {
|
|
|
212
184
|
* used by gateway unit tests stays byte-identical to the pre-T5 payload.
|
|
213
185
|
*/
|
|
214
186
|
private readonly subagentPolicy;
|
|
215
|
-
/**
|
|
216
|
-
* Optional role-record snapshot (plan subagent-role-badge T2). When omitted
|
|
217
|
-
* the `subagentRoles` endpoint returns `{}` — every existing constructor
|
|
218
|
-
* call site keeps its payload byte-identical.
|
|
219
|
-
*/
|
|
220
|
-
private readonly subagentRolesSnapshot;
|
|
221
187
|
/**
|
|
222
188
|
* @param ctx - owning context (the plugin fiber's ctx inside `apply`).
|
|
223
189
|
* @param bridge - the same `FallbacksSettingsBridge` the runtime reads, so
|
|
@@ -227,11 +193,8 @@ export declare class FallbacksConfigGateway extends TypertRemoteService {
|
|
|
227
193
|
* copied manager logic), through the io seam built over this bridge.
|
|
228
194
|
* @param subagentPolicy - live host-policy snapshot from `apply()`; omit
|
|
229
195
|
* to leave the additive field off the wire.
|
|
230
|
-
* @param subagentRoles - live dispatch-resolved role-record snapshot from
|
|
231
|
-
* `apply()` (plan subagent-role-badge T2); omit to make the
|
|
232
|
-
* `subagent-roles` endpoint return `{}`.
|
|
233
196
|
*/
|
|
234
|
-
constructor(ctx: Context, bridge: FallbacksSettingsBridge, seeds: FallbacksSeedManager, subagentPolicy?: SubagentPolicySnapshotFn
|
|
197
|
+
constructor(ctx: Context, bridge: FallbacksSettingsBridge, seeds: FallbacksSeedManager, subagentPolicy?: SubagentPolicySnapshotFn);
|
|
235
198
|
/**
|
|
236
199
|
* Read the current composed config (schema defaults → entry base → settings
|
|
237
200
|
* user layer). No hard-gate resolver (ADR-2): the raw composed config is
|
|
@@ -290,22 +253,6 @@ export declare class FallbacksConfigGateway extends TypertRemoteService {
|
|
|
290
253
|
* write channel is unavailable (KD-G5).
|
|
291
254
|
*/
|
|
292
255
|
revertSeed(id: string): Promise<FallbacksRevertResult>;
|
|
293
|
-
/**
|
|
294
|
-
* Batch readback of dispatch-resolved subagent role records (plan
|
|
295
|
-
* subagent-role-badge T2) — the read side of the session-header role badge.
|
|
296
|
-
* Ids are agent ids (≡ session ids: the same id space the dispatch inject
|
|
297
|
-
* block records under). Unknown/absent ids are OMITTED from the result,
|
|
298
|
-
* never an error — the badge renders nothing for them. No snapshot wired
|
|
299
|
-
* (pre-T2 constructors) or a throwing snapshot ⇒ `{}` — degrade-never-crash,
|
|
300
|
-
* the same fail-closed shape as `projectSubagentPolicy`.
|
|
301
|
-
* @param ids - the session ids to read (at most
|
|
302
|
-
* {@link SUBAGENT_ROLES_MAX_IDS} entries).
|
|
303
|
-
* @returns a plain JSON object keyed by KNOWN id only, each carrying the
|
|
304
|
-
* wire record (`role`, `at`, `model` when the record has one).
|
|
305
|
-
* @throws TypeError when `ids` is not an array of strings or the batch
|
|
306
|
-
* exceeds {@link SUBAGENT_ROLES_MAX_IDS} entries.
|
|
307
|
-
*/
|
|
308
|
-
subagentRoles(ids: string[]): Record<string, SubagentRoleRecord>;
|
|
309
256
|
/**
|
|
310
257
|
* Read the live composed config and normalize it to the typertGateway JSON
|
|
311
258
|
* wire boundary. Containment (guide §10): a malformed stored user layer
|
package/dist/index.d.ts
CHANGED
|
@@ -124,29 +124,6 @@ export declare function blockedAttempts(ctx: Context): ReadonlyMap<string, Block
|
|
|
124
124
|
* plugin is applied.
|
|
125
125
|
*/
|
|
126
126
|
export declare function chainHeads(ctx: Context): ReadonlyMap<string, EffectiveChainHead> | undefined;
|
|
127
|
-
/**
|
|
128
|
-
* One dispatch-resolved subagent role record (plan subagent-role-badge T1):
|
|
129
|
-
* the role `resolveRoleAtDispatch` resolved at the subagent's first request
|
|
130
|
-
* and the route the subagent will actually run after the inject decision
|
|
131
|
-
* (the override target when it applies, else the host seed).
|
|
132
|
-
*/
|
|
133
|
-
type SubagentRoleRecord = {
|
|
134
|
-
role: string;
|
|
135
|
-
model: {
|
|
136
|
-
provider: string;
|
|
137
|
-
model: string;
|
|
138
|
-
};
|
|
139
|
-
at: number;
|
|
140
|
-
};
|
|
141
|
-
/**
|
|
142
|
-
* @internal Test seam (mirrors `chainHeads`): the per-agent dispatch-resolved
|
|
143
|
-
* role records written by the role-inject block (plan subagent-role-badge T1)
|
|
144
|
-
* for the plugin applied to `ctx`. Not part of the plugin's public surface;
|
|
145
|
-
* lets tests read the badge record without reaching into the closure (the
|
|
146
|
-
* gateway readback closes over the per-apply map directly). `undefined` when
|
|
147
|
-
* no plugin is applied.
|
|
148
|
-
*/
|
|
149
|
-
export declare function subagentRoleRecords(ctx: Context): ReadonlyMap<string, SubagentRoleRecord> | undefined;
|
|
150
127
|
/**
|
|
151
128
|
* Count durable `llm/retry` events for the current (turn, step, provider) in
|
|
152
129
|
* **always mode** (ADR-2; spec §2 clause 5). Normal-mode retries belong to
|