@qwen-code/qwen-code 0.18.0-preview.2 → 0.18.1
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/bundled/loop/SKILL.md +2 -1
- package/bundled/qc-helper/docs/_meta.ts +1 -0
- package/bundled/qc-helper/docs/configuration/auth.md +1 -1
- package/bundled/qc-helper/docs/configuration/model-providers.md +12 -5
- package/bundled/qc-helper/docs/configuration/settings.md +33 -32
- package/bundled/qc-helper/docs/features/approval-mode.md +10 -14
- package/bundled/qc-helper/docs/features/commands.md +33 -11
- package/bundled/qc-helper/docs/features/dual-output.md +37 -3
- package/bundled/qc-helper/docs/features/followup-suggestions.md +2 -2
- package/bundled/qc-helper/docs/features/skills.md +29 -3
- package/bundled/qc-helper/docs/features/sub-agents.md +34 -12
- package/bundled/qc-helper/docs/qwen-serve-deploy-local.md +221 -0
- package/bundled/qc-helper/docs/qwen-serve.md +246 -28
- package/chunks/{agent-QB7TZ4HW.js → agent-XT7NHZ5H.js} +25 -24
- package/chunks/agent-headless-LNRE63ZL.js +51 -0
- package/chunks/{anthropicContentGenerator-M45EVVRM.js → anthropicContentGenerator-DCI26OQF.js} +7 -7
- package/chunks/{askUserQuestion-WM2KHM3K.js → askUserQuestion-ITYUTWLR.js} +45 -3
- package/chunks/{ca-BARBRL6N.js → ca-RK4QPLIX.js} +18 -1
- package/chunks/{chunk-CWV3SJZS.js → chunk-3NRO6NHX.js} +2 -2
- package/chunks/{chunk-BNESGOSJ.js → chunk-55ZMG67I.js} +1 -1
- package/chunks/{chunk-QCG6KPNM.js → chunk-6T7Y7USE.js} +18017 -11621
- package/chunks/{chunk-CNHFPN7T.js → chunk-7KPZFE5A.js} +1 -1
- package/chunks/{chunk-2ZTWI7KH.js → chunk-A2ZIEEGJ.js} +30 -22
- package/chunks/{chunk-JUGRPQAB.js → chunk-B4ZF2KSI.js} +1 -1
- package/chunks/chunk-BJ5HQ23U.js +178 -0
- package/chunks/{chunk-HXJE7VOG.js → chunk-BXYRCW2C.js} +1074 -144
- package/chunks/{chunk-ICOI4E4S.js → chunk-CPVI5J2L.js} +101 -23
- package/chunks/{chunk-HV3ZZ7G4.js → chunk-DHZREJTG.js} +2 -2
- package/chunks/{chunk-GX7VH5JQ.js → chunk-FIQECJTQ.js} +1 -1
- package/chunks/{chunk-SZOEIL6S.js → chunk-H6BD2ELD.js} +1 -0
- package/chunks/{chunk-JXAZUMDW.js → chunk-HA2UEYZP.js} +7 -4
- package/chunks/{chunk-USE2VQ5P.js → chunk-HED55F43.js} +26 -1
- package/chunks/{chunk-PAEBHDIO.js → chunk-HQUWWSSP.js} +1 -1
- package/chunks/{chunk-MVIVIPCU.js → chunk-IDYDPBBN.js} +361 -583
- package/chunks/{chunk-JVQOQ3OU.js → chunk-IQHSD7K5.js} +1 -1
- package/chunks/{chunk-NW5QBUYO.js → chunk-IS7UA4W3.js} +14 -14
- package/chunks/{chunk-UAMOBVVW.js → chunk-LXYWINWF.js} +1 -1
- package/chunks/{chunk-P4J26VDS.js → chunk-LYRSMKLS.js} +2 -2
- package/chunks/{chunk-Y7R6H6FT.js → chunk-LYSND7KR.js} +9 -4
- package/chunks/{chunk-LR62TEET.js → chunk-NNIYWQIS.js} +1 -1
- package/chunks/chunk-OMX7CUOE.js +356 -0
- package/chunks/{chunk-CNSMKPK6.js → chunk-QILTEBWS.js} +1 -1
- package/chunks/chunk-QQDPRDVW.js +25 -0
- package/chunks/{chunk-ZK4AMNIU.js → chunk-RON7LFNH.js} +1294 -314
- package/chunks/chunk-SFRV6BGY.js +243 -0
- package/chunks/{chunk-AVW55ZCO.js → chunk-WJ3SND6W.js} +37 -16
- package/chunks/{chunk-HGJPQK33.js → chunk-WPTCDQN6.js} +188 -534
- package/chunks/{chunk-7YKXFA3D.js → chunk-XZTNBSMW.js} +11 -11
- package/chunks/{chunk-C6WMLUNB.js → chunk-Y7KMDUEP.js} +1 -1
- package/chunks/{chunk-WFVXF3OM.js → chunk-Z2Z3GUXZ.js} +1 -0
- package/chunks/{chunk-KC6ZMJ5X.js → chunk-ZMIBJS45.js} +1 -1
- package/chunks/chunk-ZOFNJQNJ.js +607 -0
- package/chunks/computer-use-4YX3JGBV.js +2052 -0
- package/chunks/contextCommand-KS2H7MW5.js +53 -0
- package/chunks/cron-create-CAPUKK7I.js +184 -0
- package/chunks/{cron-delete-ZGUXWBTG.js → cron-delete-G3KAR26Q.js} +28 -5
- package/chunks/{cron-list-QNNZGMN3.js → cron-list-ZA4ZIUS5.js} +40 -7
- package/chunks/{de-YGKK2BC4.js → de-FGPM4KW5.js} +18 -1
- package/chunks/{devtools-IXE4UP72.js → devtools-FM6GJPYG.js} +1 -1
- package/chunks/{dist-ZMQ4TXD5.js → dist-7YWFWOCJ.js} +2 -2
- package/chunks/{dist-R2SXPG74.js → dist-VEGFONCF.js} +2 -2
- package/chunks/{dist-TE5QKMGR.js → dist-X4EXN7W6.js} +1 -1
- package/chunks/{dist-BXDUQ2QY.js → dist-YLS6NI7H.js} +1 -1
- package/chunks/{edit-6UBTS2J5.js → edit-2ARPEO4B.js} +26 -25
- package/chunks/{en-HSQQNQUB.js → en-VP6XPGEC.js} +9 -2
- package/chunks/{enter-worktree-NN7LIXCM.js → enter-worktree-IXNXNAW5.js} +25 -24
- package/chunks/enterPlanMode-TAKAGAYP.js +159 -0
- package/chunks/{exit-worktree-GGSS5KIE.js → exit-worktree-LHTRV7ML.js} +25 -24
- package/chunks/exitPlanMode-MK5UAITL.js +743 -0
- package/chunks/{fr-JXBKPJKQ.js → fr-ATYBVCLT.js} +18 -1
- package/chunks/{geminiContentGenerator-I4H2NLJG.js → geminiContentGenerator-HFJIGO77.js} +7 -7
- package/chunks/{getMachineId-bsd-F7GNPTER.js → getMachineId-bsd-4CASPIU4.js} +1 -1
- package/chunks/{getMachineId-darwin-T73DJL27.js → getMachineId-darwin-HPQPEMZR.js} +1 -1
- package/chunks/{getMachineId-linux-MKQTFPQM.js → getMachineId-linux-AUARKYHL.js} +1 -1
- package/chunks/{getMachineId-unsupported-MUR5KOQE.js → getMachineId-unsupported-S32ZDA2T.js} +1 -1
- package/chunks/{getMachineId-win-CDYFC6ZM.js → getMachineId-win-4EFLHYIJ.js} +1 -1
- package/chunks/{glob-OLCX57MD.js → glob-I2USLUSC.js} +25 -24
- package/chunks/{grep-7HXIMDOW.js → grep-WBIF7THR.js} +37 -30
- package/chunks/{ja-TGPZSP2B.js → ja-W2QEA2OI.js} +18 -1
- package/chunks/{keychain-token-storage-LB46DAEK.js → keychain-token-storage-QSTRHKKL.js} +3 -3
- package/chunks/{ls-6PEZUK6O.js → ls-2R5RHLX5.js} +4 -4
- package/chunks/{lsp-JZSJOVT7.js → lsp-XKH6ZIAN.js} +3 -3
- package/chunks/{monitor-SQO7MVAV.js → monitor-WU7UFATU.js} +25 -24
- package/chunks/{notebook-edit-72L3EBAL.js → notebook-edit-KUHYPXEM.js} +26 -25
- package/chunks/{openaiContentGenerator-FTR7CDWF.js → openaiContentGenerator-5PLHYJQL.js} +15 -15
- package/chunks/{pt-TIBG6BIO.js → pt-ZKEWJFBW.js} +18 -1
- package/chunks/{qwenContentGenerator-U5UFQ566.js → qwenContentGenerator-TSKW73KY.js} +27 -26
- package/chunks/{qwenOAuth2-EFSECGHF.js → qwenOAuth2-KK433U33.js} +6 -5
- package/chunks/{read-file-UA64EEQC.js → read-file-VIPF2PS6.js} +11 -11
- package/chunks/ripGrep-XLIZTYE7.js +49 -0
- package/chunks/{ru-JBCHCK4L.js → ru-VEKTPJ74.js} +18 -1
- package/chunks/{scheduler-VBASHOCA.js → scheduler-O66SLJGU.js} +25 -24
- package/chunks/{send-message-OYJZ5TPG.js → send-message-CTME7DXD.js} +3 -3
- package/chunks/{serve-A7E2OJDR.js → serve-BWOLYT62.js} +13164 -3840
- package/chunks/{shell-3NFOT6F5.js → shell-XE7UYKOO.js} +25 -24
- package/chunks/{skill-RA5YUREY.js → skill-RZWM6XMC.js} +64 -113
- package/chunks/{src-NFCMARMT.js → src-L5P7K4MH.js} +176 -44
- package/chunks/{syntheticOutput-DETQ2YM6.js → syntheticOutput-ZJGSU7OQ.js} +4 -4
- package/chunks/{task-create-Y3ZKTJIG.js → task-create-EE6JEM7G.js} +8 -7
- package/chunks/{task-list-ONXJ3I3A.js → task-list-EESYAC65.js} +7 -6
- package/chunks/{task-stop-UHDC4N5B.js → task-stop-XZVCFFYY.js} +3 -3
- package/chunks/{task-update-TCNOU3P5.js → task-update-EIO4HNE3.js} +21 -9
- package/chunks/{team-create-6SR4OVRG.js → team-create-R2H7Y3SG.js} +28 -26
- package/chunks/{team-delete-EJ4U4DDP.js → team-delete-A7LXPGV7.js} +9 -6
- package/chunks/{todoWrite-TEYDRS5L.js → todoWrite-VRKSGAWM.js} +5 -5
- package/chunks/{tool-search-OD435A3X.js → tool-search-USSQMTMS.js} +11 -11
- package/chunks/{web-fetch-6W67H5PO.js → web-fetch-GHAZUA54.js} +5 -5
- package/chunks/workflow-5LNNLNUR.js +1414 -0
- package/chunks/{write-file-475L5OPP.js → write-file-2I7HP24C.js} +26 -25
- package/chunks/{zh-VCLWO26Y.js → zh-OIXDDQHB.js} +10 -3
- package/chunks/{zh-TW-G3HFHVVT.js → zh-TW-6YFNCKTA.js} +10 -3
- package/cli-entry.js +19 -0
- package/cli.js +11064 -6628
- package/examples/starter/QWEN.md +30 -0
- package/examples/starter/README.md +59 -0
- package/examples/starter/agents/diary.md +86 -0
- package/examples/starter/commands/writing/polish.md +13 -0
- package/examples/starter/example.ts +64 -0
- package/examples/starter/package.json +18 -0
- package/examples/starter/qwen-extension.json +12 -0
- package/examples/starter/skills/synonyms/SKILL.md +48 -0
- package/examples/starter/tsconfig.json +13 -0
- package/fzfWorker.js +1083 -0
- package/locales/ca.js +20 -2
- package/locales/de.js +21 -2
- package/locales/en.js +13 -4
- package/locales/fr.js +22 -2
- package/locales/ja.js +22 -2
- package/locales/pt.js +21 -2
- package/locales/ru.js +20 -2
- package/locales/zh-TW.js +11 -4
- package/locales/zh.js +11 -4
- package/package.json +5 -3
- package/chunks/agent-headless-APVHH7QM.js +0 -50
- package/chunks/chunk-AJIR24J2.js +0 -59
- package/chunks/chunk-SKBPNJEW.js +0 -45
- package/chunks/chunk-XBFVXFB2.js +0 -216
- package/chunks/computer-use-B7VIUI7F.js +0 -825
- package/chunks/contextCommand-63RZ3O5R.js +0 -52
- package/chunks/cron-create-FI5LJVUS.js +0 -140
- package/chunks/exitPlanMode-H323NHB2.js +0 -235
- package/chunks/ripGrep-WSYCWZVK.js +0 -48
- package/chunks/workflow-62DHH4EO.js +0 -708
|
@@ -0,0 +1,1414 @@
|
|
|
1
|
+
// Force strict mode and setup for ESM
|
|
2
|
+
"use strict";
|
|
3
|
+
import {
|
|
4
|
+
AgentEventEmitter,
|
|
5
|
+
FileDiscoveryService,
|
|
6
|
+
GitWorktreeService,
|
|
7
|
+
WorkspaceContext,
|
|
8
|
+
generateAgentWorktreeSlug,
|
|
9
|
+
rebuildToolRegistryOnOverride,
|
|
10
|
+
writeWorktreeSessionMarker
|
|
11
|
+
} from "./chunk-6T7Y7USE.js";
|
|
12
|
+
import "./chunk-K5PGHDBN.js";
|
|
13
|
+
import "./chunk-HQUWWSSP.js";
|
|
14
|
+
import "./chunk-O4PICXES.js";
|
|
15
|
+
import "./chunk-TW522KN6.js";
|
|
16
|
+
import "./chunk-BJ5HQ23U.js";
|
|
17
|
+
import "./chunk-SFRV6BGY.js";
|
|
18
|
+
import "./chunk-ZOFNJQNJ.js";
|
|
19
|
+
import "./chunk-WPTCDQN6.js";
|
|
20
|
+
import "./chunk-OMX7CUOE.js";
|
|
21
|
+
import "./chunk-MLZQVCF3.js";
|
|
22
|
+
import "./chunk-LD2XBG6Z.js";
|
|
23
|
+
import "./chunk-CPVI5J2L.js";
|
|
24
|
+
import "./chunk-ZMIBJS45.js";
|
|
25
|
+
import "./chunk-77WXWU44.js";
|
|
26
|
+
import "./chunk-B4ZF2KSI.js";
|
|
27
|
+
import "./chunk-RON7LFNH.js";
|
|
28
|
+
import "./chunk-3PJXIDKI.js";
|
|
29
|
+
import "./chunk-UWCTAVOD.js";
|
|
30
|
+
import "./chunk-OFEVLU4C.js";
|
|
31
|
+
import "./chunk-IQHSD7K5.js";
|
|
32
|
+
import "./chunk-LYRSMKLS.js";
|
|
33
|
+
import {
|
|
34
|
+
SyntheticOutputTool
|
|
35
|
+
} from "./chunk-QILTEBWS.js";
|
|
36
|
+
import {
|
|
37
|
+
BaseDeclarativeTool,
|
|
38
|
+
BaseToolInvocation,
|
|
39
|
+
ToolDisplayNames,
|
|
40
|
+
ToolNames
|
|
41
|
+
} from "./chunk-A2ZIEEGJ.js";
|
|
42
|
+
import "./chunk-IDYDPBBN.js";
|
|
43
|
+
import "./chunk-FIQECJTQ.js";
|
|
44
|
+
import {
|
|
45
|
+
createChildAbortController
|
|
46
|
+
} from "./chunk-64WXLC72.js";
|
|
47
|
+
import "./chunk-LXYWINWF.js";
|
|
48
|
+
import "./chunk-NNIYWQIS.js";
|
|
49
|
+
import "./chunk-LYSND7KR.js";
|
|
50
|
+
import "./chunk-55ZMG67I.js";
|
|
51
|
+
import "./chunk-H6BD2ELD.js";
|
|
52
|
+
import "./chunk-5IFG2VC4.js";
|
|
53
|
+
import {
|
|
54
|
+
createDebugLogger
|
|
55
|
+
} from "./chunk-HA2UEYZP.js";
|
|
56
|
+
import "./chunk-ZERZSAZL.js";
|
|
57
|
+
import "./chunk-QN5NZ3UQ.js";
|
|
58
|
+
import "./chunk-BR4QREVK.js";
|
|
59
|
+
import "./chunk-Z2Z3GUXZ.js";
|
|
60
|
+
import {
|
|
61
|
+
init_esbuild_shims
|
|
62
|
+
} from "./chunk-A4BMJM77.js";
|
|
63
|
+
import {
|
|
64
|
+
__name
|
|
65
|
+
} from "./chunk-J2S4EL5Y.js";
|
|
66
|
+
|
|
67
|
+
// packages/core/src/tools/workflow/workflow.ts
|
|
68
|
+
init_esbuild_shims();
|
|
69
|
+
|
|
70
|
+
// packages/core/src/agents/runtime/workflow-orchestrator.ts
|
|
71
|
+
init_esbuild_shims();
|
|
72
|
+
import { randomBytes } from "node:crypto";
|
|
73
|
+
import * as os from "node:os";
|
|
74
|
+
|
|
75
|
+
// packages/core/src/agents/runtime/workflow-sandbox.ts
|
|
76
|
+
init_esbuild_shims();
|
|
77
|
+
import * as vm from "node:vm";
|
|
78
|
+
function stripExportMeta(source) {
|
|
79
|
+
const re = /^\s*export\s+const\s+meta\s*=\s*\{/;
|
|
80
|
+
const match = re.exec(source);
|
|
81
|
+
if (!match) return source;
|
|
82
|
+
const exportIdx = match.index;
|
|
83
|
+
const startBrace = source.indexOf("{", exportIdx);
|
|
84
|
+
let depth = 1;
|
|
85
|
+
let i = startBrace + 1;
|
|
86
|
+
while (i < source.length && depth > 0) {
|
|
87
|
+
const ch = source[i];
|
|
88
|
+
const next = source[i + 1];
|
|
89
|
+
if (ch === "/" && next === "/") {
|
|
90
|
+
i += 2;
|
|
91
|
+
while (i < source.length && source[i] !== "\n") i++;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (ch === "/" && next === "*") {
|
|
95
|
+
i += 2;
|
|
96
|
+
while (i < source.length && !(source[i] === "*" && source[i + 1] === "/"))
|
|
97
|
+
i++;
|
|
98
|
+
i += 2;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
if (ch === "/" && isRegexContext(source, i)) {
|
|
102
|
+
i++;
|
|
103
|
+
let inClass = false;
|
|
104
|
+
while (i < source.length && (inClass || source[i] !== "/") && source[i] !== "\n") {
|
|
105
|
+
if (source[i] === "\\") i += 2;
|
|
106
|
+
else if (source[i] === "[") {
|
|
107
|
+
inClass = true;
|
|
108
|
+
i++;
|
|
109
|
+
} else if (source[i] === "]") {
|
|
110
|
+
inClass = false;
|
|
111
|
+
i++;
|
|
112
|
+
} else {
|
|
113
|
+
i++;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
i++;
|
|
117
|
+
while (i < source.length && /[gimsuy]/.test(source[i])) i++;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
121
|
+
const q = ch;
|
|
122
|
+
i++;
|
|
123
|
+
while (i < source.length && source[i] !== q) {
|
|
124
|
+
if (source[i] === "\\") i++;
|
|
125
|
+
i++;
|
|
126
|
+
}
|
|
127
|
+
i++;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (ch === "{") depth++;
|
|
131
|
+
else if (ch === "}") depth--;
|
|
132
|
+
i++;
|
|
133
|
+
}
|
|
134
|
+
if (depth !== 0) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
"stripExportMeta: unbalanced braces in export const meta declaration \u2014 the workflow script cannot be safely stripped. Check the meta block syntax."
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
while (i < source.length && /[\s;]/.test(source[i])) i++;
|
|
140
|
+
return source.slice(0, exportIdx) + source.slice(i);
|
|
141
|
+
}
|
|
142
|
+
__name(stripExportMeta, "stripExportMeta");
|
|
143
|
+
function isRegexContext(source, i) {
|
|
144
|
+
let j = i - 1;
|
|
145
|
+
while (j >= 0 && /\s/.test(source[j])) j--;
|
|
146
|
+
if (j < 0) return true;
|
|
147
|
+
const prev = source[j];
|
|
148
|
+
return /[{[(,;:=!&|?+\-*/%^~<>]/.test(prev);
|
|
149
|
+
}
|
|
150
|
+
__name(isRegexContext, "isRegexContext");
|
|
151
|
+
var debugLogger = createDebugLogger("WORKFLOW");
|
|
152
|
+
var MAX_LOG_LINES = 1e4;
|
|
153
|
+
var MAX_PHASE_ENTRIES = 1e4;
|
|
154
|
+
var ARGS_MAX_DEPTH = 64;
|
|
155
|
+
var DEFAULT_MAX_WALL_CLOCK_MS = 30 * 60 * 1e3;
|
|
156
|
+
function resolveMaxWallClockMs(opts) {
|
|
157
|
+
if (typeof opts.maxWallClockMs === "number" && opts.maxWallClockMs > 0) {
|
|
158
|
+
return opts.maxWallClockMs;
|
|
159
|
+
}
|
|
160
|
+
const envSec = Number(process.env["QWEN_CODE_MAX_WORKFLOW_SECONDS"]);
|
|
161
|
+
if (Number.isFinite(envSec) && envSec > 0) return envSec * 1e3;
|
|
162
|
+
return DEFAULT_MAX_WALL_CLOCK_MS;
|
|
163
|
+
}
|
|
164
|
+
__name(resolveMaxWallClockMs, "resolveMaxWallClockMs");
|
|
165
|
+
function validateArgs(val, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
|
|
166
|
+
if (depth > ARGS_MAX_DEPTH) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`WorkflowSandbox: args exceeded max nesting depth of ${ARGS_MAX_DEPTH}`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
if (val === null) return;
|
|
172
|
+
const t = typeof val;
|
|
173
|
+
if (t === "function") {
|
|
174
|
+
throw new Error(
|
|
175
|
+
"WorkflowSandbox: args must be JSON-serializable (functions are not allowed)."
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
if (t === "bigint") {
|
|
179
|
+
throw new Error(
|
|
180
|
+
"WorkflowSandbox: args must be JSON-serializable (BigInt is not allowed \u2014 pass as string)."
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
if (t !== "object") return;
|
|
184
|
+
const obj = val;
|
|
185
|
+
if (seen.has(obj)) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
"WorkflowSandbox: args must be JSON-serializable (circular reference detected)."
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
seen.add(obj);
|
|
191
|
+
if (Array.isArray(val)) {
|
|
192
|
+
for (const item of val) validateArgs(item, depth + 1, seen);
|
|
193
|
+
} else {
|
|
194
|
+
for (const v of Object.values(val)) {
|
|
195
|
+
validateArgs(v, depth + 1, seen);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
__name(validateArgs, "validateArgs");
|
|
200
|
+
function createWorkflowSandbox(opts) {
|
|
201
|
+
const phases = [];
|
|
202
|
+
const logs = [];
|
|
203
|
+
const safeLog = /* @__PURE__ */ __name((msg) => {
|
|
204
|
+
if (logs.length < MAX_LOG_LINES) {
|
|
205
|
+
logs.push(String(msg));
|
|
206
|
+
} else if (logs.length === MAX_LOG_LINES) {
|
|
207
|
+
logs.push(`[workflow log truncated at ${MAX_LOG_LINES} lines]`);
|
|
208
|
+
}
|
|
209
|
+
}, "safeLog");
|
|
210
|
+
const safePhase = /* @__PURE__ */ __name((title) => {
|
|
211
|
+
if (phases.length < MAX_PHASE_ENTRIES) {
|
|
212
|
+
phases.push(String(title));
|
|
213
|
+
} else if (phases.length === MAX_PHASE_ENTRIES) {
|
|
214
|
+
phases.push(
|
|
215
|
+
`[workflow phases truncated at ${MAX_PHASE_ENTRIES} entries]`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
}, "safePhase");
|
|
219
|
+
if (opts.args !== void 0) validateArgs(opts.args);
|
|
220
|
+
const argsJson = opts.args === void 0 ? null : JSON.stringify(opts.args);
|
|
221
|
+
const bridge = {
|
|
222
|
+
argsJson,
|
|
223
|
+
pushPhase: safePhase,
|
|
224
|
+
pushLog: safeLog,
|
|
225
|
+
lastPhase: /* @__PURE__ */ __name(() => phases[phases.length - 1], "lastPhase"),
|
|
226
|
+
hostAgent: opts.dispatch,
|
|
227
|
+
// PR #4947 R2 T7 (qwen-code-ci-bot): host-side log hook for reviveInRealm's
|
|
228
|
+
// catch path. Mirrors the rejection-logging in settleToNullArray so an
|
|
229
|
+
// operator running with debug logging can distinguish "thunk rejected"
|
|
230
|
+
// (settleToNullArray.warn) from "thunk resolved to a non-JSON-serializable
|
|
231
|
+
// value" (this warn). Receives only primitive strings/numbers — the bridge
|
|
232
|
+
// contract forbids host objects crossing back to the script.
|
|
233
|
+
logRevivalFailure: /* @__PURE__ */ __name((idx, reason) => {
|
|
234
|
+
debugLogger.warn(
|
|
235
|
+
`Workflow result revival failed at index ${idx}: ${reason}; slot set to null (non-JSON-serializable thunk return).`
|
|
236
|
+
);
|
|
237
|
+
}, "logRevivalFailure"),
|
|
238
|
+
// The truthy flags distinguish "injected" from "default stub" inside the
|
|
239
|
+
// init script without leaking the host function itself when not used.
|
|
240
|
+
hasParallel: !!opts.parallel,
|
|
241
|
+
hasPipeline: !!opts.pipeline,
|
|
242
|
+
hasBudget: !!opts.budget,
|
|
243
|
+
hostParallel: opts.parallel,
|
|
244
|
+
hostPipeline: opts.pipeline,
|
|
245
|
+
budgetTotal: opts.budget ? opts.budget.total : null,
|
|
246
|
+
hostBudgetSpent: opts.budget ? opts.budget.spent.bind(opts.budget) : null,
|
|
247
|
+
hostBudgetRemaining: opts.budget ? opts.budget.remaining.bind(opts.budget) : null
|
|
248
|
+
};
|
|
249
|
+
Object.setPrototypeOf(bridge, null);
|
|
250
|
+
const sandboxGlobals = Object.assign(
|
|
251
|
+
/* @__PURE__ */ Object.create(null),
|
|
252
|
+
{ __workflowBridge: bridge }
|
|
253
|
+
);
|
|
254
|
+
const ctx = vm.createContext(sandboxGlobals);
|
|
255
|
+
vm.runInContext(
|
|
256
|
+
`(() => {
|
|
257
|
+
const __b = globalThis.__workflowBridge;
|
|
258
|
+
delete globalThis.__workflowBridge;
|
|
259
|
+
|
|
260
|
+
// --- Math (vm-realm, random throws) ---
|
|
261
|
+
const realMath = Math;
|
|
262
|
+
const safeMath = Object.create(null);
|
|
263
|
+
for (const k of Object.getOwnPropertyNames(realMath)) {
|
|
264
|
+
if (k === 'random' || k === 'constructor') continue;
|
|
265
|
+
safeMath[k] = realMath[k];
|
|
266
|
+
}
|
|
267
|
+
safeMath.random = () => {
|
|
268
|
+
throw new Error(
|
|
269
|
+
'Math.random() is unavailable in workflow scripts (breaks resume). ' +
|
|
270
|
+
'For N independent samples, include the index in the agent label or prompt.'
|
|
271
|
+
);
|
|
272
|
+
};
|
|
273
|
+
globalThis.Math = safeMath;
|
|
274
|
+
|
|
275
|
+
// --- Date (vm-realm function that throws on any access) ---
|
|
276
|
+
const dateMsg = 'Date.now() / new Date() are unavailable in workflow ' +
|
|
277
|
+
'scripts (breaks resume). Stamp results after the workflow returns, ' +
|
|
278
|
+
'or pass timestamps via args.';
|
|
279
|
+
const safeDate = function Date() { throw new Error(dateMsg); };
|
|
280
|
+
safeDate.now = () => { throw new Error(dateMsg); };
|
|
281
|
+
safeDate.UTC = () => { throw new Error(dateMsg); };
|
|
282
|
+
safeDate.parse = () => { throw new Error(dateMsg); };
|
|
283
|
+
Object.setPrototypeOf(safeDate, null);
|
|
284
|
+
Object.defineProperty(safeDate, 'constructor', {
|
|
285
|
+
value: undefined, writable: false, configurable: false,
|
|
286
|
+
});
|
|
287
|
+
globalThis.Date = safeDate;
|
|
288
|
+
|
|
289
|
+
// --- args (parsed via vm-realm JSON \u2192 vm-realm objects/arrays) ---
|
|
290
|
+
// FIX-Round1-T2: vm-realm arrays keep their vm-realm Array.prototype,
|
|
291
|
+
// so for...of, .map, .forEach, spread, destructuring all work \u2014 and
|
|
292
|
+
// their inherited methods' constructors are vm-realm Function, which
|
|
293
|
+
// cannot reach host process.
|
|
294
|
+
globalThis.args = __b.argsJson === null ? undefined : JSON.parse(__b.argsJson);
|
|
295
|
+
|
|
296
|
+
// --- Wrap a host async function so it returns a vm-realm Promise ---
|
|
297
|
+
// FIX-Round1-T1/T8/T14: success and failure both cross the boundary
|
|
298
|
+
// as vm-realm values: resolve with the host's value (a primitive
|
|
299
|
+
// string for dispatch; vm-realm arrays for parallel/pipeline because
|
|
300
|
+
// those wrappers will produce vm-realm results); reject with a
|
|
301
|
+
// freshly-constructed vm-realm Error so e.constructor.constructor
|
|
302
|
+
// stays in the vm realm.
|
|
303
|
+
function vmAsync(hostFn) {
|
|
304
|
+
return function (...vmArgs) {
|
|
305
|
+
return new Promise(function (resolve, reject) {
|
|
306
|
+
try {
|
|
307
|
+
const hostPromise = hostFn.apply(null, vmArgs);
|
|
308
|
+
hostPromise.then(
|
|
309
|
+
function (value) { resolve(value); },
|
|
310
|
+
function (hostErr) {
|
|
311
|
+
const msg = (hostErr && hostErr.message != null)
|
|
312
|
+
? String(hostErr.message)
|
|
313
|
+
: String(hostErr);
|
|
314
|
+
reject(new Error(msg));
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
} catch (hostErr) {
|
|
318
|
+
const msg = (hostErr && hostErr.message != null)
|
|
319
|
+
? String(hostErr.message)
|
|
320
|
+
: String(hostErr);
|
|
321
|
+
reject(new Error(msg));
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// --- phase / log ---
|
|
328
|
+
globalThis.phase = function phase(title) {
|
|
329
|
+
__b.pushPhase(String(title));
|
|
330
|
+
};
|
|
331
|
+
globalThis.log = function log(msg) {
|
|
332
|
+
__b.pushLog(msg);
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
// --- console (object with hardened methods, all in vm-realm) ---
|
|
336
|
+
const safeConsole = Object.create(null);
|
|
337
|
+
safeConsole.log = function () {
|
|
338
|
+
const parts = [];
|
|
339
|
+
for (let i = 0; i < arguments.length; i++) parts.push(String(arguments[i]));
|
|
340
|
+
__b.pushLog(parts.join(' '));
|
|
341
|
+
};
|
|
342
|
+
safeConsole.warn = safeConsole.log;
|
|
343
|
+
safeConsole.error = safeConsole.log;
|
|
344
|
+
globalThis.console = safeConsole;
|
|
345
|
+
|
|
346
|
+
// --- agent (with runtime allowlist + named throws, all vm-realm) ---
|
|
347
|
+
// FIX-Round1-T13: throw on any opts key not in the allowlist \u2014 catches
|
|
348
|
+
// typos like { scema: ... } that previously slipped through the
|
|
349
|
+
// [key:string]: unknown index signature.
|
|
350
|
+
const KNOWN_AGENT_OPTS = ['label', 'phase', 'schema', 'model', 'isolation', 'agentType'];
|
|
351
|
+
globalThis.agent = vmAsync(function (prompt, agentOpts) {
|
|
352
|
+
agentOpts = agentOpts || {};
|
|
353
|
+
const keys = Object.keys(agentOpts);
|
|
354
|
+
for (let i = 0; i < keys.length; i++) {
|
|
355
|
+
const k = keys[i];
|
|
356
|
+
if (KNOWN_AGENT_OPTS.indexOf(k) === -1) {
|
|
357
|
+
throw new Error(
|
|
358
|
+
"agent({" + k + "}): unknown option. " +
|
|
359
|
+
"Known options are: " + KNOWN_AGENT_OPTS.join(', ') + "."
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
// P3: schema + model + agentType + isolation are all wired through
|
|
364
|
+
// createProductionDispatch \u2192 SubagentManager.createAgentHeadless.
|
|
365
|
+
// The dispatch surfaces descriptive errors for "agent type not found",
|
|
366
|
+
// "isolation:'remote' is not available in this build", parent-dirty
|
|
367
|
+
// refuse, worktree creation failures, and StructuredOutput contract
|
|
368
|
+
// violations ("completed without calling StructuredOutput after 2
|
|
369
|
+
// in-conversation nudges").
|
|
370
|
+
if (
|
|
371
|
+
agentOpts.isolation !== undefined &&
|
|
372
|
+
agentOpts.isolation !== 'worktree' &&
|
|
373
|
+
agentOpts.isolation !== 'remote'
|
|
374
|
+
) {
|
|
375
|
+
throw new Error(
|
|
376
|
+
"agent({isolation: '" + agentOpts.isolation + "'}): unknown isolation mode. " +
|
|
377
|
+
"Known modes are: 'worktree', 'remote'."
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
if (typeof agentOpts.phase === 'string' && agentOpts.phase.length > 0) {
|
|
381
|
+
if (__b.lastPhase() !== agentOpts.phase) {
|
|
382
|
+
__b.pushPhase(agentOpts.phase);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// SECURITY (P3 R2 self-review): user-script-controlled agentOpts
|
|
386
|
+
// cross the vm/host boundary verbatim via vmAsync's hostFn.apply.
|
|
387
|
+
// A Proxy / inherited-getter / non-plain object in agentOpts.schema
|
|
388
|
+
// would let host-side code (SyntheticOutputTool constructor + AJV
|
|
389
|
+
// compile) trigger user-controlled trap handlers that execute with
|
|
390
|
+
// the host realm's full surface. Revive agentOpts through JSON
|
|
391
|
+
// round-trip BEFORE crossing so the host only ever sees vm-realm
|
|
392
|
+
// plain objects with vm-realm prototypes. Same mechanism that
|
|
393
|
+
// makes args + parallel/pipeline results safe.
|
|
394
|
+
var safeOpts;
|
|
395
|
+
try {
|
|
396
|
+
safeOpts = JSON.parse(JSON.stringify(agentOpts));
|
|
397
|
+
} catch (e) {
|
|
398
|
+
throw new Error(
|
|
399
|
+
"agent() opts contain a non-JSON-serializable value: " +
|
|
400
|
+
String(e && e.message != null ? e.message : e)
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
// SECURITY (PR #4947 R1 wenshao, extended for P3): vmAsync's resolve
|
|
404
|
+
// path is verbatim (no re-wrap of resolved values). Host-realm
|
|
405
|
+
// strings cross the boundary harmlessly because primitives have no
|
|
406
|
+
// prototype identity. But P3's schema-mode dispatch returns the
|
|
407
|
+
// validated structured_output args as a host-realm OBJECT --
|
|
408
|
+
// handing that to the script reopens the T1/T8/T14 escape:
|
|
409
|
+
// result.constructor.constructor("return process")() would walk
|
|
410
|
+
// the host Object.prototype chain to the host Function
|
|
411
|
+
// constructor. Per-call JSON revival inside this vm runInContext
|
|
412
|
+
// block makes the returned object carry vm-realm prototypes (same
|
|
413
|
+
// mechanism as parallel/pipeline reviveInRealm and the args
|
|
414
|
+
// global revival). The fallback to null on a non-serializable
|
|
415
|
+
// resolve mirrors the errors-as-data convention parallel/pipeline
|
|
416
|
+
// already use for individual slot failures.
|
|
417
|
+
// R3 review (wenshao T3 [Suggestion]): the null fallback below is
|
|
418
|
+
// a SECURITY backstop, not a contract path. In schema mode the
|
|
419
|
+
// host return is the validated args of a structured_output tool
|
|
420
|
+
// call -- LLM tool_call payloads are always JSON-serializable
|
|
421
|
+
// (the model sends them through the OpenAI tool-call protocol
|
|
422
|
+
// which serializes through JSON itself) and SyntheticOutputTool's
|
|
423
|
+
// AJV validation runs over the parsed JSON, so a non-serializable
|
|
424
|
+
// host return is unreachable in production schema mode. The
|
|
425
|
+
// sentinel preserves the errors-as-data convention parallel /
|
|
426
|
+
// pipeline already use for individual slot failures, and stays as
|
|
427
|
+
// residual defense for any future dispatch path whose return
|
|
428
|
+
// value isn't a tool_call payload. logRevivalFailure surfaces
|
|
429
|
+
// the actionable detail (slot 0 + the error string) to operators
|
|
430
|
+
// so a real trigger in production isn't silent.
|
|
431
|
+
return __b.hostAgent(prompt, safeOpts).then(function (value) {
|
|
432
|
+
if (value === null || typeof value !== 'object') {
|
|
433
|
+
return value;
|
|
434
|
+
}
|
|
435
|
+
try {
|
|
436
|
+
return JSON.parse(JSON.stringify(value));
|
|
437
|
+
} catch (e) {
|
|
438
|
+
__b.logRevivalFailure(0, String(e && e.message != null ? e.message : e));
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
// --- parallel / pipeline ---
|
|
445
|
+
// SECURITY (PR #4732 P2): the host impl resolves with a HOST-realm array.
|
|
446
|
+
// vmAsync's resolve path is verbatim (it does NOT re-wrap resolved
|
|
447
|
+
// values), so handing that host array to the script would reopen the
|
|
448
|
+
// T1/T8/T14 escape: result.constructor.constructor('return process')()
|
|
449
|
+
// walks the host Array.prototype chain to the host Function constructor.
|
|
450
|
+
// We revive the array INSIDE the vm realm with JSON.parse(JSON.stringify)
|
|
451
|
+
// -- the same mechanism that makes the args global safe (see the args
|
|
452
|
+
// revival above) -- so the value the script sees has vm-realm prototypes
|
|
453
|
+
// whose constructors can't reach host process. Agent results are JSON
|
|
454
|
+
// strings (and null slots), so the round-trip is lossless for P2.
|
|
455
|
+
//
|
|
456
|
+
// EAD-1 (P2 self-review): revive PER-ELEMENT, not the whole array in one
|
|
457
|
+
// JSON.stringify. A single slot whose VALUE is non-serializable (a thunk
|
|
458
|
+
// that returns a BigInt or a circular object) must become null at its
|
|
459
|
+
// index -- it must NOT throw on the whole array and destroy every sibling
|
|
460
|
+
// result, which would defeat errors-as-data for return values. The outer
|
|
461
|
+
// [] is built in-realm here, so the result keeps vm-realm prototypes.
|
|
462
|
+
//
|
|
463
|
+
// SECURITY (PR #4947 R1 wenshao): reviveInRealm MUST remain inside this
|
|
464
|
+
// vm init runInContext block. JSON, Array, Object here are vm-realm
|
|
465
|
+
// globals; extracting this function to a host-side utility (e.g. a
|
|
466
|
+
// shared utils/jsonRevive.ts) would resolve those references against
|
|
467
|
+
// the HOST realm, silently reopening the T1/T8/T14 escape that the
|
|
468
|
+
// revival is designed to prevent. The textual identity to a host-side
|
|
469
|
+
// util is exactly the trap.
|
|
470
|
+
function reviveInRealm(hostArr) {
|
|
471
|
+
const out = [];
|
|
472
|
+
for (let i = 0; i < hostArr.length; i++) {
|
|
473
|
+
try {
|
|
474
|
+
out[i] = JSON.parse(JSON.stringify(hostArr[i]));
|
|
475
|
+
} catch (e) {
|
|
476
|
+
// Cross to host realm for debug logging. The bridge function
|
|
477
|
+
// accepts only primitive strings/numbers; the error message is
|
|
478
|
+
// coerced to a String here so no vm-realm Error object crosses.
|
|
479
|
+
__b.logRevivalFailure(i, String(e?.message ?? e));
|
|
480
|
+
out[i] = null;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
return out;
|
|
484
|
+
}
|
|
485
|
+
if (__b.hasParallel) {
|
|
486
|
+
const callParallel = vmAsync(function (thunks) {
|
|
487
|
+
return __b.hostParallel(thunks);
|
|
488
|
+
});
|
|
489
|
+
globalThis.parallel = function parallel(thunks) {
|
|
490
|
+
return callParallel(thunks).then(reviveInRealm);
|
|
491
|
+
};
|
|
492
|
+
} else {
|
|
493
|
+
globalThis.parallel = function parallel() {
|
|
494
|
+
return new Promise(function (_, reject) {
|
|
495
|
+
reject(new Error(
|
|
496
|
+
'parallel() is unavailable: this sandbox was created without a ' +
|
|
497
|
+
'parallel implementation. The orchestrator injects one; a bare ' +
|
|
498
|
+
'sandbox has no concurrent-dispatch capability.'
|
|
499
|
+
));
|
|
500
|
+
});
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
if (__b.hasPipeline) {
|
|
504
|
+
const callPipeline = vmAsync(function (items) {
|
|
505
|
+
const stages = [];
|
|
506
|
+
for (let i = 1; i < arguments.length; i++) stages.push(arguments[i]);
|
|
507
|
+
return __b.hostPipeline.apply(null, [items].concat(stages));
|
|
508
|
+
});
|
|
509
|
+
globalThis.pipeline = function pipeline() {
|
|
510
|
+
return callPipeline.apply(null, arguments).then(reviveInRealm);
|
|
511
|
+
};
|
|
512
|
+
} else {
|
|
513
|
+
globalThis.pipeline = function pipeline() {
|
|
514
|
+
return new Promise(function (_, reject) {
|
|
515
|
+
reject(new Error(
|
|
516
|
+
'pipeline() is unavailable: this sandbox was created without a ' +
|
|
517
|
+
'pipeline implementation. The orchestrator injects one; a bare ' +
|
|
518
|
+
'sandbox has no staggered multi-stage capability.'
|
|
519
|
+
));
|
|
520
|
+
});
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
// workflow() always throws in P1.
|
|
524
|
+
globalThis.workflow = function workflow() {
|
|
525
|
+
return new Promise(function (_, reject) {
|
|
526
|
+
reject(new Error(
|
|
527
|
+
'workflow() (nested workflow invocation) is not supported in P1. ' +
|
|
528
|
+
'Scheduled for a later phase.'
|
|
529
|
+
));
|
|
530
|
+
});
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
// --- budget ---
|
|
534
|
+
const safeBudget = Object.create(null);
|
|
535
|
+
Object.defineProperty(safeBudget, 'total', {
|
|
536
|
+
value: __b.budgetTotal,
|
|
537
|
+
writable: false, configurable: false,
|
|
538
|
+
});
|
|
539
|
+
if (__b.hasBudget) {
|
|
540
|
+
Object.defineProperty(safeBudget, 'spent', {
|
|
541
|
+
value: function spent() { return __b.hostBudgetSpent(); },
|
|
542
|
+
writable: false, configurable: false,
|
|
543
|
+
});
|
|
544
|
+
Object.defineProperty(safeBudget, 'remaining', {
|
|
545
|
+
value: function remaining() { return __b.hostBudgetRemaining(); },
|
|
546
|
+
writable: false, configurable: false,
|
|
547
|
+
});
|
|
548
|
+
} else {
|
|
549
|
+
Object.defineProperty(safeBudget, 'spent', {
|
|
550
|
+
value: function spent() {
|
|
551
|
+
throw new Error(
|
|
552
|
+
'budget.spent() is not supported in P1. Token tracking is scheduled for P5.'
|
|
553
|
+
);
|
|
554
|
+
},
|
|
555
|
+
writable: false, configurable: false,
|
|
556
|
+
});
|
|
557
|
+
Object.defineProperty(safeBudget, 'remaining', {
|
|
558
|
+
value: function remaining() {
|
|
559
|
+
throw new Error(
|
|
560
|
+
'budget.remaining() is not supported in P1. Token tracking is scheduled for P5.'
|
|
561
|
+
);
|
|
562
|
+
},
|
|
563
|
+
writable: false, configurable: false,
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
globalThis.budget = safeBudget;
|
|
567
|
+
})();`,
|
|
568
|
+
ctx,
|
|
569
|
+
{ filename: "workflow-sandbox-init.js" }
|
|
570
|
+
);
|
|
571
|
+
const maxWallClockMs = resolveMaxWallClockMs(opts);
|
|
572
|
+
return {
|
|
573
|
+
async run(scriptSource) {
|
|
574
|
+
const stripped = stripExportMeta(scriptSource);
|
|
575
|
+
const wrapped = `(async () => {
|
|
576
|
+
${stripped}
|
|
577
|
+
})()`;
|
|
578
|
+
const script = new vm.Script(wrapped, {
|
|
579
|
+
filename: "workflow.js"
|
|
580
|
+
});
|
|
581
|
+
const runOpts = {
|
|
582
|
+
timeout: 3e4
|
|
583
|
+
};
|
|
584
|
+
const result = script.runInContext(ctx, runOpts);
|
|
585
|
+
let timer;
|
|
586
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
587
|
+
timer = setTimeout(() => {
|
|
588
|
+
opts.abortOnTimeout?.abort();
|
|
589
|
+
reject(
|
|
590
|
+
new Error(
|
|
591
|
+
`Workflow execution timed out after ${maxWallClockMs} ms wall clock. Override via SandboxOptions.maxWallClockMs or QWEN_CODE_MAX_WORKFLOW_SECONDS env var.`
|
|
592
|
+
)
|
|
593
|
+
);
|
|
594
|
+
}, maxWallClockMs);
|
|
595
|
+
timer.unref?.();
|
|
596
|
+
});
|
|
597
|
+
try {
|
|
598
|
+
return await Promise.race([result, timeoutPromise]);
|
|
599
|
+
} finally {
|
|
600
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
getPhases: /* @__PURE__ */ __name(() => [...phases], "getPhases"),
|
|
604
|
+
getLogs: /* @__PURE__ */ __name(() => [...logs], "getLogs")
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
__name(createWorkflowSandbox, "createWorkflowSandbox");
|
|
608
|
+
|
|
609
|
+
// packages/core/src/agents/runtime/workflow-prompts.ts
|
|
610
|
+
init_esbuild_shims();
|
|
611
|
+
var WORKFLOW_SUBAGENT_SYSTEM_PROMPT = 'You are a subagent spawned by a workflow orchestration script. Use the tools available to complete the task.\nCRITICAL: Your final text response is returned **verbatim** as a string to the calling script \u2014 it is your return value, not a message to a human.\n- Output the literal result (data, JSON, text). Do NOT output confirmations like "Done." or "Sent."\n- If asked for JSON, return ONLY the raw JSON \u2014 no code fences, no prose, no markdown.\n- Do NOT use SendUserMessage to deliver your answer. Put your answer in your final text response.\n- Be concise. The script will parse your output.';
|
|
612
|
+
var WORKFLOW_SUBAGENT_SYSTEM_PROMPT_WITH_SCHEMA = "You are a subagent spawned by a workflow orchestration script. Use the tools available to complete the task.\nCRITICAL: You MUST deliver your final answer by calling the `structured_output` tool with arguments that conform to its parameter schema. Plain-text final answers are DISCARDED \u2014 only a valid `structured_output` call returns a result to the calling script.\n- Use other tools (Read, Grep, etc.) to gather information first.\n- When ready, call `structured_output` ONCE with the conforming JSON object.\n- If validation fails, the error tells you what to fix. Try again with corrected fields.\n- After two failed attempts, the run terminates \u2014 get the arguments right.\n- Do NOT use SendUserMessage to deliver your answer.\n- Be concise; the script reads only the structured payload, not your prose.";
|
|
613
|
+
|
|
614
|
+
// packages/core/src/utils/concurrencyLimiter.ts
|
|
615
|
+
init_esbuild_shims();
|
|
616
|
+
function abortError() {
|
|
617
|
+
return new DOMException("Concurrency limiter aborted.", "AbortError");
|
|
618
|
+
}
|
|
619
|
+
__name(abortError, "abortError");
|
|
620
|
+
function createConcurrencyLimiter(limit, signal) {
|
|
621
|
+
if (!Number.isInteger(limit) || limit < 1) {
|
|
622
|
+
throw new Error(
|
|
623
|
+
`Concurrency limit must be a positive integer, got ${String(limit)}.`
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
let active = 0;
|
|
627
|
+
const queue = [];
|
|
628
|
+
const pump = /* @__PURE__ */ __name(() => {
|
|
629
|
+
while (active < limit && queue.length > 0) {
|
|
630
|
+
const job = queue.shift();
|
|
631
|
+
if (signal?.aborted) {
|
|
632
|
+
job.reject(abortError());
|
|
633
|
+
continue;
|
|
634
|
+
}
|
|
635
|
+
active++;
|
|
636
|
+
Promise.resolve().then(job.thunk).then(job.resolve, job.reject).finally(() => {
|
|
637
|
+
active--;
|
|
638
|
+
pump();
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
}, "pump");
|
|
642
|
+
const run = /* @__PURE__ */ __name((thunk) => new Promise((resolve, reject) => {
|
|
643
|
+
if (signal?.aborted) {
|
|
644
|
+
reject(abortError());
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
queue.push({
|
|
648
|
+
thunk,
|
|
649
|
+
resolve,
|
|
650
|
+
reject
|
|
651
|
+
});
|
|
652
|
+
pump();
|
|
653
|
+
}), "run");
|
|
654
|
+
if (signal && !signal.aborted) {
|
|
655
|
+
signal.addEventListener(
|
|
656
|
+
"abort",
|
|
657
|
+
() => {
|
|
658
|
+
while (queue.length > 0) queue.shift().reject(abortError());
|
|
659
|
+
},
|
|
660
|
+
{ once: true }
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
return { run };
|
|
664
|
+
}
|
|
665
|
+
__name(createConcurrencyLimiter, "createConcurrencyLimiter");
|
|
666
|
+
|
|
667
|
+
// packages/core/src/agents/runtime/workflow-orchestrator.ts
|
|
668
|
+
var DEFAULT_MAX_AGENTS_PER_RUN = 1e3;
|
|
669
|
+
var MAX_WORKFLOW_AGENTS_ENV = "QWEN_CODE_MAX_WORKFLOW_AGENTS";
|
|
670
|
+
var HARD_MAX_AGENTS_PER_RUN_CEILING = 1e4;
|
|
671
|
+
function resolveMaxAgentsPerRun(env = process.env) {
|
|
672
|
+
const raw = env[MAX_WORKFLOW_AGENTS_ENV];
|
|
673
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
674
|
+
return DEFAULT_MAX_AGENTS_PER_RUN;
|
|
675
|
+
}
|
|
676
|
+
const parsed = Number(raw);
|
|
677
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
678
|
+
debugLogger.warn(
|
|
679
|
+
`Invalid ${MAX_WORKFLOW_AGENTS_ENV}=${JSON.stringify(raw)}, using default (${DEFAULT_MAX_AGENTS_PER_RUN})`
|
|
680
|
+
);
|
|
681
|
+
return DEFAULT_MAX_AGENTS_PER_RUN;
|
|
682
|
+
}
|
|
683
|
+
if (parsed > HARD_MAX_AGENTS_PER_RUN_CEILING) {
|
|
684
|
+
debugLogger.warn(
|
|
685
|
+
`${MAX_WORKFLOW_AGENTS_ENV}=${parsed} exceeds hard ceiling (${HARD_MAX_AGENTS_PER_RUN_CEILING}); clamping.`
|
|
686
|
+
);
|
|
687
|
+
return HARD_MAX_AGENTS_PER_RUN_CEILING;
|
|
688
|
+
}
|
|
689
|
+
return parsed;
|
|
690
|
+
}
|
|
691
|
+
__name(resolveMaxAgentsPerRun, "resolveMaxAgentsPerRun");
|
|
692
|
+
var MAX_WORKFLOW_CONCURRENCY_ENV = "QWEN_CODE_MAX_WORKFLOW_CONCURRENCY";
|
|
693
|
+
var HARD_MAX_CONCURRENCY_CEILING = 64;
|
|
694
|
+
function resolveConcurrencyLimit(env = process.env) {
|
|
695
|
+
const raw = env[MAX_WORKFLOW_CONCURRENCY_ENV];
|
|
696
|
+
if (raw !== void 0 && raw.trim() !== "") {
|
|
697
|
+
const parsed = Number(raw);
|
|
698
|
+
if (Number.isInteger(parsed) && parsed >= 1) {
|
|
699
|
+
if (parsed > HARD_MAX_CONCURRENCY_CEILING) {
|
|
700
|
+
debugLogger.warn(
|
|
701
|
+
`${MAX_WORKFLOW_CONCURRENCY_ENV}=${parsed} exceeds hard ceiling (${HARD_MAX_CONCURRENCY_CEILING}); clamping.`
|
|
702
|
+
);
|
|
703
|
+
return HARD_MAX_CONCURRENCY_CEILING;
|
|
704
|
+
}
|
|
705
|
+
return parsed;
|
|
706
|
+
}
|
|
707
|
+
debugLogger.warn(
|
|
708
|
+
`Invalid ${MAX_WORKFLOW_CONCURRENCY_ENV}=${JSON.stringify(raw)}, using cpu-derived default`
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
return Math.max(1, Math.min(16, os.cpus().length - 2));
|
|
712
|
+
}
|
|
713
|
+
__name(resolveConcurrencyLimit, "resolveConcurrencyLimit");
|
|
714
|
+
var WORKFLOW_SUBAGENT_MAX_TURNS = 50;
|
|
715
|
+
var WORKFLOW_SUBAGENT_MAX_TIME_MINUTES = 10;
|
|
716
|
+
var WORKFLOW_SUBAGENT_DISALLOWED_TOOLS = [
|
|
717
|
+
ToolNames.SEND_MESSAGE,
|
|
718
|
+
ToolNames.EXIT_PLAN_MODE
|
|
719
|
+
];
|
|
720
|
+
var WorkflowExecutionError = class extends Error {
|
|
721
|
+
constructor(message, phases, logs) {
|
|
722
|
+
super(message);
|
|
723
|
+
this.phases = phases;
|
|
724
|
+
this.logs = logs;
|
|
725
|
+
}
|
|
726
|
+
static {
|
|
727
|
+
__name(this, "WorkflowExecutionError");
|
|
728
|
+
}
|
|
729
|
+
name = "WorkflowExecutionError";
|
|
730
|
+
};
|
|
731
|
+
function generateRunId() {
|
|
732
|
+
return `wf_${randomBytes(8).toString("hex")}`;
|
|
733
|
+
}
|
|
734
|
+
__name(generateRunId, "generateRunId");
|
|
735
|
+
function sanitizeForErrorMessage(value) {
|
|
736
|
+
return value.replace(/[\u0000-\u001f\u007f]+/g, " ");
|
|
737
|
+
}
|
|
738
|
+
__name(sanitizeForErrorMessage, "sanitizeForErrorMessage");
|
|
739
|
+
function createProductionDispatch(config, signal) {
|
|
740
|
+
return async (prompt, opts) => {
|
|
741
|
+
const { AgentHeadless, ContextState } = await import("./agent-headless-LNRE63ZL.js");
|
|
742
|
+
const ctx = new ContextState();
|
|
743
|
+
ctx.set("task_prompt", prompt);
|
|
744
|
+
if (opts.agentType === void 0 && opts.model === void 0 && opts.isolation === void 0 && opts.schema === void 0) {
|
|
745
|
+
const subagent = await AgentHeadless.create(
|
|
746
|
+
opts.label ?? "workflow-agent",
|
|
747
|
+
config,
|
|
748
|
+
{
|
|
749
|
+
systemPrompt: WORKFLOW_SUBAGENT_SYSTEM_PROMPT,
|
|
750
|
+
initialMessages: []
|
|
751
|
+
},
|
|
752
|
+
{},
|
|
753
|
+
// T11 (PR #4732 R1): bound resource ceiling so a single agent() call
|
|
754
|
+
// cannot loop the model indefinitely. Without this, runConfig was {}
|
|
755
|
+
// and the loop guards never tripped — combined with the cancellation
|
|
756
|
+
// bug below, workflows were effectively unkillable.
|
|
757
|
+
{
|
|
758
|
+
max_turns: WORKFLOW_SUBAGENT_MAX_TURNS,
|
|
759
|
+
max_time_minutes: WORKFLOW_SUBAGENT_MAX_TIME_MINUTES
|
|
760
|
+
},
|
|
761
|
+
// T11 (PR #4732 R1): disallow SendMessage / ExitPlanMode to align with
|
|
762
|
+
// upstream Tg8 — closes the back-channel that would let a subagent
|
|
763
|
+
// deliver its answer via user message instead of the script's read.
|
|
764
|
+
{ tools: ["*"], disallowedTools: WORKFLOW_SUBAGENT_DISALLOWED_TOOLS }
|
|
765
|
+
);
|
|
766
|
+
await subagent.execute(ctx, signal);
|
|
767
|
+
const mode = subagent.getTerminateMode();
|
|
768
|
+
if (mode !== "GOAL" /* GOAL */) {
|
|
769
|
+
throw new Error(
|
|
770
|
+
`Workflow subagent did not complete (terminate mode: ${mode}).`
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
return subagent.getFinalText();
|
|
774
|
+
}
|
|
775
|
+
return runOverridePath(config, ctx, opts, signal);
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
__name(createProductionDispatch, "createProductionDispatch");
|
|
779
|
+
async function runOverridePath(config, ctx, opts, signal) {
|
|
780
|
+
if (opts.isolation === "remote") {
|
|
781
|
+
throw new Error(
|
|
782
|
+
"agent({isolation:'remote'}) is not available in this build."
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
const subagentMgr = config.getSubagentManager();
|
|
786
|
+
let baseConfig;
|
|
787
|
+
if (opts.agentType !== void 0) {
|
|
788
|
+
const resolved = await subagentMgr.findSubagentByName(opts.agentType);
|
|
789
|
+
if (!resolved) {
|
|
790
|
+
const safeAgentType = sanitizeForErrorMessage(opts.agentType);
|
|
791
|
+
throw new Error(
|
|
792
|
+
`agent({agentType}): agent type '${safeAgentType}' not found.`
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
baseConfig = resolved;
|
|
796
|
+
} else {
|
|
797
|
+
baseConfig = {
|
|
798
|
+
name: opts.label ?? "workflow-agent",
|
|
799
|
+
description: "Default workflow subagent (per-call overrides).",
|
|
800
|
+
systemPrompt: WORKFLOW_SUBAGENT_SYSTEM_PROMPT,
|
|
801
|
+
level: "session",
|
|
802
|
+
isBuiltin: false
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
let schemaSystemPrompt;
|
|
806
|
+
if (opts.schema !== void 0) {
|
|
807
|
+
schemaSystemPrompt = opts.agentType !== void 0 && baseConfig.systemPrompt ? `${baseConfig.systemPrompt}
|
|
808
|
+
|
|
809
|
+
${WORKFLOW_SUBAGENT_SYSTEM_PROMPT_WITH_SCHEMA}` : WORKFLOW_SUBAGENT_SYSTEM_PROMPT_WITH_SCHEMA;
|
|
810
|
+
}
|
|
811
|
+
let schemaTools;
|
|
812
|
+
if (opts.schema !== void 0 && baseConfig.tools && baseConfig.tools.length > 0 && !baseConfig.tools.includes("*") && !baseConfig.tools.includes(ToolNames.STRUCTURED_OUTPUT)) {
|
|
813
|
+
schemaTools = [...baseConfig.tools, ToolNames.STRUCTURED_OUTPUT];
|
|
814
|
+
}
|
|
815
|
+
const augmented = {
|
|
816
|
+
...baseConfig,
|
|
817
|
+
...opts.model !== void 0 ? { model: opts.model } : {},
|
|
818
|
+
...schemaSystemPrompt !== void 0 ? { systemPrompt: schemaSystemPrompt } : {},
|
|
819
|
+
...schemaTools !== void 0 ? { tools: schemaTools } : {},
|
|
820
|
+
disallowedTools: Array.from(
|
|
821
|
+
/* @__PURE__ */ new Set([
|
|
822
|
+
...baseConfig.disallowedTools ?? [],
|
|
823
|
+
...WORKFLOW_SUBAGENT_DISALLOWED_TOOLS
|
|
824
|
+
])
|
|
825
|
+
)
|
|
826
|
+
};
|
|
827
|
+
let worktreeIsolation = null;
|
|
828
|
+
let effectiveContext = config;
|
|
829
|
+
if (opts.isolation === "worktree") {
|
|
830
|
+
worktreeIsolation = await provisionWorkflowWorktree(config);
|
|
831
|
+
effectiveContext = createWorktreeConfigOverride(
|
|
832
|
+
config,
|
|
833
|
+
worktreeIsolation.path
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
let onParentAbort;
|
|
837
|
+
try {
|
|
838
|
+
let schemaState = null;
|
|
839
|
+
if (opts.schema !== void 0) {
|
|
840
|
+
effectiveContext = await createSchemaConfigOverride(
|
|
841
|
+
effectiveContext,
|
|
842
|
+
opts.schema
|
|
843
|
+
);
|
|
844
|
+
schemaState = createSchemaModeState();
|
|
845
|
+
}
|
|
846
|
+
let dispatchSignal = signal;
|
|
847
|
+
if (schemaState) {
|
|
848
|
+
const child = new AbortController();
|
|
849
|
+
if (signal) {
|
|
850
|
+
if (signal.aborted) {
|
|
851
|
+
child.abort(signal.reason);
|
|
852
|
+
} else {
|
|
853
|
+
onParentAbort = /* @__PURE__ */ __name(() => child.abort(signal.reason), "onParentAbort");
|
|
854
|
+
signal.addEventListener("abort", onParentAbort);
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
schemaState.abortController = child;
|
|
858
|
+
dispatchSignal = child.signal;
|
|
859
|
+
}
|
|
860
|
+
const eventEmitter = schemaState ? createSchemaEventEmitter(schemaState) : void 0;
|
|
861
|
+
const { subagent, dispose } = await subagentMgr.createAgentHeadless(
|
|
862
|
+
augmented,
|
|
863
|
+
effectiveContext,
|
|
864
|
+
{
|
|
865
|
+
// Workflow always bounds resource ceiling regardless of agentType's
|
|
866
|
+
// own runConfig / maxTurns — these are workflow-level safety bounds,
|
|
867
|
+
// not subagent-level preferences. P5 will refine via budget.
|
|
868
|
+
runConfigOverrides: {
|
|
869
|
+
max_turns: WORKFLOW_SUBAGENT_MAX_TURNS,
|
|
870
|
+
max_time_minutes: WORKFLOW_SUBAGENT_MAX_TIME_MINUTES
|
|
871
|
+
},
|
|
872
|
+
eventEmitter
|
|
873
|
+
}
|
|
874
|
+
);
|
|
875
|
+
try {
|
|
876
|
+
await subagent.execute(ctx, dispatchSignal);
|
|
877
|
+
if (schemaState) {
|
|
878
|
+
if (schemaState.result !== null) {
|
|
879
|
+
if (worktreeIsolation) {
|
|
880
|
+
const isolation = worktreeIsolation;
|
|
881
|
+
worktreeIsolation = null;
|
|
882
|
+
const preserved = await cleanupWorkflowWorktree(isolation);
|
|
883
|
+
if (preserved) {
|
|
884
|
+
debugLogger.info(
|
|
885
|
+
`[Workflow] Schema-mode subagent preserved worktree at ${preserved.path ?? "(directory removed)"} on branch ${preserved.branch}. The structured payload is returned verbatim; recover the work from the preserved path / branch.`
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
return schemaState.result;
|
|
890
|
+
}
|
|
891
|
+
if (signal?.aborted) {
|
|
892
|
+
throw new DOMException("Workflow aborted.", "AbortError");
|
|
893
|
+
}
|
|
894
|
+
const mode2 = subagent.getTerminateMode();
|
|
895
|
+
if (mode2 !== "GOAL" /* GOAL */ && mode2 !== "CANCELLED" /* CANCELLED */) {
|
|
896
|
+
throw new Error(
|
|
897
|
+
`Workflow subagent did not complete (terminate mode: ${mode2}).`
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
if (schemaState.attempts > 2) {
|
|
901
|
+
throw new Error(
|
|
902
|
+
"subagent completed without calling StructuredOutput (after 2 in-conversation nudges)."
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
throw new Error(
|
|
906
|
+
"subagent completed without calling structured_output (no validation attempt \u2014 model produced plain-text content)."
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
const mode = subagent.getTerminateMode();
|
|
910
|
+
if (mode !== "GOAL" /* GOAL */) {
|
|
911
|
+
throw new Error(
|
|
912
|
+
`Workflow subagent did not complete (terminate mode: ${mode}).`
|
|
913
|
+
);
|
|
914
|
+
}
|
|
915
|
+
let finalText = subagent.getFinalText();
|
|
916
|
+
if (worktreeIsolation) {
|
|
917
|
+
const isolation = worktreeIsolation;
|
|
918
|
+
worktreeIsolation = null;
|
|
919
|
+
const preserved = await cleanupWorkflowWorktree(isolation);
|
|
920
|
+
if (preserved && typeof finalText === "string") {
|
|
921
|
+
finalText = appendWorktreePreservedSuffix(finalText, preserved);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
return finalText;
|
|
925
|
+
} finally {
|
|
926
|
+
await dispose();
|
|
927
|
+
}
|
|
928
|
+
} finally {
|
|
929
|
+
if (onParentAbort && signal) {
|
|
930
|
+
signal.removeEventListener("abort", onParentAbort);
|
|
931
|
+
}
|
|
932
|
+
if (worktreeIsolation) {
|
|
933
|
+
try {
|
|
934
|
+
await cleanupWorkflowWorktree(worktreeIsolation);
|
|
935
|
+
} catch (error) {
|
|
936
|
+
debugLogger.warn(
|
|
937
|
+
`Workflow worktree cleanup in fallback finally failed for ${worktreeIsolation.path}: ${error instanceof Error ? error.message : String(error)}`
|
|
938
|
+
);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
__name(runOverridePath, "runOverridePath");
|
|
944
|
+
async function provisionWorkflowWorktree(config) {
|
|
945
|
+
const cwd = config.getTargetDir();
|
|
946
|
+
if (/\.qwen[\\/]worktrees[\\/]/.test(cwd)) {
|
|
947
|
+
throw new Error(
|
|
948
|
+
`agent({isolation:'worktree'}): parent is already inside a worktree (${cwd}). Nested isolation worktrees are not supported \u2014 the subagent's inherited paths would still reference the outer worktree.`
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
const probe = new GitWorktreeService(cwd);
|
|
952
|
+
const gitCheck = await probe.checkGitAvailable();
|
|
953
|
+
if (!gitCheck.available) {
|
|
954
|
+
throw new Error(
|
|
955
|
+
`agent({isolation:'worktree'}): ${gitCheck.error ?? "git is not available"}.`
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
if (!await probe.isGitRepository()) {
|
|
959
|
+
throw new Error(
|
|
960
|
+
`agent({isolation:'worktree'}): ${cwd} is not a git repository.`
|
|
961
|
+
);
|
|
962
|
+
}
|
|
963
|
+
const projectRoot = await probe.getRepoTopLevel() ?? cwd;
|
|
964
|
+
const wtService = projectRoot === cwd ? probe : new GitWorktreeService(projectRoot);
|
|
965
|
+
let parentDirty = false;
|
|
966
|
+
try {
|
|
967
|
+
parentDirty = await wtService.hasWorktreeChanges(projectRoot);
|
|
968
|
+
} catch (error) {
|
|
969
|
+
debugLogger.warn(
|
|
970
|
+
`[Workflow] hasWorktreeChanges failed at ${projectRoot}: ${error}`
|
|
971
|
+
);
|
|
972
|
+
parentDirty = true;
|
|
973
|
+
}
|
|
974
|
+
if (parentDirty) {
|
|
975
|
+
throw new Error(
|
|
976
|
+
`agent({isolation:'worktree'}): parent working tree at ${projectRoot} has uncommitted changes that would not propagate into the isolated worktree. The subagent would see the prior HEAD instead of the current state. Commit or stash the changes, then re-run.`
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
const slug = generateAgentWorktreeSlug();
|
|
980
|
+
let parentBranch;
|
|
981
|
+
try {
|
|
982
|
+
parentBranch = await wtService.getCurrentBranch();
|
|
983
|
+
} catch (error) {
|
|
984
|
+
debugLogger.warn(
|
|
985
|
+
`[Workflow] getCurrentBranch failed at ${projectRoot}: ${error}`
|
|
986
|
+
);
|
|
987
|
+
}
|
|
988
|
+
const created = await wtService.createUserWorktree(slug, parentBranch, {
|
|
989
|
+
symlinkDirectories: config.getWorktreeSymlinkDirectories()
|
|
990
|
+
});
|
|
991
|
+
if (!created.success || !created.worktree) {
|
|
992
|
+
throw new Error(
|
|
993
|
+
`agent({isolation:'worktree'}): failed to create worktree: ${created.error ?? "unknown error"}.`
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
try {
|
|
997
|
+
await writeWorktreeSessionMarker(
|
|
998
|
+
created.worktree.path,
|
|
999
|
+
config.getSessionId()
|
|
1000
|
+
);
|
|
1001
|
+
} catch (error) {
|
|
1002
|
+
debugLogger.warn(
|
|
1003
|
+
`[Workflow] failed to write session marker at ${created.worktree.path}: ${error}`
|
|
1004
|
+
);
|
|
1005
|
+
}
|
|
1006
|
+
return {
|
|
1007
|
+
slug,
|
|
1008
|
+
path: created.worktree.path,
|
|
1009
|
+
branch: created.worktree.branch,
|
|
1010
|
+
repoRoot: projectRoot
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
__name(provisionWorkflowWorktree, "provisionWorkflowWorktree");
|
|
1014
|
+
function createWorktreeConfigOverride(base, wtPath) {
|
|
1015
|
+
const ov = Object.create(base);
|
|
1016
|
+
ov.targetDir = wtPath;
|
|
1017
|
+
ov.cwd = wtPath;
|
|
1018
|
+
ov.getTargetDir = () => wtPath;
|
|
1019
|
+
ov.getCwd = () => wtPath;
|
|
1020
|
+
ov.getWorkingDir = () => wtPath;
|
|
1021
|
+
ov.getProjectRoot = () => wtPath;
|
|
1022
|
+
const wtFileService = new FileDiscoveryService(wtPath);
|
|
1023
|
+
ov.fileDiscoveryService = wtFileService;
|
|
1024
|
+
ov.getFileService = () => wtFileService;
|
|
1025
|
+
const wtWorkspace = new WorkspaceContext(wtPath);
|
|
1026
|
+
ov.workspaceContext = wtWorkspace;
|
|
1027
|
+
ov.getWorkspaceContext = () => wtWorkspace;
|
|
1028
|
+
return ov;
|
|
1029
|
+
}
|
|
1030
|
+
__name(createWorktreeConfigOverride, "createWorktreeConfigOverride");
|
|
1031
|
+
async function cleanupWorkflowWorktree(isolation) {
|
|
1032
|
+
const wtService = new GitWorktreeService(isolation.repoRoot);
|
|
1033
|
+
const [hasChanges, hasUnmerged] = await Promise.all([
|
|
1034
|
+
wtService.hasWorktreeChanges(isolation.path).catch((error) => {
|
|
1035
|
+
debugLogger.warn(
|
|
1036
|
+
`[Workflow] hasWorktreeChanges failed for ${isolation.path}: ${error}`
|
|
1037
|
+
);
|
|
1038
|
+
return true;
|
|
1039
|
+
}),
|
|
1040
|
+
wtService.hasUnmergedWorktreeCommits(isolation.slug).catch((error) => {
|
|
1041
|
+
debugLogger.warn(
|
|
1042
|
+
`[Workflow] hasUnmergedWorktreeCommits failed for ${isolation.slug}: ${error}`
|
|
1043
|
+
);
|
|
1044
|
+
return true;
|
|
1045
|
+
})
|
|
1046
|
+
]);
|
|
1047
|
+
if (hasChanges || hasUnmerged) {
|
|
1048
|
+
debugLogger.info(
|
|
1049
|
+
`[Workflow] Preserving isolation worktree ${isolation.path} (branch ${isolation.branch}, hasChanges=${hasChanges}, hasUnmerged=${hasUnmerged})`
|
|
1050
|
+
);
|
|
1051
|
+
return { path: isolation.path, branch: isolation.branch };
|
|
1052
|
+
}
|
|
1053
|
+
try {
|
|
1054
|
+
const result = await wtService.removeUserWorktree(isolation.slug, {
|
|
1055
|
+
deleteBranch: true
|
|
1056
|
+
});
|
|
1057
|
+
if (!result.success) {
|
|
1058
|
+
debugLogger.warn(
|
|
1059
|
+
`[Workflow] Failed to remove ephemeral worktree ${isolation.path}: ${result.error}`
|
|
1060
|
+
);
|
|
1061
|
+
return { path: isolation.path, branch: isolation.branch };
|
|
1062
|
+
}
|
|
1063
|
+
if (result.branchPreserved) {
|
|
1064
|
+
debugLogger.warn(
|
|
1065
|
+
`[Workflow] Removed worktree directory ${isolation.path} but kept branch ${isolation.branch} (unmerged commits at delete time)`
|
|
1066
|
+
);
|
|
1067
|
+
return { branch: isolation.branch };
|
|
1068
|
+
}
|
|
1069
|
+
} catch (error) {
|
|
1070
|
+
debugLogger.warn(
|
|
1071
|
+
`[Workflow] Failed to remove ephemeral worktree ${isolation.path}: ${error}`
|
|
1072
|
+
);
|
|
1073
|
+
return { path: isolation.path, branch: isolation.branch };
|
|
1074
|
+
}
|
|
1075
|
+
return null;
|
|
1076
|
+
}
|
|
1077
|
+
__name(cleanupWorkflowWorktree, "cleanupWorkflowWorktree");
|
|
1078
|
+
function appendWorktreePreservedSuffix(finalText, preserved) {
|
|
1079
|
+
const sep = finalText.endsWith("\n") ? "\n" : "\n\n";
|
|
1080
|
+
if (preserved.path) {
|
|
1081
|
+
return `${finalText}${sep}[worktree preserved: ${preserved.path} (branch ${preserved.branch})]`;
|
|
1082
|
+
}
|
|
1083
|
+
return `${finalText}${sep}[worktree directory removed; branch ${preserved.branch} preserved \u2014 recover with \`git worktree add <path> ${preserved.branch}\`]`;
|
|
1084
|
+
}
|
|
1085
|
+
__name(appendWorktreePreservedSuffix, "appendWorktreePreservedSuffix");
|
|
1086
|
+
function createSchemaModeState() {
|
|
1087
|
+
return {
|
|
1088
|
+
result: null,
|
|
1089
|
+
attempts: 0,
|
|
1090
|
+
pendingArgs: /* @__PURE__ */ new Map(),
|
|
1091
|
+
abortController: new AbortController()
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
__name(createSchemaModeState, "createSchemaModeState");
|
|
1095
|
+
function createSchemaEventEmitter(state) {
|
|
1096
|
+
const emitter = new AgentEventEmitter();
|
|
1097
|
+
const targetTool = ToolNames.STRUCTURED_OUTPUT;
|
|
1098
|
+
emitter.on("tool_call" /* TOOL_CALL */, (evt) => {
|
|
1099
|
+
if (evt.name !== targetTool) return;
|
|
1100
|
+
state.pendingArgs.set(evt.callId, evt.args);
|
|
1101
|
+
});
|
|
1102
|
+
emitter.on("tool_result" /* TOOL_RESULT */, (evt) => {
|
|
1103
|
+
if (evt.name !== targetTool) return;
|
|
1104
|
+
const args = state.pendingArgs.get(evt.callId);
|
|
1105
|
+
state.pendingArgs.delete(evt.callId);
|
|
1106
|
+
if (evt.success) {
|
|
1107
|
+
if (args !== void 0 && state.result === null) {
|
|
1108
|
+
state.result = args;
|
|
1109
|
+
state.abortController.abort();
|
|
1110
|
+
}
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
state.attempts += 1;
|
|
1114
|
+
if (state.attempts > 2 && state.result === null) {
|
|
1115
|
+
state.abortController.abort();
|
|
1116
|
+
}
|
|
1117
|
+
});
|
|
1118
|
+
return emitter;
|
|
1119
|
+
}
|
|
1120
|
+
__name(createSchemaEventEmitter, "createSchemaEventEmitter");
|
|
1121
|
+
async function createSchemaConfigOverride(base, schema) {
|
|
1122
|
+
const override = Object.create(base);
|
|
1123
|
+
await rebuildToolRegistryOnOverride(override, base);
|
|
1124
|
+
const registry = override.getToolRegistry();
|
|
1125
|
+
registry.registerTool(new SyntheticOutputTool(schema));
|
|
1126
|
+
return override;
|
|
1127
|
+
}
|
|
1128
|
+
__name(createSchemaConfigOverride, "createSchemaConfigOverride");
|
|
1129
|
+
var WorkflowOrchestrator = class {
|
|
1130
|
+
constructor(dispatch) {
|
|
1131
|
+
this.dispatch = dispatch;
|
|
1132
|
+
}
|
|
1133
|
+
static {
|
|
1134
|
+
__name(this, "WorkflowOrchestrator");
|
|
1135
|
+
}
|
|
1136
|
+
async run(req) {
|
|
1137
|
+
const runId = generateRunId();
|
|
1138
|
+
const maxAgents = resolveMaxAgentsPerRun();
|
|
1139
|
+
const signal = req.abortOnTimeout?.signal;
|
|
1140
|
+
const limiter = createConcurrencyLimiter(resolveConcurrencyLimit(), signal);
|
|
1141
|
+
let agentCount = 0;
|
|
1142
|
+
const countedDispatch = /* @__PURE__ */ __name((prompt, opts) => {
|
|
1143
|
+
agentCount += 1;
|
|
1144
|
+
if (agentCount > maxAgents) {
|
|
1145
|
+
return Promise.reject(
|
|
1146
|
+
new Error(
|
|
1147
|
+
`Workflow exceeded the maximum of ${maxAgents} agent() calls per run.`
|
|
1148
|
+
)
|
|
1149
|
+
);
|
|
1150
|
+
}
|
|
1151
|
+
return limiter.run(() => this.dispatch(prompt, opts));
|
|
1152
|
+
}, "countedDispatch");
|
|
1153
|
+
const parallelImpl = makeParallelImpl(signal);
|
|
1154
|
+
const pipelineImpl = makePipelineImpl(signal);
|
|
1155
|
+
const sandbox = createWorkflowSandbox({
|
|
1156
|
+
args: req.args,
|
|
1157
|
+
dispatch: countedDispatch,
|
|
1158
|
+
parallel: parallelImpl,
|
|
1159
|
+
pipeline: pipelineImpl,
|
|
1160
|
+
abortOnTimeout: req.abortOnTimeout
|
|
1161
|
+
});
|
|
1162
|
+
try {
|
|
1163
|
+
const result = await sandbox.run(req.script);
|
|
1164
|
+
return {
|
|
1165
|
+
runId,
|
|
1166
|
+
result,
|
|
1167
|
+
phases: sandbox.getPhases(),
|
|
1168
|
+
logs: sandbox.getLogs()
|
|
1169
|
+
};
|
|
1170
|
+
} catch (err) {
|
|
1171
|
+
throw new WorkflowExecutionError(
|
|
1172
|
+
extractErrorMessage(err),
|
|
1173
|
+
sandbox.getPhases(),
|
|
1174
|
+
sandbox.getLogs()
|
|
1175
|
+
);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
};
|
|
1179
|
+
async function settleToNullArray(thunks, signal) {
|
|
1180
|
+
const settled = await Promise.allSettled(
|
|
1181
|
+
thunks.map((t) => Promise.resolve().then(t))
|
|
1182
|
+
);
|
|
1183
|
+
if (signal?.aborted)
|
|
1184
|
+
throw new DOMException("Workflow run aborted.", "AbortError");
|
|
1185
|
+
return settled.map((r, i) => {
|
|
1186
|
+
if (r.status === "fulfilled") return r.value;
|
|
1187
|
+
debugLogger.warn(
|
|
1188
|
+
`Workflow thunk at index ${i} rejected: ${String(r.reason?.message ?? r.reason)}`
|
|
1189
|
+
);
|
|
1190
|
+
return null;
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
__name(settleToNullArray, "settleToNullArray");
|
|
1194
|
+
function makeParallelImpl(signal) {
|
|
1195
|
+
return (thunks) => {
|
|
1196
|
+
if (!Array.isArray(thunks)) {
|
|
1197
|
+
return Promise.reject(
|
|
1198
|
+
new Error(
|
|
1199
|
+
"parallel() expects an array of thunks (functions returning promises)."
|
|
1200
|
+
)
|
|
1201
|
+
);
|
|
1202
|
+
}
|
|
1203
|
+
for (const t of thunks) {
|
|
1204
|
+
if (typeof t !== "function") {
|
|
1205
|
+
return Promise.reject(
|
|
1206
|
+
new Error(
|
|
1207
|
+
"parallel() expects an array of functions, not values \u2014 wrap each call: parallel([() => agent(...), () => agent(...)])."
|
|
1208
|
+
)
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
return settleToNullArray(thunks, signal);
|
|
1213
|
+
};
|
|
1214
|
+
}
|
|
1215
|
+
__name(makeParallelImpl, "makeParallelImpl");
|
|
1216
|
+
function makePipelineImpl(signal) {
|
|
1217
|
+
return (items, ...stages) => {
|
|
1218
|
+
if (!Array.isArray(items)) {
|
|
1219
|
+
return Promise.reject(
|
|
1220
|
+
new Error(
|
|
1221
|
+
"pipeline() expects an array of items as its first argument."
|
|
1222
|
+
)
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
for (const s of stages) {
|
|
1226
|
+
if (typeof s !== "function") {
|
|
1227
|
+
return Promise.reject(
|
|
1228
|
+
new Error(
|
|
1229
|
+
"pipeline() stages must be functions: pipeline(items, item => ..., result => ...)."
|
|
1230
|
+
)
|
|
1231
|
+
);
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
const chains = items.map(
|
|
1235
|
+
(item, idx) => () => runPipelineChain(item, idx, stages)
|
|
1236
|
+
);
|
|
1237
|
+
return settleToNullArray(chains, signal);
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
__name(makePipelineImpl, "makePipelineImpl");
|
|
1241
|
+
async function runPipelineChain(item, idx, stages) {
|
|
1242
|
+
let prev = item;
|
|
1243
|
+
for (const stage of stages) {
|
|
1244
|
+
if (prev === null) break;
|
|
1245
|
+
prev = await stage(prev, item, idx);
|
|
1246
|
+
}
|
|
1247
|
+
return prev;
|
|
1248
|
+
}
|
|
1249
|
+
__name(runPipelineChain, "runPipelineChain");
|
|
1250
|
+
function extractErrorMessage(err) {
|
|
1251
|
+
if (err && typeof err === "object" && "message" in err) {
|
|
1252
|
+
const m = err.message;
|
|
1253
|
+
if (typeof m === "string") return m;
|
|
1254
|
+
return String(m);
|
|
1255
|
+
}
|
|
1256
|
+
return String(err);
|
|
1257
|
+
}
|
|
1258
|
+
__name(extractErrorMessage, "extractErrorMessage");
|
|
1259
|
+
|
|
1260
|
+
// packages/core/src/tools/workflow/workflow.ts
|
|
1261
|
+
var WORKFLOW_PARAM_SCHEMA = {
|
|
1262
|
+
type: "object",
|
|
1263
|
+
properties: {
|
|
1264
|
+
script: {
|
|
1265
|
+
type: "string",
|
|
1266
|
+
description: "JavaScript source of the workflow. Wrapped as an async IIFE. May call the injected globals `phase(title)`, `log(msg)`, `agent(prompt, opts?)`, and read `args`. agent() opts: `{ label?, phase?, schema?, model?, agentType?, isolation? }`. `schema` (JSON Schema object): the subagent must deliver its result by calling `structured_output` with arguments matching the schema; agent() resolves to the validated object. Two failed attempts produce a terminal error \"subagent completed without calling StructuredOutput (after 2 in-conversation nudges)\". `agentType` (string): resolves against the declarative-agents registry (`.qwen/agents/<name>.md`, project then user then built-in). Unresolved names throw \"agent({agentType}): agent type 'X' not found\". `model` (string): per-call model override; routes provider correctly via the subagent runtime view. `isolation`: `'worktree'` provisions a fresh git worktree under `<projectRoot>/.qwen/worktrees/agent-<7hex>`; the worktree is auto-removed if no changes, otherwise the path and branch are returned alongside the result. `'remote'` throws \"agent({isolation:'remote'}) is not available in this build\" (parity with upstream). isolation=worktree refuses to run when the parent working tree has uncommitted changes (the subagent would see a stale HEAD). Workflow subagents always have SendMessage / ExitPlanMode in their disallowed-tool floor regardless of agentType. Concurrency: `parallel([() => agent(...), ...])` runs thunks through a shared per-run window (default `max(1, min(16, cpus-2))` agents in flight; override via `QWEN_CODE_MAX_WORKFLOW_CONCURRENCY`) and resolves to a position-aligned array \u2014 a thunk that throws, or resolves to a non-JSON-serializable value, becomes `null` at its index (errors-as-data); parallel() itself rejects only on invalid arguments or abort. `pipeline(items, ...stages)` runs each item through the stages (staggered, no inter-stage barrier); a stage that throws, returns `null`, or returns a non-JSON-serializable value drops that item to `null`. Pass THUNKS to parallel, not eager calls: `parallel([() => agent(...)])`, not `parallel([agent(...)])`. At most 1000 agent() calls per run (override via `QWEN_CODE_MAX_WORKFLOW_AGENTS`). `Date.now()` and `Math.random()` both throw \u2014 workflow scripts must be deterministic for resume. `export const meta = {...}` declarations are stripped before execution."
|
|
1267
|
+
},
|
|
1268
|
+
args: {
|
|
1269
|
+
description: "Optional structured value bound to the `args` global. Pass actual JSON, not a stringified value."
|
|
1270
|
+
}
|
|
1271
|
+
},
|
|
1272
|
+
required: ["script"]
|
|
1273
|
+
};
|
|
1274
|
+
var WorkflowToolInvocation = class extends BaseToolInvocation {
|
|
1275
|
+
constructor(config, toolOptions, params) {
|
|
1276
|
+
super(params);
|
|
1277
|
+
this.config = config;
|
|
1278
|
+
this.toolOptions = toolOptions;
|
|
1279
|
+
}
|
|
1280
|
+
static {
|
|
1281
|
+
__name(this, "WorkflowToolInvocation");
|
|
1282
|
+
}
|
|
1283
|
+
getDescription() {
|
|
1284
|
+
return `Run a workflow script (${this.params.script.length} chars)`;
|
|
1285
|
+
}
|
|
1286
|
+
toolLocations() {
|
|
1287
|
+
return [];
|
|
1288
|
+
}
|
|
1289
|
+
getDefaultPermission() {
|
|
1290
|
+
return Promise.resolve("ask");
|
|
1291
|
+
}
|
|
1292
|
+
async execute(signal, _updateOutput, _shellExecutionConfig) {
|
|
1293
|
+
const dispatchController = createChildAbortController(signal);
|
|
1294
|
+
const dispatch = this.toolOptions.dispatch ?? createProductionDispatch(this.config, dispatchController.signal);
|
|
1295
|
+
const orchestrator = new WorkflowOrchestrator(dispatch);
|
|
1296
|
+
try {
|
|
1297
|
+
const outcome = await orchestrator.run({
|
|
1298
|
+
script: this.params.script,
|
|
1299
|
+
args: this.params.args,
|
|
1300
|
+
abortOnTimeout: dispatchController
|
|
1301
|
+
});
|
|
1302
|
+
const llmText = safeStringifyResult(outcome.result);
|
|
1303
|
+
const displayJson = safeStringifyDisplayPayload({
|
|
1304
|
+
runId: outcome.runId,
|
|
1305
|
+
phases: outcome.phases,
|
|
1306
|
+
logs: outcome.logs,
|
|
1307
|
+
result: outcome.result
|
|
1308
|
+
});
|
|
1309
|
+
return {
|
|
1310
|
+
llmContent: [{ text: llmText }],
|
|
1311
|
+
returnDisplay: "```json\n" + displayJson + "\n```"
|
|
1312
|
+
};
|
|
1313
|
+
} catch (err) {
|
|
1314
|
+
const message = extractErrorMessage2(err);
|
|
1315
|
+
const phases = err instanceof WorkflowExecutionError ? err.phases : void 0;
|
|
1316
|
+
const logs = err instanceof WorkflowExecutionError ? err.logs : void 0;
|
|
1317
|
+
const display = phases || logs ? `Workflow failed: ${message}
|
|
1318
|
+
|
|
1319
|
+
${safeStringifyDisplayPayload({
|
|
1320
|
+
phases: phases ?? [],
|
|
1321
|
+
logs: logs ?? []
|
|
1322
|
+
})}` : `Workflow failed: ${message}`;
|
|
1323
|
+
return {
|
|
1324
|
+
llmContent: [{ text: `Workflow failed: ${message}` }],
|
|
1325
|
+
returnDisplay: display,
|
|
1326
|
+
// FIX-10 (REUSE-I1): use the standard ToolErrorType.EXECUTION_FAILED
|
|
1327
|
+
// code so error routing / dashboards can classify workflow failures
|
|
1328
|
+
// the same way as other execution-time tool errors.
|
|
1329
|
+
error: { message, type: "execution_failed" /* EXECUTION_FAILED */ }
|
|
1330
|
+
};
|
|
1331
|
+
} finally {
|
|
1332
|
+
dispatchController.abort();
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
function safeStringifyResult(result) {
|
|
1337
|
+
if (result === void 0) return "(workflow returned no value)";
|
|
1338
|
+
if (typeof result === "string") return result;
|
|
1339
|
+
try {
|
|
1340
|
+
return JSON.stringify(result, null, 2);
|
|
1341
|
+
} catch {
|
|
1342
|
+
return `(workflow returned a non-JSON-serializable value of type ${typeof result})`;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
__name(safeStringifyResult, "safeStringifyResult");
|
|
1346
|
+
function safeStringifyDisplayPayload(payload) {
|
|
1347
|
+
try {
|
|
1348
|
+
return JSON.stringify(payload, null, 2);
|
|
1349
|
+
} catch {
|
|
1350
|
+
if (payload && typeof payload === "object") {
|
|
1351
|
+
const sanitized = {};
|
|
1352
|
+
for (const [key, value] of Object.entries(payload)) {
|
|
1353
|
+
try {
|
|
1354
|
+
JSON.stringify(value);
|
|
1355
|
+
sanitized[key] = value;
|
|
1356
|
+
} catch {
|
|
1357
|
+
sanitized[key] = `(non-JSON-serializable value of type ${typeof value})`;
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
try {
|
|
1361
|
+
return JSON.stringify(sanitized, null, 2);
|
|
1362
|
+
} catch {
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
return "(display payload not JSON-serializable)";
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
__name(safeStringifyDisplayPayload, "safeStringifyDisplayPayload");
|
|
1369
|
+
function extractErrorMessage2(err) {
|
|
1370
|
+
if (err && typeof err === "object" && "message" in err) {
|
|
1371
|
+
const m = err.message;
|
|
1372
|
+
if (typeof m === "string") return m;
|
|
1373
|
+
return String(m);
|
|
1374
|
+
}
|
|
1375
|
+
return String(err);
|
|
1376
|
+
}
|
|
1377
|
+
__name(extractErrorMessage2, "extractErrorMessage");
|
|
1378
|
+
var WorkflowTool = class extends BaseDeclarativeTool {
|
|
1379
|
+
constructor(config, toolOptions = {}) {
|
|
1380
|
+
super(
|
|
1381
|
+
ToolNames.WORKFLOW,
|
|
1382
|
+
ToolDisplayNames.WORKFLOW,
|
|
1383
|
+
"Execute a workflow script that orchestrates subagents. Supports `phase`, `log`, sequential `agent`, concurrent fan-out via `parallel(thunks)` / `pipeline(items, ...stages)` (default `max(1, min(16, cpus-2))` agents in flight per run, up to 1000 agents total; both env-overridable), per-call `agent({ schema, agentType, model, isolation: 'worktree' })` for structured-output contracts, declarative-agent selection, model override, and git-worktree-isolated subagents. No resume and no background execution yet (scheduled for later phases). Scripts run in a node:vm sandbox without access to the filesystem or shell; all I/O happens through the spawned agents.",
|
|
1384
|
+
"other" /* Other */,
|
|
1385
|
+
WORKFLOW_PARAM_SCHEMA,
|
|
1386
|
+
/* isOutputMarkdown */
|
|
1387
|
+
true,
|
|
1388
|
+
/* canUpdateOutput */
|
|
1389
|
+
false
|
|
1390
|
+
);
|
|
1391
|
+
this.config = config;
|
|
1392
|
+
this.toolOptions = toolOptions;
|
|
1393
|
+
}
|
|
1394
|
+
static {
|
|
1395
|
+
__name(this, "WorkflowTool");
|
|
1396
|
+
}
|
|
1397
|
+
validateToolParamValues(params) {
|
|
1398
|
+
if (typeof params.script !== "string" || params.script.length === 0) {
|
|
1399
|
+
return "WorkflowTool: `script` parameter is required and must be a non-empty string.";
|
|
1400
|
+
}
|
|
1401
|
+
return null;
|
|
1402
|
+
}
|
|
1403
|
+
createInvocation(params) {
|
|
1404
|
+
return new WorkflowToolInvocation(this.config, this.toolOptions, params);
|
|
1405
|
+
}
|
|
1406
|
+
};
|
|
1407
|
+
export {
|
|
1408
|
+
WorkflowTool
|
|
1409
|
+
};
|
|
1410
|
+
/**
|
|
1411
|
+
* @license
|
|
1412
|
+
* Copyright 2025 Qwen
|
|
1413
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
1414
|
+
*/
|