@qwen-code/qwen-code 0.23.5-dev.codemode-responses.20260915.f024b37689 → 0.23.5-preview.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/computer-use/SKILL.md +24 -3
- package/bundled/qc-helper/docs/configuration/model-providers.md +43 -0
- package/bundled/qc-helper/docs/configuration/settings.md +10 -9
- package/bundled/qc-helper/docs/extension/agent-plugins.md +1 -0
- package/bundled/qc-helper/docs/extension/introduction.md +49 -0
- package/bundled/qc-helper/docs/features/channels/dingtalk.md +39 -4
- package/bundled/qc-helper/docs/features/channels/overview.md +12 -0
- package/bundled/qc-helper/docs/features/commands.md +57 -52
- package/bundled/qc-helper/docs/features/computer-use.md +1 -1
- package/bundled/qc-helper/docs/features/goals.md +8 -0
- package/bundled/qc-helper/docs/features/hooks.md +16 -0
- package/bundled/qc-helper/docs/qwen-serve.md +3 -1
- package/bundled/workflow-authoring/SKILL.md +48 -22
- package/chunks/{MaxSizedBox-BFMIFJWT.js → MaxSizedBox-FCF6M53W.js} +40 -43
- package/chunks/{StandaloneSessionPicker-4ZH7MOVG.js → StandaloneSessionPicker-V635V72I.js} +60 -63
- package/chunks/{acp-startup-profiler-RUQNTNU5.js → acp-startup-profiler-MOOU37VM.js} +2 -2
- package/chunks/{acp-subagent-executor-AHMVA4P3.js → acp-subagent-executor-TND6W2O5.js} +46 -47
- package/chunks/{acpAgent-UMAJ3XR3.js → acpAgent-3DPWZTNC.js} +954 -322
- package/chunks/{agent-CXB2CCQN.js → agent-KIHGINKN.js} +27 -30
- package/chunks/{agent-headless-7D5ZC736.js → agent-headless-EJ4QOUSM.js} +27 -30
- package/chunks/{anthropicContentGenerator-H5YPXIDE.js → anthropicContentGenerator-4FGOM5LK.js} +46 -64
- package/chunks/{artifact-tool-MFGORTWZ.js → artifact-tool-MUTNH4NI.js} +1 -1
- package/chunks/{askUserQuestion-WS4GUT7C.js → askUserQuestion-AKIRLVB5.js} +1 -1
- package/chunks/bareMode-QWKZPLPX.js +14 -0
- package/chunks/{bridge-A2WKX6IK.js → bridge-MUX5YJZT.js} +46 -49
- package/chunks/{ca-GZ4FDJU2.js → ca-WC3A5B3Q.js} +12 -1
- package/chunks/{channel-management-service-AFVIT4VH.js → channel-management-service-RRILWSXX.js} +4 -4
- package/chunks/{channel-settings-store-AXJIN7HG.js → channel-settings-store-7CBIRZER.js} +51 -54
- package/chunks/{channel-worker-group-JR6JOELM.js → channel-worker-group-3DUJFZJR.js} +8 -8
- package/chunks/{channel-worker-manager-AVKJEZFL.js → channel-worker-manager-E5UW2VLA.js} +8 -8
- package/chunks/{channel-worker-supervisor-OOYY7BAQ.js → channel-worker-supervisor-VN33GSEC.js} +5 -5
- package/chunks/{chunk-2YMRRI2C.js → chunk-2C5TR6HN.js} +82 -9
- package/chunks/{chunk-YDFOHRS5.js → chunk-2GP6BQ2J.js} +1 -1
- package/chunks/{chunk-RN6BU77T.js → chunk-347O2YZJ.js} +158 -290
- package/chunks/{chunk-LTINUI3O.js → chunk-36NRLIR6.js} +10 -8
- package/chunks/{chunk-AXZMWAJV.js → chunk-36SDZ2Q4.js} +8 -0
- package/chunks/{chunk-QR4Q425T.js → chunk-3BZI5GDP.js} +1 -1
- package/chunks/{chunk-C3YMBSLR.js → chunk-3HTUE6ZR.js} +3 -3
- package/chunks/{chunk-QS5Q5F42.js → chunk-3LLEB5CU.js} +2 -2
- package/chunks/{chunk-FPPG5Y3W.js → chunk-3UGMLA4V.js} +10 -8
- package/chunks/{chunk-66N6C4EH.js → chunk-3WILPKTJ.js} +2 -2
- package/chunks/{chunk-SROLVJFY.js → chunk-3WWPEFMX.js} +1 -1
- package/chunks/{chunk-ZKIAGIGH.js → chunk-3XBUCOWS.js} +1 -1
- package/chunks/{chunk-6RQFMGN2.js → chunk-43Y26NPF.js} +3 -3
- package/chunks/{chunk-INGBUYL6.js → chunk-4FLUQWJ4.js} +1 -1
- package/chunks/{chunk-S77FFY67.js → chunk-4JFLRHNE.js} +1 -1
- package/chunks/{chunk-MJHLKWGJ.js → chunk-4QPVZA5J.js} +70 -23
- package/chunks/{chunk-XX5HQLCZ.js → chunk-6OTF7TTE.js} +32 -1
- package/chunks/{chunk-4Y2I3CI3.js → chunk-6USYH3EU.js} +99 -6
- package/chunks/{chunk-Q3TFRP5G.js → chunk-7D4CV7JH.js} +102 -9
- package/chunks/{chunk-EWJTAP3Y.js → chunk-7O5PYQ3M.js} +20 -1
- package/chunks/chunk-ADN5KESB.js +28 -0
- package/chunks/chunk-AKRSSKUT.js +229 -0
- package/chunks/{chunk-54YUXX7X.js → chunk-AQP3H22J.js} +1 -1
- package/chunks/{chunk-EQWKIA72.js → chunk-AUX2FL5T.js} +24 -7
- package/chunks/{chunk-YGFJ6BRT.js → chunk-AZCTY64Z.js} +1 -1
- package/chunks/{chunk-QLX4D55R.js → chunk-B5TJK7B3.js} +1 -1
- package/chunks/{chunk-HXMDDII6.js → chunk-B6RMK4ED.js} +4 -4
- package/chunks/{chunk-UCKM6KCJ.js → chunk-BHG3H6OZ.js} +1 -1
- package/chunks/{chunk-JLF2AQAL.js → chunk-BHVF3R6B.js} +7 -5
- package/chunks/{chunk-HFJSQ4GL.js → chunk-BTICBRPL.js} +1 -1
- package/chunks/{chunk-KZCK5JUN.js → chunk-C3T6HPNM.js} +1 -1
- package/chunks/{chunk-FCLMJS6N.js → chunk-CDDP4ONL.js} +164 -27
- package/chunks/{chunk-Y3ZXDBUL.js → chunk-CQPM66MG.js} +4 -4
- package/chunks/{chunk-WKVZES7O.js → chunk-CXONOVBN.js} +1 -1
- package/chunks/{chunk-TYBRGFYL.js → chunk-CXY6BICQ.js} +501 -479
- package/chunks/{chunk-W23HOFDK.js → chunk-D3KVGQUV.js} +1 -1
- package/chunks/{chunk-LMJSPC55.js → chunk-D7JMO2GP.js} +3 -7
- package/chunks/{chunk-CXXAZ2LZ.js → chunk-DC4ZNGH4.js} +1 -1
- package/chunks/{chunk-FMLBBXHB.js → chunk-DRAX5YAI.js} +2 -2
- package/chunks/{chunk-DOTF2PF2.js → chunk-DRD6VBNH.js} +1 -1
- package/chunks/chunk-DX6JTDMR.js +105 -0
- package/chunks/{chunk-MNRK6MIH.js → chunk-E2VSBAFA.js} +53 -11
- package/chunks/{chunk-QJKR2P2K.js → chunk-EIBECN2J.js} +3 -3
- package/chunks/{chunk-LWWRJRCD.js → chunk-ESBYWATJ.js} +1 -1
- package/chunks/{chunk-3544ZEGZ.js → chunk-FASYATAZ.js} +5 -7
- package/chunks/{chunk-6YUS5JCV.js → chunk-FBY32737.js} +4 -4
- package/chunks/{chunk-LBWIJ5T7.js → chunk-FCPJXKDG.js} +10 -7
- package/chunks/{chunk-FX6GJIPZ.js → chunk-FEDFZBGR.js} +3 -3
- package/chunks/{chunk-DQ4LBRZN.js → chunk-FIJODK52.js} +3 -3
- package/chunks/{chunk-HVSRWLOW.js → chunk-FX6PZBOV.js} +3 -3
- package/chunks/{chunk-L6DIANLD.js → chunk-G45B3INR.js} +1 -1
- package/chunks/{chunk-EYLUKEYW.js → chunk-GI7SLDOI.js} +2 -2
- package/chunks/chunk-GJKP6XOB.js +1092 -0
- package/chunks/{chunk-I6YJZGHS.js → chunk-GSAOVDTH.js} +9 -9
- package/chunks/{chunk-XK4UQSIR.js → chunk-H6L4NEAT.js} +1 -1
- package/chunks/{chunk-GMCEFKNR.js → chunk-HNFKYKHH.js} +103 -22
- package/chunks/{chunk-6XDB3FR4.js → chunk-HX4WXX3H.js} +301 -138
- package/chunks/{chunk-HG46TTAG.js → chunk-HXUI5D4H.js} +1 -1
- package/chunks/{chunk-QI4WBHPF.js → chunk-HYHSMTRA.js} +4 -4
- package/chunks/{chunk-ZUKC5J6Q.js → chunk-IKBEIDOH.js} +14 -14
- package/chunks/{chunk-LCAJ63TF.js → chunk-INN5ORKX.js} +1 -1
- package/chunks/{chunk-GNV2SFO7.js → chunk-IVFTLUZD.js} +26 -1
- package/chunks/{chunk-GNKSR3FU.js → chunk-IXO2OCIP.js} +2 -2
- package/chunks/{chunk-SNT634Q6.js → chunk-K4XMAIRX.js} +30 -33
- package/chunks/{chunk-WBP64MZR.js → chunk-KAYC5FBN.js} +5269 -2785
- package/chunks/chunk-KEM7MWS7.js +3541 -0
- package/chunks/{chunk-ADQUK6WA.js → chunk-KVC7HZWK.js} +1 -1
- package/chunks/{chunk-UQXURNLM.js → chunk-KVQ3QL4K.js} +3 -3
- package/chunks/{chunk-7U5G6JXI.js → chunk-KWYTSSTJ.js} +105 -6
- package/chunks/{chunk-Q4G2YPI4.js → chunk-L2A5VBDL.js} +3019 -2352
- package/chunks/{chunk-WD6YQI5M.js → chunk-L3SNMEDW.js} +1 -1
- package/chunks/{chunk-BYUAT5OC.js → chunk-L7TJUPNR.js} +19 -3
- package/chunks/{chunk-ABNX6E6K.js → chunk-LFKXIOGC.js} +1 -1
- package/chunks/{chunk-GFAK6DYU.js → chunk-LO2UWNO5.js} +2 -2
- package/chunks/{chunk-67ZDP7UX.js → chunk-M22WLKLI.js} +5 -5
- package/chunks/{chunk-54B2LTKP.js → chunk-M23GFJ5B.js} +24 -5
- package/chunks/{chunk-VGY2RGXL.js → chunk-M4U2LTD2.js} +1 -1
- package/chunks/{chunk-ZNI3YJHS.js → chunk-MFDKHGTD.js} +1 -1
- package/chunks/{chunk-R7OO56VZ.js → chunk-MMRGG6R4.js} +1 -1
- package/chunks/{chunk-E6IR653O.js → chunk-MO4XID7S.js} +1 -1
- package/chunks/{chunk-NJ4ZNDVP.js → chunk-NKTMUPHJ.js} +3 -6
- package/chunks/{chunk-4DTFTPTU.js → chunk-NQ272MQG.js} +1 -1
- package/chunks/{chunk-DWKFTUGI.js → chunk-OALXNGUA.js} +6 -6
- package/chunks/{chunk-FBDHP23D.js → chunk-OSG23MR3.js} +1 -1
- package/chunks/{chunk-NJ2OFVUX.js → chunk-P42KMMU6.js} +860 -1
- package/chunks/{chunk-5KJ54DY3.js → chunk-PF7GAP6K.js} +60 -58
- package/chunks/{chunk-MQRLLBMQ.js → chunk-PPJSG2PU.js} +2 -2
- package/chunks/{chunk-EPVEPTI3.js → chunk-PZRXWQUA.js} +753 -18
- package/chunks/{chunk-VI5UVPOB.js → chunk-Q54O3EIA.js} +30 -28
- package/chunks/{chunk-QTUQV33C.js → chunk-QGEISOJ2.js} +4 -4
- package/chunks/{chunk-XKRYQOHS.js → chunk-QHSOINX6.js} +2 -2
- package/chunks/{chunk-Z6Q7I37O.js → chunk-QIPLWW6Q.js} +1 -1
- package/chunks/chunk-QMENOK5T.js +1246 -0
- package/chunks/chunk-QT6Z2IBF.js +347 -0
- package/chunks/{chunk-TBSCTWKF.js → chunk-RN6A53UJ.js} +25 -0
- package/chunks/{chunk-E7ZTAOJZ.js → chunk-RRI764F7.js} +21 -19
- package/chunks/{chunk-YI2YCNSP.js → chunk-SENYKRII.js} +1 -1
- package/chunks/chunk-SFHX5G2S.js +2796 -0
- package/chunks/chunk-SK3RZOPS.js +34 -0
- package/chunks/{chunk-SKYOAH4Z.js → chunk-SOLRBF7L.js} +13 -9
- package/chunks/{chunk-5WLF44DS.js → chunk-SSWTA7CH.js} +12 -12
- package/chunks/{chunk-IAS753HG.js → chunk-SYMXYRM5.js} +11 -2
- package/chunks/{chunk-FGROOEKM.js → chunk-TDKUXSJO.js} +34 -6
- package/chunks/{chunk-ZJ7JQUGY.js → chunk-TKF4JINY.js} +1 -1
- package/chunks/{chunk-ZOQ6HC7U.js → chunk-TPY6HW55.js} +4 -4
- package/chunks/{chunk-EDB556X7.js → chunk-TRZ22SG5.js} +1 -1
- package/chunks/{chunk-FHSOZSBO.js → chunk-TXTCM3TC.js} +4 -4
- package/chunks/{chunk-5ODRE7E5.js → chunk-U4AASWUQ.js} +28 -12
- package/chunks/{chunk-PPE7VUMD.js → chunk-UQGRJTDK.js} +2 -2
- package/chunks/{chunk-JO67LVXW.js → chunk-UZSB57EV.js} +6 -8
- package/chunks/{chunk-UY4OCBX4.js → chunk-V4JJAVHQ.js} +2 -2
- package/chunks/chunk-VEXUA6DX.js +1026 -0
- package/chunks/{chunk-ME4JBHSB.js → chunk-VQDWCQOE.js} +2 -0
- package/chunks/{chunk-ZC75SULX.js → chunk-VU3XMZ5I.js} +23 -4
- package/chunks/{chunk-A2UYI473.js → chunk-WL36KHUQ.js} +6 -6
- package/chunks/{chunk-IWOJ2FHG.js → chunk-WNQIJTOP.js} +8 -8
- package/chunks/{chunk-IANIY25T.js → chunk-WSJI6ODG.js} +99 -30
- package/chunks/{chunk-3CKAOJ4L.js → chunk-WZWTY7SG.js} +6 -6
- package/chunks/{chunk-EKMJR7M3.js → chunk-X7AYIXC7.js} +1 -1
- package/chunks/{chunk-IBRQG2MP.js → chunk-XAJZFGD2.js} +11 -11
- package/chunks/{chunk-VRQZESKP.js → chunk-XICCGDWF.js} +2 -2
- package/chunks/{chunk-L5R4D5T4.js → chunk-Y2KJC55Y.js} +12 -7
- package/chunks/{chunk-2S2WIGCM.js → chunk-Y4NNIGYO.js} +1 -1
- package/chunks/{chunk-J7LBAKDH.js → chunk-YL3UJML3.js} +7 -7
- package/chunks/{chunk-GSS4SZNG.js → chunk-YP2K3WOS.js} +40 -56
- package/chunks/{chunk-ZZUXUBEB.js → chunk-YQQ2U7AS.js} +8 -5
- package/chunks/{chunk-FAWTMDX7.js → chunk-YXHY6BKS.js} +15 -0
- package/chunks/{chunk-FIP5QS2U.js → chunk-YZXSDPOQ.js} +140 -39
- package/chunks/{chunk-WATHOB6X.js → chunk-ZFHKUQZ4.js} +3 -3
- package/chunks/{chunk-R5OA3ONY.js → chunk-ZMYKNKET.js} +6 -6
- package/chunks/{codex-subagent-executor-LANZAZPH.js → codex-subagent-executor-AEAIQZDC.js} +48 -49
- package/chunks/{config-utils-CRK2LQPA.js → config-utils-KX2YS725.js} +43 -46
- package/chunks/{contextCommand-GY5X3LIY.js → contextCommand-RV6Y5LNJ.js} +41 -44
- package/chunks/{core-runtime-IJIV2LHP.js → core-runtime-ZI4RTOJQ.js} +45 -48
- package/chunks/{create-sub-session-MNIVRHGB.js → create-sub-session-BBWFQARJ.js} +46 -48
- package/chunks/{daemon-NJB4PWZ7.js → daemon-INCM4VBX.js} +3 -1
- package/chunks/{daemon-git-worktree-guard-W6Z2BGDH.js → daemon-git-worktree-guard-KECEMV6A.js} +39 -42
- package/chunks/{daemon-status-provider-TJF3APU3.js → daemon-status-provider-NULJNAEV.js} +53 -56
- package/chunks/{daemon-trust-policy-6INCISDU.js → daemon-trust-policy-S4KWGPV3.js} +47 -50
- package/chunks/{daemon-trust-policy-monitor-JRTAQG54.js → daemon-trust-policy-monitor-546N5ZVL.js} +47 -50
- package/chunks/{de-VGY3VNBU.js → de-6AVKPTXQ.js} +11 -0
- package/chunks/{deferred-core-runtime-2JRTPTNM.js → deferred-core-runtime-T23ZT443.js} +39 -42
- package/chunks/{dist-NIOJXMGY.js → dist-4P3ZJYGI.js} +1 -1
- package/chunks/{dist-VDLSBCLM.js → dist-7MGRENQ5.js} +1 -1
- package/chunks/{dist-SE4J6Z4O.js → dist-BNW423XT.js} +326 -77
- package/chunks/{dist-TFGETXFN.js → dist-CESMUYGN.js} +4 -4
- package/chunks/{dist-MPUZZOG5.js → dist-DBBOFWSC.js} +1 -1
- package/chunks/{dist-T6DDPDLQ.js → dist-G5UMXIGF.js} +1 -1
- package/chunks/{dist-PA7WPFVE.js → dist-UXVNLQHG.js} +3 -3
- package/chunks/{dist-GUB7QRYA.js → dist-VA4LREAQ.js} +2 -2
- package/chunks/{dist-2QYIVNS6.js → dist-XDRTR4QP.js} +1 -1
- package/chunks/{edit-JDIXXCYB.js → edit-CMSQB7NP.js} +27 -30
- package/chunks/{en-E5ZO5TZW.js → en-56QLKXS2.js} +23 -1
- package/chunks/{enter-worktree-CAARFWBI.js → enter-worktree-DA6A4XC5.js} +3 -3
- package/chunks/{enterPlanMode-YTNTBZBN.js → enterPlanMode-UDPMNESD.js} +27 -30
- package/chunks/{environment-UAI5L52V.js → environment-TSQJUTPV.js} +42 -45
- package/chunks/{errors-J4UCDCW5.js → errors-RIVYJS6T.js} +41 -44
- package/chunks/{exit-worktree-JXGGUOBP.js → exit-worktree-4H6Z2VQI.js} +3 -3
- package/chunks/{exitPlanMode-YMWJ2EO7.js → exitPlanMode-BYBBFN2T.js} +27 -30
- package/chunks/{fast-path-MBAN7TK3.js → fast-path-F2CYDDZ4.js} +4 -4
- package/chunks/{fast-path-settings-XDYX7X5Z.js → fast-path-settings-N2VGRGDJ.js} +2 -2
- package/chunks/{fr-UAZJJH5N.js → fr-EBUDHJL3.js} +11 -0
- package/chunks/{glob-YEM24E3Z.js → glob-Z2JMJKKN.js} +27 -30
- package/chunks/{goal-tools-LJIVH5T6.js → goal-tools-HXVVLGCV.js} +27 -30
- package/chunks/{handleAutoUpdate-DYCSYDYL.js → handleAutoUpdate-FZP45KOR.js} +43 -46
- package/chunks/{i18n-PTAV7SN3.js → i18n-OTB62AKW.js} +40 -43
- package/chunks/{image-gen-5G2TQ3DY.js → image-gen-HIAZCB5T.js} +6 -7
- package/chunks/{initializer-MPXTKQA4.js → initializer-4JXZEC4B.js} +47 -50
- package/chunks/{installationInfo-U5EFA3OL.js → installationInfo-2IJV5QKC.js} +39 -42
- package/chunks/{ja-2RITCMAJ.js → ja-NCDT4CGV.js} +11 -0
- package/chunks/{list-YOE25QUI.js → list-KQSQ4A5G.js} +50 -53
- package/chunks/{llm-ZZ2WNQMC.js → llm-LSUBOJAR.js} +154 -974
- package/chunks/{llm-content-generator-CG2HRJZC.js → llm-content-generator-MLIONRXE.js} +17 -12
- package/chunks/{loadedSettingsAdapter-NPGWGURF.js → loadedSettingsAdapter-TZTTNRHI.js} +47 -50
- package/chunks/{loggingContentGenerator-TUL6SJVL.js → loggingContentGenerator-TUPEXFMM.js} +31 -34
- package/chunks/{loop-wakeup-VG6CGBQA.js → loop-wakeup-ZBVRI6VH.js} +1 -1
- package/chunks/{managed-npm-update-YUDSCJP3.js → managed-npm-update-WEA5XMFZ.js} +40 -43
- package/chunks/{mcp-EX3GDQVO.js → mcp-TFDTUPIA.js} +47 -50
- package/chunks/{model-configuration-6DEKTXJI.js → model-configuration-ZCAKKIOF.js} +48 -51
- package/chunks/{modelConfigUtils-4NA2MHEE.js → modelConfigUtils-J3ZPW7ZK.js} +41 -44
- package/chunks/{monitor-PPUGNAWR.js → monitor-CI7YE7MT.js} +29 -32
- package/chunks/{nonInteractiveCli-6TW2XYP5.js → nonInteractiveCli-GPT3TVOK.js} +96 -98
- package/chunks/{notebook-edit-4MUXHZHT.js → notebook-edit-3WCVRNGL.js} +27 -30
- package/chunks/{openaiContentGenerator-5J73W5XZ.js → openaiContentGenerator-OGILGUZE.js} +12 -17
- package/chunks/{openaiResponsesContentGenerator-AWRYZLJU.js → openaiResponsesContentGenerator-NXOXUPFM.js} +45 -13
- package/chunks/{pidfile-M7WELKBB.js → pidfile-GBTBIGHF.js} +39 -42
- package/chunks/{process-registry-X6ZCASEN.js → process-registry-7LAPI3ZB.js} +1 -1
- package/chunks/{processUtils-SPRINFNY.js → processUtils-O4II3RYH.js} +2 -2
- package/chunks/{prompt-terminal-ledger-25BRD7FX.js → prompt-terminal-ledger-ILIXZI3E.js} +40 -43
- package/chunks/{pt-5ZF7ZQUV.js → pt-FYYFWYUC.js} +11 -0
- package/chunks/{qwenContentGenerator-H5AAKXPC.js → qwenContentGenerator-I4LRYADV.js} +33 -36
- package/chunks/{qwenOAuth2-WIEUEFEY.js → qwenOAuth2-3J66TOT5.js} +6 -7
- package/chunks/{read-file-ILGEFPB3.js → read-file-G4XKW2CS.js} +5 -9
- package/chunks/{record-artifact-PXMNP7GZ.js → record-artifact-L3CFJOMA.js} +2 -2
- package/chunks/{report-findings-W6GR7SYZ.js → report-findings-LY5HQY4K.js} +3 -3
- package/chunks/{resumeHistoryUtils-RB52QOCH.js → resumeHistoryUtils-YPJU4XFE.js} +44 -47
- package/chunks/{ripGrep-O6QAFHQK.js → ripGrep-AAKMALLM.js} +7 -11
- package/chunks/{ru-7BNZFQKG.js → ru-RHEB3RNS.js} +11 -0
- package/chunks/{run-qwen-serve-5NO3TKUL.js → run-qwen-serve-ZVEKGS7Y.js} +177 -98
- package/chunks/{runtime-G2YUYMSZ.js → runtime-SJCRYNPA.js} +52 -55
- package/chunks/safe-mode-KLGUG54M.js +11 -0
- package/chunks/sandbox-UIXL423Z.js +146 -0
- package/chunks/sandboxConfig-CY343YNJ.js +114 -0
- package/chunks/{scheduled-tasks-HDHV3URA.js → scheduled-tasks-5UGBEVNZ.js} +50 -53
- package/chunks/{scheduler-6MDJX2ZK.js → scheduler-GCZYLZ2P.js} +41 -44
- package/chunks/{sdk-exporters-http-3R2TRQHE.js → sdk-exporters-http-DR4G663K.js} +1 -1
- package/chunks/{sdk-impl-4WF3VZ2F.js → sdk-impl-SJR26MQF.js} +3 -3
- package/chunks/{send-message-UMJKN27Q.js → send-message-DN76MXDX.js} +6 -6
- package/chunks/{serve-Z2EJQYKB.js → serve-2XXQLVK2.js} +48 -51
- package/chunks/{server-JTNBK3LH.js → server-UHMKPCAZ.js} +408 -211
- package/chunks/{session-5YK2BXH4.js → session-ZLUK7E4P.js} +108 -108
- package/chunks/{session-attachments-root-QO3VFJS6.js → session-attachments-root-DHQM5IEZ.js} +39 -42
- package/chunks/{session-pr-refresh-OTK3HCJZ.js → session-pr-refresh-3OMTQWCZ.js} +48 -51
- package/chunks/{settings-2VKLACYV.js → settings-P4DHIIUP.js} +50 -53
- package/chunks/{shell-64NAVGMS.js → shell-IMFUVQQ5.js} +27 -30
- package/chunks/{skill-R3ALMLLM.js → skill-NB5RJGZW.js} +7 -11
- package/chunks/{skill-settings-HZ34TUMP.js → skill-settings-IXG5CTQO.js} +47 -50
- package/chunks/{spawnChannel-XGFWHEIK.js → spawnChannel-Q3YALTCJ.js} +44 -45
- package/chunks/{standalone-update-R32Y3XRR.js → standalone-update-H5OJ7MM7.js} +41 -44
- package/chunks/{start-opentui-ui-YGJOUE2H.js → start-opentui-ui-JKAIWGF7.js} +733 -386
- package/chunks/{startInteractiveUI-5ZEVKSFF.js → startInteractiveUI-YRPOT6GH.js} +370 -337
- package/chunks/task-create-75HVPEAH.js +1 -1
- package/chunks/{team-create-GCVUXJLE.js → team-create-2XTBYRJF.js} +31 -34
- package/chunks/{team-plan-approval-NOL62FJD.js → team-plan-approval-CFNWW7ZQ.js} +27 -30
- package/chunks/{terminal-image-renderer-WG5TLPGC.js → terminal-image-renderer-UDPJPGPQ.js} +39 -42
- package/chunks/{theme-manager-HDS4YFPW.js → theme-manager-SK57EKMO.js} +39 -42
- package/chunks/{tool-search-HMT5GYUR.js → tool-search-ZHTJU2MV.js} +5 -9
- package/chunks/{total-session-admission-Y5RCHEXO.js → total-session-admission-UUZBDQCG.js} +47 -50
- package/chunks/{tree-sitter-LSORDVZ3.js → tree-sitter-4D7QS7F6.js} +1 -1
- package/chunks/{tree-sitter-bash-UARELQ6C.js → tree-sitter-bash-VEJGF5XF.js} +1 -1
- package/chunks/{trustedFolders-3Y2WCXMR.js → trustedFolders-4LDBLNTS.js} +40 -43
- package/chunks/{update-relaunch-MTJ32SLF.js → update-relaunch-LI7GRP36.js} +5 -5
- package/chunks/{updateCheck-YFX3VY6O.js → updateCheck-PKSSORPO.js} +43 -46
- package/chunks/{useAutoAcceptIndicator-FCHNQPMD.js → useAutoAcceptIndicator-I32UEQPI.js} +50 -53
- package/chunks/{validateNonInterActiveAuth-HUFQKCGG.js → validateNonInterActiveAuth-YFPCYYNL.js} +93 -95
- package/chunks/{version-S5TGWCA3.js → version-4MTL4JVU.js} +1 -1
- package/chunks/{web-fetch-23M7BYHB.js → web-fetch-O6RG6ISB.js} +9 -13
- package/chunks/{web-search-F5PNIQUA.js → web-search-JHAXVEMD.js} +9 -14
- package/chunks/{web-shell-static-T7UGLVE4.js → web-shell-static-FFWCUUZS.js} +8 -4
- package/chunks/{workflow-BLJLID2C.js → workflow-O7L6VDYU.js} +396 -113
- package/chunks/{workspace-providers-status-UH7KLZDT.js → workspace-providers-status-WRX6CJM4.js} +53 -56
- package/chunks/{workspace-registration-store-3SA3V7BA.js → workspace-registration-store-7G4E7YPY.js} +1 -1
- package/chunks/{workspace-registry-C6C6EKQ5.js → workspace-registry-SXIMGT4D.js} +47 -50
- package/chunks/{workspace-runtime-coordinator-RAI6XDJB.js → workspace-runtime-coordinator-7DOEHXYT.js} +47 -50
- package/chunks/{workspace-service-H7UTJYJU.js → workspace-service-HDXOZMYC.js} +53 -56
- package/chunks/{workspace-skills-status-FPVC7XXK.js → workspace-skills-status-VN4L37CA.js} +50 -53
- package/chunks/{workspace-trust-reconciler-JS7A6UIF.js → workspace-trust-reconciler-XEBW5FUL.js} +57 -60
- package/chunks/{write-file-GQGG7NBQ.js → write-file-LKX4ETIO.js} +29 -32
- package/chunks/{zh-V2EYTYJP.js → zh-F76MZTL3.js} +23 -1
- package/chunks/{zh-TW-7PRVUGLW.js → zh-TW-S3YTQ4DS.js} +23 -1
- package/chunks/{zoom-image-EC43KL4H.js → zoom-image-RDPRTP4B.js} +5 -9
- package/cli.js +19 -15
- package/export-transcript-document.css +1 -1
- package/export-transcript-document.js +151 -150
- package/locales/ca.js +19 -2
- package/locales/de.js +18 -0
- package/locales/en.js +33 -2
- package/locales/fr.js +18 -0
- package/locales/ja.js +18 -0
- package/locales/pt.js +18 -0
- package/locales/ru.js +18 -0
- package/locales/zh-TW.js +33 -2
- package/locales/zh.js +33 -2
- package/package.json +3 -3
- package/web-shell/assets/{abnfDiagram-VCTEODGH-BtpzKw3A.js → abnfDiagram-VCTEODGH-RxykUDmi.js} +1 -1
- package/web-shell/assets/{arc-CQiVKZJP.js → arc-CZpgGCJC.js} +1 -1
- package/web-shell/assets/{architectureDiagram-5GKGNRK7-ekIPBb2d.js → architectureDiagram-5GKGNRK7-BHSMzNDT.js} +1 -1
- package/web-shell/assets/{blockDiagram-NRAW4CY4-BrRWkWri.js → blockDiagram-NRAW4CY4-Bn4mF9SZ.js} +1 -1
- package/web-shell/assets/{c4Diagram-UCG6FXSJ-qYS8CYZ3.js → c4Diagram-UCG6FXSJ-CBM-Bj6t.js} +1 -1
- package/web-shell/assets/channel-DFYs_LlK.js +1 -0
- package/web-shell/assets/{chunk-2Q5K7J3B-ZasyyEZm.js → chunk-2Q5K7J3B-D5Fin58O.js} +1 -1
- package/web-shell/assets/{chunk-5VM5RSS4-C_IzCGeA.js → chunk-5VM5RSS4-CY05weF7.js} +1 -1
- package/web-shell/assets/{chunk-F27PBJKO-C25NZOyD.js → chunk-F27PBJKO-3neZnj5Y.js} +1 -1
- package/web-shell/assets/{chunk-G27WJ6UU-XPOHNSGX.js → chunk-G27WJ6UU-Cd7EU4BE.js} +1 -1
- package/web-shell/assets/{chunk-JWPE2WC7-BQBh0Xni.js → chunk-JWPE2WC7-DwK1mqFw.js} +1 -1
- package/web-shell/assets/{chunk-LCL6LL3I-CoShjpX7.js → chunk-LCL6LL3I-Sl7vY22C.js} +1 -1
- package/web-shell/assets/{chunk-POPQ4Y6H-BwRmCDSz.js → chunk-POPQ4Y6H-BXNzpbVg.js} +1 -1
- package/web-shell/assets/{chunk-SVP7TREG-DSzE5-Mw.js → chunk-SVP7TREG-DjjButcp.js} +1 -1
- package/web-shell/assets/{chunk-XXDRQBXY-BQSQv30h.js → chunk-XXDRQBXY-DNOiCjOJ.js} +1 -1
- package/web-shell/assets/classDiagram-DTDB5LWJ-DnGrKXoM.js +1 -0
- package/web-shell/assets/classDiagram-v2-JRS7N3AN-DnGrKXoM.js +1 -0
- package/web-shell/assets/{cose-bilkent-JH36ORCC-XVUVTHVo.js → cose-bilkent-JH36ORCC-BUnker7a.js} +1 -1
- package/web-shell/assets/{cynefin-OW5HDTMX-CrsKx4lQ.js → cynefin-OW5HDTMX-DcUgdWE5.js} +1 -1
- package/web-shell/assets/{cynefinDiagram-5FMLGOSQ-BPeZEuHw.js → cynefinDiagram-5FMLGOSQ-CvDKsZsL.js} +1 -1
- package/web-shell/assets/{dagre-3AP2YEHR-D31cQ2Qs.js → dagre-3AP2YEHR-jpCmUbr1.js} +1 -1
- package/web-shell/assets/{diagram-S7CK7UJ4-zyiG097Q.js → diagram-S7CK7UJ4-DBe_bKE5.js} +1 -1
- package/web-shell/assets/{diagram-UQ7AKVKN-Br3vtfsy.js → diagram-UQ7AKVKN-oXvPpDgs.js} +1 -1
- package/web-shell/assets/{diagram-VSXAHHWV-Bxkhjx4u.js → diagram-VSXAHHWV-BD7-_SGh.js} +1 -1
- package/web-shell/assets/{diagram-VX7I27RA-BQH8QiDk.js → diagram-VX7I27RA-DOJL0wub.js} +1 -1
- package/web-shell/assets/{diagram-Z3DM3KII-B_aQ1cw0.js → diagram-Z3DM3KII-DRIiodDD.js} +1 -1
- package/web-shell/assets/{ebnfDiagram-PWID7BFC-B9N7BRQE.js → ebnfDiagram-PWID7BFC-Db31Ulmb.js} +1 -1
- package/web-shell/assets/{erDiagram-SSCWMZ5O-Cx6Hb1Re.js → erDiagram-SSCWMZ5O-CVHhwI2_.js} +1 -1
- package/web-shell/assets/{flowDiagram-A5DVABFB-DTuLP7GU.js → flowDiagram-A5DVABFB-DWOFy1FK.js} +1 -1
- package/web-shell/assets/{ganttDiagram-EL5Y4UJY-DYS3d-sL.js → ganttDiagram-EL5Y4UJY-C0sxQtPd.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-WWUBYQGX-Cp0EGx04.js → gitGraphDiagram-WWUBYQGX-CNituBYC.js} +1 -1
- package/web-shell/assets/index-B15NlwB8.js +2213 -0
- package/web-shell/assets/index-BSrS0tQ6.css +36 -0
- package/web-shell/assets/{index-45-LkPDB.js → index-gm9tLk7h.js} +1 -1
- package/web-shell/assets/{infoDiagram-RXCK75RN-9--kukiT.js → infoDiagram-RXCK75RN-CxhJYtIR.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-5VMMS53U-CGBdsDLD.js → ishikawaDiagram-5VMMS53U-By2mchUy.js} +1 -1
- package/web-shell/assets/{journeyDiagram-EYS64GPL-DeXhcscd.js → journeyDiagram-EYS64GPL-CVH-OweI.js} +1 -1
- package/web-shell/assets/{kanban-definition-3QL26DDD-CFdvsU3b.js → kanban-definition-3QL26DDD-Cc9dQXLy.js} +1 -1
- package/web-shell/assets/knowledge-Dg9788h4.svg +1 -0
- package/web-shell/assets/{layout-DAnwQ3KB.js → layout-C_60ysHw.js} +1 -1
- package/web-shell/assets/{linear-CTo7v7Nh.js → linear-BvAVfqwu.js} +1 -1
- package/web-shell/assets/{mermaid.core-jggSxF1i.js → mermaid.core-C3CoBKAc.js} +6 -6
- package/web-shell/assets/{mindmap-definition-FBJOCRG2-DzIepvlJ.js → mindmap-definition-FBJOCRG2-Cuu8Q8LM.js} +1 -1
- package/web-shell/assets/{pegDiagram-XKGWAZYB-BFGS9GJv.js → pegDiagram-XKGWAZYB-DmAeyDO2.js} +1 -1
- package/web-shell/assets/{pieDiagram-E7YTZNPT-B2UUcNIT.js → pieDiagram-E7YTZNPT-B7vbB_xv.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-AXDQQJYC-BgM6htrX.js → quadrantDiagram-AXDQQJYC-BqOBkRR7.js} +1 -1
- package/web-shell/assets/{railroadDiagram-O6MQD6OU-Dk9cS7N-.js → railroadDiagram-O6MQD6OU-sI06lZIu.js} +1 -1
- package/web-shell/assets/{requirementDiagram-EFPCY7ZU-C9oRg7xi.js → requirementDiagram-EFPCY7ZU-Bg13d10S.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-P5KCCOFB-B-_c7GzI.js → sankeyDiagram-P5KCCOFB-WFOzZHrR.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-WJ2MYXX4-R0M-347u.js → sequenceDiagram-WJ2MYXX4-CCdRbA4E.js} +1 -1
- package/web-shell/assets/{sizeCapture-X5ZJPWSS-Df0f8UAW.js → sizeCapture-X5ZJPWSS-BH5F3bGg.js} +1 -1
- package/web-shell/assets/{stateDiagram-HBIQ2CUA-CsUEfAA-.js → stateDiagram-HBIQ2CUA-NwS2X3QB.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-4QOOHH4V-1aU2JZXO.js +1 -0
- package/web-shell/assets/{swimlanes-XN3QIQJK-9OGHVSrx.js → swimlanes-XN3QIQJK-Bm38pM28.js} +1 -1
- package/web-shell/assets/swimlanesDiagram-VK2B7HYN-Dp-u6kQG.js +8 -0
- package/web-shell/assets/{timeline-definition-24CTP7MA-DAfGqS2B.js → timeline-definition-24CTP7MA-Do-agLSo.js} +1 -1
- package/web-shell/assets/{vennDiagram-4TSXK5OY-CKATpwHi.js → vennDiagram-4TSXK5OY-aggLfZzl.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-VM6X3IG4-CQHdhTrm.js → wardleyDiagram-VM6X3IG4-D5JR3mXX.js} +1 -1
- package/web-shell/assets/{xychartDiagram-S5SC5T6Z-5k9NLqlY.js → xychartDiagram-S5SC5T6Z-DhtLDuXs.js} +1 -1
- package/web-shell/index.html +2 -2
- package/chunks/chunk-4OQSG35W.js +0 -1416
- package/chunks/chunk-7MIBD3LG.js +0 -137
- package/chunks/chunk-ARDHE6MM.js +0 -235
- package/chunks/chunk-C75BXMPM.js +0 -169
- package/chunks/chunk-ERU3SDXT.js +0 -27
- package/chunks/chunk-FPGTNKCP.js +0 -251
- package/chunks/chunk-JIJ65YKA.js +0 -2812
- package/chunks/chunk-JL6APGBX.js +0 -43
- package/chunks/chunk-SGXJQP5Q.js +0 -4172
- package/chunks/chunk-TPEV2QHG.js +0 -514
- package/chunks/chunk-YVWS4SLL.js +0 -137
- package/web-shell/assets/channel-DSQQnOZj.js +0 -1
- package/web-shell/assets/classDiagram-DTDB5LWJ-V94ta08R.js +0 -1
- package/web-shell/assets/classDiagram-v2-JRS7N3AN-V94ta08R.js +0 -1
- package/web-shell/assets/index-H8um-iEl.js +0 -2207
- package/web-shell/assets/index-n6N-2a3v.css +0 -36
- package/web-shell/assets/stateDiagram-v2-4QOOHH4V-CnSuTPJ2.js +0 -1
- package/web-shell/assets/swimlanesDiagram-VK2B7HYN-Bb7TcWQw.js +0 -8
- package/chunks/{chunk-QZW6WRB7.js → chunk-5NUKK2DT.js} +2 -2
- package/chunks/{chunk-JS73NNWF.js → chunk-FUSEMYFX.js} +0 -0
- package/chunks/{chunk-SVWWF5TZ.js → chunk-WVURIAEE.js} +3 -3
- package/chunks/{task-update-RI7QNVDP.js → task-update-3LSYQDRW.js} +4 -4
package/chunks/chunk-SGXJQP5Q.js
DELETED
|
@@ -1,4172 +0,0 @@
|
|
|
1
|
-
// Force strict mode and setup for ESM
|
|
2
|
-
"use strict";
|
|
3
|
-
import {
|
|
4
|
-
AgentEventEmitter,
|
|
5
|
-
attachJsonlTranscriptWriter,
|
|
6
|
-
buildAgentTranscriptAttach,
|
|
7
|
-
deriveApprovalModeConfig,
|
|
8
|
-
deriveConfig,
|
|
9
|
-
deriveWorktreeConfig,
|
|
10
|
-
installSessionWorkflowRevisionWriteThrough,
|
|
11
|
-
isActiveWorkflowStatus,
|
|
12
|
-
isTerminalWorkflowStatus,
|
|
13
|
-
isWorkflowRunPersistenceActive,
|
|
14
|
-
parseSkillContent,
|
|
15
|
-
rebuildToolRegistryOnOverride,
|
|
16
|
-
resolveExternalWorktreeDir,
|
|
17
|
-
toModelVisibleSubagentResult
|
|
18
|
-
} from "./chunk-WBP64MZR.js";
|
|
19
|
-
import {
|
|
20
|
-
parsePositiveIntegerEnv
|
|
21
|
-
} from "./chunk-6PVPNMXU.js";
|
|
22
|
-
import {
|
|
23
|
-
GitWorktreeService,
|
|
24
|
-
generateAgentWorktreeSlug,
|
|
25
|
-
writeWorktreeSessionMarker
|
|
26
|
-
} from "./chunk-WKVZES7O.js";
|
|
27
|
-
import {
|
|
28
|
-
resolveBundleDir
|
|
29
|
-
} from "./chunk-ACHCT36C.js";
|
|
30
|
-
import {
|
|
31
|
-
SUBAGENT_PLAN_LIFECYCLE_TOOLS
|
|
32
|
-
} from "./chunk-AEWCOE64.js";
|
|
33
|
-
import {
|
|
34
|
-
runWithAgentContext
|
|
35
|
-
} from "./chunk-BW2SMO6L.js";
|
|
36
|
-
import {
|
|
37
|
-
read,
|
|
38
|
-
writeLine
|
|
39
|
-
} from "./chunk-FCLMJS6N.js";
|
|
40
|
-
import {
|
|
41
|
-
REASONING_EFFORT_TIERS,
|
|
42
|
-
normalizeReasoningEffort
|
|
43
|
-
} from "./chunk-ARDHE6MM.js";
|
|
44
|
-
import {
|
|
45
|
-
atomicWriteFile
|
|
46
|
-
} from "./chunk-XFFRI3WM.js";
|
|
47
|
-
import {
|
|
48
|
-
stripAnsiAndControl
|
|
49
|
-
} from "./chunk-4Y2I3CI3.js";
|
|
50
|
-
import {
|
|
51
|
-
SyntheticOutputTool
|
|
52
|
-
} from "./chunk-SYROIHU7.js";
|
|
53
|
-
import {
|
|
54
|
-
ToolNames,
|
|
55
|
-
resolveBuiltinToolName
|
|
56
|
-
} from "./chunk-RSWBT7AZ.js";
|
|
57
|
-
import {
|
|
58
|
-
Storage,
|
|
59
|
-
createDebugLogger
|
|
60
|
-
} from "./chunk-ZAFYHRVY.js";
|
|
61
|
-
import {
|
|
62
|
-
init_esbuild_shims
|
|
63
|
-
} from "./chunk-5O2XNYP6.js";
|
|
64
|
-
import {
|
|
65
|
-
__name
|
|
66
|
-
} from "./chunk-J2S4EL5Y.js";
|
|
67
|
-
|
|
68
|
-
// packages/core/src/agents/runtime/workflow-sandbox.ts
|
|
69
|
-
init_esbuild_shims();
|
|
70
|
-
import * as vm from "node:vm";
|
|
71
|
-
|
|
72
|
-
// packages/core/src/agents/runtime/workflow-meta-literal.ts
|
|
73
|
-
init_esbuild_shims();
|
|
74
|
-
var WorkflowMetaSyntaxError = class extends Error {
|
|
75
|
-
static {
|
|
76
|
-
__name(this, "WorkflowMetaSyntaxError");
|
|
77
|
-
}
|
|
78
|
-
index;
|
|
79
|
-
constructor(message, index) {
|
|
80
|
-
super(message);
|
|
81
|
-
this.name = "WorkflowMetaSyntaxError";
|
|
82
|
-
this.index = index;
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
function parseWorkflowMetaLiteral(source) {
|
|
86
|
-
const parser = new MetaLiteralParser(source);
|
|
87
|
-
parser.skipTrivia();
|
|
88
|
-
const value = parser.parseValue(0);
|
|
89
|
-
parser.skipTrivia();
|
|
90
|
-
if (parser.index < source.length) {
|
|
91
|
-
throw parser.fail("unexpected trailing content");
|
|
92
|
-
}
|
|
93
|
-
return value;
|
|
94
|
-
}
|
|
95
|
-
__name(parseWorkflowMetaLiteral, "parseWorkflowMetaLiteral");
|
|
96
|
-
var MAX_DEPTH = 32;
|
|
97
|
-
var WHITESPACE = /* @__PURE__ */ new Set([" ", " ", "\n", "\r", "\f", "\v", "\xA0", "\uFEFF"]);
|
|
98
|
-
var ID_START = /[A-Za-z_$]/;
|
|
99
|
-
var ID_PART = /[A-Za-z0-9_$]/;
|
|
100
|
-
var MetaLiteralParser = class {
|
|
101
|
-
constructor(src) {
|
|
102
|
-
this.src = src;
|
|
103
|
-
}
|
|
104
|
-
static {
|
|
105
|
-
__name(this, "MetaLiteralParser");
|
|
106
|
-
}
|
|
107
|
-
index = 0;
|
|
108
|
-
fail(message) {
|
|
109
|
-
const from = Math.max(0, this.index - 24);
|
|
110
|
-
const snippet = this.src.slice(from, this.index + 24).replace(/\s+/g, " ").trim();
|
|
111
|
-
return new WorkflowMetaSyntaxError(
|
|
112
|
-
`${message} at position ${this.index} (near "${snippet}"). meta must be a plain object literal \u2014 strings, numbers, booleans, null, arrays and objects only, with no variables, function calls, spreads, accessors or template substitutions.`,
|
|
113
|
-
this.index
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
skipTrivia() {
|
|
117
|
-
for (; ; ) {
|
|
118
|
-
while (this.index < this.src.length && WHITESPACE.has(this.src[this.index])) {
|
|
119
|
-
this.index++;
|
|
120
|
-
}
|
|
121
|
-
if (this.src.startsWith("//", this.index)) {
|
|
122
|
-
let end = this.index + 2;
|
|
123
|
-
while (end < this.src.length && !"\n\r\u2028\u2029".includes(this.src[end])) {
|
|
124
|
-
end++;
|
|
125
|
-
}
|
|
126
|
-
if (this.src[end] === "\r" && this.src[end + 1] === "\n") end++;
|
|
127
|
-
this.index = end < this.src.length ? end + 1 : this.src.length;
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
if (this.src.startsWith("/*", this.index)) {
|
|
131
|
-
const end = this.src.indexOf("*/", this.index + 2);
|
|
132
|
-
if (end === -1) throw this.fail("unterminated comment");
|
|
133
|
-
this.index = end + 2;
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
parseValue(depth) {
|
|
140
|
-
if (depth > MAX_DEPTH) throw this.fail("meta literal is nested too deeply");
|
|
141
|
-
this.skipTrivia();
|
|
142
|
-
const c = this.src[this.index];
|
|
143
|
-
if (c === void 0) throw this.fail("unexpected end of meta literal");
|
|
144
|
-
if (c === "{") return this.parseObject(depth);
|
|
145
|
-
if (c === "[") return this.parseArray(depth);
|
|
146
|
-
if (c === '"' || c === "'" || c === "`") return this.parseString();
|
|
147
|
-
if (c === "-" || c >= "0" && c <= "9") return this.parseNumber();
|
|
148
|
-
if (this.src.startsWith("true", this.index)) return this.word("true", true);
|
|
149
|
-
if (this.src.startsWith("false", this.index)) {
|
|
150
|
-
return this.word("false", false);
|
|
151
|
-
}
|
|
152
|
-
if (this.src.startsWith("null", this.index)) return this.word("null", null);
|
|
153
|
-
if (c === "/")
|
|
154
|
-
throw this.fail("regular expressions are not allowed in meta");
|
|
155
|
-
throw this.fail("unsupported value");
|
|
156
|
-
}
|
|
157
|
-
word(literal, value) {
|
|
158
|
-
const after = this.src[this.index + literal.length];
|
|
159
|
-
if (after !== void 0 && ID_PART.test(after)) {
|
|
160
|
-
throw this.fail("unsupported value");
|
|
161
|
-
}
|
|
162
|
-
this.index += literal.length;
|
|
163
|
-
return value;
|
|
164
|
-
}
|
|
165
|
-
parseObject(depth) {
|
|
166
|
-
this.index++;
|
|
167
|
-
const out = /* @__PURE__ */ Object.create(null);
|
|
168
|
-
for (; ; ) {
|
|
169
|
-
this.skipTrivia();
|
|
170
|
-
const c = this.src[this.index];
|
|
171
|
-
if (c === "}") {
|
|
172
|
-
this.index++;
|
|
173
|
-
return out;
|
|
174
|
-
}
|
|
175
|
-
if (c === void 0) throw this.fail("unterminated object");
|
|
176
|
-
if (this.src.startsWith("...", this.index)) {
|
|
177
|
-
throw this.fail("spread is not allowed in meta");
|
|
178
|
-
}
|
|
179
|
-
if (c === "[") throw this.fail("computed keys are not allowed in meta");
|
|
180
|
-
const key = this.parseKey();
|
|
181
|
-
this.skipTrivia();
|
|
182
|
-
if (this.src[this.index] === "(") {
|
|
183
|
-
throw this.fail("methods are not allowed in meta");
|
|
184
|
-
}
|
|
185
|
-
if (this.src[this.index] !== ":")
|
|
186
|
-
throw this.fail('expected ":" after key');
|
|
187
|
-
this.index++;
|
|
188
|
-
out[key] = this.parseValue(depth + 1);
|
|
189
|
-
this.skipTrivia();
|
|
190
|
-
if (this.src[this.index] === ",") {
|
|
191
|
-
this.index++;
|
|
192
|
-
continue;
|
|
193
|
-
}
|
|
194
|
-
if (this.src[this.index] === "}") {
|
|
195
|
-
this.index++;
|
|
196
|
-
return out;
|
|
197
|
-
}
|
|
198
|
-
throw this.fail('expected "," or "}"');
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
parseKey() {
|
|
202
|
-
const c = this.src[this.index];
|
|
203
|
-
if (c === '"' || c === "'" || c === "`") return this.parseString();
|
|
204
|
-
if (c !== void 0 && ID_START.test(c)) {
|
|
205
|
-
const start = this.index;
|
|
206
|
-
while (this.index < this.src.length && ID_PART.test(this.src[this.index])) {
|
|
207
|
-
this.index++;
|
|
208
|
-
}
|
|
209
|
-
const word = this.src.slice(start, this.index);
|
|
210
|
-
if (word === "get" || word === "set" || word === "async") {
|
|
211
|
-
const save = this.index;
|
|
212
|
-
this.skipTrivia();
|
|
213
|
-
const next = this.src[this.index];
|
|
214
|
-
const startsPropertyName = next !== void 0 && (ID_START.test(next) || next === '"' || next === "'" || next === "[");
|
|
215
|
-
this.index = save;
|
|
216
|
-
if (startsPropertyName) {
|
|
217
|
-
throw this.fail(
|
|
218
|
-
word === "async" ? "async members are not allowed in meta" : `${word}ters are not allowed in meta`
|
|
219
|
-
);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return word;
|
|
223
|
-
}
|
|
224
|
-
throw this.fail("expected a property name");
|
|
225
|
-
}
|
|
226
|
-
parseArray(depth) {
|
|
227
|
-
this.index++;
|
|
228
|
-
const out = [];
|
|
229
|
-
for (; ; ) {
|
|
230
|
-
this.skipTrivia();
|
|
231
|
-
const c = this.src[this.index];
|
|
232
|
-
if (c === "]") {
|
|
233
|
-
this.index++;
|
|
234
|
-
return out;
|
|
235
|
-
}
|
|
236
|
-
if (c === void 0) throw this.fail("unterminated array");
|
|
237
|
-
if (this.src.startsWith("...", this.index)) {
|
|
238
|
-
throw this.fail("spread is not allowed in meta");
|
|
239
|
-
}
|
|
240
|
-
if (c === ",") throw this.fail("missing array element");
|
|
241
|
-
out.push(this.parseValue(depth + 1));
|
|
242
|
-
this.skipTrivia();
|
|
243
|
-
if (this.src[this.index] === ",") {
|
|
244
|
-
this.index++;
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
if (this.src[this.index] === "]") {
|
|
248
|
-
this.index++;
|
|
249
|
-
return out;
|
|
250
|
-
}
|
|
251
|
-
throw this.fail('expected "," or "]"');
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
parseString() {
|
|
255
|
-
const quote = this.src[this.index++];
|
|
256
|
-
let out = "";
|
|
257
|
-
for (; ; ) {
|
|
258
|
-
const c = this.src[this.index];
|
|
259
|
-
if (c === void 0) throw this.fail("unterminated string");
|
|
260
|
-
if (c === quote) {
|
|
261
|
-
this.index++;
|
|
262
|
-
return out;
|
|
263
|
-
}
|
|
264
|
-
if (c === "\n" || c === "\r") {
|
|
265
|
-
if (quote !== "`") throw this.fail("unterminated string");
|
|
266
|
-
if (c === "\r") {
|
|
267
|
-
this.index++;
|
|
268
|
-
if (this.src[this.index] === "\n") this.index++;
|
|
269
|
-
out += "\n";
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
if (quote === "`" && c === "$" && this.src[this.index + 1] === "{") {
|
|
274
|
-
throw this.fail("template substitutions are not allowed in meta");
|
|
275
|
-
}
|
|
276
|
-
if (c === "\\") {
|
|
277
|
-
out += this.parseEscape();
|
|
278
|
-
continue;
|
|
279
|
-
}
|
|
280
|
-
out += c;
|
|
281
|
-
this.index++;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
parseEscape() {
|
|
285
|
-
this.index++;
|
|
286
|
-
const c = this.src[this.index++];
|
|
287
|
-
switch (c) {
|
|
288
|
-
case "n":
|
|
289
|
-
return "\n";
|
|
290
|
-
case "t":
|
|
291
|
-
return " ";
|
|
292
|
-
case "r":
|
|
293
|
-
return "\r";
|
|
294
|
-
case "b":
|
|
295
|
-
return "\b";
|
|
296
|
-
case "f":
|
|
297
|
-
return "\f";
|
|
298
|
-
case "v":
|
|
299
|
-
return "\v";
|
|
300
|
-
case "0":
|
|
301
|
-
if (/[0-9]/.test(this.src[this.index] ?? "")) {
|
|
302
|
-
throw this.fail("octal escapes are not allowed in meta");
|
|
303
|
-
}
|
|
304
|
-
return "\0";
|
|
305
|
-
case "x": {
|
|
306
|
-
const hex = this.src.slice(this.index, this.index + 2);
|
|
307
|
-
if (!/^[0-9a-fA-F]{2}$/.test(hex)) {
|
|
308
|
-
throw this.fail("invalid \\x escape");
|
|
309
|
-
}
|
|
310
|
-
this.index += 2;
|
|
311
|
-
return String.fromCharCode(parseInt(hex, 16));
|
|
312
|
-
}
|
|
313
|
-
case "u": {
|
|
314
|
-
if (this.src[this.index] === "{") {
|
|
315
|
-
const end = this.src.indexOf("}", this.index);
|
|
316
|
-
const hex2 = end === -1 ? "" : this.src.slice(this.index + 1, end);
|
|
317
|
-
if (!/^[0-9a-fA-F]{1,6}$/.test(hex2)) {
|
|
318
|
-
throw this.fail("invalid \\u{...} escape");
|
|
319
|
-
}
|
|
320
|
-
const code = parseInt(hex2, 16);
|
|
321
|
-
if (code > 1114111) throw this.fail("invalid \\u{...} escape");
|
|
322
|
-
this.index = end + 1;
|
|
323
|
-
return String.fromCodePoint(code);
|
|
324
|
-
}
|
|
325
|
-
const hex = this.src.slice(this.index, this.index + 4);
|
|
326
|
-
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
327
|
-
throw this.fail("invalid \\u escape");
|
|
328
|
-
}
|
|
329
|
-
this.index += 4;
|
|
330
|
-
return String.fromCharCode(parseInt(hex, 16));
|
|
331
|
-
}
|
|
332
|
-
case "\n":
|
|
333
|
-
return "";
|
|
334
|
-
// line continuation
|
|
335
|
-
case "\r":
|
|
336
|
-
if (this.src[this.index] === "\n") this.index++;
|
|
337
|
-
return "";
|
|
338
|
-
case "\u2028":
|
|
339
|
-
case "\u2029":
|
|
340
|
-
return "";
|
|
341
|
-
case void 0:
|
|
342
|
-
throw this.fail("unterminated escape sequence");
|
|
343
|
-
default:
|
|
344
|
-
return c;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
parseNumber() {
|
|
348
|
-
const start = this.index;
|
|
349
|
-
if (this.src[this.index] === "-") this.index++;
|
|
350
|
-
while (this.index < this.src.length && /[0-9]/.test(this.src[this.index])) {
|
|
351
|
-
this.index++;
|
|
352
|
-
}
|
|
353
|
-
if (this.src[this.index] === ".") {
|
|
354
|
-
this.index++;
|
|
355
|
-
while (this.index < this.src.length && /[0-9]/.test(this.src[this.index])) {
|
|
356
|
-
this.index++;
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
if (this.src[this.index] === "e" || this.src[this.index] === "E") {
|
|
360
|
-
this.index++;
|
|
361
|
-
if (this.src[this.index] === "+" || this.src[this.index] === "-") {
|
|
362
|
-
this.index++;
|
|
363
|
-
}
|
|
364
|
-
while (this.index < this.src.length && /[0-9]/.test(this.src[this.index])) {
|
|
365
|
-
this.index++;
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
const after = this.src[this.index];
|
|
369
|
-
if (after !== void 0 && ID_PART.test(after)) {
|
|
370
|
-
throw this.fail("unsupported numeric literal");
|
|
371
|
-
}
|
|
372
|
-
const text = this.src.slice(start, this.index);
|
|
373
|
-
const value = Number(text);
|
|
374
|
-
if (text.length === 0 || !Number.isFinite(value)) {
|
|
375
|
-
throw this.fail("unsupported numeric literal");
|
|
376
|
-
}
|
|
377
|
-
return value;
|
|
378
|
-
}
|
|
379
|
-
};
|
|
380
|
-
|
|
381
|
-
// packages/core/src/agents/runtime/workflow-sandbox.ts
|
|
382
|
-
function findMetaBlockBounds(source) {
|
|
383
|
-
const re = /^\s*export\s+const\s+meta\s*=\s*\{/;
|
|
384
|
-
const match = re.exec(source);
|
|
385
|
-
if (!match) return null;
|
|
386
|
-
const exportIdx = match.index;
|
|
387
|
-
const startBrace = source.indexOf("{", exportIdx);
|
|
388
|
-
let depth = 1;
|
|
389
|
-
let i = startBrace + 1;
|
|
390
|
-
while (i < source.length && depth > 0) {
|
|
391
|
-
const ch = source[i];
|
|
392
|
-
const next = source[i + 1];
|
|
393
|
-
if (ch === "/" && next === "/") {
|
|
394
|
-
i += 2;
|
|
395
|
-
while (i < source.length && source[i] !== "\n") i++;
|
|
396
|
-
continue;
|
|
397
|
-
}
|
|
398
|
-
if (ch === "/" && next === "*") {
|
|
399
|
-
i += 2;
|
|
400
|
-
while (i < source.length && !(source[i] === "*" && source[i + 1] === "/"))
|
|
401
|
-
i++;
|
|
402
|
-
i += 2;
|
|
403
|
-
continue;
|
|
404
|
-
}
|
|
405
|
-
if (ch === "/" && isRegexContext(source, i)) {
|
|
406
|
-
i++;
|
|
407
|
-
let inClass = false;
|
|
408
|
-
while (i < source.length && (inClass || source[i] !== "/") && source[i] !== "\n") {
|
|
409
|
-
if (source[i] === "\\") i += 2;
|
|
410
|
-
else if (source[i] === "[") {
|
|
411
|
-
inClass = true;
|
|
412
|
-
i++;
|
|
413
|
-
} else if (source[i] === "]") {
|
|
414
|
-
inClass = false;
|
|
415
|
-
i++;
|
|
416
|
-
} else {
|
|
417
|
-
i++;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
i++;
|
|
421
|
-
while (i < source.length && /[gimsuy]/.test(source[i])) i++;
|
|
422
|
-
continue;
|
|
423
|
-
}
|
|
424
|
-
if (ch === '"' || ch === "'" || ch === "`") {
|
|
425
|
-
const q = ch;
|
|
426
|
-
i++;
|
|
427
|
-
while (i < source.length && source[i] !== q) {
|
|
428
|
-
if (source[i] === "\\") i++;
|
|
429
|
-
i++;
|
|
430
|
-
}
|
|
431
|
-
i++;
|
|
432
|
-
continue;
|
|
433
|
-
}
|
|
434
|
-
if (ch === "{") depth++;
|
|
435
|
-
else if (ch === "}") depth--;
|
|
436
|
-
i++;
|
|
437
|
-
}
|
|
438
|
-
if (depth !== 0) {
|
|
439
|
-
throw new Error(
|
|
440
|
-
"unbalanced braces in export const meta declaration \u2014 the workflow script cannot be safely stripped. Check the meta block syntax."
|
|
441
|
-
);
|
|
442
|
-
}
|
|
443
|
-
const endBraceIncl = i - 1;
|
|
444
|
-
while (i < source.length && /[\s;]/.test(source[i])) i++;
|
|
445
|
-
return { exportIdx, startBrace, endBraceIncl, afterMeta: i };
|
|
446
|
-
}
|
|
447
|
-
__name(findMetaBlockBounds, "findMetaBlockBounds");
|
|
448
|
-
function extractAndStripMeta(source) {
|
|
449
|
-
const bounds = findMetaBlockBounds(source);
|
|
450
|
-
if (!bounds) return { stripped: source, meta: null };
|
|
451
|
-
const metaSource = source.slice(bounds.startBrace, bounds.endBraceIncl + 1);
|
|
452
|
-
const stripped = source.slice(0, bounds.exportIdx) + source.slice(bounds.afterMeta);
|
|
453
|
-
let raw;
|
|
454
|
-
try {
|
|
455
|
-
raw = parseWorkflowMetaLiteral(metaSource);
|
|
456
|
-
} catch (e) {
|
|
457
|
-
const msg = e instanceof Error ? e.message : String(e);
|
|
458
|
-
throw new Error(`invalid meta object literal: ${msg}`);
|
|
459
|
-
}
|
|
460
|
-
const meta = validateMeta(raw);
|
|
461
|
-
return { stripped, meta };
|
|
462
|
-
}
|
|
463
|
-
__name(extractAndStripMeta, "extractAndStripMeta");
|
|
464
|
-
var WORKFLOW_SCRIPT_FILENAME = "workflow.js";
|
|
465
|
-
function wrapWorkflowBody(strippedSource) {
|
|
466
|
-
return `(async () => {'use strict';
|
|
467
|
-
${strippedSource}
|
|
468
|
-
})()`;
|
|
469
|
-
}
|
|
470
|
-
__name(wrapWorkflowBody, "wrapWorkflowBody");
|
|
471
|
-
function compileWorkflowScript(scriptSource) {
|
|
472
|
-
const { stripped, meta } = extractAndStripMeta(scriptSource);
|
|
473
|
-
const bounds = findMetaBlockBounds(scriptSource);
|
|
474
|
-
const compilable = bounds ? scriptSource.slice(0, bounds.exportIdx) + scriptSource.slice(bounds.exportIdx, bounds.afterMeta).replace(/[^\r\n\u2028\u2029]/g, " ") + scriptSource.slice(bounds.afterMeta) : stripped;
|
|
475
|
-
const script = new vm.Script(wrapWorkflowBody(compilable), {
|
|
476
|
-
filename: WORKFLOW_SCRIPT_FILENAME
|
|
477
|
-
});
|
|
478
|
-
return { script, meta };
|
|
479
|
-
}
|
|
480
|
-
__name(compileWorkflowScript, "compileWorkflowScript");
|
|
481
|
-
function workflowCancelledError() {
|
|
482
|
-
const error = new Error("Workflow run was aborted (cancelled).");
|
|
483
|
-
error.name = "AbortError";
|
|
484
|
-
return error;
|
|
485
|
-
}
|
|
486
|
-
__name(workflowCancelledError, "workflowCancelledError");
|
|
487
|
-
var COMPILE_ERROR_LINE_WIDTH = 80;
|
|
488
|
-
function describeWorkflowCompileError(error, authorLineCount) {
|
|
489
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
490
|
-
const stack = typeof err.stack === "string" ? err.stack : "";
|
|
491
|
-
const frame = [];
|
|
492
|
-
for (const line of stack.split("\n")) {
|
|
493
|
-
if (frame.length >= 3 && /^\s+at\s/.test(line)) break;
|
|
494
|
-
frame.push(line);
|
|
495
|
-
}
|
|
496
|
-
const header = frame[0] ?? "";
|
|
497
|
-
const match = /^.*?:(\d+)$/.exec(header);
|
|
498
|
-
if (frame.length < 3 || !match) {
|
|
499
|
-
return err.message;
|
|
500
|
-
}
|
|
501
|
-
const bodyLine = Number(match[1]) - 1;
|
|
502
|
-
if (bodyLine < 1) return err.message;
|
|
503
|
-
if (bodyLine > authorLineCount) {
|
|
504
|
-
return "The script ends with unmatched or incomplete syntax. Check parentheses, brackets, braces, quotes, and template literals near the end.";
|
|
505
|
-
}
|
|
506
|
-
const rendered = clampSourceFrame(frame[1] ?? "", frame[2] ?? "");
|
|
507
|
-
const tail = frame.slice(3).filter((l) => l.trim().length > 0).join("\n");
|
|
508
|
-
return [`line ${bodyLine}`, rendered, tail].filter(Boolean).join("\n");
|
|
509
|
-
}
|
|
510
|
-
__name(describeWorkflowCompileError, "describeWorkflowCompileError");
|
|
511
|
-
function clampSourceFrame(sourceLine, caretLine) {
|
|
512
|
-
if (sourceLine.length <= COMPILE_ERROR_LINE_WIDTH) {
|
|
513
|
-
return `${sourceLine}
|
|
514
|
-
${caretLine}`;
|
|
515
|
-
}
|
|
516
|
-
const caret = /\^+/.exec(caretLine);
|
|
517
|
-
if (!caret) return "";
|
|
518
|
-
const caretCol = caret.index;
|
|
519
|
-
const start = Math.max(
|
|
520
|
-
0,
|
|
521
|
-
Math.min(
|
|
522
|
-
caretCol - Math.floor(COMPILE_ERROR_LINE_WIDTH / 2),
|
|
523
|
-
sourceLine.length - COMPILE_ERROR_LINE_WIDTH
|
|
524
|
-
)
|
|
525
|
-
);
|
|
526
|
-
const prefix = start > 0 ? "\u2026" : "";
|
|
527
|
-
const window = sourceLine.slice(start, start + COMPILE_ERROR_LINE_WIDTH);
|
|
528
|
-
const suffix = start + COMPILE_ERROR_LINE_WIDTH < sourceLine.length ? "\u2026" : "";
|
|
529
|
-
const visibleCaretStart = Math.max(caretCol, start);
|
|
530
|
-
const visibleCaretEnd = Math.min(
|
|
531
|
-
caretCol + caret[0].length,
|
|
532
|
-
start + COMPILE_ERROR_LINE_WIDTH
|
|
533
|
-
);
|
|
534
|
-
const shiftedCaret = " ".repeat(prefix.length + visibleCaretStart - start) + "^".repeat(visibleCaretEnd - visibleCaretStart);
|
|
535
|
-
return `${prefix}${window}${suffix}
|
|
536
|
-
${shiftedCaret}`;
|
|
537
|
-
}
|
|
538
|
-
__name(clampSourceFrame, "clampSourceFrame");
|
|
539
|
-
function validateMeta(value) {
|
|
540
|
-
if (value === null || typeof value !== "object") {
|
|
541
|
-
throw new Error("meta must be an object");
|
|
542
|
-
}
|
|
543
|
-
const obj = value;
|
|
544
|
-
if (typeof obj["name"] !== "string" || obj["name"].length === 0) {
|
|
545
|
-
throw new Error("meta.name must be a non-empty string");
|
|
546
|
-
}
|
|
547
|
-
if (typeof obj["description"] !== "string" || obj["description"].length === 0) {
|
|
548
|
-
throw new Error("meta.description must be a non-empty string");
|
|
549
|
-
}
|
|
550
|
-
if (obj["whenToUse"] !== void 0 && typeof obj["whenToUse"] !== "string") {
|
|
551
|
-
throw new Error("meta.whenToUse must be a string");
|
|
552
|
-
}
|
|
553
|
-
let phases;
|
|
554
|
-
if (obj["phases"] !== void 0) {
|
|
555
|
-
if (!Array.isArray(obj["phases"])) {
|
|
556
|
-
throw new Error("meta.phases must be an array");
|
|
557
|
-
}
|
|
558
|
-
phases = [];
|
|
559
|
-
for (const p of obj["phases"]) {
|
|
560
|
-
if (p === null || typeof p !== "object") {
|
|
561
|
-
throw new Error("meta.phases entries must be objects");
|
|
562
|
-
}
|
|
563
|
-
const ph = p;
|
|
564
|
-
if (typeof ph["title"] !== "string" || ph["title"].length === 0) {
|
|
565
|
-
throw new Error("meta.phases[].title must be a non-empty string");
|
|
566
|
-
}
|
|
567
|
-
const phase = {
|
|
568
|
-
title: ph["title"]
|
|
569
|
-
};
|
|
570
|
-
if (ph["detail"] !== void 0) {
|
|
571
|
-
if (typeof ph["detail"] !== "string") {
|
|
572
|
-
throw new Error("meta.phases[].detail must be a string");
|
|
573
|
-
}
|
|
574
|
-
phase.detail = ph["detail"];
|
|
575
|
-
}
|
|
576
|
-
if (ph["model"] !== void 0) {
|
|
577
|
-
if (typeof ph["model"] !== "string") {
|
|
578
|
-
throw new Error("meta.phases[].model must be a string");
|
|
579
|
-
}
|
|
580
|
-
phase.model = ph["model"];
|
|
581
|
-
}
|
|
582
|
-
phases.push(phase);
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
const out = {
|
|
586
|
-
name: obj["name"],
|
|
587
|
-
description: obj["description"]
|
|
588
|
-
};
|
|
589
|
-
if (obj["whenToUse"] !== void 0) {
|
|
590
|
-
out.whenToUse = obj["whenToUse"];
|
|
591
|
-
}
|
|
592
|
-
if (phases !== void 0) {
|
|
593
|
-
out.phases = phases;
|
|
594
|
-
}
|
|
595
|
-
return out;
|
|
596
|
-
}
|
|
597
|
-
__name(validateMeta, "validateMeta");
|
|
598
|
-
function isRegexContext(source, i) {
|
|
599
|
-
let j = i - 1;
|
|
600
|
-
while (j >= 0 && /\s/.test(source[j])) j--;
|
|
601
|
-
if (j < 0) return true;
|
|
602
|
-
const prev = source[j];
|
|
603
|
-
return /[{[(,;:=!&|?+\-*/%^~<>]/.test(prev);
|
|
604
|
-
}
|
|
605
|
-
__name(isRegexContext, "isRegexContext");
|
|
606
|
-
var debugLogger = createDebugLogger("WORKFLOW");
|
|
607
|
-
var MAX_LOG_LINES = 1e4;
|
|
608
|
-
var MAX_PHASE_ENTRIES = 1e4;
|
|
609
|
-
var ARGS_MAX_DEPTH = 64;
|
|
610
|
-
var DEFAULT_MAX_WALL_CLOCK_MS = 30 * 60 * 1e3;
|
|
611
|
-
var WORKFLOW_SYNC_EVALUATION_TIMEOUT_MS = 3e4;
|
|
612
|
-
function resolveMaxWallClockMs(opts) {
|
|
613
|
-
if (typeof opts.maxWallClockMs === "number" && opts.maxWallClockMs > 0) {
|
|
614
|
-
return opts.maxWallClockMs;
|
|
615
|
-
}
|
|
616
|
-
const envSec = Number(process.env["QWEN_CODE_MAX_WORKFLOW_SECONDS"]);
|
|
617
|
-
if (Number.isFinite(envSec) && envSec > 0) return envSec * 1e3;
|
|
618
|
-
return DEFAULT_MAX_WALL_CLOCK_MS;
|
|
619
|
-
}
|
|
620
|
-
__name(resolveMaxWallClockMs, "resolveMaxWallClockMs");
|
|
621
|
-
var WallClockWatchdog = class {
|
|
622
|
-
constructor(budgetMs, onFire) {
|
|
623
|
-
this.onFire = onFire;
|
|
624
|
-
this.remainingMs = budgetMs;
|
|
625
|
-
this.arm();
|
|
626
|
-
}
|
|
627
|
-
static {
|
|
628
|
-
__name(this, "WallClockWatchdog");
|
|
629
|
-
}
|
|
630
|
-
timer;
|
|
631
|
-
remainingMs;
|
|
632
|
-
armedAt = 0;
|
|
633
|
-
paused = false;
|
|
634
|
-
stopped = false;
|
|
635
|
-
pause() {
|
|
636
|
-
if (this.stopped || this.paused || this.timer === void 0) return;
|
|
637
|
-
this.paused = true;
|
|
638
|
-
clearTimeout(this.timer);
|
|
639
|
-
this.timer = void 0;
|
|
640
|
-
this.remainingMs = Math.max(
|
|
641
|
-
0,
|
|
642
|
-
this.remainingMs - (performance.now() - this.armedAt)
|
|
643
|
-
);
|
|
644
|
-
}
|
|
645
|
-
resume() {
|
|
646
|
-
if (this.stopped || !this.paused) return;
|
|
647
|
-
this.paused = false;
|
|
648
|
-
this.arm();
|
|
649
|
-
}
|
|
650
|
-
stop() {
|
|
651
|
-
this.stopped = true;
|
|
652
|
-
if (this.timer !== void 0) clearTimeout(this.timer);
|
|
653
|
-
this.timer = void 0;
|
|
654
|
-
}
|
|
655
|
-
arm() {
|
|
656
|
-
this.armedAt = performance.now();
|
|
657
|
-
this.timer = setTimeout(() => {
|
|
658
|
-
this.stopped = true;
|
|
659
|
-
this.timer = void 0;
|
|
660
|
-
this.onFire();
|
|
661
|
-
}, this.remainingMs);
|
|
662
|
-
this.timer.unref?.();
|
|
663
|
-
}
|
|
664
|
-
};
|
|
665
|
-
function validateArgs(val, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
|
|
666
|
-
if (depth > ARGS_MAX_DEPTH) {
|
|
667
|
-
throw new Error(
|
|
668
|
-
`WorkflowSandbox: args exceeded max nesting depth of ${ARGS_MAX_DEPTH}`
|
|
669
|
-
);
|
|
670
|
-
}
|
|
671
|
-
if (val === null) return;
|
|
672
|
-
const t = typeof val;
|
|
673
|
-
if (t === "function") {
|
|
674
|
-
throw new Error(
|
|
675
|
-
"WorkflowSandbox: args must be JSON-serializable (functions are not allowed)."
|
|
676
|
-
);
|
|
677
|
-
}
|
|
678
|
-
if (t === "bigint") {
|
|
679
|
-
throw new Error(
|
|
680
|
-
"WorkflowSandbox: args must be JSON-serializable (BigInt is not allowed \u2014 pass as string)."
|
|
681
|
-
);
|
|
682
|
-
}
|
|
683
|
-
if (t !== "object") return;
|
|
684
|
-
const obj = val;
|
|
685
|
-
if (seen.has(obj)) {
|
|
686
|
-
throw new Error(
|
|
687
|
-
"WorkflowSandbox: args must be JSON-serializable (circular reference detected)."
|
|
688
|
-
);
|
|
689
|
-
}
|
|
690
|
-
seen.add(obj);
|
|
691
|
-
if (Array.isArray(val)) {
|
|
692
|
-
for (const item of val) validateArgs(item, depth + 1, seen);
|
|
693
|
-
} else {
|
|
694
|
-
for (const v of Object.values(val)) {
|
|
695
|
-
validateArgs(v, depth + 1, seen);
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
__name(validateArgs, "validateArgs");
|
|
700
|
-
function createWorkflowSandbox(opts) {
|
|
701
|
-
const phases = [];
|
|
702
|
-
const logs = [];
|
|
703
|
-
const emitLog = /* @__PURE__ */ __name((line) => {
|
|
704
|
-
try {
|
|
705
|
-
opts.emitter?.logAppended?.(line);
|
|
706
|
-
} catch (e) {
|
|
707
|
-
debugLogger.warn("emitter.logAppended threw:", e);
|
|
708
|
-
}
|
|
709
|
-
}, "emitLog");
|
|
710
|
-
const safeLog = /* @__PURE__ */ __name((msg, notify = true) => {
|
|
711
|
-
if (logs.length < MAX_LOG_LINES) {
|
|
712
|
-
const line = String(msg);
|
|
713
|
-
logs.push(line);
|
|
714
|
-
if (notify) emitLog(line);
|
|
715
|
-
} else if (logs.length === MAX_LOG_LINES) {
|
|
716
|
-
const line = `[workflow log truncated at ${MAX_LOG_LINES} lines]`;
|
|
717
|
-
logs.push(line);
|
|
718
|
-
if (notify) emitLog(line);
|
|
719
|
-
}
|
|
720
|
-
}, "safeLog");
|
|
721
|
-
const safePhase = /* @__PURE__ */ __name((title) => {
|
|
722
|
-
if (phases.length < MAX_PHASE_ENTRIES) {
|
|
723
|
-
const t = stripAnsiAndControl(String(title)).slice(0, 200) || "phase";
|
|
724
|
-
if (phases[phases.length - 1] === t) return;
|
|
725
|
-
phases.push(t);
|
|
726
|
-
try {
|
|
727
|
-
opts.emitter?.phaseStarted?.(t);
|
|
728
|
-
} catch (e) {
|
|
729
|
-
debugLogger.warn("emitter.phaseStarted threw:", e);
|
|
730
|
-
}
|
|
731
|
-
} else if (phases.length === MAX_PHASE_ENTRIES) {
|
|
732
|
-
phases.push(
|
|
733
|
-
`[workflow phases truncated at ${MAX_PHASE_ENTRIES} entries]`
|
|
734
|
-
);
|
|
735
|
-
}
|
|
736
|
-
}, "safePhase");
|
|
737
|
-
if (opts.args !== void 0) validateArgs(opts.args);
|
|
738
|
-
const argsJson = opts.args === void 0 ? null : JSON.stringify(opts.args);
|
|
739
|
-
const unconsumedRoots = /* @__PURE__ */ new Map();
|
|
740
|
-
const unconsumedRejections = /* @__PURE__ */ new Map();
|
|
741
|
-
let nextUnconsumedId = 1;
|
|
742
|
-
let unconsumedSettled = false;
|
|
743
|
-
let mirroredEscapeKeys = /* @__PURE__ */ new Set();
|
|
744
|
-
const unconsumedRejectionLine = /* @__PURE__ */ __name((rec) => {
|
|
745
|
-
if (rec.dispatchFailed) {
|
|
746
|
-
return rec.isRoot ? "dispatch failed (result not consumed): " + rec.msg : "dispatch failed (rejection not handled): " + rec.msg;
|
|
747
|
-
}
|
|
748
|
-
return "script handler failed (rejection not handled): " + rec.msg;
|
|
749
|
-
}, "unconsumedRejectionLine");
|
|
750
|
-
const bridge = {
|
|
751
|
-
argsJson,
|
|
752
|
-
pushPhase: safePhase,
|
|
753
|
-
pushLog: safeLog,
|
|
754
|
-
lastPhase: /* @__PURE__ */ __name(() => phases[phases.length - 1], "lastPhase"),
|
|
755
|
-
hostAgent: opts.dispatch,
|
|
756
|
-
// Effort tiers are resolved host-side so the sandbox accepts exactly the
|
|
757
|
-
// aliases `/effort` does without a second copy of the alias table. Takes
|
|
758
|
-
// and returns primitives only.
|
|
759
|
-
normalizeEffort: /* @__PURE__ */ __name((raw) => typeof raw === "string" ? normalizeReasoningEffort(raw) ?? null : null, "normalizeEffort"),
|
|
760
|
-
effortTiers: REASONING_EFFORT_TIERS.join(", "),
|
|
761
|
-
// A built-in tool named by its display name or a legacy alias becomes its
|
|
762
|
-
// tool name, so both spellings share one resume key; any other entry comes
|
|
763
|
-
// back as given for the host to judge. Primitives only.
|
|
764
|
-
canonicalDenyName: /* @__PURE__ */ __name((raw) => typeof raw === "string" ? resolveBuiltinToolName(raw) ?? raw : null, "canonicalDenyName"),
|
|
765
|
-
// JSON.stringify escapes only C0: strip DEL / C1 (incl. NEL) from a
|
|
766
|
-
// script-controlled echo so it cannot fragment a rejection message.
|
|
767
|
-
sanitizeForMessage: /* @__PURE__ */ __name((raw) => typeof raw === "string" ? stripAnsiAndControl(raw) : "", "sanitizeForMessage"),
|
|
768
|
-
// PR #4947 R2 T7 (qwen-code-ci-bot): host-side log hook for reviveInRealm's
|
|
769
|
-
// catch path. Mirrors the rejection-logging in settleToNullArray so an
|
|
770
|
-
// operator running with debug logging can distinguish "thunk rejected"
|
|
771
|
-
// (settleToNullArray.warn) from "thunk resolved to a non-JSON-serializable
|
|
772
|
-
// value" (this warn). Receives only primitive strings/numbers — the bridge
|
|
773
|
-
// contract forbids host objects crossing back to the script.
|
|
774
|
-
logRevivalFailure: /* @__PURE__ */ __name((idx, reason) => {
|
|
775
|
-
debugLogger.warn(
|
|
776
|
-
`Workflow result revival failed at index ${idx}: ${reason}; slot set to null (non-JSON-serializable thunk return).`
|
|
777
|
-
);
|
|
778
|
-
}, "logRevivalFailure"),
|
|
779
|
-
// --- Unconsumed-rejection mirror bookkeeping ---
|
|
780
|
-
wfRegisterRoot: /* @__PURE__ */ __name(() => {
|
|
781
|
-
const id = nextUnconsumedId++;
|
|
782
|
-
unconsumedRoots.set(id, { rejectionHandled: false, adoptedOut: false });
|
|
783
|
-
return id;
|
|
784
|
-
}, "wfRegisterRoot"),
|
|
785
|
-
wfMarkRejectionHandled: /* @__PURE__ */ __name((rootId) => {
|
|
786
|
-
const root = unconsumedRoots.get(rootId);
|
|
787
|
-
if (root) root.rejectionHandled = true;
|
|
788
|
-
}, "wfMarkRejectionHandled"),
|
|
789
|
-
wfIsRootHandled: /* @__PURE__ */ __name((rootId) => unconsumedRoots.get(rootId)?.rejectionHandled === true, "wfIsRootHandled"),
|
|
790
|
-
wfMarkAdopted: /* @__PURE__ */ __name((rootId) => {
|
|
791
|
-
const root = unconsumedRoots.get(rootId);
|
|
792
|
-
if (root) root.adoptedOut = true;
|
|
793
|
-
}, "wfMarkAdopted"),
|
|
794
|
-
runId: opts.runId ?? "",
|
|
795
|
-
wfReportUnconsumed: /* @__PURE__ */ __name((rootId, isRoot, dispatchFailed, msg) => {
|
|
796
|
-
if (unconsumedSettled) {
|
|
797
|
-
const rec = {
|
|
798
|
-
rootId,
|
|
799
|
-
isRoot,
|
|
800
|
-
dispatchFailed,
|
|
801
|
-
msg
|
|
802
|
-
};
|
|
803
|
-
const rootState = unconsumedRoots.get(rootId);
|
|
804
|
-
if (!rootState?.rejectionHandled && !(dispatchFailed && rootState?.adoptedOut)) {
|
|
805
|
-
safeLog(unconsumedRejectionLine(rec));
|
|
806
|
-
}
|
|
807
|
-
return 0;
|
|
808
|
-
}
|
|
809
|
-
const id = nextUnconsumedId++;
|
|
810
|
-
unconsumedRejections.set(id, { rootId, isRoot, dispatchFailed, msg });
|
|
811
|
-
return id;
|
|
812
|
-
}, "wfReportUnconsumed"),
|
|
813
|
-
wfClearUnconsumed: /* @__PURE__ */ __name((id) => {
|
|
814
|
-
unconsumedRejections.delete(id);
|
|
815
|
-
}, "wfClearUnconsumed"),
|
|
816
|
-
// R10-1: teardown discrimination cannot key on the error name alone.
|
|
817
|
-
// The dominant in-flight cancellation path (controller.abort() →
|
|
818
|
-
// subagent returns terminateMode=CANCELLED → runSingleDispatch throws
|
|
819
|
-
// a PLAIN Error) never produces an 'AbortError', so the mirror would
|
|
820
|
-
// log a spurious dispatch failure for a correctly-cancelled run. Once
|
|
821
|
-
// the run's abort signal has fired, the run is already settling as
|
|
822
|
-
// cancelled / timed-out — every rejection still crossing the boundary
|
|
823
|
-
// is teardown noise regardless of its shape.
|
|
824
|
-
isRunAborted: /* @__PURE__ */ __name(() => opts.abortOnTimeout?.signal.aborted === true, "isRunAborted"),
|
|
825
|
-
// The truthy flags distinguish "injected" from "default stub" inside the
|
|
826
|
-
// init script without leaking the host function itself when not used.
|
|
827
|
-
hasParallel: !!opts.parallel,
|
|
828
|
-
hasPipeline: !!opts.pipeline,
|
|
829
|
-
hasWorkflow: !!opts.workflow,
|
|
830
|
-
hasBudget: !!opts.budget,
|
|
831
|
-
hostParallel: opts.parallel,
|
|
832
|
-
hostPipeline: opts.pipeline,
|
|
833
|
-
hostWorkflow: opts.workflow,
|
|
834
|
-
budgetTotal: opts.budget ? opts.budget.total : null,
|
|
835
|
-
hostBudgetSpent: opts.budget ? opts.budget.spent.bind(opts.budget) : null,
|
|
836
|
-
hostBudgetRemaining: opts.budget ? opts.budget.remaining.bind(opts.budget) : null
|
|
837
|
-
};
|
|
838
|
-
Object.setPrototypeOf(bridge, null);
|
|
839
|
-
const sandboxGlobals = Object.assign(
|
|
840
|
-
/* @__PURE__ */ Object.create(null),
|
|
841
|
-
{ __workflowBridge: bridge }
|
|
842
|
-
);
|
|
843
|
-
const ctx = vm.createContext(sandboxGlobals);
|
|
844
|
-
vm.runInContext(
|
|
845
|
-
`(() => {
|
|
846
|
-
const __b = globalThis.__workflowBridge;
|
|
847
|
-
delete globalThis.__workflowBridge;
|
|
848
|
-
|
|
849
|
-
// --- Math (vm-realm, random throws) ---
|
|
850
|
-
const realMath = Math;
|
|
851
|
-
const safeMath = Object.create(null);
|
|
852
|
-
for (const k of Object.getOwnPropertyNames(realMath)) {
|
|
853
|
-
if (k === 'random' || k === 'constructor') continue;
|
|
854
|
-
safeMath[k] = realMath[k];
|
|
855
|
-
}
|
|
856
|
-
safeMath.random = () => {
|
|
857
|
-
throw new Error(
|
|
858
|
-
'Math.random() is unavailable in workflow scripts (breaks resume). ' +
|
|
859
|
-
'For N independent samples, include the index in the agent label or prompt.'
|
|
860
|
-
);
|
|
861
|
-
};
|
|
862
|
-
globalThis.Math = safeMath;
|
|
863
|
-
|
|
864
|
-
// --- Date (vm-realm function that throws on any access) ---
|
|
865
|
-
const dateMsg = 'Date.now() / new Date() are unavailable in workflow ' +
|
|
866
|
-
'scripts (breaks resume). Stamp results after the workflow returns, ' +
|
|
867
|
-
'or pass timestamps via args.';
|
|
868
|
-
const safeDate = function Date() { throw new Error(dateMsg); };
|
|
869
|
-
safeDate.now = () => { throw new Error(dateMsg); };
|
|
870
|
-
safeDate.UTC = () => { throw new Error(dateMsg); };
|
|
871
|
-
safeDate.parse = () => { throw new Error(dateMsg); };
|
|
872
|
-
Object.setPrototypeOf(safeDate, null);
|
|
873
|
-
Object.defineProperty(safeDate, 'constructor', {
|
|
874
|
-
value: undefined, writable: false, configurable: false,
|
|
875
|
-
});
|
|
876
|
-
globalThis.Date = safeDate;
|
|
877
|
-
|
|
878
|
-
// --- args (parsed via vm-realm JSON \u2192 vm-realm objects/arrays) ---
|
|
879
|
-
// FIX-Round1-T2: vm-realm arrays keep their vm-realm Array.prototype,
|
|
880
|
-
// so for...of, .map, .forEach, spread, destructuring all work \u2014 and
|
|
881
|
-
// their inherited methods' constructors are vm-realm Function, which
|
|
882
|
-
// cannot reach host process.
|
|
883
|
-
globalThis.args = __b.argsJson === null ? undefined : JSON.parse(__b.argsJson);
|
|
884
|
-
|
|
885
|
-
// --- Wrap a host async function so it returns a vm-realm Promise ---
|
|
886
|
-
// FIX-Round1-T1/T8/T14: success and failure both cross the boundary
|
|
887
|
-
// as vm-realm values: resolve with the host's value (a primitive
|
|
888
|
-
// string for dispatch; vm-realm arrays for parallel/pipeline because
|
|
889
|
-
// those wrappers will produce vm-realm results); reject with a
|
|
890
|
-
// freshly-constructed vm-realm Error so e.constructor.constructor
|
|
891
|
-
// stays in the vm realm.
|
|
892
|
-
// Dispatch promises are ObservedPromise instances (a vm-realm
|
|
893
|
-
// Promise subclass). Its then override marks the result consumed
|
|
894
|
-
// and re-attaches the teardown observer to the derived promise, so
|
|
895
|
-
// script-derived chains (agent(...).then(...), await, and the
|
|
896
|
-
// ELEMENTS of static-combinator aggregates \u2014 everything that
|
|
897
|
-
// funnels through then) stay observed at every depth. The
|
|
898
|
-
// AGGREGATES of Promise.all/race/any are built by the native
|
|
899
|
-
// statics and never pass through the observed then, so those
|
|
900
|
-
// statics are wrapped explicitly below (observeAggregate).
|
|
901
|
-
// Without this, a correctly-cancelled run holding a pending derived
|
|
902
|
-
// chain fired a process-level unhandledRejection even though the
|
|
903
|
-
// bare dispatch promise was observed.
|
|
904
|
-
//
|
|
905
|
-
// Unconsumed rejections of an observed node are recorded through
|
|
906
|
-
// the bridge and mirrored into the run log at run settlement (a
|
|
907
|
-
// later consumption clears the record first) \u2014 a dispatch refused
|
|
908
|
-
// at the entry gate (budget / agent cap) or failing mid-run reaches
|
|
909
|
-
// no other surface, so without the mirror the failure leaves no
|
|
910
|
-
// log, alarm, or telemetry.
|
|
911
|
-
function mapDispatchError(hostErr) {
|
|
912
|
-
var msg;
|
|
913
|
-
try {
|
|
914
|
-
msg = (hostErr && hostErr.message != null)
|
|
915
|
-
? String(hostErr.message)
|
|
916
|
-
: String(hostErr);
|
|
917
|
-
} catch (e) {
|
|
918
|
-
msg = '[unserializable rejection value]';
|
|
919
|
-
}
|
|
920
|
-
const vmErr = new Error(msg);
|
|
921
|
-
// Teardown discrimination at the host boundary \u2014 before the error
|
|
922
|
-
// is flattened into a vm-realm Error. Two shapes count as
|
|
923
|
-
// teardown: an error NAMED 'AbortError' (the scheduler's
|
|
924
|
-
// abortError() DOMException), and ANY rejection that crosses
|
|
925
|
-
// after the run's abort signal has fired \u2014 the dominant
|
|
926
|
-
// cancellation path rejects with a plain Error ('did not complete
|
|
927
|
-
// (terminate mode: CANCELLED).'), which a name-only match would
|
|
928
|
-
// mirror as a spurious dispatch failure on a correctly-cancelled
|
|
929
|
-
// run. Matching the name here (rather than the message text)
|
|
930
|
-
// still suppresses teardown noise without swallowing genuine
|
|
931
|
-
// failures whose message merely contains 'aborted' (e.g. a
|
|
932
|
-
// network-layer 'connection aborted by peer').
|
|
933
|
-
var isAbort = false;
|
|
934
|
-
try {
|
|
935
|
-
isAbort = !!(hostErr && hostErr.name === 'AbortError');
|
|
936
|
-
} catch (e) {
|
|
937
|
-
isAbort = false;
|
|
938
|
-
}
|
|
939
|
-
if (isAbort || __b.isRunAborted()) vmErr.__wfAbort = true;
|
|
940
|
-
var isRunFailure = false;
|
|
941
|
-
try {
|
|
942
|
-
isRunFailure = !!(hostErr && (
|
|
943
|
-
hostErr.__wfRunFailure === true ||
|
|
944
|
-
hostErr.name === 'WorkflowBudgetExceededError' ||
|
|
945
|
-
hostErr.name === 'WorkflowAgentCapExceededError'
|
|
946
|
-
));
|
|
947
|
-
} catch (e) {
|
|
948
|
-
isRunFailure = false;
|
|
949
|
-
}
|
|
950
|
-
if (isRunFailure) vmErr.__wfRunFailure = true;
|
|
951
|
-
vmErr.__wfDispatchFailed = true;
|
|
952
|
-
return vmErr;
|
|
953
|
-
}
|
|
954
|
-
function readFlag(value, name) {
|
|
955
|
-
try {
|
|
956
|
-
return !!(value && value[name]);
|
|
957
|
-
} catch (e) {
|
|
958
|
-
return false;
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
function observeDispatch(promise) {
|
|
962
|
-
// Direct native-then call: routing through ObservedPromise.then
|
|
963
|
-
// would mark the promise consumed and recurse the observer. The
|
|
964
|
-
// observer body must be exception-safe on ANY rejection value:
|
|
965
|
-
// a script handler can throw an exotic value (a message getter
|
|
966
|
-
// that throws, a Proxy with throwing traps), and an observer
|
|
967
|
-
// killed mid-body would turn the very rejection it watches into
|
|
968
|
-
// a process-level unhandledRejection \u2014 the exact failure class
|
|
969
|
-
// the observer exists to remove.
|
|
970
|
-
Promise.prototype.then.call(promise, undefined, function (err) {
|
|
971
|
-
if (promise.__wfConsumed) return;
|
|
972
|
-
// R11-30: also suppress on the run-level abort state, not just
|
|
973
|
-
// the rejection's own marker \u2014 a wrapped Promise.any aggregate
|
|
974
|
-
// rejects with a natively built AggregateError that carries
|
|
975
|
-
// neither marker, so teardown rejections of a correctly-
|
|
976
|
-
// cancelled run would still be mirrored when it is unconsumed.
|
|
977
|
-
if (readFlag(err, '__wfAbort') || __b.isRunAborted()) return;
|
|
978
|
-
var dispatchFailed = readFlag(err, '__wfDispatchFailed');
|
|
979
|
-
var errors = null;
|
|
980
|
-
try {
|
|
981
|
-
if (err && Array.isArray(err.errors)) errors = err.errors;
|
|
982
|
-
} catch (e) {
|
|
983
|
-
errors = null;
|
|
984
|
-
}
|
|
985
|
-
if (!dispatchFailed && errors) {
|
|
986
|
-
// R11-4: Promise.any rejects with a fresh vm-realm
|
|
987
|
-
// AggregateError carrying no flags; the element errors are
|
|
988
|
-
// reachable on .errors with their markers preserved. Derive
|
|
989
|
-
// the attribution from the causes instead of blaming the
|
|
990
|
-
// script for a dispatch failure.
|
|
991
|
-
for (var i = 0; i < errors.length; i++) {
|
|
992
|
-
if (readFlag(errors[i], '__wfDispatchFailed')) {
|
|
993
|
-
dispatchFailed = true;
|
|
994
|
-
break;
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
// R11-15: cross-root suppression \u2014 aggregate roots track
|
|
999
|
-
// rejectionHandled independently of their elements' roots, so
|
|
1000
|
-
// a forwarded dispatch failure whose originating element root
|
|
1001
|
-
// the script already handled must not mirror from the
|
|
1002
|
-
// aggregate. The element rootId is stamped in vmAsync;
|
|
1003
|
-
// Promise.all/race forward the element's own reason,
|
|
1004
|
-
// Promise.any surfaces them on .errors.
|
|
1005
|
-
try {
|
|
1006
|
-
var sourceIds = [];
|
|
1007
|
-
if (err && err.__wfRootId) {
|
|
1008
|
-
sourceIds.push(err.__wfRootId);
|
|
1009
|
-
} else if (errors) {
|
|
1010
|
-
for (var j = 0; j < errors.length; j++) {
|
|
1011
|
-
if (errors[j] && errors[j].__wfRootId) {
|
|
1012
|
-
sourceIds.push(errors[j].__wfRootId);
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
for (var k = 0; k < sourceIds.length; k++) {
|
|
1017
|
-
if (
|
|
1018
|
-
sourceIds[k] !== promise.__wfRootId &&
|
|
1019
|
-
__b.wfIsRootHandled(sourceIds[k])
|
|
1020
|
-
) {
|
|
1021
|
-
return;
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
} catch (e) {
|
|
1025
|
-
// A throwing exotic value must not kill the observer; fall
|
|
1026
|
-
// through and mirror conservatively.
|
|
1027
|
-
}
|
|
1028
|
-
var msg;
|
|
1029
|
-
try {
|
|
1030
|
-
msg = String(err && err.message != null ? err.message : err);
|
|
1031
|
-
} catch (e) {
|
|
1032
|
-
msg = '[unserializable rejection value]';
|
|
1033
|
-
}
|
|
1034
|
-
promise.__wfUnconsumedId = __b.wfReportUnconsumed(
|
|
1035
|
-
promise.__wfRootId,
|
|
1036
|
-
promise.__wfIsRoot === true,
|
|
1037
|
-
dispatchFailed,
|
|
1038
|
-
msg,
|
|
1039
|
-
);
|
|
1040
|
-
});
|
|
1041
|
-
}
|
|
1042
|
-
// R11-3: await / Promise.resolve / returning a thenable from a
|
|
1043
|
-
// handler adopts an ObservedPromise through this then with the
|
|
1044
|
-
// adopting promise's capability (resolve, reject) pair \u2014 native
|
|
1045
|
-
// functions, indistinguishable from real handlers by arity. They
|
|
1046
|
-
// are detected by the native toString signature. Adoption
|
|
1047
|
-
// CONSUMES the result (a delayed adoption clears a recorded
|
|
1048
|
-
// verdict exactly like a delayed await) but is NOT a rejection
|
|
1049
|
-
// handler: the rejection transfers into the adopting promise \u2014
|
|
1050
|
-
// typically an async wrapper's implicit promise, which is a plain
|
|
1051
|
-
// vm-realm Promise the mirror cannot observe. Marking adoption as
|
|
1052
|
-
// handling would silently disarm the mirror for the forgotten-
|
|
1053
|
-
// await failure class; the run-level escape hook in the host
|
|
1054
|
-
// surfaces those escapes instead. Bound script functions also
|
|
1055
|
-
// stringify as native code; misreading one as adoption only
|
|
1056
|
-
// forgoes the rejectionHandled marking, never native semantics.
|
|
1057
|
-
function isAdoptionAttach(handler) {
|
|
1058
|
-
try {
|
|
1059
|
-
return (
|
|
1060
|
-
typeof handler === 'function' &&
|
|
1061
|
-
Function.prototype.toString
|
|
1062
|
-
.call(handler)
|
|
1063
|
-
.indexOf('[native code]') !== -1
|
|
1064
|
-
);
|
|
1065
|
-
} catch (e) {
|
|
1066
|
-
return false;
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
class ObservedPromise extends Promise {
|
|
1070
|
-
then(onFulfilled, onRejected) {
|
|
1071
|
-
// R11-26: introspect the handler BEFORE any state mutation.
|
|
1072
|
-
// The read is guarded because a script-supplied handler can
|
|
1073
|
-
// be exotic (a revoked Proxy wrapping a function, a throwing
|
|
1074
|
-
// accessor) \u2014 an unguarded read made .then() throw
|
|
1075
|
-
// synchronously, something native then never does, leaving
|
|
1076
|
-
// the promise marked consumed with no handler attached.
|
|
1077
|
-
var rethrows = false;
|
|
1078
|
-
try {
|
|
1079
|
-
rethrows = !!(onRejected && onRejected.__wfRethrows);
|
|
1080
|
-
} catch (e) {
|
|
1081
|
-
rethrows = false;
|
|
1082
|
-
}
|
|
1083
|
-
this.__wfConsumed = true;
|
|
1084
|
-
// An attached rejection handler means the script can surface the
|
|
1085
|
-
// root rejection itself; the mirror stays silent for it. The
|
|
1086
|
-
// finally override below routes through this then with marked
|
|
1087
|
-
// rethrow combinators \u2014 those re-raise the rejection instead of
|
|
1088
|
-
// handling it, so they must NOT set the flag (marking them
|
|
1089
|
-
// handled would silently drop a fire-and-forget
|
|
1090
|
-
// agent(...).finally(...) failure). Adoption attaches count
|
|
1091
|
-
// as consumption only, never handling (see isAdoptionAttach):
|
|
1092
|
-
// they mark adoptedOut so forwarded sibling entries defer to
|
|
1093
|
-
// the adopting chain / escape hook instead of mirroring.
|
|
1094
|
-
if (isAdoptionAttach(onRejected)) {
|
|
1095
|
-
__b.wfMarkAdopted(this.__wfRootId);
|
|
1096
|
-
} else if (typeof onRejected === 'function' && !rethrows) {
|
|
1097
|
-
__b.wfMarkRejectionHandled(this.__wfRootId);
|
|
1098
|
-
}
|
|
1099
|
-
// A delayed consumption clears a verdict recorded at
|
|
1100
|
-
// rejection-settlement time, so a late-but-real await is not
|
|
1101
|
-
// misreported as unconsumed.
|
|
1102
|
-
if (this.__wfUnconsumedId !== undefined) {
|
|
1103
|
-
__b.wfClearUnconsumed(this.__wfUnconsumedId);
|
|
1104
|
-
this.__wfUnconsumedId = undefined;
|
|
1105
|
-
}
|
|
1106
|
-
const derived = super.then(onFulfilled, onRejected);
|
|
1107
|
-
derived.__wfRootId = this.__wfRootId;
|
|
1108
|
-
observeDispatch(derived);
|
|
1109
|
-
return derived;
|
|
1110
|
-
}
|
|
1111
|
-
finally(onFinally) {
|
|
1112
|
-
// Native Promise.prototype.finally calls the observed then with
|
|
1113
|
-
// two function combinators, indistinguishable there from a real
|
|
1114
|
-
// then(f, g) \u2014 which would mark the root rejection handled even
|
|
1115
|
-
// though finally rethrows it. Implement finally through the
|
|
1116
|
-
// observed then with an explicitly-marked rethrow combinator so
|
|
1117
|
-
// the mirror stays armed for finally-only chains.
|
|
1118
|
-
if (typeof onFinally !== 'function') {
|
|
1119
|
-
return this.then(undefined, undefined);
|
|
1120
|
-
}
|
|
1121
|
-
const onRethrow = function (err) {
|
|
1122
|
-
return Promise.resolve(onFinally()).then(function () {
|
|
1123
|
-
throw err;
|
|
1124
|
-
});
|
|
1125
|
-
};
|
|
1126
|
-
onRethrow.__wfRethrows = true;
|
|
1127
|
-
return this.then(
|
|
1128
|
-
function (value) {
|
|
1129
|
-
return Promise.resolve(onFinally()).then(function () {
|
|
1130
|
-
return value;
|
|
1131
|
-
});
|
|
1132
|
-
},
|
|
1133
|
-
onRethrow,
|
|
1134
|
-
);
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
// --- Static combinators: observe the aggregate promise ---
|
|
1138
|
-
// Promise.all/race/any build their aggregate via the native static;
|
|
1139
|
-
// it never passes through the observed then, so a fire-and-forget
|
|
1140
|
-
// aggregate holding a failed dispatch would escape the run-log
|
|
1141
|
-
// mirror. While the run is live the host's adoption-escape hook
|
|
1142
|
-
// (R11-3) still catches and mirrors the process-level
|
|
1143
|
-
// unhandledRejection \u2014 a round-14 A/B confirmed that hook, not
|
|
1144
|
-
// this wrap, is the live-run backstop \u2014 but only with the coarse
|
|
1145
|
-
// '(rejection not handled)' wording, and once the run's hook is
|
|
1146
|
-
// detached nothing in the sandbox catches the escape. R14-A:
|
|
1147
|
-
// wrapping the statics gives each aggregate its own observer, so
|
|
1148
|
-
// consumption tracking works through the aggregate's own observed
|
|
1149
|
-
// then (await / .catch / .then marks it handled) and the mirror
|
|
1150
|
-
// itself classifies the rejection; the elements still funnel
|
|
1151
|
-
// through the then override via the native static's internal
|
|
1152
|
-
// attach, so they stay marked consumed.
|
|
1153
|
-
function observeAggregate(nativeAggregate) {
|
|
1154
|
-
const rootId = __b.wfRegisterRoot();
|
|
1155
|
-
const observed = new ObservedPromise(function (resolve, reject) {
|
|
1156
|
-
Promise.prototype.then.call(nativeAggregate, resolve, reject);
|
|
1157
|
-
});
|
|
1158
|
-
observed.__wfRootId = rootId;
|
|
1159
|
-
observed.__wfIsRoot = true;
|
|
1160
|
-
observeDispatch(observed);
|
|
1161
|
-
return observed;
|
|
1162
|
-
}
|
|
1163
|
-
const nativePromiseAll = Promise.all;
|
|
1164
|
-
const nativePromiseRace = Promise.race;
|
|
1165
|
-
const nativePromiseAny = Promise.any;
|
|
1166
|
-
// R11-16: a static called on a script-defined Promise subclass
|
|
1167
|
-
// must honor the species contract and return a subclass instance,
|
|
1168
|
-
// so non-default receivers bypass observation (their elements
|
|
1169
|
-
// still funnel through the observed then, and a fire-and-forget
|
|
1170
|
-
// dispatch failure escaping such an aggregate is still caught by
|
|
1171
|
-
// the host's adoption-escape hook via its stamped markers).
|
|
1172
|
-
Promise.all = function (items) {
|
|
1173
|
-
if (this !== Promise) return nativePromiseAll.call(this, items);
|
|
1174
|
-
return observeAggregate(nativePromiseAll.call(this, items));
|
|
1175
|
-
};
|
|
1176
|
-
Promise.race = function (items) {
|
|
1177
|
-
if (this !== Promise) return nativePromiseRace.call(this, items);
|
|
1178
|
-
return observeAggregate(nativePromiseRace.call(this, items));
|
|
1179
|
-
};
|
|
1180
|
-
Promise.any = function (items) {
|
|
1181
|
-
if (this !== Promise) return nativePromiseAny.call(this, items);
|
|
1182
|
-
return observeAggregate(nativePromiseAny.call(this, items));
|
|
1183
|
-
};
|
|
1184
|
-
function vmAsync(hostFn) {
|
|
1185
|
-
return function (...vmArgs) {
|
|
1186
|
-
const rootId = __b.wfRegisterRoot();
|
|
1187
|
-
const p = new ObservedPromise(function (resolve, reject) {
|
|
1188
|
-
function rejectMapped(hostErr) {
|
|
1189
|
-
const vmErr = mapDispatchError(hostErr);
|
|
1190
|
-
// Stamp the originating root and run BEFORE the error can
|
|
1191
|
-
// be forwarded into aggregate promises: the observer's
|
|
1192
|
-
// cross-root suppression (R11-15) reads __wfRootId and
|
|
1193
|
-
// the host's adoption-escape hook (R11-3) attributes by
|
|
1194
|
-
// __wfRunId.
|
|
1195
|
-
try {
|
|
1196
|
-
vmErr.__wfRootId = rootId;
|
|
1197
|
-
vmErr.__wfRunId = __b.runId;
|
|
1198
|
-
} catch (e) {}
|
|
1199
|
-
reject(vmErr);
|
|
1200
|
-
}
|
|
1201
|
-
try {
|
|
1202
|
-
const hostPromise = hostFn.apply(null, vmArgs);
|
|
1203
|
-
hostPromise.then(
|
|
1204
|
-
function (value) { resolve(value); },
|
|
1205
|
-
rejectMapped
|
|
1206
|
-
);
|
|
1207
|
-
} catch (hostErr) {
|
|
1208
|
-
rejectMapped(hostErr);
|
|
1209
|
-
}
|
|
1210
|
-
});
|
|
1211
|
-
p.__wfRootId = rootId;
|
|
1212
|
-
p.__wfIsRoot = true;
|
|
1213
|
-
observeDispatch(p);
|
|
1214
|
-
return p;
|
|
1215
|
-
};
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
// --- phase / log ---
|
|
1219
|
-
globalThis.phase = function phase(title) {
|
|
1220
|
-
__b.pushPhase(String(title));
|
|
1221
|
-
};
|
|
1222
|
-
globalThis.log = function log(msg) {
|
|
1223
|
-
__b.pushLog(msg);
|
|
1224
|
-
};
|
|
1225
|
-
|
|
1226
|
-
// --- console (object with hardened methods, all in vm-realm) ---
|
|
1227
|
-
const safeConsole = Object.create(null);
|
|
1228
|
-
safeConsole.log = function () {
|
|
1229
|
-
const parts = [];
|
|
1230
|
-
for (let i = 0; i < arguments.length; i++) parts.push(String(arguments[i]));
|
|
1231
|
-
__b.pushLog(parts.join(' '));
|
|
1232
|
-
};
|
|
1233
|
-
safeConsole.warn = safeConsole.log;
|
|
1234
|
-
safeConsole.error = safeConsole.log;
|
|
1235
|
-
globalThis.console = safeConsole;
|
|
1236
|
-
|
|
1237
|
-
// --- agent (with runtime allowlist + named throws, all vm-realm) ---
|
|
1238
|
-
// FIX-Round1-T13: throw on any opts key not in the allowlist \u2014 catches
|
|
1239
|
-
// typos like { scema: ... } that previously slipped through the
|
|
1240
|
-
// [key:string]: unknown index signature.
|
|
1241
|
-
const KNOWN_AGENT_OPTS = ['label', 'phase', 'schema', 'model', 'effort', 'isolation', 'agentType', 'stallMs', 'workingDir', 'disallowedTools'];
|
|
1242
|
-
globalThis.agent = vmAsync(function (prompt, agentOpts) {
|
|
1243
|
-
agentOpts = agentOpts || {};
|
|
1244
|
-
const keys = Object.keys(agentOpts);
|
|
1245
|
-
for (let i = 0; i < keys.length; i++) {
|
|
1246
|
-
const k = keys[i];
|
|
1247
|
-
if (KNOWN_AGENT_OPTS.indexOf(k) === -1) {
|
|
1248
|
-
throw new Error(
|
|
1249
|
-
"agent({" + k + "}): unknown option. " +
|
|
1250
|
-
"Known options are: " + KNOWN_AGENT_OPTS.join(', ') + "."
|
|
1251
|
-
);
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
// P3: schema + model + agentType + isolation are all wired through
|
|
1255
|
-
// createProductionDispatch \u2192 SubagentManager.createAgentHeadless.
|
|
1256
|
-
// The dispatch surfaces descriptive errors for "agent type not found",
|
|
1257
|
-
// "isolation:'remote' is not available in this build", parent-dirty
|
|
1258
|
-
// refuse, worktree creation failures, and StructuredOutput contract
|
|
1259
|
-
// violations ("completed without calling StructuredOutput after 2
|
|
1260
|
-
// in-conversation nudges").
|
|
1261
|
-
if (
|
|
1262
|
-
agentOpts.isolation !== undefined &&
|
|
1263
|
-
agentOpts.isolation !== 'worktree' &&
|
|
1264
|
-
agentOpts.isolation !== 'remote'
|
|
1265
|
-
) {
|
|
1266
|
-
throw new Error(
|
|
1267
|
-
"agent({isolation: '" + agentOpts.isolation + "'}): unknown isolation mode. " +
|
|
1268
|
-
"Known modes are: 'worktree', 'remote'."
|
|
1269
|
-
);
|
|
1270
|
-
}
|
|
1271
|
-
// NOTE: this init script is a host-side template literal \u2014 no
|
|
1272
|
-
// backticks anywhere below, in code or comments.
|
|
1273
|
-
//
|
|
1274
|
-
// A non-number stallMs is silently dropped downstream and the
|
|
1275
|
-
// default watchdog applies, contradicting "0 disables the watchdog"
|
|
1276
|
-
// \u2014 refuse it loudly like the other option gates.
|
|
1277
|
-
if (agentOpts.stallMs !== undefined && (typeof agentOpts.stallMs !== 'number' || !Number.isFinite(agentOpts.stallMs))) {
|
|
1278
|
-
throw new Error("agent({stallMs}): must be a finite number of milliseconds (0 disables the watchdog).");
|
|
1279
|
-
}
|
|
1280
|
-
// workingDir pins the agent to a worktree the CALLER already owns;
|
|
1281
|
-
// isolation creates and reaps one. Asking for both is a contradiction
|
|
1282
|
-
// about who owns the directory's lifetime, so name it here rather than
|
|
1283
|
-
// silently letting one win.
|
|
1284
|
-
if (agentOpts.workingDir !== undefined) {
|
|
1285
|
-
if (typeof agentOpts.workingDir !== 'string' || agentOpts.workingDir.trim().length === 0) {
|
|
1286
|
-
throw new Error(
|
|
1287
|
-
"agent({workingDir}): must be a non-empty string naming an existing " +
|
|
1288
|
-
"git worktree of this repository."
|
|
1289
|
-
);
|
|
1290
|
-
}
|
|
1291
|
-
if (agentOpts.isolation !== undefined) {
|
|
1292
|
-
throw new Error(
|
|
1293
|
-
"agent({workingDir, isolation}): incompatible options. workingDir " +
|
|
1294
|
-
"pins the agent to a worktree you already own; isolation creates " +
|
|
1295
|
-
"a fresh one and removes it afterwards. Pass one."
|
|
1296
|
-
);
|
|
1297
|
-
}
|
|
1298
|
-
}
|
|
1299
|
-
// SECURITY (P3 R2 self-review): user-script-controlled agentOpts
|
|
1300
|
-
// cross the vm/host boundary verbatim via vmAsync's hostFn.apply.
|
|
1301
|
-
// A Proxy / inherited-getter / non-plain object in agentOpts.schema
|
|
1302
|
-
// would let host-side code (SyntheticOutputTool constructor + AJV
|
|
1303
|
-
// compile) trigger user-controlled trap handlers that execute with
|
|
1304
|
-
// the host realm's full surface. Revive agentOpts through JSON
|
|
1305
|
-
// round-trip BEFORE crossing so the host only ever sees vm-realm
|
|
1306
|
-
// plain objects with vm-realm prototypes. Same mechanism that
|
|
1307
|
-
// makes args + parallel/pipeline results safe.
|
|
1308
|
-
var safeOpts;
|
|
1309
|
-
try {
|
|
1310
|
-
safeOpts = JSON.parse(JSON.stringify(agentOpts));
|
|
1311
|
-
} catch (e) {
|
|
1312
|
-
throw new Error(
|
|
1313
|
-
"agent() opts contain a non-JSON-serializable value: " +
|
|
1314
|
-
String(e && e.message != null ? e.message : e)
|
|
1315
|
-
);
|
|
1316
|
-
}
|
|
1317
|
-
// effort and disallowedTools are validated on the REVIVED copy: that
|
|
1318
|
-
// is the object the host dispatch and the resume key see, so a getter
|
|
1319
|
-
// cannot show one value here and hand another to the dispatch. Both
|
|
1320
|
-
// are normalized in place (an effort alias becomes its tier; a deny
|
|
1321
|
-
// list has built-in display names mapped to tool names and is sorted
|
|
1322
|
-
// and de-duplicated) so equivalent spellings share one resume key.
|
|
1323
|
-
if (safeOpts.effort !== undefined) {
|
|
1324
|
-
var tier = __b.normalizeEffort(safeOpts.effort);
|
|
1325
|
-
if (tier === null) {
|
|
1326
|
-
throw new Error(
|
|
1327
|
-
"agent({effort}): unknown effort tier " + __b.sanitizeForMessage(JSON.stringify(safeOpts.effort)) + ". " +
|
|
1328
|
-
"Known tiers are: " + __b.effortTiers + "."
|
|
1329
|
-
);
|
|
1330
|
-
}
|
|
1331
|
-
safeOpts.effort = tier;
|
|
1332
|
-
}
|
|
1333
|
-
if (safeOpts.disallowedTools !== undefined) {
|
|
1334
|
-
var denied = safeOpts.disallowedTools;
|
|
1335
|
-
if (
|
|
1336
|
-
!Array.isArray(denied) ||
|
|
1337
|
-
denied.some(function (name) {
|
|
1338
|
-
return typeof name !== 'string' || name.length === 0 || name !== name.trim();
|
|
1339
|
-
})
|
|
1340
|
-
) {
|
|
1341
|
-
throw new Error(
|
|
1342
|
-
"agent({disallowedTools}): must be an array of non-empty tool-name strings " +
|
|
1343
|
-
"without surrounding whitespace, e.g. ['run_shell_command', 'write_file']."
|
|
1344
|
-
);
|
|
1345
|
-
}
|
|
1346
|
-
var uniqueDenied = [];
|
|
1347
|
-
for (var d = 0; d < denied.length; d++) {
|
|
1348
|
-
var deniedName = __b.canonicalDenyName(denied[d]);
|
|
1349
|
-
if (uniqueDenied.indexOf(deniedName) === -1) uniqueDenied.push(deniedName);
|
|
1350
|
-
}
|
|
1351
|
-
uniqueDenied.sort();
|
|
1352
|
-
if (uniqueDenied.length === 0) {
|
|
1353
|
-
delete safeOpts.disallowedTools;
|
|
1354
|
-
} else {
|
|
1355
|
-
safeOpts.disallowedTools = uniqueDenied;
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
// The phase is recorded only once every option gate above has passed,
|
|
1359
|
-
// so a call its options rejected leaves no phase that dispatched
|
|
1360
|
-
// nothing.
|
|
1361
|
-
if (typeof agentOpts.phase === 'string' && agentOpts.phase.length > 0) {
|
|
1362
|
-
if (__b.lastPhase() !== agentOpts.phase) {
|
|
1363
|
-
__b.pushPhase(agentOpts.phase);
|
|
1364
|
-
}
|
|
1365
|
-
}
|
|
1366
|
-
// SECURITY (PR #4947 R1 wenshao, extended for P3): vmAsync's resolve
|
|
1367
|
-
// path is verbatim (no re-wrap of resolved values). Host-realm
|
|
1368
|
-
// strings cross the boundary harmlessly because primitives have no
|
|
1369
|
-
// prototype identity. But P3's schema-mode dispatch returns the
|
|
1370
|
-
// validated structured_output args as a host-realm OBJECT --
|
|
1371
|
-
// handing that to the script reopens the T1/T8/T14 escape:
|
|
1372
|
-
// result.constructor.constructor("return process")() would walk
|
|
1373
|
-
// the host Object.prototype chain to the host Function
|
|
1374
|
-
// constructor. Per-call JSON revival inside this vm runInContext
|
|
1375
|
-
// block makes the returned object carry vm-realm prototypes (same
|
|
1376
|
-
// mechanism as parallel/pipeline reviveInRealm and the args
|
|
1377
|
-
// global revival). The fallback to null on a non-serializable
|
|
1378
|
-
// resolve mirrors the errors-as-data convention parallel/pipeline
|
|
1379
|
-
// already use for individual slot failures.
|
|
1380
|
-
// R3 review (wenshao T3 [Suggestion]): the null fallback below is
|
|
1381
|
-
// a SECURITY backstop, not a contract path. In schema mode the
|
|
1382
|
-
// host return is the validated args of a structured_output tool
|
|
1383
|
-
// call -- LLM tool_call payloads are always JSON-serializable
|
|
1384
|
-
// (the model sends them through the OpenAI tool-call protocol
|
|
1385
|
-
// which serializes through JSON itself) and SyntheticOutputTool's
|
|
1386
|
-
// AJV validation runs over the parsed JSON, so a non-serializable
|
|
1387
|
-
// host return is unreachable in production schema mode. The
|
|
1388
|
-
// sentinel preserves the errors-as-data convention parallel /
|
|
1389
|
-
// pipeline already use for individual slot failures, and stays as
|
|
1390
|
-
// residual defense for any future dispatch path whose return
|
|
1391
|
-
// value isn't a tool_call payload. logRevivalFailure surfaces
|
|
1392
|
-
// the actionable detail (slot 0 + the error string) to operators
|
|
1393
|
-
// so a real trigger in production isn't silent.
|
|
1394
|
-
return __b.hostAgent(prompt, safeOpts).then(function (value) {
|
|
1395
|
-
if (value === null || typeof value !== 'object') {
|
|
1396
|
-
return value;
|
|
1397
|
-
}
|
|
1398
|
-
try {
|
|
1399
|
-
return JSON.parse(JSON.stringify(value));
|
|
1400
|
-
} catch (e) {
|
|
1401
|
-
__b.logRevivalFailure(0, String(e && e.message != null ? e.message : e));
|
|
1402
|
-
return null;
|
|
1403
|
-
}
|
|
1404
|
-
});
|
|
1405
|
-
});
|
|
1406
|
-
|
|
1407
|
-
// --- parallel / pipeline ---
|
|
1408
|
-
// SECURITY (PR #4732 P2): the host impl resolves with a HOST-realm array.
|
|
1409
|
-
// vmAsync's resolve path is verbatim (it does NOT re-wrap resolved
|
|
1410
|
-
// values), so handing that host array to the script would reopen the
|
|
1411
|
-
// T1/T8/T14 escape: result.constructor.constructor('return process')()
|
|
1412
|
-
// walks the host Array.prototype chain to the host Function constructor.
|
|
1413
|
-
// We revive the array INSIDE the vm realm with JSON.parse(JSON.stringify)
|
|
1414
|
-
// -- the same mechanism that makes the args global safe (see the args
|
|
1415
|
-
// revival above) -- so the value the script sees has vm-realm prototypes
|
|
1416
|
-
// whose constructors can't reach host process. Agent results are JSON
|
|
1417
|
-
// strings (and null slots), so the round-trip is lossless for P2.
|
|
1418
|
-
//
|
|
1419
|
-
// EAD-1 (P2 self-review): revive PER-ELEMENT, not the whole array in one
|
|
1420
|
-
// JSON.stringify. A single slot whose VALUE is non-serializable (a thunk
|
|
1421
|
-
// that returns a BigInt or a circular object) must become null at its
|
|
1422
|
-
// index -- it must NOT throw on the whole array and destroy every sibling
|
|
1423
|
-
// result, which would defeat errors-as-data for return values. The outer
|
|
1424
|
-
// [] is built in-realm here, so the result keeps vm-realm prototypes.
|
|
1425
|
-
//
|
|
1426
|
-
// SECURITY (PR #4947 R1 wenshao): reviveInRealm MUST remain inside this
|
|
1427
|
-
// vm init runInContext block. JSON, Array, Object here are vm-realm
|
|
1428
|
-
// globals; extracting this function to a host-side utility (e.g. a
|
|
1429
|
-
// shared utils/jsonRevive.ts) would resolve those references against
|
|
1430
|
-
// the HOST realm, silently reopening the T1/T8/T14 escape that the
|
|
1431
|
-
// revival is designed to prevent. The textual identity to a host-side
|
|
1432
|
-
// util is exactly the trap.
|
|
1433
|
-
function reviveInRealm(hostArr) {
|
|
1434
|
-
const out = [];
|
|
1435
|
-
for (let i = 0; i < hostArr.length; i++) {
|
|
1436
|
-
try {
|
|
1437
|
-
out[i] = JSON.parse(JSON.stringify(hostArr[i]));
|
|
1438
|
-
} catch (e) {
|
|
1439
|
-
// Cross to host realm for debug logging. The bridge function
|
|
1440
|
-
// accepts only primitive strings/numbers; the error message is
|
|
1441
|
-
// coerced to a String here so no vm-realm Error object crosses.
|
|
1442
|
-
__b.logRevivalFailure(i, String(e?.message ?? e));
|
|
1443
|
-
out[i] = null;
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
return out;
|
|
1447
|
-
}
|
|
1448
|
-
if (__b.hasParallel) {
|
|
1449
|
-
const callParallel = vmAsync(function (thunks) {
|
|
1450
|
-
return __b.hostParallel(thunks);
|
|
1451
|
-
});
|
|
1452
|
-
globalThis.parallel = function parallel(thunks) {
|
|
1453
|
-
return callParallel(thunks).then(reviveInRealm);
|
|
1454
|
-
};
|
|
1455
|
-
} else {
|
|
1456
|
-
globalThis.parallel = function parallel() {
|
|
1457
|
-
return new Promise(function (_, reject) {
|
|
1458
|
-
reject(new Error(
|
|
1459
|
-
'parallel() is unavailable: this sandbox was created without a ' +
|
|
1460
|
-
'parallel implementation. The orchestrator injects one; a bare ' +
|
|
1461
|
-
'sandbox has no concurrent-dispatch capability.'
|
|
1462
|
-
));
|
|
1463
|
-
});
|
|
1464
|
-
};
|
|
1465
|
-
}
|
|
1466
|
-
if (__b.hasPipeline) {
|
|
1467
|
-
const callPipeline = vmAsync(function (items) {
|
|
1468
|
-
const stages = [];
|
|
1469
|
-
for (let i = 1; i < arguments.length; i++) stages.push(arguments[i]);
|
|
1470
|
-
return __b.hostPipeline.apply(null, [items].concat(stages));
|
|
1471
|
-
});
|
|
1472
|
-
globalThis.pipeline = function pipeline() {
|
|
1473
|
-
return callPipeline.apply(null, arguments).then(reviveInRealm);
|
|
1474
|
-
};
|
|
1475
|
-
} else {
|
|
1476
|
-
globalThis.pipeline = function pipeline() {
|
|
1477
|
-
return new Promise(function (_, reject) {
|
|
1478
|
-
reject(new Error(
|
|
1479
|
-
'pipeline() is unavailable: this sandbox was created without a ' +
|
|
1480
|
-
'pipeline implementation. The orchestrator injects one; a bare ' +
|
|
1481
|
-
'sandbox has no staggered multi-stage capability.'
|
|
1482
|
-
));
|
|
1483
|
-
});
|
|
1484
|
-
};
|
|
1485
|
-
}
|
|
1486
|
-
// --- workflow (nested, single-level) ---
|
|
1487
|
-
// Mirrors agent(): args cross vm\u2192host verbatim (safe direction), the
|
|
1488
|
-
// single result is revived back into the vm realm via JSON round-trip
|
|
1489
|
-
// (same T1/T8/T14 escape defense as agent / parallel / pipeline). The
|
|
1490
|
-
// host impl resolves a saved workflow and runs it sharing this run's
|
|
1491
|
-
// agent-count cap + token budget. When __b.hasWorkflow is false, the
|
|
1492
|
-
// sandbox is either bare (no resolver wired) OR is itself a nested
|
|
1493
|
-
// workflow \u2014 in both cases workflow() must throw, which is how the
|
|
1494
|
-
// single-level nesting limit is enforced (a nested sandbox is created
|
|
1495
|
-
// without a workflow impl, so its workflow() lands in the else branch).
|
|
1496
|
-
if (__b.hasWorkflow) {
|
|
1497
|
-
const callWorkflow = vmAsync(function (nameOrRef, wfArgs) {
|
|
1498
|
-
// Sanitize args through a JSON round-trip BEFORE crossing so the
|
|
1499
|
-
// host only ever sees vm-realm plain objects (same defense as
|
|
1500
|
-
// agent()'s safeOpts). nameOrRef may be a string or {scriptPath}.
|
|
1501
|
-
var safeRef;
|
|
1502
|
-
var safeArgs;
|
|
1503
|
-
try {
|
|
1504
|
-
safeRef = nameOrRef === undefined
|
|
1505
|
-
? undefined
|
|
1506
|
-
: JSON.parse(JSON.stringify(nameOrRef));
|
|
1507
|
-
safeArgs = wfArgs === undefined
|
|
1508
|
-
? undefined
|
|
1509
|
-
: JSON.parse(JSON.stringify(wfArgs));
|
|
1510
|
-
} catch (e) {
|
|
1511
|
-
throw new Error(
|
|
1512
|
-
'workflow() received a non-JSON-serializable argument: ' +
|
|
1513
|
-
String(e && e.message != null ? e.message : e)
|
|
1514
|
-
);
|
|
1515
|
-
}
|
|
1516
|
-
return __b.hostWorkflow(safeRef, safeArgs).then(function (value) {
|
|
1517
|
-
if (value === null || typeof value !== 'object') {
|
|
1518
|
-
return value;
|
|
1519
|
-
}
|
|
1520
|
-
try {
|
|
1521
|
-
return JSON.parse(JSON.stringify(value));
|
|
1522
|
-
} catch (e) {
|
|
1523
|
-
__b.logRevivalFailure(0, String(e && e.message != null ? e.message : e));
|
|
1524
|
-
return null;
|
|
1525
|
-
}
|
|
1526
|
-
});
|
|
1527
|
-
});
|
|
1528
|
-
globalThis.workflow = function workflow(nameOrRef, wfArgs) {
|
|
1529
|
-
return callWorkflow(nameOrRef, wfArgs);
|
|
1530
|
-
};
|
|
1531
|
-
} else {
|
|
1532
|
-
globalThis.workflow = function workflow() {
|
|
1533
|
-
return new Promise(function (_, reject) {
|
|
1534
|
-
reject(new Error(
|
|
1535
|
-
"workflow() is unavailable here. Either this sandbox was created " +
|
|
1536
|
-
"without a saved-workflow resolver, or this script is already " +
|
|
1537
|
-
"running as a nested workflow \u2014 workflow() nesting is limited to " +
|
|
1538
|
-
"a single level (a workflow cannot call another workflow that " +
|
|
1539
|
-
"itself calls workflow())."
|
|
1540
|
-
));
|
|
1541
|
-
});
|
|
1542
|
-
};
|
|
1543
|
-
}
|
|
1544
|
-
|
|
1545
|
-
// --- budget ---
|
|
1546
|
-
const safeBudget = Object.create(null);
|
|
1547
|
-
Object.defineProperty(safeBudget, 'total', {
|
|
1548
|
-
value: __b.budgetTotal,
|
|
1549
|
-
writable: false, configurable: false,
|
|
1550
|
-
});
|
|
1551
|
-
if (__b.hasBudget) {
|
|
1552
|
-
Object.defineProperty(safeBudget, 'spent', {
|
|
1553
|
-
value: function spent() { return __b.hostBudgetSpent(); },
|
|
1554
|
-
writable: false, configurable: false,
|
|
1555
|
-
});
|
|
1556
|
-
Object.defineProperty(safeBudget, 'remaining', {
|
|
1557
|
-
value: function remaining() { return __b.hostBudgetRemaining(); },
|
|
1558
|
-
writable: false, configurable: false,
|
|
1559
|
-
});
|
|
1560
|
-
} else {
|
|
1561
|
-
Object.defineProperty(safeBudget, 'spent', {
|
|
1562
|
-
value: function spent() {
|
|
1563
|
-
throw new Error(
|
|
1564
|
-
'budget.spent() is not supported in P1. Token tracking is scheduled for P5.'
|
|
1565
|
-
);
|
|
1566
|
-
},
|
|
1567
|
-
writable: false, configurable: false,
|
|
1568
|
-
});
|
|
1569
|
-
Object.defineProperty(safeBudget, 'remaining', {
|
|
1570
|
-
value: function remaining() {
|
|
1571
|
-
throw new Error(
|
|
1572
|
-
'budget.remaining() is not supported in P1. Token tracking is scheduled for P5.'
|
|
1573
|
-
);
|
|
1574
|
-
},
|
|
1575
|
-
writable: false, configurable: false,
|
|
1576
|
-
});
|
|
1577
|
-
}
|
|
1578
|
-
globalThis.budget = safeBudget;
|
|
1579
|
-
})();`,
|
|
1580
|
-
ctx,
|
|
1581
|
-
{ filename: "workflow-sandbox-init.js" }
|
|
1582
|
-
);
|
|
1583
|
-
const maxWallClockMs = resolveMaxWallClockMs(opts);
|
|
1584
|
-
const flushUnconsumedRejections = /* @__PURE__ */ __name(async () => {
|
|
1585
|
-
unconsumedSettled = true;
|
|
1586
|
-
if (nextUnconsumedId === 1) return;
|
|
1587
|
-
await new Promise((resolve2) => {
|
|
1588
|
-
setImmediate(resolve2);
|
|
1589
|
-
});
|
|
1590
|
-
for (const rec of unconsumedRejections.values()) {
|
|
1591
|
-
const rootState = unconsumedRoots.get(rec.rootId);
|
|
1592
|
-
if (rootState?.rejectionHandled) {
|
|
1593
|
-
continue;
|
|
1594
|
-
}
|
|
1595
|
-
if (rec.dispatchFailed && rootState?.adoptedOut) {
|
|
1596
|
-
continue;
|
|
1597
|
-
}
|
|
1598
|
-
const key = rec.rootId + "\0" + rec.msg;
|
|
1599
|
-
if (mirroredEscapeKeys.has(key)) continue;
|
|
1600
|
-
mirroredEscapeKeys.add(key);
|
|
1601
|
-
safeLog(unconsumedRejectionLine(rec));
|
|
1602
|
-
}
|
|
1603
|
-
unconsumedRejections.clear();
|
|
1604
|
-
}, "flushUnconsumedRejections");
|
|
1605
|
-
const hookRunId = opts.runId ?? "";
|
|
1606
|
-
const adoptionEscapeHook = /* @__PURE__ */ __name((reason, _promise) => {
|
|
1607
|
-
try {
|
|
1608
|
-
if (!reason || typeof reason !== "object") return;
|
|
1609
|
-
const marked = reason;
|
|
1610
|
-
if (marked.__wfDispatchFailed !== true) return;
|
|
1611
|
-
if (String(marked.__wfRunId ?? "") !== hookRunId) return;
|
|
1612
|
-
const rootId = typeof marked.__wfRootId === "number" ? marked.__wfRootId : void 0;
|
|
1613
|
-
if (rootId !== void 0 && unconsumedRoots.get(rootId)?.rejectionHandled) {
|
|
1614
|
-
return;
|
|
1615
|
-
}
|
|
1616
|
-
let msg;
|
|
1617
|
-
try {
|
|
1618
|
-
msg = marked.message != null ? String(marked.message) : String(reason);
|
|
1619
|
-
} catch {
|
|
1620
|
-
msg = "[unserializable rejection value]";
|
|
1621
|
-
}
|
|
1622
|
-
const key = String(rootId ?? "") + "\0" + msg;
|
|
1623
|
-
if (mirroredEscapeKeys.has(key)) return;
|
|
1624
|
-
mirroredEscapeKeys.add(key);
|
|
1625
|
-
safeLog("dispatch failed (rejection not handled): " + msg);
|
|
1626
|
-
} catch (e) {
|
|
1627
|
-
debugLogger.warn("adoptionEscapeHook failed:", e);
|
|
1628
|
-
}
|
|
1629
|
-
}, "adoptionEscapeHook");
|
|
1630
|
-
let extractedMeta = null;
|
|
1631
|
-
return {
|
|
1632
|
-
async run(scriptSource) {
|
|
1633
|
-
unconsumedSettled = false;
|
|
1634
|
-
unconsumedRoots.clear();
|
|
1635
|
-
unconsumedRejections.clear();
|
|
1636
|
-
nextUnconsumedId = 1;
|
|
1637
|
-
mirroredEscapeKeys = /* @__PURE__ */ new Set();
|
|
1638
|
-
let watchdog;
|
|
1639
|
-
let stopWatchingState;
|
|
1640
|
-
let rearmWatchdogOnAbort;
|
|
1641
|
-
let settleOnAbort;
|
|
1642
|
-
let wallClockFired = false;
|
|
1643
|
-
process.on("unhandledRejection", adoptionEscapeHook);
|
|
1644
|
-
try {
|
|
1645
|
-
const { script, meta } = compileWorkflowScript(scriptSource);
|
|
1646
|
-
extractedMeta = meta;
|
|
1647
|
-
if (opts.abortOnTimeout?.signal.aborted) {
|
|
1648
|
-
throw workflowCancelledError();
|
|
1649
|
-
}
|
|
1650
|
-
const runOpts = {
|
|
1651
|
-
timeout: WORKFLOW_SYNC_EVALUATION_TIMEOUT_MS
|
|
1652
|
-
};
|
|
1653
|
-
const result = script.runInContext(ctx, runOpts);
|
|
1654
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
1655
|
-
watchdog = new WallClockWatchdog(maxWallClockMs, () => {
|
|
1656
|
-
wallClockFired = true;
|
|
1657
|
-
opts.abortOnTimeout?.abort();
|
|
1658
|
-
reject(
|
|
1659
|
-
new Error(
|
|
1660
|
-
`Workflow execution exceeded ${maxWallClockMs} ms of active time (paused time is not counted). Override via SandboxOptions.maxWallClockMs or QWEN_CODE_MAX_WORKFLOW_SECONDS env var.`
|
|
1661
|
-
)
|
|
1662
|
-
);
|
|
1663
|
-
});
|
|
1664
|
-
});
|
|
1665
|
-
const aborted = /* @__PURE__ */ __name(() => opts.abortOnTimeout?.signal.aborted === true, "aborted");
|
|
1666
|
-
stopWatchingState = opts.scheduler?.onStateChange(({ state }) => {
|
|
1667
|
-
if (state === "paused" && !aborted()) watchdog?.pause();
|
|
1668
|
-
else watchdog?.resume();
|
|
1669
|
-
});
|
|
1670
|
-
if (opts.scheduler?.snapshot().state === "paused" && !aborted()) {
|
|
1671
|
-
watchdog?.pause();
|
|
1672
|
-
}
|
|
1673
|
-
rearmWatchdogOnAbort = /* @__PURE__ */ __name(() => watchdog?.resume(), "rearmWatchdogOnAbort");
|
|
1674
|
-
opts.abortOnTimeout?.signal.addEventListener(
|
|
1675
|
-
"abort",
|
|
1676
|
-
rearmWatchdogOnAbort,
|
|
1677
|
-
{ once: true }
|
|
1678
|
-
);
|
|
1679
|
-
const abortPromise = new Promise((_, reject) => {
|
|
1680
|
-
const signal = opts.abortOnTimeout?.signal;
|
|
1681
|
-
if (!signal) return;
|
|
1682
|
-
settleOnAbort = /* @__PURE__ */ __name(() => {
|
|
1683
|
-
if (!wallClockFired) reject(workflowCancelledError());
|
|
1684
|
-
}, "settleOnAbort");
|
|
1685
|
-
signal.addEventListener("abort", settleOnAbort, { once: true });
|
|
1686
|
-
});
|
|
1687
|
-
return await Promise.race([result, timeoutPromise, abortPromise]);
|
|
1688
|
-
} finally {
|
|
1689
|
-
if (rearmWatchdogOnAbort) {
|
|
1690
|
-
opts.abortOnTimeout?.signal.removeEventListener(
|
|
1691
|
-
"abort",
|
|
1692
|
-
rearmWatchdogOnAbort
|
|
1693
|
-
);
|
|
1694
|
-
}
|
|
1695
|
-
if (settleOnAbort) {
|
|
1696
|
-
opts.abortOnTimeout?.signal.removeEventListener(
|
|
1697
|
-
"abort",
|
|
1698
|
-
settleOnAbort
|
|
1699
|
-
);
|
|
1700
|
-
}
|
|
1701
|
-
stopWatchingState?.();
|
|
1702
|
-
watchdog?.stop();
|
|
1703
|
-
await flushUnconsumedRejections();
|
|
1704
|
-
process.off("unhandledRejection", adoptionEscapeHook);
|
|
1705
|
-
}
|
|
1706
|
-
},
|
|
1707
|
-
getPhases: /* @__PURE__ */ __name(() => [...phases], "getPhases"),
|
|
1708
|
-
getLogs: /* @__PURE__ */ __name(() => [...logs], "getLogs"),
|
|
1709
|
-
appendLog: /* @__PURE__ */ __name((line) => safeLog(line, false), "appendLog"),
|
|
1710
|
-
getMeta: /* @__PURE__ */ __name(() => extractedMeta, "getMeta")
|
|
1711
|
-
};
|
|
1712
|
-
}
|
|
1713
|
-
__name(createWorkflowSandbox, "createWorkflowSandbox");
|
|
1714
|
-
|
|
1715
|
-
// packages/core/src/agents/runtime/workflow-budget.ts
|
|
1716
|
-
init_esbuild_shims();
|
|
1717
|
-
var debugLogger2 = createDebugLogger("WORKFLOW_BUDGET");
|
|
1718
|
-
var MAX_TOKENS_PER_WORKFLOW_ENV = "QWEN_CODE_MAX_TOKENS_PER_WORKFLOW";
|
|
1719
|
-
var HARD_MAX_TOKENS_CEILING = 1e8;
|
|
1720
|
-
function resolveMaxTokensPerWorkflow(env = process.env) {
|
|
1721
|
-
const raw = env[MAX_TOKENS_PER_WORKFLOW_ENV];
|
|
1722
|
-
if (raw === void 0 || raw.trim() === "") {
|
|
1723
|
-
return null;
|
|
1724
|
-
}
|
|
1725
|
-
const parsed = parsePositiveIntegerEnv(raw, 0);
|
|
1726
|
-
if (parsed < 1) {
|
|
1727
|
-
debugLogger2.warn(
|
|
1728
|
-
`Invalid ${MAX_TOKENS_PER_WORKFLOW_ENV}=${JSON.stringify(raw)}, treating as unset (no cap)`
|
|
1729
|
-
);
|
|
1730
|
-
return null;
|
|
1731
|
-
}
|
|
1732
|
-
if (parsed > HARD_MAX_TOKENS_CEILING) {
|
|
1733
|
-
debugLogger2.warn(
|
|
1734
|
-
`${MAX_TOKENS_PER_WORKFLOW_ENV}=${parsed} exceeds hard ceiling (${HARD_MAX_TOKENS_CEILING}); clamping.`
|
|
1735
|
-
);
|
|
1736
|
-
return HARD_MAX_TOKENS_CEILING;
|
|
1737
|
-
}
|
|
1738
|
-
return parsed;
|
|
1739
|
-
}
|
|
1740
|
-
__name(resolveMaxTokensPerWorkflow, "resolveMaxTokensPerWorkflow");
|
|
1741
|
-
var WorkflowBudgetImpl = class _WorkflowBudgetImpl {
|
|
1742
|
-
static {
|
|
1743
|
-
__name(this, "WorkflowBudgetImpl");
|
|
1744
|
-
}
|
|
1745
|
-
total;
|
|
1746
|
-
_spent;
|
|
1747
|
-
constructor(total) {
|
|
1748
|
-
this.total = total;
|
|
1749
|
-
this._spent = 0;
|
|
1750
|
-
}
|
|
1751
|
-
spent() {
|
|
1752
|
-
return this._spent;
|
|
1753
|
-
}
|
|
1754
|
-
remaining() {
|
|
1755
|
-
if (this.total === null) return Infinity;
|
|
1756
|
-
return Math.max(0, this.total - this._spent);
|
|
1757
|
-
}
|
|
1758
|
-
/**
|
|
1759
|
-
* Host-side increment. NOT exposed to the script — the
|
|
1760
|
-
* `WorkflowBudget` interface deliberately omits any setter so a
|
|
1761
|
-
* malicious workflow cannot inflate / deflate the budget. Only the
|
|
1762
|
-
* orchestrator (in `countedDispatch`) calls this after a dispatch
|
|
1763
|
-
* resolves with the agent's output token count.
|
|
1764
|
-
*
|
|
1765
|
-
* Non-positive deltas are silently dropped (some dispatches return
|
|
1766
|
-
* `output_tokens: 0` on early failures); negative deltas would be a
|
|
1767
|
-
* caller bug and are also dropped rather than silently rewinding
|
|
1768
|
-
* the counter.
|
|
1769
|
-
*/
|
|
1770
|
-
recordSpent(deltaTokens) {
|
|
1771
|
-
if (!Number.isFinite(deltaTokens) || deltaTokens <= 0) return;
|
|
1772
|
-
this._spent += deltaTokens;
|
|
1773
|
-
}
|
|
1774
|
-
/**
|
|
1775
|
-
* Factory: build a budget from the current environment. Convenience
|
|
1776
|
-
* over `new WorkflowBudgetImpl(resolveMaxTokensPerWorkflow(env))`.
|
|
1777
|
-
*/
|
|
1778
|
-
static fromEnv(env = process.env) {
|
|
1779
|
-
return new _WorkflowBudgetImpl(resolveMaxTokensPerWorkflow(env));
|
|
1780
|
-
}
|
|
1781
|
-
};
|
|
1782
|
-
var WorkflowBudgetExceededError = class extends Error {
|
|
1783
|
-
static {
|
|
1784
|
-
__name(this, "WorkflowBudgetExceededError");
|
|
1785
|
-
}
|
|
1786
|
-
name = "WorkflowBudgetExceededError";
|
|
1787
|
-
runId;
|
|
1788
|
-
budgetTotal;
|
|
1789
|
-
spent;
|
|
1790
|
-
constructor(runId, budgetTotal, spent) {
|
|
1791
|
-
super(
|
|
1792
|
-
`Workflow ${runId} exceeded the token budget (${spent} / ${budgetTotal} output tokens spent).`
|
|
1793
|
-
);
|
|
1794
|
-
this.runId = runId;
|
|
1795
|
-
this.budgetTotal = budgetTotal;
|
|
1796
|
-
this.spent = spent;
|
|
1797
|
-
}
|
|
1798
|
-
};
|
|
1799
|
-
|
|
1800
|
-
// packages/core/src/agents/runtime/workflow-saved.ts
|
|
1801
|
-
init_esbuild_shims();
|
|
1802
|
-
import { promises as fs } from "node:fs";
|
|
1803
|
-
import * as path from "node:path";
|
|
1804
|
-
var debugLogger3 = createDebugLogger("WORKFLOW_SAVED");
|
|
1805
|
-
var WORKFLOW_NAME_PATTERN = /^[a-z][a-z0-9-]{0,40}$/;
|
|
1806
|
-
function validateWorkflowName(name) {
|
|
1807
|
-
if (!name) return "Workflow name is required.";
|
|
1808
|
-
if (!WORKFLOW_NAME_PATTERN.test(name)) {
|
|
1809
|
-
return `Invalid workflow name "${name}". Use lower-case letters, digits, and hyphens only (must start with a letter, max 41 chars).`;
|
|
1810
|
-
}
|
|
1811
|
-
return null;
|
|
1812
|
-
}
|
|
1813
|
-
__name(validateWorkflowName, "validateWorkflowName");
|
|
1814
|
-
function getSavedWorkflowDirs(config) {
|
|
1815
|
-
return [
|
|
1816
|
-
{ dir: config.storage.getProjectWorkflowsDir(), source: "project" },
|
|
1817
|
-
{ dir: Storage.getUserWorkflowsDir(), source: "user" }
|
|
1818
|
-
];
|
|
1819
|
-
}
|
|
1820
|
-
__name(getSavedWorkflowDirs, "getSavedWorkflowDirs");
|
|
1821
|
-
function getWorkflowScriptRoots(config) {
|
|
1822
|
-
return [
|
|
1823
|
-
...getSavedWorkflowDirs(config).map(({ dir }) => dir),
|
|
1824
|
-
config.storage.getGeneratedWorkflowsDir()
|
|
1825
|
-
];
|
|
1826
|
-
}
|
|
1827
|
-
__name(getWorkflowScriptRoots, "getWorkflowScriptRoots");
|
|
1828
|
-
async function isSymlinkedRoot(dir) {
|
|
1829
|
-
return fs.lstat(dir).then((st) => st.isSymbolicLink()).catch(() => false);
|
|
1830
|
-
}
|
|
1831
|
-
__name(isSymlinkedRoot, "isSymlinkedRoot");
|
|
1832
|
-
async function listJsFiles(dir) {
|
|
1833
|
-
if (await isSymlinkedRoot(dir)) {
|
|
1834
|
-
debugLogger3.warn(`refusing symlinked saved-workflow dir: ${dir}`);
|
|
1835
|
-
return [];
|
|
1836
|
-
}
|
|
1837
|
-
try {
|
|
1838
|
-
const names = await fs.readdir(dir);
|
|
1839
|
-
const out = [];
|
|
1840
|
-
for (const n of names) {
|
|
1841
|
-
if (!n.endsWith(".js")) continue;
|
|
1842
|
-
const st = await fs.lstat(path.join(dir, n)).catch(() => null);
|
|
1843
|
-
if (!st || st.isSymbolicLink()) continue;
|
|
1844
|
-
out.push(n);
|
|
1845
|
-
}
|
|
1846
|
-
return out;
|
|
1847
|
-
} catch (e) {
|
|
1848
|
-
const code = e?.code;
|
|
1849
|
-
if (code !== "ENOENT") {
|
|
1850
|
-
debugLogger3.warn(`listJsFiles failed for ${dir}: ${e}`);
|
|
1851
|
-
}
|
|
1852
|
-
return [];
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1855
|
-
__name(listJsFiles, "listJsFiles");
|
|
1856
|
-
async function readWorkflowFileSecurely(filePath, config) {
|
|
1857
|
-
const real = await fs.realpath(filePath);
|
|
1858
|
-
const roots = await Promise.all(
|
|
1859
|
-
getWorkflowScriptRoots(config).map(async (dir) => {
|
|
1860
|
-
if (await isSymlinkedRoot(dir)) return { dir, real: null };
|
|
1861
|
-
try {
|
|
1862
|
-
return { dir, real: await fs.realpath(dir) };
|
|
1863
|
-
} catch {
|
|
1864
|
-
return { dir, real: path.resolve(dir) };
|
|
1865
|
-
}
|
|
1866
|
-
})
|
|
1867
|
-
);
|
|
1868
|
-
const dirs = roots.flatMap((r) => r.real === null ? [] : [r.real]);
|
|
1869
|
-
const inside = dirs.some((d) => real === d || real.startsWith(d + path.sep));
|
|
1870
|
-
if (!inside) {
|
|
1871
|
-
const refused = roots.flatMap((r) => r.real === null ? [r.dir] : []);
|
|
1872
|
-
const refusedNote = refused.length > 0 ? `; refused symlinked ${refused.length === 1 ? "root" : "roots"}: ${refused.join(", ")}` : "";
|
|
1873
|
-
throw new Error(
|
|
1874
|
-
`refusing to load a workflow file outside the workflow script roots (checked: ${dirs.join(", ")}${refusedNote}): '${filePath}'.`
|
|
1875
|
-
);
|
|
1876
|
-
}
|
|
1877
|
-
return fs.readFile(real, "utf8");
|
|
1878
|
-
}
|
|
1879
|
-
__name(readWorkflowFileSecurely, "readWorkflowFileSecurely");
|
|
1880
|
-
async function resolveSavedWorkflowNameForPath(scriptPath, config) {
|
|
1881
|
-
const realScriptPath = await fs.realpath(scriptPath);
|
|
1882
|
-
for (const { dir } of getSavedWorkflowDirs(config)) {
|
|
1883
|
-
if (await isSymlinkedRoot(dir)) continue;
|
|
1884
|
-
let realDir;
|
|
1885
|
-
try {
|
|
1886
|
-
realDir = await fs.realpath(dir);
|
|
1887
|
-
} catch {
|
|
1888
|
-
continue;
|
|
1889
|
-
}
|
|
1890
|
-
if (realScriptPath !== realDir && !realScriptPath.startsWith(realDir + path.sep)) {
|
|
1891
|
-
continue;
|
|
1892
|
-
}
|
|
1893
|
-
const name = path.basename(realScriptPath).replace(/\.js$/, "");
|
|
1894
|
-
return WORKFLOW_NAME_PATTERN.test(name) ? name : void 0;
|
|
1895
|
-
}
|
|
1896
|
-
return void 0;
|
|
1897
|
-
}
|
|
1898
|
-
__name(resolveSavedWorkflowNameForPath, "resolveSavedWorkflowNameForPath");
|
|
1899
|
-
async function listSavedWorkflows(config) {
|
|
1900
|
-
const byName = /* @__PURE__ */ new Map();
|
|
1901
|
-
for (const { dir, source } of [...getSavedWorkflowDirs(config)].reverse()) {
|
|
1902
|
-
for (const file of await listJsFiles(dir)) {
|
|
1903
|
-
const name = file.slice(0, -".js".length);
|
|
1904
|
-
if (!WORKFLOW_NAME_PATTERN.test(name)) continue;
|
|
1905
|
-
byName.set(name, { name, scriptPath: path.join(dir, file), source });
|
|
1906
|
-
}
|
|
1907
|
-
}
|
|
1908
|
-
return Array.from(byName.values()).sort(
|
|
1909
|
-
(a, b) => a.name.localeCompare(b.name)
|
|
1910
|
-
);
|
|
1911
|
-
}
|
|
1912
|
-
__name(listSavedWorkflows, "listSavedWorkflows");
|
|
1913
|
-
async function resolveSavedWorkflowScript(nameOrRef, config) {
|
|
1914
|
-
if (typeof nameOrRef === "object" && nameOrRef !== null) {
|
|
1915
|
-
const scriptPath = nameOrRef.scriptPath;
|
|
1916
|
-
if (typeof scriptPath !== "string" || scriptPath.length === 0) {
|
|
1917
|
-
throw new Error(
|
|
1918
|
-
"workflow() expects a workflow name (string) or {scriptPath: string}."
|
|
1919
|
-
);
|
|
1920
|
-
}
|
|
1921
|
-
let script;
|
|
1922
|
-
try {
|
|
1923
|
-
script = await readWorkflowFileSecurely(scriptPath, config);
|
|
1924
|
-
} catch (e) {
|
|
1925
|
-
throw new Error(
|
|
1926
|
-
`workflow({scriptPath: '${scriptPath}'}): ${e instanceof Error ? e.message : String(e)}`
|
|
1927
|
-
);
|
|
1928
|
-
}
|
|
1929
|
-
const name2 = path.basename(scriptPath).replace(/\.js$/, "");
|
|
1930
|
-
const savedWorkflowName = await resolveSavedWorkflowNameForPath(
|
|
1931
|
-
scriptPath,
|
|
1932
|
-
config
|
|
1933
|
-
);
|
|
1934
|
-
return {
|
|
1935
|
-
name: name2,
|
|
1936
|
-
scriptPath,
|
|
1937
|
-
script,
|
|
1938
|
-
...savedWorkflowName ? { savedWorkflowName } : {}
|
|
1939
|
-
};
|
|
1940
|
-
}
|
|
1941
|
-
if (typeof nameOrRef !== "string") {
|
|
1942
|
-
throw new Error(
|
|
1943
|
-
"workflow() expects a workflow name (string) or {scriptPath: string}."
|
|
1944
|
-
);
|
|
1945
|
-
}
|
|
1946
|
-
const name = nameOrRef;
|
|
1947
|
-
const nameError = validateWorkflowName(name);
|
|
1948
|
-
if (nameError) {
|
|
1949
|
-
throw new Error(`workflow('${name}'): ${nameError}`);
|
|
1950
|
-
}
|
|
1951
|
-
for (const { dir } of getSavedWorkflowDirs(config)) {
|
|
1952
|
-
const scriptPath = path.join(dir, `${name}.js`);
|
|
1953
|
-
try {
|
|
1954
|
-
const script = await readWorkflowFileSecurely(scriptPath, config);
|
|
1955
|
-
return { name, scriptPath, script, savedWorkflowName: name };
|
|
1956
|
-
} catch {
|
|
1957
|
-
}
|
|
1958
|
-
}
|
|
1959
|
-
const available = (await listSavedWorkflows(config)).map((e) => e.name);
|
|
1960
|
-
throw new Error(
|
|
1961
|
-
`workflow('${name}'): no workflow with that name. Available: ${available.length > 0 ? available.join(", ") : "(none)"}.`
|
|
1962
|
-
);
|
|
1963
|
-
}
|
|
1964
|
-
__name(resolveSavedWorkflowScript, "resolveSavedWorkflowScript");
|
|
1965
|
-
async function saveWorkflowScript(config, opts) {
|
|
1966
|
-
const { name, scope, script, overwrite = false } = opts;
|
|
1967
|
-
const nameError = validateWorkflowName(name);
|
|
1968
|
-
if (nameError) return { status: "invalid-name", error: nameError };
|
|
1969
|
-
if (!script || script.trim().length === 0) {
|
|
1970
|
-
return {
|
|
1971
|
-
status: "empty-script",
|
|
1972
|
-
error: "This run has no script source to save."
|
|
1973
|
-
};
|
|
1974
|
-
}
|
|
1975
|
-
const dir = scope === "project" ? config.storage.getProjectWorkflowsDir() : Storage.getUserWorkflowsDir();
|
|
1976
|
-
if (await isSymlinkedRoot(dir)) {
|
|
1977
|
-
throw new Error(
|
|
1978
|
-
`refusing to save into a symlinked saved-workflow directory: '${dir}'.`
|
|
1979
|
-
);
|
|
1980
|
-
}
|
|
1981
|
-
const filePath = path.join(dir, `${name}.js`);
|
|
1982
|
-
if (!overwrite) {
|
|
1983
|
-
try {
|
|
1984
|
-
await fs.access(filePath);
|
|
1985
|
-
return { status: "exists", name, scope, path: filePath };
|
|
1986
|
-
} catch {
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
|
-
await fs.mkdir(dir, { recursive: true });
|
|
1990
|
-
await fs.writeFile(filePath, script, "utf8");
|
|
1991
|
-
return { status: "saved", name, scope, path: filePath };
|
|
1992
|
-
}
|
|
1993
|
-
__name(saveWorkflowScript, "saveWorkflowScript");
|
|
1994
|
-
var INLINE_RUN_ID_PATTERN = /^wf_[0-9a-f]+$/;
|
|
1995
|
-
async function persistInlineWorkflowScript(config, runId, script) {
|
|
1996
|
-
if (!INLINE_RUN_ID_PATTERN.test(runId)) {
|
|
1997
|
-
debugLogger3.warn(`refusing to persist a script for run id: ${runId}`);
|
|
1998
|
-
return null;
|
|
1999
|
-
}
|
|
2000
|
-
const storage = config.storage;
|
|
2001
|
-
if (!storage) return null;
|
|
2002
|
-
try {
|
|
2003
|
-
const filePath = storage.getInlineWorkflowScriptPath(runId);
|
|
2004
|
-
const dir = path.dirname(filePath);
|
|
2005
|
-
if (await isSymlinkedRoot(storage.getGeneratedWorkflowsDir()) || await isSymlinkedRoot(dir)) {
|
|
2006
|
-
debugLogger3.warn(
|
|
2007
|
-
`refusing to persist an inline workflow script into a symlinked root: '${dir}'.`
|
|
2008
|
-
);
|
|
2009
|
-
return null;
|
|
2010
|
-
}
|
|
2011
|
-
await fs.mkdir(dir, { recursive: true });
|
|
2012
|
-
await atomicWriteFile(filePath, script, {
|
|
2013
|
-
encoding: "utf8",
|
|
2014
|
-
mode: 384,
|
|
2015
|
-
forceMode: true,
|
|
2016
|
-
noFollow: true
|
|
2017
|
-
});
|
|
2018
|
-
return filePath;
|
|
2019
|
-
} catch (error) {
|
|
2020
|
-
debugLogger3.warn(
|
|
2021
|
-
`failed to persist inline workflow script for ${runId}: ${error}`
|
|
2022
|
-
);
|
|
2023
|
-
return null;
|
|
2024
|
-
}
|
|
2025
|
-
}
|
|
2026
|
-
__name(persistInlineWorkflowScript, "persistInlineWorkflowScript");
|
|
2027
|
-
async function deleteInlineWorkflowScript(config, runId) {
|
|
2028
|
-
if (!INLINE_RUN_ID_PATTERN.test(runId)) return false;
|
|
2029
|
-
const storage = config.storage;
|
|
2030
|
-
if (!storage) return false;
|
|
2031
|
-
try {
|
|
2032
|
-
await fs.rm(storage.getInlineWorkflowScriptPath(runId), { force: true });
|
|
2033
|
-
return true;
|
|
2034
|
-
} catch (error) {
|
|
2035
|
-
debugLogger3.warn(
|
|
2036
|
-
`failed to delete inline workflow script for ${runId}: ${error}`
|
|
2037
|
-
);
|
|
2038
|
-
return false;
|
|
2039
|
-
}
|
|
2040
|
-
}
|
|
2041
|
-
__name(deleteInlineWorkflowScript, "deleteInlineWorkflowScript");
|
|
2042
|
-
|
|
2043
|
-
// packages/core/src/agents/runtime/workflow-orchestrator.ts
|
|
2044
|
-
init_esbuild_shims();
|
|
2045
|
-
import { randomBytes } from "node:crypto";
|
|
2046
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2047
|
-
import * as os from "node:os";
|
|
2048
|
-
|
|
2049
|
-
// packages/core/src/agents/runtime/workflow-agent-failure.ts
|
|
2050
|
-
init_esbuild_shims();
|
|
2051
|
-
var WorkflowAgentFailedError = class extends Error {
|
|
2052
|
-
static {
|
|
2053
|
-
__name(this, "WorkflowAgentFailedError");
|
|
2054
|
-
}
|
|
2055
|
-
name = "WorkflowAgentFailedError";
|
|
2056
|
-
kind;
|
|
2057
|
-
terminateMode;
|
|
2058
|
-
constructor(message, kind, terminateMode) {
|
|
2059
|
-
super(message);
|
|
2060
|
-
this.kind = kind;
|
|
2061
|
-
this.terminateMode = terminateMode;
|
|
2062
|
-
}
|
|
2063
|
-
};
|
|
2064
|
-
function isWorkflowAgentFailedError(error) {
|
|
2065
|
-
return typeof error === "object" && error !== null && error.name === "WorkflowAgentFailedError";
|
|
2066
|
-
}
|
|
2067
|
-
__name(isWorkflowAgentFailedError, "isWorkflowAgentFailedError");
|
|
2068
|
-
var WorkflowAgentCapExceededError = class extends Error {
|
|
2069
|
-
static {
|
|
2070
|
-
__name(this, "WorkflowAgentCapExceededError");
|
|
2071
|
-
}
|
|
2072
|
-
name = "WorkflowAgentCapExceededError";
|
|
2073
|
-
constructor(maxAgents) {
|
|
2074
|
-
super(
|
|
2075
|
-
`Workflow exceeded the maximum of ${maxAgents} agent() calls per run.`
|
|
2076
|
-
);
|
|
2077
|
-
}
|
|
2078
|
-
};
|
|
2079
|
-
function isWorkflowRunLevelError(error) {
|
|
2080
|
-
if (typeof error !== "object" || error === null) return false;
|
|
2081
|
-
const name = error.name;
|
|
2082
|
-
return name === "WorkflowBudgetExceededError" || name === "WorkflowAgentCapExceededError";
|
|
2083
|
-
}
|
|
2084
|
-
__name(isWorkflowRunLevelError, "isWorkflowRunLevelError");
|
|
2085
|
-
var WORKFLOW_ABORT_REASON_STALLED = "stalled";
|
|
2086
|
-
|
|
2087
|
-
// packages/core/src/agents/runtime/workflow-stall.ts
|
|
2088
|
-
init_esbuild_shims();
|
|
2089
|
-
var DEFAULT_STALL_MS = 18e4;
|
|
2090
|
-
var MAX_STALL_ATTEMPTS = 3;
|
|
2091
|
-
var MAX_WORKFLOW_STALL_MS_ENV = "QWEN_CODE_WORKFLOW_STALL_SECONDS";
|
|
2092
|
-
function resolveStallMs(perCall, env = process.env) {
|
|
2093
|
-
if (typeof perCall === "number" && Number.isFinite(perCall)) {
|
|
2094
|
-
if (perCall === 0) return 0;
|
|
2095
|
-
if (perCall > 0) return perCall;
|
|
2096
|
-
}
|
|
2097
|
-
const raw = env[MAX_WORKFLOW_STALL_MS_ENV];
|
|
2098
|
-
const trimmed = raw?.trim();
|
|
2099
|
-
if (trimmed) {
|
|
2100
|
-
if (trimmed === "0") return 0;
|
|
2101
|
-
const sec = parsePositiveIntegerEnv(trimmed, 0);
|
|
2102
|
-
if (sec > 0) return sec * 1e3;
|
|
2103
|
-
}
|
|
2104
|
-
return DEFAULT_STALL_MS;
|
|
2105
|
-
}
|
|
2106
|
-
__name(resolveStallMs, "resolveStallMs");
|
|
2107
|
-
var debugLogger4 = createDebugLogger("WORKFLOW_STALL");
|
|
2108
|
-
function attachStallWatchdog(emitter, controller, stallMs) {
|
|
2109
|
-
if (stallMs <= 0) {
|
|
2110
|
-
return { stalled: /* @__PURE__ */ __name(() => false, "stalled"), dispose: /* @__PURE__ */ __name(() => {
|
|
2111
|
-
}, "dispose") };
|
|
2112
|
-
}
|
|
2113
|
-
let inFlightTools = 0;
|
|
2114
|
-
let fired = false;
|
|
2115
|
-
let timer;
|
|
2116
|
-
let disposed = false;
|
|
2117
|
-
const clear = /* @__PURE__ */ __name(() => {
|
|
2118
|
-
if (timer) {
|
|
2119
|
-
clearTimeout(timer);
|
|
2120
|
-
timer = void 0;
|
|
2121
|
-
}
|
|
2122
|
-
}, "clear");
|
|
2123
|
-
const arm = /* @__PURE__ */ __name(() => {
|
|
2124
|
-
clear();
|
|
2125
|
-
if (disposed || fired) return;
|
|
2126
|
-
if (inFlightTools > 0) return;
|
|
2127
|
-
timer = setTimeout(() => {
|
|
2128
|
-
if (disposed || fired) return;
|
|
2129
|
-
fired = true;
|
|
2130
|
-
debugLogger4.warn(
|
|
2131
|
-
`[Workflow] agent dispatch stalled \u2014 no progress for ${stallMs}ms; aborting.`
|
|
2132
|
-
);
|
|
2133
|
-
try {
|
|
2134
|
-
controller.abort(WORKFLOW_ABORT_REASON_STALLED);
|
|
2135
|
-
} catch (e) {
|
|
2136
|
-
debugLogger4.warn("stall watchdog abort threw:", e);
|
|
2137
|
-
}
|
|
2138
|
-
}, stallMs);
|
|
2139
|
-
if (typeof timer.unref === "function") {
|
|
2140
|
-
timer.unref();
|
|
2141
|
-
}
|
|
2142
|
-
}, "arm");
|
|
2143
|
-
const onActivity = /* @__PURE__ */ __name(() => arm(), "onActivity");
|
|
2144
|
-
const onToolCall = /* @__PURE__ */ __name(() => {
|
|
2145
|
-
inFlightTools += 1;
|
|
2146
|
-
clear();
|
|
2147
|
-
}, "onToolCall");
|
|
2148
|
-
const onToolResult = /* @__PURE__ */ __name(() => {
|
|
2149
|
-
inFlightTools = Math.max(0, inFlightTools - 1);
|
|
2150
|
-
arm();
|
|
2151
|
-
}, "onToolResult");
|
|
2152
|
-
emitter.on("round_start" /* ROUND_START */, onActivity);
|
|
2153
|
-
emitter.on("round_end" /* ROUND_END */, onActivity);
|
|
2154
|
-
emitter.on("stream_text" /* STREAM_TEXT */, onActivity);
|
|
2155
|
-
emitter.on("usage_metadata" /* USAGE_METADATA */, onActivity);
|
|
2156
|
-
emitter.on("tool_call" /* TOOL_CALL */, onToolCall);
|
|
2157
|
-
emitter.on("tool_result" /* TOOL_RESULT */, onToolResult);
|
|
2158
|
-
return {
|
|
2159
|
-
stalled: /* @__PURE__ */ __name(() => fired, "stalled"),
|
|
2160
|
-
dispose: /* @__PURE__ */ __name(() => {
|
|
2161
|
-
if (disposed) return;
|
|
2162
|
-
disposed = true;
|
|
2163
|
-
clear();
|
|
2164
|
-
emitter.off("round_start" /* ROUND_START */, onActivity);
|
|
2165
|
-
emitter.off("round_end" /* ROUND_END */, onActivity);
|
|
2166
|
-
emitter.off("stream_text" /* STREAM_TEXT */, onActivity);
|
|
2167
|
-
emitter.off("usage_metadata" /* USAGE_METADATA */, onActivity);
|
|
2168
|
-
emitter.off("tool_call" /* TOOL_CALL */, onToolCall);
|
|
2169
|
-
emitter.off("tool_result" /* TOOL_RESULT */, onToolResult);
|
|
2170
|
-
}, "dispose")
|
|
2171
|
-
};
|
|
2172
|
-
}
|
|
2173
|
-
__name(attachStallWatchdog, "attachStallWatchdog");
|
|
2174
|
-
async function runStallResilient(attemptFn, opts) {
|
|
2175
|
-
const { stallMs, signal, label } = opts;
|
|
2176
|
-
if (stallMs <= 0) {
|
|
2177
|
-
const emitter = new AgentEventEmitter();
|
|
2178
|
-
return attemptFn(signal ?? new AbortController().signal, emitter);
|
|
2179
|
-
}
|
|
2180
|
-
let attempt = 0;
|
|
2181
|
-
for (; ; ) {
|
|
2182
|
-
attempt += 1;
|
|
2183
|
-
const controller = new AbortController();
|
|
2184
|
-
let onParentAbort;
|
|
2185
|
-
if (signal) {
|
|
2186
|
-
if (signal.aborted) {
|
|
2187
|
-
controller.abort(signal.reason);
|
|
2188
|
-
} else {
|
|
2189
|
-
onParentAbort = /* @__PURE__ */ __name(() => controller.abort(signal.reason), "onParentAbort");
|
|
2190
|
-
signal.addEventListener("abort", onParentAbort);
|
|
2191
|
-
}
|
|
2192
|
-
}
|
|
2193
|
-
const emitter = new AgentEventEmitter();
|
|
2194
|
-
const watchdog = attachStallWatchdog(emitter, controller, stallMs);
|
|
2195
|
-
try {
|
|
2196
|
-
return await attemptFn(controller.signal, emitter);
|
|
2197
|
-
} catch (err) {
|
|
2198
|
-
if (signal?.aborted) throw err;
|
|
2199
|
-
if (watchdog.stalled() && attempt < MAX_STALL_ATTEMPTS) {
|
|
2200
|
-
debugLogger4.warn(
|
|
2201
|
-
`[Workflow] agent "${label ?? "workflow-agent"}" stalled (attempt ${attempt}/${MAX_STALL_ATTEMPTS}) \u2014 retrying.`
|
|
2202
|
-
);
|
|
2203
|
-
continue;
|
|
2204
|
-
}
|
|
2205
|
-
if (watchdog.stalled()) {
|
|
2206
|
-
throw new WorkflowAgentFailedError(
|
|
2207
|
-
`agent "${label ?? "workflow-agent"}" stalled on all ${MAX_STALL_ATTEMPTS} attempts (no progress for ${stallMs}ms each).`,
|
|
2208
|
-
"stalled"
|
|
2209
|
-
);
|
|
2210
|
-
}
|
|
2211
|
-
throw err;
|
|
2212
|
-
} finally {
|
|
2213
|
-
watchdog.dispose();
|
|
2214
|
-
if (onParentAbort && signal) {
|
|
2215
|
-
signal.removeEventListener("abort", onParentAbort);
|
|
2216
|
-
}
|
|
2217
|
-
}
|
|
2218
|
-
}
|
|
2219
|
-
}
|
|
2220
|
-
__name(runStallResilient, "runStallResilient");
|
|
2221
|
-
|
|
2222
|
-
// packages/core/src/agents/runtime/workflow-journal.ts
|
|
2223
|
-
init_esbuild_shims();
|
|
2224
|
-
import { createHash } from "node:crypto";
|
|
2225
|
-
import { constants, promises as fs2 } from "node:fs";
|
|
2226
|
-
import path2 from "node:path";
|
|
2227
|
-
var debugLogger5 = createDebugLogger("WORKFLOW_JOURNAL");
|
|
2228
|
-
var JOURNAL_KEY_VERSION = "v2";
|
|
2229
|
-
var DISPATCH_AFFECTING_AGENT_OPTS = [
|
|
2230
|
-
"schema",
|
|
2231
|
-
"model",
|
|
2232
|
-
"effort",
|
|
2233
|
-
"isolation",
|
|
2234
|
-
"agentType",
|
|
2235
|
-
"workingDir",
|
|
2236
|
-
"disallowedTools"
|
|
2237
|
-
];
|
|
2238
|
-
function canonicalizeAgentOpts(opts) {
|
|
2239
|
-
const projected = {};
|
|
2240
|
-
for (const k of DISPATCH_AFFECTING_AGENT_OPTS) {
|
|
2241
|
-
const v = opts[k];
|
|
2242
|
-
if (v === void 0 || typeof v === "function") continue;
|
|
2243
|
-
projected[k] = v;
|
|
2244
|
-
}
|
|
2245
|
-
const sortDeep = /* @__PURE__ */ __name((val) => {
|
|
2246
|
-
if (typeof val === "function") return void 0;
|
|
2247
|
-
if (Array.isArray(val)) return val.map(sortDeep);
|
|
2248
|
-
if (val && typeof val === "object") {
|
|
2249
|
-
const out = {};
|
|
2250
|
-
for (const key of Object.keys(val).sort()) {
|
|
2251
|
-
if (key === "__proto__") continue;
|
|
2252
|
-
out[key] = sortDeep(val[key]);
|
|
2253
|
-
}
|
|
2254
|
-
return out;
|
|
2255
|
-
}
|
|
2256
|
-
return val;
|
|
2257
|
-
}, "sortDeep");
|
|
2258
|
-
try {
|
|
2259
|
-
return JSON.stringify(sortDeep(projected));
|
|
2260
|
-
} catch {
|
|
2261
|
-
return "{}";
|
|
2262
|
-
}
|
|
2263
|
-
}
|
|
2264
|
-
__name(canonicalizeAgentOpts, "canonicalizeAgentOpts");
|
|
2265
|
-
function deriveAgentKey(prefixHash, prompt, opts) {
|
|
2266
|
-
const hash = createHash("sha256");
|
|
2267
|
-
hash.update(prefixHash);
|
|
2268
|
-
hash.update("\0");
|
|
2269
|
-
hash.update(prompt);
|
|
2270
|
-
hash.update("\0");
|
|
2271
|
-
hash.update(canonicalizeAgentOpts(opts));
|
|
2272
|
-
return `${JOURNAL_KEY_VERSION}:${hash.digest("hex")}`;
|
|
2273
|
-
}
|
|
2274
|
-
__name(deriveAgentKey, "deriveAgentKey");
|
|
2275
|
-
function deriveArgsSeed(args) {
|
|
2276
|
-
const hash = createHash("sha256");
|
|
2277
|
-
let serialized;
|
|
2278
|
-
try {
|
|
2279
|
-
serialized = JSON.stringify(args ?? null) ?? "null";
|
|
2280
|
-
} catch {
|
|
2281
|
-
serialized = "non-serializable-args";
|
|
2282
|
-
}
|
|
2283
|
-
hash.update(serialized);
|
|
2284
|
-
return `${JOURNAL_KEY_VERSION}:${hash.digest("hex")}`;
|
|
2285
|
-
}
|
|
2286
|
-
__name(deriveArgsSeed, "deriveArgsSeed");
|
|
2287
|
-
function buildReplay(entries) {
|
|
2288
|
-
const results = /* @__PURE__ */ new Map();
|
|
2289
|
-
const started = /* @__PURE__ */ new Map();
|
|
2290
|
-
const failed = /* @__PURE__ */ new Set();
|
|
2291
|
-
for (const e of entries) {
|
|
2292
|
-
if (e.type === "result") {
|
|
2293
|
-
results.set(e.key, e);
|
|
2294
|
-
} else if (e.type === "started") {
|
|
2295
|
-
failed.delete(e.key);
|
|
2296
|
-
const list = started.get(e.key);
|
|
2297
|
-
if (list) list.push(e);
|
|
2298
|
-
else started.set(e.key, [e]);
|
|
2299
|
-
} else if (e.type === "failed") {
|
|
2300
|
-
failed.add(e.key);
|
|
2301
|
-
}
|
|
2302
|
-
}
|
|
2303
|
-
return { results, started, failed };
|
|
2304
|
-
}
|
|
2305
|
-
__name(buildReplay, "buildReplay");
|
|
2306
|
-
var WorkflowJournal = class {
|
|
2307
|
-
constructor(journalPath, root = path2.dirname(path2.dirname(journalPath))) {
|
|
2308
|
-
this.root = root;
|
|
2309
|
-
this.path = journalPath;
|
|
2310
|
-
}
|
|
2311
|
-
static {
|
|
2312
|
-
__name(this, "WorkflowJournal");
|
|
2313
|
-
}
|
|
2314
|
-
pending = Promise.resolve();
|
|
2315
|
-
path;
|
|
2316
|
-
async hasSymlinkedPath() {
|
|
2317
|
-
if (await isSymlinkedRoot(this.root) || await isSymlinkedRoot(path2.dirname(this.path))) {
|
|
2318
|
-
return true;
|
|
2319
|
-
}
|
|
2320
|
-
return fs2.lstat(this.path).then((stat) => stat.isSymbolicLink()).catch(() => false);
|
|
2321
|
-
}
|
|
2322
|
-
/** Ensure the advertised journal path exists without affecting the run. */
|
|
2323
|
-
async ensureExists() {
|
|
2324
|
-
try {
|
|
2325
|
-
if (await this.hasSymlinkedPath()) return false;
|
|
2326
|
-
await fs2.mkdir(path2.dirname(this.path), { recursive: true });
|
|
2327
|
-
if (await this.hasSymlinkedPath()) return false;
|
|
2328
|
-
const noFollow = process.platform === "win32" ? 0 : constants.O_NOFOLLOW;
|
|
2329
|
-
const file = await fs2.open(
|
|
2330
|
-
this.path,
|
|
2331
|
-
constants.O_APPEND | constants.O_CREAT | constants.O_WRONLY | noFollow,
|
|
2332
|
-
384
|
|
2333
|
-
);
|
|
2334
|
-
try {
|
|
2335
|
-
await file.chmod(384);
|
|
2336
|
-
} finally {
|
|
2337
|
-
await file.close();
|
|
2338
|
-
}
|
|
2339
|
-
return true;
|
|
2340
|
-
} catch (error) {
|
|
2341
|
-
debugLogger5.warn(
|
|
2342
|
-
`WorkflowJournal.ensureExists failed for ${this.path}: ${error}`
|
|
2343
|
-
);
|
|
2344
|
-
return false;
|
|
2345
|
-
}
|
|
2346
|
-
}
|
|
2347
|
-
/** Remove a never-registered run's journal file, best-effort. */
|
|
2348
|
-
async remove() {
|
|
2349
|
-
try {
|
|
2350
|
-
if (await this.hasSymlinkedPath()) return;
|
|
2351
|
-
await fs2.rm(this.path, { force: true });
|
|
2352
|
-
await fs2.rmdir(path2.dirname(this.path)).catch((error) => {
|
|
2353
|
-
const code = error.code;
|
|
2354
|
-
if (code !== "ENOENT" && code !== "ENOTEMPTY") throw error;
|
|
2355
|
-
});
|
|
2356
|
-
} catch (error) {
|
|
2357
|
-
debugLogger5.warn(
|
|
2358
|
-
`WorkflowJournal.remove failed for ${this.path}: ${error}`
|
|
2359
|
-
);
|
|
2360
|
-
}
|
|
2361
|
-
}
|
|
2362
|
-
/** Load + parse all entries into replay maps. Empty maps if no file. */
|
|
2363
|
-
async load() {
|
|
2364
|
-
try {
|
|
2365
|
-
const entries = await read(this.path);
|
|
2366
|
-
return buildReplay(entries);
|
|
2367
|
-
} catch (e) {
|
|
2368
|
-
debugLogger5.warn(`WorkflowJournal.load failed for ${this.path}: ${e}`);
|
|
2369
|
-
return { results: /* @__PURE__ */ new Map(), started: /* @__PURE__ */ new Map(), failed: /* @__PURE__ */ new Set() };
|
|
2370
|
-
}
|
|
2371
|
-
}
|
|
2372
|
-
/** Append one entry. Rejects only on I/O error (callers `.catch`). */
|
|
2373
|
-
append(entry) {
|
|
2374
|
-
const operation = this.pending.then(() => writeLine(this.path, entry));
|
|
2375
|
-
this.pending = operation.catch(() => void 0);
|
|
2376
|
-
return operation;
|
|
2377
|
-
}
|
|
2378
|
-
/** Wait until every append issued so far has settled. */
|
|
2379
|
-
drain() {
|
|
2380
|
-
return this.pending;
|
|
2381
|
-
}
|
|
2382
|
-
};
|
|
2383
|
-
|
|
2384
|
-
// packages/core/src/agents/runtime/workflow-prompts.ts
|
|
2385
|
-
init_esbuild_shims();
|
|
2386
|
-
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.';
|
|
2387
|
-
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.";
|
|
2388
|
-
|
|
2389
|
-
// packages/core/src/agents/runtime/workflow-dispatch-scheduler.ts
|
|
2390
|
-
init_esbuild_shims();
|
|
2391
|
-
function abortError() {
|
|
2392
|
-
return new DOMException("Workflow dispatch scheduler aborted.", "AbortError");
|
|
2393
|
-
}
|
|
2394
|
-
__name(abortError, "abortError");
|
|
2395
|
-
var WorkflowDispatchScheduler = class {
|
|
2396
|
-
constructor(limit, signal, onStateChange) {
|
|
2397
|
-
this.limit = limit;
|
|
2398
|
-
this.signal = signal;
|
|
2399
|
-
if (!Number.isInteger(limit) || limit < 1) {
|
|
2400
|
-
throw new Error(
|
|
2401
|
-
`Workflow dispatch limit must be a positive integer, got ${String(limit)}.`
|
|
2402
|
-
);
|
|
2403
|
-
}
|
|
2404
|
-
if (onStateChange) this.stateListeners.add(onStateChange);
|
|
2405
|
-
if (signal && !signal.aborted) {
|
|
2406
|
-
signal.addEventListener("abort", () => this.abortPending(), {
|
|
2407
|
-
once: true
|
|
2408
|
-
});
|
|
2409
|
-
}
|
|
2410
|
-
}
|
|
2411
|
-
static {
|
|
2412
|
-
__name(this, "WorkflowDispatchScheduler");
|
|
2413
|
-
}
|
|
2414
|
-
state = "running";
|
|
2415
|
-
inFlight = 0;
|
|
2416
|
-
queue = [];
|
|
2417
|
-
gateWaiters = [];
|
|
2418
|
-
stateListeners = /* @__PURE__ */ new Set();
|
|
2419
|
-
/**
|
|
2420
|
-
* Subscribe to state transitions. Returns an unsubscribe function.
|
|
2421
|
-
* The constructor callback (when given) is registered as the first
|
|
2422
|
-
* listener; this method lets additional observers — e.g. the sandbox's
|
|
2423
|
-
* pause-aware wall-clock watchdog — hook the same transitions.
|
|
2424
|
-
*/
|
|
2425
|
-
onStateChange(listener) {
|
|
2426
|
-
this.stateListeners.add(listener);
|
|
2427
|
-
return () => {
|
|
2428
|
-
this.stateListeners.delete(listener);
|
|
2429
|
-
};
|
|
2430
|
-
}
|
|
2431
|
-
run(thunk) {
|
|
2432
|
-
return new Promise((resolve2, reject) => {
|
|
2433
|
-
if (this.signal?.aborted) {
|
|
2434
|
-
reject(abortError());
|
|
2435
|
-
return;
|
|
2436
|
-
}
|
|
2437
|
-
this.queue.push({
|
|
2438
|
-
thunk,
|
|
2439
|
-
resolve: resolve2,
|
|
2440
|
-
reject
|
|
2441
|
-
});
|
|
2442
|
-
this.pump();
|
|
2443
|
-
});
|
|
2444
|
-
}
|
|
2445
|
-
pause() {
|
|
2446
|
-
if (this.state !== "running" || this.signal?.aborted) return false;
|
|
2447
|
-
this.setState("pausing");
|
|
2448
|
-
if (this.inFlight === 0) this.setState("paused");
|
|
2449
|
-
return true;
|
|
2450
|
-
}
|
|
2451
|
-
resume() {
|
|
2452
|
-
if (this.state !== "paused" || this.signal?.aborted) return false;
|
|
2453
|
-
this.setState("running");
|
|
2454
|
-
while (this.gateWaiters.length > 0) {
|
|
2455
|
-
this.gateWaiters.shift().resolve();
|
|
2456
|
-
}
|
|
2457
|
-
this.pump();
|
|
2458
|
-
return true;
|
|
2459
|
-
}
|
|
2460
|
-
waitUntilRunning() {
|
|
2461
|
-
if (this.signal?.aborted) return Promise.reject(abortError());
|
|
2462
|
-
if (this.state === "running") return Promise.resolve();
|
|
2463
|
-
return new Promise((resolve2, reject) => {
|
|
2464
|
-
this.gateWaiters.push({ resolve: resolve2, reject });
|
|
2465
|
-
});
|
|
2466
|
-
}
|
|
2467
|
-
snapshot() {
|
|
2468
|
-
return {
|
|
2469
|
-
state: this.state,
|
|
2470
|
-
queued: this.queue.length,
|
|
2471
|
-
inFlight: this.inFlight
|
|
2472
|
-
};
|
|
2473
|
-
}
|
|
2474
|
-
pump() {
|
|
2475
|
-
while (this.state === "running" && this.inFlight < this.limit && this.queue.length > 0) {
|
|
2476
|
-
const job = this.queue.shift();
|
|
2477
|
-
if (this.signal?.aborted) {
|
|
2478
|
-
job.reject(abortError());
|
|
2479
|
-
continue;
|
|
2480
|
-
}
|
|
2481
|
-
this.inFlight++;
|
|
2482
|
-
Promise.resolve().then(job.thunk).then(job.resolve, job.reject).finally(() => {
|
|
2483
|
-
this.inFlight--;
|
|
2484
|
-
if (this.state === "pausing" && this.inFlight === 0) {
|
|
2485
|
-
this.setState("paused");
|
|
2486
|
-
}
|
|
2487
|
-
this.pump();
|
|
2488
|
-
});
|
|
2489
|
-
}
|
|
2490
|
-
}
|
|
2491
|
-
setState(state) {
|
|
2492
|
-
if (this.state === state) return;
|
|
2493
|
-
this.state = state;
|
|
2494
|
-
const snapshot = this.snapshot();
|
|
2495
|
-
for (const listener of [...this.stateListeners]) listener(snapshot);
|
|
2496
|
-
}
|
|
2497
|
-
abortPending() {
|
|
2498
|
-
const error = abortError();
|
|
2499
|
-
while (this.queue.length > 0) this.queue.shift().reject(error);
|
|
2500
|
-
while (this.gateWaiters.length > 0) {
|
|
2501
|
-
this.gateWaiters.shift().reject(error);
|
|
2502
|
-
}
|
|
2503
|
-
}
|
|
2504
|
-
};
|
|
2505
|
-
|
|
2506
|
-
// packages/core/src/agents/runtime/workflow-orchestrator.ts
|
|
2507
|
-
var DEFAULT_MAX_AGENTS_PER_RUN = 1e3;
|
|
2508
|
-
var MAX_WORKFLOW_AGENTS_ENV = "QWEN_CODE_MAX_WORKFLOW_AGENTS";
|
|
2509
|
-
var HARD_MAX_AGENTS_PER_RUN_CEILING = 1e4;
|
|
2510
|
-
function resolveMaxAgentsPerRun(env = process.env) {
|
|
2511
|
-
const raw = env[MAX_WORKFLOW_AGENTS_ENV];
|
|
2512
|
-
if (raw === void 0 || raw.trim() === "") {
|
|
2513
|
-
return DEFAULT_MAX_AGENTS_PER_RUN;
|
|
2514
|
-
}
|
|
2515
|
-
const parsed = parsePositiveIntegerEnv(raw, 0);
|
|
2516
|
-
if (parsed < 1) {
|
|
2517
|
-
debugLogger.warn(
|
|
2518
|
-
`Invalid ${MAX_WORKFLOW_AGENTS_ENV}=${JSON.stringify(raw)}, using default (${DEFAULT_MAX_AGENTS_PER_RUN})`
|
|
2519
|
-
);
|
|
2520
|
-
return DEFAULT_MAX_AGENTS_PER_RUN;
|
|
2521
|
-
}
|
|
2522
|
-
if (parsed > HARD_MAX_AGENTS_PER_RUN_CEILING) {
|
|
2523
|
-
debugLogger.warn(
|
|
2524
|
-
`${MAX_WORKFLOW_AGENTS_ENV}=${parsed} exceeds hard ceiling (${HARD_MAX_AGENTS_PER_RUN_CEILING}); clamping.`
|
|
2525
|
-
);
|
|
2526
|
-
return HARD_MAX_AGENTS_PER_RUN_CEILING;
|
|
2527
|
-
}
|
|
2528
|
-
return parsed;
|
|
2529
|
-
}
|
|
2530
|
-
__name(resolveMaxAgentsPerRun, "resolveMaxAgentsPerRun");
|
|
2531
|
-
var MAX_WORKFLOW_CONCURRENCY_ENV = "QWEN_CODE_MAX_WORKFLOW_CONCURRENCY";
|
|
2532
|
-
var HARD_MAX_CONCURRENCY_CEILING = 64;
|
|
2533
|
-
function resolveConcurrencyLimit(env = process.env, availableParallelism2 = os.availableParallelism) {
|
|
2534
|
-
const raw = env[MAX_WORKFLOW_CONCURRENCY_ENV];
|
|
2535
|
-
if (raw !== void 0 && raw.trim() !== "") {
|
|
2536
|
-
const parsed = parsePositiveIntegerEnv(raw, 0);
|
|
2537
|
-
if (parsed >= 1) {
|
|
2538
|
-
if (parsed > HARD_MAX_CONCURRENCY_CEILING) {
|
|
2539
|
-
debugLogger.warn(
|
|
2540
|
-
`${MAX_WORKFLOW_CONCURRENCY_ENV}=${parsed} exceeds hard ceiling (${HARD_MAX_CONCURRENCY_CEILING}); clamping.`
|
|
2541
|
-
);
|
|
2542
|
-
return HARD_MAX_CONCURRENCY_CEILING;
|
|
2543
|
-
}
|
|
2544
|
-
return parsed;
|
|
2545
|
-
}
|
|
2546
|
-
debugLogger.warn(
|
|
2547
|
-
`Invalid ${MAX_WORKFLOW_CONCURRENCY_ENV}=${JSON.stringify(raw)}, using cpu-derived default`
|
|
2548
|
-
);
|
|
2549
|
-
}
|
|
2550
|
-
return Math.max(2, Math.min(16, availableParallelism2() - 2));
|
|
2551
|
-
}
|
|
2552
|
-
__name(resolveConcurrencyLimit, "resolveConcurrencyLimit");
|
|
2553
|
-
var DEFAULT_WORKFLOW_SUBAGENT_MAX_TURNS = 50;
|
|
2554
|
-
var DEFAULT_WORKFLOW_SUBAGENT_MAX_TIME_MINUTES = 10;
|
|
2555
|
-
var WORKFLOW_SUBAGENT_MAX_TURNS_ENV = "QWEN_CODE_WORKFLOW_AGENT_MAX_TURNS";
|
|
2556
|
-
var WORKFLOW_SUBAGENT_MAX_MINUTES_ENV = "QWEN_CODE_WORKFLOW_AGENT_MAX_MINUTES";
|
|
2557
|
-
var HARD_WORKFLOW_SUBAGENT_MAX_TURNS_CEILING = 500;
|
|
2558
|
-
var HARD_WORKFLOW_SUBAGENT_MAX_MINUTES_CEILING = 100;
|
|
2559
|
-
function resolveSubagentBound(envName, defaultValue, ceiling, env) {
|
|
2560
|
-
const raw = env[envName];
|
|
2561
|
-
if (raw === void 0 || raw.trim() === "") return defaultValue;
|
|
2562
|
-
const parsed = parsePositiveIntegerEnv(raw, 0);
|
|
2563
|
-
if (parsed < 1) {
|
|
2564
|
-
debugLogger.warn(
|
|
2565
|
-
`Invalid ${envName}=${JSON.stringify(raw)}, using default (${defaultValue})`
|
|
2566
|
-
);
|
|
2567
|
-
return defaultValue;
|
|
2568
|
-
}
|
|
2569
|
-
if (parsed > ceiling) {
|
|
2570
|
-
debugLogger.warn(
|
|
2571
|
-
`${envName}=${parsed} exceeds hard ceiling (${ceiling}); clamping.`
|
|
2572
|
-
);
|
|
2573
|
-
return ceiling;
|
|
2574
|
-
}
|
|
2575
|
-
return parsed;
|
|
2576
|
-
}
|
|
2577
|
-
__name(resolveSubagentBound, "resolveSubagentBound");
|
|
2578
|
-
function resolveSubagentMaxTurns(env = process.env, defaultValue = DEFAULT_WORKFLOW_SUBAGENT_MAX_TURNS) {
|
|
2579
|
-
return resolveSubagentBound(
|
|
2580
|
-
WORKFLOW_SUBAGENT_MAX_TURNS_ENV,
|
|
2581
|
-
defaultValue,
|
|
2582
|
-
HARD_WORKFLOW_SUBAGENT_MAX_TURNS_CEILING,
|
|
2583
|
-
env
|
|
2584
|
-
);
|
|
2585
|
-
}
|
|
2586
|
-
__name(resolveSubagentMaxTurns, "resolveSubagentMaxTurns");
|
|
2587
|
-
function resolveSubagentMaxTimeMinutes(env = process.env, defaultValue = DEFAULT_WORKFLOW_SUBAGENT_MAX_TIME_MINUTES) {
|
|
2588
|
-
return resolveSubagentBound(
|
|
2589
|
-
WORKFLOW_SUBAGENT_MAX_MINUTES_ENV,
|
|
2590
|
-
defaultValue,
|
|
2591
|
-
HARD_WORKFLOW_SUBAGENT_MAX_MINUTES_CEILING,
|
|
2592
|
-
env
|
|
2593
|
-
);
|
|
2594
|
-
}
|
|
2595
|
-
__name(resolveSubagentMaxTimeMinutes, "resolveSubagentMaxTimeMinutes");
|
|
2596
|
-
var WORKFLOW_SUBAGENT_DISALLOWED_TOOLS = [
|
|
2597
|
-
ToolNames.ASK_USER_QUESTION,
|
|
2598
|
-
ToolNames.SEND_MESSAGE,
|
|
2599
|
-
ToolNames.MONITOR,
|
|
2600
|
-
...SUBAGENT_PLAN_LIFECYCLE_TOOLS,
|
|
2601
|
-
// AgentTool: workflow subagents must not spawn sub-agents even where
|
|
2602
|
-
// maxSubagentDepth would permit nesting — a leaf-spawned agent would run
|
|
2603
|
-
// outside the orchestrator's concurrency cap, agent counter, and token
|
|
2604
|
-
// budget, and its result would bypass the script's return-value contract.
|
|
2605
|
-
// The WorkflowTool itself is already excluded from every subagent; this
|
|
2606
|
-
// closes the same loop for plain `agent` calls (review #6189).
|
|
2607
|
-
ToolNames.AGENT
|
|
2608
|
-
];
|
|
2609
|
-
var WorkflowExecutionError = class extends Error {
|
|
2610
|
-
constructor(message, phases, logs, meta = null) {
|
|
2611
|
-
super(message);
|
|
2612
|
-
this.phases = phases;
|
|
2613
|
-
this.logs = logs;
|
|
2614
|
-
this.meta = meta;
|
|
2615
|
-
}
|
|
2616
|
-
static {
|
|
2617
|
-
__name(this, "WorkflowExecutionError");
|
|
2618
|
-
}
|
|
2619
|
-
name = "WorkflowExecutionError";
|
|
2620
|
-
};
|
|
2621
|
-
function generateRunId() {
|
|
2622
|
-
return `wf_${randomBytes(8).toString("hex")}`;
|
|
2623
|
-
}
|
|
2624
|
-
__name(generateRunId, "generateRunId");
|
|
2625
|
-
function sanitizeForErrorMessage(value) {
|
|
2626
|
-
return stripAnsiAndControl(value);
|
|
2627
|
-
}
|
|
2628
|
-
__name(sanitizeForErrorMessage, "sanitizeForErrorMessage");
|
|
2629
|
-
function createProductionDispatch(config, signal, onTokens, bridgeApprovalEvents, subagentBounds) {
|
|
2630
|
-
return async (prompt, opts, dispatchId) => {
|
|
2631
|
-
if (typeof prompt !== "string" || prompt.length === 0) {
|
|
2632
|
-
throw new Error("agent() requires a non-empty string prompt.");
|
|
2633
|
-
}
|
|
2634
|
-
const stallMs = resolveStallMs(
|
|
2635
|
-
typeof opts.stallMs === "number" ? opts.stallMs : void 0
|
|
2636
|
-
);
|
|
2637
|
-
const workflowAgentId = `workflow-agent-${randomBytes(8).toString("hex")}`;
|
|
2638
|
-
const agentIdentity = await resolveWorkflowAgentIdentity(config, opts);
|
|
2639
|
-
if (agentIdentity.resolvedAgentType?.executor !== void 0) {
|
|
2640
|
-
throw new Error(
|
|
2641
|
-
"Workflow agent() does not support external-executor agents: token budgets, schema output, and workflow tool restrictions cannot be enforced. Use an in-process agent definition instead."
|
|
2642
|
-
);
|
|
2643
|
-
}
|
|
2644
|
-
let attempt = 0;
|
|
2645
|
-
return runStallResilient(
|
|
2646
|
-
async (attemptSignal, emitter) => {
|
|
2647
|
-
attempt += 1;
|
|
2648
|
-
const cleanupApprovalBridge = bridgeApprovalEvents?.(
|
|
2649
|
-
emitter,
|
|
2650
|
-
dispatchId
|
|
2651
|
-
);
|
|
2652
|
-
const cleanupTranscript = attachDispatchTranscript(
|
|
2653
|
-
config,
|
|
2654
|
-
workflowAgentId,
|
|
2655
|
-
prompt,
|
|
2656
|
-
agentIdentity.name,
|
|
2657
|
-
emitter,
|
|
2658
|
-
attempt
|
|
2659
|
-
);
|
|
2660
|
-
try {
|
|
2661
|
-
return await runSingleDispatch(
|
|
2662
|
-
config,
|
|
2663
|
-
prompt,
|
|
2664
|
-
opts,
|
|
2665
|
-
attemptSignal,
|
|
2666
|
-
emitter,
|
|
2667
|
-
workflowAgentId,
|
|
2668
|
-
agentIdentity,
|
|
2669
|
-
onTokens,
|
|
2670
|
-
subagentBounds
|
|
2671
|
-
);
|
|
2672
|
-
} finally {
|
|
2673
|
-
cleanupTranscript();
|
|
2674
|
-
cleanupApprovalBridge?.();
|
|
2675
|
-
}
|
|
2676
|
-
},
|
|
2677
|
-
{
|
|
2678
|
-
stallMs,
|
|
2679
|
-
signal,
|
|
2680
|
-
// The name can carry a model-authored agentType spelling — keep
|
|
2681
|
-
// the stall log / abandoned error single-line the same way the
|
|
2682
|
-
// "not found" throw site sanitizes it.
|
|
2683
|
-
label: sanitizeForErrorMessage(agentIdentity.name)
|
|
2684
|
-
}
|
|
2685
|
-
);
|
|
2686
|
-
};
|
|
2687
|
-
}
|
|
2688
|
-
__name(createProductionDispatch, "createProductionDispatch");
|
|
2689
|
-
async function resolveWorkflowAgentIdentity(config, opts) {
|
|
2690
|
-
if (opts.agentType) {
|
|
2691
|
-
const resolved = await config.getSubagentManager().findSubagentByName(opts.agentType);
|
|
2692
|
-
if (resolved) {
|
|
2693
|
-
return { name: resolved.name, resolvedAgentType: resolved };
|
|
2694
|
-
}
|
|
2695
|
-
}
|
|
2696
|
-
if (typeof opts.label === "string" && opts.label) {
|
|
2697
|
-
return { name: opts.label };
|
|
2698
|
-
}
|
|
2699
|
-
return { name: opts.agentType || "workflow-agent" };
|
|
2700
|
-
}
|
|
2701
|
-
__name(resolveWorkflowAgentIdentity, "resolveWorkflowAgentIdentity");
|
|
2702
|
-
function attachDispatchTranscript(config, agentId, prompt, agentName, emitter, attempt) {
|
|
2703
|
-
const append = attempt > 1;
|
|
2704
|
-
try {
|
|
2705
|
-
const { jsonlPath, options } = buildAgentTranscriptAttach(config, agentId, {
|
|
2706
|
-
agentName,
|
|
2707
|
-
// The prompt the SCRIPT dispatched, seeded as the transcript's first
|
|
2708
|
-
// user record — the same shape AgentTool writes, so a reader that
|
|
2709
|
-
// recovers a launch prompt from a transcript needs no workflow-
|
|
2710
|
-
// specific branch. A retry re-uses the first attempt's record rather
|
|
2711
|
-
// than seeding a second one.
|
|
2712
|
-
initialUserPrompt: append ? void 0 : prompt,
|
|
2713
|
-
appendToExisting: append,
|
|
2714
|
-
retryAttempt: append ? attempt : void 0
|
|
2715
|
-
});
|
|
2716
|
-
return attachJsonlTranscriptWriter(emitter, jsonlPath, options).cleanup;
|
|
2717
|
-
} catch (error) {
|
|
2718
|
-
debugLogger.warn(
|
|
2719
|
-
`[Workflow] failed to attach transcript for ${agentId}: ${error}`
|
|
2720
|
-
);
|
|
2721
|
-
return () => {
|
|
2722
|
-
};
|
|
2723
|
-
}
|
|
2724
|
-
}
|
|
2725
|
-
__name(attachDispatchTranscript, "attachDispatchTranscript");
|
|
2726
|
-
function terminalDispatchError(workflowAgentId, mode) {
|
|
2727
|
-
const message = `Workflow subagent ${workflowAgentId} did not complete (terminate mode: ${mode}).`;
|
|
2728
|
-
switch (mode) {
|
|
2729
|
-
case "MAX_TURNS" /* MAX_TURNS */:
|
|
2730
|
-
return new WorkflowAgentFailedError(message, "max_turns", mode);
|
|
2731
|
-
case "TIMEOUT" /* TIMEOUT */:
|
|
2732
|
-
return new WorkflowAgentFailedError(message, "timeout", mode);
|
|
2733
|
-
case "ERROR" /* ERROR */:
|
|
2734
|
-
return new WorkflowAgentFailedError(message, "error", mode);
|
|
2735
|
-
default:
|
|
2736
|
-
return new Error(message);
|
|
2737
|
-
}
|
|
2738
|
-
}
|
|
2739
|
-
__name(terminalDispatchError, "terminalDispatchError");
|
|
2740
|
-
async function runSingleDispatch(config, prompt, opts, attemptSignal, emitter, workflowAgentId, agentIdentity, onTokens, subagentBounds) {
|
|
2741
|
-
const { AgentHeadless, ContextState } = await import("./agent-headless-7D5ZC736.js");
|
|
2742
|
-
const ctx = new ContextState();
|
|
2743
|
-
ctx.set("task_prompt", prompt);
|
|
2744
|
-
debugLogger.debug(`[workflow] Dispatch ${workflowAgentId}`);
|
|
2745
|
-
if (DISPATCH_AFFECTING_AGENT_OPTS.every((key) => opts[key] === void 0)) {
|
|
2746
|
-
const subagent = await AgentHeadless.create(
|
|
2747
|
-
agentIdentity.name,
|
|
2748
|
-
config,
|
|
2749
|
-
{
|
|
2750
|
-
systemPrompt: WORKFLOW_SUBAGENT_SYSTEM_PROMPT
|
|
2751
|
-
},
|
|
2752
|
-
{},
|
|
2753
|
-
// T11 (PR #4732 R1): bound resource ceiling so a single agent() call
|
|
2754
|
-
// cannot loop the model indefinitely. Without this, runConfig was {}
|
|
2755
|
-
// and the loop guards never tripped — combined with the cancellation
|
|
2756
|
-
// bug below, workflows were effectively unkillable.
|
|
2757
|
-
subagentBounds ?? {
|
|
2758
|
-
max_turns: resolveSubagentMaxTurns(),
|
|
2759
|
-
max_time_minutes: resolveSubagentMaxTimeMinutes()
|
|
2760
|
-
},
|
|
2761
|
-
// T11 (PR #4732 R1): disallow SendMessage / ExitPlanMode to align with
|
|
2762
|
-
// upstream Tg8 — closes the back-channel that would let a subagent
|
|
2763
|
-
// deliver its answer via user message instead of the script's read.
|
|
2764
|
-
{ tools: ["*"], disallowedTools: WORKFLOW_SUBAGENT_DISALLOWED_TOOLS },
|
|
2765
|
-
// P-stall: the stall-watchdog emitter observes reasoning-loop events
|
|
2766
|
-
// (round/tool/usage) to detect a hang and abort `attemptSignal`.
|
|
2767
|
-
emitter,
|
|
2768
|
-
void 0,
|
|
2769
|
-
void 0,
|
|
2770
|
-
prompt,
|
|
2771
|
-
workflowAgentId
|
|
2772
|
-
);
|
|
2773
|
-
try {
|
|
2774
|
-
await runWithAgentContext(
|
|
2775
|
-
workflowAgentId,
|
|
2776
|
-
() => subagent.execute(ctx, attemptSignal)
|
|
2777
|
-
);
|
|
2778
|
-
} finally {
|
|
2779
|
-
reportTokens(subagent, opts, onTokens);
|
|
2780
|
-
}
|
|
2781
|
-
const mode = subagent.getTerminateMode();
|
|
2782
|
-
if (mode !== "GOAL" /* GOAL */) {
|
|
2783
|
-
throw terminalDispatchError(workflowAgentId, mode);
|
|
2784
|
-
}
|
|
2785
|
-
return toModelVisibleSubagentResult(subagent.getFinalText(), mode);
|
|
2786
|
-
}
|
|
2787
|
-
return runOverridePath(
|
|
2788
|
-
config,
|
|
2789
|
-
ctx,
|
|
2790
|
-
opts,
|
|
2791
|
-
attemptSignal,
|
|
2792
|
-
workflowAgentId,
|
|
2793
|
-
agentIdentity,
|
|
2794
|
-
onTokens,
|
|
2795
|
-
emitter,
|
|
2796
|
-
subagentBounds
|
|
2797
|
-
);
|
|
2798
|
-
}
|
|
2799
|
-
__name(runSingleDispatch, "runSingleDispatch");
|
|
2800
|
-
function reportTokens(subagent, opts, onTokens) {
|
|
2801
|
-
if (!onTokens) return;
|
|
2802
|
-
try {
|
|
2803
|
-
const summary = subagent.getExecutionSummary();
|
|
2804
|
-
onTokens(summary.outputTokens, opts);
|
|
2805
|
-
} catch (e) {
|
|
2806
|
-
debugLogger.warn("onTokens callback threw:", e);
|
|
2807
|
-
}
|
|
2808
|
-
}
|
|
2809
|
-
__name(reportTokens, "reportTokens");
|
|
2810
|
-
function resolveDispatchEffort(raw) {
|
|
2811
|
-
if (raw === void 0) return void 0;
|
|
2812
|
-
const tier = typeof raw === "string" ? normalizeReasoningEffort(raw) : void 0;
|
|
2813
|
-
if (tier === void 0) {
|
|
2814
|
-
throw new Error(
|
|
2815
|
-
`agent({effort}): unknown effort tier ${sanitizeForErrorMessage(
|
|
2816
|
-
JSON.stringify(raw) ?? String(raw)
|
|
2817
|
-
)}. Known tiers are: ${REASONING_EFFORT_TIERS.join(", ")}.`
|
|
2818
|
-
);
|
|
2819
|
-
}
|
|
2820
|
-
return tier;
|
|
2821
|
-
}
|
|
2822
|
-
__name(resolveDispatchEffort, "resolveDispatchEffort");
|
|
2823
|
-
function resolveDispatchDenies(raw) {
|
|
2824
|
-
if (raw === void 0) return [];
|
|
2825
|
-
if (!Array.isArray(raw) || raw.some(
|
|
2826
|
-
(name) => typeof name !== "string" || name.length === 0 || name !== name.trim()
|
|
2827
|
-
)) {
|
|
2828
|
-
throw new Error(
|
|
2829
|
-
"agent({disallowedTools}): must be an array of non-empty tool-name strings without surrounding whitespace, e.g. ['run_shell_command', 'write_file']."
|
|
2830
|
-
);
|
|
2831
|
-
}
|
|
2832
|
-
return raw.map((name) => resolveBuiltinToolName(name) ?? name);
|
|
2833
|
-
}
|
|
2834
|
-
__name(resolveDispatchDenies, "resolveDispatchDenies");
|
|
2835
|
-
async function runOverridePath(config, ctx, opts, signal, workflowAgentId, agentIdentity, onTokens, emitter, subagentBounds) {
|
|
2836
|
-
if (opts.isolation === "remote") {
|
|
2837
|
-
throw new Error(
|
|
2838
|
-
"agent({isolation:'remote'}) is not available in this build."
|
|
2839
|
-
);
|
|
2840
|
-
}
|
|
2841
|
-
const effort = resolveDispatchEffort(opts.effort);
|
|
2842
|
-
const requestedDenies = resolveDispatchDenies(opts.disallowedTools);
|
|
2843
|
-
const subagentMgr = config.getSubagentManager();
|
|
2844
|
-
let baseConfig;
|
|
2845
|
-
if (opts.agentType !== void 0) {
|
|
2846
|
-
const resolved = agentIdentity.resolvedAgentType;
|
|
2847
|
-
if (!resolved) {
|
|
2848
|
-
const safeAgentType = sanitizeForErrorMessage(opts.agentType);
|
|
2849
|
-
throw new Error(
|
|
2850
|
-
`agent({agentType}): agent type '${safeAgentType}' not found.`
|
|
2851
|
-
);
|
|
2852
|
-
}
|
|
2853
|
-
baseConfig = resolved;
|
|
2854
|
-
} else {
|
|
2855
|
-
baseConfig = {
|
|
2856
|
-
name: agentIdentity.name,
|
|
2857
|
-
description: "Default workflow subagent (per-call overrides).",
|
|
2858
|
-
systemPrompt: WORKFLOW_SUBAGENT_SYSTEM_PROMPT,
|
|
2859
|
-
level: "session",
|
|
2860
|
-
isBuiltin: false
|
|
2861
|
-
};
|
|
2862
|
-
}
|
|
2863
|
-
let schemaSystemPrompt;
|
|
2864
|
-
if (opts.schema !== void 0) {
|
|
2865
|
-
schemaSystemPrompt = opts.agentType !== void 0 && baseConfig.systemPrompt ? `${baseConfig.systemPrompt}
|
|
2866
|
-
|
|
2867
|
-
${WORKFLOW_SUBAGENT_SYSTEM_PROMPT_WITH_SCHEMA}` : WORKFLOW_SUBAGENT_SYSTEM_PROMPT_WITH_SCHEMA;
|
|
2868
|
-
}
|
|
2869
|
-
let schemaTools;
|
|
2870
|
-
if (opts.schema !== void 0 && baseConfig.tools && baseConfig.tools.length > 0 && !baseConfig.tools.includes("*") && !baseConfig.tools.includes(ToolNames.STRUCTURED_OUTPUT)) {
|
|
2871
|
-
schemaTools = [...baseConfig.tools, ToolNames.STRUCTURED_OUTPUT];
|
|
2872
|
-
}
|
|
2873
|
-
const augmented = {
|
|
2874
|
-
...baseConfig,
|
|
2875
|
-
...opts.model !== void 0 ? { model: opts.model } : {},
|
|
2876
|
-
...schemaSystemPrompt !== void 0 ? { systemPrompt: schemaSystemPrompt } : {},
|
|
2877
|
-
...schemaTools !== void 0 ? { tools: schemaTools } : {},
|
|
2878
|
-
disallowedTools: Array.from(
|
|
2879
|
-
/* @__PURE__ */ new Set([
|
|
2880
|
-
...baseConfig.disallowedTools ?? [],
|
|
2881
|
-
...WORKFLOW_SUBAGENT_DISALLOWED_TOOLS,
|
|
2882
|
-
...requestedDenies
|
|
2883
|
-
])
|
|
2884
|
-
)
|
|
2885
|
-
};
|
|
2886
|
-
if (requestedDenies.length > 0) {
|
|
2887
|
-
const unmatched = await subagentMgr.findUnmatchedToolNames(requestedDenies);
|
|
2888
|
-
if (unmatched.length > 0) {
|
|
2889
|
-
throw new Error(
|
|
2890
|
-
`agent({disallowedTools}): ${sanitizeForErrorMessage(
|
|
2891
|
-
unmatched.map((name) => JSON.stringify(name)).join(", ")
|
|
2892
|
-
)} ${unmatched.length === 1 ? "matches" : "match"} no tool. Use a tool name (run_shell_command, write_file, edit), a display name (Shell, WriteFile, Edit), or an MCP pattern (mcp__<server>, mcp__<server>__*, mcp__<server>__<tool>).`
|
|
2893
|
-
);
|
|
2894
|
-
}
|
|
2895
|
-
}
|
|
2896
|
-
if (opts.schema !== void 0 && [...baseConfig.disallowedTools ?? [], ...requestedDenies].some(
|
|
2897
|
-
(name) => (resolveBuiltinToolName(name) ?? name) === ToolNames.STRUCTURED_OUTPUT
|
|
2898
|
-
)) {
|
|
2899
|
-
throw new Error(
|
|
2900
|
-
"agent({schema, disallowedTools}): schema mode needs the structured_output tool, but disallowedTools deny it (from this call or from the agent type)."
|
|
2901
|
-
);
|
|
2902
|
-
}
|
|
2903
|
-
let worktreeIsolation = null;
|
|
2904
|
-
let effectiveContext = config;
|
|
2905
|
-
let approvalCleanup;
|
|
2906
|
-
if (opts.isolation !== void 0 && opts.workingDir !== void 0) {
|
|
2907
|
-
throw new Error(
|
|
2908
|
-
"agent({workingDir, isolation}): incompatible options. workingDir pins the agent to a worktree you already own; isolation creates a fresh one and removes it afterwards. Pass one."
|
|
2909
|
-
);
|
|
2910
|
-
}
|
|
2911
|
-
if (opts.isolation === "worktree") {
|
|
2912
|
-
worktreeIsolation = await provisionWorkflowWorktree(config);
|
|
2913
|
-
effectiveContext = deriveWorktreeConfig(config, worktreeIsolation.path, {
|
|
2914
|
-
customIgnoreFiles: config.getFileFilteringOptions().customIgnoreFiles
|
|
2915
|
-
});
|
|
2916
|
-
installSessionWorkflowRevisionWriteThrough(effectiveContext, config);
|
|
2917
|
-
} else if (opts.workingDir !== void 0) {
|
|
2918
|
-
if (typeof opts.workingDir !== "string" || opts.workingDir.trim().length === 0) {
|
|
2919
|
-
throw new Error(
|
|
2920
|
-
"agent({workingDir}): must be a non-empty string naming an existing git worktree of this repository."
|
|
2921
|
-
);
|
|
2922
|
-
}
|
|
2923
|
-
const resolved = await resolveExternalWorktreeDir(
|
|
2924
|
-
config,
|
|
2925
|
-
opts.workingDir,
|
|
2926
|
-
"workingDir"
|
|
2927
|
-
);
|
|
2928
|
-
if ("error" in resolved) {
|
|
2929
|
-
throw new Error(
|
|
2930
|
-
`agent({workingDir: ${sanitizeForErrorMessage(JSON.stringify(opts.workingDir))}}): ${sanitizeForErrorMessage(resolved.error)}`
|
|
2931
|
-
);
|
|
2932
|
-
}
|
|
2933
|
-
effectiveContext = deriveWorktreeConfig(config, resolved.path, {
|
|
2934
|
-
customIgnoreFiles: config.getFileFilteringOptions().customIgnoreFiles
|
|
2935
|
-
});
|
|
2936
|
-
installSessionWorkflowRevisionWriteThrough(effectiveContext, config);
|
|
2937
|
-
}
|
|
2938
|
-
if (effectiveContext !== config) {
|
|
2939
|
-
const approvalHandle = deriveApprovalModeConfig(
|
|
2940
|
-
effectiveContext,
|
|
2941
|
-
config.getApprovalMode()
|
|
2942
|
-
);
|
|
2943
|
-
approvalCleanup = approvalHandle.cleanup;
|
|
2944
|
-
effectiveContext = approvalHandle.config;
|
|
2945
|
-
}
|
|
2946
|
-
let onParentAbort;
|
|
2947
|
-
try {
|
|
2948
|
-
let schemaState = null;
|
|
2949
|
-
if (opts.schema !== void 0) {
|
|
2950
|
-
effectiveContext = await createSchemaConfigOverride(
|
|
2951
|
-
effectiveContext,
|
|
2952
|
-
opts.schema
|
|
2953
|
-
);
|
|
2954
|
-
schemaState = createSchemaModeState();
|
|
2955
|
-
}
|
|
2956
|
-
let dispatchSignal = signal;
|
|
2957
|
-
if (schemaState) {
|
|
2958
|
-
const child = new AbortController();
|
|
2959
|
-
if (signal) {
|
|
2960
|
-
if (signal.aborted) {
|
|
2961
|
-
child.abort(signal.reason);
|
|
2962
|
-
} else {
|
|
2963
|
-
onParentAbort = /* @__PURE__ */ __name(() => child.abort(signal.reason), "onParentAbort");
|
|
2964
|
-
signal.addEventListener("abort", onParentAbort);
|
|
2965
|
-
}
|
|
2966
|
-
}
|
|
2967
|
-
schemaState.abortController = child;
|
|
2968
|
-
dispatchSignal = child.signal;
|
|
2969
|
-
}
|
|
2970
|
-
const eventEmitter = emitter ?? (schemaState ? new AgentEventEmitter() : void 0);
|
|
2971
|
-
if (schemaState && eventEmitter) {
|
|
2972
|
-
attachSchemaListeners(eventEmitter, schemaState);
|
|
2973
|
-
}
|
|
2974
|
-
const { subagent, dispose } = await subagentMgr.createAgentHeadless(
|
|
2975
|
-
augmented,
|
|
2976
|
-
effectiveContext,
|
|
2977
|
-
{
|
|
2978
|
-
// Workflow always bounds resource ceiling regardless of agentType's
|
|
2979
|
-
// own runConfig / maxTurns — these are workflow-level safety bounds,
|
|
2980
|
-
// not subagent-level preferences. P5 will refine via budget.
|
|
2981
|
-
runConfigOverrides: subagentBounds ?? {
|
|
2982
|
-
max_turns: resolveSubagentMaxTurns(),
|
|
2983
|
-
max_time_minutes: resolveSubagentMaxTimeMinutes()
|
|
2984
|
-
},
|
|
2985
|
-
...effort !== void 0 ? { modelConfigOverrides: { reasoningEffort: effort } } : {},
|
|
2986
|
-
eventEmitter,
|
|
2987
|
-
taskName: String(ctx.get("task_prompt")),
|
|
2988
|
-
subagentId: workflowAgentId
|
|
2989
|
-
}
|
|
2990
|
-
);
|
|
2991
|
-
try {
|
|
2992
|
-
try {
|
|
2993
|
-
await runWithAgentContext(
|
|
2994
|
-
workflowAgentId,
|
|
2995
|
-
() => subagent.execute(ctx, dispatchSignal)
|
|
2996
|
-
);
|
|
2997
|
-
} finally {
|
|
2998
|
-
reportTokens(subagent, opts, onTokens);
|
|
2999
|
-
}
|
|
3000
|
-
if (schemaState) {
|
|
3001
|
-
if (schemaState.result !== null) {
|
|
3002
|
-
if (worktreeIsolation) {
|
|
3003
|
-
const isolation = worktreeIsolation;
|
|
3004
|
-
worktreeIsolation = null;
|
|
3005
|
-
const preserved = await cleanupWorkflowWorktree(isolation);
|
|
3006
|
-
if (preserved) {
|
|
3007
|
-
debugLogger.info(
|
|
3008
|
-
`[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.`
|
|
3009
|
-
);
|
|
3010
|
-
}
|
|
3011
|
-
}
|
|
3012
|
-
return schemaState.result;
|
|
3013
|
-
}
|
|
3014
|
-
if (signal?.aborted) {
|
|
3015
|
-
throw new DOMException("Workflow aborted.", "AbortError");
|
|
3016
|
-
}
|
|
3017
|
-
const mode2 = subagent.getTerminateMode();
|
|
3018
|
-
if (mode2 !== "GOAL" /* GOAL */ && mode2 !== "CANCELLED" /* CANCELLED */) {
|
|
3019
|
-
throw terminalDispatchError(workflowAgentId, mode2);
|
|
3020
|
-
}
|
|
3021
|
-
if (schemaState.attempts > 2) {
|
|
3022
|
-
throw new WorkflowAgentFailedError(
|
|
3023
|
-
"subagent completed without calling StructuredOutput (after 2 in-conversation nudges).",
|
|
3024
|
-
"no_structured_output"
|
|
3025
|
-
);
|
|
3026
|
-
}
|
|
3027
|
-
throw new WorkflowAgentFailedError(
|
|
3028
|
-
"subagent completed without calling structured_output (no validation attempt \u2014 model produced plain-text content).",
|
|
3029
|
-
"no_structured_output"
|
|
3030
|
-
);
|
|
3031
|
-
}
|
|
3032
|
-
const mode = subagent.getTerminateMode();
|
|
3033
|
-
if (mode !== "GOAL" /* GOAL */) {
|
|
3034
|
-
throw terminalDispatchError(workflowAgentId, mode);
|
|
3035
|
-
}
|
|
3036
|
-
let finalText = toModelVisibleSubagentResult(
|
|
3037
|
-
subagent.getFinalText(),
|
|
3038
|
-
mode
|
|
3039
|
-
);
|
|
3040
|
-
if (worktreeIsolation) {
|
|
3041
|
-
const isolation = worktreeIsolation;
|
|
3042
|
-
worktreeIsolation = null;
|
|
3043
|
-
const preserved = await cleanupWorkflowWorktree(isolation);
|
|
3044
|
-
if (preserved && typeof finalText === "string") {
|
|
3045
|
-
finalText = appendWorktreePreservedSuffix(finalText, preserved);
|
|
3046
|
-
}
|
|
3047
|
-
}
|
|
3048
|
-
return finalText;
|
|
3049
|
-
} finally {
|
|
3050
|
-
await dispose();
|
|
3051
|
-
}
|
|
3052
|
-
} finally {
|
|
3053
|
-
if (onParentAbort && signal) {
|
|
3054
|
-
signal.removeEventListener("abort", onParentAbort);
|
|
3055
|
-
}
|
|
3056
|
-
approvalCleanup?.();
|
|
3057
|
-
if (worktreeIsolation) {
|
|
3058
|
-
try {
|
|
3059
|
-
await cleanupWorkflowWorktree(worktreeIsolation);
|
|
3060
|
-
} catch (error) {
|
|
3061
|
-
debugLogger.warn(
|
|
3062
|
-
`Workflow worktree cleanup in fallback finally failed for ${worktreeIsolation.path}: ${error instanceof Error ? error.message : String(error)}`
|
|
3063
|
-
);
|
|
3064
|
-
}
|
|
3065
|
-
}
|
|
3066
|
-
}
|
|
3067
|
-
}
|
|
3068
|
-
__name(runOverridePath, "runOverridePath");
|
|
3069
|
-
async function provisionWorkflowWorktree(config) {
|
|
3070
|
-
const cwd = config.getTargetDir();
|
|
3071
|
-
if (/\.qwen[\\/]worktrees[\\/]/.test(cwd)) {
|
|
3072
|
-
throw new Error(
|
|
3073
|
-
`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.`
|
|
3074
|
-
);
|
|
3075
|
-
}
|
|
3076
|
-
const probe = new GitWorktreeService(cwd);
|
|
3077
|
-
const gitCheck = await probe.checkGitAvailable();
|
|
3078
|
-
if (!gitCheck.available) {
|
|
3079
|
-
throw new Error(
|
|
3080
|
-
`agent({isolation:'worktree'}): ${gitCheck.error ?? "git is not available"}.`
|
|
3081
|
-
);
|
|
3082
|
-
}
|
|
3083
|
-
if (!await probe.isGitRepository()) {
|
|
3084
|
-
throw new Error(
|
|
3085
|
-
`agent({isolation:'worktree'}): ${cwd} is not a git repository.`
|
|
3086
|
-
);
|
|
3087
|
-
}
|
|
3088
|
-
const projectRoot = await probe.getRepoTopLevel() ?? cwd;
|
|
3089
|
-
const wtService = projectRoot === cwd ? probe : new GitWorktreeService(projectRoot);
|
|
3090
|
-
let parentDirty = false;
|
|
3091
|
-
try {
|
|
3092
|
-
parentDirty = await wtService.hasWorktreeChanges(projectRoot);
|
|
3093
|
-
} catch (error) {
|
|
3094
|
-
debugLogger.warn(
|
|
3095
|
-
`[Workflow] hasWorktreeChanges failed at ${projectRoot}: ${error}`
|
|
3096
|
-
);
|
|
3097
|
-
parentDirty = true;
|
|
3098
|
-
}
|
|
3099
|
-
if (parentDirty) {
|
|
3100
|
-
throw new Error(
|
|
3101
|
-
`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.`
|
|
3102
|
-
);
|
|
3103
|
-
}
|
|
3104
|
-
const slug = generateAgentWorktreeSlug();
|
|
3105
|
-
let parentBranch;
|
|
3106
|
-
try {
|
|
3107
|
-
parentBranch = await wtService.getCurrentBranch();
|
|
3108
|
-
} catch (error) {
|
|
3109
|
-
debugLogger.warn(
|
|
3110
|
-
`[Workflow] getCurrentBranch failed at ${projectRoot}: ${error}`
|
|
3111
|
-
);
|
|
3112
|
-
}
|
|
3113
|
-
const created = await wtService.createUserWorktree(slug, parentBranch, {
|
|
3114
|
-
symlinkDirectories: config.getWorktreeSymlinkDirectories()
|
|
3115
|
-
});
|
|
3116
|
-
if (!created.success || !created.worktree) {
|
|
3117
|
-
throw new Error(
|
|
3118
|
-
`agent({isolation:'worktree'}): failed to create worktree: ${created.error ?? "unknown error"}.`
|
|
3119
|
-
);
|
|
3120
|
-
}
|
|
3121
|
-
try {
|
|
3122
|
-
await writeWorktreeSessionMarker(
|
|
3123
|
-
created.worktree.path,
|
|
3124
|
-
config.getSessionId()
|
|
3125
|
-
);
|
|
3126
|
-
} catch (error) {
|
|
3127
|
-
debugLogger.warn(
|
|
3128
|
-
`[Workflow] failed to write session marker at ${created.worktree.path}: ${error}`
|
|
3129
|
-
);
|
|
3130
|
-
}
|
|
3131
|
-
return {
|
|
3132
|
-
slug,
|
|
3133
|
-
path: created.worktree.path,
|
|
3134
|
-
branch: created.worktree.branch,
|
|
3135
|
-
repoRoot: projectRoot
|
|
3136
|
-
};
|
|
3137
|
-
}
|
|
3138
|
-
__name(provisionWorkflowWorktree, "provisionWorkflowWorktree");
|
|
3139
|
-
async function cleanupWorkflowWorktree(isolation) {
|
|
3140
|
-
const wtService = new GitWorktreeService(isolation.repoRoot);
|
|
3141
|
-
const [hasChanges, hasUnmerged] = await Promise.all([
|
|
3142
|
-
wtService.hasWorktreeChanges(isolation.path).catch((error) => {
|
|
3143
|
-
debugLogger.warn(
|
|
3144
|
-
`[Workflow] hasWorktreeChanges failed for ${isolation.path}: ${error}`
|
|
3145
|
-
);
|
|
3146
|
-
return true;
|
|
3147
|
-
}),
|
|
3148
|
-
wtService.hasUnmergedWorktreeCommits(isolation.slug).catch((error) => {
|
|
3149
|
-
debugLogger.warn(
|
|
3150
|
-
`[Workflow] hasUnmergedWorktreeCommits failed for ${isolation.slug}: ${error}`
|
|
3151
|
-
);
|
|
3152
|
-
return true;
|
|
3153
|
-
})
|
|
3154
|
-
]);
|
|
3155
|
-
if (hasChanges || hasUnmerged) {
|
|
3156
|
-
debugLogger.info(
|
|
3157
|
-
`[Workflow] Preserving isolation worktree ${isolation.path} (branch ${isolation.branch}, hasChanges=${hasChanges}, hasUnmerged=${hasUnmerged})`
|
|
3158
|
-
);
|
|
3159
|
-
return { path: isolation.path, branch: isolation.branch };
|
|
3160
|
-
}
|
|
3161
|
-
try {
|
|
3162
|
-
const result = await wtService.removeUserWorktree(isolation.slug, {
|
|
3163
|
-
deleteBranch: true
|
|
3164
|
-
});
|
|
3165
|
-
if (!result.success) {
|
|
3166
|
-
debugLogger.warn(
|
|
3167
|
-
`[Workflow] Failed to remove ephemeral worktree ${isolation.path}: ${result.error}`
|
|
3168
|
-
);
|
|
3169
|
-
return { path: isolation.path, branch: isolation.branch };
|
|
3170
|
-
}
|
|
3171
|
-
if (result.branchPreserved) {
|
|
3172
|
-
debugLogger.warn(
|
|
3173
|
-
`[Workflow] Removed worktree directory ${isolation.path} but kept branch ${isolation.branch} (unmerged commits at delete time)`
|
|
3174
|
-
);
|
|
3175
|
-
return { branch: isolation.branch };
|
|
3176
|
-
}
|
|
3177
|
-
} catch (error) {
|
|
3178
|
-
debugLogger.warn(
|
|
3179
|
-
`[Workflow] Failed to remove ephemeral worktree ${isolation.path}: ${error}`
|
|
3180
|
-
);
|
|
3181
|
-
return { path: isolation.path, branch: isolation.branch };
|
|
3182
|
-
}
|
|
3183
|
-
return null;
|
|
3184
|
-
}
|
|
3185
|
-
__name(cleanupWorkflowWorktree, "cleanupWorkflowWorktree");
|
|
3186
|
-
function appendWorktreePreservedSuffix(finalText, preserved) {
|
|
3187
|
-
const sep2 = finalText.endsWith("\n") ? "\n" : "\n\n";
|
|
3188
|
-
if (preserved.path) {
|
|
3189
|
-
return `${finalText}${sep2}[worktree preserved: ${preserved.path} (branch ${preserved.branch})]`;
|
|
3190
|
-
}
|
|
3191
|
-
return `${finalText}${sep2}[worktree directory removed; branch ${preserved.branch} preserved \u2014 recover with \`git worktree add <path> ${preserved.branch}\`]`;
|
|
3192
|
-
}
|
|
3193
|
-
__name(appendWorktreePreservedSuffix, "appendWorktreePreservedSuffix");
|
|
3194
|
-
function createSchemaModeState() {
|
|
3195
|
-
return {
|
|
3196
|
-
result: null,
|
|
3197
|
-
attempts: 0,
|
|
3198
|
-
pendingArgs: /* @__PURE__ */ new Map(),
|
|
3199
|
-
abortController: new AbortController()
|
|
3200
|
-
};
|
|
3201
|
-
}
|
|
3202
|
-
__name(createSchemaModeState, "createSchemaModeState");
|
|
3203
|
-
function attachSchemaListeners(emitter, state) {
|
|
3204
|
-
const targetTool = ToolNames.STRUCTURED_OUTPUT;
|
|
3205
|
-
emitter.on("tool_call" /* TOOL_CALL */, (evt) => {
|
|
3206
|
-
if (evt.name !== targetTool) return;
|
|
3207
|
-
state.pendingArgs.set(evt.callId, evt.args);
|
|
3208
|
-
});
|
|
3209
|
-
emitter.on("tool_result" /* TOOL_RESULT */, (evt) => {
|
|
3210
|
-
if (evt.name !== targetTool) return;
|
|
3211
|
-
const args = state.pendingArgs.get(evt.callId);
|
|
3212
|
-
state.pendingArgs.delete(evt.callId);
|
|
3213
|
-
if (evt.success) {
|
|
3214
|
-
if (args !== void 0 && state.result === null) {
|
|
3215
|
-
state.result = args;
|
|
3216
|
-
state.abortController.abort();
|
|
3217
|
-
}
|
|
3218
|
-
return;
|
|
3219
|
-
}
|
|
3220
|
-
state.attempts += 1;
|
|
3221
|
-
if (state.attempts > 2 && state.result === null) {
|
|
3222
|
-
state.abortController.abort();
|
|
3223
|
-
}
|
|
3224
|
-
});
|
|
3225
|
-
}
|
|
3226
|
-
__name(attachSchemaListeners, "attachSchemaListeners");
|
|
3227
|
-
async function createSchemaConfigOverride(base, schema) {
|
|
3228
|
-
const override = deriveConfig(base);
|
|
3229
|
-
installSessionWorkflowRevisionWriteThrough(override, base);
|
|
3230
|
-
await rebuildToolRegistryOnOverride(override, base);
|
|
3231
|
-
const registry = override.getToolRegistry();
|
|
3232
|
-
registry.registerTool(new SyntheticOutputTool(schema));
|
|
3233
|
-
return override;
|
|
3234
|
-
}
|
|
3235
|
-
__name(createSchemaConfigOverride, "createSchemaConfigOverride");
|
|
3236
|
-
var WorkflowOrchestrator = class {
|
|
3237
|
-
constructor(dispatch) {
|
|
3238
|
-
this.dispatch = dispatch;
|
|
3239
|
-
}
|
|
3240
|
-
static {
|
|
3241
|
-
__name(this, "WorkflowOrchestrator");
|
|
3242
|
-
}
|
|
3243
|
-
async run(req) {
|
|
3244
|
-
const runId = req.runId ?? generateRunId();
|
|
3245
|
-
const maxAgents = resolveMaxAgentsPerRun();
|
|
3246
|
-
const signal = req.abortOnTimeout?.signal;
|
|
3247
|
-
const scheduler = req.scheduler ?? new WorkflowDispatchScheduler(resolveConcurrencyLimit(), signal);
|
|
3248
|
-
const rejectThroughPauseGate = /* @__PURE__ */ __name((error) => scheduler.waitUntilRunning().then(
|
|
3249
|
-
() => {
|
|
3250
|
-
throw error;
|
|
3251
|
-
},
|
|
3252
|
-
() => {
|
|
3253
|
-
throw error;
|
|
3254
|
-
}
|
|
3255
|
-
), "rejectThroughPauseGate");
|
|
3256
|
-
let agentCount = 0;
|
|
3257
|
-
let dispatchTraceCount = 0;
|
|
3258
|
-
const emitter = req.emitter;
|
|
3259
|
-
const budget = req.budget;
|
|
3260
|
-
const dependencyContext = new AsyncLocalStorage();
|
|
3261
|
-
const issueDispatchTrace = /* @__PURE__ */ __name((prompt, opts, cached = false) => {
|
|
3262
|
-
const id = `dispatch-${dispatchTraceCount += 1}`;
|
|
3263
|
-
const store = dependencyContext.getStore();
|
|
3264
|
-
const dependsOn = Array.from(new Set(store?.tails ?? []));
|
|
3265
|
-
if (store) store.tails = [id];
|
|
3266
|
-
try {
|
|
3267
|
-
emitter?.dispatchQueued?.({
|
|
3268
|
-
id,
|
|
3269
|
-
...typeof opts.label === "string" ? { label: opts.label } : {},
|
|
3270
|
-
prompt,
|
|
3271
|
-
dependsOn,
|
|
3272
|
-
queuedAt: Date.now(),
|
|
3273
|
-
...cached ? { cached: true } : {}
|
|
3274
|
-
});
|
|
3275
|
-
} catch (e) {
|
|
3276
|
-
debugLogger.warn("emitter.dispatchQueued threw:", e);
|
|
3277
|
-
}
|
|
3278
|
-
return id;
|
|
3279
|
-
}, "issueDispatchTrace");
|
|
3280
|
-
const journal = req.journal;
|
|
3281
|
-
const replay = req.resumeReplay;
|
|
3282
|
-
let prefixHash = deriveArgsSeed(req.args);
|
|
3283
|
-
let hadMiss = false;
|
|
3284
|
-
let journalAgentId = 0;
|
|
3285
|
-
const parentSandboxRef = {
|
|
3286
|
-
current: void 0
|
|
3287
|
-
};
|
|
3288
|
-
const countedDispatch = /* @__PURE__ */ __name((prompt, opts) => {
|
|
3289
|
-
if (typeof prompt !== "string" || prompt.length === 0) {
|
|
3290
|
-
return rejectThroughPauseGate(
|
|
3291
|
-
new Error("agent() requires a non-empty string prompt.")
|
|
3292
|
-
);
|
|
3293
|
-
}
|
|
3294
|
-
let journalKey;
|
|
3295
|
-
let journalEntryId;
|
|
3296
|
-
let respawnLine;
|
|
3297
|
-
if (journal) {
|
|
3298
|
-
journalKey = deriveAgentKey(prefixHash, prompt, opts);
|
|
3299
|
-
prefixHash = journalKey;
|
|
3300
|
-
if (!hadMiss && replay) {
|
|
3301
|
-
const cached = replay.results.get(journalKey);
|
|
3302
|
-
if (cached !== void 0) {
|
|
3303
|
-
const respawns = replay.started.get(journalKey);
|
|
3304
|
-
if (respawns && respawns.length > 0) {
|
|
3305
|
-
debugLogger.info(
|
|
3306
|
-
`[Workflow] resume cache hit after ${respawns.length} prior respawn(s) for runId=${runId}.`
|
|
3307
|
-
);
|
|
3308
|
-
}
|
|
3309
|
-
const label2 = typeof opts.label === "string" ? opts.label : void 0;
|
|
3310
|
-
const dispatchId2 = issueDispatchTrace(prompt, opts, true);
|
|
3311
|
-
try {
|
|
3312
|
-
emitter?.agentDispatched?.(label2);
|
|
3313
|
-
} catch (e) {
|
|
3314
|
-
debugLogger.warn("emitter.agentDispatched threw:", e);
|
|
3315
|
-
}
|
|
3316
|
-
try {
|
|
3317
|
-
emitter?.agentCompleted?.(label2);
|
|
3318
|
-
} catch (e) {
|
|
3319
|
-
debugLogger.warn("emitter.agentCompleted threw:", e);
|
|
3320
|
-
}
|
|
3321
|
-
try {
|
|
3322
|
-
emitter?.dispatchSettled?.(dispatchId2, void 0, Date.now());
|
|
3323
|
-
} catch (e) {
|
|
3324
|
-
debugLogger.warn("emitter.dispatchSettled threw:", e);
|
|
3325
|
-
}
|
|
3326
|
-
return scheduler.waitUntilRunning().then(
|
|
3327
|
-
() => cached.result,
|
|
3328
|
-
() => cached.result
|
|
3329
|
-
);
|
|
3330
|
-
}
|
|
3331
|
-
}
|
|
3332
|
-
const priorStarts = replay?.started.get(journalKey);
|
|
3333
|
-
if (priorStarts && priorStarts.length > 0 && !replay?.results.has(journalKey)) {
|
|
3334
|
-
const name = typeof opts.label === "string" ? `"${stripAnsiAndControl(opts.label)}"` : "an agent";
|
|
3335
|
-
respawnLine = replay?.failed.has(journalKey) ? `[resume] re-running ${name}: it failed in the previous run` : `[resume] respawning ${name}: interrupted in a previous run (${priorStarts.length} prior attempt${priorStarts.length === 1 ? "" : "s"})`;
|
|
3336
|
-
}
|
|
3337
|
-
hadMiss = true;
|
|
3338
|
-
}
|
|
3339
|
-
if (budget && budget.total !== null && budget.remaining() <= 0) {
|
|
3340
|
-
debugLogger.warn(
|
|
3341
|
-
`[Workflow] budget gate refused dispatch at entry: runId=${runId} spent=${budget.spent()} total=${budget.total}`
|
|
3342
|
-
);
|
|
3343
|
-
return rejectThroughPauseGate(
|
|
3344
|
-
new WorkflowBudgetExceededError(runId, budget.total, budget.spent())
|
|
3345
|
-
);
|
|
3346
|
-
}
|
|
3347
|
-
if (agentCount >= maxAgents) {
|
|
3348
|
-
return rejectThroughPauseGate(
|
|
3349
|
-
new WorkflowAgentCapExceededError(maxAgents)
|
|
3350
|
-
);
|
|
3351
|
-
}
|
|
3352
|
-
agentCount += 1;
|
|
3353
|
-
if (journal && journalKey !== void 0) {
|
|
3354
|
-
journalEntryId = String(journalAgentId += 1);
|
|
3355
|
-
}
|
|
3356
|
-
const label = typeof opts.label === "string" ? opts.label : void 0;
|
|
3357
|
-
const dispatchId = issueDispatchTrace(prompt, opts);
|
|
3358
|
-
try {
|
|
3359
|
-
emitter?.agentDispatched?.(label);
|
|
3360
|
-
} catch (e) {
|
|
3361
|
-
debugLogger.warn("emitter.agentDispatched threw:", e);
|
|
3362
|
-
}
|
|
3363
|
-
let completionEmitted = false;
|
|
3364
|
-
const emitCompletion = /* @__PURE__ */ __name((error) => {
|
|
3365
|
-
if (completionEmitted) return;
|
|
3366
|
-
completionEmitted = true;
|
|
3367
|
-
const message = error === void 0 ? void 0 : error instanceof Error ? error.message : String(error);
|
|
3368
|
-
try {
|
|
3369
|
-
emitter?.agentCompleted?.(label, message);
|
|
3370
|
-
} catch (e) {
|
|
3371
|
-
debugLogger.warn("emitter.agentCompleted threw:", e);
|
|
3372
|
-
}
|
|
3373
|
-
try {
|
|
3374
|
-
emitter?.dispatchSettled?.(dispatchId, message, Date.now());
|
|
3375
|
-
} catch (e) {
|
|
3376
|
-
debugLogger.warn("emitter.dispatchSettled threw:", e);
|
|
3377
|
-
}
|
|
3378
|
-
}, "emitCompletion");
|
|
3379
|
-
return scheduler.run(async () => {
|
|
3380
|
-
try {
|
|
3381
|
-
try {
|
|
3382
|
-
emitter?.dispatchStarted?.(dispatchId, Date.now());
|
|
3383
|
-
} catch (e) {
|
|
3384
|
-
debugLogger.warn("emitter.dispatchStarted threw:", e);
|
|
3385
|
-
}
|
|
3386
|
-
if (budget && budget.total !== null && budget.remaining() <= 0) {
|
|
3387
|
-
debugLogger.warn(
|
|
3388
|
-
`[Workflow] budget gate refused dispatch at slot-acquire: runId=${runId} spent=${budget.spent()} total=${budget.total}`
|
|
3389
|
-
);
|
|
3390
|
-
throw new WorkflowBudgetExceededError(
|
|
3391
|
-
runId,
|
|
3392
|
-
budget.total,
|
|
3393
|
-
budget.spent()
|
|
3394
|
-
);
|
|
3395
|
-
}
|
|
3396
|
-
if (journal && journalKey !== void 0 && journalEntryId !== void 0) {
|
|
3397
|
-
journal.append({
|
|
3398
|
-
type: "started",
|
|
3399
|
-
key: journalKey,
|
|
3400
|
-
agentId: journalEntryId
|
|
3401
|
-
}).catch(
|
|
3402
|
-
(e) => debugLogger.warn(`journal started-append failed: ${e}`)
|
|
3403
|
-
);
|
|
3404
|
-
}
|
|
3405
|
-
if (respawnLine) {
|
|
3406
|
-
parentSandboxRef.current?.appendLog(respawnLine);
|
|
3407
|
-
try {
|
|
3408
|
-
emitter?.resumeRespawn?.(respawnLine);
|
|
3409
|
-
} catch (e) {
|
|
3410
|
-
debugLogger.warn("emitter.resumeRespawn threw:", e);
|
|
3411
|
-
}
|
|
3412
|
-
}
|
|
3413
|
-
const result = await this.dispatch(prompt, opts, dispatchId);
|
|
3414
|
-
emitCompletion();
|
|
3415
|
-
if (journal && journalKey !== void 0) {
|
|
3416
|
-
journal.append({
|
|
3417
|
-
type: "result",
|
|
3418
|
-
key: journalKey,
|
|
3419
|
-
agentId: journalEntryId ?? "",
|
|
3420
|
-
result
|
|
3421
|
-
}).catch(
|
|
3422
|
-
(e) => debugLogger.warn(`journal result-append failed: ${e}`)
|
|
3423
|
-
);
|
|
3424
|
-
}
|
|
3425
|
-
if (budget) {
|
|
3426
|
-
try {
|
|
3427
|
-
emitter?.budgetUpdated?.(budget.spent(), budget.total);
|
|
3428
|
-
} catch (e) {
|
|
3429
|
-
debugLogger.warn("emitter.budgetUpdated threw:", e);
|
|
3430
|
-
}
|
|
3431
|
-
}
|
|
3432
|
-
return result;
|
|
3433
|
-
} catch (err) {
|
|
3434
|
-
emitCompletion(err);
|
|
3435
|
-
if (budget) {
|
|
3436
|
-
try {
|
|
3437
|
-
emitter?.budgetUpdated?.(budget.spent(), budget.total);
|
|
3438
|
-
} catch (e) {
|
|
3439
|
-
debugLogger.warn("emitter.budgetUpdated threw:", e);
|
|
3440
|
-
}
|
|
3441
|
-
}
|
|
3442
|
-
if (journal && journalKey !== void 0 && !signal?.aborted && !isWorkflowRunLevelError(err)) {
|
|
3443
|
-
journal.append({
|
|
3444
|
-
type: "failed",
|
|
3445
|
-
key: journalKey,
|
|
3446
|
-
agentId: journalEntryId ?? ""
|
|
3447
|
-
}).catch(
|
|
3448
|
-
(e) => debugLogger.warn(`journal failed-append failed: ${e}`)
|
|
3449
|
-
);
|
|
3450
|
-
}
|
|
3451
|
-
if (signal?.aborted || isWorkflowRunLevelError(err)) throw err;
|
|
3452
|
-
if (isWorkflowAgentFailedError(err)) {
|
|
3453
|
-
debugLogger.warn(
|
|
3454
|
-
`[Workflow] agent settled to null (${err.kind}): ${err.message}`
|
|
3455
|
-
);
|
|
3456
|
-
} else {
|
|
3457
|
-
debugLogger.warn(
|
|
3458
|
-
`[Workflow] agent settled to null: ${extractErrorMessage(err)}`
|
|
3459
|
-
);
|
|
3460
|
-
}
|
|
3461
|
-
return null;
|
|
3462
|
-
}
|
|
3463
|
-
}).then(
|
|
3464
|
-
// Resolve even if the gate aborts: a successful dispatch must not
|
|
3465
|
-
// turn into a teardown rejection — for a fire-and-forget call the
|
|
3466
|
-
// script never attached a handler, so the rejection would surface
|
|
3467
|
-
// as a spurious process-level unhandledRejection alarm on a
|
|
3468
|
-
// correctly-cancelled run.
|
|
3469
|
-
(result) => scheduler.waitUntilRunning().then(
|
|
3470
|
-
() => result,
|
|
3471
|
-
() => result
|
|
3472
|
-
),
|
|
3473
|
-
(error) => {
|
|
3474
|
-
emitCompletion(error);
|
|
3475
|
-
return scheduler.waitUntilRunning().then(
|
|
3476
|
-
() => {
|
|
3477
|
-
throw error;
|
|
3478
|
-
},
|
|
3479
|
-
() => {
|
|
3480
|
-
throw error;
|
|
3481
|
-
}
|
|
3482
|
-
);
|
|
3483
|
-
}
|
|
3484
|
-
);
|
|
3485
|
-
}, "countedDispatch");
|
|
3486
|
-
const parallelImpl = makeParallelImpl(signal, dependencyContext);
|
|
3487
|
-
const pipelineImpl = makePipelineImpl(signal, dependencyContext);
|
|
3488
|
-
const resolveSavedWorkflow = req.resolveSavedWorkflow;
|
|
3489
|
-
const workflowImpl = resolveSavedWorkflow ? async (nameOrRef, nestedArgs) => {
|
|
3490
|
-
const resolved = await resolveSavedWorkflow(nameOrRef);
|
|
3491
|
-
const nestedSandbox = createWorkflowSandbox({
|
|
3492
|
-
args: nestedArgs,
|
|
3493
|
-
runId,
|
|
3494
|
-
dispatch: countedDispatch,
|
|
3495
|
-
parallel: parallelImpl,
|
|
3496
|
-
pipeline: pipelineImpl,
|
|
3497
|
-
abortOnTimeout: req.abortOnTimeout,
|
|
3498
|
-
emitter,
|
|
3499
|
-
budget,
|
|
3500
|
-
scheduler
|
|
3501
|
-
// No `workflow` — single-level nesting limit.
|
|
3502
|
-
});
|
|
3503
|
-
try {
|
|
3504
|
-
return await nestedSandbox.run(resolved.script);
|
|
3505
|
-
} finally {
|
|
3506
|
-
for (const line of nestedSandbox.getLogs()) {
|
|
3507
|
-
parentSandboxRef.current?.appendLog(line);
|
|
3508
|
-
}
|
|
3509
|
-
}
|
|
3510
|
-
} : void 0;
|
|
3511
|
-
const sandbox = createWorkflowSandbox({
|
|
3512
|
-
args: req.args,
|
|
3513
|
-
maxWallClockMs: req.maxWallClockMs,
|
|
3514
|
-
runId,
|
|
3515
|
-
dispatch: countedDispatch,
|
|
3516
|
-
parallel: parallelImpl,
|
|
3517
|
-
pipeline: pipelineImpl,
|
|
3518
|
-
workflow: workflowImpl,
|
|
3519
|
-
abortOnTimeout: req.abortOnTimeout,
|
|
3520
|
-
emitter,
|
|
3521
|
-
budget,
|
|
3522
|
-
scheduler
|
|
3523
|
-
});
|
|
3524
|
-
parentSandboxRef.current = sandbox;
|
|
3525
|
-
try {
|
|
3526
|
-
const result = await dependencyContext.run(
|
|
3527
|
-
{ tails: [] },
|
|
3528
|
-
() => sandbox.run(req.script)
|
|
3529
|
-
);
|
|
3530
|
-
return {
|
|
3531
|
-
runId,
|
|
3532
|
-
result,
|
|
3533
|
-
phases: sandbox.getPhases(),
|
|
3534
|
-
logs: sandbox.getLogs(),
|
|
3535
|
-
meta: sandbox.getMeta()
|
|
3536
|
-
};
|
|
3537
|
-
} catch (err) {
|
|
3538
|
-
throw new WorkflowExecutionError(
|
|
3539
|
-
extractErrorMessage(err),
|
|
3540
|
-
sandbox.getPhases(),
|
|
3541
|
-
sandbox.getLogs(),
|
|
3542
|
-
sandbox.getMeta()
|
|
3543
|
-
);
|
|
3544
|
-
}
|
|
3545
|
-
}
|
|
3546
|
-
};
|
|
3547
|
-
async function settleToNullArray(thunks, signal) {
|
|
3548
|
-
const settled = await Promise.allSettled(
|
|
3549
|
-
thunks.map((t) => Promise.resolve().then(t))
|
|
3550
|
-
);
|
|
3551
|
-
if (signal?.aborted)
|
|
3552
|
-
throw new DOMException("Workflow run aborted.", "AbortError");
|
|
3553
|
-
const visitedReasons = /* @__PURE__ */ new WeakSet();
|
|
3554
|
-
const findRunFailureReason = /* @__PURE__ */ __name((reason) => {
|
|
3555
|
-
if (reason === null || typeof reason !== "object") return void 0;
|
|
3556
|
-
if (visitedReasons.has(reason)) return void 0;
|
|
3557
|
-
visitedReasons.add(reason);
|
|
3558
|
-
try {
|
|
3559
|
-
if (reason.__wfRunFailure === true) {
|
|
3560
|
-
return reason;
|
|
3561
|
-
}
|
|
3562
|
-
const errors = reason?.errors;
|
|
3563
|
-
if (!Array.isArray(errors)) return void 0;
|
|
3564
|
-
for (const error of errors) {
|
|
3565
|
-
const runFailure = findRunFailureReason(error);
|
|
3566
|
-
if (runFailure !== void 0) return runFailure;
|
|
3567
|
-
}
|
|
3568
|
-
} catch {
|
|
3569
|
-
return void 0;
|
|
3570
|
-
}
|
|
3571
|
-
return void 0;
|
|
3572
|
-
}, "findRunFailureReason");
|
|
3573
|
-
for (const result of settled) {
|
|
3574
|
-
if (result.status !== "rejected") continue;
|
|
3575
|
-
const runFailure = findRunFailureReason(result.reason);
|
|
3576
|
-
if (runFailure !== void 0) throw runFailure;
|
|
3577
|
-
}
|
|
3578
|
-
return settled.map((r, i) => {
|
|
3579
|
-
if (r.status === "fulfilled") return r.value;
|
|
3580
|
-
const reason = r.reason;
|
|
3581
|
-
debugLogger.warn(
|
|
3582
|
-
`Workflow thunk at index ${i} rejected: ${String(reason?.message ?? r.reason)}`
|
|
3583
|
-
);
|
|
3584
|
-
return null;
|
|
3585
|
-
});
|
|
3586
|
-
}
|
|
3587
|
-
__name(settleToNullArray, "settleToNullArray");
|
|
3588
|
-
function makeParallelImpl(signal, dependencyContext) {
|
|
3589
|
-
return (thunks) => {
|
|
3590
|
-
if (!Array.isArray(thunks)) {
|
|
3591
|
-
return Promise.reject(
|
|
3592
|
-
new Error(
|
|
3593
|
-
"parallel() expects an array of thunks (functions returning promises)."
|
|
3594
|
-
)
|
|
3595
|
-
);
|
|
3596
|
-
}
|
|
3597
|
-
for (const t of thunks) {
|
|
3598
|
-
if (typeof t !== "function") {
|
|
3599
|
-
return Promise.reject(
|
|
3600
|
-
new Error(
|
|
3601
|
-
"parallel() expects an array of functions, not values \u2014 wrap each call: parallel([() => agent(...), () => agent(...)])."
|
|
3602
|
-
)
|
|
3603
|
-
);
|
|
3604
|
-
}
|
|
3605
|
-
}
|
|
3606
|
-
const parent = dependencyContext.getStore();
|
|
3607
|
-
const inheritedTails = parent?.tails ?? [];
|
|
3608
|
-
const branches = thunks.map((thunk) => {
|
|
3609
|
-
const store = { tails: [...inheritedTails] };
|
|
3610
|
-
return {
|
|
3611
|
-
store,
|
|
3612
|
-
thunk: /* @__PURE__ */ __name(() => dependencyContext.run(store, thunk), "thunk")
|
|
3613
|
-
};
|
|
3614
|
-
});
|
|
3615
|
-
return settleToNullArray(
|
|
3616
|
-
branches.map(({ thunk }) => thunk),
|
|
3617
|
-
signal
|
|
3618
|
-
).then((result) => {
|
|
3619
|
-
if (parent && branches.length > 0) {
|
|
3620
|
-
parent.tails = mergeFanoutTails(
|
|
3621
|
-
parent.tails,
|
|
3622
|
-
inheritedTails,
|
|
3623
|
-
branches.flatMap(({ store }) => store.tails)
|
|
3624
|
-
);
|
|
3625
|
-
}
|
|
3626
|
-
return result;
|
|
3627
|
-
});
|
|
3628
|
-
};
|
|
3629
|
-
}
|
|
3630
|
-
__name(makeParallelImpl, "makeParallelImpl");
|
|
3631
|
-
function makePipelineImpl(signal, dependencyContext) {
|
|
3632
|
-
return (items, ...stages) => {
|
|
3633
|
-
if (!Array.isArray(items)) {
|
|
3634
|
-
return Promise.reject(
|
|
3635
|
-
new Error(
|
|
3636
|
-
"pipeline() expects an array of items as its first argument."
|
|
3637
|
-
)
|
|
3638
|
-
);
|
|
3639
|
-
}
|
|
3640
|
-
for (const s of stages) {
|
|
3641
|
-
if (typeof s !== "function") {
|
|
3642
|
-
return Promise.reject(
|
|
3643
|
-
new Error(
|
|
3644
|
-
"pipeline() stages must be functions: pipeline(items, item => ..., result => ...)."
|
|
3645
|
-
)
|
|
3646
|
-
);
|
|
3647
|
-
}
|
|
3648
|
-
}
|
|
3649
|
-
const parent = dependencyContext.getStore();
|
|
3650
|
-
const inheritedTails = parent?.tails ?? [];
|
|
3651
|
-
const branches = items.map((item, idx) => {
|
|
3652
|
-
const store = { tails: [...inheritedTails] };
|
|
3653
|
-
return {
|
|
3654
|
-
store,
|
|
3655
|
-
thunk: /* @__PURE__ */ __name(() => dependencyContext.run(
|
|
3656
|
-
store,
|
|
3657
|
-
() => runPipelineChain(item, idx, stages)
|
|
3658
|
-
), "thunk")
|
|
3659
|
-
};
|
|
3660
|
-
});
|
|
3661
|
-
return settleToNullArray(
|
|
3662
|
-
branches.map(({ thunk }) => thunk),
|
|
3663
|
-
signal
|
|
3664
|
-
).then((result) => {
|
|
3665
|
-
if (parent && branches.length > 0) {
|
|
3666
|
-
parent.tails = mergeFanoutTails(
|
|
3667
|
-
parent.tails,
|
|
3668
|
-
inheritedTails,
|
|
3669
|
-
branches.flatMap(({ store }) => store.tails)
|
|
3670
|
-
);
|
|
3671
|
-
}
|
|
3672
|
-
return result;
|
|
3673
|
-
});
|
|
3674
|
-
};
|
|
3675
|
-
}
|
|
3676
|
-
__name(makePipelineImpl, "makePipelineImpl");
|
|
3677
|
-
function mergeFanoutTails(currentParentTails, inheritedTails, branchTails) {
|
|
3678
|
-
const inherited = new Set(inheritedTails);
|
|
3679
|
-
const newBranchTails = branchTails.filter((tail) => !inherited.has(tail));
|
|
3680
|
-
const merged = newBranchTails.length > 0 ? [
|
|
3681
|
-
...newBranchTails,
|
|
3682
|
-
...currentParentTails.filter((tail) => !inherited.has(tail))
|
|
3683
|
-
] : currentParentTails;
|
|
3684
|
-
return Array.from(new Set(merged));
|
|
3685
|
-
}
|
|
3686
|
-
__name(mergeFanoutTails, "mergeFanoutTails");
|
|
3687
|
-
async function runPipelineChain(item, idx, stages) {
|
|
3688
|
-
let prev = item;
|
|
3689
|
-
for (const stage of stages) {
|
|
3690
|
-
if (prev === null) break;
|
|
3691
|
-
prev = await stage(prev, item, idx);
|
|
3692
|
-
}
|
|
3693
|
-
return prev;
|
|
3694
|
-
}
|
|
3695
|
-
__name(runPipelineChain, "runPipelineChain");
|
|
3696
|
-
function extractErrorMessage(err) {
|
|
3697
|
-
if (err && typeof err === "object" && "message" in err) {
|
|
3698
|
-
const m = err.message;
|
|
3699
|
-
if (typeof m === "string") return m;
|
|
3700
|
-
return String(m);
|
|
3701
|
-
}
|
|
3702
|
-
return String(err);
|
|
3703
|
-
}
|
|
3704
|
-
__name(extractErrorMessage, "extractErrorMessage");
|
|
3705
|
-
|
|
3706
|
-
// packages/core/src/agents/workflow-snapshot.ts
|
|
3707
|
-
init_esbuild_shims();
|
|
3708
|
-
import { promises as fs3 } from "node:fs";
|
|
3709
|
-
import path3 from "node:path";
|
|
3710
|
-
var debugLogger6 = createDebugLogger("WORKFLOW_SNAPSHOT");
|
|
3711
|
-
var MAX_RETAINED_SNAPSHOTS = 30;
|
|
3712
|
-
function toSnapshot(task) {
|
|
3713
|
-
if (!isTerminalWorkflowStatus(task.status)) {
|
|
3714
|
-
throw new Error(`Cannot snapshot active workflow ${task.runId}.`);
|
|
3715
|
-
}
|
|
3716
|
-
return {
|
|
3717
|
-
runId: task.runId,
|
|
3718
|
-
...task.toolUseId ? { toolUseId: task.toolUseId } : {},
|
|
3719
|
-
description: task.description,
|
|
3720
|
-
...task.workflowName ? { workflowName: task.workflowName } : {},
|
|
3721
|
-
sourceRunId: task.sourceRunId,
|
|
3722
|
-
startMode: task.startMode,
|
|
3723
|
-
meta: task.meta,
|
|
3724
|
-
status: task.status,
|
|
3725
|
-
script: task.script ?? "",
|
|
3726
|
-
scriptPath: task.scriptPath,
|
|
3727
|
-
phases: [...task.phases],
|
|
3728
|
-
phaseVisits: task.phaseVisits.map((visit) => ({ ...visit })),
|
|
3729
|
-
dispatches: task.dispatches.map((dispatch) => ({
|
|
3730
|
-
...dispatch,
|
|
3731
|
-
dependsOn: [...dispatch.dependsOn]
|
|
3732
|
-
})),
|
|
3733
|
-
agentsDispatched: task.agentsDispatched,
|
|
3734
|
-
agentsCompleted: task.agentsCompleted,
|
|
3735
|
-
agentsRespawned: task.agentsRespawned ?? 0,
|
|
3736
|
-
tokensSpent: task.tokensSpent,
|
|
3737
|
-
tokenBudgetTotal: task.tokenBudgetTotal,
|
|
3738
|
-
perPhaseTokens: Array.from(task.perPhaseTokens.entries()),
|
|
3739
|
-
recentLogs: [...task.recentLogs],
|
|
3740
|
-
events: task.events.map((event) => ({ ...event })),
|
|
3741
|
-
startTime: task.startTime,
|
|
3742
|
-
endTime: task.endTime,
|
|
3743
|
-
result: safeResult(task.result),
|
|
3744
|
-
error: task.error
|
|
3745
|
-
};
|
|
3746
|
-
}
|
|
3747
|
-
__name(toSnapshot, "toSnapshot");
|
|
3748
|
-
function safeResult(result) {
|
|
3749
|
-
if (result === void 0) return void 0;
|
|
3750
|
-
try {
|
|
3751
|
-
JSON.stringify(result);
|
|
3752
|
-
return result;
|
|
3753
|
-
} catch {
|
|
3754
|
-
return `(non-JSON-serializable ${typeof result})`;
|
|
3755
|
-
}
|
|
3756
|
-
}
|
|
3757
|
-
__name(safeResult, "safeResult");
|
|
3758
|
-
async function writeWorkflowSnapshot(config, task) {
|
|
3759
|
-
const storage = config.storage;
|
|
3760
|
-
if (!storage) return false;
|
|
3761
|
-
try {
|
|
3762
|
-
const snapshot = toSnapshot(task);
|
|
3763
|
-
const dir = storage.getWorkflowRunsDir();
|
|
3764
|
-
await fs3.mkdir(dir, { recursive: true });
|
|
3765
|
-
await fs3.writeFile(
|
|
3766
|
-
storage.getWorkflowRunSnapshotPath(task.runId),
|
|
3767
|
-
JSON.stringify(snapshot, null, 2),
|
|
3768
|
-
"utf8"
|
|
3769
|
-
);
|
|
3770
|
-
await pruneSnapshots(config, dir);
|
|
3771
|
-
return true;
|
|
3772
|
-
} catch (e) {
|
|
3773
|
-
debugLogger6.warn(`writeWorkflowSnapshot failed for ${task.runId}: ${e}`);
|
|
3774
|
-
return false;
|
|
3775
|
-
}
|
|
3776
|
-
}
|
|
3777
|
-
__name(writeWorkflowSnapshot, "writeWorkflowSnapshot");
|
|
3778
|
-
async function listWorkflowSnapshots(config) {
|
|
3779
|
-
const storage = config.storage;
|
|
3780
|
-
if (!storage) return [];
|
|
3781
|
-
const dir = storage.getWorkflowRunsDir();
|
|
3782
|
-
let files;
|
|
3783
|
-
try {
|
|
3784
|
-
files = (await fs3.readdir(dir)).filter((f) => f.endsWith(".json"));
|
|
3785
|
-
} catch {
|
|
3786
|
-
return [];
|
|
3787
|
-
}
|
|
3788
|
-
const snapshots = [];
|
|
3789
|
-
for (const file of files) {
|
|
3790
|
-
try {
|
|
3791
|
-
const raw = await fs3.readFile(`${dir}/${file}`, "utf8");
|
|
3792
|
-
const parsed = JSON.parse(raw);
|
|
3793
|
-
if (!isWorkflowSnapshot(parsed)) {
|
|
3794
|
-
debugLogger6.warn(`skipping invalid workflow snapshot ${file}`);
|
|
3795
|
-
continue;
|
|
3796
|
-
}
|
|
3797
|
-
snapshots.push(parsed);
|
|
3798
|
-
} catch (e) {
|
|
3799
|
-
debugLogger6.warn(`skipping unparseable snapshot ${file}: ${e}`);
|
|
3800
|
-
}
|
|
3801
|
-
}
|
|
3802
|
-
snapshots.sort((a, b) => (b.startTime ?? 0) - (a.startTime ?? 0));
|
|
3803
|
-
return snapshots;
|
|
3804
|
-
}
|
|
3805
|
-
__name(listWorkflowSnapshots, "listWorkflowSnapshots");
|
|
3806
|
-
async function deleteWorkflowSnapshot(config, runId) {
|
|
3807
|
-
const storage = config.storage;
|
|
3808
|
-
if (!storage || !/^wf_[0-9a-f]+$/.test(runId)) return false;
|
|
3809
|
-
try {
|
|
3810
|
-
await fs3.rm(path3.dirname(storage.getWorkflowRunJournalPath(runId)), {
|
|
3811
|
-
recursive: true,
|
|
3812
|
-
force: true
|
|
3813
|
-
});
|
|
3814
|
-
} catch (error) {
|
|
3815
|
-
debugLogger6.warn(`delete workflow journal failed for ${runId}: ${error}`);
|
|
3816
|
-
return false;
|
|
3817
|
-
}
|
|
3818
|
-
if (!await deleteInlineWorkflowScript(config, runId)) return false;
|
|
3819
|
-
try {
|
|
3820
|
-
await fs3.unlink(storage.getWorkflowRunSnapshotPath(runId));
|
|
3821
|
-
} catch (error) {
|
|
3822
|
-
if (error.code !== "ENOENT") {
|
|
3823
|
-
debugLogger6.warn(`deleteWorkflowSnapshot failed for ${runId}: ${error}`);
|
|
3824
|
-
return false;
|
|
3825
|
-
}
|
|
3826
|
-
}
|
|
3827
|
-
return true;
|
|
3828
|
-
}
|
|
3829
|
-
__name(deleteWorkflowSnapshot, "deleteWorkflowSnapshot");
|
|
3830
|
-
function isRecord(value) {
|
|
3831
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3832
|
-
}
|
|
3833
|
-
__name(isRecord, "isRecord");
|
|
3834
|
-
function isFiniteNumber(value) {
|
|
3835
|
-
return typeof value === "number" && Number.isFinite(value);
|
|
3836
|
-
}
|
|
3837
|
-
__name(isFiniteNumber, "isFiniteNumber");
|
|
3838
|
-
function isOptionalString(value) {
|
|
3839
|
-
return value === void 0 || typeof value === "string";
|
|
3840
|
-
}
|
|
3841
|
-
__name(isOptionalString, "isOptionalString");
|
|
3842
|
-
function isStringArray(value) {
|
|
3843
|
-
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
3844
|
-
}
|
|
3845
|
-
__name(isStringArray, "isStringArray");
|
|
3846
|
-
function isWorkflowMeta(value) {
|
|
3847
|
-
if (value === null) return true;
|
|
3848
|
-
if (!isRecord(value)) return false;
|
|
3849
|
-
if (typeof value["name"] !== "string" || typeof value["description"] !== "string" || !isOptionalString(value["whenToUse"])) {
|
|
3850
|
-
return false;
|
|
3851
|
-
}
|
|
3852
|
-
const phases = value["phases"];
|
|
3853
|
-
return phases === void 0 || Array.isArray(phases) && phases.every(
|
|
3854
|
-
(phase) => isRecord(phase) && typeof phase["title"] === "string" && isOptionalString(phase["detail"]) && isOptionalString(phase["model"])
|
|
3855
|
-
);
|
|
3856
|
-
}
|
|
3857
|
-
__name(isWorkflowMeta, "isWorkflowMeta");
|
|
3858
|
-
function isWorkflowPhaseVisit(value) {
|
|
3859
|
-
return isRecord(value) && typeof value["id"] === "string" && isFiniteNumber(value["index"]) && typeof value["title"] === "string" && isFiniteNumber(value["startedAt"]) && (value["endedAt"] === void 0 || isFiniteNumber(value["endedAt"]));
|
|
3860
|
-
}
|
|
3861
|
-
__name(isWorkflowPhaseVisit, "isWorkflowPhaseVisit");
|
|
3862
|
-
function isWorkflowDispatch(value) {
|
|
3863
|
-
if (!isRecord(value)) return false;
|
|
3864
|
-
const status = value["status"];
|
|
3865
|
-
return typeof value["id"] === "string" && (value["phaseVisitId"] === null || typeof value["phaseVisitId"] === "string") && typeof value["label"] === "string" && typeof value["prompt"] === "string" && isOptionalString(value["subagentId"]) && (status === "queued" || status === "running" || status === "completed" || status === "failed" || status === "cancelled" || status === "cached") && isStringArray(value["dependsOn"]) && isFiniteNumber(value["queuedAt"]) && (value["startedAt"] === void 0 || isFiniteNumber(value["startedAt"])) && (value["endedAt"] === void 0 || isFiniteNumber(value["endedAt"])) && isOptionalString(value["error"]);
|
|
3866
|
-
}
|
|
3867
|
-
__name(isWorkflowDispatch, "isWorkflowDispatch");
|
|
3868
|
-
function hasOnlyKeys(value, keys) {
|
|
3869
|
-
const allowed = new Set(keys);
|
|
3870
|
-
return Object.keys(value).every((key) => allowed.has(key));
|
|
3871
|
-
}
|
|
3872
|
-
__name(hasOnlyKeys, "hasOnlyKeys");
|
|
3873
|
-
function isWorkflowEvent(value) {
|
|
3874
|
-
if (!isRecord(value) || typeof value["id"] !== "string" || !isFiniteNumber(value["at"]) || typeof value["type"] !== "string") {
|
|
3875
|
-
return false;
|
|
3876
|
-
}
|
|
3877
|
-
const base = ["id", "type", "at"];
|
|
3878
|
-
switch (value["type"]) {
|
|
3879
|
-
case "phase-started":
|
|
3880
|
-
return hasOnlyKeys(value, [...base, "phaseVisitId", "title"]) && typeof value["phaseVisitId"] === "string" && typeof value["title"] === "string";
|
|
3881
|
-
case "phase-completed":
|
|
3882
|
-
return hasOnlyKeys(value, [...base, "phaseVisitId"]) && typeof value["phaseVisitId"] === "string";
|
|
3883
|
-
case "dispatch-queued":
|
|
3884
|
-
case "dispatch-started":
|
|
3885
|
-
case "dispatch-completed":
|
|
3886
|
-
case "dispatch-cancelled":
|
|
3887
|
-
case "dispatch-cached":
|
|
3888
|
-
return hasOnlyKeys(value, [...base, "dispatchId"]) && typeof value["dispatchId"] === "string";
|
|
3889
|
-
case "dispatch-failed":
|
|
3890
|
-
return hasOnlyKeys(value, [...base, "dispatchId", "error"]) && typeof value["dispatchId"] === "string" && typeof value["error"] === "string";
|
|
3891
|
-
case "log":
|
|
3892
|
-
return hasOnlyKeys(value, [...base, "message"]) && typeof value["message"] === "string";
|
|
3893
|
-
case "approval-requested":
|
|
3894
|
-
case "approval-settled":
|
|
3895
|
-
return hasOnlyKeys(value, [...base, "name", "dispatchId"]) && typeof value["name"] === "string" && isOptionalString(value["dispatchId"]);
|
|
3896
|
-
case "workflow-completed":
|
|
3897
|
-
case "workflow-cancelled":
|
|
3898
|
-
return hasOnlyKeys(value, base);
|
|
3899
|
-
case "workflow-failed":
|
|
3900
|
-
return hasOnlyKeys(value, [...base, "error"]) && typeof value["error"] === "string";
|
|
3901
|
-
default:
|
|
3902
|
-
return false;
|
|
3903
|
-
}
|
|
3904
|
-
}
|
|
3905
|
-
__name(isWorkflowEvent, "isWorkflowEvent");
|
|
3906
|
-
function isWorkflowSnapshot(value) {
|
|
3907
|
-
if (!isRecord(value)) return false;
|
|
3908
|
-
const status = value["status"];
|
|
3909
|
-
const phaseVisits = value["phaseVisits"];
|
|
3910
|
-
const dispatches = value["dispatches"];
|
|
3911
|
-
const events = value["events"];
|
|
3912
|
-
const perPhaseTokens = value["perPhaseTokens"];
|
|
3913
|
-
return typeof value["runId"] === "string" && value["runId"].length > 0 && isOptionalString(value["toolUseId"]) && isOptionalString(value["description"]) && isOptionalString(value["workflowName"]) && isOptionalString(value["sourceRunId"]) && (value["startMode"] === void 0 || value["startMode"] === "retry" || value["startMode"] === "rerun") && isWorkflowMeta(value["meta"]) && (status === "completed" || status === "failed" || status === "cancelled") && typeof value["script"] === "string" && isOptionalString(value["scriptPath"]) && isStringArray(value["phases"]) && (phaseVisits === void 0 || Array.isArray(phaseVisits) && phaseVisits.every(isWorkflowPhaseVisit)) && (dispatches === void 0 || Array.isArray(dispatches) && dispatches.every(isWorkflowDispatch)) && (events === void 0 || Array.isArray(events) && events.every(isWorkflowEvent)) && isFiniteNumber(value["agentsDispatched"]) && isFiniteNumber(value["agentsCompleted"]) && (value["agentsRespawned"] === void 0 || isFiniteNumber(value["agentsRespawned"])) && isFiniteNumber(value["tokensSpent"]) && (value["tokenBudgetTotal"] === null || isFiniteNumber(value["tokenBudgetTotal"])) && Array.isArray(perPhaseTokens) && perPhaseTokens.every(
|
|
3914
|
-
(entry) => Array.isArray(entry) && entry.length === 2 && (entry[0] === null || typeof entry[0] === "string") && isFiniteNumber(entry[1])
|
|
3915
|
-
) && isStringArray(value["recentLogs"]) && isFiniteNumber(value["startTime"]) && (value["endTime"] === void 0 || isFiniteNumber(value["endTime"])) && isOptionalString(value["error"]);
|
|
3916
|
-
}
|
|
3917
|
-
__name(isWorkflowSnapshot, "isWorkflowSnapshot");
|
|
3918
|
-
async function pruneSnapshots(config, dir) {
|
|
3919
|
-
let files;
|
|
3920
|
-
try {
|
|
3921
|
-
files = (await fs3.readdir(dir)).filter((f) => f.endsWith(".json"));
|
|
3922
|
-
} catch {
|
|
3923
|
-
return;
|
|
3924
|
-
}
|
|
3925
|
-
if (files.length <= MAX_RETAINED_SNAPSHOTS) return;
|
|
3926
|
-
const stats = await Promise.all(
|
|
3927
|
-
files.map(async (f) => {
|
|
3928
|
-
try {
|
|
3929
|
-
const s = await fs3.stat(`${dir}/${f}`);
|
|
3930
|
-
return { f, mtime: s.mtimeMs };
|
|
3931
|
-
} catch {
|
|
3932
|
-
return { f, mtime: 0 };
|
|
3933
|
-
}
|
|
3934
|
-
})
|
|
3935
|
-
);
|
|
3936
|
-
stats.sort((a, b) => a.mtime - b.mtime);
|
|
3937
|
-
const toPrune = stats.slice(0, stats.length - MAX_RETAINED_SNAPSHOTS);
|
|
3938
|
-
const registry = config.getWorkflowRunRegistry?.();
|
|
3939
|
-
const protectedRunIds = /* @__PURE__ */ new Set([
|
|
3940
|
-
...(registry?.list() ?? []).filter((entry) => isActiveWorkflowStatus(entry.status)).map((entry) => entry.runId),
|
|
3941
|
-
...registry?.listStartingRunIds() ?? []
|
|
3942
|
-
]);
|
|
3943
|
-
await Promise.all(
|
|
3944
|
-
toPrune.map((s) => {
|
|
3945
|
-
const runId = s.f.replace(/\.json$/, "");
|
|
3946
|
-
const isRunDir = /^wf_[0-9a-f]+$/.test(runId);
|
|
3947
|
-
const deleteArtifacts = isRunDir && !protectedRunIds.has(runId) && !isWorkflowRunPersistenceActive(config, runId);
|
|
3948
|
-
return Promise.all([
|
|
3949
|
-
fs3.unlink(`${dir}/${s.f}`).catch(
|
|
3950
|
-
(e) => debugLogger6.warn(`prune unlink failed for ${s.f}: ${e}`)
|
|
3951
|
-
),
|
|
3952
|
-
...deleteArtifacts ? [
|
|
3953
|
-
fs3.rm(
|
|
3954
|
-
path3.dirname(config.storage.getWorkflowRunJournalPath(runId)),
|
|
3955
|
-
{ recursive: true, force: true }
|
|
3956
|
-
).catch(
|
|
3957
|
-
(e) => debugLogger6.warn(
|
|
3958
|
-
`prune journal dir failed for ${runId}: ${e}`
|
|
3959
|
-
)
|
|
3960
|
-
),
|
|
3961
|
-
deleteInlineWorkflowScript(config, runId)
|
|
3962
|
-
] : []
|
|
3963
|
-
]);
|
|
3964
|
-
})
|
|
3965
|
-
);
|
|
3966
|
-
}
|
|
3967
|
-
__name(pruneSnapshots, "pruneSnapshots");
|
|
3968
|
-
|
|
3969
|
-
// packages/core/src/skills/workflow-authoring-skill.ts
|
|
3970
|
-
init_esbuild_shims();
|
|
3971
|
-
import { readFileSync } from "node:fs";
|
|
3972
|
-
import * as path4 from "node:path";
|
|
3973
|
-
var debugLogger7 = createDebugLogger("WORKFLOW_AUTHORING_SKILL");
|
|
3974
|
-
var WORKFLOW_AUTHORING_SKILL_NAME = "workflow-authoring";
|
|
3975
|
-
var cachedReference;
|
|
3976
|
-
function referencePath() {
|
|
3977
|
-
return path4.join(
|
|
3978
|
-
resolveBundleDir(import.meta.url),
|
|
3979
|
-
"bundled",
|
|
3980
|
-
WORKFLOW_AUTHORING_SKILL_NAME,
|
|
3981
|
-
"SKILL.md"
|
|
3982
|
-
);
|
|
3983
|
-
}
|
|
3984
|
-
__name(referencePath, "referencePath");
|
|
3985
|
-
function readWorkflowAuthoringReference() {
|
|
3986
|
-
if (cachedReference !== void 0) return cachedReference;
|
|
3987
|
-
try {
|
|
3988
|
-
const filePath = referencePath();
|
|
3989
|
-
const parsed = parseSkillContent(readFileSync(filePath, "utf8"), filePath);
|
|
3990
|
-
cachedReference = { body: parsed.body, baseDir: path4.dirname(filePath) };
|
|
3991
|
-
} catch (error) {
|
|
3992
|
-
debugLogger7.warn(`cannot read the workflow-authoring reference: ${error}`);
|
|
3993
|
-
cachedReference = null;
|
|
3994
|
-
}
|
|
3995
|
-
return cachedReference;
|
|
3996
|
-
}
|
|
3997
|
-
__name(readWorkflowAuthoringReference, "readWorkflowAuthoringReference");
|
|
3998
|
-
function resolveWorkflowAuthoringRoute(config) {
|
|
3999
|
-
try {
|
|
4000
|
-
if (config.getDisabledSkillLevels?.()?.has("bundled")) return "withheld";
|
|
4001
|
-
if (config.isSkillEnabled?.({
|
|
4002
|
-
name: WORKFLOW_AUTHORING_SKILL_NAME,
|
|
4003
|
-
level: "bundled"
|
|
4004
|
-
}) === false) {
|
|
4005
|
-
return "withheld";
|
|
4006
|
-
}
|
|
4007
|
-
if (!config.getSkillManager?.()) return "inline";
|
|
4008
|
-
const registry = config.getToolRegistry?.();
|
|
4009
|
-
const toolNames = registry?.getAllToolNames?.();
|
|
4010
|
-
if (!Array.isArray(toolNames)) return "skill";
|
|
4011
|
-
if (!toolNames.includes(ToolNames.SKILL)) return "inline";
|
|
4012
|
-
if (isToolDeferredBehindToolSearch(config, ToolNames.SKILL)) {
|
|
4013
|
-
return toolNames.includes(ToolNames.TOOL_SEARCH) ? "skill-via-tool-search" : "inline";
|
|
4014
|
-
}
|
|
4015
|
-
return "skill";
|
|
4016
|
-
} catch (error) {
|
|
4017
|
-
debugLogger7.warn(`cannot resolve the workflow-authoring route: ${error}`);
|
|
4018
|
-
return "skill";
|
|
4019
|
-
}
|
|
4020
|
-
}
|
|
4021
|
-
__name(resolveWorkflowAuthoringRoute, "resolveWorkflowAuthoringRoute");
|
|
4022
|
-
function resolveWorkflowAuthoringSurface(config) {
|
|
4023
|
-
const route = resolveWorkflowAuthoringRoute(config);
|
|
4024
|
-
switch (route) {
|
|
4025
|
-
case "skill":
|
|
4026
|
-
return "pointer";
|
|
4027
|
-
case "skill-via-tool-search":
|
|
4028
|
-
return "pointer-via-tool-search";
|
|
4029
|
-
case "withheld":
|
|
4030
|
-
return "withheld";
|
|
4031
|
-
case "inline":
|
|
4032
|
-
return readWorkflowAuthoringReference() ? "inline" : "pointer";
|
|
4033
|
-
default: {
|
|
4034
|
-
const unhandled = route;
|
|
4035
|
-
return unhandled;
|
|
4036
|
-
}
|
|
4037
|
-
}
|
|
4038
|
-
}
|
|
4039
|
-
__name(resolveWorkflowAuthoringSurface, "resolveWorkflowAuthoringSurface");
|
|
4040
|
-
function isToolDeferredBehindToolSearch(config, name) {
|
|
4041
|
-
if (!config.getToolRegistry?.()?.isPermissionDeferred?.(name)) return false;
|
|
4042
|
-
return !config.getVisibleTools?.()?.has(name);
|
|
4043
|
-
}
|
|
4044
|
-
__name(isToolDeferredBehindToolSearch, "isToolDeferredBehindToolSearch");
|
|
4045
|
-
function isToolHiddenBehindToolSearch(config, name) {
|
|
4046
|
-
if (!isToolDeferredBehindToolSearch(config, name)) return false;
|
|
4047
|
-
return !config.getToolRegistry?.()?.isDeferredToolRevealed?.(name);
|
|
4048
|
-
}
|
|
4049
|
-
__name(isToolHiddenBehindToolSearch, "isToolHiddenBehindToolSearch");
|
|
4050
|
-
function toolSearchRevealSentence(toolDisplayName) {
|
|
4051
|
-
return `If the ${toolDisplayName} tool is not in your tool list, reveal it with ToolSearch first.`;
|
|
4052
|
-
}
|
|
4053
|
-
__name(toolSearchRevealSentence, "toolSearchRevealSentence");
|
|
4054
|
-
|
|
4055
|
-
// packages/core/src/agents/runtime/review-workflow.ts
|
|
4056
|
-
init_esbuild_shims();
|
|
4057
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
4058
|
-
import { promises as fs4 } from "node:fs";
|
|
4059
|
-
import path5 from "node:path";
|
|
4060
|
-
var debugLogger8 = createDebugLogger("REVIEW_WORKFLOW");
|
|
4061
|
-
function resolveReviewWorkflowConcurrency(env = process.env) {
|
|
4062
|
-
const configured = parsePositiveIntegerEnv(env[MAX_WORKFLOW_CONCURRENCY_ENV], 0) || parsePositiveIntegerEnv(env["QWEN_CODE_MAX_TOOL_CONCURRENCY"], 10);
|
|
4063
|
-
return resolveConcurrencyLimit({
|
|
4064
|
-
[MAX_WORKFLOW_CONCURRENCY_ENV]: String(configured)
|
|
4065
|
-
});
|
|
4066
|
-
}
|
|
4067
|
-
__name(resolveReviewWorkflowConcurrency, "resolveReviewWorkflowConcurrency");
|
|
4068
|
-
async function resolveReviewWorkflowLimits(scriptPath, generatedDir, scriptContent, env = process.env, nowMs = Date.now()) {
|
|
4069
|
-
let canonicalScript;
|
|
4070
|
-
try {
|
|
4071
|
-
if ((await fs4.lstat(generatedDir)).isSymbolicLink()) return void 0;
|
|
4072
|
-
const generatedRoot = await fs4.realpath(generatedDir);
|
|
4073
|
-
canonicalScript = await fs4.realpath(scriptPath);
|
|
4074
|
-
if (!canonicalScript.startsWith(path5.join(generatedRoot, "review") + path5.sep)) {
|
|
4075
|
-
return void 0;
|
|
4076
|
-
}
|
|
4077
|
-
} catch (error) {
|
|
4078
|
-
if (error.code !== "ENOENT") {
|
|
4079
|
-
debugLogger8.warn(
|
|
4080
|
-
"Cannot classify review workflow; using generic limits:",
|
|
4081
|
-
error
|
|
4082
|
-
);
|
|
4083
|
-
}
|
|
4084
|
-
return void 0;
|
|
4085
|
-
}
|
|
4086
|
-
const digest = /^qwen-review-[a-f0-9]{10}-([a-f0-9]{64})\.js$/.exec(
|
|
4087
|
-
path5.basename(canonicalScript)
|
|
4088
|
-
)?.[1];
|
|
4089
|
-
if (!digest || createHash2("sha256").update(scriptContent).digest("hex") !== digest) {
|
|
4090
|
-
return void 0;
|
|
4091
|
-
}
|
|
4092
|
-
const seconds = Number(env["QWEN_CODE_MAX_WORKFLOW_SECONDS"]);
|
|
4093
|
-
let maxWallClockMs = (Number.isFinite(seconds) && seconds > 0 ? seconds : 6 * 60 * 60) * 1e3;
|
|
4094
|
-
const deadline = Number(env["QWEN_REVIEW_DEADLINE_EPOCH"]);
|
|
4095
|
-
if (Number.isFinite(deadline) && deadline > 0) {
|
|
4096
|
-
const rawFloor = env["QWEN_REVIEW_DEADLINE_COMPOSE_FLOOR_SECONDS"];
|
|
4097
|
-
const floor = Number(rawFloor);
|
|
4098
|
-
const floorSeconds = rawFloor?.trim() && Number.isFinite(floor) && floor >= 0 ? floor : 1200;
|
|
4099
|
-
const remainingMs = deadline * 1e3 - nowMs - floorSeconds * 1e3;
|
|
4100
|
-
if (remainingMs <= 0) {
|
|
4101
|
-
throw new Error(
|
|
4102
|
-
"Review workflow was not launched: the review deadline has reached the compose reserve floor. Recover completed findings and compose now."
|
|
4103
|
-
);
|
|
4104
|
-
}
|
|
4105
|
-
maxWallClockMs = Math.min(maxWallClockMs, remainingMs);
|
|
4106
|
-
}
|
|
4107
|
-
return {
|
|
4108
|
-
subagent: {
|
|
4109
|
-
max_turns: resolveSubagentMaxTurns(env, 500),
|
|
4110
|
-
max_time_minutes: resolveSubagentMaxTimeMinutes(env, 100)
|
|
4111
|
-
},
|
|
4112
|
-
concurrency: resolveReviewWorkflowConcurrency(env),
|
|
4113
|
-
maxWallClockMs
|
|
4114
|
-
};
|
|
4115
|
-
}
|
|
4116
|
-
__name(resolveReviewWorkflowLimits, "resolveReviewWorkflowLimits");
|
|
4117
|
-
|
|
4118
|
-
export {
|
|
4119
|
-
extractAndStripMeta,
|
|
4120
|
-
compileWorkflowScript,
|
|
4121
|
-
describeWorkflowCompileError,
|
|
4122
|
-
MAX_TOKENS_PER_WORKFLOW_ENV,
|
|
4123
|
-
resolveMaxTokensPerWorkflow,
|
|
4124
|
-
WorkflowBudgetImpl,
|
|
4125
|
-
validateWorkflowName,
|
|
4126
|
-
isSymlinkedRoot,
|
|
4127
|
-
listSavedWorkflows,
|
|
4128
|
-
resolveSavedWorkflowScript,
|
|
4129
|
-
saveWorkflowScript,
|
|
4130
|
-
persistInlineWorkflowScript,
|
|
4131
|
-
deleteInlineWorkflowScript,
|
|
4132
|
-
WorkflowJournal,
|
|
4133
|
-
WorkflowDispatchScheduler,
|
|
4134
|
-
DEFAULT_MAX_AGENTS_PER_RUN,
|
|
4135
|
-
MAX_WORKFLOW_AGENTS_ENV,
|
|
4136
|
-
MAX_WORKFLOW_CONCURRENCY_ENV,
|
|
4137
|
-
resolveConcurrencyLimit,
|
|
4138
|
-
DEFAULT_WORKFLOW_SUBAGENT_MAX_TURNS,
|
|
4139
|
-
DEFAULT_WORKFLOW_SUBAGENT_MAX_TIME_MINUTES,
|
|
4140
|
-
WORKFLOW_SUBAGENT_MAX_TURNS_ENV,
|
|
4141
|
-
WORKFLOW_SUBAGENT_MAX_MINUTES_ENV,
|
|
4142
|
-
WorkflowExecutionError,
|
|
4143
|
-
createProductionDispatch,
|
|
4144
|
-
WorkflowOrchestrator,
|
|
4145
|
-
MAX_RETAINED_SNAPSHOTS,
|
|
4146
|
-
toSnapshot,
|
|
4147
|
-
writeWorkflowSnapshot,
|
|
4148
|
-
listWorkflowSnapshots,
|
|
4149
|
-
deleteWorkflowSnapshot,
|
|
4150
|
-
resolveReviewWorkflowConcurrency,
|
|
4151
|
-
resolveReviewWorkflowLimits,
|
|
4152
|
-
WORKFLOW_AUTHORING_SKILL_NAME,
|
|
4153
|
-
readWorkflowAuthoringReference,
|
|
4154
|
-
resolveWorkflowAuthoringSurface,
|
|
4155
|
-
isToolHiddenBehindToolSearch,
|
|
4156
|
-
toolSearchRevealSentence
|
|
4157
|
-
};
|
|
4158
|
-
/**
|
|
4159
|
-
* @license
|
|
4160
|
-
* Copyright 2025 Google LLC
|
|
4161
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
4162
|
-
*/
|
|
4163
|
-
/**
|
|
4164
|
-
* @license
|
|
4165
|
-
* Copyright 2025 Qwen
|
|
4166
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
4167
|
-
*/
|
|
4168
|
-
/**
|
|
4169
|
-
* @license
|
|
4170
|
-
* Copyright 2026 Qwen Team
|
|
4171
|
-
* SPDX-License-Identifier: Apache-2.0
|
|
4172
|
-
*/
|