dsh-model-switch 0.4.19 → 0.4.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/PRODUCT.md +4 -4
- package/README.md +8 -0
- package/README.zh.md +8 -0
- package/cordis.patch.yml +12 -0
- package/lib/auto-review.js +120 -0
- package/lib/client.js +531 -73
- package/lib/index.js +251 -6
- package/lib/types/auto-review.d.ts +68 -0
- package/lib/types/capabilities-rpc.d.ts +3 -2
- package/lib/types/client/ModelSwitchSettings.d.ts +3 -1
- package/lib/types/client/locales.d.ts +7 -0
- package/lib/types/client/picker/PlanReviewCard.d.ts +6 -0
- package/lib/types/client/picker/locales.d.ts +10 -0
- package/lib/types/client/picker/plan-preview.d.ts +49 -0
- package/lib/types/client-contract.d.ts +12 -0
- package/lib/types/host-runtime.d.ts +6 -0
- package/lib/types/host-settings.d.ts +15 -0
- package/lib/types/picker/plan-compact.d.ts +48 -0
- package/lib/types/plan-compact.d.ts +82 -0
- package/package.json +10 -1
package/lib/client.js
CHANGED
|
@@ -33,6 +33,11 @@ window.__ModuleLoader__.load({
|
|
|
33
33
|
provider: "imageProvider",
|
|
34
34
|
model: "imageModel"
|
|
35
35
|
});
|
|
36
|
+
/** Auto-preset reviewer route; both unset means the reviewer follows the session model. */
|
|
37
|
+
const AUTO_REVIEW_SETTINGS_FIELDS = Object.freeze({
|
|
38
|
+
provider: "autoReviewProvider",
|
|
39
|
+
model: "autoReviewModel"
|
|
40
|
+
});
|
|
36
41
|
function deriveSubagentSettings(settings) {
|
|
37
42
|
return {
|
|
38
43
|
mode: settings.subagentMode,
|
|
@@ -53,6 +58,16 @@ window.__ModuleLoader__.load({
|
|
|
53
58
|
...settings.imageModel === void 0 ? {} : { model: settings.imageModel }
|
|
54
59
|
};
|
|
55
60
|
}
|
|
61
|
+
function deriveAutoReviewSettings(settings) {
|
|
62
|
+
return {
|
|
63
|
+
...settings.autoReviewProvider === void 0 ? {} : { provider: settings.autoReviewProvider },
|
|
64
|
+
...settings.autoReviewModel === void 0 ? {} : { model: settings.autoReviewModel }
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/** Whether both reviewer fields form a complete route; an incomplete pair still follows the session model. */
|
|
68
|
+
function autoReviewRouteConfigured(route) {
|
|
69
|
+
return route !== void 0 && (route.provider ?? "").trim() !== "" && (route.model ?? "").trim() !== "";
|
|
70
|
+
}
|
|
56
71
|
//#endregion
|
|
57
72
|
//#region src/client/derived-config-form.ts
|
|
58
73
|
function deriveConfigForm(source, project, fields) {
|
|
@@ -686,6 +701,23 @@ window.__ModuleLoader__.load({
|
|
|
686
701
|
})
|
|
687
702
|
]
|
|
688
703
|
});
|
|
704
|
+
if (kind === "autoReview") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
705
|
+
width: "18",
|
|
706
|
+
height: "18",
|
|
707
|
+
viewBox: "0 0 18 18",
|
|
708
|
+
fill: "none",
|
|
709
|
+
"aria-hidden": "true",
|
|
710
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
711
|
+
d: "M9 2.5 14.5 4.8v4.1c0 3.1-2.3 5.7-5.5 6.6-3.2-.9-5.5-3.5-5.5-6.6V4.8L9 2.5Z",
|
|
712
|
+
stroke: "currentColor",
|
|
713
|
+
strokeLinejoin: "round"
|
|
714
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
715
|
+
d: "m6.6 8.9 1.7 1.7 3.2-3.4",
|
|
716
|
+
stroke: "currentColor",
|
|
717
|
+
strokeLinecap: "round",
|
|
718
|
+
strokeLinejoin: "round"
|
|
719
|
+
})]
|
|
720
|
+
});
|
|
689
721
|
return null;
|
|
690
722
|
}
|
|
691
723
|
function RouteCard({ title, summary, hint, icon, open, onToggle, disabled = false, badge, badgeWarn = false, trailing, expandable = true, children }) {
|
|
@@ -857,15 +889,46 @@ window.__ModuleLoader__.load({
|
|
|
857
889
|
const { main, subagent, draft, groups } = controller;
|
|
858
890
|
const search = props.useSearchSettings((value) => value);
|
|
859
891
|
const image = props.useImageSettings((value) => value);
|
|
892
|
+
const autoReview = props.useAutoReviewSettings((value) => value);
|
|
860
893
|
const switchSettings = props.useSwitchSettings((value) => value);
|
|
861
894
|
const [open, setOpen] = (0, react.useState)();
|
|
862
895
|
const [subagentDraft, setSubagentDraft, resetSubagent] = useDraft(subagent);
|
|
863
896
|
const [searchDraft, setSearchDraft, resetSearch] = useDraft(search);
|
|
864
897
|
const [imageDraft, setImageDraft, resetImage] = useDraft(image);
|
|
898
|
+
const [autoReviewDraft, setAutoReviewDraft, resetAutoReview] = useDraft(autoReview);
|
|
865
899
|
const [busy, setBusy] = (0, react.useState)();
|
|
866
900
|
const [message, setMessage] = (0, react.useState)();
|
|
867
901
|
const [compactBusy, setCompactBusy] = (0, react.useState)(false);
|
|
868
902
|
const [compactError, setCompactError] = (0, react.useState)();
|
|
903
|
+
const planCompactValue = switchSettings.value?.planCompactThreshold ?? 2e5;
|
|
904
|
+
const [planCompactDraft, setPlanCompactDraft] = (0, react.useState)(() => String(planCompactValue));
|
|
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
|
+
};
|
|
869
932
|
const loadSearchCapabilities = props.loadCapabilities;
|
|
870
933
|
const [searchSnapshot, setSearchSnapshot] = (0, react.useState)(void 0);
|
|
871
934
|
const [searchError, setSearchError] = (0, react.useState)(void 0);
|
|
@@ -956,11 +1019,14 @@ window.__ModuleLoader__.load({
|
|
|
956
1019
|
const searchChoices = capabilityChoices(searchGroups, searchDraft, void 0, "search");
|
|
957
1020
|
const searchAvailable = searchLive?.searchProviderAdapters.available ?? false;
|
|
958
1021
|
const imageChoices = capabilityChoices(groups, imageDraft, props.capabilities.imageProviderAdapters.providers ?? [], "image");
|
|
1022
|
+
const autoReviewChoices = deriveRouteChoices(groups, autoReviewDraft);
|
|
1023
|
+
const autoReviewOn = autoReviewRouteConfigured(autoReview.value);
|
|
959
1024
|
const synced = [
|
|
960
1025
|
main,
|
|
961
1026
|
subagent,
|
|
962
1027
|
search,
|
|
963
|
-
image
|
|
1028
|
+
image,
|
|
1029
|
+
autoReview
|
|
964
1030
|
].every((snapshot) => snapshot.status === "ready");
|
|
965
1031
|
const run = async (route, operation) => {
|
|
966
1032
|
if (busy !== void 0) return;
|
|
@@ -1019,12 +1085,57 @@ window.__ModuleLoader__.load({
|
|
|
1019
1085
|
if (current.value?.model !== next.model) await props.setCapability(route, "model", next.model);
|
|
1020
1086
|
});
|
|
1021
1087
|
};
|
|
1088
|
+
const saveAutoReview = () => {
|
|
1089
|
+
if (autoReviewDraft === void 0) return;
|
|
1090
|
+
run("autoReview", async () => {
|
|
1091
|
+
if (autoReview.value?.provider !== autoReviewDraft.provider) await props.setCapability("autoReview", "provider", autoReviewDraft.provider);
|
|
1092
|
+
if (autoReview.value?.model !== autoReviewDraft.model) await props.setCapability("autoReview", "model", autoReviewDraft.model);
|
|
1093
|
+
});
|
|
1094
|
+
};
|
|
1095
|
+
/**
|
|
1096
|
+
* The reviewer switch: Off unsets both stored fields (follow the session
|
|
1097
|
+
* model); On seeds the route from Main so the pin takes effect immediately
|
|
1098
|
+
* and stays editable below. A failed write reports, never flips silently.
|
|
1099
|
+
*/
|
|
1100
|
+
const persistAutoReviewEnabled = (enabled) => {
|
|
1101
|
+
if (busy === "autoReview" || !autoReview.writable || autoReview.value === void 0) return;
|
|
1102
|
+
if (!enabled) {
|
|
1103
|
+
setOpen((current) => current === "autoReview" ? void 0 : current);
|
|
1104
|
+
run("autoReview", async () => {
|
|
1105
|
+
if (autoReview.value?.provider !== void 0) await props.setCapability("autoReview", "provider", void 0);
|
|
1106
|
+
if (autoReview.value?.model !== void 0) await props.setCapability("autoReview", "model", void 0);
|
|
1107
|
+
});
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
const seed = draft;
|
|
1111
|
+
if (seed === void 0 || seed.provider.trim() === "" || seed.model.trim() === "") return;
|
|
1112
|
+
setBusy("autoReview");
|
|
1113
|
+
setMessage(void 0);
|
|
1114
|
+
(async () => {
|
|
1115
|
+
if (autoReview.value?.provider !== seed.provider) await props.setCapability("autoReview", "provider", seed.provider);
|
|
1116
|
+
if (autoReview.value?.model !== seed.model) await props.setCapability("autoReview", "model", seed.model);
|
|
1117
|
+
})().then(() => {
|
|
1118
|
+
setAutoReviewDraft({
|
|
1119
|
+
provider: seed.provider,
|
|
1120
|
+
model: seed.model
|
|
1121
|
+
});
|
|
1122
|
+
setBusy(void 0);
|
|
1123
|
+
setOpen("autoReview");
|
|
1124
|
+
}, (error) => {
|
|
1125
|
+
setBusy(void 0);
|
|
1126
|
+
setMessage({
|
|
1127
|
+
route: "autoReview",
|
|
1128
|
+
text: error instanceof Error ? error.message : props.t("requestFailed")
|
|
1129
|
+
});
|
|
1130
|
+
});
|
|
1131
|
+
};
|
|
1022
1132
|
const mainSummary = draft === void 0 ? props.t("loading") : compact(routeName(groups, draft), mainEffectiveEffort);
|
|
1023
1133
|
const subagentRole = subagentDraft?.mode === "fixed" && subagentDraft.provider !== void 0 && subagentDraft.provider !== "" ? props.providerRoleOf?.(subagentDraft.provider) : void 0;
|
|
1024
1134
|
const subagentOn = subagentDraft?.mode === "fixed";
|
|
1025
1135
|
const subagentSummary = subagentDraft === void 0 ? props.t("loading") : subagentOn ? compact(routeName(groups, subagentRoute), isAgentRole(subagentRole) ? props.t("agentBadge") : void 0, defaultEffort === void 0 ? props.t("providerDefaultShort") : compact(props.t("providerDefaultShort"), defaultEffort)) : props.t("subagentOff");
|
|
1026
1136
|
const subagentDisabled = subagent.status !== "ready" || !subagent.writable || subagentDraft === void 0 || busy === "subagent" || !subagentOn || (subagentDraft.provider ?? "").trim() === "" || (subagentDraft.model ?? "").trim() === "";
|
|
1027
1137
|
const capabilityDisabled = (route, snapshot, next) => snapshot.status !== "ready" || !snapshot.writable || next === void 0 || busy === route || (next.provider ?? "").trim() === "" || (next.model ?? "").trim() === "";
|
|
1138
|
+
const autoReviewDisabled = autoReview.status !== "ready" || !autoReview.writable || autoReviewDraft === void 0 || busy === "autoReview" || !autoReviewOn || (autoReviewDraft.provider ?? "").trim() === "" || (autoReviewDraft.model ?? "").trim() === "";
|
|
1028
1139
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("main", {
|
|
1029
1140
|
className: ModelSwitchSettings_module_css_default.section,
|
|
1030
1141
|
children: [
|
|
@@ -1042,66 +1153,129 @@ window.__ModuleLoader__.load({
|
|
|
1042
1153
|
}),
|
|
1043
1154
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1044
1155
|
className: ModelSwitchSettings_module_css_default.group,
|
|
1045
|
-
children: [
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1156
|
+
children: [
|
|
1157
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
1158
|
+
className: ModelSwitchSettings_module_css_default.groupLabel,
|
|
1159
|
+
children: props.t("sendProtection")
|
|
1160
|
+
}),
|
|
1161
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1162
|
+
className: ModelSwitchSettings_module_css_default.rowCard,
|
|
1163
|
+
children: [
|
|
1164
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1165
|
+
className: ModelSwitchSettings_module_css_default.routeIcon,
|
|
1166
|
+
"aria-hidden": "true",
|
|
1167
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
1168
|
+
width: "18",
|
|
1169
|
+
height: "18",
|
|
1170
|
+
viewBox: "0 0 18 18",
|
|
1171
|
+
fill: "none",
|
|
1172
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1173
|
+
d: "M4 5.5h10M4 9h7M4 12.5h5",
|
|
1174
|
+
stroke: "currentColor",
|
|
1175
|
+
strokeLinecap: "round"
|
|
1176
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1177
|
+
d: "M13 11.5v3.5l2-1.6",
|
|
1178
|
+
stroke: "currentColor",
|
|
1179
|
+
strokeLinecap: "round",
|
|
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
|
+
}
|
|
1069
1215
|
})
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1216
|
+
]
|
|
1217
|
+
}),
|
|
1218
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1219
|
+
className: ModelSwitchSettings_module_css_default.rowCard,
|
|
1220
|
+
children: [
|
|
1221
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1222
|
+
className: ModelSwitchSettings_module_css_default.routeIcon,
|
|
1223
|
+
"aria-hidden": "true",
|
|
1224
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
1225
|
+
width: "18",
|
|
1226
|
+
height: "18",
|
|
1227
|
+
viewBox: "0 0 18 18",
|
|
1228
|
+
fill: "none",
|
|
1229
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1230
|
+
d: "M4 5.5h10M4 9h7M4 12.5h5",
|
|
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
|
+
})]
|
|
1085
1239
|
})
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1240
|
+
}),
|
|
1241
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1242
|
+
className: ModelSwitchSettings_module_css_default.rowCopy,
|
|
1243
|
+
children: [
|
|
1244
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1245
|
+
className: ModelSwitchSettings_module_css_default.rowTitle,
|
|
1246
|
+
children: props.t("planCompactThreshold")
|
|
1247
|
+
}),
|
|
1248
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1249
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
1250
|
+
children: props.t("planCompactThresholdHelp")
|
|
1251
|
+
}),
|
|
1252
|
+
planCompactError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1253
|
+
className: cx(ModelSwitchSettings_module_css_default.hint, ModelSwitchSettings_module_css_default.message),
|
|
1254
|
+
children: planCompactError
|
|
1255
|
+
})
|
|
1256
|
+
]
|
|
1257
|
+
}),
|
|
1258
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
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
|
+
]
|
|
1105
1279
|
}),
|
|
1106
1280
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1107
1281
|
className: ModelSwitchSettings_module_css_default.group,
|
|
@@ -1394,6 +1568,70 @@ window.__ModuleLoader__.load({
|
|
|
1394
1568
|
disabled: true,
|
|
1395
1569
|
badge: props.t("unavailable"),
|
|
1396
1570
|
badgeWarn: true
|
|
1571
|
+
}),
|
|
1572
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteCard, {
|
|
1573
|
+
title: props.t("autoReview"),
|
|
1574
|
+
summary: autoReviewDraft === void 0 ? props.t("loading") : autoReviewOn ? routeName(groups, autoReviewDraft) : props.t("autoReviewFollow"),
|
|
1575
|
+
hint: props.t("autoReviewHelp"),
|
|
1576
|
+
icon: "autoReview",
|
|
1577
|
+
open: open === "autoReview" && autoReviewOn,
|
|
1578
|
+
onToggle: () => {
|
|
1579
|
+
if (autoReviewOn) toggle("autoReview");
|
|
1580
|
+
},
|
|
1581
|
+
expandable: autoReviewOn,
|
|
1582
|
+
trailing: autoReviewDraft === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
1583
|
+
checked: autoReviewOn,
|
|
1584
|
+
disabled: busy === "autoReview" || !autoReview.writable,
|
|
1585
|
+
label: props.t("autoReview"),
|
|
1586
|
+
onChange: persistAutoReviewEnabled
|
|
1587
|
+
}),
|
|
1588
|
+
children: autoReviewDraft === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1589
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
1590
|
+
children: props.t("loading")
|
|
1591
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1592
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1593
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
1594
|
+
children: props.t("autoReviewRouteHelp")
|
|
1595
|
+
}),
|
|
1596
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1597
|
+
className: ModelSwitchSettings_module_css_default.formGrid,
|
|
1598
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1599
|
+
label: props.t("provider"),
|
|
1600
|
+
value: autoReviewDraft.provider ?? "",
|
|
1601
|
+
disabled: busy === "autoReview" || !autoReview.writable,
|
|
1602
|
+
choices: autoReviewChoices.providers,
|
|
1603
|
+
onChange: (provider) => {
|
|
1604
|
+
const first = groups.find((group) => group.id === provider)?.models[0];
|
|
1605
|
+
setAutoReviewDraft({
|
|
1606
|
+
provider,
|
|
1607
|
+
...first === void 0 ? {} : { model: first.id }
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1611
|
+
label: props.t("model"),
|
|
1612
|
+
value: autoReviewDraft.model ?? "",
|
|
1613
|
+
disabled: busy === "autoReview" || !autoReview.writable,
|
|
1614
|
+
choices: autoReviewChoices.models,
|
|
1615
|
+
onChange: (model) => {
|
|
1616
|
+
setAutoReviewDraft({
|
|
1617
|
+
...autoReviewDraft,
|
|
1618
|
+
model
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
})]
|
|
1622
|
+
}),
|
|
1623
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Actions, {
|
|
1624
|
+
t: props.t,
|
|
1625
|
+
busy: busy === "autoReview",
|
|
1626
|
+
disabled: autoReviewDisabled,
|
|
1627
|
+
...message?.route === "autoReview" ? { message: message.text } : {},
|
|
1628
|
+
onCancel: () => {
|
|
1629
|
+
resetAutoReview();
|
|
1630
|
+
setMessage(void 0);
|
|
1631
|
+
},
|
|
1632
|
+
onSave: saveAutoReview
|
|
1633
|
+
})
|
|
1634
|
+
] })
|
|
1397
1635
|
})
|
|
1398
1636
|
]
|
|
1399
1637
|
})
|
|
@@ -1438,6 +1676,13 @@ window.__ModuleLoader__.load({
|
|
|
1438
1676
|
requestFailed: "保存失败",
|
|
1439
1677
|
catalogFailed: "无法加载模型目录。",
|
|
1440
1678
|
conflict: "设置已在其他位置更改,请检查最新值后重试。",
|
|
1679
|
+
autoReview: "自动审查模型",
|
|
1680
|
+
autoReviewFollow: "跟随会话模型",
|
|
1681
|
+
autoReviewHelp: "为官方 auto-review(Auto 权限预设的逐调用审查)指定审查模型;需启用官方 auto-review 能力包。关闭时审查跟随当前会话模型。",
|
|
1682
|
+
autoReviewRouteHelp: "审查请求固定使用这里的 Provider/模型,与会话模型无关;保存后对下一次审查立即生效。",
|
|
1683
|
+
planCompactThreshold: "计划执行前压缩阈值",
|
|
1684
|
+
planCompactThresholdHelp: "切换模型执行计划时,若上下文估算超过该 Token 数,先用切换后的模型压缩一次历史再提交执行;未切换模型不触发。0 = 关闭。压缩会产生额外模型用量,摘要可能省略细节。",
|
|
1685
|
+
planCompactThresholdInvalid: "请输入不小于 0 的整数。",
|
|
1441
1686
|
"reason.central-subagent-routing": "Alpha.4 没有全局子代理启动路由接口。",
|
|
1442
1687
|
"reason.packaged-preset-roots": "Alpha.4 不支持插件提供额外 preset root。",
|
|
1443
1688
|
"reason.tool-owner-suppression": "Alpha.4 没有工具所有者或来源抑制接口。",
|
|
@@ -1481,6 +1726,13 @@ window.__ModuleLoader__.load({
|
|
|
1481
1726
|
requestFailed: "Save failed",
|
|
1482
1727
|
catalogFailed: "Could not load the model catalog.",
|
|
1483
1728
|
conflict: "Settings changed elsewhere. Review the latest values and retry.",
|
|
1729
|
+
autoReview: "Auto review model",
|
|
1730
|
+
autoReviewFollow: "Follows the session model",
|
|
1731
|
+
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
|
+
autoReviewRouteHelp: "Review requests use exactly this provider/model regardless of the session model; saved changes apply to the very next review.",
|
|
1733
|
+
planCompactThreshold: "Plan-exit compaction threshold",
|
|
1734
|
+
planCompactThresholdHelp: "When a Plan is approved with a switched model and the estimated context exceeds this many tokens, compact history once with the switched-to model before committing. Approvals without a model switch never trigger it. 0 disables. Compaction uses extra model quota and may omit details.",
|
|
1735
|
+
planCompactThresholdInvalid: "Enter an integer of 0 or more.",
|
|
1484
1736
|
"reason.central-subagent-routing": "Alpha.4 exposes no global Subagent start-routing seam.",
|
|
1485
1737
|
"reason.packaged-preset-roots": "Alpha.4 exposes no plugin-owned preset root.",
|
|
1486
1738
|
"reason.tool-owner-suppression": "Alpha.4 exposes no tool owner or provenance suppression seam.",
|
|
@@ -1505,6 +1757,7 @@ window.__ModuleLoader__.load({
|
|
|
1505
1757
|
id: question.id,
|
|
1506
1758
|
question: question.question,
|
|
1507
1759
|
plan: question.detail,
|
|
1760
|
+
...intent.callId === void 0 ? {} : { callId: intent.callId },
|
|
1508
1761
|
approve,
|
|
1509
1762
|
...decline === void 0 ? {} : { decline }
|
|
1510
1763
|
};
|
|
@@ -1573,6 +1826,14 @@ window.__ModuleLoader__.load({
|
|
|
1573
1826
|
return false;
|
|
1574
1827
|
}
|
|
1575
1828
|
}
|
|
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
|
+
}
|
|
1576
1837
|
const CONTEXT_SUFFIX = /-(\d+)(k|m)$/iu;
|
|
1577
1838
|
/** Peel Fast and `-<n>k` / `-<n>m` in either order. Product names like `-max` stay. */
|
|
1578
1839
|
function parsePickerId(id) {
|
|
@@ -2785,6 +3046,79 @@ window.__ModuleLoader__.load({
|
|
|
2785
3046
|
});
|
|
2786
3047
|
}
|
|
2787
3048
|
//#endregion
|
|
3049
|
+
//#region src/client/picker/plan-preview.ts
|
|
3050
|
+
/** Registration id ui-plan uses for its `conversation.plan-review.actions` entry. */
|
|
3051
|
+
const PLAN_REVIEW_OPEN_ID = "@deepseek-ai/dsh-client-ui-plan";
|
|
3052
|
+
/** Identities whose preview was already opened this process (official parity: ui-plan's store keeps them too). */
|
|
3053
|
+
const opened = /* @__PURE__ */ new Set();
|
|
3054
|
+
/**
|
|
3055
|
+
* Stable identity of one review's sidebar preview.
|
|
3056
|
+
* @param sessionId - session that owns the review.
|
|
3057
|
+
* @param review - narrowed plan review.
|
|
3058
|
+
* @param requestKey - pending-question request key.
|
|
3059
|
+
* @returns `session:call:<callId>` for a logged plan, else `session:review:<requestKey>`.
|
|
3060
|
+
*/
|
|
3061
|
+
function planPreviewIdentity(sessionId, review, requestKey) {
|
|
3062
|
+
const identity = review.callId === void 0 ? `review:${requestKey}` : `call:${review.callId}`;
|
|
3063
|
+
return `${String(sessionId)}:${identity}`;
|
|
3064
|
+
}
|
|
3065
|
+
function faceOf(slots, sessionId) {
|
|
3066
|
+
const entry = slots.entries("conversation.plan-review.actions").find((candidate) => candidate.options.id === PLAN_REVIEW_OPEN_ID && typeof candidate.inject === "function");
|
|
3067
|
+
if (entry === void 0 || entry.inject === void 0) return void 0;
|
|
3068
|
+
try {
|
|
3069
|
+
const face = entry.inject(sessionId);
|
|
3070
|
+
if (face === void 0 || typeof face.openReview !== "function") return void 0;
|
|
3071
|
+
if (face.hooks === void 0 || typeof face.hooks.sidebarMounted?.getSnapshot !== "function") return void 0;
|
|
3072
|
+
return face;
|
|
3073
|
+
} catch {
|
|
3074
|
+
return;
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
/**
|
|
3078
|
+
* Bind the preview navigation to one slot registry.
|
|
3079
|
+
* @param slots - the live registry ledger.
|
|
3080
|
+
* @returns an idempotent binding usable from the card's render and effects.
|
|
3081
|
+
*/
|
|
3082
|
+
function createPlanPreviewBinding(slots) {
|
|
3083
|
+
const open = (sessionId, review, requestKey) => {
|
|
3084
|
+
const face = faceOf(slots, sessionId);
|
|
3085
|
+
if (face === void 0) return false;
|
|
3086
|
+
opened.add(planPreviewIdentity(sessionId, review, requestKey));
|
|
3087
|
+
face.openReview(review, requestKey);
|
|
3088
|
+
return true;
|
|
3089
|
+
};
|
|
3090
|
+
return {
|
|
3091
|
+
available: () => slots.entries("conversation.plan-review.actions").some((candidate) => candidate.options.id === PLAN_REVIEW_OPEN_ID),
|
|
3092
|
+
open,
|
|
3093
|
+
autoOpen: (sessionId, review, requestKey) => {
|
|
3094
|
+
const identity = planPreviewIdentity(sessionId, review, requestKey);
|
|
3095
|
+
if (opened.has(identity)) return () => void 0;
|
|
3096
|
+
const face = faceOf(slots, sessionId);
|
|
3097
|
+
if (face === void 0) return () => void 0;
|
|
3098
|
+
const mounted = face.hooks.sidebarMounted;
|
|
3099
|
+
if (mounted.getSnapshot() !== void 0) {
|
|
3100
|
+
opened.add(identity);
|
|
3101
|
+
face.openReview(review, requestKey);
|
|
3102
|
+
return () => void 0;
|
|
3103
|
+
}
|
|
3104
|
+
let disposed = false;
|
|
3105
|
+
let openedHere = false;
|
|
3106
|
+
const unsubscribe = mounted.subscribe(() => {
|
|
3107
|
+
if (disposed || openedHere || opened.has(identity)) return;
|
|
3108
|
+
if (mounted.getSnapshot() === void 0) return;
|
|
3109
|
+
openedHere = true;
|
|
3110
|
+
opened.add(identity);
|
|
3111
|
+
unsubscribe();
|
|
3112
|
+
face.openReview(review, requestKey);
|
|
3113
|
+
});
|
|
3114
|
+
return () => {
|
|
3115
|
+
disposed = true;
|
|
3116
|
+
unsubscribe();
|
|
3117
|
+
};
|
|
3118
|
+
}
|
|
3119
|
+
};
|
|
3120
|
+
}
|
|
3121
|
+
//#endregion
|
|
2788
3122
|
//#region node_modules/.pnpm/dsh-llm-providers-ui@https+_f1dadb1b56406564fc1bb8f2d953b82d/node_modules/dsh-llm-providers-ui/lib/order.js
|
|
2789
3123
|
/** Display order for installed provider cards when the user has not saved one. */
|
|
2790
3124
|
const PROVIDER_ITEM_ORDER = [
|
|
@@ -3139,7 +3473,7 @@ window.__ModuleLoader__.load({
|
|
|
3139
3473
|
};
|
|
3140
3474
|
//#endregion
|
|
3141
3475
|
//#region \0dsh-css:src/client/picker/PlanReviewCard.module.css.mjs
|
|
3142
|
-
const css = ".BylfoW_frame{padding:6px calc(var(--dsh-composer-side-clearance) + 16px) 10px;justify-content:center;display:flex}.BylfoW_card{width:100%;max-width:var(--dsh-chat-content-width);border:1px solid var(--dsw-alias-state-warn-secondary);background:var(--dsw-specific-input-major);max-height:min(60vh,520px);box-shadow:var(--dsw-shadow-lv2);color:var(--dsw-alias-label-primary);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:20px;flex-direction:column;display:flex;overflow:visible}.BylfoW_card,.BylfoW_card *{box-sizing:border-box}.BylfoW_strip{background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-primary);border-radius:20px 20px 0 0;flex-shrink:0;align-items:center;gap:8px;padding:10px 16px;font-size:13px;line-height:18px;display:flex}.BylfoW_dot{background:var(--dsw-alias-state-warn-primary);border-radius:50%;width:8px;height:8px}.BylfoW_stripTitle{flex:none;font-weight:600}.BylfoW_headerPicker{flex:0 auto;align-items:center;min-width:0;max-width:55%;margin-left:auto;display:flex}.BylfoW_headerPicker>*{min-width:0;max-width:100%}.BylfoW_body{overscroll-behavior:contain;flex:auto;min-height:0;padding:12px 16px 4px;font-size:14px;line-height:22px;overflow-y:auto}.BylfoW_footer{z-index:2;flex-direction:column;flex-shrink:0;gap:6px;padding:8px 16px 12px;display:flex;position:relative;overflow:visible}.BylfoW_feedback{min-height:0;color:var(--dsw-alias-state-error-primary);font-size:11px;line-height:16px}.BylfoW_feedback:empty{display:none}.BylfoW_feedbackRow{align-items:center;gap:8px;min-width:0;display:flex}.BylfoW_feedbackInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-specific-input-minor);min-width:0;height:32px;color:var(--dsw-alias-label-primary);font:var(--dsw-font-xs-13);border-radius:8px;flex:auto;padding:0 10px}.BylfoW_feedbackInput::placeholder{color:var(--dsw-alias-label-tertiary)}.BylfoW_feedbackInput:disabled{opacity:.6}.BylfoW_bar{flex-wrap:nowrap;justify-content:space-between;align-items:center;gap:12px;min-width:0;min-height:32px;display:flex}.BylfoW_picker{flex:16rem;min-width:0;max-width:16rem}.BylfoW_pickerError{color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;flex-wrap:wrap;align-items:center;gap:6px;font-size:12px;display:flex}.BylfoW_actions{flex-wrap:nowrap;flex:none;justify-content:flex-end;align-items:center;gap:8px;margin-left:auto;display:flex}.BylfoW_discuss,.BylfoW_keep,.BylfoW_goalRun,.BylfoW_approve,.BylfoW_recover{white-space:nowrap;height:32px;min-height:32px;padding:0 10px}.BylfoW_discuss,.BylfoW_recover{color:var(--dsw-alias-label-secondary)}.BylfoW_discuss:hover:not(:disabled),.BylfoW_recover:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.BylfoW_approve{padding:0 12px}@media (width<=720px){.BylfoW_frame{padding-right:max(10px, env(safe-area-inset-right));padding-bottom:max(10px, env(safe-area-inset-bottom));padding-left:max(10px, env(safe-area-inset-left))}.BylfoW_card{border-radius:16px}.BylfoW_strip,.BylfoW_body,.BylfoW_footer{padding-left:12px;padding-right:12px}.BylfoW_strip{flex-wrap:wrap;gap:6px}.BylfoW_headerPicker{flex:100%;max-width:100%;margin-left:0}.BylfoW_bar{justify-content:flex-end;gap:6px}.BylfoW_actions{flex:100%;justify-content:flex-end;gap:6px;min-width:0}.BylfoW_discuss,.BylfoW_keep,.BylfoW_goalRun,.BylfoW_approve,.BylfoW_recover{overflow-wrap:anywhere;white-space:normal;flex:1 1 0;min-width:0;height:auto;padding:4px;line-height:14px}.BylfoW_feedbackInput{height:auto;min-height:32px}}";
|
|
3476
|
+
const css = ".BylfoW_frame{padding:6px calc(var(--dsh-composer-side-clearance) + 16px) 10px;justify-content:center;display:flex}.BylfoW_card{width:100%;max-width:var(--dsh-chat-content-width);border:1px solid var(--dsw-alias-state-warn-secondary);background:var(--dsw-specific-input-major);max-height:min(60vh,520px);box-shadow:var(--dsw-shadow-lv2);color:var(--dsw-alias-label-primary);--dsh-scrollbar-thumb:var(--dsw-alias-scrollbar-bg-l2);--dsh-scrollbar-thumb-hover:var(--dsw-alias-scrollbar-hover-l2);border-radius:20px;flex-direction:column;display:flex;overflow:visible}.BylfoW_card,.BylfoW_card *{box-sizing:border-box}.BylfoW_strip{background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-primary);border-radius:20px 20px 0 0;flex-shrink:0;align-items:center;gap:8px;padding:10px 16px;font-size:13px;line-height:18px;display:flex}.BylfoW_dot{background:var(--dsw-alias-state-warn-primary);border-radius:50%;width:8px;height:8px}.BylfoW_stripTitle{flex:none;font-weight:600}.BylfoW_previewLink{color:inherit;font:inherit;cursor:pointer;opacity:.85;background:0 0;border:0;flex:none;align-items:center;gap:4px;padding:0;display:inline-flex}.BylfoW_previewLink:hover{opacity:1;text-decoration:underline}.BylfoW_previewLink:focus-visible{outline:var(--dsw-focus-ring-width) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:3px}.BylfoW_headerPicker{flex:0 auto;align-items:center;min-width:0;max-width:55%;margin-left:auto;display:flex}.BylfoW_headerPicker>*{min-width:0;max-width:100%}.BylfoW_body{overscroll-behavior:contain;flex:auto;min-height:0;padding:12px 16px 4px;font-size:14px;line-height:22px;overflow-y:auto}.BylfoW_summary{min-width:0}.BylfoW_summaryTitle{text-overflow:ellipsis;white-space:nowrap;margin:0;font-size:15px;font-weight:500;line-height:22px;overflow:hidden}.BylfoW_summaryText{-webkit-line-clamp:2;color:var(--dsw-alias-label-secondary);-webkit-box-orient:vertical;margin:8px 0 0;font-size:14px;line-height:24px;display:-webkit-box;overflow:hidden}.BylfoW_footer{z-index:2;flex-direction:column;flex-shrink:0;gap:6px;padding:8px 16px 12px;display:flex;position:relative;overflow:visible}.BylfoW_feedback{min-height:0;color:var(--dsw-alias-state-error-primary);font-size:11px;line-height:16px}.BylfoW_feedback:empty{display:none}.BylfoW_feedbackRow{align-items:center;gap:8px;min-width:0;display:flex}.BylfoW_feedbackInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-specific-input-minor);min-width:0;height:32px;color:var(--dsw-alias-label-primary);font:var(--dsw-font-xs-13);border-radius:8px;flex:auto;padding:0 10px}.BylfoW_feedbackInput::placeholder{color:var(--dsw-alias-label-tertiary)}.BylfoW_feedbackInput:disabled{opacity:.6}.BylfoW_bar{flex-wrap:nowrap;justify-content:space-between;align-items:center;gap:12px;min-width:0;min-height:32px;display:flex}.BylfoW_picker{flex:16rem;min-width:0;max-width:16rem}.BylfoW_pickerError{color:var(--dsw-alias-state-error-primary);overflow-wrap:anywhere;flex-wrap:wrap;align-items:center;gap:6px;font-size:12px;display:flex}.BylfoW_actions{flex-wrap:nowrap;flex:none;justify-content:flex-end;align-items:center;gap:8px;margin-left:auto;display:flex}.BylfoW_discuss,.BylfoW_keep,.BylfoW_goalRun,.BylfoW_approve,.BylfoW_recover{white-space:nowrap;height:32px;min-height:32px;padding:0 10px}.BylfoW_discuss,.BylfoW_recover{color:var(--dsw-alias-label-secondary)}.BylfoW_discuss:hover:not(:disabled),.BylfoW_recover:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.BylfoW_approve{padding:0 12px}@media (width<=720px){.BylfoW_frame{padding-right:max(10px, env(safe-area-inset-right));padding-bottom:max(10px, env(safe-area-inset-bottom));padding-left:max(10px, env(safe-area-inset-left))}.BylfoW_card{border-radius:16px}.BylfoW_strip,.BylfoW_body,.BylfoW_footer{padding-left:12px;padding-right:12px}.BylfoW_strip{flex-wrap:wrap;gap:6px}.BylfoW_headerPicker{flex:100%;max-width:100%;margin-left:0}.BylfoW_bar{justify-content:flex-end;gap:6px}.BylfoW_actions{flex:100%;justify-content:flex-end;gap:6px;min-width:0}.BylfoW_discuss,.BylfoW_keep,.BylfoW_goalRun,.BylfoW_approve,.BylfoW_recover{overflow-wrap:anywhere;white-space:normal;flex:1 1 0;min-width:0;height:auto;padding:4px;line-height:14px}.BylfoW_feedbackInput{height:auto;min-height:32px}}";
|
|
3143
3477
|
const tagId = "dsh-model-switch/PlanReviewCard.module.css";
|
|
3144
3478
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
3145
3479
|
const tag = document.createElement("style");
|
|
@@ -3166,9 +3500,13 @@ window.__ModuleLoader__.load({
|
|
|
3166
3500
|
"keep": "BylfoW_keep",
|
|
3167
3501
|
"picker": "BylfoW_picker",
|
|
3168
3502
|
"pickerError": "BylfoW_pickerError",
|
|
3503
|
+
"previewLink": "BylfoW_previewLink",
|
|
3169
3504
|
"recover": "BylfoW_recover",
|
|
3170
3505
|
"strip": "BylfoW_strip",
|
|
3171
|
-
"stripTitle": "BylfoW_stripTitle"
|
|
3506
|
+
"stripTitle": "BylfoW_stripTitle",
|
|
3507
|
+
"summary": "BylfoW_summary",
|
|
3508
|
+
"summaryText": "BylfoW_summaryText",
|
|
3509
|
+
"summaryTitle": "BylfoW_summaryTitle"
|
|
3172
3510
|
};
|
|
3173
3511
|
//#endregion
|
|
3174
3512
|
//#region src/client/picker/PlanReviewCard.tsx
|
|
@@ -3285,11 +3623,13 @@ window.__ModuleLoader__.load({
|
|
|
3285
3623
|
directory: pickerDirectoryViewOrdered(snapshot, props, order, props.catalogRoutes?.() ?? {}),
|
|
3286
3624
|
t: props.t,
|
|
3287
3625
|
goalRemote: props.goalRemote,
|
|
3626
|
+
planPreview: props.planPreview,
|
|
3627
|
+
...props.planCompact === void 0 ? {} : { planCompact: props.planCompact },
|
|
3288
3628
|
...props.resolveInteractionOperations === void 0 ? {} : { resolveInteractionOperations: props.resolveInteractionOperations },
|
|
3289
3629
|
...props.roleOf === void 0 ? {} : { roleOf: props.roleOf }
|
|
3290
3630
|
}, props.matched.key);
|
|
3291
3631
|
}
|
|
3292
|
-
function PlanReviewState({ matched, review, available, providerLock, agentLocked, lockFailed, settingsUnavailableReason, directory, t, goalRemote, resolveInteractionOperations, roleOf }) {
|
|
3632
|
+
function PlanReviewState({ matched, review, available, providerLock, agentLocked, lockFailed, settingsUnavailableReason, directory, t, goalRemote, planPreview, planCompact, resolveInteractionOperations, roleOf }) {
|
|
3293
3633
|
const { snapshot, getDirectorySnapshot, load, select } = directory;
|
|
3294
3634
|
const [execution, setExecution] = (0, react.useState)(snapshot.current ?? void 0);
|
|
3295
3635
|
const [feedback, setFeedback] = (0, react.useState)("");
|
|
@@ -3305,6 +3645,26 @@ window.__ModuleLoader__.load({
|
|
|
3305
3645
|
if (execution === void 0 && snapshot.current !== null) setExecution(snapshot.current);
|
|
3306
3646
|
}, [execution, snapshot.current]);
|
|
3307
3647
|
/**
|
|
3648
|
+
* The takeover hides the official panel, so this card drives ui-plan's
|
|
3649
|
+
* registered opener: the full plan opens in the right sidebar as soon as a
|
|
3650
|
+
* Session is on screen (identity-deduped inside the binding).
|
|
3651
|
+
*/
|
|
3652
|
+
(0, react.useEffect)(() => planPreview.autoOpen(matched.sessionId, review, matched.key), [
|
|
3653
|
+
planPreview,
|
|
3654
|
+
matched.sessionId,
|
|
3655
|
+
matched.key,
|
|
3656
|
+
review
|
|
3657
|
+
]);
|
|
3658
|
+
/** Official two-part summary: first line as title, first paragraph as body. */
|
|
3659
|
+
const summary = (0, react.useMemo)(() => {
|
|
3660
|
+
const title = (0, _deepseek_ai_dsh_client_ui_primitives.extractMarkdownPlainText)(review.plan, { mode: "first-line" });
|
|
3661
|
+
const description = (0, _deepseek_ai_dsh_client_ui_primitives.extractMarkdownPlainText)(review.plan, { mode: "first-paragraph" });
|
|
3662
|
+
return {
|
|
3663
|
+
title,
|
|
3664
|
+
description: description === title ? "" : description
|
|
3665
|
+
};
|
|
3666
|
+
}, [review.plan]);
|
|
3667
|
+
/**
|
|
3308
3668
|
* Run one takeover action. `force` reaches past a terminal answer state: the
|
|
3309
3669
|
* withdrawal action must stay reachable, or a rejected answer would leave the
|
|
3310
3670
|
* card with no way back to the composer.
|
|
@@ -3355,9 +3715,38 @@ window.__ModuleLoader__.load({
|
|
|
3355
3715
|
throw new Error(message === null ? t("plan.modelFailed") : t("error.action", { message }));
|
|
3356
3716
|
}
|
|
3357
3717
|
};
|
|
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
|
+
};
|
|
3358
3744
|
const onApprove = () => {
|
|
3359
3745
|
if (execution === void 0 || !executionAllowed || !available || busy || blocked) return;
|
|
3360
|
-
settle(() =>
|
|
3746
|
+
settle(async () => {
|
|
3747
|
+
await compactBeforeRun();
|
|
3748
|
+
await approveWith(execution);
|
|
3749
|
+
});
|
|
3361
3750
|
};
|
|
3362
3751
|
const goal = goalRemote();
|
|
3363
3752
|
const goalReason = goal === void 0 ? t("plan.goalUnsupported") : void 0;
|
|
@@ -3370,6 +3759,7 @@ window.__ModuleLoader__.load({
|
|
|
3370
3759
|
const onRunAsGoal = () => {
|
|
3371
3760
|
if (execution === void 0 || !executionAllowed || !available || busy || blocked || goal === void 0) return;
|
|
3372
3761
|
settle(async () => {
|
|
3762
|
+
await compactBeforeRun();
|
|
3373
3763
|
const handoff = await setPlanAsGoal(goal, matched.sessionId, review.plan);
|
|
3374
3764
|
if (handoff.kind === "unsupported") throw new Error(t("plan.goalUnsupported"));
|
|
3375
3765
|
if (handoff.kind === "failed") {
|
|
@@ -3397,6 +3787,15 @@ window.__ModuleLoader__.load({
|
|
|
3397
3787
|
className: PlanReviewCard_module_css_default.stripTitle,
|
|
3398
3788
|
children: t("plan.header")
|
|
3399
3789
|
}),
|
|
3790
|
+
planPreview.available() && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3791
|
+
type: "button",
|
|
3792
|
+
className: PlanReviewCard_module_css_default.previewLink,
|
|
3793
|
+
"data-plan-preview-open": true,
|
|
3794
|
+
onClick: () => {
|
|
3795
|
+
planPreview.open(matched.sessionId, review, matched.key);
|
|
3796
|
+
},
|
|
3797
|
+
children: t("plan.openFull")
|
|
3798
|
+
}),
|
|
3400
3799
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3401
3800
|
className: PlanReviewCard_module_css_default.headerPicker,
|
|
3402
3801
|
"aria-label": t("plan.execution"),
|
|
@@ -3428,15 +3827,16 @@ window.__ModuleLoader__.load({
|
|
|
3428
3827
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3429
3828
|
className: PlanReviewCard_module_css_default.body,
|
|
3430
3829
|
"data-plan-review-scroll": true,
|
|
3431
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3830
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3831
|
+
className: PlanReviewCard_module_css_default.summary,
|
|
3832
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
3833
|
+
className: PlanReviewCard_module_css_default.summaryTitle,
|
|
3834
|
+
"data-plan-review-summary": true,
|
|
3835
|
+
children: summary.title
|
|
3836
|
+
}), summary.description !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
3837
|
+
className: PlanReviewCard_module_css_default.summaryText,
|
|
3838
|
+
children: summary.description
|
|
3839
|
+
})]
|
|
3440
3840
|
})
|
|
3441
3841
|
}),
|
|
3442
3842
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -3581,6 +3981,7 @@ window.__ModuleLoader__.load({
|
|
|
3581
3981
|
"choice.unavailable": "目录没有这个组合",
|
|
3582
3982
|
"plan.kicker": "Plan",
|
|
3583
3983
|
"plan.header": "计划待审",
|
|
3984
|
+
"plan.openFull": "查看完整计划",
|
|
3584
3985
|
"plan.execution": "执行模型",
|
|
3585
3986
|
"plan.approve": "确认执行",
|
|
3586
3987
|
"plan.keep": "拒绝",
|
|
@@ -3597,6 +3998,10 @@ window.__ModuleLoader__.load({
|
|
|
3597
3998
|
"plan.goalUnsupported": "当前部署未挂载 goal 服务,无法把计划设为目标",
|
|
3598
3999
|
"plan.responseRejected": "计划答复已被另一客户端处理;已提交的模型切换无法由插件回滚。",
|
|
3599
4000
|
"plan.cancelRejected": "计划审查已被另一客户端处理,无法返回讨论。",
|
|
4001
|
+
"plan.compacting": "正在压缩上下文,将使用 {model}…",
|
|
4002
|
+
"plan.compacted": "上下文已压缩,开始执行。",
|
|
4003
|
+
"plan.compactEmpty": "没有可压缩的历史,继续执行。",
|
|
4004
|
+
"plan.compactFailed": "压缩上下文失败:{message}(计划仍待批准,可重试)",
|
|
3600
4005
|
"markdown.copy": "复制代码",
|
|
3601
4006
|
"markdown.copied": "已复制",
|
|
3602
4007
|
"markdown.footnotes": "脚注"
|
|
@@ -3636,6 +4041,7 @@ window.__ModuleLoader__.load({
|
|
|
3636
4041
|
"choice.unavailable": "This combination is not in the catalog",
|
|
3637
4042
|
"plan.kicker": "Plan",
|
|
3638
4043
|
"plan.header": "Plan review",
|
|
4044
|
+
"plan.openFull": "View full plan",
|
|
3639
4045
|
"plan.execution": "Execution model",
|
|
3640
4046
|
"plan.approve": "Approve",
|
|
3641
4047
|
"plan.keep": "Keep planning",
|
|
@@ -3652,6 +4058,10 @@ window.__ModuleLoader__.load({
|
|
|
3652
4058
|
"plan.goalUnsupported": "This deployment mounts no goal service, so the Plan cannot be set as a goal",
|
|
3653
4059
|
"plan.responseRejected": "Another client already handled this Plan response. A committed model change cannot be rolled back by this plugin.",
|
|
3654
4060
|
"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)",
|
|
3655
4065
|
"markdown.copy": "Copy code",
|
|
3656
4066
|
"markdown.copied": "Copied",
|
|
3657
4067
|
"markdown.footnotes": "Footnotes"
|
|
@@ -3674,6 +4084,45 @@ window.__ModuleLoader__.load({
|
|
|
3674
4084
|
}
|
|
3675
4085
|
const EMPTY_ORDER = [];
|
|
3676
4086
|
/**
|
|
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
|
+
/**
|
|
3677
4126
|
* Resolve the released goal remote without hard-depending on it.
|
|
3678
4127
|
*
|
|
3679
4128
|
* `remote.goals` exists only when the deployment mounts `@deepseek-ai/dsh-goal`,
|
|
@@ -3805,6 +4254,7 @@ window.__ModuleLoader__.load({
|
|
|
3805
4254
|
});
|
|
3806
4255
|
});
|
|
3807
4256
|
const resolveInteractionOperations = () => interactionOperationsFrom(scope);
|
|
4257
|
+
const planPreview = createPlanPreviewBinding(scope.slots);
|
|
3808
4258
|
const rpcOf = () => {
|
|
3809
4259
|
return scope.get("connection", false)?.rpc;
|
|
3810
4260
|
};
|
|
@@ -3831,6 +4281,8 @@ window.__ModuleLoader__.load({
|
|
|
3831
4281
|
subscribeMainDefaults: (listener) => mainDefaults.subscribe(listener),
|
|
3832
4282
|
getMainDefaultsSnapshot: () => mainDefaults.getSnapshot(),
|
|
3833
4283
|
goalRemote: () => goalRemoteFrom(scope),
|
|
4284
|
+
planPreview,
|
|
4285
|
+
planCompact: planCompactFrom(rpcOf),
|
|
3834
4286
|
hooks: {
|
|
3835
4287
|
directory: directory.store,
|
|
3836
4288
|
providerOrder: orderStore
|
|
@@ -3972,6 +4424,7 @@ window.__ModuleLoader__.load({
|
|
|
3972
4424
|
const subagent = deriveConfigForm(owned, deriveSubagentSettings, SUBAGENT_SETTINGS_FIELDS);
|
|
3973
4425
|
const search = deriveConfigForm(owned, deriveSearchSettings, SEARCH_SETTINGS_FIELDS);
|
|
3974
4426
|
const image = deriveConfigForm(owned, deriveImageSettings, IMAGE_SETTINGS_FIELDS);
|
|
4427
|
+
const autoReview = deriveConfigForm(owned, deriveAutoReviewSettings, AUTO_REVIEW_SETTINGS_FIELDS);
|
|
3975
4428
|
const saveMain = async (next, expectedRevision) => {
|
|
3976
4429
|
if (!await mainForm.mutate([
|
|
3977
4430
|
{
|
|
@@ -4036,18 +4489,23 @@ window.__ModuleLoader__.load({
|
|
|
4036
4489
|
subagentSettings: subagent,
|
|
4037
4490
|
searchSettings: search,
|
|
4038
4491
|
imageSettings: image,
|
|
4492
|
+
autoReviewSettings: autoReview,
|
|
4039
4493
|
switchSettings: owned
|
|
4040
4494
|
},
|
|
4041
4495
|
capabilities: RUNTIME_CAPABILITIES,
|
|
4042
4496
|
saveMain,
|
|
4043
4497
|
setSubagent: (field, value) => value === void 0 ? subagent.unset(field) : subagent.set(field, value),
|
|
4044
4498
|
setCapability: (route, field, value) => {
|
|
4045
|
-
const scope = route === "search" ? search : image;
|
|
4499
|
+
const scope = route === "search" ? search : route === "image" ? image : autoReview;
|
|
4046
4500
|
return value === void 0 ? scope.unset(field) : scope.set(field, value);
|
|
4047
4501
|
},
|
|
4048
4502
|
setCompactOnSwitch: async (value) => {
|
|
4049
4503
|
if (!await owned.set("compactOnSwitch", value)) throw new Error("settings-rejected");
|
|
4050
4504
|
},
|
|
4505
|
+
setPlanCompactThreshold: async (value) => {
|
|
4506
|
+
if (!Number.isInteger(value) || value < 0) throw new Error("settings-rejected");
|
|
4507
|
+
if (!await owned.set("planCompactThreshold", value)) throw new Error("settings-rejected");
|
|
4508
|
+
},
|
|
4051
4509
|
providerRoleOf,
|
|
4052
4510
|
loadCatalog: async () => {
|
|
4053
4511
|
const response = await remote.session.modelCatalog();
|