@qwen-code/qwen-code 0.16.2 → 0.17.0

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.
Files changed (73) hide show
  1. package/bundled/qc-helper/docs/configuration/settings.md +5 -1
  2. package/bundled/qc-helper/docs/features/channels/_meta.ts +1 -0
  3. package/bundled/qc-helper/docs/features/channels/feishu.md +170 -0
  4. package/chunks/{agent-RY5EB3XR.js → agent-KVXFGIOU.js} +17 -12
  5. package/chunks/{anthropicContentGenerator-LYI3OHBB.js → anthropicContentGenerator-L4HWAOIV.js} +5 -5
  6. package/chunks/{askUserQuestion-R3MKD2JT.js → askUserQuestion-DC6OWQIL.js} +5 -3
  7. package/chunks/chunk-24YKA2DA.js +233 -0
  8. package/chunks/{chunk-UFC57OYT.js → chunk-33RDTIU6.js} +5 -3
  9. package/chunks/{chunk-C6WMLUNB.js → chunk-3BJBCG2K.js} +1 -1
  10. package/chunks/chunk-3HX5LZ6R.js +1798 -0
  11. package/chunks/{chunk-GQXXO5HJ.js → chunk-4O2TWJK4.js} +25 -5
  12. package/chunks/{chunk-RDYWTWEM.js → chunk-5IFG2VC4.js} +299 -232
  13. package/chunks/chunk-6RQTH7UQ.js +115 -0
  14. package/chunks/{chunk-7HM6OB7M.js → chunk-7EHPK6TK.js} +1336 -5164
  15. package/chunks/{chunk-PJLEMR7N.js → chunk-7NNBQRV7.js} +6 -6
  16. package/chunks/{chunk-K5PGHDBN.js → chunk-AKBFRR6J.js} +112 -132
  17. package/chunks/{chunk-C27V5A2J.js → chunk-EMVEDSVZ.js} +1 -1
  18. package/chunks/{chunk-TXQI3VZ7.js → chunk-GJHMAWS7.js} +1 -1
  19. package/chunks/{chunk-K72FHBFO.js → chunk-HAQCNXSG.js} +1 -0
  20. package/chunks/{chunk-T4VD6OJ4.js → chunk-HCSJIOLR.js} +1 -1
  21. package/chunks/chunk-J37FGIOA.js +1623 -0
  22. package/chunks/chunk-J5VCSWPA.js +1467 -0
  23. package/chunks/{chunk-UE5LPQF7.js → chunk-JI7FDD65.js} +7 -7
  24. package/chunks/{chunk-4YNZFYJY.js → chunk-MEN6IEKX.js} +2533 -3943
  25. package/chunks/{chunk-USE2VQ5P.js → chunk-NP3ICQCN.js} +1 -1
  26. package/chunks/{chunk-66CJCYYZ.js → chunk-QEXSIXLX.js} +1 -1
  27. package/chunks/{chunk-YMDXEEOW.js → chunk-R2B65CAN.js} +1 -82
  28. package/chunks/chunk-SZOEIL6S.js +35 -0
  29. package/chunks/chunk-TI4GXJKO.js +4277 -0
  30. package/chunks/{chunk-E7E2MFYM.js → chunk-U2K6HDUJ.js} +434 -13
  31. package/chunks/{chunk-FO7BIVSR.js → chunk-UQRYJQBE.js} +5 -3
  32. package/chunks/{chunk-VMOWXTRC.js → chunk-V7LMZR76.js} +1 -1
  33. package/chunks/chunk-W57YDFU5.js +41 -0
  34. package/chunks/computer-use-2J5ZXEER.js +768 -0
  35. package/chunks/{contextCommand-DDGVLQSF.js → contextCommand-52NTEMCT.js} +19 -14
  36. package/chunks/{cron-create-BTEOGHPH.js → cron-create-FXRORK2U.js} +5 -3
  37. package/chunks/{cron-delete-56CEWELN.js → cron-delete-D24IN6CA.js} +5 -3
  38. package/chunks/{cron-list-SV6QRZW2.js → cron-list-SMOX26SL.js} +5 -3
  39. package/chunks/{devtools-WN62SQPV.js → devtools-IXE4UP72.js} +31 -3748
  40. package/chunks/{dist-MN2PDDPR.js → dist-6RUZ2JD6.js} +13 -1612
  41. package/chunks/{dist-R2SXPG74.js → dist-AHZNZWRI.js} +5 -4
  42. package/chunks/dist-GRQVFL3G.js +94147 -0
  43. package/chunks/{dist-BXDUQ2QY.js → dist-XTTPOFAH.js} +4 -3
  44. package/chunks/{edit-4LLGNYVZ.js → edit-RLFUTT5F.js} +20 -14
  45. package/chunks/{enter-worktree-E2R5XAFT.js → enter-worktree-CYRAPQKJ.js} +20 -14
  46. package/chunks/{exit-worktree-YVBYYYDD.js → exit-worktree-WQZM72QD.js} +20 -14
  47. package/chunks/{exitPlanMode-WD5IH7NS.js → exitPlanMode-STFEBQZE.js} +20 -14
  48. package/chunks/{geminiContentGenerator-LM65ADWM.js → geminiContentGenerator-DIV32SKO.js} +4 -3
  49. package/chunks/{glob-6X6OCEWE.js → glob-N3XO4RVI.js} +20 -14
  50. package/chunks/{grep-2UUPSSIQ.js → grep-AK5MP7P3.js} +20 -14
  51. package/chunks/{ls-MYXAM7LJ.js → ls-7FYQHPWF.js} +5 -3
  52. package/chunks/{lsp-PFGI35JL.js → lsp-DKG34USR.js} +5 -3
  53. package/chunks/{monitor-VUHPEGUW.js → monitor-IVBWJZEZ.js} +20 -14
  54. package/chunks/{notebook-edit-P4QVLW6I.js → notebook-edit-PM46AXFS.js} +21 -15
  55. package/chunks/{openaiContentGenerator-JH4YNZ3H.js → openaiContentGenerator-4QXCH7L2.js} +13 -10
  56. package/chunks/{qwenContentGenerator-5FE4UYUT.js → qwenContentGenerator-BLXQIIMX.js} +21 -16
  57. package/chunks/{read-file-J7DH4OKV.js → read-file-IEQAS3EZ.js} +10 -6
  58. package/chunks/{ripGrep-33DECY4F.js → ripGrep-HQO7IE4C.js} +17 -12
  59. package/chunks/{send-message-JUFP62VD.js → send-message-ZL7CDM7K.js} +5 -3
  60. package/chunks/{serve-7FX7MREA.js → serve-CYRAK4UM.js} +63 -1837
  61. package/chunks/{shell-ZNTQIRK6.js → shell-UZBGNO2Q.js} +17 -12
  62. package/chunks/{skill-CFCUIY23.js → skill-JVC34QYN.js} +24 -13
  63. package/chunks/{src-AHV2CWEQ.js → src-FOODLH7B.js} +49 -41
  64. package/chunks/{syntheticOutput-AKTXC6FR.js → syntheticOutput-U3YJ3GOO.js} +3 -2
  65. package/chunks/{task-stop-2NYFR2ES.js → task-stop-NPUI3YBA.js} +5 -3
  66. package/chunks/{todoWrite-WHZ2O2XP.js → todoWrite-Y6F7YEIM.js} +6 -4
  67. package/chunks/{tool-search-C2EMLFBJ.js → tool-search-P7PRPOW3.js} +13 -8
  68. package/chunks/{web-fetch-S6MZXPZ5.js → web-fetch-XWEK4TFX.js} +6 -4
  69. package/chunks/{write-file-EEPVRS4Q.js → write-file-SIIEUON5.js} +21 -15
  70. package/cli.js +412 -329
  71. package/package.json +2 -2
  72. package/chunks/chunk-JVD46YJV.js +0 -434
  73. package/chunks/undici-4ARNOH74.js +0 -8
