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