dsh-model-switch 0.4.38 → 0.4.39
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 +25 -0
- package/lib/{auto-review-CvmQaRVf.js → auto-review-C61WZIYI.js} +365 -10
- package/lib/auto-review.js +1 -1
- package/lib/client.js +708 -4
- package/lib/index.js +86 -3
- package/lib/types/auto-review.d.ts +39 -6
- package/lib/types/capabilities-rpc.d.ts +10 -1
- package/lib/types/client/ModelSwitchSettings.d.ts +11 -1
- package/lib/types/client/locales.d.ts +18 -0
- package/lib/types/client/picker/PlanReviewCard.d.ts +8 -0
- package/lib/types/client-contract.d.ts +54 -0
- package/lib/types/host-settings.d.ts +17 -0
- package/lib/types/picker/discuss-plan.d.ts +43 -0
- package/lib/types/tool-allowlist.d.ts +113 -0
- package/lib/types/tool-catalog.d.ts +37 -0
- package/package.json +1 -1
- package/lib/auto-review-9SreBh-0.js +0 -855
- package/lib/auto-review-BLtwr83V.js +0 -738
- package/lib/auto-review-BoF2_YtP.js +0 -745
- package/lib/auto-review-CMHNfRuV.js +0 -1015
- package/lib/auto-review-D-0A5Amx.js +0 -859
- package/lib/auto-review-oan7iXY7.js +0 -987
package/lib/client.js
CHANGED
|
@@ -8,6 +8,317 @@ window.__ModuleLoader__.load({
|
|
|
8
8
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
9
|
let react_dom = require("react-dom");
|
|
10
10
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
11
|
+
/** Catalog lookup by id. */
|
|
12
|
+
const SOURCE_BY_ID = new Map([
|
|
13
|
+
{
|
|
14
|
+
id: "core",
|
|
15
|
+
kind: "core",
|
|
16
|
+
label: "DSH 核心",
|
|
17
|
+
repo: "https://github.com/deepseek-ai/deepseek-harness"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: "dsh-ops",
|
|
21
|
+
kind: "plugin",
|
|
22
|
+
label: "插件 · dsh-ops",
|
|
23
|
+
repo: "https://github.com/T-Auto/dsh-ops"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: "dsh-browser-cdp",
|
|
27
|
+
kind: "plugin",
|
|
28
|
+
label: "插件 · dsh-browser-cdp",
|
|
29
|
+
repo: "https://github.com/drscrewdriver/dsh-browser-cdp"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: "dsh-auto-memory",
|
|
33
|
+
kind: "plugin",
|
|
34
|
+
label: "插件 · dsh-auto-memory",
|
|
35
|
+
repo: "https://github.com/AskTheWay/dsh-auto-memory"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "dsh-model-switch",
|
|
39
|
+
kind: "plugin",
|
|
40
|
+
label: "插件 · dsh-model-switch",
|
|
41
|
+
repo: "https://github.com/vandoor7-droid/dsh-model-switch"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: "mcp:wincu",
|
|
45
|
+
kind: "mcp",
|
|
46
|
+
label: "MCP · wincu(dsh-computer-use-win)",
|
|
47
|
+
repo: "https://github.com/Yu-tao-Li/dsh-computer-use-win"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "qqbot-extension",
|
|
51
|
+
kind: "extension",
|
|
52
|
+
label: "QQ 机器人扩展(框架 @zaofan/dsh-qqbot)",
|
|
53
|
+
repo: "https://github.com/zaofan-make/dsh-qqbot"
|
|
54
|
+
}
|
|
55
|
+
].map((entry) => [entry.id, entry]));
|
|
56
|
+
/**
|
|
57
|
+
* Tool names the released Host owns, calibrated against dsh 0.2.1-rc.2.
|
|
58
|
+
*
|
|
59
|
+
* A name outside the catalog would otherwise be reported as core, which is the
|
|
60
|
+
* one guess a reader could act on wrongly; it resolves to `unknown` instead.
|
|
61
|
+
*/
|
|
62
|
+
const CORE_TOOLS = /* @__PURE__ */ new Set([
|
|
63
|
+
"ask_user_question",
|
|
64
|
+
"create_goal",
|
|
65
|
+
"create_skill",
|
|
66
|
+
"create_worktree",
|
|
67
|
+
"edit",
|
|
68
|
+
"exit_plan_mode",
|
|
69
|
+
"get_goal",
|
|
70
|
+
"glob",
|
|
71
|
+
"grep",
|
|
72
|
+
"interrupt_agent",
|
|
73
|
+
"job_kill",
|
|
74
|
+
"job_list",
|
|
75
|
+
"job_output",
|
|
76
|
+
"list_agents",
|
|
77
|
+
"list_mcp_resource_templates",
|
|
78
|
+
"list_mcp_resources",
|
|
79
|
+
"load_workspace_dependencies",
|
|
80
|
+
"present",
|
|
81
|
+
"pwsh",
|
|
82
|
+
"ralph",
|
|
83
|
+
"read",
|
|
84
|
+
"read_image",
|
|
85
|
+
"read_mcp_resource",
|
|
86
|
+
"schedule_create",
|
|
87
|
+
"schedule_delete",
|
|
88
|
+
"schedule_list",
|
|
89
|
+
"schedule_update",
|
|
90
|
+
"send_message",
|
|
91
|
+
"session_event_read",
|
|
92
|
+
"session_event_search",
|
|
93
|
+
"session_event_trace",
|
|
94
|
+
"session_search",
|
|
95
|
+
"session_trace",
|
|
96
|
+
"sidebar_open",
|
|
97
|
+
"skill",
|
|
98
|
+
"spawn_teammate",
|
|
99
|
+
"subagent",
|
|
100
|
+
"subagent_fork",
|
|
101
|
+
"team_task_create",
|
|
102
|
+
"team_task_get",
|
|
103
|
+
"team_task_list",
|
|
104
|
+
"team_task_update",
|
|
105
|
+
"terminal_close",
|
|
106
|
+
"terminal_list",
|
|
107
|
+
"terminal_open",
|
|
108
|
+
"terminal_read",
|
|
109
|
+
"terminal_send",
|
|
110
|
+
"terminal_signal",
|
|
111
|
+
"todo_write",
|
|
112
|
+
"update_goal",
|
|
113
|
+
"wait_agent",
|
|
114
|
+
"web_fetch",
|
|
115
|
+
"web_search",
|
|
116
|
+
"web_search_pro",
|
|
117
|
+
"workflow",
|
|
118
|
+
"working_directory",
|
|
119
|
+
"write"
|
|
120
|
+
]);
|
|
121
|
+
/** MCP server name → catalog id, for the servers this allowlist knows. */
|
|
122
|
+
const MCP_SERVERS = /* @__PURE__ */ new Map([["wincu", "mcp:wincu"]]);
|
|
123
|
+
/** Plugin identity by tool-name prefix (checked before the core catalog). */
|
|
124
|
+
const PLUGIN_PREFIXES = [
|
|
125
|
+
["bcdp_", "dsh-browser-cdp"],
|
|
126
|
+
["browser_window_", "dsh-browser-cdp"],
|
|
127
|
+
["ops_", "dsh-ops"],
|
|
128
|
+
["memory_", "dsh-auto-memory"]
|
|
129
|
+
];
|
|
130
|
+
/** Single plugin tools that carry no prefix. */
|
|
131
|
+
const PLUGIN_TOOLS = /* @__PURE__ */ new Map([["generate_image", "dsh-model-switch"]]);
|
|
132
|
+
/** `<base>__<owner>` — the extension-tool naming the QQ bot framework uses. */
|
|
133
|
+
const EXTENSION_TOOL = /^([A-Za-z0-9_-]+)__(.+)$/;
|
|
134
|
+
/**
|
|
135
|
+
* Default allowlist: every pure-read tool this deployment exposes, calibrated on
|
|
136
|
+
* a live desktop profile (105 tools) at dsh 0.2.1-rc.2.
|
|
137
|
+
*
|
|
138
|
+
* Grouped by origin so a reader can trace each entry through {@link SOURCE_CATALOG}.
|
|
139
|
+
* Entries ending in `*` are patterns: the QQ bot extension suffixes its tools with
|
|
140
|
+
* the bot's own name, so `sessions_list__*` keeps working when the persona is
|
|
141
|
+
* renamed.
|
|
142
|
+
*/
|
|
143
|
+
const DEFAULT_READ_ONLY_TOOLS = [
|
|
144
|
+
"read",
|
|
145
|
+
"grep",
|
|
146
|
+
"glob",
|
|
147
|
+
"read_image",
|
|
148
|
+
"skill",
|
|
149
|
+
"web_search",
|
|
150
|
+
"web_search_pro",
|
|
151
|
+
"web_fetch",
|
|
152
|
+
"job_list",
|
|
153
|
+
"job_output",
|
|
154
|
+
"terminal_list",
|
|
155
|
+
"terminal_read",
|
|
156
|
+
"list_agents",
|
|
157
|
+
"team_task_list",
|
|
158
|
+
"team_task_get",
|
|
159
|
+
"get_goal",
|
|
160
|
+
"schedule_list",
|
|
161
|
+
"list_mcp_resources",
|
|
162
|
+
"list_mcp_resource_templates",
|
|
163
|
+
"read_mcp_resource",
|
|
164
|
+
"session_search",
|
|
165
|
+
"session_event_search",
|
|
166
|
+
"session_event_read",
|
|
167
|
+
"session_event_trace",
|
|
168
|
+
"session_trace",
|
|
169
|
+
"ops_read_files",
|
|
170
|
+
"ops_discover_tools",
|
|
171
|
+
"bcdp_status",
|
|
172
|
+
"bcdp_help",
|
|
173
|
+
"bcdp_doctor",
|
|
174
|
+
"bcdp_snapshot",
|
|
175
|
+
"browser_window_status",
|
|
176
|
+
"memory_list",
|
|
177
|
+
"memory_read",
|
|
178
|
+
"mcp__wincu__windows_computer_use_accessibility_tree",
|
|
179
|
+
"mcp__wincu__windows_computer_use_element_info",
|
|
180
|
+
"mcp__wincu__windows_computer_use_find",
|
|
181
|
+
"mcp__wincu__windows_computer_use_health",
|
|
182
|
+
"mcp__wincu__windows_computer_use_list_windows",
|
|
183
|
+
"mcp__wincu__windows_computer_use_ocr",
|
|
184
|
+
"mcp__wincu__windows_computer_use_screenshot",
|
|
185
|
+
"mcp__wincu__windows_computer_use_snapshot",
|
|
186
|
+
"mcp__wincu__windows_computer_use_tools",
|
|
187
|
+
"mcp__wincu__windows_computer_use_wait",
|
|
188
|
+
"mcp__wincu__windows_computer_use_wait_for",
|
|
189
|
+
"sessions_list__*",
|
|
190
|
+
"bing_search__*"
|
|
191
|
+
];
|
|
192
|
+
/**
|
|
193
|
+
* Validate one allowlist entry.
|
|
194
|
+
*
|
|
195
|
+
* `*` is a suffix wildcard: the entry must keep at least one literal leading
|
|
196
|
+
* character, so a stray `*` can never turn the list into "allow everything".
|
|
197
|
+
*
|
|
198
|
+
* @param entry - the raw entry as typed in settings.
|
|
199
|
+
* @returns `ok`, or the specific problem.
|
|
200
|
+
*/
|
|
201
|
+
function validateAllowEntry(entry) {
|
|
202
|
+
const trimmed = entry.trim();
|
|
203
|
+
if (trimmed === "") return "empty";
|
|
204
|
+
if (/^\*+$/.test(trimmed)) return "bare-wildcard";
|
|
205
|
+
if (trimmed.startsWith("*")) return "leading-wildcard";
|
|
206
|
+
return "ok";
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Whether a tool name matches one allowlist entry.
|
|
210
|
+
*
|
|
211
|
+
* Exact names match exactly; an entry may end in `*`, which matches any
|
|
212
|
+
* remaining characters (including none). Invalid entries never match.
|
|
213
|
+
*
|
|
214
|
+
* @param name - the tool name from `exec.name`.
|
|
215
|
+
* @param entry - one allowlist entry.
|
|
216
|
+
* @returns true when the call may skip review.
|
|
217
|
+
*/
|
|
218
|
+
function matchesAllowPattern(name, entry) {
|
|
219
|
+
if (name === "" || validateAllowEntry(entry) !== "ok") return false;
|
|
220
|
+
const trimmed = entry.trim();
|
|
221
|
+
const star = trimmed.indexOf("*");
|
|
222
|
+
if (star === -1) return name === trimmed;
|
|
223
|
+
const prefix = trimmed.slice(0, star);
|
|
224
|
+
const suffix = trimmed.slice(star + 1);
|
|
225
|
+
if (!name.startsWith(prefix) || !name.endsWith(suffix)) return false;
|
|
226
|
+
return name.length >= prefix.length + suffix.length;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Whether a tool name is covered by an allowlist.
|
|
230
|
+
* @param name - the tool name under review.
|
|
231
|
+
* @param allowlist - configured entries, patterns included.
|
|
232
|
+
* @returns true when at least one entry matches.
|
|
233
|
+
*/
|
|
234
|
+
function isAllowedByList(name, allowlist) {
|
|
235
|
+
return allowlist.some((entry) => matchesAllowPattern(name, entry));
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Resolve the origin of one tool name for display.
|
|
239
|
+
*
|
|
240
|
+
* Resolution order: MCP bridge → `<tool>__<owner>` extension → plugin prefix →
|
|
241
|
+
* plugin table → core catalog → unknown. Only the first two are structural; the
|
|
242
|
+
* rest are calibrated names, so an unlisted tool is reported as unknown rather
|
|
243
|
+
* than guessed into a package.
|
|
244
|
+
*
|
|
245
|
+
* @param name - the tool name.
|
|
246
|
+
* @returns The origin, with its repository when known.
|
|
247
|
+
*/
|
|
248
|
+
function resolveToolSource(name) {
|
|
249
|
+
const trimmed = name.trim().replace(/\*+$/, "");
|
|
250
|
+
const mcp = /^mcp__([A-Za-z0-9_-]+)__/.exec(trimmed);
|
|
251
|
+
if (mcp !== null) {
|
|
252
|
+
const server = mcp[1];
|
|
253
|
+
const entry = SOURCE_BY_ID.get(MCP_SERVERS.get(server) ?? "");
|
|
254
|
+
if (entry !== void 0) return {
|
|
255
|
+
kind: "mcp",
|
|
256
|
+
label: entry.label,
|
|
257
|
+
repo: entry.repo,
|
|
258
|
+
owner: server
|
|
259
|
+
};
|
|
260
|
+
return {
|
|
261
|
+
kind: "mcp",
|
|
262
|
+
label: `MCP · ${server}`,
|
|
263
|
+
owner: server
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
const extension = EXTENSION_TOOL.exec(trimmed);
|
|
267
|
+
if (extension !== null && !trimmed.startsWith("mcp__")) {
|
|
268
|
+
const entry = SOURCE_BY_ID.get("qqbot-extension");
|
|
269
|
+
const base = extension[1];
|
|
270
|
+
const owner = extension[2];
|
|
271
|
+
return {
|
|
272
|
+
kind: "extension",
|
|
273
|
+
label: `扩展 · ${base}__${owner}`,
|
|
274
|
+
...entry === void 0 ? {} : { repo: entry.repo },
|
|
275
|
+
owner
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
if (trimmed.endsWith("__")) {
|
|
279
|
+
const entry = SOURCE_BY_ID.get("qqbot-extension");
|
|
280
|
+
return {
|
|
281
|
+
kind: "extension",
|
|
282
|
+
label: `扩展 · ${trimmed.slice(0, -2)}__*`,
|
|
283
|
+
...entry === void 0 ? {} : { repo: entry.repo },
|
|
284
|
+
owner: "*"
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
for (const [prefix, id] of PLUGIN_PREFIXES) {
|
|
288
|
+
if (!trimmed.startsWith(prefix)) continue;
|
|
289
|
+
const entry = SOURCE_BY_ID.get(id);
|
|
290
|
+
if (entry !== void 0) return {
|
|
291
|
+
kind: "plugin",
|
|
292
|
+
label: entry.label,
|
|
293
|
+
repo: entry.repo,
|
|
294
|
+
owner: id
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
const plugin = PLUGIN_TOOLS.get(trimmed);
|
|
298
|
+
if (plugin !== void 0) {
|
|
299
|
+
const entry = SOURCE_BY_ID.get(plugin);
|
|
300
|
+
if (entry !== void 0) return {
|
|
301
|
+
kind: "plugin",
|
|
302
|
+
label: entry.label,
|
|
303
|
+
repo: entry.repo,
|
|
304
|
+
owner: plugin
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
if (CORE_TOOLS.has(trimmed)) {
|
|
308
|
+
const entry = SOURCE_BY_ID.get("core");
|
|
309
|
+
return {
|
|
310
|
+
kind: "core",
|
|
311
|
+
label: entry?.label ?? "DSH 核心",
|
|
312
|
+
...entry === void 0 ? {} : { repo: entry.repo },
|
|
313
|
+
owner: "core"
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
return {
|
|
317
|
+
kind: "unknown",
|
|
318
|
+
label: "未知来源"
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
//#endregion
|
|
11
322
|
//#region src/client-contract.ts
|
|
12
323
|
const MODEL_SWITCH_CONFIG_ID = "model-switch";
|
|
13
324
|
const PROVIDERS_CONFIG_ID = "llm-providers-ui";
|
|
@@ -34,6 +345,22 @@ window.__ModuleLoader__.load({
|
|
|
34
345
|
provider: "autoReviewProvider",
|
|
35
346
|
model: "autoReviewModel"
|
|
36
347
|
});
|
|
348
|
+
Object.freeze({
|
|
349
|
+
enabled: "autoReviewSkipReadOnly",
|
|
350
|
+
tools: "autoReviewAllowlist"
|
|
351
|
+
});
|
|
352
|
+
/**
|
|
353
|
+
* Derive the allowlist editor's value.
|
|
354
|
+
*
|
|
355
|
+
* An absent list means "never configured" and shows the shipped defaults; an
|
|
356
|
+
* explicitly emptied list stays empty, so clearing it is not undone by a reload.
|
|
357
|
+
*/
|
|
358
|
+
function deriveAutoReviewAllowlist(settings) {
|
|
359
|
+
return {
|
|
360
|
+
enabled: settings.autoReviewSkipReadOnly === true,
|
|
361
|
+
tools: Array.isArray(settings.autoReviewAllowlist) ? [...settings.autoReviewAllowlist] : [...DEFAULT_READ_ONLY_TOOLS]
|
|
362
|
+
};
|
|
363
|
+
}
|
|
37
364
|
/** Compaction summarization route for `/compact` and automatic compaction; both unset means the official default (the conversation target). */
|
|
38
365
|
const COMPACTION_SETTINGS_FIELDS = Object.freeze({
|
|
39
366
|
provider: "compactProvider",
|
|
@@ -86,6 +413,52 @@ window.__ModuleLoader__.load({
|
|
|
86
413
|
function routePairConfigured(route) {
|
|
87
414
|
return route !== void 0 && (route.provider ?? "").trim() !== "" && (route.model ?? "").trim() !== "";
|
|
88
415
|
}
|
|
416
|
+
/**
|
|
417
|
+
* Decode the reviewer counters from a `tools` RPC payload.
|
|
418
|
+
* @param value - the RPC result value.
|
|
419
|
+
* @returns The counters, or undefined when the Host does not report them.
|
|
420
|
+
*/
|
|
421
|
+
function decodeReviewStatus(value) {
|
|
422
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
423
|
+
const raw = value.status;
|
|
424
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return void 0;
|
|
425
|
+
const status = raw;
|
|
426
|
+
const count = (input) => typeof input === "number" && Number.isFinite(input) && input >= 0 ? input : 0;
|
|
427
|
+
return {
|
|
428
|
+
held: status.held === true,
|
|
429
|
+
allowedCalls: count(status.allowedCalls),
|
|
430
|
+
skippedReadOnly: count(status.skippedReadOnly)
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Decode the `tools` RPC payload.
|
|
435
|
+
*
|
|
436
|
+
* Malformed entries are skipped rather than failing the page: the catalog is a
|
|
437
|
+
* convenience view over the Host's tools, and a partial list is still useful.
|
|
438
|
+
*
|
|
439
|
+
* @param value - the RPC result value.
|
|
440
|
+
* @returns The rows, or undefined when the payload is not a catalog at all.
|
|
441
|
+
*/
|
|
442
|
+
function decodeToolCatalog(value) {
|
|
443
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
444
|
+
const listed = value.tools;
|
|
445
|
+
if (!Array.isArray(listed)) return void 0;
|
|
446
|
+
const rows = [];
|
|
447
|
+
for (const entry of listed) {
|
|
448
|
+
if (entry === null || typeof entry !== "object" || Array.isArray(entry)) continue;
|
|
449
|
+
const raw = entry;
|
|
450
|
+
if (typeof raw.name !== "string" || raw.name === "") continue;
|
|
451
|
+
const source = raw.source !== null && typeof raw.source === "object" && !Array.isArray(raw.source) ? raw.source : {};
|
|
452
|
+
rows.push({
|
|
453
|
+
name: raw.name,
|
|
454
|
+
description: typeof raw.description === "string" ? raw.description : "",
|
|
455
|
+
sourceLabel: typeof source.label === "string" ? source.label : "未知来源",
|
|
456
|
+
...typeof source.repo === "string" ? { repo: source.repo } : {},
|
|
457
|
+
recommended: isAllowedByList(raw.name, DEFAULT_READ_ONLY_TOOLS)
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
return rows;
|
|
461
|
+
}
|
|
89
462
|
//#endregion
|
|
90
463
|
//#region src/client/derived-config-form.ts
|
|
91
464
|
function deriveConfigForm(source, project, fields) {
|
|
@@ -666,7 +1039,7 @@ window.__ModuleLoader__.load({
|
|
|
666
1039
|
}
|
|
667
1040
|
//#endregion
|
|
668
1041
|
//#region \0dsh-css:src/client/ModelSwitchSettings.module.css.mjs
|
|
669
|
-
const css$2 = ".djvrPG_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex;container-type:inline-size}.djvrPG_title{margin:0;font-size:16px;font-weight:500;line-height:24px}.djvrPG_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.djvrPG_saved{color:var(--dsw-alias-state-success-primary);align-items:center;gap:6px;margin:0;font-size:12px;line-height:18px;display:flex}.djvrPG_savedDot{background:currentColor;border-radius:50%;width:7px;height:7px}.djvrPG_group{flex-direction:column;gap:8px;margin-top:8px;display:flex}.djvrPG_groupLabel{color:var(--dsw-alias-label-tertiary);margin:0 2px;font-size:12px;font-weight:500;line-height:18px}.djvrPG_routeCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);border-radius:10px;list-style:none;overflow:hidden}.djvrPG_routeCardUnavailable{opacity:.72}.djvrPG_routeHeader{width:100%;min-height:68px;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;justify-content:space-between;align-items:center;gap:16px;padding:12px 14px;display:flex}.djvrPG_routeHeader:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.djvrPG_routeHeader:disabled{cursor:default}.djvrPG_routeIcon{width:18px;height:18px;color:var(--dsw-alias-label-primary);flex:none;display:block}.djvrPG_routeCopy{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.djvrPG_routeName{color:var(--dsw-alias-label-primary);align-items:center;gap:8px;font-size:14px;font-weight:600;line-height:20px;display:flex}.djvrPG_routeSummary{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;font-size:13px;line-height:18px;overflow:hidden}.djvrPG_badge{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.djvrPG_badgeWarn{color:var(--dsw-alias-state-warn-label)}.djvrPG_chevron{width:18px;height:18px;color:var(--dsw-alias-label-primary);flex:none;transition:transform .16s}.djvrPG_routeCardOpen .djvrPG_chevron{transform:rotate(180deg)}.djvrPG_cardBody{border-top:1px solid var(--dsw-alias-border-l2);padding:16px 14px 18px}.djvrPG_formGrid{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;display:grid}.djvrPG_field{flex-direction:column;gap:6px;min-width:0;display:flex}.djvrPG_fieldFull{grid-column:1/-1}.djvrPG_fieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.djvrPG_input{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);appearance:none;background-color:var(--dsw-alias-bg-layer-1);width:100%;height:32px;color:var(--dsw-alias-label-primary);font:inherit;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");background-position:right 10px center;background-repeat:no-repeat;border-radius:8px;padding:0 32px 0 10px;font-size:14px;line-height:22px}.djvrPG_textInput{background-image:none;padding-right:10px}.djvrPG_input:focus{border-color:var(--dsw-alias-brand-primary);outline:none}.djvrPG_input:disabled{opacity:.6;cursor:default}.djvrPG_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.djvrPG_warning{color:var(--dsw-alias-state-warn-label)}.djvrPG_message{flex:1;min-width:0}.djvrPG_cardFooter{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;margin-top:14px;padding:12px 0 0;display:flex}.djvrPG_button{box-sizing:border-box;height:36px;font:inherit;cursor:pointer;border-radius:18px;justify-content:center;align-items:center;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.djvrPG_secondaryButton{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0}.djvrPG_secondaryButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.djvrPG_primaryButton{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border:0}.djvrPG_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.djvrPG_button:disabled{opacity:.4;cursor:default}.djvrPG_button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}@container (width<=420px){.djvrPG_formGrid{grid-template-columns:1fr}.djvrPG_cardFooter .djvrPG_button{flex:1}}.djvrPG_routeHeaderRow{align-items:center;gap:8px;padding-right:14px;display:flex}.djvrPG_routeHeaderRow .djvrPG_routeHeader{flex:1;min-width:0}.djvrPG_rowCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);border-radius:10px;align-items:flex-start;gap:16px;padding:14px 16px;display:flex}.djvrPG_rowCopy{flex:1;gap:4px;min-width:0;display:grid}.djvrPG_rowTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:600;line-height:20px}.djvrPG_switch{background:var(--dsw-alias-bg-layer-1);width:40px;height:24px;box-shadow:inset 0 0 0 1px var(--dsw-alias-border-l2);cursor:pointer;border:0;border-radius:999px;flex:none;padding:0;position:relative}.djvrPG_switchOn{background:var(--dsw-alias-button-primary-fill);box-shadow:none}.djvrPG_switch i{background:var(--dsw-alias-label-primary-foreground);pointer-events:none;border-radius:50%;width:18px;height:18px;transition:left .16s;position:absolute;top:3px;left:3px}.djvrPG_switchOn i{left:19px}.djvrPG_switch:disabled{opacity:.4;cursor:default}.djvrPG_switch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}@media (prefers-reduced-motion:reduce){.djvrPG_chevron,.djvrPG_switch i{transition:none}}";
|
|
1042
|
+
const css$2 = ".djvrPG_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex;container-type:inline-size}.djvrPG_title{margin:0;font-size:16px;font-weight:500;line-height:24px}.djvrPG_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.djvrPG_saved{color:var(--dsw-alias-state-success-primary);align-items:center;gap:6px;margin:0;font-size:12px;line-height:18px;display:flex}.djvrPG_savedDot{background:currentColor;border-radius:50%;width:7px;height:7px}.djvrPG_group{flex-direction:column;gap:8px;margin-top:8px;display:flex}.djvrPG_groupLabel{color:var(--dsw-alias-label-tertiary);margin:0 2px;font-size:12px;font-weight:500;line-height:18px}.djvrPG_routeCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);border-radius:10px;list-style:none;overflow:hidden}.djvrPG_routeCardUnavailable{opacity:.72}.djvrPG_routeHeader{width:100%;min-height:68px;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;justify-content:space-between;align-items:center;gap:16px;padding:12px 14px;display:flex}.djvrPG_routeHeader:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.djvrPG_routeHeader:disabled{cursor:default}.djvrPG_routeIcon{width:18px;height:18px;color:var(--dsw-alias-label-primary);flex:none;display:block}.djvrPG_routeCopy{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.djvrPG_routeName{color:var(--dsw-alias-label-primary);align-items:center;gap:8px;font-size:14px;font-weight:600;line-height:20px;display:flex}.djvrPG_routeSummary{color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;font-size:13px;line-height:18px;overflow:hidden}.djvrPG_badge{background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.djvrPG_badgeWarn{color:var(--dsw-alias-state-warn-label)}.djvrPG_chevron{width:18px;height:18px;color:var(--dsw-alias-label-primary);flex:none;transition:transform .16s}.djvrPG_routeCardOpen .djvrPG_chevron{transform:rotate(180deg)}.djvrPG_cardBody{border-top:1px solid var(--dsw-alias-border-l2);padding:16px 14px 18px}.djvrPG_formGrid{grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;display:grid}.djvrPG_field{flex-direction:column;gap:6px;min-width:0;display:flex}.djvrPG_fieldFull{grid-column:1/-1}.djvrPG_fieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.djvrPG_input{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);appearance:none;background-color:var(--dsw-alias-bg-layer-1);width:100%;height:32px;color:var(--dsw-alias-label-primary);font:inherit;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none'%3E%3Cpath d='M3 4.5L6 7.5L9 4.5' stroke='%2381858C' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\");background-position:right 10px center;background-repeat:no-repeat;border-radius:8px;padding:0 32px 0 10px;font-size:14px;line-height:22px}.djvrPG_textInput{background-image:none;padding-right:10px}.djvrPG_input:focus{border-color:var(--dsw-alias-brand-primary);outline:none}.djvrPG_input:disabled{opacity:.6;cursor:default}.djvrPG_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.djvrPG_warning{color:var(--dsw-alias-state-warn-label)}.djvrPG_message{flex:1;min-width:0}.djvrPG_cardFooter{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;margin-top:14px;padding:12px 0 0;display:flex}.djvrPG_button{box-sizing:border-box;height:36px;font:inherit;cursor:pointer;border-radius:18px;justify-content:center;align-items:center;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.djvrPG_secondaryButton{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0}.djvrPG_secondaryButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.djvrPG_primaryButton{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border:0}.djvrPG_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.djvrPG_button:disabled{opacity:.4;cursor:default}.djvrPG_button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}@container (width<=420px){.djvrPG_formGrid{grid-template-columns:1fr}.djvrPG_cardFooter .djvrPG_button{flex:1}}.djvrPG_routeHeaderRow{align-items:center;gap:8px;padding-right:14px;display:flex}.djvrPG_routeHeaderRow .djvrPG_routeHeader{flex:1;min-width:0}.djvrPG_rowCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);border-radius:10px;align-items:flex-start;gap:16px;padding:14px 16px;display:flex}.djvrPG_rowCopy{flex:1;gap:4px;min-width:0;display:grid}.djvrPG_rowTitle{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:600;line-height:20px}.djvrPG_switch{background:var(--dsw-alias-bg-layer-1);width:40px;height:24px;box-shadow:inset 0 0 0 1px var(--dsw-alias-border-l2);cursor:pointer;border:0;border-radius:999px;flex:none;padding:0;position:relative}.djvrPG_switchOn{background:var(--dsw-alias-button-primary-fill);box-shadow:none}.djvrPG_switch i{background:var(--dsw-alias-label-primary-foreground);pointer-events:none;border-radius:50%;width:18px;height:18px;transition:left .16s;position:absolute;top:3px;left:3px}.djvrPG_switchOn i{left:19px}.djvrPG_switch:disabled{opacity:.4;cursor:default}.djvrPG_switch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}@media (prefers-reduced-motion:reduce){.djvrPG_chevron,.djvrPG_switch i{transition:none}}.djvrPG_allowToolbar{flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:10px;display:flex}.djvrPG_allowToolbar .djvrPG_input{flex:220px;width:auto;padding-right:10px}.djvrPG_allowList{flex-direction:column;gap:6px;margin:0 0 12px;padding:0;list-style:none;display:flex}.djvrPG_allowRow{border:1px solid var(--dsw-alias-border-l2);background-color:var(--dsw-alias-bg-layer-1);border-radius:8px;align-items:center;gap:10px;padding:6px 10px;display:flex}.djvrPG_allowLabel{cursor:default;flex:auto;align-items:center;gap:8px;min-width:0;display:flex}.djvrPG_allowLabel input{width:14px;height:14px;accent-color:var(--dsw-alias-brand-primary);flex:none}.djvrPG_allowName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:18px;overflow:hidden}.djvrPG_allowSource{color:var(--dsw-alias-label-secondary);flex:none;align-items:center;gap:4px;font-size:12px;line-height:18px;display:flex}.djvrPG_allowDot{opacity:.6}.djvrPG_allowRepo{color:var(--dsw-alias-brand-primary);text-decoration:none}.djvrPG_allowRepo:hover{text-decoration:underline}";
|
|
670
1043
|
const tagId$2 = "dsh-model-switch/ModelSwitchSettings.module.css";
|
|
671
1044
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
672
1045
|
const tag = document.createElement("style");
|
|
@@ -676,6 +1049,14 @@ window.__ModuleLoader__.load({
|
|
|
676
1049
|
document.head.appendChild(tag);
|
|
677
1050
|
}
|
|
678
1051
|
var ModelSwitchSettings_module_css_default = {
|
|
1052
|
+
"allowDot": "djvrPG_allowDot",
|
|
1053
|
+
"allowLabel": "djvrPG_allowLabel",
|
|
1054
|
+
"allowList": "djvrPG_allowList",
|
|
1055
|
+
"allowName": "djvrPG_allowName",
|
|
1056
|
+
"allowRepo": "djvrPG_allowRepo",
|
|
1057
|
+
"allowRow": "djvrPG_allowRow",
|
|
1058
|
+
"allowSource": "djvrPG_allowSource",
|
|
1059
|
+
"allowToolbar": "djvrPG_allowToolbar",
|
|
679
1060
|
"badge": "djvrPG_badge",
|
|
680
1061
|
"badgeWarn": "djvrPG_badgeWarn",
|
|
681
1062
|
"button": "djvrPG_button",
|
|
@@ -1114,6 +1495,10 @@ window.__ModuleLoader__.load({
|
|
|
1114
1495
|
const [message, setMessage] = (0, react.useState)();
|
|
1115
1496
|
const [groupProvider, setGroupProvider] = (0, react.useState)("");
|
|
1116
1497
|
const [groupDraft, setGroupDraft] = (0, react.useState)(void 0);
|
|
1498
|
+
const [allowlistQuery, setAllowlistQuery] = (0, react.useState)("");
|
|
1499
|
+
const [catalogRows, setCatalogRows] = (0, react.useState)(void 0);
|
|
1500
|
+
const [catalogState, setCatalogState] = (0, react.useState)("idle");
|
|
1501
|
+
const [reviewStatus, setReviewStatus] = (0, react.useState)(void 0);
|
|
1117
1502
|
const loadSearchCapabilities = props.loadCapabilities;
|
|
1118
1503
|
const [searchSnapshot, setSearchSnapshot] = (0, react.useState)(void 0);
|
|
1119
1504
|
const [searchError, setSearchError] = (0, react.useState)(void 0);
|
|
@@ -1336,6 +1721,58 @@ window.__ModuleLoader__.load({
|
|
|
1336
1721
|
setBusy(void 0);
|
|
1337
1722
|
}
|
|
1338
1723
|
};
|
|
1724
|
+
/**
|
|
1725
|
+
* The Auto reviewer's read-only allowlist.
|
|
1726
|
+
*
|
|
1727
|
+
* An absent stored list means "never configured" and renders the shipped
|
|
1728
|
+
* defaults; an explicitly emptied list stays empty. Every write carries both
|
|
1729
|
+
* the switch and the entries, so the two can never drift apart on disk.
|
|
1730
|
+
*/
|
|
1731
|
+
const allowlistView = switchSettings.value === void 0 ? void 0 : deriveAutoReviewAllowlist(switchSettings.value);
|
|
1732
|
+
const allowlistTools = allowlistView?.tools ?? DEFAULT_READ_ONLY_TOOLS;
|
|
1733
|
+
const allowlistEnabled = allowlistView?.enabled === true;
|
|
1734
|
+
const allowlistWritable = switchSettings.status === "ready" && switchSettings.writable;
|
|
1735
|
+
const persistAllowlist = (next) => {
|
|
1736
|
+
if (busy !== void 0 || !allowlistWritable) return;
|
|
1737
|
+
const tools = next.tools.filter((entry) => validateAllowEntry(entry) === "ok");
|
|
1738
|
+
run("allowlist", () => props.saveReviewAllowlist({
|
|
1739
|
+
enabled: next.enabled,
|
|
1740
|
+
tools
|
|
1741
|
+
}));
|
|
1742
|
+
};
|
|
1743
|
+
const setAllowTool = (name, allowed) => {
|
|
1744
|
+
if (allowed) {
|
|
1745
|
+
if (isAllowedByList(name, allowlistTools)) return;
|
|
1746
|
+
persistAllowlist({
|
|
1747
|
+
enabled: true,
|
|
1748
|
+
tools: [...allowlistTools, name]
|
|
1749
|
+
});
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
persistAllowlist({
|
|
1753
|
+
enabled: allowlistEnabled,
|
|
1754
|
+
tools: allowlistTools.filter((entry) => entry !== name)
|
|
1755
|
+
});
|
|
1756
|
+
};
|
|
1757
|
+
const scanAllowlistTools = () => {
|
|
1758
|
+
const scan = props.scanTools;
|
|
1759
|
+
if (scan === void 0 || busy !== void 0) return;
|
|
1760
|
+
setCatalogState("loading");
|
|
1761
|
+
run("allowlist", async () => {
|
|
1762
|
+
const scanned = await scan();
|
|
1763
|
+
setCatalogRows(scanned.rows);
|
|
1764
|
+
setReviewStatus(scanned.status);
|
|
1765
|
+
}).then((accepted) => {
|
|
1766
|
+
setCatalogState(accepted ? "ready" : "failed");
|
|
1767
|
+
});
|
|
1768
|
+
};
|
|
1769
|
+
const allowlistQueryText = allowlistQuery.trim().toLowerCase();
|
|
1770
|
+
const allowlistIncluded = allowlistTools.map((entry) => ({
|
|
1771
|
+
entry,
|
|
1772
|
+
source: resolveToolSource(entry)
|
|
1773
|
+
})).filter((row) => allowlistQueryText === "" || row.entry.toLowerCase().includes(allowlistQueryText));
|
|
1774
|
+
const allowlistAvailable = (catalogRows ?? []).filter((row) => !isAllowedByList(row.name, allowlistTools)).filter((row) => allowlistQueryText === "" || row.name.toLowerCase().includes(allowlistQueryText) || row.sourceLabel.toLowerCase().includes(allowlistQueryText));
|
|
1775
|
+
const allowlistSummaryText = allowlistView === void 0 ? props.t("loading") : allowlistEnabled ? props.t("reviewAllowlistOnSummary").replace("{count}", String(allowlistTools.length)) : props.t("reviewAllowlistOffSummary");
|
|
1339
1776
|
const saveSubagent = () => {
|
|
1340
1777
|
if (subagentDraft === void 0 || subagentDraft.mode !== "fixed") return;
|
|
1341
1778
|
const matched = subagentCatalogModel;
|
|
@@ -2074,6 +2511,174 @@ window.__ModuleLoader__.load({
|
|
|
2074
2511
|
})
|
|
2075
2512
|
]
|
|
2076
2513
|
}),
|
|
2514
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2515
|
+
className: ModelSwitchSettings_module_css_default.group,
|
|
2516
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
2517
|
+
className: ModelSwitchSettings_module_css_default.groupLabel,
|
|
2518
|
+
children: props.t("reviewAllowlistGroup")
|
|
2519
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(RouteCard, {
|
|
2520
|
+
title: props.t("reviewAllowlist"),
|
|
2521
|
+
summary: allowlistSummaryText,
|
|
2522
|
+
hint: props.t("reviewAllowlistHelp"),
|
|
2523
|
+
icon: "autoReview",
|
|
2524
|
+
open: open === "allowlist",
|
|
2525
|
+
onToggle: () => {
|
|
2526
|
+
toggle("allowlist");
|
|
2527
|
+
},
|
|
2528
|
+
...allowlistEnabled ? { badge: props.t("reviewAllowlistBadge") } : {},
|
|
2529
|
+
trailing: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
2530
|
+
checked: allowlistEnabled,
|
|
2531
|
+
disabled: busy !== void 0 || !allowlistWritable,
|
|
2532
|
+
label: props.t("reviewAllowlist"),
|
|
2533
|
+
onChange: (value) => {
|
|
2534
|
+
persistAllowlist({
|
|
2535
|
+
enabled: value,
|
|
2536
|
+
tools: allowlistTools
|
|
2537
|
+
});
|
|
2538
|
+
}
|
|
2539
|
+
}),
|
|
2540
|
+
children: [
|
|
2541
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2542
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
2543
|
+
children: props.t("reviewAllowlistNote")
|
|
2544
|
+
}),
|
|
2545
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2546
|
+
className: ModelSwitchSettings_module_css_default.allowToolbar,
|
|
2547
|
+
children: [
|
|
2548
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2549
|
+
className: ModelSwitchSettings_module_css_default.input,
|
|
2550
|
+
type: "search",
|
|
2551
|
+
value: allowlistQuery,
|
|
2552
|
+
placeholder: props.t("reviewAllowlistSearch"),
|
|
2553
|
+
onChange: (event) => {
|
|
2554
|
+
setAllowlistQuery(event.target.value);
|
|
2555
|
+
}
|
|
2556
|
+
}),
|
|
2557
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2558
|
+
type: "button",
|
|
2559
|
+
className: cx(ModelSwitchSettings_module_css_default.button, ModelSwitchSettings_module_css_default.secondaryButton),
|
|
2560
|
+
disabled: busy !== void 0 || props.scanTools === void 0 || catalogState === "loading",
|
|
2561
|
+
onClick: scanAllowlistTools,
|
|
2562
|
+
children: catalogState === "loading" ? props.t("reviewAllowlistScanning") : props.t("reviewAllowlistScan")
|
|
2563
|
+
}),
|
|
2564
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2565
|
+
type: "button",
|
|
2566
|
+
className: cx(ModelSwitchSettings_module_css_default.button, ModelSwitchSettings_module_css_default.secondaryButton),
|
|
2567
|
+
disabled: busy !== void 0 || !allowlistWritable,
|
|
2568
|
+
onClick: () => {
|
|
2569
|
+
persistAllowlist({
|
|
2570
|
+
enabled: allowlistEnabled,
|
|
2571
|
+
tools: [...DEFAULT_READ_ONLY_TOOLS]
|
|
2572
|
+
});
|
|
2573
|
+
},
|
|
2574
|
+
children: props.t("reviewAllowlistReset")
|
|
2575
|
+
})
|
|
2576
|
+
]
|
|
2577
|
+
}),
|
|
2578
|
+
message?.route === "allowlist" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2579
|
+
className: cx(ModelSwitchSettings_module_css_default.hint, ModelSwitchSettings_module_css_default.message),
|
|
2580
|
+
children: message.text
|
|
2581
|
+
}) : null,
|
|
2582
|
+
reviewStatus === void 0 || reviewStatus.skippedReadOnly === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2583
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
2584
|
+
children: props.t("reviewAllowlistSkipped").replace("{count}", String(reviewStatus.skippedReadOnly))
|
|
2585
|
+
}),
|
|
2586
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
2587
|
+
className: ModelSwitchSettings_module_css_default.allowList,
|
|
2588
|
+
children: allowlistIncluded.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
2589
|
+
className: ModelSwitchSettings_module_css_default.allowRow,
|
|
2590
|
+
children: [
|
|
2591
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2592
|
+
className: ModelSwitchSettings_module_css_default.allowLabel,
|
|
2593
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2594
|
+
type: "checkbox",
|
|
2595
|
+
checked: true,
|
|
2596
|
+
readOnly: true
|
|
2597
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
2598
|
+
className: ModelSwitchSettings_module_css_default.allowName,
|
|
2599
|
+
children: row.entry
|
|
2600
|
+
})]
|
|
2601
|
+
}),
|
|
2602
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2603
|
+
className: ModelSwitchSettings_module_css_default.allowSource,
|
|
2604
|
+
children: [row.source.label, row.source.repo === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2605
|
+
className: ModelSwitchSettings_module_css_default.allowDot,
|
|
2606
|
+
children: "·"
|
|
2607
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
2608
|
+
className: ModelSwitchSettings_module_css_default.allowRepo,
|
|
2609
|
+
href: row.source.repo,
|
|
2610
|
+
target: "_blank",
|
|
2611
|
+
rel: "noreferrer",
|
|
2612
|
+
children: props.t("reviewAllowlistRepo")
|
|
2613
|
+
})] })]
|
|
2614
|
+
}),
|
|
2615
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2616
|
+
type: "button",
|
|
2617
|
+
className: cx(ModelSwitchSettings_module_css_default.button, ModelSwitchSettings_module_css_default.secondaryButton),
|
|
2618
|
+
disabled: busy !== void 0 || !allowlistWritable,
|
|
2619
|
+
onClick: () => {
|
|
2620
|
+
setAllowTool(row.entry, false);
|
|
2621
|
+
},
|
|
2622
|
+
children: props.t("reviewAllowlistRemove")
|
|
2623
|
+
})
|
|
2624
|
+
]
|
|
2625
|
+
}, row.entry))
|
|
2626
|
+
}),
|
|
2627
|
+
allowlistIncluded.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2628
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
2629
|
+
children: props.t("reviewAllowlistNone")
|
|
2630
|
+
}) : null,
|
|
2631
|
+
allowlistAvailable.length === 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2632
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
2633
|
+
children: props.t("reviewAllowlistAvailable")
|
|
2634
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
2635
|
+
className: ModelSwitchSettings_module_css_default.allowList,
|
|
2636
|
+
children: allowlistAvailable.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
2637
|
+
className: ModelSwitchSettings_module_css_default.allowRow,
|
|
2638
|
+
children: [
|
|
2639
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2640
|
+
className: ModelSwitchSettings_module_css_default.allowLabel,
|
|
2641
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2642
|
+
type: "checkbox",
|
|
2643
|
+
checked: false,
|
|
2644
|
+
readOnly: true
|
|
2645
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
2646
|
+
className: ModelSwitchSettings_module_css_default.allowName,
|
|
2647
|
+
children: row.name
|
|
2648
|
+
})]
|
|
2649
|
+
}),
|
|
2650
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2651
|
+
className: ModelSwitchSettings_module_css_default.allowSource,
|
|
2652
|
+
children: [row.sourceLabel, row.repo === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2653
|
+
className: ModelSwitchSettings_module_css_default.allowDot,
|
|
2654
|
+
children: "·"
|
|
2655
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
2656
|
+
className: ModelSwitchSettings_module_css_default.allowRepo,
|
|
2657
|
+
href: row.repo,
|
|
2658
|
+
target: "_blank",
|
|
2659
|
+
rel: "noreferrer",
|
|
2660
|
+
children: props.t("reviewAllowlistRepo")
|
|
2661
|
+
})] })]
|
|
2662
|
+
}),
|
|
2663
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2664
|
+
type: "button",
|
|
2665
|
+
className: cx(ModelSwitchSettings_module_css_default.button, ModelSwitchSettings_module_css_default.secondaryButton),
|
|
2666
|
+
disabled: busy !== void 0 || !allowlistWritable,
|
|
2667
|
+
onClick: () => {
|
|
2668
|
+
setAllowTool(row.name, true);
|
|
2669
|
+
},
|
|
2670
|
+
children: props.t("reviewAllowlistAdd")
|
|
2671
|
+
})
|
|
2672
|
+
]
|
|
2673
|
+
}, row.name))
|
|
2674
|
+
})] }),
|
|
2675
|
+
catalogState === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2676
|
+
className: cx(ModelSwitchSettings_module_css_default.hint, ModelSwitchSettings_module_css_default.message),
|
|
2677
|
+
children: props.t("reviewAllowlistScanFailed")
|
|
2678
|
+
}) : null
|
|
2679
|
+
]
|
|
2680
|
+
})]
|
|
2681
|
+
}),
|
|
2077
2682
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2078
2683
|
className: ModelSwitchSettings_module_css_default.group,
|
|
2079
2684
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
@@ -2406,6 +3011,24 @@ window.__ModuleLoader__.load({
|
|
|
2406
3011
|
groupOverridesMore: "组覆盖:{listed} 等 {count} 个组;无组时用上方默认值",
|
|
2407
3012
|
saveTimeout: "保存超时,请重试",
|
|
2408
3013
|
saveRejected: "保存被拒绝,请重试",
|
|
3014
|
+
reviewAllowlistGroup: "审查豁免",
|
|
3015
|
+
reviewAllowlist: "纯读工具免审查",
|
|
3016
|
+
reviewAllowlistBadge: "已开启",
|
|
3017
|
+
reviewAllowlistOnSummary: "名单内 {count} 条免审查",
|
|
3018
|
+
reviewAllowlistOffSummary: "未开启:每次调用都经审查",
|
|
3019
|
+
reviewAllowlistHelp: "开启后,名单内的纯读工具直接执行,不再调用审查模型;名单外的写操作、命令执行与删除仍照常审查。",
|
|
3020
|
+
reviewAllowlistNote: "来源按工具名解析(MCP 前缀 / 插件前缀 / 扩展后缀);同名工具可以冒充来源,徽章只是提示,不是安全边界。",
|
|
3021
|
+
reviewAllowlistSearch: "搜索工具名或来源",
|
|
3022
|
+
reviewAllowlistScan: "扫描本机工具",
|
|
3023
|
+
reviewAllowlistScanning: "扫描中…",
|
|
3024
|
+
reviewAllowlistScanFailed: "扫描失败:Host 没有提供工具目录接口。",
|
|
3025
|
+
reviewAllowlistReset: "恢复默认名单",
|
|
3026
|
+
reviewAllowlistRemove: "移除",
|
|
3027
|
+
reviewAllowlistAdd: "加入",
|
|
3028
|
+
reviewAllowlistNone: "名单为空:没有任何工具免审查。",
|
|
3029
|
+
reviewAllowlistAvailable: "本机存在但未放行的工具:",
|
|
3030
|
+
reviewAllowlistRepo: "仓库",
|
|
3031
|
+
reviewAllowlistSkipped: "本 Host 进程已为纯读调用跳过 {count} 次审查。",
|
|
2409
3032
|
"reason.central-subagent-routing": "Alpha.4 没有全局子代理启动路由接口。",
|
|
2410
3033
|
"reason.packaged-preset-roots": "Alpha.4 不支持插件提供额外 preset root。",
|
|
2411
3034
|
"reason.tool-owner-suppression": "Alpha.4 没有工具所有者或来源抑制接口。",
|
|
@@ -2468,6 +3091,24 @@ window.__ModuleLoader__.load({
|
|
|
2468
3091
|
groupOverridesMore: "Group overrides: {listed} and {count} groups; without a group the default above applies",
|
|
2469
3092
|
saveTimeout: "The save timed out; try again",
|
|
2470
3093
|
saveRejected: "The save was rejected; try again",
|
|
3094
|
+
reviewAllowlistGroup: "Review bypass",
|
|
3095
|
+
reviewAllowlist: "Skip review for pure reads",
|
|
3096
|
+
reviewAllowlistBadge: "On",
|
|
3097
|
+
reviewAllowlistOnSummary: "{count} entries skip review",
|
|
3098
|
+
reviewAllowlistOffSummary: "Off: every call is reviewed",
|
|
3099
|
+
reviewAllowlistHelp: "When on, allowlisted pure-read tools run immediately instead of calling the reviewer. Writes, command execution and deletions stay reviewed.",
|
|
3100
|
+
reviewAllowlistNote: "Sources are resolved from the tool name (MCP prefix, plugin prefix, extension suffix); a lookalike tool can claim any origin, so the badge is a hint, not a security boundary.",
|
|
3101
|
+
reviewAllowlistSearch: "Search tool name or source",
|
|
3102
|
+
reviewAllowlistScan: "Scan local tools",
|
|
3103
|
+
reviewAllowlistScanning: "Scanning…",
|
|
3104
|
+
reviewAllowlistScanFailed: "Scan failed: the Host exposes no tool catalog endpoint.",
|
|
3105
|
+
reviewAllowlistReset: "Restore defaults",
|
|
3106
|
+
reviewAllowlistRemove: "Remove",
|
|
3107
|
+
reviewAllowlistAdd: "Add",
|
|
3108
|
+
reviewAllowlistNone: "Empty list: no tool skips review.",
|
|
3109
|
+
reviewAllowlistAvailable: "Present on this machine but not allowed:",
|
|
3110
|
+
reviewAllowlistRepo: "repo",
|
|
3111
|
+
reviewAllowlistSkipped: "This Host process has skipped the reviewer {count} times.",
|
|
2471
3112
|
"reason.central-subagent-routing": "Alpha.4 exposes no global Subagent start-routing seam.",
|
|
2472
3113
|
"reason.packaged-preset-roots": "Alpha.4 exposes no plugin-owned preset root.",
|
|
2473
3114
|
"reason.tool-owner-suppression": "Alpha.4 exposes no tool owner or provenance suppression seam.",
|
|
@@ -3982,6 +4623,29 @@ window.__ModuleLoader__.load({
|
|
|
3982
4623
|
throw new PlanDismissalUnsupportedError("this DSH Host exposes neither PendingQuestion.dismiss() (0.2.x) nor PendingQuestion.cancel() (0.1.x)");
|
|
3983
4624
|
}
|
|
3984
4625
|
//#endregion
|
|
4626
|
+
//#region src/picker/discuss-plan.ts
|
|
4627
|
+
/**
|
|
4628
|
+
* Withdraw the review by interrupting the turn, falling back to dismissal.
|
|
4629
|
+
*
|
|
4630
|
+
* Order matters: cancellation is what keeps the work details expanded, so it is
|
|
4631
|
+
* tried first and only genuinely unavailable/refused cancellation falls through
|
|
4632
|
+
* to the released withdrawal (which still gives the human the composer back, just
|
|
4633
|
+
* without the expansion guarantee).
|
|
4634
|
+
*
|
|
4635
|
+
* @param cancelTurn - the session cancel seam; absent on a Host without one.
|
|
4636
|
+
* @param sessionId - the session whose turn is being withdrawn.
|
|
4637
|
+
* @param dismiss - the released `PendingQuestion` withdrawal.
|
|
4638
|
+
* @returns Which route withdrew the review.
|
|
4639
|
+
*/
|
|
4640
|
+
async function discussPlan(cancelTurn, sessionId, dismiss) {
|
|
4641
|
+
if (cancelTurn !== void 0 && sessionId !== "") try {
|
|
4642
|
+
await cancelTurn(sessionId);
|
|
4643
|
+
return "interrupted";
|
|
4644
|
+
} catch {}
|
|
4645
|
+
await dismiss();
|
|
4646
|
+
return "dismissed";
|
|
4647
|
+
}
|
|
4648
|
+
//#endregion
|
|
3985
4649
|
//#region src/picker/plan-goal.ts
|
|
3986
4650
|
/**
|
|
3987
4651
|
* Strip the gateway's transport prefix from a Host message.
|
|
@@ -4277,10 +4941,11 @@ window.__ModuleLoader__.load({
|
|
|
4277
4941
|
goalRemote: props.goalRemote,
|
|
4278
4942
|
planPreview: props.planPreview,
|
|
4279
4943
|
...props.resolveInteractionOperations === void 0 ? {} : { resolveInteractionOperations: props.resolveInteractionOperations },
|
|
4280
|
-
...props.roleOf === void 0 ? {} : { roleOf: props.roleOf }
|
|
4944
|
+
...props.roleOf === void 0 ? {} : { roleOf: props.roleOf },
|
|
4945
|
+
...props.cancelTurn === void 0 ? {} : { cancelTurn: props.cancelTurn }
|
|
4281
4946
|
}, props.matched.key);
|
|
4282
4947
|
}
|
|
4283
|
-
function PlanReviewState({ matched, review, available, providerLock, agentLocked, lockFailed, directory, t, goalRemote, planPreview, resolveInteractionOperations, roleOf }) {
|
|
4948
|
+
function PlanReviewState({ matched, review, available, providerLock, agentLocked, lockFailed, directory, t, goalRemote, planPreview, resolveInteractionOperations, roleOf, cancelTurn }) {
|
|
4284
4949
|
const { snapshot, getDirectorySnapshot, load, select } = directory;
|
|
4285
4950
|
const [execution, setExecution] = (0, react.useState)(snapshot.current ?? void 0);
|
|
4286
4951
|
const [feedback, setFeedback] = (0, react.useState)("");
|
|
@@ -4501,7 +5166,9 @@ window.__ModuleLoader__.load({
|
|
|
4501
5166
|
icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconEditOutlineRegular, { size: 14 }),
|
|
4502
5167
|
disabled: busy,
|
|
4503
5168
|
onClick: () => {
|
|
4504
|
-
settle(() =>
|
|
5169
|
+
settle(async () => {
|
|
5170
|
+
await discussPlan(cancelTurn, matched.sessionId, () => respondDismiss(matched, t("plan.cancelRejected"), t("plan.dismissUnsupported")));
|
|
5171
|
+
}, true);
|
|
4505
5172
|
},
|
|
4506
5173
|
children: t("plan.discuss")
|
|
4507
5174
|
}),
|
|
@@ -4820,6 +5487,20 @@ window.__ModuleLoader__.load({
|
|
|
4820
5487
|
const rpcOf = () => {
|
|
4821
5488
|
return scope.get("connection", false)?.rpc;
|
|
4822
5489
|
};
|
|
5490
|
+
/**
|
|
5491
|
+
* Cancel the session's running turn through the official Stop seam.
|
|
5492
|
+
*
|
|
5493
|
+
* The discuss action interrupts instead of merely withdrawing the pending
|
|
5494
|
+
* question: an aborted turn is kept expanded by the transcript, so the work
|
|
5495
|
+
* details the human was reading never fold. The conversation service is
|
|
5496
|
+
* addressed through the session scope exactly like the official composer's
|
|
5497
|
+
* Stop button does.
|
|
5498
|
+
*/
|
|
5499
|
+
const cancelTurn = async (sessionId) => {
|
|
5500
|
+
const conversation = (sessions?.scope?.(sessionId))?.get?.("conversation");
|
|
5501
|
+
if (typeof conversation?.cancel !== "function") throw new Error("model-switch: the conversation service exposes no cancel() through the session scope");
|
|
5502
|
+
await conversation.cancel();
|
|
5503
|
+
};
|
|
4823
5504
|
const directoryFace = (sessionId) => {
|
|
4824
5505
|
const directory = models.directoryFor(sessionId);
|
|
4825
5506
|
const available = sessions?.subagentAddress?.(sessionId) === void 0;
|
|
@@ -4842,6 +5523,7 @@ window.__ModuleLoader__.load({
|
|
|
4842
5523
|
},
|
|
4843
5524
|
goalRemote: () => goalRemoteFrom(scope),
|
|
4844
5525
|
planPreview,
|
|
5526
|
+
cancelTurn,
|
|
4845
5527
|
hooks: {
|
|
4846
5528
|
directory: directory.store,
|
|
4847
5529
|
providerOrder: orderStore
|
|
@@ -5050,6 +5732,28 @@ window.__ModuleLoader__.load({
|
|
|
5050
5732
|
saveCapability: (route, ops) => {
|
|
5051
5733
|
return (route === "search" ? search : route === "image" ? image : route === "compaction" ? compaction : route === "title" ? sessionTitle : autoReview).mutate(ops);
|
|
5052
5734
|
},
|
|
5735
|
+
saveReviewAllowlist: async ({ enabled, tools }) => {
|
|
5736
|
+
if (!await owned.mutate([{
|
|
5737
|
+
op: "set",
|
|
5738
|
+
path: ["autoReviewSkipReadOnly"],
|
|
5739
|
+
value: enabled
|
|
5740
|
+
}, {
|
|
5741
|
+
op: "set",
|
|
5742
|
+
path: ["autoReviewAllowlist"],
|
|
5743
|
+
value: [...tools]
|
|
5744
|
+
}])) throw new Error("settings-rejected");
|
|
5745
|
+
},
|
|
5746
|
+
...connectionRpc === void 0 ? {} : { scanTools: async () => {
|
|
5747
|
+
const response = await callPluginRpc(connectionRpc, "plugin-rpc/model-switch", "tools", {});
|
|
5748
|
+
if (!response.ok || response.value === void 0) throw new Error(t("reviewAllowlistScanFailed"));
|
|
5749
|
+
const rows = decodeToolCatalog(response.value);
|
|
5750
|
+
if (rows === void 0) throw new Error(t("reviewAllowlistScanFailed"));
|
|
5751
|
+
const status = decodeReviewStatus(response.value);
|
|
5752
|
+
return status === void 0 ? { rows } : {
|
|
5753
|
+
rows,
|
|
5754
|
+
status
|
|
5755
|
+
};
|
|
5756
|
+
} },
|
|
5053
5757
|
saveProviderGroup: async (provider, group) => {
|
|
5054
5758
|
await saveProviderGroupFenced(owned, provider, group);
|
|
5055
5759
|
},
|