@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.
Files changed (291) hide show
  1. package/bundled/extension-creator/SKILL.md +357 -0
  2. package/bundled/loop/SKILL.md +34 -10
  3. package/bundled/loop/SKILL.test.ts +52 -0
  4. package/bundled/loop/autonomous-loop.test.ts +59 -0
  5. package/bundled/loop/autonomous-loop.ts +102 -0
  6. package/bundled/loop/loop-task-file.test.ts +1064 -0
  7. package/bundled/loop/loop-task-file.ts +404 -0
  8. package/bundled/loop/loop-tick-resolver.test.ts +864 -0
  9. package/bundled/loop/loop-tick-resolver.ts +416 -0
  10. package/bundled/qc-helper/SKILL.md +26 -19
  11. package/bundled/qc-helper/docs/configuration/auth.md +11 -11
  12. package/bundled/qc-helper/docs/configuration/model-providers.md +13 -13
  13. package/bundled/qc-helper/docs/configuration/settings.md +77 -75
  14. package/bundled/qc-helper/docs/features/channels/plugins.md +1 -1
  15. package/bundled/qc-helper/docs/features/commands.md +108 -77
  16. package/bundled/qc-helper/docs/features/hooks.md +6 -1
  17. package/bundled/qc-helper/docs/features/memory.md +53 -1
  18. package/bundled/qc-helper/docs/features/status-line.md +3 -3
  19. package/bundled/qc-helper/docs/ide-integration/ide-integration.md +4 -4
  20. package/bundled/qc-helper/docs/qwen-serve.md +24 -2
  21. package/bundled/qc-helper/docs/support/tos-privacy.md +16 -3
  22. package/bundled/qc-helper/docs/support/troubleshooting.md +6 -0
  23. package/chunks/{MaxSizedBox-7RERNLFK.js → MaxSizedBox-UZHJ73JB.js} +29 -27
  24. package/chunks/{StandaloneSessionPicker-S22XBSN6.js → StandaloneSessionPicker-GGUIZWCE.js} +42 -37
  25. package/chunks/{acpAgent-4MCETHF2.js → acpAgent-QYZFICEZ.js} +1292 -332
  26. package/chunks/{agent-K3ENVYSB.js → agent-2EN7YNSW.js} +24 -23
  27. package/chunks/{agent-headless-HO3V3A3A.js → agent-headless-SLTEZ5LV.js} +24 -23
  28. package/chunks/{anthropicContentGenerator-E3TQ5XJJ.js → anthropicContentGenerator-CA3JTAMO.js} +33 -12
  29. package/chunks/{artifact-tool-QA4KRO3B.js → artifact-tool-RM7WINJO.js} +3 -3
  30. package/chunks/{askUserQuestion-HMWXW6VN.js → askUserQuestion-J3P6QAJ7.js} +2 -2
  31. package/chunks/{bridge-4LV2HBH2.js → bridge-HZVAUTMX.js} +29 -28
  32. package/chunks/{ca-7WC7ELX6.js → ca-RYOETBUW.js} +10 -9
  33. package/chunks/{chunk-JXQ23SNT.js → chunk-2JKEQWDZ.js} +623 -78
  34. package/chunks/chunk-32MCHZQC.js +73 -0
  35. package/chunks/{chunk-PXFFKNCO.js → chunk-35FH347N.js} +5308 -2500
  36. package/chunks/{chunk-5JZLXFPD.js → chunk-3O2BJMFW.js} +11 -11
  37. package/chunks/{chunk-2JD55C6G.js → chunk-3TK7FSCP.js} +2 -2
  38. package/chunks/{chunk-USGMPVOY.js → chunk-4722VFFU.js} +19 -10
  39. package/chunks/chunk-6FIWYVN4.js +4932 -0
  40. package/chunks/{chunk-I2RFAW6Q.js → chunk-7SP275SH.js} +7 -5
  41. package/chunks/{chunk-5R4SBCX6.js → chunk-AIBPG5MB.js} +3 -3
  42. package/chunks/{chunk-C4K3FEQ2.js → chunk-AVQDCKNF.js} +124 -21
  43. package/chunks/{chunk-OTBK43JR.js → chunk-B236XPGF.js} +1 -1
  44. package/chunks/{chunk-WA5OJGFL.js → chunk-B5MPZI4K.js} +458 -125
  45. package/chunks/{chunk-5DWXLGOM.js → chunk-BJBWRCSK.js} +1 -1
  46. package/chunks/{chunk-4W4T77MO.js → chunk-BLAE2TCO.js} +109 -6
  47. package/chunks/chunk-BMFMJINR.js +45 -0
  48. package/chunks/{chunk-T2VDEFUU.js → chunk-BZHLWC6T.js} +2 -2
  49. package/chunks/{chunk-XU5P23GN.js → chunk-CA4ML3VH.js} +3 -3
  50. package/chunks/{chunk-WGAEWQTF.js → chunk-DBR7PF4B.js} +1 -2
  51. package/chunks/{chunk-S566HKUS.js → chunk-DHXWNSJA.js} +15 -1
  52. package/chunks/{chunk-LLFFKXZZ.js → chunk-DJ25MIU2.js} +167 -43
  53. package/chunks/{chunk-KBXLIVWQ.js → chunk-DX3TKXNT.js} +2 -2
  54. package/chunks/{chunk-OIC6S73E.js → chunk-E5I6YSNO.js} +1 -1
  55. package/chunks/{chunk-TXVGVZYR.js → chunk-ESVSWG6Z.js} +3 -3
  56. package/chunks/{chunk-EX4W6ZYG.js → chunk-EUURTJY7.js} +1 -1
  57. package/chunks/{chunk-QBZ22PQX.js → chunk-EWALXUAD.js} +3 -1
  58. package/chunks/{chunk-F6XNTYNE.js → chunk-FQDMX5IL.js} +30 -307
  59. package/chunks/{chunk-3QFE4OTE.js → chunk-FR3II765.js} +4 -4
  60. package/chunks/{chunk-VYFMN7C6.js → chunk-FV4SQ54H.js} +382 -55
  61. package/chunks/{chunk-ZD3NW5KL.js → chunk-FZY7NH37.js} +63 -9
  62. package/chunks/{chunk-CI5O6SQL.js → chunk-G525INVW.js} +4 -4
  63. package/chunks/{chunk-CGOMICBE.js → chunk-GDIG7TMG.js} +34 -1
  64. package/chunks/{chunk-R2BXK7SX.js → chunk-GHOTR7HL.js} +6 -1
  65. package/chunks/{chunk-TZM5JIAX.js → chunk-GZKQ36JS.js} +10 -7
  66. package/chunks/{chunk-BS64PXY3.js → chunk-HIYUAXBG.js} +1 -1
  67. package/chunks/{chunk-TMBPDHXD.js → chunk-HTWHAN44.js} +1 -1
  68. package/chunks/{chunk-EL5IHQON.js → chunk-HUGAEYZR.js} +6 -27
  69. package/chunks/{chunk-2BIF2NVV.js → chunk-HWG4XLD7.js} +2 -2
  70. package/chunks/{chunk-I5T6PIBQ.js → chunk-I4MXURND.js} +1076 -954
  71. package/chunks/{chunk-JTYRZODB.js → chunk-IBEM64M4.js} +39 -11
  72. package/chunks/{chunk-XKBP7M7A.js → chunk-IKO44NAT.js} +149 -31
  73. package/chunks/{chunk-2BAQ3ACS.js → chunk-JERW36RG.js} +404 -4
  74. package/chunks/{chunk-TOEGRAVX.js → chunk-JV7A66DR.js} +28 -16
  75. package/chunks/{chunk-3RJ3ZPPR.js → chunk-KKQWC6WS.js} +7 -7
  76. package/chunks/{chunk-EXMX5YHG.js → chunk-KXVVXAAC.js} +1 -1
  77. package/chunks/{chunk-Z7XXFPGP.js → chunk-LDWTYE33.js} +1 -1
  78. package/chunks/{chunk-X6EDWN44.js → chunk-LWGLBVWA.js} +1 -1
  79. package/chunks/{chunk-NYOHZYHM.js → chunk-LXEW6BHF.js} +45 -3
  80. package/chunks/{chunk-BGUDAULQ.js → chunk-LXGWLOCM.js} +5 -5
  81. package/chunks/{chunk-ZUAXI2OA.js → chunk-M2U5NM24.js} +188 -11366
  82. package/chunks/{chunk-BNXANYLH.js → chunk-MOUXUCXQ.js} +81 -1
  83. package/chunks/{chunk-FPACXT7T.js → chunk-MTJVHQDP.js} +534 -164
  84. package/chunks/{chunk-UAARJR2G.js → chunk-N4V4VBP6.js} +2 -2
  85. package/chunks/chunk-NIOPRZNN.js +27 -0
  86. package/chunks/{chunk-2TJ3YN6Q.js → chunk-NOYV2NXI.js} +517 -490
  87. package/chunks/{chunk-PRYMDRBV.js → chunk-NPQWQYU6.js} +1 -1
  88. package/chunks/{chunk-J6AW7O5D.js → chunk-NVTRORSK.js} +2 -2
  89. package/chunks/{chunk-PF523XJX.js → chunk-OE5J6UA7.js} +1 -1
  90. package/chunks/{chunk-RS4HRC75.js → chunk-OEY3YAL2.js} +99 -8
  91. package/chunks/{chunk-R36VMVEP.js → chunk-PLQTLKMR.js} +5 -5
  92. package/chunks/{chunk-UN3C6GOR.js → chunk-PPFWEV7G.js} +10 -108
  93. package/chunks/chunk-Q3JOGB4U.js +481 -0
  94. package/chunks/{chunk-HG6YKEBI.js → chunk-Q7TDHJ2G.js} +192 -30
  95. package/chunks/{chunk-EW6C27XX.js → chunk-QHHNFG22.js} +9 -3
  96. package/chunks/chunk-QYUE6W3T.js +123 -0
  97. package/chunks/{chunk-GZCJS5WH.js → chunk-SJBIFVD2.js} +2 -2
  98. package/chunks/{chunk-3UALQ56H.js → chunk-T42ACHFI.js} +1 -1
  99. package/chunks/chunk-T44AF4I6.js +690 -0
  100. package/chunks/{chunk-DBZUXSSV.js → chunk-TMILTLP3.js} +6 -6
  101. package/chunks/{chunk-EEEZIVFJ.js → chunk-TOAUDIS7.js} +1 -1
  102. package/chunks/chunk-UAKXW3DN.js +30 -0
  103. package/chunks/{chunk-VQRYPFCV.js → chunk-V5YJAUHW.js} +10 -460
  104. package/chunks/{chunk-D5E5TMYH.js → chunk-VHRREANT.js} +16 -2
  105. package/chunks/{chunk-5RVSM6NW.js → chunk-VN4CKYUW.js} +1 -1
  106. package/chunks/chunk-VXGPYF7U.js +37 -0
  107. package/chunks/{chunk-UAARVVBI.js → chunk-WGY3GIPP.js} +1 -1
  108. package/chunks/{chunk-GYZQA2FO.js → chunk-WJAY4WXR.js} +1 -1
  109. package/chunks/chunk-XKSCTXPK.js +167 -0
  110. package/chunks/chunk-YCEHOJZJ.js +840 -0
  111. package/chunks/{chunk-BG4BIP7F.js → chunk-YCRTJYSQ.js} +4 -2
  112. package/chunks/{chunk-CGEGHF2C.js → chunk-YPCPZSZB.js} +1 -1
  113. package/chunks/chunk-YTFBURQD.js +313 -0
  114. package/chunks/{chunk-HGNSRCRB.js → chunk-YXK4GPYO.js} +1 -1
  115. package/chunks/{chunk-OQHQHJRK.js → chunk-Z34RYLRO.js} +151 -4775
  116. package/chunks/chunk-ZGSV5ORL.js +11273 -0
  117. package/chunks/{computer-use-6BBRNT6S.js → computer-use-FCNWHVPW.js} +103 -49
  118. package/chunks/{contextCommand-UJAJ6N57.js → contextCommand-FVFMZZEU.js} +26 -25
  119. package/chunks/{cron-create-4KQE6YLH.js → cron-create-IY5NMIHQ.js} +4 -4
  120. package/chunks/{cron-delete-THME66UP.js → cron-delete-OLNEQWKO.js} +6 -6
  121. package/chunks/{cron-list-7A54SJ5N.js → cron-list-YAUAUV24.js} +5 -5
  122. package/chunks/{daemon-status-provider-CSL3BUOY.js → daemon-status-provider-42JEXENZ.js} +32 -32
  123. package/chunks/{de-ACHALDED.js → de-HB5NJXGB.js} +10 -9
  124. package/chunks/{dist-BD27IA3L.js → dist-4FCNM6KJ.js} +2 -2
  125. package/chunks/{dist-OD2FY543.js → dist-K4XZILIO.js} +1 -1
  126. package/chunks/{dist-BOOH5JWN.js → dist-SU6L7ZNJ.js} +114 -9
  127. package/chunks/{dist-QHJRBCMP.js → dist-UKPWQJCL.js} +44 -2
  128. package/chunks/{dist-QFFZWACU.js → dist-VD7HXTML.js} +20 -9
  129. package/chunks/{earlyInputCapture-X6KMCSHD.js → earlyInputCapture-FFFEC6P4.js} +25 -24
  130. package/chunks/{edit-7J6WXARF.js → edit-32ILSGZY.js} +72 -30
  131. package/chunks/{en-NCUBOJOY.js → en-U3LWGSFX.js} +19 -9
  132. package/chunks/{enter-worktree-KULCKJPJ.js → enter-worktree-XWZKIN6A.js} +24 -23
  133. package/chunks/{enterPlanMode-H2CIOLMH.js → enterPlanMode-LPFFY7BJ.js} +24 -23
  134. package/chunks/{errors-6G2JYR54.js → errors-UXBPYPI2.js} +26 -25
  135. package/chunks/{exit-worktree-ECBWBVC5.js → exit-worktree-IBS4UIU7.js} +24 -23
  136. package/chunks/{exitPlanMode-DQV56GTW.js → exitPlanMode-JA72YHII.js} +24 -23
  137. package/chunks/{fast-path-35JJFS3C.js → fast-path-7UU67Y2W.js} +30 -13
  138. package/chunks/{fast-path-settings-TSVI53EB.js → fast-path-settings-UQLRFDQP.js} +3 -2
  139. package/chunks/{fr-GVKZ27MF.js → fr-XY3NQFCM.js} +10 -9
  140. package/chunks/{gemini-NDTG7WAX.js → gemini-GMVKJNMX.js} +211 -105
  141. package/chunks/{geminiContentGenerator-XTG6SFLA.js → geminiContentGenerator-7NIFF2L4.js} +4 -4
  142. package/chunks/{glob-SJ2JBZK4.js → glob-4NJCW5HI.js} +24 -23
  143. package/chunks/{grep-BRMWFEWR.js → grep-PAIMLQEL.js} +24 -23
  144. package/chunks/{initializer-P5AXF7PV.js → initializer-R6KU4VYM.js} +31 -28
  145. package/chunks/{ja-FKZ2WZ4D.js → ja-J7YAG7SA.js} +10 -9
  146. package/chunks/{keychain-token-storage-CSYMNRPP.js → keychain-token-storage-GPAKRDKQ.js} +2 -2
  147. package/chunks/{list-6COK34BP.js → list-H7LXAO64.js} +32 -29
  148. package/chunks/{loadedSettingsAdapter-XIUTN245.js → loadedSettingsAdapter-H27JMTLU.js} +30 -27
  149. package/chunks/{loop-wakeup-33GPXJLA.js → loop-wakeup-Y3OIRQFM.js} +7 -7
  150. package/chunks/{ls-BYQY7RVV.js → ls-TVJEYS7O.js} +4 -4
  151. package/chunks/{lsp-X54RO4IV.js → lsp-JZSDVR3F.js} +2 -2
  152. package/chunks/{monitor-DH7KFSJV.js → monitor-JSIHDPXP.js} +24 -23
  153. package/chunks/nonInteractiveCli-Y4CHOETG.js +100 -0
  154. package/chunks/{notebook-edit-D4R4ZLMA.js → notebook-edit-SHRNL2UM.js} +52 -23
  155. package/chunks/{openaiContentGenerator-F74IPQ6E.js → openaiContentGenerator-ORZZ7VFO.js} +13 -12
  156. package/chunks/{pt-SY5NCFGR.js → pt-6RTAA6SS.js} +10 -9
  157. package/chunks/{qwenContentGenerator-ATTK5P4D.js → qwenContentGenerator-H643PL2G.js} +26 -25
  158. package/chunks/{qwenOAuth2-K4AASPCV.js → qwenOAuth2-OI2B7PES.js} +5 -4
  159. package/chunks/{read-file-767G23OY.js → read-file-5PRNPTTY.js} +8 -8
  160. package/chunks/{read-mcp-resource-I5HCZJ6X.js → read-mcp-resource-QTXGKTEV.js} +2 -2
  161. package/chunks/{ripGrep-4OMMGSWX.js → ripGrep-JIKZMABJ.js} +24 -23
  162. package/chunks/{ru-FWNXJG4F.js → ru-AUBGJ2C4.js} +10 -9
  163. package/chunks/{run-qwen-serve-REKSTYRA.js → run-qwen-serve-PQV7QOHZ.js} +284 -52
  164. package/chunks/{scheduler-UNTFDQLG.js → scheduler-OS3R5MGU.js} +24 -23
  165. package/chunks/{send-message-2DAFXEYP.js → send-message-DKGHWSTF.js} +2 -2
  166. package/chunks/{serve-KZ56QB5S.js → serve-LVYZDKCZ.js} +31 -29
  167. package/chunks/{server-BV5TZUMU.js → server-P4YIH35I.js} +11458 -8447
  168. package/chunks/{session-OIBXFSGD.js → session-DE4TZBKH.js} +195 -58
  169. package/chunks/{settings-WJTCBIRA.js → settings-DFTYMJYF.js} +30 -27
  170. package/chunks/{shell-WC52IBHW.js → shell-3GNQLNV7.js} +24 -23
  171. package/chunks/{skill-U7F7JR4M.js → skill-NCNG4KKZ.js} +38 -11
  172. package/chunks/{spawnChannel-EI2E5BCW.js → spawnChannel-DHRIMDVL.js} +26 -25
  173. package/chunks/{src-4F6FBZFI.js → src-PNZYGM3R.js} +104 -28
  174. package/chunks/{startInteractiveUI-SFRVB3CE.js → startInteractiveUI-P6GZBFQW.js} +2817 -2500
  175. package/chunks/{syntheticOutput-PPS3HDVA.js → syntheticOutput-X7L5HWLN.js} +3 -3
  176. package/chunks/{task-create-OUYCA553.js → task-create-AWEUI4GI.js} +6 -6
  177. package/chunks/{task-list-WAX7I2IA.js → task-list-KZ3VJLDU.js} +5 -5
  178. package/chunks/{task-stop-CAIL6FNH.js → task-stop-GUNOZKJ5.js} +2 -2
  179. package/chunks/{task-update-AHEO5OUT.js → task-update-Y56GP6DU.js} +6 -6
  180. package/chunks/{team-create-3WC7CIIW.js → team-create-6JMW3KQK.js} +24 -23
  181. package/chunks/{team-delete-HGHFG23B.js → team-delete-6AFRGDJY.js} +5 -5
  182. package/chunks/{theme-manager-DICFCT3P.js → theme-manager-FEHQO22G.js} +25 -24
  183. package/chunks/{todoWrite-IFBMN3FH.js → todoWrite-JRS6NOEU.js} +4 -4
  184. package/chunks/{tool-search-L5K7HLER.js → tool-search-IXAEKS7J.js} +101 -16
  185. package/chunks/trustedFolders-SJRXNHZE.js +76 -0
  186. package/chunks/types-QX5C3CHJ.js +12 -0
  187. package/chunks/{validateNonInterActiveAuth-GCH4Z6KI.js → validateNonInterActiveAuth-HCH7OBDH.js} +56 -51
  188. package/chunks/{web-fetch-B5IIQ5TT.js → web-fetch-RELNL37A.js} +29 -15
  189. package/chunks/{workflow-OB3AND7I.js → workflow-M2COBI7R.js} +38 -32
  190. package/chunks/{workspace-providers-status-ZIR5EPOO.js → workspace-providers-status-GOCWYDMK.js} +32 -29
  191. package/chunks/workspace-service-34C7SUBU.js +75 -0
  192. package/chunks/{write-file-SEXBNDZM.js → write-file-E52WRXZL.js} +67 -31
  193. package/chunks/{zh-RBEGQVQT.js → zh-F5T4WWZO.js} +19 -9
  194. package/chunks/{zh-TW-QGAVRXT3.js → zh-TW-LKLBTZHE.js} +19 -9
  195. package/cli-entry.js +20 -9
  196. package/cli.js +6 -6
  197. package/locales/ca.js +15 -13
  198. package/locales/de.js +15 -13
  199. package/locales/en.js +31 -13
  200. package/locales/fr.js +16 -14
  201. package/locales/ja.js +15 -14
  202. package/locales/pt.js +15 -13
  203. package/locales/ru.js +15 -13
  204. package/locales/zh-TW.js +28 -12
  205. package/locales/zh.js +28 -12
  206. package/node_modules/@qwen-code/audio-capture/dist/index.d.ts +35 -0
  207. package/node_modules/@qwen-code/audio-capture/dist/index.js +48 -0
  208. package/node_modules/@qwen-code/audio-capture/dist/index.js.map +1 -0
  209. package/node_modules/@qwen-code/audio-capture/dist/platform.d.ts +7 -0
  210. package/node_modules/@qwen-code/audio-capture/dist/platform.js +18 -0
  211. package/node_modules/@qwen-code/audio-capture/dist/platform.js.map +1 -0
  212. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/LICENSE +21 -0
  213. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/README.md +58 -0
  214. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/SECURITY.md +5 -0
  215. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/bin.js +84 -0
  216. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/build-test.js +19 -0
  217. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/index.js +6 -0
  218. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/node-gyp-build.js +207 -0
  219. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/optional.js +7 -0
  220. package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/package.json +43 -0
  221. package/node_modules/@qwen-code/audio-capture/package.json +30 -0
  222. package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-arm64/@qwen-code+audio-capture.node +0 -0
  223. package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-x64/@qwen-code+audio-capture.node +0 -0
  224. package/node_modules/@qwen-code/audio-capture/prebuilds/linux-arm64/@qwen-code+audio-capture.node +0 -0
  225. package/node_modules/@qwen-code/audio-capture/prebuilds/linux-x64/@qwen-code+audio-capture.node +0 -0
  226. package/node_modules/@qwen-code/audio-capture/prebuilds/win32-x64/@qwen-code+audio-capture.node +0 -0
  227. package/package.json +18 -6
  228. package/patches/chrome-devtools-mcp+1.4.0.patch +31 -0
  229. package/postinstall.js +50 -0
  230. package/web-shell/assets/{arc-D_ae_Y9k.js → arc-C2DILvUl.js} +1 -1
  231. package/web-shell/assets/{architectureDiagram-3BPJPVTR-BMLRjppa.js → architectureDiagram-3BPJPVTR-b8gZ2cNw.js} +1 -1
  232. package/web-shell/assets/{blockDiagram-GPEHLZMM-qVDQC8KL.js → blockDiagram-GPEHLZMM-BuRpghMj.js} +1 -1
  233. package/web-shell/assets/{c4Diagram-AAUBKEIU-CxWS6MBd.js → c4Diagram-AAUBKEIU-2ItdhFmE.js} +1 -1
  234. package/web-shell/assets/channel-eRvn2UA3.js +1 -0
  235. package/web-shell/assets/{chunk-2J33WTMH-BtNuY5Yp.js → chunk-2J33WTMH-B1QXa0q3.js} +1 -1
  236. package/web-shell/assets/{chunk-4BX2VUAB-BU-x368d.js → chunk-4BX2VUAB-DXfhKlNq.js} +1 -1
  237. package/web-shell/assets/{chunk-55IACEB6-DRgTLvBu.js → chunk-55IACEB6-ClSNjxLo.js} +1 -1
  238. package/web-shell/assets/{chunk-727SXJPM-D2aElv7q.js → chunk-727SXJPM-Dsh2wisB.js} +1 -1
  239. package/web-shell/assets/{chunk-AQP2D5EJ-BwHCMRQ0.js → chunk-AQP2D5EJ-C1TkY5AR.js} +1 -1
  240. package/web-shell/assets/{chunk-FMBD7UC4-BMyPH-ku.js → chunk-FMBD7UC4-CGtN9XR6.js} +1 -1
  241. package/web-shell/assets/{chunk-ND2GUHAM-BNKTstUf.js → chunk-ND2GUHAM-BHiPi1SS.js} +1 -1
  242. package/web-shell/assets/{chunk-QZHKN3VN-gD3vsFId.js → chunk-QZHKN3VN-CzG3I5o1.js} +1 -1
  243. package/web-shell/assets/classDiagram-4FO5ZUOK-YZNwy1rx.js +1 -0
  244. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-YZNwy1rx.js +1 -0
  245. package/web-shell/assets/{cose-bilkent-S5V4N54A-BG9FAsXE.js → cose-bilkent-S5V4N54A-EgrIoH_K.js} +1 -1
  246. package/web-shell/assets/{dagre-BM42HDAG-Cy1sR7om.js → dagre-BM42HDAG-BUnpdX8S.js} +1 -1
  247. package/web-shell/assets/default-aEtf0G7-.svg +1 -0
  248. package/web-shell/assets/{diagram-2AECGRRQ-Bxi1I_Vu.js → diagram-2AECGRRQ-BlLUCVYu.js} +1 -1
  249. package/web-shell/assets/{diagram-5GNKFQAL-BGCJjO6y.js → diagram-5GNKFQAL-FG-enmjQ.js} +1 -1
  250. package/web-shell/assets/{diagram-KO2AKTUF-CMTeLXp7.js → diagram-KO2AKTUF-BlzmTONk.js} +1 -1
  251. package/web-shell/assets/{diagram-LMA3HP47-BN1wyAGh.js → diagram-LMA3HP47-CoBQ8mIx.js} +1 -1
  252. package/web-shell/assets/{diagram-OG6HWLK6-UYSG_zcp.js → diagram-OG6HWLK6-DvxV6sX8.js} +1 -1
  253. package/web-shell/assets/{erDiagram-TEJ5UH35-DqXL08lw.js → erDiagram-TEJ5UH35-Jq1tpAoj.js} +1 -1
  254. package/web-shell/assets/{flowDiagram-I6XJVG4X-BPJ9SFji.js → flowDiagram-I6XJVG4X-CbtYOry5.js} +1 -1
  255. package/web-shell/assets/{ganttDiagram-6RSMTGT7-BEd9Pnms.js → ganttDiagram-6RSMTGT7-ZEn2VEKR.js} +1 -1
  256. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-DrNRH3oi.js → gitGraphDiagram-PVQCEYII-BZh-LkhJ.js} +1 -1
  257. package/web-shell/assets/index-5FhE0ORO.css +5 -0
  258. package/web-shell/assets/index-CDTGmqVm.js +720 -0
  259. package/web-shell/assets/{infoDiagram-5YYISTIA-CFYBrkVI.js → infoDiagram-5YYISTIA-B9HuZygK.js} +1 -1
  260. package/web-shell/assets/insert-DmRbh9Rv.svg +1 -0
  261. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-Dxsddth7.js → ishikawaDiagram-YF4QCWOH-D76cNpYG.js} +1 -1
  262. package/web-shell/assets/{journeyDiagram-JHISSGLW-DxaDK80t.js → journeyDiagram-JHISSGLW-6bGo7P1t.js} +1 -1
  263. package/web-shell/assets/{kanban-definition-UN3LZRKU-Q0jQCOct.js → kanban-definition-UN3LZRKU-DuYyRBvB.js} +1 -1
  264. package/web-shell/assets/{linear-DGGM2kcP.js → linear-CpdSW1BC.js} +1 -1
  265. package/web-shell/assets/{mermaid.core-ByoxnWut.js → mermaid.core-s5xbZMMj.js} +5 -5
  266. package/web-shell/assets/{mindmap-definition-RKZ34NQL-C0ZseHsi.js → mindmap-definition-RKZ34NQL-go01fZ2y.js} +1 -1
  267. package/web-shell/assets/{pieDiagram-4H26LBE5-MQXNhl5A.js → pieDiagram-4H26LBE5-BfuZiEHO.js} +1 -1
  268. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-DIR4ee2A.js → quadrantDiagram-W4KKPZXB-DsoINqV6.js} +1 -1
  269. package/web-shell/assets/queue-DWpnpFgW.svg +1 -0
  270. package/web-shell/assets/{requirementDiagram-4Y6WPE33-BTR0UdYQ.js → requirementDiagram-4Y6WPE33-B7FU4LiU.js} +1 -1
  271. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-sfnhMELN.js → sankeyDiagram-5OEKKPKP-BEDjFnGB.js} +1 -1
  272. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-DXczHOHH.js → sequenceDiagram-3UESZ5HK-gzmmL5tK.js} +1 -1
  273. package/web-shell/assets/{stateDiagram-AJRCARHV-DdE_KQFH.js → stateDiagram-AJRCARHV-BtHD4IN_.js} +1 -1
  274. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-DFy6gLDd.js +1 -0
  275. package/web-shell/assets/{timeline-definition-PNZ67QCA-DIc61uE8.js → timeline-definition-PNZ67QCA-T_8gmMCf.js} +1 -1
  276. package/web-shell/assets/{vennDiagram-CIIHVFJN-BF7ODmxB.js → vennDiagram-CIIHVFJN-DdjuqnjO.js} +1 -1
  277. package/web-shell/assets/{wardley-L42UT6IY-3pc1MEaw.js → wardley-L42UT6IY-Cgtrvb37.js} +1 -1
  278. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-9jnPcZuP.js → wardleyDiagram-YWT4CUSO-BW3f3-yW.js} +1 -1
  279. package/web-shell/assets/{xychartDiagram-2RQKCTM6-DcthaZK6.js → xychartDiagram-2RQKCTM6-BUS9IKkc.js} +1 -1
  280. package/web-shell/index.html +2 -2
  281. package/chunks/chunk-B6HQGTYR.js +0 -410
  282. package/chunks/chunk-RKIZXJMB.js +0 -13
  283. package/chunks/headlessSafetyWarnings-LJ6FJX77.js +0 -57
  284. package/chunks/nonInteractiveCli-7575LBXP.js +0 -91
  285. package/chunks/workspace-service-OM5D4OI2.js +0 -60
  286. package/web-shell/assets/channel-DYPNiTww.js +0 -1
  287. package/web-shell/assets/classDiagram-4FO5ZUOK-p_g25Tni.js +0 -1
  288. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-p_g25Tni.js +0 -1
  289. package/web-shell/assets/index-CpOXWDPh.js +0 -714
  290. package/web-shell/assets/index-oVK7qhre.css +0 -5
  291. 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-LLFFKXZZ.js";
