dsh-llm-fallbacks 0.1.3 → 0.1.5

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.
@@ -100,6 +100,20 @@ window.__ModuleLoader__.load({
100
100
  return current;
101
101
  }
102
102
  /**
103
+ * Shape-guard the wire `seeds` badge field (spec §9.4): only `{ id,
104
+ * overridden }` entries survive — the `legacyKeys` element-filter
105
+ * precedent. A non-array value resolves to `[]`; malformed entries are
106
+ * dropped, so an all-bad array also lands `[]`. The store never trusts a
107
+ * misshapen badge field.
108
+ */
109
+ function parseSeedsWire(value) {
110
+ if (!Array.isArray(value)) return [];
111
+ return value.filter((entry) => {
112
+ if (!isRecord(entry)) return false;
113
+ return typeof entry.id === "string" && typeof entry.overridden === "boolean";
114
+ });
115
+ }
116
+ /**
103
117
  * The provider dropdown's offer set (spec §2.5 D-4): catalog providers whose
104
118
  * settings profile resolves in the describe namespaces — the Models page's
105
119
  * `configured` predicate (`ui-models` store.ts): a provider is configured
@@ -410,6 +424,7 @@ window.__ModuleLoader__.load({
410
424
  config: defaultFallbacksConfig,
411
425
  present: false,
412
426
  legacyKeys: [],
427
+ seeds: [],
413
428
  catalogStatus: "idle",
414
429
  catalogError: null,
415
430
  providers: [],
@@ -420,7 +435,14 @@ window.__ModuleLoader__.load({
420
435
  switchesError: null,
421
436
  switches: []
422
437
  });
423
- generation = 0;
438
+ /** Read guard: a newer load() supersedes an older one's publish. */
439
+ readGeneration = 0;
440
+ /**
441
+ * Write guard: save()/resetToDefaults() completions ALWAYS publish unless
442
+ * dispose() invalidated them — an overlapping read must never discard a
443
+ * successful write's accept() (audit F1).
444
+ */
445
+ writeGeneration = 0;
424
446
  catalogGeneration = 0;
425
447
  switchesGeneration = 0;
426
448
  /** Every settings namespace from the last describe, keyed by ns — the configured-provider join's other input. */
@@ -451,29 +473,34 @@ window.__ModuleLoader__.load({
451
473
  * @returns nothing; {@link store} carries success or failure.
452
474
  */
453
475
  async load() {
454
- const generation = ++this.generation;
476
+ const generation = ++this.readGeneration;
477
+ const writeGenerationAtStart = this.writeGeneration;
455
478
  this.store.update((state) => {
456
479
  state.status = "loading";
457
480
  state.error = null;
458
481
  });
459
482
  try {
460
483
  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;
484
+ if (generation !== this.readGeneration) return;
485
+ if (writeGenerationAtStart !== this.writeGeneration) return;
462
486
  if (!describeResult.result.ok) throw describeResult.result.error;
463
487
  this.namespaces = new Map(describeResult.result.value.namespaces.map((entry) => [entry.ns, entry]));
464
488
  const writable = describeResult.result.value.writable;
465
489
  let config;
466
490
  let legacyKeys = [];
491
+ let seeds = [];
467
492
  if (getResult !== void 0 && getResult.ok && getResult.value !== null && typeof getResult.value === "object") {
468
493
  if ("config" in getResult.value) config = getResult.value.config;
469
494
  if ("legacyKeys" in getResult.value) {
470
495
  const wireLegacyKeys = getResult.value.legacyKeys;
471
496
  if (Array.isArray(wireLegacyKeys)) legacyKeys = wireLegacyKeys.filter((key) => typeof key === "string");
472
497
  }
498
+ if ("seeds" in getResult.value) seeds = parseSeedsWire(getResult.value.seeds);
473
499
  }
474
- this.accept(config, writable, legacyKeys);
500
+ this.accept(config, writable, legacyKeys, seeds);
475
501
  } catch (error) {
476
- if (generation !== this.generation) return;
502
+ if (generation !== this.readGeneration) return;
503
+ if (writeGenerationAtStart !== this.writeGeneration) return;
477
504
  this.fail(error);
478
505
  }
479
506
  }
@@ -590,14 +617,14 @@ window.__ModuleLoader__.load({
590
617
  async save(next) {
591
618
  const state = this.store.getSnapshot();
592
619
  if (!state.writable || state.status === "saving") return;
593
- const generation = ++this.generation;
620
+ const generation = ++this.writeGeneration;
594
621
  this.store.update((draft) => {
595
622
  draft.status = "saving";
596
623
  draft.error = null;
597
624
  });
598
625
  try {
599
626
  const result = await this.rpc.call("/api", "fallbacks/set", { args: { patch: next } });
600
- if (generation !== this.generation) return;
627
+ if (generation !== this.writeGeneration) return;
601
628
  if (!result.ok) throw result.error;
602
629
  const value = result.value;
603
630
  const config = value !== null && typeof value === "object" && "config" in value ? value.config : void 0;
@@ -606,9 +633,14 @@ window.__ModuleLoader__.load({
606
633
  const wireLegacyKeys = value.legacyKeys;
607
634
  if (Array.isArray(wireLegacyKeys)) legacyKeys = wireLegacyKeys.filter((key) => typeof key === "string");
608
635
  }
609
- this.accept(config, true, legacyKeys);
636
+ let seeds = this.store.getSnapshot().seeds;
637
+ if (value !== null && typeof value === "object" && "seeds" in value) {
638
+ const wireSeeds = value.seeds;
639
+ if (Array.isArray(wireSeeds)) seeds = parseSeedsWire(wireSeeds);
640
+ }
641
+ this.accept(config, true, legacyKeys, seeds);
610
642
  } catch (error) {
611
- if (generation !== this.generation) return;
643
+ if (generation !== this.writeGeneration) return;
612
644
  this.fail(error);
613
645
  }
614
646
  }
@@ -621,14 +653,57 @@ window.__ModuleLoader__.load({
621
653
  async resetToDefaults() {
622
654
  const state = this.store.getSnapshot();
623
655
  if (!state.writable || state.status === "saving") return;
624
- const generation = ++this.generation;
656
+ const generation = ++this.writeGeneration;
625
657
  this.store.update((draft) => {
626
658
  draft.status = "saving";
627
659
  draft.error = null;
628
660
  });
629
661
  try {
630
662
  const result = await this.rpc.call("/api", "fallbacks/reset", { args: {} });
631
- if (generation !== this.generation) return;
663
+ if (generation !== this.writeGeneration) return;
664
+ if (!result.ok) throw result.error;
665
+ const value = result.value;
666
+ const config = value !== null && typeof value === "object" && "config" in value ? value.config : void 0;
667
+ let legacyKeys = this.store.getSnapshot().legacyKeys;
668
+ if (value !== null && typeof value === "object" && "legacyKeys" in value) {
669
+ const wireLegacyKeys = value.legacyKeys;
670
+ if (Array.isArray(wireLegacyKeys)) legacyKeys = wireLegacyKeys.filter((key) => typeof key === "string");
671
+ }
672
+ let seeds = this.store.getSnapshot().seeds;
673
+ if (value !== null && typeof value === "object" && "seeds" in value) {
674
+ const wireSeeds = value.seeds;
675
+ if (Array.isArray(wireSeeds)) seeds = parseSeedsWire(wireSeeds);
676
+ }
677
+ this.accept(config, true, legacyKeys, seeds);
678
+ } catch (error) {
679
+ if (generation !== this.writeGeneration) return;
680
+ this.fail(error);
681
+ }
682
+ }
683
+ /**
684
+ * Revert one seeded role to its CURRENT declared seed default (spec §9.4,
685
+ * AC-3) through the gateway channel (`/api/fallbacks/revert-seed`). Same
686
+ * write guards as {@link save} — writable / saving / write-generation —
687
+ * and the same KD-G3 error handling: any business rejection or transport
688
+ * failure surfaces its message in `state.error` for the error banner and
689
+ * the form stays editable for retry. A business `{ reverted: false,
690
+ * reason }` outcome is still a successful RPC — the post-write read
691
+ * result (config / legacyKeys / seeds) lands either way, and the revert
692
+ * button stays disabled while the write is in flight.
693
+ * @param id - the seeded role id; the host matches it by trimmed id
694
+ * against the seed registry (spec §9.3).
695
+ */
696
+ async revertSeed(id) {
697
+ const state = this.store.getSnapshot();
698
+ if (!state.writable || state.status === "saving") return;
699
+ const generation = ++this.writeGeneration;
700
+ this.store.update((draft) => {
701
+ draft.status = "saving";
702
+ draft.error = null;
703
+ });
704
+ try {
705
+ const result = await this.rpc.call("/api", "fallbacks/revert-seed", { args: { id } });
706
+ if (generation !== this.writeGeneration) return;
632
707
  if (!result.ok) throw result.error;
633
708
  const value = result.value;
634
709
  const config = value !== null && typeof value === "object" && "config" in value ? value.config : void 0;
@@ -637,15 +712,21 @@ window.__ModuleLoader__.load({
637
712
  const wireLegacyKeys = value.legacyKeys;
638
713
  if (Array.isArray(wireLegacyKeys)) legacyKeys = wireLegacyKeys.filter((key) => typeof key === "string");
639
714
  }
640
- this.accept(config, true, legacyKeys);
715
+ let seeds = this.store.getSnapshot().seeds;
716
+ if (value !== null && typeof value === "object" && "seeds" in value) {
717
+ const wireSeeds = value.seeds;
718
+ if (Array.isArray(wireSeeds)) seeds = parseSeedsWire(wireSeeds);
719
+ }
720
+ this.accept(config, true, legacyKeys, seeds);
641
721
  } catch (error) {
642
- if (generation !== this.generation) return;
722
+ if (generation !== this.writeGeneration) return;
643
723
  this.fail(error);
644
724
  }
645
725
  }
646
726
  /** Stop in-flight responses from publishing after plugin disposal. */
647
727
  dispose() {
648
- this.generation += 1;
728
+ this.readGeneration += 1;
729
+ this.writeGeneration += 1;
649
730
  this.catalogGeneration += 1;
650
731
  this.switchesGeneration += 1;
651
732
  this.namespaces = /* @__PURE__ */ new Map();
@@ -661,9 +742,12 @@ window.__ModuleLoader__.load({
661
742
  * same publish: the wire field drives the migration banner. save/reset
662
743
  * pass the POST-WRITE value (W-1/F-1) — or the previous value when the
663
744
  * response omits the field, so a write can never clear the banner
664
- * against server truth; only a real `get` may.
745
+ * against server truth; only a real `get` may. `seeds` (spec §9.4)
746
+ * follows the same honest rule: the wire badge field is authoritative
747
+ * only when a real config resolved — a transient channel-down keeps the
748
+ * last accepted badge state.
665
749
  */
666
- accept(config, writable, legacyKeys) {
750
+ accept(config, writable, legacyKeys, seeds) {
667
751
  const parsed = config === void 0 ? void 0 : parseFallbacksConfig(config);
668
752
  this.store.update((state) => {
669
753
  state.status = "ready";
@@ -671,6 +755,7 @@ window.__ModuleLoader__.load({
671
755
  state.writable = writable;
672
756
  state.present = parsed !== void 0;
673
757
  state.legacyKeys = parsed === void 0 ? state.legacyKeys : legacyKeys;
758
+ state.seeds = parsed === void 0 ? state.seeds : seeds;
674
759
  if (parsed !== void 0) state.config = parsed;
675
760
  state.configuredProviders = configuredProvidersOf(state.providers, this.namespaces);
676
761
  });
@@ -759,6 +844,8 @@ window.__ModuleLoader__.load({
759
844
  "alwaysModeRetryCap.hint": "达到上限次数后切换;0 表示禁用",
760
845
  "alwaysModeRetryCap.tooltip": "retryPolicy 为 always 的模型在同一请求内重试达到该次数后切换;0 表示禁用。",
761
846
  "advanced.label": "高级选项",
847
+ "advanced.expand": "展开高级选项",
848
+ "advanced.collapse": "收起高级选项",
762
849
  "rootChain.label": "root 主代理降级链",
763
850
  "rootChain.hint": "未配置 = root 不降级",
764
851
  "rootChain.tooltip": "root 主代理失败时按此有序选择器列表依次降级;未配置时行为与未安装插件一致。",
@@ -766,8 +853,7 @@ window.__ModuleLoader__.load({
766
853
  "chains.selector.remove": "删除该选择器",
767
854
  "chains.selector.providerPlaceholder": "选择 provider",
768
855
  "chains.selector.modelPlaceholder": "选择 model",
769
- "chains.selector.wildcard": "通配该 provider(provider/*)",
770
- "chains.selector.noModels": "该 provider 暂无可用模型(目录查询失败),请使用通配或改选。",
856
+ "chains.selector.wildcardLegacy": "该条目为通配(provider/*):选择具体模型后将转为精确条目",
771
857
  "chains.selector.noModelsStrict": "该 provider 暂无可用模型(目录查询失败),请改选其他 provider。",
772
858
  "roles.list.label": "角色实体",
773
859
  "roles.list.hint": "先声明角色,规则才能引用",
@@ -777,6 +863,10 @@ window.__ModuleLoader__.load({
777
863
  "roles.idPlaceholder": "例如 reviewer",
778
864
  "roles.persona": "人格提示",
779
865
  "roles.personaPlaceholder": "例如:你是资深代码审查员",
866
+ "roles.seedDefault": "seed 默认",
867
+ "roles.seedOverride": "seed 覆盖",
868
+ "roles.revertPersona": "还原 Seed 默认",
869
+ "roles.seedChainOptional": "角色 \"{id}\" 为 seed 角色:链可留空,保存不会被拦截",
780
870
  "roles.fallback": "链拼接策略",
781
871
  "roles.fallback.inherit-root": "继承 root(角色链后追加 rootChain)",
782
872
  "roles.fallback.none": "仅角色链(不追加 rootChain)",
@@ -888,6 +978,8 @@ window.__ModuleLoader__.load({
888
978
  "alwaysModeRetryCap.hint": "Switches after the cap; 0 disables",
889
979
  "alwaysModeRetryCap.tooltip": "Models whose retryPolicy is always switch after this many retries within one request; 0 disables.",
890
980
  "advanced.label": "Advanced options",
981
+ "advanced.expand": "Show advanced options",
982
+ "advanced.collapse": "Hide advanced options",
891
983
  "rootChain.label": "Root agent fallback chain",
892
984
  "rootChain.hint": "Unset = root does not fall back",
893
985
  "rootChain.tooltip": "When the root agent fails it falls back down this ordered selector list; unset behaves like an uninstalled plugin.",
@@ -895,8 +987,7 @@ window.__ModuleLoader__.load({
895
987
  "chains.selector.remove": "Remove this selector",
896
988
  "chains.selector.providerPlaceholder": "Select provider",
897
989
  "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.",
990
+ "chains.selector.wildcardLegacy": "This entry is a wildcard (provider/*): picking a model converts it to an exact entry",
900
991
  "chains.selector.noModelsStrict": "No models available for this provider (catalog lookup failed); pick another provider.",
901
992
  "roles.list.label": "Declared roles",
902
993
  "roles.list.hint": "Declare roles before rules can reference them",
@@ -906,6 +997,10 @@ window.__ModuleLoader__.load({
906
997
  "roles.idPlaceholder": "e.g. reviewer",
907
998
  "roles.persona": "Persona",
908
999
  "roles.personaPlaceholder": "e.g. you are a senior code reviewer",
1000
+ "roles.seedDefault": "Seed default",
1001
+ "roles.seedOverride": "Seed override",
1002
+ "roles.revertPersona": "Revert to seed default",
1003
+ "roles.seedChainOptional": "Role \"{id}\" is a seed role: the chain may stay empty",
909
1004
  "roles.fallback": "Chain append",
910
1005
  "roles.fallback.inherit-root": "Inherit root (append rootChain after the role chain)",
911
1006
  "roles.fallback.none": "Role chain only (no rootChain)",
@@ -1015,7 +1110,7 @@ window.__ModuleLoader__.load({
1015
1110
  }
1016
1111
  //#endregion
1017
1112
  //#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";
1113
+ 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
1114
  const tagId$2 = "dsh-llm-fallbacks/FallbacksCard.module.css";
1020
1115
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
1021
1116
  const tag = document.createElement("style");
@@ -1050,13 +1145,15 @@ window.__ModuleLoader__.load({
1050
1145
  "fieldset": "_94c91bfd_fieldset",
1051
1146
  "field": "_67826267_field",
1052
1147
  "fieldLabel": "_13e68c3f_fieldLabel",
1148
+ "sectionToggle": "_b116c758_sectionToggle",
1149
+ "sectionToggleText": "_a5313fdf_sectionToggleText",
1053
1150
  "hint": "_4bc809b8_hint",
1054
1151
  "defaultNote": "_579f813a_defaultNote",
1055
1152
  "infoHint": "_a6244318_infoHint",
1056
1153
  "infoHintDisabled": "_e655c840_infoHintDisabled",
1057
1154
  "optionRow": "_a2521bb0_optionRow",
1058
- "wildcardCell": "_929f3ef3_wildcardCell",
1059
1155
  "input": "_f9d86f7b_input",
1156
+ "inputTextarea": "_3a9b42fd_inputTextarea",
1060
1157
  "inputInvalid": "_7b60337a_inputInvalid",
1061
1158
  "selectInput": "_16eb7153_selectInput",
1062
1159
  "numberFields": "_3e95886d_numberFields",
@@ -1190,8 +1287,14 @@ window.__ModuleLoader__.load({
1190
1287
  * rootChain and role chains. Returns one localized message per violation;
1191
1288
  * a non-empty result blocks {@link save} — the draft is never written.
1192
1289
  * `persona` is free text and never validated.
1290
+ *
1291
+ * `seededIds` is the live trimmed-id → overridden map derived from
1292
+ * `state.seeds` (spec §9.4): the empty-chain block relaxes for seeded ids
1293
+ * only (spec §9.6 / AC-3 — a seeded role's chain is legitimately empty by
1294
+ * design, R4, and its persona edits must stay persistable); non-seeded
1295
+ * behavior is byte-identical.
1193
1296
  */
1194
- function validateDraft(draft, t) {
1297
+ function validateDraft(draft, t, seededIds) {
1195
1298
  const errors = [];
1196
1299
  const declaredIds = /* @__PURE__ */ new Set();
1197
1300
  for (const role of draft.roles.list) {
@@ -1207,7 +1310,7 @@ window.__ModuleLoader__.load({
1207
1310
  message: error.message
1208
1311
  }));
1209
1312
  }
1210
- if ((role.chain ?? []).length === 0) errors.push(t("validation.roleChainRequired", { id: role.id }));
1313
+ if ((role.chain ?? []).length === 0 && !seededIds.has(role.id.trim())) errors.push(t("validation.roleChainRequired", { id: role.id }));
1211
1314
  }
1212
1315
  for (const entry of draft.rootChain) try {
1213
1316
  parseSelector(entry);
@@ -1288,14 +1391,16 @@ window.__ModuleLoader__.load({
1288
1391
  });
1289
1392
  }
1290
1393
  /**
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
1394
+ * One chain entry selector row: provider select + model select (cascade).
1395
+ * The GUI never offers a `provider/*` wildcard (root agent and role chains
1396
+ * alike; provider-any matching lives in the role rules) — but `provider/*`
1397
+ * stays a legal YAML entry, so a wildcard row read back from the server
1398
+ * renders with the legacy-conversion hint and an enabled model select:
1399
+ * picking a model converts the row to an exact entry (the patch carries
1400
+ * `wildcard: false`). The provider options are the catalog providers
1401
+ * **configured on the Models page** (`configuredProviders`, the Models-page
1402
+ * `configured` join) — unconfigured directory providers never become
1403
+ * offerable. Out-of-catalog values read back from the server render as
1299
1404
  * a synthetic option with the short "outside catalog" annotation and stay
1300
1405
  * selected — keeping them saves verbatim; picking a catalog option is an
1301
1406
  * intentional change. A directory provider that is not configured is offered
@@ -1303,107 +1408,93 @@ window.__ModuleLoader__.load({
1303
1408
  * existing value is never hidden or dropped. New rows only offer configured
1304
1409
  * options.
1305
1410
  */
1306
- function ChainSelectorEditor({ selector, catalog, configuredProviders, disabled, t, onChange, onRemove, allowWildcard = true }) {
1411
+ function ChainSelectorEditor({ selector, catalog, configuredProviders, disabled, t, onChange, onRemove }) {
1307
1412
  const providerRaw = selectionToRaw(selector.provider);
1308
1413
  const providerOutside = selector.provider?.kind === "outside";
1309
1414
  const providerUnconfigured = !providerOutside && providerRaw !== "" && (catalog?.providers.some((entry) => entry.provider === providerRaw) ?? false) && !configuredProviders.some((entry) => entry.provider === providerRaw);
1310
1415
  const modelRaw = selectionToRaw(selector.model);
1311
1416
  const modelOutside = selector.model?.kind === "outside";
1312
1417
  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 === "";
1418
+ const groupMissing = providerRaw !== "" && !providerOutside && group === void 0;
1419
+ const modelDisabled = disabled || providerRaw === "" || groupMissing || providerOutside && modelRaw === "";
1315
1420
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1316
1421
  className: FallbacksCard_module_css_default.selectorRow,
1317
1422
  children: [
1318
1423
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1319
1424
  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", {
1425
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1426
+ className: FallbacksCard_module_css_default.ruleCell,
1427
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1428
+ className: FallbacksCard_module_css_default.ruleCellLabel,
1429
+ children: t("roles.rule.provider")
1430
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1431
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
1432
+ value: providerRaw,
1433
+ disabled,
1434
+ onChange: (event) => {
1435
+ if (event.target.value === providerRaw) return;
1436
+ onChange({
1437
+ provider: classifyProvider(event.target.value, catalog),
1438
+ model: null
1439
+ });
1440
+ },
1441
+ children: [
1442
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1443
+ value: "",
1444
+ children: t("chains.selector.providerPlaceholder")
1445
+ }),
1446
+ configuredProviders.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1447
+ value: entry.provider,
1448
+ children: entry.displayName
1449
+ }, entry.provider)),
1450
+ providerUnconfigured && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1451
+ value: providerRaw,
1452
+ children: `${providerRaw}${t("catalog.unconfigured.short")}`
1453
+ }),
1454
+ providerOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1455
+ value: providerRaw,
1456
+ children: `${providerRaw}${t("catalog.outside.short")}`
1457
+ })
1458
+ ]
1459
+ })]
1460
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1461
+ className: FallbacksCard_module_css_default.ruleCell,
1462
+ children: [
1463
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1324
1464
  className: FallbacksCard_module_css_default.ruleCellLabel,
1325
- children: t("roles.rule.provider")
1326
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1465
+ children: t("roles.rule.model")
1466
+ }),
1467
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1327
1468
  className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.selectInput}`,
1328
- value: providerRaw,
1329
- disabled,
1469
+ value: modelRaw,
1470
+ disabled: modelDisabled,
1330
1471
  onChange: (event) => {
1331
- if (event.target.value === providerRaw) return;
1332
1472
  onChange({
1333
- provider: classifyProvider(event.target.value, catalog),
1334
- model: null
1473
+ model: classifyModel(providerRaw, event.target.value, catalog),
1474
+ ...selector.wildcard ? { wildcard: false } : {}
1335
1475
  });
1336
1476
  },
1337
1477
  children: [
1338
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1478
+ modelRaw === "" && !providerOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1339
1479
  value: "",
1340
- children: t("chains.selector.providerPlaceholder")
1341
- }),
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")}`
1480
+ children: t("chains.selector.modelPlaceholder")
1349
1481
  }),
1350
- providerOutside && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1351
- value: providerRaw,
1352
- children: `${providerRaw}${t("catalog.outside.short")}`
1482
+ (group?.models ?? []).map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1483
+ value: model.id,
1484
+ children: model.name
1485
+ }, model.id)),
1486
+ modelOutside && !selector.wildcard && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1487
+ value: modelRaw,
1488
+ children: `${modelRaw}${t("catalog.outside.short")}`
1353
1489
  })
1354
1490
  ]
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
- ]
1491
+ }),
1492
+ groupMissing && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1493
+ className: FallbacksCard_module_css_default.hint,
1494
+ children: t("chains.selector.noModelsStrict")
1495
+ })
1496
+ ]
1497
+ })]
1407
1498
  }),
1408
1499
  (providerOutside || modelOutside) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1409
1500
  className: FallbacksCard_module_css_default.hint,
@@ -1412,6 +1503,10 @@ window.__ModuleLoader__.load({
1412
1503
  disabled
1413
1504
  })]
1414
1505
  }),
