dsh-model-switch 0.4.20 → 0.4.22
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 +220 -203
- package/IMPLEMENTATION_PLAN.md +40 -40
- package/LICENSE +21 -21
- package/PRODUCT.md +33 -33
- package/README.md +218 -208
- package/README.zh.md +216 -206
- package/cordis.patch.yml +515 -30
- package/lib/client.js +845 -258
- package/lib/compaction.js +205 -0
- package/lib/index.js +100 -242
- package/lib/types/capabilities-rpc.d.ts +2 -3
- package/lib/types/client/ModelSwitchSettings.d.ts +5 -2
- package/lib/types/client/derived-settings-scope.d.ts +4 -4
- package/lib/types/client/locales.d.ts +12 -3
- package/lib/types/client/picker/PlanReviewCard.d.ts +0 -3
- package/lib/types/client/picker/locales.d.ts +0 -8
- package/lib/types/client/remote-settings-scope.d.ts +45 -45
- package/lib/types/client-contract.d.ts +13 -4
- package/lib/types/compaction.d.ts +56 -0
- package/lib/types/host-runtime.d.ts +6 -28
- package/lib/types/host-settings.d.ts +17 -30
- package/lib/types/picker/plan-goal.d.ts +29 -3
- package/lib/types/provider-group.d.ts +78 -0
- package/package.json +360 -356
- package/lib/types/picker/plan-compact.d.ts +0 -48
- package/lib/types/plan-compact.d.ts +0 -82
package/lib/client.js
CHANGED
|
@@ -38,6 +38,11 @@ window.__ModuleLoader__.load({
|
|
|
38
38
|
provider: "autoReviewProvider",
|
|
39
39
|
model: "autoReviewModel"
|
|
40
40
|
});
|
|
41
|
+
/** Compaction summarization route for `/compact` and automatic compaction; both unset means the official default (the conversation target). */
|
|
42
|
+
const COMPACTION_SETTINGS_FIELDS = Object.freeze({
|
|
43
|
+
provider: "compactProvider",
|
|
44
|
+
model: "compactModel"
|
|
45
|
+
});
|
|
41
46
|
function deriveSubagentSettings(settings) {
|
|
42
47
|
return {
|
|
43
48
|
mode: settings.subagentMode,
|
|
@@ -64,11 +69,185 @@ window.__ModuleLoader__.load({
|
|
|
64
69
|
...settings.autoReviewModel === void 0 ? {} : { model: settings.autoReviewModel }
|
|
65
70
|
};
|
|
66
71
|
}
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
function deriveCompactionSettings(settings) {
|
|
73
|
+
return {
|
|
74
|
+
...settings.compactProvider === void 0 ? {} : { provider: settings.compactProvider },
|
|
75
|
+
...settings.compactModel === void 0 ? {} : { model: settings.compactModel }
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** Whether both fields of a model route form a complete pair; an incomplete pair stays off. */
|
|
79
|
+
function routePairConfigured(route) {
|
|
69
80
|
return route !== void 0 && (route.provider ?? "").trim() !== "" && (route.model ?? "").trim() !== "";
|
|
70
81
|
}
|
|
71
82
|
//#endregion
|
|
83
|
+
//#region src/provider-group.ts
|
|
84
|
+
const ROUTE_FIELDS = Object.freeze([
|
|
85
|
+
"compactProvider",
|
|
86
|
+
"compactModel",
|
|
87
|
+
"autoReviewProvider",
|
|
88
|
+
"autoReviewModel",
|
|
89
|
+
"subagentProvider",
|
|
90
|
+
"subagentModel",
|
|
91
|
+
"subagentReasoningEffort"
|
|
92
|
+
]);
|
|
93
|
+
/** Own data property, so a literal '__proto__' provider id can never touch a prototype. */
|
|
94
|
+
function setKey(target, key, value) {
|
|
95
|
+
Object.defineProperty(target, key, {
|
|
96
|
+
value,
|
|
97
|
+
enumerable: true,
|
|
98
|
+
writable: true,
|
|
99
|
+
configurable: true
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/** A non-blank string. Blank values behave exactly like an absent field. */
|
|
103
|
+
function present(value) {
|
|
104
|
+
return typeof value === "string" && value.trim() !== "";
|
|
105
|
+
}
|
|
106
|
+
/** Normalize one raw entry to its owning provider and route view; undefined for non-object junk. */
|
|
107
|
+
function normalizeEntry(value) {
|
|
108
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
109
|
+
const source = value;
|
|
110
|
+
const group = {};
|
|
111
|
+
for (const field of ROUTE_FIELDS) {
|
|
112
|
+
const item = source[field];
|
|
113
|
+
if (present(item)) group[field] = item;
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
provider: present(source.provider) ? source.provider : "",
|
|
117
|
+
group
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Strip a raw stored value to well-formed groups; never throws. Both storage
|
|
122
|
+
* shapes are accepted: the stored entry array (keys live in each entry's
|
|
123
|
+
* `provider` field) and a hand-written provider → routes map (keys are the
|
|
124
|
+
* providers). All-empty entries stay: they pin a provider to every default.
|
|
125
|
+
*/
|
|
126
|
+
function normalizeProviderGroups(raw) {
|
|
127
|
+
const groups = {};
|
|
128
|
+
if (Array.isArray(raw)) {
|
|
129
|
+
for (const entry of raw) {
|
|
130
|
+
const view = normalizeEntry(entry);
|
|
131
|
+
if (view === void 0 || view.provider === "") continue;
|
|
132
|
+
setKey(groups, view.provider, view.group);
|
|
133
|
+
}
|
|
134
|
+
return groups;
|
|
135
|
+
}
|
|
136
|
+
if (raw !== null && typeof raw === "object") for (const [key, value] of Object.entries(raw)) {
|
|
137
|
+
if (key.trim() === "") continue;
|
|
138
|
+
const view = normalizeEntry(value);
|
|
139
|
+
if (view === void 0) continue;
|
|
140
|
+
setKey(groups, key, view.group);
|
|
141
|
+
}
|
|
142
|
+
return groups;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* The saved group for a provider.
|
|
146
|
+
* @returns the normalized group, or undefined when the provider has no group —
|
|
147
|
+
* which is what leaves the live routes untouched during a switch.
|
|
148
|
+
*/
|
|
149
|
+
function groupOf(groups, provider) {
|
|
150
|
+
if (groups === void 0 || provider.trim() === "") return void 0;
|
|
151
|
+
if (!Object.prototype.hasOwnProperty.call(groups, provider)) return void 0;
|
|
152
|
+
return groups[provider];
|
|
153
|
+
}
|
|
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
|
+
/** Storage entry for one provider's group; absent or blank route fields become ''. */
|
|
211
|
+
function entryOf(provider, group) {
|
|
212
|
+
return {
|
|
213
|
+
provider,
|
|
214
|
+
compactProvider: group.compactProvider ?? "",
|
|
215
|
+
compactModel: group.compactModel ?? "",
|
|
216
|
+
autoReviewProvider: group.autoReviewProvider ?? "",
|
|
217
|
+
autoReviewModel: group.autoReviewModel ?? "",
|
|
218
|
+
subagentProvider: group.subagentProvider ?? "",
|
|
219
|
+
subagentModel: group.subagentModel ?? "",
|
|
220
|
+
subagentReasoningEffort: group.subagentReasoningEffort ?? ""
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* The stored entry list with one provider's group replaced (an entry) or
|
|
225
|
+
* removed (undefined). Every entry is re-normalized and de-duplicated so a
|
|
226
|
+
* save can never write junk back; the input is never mutated. A blank
|
|
227
|
+
* provider only normalizes.
|
|
228
|
+
*/
|
|
229
|
+
function withProviderGroup(storage, provider, group) {
|
|
230
|
+
const groups = { ...normalizeProviderGroups(storage) };
|
|
231
|
+
if (provider.trim() !== "") {
|
|
232
|
+
if (group === void 0) delete groups[provider];
|
|
233
|
+
else setKey(groups, provider, group);
|
|
234
|
+
}
|
|
235
|
+
return Object.entries(groups).map(([key, value]) => entryOf(key, value));
|
|
236
|
+
}
|
|
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
|
+
//#endregion
|
|
72
251
|
//#region src/client/derived-config-form.ts
|
|
73
252
|
function deriveConfigForm(source, project, fields) {
|
|
74
253
|
let sourceSnapshot;
|
|
@@ -617,7 +796,53 @@ window.__ModuleLoader__.load({
|
|
|
617
796
|
function compact(...values) {
|
|
618
797
|
return values.filter((value) => value !== void 0 && value !== "").join(" · ");
|
|
619
798
|
}
|
|
799
|
+
/** A complete pinned pair; either half blank leaves the capability at its default. */
|
|
800
|
+
function draftPair(provider, model) {
|
|
801
|
+
return provider !== void 0 && provider.trim() !== "" && model !== void 0 && model.trim() !== "" ? {
|
|
802
|
+
provider,
|
|
803
|
+
model
|
|
804
|
+
} : void 0;
|
|
805
|
+
}
|
|
620
806
|
function RouteIcon({ kind }) {
|
|
807
|
+
if (kind === "providerGroups") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
808
|
+
width: "18",
|
|
809
|
+
height: "18",
|
|
810
|
+
viewBox: "0 0 18 18",
|
|
811
|
+
fill: "none",
|
|
812
|
+
"aria-hidden": "true",
|
|
813
|
+
children: [
|
|
814
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
815
|
+
d: "M3 5.5h12M3 12.5h12",
|
|
816
|
+
stroke: "currentColor",
|
|
817
|
+
strokeLinecap: "round"
|
|
818
|
+
}),
|
|
819
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
820
|
+
cx: "6.5",
|
|
821
|
+
cy: "5.5",
|
|
822
|
+
r: "2",
|
|
823
|
+
stroke: "currentColor"
|
|
824
|
+
}),
|
|
825
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
826
|
+
cx: "11.5",
|
|
827
|
+
cy: "12.5",
|
|
828
|
+
r: "2",
|
|
829
|
+
stroke: "currentColor"
|
|
830
|
+
})
|
|
831
|
+
]
|
|
832
|
+
});
|
|
833
|
+
if (kind === "compaction") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
834
|
+
width: "18",
|
|
835
|
+
height: "18",
|
|
836
|
+
viewBox: "0 0 18 18",
|
|
837
|
+
fill: "none",
|
|
838
|
+
"aria-hidden": "true",
|
|
839
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
840
|
+
d: "M5 3.5 9 7.5l4-4M5 14.5 9 10.5l4 4",
|
|
841
|
+
stroke: "currentColor",
|
|
842
|
+
strokeLinecap: "round",
|
|
843
|
+
strokeLinejoin: "round"
|
|
844
|
+
})
|
|
845
|
+
});
|
|
621
846
|
if (kind === "main") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
622
847
|
width: "18",
|
|
623
848
|
height: "18",
|
|
@@ -816,7 +1041,7 @@ window.__ModuleLoader__.load({
|
|
|
816
1041
|
})]
|
|
817
1042
|
});
|
|
818
1043
|
}
|
|
819
|
-
function Actions({ t, busy, disabled, message, onCancel, onSave }) {
|
|
1044
|
+
function Actions({ t, busy, disabled, message, extra, onCancel, onSave }) {
|
|
820
1045
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
821
1046
|
className: ModelSwitchSettings_module_css_default.cardFooter,
|
|
822
1047
|
children: [
|
|
@@ -824,6 +1049,7 @@ window.__ModuleLoader__.load({
|
|
|
824
1049
|
className: cx(ModelSwitchSettings_module_css_default.hint, ModelSwitchSettings_module_css_default.message),
|
|
825
1050
|
children: message
|
|
826
1051
|
}),
|
|
1052
|
+
extra,
|
|
827
1053
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
828
1054
|
type: "button",
|
|
829
1055
|
className: cx(ModelSwitchSettings_module_css_default.button, ModelSwitchSettings_module_css_default.secondaryButton),
|
|
@@ -890,45 +1116,20 @@ window.__ModuleLoader__.load({
|
|
|
890
1116
|
const search = props.useSearchSettings((value) => value);
|
|
891
1117
|
const image = props.useImageSettings((value) => value);
|
|
892
1118
|
const autoReview = props.useAutoReviewSettings((value) => value);
|
|
1119
|
+
const compaction = props.useCompactionSettings((value) => value);
|
|
893
1120
|
const switchSettings = props.useSwitchSettings((value) => value);
|
|
894
1121
|
const [open, setOpen] = (0, react.useState)();
|
|
895
1122
|
const [subagentDraft, setSubagentDraft, resetSubagent] = useDraft(subagent);
|
|
896
1123
|
const [searchDraft, setSearchDraft, resetSearch] = useDraft(search);
|
|
897
1124
|
const [imageDraft, setImageDraft, resetImage] = useDraft(image);
|
|
898
1125
|
const [autoReviewDraft, setAutoReviewDraft, resetAutoReview] = useDraft(autoReview);
|
|
1126
|
+
const [compactionDraft, setCompactionDraft, resetCompaction] = useDraft(compaction);
|
|
899
1127
|
const [busy, setBusy] = (0, react.useState)();
|
|
900
1128
|
const [message, setMessage] = (0, react.useState)();
|
|
901
1129
|
const [compactBusy, setCompactBusy] = (0, react.useState)(false);
|
|
902
1130
|
const [compactError, setCompactError] = (0, react.useState)();
|
|
903
|
-
const
|
|
904
|
-
const [
|
|
905
|
-
const [planCompactBusy, setPlanCompactBusy] = (0, react.useState)(false);
|
|
906
|
-
const [planCompactError, setPlanCompactError] = (0, react.useState)();
|
|
907
|
-
(0, react.useEffect)(() => {
|
|
908
|
-
setPlanCompactDraft(String(planCompactValue));
|
|
909
|
-
setPlanCompactError(void 0);
|
|
910
|
-
}, [planCompactValue]);
|
|
911
|
-
/** Commit the threshold on blur/Enter only when it is a non-negative integer. */
|
|
912
|
-
const commitPlanCompactThreshold = () => {
|
|
913
|
-
const parsed = Number(planCompactDraft);
|
|
914
|
-
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
915
|
-
setPlanCompactError(props.t("planCompactThresholdInvalid"));
|
|
916
|
-
return;
|
|
917
|
-
}
|
|
918
|
-
if (parsed === planCompactValue) {
|
|
919
|
-
setPlanCompactError(void 0);
|
|
920
|
-
return;
|
|
921
|
-
}
|
|
922
|
-
setPlanCompactBusy(true);
|
|
923
|
-
setPlanCompactError(void 0);
|
|
924
|
-
props.setPlanCompactThreshold(parsed).then(() => {
|
|
925
|
-
setPlanCompactBusy(false);
|
|
926
|
-
}, (error) => {
|
|
927
|
-
setPlanCompactBusy(false);
|
|
928
|
-
setPlanCompactDraft(String(planCompactValue));
|
|
929
|
-
setPlanCompactError(error instanceof Error ? error.message : props.t("requestFailed"));
|
|
930
|
-
});
|
|
931
|
-
};
|
|
1131
|
+
const [groupProvider, setGroupProvider] = (0, react.useState)("");
|
|
1132
|
+
const [groupDraft, setGroupDraft] = (0, react.useState)(void 0);
|
|
932
1133
|
const loadSearchCapabilities = props.loadCapabilities;
|
|
933
1134
|
const [searchSnapshot, setSearchSnapshot] = (0, react.useState)(void 0);
|
|
934
1135
|
const [searchError, setSearchError] = (0, react.useState)(void 0);
|
|
@@ -967,6 +1168,60 @@ window.__ModuleLoader__.load({
|
|
|
967
1168
|
if (timer !== void 0) clearTimeout(timer);
|
|
968
1169
|
};
|
|
969
1170
|
}, [loadSearchCapabilities]);
|
|
1171
|
+
const providerGroups = normalizeProviderGroups(switchSettings.value?.providerGroups);
|
|
1172
|
+
/** Seed an unsaved group from the live routes, mirroring how the switches seed from Main. */
|
|
1173
|
+
const seedGroupFromActive = () => {
|
|
1174
|
+
const compact = compaction.value !== void 0 && routePairConfigured(compaction.value) ? {
|
|
1175
|
+
provider: compaction.value.provider ?? "",
|
|
1176
|
+
model: compaction.value.model ?? ""
|
|
1177
|
+
} : void 0;
|
|
1178
|
+
const review = autoReview.value !== void 0 && routePairConfigured(autoReview.value) ? {
|
|
1179
|
+
provider: autoReview.value.provider ?? "",
|
|
1180
|
+
model: autoReview.value.model ?? ""
|
|
1181
|
+
} : void 0;
|
|
1182
|
+
const fixedSubagent = subagent.value !== void 0 && subagent.value.mode === "fixed" && routePairConfigured(subagent.value) ? {
|
|
1183
|
+
provider: subagent.value.provider ?? "",
|
|
1184
|
+
model: subagent.value.model ?? "",
|
|
1185
|
+
reasoningEffort: subagent.value.reasoningEffort
|
|
1186
|
+
} : void 0;
|
|
1187
|
+
return {
|
|
1188
|
+
...compact === void 0 ? {} : {
|
|
1189
|
+
compactProvider: compact.provider,
|
|
1190
|
+
compactModel: compact.model
|
|
1191
|
+
},
|
|
1192
|
+
...review === void 0 ? {} : {
|
|
1193
|
+
autoReviewProvider: review.provider,
|
|
1194
|
+
autoReviewModel: review.model
|
|
1195
|
+
},
|
|
1196
|
+
...fixedSubagent === void 0 ? {} : {
|
|
1197
|
+
subagentProvider: fixedSubagent.provider,
|
|
1198
|
+
subagentModel: fixedSubagent.model,
|
|
1199
|
+
...fixedSubagent.reasoningEffort === void 0 ? {} : { subagentReasoningEffort: fixedSubagent.reasoningEffort }
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1202
|
+
};
|
|
1203
|
+
/** Reload the editor draft: the saved group when one exists, else a seed from the live routes. */
|
|
1204
|
+
const resyncGroupDraft = () => {
|
|
1205
|
+
if (groupProvider === "") {
|
|
1206
|
+
setGroupDraft(void 0);
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
setGroupDraft(groupOf(providerGroups, groupProvider) ?? seedGroupFromActive());
|
|
1210
|
+
};
|
|
1211
|
+
(0, react.useEffect)(() => {
|
|
1212
|
+
if (groupProvider !== "") return;
|
|
1213
|
+
const initial = controller.draft?.provider;
|
|
1214
|
+
if (initial !== void 0 && initial.trim() !== "") setGroupProvider(initial);
|
|
1215
|
+
}, [groupProvider, controller.draft]);
|
|
1216
|
+
(0, react.useEffect)(() => {
|
|
1217
|
+
resyncGroupDraft();
|
|
1218
|
+
}, [
|
|
1219
|
+
groupProvider,
|
|
1220
|
+
switchSettings.value,
|
|
1221
|
+
subagent.value,
|
|
1222
|
+
autoReview.value,
|
|
1223
|
+
compaction.value
|
|
1224
|
+
]);
|
|
970
1225
|
if ([
|
|
971
1226
|
controller.main,
|
|
972
1227
|
controller.subagent,
|
|
@@ -1020,13 +1275,16 @@ window.__ModuleLoader__.load({
|
|
|
1020
1275
|
const searchAvailable = searchLive?.searchProviderAdapters.available ?? false;
|
|
1021
1276
|
const imageChoices = capabilityChoices(groups, imageDraft, props.capabilities.imageProviderAdapters.providers ?? [], "image");
|
|
1022
1277
|
const autoReviewChoices = deriveRouteChoices(groups, autoReviewDraft);
|
|
1023
|
-
const autoReviewOn =
|
|
1278
|
+
const autoReviewOn = routePairConfigured(autoReview.value);
|
|
1279
|
+
const compactionChoices = deriveRouteChoices(groups, compactionDraft);
|
|
1280
|
+
const compactionOn = routePairConfigured(compaction.value);
|
|
1024
1281
|
const synced = [
|
|
1025
1282
|
main,
|
|
1026
1283
|
subagent,
|
|
1027
1284
|
search,
|
|
1028
1285
|
image,
|
|
1029
|
-
autoReview
|
|
1286
|
+
autoReview,
|
|
1287
|
+
compaction
|
|
1030
1288
|
].every((snapshot) => snapshot.status === "ready");
|
|
1031
1289
|
const run = async (route, operation) => {
|
|
1032
1290
|
if (busy !== void 0) return;
|
|
@@ -1092,6 +1350,13 @@ window.__ModuleLoader__.load({
|
|
|
1092
1350
|
if (autoReview.value?.model !== autoReviewDraft.model) await props.setCapability("autoReview", "model", autoReviewDraft.model);
|
|
1093
1351
|
});
|
|
1094
1352
|
};
|
|
1353
|
+
const saveCompaction = () => {
|
|
1354
|
+
if (compactionDraft === void 0) return;
|
|
1355
|
+
run("compaction", async () => {
|
|
1356
|
+
if (compaction.value?.provider !== compactionDraft.provider) await props.setCapability("compaction", "provider", compactionDraft.provider);
|
|
1357
|
+
if (compaction.value?.model !== compactionDraft.model) await props.setCapability("compaction", "model", compactionDraft.model);
|
|
1358
|
+
});
|
|
1359
|
+
};
|
|
1095
1360
|
/**
|
|
1096
1361
|
* The reviewer switch: Off unsets both stored fields (follow the session
|
|
1097
1362
|
* model); On seeds the route from Main so the pin takes effect immediately
|
|
@@ -1129,6 +1394,44 @@ window.__ModuleLoader__.load({
|
|
|
1129
1394
|
});
|
|
1130
1395
|
});
|
|
1131
1396
|
};
|
|
1397
|
+
/**
|
|
1398
|
+
* The compaction switch: Off unsets both stored fields (official default =
|
|
1399
|
+
* the conversation target); On seeds the route from Main so the pin takes
|
|
1400
|
+
* effect immediately and stays editable below. A failed write reports,
|
|
1401
|
+
* never flips silently.
|
|
1402
|
+
*/
|
|
1403
|
+
const persistCompactionEnabled = (enabled) => {
|
|
1404
|
+
if (busy === "compaction" || !compaction.writable || compaction.value === void 0) return;
|
|
1405
|
+
if (!enabled) {
|
|
1406
|
+
setOpen((current) => current === "compaction" ? void 0 : current);
|
|
1407
|
+
run("compaction", async () => {
|
|
1408
|
+
if (compaction.value?.provider !== void 0) await props.setCapability("compaction", "provider", void 0);
|
|
1409
|
+
if (compaction.value?.model !== void 0) await props.setCapability("compaction", "model", void 0);
|
|
1410
|
+
});
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
const seed = draft;
|
|
1414
|
+
if (seed === void 0 || seed.provider.trim() === "" || seed.model.trim() === "") return;
|
|
1415
|
+
setBusy("compaction");
|
|
1416
|
+
setMessage(void 0);
|
|
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(() => {
|
|
1421
|
+
setCompactionDraft({
|
|
1422
|
+
provider: seed.provider,
|
|
1423
|
+
model: seed.model
|
|
1424
|
+
});
|
|
1425
|
+
setBusy(void 0);
|
|
1426
|
+
setOpen("compaction");
|
|
1427
|
+
}, (error) => {
|
|
1428
|
+
setBusy(void 0);
|
|
1429
|
+
setMessage({
|
|
1430
|
+
route: "compaction",
|
|
1431
|
+
text: error instanceof Error ? error.message : props.t("requestFailed")
|
|
1432
|
+
});
|
|
1433
|
+
});
|
|
1434
|
+
};
|
|
1132
1435
|
const mainSummary = draft === void 0 ? props.t("loading") : compact(routeName(groups, draft), mainEffectiveEffort);
|
|
1133
1436
|
const subagentRole = subagentDraft?.mode === "fixed" && subagentDraft.provider !== void 0 && subagentDraft.provider !== "" ? props.providerRoleOf?.(subagentDraft.provider) : void 0;
|
|
1134
1437
|
const subagentOn = subagentDraft?.mode === "fixed";
|
|
@@ -1136,6 +1439,84 @@ window.__ModuleLoader__.load({
|
|
|
1136
1439
|
const subagentDisabled = subagent.status !== "ready" || !subagent.writable || subagentDraft === void 0 || busy === "subagent" || !subagentOn || (subagentDraft.provider ?? "").trim() === "" || (subagentDraft.model ?? "").trim() === "";
|
|
1137
1440
|
const capabilityDisabled = (route, snapshot, next) => snapshot.status !== "ready" || !snapshot.writable || next === void 0 || busy === route || (next.provider ?? "").trim() === "" || (next.model ?? "").trim() === "";
|
|
1138
1441
|
const autoReviewDisabled = autoReview.status !== "ready" || !autoReview.writable || autoReviewDraft === void 0 || busy === "autoReview" || !autoReviewOn || (autoReviewDraft.provider ?? "").trim() === "" || (autoReviewDraft.model ?? "").trim() === "";
|
|
1442
|
+
const compactionDisabled = compaction.status !== "ready" || !compaction.writable || compactionDraft === void 0 || busy === "compaction" || !compactionOn || (compactionDraft.provider ?? "").trim() === "" || (compactionDraft.model ?? "").trim() === "";
|
|
1443
|
+
const configuredGroupCount = Object.keys(providerGroups).length;
|
|
1444
|
+
const groupControlsDisabled = busy === "providerGroups" || switchSettings.status !== "ready" || !switchSettings.writable;
|
|
1445
|
+
const groupProviderChoices = deriveRouteChoices(groups, groupProvider === "" ? void 0 : { provider: groupProvider }).providers.map((choice) => groupOf(providerGroups, choice.id) === void 0 ? choice : {
|
|
1446
|
+
...choice,
|
|
1447
|
+
name: choice.name + " ✓"
|
|
1448
|
+
});
|
|
1449
|
+
const groupCompaction = groupDraft === void 0 ? void 0 : draftPair(groupDraft.compactProvider, groupDraft.compactModel);
|
|
1450
|
+
const groupReview = groupDraft === void 0 ? void 0 : draftPair(groupDraft.autoReviewProvider, groupDraft.autoReviewModel);
|
|
1451
|
+
const groupSubagent = groupDraft === void 0 ? void 0 : draftPair(groupDraft.subagentProvider, groupDraft.subagentModel);
|
|
1452
|
+
const groupCompactionChoices = deriveRouteChoices(groups, groupCompaction);
|
|
1453
|
+
const groupReviewChoices = deriveRouteChoices(groups, groupReview);
|
|
1454
|
+
const groupSubagentChoices = deriveRouteChoices(groups, groupSubagent);
|
|
1455
|
+
const groupSubMatched = matchCatalogModel(groups.find((group) => group.id === (groupDraft?.subagentProvider ?? ""))?.models ?? [], groupDraft?.subagentModel ?? "");
|
|
1456
|
+
const groupSubEfforts = (() => {
|
|
1457
|
+
const efforts = (groupSubMatched?.model.reasoning?.efforts ?? []).map((effort) => ({
|
|
1458
|
+
id: effort.id,
|
|
1459
|
+
name: effort.name
|
|
1460
|
+
}));
|
|
1461
|
+
const current = groupDraft?.subagentReasoningEffort;
|
|
1462
|
+
if (current !== void 0 && current !== "" && !efforts.some((option) => option.id === current)) efforts.push({
|
|
1463
|
+
id: current,
|
|
1464
|
+
name: current
|
|
1465
|
+
});
|
|
1466
|
+
return efforts;
|
|
1467
|
+
})();
|
|
1468
|
+
/** Enable a capability row by pinning it to the Main route, like the switches above seed from Main. */
|
|
1469
|
+
const enableGroupCapability = (kind) => {
|
|
1470
|
+
setGroupDraft((draft) => {
|
|
1471
|
+
const main = controller.draft;
|
|
1472
|
+
if (draft === void 0 || main === void 0) return draft;
|
|
1473
|
+
if (kind === "compaction") return {
|
|
1474
|
+
...draft,
|
|
1475
|
+
compactProvider: main.provider,
|
|
1476
|
+
compactModel: main.model
|
|
1477
|
+
};
|
|
1478
|
+
if (kind === "autoReview") return {
|
|
1479
|
+
...draft,
|
|
1480
|
+
autoReviewProvider: main.provider,
|
|
1481
|
+
autoReviewModel: main.model
|
|
1482
|
+
};
|
|
1483
|
+
return {
|
|
1484
|
+
...draft,
|
|
1485
|
+
subagentProvider: main.provider,
|
|
1486
|
+
subagentModel: main.model,
|
|
1487
|
+
...main.reasoningEffort === void 0 ? {} : { subagentReasoningEffort: main.reasoningEffort }
|
|
1488
|
+
};
|
|
1489
|
+
});
|
|
1490
|
+
};
|
|
1491
|
+
const disableGroupCapability = (kind) => {
|
|
1492
|
+
setGroupDraft((draft) => {
|
|
1493
|
+
if (draft === void 0) return draft;
|
|
1494
|
+
if (kind === "compaction") return {
|
|
1495
|
+
...draft,
|
|
1496
|
+
compactProvider: "",
|
|
1497
|
+
compactModel: ""
|
|
1498
|
+
};
|
|
1499
|
+
if (kind === "autoReview") return {
|
|
1500
|
+
...draft,
|
|
1501
|
+
autoReviewProvider: "",
|
|
1502
|
+
autoReviewModel: ""
|
|
1503
|
+
};
|
|
1504
|
+
return {
|
|
1505
|
+
...draft,
|
|
1506
|
+
subagentProvider: "",
|
|
1507
|
+
subagentModel: "",
|
|
1508
|
+
subagentReasoningEffort: ""
|
|
1509
|
+
};
|
|
1510
|
+
});
|
|
1511
|
+
};
|
|
1512
|
+
const saveProviderGroupCard = () => {
|
|
1513
|
+
if (groupProvider === "" || groupDraft === void 0) return;
|
|
1514
|
+
run("providerGroups", () => props.saveProviderGroup(groupProvider, groupDraft));
|
|
1515
|
+
};
|
|
1516
|
+
const deleteProviderGroupCard = () => {
|
|
1517
|
+
if (groupProvider === "") return;
|
|
1518
|
+
run("providerGroups", () => props.saveProviderGroup(groupProvider, void 0));
|
|
1519
|
+
};
|
|
1139
1520
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("main", {
|
|
1140
1521
|
className: ModelSwitchSettings_module_css_default.section,
|
|
1141
1522
|
children: [
|
|
@@ -1153,129 +1534,66 @@ window.__ModuleLoader__.load({
|
|
|
1153
1534
|
}),
|
|
1154
1535
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1155
1536
|
className: ModelSwitchSettings_module_css_default.group,
|
|
1156
|
-
children: [
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
strokeLinejoin: "round"
|
|
1181
|
-
})]
|
|
1182
|
-
})
|
|
1183
|
-
}),
|
|
1184
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1185
|
-
className: ModelSwitchSettings_module_css_default.rowCopy,
|
|
1186
|
-
children: [
|
|
1187
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1188
|
-
className: ModelSwitchSettings_module_css_default.rowTitle,
|
|
1189
|
-
children: props.t("compactOnSwitch")
|
|
1190
|
-
}),
|
|
1191
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1192
|
-
className: ModelSwitchSettings_module_css_default.hint,
|
|
1193
|
-
children: props.t("compactOnSwitchHelp")
|
|
1194
|
-
}),
|
|
1195
|
-
compactError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1196
|
-
className: cx(ModelSwitchSettings_module_css_default.hint, ModelSwitchSettings_module_css_default.message),
|
|
1197
|
-
children: compactError
|
|
1198
|
-
})
|
|
1199
|
-
]
|
|
1200
|
-
}),
|
|
1201
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
1202
|
-
checked: switchSettings.value?.compactOnSwitch !== false,
|
|
1203
|
-
disabled: !switchSettings.writable || compactBusy,
|
|
1204
|
-
label: props.t("compactOnSwitch"),
|
|
1205
|
-
onChange: (value) => {
|
|
1206
|
-
setCompactBusy(true);
|
|
1207
|
-
setCompactError(void 0);
|
|
1208
|
-
props.setCompactOnSwitch(value).then(() => {
|
|
1209
|
-
setCompactBusy(false);
|
|
1210
|
-
}, (error) => {
|
|
1211
|
-
setCompactBusy(false);
|
|
1212
|
-
setCompactError(error instanceof Error ? error.message : props.t("requestFailed"));
|
|
1213
|
-
});
|
|
1214
|
-
}
|
|
1537
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
1538
|
+
className: ModelSwitchSettings_module_css_default.groupLabel,
|
|
1539
|
+
children: props.t("sendProtection")
|
|
1540
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1541
|
+
className: ModelSwitchSettings_module_css_default.rowCard,
|
|
1542
|
+
children: [
|
|
1543
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1544
|
+
className: ModelSwitchSettings_module_css_default.routeIcon,
|
|
1545
|
+
"aria-hidden": "true",
|
|
1546
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
1547
|
+
width: "18",
|
|
1548
|
+
height: "18",
|
|
1549
|
+
viewBox: "0 0 18 18",
|
|
1550
|
+
fill: "none",
|
|
1551
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1552
|
+
d: "M4 5.5h10M4 9h7M4 12.5h5",
|
|
1553
|
+
stroke: "currentColor",
|
|
1554
|
+
strokeLinecap: "round"
|
|
1555
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1556
|
+
d: "M13 11.5v3.5l2-1.6",
|
|
1557
|
+
stroke: "currentColor",
|
|
1558
|
+
strokeLinecap: "round",
|
|
1559
|
+
strokeLinejoin: "round"
|
|
1560
|
+
})]
|
|
1215
1561
|
})
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
stroke: "currentColor",
|
|
1232
|
-
strokeLinecap: "round"
|
|
1233
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1234
|
-
d: "M13 11.5v3.5l2-1.6",
|
|
1235
|
-
stroke: "currentColor",
|
|
1236
|
-
strokeLinecap: "round",
|
|
1237
|
-
strokeLinejoin: "round"
|
|
1238
|
-
})]
|
|
1562
|
+
}),
|
|
1563
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1564
|
+
className: ModelSwitchSettings_module_css_default.rowCopy,
|
|
1565
|
+
children: [
|
|
1566
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1567
|
+
className: ModelSwitchSettings_module_css_default.rowTitle,
|
|
1568
|
+
children: props.t("compactOnSwitch")
|
|
1569
|
+
}),
|
|
1570
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1571
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
1572
|
+
children: props.t("compactOnSwitchHelp")
|
|
1573
|
+
}),
|
|
1574
|
+
compactError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1575
|
+
className: cx(ModelSwitchSettings_module_css_default.hint, ModelSwitchSettings_module_css_default.message),
|
|
1576
|
+
children: compactError
|
|
1239
1577
|
})
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
className: ModelSwitchSettings_module_css_default.input,
|
|
1260
|
-
type: "number",
|
|
1261
|
-
min: 0,
|
|
1262
|
-
step: 1e3,
|
|
1263
|
-
inputMode: "numeric",
|
|
1264
|
-
"aria-label": props.t("planCompactThreshold"),
|
|
1265
|
-
value: planCompactDraft,
|
|
1266
|
-
disabled: !switchSettings.writable || planCompactBusy,
|
|
1267
|
-
onChange: (event) => {
|
|
1268
|
-
setPlanCompactDraft(event.target.value);
|
|
1269
|
-
setPlanCompactError(void 0);
|
|
1270
|
-
},
|
|
1271
|
-
onBlur: commitPlanCompactThreshold,
|
|
1272
|
-
onKeyDown: (event) => {
|
|
1273
|
-
if (event.key === "Enter") commitPlanCompactThreshold();
|
|
1274
|
-
}
|
|
1275
|
-
})
|
|
1276
|
-
]
|
|
1277
|
-
})
|
|
1278
|
-
]
|
|
1578
|
+
]
|
|
1579
|
+
}),
|
|
1580
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
1581
|
+
checked: switchSettings.value?.compactOnSwitch !== false,
|
|
1582
|
+
disabled: !switchSettings.writable || compactBusy,
|
|
1583
|
+
label: props.t("compactOnSwitch"),
|
|
1584
|
+
onChange: (value) => {
|
|
1585
|
+
setCompactBusy(true);
|
|
1586
|
+
setCompactError(void 0);
|
|
1587
|
+
props.setCompactOnSwitch(value).then(() => {
|
|
1588
|
+
setCompactBusy(false);
|
|
1589
|
+
}, (error) => {
|
|
1590
|
+
setCompactBusy(false);
|
|
1591
|
+
setCompactError(error instanceof Error ? error.message : props.t("requestFailed"));
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
})
|
|
1595
|
+
]
|
|
1596
|
+
})]
|
|
1279
1597
|
}),
|
|
1280
1598
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1281
1599
|
className: ModelSwitchSettings_module_css_default.group,
|
|
@@ -1632,8 +1950,295 @@ window.__ModuleLoader__.load({
|
|
|
1632
1950
|
onSave: saveAutoReview
|
|
1633
1951
|
})
|
|
1634
1952
|
] })
|
|
1953
|
+
}),
|
|
1954
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteCard, {
|
|
1955
|
+
title: props.t("compaction"),
|
|
1956
|
+
summary: compactionDraft === void 0 ? props.t("loading") : compactionOn ? routeName(groups, compactionDraft) : props.t("compactionFollow"),
|
|
1957
|
+
hint: props.t("compactionHelp"),
|
|
1958
|
+
icon: "compaction",
|
|
1959
|
+
open: open === "compaction" && compactionOn,
|
|
1960
|
+
onToggle: () => {
|
|
1961
|
+
if (compactionOn) toggle("compaction");
|
|
1962
|
+
},
|
|
1963
|
+
expandable: compactionOn,
|
|
1964
|
+
trailing: compactionDraft === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
1965
|
+
checked: compactionOn,
|
|
1966
|
+
disabled: busy === "compaction" || !compaction.writable,
|
|
1967
|
+
label: props.t("compaction"),
|
|
1968
|
+
onChange: persistCompactionEnabled
|
|
1969
|
+
}),
|
|
1970
|
+
children: compactionDraft === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1971
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
1972
|
+
children: props.t("loading")
|
|
1973
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1974
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1975
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
1976
|
+
children: props.t("compactionRouteHelp")
|
|
1977
|
+
}),
|
|
1978
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1979
|
+
className: ModelSwitchSettings_module_css_default.formGrid,
|
|
1980
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1981
|
+
label: props.t("provider"),
|
|
1982
|
+
value: compactionDraft.provider ?? "",
|
|
1983
|
+
disabled: busy === "compaction" || !compaction.writable,
|
|
1984
|
+
choices: compactionChoices.providers,
|
|
1985
|
+
onChange: (provider) => {
|
|
1986
|
+
const first = groups.find((group) => group.id === provider)?.models[0];
|
|
1987
|
+
setCompactionDraft({
|
|
1988
|
+
provider,
|
|
1989
|
+
...first === void 0 ? {} : { model: first.id }
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1993
|
+
label: props.t("model"),
|
|
1994
|
+
value: compactionDraft.model ?? "",
|
|
1995
|
+
disabled: busy === "compaction" || !compaction.writable,
|
|
1996
|
+
choices: compactionChoices.models,
|
|
1997
|
+
onChange: (model) => {
|
|
1998
|
+
setCompactionDraft({
|
|
1999
|
+
...compactionDraft,
|
|
2000
|
+
model
|
|
2001
|
+
});
|
|
2002
|
+
}
|
|
2003
|
+
})]
|
|
2004
|
+
}),
|
|
2005
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Actions, {
|
|
2006
|
+
t: props.t,
|
|
2007
|
+
busy: busy === "compaction",
|
|
2008
|
+
disabled: compactionDisabled,
|
|
2009
|
+
...message?.route === "compaction" ? { message: message.text } : {},
|
|
2010
|
+
onCancel: () => {
|
|
2011
|
+
resetCompaction();
|
|
2012
|
+
setMessage(void 0);
|
|
2013
|
+
},
|
|
2014
|
+
onSave: saveCompaction
|
|
2015
|
+
})
|
|
2016
|
+
] })
|
|
1635
2017
|
})
|
|
1636
2018
|
]
|
|
2019
|
+
}),
|
|
2020
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2021
|
+
className: ModelSwitchSettings_module_css_default.group,
|
|
2022
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
2023
|
+
className: ModelSwitchSettings_module_css_default.groupLabel,
|
|
2024
|
+
children: props.t("providerGroups")
|
|
2025
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(RouteCard, {
|
|
2026
|
+
title: props.t("providerGroupsCard"),
|
|
2027
|
+
summary: props.t("providerGroupsSummary"),
|
|
2028
|
+
hint: props.t("providerGroupsHelp"),
|
|
2029
|
+
icon: "providerGroups",
|
|
2030
|
+
open: open === "providerGroups",
|
|
2031
|
+
onToggle: () => {
|
|
2032
|
+
toggle("providerGroups");
|
|
2033
|
+
},
|
|
2034
|
+
...configuredGroupCount > 0 ? { badge: String(configuredGroupCount) } : {},
|
|
2035
|
+
children: [
|
|
2036
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2037
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
2038
|
+
children: props.t("providerGroupsBodyHelp")
|
|
2039
|
+
}),
|
|
2040
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2041
|
+
className: ModelSwitchSettings_module_css_default.formGrid,
|
|
2042
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2043
|
+
label: props.t("provider"),
|
|
2044
|
+
value: groupProvider,
|
|
2045
|
+
disabled: groupControlsDisabled,
|
|
2046
|
+
choices: groupProviderChoices,
|
|
2047
|
+
onChange: setGroupProvider
|
|
2048
|
+
})
|
|
2049
|
+
}),
|
|
2050
|
+
groupProvider === "" || groupDraft === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2051
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
2052
|
+
children: props.t("providerGroupPick")
|
|
2053
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2054
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2055
|
+
className: ModelSwitchSettings_module_css_default.rowCard,
|
|
2056
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2057
|
+
className: ModelSwitchSettings_module_css_default.rowCopy,
|
|
2058
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2059
|
+
className: ModelSwitchSettings_module_css_default.rowTitle,
|
|
2060
|
+
children: props.t("compaction")
|
|
2061
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2062
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
2063
|
+
children: groupCompaction === void 0 ? props.t("compactionFollow") : routeName(groups, groupCompaction)
|
|
2064
|
+
})]
|
|
2065
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
2066
|
+
checked: groupCompaction !== void 0,
|
|
2067
|
+
disabled: groupControlsDisabled,
|
|
2068
|
+
label: props.t("compaction"),
|
|
2069
|
+
onChange: (value) => {
|
|
2070
|
+
if (value) enableGroupCapability("compaction");
|
|
2071
|
+
else disableGroupCapability("compaction");
|
|
2072
|
+
}
|
|
2073
|
+
})]
|
|
2074
|
+
}),
|
|
2075
|
+
groupCompaction === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2076
|
+
className: ModelSwitchSettings_module_css_default.formGrid,
|
|
2077
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2078
|
+
label: props.t("provider"),
|
|
2079
|
+
value: groupCompaction.provider,
|
|
2080
|
+
disabled: groupControlsDisabled,
|
|
2081
|
+
choices: groupCompactionChoices.providers,
|
|
2082
|
+
onChange: (provider) => {
|
|
2083
|
+
const first = groups.find((item) => item.id === provider)?.models[0];
|
|
2084
|
+
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
2085
|
+
...draft,
|
|
2086
|
+
compactProvider: provider,
|
|
2087
|
+
compactModel: first?.id ?? ""
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2090
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2091
|
+
label: props.t("model"),
|
|
2092
|
+
value: groupCompaction.model,
|
|
2093
|
+
disabled: groupControlsDisabled,
|
|
2094
|
+
choices: groupCompactionChoices.models,
|
|
2095
|
+
onChange: (model) => {
|
|
2096
|
+
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
2097
|
+
...draft,
|
|
2098
|
+
compactModel: model
|
|
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
|
|
2148
|
+
});
|
|
2149
|
+
}
|
|
2150
|
+
})]
|
|
2151
|
+
}),
|
|
2152
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2153
|
+
className: ModelSwitchSettings_module_css_default.rowCard,
|
|
2154
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2155
|
+
className: ModelSwitchSettings_module_css_default.rowCopy,
|
|
2156
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2157
|
+
className: ModelSwitchSettings_module_css_default.rowTitle,
|
|
2158
|
+
children: props.t("subagent")
|
|
2159
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2160
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
2161
|
+
children: groupSubagent === void 0 ? props.t("subagentOff") : routeName(groups, groupSubagent)
|
|
2162
|
+
})]
|
|
2163
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
2164
|
+
checked: groupSubagent !== void 0,
|
|
2165
|
+
disabled: groupControlsDisabled,
|
|
2166
|
+
label: props.t("subagent"),
|
|
2167
|
+
onChange: (value) => {
|
|
2168
|
+
if (value) enableGroupCapability("subagent");
|
|
2169
|
+
else disableGroupCapability("subagent");
|
|
2170
|
+
}
|
|
2171
|
+
})]
|
|
2172
|
+
}),
|
|
2173
|
+
groupSubagent === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2174
|
+
className: ModelSwitchSettings_module_css_default.formGrid,
|
|
2175
|
+
children: [
|
|
2176
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2177
|
+
label: props.t("provider"),
|
|
2178
|
+
value: groupSubagent.provider,
|
|
2179
|
+
disabled: groupControlsDisabled,
|
|
2180
|
+
choices: groupSubagentChoices.providers,
|
|
2181
|
+
onChange: (provider) => {
|
|
2182
|
+
const first = groups.find((item) => item.id === provider)?.models[0];
|
|
2183
|
+
const routed = selectRouteModel(groups, provider, first?.id ?? groupSubagent.model);
|
|
2184
|
+
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
2185
|
+
...draft,
|
|
2186
|
+
subagentProvider: routed.provider,
|
|
2187
|
+
subagentModel: routed.model,
|
|
2188
|
+
subagentReasoningEffort: routed.reasoningEffort ?? ""
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
}),
|
|
2192
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2193
|
+
label: props.t("model"),
|
|
2194
|
+
value: groupSubagent.model,
|
|
2195
|
+
disabled: groupControlsDisabled,
|
|
2196
|
+
choices: groupSubagentChoices.models,
|
|
2197
|
+
onChange: (model) => {
|
|
2198
|
+
const routed = selectRouteModel(groups, groupSubagent.provider, model);
|
|
2199
|
+
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
2200
|
+
...draft,
|
|
2201
|
+
subagentProvider: routed.provider,
|
|
2202
|
+
subagentModel: routed.model,
|
|
2203
|
+
subagentReasoningEffort: routed.reasoningEffort ?? ""
|
|
2204
|
+
});
|
|
2205
|
+
}
|
|
2206
|
+
}),
|
|
2207
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2208
|
+
label: props.t("effort"),
|
|
2209
|
+
value: groupDraft.subagentReasoningEffort ?? "",
|
|
2210
|
+
disabled: groupControlsDisabled,
|
|
2211
|
+
choices: groupSubEfforts,
|
|
2212
|
+
onChange: (effort) => {
|
|
2213
|
+
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
2214
|
+
...draft,
|
|
2215
|
+
subagentReasoningEffort: effort
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
})
|
|
2219
|
+
]
|
|
2220
|
+
}),
|
|
2221
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Actions, {
|
|
2222
|
+
t: props.t,
|
|
2223
|
+
busy: busy === "providerGroups",
|
|
2224
|
+
disabled: groupControlsDisabled || groupProvider === "",
|
|
2225
|
+
...message?.route === "providerGroups" ? { message: message.text } : {},
|
|
2226
|
+
extra: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2227
|
+
type: "button",
|
|
2228
|
+
className: cx(ModelSwitchSettings_module_css_default.button, ModelSwitchSettings_module_css_default.secondaryButton),
|
|
2229
|
+
disabled: groupControlsDisabled || groupOf(providerGroups, groupProvider) === void 0,
|
|
2230
|
+
onClick: deleteProviderGroupCard,
|
|
2231
|
+
children: props.t("providerGroupDelete")
|
|
2232
|
+
}),
|
|
2233
|
+
onCancel: () => {
|
|
2234
|
+
resyncGroupDraft();
|
|
2235
|
+
setMessage(void 0);
|
|
2236
|
+
},
|
|
2237
|
+
onSave: saveProviderGroupCard
|
|
2238
|
+
})
|
|
2239
|
+
] })
|
|
2240
|
+
]
|
|
2241
|
+
})]
|
|
1637
2242
|
})
|
|
1638
2243
|
]
|
|
1639
2244
|
});
|
|
@@ -1680,9 +2285,18 @@ window.__ModuleLoader__.load({
|
|
|
1680
2285
|
autoReviewFollow: "跟随会话模型",
|
|
1681
2286
|
autoReviewHelp: "为官方 auto-review(Auto 权限预设的逐调用审查)指定审查模型;需启用官方 auto-review 能力包。关闭时审查跟随当前会话模型。",
|
|
1682
2287
|
autoReviewRouteHelp: "审查请求固定使用这里的 Provider/模型,与会话模型无关;保存后对下一次审查立即生效。",
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
2288
|
+
compaction: "压缩模型",
|
|
2289
|
+
compactionFollow: "跟随会话模型",
|
|
2290
|
+
compactionHelp: "为官方压缩(/compact 与官方自动压缩)指定摘要模型;关闭(两个字段都留空)时使用官方默认,即当前会话的目标模型。",
|
|
2291
|
+
compactionRouteHelp: "摘要请求固定使用这里的 Provider/模型,与会话目标模型无关;保存后对下一次压缩立即生效。保存时若恰有压缩进行中,可能被打断并在下次重试。",
|
|
2292
|
+
providerGroups: "供应商配置组",
|
|
2293
|
+
providerGroupsCard: "按供应商预设路由",
|
|
2294
|
+
providerGroupsSummary: "切换主模型供应商时自动应用对应组",
|
|
2295
|
+
providerGroupsHelp: "为压缩、子代理、审查按供应商分别保存路由。",
|
|
2296
|
+
providerGroupsBodyHelp: "每个供应商保存一组压缩、子代理、审查路由;主模型切换到该供应商时,这三条路由自动写入上方对应卡片。切换到未配置的供应商时现有路由保持不变;应用会覆盖三张卡片的当前值(含手动修改)。",
|
|
2297
|
+
providerGroupPick: "选择供应商以编辑其配置组。",
|
|
2298
|
+
providerGroupDelete: "删除该组",
|
|
2299
|
+
providerGroupApplyFailed: "主模型已保存,但供应商组应用失败:",
|
|
1686
2300
|
"reason.central-subagent-routing": "Alpha.4 没有全局子代理启动路由接口。",
|
|
1687
2301
|
"reason.packaged-preset-roots": "Alpha.4 不支持插件提供额外 preset root。",
|
|
1688
2302
|
"reason.tool-owner-suppression": "Alpha.4 没有工具所有者或来源抑制接口。",
|
|
@@ -1730,9 +2344,18 @@ window.__ModuleLoader__.load({
|
|
|
1730
2344
|
autoReviewFollow: "Follows the session model",
|
|
1731
2345
|
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.",
|
|
1732
2346
|
autoReviewRouteHelp: "Review requests use exactly this provider/model regardless of the session model; saved changes apply to the very next review.",
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
2347
|
+
compaction: "Compaction model",
|
|
2348
|
+
compactionFollow: "Follows the conversation model",
|
|
2349
|
+
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
|
+
compactionRouteHelp: "Summarization requests use exactly this provider/model regardless of the conversation target; saved changes apply to the very next compaction. Saving while a compaction is running may interrupt it; it retries next time.",
|
|
2351
|
+
providerGroups: "Provider groups",
|
|
2352
|
+
providerGroupsCard: "Per-provider route presets",
|
|
2353
|
+
providerGroupsSummary: "Applied automatically when the main model provider switches",
|
|
2354
|
+
providerGroupsHelp: "Save compaction, subagent, and review routes per provider.",
|
|
2355
|
+
providerGroupsBodyHelp: "Each provider stores one compaction, subagent, and review route group. Switching the main model to that provider writes these three routes into the cards above; switching to a provider without a group leaves the current routes untouched. Applying a group overwrites those three cards, including manual edits.",
|
|
2356
|
+
providerGroupPick: "Pick a provider to edit its group.",
|
|
2357
|
+
providerGroupDelete: "Delete group",
|
|
2358
|
+
providerGroupApplyFailed: "Main model saved, but applying the provider group failed: ",
|
|
1736
2359
|
"reason.central-subagent-routing": "Alpha.4 exposes no global Subagent start-routing seam.",
|
|
1737
2360
|
"reason.packaged-preset-roots": "Alpha.4 exposes no plugin-owned preset root.",
|
|
1738
2361
|
"reason.tool-owner-suppression": "Alpha.4 exposes no tool owner or provenance suppression seam.",
|
|
@@ -1826,14 +2449,6 @@ window.__ModuleLoader__.load({
|
|
|
1826
2449
|
return false;
|
|
1827
2450
|
}
|
|
1828
2451
|
}
|
|
1829
|
-
//#endregion
|
|
1830
|
-
//#region src/picker/plan-compact.ts
|
|
1831
|
-
/** Runtime guard for the wire value returned by the Host RPC. */
|
|
1832
|
-
function isPlanCompactOutcome(value) {
|
|
1833
|
-
if (value === null || typeof value !== "object") return false;
|
|
1834
|
-
const status = value.status;
|
|
1835
|
-
return status === "disabled" || status === "skipped" || status === "compacted" || status === "empty" || status === "error";
|
|
1836
|
-
}
|
|
1837
2452
|
const CONTEXT_SUFFIX = /-(\d+)(k|m)$/iu;
|
|
1838
2453
|
/** Peel Fast and `-<n>k` / `-<n>m` in either order. Product names like `-max` stay. */
|
|
1839
2454
|
function parsePickerId(id) {
|
|
@@ -3119,7 +3734,7 @@ window.__ModuleLoader__.load({
|
|
|
3119
3734
|
};
|
|
3120
3735
|
}
|
|
3121
3736
|
//#endregion
|
|
3122
|
-
//#region node_modules/.pnpm/dsh-llm-providers-ui@https+
|
|
3737
|
+
//#region node_modules/.pnpm/dsh-llm-providers-ui@https+++github.com+NOirBRight+dsh-llm-providers-ui+releases+downlo_f1dadb1b56406564fc1bb8f2d953b82d/node_modules/dsh-llm-providers-ui/lib/order.js
|
|
3123
3738
|
/** Display order for installed provider cards when the user has not saved one. */
|
|
3124
3739
|
const PROVIDER_ITEM_ORDER = [
|
|
3125
3740
|
"llm-cursor",
|
|
@@ -3407,7 +4022,28 @@ window.__ModuleLoader__.load({
|
|
|
3407
4022
|
};
|
|
3408
4023
|
}
|
|
3409
4024
|
/**
|
|
3410
|
-
*
|
|
4025
|
+
* Address the plan document exactly as the official opener does.
|
|
4026
|
+
*
|
|
4027
|
+
* A logged plan (`callId` present) resolves from `dsh-resource://plan/...`
|
|
4028
|
+
* for as long as the session log holds the call; before that the pending
|
|
4029
|
+
* review's own `dsh-resource://plan-review/...` address is the one that
|
|
4030
|
+
* opens its preview.
|
|
4031
|
+
*
|
|
4032
|
+
* @param sessionId - the session that owns the review.
|
|
4033
|
+
* @param ref - logged call identity plus the pending request key.
|
|
4034
|
+
* @returns The plan resource address.
|
|
4035
|
+
*/
|
|
4036
|
+
function planDocumentAddress(sessionId, ref) {
|
|
4037
|
+
const encode = encodeURIComponent;
|
|
4038
|
+
return ref.callId === void 0 ? `dsh-resource://plan-review/${encode(sessionId)}/${encode(ref.requestKey)}` : `dsh-resource://plan/${encode(sessionId)}/${encode(ref.callId)}`;
|
|
4039
|
+
}
|
|
4040
|
+
/**
|
|
4041
|
+
* Make this plan the session's armed goal, replacing any unfinished goal.
|
|
4042
|
+
*
|
|
4043
|
+
* The objective is a short instruction carrying the plan document's address,
|
|
4044
|
+
* not the plan's raw markdown: goal objectives are injected into every round,
|
|
4045
|
+
* and a full plan would be truncated by length limits mid-sentence. The round
|
|
4046
|
+
* driver reads the plan from the address instead.
|
|
3411
4047
|
*
|
|
3412
4048
|
* Ordering mirrors the card's own commit-then-answer rule: a refusal here keeps
|
|
3413
4049
|
* the Plan pending instead of approving work that no goal tracks. The replace
|
|
@@ -3416,17 +4052,18 @@ window.__ModuleLoader__.load({
|
|
|
3416
4052
|
*
|
|
3417
4053
|
* @param remote - the released goal remote, absent when no goal package is mounted.
|
|
3418
4054
|
* @param sessionId - the session that owns both the review and the goal.
|
|
3419
|
-
* @param plan - the plan markdown under review.
|
|
4055
|
+
* @param plan - the plan markdown under review (validated non-empty).
|
|
4056
|
+
* @param ref - the plan document identity used for its address.
|
|
3420
4057
|
* @returns Whether the goal now tracks this plan, or why it does not.
|
|
3421
4058
|
*/
|
|
3422
|
-
async function setPlanAsGoal(remote, sessionId, plan) {
|
|
4059
|
+
async function setPlanAsGoal(remote, sessionId, plan, ref) {
|
|
3423
4060
|
if (remote === void 0) return { kind: "unsupported" };
|
|
3424
|
-
|
|
3425
|
-
if (objective === "") return {
|
|
4061
|
+
if (plan.trim() === "") return {
|
|
3426
4062
|
kind: "failed",
|
|
3427
4063
|
code: "GOAL_INVALID_OBJECTIVE",
|
|
3428
4064
|
message: "goal objective must be a non-empty string"
|
|
3429
4065
|
};
|
|
4066
|
+
const objective = `持续推进当前plan直到完成(plan 文档:${planDocumentAddress(sessionId, ref)})`;
|
|
3430
4067
|
const created = await remote.create(sessionId, { objective });
|
|
3431
4068
|
if (created.ok) return { kind: "created" };
|
|
3432
4069
|
if (created.error.code !== "GOAL_ALREADY_EXISTS") return {
|
|
@@ -3624,12 +4261,11 @@ window.__ModuleLoader__.load({
|
|
|
3624
4261
|
t: props.t,
|
|
3625
4262
|
goalRemote: props.goalRemote,
|
|
3626
4263
|
planPreview: props.planPreview,
|
|
3627
|
-
...props.planCompact === void 0 ? {} : { planCompact: props.planCompact },
|
|
3628
4264
|
...props.resolveInteractionOperations === void 0 ? {} : { resolveInteractionOperations: props.resolveInteractionOperations },
|
|
3629
4265
|
...props.roleOf === void 0 ? {} : { roleOf: props.roleOf }
|
|
3630
4266
|
}, props.matched.key);
|
|
3631
4267
|
}
|
|
3632
|
-
function PlanReviewState({ matched, review, available, providerLock, agentLocked, lockFailed, settingsUnavailableReason, directory, t, goalRemote, planPreview,
|
|
4268
|
+
function PlanReviewState({ matched, review, available, providerLock, agentLocked, lockFailed, settingsUnavailableReason, directory, t, goalRemote, planPreview, resolveInteractionOperations, roleOf }) {
|
|
3633
4269
|
const { snapshot, getDirectorySnapshot, load, select } = directory;
|
|
3634
4270
|
const [execution, setExecution] = (0, react.useState)(snapshot.current ?? void 0);
|
|
3635
4271
|
const [feedback, setFeedback] = (0, react.useState)("");
|
|
@@ -3715,36 +4351,9 @@ window.__ModuleLoader__.load({
|
|
|
3715
4351
|
throw new Error(message === null ? t("plan.modelFailed") : t("error.action", { message }));
|
|
3716
4352
|
}
|
|
3717
4353
|
};
|
|
3718
|
-
/**
|
|
3719
|
-
* Compact the history before a switched approval commits anything.
|
|
3720
|
-
*
|
|
3721
|
-
* Only an approval that changes provider/model triggers the Host check —
|
|
3722
|
-
* effort-only changes and same-model approvals execute untouched. A failed
|
|
3723
|
-
* compaction keeps the Plan pending and retryable; `empty` announces that
|
|
3724
|
-
* nothing safe could be reduced and execution proceeds.
|
|
3725
|
-
*/
|
|
3726
|
-
const compactBeforeRun = async () => {
|
|
3727
|
-
if (execution === void 0) return;
|
|
3728
|
-
const current = snapshot.current;
|
|
3729
|
-
if (!(current === null || current === void 0 || current.provider !== execution.provider || current.model !== execution.model)) return;
|
|
3730
|
-
if (planCompact === void 0) {
|
|
3731
|
-
console.warn("dsh-model-switch: plan compaction seam absent; executing the switched Plan without it");
|
|
3732
|
-
return;
|
|
3733
|
-
}
|
|
3734
|
-
setNotice(t("plan.compacting", { model: execution.model }));
|
|
3735
|
-
let outcome;
|
|
3736
|
-
try {
|
|
3737
|
-
outcome = await planCompact(matched.sessionId, execution.provider, execution.model);
|
|
3738
|
-
} catch (cause) {
|
|
3739
|
-
throw new Error(t("plan.compactFailed", { message: cause instanceof Error ? cause.message : String(cause) }));
|
|
3740
|
-
}
|
|
3741
|
-
if (outcome.status === "error") throw new Error(t("plan.compactFailed", { message: outcome.message ?? "unknown failure" }));
|
|
3742
|
-
setNotice(outcome.status === "empty" ? t("plan.compactEmpty") : outcome.status === "compacted" ? t("plan.compacted") : null);
|
|
3743
|
-
};
|
|
3744
4354
|
const onApprove = () => {
|
|
3745
4355
|
if (execution === void 0 || !executionAllowed || !available || busy || blocked) return;
|
|
3746
4356
|
settle(async () => {
|
|
3747
|
-
await compactBeforeRun();
|
|
3748
4357
|
await approveWith(execution);
|
|
3749
4358
|
});
|
|
3750
4359
|
};
|
|
@@ -3759,8 +4368,10 @@ window.__ModuleLoader__.load({
|
|
|
3759
4368
|
const onRunAsGoal = () => {
|
|
3760
4369
|
if (execution === void 0 || !executionAllowed || !available || busy || blocked || goal === void 0) return;
|
|
3761
4370
|
settle(async () => {
|
|
3762
|
-
await
|
|
3763
|
-
|
|
4371
|
+
const handoff = await setPlanAsGoal(goal, matched.sessionId, review.plan, {
|
|
4372
|
+
...review.callId === void 0 ? {} : { callId: review.callId },
|
|
4373
|
+
requestKey: matched.key
|
|
4374
|
+
});
|
|
3764
4375
|
if (handoff.kind === "unsupported") throw new Error(t("plan.goalUnsupported"));
|
|
3765
4376
|
if (handoff.kind === "failed") {
|
|
3766
4377
|
const message = handoff.code === void 0 ? handoff.message : `${handoff.code}: ${handoff.message}`;
|
|
@@ -3998,10 +4609,6 @@ window.__ModuleLoader__.load({
|
|
|
3998
4609
|
"plan.goalUnsupported": "当前部署未挂载 goal 服务,无法把计划设为目标",
|
|
3999
4610
|
"plan.responseRejected": "计划答复已被另一客户端处理;已提交的模型切换无法由插件回滚。",
|
|
4000
4611
|
"plan.cancelRejected": "计划审查已被另一客户端处理,无法返回讨论。",
|
|
4001
|
-
"plan.compacting": "正在压缩上下文,将使用 {model}…",
|
|
4002
|
-
"plan.compacted": "上下文已压缩,开始执行。",
|
|
4003
|
-
"plan.compactEmpty": "没有可压缩的历史,继续执行。",
|
|
4004
|
-
"plan.compactFailed": "压缩上下文失败:{message}(计划仍待批准,可重试)",
|
|
4005
4612
|
"markdown.copy": "复制代码",
|
|
4006
4613
|
"markdown.copied": "已复制",
|
|
4007
4614
|
"markdown.footnotes": "脚注"
|
|
@@ -4058,10 +4665,6 @@ window.__ModuleLoader__.load({
|
|
|
4058
4665
|
"plan.goalUnsupported": "This deployment mounts no goal service, so the Plan cannot be set as a goal",
|
|
4059
4666
|
"plan.responseRejected": "Another client already handled this Plan response. A committed model change cannot be rolled back by this plugin.",
|
|
4060
4667
|
"plan.cancelRejected": "Another client already handled this Plan review, so it cannot return to discussion.",
|
|
4061
|
-
"plan.compacting": "Compacting context with {model}…",
|
|
4062
|
-
"plan.compacted": "Context compacted; starting execution.",
|
|
4063
|
-
"plan.compactEmpty": "No compactable history; continuing.",
|
|
4064
|
-
"plan.compactFailed": "Context compaction failed: {message} (the Plan stays pending and can be retried)",
|
|
4065
4668
|
"markdown.copy": "Copy code",
|
|
4066
4669
|
"markdown.copied": "Copied",
|
|
4067
4670
|
"markdown.footnotes": "Footnotes"
|
|
@@ -4084,45 +4687,6 @@ window.__ModuleLoader__.load({
|
|
|
4084
4687
|
}
|
|
4085
4688
|
const EMPTY_ORDER = [];
|
|
4086
4689
|
/**
|
|
4087
|
-
* Ask the Host to compact this session before a switched approval executes.
|
|
4088
|
-
*
|
|
4089
|
-
* An absent connection degrades to `skipped` (with a console note): the
|
|
4090
|
-
* mechanism protects against long-context timeouts, it must not brick plan
|
|
4091
|
-
* approvals in environments that expose no Host RPC at all.
|
|
4092
|
-
* @param rpcOf - resolves the live connection RPC.
|
|
4093
|
-
* @returns The card-facing runner bound to this seat's connection.
|
|
4094
|
-
*/
|
|
4095
|
-
function planCompactFrom(rpcOf) {
|
|
4096
|
-
return async (sessionId, provider, model) => {
|
|
4097
|
-
const rpc = rpcOf();
|
|
4098
|
-
if (rpc === void 0) {
|
|
4099
|
-
console.warn("dsh-model-switch: Host RPC unavailable; executing the Plan without a compaction check");
|
|
4100
|
-
return { status: "skipped" };
|
|
4101
|
-
}
|
|
4102
|
-
try {
|
|
4103
|
-
const response = await callPluginRpc(rpc, "plugin-rpc/model-switch", "plan-compact", {
|
|
4104
|
-
sessionId,
|
|
4105
|
-
provider,
|
|
4106
|
-
model
|
|
4107
|
-
});
|
|
4108
|
-
if (!response.ok) return {
|
|
4109
|
-
status: "error",
|
|
4110
|
-
message: "the Host rejected the plan compaction request"
|
|
4111
|
-
};
|
|
4112
|
-
if (!isPlanCompactOutcome(response.value)) return {
|
|
4113
|
-
status: "error",
|
|
4114
|
-
message: "the Host returned an unreadable plan compaction verdict"
|
|
4115
|
-
};
|
|
4116
|
-
return response.value;
|
|
4117
|
-
} catch (cause) {
|
|
4118
|
-
return {
|
|
4119
|
-
status: "error",
|
|
4120
|
-
message: cause instanceof Error ? cause.message : String(cause)
|
|
4121
|
-
};
|
|
4122
|
-
}
|
|
4123
|
-
};
|
|
4124
|
-
}
|
|
4125
|
-
/**
|
|
4126
4690
|
* Resolve the released goal remote without hard-depending on it.
|
|
4127
4691
|
*
|
|
4128
4692
|
* `remote.goals` exists only when the deployment mounts `@deepseek-ai/dsh-goal`,
|
|
@@ -4240,6 +4804,7 @@ window.__ModuleLoader__.load({
|
|
|
4240
4804
|
const models = scope.modelDirectories;
|
|
4241
4805
|
const sessions = scope.sessions;
|
|
4242
4806
|
const mainDefaults = scope.configForms.get(MAIN_DEFAULT_CONFIG_ID);
|
|
4807
|
+
const ownedForm = scope.configForms.get(MODEL_SWITCH_CONFIG_ID);
|
|
4243
4808
|
let directoryService;
|
|
4244
4809
|
const orderStore = providerOrderStore(scope.configForms.get(PROVIDERS_CONFIG_ID));
|
|
4245
4810
|
scope.inject(["providerDirectory"], (directoryScope) => {
|
|
@@ -4282,7 +4847,6 @@ window.__ModuleLoader__.load({
|
|
|
4282
4847
|
getMainDefaultsSnapshot: () => mainDefaults.getSnapshot(),
|
|
4283
4848
|
goalRemote: () => goalRemoteFrom(scope),
|
|
4284
4849
|
planPreview,
|
|
4285
|
-
planCompact: planCompactFrom(rpcOf),
|
|
4286
4850
|
hooks: {
|
|
4287
4851
|
directory: directory.store,
|
|
4288
4852
|
providerOrder: orderStore
|
|
@@ -4314,6 +4878,11 @@ window.__ModuleLoader__.load({
|
|
|
4314
4878
|
return uncommitted;
|
|
4315
4879
|
}
|
|
4316
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);
|
|
4885
|
+
}
|
|
4317
4886
|
return {
|
|
4318
4887
|
committed: true,
|
|
4319
4888
|
mainDefaultRestored: (await restoreMainDefault(mainDefaults, defaultBeforeSwitch)).restored
|
|
@@ -4425,7 +4994,13 @@ window.__ModuleLoader__.load({
|
|
|
4425
4994
|
const search = deriveConfigForm(owned, deriveSearchSettings, SEARCH_SETTINGS_FIELDS);
|
|
4426
4995
|
const image = deriveConfigForm(owned, deriveImageSettings, IMAGE_SETTINGS_FIELDS);
|
|
4427
4996
|
const autoReview = deriveConfigForm(owned, deriveAutoReviewSettings, AUTO_REVIEW_SETTINGS_FIELDS);
|
|
4997
|
+
const compaction = deriveConfigForm(owned, deriveCompactionSettings, COMPACTION_SETTINGS_FIELDS);
|
|
4998
|
+
/** Adopt the target provider's saved group; a provider without a group leaves the routes untouched. */
|
|
4999
|
+
const applyProviderGroupFor = async (provider) => {
|
|
5000
|
+
await applyProviderGroupTo(owned, normalizeProviderGroups(owned.getSnapshot().value?.providerGroups), provider);
|
|
5001
|
+
};
|
|
4428
5002
|
const saveMain = async (next, expectedRevision) => {
|
|
5003
|
+
const previousProvider = mainForm.getSnapshot().value?.provider;
|
|
4429
5004
|
if (!await mainForm.mutate([
|
|
4430
5005
|
{
|
|
4431
5006
|
op: "set",
|
|
@@ -4451,6 +5026,11 @@ window.__ModuleLoader__.load({
|
|
|
4451
5026
|
}
|
|
4452
5027
|
const revision = mainForm.getSnapshot().revision;
|
|
4453
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
|
+
}
|
|
4454
5034
|
return revision;
|
|
4455
5035
|
};
|
|
4456
5036
|
const connectionRpc = (() => {
|
|
@@ -4490,21 +5070,28 @@ window.__ModuleLoader__.load({
|
|
|
4490
5070
|
searchSettings: search,
|
|
4491
5071
|
imageSettings: image,
|
|
4492
5072
|
autoReviewSettings: autoReview,
|
|
5073
|
+
compactionSettings: compaction,
|
|
4493
5074
|
switchSettings: owned
|
|
4494
5075
|
},
|
|
4495
5076
|
capabilities: RUNTIME_CAPABILITIES,
|
|
4496
5077
|
saveMain,
|
|
4497
5078
|
setSubagent: (field, value) => value === void 0 ? subagent.unset(field) : subagent.set(field, value),
|
|
4498
5079
|
setCapability: (route, field, value) => {
|
|
4499
|
-
const scope = route === "search" ? search : route === "image" ? image : autoReview;
|
|
5080
|
+
const scope = route === "search" ? search : route === "image" ? image : route === "compaction" ? compaction : autoReview;
|
|
4500
5081
|
return value === void 0 ? scope.unset(field) : scope.set(field, value);
|
|
4501
5082
|
},
|
|
4502
5083
|
setCompactOnSwitch: async (value) => {
|
|
4503
5084
|
if (!await owned.set("compactOnSwitch", value)) throw new Error("settings-rejected");
|
|
4504
5085
|
},
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
5086
|
+
saveProviderGroup: async (provider, group) => {
|
|
5087
|
+
const current = owned.getSnapshot().value?.providerGroups;
|
|
5088
|
+
const next = withProviderGroup(current, provider, group);
|
|
5089
|
+
if (next.length === 0) {
|
|
5090
|
+
if (current === void 0) return;
|
|
5091
|
+
if (!await owned.unset("providerGroups")) throw new Error("settings-rejected");
|
|
5092
|
+
return;
|
|
5093
|
+
}
|
|
5094
|
+
if (!await owned.set("providerGroups", next)) throw new Error("settings-rejected");
|
|
4508
5095
|
},
|
|
4509
5096
|
providerRoleOf,
|
|
4510
5097
|
loadCatalog: async () => {
|