@logictan/dsh-desktop-agent 0.1.6 → 0.1.8
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/README.md +7 -5
- package/lib/client.js +139 -553
- package/lib/config.js +46 -7
- package/lib/driver.js +15 -0
- package/lib/index.js +2 -4
- package/package.json +9 -2
package/README.md
CHANGED
|
@@ -53,6 +53,11 @@ Only elements carrying both a token and a non-empty label become anchors.
|
|
|
53
53
|
None beyond installing this plugin. It publishes the `cua_driver_native__*` tools
|
|
54
54
|
itself, from the Cua Driver native SDK it depends on, and dispatches to them.
|
|
55
55
|
|
|
56
|
+
Those tools are registered but **not announced**: their 56 schemas are 109,857
|
|
57
|
+
characters of the first request, and the work goes through `desktop_agent` instead.
|
|
58
|
+
The registry resolves a call from its own layers rather than from the prompt, so a
|
|
59
|
+
call to one of these names still executes — it just is not advertised.
|
|
60
|
+
|
|
56
61
|
A profile that still mounts the old
|
|
57
62
|
`@deepseek-ai/dsh-experimental-computer-use-cua-driver-native` provider must drop
|
|
58
63
|
that row and its `@deepseek-ai/dsh-computer-use` dependency first: both would
|
|
@@ -81,11 +86,8 @@ it uses the frontmost window.
|
|
|
81
86
|
|
|
82
87
|
### When to call it
|
|
83
88
|
|
|
84
|
-
For any desktop task
|
|
85
|
-
|
|
86
|
-
captures and decisions stay inside this one call; driving the raw tools by hand
|
|
87
|
-
puts every screenshot into the agent's own context instead. The raw tools remain
|
|
88
|
-
the right choice for resolving a window, or for a single action already decided on.
|
|
89
|
+
For any desktop task, call `desktop_agent`. The loop's ~40 captures and decisions
|
|
90
|
+
stay inside this one call rather than entering the agent's own context.
|
|
89
91
|
|
|
90
92
|
For a task that lives in a web page, `browser_agent` is the better tool — it
|
|
91
93
|
drives the DOM over CDP and does not depend on reading pixels. This tool is for
|
package/lib/client.js
CHANGED
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
* `<bundle>#desktop-agent`, the key the Plugins page derives from the bundle
|
|
10
10
|
* that declares the row.
|
|
11
11
|
*
|
|
12
|
-
* The
|
|
13
|
-
*
|
|
12
|
+
* The card renders the host's `Config` schema through the shared `ConfigForm`.
|
|
13
|
+
* Scalar fields are rendered automatically from the describe mirror; this half
|
|
14
|
+
* supplies only the vision-catalog dropdowns (replacing the route fields via
|
|
15
|
+
* `renderControl`) and the catalog status banners in `children`.
|
|
14
16
|
*
|
|
15
17
|
* Two catalogs feed this card, and they are not interchangeable:
|
|
16
18
|
*
|
|
@@ -33,6 +35,7 @@ window.__ModuleLoader__.load({
|
|
|
33
35
|
id: "@logictan/dsh-desktop-agent",
|
|
34
36
|
factory: function (require) {
|
|
35
37
|
var React = require("react");
|
|
38
|
+
var UI = require("@logictan/dsh-plugins-shared-client");
|
|
36
39
|
|
|
37
40
|
/** Loader entry id this card's form is keyed by. */
|
|
38
41
|
var NS = "desktop-agent";
|
|
@@ -58,10 +61,6 @@ window.__ModuleLoader__.load({
|
|
|
58
61
|
intro:
|
|
59
62
|
"用截图作为主要感知、通过 Cua Driver 操作桌面窗口。留空表示继承默认值。需先在 profile 挂载 computer-use 的 cua-driver-native provider。",
|
|
60
63
|
inherit: "继承默认",
|
|
61
|
-
save: "保存",
|
|
62
|
-
saving: "保存中…",
|
|
63
|
-
reset: "恢复默认",
|
|
64
|
-
overridden: "已覆盖",
|
|
65
64
|
unwritable: "当前连接不写回本机设置,无法保存。",
|
|
66
65
|
catalogLoading: "正在读取视觉模型目录…",
|
|
67
66
|
catalogError: "视觉模型目录读取失败,可切换为手填。",
|
|
@@ -72,149 +71,8 @@ window.__ModuleLoader__.load({
|
|
|
72
71
|
manual: "手填",
|
|
73
72
|
picker: "从目录选择",
|
|
74
73
|
routeMissing: "已存的路由不在当前目录中(provider 读取失败或模型已下线),已切换为手填。",
|
|
75
|
-
deliveryBackground: "后台投递(默认)",
|
|
76
|
-
deliveryForeground: "前台投递(画布/游戏)",
|
|
77
74
|
};
|
|
78
75
|
|
|
79
|
-
/**
|
|
80
|
-
* Editable fields, in render order. `kind` picks the control.
|
|
81
|
-
*
|
|
82
|
-
* The three vision-route keys are rendered together below the catalog picker
|
|
83
|
-
* and are listed here only so that override detection, save, and reset see
|
|
84
|
-
* them.
|
|
85
|
-
*/
|
|
86
|
-
var FIELDS = [
|
|
87
|
-
{
|
|
88
|
-
key: "maxSteps",
|
|
89
|
-
label: "步数上限",
|
|
90
|
-
kind: "number",
|
|
91
|
-
min: 1,
|
|
92
|
-
step: 1,
|
|
93
|
-
hint: "一次任务最多执行多少个动作,默认 40。",
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
key: "maxImageDimension",
|
|
97
|
-
label: "截图长边上限",
|
|
98
|
-
kind: "number",
|
|
99
|
-
min: 200,
|
|
100
|
-
step: 1,
|
|
101
|
-
hint: "送给模型的截图最长边(像素),默认 1568。调小可省 token,但太小会看不清控件。",
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
key: "deliveryMode",
|
|
105
|
-
label: "投递方式",
|
|
106
|
-
kind: "select",
|
|
107
|
-
options: [
|
|
108
|
-
{ value: "background", label: copy.deliveryBackground },
|
|
109
|
-
{ value: "foreground", label: copy.deliveryForeground },
|
|
110
|
-
],
|
|
111
|
-
hint: "后台投递不抢焦点;游戏等自绘表面会过滤按 pid 路由的事件,需要前台投递。",
|
|
112
|
-
},
|
|
113
|
-
{
|
|
114
|
-
key: "allowBringToFront",
|
|
115
|
-
label: "允许抢前台",
|
|
116
|
-
kind: "checkbox",
|
|
117
|
-
hint: "关闭时模型不能使用 bring_to_front。开启后模型可把目标窗口提到最前,代价是抢走你当前的焦点;仅当窗口被压在后台、内容不刷新时才需要。",
|
|
118
|
-
},
|
|
119
|
-
];
|
|
120
|
-
|
|
121
|
-
/** The three vision route fields, rendered together below the catalog. */
|
|
122
|
-
var ROUTE_KEYS = ["visionProvider", "visionModel", "visionReasoningEffort"];
|
|
123
|
-
|
|
124
|
-
var wrapStyle = {
|
|
125
|
-
display: "flex",
|
|
126
|
-
flexDirection: "column",
|
|
127
|
-
gap: "12px",
|
|
128
|
-
maxWidth: "620px",
|
|
129
|
-
color: "var(--dsw-alias-label-primary)",
|
|
130
|
-
fontSize: "13px",
|
|
131
|
-
lineHeight: 1.6,
|
|
132
|
-
};
|
|
133
|
-
var headingStyle = { fontSize: "14px", fontWeight: 600 };
|
|
134
|
-
var hintStyle = { color: "var(--dsw-alias-label-tertiary)" };
|
|
135
|
-
var rowStyle = { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" };
|
|
136
|
-
var fieldStyle = { display: "flex", alignItems: "center", gap: "8px", flexWrap: "wrap" };
|
|
137
|
-
var labelStyle = { minWidth: "140px" };
|
|
138
|
-
var controlStyle = {
|
|
139
|
-
padding: "6px 8px",
|
|
140
|
-
border: "1px solid var(--dsw-alias-border-l2)",
|
|
141
|
-
borderRadius: "6px",
|
|
142
|
-
background: "var(--dsw-alias-bg-layer-1)",
|
|
143
|
-
color: "inherit",
|
|
144
|
-
font: "inherit",
|
|
145
|
-
fontVariantNumeric: "tabular-nums",
|
|
146
|
-
};
|
|
147
|
-
var inputStyle = Object.assign({ width: "132px" }, controlStyle);
|
|
148
|
-
var textStyle = Object.assign({ width: "260px" }, controlStyle);
|
|
149
|
-
var selectStyle = Object.assign({ minWidth: "200px" }, controlStyle);
|
|
150
|
-
var buttonStyle = {
|
|
151
|
-
padding: "6px 12px",
|
|
152
|
-
border: "1px solid var(--dsw-alias-border-l2)",
|
|
153
|
-
borderRadius: "6px",
|
|
154
|
-
background: "var(--dsw-alias-bg-layer-1)",
|
|
155
|
-
color: "inherit",
|
|
156
|
-
font: "inherit",
|
|
157
|
-
cursor: "pointer",
|
|
158
|
-
};
|
|
159
|
-
var primaryButtonStyle = Object.assign({}, buttonStyle, {
|
|
160
|
-
border: "1px solid var(--dsw-alias-button-primary-fill)",
|
|
161
|
-
background: "var(--dsw-alias-button-primary-fill)",
|
|
162
|
-
color: "var(--dsw-alias-label-primary-foreground)",
|
|
163
|
-
});
|
|
164
|
-
var errorStyle = { color: "var(--dsw-alias-state-error-primary)" };
|
|
165
|
-
var badgeStyle = {
|
|
166
|
-
padding: "1px 6px",
|
|
167
|
-
borderRadius: "999px",
|
|
168
|
-
background: "var(--dsw-alias-bg-layer-2, rgba(0, 0, 0, 0.08))",
|
|
169
|
-
color: "var(--dsw-alias-label-secondary)",
|
|
170
|
-
fontSize: "12px",
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
function has(object, key) {
|
|
174
|
-
return Object.prototype.hasOwnProperty.call(object, key);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function objectLayer(snapshot, which) {
|
|
178
|
-
var layer = snapshot && snapshot[which];
|
|
179
|
-
return layer !== null && typeof layer === "object" ? layer : {};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/** The schema-resolved section the Host last accepted. */
|
|
183
|
-
function accepted(snapshot) {
|
|
184
|
-
return objectLayer(snapshot, "value");
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/** The raw user layer; a key's presence here is what marks it overridden. */
|
|
188
|
-
function userLayer(snapshot) {
|
|
189
|
-
return objectLayer(snapshot, "user");
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function overridden(snapshot, key) {
|
|
193
|
-
return has(userLayer(snapshot), key);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/** The effective value for a field, as the string a control holds. */
|
|
197
|
-
function stored(snapshot, key) {
|
|
198
|
-
var value = accepted(snapshot)[key];
|
|
199
|
-
return value === undefined || value === null ? "" : String(value);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Parse a draft string into the value to store.
|
|
204
|
-
* @throws {Error} when the draft does not satisfy the field's schema.
|
|
205
|
-
*/
|
|
206
|
-
function parse(field, raw) {
|
|
207
|
-
if (field.kind === "text") return raw;
|
|
208
|
-
if (field.kind === "select") return raw;
|
|
209
|
-
if (field.kind === "checkbox") return raw === "true";
|
|
210
|
-
var value = Number(raw);
|
|
211
|
-
if (!isFinite(value)) throw new Error(field.label + "必须是数字。");
|
|
212
|
-
if (field.step === 1 && !Number.isInteger(value)) throw new Error(field.label + "必须是整数。");
|
|
213
|
-
if (field.min !== undefined && value < field.min) throw new Error(field.label + "不能小于 " + field.min + "。");
|
|
214
|
-
if (field.max !== undefined && value > field.max) throw new Error(field.label + "不能大于 " + field.max + "。");
|
|
215
|
-
return value;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
76
|
/**
|
|
219
77
|
* Load this plugin's own vision-capable model list.
|
|
220
78
|
*
|
|
@@ -328,11 +186,15 @@ window.__ModuleLoader__.load({
|
|
|
328
186
|
* The row's configuration card.
|
|
329
187
|
*
|
|
330
188
|
* `scope` is the configuration form `apply` obtained for this entry from
|
|
331
|
-
* `configForms.get(NS)`; `
|
|
332
|
-
*
|
|
189
|
+
* `configForms.get(NS)`; `describe` is the schema mirror obtained from
|
|
190
|
+
* `configForms.describe()`; `ctx` is the scoped context supplying the live
|
|
191
|
+
* model directory.
|
|
333
192
|
*/
|
|
334
193
|
function DesktopAgentSettingsCard(props) {
|
|
194
|
+
if (props.view === "summary") return copy.summary;
|
|
195
|
+
|
|
335
196
|
var scope = props.scope;
|
|
197
|
+
var describe = props.describe;
|
|
336
198
|
var ctx = props.ctx;
|
|
337
199
|
|
|
338
200
|
var subscribe = React.useCallback(
|
|
@@ -352,417 +214,152 @@ window.__ModuleLoader__.load({
|
|
|
352
214
|
var catalog = useVisionCatalog(ctx);
|
|
353
215
|
var host = useHostCatalog(ctx);
|
|
354
216
|
|
|
355
|
-
var draftState = React.useState({});
|
|
356
|
-
var draft = draftState[0];
|
|
357
|
-
var setDraft = draftState[1];
|
|
358
|
-
var busyState = React.useState(false);
|
|
359
|
-
var busy = busyState[0];
|
|
360
|
-
var setBusy = busyState[1];
|
|
361
|
-
var errorState = React.useState(null);
|
|
362
|
-
var error = errorState[0];
|
|
363
|
-
var setError = errorState[1];
|
|
364
217
|
/** Whether the user chose to type the route instead of picking it. */
|
|
365
218
|
var manualState = React.useState(false);
|
|
366
219
|
var manual = manualState[0];
|
|
367
220
|
var setManual = manualState[1];
|
|
368
221
|
|
|
369
|
-
if (props.view === "summary") return copy.summary;
|
|
370
|
-
|
|
371
222
|
var writable = snapshot.writable === true;
|
|
372
|
-
var current =
|
|
373
|
-
|
|
374
|
-
/** Record one field's draft, leaving the others untouched. */
|
|
375
|
-
function edit(key, value) {
|
|
376
|
-
setDraft(function (previous) {
|
|
377
|
-
var next = {};
|
|
378
|
-
for (var name in previous) if (has(previous, name)) next[name] = previous[name];
|
|
379
|
-
next[key] = value;
|
|
380
|
-
return next;
|
|
381
|
-
});
|
|
382
|
-
}
|
|
223
|
+
var current = snapshot && snapshot.value && typeof snapshot.value === "object" ? snapshot.value : {};
|
|
383
224
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
* to the previous provider and would otherwise be saved as a route the
|
|
389
|
-
* Host cannot resolve.
|
|
390
|
-
*/
|
|
391
|
-
function editMany(patch) {
|
|
392
|
-
setDraft(function (previous) {
|
|
393
|
-
var next = {};
|
|
394
|
-
for (var name in previous) if (has(previous, name)) next[name] = previous[name];
|
|
395
|
-
for (var key in patch) if (has(patch, key)) next[key] = patch[key];
|
|
396
|
-
return next;
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
var allFields = FIELDS.concat([
|
|
401
|
-
{ key: "visionProvider", label: "视觉 provider", kind: "text" },
|
|
402
|
-
{ key: "visionModel", label: "视觉 model", kind: "text" },
|
|
403
|
-
{ key: "visionReasoningEffort", label: "视觉 reasoning effort", kind: "text" },
|
|
404
|
-
]);
|
|
405
|
-
|
|
406
|
-
/** Whether a field currently carries a user override. */
|
|
407
|
-
function isOverridden(field) {
|
|
408
|
-
return overridden(snapshot, field.key);
|
|
409
|
-
}
|
|
225
|
+
var groups = catalog.value.groups || [];
|
|
226
|
+
var provider = typeof current.visionProvider === "string" ? current.visionProvider : "";
|
|
227
|
+
var model = typeof current.visionModel === "string" ? current.visionModel : "";
|
|
228
|
+
var selected = matchModel(groups, provider, model);
|
|
410
229
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
var edits = [];
|
|
415
|
-
var invalid = null;
|
|
416
|
-
allFields.forEach(function (field) {
|
|
417
|
-
if (!has(draft, field.key)) return;
|
|
418
|
-
var raw = draft[field.key];
|
|
419
|
-
if (raw === "") {
|
|
420
|
-
if (isOverridden(field)) edits.push({ field: field, raw: raw });
|
|
421
|
-
return;
|
|
422
|
-
}
|
|
423
|
-
var value;
|
|
424
|
-
try {
|
|
425
|
-
value = parse(field, raw);
|
|
426
|
-
} catch (cause) {
|
|
427
|
-
if (invalid === null) invalid = String((cause && cause.message) || cause);
|
|
428
|
-
return;
|
|
429
|
-
}
|
|
430
|
-
if (String(value) !== String(current[field.key])) edits.push({ field: field, raw: raw, value: value });
|
|
230
|
+
var providers = [];
|
|
231
|
+
groups.forEach(function (group) {
|
|
232
|
+
if (providers.indexOf(group.id) === -1) providers.push(group.id);
|
|
431
233
|
});
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
/** Persist every edit, in order, one write at a time. */
|
|
438
|
-
function save() {
|
|
439
|
-
if (!dirty || blocked) return;
|
|
440
|
-
setError(null);
|
|
441
|
-
setBusy(true);
|
|
442
|
-
var chain = Promise.resolve();
|
|
443
|
-
edits.forEach(function (item) {
|
|
444
|
-
chain = chain.then(function () {
|
|
445
|
-
return item.raw === "" ? scope.unset(item.field.key) : scope.set(item.field.key, item.value);
|
|
446
|
-
});
|
|
234
|
+
var modelsForProvider = [];
|
|
235
|
+
groups.forEach(function (group) {
|
|
236
|
+
if (group.id !== provider) return;
|
|
237
|
+
(group.models || []).forEach(function (entry) {
|
|
238
|
+
modelsForProvider.push(entry);
|
|
447
239
|
});
|
|
448
|
-
|
|
449
|
-
function () {
|
|
450
|
-
setBusy(false);
|
|
451
|
-
setDraft({});
|
|
452
|
-
},
|
|
453
|
-
function (cause) {
|
|
454
|
-
setBusy(false);
|
|
455
|
-
setError(String((cause && cause.message) || cause));
|
|
456
|
-
},
|
|
457
|
-
);
|
|
458
|
-
}
|
|
240
|
+
});
|
|
459
241
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
var keys = allFields
|
|
464
|
-
.filter(function (field) {
|
|
465
|
-
return isOverridden(field);
|
|
466
|
-
})
|
|
467
|
-
.map(function (field) {
|
|
468
|
-
return field.key;
|
|
469
|
-
});
|
|
470
|
-
if (keys.length === 0) return;
|
|
471
|
-
setError(null);
|
|
472
|
-
setDraft({});
|
|
473
|
-
setBusy(true);
|
|
474
|
-
var chain = Promise.resolve();
|
|
475
|
-
keys.forEach(function (key) {
|
|
476
|
-
chain = chain.then(function () {
|
|
477
|
-
return scope.unset(key);
|
|
478
|
-
});
|
|
479
|
-
});
|
|
480
|
-
chain.then(
|
|
481
|
-
function () {
|
|
482
|
-
setBusy(false);
|
|
483
|
-
},
|
|
484
|
-
function (cause) {
|
|
485
|
-
setBusy(false);
|
|
486
|
-
setError(String((cause && cause.message) || cause));
|
|
487
|
-
},
|
|
488
|
-
);
|
|
489
|
-
}
|
|
242
|
+
var routeLost = (provider !== "" || model !== "") && selected === null;
|
|
243
|
+
var pickable = catalog.value.status === "ready" && providers.length > 0;
|
|
244
|
+
var usePicker = pickable && !manual && !routeLost;
|
|
490
245
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
246
|
+
function renderControl(field) {
|
|
247
|
+
if (!usePicker) {
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
495
250
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
FIELDS.forEach(function (field) {
|
|
502
|
-
var disabled = !writable || busy;
|
|
503
|
-
var control;
|
|
504
|
-
if (field.kind === "select") {
|
|
505
|
-
control = React.createElement(
|
|
506
|
-
"select",
|
|
507
|
-
{
|
|
508
|
-
style: selectStyle,
|
|
509
|
-
value: rawOf(field.key),
|
|
510
|
-
disabled: disabled,
|
|
511
|
-
"aria-label": field.label,
|
|
512
|
-
onChange: function (event) {
|
|
513
|
-
edit(field.key, event.target.value);
|
|
514
|
-
},
|
|
515
|
-
},
|
|
516
|
-
field.options.map(function (option) {
|
|
517
|
-
return React.createElement("option", { key: option.value, value: option.value }, option.label);
|
|
518
|
-
}),
|
|
519
|
-
);
|
|
520
|
-
} else if (field.kind === "checkbox") {
|
|
521
|
-
control = React.createElement("input", {
|
|
522
|
-
type: "checkbox",
|
|
523
|
-
checked: rawOf(field.key) === "true",
|
|
524
|
-
disabled: disabled,
|
|
525
|
-
"aria-label": field.label,
|
|
526
|
-
onChange: function (event) {
|
|
527
|
-
edit(field.key, event.target.checked ? "true" : "false");
|
|
528
|
-
},
|
|
529
|
-
});
|
|
530
|
-
} else if (field.kind === "number") {
|
|
531
|
-
control = React.createElement("input", {
|
|
532
|
-
type: "number",
|
|
533
|
-
min: field.min,
|
|
534
|
-
max: field.max,
|
|
535
|
-
step: field.step,
|
|
536
|
-
style: inputStyle,
|
|
537
|
-
value: rawOf(field.key),
|
|
538
|
-
disabled: disabled,
|
|
251
|
+
if (field.key === "visionProvider") {
|
|
252
|
+
return React.createElement(UI.Select, {
|
|
253
|
+
value: provider,
|
|
254
|
+
disabled: !writable,
|
|
255
|
+
label: "视觉 provider",
|
|
539
256
|
placeholder: copy.inherit,
|
|
540
|
-
"
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
257
|
+
options: [{ value: "", label: copy.inherit }].concat(
|
|
258
|
+
providers.map(function (id) {
|
|
259
|
+
return { value: id, label: id };
|
|
260
|
+
}),
|
|
261
|
+
),
|
|
262
|
+
onChange: function (value) {
|
|
263
|
+
var ops = [
|
|
264
|
+
value === "" ? { op: "unset", path: ["visionProvider"] } : { op: "set", path: ["visionProvider"], value: value },
|
|
265
|
+
{ op: "unset", path: ["visionModel"] },
|
|
266
|
+
{ op: "unset", path: ["visionReasoningEffort"] },
|
|
267
|
+
];
|
|
268
|
+
void scope.mutate(ops, snapshot.revision);
|
|
546
269
|
},
|
|
547
270
|
});
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
value:
|
|
553
|
-
disabled:
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (field.key === "visionModel") {
|
|
274
|
+
return React.createElement(UI.Select, {
|
|
275
|
+
value: selected === null ? "" : model,
|
|
276
|
+
disabled: !writable || provider === "",
|
|
277
|
+
label: "视觉 model",
|
|
554
278
|
placeholder: copy.inherit,
|
|
555
|
-
"
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
279
|
+
options: [{ value: "", label: copy.inherit }].concat(
|
|
280
|
+
modelsForProvider.map(function (entry) {
|
|
281
|
+
return { value: entry.id, label: entry.name || entry.id };
|
|
282
|
+
}),
|
|
283
|
+
),
|
|
284
|
+
onChange: function (value) {
|
|
285
|
+
var ops = [
|
|
286
|
+
value === "" ? { op: "unset", path: ["visionModel"] } : { op: "set", path: ["visionModel"], value: value },
|
|
287
|
+
{ op: "unset", path: ["visionReasoningEffort"] },
|
|
288
|
+
];
|
|
289
|
+
void scope.mutate(ops, snapshot.revision);
|
|
561
290
|
},
|
|
562
291
|
});
|
|
563
292
|
}
|
|
564
293
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
294
|
+
if (field.key === "visionReasoningEffort") {
|
|
295
|
+
var hostEntry = matchModel(host.groups, provider, model);
|
|
296
|
+
var reasoning = (hostEntry && hostEntry.reasoning) || null;
|
|
297
|
+
var effortOptions = reasoning ? reasoning.efforts || [] : [];
|
|
298
|
+
if (effortOptions.length > 0) {
|
|
299
|
+
var currentEffort = typeof current.visionReasoningEffort === "string" ? current.visionReasoningEffort : "";
|
|
300
|
+
var effortValue = currentEffort || reasoning.defaultEffort || "";
|
|
301
|
+
return React.createElement(UI.Select, {
|
|
302
|
+
value: effortValue,
|
|
303
|
+
disabled: !writable,
|
|
304
|
+
label: "视觉 reasoning effort",
|
|
305
|
+
placeholder: copy.inherit,
|
|
306
|
+
options: [{ value: "", label: copy.inherit }].concat(
|
|
307
|
+
effortOptions.map(function (effort) {
|
|
308
|
+
return { value: effort.id, label: effort.name || effort.id };
|
|
309
|
+
}),
|
|
310
|
+
),
|
|
311
|
+
onChange: function (value) {
|
|
312
|
+
var ops = [
|
|
313
|
+
value === ""
|
|
314
|
+
? { op: "unset", path: ["visionReasoningEffort"] }
|
|
315
|
+
: { op: "set", path: ["visionReasoningEffort"], value: value },
|
|
316
|
+
];
|
|
317
|
+
void scope.mutate(ops, snapshot.revision);
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
if (reasoning) {
|
|
322
|
+
return React.createElement("div", { className: "dsp-field-hint" }, copy.effortNone);
|
|
323
|
+
}
|
|
324
|
+
return React.createElement(UI.Select, {
|
|
325
|
+
value: "",
|
|
326
|
+
disabled: true,
|
|
327
|
+
label: "视觉 reasoning effort",
|
|
328
|
+
placeholder: copy.inherit,
|
|
329
|
+
options: [{ value: "", label: copy.inherit }],
|
|
330
|
+
onChange: function () {},
|
|
331
|
+
});
|
|
576
332
|
}
|
|
577
|
-
});
|
|
578
|
-
|
|
579
|
-
children.push(
|
|
580
|
-
React.createElement("hr", {
|
|
581
|
-
key: "route-sep",
|
|
582
|
-
style: { border: "none", borderTop: "1px solid var(--dsw-alias-border-l2)", margin: "4px 0" },
|
|
583
|
-
}),
|
|
584
|
-
);
|
|
585
|
-
children.push(React.createElement("div", { key: "route-heading", style: headingStyle }, "视觉模型路由"));
|
|
586
|
-
children.push(
|
|
587
|
-
React.createElement(
|
|
588
|
-
"div",
|
|
589
|
-
{ key: "route-hint", style: hintStyle },
|
|
590
|
-
"每步决策用哪个模型看图。全空 = 用会话当前模型;会话模型不支持图像时自动走 AX 元素表。",
|
|
591
|
-
),
|
|
592
|
-
);
|
|
593
333
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
var model = rawOf("visionModel");
|
|
597
|
-
var selected = matchModel(groups, provider, model);
|
|
598
|
-
var providers = [];
|
|
599
|
-
groups.forEach(function (group) {
|
|
600
|
-
if (providers.indexOf(group.id) === -1) providers.push(group.id);
|
|
601
|
-
});
|
|
602
|
-
var modelsForProvider = [];
|
|
603
|
-
groups.forEach(function (group) {
|
|
604
|
-
if (group.id !== provider) return;
|
|
605
|
-
(group.models || []).forEach(function (entry) {
|
|
606
|
-
modelsForProvider.push(entry);
|
|
607
|
-
});
|
|
608
|
-
});
|
|
334
|
+
return undefined;
|
|
335
|
+
}
|
|
609
336
|
|
|
610
|
-
|
|
611
|
-
// available. A stored route the catalog no longer offers (a provider that
|
|
612
|
-
// failed to load, or a model removed upstream) is never silently
|
|
613
|
-
// rewritten: the card falls back to manual entry and says so.
|
|
614
|
-
var pickable = catalog.value.status === "ready" && providers.length > 0;
|
|
615
|
-
var usePicker = pickable && !manual;
|
|
337
|
+
var children = [];
|
|
616
338
|
|
|
617
339
|
if (catalog.value.status === "loading") {
|
|
618
|
-
children.push(React.createElement(
|
|
340
|
+
children.push(React.createElement(UI.Banner, { key: "catalog-status", kind: "info" }, copy.catalogLoading));
|
|
619
341
|
} else if (catalog.value.status === "error") {
|
|
620
|
-
children.push(React.createElement(
|
|
342
|
+
children.push(React.createElement(UI.Banner, { key: "catalog-status", kind: "error" }, copy.catalogError));
|
|
621
343
|
} else if (providers.length === 0) {
|
|
622
|
-
children.push(React.createElement(
|
|
344
|
+
children.push(React.createElement(UI.Banner, { key: "catalog-status", kind: "info" }, copy.catalogEmpty));
|
|
623
345
|
} else if (catalog.value.failures > 0) {
|
|
624
|
-
children.push(React.createElement(
|
|
346
|
+
children.push(React.createElement(UI.Banner, { key: "catalog-status", kind: "warn" }, copy.catalogPartial));
|
|
625
347
|
}
|
|
626
348
|
|
|
627
|
-
if (
|
|
628
|
-
children.push(
|
|
629
|
-
React.createElement(
|
|
630
|
-
"div",
|
|
631
|
-
{ key: "route-provider", style: fieldStyle },
|
|
632
|
-
React.createElement("span", { style: labelStyle }, "provider"),
|
|
633
|
-
React.createElement(
|
|
634
|
-
"select",
|
|
635
|
-
{
|
|
636
|
-
style: selectStyle,
|
|
637
|
-
value: provider,
|
|
638
|
-
disabled: !writable || busy,
|
|
639
|
-
"aria-label": "视觉 provider",
|
|
640
|
-
onChange: function (event) {
|
|
641
|
-
// The previous model and effort belong to the previous
|
|
642
|
-
// provider, so they are cleared together with it.
|
|
643
|
-
editMany({ visionProvider: event.target.value, visionModel: "", visionReasoningEffort: "" });
|
|
644
|
-
},
|
|
645
|
-
},
|
|
646
|
-
[React.createElement("option", { key: "", value: "" }, copy.inherit)].concat(
|
|
647
|
-
providers.map(function (id) {
|
|
648
|
-
return React.createElement("option", { key: id, value: id }, id);
|
|
649
|
-
}),
|
|
650
|
-
),
|
|
651
|
-
),
|
|
652
|
-
overridden(snapshot, "visionProvider")
|
|
653
|
-
? React.createElement("span", { style: badgeStyle }, copy.overridden)
|
|
654
|
-
: null,
|
|
655
|
-
),
|
|
656
|
-
);
|
|
657
|
-
children.push(
|
|
658
|
-
React.createElement(
|
|
659
|
-
"div",
|
|
660
|
-
{ key: "route-model", style: fieldStyle },
|
|
661
|
-
React.createElement("span", { style: labelStyle }, "model"),
|
|
662
|
-
React.createElement(
|
|
663
|
-
"select",
|
|
664
|
-
{
|
|
665
|
-
style: selectStyle,
|
|
666
|
-
value: selected === null ? "" : model,
|
|
667
|
-
disabled: !writable || busy || provider === "",
|
|
668
|
-
"aria-label": "视觉 model",
|
|
669
|
-
onChange: function (event) {
|
|
670
|
-
editMany({ visionModel: event.target.value, visionReasoningEffort: "" });
|
|
671
|
-
},
|
|
672
|
-
},
|
|
673
|
-
[React.createElement("option", { key: "", value: "" }, copy.inherit)].concat(
|
|
674
|
-
modelsForProvider.map(function (entry) {
|
|
675
|
-
return React.createElement("option", { key: entry.id, value: entry.id }, entry.name || entry.id);
|
|
676
|
-
}),
|
|
677
|
-
),
|
|
678
|
-
),
|
|
679
|
-
overridden(snapshot, "visionModel") ? React.createElement("span", { style: badgeStyle }, copy.overridden) : null,
|
|
680
|
-
),
|
|
681
|
-
);
|
|
682
|
-
|
|
683
|
-
// The effort list comes from the HOST catalog, which does carry it; the
|
|
684
|
-
// vision route above only decides which models may appear at all.
|
|
685
|
-
var hostEntry = matchModel(host.groups, provider, model);
|
|
686
|
-
var reasoning = (hostEntry && hostEntry.reasoning) || null;
|
|
687
|
-
var effortOptions = reasoning ? reasoning.efforts || [] : [];
|
|
688
|
-
if (effortOptions.length > 0) {
|
|
689
|
-
var effortValue = rawOf("visionReasoningEffort") || reasoning.defaultEffort || "";
|
|
690
|
-
children.push(
|
|
691
|
-
React.createElement(
|
|
692
|
-
"div",
|
|
693
|
-
{ key: "route-effort", style: fieldStyle },
|
|
694
|
-
React.createElement("span", { style: labelStyle }, "reasoning effort"),
|
|
695
|
-
React.createElement(
|
|
696
|
-
"select",
|
|
697
|
-
{
|
|
698
|
-
style: selectStyle,
|
|
699
|
-
value: effortValue,
|
|
700
|
-
disabled: !writable || busy,
|
|
701
|
-
"aria-label": "视觉 reasoning effort",
|
|
702
|
-
onChange: function (event) {
|
|
703
|
-
edit("visionReasoningEffort", event.target.value);
|
|
704
|
-
},
|
|
705
|
-
},
|
|
706
|
-
[React.createElement("option", { key: "", value: "" }, copy.inherit)].concat(
|
|
707
|
-
effortOptions.map(function (effort) {
|
|
708
|
-
return React.createElement("option", { key: effort.id, value: effort.id }, effort.name || effort.id);
|
|
709
|
-
}),
|
|
710
|
-
),
|
|
711
|
-
),
|
|
712
|
-
overridden(snapshot, "visionReasoningEffort")
|
|
713
|
-
? React.createElement("span", { style: badgeStyle }, copy.overridden)
|
|
714
|
-
: null,
|
|
715
|
-
),
|
|
716
|
-
);
|
|
717
|
-
} else if (reasoning) {
|
|
718
|
-
children.push(
|
|
719
|
-
React.createElement("div", { key: "route-effort", style: hintStyle }, "reasoning effort:" + copy.effortNone),
|
|
720
|
-
);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
if (selected === null && (provider !== "" || model !== "")) {
|
|
724
|
-
children.push(React.createElement("div", { key: "route-missing", style: hintStyle }, copy.routeMissing));
|
|
725
|
-
}
|
|
726
|
-
} else {
|
|
727
|
-
// Manual entry, also the fallback when the catalog failed: the card must
|
|
728
|
-
// stay usable when a provider is unreachable.
|
|
729
|
-
ROUTE_KEYS.forEach(function (key) {
|
|
730
|
-
var label = key === "visionProvider" ? "provider" : key === "visionModel" ? "model" : "reasoning effort";
|
|
731
|
-
children.push(
|
|
732
|
-
React.createElement(
|
|
733
|
-
"div",
|
|
734
|
-
{ key: "route-" + key, style: fieldStyle },
|
|
735
|
-
React.createElement("span", { style: labelStyle }, label),
|
|
736
|
-
React.createElement("input", {
|
|
737
|
-
type: "text",
|
|
738
|
-
style: textStyle,
|
|
739
|
-
value: rawOf(key),
|
|
740
|
-
disabled: !writable || busy,
|
|
741
|
-
placeholder: copy.inherit,
|
|
742
|
-
"aria-label": "视觉 " + label,
|
|
743
|
-
onChange: function (event) {
|
|
744
|
-
edit(key, event.target.value);
|
|
745
|
-
},
|
|
746
|
-
onKeyDown: function (event) {
|
|
747
|
-
if (event.key === "Enter") save();
|
|
748
|
-
},
|
|
749
|
-
}),
|
|
750
|
-
overridden(snapshot, key) ? React.createElement("span", { style: badgeStyle }, copy.overridden) : null,
|
|
751
|
-
),
|
|
752
|
-
);
|
|
753
|
-
});
|
|
349
|
+
if (catalog.value.status === "ready" && routeLost) {
|
|
350
|
+
children.push(React.createElement(UI.Banner, { key: "route-missing", kind: "warn" }, copy.routeMissing));
|
|
754
351
|
}
|
|
755
352
|
|
|
756
353
|
if (pickable) {
|
|
757
354
|
children.push(
|
|
758
355
|
React.createElement(
|
|
759
356
|
"div",
|
|
760
|
-
{ key: "route-mode", style:
|
|
357
|
+
{ key: "route-mode", style: { display: "flex", alignItems: "center", gap: "8px" } },
|
|
761
358
|
React.createElement(
|
|
762
|
-
|
|
359
|
+
UI.Button,
|
|
763
360
|
{
|
|
764
|
-
|
|
765
|
-
|
|
361
|
+
variant: "secondary",
|
|
362
|
+
size: "sm",
|
|
766
363
|
onClick: function () {
|
|
767
364
|
setManual(!manual);
|
|
768
365
|
},
|
|
@@ -773,42 +370,26 @@ window.__ModuleLoader__.load({
|
|
|
773
370
|
);
|
|
774
371
|
}
|
|
775
372
|
|
|
776
|
-
children.push(
|
|
373
|
+
children.push(
|
|
374
|
+
React.createElement("div", { key: "route-fallback", className: "dsp-field-hint" }, copy.catalogFallback),
|
|
375
|
+
);
|
|
777
376
|
|
|
778
|
-
if (invalid !== null) children.push(React.createElement("div", { key: "invalid", style: errorStyle }, invalid));
|
|
779
|
-
if (error !== null) children.push(React.createElement("div", { key: "error", style: errorStyle }, error));
|
|
780
377
|
if (!writable && snapshot.status === "ready") {
|
|
781
|
-
children.push(React.createElement(
|
|
378
|
+
children.push(React.createElement(UI.Banner, { key: "unwritable", kind: "warn" }, copy.unwritable));
|
|
782
379
|
}
|
|
783
380
|
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
},
|
|
796
|
-
busy ? copy.saving : copy.save,
|
|
797
|
-
),
|
|
798
|
-
React.createElement(
|
|
799
|
-
"button",
|
|
800
|
-
{
|
|
801
|
-
type: "button",
|
|
802
|
-
style: buttonStyle,
|
|
803
|
-
disabled: !writable || busy || !anyOverridden,
|
|
804
|
-
onClick: reset,
|
|
805
|
-
},
|
|
806
|
-
copy.reset,
|
|
807
|
-
),
|
|
808
|
-
),
|
|
381
|
+
return React.createElement(
|
|
382
|
+
UI.ConfigForm,
|
|
383
|
+
{
|
|
384
|
+
namespace: NS,
|
|
385
|
+
form: scope,
|
|
386
|
+
describe: describe,
|
|
387
|
+
title: copy.heading,
|
|
388
|
+
description: copy.intro,
|
|
389
|
+
renderControl: renderControl,
|
|
390
|
+
},
|
|
391
|
+
children,
|
|
809
392
|
);
|
|
810
|
-
|
|
811
|
-
return React.createElement("div", { style: wrapStyle }, children);
|
|
812
393
|
}
|
|
813
394
|
|
|
814
395
|
/**
|
|
@@ -821,11 +402,16 @@ window.__ModuleLoader__.load({
|
|
|
821
402
|
* just for this card. Nested, a host without the settings UI loses only this
|
|
822
403
|
* card.
|
|
823
404
|
*
|
|
405
|
+
* The nested context inherits the services declared here (`slots`,
|
|
406
|
+
* `remote`, `remote.session`), so the card is handed the scoped context and
|
|
407
|
+
* can reach both the form and the model directory through it.
|
|
408
|
+
*
|
|
824
409
|
* @param ctx - client root context.
|
|
825
410
|
*/
|
|
826
411
|
function apply(ctx) {
|
|
827
412
|
ctx.inject(["configForms"], function (scoped) {
|
|
828
413
|
var scope = scoped.configForms.get(NS);
|
|
414
|
+
var describe = scoped.configForms.describe();
|
|
829
415
|
BUNDLE_NAMES.forEach(function (bundle) {
|
|
830
416
|
scoped.slots.inject("plugins.row.config", function () {
|
|
831
417
|
return scoped.slots.register(
|
|
@@ -833,7 +419,7 @@ window.__ModuleLoader__.load({
|
|
|
833
419
|
name: "plugins.row.config",
|
|
834
420
|
key: bundle + "#" + ROW_ID,
|
|
835
421
|
inject: function () {
|
|
836
|
-
return { scope: scope, ctx: scoped };
|
|
422
|
+
return { scope: scope, describe: describe, ctx: scoped };
|
|
837
423
|
},
|
|
838
424
|
},
|
|
839
425
|
DesktopAgentSettingsCard,
|
package/lib/config.js
CHANGED
|
@@ -76,13 +76,52 @@ export const DELIVERY_MODES = ['background', 'foreground'];
|
|
|
76
76
|
* opt into, never a default this plugin picks on their behalf.
|
|
77
77
|
*/
|
|
78
78
|
export const Config = z.object({
|
|
79
|
-
visionProvider: z
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
visionProvider: z
|
|
80
|
+
.string()
|
|
81
|
+
.default('')
|
|
82
|
+
.comment('视觉 provider')
|
|
83
|
+
.description('每步决策用哪个模型看图。全空 = 用会话当前模型;会话模型不支持图像时自动走 AX 元素表。')
|
|
84
|
+
.volatile(),
|
|
85
|
+
visionModel: z
|
|
86
|
+
.string()
|
|
87
|
+
.default('')
|
|
88
|
+
.comment('视觉 model')
|
|
89
|
+
.description('')
|
|
90
|
+
.volatile(),
|
|
91
|
+
visionReasoningEffort: z
|
|
92
|
+
.string()
|
|
93
|
+
.default('')
|
|
94
|
+
.comment('视觉 reasoning effort')
|
|
95
|
+
.description('')
|
|
96
|
+
.volatile(),
|
|
97
|
+
maxSteps: z
|
|
98
|
+
.number()
|
|
99
|
+
.step(1)
|
|
100
|
+
.min(1)
|
|
101
|
+
.default(DEFAULT_MAX_STEPS)
|
|
102
|
+
.comment('步数上限')
|
|
103
|
+
.description('一次任务最多执行多少个动作,默认 40。')
|
|
104
|
+
.volatile(),
|
|
105
|
+
maxImageDimension: z
|
|
106
|
+
.number()
|
|
107
|
+
.step(1)
|
|
108
|
+
.min(200)
|
|
109
|
+
.default(1568)
|
|
110
|
+
.comment('截图长边上限')
|
|
111
|
+
.description('送给模型的截图最长边(像素),默认 1568。调小可省 token,但太小会看不清控件。')
|
|
112
|
+
.volatile(),
|
|
113
|
+
deliveryMode: z
|
|
114
|
+
.union(DELIVERY_MODES)
|
|
115
|
+
.default('background')
|
|
116
|
+
.comment('投递方式')
|
|
117
|
+
.description('后台投递不抢焦点;游戏等自绘表面会过滤按 pid 路由的事件,需要前台投递。')
|
|
118
|
+
.volatile(),
|
|
119
|
+
allowBringToFront: z
|
|
120
|
+
.boolean()
|
|
121
|
+
.default(false)
|
|
122
|
+
.comment('允许抢前台')
|
|
123
|
+
.description('关闭时模型不能使用 bring_to_front。开启后模型可把目标窗口提到最前,代价是抢走你当前的焦点;仅当窗口被压在后台、内容不刷新时才需要。')
|
|
124
|
+
.volatile(),
|
|
86
125
|
});
|
|
87
126
|
|
|
88
127
|
/**
|
package/lib/driver.js
CHANGED
|
@@ -161,6 +161,21 @@ async function publishTools(ctx, driver, lifetime, pending) {
|
|
|
161
161
|
}));
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
// These schemas are what makes the prompt expensive and the model no better:
|
|
165
|
+
// measured, they are 109,857 characters of a 220,021-character first request,
|
|
166
|
+
// and the 1,864-session log holds 509 model-direct calls against them, spread
|
|
167
|
+
// over 27 sessions — 25 of which never used `desktop_agent` at all. The work
|
|
168
|
+
// goes through `desktop_agent`, whose sub-dispatches carry a `parent` token
|
|
169
|
+
// and are resolved from the registry's own layers, never from the prompt. So
|
|
170
|
+
// the announcement is withdrawn while the definitions stay registered and
|
|
171
|
+
// executable: `resolveExecution` consults the layer visibility, not the
|
|
172
|
+
// assembly, so a model that names one anyway still gets it executed.
|
|
173
|
+
ctx.on('system-prompt/assemble', async (_assembly, _context, next) => {
|
|
174
|
+
const assembly = await next();
|
|
175
|
+
const kept = assembly.tools.filter((tool) => !names.has(tool.name));
|
|
176
|
+
return kept.length === assembly.tools.length ? assembly : { ...assembly, tools: kept };
|
|
177
|
+
});
|
|
178
|
+
|
|
164
179
|
const systemPrompt = ctx.get('systemPrompt');
|
|
165
180
|
if (systemPrompt === null || systemPrompt === undefined) {
|
|
166
181
|
ctx.logger?.warn?.('desktop-agent: the systemPrompt service is unavailable; the Cua Driver guidance was not registered.');
|
package/lib/index.js
CHANGED
|
@@ -154,10 +154,8 @@ export async function apply(ctx, config, loadSdk) {
|
|
|
154
154
|
'asks the configured model what to do, and performs the click, key, or text entry it chose. ' +
|
|
155
155
|
'Works on ordinary applications and on games that expose no usable accessibility tree, because it reads the ' +
|
|
156
156
|
'picture rather than the element table. ' +
|
|
157
|
-
'\n\nPREFER THIS
|
|
158
|
-
'
|
|
159
|
-
'this conversation; driving those tools by hand puts every screenshot into the context instead. Reach for the ' +
|
|
160
|
-
'raw tools only to resolve a window, or for a single action you have already decided on. ' +
|
|
157
|
+
'\n\nPREFER THIS for any desktop task. It runs the whole loop inside one call, so its 40 screenshots ' +
|
|
158
|
+
'and decisions never enter this conversation. ' +
|
|
161
159
|
'\n\nChoose this for native windows and desktop applications. For a task that lives in a web page, ' +
|
|
162
160
|
'browser_agent is the better tool: it drives the DOM over CDP and is far more precise than a screenshot. ' +
|
|
163
161
|
'Note that a covered window cannot be driven, and a game that filters injected input needs foreground ' +
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logictan/dsh-desktop-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "DSH desktop agent: a native-vision decision loop that drives the host desktop through the Cua Driver tools. DSH 桌面 Agent 子插件。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
"patch": "./cordis.patch.yml"
|
|
15
15
|
},
|
|
16
16
|
"client": {
|
|
17
|
+
"external": [
|
|
18
|
+
"@logictan/dsh-plugins-shared-client"
|
|
19
|
+
],
|
|
17
20
|
"inject": [
|
|
18
21
|
"slots",
|
|
19
22
|
"remote",
|
|
@@ -59,7 +62,8 @@
|
|
|
59
62
|
"@deepseek-ai/dsh-system-prompt": "^0.1.5-alpha.1 || ^0.1.6-alpha.1 || ^0.1.7-alpha.1 || ^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.0-rc.2",
|
|
60
63
|
"@deepseek-ai/dsh-mcp-client": "^0.1.5-alpha.1 || ^0.1.6-alpha.1 || ^0.1.7-alpha.1 || ^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.0-rc.2",
|
|
61
64
|
"@deepseek-ai/dsh-tools": "^0.1.5-alpha.1 || ^0.1.6-alpha.1 || ^0.1.7-alpha.1 || ^0.1.7-rc.1 || ^0.2.0-rc.1 || ^0.2.0-rc.2",
|
|
62
|
-
"@deepseek-ai/schemastery": "^3.18.3"
|
|
65
|
+
"@deepseek-ai/schemastery": "^3.18.3",
|
|
66
|
+
"@logictan/dsh-plugins-shared-client": "^0.1.0"
|
|
63
67
|
},
|
|
64
68
|
"peerDependenciesMeta": {
|
|
65
69
|
"@deepseek-ai/cordis": {
|
|
@@ -82,6 +86,9 @@
|
|
|
82
86
|
},
|
|
83
87
|
"@deepseek-ai/schemastery": {
|
|
84
88
|
"optional": true
|
|
89
|
+
},
|
|
90
|
+
"@logictan/dsh-plugins-shared-client": {
|
|
91
|
+
"optional": true
|
|
85
92
|
}
|
|
86
93
|
},
|
|
87
94
|
"dependencies": {
|