1506
+ selector.wildcard && !modelDisabled && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1507
+ className: FallbacksCard_module_css_default.hint,
1508
+ children: t("chains.selector.wildcardLegacy")
1509
+ }),
1415
1510
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1416
1511
  className: FallbacksCard_module_css_default.cardFoot,
1417
1512
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -1565,6 +1660,8 @@ window.__ModuleLoader__.load({
1565
1660
  const unknownCodes = scalars.triggerCodes.filter((code) => !KNOWN_TRIGGER_CODES.includes(code));
1566
1661
  const roleOptions = ruleRoleOptions({ list: roleRows });
1567
1662
  const invalidRoleIds = validationAttempted ? collectInvalidRoleIds(roleRows) : null;
1663
+ const seededIds = /* @__PURE__ */ new Map();
1664
+ for (const seed of state.seeds) seededIds.set(seed.id.trim(), seed.overridden);
1568
1665
  const effectiveModel = deriveEffectiveModel(state.config, state.switches);
1569
1666
  const effectiveModelLine = effectiveModel.kind === "unavailable" ? t("status.effectiveModel.unavailable") : `${effectiveModel.provider}/${effectiveModel.model} · ${t("status.effectiveModel.note")}`;
1570
1667
  const latestSwitch = state.switches[0];
@@ -1598,7 +1695,7 @@ window.__ModuleLoader__.load({
1598
1695
  dirty
1599
1696
  ]);
1600
1697
  const save = () => {
1601
- const errors = validateDraft(draft, t);
1698
+ const errors = validateDraft(draft, t, seededIds);
1602
1699
  if (hasEmptyRuleRows) errors.push(t("validation.ruleRoleRequired"));
1603
1700
  if (errors.length > 0) {
1604
1701
  setValidationErrors(errors);
@@ -1619,7 +1716,7 @@ window.__ModuleLoader__.load({
1619
1716
  };
1620
1717
  (0, react.useEffect)(() => {
1621
1718
  if (!validationAttempted) return;
1622
- if (validateDraft(draft, t).length === 0 && !ruleRows.some((row) => row.role === "")) {
1719
+ if (validateDraft(draft, t, seededIds).length === 0 && !ruleRows.some((row) => row.role === "")) {
1623
1720
  setValidationErrors([]);
1624
1721
  setValidationAttempted(false);
1625
1722
  }
@@ -1637,6 +1734,7 @@ window.__ModuleLoader__.load({
1637
1734
  });
1638
1735
  };
1639
1736
  const [userOpen, setUserOpen] = (0, react.useState)(false);
1737
+ const [advancedOpen, setAdvancedOpen] = (0, react.useState)(false);
1640
1738
  const degradedLatch = (0, react.useRef)(false);
1641
1739
  const errorLatch = (0, react.useRef)(false);
1642
1740
  if (state.status === "ready") {
@@ -1645,6 +1743,7 @@ window.__ModuleLoader__.load({
1645
1743
  } else if (state.status === "error") errorLatch.current = true;
1646
1744
  const degraded = state.status === "ready" ? !state.present : degradedLatch.current;
1647
1745
  const open = userOpen || errorLatch.current || degraded;
1746
+ const advancedVisible = advancedOpen || !writable;
1648
1747
  const title = t("title");
1649
1748
  const header = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1650
1749
  type: "button",
@@ -1798,7 +1897,6 @@ window.__ModuleLoader__.load({
1798
1897
  configuredProviders: state.configuredProviders,
1799
1898
  disabled: !writable,
1800
1899
  t,
1801
- allowWildcard: false,
1802
1900
  onChange: (patch) => {
1803
1901
  updateRootChainSelector(selectorIndex, patch);
1804
1902
  },
@@ -1841,6 +1939,7 @@ window.__ModuleLoader__.load({
1841
1939
  className: FallbacksCard_module_css_default.list,
1842
1940
  children: roleRows.map((row, index) => {
1843
1941
  const invalid = invalidRoleIds?.has(row.id.trim()) ?? false;
1942
+ const seed = seededIds.get(row.id.trim());
1844
1943
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1845
1944
  className: FallbacksCard_module_css_default.editorCard,
1846
1945
  children: [
@@ -1875,19 +1974,38 @@ window.__ModuleLoader__.load({
1875
1974
  className: FallbacksCard_module_css_default.ruleGrid,
1876
1975
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1877
1976
  className: FallbacksCard_module_css_default.ruleCell,
1878
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1879
- className: FallbacksCard_module_css_default.ruleCellLabel,
1880
- children: t("roles.persona")
1881
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1882
- className: FallbacksCard_module_css_default.input,
1883
- value: row.persona,
1884
- placeholder: t("roles.personaPlaceholder"),
1885
- "aria-label": t("roles.persona"),
1886
- disabled: !writable,
1887
- onChange: (event) => {
1888
- updateRoleRow(index, { persona: event.target.value });
1889
- }
1890
- })]
1977
+ children: [
1978
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1979
+ className: FallbacksCard_module_css_default.ruleCellLabel,
1980
+ children: t("roles.persona")
1981
+ }),
1982
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
1983
+ rows: 3,
1984
+ className: `${FallbacksCard_module_css_default.input} ${FallbacksCard_module_css_default.inputTextarea}`,
1985
+ value: row.persona,
1986
+ placeholder: t("roles.personaPlaceholder"),
1987
+ "aria-label": t("roles.persona"),
1988
+ disabled: !writable,
1989
+ onChange: (event) => {
1990
+ updateRoleRow(index, { persona: event.target.value });
1991
+ }
1992
+ }),
1993
+ seed !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1994
+ className: FallbacksCard_module_css_default.hint,
1995
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1996
+ className: FallbacksCard_module_css_default.pending,
1997
+ children: t(seed ? "roles.seedOverride" : "roles.seedDefault")
1998
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1999
+ variant: "outline",
2000
+ size: "sm",
2001
+ disabled: !writable || saving,
2002
+ onClick: () => {
2003
+ controller.revertSeed(row.id.trim());
2004
+ },
2005
+ children: t("roles.revertPersona")
2006
+ })]
2007
+ })
2008
+ ]
1891
2009
  })
1892
2010
  }),
1893
2011
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -1906,7 +2024,7 @@ window.__ModuleLoader__.load({
1906
2024
  }
1907
2025
  }, selectorIndex)), row.selectors.every((selector) => selectorRowToRaw(selector) === "") && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1908