7
- import "./chunk-B6HQGTYR.js";
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-2TJ3YN6Q.js";
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-ZUAXI2OA.js";
33
+ } from "./chunk-ZGSV5ORL.js";
24
34
  import {
25
35
  getCommandSubcommandNames
26
- } from "./chunk-4W4T77MO.js";
36
+ } from "./chunk-BLAE2TCO.js";
37
+ import "./chunk-32MCHZQC.js";
27
38
  import {
28
39
  getEffectiveSupportedModes
29
- } from "./chunk-JTYRZODB.js";
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-I2RFAW6Q.js";
48
+ import "./chunk-JZSY4WP3.js";
49
+ import "./chunk-HTWHAN44.js";
50
+ import "./chunk-7SP275SH.js";
42
51
  import "./chunk-3GONHQOA.js";
43
- import "./chunk-5R4SBCX6.js";
44
- import "./chunk-Z7XXFPGP.js";
45
- import "./chunk-JXQ23SNT.js";
46
- import "./chunk-TMBPDHXD.js";
52
+ import "./chunk-AIBPG5MB.js";
53
+ import "./chunk-LDWTYE33.js";
54
+ import "./chunk-2JKEQWDZ.js";
47
55
  import {
48
- PERMISSION_RULE_TYPES,
49
- PermissionRulesValidationError,
50
- buildPermissionSettings,
51
- normalizePermissionRules,
52
- readPermissionRuleSet
53
- } from "./chunk-WGAEWQTF.js";
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-RS4HRC75.js";
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-S566HKUS.js";
71
- import "./chunk-BS64PXY3.js";
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-DBZUXSSV.js";
74
- import "./chunk-R2BXK7SX.js";
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-5RVSM6NW.js";
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-OQHQHJRK.js";
103
+ } from "./chunk-6FIWYVN4.js";
92
104
  import {
93
105
  SUPPORTED_LANGUAGES,
94
106
  getCurrentLanguage,
95
107
  setLanguageAsync
96
- } from "./chunk-5JZLXFPD.js";
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-D5E5TMYH.js";
113
- import "./chunk-UN3C6GOR.js";
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-PXFFKNCO.js";
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-TXVGVZYR.js";
203
- import "./chunk-XU5P23GN.js";
204
- import "./chunk-2BIF2NVV.js";
231
+ import "./chunk-ESVSWG6Z.js";
232
+ import "./chunk-CA4ML3VH.js";
233
+ import "./chunk-HWG4XLD7.js";
205
234
  import {
206
235
  runSideQuery
207
- } from "./chunk-BG4BIP7F.js";
208
- import "./chunk-ZD3NW5KL.js";
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-GZCJS5WH.js";
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-OTBK43JR.js";
244
+ import "./chunk-B236XPGF.js";
216
245
  import {
217
246
  HookEventName,
218
247
  createHookOutput
219
- } from "./chunk-HGNSRCRB.js";
220
- import "./chunk-EEEZIVFJ.js";
221
- import "./chunk-PRYMDRBV.js";
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-WA5OJGFL.js";
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
- } from "./chunk-BNXANYLH.js";
250
- import "./chunk-T2VDEFUU.js";
251
- import "./chunk-OIC6S73E.js";
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-TOEGRAVX.js";
287
+ } from "./chunk-JV7A66DR.js";
256
288
  import {
257
289
  clearCachedCredentialFile,
258
290
  qwenOAuth2Events
259
- } from "./chunk-BGUDAULQ.js";
260
- import "./chunk-EW6C27XX.js";
291
+ } from "./chunk-LXGWLOCM.js";
292
+ import "./chunk-QHHNFG22.js";
261
293
  import "./chunk-22IFUCVR.js";
