@qwen-code/qwen-code 0.19.9 → 0.19.10

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 (310) hide show
  1. package/bundled/qc-helper/docs/configuration/settings.md +7 -0
  2. package/bundled/qc-helper/docs/features/channels/dingtalk.md +3 -0
  3. package/bundled/qc-helper/docs/features/channels/overview.md +11 -3
  4. package/bundled/qc-helper/docs/features/code-review.md +88 -44
  5. package/bundled/qc-helper/docs/features/commands.md +6 -6
  6. package/bundled/qc-helper/docs/features/hooks.md +29 -0
  7. package/bundled/qc-helper/docs/features/sub-agents.md +21 -0
  8. package/bundled/qc-helper/docs/qwen-serve.md +46 -20
  9. package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +1 -1
  10. package/bundled/review/DESIGN.md +219 -33
  11. package/bundled/review/SKILL.md +449 -153
  12. package/chunks/{MaxSizedBox-6HYJ3ZPJ.js → MaxSizedBox-67ODZ4SZ.js} +31 -31
  13. package/chunks/{StandaloneSessionPicker-WS4GXYOG.js → StandaloneSessionPicker-SIPBQT5Z.js} +51 -49
  14. package/chunks/{acpAgent-5FWSOZL5.js → acpAgent-VPZJXLFX.js} +528 -478
  15. package/chunks/{agent-HOLHB3RT.js → agent-Z7NWL6XL.js} +26 -26
  16. package/chunks/{agent-headless-SXL6H56A.js → agent-headless-73HZ6ROQ.js} +26 -26
  17. package/chunks/{anthropicContentGenerator-L73TO3DB.js → anthropicContentGenerator-56U3YCP2.js} +5 -5
  18. package/chunks/{artifact-tool-U7IRTLIV.js → artifact-tool-BWZZJ42H.js} +2 -2
  19. package/chunks/{askUserQuestion-2DLUUZZG.js → askUserQuestion-WU3S5A5T.js} +2 -2
  20. package/chunks/{bridge-BCJQ7BBB.js → bridge-XFKW6S7Q.js} +32 -32
  21. package/chunks/{ca-UJ6HC4H2.js → ca-C2JTT2N5.js} +4 -1
  22. package/chunks/channel-worker-group-K7QKH6IG.js +13 -0
  23. package/chunks/channel-worker-manager-5RWKSGEU.js +418 -0
  24. package/chunks/channel-worker-supervisor-PCDAZN4D.js +20 -0
  25. package/chunks/{chunk-LXNEJD34.js → chunk-2FO5GFKZ.js} +1 -1
  26. package/chunks/{chunk-B5ORX3FG.js → chunk-2UUAIFQO.js} +1 -1
  27. package/chunks/{chunk-L36P3NBD.js → chunk-2W3OOD4W.js} +2 -0
  28. package/chunks/{chunk-7R3CZA3Q.js → chunk-2Z5UX4KI.js} +1 -1
  29. package/chunks/chunk-3MX7D6QN.js +516 -0
  30. package/chunks/{chunk-E2IY465S.js → chunk-3XJFUTNM.js} +10 -10
  31. package/chunks/{chunk-JZETEN3Z.js → chunk-422XAKCK.js} +3 -3
  32. package/chunks/{chunk-N3EMS75V.js → chunk-4FMG7EDJ.js} +1268 -1233
  33. package/chunks/{chunk-HKMAENAO.js → chunk-4YWLED2A.js} +8 -6
  34. package/chunks/{chunk-SCGXUJBT.js → chunk-5EBKKSBX.js} +3 -3
  35. package/chunks/chunk-5HBA2Z7V.js +28 -0
  36. package/chunks/{chunk-XYQSMABS.js → chunk-5LROA5W4.js} +4 -4
  37. package/chunks/{chunk-ZJE7NSL3.js → chunk-5M6IDOMF.js} +34 -97
  38. package/chunks/{chunk-VA4KQY6V.js → chunk-65N2J6V2.js} +3 -3
  39. package/chunks/{chunk-4C5BQ2D2.js → chunk-6GETGQWP.js} +71 -12
  40. package/chunks/{chunk-PVZM22CK.js → chunk-6GKMJPOC.js} +5 -5
  41. package/chunks/{chunk-W2UIXQDL.js → chunk-6OATMEYN.js} +1 -1
  42. package/chunks/{chunk-GZRTYZ2R.js → chunk-6W6DI34P.js} +154 -1
  43. package/chunks/{chunk-EMDMPBJ4.js → chunk-7L4XBYVW.js} +3 -3
  44. package/chunks/{chunk-EAXLGSFP.js → chunk-7MJAZMXK.js} +8 -8
  45. package/chunks/{chunk-K434OPES.js → chunk-7NAWTFVY.js} +3 -3
  46. package/chunks/{chunk-J4TSQ5EQ.js → chunk-7NFB7LBQ.js} +8 -1
  47. package/chunks/{chunk-5SFOMFG4.js → chunk-7R4DPSRA.js} +36 -2
  48. package/chunks/{chunk-P67PFTUT.js → chunk-AAR5SGMD.js} +4 -4
  49. package/chunks/{chunk-RDJSGHZE.js → chunk-ALGGS7UH.js} +1 -38
  50. package/chunks/{chunk-GWHYEOV2.js → chunk-ARKANCNX.js} +2 -1
  51. package/chunks/{chunk-D2GFWEXB.js → chunk-B5I2IIWY.js} +6 -6
  52. package/chunks/{chunk-524RQP4Q.js → chunk-B7NUUEXC.js} +10 -4
  53. package/chunks/{chunk-CUMMSHH6.js → chunk-BBNVFRGD.js} +1 -1
  54. package/chunks/{chunk-5HOMWMCL.js → chunk-CEW4HDPK.js} +4166 -1338
  55. package/chunks/chunk-CJFNEBYB.js +91 -0
  56. package/chunks/{chunk-VWPVHAMO.js → chunk-CUUXANEQ.js} +5 -1
  57. package/chunks/{chunk-GWLSNBG4.js → chunk-DK33QJ3V.js} +30 -26
  58. package/chunks/{chunk-HPDUL5MF.js → chunk-DPFBNG72.js} +1 -1
  59. package/chunks/{chunk-KKNMOEZH.js → chunk-E2UVIFI2.js} +185 -398
  60. package/chunks/{chunk-MC23XKFE.js → chunk-E67NFMF4.js} +3 -3
  61. package/chunks/{chunk-OQ4DRIOA.js → chunk-EQY6POVO.js} +2 -2
  62. package/chunks/{chunk-XUOOWUZS.js → chunk-F7NXQH4Y.js} +1 -1
  63. package/chunks/{chunk-YLV3XXFB.js → chunk-FBU7WRZI.js} +17 -1
  64. package/chunks/{chunk-LK7KUE55.js → chunk-FSA7ERJ2.js} +1 -1
  65. package/chunks/{chunk-PDZ7MBDG.js → chunk-FXMC7SL2.js} +27 -6
  66. package/chunks/{chunk-SJHDYNHF.js → chunk-GDQQRV43.js} +3 -3
  67. package/chunks/{chunk-34MW4KTI.js → chunk-GGJII3FM.js} +2 -2
  68. package/chunks/{chunk-3BTRNWWD.js → chunk-GOXKNSDZ.js} +1 -64
  69. package/chunks/chunk-GYTKQYDB.js +23 -0
  70. package/chunks/{chunk-HVY356M2.js → chunk-H3FQOPPS.js} +3 -3
  71. package/chunks/{chunk-YVEDCAUE.js → chunk-HGT6JR3U.js} +19 -0
  72. package/chunks/{chunk-WQUQVGTF.js → chunk-HHEFI4RP.js} +183 -96
  73. package/chunks/chunk-IDS7MSUP.js +74 -0
  74. package/chunks/{chunk-6PESIEKJ.js → chunk-JSJFKYWT.js} +68 -6
  75. package/chunks/{chunk-CNEIYWSA.js → chunk-K4PZ5F5T.js} +5 -5
  76. package/chunks/{chunk-NVYTCB5Z.js → chunk-K7CRWKOE.js} +14534 -13980
  77. package/chunks/{chunk-ORBGV7NJ.js → chunk-KKRTYONM.js} +6 -4
  78. package/chunks/{chunk-IAVB3HM3.js → chunk-KSO42X3Z.js} +13 -0
  79. package/chunks/chunk-L37RC7MQ.js +493 -0
  80. package/chunks/{chunk-4QLQE5WJ.js → chunk-L5U3ELZX.js} +1 -1
  81. package/chunks/{chunk-3G7CIHVV.js → chunk-L62SEJWY.js} +4 -4
  82. package/chunks/{chunk-EGYZ275M.js → chunk-LNUXFLLJ.js} +4 -4
  83. package/chunks/{chunk-4ZVWQHEQ.js → chunk-LQIDP6K5.js} +1 -1
  84. package/chunks/{chunk-VTU57BWZ.js → chunk-LQU7EVFA.js} +1 -1
  85. package/chunks/{chunk-B4G2WOFA.js → chunk-LTNI523B.js} +2 -2
  86. package/chunks/{chunk-DC5Z3DU6.js → chunk-LXLTBIDP.js} +43 -6
  87. package/chunks/{chunk-KHUQZZJ6.js → chunk-MO7O5722.js} +370 -36
  88. package/chunks/chunk-MR3PXB6E.js +48 -0
  89. package/chunks/{chunk-5VXNZWGF.js → chunk-N6FXRR6P.js} +1 -1
  90. package/chunks/{chunk-WL4L7EZH.js → chunk-OA4JVLSA.js} +27 -1
  91. package/chunks/{chunk-TO6QE22P.js → chunk-OATTJGPJ.js} +26 -22
  92. package/chunks/{chunk-X6TJ3YZV.js → chunk-OFEVHGVU.js} +159 -57
  93. package/chunks/{chunk-X4NKFGFJ.js → chunk-OJAMDVF5.js} +0 -15
  94. package/chunks/{chunk-BZIFY5TF.js → chunk-OKAIGAYW.js} +3 -3
  95. package/chunks/{chunk-3VAGG5T5.js → chunk-P6LART6G.js} +116 -15
  96. package/chunks/{chunk-3SJSQXNS.js → chunk-PEJWA3QQ.js} +12 -8
  97. package/chunks/{chunk-BAPGSC7U.js → chunk-Q5GLXH5Q.js} +23 -9
  98. package/chunks/{chunk-MXL5UP4X.js → chunk-Q6TUALBE.js} +1 -1
  99. package/chunks/{chunk-QYMQSECS.js → chunk-QAW7PIHT.js} +2 -2
  100. package/chunks/{chunk-LUODI7WD.js → chunk-RCEEXIOO.js} +6 -4
  101. package/chunks/{chunk-YAUFFXDS.js → chunk-RDRVMDWV.js} +12 -11
  102. package/chunks/{chunk-CEA3E3JB.js → chunk-SPJQO2CE.js} +2 -2
  103. package/chunks/{chunk-U2NNEY4R.js → chunk-SPUYQB4T.js} +1 -1
  104. package/chunks/{chunk-34Z3U5RQ.js → chunk-SXU3KKNI.js} +514 -114
  105. package/chunks/{chunk-ECNYL4AA.js → chunk-TISZXPAX.js} +1 -1
  106. package/chunks/{chunk-JSG7ZSBC.js → chunk-TQBODCFD.js} +1 -1
  107. package/chunks/{chunk-AKIVHSJR.js → chunk-TTUO4DNG.js} +111 -93
  108. package/chunks/chunk-TV6K2FB4.js +224 -0
  109. package/chunks/{chunk-4GORAUCN.js → chunk-TXY2C3D2.js} +1 -1
  110. package/chunks/{chunk-5XUCZNSY.js → chunk-TYLOFD2U.js} +1 -1
  111. package/chunks/{chunk-CLFKOZEK.js → chunk-UFOKA4PR.js} +1 -1
  112. package/chunks/{channel-worker-supervisor-WSOAAYUO.js → chunk-URU5UEK6.js} +67 -35
  113. package/chunks/{chunk-D5SKC4YG.js → chunk-V7EQTZYR.js} +41 -13
  114. package/chunks/{chunk-L6P4KPHR.js → chunk-VBRRNQB7.js} +59 -39
  115. package/chunks/{chunk-BMGVRZN4.js → chunk-W34A5MJH.js} +1 -1
  116. package/chunks/{chunk-PODM4SZ4.js → chunk-WDW64OW3.js} +5 -5
  117. package/chunks/{chunk-W3JHSWRP.js → chunk-WDWJ4OQJ.js} +4 -4
  118. package/chunks/{chunk-Q4YQW46J.js → chunk-WIASPMLX.js} +31 -17
  119. package/chunks/{chunk-ZLXQN2QS.js → chunk-WIEO4CWB.js} +1 -1
  120. package/chunks/{chunk-A3XKDTSH.js → chunk-WLXYAKV7.js} +4 -4
  121. package/chunks/{chunk-Q6I5QHMH.js → chunk-WTSXKLG5.js} +1 -1
  122. package/chunks/{chunk-J3JA76CF.js → chunk-WX6SD4IQ.js} +1 -1
  123. package/chunks/{chunk-77XJ3SWN.js → chunk-X362TMXZ.js} +5 -5
  124. package/chunks/{chunk-CCDMCZPP.js → chunk-XLIRF7IK.js} +1 -1
  125. package/chunks/{chunk-AURZZYMD.js → chunk-XXJKGHZV.js} +4 -4
  126. package/chunks/{chunk-TDEXZKMT.js → chunk-Y66VOBRP.js} +2 -2
  127. package/chunks/{chunk-CC2ITGCF.js → chunk-YHN5SUIJ.js} +49 -6
  128. package/chunks/{chunk-3AHXV67U.js → chunk-YU56QGXX.js} +1 -1
  129. package/chunks/{chunk-5U27BZ72.js → chunk-YX4LLZ3D.js} +187 -75
  130. package/chunks/{chunk-ATAKVRIF.js → chunk-YYPXHMWO.js} +7 -7
  131. package/chunks/{chunk-XLA62MYB.js → chunk-ZKMGAFYV.js} +2 -2
  132. package/chunks/{chunk-UOZWKTQG.js → chunk-ZPAHLFZU.js} +2 -2
  133. package/chunks/{chunk-7PU4FYLG.js → chunk-ZQTZXVHP.js} +4 -4
  134. package/chunks/chunk-ZRKBI3DP.js +34 -0
  135. package/chunks/{computer-use-HM37RX6A.js → computer-use-IYTX45WA.js} +26 -26
  136. package/chunks/{config-utils-NV2SVGPB.js → config-utils-CFT33LTP.js} +3 -2
  137. package/chunks/{contextCommand-OF5SFGH2.js → contextCommand-272V77WU.js} +28 -28
  138. package/chunks/{create-sub-session-KPRT7ECJ.js → create-sub-session-CBJMQLYP.js} +26 -26
  139. package/chunks/{create-sub-session-INX7TXJI.js → create-sub-session-WNX2OQVX.js} +2 -2
  140. package/chunks/{cron-create-AMBKGZX6.js → cron-create-36TIFKRT.js} +4 -4
  141. package/chunks/{cron-delete-LRU4OJYG.js → cron-delete-OQD3WULU.js} +4 -4
  142. package/chunks/{cron-list-WVP2JXER.js → cron-list-4533T2FH.js} +4 -4
  143. package/chunks/{daemon-ARHTSTXP.js → daemon-RNGFPMAB.js} +575 -58
  144. package/chunks/{daemon-status-provider-B2UTZUJ2.js → daemon-status-provider-L4STG5VO.js} +36 -36
  145. package/chunks/{de-EBJN3OVN.js → de-2VCDRDGU.js} +4 -1
  146. package/chunks/{dist-VHV4EVHG.js → dist-3V3ZV6ML.js} +1 -1
  147. package/chunks/{dist-XKU3ABM5.js → dist-ERCPDVA6.js} +1 -1
  148. package/chunks/{dist-PNVLTKTL.js → dist-HNN7DR2L.js} +158 -4
  149. package/chunks/{dist-WKPOYU7O.js → dist-JZE7F6YR.js} +1 -1
  150. package/chunks/{dist-QYCAEZIT.js → dist-MOEC47TR.js} +5 -1
  151. package/chunks/{dist-R7LN5AZE.js → dist-PHEPJLXD.js} +2 -2
  152. package/chunks/{earlyInputCapture-3VTHRC4C.js → earlyInputCapture-EN4BRREQ.js} +27 -27
  153. package/chunks/{edit-VNI4NJ73.js → edit-OMAF2QDR.js} +27 -27
  154. package/chunks/{en-5A6LA7RK.js → en-NJCA3TAA.js} +5 -1
  155. package/chunks/{enter-worktree-IYYBKPZT.js → enter-worktree-BBZX52HG.js} +26 -26
  156. package/chunks/{enterPlanMode-GMC5FD5A.js → enterPlanMode-5NNFMBUY.js} +42 -27
  157. package/chunks/{environment-5HSSRMJ7.js → environment-7TEWMJYN.js} +30 -29
  158. package/chunks/{errors-HMFCJJ7P.js → errors-I4VKVX3F.js} +28 -28
  159. package/chunks/{exit-worktree-6BHCMP5G.js → exit-worktree-FUWRJI33.js} +26 -26
  160. package/chunks/{exitPlanMode-CJW7G74W.js → exitPlanMode-IHCJ5EEI.js} +26 -26
  161. package/chunks/{fast-path-FRDMDVHG.js → fast-path-ZA57TOH4.js} +3 -3
  162. package/chunks/{fast-path-settings-5QZE64HR.js → fast-path-settings-DAOVFADC.js} +6 -4
  163. package/chunks/{fr-YXRABLYZ.js → fr-5F3E7WKD.js} +4 -1
  164. package/chunks/{gemini-QYSEGHAX.js → gemini-UOAKVXBG.js} +139 -76
  165. package/chunks/{geminiContentGenerator-J7YNZDYP.js → geminiContentGenerator-G6UCE6MW.js} +4 -4
  166. package/chunks/{glob-BRCSGFOT.js → glob-4GJQ2UFF.js} +28 -27
  167. package/chunks/{grep-EE2DUOSY.js → grep-5QSYXF2E.js} +26 -26
  168. package/chunks/{handleAutoUpdate-5NWTLR67.js → handleAutoUpdate-ULHNWL6R.js} +30 -30
  169. package/chunks/{i18n-BASPENNQ.js → i18n-CYDUTF6F.js} +27 -27
  170. package/chunks/initializer-NRFKUGIO.js +73 -0
  171. package/chunks/{installationInfo-W6V4DT7W.js → installationInfo-WELKRHHE.js} +27 -27
  172. package/chunks/{ja-7VQOANVG.js → ja-66J6B53L.js} +5 -2
  173. package/chunks/{keychain-token-storage-VKUBNCP4.js → keychain-token-storage-AU22CQI6.js} +2 -2
  174. package/chunks/list-JVDXEG2E.js +76 -0
  175. package/chunks/loadedSettingsAdapter-WAM4VUTG.js +70 -0
  176. package/chunks/{loop-wakeup-WX7BLWWP.js → loop-wakeup-MVUKDVCP.js} +5 -5
  177. package/chunks/{ls-ZB2BDP6T.js → ls-RXNIAQDE.js} +4 -4
  178. package/chunks/{lsp-V5DN4YUJ.js → lsp-HB5LM5IL.js} +2 -2
  179. package/chunks/mcp-YHOZN3ZM.js +70 -0
  180. package/chunks/{monitor-X5Z3H4FY.js → monitor-R7XZPJHQ.js} +26 -26
  181. package/chunks/nonInteractiveCli-NSOB6D74.js +129 -0
  182. package/chunks/{notebook-edit-TRM2QIJK.js → notebook-edit-6RNKE2L2.js} +27 -27
  183. package/chunks/{openaiContentGenerator-AAYONPMF.js → openaiContentGenerator-XNS5S64I.js} +12 -12
  184. package/chunks/{pidfile-D4I4FTVA.js → pidfile-XE4RRR2P.js} +27 -27
  185. package/chunks/{pt-2C6YCSHL.js → pt-TFZO5Y6T.js} +4 -1
  186. package/chunks/{qwenContentGenerator-K3HZ2H76.js → qwenContentGenerator-APDPJ3VN.js} +28 -28
  187. package/chunks/{qwenOAuth2-PJQ27GSO.js → qwenOAuth2-LEYOU7RU.js} +5 -5
  188. package/chunks/{read-file-7SZSIZOL.js → read-file-XSUWXSR7.js} +8 -8
  189. package/chunks/{read-mcp-resource-SM5DA4RG.js → read-mcp-resource-6LRTZKXI.js} +2 -2
  190. package/chunks/{record-artifact-4XKZX3UW.js → record-artifact-PMDTY4OS.js} +2 -2
  191. package/chunks/{ripGrep-YNPFVCZP.js → ripGrep-APB3CDMU.js} +26 -26
  192. package/chunks/{ru-6BBHVZZV.js → ru-4L4LFHIT.js} +4 -1
  193. package/chunks/{run-qwen-serve-DMWV5N6L.js → run-qwen-serve-NWTGPERT.js} +1232 -371
  194. package/chunks/{runtime-57WWRVHN.js → runtime-EIO3NQKR.js} +42 -37
  195. package/chunks/{scheduler-YBLGWSIL.js → scheduler-JJDZBDRN.js} +26 -26
  196. package/chunks/{send-message-F36YD6KQ.js → send-message-SR4WSXP2.js} +3 -3
  197. package/chunks/{serve-R63NK3CV.js → serve-WTIPB3EI.js} +36 -34
  198. package/chunks/{server-Z22GCAXP.js → server-NJPHQHLH.js} +5238 -1328
  199. package/chunks/{session-HKAAI7BN.js → session-O4AO7FTS.js} +298 -98
  200. package/chunks/{settings-3EYN66GK.js → settings-HQNDK7TG.js} +34 -32
  201. package/chunks/{shell-6YXINTWJ.js → shell-UIXM6PWG.js} +26 -26
  202. package/chunks/{skill-OO57P4X6.js → skill-3XMB4ULV.js} +10 -10
  203. package/chunks/{spawnChannel-JD7G4VU6.js → spawnChannel-3R632QRM.js} +28 -28
  204. package/chunks/{src-G5UPLZSZ.js → src-USZKPUOC.js} +85 -27
  205. package/chunks/{standalone-update-OB7F4JPZ.js → standalone-update-L5XW3D7K.js} +28 -28
  206. package/chunks/{startInteractiveUI-73EGLIIH.js → startInteractiveUI-CPZOL2WB.js} +730 -397
  207. package/chunks/{syntheticOutput-34H7LGA7.js → syntheticOutput-YKFNUQZD.js} +3 -3
  208. package/chunks/{task-create-FRGOEIKY.js → task-create-NUFOHU53.js} +8 -7
  209. package/chunks/{task-list-OW766BXZ.js → task-list-Z2IKQY3S.js} +6 -5
  210. package/chunks/{task-stop-SIR5SNBN.js → task-stop-KQFIVQTJ.js} +2 -2
  211. package/chunks/{task-update-YRA532MT.js → task-update-HKADXSTD.js} +8 -7
  212. package/chunks/{team-create-PECAAPXK.js → team-create-XXPRF3IQ.js} +26 -26
  213. package/chunks/{team-delete-6OSECUZC.js → team-delete-SDNTGZLG.js} +6 -5
  214. package/chunks/{team-plan-approval-L6F434RY.js → team-plan-approval-EOTTKHDW.js} +26 -26
  215. package/chunks/{theme-manager-ZG4RY6GU.js → theme-manager-P6UITO2L.js} +27 -27
  216. package/chunks/{todoWrite-YDMFEV4G.js → todoWrite-YNXB27GB.js} +4 -4
  217. package/chunks/{tool-search-THVIWAPB.js → tool-search-DDNGK2LR.js} +9 -9
  218. package/chunks/{total-session-admission-JOKNRIIA.js → total-session-admission-PL5N7DNY.js} +33 -33
  219. package/chunks/tree-sitter-YJVE2ZUK.js +2980 -0
  220. package/chunks/{trustedFolders-EOGT3UVS.js → trustedFolders-C5LMF3UR.js} +29 -28
  221. package/chunks/types-ML3TRJQ5.js +16 -0
  222. package/chunks/{updateCheck-2KYEYVSB.js → updateCheck-QTVVISH7.js} +29 -29
  223. package/chunks/{validateNonInterActiveAuth-EN6ZQSR2.js → validateNonInterActiveAuth-RY4RKBQG.js} +75 -71
  224. package/chunks/{version-3UEA6ZIR.js → version-U45RYOSD.js} +1 -1
  225. package/chunks/{web-fetch-PAAEQKBW.js → web-fetch-ZSPR3PRR.js} +5 -5
  226. package/chunks/{workflow-QCCCMYP3.js → workflow-TCIAQYOA.js} +27 -27
  227. package/chunks/workspace-providers-status-I3G2HYLY.js +73 -0
  228. package/chunks/workspace-registration-store-IH5GLZ6Y.js +27 -0
  229. package/chunks/{workspace-registry-24QFLQYH.js → workspace-registry-QEKBELIX.js} +33 -33
  230. package/chunks/workspace-service-ORKHGLP3.js +86 -0
  231. package/chunks/workspace-skills-status-D7QCNDYY.js +72 -0
  232. package/chunks/{write-file-I3MUDRIE.js → write-file-JGRQR5VM.js} +27 -27
  233. package/chunks/{zh-RQE7I22T.js → zh-FSAH32JB.js} +6 -2
  234. package/chunks/{zh-TW-TBPQGHKH.js → zh-TW-D2YIE2LO.js} +6 -2
  235. package/cli.js +11 -11
  236. package/locales/ca.js +6 -0
  237. package/locales/de.js +6 -0
  238. package/locales/en.js +8 -0
  239. package/locales/fr.js +6 -0
  240. package/locales/ja.js +7 -1
  241. package/locales/pt.js +6 -0
  242. package/locales/ru.js +6 -0
  243. package/locales/zh-TW.js +9 -1
  244. package/locales/zh.js +9 -1
  245. package/package.json +3 -3
  246. package/web-shell/assets/{arc-C15hqnJe.js → arc-CUeDlxvE.js} +1 -1
  247. package/web-shell/assets/{architectureDiagram-3BPJPVTR-B-At37J_.js → architectureDiagram-3BPJPVTR-PiUszkQA.js} +1 -1
  248. package/web-shell/assets/{blockDiagram-GPEHLZMM-B51wjcU7.js → blockDiagram-GPEHLZMM-s2wYSO7e.js} +1 -1
  249. package/web-shell/assets/{c4Diagram-AAUBKEIU-xBK7BAwN.js → c4Diagram-AAUBKEIU-DyLxZoz9.js} +1 -1
  250. package/web-shell/assets/channel-DkuJJ0qd.js +1 -0
  251. package/web-shell/assets/{chunk-2J33WTMH-xKmLg7Yr.js → chunk-2J33WTMH-Dsdai7Tk.js} +1 -1
  252. package/web-shell/assets/{chunk-4BX2VUAB-BfkdKZCM.js → chunk-4BX2VUAB-DEosOjV7.js} +1 -1
  253. package/web-shell/assets/{chunk-55IACEB6-DT713sZf.js → chunk-55IACEB6-sNsRLTXf.js} +1 -1
  254. package/web-shell/assets/{chunk-727SXJPM-CYEeAamv.js → chunk-727SXJPM-DkRNZzmh.js} +1 -1
  255. package/web-shell/assets/{chunk-AQP2D5EJ-DBN-5CFd.js → chunk-AQP2D5EJ-BrsRNpzd.js} +1 -1
  256. package/web-shell/assets/{chunk-FMBD7UC4-CGZKk4PH.js → chunk-FMBD7UC4-CI039lpl.js} +1 -1
  257. package/web-shell/assets/{chunk-ND2GUHAM-qQbC2O-y.js → chunk-ND2GUHAM-M-pkeIfu.js} +1 -1
  258. package/web-shell/assets/{chunk-QZHKN3VN-B3zeXwsX.js → chunk-QZHKN3VN-C4ckGNbH.js} +1 -1
  259. package/web-shell/assets/classDiagram-4FO5ZUOK-CwqHc0TB.js +1 -0
  260. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-CwqHc0TB.js +1 -0
  261. package/web-shell/assets/{cose-bilkent-S5V4N54A-BKsl1Wvy.js → cose-bilkent-S5V4N54A-BciKUAFq.js} +1 -1
  262. package/web-shell/assets/{dagre-BM42HDAG-CuuuLiBK.js → dagre-BM42HDAG-C1nTSgU6.js} +1 -1
  263. package/web-shell/assets/{diagram-2AECGRRQ-CG4_eKiV.js → diagram-2AECGRRQ-BDPNWr1m.js} +1 -1
  264. package/web-shell/assets/{diagram-5GNKFQAL-DSFTmZB1.js → diagram-5GNKFQAL-CNFhTszL.js} +1 -1
  265. package/web-shell/assets/{diagram-KO2AKTUF-BxwlEHn8.js → diagram-KO2AKTUF-CjPa4p5a.js} +1 -1
  266. package/web-shell/assets/{diagram-LMA3HP47-BUAg-jMG.js → diagram-LMA3HP47-ZzPykSyG.js} +1 -1
  267. package/web-shell/assets/{diagram-OG6HWLK6-BFM2jlO5.js → diagram-OG6HWLK6-0UVtZ9A4.js} +1 -1
  268. package/web-shell/assets/{erDiagram-TEJ5UH35-CerbFPa2.js → erDiagram-TEJ5UH35-CajDZCat.js} +1 -1
  269. package/web-shell/assets/{flowDiagram-I6XJVG4X-BNeZz5Zz.js → flowDiagram-I6XJVG4X-BRuFBIvL.js} +1 -1
  270. package/web-shell/assets/{ganttDiagram-6RSMTGT7-CTOogLaM.js → ganttDiagram-6RSMTGT7-BevGmJqG.js} +3 -3
  271. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-yxSFkXPj.js → gitGraphDiagram-PVQCEYII-DvPMpBMb.js} +1 -1
  272. package/web-shell/assets/index-Cnvv4VcI.css +5 -0
  273. package/web-shell/assets/index-D1Uyklxa.js +1088 -0
  274. package/web-shell/assets/index-DzHwQkQq.js +3 -0
  275. package/web-shell/assets/{infoDiagram-5YYISTIA-DYdAUevm.js → infoDiagram-5YYISTIA-Oixn7tA9.js} +1 -1
  276. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-CNuijZ90.js → ishikawaDiagram-YF4QCWOH-BgKpeg-j.js} +1 -1
  277. package/web-shell/assets/{journeyDiagram-JHISSGLW-BTyiuonf.js → journeyDiagram-JHISSGLW-74mwxIaA.js} +1 -1
  278. package/web-shell/assets/{kanban-definition-UN3LZRKU-DPJo0Uow.js → kanban-definition-UN3LZRKU-D4D6cEqA.js} +1 -1
  279. package/web-shell/assets/{linear-C8wKCbIC.js → linear-atZRE_L2.js} +1 -1
  280. package/web-shell/assets/{mermaid.core-BV5o7nW5.js → mermaid.core-KKudX-JC.js} +5 -5
  281. package/web-shell/assets/{mindmap-definition-RKZ34NQL-BtLcBmSt.js → mindmap-definition-RKZ34NQL-CMCej9yG.js} +1 -1
  282. package/web-shell/assets/{pieDiagram-4H26LBE5-C22J7A9Q.js → pieDiagram-4H26LBE5-nhx40X7q.js} +1 -1
  283. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-DSL10wiL.js → quadrantDiagram-W4KKPZXB-CUmVcN3M.js} +1 -1
  284. package/web-shell/assets/{requirementDiagram-4Y6WPE33-feqh0UpX.js → requirementDiagram-4Y6WPE33-C_vyEwyy.js} +1 -1
  285. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-Z4OVaWaZ.js → sankeyDiagram-5OEKKPKP-DU6G4VWR.js} +1 -1
  286. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-DIluIcb1.js → sequenceDiagram-3UESZ5HK-DdeRR_KJ.js} +1 -1
  287. package/web-shell/assets/{stateDiagram-AJRCARHV-hlhnuoYH.js → stateDiagram-AJRCARHV-C6iCI8ps.js} +1 -1
  288. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-C0_6nR6C.js +1 -0
  289. package/web-shell/assets/{timeline-definition-PNZ67QCA-Dqf_CqgL.js → timeline-definition-PNZ67QCA-B_Ea8Cb7.js} +1 -1
  290. package/web-shell/assets/{vennDiagram-CIIHVFJN-DD2QhaTX.js → vennDiagram-CIIHVFJN-BPUVZEJA.js} +1 -1
  291. package/web-shell/assets/{wardley-L42UT6IY-B2uI85U_.js → wardley-L42UT6IY-BWEj0lKY.js} +1 -1
  292. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-U1aJX71g.js → wardleyDiagram-YWT4CUSO-BvDsdIQw.js} +1 -1
  293. package/web-shell/assets/{xychartDiagram-2RQKCTM6-C0ODHUee.js → xychartDiagram-2RQKCTM6-crz0QhF_.js} +1 -1
  294. package/web-shell/index.html +2 -2
  295. package/chunks/chunk-CU3L64TP.js +0 -93
  296. package/chunks/initializer-UTQ7FB7G.js +0 -71
  297. package/chunks/list-DEICDL65.js +0 -74
  298. package/chunks/loadedSettingsAdapter-NOWEGDYJ.js +0 -68
  299. package/chunks/mcp-L5JXEEIA.js +0 -68
  300. package/chunks/nonInteractiveCli-LS2K4X6A.js +0 -124
  301. package/chunks/types-JNKGKUJT.js +0 -12
  302. package/chunks/workspace-providers-status-4KMGETOJ.js +0 -71
  303. package/chunks/workspace-service-RVLBMSMZ.js +0 -80
  304. package/chunks/workspace-skills-status-DS4ZVFTO.js +0 -70
  305. package/web-shell/assets/channel-CLiQD50f.js +0 -1
  306. package/web-shell/assets/classDiagram-4FO5ZUOK-D3sYqow5.js +0 -1
  307. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-D3sYqow5.js +0 -1
  308. package/web-shell/assets/index-D5IWFVk1.css +0 -5
  309. package/web-shell/assets/index-KFJRDhX7.js +0 -783
  310. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BhQMwgpl.js +0 -1
