dsh-llm-fallbacks 0.1.0-alpha.4 → 0.1.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 +3 -4
- package/README.zh-CN.md +3 -4
- package/dist/client/fallbacks-store.d.ts +1 -2
- package/dist/client/index.js +283 -367
- package/dist/client/locales.d.ts +8 -4
- package/dist/config.d.ts +18 -24
- package/dist/index.d.ts +2 -1
- package/dist/index.js +8 -4
- package/dist/schema.d.ts +26 -0
- package/package.json +1 -1
- package/scripts/build-client.ts +0 -5
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ dsh plugin --profile web add dsh-llm-fallbacks # pin a version with @<version>
|
|
|
18
18
|
## Features
|
|
19
19
|
|
|
20
20
|
- **Automatic fallback for root and subagents**: any agent switches down the chain to the next available provider/model on model failure — no manual model switching.
|
|
21
|
-
- **Two-block config**: block 1 `rootChain` — the root agent's single fallback chain (empty = root does not fall back); block 2 declared roles — `roles.list` role entities (id/
|
|
21
|
+
- **Two-block config**: block 1 `rootChain` — the root agent's single fallback chain (empty = root does not fall back); block 2 declared roles — `roles.list` role entities (id/persona/chain/fallback) that `roles.rules` reference by id (or the built-in `inherit`); no rule match → `inherit` → `rootChain`.
|
|
22
22
|
- **Entry syntax**: chain entries are `provider/model` (exact switch) or `provider/*` (keep the failed model id, switch provider only) — the old chain-key namespace (provider/model keys, role-name keys) is gone.
|
|
23
23
|
- **Cooldown and revert**: models that were switched away from / failed are not re-selected during the cooldown period; `revertPolicy: cooldown-expiry` automatically returns to the primary model when the cooldown expires, while `never` does not return within the session.
|
|
24
24
|
- **Visible behavior**: every switch appends a persisted session event `fallbacks/switch` (from/to/role/reason), alongside info-level logs (candidate attempt order and skip reasons) and the read-only status block on the Settings → 插件配置 → Fallbacks card — no silent model switching.
|
|
@@ -85,8 +85,7 @@ fallbacks:
|
|
|
85
85
|
roles: # block 2: declare roles first, then let rules reference them
|
|
86
86
|
list:
|
|
87
87
|
- id: reviewer # role entity: unique id (/^[a-z0-9-]{1,32}$/); "inherit" is reserved
|
|
88
|
-
|
|
89
|
-
description: Code-review subagents
|
|
88
|
+
persona: Code-review subagents
|
|
90
89
|
chain:
|
|
91
90
|
- openai/gpt-4o-mini
|
|
92
91
|
fallback: inherit-root # default: role chain, then append rootChain
|
|
@@ -95,7 +94,7 @@ fallbacks:
|
|
|
95
94
|
role: reviewer
|
|
96
95
|
```
|
|
97
96
|
|
|
98
|
-
Roles are declared entities: `roles.list` holds role cards (id/
|
|
97
|
+
Roles are declared entities: `roles.list` holds role cards (id/persona + chain/fallback), and `roles.rules` match origin/provider/model in order to a declared role id or the built-in `inherit` (first match wins) — **no rule match → `inherit` → `rootChain`**. A declared role is only ever hit when a rule references it. A role without a chain is meaningless — the settings card blocks saving it and the host warns at startup; give every declared role a chain, or point its rules at the built-in `inherit`. (A hand-written YAML role with a missing/empty chain is not fatal: with `fallback: inherit-root` (the default) an empty chain still falls back to `rootChain`; with `fallback: none` an empty chain leaves no candidates and the request passes through untouched — either way it is only a startup warn.) The legacy chain-key namespace and role-default field are gone (migration table: [docs/configuration.md](docs/configuration.md)).
|
|
99
98
|
|
|
100
99
|
Save and restart the web session for the changes to take effect. The feature switch `fallbacks.enabled` **defaults to off (`false`)** — the plugin only engages once it is turned on; `triggerCodes` defaults to `AUTH` / `QUOTA` / `RATE_LIMIT`; and with **no `rootChain`/role chains configured the behavior is identical to not having the plugin installed**. More examples (role entities, fallback strategies, rules referencing `inherit`) → [docs/configuration.md](docs/configuration.md).
|
|
101
100
|
|
package/README.zh-CN.md
CHANGED
|
@@ -18,7 +18,7 @@ dsh plugin --profile web add dsh-llm-fallbacks # 钉版本:加 @<version>
|
|
|
18
18
|
## 能力一览
|
|
19
19
|
|
|
20
20
|
- **root / subagent 自动降级**:任意 agent 在模型故障下按链切换到下一个可用 provider/model,无需手动换模型。
|
|
21
|
-
- **两块制配置**:块 1 `rootChain`——root 主代理唯一降级链(空 = root 不降级);块 2 声明式角色——`roles.list` 角色实体(id/
|
|
21
|
+
- **两块制配置**:块 1 `rootChain`——root 主代理唯一降级链(空 = root 不降级);块 2 声明式角色——`roles.list` 角色实体(id/persona/chain/fallback)供 `roles.rules` 按 id 引用(或内置 `inherit`);未命中规则 → `inherit` → `rootChain`。
|
|
22
22
|
- **条目语法**:链条目为 `provider/model`(精确切换)或 `provider/*`(保留失败模型 id 仅换 provider)——旧链键命名空间(provider/model 键、角色名键)已删除。
|
|
23
23
|
- **冷却与回主**:被切离/失败的模型在冷却期内不再入选;`revertPolicy: cooldown-expiry` 冷却到期后自动回主模型,`never` 会话内不回。
|
|
24
24
|
- **行为可见**:每次切换追加持久化会话事件 `fallbacks/switch`(from/to/role/reason),配合 info 级日志(候选尝试顺序与跳过原因)与设置 → 插件配置 → Fallbacks 卡片上的只读状态块,无静默换模型。
|
|
@@ -83,8 +83,7 @@ fallbacks:
|
|
|
83
83
|
roles: # 块 2:先声明角色,再让规则引用
|
|
84
84
|
list:
|
|
85
85
|
- id: reviewer # 角色实体:id 唯一(/^[a-z0-9-]{1,32}$/);inherit 为保留字
|
|
86
|
-
|
|
87
|
-
description: 代码审查子代理
|
|
86
|
+
persona: 代码审查子代理
|
|
88
87
|
chain:
|
|
89
88
|
- openai/gpt-4o-mini
|
|
90
89
|
fallback: inherit-root # 默认:角色链后追加 rootChain
|
|
@@ -93,7 +92,7 @@ fallbacks:
|
|
|
93
92
|
role: reviewer
|
|
94
93
|
```
|
|
95
94
|
|
|
96
|
-
角色是声明式实体:`roles.list` 存放角色卡(id/
|
|
95
|
+
角色是声明式实体:`roles.list` 存放角色卡(id/persona + chain/fallback),`roles.rules` 按 origin/provider/model 顺序匹配到**已声明角色 id 或内置 `inherit`**(首个命中即停)——**未命中 → `inherit` → `rootChain`**。只声明不写规则的角色永不命中。角色无 chain 无意义——设置卡拦截保存、启动时告警;请为每个声明角色配置 chain,或让规则直接引用内置 `inherit`。(手写 YAML 角色链缺省/为空不致命:`fallback: inherit-root`(默认)时空链仍按防御语义回退 `rootChain`;`fallback: none` 时空链无候选 → no-op 透传;两种情况都仅启动告警。)旧链键命名空间与角色兜底字段已删除(迁移表见 [docs/configuration.md](docs/configuration.md))。
|
|
97
96
|
|
|
98
97
|
保存并重启 web 会话后生效。功能级开关 `fallbacks.enabled` **默认关闭(`false`)**——打开开关后插件才会介入;`triggerCodes` 默认覆盖 `AUTH` / `QUOTA` / `RATE_LIMIT`;**未配置任何 `rootChain`/角色链时行为与未安装插件完全一致**。更多示例(角色实体、fallback 策略、引用 `inherit` 的规则)见 [docs/configuration.md](docs/configuration.md)。
|
|
99
98
|
|
|
@@ -210,8 +210,7 @@ export declare function rowsToRootChain(rows: readonly RootChainRow[]): string[]
|
|
|
210
210
|
*/
|
|
211
211
|
export interface RoleRow {
|
|
212
212
|
id: string;
|
|
213
|
-
|
|
214
|
-
description: string;
|
|
213
|
+
persona: string;
|
|
215
214
|
selectors: ChainSelectorRow[];
|
|
216
215
|
fallback: FallbackStrategy;
|
|
217
216
|
}
|
package/dist/client/index.js
CHANGED
|
@@ -4,33 +4,9 @@ window.__ModuleLoader__.load({
|
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
-
//#region \0rolldown/runtime.js
|
|
8
|
-
var __create = Object.create;
|
|
9
|
-
var __defProp = Object.defineProperty;
|
|
10
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
11
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
12
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
13
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
-
var __copyProps = (to, from, except, desc) => {
|
|
15
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
16
|
-
key = keys[i];
|
|
17
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
18
|
-
get: ((k) => from[k]).bind(null, key),
|
|
19
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
return to;
|
|
23
|
-
};
|
|
24
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
25
|
-
value: mod,
|
|
26
|
-
enumerable: true
|
|
27
|
-
}) : target, mod));
|
|
28
|
-
//#endregion
|
|
29
7
|
let _deepseek_ai_dsh_client_web_react = require("@deepseek-ai/dsh-client-web-react");
|
|
30
8
|
let react = require("react");
|
|
31
9
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
32
|
-
let _deepseek_ai_schemastery = require("@deepseek-ai/schemastery");
|
|
33
|
-
_deepseek_ai_schemastery = __toESM(_deepseek_ai_schemastery, 1);
|
|
34
10
|
let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
|
|
35
11
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
36
12
|
//#region src/selectors.ts
|
|
@@ -65,32 +41,6 @@ window.__ModuleLoader__.load({
|
|
|
65
41
|
//#endregion
|
|
66
42
|
//#region src/config.ts
|
|
67
43
|
/**
|
|
68
|
-
* The `fallbacks` settings namespace: plugin config schema + defaults.
|
|
69
|
-
*
|
|
70
|
-
* Two-block config model (plan fallbacks-role-config-model): block 1
|
|
71
|
-
* `rootChain` — the root agent's single fallback chain (empty = no
|
|
72
|
-
* degradation) — plus block 2 declared role entities: `roles.list`
|
|
73
|
-
* (id/label/description/prompt?/permissions?/chain?/fallback) and
|
|
74
|
-
* `roles.rules` enum references into the declared ids (or the built-in
|
|
75
|
-
* `'inherit'` role). The legacy `chains` / `roles.default` keys are gone
|
|
76
|
-
* from the schema and type (zero residual, migration table excepted); the
|
|
77
|
-
* runtime consumes the new shape directly and flags surviving legacy keys
|
|
78
|
-
* at startup via `detectLegacyKeys` (see `src/index.ts` apply()).
|
|
79
|
-
*
|
|
80
|
-
* Spec §4 is authoritative for field names and default values — notably
|
|
81
|
-
* `triggerCodes` defaults to dsh's stable failure codes `['AUTH', 'QUOTA',
|
|
82
|
-
* 'RATE_LIMIT']` (there is no `QUOTA_EXCEEDED` code in dsh), and an
|
|
83
|
-
* unconfigured install (`enabled: false`, empty `rootChain`, empty roles)
|
|
84
|
-
* is a no-op pass-through exactly like an uninstalled plugin (AC-8).
|
|
85
|
-
*
|
|
86
|
-
* This module is pure logic: it must not import any `@deepseek-ai/*` package
|
|
87
|
-
* (types included) — `FallbacksConfig` is the plugin's own type. Task 3
|
|
88
|
-
* registers this schema with `installSettingsSection` under the `fallbacks`
|
|
89
|
-
* settings namespace.
|
|
90
|
-
*
|
|
91
|
-
* @module dsh-llm-fallbacks/config
|
|
92
|
-
*/
|
|
93
|
-
/**
|
|
94
44
|
* Spec §4 defaults — `Config({})` must equal this (no-op install).
|
|
95
45
|
* `enabled` defaults to `false` (readme-settings spec §1.2): the feature
|
|
96
46
|
* switch is off until the user turns it on in the settings page; an
|
|
@@ -121,42 +71,6 @@ window.__ModuleLoader__.load({
|
|
|
121
71
|
const INHERIT_ROLE_ID = "inherit";
|
|
122
72
|
/** Role id format (aligned with yet-another-subagent `isValidProfileId`). */
|
|
123
73
|
const ROLE_ID_PATTERN = /^[a-z0-9-]{1,32}$/;
|
|
124
|
-
_deepseek_ai_schemastery.default.object({
|
|
125
|
-
enabled: _deepseek_ai_schemastery.default.boolean().default(false),
|
|
126
|
-
triggerCodes: _deepseek_ai_schemastery.default.array(_deepseek_ai_schemastery.default.string()).default([
|
|
127
|
-
"AUTH",
|
|
128
|
-
"QUOTA",
|
|
129
|
-
"RATE_LIMIT"
|
|
130
|
-
]),
|
|
131
|
-
rootChain: _deepseek_ai_schemastery.default.array(_deepseek_ai_schemastery.default.string()).default([]),
|
|
132
|
-
roles: _deepseek_ai_schemastery.default.object({
|
|
133
|
-
list: _deepseek_ai_schemastery.default.array(_deepseek_ai_schemastery.default.object({
|
|
134
|
-
id: _deepseek_ai_schemastery.default.string().required(),
|
|
135
|
-
label: _deepseek_ai_schemastery.default.string().default(""),
|
|
136
|
-
description: _deepseek_ai_schemastery.default.string().default(""),
|
|
137
|
-
prompt: _deepseek_ai_schemastery.default.string(),
|
|
138
|
-
permissions: _deepseek_ai_schemastery.default.object({
|
|
139
|
-
allow: _deepseek_ai_schemastery.default.array(_deepseek_ai_schemastery.default.string()),
|
|
140
|
-
deny: _deepseek_ai_schemastery.default.array(_deepseek_ai_schemastery.default.string())
|
|
141
|
-
}),
|
|
142
|
-
chain: _deepseek_ai_schemastery.default.array(_deepseek_ai_schemastery.default.string()),
|
|
143
|
-
fallback: _deepseek_ai_schemastery.default.union([_deepseek_ai_schemastery.default.const("inherit-root"), _deepseek_ai_schemastery.default.const("none")]).default("inherit-root")
|
|
144
|
-
})).default([]),
|
|
145
|
-
rules: _deepseek_ai_schemastery.default.array(_deepseek_ai_schemastery.default.object({
|
|
146
|
-
origin: _deepseek_ai_schemastery.default.union([_deepseek_ai_schemastery.default.const("root"), _deepseek_ai_schemastery.default.const("subagent")]),
|
|
147
|
-
provider: _deepseek_ai_schemastery.default.string(),
|
|
148
|
-
model: _deepseek_ai_schemastery.default.string(),
|
|
149
|
-
role: _deepseek_ai_schemastery.default.string().required()
|
|
150
|
-
})).default([])
|
|
151
|
-
}).default({
|
|
152
|
-
list: [],
|
|
153
|
-
rules: []
|
|
154
|
-
}),
|
|
155
|
-
cooldownMs: _deepseek_ai_schemastery.default.number().default(3e5),
|
|
156
|
-
revertPolicy: _deepseek_ai_schemastery.default.union([_deepseek_ai_schemastery.default.const("cooldown-expiry"), _deepseek_ai_schemastery.default.const("never")]).default("cooldown-expiry"),
|
|
157
|
-
maxSwitchesPerStep: _deepseek_ai_schemastery.default.number().default(8),
|
|
158
|
-
alwaysModeRetryCap: _deepseek_ai_schemastery.default.number().default(5)
|
|
159
|
-
});
|
|
160
74
|
//#endregion
|
|
161
75
|
//#region src/client/fallbacks-store.ts
|
|
162
76
|
/** The plugin's settings namespace on the host wire (settings/document-updated ns filter). */
|
|
@@ -215,10 +129,8 @@ window.__ModuleLoader__.load({
|
|
|
215
129
|
const roles = isRecord(value.roles) ? value.roles : {};
|
|
216
130
|
const parsedList = (Array.isArray(roles.list) ? roles.list : []).map((role, index) => {
|
|
217
131
|
if (!isRecord(role) || typeof role.id !== "string") throw new TypeError(`fallbacks descriptor roles.list[${String(index)}] must have a string id`);
|
|
218
|
-
const
|
|
219
|
-
if (
|
|
220
|
-
const description = role.description;
|
|
221
|
-
if (description !== void 0 && typeof description !== "string") throw new TypeError(`fallbacks descriptor roles.list[${String(index)}].description must be a string`);
|
|
132
|
+
const persona = role.persona;
|
|
133
|
+
if (persona !== void 0 && typeof persona !== "string") throw new TypeError(`fallbacks descriptor roles.list[${String(index)}].persona must be a string`);
|
|
222
134
|
const prompt = role.prompt;
|
|
223
135
|
if (prompt !== void 0 && typeof prompt !== "string") throw new TypeError(`fallbacks descriptor roles.list[${String(index)}].prompt must be a string`);
|
|
224
136
|
const permissions = role.permissions;
|
|
@@ -229,8 +141,7 @@ window.__ModuleLoader__.load({
|
|
|
229
141
|
if (fallback !== void 0 && fallback !== "inherit-root" && fallback !== "none") throw new TypeError(`fallbacks descriptor roles.list[${String(index)}].fallback must be inherit-root|none`);
|
|
230
142
|
return {
|
|
231
143
|
id: role.id,
|
|
232
|
-
|
|
233
|
-
description: description ?? "",
|
|
144
|
+
persona: persona ?? "",
|
|
234
145
|
...prompt === void 0 ? {} : { prompt },
|
|
235
146
|
...permissions === void 0 ? {} : { permissions },
|
|
236
147
|
chain: chain ?? [],
|
|
@@ -421,8 +332,7 @@ window.__ModuleLoader__.load({
|
|
|
421
332
|
function rolesToRows(roles, catalog) {
|
|
422
333
|
return roles.map((role) => ({
|
|
423
334
|
id: role.id,
|
|
424
|
-
|
|
425
|
-
description: role.description,
|
|
335
|
+
persona: role.persona,
|
|
426
336
|
selectors: (role.chain ?? []).map((entry) => entryToSelectorRow(entry, catalog)),
|
|
427
337
|
fallback: role.fallback ?? "inherit-root"
|
|
428
338
|
}));
|
|
@@ -431,8 +341,7 @@ window.__ModuleLoader__.load({
|
|
|
431
341
|
function rowsToRoles(rows) {
|
|
432
342
|
return rows.map((row) => ({
|
|
433
343
|
id: row.id.trim(),
|
|
434
|
-
|
|
435
|
-
description: row.description,
|
|
344
|
+
persona: row.persona,
|
|
436
345
|
chain: row.selectors.map(selectorRowToRaw).filter((entry) => entry !== ""),
|
|
437
346
|
fallback: row.fallback
|
|
438
347
|
}));
|
|
@@ -454,8 +363,7 @@ window.__ModuleLoader__.load({
|
|
|
454
363
|
if (original === void 0) return role;
|
|
455
364
|
return {
|
|
456
365
|
id: role.id,
|
|
457
|
-
|
|
458
|
-
description: role.description,
|
|
366
|
+
persona: role.persona,
|
|
459
367
|
...original.prompt === void 0 ? {} : { prompt: original.prompt },
|
|
460
368
|
...original.permissions === void 0 ? {} : { permissions: original.permissions },
|
|
461
369
|
chain: role.chain,
|
|
@@ -850,6 +758,7 @@ window.__ModuleLoader__.load({
|
|
|
850
758
|
"alwaysModeRetryCap.label": "always 模式重试上限",
|
|
851
759
|
"alwaysModeRetryCap.hint": "达到上限次数后切换;0 表示禁用",
|
|
852
760
|
"alwaysModeRetryCap.tooltip": "retryPolicy 为 always 的模型在同一请求内重试达到该次数后切换;0 表示禁用。",
|
|
761
|
+
"advanced.label": "高级选项",
|
|
853
762
|
"rootChain.label": "root 主代理降级链",
|
|
854
763
|
"rootChain.hint": "未配置 = root 不降级",
|
|
855
764
|
"rootChain.tooltip": "root 主代理失败时按此有序选择器列表依次降级;未配置时行为与未安装插件一致。",
|
|
@@ -859,14 +768,15 @@ window.__ModuleLoader__.load({
|
|
|
859
768
|
"chains.selector.modelPlaceholder": "选择 model",
|
|
860
769
|
"chains.selector.wildcard": "通配该 provider(provider/*)",
|
|
861
770
|
"chains.selector.noModels": "该 provider 暂无可用模型(目录查询失败),请使用通配或改选。",
|
|
771
|
+
"chains.selector.noModelsStrict": "该 provider 暂无可用模型(目录查询失败),请改选其他 provider。",
|
|
862
772
|
"roles.list.label": "角色实体",
|
|
863
773
|
"roles.list.hint": "先声明角色,规则才能引用",
|
|
864
774
|
"roles.list.tooltip": "角色 id 须匹配 /^[a-z0-9-]{1,32}$/ 且唯一;\"inherit\" 为保留字,不能用作角色 id。",
|
|
865
775
|
"roles.id": "id",
|
|
866
776
|
"roles.id.hint": "小写字母/数字/连字符,1–32 字符",
|
|
867
777
|
"roles.idPlaceholder": "例如 reviewer",
|
|
868
|
-
"roles.
|
|
869
|
-
"roles.
|
|
778
|
+
"roles.persona": "人格提示",
|
|
779
|
+
"roles.personaPlaceholder": "例如:你是资深代码审查员",
|
|
870
780
|
"roles.fallback": "链拼接策略",
|
|
871
781
|
"roles.fallback.inherit-root": "继承 root(角色链后追加 rootChain)",
|
|
872
782
|
"roles.fallback.none": "仅角色链(不追加 rootChain)",
|
|
@@ -977,6 +887,7 @@ window.__ModuleLoader__.load({
|
|
|
977
887
|
"alwaysModeRetryCap.label": "Always-mode retry cap",
|
|
978
888
|
"alwaysModeRetryCap.hint": "Switches after the cap; 0 disables",
|
|
979
889
|
"alwaysModeRetryCap.tooltip": "Models whose retryPolicy is always switch after this many retries within one request; 0 disables.",
|
|
890
|
+
"advanced.label": "Advanced options",
|
|
980
891
|
"rootChain.label": "Root agent fallback chain",
|
|
981
892
|
"rootChain.hint": "Unset = root does not fall back",
|
|
982
893
|
"rootChain.tooltip": "When the root agent fails it falls back down this ordered selector list; unset behaves like an uninstalled plugin.",
|
|
@@ -986,14 +897,15 @@ window.__ModuleLoader__.load({
|
|
|
986
897
|
"chains.selector.modelPlaceholder": "Select model",
|
|
987
898
|
"chains.selector.wildcard": "Wildcard this provider (provider/*)",
|
|
988
899
|
"chains.selector.noModels": "No models available for this provider (catalog lookup failed); use the wildcard or pick another provider.",
|
|
900
|
+
"chains.selector.noModelsStrict": "No models available for this provider (catalog lookup failed); pick another provider.",
|
|
989
901
|
"roles.list.label": "Declared roles",
|
|
990
902
|
"roles.list.hint": "Declare roles before rules can reference them",
|
|
991
903
|
"roles.list.tooltip": "Role ids must match /^[a-z0-9-]{1,32}$/ and be unique; \"inherit\" is reserved and cannot be used as a role id.",
|
|
992
904
|
"roles.id": "ID",
|
|
993
905
|
"roles.id.hint": "lowercase letters, digits, hyphens; 1–32 chars",
|
|
994
906
|
"roles.idPlaceholder": "e.g. reviewer",
|
|
995
|
-
"roles.
|
|
996
|
-
"roles.
|
|
907
|
+
"roles.persona": "Persona",
|
|
908
|
+
"roles.personaPlaceholder": "e.g. you are a senior code reviewer",
|
|
997
909
|
"roles.fallback": "Chain append",
|
|
998
910
|
"roles.fallback.inherit-root": "Inherit root (append rootChain after the role chain)",
|
|
999
911
|
"roles.fallback.none": "Role chain only (no rootChain)",
|
|
@@ -1277,7 +1189,7 @@ window.__ModuleLoader__.load({
|
|
|
1277
1189
|
* itself constrains normal edits), and illegal selector entries in
|
|
1278
1190
|
* rootChain and role chains. Returns one localized message per violation;
|
|
1279
1191
|
* a non-empty result blocks {@link save} — the draft is never written.
|
|
1280
|
-
* `
|
|
1192
|
+
* `persona` is free text and never validated.
|
|
1281
1193
|
*/
|
|
1282
1194
|
function validateDraft(draft, t) {
|
|
1283
1195
|
const errors = [];
|
|
@@ -1377,7 +1289,10 @@ window.__ModuleLoader__.load({
|
|
|
1377
1289
|
}
|
|
1378
1290
|
/**
|
|
1379
1291
|
* One chain entry selector row: provider select + model select (cascade) +
|
|
1380
|
-
* wildcard checkbox (spec §2.5 D-3).
|
|
1292
|
+
* wildcard checkbox (spec §2.5 D-3). `allowWildcard` gates the `provider/*`
|
|
1293
|
+
* checkbox — the root agent's chain editor passes false (the main agent's
|
|
1294
|
+
* chain stays provider/model lines; provider-any matching lives in the role
|
|
1295
|
+
* rules), role chain editors keep it. The provider options are the catalog
|
|
1381
1296
|
* providers **configured on the Models page** (`configuredProviders`, the
|
|
1382
1297
|
* Models-page `configured` join) — unconfigured directory providers never
|
|
1383
1298
|
* become offerable. Out-of-catalog values read back from the server render as
|
|
@@ -1388,7 +1303,7 @@ window.__ModuleLoader__.load({
|
|
|
1388
1303
|
* existing value is never hidden or dropped. New rows only offer configured
|
|
1389
1304
|
* options.
|
|
1390
1305
|
*/
|
|
1391
|
-
function ChainSelectorEditor({ selector, catalog, configuredProviders, disabled, t, onChange, onRemove }) {
|
|
1306
|
+
function ChainSelectorEditor({ selector, catalog, configuredProviders, disabled, t, onChange, onRemove, allowWildcard = true }) {
|
|
1392
1307
|
const providerRaw = selectionToRaw(selector.provider);
|
|
1393
1308
|
const providerOutside = selector.provider?.kind === "outside";
|
|
1394
1309
|
const providerUnconfigured = !providerOutside && providerRaw !== "" && (catalog?.providers.some((entry) => entry.provider === providerRaw) ?? false) && !configuredProviders.some((entry) => entry.provider === providerRaw);
|
|
@@ -1470,11 +1385,11 @@ window.__ModuleLoader__.load({
|
|
|
1470
1385
|
}),
|
|
1471
1386
|
groupMissing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1472
1387
|
className: FallbacksCard_module_css_default.hint,
|
|
1473
|
-
children: t("chains.selector.noModels")
|
|
1388
|
+
children: t(allowWildcard ? "chains.selector.noModels" : "chains.selector.noModelsStrict")
|
|
1474
1389
|
})
|
|
1475
1390
|
]
|
|
1476
1391
|
}),
|
|
1477
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1392
|
+
allowWildcard && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1478
1393
|
className: `${FallbacksCard_module_css_default.ruleCell} ${FallbacksCard_module_css_default.wildcardCell}`,
|
|
1479
1394
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1480
1395
|
type: "checkbox",
|
|
@@ -1624,8 +1539,7 @@ window.__ModuleLoader__.load({
|
|
|
1624
1539
|
const addRole = () => {
|
|
1625
1540
|
setRoleRows((rows) => [...rows, {
|
|
1626
1541
|
id: "",
|
|
1627
|
-
|
|
1628
|
-
description: "",
|
|
1542
|
+
persona: "",
|
|
1629
1543
|
selectors: [],
|
|
1630
1544
|
fallback: "inherit-root"
|
|
1631
1545
|
}]);
|
|
@@ -1841,211 +1755,6 @@ window.__ModuleLoader__.load({
|
|
|
1841
1755
|
className: FallbacksCard_module_css_default.fieldset,
|
|
1842
1756
|
disabled: !writable,
|
|
1843
1757
|
children: [
|
|
1844
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1845
|
-
className: FallbacksCard_module_css_default.field,
|
|
1846
|
-
role: "group",
|
|
1847
|
-
"aria-labelledby": "fallbacks-trigger-codes",
|
|
1848
|
-
children: [
|
|
1849
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1850
|
-
className: FallbacksCard_module_css_default.fieldLabel,
|
|
1851
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1852
|
-
id: "fallbacks-trigger-codes",
|
|
1853
|
-
children: t("triggerCodes.label")
|
|
1854
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
1855
|
-
label: t("triggerCodes.tooltip"),
|
|
1856
|
-
disabled: !writable
|
|
1857
|
-
})]
|
|
1858
|
-
}),
|
|
1859
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1860
|
-
className: FallbacksCard_module_css_default.hint,
|
|
1861
|
-
children: t("triggerCodes.hint")
|
|
1862
|
-
}),
|
|
1863
|
-
KNOWN_TRIGGER_CODES.map((code) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1864
|
-
className: FallbacksCard_module_css_default.optionRow,
|
|
1865
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1866
|
-
type: "checkbox",
|
|
1867
|
-
checked: scalars.triggerCodes.includes(code),
|
|
1868
|
-
onChange: (event) => {
|
|
1869
|
-
updateScalars((draft) => {
|
|
1870
|
-
draft.triggerCodes = withTriggerCode(draft.triggerCodes, code, event.target.checked);
|
|
1871
|
-
});
|
|
1872
|
-
}
|
|
1873
|
-
}), t(TRIGGER_CODE_LABELS[code])]
|
|
1874
|
-
}, code)),
|
|
1875
|
-
unknownCodes.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1876
|
-
className: FallbacksCard_module_css_default.hint,
|
|
1877
|
-
children: t("triggerCodes.extra", { codes: unknownCodes.join(", ") })
|
|
1878
|
-
})
|
|
1879
|
-
]
|
|
1880
|
-
}),
|
|
1881
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1882
|
-
className: FallbacksCard_module_css_default.field,
|
|
1883
|
-
role: "group",
|
|
1884
|
-
"aria-labelledby": "fallbacks-revert-policy",
|
|
1885
|
-
children: [
|
|
1886
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1887
|
-
className: FallbacksCard_module_css_default.fieldLabel,
|
|
1888
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1889
|
-
id: "fallbacks-revert-policy",
|
|
1890
|
-
children: t("revertPolicy.label")
|
|
1891
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
1892
|
-
label: t("revertPolicy.tooltip"),
|
|
1893
|
-
disabled: !writable
|
|
1894
|
-
})]
|
|
1895
|
-
}),
|
|
1896
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1897
|
-
className: FallbacksCard_module_css_default.hint,
|
|
1898
|
-
children: t("revertPolicy.hint")
|
|
1899
|
-
}),
|
|
1900
|
-
["cooldown-expiry", "never"].map((policy) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1901
|
-
className: FallbacksCard_module_css_default.optionRow,
|
|
1902
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1903
|
-
type: "radio",
|
|
1904
|
-
name: "fallbacks-revert-policy",
|
|
1905
|
-
checked: scalars.revertPolicy === policy,
|
|
1906
|
-
onChange: () => {
|
|
1907
|
-
updateScalars((draft) => {
|
|
1908
|
-
draft.revertPolicy = policy;
|
|
1909
|
-
});
|
|
1910
|
-
}
|
|
1911
|
-
}), t(`revertPolicy.${policy}`)]
|
|
1912
|
-
}, policy))
|
|
1913
|
-
]
|
|
1914
|
-
}),
|
|
1915
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1916
|
-
className: FallbacksCard_module_css_default.numberFields,
|
|
1917
|
-
children: [
|
|
1918
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1919
|
-
className: FallbacksCard_module_css_default.field,
|
|
1920
|
-
children: [
|
|
1921
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1922
|
-
className: FallbacksCard_module_css_default.fieldLabel,
|
|
1923
|
-
children: [
|
|
1924
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
1925
|
-
htmlFor: "fallbacks-cooldown-ms",
|
|
1926
|
-
children: t("cooldownMs.label")
|
|
1927
|
-
}),
|
|
1928
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
1929
|
-
label: t("cooldownMs.tooltip"),
|
|
1930
|
-
disabled: !writable
|
|
1931
|
-
}),
|
|
1932
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1933
|
-
className: FallbacksCard_module_css_default.defaultNote,
|
|
1934
|
-
children: [
|
|
1935
|
-
t("defaults.prefix"),
|
|
1936
|
-
": ",
|
|
1937
|
-
state.config.cooldownMs
|
|
1938
|
-
]
|
|
1939
|
-
})
|
|
1940
|
-
]
|
|
1941
|
-
}),
|
|
1942
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1943
|
-
id: "fallbacks-cooldown-ms",
|
|
1944
|
-
className: FallbacksCard_module_css_default.input,
|
|
1945
|
-
type: "number",
|
|
1946
|
-
min: 0,
|
|
1947
|
-
value: String(scalars.cooldownMs),
|
|
1948
|
-
disabled: !writable,
|
|
1949
|
-
onChange: (event) => {
|
|
1950
|
-
updateScalars((draft) => {
|
|
1951
|
-
draft.cooldownMs = parseCount(event.target.value);
|
|
1952
|
-
});
|
|
1953
|
-
}
|
|
1954
|
-
}),
|
|
1955
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1956
|
-
className: FallbacksCard_module_css_default.hint,
|
|
1957
|
-
children: t("cooldownMs.hint")
|
|
1958
|
-
})
|
|
1959
|
-
]
|
|
1960
|
-
}),
|
|
1961
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1962
|
-
className: FallbacksCard_module_css_default.field,
|
|
1963
|
-
children: [
|
|
1964
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1965
|
-
className: FallbacksCard_module_css_default.fieldLabel,
|
|
1966
|
-
children: [
|
|
1967
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
1968
|
-
htmlFor: "fallbacks-max-switches",
|
|
1969
|
-
children: t("maxSwitchesPerStep.label")
|
|
1970
|
-
}),
|
|
1971
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
1972
|
-
label: t("maxSwitchesPerStep.tooltip"),
|
|
1973
|
-
disabled: !writable
|
|
1974
|
-
}),
|
|
1975
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1976
|
-
className: FallbacksCard_module_css_default.defaultNote,
|
|
1977
|
-
children: [
|
|
1978
|
-
t("defaults.prefix"),
|
|
1979
|
-
": ",
|
|
1980
|
-
state.config.maxSwitchesPerStep
|
|
1981
|
-
]
|
|
1982
|
-
})
|
|
1983
|
-
]
|
|
1984
|
-
}),
|
|
1985
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1986
|
-
id: "fallbacks-max-switches",
|
|
1987
|
-
className: FallbacksCard_module_css_default.input,
|
|
1988
|
-
type: "number",
|
|
1989
|
-
min: 0,
|
|
1990
|
-
value: String(scalars.maxSwitchesPerStep),
|
|
1991
|
-
disabled: !writable,
|
|
1992
|
-
onChange: (event) => {
|
|
1993
|
-
updateScalars((draft) => {
|
|
1994
|
-
draft.maxSwitchesPerStep = parseCount(event.target.value);
|
|
1995
|
-
});
|
|
1996
|
-
}
|
|
1997
|
-
}),
|
|
1998
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1999
|
-
className: FallbacksCard_module_css_default.hint,
|
|
2000
|
-
children: t("maxSwitchesPerStep.hint")
|
|
2001
|
-
})
|
|
2002
|
-
]
|
|
2003
|
-
}),
|
|
2004
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2005
|
-
className: FallbacksCard_module_css_default.field,
|
|
2006
|
-
children: [
|
|
2007
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2008
|
-
className: FallbacksCard_module_css_default.fieldLabel,
|
|
2009
|
-
children: [
|
|
2010
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2011
|
-
htmlFor: "fallbacks-always-cap",
|
|
2012
|
-
children: t("alwaysModeRetryCap.label")
|
|
2013
|
-
}),
|
|
2014
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
2015
|
-
label: t("alwaysModeRetryCap.tooltip"),
|
|
2016
|
-
disabled: !writable
|
|
2017
|
-
}),
|
|
2018
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2019
|
-
className: FallbacksCard_module_css_default.defaultNote,
|
|
2020
|
-
children: [
|
|
2021
|
-
t("defaults.prefix"),
|
|
2022
|
-
": ",
|
|
2023
|
-
state.config.alwaysModeRetryCap
|
|
2024
|
-
]
|
|
2025
|
-
})
|
|
2026
|
-
]
|
|
2027
|
-
}),
|
|
2028
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2029
|
-
id: "fallbacks-always-cap",
|
|
2030
|
-
className: FallbacksCard_module_css_default.input,
|
|
2031
|
-
type: "number",
|
|
2032
|
-
min: 0,
|
|
2033
|
-
value: String(scalars.alwaysModeRetryCap),
|
|
2034
|
-
disabled: !writable,
|
|
2035
|
-
onChange: (event) => {
|
|
2036
|
-
updateScalars((draft) => {
|
|
2037
|
-
draft.alwaysModeRetryCap = parseCount(event.target.value);
|
|
2038
|
-
});
|
|
2039
|
-
}
|
|
2040
|
-
}),
|
|
2041
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2042
|
-
className: FallbacksCard_module_css_default.hint,
|
|
2043
|
-
children: t("alwaysModeRetryCap.hint")
|
|
2044
|
-
})
|
|
2045
|
-
]
|
|
2046
|
-
})
|
|
2047
|
-
]
|
|
2048
|
-
}),
|
|
2049
1758
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2050
1759
|
className: FallbacksCard_module_css_default.field,
|
|
2051
1760
|
role: "group",
|
|
@@ -2089,6 +1798,7 @@ window.__ModuleLoader__.load({
|
|
|
2089
1798
|
configuredProviders: state.configuredProviders,
|
|
2090
1799
|
disabled: !writable,
|
|
2091
1800
|
t,
|
|
1801
|
+
allowWildcard: false,
|
|
2092
1802
|
onChange: (patch) => {
|
|
2093
1803
|
updateRootChainSelector(selectorIndex, patch);
|
|
2094
1804
|
},
|
|
@@ -2134,64 +1844,51 @@ window.__ModuleLoader__.load({
|
|
|
2134
1844
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2135
1845
|
className: FallbacksCard_module_css_default.editorCard,
|
|
2136
1846
|
children: [
|
|
2137
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1847
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2138
1848
|
className: FallbacksCard_module_css_default.ruleGrid,
|
|
2139
|
-
children:
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2144
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
2145
|
-
children: t("roles.id")
|
|
2146
|
-
}),
|
|
2147
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2148
|
-
className: `${FallbacksCard_module_css_default.input} ${invalid ? FallbacksCard_module_css_default.inputInvalid : ""}`,
|
|
2149
|
-
value: row.id,
|
|
2150
|
-
placeholder: t("roles.idPlaceholder"),
|
|
2151
|
-
"aria-label": t("roles.id"),
|
|
2152
|
-
"aria-invalid": invalid ? true : void 0,
|
|
2153
|
-
disabled: !writable,
|
|
2154
|
-
onChange: (event) => {
|
|
2155
|
-
updateRoleRow(index, { id: event.target.value });
|
|
2156
|
-
}
|
|
2157
|
-
}),
|
|
2158
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2159
|
-
className: FallbacksCard_module_css_default.hint,
|
|
2160
|
-
children: t("roles.id.hint")
|
|
2161
|
-
})
|
|
2162
|
-
]
|
|
2163
|
-
}),
|
|
2164
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2165
|
-
className: FallbacksCard_module_css_default.ruleCell,
|
|
2166
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2167
|
-
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
2168
|
-
children: t("roles.label")
|
|
2169
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2170
|
-
className: FallbacksCard_module_css_default.input,
|
|
2171
|
-
value: row.label,
|
|
2172
|
-
"aria-label": t("roles.label"),
|
|
2173
|
-
disabled: !writable,
|
|
2174
|
-
onChange: (event) => {
|
|
2175
|
-
updateRoleRow(index, { label: event.target.value });
|
|
2176
|
-
}
|
|
2177
|
-
})]
|
|
2178
|
-
}),
|
|
2179
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2180
|
-
className: FallbacksCard_module_css_default.ruleCell,
|
|
2181
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1849
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1850
|
+
className: FallbacksCard_module_css_default.ruleCell,
|
|
1851
|
+
children: [
|
|
1852
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2182
1853
|
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
2183
|
-
children: t("roles.
|
|
2184
|
-
}),
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
1854
|
+
children: t("roles.id")
|
|
1855
|
+
}),
|
|
1856
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1857
|
+
className: `${FallbacksCard_module_css_default.input} ${invalid ? FallbacksCard_module_css_default.inputInvalid : ""}`,
|
|
1858
|
+
value: row.id,
|
|
1859
|
+
placeholder: t("roles.idPlaceholder"),
|
|
1860
|
+
"aria-label": t("roles.id"),
|
|
1861
|
+
"aria-invalid": invalid ? true : void 0,
|
|
2188
1862
|
disabled: !writable,
|
|
2189
1863
|
onChange: (event) => {
|
|
2190
|
-
updateRoleRow(index, {
|
|
1864
|
+
updateRoleRow(index, { id: event.target.value });
|
|
2191
1865
|
}
|
|
2192
|
-
})
|
|
2193
|
-
|
|
2194
|
-
|
|
1866
|
+
}),
|
|
1867
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1868
|
+
className: FallbacksCard_module_css_default.hint,
|
|
1869
|
+
children: t("roles.id.hint")
|
|
1870
|
+
})
|
|
1871
|
+
]
|
|
1872
|
+
})
|
|
1873
|
+
}),
|
|
1874
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1875
|
+
className: FallbacksCard_module_css_default.ruleGrid,
|
|
1876
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1877
|
+
className: FallbacksCard_module_css_default.ruleCell,
|
|
1878
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1879
|
+
className: FallbacksCard_module_css_default.ruleCellLabel,
|
|
1880
|
+
children: t("roles.persona")
|
|
1881
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1882
|
+
className: FallbacksCard_module_css_default.input,
|
|
1883
|
+
value: row.persona,
|
|
1884
|
+
placeholder: t("roles.personaPlaceholder"),
|
|
1885
|
+
"aria-label": t("roles.persona"),
|
|
1886
|
+
disabled: !writable,
|
|
1887
|
+
onChange: (event) => {
|
|
1888
|
+
updateRoleRow(index, { persona: event.target.value });
|
|
1889
|
+
}
|
|
1890
|
+
})]
|
|
1891
|
+
})
|
|
2195
1892
|
}),
|
|
2196
1893
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2197
1894
|
className: FallbacksCard_module_css_default.chainSelectors,
|
|
@@ -2484,6 +2181,225 @@ window.__ModuleLoader__.load({
|
|
|
2484
2181
|
children: t("roles.addRule")
|
|
2485
2182
|
})
|
|
2486
2183
|
]
|
|
2184
|
+
}),
|
|
2185
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2186
|
+
className: FallbacksCard_module_css_default.field,
|
|
2187
|
+
role: "group",
|
|
2188
|
+
"aria-labelledby": "fallbacks-advanced",
|
|
2189
|
+
children: [
|
|
2190
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2191
|
+
className: FallbacksCard_module_css_default.fieldLabel,
|
|
2192
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2193
|
+
id: "fallbacks-advanced",
|
|
2194
|
+
children: t("advanced.label")
|
|
2195
|
+
})
|
|
2196
|
+
}),
|
|
2197
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2198
|
+
className: FallbacksCard_module_css_default.field,
|
|
2199
|
+
role: "group",
|
|
2200
|
+
"aria-labelledby": "fallbacks-trigger-codes",
|
|
2201
|
+
children: [
|
|
2202
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2203
|
+
className: FallbacksCard_module_css_default.fieldLabel,
|
|
2204
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2205
|
+
id: "fallbacks-trigger-codes",
|
|
2206
|
+
children: t("triggerCodes.label")
|
|
2207
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
2208
|
+
label: t("triggerCodes.tooltip"),
|
|
2209
|
+
disabled: !writable
|
|
2210
|
+
})]
|
|
2211
|
+
}),
|
|
2212
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2213
|
+
className: FallbacksCard_module_css_default.hint,
|
|
2214
|
+
children: t("triggerCodes.hint")
|
|
2215
|
+
}),
|
|
2216
|
+
KNOWN_TRIGGER_CODES.map((code) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2217
|
+
className: FallbacksCard_module_css_default.optionRow,
|
|
2218
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2219
|
+
type: "checkbox",
|
|
2220
|
+
checked: scalars.triggerCodes.includes(code),
|
|
2221
|
+
onChange: (event) => {
|
|
2222
|
+
updateScalars((draft) => {
|
|
2223
|
+
draft.triggerCodes = withTriggerCode(draft.triggerCodes, code, event.target.checked);
|
|
2224
|
+
});
|
|
2225
|
+
}
|
|
2226
|
+
}), t(TRIGGER_CODE_LABELS[code])]
|
|
2227
|
+
}, code)),
|
|
2228
|
+
unknownCodes.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2229
|
+
className: FallbacksCard_module_css_default.hint,
|
|
2230
|
+
children: t("triggerCodes.extra", { codes: unknownCodes.join(", ") })
|
|
2231
|
+
})
|
|
2232
|
+
]
|
|
2233
|
+
}),
|
|
2234
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2235
|
+
className: FallbacksCard_module_css_default.field,
|
|
2236
|
+
role: "group",
|
|
2237
|
+
"aria-labelledby": "fallbacks-revert-policy",
|
|
2238
|
+
children: [
|
|
2239
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2240
|
+
className: FallbacksCard_module_css_default.fieldLabel,
|
|
2241
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2242
|
+
id: "fallbacks-revert-policy",
|
|
2243
|
+
children: t("revertPolicy.label")
|
|
2244
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
2245
|
+
label: t("revertPolicy.tooltip"),
|
|
2246
|
+
disabled: !writable
|
|
2247
|
+
})]
|
|
2248
|
+
}),
|
|
2249
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2250
|
+
className: FallbacksCard_module_css_default.hint,
|
|
2251
|
+
children: t("revertPolicy.hint")
|
|
2252
|
+
}),
|
|
2253
|
+
["cooldown-expiry", "never"].map((policy) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2254
|
+
className: FallbacksCard_module_css_default.optionRow,
|
|
2255
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2256
|
+
type: "radio",
|
|
2257
|
+
name: "fallbacks-revert-policy",
|
|
2258
|
+
checked: scalars.revertPolicy === policy,
|
|
2259
|
+
onChange: () => {
|
|
2260
|
+
updateScalars((draft) => {
|
|
2261
|
+
draft.revertPolicy = policy;
|
|
2262
|
+
});
|
|
2263
|
+
}
|
|
2264
|
+
}), t(`revertPolicy.${policy}`)]
|
|
2265
|
+
}, policy))
|
|
2266
|
+
]
|
|
2267
|
+
}),
|
|
2268
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2269
|
+
className: FallbacksCard_module_css_default.numberFields,
|
|
2270
|
+
children: [
|
|
2271
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2272
|
+
className: FallbacksCard_module_css_default.field,
|
|
2273
|
+
children: [
|
|
2274
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2275
|
+
className: FallbacksCard_module_css_default.fieldLabel,
|
|
2276
|
+
children: [
|
|
2277
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2278
|
+
htmlFor: "fallbacks-cooldown-ms",
|
|
2279
|
+
children: t("cooldownMs.label")
|
|
2280
|
+
}),
|
|
2281
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
2282
|
+
label: t("cooldownMs.tooltip"),
|
|
2283
|
+
disabled: !writable
|
|
2284
|
+
}),
|
|
2285
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2286
|
+
className: FallbacksCard_module_css_default.defaultNote,
|
|
2287
|
+
children: [
|
|
2288
|
+
t("defaults.prefix"),
|
|
2289
|
+
": ",
|
|
2290
|
+
state.config.cooldownMs
|
|
2291
|
+
]
|
|
2292
|
+
})
|
|
2293
|
+
]
|
|
2294
|
+
}),
|
|
2295
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2296
|
+
id: "fallbacks-cooldown-ms",
|
|
2297
|
+
className: FallbacksCard_module_css_default.input,
|
|
2298
|
+
type: "number",
|
|
2299
|
+
min: 0,
|
|
2300
|
+
value: String(scalars.cooldownMs),
|
|
2301
|
+
disabled: !writable,
|
|
2302
|
+
onChange: (event) => {
|
|
2303
|
+
updateScalars((draft) => {
|
|
2304
|
+
draft.cooldownMs = parseCount(event.target.value);
|
|
2305
|
+
});
|
|
2306
|
+
}
|
|
2307
|
+
}),
|
|
2308
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2309
|
+
className: FallbacksCard_module_css_default.hint,
|
|
2310
|
+
children: t("cooldownMs.hint")
|
|
2311
|
+
})
|
|
2312
|
+
]
|
|
2313
|
+
}),
|
|
2314
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2315
|
+
className: FallbacksCard_module_css_default.field,
|
|
2316
|
+
children: [
|
|
2317
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2318
|
+
className: FallbacksCard_module_css_default.fieldLabel,
|
|
2319
|
+
children: [
|
|
2320
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2321
|
+
htmlFor: "fallbacks-max-switches",
|
|
2322
|
+
children: t("maxSwitchesPerStep.label")
|
|
2323
|
+
}),
|
|
2324
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
2325
|
+
label: t("maxSwitchesPerStep.tooltip"),
|
|
2326
|
+
disabled: !writable
|
|
2327
|
+
}),
|
|
2328
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2329
|
+
className: FallbacksCard_module_css_default.defaultNote,
|
|
2330
|
+
children: [
|
|
2331
|
+
t("defaults.prefix"),
|
|
2332
|
+
": ",
|
|
2333
|
+
state.config.maxSwitchesPerStep
|
|
2334
|
+
]
|
|
2335
|
+
})
|
|
2336
|
+
]
|
|
2337
|
+
}),
|
|
2338
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2339
|
+
id: "fallbacks-max-switches",
|
|
2340
|
+
className: FallbacksCard_module_css_default.input,
|
|
2341
|
+
type: "number",
|
|
2342
|
+
min: 0,
|
|
2343
|
+
value: String(scalars.maxSwitchesPerStep),
|
|
2344
|
+
disabled: !writable,
|
|
2345
|
+
onChange: (event) => {
|
|
2346
|
+
updateScalars((draft) => {
|
|
2347
|
+
draft.maxSwitchesPerStep = parseCount(event.target.value);
|
|
2348
|
+
});
|
|
2349
|
+
}
|
|
2350
|
+
}),
|
|
2351
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2352
|
+
className: FallbacksCard_module_css_default.hint,
|
|
2353
|
+
children: t("maxSwitchesPerStep.hint")
|
|
2354
|
+
})
|
|
2355
|
+
]
|
|
2356
|
+
}),
|
|
2357
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2358
|
+
className: FallbacksCard_module_css_default.field,
|
|
2359
|
+
children: [
|
|
2360
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2361
|
+
className: FallbacksCard_module_css_default.fieldLabel,
|
|
2362
|
+
children: [
|
|
2363
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
2364
|
+
htmlFor: "fallbacks-always-cap",
|
|
2365
|
+
children: t("alwaysModeRetryCap.label")
|
|
2366
|
+
}),
|
|
2367
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
|
|
2368
|
+
label: t("alwaysModeRetryCap.tooltip"),
|
|
2369
|
+
disabled: !writable
|
|
2370
|
+
}),
|
|
2371
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2372
|
+
className: FallbacksCard_module_css_default.defaultNote,
|
|
2373
|
+
children: [
|
|
2374
|
+
t("defaults.prefix"),
|
|
2375
|
+
": ",
|
|
2376
|
+
state.config.alwaysModeRetryCap
|
|
2377
|
+
]
|
|
2378
|
+
})
|
|
2379
|
+
]
|
|
2380
|
+
}),
|
|
2381
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2382
|
+
id: "fallbacks-always-cap",
|
|
2383
|
+
className: FallbacksCard_module_css_default.input,
|
|
2384
|
+
type: "number",
|
|
2385
|
+
min: 0,
|
|
2386
|
+
value: String(scalars.alwaysModeRetryCap),
|
|
2387
|
+
disabled: !writable,
|
|
2388
|
+
onChange: (event) => {
|
|
2389
|
+
updateScalars((draft) => {
|
|
2390
|
+
draft.alwaysModeRetryCap = parseCount(event.target.value);
|
|
2391
|
+
});
|
|
2392
|
+
}
|
|
2393
|
+
}),
|
|
2394
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2395
|
+
className: FallbacksCard_module_css_default.hint,
|
|
2396
|
+
children: t("alwaysModeRetryCap.hint")
|
|
2397
|
+
})
|
|
2398
|
+
]
|
|
2399
|
+
})
|
|
2400
|
+
]
|
|
2401
|
+
})
|
|
2402
|
+
]
|
|
2487
2403
|
})
|
|
2488
2404
|
]
|
|
2489
2405
|
})
|
package/dist/client/locales.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export declare const zh: {
|
|
|
34
34
|
'alwaysModeRetryCap.label': string;
|
|
35
35
|
'alwaysModeRetryCap.hint': string;
|
|
36
36
|
'alwaysModeRetryCap.tooltip': string;
|
|
37
|
+
'advanced.label': string;
|
|
37
38
|
'rootChain.label': string;
|
|
38
39
|
'rootChain.hint': string;
|
|
39
40
|
'rootChain.tooltip': string;
|
|
@@ -43,14 +44,15 @@ export declare const zh: {
|
|
|
43
44
|
'chains.selector.modelPlaceholder': string;
|
|
44
45
|
'chains.selector.wildcard': string;
|
|
45
46
|
'chains.selector.noModels': string;
|
|
47
|
+
'chains.selector.noModelsStrict': string;
|
|
46
48
|
'roles.list.label': string;
|
|
47
49
|
'roles.list.hint': string;
|
|
48
50
|
'roles.list.tooltip': string;
|
|
49
51
|
'roles.id': string;
|
|
50
52
|
'roles.id.hint': string;
|
|
51
53
|
'roles.idPlaceholder': string;
|
|
52
|
-
'roles.
|
|
53
|
-
'roles.
|
|
54
|
+
'roles.persona': string;
|
|
55
|
+
'roles.personaPlaceholder': string;
|
|
54
56
|
'roles.fallback': string;
|
|
55
57
|
'roles.fallback.inherit-root': string;
|
|
56
58
|
'roles.fallback.none': string;
|
|
@@ -163,6 +165,7 @@ export declare const en: {
|
|
|
163
165
|
'alwaysModeRetryCap.label': string;
|
|
164
166
|
'alwaysModeRetryCap.hint': string;
|
|
165
167
|
'alwaysModeRetryCap.tooltip': string;
|
|
168
|
+
'advanced.label': string;
|
|
166
169
|
'rootChain.label': string;
|
|
167
170
|
'rootChain.hint': string;
|
|
168
171
|
'rootChain.tooltip': string;
|
|
@@ -172,14 +175,15 @@ export declare const en: {
|
|
|
172
175
|
'chains.selector.modelPlaceholder': string;
|
|
173
176
|
'chains.selector.wildcard': string;
|
|
174
177
|
'chains.selector.noModels': string;
|
|
178
|
+
'chains.selector.noModelsStrict': string;
|
|
175
179
|
'roles.list.label': string;
|
|
176
180
|
'roles.list.hint': string;
|
|
177
181
|
'roles.list.tooltip': string;
|
|
178
182
|
'roles.id': string;
|
|
179
183
|
'roles.id.hint': string;
|
|
180
184
|
'roles.idPlaceholder': string;
|
|
181
|
-
'roles.
|
|
182
|
-
'roles.
|
|
185
|
+
'roles.persona': string;
|
|
186
|
+
'roles.personaPlaceholder': string;
|
|
183
187
|
'roles.fallback': string;
|
|
184
188
|
'roles.fallback.inherit-root': string;
|
|
185
189
|
'roles.fallback.none': string;
|
package/dist/config.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `fallbacks` settings namespace:
|
|
2
|
+
* The `fallbacks` settings namespace: config types + defaults (the
|
|
3
|
+
* schemastery `Config` schema lives in `./schema.ts`, host-only — this
|
|
4
|
+
* module must stay free of `@deepseek-ai/*` value imports so the client
|
|
5
|
+
* bundle never reaches schemastery).
|
|
3
6
|
*
|
|
4
7
|
* Two-block config model (plan fallbacks-role-config-model): block 1
|
|
5
8
|
* `rootChain` — the root agent's single fallback chain (empty = no
|
|
6
9
|
* degradation) — plus block 2 declared role entities: `roles.list`
|
|
7
|
-
* (id/
|
|
10
|
+
* (id/persona/prompt?/permissions?/chain?/fallback) and
|
|
8
11
|
* `roles.rules` enum references into the declared ids (or the built-in
|
|
9
12
|
* `'inherit'` role). The legacy `chains` / `roles.default` keys are gone
|
|
10
13
|
* from the schema and type (zero residual, migration table excepted); the
|
|
@@ -19,12 +22,11 @@
|
|
|
19
22
|
*
|
|
20
23
|
* This module is pure logic: it must not import any `@deepseek-ai/*` package
|
|
21
24
|
* (types included) — `FallbacksConfig` is the plugin's own type. Task 3
|
|
22
|
-
* registers
|
|
23
|
-
* settings namespace.
|
|
25
|
+
* registers the schemastery schema (see `./schema.ts`) with
|
|
26
|
+
* `installSettingsSection` under the `fallbacks` settings namespace.
|
|
24
27
|
*
|
|
25
28
|
* @module dsh-llm-fallbacks/config
|
|
26
29
|
*/
|
|
27
|
-
import z from '@deepseek-ai/schemastery';
|
|
28
30
|
/** How a cooled-down model comes back (spec §4). */
|
|
29
31
|
export type RevertPolicy = 'cooldown-expiry' | 'never';
|
|
30
32
|
/** A single role rule: match on origin/provider/model patterns (spec §3). */
|
|
@@ -49,8 +51,8 @@ export type FallbackStrategy = 'inherit-root' | 'none';
|
|
|
49
51
|
*/
|
|
50
52
|
export interface FallbacksRole {
|
|
51
53
|
id: string;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
/** Personality hint (人格提示) — free text, never validated. */
|
|
55
|
+
persona: string;
|
|
54
56
|
/** Reserved for next iteration — no consumer this round. */
|
|
55
57
|
prompt?: string;
|
|
56
58
|
/** Reserved for next iteration — no consumer this round. */
|
|
@@ -110,7 +112,7 @@ export interface FallbacksConfigLogger {
|
|
|
110
112
|
* `rootChain`/role-chain selector legality, and the role model-config
|
|
111
113
|
* requirement (a declared role with a missing/empty chain warns — a role
|
|
112
114
|
* without a model config is meaningless, plan fallbacks-feedback-round
|
|
113
|
-
* T2). `
|
|
115
|
+
* T2). `persona` is free text and is deliberately not
|
|
114
116
|
* validated. Each violation emits one `llm-fallbacks: ...` warn and "does
|
|
115
117
|
* not take effect" — the config stays usable (spec §4 / AC-4
|
|
116
118
|
* warn-not-crash semantics).
|
|
@@ -119,21 +121,13 @@ export declare function validateFallbacksConfig(config: FallbacksConfig, logger:
|
|
|
119
121
|
/**
|
|
120
122
|
* Detect legacy (two-block-era) leftovers in a config SOURCE — the composed
|
|
121
123
|
* object `source()` returns, or a raw settings document. Recognizes the
|
|
122
|
-
* removed `chains` key, the removed `roles.default` field,
|
|
123
|
-
* `roles.
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
124
|
+
* removed `chains` key, the removed `roles.default` field, the removed
|
|
125
|
+
* role-entity fields `roles.list[].label` / `roles.list[].description`
|
|
126
|
+
* (renamed to `persona`), and `roles.rules[].role` values that reference no
|
|
127
|
+
* declared `roles.list` id and are not the built-in `'inherit'`. Returns
|
|
128
|
+
* descriptive key/role names; the gateway attaches them as `get().legacyKeys`
|
|
129
|
+
* so the client can show a migration banner (spec §9 — the source is read
|
|
130
|
+
* directly because schemastery retains unknown keys, verified plan Task 1
|
|
131
|
+
* Step 1).
|
|
128
132
|
*/
|
|
129
133
|
export declare function detectLegacyKeys(source: Record<string, unknown>): string[];
|
|
130
|
-
/**
|
|
131
|
-
* Plugin Config schema (schemastery), mirroring {@link FallbacksConfig}.
|
|
132
|
-
* Object fields are optional by default in schemastery; `.default()` fills
|
|
133
|
-
* the spec defaults, `.required()` keeps mandatory fields. Unknown keys are
|
|
134
|
-
* RETAINED by the composition (verified plan Task 1 Step 1) — that is what
|
|
135
|
-
* lets `detectLegacyKeys` flag two-block-era leftovers (`chains` /
|
|
136
|
-
* `roles.default`) on the composed object at startup (warn + gateway
|
|
137
|
-
* `legacyKeys`, see `src/index.ts` apply()).
|
|
138
|
-
*/
|
|
139
|
-
export declare const Config: z<FallbacksConfig>;
|
package/dist/index.d.ts
CHANGED
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import type { Context } from '@deepseek-ai/cordis';
|
|
31
31
|
import type { Session } from '@deepseek-ai/dsh-session';
|
|
32
|
-
import {
|
|
32
|
+
import { detectLegacyKeys, validateFallbacksConfig, type FallbacksConfig } from './config.ts';
|
|
33
|
+
import { Config } from './schema.ts';
|
|
33
34
|
import { resolveChain } from './chains.ts';
|
|
34
35
|
import { resolveRole } from './roles.ts';
|
|
35
36
|
import { FallbackStateStore } from './state.ts';
|
package/dist/index.js
CHANGED
|
@@ -86,8 +86,11 @@ function detectLegacyKeys(source) {
|
|
|
86
86
|
if (isRecordLike(roles)) {
|
|
87
87
|
if (Object.hasOwn(roles, "default")) keys.push("roles.default");
|
|
88
88
|
const declared = new Set();
|
|
89
|
-
if (Array.isArray(roles.list)) {
|
|
90
|
-
|
|
89
|
+
if (Array.isArray(roles.list)) for (const item of roles.list) {
|
|
90
|
+
if (!isRecordLike(item)) continue;
|
|
91
|
+
if (typeof item.id === "string") declared.add(item.id);
|
|
92
|
+
if (Object.hasOwn(item, "label")) keys.push("roles.list[].label");
|
|
93
|
+
if (Object.hasOwn(item, "description")) keys.push("roles.list[].description");
|
|
91
94
|
}
|
|
92
95
|
if (Array.isArray(roles.rules)) {
|
|
93
96
|
for (const rule of roles.rules) if (isRecordLike(rule) && typeof rule.role === "string" && rule.role !== "inherit" && !declared.has(rule.role)) keys.push(`roles.rules[].role: ${rule.role}`);
|
|
@@ -98,6 +101,8 @@ function detectLegacyKeys(source) {
|
|
|
98
101
|
function isRecordLike(value) {
|
|
99
102
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
100
103
|
}
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region .build/host/schema.js
|
|
101
106
|
const Config = z.object({
|
|
102
107
|
enabled: z.boolean().default(false),
|
|
103
108
|
triggerCodes: z.array(z.string()).default([
|
|
@@ -109,8 +114,7 @@ const Config = z.object({
|
|
|
109
114
|
roles: z.object({
|
|
110
115
|
list: z.array(z.object({
|
|
111
116
|
id: z.string().required(),
|
|
112
|
-
|
|
113
|
-
description: z.string().default(""),
|
|
117
|
+
persona: z.string().default(""),
|
|
114
118
|
prompt: z.string(),
|
|
115
119
|
permissions: z.object({
|
|
116
120
|
allow: z.array(z.string()),
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `fallbacks` settings schema (schemastery), mirroring
|
|
3
|
+
* {@link FallbacksConfig}.
|
|
4
|
+
*
|
|
5
|
+
* Host-only module: `Config` is the schemastery schema the settings section
|
|
6
|
+
* validates/composes against, and `@deepseek-ai/schemastery` is an
|
|
7
|
+
* `@deepseek-ai/*` RUNTIME value import — it must never enter the client
|
|
8
|
+
* bundle, because the web loader module table cannot answer that require
|
|
9
|
+
* (build-time externals drift, 20260815: the client bundle previously
|
|
10
|
+
* externalized `@deepseek-ai/schemastery` and the web settings card failed
|
|
11
|
+
* to load). The client half consumes `FallbacksConfig` and the other
|
|
12
|
+
* config types from `./config.ts` type-only, so the schema stays here, out
|
|
13
|
+
* of the client module graph.
|
|
14
|
+
*
|
|
15
|
+
* Object fields are optional by default in schemastery; `.default()` fills
|
|
16
|
+
* the spec defaults, `.required()` keeps mandatory fields. Unknown keys are
|
|
17
|
+
* RETAINED by the composition (verified plan Task 1 Step 1) — that is what
|
|
18
|
+
* lets `detectLegacyKeys` flag two-block-era leftovers (`chains` /
|
|
19
|
+
* `roles.default`) on the composed object at startup (warn + gateway
|
|
20
|
+
* `legacyKeys`, see `src/index.ts` apply()).
|
|
21
|
+
*
|
|
22
|
+
* @module dsh-llm-fallbacks/schema
|
|
23
|
+
*/
|
|
24
|
+
import z from '@deepseek-ai/schemastery';
|
|
25
|
+
import type { FallbacksConfig } from './config.ts';
|
|
26
|
+
export declare const Config: z<FallbacksConfig>;
|
package/package.json
CHANGED
package/scripts/build-client.ts
CHANGED
|
@@ -66,11 +66,6 @@ export const CLIENT_EXTERNALS: readonly string[] = [
|
|
|
66
66
|
'@deepseek-ai/dsh-client-ui-attachment',
|
|
67
67
|
'@deepseek-ai/dsh-client-schema-form',
|
|
68
68
|
'@deepseek-ai/dsh-client-runtime/client',
|
|
69
|
-
// @deepseek-ai/schemastery: config.ts's schema value import reaches the
|
|
70
|
-
// client module graph (client consumes FallbacksConfig types via config.ts);
|
|
71
|
-
// the dsh host provides the package in-box (dsh-settings depends on it), so
|
|
72
|
-
// it stays external like the other @deepseek-ai/* runtime faces.
|
|
73
|
-
'@deepseek-ai/schemastery',
|
|
74
69
|
]
|
|
75
70
|
|
|
76
71
|
/**
|