dsh-model-switch 0.4.18 → 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 +19 -0
- package/PRODUCT.md +4 -4
- package/README.md +10 -0
- package/README.zh.md +10 -0
- package/cordis.patch.yml +12 -0
- package/lib/auto-review.js +120 -0
- package/lib/client.js +664 -84
- 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 +9 -0
- package/lib/types/client/picker/locales.d.ts +18 -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/picker/plan-goal.d.ts +77 -0
- package/lib/types/plan-compact.d.ts +82 -0
- package/package.json +14 -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 = [
|
|
@@ -3063,6 +3397,63 @@ window.__ModuleLoader__.load({
|
|
|
3063
3397
|
throw new PlanDismissalUnsupportedError("this DSH Host exposes neither PendingQuestion.dismiss() (0.2.x) nor PendingQuestion.cancel() (0.1.x)");
|
|
3064
3398
|
}
|
|
3065
3399
|
//#endregion
|
|
3400
|
+
//#region src/picker/plan-goal.ts
|
|
3401
|
+
function failure(result, fallback) {
|
|
3402
|
+
if (result.ok) return { message: fallback };
|
|
3403
|
+
const { code, message } = result.error;
|
|
3404
|
+
return {
|
|
3405
|
+
code,
|
|
3406
|
+
message: message.trim() === "" ? fallback : message
|
|
3407
|
+
};
|
|
3408
|
+
}
|
|
3409
|
+
/**
|
|
3410
|
+
* Make the plan text this session's armed goal, replacing any unfinished goal.
|
|
3411
|
+
*
|
|
3412
|
+
* Ordering mirrors the card's own commit-then-answer rule: a refusal here keeps
|
|
3413
|
+
* the Plan pending instead of approving work that no goal tracks. The replace
|
|
3414
|
+
* path clears rather than edits so a new plan receives a fresh round budget —
|
|
3415
|
+
* `edit` would inherit the rounds the previous goal already spent.
|
|
3416
|
+
*
|
|
3417
|
+
* @param remote - the released goal remote, absent when no goal package is mounted.
|
|
3418
|
+
* @param sessionId - the session that owns both the review and the goal.
|
|
3419
|
+
* @param plan - the plan markdown under review.
|
|
3420
|
+
* @returns Whether the goal now tracks this plan, or why it does not.
|
|
3421
|
+
*/
|
|
3422
|
+
async function setPlanAsGoal(remote, sessionId, plan) {
|
|
3423
|
+
if (remote === void 0) return { kind: "unsupported" };
|
|
3424
|
+
const objective = plan.trim();
|
|
3425
|
+
if (objective === "") return {
|
|
3426
|
+
kind: "failed",
|
|
3427
|
+
code: "GOAL_INVALID_OBJECTIVE",
|
|
3428
|
+
message: "goal objective must be a non-empty string"
|
|
3429
|
+
};
|
|
3430
|
+
const created = await remote.create(sessionId, { objective });
|
|
3431
|
+
if (created.ok) return { kind: "created" };
|
|
3432
|
+
if (created.error.code !== "GOAL_ALREADY_EXISTS") return {
|
|
3433
|
+
kind: "failed",
|
|
3434
|
+
...failure(created, "the goal could not be created")
|
|
3435
|
+
};
|
|
3436
|
+
const current = await remote.get(sessionId);
|
|
3437
|
+
if (!current.ok || current.value === void 0 || current.value === null) return {
|
|
3438
|
+
kind: "failed",
|
|
3439
|
+
...failure(current, "the current goal could not be read for replacement")
|
|
3440
|
+
};
|
|
3441
|
+
const cleared = await remote.clear(sessionId, {
|
|
3442
|
+
id: current.value.id,
|
|
3443
|
+
revision: current.value.revision
|
|
3444
|
+
});
|
|
3445
|
+
if (!cleared.ok) return {
|
|
3446
|
+
kind: "failed",
|
|
3447
|
+
...failure(cleared, "the current goal could not be cleared")
|
|
3448
|
+
};
|
|
3449
|
+
const retry = await remote.create(sessionId, { objective });
|
|
3450
|
+
if (retry.ok) return { kind: "replaced" };
|
|
3451
|
+
return {
|
|
3452
|
+
kind: "failed",
|
|
3453
|
+
...failure(retry, "the goal could not be created")
|
|
3454
|
+
};
|
|
3455
|
+
}
|
|
3456
|
+
//#endregion
|
|
3066
3457
|
//#region src/client/picker/RetryBoundary.tsx
|
|
3067
3458
|
/** Own retryable React error-boundary state while callers own presentation and copy. */
|
|
3068
3459
|
var RetryBoundary = class extends react.Component {
|
|
@@ -3082,7 +3473,7 @@ window.__ModuleLoader__.load({
|
|
|
3082
3473
|
};
|
|
3083
3474
|
//#endregion
|
|
3084
3475
|
//#region \0dsh-css:src/client/picker/PlanReviewCard.module.css.mjs
|
|
3085
|
-
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_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_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}}";
|
|
3086
3477
|
const tagId = "dsh-model-switch/PlanReviewCard.module.css";
|
|
3087
3478
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
3088
3479
|
const tag = document.createElement("style");
|
|
@@ -3104,13 +3495,18 @@ window.__ModuleLoader__.load({
|
|
|
3104
3495
|
"feedbackRow": "BylfoW_feedbackRow",
|
|
3105
3496
|
"footer": "BylfoW_footer",
|
|
3106
3497
|
"frame": "BylfoW_frame",
|
|
3498
|
+
"goalRun": "BylfoW_goalRun",
|
|
3107
3499
|
"headerPicker": "BylfoW_headerPicker",
|
|
3108
3500
|
"keep": "BylfoW_keep",
|
|
3109
3501
|
"picker": "BylfoW_picker",
|
|
3110
3502
|
"pickerError": "BylfoW_pickerError",
|
|
3503
|
+
"previewLink": "BylfoW_previewLink",
|
|
3111
3504
|
"recover": "BylfoW_recover",
|
|
3112
3505
|
"strip": "BylfoW_strip",
|
|
3113
|
-
"stripTitle": "BylfoW_stripTitle"
|
|
3506
|
+
"stripTitle": "BylfoW_stripTitle",
|
|
3507
|
+
"summary": "BylfoW_summary",
|
|
3508
|
+
"summaryText": "BylfoW_summaryText",
|
|
3509
|
+
"summaryTitle": "BylfoW_summaryTitle"
|
|
3114
3510
|
};
|
|
3115
3511
|
//#endregion
|
|
3116
3512
|
//#region src/client/picker/PlanReviewCard.tsx
|
|
@@ -3226,11 +3622,14 @@ window.__ModuleLoader__.load({
|
|
|
3226
3622
|
...settingsUnavailableReason === void 0 ? {} : { settingsUnavailableReason },
|
|
3227
3623
|
directory: pickerDirectoryViewOrdered(snapshot, props, order, props.catalogRoutes?.() ?? {}),
|
|
3228
3624
|
t: props.t,
|
|
3625
|
+
goalRemote: props.goalRemote,
|
|
3626
|
+
planPreview: props.planPreview,
|
|
3627
|
+
...props.planCompact === void 0 ? {} : { planCompact: props.planCompact },
|
|
3229
3628
|
...props.resolveInteractionOperations === void 0 ? {} : { resolveInteractionOperations: props.resolveInteractionOperations },
|
|
3230
3629
|
...props.roleOf === void 0 ? {} : { roleOf: props.roleOf }
|
|
3231
3630
|
}, props.matched.key);
|
|
3232
3631
|
}
|
|
3233
|
-
function PlanReviewState({ matched, review, available, providerLock, agentLocked, lockFailed, settingsUnavailableReason, directory, t, resolveInteractionOperations, roleOf }) {
|
|
3632
|
+
function PlanReviewState({ matched, review, available, providerLock, agentLocked, lockFailed, settingsUnavailableReason, directory, t, goalRemote, planPreview, planCompact, resolveInteractionOperations, roleOf }) {
|
|
3234
3633
|
const { snapshot, getDirectorySnapshot, load, select } = directory;
|
|
3235
3634
|
const [execution, setExecution] = (0, react.useState)(snapshot.current ?? void 0);
|
|
3236
3635
|
const [feedback, setFeedback] = (0, react.useState)("");
|
|
@@ -3246,6 +3645,26 @@ window.__ModuleLoader__.load({
|
|
|
3246
3645
|
if (execution === void 0 && snapshot.current !== null) setExecution(snapshot.current);
|
|
3247
3646
|
}, [execution, snapshot.current]);
|
|
3248
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
|
+
/**
|
|
3249
3668
|
* Run one takeover action. `force` reaches past a terminal answer state: the
|
|
3250
3669
|
* withdrawal action must stay reachable, or a rejected answer would leave the
|
|
3251
3670
|
* card with no way back to the composer.
|
|
@@ -3280,22 +3699,75 @@ window.__ModuleLoader__.load({
|
|
|
3280
3699
|
blocked,
|
|
3281
3700
|
error
|
|
3282
3701
|
}, available, executionAllowed);
|
|
3702
|
+
/** Approve this review after committing its execution model; a refused commit keeps the Plan pending. */
|
|
3703
|
+
const approveWith = async (model) => {
|
|
3704
|
+
if (!await approvePlanReview({
|
|
3705
|
+
select,
|
|
3706
|
+
selection: model,
|
|
3707
|
+
current: snapshot.current,
|
|
3708
|
+
answer: () => respondAnswer(matched, review.id, review.approve.label, t("plan.responseRejected"), true),
|
|
3709
|
+
onNotRestored: (switched) => {
|
|
3710
|
+
setNotice(t("plan.notRestored", { model: switched }));
|
|
3711
|
+
console.warn("dsh-model-switch: the Session model changed but the Main default was not restored", switched);
|
|
3712
|
+
}
|
|
3713
|
+
})) {
|
|
3714
|
+
const message = getDirectorySnapshot().error;
|
|
3715
|
+
throw new Error(message === null ? t("plan.modelFailed") : t("error.action", { message }));
|
|
3716
|
+
}
|
|
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
|
+
};
|
|
3283
3744
|
const onApprove = () => {
|
|
3284
3745
|
if (execution === void 0 || !executionAllowed || !available || busy || blocked) return;
|
|
3285
3746
|
settle(async () => {
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3747
|
+
await compactBeforeRun();
|
|
3748
|
+
await approveWith(execution);
|
|
3749
|
+
});
|
|
3750
|
+
};
|
|
3751
|
+
const goal = goalRemote();
|
|
3752
|
+
const goalReason = goal === void 0 ? t("plan.goalUnsupported") : void 0;
|
|
3753
|
+
/**
|
|
3754
|
+
* Arm this plan as the session's goal first, then approve. The goal must be
|
|
3755
|
+
* set before the answer because a refusal must leave the Plan pending, and
|
|
3756
|
+
* because only an armed goal lets the round driver carry the plan after plan
|
|
3757
|
+
* mode exits.
|
|
3758
|
+
*/
|
|
3759
|
+
const onRunAsGoal = () => {
|
|
3760
|
+
if (execution === void 0 || !executionAllowed || !available || busy || blocked || goal === void 0) return;
|
|
3761
|
+
settle(async () => {
|
|
3762
|
+
await compactBeforeRun();
|
|
3763
|
+
const handoff = await setPlanAsGoal(goal, matched.sessionId, review.plan);
|
|
3764
|
+
if (handoff.kind === "unsupported") throw new Error(t("plan.goalUnsupported"));
|
|
3765
|
+
if (handoff.kind === "failed") {
|
|
3766
|
+
const message = handoff.code === void 0 ? handoff.message : `${handoff.code}: ${handoff.message}`;
|
|
3767
|
+
throw new Error(t("plan.goalFailed", { message }));
|
|
3298
3768
|
}
|
|
3769
|
+
if (handoff.kind === "replaced") setNotice(t("plan.goalReplaced"));
|
|
3770
|
+
await approveWith(execution);
|
|
3299
3771
|
});
|
|
3300
3772
|
};
|
|
3301
3773
|
const decline = review.decline;
|
|
@@ -3315,6 +3787,15 @@ window.__ModuleLoader__.load({
|
|
|
3315
3787
|
className: PlanReviewCard_module_css_default.stripTitle,
|
|
3316
3788
|
children: t("plan.header")
|
|
3317
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
|
+
}),
|
|
3318
3799
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3319
3800
|
className: PlanReviewCard_module_css_default.headerPicker,
|
|
3320
3801
|
"aria-label": t("plan.execution"),
|
|
@@ -3346,15 +3827,16 @@ window.__ModuleLoader__.load({
|
|
|
3346
3827
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3347
3828
|
className: PlanReviewCard_module_css_default.body,
|
|
3348
3829
|
"data-plan-review-scroll": true,
|
|
3349
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
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
|
+
})]
|
|
3358
3840
|
})
|
|
3359
3841
|
}),
|
|
3360
3842
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -3413,6 +3895,14 @@ window.__ModuleLoader__.load({
|
|
|
3413
3895
|
},
|
|
3414
3896
|
children: t("plan.keep")
|
|
3415
3897
|
}),
|
|
3898
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3899
|
+
variant: "outline",
|
|
3900
|
+
className: PlanReviewCard_module_css_default.goalRun,
|
|
3901
|
+
disabled: action.approveDisabled || goalReason !== void 0,
|
|
3902
|
+
...goalReason === void 0 ? {} : { title: goalReason },
|
|
3903
|
+
onClick: onRunAsGoal,
|
|
3904
|
+
children: t("plan.goalRun")
|
|
3905
|
+
}),
|
|
3416
3906
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
|
|
3417
3907
|
variant: "primary",
|
|
3418
3908
|
className: PlanReviewCard_module_css_default.approve,
|
|
@@ -3491,6 +3981,7 @@ window.__ModuleLoader__.load({
|
|
|
3491
3981
|
"choice.unavailable": "目录没有这个组合",
|
|
3492
3982
|
"plan.kicker": "Plan",
|
|
3493
3983
|
"plan.header": "计划待审",
|
|
3984
|
+
"plan.openFull": "查看完整计划",
|
|
3494
3985
|
"plan.execution": "执行模型",
|
|
3495
3986
|
"plan.approve": "确认执行",
|
|
3496
3987
|
"plan.keep": "拒绝",
|
|
@@ -3501,8 +3992,16 @@ window.__ModuleLoader__.load({
|
|
|
3501
3992
|
"plan.feedbackPlaceholder": "说明要改什么(可留空)",
|
|
3502
3993
|
"plan.notRestored": "执行模型已切换,但主模型默认值未能还原({model});请在「设置 → 模型」中确认默认模型。",
|
|
3503
3994
|
"plan.dismissUnsupported": "当前 DSH 版本既未提供 PendingQuestion.dismiss() 也未提供 cancel(),无法回到聊天;请直接在输入框里发送你的意见。",
|
|
3995
|
+
"plan.goalRun": "设置为目标并执行",
|
|
3996
|
+
"plan.goalFailed": "设置目标失败:{message}",
|
|
3997
|
+
"plan.goalReplaced": "已替换本会话原有的目标",
|
|
3998
|
+
"plan.goalUnsupported": "当前部署未挂载 goal 服务,无法把计划设为目标",
|
|
3504
3999
|
"plan.responseRejected": "计划答复已被另一客户端处理;已提交的模型切换无法由插件回滚。",
|
|
3505
4000
|
"plan.cancelRejected": "计划审查已被另一客户端处理,无法返回讨论。",
|
|
4001
|
+
"plan.compacting": "正在压缩上下文,将使用 {model}…",
|
|
4002
|
+
"plan.compacted": "上下文已压缩,开始执行。",
|
|
4003
|
+
"plan.compactEmpty": "没有可压缩的历史,继续执行。",
|
|
4004
|
+
"plan.compactFailed": "压缩上下文失败:{message}(计划仍待批准,可重试)",
|
|
3506
4005
|
"markdown.copy": "复制代码",
|
|
3507
4006
|
"markdown.copied": "已复制",
|
|
3508
4007
|
"markdown.footnotes": "脚注"
|
|
@@ -3542,6 +4041,7 @@ window.__ModuleLoader__.load({
|
|
|
3542
4041
|
"choice.unavailable": "This combination is not in the catalog",
|
|
3543
4042
|
"plan.kicker": "Plan",
|
|
3544
4043
|
"plan.header": "Plan review",
|
|
4044
|
+
"plan.openFull": "View full plan",
|
|
3545
4045
|
"plan.execution": "Execution model",
|
|
3546
4046
|
"plan.approve": "Approve",
|
|
3547
4047
|
"plan.keep": "Keep planning",
|
|
@@ -3552,8 +4052,16 @@ window.__ModuleLoader__.load({
|
|
|
3552
4052
|
"plan.feedbackPlaceholder": "Say what to change (optional)",
|
|
3553
4053
|
"plan.notRestored": "The execution model switched, but the Main default could not be restored ({model}); confirm the default in Settings → Models.",
|
|
3554
4054
|
"plan.dismissUnsupported": "This DSH Host exposes neither PendingQuestion.dismiss() nor cancel(), so the review cannot return to chat; send your feedback as a message instead.",
|
|
4055
|
+
"plan.goalRun": "Set as goal and run",
|
|
4056
|
+
"plan.goalFailed": "Could not set the goal: {message}",
|
|
4057
|
+
"plan.goalReplaced": "Replaced this session's previous goal",
|
|
4058
|
+
"plan.goalUnsupported": "This deployment mounts no goal service, so the Plan cannot be set as a goal",
|
|
3555
4059
|
"plan.responseRejected": "Another client already handled this Plan response. A committed model change cannot be rolled back by this plugin.",
|
|
3556
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)",
|
|
3557
4065
|
"markdown.copy": "Copy code",
|
|
3558
4066
|
"markdown.copied": "Copied",
|
|
3559
4067
|
"markdown.footnotes": "Footnotes"
|
|
@@ -3576,6 +4084,68 @@ window.__ModuleLoader__.load({
|
|
|
3576
4084
|
}
|
|
3577
4085
|
const EMPTY_ORDER = [];
|
|
3578
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
|
+
/**
|
|
4126
|
+
* Resolve the released goal remote without hard-depending on it.
|
|
4127
|
+
*
|
|
4128
|
+
* `remote.goals` exists only when the deployment mounts `@deepseek-ai/dsh-goal`,
|
|
4129
|
+
* so the capability must be looked up per render rather than injected: an
|
|
4130
|
+
* `inject` on a dotted service name would otherwise block this whole client
|
|
4131
|
+
* plugin on a deployment that does not mount the goal package. The dotted name
|
|
4132
|
+
* is read first, with the namespace object as the fallback shape.
|
|
4133
|
+
* @param ctx - client root context holding the remote namespace.
|
|
4134
|
+
* @returns The goal remote, or undefined when no goal service is mounted.
|
|
4135
|
+
*/
|
|
4136
|
+
function goalRemoteFrom(ctx) {
|
|
4137
|
+
const read = (name) => {
|
|
4138
|
+
try {
|
|
4139
|
+
return ctx.get(name, false);
|
|
4140
|
+
} catch {
|
|
4141
|
+
return;
|
|
4142
|
+
}
|
|
4143
|
+
};
|
|
4144
|
+
const nested = read("remote.goals");
|
|
4145
|
+
if (nested !== void 0 && nested !== null) return nested;
|
|
4146
|
+
return read("remote")?.goals;
|
|
4147
|
+
}
|
|
4148
|
+
/**
|
|
3579
4149
|
* Bind the optional Providers entry order as a React external store.
|
|
3580
4150
|
* @returns A subscribable order snapshot with an invalidation hook for directory changes.
|
|
3581
4151
|
*/
|
|
@@ -3684,6 +4254,7 @@ window.__ModuleLoader__.load({
|
|
|
3684
4254
|
});
|
|
3685
4255
|
});
|
|
3686
4256
|
const resolveInteractionOperations = () => interactionOperationsFrom(scope);
|
|
4257
|
+
const planPreview = createPlanPreviewBinding(scope.slots);
|
|
3687
4258
|
const rpcOf = () => {
|
|
3688
4259
|
return scope.get("connection", false)?.rpc;
|
|
3689
4260
|
};
|
|
@@ -3709,6 +4280,9 @@ window.__ModuleLoader__.load({
|
|
|
3709
4280
|
},
|
|
3710
4281
|
subscribeMainDefaults: (listener) => mainDefaults.subscribe(listener),
|
|
3711
4282
|
getMainDefaultsSnapshot: () => mainDefaults.getSnapshot(),
|
|
4283
|
+
goalRemote: () => goalRemoteFrom(scope),
|
|
4284
|
+
planPreview,
|
|
4285
|
+
planCompact: planCompactFrom(rpcOf),
|
|
3712
4286
|
hooks: {
|
|
3713
4287
|
directory: directory.store,
|
|
3714
4288
|
providerOrder: orderStore
|
|
@@ -3850,6 +4424,7 @@ window.__ModuleLoader__.load({
|
|
|
3850
4424
|
const subagent = deriveConfigForm(owned, deriveSubagentSettings, SUBAGENT_SETTINGS_FIELDS);
|
|
3851
4425
|
const search = deriveConfigForm(owned, deriveSearchSettings, SEARCH_SETTINGS_FIELDS);
|
|
3852
4426
|
const image = deriveConfigForm(owned, deriveImageSettings, IMAGE_SETTINGS_FIELDS);
|
|
4427
|
+
const autoReview = deriveConfigForm(owned, deriveAutoReviewSettings, AUTO_REVIEW_SETTINGS_FIELDS);
|
|
3853
4428
|
const saveMain = async (next, expectedRevision) => {
|
|
3854
4429
|
if (!await mainForm.mutate([
|
|
3855
4430
|
{
|
|
@@ -3914,18 +4489,23 @@ window.__ModuleLoader__.load({
|
|
|
3914
4489
|
subagentSettings: subagent,
|
|
3915
4490
|
searchSettings: search,
|
|
3916
4491
|
imageSettings: image,
|
|
4492
|
+
autoReviewSettings: autoReview,
|
|
3917
4493
|
switchSettings: owned
|
|
3918
4494
|
},
|
|
3919
4495
|
capabilities: RUNTIME_CAPABILITIES,
|
|
3920
4496
|
saveMain,
|
|
3921
4497
|
setSubagent: (field, value) => value === void 0 ? subagent.unset(field) : subagent.set(field, value),
|
|
3922
4498
|
setCapability: (route, field, value) => {
|
|
3923
|
-
const scope = route === "search" ? search : image;
|
|
4499
|
+
const scope = route === "search" ? search : route === "image" ? image : autoReview;
|
|
3924
4500
|
return value === void 0 ? scope.unset(field) : scope.set(field, value);
|
|
3925
4501
|
},
|
|
3926
4502
|
setCompactOnSwitch: async (value) => {
|
|
3927
4503
|
if (!await owned.set("compactOnSwitch", value)) throw new Error("settings-rejected");
|
|
3928
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
|
+
},
|
|
3929
4509
|
providerRoleOf,
|
|
3930
4510
|
loadCatalog: async () => {
|
|
3931
4511
|
const response = await remote.session.modelCatalog();
|