dsh-model-switch 0.4.24 → 0.4.25

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/lib/client.js CHANGED
@@ -1026,8 +1026,6 @@ window.__ModuleLoader__.load({
1026
1026
  const [titleDraft, setTitleDraft, resetTitle] = useDraft(sessionTitle);
1027
1027
  const [busy, setBusy] = (0, react.useState)();
1028
1028
  const [message, setMessage] = (0, react.useState)();
1029
- const [compactBusy, setCompactBusy] = (0, react.useState)(false);
1030
- const [compactError, setCompactError] = (0, react.useState)();
1031
1029
  const [groupProvider, setGroupProvider] = (0, react.useState)("");
1032
1030
  const [groupDraft, setGroupDraft] = (0, react.useState)(void 0);
1033
1031
  const loadSearchCapabilities = props.loadCapabilities;
@@ -1072,11 +1070,19 @@ window.__ModuleLoader__.load({
1072
1070
  /**
1073
1071
  * Seed an unsaved group from the live routes.
1074
1072
  *
1075
- * Only the two per-session capabilities are seeded: a group carrying a
1076
- * compaction or reviewer pin would store a value no consumer reads (see
1077
- * src/session-route.ts), which is worse than storing nothing.
1073
+ * All four per-session capabilities (compaction, autoReview, sessionTitle,
1074
+ * subagent) are seeded from their active deployment-wide cards so a newly
1075
+ * edited provider group starts from the currently active configuration.
1078
1076
  */
1079
1077
  const seedGroupFromActive = () => {
1078
+ const compactionPin = compaction.value !== void 0 && routePairConfigured(compaction.value) ? {
1079
+ provider: compaction.value.provider ?? "",
1080
+ model: compaction.value.model ?? ""
1081
+ } : void 0;
1082
+ const reviewPin = autoReview.value !== void 0 && routePairConfigured(autoReview.value) ? {
1083
+ provider: autoReview.value.provider ?? "",
1084
+ model: autoReview.value.model ?? ""
1085
+ } : void 0;
1080
1086
  const title = sessionTitle.value !== void 0 && routePairConfigured(sessionTitle.value) ? {
1081
1087
  provider: sessionTitle.value.provider ?? "",
1082
1088
  model: sessionTitle.value.model ?? ""
@@ -1087,6 +1093,14 @@ window.__ModuleLoader__.load({
1087
1093
  reasoningEffort: subagent.value.reasoningEffort
1088
1094
  } : void 0;
1089
1095
  return {
1096
+ ...compactionPin === void 0 ? {} : {
1097
+ compactProvider: compactionPin.provider,
1098
+ compactModel: compactionPin.model
1099
+ },
1100
+ ...reviewPin === void 0 ? {} : {
1101
+ autoReviewProvider: reviewPin.provider,
1102
+ autoReviewModel: reviewPin.model
1103
+ },
1090
1104
  ...title === void 0 ? {} : {
1091
1105
  titleProvider: title.provider,
1092
1106
  titleModel: title.model
@@ -1183,6 +1197,7 @@ window.__ModuleLoader__.load({
1183
1197
  const compactionOn = routePairConfigured(compaction.value);
1184
1198
  const titleChoices = deriveRouteChoices(groups, titleDraft);
1185
1199
  const titleOn = routePairConfigured(sessionTitle.value);
1200
+ const catalogSeed = firstCatalogRoute(groups);
1186
1201
  const synced = [
1187
1202
  subagent,
1188
1203
  search,
@@ -1251,7 +1266,7 @@ window.__ModuleLoader__.load({
1251
1266
  ]));
1252
1267
  };
1253
1268
  const persistSubagentEnabled = (enabled) => {
1254
- if (subagentDraft === void 0 || busy === "subagent" || !subagent.writable) return;
1269
+ if (subagentDraft === void 0 || busy !== void 0 || !subagent.writable) return;
1255
1270
  const mode = subagentModeForEnabled(enabled);
1256
1271
  run("subagent", () => props.saveSubagent([{
1257
1272
  field: "mode",
@@ -1308,7 +1323,7 @@ window.__ModuleLoader__.load({
1308
1323
  * flips silently.
1309
1324
  */
1310
1325
  const persistAutoReviewEnabled = (enabled) => {
1311
- if (busy === "autoReview" || !autoReview.writable || autoReview.value === void 0) return;
1326
+ if (busy !== void 0 || !autoReview.writable || autoReview.value === void 0) return;
1312
1327
  if (!enabled) {
1313
1328
  setOpen((current) => current === "autoReview" ? void 0 : current);
1314
1329
  run("autoReview", () => props.saveCapability("autoReview", [{
@@ -1320,13 +1335,12 @@ window.__ModuleLoader__.load({
1320
1335
  }]));
1321
1336
  return;
1322
1337
  }
1323
- const seed = firstCatalogRoute(groups);
1324
- if (seed === void 0) return;
1325
- run("autoReview", () => props.saveCapability("autoReview", pairOps(seed))).then((accepted) => {
1338
+ if (catalogSeed === void 0) return;
1339
+ run("autoReview", () => props.saveCapability("autoReview", pairOps(catalogSeed))).then((accepted) => {
1326
1340
  if (!accepted) return;
1327
1341
  setAutoReviewDraft({
1328
- provider: seed.provider,
1329
- model: seed.model
1342
+ provider: catalogSeed.provider,
1343
+ model: catalogSeed.model
1330
1344
  });
1331
1345
  setOpen("autoReview");
1332
1346
  });
@@ -1338,7 +1352,7 @@ window.__ModuleLoader__.load({
1338
1352
  * reports, never flips silently.
1339
1353
  */
1340
1354
  const persistCompactionEnabled = (enabled) => {
1341
- if (busy === "compaction" || !compaction.writable || compaction.value === void 0) return;
1355
+ if (busy !== void 0 || !compaction.writable || compaction.value === void 0) return;
1342
1356
  if (!enabled) {
1343
1357
  setOpen((current) => current === "compaction" ? void 0 : current);
1344
1358
  run("compaction", () => props.saveCapability("compaction", [{
@@ -1350,13 +1364,12 @@ window.__ModuleLoader__.load({
1350
1364
  }]));
1351
1365
  return;
1352
1366
  }
1353
- const seed = firstCatalogRoute(groups);
1354
- if (seed === void 0) return;
1355
- run("compaction", () => props.saveCapability("compaction", pairOps(seed))).then((accepted) => {
1367
+ if (catalogSeed === void 0) return;
1368
+ run("compaction", () => props.saveCapability("compaction", pairOps(catalogSeed))).then((accepted) => {
1356
1369
  if (!accepted) return;
1357
1370
  setCompactionDraft({
1358
- provider: seed.provider,
1359
- model: seed.model
1371
+ provider: catalogSeed.provider,
1372
+ model: catalogSeed.model
1360
1373
  });
1361
1374
  setOpen("compaction");
1362
1375
  });
@@ -1368,7 +1381,7 @@ window.__ModuleLoader__.load({
1368
1381
  * reports, never flips silently.
1369
1382
  */
1370
1383
  const persistTitleEnabled = (enabled) => {
1371
- if (busy === "sessionTitle" || !sessionTitle.writable || sessionTitle.value === void 0) return;
1384
+ if (busy !== void 0 || !sessionTitle.writable || sessionTitle.value === void 0) return;
1372
1385
  if (!enabled) {
1373
1386
  setOpen((current) => current === "sessionTitle" ? void 0 : current);
1374
1387
  run("sessionTitle", () => props.saveCapability("title", [{
@@ -1380,13 +1393,12 @@ window.__ModuleLoader__.load({
1380
1393
  }]));
1381
1394
  return;
1382
1395
  }
1383
- const seed = firstCatalogRoute(groups);
1384
- if (seed === void 0) return;
1385
- run("sessionTitle", () => props.saveCapability("title", pairOps(seed))).then((accepted) => {
1396
+ if (catalogSeed === void 0) return;
1397
+ run("sessionTitle", () => props.saveCapability("title", pairOps(catalogSeed))).then((accepted) => {
1386
1398
  if (!accepted) return;
1387
1399
  setTitleDraft({
1388
- provider: seed.provider,
1389
- model: seed.model
1400
+ provider: catalogSeed.provider,
1401
+ model: catalogSeed.model
1390
1402
  });
1391
1403
  setOpen("sessionTitle");
1392
1404
  });
@@ -1394,19 +1406,27 @@ window.__ModuleLoader__.load({
1394
1406
  const subagentRole = subagentDraft?.mode === "fixed" && subagentDraft.provider !== void 0 && subagentDraft.provider !== "" ? props.providerRoleOf?.(subagentDraft.provider) : void 0;
1395
1407
  const subagentOn = subagentDraft?.mode === "fixed";
1396
1408
  const subagentSummary = subagentDraft === void 0 ? props.t("loading") : subagentOn ? compact(routeName(groups, subagentRoute), isAgentRole(subagentRole) ? props.t("agentBadge") : void 0, subagentEffortSegment(props.t, subagentCatalogModel, (subagentDraft.reasoningEffort ?? "").trim(), defaultEffort)) : props.t("subagentOff");
1397
- const subagentDisabled = subagent.status !== "ready" || !subagent.writable || subagentDraft === void 0 || busy === "subagent" || !subagentOn || (subagentDraft.provider ?? "").trim() === "" || (subagentDraft.model ?? "").trim() === "";
1398
- const capabilityDisabled = (route, snapshot, next) => snapshot.status !== "ready" || !snapshot.writable || next === void 0 || busy === route || (next.provider ?? "").trim() === "" || (next.model ?? "").trim() === "";
1399
- const autoReviewDisabled = autoReview.status !== "ready" || !autoReview.writable || autoReviewDraft === void 0 || busy === "autoReview" || !autoReviewOn || (autoReviewDraft.provider ?? "").trim() === "" || (autoReviewDraft.model ?? "").trim() === "";
1400
- const compactionDisabled = compaction.status !== "ready" || !compaction.writable || compactionDraft === void 0 || busy === "compaction" || !compactionOn || (compactionDraft.provider ?? "").trim() === "" || (compactionDraft.model ?? "").trim() === "";
1401
- const titleDisabled = sessionTitle.status !== "ready" || !sessionTitle.writable || titleDraft === void 0 || busy === "sessionTitle" || !titleOn || (titleDraft.provider ?? "").trim() === "" || (titleDraft.model ?? "").trim() === "";
1409
+ const subagentSwitchDisabled = busy !== void 0 || !subagent.writable || subagentDraft === void 0;
1410
+ const autoReviewSwitchDisabled = busy !== void 0 || !autoReview.writable || autoReview.value === void 0 || !autoReviewOn && catalogSeed === void 0;
1411
+ const compactionSwitchDisabled = busy !== void 0 || !compaction.writable || compaction.value === void 0 || !compactionOn && catalogSeed === void 0;
1412
+ const titleSwitchDisabled = busy !== void 0 || !sessionTitle.writable || sessionTitle.value === void 0 || !titleOn && catalogSeed === void 0;
1413
+ const subagentDisabled = subagent.status !== "ready" || !subagent.writable || subagentDraft === void 0 || busy !== void 0 || !subagentOn || (subagentDraft.provider ?? "").trim() === "" || (subagentDraft.model ?? "").trim() === "";
1414
+ const capabilityDisabled = (_route, snapshot, next) => snapshot.status !== "ready" || !snapshot.writable || next === void 0 || busy !== void 0 || (next.provider ?? "").trim() === "" || (next.model ?? "").trim() === "";
1415
+ const autoReviewDisabled = autoReview.status !== "ready" || !autoReview.writable || autoReviewDraft === void 0 || busy !== void 0 || !autoReviewOn || (autoReviewDraft.provider ?? "").trim() === "" || (autoReviewDraft.model ?? "").trim() === "";
1416
+ const compactionDisabled = compaction.status !== "ready" || !compaction.writable || compactionDraft === void 0 || busy !== void 0 || !compactionOn || (compactionDraft.provider ?? "").trim() === "" || (compactionDraft.model ?? "").trim() === "";
1417
+ const titleDisabled = sessionTitle.status !== "ready" || !sessionTitle.writable || titleDraft === void 0 || busy !== void 0 || !titleOn || (titleDraft.provider ?? "").trim() === "" || (titleDraft.model ?? "").trim() === "";
1402
1418
  const configuredGroupCount = Object.keys(providerGroups).length;
1403
- const groupControlsDisabled = busy === "providerGroups" || switchSettings.status !== "ready" || !switchSettings.writable;
1419
+ const groupControlsDisabled = busy !== void 0 || switchSettings.status !== "ready" || !switchSettings.writable;
1404
1420
  const groupProviderChoices = deriveRouteChoices(groups, groupProvider === "" ? void 0 : { provider: groupProvider }).providers.map((choice) => groupOf(providerGroups, choice.id) === void 0 ? choice : {
1405
1421
  ...choice,
1406
1422
  name: choice.name + " ✓"
1407
1423
  });
1424
+ const groupCompaction = groupDraft === void 0 ? void 0 : draftPair(groupDraft.compactProvider, groupDraft.compactModel);
1425
+ const groupAutoReview = groupDraft === void 0 ? void 0 : draftPair(groupDraft.autoReviewProvider, groupDraft.autoReviewModel);
1408
1426
  const groupTitle = groupDraft === void 0 ? void 0 : draftPair(groupDraft.titleProvider, groupDraft.titleModel);
1409
1427
  const groupSubagent = groupDraft === void 0 ? void 0 : draftPair(groupDraft.subagentProvider, groupDraft.subagentModel);
1428
+ const groupCompactionChoices = deriveRouteChoices(groups, groupCompaction);
1429
+ const groupAutoReviewChoices = deriveRouteChoices(groups, groupAutoReview);
1410
1430
  const groupTitleChoices = deriveRouteChoices(groups, groupTitle);
1411
1431
  const groupSubagentChoices = deriveRouteChoices(groups, groupSubagent);
1412
1432
  const groupSubMatched = matchCatalogModel(groups.find((group) => group.id === (groupDraft?.subagentProvider ?? ""))?.models ?? [], groupDraft?.subagentModel ?? "");
@@ -1423,23 +1443,36 @@ window.__ModuleLoader__.load({
1423
1443
  return efforts;
1424
1444
  })();
1425
1445
  /**
1426
- * Enable a capability row by pinning it: to that capability's live route
1427
- * when one is configured, else to the catalog head (same sources the
1428
- * editor's first-edit seed uses).
1429
- *
1430
- * Only the two capabilities with a public per-session seam are editable here
1431
- * (see src/session-route.ts): compaction records the summarizer that wrote
1432
- * each summary durably and auto review carries no session identity at its
1433
- * interception point, so both stay deployment-wide cards.
1446
+ * Enable a capability row inside a provider group by pinning it: to that
1447
+ * capability's live route when one is configured, else to the catalog head.
1434
1448
  */
1435
1449
  const enableGroupCapability = (kind) => {
1436
1450
  const active = seedGroupFromActive();
1437
- const seed = firstCatalogRoute(groups);
1438
1451
  setGroupDraft((draft) => {
1439
1452
  if (draft === void 0) return draft;
1453
+ if (kind === "compaction") {
1454
+ const provider = active.compactProvider ?? catalogSeed?.provider;
1455
+ const model = active.compactModel ?? catalogSeed?.model;
1456
+ if (provider === void 0 || model === void 0) return draft;
1457
+ return {
1458
+ ...draft,
1459
+ compactProvider: provider,
1460
+ compactModel: model
1461
+ };
1462
+ }
1463
+ if (kind === "autoReview") {
1464
+ const provider = active.autoReviewProvider ?? catalogSeed?.provider;
1465
+ const model = active.autoReviewModel ?? catalogSeed?.model;
1466
+ if (provider === void 0 || model === void 0) return draft;
1467
+ return {
1468
+ ...draft,
1469
+ autoReviewProvider: provider,
1470
+ autoReviewModel: model
1471
+ };
1472
+ }
1440
1473
  if (kind === "sessionTitle") {
1441
- const provider = active.titleProvider ?? seed?.provider;
1442
- const model = active.titleModel ?? seed?.model;
1474
+ const provider = active.titleProvider ?? catalogSeed?.provider;
1475
+ const model = active.titleModel ?? catalogSeed?.model;
1443
1476
  if (provider === void 0 || model === void 0) return draft;
1444
1477
  return {
1445
1478
  ...draft,
@@ -1447,10 +1480,10 @@ window.__ModuleLoader__.load({
1447
1480
  titleModel: model
1448
1481
  };
1449
1482
  }
1450
- const provider = active.subagentProvider ?? seed?.provider;
1451
- const model = active.subagentModel ?? seed?.model;
1483
+ const provider = active.subagentProvider ?? catalogSeed?.provider;
1484
+ const model = active.subagentModel ?? catalogSeed?.model;
1452
1485
  if (provider === void 0 || model === void 0) return draft;
1453
- const effort = active.subagentProvider !== void 0 ? active.subagentReasoningEffort : seed?.reasoningEffort;
1486
+ const effort = active.subagentProvider !== void 0 ? active.subagentReasoningEffort : catalogSeed?.reasoningEffort;
1454
1487
  return {
1455
1488
  ...draft,
1456
1489
  subagentProvider: provider,
@@ -1462,6 +1495,16 @@ window.__ModuleLoader__.load({
1462
1495
  const disableGroupCapability = (kind) => {
1463
1496
  setGroupDraft((draft) => {
1464
1497
  if (draft === void 0) return draft;
1498
+ if (kind === "compaction") return {
1499
+ ...draft,
1500
+ compactProvider: "",
1501
+ compactModel: ""
1502
+ };
1503
+ if (kind === "autoReview") return {
1504
+ ...draft,
1505
+ autoReviewProvider: "",
1506
+ autoReviewModel: ""
1507
+ };
1465
1508
  if (kind === "sessionTitle") return {
1466
1509
  ...draft,
1467
1510
  titleProvider: "",
@@ -1502,69 +1545,6 @@ window.__ModuleLoader__.load({
1502
1545
  className: cx(ModelSwitchSettings_module_css_default.hint, ModelSwitchSettings_module_css_default.message),
1503
1546
  children: catalogError
1504
1547
  }),
1505
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1506
- className: ModelSwitchSettings_module_css_default.group,
1507
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1508
- className: ModelSwitchSettings_module_css_default.groupLabel,
1509
- children: props.t("sendProtection")
1510
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1511
- className: ModelSwitchSettings_module_css_default.rowCard,
1512
- children: [
1513
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1514
- className: ModelSwitchSettings_module_css_default.routeIcon,
1515
- "aria-hidden": "true",
1516
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
1517
- width: "18",
1518
- height: "18",
1519
- viewBox: "0 0 18 18",
1520
- fill: "none",
1521
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1522
- d: "M4 5.5h10M4 9h7M4 12.5h5",
1523
- stroke: "currentColor",
1524
- strokeLinecap: "round"
1525
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1526
- d: "M13 11.5v3.5l2-1.6",
1527
- stroke: "currentColor",
1528
- strokeLinecap: "round",
1529
- strokeLinejoin: "round"
1530
- })]
1531
- })
1532
- }),
1533
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1534
- className: ModelSwitchSettings_module_css_default.rowCopy,
1535
- children: [
1536
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1537
- className: ModelSwitchSettings_module_css_default.rowTitle,
1538
- children: props.t("compactOnSwitch")
1539
- }),
1540
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1541
- className: ModelSwitchSettings_module_css_default.hint,
1542
- children: props.t("compactOnSwitchHelp")
1543
- }),
1544
- compactError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1545
- className: cx(ModelSwitchSettings_module_css_default.hint, ModelSwitchSettings_module_css_default.message),
1546
- children: compactError
1547
- })
1548
- ]
1549
- }),
1550
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
1551
- checked: switchSettings.value?.compactOnSwitch !== false,
1552
- disabled: !switchSettings.writable || compactBusy,
1553
- label: props.t("compactOnSwitch"),
1554
- onChange: (value) => {
1555
- setCompactBusy(true);
1556
- setCompactError(void 0);
1557
- props.setCompactOnSwitch(value).then(() => {
1558
- setCompactBusy(false);
1559
- }, (error) => {
1560
- setCompactBusy(false);
1561
- setCompactError(error instanceof Error ? error.message : props.t("requestFailed"));
1562
- });
1563
- }
1564
- })
1565
- ]
1566
- })]
1567
- }),
1568
1548
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1569
1549
  className: ModelSwitchSettings_module_css_default.group,
1570
1550
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
@@ -1582,7 +1562,7 @@ window.__ModuleLoader__.load({
1582
1562
  expandable: subagentOn,
1583
1563
  trailing: subagentDraft === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
1584
1564
  checked: subagentOn,
1585
- disabled: busy === "subagent" || !subagent.writable,
1565
+ disabled: subagentSwitchDisabled,
1586
1566
  label: props.t("subagent"),
1587
1567
  onChange: persistSubagentEnabled
1588
1568
  }),
@@ -1595,7 +1575,7 @@ window.__ModuleLoader__.load({
1595
1575
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1596
1576
  label: props.t("provider"),
1597
1577
  value: subagentDraft.provider ?? "",
1598
- disabled: busy === "subagent" || !subagent.writable,
1578
+ disabled: busy !== void 0 || !subagent.writable,
1599
1579
  choices: subagentChoices.providers,
1600
1580
  onChange: (provider) => {
1601
1581
  const first = groups.find((group) => group.id === provider)?.models[0];
@@ -1608,7 +1588,7 @@ window.__ModuleLoader__.load({
1608
1588
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1609
1589
  label: props.t("model"),
1610
1590
  value: subagentCatalogModel?.model.id ?? subagentDraft.model ?? "",
1611
- disabled: busy === "subagent" || !subagent.writable,
1591
+ disabled: busy !== void 0 || !subagent.writable,
1612
1592
  choices: subagentChoices.models,
1613
1593
  onChange: (model) => {
1614
1594
  setSubagentDraft({
@@ -1620,7 +1600,7 @@ window.__ModuleLoader__.load({
1620
1600
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1621
1601
  label: props.t("effort"),
1622
1602
  value: subagentDraft.reasoningEffort || subagentCatalogModel?.effort || "",
1623
- disabled: busy === "subagent" || !subagent.writable,
1603
+ disabled: busy !== void 0 || !subagent.writable,
1624
1604
  choices: subagentEfforts,
1625
1605
  onChange: (effort) => {
1626
1606
  const matched = subagentCatalogModel;
@@ -1686,7 +1666,7 @@ window.__ModuleLoader__.load({
1686
1666
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1687
1667
  label: props.t("provider"),
1688
1668
  value: searchDraft.provider,
1689
- disabled: busy === "search" || !search.writable || searchError !== void 0,
1669
+ disabled: busy !== void 0 || !search.writable || searchError !== void 0,
1690
1670
  choices: searchChoices.providers,
1691
1671
  onChange: (provider) => {
1692
1672
  const first = searchGroups.find((group) => group.id === provider)?.models[0];
@@ -1698,7 +1678,7 @@ window.__ModuleLoader__.load({
1698
1678
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1699
1679
  label: props.t("model"),
1700
1680
  value: searchDraft.model,
1701
- disabled: busy === "search" || !search.writable || searchError !== void 0,
1681
+ disabled: busy !== void 0 || !search.writable || searchError !== void 0,
1702
1682
  choices: searchChoices.models,
1703
1683
  onChange: (model) => {
1704
1684
  setSearchDraft({
@@ -1753,7 +1733,7 @@ window.__ModuleLoader__.load({
1753
1733
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1754
1734
  label: props.t("provider"),
1755
1735
  value: imageDraft.provider,
1756
- disabled: busy === "image" || !image.writable,
1736
+ disabled: busy !== void 0 || !image.writable,
1757
1737
  choices: imageChoices.providers,
1758
1738
  onChange: (provider) => {
1759
1739
  const model = provider === "grok" ? "grok-imagine-image-quality" : groups.find((group) => group.id === provider)?.models[0]?.id;
@@ -1765,7 +1745,7 @@ window.__ModuleLoader__.load({
1765
1745
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1766
1746
  label: props.t("model"),
1767
1747
  value: imageDraft.model,
1768
- disabled: busy === "image" || !image.writable,
1748
+ disabled: busy !== void 0 || !image.writable,
1769
1749
  choices: imageChoices.models,
1770
1750
  onChange: (model) => {
1771
1751
  setImageDraft({
@@ -1811,7 +1791,7 @@ window.__ModuleLoader__.load({
1811
1791
  expandable: autoReviewOn,
1812
1792
  trailing: autoReviewDraft === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
1813
1793
  checked: autoReviewOn,
1814
- disabled: busy === "autoReview" || !autoReview.writable,
1794
+ disabled: autoReviewSwitchDisabled,
1815
1795
  label: props.t("autoReview"),
1816
1796
  onChange: persistAutoReviewEnabled
1817
1797
  }),
@@ -1828,7 +1808,7 @@ window.__ModuleLoader__.load({
1828
1808
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1829
1809
  label: props.t("provider"),
1830
1810
  value: autoReviewDraft.provider ?? "",
1831
- disabled: busy === "autoReview" || !autoReview.writable,
1811
+ disabled: busy !== void 0 || !autoReview.writable,
1832
1812
  choices: autoReviewChoices.providers,
1833
1813
  onChange: (provider) => {
1834
1814
  const first = groups.find((group) => group.id === provider)?.models[0];
@@ -1840,7 +1820,7 @@ window.__ModuleLoader__.load({
1840
1820
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1841
1821
  label: props.t("model"),
1842
1822
  value: autoReviewDraft.model ?? "",
1843
- disabled: busy === "autoReview" || !autoReview.writable,
1823
+ disabled: busy !== void 0 || !autoReview.writable,
1844
1824
  choices: autoReviewChoices.models,
1845
1825
  onChange: (model) => {
1846
1826
  setAutoReviewDraft({
@@ -1875,7 +1855,7 @@ window.__ModuleLoader__.load({
1875
1855
  expandable: compactionOn,
1876
1856
  trailing: compactionDraft === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
1877
1857
  checked: compactionOn,
1878
- disabled: busy === "compaction" || !compaction.writable,
1858
+ disabled: compactionSwitchDisabled,
1879
1859
  label: props.t("compaction"),
1880
1860
  onChange: persistCompactionEnabled
1881
1861
  }),
@@ -1892,7 +1872,7 @@ window.__ModuleLoader__.load({
1892
1872
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1893
1873
  label: props.t("provider"),
1894
1874
  value: compactionDraft.provider ?? "",
1895
- disabled: busy === "compaction" || !compaction.writable,
1875
+ disabled: busy !== void 0 || !compaction.writable,
1896
1876
  choices: compactionChoices.providers,
1897
1877
  onChange: (provider) => {
1898
1878
  const first = groups.find((group) => group.id === provider)?.models[0];
@@ -1904,7 +1884,7 @@ window.__ModuleLoader__.load({
1904
1884
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1905
1885
  label: props.t("model"),
1906
1886
  value: compactionDraft.model ?? "",
1907
- disabled: busy === "compaction" || !compaction.writable,
1887
+ disabled: busy !== void 0 || !compaction.writable,
1908
1888
  choices: compactionChoices.models,
1909
1889
  onChange: (model) => {
1910
1890
  setCompactionDraft({
@@ -1939,7 +1919,7 @@ window.__ModuleLoader__.load({
1939
1919
  expandable: titleOn,
1940
1920
  trailing: titleDraft === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
1941
1921
  checked: titleOn,
1942
- disabled: busy === "sessionTitle" || !sessionTitle.writable,
1922
+ disabled: titleSwitchDisabled,
1943
1923
  label: props.t("sessionTitle"),
1944
1924
  onChange: persistTitleEnabled
1945
1925
  }),
@@ -1956,7 +1936,7 @@ window.__ModuleLoader__.load({
1956
1936
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1957
1937
  label: props.t("provider"),
1958
1938
  value: titleDraft.provider ?? "",
1959
- disabled: busy === "sessionTitle" || !sessionTitle.writable,
1939
+ disabled: busy !== void 0 || !sessionTitle.writable,
1960
1940
  choices: titleChoices.providers,
1961
1941
  onChange: (provider) => {
1962
1942
  const first = groups.find((group) => group.id === provider)?.models[0];
@@ -1968,7 +1948,7 @@ window.__ModuleLoader__.load({
1968
1948
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
1969
1949
  label: props.t("model"),
1970
1950
  value: titleDraft.model ?? "",
1971
- disabled: busy === "sessionTitle" || !sessionTitle.writable,
1951
+ disabled: busy !== void 0 || !sessionTitle.writable,
1972
1952
  choices: titleChoices.models,
1973
1953
  onChange: (model) => {
1974
1954
  setTitleDraft({
@@ -2027,6 +2007,104 @@ window.__ModuleLoader__.load({
2027
2007
  className: ModelSwitchSettings_module_css_default.hint,
2028
2008
  children: props.t("providerGroupPick")
2029
2009
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
2010
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2011
+ className: ModelSwitchSettings_module_css_default.rowCard,
2012
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2013
+ className: ModelSwitchSettings_module_css_default.rowCopy,
2014
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2015
+ className: ModelSwitchSettings_module_css_default.rowTitle,
2016
+ children: props.t("compaction")
2017
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2018
+ className: ModelSwitchSettings_module_css_default.hint,
2019
+ children: groupCompaction === void 0 ? props.t("compactionFollow") : routeName(groups, groupCompaction)
2020
+ })]
2021
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
2022
+ checked: groupCompaction !== void 0,
2023
+ disabled: groupControlsDisabled || groupCompaction === void 0 && catalogSeed === void 0 && !compactionOn,
2024
+ label: props.t("compaction"),
2025
+ onChange: (value) => {
2026
+ if (value) enableGroupCapability("compaction");
2027
+ else disableGroupCapability("compaction");
2028
+ }
2029
+ })]
2030
+ }),
2031
+ groupCompaction === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2032
+ className: ModelSwitchSettings_module_css_default.formGrid,
2033
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
2034
+ label: props.t("provider"),
2035
+ value: groupCompaction.provider,
2036
+ disabled: groupControlsDisabled,
2037
+ choices: groupCompactionChoices.providers,
2038
+ onChange: (provider) => {
2039
+ const first = groups.find((item) => item.id === provider)?.models[0];
2040
+ setGroupDraft((draft) => draft === void 0 ? draft : {
2041
+ ...draft,
2042
+ compactProvider: provider,
2043
+ compactModel: first?.id ?? ""
2044
+ });
2045
+ }
2046
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
2047
+ label: props.t("model"),
2048
+ value: groupCompaction.model,
2049
+ disabled: groupControlsDisabled,
2050
+ choices: groupCompactionChoices.models,
2051
+ onChange: (model) => {
2052
+ setGroupDraft((draft) => draft === void 0 ? draft : {
2053
+ ...draft,
2054
+ compactModel: model
2055
+ });
2056
+ }
2057
+ })]
2058
+ }),
2059
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2060
+ className: ModelSwitchSettings_module_css_default.rowCard,
2061
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2062
+ className: ModelSwitchSettings_module_css_default.rowCopy,
2063
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2064
+ className: ModelSwitchSettings_module_css_default.rowTitle,
2065
+ children: props.t("autoReview")
2066
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
2067
+ className: ModelSwitchSettings_module_css_default.hint,
2068
+ children: groupAutoReview === void 0 ? props.t("autoReviewFollow") : routeName(groups, groupAutoReview)
2069
+ })]
2070
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
2071
+ checked: groupAutoReview !== void 0,
2072
+ disabled: groupControlsDisabled || groupAutoReview === void 0 && catalogSeed === void 0 && !autoReviewOn,
2073
+ label: props.t("autoReview"),
2074
+ onChange: (value) => {
2075
+ if (value) enableGroupCapability("autoReview");
2076
+ else disableGroupCapability("autoReview");
2077
+ }
2078
+ })]
2079
+ }),
2080
+ groupAutoReview === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2081
+ className: ModelSwitchSettings_module_css_default.formGrid,
2082
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
2083
+ label: props.t("provider"),
2084
+ value: groupAutoReview.provider,
2085
+ disabled: groupControlsDisabled,
2086
+ choices: groupAutoReviewChoices.providers,
2087
+ onChange: (provider) => {
2088
+ const first = groups.find((item) => item.id === provider)?.models[0];
2089
+ setGroupDraft((draft) => draft === void 0 ? draft : {
2090
+ ...draft,
2091
+ autoReviewProvider: provider,
2092
+ autoReviewModel: first?.id ?? ""
2093
+ });
2094
+ }
2095
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
2096
+ label: props.t("model"),
2097
+ value: groupAutoReview.model,
2098
+ disabled: groupControlsDisabled,
2099
+ choices: groupAutoReviewChoices.models,
2100
+ onChange: (model) => {
2101
+ setGroupDraft((draft) => draft === void 0 ? draft : {
2102
+ ...draft,
2103
+ autoReviewModel: model
2104
+ });
2105
+ }
2106
+ })]
2107
+ }),
2030
2108
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2031
2109
  className: ModelSwitchSettings_module_css_default.rowCard,
2032
2110
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -2040,7 +2118,7 @@ window.__ModuleLoader__.load({
2040
2118
  })]
2041
2119
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
2042
2120
  checked: groupTitle !== void 0,
2043
- disabled: groupControlsDisabled,
2121
+ disabled: groupControlsDisabled || groupTitle === void 0 && catalogSeed === void 0 && !titleOn,
2044
2122
  label: props.t("sessionTitle"),
2045
2123
  onChange: (value) => {
2046
2124
  if (value) enableGroupCapability("sessionTitle");
@@ -2089,7 +2167,7 @@ window.__ModuleLoader__.load({
2089
2167
  })]
2090
2168
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
2091
2169
  checked: groupSubagent !== void 0,
2092
- disabled: groupControlsDisabled,
2170
+ disabled: groupControlsDisabled || groupSubagent === void 0 && catalogSeed === void 0 && !(subagent.value?.mode === "fixed" && routePairConfigured(subagent.value)),
2093
2171
  label: props.t("subagent"),
2094
2172
  onChange: (value) => {
2095
2173
  if (value) enableGroupCapability("subagent");
@@ -2178,12 +2256,9 @@ window.__ModuleLoader__.load({
2178
2256
  subtitle: "为子代理与能力路由设置固定模型;当前会话模型用输入框旁的选择器直接切换。",
2179
2257
  conversationRoutes: "对话路由",
2180
2258
  capabilityRoutes: "能力路由",
2181
- sendProtection: "发送保护",
2182
2259
  settingsSynced: "设置已同步",
2183
2260
  remoteSettingsUnavailable: "官方 DSH 在非本机浏览器禁用配置读取与写入;请在 Host 的 127.0.0.1 页面打开设置。",
2184
2261
  cancel: "取消",
2185
- compactOnSwitch: "切换时压缩",
2186
- compactOnSwitchHelp: "发送且换了模型时,先按目标窗口检查上下文;超了就用切换前的模型压一次历史,再继续请求。只选模型、不发送,不会压缩。官方自动压缩和 /compact 不受影响。压缩会产生额外模型用量,摘要可能省略细节。",
2187
2262
  provider: "提供商",
2188
2263
  model: "模型",
2189
2264
  providerDefault: "使用提供商默认值",
@@ -2220,8 +2295,8 @@ window.__ModuleLoader__.load({
2220
2295
  providerGroups: "供应商配置组",
2221
2296
  providerGroupsCard: "按供应商预设路由",
2222
2297
  providerGroupsSummary: "使用该供应商的会话自动采用对应组",
2223
- providerGroupsHelp: "为会话命名与子代理按供应商分别保存路由。",
2224
- providerGroupsBodyHelp: "每个供应商保存一组会话命名与子代理路由。会话自身的供应商与该组一致时,该会话的命名与子代理按组解析,否则回落到上方卡片里的全局设置。切换某个会话的模型不会写入任何全局设置,因此不同供应商的多个会话互不影响。压缩与自动审查仍是全局卡片:压缩事件要如实记录实际写入摘要的模型,审查的拦截点没有会话身份。",
2298
+ providerGroupsHelp: "为压缩、自动审查、会话命名与子代理按供应商分别保存路由。",
2299
+ providerGroupsBodyHelp: "每个供应商保存一组压缩、自动审查、会话命名与子代理路由。会话自身的供应商与该组一致时,这四项能力按组实时解析,未固定项回落到上方卡片里的全局设置。切换某个会话的模型不会写入任何全局设置,因此不同供应商的多个会话并行时互不影响,会话中切换供应商后下一次调用也会立即跟随新供应商的配置组。",
2225
2300
  providerGroupPick: "选择供应商以编辑其配置组。",
2226
2301
  providerGroupDelete: "删除该组",
2227
2302
  saveTimeout: "保存超时,请重试",
@@ -2238,12 +2313,9 @@ window.__ModuleLoader__.load({
2238
2313
  subtitle: "Pin routes for Subagents and capability tools; switch the active session model through the composer picker.",
2239
2314
  conversationRoutes: "Conversation routes",
2240
2315
  capabilityRoutes: "Capability routes",
2241
- sendProtection: "Send protection",
2242
2316
  settingsSynced: "Settings synced",
2243
2317
  remoteSettingsUnavailable: "Official DSH disables settings reads and writes in non-loopback browsers. Open Settings on the Host loopback URL to edit model routes.",
2244
2318
  cancel: "Cancel",
2245
- compactOnSwitch: "Compact on switch",
2246
- compactOnSwitchHelp: "When a sent message uses a new model, check the target window first. If needed, compact history with the previous model, then continue. Choosing a model does not compact. Official automatic compaction and /compact are unchanged. Compaction uses extra model quota and may omit details.",
2247
2319
  provider: "Provider",
2248
2320
  model: "Model",
2249
2321
  providerDefault: "Provider default",
@@ -2280,8 +2352,8 @@ window.__ModuleLoader__.load({
2280
2352
  providerGroups: "Provider groups",
2281
2353
  providerGroupsCard: "Per-provider route presets",
2282
2354
  providerGroupsSummary: "Sessions using that provider adopt this group",
2283
- providerGroupsHelp: "Save session-naming and subagent routes per provider.",
2284
- providerGroupsBodyHelp: "Each provider stores one session-naming and subagent route group. A session running on that provider resolves its naming and subagent calls from the group, and falls back to the cards above otherwise. Switching a session’s model writes no global setting, so sessions on different providers never affect each other. Compaction and auto review stay global cards: the compaction event must record the model that actually wrote each summary, and the reviewer gate carries no session identity.",
2355
+ providerGroupsHelp: "Save compaction, auto-review, session-naming, and subagent routes per provider.",
2356
+ providerGroupsBodyHelp: "Each provider stores one route group for compaction, auto review, session naming, and subagents. A session running on that provider resolves all four capabilities from the group and falls back to the cards above when a row is off. Switching a session’s model writes no global setting, so parallel sessions on different providers never affect each other, and mid-session provider switches take effect on the very next call.",
2285
2357
  providerGroupPick: "Pick a provider to edit its group.",
2286
2358
  providerGroupDelete: "Delete group",
2287
2359
  saveTimeout: "The save timed out; try again",
@@ -4780,9 +4852,6 @@ window.__ModuleLoader__.load({
4780
4852
  saveCapability: (route, ops) => {
4781
4853
  return (route === "search" ? search : route === "image" ? image : route === "compaction" ? compaction : route === "title" ? sessionTitle : autoReview).mutate(ops);
4782
4854
  },
4783
- setCompactOnSwitch: async (value) => {
4784
- if (!await owned.set("compactOnSwitch", value)) throw new Error("settings-rejected");
4785
- },
4786
4855
  saveProviderGroup: async (provider, group) => {
4787
4856
  const current = owned.getSnapshot().value?.providerGroups;
4788
4857
  const next = withProviderGroup(current, provider, group);