2026
  className: FallbacksCard_module_css_default.hint,
1909
- children: t("validation.roleChainRequired", { id: row.id })
2027
+ children: seed !== void 0 ? t("roles.seedChainOptional", { id: row.id }) : t("validation.roleChainRequired", { id: row.id })
1910
2028
  })]
1911
2029
  }),
1912
2030
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -2186,220 +2304,231 @@ window.__ModuleLoader__.load({
2186
2304
  className: FallbacksCard_module_css_default.field,
2187
2305
  role: "group",
2188
2306
  "aria-labelledby": "fallbacks-advanced",
2189
- children: [
2190
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2191
- className: FallbacksCard_module_css_default.fieldLabel,
2192
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2193
- id: "fallbacks-advanced",
2194
- children: t("advanced.label")
2195
- })
2196
- }),
2197
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2198
- className: FallbacksCard_module_css_default.field,
2199
- role: "group",
2200
- "aria-labelledby": "fallbacks-trigger-codes",
2201
- children: [
2202
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2203
- className: FallbacksCard_module_css_default.fieldLabel,
2204
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2205
- id: "fallbacks-trigger-codes",
2206
- children: t("triggerCodes.label")
2207
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2208
- label: t("triggerCodes.tooltip"),
2209
- disabled: !writable
2210
- })]
2211
- }),
2212
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2213
- className: FallbacksCard_module_css_default.hint,
2214
- children: t("triggerCodes.hint")
2215
- }),
2216
- KNOWN_TRIGGER_CODES.map((code) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2217
- className: FallbacksCard_module_css_default.optionRow,
2218
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2219
- type: "checkbox",
2220
- checked: scalars.triggerCodes.includes(code),
2221
- onChange: (event) => {
2222
- updateScalars((draft) => {
2223
- draft.triggerCodes = withTriggerCode(draft.triggerCodes, code, event.target.checked);
2224
- });
2225
- }
2226
- }), t(TRIGGER_CODE_LABELS[code])]
2227
- }, code)),
2228
- unknownCodes.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2229
- className: FallbacksCard_module_css_default.hint,
2230
- children: t("triggerCodes.extra", { codes: unknownCodes.join(", ") })
2231
- })
2232
- ]
2233
- }),
2234
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2235
- className: FallbacksCard_module_css_default.field,
2236
- role: "group",
2237
- "aria-labelledby": "fallbacks-revert-policy",
2238
- children: [
2239
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2240
- className: FallbacksCard_module_css_default.fieldLabel,
2241
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2242
- id: "fallbacks-revert-policy",
2243
- children: t("revertPolicy.label")
2244
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2245
- label: t("revertPolicy.tooltip"),
2246
- disabled: !writable
2247
- })]
2248
- }),
2249
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2250
- className: FallbacksCard_module_css_default.hint,
2251
- children: t("revertPolicy.hint")
2252
- }),
2253
- ["cooldown-expiry", "never"].map((policy) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2254
- className: FallbacksCard_module_css_default.optionRow,
2255
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2256
- type: "radio",
2257
- name: "fallbacks-revert-policy",
2258
- checked: scalars.revertPolicy === policy,
2259
- onChange: () => {
2260
- updateScalars((draft) => {
2261
- draft.revertPolicy = policy;
2262
- });
2263
- }
2264
- }), t(`revertPolicy.${policy}`)]
2265
- }, policy))
2266
- ]
2267
- }),
2268
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2269
- className: FallbacksCard_module_css_default.numberFields,
2270
- children: [
2271
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2272
- className: FallbacksCard_module_css_default.field,
2273
- children: [
2274
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2275
- className: FallbacksCard_module_css_default.fieldLabel,
2276
- children: [
2277
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2278
- htmlFor: "fallbacks-cooldown-ms",
2279
- children: t("cooldownMs.label")
2280
- }),
2281
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2282
- label: t("cooldownMs.tooltip"),
2283
- disabled: !writable
2284
- }),
2285
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2286
- className: FallbacksCard_module_css_default.defaultNote,
2287
- children: [
2288
- t("defaults.prefix"),
2289
- ": ",
2290
- state.config.cooldownMs
2291
- ]
2292
- })
2293
- ]
2294
- }),
2295
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2296
- id: "fallbacks-cooldown-ms",
2297
- className: FallbacksCard_module_css_default.input,
2298
- type: "number",
2299
- min: 0,
2300
- value: String(scalars.cooldownMs),
2301
- disabled: !writable,
2302
- onChange: (event) => {
2303
- updateScalars((draft) => {
2304
- draft.cooldownMs = parseCount(event.target.value);
2305
- });
2306
- }
2307
- }),
2308
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2309
- className: FallbacksCard_module_css_default.hint,
2310
- children: t("cooldownMs.hint")
2311
- })
2312
- ]
2313
- }),
2314
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2315
- className: FallbacksCard_module_css_default.field,
2316
- children: [
2317
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2318
- className: FallbacksCard_module_css_default.fieldLabel,
2319
- children: [
2320
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2321
- htmlFor: "fallbacks-max-switches",
2322
- children: t("maxSwitchesPerStep.label")
2323
- }),
2324
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2325
- label: t("maxSwitchesPerStep.tooltip"),
2326
- disabled: !writable
2327
- }),
2328
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2329
- className: FallbacksCard_module_css_default.defaultNote,
2330
- children: [
2331
- t("defaults.prefix"),
2332
- ": ",
2333
- state.config.maxSwitchesPerStep
2334
- ]
2335
- })
2336
- ]
2337
- }),
2338
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2339
- id: "fallbacks-max-switches",
2340
- className: FallbacksCard_module_css_default.input,
2341
- type: "number",
2342
- min: 0,
2343
- value: String(scalars.maxSwitchesPerStep),
2344
- disabled: !writable,
2307
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
2308
+ type: "button",
2309
+ className: FallbacksCard_module_css_default.sectionToggle,
2310
+ disabled: !writable,
2311
+ "aria-expanded": advancedVisible,
2312
+ "aria-controls": advancedVisible ? "fallbacks-advanced-body" : void 0,
2313
+ "aria-label": t(advancedVisible ? "advanced.collapse" : "advanced.expand"),
2314
+ onClick: () => {
2315
+ if (writable) setAdvancedOpen(!advancedOpen);
2316
+ },
2317
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2318
+ id: "fallbacks-advanced",
2319
+ className: FallbacksCard_module_css_default.sectionToggleText,
2320
+ children: t("advanced.label")
2321
+ }), /* @__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 })]
2322
+ }), advancedVisible && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2323
+ id: "fallbacks-advanced-body",
2324
+ children: [
2325
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2326
+ className: FallbacksCard_module_css_default.field,
2327
+ role: "group",
2328
+ "aria-labelledby": "fallbacks-trigger-codes",
2329
+ children: [
2330
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2331
+ className: FallbacksCard_module_css_default.fieldLabel,
2332
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2333
+ id: "fallbacks-trigger-codes",
2334
+ children: t("triggerCodes.label")
2335
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2336
+ label: t("triggerCodes.tooltip"),
2337
+ disabled: !writable
2338
+ })]
2339
+ }),
2340
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2341
+ className: FallbacksCard_module_css_default.hint,
2342
+ children: t("triggerCodes.hint")
2343
+ }),
2344
+ KNOWN_TRIGGER_CODES.map((code) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2345
+ className: FallbacksCard_module_css_default.optionRow,
2346
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2347
+ type: "checkbox",
2348
+ checked: scalars.triggerCodes.includes(code),
2345
2349
  onChange: (event) => {
2346
2350
  updateScalars((draft) => {
2347
- draft.maxSwitchesPerStep = parseCount(event.target.value);
2351
+ draft.triggerCodes = withTriggerCode(draft.triggerCodes, code, event.target.checked);
2348
2352
  });
2349
2353
  }
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) => {
2354
+ }), t(TRIGGER_CODE_LABELS[code])]
2355
+ }, code)),
2356
+ unknownCodes.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2357
+ className: FallbacksCard_module_css_default.hint,
2358
+ children: t("triggerCodes.extra", { codes: unknownCodes.join(", ") })
2359
+ })
2360
+ ]
2361
+ }),
2362
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2363
+ className: FallbacksCard_module_css_default.field,
2364
+ role: "group",
2365
+ "aria-labelledby": "fallbacks-revert-policy",
2366
+ children: [
2367
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2368
+ className: FallbacksCard_module_css_default.fieldLabel,
2369
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2370
+ id: "fallbacks-revert-policy",
2371
+ children: t("revertPolicy.label")
2372
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2373
+ label: t("revertPolicy.tooltip"),
2374
+ disabled: !writable
2375
+ })]
2376
+ }),
2377
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2378
+ className: FallbacksCard_module_css_default.hint,
2379
+ children: t("revertPolicy.hint")
2380
+ }),
2381
+ ["cooldown-expiry", "never"].map((policy) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
2382
+ className: FallbacksCard_module_css_default.optionRow,
2383
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2384
+ type: "radio",
2385
+ name: "fallbacks-revert-policy",
2386
+ checked: scalars.revertPolicy === policy,
2387
+ onChange: () => {
2389
2388
  updateScalars((draft) => {
2390
- draft.alwaysModeRetryCap = parseCount(event.target.value);
2389
+ draft.revertPolicy = policy;
2391
2390
  });
2392
2391
  }
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
- ]
2392
+ }), t(`revertPolicy.${policy}`)]
2393
+ }, policy))
2394
+ ]
2395
+ }),
2396
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2397
+ className: FallbacksCard_module_css_default.numberFields,
2398
+ children: [
2399
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2400
+ className: FallbacksCard_module_css_default.field,
2401
+ children: [
2402
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2403
+ className: FallbacksCard_module_css_default.fieldLabel,
2404
+ children: [
2405
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2406
+ htmlFor: "fallbacks-cooldown-ms",
2407
+ children: t("cooldownMs.label")
2408
+ }),
2409
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2410
+ label: t("cooldownMs.tooltip"),
2411
+ disabled: !writable
2412
+ }),
2413
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2414
+ className: FallbacksCard_module_css_default.defaultNote,
2415
+ children: [
2416
+ t("defaults.prefix"),
2417
+ ": ",
2418
+ state.config.cooldownMs
2419
+ ]
2420
+ })
2421
+ ]
2422
+ }),
2423
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2424
+ id: "fallbacks-cooldown-ms",
2425
+ className: FallbacksCard_module_css_default.input,
2426
+ type: "number",
2427
+ min: 0,
2428
+ value: String(scalars.cooldownMs),
2429
+ disabled: !writable,
2430
+ onChange: (event) => {
2431
+ updateScalars((draft) => {
2432
+ draft.cooldownMs = parseCount(event.target.value);
2433
+ });
2434
+ }
2435
+ }),
2436
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2437
+ className: FallbacksCard_module_css_default.hint,
2438
+ children: t("cooldownMs.hint")
2439
+ })
2440
+ ]
2441
+ }),
2442
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2443
+ className: FallbacksCard_module_css_default.field,
2444
+ children: [
2445
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2446
+ className: FallbacksCard_module_css_default.fieldLabel,
2447
+ children: [
2448
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2449
+ htmlFor: "fallbacks-max-switches",
2450
+ children: t("maxSwitchesPerStep.label")
2451
+ }),
2452
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2453
+ label: t("maxSwitchesPerStep.tooltip"),
2454
+ disabled: !writable
2455
+ }),
2456
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2457
+ className: FallbacksCard_module_css_default.defaultNote,
2458
+ children: [
2459
+ t("defaults.prefix"),
2460
+ ": ",
2461
+ state.config.maxSwitchesPerStep
2462
+ ]
2463
+ })
2464
+ ]
2465
+ }),
2466
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2467
+ id: "fallbacks-max-switches",
2468
+ className: FallbacksCard_module_css_default.input,
2469
+ type: "number",
2470
+ min: 0,
2471
+ value: String(scalars.maxSwitchesPerStep),
2472
+ disabled: !writable,
2473
+ onChange: (event) => {
2474
+ updateScalars((draft) => {
2475
+ draft.maxSwitchesPerStep = parseCount(event.target.value);
2476
+ });
2477
+ }
2478
+ }),
2479
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2480
+ className: FallbacksCard_module_css_default.hint,
2481
+ children: t("maxSwitchesPerStep.hint")
2482
+ })
2483
+ ]
2484
+ }),
2485
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2486
+ className: FallbacksCard_module_css_default.field,
2487
+ children: [
2488
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2489
+ className: FallbacksCard_module_css_default.fieldLabel,
2490
+ children: [
2491
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
2492
+ htmlFor: "fallbacks-always-cap",
2493
+ children: t("alwaysModeRetryCap.label")
2494
+ }),
2495
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InfoHint, {
2496
+ label: t("alwaysModeRetryCap.tooltip"),
2497
+ disabled: !writable
2498
+ }),
2499
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2500
+ className: FallbacksCard_module_css_default.defaultNote,
2501
+ children: [
2502
+ t("defaults.prefix"),
2503
+ ": ",
2504
+ state.config.alwaysModeRetryCap
2505
+ ]
2506
+ })
2507
+ ]
2508
+ }),
2509
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
2510
+ id: "fallbacks-always-cap",
2511
+ className: FallbacksCard_module_css_default.input,
2512
+ type: "number",
2513
+ min: 0,
2514
+ value: String(scalars.alwaysModeRetryCap),
2515
+ disabled: !writable,
2516
+ onChange: (event) => {
2517
+ updateScalars((draft) => {
2518
+ draft.alwaysModeRetryCap = parseCount(event.target.value);
2519
+ });
2520
+ }
2521
+ }),
2522
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2523
+ className: FallbacksCard_module_css_default.hint,
2524
+ children: t("alwaysModeRetryCap.hint")
2525
+ })
2526
+ ]
2527
+ })
2528
+ ]
2529
+ })
2530
+ ]
2531
+ })]
2403
2532
  })
2404
2533
  ]
2405
2534
  })