dsh-model-switch 0.4.22 → 0.4.24
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 +23 -0
- package/PRODUCT.md +7 -6
- package/README.md +43 -15
- package/README.zh.md +43 -15
- package/cordis.patch.yml +15 -1
- package/docs/screenshots/settings-subagent.png +0 -0
- package/lib/auto-review.js +8 -0
- package/lib/client.js +510 -810
- package/lib/compaction.js +51 -18
- package/lib/index.js +25 -96
- package/lib/provider-group-CsZi7H29.js +94 -0
- package/lib/session-route-h9SAqajc.js +172 -0
- package/lib/session-title.js +217 -0
- package/lib/subagent-runtime.js +20 -17
- package/lib/types/auto-review.d.ts +8 -0
- package/lib/types/client/ModelSwitchSettings.d.ts +9 -6
- package/lib/types/client/derived-config-form.d.ts +14 -0
- package/lib/types/client/locales.d.ts +10 -9
- 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 +8 -9
- package/lib/types/compaction.d.ts +25 -4
- package/lib/types/host-runtime.d.ts +1 -11
- package/lib/types/host-settings.d.ts +6 -0
- package/lib/types/index.d.ts +1 -1
- package/lib/types/picker/plan-review.d.ts +2 -10
- package/lib/types/provider-group.d.ts +20 -36
- package/lib/types/session-route.d.ts +111 -0
- package/lib/types/session-title.d.ts +104 -0
- package/lib/types/subagent-runtime.d.ts +14 -3
- package/package.json +24 -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",
|
|
@@ -43,6 +39,11 @@ window.__ModuleLoader__.load({
|
|
|
43
39
|
provider: "compactProvider",
|
|
44
40
|
model: "compactModel"
|
|
45
41
|
});
|
|
42
|
+
/** Automatic session-title naming route; both unset means the official default (the session's own logged route). */
|
|
43
|
+
const TITLE_SETTINGS_FIELDS = Object.freeze({
|
|
44
|
+
provider: "titleProvider",
|
|
45
|
+
model: "titleModel"
|
|
46
|
+
});
|
|
46
47
|
function deriveSubagentSettings(settings) {
|
|
47
48
|
return {
|
|
48
49
|
mode: settings.subagentMode,
|
|
@@ -75,6 +76,12 @@ window.__ModuleLoader__.load({
|
|
|
75
76
|
...settings.compactModel === void 0 ? {} : { model: settings.compactModel }
|
|
76
77
|
};
|
|
77
78
|
}
|
|
79
|
+
function deriveTitleSettings(settings) {
|
|
80
|
+
return {
|
|
81
|
+
...settings.titleProvider === void 0 ? {} : { provider: settings.titleProvider },
|
|
82
|
+
...settings.titleModel === void 0 ? {} : { model: settings.titleModel }
|
|
83
|
+
};
|
|
84
|
+
}
|
|
78
85
|
/** Whether both fields of a model route form a complete pair; an incomplete pair stays off. */
|
|
79
86
|
function routePairConfigured(route) {
|
|
80
87
|
return route !== void 0 && (route.provider ?? "").trim() !== "" && (route.model ?? "").trim() !== "";
|
|
@@ -88,7 +95,9 @@ window.__ModuleLoader__.load({
|
|
|
88
95
|
"autoReviewModel",
|
|
89
96
|
"subagentProvider",
|
|
90
97
|
"subagentModel",
|
|
91
|
-
"subagentReasoningEffort"
|
|
98
|
+
"subagentReasoningEffort",
|
|
99
|
+
"titleProvider",
|
|
100
|
+
"titleModel"
|
|
92
101
|
]);
|
|
93
102
|
/** Own data property, so a literal '__proto__' provider id can never touch a prototype. */
|
|
94
103
|
function setKey(target, key, value) {
|
|
@@ -151,62 +160,6 @@ window.__ModuleLoader__.load({
|
|
|
151
160
|
if (!Object.prototype.hasOwnProperty.call(groups, provider)) return void 0;
|
|
152
161
|
return groups[provider];
|
|
153
162
|
}
|
|
154
|
-
/** A complete pinned pair; either half missing leaves the capability at its default. */
|
|
155
|
-
function pair(group, providerField, modelField) {
|
|
156
|
-
const provider = group[providerField];
|
|
157
|
-
const model = group[modelField];
|
|
158
|
-
return present(provider) && present(model) ? {
|
|
159
|
-
provider,
|
|
160
|
-
model
|
|
161
|
-
} : void 0;
|
|
162
|
-
}
|
|
163
|
-
/**
|
|
164
|
-
* Ordered writes applying one group with full-replacement semantics: pinned
|
|
165
|
-
* capabilities land as pairs, everything else reverts to its default state
|
|
166
|
-
* (compaction/review follow the session model; Subagent follows official
|
|
167
|
-
* inherit). The mode write comes last so a fixed route never publishes with
|
|
168
|
-
* the previous provider's fields still half-written.
|
|
169
|
-
*/
|
|
170
|
-
function providerGroupWrites(group) {
|
|
171
|
-
const compact = pair(group, "compactProvider", "compactModel");
|
|
172
|
-
const review = pair(group, "autoReviewProvider", "autoReviewModel");
|
|
173
|
-
const subagent = pair(group, "subagentProvider", "subagentModel");
|
|
174
|
-
const effort = subagent !== void 0 && present(group.subagentReasoningEffort) ? group.subagentReasoningEffort : void 0;
|
|
175
|
-
return [
|
|
176
|
-
{
|
|
177
|
-
field: "compactProvider",
|
|
178
|
-
value: compact?.provider
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
field: "compactModel",
|
|
182
|
-
value: compact?.model
|
|
183
|
-
},
|
|
184
|
-
{
|
|
185
|
-
field: "autoReviewProvider",
|
|
186
|
-
value: review?.provider
|
|
187
|
-
},
|
|
188
|
-
{
|
|
189
|
-
field: "autoReviewModel",
|
|
190
|
-
value: review?.model
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
field: "subagentProvider",
|
|
194
|
-
value: subagent?.provider
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
field: "subagentModel",
|
|
198
|
-
value: subagent?.model
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
field: "subagentReasoningEffort",
|
|
202
|
-
value: effort
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
field: "subagentMode",
|
|
206
|
-
mode: subagent !== void 0 ? "fixed" : "follow-main"
|
|
207
|
-
}
|
|
208
|
-
];
|
|
209
|
-
}
|
|
210
163
|
/** Storage entry for one provider's group; absent or blank route fields become ''. */
|
|
211
164
|
function entryOf(provider, group) {
|
|
212
165
|
return {
|
|
@@ -217,7 +170,9 @@ window.__ModuleLoader__.load({
|
|
|
217
170
|
autoReviewModel: group.autoReviewModel ?? "",
|
|
218
171
|
subagentProvider: group.subagentProvider ?? "",
|
|
219
172
|
subagentModel: group.subagentModel ?? "",
|
|
220
|
-
subagentReasoningEffort: group.subagentReasoningEffort ?? ""
|
|
173
|
+
subagentReasoningEffort: group.subagentReasoningEffort ?? "",
|
|
174
|
+
titleProvider: group.titleProvider ?? "",
|
|
175
|
+
titleModel: group.titleModel ?? ""
|
|
221
176
|
};
|
|
222
177
|
}
|
|
223
178
|
/**
|
|
@@ -234,19 +189,6 @@ window.__ModuleLoader__.load({
|
|
|
234
189
|
}
|
|
235
190
|
return Object.entries(groups).map(([key, value]) => entryOf(key, value));
|
|
236
191
|
}
|
|
237
|
-
/**
|
|
238
|
-
* Apply a saved group's routes to the live flat settings.
|
|
239
|
-
* @returns true when a group existed and every write settled; false when the
|
|
240
|
-
* provider has no group (nothing was written). A rejected write propagates.
|
|
241
|
-
*/
|
|
242
|
-
async function applyProviderGroupTo(sink, groups, provider) {
|
|
243
|
-
const group = groupOf(groups, provider);
|
|
244
|
-
if (group === void 0) return false;
|
|
245
|
-
for (const write of providerGroupWrites(group)) if (write.field === "subagentMode") await sink.set("subagentMode", write.mode);
|
|
246
|
-
else if (write.value === void 0) await sink.unset(write.field);
|
|
247
|
-
else await sink.set(write.field, write.value);
|
|
248
|
-
return true;
|
|
249
|
-
}
|
|
250
192
|
//#endregion
|
|
251
193
|
//#region src/client/derived-config-form.ts
|
|
252
194
|
function deriveConfigForm(source, project, fields) {
|
|
@@ -267,11 +209,28 @@ window.__ModuleLoader__.load({
|
|
|
267
209
|
const sourceField = fields[field];
|
|
268
210
|
if (!(operation === "set" ? await source.set(sourceField, value) : await source.unset(sourceField))) throw new Error("settings-rejected");
|
|
269
211
|
};
|
|
212
|
+
/**
|
|
213
|
+
* Batch write: map every view field through the same `fields` table `set`/`unset`
|
|
214
|
+
* use, so one atomic source mutate can carry a whole row's worth of edits under
|
|
215
|
+
* a single revision fence instead of N independent fenced writes.
|
|
216
|
+
*/
|
|
217
|
+
const mutate = async (ops, expectedRevision) => {
|
|
218
|
+
const mapped = ops.map((op) => op.value === void 0 ? {
|
|
219
|
+
op: "unset",
|
|
220
|
+
path: [fields[op.field]]
|
|
221
|
+
} : {
|
|
222
|
+
op: "set",
|
|
223
|
+
path: [fields[op.field]],
|
|
224
|
+
value: op.value
|
|
225
|
+
});
|
|
226
|
+
if (!await source.mutate(mapped, expectedRevision)) throw new Error("settings-rejected");
|
|
227
|
+
};
|
|
270
228
|
return {
|
|
271
229
|
getSnapshot,
|
|
272
230
|
subscribe: (listener) => source.subscribe(listener),
|
|
273
231
|
set: (field, value) => write("set", field, value),
|
|
274
|
-
unset: (field) => write("unset", field)
|
|
232
|
+
unset: (field) => write("unset", field),
|
|
233
|
+
mutate
|
|
275
234
|
};
|
|
276
235
|
}
|
|
277
236
|
//#endregion
|
|
@@ -604,65 +563,20 @@ window.__ModuleLoader__.load({
|
|
|
604
563
|
models
|
|
605
564
|
};
|
|
606
565
|
}
|
|
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);
|
|
566
|
+
/** Load the model catalog backing every route editor, reloading on provider-order changes. */
|
|
567
|
+
function useSettingsCatalog(input) {
|
|
649
568
|
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]);
|
|
569
|
+
const [catalogError, setCatalogError] = (0, react.useState)();
|
|
659
570
|
(0, react.useEffect)(() => {
|
|
660
571
|
let live = true;
|
|
661
572
|
const load = () => {
|
|
662
573
|
input.loadCatalog().then((value) => {
|
|
663
|
-
if (live)
|
|
574
|
+
if (live) {
|
|
575
|
+
setGroups(value);
|
|
576
|
+
setCatalogError(void 0);
|
|
577
|
+
}
|
|
664
578
|
}).catch(() => {
|
|
665
|
-
if (live)
|
|
579
|
+
if (live) setCatalogError(input.t("catalogFailed"));
|
|
666
580
|
});
|
|
667
581
|
};
|
|
668
582
|
load();
|
|
@@ -678,63 +592,9 @@ window.__ModuleLoader__.load({
|
|
|
678
592
|
input.subscribeProviderOrder,
|
|
679
593
|
input.t
|
|
680
594
|
]);
|
|
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
595
|
return {
|
|
723
|
-
main,
|
|
724
|
-
subagent,
|
|
725
|
-
draft,
|
|
726
596
|
groups,
|
|
727
|
-
|
|
728
|
-
models,
|
|
729
|
-
efforts,
|
|
730
|
-
busy,
|
|
731
|
-
message,
|
|
732
|
-
disabled,
|
|
733
|
-
setProvider,
|
|
734
|
-
setModel,
|
|
735
|
-
setReasoningEffort,
|
|
736
|
-
reset,
|
|
737
|
-
save
|
|
597
|
+
catalogError
|
|
738
598
|
};
|
|
739
599
|
}
|
|
740
600
|
//#endregion
|
|
@@ -843,18 +703,6 @@ window.__ModuleLoader__.load({
|
|
|
843
703
|
strokeLinejoin: "round"
|
|
844
704
|
})
|
|
845
705
|
});
|
|
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
706
|
if (kind === "subagent") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
859
707
|
width: "18",
|
|
860
708
|
height: "18",
|
|
@@ -943,6 +791,23 @@ window.__ModuleLoader__.load({
|
|
|
943
791
|
strokeLinejoin: "round"
|
|
944
792
|
})]
|
|
945
793
|
});
|
|
794
|
+
if (kind === "sessionTitle") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
795
|
+
width: "18",
|
|
796
|
+
height: "18",
|
|
797
|
+
viewBox: "0 0 18 18",
|
|
798
|
+
fill: "none",
|
|
799
|
+
"aria-hidden": "true",
|
|
800
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
801
|
+
d: "M3 4.5h7.5l4.2 4.2-5.7 5.7L3 9.8V4.5Z",
|
|
802
|
+
stroke: "currentColor",
|
|
803
|
+
strokeLinejoin: "round"
|
|
804
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
805
|
+
cx: "6.2",
|
|
806
|
+
cy: "7.2",
|
|
807
|
+
r: "1.1",
|
|
808
|
+
stroke: "currentColor"
|
|
809
|
+
})]
|
|
810
|
+
});
|
|
946
811
|
return null;
|
|
947
812
|
}
|
|
948
813
|
function RouteCard({ title, summary, hint, icon, open, onToggle, disabled = false, badge, badgeWarn = false, trailing, expandable = true, children }) {
|
|
@@ -1067,17 +932,34 @@ window.__ModuleLoader__.load({
|
|
|
1067
932
|
]
|
|
1068
933
|
});
|
|
1069
934
|
}
|
|
935
|
+
/**
|
|
936
|
+
* One editable row draft.
|
|
937
|
+
*
|
|
938
|
+
* Only a PRISTINE draft follows the accepted snapshot. A draft the user has
|
|
939
|
+
* edited is never overwritten by an incoming snapshot — another tab's write, or
|
|
940
|
+
* the recovery read that follows a refused write, would otherwise discard the
|
|
941
|
+
* typed values and make the page look like it rolled back on its own. The user
|
|
942
|
+
* either saves the draft or cancels it back to the accepted value.
|
|
943
|
+
* @param snapshot - the accepted form snapshot for this row.
|
|
944
|
+
* @returns the draft, its setter, and a reset that re-adopts the accepted value.
|
|
945
|
+
*/
|
|
1070
946
|
function useDraft(snapshot) {
|
|
1071
947
|
const [draft, setDraft] = (0, react.useState)(snapshot.value);
|
|
948
|
+
const pristine = (0, react.useRef)(true);
|
|
1072
949
|
(0, react.useEffect)(() => {
|
|
950
|
+
if (!pristine.current) return;
|
|
1073
951
|
setDraft(snapshot.value);
|
|
1074
952
|
}, [snapshot.revision, snapshot.value]);
|
|
1075
953
|
return [
|
|
1076
954
|
draft,
|
|
1077
|
-
|
|
1078
|
-
|
|
955
|
+
(0, react.useCallback)((value) => {
|
|
956
|
+
pristine.current = false;
|
|
957
|
+
setDraft(value);
|
|
958
|
+
}, []),
|
|
959
|
+
(0, react.useCallback)(() => {
|
|
960
|
+
pristine.current = true;
|
|
1079
961
|
setDraft(snapshot.value);
|
|
1080
|
-
}
|
|
962
|
+
}, [snapshot.value])
|
|
1081
963
|
];
|
|
1082
964
|
}
|
|
1083
965
|
function routeName(groups, route) {
|
|
@@ -1091,6 +973,22 @@ window.__ModuleLoader__.load({
|
|
|
1091
973
|
const matched = matchCatalogModel(groups.find((group) => group.id === route.provider)?.models ?? [], route.model);
|
|
1092
974
|
return matched?.effort ?? matched?.model.reasoning?.defaultEffort;
|
|
1093
975
|
}
|
|
976
|
+
/**
|
|
977
|
+
* The effort caption for the subagent summary: a stored effort is the pinned
|
|
978
|
+
* one ("已固定 · High"); an unset effort follows the provider default and says
|
|
979
|
+
* so ("Provider 默认 · High"), using the same effort-name lookup the picker does.
|
|
980
|
+
*/
|
|
981
|
+
function subagentEffortSegment(t, matched, pinned, defaultEffort) {
|
|
982
|
+
const effortName = (id) => matched?.model.reasoning?.efforts.find((level) => level.id === id)?.name ?? id;
|
|
983
|
+
if (pinned !== "") return compact(t("effortPinned"), effortName(pinned));
|
|
984
|
+
return defaultEffort === void 0 ? t("providerDefaultShort") : compact(t("providerDefaultShort"), effortName(defaultEffort));
|
|
985
|
+
}
|
|
986
|
+
/** The catalog head route; capability switches pin here when turned On. */
|
|
987
|
+
function firstCatalogRoute(groups) {
|
|
988
|
+
const provider = groups[0];
|
|
989
|
+
const model = provider?.models[0];
|
|
990
|
+
return provider === void 0 || model === void 0 ? void 0 : selectRouteModel(groups, provider.id, model.id);
|
|
991
|
+
}
|
|
1094
992
|
function capabilityChoices(groups, route, providers, kind) {
|
|
1095
993
|
const choices = deriveRouteChoices(groups, route, providers);
|
|
1096
994
|
if (kind === "image" && route?.provider === "grok") {
|
|
@@ -1111,12 +1009,13 @@ window.__ModuleLoader__.load({
|
|
|
1111
1009
|
return choices;
|
|
1112
1010
|
}
|
|
1113
1011
|
function ModelSwitchSettings(props) {
|
|
1114
|
-
const
|
|
1115
|
-
const
|
|
1012
|
+
const { groups, catalogError } = useSettingsCatalog(props);
|
|
1013
|
+
const subagent = props.useSubagentSettings((value) => value);
|
|
1116
1014
|
const search = props.useSearchSettings((value) => value);
|
|
1117
1015
|
const image = props.useImageSettings((value) => value);
|
|
1118
1016
|
const autoReview = props.useAutoReviewSettings((value) => value);
|
|
1119
1017
|
const compaction = props.useCompactionSettings((value) => value);
|
|
1018
|
+
const sessionTitle = props.useTitleSettings((value) => value);
|
|
1120
1019
|
const switchSettings = props.useSwitchSettings((value) => value);
|
|
1121
1020
|
const [open, setOpen] = (0, react.useState)();
|
|
1122
1021
|
const [subagentDraft, setSubagentDraft, resetSubagent] = useDraft(subagent);
|
|
@@ -1124,6 +1023,7 @@ window.__ModuleLoader__.load({
|
|
|
1124
1023
|
const [imageDraft, setImageDraft, resetImage] = useDraft(image);
|
|
1125
1024
|
const [autoReviewDraft, setAutoReviewDraft, resetAutoReview] = useDraft(autoReview);
|
|
1126
1025
|
const [compactionDraft, setCompactionDraft, resetCompaction] = useDraft(compaction);
|
|
1026
|
+
const [titleDraft, setTitleDraft, resetTitle] = useDraft(sessionTitle);
|
|
1127
1027
|
const [busy, setBusy] = (0, react.useState)();
|
|
1128
1028
|
const [message, setMessage] = (0, react.useState)();
|
|
1129
1029
|
const [compactBusy, setCompactBusy] = (0, react.useState)(false);
|
|
@@ -1169,15 +1069,17 @@ window.__ModuleLoader__.load({
|
|
|
1169
1069
|
};
|
|
1170
1070
|
}, [loadSearchCapabilities]);
|
|
1171
1071
|
const providerGroups = normalizeProviderGroups(switchSettings.value?.providerGroups);
|
|
1172
|
-
/**
|
|
1072
|
+
/**
|
|
1073
|
+
* Seed an unsaved group from the live routes.
|
|
1074
|
+
*
|
|
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.
|
|
1078
|
+
*/
|
|
1173
1079
|
const seedGroupFromActive = () => {
|
|
1174
|
-
const
|
|
1175
|
-
provider:
|
|
1176
|
-
model:
|
|
1177
|
-
} : void 0;
|
|
1178
|
-
const review = autoReview.value !== void 0 && routePairConfigured(autoReview.value) ? {
|
|
1179
|
-
provider: autoReview.value.provider ?? "",
|
|
1180
|
-
model: autoReview.value.model ?? ""
|
|
1080
|
+
const title = sessionTitle.value !== void 0 && routePairConfigured(sessionTitle.value) ? {
|
|
1081
|
+
provider: sessionTitle.value.provider ?? "",
|
|
1082
|
+
model: sessionTitle.value.model ?? ""
|
|
1181
1083
|
} : void 0;
|
|
1182
1084
|
const fixedSubagent = subagent.value !== void 0 && subagent.value.mode === "fixed" && routePairConfigured(subagent.value) ? {
|
|
1183
1085
|
provider: subagent.value.provider ?? "",
|
|
@@ -1185,13 +1087,9 @@ window.__ModuleLoader__.load({
|
|
|
1185
1087
|
reasoningEffort: subagent.value.reasoningEffort
|
|
1186
1088
|
} : void 0;
|
|
1187
1089
|
return {
|
|
1188
|
-
...
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
},
|
|
1192
|
-
...review === void 0 ? {} : {
|
|
1193
|
-
autoReviewProvider: review.provider,
|
|
1194
|
-
autoReviewModel: review.model
|
|
1090
|
+
...title === void 0 ? {} : {
|
|
1091
|
+
titleProvider: title.provider,
|
|
1092
|
+
titleModel: title.model
|
|
1195
1093
|
},
|
|
1196
1094
|
...fixedSubagent === void 0 ? {} : {
|
|
1197
1095
|
subagentProvider: fixedSubagent.provider,
|
|
@@ -1208,11 +1106,16 @@ window.__ModuleLoader__.load({
|
|
|
1208
1106
|
}
|
|
1209
1107
|
setGroupDraft(groupOf(providerGroups, groupProvider) ?? seedGroupFromActive());
|
|
1210
1108
|
};
|
|
1109
|
+
/** Open the editor on the provider in use: the first configured group, else the catalog head. */
|
|
1211
1110
|
(0, react.useEffect)(() => {
|
|
1212
1111
|
if (groupProvider !== "") return;
|
|
1213
|
-
const initial =
|
|
1112
|
+
const initial = Object.keys(providerGroups)[0] ?? groups[0]?.id;
|
|
1214
1113
|
if (initial !== void 0 && initial.trim() !== "") setGroupProvider(initial);
|
|
1215
|
-
}, [
|
|
1114
|
+
}, [
|
|
1115
|
+
groupProvider,
|
|
1116
|
+
providerGroups,
|
|
1117
|
+
groups
|
|
1118
|
+
]);
|
|
1216
1119
|
(0, react.useEffect)(() => {
|
|
1217
1120
|
resyncGroupDraft();
|
|
1218
1121
|
}, [
|
|
@@ -1220,14 +1123,15 @@ window.__ModuleLoader__.load({
|
|
|
1220
1123
|
switchSettings.value,
|
|
1221
1124
|
subagent.value,
|
|
1222
1125
|
autoReview.value,
|
|
1223
|
-
compaction.value
|
|
1126
|
+
compaction.value,
|
|
1127
|
+
sessionTitle.value
|
|
1224
1128
|
]);
|
|
1225
1129
|
if ([
|
|
1226
|
-
|
|
1227
|
-
controller.subagent,
|
|
1130
|
+
subagent,
|
|
1228
1131
|
search,
|
|
1229
1132
|
image,
|
|
1230
|
-
switchSettings
|
|
1133
|
+
switchSettings,
|
|
1134
|
+
sessionTitle
|
|
1231
1135
|
].some((form) => form.mode === "memory")) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("main", {
|
|
1232
1136
|
className: ModelSwitchSettings_module_css_default.section,
|
|
1233
1137
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
|
|
@@ -1267,8 +1171,7 @@ window.__ModuleLoader__.load({
|
|
|
1267
1171
|
});
|
|
1268
1172
|
return efforts;
|
|
1269
1173
|
})();
|
|
1270
|
-
const defaultEffort =
|
|
1271
|
-
const mainEffectiveEffort = draft?.reasoningEffort ?? routeDefaultEffort(groups, draft);
|
|
1174
|
+
const defaultEffort = routeDefaultEffort(groups, subagentRoute);
|
|
1272
1175
|
const searchLive = searchSnapshot?.capabilities;
|
|
1273
1176
|
const searchGroups = searchGroupsFromCapabilities(searchLive);
|
|
1274
1177
|
const searchChoices = capabilityChoices(searchGroups, searchDraft, void 0, "search");
|
|
@@ -1278,179 +1181,233 @@ window.__ModuleLoader__.load({
|
|
|
1278
1181
|
const autoReviewOn = routePairConfigured(autoReview.value);
|
|
1279
1182
|
const compactionChoices = deriveRouteChoices(groups, compactionDraft);
|
|
1280
1183
|
const compactionOn = routePairConfigured(compaction.value);
|
|
1184
|
+
const titleChoices = deriveRouteChoices(groups, titleDraft);
|
|
1185
|
+
const titleOn = routePairConfigured(sessionTitle.value);
|
|
1281
1186
|
const synced = [
|
|
1282
|
-
main,
|
|
1283
1187
|
subagent,
|
|
1284
1188
|
search,
|
|
1285
1189
|
image,
|
|
1286
1190
|
autoReview,
|
|
1287
|
-
compaction
|
|
1191
|
+
compaction,
|
|
1192
|
+
sessionTitle
|
|
1288
1193
|
].every((snapshot) => snapshot.status === "ready");
|
|
1194
|
+
/**
|
|
1195
|
+
* Run one row save with a visible outcome that ALWAYS settles.
|
|
1196
|
+
*
|
|
1197
|
+
* A stalled write used to latch the page on "保存中…" forever, because `busy`
|
|
1198
|
+
* was only cleared by the settling promise. The 30s deadline reports the stall
|
|
1199
|
+
* instead; a late result is ignored by the race, so it can never overwrite a
|
|
1200
|
+
* newer draft either.
|
|
1201
|
+
* @param route - the row whose save is running, for busy and message routing.
|
|
1202
|
+
* @param operation - the single fenced write to perform.
|
|
1203
|
+
* @returns whether the write was accepted.
|
|
1204
|
+
*/
|
|
1289
1205
|
const run = async (route, operation) => {
|
|
1290
|
-
if (busy !== void 0) return;
|
|
1206
|
+
if (busy !== void 0) return false;
|
|
1207
|
+
const timeoutMs = 3e4;
|
|
1291
1208
|
setBusy(route);
|
|
1292
1209
|
setMessage(void 0);
|
|
1210
|
+
let timer;
|
|
1293
1211
|
try {
|
|
1294
|
-
await operation()
|
|
1212
|
+
await Promise.race([operation(), new Promise((_resolve, reject) => {
|
|
1213
|
+
timer = setTimeout(() => reject(new Error(props.t("saveTimeout"))), timeoutMs);
|
|
1214
|
+
})]);
|
|
1295
1215
|
setMessage({
|
|
1296
1216
|
route,
|
|
1297
1217
|
text: props.t("saved")
|
|
1298
1218
|
});
|
|
1219
|
+
return true;
|
|
1299
1220
|
} catch (error) {
|
|
1300
1221
|
setMessage({
|
|
1301
1222
|
route,
|
|
1302
1223
|
text: error instanceof Error ? error.message : props.t("requestFailed")
|
|
1303
1224
|
});
|
|
1225
|
+
return false;
|
|
1304
1226
|
} finally {
|
|
1227
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1305
1228
|
setBusy(void 0);
|
|
1306
1229
|
}
|
|
1307
1230
|
};
|
|
1308
1231
|
const saveSubagent = () => {
|
|
1309
1232
|
if (subagentDraft === void 0 || subagentDraft.mode !== "fixed") return;
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1233
|
+
const matched = subagentCatalogModel;
|
|
1234
|
+
const provider = (subagentDraft.provider ?? "").trim();
|
|
1235
|
+
const model = (matched?.model.id ?? subagentDraft.model ?? "").trim();
|
|
1236
|
+
if (provider === "" || model === "") return;
|
|
1237
|
+
const nextEffort = subagentDraft.reasoningEffort === "" ? void 0 : subagentDraft.reasoningEffort || matched?.effort;
|
|
1238
|
+
run("subagent", () => props.saveSubagent([
|
|
1239
|
+
{
|
|
1240
|
+
field: "provider",
|
|
1241
|
+
value: provider
|
|
1242
|
+
},
|
|
1243
|
+
{
|
|
1244
|
+
field: "model",
|
|
1245
|
+
value: model
|
|
1246
|
+
},
|
|
1247
|
+
{
|
|
1248
|
+
field: "reasoningEffort",
|
|
1249
|
+
value: nextEffort
|
|
1250
|
+
}
|
|
1251
|
+
]));
|
|
1318
1252
|
};
|
|
1319
1253
|
const persistSubagentEnabled = (enabled) => {
|
|
1320
1254
|
if (subagentDraft === void 0 || busy === "subagent" || !subagent.writable) return;
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
setMessage({
|
|
1334
|
-
route: "subagent",
|
|
1335
|
-
text: error instanceof Error ? error.message : props.t("requestFailed")
|
|
1255
|
+
const mode = subagentModeForEnabled(enabled);
|
|
1256
|
+
run("subagent", () => props.saveSubagent([{
|
|
1257
|
+
field: "mode",
|
|
1258
|
+
value: mode
|
|
1259
|
+
}])).then((accepted) => {
|
|
1260
|
+
if (!accepted) {
|
|
1261
|
+
resetSubagent();
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
setSubagentDraft({
|
|
1265
|
+
...subagentDraft,
|
|
1266
|
+
mode
|
|
1336
1267
|
});
|
|
1268
|
+
setOpen((current) => enabled ? "subagent" : current === "subagent" ? void 0 : current);
|
|
1337
1269
|
});
|
|
1338
1270
|
};
|
|
1339
|
-
|
|
1271
|
+
/** Both halves of one pinned capability route, written together in one mutation. */
|
|
1272
|
+
const pairOps = (next) => [{
|
|
1273
|
+
field: "provider",
|
|
1274
|
+
value: next.provider
|
|
1275
|
+
}, {
|
|
1276
|
+
field: "model",
|
|
1277
|
+
value: next.model
|
|
1278
|
+
}];
|
|
1279
|
+
const saveCapability = (route, next) => {
|
|
1340
1280
|
if (next === void 0) return;
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1281
|
+
const provider = (next.provider ?? "").trim();
|
|
1282
|
+
const model = (next.model ?? "").trim();
|
|
1283
|
+
if (provider === "" || model === "") return;
|
|
1284
|
+
run(route, () => props.saveCapability(route, [{
|
|
1285
|
+
field: "provider",
|
|
1286
|
+
value: provider
|
|
1287
|
+
}, {
|
|
1288
|
+
field: "model",
|
|
1289
|
+
value: model
|
|
1290
|
+
}]));
|
|
1345
1291
|
};
|
|
1346
1292
|
const saveAutoReview = () => {
|
|
1347
|
-
if (autoReviewDraft === void 0) return;
|
|
1348
|
-
run("autoReview",
|
|
1349
|
-
if (autoReview.value?.provider !== autoReviewDraft.provider) await props.setCapability("autoReview", "provider", autoReviewDraft.provider);
|
|
1350
|
-
if (autoReview.value?.model !== autoReviewDraft.model) await props.setCapability("autoReview", "model", autoReviewDraft.model);
|
|
1351
|
-
});
|
|
1293
|
+
if (autoReviewDraft === void 0 || (autoReviewDraft.provider ?? "").trim() === "" || (autoReviewDraft.model ?? "").trim() === "") return;
|
|
1294
|
+
run("autoReview", () => props.saveCapability("autoReview", pairOps(autoReviewDraft)));
|
|
1352
1295
|
};
|
|
1353
1296
|
const saveCompaction = () => {
|
|
1354
|
-
if (compactionDraft === void 0) return;
|
|
1355
|
-
run("compaction",
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1297
|
+
if (compactionDraft === void 0 || (compactionDraft.provider ?? "").trim() === "" || (compactionDraft.model ?? "").trim() === "") return;
|
|
1298
|
+
run("compaction", () => props.saveCapability("compaction", pairOps(compactionDraft)));
|
|
1299
|
+
};
|
|
1300
|
+
const saveTitle = () => {
|
|
1301
|
+
if (titleDraft === void 0 || (titleDraft.provider ?? "").trim() === "" || (titleDraft.model ?? "").trim() === "") return;
|
|
1302
|
+
run("sessionTitle", () => props.saveCapability("title", pairOps(titleDraft)));
|
|
1359
1303
|
};
|
|
1360
1304
|
/**
|
|
1361
1305
|
* The reviewer switch: Off unsets both stored fields (follow the session
|
|
1362
|
-
* model); On
|
|
1363
|
-
* and stays editable below. A failed write reports, never
|
|
1306
|
+
* model); On pins the route to the catalog head so the pin takes effect
|
|
1307
|
+
* immediately and stays editable below. A failed write reports, never
|
|
1308
|
+
* flips silently.
|
|
1364
1309
|
*/
|
|
1365
1310
|
const persistAutoReviewEnabled = (enabled) => {
|
|
1366
1311
|
if (busy === "autoReview" || !autoReview.writable || autoReview.value === void 0) return;
|
|
1367
1312
|
if (!enabled) {
|
|
1368
1313
|
setOpen((current) => current === "autoReview" ? void 0 : current);
|
|
1369
|
-
run("autoReview",
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
}
|
|
1314
|
+
run("autoReview", () => props.saveCapability("autoReview", [{
|
|
1315
|
+
field: "provider",
|
|
1316
|
+
value: void 0
|
|
1317
|
+
}, {
|
|
1318
|
+
field: "model",
|
|
1319
|
+
value: void 0
|
|
1320
|
+
}]));
|
|
1373
1321
|
return;
|
|
1374
1322
|
}
|
|
1375
|
-
const seed =
|
|
1376
|
-
if (seed === void 0
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
(async () => {
|
|
1380
|
-
if (autoReview.value?.provider !== seed.provider) await props.setCapability("autoReview", "provider", seed.provider);
|
|
1381
|
-
if (autoReview.value?.model !== seed.model) await props.setCapability("autoReview", "model", seed.model);
|
|
1382
|
-
})().then(() => {
|
|
1323
|
+
const seed = firstCatalogRoute(groups);
|
|
1324
|
+
if (seed === void 0) return;
|
|
1325
|
+
run("autoReview", () => props.saveCapability("autoReview", pairOps(seed))).then((accepted) => {
|
|
1326
|
+
if (!accepted) return;
|
|
1383
1327
|
setAutoReviewDraft({
|
|
1384
1328
|
provider: seed.provider,
|
|
1385
1329
|
model: seed.model
|
|
1386
1330
|
});
|
|
1387
|
-
setBusy(void 0);
|
|
1388
1331
|
setOpen("autoReview");
|
|
1389
|
-
}, (error) => {
|
|
1390
|
-
setBusy(void 0);
|
|
1391
|
-
setMessage({
|
|
1392
|
-
route: "autoReview",
|
|
1393
|
-
text: error instanceof Error ? error.message : props.t("requestFailed")
|
|
1394
|
-
});
|
|
1395
1332
|
});
|
|
1396
1333
|
};
|
|
1397
1334
|
/**
|
|
1398
1335
|
* 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.
|
|
1336
|
+
* the conversation target); On pins the route to the catalog head so the
|
|
1337
|
+
* pin takes effect immediately and stays editable below. A failed write
|
|
1338
|
+
* reports, never flips silently.
|
|
1402
1339
|
*/
|
|
1403
1340
|
const persistCompactionEnabled = (enabled) => {
|
|
1404
1341
|
if (busy === "compaction" || !compaction.writable || compaction.value === void 0) return;
|
|
1405
1342
|
if (!enabled) {
|
|
1406
1343
|
setOpen((current) => current === "compaction" ? void 0 : current);
|
|
1407
|
-
run("compaction",
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
}
|
|
1344
|
+
run("compaction", () => props.saveCapability("compaction", [{
|
|
1345
|
+
field: "provider",
|
|
1346
|
+
value: void 0
|
|
1347
|
+
}, {
|
|
1348
|
+
field: "model",
|
|
1349
|
+
value: void 0
|
|
1350
|
+
}]));
|
|
1411
1351
|
return;
|
|
1412
1352
|
}
|
|
1413
|
-
const seed =
|
|
1414
|
-
if (seed === void 0
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
(async () => {
|
|
1418
|
-
if (compaction.value?.provider !== seed.provider) await props.setCapability("compaction", "provider", seed.provider);
|
|
1419
|
-
if (compaction.value?.model !== seed.model) await props.setCapability("compaction", "model", seed.model);
|
|
1420
|
-
})().then(() => {
|
|
1353
|
+
const seed = firstCatalogRoute(groups);
|
|
1354
|
+
if (seed === void 0) return;
|
|
1355
|
+
run("compaction", () => props.saveCapability("compaction", pairOps(seed))).then((accepted) => {
|
|
1356
|
+
if (!accepted) return;
|
|
1421
1357
|
setCompactionDraft({
|
|
1422
1358
|
provider: seed.provider,
|
|
1423
1359
|
model: seed.model
|
|
1424
1360
|
});
|
|
1425
|
-
setBusy(void 0);
|
|
1426
1361
|
setOpen("compaction");
|
|
1427
|
-
}
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1362
|
+
});
|
|
1363
|
+
};
|
|
1364
|
+
/**
|
|
1365
|
+
* The session-title switch: Off unsets both stored fields (naming follows
|
|
1366
|
+
* the session's own logged route); On pins the route to the catalog head so
|
|
1367
|
+
* the pin takes effect immediately and stays editable below. A failed write
|
|
1368
|
+
* reports, never flips silently.
|
|
1369
|
+
*/
|
|
1370
|
+
const persistTitleEnabled = (enabled) => {
|
|
1371
|
+
if (busy === "sessionTitle" || !sessionTitle.writable || sessionTitle.value === void 0) return;
|
|
1372
|
+
if (!enabled) {
|
|
1373
|
+
setOpen((current) => current === "sessionTitle" ? void 0 : current);
|
|
1374
|
+
run("sessionTitle", () => props.saveCapability("title", [{
|
|
1375
|
+
field: "provider",
|
|
1376
|
+
value: void 0
|
|
1377
|
+
}, {
|
|
1378
|
+
field: "model",
|
|
1379
|
+
value: void 0
|
|
1380
|
+
}]));
|
|
1381
|
+
return;
|
|
1382
|
+
}
|
|
1383
|
+
const seed = firstCatalogRoute(groups);
|
|
1384
|
+
if (seed === void 0) return;
|
|
1385
|
+
run("sessionTitle", () => props.saveCapability("title", pairOps(seed))).then((accepted) => {
|
|
1386
|
+
if (!accepted) return;
|
|
1387
|
+
setTitleDraft({
|
|
1388
|
+
provider: seed.provider,
|
|
1389
|
+
model: seed.model
|
|
1432
1390
|
});
|
|
1391
|
+
setOpen("sessionTitle");
|
|
1433
1392
|
});
|
|
1434
1393
|
};
|
|
1435
|
-
const mainSummary = draft === void 0 ? props.t("loading") : compact(routeName(groups, draft), mainEffectiveEffort);
|
|
1436
1394
|
const subagentRole = subagentDraft?.mode === "fixed" && subagentDraft.provider !== void 0 && subagentDraft.provider !== "" ? props.providerRoleOf?.(subagentDraft.provider) : void 0;
|
|
1437
1395
|
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,
|
|
1396
|
+
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
1397
|
const subagentDisabled = subagent.status !== "ready" || !subagent.writable || subagentDraft === void 0 || busy === "subagent" || !subagentOn || (subagentDraft.provider ?? "").trim() === "" || (subagentDraft.model ?? "").trim() === "";
|
|
1440
1398
|
const capabilityDisabled = (route, snapshot, next) => snapshot.status !== "ready" || !snapshot.writable || next === void 0 || busy === route || (next.provider ?? "").trim() === "" || (next.model ?? "").trim() === "";
|
|
1441
1399
|
const autoReviewDisabled = autoReview.status !== "ready" || !autoReview.writable || autoReviewDraft === void 0 || busy === "autoReview" || !autoReviewOn || (autoReviewDraft.provider ?? "").trim() === "" || (autoReviewDraft.model ?? "").trim() === "";
|
|
1442
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() === "";
|
|
1443
1402
|
const configuredGroupCount = Object.keys(providerGroups).length;
|
|
1444
1403
|
const groupControlsDisabled = busy === "providerGroups" || switchSettings.status !== "ready" || !switchSettings.writable;
|
|
1445
1404
|
const groupProviderChoices = deriveRouteChoices(groups, groupProvider === "" ? void 0 : { provider: groupProvider }).providers.map((choice) => groupOf(providerGroups, choice.id) === void 0 ? choice : {
|
|
1446
1405
|
...choice,
|
|
1447
1406
|
name: choice.name + " ✓"
|
|
1448
1407
|
});
|
|
1449
|
-
const
|
|
1450
|
-
const groupReview = groupDraft === void 0 ? void 0 : draftPair(groupDraft.autoReviewProvider, groupDraft.autoReviewModel);
|
|
1408
|
+
const groupTitle = groupDraft === void 0 ? void 0 : draftPair(groupDraft.titleProvider, groupDraft.titleModel);
|
|
1451
1409
|
const groupSubagent = groupDraft === void 0 ? void 0 : draftPair(groupDraft.subagentProvider, groupDraft.subagentModel);
|
|
1452
|
-
const
|
|
1453
|
-
const groupReviewChoices = deriveRouteChoices(groups, groupReview);
|
|
1410
|
+
const groupTitleChoices = deriveRouteChoices(groups, groupTitle);
|
|
1454
1411
|
const groupSubagentChoices = deriveRouteChoices(groups, groupSubagent);
|
|
1455
1412
|
const groupSubMatched = matchCatalogModel(groups.find((group) => group.id === (groupDraft?.subagentProvider ?? ""))?.models ?? [], groupDraft?.subagentModel ?? "");
|
|
1456
1413
|
const groupSubEfforts = (() => {
|
|
@@ -1465,41 +1422,50 @@ window.__ModuleLoader__.load({
|
|
|
1465
1422
|
});
|
|
1466
1423
|
return efforts;
|
|
1467
1424
|
})();
|
|
1468
|
-
/**
|
|
1425
|
+
/**
|
|
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.
|
|
1434
|
+
*/
|
|
1469
1435
|
const enableGroupCapability = (kind) => {
|
|
1436
|
+
const active = seedGroupFromActive();
|
|
1437
|
+
const seed = firstCatalogRoute(groups);
|
|
1470
1438
|
setGroupDraft((draft) => {
|
|
1471
|
-
|
|
1472
|
-
if (
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1439
|
+
if (draft === void 0) return draft;
|
|
1440
|
+
if (kind === "sessionTitle") {
|
|
1441
|
+
const provider = active.titleProvider ?? seed?.provider;
|
|
1442
|
+
const model = active.titleModel ?? seed?.model;
|
|
1443
|
+
if (provider === void 0 || model === void 0) return draft;
|
|
1444
|
+
return {
|
|
1445
|
+
...draft,
|
|
1446
|
+
titleProvider: provider,
|
|
1447
|
+
titleModel: model
|
|
1448
|
+
};
|
|
1449
|
+
}
|
|
1450
|
+
const provider = active.subagentProvider ?? seed?.provider;
|
|
1451
|
+
const model = active.subagentModel ?? seed?.model;
|
|
1452
|
+
if (provider === void 0 || model === void 0) return draft;
|
|
1453
|
+
const effort = active.subagentProvider !== void 0 ? active.subagentReasoningEffort : seed?.reasoningEffort;
|
|
1483
1454
|
return {
|
|
1484
1455
|
...draft,
|
|
1485
|
-
subagentProvider:
|
|
1486
|
-
subagentModel:
|
|
1487
|
-
...
|
|
1456
|
+
subagentProvider: provider,
|
|
1457
|
+
subagentModel: model,
|
|
1458
|
+
...effort === void 0 || effort === "" ? {} : { subagentReasoningEffort: effort }
|
|
1488
1459
|
};
|
|
1489
1460
|
});
|
|
1490
1461
|
};
|
|
1491
1462
|
const disableGroupCapability = (kind) => {
|
|
1492
1463
|
setGroupDraft((draft) => {
|
|
1493
1464
|
if (draft === void 0) return draft;
|
|
1494
|
-
if (kind === "
|
|
1495
|
-
...draft,
|
|
1496
|
-
compactProvider: "",
|
|
1497
|
-
compactModel: ""
|
|
1498
|
-
};
|
|
1499
|
-
if (kind === "autoReview") return {
|
|
1465
|
+
if (kind === "sessionTitle") return {
|
|
1500
1466
|
...draft,
|
|
1501
|
-
|
|
1502
|
-
|
|
1467
|
+
titleProvider: "",
|
|
1468
|
+
titleModel: ""
|
|
1503
1469
|
};
|
|
1504
1470
|
return {
|
|
1505
1471
|
...draft,
|
|
@@ -1532,6 +1498,10 @@ window.__ModuleLoader__.load({
|
|
|
1532
1498
|
className: ModelSwitchSettings_module_css_default.saved,
|
|
1533
1499
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", { className: ModelSwitchSettings_module_css_default.savedDot }), synced ? props.t("settingsSynced") : props.t("loading")]
|
|
1534
1500
|
}),
|
|
1501
|
+
catalogError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1502
|
+
className: cx(ModelSwitchSettings_module_css_default.hint, ModelSwitchSettings_module_css_default.message),
|
|
1503
|
+
children: catalogError
|
|
1504
|
+
}),
|
|
1535
1505
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1536
1506
|
className: ModelSwitchSettings_module_css_default.group,
|
|
1537
1507
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
@@ -1597,150 +1567,92 @@ window.__ModuleLoader__.load({
|
|
|
1597
1567
|
}),
|
|
1598
1568
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1599
1569
|
className: ModelSwitchSettings_module_css_default.group,
|
|
1600
|
-
children: [
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1570
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
1571
|
+
className: ModelSwitchSettings_module_css_default.groupLabel,
|
|
1572
|
+
children: props.t("conversationRoutes")
|
|
1573
|
+
}), props.capabilities.centralSubagentRouting.available ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteCard, {
|
|
1574
|
+
title: props.t("subagent"),
|
|
1575
|
+
summary: subagentSummary,
|
|
1576
|
+
hint: props.t("subagentHelp"),
|
|
1577
|
+
icon: "subagent",
|
|
1578
|
+
open: open === "subagent" && subagentOn,
|
|
1579
|
+
onToggle: () => {
|
|
1580
|
+
if (subagentOn) toggle("subagent");
|
|
1581
|
+
},
|
|
1582
|
+
expandable: subagentOn,
|
|
1583
|
+
trailing: subagentDraft === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
1584
|
+
checked: subagentOn,
|
|
1585
|
+
disabled: busy === "subagent" || !subagent.writable,
|
|
1586
|
+
label: props.t("subagent"),
|
|
1587
|
+
onChange: persistSubagentEnabled
|
|
1604
1588
|
}),
|
|
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
|
-
]
|
|
1589
|
+
children: subagentDraft === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1590
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
1591
|
+
children: props.t("loading")
|
|
1592
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1593
|
+
className: ModelSwitchSettings_module_css_default.formGrid,
|
|
1594
|
+
children: [
|
|
1595
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1596
|
+
label: props.t("provider"),
|
|
1597
|
+
value: subagentDraft.provider ?? "",
|
|
1598
|
+
disabled: busy === "subagent" || !subagent.writable,
|
|
1599
|
+
choices: subagentChoices.providers,
|
|
1600
|
+
onChange: (provider) => {
|
|
1601
|
+
const first = groups.find((group) => group.id === provider)?.models[0];
|
|
1602
|
+
setSubagentDraft({
|
|
1603
|
+
mode: "fixed",
|
|
1604
|
+
...selectRouteModel(groups, provider, first?.id ?? subagentDraft.model ?? "")
|
|
1605
|
+
});
|
|
1606
|
+
}
|
|
1643
1607
|
}),
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1608
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1609
|
+
label: props.t("model"),
|
|
1610
|
+
value: subagentCatalogModel?.model.id ?? subagentDraft.model ?? "",
|
|
1611
|
+
disabled: busy === "subagent" || !subagent.writable,
|
|
1612
|
+
choices: subagentChoices.models,
|
|
1613
|
+
onChange: (model) => {
|
|
1614
|
+
setSubagentDraft({
|
|
1615
|
+
mode: "fixed",
|
|
1616
|
+
...selectRouteModel(groups, subagentDraft.provider ?? "", model)
|
|
1617
|
+
});
|
|
1618
|
+
}
|
|
1619
|
+
}),
|
|
1620
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1621
|
+
label: props.t("effort"),
|
|
1622
|
+
value: subagentDraft.reasoningEffort || subagentCatalogModel?.effort || "",
|
|
1623
|
+
disabled: busy === "subagent" || !subagent.writable,
|
|
1624
|
+
choices: subagentEfforts,
|
|
1625
|
+
onChange: (effort) => {
|
|
1626
|
+
const matched = subagentCatalogModel;
|
|
1627
|
+
setSubagentDraft({
|
|
1628
|
+
...subagentDraft,
|
|
1629
|
+
...matched?.model.id === void 0 ? {} : { model: matched.model.id },
|
|
1630
|
+
reasoningEffort: effort
|
|
1631
|
+
});
|
|
1656
1632
|
}
|
|
1657
1633
|
})
|
|
1658
|
-
]
|
|
1659
|
-
}),
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
if (subagentOn) toggle("subagent");
|
|
1634
|
+
]
|
|
1635
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Actions, {
|
|
1636
|
+
t: props.t,
|
|
1637
|
+
busy: busy === "subagent",
|
|
1638
|
+
disabled: subagentDisabled,
|
|
1639
|
+
...message?.route === "subagent" ? { message: message.text } : {},
|
|
1640
|
+
onCancel: () => {
|
|
1641
|
+
resetSubagent();
|
|
1642
|
+
setMessage(void 0);
|
|
1668
1643
|
},
|
|
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
|
-
]
|
|
1644
|
+
onSave: saveSubagent
|
|
1645
|
+
})] })
|
|
1646
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteCard, {
|
|
1647
|
+
title: props.t("subagent"),
|
|
1648
|
+
summary: unavailable("centralSubagentRouting"),
|
|
1649
|
+
icon: "subagent",
|
|
1650
|
+
open: false,
|
|
1651
|
+
onToggle: () => {},
|
|
1652
|
+
disabled: true,
|
|
1653
|
+
badge: props.t("unavailable"),
|
|
1654
|
+
badgeWarn: true
|
|
1655
|
+
})]
|
|
1744
1656
|
}),
|
|
1745
1657
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1746
1658
|
className: ModelSwitchSettings_module_css_default.group,
|
|
@@ -1806,7 +1718,7 @@ window.__ModuleLoader__.load({
|
|
|
1806
1718
|
setMessage(void 0);
|
|
1807
1719
|
},
|
|
1808
1720
|
onSave: () => {
|
|
1809
|
-
saveCapability("search",
|
|
1721
|
+
saveCapability("search", searchDraft);
|
|
1810
1722
|
}
|
|
1811
1723
|
})
|
|
1812
1724
|
] })
|
|
@@ -1873,7 +1785,7 @@ window.__ModuleLoader__.load({
|
|
|
1873
1785
|
setMessage(void 0);
|
|
1874
1786
|
},
|
|
1875
1787
|
onSave: () => {
|
|
1876
|
-
saveCapability("image",
|
|
1788
|
+
saveCapability("image", imageDraft);
|
|
1877
1789
|
}
|
|
1878
1790
|
})
|
|
1879
1791
|
] })
|
|
@@ -2014,6 +1926,70 @@ window.__ModuleLoader__.load({
|
|
|
2014
1926
|
onSave: saveCompaction
|
|
2015
1927
|
})
|
|
2016
1928
|
] })
|
|
1929
|
+
}),
|
|
1930
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteCard, {
|
|
1931
|
+
title: props.t("sessionTitle"),
|
|
1932
|
+
summary: titleDraft === void 0 ? props.t("loading") : titleOn ? routeName(groups, titleDraft) : props.t("titleFollow"),
|
|
1933
|
+
hint: props.t("titleHelp"),
|
|
1934
|
+
icon: "sessionTitle",
|
|
1935
|
+
open: open === "sessionTitle" && titleOn,
|
|
1936
|
+
onToggle: () => {
|
|
1937
|
+
if (titleOn) toggle("sessionTitle");
|
|
1938
|
+
},
|
|
1939
|
+
expandable: titleOn,
|
|
1940
|
+
trailing: titleDraft === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
1941
|
+
checked: titleOn,
|
|
1942
|
+
disabled: busy === "sessionTitle" || !sessionTitle.writable,
|
|
1943
|
+
label: props.t("sessionTitle"),
|
|
1944
|
+
onChange: persistTitleEnabled
|
|
1945
|
+
}),
|
|
1946
|
+
children: titleDraft === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1947
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
1948
|
+
children: props.t("loading")
|
|
1949
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1950
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1951
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
1952
|
+
children: props.t("titleRouteHelp")
|
|
1953
|
+
}),
|
|
1954
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1955
|
+
className: ModelSwitchSettings_module_css_default.formGrid,
|
|
1956
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1957
|
+
label: props.t("provider"),
|
|
1958
|
+
value: titleDraft.provider ?? "",
|
|
1959
|
+
disabled: busy === "sessionTitle" || !sessionTitle.writable,
|
|
1960
|
+
choices: titleChoices.providers,
|
|
1961
|
+
onChange: (provider) => {
|
|
1962
|
+
const first = groups.find((group) => group.id === provider)?.models[0];
|
|
1963
|
+
setTitleDraft({
|
|
1964
|
+
provider,
|
|
1965
|
+
...first === void 0 ? {} : { model: first.id }
|
|
1966
|
+
});
|
|
1967
|
+
}
|
|
1968
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1969
|
+
label: props.t("model"),
|
|
1970
|
+
value: titleDraft.model ?? "",
|
|
1971
|
+
disabled: busy === "sessionTitle" || !sessionTitle.writable,
|
|
1972
|
+
choices: titleChoices.models,
|
|
1973
|
+
onChange: (model) => {
|
|
1974
|
+
setTitleDraft({
|
|
1975
|
+
...titleDraft,
|
|
1976
|
+
model
|
|
1977
|
+
});
|
|
1978
|
+
}
|
|
1979
|
+
})]
|
|
1980
|
+
}),
|
|
1981
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Actions, {
|
|
1982
|
+
t: props.t,
|
|
1983
|
+
busy: busy === "sessionTitle",
|
|
1984
|
+
disabled: titleDisabled,
|
|
1985
|
+
...message?.route === "sessionTitle" ? { message: message.text } : {},
|
|
1986
|
+
onCancel: () => {
|
|
1987
|
+
resetTitle();
|
|
1988
|
+
setMessage(void 0);
|
|
1989
|
+
},
|
|
1990
|
+
onSave: saveTitle
|
|
1991
|
+
})
|
|
1992
|
+
] })
|
|
2017
1993
|
})
|
|
2018
1994
|
]
|
|
2019
1995
|
}),
|
|
@@ -2057,94 +2033,45 @@ window.__ModuleLoader__.load({
|
|
|
2057
2033
|
className: ModelSwitchSettings_module_css_default.rowCopy,
|
|
2058
2034
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2059
2035
|
className: ModelSwitchSettings_module_css_default.rowTitle,
|
|
2060
|
-
children: props.t("
|
|
2036
|
+
children: props.t("sessionTitle")
|
|
2061
2037
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2062
2038
|
className: ModelSwitchSettings_module_css_default.hint,
|
|
2063
|
-
children:
|
|
2039
|
+
children: groupTitle === void 0 ? props.t("titleFollow") : routeName(groups, groupTitle)
|
|
2064
2040
|
})]
|
|
2065
2041
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
2066
|
-
checked:
|
|
2042
|
+
checked: groupTitle !== void 0,
|
|
2067
2043
|
disabled: groupControlsDisabled,
|
|
2068
|
-
label: props.t("
|
|
2044
|
+
label: props.t("sessionTitle"),
|
|
2069
2045
|
onChange: (value) => {
|
|
2070
|
-
if (value) enableGroupCapability("
|
|
2071
|
-
else disableGroupCapability("
|
|
2046
|
+
if (value) enableGroupCapability("sessionTitle");
|
|
2047
|
+
else disableGroupCapability("sessionTitle");
|
|
2072
2048
|
}
|
|
2073
2049
|
})]
|
|
2074
2050
|
}),
|
|
2075
|
-
|
|
2051
|
+
groupTitle === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2076
2052
|
className: ModelSwitchSettings_module_css_default.formGrid,
|
|
2077
2053
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2078
2054
|
label: props.t("provider"),
|
|
2079
|
-
value:
|
|
2055
|
+
value: groupTitle.provider,
|
|
2080
2056
|
disabled: groupControlsDisabled,
|
|
2081
|
-
choices:
|
|
2057
|
+
choices: groupTitleChoices.providers,
|
|
2082
2058
|
onChange: (provider) => {
|
|
2083
2059
|
const first = groups.find((item) => item.id === provider)?.models[0];
|
|
2084
2060
|
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
2085
2061
|
...draft,
|
|
2086
|
-
|
|
2087
|
-
|
|
2062
|
+
titleProvider: provider,
|
|
2063
|
+
titleModel: first?.id ?? ""
|
|
2088
2064
|
});
|
|
2089
2065
|
}
|
|
2090
2066
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2091
2067
|
label: props.t("model"),
|
|
2092
|
-
value:
|
|
2068
|
+
value: groupTitle.model,
|
|
2093
2069
|
disabled: groupControlsDisabled,
|
|
2094
|
-
choices:
|
|
2070
|
+
choices: groupTitleChoices.models,
|
|
2095
2071
|
onChange: (model) => {
|
|
2096
2072
|
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
2097
2073
|
...draft,
|
|
2098
|
-
|
|
2099
|
-
});
|
|
2100
|
-
}
|
|
2101
|
-
})]
|
|
2102
|
-
}),
|
|
2103
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2104
|
-
className: ModelSwitchSettings_module_css_default.rowCard,
|
|
2105
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2106
|
-
className: ModelSwitchSettings_module_css_default.rowCopy,
|
|
2107
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2108
|
-
className: ModelSwitchSettings_module_css_default.rowTitle,
|
|
2109
|
-
children: props.t("autoReview")
|
|
2110
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2111
|
-
className: ModelSwitchSettings_module_css_default.hint,
|
|
2112
|
-
children: groupReview === void 0 ? props.t("autoReviewFollow") : routeName(groups, groupReview)
|
|
2113
|
-
})]
|
|
2114
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
2115
|
-
checked: groupReview !== void 0,
|
|
2116
|
-
disabled: groupControlsDisabled,
|
|
2117
|
-
label: props.t("autoReview"),
|
|
2118
|
-
onChange: (value) => {
|
|
2119
|
-
if (value) enableGroupCapability("autoReview");
|
|
2120
|
-
else disableGroupCapability("autoReview");
|
|
2121
|
-
}
|
|
2122
|
-
})]
|
|
2123
|
-
}),
|
|
2124
|
-
groupReview === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2125
|
-
className: ModelSwitchSettings_module_css_default.formGrid,
|
|
2126
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2127
|
-
label: props.t("provider"),
|
|
2128
|
-
value: groupReview.provider,
|
|
2129
|
-
disabled: groupControlsDisabled,
|
|
2130
|
-
choices: groupReviewChoices.providers,
|
|
2131
|
-
onChange: (provider) => {
|
|
2132
|
-
const first = groups.find((item) => item.id === provider)?.models[0];
|
|
2133
|
-
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
2134
|
-
...draft,
|
|
2135
|
-
autoReviewProvider: provider,
|
|
2136
|
-
autoReviewModel: first?.id ?? ""
|
|
2137
|
-
});
|
|
2138
|
-
}
|
|
2139
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2140
|
-
label: props.t("model"),
|
|
2141
|
-
value: groupReview.model,
|
|
2142
|
-
disabled: groupControlsDisabled,
|
|
2143
|
-
choices: groupReviewChoices.models,
|
|
2144
|
-
onChange: (model) => {
|
|
2145
|
-
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
2146
|
-
...draft,
|
|
2147
|
-
autoReviewModel: model
|
|
2074
|
+
titleModel: model
|
|
2148
2075
|
});
|
|
2149
2076
|
}
|
|
2150
2077
|
})]
|
|
@@ -2248,22 +2175,21 @@ window.__ModuleLoader__.load({
|
|
|
2248
2175
|
const zh$1 = {
|
|
2249
2176
|
nav: "模型切换",
|
|
2250
2177
|
title: "模型切换",
|
|
2251
|
-
subtitle: "
|
|
2178
|
+
subtitle: "为子代理与能力路由设置固定模型;当前会话模型用输入框旁的选择器直接切换。",
|
|
2252
2179
|
conversationRoutes: "对话路由",
|
|
2253
2180
|
capabilityRoutes: "能力路由",
|
|
2254
2181
|
sendProtection: "发送保护",
|
|
2255
2182
|
settingsSynced: "设置已同步",
|
|
2256
2183
|
remoteSettingsUnavailable: "官方 DSH 在非本机浏览器禁用配置读取与写入;请在 Host 的 127.0.0.1 页面打开设置。",
|
|
2257
|
-
defaultBadge: "默认",
|
|
2258
2184
|
cancel: "取消",
|
|
2259
2185
|
compactOnSwitch: "切换时压缩",
|
|
2260
2186
|
compactOnSwitchHelp: "发送且换了模型时,先按目标窗口检查上下文;超了就用切换前的模型压一次历史,再继续请求。只选模型、不发送,不会压缩。官方自动压缩和 /compact 不受影响。压缩会产生额外模型用量,摘要可能省略细节。",
|
|
2261
|
-
main: "主模型",
|
|
2262
2187
|
provider: "提供商",
|
|
2263
2188
|
model: "模型",
|
|
2264
2189
|
providerDefault: "使用提供商默认值",
|
|
2265
2190
|
providerDefaultShort: "Provider 默认",
|
|
2266
2191
|
effort: "推理强度",
|
|
2192
|
+
effortPinned: "已固定",
|
|
2267
2193
|
save: "保存",
|
|
2268
2194
|
saving: "保存中…",
|
|
2269
2195
|
saved: "已保存",
|
|
@@ -2277,10 +2203,8 @@ window.__ModuleLoader__.load({
|
|
|
2277
2203
|
imageHelp: "统一 generate_image 会调用所选 Codex 或 Grok Adapter;Provider 原有图片工具仍保留。",
|
|
2278
2204
|
unavailable: "未接入",
|
|
2279
2205
|
loading: "正在加载设置…",
|
|
2280
|
-
readonly: "设置为只读",
|
|
2281
2206
|
requestFailed: "保存失败",
|
|
2282
2207
|
catalogFailed: "无法加载模型目录。",
|
|
2283
|
-
conflict: "设置已在其他位置更改,请检查最新值后重试。",
|
|
2284
2208
|
autoReview: "自动审查模型",
|
|
2285
2209
|
autoReviewFollow: "跟随会话模型",
|
|
2286
2210
|
autoReviewHelp: "为官方 auto-review(Auto 权限预设的逐调用审查)指定审查模型;需启用官方 auto-review 能力包。关闭时审查跟随当前会话模型。",
|
|
@@ -2289,14 +2213,18 @@ window.__ModuleLoader__.load({
|
|
|
2289
2213
|
compactionFollow: "跟随会话模型",
|
|
2290
2214
|
compactionHelp: "为官方压缩(/compact 与官方自动压缩)指定摘要模型;关闭(两个字段都留空)时使用官方默认,即当前会话的目标模型。",
|
|
2291
2215
|
compactionRouteHelp: "摘要请求固定使用这里的 Provider/模型,与会话目标模型无关;保存后对下一次压缩立即生效。保存时若恰有压缩进行中,可能被打断并在下次重试。",
|
|
2216
|
+
sessionTitle: "会话命名模型",
|
|
2217
|
+
titleFollow: "跟随会话模型",
|
|
2218
|
+
titleHelp: "为官方自动命名(新会话首条消息后生成标题)指定模型;关闭(两个字段都留空)时命名跟随当前会话模型。",
|
|
2219
|
+
titleRouteHelp: "命名请求固定使用这里的 Provider/模型(不带推理强度);关闭时跟随会话模型。保存后立即对下一次命名生效,不会中断正在进行的命名,会话日志记录的也是实际使用的模型。",
|
|
2292
2220
|
providerGroups: "供应商配置组",
|
|
2293
2221
|
providerGroupsCard: "按供应商预设路由",
|
|
2294
|
-
providerGroupsSummary: "
|
|
2295
|
-
providerGroupsHelp: "
|
|
2296
|
-
providerGroupsBodyHelp: "
|
|
2222
|
+
providerGroupsSummary: "使用该供应商的会话自动采用对应组",
|
|
2223
|
+
providerGroupsHelp: "为会话命名与子代理按供应商分别保存路由。",
|
|
2224
|
+
providerGroupsBodyHelp: "每个供应商保存一组会话命名与子代理路由。会话自身的供应商与该组一致时,该会话的命名与子代理按组解析,否则回落到上方卡片里的全局设置。切换某个会话的模型不会写入任何全局设置,因此不同供应商的多个会话互不影响。压缩与自动审查仍是全局卡片:压缩事件要如实记录实际写入摘要的模型,审查的拦截点没有会话身份。",
|
|
2297
2225
|
providerGroupPick: "选择供应商以编辑其配置组。",
|
|
2298
2226
|
providerGroupDelete: "删除该组",
|
|
2299
|
-
|
|
2227
|
+
saveTimeout: "保存超时,请重试",
|
|
2300
2228
|
"reason.central-subagent-routing": "Alpha.4 没有全局子代理启动路由接口。",
|
|
2301
2229
|
"reason.packaged-preset-roots": "Alpha.4 不支持插件提供额外 preset root。",
|
|
2302
2230
|
"reason.tool-owner-suppression": "Alpha.4 没有工具所有者或来源抑制接口。",
|
|
@@ -2307,22 +2235,21 @@ window.__ModuleLoader__.load({
|
|
|
2307
2235
|
const en$1 = {
|
|
2308
2236
|
nav: "Model Switch",
|
|
2309
2237
|
title: "Model Switch",
|
|
2310
|
-
subtitle: "
|
|
2238
|
+
subtitle: "Pin routes for Subagents and capability tools; switch the active session model through the composer picker.",
|
|
2311
2239
|
conversationRoutes: "Conversation routes",
|
|
2312
2240
|
capabilityRoutes: "Capability routes",
|
|
2313
2241
|
sendProtection: "Send protection",
|
|
2314
2242
|
settingsSynced: "Settings synced",
|
|
2315
2243
|
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
2244
|
cancel: "Cancel",
|
|
2318
2245
|
compactOnSwitch: "Compact on switch",
|
|
2319
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.",
|
|
2320
|
-
main: "Main model",
|
|
2321
2247
|
provider: "Provider",
|
|
2322
2248
|
model: "Model",
|
|
2323
2249
|
providerDefault: "Provider default",
|
|
2324
2250
|
providerDefaultShort: "Provider default",
|
|
2325
2251
|
effort: "Reasoning effort",
|
|
2252
|
+
effortPinned: "Pinned",
|
|
2326
2253
|
save: "Save",
|
|
2327
2254
|
saving: "Saving…",
|
|
2328
2255
|
saved: "Saved",
|
|
@@ -2336,10 +2263,8 @@ window.__ModuleLoader__.load({
|
|
|
2336
2263
|
imageHelp: "The stable generate_image tool calls the selected Codex or Grok Adapter; existing provider image tools remain available.",
|
|
2337
2264
|
unavailable: "Unavailable",
|
|
2338
2265
|
loading: "Loading settings…",
|
|
2339
|
-
readonly: "Settings are read-only",
|
|
2340
2266
|
requestFailed: "Save failed",
|
|
2341
2267
|
catalogFailed: "Could not load the model catalog.",
|
|
2342
|
-
conflict: "Settings changed elsewhere. Review the latest values and retry.",
|
|
2343
2268
|
autoReview: "Auto review model",
|
|
2344
2269
|
autoReviewFollow: "Follows the session model",
|
|
2345
2270
|
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.",
|
|
@@ -2348,14 +2273,18 @@ window.__ModuleLoader__.load({
|
|
|
2348
2273
|
compactionFollow: "Follows the conversation model",
|
|
2349
2274
|
compactionHelp: "Pick the summarization model for official compaction (/compact and official automatic compaction). When off (both fields empty), the official default applies: the conversation target model.",
|
|
2350
2275
|
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.",
|
|
2276
|
+
sessionTitle: "Session title model",
|
|
2277
|
+
titleFollow: "Follows the session model",
|
|
2278
|
+
titleHelp: "Pick the model used to name new sessions automatically (right after the first prompt). When off (both fields empty), naming follows the session model.",
|
|
2279
|
+
titleRouteHelp: "Naming requests use exactly this provider/model (no reasoning effort); when off they follow the session model. A saved change applies to the very next title, never interrupts a running one, and the session log records the route actually used.",
|
|
2351
2280
|
providerGroups: "Provider groups",
|
|
2352
2281
|
providerGroupsCard: "Per-provider route presets",
|
|
2353
|
-
providerGroupsSummary: "
|
|
2354
|
-
providerGroupsHelp: "Save
|
|
2355
|
-
providerGroupsBodyHelp: "Each provider stores one
|
|
2282
|
+
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.",
|
|
2356
2285
|
providerGroupPick: "Pick a provider to edit its group.",
|
|
2357
2286
|
providerGroupDelete: "Delete group",
|
|
2358
|
-
|
|
2287
|
+
saveTimeout: "The save timed out; try again",
|
|
2359
2288
|
"reason.central-subagent-routing": "Alpha.4 exposes no global Subagent start-routing seam.",
|
|
2360
2289
|
"reason.packaged-preset-roots": "Alpha.4 exposes no plugin-owned preset root.",
|
|
2361
2290
|
"reason.tool-owner-suppression": "Alpha.4 exposes no tool owner or provenance suppression seam.",
|
|
@@ -2416,13 +2345,8 @@ window.__ModuleLoader__.load({
|
|
|
2416
2345
|
}
|
|
2417
2346
|
async function approvePlanReview(args) {
|
|
2418
2347
|
const current = args.current;
|
|
2419
|
-
|
|
2420
|
-
committed: true,
|
|
2421
|
-
mainDefaultRestored: true
|
|
2422
|
-
} : await args.select(args.selection);
|
|
2423
|
-
if (!commit.committed) return false;
|
|
2348
|
+
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
2349
|
await args.answer();
|
|
2425
|
-
if (!commit.mainDefaultRestored) args.onNotRestored?.(args.selection.model);
|
|
2426
2350
|
return true;
|
|
2427
2351
|
}
|
|
2428
2352
|
function planActionView(state, available, hasExecution) {
|
|
@@ -3126,7 +3050,7 @@ window.__ModuleLoader__.load({
|
|
|
3126
3050
|
children: mark
|
|
3127
3051
|
});
|
|
3128
3052
|
}
|
|
3129
|
-
function ComposerPicker({ locked, providerLock = null, agentLocked = false, available,
|
|
3053
|
+
function ComposerPicker({ locked, providerLock = null, agentLocked = false, available, directory, t, draft, onDraftChange, embedded, tone, resolveInteractionOperations, roleOf }) {
|
|
3130
3054
|
const { snapshot: state, getDirectorySnapshot, load, select } = directory;
|
|
3131
3055
|
const [pane, setPane] = (0, react.useState)("root");
|
|
3132
3056
|
const [searching, setSearching] = (0, react.useState)(false);
|
|
@@ -3209,16 +3133,7 @@ window.__ModuleLoader__.load({
|
|
|
3209
3133
|
setQuery("");
|
|
3210
3134
|
};
|
|
3211
3135
|
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
|
-
}
|
|
3136
|
+
if (outcome.committed) return;
|
|
3222
3137
|
const message = getDirectorySnapshot().error;
|
|
3223
3138
|
if (message !== null) {
|
|
3224
3139
|
toastSeq.current += 1;
|
|
@@ -3629,11 +3544,11 @@ window.__ModuleLoader__.load({
|
|
|
3629
3544
|
ref: triggerRef,
|
|
3630
3545
|
type: "button",
|
|
3631
3546
|
className: ComposerPicker_module_css_default.trigger,
|
|
3632
|
-
"aria-label":
|
|
3547
|
+
"aria-label": triggerAria,
|
|
3633
3548
|
"aria-haspopup": "menu",
|
|
3634
3549
|
"aria-expanded": open,
|
|
3635
3550
|
"aria-controls": open ? `${id}-menu` : void 0,
|
|
3636
|
-
title:
|
|
3551
|
+
title: triggerLabel,
|
|
3637
3552
|
disabled: locked,
|
|
3638
3553
|
onPointerDown: onTriggerPointerDown,
|
|
3639
3554
|
onClick: onTriggerClick,
|
|
@@ -3858,134 +3773,6 @@ window.__ModuleLoader__.load({
|
|
|
3858
3773
|
}, operations);
|
|
3859
3774
|
}
|
|
3860
3775
|
//#endregion
|
|
3861
|
-
//#region src/client/picker/main-default-restore.ts
|
|
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
|
-
}
|
|
3929
|
-
/**
|
|
3930
|
-
* Restore the Main default captured before a Session-only model switch.
|
|
3931
|
-
*
|
|
3932
|
-
* Never throws and never blocks a Plan answer: a failed compensation is
|
|
3933
|
-
* reported, not enforced, because the Session selection the human asked for is
|
|
3934
|
-
* already installed by the time this runs.
|
|
3935
|
-
*
|
|
3936
|
-
* @param form - the official Main Settings namespace form.
|
|
3937
|
-
* @param before - the snapshot captured before the switch.
|
|
3938
|
-
* @param options - wait/timeout/attempt overrides for tests.
|
|
3939
|
-
* @returns Whether the captured default stands again, and whether a newer edit won.
|
|
3940
|
-
*/
|
|
3941
|
-
async function restoreMainDefault(form, before, options = {}) {
|
|
3942
|
-
const captured = fenced(before);
|
|
3943
|
-
if (captured === void 0) return {
|
|
3944
|
-
restored: true,
|
|
3945
|
-
conflict: false
|
|
3946
|
-
};
|
|
3947
|
-
const wait = options.wait ?? defaultWait;
|
|
3948
|
-
const timeoutMs = options.timeoutMs ?? DEFAULT_WRITE_WAIT_MS;
|
|
3949
|
-
const attempts = Math.max(1, options.attempts ?? 2);
|
|
3950
|
-
await waitForAdvance(form, captured.revision, wait, timeoutMs);
|
|
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
|
-
};
|
|
3982
|
-
}
|
|
3983
|
-
return {
|
|
3984
|
-
restored: false,
|
|
3985
|
-
conflict: true
|
|
3986
|
-
};
|
|
3987
|
-
}
|
|
3988
|
-
//#endregion
|
|
3989
3776
|
//#region src/picker/pending-dismissal.ts
|
|
3990
3777
|
/** Raised when the running Host exposes neither withdrawal verb. */
|
|
3991
3778
|
var PlanDismissalUnsupportedError = class extends Error {
|
|
@@ -4199,12 +3986,6 @@ window.__ModuleLoader__.load({
|
|
|
4199
3986
|
throw cause instanceof PlanDismissalUnsupportedError ? new Error(unsupportedMessage, { cause }) : new Error(rejectedMessage, { cause });
|
|
4200
3987
|
}
|
|
4201
3988
|
}
|
|
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
3989
|
/** Inline failed lock-read status; history and log reading stay unaffected. */
|
|
4209
3990
|
function ProviderLockHint(props) {
|
|
4210
3991
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -4220,7 +4001,6 @@ window.__ModuleLoader__.load({
|
|
|
4220
4001
|
function PlanReviewCard(props) {
|
|
4221
4002
|
const snapshot = props.useDirectory((value) => value);
|
|
4222
4003
|
const order = props.useProviderOrder((value) => value);
|
|
4223
|
-
const settingsUnavailableReason = mainDefaultsUnavailableReason((0, react.useSyncExternalStore)(props.subscribeMainDefaults, props.getMainDefaultsSnapshot), props.t);
|
|
4224
4004
|
const lock = (0, react.useSyncExternalStore)(props.providerLockStore.subscribe, props.providerLockStore.getSnapshot);
|
|
4225
4005
|
const phase = props.useInput((input) => input.phase);
|
|
4226
4006
|
const blank = props.useSession((session) => session.blank);
|
|
@@ -4256,7 +4036,6 @@ window.__ModuleLoader__.load({
|
|
|
4256
4036
|
providerLock,
|
|
4257
4037
|
agentLocked,
|
|
4258
4038
|
lockFailed: lock.failed,
|
|
4259
|
-
...settingsUnavailableReason === void 0 ? {} : { settingsUnavailableReason },
|
|
4260
4039
|
directory: pickerDirectoryViewOrdered(snapshot, props, order, props.catalogRoutes?.() ?? {}),
|
|
4261
4040
|
t: props.t,
|
|
4262
4041
|
goalRemote: props.goalRemote,
|
|
@@ -4265,7 +4044,7 @@ window.__ModuleLoader__.load({
|
|
|
4265
4044
|
...props.roleOf === void 0 ? {} : { roleOf: props.roleOf }
|
|
4266
4045
|
}, props.matched.key);
|
|
4267
4046
|
}
|
|
4268
|
-
function PlanReviewState({ matched, review, available, providerLock, agentLocked, lockFailed,
|
|
4047
|
+
function PlanReviewState({ matched, review, available, providerLock, agentLocked, lockFailed, directory, t, goalRemote, planPreview, resolveInteractionOperations, roleOf }) {
|
|
4269
4048
|
const { snapshot, getDirectorySnapshot, load, select } = directory;
|
|
4270
4049
|
const [execution, setExecution] = (0, react.useState)(snapshot.current ?? void 0);
|
|
4271
4050
|
const [feedback, setFeedback] = (0, react.useState)("");
|
|
@@ -4328,8 +4107,7 @@ window.__ModuleLoader__.load({
|
|
|
4328
4107
|
setNotice(null);
|
|
4329
4108
|
load();
|
|
4330
4109
|
};
|
|
4331
|
-
const
|
|
4332
|
-
const executionAllowed = execution !== void 0 && !settingsBlock && runtimeChoiceAllowed(providerLock, agentLocked, execution.provider, snapshot.current?.provider, isAgentRole(roleOf?.(execution.provider)));
|
|
4110
|
+
const executionAllowed = execution !== void 0 && runtimeChoiceAllowed(providerLock, agentLocked, execution.provider, snapshot.current?.provider, isAgentRole(roleOf?.(execution.provider)));
|
|
4333
4111
|
const action = planActionView({
|
|
4334
4112
|
busy,
|
|
4335
4113
|
blocked,
|
|
@@ -4341,11 +4119,7 @@ window.__ModuleLoader__.load({
|
|
|
4341
4119
|
select,
|
|
4342
4120
|
selection: model,
|
|
4343
4121
|
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
|
-
}
|
|
4122
|
+
answer: () => respondAnswer(matched, review.id, review.approve.label, t("plan.responseRejected"), true)
|
|
4349
4123
|
})) {
|
|
4350
4124
|
const message = getDirectorySnapshot().error;
|
|
4351
4125
|
throw new Error(message === null ? t("plan.modelFailed") : t("error.action", { message }));
|
|
@@ -4417,12 +4191,11 @@ window.__ModuleLoader__.load({
|
|
|
4417
4191
|
errorLabel: (message) => t("plan.pickerCrash", { message }),
|
|
4418
4192
|
retryLabel: t("retry"),
|
|
4419
4193
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ComposerPicker, {
|
|
4420
|
-
locked: busy || blocked
|
|
4194
|
+
locked: busy || blocked,
|
|
4421
4195
|
providerLock,
|
|
4422
4196
|
agentLocked,
|
|
4423
4197
|
...roleOf === void 0 ? {} : { roleOf },
|
|
4424
4198
|
available,
|
|
4425
|
-
...settingsUnavailableReason === void 0 ? {} : { unavailableReason: settingsUnavailableReason },
|
|
4426
4199
|
directory,
|
|
4427
4200
|
t,
|
|
4428
4201
|
...resolveInteractionOperations === void 0 ? {} : { resolveInteractionOperations },
|
|
@@ -4456,7 +4229,7 @@ window.__ModuleLoader__.load({
|
|
|
4456
4229
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4457
4230
|
className: PlanReviewCard_module_css_default.feedback,
|
|
4458
4231
|
role: "status",
|
|
4459
|
-
children: action.error ?? notice
|
|
4232
|
+
children: action.error ?? notice
|
|
4460
4233
|
}),
|
|
4461
4234
|
decline !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4462
4235
|
className: PlanReviewCard_module_css_default.feedbackRow,
|
|
@@ -4578,9 +4351,6 @@ window.__ModuleLoader__.load({
|
|
|
4578
4351
|
"thinking.off": "关",
|
|
4579
4352
|
"effort.providerDefault": "Default",
|
|
4580
4353
|
"status.loading": "正在刷新模型列表…",
|
|
4581
|
-
"settings.remoteUnavailable": "官方 DSH 在非本机浏览器禁用配置读取与写入;请在 Host 的 127.0.0.1 页面选择模型。",
|
|
4582
|
-
"settings.loading": "正在加载主模型设置;加载完成后才能切换模型。",
|
|
4583
|
-
"settings.unavailable": "主模型设置不可写;请在 Host 的本机页面检查设置状态。",
|
|
4584
4354
|
"error.action": "模型操作失败:{message}",
|
|
4585
4355
|
"error.picker": "模型选择器出错:{message}(点击重试)",
|
|
4586
4356
|
"action.reload": "重新加载",
|
|
@@ -4601,7 +4371,6 @@ window.__ModuleLoader__.load({
|
|
|
4601
4371
|
"plan.pickerCrash": "执行模型选择器出错:{message}",
|
|
4602
4372
|
"plan.feedback": "拒绝时的反馈",
|
|
4603
4373
|
"plan.feedbackPlaceholder": "说明要改什么(可留空)",
|
|
4604
|
-
"plan.notRestored": "执行模型已切换,但主模型默认值未能还原({model});请在「设置 → 模型」中确认默认模型。",
|
|
4605
4374
|
"plan.dismissUnsupported": "当前 DSH 版本既未提供 PendingQuestion.dismiss() 也未提供 cancel(),无法回到聊天;请直接在输入框里发送你的意见。",
|
|
4606
4375
|
"plan.goalRun": "设置为目标并执行",
|
|
4607
4376
|
"plan.goalFailed": "设置目标失败:{message}",
|
|
@@ -4634,9 +4403,6 @@ window.__ModuleLoader__.load({
|
|
|
4634
4403
|
"thinking.off": "Off",
|
|
4635
4404
|
"effort.providerDefault": "Default",
|
|
4636
4405
|
"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
4406
|
"error.action": "Model operation failed: {message}",
|
|
4641
4407
|
"error.picker": "Model picker error: {message} (click to retry)",
|
|
4642
4408
|
"action.reload": "Reload",
|
|
@@ -4657,7 +4423,6 @@ window.__ModuleLoader__.load({
|
|
|
4657
4423
|
"plan.pickerCrash": "Execution model picker error: {message}",
|
|
4658
4424
|
"plan.feedback": "Feedback when declining",
|
|
4659
4425
|
"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
4426
|
"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
4427
|
"plan.goalRun": "Set as goal and run",
|
|
4663
4428
|
"plan.goalFailed": "Could not set the goal: {message}",
|
|
@@ -4757,7 +4522,6 @@ window.__ModuleLoader__.load({
|
|
|
4757
4522
|
function ModelSeat(props) {
|
|
4758
4523
|
const directory = props.useDirectory((snapshot) => snapshot);
|
|
4759
4524
|
const order = props.useProviderOrder((value) => value);
|
|
4760
|
-
const settingsUnavailableReason = mainDefaultsUnavailableReason((0, react.useSyncExternalStore)(props.subscribeMainDefaults, props.getMainDefaultsSnapshot), props.t);
|
|
4761
4525
|
const lock = (0, react.useSyncExternalStore)(props.providerLockStore.subscribe, props.providerLockStore.getSnapshot);
|
|
4762
4526
|
const phase = props.useInput((input) => input.phase);
|
|
4763
4527
|
const blank = props.useSession((session) => session.blank);
|
|
@@ -4773,12 +4537,11 @@ window.__ModuleLoader__.load({
|
|
|
4773
4537
|
]);
|
|
4774
4538
|
const providerLock = effectiveProviderLock(lock, directory.current?.provider, active, isAgentRole(props.roleOf?.(directory.current?.provider ?? "")));
|
|
4775
4539
|
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 ||
|
|
4540
|
+
locked: props.locked || lock.failed && directory.current === null,
|
|
4777
4541
|
providerLock,
|
|
4778
4542
|
agentLocked: agentProviderLocked(blank, providerLock, active),
|
|
4779
4543
|
...props.roleOf === void 0 ? {} : { roleOf: props.roleOf },
|
|
4780
4544
|
available: props.available,
|
|
4781
|
-
...settingsUnavailableReason === void 0 ? {} : { unavailableReason: settingsUnavailableReason },
|
|
4782
4545
|
directory: pickerDirectoryViewOrdered(directory, props, order, props.catalogRoutes?.() ?? {}),
|
|
4783
4546
|
t: props.t,
|
|
4784
4547
|
...props.resolveInteractionOperations === void 0 ? {} : { resolveInteractionOperations: props.resolveInteractionOperations }
|
|
@@ -4803,8 +4566,6 @@ window.__ModuleLoader__.load({
|
|
|
4803
4566
|
], (scope) => {
|
|
4804
4567
|
const models = scope.modelDirectories;
|
|
4805
4568
|
const sessions = scope.sessions;
|
|
4806
|
-
const mainDefaults = scope.configForms.get(MAIN_DEFAULT_CONFIG_ID);
|
|
4807
|
-
const ownedForm = scope.configForms.get(MODEL_SWITCH_CONFIG_ID);
|
|
4808
4569
|
let directoryService;
|
|
4809
4570
|
const orderStore = providerOrderStore(scope.configForms.get(PROVIDERS_CONFIG_ID));
|
|
4810
4571
|
scope.inject(["providerDirectory"], (directoryScope) => {
|
|
@@ -4843,8 +4604,6 @@ window.__ModuleLoader__.load({
|
|
|
4843
4604
|
refreshProviderLock: () => {
|
|
4844
4605
|
providerLockStore.refresh();
|
|
4845
4606
|
},
|
|
4846
|
-
subscribeMainDefaults: (listener) => mainDefaults.subscribe(listener),
|
|
4847
|
-
getMainDefaultsSnapshot: () => mainDefaults.getSnapshot(),
|
|
4848
4607
|
goalRemote: () => goalRemoteFrom(scope),
|
|
4849
4608
|
planPreview,
|
|
4850
4609
|
hooks: {
|
|
@@ -4857,36 +4616,22 @@ window.__ModuleLoader__.load({
|
|
|
4857
4616
|
if (available) directory.load().catch(() => {});
|
|
4858
4617
|
},
|
|
4859
4618
|
select: async (selection) => {
|
|
4860
|
-
|
|
4861
|
-
committed: false,
|
|
4862
|
-
mainDefaultRestored: true
|
|
4863
|
-
};
|
|
4864
|
-
if (!available) return uncommitted;
|
|
4619
|
+
if (!available) return { committed: false };
|
|
4865
4620
|
const state = await providerLockStore.refresh();
|
|
4866
4621
|
const currentProvider = directory.store.getSnapshot().current?.provider;
|
|
4867
4622
|
if (!isProviderAllowed(state, selection.provider, currentProvider, {
|
|
4868
4623
|
...readSessionState(sessions, sessionId),
|
|
4869
4624
|
agent: isAgentRole(roleOf(selection.provider)),
|
|
4870
4625
|
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;
|
|
4626
|
+
})) return { committed: false };
|
|
4874
4627
|
let result;
|
|
4875
4628
|
try {
|
|
4876
4629
|
result = await directory.select(selection);
|
|
4877
4630
|
} 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);
|
|
4631
|
+
return { committed: false };
|
|
4885
4632
|
}
|
|
4886
|
-
return {
|
|
4887
|
-
|
|
4888
|
-
mainDefaultRestored: (await restoreMainDefault(mainDefaults, defaultBeforeSwitch)).restored
|
|
4889
|
-
};
|
|
4633
|
+
if (!result.ok) return { committed: false };
|
|
4634
|
+
return { committed: true };
|
|
4890
4635
|
}
|
|
4891
4636
|
};
|
|
4892
4637
|
};
|
|
@@ -4983,56 +4728,13 @@ window.__ModuleLoader__.load({
|
|
|
4983
4728
|
}), "dsh-model-switch: localized Settings section");
|
|
4984
4729
|
const t = ctx.locale.bind(localeNamespace);
|
|
4985
4730
|
const remote = ctx.remote;
|
|
4986
|
-
const mainForm = ctx.configForms.get(MAIN_DEFAULT_CONFIG_ID);
|
|
4987
4731
|
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
4732
|
const subagent = deriveConfigForm(owned, deriveSubagentSettings, SUBAGENT_SETTINGS_FIELDS);
|
|
4994
4733
|
const search = deriveConfigForm(owned, deriveSearchSettings, SEARCH_SETTINGS_FIELDS);
|
|
4995
4734
|
const image = deriveConfigForm(owned, deriveImageSettings, IMAGE_SETTINGS_FIELDS);
|
|
4996
4735
|
const autoReview = deriveConfigForm(owned, deriveAutoReviewSettings, AUTO_REVIEW_SETTINGS_FIELDS);
|
|
4997
4736
|
const compaction = deriveConfigForm(owned, deriveCompactionSettings, COMPACTION_SETTINGS_FIELDS);
|
|
4998
|
-
|
|
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
|
-
};
|
|
4737
|
+
const sessionTitle = deriveConfigForm(owned, deriveTitleSettings, TITLE_SETTINGS_FIELDS);
|
|
5036
4738
|
const connectionRpc = (() => {
|
|
5037
4739
|
try {
|
|
5038
4740
|
const rpc = ctx.get("connection", false)?.rpc;
|
|
@@ -5065,20 +4767,18 @@ window.__ModuleLoader__.load({
|
|
|
5065
4767
|
inject: () => ({
|
|
5066
4768
|
t,
|
|
5067
4769
|
hooks: {
|
|
5068
|
-
mainSettings: main,
|
|
5069
4770
|
subagentSettings: subagent,
|
|
5070
4771
|
searchSettings: search,
|
|
5071
4772
|
imageSettings: image,
|
|
5072
4773
|
autoReviewSettings: autoReview,
|
|
5073
4774
|
compactionSettings: compaction,
|
|
4775
|
+
titleSettings: sessionTitle,
|
|
5074
4776
|
switchSettings: owned
|
|
5075
4777
|
},
|
|
5076
4778
|
capabilities: RUNTIME_CAPABILITIES,
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
const scope = route === "search" ? search : route === "image" ? image : route === "compaction" ? compaction : autoReview;
|
|
5081
|
-
return value === void 0 ? scope.unset(field) : scope.set(field, value);
|
|
4779
|
+
saveSubagent: (ops) => subagent.mutate(ops),
|
|
4780
|
+
saveCapability: (route, ops) => {
|
|
4781
|
+
return (route === "search" ? search : route === "image" ? image : route === "compaction" ? compaction : route === "title" ? sessionTitle : autoReview).mutate(ops);
|
|
5082
4782
|
},
|
|
5083
4783
|
setCompactOnSwitch: async (value) => {
|
|
5084
4784
|
if (!await owned.set("compactOnSwitch", value)) throw new Error("settings-rejected");
|