@@ -3,15 +3,13 @@
3
3
  import {
4
4
  DaemonMetricsRing,
5
5
  computeCpuPercent
6
- } from "./chunk-YVEDCAUE.js";
7
- import {
8
- RUNTIME_STARTUP_CANCELLED_MESSAGE
9
- } from "./chunk-QUPXZXLV.js";
6
+ } from "./chunk-HGT6JR3U.js";
10
7
  import {
11
8
  CAPABILITIES_SCHEMA_VERSION,
12
9
  ClientMcpSenderRegistry,
13
10
  PathMutexRegistry,
14
11
  SERVE_CAPABILITY_REGISTRY,
12
+ WorkspaceVoiceCoordinator,
15
13
  allowOriginCors,
16
14
  allowOriginMode,
17
15
  bearerAuth,
@@ -29,56 +27,73 @@ import {
29
27
  positiveFiniteOrNull,
30
28
  require_express,
31
29
  resolveWebShellDir
32
- } from "./chunk-KHUQZZJ6.js";
30
+ } from "./chunk-MO7O5722.js";
33
31
  import {
34
32
  isLoopbackBind
35
33
  } from "./chunk-RXFQM6FQ.js";
34
+ import {
35
+ RUNTIME_STARTUP_CANCELLED_MESSAGE
36
+ } from "./chunk-QUPXZXLV.js";
36
37
  import {
37
38
  LOAD_REPLAY_META_KEY
38
39
  } from "./chunk-PZL23GTM.js";
39
- import {
40
- canonicalizeWorkspace
41
- } from "./chunk-W7FJ3N32.js";
42
40
  import {
43
41
  DEFAULT_COMPACTED_REPLAY_MAX_BYTES,
44
42
  normalizeCompactedReplayMaxBytes
45
43
  } from "./chunk-COH43MRJ.js";
46
44
  import "./chunk-WBL3FJEU.js";
47
- import "./chunk-T2NCM2ET.js";
48
45
  import {
49
- loadServeFastPathSettings,
50
- preResolveServeFastPathHomeEnvOverrides
51
- } from "./chunk-3SJSQXNS.js";
46
+ resolveChannelCwd
47
+ } from "./chunk-ZRKBI3DP.js";
52
48
  import {
53
- getGlobalQwenDirLite,
54
- resolveConfigPathLite
55
- } from "./chunk-3BTRNWWD.js";
56
- import "./chunk-QRUIS3DN.js";
49
+ MAX_REGISTERED_WORKSPACES,
50
+ resolveWorkspaceInputs,
51
+ workspaceRegistrationId
52
+ } from "./chunk-L37RC7MQ.js";
53
+ import {
54
+ canonicalizeWorkspace
55
+ } from "./chunk-W7FJ3N32.js";
56
+ import "./chunk-3MX7D6QN.js";
57
57
  import {
58
- ChannelWebhookEnqueueError,
59
58
  QWEN_SERVER_TOKEN_ENV
60
- } from "./chunk-X4NKFGFJ.js";
59
+ } from "./chunk-GYTKQYDB.js";
61
60
  import {
62
61
  channelSelectionNames
63
62
  } from "./chunk-TJTKSVIV.js";
63
+ import "./chunk-5HBA2Z7V.js";
64
+ import {
65
+ ChannelWebhookEnqueueError
66
+ } from "./chunk-OJAMDVF5.js";
67
+ import "./chunk-T2NCM2ET.js";
64
68
  import {
65
69
  sanitizeLogText
66
- } from "./chunk-34Z3U5RQ.js";
70
+ } from "./chunk-SXU3KKNI.js";
67
71
  import "./chunk-UDXSXLER.js";
68
72
  import {
69
- isWithinRoot
70
- } from "./chunk-RDJSGHZE.js";
73
+ loadServeFastPathSettings,
74
+ preResolveServeFastPathHomeEnvOverrides
75
+ } from "./chunk-PEJWA3QQ.js";
76
+ import "./chunk-GOXKNSDZ.js";
71
77
  import {
72
- getCliVersion
73
- } from "./chunk-3AHXV67U.js";
74
- import "./chunk-VJNBRRZG.js";
78
+ getGlobalQwenDirLite,
79
+ resolveConfigPathLite
80
+ } from "./chunk-IDS7MSUP.js";
81
+ import "./chunk-QRUIS3DN.js";
82
+ import "./chunk-ALGGS7UH.js";
83
+ import {
84
+ isWithinRoot
85
+ } from "./chunk-MR3PXB6E.js";
75
86
  import {
76
87
  writeStderrLine,
77
88
  writeStdoutLine
78
89
  } from "./chunk-YUZI3WAC.js";
