@qwen-code/qwen-code 0.19.3 → 0.19.4-nightly.20260702.46814e4f1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled/loop/SKILL.md +22 -3
- package/bundled/loop/SKILL.test.ts +20 -0
- package/bundled/loop/autonomous-loop.test.ts +59 -0
- package/bundled/loop/autonomous-loop.ts +102 -0
- package/bundled/loop/loop-task-file.test.ts +1064 -0
- package/bundled/loop/loop-task-file.ts +404 -0
- package/bundled/loop/loop-tick-resolver.test.ts +906 -0
- package/bundled/loop/loop-tick-resolver.ts +416 -0
- package/bundled/qc-helper/SKILL.md +12 -5
- package/bundled/qc-helper/docs/configuration/auth.md +1 -1
- package/bundled/qc-helper/docs/configuration/settings.md +62 -32
- package/bundled/qc-helper/docs/features/approval-mode.md +3 -0
- package/bundled/qc-helper/docs/features/auto-mode.md +40 -0
- package/bundled/qc-helper/docs/features/channels/overview.md +81 -18
- package/bundled/qc-helper/docs/features/channels/plugins.md +1 -1
- package/bundled/qc-helper/docs/features/code-review.md +17 -61
- package/bundled/qc-helper/docs/features/commands.md +42 -40
- package/bundled/qc-helper/docs/features/headless.md +19 -18
- package/bundled/qc-helper/docs/features/hooks.md +6 -1
- package/bundled/qc-helper/docs/features/mcp.md +3 -1
- package/bundled/qc-helper/docs/features/memory.md +1 -1
- package/bundled/qc-helper/docs/features/scheduled-tasks.md +10 -0
- package/bundled/qc-helper/docs/features/status-line.md +3 -3
- package/bundled/qc-helper/docs/ide-integration/ide-integration.md +4 -4
- package/bundled/qc-helper/docs/qwen-serve.md +53 -0
- package/bundled/qc-helper/docs/support/troubleshooting.md +11 -0
- package/bundled/review/DESIGN.md +21 -26
- package/bundled/review/SKILL.md +45 -129
- package/chunks/MaxSizedBox-GHGRQ2OS.js +69 -0
- package/chunks/{StandaloneSessionPicker-Z52JTYST.js → StandaloneSessionPicker-G7YTTM27.js} +49 -42
- package/chunks/{acpAgent-7RZUOWOM.js → acpAgent-SDOILBSU.js} +1325 -365
- package/chunks/{agent-4FG7WTHJ.js → agent-NTSXGV4L.js} +31 -25
- package/chunks/agent-headless-WYURMSAB.js +62 -0
- package/chunks/{anthropicContentGenerator-PFITVL7Y.js → anthropicContentGenerator-WL3LJOUB.js} +110 -31
- package/chunks/{artifact-tool-D2X7G3C3.js → artifact-tool-W76WC2JX.js} +3 -3
- package/chunks/{askUserQuestion-UGETICJQ.js → askUserQuestion-J3P6TH3V.js} +2 -2
- package/chunks/bridge-AMOL4W4P.js +73 -0
- package/chunks/{ca-IUFWESZL.js → ca-HCMB2I54.js} +10 -9
- package/chunks/channel-worker-supervisor-C3IB63V7.js +659 -0
- package/chunks/{chunk-STUWFKGD.js → chunk-26JIREA5.js} +466 -543
- package/chunks/{chunk-SLNKLLEO.js → chunk-2MRV62AU.js} +37 -8
- package/chunks/{chunk-ODLIHNOY.js → chunk-2U4HUMOL.js} +1 -1
- package/chunks/{chunk-CKQA6AWF.js → chunk-4D73HUOP.js} +1 -1
- package/chunks/{chunk-M2U5NM24.js → chunk-54YZOP7P.js} +309 -72
- package/chunks/{chunk-J7FA27RO.js → chunk-5LKL3PAU.js} +3 -3
- package/chunks/chunk-5P5XGNYH.js +93 -0
- package/chunks/{chunk-UFLGERMB.js → chunk-6JE32BRB.js} +178 -124
- package/chunks/{chunk-FKAVULLM.js → chunk-75AVTQJ2.js} +74 -10
- package/chunks/{chunk-I2LYBGNI.js → chunk-7IOMIYOE.js} +1 -1
- package/chunks/{chunk-4LUZAKMK.js → chunk-7KXAGQJ2.js} +1 -1
- package/chunks/{chunk-BXSHL2SF.js → chunk-7LUOQJ3D.js} +1 -1
- package/chunks/{chunk-YAINVQ3Q.js → chunk-A5OPLHFM.js} +18693 -17055
- package/chunks/chunk-AHNXYU4O.js +24 -0
- package/chunks/{chunk-UX4SGCGN.js → chunk-AVQDCKNF.js} +96 -22
- package/chunks/{chunk-HYCC3XM3.js → chunk-B236XPGF.js} +1 -1
- package/chunks/chunk-B6SZLQAZ.js +3523 -0
- package/chunks/{chunk-5DWXLGOM.js → chunk-BJBWRCSK.js} +1 -1
- package/chunks/chunk-BPFJQGY7.js +193 -0
- package/chunks/{chunk-SKQZZVAV.js → chunk-BZHLWC6T.js} +2 -2
- package/chunks/{chunk-U3KSMM7G.js → chunk-D5KISTLW.js} +339 -383
- package/chunks/{chunk-GT2FGICD.js → chunk-DE7YXUR7.js} +1 -1
- package/chunks/{chunk-BUUQ5HPX.js → chunk-DG573EOO.js} +1 -1
- package/chunks/{chunk-VVLOGDB6.js → chunk-DX3TKXNT.js} +2 -2
- package/chunks/chunk-E5A7LHNN.js +51 -0
- package/chunks/{chunk-3PJXIDKI.js → chunk-E6US47KI.js} +3 -27
- package/chunks/{chunk-QBZ22PQX.js → chunk-EWALXUAD.js} +3 -1
- package/chunks/{chunk-PDE7CSDS.js → chunk-FXKI7OW6.js} +2 -2
- package/chunks/{chunk-F3ZYSEY6.js → chunk-FY4TT4L4.js} +3 -3
- package/chunks/{chunk-QLDPRILK.js → chunk-G7OQ3RFJ.js} +3 -3
- package/chunks/{chunk-R2BXK7SX.js → chunk-GHOTR7HL.js} +6 -1
- package/chunks/{chunk-KMGMKW5Q.js → chunk-GLPV75ES.js} +9 -3
- package/chunks/{chunk-LD2XBG6Z.js → chunk-GO6LNQXT.js} +1 -39
- package/chunks/{chunk-CNLEDYE3.js → chunk-H6TNLOLM.js} +5 -5
- package/chunks/{chunk-JVHEVCAU.js → chunk-HLW4EGKR.js} +31 -6
- package/chunks/{chunk-I4H7PRWQ.js → chunk-HTWHAN44.js} +1 -1
- package/chunks/{chunk-QE6KJYLT.js → chunk-IPYE5BIS.js} +123 -25
- package/chunks/{chunk-EJYJYJZ2.js → chunk-IRJWHZWU.js} +3 -70
- package/chunks/{chunk-EDMZZFDU.js → chunk-J3YPTTNL.js} +930 -798
- package/chunks/{chunk-GGBIN5K7.js → chunk-JGAJMIJZ.js} +1 -1
- package/chunks/{chunk-N4EJFBN5.js → chunk-JVLP5US5.js} +427 -263
- package/chunks/{chunk-R5OP2ATH.js → chunk-JZG7VB6C.js} +11 -11
- package/chunks/{chunk-QI7GOGJR.js → chunk-KKQWC6WS.js} +1 -1
- package/chunks/{chunk-ADCIHOQZ.js → chunk-KX5QZRW2.js} +142 -30
- package/chunks/{chunk-SBBN4BWR.js → chunk-L7OJ3ZO3.js} +1 -1
- package/chunks/{chunk-NDOC6M5S.js → chunk-L7QPM2YD.js} +2 -2
- package/chunks/{chunk-BVSVIN45.js → chunk-LKWAVAB5.js} +3 -3
- package/chunks/{chunk-T4T2FNV4.js → chunk-LOW52WYZ.js} +2 -2
- package/chunks/{chunk-4QK3O43S.js → chunk-LU7BIVWC.js} +4 -4
- package/chunks/{chunk-ZP6UKOPB.js → chunk-LWGLBVWA.js} +1 -1
- package/chunks/{chunk-L7OKCI7T.js → chunk-M7BLWURA.js} +15 -1
- package/chunks/{chunk-PR32FRT6.js → chunk-MIM62IXS.js} +3 -3
- package/chunks/{chunk-RD6YJJJ6.js → chunk-MOUXUCXQ.js} +1 -1
- package/chunks/{chunk-VBZ452JT.js → chunk-MSNGFH6L.js} +494 -114
- package/chunks/{chunk-BX5YO7UW.js → chunk-MTNTVOJR.js} +542 -370
- package/chunks/chunk-MX2YXRER.js +44 -0
- package/chunks/chunk-NIOPRZNN.js +27 -0
- package/chunks/{chunk-SMYQZVZ3.js → chunk-NOGSPTLJ.js} +1 -1
- package/chunks/{chunk-DDMOU62Q.js → chunk-NPQWQYU6.js} +1 -1
- package/chunks/{chunk-MNF6S5PR.js → chunk-P5THQV6A.js} +4 -4
- package/chunks/{chunk-6V4NZFUS.js → chunk-PCYOT4PK.js} +5 -5
- package/chunks/{chunk-CF6BB3LT.js → chunk-PPFWEV7G.js} +10 -1
- package/chunks/{chunk-FXKVLFFY.js → chunk-PSFDB5FZ.js} +3 -3
- package/chunks/{chunk-XJYUPHRC.js → chunk-QOMAHFYI.js} +3 -3
- package/chunks/{chunk-J37FGIOA.js → chunk-QXPI7FEC.js} +38 -4
- package/chunks/{chunk-RRFZXMYP.js → chunk-QZDBHNMV.js} +691 -164
- package/chunks/{chunk-S7AKE7HL.js → chunk-R6RI2HIC.js} +63 -9
- package/chunks/{chunk-HZT4UCFB.js → chunk-RB7S52NJ.js} +4 -4
- package/chunks/{chunk-J33ZR6OJ.js → chunk-RK22ACPP.js} +16 -16
- package/chunks/chunk-RM3PGSGL.js +66 -0
- package/chunks/chunk-RNBYOUGV.js +42 -0
- package/chunks/chunk-S6RLAIUR.js +23 -0
- package/chunks/{chunk-3TV3HUWZ.js → chunk-SCYK2YLI.js} +81 -30
- package/chunks/{chunk-HPZTOP4I.js → chunk-SEKRT4SD.js} +133 -23
- package/chunks/chunk-SEZC2725.js +36 -0
- package/chunks/chunk-SIUQ3YYX.js +78 -0
- package/chunks/{chunk-O2VBQ3Y2.js → chunk-SJBIFVD2.js} +2 -2
- package/chunks/chunk-SYCJMSIJ.js +82 -0
- package/chunks/{chunk-JE5FVRPC.js → chunk-T22H4V4R.js} +48 -23
- package/chunks/{chunk-3227DZO4.js → chunk-T42ACHFI.js} +1 -1
- package/chunks/{chunk-UO5GVXQE.js → chunk-TENOUWY2.js} +3 -3
- package/chunks/{chunk-JOLIREYR.js → chunk-TOAUDIS7.js} +1 -1
- package/chunks/{chunk-QNKBEJO2.js → chunk-UA6XBZT7.js} +2776 -1289
- package/chunks/{chunk-O75BHFQW.js → chunk-UQTYK5SS.js} +2 -2
- package/chunks/{chunk-Y6YMRS3U.js → chunk-UY2TCIZS.js} +17 -3
- package/chunks/{chunk-FNJ7BDPH.js → chunk-UY7ZAYTY.js} +10 -7
- package/chunks/{chunk-UYT3MWEB.js → chunk-UYHWM24C.js} +30 -2
- package/chunks/{chunk-SMXULRAN.js → chunk-VXGPYF7U.js} +1 -1
- package/chunks/{chunk-BTLJ2MF5.js → chunk-W7CG5HPC.js} +3 -3
- package/chunks/{chunk-JJXWTVSK.js → chunk-XHZKDN7M.js} +34 -1
- package/chunks/{chunk-K5XU6E4T.js → chunk-XKSCTXPK.js} +2 -2
- package/chunks/chunk-Y4LITCRB.js +367 -0
- package/chunks/chunk-YHTNWGRR.js +239 -0
- package/chunks/{chunk-BKXBARJZ.js → chunk-YPCPZSZB.js} +1 -1
- package/chunks/{chunk-QWFNTRH4.js → chunk-YSM24SCE.js} +1 -1
- package/chunks/chunk-YTFBURQD.js +313 -0
- package/chunks/{chunk-NL4C3H6C.js → chunk-YXK4GPYO.js} +1 -1
- package/chunks/{chunk-RYLJU2DY.js → chunk-ZN5T4BHI.js} +1 -1
- package/chunks/{computer-use-JGB36FDW.js → computer-use-MTSVPLPY.js} +34 -28
- package/chunks/contextCommand-VGJ3BPTK.js +67 -0
- package/chunks/{cron-create-SKEY55RG.js → cron-create-6YTCOX6P.js} +4 -4
- package/chunks/{cron-delete-I3NAT32Z.js → cron-delete-E3NIFSUK.js} +4 -4
- package/chunks/{cron-list-6XRRL6TH.js → cron-list-Q64MNQNE.js} +4 -4
- package/chunks/daemon-EEB2RYOL.js +8453 -0
- package/chunks/daemon-status-provider-H5SNMK42.js +71 -0
- package/chunks/{de-EOW5PVHF.js → de-PLLFDERI.js} +10 -9
- package/chunks/{dist-NHEYYB2B.js → dist-ADMIQGHI.js} +2 -2
- package/chunks/{dist-J37VXPLD.js → dist-BCQ7INM7.js} +40 -15
- package/chunks/{dist-663NZ6MI.js → dist-KNZFCY5F.js} +116 -11
- package/chunks/{dist-LB6OGPVM.js → dist-LHUWC3K6.js} +26 -5
- package/chunks/{dist-PQPZ5Q4N.js → dist-WBBZ76JZ.js} +20 -9
- package/chunks/{earlyInputCapture-44RISVVJ.js → earlyInputCapture-U4ZIQSQU.js} +32 -26
- package/chunks/{edit-NN6GM5DT.js → edit-SYIQ2KBI.js} +41 -38
- package/chunks/{en-HPWEJBTR.js → en-M4EBUBNU.js} +32 -10
- package/chunks/{enter-worktree-PB3O2C7D.js → enter-worktree-HPPOO244.js} +31 -25
- package/chunks/{enterPlanMode-YQFNNSUA.js → enterPlanMode-56YYO52U.js} +41 -25
- package/chunks/{errors-LS7XU5DZ.js → errors-EO4SXCFP.js} +33 -27
- package/chunks/{exit-worktree-E2FTNSBK.js → exit-worktree-UDM7E6XH.js} +31 -25
- package/chunks/{exitPlanMode-GLYK2BUB.js → exitPlanMode-M6P3AWLJ.js} +149 -33
- package/chunks/{fast-path-WQ6M7LI7.js → fast-path-42P4QYWI.js} +33 -14
- package/chunks/{fast-path-settings-OAGU3X5H.js → fast-path-settings-UQLRFDQP.js} +2 -2
- package/chunks/{fr-BN5NKSAC.js → fr-MBMNQSQ6.js} +10 -9
- package/chunks/{gemini-2YFLVA2M.js → gemini-NRLXS5J7.js} +84 -70
- package/chunks/{geminiContentGenerator-XV6SV6CD.js → geminiContentGenerator-DLJPYYPA.js} +41 -5
- package/chunks/{glob-2H6CDDMK.js → glob-JPY3F74Y.js} +31 -25
- package/chunks/{grep-VXIUXM3C.js → grep-HW23YES5.js} +31 -25
- package/chunks/initializer-HWOZTC4A.js +67 -0
- package/chunks/{ja-7DEUB2TS.js → ja-HFV5QCHR.js} +10 -9
- package/chunks/{keychain-token-storage-YLMDLKXT.js → keychain-token-storage-GPAKRDKQ.js} +2 -2
- package/chunks/list-OZGAGRDG.js +71 -0
- package/chunks/loadedSettingsAdapter-RUTZVVIA.js +66 -0
- package/chunks/{loop-wakeup-CITGGQHA.js → loop-wakeup-G2EHBRGR.js} +5 -5
- package/chunks/{ls-R5WP4BGZ.js → ls-IDHUVSAG.js} +4 -4
- package/chunks/{lsp-GSICJMMW.js → lsp-FSRYXGFT.js} +2 -2
- package/chunks/{monitor-HKLRS2HQ.js → monitor-QVWEIXU3.js} +33 -26
- package/chunks/nonInteractiveCli-RV2WYPML.js +109 -0
- package/chunks/{notebook-edit-Y6SKSGTT.js → notebook-edit-B26W3FFH.js} +37 -34
- package/chunks/{openaiContentGenerator-J7G2FYJT.js → openaiContentGenerator-6TH7AZGQ.js} +15 -13
- package/chunks/pidfile-KW2QQJZI.js +73 -0
- package/chunks/{pt-TNZRNLQB.js → pt-2THT3YHA.js} +10 -9
- package/chunks/{qwenContentGenerator-5XIEM5GV.js → qwenContentGenerator-5IQAC7RN.js} +33 -27
- package/chunks/{qwenOAuth2-AG3PFFLZ.js → qwenOAuth2-LWLA6MQG.js} +5 -4
- package/chunks/{read-file-J7D552ZJ.js → read-file-CWFAHOI2.js} +11 -10
- package/chunks/{read-mcp-resource-DQQW7H7T.js → read-mcp-resource-PIJF24RO.js} +2 -2
- package/chunks/ripGrep-FTZGDKY4.js +60 -0
- package/chunks/{ru-NIAUBKCN.js → ru-Z67YJHBG.js} +10 -9
- package/chunks/{run-qwen-serve-SGEEVMXD.js → run-qwen-serve-FN2SW5ZI.js} +545 -97
- package/chunks/{scheduler-7CQI3S4Q.js → scheduler-BMS23O3I.js} +31 -25
- package/chunks/{send-message-EE4TWKVU.js → send-message-33PYFMCT.js} +20 -4
- package/chunks/serve-PXFYKL65.js +71 -0
- package/chunks/{server-4QMUWXPW.js → server-BRKALJ4F.js} +4132 -1175
- package/chunks/{session-NRO7UFOY.js → session-AQMISBP4.js} +205 -64
- package/chunks/{settings-SGWRDQSC.js → settings-JCPVUZMB.js} +35 -29
- package/chunks/{shell-62ANGIHL.js → shell-POHCRVN2.js} +31 -25
- package/chunks/{skill-VWAQAWW6.js → skill-QPEVHS6H.js} +13 -12
- package/chunks/spawnChannel-V3QT62TE.js +69 -0
- package/chunks/{src-KUV5J5Q2.js → src-IZSP7L6Y.js} +127 -32
- package/chunks/{startInteractiveUI-6EYJN3JV.js → startInteractiveUI-63KATISO.js} +16762 -16075
- package/chunks/{syntheticOutput-T5YHQ6S2.js → syntheticOutput-YOSWIHDJ.js} +3 -3
- package/chunks/{task-create-BLY7RHXX.js → task-create-NHS2NGB3.js} +9 -7
- package/chunks/{task-list-3QUYNVYR.js → task-list-5UIB564R.js} +7 -6
- package/chunks/{task-stop-U4RATK2V.js → task-stop-HGXZ2VZ3.js} +2 -2
- package/chunks/{task-update-U54O7KJX.js → task-update-IYYWUILG.js} +40 -7
- package/chunks/{team-create-LPOGCPM7.js → team-create-E5O2F3CA.js} +31 -25
- package/chunks/{team-delete-LKBYPASD.js → team-delete-FEKJCU4P.js} +7 -6
- package/chunks/team-plan-approval-2TU4YROB.js +202 -0
- package/chunks/theme-manager-37KOGPJL.js +64 -0
- package/chunks/{todoWrite-OYXKTVYN.js → todoWrite-TKMVZ2HT.js} +4 -4
- package/chunks/{tool-search-YPUFJ3ZQ.js → tool-search-4ZTGNSFI.js} +38 -11
- package/chunks/{trustedFolders-5B24TMEA.js → trustedFolders-RJ5WXJ6L.js} +32 -26
- package/chunks/{validateNonInterActiveAuth-BTDTYOVT.js → validateNonInterActiveAuth-ZWMHZAOK.js} +66 -55
- package/chunks/{web-fetch-EEB5LZYU.js → web-fetch-J4PIW36Q.js} +5 -4
- package/chunks/{workflow-LAWIW5LI.js → workflow-6PG2PC5M.js} +68 -39
- package/chunks/workspace-providers-status-4QIZN2W4.js +68 -0
- package/chunks/workspace-service-CYMCOEIO.js +78 -0
- package/chunks/{write-file-PCV226DJ.js → write-file-R354LGQF.js} +41 -40
- package/chunks/{zh-5EUYXASN.js → zh-SLZNSSZB.js} +32 -10
- package/chunks/{zh-TW-TR3XYGE5.js → zh-TW-P53C2SL5.js} +32 -10
- package/cli.js +6 -6
- package/locales/ca.js +15 -13
- package/locales/de.js +15 -13
- package/locales/en.js +52 -15
- package/locales/fr.js +15 -14
- package/locales/ja.js +15 -14
- package/locales/pt.js +15 -13
- package/locales/ru.js +15 -13
- package/locales/zh-TW.js +50 -14
- package/locales/zh.js +50 -14
- package/node_modules/@qwen-code/audio-capture/package.json +1 -1
- package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-arm64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-x64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/win32-x64/@qwen-code+audio-capture.node +0 -0
- package/package.json +15 -6
- package/patches/chrome-devtools-mcp+1.4.0.patch +31 -0
- package/postinstall.js +50 -0
- package/web-shell/assets/{arc-DA7r8Ueo.js → arc-Bd3048nX.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-ZDUxPtZ2.js → architectureDiagram-3BPJPVTR-DyzbzABx.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-vrtOf1kJ.js → blockDiagram-GPEHLZMM-CTjxm9Sy.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-BHFjzIi4.js → c4Diagram-AAUBKEIU-DSOFWNU6.js} +1 -1
- package/web-shell/assets/channel-oVoup_Ci.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-mdeQVz59.js → chunk-2J33WTMH-D_PFdjo2.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-BkKMlwBX.js → chunk-4BX2VUAB-52eG6wOT.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-B7bnXNfu.js → chunk-55IACEB6-CGTZfKkC.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-DGz8Mm6i.js → chunk-727SXJPM-CZ-qfRUm.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-BQAydUNk.js → chunk-AQP2D5EJ-B2dSDcqM.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-GHr2LN_p.js → chunk-FMBD7UC4-Cfy3tyA0.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-7R91lnUx.js → chunk-ND2GUHAM-CApNUpUq.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-aTJD3lSQ.js → chunk-QZHKN3VN-CVBG8kgg.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-B8WsaT2G.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-B8WsaT2G.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-BTf8t1RL.js → cose-bilkent-S5V4N54A-URd77-_-.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-_sgp3WuA.js → dagre-BM42HDAG-C07H4G9-.js} +1 -1
- package/web-shell/assets/{diagram-2AECGRRQ-CROtRy1w.js → diagram-2AECGRRQ-CYbpXrSn.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-BqGRzwSc.js → diagram-5GNKFQAL-SI8s6fjX.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-CMrcEtA7.js → diagram-KO2AKTUF-mBkC4RTW.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-BRydGNW5.js → diagram-LMA3HP47-CyBPDzWJ.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-D0ilgNjV.js → diagram-OG6HWLK6-DwK7kBCu.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-2lnQeJUM.js → erDiagram-TEJ5UH35-Ds1StEUd.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-C_ZMdQiC.js → flowDiagram-I6XJVG4X-BDC4-jHO.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-Dw9V_Ny0.js → ganttDiagram-6RSMTGT7-CV2aHQqf.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-HDXI-F1B.js → gitGraphDiagram-PVQCEYII-B8RcuV_L.js} +1 -1
- package/web-shell/assets/index-B5WZ0qP1.js +717 -0
- package/web-shell/assets/index-CYE1IMd1.css +5 -0
- package/web-shell/assets/{infoDiagram-5YYISTIA-CuB8Ebua.js → infoDiagram-5YYISTIA-DUPoTf50.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-D_tyhVwh.js → ishikawaDiagram-YF4QCWOH-FDYOx9rf.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-CgCwJQNU.js → journeyDiagram-JHISSGLW-C5_ohMRp.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-PZUtcbPx.js → kanban-definition-UN3LZRKU-Bn4UnOMG.js} +1 -1
- package/web-shell/assets/{linear-CZqRdJIU.js → linear-Bl0pKQtY.js} +1 -1
- package/web-shell/assets/{mermaid.core-CPW_4UEF.js → mermaid.core-sevQFoji.js} +72 -72
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-D_Y75ID7.js → mindmap-definition-RKZ34NQL-DSH4JZF9.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-Q4dTyLeF.js → pieDiagram-4H26LBE5-acWvdQTD.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-ckw329Ft.js → quadrantDiagram-W4KKPZXB-DlggX4Sm.js} +1 -1
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-DbVDm4It.js → requirementDiagram-4Y6WPE33--mMqwmV8.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-CHuZ_ecH.js → sankeyDiagram-5OEKKPKP-BtV9tZ77.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-DU2YSpq-.js → sequenceDiagram-3UESZ5HK-iDQzPFlj.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-CA9PEBUo.js → stateDiagram-AJRCARHV-CsCyQLZ3.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-CPapECG9.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-Bhlo4WVc.js → timeline-definition-PNZ67QCA-Dl0rnKvr.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-6xtPLsyl.js → vennDiagram-CIIHVFJN-qF7nTZzA.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-3eozmXPf.js → wardley-L42UT6IY-fWoK9i7F.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-BOn6hKss.js → wardleyDiagram-YWT4CUSO-pHGfkMnc.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-BsP4-I_y.js → xychartDiagram-2RQKCTM6-3Doc4Trz.js} +1 -1
- package/web-shell/index.html +18 -2
- package/chunks/MaxSizedBox-QNF3EIGU.js +0 -63
- package/chunks/agent-headless-RQ5BTX3G.js +0 -56
- package/chunks/bridge-JL2PMCIW.js +0 -66
- package/chunks/chunk-5GOVVXEV.js +0 -73
- package/chunks/chunk-BC3C4F3Q.js +0 -40
- package/chunks/chunk-BW4KCQFD.js +0 -1103
- package/chunks/chunk-RKIZXJMB.js +0 -13
- package/chunks/contextCommand-WYU6OQ2F.js +0 -61
- package/chunks/daemon-status-provider-HH2EC5FI.js +0 -65
- package/chunks/headlessSafetyWarnings-TBE35GJ4.js +0 -55
- package/chunks/initializer-2Y7SLOKV.js +0 -61
- package/chunks/list-ZAFHZ7OS.js +0 -65
- package/chunks/loadedSettingsAdapter-6KJXD6M3.js +0 -60
- package/chunks/nonInteractiveCli-5W4GIZQM.js +0 -96
- package/chunks/ripGrep-IEIFIWP4.js +0 -54
- package/chunks/serve-V6GVZR6U.js +0 -65
- package/chunks/spawnChannel-3VHW5EPY.js +0 -63
- package/chunks/theme-manager-LYLGAERF.js +0 -58
- package/chunks/workspace-providers-status-PIAFVYHA.js +0 -62
- package/chunks/workspace-service-YAIOD4HG.js +0 -71
- package/web-shell/assets/channel-iRo68q4Q.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-iHF24a8X.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-iHF24a8X.js +0 -1
- package/web-shell/assets/index-CHmzAWys.css +0 -5
- package/web-shell/assets/index-w6GTOHh1.js +0 -713
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-t1LRI0oY.js +0 -1
|
@@ -3,8 +3,13 @@
|
|
|
3
3
|
import {
|
|
4
4
|
classifyApiError,
|
|
5
5
|
prefixMidTurnUserMessageParts
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import
|
|
6
|
+
} from "./chunk-IPYE5BIS.js";
|
|
7
|
+
import {
|
|
8
|
+
EXTENSION_CONTEXT_BUDGET,
|
|
9
|
+
buildExtensionMentionContext,
|
|
10
|
+
matchExtensionByRef,
|
|
11
|
+
parseExtensionRef
|
|
12
|
+
} from "./chunk-6JE32BRB.js";
|
|
8
13
|
import {
|
|
9
14
|
HistoryReplayer,
|
|
10
15
|
MessageEmitter,
|
|
@@ -12,74 +17,91 @@ import {
|
|
|
12
17
|
ToolCallEmitter,
|
|
13
18
|
getAvailableCommands,
|
|
14
19
|
handleSlashCommand,
|
|
20
|
+
insertAfterFunctionResponses,
|
|
15
21
|
isSlashCommand,
|
|
16
22
|
normalizePartList
|
|
17
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-QZDBHNMV.js";
|
|
18
24
|
import {
|
|
19
25
|
appEvents,
|
|
20
26
|
buildDisabledSkillNamesProvider,
|
|
21
27
|
loadCliConfig,
|
|
22
28
|
normalizeDisabledToolList
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import {
|
|
25
|
-
collectContextData,
|
|
26
|
-
formatContextUsageText
|
|
27
|
-
} from "./chunk-FNJ7BDPH.js";
|
|
29
|
+
} from "./chunk-J3YPTTNL.js";
|
|
28
30
|
import {
|
|
29
31
|
getCommandSubcommandNames
|
|
30
|
-
} from "./chunk-
|
|
31
|
-
import "./chunk-
|
|
32
|
+
} from "./chunk-5LKL3PAU.js";
|
|
33
|
+
import "./chunk-Y4LITCRB.js";
|
|
32
34
|
import {
|
|
33
35
|
getEffectiveSupportedModes
|
|
34
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-SCYK2YLI.js";
|
|
37
|
+
import "./chunk-JZSY4WP3.js";
|
|
38
|
+
import {
|
|
39
|
+
collectContextData,
|
|
40
|
+
formatContextUsageText
|
|
41
|
+
} from "./chunk-UY7ZAYTY.js";
|
|
35
42
|
import {
|
|
36
43
|
isTerminalGoalStatusKind
|
|
37
44
|
} from "./chunk-KSPVDFE4.js";
|
|
38
45
|
import "./chunk-MRO43B25.js";
|
|
39
|
-
import "./chunk-
|
|
40
|
-
import "./chunk-
|
|
46
|
+
import "./chunk-VXGPYF7U.js";
|
|
47
|
+
import "./chunk-LOW52WYZ.js";
|
|
41
48
|
import "./chunk-KYMBIKIW.js";
|
|
42
|
-
import "./chunk-
|
|
43
|
-
import
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
import "./chunk-HTWHAN44.js";
|
|
50
|
+
import {
|
|
51
|
+
MAX_REMEMBER_CONTENT_BYTES,
|
|
52
|
+
extractRememberErrorCode,
|
|
53
|
+
isValidServerName
|
|
54
|
+
} from "./chunk-RM3PGSGL.js";
|
|
55
|
+
import "./chunk-L7QPM2YD.js";
|
|
56
|
+
import "./chunk-54YZOP7P.js";
|
|
57
|
+
import "./chunk-XHZKDN7M.js";
|
|
58
|
+
import "./chunk-AHNXYU4O.js";
|
|
59
|
+
import "./chunk-RNBYOUGV.js";
|
|
60
|
+
import "./chunk-HLW4EGKR.js";
|
|
61
|
+
import "./chunk-BJBWRCSK.js";
|
|
62
|
+
import "./chunk-H6TNLOLM.js";
|
|
46
63
|
import "./chunk-3GONHQOA.js";
|
|
47
|
-
import "./chunk-
|
|
48
|
-
import "./chunk-
|
|
49
|
-
import "./chunk-
|
|
64
|
+
import "./chunk-MIM62IXS.js";
|
|
65
|
+
import "./chunk-4D73HUOP.js";
|
|
66
|
+
import "./chunk-YHTNWGRR.js";
|
|
50
67
|
import {
|
|
51
68
|
createLoadedSettingsAdapter
|
|
52
|
-
} from "./chunk-
|
|
53
|
-
import "./chunk-
|
|
54
|
-
import "./chunk-M2U5NM24.js";
|
|
55
|
-
import "./chunk-JJXWTVSK.js";
|
|
56
|
-
import "./chunk-R2BXK7SX.js";
|
|
57
|
-
import "./chunk-RKIZXJMB.js";
|
|
58
|
-
import "./chunk-MNF6S5PR.js";
|
|
69
|
+
} from "./chunk-7LUOQJ3D.js";
|
|
70
|
+
import "./chunk-P5THQV6A.js";
|
|
59
71
|
import "./chunk-KHDZHZMH.js";
|
|
72
|
+
import "./chunk-DE7YXUR7.js";
|
|
73
|
+
import {
|
|
74
|
+
CLIENT_MCP_OVER_WS_CONFIG_FLAG,
|
|
75
|
+
MID_TURN_QUEUE_DRAIN_METHOD
|
|
76
|
+
} from "./chunk-EWALXUAD.js";
|
|
77
|
+
import "./chunk-ALX6BUYW.js";
|
|
60
78
|
import {
|
|
61
79
|
formatAcpModelId,
|
|
62
80
|
parseAcpBaseModelId,
|
|
63
81
|
parseAcpModelOption,
|
|
64
82
|
sanitizeProviderBaseUrl
|
|
65
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-UYHWM24C.js";
|
|
66
84
|
import {
|
|
67
85
|
PERMISSION_RULE_TYPES,
|
|
68
86
|
PermissionRulesValidationError,
|
|
69
87
|
buildPermissionSettings,
|
|
70
88
|
normalizePermissionRules,
|
|
71
89
|
readPermissionRuleSet
|
|
72
|
-
} from "./chunk-
|
|
73
|
-
import "./chunk-
|
|
74
|
-
import "./chunk-
|
|
75
|
-
import "./chunk-GT2FGICD.js";
|
|
76
|
-
import {
|
|
77
|
-
MID_TURN_QUEUE_DRAIN_METHOD
|
|
78
|
-
} from "./chunk-QBZ22PQX.js";
|
|
79
|
-
import "./chunk-ALX6BUYW.js";
|
|
90
|
+
} from "./chunk-2U4HUMOL.js";
|
|
91
|
+
import "./chunk-7KXAGQJ2.js";
|
|
92
|
+
import "./chunk-NIOPRZNN.js";
|
|
80
93
|
import {
|
|
81
94
|
NOT_CURRENTLY_GENERATING_CANCEL_MESSAGE
|
|
82
|
-
} from "./chunk-
|
|
95
|
+
} from "./chunk-T22H4V4R.js";
|
|
96
|
+
import "./chunk-SEZC2725.js";
|
|
97
|
+
import {
|
|
98
|
+
ACP_PREFLIGHT_KINDS,
|
|
99
|
+
IDLE_HOOK_EVENTS,
|
|
100
|
+
SERVE_CONTROL_EXT_METHODS,
|
|
101
|
+
SERVE_STATUS_EXT_METHODS,
|
|
102
|
+
STATUS_SCHEMA_VERSION,
|
|
103
|
+
mapDomainErrorToErrorKind
|
|
104
|
+
} from "./chunk-M7BLWURA.js";
|
|
83
105
|
import {
|
|
84
106
|
OUTPUT_LANGUAGE_AUTO,
|
|
85
107
|
getOutputLanguageFilePath,
|
|
@@ -90,44 +112,38 @@ import {
|
|
|
90
112
|
resolveOutputLanguage,
|
|
91
113
|
updateOutputLanguageFile,
|
|
92
114
|
writeOutputLanguageAndRegisterPath
|
|
93
|
-
} from "./chunk-
|
|
115
|
+
} from "./chunk-75AVTQJ2.js";
|
|
94
116
|
import {
|
|
95
117
|
SUPPORTED_LANGUAGES,
|
|
96
118
|
getCurrentLanguage,
|
|
97
119
|
setLanguageAsync
|
|
98
|
-
} from "./chunk-
|
|
120
|
+
} from "./chunk-JZG7VB6C.js";
|
|
99
121
|
import "./chunk-TLRYABYP.js";
|
|
100
|
-
import "./chunk-
|
|
122
|
+
import "./chunk-S6RLAIUR.js";
|
|
123
|
+
import "./chunk-MX2YXRER.js";
|
|
124
|
+
import "./chunk-B6SZLQAZ.js";
|
|
101
125
|
import {
|
|
102
126
|
AgentSideConnection,
|
|
103
127
|
PROTOCOL_VERSION,
|
|
104
128
|
RequestError,
|
|
105
129
|
ndJsonStream
|
|
106
130
|
} from "./chunk-UFC57OYT.js";
|
|
107
|
-
import
|
|
108
|
-
ACP_PREFLIGHT_KINDS,
|
|
109
|
-
IDLE_HOOK_EVENTS,
|
|
110
|
-
SERVE_CONTROL_EXT_METHODS,
|
|
111
|
-
SERVE_STATUS_EXT_METHODS,
|
|
112
|
-
STATUS_SCHEMA_VERSION,
|
|
113
|
-
mapDomainErrorToErrorKind
|
|
114
|
-
} from "./chunk-L7OKCI7T.js";
|
|
115
|
-
import "./chunk-CF6BB3LT.js";
|
|
131
|
+
import "./chunk-PPFWEV7G.js";
|
|
116
132
|
import {
|
|
117
133
|
isFolderTrustEnabled,
|
|
118
134
|
isWorkspaceTrusted,
|
|
119
135
|
loadTrustedFolders
|
|
120
|
-
} from "./chunk-
|
|
136
|
+
} from "./chunk-FY4TT4L4.js";
|
|
121
137
|
import "./chunk-Y6Z2O3WR.js";
|
|
122
138
|
import "./chunk-QYUE6W3T.js";
|
|
123
139
|
import {
|
|
124
140
|
ALL_PROVIDERS,
|
|
125
|
-
APPROVAL_MODES,
|
|
126
141
|
APPROVAL_MODE_INFO,
|
|
127
142
|
BTW_MAX_INPUT_LENGTH,
|
|
128
143
|
ExtensionManager,
|
|
129
144
|
FORK_SUBAGENT_TYPE,
|
|
130
145
|
InvalidMcpConfigError,
|
|
146
|
+
LoopTickResolver,
|
|
131
147
|
MAX_TRANSCRIPT_MESSAGES,
|
|
132
148
|
MCPOAuthProvider,
|
|
133
149
|
MCPOAuthTokenStorage,
|
|
@@ -136,9 +152,11 @@ import {
|
|
|
136
152
|
McpBudgetWouldExceedError,
|
|
137
153
|
McpServerSpawnFailedError,
|
|
138
154
|
McpTransportPool,
|
|
155
|
+
ORPHAN_TOOL_USE_REPAIR_REASON,
|
|
139
156
|
POOLED_TRANSPORTS_DEFAULT,
|
|
140
157
|
SESSION_TITLE_MAX_LENGTH,
|
|
141
158
|
SessionService,
|
|
159
|
+
TURN_INTERRUPTION_HISTORY_TAIL_COUNT,
|
|
142
160
|
WorkspaceMcpBudget,
|
|
143
161
|
abortGoalForStopHookCap,
|
|
144
162
|
acquireSleepInhibitor,
|
|
@@ -147,13 +165,18 @@ import {
|
|
|
147
165
|
buildBackgroundEntryLabel,
|
|
148
166
|
buildBtwCacheSafeParams,
|
|
149
167
|
buildBtwPrompt,
|
|
168
|
+
buildContextUsage,
|
|
150
169
|
buildInstallPlan,
|
|
170
|
+
buildSyntheticToolResponseParts,
|
|
151
171
|
clampInlineMediaPart,
|
|
152
172
|
clearGoalTerminalObserver,
|
|
153
173
|
computeUniqueBranchTitle,
|
|
154
174
|
convertToFunctionResponse,
|
|
155
175
|
createDuplicateProviderToolCallResponse,
|
|
156
176
|
dedupeToolCallsById,
|
|
177
|
+
detectAutonomousSentinel,
|
|
178
|
+
detectLoopSentinel,
|
|
179
|
+
detectTurnInterruption,
|
|
157
180
|
evaluateAutoMode,
|
|
158
181
|
evaluatePermissionFlow,
|
|
159
182
|
findExistingProviderModels,
|
|
@@ -179,13 +202,16 @@ import {
|
|
|
179
202
|
getProviderToolCallId,
|
|
180
203
|
getScopedEnvContents,
|
|
181
204
|
getStartupContextLength,
|
|
205
|
+
hasImageParts,
|
|
182
206
|
injectPermissionRulesIfMissing,
|
|
183
207
|
isApproveOutcome,
|
|
184
208
|
isDenialFallbackReason,
|
|
185
209
|
isPlanModeBlocked,
|
|
186
210
|
isSystemReminderContent,
|
|
187
211
|
markDuplicateProviderToolCallResponseSent,
|
|
212
|
+
matchesAnyServerPattern,
|
|
188
213
|
needsConfirmation,
|
|
214
|
+
normalizeParts,
|
|
189
215
|
parsePositiveIntegerEnv,
|
|
190
216
|
persistPermissionOutcome,
|
|
191
217
|
readManyFiles,
|
|
@@ -195,44 +221,55 @@ import {
|
|
|
195
221
|
resolveBaseUrl,
|
|
196
222
|
restoreWorktreeContext,
|
|
197
223
|
runForkedAgent,
|
|
224
|
+
runManagedRememberByAgent,
|
|
225
|
+
runVisionBridge,
|
|
198
226
|
sessionIdContext,
|
|
199
227
|
setGoalTerminalObserver,
|
|
228
|
+
shouldClassifyAllShellForAutoMode,
|
|
200
229
|
shouldFallback,
|
|
201
230
|
shouldFirePermissionDeniedForAutoMode,
|
|
202
231
|
shouldForceAutoModeReviewForAllow,
|
|
203
232
|
shouldRunAutoModeForCall,
|
|
233
|
+
shouldRunVisionBridge,
|
|
234
|
+
splitImageParts,
|
|
204
235
|
subagentGenerator,
|
|
205
236
|
unregisterGoalHook,
|
|
206
237
|
updateSetting
|
|
207
|
-
} from "./chunk-
|
|
238
|
+
} from "./chunk-A5OPLHFM.js";
|
|
239
|
+
import "./chunk-5P5XGNYH.js";
|
|
208
240
|
import {
|
|
209
241
|
external_exports
|
|
210
242
|
} from "./chunk-K5PGHDBN.js";
|
|
211
243
|
import "./chunk-SKAZWEV5.js";
|
|
212
|
-
import "./chunk-
|
|
213
|
-
import "./chunk-
|
|
214
|
-
import "./chunk-
|
|
244
|
+
import "./chunk-QOMAHFYI.js";
|
|
245
|
+
import "./chunk-G7OQ3RFJ.js";
|
|
246
|
+
import "./chunk-LKWAVAB5.js";
|
|
215
247
|
import {
|
|
216
248
|
runSideQuery
|
|
217
|
-
} from "./chunk-
|
|
218
|
-
import "./chunk-
|
|
249
|
+
} from "./chunk-L7OJ3ZO3.js";
|
|
250
|
+
import "./chunk-R6RI2HIC.js";
|
|
219
251
|
import "./chunk-2MPVVENX.js";
|
|
220
252
|
import "./chunk-ZTQ26VBE.js";
|
|
221
|
-
import "./chunk-
|
|
253
|
+
import "./chunk-SJBIFVD2.js";
|
|
222
254
|
import "./chunk-MLZQVCF3.js";
|
|
223
255
|
import "./chunk-LQ7TMOCE.js";
|
|
224
|
-
import "./chunk-
|
|
225
|
-
import
|
|
256
|
+
import "./chunk-E5A7LHNN.js";
|
|
257
|
+
import {
|
|
258
|
+
APPROVAL_MODES
|
|
259
|
+
} from "./chunk-BPFJQGY7.js";
|
|
260
|
+
import "./chunk-GO6LNQXT.js";
|
|
261
|
+
import "./chunk-B236XPGF.js";
|
|
226
262
|
import {
|
|
227
263
|
HookEventName,
|
|
228
264
|
createHookOutput
|
|
229
|
-
} from "./chunk-
|
|
230
|
-
import "./chunk-
|
|
231
|
-
import "./chunk-
|
|
265
|
+
} from "./chunk-YXK4GPYO.js";
|
|
266
|
+
import "./chunk-TOAUDIS7.js";
|
|
267
|
+
import "./chunk-NPQWQYU6.js";
|
|
232
268
|
import {
|
|
233
269
|
AuthType,
|
|
234
270
|
ConversationFinishedEvent,
|
|
235
271
|
DiscoveredMCPTool,
|
|
272
|
+
LoopDetectedEvent,
|
|
236
273
|
PromptSuggestionEvent,
|
|
237
274
|
UserPromptEvent,
|
|
238
275
|
endToolExecutionSpan,
|
|
@@ -241,6 +278,7 @@ import {
|
|
|
241
278
|
getAllGeminiMdFilenames,
|
|
242
279
|
getMCPServerStatus,
|
|
243
280
|
logConversationFinishedEvent,
|
|
281
|
+
logLoopDetected,
|
|
244
282
|
logPromptSuggestion,
|
|
245
283
|
logToolCall,
|
|
246
284
|
logUserPrompt,
|
|
@@ -249,45 +287,52 @@ import {
|
|
|
249
287
|
startToolSpan,
|
|
250
288
|
uiTelemetryService,
|
|
251
289
|
withInteractionSpan
|
|
252
|
-
} from "./chunk-
|
|
253
|
-
import "./chunk-
|
|
290
|
+
} from "./chunk-UA6XBZT7.js";
|
|
291
|
+
import "./chunk-E6US47KI.js";
|
|
254
292
|
import "./chunk-UWCTAVOD.js";
|
|
255
293
|
import "./chunk-OFEVLU4C.js";
|
|
256
294
|
import "./chunk-OMX7CUOE.js";
|
|
257
295
|
import {
|
|
258
|
-
getAutoMemoryRoot
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
import "./chunk-
|
|
296
|
+
getAutoMemoryRoot,
|
|
297
|
+
getUserAutoMemoryRoot
|
|
298
|
+
} from "./chunk-MOUXUCXQ.js";
|
|
299
|
+
import "./chunk-BZHLWC6T.js";
|
|
300
|
+
import "./chunk-DG573EOO.js";
|
|
262
301
|
import {
|
|
263
302
|
ToolConfirmationOutcome,
|
|
264
303
|
ToolNames
|
|
265
|
-
} from "./chunk-
|
|
304
|
+
} from "./chunk-MTNTVOJR.js";
|
|
266
305
|
import {
|
|
267
306
|
clearCachedCredentialFile,
|
|
268
307
|
qwenOAuth2Events
|
|
269
|
-
} from "./chunk-
|
|
270
|
-
import "./chunk-
|
|
308
|
+
} from "./chunk-TENOUWY2.js";
|
|
309
|
+
import "./chunk-GLPV75ES.js";
|
|
271
310
|
import "./chunk-22IFUCVR.js";
|
|
272
|
-
import "./chunk-
|
|
311
|
+
import "./chunk-T42ACHFI.js";
|
|
312
|
+
import "./chunk-SIUQ3YYX.js";
|
|
273
313
|
import {
|
|
314
|
+
DEFAULT_TOKEN_LIMIT,
|
|
274
315
|
tokenLimit
|
|
275
|
-
} from "./chunk-
|
|
276
|
-
import "./chunk-
|
|
277
|
-
import "./chunk-
|
|
316
|
+
} from "./chunk-YTFBURQD.js";
|
|
317
|
+
import "./chunk-YPCPZSZB.js";
|
|
318
|
+
import "./chunk-IRJWHZWU.js";
|
|
319
|
+
import "./chunk-SYCJMSIJ.js";
|
|
278
320
|
import "./chunk-55ZMG67I.js";
|
|
279
321
|
import "./chunk-H6BD2ELD.js";
|
|
280
322
|
import "./chunk-5IFG2VC4.js";
|
|
281
323
|
import "./chunk-64WXLC72.js";
|
|
324
|
+
import "./chunk-D5KISTLW.js";
|
|
282
325
|
import {
|
|
283
326
|
Storage,
|
|
284
327
|
createDebugLogger,
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
328
|
+
getErrorMessage,
|
|
329
|
+
getErrorStatus,
|
|
330
|
+
isSubpath
|
|
331
|
+
} from "./chunk-AVQDCKNF.js";
|
|
332
|
+
import "./chunk-NOGSPTLJ.js";
|
|
288
333
|
import {
|
|
289
334
|
runExitCleanup
|
|
290
|
-
} from "./chunk-
|
|
335
|
+
} from "./chunk-7IOMIYOE.js";
|
|
291
336
|
import "./chunk-ZERZSAZL.js";
|
|
292
337
|
import "./chunk-QN5NZ3UQ.js";
|
|
293
338
|
import "./chunk-BR4QREVK.js";
|
|
@@ -331,17 +376,50 @@ __name(pickAuthMethodsForAuthRequired, "pickAuthMethodsForAuthRequired");
|
|
|
331
376
|
|
|
332
377
|
// packages/cli/src/acp-integration/service/filesystem.ts
|
|
333
378
|
init_esbuild_shims();
|
|
379
|
+
import { realpath } from "node:fs/promises";
|
|
380
|
+
import path from "node:path";
|
|
334
381
|
var RESOURCE_NOT_FOUND_CODE = -32002;
|
|
382
|
+
var PATH_OUTSIDE_WORKSPACE_KIND = "path_outside_workspace";
|
|
383
|
+
var SYMLINK_ESCAPE_KIND = "symlink_escape";
|
|
384
|
+
var LOCAL_READ_FALLBACK_ERROR_KINDS = /* @__PURE__ */ new Set([
|
|
385
|
+
PATH_OUTSIDE_WORKSPACE_KIND,
|
|
386
|
+
SYMLINK_ESCAPE_KIND
|
|
387
|
+
]);
|
|
388
|
+
var debugLogger = createDebugLogger("ACP_FILE_SYSTEM");
|
|
389
|
+
function isRecord(value) {
|
|
390
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
391
|
+
}
|
|
392
|
+
__name(isRecord, "isRecord");
|
|
335
393
|
function getErrorCode(error) {
|
|
336
394
|
if (error instanceof RequestError) {
|
|
337
395
|
return error.code;
|
|
338
396
|
}
|
|
339
|
-
if (
|
|
340
|
-
return error
|
|
397
|
+
if (isRecord(error)) {
|
|
398
|
+
return error["code"];
|
|
341
399
|
}
|
|
342
400
|
return void 0;
|
|
343
401
|
}
|
|
344
402
|
__name(getErrorCode, "getErrorCode");
|
|
403
|
+
function getErrorData(error) {
|
|
404
|
+
const data = isRecord(error) ? error["data"] : void 0;
|
|
405
|
+
return isRecord(data) ? data : void 0;
|
|
406
|
+
}
|
|
407
|
+
__name(getErrorData, "getErrorData");
|
|
408
|
+
function getErrorKind(error) {
|
|
409
|
+
const data = getErrorData(error);
|
|
410
|
+
if (data && typeof data["errorKind"] === "string") {
|
|
411
|
+
return data["errorKind"];
|
|
412
|
+
}
|
|
413
|
+
return void 0;
|
|
414
|
+
}
|
|
415
|
+
__name(getErrorKind, "getErrorKind");
|
|
416
|
+
function normalizeError(error) {
|
|
417
|
+
if (error instanceof Error) return error;
|
|
418
|
+
return new Error(getErrorMessage(error), {
|
|
419
|
+
cause: error
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
__name(normalizeError, "normalizeError");
|
|
345
423
|
function createEnoentError(filePath) {
|
|
346
424
|
const err = new Error(`File not found: ${filePath}`);
|
|
347
425
|
err.code = "ENOENT";
|
|
@@ -350,12 +428,32 @@ function createEnoentError(filePath) {
|
|
|
350
428
|
return err;
|
|
351
429
|
}
|
|
352
430
|
__name(createEnoentError, "createEnoentError");
|
|
431
|
+
function isLocalReadFallbackErrorKind(errorKind) {
|
|
432
|
+
return typeof errorKind === "string" && LOCAL_READ_FALLBACK_ERROR_KINDS.has(errorKind);
|
|
433
|
+
}
|
|
434
|
+
__name(isLocalReadFallbackErrorKind, "isLocalReadFallbackErrorKind");
|
|
435
|
+
async function resolveRealPath(value) {
|
|
436
|
+
if (!value.trim()) return void 0;
|
|
437
|
+
try {
|
|
438
|
+
return await realpath(path.resolve(value));
|
|
439
|
+
} catch (error) {
|
|
440
|
+
if (getErrorCode(error) !== "ENOENT") {
|
|
441
|
+
debugLogger.warn("realpath failed during ACP local read fallback check", {
|
|
442
|
+
path: value,
|
|
443
|
+
error: getErrorMessage(error)
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
return void 0;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
__name(resolveRealPath, "resolveRealPath");
|
|
353
450
|
var AcpFileSystemService = class {
|
|
354
|
-
constructor(connection, sessionId, capabilities, fallback) {
|
|
451
|
+
constructor(connection, sessionId, capabilities, fallback, options = {}) {
|
|
355
452
|
this.connection = connection;
|
|
356
453
|
this.sessionId = sessionId;
|
|
357
454
|
this.capabilities = capabilities;
|
|
358
455
|
this.fallback = fallback;
|
|
456
|
+
this.options = options;
|
|
359
457
|
}
|
|
360
458
|
static {
|
|
361
459
|
__name(this, "AcpFileSystemService");
|
|
@@ -375,7 +473,45 @@ var AcpFileSystemService = class {
|
|
|
375
473
|
if (errorCode === RESOURCE_NOT_FOUND_CODE) {
|
|
376
474
|
throw createEnoentError(params.path);
|
|
377
475
|
}
|
|
378
|
-
|
|
476
|
+
const errorKind = getErrorKind(error);
|
|
477
|
+
const shouldTryLocalReadFallback = isLocalReadFallbackErrorKind(errorKind);
|
|
478
|
+
const fallbackPath = shouldTryLocalReadFallback ? await this.getLocalReadFallbackPath(params.path) : void 0;
|
|
479
|
+
if (shouldTryLocalReadFallback && !fallbackPath) {
|
|
480
|
+
debugLogger.debug("Local read fallback skipped - no safe local path", {
|
|
481
|
+
path: params.path,
|
|
482
|
+
errorKind
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
if (shouldTryLocalReadFallback && fallbackPath) {
|
|
486
|
+
debugLogger.debug("Falling back to local read after ACP error", {
|
|
487
|
+
path: params.path,
|
|
488
|
+
resolvedPath: fallbackPath,
|
|
489
|
+
errorKind,
|
|
490
|
+
error: getErrorMessage(error)
|
|
491
|
+
});
|
|
492
|
+
try {
|
|
493
|
+
return await this.fallback.readTextFile({
|
|
494
|
+
...params,
|
|
495
|
+
path: fallbackPath
|
|
496
|
+
});
|
|
497
|
+
} catch (fallbackError) {
|
|
498
|
+
if (getErrorCode(fallbackError) === "ENOENT") {
|
|
499
|
+
throw fallbackError;
|
|
500
|
+
}
|
|
501
|
+
debugLogger.warn("Local read fallback failed after ACP error", {
|
|
502
|
+
path: params.path,
|
|
503
|
+
resolvedPath: fallbackPath,
|
|
504
|
+
errorKind,
|
|
505
|
+
originalError: getErrorMessage(error),
|
|
506
|
+
fallbackError: getErrorMessage(fallbackError)
|
|
507
|
+
});
|
|
508
|
+
throw new Error(
|
|
509
|
+
`Local fallback read failed for ${params.path}: ${getErrorMessage(fallbackError)} (original ACP error: ${getErrorMessage(error)})`,
|
|
510
|
+
{ cause: { fallbackError, acpError: error } }
|
|
511
|
+
);
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
throw normalizeError(error);
|
|
379
515
|
}
|
|
380
516
|
return response;
|
|
381
517
|
}
|
|
@@ -384,27 +520,53 @@ var AcpFileSystemService = class {
|
|
|
384
520
|
return this.fallback.writeTextFile(params);
|
|
385
521
|
}
|
|
386
522
|
const finalContent = params._meta?.["bom"] && params.content.charCodeAt(0) !== 65279 ? "\uFEFF" + params.content : params.content;
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
523
|
+
try {
|
|
524
|
+
await this.connection.writeTextFile({
|
|
525
|
+
...params,
|
|
526
|
+
content: finalContent,
|
|
527
|
+
sessionId: this.sessionId
|
|
528
|
+
});
|
|
529
|
+
} catch (error) {
|
|
530
|
+
if (getErrorCode(error) === RESOURCE_NOT_FOUND_CODE) {
|
|
531
|
+
throw createEnoentError(params.path);
|
|
532
|
+
}
|
|
533
|
+
throw normalizeError(error);
|
|
534
|
+
}
|
|
392
535
|
return { _meta: params._meta };
|
|
393
536
|
}
|
|
394
537
|
findFiles(fileName, searchPaths) {
|
|
395
538
|
return this.fallback.findFiles(fileName, searchPaths);
|
|
396
539
|
}
|
|
540
|
+
async getResolvedLocalReadRoots() {
|
|
541
|
+
const roots = await Promise.all(
|
|
542
|
+
(this.options.localReadRoots ?? []).map(resolveRealPath)
|
|
543
|
+
);
|
|
544
|
+
return roots.filter((root) => Boolean(root));
|
|
545
|
+
}
|
|
546
|
+
async getLocalReadFallbackPath(filePath) {
|
|
547
|
+
const normalizedFilePath = path.resolve(filePath);
|
|
548
|
+
const realFilePath = await resolveRealPath(normalizedFilePath);
|
|
549
|
+
if (!realFilePath) return void 0;
|
|
550
|
+
for (const realRoot of await this.getResolvedLocalReadRoots()) {
|
|
551
|
+
if (isSubpath(realRoot, realFilePath)) {
|
|
552
|
+
return realFilePath;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
return void 0;
|
|
556
|
+
}
|
|
397
557
|
};
|
|
398
558
|
|
|
399
559
|
// packages/cli/src/acp-integration/acpAgent.ts
|
|
400
560
|
import { Readable, Writable } from "node:stream";
|
|
401
561
|
import { pipeline } from "node:stream/promises";
|
|
402
562
|
import * as fs from "node:fs/promises";
|
|
403
|
-
import * as
|
|
563
|
+
import * as path3 from "node:path";
|
|
404
564
|
import { createGunzip } from "node:zlib";
|
|
405
565
|
|
|
406
566
|
// packages/cli/src/acp-integration/session/Session.ts
|
|
407
567
|
init_esbuild_shims();
|
|
568
|
+
import * as os from "node:os";
|
|
569
|
+
import * as path2 from "node:path";
|
|
408
570
|
|
|
409
571
|
// packages/cli/src/acp-integration/session/SubAgentTracker.ts
|
|
410
572
|
init_esbuild_shims();
|
|
@@ -586,7 +748,7 @@ function toPermissionOptions(confirmation, forceHideAlwaysAllow = false) {
|
|
|
586
748
|
__name(toPermissionOptions, "toPermissionOptions");
|
|
587
749
|
|
|
588
750
|
// packages/cli/src/acp-integration/session/SubAgentTracker.ts
|
|
589
|
-
var
|
|
751
|
+
var debugLogger2 = createDebugLogger("ACP_SUBAGENT_TRACKER");
|
|
590
752
|
var SubAgentTracker = class {
|
|
591
753
|
constructor(ctx, client, parentToolCallId, subagentType, onPermissionCancel) {
|
|
592
754
|
this.ctx = ctx;
|
|
@@ -646,7 +808,7 @@ var SubAgentTracker = class {
|
|
|
646
808
|
try {
|
|
647
809
|
invocation = tool.build(event.args);
|
|
648
810
|
} catch (e) {
|
|
649
|
-
|
|
811
|
+
debugLogger2.warn(`Failed to build subagent tool ${event.name}:`, e);
|
|
650
812
|
}
|
|
651
813
|
}
|
|
652
814
|
this.toolStates.set(event.callId, {
|
|
@@ -725,7 +887,7 @@ var SubAgentTracker = class {
|
|
|
725
887
|
this.onPermissionCancel?.();
|
|
726
888
|
}
|
|
727
889
|
} catch (error) {
|
|
728
|
-
|
|
890
|
+
debugLogger2.error(
|
|
729
891
|
`Permission request failed for subagent tool ${event.name}:`,
|
|
730
892
|
error
|
|
731
893
|
);
|
|
@@ -733,7 +895,7 @@ var SubAgentTracker = class {
|
|
|
733
895
|
try {
|
|
734
896
|
await event.respond("cancel" /* Cancel */);
|
|
735
897
|
} catch (respondError) {
|
|
736
|
-
|
|
898
|
+
debugLogger2.error(
|
|
737
899
|
`Failed to cancel subagent tool ${event.name} after permission request failure:`,
|
|
738
900
|
respondError
|
|
739
901
|
);
|
|
@@ -832,7 +994,7 @@ var TurnBuffer = class {
|
|
|
832
994
|
init_esbuild_shims();
|
|
833
995
|
import { readFileSync, existsSync } from "node:fs";
|
|
834
996
|
import { resolve } from "node:path";
|
|
835
|
-
var
|
|
997
|
+
var debugLogger3 = createDebugLogger("MESSAGE_REWRITER");
|
|
836
998
|
var DEFAULT_REWRITE_PROMPT = `You are an assistant that rewrites raw coding-agent output into concise, user-friendly progress updates.
|
|
837
999
|
|
|
838
1000
|
The agent is a software engineering assistant that reads files, writes code, runs commands, and uses tools. Its raw output mixes internal reasoning with user-facing information. Your job: extract what the user cares about, drop what they don't.
|
|
@@ -861,11 +1023,11 @@ var LlmRewriter = class {
|
|
|
861
1023
|
const filePath = resolve(rewriteConfig.promptFile);
|
|
862
1024
|
if (existsSync(filePath)) {
|
|
863
1025
|
this.prompt = readFileSync(filePath, "utf-8").trim();
|
|
864
|
-
|
|
1026
|
+
debugLogger3.info(
|
|
865
1027
|
`Loaded rewrite prompt from file: ${filePath} (${this.prompt.length} chars)`
|
|
866
1028
|
);
|
|
867
1029
|
} else {
|
|
868
|
-
|
|
1030
|
+
debugLogger3.warn(
|
|
869
1031
|
`Rewrite prompt file not found: ${filePath}, using default`
|
|
870
1032
|
);
|
|
871
1033
|
this.prompt = DEFAULT_REWRITE_PROMPT;
|
|
@@ -902,7 +1064,7 @@ var LlmRewriter = class {
|
|
|
902
1064
|
const inputText = inputParts.join("\n\n");
|
|
903
1065
|
if (!inputText.trim()) return null;
|
|
904
1066
|
if (inputText.length < 10) return null;
|
|
905
|
-
|
|
1067
|
+
debugLogger3.info(
|
|
906
1068
|
`[REWRITE INPUT] system_prompt_len=${this.prompt.length} input_len=${inputText.length} context_turns=${this.outputHistory.length}
|
|
907
1069
|
--- INPUT TEXT ---
|
|
908
1070
|
${inputText}
|
|
@@ -931,10 +1093,10 @@ ${inputText}
|
|
|
931
1093
|
});
|
|
932
1094
|
const rewritten = result.text;
|
|
933
1095
|
if (!rewritten || rewritten.length < 5) {
|
|
934
|
-
|
|
1096
|
+
debugLogger3.info(`[REWRITE OUTPUT] empty or too short, skipping`);
|
|
935
1097
|
return null;
|
|
936
1098
|
}
|
|
937
|
-
|
|
1099
|
+
debugLogger3.info(
|
|
938
1100
|
`[REWRITE OUTPUT] len=${rewritten.length}
|
|
939
1101
|
--- OUTPUT ---
|
|
940
1102
|
${rewritten}
|
|
@@ -943,7 +1105,7 @@ ${rewritten}
|
|
|
943
1105
|
this.outputHistory.push(rewritten);
|
|
944
1106
|
return rewritten;
|
|
945
1107
|
} catch (error) {
|
|
946
|
-
|
|
1108
|
+
debugLogger3.warn(
|
|
947
1109
|
`LLM rewrite failed, skipping: ${error instanceof Error ? error.message : String(error)}`
|
|
948
1110
|
);
|
|
949
1111
|
return null;
|
|
@@ -952,7 +1114,7 @@ ${rewritten}
|
|
|
952
1114
|
};
|
|
953
1115
|
|
|
954
1116
|
// packages/cli/src/acp-integration/session/rewrite/MessageRewriteMiddleware.ts
|
|
955
|
-
var
|
|
1117
|
+
var debugLogger4 = createDebugLogger("MESSAGE_REWRITE");
|
|
956
1118
|
var DEFAULT_REWRITE_TIMEOUT_MS = 3e4;
|
|
957
1119
|
var REWRITE_META_EXCLUDED_KEYS = /* @__PURE__ */ new Set([]);
|
|
958
1120
|
var MessageRewriteMiddleware = class {
|
|
@@ -1032,10 +1194,10 @@ var MessageRewriteMiddleware = class {
|
|
|
1032
1194
|
try {
|
|
1033
1195
|
const rewritten = await this.rewriter.rewrite(content, rewriteSignal);
|
|
1034
1196
|
if (!rewritten) {
|
|
1035
|
-
|
|
1197
|
+
debugLogger4.info(`Turn ${turnIdx}: no rewrite output`);
|
|
1036
1198
|
return;
|
|
1037
1199
|
}
|
|
1038
|
-
|
|
1200
|
+
debugLogger4.info(
|
|
1039
1201
|
`Turn ${turnIdx}: rewritten ${rewritten.length} chars`
|
|
1040
1202
|
);
|
|
1041
1203
|
await this.sendUpdate({
|
|
@@ -1048,7 +1210,7 @@ var MessageRewriteMiddleware = class {
|
|
|
1048
1210
|
}
|
|
1049
1211
|
});
|
|
1050
1212
|
} catch (error) {
|
|
1051
|
-
|
|
1213
|
+
debugLogger4.warn(
|
|
1052
1214
|
`Turn ${turnIdx}: rewrite failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1053
1215
|
);
|
|
1054
1216
|
}
|
|
@@ -1093,9 +1255,10 @@ function loadRewriteConfig(settings) {
|
|
|
1093
1255
|
__name(loadRewriteConfig, "loadRewriteConfig");
|
|
1094
1256
|
|
|
1095
1257
|
// packages/cli/src/acp-integration/session/Session.ts
|
|
1096
|
-
var
|
|
1258
|
+
var debugLogger5 = createDebugLogger("SESSION");
|
|
1097
1259
|
var USER_CANCEL_ABORT_REASON = "qwen:user-cancel";
|
|
1098
1260
|
var DAEMON_RETRY_META_KEY = "qwen.daemon.retry";
|
|
1261
|
+
var DAEMON_CONTINUE_META_KEY = "qwen.daemon.continueLastTurn";
|
|
1099
1262
|
function maskApiKeyForDisplay(apiKey) {
|
|
1100
1263
|
const trimmed = apiKey?.trim() ?? "";
|
|
1101
1264
|
if (trimmed.length === 0) return "(not set)";
|
|
@@ -1103,7 +1266,58 @@ function maskApiKeyForDisplay(apiKey) {
|
|
|
1103
1266
|
return `${trimmed.slice(0, 3)}...${trimmed.slice(-4)}`;
|
|
1104
1267
|
}
|
|
1105
1268
|
__name(maskApiKeyForDisplay, "maskApiKeyForDisplay");
|
|
1269
|
+
var DAEMON_TURN_TOOL_CALL_CAP = 100;
|
|
1270
|
+
var DAEMON_INVALID_TOOL_PARAMS_THRESHOLD = 3;
|
|
1106
1271
|
var PERMISSION_CANCEL_SKIP_MESSAGE = "Skipped because a permission request was cancelled before the user answered; user input is required before continuing.";
|
|
1272
|
+
var LOOP_DETECTED_SKIP_MESSAGE = "Skipped because loop detection stopped the current turn before this tool call could run.";
|
|
1273
|
+
var LOOP_DETECTED_CONTEXT_MESSAGE = "System: this turn was terminated because the model exceeded tool-call safety limits. Try a different approach on the next turn.";
|
|
1274
|
+
function createDaemonToolLoopState() {
|
|
1275
|
+
return {
|
|
1276
|
+
totalToolCalls: 0,
|
|
1277
|
+
invalidToolParamErrors: /* @__PURE__ */ new Map(),
|
|
1278
|
+
loopDetected: false
|
|
1279
|
+
};
|
|
1280
|
+
}
|
|
1281
|
+
__name(createDaemonToolLoopState, "createDaemonToolLoopState");
|
|
1282
|
+
function recordDaemonLoopDetected(config, promptId, loopType, message, loopState) {
|
|
1283
|
+
if (!loopState.loopDetected) {
|
|
1284
|
+
loopState.loopDetected = true;
|
|
1285
|
+
debugLogger5.warn(message);
|
|
1286
|
+
logLoopDetected(config, new LoopDetectedEvent(loopType, promptId));
|
|
1287
|
+
}
|
|
1288
|
+
return true;
|
|
1289
|
+
}
|
|
1290
|
+
__name(recordDaemonLoopDetected, "recordDaemonLoopDetected");
|
|
1291
|
+
function recordDaemonToolCalls(config, promptId, loopState, count) {
|
|
1292
|
+
if (!loopState || loopState.loopDetected)
|
|
1293
|
+
return loopState?.loopDetected ?? false;
|
|
1294
|
+
loopState.totalToolCalls += count;
|
|
1295
|
+
if (loopState.totalToolCalls <= DAEMON_TURN_TOOL_CALL_CAP) return false;
|
|
1296
|
+
return recordDaemonLoopDetected(
|
|
1297
|
+
config,
|
|
1298
|
+
promptId,
|
|
1299
|
+
"turn_tool_call_cap" /* TURN_TOOL_CALL_CAP */,
|
|
1300
|
+
`Stopping ACP turn after ${loopState.totalToolCalls} tool calls in one turn.`,
|
|
1301
|
+
loopState
|
|
1302
|
+
);
|
|
1303
|
+
}
|
|
1304
|
+
__name(recordDaemonToolCalls, "recordDaemonToolCalls");
|
|
1305
|
+
function recordDaemonInvalidToolParams(config, promptId, loopState, toolName, error) {
|
|
1306
|
+
if (!loopState || loopState.loopDetected)
|
|
1307
|
+
return loopState?.loopDetected ?? false;
|
|
1308
|
+
const key = toolName;
|
|
1309
|
+
const count = (loopState.invalidToolParamErrors.get(key) ?? 0) + 1;
|
|
1310
|
+
loopState.invalidToolParamErrors.set(key, count);
|
|
1311
|
+
if (count < DAEMON_INVALID_TOOL_PARAMS_THRESHOLD) return false;
|
|
1312
|
+
return recordDaemonLoopDetected(
|
|
1313
|
+
config,
|
|
1314
|
+
promptId,
|
|
1315
|
+
"invalid_tool_params_stagnation" /* INVALID_TOOL_PARAMS_STAGNATION */,
|
|
1316
|
+
`Stopping ACP turn after repeated tool parameter errors from ${toolName}: ${error.message}`,
|
|
1317
|
+
loopState
|
|
1318
|
+
);
|
|
1319
|
+
}
|
|
1320
|
+
__name(recordDaemonInvalidToolParams, "recordDaemonInvalidToolParams");
|
|
1107
1321
|
var MID_TURN_QUEUE_DRAIN_TIMEOUT_MS = 2e3;
|
|
1108
1322
|
var MID_TURN_QUEUE_RECOVERY_TIMEOUT_MS = 3e4;
|
|
1109
1323
|
var MID_TURN_QUEUE_RESOLVE_TIMEOUT_MS = 1e4;
|
|
@@ -1111,12 +1325,20 @@ var MAX_MID_TURN_DRAIN_ITEMS = 10;
|
|
|
1111
1325
|
var MID_TURN_ATTACHMENT_PROCESSING_FAILURE_TEXT = "[Attachment could not be processed]";
|
|
1112
1326
|
var MAX_MID_TURN_RESOURCE_TEXT_LENGTH = 1e5;
|
|
1113
1327
|
var MID_TURN_QUEUE_DRAIN_MAX_TIMEOUT_STRIKES = 3;
|
|
1114
|
-
|
|
1328
|
+
var TRANSIENT_FS_CODES = [
|
|
1329
|
+
"EACCES",
|
|
1330
|
+
"EIO",
|
|
1331
|
+
"EBUSY",
|
|
1332
|
+
"EPERM",
|
|
1333
|
+
"EISDIR",
|
|
1334
|
+
"ENOTDIR"
|
|
1335
|
+
];
|
|
1336
|
+
function isRecord2(value) {
|
|
1115
1337
|
return value !== null && typeof value === "object";
|
|
1116
1338
|
}
|
|
1117
|
-
__name(
|
|
1339
|
+
__name(isRecord2, "isRecord");
|
|
1118
1340
|
function isContentBlock(value) {
|
|
1119
|
-
if (!
|
|
1341
|
+
if (!isRecord2(value) || typeof value["type"] !== "string") return false;
|
|
1120
1342
|
switch (value["type"]) {
|
|
1121
1343
|
case "text":
|
|
1122
1344
|
return typeof value["text"] === "string";
|
|
@@ -1129,7 +1351,7 @@ function isContentBlock(value) {
|
|
|
1129
1351
|
case "resource":
|
|
1130
1352
|
return isEmbeddedResourceResource(value["resource"]);
|
|
1131
1353
|
default:
|
|
1132
|
-
|
|
1354
|
+
debugLogger5.warn(`Unknown ContentBlock type: ${value["type"]}`);
|
|
1133
1355
|
return false;
|
|
1134
1356
|
}
|
|
1135
1357
|
}
|
|
@@ -1155,7 +1377,7 @@ async function withTimeoutSignal(parentSignal, timeoutMs, fn) {
|
|
|
1155
1377
|
}
|
|
1156
1378
|
__name(withTimeoutSignal, "withTimeoutSignal");
|
|
1157
1379
|
function isEmbeddedResourceResource(value) {
|
|
1158
|
-
if (!
|
|
1380
|
+
if (!isRecord2(value) || typeof value["uri"] !== "string") return false;
|
|
1159
1381
|
if (typeof value["text"] === "string") {
|
|
1160
1382
|
return value["text"].length <= MAX_MID_TURN_RESOURCE_TEXT_LENGTH;
|
|
1161
1383
|
}
|
|
@@ -1168,7 +1390,7 @@ function hasInlineMediaContentBlock(content) {
|
|
|
1168
1390
|
__name(hasInlineMediaContentBlock, "hasInlineMediaContentBlock");
|
|
1169
1391
|
function capMidTurnDrainItems(items, fieldName) {
|
|
1170
1392
|
if (items.length <= MAX_MID_TURN_DRAIN_ITEMS) return items;
|
|
1171
|
-
|
|
1393
|
+
debugLogger5.warn(
|
|
1172
1394
|
`Mid-turn drain response had ${items.length} ${fieldName}; processing first ${MAX_MID_TURN_DRAIN_ITEMS}`
|
|
1173
1395
|
);
|
|
1174
1396
|
return items.slice(0, MAX_MID_TURN_DRAIN_ITEMS);
|
|
@@ -1183,7 +1405,7 @@ function getMidTurnItemDisplayTextForLog(displayText) {
|
|
|
1183
1405
|
__name(getMidTurnItemDisplayTextForLog, "getMidTurnItemDisplayTextForLog");
|
|
1184
1406
|
function getValidMidTurnContentBlocks(content, displayText) {
|
|
1185
1407
|
if (!Array.isArray(content)) {
|
|
1186
|
-
|
|
1408
|
+
debugLogger5.warn(
|
|
1187
1409
|
`Dropped invalid mid-turn item: ${getMidTurnItemDisplayTextForLog(
|
|
1188
1410
|
displayText
|
|
1189
1411
|
)}`
|
|
@@ -1193,7 +1415,7 @@ function getValidMidTurnContentBlocks(content, displayText) {
|
|
|
1193
1415
|
const validBlocks = content.filter(isContentBlock);
|
|
1194
1416
|
const invalidBlockCount = content.length - validBlocks.length;
|
|
1195
1417
|
if (invalidBlockCount > 0) {
|
|
1196
|
-
|
|
1418
|
+
debugLogger5.warn(
|
|
1197
1419
|
`Dropped ${invalidBlockCount} invalid mid-turn content block(s): ${getMidTurnItemDisplayTextForLog(
|
|
1198
1420
|
displayText
|
|
1199
1421
|
)}`
|
|
@@ -1213,11 +1435,11 @@ function getStructuredMidTurnDisplayText(content, displayText) {
|
|
|
1213
1435
|
}
|
|
1214
1436
|
__name(getStructuredMidTurnDisplayText, "getStructuredMidTurnDisplayText");
|
|
1215
1437
|
function parseMidTurnDrainResponse(response) {
|
|
1216
|
-
if (!
|
|
1438
|
+
if (!isRecord2(response)) return [];
|
|
1217
1439
|
if (Array.isArray(response["items"])) {
|
|
1218
1440
|
return capMidTurnDrainItems(response["items"], "item(s)").flatMap(
|
|
1219
1441
|
(item) => {
|
|
1220
|
-
if (!
|
|
1442
|
+
if (!isRecord2(item)) {
|
|
1221
1443
|
return [];
|
|
1222
1444
|
}
|
|
1223
1445
|
const content = getValidMidTurnContentBlocks(
|
|
@@ -1239,7 +1461,7 @@ function parseMidTurnDrainResponse(response) {
|
|
|
1239
1461
|
);
|
|
1240
1462
|
}
|
|
1241
1463
|
if (!Array.isArray(response["messages"])) {
|
|
1242
|
-
|
|
1464
|
+
debugLogger5.warn(
|
|
1243
1465
|
`Mid-turn drain response had no recognized 'items' or 'messages' field; keys: ${Object.keys(
|
|
1244
1466
|
response
|
|
1245
1467
|
).join(", ")}`
|
|
@@ -1262,6 +1484,17 @@ var MidTurnDrainTimeoutError = class extends Error {
|
|
|
1262
1484
|
}
|
|
1263
1485
|
};
|
|
1264
1486
|
var MAX_NOTIFICATION_QUEUE = 20;
|
|
1487
|
+
function resolveHomeLoopResolverRoots({
|
|
1488
|
+
homeQwenDir = Storage.getGlobalQwenDir(),
|
|
1489
|
+
homeDir = os.homedir(),
|
|
1490
|
+
qwenHome = process.env["QWEN_HOME"]
|
|
1491
|
+
} = {}) {
|
|
1492
|
+
return {
|
|
1493
|
+
homeConfineRoot: (qwenHome ? homeQwenDir : homeDir) || path2.dirname(homeQwenDir),
|
|
1494
|
+
homeQwenDir
|
|
1495
|
+
};
|
|
1496
|
+
}
|
|
1497
|
+
__name(resolveHomeLoopResolverRoots, "resolveHomeLoopResolverRoots");
|
|
1265
1498
|
function computeInitialTurnFromHistory(records, sessionId) {
|
|
1266
1499
|
let maxPromptTurn = 0;
|
|
1267
1500
|
let userMessageCount = 0;
|
|
@@ -1296,7 +1529,7 @@ async function fireSessionPermissionDeniedForAutoMode(config, decision, outcome,
|
|
|
1296
1529
|
callId
|
|
1297
1530
|
);
|
|
1298
1531
|
} catch (hookError) {
|
|
1299
|
-
|
|
1532
|
+
debugLogger5.warn(
|
|
1300
1533
|
`PermissionDenied hook failed for tool ${callId}: ${hookError instanceof Error ? hookError.message : String(hookError)}`
|
|
1301
1534
|
);
|
|
1302
1535
|
}
|
|
@@ -1337,6 +1570,18 @@ function isUserPromptRecord(record) {
|
|
|
1337
1570
|
) ?? false;
|
|
1338
1571
|
}
|
|
1339
1572
|
__name(isUserPromptRecord, "isUserPromptRecord");
|
|
1573
|
+
var AT_TOKEN_RE = /@([^\s,;!?()[\]{}]+)/g;
|
|
1574
|
+
function collectExtensionMentionRefs(text, mentions) {
|
|
1575
|
+
for (const match of text.matchAll(AT_TOKEN_RE)) {
|
|
1576
|
+
const pathName = match[1];
|
|
1577
|
+
if (!pathName) continue;
|
|
1578
|
+
const ref = parseExtensionRef(pathName);
|
|
1579
|
+
if (ref) {
|
|
1580
|
+
mentions.set(ref.name.toLowerCase(), ref.name);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
__name(collectExtensionMentionRefs, "collectExtensionMentionRefs");
|
|
1340
1585
|
async function buildAvailableCommandsSnapshot(config, abortSignal = AbortSignal.timeout(1e4), settings) {
|
|
1341
1586
|
const slashCommands = await getAvailableCommands(
|
|
1342
1587
|
config,
|
|
@@ -1356,7 +1601,12 @@ async function buildAvailableCommandsSnapshot(config, abortSignal = AbortSignal.
|
|
|
1356
1601
|
sourceLabel: cmd.sourceLabel,
|
|
1357
1602
|
supportedModes: getEffectiveSupportedModes(cmd),
|
|
1358
1603
|
subcommands: getCommandSubcommandNames(cmd),
|
|
1359
|
-
modelInvocable: cmd.modelInvocable === true
|
|
1604
|
+
modelInvocable: cmd.modelInvocable === true,
|
|
1605
|
+
// Carry aliases so a channel consumer (which only sees the wire snapshot,
|
|
1606
|
+
// not the command registry) can recognize an aliased command and avoid
|
|
1607
|
+
// tagging it. _meta is ACP's extension point; omitted when there are none
|
|
1608
|
+
// so command entries without aliases stay byte-identical on the wire.
|
|
1609
|
+
...cmd.altNames && cmd.altNames.length > 0 ? { altNames: cmd.altNames } : {}
|
|
1360
1610
|
}
|
|
1361
1611
|
};
|
|
1362
1612
|
});
|
|
@@ -1379,7 +1629,7 @@ async function buildAvailableCommandsSnapshot(config, abortSignal = AbortSignal.
|
|
|
1379
1629
|
}
|
|
1380
1630
|
}
|
|
1381
1631
|
} catch (error) {
|
|
1382
|
-
|
|
1632
|
+
debugLogger5.error("Error loading available skills:", error);
|
|
1383
1633
|
}
|
|
1384
1634
|
for (const command of slashCommands) {
|
|
1385
1635
|
if (command.kind !== "skill" /* SKILL */ || !command.skillDetail) {
|
|
@@ -1451,6 +1701,13 @@ var Session = class {
|
|
|
1451
1701
|
cronQueue = [];
|
|
1452
1702
|
cronProcessing = false;
|
|
1453
1703
|
cronAbortController = null;
|
|
1704
|
+
// Resolves the `<<loop.md>>` / `<<loop.md-dynamic>>` sentinels at fire time.
|
|
1705
|
+
// Lazily created on the first loop tick; its content cache is reset on
|
|
1706
|
+
// compaction (see #sendMessageStreamWithAutoCompression) and it is rebuilt if
|
|
1707
|
+
// the working dir changes (e.g. /cd) so it always reads the current project's
|
|
1708
|
+
// loop.md.
|
|
1709
|
+
loopTickResolver = null;
|
|
1710
|
+
loopTickResolverRoot = null;
|
|
1454
1711
|
cronCompletion = null;
|
|
1455
1712
|
cronDisabledByTokenLimit = false;
|
|
1456
1713
|
lastPromptTokenCount = 0;
|
|
@@ -1514,7 +1771,7 @@ var Session = class {
|
|
|
1514
1771
|
*/
|
|
1515
1772
|
startCronScheduler() {
|
|
1516
1773
|
this.#startCronSchedulerIfNeeded().catch((error) => {
|
|
1517
|
-
|
|
1774
|
+
debugLogger5.warn(
|
|
1518
1775
|
`Cron scheduler startup failed [session ${this.sessionId}]: ${error}`
|
|
1519
1776
|
);
|
|
1520
1777
|
});
|
|
@@ -1561,7 +1818,7 @@ var Session = class {
|
|
|
1561
1818
|
installRewriter() {
|
|
1562
1819
|
const rewriteConfig = loadRewriteConfig(this.settings);
|
|
1563
1820
|
if (rewriteConfig?.enabled) {
|
|
1564
|
-
|
|
1821
|
+
debugLogger5.info("Message rewrite middleware enabled");
|
|
1565
1822
|
this.messageRewriter = new MessageRewriteMiddleware(
|
|
1566
1823
|
this.config,
|
|
1567
1824
|
rewriteConfig,
|
|
@@ -1572,7 +1829,7 @@ var Session = class {
|
|
|
1572
1829
|
#installGoalTerminalObserver() {
|
|
1573
1830
|
setGoalTerminalObserver(this.sessionId, (event) => {
|
|
1574
1831
|
void this.messageEmitter.emitGoalTerminal(event).catch((error) => {
|
|
1575
|
-
|
|
1832
|
+
debugLogger5.warn(
|
|
1576
1833
|
`Failed to emit goal terminal update: ${this.#formatError(error)}`
|
|
1577
1834
|
);
|
|
1578
1835
|
});
|
|
@@ -1580,7 +1837,7 @@ var Session = class {
|
|
|
1580
1837
|
}
|
|
1581
1838
|
emitGoalStatus(status) {
|
|
1582
1839
|
void this.messageEmitter.emitGoalStatus(status).catch((error) => {
|
|
1583
|
-
|
|
1840
|
+
debugLogger5.warn(
|
|
1584
1841
|
`Failed to emit goal status update: ${this.#formatError(error)}`
|
|
1585
1842
|
);
|
|
1586
1843
|
});
|
|
@@ -1783,6 +2040,36 @@ var Session = class {
|
|
|
1783
2040
|
this.pendingPromptCompletion = null;
|
|
1784
2041
|
}
|
|
1785
2042
|
}
|
|
2043
|
+
/**
|
|
2044
|
+
* Classify whether an unfinished previous turn can be resumed — an
|
|
2045
|
+
* interrupted prompt (the model never answered) or a turn left with dangling
|
|
2046
|
+
* tool calls — without injecting a synthetic "continue" user message.
|
|
2047
|
+
* Classifies from persisted history. Idempotent no-op (accepted:false) when
|
|
2048
|
+
* the last turn ended cleanly or a prompt is already in flight.
|
|
2049
|
+
*
|
|
2050
|
+
* This is the accept/reject pre-check only — it does NOT fire the turn. When
|
|
2051
|
+
* accepted, the daemon bridge drives the continuation through the normal
|
|
2052
|
+
* prompt-admission path (`sendPrompt` with the trusted continue meta) so it is
|
|
2053
|
+
* tracked like any other prompt; `prompt()` then re-detects/strips
|
|
2054
|
+
* authoritatively. Powers `qwen/control/session/continue`.
|
|
2055
|
+
*/
|
|
2056
|
+
async continueLastTurn() {
|
|
2057
|
+
const geminiClient = this.config.getGeminiClient();
|
|
2058
|
+
if (!geminiClient || !geminiClient.isInitialized()) {
|
|
2059
|
+
return { accepted: false, interruption: "none" };
|
|
2060
|
+
}
|
|
2061
|
+
const chat = this.#getCurrentChat();
|
|
2062
|
+
const detection = detectTurnInterruption(
|
|
2063
|
+
chat.getHistoryTailShallow?.(TURN_INTERRUPTION_HISTORY_TAIL_COUNT) ?? chat.getHistoryTail(TURN_INTERRUPTION_HISTORY_TAIL_COUNT)
|
|
2064
|
+
);
|
|
2065
|
+
if (detection.kind === "none") {
|
|
2066
|
+
return { accepted: false, interruption: "none" };
|
|
2067
|
+
}
|
|
2068
|
+
if (this.pendingPrompt && !this.pendingPrompt.signal.aborted) {
|
|
2069
|
+
return { accepted: false, interruption: detection.kind };
|
|
2070
|
+
}
|
|
2071
|
+
return { accepted: true, interruption: detection.kind };
|
|
2072
|
+
}
|
|
1786
2073
|
/**
|
|
1787
2074
|
* Generate a server-side follow-up suggestion for the just-completed
|
|
1788
2075
|
* turn and push it to attached clients via the daemon's
|
|
@@ -1819,7 +2106,7 @@ var Session = class {
|
|
|
1819
2106
|
const fullHistory = chat.getHistory(true);
|
|
1820
2107
|
const lastEntry = fullHistory[fullHistory.length - 1];
|
|
1821
2108
|
if (!lastEntry || lastEntry.role !== "model") {
|
|
1822
|
-
|
|
2109
|
+
debugLogger5.debug(
|
|
1823
2110
|
"Skipping followup suggestion: last history entry is not model"
|
|
1824
2111
|
);
|
|
1825
2112
|
return;
|
|
@@ -1855,9 +2142,9 @@ var Session = class {
|
|
|
1855
2142
|
}
|
|
1856
2143
|
} catch (error) {
|
|
1857
2144
|
if (ac.signal.aborted) {
|
|
1858
|
-
|
|
2145
|
+
debugLogger5.debug("Follow-up suggestion generation aborted");
|
|
1859
2146
|
} else {
|
|
1860
|
-
|
|
2147
|
+
debugLogger5.warn("Follow-up suggestion generation failed", error);
|
|
1861
2148
|
}
|
|
1862
2149
|
} finally {
|
|
1863
2150
|
if (this.followupAbort === ac) {
|
|
@@ -1900,7 +2187,40 @@ var Session = class {
|
|
|
1900
2187
|
)
|
|
1901
2188
|
);
|
|
1902
2189
|
const isRetry = params.retry === true || params._meta?.[DAEMON_RETRY_META_KEY] === true;
|
|
1903
|
-
|
|
2190
|
+
const isContinue = params._meta?.[DAEMON_CONTINUE_META_KEY] === true;
|
|
2191
|
+
let continuationParts = null;
|
|
2192
|
+
let strippedOrphanEntries = null;
|
|
2193
|
+
let orphanPushCountSnapshot = 0;
|
|
2194
|
+
if (isContinue) {
|
|
2195
|
+
const detection = detectTurnInterruption(
|
|
2196
|
+
this.#getCurrentChat().getHistory()
|
|
2197
|
+
);
|
|
2198
|
+
if (detection.kind === "none") {
|
|
2199
|
+
debugLogger5.warn(
|
|
2200
|
+
`[Session] continue ${promptId}: no interrupted turn on re-detection, nothing to continue`
|
|
2201
|
+
);
|
|
2202
|
+
logConversationFinishedEvent(
|
|
2203
|
+
this.config,
|
|
2204
|
+
new ConversationFinishedEvent(
|
|
2205
|
+
this.config.getApprovalMode(),
|
|
2206
|
+
0
|
|
2207
|
+
)
|
|
2208
|
+
);
|
|
2209
|
+
return { stopReason: "end_turn" };
|
|
2210
|
+
}
|
|
2211
|
+
if (detection.kind === "interrupted_prompt") {
|
|
2212
|
+
strippedOrphanEntries = this.#getCurrentChat().stripOrphanedUserEntriesFromHistory() ?? null;
|
|
2213
|
+
orphanPushCountSnapshot = this.#getCurrentChat().getUserContentPushCount?.() ?? 0;
|
|
2214
|
+
continuationParts = detection.parts;
|
|
2215
|
+
} else {
|
|
2216
|
+
continuationParts = buildSyntheticToolResponseParts(
|
|
2217
|
+
detection.danglingCalls,
|
|
2218
|
+
ORPHAN_TOOL_USE_REPAIR_REASON
|
|
2219
|
+
);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
if (isContinue) {
|
|
2223
|
+
} else if (isRetry) {
|
|
1904
2224
|
this.#getCurrentChat().stripOrphanedUserEntriesFromHistory();
|
|
1905
2225
|
} else {
|
|
1906
2226
|
this.config.getChatRecordingService()?.recordUserMessage(promptText);
|
|
@@ -1910,7 +2230,9 @@ var Session = class {
|
|
|
1910
2230
|
);
|
|
1911
2231
|
const inputText = firstTextBlock?.text || "";
|
|
1912
2232
|
let parts;
|
|
1913
|
-
if (
|
|
2233
|
+
if (isContinue) {
|
|
2234
|
+
parts = continuationParts;
|
|
2235
|
+
} else if (isSlashCommand(inputText)) {
|
|
1914
2236
|
const slashCommandResult = await handleSlashCommand(
|
|
1915
2237
|
inputText,
|
|
1916
2238
|
pendingSend,
|
|
@@ -1932,7 +2254,7 @@ var Session = class {
|
|
|
1932
2254
|
}
|
|
1933
2255
|
const hooksEnabled = !this.config.getDisableAllHooks?.();
|
|
1934
2256
|
const messageBus = this.config.getMessageBus?.();
|
|
1935
|
-
if (hooksEnabled && messageBus && this.config.hasHooksForEvent?.("UserPromptSubmit")) {
|
|
2257
|
+
if (!isContinue && hooksEnabled && messageBus && this.config.hasHooksForEvent?.("UserPromptSubmit")) {
|
|
1936
2258
|
const response = await messageBus.request(
|
|
1937
2259
|
{
|
|
1938
2260
|
type: "hook-execution-request" /* HOOK_EXECUTION_REQUEST */,
|
|
@@ -1948,7 +2270,7 @@ var Session = class {
|
|
|
1948
2270
|
if (hookOutput?.isBlockingDecision() || hookOutput?.shouldStopExecution()) {
|
|
1949
2271
|
const blockReason = hookOutput?.getEffectiveReason() || "No reason provided";
|
|
1950
2272
|
await this.messageEmitter.emitAgentMessage(
|
|
1951
|
-
`\
|
|
2273
|
+
`\u2717 **UserPromptSubmit blocked**: ${blockReason}`
|
|
1952
2274
|
);
|
|
1953
2275
|
return { stopReason: "end_turn" };
|
|
1954
2276
|
}
|
|
@@ -1966,30 +2288,34 @@ var Session = class {
|
|
|
1966
2288
|
this.config.getChatRecordingService()?.recordFileHistorySnapshot(latestSnapshot);
|
|
1967
2289
|
}
|
|
1968
2290
|
} catch (e) {
|
|
1969
|
-
|
|
2291
|
+
debugLogger5.error(`FileHistory: recordSnapshot failed: ${e}`);
|
|
1970
2292
|
}
|
|
1971
2293
|
} catch (e) {
|
|
1972
|
-
|
|
2294
|
+
debugLogger5.error(`FileHistory: makeSnapshot failed: ${e}`);
|
|
1973
2295
|
}
|
|
1974
2296
|
const systemReminders = await this.#buildInitialSystemReminders();
|
|
1975
2297
|
if (systemReminders.length > 0) {
|
|
1976
|
-
|
|
2298
|
+
const alreadyHasReminder = isContinue && parts.some(
|
|
2299
|
+
(part) => isSystemReminderContent({ role: "user", parts: [part] })
|
|
2300
|
+
);
|
|
2301
|
+
if (!alreadyHasReminder) {
|
|
2302
|
+
parts = insertAfterFunctionResponses(parts, systemReminders);
|
|
2303
|
+
}
|
|
1977
2304
|
}
|
|
1978
2305
|
if (this.pendingWorktreeNotice) {
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
text: `<system-reminder>
|
|
2306
|
+
const noticePart = {
|
|
2307
|
+
text: `<system-reminder>
|
|
1982
2308
|
${this.pendingWorktreeNotice}
|
|
1983
2309
|
</system-reminder>
|
|
1984
2310
|
|
|
1985
2311
|
`
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
];
|
|
2312
|
+
};
|
|
2313
|
+
parts = insertAfterFunctionResponses(parts, [noticePart]);
|
|
1989
2314
|
this.pendingWorktreeNotice = null;
|
|
1990
2315
|
}
|
|
1991
2316
|
let nextMessage = { role: "user", parts };
|
|
1992
2317
|
let turnCount = 0;
|
|
2318
|
+
const toolLoopState = createDaemonToolLoopState();
|
|
1993
2319
|
try {
|
|
1994
2320
|
while (nextMessage !== null) {
|
|
1995
2321
|
turnCount++;
|
|
@@ -2009,7 +2335,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2009
2335
|
if (!sendResult.responseStream) {
|
|
2010
2336
|
this.#preserveUnsentMessageHistory(
|
|
2011
2337
|
nextMessage,
|
|
2012
|
-
sendResult.stopReason === "cancelled"
|
|
2338
|
+
isContinue || sendResult.stopReason === "cancelled"
|
|
2013
2339
|
);
|
|
2014
2340
|
return { stopReason: sendResult.stopReason };
|
|
2015
2341
|
}
|
|
@@ -2040,6 +2366,12 @@ ${this.pendingWorktreeNotice}
|
|
|
2040
2366
|
}
|
|
2041
2367
|
}
|
|
2042
2368
|
} catch (error) {
|
|
2369
|
+
if (strippedOrphanEntries && (this.#getCurrentChat().getUserContentPushCount?.() ?? 0) <= orphanPushCountSnapshot) {
|
|
2370
|
+
for (const entry of strippedOrphanEntries) {
|
|
2371
|
+
this.#getCurrentChat().addHistory(entry);
|
|
2372
|
+
}
|
|
2373
|
+
strippedOrphanEntries = null;
|
|
2374
|
+
}
|
|
2043
2375
|
if (pendingSend.signal.aborted && pendingSend.signal.reason === USER_CANCEL_ABORT_REASON && this.#isAbortError(error)) {
|
|
2044
2376
|
return { stopReason: "cancelled" };
|
|
2045
2377
|
}
|
|
@@ -2053,7 +2385,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2053
2385
|
const hooksEnabledForStopFailure = !this.config.getDisableAllHooks?.();
|
|
2054
2386
|
if (hooksEnabledForStopFailure && hookSystem && this.config.hasHooksForEvent?.("StopFailure")) {
|
|
2055
2387
|
hookSystem.fireStopFailureEvent(errorType, errorMessage).catch((err) => {
|
|
2056
|
-
|
|
2388
|
+
debugLogger5.warn(`StopFailure hook failed: ${err}`);
|
|
2057
2389
|
});
|
|
2058
2390
|
}
|
|
2059
2391
|
if (errorStatus === 429) {
|
|
@@ -2080,10 +2412,11 @@ ${this.pendingWorktreeNotice}
|
|
|
2080
2412
|
const toolRun = await this.runToolCalls(
|
|
2081
2413
|
pendingSend.signal,
|
|
2082
2414
|
promptId,
|
|
2083
|
-
functionCalls
|
|
2415
|
+
functionCalls,
|
|
2416
|
+
toolLoopState
|
|
2084
2417
|
);
|
|
2085
2418
|
if (toolRun.stopAfterPermissionCancel) {
|
|
2086
|
-
await this.#
|
|
2419
|
+
await this.#preserveStoppedToolRun(
|
|
2087
2420
|
toolRun,
|
|
2088
2421
|
pendingSend.signal
|
|
2089
2422
|
);
|
|
@@ -2093,6 +2426,13 @@ ${this.pendingWorktreeNotice}
|
|
|
2093
2426
|
toolRun,
|
|
2094
2427
|
pendingSend.signal
|
|
2095
2428
|
);
|
|
2429
|
+
if (toolRun.loopDetected) {
|
|
2430
|
+
await this.#preserveStoppedToolRun(
|
|
2431
|
+
toolRun,
|
|
2432
|
+
pendingSend.signal
|
|
2433
|
+
);
|
|
2434
|
+
return { stopReason: "end_turn" };
|
|
2435
|
+
}
|
|
2096
2436
|
}
|
|
2097
2437
|
}
|
|
2098
2438
|
if (this.messageRewriter) {
|
|
@@ -2140,13 +2480,18 @@ ${this.pendingWorktreeNotice}
|
|
|
2140
2480
|
return { stopReason: "end_turn" };
|
|
2141
2481
|
}
|
|
2142
2482
|
const responseText = this.#getCurrentChat().getLastModelMessageText?.() || "[no response text]";
|
|
2483
|
+
const contextUsage = buildContextUsage(
|
|
2484
|
+
this.config.getContentGeneratorConfig()?.contextWindowSize ?? DEFAULT_TOKEN_LIMIT,
|
|
2485
|
+
this.lastPromptTokenCount
|
|
2486
|
+
);
|
|
2143
2487
|
const response = await messageBus.request(
|
|
2144
2488
|
{
|
|
2145
2489
|
type: "hook-execution-request" /* HOOK_EXECUTION_REQUEST */,
|
|
2146
2490
|
eventName: "Stop",
|
|
2147
2491
|
input: {
|
|
2148
2492
|
stop_hook_active: true,
|
|
2149
|
-
last_assistant_message: responseText
|
|
2493
|
+
last_assistant_message: responseText,
|
|
2494
|
+
...contextUsage
|
|
2150
2495
|
},
|
|
2151
2496
|
signal: pendingSend.signal
|
|
2152
2497
|
},
|
|
@@ -2175,7 +2520,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2175
2520
|
warning
|
|
2176
2521
|
);
|
|
2177
2522
|
await this.messageEmitter.emitAgentMessage(warning);
|
|
2178
|
-
|
|
2523
|
+
debugLogger5.warn(warning);
|
|
2179
2524
|
return { stopReason: "end_turn" };
|
|
2180
2525
|
}
|
|
2181
2526
|
if (stopHookIterationCount > 1) {
|
|
@@ -2190,6 +2535,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2190
2535
|
role: "user",
|
|
2191
2536
|
parts: continueParts
|
|
2192
2537
|
};
|
|
2538
|
+
const toolLoopState = createDaemonToolLoopState();
|
|
2193
2539
|
while (nextMessage !== null) {
|
|
2194
2540
|
if (pendingSend.signal.aborted) {
|
|
2195
2541
|
return { stopReason: "cancelled" };
|
|
@@ -2246,7 +2592,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2246
2592
|
const hooksEnabledForStopFailure = !this.config.getDisableAllHooks?.();
|
|
2247
2593
|
if (hooksEnabledForStopFailure && hookSystem && this.config.hasHooksForEvent?.("StopFailure")) {
|
|
2248
2594
|
hookSystem.fireStopFailureEvent(errorType, errorMessage).catch((err) => {
|
|
2249
|
-
|
|
2595
|
+
debugLogger5.warn(`StopFailure hook failed: ${err}`);
|
|
2250
2596
|
});
|
|
2251
2597
|
}
|
|
2252
2598
|
if (errorStatus === 429) {
|
|
@@ -2270,19 +2616,21 @@ ${this.pendingWorktreeNotice}
|
|
|
2270
2616
|
const toolRun = await this.runToolCalls(
|
|
2271
2617
|
pendingSend.signal,
|
|
2272
2618
|
promptId,
|
|
2273
|
-
functionCalls
|
|
2619
|
+
functionCalls,
|
|
2620
|
+
toolLoopState
|
|
2274
2621
|
);
|
|
2275
2622
|
if (toolRun.stopAfterPermissionCancel) {
|
|
2276
|
-
await this.#
|
|
2277
|
-
toolRun,
|
|
2278
|
-
pendingSend.signal
|
|
2279
|
-
);
|
|
2623
|
+
await this.#preserveStoppedToolRun(toolRun, pendingSend.signal);
|
|
2280
2624
|
return { stopReason: "end_turn" };
|
|
2281
2625
|
}
|
|
2282
2626
|
nextMessage = await this.#buildNextMessageAfterToolRun(
|
|
2283
2627
|
toolRun,
|
|
2284
2628
|
pendingSend.signal
|
|
2285
2629
|
);
|
|
2630
|
+
if (toolRun.loopDetected) {
|
|
2631
|
+
await this.#preserveStoppedToolRun(toolRun, pendingSend.signal);
|
|
2632
|
+
return { stopReason: "end_turn" };
|
|
2633
|
+
}
|
|
2286
2634
|
}
|
|
2287
2635
|
}
|
|
2288
2636
|
continue;
|
|
@@ -2323,21 +2671,22 @@ ${this.pendingWorktreeNotice}
|
|
|
2323
2671
|
compressionInfo = compressed;
|
|
2324
2672
|
this.#recordCompressionTokenCount(compressed);
|
|
2325
2673
|
if (compressed.compressionStatus === 1 /* COMPRESSED */) {
|
|
2674
|
+
this.loopTickResolver?.resetCache();
|
|
2326
2675
|
const reasonClause = compressed.triggerReason === "image_overflow" ? `accumulated enough tool screenshots to trigger compaction for ${this.config.getModel()}` : `approached the input token limit for ${this.config.getModel()}`;
|
|
2327
2676
|
compressionDiagnostic = `IMPORTANT: This conversation ${reasonClause}. A compressed context will be sent for future messages (compressed from: ${compressed.originalTokenCount ?? "unknown"} to ${compressed.newTokenCount ?? "unknown"} tokens).`;
|
|
2328
2677
|
}
|
|
2329
2678
|
} catch (compressionError) {
|
|
2330
2679
|
if (abortSignal.aborted || this.#isAbortError(compressionError)) {
|
|
2331
|
-
|
|
2680
|
+
debugLogger5.debug(`Auto-compression aborted for prompt ${promptId}`);
|
|
2332
2681
|
return { responseStream: null, stopReason: "cancelled" };
|
|
2333
2682
|
}
|
|
2334
|
-
|
|
2683
|
+
debugLogger5.warn(
|
|
2335
2684
|
`Auto-compression failed for prompt ${promptId}; proceeding without compression: ` + this.#formatError(compressionError)
|
|
2336
2685
|
);
|
|
2337
2686
|
}
|
|
2338
2687
|
}
|
|
2339
2688
|
if (abortSignal.aborted) {
|
|
2340
|
-
|
|
2689
|
+
debugLogger5.debug(`Auto-compression aborted for prompt ${promptId}`);
|
|
2341
2690
|
return { responseStream: null, stopReason: "cancelled" };
|
|
2342
2691
|
}
|
|
2343
2692
|
if (!compressionInfo) {
|
|
@@ -2347,7 +2696,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2347
2696
|
if (sessionTokenLimit > 0) {
|
|
2348
2697
|
const lastPromptTokenCount = this.#getPostCompressionTokenCount(compressionInfo);
|
|
2349
2698
|
if (lastPromptTokenCount > sessionTokenLimit) {
|
|
2350
|
-
|
|
2699
|
+
debugLogger5.warn(
|
|
2351
2700
|
`Session token limit exceeded for prompt ${promptId}: ${lastPromptTokenCount} > ${sessionTokenLimit}. Send dropped.`
|
|
2352
2701
|
);
|
|
2353
2702
|
await this.#emitAgentDiagnosticMessageSafely(
|
|
@@ -2364,7 +2713,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2364
2713
|
);
|
|
2365
2714
|
}
|
|
2366
2715
|
if (abortSignal.aborted) {
|
|
2367
|
-
|
|
2716
|
+
debugLogger5.debug(
|
|
2368
2717
|
`Send aborted after compression diagnostic for prompt ${promptId}`
|
|
2369
2718
|
);
|
|
2370
2719
|
return { responseStream: null, stopReason: "cancelled" };
|
|
@@ -2392,7 +2741,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2392
2741
|
) ?? [];
|
|
2393
2742
|
const droppedParts = (message.parts?.length ?? 0) - functionResponseParts.length;
|
|
2394
2743
|
if (droppedParts > 0) {
|
|
2395
|
-
|
|
2744
|
+
debugLogger5.debug(
|
|
2396
2745
|
`Dropping ${droppedParts} non-functionResponse part(s) from unsent ACP message after send was skipped.`
|
|
2397
2746
|
);
|
|
2398
2747
|
}
|
|
@@ -2403,12 +2752,13 @@ ${this.pendingWorktreeNotice}
|
|
|
2403
2752
|
});
|
|
2404
2753
|
}
|
|
2405
2754
|
}
|
|
2406
|
-
async #
|
|
2755
|
+
async #preserveStoppedToolRun(toolRun, abortSignal) {
|
|
2407
2756
|
this.#preserveUnsentMessageHistory(
|
|
2408
2757
|
{
|
|
2409
2758
|
role: "user",
|
|
2410
2759
|
parts: [
|
|
2411
2760
|
...toolRun.parts,
|
|
2761
|
+
...toolRun.loopDetected ? [{ text: LOOP_DETECTED_CONTEXT_MESSAGE }] : [],
|
|
2412
2762
|
...await this.#drainMidTurnUserMessages(abortSignal)
|
|
2413
2763
|
]
|
|
2414
2764
|
},
|
|
@@ -2417,8 +2767,12 @@ ${this.pendingWorktreeNotice}
|
|
|
2417
2767
|
await this.messageRewriter?.waitForPendingRewrites();
|
|
2418
2768
|
}
|
|
2419
2769
|
async #buildNextMessageAfterToolRun(toolRun, abortSignal) {
|
|
2770
|
+
if (toolRun.loopDetected) {
|
|
2771
|
+
debugLogger5.debug("Stopping ACP turn after daemon loop detection.");
|
|
2772
|
+
return null;
|
|
2773
|
+
}
|
|
2420
2774
|
if (toolRun.repeatedDuplicateProviderToolCall) {
|
|
2421
|
-
|
|
2775
|
+
debugLogger5.debug(
|
|
2422
2776
|
"Stopping ACP turn after dropping repeated duplicate provider tool-call response."
|
|
2423
2777
|
);
|
|
2424
2778
|
return null;
|
|
@@ -2496,7 +2850,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2496
2850
|
try {
|
|
2497
2851
|
await this.#emitAgentDiagnosticMessage(text);
|
|
2498
2852
|
} catch (notifyError) {
|
|
2499
|
-
|
|
2853
|
+
debugLogger5.warn(`${failureContext}: ${this.#formatError(notifyError)}`);
|
|
2500
2854
|
}
|
|
2501
2855
|
}
|
|
2502
2856
|
async #emitAgentDiagnosticMessage(text) {
|
|
@@ -2545,7 +2899,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2545
2899
|
if (isPermanentError) {
|
|
2546
2900
|
this.midTurnDrainUnavailable = true;
|
|
2547
2901
|
}
|
|
2548
|
-
|
|
2902
|
+
debugLogger5.warn(
|
|
2549
2903
|
`Mid-turn queue drain ${isPermanentError ? "permanently " : ""}unavailable [session ${this.sessionId}]: ${errorMessage}`
|
|
2550
2904
|
);
|
|
2551
2905
|
return this.#buildMidTurnParts(recovered, abortSignal);
|
|
@@ -2587,14 +2941,14 @@ ${this.pendingWorktreeNotice}
|
|
|
2587
2941
|
clearTimeout(timer);
|
|
2588
2942
|
}
|
|
2589
2943
|
if (late === expired) {
|
|
2590
|
-
|
|
2944
|
+
debugLogger5.warn(
|
|
2591
2945
|
`[mid-turn] dropped a drain response that arrived after the ${MID_TURN_QUEUE_RECOVERY_TIMEOUT_MS}ms recovery deadline [session ${this.sessionId}]`
|
|
2592
2946
|
);
|
|
2593
2947
|
return;
|
|
2594
2948
|
}
|
|
2595
2949
|
const lateMessages = parseMidTurnDrainResponse(late);
|
|
2596
2950
|
if (lateMessages.length > 0) {
|
|
2597
|
-
|
|
2951
|
+
debugLogger5.debug(
|
|
2598
2952
|
`[mid-turn] recovered ${lateMessages.length} message(s) from a timed-out drain [session ${this.sessionId}]`
|
|
2599
2953
|
);
|
|
2600
2954
|
this.midTurnRecoveredMessages.push(...lateMessages);
|
|
@@ -2620,7 +2974,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2620
2974
|
} catch (messageError) {
|
|
2621
2975
|
if (abortSignal.aborted) return parts;
|
|
2622
2976
|
const errorMessage = this.#formatError(messageError);
|
|
2623
|
-
|
|
2977
|
+
debugLogger5.warn(`Failed to resolve mid-turn message: ${errorMessage}`);
|
|
2624
2978
|
rawParts = [{ text: displayText }];
|
|
2625
2979
|
if (message.kind === "structured" && hasInlineMediaContentBlock(message.content)) {
|
|
2626
2980
|
rawParts.push({ text: MID_TURN_ATTACHMENT_PROCESSING_FAILURE_TEXT });
|
|
@@ -2645,20 +2999,23 @@ ${this.pendingWorktreeNotice}
|
|
|
2645
2999
|
try {
|
|
2646
3000
|
await scheduler.enableDurable(this.sessionId);
|
|
2647
3001
|
} catch (err) {
|
|
2648
|
-
|
|
3002
|
+
debugLogger5.warn(
|
|
2649
3003
|
`Durable cron init failed \u2014 persistent tasks will not fire in this session: ${err}`
|
|
2650
3004
|
);
|
|
2651
3005
|
}
|
|
2652
3006
|
if (this.disposed) return;
|
|
2653
3007
|
if (!scheduler.hasPendingWork) return;
|
|
2654
|
-
scheduler.start(
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
3008
|
+
scheduler.start(
|
|
3009
|
+
(job) => {
|
|
3010
|
+
if (this.cronDisabledByTokenLimit) return;
|
|
3011
|
+
if (job.missed && detectAutonomousSentinel(job.prompt)) return;
|
|
3012
|
+
this.cronQueue.push({
|
|
3013
|
+
prompt: job.prompt,
|
|
3014
|
+
source: job.cronExpr === "@wakeup" ? "loop" : "cron"
|
|
3015
|
+
});
|
|
3016
|
+
void this.#drainCronQueue();
|
|
3017
|
+
}
|
|
3018
|
+
);
|
|
2662
3019
|
}
|
|
2663
3020
|
/**
|
|
2664
3021
|
* Processes queued cron prompts one at a time. Uses `cronProcessing`
|
|
@@ -2692,6 +3049,27 @@ ${this.pendingWorktreeNotice}
|
|
|
2692
3049
|
}
|
|
2693
3050
|
}
|
|
2694
3051
|
}
|
|
3052
|
+
#getLoopTickResolver() {
|
|
3053
|
+
const root = this.config.getWorkingDir();
|
|
3054
|
+
if (!this.loopTickResolver || this.loopTickResolverRoot !== root) {
|
|
3055
|
+
const { homeConfineRoot, homeQwenDir } = resolveHomeLoopResolverRoots();
|
|
3056
|
+
this.loopTickResolver = new LoopTickResolver({
|
|
3057
|
+
projectRoot: root,
|
|
3058
|
+
homeDir: homeConfineRoot,
|
|
3059
|
+
homeQwenDir,
|
|
3060
|
+
// The project `.qwen/loop.md` is repo-controlled, so an untrusted folder
|
|
3061
|
+
// must not read it and feed it to the model (mirrors getProjectHooks()'s
|
|
3062
|
+
// trust gate). The home/global `~/.qwen/loop.md` is user-owned and stays
|
|
3063
|
+
// allowed. Pass a getter, not a snapshot: isTrustedFolder() can flip
|
|
3064
|
+
// mid-session on an IDE workspace-trust update, and the resolver outlives
|
|
3065
|
+
// a single tick — re-read it on every resolve() so a trusted→untrusted
|
|
3066
|
+
// flip stops reading the project file immediately.
|
|
3067
|
+
allowProjectFile: /* @__PURE__ */ __name(() => this.config.isTrustedFolder(), "allowProjectFile")
|
|
3068
|
+
});
|
|
3069
|
+
this.loopTickResolverRoot = root;
|
|
3070
|
+
}
|
|
3071
|
+
return this.loopTickResolver;
|
|
3072
|
+
}
|
|
2695
3073
|
/**
|
|
2696
3074
|
* Executes a single cron-fired prompt: echoes it as a user message with
|
|
2697
3075
|
* `_meta.source='cron'`, streams the model response, and handles tool calls.
|
|
@@ -2722,16 +3100,65 @@ ${this.pendingWorktreeNotice}
|
|
|
2722
3100
|
async () => {
|
|
2723
3101
|
let turnCount = 0;
|
|
2724
3102
|
try {
|
|
3103
|
+
const loopMode = detectLoopSentinel(prompt);
|
|
3104
|
+
const autonomousMode = loopMode ? null : detectAutonomousSentinel(prompt);
|
|
3105
|
+
let loopTick = null;
|
|
3106
|
+
if (loopMode) {
|
|
3107
|
+
const resolver = this.#getLoopTickResolver();
|
|
3108
|
+
const trustedAtResolve = this.config.isTrustedFolder();
|
|
3109
|
+
try {
|
|
3110
|
+
loopTick = await resolver.resolve(loopMode, trustedAtResolve);
|
|
3111
|
+
} catch (resolveErr) {
|
|
3112
|
+
const code = resolveErr.code ?? "unknown";
|
|
3113
|
+
debugLogger5.warn(
|
|
3114
|
+
`loop.md sentinel resolution failed (mode=${loopMode}, code=${code}) \u2014 check .qwen/loop.md permissions/IO`,
|
|
3115
|
+
resolveErr
|
|
3116
|
+
);
|
|
3117
|
+
if (loopMode === "dynamic" && TRANSIENT_FS_CODES.includes(code)) {
|
|
3118
|
+
loopTick = resolver.buildTransientErrorTick(
|
|
3119
|
+
loopMode,
|
|
3120
|
+
trustedAtResolve,
|
|
3121
|
+
code
|
|
3122
|
+
);
|
|
3123
|
+
} else {
|
|
3124
|
+
throw new Error(
|
|
3125
|
+
`loop.md resolution failed (${code}) for ${resolver.absentLocations(
|
|
3126
|
+
trustedAtResolve
|
|
3127
|
+
)}`
|
|
3128
|
+
);
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
} else if (autonomousMode) {
|
|
3132
|
+
loopTick = this.#getLoopTickResolver().resolveAutonomous(autonomousMode);
|
|
3133
|
+
}
|
|
3134
|
+
const modelText = loopTick ? loopTick.modelText : prompt;
|
|
3135
|
+
if (loopTick) {
|
|
3136
|
+
debugLogger5.debug(
|
|
3137
|
+
`loop tick: mode=${loopMode ?? autonomousMode} delivery=${loopTick.full ? "full" : loopTick.transientError ? "transient-error" : loopTick.autonomous ? "autonomous-tick" : loopTick.sourceLabel ? "reminder" : "absent"} source=${loopTick.sourceLabel ?? "none"} autonomous=${loopTick.autonomous ?? false} transient=${loopTick.transientError ?? false}`
|
|
3138
|
+
);
|
|
3139
|
+
}
|
|
3140
|
+
const echoText = !loopTick ? prompt : (
|
|
3141
|
+
// An autonomous tick (a bare-`/loop` sentinel, or a loop.md
|
|
3142
|
+
// sentinel whose file is gone and converged on the preamble).
|
|
3143
|
+
loopTick.autonomous ? "Autonomous loop tick" : loopTick.sourceLabel ? `Loop tick \u2014 tasks from ${loopTick.sourceLabel}` : (
|
|
3144
|
+
// The only remaining tick is a transient read failure
|
|
3145
|
+
// (buildTransientErrorTick): a loop.md exists but couldn't be
|
|
3146
|
+
// read this tick. A genuinely-absent loop.md converges on the
|
|
3147
|
+
// autonomous branch above, so there is no "not present" echo.
|
|
3148
|
+
"Loop tick \u2014 loop.md temporarily unavailable"
|
|
3149
|
+
)
|
|
3150
|
+
);
|
|
2725
3151
|
await this.sendUpdate({
|
|
2726
3152
|
sessionUpdate: "user_message_chunk",
|
|
2727
|
-
content: { type: "text", text:
|
|
3153
|
+
content: { type: "text", text: echoText },
|
|
2728
3154
|
_meta: { source: item.source }
|
|
2729
3155
|
});
|
|
2730
3156
|
const cronReminders = await this.#buildInitialSystemReminders();
|
|
2731
3157
|
let nextMessage = {
|
|
2732
3158
|
role: "user",
|
|
2733
|
-
parts: [...cronReminders, { text:
|
|
3159
|
+
parts: [...cronReminders, { text: modelText }]
|
|
2734
3160
|
};
|
|
3161
|
+
const toolLoopState = createDaemonToolLoopState();
|
|
2735
3162
|
while (nextMessage !== null) {
|
|
2736
3163
|
turnCount++;
|
|
2737
3164
|
if (ac.signal.aborted) return;
|
|
@@ -2754,6 +3181,9 @@ ${this.pendingWorktreeNotice}
|
|
|
2754
3181
|
return;
|
|
2755
3182
|
}
|
|
2756
3183
|
const responseStream = sendResult.responseStream;
|
|
3184
|
+
if (loopTick && turnCount === 1) {
|
|
3185
|
+
this.loopTickResolver?.markDelivered();
|
|
3186
|
+
}
|
|
2757
3187
|
nextMessage = null;
|
|
2758
3188
|
for await (const resp of responseStream) {
|
|
2759
3189
|
if (ac.signal.aborted) return;
|
|
@@ -2791,25 +3221,27 @@ ${this.pendingWorktreeNotice}
|
|
|
2791
3221
|
const toolRun = await this.runToolCalls(
|
|
2792
3222
|
ac.signal,
|
|
2793
3223
|
promptId,
|
|
2794
|
-
functionCalls
|
|
3224
|
+
functionCalls,
|
|
3225
|
+
toolLoopState
|
|
2795
3226
|
);
|
|
2796
3227
|
if (toolRun.stopAfterPermissionCancel) {
|
|
2797
|
-
await this.#
|
|
2798
|
-
toolRun,
|
|
2799
|
-
ac.signal
|
|
2800
|
-
);
|
|
3228
|
+
await this.#preserveStoppedToolRun(toolRun, ac.signal);
|
|
2801
3229
|
return;
|
|
2802
3230
|
}
|
|
2803
3231
|
nextMessage = await this.#buildNextMessageAfterToolRun(
|
|
2804
3232
|
toolRun,
|
|
2805
3233
|
ac.signal
|
|
2806
3234
|
);
|
|
3235
|
+
if (toolRun.loopDetected) {
|
|
3236
|
+
await this.#preserveStoppedToolRun(toolRun, ac.signal);
|
|
3237
|
+
return;
|
|
3238
|
+
}
|
|
2807
3239
|
}
|
|
2808
3240
|
}
|
|
2809
3241
|
} catch (error) {
|
|
2810
3242
|
if (ac.signal.aborted) return;
|
|
2811
3243
|
cronHadError = true;
|
|
2812
|
-
|
|
3244
|
+
debugLogger5.error("Error processing cron prompt:", error);
|
|
2813
3245
|
const msg = error instanceof Error ? error.message : String(error);
|
|
2814
3246
|
await this.messageEmitter.emitAgentMessage(
|
|
2815
3247
|
`[${item.source} error] ${msg}`
|
|
@@ -2893,7 +3325,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2893
3325
|
#enqueueBackgroundNotification(item) {
|
|
2894
3326
|
while (this.notificationQueue.length >= MAX_NOTIFICATION_QUEUE) {
|
|
2895
3327
|
const evicted = this.notificationQueue.shift();
|
|
2896
|
-
|
|
3328
|
+
debugLogger5.warn(
|
|
2897
3329
|
`Notification queue overflow: evicting task=${evicted.taskId} kind=${evicted.kind}`
|
|
2898
3330
|
);
|
|
2899
3331
|
}
|
|
@@ -2950,6 +3382,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2950
3382
|
role: "user",
|
|
2951
3383
|
parts: [...notificationReminders, ...notificationParts]
|
|
2952
3384
|
};
|
|
3385
|
+
const toolLoopState = createDaemonToolLoopState();
|
|
2953
3386
|
while (nextMessage !== null) {
|
|
2954
3387
|
if (ac.signal.aborted) {
|
|
2955
3388
|
await this.#emitBackgroundNotificationEndTurn("cancelled");
|
|
@@ -3026,13 +3459,11 @@ ${this.pendingWorktreeNotice}
|
|
|
3026
3459
|
const toolRun = await this.runToolCalls(
|
|
3027
3460
|
ac.signal,
|
|
3028
3461
|
promptId,
|
|
3029
|
-
functionCalls
|
|
3462
|
+
functionCalls,
|
|
3463
|
+
toolLoopState
|
|
3030
3464
|
);
|
|
3031
3465
|
if (toolRun.stopAfterPermissionCancel) {
|
|
3032
|
-
await this.#
|
|
3033
|
-
toolRun,
|
|
3034
|
-
ac.signal
|
|
3035
|
-
);
|
|
3466
|
+
await this.#preserveStoppedToolRun(toolRun, ac.signal);
|
|
3036
3467
|
await this.#emitBackgroundNotificationEndTurn("end_turn");
|
|
3037
3468
|
return;
|
|
3038
3469
|
}
|
|
@@ -3040,6 +3471,11 @@ ${this.pendingWorktreeNotice}
|
|
|
3040
3471
|
toolRun,
|
|
3041
3472
|
ac.signal
|
|
3042
3473
|
);
|
|
3474
|
+
if (toolRun.loopDetected) {
|
|
3475
|
+
await this.#preserveStoppedToolRun(toolRun, ac.signal);
|
|
3476
|
+
await this.#emitBackgroundNotificationEndTurn("end_turn");
|
|
3477
|
+
return;
|
|
3478
|
+
}
|
|
3043
3479
|
}
|
|
3044
3480
|
}
|
|
3045
3481
|
if (this.messageRewriter) {
|
|
@@ -3051,14 +3487,14 @@ ${this.pendingWorktreeNotice}
|
|
|
3051
3487
|
await this.#emitBackgroundNotificationEndTurn("cancelled");
|
|
3052
3488
|
return;
|
|
3053
3489
|
}
|
|
3054
|
-
|
|
3490
|
+
debugLogger5.error("Error processing background notification:", error);
|
|
3055
3491
|
const msg = error instanceof Error ? error.message : String(error);
|
|
3056
3492
|
try {
|
|
3057
3493
|
await this.messageEmitter.emitAgentMessage(
|
|
3058
3494
|
`[notification error] ${msg}`
|
|
3059
3495
|
);
|
|
3060
3496
|
} catch (emitError) {
|
|
3061
|
-
|
|
3497
|
+
debugLogger5.error(
|
|
3062
3498
|
"Failed to emit background notification error:",
|
|
3063
3499
|
emitError
|
|
3064
3500
|
);
|
|
@@ -3118,7 +3554,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3118
3554
|
source: "background_notification"
|
|
3119
3555
|
});
|
|
3120
3556
|
} catch (error) {
|
|
3121
|
-
|
|
3557
|
+
debugLogger5.debug(
|
|
3122
3558
|
`Background notification end-turn extNotification dropped: ${this.#formatError(error)}`
|
|
3123
3559
|
);
|
|
3124
3560
|
}
|
|
@@ -3142,7 +3578,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3142
3578
|
};
|
|
3143
3579
|
await this.sendUpdate(update);
|
|
3144
3580
|
} catch (error) {
|
|
3145
|
-
|
|
3581
|
+
debugLogger5.error("Error sending available commands update:", error);
|
|
3146
3582
|
}
|
|
3147
3583
|
}
|
|
3148
3584
|
/**
|
|
@@ -3177,7 +3613,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3177
3613
|
sessionId: this.sessionId,
|
|
3178
3614
|
currentModeId: params.modeId
|
|
3179
3615
|
}).catch((error) => {
|
|
3180
|
-
|
|
3616
|
+
debugLogger5.debug("mode-update extNotification failed", error);
|
|
3181
3617
|
});
|
|
3182
3618
|
}
|
|
3183
3619
|
/**
|
|
@@ -3211,7 +3647,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3211
3647
|
sessionId: this.sessionId,
|
|
3212
3648
|
currentModelId: effectiveModelId
|
|
3213
3649
|
}).catch((error) => {
|
|
3214
|
-
|
|
3650
|
+
debugLogger5.debug("model-update extNotification failed", error);
|
|
3215
3651
|
});
|
|
3216
3652
|
if (options.persistDefault ?? true) {
|
|
3217
3653
|
const persistScope = getPersistScopeForModelSelection(this.settings);
|
|
@@ -3264,7 +3700,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3264
3700
|
currentModeId: newModeId,
|
|
3265
3701
|
legacyFrameSent: true
|
|
3266
3702
|
}).catch((error) => {
|
|
3267
|
-
|
|
3703
|
+
debugLogger5.debug("mode-update extNotification failed", error);
|
|
3268
3704
|
});
|
|
3269
3705
|
}
|
|
3270
3706
|
/**
|
|
@@ -3277,8 +3713,58 @@ ${this.pendingWorktreeNotice}
|
|
|
3277
3713
|
* sequential batch to preserve the implicit ordering the model may rely
|
|
3278
3714
|
* on. Response-part ordering matches the original `functionCalls` order.
|
|
3279
3715
|
*/
|
|
3280
|
-
async runToolCalls(abortSignal, promptId, functionCalls) {
|
|
3716
|
+
async runToolCalls(abortSignal, promptId, functionCalls, toolLoopState) {
|
|
3281
3717
|
const dedupedFunctionCalls = dedupeToolCallsById(functionCalls);
|
|
3718
|
+
let skippedToolCallCounter = 0;
|
|
3719
|
+
const recordSkippedToolCall = /* @__PURE__ */ __name(async (fc, message = PERMISSION_CANCEL_SKIP_MESSAGE, emitStart = true) => {
|
|
3720
|
+
const toolName = fc.name ?? "unknown_tool";
|
|
3721
|
+
const callId = fc.id ?? `${toolName}-skip-${++skippedToolCallCounter}`;
|
|
3722
|
+
const part = {
|
|
3723
|
+
functionResponse: {
|
|
3724
|
+
id: callId,
|
|
3725
|
+
name: toolName,
|
|
3726
|
+
response: { error: message }
|
|
3727
|
+
}
|
|
3728
|
+
};
|
|
3729
|
+
const error = new Error(message);
|
|
3730
|
+
try {
|
|
3731
|
+
this.config.getChatRecordingService()?.recordToolResult([part], {
|
|
3732
|
+
callId,
|
|
3733
|
+
status: "error",
|
|
3734
|
+
resultDisplay: void 0,
|
|
3735
|
+
error,
|
|
3736
|
+
errorType: void 0
|
|
3737
|
+
});
|
|
3738
|
+
if (emitStart) {
|
|
3739
|
+
await this.toolCallEmitter.emitStart({
|
|
3740
|
+
callId,
|
|
3741
|
+
toolName,
|
|
3742
|
+
args: fc.args ?? {},
|
|
3743
|
+
status: "pending"
|
|
3744
|
+
});
|
|
3745
|
+
}
|
|
3746
|
+
await this.toolCallEmitter.emitError(callId, toolName, error);
|
|
3747
|
+
} catch (recordError) {
|
|
3748
|
+
debugLogger5.error("Failed to record skipped tool call:", recordError);
|
|
3749
|
+
}
|
|
3750
|
+
return part;
|
|
3751
|
+
}, "recordSkippedToolCall");
|
|
3752
|
+
if (recordDaemonToolCalls(
|
|
3753
|
+
this.config,
|
|
3754
|
+
promptId,
|
|
3755
|
+
toolLoopState,
|
|
3756
|
+
dedupedFunctionCalls.length
|
|
3757
|
+
)) {
|
|
3758
|
+
return {
|
|
3759
|
+
parts: await Promise.all(
|
|
3760
|
+
dedupedFunctionCalls.map(
|
|
3761
|
+
(fc) => recordSkippedToolCall(fc, LOOP_DETECTED_SKIP_MESSAGE, false)
|
|
3762
|
+
)
|
|
3763
|
+
),
|
|
3764
|
+
stopAfterPermissionCancel: false,
|
|
3765
|
+
loopDetected: true
|
|
3766
|
+
};
|
|
3767
|
+
}
|
|
3282
3768
|
const batches = [];
|
|
3283
3769
|
const handledProviderToolCallIds = new Set(
|
|
3284
3770
|
this.#getCurrentChat().getHistoryFunctionResponseIds()
|
|
@@ -3291,7 +3777,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3291
3777
|
);
|
|
3292
3778
|
if (repeatedDuplicateCall) {
|
|
3293
3779
|
const providerCallId = getProviderToolCallId(repeatedDuplicateCall) ?? repeatedDuplicateCall.id;
|
|
3294
|
-
|
|
3780
|
+
debugLogger5.debug(
|
|
3295
3781
|
`[Session.runToolCalls] Dropping batch after repeated duplicate provider tool-call id: ${providerCallId} (tool: ${repeatedDuplicateCall.name ?? "unknown_tool"})`
|
|
3296
3782
|
);
|
|
3297
3783
|
return {
|
|
@@ -3307,7 +3793,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3307
3793
|
this.duplicateProviderToolCallResponseIds
|
|
3308
3794
|
);
|
|
3309
3795
|
const response = createDuplicateProviderToolCallResponse(request);
|
|
3310
|
-
|
|
3796
|
+
debugLogger5.debug(
|
|
3311
3797
|
`[Session.runToolCalls] Suppressing duplicate provider tool-call id: ${providerCallId} (tool: ${request.name})`
|
|
3312
3798
|
);
|
|
3313
3799
|
batches.push({ kind: "duplicate", request, response });
|
|
@@ -3380,53 +3866,80 @@ ${this.pendingWorktreeNotice}
|
|
|
3380
3866
|
batches.push({ kind: "execute", concurrent: isAgent, calls: [fc] });
|
|
3381
3867
|
}
|
|
3382
3868
|
}
|
|
3383
|
-
|
|
3384
|
-
const recordSkippedToolCall = /* @__PURE__ */ __name(async (fc) => {
|
|
3385
|
-
const toolName = fc.name ?? "unknown_tool";
|
|
3386
|
-
const callId = fc.id ?? `${toolName}-skip-${++skippedToolCallCounter}`;
|
|
3387
|
-
const part = {
|
|
3388
|
-
functionResponse: {
|
|
3389
|
-
id: callId,
|
|
3390
|
-
name: toolName,
|
|
3391
|
-
response: { error: PERMISSION_CANCEL_SKIP_MESSAGE }
|
|
3392
|
-
}
|
|
3393
|
-
};
|
|
3394
|
-
const error = new Error(PERMISSION_CANCEL_SKIP_MESSAGE);
|
|
3395
|
-
try {
|
|
3396
|
-
this.config.getChatRecordingService()?.recordToolResult([part], {
|
|
3397
|
-
callId,
|
|
3398
|
-
status: "error",
|
|
3399
|
-
resultDisplay: void 0,
|
|
3400
|
-
error,
|
|
3401
|
-
errorType: void 0
|
|
3402
|
-
});
|
|
3403
|
-
await this.toolCallEmitter.emitStart({
|
|
3404
|
-
callId,
|
|
3405
|
-
toolName,
|
|
3406
|
-
args: fc.args ?? {},
|
|
3407
|
-
status: "pending"
|
|
3408
|
-
});
|
|
3409
|
-
await this.toolCallEmitter.emitError(callId, toolName, error);
|
|
3410
|
-
} catch (recordError) {
|
|
3411
|
-
debugLogger4.error("Failed to record skipped tool call:", recordError);
|
|
3412
|
-
}
|
|
3413
|
-
return part;
|
|
3414
|
-
}, "recordSkippedToolCall");
|
|
3415
|
-
const appendSkippedAfter = /* @__PURE__ */ __name(async (parts2, fc) => {
|
|
3869
|
+
const appendSkippedAfter = /* @__PURE__ */ __name(async (parts2, fc, message = PERMISSION_CANCEL_SKIP_MESSAGE) => {
|
|
3416
3870
|
const startIndex = dedupedFunctionCalls.indexOf(fc) + 1;
|
|
3417
3871
|
for (const remainingCall of dedupedFunctionCalls.slice(startIndex)) {
|
|
3418
|
-
parts2.push(await recordSkippedToolCall(remainingCall));
|
|
3872
|
+
parts2.push(await recordSkippedToolCall(remainingCall, message));
|
|
3419
3873
|
}
|
|
3420
3874
|
}, "appendSkippedAfter");
|
|
3421
|
-
const runBounded = /* @__PURE__ */ __name(async (calls, runAbortSignal, onStopAfterPermissionCancel, shouldSkipUnstarted) => {
|
|
3422
|
-
const
|
|
3875
|
+
const runBounded = /* @__PURE__ */ __name(async (calls, runAbortSignal, onStopAfterPermissionCancel, onStopAfterLoopDetected, shouldSkipUnstarted) => {
|
|
3876
|
+
const configuredMaxConcurrency = parsePositiveIntegerEnv(
|
|
3423
3877
|
process.env["QWEN_CODE_MAX_TOOL_CONCURRENCY"],
|
|
3424
3878
|
10
|
|
3425
3879
|
);
|
|
3880
|
+
const maxConcurrency = toolLoopState ? Math.min(
|
|
3881
|
+
configuredMaxConcurrency,
|
|
3882
|
+
DAEMON_INVALID_TOOL_PARAMS_THRESHOLD
|
|
3883
|
+
) : configuredMaxConcurrency;
|
|
3426
3884
|
const results = new Array(calls.length);
|
|
3427
3885
|
const executing = /* @__PURE__ */ new Set();
|
|
3428
|
-
|
|
3886
|
+
const fillLoopSkippedFrom = /* @__PURE__ */ __name(async (startIndex2) => {
|
|
3887
|
+
for (let i = startIndex2; i < calls.length; i++) {
|
|
3888
|
+
if (results[i]) continue;
|
|
3889
|
+
results[i] = {
|
|
3890
|
+
parts: [
|
|
3891
|
+
await recordSkippedToolCall(calls[i], LOOP_DETECTED_SKIP_MESSAGE)
|
|
3892
|
+
],
|
|
3893
|
+
stopAfterPermissionCancel: false,
|
|
3894
|
+
loopDetected: true
|
|
3895
|
+
};
|
|
3896
|
+
}
|
|
3897
|
+
}, "fillLoopSkippedFrom");
|
|
3898
|
+
const fillPermissionSkippedFrom = /* @__PURE__ */ __name(async (startIndex2) => {
|
|
3899
|
+
for (let i = startIndex2; i < calls.length; i++) {
|
|
3900
|
+
if (results[i]) continue;
|
|
3901
|
+
results[i] = {
|
|
3902
|
+
parts: [await recordSkippedToolCall(calls[i])],
|
|
3903
|
+
stopAfterPermissionCancel: false
|
|
3904
|
+
};
|
|
3905
|
+
}
|
|
3906
|
+
}, "fillPermissionSkippedFrom");
|
|
3907
|
+
let startIndex = 0;
|
|
3908
|
+
if (toolLoopState && calls.length > DAEMON_INVALID_TOOL_PARAMS_THRESHOLD) {
|
|
3909
|
+
startIndex = DAEMON_INVALID_TOOL_PARAMS_THRESHOLD;
|
|
3910
|
+
for (let i = 0; i < startIndex; i++) {
|
|
3911
|
+
if (runAbortSignal.aborted && shouldSkipUnstarted?.()) {
|
|
3912
|
+
results[i] = {
|
|
3913
|
+
parts: [await recordSkippedToolCall(calls[i])],
|
|
3914
|
+
stopAfterPermissionCancel: false
|
|
3915
|
+
};
|
|
3916
|
+
continue;
|
|
3917
|
+
}
|
|
3918
|
+
const r = await this.runTool(
|
|
3919
|
+
runAbortSignal,
|
|
3920
|
+
promptId,
|
|
3921
|
+
calls[i],
|
|
3922
|
+
onStopAfterPermissionCancel,
|
|
3923
|
+
toolLoopState,
|
|
3924
|
+
recordSkippedToolCall
|
|
3925
|
+
);
|
|
3926
|
+
results[i] = r;
|
|
3927
|
+
if (r.loopDetected) {
|
|
3928
|
+
await fillLoopSkippedFrom(i + 1);
|
|
3929
|
+
return results;
|
|
3930
|
+
}
|
|
3931
|
+
if (r.stopAfterPermissionCancel) {
|
|
3932
|
+
await fillPermissionSkippedFrom(i + 1);
|
|
3933
|
+
return results;
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
}
|
|
3937
|
+
for (let i = startIndex; i < calls.length; i++) {
|
|
3429
3938
|
const idx = i;
|
|
3939
|
+
if (toolLoopState?.loopDetected) {
|
|
3940
|
+
await fillLoopSkippedFrom(idx);
|
|
3941
|
+
return results;
|
|
3942
|
+
}
|
|
3430
3943
|
if (runAbortSignal.aborted && shouldSkipUnstarted?.()) {
|
|
3431
3944
|
results[idx] = {
|
|
3432
3945
|
parts: [await recordSkippedToolCall(calls[idx])],
|
|
@@ -3438,7 +3951,9 @@ ${this.pendingWorktreeNotice}
|
|
|
3438
3951
|
runAbortSignal,
|
|
3439
3952
|
promptId,
|
|
3440
3953
|
calls[idx],
|
|
3441
|
-
onStopAfterPermissionCancel
|
|
3954
|
+
onStopAfterPermissionCancel,
|
|
3955
|
+
toolLoopState,
|
|
3956
|
+
recordSkippedToolCall
|
|
3442
3957
|
).then((r) => {
|
|
3443
3958
|
results[idx] = r;
|
|
3444
3959
|
}).finally(() => {
|
|
@@ -3447,6 +3962,23 @@ ${this.pendingWorktreeNotice}
|
|
|
3447
3962
|
executing.add(p);
|
|
3448
3963
|
if (executing.size >= maxConcurrency) {
|
|
3449
3964
|
await Promise.race(executing);
|
|
3965
|
+
if (results.some((result) => result?.loopDetected)) {
|
|
3966
|
+
onStopAfterLoopDetected?.();
|
|
3967
|
+
await Promise.all(executing);
|
|
3968
|
+
await fillLoopSkippedFrom(idx + 1);
|
|
3969
|
+
return results;
|
|
3970
|
+
}
|
|
3971
|
+
const invalidToolErrorNearThreshold = toolLoopState && [...toolLoopState.invalidToolParamErrors.values()].some(
|
|
3972
|
+
(count) => count >= DAEMON_INVALID_TOOL_PARAMS_THRESHOLD - 1
|
|
3973
|
+
);
|
|
3974
|
+
if (invalidToolErrorNearThreshold && executing.size > 0) {
|
|
3975
|
+
await Promise.all(executing);
|
|
3976
|
+
if (results.some((result) => result?.loopDetected)) {
|
|
3977
|
+
onStopAfterLoopDetected?.();
|
|
3978
|
+
await fillLoopSkippedFrom(idx + 1);
|
|
3979
|
+
return results;
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3450
3982
|
}
|
|
3451
3983
|
}
|
|
3452
3984
|
await Promise.all(executing);
|
|
@@ -3482,15 +4014,30 @@ ${this.pendingWorktreeNotice}
|
|
|
3482
4014
|
batch.calls,
|
|
3483
4015
|
batchAbortController.signal,
|
|
3484
4016
|
stopBatchAfterPermissionCancel,
|
|
4017
|
+
() => batchAbortController.abort("loop_detected"),
|
|
3485
4018
|
() => batchStopAfterPermissionCancel
|
|
3486
4019
|
);
|
|
3487
4020
|
} finally {
|
|
3488
4021
|
abortSignal.removeEventListener("abort", propagateAbort);
|
|
3489
4022
|
}
|
|
3490
4023
|
let shouldStop = false;
|
|
4024
|
+
let shouldStopForLoop = false;
|
|
3491
4025
|
for (const r of results) {
|
|
3492
4026
|
parts.push(...r.parts);
|
|
3493
4027
|
shouldStop ||= r.stopAfterPermissionCancel;
|
|
4028
|
+
shouldStopForLoop ||= r.loopDetected === true;
|
|
4029
|
+
}
|
|
4030
|
+
if (shouldStopForLoop) {
|
|
4031
|
+
await appendSkippedAfter(
|
|
4032
|
+
parts,
|
|
4033
|
+
batch.calls[batch.calls.length - 1],
|
|
4034
|
+
LOOP_DETECTED_SKIP_MESSAGE
|
|
4035
|
+
);
|
|
4036
|
+
return {
|
|
4037
|
+
parts,
|
|
4038
|
+
stopAfterPermissionCancel: false,
|
|
4039
|
+
loopDetected: true
|
|
4040
|
+
};
|
|
3494
4041
|
}
|
|
3495
4042
|
if (shouldStop) {
|
|
3496
4043
|
await appendSkippedAfter(parts, batch.calls[batch.calls.length - 1]);
|
|
@@ -3502,8 +4049,23 @@ ${this.pendingWorktreeNotice}
|
|
|
3502
4049
|
}
|
|
3503
4050
|
} else {
|
|
3504
4051
|
for (const fc of batch.calls) {
|
|
3505
|
-
const r = await this.runTool(
|
|
4052
|
+
const r = await this.runTool(
|
|
4053
|
+
abortSignal,
|
|
4054
|
+
promptId,
|
|
4055
|
+
fc,
|
|
4056
|
+
void 0,
|
|
4057
|
+
toolLoopState,
|
|
4058
|
+
recordSkippedToolCall
|
|
4059
|
+
);
|
|
3506
4060
|
parts.push(...r.parts);
|
|
4061
|
+
if (r.loopDetected) {
|
|
4062
|
+
await appendSkippedAfter(parts, fc, LOOP_DETECTED_SKIP_MESSAGE);
|
|
4063
|
+
return {
|
|
4064
|
+
parts,
|
|
4065
|
+
stopAfterPermissionCancel: false,
|
|
4066
|
+
loopDetected: true
|
|
4067
|
+
};
|
|
4068
|
+
}
|
|
3507
4069
|
if (r.stopAfterPermissionCancel) {
|
|
3508
4070
|
await appendSkippedAfter(parts, fc);
|
|
3509
4071
|
return {
|
|
@@ -3551,9 +4113,24 @@ ${this.pendingWorktreeNotice}
|
|
|
3551
4113
|
}
|
|
3552
4114
|
return reminders;
|
|
3553
4115
|
}
|
|
3554
|
-
async runTool(abortSignal, promptId, fc, onStopAfterPermissionCancel) {
|
|
4116
|
+
async runTool(abortSignal, promptId, fc, onStopAfterPermissionCancel, toolLoopState, recordSkippedToolCall) {
|
|
3555
4117
|
const callId = fc.id ?? `${fc.name}-${Date.now()}`;
|
|
3556
4118
|
let args = fc.args ?? {};
|
|
4119
|
+
if (toolLoopState?.loopDetected) {
|
|
4120
|
+
return {
|
|
4121
|
+
parts: [
|
|
4122
|
+
recordSkippedToolCall ? await recordSkippedToolCall(fc, LOOP_DETECTED_SKIP_MESSAGE, false) : {
|
|
4123
|
+
functionResponse: {
|
|
4124
|
+
id: callId,
|
|
4125
|
+
name: fc.name ?? "unknown_tool",
|
|
4126
|
+
response: { error: LOOP_DETECTED_SKIP_MESSAGE }
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
],
|
|
4130
|
+
stopAfterPermissionCancel: false,
|
|
4131
|
+
loopDetected: true
|
|
4132
|
+
};
|
|
4133
|
+
}
|
|
3557
4134
|
const startTime = Date.now();
|
|
3558
4135
|
let spanError;
|
|
3559
4136
|
let activeToolAbortSignal = abortSignal;
|
|
@@ -3606,20 +4183,32 @@ ${this.pendingWorktreeNotice}
|
|
|
3606
4183
|
error,
|
|
3607
4184
|
errorType: void 0
|
|
3608
4185
|
});
|
|
4186
|
+
const loopDetected = opts?.recordInvalidToolParams === true && !activeToolAbortSignal.aborted && !opts?.stopAfterPermissionCancel && recordDaemonInvalidToolParams(
|
|
4187
|
+
this.config,
|
|
4188
|
+
promptId,
|
|
4189
|
+
toolLoopState,
|
|
4190
|
+
toolName2,
|
|
4191
|
+
error
|
|
4192
|
+
);
|
|
3609
4193
|
return {
|
|
3610
4194
|
parts: errorParts,
|
|
3611
|
-
stopAfterPermissionCancel: opts?.stopAfterPermissionCancel ?? false
|
|
4195
|
+
stopAfterPermissionCancel: opts?.stopAfterPermissionCancel ?? false,
|
|
4196
|
+
loopDetected
|
|
3612
4197
|
};
|
|
3613
4198
|
}, "earlyErrorResponse");
|
|
3614
4199
|
if (!fc.name) {
|
|
3615
|
-
return earlyErrorResponse(new Error("Missing function name")
|
|
4200
|
+
return earlyErrorResponse(new Error("Missing function name"), void 0, {
|
|
4201
|
+
recordInvalidToolParams: true
|
|
4202
|
+
});
|
|
3616
4203
|
}
|
|
3617
4204
|
const toolName = fc.name;
|
|
3618
4205
|
const toolRegistry = this.config.getToolRegistry();
|
|
3619
4206
|
const tool = toolRegistry.getTool(toolName);
|
|
3620
4207
|
if (!tool) {
|
|
3621
4208
|
return earlyErrorResponse(
|
|
3622
|
-
new Error(`Tool "${toolName}" not found in registry.`)
|
|
4209
|
+
new Error(`Tool "${toolName}" not found in registry.`),
|
|
4210
|
+
toolName,
|
|
4211
|
+
{ recordInvalidToolParams: true }
|
|
3623
4212
|
);
|
|
3624
4213
|
}
|
|
3625
4214
|
const toolSpan = startToolSpan(toolName, {
|
|
@@ -3663,8 +4252,10 @@ ${this.pendingWorktreeNotice}
|
|
|
3663
4252
|
}
|
|
3664
4253
|
const toolUseId = generateToolUseId();
|
|
3665
4254
|
const approvalMode = this.config.getApprovalMode();
|
|
4255
|
+
let toolBuildSucceeded = false;
|
|
3666
4256
|
try {
|
|
3667
4257
|
const invocation = tool.build(args);
|
|
4258
|
+
toolBuildSucceeded = true;
|
|
3668
4259
|
const taskEventEmitter = invocation.eventEmitter;
|
|
3669
4260
|
if (isAgentTool && taskEventEmitter) {
|
|
3670
4261
|
const parentToolCallId = callId;
|
|
@@ -3701,13 +4292,13 @@ ${this.pendingWorktreeNotice}
|
|
|
3701
4292
|
toolName
|
|
3702
4293
|
);
|
|
3703
4294
|
}
|
|
3704
|
-
const forceAutoReviewForAllow = approvalMode === "auto" /* AUTO */ && shouldForceAutoModeReviewForAllow(pmCtx, this.config.getCwd());
|
|
4295
|
+
const forceAutoReviewForAllow = approvalMode === "auto" /* AUTO */ && (shouldForceAutoModeReviewForAllow(pmCtx, this.config.getCwd()) || shouldClassifyAllShellForAutoMode(toolName, this.config));
|
|
3705
4296
|
const confirmationPermission = getEffectivePermissionForConfirmation(
|
|
3706
4297
|
finalPermission,
|
|
3707
4298
|
forceAutoReviewForAllow
|
|
3708
4299
|
);
|
|
3709
4300
|
if (finalPermission === "allow" && forceAutoReviewForAllow) {
|
|
3710
|
-
|
|
4301
|
+
debugLogger5.info(
|
|
3711
4302
|
`Auto mode: L4 allow overridden by protected-write guard for ${toolName}`
|
|
3712
4303
|
);
|
|
3713
4304
|
}
|
|
@@ -3750,7 +4341,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3750
4341
|
autoModeAllowed = true;
|
|
3751
4342
|
break;
|
|
3752
4343
|
case "blocked":
|
|
3753
|
-
|
|
4344
|
+
debugLogger5.warn(
|
|
3754
4345
|
`Auto mode blocked (${outcome.reason}): tool=${toolName}, ` + formatDenialStateLog(denialState)
|
|
3755
4346
|
);
|
|
3756
4347
|
return earlyErrorResponse(
|
|
@@ -3762,7 +4353,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3762
4353
|
outcome.reason
|
|
3763
4354
|
);
|
|
3764
4355
|
if (wasAutoModeDenialFallback) {
|
|
3765
|
-
|
|
4356
|
+
debugLogger5.warn(
|
|
3766
4357
|
`Auto mode fallback to manual approval (${outcome.reason}): ` + formatDenialStateLog(denialState)
|
|
3767
4358
|
);
|
|
3768
4359
|
}
|
|
@@ -3780,12 +4371,12 @@ ${this.pendingWorktreeNotice}
|
|
|
3780
4371
|
const before = this.config.getAutoModeDenialState();
|
|
3781
4372
|
const after = recordFallbackApprove(before);
|
|
3782
4373
|
if (after === before) {
|
|
3783
|
-
|
|
4374
|
+
debugLogger5.warn(
|
|
3784
4375
|
`Auto mode denial counters already clear after fallback approval: ` + formatDenialStateLog(before)
|
|
3785
4376
|
);
|
|
3786
4377
|
return;
|
|
3787
4378
|
}
|
|
3788
|
-
|
|
4379
|
+
debugLogger5.warn(
|
|
3789
4380
|
`Auto mode denial counters reset after fallback approval: ${formatDenialStateLog(before)} -> ${formatDenialStateLog(after)}`
|
|
3790
4381
|
);
|
|
3791
4382
|
this.config.setAutoModeDenialState(after);
|
|
@@ -3891,7 +4482,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3891
4482
|
);
|
|
3892
4483
|
outcome = output.outcome.outcome === "cancelled" ? "cancel" /* Cancel */ : external_exports.nativeEnum(ToolConfirmationOutcome).parse(output.outcome.optionId);
|
|
3893
4484
|
} catch (error) {
|
|
3894
|
-
|
|
4485
|
+
debugLogger5.error(
|
|
3895
4486
|
`Permission request failed for tool ${toolName}:`,
|
|
3896
4487
|
error
|
|
3897
4488
|
);
|
|
@@ -3900,7 +4491,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3900
4491
|
"cancel" /* Cancel */
|
|
3901
4492
|
);
|
|
3902
4493
|
} catch (confirmError) {
|
|
3903
|
-
|
|
4494
|
+
debugLogger5.error(
|
|
3904
4495
|
`Failed to cancel tool ${toolName} after permission request failure:`,
|
|
3905
4496
|
confirmError
|
|
3906
4497
|
);
|
|
@@ -3925,7 +4516,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3925
4516
|
if (outcome !== "cancel" /* Cancel */) {
|
|
3926
4517
|
throw error;
|
|
3927
4518
|
}
|
|
3928
|
-
|
|
4519
|
+
debugLogger5.error(
|
|
3929
4520
|
`Failed to confirm cancellation for tool ${toolName}:`,
|
|
3930
4521
|
error
|
|
3931
4522
|
);
|
|
@@ -3990,12 +4581,12 @@ ${this.pendingWorktreeNotice}
|
|
|
3990
4581
|
if (!preHookResult.shouldProceed) {
|
|
3991
4582
|
const blockReason = preHookResult.blockReason || "Blocked by PreToolUse hook";
|
|
3992
4583
|
await this.messageEmitter.emitAgentMessage(
|
|
3993
|
-
`\
|
|
4584
|
+
`\u2717 **PreToolUse blocked**: ${toolName} - ${blockReason}`
|
|
3994
4585
|
);
|
|
3995
4586
|
return earlyErrorResponse(new Error(blockReason), toolName);
|
|
3996
4587
|
}
|
|
3997
4588
|
if (preHookResult.additionalContext) {
|
|
3998
|
-
|
|
4589
|
+
debugLogger5.debug(
|
|
3999
4590
|
`PreToolUse hook additional context for ${toolName}: ${preHookResult.additionalContext}`
|
|
4000
4591
|
);
|
|
4001
4592
|
}
|
|
@@ -4058,7 +4649,7 @@ ${this.pendingWorktreeNotice}
|
|
|
4058
4649
|
);
|
|
4059
4650
|
if (postHookResult.shouldStop) {
|
|
4060
4651
|
const stopMessage = postHookResult.stopReason || "Execution stopped by PostToolUse hook";
|
|
4061
|
-
|
|
4652
|
+
debugLogger5.info(
|
|
4062
4653
|
`PostToolUse hook requested stop for ${toolName}: ${stopMessage}`
|
|
4063
4654
|
);
|
|
4064
4655
|
return earlyErrorResponse(new Error(stopMessage), toolName);
|
|
@@ -4081,7 +4672,7 @@ ${this.pendingWorktreeNotice}
|
|
|
4081
4672
|
callId
|
|
4082
4673
|
);
|
|
4083
4674
|
if (failureHookResult.additionalContext) {
|
|
4084
|
-
|
|
4675
|
+
debugLogger5.debug(
|
|
4085
4676
|
`PostToolUseFailure hook additional context for ${toolName}: ${failureHookResult.additionalContext}`
|
|
4086
4677
|
);
|
|
4087
4678
|
}
|
|
@@ -4157,7 +4748,7 @@ ${this.pendingWorktreeNotice}
|
|
|
4157
4748
|
callId
|
|
4158
4749
|
);
|
|
4159
4750
|
if (failureHookResult.additionalContext) {
|
|
4160
|
-
|
|
4751
|
+
debugLogger5.debug(
|
|
4161
4752
|
`PostToolUseFailure hook additional context for ${toolName}: ${failureHookResult.additionalContext}`
|
|
4162
4753
|
);
|
|
4163
4754
|
}
|
|
@@ -4181,9 +4772,17 @@ ${this.pendingWorktreeNotice}
|
|
|
4181
4772
|
error,
|
|
4182
4773
|
errorType: void 0
|
|
4183
4774
|
});
|
|
4775
|
+
const loopDetected = !activeToolAbortSignal.aborted && !toolBuildSucceeded && recordDaemonInvalidToolParams(
|
|
4776
|
+
this.config,
|
|
4777
|
+
promptId,
|
|
4778
|
+
toolLoopState,
|
|
4779
|
+
toolName,
|
|
4780
|
+
error
|
|
4781
|
+
);
|
|
4184
4782
|
return {
|
|
4185
4783
|
parts: errorResponse(error),
|
|
4186
|
-
stopAfterPermissionCancel: nestedPermissionCancelled
|
|
4784
|
+
stopAfterPermissionCancel: nestedPermissionCancelled,
|
|
4785
|
+
loopDetected
|
|
4187
4786
|
};
|
|
4188
4787
|
}
|
|
4189
4788
|
});
|
|
@@ -4292,11 +4891,30 @@ ${this.pendingWorktreeNotice}
|
|
|
4292
4891
|
async #resolvePrompt(message, abortSignal) {
|
|
4293
4892
|
const FILE_URI_SCHEME = "file://";
|
|
4294
4893
|
const embeddedContext = [];
|
|
4894
|
+
const extensionMentions = /* @__PURE__ */ new Map();
|
|
4895
|
+
const preserveUnsupportedImageForBridge = shouldRunVisionBridge(
|
|
4896
|
+
this.config
|
|
4897
|
+
);
|
|
4295
4898
|
const parts = message.map((part) => {
|
|
4296
4899
|
switch (part.type) {
|
|
4297
4900
|
case "text":
|
|
4901
|
+
collectExtensionMentionRefs(part.text, extensionMentions);
|
|
4298
4902
|
return { text: part.text };
|
|
4299
4903
|
case "image":
|
|
4904
|
+
if (preserveUnsupportedImageForBridge) {
|
|
4905
|
+
return {
|
|
4906
|
+
inlineData: {
|
|
4907
|
+
mimeType: part.mimeType,
|
|
4908
|
+
data: part.data
|
|
4909
|
+
}
|
|
4910
|
+
};
|
|
4911
|
+
}
|
|
4912
|
+
return clampInlineMediaPart({
|
|
4913
|
+
inlineData: {
|
|
4914
|
+
mimeType: part.mimeType,
|
|
4915
|
+
data: part.data
|
|
4916
|
+
}
|
|
4917
|
+
});
|
|
4300
4918
|
case "audio":
|
|
4301
4919
|
return clampInlineMediaPart({
|
|
4302
4920
|
inlineData: {
|
|
@@ -4328,8 +4946,18 @@ ${this.pendingWorktreeNotice}
|
|
|
4328
4946
|
}
|
|
4329
4947
|
});
|
|
4330
4948
|
const atPathCommandParts = parts.filter((part) => "fileData" in part);
|
|
4949
|
+
const extensionParts = await this.#resolveExtensionMentionParts(
|
|
4950
|
+
extensionMentions,
|
|
4951
|
+
abortSignal
|
|
4952
|
+
);
|
|
4953
|
+
if (atPathCommandParts.length === 0 && embeddedContext.length === 0 && extensionParts.length === 0) {
|
|
4954
|
+
return this.#applyVisionBridgeIfNeeded(parts, abortSignal);
|
|
4955
|
+
}
|
|
4331
4956
|
if (atPathCommandParts.length === 0 && embeddedContext.length === 0) {
|
|
4332
|
-
return
|
|
4957
|
+
return this.#applyVisionBridgeIfNeeded(
|
|
4958
|
+
[...parts, ...extensionParts],
|
|
4959
|
+
abortSignal
|
|
4960
|
+
);
|
|
4333
4961
|
}
|
|
4334
4962
|
const pathSpecsToRead = atPathCommandParts.map(
|
|
4335
4963
|
(part) => part.fileData.fileUri
|
|
@@ -4351,19 +4979,24 @@ ${this.pendingWorktreeNotice}
|
|
|
4351
4979
|
if (pathSpecsToRead.length > 0) {
|
|
4352
4980
|
const readResult = await readManyFiles(this.config, {
|
|
4353
4981
|
paths: pathSpecsToRead,
|
|
4354
|
-
signal: abortSignal
|
|
4982
|
+
signal: abortSignal,
|
|
4983
|
+
...preserveUnsupportedImageForBridge ? { preserveUnsupportedImageForBridge } : {}
|
|
4355
4984
|
});
|
|
4356
4985
|
const contentParts = Array.isArray(readResult.contentParts) ? readResult.contentParts : [readResult.contentParts];
|
|
4357
4986
|
processedQueryParts.push({ text: initialQueryText });
|
|
4987
|
+
processedQueryParts.push(...extensionParts);
|
|
4358
4988
|
for (const part of contentParts) {
|
|
4359
4989
|
if (typeof part === "string") {
|
|
4360
4990
|
processedQueryParts.push({ text: part });
|
|
4991
|
+
} else if (preserveUnsupportedImageForBridge && hasImageParts([part])) {
|
|
4992
|
+
processedQueryParts.push(part);
|
|
4361
4993
|
} else {
|
|
4362
4994
|
processedQueryParts.push(clampInlineMediaPart(part));
|
|
4363
4995
|
}
|
|
4364
4996
|
}
|
|
4365
4997
|
} else {
|
|
4366
4998
|
processedQueryParts.push({ text: initialQueryText.trim() });
|
|
4999
|
+
processedQueryParts.push(...extensionParts);
|
|
4367
5000
|
}
|
|
4368
5001
|
for (const contextPart of embeddedContext) {
|
|
4369
5002
|
if ("text" in contextPart && contextPart.text) {
|
|
@@ -4373,21 +5006,104 @@ ${contextPart.text}`
|
|
|
4373
5006
|
});
|
|
4374
5007
|
}
|
|
4375
5008
|
if ("blob" in contextPart && contextPart.blob) {
|
|
5009
|
+
const inlinePart = {
|
|
5010
|
+
inlineData: {
|
|
5011
|
+
mimeType: contextPart.mimeType ?? "application/octet-stream",
|
|
5012
|
+
data: contextPart.blob
|
|
5013
|
+
}
|
|
5014
|
+
};
|
|
4376
5015
|
processedQueryParts.push(
|
|
4377
|
-
clampInlineMediaPart(
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
5016
|
+
preserveUnsupportedImageForBridge && hasImageParts([inlinePart]) ? inlinePart : clampInlineMediaPart(inlinePart)
|
|
5017
|
+
);
|
|
5018
|
+
}
|
|
5019
|
+
}
|
|
5020
|
+
return this.#applyVisionBridgeIfNeeded(processedQueryParts, abortSignal);
|
|
5021
|
+
}
|
|
5022
|
+
async #applyVisionBridgeIfNeeded(parts, abortSignal) {
|
|
5023
|
+
if (!hasImageParts(parts) || !shouldRunVisionBridge(this.config)) {
|
|
5024
|
+
return parts;
|
|
5025
|
+
}
|
|
5026
|
+
let bridgeResult;
|
|
5027
|
+
try {
|
|
5028
|
+
debugLogger5.debug("vision bridge: gate matched, running conversion");
|
|
5029
|
+
bridgeResult = await runVisionBridge({
|
|
5030
|
+
config: this.config,
|
|
5031
|
+
parts,
|
|
5032
|
+
signal: abortSignal
|
|
5033
|
+
});
|
|
5034
|
+
} catch (error) {
|
|
5035
|
+
debugLogger5.debug(
|
|
5036
|
+
`vision bridge: failed before replacement; falling back to text-only parts error=${String(error instanceof Error ? error.message : error)}`
|
|
5037
|
+
);
|
|
5038
|
+
return splitImageParts(parts).nonImageParts;
|
|
5039
|
+
}
|
|
5040
|
+
debugLogger5.debug(
|
|
5041
|
+
`vision bridge: status=${bridgeResult.status} applied=${bridgeResult.applied} model=${bridgeResult.modelId ?? "(none)"}${bridgeResult.error ? ` error=${bridgeResult.error}` : ""}`
|
|
5042
|
+
);
|
|
5043
|
+
if (bridgeResult.status !== "skipped" || bridgeResult.egressOccurred) {
|
|
5044
|
+
try {
|
|
5045
|
+
await this.messageEmitter.emitAgentMessage(
|
|
5046
|
+
this.#formatVisionBridgeNotice(bridgeResult)
|
|
5047
|
+
);
|
|
5048
|
+
} catch (error) {
|
|
5049
|
+
debugLogger5.debug(
|
|
5050
|
+
`vision bridge: failed to emit notice; continuing with bridge result error=${String(error instanceof Error ? error.message : error)}`
|
|
5051
|
+
);
|
|
5052
|
+
}
|
|
5053
|
+
}
|
|
5054
|
+
if (abortSignal.aborted) {
|
|
5055
|
+
debugLogger5.debug("vision bridge: turn aborted after bridge returned");
|
|
5056
|
+
return splitImageParts(parts).nonImageParts;
|
|
5057
|
+
}
|
|
5058
|
+
if (bridgeResult.applied && bridgeResult.parts != null) {
|
|
5059
|
+
return normalizeParts(bridgeResult.parts);
|
|
5060
|
+
}
|
|
5061
|
+
return splitImageParts(parts).nonImageParts;
|
|
5062
|
+
}
|
|
5063
|
+
#formatVisionBridgeNotice(result) {
|
|
5064
|
+
const modelName = result.modelId ?? "vision model";
|
|
5065
|
+
const target = result.modelEndpoint ? `${modelName} (${result.modelEndpoint})` : modelName;
|
|
5066
|
+
const egressNote = result.egressOccurred ? ` Your image and prompt/context were sent to ${target}.` : "";
|
|
5067
|
+
if (result.status === "failed") {
|
|
5068
|
+
const reason = result.egressOccurred ? "the vision model request failed" : "the vision bridge could not run";
|
|
5069
|
+
return `Vision bridge (${modelName}) failed: ${reason}.${egressNote} The image was not interpreted.`;
|
|
5070
|
+
}
|
|
5071
|
+
if (result.status === "skipped") {
|
|
5072
|
+
return `Vision bridge cancelled.${egressNote}`;
|
|
5073
|
+
}
|
|
5074
|
+
const omitted = result.omittedCount > 0 ? ` (${result.omittedCount} image(s) omitted)` : "";
|
|
5075
|
+
return `Converted ${result.convertedCount} image(s)${omitted} to text via ${target}. Your image and prompt/context were sent to that model.`;
|
|
5076
|
+
}
|
|
5077
|
+
async #resolveExtensionMentionParts(extensionMentions, abortSignal) {
|
|
5078
|
+
if (extensionMentions.size === 0) return [];
|
|
5079
|
+
const activeExtensions = this.config.getActiveExtensions?.() ?? [];
|
|
5080
|
+
if (activeExtensions.length === 0) return [];
|
|
5081
|
+
const extensionParts = [];
|
|
5082
|
+
const resolvedExtensionNames = /* @__PURE__ */ new Set();
|
|
5083
|
+
let remainingBudget = EXTENSION_CONTEXT_BUDGET;
|
|
5084
|
+
for (const name of extensionMentions.values()) {
|
|
5085
|
+
const extension = matchExtensionByRef(name, activeExtensions);
|
|
5086
|
+
if (!extension) {
|
|
5087
|
+
this.debug(
|
|
5088
|
+
`Extension "${name}" not found among active extensions. Available: ${activeExtensions.map((e) => e.name).join(", ") || "(none)"}`
|
|
4383
5089
|
);
|
|
5090
|
+
continue;
|
|
4384
5091
|
}
|
|
5092
|
+
if (resolvedExtensionNames.has(extension.name)) continue;
|
|
5093
|
+
resolvedExtensionNames.add(extension.name);
|
|
5094
|
+
const context = await buildExtensionMentionContext(extension, {
|
|
5095
|
+
remainingBudget,
|
|
5096
|
+
signal: abortSignal,
|
|
5097
|
+
onDebugMessage: /* @__PURE__ */ __name((message) => this.debug(message), "onDebugMessage")
|
|
5098
|
+
});
|
|
5099
|
+
remainingBudget = context.remainingBudget;
|
|
5100
|
+
extensionParts.push({ text: context.text });
|
|
4385
5101
|
}
|
|
4386
|
-
return
|
|
5102
|
+
return extensionParts;
|
|
4387
5103
|
}
|
|
4388
5104
|
debug(msg) {
|
|
4389
5105
|
if (this.config.getDebugMode()) {
|
|
4390
|
-
|
|
5106
|
+
debugLogger5.warn(msg);
|
|
4391
5107
|
}
|
|
4392
5108
|
}
|
|
4393
5109
|
/**
|
|
@@ -4407,7 +5123,7 @@ ${contextPart.text}`
|
|
|
4407
5123
|
}
|
|
4408
5124
|
);
|
|
4409
5125
|
}).catch((err) => {
|
|
4410
|
-
|
|
5126
|
+
debugLogger5.debug(
|
|
4411
5127
|
`ACP terminalSequence notification dropped (session=${this.sessionId}): ${err instanceof Error ? err.message : String(err)}`
|
|
4412
5128
|
);
|
|
4413
5129
|
});
|
|
@@ -4518,8 +5234,9 @@ function runWithAcpRuntimeOutputDir(settings, cwd, fn) {
|
|
|
4518
5234
|
__name(runWithAcpRuntimeOutputDir, "runWithAcpRuntimeOutputDir");
|
|
4519
5235
|
|
|
4520
5236
|
// packages/cli/src/acp-integration/acpAgent.ts
|
|
4521
|
-
var
|
|
5237
|
+
var debugLogger6 = createDebugLogger("ACP_AGENT");
|
|
4522
5238
|
var BTW_CHILD_TIMEOUT_MS = 55e3;
|
|
5239
|
+
var WORKSPACE_MEMORY_REMEMBER_CHILD_TIMEOUT_MS = 295e3;
|
|
4523
5240
|
function collapseForkDirective(directive, maxLength) {
|
|
4524
5241
|
const oneLine = directive.replace(/\s+/g, " ").trim();
|
|
4525
5242
|
return oneLine.length > maxLength ? `${oneLine.slice(0, maxLength - 3)}\u2026` : oneLine;
|
|
@@ -4642,7 +5359,7 @@ function readRequiredString(value, fieldName) {
|
|
|
4642
5359
|
}
|
|
4643
5360
|
__name(readRequiredString, "readRequiredString");
|
|
4644
5361
|
function validateSkillSlug(slug) {
|
|
4645
|
-
if (!slug || slug === "." || slug === ".." || slug.includes("/") || slug.includes(
|
|
5362
|
+
if (!slug || slug === "." || slug === ".." || slug.includes("/") || slug.includes(path3.sep) || !/^[a-zA-Z0-9._-]+$/.test(slug)) {
|
|
4646
5363
|
throw RequestError.invalidParams(void 0, "Invalid skill.slug");
|
|
4647
5364
|
}
|
|
4648
5365
|
}
|
|
@@ -5111,7 +5828,7 @@ async function downloadGitHubSkillDirectoryFromApi(githubUrl, directoryPath, rel
|
|
|
5111
5828
|
const name = readRequiredString(record["name"], "github.name");
|
|
5112
5829
|
const itemPath = readRequiredString(record["path"], "github.path");
|
|
5113
5830
|
const type = readRequiredString(record["type"], "github.type");
|
|
5114
|
-
const relativePath = relativeRoot ?
|
|
5831
|
+
const relativePath = relativeRoot ? path3.posix.join(relativeRoot, name) : name;
|
|
5115
5832
|
if (type === "dir") {
|
|
5116
5833
|
files.push(
|
|
5117
5834
|
...await downloadGitHubSkillDirectoryFromApi(
|
|
@@ -5158,7 +5875,7 @@ async function downloadGitHubSkillDirectory(githubUrl, directoryPath) {
|
|
|
5158
5875
|
githubUrl,
|
|
5159
5876
|
directoryPath
|
|
5160
5877
|
).catch((error) => {
|
|
5161
|
-
|
|
5878
|
+
debugLogger6.warn(
|
|
5162
5879
|
"GitHub API directory listing failed, falling back to archive download:",
|
|
5163
5880
|
error
|
|
5164
5881
|
);
|
|
@@ -5171,10 +5888,10 @@ __name(downloadGitHubSkillDirectory, "downloadGitHubSkillDirectory");
|
|
|
5171
5888
|
async function downloadSkill(sourceUrl) {
|
|
5172
5889
|
assertAllowedSkillSourceUrl(sourceUrl);
|
|
5173
5890
|
const githubUrl = parseGitHubBlobSkillUrl(sourceUrl);
|
|
5174
|
-
if (!githubUrl ||
|
|
5891
|
+
if (!githubUrl || path3.posix.basename(githubUrl.filePath) !== "SKILL.md") {
|
|
5175
5892
|
return downloadSingleSkillFile(sourceUrl);
|
|
5176
5893
|
}
|
|
5177
|
-
const skillDirectory =
|
|
5894
|
+
const skillDirectory = path3.posix.dirname(githubUrl.filePath);
|
|
5178
5895
|
const files = await downloadGitHubSkillDirectory(githubUrl, skillDirectory);
|
|
5179
5896
|
const skillFile = files.find((file) => file.relativePath === "SKILL.md");
|
|
5180
5897
|
if (!skillFile) {
|
|
@@ -5190,9 +5907,9 @@ async function downloadSkill(sourceUrl) {
|
|
|
5190
5907
|
}
|
|
5191
5908
|
__name(downloadSkill, "downloadSkill");
|
|
5192
5909
|
function resolveSkillInstallPath(skillDir, relativePath) {
|
|
5193
|
-
const root =
|
|
5194
|
-
const target =
|
|
5195
|
-
if (target !== root && !target.startsWith(root +
|
|
5910
|
+
const root = path3.resolve(skillDir);
|
|
5911
|
+
const target = path3.resolve(skillDir, relativePath);
|
|
5912
|
+
if (target !== root && !target.startsWith(root + path3.sep)) {
|
|
5196
5913
|
throw RequestError.invalidParams(
|
|
5197
5914
|
void 0,
|
|
5198
5915
|
`Invalid skill file path: ${relativePath}`
|
|
@@ -5202,9 +5919,9 @@ function resolveSkillInstallPath(skillDir, relativePath) {
|
|
|
5202
5919
|
}
|
|
5203
5920
|
__name(resolveSkillInstallPath, "resolveSkillInstallPath");
|
|
5204
5921
|
function resolveManagedSkillDir(skillsBaseDir, slug) {
|
|
5205
|
-
const root =
|
|
5206
|
-
const skillDir =
|
|
5207
|
-
if (!skillDir.startsWith(root +
|
|
5922
|
+
const root = path3.resolve(skillsBaseDir);
|
|
5923
|
+
const skillDir = path3.resolve(skillsBaseDir, slug);
|
|
5924
|
+
if (!skillDir.startsWith(root + path3.sep)) {
|
|
5208
5925
|
throw RequestError.invalidParams(void 0, "Invalid skill.slug");
|
|
5209
5926
|
}
|
|
5210
5927
|
return skillDir;
|
|
@@ -5760,7 +6477,7 @@ function readMcpServers(source, scope) {
|
|
|
5760
6477
|
const server = toMcpServerConfig(value);
|
|
5761
6478
|
return server ? { name, scope, server: redactMcpServerSecrets(server) } : void 0;
|
|
5762
6479
|
} catch (error) {
|
|
5763
|
-
|
|
6480
|
+
debugLogger6.warn(
|
|
5764
6481
|
`Skipping malformed MCP server config [${scope}:${name}]:`,
|
|
5765
6482
|
error
|
|
5766
6483
|
);
|
|
@@ -5856,7 +6573,7 @@ function readHooks(source, scope, extensionName) {
|
|
|
5856
6573
|
extensionName
|
|
5857
6574
|
});
|
|
5858
6575
|
} catch (error) {
|
|
5859
|
-
|
|
6576
|
+
debugLogger6.warn(
|
|
5860
6577
|
`Skipping malformed hook entry [${scope}:${event}:${index}]:`,
|
|
5861
6578
|
error
|
|
5862
6579
|
);
|
|
@@ -5881,14 +6598,14 @@ function readScopeSettings(settings, scope) {
|
|
|
5881
6598
|
__name(readScopeSettings, "readScopeSettings");
|
|
5882
6599
|
async function resolvePreferredMemoryFile(dir, fallbackFilename) {
|
|
5883
6600
|
for (const filename of getAllGeminiMdFilenames()) {
|
|
5884
|
-
const filePath =
|
|
6601
|
+
const filePath = path3.join(dir, filename);
|
|
5885
6602
|
try {
|
|
5886
6603
|
await fs.access(filePath);
|
|
5887
6604
|
return filePath;
|
|
5888
6605
|
} catch {
|
|
5889
6606
|
}
|
|
5890
6607
|
}
|
|
5891
|
-
return
|
|
6608
|
+
return path3.join(dir, fallbackFilename);
|
|
5892
6609
|
}
|
|
5893
6610
|
__name(resolvePreferredMemoryFile, "resolvePreferredMemoryFile");
|
|
5894
6611
|
async function resolveQwenMemoryPaths(params) {
|
|
@@ -5909,13 +6626,37 @@ async function resolveQwenMemoryPaths(params) {
|
|
|
5909
6626
|
};
|
|
5910
6627
|
}
|
|
5911
6628
|
__name(resolveQwenMemoryPaths, "resolveQwenMemoryPaths");
|
|
6629
|
+
async function deliverClientMcpMessage(connection, serverName, message) {
|
|
6630
|
+
if (!connection) {
|
|
6631
|
+
throw new Error(
|
|
6632
|
+
`client MCP server '${serverName}' has no ACP connection yet`
|
|
6633
|
+
);
|
|
6634
|
+
}
|
|
6635
|
+
const response = await connection.extMethod(
|
|
6636
|
+
SERVE_CONTROL_EXT_METHODS.clientMcpMessage,
|
|
6637
|
+
{ server: serverName, payload: message }
|
|
6638
|
+
);
|
|
6639
|
+
const payload = response["payload"];
|
|
6640
|
+
if (payload === void 0 || payload === null) {
|
|
6641
|
+
throw new Error(
|
|
6642
|
+
`client_mcp/message returned no payload for server '${serverName}'`
|
|
6643
|
+
);
|
|
6644
|
+
}
|
|
6645
|
+
return payload;
|
|
6646
|
+
}
|
|
6647
|
+
__name(deliverClientMcpMessage, "deliverClientMcpMessage");
|
|
5912
6648
|
async function runAcpAgent(config, settings, argv) {
|
|
6649
|
+
let acpConnection;
|
|
6650
|
+
const bootstrapClientMcpSender = /* @__PURE__ */ __name((serverName, message) => deliverClientMcpMessage(acpConnection, serverName, message), "bootstrapClientMcpSender");
|
|
5913
6651
|
await config.initialize({
|
|
5914
6652
|
skipGeminiInitialization: true,
|
|
5915
6653
|
// Bootstrap skips MCP discovery — each session runs its own
|
|
5916
6654
|
// pool-routed discovery, so bootstrap-level spawns would be
|
|
5917
6655
|
// redundant subprocess leaks (W119).
|
|
5918
|
-
skipMcpDiscovery: true
|
|
6656
|
+
skipMcpDiscovery: true,
|
|
6657
|
+
// Bind the workspace-level manager's SDK callback so a runtime-added
|
|
6658
|
+
// client-hosted MCP server (#5626) round-trips over the parent WS.
|
|
6659
|
+
sendSdkMcpMessage: bootstrapClientMcpSender
|
|
5919
6660
|
});
|
|
5920
6661
|
const stdout = Writable.toWeb(process.stdout);
|
|
5921
6662
|
const stdin = Readable.toWeb(process.stdin);
|
|
@@ -5925,6 +6666,7 @@ async function runAcpAgent(config, settings, argv) {
|
|
|
5925
6666
|
const stream = ndJsonStream(stdout, stdin);
|
|
5926
6667
|
let agentInstance;
|
|
5927
6668
|
const connection = new AgentSideConnection((conn) => {
|
|
6669
|
+
acpConnection = conn;
|
|
5928
6670
|
agentInstance = new QwenAgent(config, settings, argv, conn);
|
|
5929
6671
|
return agentInstance;
|
|
5930
6672
|
}, stream);
|
|
@@ -5933,7 +6675,7 @@ async function runAcpAgent(config, settings, argv) {
|
|
|
5933
6675
|
try {
|
|
5934
6676
|
await agentInstance.shutdownMcpPool(8e3);
|
|
5935
6677
|
} catch (err) {
|
|
5936
|
-
|
|
6678
|
+
debugLogger6.error(`[ACP] MCP pool drain (${label}) error:`, err);
|
|
5937
6679
|
}
|
|
5938
6680
|
}, "drainPoolBeforeExit");
|
|
5939
6681
|
let shuttingDown = false;
|
|
@@ -5960,7 +6702,7 @@ async function runAcpAgent(config, settings, argv) {
|
|
|
5960
6702
|
try {
|
|
5961
6703
|
await hookSystem.fireSessionEndEvent(reason);
|
|
5962
6704
|
} catch (err) {
|
|
5963
|
-
|
|
6705
|
+
debugLogger6.warn(
|
|
5964
6706
|
`SessionEnd hook failed: ${err instanceof Error ? err.message : String(err)}`
|
|
5965
6707
|
);
|
|
5966
6708
|
}
|
|
@@ -5969,7 +6711,7 @@ async function runAcpAgent(config, settings, argv) {
|
|
|
5969
6711
|
const shutdownHandler = /* @__PURE__ */ __name(async () => {
|
|
5970
6712
|
if (shuttingDown) return;
|
|
5971
6713
|
shuttingDown = true;
|
|
5972
|
-
|
|
6714
|
+
debugLogger6.debug("[ACP] Shutdown signal received, closing streams");
|
|
5973
6715
|
await fireSessionEndOnce("other" /* Other */);
|
|
5974
6716
|
agentInstance?.disposeSessions();
|
|
5975
6717
|
try {
|
|
@@ -5982,7 +6724,7 @@ async function runAcpAgent(config, settings, argv) {
|
|
|
5982
6724
|
}
|
|
5983
6725
|
await drainPoolBeforeExit("signal");
|
|
5984
6726
|
runExitCleanup().catch((err) => {
|
|
5985
|
-
|
|
6727
|
+
debugLogger6.error("[ACP] Cleanup error:", err);
|
|
5986
6728
|
}).finally(() => {
|
|
5987
6729
|
process.exit(0);
|
|
5988
6730
|
});
|
|
@@ -6169,33 +6911,55 @@ var QwenAgent = class {
|
|
|
6169
6911
|
try {
|
|
6170
6912
|
const result = await this.mcpPool.drainAll({ force: true, timeoutMs });
|
|
6171
6913
|
if (result.forced > 0 || result.errors.length > 0) {
|
|
6172
|
-
|
|
6914
|
+
debugLogger6.warn(
|
|
6173
6915
|
`MCP pool drain: ${result.drained} clean, ${result.forced} timed out, ${result.errors.length} errors`
|
|
6174
6916
|
);
|
|
6175
6917
|
}
|
|
6176
6918
|
} catch (err) {
|
|
6177
|
-
|
|
6919
|
+
debugLogger6.error(
|
|
6178
6920
|
`MCP pool drainAll failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6179
6921
|
);
|
|
6180
6922
|
}
|
|
6181
6923
|
}
|
|
6182
|
-
async closeStoredSession(sessionId) {
|
|
6924
|
+
async closeStoredSession(sessionId, opts) {
|
|
6183
6925
|
const session = this.sessions.get(sessionId);
|
|
6184
6926
|
if (!session) {
|
|
6185
6927
|
this.mcpPool?.releaseSession(sessionId);
|
|
6186
6928
|
return;
|
|
6187
6929
|
}
|
|
6930
|
+
const requireFlush = opts?.requireFlush === true;
|
|
6931
|
+
const flushRecording = /* @__PURE__ */ __name(async () => {
|
|
6932
|
+
try {
|
|
6933
|
+
await session.getConfig().getChatRecordingService()?.flush();
|
|
6934
|
+
return void 0;
|
|
6935
|
+
} catch (err) {
|
|
6936
|
+
debugLogger6.debug(
|
|
6937
|
+
`Session ${sessionId} chat recording flush during close failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6938
|
+
);
|
|
6939
|
+
return err;
|
|
6940
|
+
}
|
|
6941
|
+
}, "flushRecording");
|
|
6942
|
+
if (requireFlush) {
|
|
6943
|
+
const preCancelFlushError = await flushRecording();
|
|
6944
|
+
if (preCancelFlushError !== void 0) {
|
|
6945
|
+
throw preCancelFlushError;
|
|
6946
|
+
}
|
|
6947
|
+
}
|
|
6188
6948
|
try {
|
|
6189
6949
|
await session.cancelPendingPrompt();
|
|
6190
6950
|
} catch (err) {
|
|
6191
|
-
|
|
6951
|
+
debugLogger6.debug(
|
|
6192
6952
|
`Session ${sessionId} cancel during close failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6193
6953
|
);
|
|
6194
6954
|
}
|
|
6955
|
+
const flushError = await flushRecording();
|
|
6956
|
+
if (flushError !== void 0 && requireFlush) {
|
|
6957
|
+
throw flushError;
|
|
6958
|
+
}
|
|
6195
6959
|
try {
|
|
6196
6960
|
await session.getConfig().getToolRegistry()?.stop();
|
|
6197
6961
|
} catch (err) {
|
|
6198
|
-
|
|
6962
|
+
debugLogger6.debug(
|
|
6199
6963
|
`Session ${sessionId} tool registry stop during close failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6200
6964
|
);
|
|
6201
6965
|
}
|
|
@@ -6229,7 +6993,7 @@ var QwenAgent = class {
|
|
|
6229
6993
|
scope: "workspace",
|
|
6230
6994
|
...event
|
|
6231
6995
|
}).catch((err) => {
|
|
6232
|
-
|
|
6996
|
+
debugLogger6.debug(
|
|
6233
6997
|
`MCP workspace budget event delivery to session ${sid} failed (kind=${event.kind}): ${err instanceof Error ? err.message : String(err)}`
|
|
6234
6998
|
);
|
|
6235
6999
|
});
|
|
@@ -6238,7 +7002,7 @@ var QwenAgent = class {
|
|
|
6238
7002
|
async initialize(args) {
|
|
6239
7003
|
this.clientCapabilities = args.clientCapabilities;
|
|
6240
7004
|
const authMethods = buildAuthMethods();
|
|
6241
|
-
const version = "0.19.
|
|
7005
|
+
const version = "0.19.4-nightly.20260702.46814e4f1";
|
|
6242
7006
|
return {
|
|
6243
7007
|
protocolVersion: PROTOCOL_VERSION,
|
|
6244
7008
|
agentInfo: {
|
|
@@ -6394,7 +7158,7 @@ var QwenAgent = class {
|
|
|
6394
7158
|
session.pendingWorktreeNotice = restored.contextMessage;
|
|
6395
7159
|
}
|
|
6396
7160
|
} catch (error) {
|
|
6397
|
-
|
|
7161
|
+
debugLogger6.warn(`ACP worktree restore failed: ${error}`);
|
|
6398
7162
|
}
|
|
6399
7163
|
}
|
|
6400
7164
|
async unstable_listSessions(params) {
|
|
@@ -6515,7 +7279,7 @@ var QwenAgent = class {
|
|
|
6515
7279
|
await extensionManager.refreshCache();
|
|
6516
7280
|
extensions = extensionManager.getLoadedExtensions();
|
|
6517
7281
|
} catch (error) {
|
|
6518
|
-
|
|
7282
|
+
debugLogger6.warn(
|
|
6519
7283
|
"Extension loading failed, continuing without extensions:",
|
|
6520
7284
|
error
|
|
6521
7285
|
);
|
|
@@ -6641,7 +7405,7 @@ var QwenAgent = class {
|
|
|
6641
7405
|
pm.addPersistentRule(rule, ruleType);
|
|
6642
7406
|
}
|
|
6643
7407
|
} catch (error) {
|
|
6644
|
-
|
|
7408
|
+
debugLogger6.warn(
|
|
6645
7409
|
`Failed to sync permission rules to a live session: ${error instanceof Error ? error.message : String(error)}`
|
|
6646
7410
|
);
|
|
6647
7411
|
}
|
|
@@ -7151,7 +7915,7 @@ var QwenAgent = class {
|
|
|
7151
7915
|
const currentAuth = activeRuntimeSnapshot?.authType ?? currentAuthType;
|
|
7152
7916
|
const currentAcpModelId = hasCurrentModel && currentAuth ? formatAcpModelId(currentModelId, currentAuth) : currentModelId || void 0;
|
|
7153
7917
|
const providers = /* @__PURE__ */ new Map();
|
|
7154
|
-
for (const model of config.getAllConfiguredModels()) {
|
|
7918
|
+
for (const model of config.getAllConfiguredModels().filter(isMainSelectableModel)) {
|
|
7155
7919
|
const authType = String(model.authType);
|
|
7156
7920
|
let provider = providers.get(authType);
|
|
7157
7921
|
if (!provider) {
|
|
@@ -7871,6 +8635,7 @@ var QwenAgent = class {
|
|
|
7871
8635
|
id: ext.id,
|
|
7872
8636
|
name: ext.name,
|
|
7873
8637
|
displayName: ext.displayName,
|
|
8638
|
+
...ext.config.description ? { description: ext.config.description } : {},
|
|
7874
8639
|
version: ext.version,
|
|
7875
8640
|
isActive: ext.isActive,
|
|
7876
8641
|
path: ext.path,
|
|
@@ -7917,9 +8682,9 @@ var QwenAgent = class {
|
|
|
7917
8682
|
);
|
|
7918
8683
|
}
|
|
7919
8684
|
const download = await downloadSkill(request.sourceUrl);
|
|
7920
|
-
const skillsBaseDir =
|
|
8685
|
+
const skillsBaseDir = path3.join(Storage.getGlobalQwenDir(), "skills");
|
|
7921
8686
|
const skillDir = resolveManagedSkillDir(skillsBaseDir, request.slug);
|
|
7922
|
-
const skillFile =
|
|
8687
|
+
const skillFile = path3.join(skillDir, "SKILL.md");
|
|
7923
8688
|
const parsed = skillManager.parseSkillContent(
|
|
7924
8689
|
download.skillContent,
|
|
7925
8690
|
skillFile,
|
|
@@ -7939,7 +8704,7 @@ var QwenAgent = class {
|
|
|
7939
8704
|
stagingDir,
|
|
7940
8705
|
file.relativePath
|
|
7941
8706
|
);
|
|
7942
|
-
await fs.mkdir(
|
|
8707
|
+
await fs.mkdir(path3.dirname(targetPath), { recursive: true });
|
|
7943
8708
|
await fs.writeFile(targetPath, file.content);
|
|
7944
8709
|
}
|
|
7945
8710
|
await fs.rm(skillDir, { recursive: true, force: true });
|
|
@@ -7978,11 +8743,11 @@ var QwenAgent = class {
|
|
|
7978
8743
|
`Skill name "${parsed.name}" does not match requested slug "${request.slug}"`
|
|
7979
8744
|
);
|
|
7980
8745
|
}
|
|
7981
|
-
const resolvedSkillDir =
|
|
7982
|
-
const resolvedSkillFile =
|
|
7983
|
-
const globalDir =
|
|
7984
|
-
const isDedicatedSkillDir = resolvedSkillFile ===
|
|
7985
|
-
if (!isDedicatedSkillDir || resolvedSkillDir ===
|
|
8746
|
+
const resolvedSkillDir = path3.resolve(skillDir);
|
|
8747
|
+
const resolvedSkillFile = path3.resolve(skillFile);
|
|
8748
|
+
const globalDir = path3.resolve(Storage.getGlobalQwenDir());
|
|
8749
|
+
const isDedicatedSkillDir = resolvedSkillFile === path3.join(resolvedSkillDir, "SKILL.md");
|
|
8750
|
+
if (!isDedicatedSkillDir || resolvedSkillDir === path3.parse(resolvedSkillDir).root || resolvedSkillDir === globalDir) {
|
|
7986
8751
|
throw RequestError.invalidParams(
|
|
7987
8752
|
void 0,
|
|
7988
8753
|
`Refusing to delete unexpected skill directory: ${skillDir}`
|
|
@@ -7998,10 +8763,10 @@ var QwenAgent = class {
|
|
|
7998
8763
|
async readManagedSkillFile(slug, scope, skillManager, cwd) {
|
|
7999
8764
|
if (scope === "global") {
|
|
8000
8765
|
const qwenSkillDir = resolveManagedSkillDir(
|
|
8001
|
-
|
|
8766
|
+
path3.join(Storage.getGlobalQwenDir(), "skills"),
|
|
8002
8767
|
slug
|
|
8003
8768
|
);
|
|
8004
|
-
const qwenSkillFile =
|
|
8769
|
+
const qwenSkillFile = path3.join(qwenSkillDir, "SKILL.md");
|
|
8005
8770
|
const qwenContent = await fs.readFile(qwenSkillFile, "utf8").catch(() => void 0);
|
|
8006
8771
|
if (qwenContent !== void 0) {
|
|
8007
8772
|
return {
|
|
@@ -8037,15 +8802,15 @@ var QwenAgent = class {
|
|
|
8037
8802
|
);
|
|
8038
8803
|
});
|
|
8039
8804
|
return {
|
|
8040
|
-
skillDir:
|
|
8805
|
+
skillDir: path3.dirname(skillFile),
|
|
8041
8806
|
skillFile,
|
|
8042
8807
|
content
|
|
8043
8808
|
};
|
|
8044
8809
|
}
|
|
8045
8810
|
async findProjectSkillFileFromCwd(slug, cwd, skillManager) {
|
|
8046
|
-
const projectRoot =
|
|
8811
|
+
const projectRoot = path3.resolve(cwd);
|
|
8047
8812
|
for (const configDir of PROJECT_SKILL_DIRS) {
|
|
8048
|
-
const baseDir =
|
|
8813
|
+
const baseDir = path3.join(projectRoot, configDir, SKILLS_DIR);
|
|
8049
8814
|
const skills = await skillManager.loadSkillsFromDir(baseDir, "project");
|
|
8050
8815
|
const skill = skills.find((candidate) => candidate.name === slug);
|
|
8051
8816
|
const skillFile = skill?.filePath;
|
|
@@ -8057,7 +8822,7 @@ var QwenAgent = class {
|
|
|
8057
8822
|
);
|
|
8058
8823
|
});
|
|
8059
8824
|
return {
|
|
8060
|
-
skillDir:
|
|
8825
|
+
skillDir: path3.dirname(skillFile),
|
|
8061
8826
|
skillFile,
|
|
8062
8827
|
content
|
|
8063
8828
|
};
|
|
@@ -8088,7 +8853,7 @@ var QwenAgent = class {
|
|
|
8088
8853
|
}
|
|
8089
8854
|
const nextContent = setSkillFrontmatterEnabled(content, request.enabled);
|
|
8090
8855
|
skillManager.parseSkillContent(nextContent, skillFile, level);
|
|
8091
|
-
if (
|
|
8856
|
+
if (path3.basename(skillFile) !== "SKILL.md") {
|
|
8092
8857
|
throw RequestError.invalidParams(
|
|
8093
8858
|
void 0,
|
|
8094
8859
|
`Refusing to write to unexpected skill file: ${skillFile}`
|
|
@@ -8368,6 +9133,76 @@ var QwenAgent = class {
|
|
|
8368
9133
|
return this.buildWorkspaceExtensionsStatus(
|
|
8369
9134
|
this.config
|
|
8370
9135
|
);
|
|
9136
|
+
case SERVE_CONTROL_EXT_METHODS.workspaceMemoryRememberAvailability:
|
|
9137
|
+
return {
|
|
9138
|
+
available: this.config.isManagedMemoryAvailable()
|
|
9139
|
+
};
|
|
9140
|
+
case SERVE_CONTROL_EXT_METHODS.workspaceMemoryRemember: {
|
|
9141
|
+
const content = params["content"];
|
|
9142
|
+
if (typeof content !== "string" || !content.trim()) {
|
|
9143
|
+
throw RequestError.invalidParams(
|
|
9144
|
+
void 0,
|
|
9145
|
+
"Invalid or missing content"
|
|
9146
|
+
);
|
|
9147
|
+
}
|
|
9148
|
+
if (Buffer.byteLength(content, "utf8") > MAX_REMEMBER_CONTENT_BYTES) {
|
|
9149
|
+
throw RequestError.invalidParams(
|
|
9150
|
+
void 0,
|
|
9151
|
+
"Content exceeds maximum size"
|
|
9152
|
+
);
|
|
9153
|
+
}
|
|
9154
|
+
const rawContextMode = params["contextMode"] ?? "workspace";
|
|
9155
|
+
if (rawContextMode !== "workspace" && rawContextMode !== "clean") {
|
|
9156
|
+
throw RequestError.invalidParams(void 0, "Invalid contextMode");
|
|
9157
|
+
}
|
|
9158
|
+
const contextMode = rawContextMode;
|
|
9159
|
+
if (!this.config.isManagedMemoryAvailable()) {
|
|
9160
|
+
throw new RequestError(
|
|
9161
|
+
-32009,
|
|
9162
|
+
"Managed memory is unavailable for this daemon workspace",
|
|
9163
|
+
{ errorKind: "managed_memory_unavailable" }
|
|
9164
|
+
);
|
|
9165
|
+
}
|
|
9166
|
+
const childSignal = AbortSignal.timeout(
|
|
9167
|
+
WORKSPACE_MEMORY_REMEMBER_CHILD_TIMEOUT_MS
|
|
9168
|
+
);
|
|
9169
|
+
try {
|
|
9170
|
+
const result = await runManagedRememberByAgent({
|
|
9171
|
+
config: this.config,
|
|
9172
|
+
projectRoot: this.config.getProjectRoot(),
|
|
9173
|
+
content: content.trim(),
|
|
9174
|
+
contextMode,
|
|
9175
|
+
abortSignal: childSignal
|
|
9176
|
+
});
|
|
9177
|
+
return result;
|
|
9178
|
+
} catch (err) {
|
|
9179
|
+
if (err instanceof RequestError) {
|
|
9180
|
+
throw err;
|
|
9181
|
+
}
|
|
9182
|
+
if (childSignal.aborted) {
|
|
9183
|
+
throw new RequestError(
|
|
9184
|
+
-32099,
|
|
9185
|
+
"Workspace memory remember timed out",
|
|
9186
|
+
{ errorKind: "remember_timeout" }
|
|
9187
|
+
);
|
|
9188
|
+
}
|
|
9189
|
+
const code = extractRememberErrorCode(err);
|
|
9190
|
+
if (code === "managed_memory_unavailable") {
|
|
9191
|
+
throw new RequestError(
|
|
9192
|
+
-32009,
|
|
9193
|
+
"Managed memory is unavailable for this daemon workspace",
|
|
9194
|
+
{ errorKind: "managed_memory_unavailable" }
|
|
9195
|
+
);
|
|
9196
|
+
}
|
|
9197
|
+
throw new RequestError(
|
|
9198
|
+
-32099,
|
|
9199
|
+
err instanceof Error && err.message ? err.message : "Workspace memory remember failed",
|
|
9200
|
+
{
|
|
9201
|
+
errorKind: code
|
|
9202
|
+
}
|
|
9203
|
+
);
|
|
9204
|
+
}
|
|
9205
|
+
}
|
|
8371
9206
|
case SERVE_CONTROL_EXT_METHODS.workspaceMcpRestart: {
|
|
8372
9207
|
const serverName = params["serverName"];
|
|
8373
9208
|
if (typeof serverName !== "string" || serverName.length === 0) {
|
|
@@ -8523,23 +9358,34 @@ var QwenAgent = class {
|
|
|
8523
9358
|
}
|
|
8524
9359
|
if (action === "enable") {
|
|
8525
9360
|
const settings = loadSettings(this.config.getTargetDir());
|
|
9361
|
+
let settingsChanged = false;
|
|
8526
9362
|
for (const scope of ["User" /* User */, "Workspace" /* Workspace */]) {
|
|
8527
9363
|
const scopeSettings = settings.forScope(scope).settings;
|
|
8528
9364
|
const currentExcluded2 = scopeSettings.mcp?.excluded || [];
|
|
8529
|
-
|
|
8530
|
-
|
|
8531
|
-
|
|
8532
|
-
|
|
8533
|
-
|
|
8534
|
-
|
|
9365
|
+
const filtered = currentExcluded2.filter(
|
|
9366
|
+
(pattern) => pattern !== serverName
|
|
9367
|
+
);
|
|
9368
|
+
if (filtered.length !== currentExcluded2.length) {
|
|
9369
|
+
settings.setValue(scope, "mcp.excluded", filtered);
|
|
9370
|
+
settingsChanged = true;
|
|
8535
9371
|
}
|
|
8536
9372
|
}
|
|
8537
9373
|
const currentExcluded = this.config.getExcludedMcpServers() || [];
|
|
8538
|
-
|
|
8539
|
-
|
|
9374
|
+
const runtimeFiltered = currentExcluded.filter(
|
|
9375
|
+
(pattern) => pattern !== serverName
|
|
8540
9376
|
);
|
|
9377
|
+
let runtimeChanged = false;
|
|
9378
|
+
if (runtimeFiltered.length !== currentExcluded.length) {
|
|
9379
|
+
this.config.setExcludedMcpServers(runtimeFiltered);
|
|
9380
|
+
runtimeChanged = true;
|
|
9381
|
+
}
|
|
8541
9382
|
await toolRegistry.discoverToolsForServer(serverName);
|
|
8542
|
-
return {
|
|
9383
|
+
return {
|
|
9384
|
+
serverName,
|
|
9385
|
+
action,
|
|
9386
|
+
ok: true,
|
|
9387
|
+
changed: settingsChanged || runtimeChanged
|
|
9388
|
+
};
|
|
8543
9389
|
}
|
|
8544
9390
|
if (action === "disable") {
|
|
8545
9391
|
const settings = loadSettings(this.config.getTargetDir());
|
|
@@ -8561,18 +9407,27 @@ var QwenAgent = class {
|
|
|
8561
9407
|
}
|
|
8562
9408
|
const scopeSettings = settings.forScope(targetScope).settings;
|
|
8563
9409
|
const currentExcluded = scopeSettings.mcp?.excluded || [];
|
|
8564
|
-
|
|
9410
|
+
let settingsChanged = false;
|
|
9411
|
+
if (!matchesAnyServerPattern(serverName, currentExcluded)) {
|
|
8565
9412
|
settings.setValue(targetScope, "mcp.excluded", [
|
|
8566
9413
|
...currentExcluded,
|
|
8567
9414
|
serverName
|
|
8568
9415
|
]);
|
|
9416
|
+
settingsChanged = true;
|
|
8569
9417
|
}
|
|
8570
9418
|
const runtimeExcluded = this.config.getExcludedMcpServers() || [];
|
|
8571
|
-
|
|
9419
|
+
let runtimeChanged = false;
|
|
9420
|
+
if (!matchesAnyServerPattern(serverName, runtimeExcluded)) {
|
|
8572
9421
|
this.config.setExcludedMcpServers([...runtimeExcluded, serverName]);
|
|
9422
|
+
runtimeChanged = true;
|
|
8573
9423
|
}
|
|
8574
9424
|
await toolRegistry.disableMcpServer(serverName);
|
|
8575
|
-
return {
|
|
9425
|
+
return {
|
|
9426
|
+
serverName,
|
|
9427
|
+
action,
|
|
9428
|
+
ok: true,
|
|
9429
|
+
changed: settingsChanged || runtimeChanged
|
|
9430
|
+
};
|
|
8576
9431
|
}
|
|
8577
9432
|
if (action === "clear-auth") {
|
|
8578
9433
|
const tokenStorage = new MCPOAuthTokenStorage();
|
|
@@ -8687,7 +9542,9 @@ var QwenAgent = class {
|
|
|
8687
9542
|
"Invalid or missing sessionId"
|
|
8688
9543
|
);
|
|
8689
9544
|
}
|
|
8690
|
-
await this.closeStoredSession(sessionId
|
|
9545
|
+
await this.closeStoredSession(sessionId, {
|
|
9546
|
+
requireFlush: params["requireFlush"] === true
|
|
9547
|
+
});
|
|
8691
9548
|
return { sessionId, closed: true };
|
|
8692
9549
|
}
|
|
8693
9550
|
case SERVE_CONTROL_EXT_METHODS.sessionCd: {
|
|
@@ -8699,7 +9556,7 @@ var QwenAgent = class {
|
|
|
8699
9556
|
"Invalid or missing sessionId"
|
|
8700
9557
|
);
|
|
8701
9558
|
}
|
|
8702
|
-
if (typeof targetPath !== "string" || targetPath.length === 0 || !
|
|
9559
|
+
if (typeof targetPath !== "string" || targetPath.length === 0 || !path3.isAbsolute(targetPath) || targetPath.includes("\0")) {
|
|
8703
9560
|
throw RequestError.invalidParams(
|
|
8704
9561
|
void 0,
|
|
8705
9562
|
"Invalid or missing path (must be an absolute path)"
|
|
@@ -8817,7 +9674,7 @@ var QwenAgent = class {
|
|
|
8817
9674
|
try {
|
|
8818
9675
|
await setLanguageAsync(language);
|
|
8819
9676
|
} catch (err) {
|
|
8820
|
-
|
|
9677
|
+
debugLogger6.warn("setLanguageAsync failed:", err);
|
|
8821
9678
|
throw new RequestError(
|
|
8822
9679
|
-32603,
|
|
8823
9680
|
`Failed to switch UI language: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -8831,7 +9688,7 @@ var QwenAgent = class {
|
|
|
8831
9688
|
language
|
|
8832
9689
|
);
|
|
8833
9690
|
} catch (err) {
|
|
8834
|
-
|
|
9691
|
+
debugLogger6.warn("Failed to persist UI language setting:", err);
|
|
8835
9692
|
}
|
|
8836
9693
|
let outputLanguage = null;
|
|
8837
9694
|
let refreshed = false;
|
|
@@ -8846,7 +9703,7 @@ var QwenAgent = class {
|
|
|
8846
9703
|
);
|
|
8847
9704
|
fileWriteOk = true;
|
|
8848
9705
|
} catch (err) {
|
|
8849
|
-
|
|
9706
|
+
debugLogger6.warn("Failed to write output-language.md:", err);
|
|
8850
9707
|
}
|
|
8851
9708
|
if (fileWriteOk) {
|
|
8852
9709
|
try {
|
|
@@ -8856,7 +9713,7 @@ var QwenAgent = class {
|
|
|
8856
9713
|
settingValue
|
|
8857
9714
|
);
|
|
8858
9715
|
} catch (err) {
|
|
8859
|
-
|
|
9716
|
+
debugLogger6.warn(
|
|
8860
9717
|
"Failed to persist output language setting:",
|
|
8861
9718
|
err
|
|
8862
9719
|
);
|
|
@@ -8876,7 +9733,7 @@ var QwenAgent = class {
|
|
|
8876
9733
|
writeOutputLanguageAndRegisterPath(settingValue, cfg);
|
|
8877
9734
|
}
|
|
8878
9735
|
} catch (err) {
|
|
8879
|
-
|
|
9736
|
+
debugLogger6.warn(
|
|
8880
9737
|
`Failed to write output-language.md for session ${s.getId()} (path=${sessionPath ?? "global-default"}):`,
|
|
8881
9738
|
err
|
|
8882
9739
|
);
|
|
@@ -8889,7 +9746,7 @@ var QwenAgent = class {
|
|
|
8889
9746
|
(r) => r.status === "rejected"
|
|
8890
9747
|
).length;
|
|
8891
9748
|
if (failedCount > 0) {
|
|
8892
|
-
|
|
9749
|
+
debugLogger6.warn(
|
|
8893
9750
|
`Language refresh failed for ${failedCount}/${results.length} session(s)`
|
|
8894
9751
|
);
|
|
8895
9752
|
}
|
|
@@ -8907,14 +9764,14 @@ var QwenAgent = class {
|
|
|
8907
9764
|
"Invalid or missing sessionId"
|
|
8908
9765
|
);
|
|
8909
9766
|
}
|
|
8910
|
-
|
|
9767
|
+
debugLogger6.debug(`recap ext-method received for session=${sessionId}`);
|
|
8911
9768
|
const session = this.sessionOrThrow(sessionId);
|
|
8912
9769
|
const config = session.getConfig();
|
|
8913
9770
|
const recap = await generateSessionRecap(
|
|
8914
9771
|
config,
|
|
8915
9772
|
new AbortController().signal
|
|
8916
9773
|
);
|
|
8917
|
-
|
|
9774
|
+
debugLogger6.debug(
|
|
8918
9775
|
`recap ext-method completed for session=${sessionId} result=${recap ? `len=${recap.length}` : "null"}`
|
|
8919
9776
|
);
|
|
8920
9777
|
return { sessionId, recap };
|
|
@@ -8938,7 +9795,7 @@ var QwenAgent = class {
|
|
|
8938
9795
|
const config = session.getConfig();
|
|
8939
9796
|
const cacheSafeParams = buildBtwCacheSafeParams(config);
|
|
8940
9797
|
if (!cacheSafeParams) {
|
|
8941
|
-
|
|
9798
|
+
debugLogger6.debug(`btw: no cacheSafeParams for session=${sessionId}`);
|
|
8942
9799
|
return { sessionId, answer: null };
|
|
8943
9800
|
}
|
|
8944
9801
|
const childSignal = AbortSignal.timeout(BTW_CHILD_TIMEOUT_MS);
|
|
@@ -8986,7 +9843,7 @@ var QwenAgent = class {
|
|
|
8986
9843
|
try {
|
|
8987
9844
|
hasHistory = (config.getGeminiClient().getHistoryShallow() ?? []).length > 0;
|
|
8988
9845
|
} catch (error) {
|
|
8989
|
-
|
|
9846
|
+
debugLogger6.debug("Failed to read history before /fork:", error);
|
|
8990
9847
|
}
|
|
8991
9848
|
if (!hasHistory) {
|
|
8992
9849
|
throw RequestError.invalidParams(
|
|
@@ -9028,7 +9885,7 @@ var QwenAgent = class {
|
|
|
9028
9885
|
]
|
|
9029
9886
|
});
|
|
9030
9887
|
} catch (error) {
|
|
9031
|
-
|
|
9888
|
+
debugLogger6.debug("Failed to record fork event in history:", error);
|
|
9032
9889
|
}
|
|
9033
9890
|
return { sessionId, description, launched: true };
|
|
9034
9891
|
}
|
|
@@ -9091,7 +9948,7 @@ ${outputText}
|
|
|
9091
9948
|
'taskKind must be "agent", "shell", or "monitor"'
|
|
9092
9949
|
);
|
|
9093
9950
|
}
|
|
9094
|
-
|
|
9951
|
+
debugLogger6.info(
|
|
9095
9952
|
`sessionTaskCancel requested sessionId=${sessionId} taskId=${taskId} taskKind=${taskKind}`
|
|
9096
9953
|
);
|
|
9097
9954
|
const session = this.sessionOrThrow(sessionId);
|
|
@@ -9101,7 +9958,7 @@ ${outputText}
|
|
|
9101
9958
|
const task = config.getBackgroundTaskRegistry().get(taskId);
|
|
9102
9959
|
if (!task || task.status !== "running" && task.status !== "paused") {
|
|
9103
9960
|
const reason = task ? "not_running" : "not_found";
|
|
9104
|
-
|
|
9961
|
+
debugLogger6.info(
|
|
9105
9962
|
`sessionTaskCancel skipped sessionId=${sessionId} taskId=${taskId} taskKind=${taskKind} reason=${reason} status=${task?.status ?? "missing"}`
|
|
9106
9963
|
);
|
|
9107
9964
|
return { cancelled: false, reason, status: task?.status };
|
|
@@ -9111,7 +9968,7 @@ ${outputText}
|
|
|
9111
9968
|
} else {
|
|
9112
9969
|
config.getBackgroundTaskRegistry().cancel(taskId);
|
|
9113
9970
|
}
|
|
9114
|
-
|
|
9971
|
+
debugLogger6.info(
|
|
9115
9972
|
`sessionTaskCancel completed sessionId=${sessionId} taskId=${taskId} taskKind=${taskKind} status=${task.status}`
|
|
9116
9973
|
);
|
|
9117
9974
|
return { cancelled: true, status: task.status };
|
|
@@ -9120,13 +9977,13 @@ ${outputText}
|
|
|
9120
9977
|
const task = config.getBackgroundShellRegistry().get(taskId);
|
|
9121
9978
|
if (!task || task.status !== "running") {
|
|
9122
9979
|
const reason = task ? "not_running" : "not_found";
|
|
9123
|
-
|
|
9980
|
+
debugLogger6.info(
|
|
9124
9981
|
`sessionTaskCancel skipped sessionId=${sessionId} taskId=${taskId} taskKind=${taskKind} reason=${reason} status=${task?.status ?? "missing"}`
|
|
9125
9982
|
);
|
|
9126
9983
|
return { cancelled: false, reason, status: task?.status };
|
|
9127
9984
|
}
|
|
9128
9985
|
config.getBackgroundShellRegistry().requestCancel(taskId);
|
|
9129
|
-
|
|
9986
|
+
debugLogger6.info(
|
|
9130
9987
|
`sessionTaskCancel completed sessionId=${sessionId} taskId=${taskId} taskKind=${taskKind} status=${task.status}`
|
|
9131
9988
|
);
|
|
9132
9989
|
return { cancelled: true, status: task.status };
|
|
@@ -9135,13 +9992,13 @@ ${outputText}
|
|
|
9135
9992
|
const task = config.getMonitorRegistry().get(taskId);
|
|
9136
9993
|
if (!task || task.status !== "running") {
|
|
9137
9994
|
const reason = task ? "not_running" : "not_found";
|
|
9138
|
-
|
|
9995
|
+
debugLogger6.info(
|
|
9139
9996
|
`sessionTaskCancel skipped sessionId=${sessionId} taskId=${taskId} taskKind=${taskKind} reason=${reason} status=${task?.status ?? "missing"}`
|
|
9140
9997
|
);
|
|
9141
9998
|
return { cancelled: false, reason, status: task?.status };
|
|
9142
9999
|
}
|
|
9143
10000
|
config.getMonitorRegistry().cancel(taskId);
|
|
9144
|
-
|
|
10001
|
+
debugLogger6.info(
|
|
9145
10002
|
`sessionTaskCancel completed sessionId=${sessionId} taskId=${taskId} taskKind=${taskKind} status=${task.status}`
|
|
9146
10003
|
);
|
|
9147
10004
|
return { cancelled: true, status: task.status };
|
|
@@ -9171,7 +10028,7 @@ ${outputText}
|
|
|
9171
10028
|
durationMs: Date.now() - cleared.setAt
|
|
9172
10029
|
});
|
|
9173
10030
|
}
|
|
9174
|
-
|
|
10031
|
+
debugLogger6.info(
|
|
9175
10032
|
`sessionGoalClear sessionId=${sessionId} cleared=${!!cleared} condition=${cleared?.condition ?? "(none)"}`
|
|
9176
10033
|
);
|
|
9177
10034
|
return {
|
|
@@ -9179,6 +10036,21 @@ ${outputText}
|
|
|
9179
10036
|
condition: cleared?.condition
|
|
9180
10037
|
};
|
|
9181
10038
|
}
|
|
10039
|
+
case SERVE_CONTROL_EXT_METHODS.sessionContinue: {
|
|
10040
|
+
const sessionId = params["sessionId"];
|
|
10041
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
10042
|
+
throw RequestError.invalidParams(
|
|
10043
|
+
void 0,
|
|
10044
|
+
"Invalid or missing sessionId"
|
|
10045
|
+
);
|
|
10046
|
+
}
|
|
10047
|
+
const session = this.sessionOrThrow(sessionId);
|
|
10048
|
+
const result = await session.continueLastTurn();
|
|
10049
|
+
debugLogger6.info(
|
|
10050
|
+
`sessionContinue sessionId=${sessionId} accepted=${result.accepted} interruption=${result.interruption}`
|
|
10051
|
+
);
|
|
10052
|
+
return result;
|
|
10053
|
+
}
|
|
9182
10054
|
case SERVE_CONTROL_EXT_METHODS.workspaceMcpRuntimeAdd: {
|
|
9183
10055
|
const name = params["name"];
|
|
9184
10056
|
const config = params["config"];
|
|
@@ -9189,7 +10061,7 @@ ${outputText}
|
|
|
9189
10061
|
"Invalid or missing name"
|
|
9190
10062
|
);
|
|
9191
10063
|
}
|
|
9192
|
-
if (
|
|
10064
|
+
if (!isValidServerName(name)) {
|
|
9193
10065
|
throw RequestError.invalidParams(
|
|
9194
10066
|
void 0,
|
|
9195
10067
|
"Server name must be \u2264256 chars, alphanumeric + underscore/hyphen, and not a reserved JS property name"
|
|
@@ -9225,13 +10097,37 @@ ${outputText}
|
|
|
9225
10097
|
oauth: _oauth,
|
|
9226
10098
|
headers: _headers,
|
|
9227
10099
|
type: _type,
|
|
10100
|
+
// Reverse tool channel marker (issue #5626, Phase 2). The parent
|
|
10101
|
+
// serve process stamps this on a client-hosted (extension) MCP
|
|
10102
|
+
// server's runtime config; it never reaches the transport itself.
|
|
10103
|
+
[CLIENT_MCP_OVER_WS_CONFIG_FLAG]: clientMcpOverWs,
|
|
9228
10104
|
...safeConfig
|
|
9229
10105
|
} = config;
|
|
10106
|
+
if (clientMcpOverWs === true) {
|
|
10107
|
+
safeConfig["type"] = "sdk";
|
|
10108
|
+
}
|
|
9230
10109
|
const result = await manager.addRuntimeMcpServer(
|
|
9231
10110
|
name,
|
|
9232
10111
|
safeConfig,
|
|
9233
10112
|
originatorClientId
|
|
9234
10113
|
);
|
|
10114
|
+
await Promise.all(
|
|
10115
|
+
this.getActiveSessions().map(async (session) => {
|
|
10116
|
+
const sessionManager = session.getConfig().getToolRegistry()?.getMcpClientManager();
|
|
10117
|
+
if (!sessionManager) return;
|
|
10118
|
+
try {
|
|
10119
|
+
await sessionManager.addRuntimeMcpServer(
|
|
10120
|
+
name,
|
|
10121
|
+
safeConfig,
|
|
10122
|
+
originatorClientId
|
|
10123
|
+
);
|
|
10124
|
+
} catch (sessionErr) {
|
|
10125
|
+
debugLogger6.warn(
|
|
10126
|
+
`workspaceMcpRuntimeAdd: failed to add runtime MCP server '${name}' to active session ${session.getConfig().getSessionId()}: ${sessionErr instanceof Error ? sessionErr.message : String(sessionErr)}`
|
|
10127
|
+
);
|
|
10128
|
+
}
|
|
10129
|
+
})
|
|
10130
|
+
);
|
|
9235
10131
|
return result;
|
|
9236
10132
|
} catch (err) {
|
|
9237
10133
|
if (err instanceof McpBudgetWouldExceedError) {
|
|
@@ -9266,7 +10162,7 @@ ${outputText}
|
|
|
9266
10162
|
"Invalid or missing name"
|
|
9267
10163
|
);
|
|
9268
10164
|
}
|
|
9269
|
-
if (
|
|
10165
|
+
if (!isValidServerName(name)) {
|
|
9270
10166
|
throw RequestError.invalidParams(
|
|
9271
10167
|
void 0,
|
|
9272
10168
|
"Server name must be \u2264256 chars, alphanumeric + underscore/hyphen, and not a reserved JS property name"
|
|
@@ -9289,6 +10185,22 @@ ${outputText}
|
|
|
9289
10185
|
name,
|
|
9290
10186
|
originatorClientId
|
|
9291
10187
|
);
|
|
10188
|
+
await Promise.all(
|
|
10189
|
+
this.getActiveSessions().map(async (session) => {
|
|
10190
|
+
const sessionManager = session.getConfig().getToolRegistry()?.getMcpClientManager();
|
|
10191
|
+
if (!sessionManager) return;
|
|
10192
|
+
try {
|
|
10193
|
+
await sessionManager.removeRuntimeMcpServer(
|
|
10194
|
+
name,
|
|
10195
|
+
originatorClientId
|
|
10196
|
+
);
|
|
10197
|
+
} catch (sessionErr) {
|
|
10198
|
+
debugLogger6.warn(
|
|
10199
|
+
`workspaceMcpRuntimeRemove: failed to remove runtime MCP server '${name}' from active session ${session.getConfig().getSessionId()}: ${sessionErr instanceof Error ? sessionErr.message : String(sessionErr)}`
|
|
10200
|
+
);
|
|
10201
|
+
}
|
|
10202
|
+
})
|
|
10203
|
+
);
|
|
9292
10204
|
return result;
|
|
9293
10205
|
}
|
|
9294
10206
|
case SERVE_CONTROL_EXT_METHODS.workspaceExtensionsRefresh: {
|
|
@@ -9299,7 +10211,7 @@ ${outputText}
|
|
|
9299
10211
|
try {
|
|
9300
10212
|
await extensionManager.refreshTools();
|
|
9301
10213
|
} catch (err) {
|
|
9302
|
-
|
|
10214
|
+
debugLogger6.warn(
|
|
9303
10215
|
`Extension tool refresh failed for session ${sessionId}: ${err instanceof Error ? err.message : String(err)}`
|
|
9304
10216
|
);
|
|
9305
10217
|
}
|
|
@@ -9447,7 +10359,7 @@ ${outputText}
|
|
|
9447
10359
|
filesFailed = fileResult.filesFailed;
|
|
9448
10360
|
} catch (err) {
|
|
9449
10361
|
const reason = err instanceof Error ? err.message : String(err);
|
|
9450
|
-
|
|
10362
|
+
debugLogger6.error(
|
|
9451
10363
|
`[ACP] File-history rewind failed for session=${sessionId} promptId=${promptId}: ${reason}`
|
|
9452
10364
|
);
|
|
9453
10365
|
filesFailed = [`file-history-rewind: ${reason}`];
|
|
@@ -9505,7 +10417,7 @@ ${outputText}
|
|
|
9505
10417
|
);
|
|
9506
10418
|
} catch (error) {
|
|
9507
10419
|
replayError = error instanceof Error ? error.message : String(error);
|
|
9508
|
-
|
|
10420
|
+
debugLogger6.warn(
|
|
9509
10421
|
"[loadUpdates] History replay failed for session %s (partial updates: %d):",
|
|
9510
10422
|
sessionId,
|
|
9511
10423
|
updates.length,
|
|
@@ -9865,7 +10777,7 @@ ${outputText}
|
|
|
9865
10777
|
newMerged.providerProtocol ?? {}
|
|
9866
10778
|
);
|
|
9867
10779
|
} catch (err) {
|
|
9868
|
-
|
|
10780
|
+
debugLogger6.warn(
|
|
9869
10781
|
`reload: reloadModelProvidersConfig failed for session ${id}: ${err}`
|
|
9870
10782
|
);
|
|
9871
10783
|
}
|
|
@@ -9875,7 +10787,7 @@ ${outputText}
|
|
|
9875
10787
|
try {
|
|
9876
10788
|
await config.switchModel(authType, newModelName);
|
|
9877
10789
|
} catch (err) {
|
|
9878
|
-
|
|
10790
|
+
debugLogger6.warn(
|
|
9879
10791
|
`reload: switchModel failed for session ${id}: ${err}`
|
|
9880
10792
|
);
|
|
9881
10793
|
}
|
|
@@ -9883,7 +10795,7 @@ ${outputText}
|
|
|
9883
10795
|
try {
|
|
9884
10796
|
await config.refreshAuth(authType);
|
|
9885
10797
|
} catch (err) {
|
|
9886
|
-
|
|
10798
|
+
debugLogger6.warn(
|
|
9887
10799
|
`reload: refreshAuth failed for session ${id}: ${err}`
|
|
9888
10800
|
);
|
|
9889
10801
|
}
|
|
@@ -9898,7 +10810,7 @@ ${outputText}
|
|
|
9898
10810
|
try {
|
|
9899
10811
|
config.setApprovalMode(newMode);
|
|
9900
10812
|
} catch (err) {
|
|
9901
|
-
|
|
10813
|
+
debugLogger6.warn(
|
|
9902
10814
|
`reload: setApprovalMode failed for session ${id}: ${err}`
|
|
9903
10815
|
);
|
|
9904
10816
|
}
|
|
@@ -9907,14 +10819,14 @@ ${outputText}
|
|
|
9907
10819
|
try {
|
|
9908
10820
|
await config.refreshHierarchicalMemory();
|
|
9909
10821
|
} catch (err) {
|
|
9910
|
-
|
|
10822
|
+
debugLogger6.warn(
|
|
9911
10823
|
`reload: refreshHierarchicalMemory failed for session ${id}: ${err}`
|
|
9912
10824
|
);
|
|
9913
10825
|
}
|
|
9914
10826
|
try {
|
|
9915
10827
|
await config.getGeminiClient()?.refreshSystemInstruction();
|
|
9916
10828
|
} catch (err) {
|
|
9917
|
-
|
|
10829
|
+
debugLogger6.warn(
|
|
9918
10830
|
`reload: refreshSystemInstruction failed for session ${id}: ${err}`
|
|
9919
10831
|
);
|
|
9920
10832
|
}
|
|
@@ -9924,7 +10836,7 @@ ${outputText}
|
|
|
9924
10836
|
for (let i = 0; i < results.length; i++) {
|
|
9925
10837
|
if (results[i].status === "rejected") {
|
|
9926
10838
|
const reason = results[i].reason;
|
|
9927
|
-
|
|
10839
|
+
debugLogger6.warn(
|
|
9928
10840
|
`Session ${sessions[i][0]} reload failed: ${reason}`
|
|
9929
10841
|
);
|
|
9930
10842
|
skipped.push(sessions[i][0]);
|
|
@@ -9942,6 +10854,21 @@ ${outputText}
|
|
|
9942
10854
|
}
|
|
9943
10855
|
}
|
|
9944
10856
|
// --- private helpers ---
|
|
10857
|
+
/**
|
|
10858
|
+
* Reverse tool channel (issue #5626, Phase 2). Build the session
|
|
10859
|
+
* `McpClientManager`'s `sendSdkMcpMessage` callback. Client-hosted
|
|
10860
|
+
* (extension) MCP servers are registered SDK-type, so the manager routes
|
|
10861
|
+
* their JSON-RPC through this callback. We forward each frame UP to the
|
|
10862
|
+
* parent serve process via the `qwen/control/client_mcp/message` ext-method;
|
|
10863
|
+
* the parent's `BridgeClient.extMethod` hands it to the per-WS-connection
|
|
10864
|
+
* `ClientMcpRegistrar`, which carries it down the daemon WS to the extension
|
|
10865
|
+
* and returns the correlated response (the `payload` field). All SDK-type
|
|
10866
|
+
* servers in this session share one callback — the `serverName` argument
|
|
10867
|
+
* routes to the right client-hosted server in the parent.
|
|
10868
|
+
*/
|
|
10869
|
+
buildClientMcpSender() {
|
|
10870
|
+
return (serverName, message) => deliverClientMcpMessage(this.connection, serverName, message);
|
|
10871
|
+
}
|
|
9945
10872
|
async newSessionConfig(cwd, mcpServers, sessionId, resume) {
|
|
9946
10873
|
this.settings = loadSettings(cwd);
|
|
9947
10874
|
const sessionMcpServers = {};
|
|
@@ -10026,6 +10953,14 @@ ${outputText}
|
|
|
10026
10953
|
if (typeof config.enableFileCheckpointing === "function") {
|
|
10027
10954
|
config.enableFileCheckpointing();
|
|
10028
10955
|
}
|
|
10956
|
+
if (typeof this.config.getRuntimeMcpServers === "function" && typeof config.addRuntimeMcpServer === "function") {
|
|
10957
|
+
const bootstrapRuntimeMcpServers = this.config.getRuntimeMcpServers();
|
|
10958
|
+
for (const [runtimeServerName, runtimeServerConfig] of Object.entries(
|
|
10959
|
+
bootstrapRuntimeMcpServers
|
|
10960
|
+
)) {
|
|
10961
|
+
config.addRuntimeMcpServer(runtimeServerName, runtimeServerConfig);
|
|
10962
|
+
}
|
|
10963
|
+
}
|
|
10029
10964
|
if (this.mcpPool !== void 0 && typeof config.setMcpTransportPool === "function") {
|
|
10030
10965
|
config.setMcpTransportPool(this.mcpPool);
|
|
10031
10966
|
}
|
|
@@ -10039,13 +10974,20 @@ ${outputText}
|
|
|
10039
10974
|
sessionId: sid,
|
|
10040
10975
|
...event
|
|
10041
10976
|
}).catch((err) => {
|
|
10042
|
-
|
|
10977
|
+
debugLogger6.debug(
|
|
10043
10978
|
`MCP budget extNotification dropped (session=${sid}, kind=${event.kind}): ${err instanceof Error ? err.message : String(err)}`
|
|
10044
10979
|
);
|
|
10045
10980
|
});
|
|
10046
10981
|
});
|
|
10047
10982
|
}
|
|
10048
|
-
await config.initialize(
|
|
10983
|
+
await config.initialize({
|
|
10984
|
+
// Reverse tool channel (issue #5626, Phase 2): bind the session
|
|
10985
|
+
// manager's SDK MCP callback to the `client_mcp/message` ext-method so a
|
|
10986
|
+
// client-hosted (extension) MCP server added at runtime reaches the
|
|
10987
|
+
// daemon WS. Servers that aren't client-hosted never use this callback
|
|
10988
|
+
// (the daemon only adds SDK-type runtime servers for client MCP).
|
|
10989
|
+
sendSdkMcpMessage: this.buildClientMcpSender()
|
|
10990
|
+
});
|
|
10049
10991
|
await config.waitForMcpReady();
|
|
10050
10992
|
const failedMcpServers = typeof config.getFailedMcpServerNames === "function" ? config.getFailedMcpServerNames() : [];
|
|
10051
10993
|
if (failedMcpServers.length > 0) {
|
|
@@ -10067,7 +11009,7 @@ ${outputText}
|
|
|
10067
11009
|
try {
|
|
10068
11010
|
await config.refreshAuth(selectedType, true);
|
|
10069
11011
|
} catch (e) {
|
|
10070
|
-
|
|
11012
|
+
debugLogger6.error(`Authentication failed: ${e}`);
|
|
10071
11013
|
throw RequestError.authRequired(
|
|
10072
11014
|
{
|
|
10073
11015
|
authMethods: pickAuthMethodsForAuthRequired(selectedType)
|
|
@@ -10082,7 +11024,20 @@ ${outputText}
|
|
|
10082
11024
|
this.connection,
|
|
10083
11025
|
config.getSessionId(),
|
|
10084
11026
|
this.clientCapabilities.fs,
|
|
10085
|
-
config.getFileSystemService()
|
|
11027
|
+
config.getFileSystemService(),
|
|
11028
|
+
{
|
|
11029
|
+
// SYNC: Mirrors ReadFileTool's default allowed local roots, including
|
|
11030
|
+
// auto-memory roots, so ACP-local read fallback follows the same policy.
|
|
11031
|
+
localReadRoots: [
|
|
11032
|
+
config.storage.getProjectTempDir(),
|
|
11033
|
+
path3.join(config.storage.getProjectDir(), "subagents"),
|
|
11034
|
+
Storage.getGlobalTempDir(),
|
|
11035
|
+
getAutoMemoryRoot(config.getTargetDir()),
|
|
11036
|
+
getUserAutoMemoryRoot(),
|
|
11037
|
+
...config.storage.getUserSkillsDirs(),
|
|
11038
|
+
Storage.getUserExtensionsDir()
|
|
11039
|
+
]
|
|
11040
|
+
}
|
|
10086
11041
|
);
|
|
10087
11042
|
config.setFileSystemService(acpFileSystemService);
|
|
10088
11043
|
}
|
|
@@ -10120,7 +11075,7 @@ ${outputText}
|
|
|
10120
11075
|
buildAvailableModels(config) {
|
|
10121
11076
|
const rawCurrentModelId = (config.getModel() || this.config.getModel() || "").trim();
|
|
10122
11077
|
const currentAuthType = config.getAuthType();
|
|
10123
|
-
const allConfiguredModels = config.getAllConfiguredModels();
|
|
11078
|
+
const allConfiguredModels = config.getAllConfiguredModels().filter(isMainSelectableModel);
|
|
10124
11079
|
const activeRuntimeSnapshot = config.getActiveRuntimeModelSnapshot?.();
|
|
10125
11080
|
const currentModelId = activeRuntimeSnapshot ? formatAcpModelId(
|
|
10126
11081
|
activeRuntimeSnapshot.id,
|
|
@@ -10156,7 +11111,7 @@ ${outputText}
|
|
|
10156
11111
|
}
|
|
10157
11112
|
buildConfigOptions(config) {
|
|
10158
11113
|
const currentApprovalMode = config.getApprovalMode();
|
|
10159
|
-
const allConfiguredModels = config.getAllConfiguredModels();
|
|
11114
|
+
const allConfiguredModels = config.getAllConfiguredModels().filter(isMainSelectableModel);
|
|
10160
11115
|
const rawCurrentModelId = (config.getModel() || "").trim();
|
|
10161
11116
|
const currentAuthType = config.getAuthType?.();
|
|
10162
11117
|
const activeRuntimeSnapshot = config.getActiveRuntimeModelSnapshot?.();
|
|
@@ -10202,6 +11157,10 @@ ${outputText}
|
|
|
10202
11157
|
return authType ? formatAcpModelId(baseModelId, authType) : baseModelId;
|
|
10203
11158
|
}
|
|
10204
11159
|
};
|
|
11160
|
+
function isMainSelectableModel(model) {
|
|
11161
|
+
return model.fastOnly !== true && model.voiceOnly !== true;
|
|
11162
|
+
}
|
|
11163
|
+
__name(isMainSelectableModel, "isMainSelectableModel");
|
|
10205
11164
|
function diffSettingsKeys(oldMerged, newMerged) {
|
|
10206
11165
|
const changed = /* @__PURE__ */ new Set();
|
|
10207
11166
|
const allKeys = /* @__PURE__ */ new Set([
|
|
@@ -10220,6 +11179,7 @@ export {
|
|
|
10220
11179
|
AUTH_PREFLIGHT_ENV_KEYS,
|
|
10221
11180
|
AUTH_PREFLIGHT_WAIVED_AUTH_TYPES,
|
|
10222
11181
|
createWorkspaceMcpBudget,
|
|
11182
|
+
deliverClientMcpMessage,
|
|
10223
11183
|
extractFilesFromTarGz,
|
|
10224
11184
|
fetchAllowedGitHub,
|
|
10225
11185
|
normalizeCoreSettingValue,
|