dsh-llm-fallbacks 0.3.1 → 0.3.3
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 +52 -16
- package/README.zh-CN.md +58 -22
- package/dist/client/index.d.ts +4 -2
- package/dist/client/index.js +7 -4
- package/dist/commands.d.ts +120 -27
- package/dist/gateway.d.ts +17 -0
- package/dist/index.js +473 -12
- package/dist/selectors.d.ts +6 -3
- package/dist/tui-settings.d.ts +140 -0
- package/dist/tui.d.ts +5 -4
- package/package.json +18 -18
package/README.md
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|

|
|
7
7
|

|
|
8
8
|

|
|
9
|
-

|
|
10
10
|
[](https://dshfind.com/zh/plugins/omdsh-dev/dsh-llm-fallbacks?ref=badge)
|
|
11
11
|
|
|
12
12
|
Automatic provider/model fallback chains for dsh (DeepSeek Harness): when an agent's LLM requests keep failing — retries exhausted, auth errors, quota exceeded, rate limiting (429) — the plugin switches provider/model along the fallback chain for the current role, and the current step/turn continues on the target model: tasks are not interrupted by model problems.
|
|
13
13
|
|
|
14
|
-
Works in both dsh front ends: the **web** profile (Settings → Plugins → Fallbacks card) and the **dsh-tui** terminal profile (`/fallbacks`
|
|
14
|
+
Works in both dsh front ends: the **web** profile (Settings → Plugins → Fallbacks card) and the **dsh-tui** terminal profile (`/fallbacks` session diagnostics, `/fallbacks config` readback, and the `/settings` fallbacks section for editing).
|
|
15
15
|
|
|
16
16
|
## Time slots
|
|
17
17
|
|
|
@@ -57,18 +57,30 @@ pnpm repair:fallbacks-switch-logs -- --apply --backup # mark legacy events i
|
|
|
57
57
|
|
|
58
58
|
The script scans `~/.dsh/sessions` by default (override with `--root <dir>`), marks legacy `fallbacks/switch` events `ignorable: true` so the host read path accepts the session again, and keeps a `<file>.bak` per repaired log. `--apply` requires `--backup` and must run with dsh stopped. From 0.2.2 on, the plugin stops writing durable switch events, so no new sessions need repair.
|
|
59
59
|
|
|
60
|
+
### Configuration surfaces
|
|
61
|
+
|
|
62
|
+
The plugin's settings live in a shared `fallbacks:` namespace, editable from three surfaces:
|
|
63
|
+
|
|
64
|
+
| Surface | What it is | Notes |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| **Web settings card** | Settings → Plugins → Fallbacks | Full GUI editor for the `fallbacks:` namespace; writes the shared settings document |
|
|
67
|
+
| **`$DSH_HOME/settings.yaml`** | `fallbacks:` section in the dsh settings document | The shared source of truth — the same file the web card writes; readable and editable everywhere, including scripted setups |
|
|
68
|
+
| **TUI `/settings`** | fallbacks section in the dsh-tui settings screen | dsh-tui ≥ v0.8.5; native fields for simple keys, JSON text fields for complex structures (see [dsh-tui profile (terminal)](#dsh-tui-profile-terminal)) |
|
|
69
|
+
|
|
70
|
+
Pick the surface that matches your front end: web users get the card, terminal users get `/settings`, and the YAML file works everywhere. (`/fallbacks` and `/fallbacks config` are diagnostics — read-only views, not edit surfaces.)
|
|
71
|
+
|
|
60
72
|
### Minimal configuration
|
|
61
73
|
|
|
62
|
-
Add a `fallbacks:` section to the
|
|
74
|
+
Add a `fallbacks:` section to the shared settings document (`$DSH_HOME/settings.yaml` — see [Configuration surfaces](#configuration-surfaces)):
|
|
63
75
|
|
|
64
76
|
```yaml
|
|
65
77
|
fallbacks:
|
|
66
|
-
enabled: true # feature switch
|
|
67
|
-
rootChain: # all-day chain:
|
|
68
|
-
- anthropic/claude-3-5-sonnet #
|
|
69
|
-
- deepseek-official/deepseek-v4-flash # last
|
|
78
|
+
enabled: true # feature switch — defaults to off (plugin is a no-op otherwise)
|
|
79
|
+
rootChain: # all-day chain: leading entries = fallback walk, last = Default model (official V4)
|
|
80
|
+
- anthropic/claude-3-5-sonnet # walked first
|
|
81
|
+
- deepseek-official/deepseek-v4-flash # last resort (Flash or Pro)
|
|
70
82
|
timeSlots: # optional: rotate the effective root chain by wall-clock windows
|
|
71
|
-
- kind: preset # frozen UTC+8
|
|
83
|
+
- kind: preset # frozen UTC+8 window; only the chain is editable
|
|
72
84
|
preset: liang-peak # 09:00–12:00 and 14:00–18:00, every day
|
|
73
85
|
chain:
|
|
74
86
|
- anthropic/claude-3-5-sonnet
|
|
@@ -79,24 +91,36 @@ fallbacks:
|
|
|
79
91
|
days: [1, 5] # optional; omitted/empty = every day (0=Sunday…6=Saturday)
|
|
80
92
|
chain:
|
|
81
93
|
- openai/gpt-4o
|
|
82
|
-
roles: #
|
|
94
|
+
roles: # optional: declare role entities, then reference them from rules
|
|
83
95
|
list:
|
|
84
|
-
- id: reviewer # unique id
|
|
96
|
+
- id: reviewer # unique id; "inherit" is reserved
|
|
85
97
|
persona: Code-review subagents
|
|
86
98
|
chain:
|
|
87
99
|
- openai/gpt-4o-mini
|
|
88
|
-
fallback: inherit-root #
|
|
100
|
+
fallback: inherit-root # role chain first, then the inherited rootChain
|
|
89
101
|
rules: # subagent-only: rules never match root requests
|
|
90
|
-
- role: reviewer # all subagents → reviewer role
|
|
102
|
+
- role: reviewer # all subagents → the reviewer role
|
|
91
103
|
```
|
|
92
104
|
|
|
93
|
-
|
|
105
|
+
Build the section up in four steps:
|
|
106
|
+
|
|
107
|
+
**1. Enable the plugin.** `enabled: true` turns the fallback engine on. It defaults to **off** — with no chains configured the plugin is a complete no-op.
|
|
108
|
+
|
|
109
|
+
**2. Set the all-day `rootChain`.** Leading entries are the fallback chain, walked first when a request fails; the **last** entry is the Default model.
|
|
110
|
+
|
|
111
|
+
> **Conformance**: the last entry must be exactly one official V4 model — `deepseek-official/deepseek-v4-flash` XOR `deepseek-official/deepseek-v4-pro`. The settings card and gateway reject any other tail on save; a legacy non-official tail warns at startup and keeps working as a fallback-only walk, but cannot be saved as-is.
|
|
112
|
+
|
|
113
|
+
**3. Add `timeSlots` (optional).** Rows rotate the effective root chain by wall-clock windows. Preset rows use frozen UTC+8 windows (only their chain is editable; while a preset row exists, `tz` locks to `Asia/Shanghai`); custom rows take `start`/`end` (may wrap midnight) and an optional `days` list. The first row whose window contains the current moment wins; no match → the all-day `rootChain`. Rotation is a routing seed — it applies on the next root request and consumes no cooldown (see [Time slots](#time-slots)).
|
|
114
|
+
|
|
115
|
+
**4. Add `roles` (optional).** Declare role entities in `roles.list` (id, persona, chain, optional `fallback` policy), then map subagents to them with `roles.rules`. Rules never match root requests — with no rule match (or on a root request) the built-in `inherit` role applies and appends the `rootChain`.
|
|
116
|
+
|
|
117
|
+
Full reference (role entities, fallback strategies, rules, selectors, preset roles, time-slot presets) → [docs/configuration.md](docs/configuration.md).
|
|
94
118
|
|
|
95
119
|
> **Upgrade note (behavior change)**: an existing `fallbacks:` section **without an explicit `enabled` key** resolves to `false` after upgrading — add `enabled: true` to keep the plugin active.
|
|
96
120
|
|
|
97
121
|
### Verify
|
|
98
122
|
|
|
99
|
-
Save and restart the session, then type `/fallbacks` — the read-only in-session diagnostics (origin, resolved role, chain, recent
|
|
123
|
+
Save the config and restart the session, then type `/fallbacks` — the read-only in-session diagnostics (origin, resolved role, chain, recent fallback switches, cooldown status). In a dsh-tui profile, `/fallbacks config` reads back the composed configuration; see [dsh-tui profile (terminal)](#dsh-tui-profile-terminal).
|
|
100
124
|
|
|
101
125
|
## Features
|
|
102
126
|
|
|
@@ -108,11 +132,23 @@ Save and restart the session, then type `/fallbacks` — the read-only in-sessio
|
|
|
108
132
|
- **Cooldown and revert**: failed / switched-away models are not re-selected during cooldown; `revertPolicy: cooldown-expiry` returns to the primary model automatically.
|
|
109
133
|
- **Visible behavior**: every switch is recorded in an info-level log line (from/to/role/reason) — no silent model switching. The plugin deliberately writes **no** durable `fallbacks/switch` session events (issue #52: the apply()-time event-type registration was proven ineffective, and a session containing the event refused to load after a dsh restart). Sessions written by older plugin versions that contain such events are repaired by `scripts/repair-fallbacks-switch-logs.ts`, which marks legacy events ignorable so affected sessions load again.
|
|
110
134
|
- **Safety valves**: `maxSwitchesPerStep` caps switches per step and `alwaysModeRetryCap` caps always-mode retries — chain loops cannot amplify latency.
|
|
111
|
-
- **No-config no-op**:
|
|
135
|
+
- **No-config no-op**: with no chains configured the plugin behaves exactly like not being installed (`enabled` is off by default — see [Minimal configuration](#minimal-configuration)).
|
|
136
|
+
|
|
137
|
+
## dsh-tui profile (terminal)
|
|
138
|
+
|
|
139
|
+
In a dsh-tui profile the plugin has three operator surfaces, with a strict duty split:
|
|
140
|
+
|
|
141
|
+
- **`/fallbacks`** — what happened this session: origin, resolved role, effective chain, recent fallback switches, cooldown status. Read-only.
|
|
142
|
+
- **`/fallbacks config`** — what is configured: composed-config readback (trigger codes, root chain, time slots, timezone, roles, role rules, cooldown, revert policy, safety valves, presets, role auto-match). Read-only apart from the one action command **`/fallbacks config revert-seed <role-id>`**, which restores a seeded role's persona to its declared seed default (a web-card action the settings seam cannot express).
|
|
143
|
+
- **`/settings`** — the edit surface. The plugin registers a **fallbacks** section with full parity to the web settings card: booleans (`enabled`, `roleAutoMatch`) render as toggles, selects (`presets`, `revertPolicy`) as pickers, and numbers (`cooldownMs`, `maxSwitchesPerStep`, `alwaysModeRetryCap`) as numeric inputs; complex structures (`rootChain`, `timeSlots`, `roles.list`, `roles.rules`) are JSON text fields and `triggerCodes` a comma-separated text field. Invalid drafts (bad JSON, non-conforming chains, malformed time-slot rows) block the save — the section never corrupts the config.
|
|
144
|
+
|
|
145
|
+
**Requirements**: the `/settings` fallbacks section needs **dsh-tui ≥ v0.8.5** (commit `c51661f` or later on `main`; the settings seam shipped in v0.8.0, the groups shape + validation in v0.8.5). On an older dsh-tui the section is absent, and file editing remains the only TUI edit surface.
|
|
146
|
+
|
|
147
|
+
File editing still works everywhere: the shared `$DSH_HOME/settings.yaml` (`fallbacks:` section — the same file the web card writes) for global settings, or the profile patch `~/.dsh/profiles/dsh-tui/cordis.patch.yml` (`config:` overrides on the plugin row) for dsh-tui-specific values. A patch row **replaces** the targeted row's whole `config` — restate every field you want to keep (schema defaults fill the rest).
|
|
112
148
|
|
|
113
149
|
## FallbacksChain in the model picker
|
|
114
150
|
|
|
115
|
-
When `enabled: true`, the plugin registers a virtual provider, **FallbacksChain**, with a single catalog row: **Auto**. The web profile and dsh-tui both see the row: they share the same adapter catalog, so no
|
|
151
|
+
When `enabled: true`, the plugin registers a virtual provider, **FallbacksChain**, with a single catalog row: **Auto**. The web profile and dsh-tui both see the row: they share the same adapter catalog, so the row needs no settings-page wiring or host patch (it is independent of the `/settings` fallbacks section, which edits configuration rather than the picker catalog). The row is visible whenever the plugin is enabled — a legacy or empty all-day chain does NOT hide it (the override just refuses to fire).
|
|
116
152
|
|
|
117
153
|
Selecting **FallbacksChain / Auto** uses the configured chain as the root **primary**: root requests route to the effective chain's first exact `provider/model` at request time, and the fallback engine degrades from that head as usual. Selecting any real catalog model keeps the v0.2.2 fallback-only behavior — the session model is primary and the chain engages only after it fails.
|
|
118
154
|
|
package/README.zh-CN.md
CHANGED
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|

|
|
7
7
|

|
|
8
8
|

|
|
9
|
-

|
|
10
10
|
[](https://dshfind.com/zh/plugins/omdsh-dev/dsh-llm-fallbacks?ref=badge)
|
|
11
11
|
|
|
12
12
|
dsh(DeepSeek Harness)的自动模型降级插件:当 root agent 或 subagent 的模型请求持续失败(重试耗尽、权限、配额超限、限流 429)时,按角色/模型 fallback 链自动切换 provider/model,当前 step/turn 在目标模型上继续完成——任务不因模型问题中断。
|
|
13
13
|
|
|
14
|
-
两个 dsh 前端均可用:**web** profile(设置 → 插件配置 → Fallbacks 卡片)与 **dsh-tui** 终端 profile(`/fallbacks`
|
|
14
|
+
两个 dsh 前端均可用:**web** profile(设置 → 插件配置 → Fallbacks 卡片)与 **dsh-tui** 终端 profile(`/fallbacks` 会话诊断、`/fallbacks config` 回读,以及 `/settings` 中的 fallbacks 区块用于编辑)。
|
|
15
15
|
|
|
16
16
|
## 峰谷无忧
|
|
17
17
|
|
|
@@ -57,46 +57,70 @@ pnpm repair:fallbacks-switch-logs -- --apply --backup # 给旧事件打 igno
|
|
|
57
57
|
|
|
58
58
|
脚本默认扫描 `~/.dsh/sessions`(可用 `--root <dir>` 覆盖),把遗留 `fallbacks/switch` 事件标记为 `ignorable: true`,宿主读路径即可重新接受该会话;每个被修复的日志保留一份 `<file>.bak`。`--apply` 必须搭配 `--backup`,且须在 dsh 停止时运行。从 0.2.2 起插件不再写 durable 切换事件,新会话无需修复。
|
|
59
59
|
|
|
60
|
+
### 配置界面
|
|
61
|
+
|
|
62
|
+
插件的设置存在于共享的 `fallbacks:` 命名空间中,可通过三个界面编辑:
|
|
63
|
+
|
|
64
|
+
| 界面 | 是什么 | 说明 |
|
|
65
|
+
|---|---|---|
|
|
66
|
+
| **Web 设置卡** | 设置 → 插件配置 → Fallbacks | `fallbacks:` 命名空间的完整 GUI 编辑器;写入共享设置文档 |
|
|
67
|
+
| **`$DSH_HOME/settings.yaml`** | dsh 设置文档中的 `fallbacks:` 分节 | 共享的事实源——与 Web 卡写入的是同一个文件;任何场景(包括脚本化配置)都可读写 |
|
|
68
|
+
| **TUI `/settings`** | dsh-tui 设置界面中的 fallbacks 区块 | 需要 dsh-tui ≥ v0.8.5;简单键用原生字段,复杂结构用 JSON 文本字段(见 [dsh-tui profile(终端)](#dsh-tui-profile终端)) |
|
|
69
|
+
|
|
70
|
+
按你的前端选择入口:web 用户用设置卡,终端用户用 `/settings`,YAML 文件处处可用。(`/fallbacks` 与 `/fallbacks config` 是诊断命令——只读视图,不是编辑入口。)
|
|
71
|
+
|
|
60
72
|
### 最小配置
|
|
61
73
|
|
|
62
|
-
|
|
74
|
+
在共享设置文档(`$DSH_HOME/settings.yaml`——见 [配置界面](#配置界面))中添加 `fallbacks:` 分节:
|
|
63
75
|
|
|
64
76
|
```yaml
|
|
65
77
|
fallbacks:
|
|
66
|
-
enabled: true
|
|
67
|
-
rootChain:
|
|
68
|
-
- anthropic/claude-3-5-sonnet #
|
|
69
|
-
- deepseek-official/deepseek-v4-flash #
|
|
70
|
-
timeSlots:
|
|
71
|
-
- kind: preset
|
|
72
|
-
preset: liang-peak
|
|
78
|
+
enabled: true # 功能开关——默认关闭(否则插件完全 no-op)
|
|
79
|
+
rootChain: # 全时段链:前面的条目 = 降级路径,最后一项 = 默认模型(官方 V4)
|
|
80
|
+
- anthropic/claude-3-5-sonnet # 先走
|
|
81
|
+
- deepseek-official/deepseek-v4-flash # 最后一档(Flash 或 Pro)
|
|
82
|
+
timeSlots: # 可选:按墙钟窗口轮换生效 root 链
|
|
83
|
+
- kind: preset # 冻结的 UTC+8 窗口;仅链可编辑
|
|
84
|
+
preset: liang-peak # 每天 09:00–12:00 与 14:00–18:00
|
|
73
85
|
chain:
|
|
74
86
|
- anthropic/claude-3-5-sonnet
|
|
75
|
-
- kind: custom
|
|
76
|
-
name: evening
|
|
87
|
+
- kind: custom # 自定义窗口(可跨午夜)
|
|
88
|
+
name: evening # 可选显示名称
|
|
77
89
|
start: '22:00'
|
|
78
90
|
end: '02:00'
|
|
79
|
-
days: [1, 5]
|
|
91
|
+
days: [1, 5] # 可选;缺省/空 = 每天(0=周日…6=周六)
|
|
80
92
|
chain:
|
|
81
93
|
- openai/gpt-4o
|
|
82
|
-
roles:
|
|
94
|
+
roles: # 可选:先声明角色实体,再由规则引用
|
|
83
95
|
list:
|
|
84
|
-
- id: reviewer
|
|
96
|
+
- id: reviewer # 唯一 id;"inherit" 为保留字
|
|
85
97
|
persona: 代码审查子代理
|
|
86
98
|
chain:
|
|
87
99
|
- openai/gpt-4o-mini
|
|
88
|
-
fallback: inherit-root #
|
|
89
|
-
rules:
|
|
90
|
-
- role: reviewer
|
|
100
|
+
fallback: inherit-root # 先走角色链,再追加继承的 rootChain
|
|
101
|
+
rules: # 仅对子代理生效:规则不匹配 root 请求
|
|
102
|
+
- role: reviewer # 所有 subagent → reviewer 角色
|
|
91
103
|
```
|
|
92
104
|
|
|
93
|
-
|
|
105
|
+
按四个步骤逐步构建:
|
|
106
|
+
|
|
107
|
+
**1. 启用插件。** `enabled: true` 打开降级引擎。默认**关闭(`false`)**——未配置任何链时插件完全 no-op。
|
|
108
|
+
|
|
109
|
+
**2. 配置全时段 `rootChain`。** 前面的条目是降级链,请求失败时先走;**最后**一项是默认模型。
|
|
110
|
+
|
|
111
|
+
> **链尾合规**:最后一项必须是恰好一个官方 V4 模型——`deepseek-official/deepseek-v4-flash` 或 `deepseek-official/deepseek-v4-pro`(二选一)。设置卡与 gateway 在保存时拒绝其它尾巴;遗留的非合规尾巴启动时告警并继续按 fallback-only 走原链,但无法原样保存。
|
|
112
|
+
|
|
113
|
+
**3. 添加 `timeSlots`(可选)。** 各行按墙钟窗口轮换生效 root 链。预设行使用冻结的 UTC+8 窗口(仅链可编辑;存在预设行时 `tz` 锁定 `Asia/Shanghai`);自定义行使用 `start`/`end`(可跨午夜)与可选的 `days` 列表。第一个窗口包含当前时刻的行生效;无行命中 → 全时段 `rootChain`。分时切换是路由种子——在下一个 root 请求生效、不消耗冷却(见 [峰谷无忧](#峰谷无忧))。
|
|
114
|
+
|
|
115
|
+
**4. 添加 `roles`(可选)。** 在 `roles.list` 中声明角色实体(id、persona、chain、可选的 `fallback` 策略),再用 `roles.rules` 把 subagent 映射到角色。规则绝不匹配 root 请求——未命中规则(或 root 请求)时由内置 `inherit` 角色兜底,追加 `rootChain`。
|
|
116
|
+
|
|
117
|
+
完整参考(角色实体、fallback 策略、规则、selector、预设角色、分时槽预设)→ [docs/configuration.md](docs/configuration.md)。
|
|
94
118
|
|
|
95
119
|
> **升级提示(行为变更)**:已有 `fallbacks:` 配置若**未显式写 `enabled` 键**,升级后解析为 `false`——请补上 `enabled: true` 以保持插件继续生效。
|
|
96
120
|
|
|
97
121
|
### 验证
|
|
98
122
|
|
|
99
|
-
|
|
123
|
+
保存配置并重启会话,然后键入 `/fallbacks`——只读的会话内诊断(来源、解析角色、链、最近降级切换、冷却状态)。在 dsh-tui profile 中,`/fallbacks config` 回读组合配置;见 [dsh-tui profile(终端)](#dsh-tui-profile终端)。
|
|
100
124
|
|
|
101
125
|
## 能力一览
|
|
102
126
|
|
|
@@ -108,11 +132,23 @@ fallbacks:
|
|
|
108
132
|
- **冷却与回主**:被切离/失败的模型在冷却期内不再入选;`revertPolicy: cooldown-expiry` 冷却到期后自动回主模型。
|
|
109
133
|
- **行为可见**:每次切换以 info 级日志行(from/to/role/reason)记录——无静默换模型。插件**刻意不写** durable `fallbacks/switch` 会话事件(issue #52——apply() 时的事件类型注册被证伪无效,含该事件的会话在 dsh 重启后拒绝加载)。由旧版插件写入、含此类事件的会话由 `scripts/repair-fallbacks-switch-logs.ts` 修复——旧事件被标记 ignorable 后,受影响会话可重新加载。
|
|
110
134
|
- **安全阀**:`maxSwitchesPerStep` 限制每 step 切换次数、`alwaysModeRetryCap` 限制 always 模式重试——链循环不会放大延迟。
|
|
111
|
-
- **无配置回归(no-op
|
|
135
|
+
- **无配置回归(no-op)**:未配置任何链时行为与未安装插件完全一致——`enabled` 默认关闭(见 [最小配置](#最小配置))。
|
|
136
|
+
|
|
137
|
+
## dsh-tui profile(终端)
|
|
138
|
+
|
|
139
|
+
在 dsh-tui profile 中,插件有三个操作面——职责严格区分:
|
|
140
|
+
|
|
141
|
+
- **`/fallbacks`** —— 本次会话发生了什么:来源、解析角色、生效链、最近降级切换、冷却状态。只读。
|
|
142
|
+
- **`/fallbacks config`** —— 配置了什么:组合配置回读(触发码、根链、分时槽、时区、角色、角色规则、冷却、回主策略、安全阀、预置、角色自动匹配)。除唯一的动作命令 **`/fallbacks config revert-seed <role-id>`** 外只读——该命令把某个 seed 角色的 persona 还原为已声明的默认(设置 seam 无法表达 Web 卡的这类动作能力)。
|
|
143
|
+
- **`/settings`** —— 编辑界面。插件注册 **fallbacks** 区块,与 **Web 设置卡完全一致**:布尔(`enabled`、`roleAutoMatch`)渲染为开关、下拉(`presets`、`revertPolicy`)为选择器、数值(`cooldownMs`、`maxSwitchesPerStep`、`alwaysModeRetryCap`)为数字输入;复杂结构(`rootChain`、`timeSlots`、`roles.list`、`roles.rules`)为 JSON 文本字段,`triggerCodes` 为逗号分隔文本字段。非法草稿(JSON 解析失败、链尾不合规、分时行畸形)会阻止保存——区块绝不写入损坏配置。
|
|
144
|
+
|
|
145
|
+
**版本要求**:`/settings` 的 fallbacks 区块需要 **dsh-tui ≥ v0.8.5**(`main` 上 commit `c51661f` 及以后;settings seam 于 v0.8.0 引入,groups 结构与校验于 v0.8.5 引入)。更旧的 dsh-tui 没有该区块,文件编辑仍是 TUI 唯一编辑面。
|
|
146
|
+
|
|
147
|
+
文件编辑在任意情况下仍然可用:全局设置写共享的 `$DSH_HOME/settings.yaml`(`fallbacks:` 分节——与 Web 卡写的是同一个文件);dsh-tui 专属覆盖写 profile patch `~/.dsh/profiles/dsh-tui/cordis.patch.yml`(插件行上的 `config:` 覆盖)。注意:patch 行会**整体替换**目标行的整个 `config`——想保留的字段都要写全(schema 默认值补齐其余)。
|
|
112
148
|
|
|
113
149
|
## 模型选择器中的 FallbacksChain
|
|
114
150
|
|
|
115
|
-
当 `enabled: true` 时,插件注册一个虚拟 provider **FallbacksChain**,目录中只有一行:**Auto**。web profile 与 dsh-tui 都能看到这一行:两者共享同一个 adapter catalog
|
|
151
|
+
当 `enabled: true` 时,插件注册一个虚拟 provider **FallbacksChain**,目录中只有一行:**Auto**。web profile 与 dsh-tui 都能看到这一行:两者共享同一个 adapter catalog,无需设置页接线或宿主补丁(它与 `/settings` 的 fallbacks 区块相互独立——区块编辑的是配置,不是选择器目录)。该行只要插件启用就可见——遗留多模型或空的 all-day 链**不会**隐藏它(只是覆盖不会生效)。
|
|
116
152
|
|
|
117
153
|
选择 **FallbacksChain / Auto** = 把配置的链作为 root **主模型**:root 请求路由到请求时刻生效链的第一个精确 `provider/model`,失败后由降级引擎从该链头照常沿链切换。选择任何真实目录模型则保持 v0.2.2 的 fallback-only 行为——会话模型为主,链只在它失败后介入。
|
|
118
154
|
|
package/dist/client/index.d.ts
CHANGED
|
@@ -12,8 +12,10 @@
|
|
|
12
12
|
* `/api/fallbacks/get|set|reset`), while `settings.describe` (writable +
|
|
13
13
|
* namespace directory) and the provider/model catalog stay on
|
|
14
14
|
* `connection.api` (see `fallbacks-store.ts`).
|
|
15
|
-
* - Registers the `settings.plugin.item` card `key: 'fallbacks'`
|
|
16
|
-
* keyed slot — no `
|
|
15
|
+
* - Registers the `settings.plugin.item` card `key: 'fallbacks'` with a
|
|
16
|
+
* matching `id: 'fallbacks'` (the rc.7 keyed slot — no `order`; the id
|
|
17
|
+
* keeps the card mountable on hosts that still declare the slot as a list,
|
|
18
|
+
* which requires `options.id`) with a business-only inject face
|
|
17
19
|
* ({@link FallbacksSettingsController} + the snapshot-selector hook); the
|
|
18
20
|
* old Settings-nav section registration is removed — deleting the section
|
|
19
21
|
* registration deletes the nav entry.
|
package/dist/client/index.js
CHANGED
|
@@ -180,7 +180,8 @@ window.__ModuleLoader__.load({
|
|
|
180
180
|
* Parse a chain key or entry selector.
|
|
181
181
|
*
|
|
182
182
|
* Accepts `provider/model` and `provider/*`; throws {@link SelectorError}
|
|
183
|
-
* on anything else (missing separator, empty parts,
|
|
183
|
+
* on anything else (missing separator, empty parts, wildcard inside the
|
|
184
|
+
* model segment).
|
|
184
185
|
*/
|
|
185
186
|
function parseSelector(input) {
|
|
186
187
|
if (typeof input !== "string") throw new SelectorError(`invalid selector ${String(input)}: expected "provider/model" or "provider/*"`);
|
|
@@ -190,7 +191,7 @@ window.__ModuleLoader__.load({
|
|
|
190
191
|
const provider = trimmed.slice(0, slash).trim();
|
|
191
192
|
const modelPart = trimmed.slice(slash + 1).trim();
|
|
192
193
|
if (!provider || !modelPart) throw new SelectorError(`invalid selector "${input}": empty provider or model`);
|
|
193
|
-
if (modelPart.includes("
|
|
194
|
+
if (modelPart !== "*" && modelPart.includes("*")) throw new SelectorError(`invalid selector "${input}": unexpected wildcard in model`);
|
|
194
195
|
return {
|
|
195
196
|
provider,
|
|
196
197
|
model: modelPart === "*" ? void 0 : modelPart,
|
|
@@ -4385,15 +4386,17 @@ window.__ModuleLoader__.load({
|
|
|
4385
4386
|
};
|
|
4386
4387
|
}, "llm-fallbacks: pushed invalidations");
|
|
4387
4388
|
ctx.slots.inject("settings.plugin.item", function* () {
|
|
4388
|
-
|
|
4389
|
+
const cardOptions = {
|
|
4389
4390
|
name: "settings.plugin.item",
|
|
4390
4391
|
key: "fallbacks",
|
|
4392
|
+
id: "fallbacks",
|
|
4391
4393
|
locale: NS,
|
|
4392
4394
|
inject: () => ({
|
|
4393
4395
|
controller,
|
|
4394
4396
|
useSnapshot
|
|
4395
4397
|
})
|
|
4396
|
-
}
|
|
4398
|
+
};
|
|
4399
|
+
yield ctx.slots.register(cardOptions, FallbacksCard);
|
|
4397
4400
|
});
|
|
4398
4401
|
ctx.slots.inject("settings.general.item", function* () {
|
|
4399
4402
|
yield ctx.slots.register({
|
package/dist/commands.d.ts
CHANGED
|
@@ -11,12 +11,17 @@
|
|
|
11
11
|
* the command is silently absent when no command registry is composed (no
|
|
12
12
|
* top-level inject pollution — advisor T1 fix).
|
|
13
13
|
*
|
|
14
|
-
* The
|
|
15
|
-
* reset, no pending-switch writes).
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
14
|
+
* The bare diagnostic and the config readback are **read-only**: they never
|
|
15
|
+
* mutate fallback state (no cooldown reset, no pending-switch writes). The
|
|
16
|
+
* `config revert-seed <role-id>` subcommand is the one write action (plan
|
|
17
|
+
* fallbacks-tui-settings Task 2, AC-3): it delegates to the controller's
|
|
18
|
+
* `revertSeed` (wired to the seeds service) and prints the outcome — a
|
|
19
|
+
* web-card action capability the settings seam cannot express. zh/en copy
|
|
20
|
+
* lives in this file — the client half's `src/client/locales.ts` is a
|
|
21
|
+
* separate client-side dictionary. The host carries no per-session locale
|
|
22
|
+
* signal (the `locale` service is client-side), so the wiring picks a
|
|
23
|
+
* deterministic default (`zh`, this repo's primary language); both
|
|
24
|
+
* dictionaries are unit-tested.
|
|
20
25
|
*
|
|
21
26
|
* @module dsh-llm-fallbacks/commands
|
|
22
27
|
*/
|
|
@@ -24,7 +29,8 @@ import type { CommandDefinition } from '@deepseek-ai/dsh-commands';
|
|
|
24
29
|
import { type FallbacksRole } from './config.ts';
|
|
25
30
|
import type { Origin } from './roles.ts';
|
|
26
31
|
import type { FallbacksSwitchEventData } from './events.ts';
|
|
27
|
-
import type {
|
|
32
|
+
import type { SeedRevertFailReason } from './seeds.ts';
|
|
33
|
+
import { type SlotRowConfig } from './time-slots.ts';
|
|
28
34
|
/** How many recent `fallbacks/switch` events `/fallbacks` shows (newest first). */
|
|
29
35
|
export declare const RECENT_SWITCHES_LIMIT = 5;
|
|
30
36
|
/** Minimal command registry surface (satisfied by the dsh `CommandService`). */
|
|
@@ -90,15 +96,40 @@ export interface FallbacksCommandSnapshot {
|
|
|
90
96
|
* entities carry id/persona/chain/fallback (NO per-role `model`;
|
|
91
97
|
* `provider`/`model` live on `roles.rules`), so the readback line is id +
|
|
92
98
|
* chain count, never a rules dump.
|
|
99
|
+
*
|
|
100
|
+
* Enriched readback (plan fallbacks-tui-settings Task 2, AC-4): timeSlots /
|
|
101
|
+
* tz / roles.rules join the summary so TUI operators can verify `/settings`
|
|
102
|
+
* edits. One summarized time-slot row: preset rows carry `{ preset,
|
|
103
|
+
* chainCount }` (windows are frozen code constants in {@link PRESETS} and
|
|
104
|
+
* never stored on the row — the render resolves the window text from
|
|
105
|
+
* PRESETS); custom rows carry `{ start, end, chainCount }`. Rules rows carry
|
|
106
|
+
* `{ provider, model, role }` — an omitted provider/model (wildcard match at
|
|
107
|
+
* runtime) is summarized as `''` and rendered as `*`.
|
|
93
108
|
*/
|
|
94
109
|
export interface FallbacksConfigSummary {
|
|
95
110
|
readonly enabled: boolean;
|
|
96
111
|
readonly triggerCodes: readonly string[];
|
|
97
112
|
readonly rootChain: readonly string[];
|
|
113
|
+
/** Summarized time-slot rows (preset rows carry `preset`, custom rows carry `start`/`end`; both may carry a day mask). */
|
|
114
|
+
readonly timeSlots: readonly {
|
|
115
|
+
preset?: string;
|
|
116
|
+
start?: string;
|
|
117
|
+
end?: string;
|
|
118
|
+
days?: readonly number[];
|
|
119
|
+
chainCount: number;
|
|
120
|
+
}[];
|
|
121
|
+
/** Config-level timezone for slot matching (default `Asia/Shanghai`). */
|
|
122
|
+
readonly tz: string;
|
|
98
123
|
readonly roles: readonly {
|
|
99
124
|
id: string;
|
|
100
125
|
chainCount: number;
|
|
101
126
|
}[];
|
|
127
|
+
/** Summarized role rules: provider/model patterns → declared role (or the built-in `'inherit'`). */
|
|
128
|
+
readonly rules: readonly {
|
|
129
|
+
provider: string;
|
|
130
|
+
model: string;
|
|
131
|
+
role: string;
|
|
132
|
+
}[];
|
|
102
133
|
readonly cooldownMs: number;
|
|
103
134
|
readonly revertPolicy: string;
|
|
104
135
|
readonly maxSwitchesPerStep: number;
|
|
@@ -122,6 +153,23 @@ export interface FallbacksCommandController {
|
|
|
122
153
|
* same live config source the runtime reads. Never mutates state.
|
|
123
154
|
*/
|
|
124
155
|
getConfig(): FallbacksConfigSummary;
|
|
156
|
+
/**
|
|
157
|
+
* Revert one role's persona to its CURRENT declared seed default (AC-3).
|
|
158
|
+
* Surfaces the outcome as VALUES, not copy (qc1 F-003 / qc2 F-006 / qc3
|
|
159
|
+
* F-003): `ok: true` when reverted; `ok: false` with the
|
|
160
|
+
* {@link SeedRevertFailReason} code explaining why not (not a seeded role
|
|
161
|
+
* / role row absent). The command handler localizes the code per its
|
|
162
|
+
* registration locale — the controller never pre-localizes. A
|
|
163
|
+
* settings-write failure propagates loudly as a rejected promise (the
|
|
164
|
+
* same contract as the seeds service — never swallowed into an
|
|
165
|
+
* `ok: false`); the handler maps it to a structured error. Implemented by
|
|
166
|
+
* the wiring against the per-apply seed manager
|
|
167
|
+
* (`seeds.revert(roleId, seedsIo)`), not the typert gateway.
|
|
168
|
+
*/
|
|
169
|
+
revertSeed(roleId: string): Promise<{
|
|
170
|
+
ok: boolean;
|
|
171
|
+
reason?: SeedRevertFailReason;
|
|
172
|
+
}>;
|
|
125
173
|
}
|
|
126
174
|
/** zh/en dictionaries for the `/fallbacks` output. */
|
|
127
175
|
export declare const FALLBACKS_COMMAND_LOCALES: {
|
|
@@ -129,7 +177,8 @@ export declare const FALLBACKS_COMMAND_LOCALES: {
|
|
|
129
177
|
readonly title: "当前会话 fallback 诊断(只读)";
|
|
130
178
|
readonly description: "查看当前会话的降级链、最近降级切换与冷却状态(只读)";
|
|
131
179
|
readonly usageConfig: "查看组合后的 fallbacks 配置(设置回读)";
|
|
132
|
-
readonly
|
|
180
|
+
readonly usageRevertSeed: "将角色的 persona 还原为已声明的 Seed 默认";
|
|
181
|
+
readonly usage: " /fallbacks config 查看组合后的 fallbacks 配置(设置回读)\n /fallbacks config revert-seed <role-id> 将角色的 persona 还原为已声明的 Seed 默认";
|
|
133
182
|
readonly origin: "会话来源";
|
|
134
183
|
readonly role: "角色";
|
|
135
184
|
readonly chain: "链";
|
|
@@ -153,22 +202,37 @@ export declare const FALLBACKS_COMMAND_LOCALES: {
|
|
|
153
202
|
readonly configTriggerCodes: "触发码";
|
|
154
203
|
readonly configRootChain: "根链";
|
|
155
204
|
readonly configEmpty: "(空)";
|
|
205
|
+
readonly configTimeSlots: "分时槽";
|
|
206
|
+
readonly configSlotPresetItem: "{preset}(chain: {n}, window {window})";
|
|
207
|
+
readonly configSlotPresetBare: "{preset}(chain: {n})";
|
|
208
|
+
readonly configSlotCustomItem: "custom {start}-{end}(chain: {n})";
|
|
209
|
+
readonly configTz: "时区";
|
|
156
210
|
readonly configRoles: "角色";
|
|
157
211
|
readonly configRoleItem: "{id}(chain: {n})";
|
|
212
|
+
readonly configRules: "角色规则";
|
|
158
213
|
readonly configCooldown: "冷却";
|
|
159
214
|
readonly configRevert: "回主策略";
|
|
160
215
|
readonly configMaxSwitches: "单步最大切换";
|
|
161
216
|
readonly configAlwaysCap: "always 上限";
|
|
162
217
|
readonly configPresets: "预置";
|
|
163
218
|
readonly configRoleAutoMatch: "角色自动匹配";
|
|
164
|
-
readonly configEdit: "
|
|
165
|
-
readonly configEditHint: "TUI
|
|
219
|
+
readonly configEdit: "编辑:/settings(TUI 设置界面)或 ~/.dsh/profiles/<profile>/cordis.patch.yml(插件行)/ $DSH_HOME/settings.yaml(fallbacks: 分节)";
|
|
220
|
+
readonly configEditHint: "TUI 通过 /settings 修改配置;文件编辑仍然可用";
|
|
221
|
+
readonly revertSeedOk: "角色 {id} 已还原为 Seed 默认";
|
|
222
|
+
readonly revertSeedFail: "角色 {id} 未还原({reason})";
|
|
223
|
+
readonly revertSeedError: "角色 {id} 还原失败(设置写入失败)";
|
|
224
|
+
readonly revertSeedReason: {
|
|
225
|
+
readonly 'not-seeded': "未声明种子";
|
|
226
|
+
readonly 'row-absent': "角色行不存在";
|
|
227
|
+
readonly 'settings-unavailable': "设置通道不可用";
|
|
228
|
+
};
|
|
166
229
|
};
|
|
167
230
|
readonly en: {
|
|
168
231
|
readonly title: "Session fallback diagnostics (read-only)";
|
|
169
232
|
readonly description: "Inspect fallback chain, recent fallback switches, and cooldown for this session (read-only)";
|
|
170
233
|
readonly usageConfig: "show the composed fallbacks config (settings readback)";
|
|
171
|
-
readonly
|
|
234
|
+
readonly usageRevertSeed: "revert a role's persona to its declared seed default";
|
|
235
|
+
readonly usage: " /fallbacks config show the composed fallbacks config (settings readback)\n /fallbacks config revert-seed <role-id> revert a role's persona to its declared seed default";
|
|
172
236
|
readonly origin: "Session origin";
|
|
173
237
|
readonly role: "Role";
|
|
174
238
|
readonly chain: "Chain";
|
|
@@ -192,29 +256,53 @@ export declare const FALLBACKS_COMMAND_LOCALES: {
|
|
|
192
256
|
readonly configTriggerCodes: "Trigger codes";
|
|
193
257
|
readonly configRootChain: "Root chain";
|
|
194
258
|
readonly configEmpty: "(empty)";
|
|
259
|
+
readonly configTimeSlots: "Time slots";
|
|
260
|
+
readonly configSlotPresetItem: "{preset} (chain: {n}, window {window})";
|
|
261
|
+
readonly configSlotPresetBare: "{preset} (chain: {n})";
|
|
262
|
+
readonly configSlotCustomItem: "custom {start}-{end} (chain: {n})";
|
|
263
|
+
readonly configTz: "TZ";
|
|
195
264
|
readonly configRoles: "Roles";
|
|
196
265
|
readonly configRoleItem: "{id} (chain: {n})";
|
|
266
|
+
readonly configRules: "Rules";
|
|
197
267
|
readonly configCooldown: "Cooldown";
|
|
198
268
|
readonly configRevert: "Revert";
|
|
199
269
|
readonly configMaxSwitches: "Max switches/step";
|
|
200
270
|
readonly configAlwaysCap: "Always-mode cap";
|
|
201
271
|
readonly configPresets: "Presets";
|
|
202
272
|
readonly configRoleAutoMatch: "Auto-match";
|
|
203
|
-
readonly configEdit: "Edit: ~/.dsh/profiles/<profile>/cordis.patch.yml (plugin row)
|
|
204
|
-
readonly configEditHint: "TUI
|
|
273
|
+
readonly configEdit: "Edit: /settings (TUI settings screen) or ~/.dsh/profiles/<profile>/cordis.patch.yml (plugin row) / $DSH_HOME/settings.yaml (fallbacks: section)";
|
|
274
|
+
readonly configEditHint: "TUI edits config via /settings; file editing still works";
|
|
275
|
+
readonly revertSeedOk: "role {id} reverted to its seed default";
|
|
276
|
+
readonly revertSeedFail: "role {id} not reverted ({reason})";
|
|
277
|
+
readonly revertSeedError: "role {id} revert failed (settings write failed)";
|
|
278
|
+
readonly revertSeedReason: {
|
|
279
|
+
readonly 'not-seeded': "not a seeded role";
|
|
280
|
+
readonly 'row-absent': "role row absent";
|
|
281
|
+
readonly 'settings-unavailable': "settings channel unavailable";
|
|
282
|
+
};
|
|
205
283
|
};
|
|
206
284
|
};
|
|
207
285
|
/** A locale id supported by {@link FALLBACKS_COMMAND_LOCALES}. */
|
|
208
286
|
export type FallbacksCommandLocale = keyof typeof FALLBACKS_COMMAND_LOCALES;
|
|
209
|
-
/** The `/fallbacks` subcommands: `'config'` (composed-config readback)
|
|
210
|
-
* `''` (
|
|
211
|
-
|
|
287
|
+
/** The `/fallbacks` subcommands: `'config'` (composed-config readback),
|
|
288
|
+
* `'revert-seed'` (persona revert action, requires a role id), or `''` (the
|
|
289
|
+
* bare session snapshot). */
|
|
290
|
+
export type FallbacksSubcommand = '' | 'config' | 'revert-seed';
|
|
291
|
+
/** One parsed subcommand invocation: the kind plus the optional role id
|
|
292
|
+
* carried by `revert-seed`. */
|
|
293
|
+
export interface FallbacksSubcommandParse {
|
|
294
|
+
readonly kind: FallbacksSubcommand;
|
|
295
|
+
/** The role id argument of `revert-seed` (present iff `kind === 'revert-seed'`). */
|
|
296
|
+
readonly arg?: string;
|
|
297
|
+
}
|
|
212
298
|
/**
|
|
213
299
|
* Map an invocation's rawInput to a subcommand: trimmed `'config'` →
|
|
214
|
-
* `'config'`;
|
|
215
|
-
*
|
|
300
|
+
* `{ kind: 'config' }`; `'config revert-seed <id>'` → `{ kind: 'revert-seed',
|
|
301
|
+
* arg: id }`; everything else (incl. empty, a missing id, or an unknown
|
|
302
|
+
* subcommand under `config`) → `{ kind: '' }` (bare snapshot). Lenient by
|
|
303
|
+
* design — unknown input keeps today's bare behavior, never errors.
|
|
216
304
|
*/
|
|
217
|
-
export declare function parseFallbacksSubcommand(rawInput: string):
|
|
305
|
+
export declare function parseFallbacksSubcommand(rawInput: string): FallbacksSubcommandParse;
|
|
218
306
|
/**
|
|
219
307
|
* The newest `limit` `fallbacks/switch` events from a session's raw event
|
|
220
308
|
* log, newest first. Unknown event shapes and malformed `fallbacks/switch`
|
|
@@ -248,18 +336,23 @@ export declare function resolveChainForDiagnostic(roles: readonly FallbacksRole[
|
|
|
248
336
|
readonly inherit: boolean;
|
|
249
337
|
};
|
|
250
338
|
/**
|
|
251
|
-
* Cap for
|
|
252
|
-
*
|
|
253
|
-
*
|
|
339
|
+
* Cap for the composed-config readback's LIST lines — Trigger codes, Root
|
|
340
|
+
* chain, Time slots, Roles, and Rules (qc2 Task-2 Minor): beyond this many
|
|
341
|
+
* items a line truncates with `…` while its leading count always stays the
|
|
342
|
+
* FULL count. Same sanity scale as {@link RECENT_SWITCHES_LIMIT}.
|
|
254
343
|
*/
|
|
255
344
|
export declare const FALLBACKS_CONFIG_LIST_CAP = 5;
|
|
256
345
|
/**
|
|
257
346
|
* Render the `/fallbacks config` surface (plan fallbacks-tui-client T2,
|
|
258
|
-
* AC-2): the composed `fallbacks`
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
347
|
+
* AC-2 + fallbacks-tui-settings Task 2 AC-4): the composed `fallbacks`
|
|
348
|
+
* namespace as the runtime reads it — enriched with the time-slot rows
|
|
349
|
+
* (preset rows resolve their frozen window from {@link PRESETS}), the
|
|
350
|
+
* config timezone, and the role rules — plus edit hints pointing at the
|
|
351
|
+
* `/settings` TUI edit surface while keeping the file-edit documentation.
|
|
352
|
+
* The FIRST LINE marks the composed-config readback — distinct from the
|
|
353
|
+
* diagnostic title and never merged into {@link fallbacksCommandText} (two
|
|
354
|
+
* operator surfaces, product lock). Locale defaults to `zh` (the command
|
|
355
|
+
* default); en dictionary tested.
|
|
263
356
|
*/
|
|
264
357
|
export declare function fallbacksConfigText(summary: FallbacksConfigSummary, locale?: FallbacksCommandLocale): string;
|
|
265
358
|
/**
|
package/dist/gateway.d.ts
CHANGED
|
@@ -214,6 +214,23 @@ export declare class FallbacksConfigGateway extends TypertRemoteService {
|
|
|
214
214
|
*/
|
|
215
215
|
private seedsIo;
|
|
216
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Reject a patch the `Config` schema cannot express: non-object input, unknown
|
|
219
|
+
* top-level keys (schemastery merges them silently — the settings service
|
|
220
|
+
* would accept them), and schema type violations — plus the Task 3 semantic
|
|
221
|
+
* guards the permissive schema deliberately does not carry: nested `roles`
|
|
222
|
+
* keys, `timeSlots` row shapes (unknown preset ids, duplicate presets, preset
|
|
223
|
+
* rows carrying windows, non-`HH:mm` custom bounds, out-of-range days, empty
|
|
224
|
+
* chains), and the all-day `rootChain` head conformance.
|
|
225
|
+
*
|
|
226
|
+
* Exported (plan fallbacks-tui-settings Task 1): the TUI settings section's
|
|
227
|
+
* JSON-field `parse` mirrors the gateway save rules through this single
|
|
228
|
+
* validator (it internally routes `timeSlots` → {@link validateTimeSlotsPatch}),
|
|
229
|
+
* so an invalid draft blocks the TUI save exactly like a rejected gateway
|
|
230
|
+
* patch. No shared-module extraction — the two-keyword diff is the minimal
|
|
231
|
+
* change.
|
|
232
|
+
*/
|
|
233
|
+
export declare function validateConfigPatch(patch: unknown): void;
|
|
217
234
|
/**
|
|
218
235
|
* The explicit typert contribution for the `fallbacks` gateway endpoints —
|
|
219
236
|
* registered via `ctx.typert.register(...)` (see `apply` in `src/index.ts`).
|