dsh-llm-fallbacks 0.1.3 → 0.1.4

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
@@ -6,6 +6,7 @@
6
6
  ![node](https://img.shields.io/badge/node-%3E%3D22-339933.svg)
7
7
  ![pnpm](https://img.shields.io/badge/pnpm-%3E%3D10-f69220.svg)
8
8
  ![dsh](https://img.shields.io/badge/dsh-DeepSeek%20Harness%20compatible-4B32C3.svg)
9
+ [![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)
9
10
 
10
11
  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.
11
12
 
@@ -35,7 +36,7 @@ dsh plugin --profile web add dsh-llm-fallbacks # pin a version with @<version>
35
36
 
36
37
  A registry install fetches the **built package** (`dist/`) — nothing is built on the target machine. The plugin is **mount-only**: it never modifies the dsh source tree, and no patch / postinstall step exists — dsh upgrades never require re-patching. Versioning follows npm dist-tags (`latest` by default); pin an exact version with `dsh plugin --profile web add dsh-llm-fallbacks@<version>`.
37
38
 
38
- > **Release status**: published as `dsh-llm-fallbacks@0.1.0-alpha.2` (latest). The first publish used a one-time `NODE_AUTH_TOKEN` bootstrap secret; Trusted Publishing is configured afterwards for tokenless releases.
39
+ > **Release status**: published as `dsh-llm-fallbacks@0.1.3` (latest). The first publish used a one-time `NODE_AUTH_TOKEN` bootstrap secret; Trusted Publishing is configured afterwards for tokenless releases.
39
40
 
40
41
  ### Registry package (npm / pnpm)
41
42
 
package/README.zh-CN.md CHANGED
@@ -6,6 +6,7 @@
6
6
  ![node](https://img.shields.io/badge/node-%3E%3D22-339933.svg)
7
7
  ![pnpm](https://img.shields.io/badge/pnpm-%3E%3D10-f69220.svg)
8
8
  ![dsh](https://img.shields.io/badge/dsh-DeepSeek%20Harness%20compatible-4B32C3.svg)
9
+ [![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)
9
10
 
10
11
  dsh(DeepSeek Harness)的自动模型降级插件:当 root agent 或 subagent 的模型请求持续失败(重试耗尽、权限、配额超限、限流 429)时,按角色/模型 fallback 链自动切换 provider/model,当前 step/turn 在目标模型上继续完成——任务不因模型问题中断。
11
12
 
@@ -35,7 +36,7 @@ dsh plugin --profile web add dsh-llm-fallbacks # 钉版本:加 @<version>
35
36
 
36
37
  registry 安装拉取的是**已构建产物**(`dist/`),目标机无需构建。插件为**纯挂载**:对 dsh 源码树零修改,无任何补丁 / postinstall 步骤——dsh 升级无需重打。版本跟随 npm dist-tag(默认 `latest`);钉精确版本:`dsh plugin --profile web add dsh-llm-fallbacks@<version>`。
37
38
 
38
- > **发布状态**:已发布 `dsh-llm-fallbacks@0.1.0-alpha.2`(latest)。首次发布用一次性 `NODE_AUTH_TOKEN` bootstrap;Trusted Publishing 后续配置为无 token 发布。
39
+ > **发布状态**:已发布 `dsh-llm-fallbacks@0.1.3`(latest)。首次发布用一次性 `NODE_AUTH_TOKEN` bootstrap;Trusted Publishing 后续配置为无 token 发布。
39
40
 
40
41
  ### registry 包安装(npm / pnpm)
41
42
 
@@ -262,7 +262,14 @@ export declare class FallbacksSettingsController {
262
262
  private readonly rpc;
263
263
  /** Snapshot consumed by the section through `useSyncExternalStore`. */
264
264
  readonly store: SnapshotStore<FallbacksSettingsState>;
265
- private generation;
265
+ /** Read guard: a newer load() supersedes an older one's publish. */
266
+ private readGeneration;
267
+ /**
268
+ * Write guard: save()/resetToDefaults() completions ALWAYS publish unless
269
+ * dispose() invalidated them — an overlapping read must never discard a
270
+ * successful write's accept() (audit F1).
271
+ */
272
+ private writeGeneration;
266
273
  private catalogGeneration;
267
274
  private switchesGeneration;
268
275
  /** Every settings namespace from the last describe, keyed by ns — the configured-provider join's other input. */
@@ -420,7 +420,14 @@ window.__ModuleLoader__.load({
420
420
  switchesError: null,
421
421
  switches: []
422
422
  });
423
- generation = 0;
423
+ /** Read guard: a newer load() supersedes an older one's publish. */
424
+ readGeneration = 0;
425
+ /**
426
+ * Write guard: save()/resetToDefaults() completions ALWAYS publish unless
427
+ * dispose() invalidated them — an overlapping read must never discard a
428
+ * successful write's accept() (audit F1).
429
+ */
430
+ writeGeneration = 0;
424
431
  catalogGeneration = 0;
425
432
  switchesGeneration = 0;
426
433
  /** Every settings namespace from the last describe, keyed by ns — the configured-provider join's other input. */
@@ -451,14 +458,16 @@ window.__ModuleLoader__.load({
451
458
  * @returns nothing; {@link store} carries success or failure.
452
459
  */
453
460
  async load() {
454
- const generation = ++this.generation;
461
+ const generation = ++this.readGeneration;
462
+ const writeGenerationAtStart = this.writeGeneration;
455
463
  this.store.update((state) => {
456
464
  state.status = "loading";
457
465
  state.error = null;
458
466
  });
459
467
  try {
460
468
  const [describeResult, getResult] = await Promise.all([this.api.settings.describe({}), this.rpc.call("/api", "fallbacks/get", { args: {} }).catch(() => void 0)]);
461
- if (generation !== this.generation) return;
469
+ if (generation !== this.readGeneration) return;
470
+ if (writeGenerationAtStart !== this.writeGeneration) return;
462
471
  if (!describeResult.result.ok) throw describeResult.result.error;
463
472
  this.namespaces = new Map(describeResult.result.value.namespaces.map((entry) => [entry.ns, entry]));
464
473
  const writable = describeResult.result.value.writable;
@@ -473,7 +482,8 @@ window.__ModuleLoader__.load({
473
482
  }
474
483
  this.accept(config, writable, legacyKeys);
475
484
  } catch (error) {
476
- if (generation !== this.generation) return;
485
+ if (generation !== this.readGeneration) return;
486
+ if (writeGenerationAtStart !== this.writeGeneration) return;
477
487
  this.fail(error);
478
488
  }
479
489
  }
@@ -590,14 +600,14 @@ window.__ModuleLoader__.load({
590
600
  async save(next) {
591
601
  const state = this.store.getSnapshot();
592
602
  if (!state.writable || state.status === "saving") return;
593
- const generation = ++this.generation;
603
+ const generation = ++this.writeGeneration;
594
604
  this.store.update((draft) => {
595
605
  draft.status = "saving";
596
606
  draft.error = null;
597
607
  });
598
608
  try {
599
609
  const result = await this.rpc.call("/api", "fallbacks/set", { args: { patch: next } });
600
- if (generation !== this.generation) return;
610
+ if (generation !== this.writeGeneration) return;
601
611
  if (!result.ok) throw result.error;
602
612
  const value = result.value;
603
613
  const config = value !== null && typeof value === "object" && "config" in value ? value.config : void 0;
@@ -608,7 +618,7 @@ window.__ModuleLoader__.load({
608
618
  }
609
619
  this.accept(config, true, legacyKeys);
610
620
  } catch (error) {
611
- if (generation !== this.generation) return;
621
+ if (generation !== this.writeGeneration) return;
612
622
  this.fail(error);
613
623
  }
614
624
  }
@@ -621,14 +631,14 @@ window.__ModuleLoader__.load({
621
631
  async resetToDefaults() {
622
632
  const state = this.store.getSnapshot();
623
633
  if (!state.writable || state.status === "saving") return;
624
- const generation = ++this.generation;
634
+ const generation = ++this.writeGeneration;
625
635
  this.store.update((draft) => {
626
636
  draft.status = "saving";
627
637
  draft.error = null;
628
638
  });
629
639
  try {
630
640
  const result = await this.rpc.call("/api", "fallbacks/reset", { args: {} });
631
- if (generation !== this.generation) return;
641
+ if (generation !== this.writeGeneration) return;
632
642
  if (!result.ok) throw result.error;
633
643
  const value = result.value;
634
644
  const config = value !== null && typeof value === "object" && "config" in value ? value.config : void 0;
@@ -639,13 +649,14 @@ window.__ModuleLoader__.load({
639
649
  }
640
650
  this.accept(config, true, legacyKeys);
641
651
  } catch (error) {
642
- if (generation !== this.generation) return;
652
+ if (generation !== this.writeGeneration) return;
643
653
  this.fail(error);
644
654
  }
645
655
  }
646
656
  /** Stop in-flight responses from publishing after plugin disposal. */
647
657
  dispose() {
648
- this.generation += 1;
658
+ this.readGeneration += 1;
659
+ this.writeGeneration += 1;
649
660
  this.catalogGeneration += 1;
650
661
  this.switchesGeneration += 1;
651
662
  this.namespaces = /* @__PURE__ */ new Map();
@@ -759,6 +770,8 @@ window.__ModuleLoader__.load({
759
770
  "alwaysModeRetryCap.hint": "达到上限次数后切换;0 表示禁用",
760
771
  "alwaysModeRetryCap.tooltip": "retryPolicy 为 always 的模型在同一请求内重试达到该次数后切换;0 表示禁用。",
761
772
  "advanced.label": "高级选项",
773
+ "advanced.expand": "展开高级选项",
774
+ "advanced.collapse": "收起高级选项",
762
775
  "rootChain.label": "root 主代理降级链",
763
776
  "rootChain.hint": "未配置 = root 不降级",
764
777
  "rootChain.tooltip": "root 主代理失败时按此有序选择器列表依次降级;未配置时行为与未安装插件一致。",
@@ -766,8 +779,7 @@ window.__ModuleLoader__.load({
766
779
  "chains.selector.remove": "删除该选择器",
767
780
  "chains.selector.providerPlaceholder": "选择 provider",
768
781
  "chains.selector.modelPlaceholder": "选择 model",
769
- "chains.selector.wildcard": "通配该 provider(provider/*)",
770
- "chains.selector.noModels": "该 provider 暂无可用模型(目录查询失败),请使用通配或改选。",
782
+ "chains.selector.wildcardLegacy": "该条目为通配(provider/*):选择具体模型后将转为精确条目",
771
783
  "chains.selector.noModelsStrict": "该 provider 暂无可用模型(目录查询失败),请改选其他 provider。",
772
784
  "roles.list.label": "角色实体",
773
785
  "roles.list.hint": "先声明角色,规则才能引用",
@@ -888,6 +900,8 @@ window.__ModuleLoader__.load({
888
900
  "alwaysModeRetryCap.hint": "Switches after the cap; 0 disables",
889
901
  "alwaysModeRetryCap.tooltip": "Models whose retryPolicy is always switch after this many retries within one request; 0 disables.",
890
902
  "advanced.label": "Advanced options",
903
+ "advanced.expand": "Show advanced options",
904
+ "advanced.collapse": "Hide advanced options",
891
905
  "rootChain.label": "Root agent fallback chain",
892
906
  "rootChain.hint": "Unset = root does not fall back",
893
907
  "rootChain.tooltip": "When the root agent fails it falls back down this ordered selector list; unset behaves like an uninstalled plugin.",
@@ -895,8 +909,7 @@ window.__ModuleLoader__.load({
895
909
  "chains.selector.remove": "Remove this selector",
896
910
  "chains.selector.providerPlaceholder": "Select provider",
897
911
  "chains.selector.modelPlaceholder": "Select model",
898
- "chains.selector.wildcard": "Wildcard this provider (provider/*)",
899
- "chains.selector.noModels": "No models available for this provider (catalog lookup failed); use the wildcard or pick another provider.",
912
+ "chains.selector.wildcardLegacy": "This entry is a wildcard (provider/*): picking a model converts it to an exact entry",
900
913
  "chains.selector.noModelsStrict": "No models available for this provider (catalog lookup failed); pick another provider.",
901
914
  "roles.list.label": "Declared roles",
902
915
  "roles.list.hint": "Declare roles before rules can reference them",
@@ -1015,7 +1028,7 @@ window.__ModuleLoader__.load({
1015
1028
  }
1016
1029
  //#endregion
1017
1030
  //#region \0dsh-css:/home/runner/work/dsh-llm-fallbacks/dsh-llm-fallbacks/src/client/FallbacksCard.module.css.mjs
1018
- const css$2 = "\n\n\n._8827595f_card {\n list-style: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-3);\n transition: border-color .16s, background .16s;\n}\n\n._8827595f_card:hover {\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n\n._06704203_cardOpen {\n background: var(--dsw-alias-bg-layer-2);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n\n._e488d460_header {\n width: 100%;\n appearance: none;\n border: 0;\n background: none;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n border-radius: 12px;\n}\n\n._e488d460_header:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n\n._f5dfe084_headText {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n._8d39bde6_name {\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n color: var(--dsw-alias-label-primary);\n}\n\n._346f3b69_description {\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n\n._631094a0_chevron {\n flex: none;\n color: var(--dsw-alias-label-tertiary);\n transition: transform .16s;\n}\n\n._44836ce8_chevronOpen {\n transform: rotate(180deg);\n}\n\n\n._dbaa7975_body {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin: 0 16px;\n padding-bottom: 8px;\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\n._99bf7f3c_pending {\n flex: none;\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-module-platform);\n color: var(--dsw-alias-label-secondary);\n}\n\n._114985b2_footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 0 4px;\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\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._4058c747_form {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px 0 0;\n}\n\n\n._9998065c_checkboxRow {\n display: flex;\n align-items: center;\n gap: 8px;\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._94c91bfd_fieldset {\n margin: 0;\n padding: 0;\n border: none;\n display: flex;\n flex-direction: column;\n gap: 12px;\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._13e68c3f_fieldLabel {\n \n display: inline-flex;\n align-items: center;\n gap: 10px;\n padding: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\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._579f813a_defaultNote {\n font-size: 12px;\n line-height: 18px;\n font-weight: 400;\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._a6244318_infoHint {\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\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._929f3ef3_wildcardCell 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._929f3ef3_wildcardCell 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._929f3ef3_wildcardCell input:disabled {\n cursor: default;\n}\n\n\n._f9d86f7b_input {\n box-sizing: border-box;\n width: 100%;\n height: 32px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n}\n\n._f9d86f7b_input:focus {\n outline: none;\n border-color: var(--dsw-alias-brand-primary);\n}\n\n._f9d86f7b_input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n\n._f9d86f7b_input:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n\n._7b60337a_inputInvalid,\n._7b60337a_inputInvalid:focus {\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: 8px;\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._15a12620_chainSelectors {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n._240cbbf8_selectorRow {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n\n._929f3ef3_wildcardCell {\n flex-direction: row;\n align-items: center;\n gap: 8px;\n min-width: 200px;\n align-self: center;\n padding: 6px 8px;\n border-radius: 6px;\n cursor: pointer;\n}\n\n._929f3ef3_wildcardCell:hover:has(input:not(:disabled)) {\n background: var(--dsw-alias-interactive-bg-hover);\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._28202c90_addButton {\n align-self: flex-start;\n}\n\n\n._747fd56d_primaryButton,\n._7e855445_secondaryButton {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 36px;\n padding: 0 14px;\n border: none;\n border-radius: 18px;\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n cursor: pointer;\n}\n\n._747fd56d_primaryButton {\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n._747fd56d_primaryButton:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover);\n}\n\n._7e855445_secondaryButton {\n border: 1px solid var(--dsw-alias-border-l2);\n background: transparent;\n color: var(--dsw-alias-label-primary);\n}\n\n._7e855445_secondaryButton:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-solid);\n}\n\n._747fd56d_primaryButton:disabled,\n._7e855445_secondaryButton:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._747fd56d_primaryButton:focus-visible,\n._7e855445_secondaryButton:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\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._b48dfcba_offNotice {\n margin: 0;\n padding: 12px;\n border: 1px dashed var(--dsw-alias-border-l3);\n border-radius: 8px;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n text-align: center;\n}\n\n\n._ec8bd970_resetDialog {\n width: min(480px, 100%);\n}\n\n._8677165c_confirmDanger:not(:disabled) {\n border-color: var(--dsw-alias-state-error-primary);\n color: var(--dsw-alias-state-error-primary);\n}\n\n._8677165c_confirmDanger:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n}\n\n@media (prefers-reduced-motion: reduce) {\n ._70effa64_iconButton::after {\n transition: none;\n }\n}\n";
1031
+ const css$2 = "\n\n\n._8827595f_card {\n list-style: none;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 12px;\n background: var(--dsw-alias-bg-layer-3);\n transition: border-color .16s, background .16s;\n}\n\n._8827595f_card:hover {\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n\n._06704203_cardOpen {\n background: var(--dsw-alias-bg-layer-2);\n border-color: var(--dsw-alias-label-dimmed);\n}\n\n\n._e488d460_header {\n width: 100%;\n appearance: none;\n border: 0;\n background: none;\n font: inherit;\n color: inherit;\n text-align: left;\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 14px 16px;\n border-radius: 12px;\n}\n\n._e488d460_header:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n\n._f5dfe084_headText {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n._8d39bde6_name {\n font-size: 15px;\n font-weight: 600;\n line-height: 1.4;\n color: var(--dsw-alias-label-primary);\n}\n\n._346f3b69_description {\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-tertiary);\n}\n\n._631094a0_chevron {\n flex: none;\n color: var(--dsw-alias-label-tertiary);\n transition: transform .16s;\n}\n\n._44836ce8_chevronOpen {\n transform: rotate(180deg);\n}\n\n\n._dbaa7975_body {\n border-top: 1px solid var(--dsw-alias-border-l2);\n margin: 0 16px;\n padding-bottom: 8px;\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\n._99bf7f3c_pending {\n flex: none;\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-module-platform);\n color: var(--dsw-alias-label-secondary);\n}\n\n._114985b2_footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 0 4px;\n border-top: 1px solid var(--dsw-alias-border-l2);\n}\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._4058c747_form {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 12px 0 0;\n}\n\n\n._9998065c_checkboxRow {\n display: flex;\n align-items: center;\n gap: 8px;\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._94c91bfd_fieldset {\n margin: 0;\n padding: 0;\n border: none;\n display: flex;\n flex-direction: column;\n gap: 12px;\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._13e68c3f_fieldLabel {\n \n display: inline-flex;\n align-items: center;\n gap: 10px;\n padding: 0;\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n}\n\n\n._b116c758_sectionToggle {\n width: 100%;\n appearance: none;\n border: 0;\n background: none;\n font: inherit;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 0;\n cursor: pointer;\n font-size: 12px;\n line-height: 18px;\n font-weight: 500;\n color: var(--dsw-alias-label-secondary);\n}\n\n._b116c758_sectionToggle:hover:not(:disabled) {\n color: var(--dsw-alias-label-primary);\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: 2px;\n border-radius: 6px;\n}\n\n._a5313fdf_sectionToggleText {\n flex: 1;\n min-width: 0;\n text-align: left;\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._579f813a_defaultNote {\n font-size: 12px;\n line-height: 18px;\n font-weight: 400;\n color: var(--dsw-alias-label-tertiary);\n}\n\n\n._a6244318_infoHint {\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\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._f9d86f7b_input {\n box-sizing: border-box;\n width: 100%;\n height: 32px;\n padding: 0 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 8px;\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n}\n\n._f9d86f7b_input:focus {\n outline: none;\n border-color: var(--dsw-alias-brand-primary);\n}\n\n._f9d86f7b_input::placeholder {\n color: var(--dsw-alias-label-dimmed);\n}\n\n._f9d86f7b_input:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n\n._3a9b42fd_inputTextarea {\n height: auto;\n min-height: 72px;\n padding: 6px 10px;\n resize: vertical;\n line-height: 20px;\n}\n\n\n._7b60337a_inputInvalid,\n._7b60337a_inputInvalid:focus {\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: 8px;\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._15a12620_chainSelectors {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n._240cbbf8_selectorRow {\n display: flex;\n flex-direction: column;\n gap: 6px;\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._28202c90_addButton {\n align-self: flex-start;\n}\n\n\n._747fd56d_primaryButton,\n._7e855445_secondaryButton {\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n height: 36px;\n padding: 0 14px;\n border: none;\n border-radius: 18px;\n font: inherit;\n font-size: 14px;\n line-height: 22px;\n cursor: pointer;\n}\n\n._747fd56d_primaryButton {\n background: var(--dsw-alias-button-primary-fill);\n color: var(--dsw-alias-label-primary-foreground);\n}\n\n._747fd56d_primaryButton:hover:not(:disabled) {\n background: var(--dsw-alias-button-primary-hover);\n}\n\n._7e855445_secondaryButton {\n border: 1px solid var(--dsw-alias-border-l2);\n background: transparent;\n color: var(--dsw-alias-label-primary);\n}\n\n._7e855445_secondaryButton:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-solid);\n}\n\n._747fd56d_primaryButton:disabled,\n._7e855445_secondaryButton:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n._747fd56d_primaryButton:focus-visible,\n._7e855445_secondaryButton:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--dsw-alias-border-l3);\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._b48dfcba_offNotice {\n margin: 0;\n padding: 12px;\n border: 1px dashed var(--dsw-alias-border-l3);\n border-radius: 8px;\n font-size: 12px;\n line-height: 18px;\n color: var(--dsw-alias-label-tertiary);\n text-align: center;\n}\n\n\n._ec8bd970_resetDialog {\n width: min(480px, 100%);\n}\n\n._8677165c_confirmDanger:not(:disabled) {\n border-color: var(--dsw-alias-state-error-primary);\n color: var(--dsw-alias-state-error-primary);\n}\n\n._8677165c_confirmDanger:hover:not(:disabled) {\n background: var(--dsw-alias-interactive-bg-hover-danger);\n}\n\n@media (prefers-reduced-motion: reduce) {\n ._70effa64_iconButton::after {\n transition: none;\n }\n}\n";
1019
1032
  const tagId$2 = "dsh-llm-fallbacks/FallbacksCard.module.css";
1020
1033
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
1021
1034
  const tag = document.createElement("style");
@@ -1050,13 +1063,15 @@ window.__ModuleLoader__.load({
1050
1063
  "fieldset": "_94c91bfd_fieldset",
1051
1064
  "field": "_67826267_field",
1052
1065
  "fieldLabel": "_13e68c3f_fieldLabel",
1066
+ "sectionToggle": "_b116c758_sectionToggle",
1067
+ "sectionToggleText": "_a5313fdf_sectionToggleText",
1053
1068
  "hint": "_4bc809b8_hint",
1054
1069
  "defaultNote": "_579f813a_defaultNote",
1055
1070
  "infoHint": "_a6244318_infoHint",
1056
1071
  "infoHintDisabled": "_e655c840_infoHintDisabled",
1057
1072
  "optionRow": "_a2521bb0_optionRow",
1058
- "wildcardCell": "_929f3ef3_wildcardCell",
1059
1073
  "input": "_f9d86f7b_input",
1074
+ "inputTextarea": "_3a9b42fd_inputTextarea",
1060
1075
  "inputInvalid": "_7b60337a_inputInvalid",
1061
1076
  "selectInput": "_16eb7153_selectInput",
1062
1077
  "numberFields": "_3e95886d_numberFields",
@@ -1288,14 +1303,16 @@ window.__ModuleLoader__.load({
1288
1303
  });
1289
1304
  }
1290
1305
  /**
1291
- * One chain entry selector row: provider select + model select (cascade) +
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
1296
- * providers **configured on the Models page** (`configuredProviders`, the
1297
- * Models-page `configured` join) — unconfigured directory providers never
1298
- * become offerable. Out-of-catalog values read back from the server render as
1306
+ * One chain entry selector row: provider select + model select (cascade).
1307
+ * The GUI never offers a `provider/*` wildcard (root agent and role chains
1308
+ * alike; provider-any matching lives in the role rules) — but `provider/*`
1309
+ * stays a legal YAML entry, so a wildcard row read back from the server
1310
+ * renders with the legacy-conversion hint and an enabled model select:
1311
+ * picking a model converts the row to an exact entry (the patch carries
1312
+ * `wildcard: false`). The provider options are the catalog providers
1313
+ * **configured on the Models page** (`configuredProviders`, the Models-page
1314
+ * `configured` join) — unconfigured directory providers never become
1315
+ * offerable. Out-of-catalog values read back from the server render as
1299
1316
  * a synthetic option with the short "outside catalog" annotation and stay
1300
1317
  * selected — keeping them saves verbatim; picking a catalog option is an
1301
1318
  * intentional change. A directory provider that is not configured is offered
@@ -1303,107 +1320,93 @@ window.__ModuleLoader__.load({
1303
1320
  * existing value is never hidden or dropped. New rows only offer configured
1304
1321
  * options.
1305
1322
  */
1306
- function ChainSelectorEditor({ selector, catalog, configuredProviders, disabled, t, onChange, onRemove, allowWildcard = true }) {
1323
+ function ChainSelectorEditor({ selector, catalog, configuredProviders, disabled, t, onChange, onRemove }) {
1307
1324
  const providerRaw = selectionToRaw(selector.provider);
1308
1325
  const providerOutside = selector.provider?.kind === "outside";
1309
1326
  const providerUnconfigured = !providerOutside && providerRaw !== "" && (catalog?.providers.some((entry) => entry.provider === providerRaw) ?? false) && !configuredProviders.some((entry) => entry.provider === providerRaw);
1310
1327
  const modelRaw = selectionToRaw(selector.model);
1311
1328
  const modelOutside = selector.model?.kind === "outside";
1312
1329
  const group = catalog?.groups.find((entry) => entry.id === providerRaw);
1313
- const groupMissing = providerRaw !== "" && !providerOutside && !selector.wildcard && group === void 0;
1314
- const modelDisabled = disabled || selector.wildcard || providerRaw === "" || groupMissing || providerOutside && modelRaw === "";
1330
+ const groupMissing = providerRaw !== "" && !providerOutside && group === void 0;
1331
+ const modelDisabled = disabled || providerRaw === "" || groupMissing || providerOutside && modelRaw === "";
1315
1332
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1316
1333
  className: FallbacksCard_module_css_default.selectorRow,
1317
1334
  children: [
1318
1335
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1319
1336
  className: FallbacksCard_module_css_default.ruleGrid,
1320
- children: [
1321
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1322
- className: FallbacksCard_module_css_default.ruleCell,
1323
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1337
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1338
+ className: FallbacksCard_module_css_default.ruleCell,
1339
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1340
+ className: FallbacksCard_module_css_default.ruleCellLabel,
1341
+ children: t("roles.rule.provider")
1342
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1343
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
1344
+ value: providerRaw,
1345
+ disabled,
1346
+ onChange: (event) => {
1347
+ if (event.target.value === providerRaw) return;
1348
+ onChange({
1349
+ provider: classifyProvider(event.target.value, catalog),
1350
+ model: null
1351
+ });
1352
+ },
1353
+ children: [
1354
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1355
+ value: "",
1356
+ children: t("chains.selector.providerPlaceholder")
1357
+ }),
1358
+ configuredProviders.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1359
+ value: entry.provider,
1360
+ children: entry.displayName
1361
+ }, entry.provider)),
1362
+ providerUnconfigured && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1363
+ value: providerRaw,
1364
+ children: `${providerRaw}${t("catalog.unconfigured.short")}`
1365
+ }),
1366
+ providerOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1367
+ value: providerRaw,
1368
+ children: `${providerRaw}${t("catalog.outside.short")}`
1369
+ })
1370
+ ]
1371
+ })]
1372
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1373
+ className: FallbacksCard_module_css_default.ruleCell,
1374
+ children: [
1375
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1324
1376
  className: FallbacksCard_module_css_default.ruleCellLabel,
1325
- children: t("roles.rule.provider")
1326
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1377
+ children: t("roles.rule.model")
1378
+ }),
1379
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1327
1380
  className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
1328
- value: providerRaw,
1329
- disabled,
1381
+ value: modelRaw,
1382
+ disabled: modelDisabled,
1330
1383
  onChange: (event) => {
1331
- if (event.target.value === providerRaw) return;
1332
1384
  onChange({
1333
- provider: classifyProvider(event.target.value, catalog),
1334
- model: null
1385
+ model: classifyModel(providerRaw, event.target.value, catalog),
1386
+ ...selector.wildcard ? { wildcard: false } : {}
1335
1387
  });
1336
1388
  },
1337
1389
  children: [
1338
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1390
+ modelRaw === "" && !providerOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1339
1391
  value: "",
1340
- children: t("chains.selector.providerPlaceholder")
1392
+ children: t("chains.selector.modelPlaceholder")
1341
1393
  }),
1342
- configuredProviders.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1343
- value: entry.provider,
1344
- children: entry.displayName
1345
- }, entry.provider)),
1346
- providerUnconfigured && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1347
- value: providerRaw,
1348
- children: `${providerRaw}${t("catalog.unconfigured.short")}`
1349
- }),
1350
- providerOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1351
- value: providerRaw,
1352
- children: `${providerRaw}${t("catalog.outside.short")}`
1394
+ (group?.models ?? []).map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1395
+ value: model.id,
1396
+ children: model.name
1397
+ }, model.id)),
1398
+ modelOutside && !selector.wildcard && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1399
+ value: modelRaw,
1400
+ children: `${modelRaw}${t("catalog.outside.short")}`
1353
1401
  })
1354
1402
  ]
1355
- })]
1356
- }),
1357
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1358
- className: FallbacksCard_module_css_default.ruleCell,
1359
- children: [
1360
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1361
- className: FallbacksCard_module_css_default.ruleCellLabel,
1362
- children: t("roles.rule.model")
1363
- }),
1364
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1365
- className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
1366
- value: selector.wildcard ? "" : modelRaw,
1367
- disabled: modelDisabled,
1368
- onChange: (event) => {
1369
- onChange({ model: classifyModel(providerRaw, event.target.value, catalog) });
1370
- },
1371
- children: [
1372
- modelRaw === "" && !providerOutside && !selector.wildcard && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1373
- value: "",
1374
- children: t("chains.selector.modelPlaceholder")
1375
- }),
1376
- (group?.models ?? []).map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1377
- value: model.id,
1378
- children: model.name
1379
- }, model.id)),
1380
- modelOutside && !selector.wildcard && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1381
- value: modelRaw,
1382
- children: `${modelRaw}${t("catalog.outside.short")}`
1383
- })
1384
- ]
1385
- }),
1386
- groupMissing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1387
- className: FallbacksCard_module_css_default.hint,
1388
- children: t(allowWildcard ? "chains.selector.noModels" : "chains.selector.noModelsStrict")
1389
- })
1390
- ]
1391
- }),
1392
- allowWildcard && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1393
- className: `${FallbacksCard_module_css_default.ruleCell} ${FallbacksCard_module_css_default.wildcardCell}`,
1394
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1395
- type: "checkbox",
1396
- checked: selector.wildcard,
1397
- disabled: disabled || providerRaw === "",
1398
- onChange: (event) => {
1399
- onChange({
1400
- wildcard: event.target.checked,
1401
- ...event.target.checked ? { model: null } : {}
1402
- });
1403
- }
1404
- }), t("chains.selector.wildcard")]
1405
- })
1406
- ]
1403
+ }),
1404
+ groupMissing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1405
+ className: FallbacksCard_module_css_default.hint,
1406
+ children: t("chains.selector.noModelsStrict")
1407
+ })
1408
+ ]
1409
+ })]
1407
1410
  }),
