dsh-model-switch 0.4.22 → 0.4.23
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/CHANGELOG.md +9 -0
- package/PRODUCT.md +4 -4
- package/README.md +14 -13
- package/README.zh.md +14 -13
- package/lib/client.js +262 -554
- package/lib/index.js +3 -21
- package/lib/types/client/ModelSwitchSettings.d.ts +1 -3
- package/lib/types/client/locales.d.ts +4 -8
- package/lib/types/client/main-row-controller.d.ts +16 -35
- package/lib/types/client/picker/ComposerPicker.d.ts +1 -3
- package/lib/types/client/picker/PlanReviewCard.d.ts +0 -6
- package/lib/types/client/picker/locales.d.ts +0 -8
- package/lib/types/client/picker/provider-group-watch.d.ts +48 -0
- package/lib/types/client-contract.d.ts +0 -9
- package/lib/types/host-runtime.d.ts +1 -11
- package/lib/types/index.d.ts +1 -1
- package/lib/types/picker/plan-review.d.ts +2 -10
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -10,11 +10,7 @@ window.__ModuleLoader__.load({
|
|
|
10
10
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
11
11
|
//#region src/client-contract.ts
|
|
12
12
|
const MODEL_SWITCH_CONFIG_ID = "model-switch";
|
|
13
|
-
const MAIN_DEFAULT_CONFIG_ID = "agent-default-model";
|
|
14
13
|
const PROVIDERS_CONFIG_ID = "llm-providers-ui";
|
|
15
|
-
var MainSettingsConflictError = class extends Error {
|
|
16
|
-
name = "MainSettingsConflictError";
|
|
17
|
-
};
|
|
18
14
|
const SUBAGENT_SETTINGS_FIELDS = Object.freeze({
|
|
19
15
|
mode: "subagentMode",
|
|
20
16
|
provider: "subagentProvider",
|
|
@@ -604,65 +600,20 @@ window.__ModuleLoader__.load({
|
|
|
604
600
|
models
|
|
605
601
|
};
|
|
606
602
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
}
|
|
610
|
-
function acceptedRevisionAfterFailure(accepted, error) {
|
|
611
|
-
return error instanceof MainSettingsConflictError ? void 0 : accepted;
|
|
612
|
-
}
|
|
613
|
-
function deriveMainChoices(groups, draft) {
|
|
614
|
-
const providers = groups.map((group) => ({
|
|
615
|
-
id: group.id,
|
|
616
|
-
name: group.name
|
|
617
|
-
}));
|
|
618
|
-
if (draft !== void 0 && !providers.some((option) => option.id === draft.provider)) providers.push({
|
|
619
|
-
id: draft.provider,
|
|
620
|
-
name: draft.provider
|
|
621
|
-
});
|
|
622
|
-
const group = groups.find((item) => item.id === draft?.provider);
|
|
623
|
-
const models = (group?.models ?? []).map((model) => ({
|
|
624
|
-
id: model.id,
|
|
625
|
-
name: model.name
|
|
626
|
-
}));
|
|
627
|
-
if (draft !== void 0 && !models.some((option) => option.id === draft.model)) models.push({
|
|
628
|
-
id: draft.model,
|
|
629
|
-
name: draft.model
|
|
630
|
-
});
|
|
631
|
-
const efforts = ((group?.models.find((item) => item.id === draft?.model))?.reasoning?.efforts ?? []).map((effort) => ({
|
|
632
|
-
id: effort.id,
|
|
633
|
-
name: effort.name
|
|
634
|
-
}));
|
|
635
|
-
if (draft?.reasoningEffort !== void 0 && !efforts.some((option) => option.id === draft.reasoningEffort)) efforts.push({
|
|
636
|
-
id: draft.reasoningEffort,
|
|
637
|
-
name: draft.reasoningEffort
|
|
638
|
-
});
|
|
639
|
-
return {
|
|
640
|
-
providers,
|
|
641
|
-
models,
|
|
642
|
-
efforts
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
function useModelSwitchSettingsController(input) {
|
|
646
|
-
const main = input.useMainSettings((value) => value);
|
|
647
|
-
const subagent = input.useSubagentSettings((value) => value);
|
|
648
|
-
const [draft, setDraft] = (0, react.useState)(main.value);
|
|
603
|
+
/** Load the model catalog backing every route editor, reloading on provider-order changes. */
|
|
604
|
+
function useSettingsCatalog(input) {
|
|
649
605
|
const [groups, setGroups] = (0, react.useState)([]);
|
|
650
|
-
const [
|
|
651
|
-
const [message, setMessage] = (0, react.useState)();
|
|
652
|
-
const locked = (0, react.useRef)(false);
|
|
653
|
-
const acceptedRevision = (0, react.useRef)();
|
|
654
|
-
(0, react.useEffect)(() => {
|
|
655
|
-
setDraft(main.value);
|
|
656
|
-
setMessage(void 0);
|
|
657
|
-
if (main.revision !== void 0 && (acceptedRevision.current === void 0 || main.revision > acceptedRevision.current)) acceptedRevision.current = main.revision;
|
|
658
|
-
}, [main.revision, main.value]);
|
|
606
|
+
const [catalogError, setCatalogError] = (0, react.useState)();
|
|
659
607
|
(0, react.useEffect)(() => {
|
|
660
608
|
let live = true;
|
|
661
609
|
const load = () => {
|
|
662
610
|
input.loadCatalog().then((value) => {
|
|
663
|
-
if (live)
|
|
611
|
+
if (live) {
|
|
612
|
+
setGroups(value);
|
|
613
|
+
setCatalogError(void 0);
|
|
614
|
+
}
|
|
664
615
|
}).catch(() => {
|
|
665
|
-
if (live)
|
|
616
|
+
if (live) setCatalogError(input.t("catalogFailed"));
|
|
666
617
|
});
|
|
667
618
|
};
|
|
668
619
|
load();
|
|
@@ -678,63 +629,9 @@ window.__ModuleLoader__.load({
|
|
|
678
629
|
input.subscribeProviderOrder,
|
|
679
630
|
input.t
|
|
680
631
|
]);
|
|
681
|
-
const { providers, models, efforts } = deriveMainChoices(groups, draft);
|
|
682
|
-
const disabled = main.status !== "ready" || !main.writable || draft === void 0 || busy || draft.provider.trim() === "" || draft.model.trim() === "";
|
|
683
|
-
const setProvider = (provider) => setDraft((current) => {
|
|
684
|
-
if (current === void 0) return current;
|
|
685
|
-
const first = groups.find((item) => item.id === provider)?.models[0];
|
|
686
|
-
return selectRouteModel(groups, provider, first?.id ?? current.model);
|
|
687
|
-
});
|
|
688
|
-
const setModel = (id) => setDraft((current) => current === void 0 ? current : selectRouteModel(groups, current.provider, id));
|
|
689
|
-
const setReasoningEffort = (value) => setDraft((current) => {
|
|
690
|
-
if (current === void 0) return current;
|
|
691
|
-
const next = { ...current };
|
|
692
|
-
if (value === "") delete next.reasoningEffort;
|
|
693
|
-
else next.reasoningEffort = value;
|
|
694
|
-
return next;
|
|
695
|
-
});
|
|
696
|
-
const reset = () => {
|
|
697
|
-
setDraft(main.value);
|
|
698
|
-
setMessage(void 0);
|
|
699
|
-
};
|
|
700
|
-
const save = async () => {
|
|
701
|
-
if (disabled || locked.current || draft === void 0) return;
|
|
702
|
-
const mirrorRevision = main.revision;
|
|
703
|
-
if (mirrorRevision === void 0) {
|
|
704
|
-
setMessage(input.t("requestFailed"));
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
707
|
-
const expectedRevision = expectedMainRevision(mirrorRevision, acceptedRevision.current);
|
|
708
|
-
locked.current = true;
|
|
709
|
-
setBusy(true);
|
|
710
|
-
setMessage(void 0);
|
|
711
|
-
try {
|
|
712
|
-
acceptedRevision.current = await input.saveMain(draft, expectedRevision);
|
|
713
|
-
setMessage(input.t("saved"));
|
|
714
|
-
} catch (error) {
|
|
715
|
-
acceptedRevision.current = acceptedRevisionAfterFailure(acceptedRevision.current, error);
|
|
716
|
-
setMessage(error instanceof Error ? error.message : input.t("requestFailed"));
|
|
717
|
-
} finally {
|
|
718
|
-
locked.current = false;
|
|
719
|
-
setBusy(false);
|
|
720
|
-
}
|
|
721
|
-
};
|
|
722
632
|
return {
|
|
723
|
-
main,
|
|
724
|
-
subagent,
|
|
725
|
-
draft,
|
|
726
633
|
groups,
|
|
727
|
-
|
|
728
|
-
models,
|
|
729
|
-
efforts,
|
|
730
|
-
busy,
|
|
731
|
-
message,
|
|
732
|
-
disabled,
|
|
733
|
-
setProvider,
|
|
734
|
-
setModel,
|
|
735
|
-
setReasoningEffort,
|
|
736
|
-
reset,
|
|
737
|
-
save
|
|
634
|
+
catalogError
|
|
738
635
|
};
|
|
739
636
|
}
|
|
740
637
|
//#endregion
|
|
@@ -843,18 +740,6 @@ window.__ModuleLoader__.load({
|
|
|
843
740
|
strokeLinejoin: "round"
|
|
844
741
|
})
|
|
845
742
|
});
|
|
846
|
-
if (kind === "main") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
847
|
-
width: "18",
|
|
848
|
-
height: "18",
|
|
849
|
-
viewBox: "0 0 18 18",
|
|
850
|
-
fill: "none",
|
|
851
|
-
"aria-hidden": "true",
|
|
852
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
853
|
-
d: "M3 4.5h12v8H8l-3.5 2v-2H3v-8Z",
|
|
854
|
-
stroke: "currentColor",
|
|
855
|
-
strokeLinejoin: "round"
|
|
856
|
-
})
|
|
857
|
-
});
|
|
858
743
|
if (kind === "subagent") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
859
744
|
width: "18",
|
|
860
745
|
height: "18",
|
|
@@ -1091,6 +976,22 @@ window.__ModuleLoader__.load({
|
|
|
1091
976
|
const matched = matchCatalogModel(groups.find((group) => group.id === route.provider)?.models ?? [], route.model);
|
|
1092
977
|
return matched?.effort ?? matched?.model.reasoning?.defaultEffort;
|
|
1093
978
|
}
|
|
979
|
+
/**
|
|
980
|
+
* The effort caption for the subagent summary: a stored effort is the pinned
|
|
981
|
+
* one ("已固定 · High"); an unset effort follows the provider default and says
|
|
982
|
+
* so ("Provider 默认 · High"), using the same effort-name lookup the picker does.
|
|
983
|
+
*/
|
|
984
|
+
function subagentEffortSegment(t, matched, pinned, defaultEffort) {
|
|
985
|
+
const effortName = (id) => matched?.model.reasoning?.efforts.find((level) => level.id === id)?.name ?? id;
|
|
986
|
+
if (pinned !== "") return compact(t("effortPinned"), effortName(pinned));
|
|
987
|
+
return defaultEffort === void 0 ? t("providerDefaultShort") : compact(t("providerDefaultShort"), effortName(defaultEffort));
|
|
988
|
+
}
|
|
989
|
+
/** The catalog head route; capability switches pin here when turned On. */
|
|
990
|
+
function firstCatalogRoute(groups) {
|
|
991
|
+
const provider = groups[0];
|
|
992
|
+
const model = provider?.models[0];
|
|
993
|
+
return provider === void 0 || model === void 0 ? void 0 : selectRouteModel(groups, provider.id, model.id);
|
|
994
|
+
}
|
|
1094
995
|
function capabilityChoices(groups, route, providers, kind) {
|
|
1095
996
|
const choices = deriveRouteChoices(groups, route, providers);
|
|
1096
997
|
if (kind === "image" && route?.provider === "grok") {
|
|
@@ -1111,8 +1012,8 @@ window.__ModuleLoader__.load({
|
|
|
1111
1012
|
return choices;
|
|
1112
1013
|
}
|
|
1113
1014
|
function ModelSwitchSettings(props) {
|
|
1114
|
-
const
|
|
1115
|
-
const
|
|
1015
|
+
const { groups, catalogError } = useSettingsCatalog(props);
|
|
1016
|
+
const subagent = props.useSubagentSettings((value) => value);
|
|
1116
1017
|
const search = props.useSearchSettings((value) => value);
|
|
1117
1018
|
const image = props.useImageSettings((value) => value);
|
|
1118
1019
|
const autoReview = props.useAutoReviewSettings((value) => value);
|
|
@@ -1169,7 +1070,7 @@ window.__ModuleLoader__.load({
|
|
|
1169
1070
|
};
|
|
1170
1071
|
}, [loadSearchCapabilities]);
|
|
1171
1072
|
const providerGroups = normalizeProviderGroups(switchSettings.value?.providerGroups);
|
|
1172
|
-
/** Seed an unsaved group from the live routes
|
|
1073
|
+
/** Seed an unsaved group from the live routes. */
|
|
1173
1074
|
const seedGroupFromActive = () => {
|
|
1174
1075
|
const compact = compaction.value !== void 0 && routePairConfigured(compaction.value) ? {
|
|
1175
1076
|
provider: compaction.value.provider ?? "",
|
|
@@ -1208,11 +1109,16 @@ window.__ModuleLoader__.load({
|
|
|
1208
1109
|
}
|
|
1209
1110
|
setGroupDraft(groupOf(providerGroups, groupProvider) ?? seedGroupFromActive());
|
|
1210
1111
|
};
|
|
1112
|
+
/** Open the editor on the provider in use: the first configured group, else the catalog head. */
|
|
1211
1113
|
(0, react.useEffect)(() => {
|
|
1212
1114
|
if (groupProvider !== "") return;
|
|
1213
|
-
const initial =
|
|
1115
|
+
const initial = Object.keys(providerGroups)[0] ?? groups[0]?.id;
|
|
1214
1116
|
if (initial !== void 0 && initial.trim() !== "") setGroupProvider(initial);
|
|
1215
|
-
}, [
|
|
1117
|
+
}, [
|
|
1118
|
+
groupProvider,
|
|
1119
|
+
providerGroups,
|
|
1120
|
+
groups
|
|
1121
|
+
]);
|
|
1216
1122
|
(0, react.useEffect)(() => {
|
|
1217
1123
|
resyncGroupDraft();
|
|
1218
1124
|
}, [
|
|
@@ -1223,8 +1129,7 @@ window.__ModuleLoader__.load({
|
|
|
1223
1129
|
compaction.value
|
|
1224
1130
|
]);
|
|
1225
1131
|
if ([
|
|
1226
|
-
|
|
1227
|
-
controller.subagent,
|
|
1132
|
+
subagent,
|
|
1228
1133
|
search,
|
|
1229
1134
|
image,
|
|
1230
1135
|
switchSettings
|
|
@@ -1267,8 +1172,7 @@ window.__ModuleLoader__.load({
|
|
|
1267
1172
|
});
|
|
1268
1173
|
return efforts;
|
|
1269
1174
|
})();
|
|
1270
|
-
const defaultEffort =
|
|
1271
|
-
const mainEffectiveEffort = draft?.reasoningEffort ?? routeDefaultEffort(groups, draft);
|
|
1175
|
+
const defaultEffort = routeDefaultEffort(groups, subagentRoute);
|
|
1272
1176
|
const searchLive = searchSnapshot?.capabilities;
|
|
1273
1177
|
const searchGroups = searchGroupsFromCapabilities(searchLive);
|
|
1274
1178
|
const searchChoices = capabilityChoices(searchGroups, searchDraft, void 0, "search");
|
|
@@ -1279,7 +1183,6 @@ window.__ModuleLoader__.load({
|
|
|
1279
1183
|
const compactionChoices = deriveRouteChoices(groups, compactionDraft);
|
|
1280
1184
|
const compactionOn = routePairConfigured(compaction.value);
|
|
1281
1185
|
const synced = [
|
|
1282
|
-
main,
|
|
1283
1186
|
subagent,
|
|
1284
1187
|
search,
|
|
1285
1188
|
image,
|
|
@@ -1359,8 +1262,9 @@ window.__ModuleLoader__.load({
|
|
|
1359
1262
|
};
|
|
1360
1263
|
/**
|
|
1361
1264
|
* The reviewer switch: Off unsets both stored fields (follow the session
|
|
1362
|
-
* model); On
|
|
1363
|
-
* and stays editable below. A failed write reports, never
|
|
1265
|
+
* model); On pins the route to the catalog head so the pin takes effect
|
|
1266
|
+
* immediately and stays editable below. A failed write reports, never
|
|
1267
|
+
* flips silently.
|
|
1364
1268
|
*/
|
|
1365
1269
|
const persistAutoReviewEnabled = (enabled) => {
|
|
1366
1270
|
if (busy === "autoReview" || !autoReview.writable || autoReview.value === void 0) return;
|
|
@@ -1372,8 +1276,8 @@ window.__ModuleLoader__.load({
|
|
|
1372
1276
|
});
|
|
1373
1277
|
return;
|
|
1374
1278
|
}
|
|
1375
|
-
const seed =
|
|
1376
|
-
if (seed === void 0
|
|
1279
|
+
const seed = firstCatalogRoute(groups);
|
|
1280
|
+
if (seed === void 0) return;
|
|
1377
1281
|
setBusy("autoReview");
|
|
1378
1282
|
setMessage(void 0);
|
|
1379
1283
|
(async () => {
|
|
@@ -1396,9 +1300,9 @@ window.__ModuleLoader__.load({
|
|
|
1396
1300
|
};
|
|
1397
1301
|
/**
|
|
1398
1302
|
* The compaction switch: Off unsets both stored fields (official default =
|
|
1399
|
-
* the conversation target); On
|
|
1400
|
-
* effect immediately and stays editable below. A failed write
|
|
1401
|
-
* never flips silently.
|
|
1303
|
+
* the conversation target); On pins the route to the catalog head so the
|
|
1304
|
+
* pin takes effect immediately and stays editable below. A failed write
|
|
1305
|
+
* reports, never flips silently.
|
|
1402
1306
|
*/
|
|
1403
1307
|
const persistCompactionEnabled = (enabled) => {
|
|
1404
1308
|
if (busy === "compaction" || !compaction.writable || compaction.value === void 0) return;
|
|
@@ -1410,8 +1314,8 @@ window.__ModuleLoader__.load({
|
|
|
1410
1314
|
});
|
|
1411
1315
|
return;
|
|
1412
1316
|
}
|
|
1413
|
-
const seed =
|
|
1414
|
-
if (seed === void 0
|
|
1317
|
+
const seed = firstCatalogRoute(groups);
|
|
1318
|
+
if (seed === void 0) return;
|
|
1415
1319
|
setBusy("compaction");
|
|
1416
1320
|
setMessage(void 0);
|
|
1417
1321
|
(async () => {
|
|
@@ -1432,10 +1336,9 @@ window.__ModuleLoader__.load({
|
|
|
1432
1336
|
});
|
|
1433
1337
|
});
|
|
1434
1338
|
};
|
|
1435
|
-
const mainSummary = draft === void 0 ? props.t("loading") : compact(routeName(groups, draft), mainEffectiveEffort);
|
|
1436
1339
|
const subagentRole = subagentDraft?.mode === "fixed" && subagentDraft.provider !== void 0 && subagentDraft.provider !== "" ? props.providerRoleOf?.(subagentDraft.provider) : void 0;
|
|
1437
1340
|
const subagentOn = subagentDraft?.mode === "fixed";
|
|
1438
|
-
const subagentSummary = subagentDraft === void 0 ? props.t("loading") : subagentOn ? compact(routeName(groups, subagentRoute), isAgentRole(subagentRole) ? props.t("agentBadge") : void 0,
|
|
1341
|
+
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");
|
|
1439
1342
|
const subagentDisabled = subagent.status !== "ready" || !subagent.writable || subagentDraft === void 0 || busy === "subagent" || !subagentOn || (subagentDraft.provider ?? "").trim() === "" || (subagentDraft.model ?? "").trim() === "";
|
|
1440
1343
|
const capabilityDisabled = (route, snapshot, next) => snapshot.status !== "ready" || !snapshot.writable || next === void 0 || busy === route || (next.provider ?? "").trim() === "" || (next.model ?? "").trim() === "";
|
|
1441
1344
|
const autoReviewDisabled = autoReview.status !== "ready" || !autoReview.writable || autoReviewDraft === void 0 || busy === "autoReview" || !autoReviewOn || (autoReviewDraft.provider ?? "").trim() === "" || (autoReviewDraft.model ?? "").trim() === "";
|
|
@@ -1465,26 +1368,45 @@ window.__ModuleLoader__.load({
|
|
|
1465
1368
|
});
|
|
1466
1369
|
return efforts;
|
|
1467
1370
|
})();
|
|
1468
|
-
/**
|
|
1371
|
+
/**
|
|
1372
|
+
* Enable a capability row by pinning it: to that capability's live route
|
|
1373
|
+
* when one is configured, else to the catalog head (same sources the
|
|
1374
|
+
* editor's first-edit seed uses).
|
|
1375
|
+
*/
|
|
1469
1376
|
const enableGroupCapability = (kind) => {
|
|
1377
|
+
const active = seedGroupFromActive();
|
|
1378
|
+
const seed = firstCatalogRoute(groups);
|
|
1470
1379
|
setGroupDraft((draft) => {
|
|
1471
|
-
|
|
1472
|
-
if (
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1380
|
+
if (draft === void 0) return draft;
|
|
1381
|
+
if (kind === "compaction") {
|
|
1382
|
+
const provider = active.compactProvider ?? seed?.provider;
|
|
1383
|
+
const model = active.compactModel ?? seed?.model;
|
|
1384
|
+
if (provider === void 0 || model === void 0) return draft;
|
|
1385
|
+
return {
|
|
1386
|
+
...draft,
|
|
1387
|
+
compactProvider: provider,
|
|
1388
|
+
compactModel: model
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
1391
|
+
if (kind === "autoReview") {
|
|
1392
|
+
const provider = active.autoReviewProvider ?? seed?.provider;
|
|
1393
|
+
const model = active.autoReviewModel ?? seed?.model;
|
|
1394
|
+
if (provider === void 0 || model === void 0) return draft;
|
|
1395
|
+
return {
|
|
1396
|
+
...draft,
|
|
1397
|
+
autoReviewProvider: provider,
|
|
1398
|
+
autoReviewModel: model
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
const provider = active.subagentProvider ?? seed?.provider;
|
|
1402
|
+
const model = active.subagentModel ?? seed?.model;
|
|
1403
|
+
if (provider === void 0 || model === void 0) return draft;
|
|
1404
|
+
const effort = active.subagentProvider !== void 0 ? active.subagentReasoningEffort : seed?.reasoningEffort;
|
|
1483
1405
|
return {
|
|
1484
1406
|
...draft,
|
|
1485
|
-
subagentProvider:
|
|
1486
|
-
subagentModel:
|
|
1487
|
-
...
|
|
1407
|
+
subagentProvider: provider,
|
|
1408
|
+
subagentModel: model,
|
|
1409
|
+
...effort === void 0 || effort === "" ? {} : { subagentReasoningEffort: effort }
|
|
1488
1410
|
};
|
|
1489
1411
|
});
|
|
1490
1412
|
};
|
|
@@ -1532,6 +1454,10 @@ window.__ModuleLoader__.load({
|
|
|
1532
1454
|
className: ModelSwitchSettings_module_css_default.saved,
|
|
1533
1455
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", { className: ModelSwitchSettings_module_css_default.savedDot }), synced ? props.t("settingsSynced") : props.t("loading")]
|
|
1534
1456
|
}),
|
|
1457
|
+
catalogError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1458
|
+
className: cx(ModelSwitchSettings_module_css_default.hint, ModelSwitchSettings_module_css_default.message),
|
|
1459
|
+
children: catalogError
|
|
1460
|
+
}),
|
|
1535
1461
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1536
1462
|
className: ModelSwitchSettings_module_css_default.group,
|
|
1537
1463
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
@@ -1597,150 +1523,92 @@ window.__ModuleLoader__.load({
|
|
|
1597
1523
|
}),
|
|
1598
1524
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1599
1525
|
className: ModelSwitchSettings_module_css_default.group,
|
|
1600
|
-
children: [
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1526
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
1527
|
+
className: ModelSwitchSettings_module_css_default.groupLabel,
|
|
1528
|
+
children: props.t("conversationRoutes")
|
|
1529
|
+
}), props.capabilities.centralSubagentRouting.available ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteCard, {
|
|
1530
|
+
title: props.t("subagent"),
|
|
1531
|
+
summary: subagentSummary,
|
|
1532
|
+
hint: props.t("subagentHelp"),
|
|
1533
|
+
icon: "subagent",
|
|
1534
|
+
open: open === "subagent" && subagentOn,
|
|
1535
|
+
onToggle: () => {
|
|
1536
|
+
if (subagentOn) toggle("subagent");
|
|
1537
|
+
},
|
|
1538
|
+
expandable: subagentOn,
|
|
1539
|
+
trailing: subagentDraft === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
1540
|
+
checked: subagentOn,
|
|
1541
|
+
disabled: busy === "subagent" || !subagent.writable,
|
|
1542
|
+
label: props.t("subagent"),
|
|
1543
|
+
onChange: persistSubagentEnabled
|
|
1604
1544
|
}),
|
|
1605
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
value: draft.provider,
|
|
1624
|
-
disabled: controller.busy || !main.writable,
|
|
1625
|
-
choices: controller.providers,
|
|
1626
|
-
onChange: controller.setProvider
|
|
1627
|
-
}),
|
|
1628
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1629
|
-
label: props.t("model"),
|
|
1630
|
-
value: draft.model,
|
|
1631
|
-
disabled: controller.busy || !main.writable,
|
|
1632
|
-
choices: controller.models,
|
|
1633
|
-
onChange: controller.setModel
|
|
1634
|
-
}),
|
|
1635
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1636
|
-
label: props.t("effort"),
|
|
1637
|
-
value: draft.reasoningEffort ?? "",
|
|
1638
|
-
disabled: controller.busy || !main.writable,
|
|
1639
|
-
choices: controller.efforts,
|
|
1640
|
-
onChange: controller.setReasoningEffort
|
|
1641
|
-
})
|
|
1642
|
-
]
|
|
1545
|
+
children: subagentDraft === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1546
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
1547
|
+
children: props.t("loading")
|
|
1548
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1549
|
+
className: ModelSwitchSettings_module_css_default.formGrid,
|
|
1550
|
+
children: [
|
|
1551
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1552
|
+
label: props.t("provider"),
|
|
1553
|
+
value: subagentDraft.provider ?? "",
|
|
1554
|
+
disabled: busy === "subagent" || !subagent.writable,
|
|
1555
|
+
choices: subagentChoices.providers,
|
|
1556
|
+
onChange: (provider) => {
|
|
1557
|
+
const first = groups.find((group) => group.id === provider)?.models[0];
|
|
1558
|
+
setSubagentDraft({
|
|
1559
|
+
mode: "fixed",
|
|
1560
|
+
...selectRouteModel(groups, provider, first?.id ?? subagentDraft.model ?? "")
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1643
1563
|
}),
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1564
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1565
|
+
label: props.t("model"),
|
|
1566
|
+
value: subagentCatalogModel?.model.id ?? subagentDraft.model ?? "",
|
|
1567
|
+
disabled: busy === "subagent" || !subagent.writable,
|
|
1568
|
+
choices: subagentChoices.models,
|
|
1569
|
+
onChange: (model) => {
|
|
1570
|
+
setSubagentDraft({
|
|
1571
|
+
mode: "fixed",
|
|
1572
|
+
...selectRouteModel(groups, subagentDraft.provider ?? "", model)
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
}),
|
|
1576
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1577
|
+
label: props.t("effort"),
|
|
1578
|
+
value: subagentDraft.reasoningEffort || subagentCatalogModel?.effort || "",
|
|
1579
|
+
disabled: busy === "subagent" || !subagent.writable,
|
|
1580
|
+
choices: subagentEfforts,
|
|
1581
|
+
onChange: (effort) => {
|
|
1582
|
+
const matched = subagentCatalogModel;
|
|
1583
|
+
setSubagentDraft({
|
|
1584
|
+
...subagentDraft,
|
|
1585
|
+
...matched?.model.id === void 0 ? {} : { model: matched.model.id },
|
|
1586
|
+
reasoningEffort: effort
|
|
1587
|
+
});
|
|
1656
1588
|
}
|
|
1657
1589
|
})
|
|
1658
|
-
]
|
|
1659
|
-
}),
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
if (subagentOn) toggle("subagent");
|
|
1590
|
+
]
|
|
1591
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Actions, {
|
|
1592
|
+
t: props.t,
|
|
1593
|
+
busy: busy === "subagent",
|
|
1594
|
+
disabled: subagentDisabled,
|
|
1595
|
+
...message?.route === "subagent" ? { message: message.text } : {},
|
|
1596
|
+
onCancel: () => {
|
|
1597
|
+
resetSubagent();
|
|
1598
|
+
setMessage(void 0);
|
|
1668
1599
|
},
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
children: [
|
|
1682
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1683
|
-
label: props.t("provider"),
|
|
1684
|
-
value: subagentDraft.provider ?? "",
|
|
1685
|
-
disabled: busy === "subagent" || !subagent.writable,
|
|
1686
|
-
choices: subagentChoices.providers,
|
|
1687
|
-
onChange: (provider) => {
|
|
1688
|
-
const first = groups.find((group) => group.id === provider)?.models[0];
|
|
1689
|
-
setSubagentDraft({
|
|
1690
|
-
mode: "fixed",
|
|
1691
|
-
...selectRouteModel(groups, provider, first?.id ?? subagentDraft.model ?? "")
|
|
1692
|
-
});
|
|
1693
|
-
}
|
|
1694
|
-
}),
|
|
1695
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1696
|
-
label: props.t("model"),
|
|
1697
|
-
value: subagentCatalogModel?.model.id ?? subagentDraft.model ?? "",
|
|
1698
|
-
disabled: busy === "subagent" || !subagent.writable,
|
|
1699
|
-
choices: subagentChoices.models,
|
|
1700
|
-
onChange: (model) => {
|
|
1701
|
-
setSubagentDraft({
|
|
1702
|
-
mode: "fixed",
|
|
1703
|
-
...selectRouteModel(groups, subagentDraft.provider ?? "", model)
|
|
1704
|
-
});
|
|
1705
|
-
}
|
|
1706
|
-
}),
|
|
1707
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1708
|
-
label: props.t("effort"),
|
|
1709
|
-
value: subagentDraft.reasoningEffort || subagentCatalogModel?.effort || "",
|
|
1710
|
-
disabled: busy === "subagent" || !subagent.writable,
|
|
1711
|
-
choices: subagentEfforts,
|
|
1712
|
-
onChange: (effort) => {
|
|
1713
|
-
const matched = subagentCatalogModel;
|
|
1714
|
-
setSubagentDraft({
|
|
1715
|
-
...subagentDraft,
|
|
1716
|
-
...matched?.model.id === void 0 ? {} : { model: matched.model.id },
|
|
1717
|
-
reasoningEffort: effort
|
|
1718
|
-
});
|
|
1719
|
-
}
|
|
1720
|
-
})
|
|
1721
|
-
]
|
|
1722
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Actions, {
|
|
1723
|
-
t: props.t,
|
|
1724
|
-
busy: busy === "subagent",
|
|
1725
|
-
disabled: subagentDisabled,
|
|
1726
|
-
...message?.route === "subagent" ? { message: message.text } : {},
|
|
1727
|
-
onCancel: () => {
|
|
1728
|
-
resetSubagent();
|
|
1729
|
-
setMessage(void 0);
|
|
1730
|
-
},
|
|
1731
|
-
onSave: saveSubagent
|
|
1732
|
-
})] })
|
|
1733
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteCard, {
|
|
1734
|
-
title: props.t("subagent"),
|
|
1735
|
-
summary: unavailable("centralSubagentRouting"),
|
|
1736
|
-
icon: "subagent",
|
|
1737
|
-
open: false,
|
|
1738
|
-
onToggle: () => {},
|
|
1739
|
-
disabled: true,
|
|
1740
|
-
badge: props.t("unavailable"),
|
|
1741
|
-
badgeWarn: true
|
|
1742
|
-
})
|
|
1743
|
-
]
|
|
1600
|
+
onSave: saveSubagent
|
|
1601
|
+
})] })
|
|
1602
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteCard, {
|
|
1603
|
+
title: props.t("subagent"),
|
|
1604
|
+
summary: unavailable("centralSubagentRouting"),
|
|
1605
|
+
icon: "subagent",
|
|
1606
|
+
open: false,
|
|
1607
|
+
onToggle: () => {},
|
|
1608
|
+
disabled: true,
|
|
1609
|
+
badge: props.t("unavailable"),
|
|
1610
|
+
badgeWarn: true
|
|
1611
|
+
})]
|
|
1744
1612
|
}),
|
|
1745
1613
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1746
1614
|
className: ModelSwitchSettings_module_css_default.group,
|
|
@@ -2248,22 +2116,21 @@ window.__ModuleLoader__.load({
|
|
|
2248
2116
|
const zh$1 = {
|
|
2249
2117
|
nav: "模型切换",
|
|
2250
2118
|
title: "模型切换",
|
|
2251
|
-
subtitle: "
|
|
2119
|
+
subtitle: "为子代理与能力路由设置固定模型;当前会话模型用输入框旁的选择器直接切换。",
|
|
2252
2120
|
conversationRoutes: "对话路由",
|
|
2253
2121
|
capabilityRoutes: "能力路由",
|
|
2254
2122
|
sendProtection: "发送保护",
|
|
2255
2123
|
settingsSynced: "设置已同步",
|
|
2256
2124
|
remoteSettingsUnavailable: "官方 DSH 在非本机浏览器禁用配置读取与写入;请在 Host 的 127.0.0.1 页面打开设置。",
|
|
2257
|
-
defaultBadge: "默认",
|
|
2258
2125
|
cancel: "取消",
|
|
2259
2126
|
compactOnSwitch: "切换时压缩",
|
|
2260
2127
|
compactOnSwitchHelp: "发送且换了模型时,先按目标窗口检查上下文;超了就用切换前的模型压一次历史,再继续请求。只选模型、不发送,不会压缩。官方自动压缩和 /compact 不受影响。压缩会产生额外模型用量,摘要可能省略细节。",
|
|
2261
|
-
main: "主模型",
|
|
2262
2128
|
provider: "提供商",
|
|
2263
2129
|
model: "模型",
|
|
2264
2130
|
providerDefault: "使用提供商默认值",
|
|
2265
2131
|
providerDefaultShort: "Provider 默认",
|
|
2266
2132
|
effort: "推理强度",
|
|
2133
|
+
effortPinned: "已固定",
|
|
2267
2134
|
save: "保存",
|
|
2268
2135
|
saving: "保存中…",
|
|
2269
2136
|
saved: "已保存",
|
|
@@ -2277,10 +2144,8 @@ window.__ModuleLoader__.load({
|
|
|
2277
2144
|
imageHelp: "统一 generate_image 会调用所选 Codex 或 Grok Adapter;Provider 原有图片工具仍保留。",
|
|
2278
2145
|
unavailable: "未接入",
|
|
2279
2146
|
loading: "正在加载设置…",
|
|
2280
|
-
readonly: "设置为只读",
|
|
2281
2147
|
requestFailed: "保存失败",
|
|
2282
2148
|
catalogFailed: "无法加载模型目录。",
|
|
2283
|
-
conflict: "设置已在其他位置更改,请检查最新值后重试。",
|
|
2284
2149
|
autoReview: "自动审查模型",
|
|
2285
2150
|
autoReviewFollow: "跟随会话模型",
|
|
2286
2151
|
autoReviewHelp: "为官方 auto-review(Auto 权限预设的逐调用审查)指定审查模型;需启用官方 auto-review 能力包。关闭时审查跟随当前会话模型。",
|
|
@@ -2291,12 +2156,11 @@ window.__ModuleLoader__.load({
|
|
|
2291
2156
|
compactionRouteHelp: "摘要请求固定使用这里的 Provider/模型,与会话目标模型无关;保存后对下一次压缩立即生效。保存时若恰有压缩进行中,可能被打断并在下次重试。",
|
|
2292
2157
|
providerGroups: "供应商配置组",
|
|
2293
2158
|
providerGroupsCard: "按供应商预设路由",
|
|
2294
|
-
providerGroupsSummary: "
|
|
2159
|
+
providerGroupsSummary: "会话模型切换到该供应商时自动应用对应组",
|
|
2295
2160
|
providerGroupsHelp: "为压缩、子代理、审查按供应商分别保存路由。",
|
|
2296
|
-
providerGroupsBodyHelp: "
|
|
2161
|
+
providerGroupsBodyHelp: "每个供应商保存一组压缩、子代理、审查路由;本会话的模型切换到该供应商时(无论通过插件的模型选择器还是 TUI 等其他界面),这三条路由自动写入上方对应卡片。切换到未配置的供应商时现有路由保持不变;应用会覆盖三张卡片的当前值(含手动修改)。",
|
|
2297
2162
|
providerGroupPick: "选择供应商以编辑其配置组。",
|
|
2298
2163
|
providerGroupDelete: "删除该组",
|
|
2299
|
-
providerGroupApplyFailed: "主模型已保存,但供应商组应用失败:",
|
|
2300
2164
|
"reason.central-subagent-routing": "Alpha.4 没有全局子代理启动路由接口。",
|
|
2301
2165
|
"reason.packaged-preset-roots": "Alpha.4 不支持插件提供额外 preset root。",
|
|
2302
2166
|
"reason.tool-owner-suppression": "Alpha.4 没有工具所有者或来源抑制接口。",
|
|
@@ -2307,22 +2171,21 @@ window.__ModuleLoader__.load({
|
|
|
2307
2171
|
const en$1 = {
|
|
2308
2172
|
nav: "Model Switch",
|
|
2309
2173
|
title: "Model Switch",
|
|
2310
|
-
subtitle: "
|
|
2174
|
+
subtitle: "Pin routes for Subagents and capability tools; switch the active session model through the composer picker.",
|
|
2311
2175
|
conversationRoutes: "Conversation routes",
|
|
2312
2176
|
capabilityRoutes: "Capability routes",
|
|
2313
2177
|
sendProtection: "Send protection",
|
|
2314
2178
|
settingsSynced: "Settings synced",
|
|
2315
2179
|
remoteSettingsUnavailable: "Official DSH disables settings reads and writes in non-loopback browsers. Open Settings on the Host loopback URL to edit model routes.",
|
|
2316
|
-
defaultBadge: "Default",
|
|
2317
2180
|
cancel: "Cancel",
|
|
2318
2181
|
compactOnSwitch: "Compact on switch",
|
|
2319
2182
|
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.",
|
|
2320
|
-
main: "Main model",
|
|
2321
2183
|
provider: "Provider",
|
|
2322
2184
|
model: "Model",
|
|
2323
2185
|
providerDefault: "Provider default",
|
|
2324
2186
|
providerDefaultShort: "Provider default",
|
|
2325
2187
|
effort: "Reasoning effort",
|
|
2188
|
+
effortPinned: "Pinned",
|
|
2326
2189
|
save: "Save",
|
|
2327
2190
|
saving: "Saving…",
|
|
2328
2191
|
saved: "Saved",
|
|
@@ -2336,10 +2199,8 @@ window.__ModuleLoader__.load({
|
|
|
2336
2199
|
imageHelp: "The stable generate_image tool calls the selected Codex or Grok Adapter; existing provider image tools remain available.",
|
|
2337
2200
|
unavailable: "Unavailable",
|
|
2338
2201
|
loading: "Loading settings…",
|
|
2339
|
-
readonly: "Settings are read-only",
|
|
2340
2202
|
requestFailed: "Save failed",
|
|
2341
2203
|
catalogFailed: "Could not load the model catalog.",
|
|
2342
|
-
conflict: "Settings changed elsewhere. Review the latest values and retry.",
|
|
2343
2204
|
autoReview: "Auto review model",
|
|
2344
2205
|
autoReviewFollow: "Follows the session model",
|
|
2345
2206
|
autoReviewHelp: "Pin the reviewer model for the official auto-review gate (per-call review under the Auto permission preset). Requires the official auto-review bundle. When off, reviews follow the session model.",
|
|
@@ -2350,12 +2211,11 @@ window.__ModuleLoader__.load({
|
|
|
2350
2211
|
compactionRouteHelp: "Summarization requests use exactly this provider/model regardless of the conversation target; saved changes apply to the very next compaction. Saving while a compaction is running may interrupt it; it retries next time.",
|
|
2351
2212
|
providerGroups: "Provider groups",
|
|
2352
2213
|
providerGroupsCard: "Per-provider route presets",
|
|
2353
|
-
providerGroupsSummary: "Applied automatically when
|
|
2214
|
+
providerGroupsSummary: "Applied automatically when this session switches to that provider",
|
|
2354
2215
|
providerGroupsHelp: "Save compaction, subagent, and review routes per provider.",
|
|
2355
|
-
providerGroupsBodyHelp: "Each provider stores one compaction, subagent, and review route group.
|
|
2216
|
+
providerGroupsBodyHelp: "Each provider stores one compaction, subagent, and review route group. When this session switches to that provider — through the plugin picker or any other surface such as the TUI — these three routes are written into the cards above. Switching to a provider without a group leaves the current routes untouched. Applying a group overwrites those three cards, including manual edits.",
|
|
2356
2217
|
providerGroupPick: "Pick a provider to edit its group.",
|
|
2357
2218
|
providerGroupDelete: "Delete group",
|
|
2358
|
-
providerGroupApplyFailed: "Main model saved, but applying the provider group failed: ",
|
|
2359
2219
|
"reason.central-subagent-routing": "Alpha.4 exposes no global Subagent start-routing seam.",
|
|
2360
2220
|
"reason.packaged-preset-roots": "Alpha.4 exposes no plugin-owned preset root.",
|
|
2361
2221
|
"reason.tool-owner-suppression": "Alpha.4 exposes no tool owner or provenance suppression seam.",
|
|
@@ -2416,13 +2276,8 @@ window.__ModuleLoader__.load({
|
|
|
2416
2276
|
}
|
|
2417
2277
|
async function approvePlanReview(args) {
|
|
2418
2278
|
const current = args.current;
|
|
2419
|
-
|
|
2420
|
-
committed: true,
|
|
2421
|
-
mainDefaultRestored: true
|
|
2422
|
-
} : await args.select(args.selection);
|
|
2423
|
-
if (!commit.committed) return false;
|
|
2279
|
+
if (!(current !== void 0 && current !== null && current.provider === args.selection.provider && current.model === args.selection.model && current.reasoningEffort === args.selection.reasoningEffort ? { committed: true } : await args.select(args.selection)).committed) return false;
|
|
2424
2280
|
await args.answer();
|
|
2425
|
-
if (!commit.mainDefaultRestored) args.onNotRestored?.(args.selection.model);
|
|
2426
2281
|
return true;
|
|
2427
2282
|
}
|
|
2428
2283
|
function planActionView(state, available, hasExecution) {
|
|
@@ -3126,7 +2981,7 @@ window.__ModuleLoader__.load({
|
|
|
3126
2981
|
children: mark
|
|
3127
2982
|
});
|
|
3128
2983
|
}
|
|
3129
|
-
function ComposerPicker({ locked, providerLock = null, agentLocked = false, available,
|
|
2984
|
+
function ComposerPicker({ locked, providerLock = null, agentLocked = false, available, directory, t, draft, onDraftChange, embedded, tone, resolveInteractionOperations, roleOf }) {
|
|
3130
2985
|
const { snapshot: state, getDirectorySnapshot, load, select } = directory;
|
|
3131
2986
|
const [pane, setPane] = (0, react.useState)("root");
|
|
3132
2987
|
const [searching, setSearching] = (0, react.useState)(false);
|
|
@@ -3209,16 +3064,7 @@ window.__ModuleLoader__.load({
|
|
|
3209
3064
|
setQuery("");
|
|
3210
3065
|
};
|
|
3211
3066
|
const settleSelection = (outcome) => {
|
|
3212
|
-
if (outcome.committed)
|
|
3213
|
-
if (!outcome.mainDefaultRestored) {
|
|
3214
|
-
toastSeq.current += 1;
|
|
3215
|
-
setToast({
|
|
3216
|
-
seq: toastSeq.current,
|
|
3217
|
-
text: t("plan.notRestored")
|
|
3218
|
-
});
|
|
3219
|
-
}
|
|
3220
|
-
return;
|
|
3221
|
-
}
|
|
3067
|
+
if (outcome.committed) return;
|
|
3222
3068
|
const message = getDirectorySnapshot().error;
|
|
3223
3069
|
if (message !== null) {
|
|
3224
3070
|
toastSeq.current += 1;
|
|
@@ -3629,11 +3475,11 @@ window.__ModuleLoader__.load({
|
|
|
3629
3475
|
ref: triggerRef,
|
|
3630
3476
|
type: "button",
|
|
3631
3477
|
className: ComposerPicker_module_css_default.trigger,
|
|
3632
|
-
"aria-label":
|
|
3478
|
+
"aria-label": triggerAria,
|
|
3633
3479
|
"aria-haspopup": "menu",
|
|
3634
3480
|
"aria-expanded": open,
|
|
3635
3481
|
"aria-controls": open ? `${id}-menu` : void 0,
|
|
3636
|
-
title:
|
|
3482
|
+
title: triggerLabel,
|
|
3637
3483
|
disabled: locked,
|
|
3638
3484
|
onPointerDown: onTriggerPointerDown,
|
|
3639
3485
|
onClick: onTriggerClick,
|
|
@@ -3858,132 +3704,64 @@ window.__ModuleLoader__.load({
|
|
|
3858
3704
|
}, operations);
|
|
3859
3705
|
}
|
|
3860
3706
|
//#endregion
|
|
3861
|
-
//#region src/client/picker/
|
|
3862
|
-
/** Bound on how long the unawaited Host default write is awaited. */
|
|
3863
|
-
const DEFAULT_WRITE_WAIT_MS = 750;
|
|
3864
|
-
function defaultWait(ms) {
|
|
3865
|
-
return new Promise((resolve) => {
|
|
3866
|
-
setTimeout(resolve, ms);
|
|
3867
|
-
});
|
|
3868
|
-
}
|
|
3869
|
-
/** A fenced snapshot: ready, Host-synced, writable, with a concrete revision. */
|
|
3870
|
-
function fenced(snapshot) {
|
|
3871
|
-
if (snapshot.status !== "ready" || snapshot.mode !== "host" || !snapshot.writable) return void 0;
|
|
3872
|
-
if (snapshot.value === void 0 || snapshot.revision === void 0) return void 0;
|
|
3873
|
-
return {
|
|
3874
|
-
revision: snapshot.revision,
|
|
3875
|
-
value: snapshot.value
|
|
3876
|
-
};
|
|
3877
|
-
}
|
|
3878
|
-
/** Field writes restoring one complete Main selection; the Host replaces the section. */
|
|
3879
|
-
function mainDefaultOps(selection) {
|
|
3880
|
-
return [
|
|
3881
|
-
{
|
|
3882
|
-
op: "set",
|
|
3883
|
-
path: ["provider"],
|
|
3884
|
-
value: selection.provider
|
|
3885
|
-
},
|
|
3886
|
-
{
|
|
3887
|
-
op: "set",
|
|
3888
|
-
path: ["model"],
|
|
3889
|
-
value: selection.model
|
|
3890
|
-
},
|
|
3891
|
-
selection.reasoningEffort === void 0 || selection.reasoningEffort === "" ? {
|
|
3892
|
-
op: "unset",
|
|
3893
|
-
path: ["reasoningEffort"]
|
|
3894
|
-
} : {
|
|
3895
|
-
op: "set",
|
|
3896
|
-
path: ["reasoningEffort"],
|
|
3897
|
-
value: selection.reasoningEffort
|
|
3898
|
-
}
|
|
3899
|
-
];
|
|
3900
|
-
}
|
|
3901
|
-
/** An effort never leaks across a comparison: absent and cleared are the same selection. */
|
|
3902
|
-
function sameMainSelection(left, right) {
|
|
3903
|
-
if (left === void 0) return false;
|
|
3904
|
-
const leftEffort = left.reasoningEffort === void 0 || left.reasoningEffort === "" ? void 0 : left.reasoningEffort;
|
|
3905
|
-
const rightEffort = right.reasoningEffort === void 0 || right.reasoningEffort === "" ? void 0 : right.reasoningEffort;
|
|
3906
|
-
return left.provider === right.provider && left.model === right.model && leftEffort === rightEffort;
|
|
3907
|
-
}
|
|
3908
|
-
/** Resolve as soon as the revision leaves `revision`, or after the bounded wait. */
|
|
3909
|
-
function waitForAdvance(form, revision, wait, timeoutMs) {
|
|
3910
|
-
const current = form.getSnapshot();
|
|
3911
|
-
if (current.revision !== revision) return Promise.resolve(current);
|
|
3912
|
-
return new Promise((resolve) => {
|
|
3913
|
-
let settled = false;
|
|
3914
|
-
const finish = (snapshot) => {
|
|
3915
|
-
if (settled) return;
|
|
3916
|
-
settled = true;
|
|
3917
|
-
unsubscribe();
|
|
3918
|
-
resolve(snapshot);
|
|
3919
|
-
};
|
|
3920
|
-
const unsubscribe = form.subscribe(() => {
|
|
3921
|
-
const next = form.getSnapshot();
|
|
3922
|
-
if (next.revision !== revision) finish(next);
|
|
3923
|
-
});
|
|
3924
|
-
wait(timeoutMs).then(() => {
|
|
3925
|
-
finish(form.getSnapshot());
|
|
3926
|
-
});
|
|
3927
|
-
});
|
|
3928
|
-
}
|
|
3707
|
+
//#region src/client/picker/provider-group-watch.ts
|
|
3929
3708
|
/**
|
|
3930
|
-
*
|
|
3709
|
+
* Watch one directory store and apply groups on provider changes.
|
|
3931
3710
|
*
|
|
3932
|
-
*
|
|
3933
|
-
*
|
|
3934
|
-
*
|
|
3935
|
-
*
|
|
3936
|
-
*
|
|
3937
|
-
*
|
|
3938
|
-
* @param options -
|
|
3939
|
-
* @returns
|
|
3711
|
+
* - The first observed provider (or the one already loaded) is the baseline
|
|
3712
|
+
* and never triggers a write, so mounting and initial load stay silent.
|
|
3713
|
+
* - Applies are serialized: a change arriving mid-apply coalesces to the
|
|
3714
|
+
* latest target and runs once the current apply finishes, so two writes
|
|
3715
|
+
* never interleave pair writes.
|
|
3716
|
+
* - `current` being null (catalog still loading) is ignored, not a change.
|
|
3717
|
+
* @param options - the store to watch and the apply to run.
|
|
3718
|
+
* @returns the watch handle.
|
|
3940
3719
|
*/
|
|
3941
|
-
|
|
3942
|
-
const
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
const
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
const observed = fenced(form.getSnapshot());
|
|
3952
|
-
if (observed === void 0) return {
|
|
3953
|
-
restored: true,
|
|
3954
|
-
conflict: false
|
|
3955
|
-
};
|
|
3956
|
-
if (observed.revision === captured.revision) return {
|
|
3957
|
-
restored: true,
|
|
3958
|
-
conflict: false
|
|
3959
|
-
};
|
|
3960
|
-
if (sameMainSelection(observed.value, captured.value)) return {
|
|
3961
|
-
restored: true,
|
|
3962
|
-
conflict: false
|
|
3963
|
-
};
|
|
3964
|
-
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
3965
|
-
const snapshot = fenced(form.getSnapshot());
|
|
3966
|
-
if (snapshot === void 0) return {
|
|
3967
|
-
restored: false,
|
|
3968
|
-
conflict: false
|
|
3969
|
-
};
|
|
3970
|
-
if (sameMainSelection(snapshot.value, captured.value)) return {
|
|
3971
|
-
restored: true,
|
|
3972
|
-
conflict: false
|
|
3973
|
-
};
|
|
3974
|
-
if (await form.mutate(mainDefaultOps(captured.value), snapshot.revision)) return {
|
|
3975
|
-
restored: true,
|
|
3976
|
-
conflict: false
|
|
3977
|
-
};
|
|
3978
|
-
if (form.getSnapshot().revision === snapshot.revision) return {
|
|
3979
|
-
restored: false,
|
|
3980
|
-
conflict: false
|
|
3981
|
-
};
|
|
3720
|
+
function createProviderGroupWatch(options) {
|
|
3721
|
+
const onFailure = options.onFailure ?? ((error) => {
|
|
3722
|
+
console.warn("dsh-model-switch: provider group apply failed", error);
|
|
3723
|
+
});
|
|
3724
|
+
let baselined = false;
|
|
3725
|
+
let lastSeen;
|
|
3726
|
+
const initial = options.source.getSnapshot().current;
|
|
3727
|
+
if (initial !== null) {
|
|
3728
|
+
baselined = true;
|
|
3729
|
+
lastSeen = initial.provider;
|
|
3982
3730
|
}
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3731
|
+
let running = false;
|
|
3732
|
+
let pending;
|
|
3733
|
+
const run = async (provider) => {
|
|
3734
|
+
running = true;
|
|
3735
|
+
try {
|
|
3736
|
+
await options.apply(provider);
|
|
3737
|
+
} catch (error) {
|
|
3738
|
+
onFailure(error);
|
|
3739
|
+
} finally {
|
|
3740
|
+
running = false;
|
|
3741
|
+
if (pending !== void 0) {
|
|
3742
|
+
const next = pending;
|
|
3743
|
+
pending = void 0;
|
|
3744
|
+
run(next);
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3986
3747
|
};
|
|
3748
|
+
const check = () => {
|
|
3749
|
+
const current = options.source.getSnapshot().current;
|
|
3750
|
+
if (current === null) return;
|
|
3751
|
+
if (!baselined) {
|
|
3752
|
+
baselined = true;
|
|
3753
|
+
lastSeen = current.provider;
|
|
3754
|
+
return;
|
|
3755
|
+
}
|
|
3756
|
+
if (current.provider === lastSeen) return;
|
|
3757
|
+
lastSeen = current.provider;
|
|
3758
|
+
if (running) {
|
|
3759
|
+
pending = current.provider;
|
|
3760
|
+
return;
|
|
3761
|
+
}
|
|
3762
|
+
run(current.provider);
|
|
3763
|
+
};
|
|
3764
|
+
return { dispose: options.source.subscribe(check) };
|
|
3987
3765
|
}
|
|
3988
3766
|
//#endregion
|
|
3989
3767
|
//#region src/picker/pending-dismissal.ts
|
|
@@ -4199,12 +3977,6 @@ window.__ModuleLoader__.load({
|
|
|
4199
3977
|
throw cause instanceof PlanDismissalUnsupportedError ? new Error(unsupportedMessage, { cause }) : new Error(rejectedMessage, { cause });
|
|
4200
3978
|
}
|
|
4201
3979
|
}
|
|
4202
|
-
/** Match the model seat's admission guard before letting either picker offer a change. */
|
|
4203
|
-
function mainDefaultsUnavailableReason(snapshot, t) {
|
|
4204
|
-
if (snapshot.mode === "memory") return t("settings.remoteUnavailable");
|
|
4205
|
-
if (snapshot.status === "loading") return t("settings.loading");
|
|
4206
|
-
if (snapshot.status !== "ready" || snapshot.mode !== "host" || !snapshot.writable || snapshot.value === void 0 || snapshot.revision === void 0) return t("settings.unavailable");
|
|
4207
|
-
}
|
|
4208
3980
|
/** Inline failed lock-read status; history and log reading stay unaffected. */
|
|
4209
3981
|
function ProviderLockHint(props) {
|
|
4210
3982
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -4220,7 +3992,6 @@ window.__ModuleLoader__.load({
|
|
|
4220
3992
|
function PlanReviewCard(props) {
|
|
4221
3993
|
const snapshot = props.useDirectory((value) => value);
|
|
4222
3994
|
const order = props.useProviderOrder((value) => value);
|
|
4223
|
-
const settingsUnavailableReason = mainDefaultsUnavailableReason((0, react.useSyncExternalStore)(props.subscribeMainDefaults, props.getMainDefaultsSnapshot), props.t);
|
|
4224
3995
|
const lock = (0, react.useSyncExternalStore)(props.providerLockStore.subscribe, props.providerLockStore.getSnapshot);
|
|
4225
3996
|
const phase = props.useInput((input) => input.phase);
|
|
4226
3997
|
const blank = props.useSession((session) => session.blank);
|
|
@@ -4256,7 +4027,6 @@ window.__ModuleLoader__.load({
|
|
|
4256
4027
|
providerLock,
|
|
4257
4028
|
agentLocked,
|
|
4258
4029
|
lockFailed: lock.failed,
|
|
4259
|
-
...settingsUnavailableReason === void 0 ? {} : { settingsUnavailableReason },
|
|
4260
4030
|
directory: pickerDirectoryViewOrdered(snapshot, props, order, props.catalogRoutes?.() ?? {}),
|
|
4261
4031
|
t: props.t,
|
|
4262
4032
|
goalRemote: props.goalRemote,
|
|
@@ -4265,7 +4035,7 @@ window.__ModuleLoader__.load({
|
|
|
4265
4035
|
...props.roleOf === void 0 ? {} : { roleOf: props.roleOf }
|
|
4266
4036
|
}, props.matched.key);
|
|
4267
4037
|
}
|
|
4268
|
-
function PlanReviewState({ matched, review, available, providerLock, agentLocked, lockFailed,
|
|
4038
|
+
function PlanReviewState({ matched, review, available, providerLock, agentLocked, lockFailed, directory, t, goalRemote, planPreview, resolveInteractionOperations, roleOf }) {
|
|
4269
4039
|
const { snapshot, getDirectorySnapshot, load, select } = directory;
|
|
4270
4040
|
const [execution, setExecution] = (0, react.useState)(snapshot.current ?? void 0);
|
|
4271
4041
|
const [feedback, setFeedback] = (0, react.useState)("");
|
|
@@ -4328,8 +4098,7 @@ window.__ModuleLoader__.load({
|
|
|
4328
4098
|
setNotice(null);
|
|
4329
4099
|
load();
|
|
4330
4100
|
};
|
|
4331
|
-
const
|
|
4332
|
-
const executionAllowed = execution !== void 0 && !settingsBlock && runtimeChoiceAllowed(providerLock, agentLocked, execution.provider, snapshot.current?.provider, isAgentRole(roleOf?.(execution.provider)));
|
|
4101
|
+
const executionAllowed = execution !== void 0 && runtimeChoiceAllowed(providerLock, agentLocked, execution.provider, snapshot.current?.provider, isAgentRole(roleOf?.(execution.provider)));
|
|
4333
4102
|
const action = planActionView({
|
|
4334
4103
|
busy,
|
|
4335
4104
|
blocked,
|
|
@@ -4341,11 +4110,7 @@ window.__ModuleLoader__.load({
|
|
|
4341
4110
|
select,
|
|
4342
4111
|
selection: model,
|
|
4343
4112
|
current: snapshot.current,
|
|
4344
|
-
answer: () => respondAnswer(matched, review.id, review.approve.label, t("plan.responseRejected"), true)
|
|
4345
|
-
onNotRestored: (switched) => {
|
|
4346
|
-
setNotice(t("plan.notRestored", { model: switched }));
|
|
4347
|
-
console.warn("dsh-model-switch: the Session model changed but the Main default was not restored", switched);
|
|
4348
|
-
}
|
|
4113
|
+
answer: () => respondAnswer(matched, review.id, review.approve.label, t("plan.responseRejected"), true)
|
|
4349
4114
|
})) {
|
|
4350
4115
|
const message = getDirectorySnapshot().error;
|
|
4351
4116
|
throw new Error(message === null ? t("plan.modelFailed") : t("error.action", { message }));
|
|
@@ -4417,12 +4182,11 @@ window.__ModuleLoader__.load({
|
|
|
4417
4182
|
errorLabel: (message) => t("plan.pickerCrash", { message }),
|
|
4418
4183
|
retryLabel: t("retry"),
|
|
4419
4184
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ComposerPicker, {
|
|
4420
|
-
locked: busy || blocked
|
|
4185
|
+
locked: busy || blocked,
|
|
4421
4186
|
providerLock,
|
|
4422
4187
|
agentLocked,
|
|
4423
4188
|
...roleOf === void 0 ? {} : { roleOf },
|
|
4424
4189
|
available,
|
|
4425
|
-
...settingsUnavailableReason === void 0 ? {} : { unavailableReason: settingsUnavailableReason },
|
|
4426
4190
|
directory,
|
|
4427
4191
|
t,
|
|
4428
4192
|
...resolveInteractionOperations === void 0 ? {} : { resolveInteractionOperations },
|
|
@@ -4456,7 +4220,7 @@ window.__ModuleLoader__.load({
|
|
|
4456
4220
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4457
4221
|
className: PlanReviewCard_module_css_default.feedback,
|
|
4458
4222
|
role: "status",
|
|
4459
|
-
children: action.error ?? notice
|
|
4223
|
+
children: action.error ?? notice
|
|
4460
4224
|
}),
|
|
4461
4225
|
decline !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4462
4226
|
className: PlanReviewCard_module_css_default.feedbackRow,
|
|
@@ -4578,9 +4342,6 @@ window.__ModuleLoader__.load({
|
|
|
4578
4342
|
"thinking.off": "关",
|
|
4579
4343
|
"effort.providerDefault": "Default",
|
|
4580
4344
|
"status.loading": "正在刷新模型列表…",
|
|
4581
|
-
"settings.remoteUnavailable": "官方 DSH 在非本机浏览器禁用配置读取与写入;请在 Host 的 127.0.0.1 页面选择模型。",
|
|
4582
|
-
"settings.loading": "正在加载主模型设置;加载完成后才能切换模型。",
|
|
4583
|
-
"settings.unavailable": "主模型设置不可写;请在 Host 的本机页面检查设置状态。",
|
|
4584
4345
|
"error.action": "模型操作失败:{message}",
|
|
4585
4346
|
"error.picker": "模型选择器出错:{message}(点击重试)",
|
|
4586
4347
|
"action.reload": "重新加载",
|
|
@@ -4601,7 +4362,6 @@ window.__ModuleLoader__.load({
|
|
|
4601
4362
|
"plan.pickerCrash": "执行模型选择器出错:{message}",
|
|
4602
4363
|
"plan.feedback": "拒绝时的反馈",
|
|
4603
4364
|
"plan.feedbackPlaceholder": "说明要改什么(可留空)",
|
|
4604
|
-
"plan.notRestored": "执行模型已切换,但主模型默认值未能还原({model});请在「设置 → 模型」中确认默认模型。",
|
|
4605
4365
|
"plan.dismissUnsupported": "当前 DSH 版本既未提供 PendingQuestion.dismiss() 也未提供 cancel(),无法回到聊天;请直接在输入框里发送你的意见。",
|
|
4606
4366
|
"plan.goalRun": "设置为目标并执行",
|
|
4607
4367
|
"plan.goalFailed": "设置目标失败:{message}",
|
|
@@ -4634,9 +4394,6 @@ window.__ModuleLoader__.load({
|
|
|
4634
4394
|
"thinking.off": "Off",
|
|
4635
4395
|
"effort.providerDefault": "Default",
|
|
4636
4396
|
"status.loading": "Refreshing model list…",
|
|
4637
|
-
"settings.remoteUnavailable": "Official DSH disables settings reads and writes in non-loopback browsers. Select a model on the Host loopback URL.",
|
|
4638
|
-
"settings.loading": "Main model settings are loading. Wait until they are ready before switching models.",
|
|
4639
|
-
"settings.unavailable": "Main model settings are not writable. Check their status on the Host loopback URL.",
|
|
4640
4397
|
"error.action": "Model operation failed: {message}",
|
|
4641
4398
|
"error.picker": "Model picker error: {message} (click to retry)",
|
|
4642
4399
|
"action.reload": "Reload",
|
|
@@ -4657,7 +4414,6 @@ window.__ModuleLoader__.load({
|
|
|
4657
4414
|
"plan.pickerCrash": "Execution model picker error: {message}",
|
|
4658
4415
|
"plan.feedback": "Feedback when declining",
|
|
4659
4416
|
"plan.feedbackPlaceholder": "Say what to change (optional)",
|
|
4660
|
-
"plan.notRestored": "The execution model switched, but the Main default could not be restored ({model}); confirm the default in Settings → Models.",
|
|
4661
4417
|
"plan.dismissUnsupported": "This DSH Host exposes neither PendingQuestion.dismiss() nor cancel(), so the review cannot return to chat; send your feedback as a message instead.",
|
|
4662
4418
|
"plan.goalRun": "Set as goal and run",
|
|
4663
4419
|
"plan.goalFailed": "Could not set the goal: {message}",
|
|
@@ -4757,7 +4513,6 @@ window.__ModuleLoader__.load({
|
|
|
4757
4513
|
function ModelSeat(props) {
|
|
4758
4514
|
const directory = props.useDirectory((snapshot) => snapshot);
|
|
4759
4515
|
const order = props.useProviderOrder((value) => value);
|
|
4760
|
-
const settingsUnavailableReason = mainDefaultsUnavailableReason((0, react.useSyncExternalStore)(props.subscribeMainDefaults, props.getMainDefaultsSnapshot), props.t);
|
|
4761
4516
|
const lock = (0, react.useSyncExternalStore)(props.providerLockStore.subscribe, props.providerLockStore.getSnapshot);
|
|
4762
4517
|
const phase = props.useInput((input) => input.phase);
|
|
4763
4518
|
const blank = props.useSession((session) => session.blank);
|
|
@@ -4773,12 +4528,11 @@ window.__ModuleLoader__.load({
|
|
|
4773
4528
|
]);
|
|
4774
4529
|
const providerLock = effectiveProviderLock(lock, directory.current?.provider, active, isAgentRole(props.roleOf?.(directory.current?.provider ?? "")));
|
|
4775
4530
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [lock.failed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderLockHint, { t: props.t }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ComposerPicker, {
|
|
4776
|
-
locked: props.locked ||
|
|
4531
|
+
locked: props.locked || lock.failed && directory.current === null,
|
|
4777
4532
|
providerLock,
|
|
4778
4533
|
agentLocked: agentProviderLocked(blank, providerLock, active),
|
|
4779
4534
|
...props.roleOf === void 0 ? {} : { roleOf: props.roleOf },
|
|
4780
4535
|
available: props.available,
|
|
4781
|
-
...settingsUnavailableReason === void 0 ? {} : { unavailableReason: settingsUnavailableReason },
|
|
4782
4536
|
directory: pickerDirectoryViewOrdered(directory, props, order, props.catalogRoutes?.() ?? {}),
|
|
4783
4537
|
t: props.t,
|
|
4784
4538
|
...props.resolveInteractionOperations === void 0 ? {} : { resolveInteractionOperations: props.resolveInteractionOperations }
|
|
@@ -4803,8 +4557,23 @@ window.__ModuleLoader__.load({
|
|
|
4803
4557
|
], (scope) => {
|
|
4804
4558
|
const models = scope.modelDirectories;
|
|
4805
4559
|
const sessions = scope.sessions;
|
|
4806
|
-
const mainDefaults = scope.configForms.get(MAIN_DEFAULT_CONFIG_ID);
|
|
4807
4560
|
const ownedForm = scope.configForms.get(MODEL_SWITCH_CONFIG_ID);
|
|
4561
|
+
const groupWatches = /* @__PURE__ */ new Map();
|
|
4562
|
+
const watchGroupsFor = (sessionId, source) => {
|
|
4563
|
+
const key = String(sessionId);
|
|
4564
|
+
if (groupWatches.has(key)) return;
|
|
4565
|
+
const watch = createProviderGroupWatch({
|
|
4566
|
+
source,
|
|
4567
|
+
apply: async (provider) => {
|
|
4568
|
+
await applyProviderGroupTo(ownedForm, normalizeProviderGroups(ownedForm.getSnapshot().value?.providerGroups), provider);
|
|
4569
|
+
}
|
|
4570
|
+
});
|
|
4571
|
+
groupWatches.set(key, watch.dispose);
|
|
4572
|
+
};
|
|
4573
|
+
scope.effect(() => () => {
|
|
4574
|
+
for (const dispose of groupWatches.values()) dispose();
|
|
4575
|
+
groupWatches.clear();
|
|
4576
|
+
});
|
|
4808
4577
|
let directoryService;
|
|
4809
4578
|
const orderStore = providerOrderStore(scope.configForms.get(PROVIDERS_CONFIG_ID));
|
|
4810
4579
|
scope.inject(["providerDirectory"], (directoryScope) => {
|
|
@@ -4835,6 +4604,7 @@ window.__ModuleLoader__.load({
|
|
|
4835
4604
|
};
|
|
4836
4605
|
const roleOf = (key) => readProviderRole(directoryService, key);
|
|
4837
4606
|
const providerLockStore = createProviderLockStore(readLock);
|
|
4607
|
+
if (available) watchGroupsFor(sessionId, directory.store);
|
|
4838
4608
|
return {
|
|
4839
4609
|
available,
|
|
4840
4610
|
roleOf,
|
|
@@ -4843,8 +4613,6 @@ window.__ModuleLoader__.load({
|
|
|
4843
4613
|
refreshProviderLock: () => {
|
|
4844
4614
|
providerLockStore.refresh();
|
|
4845
4615
|
},
|
|
4846
|
-
subscribeMainDefaults: (listener) => mainDefaults.subscribe(listener),
|
|
4847
|
-
getMainDefaultsSnapshot: () => mainDefaults.getSnapshot(),
|
|
4848
4616
|
goalRemote: () => goalRemoteFrom(scope),
|
|
4849
4617
|
planPreview,
|
|
4850
4618
|
hooks: {
|
|
@@ -4857,36 +4625,22 @@ window.__ModuleLoader__.load({
|
|
|
4857
4625
|
if (available) directory.load().catch(() => {});
|
|
4858
4626
|
},
|
|
4859
4627
|
select: async (selection) => {
|
|
4860
|
-
|
|
4861
|
-
committed: false,
|
|
4862
|
-
mainDefaultRestored: true
|
|
4863
|
-
};
|
|
4864
|
-
if (!available) return uncommitted;
|
|
4628
|
+
if (!available) return { committed: false };
|
|
4865
4629
|
const state = await providerLockStore.refresh();
|
|
4866
4630
|
const currentProvider = directory.store.getSnapshot().current?.provider;
|
|
4867
4631
|
if (!isProviderAllowed(state, selection.provider, currentProvider, {
|
|
4868
4632
|
...readSessionState(sessions, sessionId),
|
|
4869
4633
|
agent: isAgentRole(roleOf(selection.provider)),
|
|
4870
4634
|
currentAgent: currentProvider !== void 0 && isAgentRole(roleOf(currentProvider))
|
|
4871
|
-
})) return
|
|
4872
|
-
const defaultBeforeSwitch = mainDefaults.getSnapshot();
|
|
4873
|
-
if (defaultBeforeSwitch.status !== "ready" || defaultBeforeSwitch.mode !== "host" || !defaultBeforeSwitch.writable || defaultBeforeSwitch.value === void 0 || defaultBeforeSwitch.revision === void 0) return uncommitted;
|
|
4635
|
+
})) return { committed: false };
|
|
4874
4636
|
let result;
|
|
4875
4637
|
try {
|
|
4876
4638
|
result = await directory.select(selection);
|
|
4877
4639
|
} catch {
|
|
4878
|
-
return
|
|
4879
|
-
}
|
|
4880
|
-
if (!result.ok) return uncommitted;
|
|
4881
|
-
if (selection.provider !== currentProvider) try {
|
|
4882
|
-
await applyProviderGroupTo(ownedForm, normalizeProviderGroups(ownedForm.getSnapshot().value?.providerGroups), selection.provider);
|
|
4883
|
-
} catch (error) {
|
|
4884
|
-
console.warn("dsh-model-switch: provider group apply failed", error);
|
|
4640
|
+
return { committed: false };
|
|
4885
4641
|
}
|
|
4886
|
-
return {
|
|
4887
|
-
|
|
4888
|
-
mainDefaultRestored: (await restoreMainDefault(mainDefaults, defaultBeforeSwitch)).restored
|
|
4889
|
-
};
|
|
4642
|
+
if (!result.ok) return { committed: false };
|
|
4643
|
+
return { committed: true };
|
|
4890
4644
|
}
|
|
4891
4645
|
};
|
|
4892
4646
|
};
|
|
@@ -4983,56 +4737,12 @@ window.__ModuleLoader__.load({
|
|
|
4983
4737
|
}), "dsh-model-switch: localized Settings section");
|
|
4984
4738
|
const t = ctx.locale.bind(localeNamespace);
|
|
4985
4739
|
const remote = ctx.remote;
|
|
4986
|
-
const mainForm = ctx.configForms.get(MAIN_DEFAULT_CONFIG_ID);
|
|
4987
4740
|
const owned = ctx.configForms.get(MODEL_SWITCH_CONFIG_ID);
|
|
4988
|
-
const main = deriveConfigForm(mainForm, (value) => value, {
|
|
4989
|
-
provider: "provider",
|
|
4990
|
-
model: "model",
|
|
4991
|
-
reasoningEffort: "reasoningEffort"
|
|
4992
|
-
});
|
|
4993
4741
|
const subagent = deriveConfigForm(owned, deriveSubagentSettings, SUBAGENT_SETTINGS_FIELDS);
|
|
4994
4742
|
const search = deriveConfigForm(owned, deriveSearchSettings, SEARCH_SETTINGS_FIELDS);
|
|
4995
4743
|
const image = deriveConfigForm(owned, deriveImageSettings, IMAGE_SETTINGS_FIELDS);
|
|
4996
4744
|
const autoReview = deriveConfigForm(owned, deriveAutoReviewSettings, AUTO_REVIEW_SETTINGS_FIELDS);
|
|
4997
4745
|
const compaction = deriveConfigForm(owned, deriveCompactionSettings, COMPACTION_SETTINGS_FIELDS);
|
|
4998
|
-
/** Adopt the target provider's saved group; a provider without a group leaves the routes untouched. */
|
|
4999
|
-
const applyProviderGroupFor = async (provider) => {
|
|
5000
|
-
await applyProviderGroupTo(owned, normalizeProviderGroups(owned.getSnapshot().value?.providerGroups), provider);
|
|
5001
|
-
};
|
|
5002
|
-
const saveMain = async (next, expectedRevision) => {
|
|
5003
|
-
const previousProvider = mainForm.getSnapshot().value?.provider;
|
|
5004
|
-
if (!await mainForm.mutate([
|
|
5005
|
-
{
|
|
5006
|
-
op: "set",
|
|
5007
|
-
path: ["provider"],
|
|
5008
|
-
value: next.provider
|
|
5009
|
-
},
|
|
5010
|
-
{
|
|
5011
|
-
op: "set",
|
|
5012
|
-
path: ["model"],
|
|
5013
|
-
value: next.model
|
|
5014
|
-
},
|
|
5015
|
-
next.reasoningEffort === void 0 || next.reasoningEffort === "" ? {
|
|
5016
|
-
op: "unset",
|
|
5017
|
-
path: ["reasoningEffort"]
|
|
5018
|
-
} : {
|
|
5019
|
-
op: "set",
|
|
5020
|
-
path: ["reasoningEffort"],
|
|
5021
|
-
value: next.reasoningEffort
|
|
5022
|
-
}
|
|
5023
|
-
], expectedRevision)) {
|
|
5024
|
-
if (mainForm.getSnapshot().revision !== expectedRevision) throw new MainSettingsConflictError(t("conflict"));
|
|
5025
|
-
throw new Error("settings-rejected");
|
|
5026
|
-
}
|
|
5027
|
-
const revision = mainForm.getSnapshot().revision;
|
|
5028
|
-
if (revision === void 0) throw new Error("settings unavailable after save");
|
|
5029
|
-
if (next.provider.trim() !== "" && next.provider !== previousProvider) try {
|
|
5030
|
-
await applyProviderGroupFor(next.provider);
|
|
5031
|
-
} catch (error) {
|
|
5032
|
-
throw new Error(t("providerGroupApplyFailed") + (error instanceof Error ? error.message : t("requestFailed")));
|
|
5033
|
-
}
|
|
5034
|
-
return revision;
|
|
5035
|
-
};
|
|
5036
4746
|
const connectionRpc = (() => {
|
|
5037
4747
|
try {
|
|
5038
4748
|
const rpc = ctx.get("connection", false)?.rpc;
|
|
@@ -5065,7 +4775,6 @@ window.__ModuleLoader__.load({
|
|
|
5065
4775
|
inject: () => ({
|
|
5066
4776
|
t,
|
|
5067
4777
|
hooks: {
|
|
5068
|
-
mainSettings: main,
|
|
5069
4778
|
subagentSettings: subagent,
|
|
5070
4779
|
searchSettings: search,
|
|
5071
4780
|
imageSettings: image,
|
|
@@ -5074,7 +4783,6 @@ window.__ModuleLoader__.load({
|
|
|
5074
4783
|
switchSettings: owned
|
|
5075
4784
|
},
|
|
5076
4785
|
capabilities: RUNTIME_CAPABILITIES,
|
|
5077
|
-
saveMain,
|
|
5078
4786
|
setSubagent: (field, value) => value === void 0 ? subagent.unset(field) : subagent.set(field, value),
|
|
5079
4787
|
setCapability: (route, field, value) => {
|
|
5080
4788
|
const scope = route === "search" ? search : route === "image" ? image : route === "compaction" ? compaction : autoReview;
|