@qwen-code/qwen-code 0.19.2 → 0.19.3-nightly.20260701.a974594d7
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/extension-creator/SKILL.md +357 -0
- package/bundled/loop/SKILL.md +34 -10
- package/bundled/loop/SKILL.test.ts +52 -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 +864 -0
- package/bundled/loop/loop-tick-resolver.ts +416 -0
- package/bundled/qc-helper/SKILL.md +26 -19
- package/bundled/qc-helper/docs/configuration/auth.md +11 -11
- package/bundled/qc-helper/docs/configuration/model-providers.md +13 -13
- package/bundled/qc-helper/docs/configuration/settings.md +77 -75
- package/bundled/qc-helper/docs/features/channels/plugins.md +1 -1
- package/bundled/qc-helper/docs/features/commands.md +108 -77
- package/bundled/qc-helper/docs/features/hooks.md +6 -1
- package/bundled/qc-helper/docs/features/memory.md +53 -1
- 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 +24 -2
- package/bundled/qc-helper/docs/support/tos-privacy.md +16 -3
- package/bundled/qc-helper/docs/support/troubleshooting.md +6 -0
- package/chunks/{MaxSizedBox-7RERNLFK.js → MaxSizedBox-UZHJ73JB.js} +29 -27
- package/chunks/{StandaloneSessionPicker-S22XBSN6.js → StandaloneSessionPicker-GGUIZWCE.js} +42 -37
- package/chunks/{acpAgent-4MCETHF2.js → acpAgent-QYZFICEZ.js} +1292 -332
- package/chunks/{agent-K3ENVYSB.js → agent-2EN7YNSW.js} +24 -23
- package/chunks/{agent-headless-HO3V3A3A.js → agent-headless-SLTEZ5LV.js} +24 -23
- package/chunks/{anthropicContentGenerator-E3TQ5XJJ.js → anthropicContentGenerator-CA3JTAMO.js} +33 -12
- package/chunks/{artifact-tool-QA4KRO3B.js → artifact-tool-RM7WINJO.js} +3 -3
- package/chunks/{askUserQuestion-HMWXW6VN.js → askUserQuestion-J3P6QAJ7.js} +2 -2
- package/chunks/{bridge-4LV2HBH2.js → bridge-HZVAUTMX.js} +29 -28
- package/chunks/{ca-7WC7ELX6.js → ca-RYOETBUW.js} +10 -9
- package/chunks/{chunk-JXQ23SNT.js → chunk-2JKEQWDZ.js} +623 -78
- package/chunks/chunk-32MCHZQC.js +73 -0
- package/chunks/{chunk-PXFFKNCO.js → chunk-35FH347N.js} +5308 -2500
- package/chunks/{chunk-5JZLXFPD.js → chunk-3O2BJMFW.js} +11 -11
- package/chunks/{chunk-2JD55C6G.js → chunk-3TK7FSCP.js} +2 -2
- package/chunks/{chunk-USGMPVOY.js → chunk-4722VFFU.js} +19 -10
- package/chunks/chunk-6FIWYVN4.js +4932 -0
- package/chunks/{chunk-I2RFAW6Q.js → chunk-7SP275SH.js} +7 -5
- package/chunks/{chunk-5R4SBCX6.js → chunk-AIBPG5MB.js} +3 -3
- package/chunks/{chunk-C4K3FEQ2.js → chunk-AVQDCKNF.js} +124 -21
- package/chunks/{chunk-OTBK43JR.js → chunk-B236XPGF.js} +1 -1
- package/chunks/{chunk-WA5OJGFL.js → chunk-B5MPZI4K.js} +458 -125
- package/chunks/{chunk-5DWXLGOM.js → chunk-BJBWRCSK.js} +1 -1
- package/chunks/{chunk-4W4T77MO.js → chunk-BLAE2TCO.js} +109 -6
- package/chunks/chunk-BMFMJINR.js +45 -0
- package/chunks/{chunk-T2VDEFUU.js → chunk-BZHLWC6T.js} +2 -2
- package/chunks/{chunk-XU5P23GN.js → chunk-CA4ML3VH.js} +3 -3
- package/chunks/{chunk-WGAEWQTF.js → chunk-DBR7PF4B.js} +1 -2
- package/chunks/{chunk-S566HKUS.js → chunk-DHXWNSJA.js} +15 -1
- package/chunks/{chunk-LLFFKXZZ.js → chunk-DJ25MIU2.js} +167 -43
- package/chunks/{chunk-KBXLIVWQ.js → chunk-DX3TKXNT.js} +2 -2
- package/chunks/{chunk-OIC6S73E.js → chunk-E5I6YSNO.js} +1 -1
- package/chunks/{chunk-TXVGVZYR.js → chunk-ESVSWG6Z.js} +3 -3
- package/chunks/{chunk-EX4W6ZYG.js → chunk-EUURTJY7.js} +1 -1
- package/chunks/{chunk-QBZ22PQX.js → chunk-EWALXUAD.js} +3 -1
- package/chunks/{chunk-F6XNTYNE.js → chunk-FQDMX5IL.js} +30 -307
- package/chunks/{chunk-3QFE4OTE.js → chunk-FR3II765.js} +4 -4
- package/chunks/{chunk-VYFMN7C6.js → chunk-FV4SQ54H.js} +382 -55
- package/chunks/{chunk-ZD3NW5KL.js → chunk-FZY7NH37.js} +63 -9
- package/chunks/{chunk-CI5O6SQL.js → chunk-G525INVW.js} +4 -4
- package/chunks/{chunk-CGOMICBE.js → chunk-GDIG7TMG.js} +34 -1
- package/chunks/{chunk-R2BXK7SX.js → chunk-GHOTR7HL.js} +6 -1
- package/chunks/{chunk-TZM5JIAX.js → chunk-GZKQ36JS.js} +10 -7
- package/chunks/{chunk-BS64PXY3.js → chunk-HIYUAXBG.js} +1 -1
- package/chunks/{chunk-TMBPDHXD.js → chunk-HTWHAN44.js} +1 -1
- package/chunks/{chunk-EL5IHQON.js → chunk-HUGAEYZR.js} +6 -27
- package/chunks/{chunk-2BIF2NVV.js → chunk-HWG4XLD7.js} +2 -2
- package/chunks/{chunk-I5T6PIBQ.js → chunk-I4MXURND.js} +1076 -954
- package/chunks/{chunk-JTYRZODB.js → chunk-IBEM64M4.js} +39 -11
- package/chunks/{chunk-XKBP7M7A.js → chunk-IKO44NAT.js} +149 -31
- package/chunks/{chunk-2BAQ3ACS.js → chunk-JERW36RG.js} +404 -4
- package/chunks/{chunk-TOEGRAVX.js → chunk-JV7A66DR.js} +28 -16
- package/chunks/{chunk-3RJ3ZPPR.js → chunk-KKQWC6WS.js} +7 -7
- package/chunks/{chunk-EXMX5YHG.js → chunk-KXVVXAAC.js} +1 -1
- package/chunks/{chunk-Z7XXFPGP.js → chunk-LDWTYE33.js} +1 -1
- package/chunks/{chunk-X6EDWN44.js → chunk-LWGLBVWA.js} +1 -1
- package/chunks/{chunk-NYOHZYHM.js → chunk-LXEW6BHF.js} +45 -3
- package/chunks/{chunk-BGUDAULQ.js → chunk-LXGWLOCM.js} +5 -5
- package/chunks/{chunk-ZUAXI2OA.js → chunk-M2U5NM24.js} +188 -11366
- package/chunks/{chunk-BNXANYLH.js → chunk-MOUXUCXQ.js} +81 -1
- package/chunks/{chunk-FPACXT7T.js → chunk-MTJVHQDP.js} +534 -164
- package/chunks/{chunk-UAARJR2G.js → chunk-N4V4VBP6.js} +2 -2
- package/chunks/chunk-NIOPRZNN.js +27 -0
- package/chunks/{chunk-2TJ3YN6Q.js → chunk-NOYV2NXI.js} +517 -490
- package/chunks/{chunk-PRYMDRBV.js → chunk-NPQWQYU6.js} +1 -1
- package/chunks/{chunk-J6AW7O5D.js → chunk-NVTRORSK.js} +2 -2
- package/chunks/{chunk-PF523XJX.js → chunk-OE5J6UA7.js} +1 -1
- package/chunks/{chunk-RS4HRC75.js → chunk-OEY3YAL2.js} +99 -8
- package/chunks/{chunk-R36VMVEP.js → chunk-PLQTLKMR.js} +5 -5
- package/chunks/{chunk-UN3C6GOR.js → chunk-PPFWEV7G.js} +10 -108
- package/chunks/chunk-Q3JOGB4U.js +481 -0
- package/chunks/{chunk-HG6YKEBI.js → chunk-Q7TDHJ2G.js} +192 -30
- package/chunks/{chunk-EW6C27XX.js → chunk-QHHNFG22.js} +9 -3
- package/chunks/chunk-QYUE6W3T.js +123 -0
- package/chunks/{chunk-GZCJS5WH.js → chunk-SJBIFVD2.js} +2 -2
- package/chunks/{chunk-3UALQ56H.js → chunk-T42ACHFI.js} +1 -1
- package/chunks/chunk-T44AF4I6.js +690 -0
- package/chunks/{chunk-DBZUXSSV.js → chunk-TMILTLP3.js} +6 -6
- package/chunks/{chunk-EEEZIVFJ.js → chunk-TOAUDIS7.js} +1 -1
- package/chunks/chunk-UAKXW3DN.js +30 -0
- package/chunks/{chunk-VQRYPFCV.js → chunk-V5YJAUHW.js} +10 -460
- package/chunks/{chunk-D5E5TMYH.js → chunk-VHRREANT.js} +16 -2
- package/chunks/{chunk-5RVSM6NW.js → chunk-VN4CKYUW.js} +1 -1
- package/chunks/chunk-VXGPYF7U.js +37 -0
- package/chunks/{chunk-UAARVVBI.js → chunk-WGY3GIPP.js} +1 -1
- package/chunks/{chunk-GYZQA2FO.js → chunk-WJAY4WXR.js} +1 -1
- package/chunks/chunk-XKSCTXPK.js +167 -0
- package/chunks/chunk-YCEHOJZJ.js +840 -0
- package/chunks/{chunk-BG4BIP7F.js → chunk-YCRTJYSQ.js} +4 -2
- package/chunks/{chunk-CGEGHF2C.js → chunk-YPCPZSZB.js} +1 -1
- package/chunks/chunk-YTFBURQD.js +313 -0
- package/chunks/{chunk-HGNSRCRB.js → chunk-YXK4GPYO.js} +1 -1
- package/chunks/{chunk-OQHQHJRK.js → chunk-Z34RYLRO.js} +151 -4775
- package/chunks/chunk-ZGSV5ORL.js +11273 -0
- package/chunks/{computer-use-6BBRNT6S.js → computer-use-FCNWHVPW.js} +103 -49
- package/chunks/{contextCommand-UJAJ6N57.js → contextCommand-FVFMZZEU.js} +26 -25
- package/chunks/{cron-create-4KQE6YLH.js → cron-create-IY5NMIHQ.js} +4 -4
- package/chunks/{cron-delete-THME66UP.js → cron-delete-OLNEQWKO.js} +6 -6
- package/chunks/{cron-list-7A54SJ5N.js → cron-list-YAUAUV24.js} +5 -5
- package/chunks/{daemon-status-provider-CSL3BUOY.js → daemon-status-provider-42JEXENZ.js} +32 -32
- package/chunks/{de-ACHALDED.js → de-HB5NJXGB.js} +10 -9
- package/chunks/{dist-BD27IA3L.js → dist-4FCNM6KJ.js} +2 -2
- package/chunks/{dist-OD2FY543.js → dist-K4XZILIO.js} +1 -1
- package/chunks/{dist-BOOH5JWN.js → dist-SU6L7ZNJ.js} +114 -9
- package/chunks/{dist-QHJRBCMP.js → dist-UKPWQJCL.js} +44 -2
- package/chunks/{dist-QFFZWACU.js → dist-VD7HXTML.js} +20 -9
- package/chunks/{earlyInputCapture-X6KMCSHD.js → earlyInputCapture-FFFEC6P4.js} +25 -24
- package/chunks/{edit-7J6WXARF.js → edit-32ILSGZY.js} +72 -30
- package/chunks/{en-NCUBOJOY.js → en-U3LWGSFX.js} +19 -9
- package/chunks/{enter-worktree-KULCKJPJ.js → enter-worktree-XWZKIN6A.js} +24 -23
- package/chunks/{enterPlanMode-H2CIOLMH.js → enterPlanMode-LPFFY7BJ.js} +24 -23
- package/chunks/{errors-6G2JYR54.js → errors-UXBPYPI2.js} +26 -25
- package/chunks/{exit-worktree-ECBWBVC5.js → exit-worktree-IBS4UIU7.js} +24 -23
- package/chunks/{exitPlanMode-DQV56GTW.js → exitPlanMode-JA72YHII.js} +24 -23
- package/chunks/{fast-path-35JJFS3C.js → fast-path-7UU67Y2W.js} +30 -13
- package/chunks/{fast-path-settings-TSVI53EB.js → fast-path-settings-UQLRFDQP.js} +3 -2
- package/chunks/{fr-GVKZ27MF.js → fr-XY3NQFCM.js} +10 -9
- package/chunks/{gemini-NDTG7WAX.js → gemini-GMVKJNMX.js} +211 -105
- package/chunks/{geminiContentGenerator-XTG6SFLA.js → geminiContentGenerator-7NIFF2L4.js} +4 -4
- package/chunks/{glob-SJ2JBZK4.js → glob-4NJCW5HI.js} +24 -23
- package/chunks/{grep-BRMWFEWR.js → grep-PAIMLQEL.js} +24 -23
- package/chunks/{initializer-P5AXF7PV.js → initializer-R6KU4VYM.js} +31 -28
- package/chunks/{ja-FKZ2WZ4D.js → ja-J7YAG7SA.js} +10 -9
- package/chunks/{keychain-token-storage-CSYMNRPP.js → keychain-token-storage-GPAKRDKQ.js} +2 -2
- package/chunks/{list-6COK34BP.js → list-H7LXAO64.js} +32 -29
- package/chunks/{loadedSettingsAdapter-XIUTN245.js → loadedSettingsAdapter-H27JMTLU.js} +30 -27
- package/chunks/{loop-wakeup-33GPXJLA.js → loop-wakeup-Y3OIRQFM.js} +7 -7
- package/chunks/{ls-BYQY7RVV.js → ls-TVJEYS7O.js} +4 -4
- package/chunks/{lsp-X54RO4IV.js → lsp-JZSDVR3F.js} +2 -2
- package/chunks/{monitor-DH7KFSJV.js → monitor-JSIHDPXP.js} +24 -23
- package/chunks/nonInteractiveCli-Y4CHOETG.js +100 -0
- package/chunks/{notebook-edit-D4R4ZLMA.js → notebook-edit-SHRNL2UM.js} +52 -23
- package/chunks/{openaiContentGenerator-F74IPQ6E.js → openaiContentGenerator-ORZZ7VFO.js} +13 -12
- package/chunks/{pt-SY5NCFGR.js → pt-6RTAA6SS.js} +10 -9
- package/chunks/{qwenContentGenerator-ATTK5P4D.js → qwenContentGenerator-H643PL2G.js} +26 -25
- package/chunks/{qwenOAuth2-K4AASPCV.js → qwenOAuth2-OI2B7PES.js} +5 -4
- package/chunks/{read-file-767G23OY.js → read-file-5PRNPTTY.js} +8 -8
- package/chunks/{read-mcp-resource-I5HCZJ6X.js → read-mcp-resource-QTXGKTEV.js} +2 -2
- package/chunks/{ripGrep-4OMMGSWX.js → ripGrep-JIKZMABJ.js} +24 -23
- package/chunks/{ru-FWNXJG4F.js → ru-AUBGJ2C4.js} +10 -9
- package/chunks/{run-qwen-serve-REKSTYRA.js → run-qwen-serve-PQV7QOHZ.js} +284 -52
- package/chunks/{scheduler-UNTFDQLG.js → scheduler-OS3R5MGU.js} +24 -23
- package/chunks/{send-message-2DAFXEYP.js → send-message-DKGHWSTF.js} +2 -2
- package/chunks/{serve-KZ56QB5S.js → serve-LVYZDKCZ.js} +31 -29
- package/chunks/{server-BV5TZUMU.js → server-P4YIH35I.js} +11458 -8447
- package/chunks/{session-OIBXFSGD.js → session-DE4TZBKH.js} +195 -58
- package/chunks/{settings-WJTCBIRA.js → settings-DFTYMJYF.js} +30 -27
- package/chunks/{shell-WC52IBHW.js → shell-3GNQLNV7.js} +24 -23
- package/chunks/{skill-U7F7JR4M.js → skill-NCNG4KKZ.js} +38 -11
- package/chunks/{spawnChannel-EI2E5BCW.js → spawnChannel-DHRIMDVL.js} +26 -25
- package/chunks/{src-4F6FBZFI.js → src-PNZYGM3R.js} +104 -28
- package/chunks/{startInteractiveUI-SFRVB3CE.js → startInteractiveUI-P6GZBFQW.js} +2817 -2500
- package/chunks/{syntheticOutput-PPS3HDVA.js → syntheticOutput-X7L5HWLN.js} +3 -3
- package/chunks/{task-create-OUYCA553.js → task-create-AWEUI4GI.js} +6 -6
- package/chunks/{task-list-WAX7I2IA.js → task-list-KZ3VJLDU.js} +5 -5
- package/chunks/{task-stop-CAIL6FNH.js → task-stop-GUNOZKJ5.js} +2 -2
- package/chunks/{task-update-AHEO5OUT.js → task-update-Y56GP6DU.js} +6 -6
- package/chunks/{team-create-3WC7CIIW.js → team-create-6JMW3KQK.js} +24 -23
- package/chunks/{team-delete-HGHFG23B.js → team-delete-6AFRGDJY.js} +5 -5
- package/chunks/{theme-manager-DICFCT3P.js → theme-manager-FEHQO22G.js} +25 -24
- package/chunks/{todoWrite-IFBMN3FH.js → todoWrite-JRS6NOEU.js} +4 -4
- package/chunks/{tool-search-L5K7HLER.js → tool-search-IXAEKS7J.js} +101 -16
- package/chunks/trustedFolders-SJRXNHZE.js +76 -0
- package/chunks/types-QX5C3CHJ.js +12 -0
- package/chunks/{validateNonInterActiveAuth-GCH4Z6KI.js → validateNonInterActiveAuth-HCH7OBDH.js} +56 -51
- package/chunks/{web-fetch-B5IIQ5TT.js → web-fetch-RELNL37A.js} +29 -15
- package/chunks/{workflow-OB3AND7I.js → workflow-M2COBI7R.js} +38 -32
- package/chunks/{workspace-providers-status-ZIR5EPOO.js → workspace-providers-status-GOCWYDMK.js} +32 -29
- package/chunks/workspace-service-34C7SUBU.js +75 -0
- package/chunks/{write-file-SEXBNDZM.js → write-file-E52WRXZL.js} +67 -31
- package/chunks/{zh-RBEGQVQT.js → zh-F5T4WWZO.js} +19 -9
- package/chunks/{zh-TW-QGAVRXT3.js → zh-TW-LKLBTZHE.js} +19 -9
- package/cli-entry.js +20 -9
- package/cli.js +6 -6
- package/locales/ca.js +15 -13
- package/locales/de.js +15 -13
- package/locales/en.js +31 -13
- package/locales/fr.js +16 -14
- package/locales/ja.js +15 -14
- package/locales/pt.js +15 -13
- package/locales/ru.js +15 -13
- package/locales/zh-TW.js +28 -12
- package/locales/zh.js +28 -12
- package/node_modules/@qwen-code/audio-capture/dist/index.d.ts +35 -0
- package/node_modules/@qwen-code/audio-capture/dist/index.js +48 -0
- package/node_modules/@qwen-code/audio-capture/dist/index.js.map +1 -0
- package/node_modules/@qwen-code/audio-capture/dist/platform.d.ts +7 -0
- package/node_modules/@qwen-code/audio-capture/dist/platform.js +18 -0
- package/node_modules/@qwen-code/audio-capture/dist/platform.js.map +1 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/LICENSE +21 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/README.md +58 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/SECURITY.md +5 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/bin.js +84 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/build-test.js +19 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/index.js +6 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/node-gyp-build.js +207 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/optional.js +7 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/package.json +43 -0
- package/node_modules/@qwen-code/audio-capture/package.json +30 -0
- 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/linux-arm64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/linux-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 +18 -6
- package/patches/chrome-devtools-mcp+1.4.0.patch +31 -0
- package/postinstall.js +50 -0
- package/web-shell/assets/{arc-D_ae_Y9k.js → arc-C2DILvUl.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-BMLRjppa.js → architectureDiagram-3BPJPVTR-b8gZ2cNw.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-qVDQC8KL.js → blockDiagram-GPEHLZMM-BuRpghMj.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-CxWS6MBd.js → c4Diagram-AAUBKEIU-2ItdhFmE.js} +1 -1
- package/web-shell/assets/channel-eRvn2UA3.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-BtNuY5Yp.js → chunk-2J33WTMH-B1QXa0q3.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-BU-x368d.js → chunk-4BX2VUAB-DXfhKlNq.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-DRgTLvBu.js → chunk-55IACEB6-ClSNjxLo.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-D2aElv7q.js → chunk-727SXJPM-Dsh2wisB.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-BwHCMRQ0.js → chunk-AQP2D5EJ-C1TkY5AR.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-BMyPH-ku.js → chunk-FMBD7UC4-CGtN9XR6.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-BNKTstUf.js → chunk-ND2GUHAM-BHiPi1SS.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-gD3vsFId.js → chunk-QZHKN3VN-CzG3I5o1.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-YZNwy1rx.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-YZNwy1rx.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-BG9FAsXE.js → cose-bilkent-S5V4N54A-EgrIoH_K.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-Cy1sR7om.js → dagre-BM42HDAG-BUnpdX8S.js} +1 -1
- package/web-shell/assets/default-aEtf0G7-.svg +1 -0
- package/web-shell/assets/{diagram-2AECGRRQ-Bxi1I_Vu.js → diagram-2AECGRRQ-BlLUCVYu.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-BGCJjO6y.js → diagram-5GNKFQAL-FG-enmjQ.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-CMTeLXp7.js → diagram-KO2AKTUF-BlzmTONk.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-BN1wyAGh.js → diagram-LMA3HP47-CoBQ8mIx.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-UYSG_zcp.js → diagram-OG6HWLK6-DvxV6sX8.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-DqXL08lw.js → erDiagram-TEJ5UH35-Jq1tpAoj.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-BPJ9SFji.js → flowDiagram-I6XJVG4X-CbtYOry5.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-BEd9Pnms.js → ganttDiagram-6RSMTGT7-ZEn2VEKR.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-DrNRH3oi.js → gitGraphDiagram-PVQCEYII-BZh-LkhJ.js} +1 -1
- package/web-shell/assets/index-5FhE0ORO.css +5 -0
- package/web-shell/assets/index-CDTGmqVm.js +720 -0
- package/web-shell/assets/{infoDiagram-5YYISTIA-CFYBrkVI.js → infoDiagram-5YYISTIA-B9HuZygK.js} +1 -1
- package/web-shell/assets/insert-DmRbh9Rv.svg +1 -0
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-Dxsddth7.js → ishikawaDiagram-YF4QCWOH-D76cNpYG.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-DxaDK80t.js → journeyDiagram-JHISSGLW-6bGo7P1t.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-Q0jQCOct.js → kanban-definition-UN3LZRKU-DuYyRBvB.js} +1 -1
- package/web-shell/assets/{linear-DGGM2kcP.js → linear-CpdSW1BC.js} +1 -1
- package/web-shell/assets/{mermaid.core-ByoxnWut.js → mermaid.core-s5xbZMMj.js} +5 -5
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-C0ZseHsi.js → mindmap-definition-RKZ34NQL-go01fZ2y.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-MQXNhl5A.js → pieDiagram-4H26LBE5-BfuZiEHO.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-DIR4ee2A.js → quadrantDiagram-W4KKPZXB-DsoINqV6.js} +1 -1
- package/web-shell/assets/queue-DWpnpFgW.svg +1 -0
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-BTR0UdYQ.js → requirementDiagram-4Y6WPE33-B7FU4LiU.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-sfnhMELN.js → sankeyDiagram-5OEKKPKP-BEDjFnGB.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-DXczHOHH.js → sequenceDiagram-3UESZ5HK-gzmmL5tK.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-DdE_KQFH.js → stateDiagram-AJRCARHV-BtHD4IN_.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-DFy6gLDd.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-DIc61uE8.js → timeline-definition-PNZ67QCA-T_8gmMCf.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-BF7ODmxB.js → vennDiagram-CIIHVFJN-DdjuqnjO.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-3pc1MEaw.js → wardley-L42UT6IY-Cgtrvb37.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-9jnPcZuP.js → wardleyDiagram-YWT4CUSO-BW3f3-yW.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-DcthaZK6.js → xychartDiagram-2RQKCTM6-BUS9IKkc.js} +1 -1
- package/web-shell/index.html +2 -2
- package/chunks/chunk-B6HQGTYR.js +0 -410
- package/chunks/chunk-RKIZXJMB.js +0 -13
- package/chunks/headlessSafetyWarnings-LJ6FJX77.js +0 -57
- package/chunks/nonInteractiveCli-7575LBXP.js +0 -91
- package/chunks/workspace-service-OM5D4OI2.js +0 -60
- package/web-shell/assets/channel-DYPNiTww.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-p_g25Tni.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-p_g25Tni.js +0 -1
- package/web-shell/assets/index-CpOXWDPh.js +0 -714
- package/web-shell/assets/index-oVK7qhre.css +0 -5
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BHUSEndU.js +0 -1
|
@@ -3,8 +3,13 @@
|
|
|
3
3
|
import {
|
|
4
4
|
classifyApiError,
|
|
5
5
|
prefixMidTurnUserMessageParts
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import
|
|
6
|
+
} from "./chunk-DJ25MIU2.js";
|
|
7
|
+
import {
|
|
8
|
+
EXTENSION_CONTEXT_BUDGET,
|
|
9
|
+
buildExtensionMentionContext,
|
|
10
|
+
matchExtensionByRef,
|
|
11
|
+
parseExtensionRef
|
|
12
|
+
} from "./chunk-YCEHOJZJ.js";
|
|
8
13
|
import {
|
|
9
14
|
HistoryReplayer,
|
|
10
15
|
MessageEmitter,
|
|
@@ -12,90 +17,96 @@ import {
|
|
|
12
17
|
ToolCallEmitter,
|
|
13
18
|
getAvailableCommands,
|
|
14
19
|
handleSlashCommand,
|
|
20
|
+
insertAfterFunctionResponses,
|
|
15
21
|
isSlashCommand,
|
|
16
22
|
normalizePartList
|
|
17
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-NOYV2NXI.js";
|
|
24
|
+
import {
|
|
25
|
+
collectContextData,
|
|
26
|
+
formatContextUsageText
|
|
27
|
+
} from "./chunk-GZKQ36JS.js";
|
|
18
28
|
import {
|
|
19
29
|
appEvents,
|
|
20
30
|
buildDisabledSkillNamesProvider,
|
|
21
31
|
loadCliConfig,
|
|
22
32
|
normalizeDisabledToolList
|
|
23
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-ZGSV5ORL.js";
|
|
24
34
|
import {
|
|
25
35
|
getCommandSubcommandNames
|
|
26
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-BLAE2TCO.js";
|
|
37
|
+
import "./chunk-32MCHZQC.js";
|
|
27
38
|
import {
|
|
28
39
|
getEffectiveSupportedModes
|
|
29
|
-
} from "./chunk-
|
|
30
|
-
import "./chunk-MRO43B25.js";
|
|
31
|
-
import "./chunk-UAARJR2G.js";
|
|
32
|
-
import "./chunk-JZSY4WP3.js";
|
|
33
|
-
import {
|
|
34
|
-
collectContextData,
|
|
35
|
-
formatContextUsageText
|
|
36
|
-
} from "./chunk-TZM5JIAX.js";
|
|
40
|
+
} from "./chunk-IBEM64M4.js";
|
|
37
41
|
import {
|
|
38
42
|
isTerminalGoalStatusKind
|
|
39
43
|
} from "./chunk-KSPVDFE4.js";
|
|
44
|
+
import "./chunk-MRO43B25.js";
|
|
45
|
+
import "./chunk-VXGPYF7U.js";
|
|
46
|
+
import "./chunk-N4V4VBP6.js";
|
|
40
47
|
import "./chunk-KYMBIKIW.js";
|
|
41
|
-
import "./chunk-
|
|
48
|
+
import "./chunk-JZSY4WP3.js";
|
|
49
|
+
import "./chunk-HTWHAN44.js";
|
|
50
|
+
import "./chunk-7SP275SH.js";
|
|
42
51
|
import "./chunk-3GONHQOA.js";
|
|
43
|
-
import "./chunk-
|
|
44
|
-
import "./chunk-
|
|
45
|
-
import "./chunk-
|
|
46
|
-
import "./chunk-TMBPDHXD.js";
|
|
52
|
+
import "./chunk-AIBPG5MB.js";
|
|
53
|
+
import "./chunk-LDWTYE33.js";
|
|
54
|
+
import "./chunk-2JKEQWDZ.js";
|
|
47
55
|
import {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
import "./chunk-NYOHZYHM.js";
|
|
55
|
-
import "./chunk-CGOMICBE.js";
|
|
56
|
-
import "./chunk-RKIZXJMB.js";
|
|
57
|
-
import "./chunk-CI5O6SQL.js";
|
|
56
|
+
isValidServerName
|
|
57
|
+
} from "./chunk-UAKXW3DN.js";
|
|
58
|
+
import "./chunk-Q3JOGB4U.js";
|
|
59
|
+
import "./chunk-M2U5NM24.js";
|
|
60
|
+
import "./chunk-GDIG7TMG.js";
|
|
61
|
+
import "./chunk-G525INVW.js";
|
|
58
62
|
import "./chunk-KHDZHZMH.js";
|
|
59
|
-
import {
|
|
60
|
-
MID_TURN_QUEUE_DRAIN_METHOD
|
|
61
|
-
} from "./chunk-QBZ22PQX.js";
|
|
62
63
|
import {
|
|
63
64
|
formatAcpModelId,
|
|
64
65
|
parseAcpBaseModelId,
|
|
65
66
|
parseAcpModelOption,
|
|
66
67
|
sanitizeProviderBaseUrl
|
|
67
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-OEY3YAL2.js";
|
|
69
|
+
import {
|
|
70
|
+
PERMISSION_RULE_TYPES,
|
|
71
|
+
PermissionRulesValidationError,
|
|
72
|
+
buildPermissionSettings,
|
|
73
|
+
normalizePermissionRules,
|
|
74
|
+
readPermissionRuleSet
|
|
75
|
+
} from "./chunk-DBR7PF4B.js";
|
|
76
|
+
import "./chunk-LXEW6BHF.js";
|
|
77
|
+
import "./chunk-NIOPRZNN.js";
|
|
68
78
|
import {
|
|
69
79
|
NOT_CURRENTLY_GENERATING_CANCEL_MESSAGE
|
|
70
|
-
} from "./chunk-
|
|
71
|
-
import "./chunk-
|
|
80
|
+
} from "./chunk-DHXWNSJA.js";
|
|
81
|
+
import "./chunk-HIYUAXBG.js";
|
|
82
|
+
import {
|
|
83
|
+
CLIENT_MCP_OVER_WS_CONFIG_FLAG,
|
|
84
|
+
MID_TURN_QUEUE_DRAIN_METHOD
|
|
85
|
+
} from "./chunk-EWALXUAD.js";
|
|
72
86
|
import "./chunk-SEZC2725.js";
|
|
73
|
-
import "./chunk-
|
|
74
|
-
import "./chunk-
|
|
87
|
+
import "./chunk-TMILTLP3.js";
|
|
88
|
+
import "./chunk-BJBWRCSK.js";
|
|
75
89
|
import "./chunk-ALX6BUYW.js";
|
|
76
|
-
import "./chunk-5DWXLGOM.js";
|
|
77
90
|
import {
|
|
78
91
|
createLoadedSettingsAdapter
|
|
79
|
-
} from "./chunk-
|
|
92
|
+
} from "./chunk-VN4CKYUW.js";
|
|
80
93
|
import {
|
|
81
94
|
OUTPUT_LANGUAGE_AUTO,
|
|
82
95
|
getOutputLanguageFilePath,
|
|
83
96
|
getPersistScopeForModelSelection,
|
|
84
97
|
isAutoLanguage,
|
|
85
|
-
isWorkspaceTrusted,
|
|
86
98
|
loadSettings,
|
|
87
99
|
reloadEnvironment,
|
|
88
100
|
resolveOutputLanguage,
|
|
89
101
|
updateOutputLanguageFile,
|
|
90
102
|
writeOutputLanguageAndRegisterPath
|
|
91
|
-
} from "./chunk-
|
|
103
|
+
} from "./chunk-6FIWYVN4.js";
|
|
92
104
|
import {
|
|
93
105
|
SUPPORTED_LANGUAGES,
|
|
94
106
|
getCurrentLanguage,
|
|
95
107
|
setLanguageAsync
|
|
96
|
-
} from "./chunk-
|
|
108
|
+
} from "./chunk-3O2BJMFW.js";
|
|
97
109
|
import "./chunk-TLRYABYP.js";
|
|
98
|
-
import "./chunk-Y6Z2O3WR.js";
|
|
99
110
|
import {
|
|
100
111
|
AgentSideConnection,
|
|
101
112
|
PROTOCOL_VERSION,
|
|
@@ -109,8 +120,15 @@ import {
|
|
|
109
120
|
SERVE_STATUS_EXT_METHODS,
|
|
110
121
|
STATUS_SCHEMA_VERSION,
|
|
111
122
|
mapDomainErrorToErrorKind
|
|
112
|
-
} from "./chunk-
|
|
113
|
-
import "./chunk-
|
|
123
|
+
} from "./chunk-VHRREANT.js";
|
|
124
|
+
import "./chunk-PPFWEV7G.js";
|
|
125
|
+
import {
|
|
126
|
+
isFolderTrustEnabled,
|
|
127
|
+
isWorkspaceTrusted,
|
|
128
|
+
loadTrustedFolders
|
|
129
|
+
} from "./chunk-Z34RYLRO.js";
|
|
130
|
+
import "./chunk-Y6Z2O3WR.js";
|
|
131
|
+
import "./chunk-QYUE6W3T.js";
|
|
114
132
|
import {
|
|
115
133
|
ALL_PROVIDERS,
|
|
116
134
|
APPROVAL_MODES,
|
|
@@ -119,6 +137,7 @@ import {
|
|
|
119
137
|
ExtensionManager,
|
|
120
138
|
FORK_SUBAGENT_TYPE,
|
|
121
139
|
InvalidMcpConfigError,
|
|
140
|
+
LoopTickResolver,
|
|
122
141
|
MAX_TRANSCRIPT_MESSAGES,
|
|
123
142
|
MCPOAuthProvider,
|
|
124
143
|
MCPOAuthTokenStorage,
|
|
@@ -127,9 +146,11 @@ import {
|
|
|
127
146
|
McpBudgetWouldExceedError,
|
|
128
147
|
McpServerSpawnFailedError,
|
|
129
148
|
McpTransportPool,
|
|
149
|
+
ORPHAN_TOOL_USE_REPAIR_REASON,
|
|
130
150
|
POOLED_TRANSPORTS_DEFAULT,
|
|
131
151
|
SESSION_TITLE_MAX_LENGTH,
|
|
132
152
|
SessionService,
|
|
153
|
+
TURN_INTERRUPTION_HISTORY_TAIL_COUNT,
|
|
133
154
|
WorkspaceMcpBudget,
|
|
134
155
|
abortGoalForStopHookCap,
|
|
135
156
|
acquireSleepInhibitor,
|
|
@@ -138,17 +159,23 @@ import {
|
|
|
138
159
|
buildBackgroundEntryLabel,
|
|
139
160
|
buildBtwCacheSafeParams,
|
|
140
161
|
buildBtwPrompt,
|
|
162
|
+
buildContextUsage,
|
|
141
163
|
buildInstallPlan,
|
|
164
|
+
buildSyntheticToolResponseParts,
|
|
142
165
|
clampInlineMediaPart,
|
|
143
166
|
clearGoalTerminalObserver,
|
|
144
167
|
computeUniqueBranchTitle,
|
|
145
168
|
convertToFunctionResponse,
|
|
146
169
|
createDuplicateProviderToolCallResponse,
|
|
147
170
|
dedupeToolCallsById,
|
|
171
|
+
detectAutonomousSentinel,
|
|
172
|
+
detectLoopSentinel,
|
|
173
|
+
detectTurnInterruption,
|
|
148
174
|
evaluateAutoMode,
|
|
149
175
|
evaluatePermissionFlow,
|
|
150
176
|
findExistingProviderModels,
|
|
151
177
|
findProviderById,
|
|
178
|
+
findRepeatedDuplicateProviderToolCall,
|
|
152
179
|
fireNotificationHook,
|
|
153
180
|
firePermissionRequestHook,
|
|
154
181
|
firePostToolUseFailureHook,
|
|
@@ -174,6 +201,8 @@ import {
|
|
|
174
201
|
isDenialFallbackReason,
|
|
175
202
|
isPlanModeBlocked,
|
|
176
203
|
isSystemReminderContent,
|
|
204
|
+
markDuplicateProviderToolCallResponseSent,
|
|
205
|
+
matchesAnyServerPattern,
|
|
177
206
|
needsConfirmation,
|
|
178
207
|
parsePositiveIntegerEnv,
|
|
179
208
|
persistPermissionOutcome,
|
|
@@ -193,36 +222,37 @@ import {
|
|
|
193
222
|
subagentGenerator,
|
|
194
223
|
unregisterGoalHook,
|
|
195
224
|
updateSetting
|
|
196
|
-
} from "./chunk-
|
|
225
|
+
} from "./chunk-35FH347N.js";
|
|
197
226
|
import {
|
|
198
227
|
external_exports
|
|
199
228
|
} from "./chunk-K5PGHDBN.js";
|
|
200
229
|
import "./chunk-5P5XGNYH.js";
|
|
201
230
|
import "./chunk-SKAZWEV5.js";
|
|
202
|
-
import "./chunk-
|
|
203
|
-
import "./chunk-
|
|
204
|
-
import "./chunk-
|
|
231
|
+
import "./chunk-ESVSWG6Z.js";
|
|
232
|
+
import "./chunk-CA4ML3VH.js";
|
|
233
|
+
import "./chunk-HWG4XLD7.js";
|
|
205
234
|
import {
|
|
206
235
|
runSideQuery
|
|
207
|
-
} from "./chunk-
|
|
208
|
-
import "./chunk-
|
|
236
|
+
} from "./chunk-YCRTJYSQ.js";
|
|
237
|
+
import "./chunk-FZY7NH37.js";
|
|
209
238
|
import "./chunk-2MPVVENX.js";
|
|
210
239
|
import "./chunk-ZTQ26VBE.js";
|
|
211
|
-
import "./chunk-
|
|
240
|
+
import "./chunk-SJBIFVD2.js";
|
|
212
241
|
import "./chunk-MLZQVCF3.js";
|
|
213
242
|
import "./chunk-LQ7TMOCE.js";
|
|
214
243
|
import "./chunk-LD2XBG6Z.js";
|
|
215
|
-
import "./chunk-
|
|
244
|
+
import "./chunk-B236XPGF.js";
|
|
216
245
|
import {
|
|
217
246
|
HookEventName,
|
|
218
247
|
createHookOutput
|
|
219
|
-
} from "./chunk-
|
|
220
|
-
import "./chunk-
|
|
221
|
-
import "./chunk-
|
|
248
|
+
} from "./chunk-YXK4GPYO.js";
|
|
249
|
+
import "./chunk-TOAUDIS7.js";
|
|
250
|
+
import "./chunk-NPQWQYU6.js";
|
|
222
251
|
import {
|
|
223
252
|
AuthType,
|
|
224
253
|
ConversationFinishedEvent,
|
|
225
254
|
DiscoveredMCPTool,
|
|
255
|
+
LoopDetectedEvent,
|
|
226
256
|
PromptSuggestionEvent,
|
|
227
257
|
UserPromptEvent,
|
|
228
258
|
endToolExecutionSpan,
|
|
@@ -231,6 +261,7 @@ import {
|
|
|
231
261
|
getAllGeminiMdFilenames,
|
|
232
262
|
getMCPServerStatus,
|
|
233
263
|
logConversationFinishedEvent,
|
|
264
|
+
logLoopDetected,
|
|
234
265
|
logPromptSuggestion,
|
|
235
266
|
logToolCall,
|
|
236
267
|
logUserPrompt,
|
|
@@ -239,46 +270,51 @@ import {
|
|
|
239
270
|
startToolSpan,
|
|
240
271
|
uiTelemetryService,
|
|
241
272
|
withInteractionSpan
|
|
242
|
-
} from "./chunk-
|
|
273
|
+
} from "./chunk-B5MPZI4K.js";
|
|
243
274
|
import "./chunk-3PJXIDKI.js";
|
|
244
275
|
import "./chunk-UWCTAVOD.js";
|
|
245
276
|
import "./chunk-OFEVLU4C.js";
|
|
246
277
|
import "./chunk-OMX7CUOE.js";
|
|
247
278
|
import {
|
|
248
|
-
getAutoMemoryRoot
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
import "./chunk-
|
|
279
|
+
getAutoMemoryRoot,
|
|
280
|
+
getUserAutoMemoryRoot
|
|
281
|
+
} from "./chunk-MOUXUCXQ.js";
|
|
282
|
+
import "./chunk-BZHLWC6T.js";
|
|
283
|
+
import "./chunk-E5I6YSNO.js";
|
|
252
284
|
import {
|
|
253
285
|
ToolConfirmationOutcome,
|
|
254
286
|
ToolNames
|
|
255
|
-
} from "./chunk-
|
|
287
|
+
} from "./chunk-JV7A66DR.js";
|
|
256
288
|
import {
|
|
257
289
|
clearCachedCredentialFile,
|
|
258
290
|
qwenOAuth2Events
|
|
259
|
-
} from "./chunk-
|
|
260
|
-
import "./chunk-
|
|
291
|
+
} from "./chunk-LXGWLOCM.js";
|
|
292
|
+
import "./chunk-QHHNFG22.js";
|
|
261
293
|
import "./chunk-22IFUCVR.js";
|
|
262
|
-
import "./chunk-
|
|
263
|
-
import "./chunk-3UALQ56H.js";
|
|
294
|
+
import "./chunk-T42ACHFI.js";
|
|
264
295
|
import {
|
|
296
|
+
DEFAULT_TOKEN_LIMIT,
|
|
265
297
|
tokenLimit
|
|
266
|
-
} from "./chunk-
|
|
267
|
-
import "./chunk-
|
|
298
|
+
} from "./chunk-YTFBURQD.js";
|
|
299
|
+
import "./chunk-YPCPZSZB.js";
|
|
268
300
|
import "./chunk-IRJWHZWU.js";
|
|
269
301
|
import "./chunk-SYCJMSIJ.js";
|
|
270
302
|
import "./chunk-55ZMG67I.js";
|
|
271
303
|
import "./chunk-H6BD2ELD.js";
|
|
272
304
|
import "./chunk-5IFG2VC4.js";
|
|
305
|
+
import "./chunk-64WXLC72.js";
|
|
306
|
+
import "./chunk-FQDMX5IL.js";
|
|
273
307
|
import {
|
|
274
308
|
Storage,
|
|
275
309
|
createDebugLogger,
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
310
|
+
getErrorMessage,
|
|
311
|
+
getErrorStatus,
|
|
312
|
+
isSubpath
|
|
313
|
+
} from "./chunk-AVQDCKNF.js";
|
|
314
|
+
import "./chunk-OE5J6UA7.js";
|
|
279
315
|
import {
|
|
280
316
|
runExitCleanup
|
|
281
|
-
} from "./chunk-
|
|
317
|
+
} from "./chunk-WJAY4WXR.js";
|
|
282
318
|
import "./chunk-ZERZSAZL.js";
|
|
283
319
|
import "./chunk-QN5NZ3UQ.js";
|
|
284
320
|
import "./chunk-BR4QREVK.js";
|
|
@@ -322,17 +358,50 @@ __name(pickAuthMethodsForAuthRequired, "pickAuthMethodsForAuthRequired");
|
|
|
322
358
|
|
|
323
359
|
// packages/cli/src/acp-integration/service/filesystem.ts
|
|
324
360
|
init_esbuild_shims();
|
|
361
|
+
import { realpath } from "node:fs/promises";
|
|
362
|
+
import path from "node:path";
|
|
325
363
|
var RESOURCE_NOT_FOUND_CODE = -32002;
|
|
364
|
+
var PATH_OUTSIDE_WORKSPACE_KIND = "path_outside_workspace";
|
|
365
|
+
var SYMLINK_ESCAPE_KIND = "symlink_escape";
|
|
366
|
+
var LOCAL_READ_FALLBACK_ERROR_KINDS = /* @__PURE__ */ new Set([
|
|
367
|
+
PATH_OUTSIDE_WORKSPACE_KIND,
|
|
368
|
+
SYMLINK_ESCAPE_KIND
|
|
369
|
+
]);
|
|
370
|
+
var debugLogger = createDebugLogger("ACP_FILE_SYSTEM");
|
|
371
|
+
function isRecord(value) {
|
|
372
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
373
|
+
}
|
|
374
|
+
__name(isRecord, "isRecord");
|
|
326
375
|
function getErrorCode(error) {
|
|
327
376
|
if (error instanceof RequestError) {
|
|
328
377
|
return error.code;
|
|
329
378
|
}
|
|
330
|
-
if (
|
|
331
|
-
return error
|
|
379
|
+
if (isRecord(error)) {
|
|
380
|
+
return error["code"];
|
|
332
381
|
}
|
|
333
382
|
return void 0;
|
|
334
383
|
}
|
|
335
384
|
__name(getErrorCode, "getErrorCode");
|
|
385
|
+
function getErrorData(error) {
|
|
386
|
+
const data = isRecord(error) ? error["data"] : void 0;
|
|
387
|
+
return isRecord(data) ? data : void 0;
|
|
388
|
+
}
|
|
389
|
+
__name(getErrorData, "getErrorData");
|
|
390
|
+
function getErrorKind(error) {
|
|
391
|
+
const data = getErrorData(error);
|
|
392
|
+
if (data && typeof data["errorKind"] === "string") {
|
|
393
|
+
return data["errorKind"];
|
|
394
|
+
}
|
|
395
|
+
return void 0;
|
|
396
|
+
}
|
|
397
|
+
__name(getErrorKind, "getErrorKind");
|
|
398
|
+
function normalizeError(error) {
|
|
399
|
+
if (error instanceof Error) return error;
|
|
400
|
+
return new Error(getErrorMessage(error), {
|
|
401
|
+
cause: error
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
__name(normalizeError, "normalizeError");
|
|
336
405
|
function createEnoentError(filePath) {
|
|
337
406
|
const err = new Error(`File not found: ${filePath}`);
|
|
338
407
|
err.code = "ENOENT";
|
|
@@ -341,12 +410,32 @@ function createEnoentError(filePath) {
|
|
|
341
410
|
return err;
|
|
342
411
|
}
|
|
343
412
|
__name(createEnoentError, "createEnoentError");
|
|
413
|
+
function isLocalReadFallbackErrorKind(errorKind) {
|
|
414
|
+
return typeof errorKind === "string" && LOCAL_READ_FALLBACK_ERROR_KINDS.has(errorKind);
|
|
415
|
+
}
|
|
416
|
+
__name(isLocalReadFallbackErrorKind, "isLocalReadFallbackErrorKind");
|
|
417
|
+
async function resolveRealPath(value) {
|
|
418
|
+
if (!value.trim()) return void 0;
|
|
419
|
+
try {
|
|
420
|
+
return await realpath(path.resolve(value));
|
|
421
|
+
} catch (error) {
|
|
422
|
+
if (getErrorCode(error) !== "ENOENT") {
|
|
423
|
+
debugLogger.warn("realpath failed during ACP local read fallback check", {
|
|
424
|
+
path: value,
|
|
425
|
+
error: getErrorMessage(error)
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
return void 0;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
__name(resolveRealPath, "resolveRealPath");
|
|
344
432
|
var AcpFileSystemService = class {
|
|
345
|
-
constructor(connection, sessionId, capabilities, fallback) {
|
|
433
|
+
constructor(connection, sessionId, capabilities, fallback, options = {}) {
|
|
346
434
|
this.connection = connection;
|
|
347
435
|
this.sessionId = sessionId;
|
|
348
436
|
this.capabilities = capabilities;
|
|
349
437
|
this.fallback = fallback;
|
|
438
|
+
this.options = options;
|
|
350
439
|
}
|
|
351
440
|
static {
|
|
352
441
|
__name(this, "AcpFileSystemService");
|
|
@@ -366,7 +455,45 @@ var AcpFileSystemService = class {
|
|
|
366
455
|
if (errorCode === RESOURCE_NOT_FOUND_CODE) {
|
|
367
456
|
throw createEnoentError(params.path);
|
|
368
457
|
}
|
|
369
|
-
|
|
458
|
+
const errorKind = getErrorKind(error);
|
|
459
|
+
const shouldTryLocalReadFallback = isLocalReadFallbackErrorKind(errorKind);
|
|
460
|
+
const fallbackPath = shouldTryLocalReadFallback ? await this.getLocalReadFallbackPath(params.path) : void 0;
|
|
461
|
+
if (shouldTryLocalReadFallback && !fallbackPath) {
|
|
462
|
+
debugLogger.debug("Local read fallback skipped - no safe local path", {
|
|
463
|
+
path: params.path,
|
|
464
|
+
errorKind
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
if (shouldTryLocalReadFallback && fallbackPath) {
|
|
468
|
+
debugLogger.debug("Falling back to local read after ACP error", {
|
|
469
|
+
path: params.path,
|
|
470
|
+
resolvedPath: fallbackPath,
|
|
471
|
+
errorKind,
|
|
472
|
+
error: getErrorMessage(error)
|
|
473
|
+
});
|
|
474
|
+
try {
|
|
475
|
+
return await this.fallback.readTextFile({
|
|
476
|
+
...params,
|
|
477
|
+
path: fallbackPath
|
|
478
|
+
});
|
|
479
|
+
} catch (fallbackError) {
|
|
480
|
+
if (getErrorCode(fallbackError) === "ENOENT") {
|
|
481
|
+
throw fallbackError;
|
|
482
|
+
}
|
|
483
|
+
debugLogger.warn("Local read fallback failed after ACP error", {
|
|
484
|
+
path: params.path,
|
|
485
|
+
resolvedPath: fallbackPath,
|
|
486
|
+
errorKind,
|
|
487
|
+
originalError: getErrorMessage(error),
|
|
488
|
+
fallbackError: getErrorMessage(fallbackError)
|
|
489
|
+
});
|
|
490
|
+
throw new Error(
|
|
491
|
+
`Local fallback read failed for ${params.path}: ${getErrorMessage(fallbackError)} (original ACP error: ${getErrorMessage(error)})`,
|
|
492
|
+
{ cause: { fallbackError, acpError: error } }
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
throw normalizeError(error);
|
|
370
497
|
}
|
|
371
498
|
return response;
|
|
372
499
|
}
|
|
@@ -375,27 +502,54 @@ var AcpFileSystemService = class {
|
|
|
375
502
|
return this.fallback.writeTextFile(params);
|
|
376
503
|
}
|
|
377
504
|
const finalContent = params._meta?.["bom"] && params.content.charCodeAt(0) !== 65279 ? "\uFEFF" + params.content : params.content;
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
505
|
+
try {
|
|
506
|
+
await this.connection.writeTextFile({
|
|
507
|
+
...params,
|
|
508
|
+
content: finalContent,
|
|
509
|
+
sessionId: this.sessionId
|
|
510
|
+
});
|
|
511
|
+
} catch (error) {
|
|
512
|
+
if (getErrorCode(error) === RESOURCE_NOT_FOUND_CODE) {
|
|
513
|
+
throw createEnoentError(params.path);
|
|
514
|
+
}
|
|
515
|
+
throw normalizeError(error);
|
|
516
|
+
}
|
|
383
517
|
return { _meta: params._meta };
|
|
384
518
|
}
|
|
385
519
|
findFiles(fileName, searchPaths) {
|
|
386
520
|
return this.fallback.findFiles(fileName, searchPaths);
|
|
387
521
|
}
|
|
522
|
+
async getResolvedLocalReadRoots() {
|
|
523
|
+
const roots = await Promise.all(
|
|
524
|
+
(this.options.localReadRoots ?? []).map(resolveRealPath)
|
|
525
|
+
);
|
|
526
|
+
return roots.filter((root) => Boolean(root));
|
|
527
|
+
}
|
|
528
|
+
async getLocalReadFallbackPath(filePath) {
|
|
529
|
+
const normalizedFilePath = path.resolve(filePath);
|
|
530
|
+
const realFilePath = await resolveRealPath(normalizedFilePath);
|
|
531
|
+
if (!realFilePath) return void 0;
|
|
532
|
+
for (const realRoot of await this.getResolvedLocalReadRoots()) {
|
|
533
|
+
if (isSubpath(realRoot, realFilePath)) {
|
|
534
|
+
return realFilePath;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return void 0;
|
|
538
|
+
}
|
|
388
539
|
};
|
|
389
540
|
|
|
390
541
|
// packages/cli/src/acp-integration/acpAgent.ts
|
|
391
542
|
import { Readable, Writable } from "node:stream";
|
|
392
543
|
import { pipeline } from "node:stream/promises";
|
|
393
544
|
import * as fs from "node:fs/promises";
|
|
394
|
-
import
|
|
545
|
+
import { createRequire } from "node:module";
|
|
546
|
+
import * as path3 from "node:path";
|
|
395
547
|
import { createGunzip } from "node:zlib";
|
|
396
548
|
|
|
397
549
|
// packages/cli/src/acp-integration/session/Session.ts
|
|
398
550
|
init_esbuild_shims();
|
|
551
|
+
import * as os from "node:os";
|
|
552
|
+
import * as path2 from "node:path";
|
|
399
553
|
|
|
400
554
|
// packages/cli/src/acp-integration/session/SubAgentTracker.ts
|
|
401
555
|
init_esbuild_shims();
|
|
@@ -577,7 +731,7 @@ function toPermissionOptions(confirmation, forceHideAlwaysAllow = false) {
|
|
|
577
731
|
__name(toPermissionOptions, "toPermissionOptions");
|
|
578
732
|
|
|
579
733
|
// packages/cli/src/acp-integration/session/SubAgentTracker.ts
|
|
580
|
-
var
|
|
734
|
+
var debugLogger2 = createDebugLogger("ACP_SUBAGENT_TRACKER");
|
|
581
735
|
var SubAgentTracker = class {
|
|
582
736
|
constructor(ctx, client, parentToolCallId, subagentType, onPermissionCancel) {
|
|
583
737
|
this.ctx = ctx;
|
|
@@ -637,7 +791,7 @@ var SubAgentTracker = class {
|
|
|
637
791
|
try {
|
|
638
792
|
invocation = tool.build(event.args);
|
|
639
793
|
} catch (e) {
|
|
640
|
-
|
|
794
|
+
debugLogger2.warn(`Failed to build subagent tool ${event.name}:`, e);
|
|
641
795
|
}
|
|
642
796
|
}
|
|
643
797
|
this.toolStates.set(event.callId, {
|
|
@@ -716,7 +870,7 @@ var SubAgentTracker = class {
|
|
|
716
870
|
this.onPermissionCancel?.();
|
|
717
871
|
}
|
|
718
872
|
} catch (error) {
|
|
719
|
-
|
|
873
|
+
debugLogger2.error(
|
|
720
874
|
`Permission request failed for subagent tool ${event.name}:`,
|
|
721
875
|
error
|
|
722
876
|
);
|
|
@@ -724,7 +878,7 @@ var SubAgentTracker = class {
|
|
|
724
878
|
try {
|
|
725
879
|
await event.respond("cancel" /* Cancel */);
|
|
726
880
|
} catch (respondError) {
|
|
727
|
-
|
|
881
|
+
debugLogger2.error(
|
|
728
882
|
`Failed to cancel subagent tool ${event.name} after permission request failure:`,
|
|
729
883
|
respondError
|
|
730
884
|
);
|
|
@@ -823,7 +977,7 @@ var TurnBuffer = class {
|
|
|
823
977
|
init_esbuild_shims();
|
|
824
978
|
import { readFileSync, existsSync } from "node:fs";
|
|
825
979
|
import { resolve } from "node:path";
|
|
826
|
-
var
|
|
980
|
+
var debugLogger3 = createDebugLogger("MESSAGE_REWRITER");
|
|
827
981
|
var DEFAULT_REWRITE_PROMPT = `You are an assistant that rewrites raw coding-agent output into concise, user-friendly progress updates.
|
|
828
982
|
|
|
829
983
|
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.
|
|
@@ -852,11 +1006,11 @@ var LlmRewriter = class {
|
|
|
852
1006
|
const filePath = resolve(rewriteConfig.promptFile);
|
|
853
1007
|
if (existsSync(filePath)) {
|
|
854
1008
|
this.prompt = readFileSync(filePath, "utf-8").trim();
|
|
855
|
-
|
|
1009
|
+
debugLogger3.info(
|
|
856
1010
|
`Loaded rewrite prompt from file: ${filePath} (${this.prompt.length} chars)`
|
|
857
1011
|
);
|
|
858
1012
|
} else {
|
|
859
|
-
|
|
1013
|
+
debugLogger3.warn(
|
|
860
1014
|
`Rewrite prompt file not found: ${filePath}, using default`
|
|
861
1015
|
);
|
|
862
1016
|
this.prompt = DEFAULT_REWRITE_PROMPT;
|
|
@@ -893,7 +1047,7 @@ var LlmRewriter = class {
|
|
|
893
1047
|
const inputText = inputParts.join("\n\n");
|
|
894
1048
|
if (!inputText.trim()) return null;
|
|
895
1049
|
if (inputText.length < 10) return null;
|
|
896
|
-
|
|
1050
|
+
debugLogger3.info(
|
|
897
1051
|
`[REWRITE INPUT] system_prompt_len=${this.prompt.length} input_len=${inputText.length} context_turns=${this.outputHistory.length}
|
|
898
1052
|
--- INPUT TEXT ---
|
|
899
1053
|
${inputText}
|
|
@@ -922,10 +1076,10 @@ ${inputText}
|
|
|
922
1076
|
});
|
|
923
1077
|
const rewritten = result.text;
|
|
924
1078
|
if (!rewritten || rewritten.length < 5) {
|
|
925
|
-
|
|
1079
|
+
debugLogger3.info(`[REWRITE OUTPUT] empty or too short, skipping`);
|
|
926
1080
|
return null;
|
|
927
1081
|
}
|
|
928
|
-
|
|
1082
|
+
debugLogger3.info(
|
|
929
1083
|
`[REWRITE OUTPUT] len=${rewritten.length}
|
|
930
1084
|
--- OUTPUT ---
|
|
931
1085
|
${rewritten}
|
|
@@ -934,7 +1088,7 @@ ${rewritten}
|
|
|
934
1088
|
this.outputHistory.push(rewritten);
|
|
935
1089
|
return rewritten;
|
|
936
1090
|
} catch (error) {
|
|
937
|
-
|
|
1091
|
+
debugLogger3.warn(
|
|
938
1092
|
`LLM rewrite failed, skipping: ${error instanceof Error ? error.message : String(error)}`
|
|
939
1093
|
);
|
|
940
1094
|
return null;
|
|
@@ -943,7 +1097,7 @@ ${rewritten}
|
|
|
943
1097
|
};
|
|
944
1098
|
|
|
945
1099
|
// packages/cli/src/acp-integration/session/rewrite/MessageRewriteMiddleware.ts
|
|
946
|
-
var
|
|
1100
|
+
var debugLogger4 = createDebugLogger("MESSAGE_REWRITE");
|
|
947
1101
|
var DEFAULT_REWRITE_TIMEOUT_MS = 3e4;
|
|
948
1102
|
var REWRITE_META_EXCLUDED_KEYS = /* @__PURE__ */ new Set([]);
|
|
949
1103
|
var MessageRewriteMiddleware = class {
|
|
@@ -1023,10 +1177,10 @@ var MessageRewriteMiddleware = class {
|
|
|
1023
1177
|
try {
|
|
1024
1178
|
const rewritten = await this.rewriter.rewrite(content, rewriteSignal);
|
|
1025
1179
|
if (!rewritten) {
|
|
1026
|
-
|
|
1180
|
+
debugLogger4.info(`Turn ${turnIdx}: no rewrite output`);
|
|
1027
1181
|
return;
|
|
1028
1182
|
}
|
|
1029
|
-
|
|
1183
|
+
debugLogger4.info(
|
|
1030
1184
|
`Turn ${turnIdx}: rewritten ${rewritten.length} chars`
|
|
1031
1185
|
);
|
|
1032
1186
|
await this.sendUpdate({
|
|
@@ -1039,7 +1193,7 @@ var MessageRewriteMiddleware = class {
|
|
|
1039
1193
|
}
|
|
1040
1194
|
});
|
|
1041
1195
|
} catch (error) {
|
|
1042
|
-
|
|
1196
|
+
debugLogger4.warn(
|
|
1043
1197
|
`Turn ${turnIdx}: rewrite failed: ${error instanceof Error ? error.message : String(error)}`
|
|
1044
1198
|
);
|
|
1045
1199
|
}
|
|
@@ -1084,9 +1238,10 @@ function loadRewriteConfig(settings) {
|
|
|
1084
1238
|
__name(loadRewriteConfig, "loadRewriteConfig");
|
|
1085
1239
|
|
|
1086
1240
|
// packages/cli/src/acp-integration/session/Session.ts
|
|
1087
|
-
var
|
|
1241
|
+
var debugLogger5 = createDebugLogger("SESSION");
|
|
1088
1242
|
var USER_CANCEL_ABORT_REASON = "qwen:user-cancel";
|
|
1089
1243
|
var DAEMON_RETRY_META_KEY = "qwen.daemon.retry";
|
|
1244
|
+
var DAEMON_CONTINUE_META_KEY = "qwen.daemon.continueLastTurn";
|
|
1090
1245
|
function maskApiKeyForDisplay(apiKey) {
|
|
1091
1246
|
const trimmed = apiKey?.trim() ?? "";
|
|
1092
1247
|
if (trimmed.length === 0) return "(not set)";
|
|
@@ -1094,7 +1249,56 @@ function maskApiKeyForDisplay(apiKey) {
|
|
|
1094
1249
|
return `${trimmed.slice(0, 3)}...${trimmed.slice(-4)}`;
|
|
1095
1250
|
}
|
|
1096
1251
|
__name(maskApiKeyForDisplay, "maskApiKeyForDisplay");
|
|
1252
|
+
var DAEMON_TURN_TOOL_CALL_CAP = 100;
|
|
1253
|
+
var DAEMON_INVALID_TOOL_PARAMS_THRESHOLD = 3;
|
|
1097
1254
|
var PERMISSION_CANCEL_SKIP_MESSAGE = "Skipped because a permission request was cancelled before the user answered; user input is required before continuing.";
|
|
1255
|
+
function createDaemonToolLoopState() {
|
|
1256
|
+
return {
|
|
1257
|
+
totalToolCalls: 0,
|
|
1258
|
+
invalidToolParamErrors: /* @__PURE__ */ new Map(),
|
|
1259
|
+
loopDetected: false
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
__name(createDaemonToolLoopState, "createDaemonToolLoopState");
|
|
1263
|
+
function recordDaemonLoopDetected(config, promptId, loopType, message, loopState) {
|
|
1264
|
+
if (!loopState.loopDetected) {
|
|
1265
|
+
loopState.loopDetected = true;
|
|
1266
|
+
debugLogger5.warn(message);
|
|
1267
|
+
logLoopDetected(config, new LoopDetectedEvent(loopType, promptId));
|
|
1268
|
+
}
|
|
1269
|
+
return true;
|
|
1270
|
+
}
|
|
1271
|
+
__name(recordDaemonLoopDetected, "recordDaemonLoopDetected");
|
|
1272
|
+
function recordDaemonToolCalls(config, promptId, loopState, count) {
|
|
1273
|
+
if (!loopState || loopState.loopDetected)
|
|
1274
|
+
return loopState?.loopDetected ?? false;
|
|
1275
|
+
loopState.totalToolCalls += count;
|
|
1276
|
+
if (loopState.totalToolCalls <= DAEMON_TURN_TOOL_CALL_CAP) return false;
|
|
1277
|
+
return recordDaemonLoopDetected(
|
|
1278
|
+
config,
|
|
1279
|
+
promptId,
|
|
1280
|
+
"turn_tool_call_cap" /* TURN_TOOL_CALL_CAP */,
|
|
1281
|
+
`Stopping ACP turn after ${loopState.totalToolCalls} tool calls in one turn.`,
|
|
1282
|
+
loopState
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1285
|
+
__name(recordDaemonToolCalls, "recordDaemonToolCalls");
|
|
1286
|
+
function recordDaemonInvalidToolParams(config, promptId, loopState, toolName, error) {
|
|
1287
|
+
if (!loopState || loopState.loopDetected)
|
|
1288
|
+
return loopState?.loopDetected ?? false;
|
|
1289
|
+
const key = `${toolName}\0${error.message}`;
|
|
1290
|
+
const count = (loopState.invalidToolParamErrors.get(key) ?? 0) + 1;
|
|
1291
|
+
loopState.invalidToolParamErrors.set(key, count);
|
|
1292
|
+
if (count < DAEMON_INVALID_TOOL_PARAMS_THRESHOLD) return false;
|
|
1293
|
+
return recordDaemonLoopDetected(
|
|
1294
|
+
config,
|
|
1295
|
+
promptId,
|
|
1296
|
+
"invalid_tool_params_stagnation" /* INVALID_TOOL_PARAMS_STAGNATION */,
|
|
1297
|
+
`Stopping ACP turn after repeated tool parameter errors from ${toolName}: ${error.message}`,
|
|
1298
|
+
loopState
|
|
1299
|
+
);
|
|
1300
|
+
}
|
|
1301
|
+
__name(recordDaemonInvalidToolParams, "recordDaemonInvalidToolParams");
|
|
1098
1302
|
var MID_TURN_QUEUE_DRAIN_TIMEOUT_MS = 2e3;
|
|
1099
1303
|
var MID_TURN_QUEUE_RECOVERY_TIMEOUT_MS = 3e4;
|
|
1100
1304
|
var MID_TURN_QUEUE_RESOLVE_TIMEOUT_MS = 1e4;
|
|
@@ -1102,12 +1306,20 @@ var MAX_MID_TURN_DRAIN_ITEMS = 10;
|
|
|
1102
1306
|
var MID_TURN_ATTACHMENT_PROCESSING_FAILURE_TEXT = "[Attachment could not be processed]";
|
|
1103
1307
|
var MAX_MID_TURN_RESOURCE_TEXT_LENGTH = 1e5;
|
|
1104
1308
|
var MID_TURN_QUEUE_DRAIN_MAX_TIMEOUT_STRIKES = 3;
|
|
1105
|
-
|
|
1309
|
+
var TRANSIENT_FS_CODES = [
|
|
1310
|
+
"EACCES",
|
|
1311
|
+
"EIO",
|
|
1312
|
+
"EBUSY",
|
|
1313
|
+
"EPERM",
|
|
1314
|
+
"EISDIR",
|
|
1315
|
+
"ENOTDIR"
|
|
1316
|
+
];
|
|
1317
|
+
function isRecord2(value) {
|
|
1106
1318
|
return value !== null && typeof value === "object";
|
|
1107
1319
|
}
|
|
1108
|
-
__name(
|
|
1320
|
+
__name(isRecord2, "isRecord");
|
|
1109
1321
|
function isContentBlock(value) {
|
|
1110
|
-
if (!
|
|
1322
|
+
if (!isRecord2(value) || typeof value["type"] !== "string") return false;
|
|
1111
1323
|
switch (value["type"]) {
|
|
1112
1324
|
case "text":
|
|
1113
1325
|
return typeof value["text"] === "string";
|
|
@@ -1120,7 +1332,7 @@ function isContentBlock(value) {
|
|
|
1120
1332
|
case "resource":
|
|
1121
1333
|
return isEmbeddedResourceResource(value["resource"]);
|
|
1122
1334
|
default:
|
|
1123
|
-
|
|
1335
|
+
debugLogger5.warn(`Unknown ContentBlock type: ${value["type"]}`);
|
|
1124
1336
|
return false;
|
|
1125
1337
|
}
|
|
1126
1338
|
}
|
|
@@ -1146,7 +1358,7 @@ async function withTimeoutSignal(parentSignal, timeoutMs, fn) {
|
|
|
1146
1358
|
}
|
|
1147
1359
|
__name(withTimeoutSignal, "withTimeoutSignal");
|
|
1148
1360
|
function isEmbeddedResourceResource(value) {
|
|
1149
|
-
if (!
|
|
1361
|
+
if (!isRecord2(value) || typeof value["uri"] !== "string") return false;
|
|
1150
1362
|
if (typeof value["text"] === "string") {
|
|
1151
1363
|
return value["text"].length <= MAX_MID_TURN_RESOURCE_TEXT_LENGTH;
|
|
1152
1364
|
}
|
|
@@ -1159,7 +1371,7 @@ function hasInlineMediaContentBlock(content) {
|
|
|
1159
1371
|
__name(hasInlineMediaContentBlock, "hasInlineMediaContentBlock");
|
|
1160
1372
|
function capMidTurnDrainItems(items, fieldName) {
|
|
1161
1373
|
if (items.length <= MAX_MID_TURN_DRAIN_ITEMS) return items;
|
|
1162
|
-
|
|
1374
|
+
debugLogger5.warn(
|
|
1163
1375
|
`Mid-turn drain response had ${items.length} ${fieldName}; processing first ${MAX_MID_TURN_DRAIN_ITEMS}`
|
|
1164
1376
|
);
|
|
1165
1377
|
return items.slice(0, MAX_MID_TURN_DRAIN_ITEMS);
|
|
@@ -1174,7 +1386,7 @@ function getMidTurnItemDisplayTextForLog(displayText) {
|
|
|
1174
1386
|
__name(getMidTurnItemDisplayTextForLog, "getMidTurnItemDisplayTextForLog");
|
|
1175
1387
|
function getValidMidTurnContentBlocks(content, displayText) {
|
|
1176
1388
|
if (!Array.isArray(content)) {
|
|
1177
|
-
|
|
1389
|
+
debugLogger5.warn(
|
|
1178
1390
|
`Dropped invalid mid-turn item: ${getMidTurnItemDisplayTextForLog(
|
|
1179
1391
|
displayText
|
|
1180
1392
|
)}`
|
|
@@ -1184,7 +1396,7 @@ function getValidMidTurnContentBlocks(content, displayText) {
|
|
|
1184
1396
|
const validBlocks = content.filter(isContentBlock);
|
|
1185
1397
|
const invalidBlockCount = content.length - validBlocks.length;
|
|
1186
1398
|
if (invalidBlockCount > 0) {
|
|
1187
|
-
|
|
1399
|
+
debugLogger5.warn(
|
|
1188
1400
|
`Dropped ${invalidBlockCount} invalid mid-turn content block(s): ${getMidTurnItemDisplayTextForLog(
|
|
1189
1401
|
displayText
|
|
1190
1402
|
)}`
|
|
@@ -1204,11 +1416,11 @@ function getStructuredMidTurnDisplayText(content, displayText) {
|
|
|
1204
1416
|
}
|
|
1205
1417
|
__name(getStructuredMidTurnDisplayText, "getStructuredMidTurnDisplayText");
|
|
1206
1418
|
function parseMidTurnDrainResponse(response) {
|
|
1207
|
-
if (!
|
|
1419
|
+
if (!isRecord2(response)) return [];
|
|
1208
1420
|
if (Array.isArray(response["items"])) {
|
|
1209
1421
|
return capMidTurnDrainItems(response["items"], "item(s)").flatMap(
|
|
1210
1422
|
(item) => {
|
|
1211
|
-
if (!
|
|
1423
|
+
if (!isRecord2(item)) {
|
|
1212
1424
|
return [];
|
|
1213
1425
|
}
|
|
1214
1426
|
const content = getValidMidTurnContentBlocks(
|
|
@@ -1230,7 +1442,7 @@ function parseMidTurnDrainResponse(response) {
|
|
|
1230
1442
|
);
|
|
1231
1443
|
}
|
|
1232
1444
|
if (!Array.isArray(response["messages"])) {
|
|
1233
|
-
|
|
1445
|
+
debugLogger5.warn(
|
|
1234
1446
|
`Mid-turn drain response had no recognized 'items' or 'messages' field; keys: ${Object.keys(
|
|
1235
1447
|
response
|
|
1236
1448
|
).join(", ")}`
|
|
@@ -1287,7 +1499,7 @@ async function fireSessionPermissionDeniedForAutoMode(config, decision, outcome,
|
|
|
1287
1499
|
callId
|
|
1288
1500
|
);
|
|
1289
1501
|
} catch (hookError) {
|
|
1290
|
-
|
|
1502
|
+
debugLogger5.warn(
|
|
1291
1503
|
`PermissionDenied hook failed for tool ${callId}: ${hookError instanceof Error ? hookError.message : String(hookError)}`
|
|
1292
1504
|
);
|
|
1293
1505
|
}
|
|
@@ -1328,6 +1540,18 @@ function isUserPromptRecord(record) {
|
|
|
1328
1540
|
) ?? false;
|
|
1329
1541
|
}
|
|
1330
1542
|
__name(isUserPromptRecord, "isUserPromptRecord");
|
|
1543
|
+
var AT_TOKEN_RE = /@([^\s,;!?()[\]{}]+)/g;
|
|
1544
|
+
function collectExtensionMentionRefs(text, mentions) {
|
|
1545
|
+
for (const match of text.matchAll(AT_TOKEN_RE)) {
|
|
1546
|
+
const pathName = match[1];
|
|
1547
|
+
if (!pathName) continue;
|
|
1548
|
+
const ref = parseExtensionRef(pathName);
|
|
1549
|
+
if (ref) {
|
|
1550
|
+
mentions.set(ref.name.toLowerCase(), ref.name);
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
__name(collectExtensionMentionRefs, "collectExtensionMentionRefs");
|
|
1331
1555
|
async function buildAvailableCommandsSnapshot(config, abortSignal = AbortSignal.timeout(1e4), settings) {
|
|
1332
1556
|
const slashCommands = await getAvailableCommands(
|
|
1333
1557
|
config,
|
|
@@ -1347,7 +1571,12 @@ async function buildAvailableCommandsSnapshot(config, abortSignal = AbortSignal.
|
|
|
1347
1571
|
sourceLabel: cmd.sourceLabel,
|
|
1348
1572
|
supportedModes: getEffectiveSupportedModes(cmd),
|
|
1349
1573
|
subcommands: getCommandSubcommandNames(cmd),
|
|
1350
|
-
modelInvocable: cmd.modelInvocable === true
|
|
1574
|
+
modelInvocable: cmd.modelInvocable === true,
|
|
1575
|
+
// Carry aliases so a channel consumer (which only sees the wire snapshot,
|
|
1576
|
+
// not the command registry) can recognize an aliased command and avoid
|
|
1577
|
+
// tagging it. _meta is ACP's extension point; omitted when there are none
|
|
1578
|
+
// so command entries without aliases stay byte-identical on the wire.
|
|
1579
|
+
...cmd.altNames && cmd.altNames.length > 0 ? { altNames: cmd.altNames } : {}
|
|
1351
1580
|
}
|
|
1352
1581
|
};
|
|
1353
1582
|
});
|
|
@@ -1370,7 +1599,7 @@ async function buildAvailableCommandsSnapshot(config, abortSignal = AbortSignal.
|
|
|
1370
1599
|
}
|
|
1371
1600
|
}
|
|
1372
1601
|
} catch (error) {
|
|
1373
|
-
|
|
1602
|
+
debugLogger5.error("Error loading available skills:", error);
|
|
1374
1603
|
}
|
|
1375
1604
|
for (const command of slashCommands) {
|
|
1376
1605
|
if (command.kind !== "skill" /* SKILL */ || !command.skillDetail) {
|
|
@@ -1442,12 +1671,23 @@ var Session = class {
|
|
|
1442
1671
|
cronQueue = [];
|
|
1443
1672
|
cronProcessing = false;
|
|
1444
1673
|
cronAbortController = null;
|
|
1674
|
+
// Resolves the `<<loop.md>>` / `<<loop.md-dynamic>>` sentinels at fire time.
|
|
1675
|
+
// Lazily created on the first loop tick; its content cache is reset on
|
|
1676
|
+
// compaction (see #sendMessageStreamWithAutoCompression) and it is rebuilt if
|
|
1677
|
+
// the working dir changes (e.g. /cd) so it always reads the current project's
|
|
1678
|
+
// loop.md.
|
|
1679
|
+
loopTickResolver = null;
|
|
1680
|
+
loopTickResolverRoot = null;
|
|
1445
1681
|
cronCompletion = null;
|
|
1446
1682
|
cronDisabledByTokenLimit = false;
|
|
1447
1683
|
lastPromptTokenCount = 0;
|
|
1448
1684
|
lastPromptTokenCountChat = null;
|
|
1449
1685
|
midTurnDrainUnavailable = false;
|
|
1450
1686
|
midTurnDrainTimeoutStrikes = 0;
|
|
1687
|
+
// ACP can continue one logical conversation through prompt, cron, and
|
|
1688
|
+
// background loops, so keep this with the session instead of a single
|
|
1689
|
+
// runToolCalls invocation.
|
|
1690
|
+
duplicateProviderToolCallResponseIds = /* @__PURE__ */ new Set();
|
|
1451
1691
|
// Messages from a drain that the daemon answered but we timed out waiting for
|
|
1452
1692
|
// (the daemon already spliced + SSE-published them). Re-injected on the next
|
|
1453
1693
|
// batch so a transient stall can't silently lose them. See
|
|
@@ -1501,7 +1741,7 @@ var Session = class {
|
|
|
1501
1741
|
*/
|
|
1502
1742
|
startCronScheduler() {
|
|
1503
1743
|
this.#startCronSchedulerIfNeeded().catch((error) => {
|
|
1504
|
-
|
|
1744
|
+
debugLogger5.warn(
|
|
1505
1745
|
`Cron scheduler startup failed [session ${this.sessionId}]: ${error}`
|
|
1506
1746
|
);
|
|
1507
1747
|
});
|
|
@@ -1548,7 +1788,7 @@ var Session = class {
|
|
|
1548
1788
|
installRewriter() {
|
|
1549
1789
|
const rewriteConfig = loadRewriteConfig(this.settings);
|
|
1550
1790
|
if (rewriteConfig?.enabled) {
|
|
1551
|
-
|
|
1791
|
+
debugLogger5.info("Message rewrite middleware enabled");
|
|
1552
1792
|
this.messageRewriter = new MessageRewriteMiddleware(
|
|
1553
1793
|
this.config,
|
|
1554
1794
|
rewriteConfig,
|
|
@@ -1559,7 +1799,7 @@ var Session = class {
|
|
|
1559
1799
|
#installGoalTerminalObserver() {
|
|
1560
1800
|
setGoalTerminalObserver(this.sessionId, (event) => {
|
|
1561
1801
|
void this.messageEmitter.emitGoalTerminal(event).catch((error) => {
|
|
1562
|
-
|
|
1802
|
+
debugLogger5.warn(
|
|
1563
1803
|
`Failed to emit goal terminal update: ${this.#formatError(error)}`
|
|
1564
1804
|
);
|
|
1565
1805
|
});
|
|
@@ -1567,7 +1807,7 @@ var Session = class {
|
|
|
1567
1807
|
}
|
|
1568
1808
|
emitGoalStatus(status) {
|
|
1569
1809
|
void this.messageEmitter.emitGoalStatus(status).catch((error) => {
|
|
1570
|
-
|
|
1810
|
+
debugLogger5.warn(
|
|
1571
1811
|
`Failed to emit goal status update: ${this.#formatError(error)}`
|
|
1572
1812
|
);
|
|
1573
1813
|
});
|
|
@@ -1751,6 +1991,7 @@ var Session = class {
|
|
|
1751
1991
|
if (pendingSend.signal.aborted) {
|
|
1752
1992
|
return { stopReason: "cancelled" };
|
|
1753
1993
|
}
|
|
1994
|
+
this.duplicateProviderToolCallResponseIds.clear();
|
|
1754
1995
|
let resolveCompletion;
|
|
1755
1996
|
this.pendingPromptCompletion = new Promise((resolve3) => {
|
|
1756
1997
|
resolveCompletion = resolve3;
|
|
@@ -1769,6 +2010,36 @@ var Session = class {
|
|
|
1769
2010
|
this.pendingPromptCompletion = null;
|
|
1770
2011
|
}
|
|
1771
2012
|
}
|
|
2013
|
+
/**
|
|
2014
|
+
* Classify whether an unfinished previous turn can be resumed — an
|
|
2015
|
+
* interrupted prompt (the model never answered) or a turn left with dangling
|
|
2016
|
+
* tool calls — without injecting a synthetic "continue" user message.
|
|
2017
|
+
* Classifies from persisted history. Idempotent no-op (accepted:false) when
|
|
2018
|
+
* the last turn ended cleanly or a prompt is already in flight.
|
|
2019
|
+
*
|
|
2020
|
+
* This is the accept/reject pre-check only — it does NOT fire the turn. When
|
|
2021
|
+
* accepted, the daemon bridge drives the continuation through the normal
|
|
2022
|
+
* prompt-admission path (`sendPrompt` with the trusted continue meta) so it is
|
|
2023
|
+
* tracked like any other prompt; `prompt()` then re-detects/strips
|
|
2024
|
+
* authoritatively. Powers `qwen/control/session/continue`.
|
|
2025
|
+
*/
|
|
2026
|
+
async continueLastTurn() {
|
|
2027
|
+
const geminiClient = this.config.getGeminiClient();
|
|
2028
|
+
if (!geminiClient || !geminiClient.isInitialized()) {
|
|
2029
|
+
return { accepted: false, interruption: "none" };
|
|
2030
|
+
}
|
|
2031
|
+
const chat = this.#getCurrentChat();
|
|
2032
|
+
const detection = detectTurnInterruption(
|
|
2033
|
+
chat.getHistoryTailShallow?.(TURN_INTERRUPTION_HISTORY_TAIL_COUNT) ?? chat.getHistoryTail(TURN_INTERRUPTION_HISTORY_TAIL_COUNT)
|
|
2034
|
+
);
|
|
2035
|
+
if (detection.kind === "none") {
|
|
2036
|
+
return { accepted: false, interruption: "none" };
|
|
2037
|
+
}
|
|
2038
|
+
if (this.pendingPrompt && !this.pendingPrompt.signal.aborted) {
|
|
2039
|
+
return { accepted: false, interruption: detection.kind };
|
|
2040
|
+
}
|
|
2041
|
+
return { accepted: true, interruption: detection.kind };
|
|
2042
|
+
}
|
|
1772
2043
|
/**
|
|
1773
2044
|
* Generate a server-side follow-up suggestion for the just-completed
|
|
1774
2045
|
* turn and push it to attached clients via the daemon's
|
|
@@ -1805,7 +2076,7 @@ var Session = class {
|
|
|
1805
2076
|
const fullHistory = chat.getHistory(true);
|
|
1806
2077
|
const lastEntry = fullHistory[fullHistory.length - 1];
|
|
1807
2078
|
if (!lastEntry || lastEntry.role !== "model") {
|
|
1808
|
-
|
|
2079
|
+
debugLogger5.debug(
|
|
1809
2080
|
"Skipping followup suggestion: last history entry is not model"
|
|
1810
2081
|
);
|
|
1811
2082
|
return;
|
|
@@ -1841,9 +2112,9 @@ var Session = class {
|
|
|
1841
2112
|
}
|
|
1842
2113
|
} catch (error) {
|
|
1843
2114
|
if (ac.signal.aborted) {
|
|
1844
|
-
|
|
2115
|
+
debugLogger5.debug("Follow-up suggestion generation aborted");
|
|
1845
2116
|
} else {
|
|
1846
|
-
|
|
2117
|
+
debugLogger5.warn("Follow-up suggestion generation failed", error);
|
|
1847
2118
|
}
|
|
1848
2119
|
} finally {
|
|
1849
2120
|
if (this.followupAbort === ac) {
|
|
@@ -1886,7 +2157,40 @@ var Session = class {
|
|
|
1886
2157
|
)
|
|
1887
2158
|
);
|
|
1888
2159
|
const isRetry = params.retry === true || params._meta?.[DAEMON_RETRY_META_KEY] === true;
|
|
1889
|
-
|
|
2160
|
+
const isContinue = params._meta?.[DAEMON_CONTINUE_META_KEY] === true;
|
|
2161
|
+
let continuationParts = null;
|
|
2162
|
+
let strippedOrphanEntries = null;
|
|
2163
|
+
let orphanPushCountSnapshot = 0;
|
|
2164
|
+
if (isContinue) {
|
|
2165
|
+
const detection = detectTurnInterruption(
|
|
2166
|
+
this.#getCurrentChat().getHistory()
|
|
2167
|
+
);
|
|
2168
|
+
if (detection.kind === "none") {
|
|
2169
|
+
debugLogger5.warn(
|
|
2170
|
+
`[Session] continue ${promptId}: no interrupted turn on re-detection, nothing to continue`
|
|
2171
|
+
);
|
|
2172
|
+
logConversationFinishedEvent(
|
|
2173
|
+
this.config,
|
|
2174
|
+
new ConversationFinishedEvent(
|
|
2175
|
+
this.config.getApprovalMode(),
|
|
2176
|
+
0
|
|
2177
|
+
)
|
|
2178
|
+
);
|
|
2179
|
+
return { stopReason: "end_turn" };
|
|
2180
|
+
}
|
|
2181
|
+
if (detection.kind === "interrupted_prompt") {
|
|
2182
|
+
strippedOrphanEntries = this.#getCurrentChat().stripOrphanedUserEntriesFromHistory() ?? null;
|
|
2183
|
+
orphanPushCountSnapshot = this.#getCurrentChat().getUserContentPushCount?.() ?? 0;
|
|
2184
|
+
continuationParts = detection.parts;
|
|
2185
|
+
} else {
|
|
2186
|
+
continuationParts = buildSyntheticToolResponseParts(
|
|
2187
|
+
detection.danglingCalls,
|
|
2188
|
+
ORPHAN_TOOL_USE_REPAIR_REASON
|
|
2189
|
+
);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
if (isContinue) {
|
|
2193
|
+
} else if (isRetry) {
|
|
1890
2194
|
this.#getCurrentChat().stripOrphanedUserEntriesFromHistory();
|
|
1891
2195
|
} else {
|
|
1892
2196
|
this.config.getChatRecordingService()?.recordUserMessage(promptText);
|
|
@@ -1896,7 +2200,9 @@ var Session = class {
|
|
|
1896
2200
|
);
|
|
1897
2201
|
const inputText = firstTextBlock?.text || "";
|
|
1898
2202
|
let parts;
|
|
1899
|
-
if (
|
|
2203
|
+
if (isContinue) {
|
|
2204
|
+
parts = continuationParts;
|
|
2205
|
+
} else if (isSlashCommand(inputText)) {
|
|
1900
2206
|
const slashCommandResult = await handleSlashCommand(
|
|
1901
2207
|
inputText,
|
|
1902
2208
|
pendingSend,
|
|
@@ -1918,7 +2224,7 @@ var Session = class {
|
|
|
1918
2224
|
}
|
|
1919
2225
|
const hooksEnabled = !this.config.getDisableAllHooks?.();
|
|
1920
2226
|
const messageBus = this.config.getMessageBus?.();
|
|
1921
|
-
if (hooksEnabled && messageBus && this.config.hasHooksForEvent?.("UserPromptSubmit")) {
|
|
2227
|
+
if (!isContinue && hooksEnabled && messageBus && this.config.hasHooksForEvent?.("UserPromptSubmit")) {
|
|
1922
2228
|
const response = await messageBus.request(
|
|
1923
2229
|
{
|
|
1924
2230
|
type: "hook-execution-request" /* HOOK_EXECUTION_REQUEST */,
|
|
@@ -1934,7 +2240,7 @@ var Session = class {
|
|
|
1934
2240
|
if (hookOutput?.isBlockingDecision() || hookOutput?.shouldStopExecution()) {
|
|
1935
2241
|
const blockReason = hookOutput?.getEffectiveReason() || "No reason provided";
|
|
1936
2242
|
await this.messageEmitter.emitAgentMessage(
|
|
1937
|
-
`\
|
|
2243
|
+
`\u2717 **UserPromptSubmit blocked**: ${blockReason}`
|
|
1938
2244
|
);
|
|
1939
2245
|
return { stopReason: "end_turn" };
|
|
1940
2246
|
}
|
|
@@ -1952,30 +2258,34 @@ var Session = class {
|
|
|
1952
2258
|
this.config.getChatRecordingService()?.recordFileHistorySnapshot(latestSnapshot);
|
|
1953
2259
|
}
|
|
1954
2260
|
} catch (e) {
|
|
1955
|
-
|
|
2261
|
+
debugLogger5.error(`FileHistory: recordSnapshot failed: ${e}`);
|
|
1956
2262
|
}
|
|
1957
2263
|
} catch (e) {
|
|
1958
|
-
|
|
2264
|
+
debugLogger5.error(`FileHistory: makeSnapshot failed: ${e}`);
|
|
1959
2265
|
}
|
|
1960
2266
|
const systemReminders = await this.#buildInitialSystemReminders();
|
|
1961
2267
|
if (systemReminders.length > 0) {
|
|
1962
|
-
|
|
2268
|
+
const alreadyHasReminder = isContinue && parts.some(
|
|
2269
|
+
(part) => isSystemReminderContent({ role: "user", parts: [part] })
|
|
2270
|
+
);
|
|
2271
|
+
if (!alreadyHasReminder) {
|
|
2272
|
+
parts = insertAfterFunctionResponses(parts, systemReminders);
|
|
2273
|
+
}
|
|
1963
2274
|
}
|
|
1964
2275
|
if (this.pendingWorktreeNotice) {
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
text: `<system-reminder>
|
|
2276
|
+
const noticePart = {
|
|
2277
|
+
text: `<system-reminder>
|
|
1968
2278
|
${this.pendingWorktreeNotice}
|
|
1969
2279
|
</system-reminder>
|
|
1970
2280
|
|
|
1971
2281
|
`
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
];
|
|
2282
|
+
};
|
|
2283
|
+
parts = insertAfterFunctionResponses(parts, [noticePart]);
|
|
1975
2284
|
this.pendingWorktreeNotice = null;
|
|
1976
2285
|
}
|
|
1977
2286
|
let nextMessage = { role: "user", parts };
|
|
1978
2287
|
let turnCount = 0;
|
|
2288
|
+
const toolLoopState = createDaemonToolLoopState();
|
|
1979
2289
|
try {
|
|
1980
2290
|
while (nextMessage !== null) {
|
|
1981
2291
|
turnCount++;
|
|
@@ -1995,7 +2305,7 @@ ${this.pendingWorktreeNotice}
|
|
|
1995
2305
|
if (!sendResult.responseStream) {
|
|
1996
2306
|
this.#preserveUnsentMessageHistory(
|
|
1997
2307
|
nextMessage,
|
|
1998
|
-
sendResult.stopReason === "cancelled"
|
|
2308
|
+
isContinue || sendResult.stopReason === "cancelled"
|
|
1999
2309
|
);
|
|
2000
2310
|
return { stopReason: sendResult.stopReason };
|
|
2001
2311
|
}
|
|
@@ -2026,6 +2336,12 @@ ${this.pendingWorktreeNotice}
|
|
|
2026
2336
|
}
|
|
2027
2337
|
}
|
|
2028
2338
|
} catch (error) {
|
|
2339
|
+
if (strippedOrphanEntries && (this.#getCurrentChat().getUserContentPushCount?.() ?? 0) <= orphanPushCountSnapshot) {
|
|
2340
|
+
for (const entry of strippedOrphanEntries) {
|
|
2341
|
+
this.#getCurrentChat().addHistory(entry);
|
|
2342
|
+
}
|
|
2343
|
+
strippedOrphanEntries = null;
|
|
2344
|
+
}
|
|
2029
2345
|
if (pendingSend.signal.aborted && pendingSend.signal.reason === USER_CANCEL_ABORT_REASON && this.#isAbortError(error)) {
|
|
2030
2346
|
return { stopReason: "cancelled" };
|
|
2031
2347
|
}
|
|
@@ -2039,7 +2355,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2039
2355
|
const hooksEnabledForStopFailure = !this.config.getDisableAllHooks?.();
|
|
2040
2356
|
if (hooksEnabledForStopFailure && hookSystem && this.config.hasHooksForEvent?.("StopFailure")) {
|
|
2041
2357
|
hookSystem.fireStopFailureEvent(errorType, errorMessage).catch((err) => {
|
|
2042
|
-
|
|
2358
|
+
debugLogger5.warn(`StopFailure hook failed: ${err}`);
|
|
2043
2359
|
});
|
|
2044
2360
|
}
|
|
2045
2361
|
if (errorStatus === 429) {
|
|
@@ -2066,7 +2382,8 @@ ${this.pendingWorktreeNotice}
|
|
|
2066
2382
|
const toolRun = await this.runToolCalls(
|
|
2067
2383
|
pendingSend.signal,
|
|
2068
2384
|
promptId,
|
|
2069
|
-
functionCalls
|
|
2385
|
+
functionCalls,
|
|
2386
|
+
toolLoopState
|
|
2070
2387
|
);
|
|
2071
2388
|
if (toolRun.stopAfterPermissionCancel) {
|
|
2072
2389
|
await this.#preserveCancelledPermissionToolRun(
|
|
@@ -2075,15 +2392,10 @@ ${this.pendingWorktreeNotice}
|
|
|
2075
2392
|
);
|
|
2076
2393
|
return { stopReason: "end_turn" };
|
|
2077
2394
|
}
|
|
2078
|
-
nextMessage =
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
...await this.#drainMidTurnUserMessages(
|
|
2083
|
-
pendingSend.signal
|
|
2084
|
-
)
|
|
2085
|
-
]
|
|
2086
|
-
};
|
|
2395
|
+
nextMessage = await this.#buildNextMessageAfterToolRun(
|
|
2396
|
+
toolRun,
|
|
2397
|
+
pendingSend.signal
|
|
2398
|
+
);
|
|
2087
2399
|
}
|
|
2088
2400
|
}
|
|
2089
2401
|
if (this.messageRewriter) {
|
|
@@ -2131,13 +2443,18 @@ ${this.pendingWorktreeNotice}
|
|
|
2131
2443
|
return { stopReason: "end_turn" };
|
|
2132
2444
|
}
|
|
2133
2445
|
const responseText = this.#getCurrentChat().getLastModelMessageText?.() || "[no response text]";
|
|
2446
|
+
const contextUsage = buildContextUsage(
|
|
2447
|
+
this.config.getContentGeneratorConfig()?.contextWindowSize ?? DEFAULT_TOKEN_LIMIT,
|
|
2448
|
+
this.lastPromptTokenCount
|
|
2449
|
+
);
|
|
2134
2450
|
const response = await messageBus.request(
|
|
2135
2451
|
{
|
|
2136
2452
|
type: "hook-execution-request" /* HOOK_EXECUTION_REQUEST */,
|
|
2137
2453
|
eventName: "Stop",
|
|
2138
2454
|
input: {
|
|
2139
2455
|
stop_hook_active: true,
|
|
2140
|
-
last_assistant_message: responseText
|
|
2456
|
+
last_assistant_message: responseText,
|
|
2457
|
+
...contextUsage
|
|
2141
2458
|
},
|
|
2142
2459
|
signal: pendingSend.signal
|
|
2143
2460
|
},
|
|
@@ -2166,7 +2483,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2166
2483
|
warning
|
|
2167
2484
|
);
|
|
2168
2485
|
await this.messageEmitter.emitAgentMessage(warning);
|
|
2169
|
-
|
|
2486
|
+
debugLogger5.warn(warning);
|
|
2170
2487
|
return { stopReason: "end_turn" };
|
|
2171
2488
|
}
|
|
2172
2489
|
if (stopHookIterationCount > 1) {
|
|
@@ -2181,6 +2498,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2181
2498
|
role: "user",
|
|
2182
2499
|
parts: continueParts
|
|
2183
2500
|
};
|
|
2501
|
+
const toolLoopState = createDaemonToolLoopState();
|
|
2184
2502
|
while (nextMessage !== null) {
|
|
2185
2503
|
if (pendingSend.signal.aborted) {
|
|
2186
2504
|
return { stopReason: "cancelled" };
|
|
@@ -2237,7 +2555,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2237
2555
|
const hooksEnabledForStopFailure = !this.config.getDisableAllHooks?.();
|
|
2238
2556
|
if (hooksEnabledForStopFailure && hookSystem && this.config.hasHooksForEvent?.("StopFailure")) {
|
|
2239
2557
|
hookSystem.fireStopFailureEvent(errorType, errorMessage).catch((err) => {
|
|
2240
|
-
|
|
2558
|
+
debugLogger5.warn(`StopFailure hook failed: ${err}`);
|
|
2241
2559
|
});
|
|
2242
2560
|
}
|
|
2243
2561
|
if (errorStatus === 429) {
|
|
@@ -2261,7 +2579,8 @@ ${this.pendingWorktreeNotice}
|
|
|
2261
2579
|
const toolRun = await this.runToolCalls(
|
|
2262
2580
|
pendingSend.signal,
|
|
2263
2581
|
promptId,
|
|
2264
|
-
functionCalls
|
|
2582
|
+
functionCalls,
|
|
2583
|
+
toolLoopState
|
|
2265
2584
|
);
|
|
2266
2585
|
if (toolRun.stopAfterPermissionCancel) {
|
|
2267
2586
|
await this.#preserveCancelledPermissionToolRun(
|
|
@@ -2270,13 +2589,10 @@ ${this.pendingWorktreeNotice}
|
|
|
2270
2589
|
);
|
|
2271
2590
|
return { stopReason: "end_turn" };
|
|
2272
2591
|
}
|
|
2273
|
-
nextMessage =
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
...await this.#drainMidTurnUserMessages(pendingSend.signal)
|
|
2278
|
-
]
|
|
2279
|
-
};
|
|
2592
|
+
nextMessage = await this.#buildNextMessageAfterToolRun(
|
|
2593
|
+
toolRun,
|
|
2594
|
+
pendingSend.signal
|
|
2595
|
+
);
|
|
2280
2596
|
}
|
|
2281
2597
|
}
|
|
2282
2598
|
continue;
|
|
@@ -2317,21 +2633,22 @@ ${this.pendingWorktreeNotice}
|
|
|
2317
2633
|
compressionInfo = compressed;
|
|
2318
2634
|
this.#recordCompressionTokenCount(compressed);
|
|
2319
2635
|
if (compressed.compressionStatus === 1 /* COMPRESSED */) {
|
|
2636
|
+
this.loopTickResolver?.resetCache();
|
|
2320
2637
|
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()}`;
|
|
2321
2638
|
compressionDiagnostic = `IMPORTANT: This conversation ${reasonClause}. A compressed context will be sent for future messages (compressed from: ${compressed.originalTokenCount ?? "unknown"} to ${compressed.newTokenCount ?? "unknown"} tokens).`;
|
|
2322
2639
|
}
|
|
2323
2640
|
} catch (compressionError) {
|
|
2324
2641
|
if (abortSignal.aborted || this.#isAbortError(compressionError)) {
|
|
2325
|
-
|
|
2642
|
+
debugLogger5.debug(`Auto-compression aborted for prompt ${promptId}`);
|
|
2326
2643
|
return { responseStream: null, stopReason: "cancelled" };
|
|
2327
2644
|
}
|
|
2328
|
-
|
|
2645
|
+
debugLogger5.warn(
|
|
2329
2646
|
`Auto-compression failed for prompt ${promptId}; proceeding without compression: ` + this.#formatError(compressionError)
|
|
2330
2647
|
);
|
|
2331
2648
|
}
|
|
2332
2649
|
}
|
|
2333
2650
|
if (abortSignal.aborted) {
|
|
2334
|
-
|
|
2651
|
+
debugLogger5.debug(`Auto-compression aborted for prompt ${promptId}`);
|
|
2335
2652
|
return { responseStream: null, stopReason: "cancelled" };
|
|
2336
2653
|
}
|
|
2337
2654
|
if (!compressionInfo) {
|
|
@@ -2341,7 +2658,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2341
2658
|
if (sessionTokenLimit > 0) {
|
|
2342
2659
|
const lastPromptTokenCount = this.#getPostCompressionTokenCount(compressionInfo);
|
|
2343
2660
|
if (lastPromptTokenCount > sessionTokenLimit) {
|
|
2344
|
-
|
|
2661
|
+
debugLogger5.warn(
|
|
2345
2662
|
`Session token limit exceeded for prompt ${promptId}: ${lastPromptTokenCount} > ${sessionTokenLimit}. Send dropped.`
|
|
2346
2663
|
);
|
|
2347
2664
|
await this.#emitAgentDiagnosticMessageSafely(
|
|
@@ -2358,7 +2675,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2358
2675
|
);
|
|
2359
2676
|
}
|
|
2360
2677
|
if (abortSignal.aborted) {
|
|
2361
|
-
|
|
2678
|
+
debugLogger5.debug(
|
|
2362
2679
|
`Send aborted after compression diagnostic for prompt ${promptId}`
|
|
2363
2680
|
);
|
|
2364
2681
|
return { responseStream: null, stopReason: "cancelled" };
|
|
@@ -2386,7 +2703,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2386
2703
|
) ?? [];
|
|
2387
2704
|
const droppedParts = (message.parts?.length ?? 0) - functionResponseParts.length;
|
|
2388
2705
|
if (droppedParts > 0) {
|
|
2389
|
-
|
|
2706
|
+
debugLogger5.debug(
|
|
2390
2707
|
`Dropping ${droppedParts} non-functionResponse part(s) from unsent ACP message after send was skipped.`
|
|
2391
2708
|
);
|
|
2392
2709
|
}
|
|
@@ -2410,6 +2727,23 @@ ${this.pendingWorktreeNotice}
|
|
|
2410
2727
|
);
|
|
2411
2728
|
await this.messageRewriter?.waitForPendingRewrites();
|
|
2412
2729
|
}
|
|
2730
|
+
async #buildNextMessageAfterToolRun(toolRun, abortSignal) {
|
|
2731
|
+
if (toolRun.loopDetected) {
|
|
2732
|
+
debugLogger5.debug("Stopping ACP turn after daemon loop detection.");
|
|
2733
|
+
return null;
|
|
2734
|
+
}
|
|
2735
|
+
if (toolRun.repeatedDuplicateProviderToolCall) {
|
|
2736
|
+
debugLogger5.debug(
|
|
2737
|
+
"Stopping ACP turn after dropping repeated duplicate provider tool-call response."
|
|
2738
|
+
);
|
|
2739
|
+
return null;
|
|
2740
|
+
}
|
|
2741
|
+
const parts = [
|
|
2742
|
+
...toolRun.parts,
|
|
2743
|
+
...await this.#drainMidTurnUserMessages(abortSignal)
|
|
2744
|
+
];
|
|
2745
|
+
return { role: "user", parts };
|
|
2746
|
+
}
|
|
2413
2747
|
#recordCompressionTokenCount(info) {
|
|
2414
2748
|
this.#syncPromptTokenCountWithCurrentChat();
|
|
2415
2749
|
const tokenCount = this.#extractCompressionTokenCount(info);
|
|
@@ -2477,7 +2811,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2477
2811
|
try {
|
|
2478
2812
|
await this.#emitAgentDiagnosticMessage(text);
|
|
2479
2813
|
} catch (notifyError) {
|
|
2480
|
-
|
|
2814
|
+
debugLogger5.warn(`${failureContext}: ${this.#formatError(notifyError)}`);
|
|
2481
2815
|
}
|
|
2482
2816
|
}
|
|
2483
2817
|
async #emitAgentDiagnosticMessage(text) {
|
|
@@ -2526,7 +2860,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2526
2860
|
if (isPermanentError) {
|
|
2527
2861
|
this.midTurnDrainUnavailable = true;
|
|
2528
2862
|
}
|
|
2529
|
-
|
|
2863
|
+
debugLogger5.warn(
|
|
2530
2864
|
`Mid-turn queue drain ${isPermanentError ? "permanently " : ""}unavailable [session ${this.sessionId}]: ${errorMessage}`
|
|
2531
2865
|
);
|
|
2532
2866
|
return this.#buildMidTurnParts(recovered, abortSignal);
|
|
@@ -2568,14 +2902,14 @@ ${this.pendingWorktreeNotice}
|
|
|
2568
2902
|
clearTimeout(timer);
|
|
2569
2903
|
}
|
|
2570
2904
|
if (late === expired) {
|
|
2571
|
-
|
|
2905
|
+
debugLogger5.warn(
|
|
2572
2906
|
`[mid-turn] dropped a drain response that arrived after the ${MID_TURN_QUEUE_RECOVERY_TIMEOUT_MS}ms recovery deadline [session ${this.sessionId}]`
|
|
2573
2907
|
);
|
|
2574
2908
|
return;
|
|
2575
2909
|
}
|
|
2576
2910
|
const lateMessages = parseMidTurnDrainResponse(late);
|
|
2577
2911
|
if (lateMessages.length > 0) {
|
|
2578
|
-
|
|
2912
|
+
debugLogger5.debug(
|
|
2579
2913
|
`[mid-turn] recovered ${lateMessages.length} message(s) from a timed-out drain [session ${this.sessionId}]`
|
|
2580
2914
|
);
|
|
2581
2915
|
this.midTurnRecoveredMessages.push(...lateMessages);
|
|
@@ -2601,7 +2935,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2601
2935
|
} catch (messageError) {
|
|
2602
2936
|
if (abortSignal.aborted) return parts;
|
|
2603
2937
|
const errorMessage = this.#formatError(messageError);
|
|
2604
|
-
|
|
2938
|
+
debugLogger5.warn(`Failed to resolve mid-turn message: ${errorMessage}`);
|
|
2605
2939
|
rawParts = [{ text: displayText }];
|
|
2606
2940
|
if (message.kind === "structured" && hasInlineMediaContentBlock(message.content)) {
|
|
2607
2941
|
rawParts.push({ text: MID_TURN_ATTACHMENT_PROCESSING_FAILURE_TEXT });
|
|
@@ -2626,20 +2960,23 @@ ${this.pendingWorktreeNotice}
|
|
|
2626
2960
|
try {
|
|
2627
2961
|
await scheduler.enableDurable(this.sessionId);
|
|
2628
2962
|
} catch (err) {
|
|
2629
|
-
|
|
2963
|
+
debugLogger5.warn(
|
|
2630
2964
|
`Durable cron init failed \u2014 persistent tasks will not fire in this session: ${err}`
|
|
2631
2965
|
);
|
|
2632
2966
|
}
|
|
2633
2967
|
if (this.disposed) return;
|
|
2634
2968
|
if (!scheduler.hasPendingWork) return;
|
|
2635
|
-
scheduler.start(
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2969
|
+
scheduler.start(
|
|
2970
|
+
(job) => {
|
|
2971
|
+
if (this.cronDisabledByTokenLimit) return;
|
|
2972
|
+
if (job.missed && detectAutonomousSentinel(job.prompt)) return;
|
|
2973
|
+
this.cronQueue.push({
|
|
2974
|
+
prompt: job.prompt,
|
|
2975
|
+
source: job.cronExpr === "@wakeup" ? "loop" : "cron"
|
|
2976
|
+
});
|
|
2977
|
+
void this.#drainCronQueue();
|
|
2978
|
+
}
|
|
2979
|
+
);
|
|
2643
2980
|
}
|
|
2644
2981
|
/**
|
|
2645
2982
|
* Processes queued cron prompts one at a time. Uses `cronProcessing`
|
|
@@ -2673,6 +3010,28 @@ ${this.pendingWorktreeNotice}
|
|
|
2673
3010
|
}
|
|
2674
3011
|
}
|
|
2675
3012
|
}
|
|
3013
|
+
#getLoopTickResolver() {
|
|
3014
|
+
const root = this.config.getWorkingDir();
|
|
3015
|
+
if (!this.loopTickResolver || this.loopTickResolverRoot !== root) {
|
|
3016
|
+
const homeQwenDir = Storage.getGlobalQwenDir();
|
|
3017
|
+
const homeConfineRoot = (process.env["QWEN_HOME"] ? homeQwenDir : os.homedir()) || path2.dirname(homeQwenDir);
|
|
3018
|
+
this.loopTickResolver = new LoopTickResolver({
|
|
3019
|
+
projectRoot: root,
|
|
3020
|
+
homeDir: homeConfineRoot,
|
|
3021
|
+
homeQwenDir,
|
|
3022
|
+
// The project `.qwen/loop.md` is repo-controlled, so an untrusted folder
|
|
3023
|
+
// must not read it and feed it to the model (mirrors getProjectHooks()'s
|
|
3024
|
+
// trust gate). The home/global `~/.qwen/loop.md` is user-owned and stays
|
|
3025
|
+
// allowed. Pass a getter, not a snapshot: isTrustedFolder() can flip
|
|
3026
|
+
// mid-session on an IDE workspace-trust update, and the resolver outlives
|
|
3027
|
+
// a single tick — re-read it on every resolve() so a trusted→untrusted
|
|
3028
|
+
// flip stops reading the project file immediately.
|
|
3029
|
+
allowProjectFile: /* @__PURE__ */ __name(() => this.config.isTrustedFolder(), "allowProjectFile")
|
|
3030
|
+
});
|
|
3031
|
+
this.loopTickResolverRoot = root;
|
|
3032
|
+
}
|
|
3033
|
+
return this.loopTickResolver;
|
|
3034
|
+
}
|
|
2676
3035
|
/**
|
|
2677
3036
|
* Executes a single cron-fired prompt: echoes it as a user message with
|
|
2678
3037
|
* `_meta.source='cron'`, streams the model response, and handles tool calls.
|
|
@@ -2703,16 +3062,65 @@ ${this.pendingWorktreeNotice}
|
|
|
2703
3062
|
async () => {
|
|
2704
3063
|
let turnCount = 0;
|
|
2705
3064
|
try {
|
|
3065
|
+
const loopMode = detectLoopSentinel(prompt);
|
|
3066
|
+
const autonomousMode = loopMode ? null : detectAutonomousSentinel(prompt);
|
|
3067
|
+
let loopTick = null;
|
|
3068
|
+
if (loopMode) {
|
|
3069
|
+
const resolver = this.#getLoopTickResolver();
|
|
3070
|
+
const trustedAtResolve = this.config.isTrustedFolder();
|
|
3071
|
+
try {
|
|
3072
|
+
loopTick = await resolver.resolve(loopMode, trustedAtResolve);
|
|
3073
|
+
} catch (resolveErr) {
|
|
3074
|
+
const code = resolveErr.code ?? "unknown";
|
|
3075
|
+
debugLogger5.warn(
|
|
3076
|
+
`loop.md sentinel resolution failed (mode=${loopMode}, code=${code}) \u2014 check .qwen/loop.md permissions/IO`,
|
|
3077
|
+
resolveErr
|
|
3078
|
+
);
|
|
3079
|
+
if (loopMode === "dynamic" && TRANSIENT_FS_CODES.includes(code)) {
|
|
3080
|
+
loopTick = resolver.buildTransientErrorTick(
|
|
3081
|
+
loopMode,
|
|
3082
|
+
trustedAtResolve,
|
|
3083
|
+
code
|
|
3084
|
+
);
|
|
3085
|
+
} else {
|
|
3086
|
+
throw new Error(
|
|
3087
|
+
`loop.md resolution failed (${code}) for ${resolver.absentLocations(
|
|
3088
|
+
trustedAtResolve
|
|
3089
|
+
)}`
|
|
3090
|
+
);
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
} else if (autonomousMode) {
|
|
3094
|
+
loopTick = this.#getLoopTickResolver().resolveAutonomous(autonomousMode);
|
|
3095
|
+
}
|
|
3096
|
+
const modelText = loopTick ? loopTick.modelText : prompt;
|
|
3097
|
+
if (loopTick) {
|
|
3098
|
+
debugLogger5.debug(
|
|
3099
|
+
`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}`
|
|
3100
|
+
);
|
|
3101
|
+
}
|
|
3102
|
+
const echoText = !loopTick ? prompt : (
|
|
3103
|
+
// An autonomous tick (a bare-`/loop` sentinel, or a loop.md
|
|
3104
|
+
// sentinel whose file is gone and converged on the preamble).
|
|
3105
|
+
loopTick.autonomous ? "Autonomous loop tick" : loopTick.sourceLabel ? `Loop tick \u2014 tasks from ${loopTick.sourceLabel}` : (
|
|
3106
|
+
// The only remaining tick is a transient read failure
|
|
3107
|
+
// (buildTransientErrorTick): a loop.md exists but couldn't be
|
|
3108
|
+
// read this tick. A genuinely-absent loop.md converges on the
|
|
3109
|
+
// autonomous branch above, so there is no "not present" echo.
|
|
3110
|
+
"Loop tick \u2014 loop.md temporarily unavailable"
|
|
3111
|
+
)
|
|
3112
|
+
);
|
|
2706
3113
|
await this.sendUpdate({
|
|
2707
3114
|
sessionUpdate: "user_message_chunk",
|
|
2708
|
-
content: { type: "text", text:
|
|
3115
|
+
content: { type: "text", text: echoText },
|
|
2709
3116
|
_meta: { source: item.source }
|
|
2710
3117
|
});
|
|
2711
3118
|
const cronReminders = await this.#buildInitialSystemReminders();
|
|
2712
3119
|
let nextMessage = {
|
|
2713
3120
|
role: "user",
|
|
2714
|
-
parts: [...cronReminders, { text:
|
|
3121
|
+
parts: [...cronReminders, { text: modelText }]
|
|
2715
3122
|
};
|
|
3123
|
+
const toolLoopState = createDaemonToolLoopState();
|
|
2716
3124
|
while (nextMessage !== null) {
|
|
2717
3125
|
turnCount++;
|
|
2718
3126
|
if (ac.signal.aborted) return;
|
|
@@ -2735,6 +3143,9 @@ ${this.pendingWorktreeNotice}
|
|
|
2735
3143
|
return;
|
|
2736
3144
|
}
|
|
2737
3145
|
const responseStream = sendResult.responseStream;
|
|
3146
|
+
if (loopTick && turnCount === 1) {
|
|
3147
|
+
this.loopTickResolver?.markDelivered();
|
|
3148
|
+
}
|
|
2738
3149
|
nextMessage = null;
|
|
2739
3150
|
for await (const resp of responseStream) {
|
|
2740
3151
|
if (ac.signal.aborted) return;
|
|
@@ -2772,7 +3183,8 @@ ${this.pendingWorktreeNotice}
|
|
|
2772
3183
|
const toolRun = await this.runToolCalls(
|
|
2773
3184
|
ac.signal,
|
|
2774
3185
|
promptId,
|
|
2775
|
-
functionCalls
|
|
3186
|
+
functionCalls,
|
|
3187
|
+
toolLoopState
|
|
2776
3188
|
);
|
|
2777
3189
|
if (toolRun.stopAfterPermissionCancel) {
|
|
2778
3190
|
await this.#preserveCancelledPermissionToolRun(
|
|
@@ -2781,19 +3193,16 @@ ${this.pendingWorktreeNotice}
|
|
|
2781
3193
|
);
|
|
2782
3194
|
return;
|
|
2783
3195
|
}
|
|
2784
|
-
nextMessage =
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
...await this.#drainMidTurnUserMessages(ac.signal)
|
|
2789
|
-
]
|
|
2790
|
-
};
|
|
3196
|
+
nextMessage = await this.#buildNextMessageAfterToolRun(
|
|
3197
|
+
toolRun,
|
|
3198
|
+
ac.signal
|
|
3199
|
+
);
|
|
2791
3200
|
}
|
|
2792
3201
|
}
|
|
2793
3202
|
} catch (error) {
|
|
2794
3203
|
if (ac.signal.aborted) return;
|
|
2795
3204
|
cronHadError = true;
|
|
2796
|
-
|
|
3205
|
+
debugLogger5.error("Error processing cron prompt:", error);
|
|
2797
3206
|
const msg = error instanceof Error ? error.message : String(error);
|
|
2798
3207
|
await this.messageEmitter.emitAgentMessage(
|
|
2799
3208
|
`[${item.source} error] ${msg}`
|
|
@@ -2877,7 +3286,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2877
3286
|
#enqueueBackgroundNotification(item) {
|
|
2878
3287
|
while (this.notificationQueue.length >= MAX_NOTIFICATION_QUEUE) {
|
|
2879
3288
|
const evicted = this.notificationQueue.shift();
|
|
2880
|
-
|
|
3289
|
+
debugLogger5.warn(
|
|
2881
3290
|
`Notification queue overflow: evicting task=${evicted.taskId} kind=${evicted.kind}`
|
|
2882
3291
|
);
|
|
2883
3292
|
}
|
|
@@ -2934,6 +3343,7 @@ ${this.pendingWorktreeNotice}
|
|
|
2934
3343
|
role: "user",
|
|
2935
3344
|
parts: [...notificationReminders, ...notificationParts]
|
|
2936
3345
|
};
|
|
3346
|
+
const toolLoopState = createDaemonToolLoopState();
|
|
2937
3347
|
while (nextMessage !== null) {
|
|
2938
3348
|
if (ac.signal.aborted) {
|
|
2939
3349
|
await this.#emitBackgroundNotificationEndTurn("cancelled");
|
|
@@ -3010,7 +3420,8 @@ ${this.pendingWorktreeNotice}
|
|
|
3010
3420
|
const toolRun = await this.runToolCalls(
|
|
3011
3421
|
ac.signal,
|
|
3012
3422
|
promptId,
|
|
3013
|
-
functionCalls
|
|
3423
|
+
functionCalls,
|
|
3424
|
+
toolLoopState
|
|
3014
3425
|
);
|
|
3015
3426
|
if (toolRun.stopAfterPermissionCancel) {
|
|
3016
3427
|
await this.#preserveCancelledPermissionToolRun(
|
|
@@ -3020,13 +3431,10 @@ ${this.pendingWorktreeNotice}
|
|
|
3020
3431
|
await this.#emitBackgroundNotificationEndTurn("end_turn");
|
|
3021
3432
|
return;
|
|
3022
3433
|
}
|
|
3023
|
-
nextMessage =
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
...await this.#drainMidTurnUserMessages(ac.signal)
|
|
3028
|
-
]
|
|
3029
|
-
};
|
|
3434
|
+
nextMessage = await this.#buildNextMessageAfterToolRun(
|
|
3435
|
+
toolRun,
|
|
3436
|
+
ac.signal
|
|
3437
|
+
);
|
|
3030
3438
|
}
|
|
3031
3439
|
}
|
|
3032
3440
|
if (this.messageRewriter) {
|
|
@@ -3038,14 +3446,14 @@ ${this.pendingWorktreeNotice}
|
|
|
3038
3446
|
await this.#emitBackgroundNotificationEndTurn("cancelled");
|
|
3039
3447
|
return;
|
|
3040
3448
|
}
|
|
3041
|
-
|
|
3449
|
+
debugLogger5.error("Error processing background notification:", error);
|
|
3042
3450
|
const msg = error instanceof Error ? error.message : String(error);
|
|
3043
3451
|
try {
|
|
3044
3452
|
await this.messageEmitter.emitAgentMessage(
|
|
3045
3453
|
`[notification error] ${msg}`
|
|
3046
3454
|
);
|
|
3047
3455
|
} catch (emitError) {
|
|
3048
|
-
|
|
3456
|
+
debugLogger5.error(
|
|
3049
3457
|
"Failed to emit background notification error:",
|
|
3050
3458
|
emitError
|
|
3051
3459
|
);
|
|
@@ -3105,7 +3513,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3105
3513
|
source: "background_notification"
|
|
3106
3514
|
});
|
|
3107
3515
|
} catch (error) {
|
|
3108
|
-
|
|
3516
|
+
debugLogger5.debug(
|
|
3109
3517
|
`Background notification end-turn extNotification dropped: ${this.#formatError(error)}`
|
|
3110
3518
|
);
|
|
3111
3519
|
}
|
|
@@ -3129,7 +3537,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3129
3537
|
};
|
|
3130
3538
|
await this.sendUpdate(update);
|
|
3131
3539
|
} catch (error) {
|
|
3132
|
-
|
|
3540
|
+
debugLogger5.error("Error sending available commands update:", error);
|
|
3133
3541
|
}
|
|
3134
3542
|
}
|
|
3135
3543
|
/**
|
|
@@ -3164,7 +3572,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3164
3572
|
sessionId: this.sessionId,
|
|
3165
3573
|
currentModeId: params.modeId
|
|
3166
3574
|
}).catch((error) => {
|
|
3167
|
-
|
|
3575
|
+
debugLogger5.debug("mode-update extNotification failed", error);
|
|
3168
3576
|
});
|
|
3169
3577
|
}
|
|
3170
3578
|
/**
|
|
@@ -3198,7 +3606,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3198
3606
|
sessionId: this.sessionId,
|
|
3199
3607
|
currentModelId: effectiveModelId
|
|
3200
3608
|
}).catch((error) => {
|
|
3201
|
-
|
|
3609
|
+
debugLogger5.debug("model-update extNotification failed", error);
|
|
3202
3610
|
});
|
|
3203
3611
|
if (options.persistDefault ?? true) {
|
|
3204
3612
|
const persistScope = getPersistScopeForModelSelection(this.settings);
|
|
@@ -3251,7 +3659,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3251
3659
|
currentModeId: newModeId,
|
|
3252
3660
|
legacyFrameSent: true
|
|
3253
3661
|
}).catch((error) => {
|
|
3254
|
-
|
|
3662
|
+
debugLogger5.debug("mode-update extNotification failed", error);
|
|
3255
3663
|
});
|
|
3256
3664
|
}
|
|
3257
3665
|
/**
|
|
@@ -3264,16 +3672,50 @@ ${this.pendingWorktreeNotice}
|
|
|
3264
3672
|
* sequential batch to preserve the implicit ordering the model may rely
|
|
3265
3673
|
* on. Response-part ordering matches the original `functionCalls` order.
|
|
3266
3674
|
*/
|
|
3267
|
-
async runToolCalls(abortSignal, promptId, functionCalls) {
|
|
3675
|
+
async runToolCalls(abortSignal, promptId, functionCalls, toolLoopState) {
|
|
3676
|
+
if (recordDaemonToolCalls(
|
|
3677
|
+
this.config,
|
|
3678
|
+
promptId,
|
|
3679
|
+
toolLoopState,
|
|
3680
|
+
functionCalls.length
|
|
3681
|
+
)) {
|
|
3682
|
+
return {
|
|
3683
|
+
parts: [],
|
|
3684
|
+
stopAfterPermissionCancel: false,
|
|
3685
|
+
loopDetected: true
|
|
3686
|
+
};
|
|
3687
|
+
}
|
|
3268
3688
|
const dedupedFunctionCalls = dedupeToolCallsById(functionCalls);
|
|
3269
3689
|
const batches = [];
|
|
3270
3690
|
const handledProviderToolCallIds = new Set(
|
|
3271
3691
|
this.#getCurrentChat().getHistoryFunctionResponseIds()
|
|
3272
3692
|
);
|
|
3693
|
+
const repeatedDuplicateCall = findRepeatedDuplicateProviderToolCall(
|
|
3694
|
+
dedupedFunctionCalls,
|
|
3695
|
+
(fc) => getProviderToolCallId(fc) ?? fc.id,
|
|
3696
|
+
handledProviderToolCallIds,
|
|
3697
|
+
this.duplicateProviderToolCallResponseIds
|
|
3698
|
+
);
|
|
3699
|
+
if (repeatedDuplicateCall) {
|
|
3700
|
+
const providerCallId = getProviderToolCallId(repeatedDuplicateCall) ?? repeatedDuplicateCall.id;
|
|
3701
|
+
debugLogger5.debug(
|
|
3702
|
+
`[Session.runToolCalls] Dropping batch after repeated duplicate provider tool-call id: ${providerCallId} (tool: ${repeatedDuplicateCall.name ?? "unknown_tool"})`
|
|
3703
|
+
);
|
|
3704
|
+
return {
|
|
3705
|
+
parts: [],
|
|
3706
|
+
stopAfterPermissionCancel: false,
|
|
3707
|
+
repeatedDuplicateProviderToolCall: true
|
|
3708
|
+
};
|
|
3709
|
+
}
|
|
3273
3710
|
const pushDuplicateBatch = /* @__PURE__ */ __name((request) => {
|
|
3711
|
+
const providerCallId = request.providerCallId ?? request.callId;
|
|
3712
|
+
markDuplicateProviderToolCallResponseSent(
|
|
3713
|
+
providerCallId,
|
|
3714
|
+
this.duplicateProviderToolCallResponseIds
|
|
3715
|
+
);
|
|
3274
3716
|
const response = createDuplicateProviderToolCallResponse(request);
|
|
3275
|
-
|
|
3276
|
-
`[Session.runToolCalls] Suppressing duplicate provider tool-call id: ${
|
|
3717
|
+
debugLogger5.debug(
|
|
3718
|
+
`[Session.runToolCalls] Suppressing duplicate provider tool-call id: ${providerCallId} (tool: ${request.name})`
|
|
3277
3719
|
);
|
|
3278
3720
|
batches.push({ kind: "duplicate", request, response });
|
|
3279
3721
|
}, "pushDuplicateBatch");
|
|
@@ -3373,7 +3815,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3373
3815
|
});
|
|
3374
3816
|
await this.toolCallEmitter.emitError(callId, toolName, error);
|
|
3375
3817
|
} catch (recordError) {
|
|
3376
|
-
|
|
3818
|
+
debugLogger5.error("Failed to record skipped tool call:", recordError);
|
|
3377
3819
|
}
|
|
3378
3820
|
return part;
|
|
3379
3821
|
}, "recordSkippedToolCall");
|
|
@@ -3403,7 +3845,8 @@ ${this.pendingWorktreeNotice}
|
|
|
3403
3845
|
runAbortSignal,
|
|
3404
3846
|
promptId,
|
|
3405
3847
|
calls[idx],
|
|
3406
|
-
onStopAfterPermissionCancel
|
|
3848
|
+
onStopAfterPermissionCancel,
|
|
3849
|
+
toolLoopState
|
|
3407
3850
|
).then((r) => {
|
|
3408
3851
|
results[idx] = r;
|
|
3409
3852
|
}).finally(() => {
|
|
@@ -3453,26 +3896,60 @@ ${this.pendingWorktreeNotice}
|
|
|
3453
3896
|
abortSignal.removeEventListener("abort", propagateAbort);
|
|
3454
3897
|
}
|
|
3455
3898
|
let shouldStop = false;
|
|
3899
|
+
let shouldStopForLoop = false;
|
|
3456
3900
|
for (const r of results) {
|
|
3457
3901
|
parts.push(...r.parts);
|
|
3458
3902
|
shouldStop ||= r.stopAfterPermissionCancel;
|
|
3903
|
+
shouldStopForLoop ||= r.loopDetected === true;
|
|
3904
|
+
}
|
|
3905
|
+
if (shouldStopForLoop) {
|
|
3906
|
+
return {
|
|
3907
|
+
parts,
|
|
3908
|
+
stopAfterPermissionCancel: false,
|
|
3909
|
+
loopDetected: true
|
|
3910
|
+
};
|
|
3459
3911
|
}
|
|
3460
3912
|
if (shouldStop) {
|
|
3461
3913
|
await appendSkippedAfter(parts, batch.calls[batch.calls.length - 1]);
|
|
3462
|
-
return {
|
|
3914
|
+
return {
|
|
3915
|
+
parts,
|
|
3916
|
+
stopAfterPermissionCancel: true,
|
|
3917
|
+
repeatedDuplicateProviderToolCall: false
|
|
3918
|
+
};
|
|
3463
3919
|
}
|
|
3464
3920
|
} else {
|
|
3465
3921
|
for (const fc of batch.calls) {
|
|
3466
|
-
const r = await this.runTool(
|
|
3922
|
+
const r = await this.runTool(
|
|
3923
|
+
abortSignal,
|
|
3924
|
+
promptId,
|
|
3925
|
+
fc,
|
|
3926
|
+
void 0,
|
|
3927
|
+
toolLoopState
|
|
3928
|
+
);
|
|
3467
3929
|
parts.push(...r.parts);
|
|
3930
|
+
if (r.loopDetected) {
|
|
3931
|
+
return {
|
|
3932
|
+
parts,
|
|
3933
|
+
stopAfterPermissionCancel: false,
|
|
3934
|
+
loopDetected: true
|
|
3935
|
+
};
|
|
3936
|
+
}
|
|
3468
3937
|
if (r.stopAfterPermissionCancel) {
|
|
3469
3938
|
await appendSkippedAfter(parts, fc);
|
|
3470
|
-
return {
|
|
3939
|
+
return {
|
|
3940
|
+
parts,
|
|
3941
|
+
stopAfterPermissionCancel: true,
|
|
3942
|
+
repeatedDuplicateProviderToolCall: false
|
|
3943
|
+
};
|
|
3471
3944
|
}
|
|
3472
3945
|
}
|
|
3473
3946
|
}
|
|
3474
3947
|
}
|
|
3475
|
-
return {
|
|
3948
|
+
return {
|
|
3949
|
+
parts,
|
|
3950
|
+
stopAfterPermissionCancel: false,
|
|
3951
|
+
repeatedDuplicateProviderToolCall: false
|
|
3952
|
+
};
|
|
3476
3953
|
}
|
|
3477
3954
|
/**
|
|
3478
3955
|
* Assemble the per-turn system reminders the model needs to see at the
|
|
@@ -3504,7 +3981,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3504
3981
|
}
|
|
3505
3982
|
return reminders;
|
|
3506
3983
|
}
|
|
3507
|
-
async runTool(abortSignal, promptId, fc, onStopAfterPermissionCancel) {
|
|
3984
|
+
async runTool(abortSignal, promptId, fc, onStopAfterPermissionCancel, toolLoopState) {
|
|
3508
3985
|
const callId = fc.id ?? `${fc.name}-${Date.now()}`;
|
|
3509
3986
|
let args = fc.args ?? {};
|
|
3510
3987
|
const startTime = Date.now();
|
|
@@ -3616,8 +4093,10 @@ ${this.pendingWorktreeNotice}
|
|
|
3616
4093
|
}
|
|
3617
4094
|
const toolUseId = generateToolUseId();
|
|
3618
4095
|
const approvalMode = this.config.getApprovalMode();
|
|
4096
|
+
let toolBuildSucceeded = false;
|
|
3619
4097
|
try {
|
|
3620
4098
|
const invocation = tool.build(args);
|
|
4099
|
+
toolBuildSucceeded = true;
|
|
3621
4100
|
const taskEventEmitter = invocation.eventEmitter;
|
|
3622
4101
|
if (isAgentTool && taskEventEmitter) {
|
|
3623
4102
|
const parentToolCallId = callId;
|
|
@@ -3660,7 +4139,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3660
4139
|
forceAutoReviewForAllow
|
|
3661
4140
|
);
|
|
3662
4141
|
if (finalPermission === "allow" && forceAutoReviewForAllow) {
|
|
3663
|
-
|
|
4142
|
+
debugLogger5.info(
|
|
3664
4143
|
`Auto mode: L4 allow overridden by protected-write guard for ${toolName}`
|
|
3665
4144
|
);
|
|
3666
4145
|
}
|
|
@@ -3703,7 +4182,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3703
4182
|
autoModeAllowed = true;
|
|
3704
4183
|
break;
|
|
3705
4184
|
case "blocked":
|
|
3706
|
-
|
|
4185
|
+
debugLogger5.warn(
|
|
3707
4186
|
`Auto mode blocked (${outcome.reason}): tool=${toolName}, ` + formatDenialStateLog(denialState)
|
|
3708
4187
|
);
|
|
3709
4188
|
return earlyErrorResponse(
|
|
@@ -3715,7 +4194,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3715
4194
|
outcome.reason
|
|
3716
4195
|
);
|
|
3717
4196
|
if (wasAutoModeDenialFallback) {
|
|
3718
|
-
|
|
4197
|
+
debugLogger5.warn(
|
|
3719
4198
|
`Auto mode fallback to manual approval (${outcome.reason}): ` + formatDenialStateLog(denialState)
|
|
3720
4199
|
);
|
|
3721
4200
|
}
|
|
@@ -3733,12 +4212,12 @@ ${this.pendingWorktreeNotice}
|
|
|
3733
4212
|
const before = this.config.getAutoModeDenialState();
|
|
3734
4213
|
const after = recordFallbackApprove(before);
|
|
3735
4214
|
if (after === before) {
|
|
3736
|
-
|
|
4215
|
+
debugLogger5.warn(
|
|
3737
4216
|
`Auto mode denial counters already clear after fallback approval: ` + formatDenialStateLog(before)
|
|
3738
4217
|
);
|
|
3739
4218
|
return;
|
|
3740
4219
|
}
|
|
3741
|
-
|
|
4220
|
+
debugLogger5.warn(
|
|
3742
4221
|
`Auto mode denial counters reset after fallback approval: ${formatDenialStateLog(before)} -> ${formatDenialStateLog(after)}`
|
|
3743
4222
|
);
|
|
3744
4223
|
this.config.setAutoModeDenialState(after);
|
|
@@ -3844,7 +4323,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3844
4323
|
);
|
|
3845
4324
|
outcome = output.outcome.outcome === "cancelled" ? "cancel" /* Cancel */ : external_exports.nativeEnum(ToolConfirmationOutcome).parse(output.outcome.optionId);
|
|
3846
4325
|
} catch (error) {
|
|
3847
|
-
|
|
4326
|
+
debugLogger5.error(
|
|
3848
4327
|
`Permission request failed for tool ${toolName}:`,
|
|
3849
4328
|
error
|
|
3850
4329
|
);
|
|
@@ -3853,7 +4332,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3853
4332
|
"cancel" /* Cancel */
|
|
3854
4333
|
);
|
|
3855
4334
|
} catch (confirmError) {
|
|
3856
|
-
|
|
4335
|
+
debugLogger5.error(
|
|
3857
4336
|
`Failed to cancel tool ${toolName} after permission request failure:`,
|
|
3858
4337
|
confirmError
|
|
3859
4338
|
);
|
|
@@ -3878,7 +4357,7 @@ ${this.pendingWorktreeNotice}
|
|
|
3878
4357
|
if (outcome !== "cancel" /* Cancel */) {
|
|
3879
4358
|
throw error;
|
|
3880
4359
|
}
|
|
3881
|
-
|
|
4360
|
+
debugLogger5.error(
|
|
3882
4361
|
`Failed to confirm cancellation for tool ${toolName}:`,
|
|
3883
4362
|
error
|
|
3884
4363
|
);
|
|
@@ -3943,12 +4422,12 @@ ${this.pendingWorktreeNotice}
|
|
|
3943
4422
|
if (!preHookResult.shouldProceed) {
|
|
3944
4423
|
const blockReason = preHookResult.blockReason || "Blocked by PreToolUse hook";
|
|
3945
4424
|
await this.messageEmitter.emitAgentMessage(
|
|
3946
|
-
`\
|
|
4425
|
+
`\u2717 **PreToolUse blocked**: ${toolName} - ${blockReason}`
|
|
3947
4426
|
);
|
|
3948
4427
|
return earlyErrorResponse(new Error(blockReason), toolName);
|
|
3949
4428
|
}
|
|
3950
4429
|
if (preHookResult.additionalContext) {
|
|
3951
|
-
|
|
4430
|
+
debugLogger5.debug(
|
|
3952
4431
|
`PreToolUse hook additional context for ${toolName}: ${preHookResult.additionalContext}`
|
|
3953
4432
|
);
|
|
3954
4433
|
}
|
|
@@ -4011,7 +4490,7 @@ ${this.pendingWorktreeNotice}
|
|
|
4011
4490
|
);
|
|
4012
4491
|
if (postHookResult.shouldStop) {
|
|
4013
4492
|
const stopMessage = postHookResult.stopReason || "Execution stopped by PostToolUse hook";
|
|
4014
|
-
|
|
4493
|
+
debugLogger5.info(
|
|
4015
4494
|
`PostToolUse hook requested stop for ${toolName}: ${stopMessage}`
|
|
4016
4495
|
);
|
|
4017
4496
|
return earlyErrorResponse(new Error(stopMessage), toolName);
|
|
@@ -4034,7 +4513,7 @@ ${this.pendingWorktreeNotice}
|
|
|
4034
4513
|
callId
|
|
4035
4514
|
);
|
|
4036
4515
|
if (failureHookResult.additionalContext) {
|
|
4037
|
-
|
|
4516
|
+
debugLogger5.debug(
|
|
4038
4517
|
`PostToolUseFailure hook additional context for ${toolName}: ${failureHookResult.additionalContext}`
|
|
4039
4518
|
);
|
|
4040
4519
|
}
|
|
@@ -4110,7 +4589,7 @@ ${this.pendingWorktreeNotice}
|
|
|
4110
4589
|
callId
|
|
4111
4590
|
);
|
|
4112
4591
|
if (failureHookResult.additionalContext) {
|
|
4113
|
-
|
|
4592
|
+
debugLogger5.debug(
|
|
4114
4593
|
`PostToolUseFailure hook additional context for ${toolName}: ${failureHookResult.additionalContext}`
|
|
4115
4594
|
);
|
|
4116
4595
|
}
|
|
@@ -4134,9 +4613,17 @@ ${this.pendingWorktreeNotice}
|
|
|
4134
4613
|
error,
|
|
4135
4614
|
errorType: void 0
|
|
4136
4615
|
});
|
|
4616
|
+
const loopDetected = !activeToolAbortSignal.aborted && !toolBuildSucceeded && recordDaemonInvalidToolParams(
|
|
4617
|
+
this.config,
|
|
4618
|
+
promptId,
|
|
4619
|
+
toolLoopState,
|
|
4620
|
+
toolName,
|
|
4621
|
+
error
|
|
4622
|
+
);
|
|
4137
4623
|
return {
|
|
4138
4624
|
parts: errorResponse(error),
|
|
4139
|
-
stopAfterPermissionCancel: nestedPermissionCancelled
|
|
4625
|
+
stopAfterPermissionCancel: nestedPermissionCancelled,
|
|
4626
|
+
loopDetected
|
|
4140
4627
|
};
|
|
4141
4628
|
}
|
|
4142
4629
|
});
|
|
@@ -4245,9 +4732,11 @@ ${this.pendingWorktreeNotice}
|
|
|
4245
4732
|
async #resolvePrompt(message, abortSignal) {
|
|
4246
4733
|
const FILE_URI_SCHEME = "file://";
|
|
4247
4734
|
const embeddedContext = [];
|
|
4735
|
+
const extensionMentions = /* @__PURE__ */ new Map();
|
|
4248
4736
|
const parts = message.map((part) => {
|
|
4249
4737
|
switch (part.type) {
|
|
4250
4738
|
case "text":
|
|
4739
|
+
collectExtensionMentionRefs(part.text, extensionMentions);
|
|
4251
4740
|
return { text: part.text };
|
|
4252
4741
|
case "image":
|
|
4253
4742
|
case "audio":
|
|
@@ -4281,9 +4770,16 @@ ${this.pendingWorktreeNotice}
|
|
|
4281
4770
|
}
|
|
4282
4771
|
});
|
|
4283
4772
|
const atPathCommandParts = parts.filter((part) => "fileData" in part);
|
|
4284
|
-
|
|
4773
|
+
const extensionParts = await this.#resolveExtensionMentionParts(
|
|
4774
|
+
extensionMentions,
|
|
4775
|
+
abortSignal
|
|
4776
|
+
);
|
|
4777
|
+
if (atPathCommandParts.length === 0 && embeddedContext.length === 0 && extensionParts.length === 0) {
|
|
4285
4778
|
return parts;
|
|
4286
4779
|
}
|
|
4780
|
+
if (atPathCommandParts.length === 0 && embeddedContext.length === 0) {
|
|
4781
|
+
return [...parts, ...extensionParts];
|
|
4782
|
+
}
|
|
4287
4783
|
const pathSpecsToRead = atPathCommandParts.map(
|
|
4288
4784
|
(part) => part.fileData.fileUri
|
|
4289
4785
|
);
|
|
@@ -4308,6 +4804,7 @@ ${this.pendingWorktreeNotice}
|
|
|
4308
4804
|
});
|
|
4309
4805
|
const contentParts = Array.isArray(readResult.contentParts) ? readResult.contentParts : [readResult.contentParts];
|
|
4310
4806
|
processedQueryParts.push({ text: initialQueryText });
|
|
4807
|
+
processedQueryParts.push(...extensionParts);
|
|
4311
4808
|
for (const part of contentParts) {
|
|
4312
4809
|
if (typeof part === "string") {
|
|
4313
4810
|
processedQueryParts.push({ text: part });
|
|
@@ -4317,6 +4814,7 @@ ${this.pendingWorktreeNotice}
|
|
|
4317
4814
|
}
|
|
4318
4815
|
} else {
|
|
4319
4816
|
processedQueryParts.push({ text: initialQueryText.trim() });
|
|
4817
|
+
processedQueryParts.push(...extensionParts);
|
|
4320
4818
|
}
|
|
4321
4819
|
for (const contextPart of embeddedContext) {
|
|
4322
4820
|
if ("text" in contextPart && contextPart.text) {
|
|
@@ -4338,9 +4836,36 @@ ${contextPart.text}`
|
|
|
4338
4836
|
}
|
|
4339
4837
|
return processedQueryParts;
|
|
4340
4838
|
}
|
|
4839
|
+
async #resolveExtensionMentionParts(extensionMentions, abortSignal) {
|
|
4840
|
+
if (extensionMentions.size === 0) return [];
|
|
4841
|
+
const activeExtensions = this.config.getActiveExtensions?.() ?? [];
|
|
4842
|
+
if (activeExtensions.length === 0) return [];
|
|
4843
|
+
const extensionParts = [];
|
|
4844
|
+
const resolvedExtensionNames = /* @__PURE__ */ new Set();
|
|
4845
|
+
let remainingBudget = EXTENSION_CONTEXT_BUDGET;
|
|
4846
|
+
for (const name of extensionMentions.values()) {
|
|
4847
|
+
const extension = matchExtensionByRef(name, activeExtensions);
|
|
4848
|
+
if (!extension) {
|
|
4849
|
+
this.debug(
|
|
4850
|
+
`Extension "${name}" not found among active extensions. Available: ${activeExtensions.map((e) => e.name).join(", ") || "(none)"}`
|
|
4851
|
+
);
|
|
4852
|
+
continue;
|
|
4853
|
+
}
|
|
4854
|
+
if (resolvedExtensionNames.has(extension.name)) continue;
|
|
4855
|
+
resolvedExtensionNames.add(extension.name);
|
|
4856
|
+
const context = await buildExtensionMentionContext(extension, {
|
|
4857
|
+
remainingBudget,
|
|
4858
|
+
signal: abortSignal,
|
|
4859
|
+
onDebugMessage: /* @__PURE__ */ __name((message) => this.debug(message), "onDebugMessage")
|
|
4860
|
+
});
|
|
4861
|
+
remainingBudget = context.remainingBudget;
|
|
4862
|
+
extensionParts.push({ text: context.text });
|
|
4863
|
+
}
|
|
4864
|
+
return extensionParts;
|
|
4865
|
+
}
|
|
4341
4866
|
debug(msg) {
|
|
4342
4867
|
if (this.config.getDebugMode()) {
|
|
4343
|
-
|
|
4868
|
+
debugLogger5.warn(msg);
|
|
4344
4869
|
}
|
|
4345
4870
|
}
|
|
4346
4871
|
/**
|
|
@@ -4360,7 +4885,7 @@ ${contextPart.text}`
|
|
|
4360
4885
|
}
|
|
4361
4886
|
);
|
|
4362
4887
|
}).catch((err) => {
|
|
4363
|
-
|
|
4888
|
+
debugLogger5.debug(
|
|
4364
4889
|
`ACP terminalSequence notification dropped (session=${this.sessionId}): ${err instanceof Error ? err.message : String(err)}`
|
|
4365
4890
|
);
|
|
4366
4891
|
});
|
|
@@ -4471,7 +4996,7 @@ function runWithAcpRuntimeOutputDir(settings, cwd, fn) {
|
|
|
4471
4996
|
__name(runWithAcpRuntimeOutputDir, "runWithAcpRuntimeOutputDir");
|
|
4472
4997
|
|
|
4473
4998
|
// packages/cli/src/acp-integration/acpAgent.ts
|
|
4474
|
-
var
|
|
4999
|
+
var debugLogger6 = createDebugLogger("ACP_AGENT");
|
|
4475
5000
|
var BTW_CHILD_TIMEOUT_MS = 55e3;
|
|
4476
5001
|
function collapseForkDirective(directive, maxLength) {
|
|
4477
5002
|
const oneLine = directive.replace(/\s+/g, " ").trim();
|
|
@@ -4527,6 +5052,9 @@ var QWEN_CORE_SETTING_DEFINITIONS = {
|
|
|
4527
5052
|
"memory.enableManagedAutoMemory": { type: "boolean" },
|
|
4528
5053
|
"memory.enableManagedAutoDream": { type: "boolean" },
|
|
4529
5054
|
"memory.enableAutoSkill": { type: "boolean" },
|
|
5055
|
+
"memory.autoSkillConfirm": { type: "boolean" },
|
|
5056
|
+
"memory.enableTeamMemory": { type: "boolean" },
|
|
5057
|
+
"memory.enableTeamMemorySync": { type: "boolean" },
|
|
4530
5058
|
disableAllHooks: { type: "boolean" }
|
|
4531
5059
|
};
|
|
4532
5060
|
var QWEN_CORE_SETTING_KEYS = Object.keys(
|
|
@@ -4536,12 +5064,18 @@ var QWEN_HOOK_EVENTS = Object.values(HookEventName);
|
|
|
4536
5064
|
var DEFAULT_QWEN_MEMORY_SETTINGS = {
|
|
4537
5065
|
enableManagedAutoMemory: true,
|
|
4538
5066
|
enableManagedAutoDream: true,
|
|
4539
|
-
enableAutoSkill: true
|
|
5067
|
+
enableAutoSkill: true,
|
|
5068
|
+
autoSkillConfirm: true,
|
|
5069
|
+
enableTeamMemory: false,
|
|
5070
|
+
enableTeamMemorySync: false
|
|
4540
5071
|
};
|
|
4541
5072
|
var QWEN_MEMORY_SETTING_KEYS = [
|
|
4542
5073
|
"enableManagedAutoMemory",
|
|
4543
5074
|
"enableManagedAutoDream",
|
|
4544
|
-
"enableAutoSkill"
|
|
5075
|
+
"enableAutoSkill",
|
|
5076
|
+
"autoSkillConfirm",
|
|
5077
|
+
"enableTeamMemory",
|
|
5078
|
+
"enableTeamMemorySync"
|
|
4545
5079
|
];
|
|
4546
5080
|
function normalizeQwenMemorySettings(value) {
|
|
4547
5081
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
@@ -4551,7 +5085,10 @@ function normalizeQwenMemorySettings(value) {
|
|
|
4551
5085
|
return {
|
|
4552
5086
|
enableManagedAutoMemory: typeof record["enableManagedAutoMemory"] === "boolean" ? record["enableManagedAutoMemory"] : DEFAULT_QWEN_MEMORY_SETTINGS.enableManagedAutoMemory,
|
|
4553
5087
|
enableManagedAutoDream: typeof record["enableManagedAutoDream"] === "boolean" ? record["enableManagedAutoDream"] : DEFAULT_QWEN_MEMORY_SETTINGS.enableManagedAutoDream,
|
|
4554
|
-
enableAutoSkill: typeof record["enableAutoSkill"] === "boolean" ? record["enableAutoSkill"] : DEFAULT_QWEN_MEMORY_SETTINGS.enableAutoSkill
|
|
5088
|
+
enableAutoSkill: typeof record["enableAutoSkill"] === "boolean" ? record["enableAutoSkill"] : DEFAULT_QWEN_MEMORY_SETTINGS.enableAutoSkill,
|
|
5089
|
+
autoSkillConfirm: typeof record["autoSkillConfirm"] === "boolean" ? record["autoSkillConfirm"] : DEFAULT_QWEN_MEMORY_SETTINGS.autoSkillConfirm,
|
|
5090
|
+
enableTeamMemory: typeof record["enableTeamMemory"] === "boolean" ? record["enableTeamMemory"] : DEFAULT_QWEN_MEMORY_SETTINGS.enableTeamMemory,
|
|
5091
|
+
enableTeamMemorySync: typeof record["enableTeamMemorySync"] === "boolean" ? record["enableTeamMemorySync"] : DEFAULT_QWEN_MEMORY_SETTINGS.enableTeamMemorySync
|
|
4555
5092
|
};
|
|
4556
5093
|
}
|
|
4557
5094
|
__name(normalizeQwenMemorySettings, "normalizeQwenMemorySettings");
|
|
@@ -4583,7 +5120,7 @@ function readRequiredString(value, fieldName) {
|
|
|
4583
5120
|
}
|
|
4584
5121
|
__name(readRequiredString, "readRequiredString");
|
|
4585
5122
|
function validateSkillSlug(slug) {
|
|
4586
|
-
if (!slug || slug === "." || slug === ".." || slug.includes("/") || slug.includes(
|
|
5123
|
+
if (!slug || slug === "." || slug === ".." || slug.includes("/") || slug.includes(path3.sep) || !/^[a-zA-Z0-9._-]+$/.test(slug)) {
|
|
4587
5124
|
throw RequestError.invalidParams(void 0, "Invalid skill.slug");
|
|
4588
5125
|
}
|
|
4589
5126
|
}
|
|
@@ -5052,7 +5589,7 @@ async function downloadGitHubSkillDirectoryFromApi(githubUrl, directoryPath, rel
|
|
|
5052
5589
|
const name = readRequiredString(record["name"], "github.name");
|
|
5053
5590
|
const itemPath = readRequiredString(record["path"], "github.path");
|
|
5054
5591
|
const type = readRequiredString(record["type"], "github.type");
|
|
5055
|
-
const relativePath = relativeRoot ?
|
|
5592
|
+
const relativePath = relativeRoot ? path3.posix.join(relativeRoot, name) : name;
|
|
5056
5593
|
if (type === "dir") {
|
|
5057
5594
|
files.push(
|
|
5058
5595
|
...await downloadGitHubSkillDirectoryFromApi(
|
|
@@ -5099,7 +5636,7 @@ async function downloadGitHubSkillDirectory(githubUrl, directoryPath) {
|
|
|
5099
5636
|
githubUrl,
|
|
5100
5637
|
directoryPath
|
|
5101
5638
|
).catch((error) => {
|
|
5102
|
-
|
|
5639
|
+
debugLogger6.warn(
|
|
5103
5640
|
"GitHub API directory listing failed, falling back to archive download:",
|
|
5104
5641
|
error
|
|
5105
5642
|
);
|
|
@@ -5112,10 +5649,10 @@ __name(downloadGitHubSkillDirectory, "downloadGitHubSkillDirectory");
|
|
|
5112
5649
|
async function downloadSkill(sourceUrl) {
|
|
5113
5650
|
assertAllowedSkillSourceUrl(sourceUrl);
|
|
5114
5651
|
const githubUrl = parseGitHubBlobSkillUrl(sourceUrl);
|
|
5115
|
-
if (!githubUrl ||
|
|
5652
|
+
if (!githubUrl || path3.posix.basename(githubUrl.filePath) !== "SKILL.md") {
|
|
5116
5653
|
return downloadSingleSkillFile(sourceUrl);
|
|
5117
5654
|
}
|
|
5118
|
-
const skillDirectory =
|
|
5655
|
+
const skillDirectory = path3.posix.dirname(githubUrl.filePath);
|
|
5119
5656
|
const files = await downloadGitHubSkillDirectory(githubUrl, skillDirectory);
|
|
5120
5657
|
const skillFile = files.find((file) => file.relativePath === "SKILL.md");
|
|
5121
5658
|
if (!skillFile) {
|
|
@@ -5131,9 +5668,9 @@ async function downloadSkill(sourceUrl) {
|
|
|
5131
5668
|
}
|
|
5132
5669
|
__name(downloadSkill, "downloadSkill");
|
|
5133
5670
|
function resolveSkillInstallPath(skillDir, relativePath) {
|
|
5134
|
-
const root =
|
|
5135
|
-
const target =
|
|
5136
|
-
if (target !== root && !target.startsWith(root +
|
|
5671
|
+
const root = path3.resolve(skillDir);
|
|
5672
|
+
const target = path3.resolve(skillDir, relativePath);
|
|
5673
|
+
if (target !== root && !target.startsWith(root + path3.sep)) {
|
|
5137
5674
|
throw RequestError.invalidParams(
|
|
5138
5675
|
void 0,
|
|
5139
5676
|
`Invalid skill file path: ${relativePath}`
|
|
@@ -5143,9 +5680,9 @@ function resolveSkillInstallPath(skillDir, relativePath) {
|
|
|
5143
5680
|
}
|
|
5144
5681
|
__name(resolveSkillInstallPath, "resolveSkillInstallPath");
|
|
5145
5682
|
function resolveManagedSkillDir(skillsBaseDir, slug) {
|
|
5146
|
-
const root =
|
|
5147
|
-
const skillDir =
|
|
5148
|
-
if (!skillDir.startsWith(root +
|
|
5683
|
+
const root = path3.resolve(skillsBaseDir);
|
|
5684
|
+
const skillDir = path3.resolve(skillsBaseDir, slug);
|
|
5685
|
+
if (!skillDir.startsWith(root + path3.sep)) {
|
|
5149
5686
|
throw RequestError.invalidParams(void 0, "Invalid skill.slug");
|
|
5150
5687
|
}
|
|
5151
5688
|
return skillDir;
|
|
@@ -5701,7 +6238,7 @@ function readMcpServers(source, scope) {
|
|
|
5701
6238
|
const server = toMcpServerConfig(value);
|
|
5702
6239
|
return server ? { name, scope, server: redactMcpServerSecrets(server) } : void 0;
|
|
5703
6240
|
} catch (error) {
|
|
5704
|
-
|
|
6241
|
+
debugLogger6.warn(
|
|
5705
6242
|
`Skipping malformed MCP server config [${scope}:${name}]:`,
|
|
5706
6243
|
error
|
|
5707
6244
|
);
|
|
@@ -5797,7 +6334,7 @@ function readHooks(source, scope, extensionName) {
|
|
|
5797
6334
|
extensionName
|
|
5798
6335
|
});
|
|
5799
6336
|
} catch (error) {
|
|
5800
|
-
|
|
6337
|
+
debugLogger6.warn(
|
|
5801
6338
|
`Skipping malformed hook entry [${scope}:${event}:${index}]:`,
|
|
5802
6339
|
error
|
|
5803
6340
|
);
|
|
@@ -5822,14 +6359,14 @@ function readScopeSettings(settings, scope) {
|
|
|
5822
6359
|
__name(readScopeSettings, "readScopeSettings");
|
|
5823
6360
|
async function resolvePreferredMemoryFile(dir, fallbackFilename) {
|
|
5824
6361
|
for (const filename of getAllGeminiMdFilenames()) {
|
|
5825
|
-
const filePath =
|
|
6362
|
+
const filePath = path3.join(dir, filename);
|
|
5826
6363
|
try {
|
|
5827
6364
|
await fs.access(filePath);
|
|
5828
6365
|
return filePath;
|
|
5829
6366
|
} catch {
|
|
5830
6367
|
}
|
|
5831
6368
|
}
|
|
5832
|
-
return
|
|
6369
|
+
return path3.join(dir, fallbackFilename);
|
|
5833
6370
|
}
|
|
5834
6371
|
__name(resolvePreferredMemoryFile, "resolvePreferredMemoryFile");
|
|
5835
6372
|
async function resolveQwenMemoryPaths(params) {
|
|
@@ -5850,13 +6387,37 @@ async function resolveQwenMemoryPaths(params) {
|
|
|
5850
6387
|
};
|
|
5851
6388
|
}
|
|
5852
6389
|
__name(resolveQwenMemoryPaths, "resolveQwenMemoryPaths");
|
|
6390
|
+
async function deliverClientMcpMessage(connection, serverName, message) {
|
|
6391
|
+
if (!connection) {
|
|
6392
|
+
throw new Error(
|
|
6393
|
+
`client MCP server '${serverName}' has no ACP connection yet`
|
|
6394
|
+
);
|
|
6395
|
+
}
|
|
6396
|
+
const response = await connection.extMethod(
|
|
6397
|
+
SERVE_CONTROL_EXT_METHODS.clientMcpMessage,
|
|
6398
|
+
{ server: serverName, payload: message }
|
|
6399
|
+
);
|
|
6400
|
+
const payload = response["payload"];
|
|
6401
|
+
if (payload === void 0 || payload === null) {
|
|
6402
|
+
throw new Error(
|
|
6403
|
+
`client_mcp/message returned no payload for server '${serverName}'`
|
|
6404
|
+
);
|
|
6405
|
+
}
|
|
6406
|
+
return payload;
|
|
6407
|
+
}
|
|
6408
|
+
__name(deliverClientMcpMessage, "deliverClientMcpMessage");
|
|
5853
6409
|
async function runAcpAgent(config, settings, argv) {
|
|
6410
|
+
let acpConnection;
|
|
6411
|
+
const bootstrapClientMcpSender = /* @__PURE__ */ __name((serverName, message) => deliverClientMcpMessage(acpConnection, serverName, message), "bootstrapClientMcpSender");
|
|
5854
6412
|
await config.initialize({
|
|
5855
6413
|
skipGeminiInitialization: true,
|
|
5856
6414
|
// Bootstrap skips MCP discovery — each session runs its own
|
|
5857
6415
|
// pool-routed discovery, so bootstrap-level spawns would be
|
|
5858
6416
|
// redundant subprocess leaks (W119).
|
|
5859
|
-
skipMcpDiscovery: true
|
|
6417
|
+
skipMcpDiscovery: true,
|
|
6418
|
+
// Bind the workspace-level manager's SDK callback so a runtime-added
|
|
6419
|
+
// client-hosted MCP server (#5626) round-trips over the parent WS.
|
|
6420
|
+
sendSdkMcpMessage: bootstrapClientMcpSender
|
|
5860
6421
|
});
|
|
5861
6422
|
const stdout = Writable.toWeb(process.stdout);
|
|
5862
6423
|
const stdin = Readable.toWeb(process.stdin);
|
|
@@ -5866,6 +6427,7 @@ async function runAcpAgent(config, settings, argv) {
|
|
|
5866
6427
|
const stream = ndJsonStream(stdout, stdin);
|
|
5867
6428
|
let agentInstance;
|
|
5868
6429
|
const connection = new AgentSideConnection((conn) => {
|
|
6430
|
+
acpConnection = conn;
|
|
5869
6431
|
agentInstance = new QwenAgent(config, settings, argv, conn);
|
|
5870
6432
|
return agentInstance;
|
|
5871
6433
|
}, stream);
|
|
@@ -5874,7 +6436,7 @@ async function runAcpAgent(config, settings, argv) {
|
|
|
5874
6436
|
try {
|
|
5875
6437
|
await agentInstance.shutdownMcpPool(8e3);
|
|
5876
6438
|
} catch (err) {
|
|
5877
|
-
|
|
6439
|
+
debugLogger6.error(`[ACP] MCP pool drain (${label}) error:`, err);
|
|
5878
6440
|
}
|
|
5879
6441
|
}, "drainPoolBeforeExit");
|
|
5880
6442
|
let shuttingDown = false;
|
|
@@ -5901,7 +6463,7 @@ async function runAcpAgent(config, settings, argv) {
|
|
|
5901
6463
|
try {
|
|
5902
6464
|
await hookSystem.fireSessionEndEvent(reason);
|
|
5903
6465
|
} catch (err) {
|
|
5904
|
-
|
|
6466
|
+
debugLogger6.warn(
|
|
5905
6467
|
`SessionEnd hook failed: ${err instanceof Error ? err.message : String(err)}`
|
|
5906
6468
|
);
|
|
5907
6469
|
}
|
|
@@ -5910,7 +6472,7 @@ async function runAcpAgent(config, settings, argv) {
|
|
|
5910
6472
|
const shutdownHandler = /* @__PURE__ */ __name(async () => {
|
|
5911
6473
|
if (shuttingDown) return;
|
|
5912
6474
|
shuttingDown = true;
|
|
5913
|
-
|
|
6475
|
+
debugLogger6.debug("[ACP] Shutdown signal received, closing streams");
|
|
5914
6476
|
await fireSessionEndOnce("other" /* Other */);
|
|
5915
6477
|
agentInstance?.disposeSessions();
|
|
5916
6478
|
try {
|
|
@@ -5923,7 +6485,7 @@ async function runAcpAgent(config, settings, argv) {
|
|
|
5923
6485
|
}
|
|
5924
6486
|
await drainPoolBeforeExit("signal");
|
|
5925
6487
|
runExitCleanup().catch((err) => {
|
|
5926
|
-
|
|
6488
|
+
debugLogger6.error("[ACP] Cleanup error:", err);
|
|
5927
6489
|
}).finally(() => {
|
|
5928
6490
|
process.exit(0);
|
|
5929
6491
|
});
|
|
@@ -6110,12 +6672,12 @@ var QwenAgent = class {
|
|
|
6110
6672
|
try {
|
|
6111
6673
|
const result = await this.mcpPool.drainAll({ force: true, timeoutMs });
|
|
6112
6674
|
if (result.forced > 0 || result.errors.length > 0) {
|
|
6113
|
-
|
|
6675
|
+
debugLogger6.warn(
|
|
6114
6676
|
`MCP pool drain: ${result.drained} clean, ${result.forced} timed out, ${result.errors.length} errors`
|
|
6115
6677
|
);
|
|
6116
6678
|
}
|
|
6117
6679
|
} catch (err) {
|
|
6118
|
-
|
|
6680
|
+
debugLogger6.error(
|
|
6119
6681
|
`MCP pool drainAll failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6120
6682
|
);
|
|
6121
6683
|
}
|
|
@@ -6129,14 +6691,14 @@ var QwenAgent = class {
|
|
|
6129
6691
|
try {
|
|
6130
6692
|
await session.cancelPendingPrompt();
|
|
6131
6693
|
} catch (err) {
|
|
6132
|
-
|
|
6694
|
+
debugLogger6.debug(
|
|
6133
6695
|
`Session ${sessionId} cancel during close failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6134
6696
|
);
|
|
6135
6697
|
}
|
|
6136
6698
|
try {
|
|
6137
6699
|
await session.getConfig().getToolRegistry()?.stop();
|
|
6138
6700
|
} catch (err) {
|
|
6139
|
-
|
|
6701
|
+
debugLogger6.debug(
|
|
6140
6702
|
`Session ${sessionId} tool registry stop during close failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6141
6703
|
);
|
|
6142
6704
|
}
|
|
@@ -6170,7 +6732,7 @@ var QwenAgent = class {
|
|
|
6170
6732
|
scope: "workspace",
|
|
6171
6733
|
...event
|
|
6172
6734
|
}).catch((err) => {
|
|
6173
|
-
|
|
6735
|
+
debugLogger6.debug(
|
|
6174
6736
|
`MCP workspace budget event delivery to session ${sid} failed (kind=${event.kind}): ${err instanceof Error ? err.message : String(err)}`
|
|
6175
6737
|
);
|
|
6176
6738
|
});
|
|
@@ -6179,7 +6741,7 @@ var QwenAgent = class {
|
|
|
6179
6741
|
async initialize(args) {
|
|
6180
6742
|
this.clientCapabilities = args.clientCapabilities;
|
|
6181
6743
|
const authMethods = buildAuthMethods();
|
|
6182
|
-
const version = "0.19.
|
|
6744
|
+
const version = "0.19.3-nightly.20260701.a974594d7";
|
|
6183
6745
|
return {
|
|
6184
6746
|
protocolVersion: PROTOCOL_VERSION,
|
|
6185
6747
|
agentInfo: {
|
|
@@ -6335,7 +6897,7 @@ var QwenAgent = class {
|
|
|
6335
6897
|
session.pendingWorktreeNotice = restored.contextMessage;
|
|
6336
6898
|
}
|
|
6337
6899
|
} catch (error) {
|
|
6338
|
-
|
|
6900
|
+
debugLogger6.warn(`ACP worktree restore failed: ${error}`);
|
|
6339
6901
|
}
|
|
6340
6902
|
}
|
|
6341
6903
|
async unstable_listSessions(params) {
|
|
@@ -6456,7 +7018,7 @@ var QwenAgent = class {
|
|
|
6456
7018
|
await extensionManager.refreshCache();
|
|
6457
7019
|
extensions = extensionManager.getLoadedExtensions();
|
|
6458
7020
|
} catch (error) {
|
|
6459
|
-
|
|
7021
|
+
debugLogger6.warn(
|
|
6460
7022
|
"Extension loading failed, continuing without extensions:",
|
|
6461
7023
|
error
|
|
6462
7024
|
);
|
|
@@ -6582,7 +7144,7 @@ var QwenAgent = class {
|
|
|
6582
7144
|
pm.addPersistentRule(rule, ruleType);
|
|
6583
7145
|
}
|
|
6584
7146
|
} catch (error) {
|
|
6585
|
-
|
|
7147
|
+
debugLogger6.warn(
|
|
6586
7148
|
`Failed to sync permission rules to a live session: ${error instanceof Error ? error.message : String(error)}`
|
|
6587
7149
|
);
|
|
6588
7150
|
}
|
|
@@ -6774,6 +7336,16 @@ var QwenAgent = class {
|
|
|
6774
7336
|
status: this.mcpStatus(e.status)
|
|
6775
7337
|
}));
|
|
6776
7338
|
}
|
|
7339
|
+
if (!disabled) {
|
|
7340
|
+
out.resourceCount = this.resolveServerMcpResources(
|
|
7341
|
+
config,
|
|
7342
|
+
name
|
|
7343
|
+
).length;
|
|
7344
|
+
out.promptCount = this.resolveServerMcpPrompts(
|
|
7345
|
+
config,
|
|
7346
|
+
name
|
|
7347
|
+
).length;
|
|
7348
|
+
}
|
|
6777
7349
|
return out;
|
|
6778
7350
|
})
|
|
6779
7351
|
),
|
|
@@ -6880,6 +7452,102 @@ var QwenAgent = class {
|
|
|
6880
7452
|
};
|
|
6881
7453
|
}
|
|
6882
7454
|
}
|
|
7455
|
+
/**
|
|
7456
|
+
* Resolve the resources discovered for one server, with the same
|
|
7457
|
+
* pool-mode fallback `buildWorkspaceMcpToolsStatus` uses for tools: the
|
|
7458
|
+
* workspace `Config`'s `ResourceRegistry` is authoritative in
|
|
7459
|
+
* single-session mode, but in pool mode resources are registered into
|
|
7460
|
+
* per-session registries (`SessionMcpView.applyResources`), leaving the
|
|
7461
|
+
* workspace registry empty. Fall back to the first active session that
|
|
7462
|
+
* has this server's resources.
|
|
7463
|
+
*/
|
|
7464
|
+
resolveServerMcpResources(config, serverName) {
|
|
7465
|
+
const resources = config.getResourceRegistry?.()?.getResourcesByServer(serverName) ?? [];
|
|
7466
|
+
if (resources.length > 0) {
|
|
7467
|
+
return resources;
|
|
7468
|
+
}
|
|
7469
|
+
for (const session of this.getActiveSessions()) {
|
|
7470
|
+
try {
|
|
7471
|
+
const sessionResources = session.getConfig().getResourceRegistry?.()?.getResourcesByServer(serverName) ?? [];
|
|
7472
|
+
if (sessionResources.length > 0) {
|
|
7473
|
+
return sessionResources;
|
|
7474
|
+
}
|
|
7475
|
+
} catch {
|
|
7476
|
+
}
|
|
7477
|
+
}
|
|
7478
|
+
return resources;
|
|
7479
|
+
}
|
|
7480
|
+
/**
|
|
7481
|
+
* Resolve the prompts discovered for one server, mirroring
|
|
7482
|
+
* {@link resolveServerMcpResources}. Used only for the per-server
|
|
7483
|
+
* `promptCount` on the base status — prompts have no drill-down
|
|
7484
|
+
* endpoint (they surface as slash commands).
|
|
7485
|
+
*/
|
|
7486
|
+
resolveServerMcpPrompts(config, serverName) {
|
|
7487
|
+
const prompts = config.getPromptRegistry?.()?.getPromptsByServer(serverName) ?? [];
|
|
7488
|
+
if (prompts.length > 0) {
|
|
7489
|
+
return prompts;
|
|
7490
|
+
}
|
|
7491
|
+
for (const session of this.getActiveSessions()) {
|
|
7492
|
+
try {
|
|
7493
|
+
const sessionPrompts = session.getConfig().getPromptRegistry?.()?.getPromptsByServer(serverName) ?? [];
|
|
7494
|
+
if (sessionPrompts.length > 0) {
|
|
7495
|
+
return sessionPrompts;
|
|
7496
|
+
}
|
|
7497
|
+
} catch {
|
|
7498
|
+
}
|
|
7499
|
+
}
|
|
7500
|
+
return prompts;
|
|
7501
|
+
}
|
|
7502
|
+
buildWorkspaceMcpResourcesStatus(config, serverName) {
|
|
7503
|
+
const workspaceCwd = this.safeWorkspaceCwd(config);
|
|
7504
|
+
try {
|
|
7505
|
+
const servers = config.getMcpServers() ?? {};
|
|
7506
|
+
if (!Object.prototype.hasOwnProperty.call(servers, serverName)) {
|
|
7507
|
+
return {
|
|
7508
|
+
v: STATUS_SCHEMA_VERSION,
|
|
7509
|
+
workspaceCwd,
|
|
7510
|
+
serverName,
|
|
7511
|
+
initialized: true,
|
|
7512
|
+
acpChannelLive: true,
|
|
7513
|
+
resources: [],
|
|
7514
|
+
errors: [
|
|
7515
|
+
{
|
|
7516
|
+
kind: "mcp_resources",
|
|
7517
|
+
status: "error",
|
|
7518
|
+
error: `MCP server not configured: ${serverName}`
|
|
7519
|
+
}
|
|
7520
|
+
]
|
|
7521
|
+
};
|
|
7522
|
+
}
|
|
7523
|
+
const resources = this.resolveServerMcpResources(config, serverName).map((resource) => ({
|
|
7524
|
+
uri: resource.uri,
|
|
7525
|
+
...typeof resource.name === "string" ? { name: resource.name } : {},
|
|
7526
|
+
...typeof resource.title === "string" ? { title: resource.title } : {},
|
|
7527
|
+
...typeof resource.description === "string" ? { description: resource.description } : {},
|
|
7528
|
+
...typeof resource.mimeType === "string" ? { mimeType: resource.mimeType } : {},
|
|
7529
|
+
...typeof resource.size === "number" ? { size: resource.size } : {}
|
|
7530
|
+
}));
|
|
7531
|
+
return {
|
|
7532
|
+
v: STATUS_SCHEMA_VERSION,
|
|
7533
|
+
workspaceCwd,
|
|
7534
|
+
serverName,
|
|
7535
|
+
initialized: true,
|
|
7536
|
+
acpChannelLive: true,
|
|
7537
|
+
resources
|
|
7538
|
+
};
|
|
7539
|
+
} catch (error) {
|
|
7540
|
+
return {
|
|
7541
|
+
v: STATUS_SCHEMA_VERSION,
|
|
7542
|
+
workspaceCwd,
|
|
7543
|
+
serverName,
|
|
7544
|
+
initialized: true,
|
|
7545
|
+
acpChannelLive: true,
|
|
7546
|
+
resources: [],
|
|
7547
|
+
errors: [this.errorCell("mcp_resources", error)]
|
|
7548
|
+
};
|
|
7549
|
+
}
|
|
7550
|
+
}
|
|
6883
7551
|
/**
|
|
6884
7552
|
* Build the MCP budget status cells exposed on `GET /workspace/mcp`.
|
|
6885
7553
|
*
|
|
@@ -6986,7 +7654,7 @@ var QwenAgent = class {
|
|
|
6986
7654
|
const currentAuth = activeRuntimeSnapshot?.authType ?? currentAuthType;
|
|
6987
7655
|
const currentAcpModelId = hasCurrentModel && currentAuth ? formatAcpModelId(currentModelId, currentAuth) : currentModelId || void 0;
|
|
6988
7656
|
const providers = /* @__PURE__ */ new Map();
|
|
6989
|
-
for (const model of config.getAllConfiguredModels()) {
|
|
7657
|
+
for (const model of config.getAllConfiguredModels().filter(isMainSelectableModel)) {
|
|
6990
7658
|
const authType = String(model.authType);
|
|
6991
7659
|
let provider = providers.get(authType);
|
|
6992
7660
|
if (!provider) {
|
|
@@ -7480,6 +8148,25 @@ var QwenAgent = class {
|
|
|
7480
8148
|
}
|
|
7481
8149
|
};
|
|
7482
8150
|
}
|
|
8151
|
+
const skillMetrics = metrics.skills ?? {
|
|
8152
|
+
totalCalls: 0,
|
|
8153
|
+
totalSuccess: 0,
|
|
8154
|
+
totalFail: 0,
|
|
8155
|
+
byName: {}
|
|
8156
|
+
};
|
|
8157
|
+
const skillsByName = {};
|
|
8158
|
+
for (const [name, skill] of Object.entries(skillMetrics.byName)) {
|
|
8159
|
+
Object.defineProperty(skillsByName, name, {
|
|
8160
|
+
value: {
|
|
8161
|
+
count: skill.count,
|
|
8162
|
+
success: skill.success,
|
|
8163
|
+
fail: skill.fail
|
|
8164
|
+
},
|
|
8165
|
+
enumerable: true,
|
|
8166
|
+
configurable: true,
|
|
8167
|
+
writable: true
|
|
8168
|
+
});
|
|
8169
|
+
}
|
|
7483
8170
|
return {
|
|
7484
8171
|
v: STATUS_SCHEMA_VERSION,
|
|
7485
8172
|
sessionId,
|
|
@@ -7498,6 +8185,12 @@ var QwenAgent = class {
|
|
|
7498
8185
|
files: {
|
|
7499
8186
|
totalLinesAdded: metrics.files.totalLinesAdded,
|
|
7500
8187
|
totalLinesRemoved: metrics.files.totalLinesRemoved
|
|
8188
|
+
},
|
|
8189
|
+
skills: {
|
|
8190
|
+
totalCalls: skillMetrics.totalCalls,
|
|
8191
|
+
totalSuccess: skillMetrics.totalSuccess,
|
|
8192
|
+
totalFail: skillMetrics.totalFail,
|
|
8193
|
+
byName: skillsByName
|
|
7501
8194
|
}
|
|
7502
8195
|
};
|
|
7503
8196
|
}
|
|
@@ -7681,6 +8374,7 @@ var QwenAgent = class {
|
|
|
7681
8374
|
id: ext.id,
|
|
7682
8375
|
name: ext.name,
|
|
7683
8376
|
displayName: ext.displayName,
|
|
8377
|
+
...ext.config.description ? { description: ext.config.description } : {},
|
|
7684
8378
|
version: ext.version,
|
|
7685
8379
|
isActive: ext.isActive,
|
|
7686
8380
|
path: ext.path,
|
|
@@ -7727,9 +8421,9 @@ var QwenAgent = class {
|
|
|
7727
8421
|
);
|
|
7728
8422
|
}
|
|
7729
8423
|
const download = await downloadSkill(request.sourceUrl);
|
|
7730
|
-
const skillsBaseDir =
|
|
8424
|
+
const skillsBaseDir = path3.join(Storage.getGlobalQwenDir(), "skills");
|
|
7731
8425
|
const skillDir = resolveManagedSkillDir(skillsBaseDir, request.slug);
|
|
7732
|
-
const skillFile =
|
|
8426
|
+
const skillFile = path3.join(skillDir, "SKILL.md");
|
|
7733
8427
|
const parsed = skillManager.parseSkillContent(
|
|
7734
8428
|
download.skillContent,
|
|
7735
8429
|
skillFile,
|
|
@@ -7749,7 +8443,7 @@ var QwenAgent = class {
|
|
|
7749
8443
|
stagingDir,
|
|
7750
8444
|
file.relativePath
|
|
7751
8445
|
);
|
|
7752
|
-
await fs.mkdir(
|
|
8446
|
+
await fs.mkdir(path3.dirname(targetPath), { recursive: true });
|
|
7753
8447
|
await fs.writeFile(targetPath, file.content);
|
|
7754
8448
|
}
|
|
7755
8449
|
await fs.rm(skillDir, { recursive: true, force: true });
|
|
@@ -7788,11 +8482,11 @@ var QwenAgent = class {
|
|
|
7788
8482
|
`Skill name "${parsed.name}" does not match requested slug "${request.slug}"`
|
|
7789
8483
|
);
|
|
7790
8484
|
}
|
|
7791
|
-
const resolvedSkillDir =
|
|
7792
|
-
const resolvedSkillFile =
|
|
7793
|
-
const globalDir =
|
|
7794
|
-
const isDedicatedSkillDir = resolvedSkillFile ===
|
|
7795
|
-
if (!isDedicatedSkillDir || resolvedSkillDir ===
|
|
8485
|
+
const resolvedSkillDir = path3.resolve(skillDir);
|
|
8486
|
+
const resolvedSkillFile = path3.resolve(skillFile);
|
|
8487
|
+
const globalDir = path3.resolve(Storage.getGlobalQwenDir());
|
|
8488
|
+
const isDedicatedSkillDir = resolvedSkillFile === path3.join(resolvedSkillDir, "SKILL.md");
|
|
8489
|
+
if (!isDedicatedSkillDir || resolvedSkillDir === path3.parse(resolvedSkillDir).root || resolvedSkillDir === globalDir) {
|
|
7796
8490
|
throw RequestError.invalidParams(
|
|
7797
8491
|
void 0,
|
|
7798
8492
|
`Refusing to delete unexpected skill directory: ${skillDir}`
|
|
@@ -7808,10 +8502,10 @@ var QwenAgent = class {
|
|
|
7808
8502
|
async readManagedSkillFile(slug, scope, skillManager, cwd) {
|
|
7809
8503
|
if (scope === "global") {
|
|
7810
8504
|
const qwenSkillDir = resolveManagedSkillDir(
|
|
7811
|
-
|
|
8505
|
+
path3.join(Storage.getGlobalQwenDir(), "skills"),
|
|
7812
8506
|
slug
|
|
7813
8507
|
);
|
|
7814
|
-
const qwenSkillFile =
|
|
8508
|
+
const qwenSkillFile = path3.join(qwenSkillDir, "SKILL.md");
|
|
7815
8509
|
const qwenContent = await fs.readFile(qwenSkillFile, "utf8").catch(() => void 0);
|
|
7816
8510
|
if (qwenContent !== void 0) {
|
|
7817
8511
|
return {
|
|
@@ -7847,15 +8541,15 @@ var QwenAgent = class {
|
|
|
7847
8541
|
);
|
|
7848
8542
|
});
|
|
7849
8543
|
return {
|
|
7850
|
-
skillDir:
|
|
8544
|
+
skillDir: path3.dirname(skillFile),
|
|
7851
8545
|
skillFile,
|
|
7852
8546
|
content
|
|
7853
8547
|
};
|
|
7854
8548
|
}
|
|
7855
8549
|
async findProjectSkillFileFromCwd(slug, cwd, skillManager) {
|
|
7856
|
-
const projectRoot =
|
|
8550
|
+
const projectRoot = path3.resolve(cwd);
|
|
7857
8551
|
for (const configDir of PROJECT_SKILL_DIRS) {
|
|
7858
|
-
const baseDir =
|
|
8552
|
+
const baseDir = path3.join(projectRoot, configDir, SKILLS_DIR);
|
|
7859
8553
|
const skills = await skillManager.loadSkillsFromDir(baseDir, "project");
|
|
7860
8554
|
const skill = skills.find((candidate) => candidate.name === slug);
|
|
7861
8555
|
const skillFile = skill?.filePath;
|
|
@@ -7867,7 +8561,7 @@ var QwenAgent = class {
|
|
|
7867
8561
|
);
|
|
7868
8562
|
});
|
|
7869
8563
|
return {
|
|
7870
|
-
skillDir:
|
|
8564
|
+
skillDir: path3.dirname(skillFile),
|
|
7871
8565
|
skillFile,
|
|
7872
8566
|
content
|
|
7873
8567
|
};
|
|
@@ -7898,7 +8592,7 @@ var QwenAgent = class {
|
|
|
7898
8592
|
}
|
|
7899
8593
|
const nextContent = setSkillFrontmatterEnabled(content, request.enabled);
|
|
7900
8594
|
skillManager.parseSkillContent(nextContent, skillFile, level);
|
|
7901
|
-
if (
|
|
8595
|
+
if (path3.basename(skillFile) !== "SKILL.md") {
|
|
7902
8596
|
throw RequestError.invalidParams(
|
|
7903
8597
|
void 0,
|
|
7904
8598
|
`Refusing to write to unexpected skill file: ${skillFile}`
|
|
@@ -7948,19 +8642,25 @@ var QwenAgent = class {
|
|
|
7948
8642
|
);
|
|
7949
8643
|
const persistScope = readProviderConnectScope(params["scope"]);
|
|
7950
8644
|
const plan = buildInstallPlan(providerConfig, inputs);
|
|
8645
|
+
const adapter = createLoadedSettingsAdapter(
|
|
8646
|
+
this.settings,
|
|
8647
|
+
persistScope
|
|
8648
|
+
);
|
|
7951
8649
|
await applyProviderInstallPlan(plan, {
|
|
7952
|
-
settings:
|
|
8650
|
+
settings: adapter,
|
|
7953
8651
|
reloadModelProviders: /* @__PURE__ */ __name((modelProviders) => this.config.reloadModelProvidersConfig(modelProviders), "reloadModelProviders"),
|
|
7954
8652
|
syncAuthState: /* @__PURE__ */ __name((authType, modelId, baseUrl) => this.config.getModelsConfig().syncAfterAuthRefresh(authType, modelId, baseUrl), "syncAuthState"),
|
|
7955
8653
|
refreshAuth: /* @__PURE__ */ __name((authType) => this.config.refreshAuth(authType), "refreshAuth")
|
|
7956
8654
|
});
|
|
8655
|
+
const effectiveModelId = adapter.getValue("model.name") ?? plan.modelSelection?.modelId;
|
|
8656
|
+
const effectiveBaseUrl = adapter.getValue("model.baseUrl") ?? plan.modelSelection?.baseUrl;
|
|
7957
8657
|
return {
|
|
7958
8658
|
success: true,
|
|
7959
8659
|
providerId: providerConfig.id,
|
|
7960
8660
|
providerLabel: providerConfig.label,
|
|
7961
8661
|
authType: plan.authType,
|
|
7962
|
-
modelId:
|
|
7963
|
-
...
|
|
8662
|
+
...effectiveModelId ? { modelId: effectiveModelId } : {},
|
|
8663
|
+
...effectiveBaseUrl ? { baseUrl: effectiveBaseUrl } : {}
|
|
7964
8664
|
};
|
|
7965
8665
|
}
|
|
7966
8666
|
case "qwen/skills/install": {
|
|
@@ -8026,6 +8726,19 @@ var QwenAgent = class {
|
|
|
8026
8726
|
serverName
|
|
8027
8727
|
);
|
|
8028
8728
|
}
|
|
8729
|
+
case SERVE_STATUS_EXT_METHODS.workspaceMcpResources: {
|
|
8730
|
+
const serverName = params["serverName"];
|
|
8731
|
+
if (typeof serverName !== "string" || serverName.length === 0) {
|
|
8732
|
+
throw RequestError.invalidParams(
|
|
8733
|
+
void 0,
|
|
8734
|
+
"Invalid or missing serverName"
|
|
8735
|
+
);
|
|
8736
|
+
}
|
|
8737
|
+
return this.buildWorkspaceMcpResourcesStatus(
|
|
8738
|
+
this.config,
|
|
8739
|
+
serverName
|
|
8740
|
+
);
|
|
8741
|
+
}
|
|
8029
8742
|
case SERVE_STATUS_EXT_METHODS.workspaceSkills:
|
|
8030
8743
|
return await this.buildWorkspaceSkillsStatus(
|
|
8031
8744
|
this.config
|
|
@@ -8314,23 +9027,34 @@ var QwenAgent = class {
|
|
|
8314
9027
|
}
|
|
8315
9028
|
if (action === "enable") {
|
|
8316
9029
|
const settings = loadSettings(this.config.getTargetDir());
|
|
9030
|
+
let settingsChanged = false;
|
|
8317
9031
|
for (const scope of ["User" /* User */, "Workspace" /* Workspace */]) {
|
|
8318
9032
|
const scopeSettings = settings.forScope(scope).settings;
|
|
8319
9033
|
const currentExcluded2 = scopeSettings.mcp?.excluded || [];
|
|
8320
|
-
|
|
8321
|
-
|
|
8322
|
-
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
9034
|
+
const filtered = currentExcluded2.filter(
|
|
9035
|
+
(pattern) => pattern !== serverName
|
|
9036
|
+
);
|
|
9037
|
+
if (filtered.length !== currentExcluded2.length) {
|
|
9038
|
+
settings.setValue(scope, "mcp.excluded", filtered);
|
|
9039
|
+
settingsChanged = true;
|
|
8326
9040
|
}
|
|
8327
9041
|
}
|
|
8328
9042
|
const currentExcluded = this.config.getExcludedMcpServers() || [];
|
|
8329
|
-
|
|
8330
|
-
|
|
9043
|
+
const runtimeFiltered = currentExcluded.filter(
|
|
9044
|
+
(pattern) => pattern !== serverName
|
|
8331
9045
|
);
|
|
9046
|
+
let runtimeChanged = false;
|
|
9047
|
+
if (runtimeFiltered.length !== currentExcluded.length) {
|
|
9048
|
+
this.config.setExcludedMcpServers(runtimeFiltered);
|
|
9049
|
+
runtimeChanged = true;
|
|
9050
|
+
}
|
|
8332
9051
|
await toolRegistry.discoverToolsForServer(serverName);
|
|
8333
|
-
return {
|
|
9052
|
+
return {
|
|
9053
|
+
serverName,
|
|
9054
|
+
action,
|
|
9055
|
+
ok: true,
|
|
9056
|
+
changed: settingsChanged || runtimeChanged
|
|
9057
|
+
};
|
|
8334
9058
|
}
|
|
8335
9059
|
if (action === "disable") {
|
|
8336
9060
|
const settings = loadSettings(this.config.getTargetDir());
|
|
@@ -8352,18 +9076,27 @@ var QwenAgent = class {
|
|
|
8352
9076
|
}
|
|
8353
9077
|
const scopeSettings = settings.forScope(targetScope).settings;
|
|
8354
9078
|
const currentExcluded = scopeSettings.mcp?.excluded || [];
|
|
8355
|
-
|
|
9079
|
+
let settingsChanged = false;
|
|
9080
|
+
if (!matchesAnyServerPattern(serverName, currentExcluded)) {
|
|
8356
9081
|
settings.setValue(targetScope, "mcp.excluded", [
|
|
8357
9082
|
...currentExcluded,
|
|
8358
9083
|
serverName
|
|
8359
9084
|
]);
|
|
9085
|
+
settingsChanged = true;
|
|
8360
9086
|
}
|
|
8361
9087
|
const runtimeExcluded = this.config.getExcludedMcpServers() || [];
|
|
8362
|
-
|
|
9088
|
+
let runtimeChanged = false;
|
|
9089
|
+
if (!matchesAnyServerPattern(serverName, runtimeExcluded)) {
|
|
8363
9090
|
this.config.setExcludedMcpServers([...runtimeExcluded, serverName]);
|
|
9091
|
+
runtimeChanged = true;
|
|
8364
9092
|
}
|
|
8365
9093
|
await toolRegistry.disableMcpServer(serverName);
|
|
8366
|
-
return {
|
|
9094
|
+
return {
|
|
9095
|
+
serverName,
|
|
9096
|
+
action,
|
|
9097
|
+
ok: true,
|
|
9098
|
+
changed: settingsChanged || runtimeChanged
|
|
9099
|
+
};
|
|
8367
9100
|
}
|
|
8368
9101
|
if (action === "clear-auth") {
|
|
8369
9102
|
const tokenStorage = new MCPOAuthTokenStorage();
|
|
@@ -8481,6 +9214,78 @@ var QwenAgent = class {
|
|
|
8481
9214
|
await this.closeStoredSession(sessionId);
|
|
8482
9215
|
return { sessionId, closed: true };
|
|
8483
9216
|
}
|
|
9217
|
+
case SERVE_CONTROL_EXT_METHODS.sessionCd: {
|
|
9218
|
+
const sessionId = params["sessionId"];
|
|
9219
|
+
const targetPath = params["path"];
|
|
9220
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
9221
|
+
throw RequestError.invalidParams(
|
|
9222
|
+
void 0,
|
|
9223
|
+
"Invalid or missing sessionId"
|
|
9224
|
+
);
|
|
9225
|
+
}
|
|
9226
|
+
if (typeof targetPath !== "string" || targetPath.length === 0 || !path3.isAbsolute(targetPath) || targetPath.includes("\0")) {
|
|
9227
|
+
throw RequestError.invalidParams(
|
|
9228
|
+
void 0,
|
|
9229
|
+
"Invalid or missing path (must be an absolute path)"
|
|
9230
|
+
);
|
|
9231
|
+
}
|
|
9232
|
+
const session = this.sessionOrThrow(sessionId);
|
|
9233
|
+
const config = session.getConfig();
|
|
9234
|
+
if (config.isRestrictiveSandbox()) {
|
|
9235
|
+
throw new RequestError(-32003, "Restrictive sandbox mode active", {
|
|
9236
|
+
errorKind: "restrictive_sandbox"
|
|
9237
|
+
});
|
|
9238
|
+
}
|
|
9239
|
+
let stats;
|
|
9240
|
+
try {
|
|
9241
|
+
stats = await fs.stat(targetPath);
|
|
9242
|
+
} catch {
|
|
9243
|
+
throw new RequestError(-32002, `Directory not found: ${targetPath}`, {
|
|
9244
|
+
errorKind: "directory_not_found",
|
|
9245
|
+
path: targetPath
|
|
9246
|
+
});
|
|
9247
|
+
}
|
|
9248
|
+
if (!stats.isDirectory()) {
|
|
9249
|
+
throw new RequestError(-32002, `Not a directory: ${targetPath}`, {
|
|
9250
|
+
errorKind: "directory_not_found",
|
|
9251
|
+
path: targetPath
|
|
9252
|
+
});
|
|
9253
|
+
}
|
|
9254
|
+
const canonicalPath = await fs.realpath(targetPath);
|
|
9255
|
+
const previousCwd = config.getTargetDir();
|
|
9256
|
+
if (canonicalPath === previousCwd) {
|
|
9257
|
+
return { previousCwd, newCwd: canonicalPath, warnings: [] };
|
|
9258
|
+
}
|
|
9259
|
+
if (isFolderTrustEnabled(this.settings.merged)) {
|
|
9260
|
+
const trustedFolders = loadTrustedFolders();
|
|
9261
|
+
if (trustedFolders.isPathTrusted(canonicalPath) !== true) {
|
|
9262
|
+
throw new RequestError(
|
|
9263
|
+
-32001,
|
|
9264
|
+
`Directory not trusted: ${canonicalPath}`,
|
|
9265
|
+
{ errorKind: "directory_not_trusted", path: canonicalPath }
|
|
9266
|
+
);
|
|
9267
|
+
}
|
|
9268
|
+
}
|
|
9269
|
+
const warnings = [];
|
|
9270
|
+
const relocation = await config.relocateWorkingDirectory(
|
|
9271
|
+
canonicalPath,
|
|
9272
|
+
canonicalPath,
|
|
9273
|
+
{ skipProcessChdir: true, skipArtifactMigration: true }
|
|
9274
|
+
);
|
|
9275
|
+
if (relocation.memoryRefreshError) {
|
|
9276
|
+
warnings.push(
|
|
9277
|
+
`Memory refresh failed: ${relocation.memoryRefreshError instanceof Error ? relocation.memoryRefreshError.message : String(relocation.memoryRefreshError)}`
|
|
9278
|
+
);
|
|
9279
|
+
}
|
|
9280
|
+
try {
|
|
9281
|
+
await config.getGeminiClient()?.addWorkingDirectoryChangedContext(previousCwd, canonicalPath);
|
|
9282
|
+
} catch (error) {
|
|
9283
|
+
warnings.push(
|
|
9284
|
+
`Model context refresh failed: ${error instanceof Error ? error.message : String(error)}`
|
|
9285
|
+
);
|
|
9286
|
+
}
|
|
9287
|
+
return { previousCwd, newCwd: canonicalPath, warnings };
|
|
9288
|
+
}
|
|
8484
9289
|
case SERVE_CONTROL_EXT_METHODS.sessionApprovalMode: {
|
|
8485
9290
|
const sessionId = params["sessionId"];
|
|
8486
9291
|
const mode = params["mode"];
|
|
@@ -8536,7 +9341,7 @@ var QwenAgent = class {
|
|
|
8536
9341
|
try {
|
|
8537
9342
|
await setLanguageAsync(language);
|
|
8538
9343
|
} catch (err) {
|
|
8539
|
-
|
|
9344
|
+
debugLogger6.warn("setLanguageAsync failed:", err);
|
|
8540
9345
|
throw new RequestError(
|
|
8541
9346
|
-32603,
|
|
8542
9347
|
`Failed to switch UI language: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -8550,7 +9355,7 @@ var QwenAgent = class {
|
|
|
8550
9355
|
language
|
|
8551
9356
|
);
|
|
8552
9357
|
} catch (err) {
|
|
8553
|
-
|
|
9358
|
+
debugLogger6.warn("Failed to persist UI language setting:", err);
|
|
8554
9359
|
}
|
|
8555
9360
|
let outputLanguage = null;
|
|
8556
9361
|
let refreshed = false;
|
|
@@ -8565,7 +9370,7 @@ var QwenAgent = class {
|
|
|
8565
9370
|
);
|
|
8566
9371
|
fileWriteOk = true;
|
|
8567
9372
|
} catch (err) {
|
|
8568
|
-
|
|
9373
|
+
debugLogger6.warn("Failed to write output-language.md:", err);
|
|
8569
9374
|
}
|
|
8570
9375
|
if (fileWriteOk) {
|
|
8571
9376
|
try {
|
|
@@ -8575,7 +9380,7 @@ var QwenAgent = class {
|
|
|
8575
9380
|
settingValue
|
|
8576
9381
|
);
|
|
8577
9382
|
} catch (err) {
|
|
8578
|
-
|
|
9383
|
+
debugLogger6.warn(
|
|
8579
9384
|
"Failed to persist output language setting:",
|
|
8580
9385
|
err
|
|
8581
9386
|
);
|
|
@@ -8595,7 +9400,7 @@ var QwenAgent = class {
|
|
|
8595
9400
|
writeOutputLanguageAndRegisterPath(settingValue, cfg);
|
|
8596
9401
|
}
|
|
8597
9402
|
} catch (err) {
|
|
8598
|
-
|
|
9403
|
+
debugLogger6.warn(
|
|
8599
9404
|
`Failed to write output-language.md for session ${s.getId()} (path=${sessionPath ?? "global-default"}):`,
|
|
8600
9405
|
err
|
|
8601
9406
|
);
|
|
@@ -8608,7 +9413,7 @@ var QwenAgent = class {
|
|
|
8608
9413
|
(r) => r.status === "rejected"
|
|
8609
9414
|
).length;
|
|
8610
9415
|
if (failedCount > 0) {
|
|
8611
|
-
|
|
9416
|
+
debugLogger6.warn(
|
|
8612
9417
|
`Language refresh failed for ${failedCount}/${results.length} session(s)`
|
|
8613
9418
|
);
|
|
8614
9419
|
}
|
|
@@ -8626,14 +9431,14 @@ var QwenAgent = class {
|
|
|
8626
9431
|
"Invalid or missing sessionId"
|
|
8627
9432
|
);
|
|
8628
9433
|
}
|
|
8629
|
-
|
|
9434
|
+
debugLogger6.debug(`recap ext-method received for session=${sessionId}`);
|
|
8630
9435
|
const session = this.sessionOrThrow(sessionId);
|
|
8631
9436
|
const config = session.getConfig();
|
|
8632
9437
|
const recap = await generateSessionRecap(
|
|
8633
9438
|
config,
|
|
8634
9439
|
new AbortController().signal
|
|
8635
9440
|
);
|
|
8636
|
-
|
|
9441
|
+
debugLogger6.debug(
|
|
8637
9442
|
`recap ext-method completed for session=${sessionId} result=${recap ? `len=${recap.length}` : "null"}`
|
|
8638
9443
|
);
|
|
8639
9444
|
return { sessionId, recap };
|
|
@@ -8657,7 +9462,7 @@ var QwenAgent = class {
|
|
|
8657
9462
|
const config = session.getConfig();
|
|
8658
9463
|
const cacheSafeParams = buildBtwCacheSafeParams(config);
|
|
8659
9464
|
if (!cacheSafeParams) {
|
|
8660
|
-
|
|
9465
|
+
debugLogger6.debug(`btw: no cacheSafeParams for session=${sessionId}`);
|
|
8661
9466
|
return { sessionId, answer: null };
|
|
8662
9467
|
}
|
|
8663
9468
|
const childSignal = AbortSignal.timeout(BTW_CHILD_TIMEOUT_MS);
|
|
@@ -8705,7 +9510,7 @@ var QwenAgent = class {
|
|
|
8705
9510
|
try {
|
|
8706
9511
|
hasHistory = (config.getGeminiClient().getHistoryShallow() ?? []).length > 0;
|
|
8707
9512
|
} catch (error) {
|
|
8708
|
-
|
|
9513
|
+
debugLogger6.debug("Failed to read history before /fork:", error);
|
|
8709
9514
|
}
|
|
8710
9515
|
if (!hasHistory) {
|
|
8711
9516
|
throw RequestError.invalidParams(
|
|
@@ -8747,7 +9552,7 @@ var QwenAgent = class {
|
|
|
8747
9552
|
]
|
|
8748
9553
|
});
|
|
8749
9554
|
} catch (error) {
|
|
8750
|
-
|
|
9555
|
+
debugLogger6.debug("Failed to record fork event in history:", error);
|
|
8751
9556
|
}
|
|
8752
9557
|
return { sessionId, description, launched: true };
|
|
8753
9558
|
}
|
|
@@ -8810,7 +9615,7 @@ ${outputText}
|
|
|
8810
9615
|
'taskKind must be "agent", "shell", or "monitor"'
|
|
8811
9616
|
);
|
|
8812
9617
|
}
|
|
8813
|
-
|
|
9618
|
+
debugLogger6.info(
|
|
8814
9619
|
`sessionTaskCancel requested sessionId=${sessionId} taskId=${taskId} taskKind=${taskKind}`
|
|
8815
9620
|
);
|
|
8816
9621
|
const session = this.sessionOrThrow(sessionId);
|
|
@@ -8820,7 +9625,7 @@ ${outputText}
|
|
|
8820
9625
|
const task = config.getBackgroundTaskRegistry().get(taskId);
|
|
8821
9626
|
if (!task || task.status !== "running" && task.status !== "paused") {
|
|
8822
9627
|
const reason = task ? "not_running" : "not_found";
|
|
8823
|
-
|
|
9628
|
+
debugLogger6.info(
|
|
8824
9629
|
`sessionTaskCancel skipped sessionId=${sessionId} taskId=${taskId} taskKind=${taskKind} reason=${reason} status=${task?.status ?? "missing"}`
|
|
8825
9630
|
);
|
|
8826
9631
|
return { cancelled: false, reason, status: task?.status };
|
|
@@ -8830,7 +9635,7 @@ ${outputText}
|
|
|
8830
9635
|
} else {
|
|
8831
9636
|
config.getBackgroundTaskRegistry().cancel(taskId);
|
|
8832
9637
|
}
|
|
8833
|
-
|
|
9638
|
+
debugLogger6.info(
|
|
8834
9639
|
`sessionTaskCancel completed sessionId=${sessionId} taskId=${taskId} taskKind=${taskKind} status=${task.status}`
|
|
8835
9640
|
);
|
|
8836
9641
|
return { cancelled: true, status: task.status };
|
|
@@ -8839,13 +9644,13 @@ ${outputText}
|
|
|
8839
9644
|
const task = config.getBackgroundShellRegistry().get(taskId);
|
|
8840
9645
|
if (!task || task.status !== "running") {
|
|
8841
9646
|
const reason = task ? "not_running" : "not_found";
|
|
8842
|
-
|
|
9647
|
+
debugLogger6.info(
|
|
8843
9648
|
`sessionTaskCancel skipped sessionId=${sessionId} taskId=${taskId} taskKind=${taskKind} reason=${reason} status=${task?.status ?? "missing"}`
|
|
8844
9649
|
);
|
|
8845
9650
|
return { cancelled: false, reason, status: task?.status };
|
|
8846
9651
|
}
|
|
8847
9652
|
config.getBackgroundShellRegistry().requestCancel(taskId);
|
|
8848
|
-
|
|
9653
|
+
debugLogger6.info(
|
|
8849
9654
|
`sessionTaskCancel completed sessionId=${sessionId} taskId=${taskId} taskKind=${taskKind} status=${task.status}`
|
|
8850
9655
|
);
|
|
8851
9656
|
return { cancelled: true, status: task.status };
|
|
@@ -8854,13 +9659,13 @@ ${outputText}
|
|
|
8854
9659
|
const task = config.getMonitorRegistry().get(taskId);
|
|
8855
9660
|
if (!task || task.status !== "running") {
|
|
8856
9661
|
const reason = task ? "not_running" : "not_found";
|
|
8857
|
-
|
|
9662
|
+
debugLogger6.info(
|
|
8858
9663
|
`sessionTaskCancel skipped sessionId=${sessionId} taskId=${taskId} taskKind=${taskKind} reason=${reason} status=${task?.status ?? "missing"}`
|
|
8859
9664
|
);
|
|
8860
9665
|
return { cancelled: false, reason, status: task?.status };
|
|
8861
9666
|
}
|
|
8862
9667
|
config.getMonitorRegistry().cancel(taskId);
|
|
8863
|
-
|
|
9668
|
+
debugLogger6.info(
|
|
8864
9669
|
`sessionTaskCancel completed sessionId=${sessionId} taskId=${taskId} taskKind=${taskKind} status=${task.status}`
|
|
8865
9670
|
);
|
|
8866
9671
|
return { cancelled: true, status: task.status };
|
|
@@ -8890,7 +9695,7 @@ ${outputText}
|
|
|
8890
9695
|
durationMs: Date.now() - cleared.setAt
|
|
8891
9696
|
});
|
|
8892
9697
|
}
|
|
8893
|
-
|
|
9698
|
+
debugLogger6.info(
|
|
8894
9699
|
`sessionGoalClear sessionId=${sessionId} cleared=${!!cleared} condition=${cleared?.condition ?? "(none)"}`
|
|
8895
9700
|
);
|
|
8896
9701
|
return {
|
|
@@ -8898,6 +9703,21 @@ ${outputText}
|
|
|
8898
9703
|
condition: cleared?.condition
|
|
8899
9704
|
};
|
|
8900
9705
|
}
|
|
9706
|
+
case SERVE_CONTROL_EXT_METHODS.sessionContinue: {
|
|
9707
|
+
const sessionId = params["sessionId"];
|
|
9708
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
9709
|
+
throw RequestError.invalidParams(
|
|
9710
|
+
void 0,
|
|
9711
|
+
"Invalid or missing sessionId"
|
|
9712
|
+
);
|
|
9713
|
+
}
|
|
9714
|
+
const session = this.sessionOrThrow(sessionId);
|
|
9715
|
+
const result = await session.continueLastTurn();
|
|
9716
|
+
debugLogger6.info(
|
|
9717
|
+
`sessionContinue sessionId=${sessionId} accepted=${result.accepted} interruption=${result.interruption}`
|
|
9718
|
+
);
|
|
9719
|
+
return result;
|
|
9720
|
+
}
|
|
8901
9721
|
case SERVE_CONTROL_EXT_METHODS.workspaceMcpRuntimeAdd: {
|
|
8902
9722
|
const name = params["name"];
|
|
8903
9723
|
const config = params["config"];
|
|
@@ -8908,7 +9728,7 @@ ${outputText}
|
|
|
8908
9728
|
"Invalid or missing name"
|
|
8909
9729
|
);
|
|
8910
9730
|
}
|
|
8911
|
-
if (
|
|
9731
|
+
if (!isValidServerName(name)) {
|
|
8912
9732
|
throw RequestError.invalidParams(
|
|
8913
9733
|
void 0,
|
|
8914
9734
|
"Server name must be \u2264256 chars, alphanumeric + underscore/hyphen, and not a reserved JS property name"
|
|
@@ -8944,13 +9764,37 @@ ${outputText}
|
|
|
8944
9764
|
oauth: _oauth,
|
|
8945
9765
|
headers: _headers,
|
|
8946
9766
|
type: _type,
|
|
9767
|
+
// Reverse tool channel marker (issue #5626, Phase 2). The parent
|
|
9768
|
+
// serve process stamps this on a client-hosted (extension) MCP
|
|
9769
|
+
// server's runtime config; it never reaches the transport itself.
|
|
9770
|
+
[CLIENT_MCP_OVER_WS_CONFIG_FLAG]: clientMcpOverWs,
|
|
8947
9771
|
...safeConfig
|
|
8948
9772
|
} = config;
|
|
9773
|
+
if (clientMcpOverWs === true) {
|
|
9774
|
+
safeConfig["type"] = "sdk";
|
|
9775
|
+
}
|
|
8949
9776
|
const result = await manager.addRuntimeMcpServer(
|
|
8950
9777
|
name,
|
|
8951
9778
|
safeConfig,
|
|
8952
9779
|
originatorClientId
|
|
8953
9780
|
);
|
|
9781
|
+
await Promise.all(
|
|
9782
|
+
this.getActiveSessions().map(async (session) => {
|
|
9783
|
+
const sessionManager = session.getConfig().getToolRegistry()?.getMcpClientManager();
|
|
9784
|
+
if (!sessionManager) return;
|
|
9785
|
+
try {
|
|
9786
|
+
await sessionManager.addRuntimeMcpServer(
|
|
9787
|
+
name,
|
|
9788
|
+
safeConfig,
|
|
9789
|
+
originatorClientId
|
|
9790
|
+
);
|
|
9791
|
+
} catch (sessionErr) {
|
|
9792
|
+
debugLogger6.warn(
|
|
9793
|
+
`workspaceMcpRuntimeAdd: failed to add runtime MCP server '${name}' to active session ${session.getConfig().getSessionId()}: ${sessionErr instanceof Error ? sessionErr.message : String(sessionErr)}`
|
|
9794
|
+
);
|
|
9795
|
+
}
|
|
9796
|
+
})
|
|
9797
|
+
);
|
|
8954
9798
|
return result;
|
|
8955
9799
|
} catch (err) {
|
|
8956
9800
|
if (err instanceof McpBudgetWouldExceedError) {
|
|
@@ -8985,7 +9829,7 @@ ${outputText}
|
|
|
8985
9829
|
"Invalid or missing name"
|
|
8986
9830
|
);
|
|
8987
9831
|
}
|
|
8988
|
-
if (
|
|
9832
|
+
if (!isValidServerName(name)) {
|
|
8989
9833
|
throw RequestError.invalidParams(
|
|
8990
9834
|
void 0,
|
|
8991
9835
|
"Server name must be \u2264256 chars, alphanumeric + underscore/hyphen, and not a reserved JS property name"
|
|
@@ -9008,6 +9852,22 @@ ${outputText}
|
|
|
9008
9852
|
name,
|
|
9009
9853
|
originatorClientId
|
|
9010
9854
|
);
|
|
9855
|
+
await Promise.all(
|
|
9856
|
+
this.getActiveSessions().map(async (session) => {
|
|
9857
|
+
const sessionManager = session.getConfig().getToolRegistry()?.getMcpClientManager();
|
|
9858
|
+
if (!sessionManager) return;
|
|
9859
|
+
try {
|
|
9860
|
+
await sessionManager.removeRuntimeMcpServer(
|
|
9861
|
+
name,
|
|
9862
|
+
originatorClientId
|
|
9863
|
+
);
|
|
9864
|
+
} catch (sessionErr) {
|
|
9865
|
+
debugLogger6.warn(
|
|
9866
|
+
`workspaceMcpRuntimeRemove: failed to remove runtime MCP server '${name}' from active session ${session.getConfig().getSessionId()}: ${sessionErr instanceof Error ? sessionErr.message : String(sessionErr)}`
|
|
9867
|
+
);
|
|
9868
|
+
}
|
|
9869
|
+
})
|
|
9870
|
+
);
|
|
9011
9871
|
return result;
|
|
9012
9872
|
}
|
|
9013
9873
|
case SERVE_CONTROL_EXT_METHODS.workspaceExtensionsRefresh: {
|
|
@@ -9018,7 +9878,7 @@ ${outputText}
|
|
|
9018
9878
|
try {
|
|
9019
9879
|
await extensionManager.refreshTools();
|
|
9020
9880
|
} catch (err) {
|
|
9021
|
-
|
|
9881
|
+
debugLogger6.warn(
|
|
9022
9882
|
`Extension tool refresh failed for session ${sessionId}: ${err instanceof Error ? err.message : String(err)}`
|
|
9023
9883
|
);
|
|
9024
9884
|
}
|
|
@@ -9166,7 +10026,7 @@ ${outputText}
|
|
|
9166
10026
|
filesFailed = fileResult.filesFailed;
|
|
9167
10027
|
} catch (err) {
|
|
9168
10028
|
const reason = err instanceof Error ? err.message : String(err);
|
|
9169
|
-
|
|
10029
|
+
debugLogger6.error(
|
|
9170
10030
|
`[ACP] File-history rewind failed for session=${sessionId} promptId=${promptId}: ${reason}`
|
|
9171
10031
|
);
|
|
9172
10032
|
filesFailed = [`file-history-rewind: ${reason}`];
|
|
@@ -9224,7 +10084,7 @@ ${outputText}
|
|
|
9224
10084
|
);
|
|
9225
10085
|
} catch (error) {
|
|
9226
10086
|
replayError = error instanceof Error ? error.message : String(error);
|
|
9227
|
-
|
|
10087
|
+
debugLogger6.warn(
|
|
9228
10088
|
"[loadUpdates] History replay failed for session %s (partial updates: %d):",
|
|
9229
10089
|
sessionId,
|
|
9230
10090
|
updates.length,
|
|
@@ -9576,11 +10436,15 @@ ${outputText}
|
|
|
9576
10436
|
}
|
|
9577
10437
|
const config = session.getConfig();
|
|
9578
10438
|
const authType = config.getAuthType();
|
|
9579
|
-
|
|
10439
|
+
const providersChanged = changed.has("modelProviders") || changed.has("providerProtocol");
|
|
10440
|
+
if (providersChanged) {
|
|
9580
10441
|
try {
|
|
9581
|
-
config.reloadModelProvidersConfig(
|
|
10442
|
+
config.reloadModelProvidersConfig(
|
|
10443
|
+
newMerged.modelProviders,
|
|
10444
|
+
newMerged.providerProtocol ?? {}
|
|
10445
|
+
);
|
|
9582
10446
|
} catch (err) {
|
|
9583
|
-
|
|
10447
|
+
debugLogger6.warn(
|
|
9584
10448
|
`reload: reloadModelProvidersConfig failed for session ${id}: ${err}`
|
|
9585
10449
|
);
|
|
9586
10450
|
}
|
|
@@ -9590,15 +10454,15 @@ ${outputText}
|
|
|
9590
10454
|
try {
|
|
9591
10455
|
await config.switchModel(authType, newModelName);
|
|
9592
10456
|
} catch (err) {
|
|
9593
|
-
|
|
10457
|
+
debugLogger6.warn(
|
|
9594
10458
|
`reload: switchModel failed for session ${id}: ${err}`
|
|
9595
10459
|
);
|
|
9596
10460
|
}
|
|
9597
|
-
} else if ((
|
|
10461
|
+
} else if ((providersChanged || envChanged) && authType) {
|
|
9598
10462
|
try {
|
|
9599
10463
|
await config.refreshAuth(authType);
|
|
9600
10464
|
} catch (err) {
|
|
9601
|
-
|
|
10465
|
+
debugLogger6.warn(
|
|
9602
10466
|
`reload: refreshAuth failed for session ${id}: ${err}`
|
|
9603
10467
|
);
|
|
9604
10468
|
}
|
|
@@ -9613,7 +10477,7 @@ ${outputText}
|
|
|
9613
10477
|
try {
|
|
9614
10478
|
config.setApprovalMode(newMode);
|
|
9615
10479
|
} catch (err) {
|
|
9616
|
-
|
|
10480
|
+
debugLogger6.warn(
|
|
9617
10481
|
`reload: setApprovalMode failed for session ${id}: ${err}`
|
|
9618
10482
|
);
|
|
9619
10483
|
}
|
|
@@ -9622,14 +10486,14 @@ ${outputText}
|
|
|
9622
10486
|
try {
|
|
9623
10487
|
await config.refreshHierarchicalMemory();
|
|
9624
10488
|
} catch (err) {
|
|
9625
|
-
|
|
10489
|
+
debugLogger6.warn(
|
|
9626
10490
|
`reload: refreshHierarchicalMemory failed for session ${id}: ${err}`
|
|
9627
10491
|
);
|
|
9628
10492
|
}
|
|
9629
10493
|
try {
|
|
9630
10494
|
await config.getGeminiClient()?.refreshSystemInstruction();
|
|
9631
10495
|
} catch (err) {
|
|
9632
|
-
|
|
10496
|
+
debugLogger6.warn(
|
|
9633
10497
|
`reload: refreshSystemInstruction failed for session ${id}: ${err}`
|
|
9634
10498
|
);
|
|
9635
10499
|
}
|
|
@@ -9639,7 +10503,7 @@ ${outputText}
|
|
|
9639
10503
|
for (let i = 0; i < results.length; i++) {
|
|
9640
10504
|
if (results[i].status === "rejected") {
|
|
9641
10505
|
const reason = results[i].reason;
|
|
9642
|
-
|
|
10506
|
+
debugLogger6.warn(
|
|
9643
10507
|
`Session ${sessions[i][0]} reload failed: ${reason}`
|
|
9644
10508
|
);
|
|
9645
10509
|
skipped.push(sessions[i][0]);
|
|
@@ -9657,6 +10521,21 @@ ${outputText}
|
|
|
9657
10521
|
}
|
|
9658
10522
|
}
|
|
9659
10523
|
// --- private helpers ---
|
|
10524
|
+
/**
|
|
10525
|
+
* Reverse tool channel (issue #5626, Phase 2). Build the session
|
|
10526
|
+
* `McpClientManager`'s `sendSdkMcpMessage` callback. Client-hosted
|
|
10527
|
+
* (extension) MCP servers are registered SDK-type, so the manager routes
|
|
10528
|
+
* their JSON-RPC through this callback. We forward each frame UP to the
|
|
10529
|
+
* parent serve process via the `qwen/control/client_mcp/message` ext-method;
|
|
10530
|
+
* the parent's `BridgeClient.extMethod` hands it to the per-WS-connection
|
|
10531
|
+
* `ClientMcpRegistrar`, which carries it down the daemon WS to the extension
|
|
10532
|
+
* and returns the correlated response (the `payload` field). All SDK-type
|
|
10533
|
+
* servers in this session share one callback — the `serverName` argument
|
|
10534
|
+
* routes to the right client-hosted server in the parent.
|
|
10535
|
+
*/
|
|
10536
|
+
buildClientMcpSender() {
|
|
10537
|
+
return (serverName, message) => deliverClientMcpMessage(this.connection, serverName, message);
|
|
10538
|
+
}
|
|
9660
10539
|
async newSessionConfig(cwd, mcpServers, sessionId, resume) {
|
|
9661
10540
|
this.settings = loadSettings(cwd);
|
|
9662
10541
|
const sessionMcpServers = {};
|
|
@@ -9710,6 +10589,10 @@ ${outputText}
|
|
|
9710
10589
|
continue;
|
|
9711
10590
|
}
|
|
9712
10591
|
}
|
|
10592
|
+
const cdpTunnelMcp = buildCdpTunnelMcpServer();
|
|
10593
|
+
if (cdpTunnelMcp && !sessionMcpServers["chrome-devtools"]) {
|
|
10594
|
+
sessionMcpServers["chrome-devtools"] = cdpTunnelMcp;
|
|
10595
|
+
}
|
|
9713
10596
|
const settings = this.settings.merged;
|
|
9714
10597
|
const argvForSession = {
|
|
9715
10598
|
...this.argv,
|
|
@@ -9741,6 +10624,14 @@ ${outputText}
|
|
|
9741
10624
|
if (typeof config.enableFileCheckpointing === "function") {
|
|
9742
10625
|
config.enableFileCheckpointing();
|
|
9743
10626
|
}
|
|
10627
|
+
if (typeof this.config.getRuntimeMcpServers === "function" && typeof config.addRuntimeMcpServer === "function") {
|
|
10628
|
+
const bootstrapRuntimeMcpServers = this.config.getRuntimeMcpServers();
|
|
10629
|
+
for (const [runtimeServerName, runtimeServerConfig] of Object.entries(
|
|
10630
|
+
bootstrapRuntimeMcpServers
|
|
10631
|
+
)) {
|
|
10632
|
+
config.addRuntimeMcpServer(runtimeServerName, runtimeServerConfig);
|
|
10633
|
+
}
|
|
10634
|
+
}
|
|
9744
10635
|
if (this.mcpPool !== void 0 && typeof config.setMcpTransportPool === "function") {
|
|
9745
10636
|
config.setMcpTransportPool(this.mcpPool);
|
|
9746
10637
|
}
|
|
@@ -9754,13 +10645,20 @@ ${outputText}
|
|
|
9754
10645
|
sessionId: sid,
|
|
9755
10646
|
...event
|
|
9756
10647
|
}).catch((err) => {
|
|
9757
|
-
|
|
10648
|
+
debugLogger6.debug(
|
|
9758
10649
|
`MCP budget extNotification dropped (session=${sid}, kind=${event.kind}): ${err instanceof Error ? err.message : String(err)}`
|
|
9759
10650
|
);
|
|
9760
10651
|
});
|
|
9761
10652
|
});
|
|
9762
10653
|
}
|
|
9763
|
-
await config.initialize(
|
|
10654
|
+
await config.initialize({
|
|
10655
|
+
// Reverse tool channel (issue #5626, Phase 2): bind the session
|
|
10656
|
+
// manager's SDK MCP callback to the `client_mcp/message` ext-method so a
|
|
10657
|
+
// client-hosted (extension) MCP server added at runtime reaches the
|
|
10658
|
+
// daemon WS. Servers that aren't client-hosted never use this callback
|
|
10659
|
+
// (the daemon only adds SDK-type runtime servers for client MCP).
|
|
10660
|
+
sendSdkMcpMessage: this.buildClientMcpSender()
|
|
10661
|
+
});
|
|
9764
10662
|
await config.waitForMcpReady();
|
|
9765
10663
|
const failedMcpServers = typeof config.getFailedMcpServerNames === "function" ? config.getFailedMcpServerNames() : [];
|
|
9766
10664
|
if (failedMcpServers.length > 0) {
|
|
@@ -9782,7 +10680,7 @@ ${outputText}
|
|
|
9782
10680
|
try {
|
|
9783
10681
|
await config.refreshAuth(selectedType, true);
|
|
9784
10682
|
} catch (e) {
|
|
9785
|
-
|
|
10683
|
+
debugLogger6.error(`Authentication failed: ${e}`);
|
|
9786
10684
|
throw RequestError.authRequired(
|
|
9787
10685
|
{
|
|
9788
10686
|
authMethods: pickAuthMethodsForAuthRequired(selectedType)
|
|
@@ -9797,7 +10695,20 @@ ${outputText}
|
|
|
9797
10695
|
this.connection,
|
|
9798
10696
|
config.getSessionId(),
|
|
9799
10697
|
this.clientCapabilities.fs,
|
|
9800
|
-
config.getFileSystemService()
|
|
10698
|
+
config.getFileSystemService(),
|
|
10699
|
+
{
|
|
10700
|
+
// SYNC: Mirrors ReadFileTool's default allowed local roots, including
|
|
10701
|
+
// auto-memory roots, so ACP-local read fallback follows the same policy.
|
|
10702
|
+
localReadRoots: [
|
|
10703
|
+
config.storage.getProjectTempDir(),
|
|
10704
|
+
path3.join(config.storage.getProjectDir(), "subagents"),
|
|
10705
|
+
Storage.getGlobalTempDir(),
|
|
10706
|
+
getAutoMemoryRoot(config.getTargetDir()),
|
|
10707
|
+
getUserAutoMemoryRoot(),
|
|
10708
|
+
...config.storage.getUserSkillsDirs(),
|
|
10709
|
+
Storage.getUserExtensionsDir()
|
|
10710
|
+
]
|
|
10711
|
+
}
|
|
9801
10712
|
);
|
|
9802
10713
|
config.setFileSystemService(acpFileSystemService);
|
|
9803
10714
|
}
|
|
@@ -9822,6 +10733,9 @@ ${outputText}
|
|
|
9822
10733
|
if (sessionData?.fileHistorySnapshots?.length) {
|
|
9823
10734
|
config.getFileHistoryService().restoreFromSnapshots(sessionData.fileHistorySnapshots);
|
|
9824
10735
|
}
|
|
10736
|
+
if (sessionData?.conversation.messages) {
|
|
10737
|
+
config.getChatRecordingService()?.rebuildTurnBoundaries(sessionData.conversation.messages);
|
|
10738
|
+
}
|
|
9825
10739
|
if (options.replayHistory !== false && sessionData?.conversation.messages) {
|
|
9826
10740
|
await session.replayHistory(sessionData.conversation.messages);
|
|
9827
10741
|
}
|
|
@@ -9832,7 +10746,7 @@ ${outputText}
|
|
|
9832
10746
|
buildAvailableModels(config) {
|
|
9833
10747
|
const rawCurrentModelId = (config.getModel() || this.config.getModel() || "").trim();
|
|
9834
10748
|
const currentAuthType = config.getAuthType();
|
|
9835
|
-
const allConfiguredModels = config.getAllConfiguredModels();
|
|
10749
|
+
const allConfiguredModels = config.getAllConfiguredModels().filter(isMainSelectableModel);
|
|
9836
10750
|
const activeRuntimeSnapshot = config.getActiveRuntimeModelSnapshot?.();
|
|
9837
10751
|
const currentModelId = activeRuntimeSnapshot ? formatAcpModelId(
|
|
9838
10752
|
activeRuntimeSnapshot.id,
|
|
@@ -9868,7 +10782,7 @@ ${outputText}
|
|
|
9868
10782
|
}
|
|
9869
10783
|
buildConfigOptions(config) {
|
|
9870
10784
|
const currentApprovalMode = config.getApprovalMode();
|
|
9871
|
-
const allConfiguredModels = config.getAllConfiguredModels();
|
|
10785
|
+
const allConfiguredModels = config.getAllConfiguredModels().filter(isMainSelectableModel);
|
|
9872
10786
|
const rawCurrentModelId = (config.getModel() || "").trim();
|
|
9873
10787
|
const currentAuthType = config.getAuthType?.();
|
|
9874
10788
|
const activeRuntimeSnapshot = config.getActiveRuntimeModelSnapshot?.();
|
|
@@ -9914,6 +10828,10 @@ ${outputText}
|
|
|
9914
10828
|
return authType ? formatAcpModelId(baseModelId, authType) : baseModelId;
|
|
9915
10829
|
}
|
|
9916
10830
|
};
|
|
10831
|
+
function isMainSelectableModel(model) {
|
|
10832
|
+
return model.fastOnly !== true && model.voiceOnly !== true;
|
|
10833
|
+
}
|
|
10834
|
+
__name(isMainSelectableModel, "isMainSelectableModel");
|
|
9917
10835
|
function diffSettingsKeys(oldMerged, newMerged) {
|
|
9918
10836
|
const changed = /* @__PURE__ */ new Set();
|
|
9919
10837
|
const allKeys = /* @__PURE__ */ new Set([
|
|
@@ -9928,10 +10846,52 @@ function diffSettingsKeys(oldMerged, newMerged) {
|
|
|
9928
10846
|
return changed;
|
|
9929
10847
|
}
|
|
9930
10848
|
__name(diffSettingsKeys, "diffSettingsKeys");
|
|
10849
|
+
function buildCdpTunnelMcpServer() {
|
|
10850
|
+
if (process.env["QWEN_SERVE_CDP_TUNNEL_OVER_WS"] !== "1") return void 0;
|
|
10851
|
+
if (process.env["QWEN_SERVE_CDP_TUNNEL_AUTH_REQUIRED"] === "1") {
|
|
10852
|
+
process.stderr.write(
|
|
10853
|
+
"qwen serve: browser tools (chrome-devtools-mcp) disabled \u2014 the /cdp tunnel requires bearer auth, which is not yet supported for the auto-registered browser MCP server.\n"
|
|
10854
|
+
);
|
|
10855
|
+
return void 0;
|
|
10856
|
+
}
|
|
10857
|
+
const port = Number(process.env["QWEN_SERVE_CDP_TUNNEL_PORT"]);
|
|
10858
|
+
if (!Number.isInteger(port) || port <= 0) {
|
|
10859
|
+
process.stderr.write(
|
|
10860
|
+
`qwen serve: browser tools (chrome-devtools-mcp) disabled \u2014 no valid /cdp tunnel port (QWEN_SERVE_CDP_TUNNEL_PORT=${process.env["QWEN_SERVE_CDP_TUNNEL_PORT"] ?? "<unset>"}). Start the daemon with a fixed --port (ephemeral --port 0 is not supported for the CDP tunnel).
|
|
10861
|
+
`
|
|
10862
|
+
);
|
|
10863
|
+
return void 0;
|
|
10864
|
+
}
|
|
10865
|
+
try {
|
|
10866
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
10867
|
+
const pkgJsonPath = requireFromHere.resolve(
|
|
10868
|
+
"chrome-devtools-mcp/package.json"
|
|
10869
|
+
);
|
|
10870
|
+
const pkg = requireFromHere("chrome-devtools-mcp/package.json");
|
|
10871
|
+
const binRel = typeof pkg.bin === "string" ? pkg.bin : Object.values(pkg.bin ?? {})[0];
|
|
10872
|
+
if (!binRel) return void 0;
|
|
10873
|
+
const pkgDir = path3.dirname(pkgJsonPath);
|
|
10874
|
+
const binPath = path3.resolve(pkgDir, binRel);
|
|
10875
|
+
const binRelToPkg = path3.relative(pkgDir, binPath);
|
|
10876
|
+
if (binRelToPkg.startsWith("..") || path3.isAbsolute(binRelToPkg)) {
|
|
10877
|
+
return void 0;
|
|
10878
|
+
}
|
|
10879
|
+
return new MCPServerConfig(process.execPath, [
|
|
10880
|
+
binPath,
|
|
10881
|
+
"--wsEndpoint",
|
|
10882
|
+
`ws://127.0.0.1:${port}/cdp`
|
|
10883
|
+
]);
|
|
10884
|
+
} catch {
|
|
10885
|
+
return void 0;
|
|
10886
|
+
}
|
|
10887
|
+
}
|
|
10888
|
+
__name(buildCdpTunnelMcpServer, "buildCdpTunnelMcpServer");
|
|
9931
10889
|
export {
|
|
9932
10890
|
AUTH_PREFLIGHT_ENV_KEYS,
|
|
9933
10891
|
AUTH_PREFLIGHT_WAIVED_AUTH_TYPES,
|
|
10892
|
+
buildCdpTunnelMcpServer,
|
|
9934
10893
|
createWorkspaceMcpBudget,
|
|
10894
|
+
deliverClientMcpMessage,
|
|
9935
10895
|
extractFilesFromTarGz,
|
|
9936
10896
|
fetchAllowedGitHub,
|
|
9937
10897
|
normalizeCoreSettingValue,
|