@qwen-code/qwen-code 0.18.5-nightly.20260622.6bc3f853e → 0.19.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.
- package/bundled/qc-helper/docs/configuration/qwen-ignore.md +31 -15
- package/bundled/qc-helper/docs/configuration/settings.md +15 -14
- package/bundled/qc-helper/docs/extension/introduction.md +24 -6
- package/bundled/qc-helper/docs/features/mcp.md +9 -6
- package/bundled/qc-helper/docs/qwen-serve.md +1 -1
- package/chunks/{agent-WFLPE5MW.js → agent-IPSDEZNB.js} +24 -22
- package/chunks/{agent-headless-Q27MG73I.js → agent-headless-ZMTUWMZJ.js} +24 -22
- package/chunks/{anthropicContentGenerator-5S4OMMNL.js → anthropicContentGenerator-6MCH4X6S.js} +12 -11
- package/chunks/artifact-tool-PBX64YTT.js +675 -0
- package/chunks/{askUserQuestion-WHVYL2S2.js → askUserQuestion-5ZQOWSRT.js} +20 -7
- package/chunks/chunk-22IFUCVR.js +291 -0
- package/chunks/{chunk-XGU3ETVO.js → chunk-255L5QYW.js} +3 -3
- package/chunks/{chunk-26ZYKTYZ.js → chunk-2BIF2NVV.js} +2 -2
- package/chunks/{chunk-LTN3VYMZ.js → chunk-3UALQ56H.js} +1 -1
- package/chunks/{chunk-2O3NAOJG.js → chunk-57XRIZVC.js} +1 -1
- package/chunks/{chunk-MSJ2MXJ6.js → chunk-BGUDAULQ.js} +11 -290
- package/chunks/{chunk-FH4P6RQA.js → chunk-BNXANYLH.js} +1 -1
- package/chunks/{chunk-WE6TNQJU.js → chunk-C4K3FEQ2.js} +36 -0
- package/chunks/{chunk-G4BUMVEY.js → chunk-CGEGHF2C.js} +2 -217
- package/chunks/{chunk-GM3LTU6K.js → chunk-CICUTYTF.js} +173 -5025
- package/chunks/chunk-EEEZIVFJ.js +908 -0
- package/chunks/chunk-EJYJYJZ2.js +3703 -0
- package/chunks/{chunk-ATLUHLZE.js → chunk-EW6C27XX.js} +1 -1
- package/chunks/{chunk-QBUR6J4B.js → chunk-GAAALA2X.js} +6 -6
- package/chunks/{chunk-C6MI2ASN.js → chunk-GZCJS5WH.js} +2 -2
- package/chunks/{chunk-KGV3F6UF.js → chunk-HGNSRCRB.js} +1 -1
- package/chunks/{chunk-42HGOMED.js → chunk-JDV3VBPY.js} +216 -1
- package/chunks/{chunk-CX3SGIPK.js → chunk-JRVB4QUY.js} +2 -2
- package/chunks/{chunk-7Z2O7IBO.js → chunk-KFR4QTV3.js} +557 -22
- package/chunks/{chunk-ID533GA3.js → chunk-OTBK43JR.js} +1 -1
- package/chunks/{chunk-GOMVJKBL.js → chunk-PLY4F7QW.js} +5 -3
- package/chunks/{chunk-VKILGMZQ.js → chunk-PO5LVPK3.js} +1 -1
- package/chunks/{chunk-AE6RQU6F.js → chunk-PRYMDRBV.js} +1 -1
- package/chunks/{chunk-GBN55AWZ.js → chunk-QKUAGMGE.js} +2 -2
- package/chunks/{chunk-KQIKOTQJ.js → chunk-RYLJU2DY.js} +60 -5
- package/chunks/{chunk-SWGPABMH.js → chunk-S7ILEIB4.js} +82 -84
- package/chunks/{chunk-CTHZVIMD.js → chunk-T2VDEFUU.js} +2 -2
- package/chunks/{chunk-LF6BKGZT.js → chunk-TXVGVZYR.js} +3 -3
- package/chunks/{chunk-EJ6NFQ2L.js → chunk-VRIVIYRL.js} +5 -5
- package/chunks/{chunk-XU7BKR3B.js → chunk-XU5P23GN.js} +3 -3
- package/chunks/{chunk-CXZV4Q4J.js → chunk-YF3ORUIU.js} +6362 -4888
- package/chunks/{computer-use-F5O56FVW.js → computer-use-YYK42DD5.js} +24 -22
- package/chunks/contextCommand-BO45MUAA.js +56 -0
- package/chunks/{cron-create-THNDTYXU.js → cron-create-DXKAP5KN.js} +4 -4
- package/chunks/{cron-delete-BS5H6TFJ.js → cron-delete-WGO4PZTF.js} +4 -4
- package/chunks/{cron-list-7OBVHT7K.js → cron-list-EY7D3ZUC.js} +4 -4
- package/chunks/{dist-GKRNVFBH.js → dist-ZBX7LPJZ.js} +2 -1
- package/chunks/{edit-UV7MBOJU.js → edit-QERNJN7K.js} +24 -22
- package/chunks/{en-S3OQBISC.js → en-UTXMGXIR.js} +47 -0
- package/chunks/{enter-worktree-TSCHZMJ3.js → enter-worktree-6ZKH5IL3.js} +24 -22
- package/chunks/{enterPlanMode-LKS7VIUG.js → enterPlanMode-B2PBF4MA.js} +27 -23
- package/chunks/{exit-worktree-Q6EWFLZU.js → exit-worktree-EOCMQO4I.js} +24 -22
- package/chunks/{exitPlanMode-HS6I4D2O.js → exitPlanMode-WSRXCBRZ.js} +36 -28
- package/chunks/{geminiContentGenerator-GEZ63VSW.js → geminiContentGenerator-NTR4LTKS.js} +4 -4
- package/chunks/{glob-ZN5LKB7E.js → glob-IWDJQI2V.js} +26 -23
- package/chunks/{grep-NIKBBYBU.js → grep-2DV5BXMH.js} +24 -22
- package/chunks/{keychain-token-storage-YHXIWXIB.js → keychain-token-storage-CSYMNRPP.js} +2 -2
- package/chunks/{loop-wakeup-SSBWZ64K.js → loop-wakeup-XVSDE7QR.js} +5 -5
- package/chunks/{ls-YPFUHZRQ.js → ls-LPY3ATQQ.js} +6 -6
- package/chunks/{lsp-FD6DNVAF.js → lsp-ZT3AAXIF.js} +2 -2
- package/chunks/{monitor-A25XKPU5.js → monitor-6HBDS2RS.js} +24 -22
- package/chunks/{notebook-edit-E72AIOUS.js → notebook-edit-J5AY5BRY.js} +25 -23
- package/chunks/{openaiContentGenerator-ZDLZDXLU.js → openaiContentGenerator-ENABCJDM.js} +12 -15
- package/chunks/{qwenContentGenerator-HVJPPMTT.js → qwenContentGenerator-ISW42U7C.js} +26 -24
- package/chunks/{qwenOAuth2-WZ54YZ4J.js → qwenOAuth2-K4AASPCV.js} +5 -4
- package/chunks/{read-file-L6FM3KUX.js → read-file-NRX2FSSQ.js} +9 -8
- package/chunks/{ripGrep-YRJCUISE.js → ripGrep-WF5CKHR5.js} +24 -22
- package/chunks/{scheduler-M3742P2I.js → scheduler-PNPG5WJ3.js} +73 -27
- package/chunks/{send-message-FWFTX62Q.js → send-message-447W6NH6.js} +24 -4
- package/chunks/{serve-ZIP2GDFY.js → serve-ZBRCIIHW.js} +430 -70
- package/chunks/{shell-T5VSPJLU.js → shell-DLIL6MLU.js} +24 -22
- package/chunks/{skill-ETTFYDOV.js → skill-32TXHY4A.js} +11 -10
- package/chunks/{src-UKOPSYDK.js → src-PDDLF5BB.js} +114 -67
- package/chunks/{syntheticOutput-JY5NEDTV.js → syntheticOutput-GQRDRLGR.js} +3 -3
- package/chunks/{task-create-IGILDV25.js → task-create-66DDSTVI.js} +6 -6
- package/chunks/{task-list-KBEJVAAG.js → task-list-GOPXRSHG.js} +5 -5
- package/chunks/{task-stop-WFTMQ6AC.js → task-stop-M7NKGFX2.js} +2 -2
- package/chunks/{task-update-UIVX7ZEV.js → task-update-M5TBHLZV.js} +6 -6
- package/chunks/{team-create-DRKUVGBN.js → team-create-EHLDSJGI.js} +28 -26
- package/chunks/{team-delete-LPOXDPVB.js → team-delete-DKKDS65W.js} +5 -5
- package/chunks/{todoWrite-R4RIJKOD.js → todoWrite-4VX7X44D.js} +4 -4
- package/chunks/{tool-search-EIIK27ON.js → tool-search-FU7IZAOO.js} +9 -8
- package/chunks/{web-fetch-JU7SS6KE.js → web-fetch-YPP3V4FJ.js} +4 -4
- package/chunks/{workflow-4RGAYFSJ.js → workflow-WUDNBCGX.js} +544 -92
- package/chunks/{write-file-GLQYJFEZ.js → write-file-NVFPX6CH.js} +24 -22
- package/chunks/{zh-TW-3OKU7LDA.js → zh-TW-J2TTW67R.js} +47 -0
- package/chunks/{zh-QXLZV2PX.js → zh-YOXVTJU5.js} +47 -0
- package/cli.js +7932 -4946
- package/locales/en.js +68 -0
- package/locales/zh-TW.js +65 -0
- package/locales/zh.js +65 -0
- package/package.json +3 -2
- package/web-shell/assets/{arc-CRFN10gD.js → arc-pxOt9B72.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-IRlu1CpH.js → architectureDiagram-3BPJPVTR-B9XuYYOT.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-htIZ6iw8.js → blockDiagram-GPEHLZMM-5yK68iu4.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-A2GoRq3O.js → c4Diagram-AAUBKEIU-B1LY-qNI.js} +1 -1
- package/web-shell/assets/channel-Cx7egbSX.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-CdP2AC6u.js → chunk-2J33WTMH-DkgXg7cM.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-DrA07nW2.js → chunk-4BX2VUAB-DdM45KFM.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-CFGnVU2f.js → chunk-55IACEB6-CmdsDjD4.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-BTMkq3Tk.js → chunk-727SXJPM-CQ4pJ5GH.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-C9sYg04Z.js → chunk-AQP2D5EJ-Dy6UWz37.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-Cag-cj6I.js → chunk-FMBD7UC4-CC8nFqap.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-CDSTFoR7.js → chunk-ND2GUHAM-CkmS7lx7.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-CN9Z6oOs.js → chunk-QZHKN3VN-DynyqleZ.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-CwH_F8cH.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-CwH_F8cH.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-DuN9j8Du.js → cose-bilkent-S5V4N54A-DA0fzCsY.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-ByCWe45f.js → dagre-BM42HDAG-DHYXRqCo.js} +1 -1
- package/web-shell/assets/{diagram-2AECGRRQ-eWyU_pKZ.js → diagram-2AECGRRQ-DWD6BcPs.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-0Bfu3Fq5.js → diagram-5GNKFQAL-CX9wnqVc.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-B6KBKkQP.js → diagram-KO2AKTUF-BpE8Kbt8.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-TToDmeA_.js → diagram-LMA3HP47-DLPcrP0C.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-CQCkC2Yx.js → diagram-OG6HWLK6-CSUCBsCe.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-Cm9ls1iB.js → erDiagram-TEJ5UH35-d3Z0uMXS.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-Dyoz80Pv.js → flowDiagram-I6XJVG4X-C7vj5FX3.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-Cfut9yaU.js → ganttDiagram-6RSMTGT7-BV9UvDV_.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-BCuJdlfD.js → gitGraphDiagram-PVQCEYII-CoWp0mW4.js} +1 -1
- package/web-shell/assets/index-CjTH6cLP.js +516 -0
- package/web-shell/assets/{infoDiagram-5YYISTIA-CXKxF2VL.js → infoDiagram-5YYISTIA-DUswfJHA.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-DE7HWSU8.js → ishikawaDiagram-YF4QCWOH-WidEKukx.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-D__YUfds.js → journeyDiagram-JHISSGLW-D7IRK3VN.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-D82R169M.js → kanban-definition-UN3LZRKU-CwFJ6Cja.js} +1 -1
- package/web-shell/assets/{linear-DpyMbKqT.js → linear-BQ8GvDRY.js} +1 -1
- package/web-shell/assets/{mermaid.core-D1KEFDhn.js → mermaid.core-CwF8A_xO.js} +5 -5
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-BwVUFGFE.js → mindmap-definition-RKZ34NQL-TR1s0iZi.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-BO_qoEGx.js → pieDiagram-4H26LBE5-Cd91pMl9.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-Cx0ZaWPo.js → quadrantDiagram-W4KKPZXB-BGxGDNMl.js} +1 -1
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-BJPXejOA.js → requirementDiagram-4Y6WPE33-Cq3fWmYz.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-C1xeIL9i.js → sankeyDiagram-5OEKKPKP-ChLyGdq0.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-CSYKQUjf.js → sequenceDiagram-3UESZ5HK-tazy79aB.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-d9hiHlAW.js → stateDiagram-AJRCARHV-dIYLUNSf.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-luL7KTiX.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-BhCBkq8k.js → timeline-definition-PNZ67QCA-BuENhrC2.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-DPPFWp_y.js → vennDiagram-CIIHVFJN-DLtum9dF.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-CcOUIgXY.js → wardley-L42UT6IY-Bf7OSF3y.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-p7oR5X7r.js → wardleyDiagram-YWT4CUSO-slMw3OB3.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-Dfr7-SEy.js → xychartDiagram-2RQKCTM6-CdxOm8oC.js} +1 -1
- package/web-shell/index.html +1 -1
- package/chunks/chunk-77WXWU44.js +0 -26
- package/chunks/contextCommand-WBVNKL2C.js +0 -54
- package/web-shell/assets/channel-WwImfsc7.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-CJKQYFeJ.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-CJKQYFeJ.js +0 -1
- package/web-shell/assets/index-DaebdpHq.js +0 -516
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-DgReoybQ.js +0 -1
|
@@ -4,57 +4,67 @@ import {
|
|
|
4
4
|
AgentEventEmitter,
|
|
5
5
|
FileDiscoveryService,
|
|
6
6
|
GitWorktreeService,
|
|
7
|
-
WorkspaceContext,
|
|
8
7
|
generateAgentWorktreeSlug,
|
|
9
8
|
rebuildToolRegistryOnOverride,
|
|
9
|
+
resolveSavedWorkflowScript,
|
|
10
10
|
stripAnsiAndControl,
|
|
11
|
+
writeWorkflowSnapshot,
|
|
11
12
|
writeWorktreeSessionMarker
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-YF3ORUIU.js";
|
|
13
14
|
import "./chunk-K5PGHDBN.js";
|
|
14
|
-
import "./chunk-
|
|
15
|
-
import "./chunk-
|
|
16
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-TXVGVZYR.js";
|
|
16
|
+
import "./chunk-XU5P23GN.js";
|
|
17
|
+
import "./chunk-2BIF2NVV.js";
|
|
18
|
+
import "./chunk-57XRIZVC.js";
|
|
19
|
+
import "./chunk-255L5QYW.js";
|
|
17
20
|
import "./chunk-2MPVVENX.js";
|
|
18
21
|
import "./chunk-ZTQ26VBE.js";
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-LF6BKGZT.js";
|
|
21
|
-
import "./chunk-26ZYKTYZ.js";
|
|
22
|
+
import "./chunk-GZCJS5WH.js";
|
|
22
23
|
import "./chunk-MLZQVCF3.js";
|
|
23
24
|
import "./chunk-LD2XBG6Z.js";
|
|
24
|
-
import "./chunk-
|
|
25
|
-
import "./chunk-
|
|
26
|
-
import
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
import "./chunk-OTBK43JR.js";
|
|
26
|
+
import "./chunk-HGNSRCRB.js";
|
|
27
|
+
import {
|
|
28
|
+
WorkspaceContext
|
|
29
|
+
} from "./chunk-EEEZIVFJ.js";
|
|
30
|
+
import "./chunk-PRYMDRBV.js";
|
|
31
|
+
import {
|
|
32
|
+
WorkflowRunEvent,
|
|
33
|
+
logWorkflowRun,
|
|
34
|
+
read,
|
|
35
|
+
writeLine
|
|
36
|
+
} from "./chunk-CICUTYTF.js";
|
|
29
37
|
import "./chunk-3PJXIDKI.js";
|
|
30
38
|
import "./chunk-UWCTAVOD.js";
|
|
31
39
|
import "./chunk-OFEVLU4C.js";
|
|
32
40
|
import "./chunk-OMX7CUOE.js";
|
|
33
|
-
import "./chunk-
|
|
34
|
-
import "./chunk-
|
|
41
|
+
import "./chunk-BNXANYLH.js";
|
|
42
|
+
import "./chunk-T2VDEFUU.js";
|
|
35
43
|
import {
|
|
36
44
|
SyntheticOutputTool
|
|
37
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-PO5LVPK3.js";
|
|
38
46
|
import {
|
|
39
47
|
BaseDeclarativeTool,
|
|
40
48
|
BaseToolInvocation,
|
|
41
49
|
ToolDisplayNames,
|
|
42
50
|
ToolNames
|
|
43
|
-
} from "./chunk-
|
|
44
|
-
import "./chunk-
|
|
45
|
-
import "./chunk-
|
|
51
|
+
} from "./chunk-PLY4F7QW.js";
|
|
52
|
+
import "./chunk-BGUDAULQ.js";
|
|
53
|
+
import "./chunk-EW6C27XX.js";
|
|
54
|
+
import "./chunk-22IFUCVR.js";
|
|
46
55
|
import {
|
|
47
56
|
createChildAbortController
|
|
48
57
|
} from "./chunk-64WXLC72.js";
|
|
49
|
-
import "./chunk-
|
|
50
|
-
import "./chunk-
|
|
51
|
-
import "./chunk-
|
|
58
|
+
import "./chunk-3UALQ56H.js";
|
|
59
|
+
import "./chunk-JDV3VBPY.js";
|
|
60
|
+
import "./chunk-CGEGHF2C.js";
|
|
61
|
+
import "./chunk-EJYJYJZ2.js";
|
|
52
62
|
import "./chunk-55ZMG67I.js";
|
|
53
63
|
import "./chunk-H6BD2ELD.js";
|
|
54
64
|
import "./chunk-5IFG2VC4.js";
|
|
55
65
|
import {
|
|
56
66
|
createDebugLogger
|
|
57
|
-
} from "./chunk-
|
|
67
|
+
} from "./chunk-C4K3FEQ2.js";
|
|
58
68
|
import "./chunk-ZERZSAZL.js";
|
|
59
69
|
import "./chunk-QN5NZ3UQ.js";
|
|
60
70
|
import "./chunk-BR4QREVK.js";
|
|
@@ -358,9 +368,11 @@ function createWorkflowSandbox(opts) {
|
|
|
358
368
|
// init script without leaking the host function itself when not used.
|
|
359
369
|
hasParallel: !!opts.parallel,
|
|
360
370
|
hasPipeline: !!opts.pipeline,
|
|
371
|
+
hasWorkflow: !!opts.workflow,
|
|
361
372
|
hasBudget: !!opts.budget,
|
|
362
373
|
hostParallel: opts.parallel,
|
|
363
374
|
hostPipeline: opts.pipeline,
|
|
375
|
+
hostWorkflow: opts.workflow,
|
|
364
376
|
budgetTotal: opts.budget ? opts.budget.total : null,
|
|
365
377
|
hostBudgetSpent: opts.budget ? opts.budget.spent.bind(opts.budget) : null,
|
|
366
378
|
hostBudgetRemaining: opts.budget ? opts.budget.remaining.bind(opts.budget) : null
|
|
@@ -466,7 +478,7 @@ function createWorkflowSandbox(opts) {
|
|
|
466
478
|
// FIX-Round1-T13: throw on any opts key not in the allowlist \u2014 catches
|
|
467
479
|
// typos like { scema: ... } that previously slipped through the
|
|
468
480
|
// [key:string]: unknown index signature.
|
|
469
|
-
const KNOWN_AGENT_OPTS = ['label', 'phase', 'schema', 'model', 'isolation', 'agentType'];
|
|
481
|
+
const KNOWN_AGENT_OPTS = ['label', 'phase', 'schema', 'model', 'isolation', 'agentType', 'stallMs'];
|
|
470
482
|
globalThis.agent = vmAsync(function (prompt, agentOpts) {
|
|
471
483
|
agentOpts = agentOpts || {};
|
|
472
484
|
const keys = Object.keys(agentOpts);
|
|
@@ -639,15 +651,64 @@ function createWorkflowSandbox(opts) {
|
|
|
639
651
|
});
|
|
640
652
|
};
|
|
641
653
|
}
|
|
642
|
-
// workflow()
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
654
|
+
// --- workflow (nested, single-level) ---
|
|
655
|
+
// Mirrors agent(): args cross vm\u2192host verbatim (safe direction), the
|
|
656
|
+
// single result is revived back into the vm realm via JSON round-trip
|
|
657
|
+
// (same T1/T8/T14 escape defense as agent / parallel / pipeline). The
|
|
658
|
+
// host impl resolves a saved workflow and runs it sharing this run's
|
|
659
|
+
// agent-count cap + token budget. When __b.hasWorkflow is false, the
|
|
660
|
+
// sandbox is either bare (no resolver wired) OR is itself a nested
|
|
661
|
+
// workflow \u2014 in both cases workflow() must throw, which is how the
|
|
662
|
+
// single-level nesting limit is enforced (a nested sandbox is created
|
|
663
|
+
// without a workflow impl, so its workflow() lands in the else branch).
|
|
664
|
+
if (__b.hasWorkflow) {
|
|
665
|
+
const callWorkflow = vmAsync(function (nameOrRef, wfArgs) {
|
|
666
|
+
// Sanitize args through a JSON round-trip BEFORE crossing so the
|
|
667
|
+
// host only ever sees vm-realm plain objects (same defense as
|
|
668
|
+
// agent()'s safeOpts). nameOrRef may be a string or {scriptPath}.
|
|
669
|
+
var safeRef;
|
|
670
|
+
var safeArgs;
|
|
671
|
+
try {
|
|
672
|
+
safeRef = nameOrRef === undefined
|
|
673
|
+
? undefined
|
|
674
|
+
: JSON.parse(JSON.stringify(nameOrRef));
|
|
675
|
+
safeArgs = wfArgs === undefined
|
|
676
|
+
? undefined
|
|
677
|
+
: JSON.parse(JSON.stringify(wfArgs));
|
|
678
|
+
} catch (e) {
|
|
679
|
+
throw new Error(
|
|
680
|
+
'workflow() received a non-JSON-serializable argument: ' +
|
|
681
|
+
String(e && e.message != null ? e.message : e)
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
return __b.hostWorkflow(safeRef, safeArgs).then(function (value) {
|
|
685
|
+
if (value === null || typeof value !== 'object') {
|
|
686
|
+
return value;
|
|
687
|
+
}
|
|
688
|
+
try {
|
|
689
|
+
return JSON.parse(JSON.stringify(value));
|
|
690
|
+
} catch (e) {
|
|
691
|
+
__b.logRevivalFailure(0, String(e && e.message != null ? e.message : e));
|
|
692
|
+
return null;
|
|
693
|
+
}
|
|
694
|
+
});
|
|
649
695
|
});
|
|
650
|
-
|
|
696
|
+
globalThis.workflow = function workflow(nameOrRef, wfArgs) {
|
|
697
|
+
return callWorkflow(nameOrRef, wfArgs);
|
|
698
|
+
};
|
|
699
|
+
} else {
|
|
700
|
+
globalThis.workflow = function workflow() {
|
|
701
|
+
return new Promise(function (_, reject) {
|
|
702
|
+
reject(new Error(
|
|
703
|
+
"workflow() is unavailable here. Either this sandbox was created " +
|
|
704
|
+
"without a saved-workflow resolver, or this script is already " +
|
|
705
|
+
"running as a nested workflow \u2014 workflow() nesting is limited to " +
|
|
706
|
+
"a single level (a workflow cannot call another workflow that " +
|
|
707
|
+
"itself calls workflow())."
|
|
708
|
+
));
|
|
709
|
+
});
|
|
710
|
+
};
|
|
711
|
+
}
|
|
651
712
|
|
|
652
713
|
// --- budget ---
|
|
653
714
|
const safeBudget = Object.create(null);
|
|
@@ -813,6 +874,231 @@ var WorkflowBudgetExceededError = class extends Error {
|
|
|
813
874
|
}
|
|
814
875
|
};
|
|
815
876
|
|
|
877
|
+
// packages/core/src/agents/runtime/workflow-stall.ts
|
|
878
|
+
init_esbuild_shims();
|
|
879
|
+
var DEFAULT_STALL_MS = 6e4;
|
|
880
|
+
var MAX_STALL_ATTEMPTS = 3;
|
|
881
|
+
var MAX_WORKFLOW_STALL_MS_ENV = "QWEN_CODE_WORKFLOW_STALL_SECONDS";
|
|
882
|
+
function resolveStallMs(perCall, env = process.env) {
|
|
883
|
+
if (typeof perCall === "number" && Number.isFinite(perCall)) {
|
|
884
|
+
if (perCall === 0) return 0;
|
|
885
|
+
if (perCall > 0) return perCall;
|
|
886
|
+
}
|
|
887
|
+
const raw = env[MAX_WORKFLOW_STALL_MS_ENV];
|
|
888
|
+
if (raw !== void 0 && raw.trim() !== "") {
|
|
889
|
+
const sec = Number(raw);
|
|
890
|
+
if (Number.isFinite(sec) && sec > 0) return sec * 1e3;
|
|
891
|
+
if (sec === 0) return 0;
|
|
892
|
+
}
|
|
893
|
+
return DEFAULT_STALL_MS;
|
|
894
|
+
}
|
|
895
|
+
__name(resolveStallMs, "resolveStallMs");
|
|
896
|
+
var debugLogger3 = createDebugLogger("WORKFLOW_STALL");
|
|
897
|
+
function attachStallWatchdog(emitter, controller, stallMs) {
|
|
898
|
+
if (stallMs <= 0) {
|
|
899
|
+
return { stalled: /* @__PURE__ */ __name(() => false, "stalled"), dispose: /* @__PURE__ */ __name(() => {
|
|
900
|
+
}, "dispose") };
|
|
901
|
+
}
|
|
902
|
+
let inFlightTools = 0;
|
|
903
|
+
let fired = false;
|
|
904
|
+
let timer;
|
|
905
|
+
let disposed = false;
|
|
906
|
+
const clear = /* @__PURE__ */ __name(() => {
|
|
907
|
+
if (timer) {
|
|
908
|
+
clearTimeout(timer);
|
|
909
|
+
timer = void 0;
|
|
910
|
+
}
|
|
911
|
+
}, "clear");
|
|
912
|
+
const arm = /* @__PURE__ */ __name(() => {
|
|
913
|
+
clear();
|
|
914
|
+
if (disposed || fired) return;
|
|
915
|
+
if (inFlightTools > 0) return;
|
|
916
|
+
timer = setTimeout(() => {
|
|
917
|
+
if (disposed || fired) return;
|
|
918
|
+
fired = true;
|
|
919
|
+
debugLogger3.warn(
|
|
920
|
+
`[Workflow] agent dispatch stalled \u2014 no progress for ${stallMs}ms; aborting.`
|
|
921
|
+
);
|
|
922
|
+
try {
|
|
923
|
+
controller.abort("stalled");
|
|
924
|
+
} catch (e) {
|
|
925
|
+
debugLogger3.warn("stall watchdog abort threw:", e);
|
|
926
|
+
}
|
|
927
|
+
}, stallMs);
|
|
928
|
+
if (typeof timer.unref === "function") {
|
|
929
|
+
timer.unref();
|
|
930
|
+
}
|
|
931
|
+
}, "arm");
|
|
932
|
+
const onActivity = /* @__PURE__ */ __name(() => arm(), "onActivity");
|
|
933
|
+
const onToolCall = /* @__PURE__ */ __name(() => {
|
|
934
|
+
inFlightTools += 1;
|
|
935
|
+
clear();
|
|
936
|
+
}, "onToolCall");
|
|
937
|
+
const onToolResult = /* @__PURE__ */ __name(() => {
|
|
938
|
+
inFlightTools = Math.max(0, inFlightTools - 1);
|
|
939
|
+
arm();
|
|
940
|
+
}, "onToolResult");
|
|
941
|
+
emitter.on("round_start" /* ROUND_START */, onActivity);
|
|
942
|
+
emitter.on("round_end" /* ROUND_END */, onActivity);
|
|
943
|
+
emitter.on("stream_text" /* STREAM_TEXT */, onActivity);
|
|
944
|
+
emitter.on("usage_metadata" /* USAGE_METADATA */, onActivity);
|
|
945
|
+
emitter.on("tool_call" /* TOOL_CALL */, onToolCall);
|
|
946
|
+
emitter.on("tool_result" /* TOOL_RESULT */, onToolResult);
|
|
947
|
+
return {
|
|
948
|
+
stalled: /* @__PURE__ */ __name(() => fired, "stalled"),
|
|
949
|
+
dispose: /* @__PURE__ */ __name(() => {
|
|
950
|
+
if (disposed) return;
|
|
951
|
+
disposed = true;
|
|
952
|
+
clear();
|
|
953
|
+
emitter.off("round_start" /* ROUND_START */, onActivity);
|
|
954
|
+
emitter.off("round_end" /* ROUND_END */, onActivity);
|
|
955
|
+
emitter.off("stream_text" /* STREAM_TEXT */, onActivity);
|
|
956
|
+
emitter.off("usage_metadata" /* USAGE_METADATA */, onActivity);
|
|
957
|
+
emitter.off("tool_call" /* TOOL_CALL */, onToolCall);
|
|
958
|
+
emitter.off("tool_result" /* TOOL_RESULT */, onToolResult);
|
|
959
|
+
}, "dispose")
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
__name(attachStallWatchdog, "attachStallWatchdog");
|
|
963
|
+
async function runStallResilient(attemptFn, opts) {
|
|
964
|
+
const { stallMs, signal, label } = opts;
|
|
965
|
+
if (stallMs <= 0) {
|
|
966
|
+
const emitter = new AgentEventEmitter();
|
|
967
|
+
return attemptFn(signal ?? new AbortController().signal, emitter);
|
|
968
|
+
}
|
|
969
|
+
let attempt = 0;
|
|
970
|
+
for (; ; ) {
|
|
971
|
+
attempt += 1;
|
|
972
|
+
const controller = new AbortController();
|
|
973
|
+
let onParentAbort;
|
|
974
|
+
if (signal) {
|
|
975
|
+
if (signal.aborted) {
|
|
976
|
+
controller.abort(signal.reason);
|
|
977
|
+
} else {
|
|
978
|
+
onParentAbort = /* @__PURE__ */ __name(() => controller.abort(signal.reason), "onParentAbort");
|
|
979
|
+
signal.addEventListener("abort", onParentAbort);
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
const emitter = new AgentEventEmitter();
|
|
983
|
+
const watchdog = attachStallWatchdog(emitter, controller, stallMs);
|
|
984
|
+
try {
|
|
985
|
+
return await attemptFn(controller.signal, emitter);
|
|
986
|
+
} catch (err) {
|
|
987
|
+
if (signal?.aborted) throw err;
|
|
988
|
+
if (watchdog.stalled() && attempt < MAX_STALL_ATTEMPTS) {
|
|
989
|
+
debugLogger3.warn(
|
|
990
|
+
`[Workflow] agent "${label ?? "workflow-agent"}" stalled (attempt ${attempt}/${MAX_STALL_ATTEMPTS}) \u2014 retrying.`
|
|
991
|
+
);
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
if (watchdog.stalled()) {
|
|
995
|
+
throw new Error(
|
|
996
|
+
`agent "${label ?? "workflow-agent"}" stalled on all ${MAX_STALL_ATTEMPTS} attempts (no progress for ${stallMs}ms each).`
|
|
997
|
+
);
|
|
998
|
+
}
|
|
999
|
+
throw err;
|
|
1000
|
+
} finally {
|
|
1001
|
+
watchdog.dispose();
|
|
1002
|
+
if (onParentAbort && signal) {
|
|
1003
|
+
signal.removeEventListener("abort", onParentAbort);
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
__name(runStallResilient, "runStallResilient");
|
|
1009
|
+
|
|
1010
|
+
// packages/core/src/agents/runtime/workflow-journal.ts
|
|
1011
|
+
init_esbuild_shims();
|
|
1012
|
+
import { createHash } from "node:crypto";
|
|
1013
|
+
var debugLogger4 = createDebugLogger("WORKFLOW_JOURNAL");
|
|
1014
|
+
var JOURNAL_KEY_VERSION = "v2";
|
|
1015
|
+
function canonicalizeAgentOpts(opts) {
|
|
1016
|
+
const projected = {};
|
|
1017
|
+
for (const k of ["schema", "model", "isolation", "agentType"]) {
|
|
1018
|
+
const v = opts[k];
|
|
1019
|
+
if (v === void 0 || typeof v === "function") continue;
|
|
1020
|
+
projected[k] = v;
|
|
1021
|
+
}
|
|
1022
|
+
const sortDeep = /* @__PURE__ */ __name((val) => {
|
|
1023
|
+
if (typeof val === "function") return void 0;
|
|
1024
|
+
if (Array.isArray(val)) return val.map(sortDeep);
|
|
1025
|
+
if (val && typeof val === "object") {
|
|
1026
|
+
const out = {};
|
|
1027
|
+
for (const key of Object.keys(val).sort()) {
|
|
1028
|
+
if (key === "__proto__") continue;
|
|
1029
|
+
out[key] = sortDeep(val[key]);
|
|
1030
|
+
}
|
|
1031
|
+
return out;
|
|
1032
|
+
}
|
|
1033
|
+
return val;
|
|
1034
|
+
}, "sortDeep");
|
|
1035
|
+
try {
|
|
1036
|
+
return JSON.stringify(sortDeep(projected));
|
|
1037
|
+
} catch {
|
|
1038
|
+
return "{}";
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
__name(canonicalizeAgentOpts, "canonicalizeAgentOpts");
|
|
1042
|
+
function deriveAgentKey(prefixHash, prompt, opts) {
|
|
1043
|
+
const hash = createHash("sha256");
|
|
1044
|
+
hash.update(prefixHash);
|
|
1045
|
+
hash.update("\0");
|
|
1046
|
+
hash.update(prompt);
|
|
1047
|
+
hash.update("\0");
|
|
1048
|
+
hash.update(canonicalizeAgentOpts(opts));
|
|
1049
|
+
return `${JOURNAL_KEY_VERSION}:${hash.digest("hex")}`;
|
|
1050
|
+
}
|
|
1051
|
+
__name(deriveAgentKey, "deriveAgentKey");
|
|
1052
|
+
function deriveArgsSeed(args) {
|
|
1053
|
+
const hash = createHash("sha256");
|
|
1054
|
+
let serialized;
|
|
1055
|
+
try {
|
|
1056
|
+
serialized = JSON.stringify(args ?? null) ?? "null";
|
|
1057
|
+
} catch {
|
|
1058
|
+
serialized = "non-serializable-args";
|
|
1059
|
+
}
|
|
1060
|
+
hash.update(serialized);
|
|
1061
|
+
return `${JOURNAL_KEY_VERSION}:${hash.digest("hex")}`;
|
|
1062
|
+
}
|
|
1063
|
+
__name(deriveArgsSeed, "deriveArgsSeed");
|
|
1064
|
+
function buildReplay(entries) {
|
|
1065
|
+
const results = /* @__PURE__ */ new Map();
|
|
1066
|
+
const started = /* @__PURE__ */ new Map();
|
|
1067
|
+
for (const e of entries) {
|
|
1068
|
+
if (e.type === "result") {
|
|
1069
|
+
results.set(e.key, e);
|
|
1070
|
+
} else if (e.type === "started") {
|
|
1071
|
+
const list = started.get(e.key);
|
|
1072
|
+
if (list) list.push(e);
|
|
1073
|
+
else started.set(e.key, [e]);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
return { results, started };
|
|
1077
|
+
}
|
|
1078
|
+
__name(buildReplay, "buildReplay");
|
|
1079
|
+
var WorkflowJournal = class {
|
|
1080
|
+
constructor(path2) {
|
|
1081
|
+
this.path = path2;
|
|
1082
|
+
}
|
|
1083
|
+
static {
|
|
1084
|
+
__name(this, "WorkflowJournal");
|
|
1085
|
+
}
|
|
1086
|
+
/** Load + parse all entries into replay maps. Empty maps if no file. */
|
|
1087
|
+
async load() {
|
|
1088
|
+
try {
|
|
1089
|
+
const entries = await read(this.path);
|
|
1090
|
+
return buildReplay(entries);
|
|
1091
|
+
} catch (e) {
|
|
1092
|
+
debugLogger4.warn(`WorkflowJournal.load failed for ${this.path}: ${e}`);
|
|
1093
|
+
return { results: /* @__PURE__ */ new Map(), started: /* @__PURE__ */ new Map() };
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
/** Append one entry. Rejects only on I/O error (callers `.catch`). */
|
|
1097
|
+
append(entry) {
|
|
1098
|
+
return writeLine(this.path, entry);
|
|
1099
|
+
}
|
|
1100
|
+
};
|
|
1101
|
+
|
|
816
1102
|
// packages/core/src/agents/runtime/workflow-prompts.ts
|
|
817
1103
|
init_esbuild_shims();
|
|
818
1104
|
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.';
|
|
@@ -946,48 +1232,65 @@ function sanitizeForErrorMessage(value) {
|
|
|
946
1232
|
__name(sanitizeForErrorMessage, "sanitizeForErrorMessage");
|
|
947
1233
|
function createProductionDispatch(config, signal, onTokens) {
|
|
948
1234
|
return async (prompt, opts) => {
|
|
949
|
-
const
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
initialMessages: []
|
|
959
|
-
},
|
|
960
|
-
{},
|
|
961
|
-
// T11 (PR #4732 R1): bound resource ceiling so a single agent() call
|
|
962
|
-
// cannot loop the model indefinitely. Without this, runConfig was {}
|
|
963
|
-
// and the loop guards never tripped — combined with the cancellation
|
|
964
|
-
// bug below, workflows were effectively unkillable.
|
|
965
|
-
{
|
|
966
|
-
max_turns: WORKFLOW_SUBAGENT_MAX_TURNS,
|
|
967
|
-
max_time_minutes: WORKFLOW_SUBAGENT_MAX_TIME_MINUTES
|
|
968
|
-
},
|
|
969
|
-
// T11 (PR #4732 R1): disallow SendMessage / ExitPlanMode to align with
|
|
970
|
-
// upstream Tg8 — closes the back-channel that would let a subagent
|
|
971
|
-
// deliver its answer via user message instead of the script's read.
|
|
972
|
-
{ tools: ["*"], disallowedTools: WORKFLOW_SUBAGENT_DISALLOWED_TOOLS }
|
|
973
|
-
);
|
|
974
|
-
try {
|
|
975
|
-
await subagent.execute(ctx, signal);
|
|
976
|
-
} finally {
|
|
977
|
-
reportTokens(subagent, opts, onTokens);
|
|
978
|
-
}
|
|
979
|
-
const mode = subagent.getTerminateMode();
|
|
980
|
-
if (mode !== "GOAL" /* GOAL */) {
|
|
981
|
-
throw new Error(
|
|
982
|
-
`Workflow subagent did not complete (terminate mode: ${mode}).`
|
|
983
|
-
);
|
|
1235
|
+
const stallMs = resolveStallMs(
|
|
1236
|
+
typeof opts.stallMs === "number" ? opts.stallMs : void 0
|
|
1237
|
+
);
|
|
1238
|
+
return runStallResilient(
|
|
1239
|
+
(attemptSignal, emitter) => runSingleDispatch(config, prompt, opts, attemptSignal, emitter, onTokens),
|
|
1240
|
+
{
|
|
1241
|
+
stallMs,
|
|
1242
|
+
signal,
|
|
1243
|
+
label: typeof opts.label === "string" ? opts.label : void 0
|
|
984
1244
|
}
|
|
985
|
-
|
|
986
|
-
}
|
|
987
|
-
return runOverridePath(config, ctx, opts, signal, onTokens);
|
|
1245
|
+
);
|
|
988
1246
|
};
|
|
989
1247
|
}
|
|
990
1248
|
__name(createProductionDispatch, "createProductionDispatch");
|
|
1249
|
+
async function runSingleDispatch(config, prompt, opts, attemptSignal, emitter, onTokens) {
|
|
1250
|
+
const { AgentHeadless, ContextState } = await import("./agent-headless-ZMTUWMZJ.js");
|
|
1251
|
+
const ctx = new ContextState();
|
|
1252
|
+
ctx.set("task_prompt", prompt);
|
|
1253
|
+
if (opts.agentType === void 0 && opts.model === void 0 && opts.isolation === void 0 && opts.schema === void 0) {
|
|
1254
|
+
const subagent = await AgentHeadless.create(
|
|
1255
|
+
opts.label ?? "workflow-agent",
|
|
1256
|
+
config,
|
|
1257
|
+
{
|
|
1258
|
+
systemPrompt: WORKFLOW_SUBAGENT_SYSTEM_PROMPT,
|
|
1259
|
+
initialMessages: []
|
|
1260
|
+
},
|
|
1261
|
+
{},
|
|
1262
|
+
// T11 (PR #4732 R1): bound resource ceiling so a single agent() call
|
|
1263
|
+
// cannot loop the model indefinitely. Without this, runConfig was {}
|
|
1264
|
+
// and the loop guards never tripped — combined with the cancellation
|
|
1265
|
+
// bug below, workflows were effectively unkillable.
|
|
1266
|
+
{
|
|
1267
|
+
max_turns: WORKFLOW_SUBAGENT_MAX_TURNS,
|
|
1268
|
+
max_time_minutes: WORKFLOW_SUBAGENT_MAX_TIME_MINUTES
|
|
1269
|
+
},
|
|
1270
|
+
// T11 (PR #4732 R1): disallow SendMessage / ExitPlanMode to align with
|
|
1271
|
+
// upstream Tg8 — closes the back-channel that would let a subagent
|
|
1272
|
+
// deliver its answer via user message instead of the script's read.
|
|
1273
|
+
{ tools: ["*"], disallowedTools: WORKFLOW_SUBAGENT_DISALLOWED_TOOLS },
|
|
1274
|
+
// P-stall: the stall-watchdog emitter observes reasoning-loop events
|
|
1275
|
+
// (round/tool/usage) to detect a hang and abort `attemptSignal`.
|
|
1276
|
+
emitter
|
|
1277
|
+
);
|
|
1278
|
+
try {
|
|
1279
|
+
await subagent.execute(ctx, attemptSignal);
|
|
1280
|
+
} finally {
|
|
1281
|
+
reportTokens(subagent, opts, onTokens);
|
|
1282
|
+
}
|
|
1283
|
+
const mode = subagent.getTerminateMode();
|
|
1284
|
+
if (mode !== "GOAL" /* GOAL */) {
|
|
1285
|
+
throw new Error(
|
|
1286
|
+
`Workflow subagent did not complete (terminate mode: ${mode}).`
|
|
1287
|
+
);
|
|
1288
|
+
}
|
|
1289
|
+
return subagent.getFinalText();
|
|
1290
|
+
}
|
|
1291
|
+
return runOverridePath(config, ctx, opts, attemptSignal, onTokens, emitter);
|
|
1292
|
+
}
|
|
1293
|
+
__name(runSingleDispatch, "runSingleDispatch");
|
|
991
1294
|
function reportTokens(subagent, opts, onTokens) {
|
|
992
1295
|
if (!onTokens) return;
|
|
993
1296
|
try {
|
|
@@ -998,7 +1301,7 @@ function reportTokens(subagent, opts, onTokens) {
|
|
|
998
1301
|
}
|
|
999
1302
|
}
|
|
1000
1303
|
__name(reportTokens, "reportTokens");
|
|
1001
|
-
async function runOverridePath(config, ctx, opts, signal, onTokens) {
|
|
1304
|
+
async function runOverridePath(config, ctx, opts, signal, onTokens, emitter) {
|
|
1002
1305
|
if (opts.isolation === "remote") {
|
|
1003
1306
|
throw new Error(
|
|
1004
1307
|
"agent({isolation:'remote'}) is not available in this build."
|
|
@@ -1079,7 +1382,10 @@ ${WORKFLOW_SUBAGENT_SYSTEM_PROMPT_WITH_SCHEMA}` : WORKFLOW_SUBAGENT_SYSTEM_PROMP
|
|
|
1079
1382
|
schemaState.abortController = child;
|
|
1080
1383
|
dispatchSignal = child.signal;
|
|
1081
1384
|
}
|
|
1082
|
-
const eventEmitter = schemaState ?
|
|
1385
|
+
const eventEmitter = emitter ?? (schemaState ? new AgentEventEmitter() : void 0);
|
|
1386
|
+
if (schemaState && eventEmitter) {
|
|
1387
|
+
attachSchemaListeners(eventEmitter, schemaState);
|
|
1388
|
+
}
|
|
1083
1389
|
const { subagent, dispose } = await subagentMgr.createAgentHeadless(
|
|
1084
1390
|
augmented,
|
|
1085
1391
|
effectiveContext,
|
|
@@ -1318,8 +1624,7 @@ function createSchemaModeState() {
|
|
|
1318
1624
|
};
|
|
1319
1625
|
}
|
|
1320
1626
|
__name(createSchemaModeState, "createSchemaModeState");
|
|
1321
|
-
function
|
|
1322
|
-
const emitter = new AgentEventEmitter();
|
|
1627
|
+
function attachSchemaListeners(emitter, state) {
|
|
1323
1628
|
const targetTool = ToolNames.STRUCTURED_OUTPUT;
|
|
1324
1629
|
emitter.on("tool_call" /* TOOL_CALL */, (evt) => {
|
|
1325
1630
|
if (evt.name !== targetTool) return;
|
|
@@ -1341,9 +1646,8 @@ function createSchemaEventEmitter(state) {
|
|
|
1341
1646
|
state.abortController.abort();
|
|
1342
1647
|
}
|
|
1343
1648
|
});
|
|
1344
|
-
return emitter;
|
|
1345
1649
|
}
|
|
1346
|
-
__name(
|
|
1650
|
+
__name(attachSchemaListeners, "attachSchemaListeners");
|
|
1347
1651
|
async function createSchemaConfigOverride(base, schema) {
|
|
1348
1652
|
const override = Object.create(base);
|
|
1349
1653
|
await rebuildToolRegistryOnOverride(override, base);
|
|
@@ -1367,7 +1671,46 @@ var WorkflowOrchestrator = class {
|
|
|
1367
1671
|
let agentCount = 0;
|
|
1368
1672
|
const emitter = req.emitter;
|
|
1369
1673
|
const budget = req.budget;
|
|
1674
|
+
const journal = req.journal;
|
|
1675
|
+
const replay = req.resumeReplay;
|
|
1676
|
+
let prefixHash = deriveArgsSeed(req.args);
|
|
1677
|
+
let hadMiss = false;
|
|
1678
|
+
let journalAgentId = 0;
|
|
1370
1679
|
const countedDispatch = /* @__PURE__ */ __name((prompt, opts) => {
|
|
1680
|
+
let journalKey;
|
|
1681
|
+
let journalEntryId;
|
|
1682
|
+
if (journal) {
|
|
1683
|
+
journalKey = deriveAgentKey(prefixHash, prompt, opts);
|
|
1684
|
+
prefixHash = journalKey;
|
|
1685
|
+
if (!hadMiss && replay) {
|
|
1686
|
+
const cached = replay.results.get(journalKey);
|
|
1687
|
+
if (cached !== void 0) {
|
|
1688
|
+
const respawns = replay.started.get(journalKey);
|
|
1689
|
+
if (respawns && respawns.length > 0) {
|
|
1690
|
+
debugLogger.info(
|
|
1691
|
+
`[Workflow] resume cache hit after ${respawns.length} prior respawn(s) for runId=${runId}.`
|
|
1692
|
+
);
|
|
1693
|
+
}
|
|
1694
|
+
const label2 = typeof opts.label === "string" ? opts.label : void 0;
|
|
1695
|
+
try {
|
|
1696
|
+
emitter?.agentDispatched?.(label2);
|
|
1697
|
+
} catch (e) {
|
|
1698
|
+
debugLogger.warn("emitter.agentDispatched threw:", e);
|
|
1699
|
+
}
|
|
1700
|
+
try {
|
|
1701
|
+
emitter?.agentCompleted?.(label2);
|
|
1702
|
+
} catch (e) {
|
|
1703
|
+
debugLogger.warn("emitter.agentCompleted threw:", e);
|
|
1704
|
+
}
|
|
1705
|
+
return Promise.resolve(cached.result);
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
hadMiss = true;
|
|
1709
|
+
journalEntryId = String(journalAgentId += 1);
|
|
1710
|
+
journal.append({ type: "started", key: journalKey, agentId: journalEntryId }).catch(
|
|
1711
|
+
(e) => debugLogger.warn(`journal started-append failed: ${e}`)
|
|
1712
|
+
);
|
|
1713
|
+
}
|
|
1371
1714
|
if (budget && budget.total !== null && budget.remaining() <= 0) {
|
|
1372
1715
|
debugLogger.warn(
|
|
1373
1716
|
`[Workflow] budget gate refused dispatch at entry: runId=${runId} spent=${budget.spent()} total=${budget.total}`
|
|
@@ -1409,6 +1752,16 @@ var WorkflowOrchestrator = class {
|
|
|
1409
1752
|
} catch (e) {
|
|
1410
1753
|
debugLogger.warn("emitter.agentCompleted threw:", e);
|
|
1411
1754
|
}
|
|
1755
|
+
if (journal && journalKey !== void 0) {
|
|
1756
|
+
journal.append({
|
|
1757
|
+
type: "result",
|
|
1758
|
+
key: journalKey,
|
|
1759
|
+
agentId: journalEntryId ?? "",
|
|
1760
|
+
result
|
|
1761
|
+
}).catch(
|
|
1762
|
+
(e) => debugLogger.warn(`journal result-append failed: ${e}`)
|
|
1763
|
+
);
|
|
1764
|
+
}
|
|
1412
1765
|
if (budget) {
|
|
1413
1766
|
try {
|
|
1414
1767
|
emitter?.budgetUpdated?.(budget.spent(), budget.total);
|
|
@@ -1438,11 +1791,27 @@ var WorkflowOrchestrator = class {
|
|
|
1438
1791
|
}, "countedDispatch");
|
|
1439
1792
|
const parallelImpl = makeParallelImpl(signal);
|
|
1440
1793
|
const pipelineImpl = makePipelineImpl(signal);
|
|
1794
|
+
const resolveSavedWorkflow = req.resolveSavedWorkflow;
|
|
1795
|
+
const workflowImpl = resolveSavedWorkflow ? async (nameOrRef, nestedArgs) => {
|
|
1796
|
+
const resolved = await resolveSavedWorkflow(nameOrRef);
|
|
1797
|
+
const nestedSandbox = createWorkflowSandbox({
|
|
1798
|
+
args: nestedArgs,
|
|
1799
|
+
dispatch: countedDispatch,
|
|
1800
|
+
parallel: parallelImpl,
|
|
1801
|
+
pipeline: pipelineImpl,
|
|
1802
|
+
abortOnTimeout: req.abortOnTimeout,
|
|
1803
|
+
emitter,
|
|
1804
|
+
budget
|
|
1805
|
+
// No `workflow` — single-level nesting limit.
|
|
1806
|
+
});
|
|
1807
|
+
return nestedSandbox.run(resolved.script);
|
|
1808
|
+
} : void 0;
|
|
1441
1809
|
const sandbox = createWorkflowSandbox({
|
|
1442
1810
|
args: req.args,
|
|
1443
1811
|
dispatch: countedDispatch,
|
|
1444
1812
|
parallel: parallelImpl,
|
|
1445
1813
|
pipeline: pipelineImpl,
|
|
1814
|
+
workflow: workflowImpl,
|
|
1446
1815
|
abortOnTimeout: req.abortOnTimeout,
|
|
1447
1816
|
emitter,
|
|
1448
1817
|
budget
|
|
@@ -1466,19 +1835,31 @@ var WorkflowOrchestrator = class {
|
|
|
1466
1835
|
}
|
|
1467
1836
|
}
|
|
1468
1837
|
};
|
|
1469
|
-
async function settleToNullArray(thunks, signal) {
|
|
1838
|
+
async function settleToNullArray(thunks, signal, kind = "parallel") {
|
|
1470
1839
|
const settled = await Promise.allSettled(
|
|
1471
1840
|
thunks.map((t) => Promise.resolve().then(t))
|
|
1472
1841
|
);
|
|
1473
1842
|
if (signal?.aborted)
|
|
1474
1843
|
throw new DOMException("Workflow run aborted.", "AbortError");
|
|
1475
|
-
|
|
1844
|
+
let budgetDropped = 0;
|
|
1845
|
+
const result = settled.map((r, i) => {
|
|
1476
1846
|
if (r.status === "fulfilled") return r.value;
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1847
|
+
const reason = r.reason;
|
|
1848
|
+
if (reason?.name === "WorkflowBudgetExceededError") {
|
|
1849
|
+
budgetDropped += 1;
|
|
1850
|
+
} else {
|
|
1851
|
+
debugLogger.warn(
|
|
1852
|
+
`Workflow thunk at index ${i} rejected: ${String(reason?.message ?? r.reason)}`
|
|
1853
|
+
);
|
|
1854
|
+
}
|
|
1480
1855
|
return null;
|
|
1481
1856
|
});
|
|
1857
|
+
if (budgetDropped > 0) {
|
|
1858
|
+
debugLogger.warn(
|
|
1859
|
+
`${kind}: ${budgetDropped} slot${budgetDropped === 1 ? "" : "s"} dropped \u2014 token budget exceeded.`
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
1862
|
+
return result;
|
|
1482
1863
|
}
|
|
1483
1864
|
__name(settleToNullArray, "settleToNullArray");
|
|
1484
1865
|
function makeParallelImpl(signal) {
|
|
@@ -1524,7 +1905,7 @@ function makePipelineImpl(signal) {
|
|
|
1524
1905
|
const chains = items.map(
|
|
1525
1906
|
(item, idx) => () => runPipelineChain(item, idx, stages)
|
|
1526
1907
|
);
|
|
1527
|
-
return settleToNullArray(chains, signal);
|
|
1908
|
+
return settleToNullArray(chains, signal, "pipeline");
|
|
1528
1909
|
};
|
|
1529
1910
|
}
|
|
1530
1911
|
__name(makePipelineImpl, "makePipelineImpl");
|
|
@@ -1549,6 +1930,7 @@ __name(extractErrorMessage, "extractErrorMessage");
|
|
|
1549
1930
|
|
|
1550
1931
|
// packages/core/src/tools/workflow/workflow.ts
|
|
1551
1932
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
1933
|
+
import * as path from "node:path";
|
|
1552
1934
|
var WORKFLOW_PARAM_SCHEMA = {
|
|
1553
1935
|
type: "object",
|
|
1554
1936
|
properties: {
|
|
@@ -1556,11 +1938,22 @@ var WORKFLOW_PARAM_SCHEMA = {
|
|
|
1556
1938
|
type: "string",
|
|
1557
1939
|
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."
|
|
1558
1940
|
},
|
|
1941
|
+
scriptPath: {
|
|
1942
|
+
type: "string",
|
|
1943
|
+
description: "Optional. Absolute path to a saved workflow `.js` file to load and run instead of inline `script`. Primarily set by the `/<name>` saved-workflow slash command. Provide exactly ONE of `script` or `scriptPath`. The file is read at execution time, so edits to a saved workflow take effect on the next run."
|
|
1944
|
+
},
|
|
1559
1945
|
args: {
|
|
1560
1946
|
description: "Optional structured value bound to the `args` global. Pass actual JSON, not a stringified value."
|
|
1947
|
+
},
|
|
1948
|
+
resumeFromRunId: {
|
|
1949
|
+
type: "string",
|
|
1950
|
+
description: "Optional. Resume a prior workflow run by id (e.g. wf_abc123\u2026). Re-runs the SAME script; agent() calls whose rolling prefix-hash (prompt + opts, chained in call order) matches a journaled result are served from cache for the longest unchanged prefix, and the first changed/missing call onward runs live. Pass the same script and args as the original run for the cache to apply."
|
|
1561
1951
|
}
|
|
1562
|
-
}
|
|
1563
|
-
required
|
|
1952
|
+
}
|
|
1953
|
+
// `script` is required UNLESS `scriptPath` is supplied; this XOR can't be
|
|
1954
|
+
// expressed as a plain `required` list, so it's enforced in
|
|
1955
|
+
// `validateToolParamValues`. Inline authoring (the LLM path) should always
|
|
1956
|
+
// pass `script`; the `scriptPath` property description states the XOR.
|
|
1564
1957
|
};
|
|
1565
1958
|
var WorkflowToolInvocation = class extends BaseToolInvocation {
|
|
1566
1959
|
constructor(config, toolOptions, params) {
|
|
@@ -1572,7 +1965,10 @@ var WorkflowToolInvocation = class extends BaseToolInvocation {
|
|
|
1572
1965
|
__name(this, "WorkflowToolInvocation");
|
|
1573
1966
|
}
|
|
1574
1967
|
getDescription() {
|
|
1575
|
-
|
|
1968
|
+
if (this.params.scriptPath && this.params.script === void 0) {
|
|
1969
|
+
return `Run saved workflow (${path.basename(this.params.scriptPath)})`;
|
|
1970
|
+
}
|
|
1971
|
+
return `Run a workflow script (${this.params.script?.length ?? 0} chars)`;
|
|
1576
1972
|
}
|
|
1577
1973
|
toolLocations() {
|
|
1578
1974
|
return [];
|
|
@@ -1592,7 +1988,26 @@ var WorkflowToolInvocation = class extends BaseToolInvocation {
|
|
|
1592
1988
|
(outputTokens) => budget.recordSpent(outputTokens)
|
|
1593
1989
|
);
|
|
1594
1990
|
const orchestrator = new WorkflowOrchestrator(dispatch);
|
|
1595
|
-
|
|
1991
|
+
let resolvedScript = this.params.script ?? "";
|
|
1992
|
+
let resolvedScriptPath = this.params.scriptPath;
|
|
1993
|
+
if (this.params.scriptPath && this.params.script === void 0) {
|
|
1994
|
+
const loaded = await resolveSavedWorkflowScript(
|
|
1995
|
+
{ scriptPath: this.params.scriptPath },
|
|
1996
|
+
this.config
|
|
1997
|
+
);
|
|
1998
|
+
resolvedScript = loaded.script;
|
|
1999
|
+
resolvedScriptPath = loaded.scriptPath;
|
|
2000
|
+
}
|
|
2001
|
+
const runId = this.params.resumeFromRunId ?? `wf_${randomBytes2(8).toString("hex")}`;
|
|
2002
|
+
let journal;
|
|
2003
|
+
let resumeReplay;
|
|
2004
|
+
const storage = this.config.storage;
|
|
2005
|
+
if (storage) {
|
|
2006
|
+
journal = new WorkflowJournal(storage.getWorkflowRunJournalPath(runId));
|
|
2007
|
+
if (this.params.resumeFromRunId) {
|
|
2008
|
+
resumeReplay = await journal.load();
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
1596
2011
|
const registry = this.config.getWorkflowRunRegistry?.();
|
|
1597
2012
|
const registryEntry = registry?.register({
|
|
1598
2013
|
runId,
|
|
@@ -1606,7 +2021,13 @@ var WorkflowToolInvocation = class extends BaseToolInvocation {
|
|
|
1606
2021
|
// P5: seed the cap so the dialog can render the `M / N` form
|
|
1607
2022
|
// immediately, before the first `budgetUpdated` fires. Stays
|
|
1608
2023
|
// `null` when no env override.
|
|
1609
|
-
tokenBudgetTotal: budget.total
|
|
2024
|
+
tokenBudgetTotal: budget.total,
|
|
2025
|
+
// P7b: carry the script source so a completed run can be snapshotted
|
|
2026
|
+
// to disk and saved to `.qwen/workflows/<name>.js` from the dialog.
|
|
2027
|
+
// `scriptPath` is set when the run was launched from a saved file (run
|
|
2028
|
+
// provenance for the snapshot).
|
|
2029
|
+
script: resolvedScript,
|
|
2030
|
+
scriptPath: resolvedScriptPath
|
|
1610
2031
|
});
|
|
1611
2032
|
const emitter = {
|
|
1612
2033
|
phaseStarted: /* @__PURE__ */ __name((title) => {
|
|
@@ -1629,12 +2050,18 @@ var WorkflowToolInvocation = class extends BaseToolInvocation {
|
|
|
1629
2050
|
};
|
|
1630
2051
|
try {
|
|
1631
2052
|
const outcome = await orchestrator.run({
|
|
1632
|
-
script:
|
|
2053
|
+
script: resolvedScript,
|
|
1633
2054
|
args: this.params.args,
|
|
1634
2055
|
abortOnTimeout: dispatchController,
|
|
1635
2056
|
runId,
|
|
1636
2057
|
emitter,
|
|
1637
|
-
budget
|
|
2058
|
+
budget,
|
|
2059
|
+
// P-nested: resolve `workflow('<name>')` / `workflow({scriptPath})`
|
|
2060
|
+
// against the saved-workflow scripts in `.qwen/workflows/`.
|
|
2061
|
+
resolveSavedWorkflow: /* @__PURE__ */ __name((ref) => resolveSavedWorkflowScript(ref, this.config), "resolveSavedWorkflow"),
|
|
2062
|
+
// P6: resume journal (always wired) + replay maps (resume only).
|
|
2063
|
+
journal,
|
|
2064
|
+
resumeReplay
|
|
1638
2065
|
});
|
|
1639
2066
|
if (registryEntry) {
|
|
1640
2067
|
registryEntry.meta = outcome.meta;
|
|
@@ -1704,6 +2131,23 @@ ${safeStringifyDisplayPayload({
|
|
|
1704
2131
|
};
|
|
1705
2132
|
} finally {
|
|
1706
2133
|
dispatchController.abort();
|
|
2134
|
+
if (registryEntry && registryEntry.status !== "running") {
|
|
2135
|
+
await writeWorkflowSnapshot(this.config, registryEntry);
|
|
2136
|
+
try {
|
|
2137
|
+
logWorkflowRun(
|
|
2138
|
+
this.config,
|
|
2139
|
+
new WorkflowRunEvent({
|
|
2140
|
+
status: registryEntry.status,
|
|
2141
|
+
agents_dispatched: registryEntry.agentsDispatched,
|
|
2142
|
+
agents_completed: registryEntry.agentsCompleted,
|
|
2143
|
+
phase_count: registryEntry.phases.length,
|
|
2144
|
+
tokens_spent: registryEntry.tokensSpent,
|
|
2145
|
+
duration_ms: (registryEntry.endTime ?? registryEntry.startTime) - registryEntry.startTime
|
|
2146
|
+
})
|
|
2147
|
+
);
|
|
2148
|
+
} catch {
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
1707
2151
|
}
|
|
1708
2152
|
}
|
|
1709
2153
|
};
|
|
@@ -1803,7 +2247,7 @@ var WorkflowTool = class extends BaseDeclarativeTool {
|
|
|
1803
2247
|
super(
|
|
1804
2248
|
ToolNames.WORKFLOW,
|
|
1805
2249
|
ToolDisplayNames.WORKFLOW,
|
|
1806
|
-
"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.
|
|
2250
|
+
"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. Pass `resumeFromRunId` to resume a prior run \u2014 agent() calls whose rolling prefix-hash matches the journal are served from cache for the longest unchanged prefix. Runs are tracked in the background-tasks view and the `/workflows` dialog (live phase tree, token usage, cancel). Scripts run in a node:vm sandbox without access to the filesystem or shell; all I/O happens through the spawned agents.",
|
|
1807
2251
|
"other" /* Other */,
|
|
1808
2252
|
WORKFLOW_PARAM_SCHEMA,
|
|
1809
2253
|
/* isOutputMarkdown */
|
|
@@ -1818,8 +2262,16 @@ var WorkflowTool = class extends BaseDeclarativeTool {
|
|
|
1818
2262
|
__name(this, "WorkflowTool");
|
|
1819
2263
|
}
|
|
1820
2264
|
validateToolParamValues(params) {
|
|
1821
|
-
|
|
1822
|
-
|
|
2265
|
+
const hasScript = typeof params.script === "string" && params.script.length > 0;
|
|
2266
|
+
const hasPath = typeof params.scriptPath === "string" && params.scriptPath.length > 0;
|
|
2267
|
+
if (!hasScript && !hasPath) {
|
|
2268
|
+
return "WorkflowTool: provide `script` (inline source) or `scriptPath` (a saved workflow file).";
|
|
2269
|
+
}
|
|
2270
|
+
if (hasScript && hasPath) {
|
|
2271
|
+
return "WorkflowTool: provide exactly one of `script` or `scriptPath`, not both.";
|
|
2272
|
+
}
|
|
2273
|
+
if (params.resumeFromRunId !== void 0 && !/^wf_[0-9a-f]+$/.test(params.resumeFromRunId)) {
|
|
2274
|
+
return "WorkflowTool: `resumeFromRunId` must match the generated id format `wf_<hex>`.";
|
|
1823
2275
|
}
|
|
1824
2276
|
return null;
|
|
1825
2277
|
}
|