79
- import "./chunk-CU3L64TP.js";
80
- import "./chunk-ZJE7NSL3.js";
90
+ import {
91
+ getCliVersion
92
+ } from "./chunk-YU56QGXX.js";
93
+ import "./chunk-VJNBRRZG.js";
81
94
  import "./chunk-H6XPXXMH.js";
95
+ import "./chunk-5M6IDOMF.js";
96
+ import "./chunk-E2UVIFI2.js";
82
97
  import "./chunk-CR3C7WXL.js";
83
98
  import "./chunk-XWRJCPHC.js";
84
99
  import {
@@ -106,92 +121,6 @@ import * as path from "node:path";
106
121
  import * as os from "node:os";
107
122
  import { monitorEventLoopDelay, performance } from "node:perf_hooks";
108
123
 
109
- // packages/cli/src/serve/workspace-inputs.ts
110
- init_esbuild_shims();
111
- var DuplicateWorkspaceInputError = class extends Error {
112
- static {
113
- __name(this, "DuplicateWorkspaceInputError");
114
- }
115
- constructor(workspace) {
116
- super(
117
- `Duplicate --workspace value resolves to ${JSON.stringify(workspace)}. Pass distinct --workspace values.`
118
- );
119
- this.name = "DuplicateWorkspaceInputError";
120
- }
121
- };
122
- var NestedWorkspaceInputError = class extends Error {
123
- static {
124
- __name(this, "NestedWorkspaceInputError");
125
- }
126
- constructor(parent, child) {
127
- super(
128
- `Nested --workspace values are not supported yet: ${JSON.stringify(child)} is inside ${JSON.stringify(parent)}. Pass non-nested --workspace values.`
129
- );
130
- this.name = "NestedWorkspaceInputError";
131
- }
132
- };
133
- var MissingWorkspaceInputError = class extends Error {
134
- static {
135
- __name(this, "MissingWorkspaceInputError");
136
- }
137
- constructor() {
138
- super("--workspace requires a value.");
139
- this.name = "MissingWorkspaceInputError";
140
- }
141
- };
142
- function normalizeWorkspaceInputs(workspace) {
143
- if (Array.isArray(workspace)) {
144
- if (workspace.length === 0) {
145
- throw new MissingWorkspaceInputError();
146
- }
147
- return workspace.map((value) => String(value));
148
- }
149
- if (workspace === void 0) return [process.cwd()];
150
- return [String(workspace)];
151
- }
152
- __name(normalizeWorkspaceInputs, "normalizeWorkspaceInputs");
153
- function isNestedWorkspace(parent, child) {
154
- return parent !== child && isWithinRoot(child, parent);
155
- }
156
- __name(isNestedWorkspace, "isNestedWorkspace");
157
- function rejectDuplicateOrNestedWorkspaceInputs(workspaces) {
158
- if (workspaces.length <= 1) return;
159
- let canonicalWorkspaces;
160
- try {
161
- canonicalWorkspaces = workspaces.map(
162
- (workspace) => canonicalizeWorkspace(workspace)
163
- );
164
- } catch {
165
- return;
166
- }
167
- const seen = /* @__PURE__ */ new Set();
168
- for (const workspace of canonicalWorkspaces) {
169
- if (seen.has(workspace)) {
170
- throw new DuplicateWorkspaceInputError(workspace);
171
- }
172
- seen.add(workspace);
173
- }
174
- for (let i = 0; i < canonicalWorkspaces.length; i++) {
175
- for (let j = i + 1; j < canonicalWorkspaces.length; j++) {
176
- const first = canonicalWorkspaces[i];
177
- const second = canonicalWorkspaces[j];
178
- if (isNestedWorkspace(first, second)) {
179
- throw new NestedWorkspaceInputError(first, second);
180
- }
181
- if (isNestedWorkspace(second, first)) {
182
- throw new NestedWorkspaceInputError(second, first);
183
- }
184
- }
185
- }
186
- }
187
- __name(rejectDuplicateOrNestedWorkspaceInputs, "rejectDuplicateOrNestedWorkspaceInputs");
188
- function resolveWorkspaceInputs(workspace) {
189
- const workspaces = normalizeWorkspaceInputs(workspace);
190
- rejectDuplicateOrNestedWorkspaceInputs(workspaces);
191
- return workspaces;
192
- }
193
- __name(resolveWorkspaceInputs, "resolveWorkspaceInputs");
194
-
195
124
  // packages/cli/src/serve/permission-audit.ts
196
125
  init_esbuild_shims();
197
126
  var DEFAULT_PERMISSION_AUDIT_RING_SIZE = 512;
@@ -889,6 +818,120 @@ function asRecord(value) {
889
818
  }
890
819
  __name(asRecord, "asRecord");
891
820
 
821
+ // packages/cli/src/serve/channel-workspace-grouping.ts
822
+ init_esbuild_shims();
823
+ function resolveChannelOwnerCwd(rawCwd, workspaceCwd) {
824
+ return canonicalizeWorkspace(resolveChannelCwd(rawCwd, workspaceCwd));
825
+ }
826
+ __name(resolveChannelOwnerCwd, "resolveChannelOwnerCwd");
827
+ function rawChannelCwd(entry) {
828
+ if (!entry || typeof entry !== "object") return void 0;
829
+ const cwd = entry.cwd;
830
+ return typeof cwd === "string" && cwd.length > 0 ? cwd : void 0;
831
+ }
832
+ __name(rawChannelCwd, "rawChannelCwd");
833
+ function resolveChannelWorkspaceGroups(input) {
834
+ const { workspaces, selection, loadChannelsConfig } = input;
835
+ const primary = workspaces.find((workspace) => workspace.primary);
836
+ if (!primary) {
837
+ return {
838
+ ok: false,
839
+ error: {
840
+ code: "no_primary_workspace",
841
+ message: "No primary workspace is registered."
842
+ }
843
+ };
844
+ }
845
+ if (selection.mode === "all") {
846
+ if (!primary.trusted) {
847
+ return {
848
+ ok: false,
849
+ error: {
850
+ code: "untrusted_workspace",
851
+ message: `Primary workspace "${primary.workspaceCwd}" is not trusted; cannot host channels.`
852
+ }
853
+ };
854
+ }
855
+ return {
856
+ ok: true,
857
+ groups: [
858
+ { workspaceCwd: primary.workspaceCwd, selection: { mode: "all" } }
859
+ ]
860
+ };
861
+ }
862
+ const channelsConfigByWorkspace = /* @__PURE__ */ new Map();
863
+ for (const workspace of workspaces) {
864
+ channelsConfigByWorkspace.set(
865
+ workspace.workspaceCwd,
866
+ loadChannelsConfig(workspace.workspaceCwd)
867
+ );
868
+ }
869
+ const namesByWorkspace = /* @__PURE__ */ new Map();
870
+ for (const name of selection.names) {
871
+ const owners = [];
872
+ for (const workspace of workspaces) {
873
+ const entry = (channelsConfigByWorkspace.get(workspace.workspaceCwd) ?? {})[name];
874
+ if (!entry || typeof entry !== "object") continue;
875
+ let ownerCwd;
876
+ try {
877
+ ownerCwd = resolveChannelOwnerCwd(
878
+ rawChannelCwd(entry),
879
+ workspace.workspaceCwd
880
+ );
881
+ } catch {
882
+ continue;
883
+ }
884
+ if (ownerCwd === workspace.workspaceCwd) {
885
+ owners.push(workspace);
886
+ }
887
+ }
888
+ if (owners.length === 0) {
889
+ return {
890
+ ok: false,
891
+ error: {
892
+ code: "channel_workspace_mismatch",
893
+ channel: name,
894
+ message: `Channel "${name}" is not configured in any registered workspace, or its "cwd" points outside them.`
895
+ }
896
+ };
897
+ }
898
+ if (owners.length > 1) {
899
+ return {
900
+ ok: false,
901
+ error: {
902
+ code: "ambiguous_channel_workspace",
903
+ channel: name,
904
+ message: `Channel "${name}" is configured in multiple registered workspaces (${owners.map((owner2) => owner2.workspaceCwd).join(
905
+ ", "
906
+ )}). Define it in one workspace's settings or set an explicit "cwd".`
907
+ }
908
+ };
909
+ }
910
+ const owner = owners[0];
911
+ if (!owner.trusted) {
912
+ return {
913
+ ok: false,
914
+ error: {
915
+ code: "untrusted_workspace",
916
+ channel: name,
917
+ message: `Channel "${name}" targets untrusted workspace "${owner.workspaceCwd}".`
918
+ }
919
+ };
920
+ }
921
+ const names = namesByWorkspace.get(owner.workspaceCwd) ?? [];
922
+ names.push(name);
923
+ namesByWorkspace.set(owner.workspaceCwd, names);
924
+ }
925
+ return {
926
+ ok: true,
927
+ groups: [...namesByWorkspace.entries()].map(([workspaceCwd, names]) => ({
928
+ workspaceCwd,
929
+ selection: { mode: "names", names }
930
+ }))
931
+ };
932
+ }
933
+ __name(resolveChannelWorkspaceGroups, "resolveChannelWorkspaceGroups");
934
+
892
935
  // packages/cli/src/serve/run-qwen-serve.ts
893
936
  var QWEN_SERVE_CLIENT_MCP_OVER_WS_ENV = "QWEN_SERVE_CLIENT_MCP_OVER_WS";
894
937
  var QWEN_SERVE_CDP_TUNNEL_OVER_WS_ENV = "QWEN_SERVE_CDP_TUNNEL_OVER_WS";
@@ -1093,14 +1136,29 @@ __name(withSettingsLock, "withSettingsLock");
1093
1136
  var channelWorkerRuntimePromise;
1094
1137
  async function loadChannelWorkerRuntime() {
1095
1138
  channelWorkerRuntimePromise ??= Promise.all([
1096
- import("./channel-worker-supervisor-WSOAAYUO.js"),
1097
- import("./pidfile-D4I4FTVA.js"),
1098
- import("./cli-entry-path-4VJ3Y6T2.js")
1099
- ]).then(([supervisor, pidfile, cliEntryPath]) => ({
1100
- createChannelWorkerSupervisor: supervisor.createChannelWorkerSupervisor,
1101
- channelServicePidfile: pidfile,
1102
- findCliEntryPath: cliEntryPath.findCliEntryPath
1103
- })).catch((err) => {
1139
+ import("./channel-worker-supervisor-PCDAZN4D.js"),
1140
+ import("./pidfile-XE4RRR2P.js"),
1141
+ import("./runtime-EIO3NQKR.js"),
1142
+ import("./cli-entry-path-4VJ3Y6T2.js"),
1143
+ import("./channel-worker-group-K7QKH6IG.js"),
1144
+ import("./channel-worker-manager-5RWKSGEU.js")
1145
+ ]).then(
1146
+ ([
1147
+ supervisor,
1148
+ pidfile,
1149
+ channelRuntime,
1150
+ cliEntryPath,
1151
+ workerGroup,
1152
+ workerManager
1153
+ ]) => ({
1154
+ createChannelWorkerSupervisor: supervisor.createChannelWorkerSupervisor,
1155
+ channelServicePidfile: pidfile,
1156
+ loadChannelsConfig: channelRuntime.loadChannelsConfig,
1157
+ createChannelWorkerGroup: workerGroup.createChannelWorkerGroup,
1158
+ createChannelWorkerManager: workerManager.createChannelWorkerManager,
1159
+ findCliEntryPath: cliEntryPath.findCliEntryPath
1160
+ })
1161
+ ).catch((err) => {
1104
1162
  channelWorkerRuntimePromise = void 0;
1105
1163
  throw err;
1106
1164
  });
@@ -1142,11 +1200,23 @@ function writeServeChannelReservation(channelServicePidfile, channels) {
1142
1200
  __name(writeServeChannelReservation, "writeServeChannelReservation");
1143
1201
  function channelServicePidfileConflictError(info) {
1144
1202
  const owner = info.owner === "serve" ? "qwen serve" : "qwen channel start";
1145
- return new Error(
1146
- `Channel service is already running under ${owner} (PID ${info.pid}). Stop it before starting qwen serve --channel.`
1203
+ return Object.assign(
1204
+ new Error(
1205
+ `Channel service is already running under ${owner} (PID ${info.pid}). Stop it before enabling daemon-managed channels.`
1206
+ ),
1207
+ { code: "channel_service_conflict", owner: info.owner, pid: info.pid }
1147
1208
  );
1148
1209
  }
1149
1210
  __name(channelServicePidfileConflictError, "channelServicePidfileConflictError");
1211
+ function channelServiceStartingConflictError() {
1212
+ return Object.assign(
1213
+ new Error(
1214
+ "Channel service is already starting. Retry after the current startup finishes."
1215
+ ),
1216
+ { code: "channel_service_conflict" }
1217
+ );
1218
+ }
1219
+ __name(channelServiceStartingConflictError, "channelServiceStartingConflictError");
1150
1220
  function normalizeInstallModelIds(req, provider, getDefaultModelIds) {
1151
1221
  const fromRequest = req.modelIds?.map((id) => id.trim()).filter((id) => id.length > 0);
1152
1222
  const modelIds = fromRequest && fromRequest.length > 0 ? fromRequest : getDefaultModelIds(provider);
@@ -1176,7 +1246,7 @@ function shouldPreheatBridge(deps) {
1176
1246
  __name(shouldPreheatBridge, "shouldPreheatBridge");
1177
1247
  var coreRuntimePromise;
1178
1248
  function loadCoreRuntime() {
1179
- coreRuntimePromise ??= import("./src-G5UPLZSZ.js");
1249
+ coreRuntimePromise ??= import("./src-USZKPUOC.js");
1180
1250
  return coreRuntimePromise;
1181
1251
  }
1182
1252
  __name(loadCoreRuntime, "loadCoreRuntime");
@@ -1206,10 +1276,10 @@ __name(resolveDaemonLogBaseDirForRun, "resolveDaemonLogBaseDirForRun");
1206
1276
  var settingsRuntimePromise;
1207
1277
  function loadSettingsRuntimeModules() {
1208
1278
  settingsRuntimePromise ??= Promise.all([
1209
- import("./settings-3EYN66GK.js"),
1210
- import("./environment-5HSSRMJ7.js"),
1211
- import("./loadedSettingsAdapter-NOWEGDYJ.js"),
1212
- import("./trustedFolders-EOGT3UVS.js")
1279
+ import("./settings-HQNDK7TG.js"),
1280
+ import("./environment-7TEWMJYN.js"),
1281
+ import("./loadedSettingsAdapter-WAM4VUTG.js"),
1282
+ import("./trustedFolders-C5LMF3UR.js")
1213
1283
  ]).then(([settings, environment, loadedSettingsAdapter, trustedFolders]) => ({
1214
1284
  settings,
1215
1285
  environment,
@@ -1222,8 +1292,8 @@ __name(loadSettingsRuntimeModules, "loadSettingsRuntimeModules");
1222
1292
  var channelWebhookConfigRuntimePromise;
1223
1293
  function loadChannelWebhookConfigRuntime() {
1224
1294
  channelWebhookConfigRuntimePromise ??= Promise.all([
1225
- import("./runtime-57WWRVHN.js"),
1226
- import("./config-utils-NV2SVGPB.js")
1295
+ import("./runtime-EIO3NQKR.js"),
1296
+ import("./config-utils-CFT33LTP.js")
1227
1297
  ]).then(([channelRuntime, configUtils]) => ({
1228
1298
  loadChannelsConfig: channelRuntime.loadChannelsConfig,
1229
1299
  parseChannelWebhookConfig: configUtils.parseChannelWebhookConfig
@@ -1247,16 +1317,16 @@ async function loadServeRuntimeModules() {
1247
1317
  totalSessionAdmissionModule,
1248
1318
  workspaceRegistryModule
1249
1319
  ] = await Promise.all([
1250
- import("./server-Z22GCAXP.js"),
1251
- import("./bridge-BCJQ7BBB.js"),
1252
- import("./spawnChannel-JD7G4VU6.js"),
1253
- import("./workspace-service-RVLBMSMZ.js"),
1254
- import("./types-JNKGKUJT.js"),
1255
- import("./daemon-status-provider-B2UTZUJ2.js"),
1256
- import("./workspace-providers-status-4KMGETOJ.js"),
1257
- import("./workspace-skills-status-DS4ZVFTO.js"),
1258
- import("./total-session-admission-JOKNRIIA.js"),
1259
- import("./workspace-registry-24QFLQYH.js")
1320
+ import("./server-NJPHQHLH.js"),
1321
+ import("./bridge-XFKW6S7Q.js"),
1322
+ import("./spawnChannel-3R632QRM.js"),
1323
+ import("./workspace-service-ORKHGLP3.js"),
1324
+ import("./types-ML3TRJQ5.js"),
1325
+ import("./daemon-status-provider-L4STG5VO.js"),
1326
+ import("./workspace-providers-status-I3G2HYLY.js"),
1327
+ import("./workspace-skills-status-D7QCNDYY.js"),
1328
+ import("./total-session-admission-PL5N7DNY.js"),
1329
+ import("./workspace-registry-QEKBELIX.js")
1260
1330
  ]);
1261
1331
  return {
1262
1332
  createServeApp: serverModule.createServeApp,
@@ -1267,6 +1337,7 @@ async function loadServeRuntimeModules() {
1267
1337
  createSpawnChannelFactory: spawnChannelModule.createSpawnChannelFactory,
1268
1338
  createDaemonWorkspaceService: workspaceModule.createDaemonWorkspaceService,
1269
1339
  WorkspaceSettingsPartialPersistError: workspaceTypesModule.WorkspaceSettingsPartialPersistError,
1340
+ WorkspaceSkillNotToggleableError: workspaceTypesModule.WorkspaceSkillNotToggleableError,
1270
1341
  createDaemonStatusProvider: daemonStatusProviderModule.createDaemonStatusProvider,
1271
1342
  createWorkspaceProvidersStatusProvider: workspaceProvidersStatusModule.createWorkspaceProvidersStatusProvider,
1272
1343
  createWorkspaceSkillsStatusProvider: workspaceSkillsStatusModule.createWorkspaceSkillsStatusProvider,
@@ -1309,6 +1380,9 @@ function currentServeFeaturesForRunQwenServe(opts, sessionShellCommandEnabled, s
1309
1380
  rateLimit: opts.rateLimit === true,
1310
1381
  reloadAvailable: true,
1311
1382
  channelReloadAvailable: opts.channelSelection !== void 0,
1383
+ channelControlAvailable: true,
1384
+ persistentWorkspaceRegistrationAvailable: true,
1385
+ workspaceRuntimeRemovalAvailable: true,
1312
1386
  // Advertise the same WS feature flags as the runtime path (serve-features.ts)
1313
1387
  // so the bootstrap `/capabilities` window doesn't briefly under-report them.
1314
1388
  clientMcpOverWsEnabled: opts.clientMcpOverWs === true,
@@ -1477,6 +1551,7 @@ function createBootstrapServeApp(input) {
1477
1551
  multiWorkspaceCapabilitiesRequireRuntime,
1478
1552
  getRuntimeError,
1479
1553
  getChannelWorkerSnapshot,
1554
+ getChannelWorkerSnapshots,
1480
1555
  onHealthServed
1481
1556
  } = input;
1482
1557
  const app = (0, import_express.default)();
@@ -1545,6 +1620,7 @@ function createBootstrapServeApp(input) {
1545
1620
  }
1546
1621
  const runtimeError = getRuntimeError();
1547
1622
  const channelWorker = getChannelWorkerSnapshot();
1623
+ const channelWorkers = getChannelWorkerSnapshots();
1548
1624
  const runtimeFailed = runtimeError !== void 0;
1549
1625
  const issue = runtimeError ? {
1550
1626
  code: "daemon_runtime_failed",
@@ -1616,6 +1692,7 @@ function createBootstrapServeApp(input) {
1616
1692
  },
1617
1693
  channel: { live: false },
1618
1694
  channelWorker,
1695
+ ...channelWorkers.length > 0 ? { channelWorkers } : {},
1619
1696
  transport: {
1620
1697
  restSseActive: 0,
1621
1698
  acp: {
@@ -1706,7 +1783,7 @@ function isChannelWebhookRequest(req) {
1706
1783
  return req.method === "POST" && /^\/channels\/[^/]+\/webhooks\/[^/]+\/?$/u.test(req.path);
1707
1784
  }
1708
1785
  __name(isChannelWebhookRequest, "isChannelWebhookRequest");
1709
- function createDeferredChannelWebhookAuth(workspace, runtime, daemonLog) {
1786
+ function createDeferredChannelWebhookAuth(resolveSource, runtime, daemonLog) {
1710
1787
  return (req, res, next) => {
1711
1788
  const match = /^\/channels\/([^/]+)\/webhooks\/([^/]+)\/?$/u.exec(req.path);
1712
1789
  const channelName = decodeDeferredWebhookPathSegment(match?.[1]);
@@ -1720,11 +1797,13 @@ function createDeferredChannelWebhookAuth(workspace, runtime, daemonLog) {
1720
1797
  res.status(401).json({ error: "Invalid webhook secret" });
1721
1798
  return;
1722
1799
  }
1800
+ const configSource = resolveSource(channelName);
1723
1801
  const secret = readDeferredWebhookSecret(
1724
1802
  runtime,
1725
- workspace,
1803
+ configSource.workspaceCwd,
1726
1804
  channelName,
1727
- source
1805
+ source,
1806
+ configSource.env
1728
1807
  );
1729
1808
  if (!matchesWebhookSecret(req.get("x-qwen-webhook-secret"), secret)) {
1730
1809
  daemonLog.warn("deferred webhook auth failed", {
@@ -1748,7 +1827,7 @@ function decodeDeferredWebhookPathSegment(segment) {
1748
1827
  }
1749
1828
  }
1750
1829
  __name(decodeDeferredWebhookPathSegment, "decodeDeferredWebhookPathSegment");
1751
- function readDeferredWebhookSecret(runtime, workspace, channelName, source) {
1830
+ function readDeferredWebhookSecret(runtime, workspace, channelName, source, env) {
1752
1831
  try {
1753
1832
  const rawConfig = runtime.loadChannelsConfig(workspace)[channelName];
1754
1833
  if (typeof rawConfig !== "object" || rawConfig === null) {
@@ -1756,7 +1835,8 @@ function readDeferredWebhookSecret(runtime, workspace, channelName, source) {
1756
1835
  }
1757
1836
  return runtime.parseChannelWebhookConfig(
1758
1837
  channelName,
1759
- rawConfig
1838
+ rawConfig,
1839
+ env
1760
1840
  )?.sources[source]?.secret;
1761
1841
  } catch (err) {
1762
1842
  const reason = err instanceof Error ? err.message : String(err);
@@ -1841,30 +1921,42 @@ async function runQwenServe(optsIn, deps = {}) {
1841
1921
  clientMcpOverWs: optsIn.clientMcpOverWs ?? (!envFlagDisabled(clientMcpOverWsEnv) && clientMcpOverWsEnv !== void 0),
1842
1922
  cdpTunnelOverWs: optsIn.cdpTunnelOverWs ?? (!envFlagDisabled(cdpTunnelOverWsEnv) && (cdpTunnelOverWsEnv !== void 0 || chromeExtensionOriginAllowed))
1843
1923
  };
1844
- const channelRuntime = opts.channelSelection ? await loadChannelWorkerRuntime() : void 0;
1845
- const channelServicePidfile = deps.channelServicePidfile ?? channelRuntime?.channelServicePidfile;
1924
+ let channelRuntime = opts.channelSelection ? await loadChannelWorkerRuntime() : void 0;
1925
+ let channelServicePidfile = deps.channelServicePidfile ?? channelRuntime?.channelServicePidfile;
1926
+ const ensureChannelRuntime = /* @__PURE__ */ __name(async () => {
1927
+ channelRuntime ??= await loadChannelWorkerRuntime();
1928
+ channelServicePidfile ??= channelRuntime.channelServicePidfile;
1929
+ return channelRuntime;
1930
+ }, "ensureChannelRuntime");
1846
1931
  let channelPidfileReserved = false;
1847
1932
  const removeCurrentServePidfile = /* @__PURE__ */ __name(() => {
1848
- if (!opts.channelSelection || !channelServicePidfile) return;
1933
+ if (!channelServicePidfile) return;
1849
1934
  if (!channelPidfileReserved) return;
1850
1935
  if (channelServicePidfile.removeServeServiceInfo) {
1851
1936
  if (channelServicePidfile.removeServeServiceInfo(process.pid)) {
1852
1937
  channelPidfileReserved = false;
1938
+ } else {
1939
+ const info2 = channelServicePidfile.readServiceInfo();
1940
+ if (!info2 || info2.owner !== "serve" || info2.pid !== process.pid || info2.servePid !== process.pid) {
1941
+ channelPidfileReserved = false;
1942
+ }
1853
1943
  }
1854
1944
  return;
1855
1945
  }
1856
1946
  const info = channelServicePidfile.readServiceInfo();
1857
1947
  if (info?.owner === "serve" && info.pid === process.pid && info.servePid === process.pid) {
1858
1948
  channelServicePidfile.removeServiceInfo();
1949
+ const remaining = channelServicePidfile.readServiceInfo();
1950
+ channelPidfileReserved = remaining?.owner === "serve" && remaining.pid === process.pid && remaining.servePid === process.pid;
1951
+ } else {
1859
1952
  channelPidfileReserved = false;
1860
1953
  }
1861
1954
  }, "removeCurrentServePidfile");
1862
- const reserveChannelServicePidfile = /* @__PURE__ */ __name(() => {
1863
- if (!opts.channelSelection) return;
1955
+ const reserveChannelServicePidfile = /* @__PURE__ */ __name((selection) => {
1864
1956
  if (!channelServicePidfile) {
1865
1957
  throw new Error("Channel service pidfile runtime is not available.");
1866
1958
  }
1867
- const channelPidfileNames = channelSelectionNames(opts.channelSelection);
1959
+ const channelPidfileNames = channelSelectionNames(selection);
1868
1960
  const existingChannelService = channelServicePidfile.readServiceInfo();
1869
1961
  if (existingChannelService) {
1870
1962
  throw channelServicePidfileConflictError(existingChannelService);
@@ -1889,9 +1981,7 @@ async function runQwenServe(optsIn, deps = {}) {
1889
1981
  return;
1890
1982
  } catch (retryErr) {
1891
1983
  if (retryErr && typeof retryErr === "object" && "code" in retryErr && retryErr.code === "EEXIST") {
1892
- throw new Error(
1893
- "Channel service is already starting. Retry after the current startup finishes."
1894
- );
1984
+ throw channelServiceStartingConflictError();
1895
1985
  }
1896
1986
  throw retryErr;
1897
1987
  }
@@ -2010,7 +2100,9 @@ async function runQwenServe(optsIn, deps = {}) {
2010
2100
  }, "validateAndCanonicalizeWorkspace");
2011
2101
  const workspaceInputs = rawWorkspaces.map((workspace) => ({
2012
2102
  raw: workspace,
2013
- cwd: validateAndCanonicalizeWorkspace(workspace)
2103
+ cwd: validateAndCanonicalizeWorkspace(workspace),
2104
+ removable: false,
2105
+ registrationIds: []
2014
2106
  }));
2015
2107
  const boundWorkspace = workspaceInputs[0].cwd;
2016
2108
  const seenCanonicalWorkspaces = /* @__PURE__ */ new Set();
@@ -2042,6 +2134,71 @@ async function runQwenServe(optsIn, deps = {}) {
2042
2134
  }
2043
2135
  }
2044
2136
  }
2137
+ if (workspaceInputs.length > MAX_REGISTERED_WORKSPACES) {
2138
+ throw new Error(
2139
+ `At most ${MAX_REGISTERED_WORKSPACES} --workspace values may be registered.`
2140
+ );
2141
+ }
2142
+ let workspaceRegistrationStore = deps.workspaceRegistrationStore;
2143
+ if (workspaceRegistrationStore === void 0 && process.env["QWEN_SERVE_NO_PERSISTENT_REGISTRATION"] !== "1") {
2144
+ const { WorkspaceRegistrationStore } = await import("./workspace-registration-store-IH5GLZ6Y.js");
2145
+ workspaceRegistrationStore = new WorkspaceRegistrationStore(boundWorkspace);
2146
+ }
2147
+ if (workspaceRegistrationStore) {
2148
+ try {
2149
+ const stored = await workspaceRegistrationStore.read();
2150
+ for (const storedWorkspace of stored.workspaces) {
2151
+ const registrationId = workspaceRegistrationId(storedWorkspace);
2152
+ let cwd;
2153
+ try {
2154
+ cwd = validateAndCanonicalizeWorkspace(storedWorkspace);
2155
+ } catch (err) {
2156
+ writeStderrLine(
2157
+ `qwen serve: skipping persisted workspace registration ${JSON.stringify(
2158
+ storedWorkspace
2159
+ )}: ${err instanceof Error ? err.message : String(err)}`
2160
+ );
2161
+ continue;
2162
+ }
2163
+ const existingInput = workspaceInputs.find(
2164
+ (workspace) => workspace.cwd === cwd
2165
+ );
2166
+ if (existingInput) {
2167
+ existingInput.registrationIds.push(registrationId);
2168
+ continue;
2169
+ }
2170
+ const nested = workspaceInputs.some(
2171
+ (workspace) => isWithinRoot(cwd, workspace.cwd) || isWithinRoot(workspace.cwd, cwd)
2172
+ );
2173
+ if (nested) {
2174
+ writeStderrLine(
2175
+ `qwen serve: skipping persisted workspace registration ${JSON.stringify(
2176
+ storedWorkspace
2177
+ )}: path nests with an explicit or earlier restored workspace`
2178
+ );
2179
+ continue;
2180
+ }
2181
+ if (workspaceInputs.length >= MAX_REGISTERED_WORKSPACES) {
2182
+ writeStderrLine(
2183
+ `qwen serve: skipping persisted workspace registration ${JSON.stringify(
2184
+ storedWorkspace
2185
+ )}: workspace limit reached`
2186
+ );
2187
+ continue;
2188
+ }
2189
+ workspaceInputs.push({
2190
+ raw: storedWorkspace,
2191
+ cwd,
2192
+ removable: true,
2193
+ registrationIds: [registrationId]
2194
+ });
2195
+ }
2196
+ } catch (err) {
2197
+ writeStderrLine(
2198
+ `qwen serve: failed to read persisted workspace registrations: ${err instanceof Error ? err.message : String(err)}; continuing with explicit workspaces only`
2199
+ );
2200
+ }
2201
+ }
2045
2202
  if (workspaceInputs.length > 1 && deps.bridge) {
2046
2203
  throw new Error(
2047
2204
  "Injected bridge dependencies are only supported with a single workspace; multiple --workspace values require runQwenServe to construct one bridge per workspace."
@@ -2227,21 +2384,139 @@ async function runQwenServe(optsIn, deps = {}) {
2227
2384
  void runtimeReady.catch(() => {
2228
2385
  });
2229
2386
  const disposeDaemonEventLoopMonitor = /* @__PURE__ */ __name(() => {
2230
- daemonEventLoopMonitor?.dispose();
2387
+ const eventLoopMonitor = daemonEventLoopMonitor;
2231
2388
  daemonEventLoopMonitor = void 0;
2232
- daemonMetricsSampler?.dispose();
2389
+ const metricsSampler = daemonMetricsSampler;
2233
2390
  daemonMetricsSampler = void 0;
2391
+ try {
2392
+ eventLoopMonitor?.dispose();
2393
+ } catch (err) {
2394
+ daemonLog.warn(
2395
+ `event loop monitor dispose error: ${err instanceof Error ? err.message : String(err)}`
2396
+ );
2397
+ }
2398
+ try {
2399
+ metricsSampler?.dispose();
2400
+ } catch (err) {
2401
+ daemonLog.warn(
2402
+ `metrics sampler dispose error: ${err instanceof Error ? err.message : String(err)}`
2403
+ );
2404
+ }
2234
2405
  }, "disposeDaemonEventLoopMonitor");
2235
- let channelWorker = createDisabledChannelWorkerSupervisor();
2236
- const getChannelWorkerSnapshot = /* @__PURE__ */ __name(() => channelWorker.snapshot(), "getChannelWorkerSnapshot");
2237
- const reloadChannelWorker = /* @__PURE__ */ __name(() => channelWorker.restart(), "reloadChannelWorker");
2238
- const writeChannelWorkerPidfile = /* @__PURE__ */ __name((snapshot = channelWorker.snapshot(), options = {}) => {
2239
- if (!opts.channelSelection || !channelServicePidfile) return;
2406
+ let channelWorkerManager;
2407
+ let channelWorkerManagerStarting;
2408
+ let channelControlDraining = false;
2409
+ let channelWorkspaceGroups;
2410
+ const channelWebhookEnvByWorkspace = /* @__PURE__ */ new Map();
2411
+ let channelWebhookConfigVersion = 0;
2412
+ let refreshChannelWebhookConfigs;
2413
+ let ensureChannelWorkerManager;
2414
+ const getChannelWebhookConfigSources = /* @__PURE__ */ __name(() => {
2415
+ const app2 = runtimeApp ?? runtimeAppForCleanup;
2416
+ const registry = app2?.locals?.["workspaceRegistry"];
2417
+ return (channelWorkspaceGroups ?? []).map((group) => {
2418
+ const env = registry?.getByWorkspaceCwd(group.workspaceCwd)?.env.effectiveEnv ?? channelWebhookEnvByWorkspace.get(group.workspaceCwd);
2419
+ return {
2420
+ workspaceCwd: group.workspaceCwd,
2421
+ ...group.selection.mode === "names" ? { channelNames: group.selection.names } : {},
2422
+ ...env ? { env } : {}
2423
+ };
2424
+ });
2425
+ }, "getChannelWebhookConfigSources");
2426
+ const resolveChannelWebhookConfigSource = /* @__PURE__ */ __name((channelName) => {
2427
+ const source = getChannelWebhookConfigSources().find(
2428
+ (source2) => !source2.channelNames || source2.channelNames.includes(channelName)
2429
+ );
2430
+ if (source) return source;
2431
+ const env = channelWebhookEnvByWorkspace.get(boundWorkspace);
2432
+ return {
2433
+ workspaceCwd: boundWorkspace,
2434
+ ...env ? { env } : {}
2435
+ };
2436
+ }, "resolveChannelWebhookConfigSource");
2437
+ let closeServerAfterChannelWorkerStartupFailure = false;
2438
+ let runtimeFailureListenerClose;
2439
+ const getChannelWorkerSnapshot = /* @__PURE__ */ __name(() => channelWorkerManager?.primarySnapshot() ?? {
2440
+ enabled: false,
2441
+ state: "disabled",
2442
+ channels: []
2443
+ }, "getChannelWorkerSnapshot");
2444
+ const getChannelWorkerSnapshots = /* @__PURE__ */ __name(() => channelWorkerManager?.snapshots() ?? [], "getChannelWorkerSnapshots");
2445
+ const getChannelWorkerControl = /* @__PURE__ */ __name(() => channelWorkerManager?.state() ?? {
2446
+ enabled: false,
2447
+ selection: null,
2448
+ transition: "idle",
2449
+ workers: []
2450
+ }, "getChannelWorkerControl");
2451
+ const daemonDrainingError = /* @__PURE__ */ __name(() => Object.assign(new Error("Daemon is shutting down."), {
2452
+ code: "daemon_draining"
2453
+ }), "daemonDrainingError");
2454
+ const setChannelWorkerSelection = /* @__PURE__ */ __name(async (selection) => {
2455
+ if (channelControlDraining) throw daemonDrainingError();
2456
+ const manager = await ensureChannelWorkerManager?.();
2457
+ if (!manager) throw new Error("Channel worker manager is unavailable.");
2458
+ if (channelControlDraining) {
2459
+ await manager.shutdown().catch(() => void 0);
2460
+ throw daemonDrainingError();
2461
+ }
2462
+ return manager.setSelection(selection);
2463
+ }, "setChannelWorkerSelection");
2464
+ const stopChannelWorker = /* @__PURE__ */ __name(async () => {
2465
+ if (channelControlDraining) throw daemonDrainingError();
2466
+ const manager = channelWorkerManager ?? await channelWorkerManagerStarting;
2467
+ if (channelControlDraining) {
2468
+ await manager?.shutdown().catch(() => void 0);
2469
+ throw daemonDrainingError();
2470
+ }
2471
+ if (!manager) {
2472
+ return { changed: false, state: getChannelWorkerControl() };
2473
+ }
2474
+ return manager.stopSelection();
2475
+ }, "stopChannelWorker");
2476
+ const reloadChannelWorker = /* @__PURE__ */ __name(async () => {
2477
+ if (channelControlDraining) throw daemonDrainingError();
2478
+ const manager = channelWorkerManager ?? await channelWorkerManagerStarting;
2479
+ if (channelControlDraining) {
2480
+ await manager?.shutdown().catch(() => void 0);
2481
+ throw daemonDrainingError();
2482
+ }
2483
+ if (!manager) {
2484
+ return { enabled: false, state: "disabled", channels: [] };
2485
+ }
2486
+ try {
2487
+ return await manager.reload();
2488
+ } finally {
2489
+ writeChannelWorkerPidfile();
2490
+ }
2491
+ }, "reloadChannelWorker");
2492
+ const isLiveWorker = /* @__PURE__ */ __name((snapshot) => snapshot.state === "running" || snapshot.state === "starting", "isLiveWorker");
2493
+ let channelWorkerPidfileUsesWorkers = workspaceInputs.length > 1;
2494
+ const writeChannelWorkerPidfile = /* @__PURE__ */ __name(() => {
2495
+ if (runtimeStartupError !== void 0) return;
2496
+ if (!channelPidfileReserved || !channelServicePidfile) return;
2497
+ const snapshots = getChannelWorkerSnapshots();
2498
+ const workers = snapshots.map((snapshot) => ({
2499
+ workspaceId: snapshot.workspaceId,
2500
+ workspaceCwd: snapshot.workspaceCwd,
2501
+ channels: snapshot.channels,
2502
+ // Drop a stale pid (worker exited/failed/stopped) so readers never signal
2503
+ // a dead process — mirrors the pre-4b clear-on-exit behavior.
2504
+ ...isLiveWorker(snapshot) && snapshot.pid !== void 0 ? { workerPid: snapshot.pid } : {}
2505
+ }));
2506
+ const channels = [
2507
+ ...new Set(snapshots.flatMap((snapshot) => snapshot.channels))
2508
+ ];
2509
+ const primary = snapshots.find((snapshot) => snapshot.primary);
2510
+ if (workers.length > 1 || snapshots.some((snapshot) => !snapshot.primary)) {
2511
+ channelWorkerPidfileUsesWorkers = true;
2512
+ }
2513
+ const includeWorkers = channelWorkerPidfileUsesWorkers && workers.length > 0;
2240
2514
  try {
2241
2515
  channelServicePidfile.writeServeServiceInfo({
2242
- channels: snapshot.channels,
2516
+ channels,
2243
2517
  servePid: process.pid,
2244
- ...!options.clearWorkerPid && snapshot.pid !== void 0 ? { workerPid: snapshot.pid } : {}
2518
+ ...primary && isLiveWorker(primary) && primary.pid !== void 0 ? { workerPid: primary.pid } : {},
2519
+ ...includeWorkers ? { workers } : {}
2245
2520
  });
2246
2521
  } catch (err) {
2247
2522
  daemonLog.warn(
@@ -2254,11 +2529,83 @@ async function runQwenServe(optsIn, deps = {}) {
2254
2529
  () => runtimeStartupError
2255
2530
  );
2256
2531
  const shutdownBridges = /* @__PURE__ */ new WeakSet();
2532
+ const disposedRuntimeApps = /* @__PURE__ */ new WeakSet();
2533
+ const stoppedRuntimeAppProducers = /* @__PURE__ */ new WeakSet();
2534
+ const stoppedExtensionReconcilers = /* @__PURE__ */ new WeakSet();
2535
+ const stopExtensionReconciler = /* @__PURE__ */ __name((app2) => {
2536
+ if (!app2 || stoppedExtensionReconcilers.has(app2)) return;
2537
+ stoppedExtensionReconcilers.add(app2);
2538
+ const stopExtensionGenerationReconciler = app2.locals?.["stopExtensionGenerationReconciler"];
2539
+ try {
2540
+ stopExtensionGenerationReconciler?.();
2541
+ } catch (err) {
2542
+ daemonLog.warn(
2543
+ `extension generation reconciler dispose error: ${err instanceof Error ? err.message : String(err)}`
2544
+ );
2545
+ }
2546
+ }, "stopExtensionReconciler");
2547
+ const stopRuntimeAppProducers = /* @__PURE__ */ __name((app2) => {
2548
+ if (!app2 || stoppedRuntimeAppProducers.has(app2)) return;
2549
+ stoppedRuntimeAppProducers.add(app2);
2550
+ const locals = app2.locals;
2551
+ const stopSafely = /* @__PURE__ */ __name((name, stop) => {
2552
+ try {
2553
+ stop?.();
2554
+ } catch (err) {
2555
+ daemonLog.warn(
2556
+ `${name} dispose error: ${err instanceof Error ? err.message : String(err)}`
2557
+ );
2558
+ }
2559
+ }, "stopSafely");
2560
+ stopSafely("scheduled-task keepalive", locals.stopScheduledTaskKeepalive);
2561
+ stopSafely("workspace git state", locals.stopWorkspaceGitState);
2562
+ for (const stop of locals.subSessionStoppers ?? []) {
2563
+ stopSafely("sub-session launcher", stop);
2564
+ }
2565
+ stopExtensionReconciler(app2);
2566
+ }, "stopRuntimeAppProducers");
2567
+ const disposeRuntimeAppResources = /* @__PURE__ */ __name((app2) => {
2568
+ if (!app2 || disposedRuntimeApps.has(app2)) return;
2569
+ disposedRuntimeApps.add(app2);
2570
+ stopRuntimeAppProducers(app2);
2571
+ const deviceFlowRegistry = getDeviceFlowRegistry(app2);
2572
+ if (deviceFlowRegistry) {
2573
+ try {
2574
+ deviceFlowRegistry.dispose();
2575
+ } catch (err) {
2576
+ daemonLog.warn(
2577
+ `device-flow registry dispose error: ${err instanceof Error ? err.message : String(err)}`
2578
+ );
2579
+ }
2580
+ }
2581
+ const acpHandle = app2.locals?.["acpHandle"];
2582
+ if (acpHandle?.dispose) {
2583
+ try {
2584
+ acpHandle.dispose();
2585
+ } catch (err) {
2586
+ daemonLog.warn(
2587
+ `ACP handle dispose error: ${err instanceof Error ? err.message : String(err)}`
2588
+ );
2589
+ }
2590
+ }
2591
+ const rateLimiter = getRateLimiter(app2);
2592
+ if (rateLimiter) {
2593
+ try {
2594
+ rateLimiter.setDraining(true);
2595
+ rateLimiter.dispose();
2596
+ } catch (err) {
2597
+ daemonLog.warn(
2598
+ `rate limiter dispose error: ${err instanceof Error ? err.message : String(err)}`
2599
+ );
2600
+ }
2601
+ }
2602
+ disposeDaemonEventLoopMonitor();
2603
+ }, "disposeRuntimeAppResources");
2257
2604
  const getRuntimeBridgesForCleanup = /* @__PURE__ */ __name(() => {
2258
2605
  const appForCleanup = runtimeApp ?? runtimeAppForCleanup;
2259
2606
  const registry = appForCleanup?.locals?.["workspaceRegistry"];
2260
2607
  const bridges = [
2261
- ...registry ? registry.list().map((runtime) => runtime.bridge) : [],
2608
+ ...registry ? registry.listManaged().map((runtime) => runtime.bridge) : [],
2262
2609
  ...bridgeRef ? [bridgeRef] : [],
2263
2610
  ...internalRuntimeBridgesForCleanup
2264
2611
  ];
@@ -2440,12 +2787,15 @@ async function runQwenServe(optsIn, deps = {}) {
2440
2787
  // `agent_message_chunk._meta` (`usage` + `durationMs`) at the bridge's
2441
2788
  // single session/update fan-in. Increments (not cumulative), so the ring
2442
2789
  // sums tokens per window (token-burn chart) and pools the round-trip times
2443
- // for the LLM-latency percentiles.
2444
- tokenUsage(inputTokens, outputTokens, durationMs) {
2790
+ // for the LLM-latency percentiles. `apiErrors` / `apiRetries` ride the
2791
+ // same frame (per-round increments, 0 when none) and window into the
2792
+ // model-API-health chart.
2793
+ tokenUsage(inputTokens, outputTokens, durationMs, apiErrors, apiRetries) {
2445
2794
  metricsRing.recordTokens(inputTokens, outputTokens);
2446
2795
  if (typeof durationMs === "number") {
2447
2796
  metricsRing.recordLlmDuration(durationMs);
2448
2797
  }
2798
+ metricsRing.recordApiActivity(apiErrors ?? 0, apiRetries ?? 0);
2449
2799
  }
2450
2800
  };
2451
2801
  return telemetry;
@@ -2550,6 +2900,63 @@ async function runQwenServe(optsIn, deps = {}) {
2550
2900
  [...next].sort()
2551
2901
  );
2552
2902
  }), "persistDisabledToolsFn");
2903
+ const persistDisabledSkillsFn = /* @__PURE__ */ __name((workspace, skillName, enabled) => withSettingsLock(workspace, async () => {
2904
+ const fresh = settingsRuntime.settings.loadSettings(workspace);
2905
+ const normalizedName = skillName.trim().toLowerCase();
2906
+ const disabledNames = /* @__PURE__ */ __name((value) => Array.isArray(value) ? value.filter(
2907
+ (entry) => typeof entry === "string"
2908
+ ) : [], "disabledNames");
2909
+ const lockedScopes = [
2910
+ ["system", fresh.system.settings.skills?.disabled],
2911
+ ["user", fresh.user.settings.skills?.disabled],
2912
+ ["systemDefaults", fresh.systemDefaults.settings.skills?.disabled]
2913
+ ];
2914
+ for (const [scope, names] of lockedScopes) {
2915
+ if (disabledNames(names).some(
2916
+ (name) => name.trim().toLowerCase() === normalizedName
2917
+ )) {
2918
+ throw new runtime.WorkspaceSkillNotToggleableError(
2919
+ skillName,
2920
+ "locked",
2921
+ scope
2922
+ );
2923
+ }
2924
+ }
2925
+ const workspaceDisabled = disabledNames(
2926
+ fresh.workspace.settings.skills?.disabled
2927
+ );
2928
+ const next = [];
2929
+ let found = false;
2930
+ let changed = false;
2931
+ for (const name of workspaceDisabled) {
2932
+ if (name.trim().toLowerCase() !== normalizedName) {
2933
+ next.push(name);
2934
+ continue;
2935
+ }
2936
+ if (enabled) {
2937
+ changed = true;
2938
+ continue;
2939
+ }
2940
+ if (!found) {
2941
+ next.push(skillName);
2942
+ found = true;
2943
+ if (name !== skillName) changed = true;
2944
+ } else {
2945
+ changed = true;
2946
+ }
2947
+ }
2948
+ if (!enabled && !found) {
2949
+ next.push(skillName);
2950
+ changed = true;
2951
+ }
2952
+ if (!changed) return { changed: false, disabled: workspaceDisabled };
2953
+ fresh.setValue(
2954
+ WORKSPACE_SETTING_SCOPE,
2955
+ "skills.disabled",
2956
+ next.length > 0 ? next : void 0
2957
+ );
2958
+ return { changed: true, disabled: next };
2959
+ }), "persistDisabledSkillsFn");
2553
2960
  const persistSettingFn = /* @__PURE__ */ __name((workspace, scope, key, value) => withSettingsLock(workspace, async () => {
2554
2961
  const fresh = settingsRuntime.settings.loadSettings(workspace);
2555
2962
  fresh.setValue(scope, key, value);
@@ -2582,7 +2989,7 @@ async function runQwenServe(optsIn, deps = {}) {
2582
2989
  );
2583
2990
  }
2584
2991
  }), "persistSettingsFn");
2585
- const { createSubSessionLauncher } = await import("./create-sub-session-KPRT7ECJ.js");
2992
+ const { createSubSessionLauncher } = await import("./create-sub-session-CBJMQLYP.js");
2586
2993
  const subSessionLauncher = createSubSessionLauncher({
2587
2994
  getBridge: /* @__PURE__ */ __name(() => bridgeRef, "getBridge"),
2588
2995
  boundWorkspace
@@ -2623,14 +3030,18 @@ async function runQwenServe(optsIn, deps = {}) {
2623
3030
  internalRuntimeBridgesForCleanup.push(bridge);
2624
3031
  }
2625
3032
  runtimeBridges.push(bridge);
3033
+ let invalidatePrimaryServeFeaturesCache = /* @__PURE__ */ __name(() => {
3034
+ }, "invalidatePrimaryServeFeaturesCache");
2626
3035
  const workspaceService = runtime.createDaemonWorkspaceService({
2627
3036
  boundWorkspace,
2628
3037
  contextFilename: contextFilenameForInit ?? "QWEN.md",
2629
3038
  statusProvider,
2630
3039
  workspaceProvidersStatusProvider,
2631
3040
  workspaceSkillsStatusProvider,
3041
+ voiceEnv: runtimeEffectiveEnv,
2632
3042
  isChannelLive: /* @__PURE__ */ __name(() => bridge.isChannelLive(), "isChannelLive"),
2633
3043
  persistDisabledTools: persistDisabledToolsFn,
3044
+ persistDisabledSkills: persistDisabledSkillsFn,
2634
3045
  persistSetting: persistSettingFn,
2635
3046
  persistSettings: persistSettingsFn,
2636
3047
  preheatAcpChild: /* @__PURE__ */ __name(() => bridge.preheat(), "preheatAcpChild"),
@@ -2696,7 +3107,12 @@ async function runQwenServe(optsIn, deps = {}) {
2696
3107
  queryWorkspaceStatus: /* @__PURE__ */ __name((method, idle) => bridge.queryWorkspaceStatus(method, idle), "queryWorkspaceStatus"),
2697
3108
  invokeWorkspaceCommand: /* @__PURE__ */ __name((method, params, invokeOpts) => bridge.invokeWorkspaceCommand(method, params, invokeOpts), "invokeWorkspaceCommand"),
2698
3109
  refreshExtensionsForAllSessions: /* @__PURE__ */ __name(() => bridge.refreshExtensionsForAllSessions(), "refreshExtensionsForAllSessions"),
2699
- publishWorkspaceEvent: /* @__PURE__ */ __name((event) => bridge.publishWorkspaceEvent(event), "publishWorkspaceEvent")
3110
+ publishWorkspaceEvent: /* @__PURE__ */ __name((event) => {
3111
+ if (event.type === "settings_changed" || event.type === "settings_reloaded") {
3112
+ invalidatePrimaryServeFeaturesCache();
3113
+ }
3114
+ bridge.publishWorkspaceEvent(event);
3115
+ }, "publishWorkspaceEvent")
2700
3116
  });
2701
3117
  const workspaceRuntimes = [
2702
3118
  {
@@ -2704,6 +3120,8 @@ async function runQwenServe(optsIn, deps = {}) {
2704
3120
  workspaceCwd: boundWorkspace,
2705
3121
  primary: true,
2706
3122
  trusted: trustedWorkspace,
3123
+ removable: false,
3124
+ registrationIds: workspaceInputs[0]?.registrationIds ?? [],
2707
3125
  env: primaryRuntimeEnv,
2708
3126
  bridge,
2709
3127
  workspaceService,
@@ -2745,6 +3163,12 @@ async function runQwenServe(optsIn, deps = {}) {
2745
3163
  };
2746
3164
  }, "createRuntimeEnvMetadata");
2747
3165
  const subSessionStoppers = [];
3166
+ const subSessionStoppersByWorkspace = /* @__PURE__ */ new Map();
3167
+ const runtimeCleanupPromises = /* @__PURE__ */ new WeakMap();
3168
+ const removeArrayValue = /* @__PURE__ */ __name((values, value) => {
3169
+ const index = values.indexOf(value);
3170
+ if (index >= 0) values.splice(index, 1);
3171
+ }, "removeArrayValue");
2748
3172
  for (const workspaceInput of workspaceInputs.slice(1)) {
2749
3173
  let secondarySettings;
2750
3174
  try {
@@ -2838,6 +3262,10 @@ async function runQwenServe(optsIn, deps = {}) {
2838
3262
  runtimeBridges.push(secondaryBridge);
2839
3263
  internalRuntimeBridgesForCleanup.push(secondaryBridge);
2840
3264
  subSessionStoppers.push(secondarySubSessionLauncher.stop);
3265
+ subSessionStoppersByWorkspace.set(
3266
+ workspaceInput.cwd,
3267
+ secondarySubSessionLauncher.stop
3268
+ );
2841
3269
  const secondaryWorkspaceService = runtime.createDaemonWorkspaceService({
2842
3270
  boundWorkspace: workspaceInput.cwd,
2843
3271
  contextFilename: contextFilenameForInit ?? "QWEN.md",
@@ -2846,9 +3274,12 @@ async function runQwenServe(optsIn, deps = {}) {
2846
3274
  env: secondaryEnv.effectiveEnv
2847
3275
  }),
2848
3276
  workspaceSkillsStatusProvider: runtime.createWorkspaceSkillsStatusProvider(),
3277
+ voiceEnv: secondaryEnv.effectiveEnv,
3278
+ voiceSettingsScope: WORKSPACE_SETTING_SCOPE,
2849
3279
  isChannelLive: /* @__PURE__ */ __name(() => secondaryBridge.isChannelLive(), "isChannelLive"),
2850
3280
  preheatAcpChild: /* @__PURE__ */ __name(() => secondaryBridge.preheat(), "preheatAcpChild"),
2851
3281
  persistDisabledTools: persistDisabledToolsFn,
3282
+ persistDisabledSkills: persistDisabledSkillsFn,
2852
3283
  persistSetting: persistSettingFn,
2853
3284
  persistSettings: persistSettingsFn,
2854
3285
  reloadDaemonEnv: /* @__PURE__ */ __name((workspace) => withSettingsLock(workspace, async () => {
@@ -2906,6 +3337,8 @@ async function runQwenServe(optsIn, deps = {}) {
2906
3337
  workspaceCwd: workspaceInput.cwd,
2907
3338
  primary: false,
2908
3339
  trusted: secondaryTrusted,
3340
+ removable: workspaceInput.removable,
3341
+ registrationIds: workspaceInput.registrationIds,
2909
3342
  env: secondaryEnv.metadata,
2910
3343
  bridge: secondaryBridge,
2911
3344
  workspaceService: secondaryWorkspaceService,
@@ -2916,6 +3349,7 @@ async function runQwenServe(optsIn, deps = {}) {
2916
3349
  const workspaceRegistry = runtime.createWorkspaceRegistry(workspaceRuntimes, {
2917
3350
  sessionOwnerIndex
2918
3351
  });
3352
+ const workspaceVoiceCoordinator = new WorkspaceVoiceCoordinator();
2919
3353
  core.registerDaemonGaugeCallbacks({
2920
3354
  sessionCount: /* @__PURE__ */ __name(() => workspaceRegistry.list().reduce((sum, item) => sum + item.bridge.sessionCount, 0), "sessionCount"),
2921
3355
  sseCount: /* @__PURE__ */ __name(() => runtime.getActiveSseCount(), "sseCount"),
@@ -2944,7 +3378,7 @@ async function runQwenServe(optsIn, deps = {}) {
2944
3378
  prevCpu = cpu;
2945
3379
  prevCpuAt = nowMs;
2946
3380
  }
2947
- const acp = app2.locals?.["acpHandle"]?.registry.getSnapshot();
3381
+ const acp = app2.locals?.["acpHandle"]?.getSnapshot();
2948
3382
  const hits = getRateLimiter(app2)?.getHitCounts();
2949
3383
  const rejectedTotal = hits ? hits.prompt + hits.mutation + hits.read : 0;
2950
3384
  const rateLimitRejected = prevRateRejected === void 0 ? 0 : Math.max(0, rejectedTotal - prevRateRejected);
@@ -3049,111 +3483,139 @@ async function runQwenServe(optsIn, deps = {}) {
3049
3483
  getBridge: /* @__PURE__ */ __name(() => wsBridgeRef, "getBridge"),
3050
3484
  boundWorkspace: cwd
3051
3485
  });
3052
- const wsBridge = runtime.createAcpSessionBridge({
3053
- clientMcpSender: wsClientMcpRegistry.lookup,
3054
- onCreateSubSession: wsSubSessionLauncher.launch,
3055
- maxSessions: opts.maxSessions,
3056
- freshSessionAdmission: totalSessionAdmission.admit,
3057
- sessionLifecycle: sessionOwnerIndex.handleBridgeSessionLifecycle,
3058
- ...opts.maxPendingPromptsPerSession !== void 0 ? { maxPendingPromptsPerSession: opts.maxPendingPromptsPerSession } : {},
3059
- ...opts.eventRingSize !== void 0 ? { eventRingSize: opts.eventRingSize } : {},
3060
- ...opts.compactedReplayMaxBytes !== void 0 ? { compactedReplayMaxBytes: opts.compactedReplayMaxBytes } : {},
3061
- ...opts.channelIdleTimeoutMs !== void 0 ? { channelIdleTimeoutMs: opts.channelIdleTimeoutMs } : {},
3062
- ...opts.sessionReapIntervalMs !== void 0 ? { sessionReapIntervalMs: opts.sessionReapIntervalMs } : {},
3063
- ...opts.sessionIdleTimeoutMs !== void 0 ? { sessionIdleTimeoutMs: opts.sessionIdleTimeoutMs } : {},
3064
- ...opts.permissionResponseTimeoutMs !== void 0 ? { permissionResponseTimeoutMs: opts.permissionResponseTimeoutMs } : {},
3065
- boundWorkspace: cwd,
3066
- sessionShellCommandEnabled,
3067
- childEnvOverrides,
3068
- channelFactory: wsChannelFactory,
3069
- onDiagnosticLine: diagnosticSink,
3070
- telemetry: createRuntimeBridgeTelemetry(wsHash),
3071
- ...permissionPolicy !== void 0 ? { permissionPolicy } : {},
3072
- ...permissionConsensusQuorum !== void 0 ? { permissionConsensusQuorum } : {},
3073
- permissionAudit: permissionAuditPublisher,
3074
- statusProvider: runtime.createDaemonStatusProvider({
3075
- env: wsEnv.effectiveEnv
3076
- }),
3077
- fileSystem: createBridgeFileSystemAdapter(wsFsFactory),
3078
- persistApprovalMode: /* @__PURE__ */ __name((workspace, mode) => withSettingsLock(workspace, async () => {
3079
- const fresh = settingsRuntime.settings.loadSettings(workspace);
3080
- fresh.setValue(WORKSPACE_SETTING_SCOPE, "tools.approvalMode", mode);
3081
- }), "persistApprovalMode")
3082
- });
3486
+ let wsBridge;
3487
+ try {
3488
+ wsBridge = runtime.createAcpSessionBridge({
3489
+ clientMcpSender: wsClientMcpRegistry.lookup,
3490
+ onCreateSubSession: wsSubSessionLauncher.launch,
3491
+ maxSessions: opts.maxSessions,
3492
+ freshSessionAdmission: totalSessionAdmission.admit,
3493
+ sessionLifecycle: sessionOwnerIndex.handleBridgeSessionLifecycle,
3494
+ ...opts.maxPendingPromptsPerSession !== void 0 ? { maxPendingPromptsPerSession: opts.maxPendingPromptsPerSession } : {},
3495
+ ...opts.eventRingSize !== void 0 ? { eventRingSize: opts.eventRingSize } : {},
3496
+ ...opts.compactedReplayMaxBytes !== void 0 ? { compactedReplayMaxBytes: opts.compactedReplayMaxBytes } : {},
3497
+ ...opts.channelIdleTimeoutMs !== void 0 ? { channelIdleTimeoutMs: opts.channelIdleTimeoutMs } : {},
3498
+ ...opts.sessionReapIntervalMs !== void 0 ? { sessionReapIntervalMs: opts.sessionReapIntervalMs } : {},
3499
+ ...opts.sessionIdleTimeoutMs !== void 0 ? { sessionIdleTimeoutMs: opts.sessionIdleTimeoutMs } : {},
3500
+ ...opts.permissionResponseTimeoutMs !== void 0 ? { permissionResponseTimeoutMs: opts.permissionResponseTimeoutMs } : {},
3501
+ boundWorkspace: cwd,
3502
+ sessionShellCommandEnabled,
3503
+ childEnvOverrides,
3504
+ channelFactory: wsChannelFactory,
3505
+ onDiagnosticLine: diagnosticSink,
3506
+ telemetry: createRuntimeBridgeTelemetry(wsHash),
3507
+ ...permissionPolicy !== void 0 ? { permissionPolicy } : {},
3508
+ ...permissionConsensusQuorum !== void 0 ? { permissionConsensusQuorum } : {},
3509
+ permissionAudit: permissionAuditPublisher,
3510
+ statusProvider: runtime.createDaemonStatusProvider({
3511
+ env: wsEnv.effectiveEnv
3512
+ }),
3513
+ fileSystem: createBridgeFileSystemAdapter(wsFsFactory),
3514
+ persistApprovalMode: /* @__PURE__ */ __name((workspace, mode) => withSettingsLock(workspace, async () => {
3515
+ const fresh = settingsRuntime.settings.loadSettings(workspace);
3516
+ fresh.setValue(
3517
+ WORKSPACE_SETTING_SCOPE,
3518
+ "tools.approvalMode",
3519
+ mode
3520
+ );
3521
+ }), "persistApprovalMode")
3522
+ });
3523
+ } catch (err) {
3524
+ wsSubSessionLauncher.stop();
3525
+ throw err;
3526
+ }
3083
3527
  wsBridgeRef = wsBridge;
3084
- const wsService = runtime.createDaemonWorkspaceService({
3085
- boundWorkspace: cwd,
3086
- contextFilename: contextFilenameForInit ?? "QWEN.md",
3087
- statusProvider: runtime.createDaemonStatusProvider({
3088
- env: wsEnv.effectiveEnv
3089
- }),
3090
- workspaceProvidersStatusProvider: runtime.createWorkspaceProvidersStatusProvider({
3091
- env: wsEnv.effectiveEnv
3092
- }),
3093
- workspaceSkillsStatusProvider: runtime.createWorkspaceSkillsStatusProvider(),
3094
- isChannelLive: /* @__PURE__ */ __name(() => wsBridge.isChannelLive(), "isChannelLive"),
3095
- preheatAcpChild: /* @__PURE__ */ __name(() => wsBridge.preheat(), "preheatAcpChild"),
3096
- persistDisabledTools: persistDisabledToolsFn,
3097
- persistSetting: persistSettingFn,
3098
- persistSettings: persistSettingsFn,
3099
- reloadDaemonEnv: /* @__PURE__ */ __name((workspace) => withSettingsLock(workspace, async () => {
3100
- const fresh = settingsRuntime.settings.loadSettings(workspace, {
3101
- skipLoadEnvironment: true
3102
- });
3103
- const result = settingsRuntime.settings.reloadEnvironment(
3104
- fresh.merged,
3105
- workspace
3106
- );
3107
- try {
3108
- const refreshedRuntimeEnv = settingsRuntime.environment.buildRuntimeEnvironment(
3528
+ let wsService;
3529
+ try {
3530
+ wsService = runtime.createDaemonWorkspaceService({
3531
+ boundWorkspace: cwd,
3532
+ contextFilename: contextFilenameForInit ?? "QWEN.md",
3533
+ statusProvider: runtime.createDaemonStatusProvider({
3534
+ env: wsEnv.effectiveEnv
3535
+ }),
3536
+ workspaceProvidersStatusProvider: runtime.createWorkspaceProvidersStatusProvider({
3537
+ env: wsEnv.effectiveEnv
3538
+ }),
3539
+ workspaceSkillsStatusProvider: runtime.createWorkspaceSkillsStatusProvider(),
3540
+ voiceEnv: wsEnv.effectiveEnv,
3541
+ voiceSettingsScope: WORKSPACE_SETTING_SCOPE,
3542
+ isChannelLive: /* @__PURE__ */ __name(() => wsBridge.isChannelLive(), "isChannelLive"),
3543
+ preheatAcpChild: /* @__PURE__ */ __name(() => wsBridge.preheat(), "preheatAcpChild"),
3544
+ persistDisabledTools: persistDisabledToolsFn,
3545
+ persistDisabledSkills: persistDisabledSkillsFn,
3546
+ persistSetting: persistSettingFn,
3547
+ persistSettings: persistSettingsFn,
3548
+ reloadDaemonEnv: /* @__PURE__ */ __name((workspace) => withSettingsLock(workspace, async () => {
3549
+ const fresh = settingsRuntime.settings.loadSettings(workspace, {
3550
+ skipLoadEnvironment: true
3551
+ });
3552
+ const result = settingsRuntime.settings.reloadEnvironment(
3109
3553
  fresh.merged,
3110
- workspace,
3111
- daemonRuntimeBaseEnv
3554
+ workspace
3112
3555
  );
3113
- logRuntimeEnvFileReadFailures(workspace, refreshedRuntimeEnv);
3114
- wsEnv.replace(refreshedRuntimeEnv.effectiveEnv);
3115
- wsEnv.metadata.envFileReadFailed = refreshedRuntimeEnv.envFileReadFailed;
3116
- wsEnv.metadata.envFileReadFailures.splice(
3117
- 0,
3118
- wsEnv.metadata.envFileReadFailures.length,
3119
- ...refreshedRuntimeEnv.envFileReadFailures
3120
- );
3121
- wsEnv.metadata.overlayKeys.splice(
3122
- 0,
3123
- wsEnv.metadata.overlayKeys.length,
3124
- ...refreshedRuntimeEnv.overlayKeys
3125
- );
3126
- wsEnv.metadata.envFilePaths.splice(
3127
- 0,
3128
- wsEnv.metadata.envFilePaths.length,
3129
- ...refreshedRuntimeEnv.envFilePaths
3130
- );
3131
- delete wsEnv.metadata.fallbackReason;
3132
- } catch (err) {
3133
- wsEnv.metadata.fallbackReason = err instanceof Error ? err.message : String(err);
3134
- daemonLog.warn(
3135
- "failed to rebuild dynamic runtime env snapshot after daemon env reload; preserving previous runtime env",
3136
- {
3556
+ try {
3557
+ const refreshedRuntimeEnv = settingsRuntime.environment.buildRuntimeEnvironment(
3558
+ fresh.merged,
3137
3559
  workspace,
3138
- error: wsEnv.metadata.fallbackReason
3139
- }
3140
- );
3560
+ daemonRuntimeBaseEnv
3561
+ );
3562
+ logRuntimeEnvFileReadFailures(workspace, refreshedRuntimeEnv);
3563
+ wsEnv.replace(refreshedRuntimeEnv.effectiveEnv);
3564
+ wsEnv.metadata.envFileReadFailed = refreshedRuntimeEnv.envFileReadFailed;
3565
+ wsEnv.metadata.envFileReadFailures.splice(
3566
+ 0,
3567
+ wsEnv.metadata.envFileReadFailures.length,
3568
+ ...refreshedRuntimeEnv.envFileReadFailures
3569
+ );
3570
+ wsEnv.metadata.overlayKeys.splice(
3571
+ 0,
3572
+ wsEnv.metadata.overlayKeys.length,
3573
+ ...refreshedRuntimeEnv.overlayKeys
3574
+ );
3575
+ wsEnv.metadata.envFilePaths.splice(
3576
+ 0,
3577
+ wsEnv.metadata.envFilePaths.length,
3578
+ ...refreshedRuntimeEnv.envFilePaths
3579
+ );
3580
+ delete wsEnv.metadata.fallbackReason;
3581
+ } catch (err) {
3582
+ wsEnv.metadata.fallbackReason = err instanceof Error ? err.message : String(err);
3583
+ daemonLog.warn(
3584
+ "failed to rebuild dynamic runtime env snapshot after daemon env reload; preserving previous runtime env",
3585
+ {
3586
+ workspace,
3587
+ error: wsEnv.metadata.fallbackReason
3588
+ }
3589
+ );
3590
+ }
3591
+ return result;
3592
+ }), "reloadDaemonEnv"),
3593
+ queryWorkspaceStatus: /* @__PURE__ */ __name((method, idle) => wsBridge.queryWorkspaceStatus(method, idle), "queryWorkspaceStatus"),
3594
+ invokeWorkspaceCommand: /* @__PURE__ */ __name((method, params, invokeOpts) => wsBridge.invokeWorkspaceCommand(method, params, invokeOpts), "invokeWorkspaceCommand"),
3595
+ refreshExtensionsForAllSessions: /* @__PURE__ */ __name(() => wsBridge.refreshExtensionsForAllSessions(), "refreshExtensionsForAllSessions"),
3596
+ publishWorkspaceEvent: /* @__PURE__ */ __name((event) => wsBridge.publishWorkspaceEvent(event), "publishWorkspaceEvent")
3597
+ });
3598
+ } catch (err) {
3599
+ wsSubSessionLauncher.stop();
3600
+ await wsBridge.shutdown().catch(() => {
3601
+ try {
3602
+ wsBridge.killAllSync();
3603
+ } catch {
3141
3604
  }
3142
- return result;
3143
- }), "reloadDaemonEnv"),
3144
- queryWorkspaceStatus: /* @__PURE__ */ __name((method, idle) => wsBridge.queryWorkspaceStatus(method, idle), "queryWorkspaceStatus"),
3145
- invokeWorkspaceCommand: /* @__PURE__ */ __name((method, params, invokeOpts) => wsBridge.invokeWorkspaceCommand(method, params, invokeOpts), "invokeWorkspaceCommand"),
3146
- refreshExtensionsForAllSessions: /* @__PURE__ */ __name(() => wsBridge.refreshExtensionsForAllSessions(), "refreshExtensionsForAllSessions"),
3147
- publishWorkspaceEvent: /* @__PURE__ */ __name((event) => wsBridge.publishWorkspaceEvent(event), "publishWorkspaceEvent")
3148
- });
3605
+ });
3606
+ throw err;
3607
+ }
3149
3608
  runtimeBridges.push(wsBridge);
3150
3609
  internalRuntimeBridgesForCleanup.push(wsBridge);
3151
3610
  subSessionStoppers.push(wsSubSessionLauncher.stop);
3611
+ subSessionStoppersByWorkspace.set(cwd, wsSubSessionLauncher.stop);
3152
3612
  return {
3153
3613
  workspaceId: wsHash,
3154
3614
  workspaceCwd: cwd,
3155
3615
  primary: false,
3156
3616
  trusted,
3617
+ removable: true,
3618
+ registrationIds: [],
3157
3619
  env: wsEnv.metadata,
3158
3620
  bridge: wsBridge,
3159
3621
  workspaceService: wsService,
@@ -3161,9 +3623,135 @@ async function runQwenServe(optsIn, deps = {}) {
3161
3623
  clientMcpSenderRegistry: wsClientMcpRegistry
3162
3624
  };
3163
3625
  }, "createDynamicWorkspaceRuntime");
3626
+ const workspaceRuntimeRemoval = {
3627
+ async runtimeAdded(runtimeAdded) {
3628
+ const app3 = runtimeApp ?? runtimeAppForCleanup;
3629
+ const startScheduledTaskKeepaliveForWorkspace = app3?.locals?.["startScheduledTaskKeepaliveForWorkspace"];
3630
+ startScheduledTaskKeepaliveForWorkspace?.(runtimeAdded);
3631
+ if (!channelWorkerManager) return;
3632
+ try {
3633
+ await channelWorkerManager.restoreWorkspace(
3634
+ runtimeAdded.workspaceCwd
3635
+ );
3636
+ await channelWorkerManager.refreshWorkspaces();
3637
+ } catch (err) {
3638
+ daemonLog.error(
3639
+ "workspace channel worker startup error",
3640
+ err instanceof Error ? err : null
3641
+ );
3642
+ } finally {
3643
+ writeChannelWorkerPidfile();
3644
+ }
3645
+ },
3646
+ beginDrain(runtimeToDrain) {
3647
+ totalSessionAdmission.beginWorkspaceDrain(runtimeToDrain.workspaceCwd);
3648
+ channelWorkerManager?.beginWorkspaceDrain(runtimeToDrain.workspaceCwd);
3649
+ workspaceVoiceCoordinator.beginWorkspaceDrain(runtimeToDrain);
3650
+ },
3651
+ cancelDrain(runtimeToDrain) {
3652
+ channelWorkerManager?.cancelWorkspaceDrain(runtimeToDrain.workspaceCwd);
3653
+ totalSessionAdmission.cancelWorkspaceDrain(runtimeToDrain.workspaceCwd);
3654
+ workspaceVoiceCoordinator.cancelWorkspaceDrain(runtimeToDrain);
3655
+ },
3656
+ completeDrain(runtimeToDrain) {
3657
+ totalSessionAdmission.completeWorkspaceDrain(
3658
+ runtimeToDrain.workspaceCwd
3659
+ );
3660
+ workspaceVoiceCoordinator.completeWorkspaceDrain(runtimeToDrain);
3661
+ },
3662
+ getActivity(runtimeToDrain) {
3663
+ return {
3664
+ pendingSessionStarts: totalSessionAdmission.snapshotForWorkspace(
3665
+ runtimeToDrain.workspaceCwd
3666
+ ).inFlight,
3667
+ channelWorkers: channelWorkerManager?.workspaceActivity(
3668
+ runtimeToDrain.workspaceCwd
3669
+ ) ?? 0,
3670
+ voiceSessions: workspaceVoiceCoordinator.getWorkspaceActivity(runtimeToDrain)
3671
+ };
3672
+ },
3673
+ disposeRuntime(runtimeToDrain, reason = "workspace_removed") {
3674
+ const existing = runtimeCleanupPromises.get(runtimeToDrain);
3675
+ if (existing) return existing;
3676
+ const cleanup = (async () => {
3677
+ await workspaceVoiceCoordinator.disposeRuntime(
3678
+ runtimeToDrain,
3679
+ reason
3680
+ );
3681
+ const stopSubSessions = subSessionStoppersByWorkspace.get(
3682
+ runtimeToDrain.workspaceCwd
3683
+ );
3684
+ try {
3685
+ stopSubSessions?.();
3686
+ } catch {
3687
+ }
3688
+ if (reason === "workspace_removed" && channelWorkerManager) {
3689
+ await channelWorkerManager.removeWorkspace(runtimeToDrain.workspaceCwd).catch((err) => {
3690
+ daemonLog.error(
3691
+ "workspace channel worker cleanup error",
3692
+ err instanceof Error ? err : null
3693
+ );
3694
+ });
3695
+ try {
3696
+ await channelWorkerManager.refreshWorkspaces();
3697
+ } catch (err) {
3698
+ channelWorkspaceGroups = (channelWorkspaceGroups ?? []).filter(
3699
+ (group) => group.workspaceCwd !== runtimeToDrain.workspaceCwd
3700
+ );
3701
+ channelWebhookConfigVersion += 1;
3702
+ refreshChannelWebhookConfigs?.();
3703
+ daemonLog.error(
3704
+ "workspace channel worker topology refresh error",
3705
+ err instanceof Error ? err : null
3706
+ );
3707
+ }
3708
+ writeChannelWorkerPidfile();
3709
+ }
3710
+ if (reason === "workspace_removed") {
3711
+ const app3 = runtimeApp ?? runtimeAppForCleanup;
3712
+ const stopWorkspaceGitStateForWorkspace = app3?.locals?.["stopWorkspaceGitStateForWorkspace"];
3713
+ const stopScheduledTaskKeepaliveForWorkspace = app3?.locals?.["stopScheduledTaskKeepaliveForWorkspace"];
3714
+ stopWorkspaceGitStateForWorkspace?.(runtimeToDrain.workspaceCwd);
3715
+ stopScheduledTaskKeepaliveForWorkspace?.(
3716
+ runtimeToDrain.workspaceCwd
3717
+ );
3718
+ }
3719
+ let bridgeStopped = false;
3720
+ try {
3721
+ if (!shutdownBridges.has(runtimeToDrain.bridge)) {
3722
+ await runtimeToDrain.bridge.shutdown({ reason });
3723
+ }
3724
+ bridgeStopped = true;
3725
+ } finally {
3726
+ if (bridgeStopped || reason === "workspace_removed") {
3727
+ subSessionStoppersByWorkspace.delete(runtimeToDrain.workspaceCwd);
3728
+ if (stopSubSessions) {
3729
+ removeArrayValue(subSessionStoppers, stopSubSessions);
3730
+ }
3731
+ removeArrayValue(runtimeBridges, runtimeToDrain.bridge);
3732
+ removeArrayValue(
3733
+ internalRuntimeBridgesForCleanup,
3734
+ runtimeToDrain.bridge
3735
+ );
3736
+ shutdownBridges.add(runtimeToDrain.bridge);
3737
+ }
3738
+ }
3739
+ })();
3740
+ runtimeCleanupPromises.set(runtimeToDrain, cleanup);
3741
+ void cleanup.catch(() => {
3742
+ if (reason === "daemon_shutdown" && runtimeCleanupPromises.get(runtimeToDrain) === cleanup) {
3743
+ runtimeCleanupPromises.delete(runtimeToDrain);
3744
+ }
3745
+ });
3746
+ return cleanup;
3747
+ }
3748
+ };
3164
3749
  const app2 = runtime.createServeApp(opts, () => actualPort, {
3165
3750
  workspaceRegistry,
3166
3751
  createWorkspaceRuntime: createDynamicWorkspaceRuntime,
3752
+ workspaceRegistrationStore,
3753
+ workspaceRuntimeRemoval,
3754
+ voiceCoordinator: workspaceVoiceCoordinator,
3167
3755
  bridge,
3168
3756
  webShellDir,
3169
3757
  boundWorkspace,
@@ -3178,11 +3766,27 @@ async function runQwenServe(optsIn, deps = {}) {
3178
3766
  primaryRuntimeEnv,
3179
3767
  daemonLog,
3180
3768
  getChannelWorkerSnapshot,
3181
- enqueueChannelWebhookTask: /* @__PURE__ */ __name((task) => channelWorker.enqueueWebhookTask(task), "enqueueChannelWebhookTask"),
3182
- // Gate both the `channel_reload` capability and the reload route on the
3183
- // presence of this dep, so it is advertised only when a channel worker
3184
- // exists to reload.
3185
- ...opts.channelSelection ? { reloadChannelWorker } : {},
3769
+ getChannelWorkerSnapshots,
3770
+ getChannelWorkerControl,
3771
+ isChannelControlDraining: /* @__PURE__ */ __name(() => channelControlDraining, "isChannelControlDraining"),
3772
+ isChannelControlInitializing: /* @__PURE__ */ __name(() => channelWorkerManagerStarting !== void 0, "isChannelControlInitializing"),
3773
+ setChannelWorkerSelection,
3774
+ stopChannelWorker,
3775
+ getChannelWebhookConfigSources,
3776
+ getChannelWebhookConfigVersion: /* @__PURE__ */ __name(() => channelWebhookConfigVersion, "getChannelWebhookConfigVersion"),
3777
+ registerChannelWebhookConfigRefresh: /* @__PURE__ */ __name((refresh) => {
3778
+ refreshChannelWebhookConfigs = refresh;
3779
+ }, "registerChannelWebhookConfigRefresh"),
3780
+ enqueueChannelWebhookTask: /* @__PURE__ */ __name(async (task) => {
3781
+ if (!channelWorkerManager) {
3782
+ throw new ChannelWebhookEnqueueError(
3783
+ "channel_worker_unavailable",
3784
+ "Channel worker is not running."
3785
+ );
3786
+ }
3787
+ return channelWorkerManager.enqueueWebhookTask(task);
3788
+ }, "enqueueChannelWebhookTask"),
3789
+ reloadChannelWorker,
3186
3790
  getPerfSnapshot: /* @__PURE__ */ __name(() => ({
3187
3791
  eventLoop: currentDaemonEventLoopMonitor.snapshot(),
3188
3792
  promptQueueWait: {
@@ -3204,6 +3808,7 @@ async function runQwenServe(optsIn, deps = {}) {
3204
3808
  // so the WS provider and the child-answering bridge share one sender map.
3205
3809
  clientMcpSenderRegistry,
3206
3810
  persistDisabledTools: persistDisabledToolsFn,
3811
+ persistDisabledSkills: persistDisabledSkillsFn,
3207
3812
  persistSetting: persistSettingFn,
3208
3813
  persistSettings: persistSettingsFn,
3209
3814
  sessionArtifactsPersistenceAvailable: sessionArtifactsPersistenceAvailableFromSettings(
@@ -3247,16 +3852,18 @@ async function runQwenServe(optsIn, deps = {}) {
3247
3852
  }
3248
3853
  ), "installAuthProvider")
3249
3854
  });
3855
+ invalidatePrimaryServeFeaturesCache = app2.locals.invalidateServeFeaturesCache ?? invalidatePrimaryServeFeaturesCache;
3250
3856
  app2.locals.subSessionStoppers = subSessionStoppers;
3251
3857
  subSessionStoppers.push(subSessionLauncher.stop);
3858
+ subSessionStoppersByWorkspace.set(boundWorkspace, subSessionLauncher.stop);
3252
3859
  return { app: app2, bridge };
3253
3860
  }, "buildRuntime");
3254
3861
  if (deps.bridge) {
3255
3862
  const runtime = await buildRuntime();
3256
3863
  runtimeAppForCleanup = runtime.app;
3257
- runtimeApp = runtime.app;
3258
3864
  bridgeRef = runtime.bridge;
3259
3865
  if (!opts.channelSelection) {
3866
+ runtimeApp = runtime.app;
3260
3867
  runtimeStartupSettled = true;
3261
3868
  markRuntimeReady();
3262
3869
  }
@@ -3275,10 +3882,11 @@ async function runQwenServe(optsIn, deps = {}) {
3275
3882
  multiWorkspaceCapabilitiesRequireRuntime: workspaceInputs.length > 1,
3276
3883
  getRuntimeError: /* @__PURE__ */ __name(() => runtimeStartupError, "getRuntimeError"),
3277
3884
  getChannelWorkerSnapshot,
3885
+ getChannelWorkerSnapshots,
3278
3886
  onHealthServed: deferRuntimeUntilFirstHealth ? () => startRuntimeAfterHealth?.() : void 0
3279
3887
  });
3280
3888
  const deferredChannelWebhookAuth = deferRuntimeUntilFirstHealth ? createDeferredChannelWebhookAuth(
3281
- boundWorkspace,
3889
+ resolveChannelWebhookConfigSource,
3282
3890
  await loadChannelWebhookConfigRuntime(),
3283
3891
  daemonLog
3284
3892
  ) : void 0;
@@ -3304,7 +3912,70 @@ async function runQwenServe(optsIn, deps = {}) {
3304
3912
  `Invalid maxConnections: ${opts.maxConnections}. Must be >= 0 (0 / Infinity = unlimited).`
3305
3913
  );
3306
3914
  }
3307
- reserveChannelServicePidfile();
3915
+ const channelValidationSettingsRuntime = opts.channelSelection ? await loadSettingsRuntimeModules() : void 0;
3916
+ const resolveChannelWorkspaceGroupsAtListen = /* @__PURE__ */ __name(() => {
3917
+ if (!opts.channelSelection || !channelValidationSettingsRuntime || !channelRuntime) {
3918
+ return void 0;
3919
+ }
3920
+ const settingsByWorkspace = /* @__PURE__ */ new Map();
3921
+ if (workspaceInputs.length === 1) {
3922
+ const workspace = workspaceInputs[0];
3923
+ const settings = channelValidationSettingsRuntime.settings.loadSettings(
3924
+ workspace.cwd
3925
+ );
3926
+ channelWebhookEnvByWorkspace.set(
3927
+ workspace.cwd,
3928
+ channelValidationSettingsRuntime.environment.buildRuntimeEnvironment(
3929
+ settings.merged,
3930
+ workspace.cwd,
3931
+ daemonRuntimeBaseEnv
3932
+ ).effectiveEnv
3933
+ );
3934
+ return void 0;
3935
+ }
3936
+ const workspaces = workspaceInputs.map((workspace, index) => {
3937
+ const settings = channelValidationSettingsRuntime.settings.loadSettings(
3938
+ workspace.cwd
3939
+ );
3940
+ settingsByWorkspace.set(workspace.cwd, settings);
3941
+ channelWebhookEnvByWorkspace.set(
3942
+ workspace.cwd,
3943
+ channelValidationSettingsRuntime.environment.buildRuntimeEnvironment(
3944
+ settings.merged,
3945
+ workspace.cwd,
3946
+ daemonRuntimeBaseEnv
3947
+ ).effectiveEnv
3948
+ );
3949
+ const trusted = index === 0 && deps.trustedWorkspace !== void 0 ? deps.trustedWorkspace : channelValidationSettingsRuntime.trustedFolders.getWorkspaceTrustStatus(
3950
+ settings.merged,
3951
+ workspace.cwd
3952
+ ).effective.state === "trusted";
3953
+ return {
3954
+ workspaceCwd: workspace.cwd,
3955
+ primary: index === 0,
3956
+ trusted
3957
+ };
3958
+ });
3959
+ const grouping = resolveChannelWorkspaceGroups({
3960
+ workspaces,
3961
+ selection: opts.channelSelection,
3962
+ loadChannelsConfig: /* @__PURE__ */ __name((cwd) => {
3963
+ const settings = settingsByWorkspace.get(cwd);
3964
+ if (!settings) return {};
3965
+ return channelRuntime.loadChannelsConfig(cwd, settings);
3966
+ }, "loadChannelsConfig")
3967
+ });
3968
+ if (!grouping.ok) {
3969
+ throw Object.assign(new Error(grouping.error.message), {
3970
+ code: grouping.error.code,
3971
+ ...grouping.error.channel ? { channel: grouping.error.channel } : {}
3972
+ });
3973
+ }
3974
+ return grouping.groups;
3975
+ }, "resolveChannelWorkspaceGroupsAtListen");
3976
+ if (opts.channelSelection) {
3977
+ reserveChannelServicePidfile(opts.channelSelection);
3978
+ }
3308
3979
  return await new Promise((resolve, reject) => {
3309
3980
  const onListening = /* @__PURE__ */ __name(() => {
3310
3981
  startup.listenerReadyAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -3323,46 +3994,14 @@ async function runQwenServe(optsIn, deps = {}) {
3323
3994
  const scheme = tlsOptions ? "https" : "http";
3324
3995
  const url = `${scheme}://${formatHostForUrl(opts.hostname)}:${actualPort}`;
3325
3996
  try {
3326
- if (opts.channelSelection) {
3327
- const createSupervisor = deps.channelWorkerSupervisorFactory ?? channelRuntime?.createChannelWorkerSupervisor;
3328
- const findCliEntryPath = channelRuntime?.findCliEntryPath;
3329
- if (!createSupervisor || !findCliEntryPath) {
3330
- throw new Error(
3331
- "Channel worker supervisor runtime is not available."
3332
- );
3333
- }
3334
- channelWorker = createSupervisor({
3335
- cliEntryPath: findCliEntryPath(),
3336
- daemonUrl: formatChannelWorkerDaemonUrl(opts.hostname, actualPort),
3337
- ...token ? { daemonToken: token } : {},
3338
- workspace: boundWorkspace,
3339
- selection: opts.channelSelection,
3340
- onReady: /* @__PURE__ */ __name((snapshot) => {
3341
- writeChannelWorkerPidfile(snapshot);
3342
- }, "onReady"),
3343
- onLog: /* @__PURE__ */ __name(({ stream, line }) => {
3344
- const message = `channel worker ${stream}: ${line}`;
3345
- if (stream === "stderr") {
3346
- daemonLog.warn(message);
3347
- } else {
3348
- daemonLog.info(message);
3349
- }
3350
- }, "onLog"),
3351
- onExit: /* @__PURE__ */ __name((snapshot) => {
3352
- daemonLog.warn(
3353
- `channel worker exited (state=${snapshot.state}, pid=${snapshot.pid ?? "unknown"}, code=${snapshot.exitCode ?? "null"}, signal=${snapshot.signal ?? "null"}, error=${snapshot.error ?? "none"}, restartCount=${snapshot.restartCount ?? 0}, nextRestartAt=${snapshot.nextRestartAt ?? "none"}, staleHeartbeatAt=${snapshot.staleHeartbeatAt ?? "none"})`
3354
- );
3355
- writeChannelWorkerPidfile(snapshot, { clearWorkerPid: true });
3356
- }, "onExit")
3357
- });
3358
- }
3997
+ channelWorkspaceGroups = resolveChannelWorkspaceGroupsAtListen();
3359
3998
  } catch (err) {
3360
3999
  removeCurrentServePidfile();
3361
4000
  const error = err instanceof Error ? err : new Error(String(err));
3362
4001
  server.close((closeErr) => {
3363
4002
  if (closeErr) {
3364
4003
  daemonLog.error(
3365
- "server close after channel worker setup error failed",
4004
+ "server close after channel worker validation error failed",
3366
4005
  closeErr
3367
4006
  );
3368
4007
  }
@@ -3370,6 +4009,19 @@ async function runQwenServe(optsIn, deps = {}) {
3370
4009
  });
3371
4010
  return;
3372
4011
  }
4012
+ if (channelWorkspaceGroups) {
4013
+ for (const group of channelWorkspaceGroups) {
4014
+ daemonLog.info("channel worker group assigned", {
4015
+ workspace: group.workspaceCwd,
4016
+ channels: group.selection.mode === "all" ? ["all"] : group.selection.names
4017
+ });
4018
+ }
4019
+ if (opts.channelSelection?.mode === "all") {
4020
+ writeStderrLine(
4021
+ "qwen serve: --channel all is primary-workspace only; non-primary workspace channels are not hosted."
4022
+ );
4023
+ }
4024
+ }
3373
4025
  writeStdoutLine(
3374
4026
  `qwen serve listening on ${url} (mode=${opts.mode}, workspace=${boundWorkspace})`
3375
4027
  );
@@ -3445,20 +4097,28 @@ async function runQwenServe(optsIn, deps = {}) {
3445
4097
  }
3446
4098
  }, "shutdownBridgeAfterFailedStartup");
3447
4099
  const stopChannelWorkerAfterFailedStartup = /* @__PURE__ */ __name(async () => {
3448
- await channelWorker.stop().catch((stopErr) => {
4100
+ if (!channelWorkerManager) return true;
4101
+ try {
4102
+ await channelWorkerManager.shutdown();
4103
+ return true;
4104
+ } catch (stopErr) {
3449
4105
  daemonLog.error(
3450
4106
  "channel worker stop after runtime startup error failed",
3451
4107
  stopErr instanceof Error ? stopErr : null
3452
4108
  );
3453
- });
4109
+ return false;
4110
+ }
3454
4111
  }, "stopChannelWorkerAfterFailedStartup");
3455
4112
  const failRuntimeStartup = /* @__PURE__ */ __name(async (err, bridgeForCleanup) => {
3456
4113
  const error = err instanceof Error ? err : new Error(String(err));
3457
4114
  if (runtimeStartupSettled) {
4115
+ disposeRuntimeAppResources(runtimeApp ?? runtimeAppForCleanup);
3458
4116
  await shutdownBridgeAfterFailedStartup(bridgeForCleanup);
3459
4117
  return;
3460
4118
  }
3461
4119
  runtimeStartupSettled = true;
4120
+ disposeRuntimeAppResources(runtimeApp ?? runtimeAppForCleanup);
4121
+ runtimeApp = void 0;
3462
4122
  clearRuntimeStartupTimer();
3463
4123
  const message = error.message;
3464
4124
  runtimeStartupError = message;
@@ -3468,14 +4128,28 @@ async function runQwenServe(optsIn, deps = {}) {
3468
4128
  }
3469
4129
  writeStderrLine(`qwen serve: runtime startup failed: ${message}`);
3470
4130
  daemonLog.error("runtime startup failed", error);
3471
- await stopChannelWorkerAfterFailedStartup();
4131
+ markRuntimeFailed(error);
4132
+ if (closeServerAfterChannelWorkerStartupFailure && server.listening) {
4133
+ runtimeFailureListenerClose = new Promise((resolve2) => {
4134
+ server.close((closeErr) => {
4135
+ if (closeErr) {
4136
+ daemonLog.error(
4137
+ "server close after runtime startup error failed",
4138
+ closeErr
4139
+ );
4140
+ }
4141
+ resolve2();
4142
+ });
4143
+ });
4144
+ server.closeAllConnections();
4145
+ }
4146
+ const channelWorkerStopped = await stopChannelWorkerAfterFailedStartup();
3472
4147
  disposeDaemonEventLoopMonitor();
3473
- removeCurrentServePidfile();
4148
+ if (channelWorkerStopped) removeCurrentServePidfile();
3474
4149
  const bridgesForCleanup = bridgeForCleanup ? [bridgeForCleanup, ...getRuntimeBridgesForCleanup()] : getRuntimeBridgesForCleanup();
3475
4150
  for (const bridge of [...new Set(bridgesForCleanup)]) {
3476
4151
  await shutdownBridgeAfterFailedStartup(bridge);
3477
4152
  }
3478
- markRuntimeFailed(error);
3479
4153
  }, "failRuntimeStartup");
3480
4154
  const armRuntimeStartupTimer = /* @__PURE__ */ __name(() => {
3481
4155
  if (runtimeStartupTimeoutMs <= 0 || runtimeStartupTimer) return;
@@ -3488,10 +4162,152 @@ async function runQwenServe(optsIn, deps = {}) {
3488
4162
  }, runtimeStartupTimeoutMs);
3489
4163
  runtimeStartupTimer.unref();
3490
4164
  }, "armRuntimeStartupTimer");
3491
- const completeRuntimeStartup = /* @__PURE__ */ __name(async () => {
4165
+ const resolveRuntimeChannelGroups = /* @__PURE__ */ __name(async (channelSelection, candidateApp, operation) => {
4166
+ const registry = candidateApp.locals?.["workspaceRegistry"];
4167
+ if (!registry) {
4168
+ throw new Error(
4169
+ "Workspace registry is not available for channel workers."
4170
+ );
4171
+ }
4172
+ const runtimes = registry.list();
4173
+ if (runtimes.length <= 1 && operation === "initial") {
4174
+ return [
4175
+ {
4176
+ workspaceCwd: registry.primary.workspaceCwd,
4177
+ selection: channelSelection
4178
+ }
4179
+ ];
4180
+ }
4181
+ const workerRuntime = await ensureChannelRuntime();
4182
+ const settingsRuntime = await loadSettingsRuntimeModules();
4183
+ const settingsByWorkspace = /* @__PURE__ */ new Map();
4184
+ const grouping = resolveChannelWorkspaceGroups({
4185
+ workspaces: runtimes.map((runtime) => {
4186
+ const settings = settingsRuntime.settings.loadSettings(
4187
+ runtime.workspaceCwd
4188
+ );
4189
+ settingsByWorkspace.set(runtime.workspaceCwd, settings);
4190
+ return {
4191
+ workspaceCwd: runtime.workspaceCwd,
4192
+ primary: runtime.primary,
4193
+ trusted: runtime.trusted
4194
+ };
4195
+ }),
4196
+ selection: channelSelection,
4197
+ loadChannelsConfig: /* @__PURE__ */ __name((cwd) => {
4198
+ const settings = settingsByWorkspace.get(cwd);
4199
+ return settings ? workerRuntime.loadChannelsConfig(cwd, settings) : {};
4200
+ }, "loadChannelsConfig")
4201
+ });
4202
+ if (!grouping.ok) {
4203
+ throw Object.assign(new Error(grouping.error.message), {
4204
+ code: grouping.error.code,
4205
+ ...grouping.error.channel ? { channel: grouping.error.channel } : {}
4206
+ });
4207
+ }
4208
+ return grouping.groups;
4209
+ }, "resolveRuntimeChannelGroups");
4210
+ ensureChannelWorkerManager = /* @__PURE__ */ __name(() => {
4211
+ if (channelWorkerManager) return Promise.resolve(channelWorkerManager);
4212
+ if (channelWorkerManagerStarting) return channelWorkerManagerStarting;
4213
+ const starting = (async () => {
4214
+ const candidateApp = runtimeApp ?? runtimeAppForCleanup;
4215
+ const registry = candidateApp?.locals?.["workspaceRegistry"];
4216
+ if (!candidateApp || !registry) {
4217
+ throw new Error(
4218
+ "Workspace registry is not available for channels."
4219
+ );
4220
+ }
4221
+ const workerRuntime = await ensureChannelRuntime();
4222
+ const createSupervisor = deps.channelWorkerSupervisorFactory ?? workerRuntime.createChannelWorkerSupervisor;
4223
+ const createGroup = /* @__PURE__ */ __name((groups) => workerRuntime.createChannelWorkerGroup({
4224
+ groups,
4225
+ registry,
4226
+ createSupervisor,
4227
+ shared: {
4228
+ cliEntryPath: workerRuntime.findCliEntryPath(),
4229
+ daemonUrl: formatChannelWorkerDaemonUrl(
4230
+ opts.hostname,
4231
+ actualPort
4232
+ ),
4233
+ ...token ? { daemonToken: token } : {}
4234
+ },
4235
+ onReady: /* @__PURE__ */ __name((snapshot) => {
4236
+ if (runtimeStartupError !== void 0) return;
4237
+ if (workspaceInputs.length > 1) {
4238
+ daemonLog.info("channel worker ready", {
4239
+ workspace: snapshot.workspaceCwd,
4240
+ pid: snapshot.pid,
4241
+ channels: snapshot.channels
4242
+ });
4243
+ }
4244
+ channelWorkerManager?.workerChanged();
4245
+ if (!runtimeStartupSettled) writeChannelWorkerPidfile();
4246
+ }, "onReady"),
4247
+ onExit: /* @__PURE__ */ __name((snapshot) => {
4248
+ const workspacePrefix = workspaceInputs.length > 1 ? `workspace=${snapshot.workspaceCwd}, ` : "";
4249
+ daemonLog.warn(
4250
+ `channel worker exited (${workspacePrefix}state=${snapshot.state}, pid=${snapshot.pid ?? "unknown"}, code=${snapshot.exitCode ?? "null"}, signal=${snapshot.signal ?? "null"}, error=${snapshot.error ?? "none"}, restartCount=${snapshot.restartCount ?? 0}, nextRestartAt=${snapshot.nextRestartAt ?? "none"}, staleHeartbeatAt=${snapshot.staleHeartbeatAt ?? "none"})`
4251
+ );
4252
+ channelWorkerManager?.workerChanged();
4253
+ if (!runtimeStartupSettled) writeChannelWorkerPidfile();
4254
+ }, "onExit"),
4255
+ onStateChange: /* @__PURE__ */ __name(() => {
4256
+ channelWorkerManager?.workerChanged();
4257
+ if (!runtimeStartupSettled) writeChannelWorkerPidfile();
4258
+ }, "onStateChange"),
4259
+ onLog: /* @__PURE__ */ __name(({ stream, line, workspaceCwd }) => {
4260
+ const message = workspaceInputs.length > 1 ? `channel worker [${workspaceCwd}] ${stream}: ${line}` : `channel worker ${stream}: ${line}`;
4261
+ if (stream === "stderr") daemonLog.warn(message);
4262
+ else daemonLog.info(message);
4263
+ }, "onLog")
4264
+ }), "createGroup");
4265
+ channelWorkerManager = workerRuntime.createChannelWorkerManager({
4266
+ resolveGroups: /* @__PURE__ */ __name((selection, operation) => resolveRuntimeChannelGroups(selection, candidateApp, operation), "resolveGroups"),
4267
+ createGroup,
4268
+ reserveLease: reserveChannelServicePidfile,
4269
+ releaseLease: /* @__PURE__ */ __name(() => {
4270
+ removeCurrentServePidfile();
4271
+ if (channelPidfileReserved) {
4272
+ throw new Error("Failed to release the channel service lease.");
4273
+ }
4274
+ }, "releaseLease"),
4275
+ initialLeaseReserved: channelPidfileReserved,
4276
+ onCommittedSelection: /* @__PURE__ */ __name((_selection, groups) => {
4277
+ channelWorkspaceGroups = groups;
4278
+ channelWebhookConfigVersion += 1;
4279
+ refreshChannelWebhookConfigs?.();
4280
+ }, "onCommittedSelection"),
4281
+ onStateChange: /* @__PURE__ */ __name(() => {
4282
+ if (runtimeStartupSettled) writeChannelWorkerPidfile();
4283
+ }, "onStateChange")
4284
+ });
4285
+ return channelWorkerManager;
4286
+ })();
4287
+ channelWorkerManagerStarting = starting;
4288
+ void starting.then(
4289
+ () => {
4290
+ if (channelWorkerManagerStarting === starting) {
4291
+ channelWorkerManagerStarting = void 0;
4292
+ }
4293
+ },
4294
+ () => {
4295
+ if (channelWorkerManagerStarting === starting) {
4296
+ channelWorkerManagerStarting = void 0;
4297
+ }
4298
+ }
4299
+ );
4300
+ return starting;
4301
+ }, "ensureChannelWorkerManager");
4302
+ const completeRuntimeStartup = /* @__PURE__ */ __name(async (candidateApp) => {
3492
4303
  if (runtimeStartupSettled) return;
4304
+ runtimeApp = candidateApp;
4305
+ const acpHandle = candidateApp.locals?.["acpHandle"];
4306
+ acpHandle?.attachServer?.(server);
3493
4307
  if (opts.channelSelection) {
3494
- await channelWorker.start();
4308
+ closeServerAfterChannelWorkerStartupFailure = true;
4309
+ const manager = await ensureChannelWorkerManager();
4310
+ await manager.startInitial(opts.channelSelection);
3495
4311
  if (runtimeStartupSettled) return;
3496
4312
  }
3497
4313
  if (runtimeStartupSettled) return;
@@ -3526,6 +4342,7 @@ async function runQwenServe(optsIn, deps = {}) {
3526
4342
  clearRuntimeStartFallbackTimer();
3527
4343
  runtimeStarting = buildRuntime().then(async (runtime) => {
3528
4344
  if (runtimeStartupSettled) {
4345
+ disposeRuntimeAppResources(runtime.app);
3529
4346
  await shutdownBridgeAfterFailedStartup(runtime.bridge);
3530
4347
  return;
3531
4348
  }
@@ -3538,13 +4355,10 @@ async function runQwenServe(optsIn, deps = {}) {
3538
4355
  );
3539
4356
  return;
3540
4357
  }
3541
- runtimeApp = runtime.app;
3542
- const acpHandle = runtime.app.locals?.["acpHandle"];
3543
- acpHandle?.attachServer?.(server);
3544
4358
  if (shouldPreheat) {
3545
4359
  startBridgePreheat(runtime.bridge);
3546
4360
  }
3547
- await completeRuntimeStartup();
4361
+ await completeRuntimeStartup(runtime.app);
3548
4362
  }).catch((err) => failRuntimeStartup(err));
3549
4363
  }, "startRuntime");
3550
4364
  startRuntimeForRequest = startRuntime;
@@ -3582,11 +4396,10 @@ async function runQwenServe(optsIn, deps = {}) {
3582
4396
  if (shuttingDown) {
3583
4397
  daemonLog.warn(`received ${signal} during drain \u2014 forcing exit`);
3584
4398
  try {
3585
- channelWorker.killAllSync();
4399
+ channelWorkerManager?.killAllSync();
3586
4400
  for (const runtimeBridge of getRuntimeBridgesForCleanup()) {
3587
4401
  runtimeBridge.killAllSync();
3588
4402
  }
3589
- removeCurrentServePidfile();
3590
4403
  } catch (err) {
3591
4404
  daemonLog.error(
3592
4405
  "force-kill error",
@@ -3602,16 +4415,24 @@ async function runQwenServe(optsIn, deps = {}) {
3602
4415
  try {
3603
4416
  await handle.close();
3604
4417
  await daemonLog.flush();
3605
- process.exit(0);
4418
+ process.exit(runtimeStartupError === void 0 ? 0 : 1);
3606
4419
  } catch (err) {
3607
4420
  daemonLog.error("shutdown error", err instanceof Error ? err : null);
3608
4421
  await daemonLog.flush().catch(() => {
3609
4422
  });
4423
+ if (channelWorkerManager?.state().enabled) {
4424
+ daemonLog.error(
4425
+ "refusing to exit while a channel worker or service lease remains; signal again to retry after the child exits (another signal during that retry forces exit)"
4426
+ );
4427
+ await daemonLog.flush().catch(() => {
4428
+ });
4429
+ return;
4430
+ }
3610
4431
  process.exit(1);
3611
4432
  }
3612
4433
  }, "onSignal");
3613
4434
  const onUncaughtExceptionMonitor = /* @__PURE__ */ __name(() => {
3614
- if (process.listenerCount("uncaughtException") === 0) {
4435
+ if (process.listenerCount("uncaughtException") === 0 && !channelWorkerManager?.state().enabled) {
3615
4436
  removeCurrentServePidfile();
3616
4437
  }
3617
4438
  }, "onUncaughtExceptionMonitor");
@@ -3626,21 +4447,24 @@ async function runQwenServe(optsIn, deps = {}) {
3626
4447
  if (closePromise) return closePromise;
3627
4448
  closePromise = new Promise((res, rej) => {
3628
4449
  shuttingDown = true;
3629
- app.locals.stopScheduledTaskKeepalive?.();
3630
- const stoppers = app.locals.subSessionStoppers;
3631
- if (stoppers) {
3632
- for (const stop of stoppers) stop();
3633
- }
4450
+ channelControlDraining = true;
4451
+ const initiallyMountedApp = runtimeApp ?? runtimeAppForCleanup;
4452
+ const initiallyMountedManagement = initiallyMountedApp?.locals?.["workspaceManagementHandle"];
4453
+ const initialManagementWait = initiallyMountedManagement?.sealAndWait?.();
3634
4454
  clearRuntimeStartAfterHealthTimer();
3635
4455
  clearRuntimeStartFallbackTimer();
3636
4456
  cancelDeferredRuntimeStartup();
3637
4457
  let settled = false;
3638
4458
  let bridgeShutdownError;
4459
+ let channelWorkerShutdownError;
3639
4460
  const finish = /* @__PURE__ */ __name((err) => {
3640
4461
  if (settled) return;
3641
4462
  settled = true;
3642
- process.removeListener("SIGINT", onSignal);
3643
- process.removeListener("SIGTERM", onSignal);
4463
+ const preserveSignalHandlers = channelWorkerShutdownError !== void 0 && channelWorkerManager?.state().enabled === true;
4464
+ if (!preserveSignalHandlers) {
4465
+ process.removeListener("SIGINT", onSignal);
4466
+ process.removeListener("SIGTERM", onSignal);
4467
+ }
3644
4468
  process.removeListener(
3645
4469
  "uncaughtExceptionMonitor",
3646
4470
  onUncaughtExceptionMonitor
@@ -3651,9 +4475,17 @@ async function runQwenServe(optsIn, deps = {}) {
3651
4475
  );
3652
4476
  }).finally(() => daemonLog.flush().catch(() => {
3653
4477
  })).finally(() => {
3654
- const finalErr = err ?? bridgeShutdownError;
3655
- if (finalErr) rej(finalErr);
3656
- else res();
4478
+ const finalErr = err ?? bridgeShutdownError ?? channelWorkerShutdownError;
4479
+ if (finalErr) {
4480
+ if (channelWorkerShutdownError && channelWorkerManager?.state().enabled) {
4481
+ closePromise = void 0;
4482
+ shuttingDown = false;
4483
+ channelControlDraining = false;
4484
+ }
4485
+ rej(finalErr);
4486
+ } else {
4487
+ res();
4488
+ }
3657
4489
  });
3658
4490
  }, "finish");
3659
4491
  void (coreRuntimePromise ? coreRuntimePromise.then((core) => core.forceFlushMetrics()) : Promise.resolve()).catch((flushErr) => {
@@ -3667,43 +4499,54 @@ async function runQwenServe(optsIn, deps = {}) {
3667
4499
  daemonLog
3668
4500
  );
3669
4501
  const appForCleanup = runtimeApp ?? runtimeAppForCleanup;
3670
- const deviceFlowRegistry = appForCleanup ? getDeviceFlowRegistry(appForCleanup) : void 0;
3671
- if (deviceFlowRegistry) {
3672
- try {
3673
- deviceFlowRegistry.dispose();
3674
- } catch (err) {
3675
- daemonLog.warn(
3676
- `device-flow registry dispose error: ${err instanceof Error ? err.message : String(err)}`
3677
- );
3678
- }
4502
+ const workspaceManagementHandle = appForCleanup?.locals?.["workspaceManagementHandle"];
4503
+ await initialManagementWait;
4504
+ if (workspaceManagementHandle !== initiallyMountedManagement) {
4505
+ await workspaceManagementHandle?.sealAndWait?.();
3679
4506
  }
3680
- const acpHandle = appForCleanup?.locals?.["acpHandle"];
3681
- if (acpHandle?.dispose) {
3682
- try {
3683
- acpHandle.dispose();
3684
- } catch (err) {
3685
- daemonLog.warn(
3686
- `ACP handle dispose error: ${err instanceof Error ? err.message : String(err)}`
4507
+ disposeRuntimeAppResources(appForCleanup);
4508
+ disposeDaemonEventLoopMonitor();
4509
+ if (channelWorkerManager) {
4510
+ await channelWorkerManager.shutdown().catch((err) => {
4511
+ daemonLog.error(
4512
+ "channel worker stop error",
4513
+ err instanceof Error ? err : null
3687
4514
  );
3688
- }
3689
- }
3690
- const rl = appForCleanup ? getRateLimiter(appForCleanup) : void 0;
3691
- if (rl) {
3692
- rl.setDraining(true);
3693
- rl.dispose();
4515
+ channelWorkerShutdownError = err instanceof Error ? err : new Error(String(err));
4516
+ });
4517
+ } else {
4518
+ removeCurrentServePidfile();
3694
4519
  }
3695
- disposeDaemonEventLoopMonitor();
3696
- await channelWorker.stop().catch((err) => {
3697
- daemonLog.error(
3698
- "channel worker stop error",
3699
- err instanceof Error ? err : null
4520
+ const runtimeRemoval = appForCleanup?.locals?.["workspaceRuntimeRemoval"];
4521
+ const workspaceRegistry = appForCleanup?.locals?.["workspaceRegistry"];
4522
+ const managedRuntimeBridges = /* @__PURE__ */ new Set();
4523
+ if (runtimeRemoval && workspaceRegistry) {
4524
+ const managedRuntimes = workspaceRegistry.listManaged();
4525
+ for (const workspaceRuntime of managedRuntimes) {
4526
+ managedRuntimeBridges.add(workspaceRuntime.bridge);
4527
+ }
4528
+ await Promise.all(
4529
+ managedRuntimes.map(
4530
+ (workspaceRuntime) => runtimeRemoval.disposeRuntime(workspaceRuntime, "daemon_shutdown").catch((err) => {
4531
+ daemonLog.error(
4532
+ "workspace runtime shutdown error",
4533
+ err instanceof Error ? err : null
4534
+ );
4535
+ bridgeShutdownError = err instanceof Error ? err : new Error(String(err));
4536
+ try {
4537
+ workspaceRuntime.bridge.killAllSync();
4538
+ } catch {
4539
+ }
4540
+ })
4541
+ )
3700
4542
  );
3701
- });
3702
- removeCurrentServePidfile();
4543
+ }
3703
4544
  for (const bridgeForShutdown of getRuntimeBridgesForCleanup()) {
4545
+ if (managedRuntimeBridges.has(bridgeForShutdown)) continue;
3704
4546
  if (shutdownBridges.has(bridgeForShutdown)) continue;
3705
4547
  shutdownBridges.add(bridgeForShutdown);
3706
4548
  await bridgeForShutdown.shutdown().catch((err) => {
4549
+ shutdownBridges.delete(bridgeForShutdown);
3707
4550
  daemonLog.error(
3708
4551
  "bridge shutdown error",
3709
4552
  err instanceof Error ? err : null
@@ -3712,6 +4555,12 @@ async function runQwenServe(optsIn, deps = {}) {
3712
4555
  });
3713
4556
  }
3714
4557
  }).finally(() => {
4558
+ if (!server.listening) {
4559
+ void (runtimeFailureListenerClose ?? Promise.resolve()).then(
4560
+ () => finish()
4561
+ );
4562
+ return;
4563
+ }
3715
4564
  const SECONDARY_DEADLINE_MS = 2e3;
3716
4565
  let secondaryTimer;
3717
4566
  const forceTimer = setTimeout(() => {
@@ -3745,17 +4594,19 @@ async function runQwenServe(optsIn, deps = {}) {
3745
4594
  server.on("error", (err) => {
3746
4595
  daemonLog.error("server error", err instanceof Error ? err : null);
3747
4596
  });
3748
- if (runtimeApp && bridgeRef) {
3749
- const acpHandle = runtimeApp.locals?.["acpHandle"];
3750
- acpHandle?.attachServer?.(server);
4597
+ const preparedRuntimeApp = runtimeApp ?? runtimeAppForCleanup;
4598
+ if (preparedRuntimeApp && bridgeRef && deps.bridge) {
3751
4599
  if (shouldPreheat) {
3752
4600
  startBridgePreheat(bridgeRef);
3753
4601
  }
3754
4602
  if (opts.channelSelection && !runtimeStartupSettled) {
3755
4603
  armRuntimeStartupTimer();
3756
- runtimeStarting = completeRuntimeStartup().catch(
4604
+ runtimeStarting = completeRuntimeStartup(preparedRuntimeApp).catch(
3757
4605
  (err) => failRuntimeStartup(err, bridgeRef)
3758
4606
  );
4607
+ } else {
4608
+ const acpHandle = preparedRuntimeApp.locals?.["acpHandle"];
4609
+ acpHandle?.attachServer?.(server);
3759
4610
  }
3760
4611
  } else if (deferRuntimeUntilFirstHealth) {
3761
4612
  scheduleRuntimeStartFallback();
@@ -3768,14 +4619,24 @@ async function runQwenServe(optsIn, deps = {}) {
3768
4619
  void runtimeReady.then(
3769
4620
  () => resolve(handle),
3770
4621
  (err) => {
3771
- void handle.close().catch((closeErr) => {
3772
- daemonLog.error(
3773
- "shutdown after runtime startup error failed",
3774
- closeErr instanceof Error ? closeErr : null
3775
- );
3776
- }).finally(() => {
3777
- reject(err instanceof Error ? err : new Error(String(err)));
3778
- });
4622
+ void handle.close().then(
4623
+ () => {
4624
+ reject(err instanceof Error ? err : new Error(String(err)));
4625
+ },
4626
+ (closeErr) => {
4627
+ daemonLog.error(
4628
+ "shutdown after runtime startup error failed",
4629
+ closeErr instanceof Error ? closeErr : null
4630
+ );
4631
+ if (channelWorkerManager?.state().enabled) {
4632
+ daemonLog.error(
4633
+ "runtime startup failed, but qwen serve remains alive to retain the channel service lease until worker exit is confirmed"
4634
+ );
4635
+ return;
4636
+ }
4637
+ reject(err instanceof Error ? err : new Error(String(err)));
4638
+ }
4639
+ );
3779
4640
  }
3780
4641
  );
3781
4642
  }
@@ -3843,11 +4704,11 @@ export {
3843
4704
  };
3844
4705
  /**
3845
4706
  * @license
3846
- * Copyright 2026 Qwen Team
4707
+ * Copyright 2025 Qwen Team
3847
4708
  * SPDX-License-Identifier: Apache-2.0
3848
4709
  */
3849
4710
  /**
3850
4711
  * @license
3851
- * Copyright 2025 Qwen Team
4712
+ * Copyright 2026 Qwen Team
3852
4713
  * SPDX-License-Identifier: Apache-2.0
3853
4714
  */