dsh-llm-fallbacks 0.6.9 → 0.6.11

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 CHANGED
@@ -7,7 +7,7 @@
7
7
  ![node](https://img.shields.io/badge/node-%3E%3D22-339933.svg)
8
8
  ![pnpm](https://img.shields.io/badge/pnpm-%3E%3D10-f69220.svg)
9
9
  ![dsh tui](https://img.shields.io/badge/dsh%20tui-compatible-4B32C3.svg)
10
- ![dsh](https://img.shields.io/badge/DSH-0.2.1--rc.2-4B32C3.svg)
10
+ ![dsh](https://img.shields.io/badge/DSH-0.2.1--rc.3-4B32C3.svg)
11
11
  [![dshfind](https://dshfind.com/api/badge/omdsh-dev/dsh-llm-fallbacks?lang=en)](https://dshfind.com/zh/plugins/omdsh-dev/dsh-llm-fallbacks?ref=badge)
12
12
 
13
13
  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.
@@ -50,7 +50,7 @@ GLM Peak and GLM Valley are offered in the card picker only when `zai-coding-cn`
50
50
 
51
51
  A subagent's role is declared in its dispatch prompt — the Assignment header `**Execute as**: <id>` — and it is resolved against the role ids declared in `roles.list`. Only that **declared header** delivers the child's identity: the role's `persona` is installed on the child as a system-prompt section, the child's own session gets exactly one `[role: <id>]` notice row, and its session header shows a role badge.
52
52
 
53
- Role **routing** is resolved separately, in four ordered stages, first hit winning — explicit `agentPreset` → **dispatch-declared** `**Execute as**: <id>` header → rules → LLM auto-match. If the child's dispatch prompt declares `**Execute as**: <id>`, that role's persona, `[role: <id>]` notice and badge are delivered **regardless of which routing stage wins** — an earlier explicit `agentPreset` can route the child as another role while the identity carriers stay the declared header's. A child with **no declared dispatch header** gets no persona, notice row or badge, even when rules or auto-match resolve a role for routing. Making the resolved role's chain head the child's model is plugin-originated and conditional: with the host `subagent-model-selection` policy enabled, an explicit authorized route is preserved (inject skipped) and an injected head must be inside the effective allowlist — an empty intersection injects nothing ([host policy](docs/features.md#host-subagent-model-selection-policy-dsh-012)).
53
+ Role **routing** is resolved separately, in four ordered stages, first hit winning — explicit `agentPreset` → **dispatch-declared** `**Execute as**: <id>` header → rules → LLM auto-match. If the child's dispatch prompt declares `**Execute as**: <id>`, that role's persona, `[role: <id>]` notice and badge are delivered **regardless of which routing stage wins** — an earlier explicit `agentPreset` can route the child as another role while the identity carriers stay the declared header's. A child with **no declared dispatch header** gets no persona, no notice row and no role half of the badge (the model half still renders when the child has a recorded route), even when rules or auto-match resolve a role for routing. Making the resolved role's chain head the child's model is plugin-originated and conditional: with the host `subagent-model-selection` policy enabled, an explicit authorized route is preserved (inject skipped) and an injected head must be inside the effective allowlist — an empty intersection injects nothing ([host policy](docs/features.md#host-subagent-model-selection-policy-dsh-012)).
54
54
 
55
55
  This covers an **ordinary subagent dispatch and an Agent Team teammate** (`spawn_teammate`) alike: both are children dispatched with the same header, which the model writes — the same dispatch seam, no mechanism-specific configuration.
56
56
 
package/README.zh-CN.md CHANGED
@@ -7,7 +7,7 @@
7
7
  ![node](https://img.shields.io/badge/node-%3E%3D22-339933.svg)
8
8
  ![pnpm](https://img.shields.io/badge/pnpm-%3E%3D10-f69220.svg)
9
9
  ![dsh tui](https://img.shields.io/badge/dsh%20tui-compatible-4B32C3.svg)
10
- ![dsh](https://img.shields.io/badge/DSH-0.2.1--rc.2-4B32C3.svg)
10
+ ![dsh](https://img.shields.io/badge/DSH-0.2.1--rc.3-4B32C3.svg)
11
11
  [![dshfind](https://dshfind.com/api/badge/omdsh-dev/dsh-llm-fallbacks?lang=zh)](https://dshfind.com/zh/plugins/omdsh-dev/dsh-llm-fallbacks?ref=badge)
12
12
 
13
13
  dsh(DeepSeek Harness)的自动模型降级插件:当 agent 的模型请求持续失败(重试耗尽、权限错误、配额超限、限流 429)时,插件按当前角色的 fallback 链切换 provider/model,当前 step/turn 在目标模型上继续——任务不会因模型问题中断。
@@ -1,48 +1,53 @@
1
1
  /**
2
- * Session-header subagent role badge (plan subagent-role-badge T3; durable
3
- * channel plan role-based-subagent-adoption Task 3b): a compact read-only pill
4
- * in `conversation.session.header.actions` showing which Subagent role the
5
- * viewed session's dispatch resolved, hovering as `role → latest request route`.
2
+ * Session-header subagent role/model badge (plan subagent-role-badge T3 +
3
+ * plan subagent-role-model): a compact read-only pill in
4
+ * `conversation.session.header.actions` showing the viewed CHILD session's
5
+ * dispatch role and the model route it runs on, hovering as
6
+ * `role → provider/model (request route)`.
6
7
  *
7
- * Contract notes (verified 2026-09-09 against the harness checkout):
8
+ * Contract notes (re-verified 2026-10-23 against the installed dsh 0.2.1-rc.2):
8
9
  * - The slot is `kind: 'list'`, `scope: 'session'`, owner props an empty marker
9
10
  * (`ConversationHeaderActionOwnerProps`, `children?: never`) — the header
10
11
  * renders occupants with `renderSlot(..., {})`, so ALL data flows through the
11
12
  * session standard kit.
12
- * - Every `scope: 'session'` occupant receives the session standard kit
13
- * (`useSession` + `useProjection`; ui-session `src/client/index.ts:112-119`,
14
- * merged into the runtime share by ui-slots' `PropsRuntime`). The compiled
15
- * peer types do not carry that merge into this plugin's typecheck program
16
- * (ui-session is deliberately not a plugin peer), so both seats are read
17
- * STRUCTURALLY and guarded — a version-skewed host without them renders
18
- * nothing, never throws.
19
- * - Data: the host's `fallbacksSubagentRole` projection (Task 3b), folded
13
+ * - Every `scope: 'session'` occupant receives the session standard kit, whose
14
+ * members this component uses are `sessionId`, `useSessions` (the shared
15
+ * Session store) and `useProjection` (the viewed session's projection cut).
16
+ * The compiled peer types do not carry that merge into this plugin's
17
+ * typecheck program (ui-session is deliberately not a plugin peer), so the
18
+ * seats are read STRUCTURALLY and guarded — a version-skewed host without them
19
+ * renders nothing, never throws.
20
+ * - The badge belongs to a CHILD session: `byId[sessionId].origin === 'subagent'`
21
+ * is the host's own child-session idiom (`dsh-client-ui-subagent`
22
+ * `SubagentCatalogAction`). Root rows omit `origin`, so a root session renders
23
+ * nothing even though its own `modelSelection` carries a route — the composer
24
+ * owns that control.
25
+ * - Role data: the host's `fallbacksSubagentRole` projection (Task 3b), folded
20
26
  * server-side out of the child's own durable log — the ONE role source. The
21
- * session kit binds the hook to the VIEWED session (`binding.session.
22
- * projections.faceOf(key)`), so no `sessionId` plumbing, no effect, no state,
23
- * and no polling: a settled child's follow opening snapshot already carries
24
- * the value, and a running child's change frame updates the pill. An absent
25
- * key (host without the unit, or no notice row) reads as "no pill".
26
- * - Hook order is render-independent (Task 3b L2 review C-1): the seat is a real
27
- * hook, so BOTH projection reads are unconditional and only their derived
28
- * values are guarded — a conditional second read would change the hook count
29
- * on the very re-render that publishes a running child's role.
30
- * - The hover route comes from the host's EXISTING `modelSelection` projection
31
- * (`lastUsed` — the route of the LATEST recorded request), NOT from the
32
- * dispatch-time route: it is a separate, richer fact, and the label says so.
27
+ * session kit binds the hook to the VIEWED session, so no `sessionId`
28
+ * plumbing, no effect, no state, and no polling. An absent key (host without
29
+ * the unit, or no notice row) omits the role half; `inherit` omits it too.
30
+ * - Route data: the host's EXISTING `modelSelection` projection, read as
31
+ * `next ?? lastUsed` — the route the child's next request will use, falling
32
+ * back to the one its latest recorded request ran on. The two halves are
33
+ * INDEPENDENT: either alone still renders, and both missing renders nothing.
34
+ * - Hook order is render-independent (Task 3b L2 review C-1): the seats are real
35
+ * hooks, so the three reads are unconditional and only their derived values
36
+ * are guarded — a conditional read would change the hook count on the very
37
+ * re-render that publishes a running child's role.
33
38
  * - Render-only discipline (C4 pattern, same as `ConversationFallbackSwitch`):
34
39
  * the badge contributes a view; no message construction, no model-context
35
- * injection. Degrade-never-crash: a missing/foreign projection value or an
36
- * `inherit` role renders `null` — the actions row collapses cleanly.
40
+ * injection. Degrade-never-crash: a missing/foreign projection value renders
41
+ * `null` — the actions row collapses cleanly.
37
42
  */
38
43
  import type { ReactNode } from 'react';
39
44
  import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
40
45
  /** Props delivered by the actions slot outlet: runtime share + locale seat. */
41
46
  export type SubagentRoleBadgeProps = PropsRuntime<'conversation.session.header.actions'> & PropsLocale<'fallbacks'>;
42
47
  /**
43
- * Render the viewed session's dispatch-resolved role badge.
44
- * @param props - composed slot props (the session-kit `useProjection` seat is
45
- * read structurally — see the module docblock).
48
+ * Render the viewed SUBAGENT session's role and model.
49
+ * @param props - composed slot props (the session-kit `useProjection` /
50
+ * `useSessions` seats are read structurally — see the module docblock).
46
51
  * @returns the badge element, or `null` when there is nothing to show.
47
52
  */
48
53
  export declare function SubagentRoleBadge(props: SubagentRoleBadgeProps): ReactNode;
@@ -501,6 +501,19 @@ export declare class FallbacksSettingsController {
501
501
  * @param next - the complete edited configuration.
502
502
  */
503
503
  save(next: FallbacksConfig): Promise<void>;
504
+ /**
505
+ * Explicit "sync to host allowlist" write (plan
506
+ * role-chain-allowlist-visibility T2): the gateway endpoint merges the
507
+ * CURRENT allowlist with this plugin's exact chain routes plus the virtual
508
+ * `FallbacksChain/Auto` route and writes only `allowedModels`. The button
509
+ * click is the ONLY trigger — the store never syncs on its own.
510
+ * @returns true when the write landed (the card then reads the fresh
511
+ * snapshot from the store); false on a refused/failed call, leaving the
512
+ * last accepted state untouched. A response discarded by the write
513
+ * generation guard is still a landed write — dispose() is the only
514
+ * invalidator, so it must not report failure.
515
+ */
516
+ syncAllowlist(): Promise<boolean>;
504
517
  /**
505
518
  * Reset to composition defaults through the gateway channel
506
519
  * (`/api/fallbacks/reset` — the fallbacks-specific third method; the host
@@ -510,6 +523,13 @@ export declare class FallbacksSettingsController {
510
523
  resetToDefaults(): Promise<void>;
511
524
  /** Stop in-flight responses from publishing after plugin disposal. */
512
525
  dispose(): void;
526
+ /**
527
+ * Unpack one write response (set / reset / sync-allowlist) into the store
528
+ * snapshot. The additive fields (legacyKeys §9, seeds §9.4, subagentPolicy
529
+ * D4) follow the W-1/F-1 rule: absent on the wire keeps the last accepted
530
+ * value — only a `get` may settle server truth.
531
+ */
532
+ private acceptWire;
513
533
  /**
514
534
  * Publish a settled load: `status` ready, `writable` from describe, and —
515
535
  * only when the gateway returned a REAL config — `present` true and
@@ -518,6 +518,15 @@ window.__ModuleLoader__.load({
518
518
  /** Role id format (aligned with yet-another-subagent `isValidProfileId`). */
519
519
  const ROLE_ID_PATTERN = /^[a-z0-9-]{1,32}$/;
520
520
  //#endregion
521
+ //#region src/route-allowlist.ts
522
+ /**
523
+ * Whether the route is on the allowlist under exact route identity: both the
524
+ * provider and the model strings must equal an allowed route exactly.
525
+ */
526
+ function routeOnAllowlist(route, allowedModels) {
527
+ return allowedModels.some((allowed) => allowed.provider === route.provider && allowed.model === route.model);
528
+ }
529
+ //#endregion
521
530
  //#region src/client/fallbacks-store.ts
522
531
  /** The plugin's settings namespace on the host wire (settings/document-updated ns filter). */
523
532
  const FALLBACKS_SETTINGS_NS = "fallbacks";
@@ -1187,26 +1196,37 @@ window.__ModuleLoader__.load({
1187
1196
  const result = await this.rpc.call("/api", "fallbacks/set", { args: { patch: next } });
1188
1197
  if (generation !== this.writeGeneration) return;
1189
1198
  if (!result.ok) throw result.error;
1190
- const value = result.value;
1191
- const config = value !== null && typeof value === "object" && "config" in value ? value.config : void 0;
1192
- let legacyKeys = this.store.getSnapshot().legacyKeys;
1193
- if (value !== null && typeof value === "object" && "legacyKeys" in value) {
1194
- const wireLegacyKeys = value.legacyKeys;
1195
- if (Array.isArray(wireLegacyKeys)) legacyKeys = wireLegacyKeys.filter((key) => typeof key === "string");
1196
- }
1197
- let seeds = this.store.getSnapshot().seeds;
1198
- if (value !== null && typeof value === "object" && "seeds" in value) {
1199
- const wireSeeds = value.seeds;
1200
- if (Array.isArray(wireSeeds)) seeds = parseSeedsWire(wireSeeds);
1201
- }
1202
- const subagentPolicy = subagentPolicyFromWire(value, this.store.getSnapshot().subagentPolicy);
1203
- this.accept(config, true, legacyKeys, seeds, subagentPolicy);
1199
+ this.acceptWire(result.value);
1204
1200
  } catch (error) {
1205
1201
  if (generation !== this.writeGeneration) return;
1206
1202
  this.fail(error);
1207
1203
  }
1208
1204
  }
1209
1205
  /**
1206
+ * Explicit "sync to host allowlist" write (plan
1207
+ * role-chain-allowlist-visibility T2): the gateway endpoint merges the
1208
+ * CURRENT allowlist with this plugin's exact chain routes plus the virtual
1209
+ * `FallbacksChain/Auto` route and writes only `allowedModels`. The button
1210
+ * click is the ONLY trigger — the store never syncs on its own.
1211
+ * @returns true when the write landed (the card then reads the fresh
1212
+ * snapshot from the store); false on a refused/failed call, leaving the
1213
+ * last accepted state untouched. A response discarded by the write
1214
+ * generation guard is still a landed write — dispose() is the only
1215
+ * invalidator, so it must not report failure.
1216
+ */
1217
+ async syncAllowlist() {
1218
+ if (!this.store.getSnapshot().writable) return false;
1219
+ const generation = ++this.writeGeneration;
1220
+ try {
1221
+ const result = await this.rpc.call("/api", "fallbacks/sync-allowlist", { args: {} });
1222
+ if (!result.ok) return false;
1223
+ if (generation === this.writeGeneration) this.acceptWire(result.value);
1224
+ return true;
1225
+ } catch {
1226
+ return false;
1227
+ }
1228
+ }
1229
+ /**
1210
1230
  * Reset to composition defaults through the gateway channel
1211
1231
  * (`/api/fallbacks/reset` — the fallbacks-specific third method; the host
1212
1232
  * clears the user layer via `settings.replace(ns, {})`, the removal path a
@@ -1224,20 +1244,7 @@ window.__ModuleLoader__.load({
1224
1244
  const result = await this.rpc.call("/api", "fallbacks/reset", { args: {} });
1225
1245
  if (generation !== this.writeGeneration) return;
1226
1246
  if (!result.ok) throw result.error;
1227
- const value = result.value;
1228
- const config = value !== null && typeof value === "object" && "config" in value ? value.config : void 0;
1229
- let legacyKeys = this.store.getSnapshot().legacyKeys;
1230
- if (value !== null && typeof value === "object" && "legacyKeys" in value) {
1231
- const wireLegacyKeys = value.legacyKeys;
1232
- if (Array.isArray(wireLegacyKeys)) legacyKeys = wireLegacyKeys.filter((key) => typeof key === "string");
1233
- }
1234
- let seeds = this.store.getSnapshot().seeds;
1235
- if (value !== null && typeof value === "object" && "seeds" in value) {
1236
- const wireSeeds = value.seeds;
1237
- if (Array.isArray(wireSeeds)) seeds = parseSeedsWire(wireSeeds);
1238
- }
1239
- const subagentPolicy = subagentPolicyFromWire(value, this.store.getSnapshot().subagentPolicy);
1240
- this.accept(config, true, legacyKeys, seeds, subagentPolicy);
1247
+ this.acceptWire(result.value);
1241
1248
  } catch (error) {
1242
1249
  if (generation !== this.writeGeneration) return;
1243
1250
  this.fail(error);
@@ -1253,6 +1260,27 @@ window.__ModuleLoader__.load({
1253
1260
  this.followAbort.abort();
1254
1261
  }
1255
1262
  /**
1263
+ * Unpack one write response (set / reset / sync-allowlist) into the store
1264
+ * snapshot. The additive fields (legacyKeys §9, seeds §9.4, subagentPolicy
1265
+ * D4) follow the W-1/F-1 rule: absent on the wire keeps the last accepted
1266
+ * value — only a `get` may settle server truth.
1267
+ */
1268
+ acceptWire(value) {
1269
+ const config = value !== null && typeof value === "object" && "config" in value ? value.config : void 0;
1270
+ let legacyKeys = this.store.getSnapshot().legacyKeys;
1271
+ if (value !== null && typeof value === "object" && "legacyKeys" in value) {
1272
+ const wireLegacyKeys = value.legacyKeys;
1273
+ if (Array.isArray(wireLegacyKeys)) legacyKeys = wireLegacyKeys.filter((key) => typeof key === "string");
1274
+ }
1275
+ let seeds = this.store.getSnapshot().seeds;
1276
+ if (value !== null && typeof value === "object" && "seeds" in value) {
1277
+ const wireSeeds = value.seeds;
1278
+ if (Array.isArray(wireSeeds)) seeds = parseSeedsWire(wireSeeds);
1279
+ }
1280
+ const subagentPolicy = subagentPolicyFromWire(value, this.store.getSnapshot().subagentPolicy);
1281
+ this.accept(config, true, legacyKeys, seeds, subagentPolicy);
1282
+ }
1283
+ /**
1256
1284
  * Publish a settled load: `status` ready, `writable` from describe, and —
1257
1285
  * only when the gateway returned a REAL config — `present` true and
1258
1286
  * `state.config` replaced with the parsed value. A get that did not
@@ -1382,6 +1410,12 @@ window.__ModuleLoader__.load({
1382
1410
  "subagents.policy.source.injected": "插件注入",
1383
1411
  "subagents.policy.blocked": "降级候选均不在允许列表中 — 未切换",
1384
1412
  "subagents.policy.unprovable": "策略存在但无法读取 — 插件切换已禁用",
1413
+ "subagents.policy.offAllowlist": "角色链中有 {count} 个精确条目不在允许列表 — 这些条目不会生效",
1414
+ "subagents.policy.recommend": "建议将 FallbacksChain/Auto 加入允许列表(该虚拟路由不受宿主策略过滤)",
1415
+ "subagents.policy.sync": "同步到宿主允许列表",
1416
+ "subagents.policy.sync.done": "已同步:精确条目(角色链、默认降级链与 FallbacksChain/Auto)已写入允许列表;通配条目不展开、不同步",
1417
+ "subagents.policy.sync.failed": "同步失败:宿主设置未写入(策略未启用、条目不可配置或版本冲突等宿主拒绝),请检查宿主设置",
1418
+ "subagents.policy.sync.dirty": "角色链或默认降级链有未保存的修改 — 请先保存再同步(同步只写入已保存的配置)",
1385
1419
  "timeSlots.label": "分时槽设置",
1386
1420
  "timeSlots.hint": "自上而下第一条命中生效;全时段行固定最后",
1387
1421
  "timeSlots.tz.label": "时区(UTC±)",
@@ -1510,7 +1544,7 @@ window.__ModuleLoader__.load({
1510
1544
  "chat.switch.summary": "{from} → {to}({reason})",
1511
1545
  "chat.switch.summary.roleInject": "({reason})",
1512
1546
  "chat.switch.roleMap": "{role} → {model}",
1513
- "subagentRole.hover": "{role} → {model}(最近一次请求的路由)",
1547
+ "subagentRole.hover": "{role} → {model}(请求路由)",
1514
1548
  "defaults.prefix": "默认值",
1515
1549
  "save": "保存",
1516
1550
  "save.saving": "保存中…",
@@ -1572,6 +1606,12 @@ window.__ModuleLoader__.load({
1572
1606
  "subagents.policy.source.injected": "injected",
1573
1607
  "subagents.policy.blocked": "No in-allowlist fallback candidate — switch skipped",
1574
1608
  "subagents.policy.unprovable": "Policy present but unreadable — plugin switches disabled",
1609
+ "subagents.policy.offAllowlist": "{count} exact role-chain entries are outside the allowlist — they will not run",
1610
+ "subagents.policy.recommend": "Add FallbacksChain/Auto to the allowlist (this virtual route is not filtered by the host policy)",
1611
+ "subagents.policy.sync": "Sync to host allowlist",
1612
+ "subagents.policy.sync.done": "Synced: exact entries (role chains, default fallback chain and FallbacksChain/Auto) are now allowed; wildcard entries are not expanded or synced",
1613
+ "subagents.policy.sync.failed": "Sync failed: the host settings were not written (policy off, entry not configurable, revision conflict or another host refusal) — check the host settings",
1614
+ "subagents.policy.sync.dirty": "Unsaved role-chain or default-chain edits — save first, then sync (the sync writes the saved configuration only)",
1575
1615
  "timeSlots.label": "Time slots",
1576
1616
  "timeSlots.hint": "First match from top to bottom wins; the all-day row is always last",
1577
1617
  "timeSlots.tz.label": "Timezone (UTC±)",
@@ -1700,7 +1740,7 @@ window.__ModuleLoader__.load({
1700
1740
  "chat.switch.summary": "{from} → {to} ({reason})",
1701
1741
  "chat.switch.summary.roleInject": "({reason})",
1702
1742
  "chat.switch.roleMap": "{role} → {model}",
1703
- "subagentRole.hover": "{role} → {model} (latest request route)",
1743
+ "subagentRole.hover": "{role} → {model} (request route)",
1704
1744
  "defaults.prefix": "Default",
1705
1745
  "save": "Save",
1706
1746
  "save.saving": "Saving…",
@@ -1748,7 +1788,7 @@ window.__ModuleLoader__.load({
1748
1788
  }
1749
1789
  //#endregion
1750
1790
  //#region \0dsh-css:/home/runner/work/dsh-llm-fallbacks/dsh-llm-fallbacks/src/client/FallbacksCard.module.css.mjs
1751
- const css$3 = "\n\n\n._20fd0e45_root {\n min-width: 0;\n}\n\n\n._4e39cf17_readOnly {\n margin: 12px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n\n._9860a5b1_notice {\n margin: 12px 0 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-business-primary);\n}\n\n\n._5006d43e_legacyNotice {\n margin: 12px 0 0;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--dsw-alias-bg-module-platform);\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-business-primary);\n}\n\n._21918751_error {\n margin: 12px 0 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-error-primary);\n}\n\n\n._7cea4b2f_noticeRow {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 12px 0 0;\n}\n\n._7cea4b2f_noticeRow ._21918751_error {\n flex: 1;\n min-width: 0;\n margin: 0;\n}\n\n\n._94c91bfd_fieldset {\n margin: 0;\n padding: 0;\n border: none;\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n\n._ba240d82_sectionHeading {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n padding: 4px 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n._ddbc1b49_sectionHeadingText {\n display: block;\n min-width: 0;\n}\n\n\n._c41f9db7_sectionActions {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-left: auto;\n}\n\n._c41f9db7_sectionActions ._f0864181_discard,\n._c41f9db7_sectionActions ._ccff7e48_save {\n padding: 3px 12px;\n font-size: 12px;\n line-height: 18px;\n}\n\n\n._b116c758_sectionToggle {\n appearance: none;\n border: 0;\n background: none;\n display: flex;\n align-items: center;\n gap: 4px;\n margin: 0;\n padding: 0;\n font: inherit;\n color: inherit;\n cursor: pointer;\n}\n\n._b116c758_sectionToggle:disabled {\n cursor: default;\n}\n\n._b116c758_sectionToggle:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n\n._80101203_advancedBody {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n._94c91bfd_fieldset > ._80101203_advancedBody > ._67826267_field,\n._94c91bfd_fieldset > ._80101203_advancedBody > ._9998065c_checkboxRow,\n._94c91bfd_fieldset > ._80101203_advancedBody > ._3e95886d_numberFields {\n padding: 12px 0;\n}\n\n._94c91bfd_fieldset > ._80101203_advancedBody > ._67826267_field + ._67826267_field,\n._94c91bfd_fieldset > ._80101203_advancedBody > ._9998065c_checkboxRow + ._67826267_field,\n._94c91bfd_fieldset > ._80101203_advancedBody > ._67826267_field + ._3e95886d_numberFields {\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n\n._67826267_field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin: 0;\n padding: 0;\n border: none;\n min-width: 0;\n}\n\n\n._94c91bfd_fieldset > ._67826267_field,\n._94c91bfd_fieldset > ._9998065c_checkboxRow,\n._94c91bfd_fieldset > ._3e95886d_numberFields {\n padding: 12px 0;\n}\n\n._94c91bfd_fieldset > ._67826267_field + ._67826267_field,\n._94c91bfd_fieldset > ._67826267_field + ._3e95886d_numberFields,\n._94c91bfd_fieldset > ._9998065c_checkboxRow + ._67826267_field {\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n\n._9998065c_checkboxRow {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n\n\n._9998065c_checkboxRow ._06389b18_checkbox {\n margin-top: 3px;\n}\n\n._13e68c3f_fieldLabel {\n \n display: inline-flex;\n align-items: center;\n gap: 10px;\n padding: 0;\n font-size: 13px;\n line-height: 1.5;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n._4bc809b8_hint {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._02c2f6df_checkLabel {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: 14px;\n line-height: 22px;\n font-weight: 400;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n}\n\n._f2d47237_checkLabelTitle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-weight: 400;\n}\n\n._c1bed8ea_checkLabelDesc {\n font-size: 12px;\n line-height: 18px;\n font-weight: 400;\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._06389b18_checkbox {\n flex: none;\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n._06389b18_checkbox:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n\n._b1962759_slotPresetName {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n\n._a6244318_infoHint {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n font-size: 14px;\n line-height: 1;\n color: var(--dsw-alias-label-tertiary);\n background: var(--dsw-alias-interactive-bg-hover);\n cursor: help;\n user-select: none;\n}\n\n._a6244318_infoHint:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n._a6244318_infoHint::after {\n content: attr(data-tip);\n position: absolute;\n top: 50%;\n left: calc(100% + 8px);\n transform: translateY(-50%);\n z-index: 10;\n box-sizing: border-box;\n width: max-content;\n max-width: 280px;\n padding: 5px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n font-size: 11px;\n line-height: 17px;\n font-weight: 400;\n white-space: normal;\n text-align: left;\n pointer-events: none;\n opacity: 0;\n transition: opacity .12s;\n}\n\n._a6244318_infoHint:hover::after,\n._a6244318_infoHint:focus-visible::after {\n opacity: 1;\n}\n\n\n._e655c840_infoHintDisabled {\n opacity: 0.4;\n cursor: default;\n}\n\n\n._a2521bb0_optionRow {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: 6px;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n}\n\n._a2521bb0_optionRow:hover:has(input:not(:disabled)) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n\n._06389b18_checkbox,\n._a2521bb0_optionRow input {\n flex: none;\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n._06389b18_checkbox:focus-visible,\n._a2521bb0_optionRow input:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n._a2521bb0_optionRow input:disabled {\n cursor: default;\n}\n\n\n._c4ecbdf3_dayRow {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n}\n\n._0330c1ed_dayCell {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 6px;\n border-radius: 6px;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n\n._0330c1ed_dayCell:hover:has(input:not(:disabled)) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n._0330c1ed_dayCell input {\n flex: none;\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n._0330c1ed_dayCell input:disabled {\n cursor: default;\n}\n\n._0330c1ed_dayCell input:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n\n._f9d86f7b_input {\n box-sizing: border-box;\n width: 100%;\n height: 34px;\n padding: 0 12px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: var(--dsw-radius-md);\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n}\n\n._f9d86f7b_input:focus-visible {\n outline: none;\n border-color: var(--dsw-alias-state-business-primary);\n}\n\n._f9d86f7b_input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n\n._f9d86f7b_input:disabled {\n color: var(--dsw-alias-label-tertiary);\n cursor: default;\n}\n\n\n._3a9b42fd_inputTextarea {\n height: auto;\n min-height: 72px;\n padding: 8px 12px;\n resize: vertical;\n}\n\n\n._1285c19b_personaBriefRow {\n display: flex;\n align-items: center;\n gap: 4px;\n min-width: 0;\n}\n\n._4b8f7345_personaBrief {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-primary);\n}\n\n\n._2686400e_personaBriefEmpty {\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._4e4a07be_personaFull {\n margin: 0;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-secondary);\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n}\n\n\n._7b60337a_inputInvalid,\n._7b60337a_inputInvalid:focus-visible {\n border-color: var(--dsw-alias-state-error-primary);\n}\n\n\nselect._f9d86f7b_input {\n max-width: 240px;\n cursor: pointer;\n}\n\n\n._16eb7153_selectInput {\n appearance: none;\n padding-right: 32px;\n \n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 12px center;\n background-size: 12px 12px;\n}\n\n\n._3e95886d_numberFields {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 12px;\n}\n\n._3e95886d_numberFields ._67826267_field {\n padding: 0;\n}\n\n._0cfb5881_list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 4px;\n}\n\n\n._f5c620d6_editorCard {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 14px 16px;\n border-radius: 12px;\n background: var(--dsw-alias-bg-module-platform);\n}\n\n._224b1387_cardFoot {\n display: flex;\n justify-content: flex-end;\n}\n\n._8e220e07_ruleGrid {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n._24a12a2f_ruleCell {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 120px;\n flex: 1;\n}\n\n._1014c097_ruleCellLabel {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._631094a0_chevron {\n flex: none;\n color: var(--dsw-alias-label-tertiary);\n transform: rotate(-90deg);\n transition: transform .16s;\n}\n\n._44836ce8_chevronOpen {\n transform: none;\n}\n\n\n._cf3961bb_chevronFlip {\n transform: rotate(180deg);\n}\n\n\n._86bc52d2_collapseRow {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n}\n\n._a73efcc6_collapseToggle {\n appearance: none;\n border: 0;\n background: none;\n padding: 0;\n cursor: pointer;\n color: var(--dsw-alias-label-secondary);\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n font: inherit;\n text-align: left;\n}\n\n._a73efcc6_collapseToggle:disabled {\n cursor: default;\n}\n\n._af017718_collapseTitle {\n \n flex: none;\n max-width: 55%;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n._2cabe603_collapseMeta {\n \n flex: 0 1 auto;\n min-width: 0;\n max-width: 40%;\n margin-left: auto;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n\n._52d6df87_seedBadge {\n flex: none;\n max-width: 40%;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n border-radius: 999px;\n padding: 1px 8px;\n font-size: 11px;\n line-height: 17px;\n font-weight: 500;\n white-space: nowrap;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._7b04f727_slotTag {\n flex: none;\n display: inline-flex;\n align-items: center;\n border-radius: 999px;\n padding: 0 6px;\n font-size: 11px;\n line-height: 17px;\n font-weight: 500;\n white-space: nowrap;\n border: 1px solid;\n}\n\n._c381e792_slotTagHighCost {\n color: var(--dsw-alias-state-error-primary);\n border-color: var(--dsw-alias-state-error-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);\n}\n\n._23bdb6fc_slotTagMultiplier {\n color: var(--dsw-alias-state-warn-primary);\n border-color: var(--dsw-alias-state-warn-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 10%, transparent);\n}\n\n._f000eed9_slotTagActive {\n color: var(--dsw-alias-state-success-primary);\n border-color: var(--dsw-alias-state-success-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent);\n}\n\n\n._cd905ce5_dragHandle {\n appearance: none;\n border: 0;\n background: none;\n padding: 0;\n cursor: grab;\n color: var(--dsw-alias-label-tertiary);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n border-radius: 6px;\n flex: none;\n}\n\n._cd905ce5_dragHandle:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n._cd905ce5_dragHandle:active {\n cursor: grabbing;\n}\n\n._cd905ce5_dragHandle:disabled {\n cursor: default;\n opacity: 0.4;\n}\n\n._cd905ce5_dragHandle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n\n._fee63b7e_slotCardDragging {\n opacity: 0.45;\n}\n\n._6f108491_slotCardOver {\n outline: 1px dashed var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n}\n\n\n._1a0fa2e1_selectorRowDragging {\n opacity: 0.45;\n}\n\n._688095f2_selectorRowOver {\n outline: 1px dashed var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n border-radius: 6px;\n}\n\n\n._7489fed0_selectorGrip {\n flex: none;\n}\n\n._7489fed0_selectorGrip:disabled {\n display: none;\n}\n\n\n._28237b53_chainBlock {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n\n._15a12620_chainSelectors {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n\n._5694c1de_selectorLine {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: nowrap;\n \n min-height: 32px;\n}\n\n\n._2f156f38_selectorIndex {\n flex: none;\n width: 1.5em;\n text-align: right;\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n font-variant-numeric: tabular-nums;\n}\n\n\n._4843ae21_selectorProviderCell {\n flex: 0 1 clamp(140px, 20%, 220px);\n min-width: 0;\n}\n\n._df828181_selectorModelCell {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n\n._df828181_selectorModelCell > select._f9d86f7b_input {\n max-width: 100%;\n}\n\n\n._fe6d05a5_selectorHint {\n display: flex;\n align-items: center;\n flex: 0 1 auto;\n min-width: 0;\n}\n\n\n._d04ee9fe_selectorHintText {\n flex: 0 1 auto;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._fe6d05a5_selectorHint > ._a6244318_infoHint {\n flex: none;\n margin-left: 4px;\n}\n\n\n._ec7c2fac_selectorRemove {\n flex: none;\n opacity: 0;\n transition: opacity .12s;\n}\n\n\n._ec7c2fac_selectorRemove:disabled {\n display: none;\n}\n\n\n._240cbbf8_selectorRow:hover ._ec7c2fac_selectorRemove:not(:disabled),\n._240cbbf8_selectorRow:focus-within ._ec7c2fac_selectorRemove:not(:disabled) {\n opacity: 1;\n}\n\n@media (hover: none) {\n ._ec7c2fac_selectorRemove:not(:disabled) {\n opacity: 0.7;\n }\n}\n\n\n._70effa64_iconButton {\n position: relative;\n box-sizing: border-box;\n appearance: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n border: 0;\n border-radius: 6px;\n background: none;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n}\n\n._70effa64_iconButton:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._70effa64_iconButton:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n._70effa64_iconButton:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n._70effa64_iconButton::after {\n content: attr(data-tip);\n position: absolute;\n bottom: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n padding: 3px 8px;\n border-radius: 6px;\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n font-size: 11px;\n line-height: 17px;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transition: opacity .12s;\n}\n\n._70effa64_iconButton:hover::after,\n._70effa64_iconButton:focus-visible::after {\n opacity: 1;\n}\n\n\n._a0968257_iconButtonDanger:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n color: var(--dsw-alias-state-error-primary);\n}\n\n\n._711248a9_outlineButton {\n appearance: none;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-md);\n background: none;\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n\n._711248a9_outlineButton:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n._711248a9_outlineButton:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._711248a9_outlineButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n\n._28202c90_addButton {\n align-self: flex-start;\n}\n\n\n._28237b53_chainBlock > ._28202c90_addButton {\n box-sizing: border-box;\n align-self: flex-start;\n height: 26px;\n padding: 0 8px;\n border: 1px solid transparent;\n gap: 4px;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n\n._28237b53_chainBlock > ._28202c90_addButton:hover:not(:disabled) {\n border-color: transparent;\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n\n._28237b53_chainBlock > ._28202c90_addButton:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n\n._6a2bd54c_slotAddRow {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n\n._4e77523e_rowActions {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n\n._f0864181_discard,\n._ccff7e48_save {\n appearance: none;\n border: 1px solid transparent;\n border-radius: var(--dsw-radius-md);\n padding: 5px 14px;\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n cursor: pointer;\n}\n\n._f0864181_discard {\n border-color: var(--dsw-alias-border-l2);\n background: none;\n color: var(--dsw-alias-label-secondary);\n}\n\n._f0864181_discard:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n._ccff7e48_save {\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n}\n\n._f0864181_discard:disabled,\n._ccff7e48_save:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._f0864181_discard:focus-visible,\n._ccff7e48_save:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n\n._fbd045e0_statusBlock {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px 14px;\n margin-top: 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n}\n\n._d4f70367_statusTitle {\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._8ab300ed_statusLine {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n._e8cedbf5_statusLineLabel {\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._700006c9_policyStatus {\n margin: 12px 0 0;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--dsw-alias-bg-module-platform);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n._d4b67a81_policyStatusLabel {\n font-size: 12px;\n font-weight: 600;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n\n._278bb4b3_policyStatusLine {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n}\n\n._da6387e5_policyStatusWarn {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n@media (prefers-reduced-motion: reduce) {\n ._70effa64_iconButton::after,\n ._a6244318_infoHint::after {\n transition: none;\n }\n\n ._631094a0_chevron {\n transition: none;\n }\n}\n";
1791
+ const css$3 = "\n\n\n._20fd0e45_root {\n min-width: 0;\n}\n\n\n._4e39cf17_readOnly {\n margin: 12px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n\n._9860a5b1_notice {\n margin: 12px 0 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-business-primary);\n}\n\n\n._5006d43e_legacyNotice {\n margin: 12px 0 0;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--dsw-alias-bg-module-platform);\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-business-primary);\n}\n\n._21918751_error {\n margin: 12px 0 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-error-primary);\n}\n\n\n._7cea4b2f_noticeRow {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 12px 0 0;\n}\n\n._7cea4b2f_noticeRow ._21918751_error {\n flex: 1;\n min-width: 0;\n margin: 0;\n}\n\n\n._94c91bfd_fieldset {\n margin: 0;\n padding: 0;\n border: none;\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n\n._ba240d82_sectionHeading {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n padding: 4px 0;\n font-size: 16px;\n line-height: 24px;\n font-weight: 600;\n color: var(--dsw-alias-label-primary);\n}\n\n._ddbc1b49_sectionHeadingText {\n display: block;\n min-width: 0;\n}\n\n\n._c41f9db7_sectionActions {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-left: auto;\n}\n\n._c41f9db7_sectionActions ._f0864181_discard,\n._c41f9db7_sectionActions ._ccff7e48_save {\n padding: 3px 12px;\n font-size: 12px;\n line-height: 18px;\n}\n\n\n._b116c758_sectionToggle {\n appearance: none;\n border: 0;\n background: none;\n display: flex;\n align-items: center;\n gap: 4px;\n margin: 0;\n padding: 0;\n font: inherit;\n color: inherit;\n cursor: pointer;\n}\n\n._b116c758_sectionToggle:disabled {\n cursor: default;\n}\n\n._b116c758_sectionToggle:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n\n._80101203_advancedBody {\n display: flex;\n flex-direction: column;\n min-width: 0;\n}\n\n._94c91bfd_fieldset > ._80101203_advancedBody > ._67826267_field,\n._94c91bfd_fieldset > ._80101203_advancedBody > ._9998065c_checkboxRow,\n._94c91bfd_fieldset > ._80101203_advancedBody > ._3e95886d_numberFields {\n padding: 12px 0;\n}\n\n._94c91bfd_fieldset > ._80101203_advancedBody > ._67826267_field + ._67826267_field,\n._94c91bfd_fieldset > ._80101203_advancedBody > ._9998065c_checkboxRow + ._67826267_field,\n._94c91bfd_fieldset > ._80101203_advancedBody > ._67826267_field + ._3e95886d_numberFields {\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n\n._67826267_field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin: 0;\n padding: 0;\n border: none;\n min-width: 0;\n}\n\n\n._94c91bfd_fieldset > ._67826267_field,\n._94c91bfd_fieldset > ._9998065c_checkboxRow,\n._94c91bfd_fieldset > ._3e95886d_numberFields {\n padding: 12px 0;\n}\n\n._94c91bfd_fieldset > ._67826267_field + ._67826267_field,\n._94c91bfd_fieldset > ._67826267_field + ._3e95886d_numberFields,\n._94c91bfd_fieldset > ._9998065c_checkboxRow + ._67826267_field {\n border-top: 0.5px solid var(--dsw-alias-border-l2);\n}\n\n\n._9998065c_checkboxRow {\n display: flex;\n align-items: flex-start;\n gap: 8px;\n}\n\n\n._9998065c_checkboxRow ._06389b18_checkbox {\n margin-top: 3px;\n}\n\n._13e68c3f_fieldLabel {\n \n display: inline-flex;\n align-items: center;\n gap: 10px;\n padding: 0;\n font-size: 13px;\n line-height: 1.5;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n._4bc809b8_hint {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._02c2f6df_checkLabel {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n font-size: 14px;\n line-height: 22px;\n font-weight: 400;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n}\n\n._f2d47237_checkLabelTitle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-weight: 400;\n}\n\n._c1bed8ea_checkLabelDesc {\n font-size: 12px;\n line-height: 18px;\n font-weight: 400;\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._06389b18_checkbox {\n flex: none;\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n._06389b18_checkbox:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n\n._b1962759_slotPresetName {\n font-size: 14px;\n line-height: 22px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n}\n\n\n._a6244318_infoHint {\n position: relative;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 16px;\n height: 16px;\n border-radius: 50%;\n font-size: 14px;\n line-height: 1;\n color: var(--dsw-alias-label-tertiary);\n background: var(--dsw-alias-interactive-bg-hover);\n cursor: help;\n user-select: none;\n}\n\n._a6244318_infoHint:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n._a6244318_infoHint::after {\n content: attr(data-tip);\n position: absolute;\n top: 50%;\n left: calc(100% + 8px);\n transform: translateY(-50%);\n z-index: 10;\n box-sizing: border-box;\n width: max-content;\n max-width: 280px;\n padding: 5px 10px;\n border-radius: 8px;\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n font-size: 11px;\n line-height: 17px;\n font-weight: 400;\n white-space: normal;\n text-align: left;\n pointer-events: none;\n opacity: 0;\n transition: opacity .12s;\n}\n\n._a6244318_infoHint:hover::after,\n._a6244318_infoHint:focus-visible::after {\n opacity: 1;\n}\n\n\n._e655c840_infoHintDisabled {\n opacity: 0.4;\n cursor: default;\n}\n\n\n._a2521bb0_optionRow {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: 6px;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-primary);\n cursor: pointer;\n}\n\n._a2521bb0_optionRow:hover:has(input:not(:disabled)) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n\n._06389b18_checkbox,\n._a2521bb0_optionRow input {\n flex: none;\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n._06389b18_checkbox:focus-visible,\n._a2521bb0_optionRow input:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n._a2521bb0_optionRow input:disabled {\n cursor: default;\n}\n\n\n._c4ecbdf3_dayRow {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n}\n\n._0330c1ed_dayCell {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 6px;\n border-radius: 6px;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n\n._0330c1ed_dayCell:hover:has(input:not(:disabled)) {\n background: var(--dsw-alias-interactive-bg-hover);\n}\n\n._0330c1ed_dayCell input {\n flex: none;\n width: 16px;\n height: 16px;\n margin: 0;\n accent-color: var(--dsw-alias-brand-primary);\n cursor: pointer;\n}\n\n._0330c1ed_dayCell input:disabled {\n cursor: default;\n}\n\n._0330c1ed_dayCell input:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n\n._f9d86f7b_input {\n box-sizing: border-box;\n width: 100%;\n height: 34px;\n padding: 0 12px;\n border: 0.5px solid var(--dsw-alias-border-l4);\n border-radius: var(--dsw-radius-md);\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n background: var(--dsw-alias-bg-layer-3);\n color: var(--dsw-alias-label-primary);\n}\n\n._f9d86f7b_input:focus-visible {\n outline: none;\n border-color: var(--dsw-alias-state-business-primary);\n}\n\n._f9d86f7b_input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n\n._f9d86f7b_input:disabled {\n color: var(--dsw-alias-label-tertiary);\n cursor: default;\n}\n\n\n._3a9b42fd_inputTextarea {\n height: auto;\n min-height: 72px;\n padding: 8px 12px;\n resize: vertical;\n}\n\n\n._1285c19b_personaBriefRow {\n display: flex;\n align-items: center;\n gap: 4px;\n min-width: 0;\n}\n\n._4b8f7345_personaBrief {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-primary);\n}\n\n\n._2686400e_personaBriefEmpty {\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._4e4a07be_personaFull {\n margin: 0;\n font-size: 14px;\n line-height: 22px;\n color: var(--dsw-alias-label-secondary);\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n}\n\n\n._7b60337a_inputInvalid,\n._7b60337a_inputInvalid:focus-visible {\n border-color: var(--dsw-alias-state-error-primary);\n}\n\n\nselect._f9d86f7b_input {\n max-width: 240px;\n cursor: pointer;\n}\n\n\n._16eb7153_selectInput {\n appearance: none;\n padding-right: 32px;\n \n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 12px center;\n background-size: 12px 12px;\n}\n\n\n._3e95886d_numberFields {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));\n gap: 12px;\n}\n\n._3e95886d_numberFields ._67826267_field {\n padding: 0;\n}\n\n._0cfb5881_list {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 4px;\n}\n\n\n._f5c620d6_editorCard {\n display: flex;\n flex-direction: column;\n gap: 14px;\n padding: 14px 16px;\n border-radius: 12px;\n background: var(--dsw-alias-bg-module-platform);\n}\n\n._224b1387_cardFoot {\n display: flex;\n justify-content: flex-end;\n}\n\n._8e220e07_ruleGrid {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n._24a12a2f_ruleCell {\n display: flex;\n flex-direction: column;\n gap: 4px;\n min-width: 120px;\n flex: 1;\n}\n\n._1014c097_ruleCellLabel {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._631094a0_chevron {\n flex: none;\n color: var(--dsw-alias-label-tertiary);\n transform: rotate(-90deg);\n transition: transform .16s;\n}\n\n._44836ce8_chevronOpen {\n transform: none;\n}\n\n\n._cf3961bb_chevronFlip {\n transform: rotate(180deg);\n}\n\n\n._86bc52d2_collapseRow {\n display: flex;\n align-items: center;\n gap: 8px;\n min-width: 0;\n}\n\n._a73efcc6_collapseToggle {\n appearance: none;\n border: 0;\n background: none;\n padding: 0;\n cursor: pointer;\n color: var(--dsw-alias-label-secondary);\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n min-width: 0;\n font: inherit;\n text-align: left;\n}\n\n._a73efcc6_collapseToggle:disabled {\n cursor: default;\n}\n\n._af017718_collapseTitle {\n \n flex: none;\n max-width: 55%;\n min-width: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n._2cabe603_collapseMeta {\n \n flex: 0 1 auto;\n min-width: 0;\n max-width: 40%;\n margin-left: auto;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n\n._52d6df87_seedBadge {\n flex: none;\n max-width: 40%;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n border-radius: 999px;\n padding: 1px 8px;\n font-size: 11px;\n line-height: 17px;\n font-weight: 500;\n white-space: nowrap;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._7b04f727_slotTag {\n flex: none;\n display: inline-flex;\n align-items: center;\n border-radius: 999px;\n padding: 0 6px;\n font-size: 11px;\n line-height: 17px;\n font-weight: 500;\n white-space: nowrap;\n border: 1px solid;\n}\n\n._c381e792_slotTagHighCost {\n color: var(--dsw-alias-state-error-primary);\n border-color: var(--dsw-alias-state-error-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);\n}\n\n._23bdb6fc_slotTagMultiplier {\n color: var(--dsw-alias-state-warn-primary);\n border-color: var(--dsw-alias-state-warn-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 10%, transparent);\n}\n\n._f000eed9_slotTagActive {\n color: var(--dsw-alias-state-success-primary);\n border-color: var(--dsw-alias-state-success-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 10%, transparent);\n}\n\n\n._cd905ce5_dragHandle {\n appearance: none;\n border: 0;\n background: none;\n padding: 0;\n cursor: grab;\n color: var(--dsw-alias-label-tertiary);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n border-radius: 6px;\n flex: none;\n}\n\n._cd905ce5_dragHandle:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n._cd905ce5_dragHandle:active {\n cursor: grabbing;\n}\n\n._cd905ce5_dragHandle:disabled {\n cursor: default;\n opacity: 0.4;\n}\n\n._cd905ce5_dragHandle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n\n._fee63b7e_slotCardDragging {\n opacity: 0.45;\n}\n\n._6f108491_slotCardOver {\n outline: 1px dashed var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n}\n\n\n._1a0fa2e1_selectorRowDragging {\n opacity: 0.45;\n}\n\n._688095f2_selectorRowOver {\n outline: 1px dashed var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n border-radius: 6px;\n}\n\n\n._7489fed0_selectorGrip {\n flex: none;\n}\n\n._7489fed0_selectorGrip:disabled {\n display: none;\n}\n\n\n._28237b53_chainBlock {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n\n._15a12620_chainSelectors {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n\n._5694c1de_selectorLine {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: nowrap;\n \n min-height: 32px;\n}\n\n\n._2f156f38_selectorIndex {\n flex: none;\n width: 1.5em;\n text-align: right;\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-tertiary);\n font-variant-numeric: tabular-nums;\n}\n\n\n._4843ae21_selectorProviderCell {\n flex: 0 1 clamp(140px, 20%, 220px);\n min-width: 0;\n}\n\n._df828181_selectorModelCell {\n flex: 1 1 auto;\n min-width: 0;\n}\n\n\n._df828181_selectorModelCell > select._f9d86f7b_input {\n max-width: 100%;\n}\n\n\n._fe6d05a5_selectorHint {\n display: flex;\n align-items: center;\n flex: 0 1 auto;\n min-width: 0;\n}\n\n\n._d04ee9fe_selectorHintText {\n flex: 0 1 auto;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 11px;\n line-height: 16px;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._fe6d05a5_selectorHint > ._a6244318_infoHint {\n flex: none;\n margin-left: 4px;\n}\n\n\n._ec7c2fac_selectorRemove {\n flex: none;\n opacity: 0;\n transition: opacity .12s;\n}\n\n\n._ec7c2fac_selectorRemove:disabled {\n display: none;\n}\n\n\n._240cbbf8_selectorRow:hover ._ec7c2fac_selectorRemove:not(:disabled),\n._240cbbf8_selectorRow:focus-within ._ec7c2fac_selectorRemove:not(:disabled) {\n opacity: 1;\n}\n\n@media (hover: none) {\n ._ec7c2fac_selectorRemove:not(:disabled) {\n opacity: 0.7;\n }\n}\n\n\n._70effa64_iconButton {\n position: relative;\n box-sizing: border-box;\n appearance: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 28px;\n height: 28px;\n padding: 0;\n border: 0;\n border-radius: 6px;\n background: none;\n color: var(--dsw-alias-label-tertiary);\n cursor: pointer;\n}\n\n._70effa64_iconButton:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._70effa64_iconButton:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n._70effa64_iconButton:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n._70effa64_iconButton::after {\n content: attr(data-tip);\n position: absolute;\n bottom: calc(100% + 6px);\n left: 50%;\n transform: translateX(-50%);\n padding: 3px 8px;\n border-radius: 6px;\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n font-size: 11px;\n line-height: 17px;\n white-space: nowrap;\n opacity: 0;\n pointer-events: none;\n transition: opacity .12s;\n}\n\n._70effa64_iconButton:hover::after,\n._70effa64_iconButton:focus-visible::after {\n opacity: 1;\n}\n\n\n._a0968257_iconButtonDanger:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n color: var(--dsw-alias-state-error-primary);\n}\n\n\n._711248a9_outlineButton {\n appearance: none;\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: var(--dsw-radius-md);\n background: none;\n font: inherit;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n cursor: pointer;\n}\n\n._711248a9_outlineButton:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n._711248a9_outlineButton:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._711248a9_outlineButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n\n._28202c90_addButton {\n align-self: flex-start;\n}\n\n\n._28237b53_chainBlock > ._28202c90_addButton {\n box-sizing: border-box;\n align-self: flex-start;\n height: 26px;\n padding: 0 8px;\n border: 1px solid transparent;\n gap: 4px;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n\n._28237b53_chainBlock > ._28202c90_addButton:hover:not(:disabled) {\n border-color: transparent;\n background: var(--dsw-alias-interactive-bg-hover);\n color: var(--dsw-alias-label-primary);\n}\n\n\n._28237b53_chainBlock > ._28202c90_addButton:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\n}\n\n\n._6a2bd54c_slotAddRow {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 4px;\n flex-wrap: wrap;\n}\n\n\n._4e77523e_rowActions {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n\n._f0864181_discard,\n._ccff7e48_save {\n appearance: none;\n border: 1px solid transparent;\n border-radius: var(--dsw-radius-md);\n padding: 5px 14px;\n font: inherit;\n font-size: 13px;\n line-height: 1.5;\n cursor: pointer;\n}\n\n._f0864181_discard {\n border-color: var(--dsw-alias-border-l2);\n background: none;\n color: var(--dsw-alias-label-secondary);\n}\n\n._f0864181_discard:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n._ccff7e48_save {\n background: var(--dsw-alias-label-primary);\n color: var(--dsw-alias-bg-layer-3);\n}\n\n._f0864181_discard:disabled,\n._ccff7e48_save:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._f0864181_discard:focus-visible,\n._ccff7e48_save:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n\n._fbd045e0_statusBlock {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px 14px;\n margin-top: 12px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n}\n\n._d4f70367_statusTitle {\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._8ab300ed_statusLine {\n margin: 0;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n._e8cedbf5_statusLineLabel {\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._700006c9_policyStatus {\n margin: 12px 0 0;\n padding: 10px 12px;\n border-radius: 8px;\n background: var(--dsw-alias-bg-module-platform);\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n\n._aa3fae4a_policyStatusText {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n._d90f6286_policyStatusSync {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n}\n\n\n._579f87fb_policyStatusLive:empty {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n._d4b67a81_policyStatusLabel {\n font-size: 12px;\n font-weight: 600;\n line-height: 18px;\n color: var(--dsw-alias-label-secondary);\n}\n\n._278bb4b3_policyStatusLine {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-primary);\n}\n\n._da6387e5_policyStatusWarn {\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n@media (prefers-reduced-motion: reduce) {\n ._70effa64_iconButton::after,\n ._a6244318_infoHint::after {\n transition: none;\n }\n\n ._631094a0_chevron {\n transition: none;\n }\n}\n";
1752
1792
  const tagId$3 = "dsh-llm-fallbacks/FallbacksCard.module.css";
1753
1793
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$3) + "]") === null) {
1754
1794
  const tag = document.createElement("style");
@@ -1840,6 +1880,9 @@ window.__ModuleLoader__.load({
1840
1880
  "statusLine": "_8ab300ed_statusLine",
1841
1881
  "statusLineLabel": "_e8cedbf5_statusLineLabel",
1842
1882
  "policyStatus": "_700006c9_policyStatus",
1883
+ "policyStatusText": "_aa3fae4a_policyStatusText",
1884
+ "policyStatusSync": "_d90f6286_policyStatusSync",
1885
+ "policyStatusLive": "_579f87fb_policyStatusLive",
1843
1886
  "policyStatusLabel": "_d4b67a81_policyStatusLabel",
1844
1887
  "policyStatusLine": "_278bb4b3_policyStatusLine",
1845
1888
  "policyStatusWarn": "_da6387e5_policyStatusWarn"
@@ -2327,14 +2370,58 @@ window.__ModuleLoader__.load({
2327
2370
  });
2328
2371
  }
2329
2372
  /**
2373
+ * The virtual all-fallbacks route the host carve-out recognizes (features.md):
2374
+ * a subagent spawned on it is not allowlist-filtered, so it is the
2375
+ * recommendation when the allowlist does not already carry it.
2376
+ */
2377
+ const VIRTUAL_ALL_ROUTE = {
2378
+ provider: "FallbacksChain",
2379
+ model: "Auto"
2380
+ };
2381
+ /**
2382
+ * Parse one raw selector into an exact route; wildcard and unparseable
2383
+ * entries resolve to `undefined` (they are not exact routes — never expanded,
2384
+ * never counted). The selector parser is the single grammar authority.
2385
+ */
2386
+ function exactRouteOf(raw) {
2387
+ let parsed;
2388
+ try {
2389
+ parsed = parseSelector(raw);
2390
+ } catch {
2391
+ return;
2392
+ }
2393
+ return parsed.model === void 0 ? void 0 : {
2394
+ provider: parsed.provider,
2395
+ model: parsed.model
2396
+ };
2397
+ }
2398
+ /**
2330
2399
  * The whole chain as one line (plan fallbacks-chain-reorder T2, user request):
2331
2400
  * every non-blank entry in walk order joined with `>`, or `undefined` for an
2332
2401
  * empty/blank chain so the caller keeps its own summary (a role's raw strategy
2333
2402
  * token). The card ellipsizes long summaries; the full string rides `title`.
2403
+ *
2404
+ * With `allowedModels` (host policy enabled) exact entries the policy cannot
2405
+ * use are dropped, so the title tells the truth about what will run; wildcard
2406
+ * entries stay (their resolved routes are unknown client-side) and an
2407
+ * unparseable entry is kept rather than hidden.
2334
2408
  */
2335
- function chainSummary(selectors) {
2409
+ function chainSummary(selectors, allowedModels) {
2336
2410
  const entries = selectors.map(selectorRowToRaw).filter((entry) => entry !== "");
2337
- return entries.length === 0 ? void 0 : entries.join(" > ");
2411
+ const visible = allowedModels === void 0 ? entries : entries.filter((entry) => {
2412
+ const route = exactRouteOf(entry);
2413
+ return route === void 0 || routeOnAllowlist(route, allowedModels);
2414
+ });
2415
+ return visible.length === 0 ? void 0 : visible.join(" > ");
2416
+ }
2417
+ /** How many selected role-chain exact entries the host allowlist does not carry. */
2418
+ function offAllowlistEntryCount(roles, allowedModels) {
2419
+ let count = 0;
2420
+ for (const role of roles) for (const entry of role.chain ?? []) {
2421
+ const route = exactRouteOf(entry);
2422
+ if (route !== void 0 && !routeOnAllowlist(route, allowedModels)) count += 1;
2423
+ }
2424
+ return count;
2338
2425
  }
2339
2426
  /**
2340
2427
  * Pure chain reorder (plan fallbacks-chain-reorder T1): a copy of `list` with
@@ -2644,6 +2731,19 @@ window.__ModuleLoader__.load({
2644
2731
  state.config.alwaysModeRetryCap,
2645
2732
  state.config.roleAutoMatch
2646
2733
  ]);
2734
+ const enabledPolicy = state.subagentPolicy?.state === "enabled" ? state.subagentPolicy : void 0;
2735
+ const allowedModels = enabledPolicy?.allowedModels;
2736
+ const offAllowlistCount = enabledPolicy === void 0 ? 0 : offAllowlistEntryCount(draft.roles.list, enabledPolicy.allowedModels);
2737
+ const virtualRouteAllowed = enabledPolicy !== void 0 && routeOnAllowlist(VIRTUAL_ALL_ROUTE, enabledPolicy.allowedModels);
2738
+ const [syncStatus, setSyncStatus] = (0, react.useState)("idle");
2739
+ const syncDirtyHintId = `fallbacks-policy-sync-dirty-${(0, react.useId)().replace(/:/g, "")}`;
2740
+ const syncAllowlist = () => {
2741
+ if (syncStatus === "pending") return;
2742
+ setSyncStatus("pending");
2743
+ controller.syncAllowlist().then((ok) => {
2744
+ setSyncStatus(ok ? "ok" : "error");
2745
+ });
2746
+ };
2647
2747
  const firstSeedDone = (0, react.useRef)(false);
2648
2748
  (0, react.useEffect)(() => {
2649
2749
  if (state.status !== "ready") return;
@@ -3566,26 +3666,65 @@ window.__ModuleLoader__.load({
3566
3666
  }),
3567
3667
  state.subagentPolicy?.state === "enabled" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3568
3668
  className: FallbacksCard_module_css_default.policyStatus,
3569
- role: "status",
3570
- children: [
3571
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3572
- className: FallbacksCard_module_css_default.policyStatusLabel,
3573
- children: t("subagents.policy.label")
3574
- }),
3575
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3576
- className: FallbacksCard_module_css_default.policyStatusLine,
3577
- children: `${t("subagents.policy.allowlist")}: ${state.subagentPolicy.allowedModels.map((route) => `${route.provider}/${route.model}`).join(", ")}`
3578
- }),
3579
- state.subagentPolicy.head !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3580
- className: FallbacksCard_module_css_default.policyStatusLine,
3581
- children: `${t("subagents.policy.head")}: ${state.subagentPolicy.head.route.provider}/${state.subagentPolicy.head.route.model} (${t(state.subagentPolicy.head.source === "authorized" ? "subagents.policy.source.authorized" : "subagents.policy.source.injected")})`
3582
- }),
3583
- state.subagentPolicy.blockedAttempt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3584
- className: FallbacksCard_module_css_default.policyStatusWarn,
3585
- role: "alert",
3586
- children: t("subagents.policy.blocked")
3587
- })
3588
- ]
3669
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3670
+ className: FallbacksCard_module_css_default.policyStatusText,
3671
+ role: "status",
3672
+ children: [
3673
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3674
+ className: FallbacksCard_module_css_default.policyStatusLabel,
3675
+ children: t("subagents.policy.label")
3676
+ }),
3677
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3678
+ className: FallbacksCard_module_css_default.policyStatusLine,
3679
+ children: `${t("subagents.policy.allowlist")}: ${state.subagentPolicy.allowedModels.map((route) => `${route.provider}/${route.model}`).join(", ")}`
3680
+ }),
3681
+ state.subagentPolicy.head !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3682
+ className: FallbacksCard_module_css_default.policyStatusLine,
3683
+ children: `${t("subagents.policy.head")}: ${state.subagentPolicy.head.route.provider}/${state.subagentPolicy.head.route.model} (${t(state.subagentPolicy.head.source === "authorized" ? "subagents.policy.source.authorized" : "subagents.policy.source.injected")})`
3684
+ }),
3685
+ state.subagentPolicy.blockedAttempt !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3686
+ className: FallbacksCard_module_css_default.policyStatusWarn,
3687
+ role: "alert",
3688
+ children: t("subagents.policy.blocked")
3689
+ }),
3690
+ offAllowlistCount > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3691
+ className: FallbacksCard_module_css_default.policyStatusWarn,
3692
+ children: t("subagents.policy.offAllowlist", { count: String(offAllowlistCount) })
3693
+ }),
3694
+ !virtualRouteAllowed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3695
+ className: FallbacksCard_module_css_default.policyStatusLine,
3696
+ children: t("subagents.policy.recommend")
3697
+ })
3698
+ ]
3699
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3700
+ className: FallbacksCard_module_css_default.policyStatusSync,
3701
+ role: "group",
3702
+ "aria-label": t("subagents.policy.sync"),
3703
+ children: [
3704
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
3705
+ type: "button",
3706
+ className: FallbacksCard_module_css_default.outlineButton,
3707
+ disabled: !writable || subDirty || mainDirty,
3708
+ "aria-busy": syncStatus === "pending" || void 0,
3709
+ "aria-describedby": subDirty || mainDirty ? syncDirtyHintId : void 0,
3710
+ onClick: syncAllowlist,
3711
+ children: t("subagents.policy.sync")
3712
+ }),
3713
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3714
+ id: syncDirtyHintId,
3715
+ role: "status",
3716
+ "data-sync-hint": "dirty",
3717
+ className: `${FallbacksCard_module_css_default.policyStatusWarn} ${FallbacksCard_module_css_default.policyStatusLive}`,
3718
+ children: subDirty || mainDirty ? t("subagents.policy.sync.dirty") : ""
3719
+ }),
3720
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
3721
+ role: "status",
3722
+ "data-sync-hint": "outcome",
3723
+ className: `${syncStatus === "error" ? FallbacksCard_module_css_default.policyStatusWarn : FallbacksCard_module_css_default.policyStatusLine} ${FallbacksCard_module_css_default.policyStatusLive}`,
3724
+ children: syncStatus === "ok" ? t("subagents.policy.sync.done") : syncStatus === "error" ? t("subagents.policy.sync.failed") : ""
3725
+ })
3726
+ ]
3727
+ })]
3589
3728
  }),
3590
3729
  state.subagentPolicy?.state === "unprovable" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3591
3730
  className: FallbacksCard_module_css_default.policyStatus,
@@ -3622,7 +3761,7 @@ window.__ModuleLoader__.load({
3622
3761
  const seed = seedInfo.get(row.id.trim());
3623
3762
  const seedBadge = seedBadgeLabel(seed, t);
3624
3763
  const personaBlank = row.persona.trim() === "";
3625
- const roleChainSummary = chainSummary(row.selectors);
3764
+ const roleChainSummary = chainSummary(row.selectors, allowedModels);
3626
3765
  const roleSummary = roleChainSummary ?? row.fallback;
3627
3766
  const roleExpanded = !row.collapsed || !writable;
3628
3767
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -4578,7 +4717,7 @@ window.__ModuleLoader__.load({
4578
4717
  }
4579
4718
  //#endregion
4580
4719
  //#region \0dsh-css:/home/runner/work/dsh-llm-fallbacks/dsh-llm-fallbacks/src/client/SubagentRoleBadge.module.css.mjs
4581
- const css = "\n\n._35648278_badge {\n display: inline-block;\n max-width: 120px;\n padding: 0 8px;\n border-radius: 999px;\n font-size: 12px;\n line-height: 20px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n vertical-align: middle;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-primary);\n}\n";
4720
+ const css = "\n\n._35648278_badge {\n display: inline-block;\n max-width: 240px;\n padding: 0 8px;\n border-radius: 999px;\n font-size: 12px;\n line-height: 20px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n vertical-align: middle;\n background: var(--dsw-alias-bg-module-platform);\n color: var(--dsw-alias-label-primary);\n}\n\n\n._321abf1e_route {\n color: var(--dsw-alias-label-tertiary);\n}\n";
4582
4721
  const tagId = "dsh-llm-fallbacks/SubagentRoleBadge.module.css";
4583
4722
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
4584
4723
  const tag = document.createElement("style");
@@ -4587,51 +4726,84 @@ window.__ModuleLoader__.load({
4587
4726
  tag.textContent = css;
4588
4727
  document.head.appendChild(tag);
4589
4728
  }
4590
- var SubagentRoleBadge_module_css_default = { "badge": "_35648278_badge" };
4729
+ var SubagentRoleBadge_module_css_default = {
4730
+ "badge": "_35648278_badge",
4731
+ "route": "_321abf1e_route"
4732
+ };
4591
4733
  //#endregion
4592
4734
  //#region src/client/SubagentRoleBadge.tsx
4593
4735
  /**
4594
4736
  * The host's own durable model-selection projection key (`dsh-api-session-
4595
4737
  * controller`, merged into `SessionProjectionMap`): its client value carries
4596
- * `lastUsed` — the route the LATEST recorded request ran on. Read through the
4597
- * same structural seat as the role key.
4738
+ * `next` (the route the next request will use) and `lastUsed` (the route the
4739
+ * latest recorded request ran on). Read through the same structural seat as the
4740
+ * role key.
4598
4741
  */
4599
4742
  const MODEL_SELECTION_PROJECTION_KEY = "modelSelection";
4600
4743
  /**
4601
- * `provider/model` of the model-selection projection's `lastUsed`, or
4602
- * `undefined` when the value/key/fields are absent or foreign — the badge then
4603
- * hovers the role alone instead of rendering a wrong route.
4744
+ * `provider/model` of one model selection, or `undefined` when the value's
4745
+ * fields are absent or foreign.
4604
4746
  */
4605
- function latestRequestRoute(value) {
4747
+ function selectionRoute(value) {
4606
4748
  if (typeof value !== "object" || value === null) return void 0;
4607
- const { lastUsed } = value;
4608
- if (typeof lastUsed !== "object" || lastUsed === null) return void 0;
4609
- const { provider, model } = lastUsed;
4610
- if (typeof provider !== "string" || provider === "" || typeof model !== "string" || model === "") return void 0;
4611
- return `${provider}/${model}`;
4749
+ const { provider, model } = value;
4750
+ if (typeof provider !== "string" || typeof model !== "string") return void 0;
4751
+ const trimmedProvider = provider.trim();
4752
+ const trimmedModel = model.trim();
4753
+ if (trimmedProvider === "" || trimmedModel === "") return void 0;
4754
+ return `${trimmedProvider}/${trimmedModel}`;
4755
+ }
4756
+ /** Display form of a route in the pill body; the title keeps the full route. */
4757
+ function displayRoute(route) {
4758
+ return route.length > 120 ? `${route.slice(0, 119)}…` : route;
4612
4759
  }
4613
4760
  /**
4614
- * Render the viewed session's dispatch-resolved role badge.
4615
- * @param props - composed slot props (the session-kit `useProjection` seat is
4616
- * read structurally — see the module docblock).
4761
+ * `provider/model` the child's NEXT request will use, falling back to the route
4762
+ * its latest recorded request ran on (`modelSelection.next` → `lastUsed`), or
4763
+ * `undefined` when neither carries a route — the badge then renders the role
4764
+ * alone instead of a wrong route.
4765
+ */
4766
+ function modelSelectionRoute(value) {
4767
+ if (typeof value !== "object" || value === null) return void 0;
4768
+ const { next, lastUsed } = value;
4769
+ return selectionRoute(next) ?? selectionRoute(lastUsed);
4770
+ }
4771
+ /**
4772
+ * Render the viewed SUBAGENT session's role and model.
4773
+ * @param props - composed slot props (the session-kit `useProjection` /
4774
+ * `useSessions` seats are read structurally — see the module docblock).
4617
4775
  * @returns the badge element, or `null` when there is nothing to show.
4618
4776
  */
4619
4777
  function SubagentRoleBadge(props) {
4620
4778
  const { t } = props;
4621
4779
  const readProjection = props.useProjection;
4622
- if (typeof readProjection !== "function") return null;
4780
+ const useSessions = props.useSessions;
4781
+ const sessionId = props.sessionId;
4782
+ if (typeof readProjection !== "function" || typeof useSessions !== "function") return null;
4783
+ if (typeof sessionId !== "string") return null;
4784
+ const isChild = useSessions((state) => state.byId?.[sessionId]?.origin === "subagent");
4623
4785
  const rawRole = readProjection(ROLE_PROJECTION_KEY);
4624
4786
  const rawModelSelection = readProjection(MODEL_SELECTION_PROJECTION_KEY);
4625
- const role = readRoleProjectionValue(rawRole);
4626
- if (role === void 0 || role === "inherit") return null;
4627
- const model = latestRequestRoute(rawModelSelection);
4628
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4787
+ if (!isChild) return null;
4788
+ const declared = readRoleProjectionValue(rawRole);
4789
+ const role = declared === "inherit" ? void 0 : declared;
4790
+ const model = modelSelectionRoute(rawModelSelection);
4791
+ if (role === void 0 && model === void 0) return null;
4792
+ const title = role !== void 0 && model !== void 0 ? t("subagentRole.hover", {
4793
+ role,
4794
+ model
4795
+ }) : role ?? model;
4796
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4629
4797
  className: SubagentRoleBadge_module_css_default.badge,
4630
- title: model === void 0 ? role : t("subagentRole.hover", {
4798
+ title,
4799
+ children: [
4631
4800
  role,
4632
- model
4633
- }),
4634
- children: role
4801
+ role !== void 0 && model !== void 0 && " · ",
4802
+ model !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
4803
+ className: SubagentRoleBadge_module_css_default.route,
4804
+ children: displayRoute(model)
4805
+ })
4806
+ ]
4635
4807
  });
4636
4808
  }
4637
4809
  //#endregion
@@ -51,6 +51,12 @@ export declare const zh: {
51
51
  'subagents.policy.source.injected': string;
52
52
  'subagents.policy.blocked': string;
53
53
  'subagents.policy.unprovable': string;
54
+ 'subagents.policy.offAllowlist': string;
55
+ 'subagents.policy.recommend': string;
56
+ 'subagents.policy.sync': string;
57
+ 'subagents.policy.sync.done': string;
58
+ 'subagents.policy.sync.failed': string;
59
+ 'subagents.policy.sync.dirty': string;
54
60
  'timeSlots.label': string;
55
61
  'timeSlots.hint': string;
56
62
  'timeSlots.tz.label': string;
@@ -243,6 +249,12 @@ export declare const en: {
243
249
  'subagents.policy.source.injected': string;
244
250
  'subagents.policy.blocked': string;
245
251
  'subagents.policy.unprovable': string;
252
+ 'subagents.policy.offAllowlist': string;
253
+ 'subagents.policy.recommend': string;
254
+ 'subagents.policy.sync': string;
255
+ 'subagents.policy.sync.done': string;
256
+ 'subagents.policy.sync.failed': string;
257
+ 'subagents.policy.sync.dirty': string;
246
258
  'timeSlots.label': string;
247
259
  'timeSlots.hint': string;
248
260
  'timeSlots.tz.label': string;
package/dist/gateway.d.ts CHANGED
@@ -275,6 +275,22 @@ export declare class FallbacksConfigGateway extends TypertRemoteService {
275
275
  * write channel is unavailable (KD-G5).
276
276
  */
277
277
  revertSeed(id: string): Promise<FallbacksRevertResult>;
278
+ /**
279
+ * Merge the CURRENT host allowlist with the exact routes configured in this
280
+ * plugin's chains (every `roles.list[].chain` entry plus `rootChain`), plus
281
+ * the virtual `FallbacksChain/Auto` route. De-duplicated by exact
282
+ * provider+model with first-seen order kept (the current allowlist first).
283
+ * Wildcard (`provider/*`) and unparseable entries are skipped — never
284
+ * expanded into routes. This is the only allowlist write face; it runs
285
+ * exclusively from the explicit card button (never silently) and writes
286
+ * ONLY `allowedModels` — never `enabled`.
287
+ * @returns the post-write read result (the fresh `subagentPolicy` snapshot
288
+ * reflects the new allowlist).
289
+ * @throws when no live policy snapshot is wired, the policy is not proven
290
+ * enabled (`disabled` / `unprovable`), or no settings service is composed
291
+ * (KD-G5: no allowlist write without a proven-enabled policy).
292
+ */
293
+ syncAllowlist(): Promise<FallbacksReadResult>;
278
294
  /**
279
295
  * Read the live composed config and normalize it to the typertGateway JSON
280
296
  * wire boundary. Containment (guide §10): a malformed stored patch section
package/dist/index.js CHANGED
@@ -1572,6 +1572,11 @@ function overrideConfigWithRouteRule(seed, to, explicitEffort) {
1572
1572
  //#endregion
1573
1573
  //#region .build/host/gateway.js
1574
1574
  const FALLBACKS_PROFILE_ENTRY = "llm-fallbacks";
1575
+ const SUBAGENT_POLICY_SETTINGS_NS = "subagent-model-selection-settings";
1576
+ const VIRTUAL_ALL_ROUTE = {
1577
+ provider: "FallbacksChain",
1578
+ model: "Auto"
1579
+ };
1575
1580
  const CONFIG_KEYS = {
1576
1581
  triggerCodes: true,
1577
1582
  rootChain: true,
@@ -1645,6 +1650,21 @@ var FallbacksConfigGateway = class extends TypertRemoteService {
1645
1650
  outcome
1646
1651
  };
1647
1652
  }
1653
+ async syncAllowlist() {
1654
+ const snapshot = this.subagentPolicy;
1655
+ if (snapshot === void 0) throw new Error("fallbacks: subagent policy is unavailable — allowlist cannot be synced");
1656
+ const { policy } = snapshot();
1657
+ if (policy.state !== "enabled") throw new Error(`fallbacks: subagent policy is ${policy.state} — allowlist cannot be synced`);
1658
+ const settings = this.settings;
1659
+ if (settings === void 0) throw new Error("fallbacks: settings service is unavailable — configuration cannot be written");
1660
+ const merged = mergedAllowlist(policy.allowedModels, this.bridge.source());
1661
+ await settings.mutate(SUBAGENT_POLICY_SETTINGS_NS, [{
1662
+ op: "set",
1663
+ path: ["allowedModels"],
1664
+ value: merged
1665
+ }]);
1666
+ return this.readResult();
1667
+ }
1648
1668
  readConfig(source = this.bridge.source()) {
1649
1669
  const wire = {};
1650
1670
  for (const key of Object.keys(CONFIG_KEYS)) {
@@ -1676,6 +1696,36 @@ var FallbacksConfigGateway = class extends TypertRemoteService {
1676
1696
  };
1677
1697
  }
1678
1698
  };
1699
+ function mergedAllowlist(allowedModels, source) {
1700
+ const merged = [];
1701
+ const seen = new Set();
1702
+ const push = (route) => {
1703
+ const key = `${route.provider}\0${route.model}`;
1704
+ if (seen.has(key)) return;
1705
+ seen.add(key);
1706
+ merged.push({
1707
+ provider: route.provider,
1708
+ model: route.model
1709
+ });
1710
+ };
1711
+ for (const route of allowedModels) push(route);
1712
+ const entries = [...source.roles.list.flatMap((role) => role.chain ?? []), ...source.rootChain];
1713
+ for (const entry of entries) {
1714
+ let parsed;
1715
+ try {
1716
+ parsed = parseSelector(entry);
1717
+ } catch {
1718
+ continue;
1719
+ }
1720
+ if (parsed.model === void 0) continue;
1721
+ push({
1722
+ provider: parsed.provider,
1723
+ model: parsed.model
1724
+ });
1725
+ }
1726
+ push(VIRTUAL_ALL_ROUTE);
1727
+ return merged;
1728
+ }
1679
1729
  function projectSubagentPolicy(snapshot) {
1680
1730
  if (snapshot === void 0) return void 0;
1681
1731
  let captured;
@@ -1825,6 +1875,16 @@ function fallbacksTypertContribution() {
1825
1875
  codec: { mode: "src-json" }
1826
1876
  }],
1827
1877
  result: { mode: "src-json" }
1878
+ },
1879
+ {
1880
+ id: "dsh-llm-fallbacks#fallbacks/sync-allowlist",
1881
+ service: "fallbacks",
1882
+ namespace: "fallbacks",
1883
+ method: "sync-allowlist",
1884
+ implementation: "syncAllowlist",
1885
+ invocation: { kind: "direct" },
1886
+ parameters: [],
1887
+ result: { mode: "src-json" }
1828
1888
  }
1829
1889
  ]
1830
1890
  };
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "dsh",
6
6
  "dsh-plugin"
7
7
  ],
8
- "version": "0.6.9",
8
+ "version": "0.6.11",
9
9
  "type": "module",
10
10
  "main": "./dist/index.js",
11
11
  "types": "./dist/index.d.ts",
@@ -65,29 +65,29 @@
65
65
  },
66
66
  "peerDependencies": {
67
67
  "@deepseek-ai/cordis": "^4.0.5",
68
- "@deepseek-ai/dsh-agent": "^0.2.1-rc.2",
69
- "@deepseek-ai/dsh-api-gateway": "^0.2.1-rc.2",
70
- "@deepseek-ai/dsh-api-remotes": "^0.2.1-rc.2",
71
- "@deepseek-ai/dsh-api-session-controller": "^0.2.1-rc.2",
72
- "@deepseek-ai/dsh-client-connection": "^0.2.1-rc.2",
73
- "@deepseek-ai/dsh-client-locale": "^0.2.1-rc.2",
74
- "@deepseek-ai/dsh-client-store": "^0.2.1-rc.2",
75
- "@deepseek-ai/dsh-client-ui-chat": "^0.2.1-rc.2",
76
- "@deepseek-ai/dsh-client-ui-conversation": "^0.2.1-rc.2",
77
- "@deepseek-ai/dsh-client-ui-plugin-manager": "^0.2.1-rc.2",
78
- "@deepseek-ai/dsh-client-ui-renderer": "^0.2.1-rc.2",
79
- "@deepseek-ai/dsh-client-ui-settings": "^0.2.1-rc.2",
80
- "@deepseek-ai/dsh-client-ui-slots": "^0.2.1-rc.2",
81
- "@deepseek-ai/dsh-commands": "^0.2.1-rc.2",
82
- "@deepseek-ai/dsh-llm": "^0.2.1-rc.2",
83
- "@deepseek-ai/dsh-llm-retry": "^0.2.1-rc.2",
84
- "@deepseek-ai/dsh-session": "^0.2.1-rc.2",
85
- "@deepseek-ai/dsh-session-projection": "^0.2.1-rc.2",
86
- "@deepseek-ai/dsh-settings": "^0.2.1-rc.2",
87
- "@deepseek-ai/dsh-system-prompt": "^0.2.1-rc.2",
88
- "@deepseek-ai/dsh-tool-subagent": "^0.2.1-rc.2",
89
- "@deepseek-ai/dsh-typert-protocol": "^0.2.1-rc.2",
90
- "@deepseek-ai/dsh-typert-registry": "^0.2.1-rc.2",
68
+ "@deepseek-ai/dsh-agent": "^0.2.1-rc.3",
69
+ "@deepseek-ai/dsh-api-gateway": "^0.2.1-rc.3",
70
+ "@deepseek-ai/dsh-api-remotes": "^0.2.1-rc.3",
71
+ "@deepseek-ai/dsh-api-session-controller": "^0.2.1-rc.3",
72
+ "@deepseek-ai/dsh-client-connection": "^0.2.1-rc.3",
73
+ "@deepseek-ai/dsh-client-locale": "^0.2.1-rc.3",
74
+ "@deepseek-ai/dsh-client-store": "^0.2.1-rc.3",
75
+ "@deepseek-ai/dsh-client-ui-chat": "^0.2.1-rc.3",
76
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.2.1-rc.3",
77
+ "@deepseek-ai/dsh-client-ui-plugin-manager": "^0.2.1-rc.3",
78
+ "@deepseek-ai/dsh-client-ui-renderer": "^0.2.1-rc.3",
79
+ "@deepseek-ai/dsh-client-ui-settings": "^0.2.1-rc.3",
80
+ "@deepseek-ai/dsh-client-ui-slots": "^0.2.1-rc.3",
81
+ "@deepseek-ai/dsh-commands": "^0.2.1-rc.3",
82
+ "@deepseek-ai/dsh-llm": "^0.2.1-rc.3",
83
+ "@deepseek-ai/dsh-llm-retry": "^0.2.1-rc.3",
84
+ "@deepseek-ai/dsh-session": "^0.2.1-rc.3",
85
+ "@deepseek-ai/dsh-session-projection": "^0.2.1-rc.3",
86
+ "@deepseek-ai/dsh-settings": "^0.2.1-rc.3",
87
+ "@deepseek-ai/dsh-system-prompt": "^0.2.1-rc.3",
88
+ "@deepseek-ai/dsh-tool-subagent": "^0.2.1-rc.3",
89
+ "@deepseek-ai/dsh-typert-protocol": "^0.2.1-rc.3",
90
+ "@deepseek-ai/dsh-typert-registry": "^0.2.1-rc.3",
91
91
  "@deepseek-ai/schemastery": "^3.18.5",
92
92
  "react": "^18.2.0"
93
93
  },
@@ -104,7 +104,7 @@
104
104
  "clsx": "^2.1.1",
105
105
  "immer": "^10.2.0",
106
106
  "jsdom": "29.1.1",
107
- "katex": "^0.16.47",
107
+ "katex": "^0.18.2",
108
108
  "mdast-util-from-markdown": "^2.0.3",
109
109
  "mdast-util-gfm": "^3.1.0",
110
110
  "mdast-util-math": "^3.0.0",
@@ -123,7 +123,7 @@
123
123
  "tsdown": "^0.22.2",
124
124
  "tsx": "^4.22.4",
125
125
  "typescript": "^5.9.3",
126
- "vitest": "^4.1.8",
126
+ "vitest": "^4.1.12",
127
127
  "zustand": "~4.4.7"
128
128
  },
129
129
  "repository": {