262
- import "./chunk-64WXLC72.js";
263
- import "./chunk-3UALQ56H.js";
294
+ import "./chunk-T42ACHFI.js";
264
295
  import {
296
+ DEFAULT_TOKEN_LIMIT,
265
297
  tokenLimit
266
- } from "./chunk-F6XNTYNE.js";
267
- import "./chunk-CGEGHF2C.js";
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
- getErrorStatus
277
- } from "./chunk-C4K3FEQ2.js";
278
- import "./chunk-PF523XJX.js";
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-GYZQA2FO.js";
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 (typeof error === "object" && error !== null && "code" in error) {
331
- return error.code;
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
- throw error;
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
- await this.connection.writeTextFile({
379
- ...params,
380
- content: finalContent,
381
- sessionId: this.sessionId
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 * as path from "node:path";
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 debugLogger = createDebugLogger("ACP_SUBAGENT_TRACKER");
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
- debugLogger.warn(`Failed to build subagent tool ${event.name}:`, e);
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
- debugLogger.error(
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
- debugLogger.error(
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 debugLogger2 = createDebugLogger("MESSAGE_REWRITER");
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
- debugLogger2.info(
1009
+ debugLogger3.info(
856
1010
  `Loaded rewrite prompt from file: ${filePath} (${this.prompt.length} chars)`
857
1011
  );
858
1012
  } else {
859
- debugLogger2.warn(
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
- debugLogger2.info(
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
- debugLogger2.info(`[REWRITE OUTPUT] empty or too short, skipping`);
1079
+ debugLogger3.info(`[REWRITE OUTPUT] empty or too short, skipping`);
926
1080
  return null;
927
1081
  }
928
- debugLogger2.info(
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
- debugLogger2.warn(
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 debugLogger3 = createDebugLogger("MESSAGE_REWRITE");
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
- debugLogger3.info(`Turn ${turnIdx}: no rewrite output`);
1180
+ debugLogger4.info(`Turn ${turnIdx}: no rewrite output`);
1027
1181
  return;
1028
1182
  }
1029
- debugLogger3.info(
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
- debugLogger3.warn(
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 debugLogger4 = createDebugLogger("SESSION");
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
- function isRecord(value) {
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(isRecord, "isRecord");
1320
+ __name(isRecord2, "isRecord");
1109
1321
  function isContentBlock(value) {
1110
- if (!isRecord(value) || typeof value["type"] !== "string") return false;
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
- debugLogger4.warn(`Unknown ContentBlock type: ${value["type"]}`);
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 (!isRecord(value) || typeof value["uri"] !== "string") return false;
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
- debugLogger4.warn(
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
- debugLogger4.warn(
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
- debugLogger4.warn(
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 (!isRecord(response)) return [];
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 (!isRecord(item)) {
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
- debugLogger4.warn(
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
- debugLogger4.warn(
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
- debugLogger4.error("Error loading available skills:", error);
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
- debugLogger4.warn(
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
- debugLogger4.info("Message rewrite middleware enabled");
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
- debugLogger4.warn(
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
- debugLogger4.warn(
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
- debugLogger4.debug(
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
- debugLogger4.debug("Follow-up suggestion generation aborted");
2115
+ debugLogger5.debug("Follow-up suggestion generation aborted");
1845
2116
  } else {
1846
- debugLogger4.warn("Follow-up suggestion generation failed", error);
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
- if (isRetry) {
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 (isSlashCommand(inputText)) {
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
- `\u{1F6AB} **UserPromptSubmit blocked**: ${blockReason}`
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
- debugLogger4.error(`FileHistory: recordSnapshot failed: ${e}`);
2261
+ debugLogger5.error(`FileHistory: recordSnapshot failed: ${e}`);
1956
2262
  }
1957
2263
  } catch (e) {
1958
- debugLogger4.error(`FileHistory: makeSnapshot failed: ${e}`);
2264
+ debugLogger5.error(`FileHistory: makeSnapshot failed: ${e}`);
1959
2265
  }
1960
2266
  const systemReminders = await this.#buildInitialSystemReminders();
1961
2267
  if (systemReminders.length > 0) {
1962
- parts = [...systemReminders, ...parts];
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
- parts = [
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
- ...parts
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
- debugLogger4.warn(`StopFailure hook failed: ${err}`);
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
- role: "user",
2080
- parts: [
2081
- ...toolRun.parts,
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
- debugLogger4.warn(warning);
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
- debugLogger4.warn(`StopFailure hook failed: ${err}`);
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
- role: "user",
2275
- parts: [
2276
- ...toolRun.parts,
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
- debugLogger4.debug(`Auto-compression aborted for prompt ${promptId}`);
2642
+ debugLogger5.debug(`Auto-compression aborted for prompt ${promptId}`);
2326
2643
  return { responseStream: null, stopReason: "cancelled" };
2327
2644
  }
2328
- debugLogger4.warn(
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
- debugLogger4.debug(`Auto-compression aborted for prompt ${promptId}`);
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
- debugLogger4.warn(
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
- debugLogger4.debug(
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
- debugLogger4.debug(
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
- debugLogger4.warn(`${failureContext}: ${this.#formatError(notifyError)}`);
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
- debugLogger4.warn(
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
- debugLogger4.warn(
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
- debugLogger4.debug(
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
- debugLogger4.warn(`Failed to resolve mid-turn message: ${errorMessage}`);
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
- debugLogger4.warn(
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((job) => {
2636
- if (this.cronDisabledByTokenLimit) return;
2637
- this.cronQueue.push({
2638
- prompt: job.prompt,
2639
- source: job.cronExpr === "@wakeup" ? "loop" : "cron"
2640
- });
2641
- void this.#drainCronQueue();
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: prompt },
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: prompt }]
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
- role: "user",
2786
- parts: [
2787
- ...toolRun.parts,
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
- debugLogger4.error("Error processing cron prompt:", error);
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
- debugLogger4.warn(
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
- role: "user",
3025
- parts: [
3026
- ...toolRun.parts,
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
- debugLogger4.error("Error processing background notification:", error);
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
- debugLogger4.error(
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
- debugLogger4.debug(
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
- debugLogger4.error("Error sending available commands update:", error);
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
- debugLogger4.debug("mode-update extNotification failed", error);
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
- debugLogger4.debug("model-update extNotification failed", error);
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
- debugLogger4.debug("mode-update extNotification failed", error);
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
- debugLogger4.debug(
3276
- `[Session.runToolCalls] Suppressing duplicate provider tool-call id: ${request.providerCallId} (tool: ${request.name})`
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
- debugLogger4.error("Failed to record skipped tool call:", recordError);
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 { parts, stopAfterPermissionCancel: true };
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(abortSignal, promptId, fc);
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 { parts, stopAfterPermissionCancel: true };
3939
+ return {
3940
+ parts,
3941
+ stopAfterPermissionCancel: true,
3942
+ repeatedDuplicateProviderToolCall: false
3943
+ };
3471
3944
  }
3472
3945
  }
3473
3946
  }
3474
3947
  }
3475
- return { parts, stopAfterPermissionCancel: false };
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
- debugLogger4.info(
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
- debugLogger4.warn(
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
- debugLogger4.warn(
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
- debugLogger4.warn(
4215
+ debugLogger5.warn(
3737
4216
  `Auto mode denial counters already clear after fallback approval: ` + formatDenialStateLog(before)
3738
4217
  );
3739
4218
  return;
3740
4219
  }
3741
- debugLogger4.warn(
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
- debugLogger4.error(
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
- debugLogger4.error(
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
- debugLogger4.error(
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
- `\u{1F6AB} **PreToolUse blocked**: ${toolName} - ${blockReason}`
4425
+ `\u2717 **PreToolUse blocked**: ${toolName} - ${blockReason}`
3947
4426
  );
3948
4427
  return earlyErrorResponse(new Error(blockReason), toolName);
3949
4428
  }
3950
4429
  if (preHookResult.additionalContext) {
3951
- debugLogger4.debug(
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
- debugLogger4.info(
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
- debugLogger4.debug(
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
- debugLogger4.debug(
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
- if (atPathCommandParts.length === 0 && embeddedContext.length === 0) {
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
- debugLogger4.warn(msg);
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
- debugLogger4.debug(
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 debugLogger5 = createDebugLogger("ACP_AGENT");
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(path.sep) || !/^[a-zA-Z0-9._-]+$/.test(slug)) {
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 ? path.posix.join(relativeRoot, name) : name;
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
- debugLogger5.warn(
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 || path.posix.basename(githubUrl.filePath) !== "SKILL.md") {
5652
+ if (!githubUrl || path3.posix.basename(githubUrl.filePath) !== "SKILL.md") {
5116
5653
  return downloadSingleSkillFile(sourceUrl);
5117
5654
  }
5118
- const skillDirectory = path.posix.dirname(githubUrl.filePath);
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 = path.resolve(skillDir);
5135
- const target = path.resolve(skillDir, relativePath);
5136
- if (target !== root && !target.startsWith(root + path.sep)) {
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 = path.resolve(skillsBaseDir);
5147
- const skillDir = path.resolve(skillsBaseDir, slug);
5148
- if (!skillDir.startsWith(root + path.sep)) {
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
- debugLogger5.warn(
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
- debugLogger5.warn(
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 = path.join(dir, filename);
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 path.join(dir, fallbackFilename);
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
- debugLogger5.error(`[ACP] MCP pool drain (${label}) error:`, err);
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
- debugLogger5.warn(
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
- debugLogger5.debug("[ACP] Shutdown signal received, closing streams");
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
- debugLogger5.error("[ACP] Cleanup error:", err);
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
- debugLogger5.warn(
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
- debugLogger5.error(
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
- debugLogger5.debug(
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
- debugLogger5.debug(
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
- debugLogger5.debug(
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.2";
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
- debugLogger5.warn(`ACP worktree restore failed: ${error}`);
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
- debugLogger5.warn(
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
- debugLogger5.warn(
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 = path.join(Storage.getGlobalQwenDir(), "skills");
8424
+ const skillsBaseDir = path3.join(Storage.getGlobalQwenDir(), "skills");
7731
8425
  const skillDir = resolveManagedSkillDir(skillsBaseDir, request.slug);
7732
- const skillFile = path.join(skillDir, "SKILL.md");
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(path.dirname(targetPath), { recursive: true });
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 = path.resolve(skillDir);
7792
- const resolvedSkillFile = path.resolve(skillFile);
7793
- const globalDir = path.resolve(Storage.getGlobalQwenDir());
7794
- const isDedicatedSkillDir = resolvedSkillFile === path.join(resolvedSkillDir, "SKILL.md");
7795
- if (!isDedicatedSkillDir || resolvedSkillDir === path.parse(resolvedSkillDir).root || resolvedSkillDir === globalDir) {
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
- path.join(Storage.getGlobalQwenDir(), "skills"),
8505
+ path3.join(Storage.getGlobalQwenDir(), "skills"),
7812
8506
  slug
7813
8507
  );
7814
- const qwenSkillFile = path.join(qwenSkillDir, "SKILL.md");
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: path.dirname(skillFile),
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 = path.resolve(cwd);
8550
+ const projectRoot = path3.resolve(cwd);
7857
8551
  for (const configDir of PROJECT_SKILL_DIRS) {
7858
- const baseDir = path.join(projectRoot, configDir, SKILLS_DIR);
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: path.dirname(skillFile),
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 (path.basename(skillFile) !== "SKILL.md") {
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: createLoadedSettingsAdapter(this.settings, persistScope),
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: plan.modelSelection?.modelId,
7963
- ...plan.modelSelection?.baseUrl ? { baseUrl: plan.modelSelection.baseUrl } : {}
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
- if (currentExcluded2.includes(serverName)) {
8321
- settings.setValue(
8322
- scope,
8323
- "mcp.excluded",
8324
- currentExcluded2.filter((name) => name !== serverName)
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
- this.config.setExcludedMcpServers(
8330
- currentExcluded.filter((name) => name !== serverName)
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 { serverName, action, ok: true, changed: true };
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
- if (!currentExcluded.includes(serverName)) {
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
- if (!runtimeExcluded.includes(serverName)) {
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 { serverName, action, ok: true, changed: true };
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
- debugLogger5.warn("setLanguageAsync failed:", err);
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
- debugLogger5.warn("Failed to persist UI language setting:", err);
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
- debugLogger5.warn("Failed to write output-language.md:", err);
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
- debugLogger5.warn(
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
- debugLogger5.warn(
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
- debugLogger5.warn(
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
- debugLogger5.debug(`recap ext-method received for session=${sessionId}`);
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
- debugLogger5.debug(
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
- debugLogger5.debug(`btw: no cacheSafeParams for session=${sessionId}`);
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
- debugLogger5.debug("Failed to read history before /fork:", error);
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
- debugLogger5.debug("Failed to record fork event in history:", error);
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
- debugLogger5.info(
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
- debugLogger5.info(
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
- debugLogger5.info(
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
- debugLogger5.info(
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
- debugLogger5.info(
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
- debugLogger5.info(
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
- debugLogger5.info(
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
- debugLogger5.info(
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 (name.length > 256 || !/^[A-Za-z0-9_-]+$/.test(name) || name === "__proto__" || name === "constructor" || name === "prototype") {
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 (name.length > 256 || !/^[A-Za-z0-9_-]+$/.test(name) || name === "__proto__" || name === "constructor" || name === "prototype") {
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
- debugLogger5.warn(
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
- debugLogger5.error(
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
- debugLogger5.warn(
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
- if (changed.has("modelProviders")) {
10439
+ const providersChanged = changed.has("modelProviders") || changed.has("providerProtocol");
10440
+ if (providersChanged) {
9580
10441
  try {
9581
- config.reloadModelProvidersConfig(newMerged.modelProviders);
10442
+ config.reloadModelProvidersConfig(
10443
+ newMerged.modelProviders,
10444
+ newMerged.providerProtocol ?? {}
10445
+ );
9582
10446
  } catch (err) {
9583
- debugLogger5.warn(
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
- debugLogger5.warn(
10457
+ debugLogger6.warn(
9594
10458
  `reload: switchModel failed for session ${id}: ${err}`
9595
10459
  );
9596
10460
  }
9597
- } else if ((changed.has("modelProviders") || envChanged) && authType) {
10461
+ } else if ((providersChanged || envChanged) && authType) {
9598
10462
  try {
9599
10463
  await config.refreshAuth(authType);
9600
10464
  } catch (err) {
9601
- debugLogger5.warn(
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
- debugLogger5.warn(
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
- debugLogger5.warn(
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
- debugLogger5.warn(
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
- debugLogger5.warn(
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
- debugLogger5.debug(
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
- debugLogger5.error(`Authentication failed: ${e}`);
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,