@@ -0,0 +1,768 @@
1
+ // Force strict mode and setup for ESM
2
+ "use strict";
3
+ import {
4
+ Client,
5
+ StdioClientTransport
6
+ } from "./chunk-TI4GXJKO.js";
7
+ import "./chunk-AKBFRR6J.js";
8
+ import {
9
+ safeJsonStringify
10
+ } from "./chunk-W57YDFU5.js";
11
+ import {
12
+ BaseDeclarativeTool,
13
+ BaseToolInvocation
14
+ } from "./chunk-R2B65CAN.js";
15
+ import "./chunk-ACBGEKB7.js";
16
+ import "./chunk-QWSRH265.js";
17
+ import {
18
+ init_esbuild_shims
19
+ } from "./chunk-A4BMJM77.js";
20
+ import {
21
+ __name
22
+ } from "./chunk-J2S4EL5Y.js";
23
+
24
+ // packages/core/src/tools/computer-use/index.ts
25
+ init_esbuild_shims();
26
+
27
+ // packages/core/src/tools/computer-use/tool.ts
28
+ init_esbuild_shims();
29
+
30
+ // packages/core/src/tools/computer-use/client.ts
31
+ init_esbuild_shims();
32
+
33
+ // packages/core/src/tools/computer-use/constants.ts
34
+ init_esbuild_shims();
35
+ var PINNED_OPEN_COMPUTER_USE_VERSION = "0.1.51";
36
+ function resolveComputerUsePackageSpec() {
37
+ return process.env["QWEN_COMPUTER_USE_PACKAGE"] ?? `open-computer-use@${PINNED_OPEN_COMPUTER_USE_VERSION}`;
38
+ }
39
+ __name(resolveComputerUsePackageSpec, "resolveComputerUsePackageSpec");
40
+
41
+ // packages/core/src/tools/computer-use/client.ts
42
+ var ComputerUseClient = class _ComputerUseClient {
43
+ static {
44
+ __name(this, "ComputerUseClient");
45
+ }
46
+ static singleton;
47
+ packageSpec;
48
+ onProgress;
49
+ client;
50
+ startPromise;
51
+ constructor(options) {
52
+ this.packageSpec = options.packageSpec;
53
+ this.onProgress = options.onProgress ?? (() => {
54
+ });
55
+ }
56
+ /**
57
+ * Shared singleton instance, created with default options on first
58
+ * access. Tests can replace it via `setSharedForTest()`.
59
+ */
60
+ static shared() {
61
+ if (!_ComputerUseClient.singleton) {
62
+ _ComputerUseClient.singleton = new _ComputerUseClient({
63
+ packageSpec: resolveComputerUsePackageSpec()
64
+ });
65
+ }
66
+ return _ComputerUseClient.singleton;
67
+ }
68
+ /** Test-only: replace the singleton. */
69
+ static setSharedForTest(replacement) {
70
+ _ComputerUseClient.singleton = replacement;
71
+ }
72
+ isStarted() {
73
+ return this.client !== void 0;
74
+ }
75
+ /**
76
+ * Start the upstream MCP server. Idempotent: concurrent callers share
77
+ * the same in-flight start promise.
78
+ *
79
+ * An optional `onProgress` callback can be supplied to receive download
80
+ * and startup messages during this call. It overrides the instance-level
81
+ * callback for the duration of the start operation only.
82
+ *
83
+ * Throws on spawn failure (network down, npx missing, etc.). The
84
+ * caller (bootstrap state machine) is responsible for mapping the
85
+ * throw into user-facing UX.
86
+ */
87
+ async start(onProgress) {
88
+ if (this.client) return;
89
+ if (this.startPromise) return this.startPromise;
90
+ this.startPromise = this.doStart(onProgress).finally(() => {
91
+ this.startPromise = void 0;
92
+ });
93
+ return this.startPromise;
94
+ }
95
+ async doStart(onProgress) {
96
+ const progress = onProgress ?? this.onProgress;
97
+ progress("Starting Computer Use...");
98
+ const downloadHintTimer = setTimeout(() => {
99
+ progress(
100
+ "Downloading Computer Use binary (this can take ~60s on first use)..."
101
+ );
102
+ }, 3e3);
103
+ try {
104
+ const transport = new StdioClientTransport({
105
+ command: "npx",
106
+ args: ["-y", this.packageSpec, "mcp"],
107
+ // Inherit env so HTTPS_PROXY etc. flow through to npx
108
+ env: { ...process.env }
109
+ });
110
+ const client = new Client(
111
+ { name: "qwen-code-computer-use", version: "1.0.0" },
112
+ { capabilities: {} }
113
+ );
114
+ await client.connect(transport);
115
+ this.client = client;
116
+ } finally {
117
+ clearTimeout(downloadHintTimer);
118
+ }
119
+ }
120
+ /**
121
+ * List the tools exposed by the upstream server. Used by the schema
122
+ * sync script and bootstrap diagnostics.
123
+ */
124
+ async listTools() {
125
+ if (!this.client) throw new Error("ComputerUseClient not started");
126
+ return this.client.listTools();
127
+ }
128
+ /**
129
+ * Call a tool by upstream name (NOT the qwen-code-facing
130
+ * `computer_use__` prefixed name). Returns the raw MCP result so the
131
+ * caller can inspect `isError` and parse text content.
132
+ *
133
+ * On transport-closed errors (e.g. macOS kills the upstream binary after
134
+ * the user grants Screen Recording permission), this method transparently
135
+ * tears down the stale connection, reconnects, and retries the call once.
136
+ * If the retry also fails, the error is re-thrown without further
137
+ * reconnect attempts.
138
+ */
139
+ async callTool(name, args) {
140
+ if (!this.client) throw new Error("ComputerUseClient not started");
141
+ try {
142
+ return await this.client.callTool({
143
+ name,
144
+ arguments: args
145
+ });
146
+ } catch (err) {
147
+ if (!isTransportClosedError(err)) throw err;
148
+ await this.stop();
149
+ await this.start();
150
+ if (!this.client) throw new Error("ComputerUseClient reconnect failed");
151
+ return await this.client.callTool({
152
+ name,
153
+ arguments: args
154
+ });
155
+ }
156
+ }
157
+ /** Tear down the child process. Safe to call multiple times. */
158
+ async stop() {
159
+ const client = this.client;
160
+ this.client = void 0;
161
+ if (client) {
162
+ try {
163
+ await client.close();
164
+ } catch {
165
+ }
166
+ }
167
+ }
168
+ };
169
+ function isTransportClosedError(err) {
170
+ const msg = err instanceof Error ? err.message : String(err);
171
+ return /connection closed|not connected/i.test(msg);
172
+ }
173
+ __name(isTransportClosedError, "isTransportClosedError");
174
+
175
+ // packages/core/src/tools/computer-use/bootstrap.ts
176
+ init_esbuild_shims();
177
+ import { execFile } from "node:child_process";
178
+ import { promisify } from "node:util";
179
+ import { homedir as homedir2 } from "node:os";
180
+
181
+ // packages/core/src/tools/computer-use/install-state.ts
182
+ init_esbuild_shims();
183
+ import { readFile, writeFile, mkdir } from "node:fs/promises";
184
+ import { homedir } from "node:os";
185
+ import { join, dirname } from "node:path";
186
+ function installStatePathFor(home = homedir()) {
187
+ return join(home, ".qwen", "computer-use", "installed.json");
188
+ }
189
+ __name(installStatePathFor, "installStatePathFor");
190
+ async function loadInstallState(home = homedir()) {
191
+ try {
192
+ const text = await readFile(installStatePathFor(home), "utf8");
193
+ const parsed = JSON.parse(text);
194
+ if (typeof parsed?.approvedPackageSpec !== "string") return void 0;
195
+ if (typeof parsed?.approvedAtIso !== "string") return void 0;
196
+ return parsed;
197
+ } catch (err) {
198
+ if (err?.code === "ENOENT") return void 0;
199
+ return void 0;
200
+ }
201
+ }
202
+ __name(loadInstallState, "loadInstallState");
203
+ async function saveInstallState(home = homedir(), state) {
204
+ const path = installStatePathFor(home);
205
+ await mkdir(dirname(path), { recursive: true });
206
+ await writeFile(path, JSON.stringify(state, null, 2), "utf8");
207
+ }
208
+ __name(saveInstallState, "saveInstallState");
209
+ async function isPackageSpecApproved(home = homedir(), packageSpec) {
210
+ const state = await loadInstallState(home);
211
+ return state?.approvedPackageSpec === packageSpec;
212
+ }
213
+ __name(isPackageSpecApproved, "isPackageSpecApproved");
214
+
215
+ // packages/core/src/tools/computer-use/permission-detector.ts
216
+ init_esbuild_shims();
217
+
218
+ // packages/core/src/tools/computer-use/bootstrap.ts
219
+ var execFileAsync = promisify(execFile);
220
+ function parseDoctorStdout(stdout) {
221
+ const accessibilityGranted = /accessibility\s*=\s*granted/i.test(stdout);
222
+ const screenRecordingGranted = /screenrecording\s*=\s*granted/i.test(stdout);
223
+ if (!accessibilityGranted) return "accessibility";
224
+ if (!screenRecordingGranted) return "screenRecording";
225
+ return "ok";
226
+ }
227
+ __name(parseDoctorStdout, "parseDoctorStdout");
228
+ async function probePermissionsViaDoctor(packageSpec) {
229
+ try {
230
+ const { stdout } = await execFileAsync(
231
+ "npx",
232
+ ["-y", packageSpec, "doctor"],
233
+ {
234
+ timeout: 3e4,
235
+ env: process.env
236
+ }
237
+ );
238
+ return parseDoctorStdout(stdout);
239
+ } catch {
240
+ return "other";
241
+ }
242
+ }
243
+ __name(probePermissionsViaDoctor, "probePermissionsViaDoctor");
244
+ function defaultDeps() {
245
+ const packageSpec = resolveComputerUsePackageSpec();
246
+ return {
247
+ homeDir: homedir2(),
248
+ packageSpec,
249
+ platform: process.platform,
250
+ promptInstallApproval: /* @__PURE__ */ __name(async (spec) => {
251
+ process.stderr.write(
252
+ `
253
+ [Computer Use] First-time install
254
+ Package: ${spec}
255
+ This will fetch ~50MB from the npm registry the first time.
256
+ Computer Use can click, type, and read your desktop apps.
257
+ On macOS you'll be guided through Accessibility and Screen Recording permissions next.
258
+ Set QWEN_COMPUTER_USE_AUTO_APPROVE=1 to skip this prompt.
259
+ `
260
+ );
261
+ return process.env["QWEN_COMPUTER_USE_AUTO_APPROVE"] === "1";
262
+ }, "promptInstallApproval"),
263
+ probePermissions: probePermissionsViaDoctor
264
+ };
265
+ }
266
+ __name(defaultDeps, "defaultDeps");
267
+ async function runBootstrap(client, ctx, depsOverride) {
268
+ const deps = { ...defaultDeps(), ...depsOverride };
269
+ const pollIntervalMs = deps.pollIntervalMs ?? 5e3;
270
+ const pollTimeoutMs = deps.pollTimeoutMs ?? 10 * 6e4;
271
+ const approved = await isPackageSpecApproved(deps.homeDir, deps.packageSpec);
272
+ if (!approved) {
273
+ ctx.updateOutput?.("Computer Use needs to be installed (first use).");
274
+ const ok = await deps.promptInstallApproval(deps.packageSpec);
275
+ if (!ok) {
276
+ throw new Error(
277
+ `Computer Use install declined by user. Re-invoke the tool to be prompted again.`
278
+ );
279
+ }
280
+ await saveInstallState(deps.homeDir, {
281
+ approvedPackageSpec: deps.packageSpec,
282
+ approvedAtIso: (/* @__PURE__ */ new Date()).toISOString()
283
+ });
284
+ }
285
+ const wasAlreadyStarted = client.isStarted();
286
+ if (!wasAlreadyStarted) {
287
+ await client.start(ctx.updateOutput);
288
+ }
289
+ if (wasAlreadyStarted) return;
290
+ if (deps.platform !== "darwin") return;
291
+ const probe = await deps.probePermissions(deps.packageSpec);
292
+ if (probe === "ok" || probe === "other") {
293
+ return;
294
+ }
295
+ ctx.updateOutput?.(
296
+ `Computer Use needs macOS permissions (${probe}). The onboarding window is opening \u2014 please grant Accessibility and Screen Recording, then this will continue automatically.`
297
+ );
298
+ let lastProbeKind = probe;
299
+ const startedAt = Date.now();
300
+ for (; ; ) {
301
+ if (ctx.signal.aborted) {
302
+ throw new Error("Computer Use bootstrap aborted.");
303
+ }
304
+ if (Date.now() - startedAt > pollTimeoutMs) {
305
+ throw new Error(
306
+ `Computer Use permission grant timed out after ${Math.round(pollTimeoutMs / 1e3)}s. Re-invoke the tool to retry.`
307
+ );
308
+ }
309
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
310
+ const next = await deps.probePermissions(deps.packageSpec);
311
+ if (next === "ok" || next === "other") return;
312
+ if (next !== lastProbeKind) {
313
+ ctx.updateOutput?.(
314
+ `Now waiting for ${next} permission. The onboarding window remains open \u2014 please grant this permission to continue.`
315
+ );
316
+ lastProbeKind = next;
317
+ }
318
+ const elapsedSec = Math.round((Date.now() - startedAt) / 1e3);
319
+ ctx.updateOutput?.(`Waiting for ${next} permission... (${elapsedSec}s)`);
320
+ }
321
+ }
322
+ __name(runBootstrap, "runBootstrap");
323
+
324
+ // packages/core/src/tools/computer-use/tool.ts
325
+ import { homedir as homedir3 } from "node:os";
326
+ var INSTALL_REASON = "This will install the open-computer-use binary (~50MB) via npx the first time. Computer Use can click, type, and read your desktop apps. On macOS you'll be guided through Accessibility / Screen Recording permissions next.";
327
+ var ComputerUseInvocation = class extends BaseToolInvocation {
328
+ constructor(upstreamName, params) {
329
+ super(params);
330
+ this.upstreamName = upstreamName;
331
+ }
332
+ static {
333
+ __name(this, "ComputerUseInvocation");
334
+ }
335
+ getDescription() {
336
+ return safeJsonStringify(this.params);
337
+ }
338
+ /**
339
+ * Always returns 'ask' so every desktop action surfaces through the
340
+ * standard tool-permission dialog. The PermissionManager rule system
341
+ * handles "always allow" per tool via ProceedAlwaysTool — that's the
342
+ * single source of truth for repeat-approval behavior.
343
+ *
344
+ * Earlier this returned 'allow' once the install-state file existed,
345
+ * which conflated install approval with per-action approval and
346
+ * effectively granted blanket permission for all 9 computer_use__*
347
+ * tools (including mutating actions like click / type_text / drag)
348
+ * after the first install confirmation. See PR #4590 review for the
349
+ * full discussion.
350
+ */
351
+ async getDefaultPermission() {
352
+ return "ask";
353
+ }
354
+ /**
355
+ * Builds the confirmation dialog. Two variants:
356
+ *
357
+ * 1. Install not yet approved → show install info (download size,
358
+ * permission flow to follow). onConfirm writes the install state
359
+ * so runBootstrap() inside execute() skips its env-var fallback
360
+ * prompt for headless contexts.
361
+ *
362
+ * 2. Install already approved → show per-action info (which tool +
363
+ * which args) so the user can decide whether THIS specific action
364
+ * is OK to perform.
365
+ *
366
+ * Both variants set permissionRules so the standard "Always allow"
367
+ * outcomes (ProceedAlwaysTool / ProceedAlwaysUser / ProceedAlwaysProject)
368
+ * add a rule via PermissionManager — subsequent calls of the SAME
369
+ * tool then skip the dialog. Different tools each need their own
370
+ * "always allow" choice; install approval no longer grants blanket
371
+ * access.
372
+ *
373
+ * On Cancel: install state is NOT written; execute() / runBootstrap()
374
+ * will use the env-var fallback (QWEN_COMPUTER_USE_AUTO_APPROVE),
375
+ * which defaults to refusing — producing a clear error message.
376
+ */
377
+ async getConfirmationDetails(_abortSignal) {
378
+ const permissionRules = [`computer_use__${this.upstreamName}`];
379
+ const installApproved = await isPackageSpecApproved(
380
+ homedir3(),
381
+ resolveComputerUsePackageSpec()
382
+ );
383
+ const prompt = installApproved ? `Tool: computer_use__${this.upstreamName}
384
+
385
+ Args: ${safeJsonStringify(this.params)}
386
+
387
+ This will act on your desktop via the Computer Use binary.` : `Tool: computer_use__${this.upstreamName}
388
+
389
+ ${INSTALL_REASON}`;
390
+ const details = {
391
+ type: "info",
392
+ title: `Allow Computer Use (${this.upstreamName})`,
393
+ prompt,
394
+ permissionRules,
395
+ onConfirm: /* @__PURE__ */ __name(async (outcome, _payload) => {
396
+ if (outcome !== "cancel" /* Cancel */) {
397
+ await saveInstallState(homedir3(), {
398
+ approvedPackageSpec: resolveComputerUsePackageSpec(),
399
+ approvedAtIso: (/* @__PURE__ */ new Date()).toISOString()
400
+ });
401
+ }
402
+ }, "onConfirm")
403
+ };
404
+ return details;
405
+ }
406
+ async execute(signal, updateOutput) {
407
+ const client = ComputerUseClient.shared();
408
+ await runBootstrap(client, { signal, updateOutput });
409
+ let mcpResult;
410
+ try {
411
+ mcpResult = await client.callTool(this.upstreamName, this.params);
412
+ } catch (err) {
413
+ const message = err instanceof Error ? err.message : String(err);
414
+ return {
415
+ llmContent: `Computer Use tool '${this.upstreamName}' failed: ${message}`,
416
+ returnDisplay: `Error: ${message}`,
417
+ error: { message }
418
+ };
419
+ }
420
+ const llmContent = buildLlmContent(mcpResult.content, this.upstreamName);
421
+ const returnDisplay = buildDisplayText(mcpResult.content);
422
+ if (mcpResult.isError) {
423
+ const errorText = returnDisplay || `Tool '${this.upstreamName}' returned isError=true`;
424
+ return {
425
+ llmContent: llmContent || errorText,
426
+ returnDisplay: errorText,
427
+ error: { message: errorText }
428
+ };
429
+ }
430
+ return {
431
+ llmContent,
432
+ returnDisplay
433
+ };
434
+ }
435
+ };
436
+ var ComputerUseTool = class extends BaseDeclarativeTool {
437
+ constructor(upstreamName, schema) {
438
+ const qwenName = `computer_use__${upstreamName}`;
439
+ super(
440
+ qwenName,
441
+ qwenName,
442
+ // displayName == name; no MCP branding in UI
443
+ schema.description,
444
+ "other" /* Other */,
445
+ schema.parameterSchema,
446
+ true,
447
+ // isOutputMarkdown — many results are JSON-ish text or screenshots
448
+ true,
449
+ // canUpdateOutput — bootstrap streams progress
450
+ true,
451
+ // shouldDefer — surface only via ToolSearch
452
+ false,
453
+ // alwaysLoad
454
+ `computer use desktop click type screenshot mouse keyboard scroll drag automation gui app native`
455
+ );
456
+ this.upstreamName = upstreamName;
457
+ }
458
+ static {
459
+ __name(this, "ComputerUseTool");
460
+ }
461
+ /**
462
+ * Coerce parameter types before schema validation.
463
+ * Models can send the wrong JS type for a field:
464
+ * - qwen3.6 sends `element_index: 2` (number) but upstream wants "2" (string)
465
+ * - Some models send `x: "500"` (string) but upstream wants 500 (number)
466
+ * Pre-coercing avoids spurious validation failures without loosening schema types.
467
+ */
468
+ validateToolParams(params) {
469
+ const coerced = coerceTypes(
470
+ params,
471
+ this.parameterSchema
472
+ );
473
+ return super.validateToolParams(coerced);
474
+ }
475
+ build(params) {
476
+ const coerced = coerceTypes(
477
+ params,
478
+ this.parameterSchema
479
+ );
480
+ return super.build(coerced);
481
+ }
482
+ createInvocation(params) {
483
+ return new ComputerUseInvocation(this.upstreamName, params);
484
+ }
485
+ };
486
+ function coerceTypes(params, schema) {
487
+ const properties = schema.properties;
488
+ if (!properties) return params;
489
+ const result = { ...params };
490
+ for (const [key, value] of Object.entries(result)) {
491
+ const fieldType = properties[key]?.type;
492
+ if ((fieldType === "integer" || fieldType === "number") && typeof value === "string") {
493
+ const trimmed = value.trim();
494
+ if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
495
+ const parsed = fieldType === "integer" ? parseInt(trimmed, 10) : parseFloat(trimmed);
496
+ if (Number.isFinite(parsed)) {
497
+ result[key] = parsed;
498
+ }
499
+ }
500
+ } else if (fieldType === "string" && typeof value === "number") {
501
+ result[key] = String(value);
502
+ }
503
+ }
504
+ return result;
505
+ }
506
+ __name(coerceTypes, "coerceTypes");
507
+ function buildLlmContent(content, toolName) {
508
+ const parts = [];
509
+ for (const block of content) {
510
+ if (block.type === "text" && block.text) {
511
+ parts.push({ text: block.text });
512
+ } else if ((block.type === "image" || block.type === "audio") && block.mimeType && block.data) {
513
+ parts.push({
514
+ text: `[Tool '${toolName}' provided the following ${block.type} data with mime-type: ${block.mimeType}]`
515
+ });
516
+ parts.push({
517
+ inlineData: {
518
+ mimeType: block.mimeType,
519
+ data: block.data
520
+ }
521
+ });
522
+ }
523
+ }
524
+ const hasNonText = parts.some((p) => p.inlineData !== void 0);
525
+ if (!hasNonText) {
526
+ return parts.map((p) => p.text ?? "").filter(Boolean).join("\n");
527
+ }
528
+ return parts;
529
+ }
530
+ __name(buildLlmContent, "buildLlmContent");
531
+ function buildDisplayText(content) {
532
+ return content.map((block) => block.type === "text" ? block.text ?? "" : "").filter(Boolean).join("\n");
533
+ }
534
+ __name(buildDisplayText, "buildDisplayText");
535
+
536
+ // packages/core/src/tools/computer-use/schemas.ts
537
+ init_esbuild_shims();
538
+ var COMPUTER_USE_TOOL_NAMES = [
539
+ "click",
540
+ "drag",
541
+ "get_app_state",
542
+ "list_apps",
543
+ "perform_secondary_action",
544
+ "press_key",
545
+ "scroll",
546
+ "set_value",
547
+ "type_text"
548
+ ];
549
+ var COMPUTER_USE_SCHEMAS = {
550
+ click: {
551
+ description: "Click an element by index or pixel coordinates from screenshot. This tool is part of plugin `Computer Use`.",
552
+ parameterSchema: {
553
+ type: "object",
554
+ properties: {
555
+ click_count: {
556
+ type: "integer",
557
+ description: "Number of clicks. Defaults to 1"
558
+ },
559
+ mouse_button: {
560
+ description: "Mouse button to click. Defaults to left.",
561
+ enum: ["left", "right", "middle"],
562
+ type: "string"
563
+ },
564
+ element_index: {
565
+ type: "string",
566
+ description: "Element index to click"
567
+ },
568
+ y: {
569
+ type: "number",
570
+ description: "Y coordinate in screenshot pixel coordinates"
571
+ },
572
+ app: {
573
+ type: "string",
574
+ description: "App name or bundle identifier"
575
+ },
576
+ x: {
577
+ description: "X coordinate in screenshot pixel coordinates",
578
+ type: "number"
579
+ }
580
+ },
581
+ required: ["app"],
582
+ additionalProperties: false
583
+ }
584
+ },
585
+ drag: {
586
+ description: "Drag from one point to another using pixel coordinates. This tool is part of plugin `Computer Use`.",
587
+ parameterSchema: {
588
+ type: "object",
589
+ properties: {
590
+ app: {
591
+ type: "string",
592
+ description: "App name or bundle identifier"
593
+ },
594
+ from_x: {
595
+ description: "Start X coordinate",
596
+ type: "number"
597
+ },
598
+ from_y: {
599
+ type: "number",
600
+ description: "Start Y coordinate"
601
+ },
602
+ to_x: {
603
+ description: "End X coordinate",
604
+ type: "number"
605
+ },
606
+ to_y: {
607
+ type: "number",
608
+ description: "End Y coordinate"
609
+ }
610
+ },
611
+ required: ["app", "from_x", "from_y", "to_x", "to_y"],
612
+ additionalProperties: false
613
+ }
614
+ },
615
+ get_app_state: {
616
+ description: "Start an app use session if needed, then get the state of the app's key window and return a screenshot and accessibility tree. This must be called once per assistant turn before interacting with the app. This tool is part of plugin `Computer Use`.",
617
+ parameterSchema: {
618
+ type: "object",
619
+ properties: {
620
+ app: {
621
+ description: "App name or bundle identifier",
622
+ type: "string"
623
+ }
624
+ },
625
+ required: ["app"],
626
+ additionalProperties: false
627
+ }
628
+ },
629
+ list_apps: {
630
+ description: "List the apps on this computer. Returns the set of apps that are currently running, as well as any that have been used in the last 14 days, including details on usage frequency. This tool is part of plugin `Computer Use`.",
631
+ parameterSchema: {
632
+ type: "object",
633
+ properties: {},
634
+ additionalProperties: false
635
+ }
636
+ },
637
+ perform_secondary_action: {
638
+ description: "Invoke a secondary accessibility action exposed by an element. This tool is part of plugin `Computer Use`.",
639
+ parameterSchema: {
640
+ type: "object",
641
+ properties: {
642
+ action: {
643
+ description: "Secondary accessibility action name",
644
+ type: "string"
645
+ },
646
+ app: {
647
+ description: "App name or bundle identifier",
648
+ type: "string"
649
+ },
650
+ element_index: {
651
+ description: "Element identifier",
652
+ type: "string"
653
+ }
654
+ },
655
+ required: ["app", "element_index", "action"],
656
+ additionalProperties: false
657
+ }
658
+ },
659
+ press_key: {
660
+ description: 'Press a key or key-combination on the keyboard, including modifier and navigation keys.\n - This supports xdotool\'s `key` syntax.\n - Examples: "a", "Return", "Tab", "super+c", "Up", "KP_0" (for the numpad 0 key). This tool is part of plugin `Computer Use`.',
661
+ parameterSchema: {
662
+ type: "object",
663
+ properties: {
664
+ app: {
665
+ description: "App name or bundle identifier",
666
+ type: "string"
667
+ },
668
+ key: {
669
+ type: "string",
670
+ description: "Key or key combination to press"
671
+ }
672
+ },
673
+ required: ["app", "key"],
674
+ additionalProperties: false
675
+ }
676
+ },
677
+ scroll: {
678
+ description: "Scroll an element in a direction by a number of pages. This tool is part of plugin `Computer Use`.",
679
+ parameterSchema: {
680
+ type: "object",
681
+ properties: {
682
+ pages: {
683
+ type: "number",
684
+ description: "Number of pages to scroll. Fractional values are supported. Defaults to 1"
685
+ },
686
+ app: {
687
+ description: "App name or bundle identifier",
688
+ type: "string"
689
+ },
690
+ element_index: {
691
+ description: "Element identifier",
692
+ type: "string"
693
+ },
694
+ direction: {
695
+ description: "Scroll direction: up, down, left, or right",
696
+ type: "string"
697
+ }
698
+ },
699
+ required: ["app", "element_index", "direction"],
700
+ additionalProperties: false
701
+ }
702
+ },
703
+ set_value: {
704
+ description: "Set the value of a settable accessibility element. This tool is part of plugin `Computer Use`.",
705
+ parameterSchema: {
706
+ type: "object",
707
+ properties: {
708
+ element_index: {
709
+ type: "string",
710
+ description: "Element identifier"
711
+ },
712
+ value: {
713
+ type: "string",
714
+ description: "Value to assign"
715
+ },
716
+ app: {
717
+ description: "App name or bundle identifier",
718
+ type: "string"
719
+ }
720
+ },
721
+ required: ["app", "element_index", "value"],
722
+ additionalProperties: false
723
+ }
724
+ },
725
+ type_text: {
726
+ description: "Type literal text using keyboard input. This tool is part of plugin `Computer Use`.",
727
+ parameterSchema: {
728
+ type: "object",
729
+ properties: {
730
+ app: {
731
+ type: "string",
732
+ description: "App name or bundle identifier"
733
+ },
734
+ text: {
735
+ type: "string",
736
+ description: "Literal text to type"
737
+ }
738
+ },
739
+ required: ["app", "text"],
740
+ additionalProperties: false
741
+ }
742
+ }
743
+ };
744
+
745
+ // packages/core/src/tools/computer-use/index.ts
746
+ async function registerComputerUseTools(registerLazy) {
747
+ for (const upstreamName of COMPUTER_USE_TOOL_NAMES) {
748
+ const schema = COMPUTER_USE_SCHEMAS[upstreamName];
749
+ const qwenName = `computer_use__${upstreamName}`;
750
+ await registerLazy(
751
+ qwenName,
752
+ async () => new ComputerUseTool(upstreamName, schema)
753
+ );
754
+ }
755
+ }
756
+ __name(registerComputerUseTools, "registerComputerUseTools");
757
+ export {
758
+ COMPUTER_USE_SCHEMAS,
759
+ COMPUTER_USE_TOOL_NAMES,
760
+ ComputerUseClient,
761
+ ComputerUseTool,
762
+ registerComputerUseTools
763
+ };
764
+ /**
765
+ * @license
766
+ * Copyright 2025 Qwen Team
767
+ * SPDX-License-Identifier: Apache-2.0
768
+ */