1408
1411
  (providerOutside || modelOutside) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1409
1412
  className: FallbacksCard_module_css_default.hint,
@@ -1412,6 +1415,10 @@ window.__ModuleLoader__.load({
1412
1415
  disabled
1413
1416
  })]
1414
1417
  }),
1418
+ selector.wildcard && !modelDisabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1419
+ className: FallbacksCard_module_css_default.hint,
1420
+ children: t("chains.selector.wildcardLegacy")
1421
+ }),
1415
1422
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1416
1423
  className: FallbacksCard_module_css_default.cardFoot,
1417
1424
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -1637,6 +1644,7 @@ window.__ModuleLoader__.load({
1637
1644
  });
1638
1645
  };
1639
1646
  const [userOpen, setUserOpen] = (0, react.useState)(false);
1647
+ const [advancedOpen, setAdvancedOpen] = (0, react.useState)(false);
1640
1648
  const degradedLatch = (0, react.useRef)(false);
1641
1649
  const errorLatch = (0, react.useRef)(false);
1642
1650
  if (state.status === "ready") {
@@ -1645,6 +1653,7 @@ window.__ModuleLoader__.load({
1645
1653
  } else if (state.status === "error") errorLatch.current = true;
1646
1654
  const degraded = state.status === "ready" ? !state.present : degradedLatch.current;
1647
1655
  const open = userOpen || errorLatch.current || degraded;
1656
+ const advancedVisible = advancedOpen || !writable;
1648
1657
  const title = t("title");
1649
1658
  const header = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1650
1659
  type: "button",
@@ -1798,7 +1807,6 @@ window.__ModuleLoader__.load({
1798
1807
  configuredProviders: state.configuredProviders,
1799
1808
  disabled: !writable,
1800
1809
  t,
1801
- allowWildcard: false,
1802
1810
  onChange: (patch) => {
1803
1811
  updateRootChainSelector(selectorIndex, patch);
1804
1812
  },
@@ -1878,8 +1886,9 @@ window.__ModuleLoader__.load({
1878
1886
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1879
1887
  className: FallbacksCard_module_css_default.ruleCellLabel,
1880
1888
  children: t("roles.persona")
1881
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1882
- className: FallbacksCard_module_css_default.input,
1889
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
1890
+ rows: 3,
1891
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.inputTextarea}`,
1883
1892
  value: row.persona,
1884
1893
  placeholder: t("roles.personaPlaceholder"),
1885
1894
  "aria-label": t("roles.persona"),
@@ -2186,220 +2195,231 @@ window.__ModuleLoader__.load({
2186
2195
  className: FallbacksCard_module_css_default.field,
2187
2196
  role: "group",
2188
2197
  "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,
2198
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2199
+ type: "button",
2200
+ className: FallbacksCard_module_css_default.sectionToggle,
2201
+ disabled: !writable,
2202
+ "aria-expanded": advancedVisible,
2203
+ "aria-controls": advancedVisible ? "fallbacks-advanced-body" : void 0,
2204
+ "aria-label": t(advancedVisible ? "advanced.collapse" : "advanced.expand"),
2205
+ onClick: () => {
2206
+ if (writable) setAdvancedOpen(!advancedOpen);
2207
+ },
2208
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2209
+ id: "fallbacks-advanced",
2210
+ className: FallbacksCard_module_css_default.sectionToggleText,
2211
+ children: t("advanced.label")
2212
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: advancedVisible ? `${FallbacksCard_module_css_default.chevron} ${FallbacksCard_module_css_default.chevronOpen}` : FallbacksCard_module_css_default.chevron })]
2213
+ }), advancedVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2214
+ id: "fallbacks-advanced-body",
2215
+ children: [
2216
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2217
+ className: FallbacksCard_module_css_default.field,
2218
+ role: "group",
2219
+ "aria-labelledby": "fallbacks-trigger-codes",
2220
+ children: [
2221
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2222
+ className: FallbacksCard_module_css_default.fieldLabel,
2223
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2224
+ id: "fallbacks-trigger-codes",
2225
+ children: t("triggerCodes.label")
2226
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2227
+ label: t("triggerCodes.tooltip"),
2228
+ disabled: !writable
2229
+ })]
2230
+ }),
2231
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2232
+ className: FallbacksCard_module_css_default.hint,
2233
+ children: t("triggerCodes.hint")
2234
+ }),
2235
+ KNOWN_TRIGGER_CODES.map((code) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2236
+ className: FallbacksCard_module_css_default.optionRow,
2237
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2238
+ type: "checkbox",
2239
+ checked: scalars.triggerCodes.includes(code),
2302
2240
  onChange: (event) => {
2303
2241
  updateScalars((draft) => {
2304
- draft.cooldownMs = parseCount(event.target.value);
2242
+ draft.triggerCodes = withTriggerCode(draft.triggerCodes, code, event.target.checked);
2305
2243
  });
2306
2244
  }
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) => {
2245
+ }), t(TRIGGER_CODE_LABELS[code])]
2246
+ }, code)),
2247
+ unknownCodes.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2248
+ className: FallbacksCard_module_css_default.hint,
2249
+ children: t("triggerCodes.extra", { codes: unknownCodes.join(", ") })
2250
+ })
2251
+ ]
2252
+ }),
2253
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2254
+ className: FallbacksCard_module_css_default.field,
2255
+ role: "group",
2256
+ "aria-labelledby": "fallbacks-revert-policy",
2257
+ children: [
2258
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2259
+ className: FallbacksCard_module_css_default.fieldLabel,
2260
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2261
+ id: "fallbacks-revert-policy",
2262
+ children: t("revertPolicy.label")
2263
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2264
+ label: t("revertPolicy.tooltip"),
2265
+ disabled: !writable
2266
+ })]
2267
+ }),
2268
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2269
+ className: FallbacksCard_module_css_default.hint,
2270
+ children: t("revertPolicy.hint")
2271
+ }),
2272
+ ["cooldown-expiry", "never"].map((policy) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2273
+ className: FallbacksCard_module_css_default.optionRow,
2274
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2275
+ type: "radio",
2276
+ name: "fallbacks-revert-policy",
2277
+ checked: scalars.revertPolicy === policy,
2278
+ onChange: () => {
2346
2279
  updateScalars((draft) => {
2347
- draft.maxSwitchesPerStep = parseCount(event.target.value);
2280
+ draft.revertPolicy = policy;
2348
2281
  });
2349
2282
  }
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
- ]
2283
+ }), t(`revertPolicy.${policy}`)]
2284
+ }, policy))
2285
+ ]
2286
+ }),
2287
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2288
+ className: FallbacksCard_module_css_default.numberFields,
2289
+ children: [
2290
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2291
+ className: FallbacksCard_module_css_default.field,
2292
+ children: [
2293
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2294
+ className: FallbacksCard_module_css_default.fieldLabel,
2295
+ children: [
2296
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2297
+ htmlFor: "fallbacks-cooldown-ms",
2298
+ children: t("cooldownMs.label")
2299
+ }),
2300
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2301
+ label: t("cooldownMs.tooltip"),
2302
+ disabled: !writable
2303
+ }),
2304
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2305
+ className: FallbacksCard_module_css_default.defaultNote,
2306
+ children: [
2307
+ t("defaults.prefix"),
2308
+ ": ",
2309
+ state.config.cooldownMs
2310
+ ]
2311
+ })
2312
+ ]
2313
+ }),
2314
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2315
+ id: "fallbacks-cooldown-ms",
2316
+ className: FallbacksCard_module_css_default.input,
2317
+ type: "number",
2318
+ min: 0,
2319
+ value: String(scalars.cooldownMs),
2320
+ disabled: !writable,
2321
+ onChange: (event) => {
2322
+ updateScalars((draft) => {
2323
+ draft.cooldownMs = parseCount(event.target.value);
2324
+ });
2325
+ }
2326
+ }),
2327
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2328
+ className: FallbacksCard_module_css_default.hint,
2329
+ children: t("cooldownMs.hint")
2330
+ })
2331
+ ]
2332
+ }),
2333
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2334
+ className: FallbacksCard_module_css_default.field,
2335
+ children: [
2336
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2337
+ className: FallbacksCard_module_css_default.fieldLabel,
2338
+ children: [
2339
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2340
+ htmlFor: "fallbacks-max-switches",
2341
+ children: t("maxSwitchesPerStep.label")
2342
+ }),
2343
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2344
+ label: t("maxSwitchesPerStep.tooltip"),
2345
+ disabled: !writable
2346
+ }),
2347
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2348
+ className: FallbacksCard_module_css_default.defaultNote,
2349
+ children: [
2350
+ t("defaults.prefix"),
2351
+ ": ",
2352
+ state.config.maxSwitchesPerStep
2353
+ ]
2354
+ })
2355
+ ]
2356
+ }),
2357
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2358
+ id: "fallbacks-max-switches",
2359
+ className: FallbacksCard_module_css_default.input,
2360
+ type: "number",
2361
+ min: 0,
2362
+ value: String(scalars.maxSwitchesPerStep),
2363
+ disabled: !writable,
2364
+ onChange: (event) => {
2365
+ updateScalars((draft) => {
2366
+ draft.maxSwitchesPerStep = parseCount(event.target.value);
2367
+ });
2368
+ }
2369
+ }),
2370
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2371
+ className: FallbacksCard_module_css_default.hint,
2372
+ children: t("maxSwitchesPerStep.hint")
2373
+ })
2374
+ ]
2375
+ }),
2376
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2377
+ className: FallbacksCard_module_css_default.field,
2378
+ children: [
2379
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2380
+ className: FallbacksCard_module_css_default.fieldLabel,
2381
+ children: [
2382
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2383
+ htmlFor: "fallbacks-always-cap",
2384
+ children: t("alwaysModeRetryCap.label")
2385
+ }),
2386
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2387
+ label: t("alwaysModeRetryCap.tooltip"),
2388
+ disabled: !writable
2389
+ }),
2390
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2391
+ className: FallbacksCard_module_css_default.defaultNote,
2392
+ children: [
2393
+ t("defaults.prefix"),
2394
+ ": ",
2395
+ state.config.alwaysModeRetryCap
2396
+ ]
2397
+ })
2398
+ ]
2399
+ }),
2400
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2401
+ id: "fallbacks-always-cap",
2402
+ className: FallbacksCard_module_css_default.input,
2403
+ type: "number",
2404
+ min: 0,
2405
+ value: String(scalars.alwaysModeRetryCap),
2406
+ disabled: !writable,
2407
+ onChange: (event) => {
2408
+ updateScalars((draft) => {
2409
+ draft.alwaysModeRetryCap = parseCount(event.target.value);
2410
+ });
2411
+ }
2412
+ }),
2413
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2414
+ className: FallbacksCard_module_css_default.hint,
2415
+ children: t("alwaysModeRetryCap.hint")
2416
+ })
2417
+ ]
2418
+ })
2419
+ ]
2420
+ })
2421
+ ]
2422
+ })]
2403
2423
  })
2404
2424
  ]
2405
2425
  })
@@ -35,6 +35,8 @@ export declare const zh: {
35
35
  'alwaysModeRetryCap.hint': string;
36
36
  'alwaysModeRetryCap.tooltip': string;
37
37
  'advanced.label': string;
38
+ 'advanced.expand': string;
39
+ 'advanced.collapse': string;
38
40
  'rootChain.label': string;
39
41
  'rootChain.hint': string;
40
42
  'rootChain.tooltip': string;
@@ -42,8 +44,7 @@ export declare const zh: {
42
44
  'chains.selector.remove': string;
43
45
  'chains.selector.providerPlaceholder': string;
44
46
  'chains.selector.modelPlaceholder': string;
45
- 'chains.selector.wildcard': string;
46
- 'chains.selector.noModels': string;
47
+ 'chains.selector.wildcardLegacy': string;
47
48
  'chains.selector.noModelsStrict': string;
48
49
  'roles.list.label': string;
49
50
  'roles.list.hint': string;
@@ -166,6 +167,8 @@ export declare const en: {
166
167
  'alwaysModeRetryCap.hint': string;
167
168
  'alwaysModeRetryCap.tooltip': string;
168
169
  'advanced.label': string;
170
+ 'advanced.expand': string;
171
+ 'advanced.collapse': string;
169
172
  'rootChain.label': string;
170
173
  'rootChain.hint': string;
171
174
  'rootChain.tooltip': string;
@@ -173,8 +176,7 @@ export declare const en: {
173
176
  'chains.selector.remove': string;
174
177
  'chains.selector.providerPlaceholder': string;
175
178
  'chains.selector.modelPlaceholder': string;
176
- 'chains.selector.wildcard': string;
177
- 'chains.selector.noModels': string;
179
+ 'chains.selector.wildcardLegacy': string;
178
180
  'chains.selector.noModelsStrict': string;
179
181
  'roles.list.label': string;
180
182
  'roles.list.hint': string;
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "dsh",
6
6
  "dsh-plugin"
7
7
  ],
8
- "version": "0.1.3",
8
+ "version": "0.1.4",
9
9
  "type": "module",
10
10
  "main": "./dist/index.js",
11
11
  "types": "./dist/index.d.ts",
@@ -49,6 +49,7 @@
49
49
  "release:prepare": "tsx scripts/prepare-release.ts",
50
50
  "release:validate": "tsx scripts/validate-release-version.ts",
51
51
  "test": "vitest run",
52
+ "typecheck": "tsc -p tsconfig.build.json --noEmit && tsc --noEmit",
52
53
  "prepare": "pnpm run build"
53
54
  },
54
55
  "engines": {