@qwen-code/qwen-code 0.21.7 → 0.21.8-nightly.20260810.55e20db328

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 (334) hide show
  1. package/README.md +2 -0
  2. package/bundled/qc-helper/docs/configuration/settings.md +73 -59
  3. package/bundled/qc-helper/docs/extension/introduction.md +15 -1
  4. package/bundled/qc-helper/docs/features/channels/dingtalk.md +40 -2
  5. package/bundled/qc-helper/docs/features/channels/feishu.md +13 -2
  6. package/bundled/qc-helper/docs/features/channels/github.md +13 -11
  7. package/bundled/qc-helper/docs/features/channels/gitlab.md +13 -11
  8. package/bundled/qc-helper/docs/features/channels/overview.md +49 -31
  9. package/bundled/qc-helper/docs/features/channels/plugins.md +2 -2
  10. package/bundled/qc-helper/docs/features/channels/qqbot.md +4 -2
  11. package/bundled/qc-helper/docs/features/channels/telegram.md +4 -2
  12. package/bundled/qc-helper/docs/features/channels/wecom.md +1 -1
  13. package/bundled/qc-helper/docs/features/code-review.md +4 -0
  14. package/bundled/qc-helper/docs/features/commands.md +1 -1
  15. package/bundled/qc-helper/docs/features/hooks.md +52 -0
  16. package/bundled/qc-helper/docs/integration-jetbrains.md +1 -0
  17. package/bundled/qc-helper/docs/qwen-serve-deploy-local.md +1 -1
  18. package/bundled/qc-helper/docs/qwen-serve.md +10 -5
  19. package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +50 -36
  20. package/bundled/review/SKILL.md +32 -5
  21. package/chunks/{MaxSizedBox-ANWZBPTS.js → MaxSizedBox-A52T4GEU.js} +29 -25
  22. package/chunks/{StandaloneSessionPicker-XKS3VU3A.js → StandaloneSessionPicker-4J654SOB.js} +48 -44
  23. package/chunks/{acp-startup-profiler-ORIQ2CCQ.js → acp-startup-profiler-B5BYAMYQ.js} +2 -2
  24. package/chunks/{acpAgent-KUAZ3GCZ.js → acpAgent-65AM4AOR.js} +1833 -843
  25. package/chunks/{agent-T6PTGX5R.js → agent-SVQ7U7NJ.js} +28 -24
  26. package/chunks/{agent-headless-PR7YCB7S.js → agent-headless-XXGY2SQO.js} +28 -24
  27. package/chunks/{artifact-tool-U4VOZIK5.js → artifact-tool-N652YE4L.js} +1 -1
  28. package/chunks/{askUserQuestion-XOBQLPFA.js → askUserQuestion-ONW74PKJ.js} +1 -1
  29. package/chunks/{bridge-NU6YEEHK.js → bridge-Y32B5S3G.js} +34 -30
  30. package/chunks/{ca-NYKNH222.js → ca-ZLKAB4K3.js} +6 -0
  31. package/chunks/{channel-management-service-FSQ7SHYI.js → channel-management-service-PHELKOW2.js} +19 -10
  32. package/chunks/{channel-settings-store-35VQVUVV.js → channel-settings-store-OW43U6VB.js} +36 -32
  33. package/chunks/{channel-worker-group-JWQK224X.js → channel-worker-group-MSHT6CWR.js} +7 -7
  34. package/chunks/{channel-worker-manager-77E3OXRQ.js → channel-worker-manager-XXD3GDSK.js} +7 -7
  35. package/chunks/{channel-worker-supervisor-62YWUGP2.js → channel-worker-supervisor-IVSDIMI4.js} +4 -4
  36. package/chunks/{chunk-HYRDKNUY.js → chunk-2545EHZR.js} +1 -1
  37. package/chunks/{chunk-SPMY6CX6.js → chunk-2ASQDZSJ.js} +3 -3
  38. package/chunks/{chunk-CJXIIILK.js → chunk-2FRY5CRX.js} +1 -1
  39. package/chunks/{chunk-CNIID3TV.js → chunk-2UUMVNYT.js} +1 -1
  40. package/chunks/{chunk-KXSDEOFU.js → chunk-3KJQZEN3.js} +1 -1
  41. package/chunks/{chunk-22NBYCPD.js → chunk-3P2LV43B.js} +1 -1
  42. package/chunks/{chunk-Y7E5LNLO.js → chunk-47ZUMGGY.js} +2634 -2114
  43. package/chunks/{chunk-LA7YLSR6.js → chunk-4EOURKA4.js} +839 -153
  44. package/chunks/chunk-4JRM2BSM.js +48 -0
  45. package/chunks/{chunk-BGG4MBYD.js → chunk-4PYCZH2V.js} +4 -187
  46. package/chunks/{chunk-L4N6TIP7.js → chunk-4TRQPFDP.js} +4 -2
  47. package/chunks/{chunk-6XJMFJ4D.js → chunk-5WZL724W.js} +177 -120
  48. package/chunks/{chunk-23RZCH76.js → chunk-5ZTS3OQ3.js} +3 -3
  49. package/chunks/{chunk-VWOVHOTT.js → chunk-67EWJE77.js} +1 -1
  50. package/chunks/{chunk-3K7FH2KQ.js → chunk-67N6HB5H.js} +1310 -171
  51. package/chunks/{chunk-CXE7E23H.js → chunk-6Q5ABMTU.js} +45 -17
  52. package/chunks/{chunk-NG2R4N5O.js → chunk-6ZRFAKXK.js} +1 -1
  53. package/chunks/{chunk-RERATVSK.js → chunk-7XX5IBXL.js} +145 -10
  54. package/chunks/{chunk-S7CV4KO6.js → chunk-AKJQSWV4.js} +4 -4
  55. package/chunks/{chunk-5KH532V7.js → chunk-AUWN7BAG.js} +1 -1
  56. package/chunks/{chunk-DBOFL3LQ.js → chunk-BL65MO72.js} +10 -10
  57. package/chunks/{chunk-AKA24BGO.js → chunk-BT6GQIEJ.js} +1 -1
  58. package/chunks/{chunk-W7U4ESW2.js → chunk-CG3WBKJK.js} +1 -1
  59. package/chunks/{chunk-2M5N3DHY.js → chunk-CMOBWHWW.js} +1 -1
  60. package/chunks/chunk-COBO5EBO.js +135 -0
  61. package/chunks/chunk-CPG6P4GG.js +82 -0
  62. package/chunks/{chunk-3NB7TSE2.js → chunk-D2D7J3EW.js} +1 -1
  63. package/chunks/{chunk-NLREQJFK.js → chunk-DA4HRSBW.js} +15 -0
  64. package/chunks/{chunk-RNVQB725.js → chunk-DPMJLKUP.js} +2 -2
  65. package/chunks/{chunk-DIEFUBV6.js → chunk-DX3RGJMS.js} +218 -37
  66. package/chunks/{chunk-33ACCIIY.js → chunk-DZUO626D.js} +1 -1
  67. package/chunks/chunk-E2FPSSKP.js +244 -0
  68. package/chunks/{chunk-72LNFGUY.js → chunk-E2N554HK.js} +3 -3
  69. package/chunks/{chunk-6EOI4HOK.js → chunk-E2T7FLDU.js} +12 -17
  70. package/chunks/{chunk-EUQTXRKZ.js → chunk-EISUQQPS.js} +6 -6
  71. package/chunks/{chunk-H2UQ2M5Z.js → chunk-EUVOKP6Y.js} +96 -22
  72. package/chunks/{chunk-RMH5OWLD.js → chunk-F3II4BTZ.js} +1 -1
  73. package/chunks/{chunk-P3LEG54R.js → chunk-F6TUUA2U.js} +3 -3
  74. package/chunks/chunk-F6WFNA7U.js +22 -0
  75. package/chunks/{chunk-RZHU3ILK.js → chunk-FAD7OC7V.js} +4 -4
  76. package/chunks/{chunk-OMKQH5C5.js → chunk-FWWQIFDT.js} +6 -23
  77. package/chunks/{chunk-TLV2IMHR.js → chunk-FYB4GIDW.js} +3 -3
  78. package/chunks/{chunk-ZJ3VGYHK.js → chunk-GY24H6IA.js} +40 -22
  79. package/chunks/{chunk-P2AJ7FTI.js → chunk-H4JC7LZX.js} +1 -1
  80. package/chunks/{chunk-55LYDVPU.js → chunk-HDYQYYWB.js} +1 -1
  81. package/chunks/{chunk-PQBHU2IA.js → chunk-HKI3KHAO.js} +11 -11
  82. package/chunks/{chunk-Y4NGLFGJ.js → chunk-HUWENAHO.js} +1 -1
  83. package/chunks/{chunk-3ULCB3NE.js → chunk-HV76SFIA.js} +1 -1
  84. package/chunks/{chunk-CSZEXZBE.js → chunk-HWXGX534.js} +3 -3
  85. package/chunks/{chunk-E33LOKRA.js → chunk-I5VM3SKY.js} +22 -5
  86. package/chunks/{chunk-LZC3B5N2.js → chunk-IA7TGCN2.js} +2 -2
  87. package/chunks/{chunk-RGSBTHSU.js → chunk-KMS57JDD.js} +137 -7
  88. package/chunks/{chunk-VOARQPTA.js → chunk-KP25NJNM.js} +65 -21
  89. package/chunks/{chunk-6M26GBKX.js → chunk-L2AFWWJE.js} +42 -3
  90. package/chunks/{chunk-ZXU5UHMI.js → chunk-LHEC5V6G.js} +2 -2
  91. package/chunks/{chunk-J26HHFI5.js → chunk-LP2QGNSP.js} +32 -2
  92. package/chunks/{chunk-R574ATRM.js → chunk-M2PNLYGY.js} +41 -0
  93. package/chunks/{chunk-MKVZE4ZJ.js → chunk-MFDYOYZZ.js} +3 -2
  94. package/chunks/{chunk-ALGGS7UH.js → chunk-NPQ5ZKRP.js} +82 -1
  95. package/chunks/{chunk-YHEEE624.js → chunk-NV3F44FL.js} +7 -3
  96. package/chunks/{chunk-6GONZVLS.js → chunk-NZMXUSKS.js} +1 -1
  97. package/chunks/{chunk-HQB2KUAN.js → chunk-O277O7U6.js} +1 -1
  98. package/chunks/{chunk-Y4EWSQF7.js → chunk-OBP6QWD5.js} +2 -2
  99. package/chunks/{chunk-CYR3HI5X.js → chunk-OQTXYMKW.js} +2 -2
  100. package/chunks/{chunk-TYKC75AX.js → chunk-OR6R3IUW.js} +2 -2
  101. package/chunks/{chunk-TRLYP7OF.js → chunk-OSVH4SMN.js} +16 -141
  102. package/chunks/{chunk-FPM27XN4.js → chunk-OW2BWT2P.js} +4 -4
  103. package/chunks/{chunk-TDXGKHDN.js → chunk-P5ULXPK5.js} +2 -2
  104. package/chunks/{chunk-24ER7UV4.js → chunk-PBRVUF5B.js} +1 -1
  105. package/chunks/{chunk-LKRMNRSN.js → chunk-PDPFQ2FO.js} +2 -0
  106. package/chunks/{chunk-2VSTGY6M.js → chunk-PJRW65JO.js} +1 -1
  107. package/chunks/{chunk-6HVUFDS2.js → chunk-PNVPFARW.js} +22 -13
  108. package/chunks/{chunk-IC4Z6LX6.js → chunk-PYQADEFD.js} +5 -0
  109. package/chunks/chunk-R4ZA6GBK.js +208 -0
  110. package/chunks/{chunk-KR4ZSC7K.js → chunk-RMPLEWF3.js} +3 -3
  111. package/chunks/{chunk-EORNAQDT.js → chunk-RQ6U2JJQ.js} +1 -1
  112. package/chunks/{chunk-AE77C5ZU.js → chunk-RYQMJCUO.js} +1 -1
  113. package/chunks/{chunk-6UWFTUQL.js → chunk-S5CULOHB.js} +75 -31
  114. package/chunks/{chunk-7UFKAV7Y.js → chunk-S6HYI3MF.js} +2 -2
  115. package/chunks/chunk-SAH4BD2J.js +92 -0
  116. package/chunks/chunk-SR7RZ4MS.js +202 -0
  117. package/chunks/{chunk-QETYKPMG.js → chunk-T2O6NIOY.js} +1 -1
  118. package/chunks/chunk-TQOF5KBL.js +71 -0
  119. package/chunks/{chunk-OHJLEW5J.js → chunk-TWFFY5BP.js} +4 -4
  120. package/chunks/{chunk-SCI7K3GM.js → chunk-TXO5BMR2.js} +19 -10
  121. package/chunks/{chunk-YJQCUTT2.js → chunk-UAB6J2IE.js} +4 -4
  122. package/chunks/{chunk-OFI7IGST.js → chunk-UGUKH3SU.js} +10 -10
  123. package/chunks/{chunk-SHZYHGQH.js → chunk-V2226RFW.js} +1 -1
  124. package/chunks/{chunk-KKJJB5X6.js → chunk-V47QNK5W.js} +1526 -833
  125. package/chunks/{chunk-W4B7UBUS.js → chunk-VHXPSL57.js} +6 -6
  126. package/chunks/{chunk-WUC6ZFXO.js → chunk-VKJ4KTPJ.js} +1 -1
  127. package/chunks/{chunk-MAJS7AT4.js → chunk-VNNWAZSI.js} +2 -2
  128. package/chunks/{chunk-BVWVWGLE.js → chunk-WAUGQKUU.js} +4 -64
  129. package/chunks/{chunk-OH2VKQXH.js → chunk-WH6QMLUW.js} +37 -30
  130. package/chunks/{chunk-PJOBIWUG.js → chunk-WQYWK4DG.js} +1 -1
  131. package/chunks/{chunk-LEMXGVEP.js → chunk-YFRFZCIP.js} +3 -3
  132. package/chunks/{chunk-2U3WZGZG.js → chunk-YGT2AUJ7.js} +103 -135
  133. package/chunks/{chunk-6XU3PH66.js → chunk-YR7ZQ4RD.js} +22 -69
  134. package/chunks/{chunk-OA4JVLSA.js → chunk-Z7VOMGDC.js} +22 -1
  135. package/chunks/{chunk-RUL4S3K3.js → chunk-ZCJBCKL5.js} +175 -60
  136. package/chunks/{chunk-GSS34377.js → chunk-ZI5IH2HP.js} +2 -2
  137. package/chunks/{chunk-CB2464BK.js → chunk-ZIKTCMQF.js} +117 -10
  138. package/chunks/chunk-ZLBIFIMV.js +1099 -0
  139. package/chunks/{chunk-D3UEZIQH.js → chunk-ZSNBQXMZ.js} +48 -55
  140. package/chunks/{chunk-FZBZSYKU.js → chunk-ZV7LTLFE.js} +1 -1
  141. package/chunks/{computer-use-KEAPATUY.js → computer-use-SLN7DV2E.js} +28 -24
  142. package/chunks/{config-utils-K7SCOWHD.js → config-utils-ULCSRMWD.js} +4 -4
  143. package/chunks/{contextCommand-ZJA6A5K5.js → contextCommand-EHFYX6FW.js} +30 -26
  144. package/chunks/{core-runtime-QMJPWHCI.js → core-runtime-FBSP5IIG.js} +33 -29
  145. package/chunks/{create-sub-session-E4PBYXV5.js → create-sub-session-QIDZUOPU.js} +1 -1
  146. package/chunks/{create-sub-session-DJE6XICH.js → create-sub-session-WC7YGVSL.js} +29 -25
  147. package/chunks/{cron-create-GRBWYMIJ.js → cron-create-VONA5JYY.js} +1 -1
  148. package/chunks/{cron-delete-4CMKBR2M.js → cron-delete-5JXSP2MI.js} +1 -1
  149. package/chunks/{cron-list-W7K4ASOA.js → cron-list-KE4F2CIR.js} +1 -1
  150. package/chunks/{daemon-SCTQMM7Z.js → daemon-VSX3FHTY.js} +119 -4
  151. package/chunks/{daemon-status-provider-FSWDPE2G.js → daemon-status-provider-WW2AD2T7.js} +39 -35
  152. package/chunks/{daemon-trust-policy-P5L5VFAY.js → daemon-trust-policy-E4MAYWPI.js} +35 -31
  153. package/chunks/{daemon-trust-policy-monitor-73JYN6ET.js → daemon-trust-policy-monitor-XVIDV6T4.js} +35 -31
  154. package/chunks/{de-VL76CPLV.js → de-LIW4XVLN.js} +5 -0
  155. package/chunks/{deferred-core-runtime-BWRCCFTE.js → deferred-core-runtime-5SVUC6UC.js} +28 -24
  156. package/chunks/{display-image-O2XFPQTC.js → display-image-NQRSIBXM.js} +2 -2
  157. package/chunks/{dist-HVBMEYWK.js → dist-3NTTUAHT.js} +1 -1
  158. package/chunks/{dist-7JGAAQSC.js → dist-6BMOAE3Z.js} +2 -2
  159. package/chunks/{dist-3BMEUGTG.js → dist-B2CGTW6T.js} +1 -1
  160. package/chunks/{dist-C5U4LJKW.js → dist-EWW3J4HQ.js} +5 -4
  161. package/chunks/{dist-K4WWQBHS.js → dist-EZE3V3Q7.js} +55 -15
  162. package/chunks/{dist-DPPHJAVL.js → dist-J47AXBZC.js} +918 -142
  163. package/chunks/{dist-SFQ34F4M.js → dist-OTUO3AS2.js} +8 -1091
  164. package/chunks/{dist-KS4O2NKE.js → dist-Z3CS4OVP.js} +14 -8
  165. package/chunks/{earlyInputCapture-L5ST23A6.js → earlyInputCapture-72FYHYRD.js} +28 -24
  166. package/chunks/{edit-LHHRFNFK.js → edit-SWVVWHXW.js} +28 -24
  167. package/chunks/{en-IRJ2WN5T.js → en-SY5GTIGX.js} +6 -0
  168. package/chunks/{enter-worktree-6NXCRZPF.js → enter-worktree-2HF6WQEO.js} +28 -24
  169. package/chunks/{enterPlanMode-ZVQT4U7D.js → enterPlanMode-OW6S7L4P.js} +28 -24
  170. package/chunks/{environment-GJ2HWJOS.js → environment-LRTBTDXH.js} +32 -28
  171. package/chunks/{errors-6RLH3XXC.js → errors-4CO2DME6.js} +30 -26
  172. package/chunks/{exit-worktree-I2YLUHHY.js → exit-worktree-GHJEIRGL.js} +28 -24
  173. package/chunks/{exitPlanMode-7CCWKO7I.js → exitPlanMode-JVP4FNNK.js} +28 -24
  174. package/chunks/{fast-path-DVFD4HQD.js → fast-path-HCCY7GR2.js} +24 -3
  175. package/chunks/{fast-path-settings-WQJU2LAT.js → fast-path-settings-MN5QQQET.js} +6 -3
  176. package/chunks/{fr-GK3HQYVB.js → fr-RQFO7U4P.js} +5 -0
  177. package/chunks/{gemini-7FWGOYD4.js → gemini-V3YVRKXU.js} +85 -72
  178. package/chunks/{geminiContentGenerator-V2WVLIXU.js → geminiContentGenerator-LVPMQUWX.js} +1 -1
  179. package/chunks/{glob-3N6GGI75.js → glob-7HRXHYDR.js} +28 -24
  180. package/chunks/{goal-tools-APOW6UXL.js → goal-tools-PXWN5VK3.js} +4 -2
  181. package/chunks/{grep-V43ILZVX.js → grep-BITQUON5.js} +28 -24
  182. package/chunks/{handleAutoUpdate-A5WFVWIA.js → handleAutoUpdate-DKFY454S.js} +32 -28
  183. package/chunks/{i18n-PLVQ4FBZ.js → i18n-53BQIQSH.js} +29 -25
  184. package/chunks/{image-gen-7C6IK6DC.js → image-gen-H62FLDFB.js} +7 -5
  185. package/chunks/{initializer-CVAIKZYL.js → initializer-Q7NUQRSU.js} +35 -31
  186. package/chunks/{installationInfo-J6QDHNTK.js → installationInfo-BWFHT7WU.js} +29 -25
  187. package/chunks/{ja-3FA46IKD.js → ja-KOZKHJHZ.js} +5 -0
  188. package/chunks/{list-ZZ4OUUWV.js → list-OWQKR6JK.js} +38 -34
  189. package/chunks/{list-agents-KFFIIS2G.js → list-agents-Z4WABFDZ.js} +1 -1
  190. package/chunks/{loadedSettingsAdapter-QRYRJSWL.js → loadedSettingsAdapter-HNPKQMFA.js} +35 -31
  191. package/chunks/{loggingContentGenerator-OJCO75HM.js → loggingContentGenerator-A5I3VQUC.js} +10 -8
  192. package/chunks/{loop-wakeup-TLMJPMMP.js → loop-wakeup-UZ5RL5Y2.js} +2 -2
  193. package/chunks/{ls-UVVE2XGE.js → ls-TVHDGIES.js} +1 -1
  194. package/chunks/{lsp-QMEA2VLB.js → lsp-CVN6OQPI.js} +1 -1
  195. package/chunks/main-PXAUXNF2.js +8 -0
  196. package/chunks/{managed-npm-update-PCOSCP2N.js → managed-npm-update-CUUJGUTV.js} +29 -25
  197. package/chunks/{mcp-M3WKKWKY.js → mcp-GJSC7ZPO.js} +35 -31
  198. package/chunks/{monitor-GN24FH6C.js → monitor-TZ2II7JM.js} +38 -30
  199. package/chunks/{nonInteractiveCli-KPTU3VSQ.js → nonInteractiveCli-22RS4EVH.js} +72 -66
  200. package/chunks/{notebook-edit-4YM65BBO.js → notebook-edit-YZC3YPY2.js} +36 -24
  201. package/chunks/{openaiContentGenerator-EDQT6GTV.js → openaiContentGenerator-OIGO5XNF.js} +21 -11
  202. package/chunks/{pidfile-JOIIRN2C.js → pidfile-QJQ65RJ2.js} +28 -24
  203. package/chunks/{process-registry-2CHQBXH5.js → process-registry-PMJOA5CO.js} +15 -0
  204. package/chunks/{processUtils-5SV4MX3Q.js → processUtils-V5VSDIKS.js} +2 -2
  205. package/chunks/{pt-3AOBKPIJ.js → pt-U6AOCTMF.js} +5 -0
  206. package/chunks/{qwenContentGenerator-ODRJSUGR.js → qwenContentGenerator-HXM25BGB.js} +35 -30
  207. package/chunks/{qwenOAuth2-NESCMZ47.js → qwenOAuth2-7MTMS2Z5.js} +7 -5
  208. package/chunks/{read-file-HPSODLPK.js → read-file-PMGXB3ZB.js} +8 -6
  209. package/chunks/{read-mcp-resource-2TNSJTC3.js → read-mcp-resource-PZ4ZF6YL.js} +1 -1
  210. package/chunks/{record-artifact-RAON52FC.js → record-artifact-P4HFMS7X.js} +2 -2
  211. package/chunks/{resumeHistoryUtils-TXCOFFMQ.js → resumeHistoryUtils-YUTPLHUH.js} +33 -29
  212. package/chunks/{ripGrep-ZKQGTQAO.js → ripGrep-XUS66YIV.js} +28 -24
  213. package/chunks/{ru-VJRAMC4W.js → ru-CMX4UMYP.js} +5 -0
  214. package/chunks/{run-qwen-serve-ZPKPKECF.js → run-qwen-serve-4NVSU75L.js} +261 -62
  215. package/chunks/{runtime-2FLNNM36.js → runtime-QLRVP6HM.js} +40 -36
  216. package/chunks/{scheduler-365PJHNR.js → scheduler-PBB2NSOP.js} +28 -24
  217. package/chunks/{sdk-exporters-http-KHAB5JSD.js → sdk-exporters-http-XVTZQRCE.js} +2 -2
  218. package/chunks/{sdk-impl-KD7FSTC3.js → sdk-impl-7W7AKCS4.js} +7 -3
  219. package/chunks/{send-message-ZM7MRUGP.js → send-message-CAMSLG2L.js} +4 -3
  220. package/chunks/{serve-QD2RNLKJ.js → serve-BWKTRX72.js} +37 -31
  221. package/chunks/{server-XLJVLTYM.js → server-E7CH7WPK.js} +1437 -593
  222. package/chunks/{session-3VTUHH4Q.js → session-QEKSJ6YU.js} +166 -105
  223. package/chunks/{settings-KULVPCQC.js → settings-BRAOSJIM.js} +34 -30
  224. package/chunks/{shell-3SWW22MH.js → shell-OF3CK4AT.js} +28 -24
  225. package/chunks/{skill-KDHWYS7W.js → skill-HLH2NO2H.js} +9 -7
  226. package/chunks/{skill-settings-NMY4JEJC.js → skill-settings-DRPU7YI5.js} +34 -30
  227. package/chunks/{spawnChannel-ZKS4Q5OU.js → spawnChannel-4KO7YTFM.js} +31 -27
  228. package/chunks/{standalone-update-7CXPGAQ4.js → standalone-update-DJN2R3YW.js} +30 -26
  229. package/chunks/{startInteractiveUI-2QQLEKM6.js → startInteractiveUI-O3WQQ2WC.js} +384 -173
  230. package/chunks/{syntheticOutput-ZLCUTKO2.js → syntheticOutput-PEBJQYJV.js} +2 -2
  231. package/chunks/{task-create-CQP5UEA3.js → task-create-5NWCQSJF.js} +5 -4
  232. package/chunks/{task-list-XLEZOJXP.js → task-list-53265TRZ.js} +1 -1
  233. package/chunks/{task-stop-7ZQM5CFF.js → task-stop-FPCOIRYX.js} +1 -1
  234. package/chunks/{task-update-JA75U2SN.js → task-update-5HN577LS.js} +5 -4
  235. package/chunks/{team-create-2WTNGAOX.js → team-create-2E4ACBJD.js} +28 -24
  236. package/chunks/{team-delete-MHY3UZGY.js → team-delete-MMQB4OSC.js} +1 -1
  237. package/chunks/{team-plan-approval-LZTYLEG4.js → team-plan-approval-LWIDXYOA.js} +28 -24
  238. package/chunks/{terminal-image-renderer-R5HHAFJN.js → terminal-image-renderer-PJM7663W.js} +30 -26
  239. package/chunks/{theme-manager-PAUCSJFF.js → theme-manager-FDQTM3RP.js} +28 -24
  240. package/chunks/{todoWrite-UPVOHYGD.js → todoWrite-JGV6VLOX.js} +1 -1
  241. package/chunks/{tool-search-2BFCGLI6.js → tool-search-TL5ZXBF4.js} +11 -8
  242. package/chunks/{total-session-admission-MK4SINFK.js → total-session-admission-LS6EY5KT.js} +37 -32
  243. package/chunks/{trustedFolders-TJ5S26N7.js → trustedFolders-5RWP5OB7.js} +32 -26
  244. package/chunks/{types-ML3TRJQ5.js → types-WYCFCEHR.js} +5 -3
  245. package/chunks/{update-relaunch-N7AYLARF.js → update-relaunch-HWQ4USKY.js} +5 -5
  246. package/chunks/{updateCheck-QRZ3DVT5.js → updateCheck-EZMBTEIR.js} +31 -27
  247. package/chunks/{useAutoAcceptIndicator-DBJ7Y5AF.js → useAutoAcceptIndicator-JDMI627A.js} +37 -33
  248. package/chunks/{validateNonInterActiveAuth-6ZQWUDS3.js → validateNonInterActiveAuth-RRUHC5RO.js} +67 -63
  249. package/chunks/{version-AJQGQWXC.js → version-XS4UTKUX.js} +1 -1
  250. package/chunks/{web-fetch-QWJYRXFL.js → web-fetch-2JV7H6OM.js} +14 -10
  251. package/chunks/{web-search-2AOXJGHU.js → web-search-RCTN2GAM.js} +10 -7
  252. package/chunks/{workflow-ERH3SJG2.js → workflow-6MKLKQ6R.js} +909 -273
  253. package/chunks/{workspace-providers-status-HZ474RA4.js → workspace-providers-status-YZ2IJ4DK.js} +38 -34
  254. package/chunks/{workspace-registration-store-6EUP4JXG.js → workspace-registration-store-IYN72LBS.js} +1 -1
  255. package/chunks/{workspace-registry-R4APRRNC.js → workspace-registry-7LFBZQLT.js} +37 -32
  256. package/chunks/{workspace-service-HFIFDMU7.js → workspace-service-2EHSPRJB.js} +45 -39
  257. package/chunks/{workspace-skills-status-HAHEIHGK.js → workspace-skills-status-5VJ2D5HN.js} +36 -32
  258. package/chunks/{workspace-trust-reconciler-65GTAGDK.js → workspace-trust-reconciler-X2LZ6XCI.js} +44 -39
  259. package/chunks/{write-file-B34CDELV.js → write-file-CJK6SCCF.js} +28 -24
  260. package/chunks/{zh-ARWDLDLR.js → zh-AGL2FVBI.js} +6 -0
  261. package/chunks/{zh-TW-Y4RGI3P7.js → zh-TW-KI66MHQY.js} +6 -0
  262. package/chunks/{zoom-image-HPRBZHSS.js → zoom-image-BVW4RFYW.js} +8 -6
  263. package/cli.js +13 -13
  264. package/locales/ca.js +10 -0
  265. package/locales/de.js +9 -0
  266. package/locales/en.js +10 -0
  267. package/locales/fr.js +9 -0
  268. package/locales/ja.js +9 -0
  269. package/locales/pt.js +9 -0
  270. package/locales/ru.js +9 -0
  271. package/locales/zh-TW.js +10 -0
  272. package/locales/zh.js +10 -0
  273. package/package.json +3 -3
  274. package/web-shell/assets/{arc-Ip-FFDJ0.js → arc-DDtoJrAW.js} +1 -1
  275. package/web-shell/assets/{architectureDiagram-3BPJPVTR-CChqgwIb.js → architectureDiagram-3BPJPVTR-BAInIW4O.js} +1 -1
  276. package/web-shell/assets/{blockDiagram-GPEHLZMM-BHN-Tfss.js → blockDiagram-GPEHLZMM-ChPDk7fP.js} +1 -1
  277. package/web-shell/assets/{c4Diagram-AAUBKEIU-BhyjzDjN.js → c4Diagram-AAUBKEIU-Cd9BJuUy.js} +1 -1
  278. package/web-shell/assets/channel-0OQklTfP.js +1 -0
  279. package/web-shell/assets/{chunk-2J33WTMH-BB9eyHrx.js → chunk-2J33WTMH-COQkLp56.js} +1 -1
  280. package/web-shell/assets/{chunk-4BX2VUAB-BeV7ce_J.js → chunk-4BX2VUAB-GUJshQ_l.js} +1 -1
  281. package/web-shell/assets/{chunk-55IACEB6-DO7kXDiV.js → chunk-55IACEB6-Boaa7c10.js} +1 -1
  282. package/web-shell/assets/{chunk-727SXJPM-B27MPznD.js → chunk-727SXJPM-CcRXaly2.js} +1 -1
  283. package/web-shell/assets/{chunk-AQP2D5EJ-DHIjqjD_.js → chunk-AQP2D5EJ-BY0OARHh.js} +1 -1
  284. package/web-shell/assets/{chunk-FMBD7UC4-DabMqyN2.js → chunk-FMBD7UC4-DLFQfv0S.js} +1 -1
  285. package/web-shell/assets/{chunk-ND2GUHAM-Dw7Nnnxc.js → chunk-ND2GUHAM-C_S0u-D2.js} +1 -1
  286. package/web-shell/assets/{chunk-QZHKN3VN-DdaZew1Q.js → chunk-QZHKN3VN-DP9m64hw.js} +1 -1
  287. package/web-shell/assets/classDiagram-4FO5ZUOK-CZ4-apsu.js +1 -0
  288. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-CZ4-apsu.js +1 -0
  289. package/web-shell/assets/{cose-bilkent-S5V4N54A-CgBpCYH5.js → cose-bilkent-S5V4N54A-CGY6zGE1.js} +1 -1
  290. package/web-shell/assets/{dagre-BM42HDAG-BB4IU8G0.js → dagre-BM42HDAG-yB7whKNn.js} +1 -1
  291. package/web-shell/assets/{diagram-2AECGRRQ-BcUTHU37.js → diagram-2AECGRRQ-Bdlc59La.js} +1 -1
  292. package/web-shell/assets/{diagram-5GNKFQAL-B6Lyihyv.js → diagram-5GNKFQAL-BUClP18L.js} +1 -1
  293. package/web-shell/assets/{diagram-KO2AKTUF-CRIJOjpG.js → diagram-KO2AKTUF-aZm08kjn.js} +1 -1
  294. package/web-shell/assets/{diagram-LMA3HP47-DdNiwWWT.js → diagram-LMA3HP47-7DGwEkxC.js} +1 -1
  295. package/web-shell/assets/{diagram-OG6HWLK6-Dz4b32Do.js → diagram-OG6HWLK6-CAGKayxx.js} +1 -1
  296. package/web-shell/assets/{erDiagram-TEJ5UH35-D-8NGw77.js → erDiagram-TEJ5UH35-CptoHHQi.js} +1 -1
  297. package/web-shell/assets/{flowDiagram-I6XJVG4X-Dun26LyM.js → flowDiagram-I6XJVG4X-CNOH0s7Q.js} +1 -1
  298. package/web-shell/assets/{ganttDiagram-6RSMTGT7-F10Hu1Gc.js → ganttDiagram-6RSMTGT7-CCQuCU3y.js} +1 -1
  299. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-Bf8Ez6-D.js → gitGraphDiagram-PVQCEYII-B7gv9f4E.js} +1 -1
  300. package/web-shell/assets/{index-BXqw-nGq.js → index-BoUPVvpf.js} +1 -1
  301. package/web-shell/assets/index-D1YQXYsB.css +5 -0
  302. package/web-shell/assets/index-J4N7zCmU.js +1768 -0
  303. package/web-shell/assets/{infoDiagram-5YYISTIA-DVgTMLvj.js → infoDiagram-5YYISTIA-Bb3KYEbR.js} +1 -1
  304. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-uT-KepvK.js → ishikawaDiagram-YF4QCWOH-DWEzbAGi.js} +1 -1
  305. package/web-shell/assets/{journeyDiagram-JHISSGLW-Vg_XadQf.js → journeyDiagram-JHISSGLW-CmxwEqeb.js} +1 -1
  306. package/web-shell/assets/{kanban-definition-UN3LZRKU-Dyv1ESBE.js → kanban-definition-UN3LZRKU-Ci0gDD8O.js} +1 -1
  307. package/web-shell/assets/{linear-BMIepYX3.js → linear-BqxZqGhS.js} +1 -1
  308. package/web-shell/assets/{mermaid.core-zA8pJ9ag.js → mermaid.core-BDAFffHv.js} +5 -5
  309. package/web-shell/assets/{mindmap-definition-RKZ34NQL-BCW_BZ_5.js → mindmap-definition-RKZ34NQL-Btvx-xvS.js} +1 -1
  310. package/web-shell/assets/{pieDiagram-4H26LBE5-DseNEGp2.js → pieDiagram-4H26LBE5-CKAg7yG7.js} +1 -1
  311. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-6Z7wr1Fi.js → quadrantDiagram-W4KKPZXB-DIt8E1mC.js} +1 -1
  312. package/web-shell/assets/{requirementDiagram-4Y6WPE33-CVOlCRPf.js → requirementDiagram-4Y6WPE33-BA7mQWQJ.js} +1 -1
  313. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-CGhgrUJZ.js → sankeyDiagram-5OEKKPKP-D0JppnVU.js} +1 -1
  314. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-dlqmpLuQ.js → sequenceDiagram-3UESZ5HK-29DIQFjB.js} +1 -1
  315. package/web-shell/assets/{stateDiagram-AJRCARHV-B9EW10Xb.js → stateDiagram-AJRCARHV-iiXMdK--.js} +1 -1
  316. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-CEGK3l80.js +1 -0
  317. package/web-shell/assets/{timeline-definition-PNZ67QCA-BBRh0mgZ.js → timeline-definition-PNZ67QCA-CeF7ngSW.js} +1 -1
  318. package/web-shell/assets/{vennDiagram-CIIHVFJN-B8Gs-REP.js → vennDiagram-CIIHVFJN-Beilq8D6.js} +1 -1
  319. package/web-shell/assets/{wardley-L42UT6IY-D_y80Ur7.js → wardley-L42UT6IY-DOYjY4zO.js} +1 -1
  320. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-D-CNfHJ-.js → wardleyDiagram-YWT4CUSO-Dtqp8o18.js} +1 -1
  321. package/web-shell/assets/{xychartDiagram-2RQKCTM6-DQ5CvELo.js → xychartDiagram-2RQKCTM6-cdAc0Dfv.js} +1 -1
  322. package/web-shell/index.html +2 -2
  323. package/chunks/chunk-6PEXNQTX.js +0 -68
  324. package/chunks/chunk-IUFV6FYU.js +0 -81
  325. package/chunks/chunk-RXFQM6FQ.js +0 -42
  326. package/web-shell/assets/channel-Cu8qd6OB.js +0 -1
  327. package/web-shell/assets/classDiagram-4FO5ZUOK-BI3KrXhq.js +0 -1
  328. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BI3KrXhq.js +0 -1
  329. package/web-shell/assets/index-QQlUCJGg.css +0 -5
  330. package/web-shell/assets/index-hcgjV7vS.js +0 -1768
  331. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-CJTZJJ5X.js +0 -1
  332. package/chunks/{anthropicContentGenerator-DTMVEL5M.js → anthropicContentGenerator-SSFE2753.js} +9 -9
  333. /package/chunks/{chunk-VXZBKKQR.js → chunk-NTTBXH7U.js} +0 -0
  334. /package/chunks/{chunk-GRC5HGPI.js → chunk-OIBUG24X.js} +0 -0
@@ -17,17 +17,28 @@ import {
17
17
  } from "./chunk-OCPBI7J5.js";
18
18
  import {
19
19
  defaultSpawnChannelFactory
20
- } from "./chunk-E33LOKRA.js";
20
+ } from "./chunk-I5VM3SKY.js";
21
21
  import {
22
22
  BridgeChannelClosedError,
23
23
  BridgeTimeoutError,
24
24
  SERVE_CONTROL_EXT_METHODS,
25
25
  SERVE_STATUS_EXT_METHODS,
26
26
  STATUS_SCHEMA_VERSION,
27
+ SessionRestoreTimeoutError,
27
28
  createIdleWorkspaceExtensionsStatus,
28
29
  createIdleWorkspaceHooksStatus
29
- } from "./chunk-NLREQJFK.js";
30
+ } from "./chunk-DA4HRSBW.js";
30
31
  import {
32
+ ACTIVE_WORK_CLOSE_IF_UNHELD_PARAM,
33
+ ACTIVE_WORK_CLOSE_TIMEOUT_MS,
34
+ ACTIVE_WORK_HEARTBEAT_INTERVAL_MS,
35
+ ACTIVE_WORK_HEARTBEAT_META_KEY,
36
+ ACTIVE_WORK_HEARTBEAT_VERSION,
37
+ ACTIVE_WORK_HOLD_CATEGORIES,
38
+ ACTIVE_WORK_MAX_SESSION_HOLDS,
39
+ ACTIVE_WORK_MAX_SNAPSHOT_SESSIONS,
40
+ ACTIVE_WORK_NOTIFICATION_METHOD,
41
+ ACTIVE_WORK_STALE_INTERVALS,
31
42
  CHANNEL_STARTUP_PROFILE_META_KEY,
32
43
  CHANNEL_STARTUP_PROFILE_VERSION,
33
44
  DAEMON_CHANNEL_DELIVERY_META_KEY,
@@ -44,10 +55,12 @@ import {
44
55
  TODO_STOP_GUARD_CONTINUATION_CLAIM_METHOD,
45
56
  TODO_STOP_GUARD_QUEUE_RELEASE_METHOD,
46
57
  WORKTREE_MCP_DEFER_META_KEY,
58
+ clampActiveWorkIntervalMs,
47
59
  isValidTrustedModelPrompt
48
- } from "./chunk-R574ATRM.js";
60
+ } from "./chunk-M2PNLYGY.js";
49
61
  import {
50
62
  BranchWhilePromptActiveError,
63
+ BridgeChannelQuarantinedError,
51
64
  CancelSentinelCollisionError,
52
65
  CdWhilePromptActiveError,
53
66
  InvalidClientIdError,
@@ -66,7 +79,7 @@ import {
66
79
  SessionShellDisabledError,
67
80
  WorkspaceMismatchError,
68
81
  isNotCurrentlyGeneratingCancelError
69
- } from "./chunk-J26HHFI5.js";
82
+ } from "./chunk-LP2QGNSP.js";
70
83
  import {
71
84
  canonicalizeWorkspace,
72
85
  translateAndCheckAbsoluteWorkspacePath
@@ -100,16 +113,16 @@ import {
100
113
  metadataBudgetBytes,
101
114
  normalizeSnapshotPayload,
102
115
  stableSessionArtifactId
103
- } from "./chunk-Y7E5LNLO.js";
116
+ } from "./chunk-47ZUMGGY.js";
104
117
  import {
105
118
  DAEMON_TRACEPARENT_META_KEY,
106
119
  DAEMON_TRACESTATE_META_KEY
107
- } from "./chunk-L4N6TIP7.js";
120
+ } from "./chunk-4TRQPFDP.js";
108
121
  import {
109
122
  INVOCATION_CONTEXT_META_KEY,
110
123
  PRIVATE_ACP_CAPABILITY_ENV,
111
124
  PRIVATE_PARENT_CAPABILITY_META_KEY
112
- } from "./chunk-2U3WZGZG.js";
125
+ } from "./chunk-YGT2AUJ7.js";
113
126
  import {
114
127
  init_esbuild_shims
115
128
  } from "./chunk-5O2XNYP6.js";
@@ -751,6 +764,46 @@ function mergeTranscriptUpdateMeta(existing, incoming) {
751
764
  }
752
765
  __name(mergeTranscriptUpdateMeta, "mergeTranscriptUpdateMeta");
753
766
 
767
+ // packages/acp-bridge/src/session-restore-timeout.ts
768
+ init_esbuild_shims();
769
+ var DEFAULT_SESSION_RESTORE_TIMEOUT_MS = 6e4;
770
+ var MAX_SESSION_RESTORE_TIMEOUT_MS = 2147483647;
771
+ var MIN_RESTORE_RETRY_AFTER_SECONDS = 5;
772
+ var MAX_RESTORE_RETRY_AFTER_SECONDS = 120;
773
+ function restoreRetryAfterSeconds(timeoutMs) {
774
+ return Math.min(
775
+ MAX_RESTORE_RETRY_AFTER_SECONDS,
776
+ Math.max(MIN_RESTORE_RETRY_AFTER_SECONDS, Math.ceil(timeoutMs / 1e3))
777
+ );
778
+ }
779
+ __name(restoreRetryAfterSeconds, "restoreRetryAfterSeconds");
780
+ function assertValidTimeoutMs(field, timeoutMs) {
781
+ if (!Number.isFinite(timeoutMs) || !Number.isInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > MAX_SESSION_RESTORE_TIMEOUT_MS) {
782
+ throw new TypeError(
783
+ `Invalid ${field}: ${timeoutMs}. Must be a positive integer no greater than ${MAX_SESSION_RESTORE_TIMEOUT_MS}.`
784
+ );
785
+ }
786
+ }
787
+ __name(assertValidTimeoutMs, "assertValidTimeoutMs");
788
+ function resolveSessionRestoreTimeoutMs(opts) {
789
+ if (opts.sessionRestoreTimeoutMs !== void 0) {
790
+ assertValidTimeoutMs(
791
+ "sessionRestoreTimeoutMs",
792
+ opts.sessionRestoreTimeoutMs
793
+ );
794
+ return opts.sessionRestoreTimeoutMs;
795
+ }
796
+ if (opts.initializeTimeoutMs !== void 0) {
797
+ assertValidTimeoutMs("initializeTimeoutMs", opts.initializeTimeoutMs);
798
+ return Math.max(
799
+ opts.initializeTimeoutMs,
800
+ DEFAULT_SESSION_RESTORE_TIMEOUT_MS
801
+ );
802
+ }
803
+ return DEFAULT_SESSION_RESTORE_TIMEOUT_MS;
804
+ }
805
+ __name(resolveSessionRestoreTimeoutMs, "resolveSessionRestoreTimeoutMs");
806
+
754
807
  // packages/acp-bridge/src/session-source.ts
755
808
  init_esbuild_shims();
756
809
  var SESSION_SOURCE_META_KEY = "qwen.session.source";
@@ -1580,6 +1633,42 @@ var MultiClientPermissionMediator = class {
1580
1633
  };
1581
1634
 
1582
1635
  // packages/acp-bridge/src/bridgeClient.ts
1636
+ function parseActiveWorkSnapshot(params) {
1637
+ const seq = params["seq"];
1638
+ const sessions = params["sessions"];
1639
+ if (params["v"] !== ACTIVE_WORK_HEARTBEAT_VERSION || typeof seq !== "number" || !Number.isSafeInteger(seq) || seq <= 0 || !Array.isArray(sessions) || sessions.length > ACTIVE_WORK_MAX_SNAPSHOT_SESSIONS) {
1640
+ return void 0;
1641
+ }
1642
+ const parsed = [];
1643
+ for (const raw of sessions) {
1644
+ if (typeof raw !== "object" || raw === null) return void 0;
1645
+ const entry = raw;
1646
+ const sessionId = entry["sessionId"];
1647
+ const holds = entry["holds"];
1648
+ if (typeof sessionId !== "string" || !Array.isArray(holds) || holds.length > ACTIVE_WORK_MAX_SESSION_HOLDS) {
1649
+ return void 0;
1650
+ }
1651
+ const parsedHolds = [];
1652
+ for (const rawHold of holds) {
1653
+ if (typeof rawHold !== "object" || rawHold === null) return void 0;
1654
+ const hold = rawHold;
1655
+ const category = hold["category"];
1656
+ const id = hold["id"];
1657
+ if (typeof id !== "string" || typeof category !== "string" || !ACTIVE_WORK_HOLD_CATEGORIES.includes(
1658
+ category
1659
+ )) {
1660
+ return void 0;
1661
+ }
1662
+ parsedHolds.push({
1663
+ category,
1664
+ id
1665
+ });
1666
+ }
1667
+ parsed.push({ sessionId, holds: parsedHolds });
1668
+ }
1669
+ return { v: ACTIVE_WORK_HEARTBEAT_VERSION, seq, sessions: parsed };
1670
+ }
1671
+ __name(parseActiveWorkSnapshot, "parseActiveWorkSnapshot");
1583
1672
  var PUBLISH_ARTIFACT_TOOL_NAME = "artifact";
1584
1673
  var MAX_CHANNEL_DELIVERY_TEXT_CHARS = 1e5;
1585
1674
  var MAX_CHANNEL_DELIVERY_FIELD_CHARS = 2048;
@@ -1840,7 +1929,7 @@ function sliceLineRange(content, startLine, endLine) {
1840
1929
  }
1841
1930
  __name(sliceLineRange, "sliceLineRange");
1842
1931
  var BridgeClient = class {
1843
- constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender, ownsSession = () => true, onTokenUsage, onCreateSubSession, onGenerationEvent, onWorkspaceGenerationEvent, onChannelDelivery, hasSessionSpawnInFlight = () => false, getLiveScreenContextCaptureHandler = () => void 0, getLiveTaskToolRequestHandler = () => void 0, getLiveSpeakToUserHandler = () => void 0, externalToolGuard) {
1932
+ constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender, ownsSession = () => true, onTokenUsage, onCreateSubSession, onGenerationEvent, onWorkspaceGenerationEvent, onChannelDelivery, hasSessionSpawnInFlight = () => false, getLiveScreenContextCaptureHandler = () => void 0, getLiveTaskToolRequestHandler = () => void 0, getLiveSpeakToUserHandler = () => void 0, externalToolGuard, onActiveWork) {
1844
1933
  this.resolveEntry = resolveEntry;
1845
1934
  this.resolvePendingRestoreEvents = resolvePendingRestoreEvents;
1846
1935
  this.mediator = mediator;
@@ -1861,6 +1950,7 @@ var BridgeClient = class {
1861
1950
  this.getLiveTaskToolRequestHandler = getLiveTaskToolRequestHandler;
1862
1951
  this.getLiveSpeakToUserHandler = getLiveSpeakToUserHandler;
1863
1952
  this.externalToolGuard = externalToolGuard;
1953
+ this.onActiveWork = onActiveWork;
1864
1954
  }
1865
1955
  static {
1866
1956
  __name(this, "BridgeClient");
@@ -1929,6 +2019,9 @@ var BridgeClient = class {
1929
2019
  }
1930
2020
  }
1931
2021
  async sessionUpdate(params) {
2022
+ if (this.abandonedRestoreIds.has(params.sessionId)) {
2023
+ return;
2024
+ }
1932
2025
  if (!this.ownsSession(params.sessionId) && !this.inFlightRestoreIds.has(params.sessionId)) {
1933
2026
  writeStderrLine(
1934
2027
  `[demux] session=${params.sessionId} type=session_update action=dropped reason=session_not_owned`
@@ -2099,22 +2192,19 @@ var BridgeClient = class {
2099
2192
  * stale frame for that id anywhere in the system.
2100
2193
  */
2101
2194
  tombstonedSessionIds = /* @__PURE__ */ new Map();
2195
+ /** Restore ownership for `sessionUpdate` and artifact demultiplexing. */
2196
+ inFlightRestoreIds = /* @__PURE__ */ new Set();
2102
2197
  /**
2103
- * Allow-list of sessionIds currently being restored via
2104
- * `session/load` / `session/resume`. Bypasses the tombstone check
2105
- * in `bufferEarlyEvent` so restore-time events for a
2106
- * previously-closed id flow through to the future
2107
- * `createSessionEntry -> drainEarlyEvents` call.
2108
- *
2109
- * Without this, the tombstone set before a future `load` can clear
2110
- * it via `drainEarlyEvents` would silently drop legitimate
2111
- * restore-time events (e.g. MCP discovery budget events firing
2112
- * during the ACP call window).
2113
- *
2114
- * Bridge factory enters the set before awaiting the ACP restore
2115
- * call and exits on settle (success or failure).
2198
+ * Registrations allowed to buffer early extended notifications through an
2199
+ * ordinary close tombstone. This includes restores and caller-supplied-id
2200
+ * spawns, and lasts only until their ACP registration attempt settles.
2116
2201
  */
2117
- inFlightRestoreIds = /* @__PURE__ */ new Set();
2202
+ inFlightSessionRegistrationIds = /* @__PURE__ */ new Set();
2203
+ /**
2204
+ * Restore ids whose caller timed out while the non-cancellable ACP request
2205
+ * continues.
2206
+ */
2207
+ abandonedRestoreIds = /* @__PURE__ */ new Set();
2118
2208
  /**
2119
2209
  * Handle child->bridge ACP `extMethod` requests (calls that expect a
2120
2210
  * response, unlike `extNotification`). Served methods:
@@ -2581,6 +2671,23 @@ var BridgeClient = class {
2581
2671
  * forward-compat.
2582
2672
  */
2583
2673
  async extNotification(method, params) {
2674
+ const notificationSessionId = params["sessionId"];
2675
+ if (typeof notificationSessionId === "string" && this.abandonedRestoreIds.has(notificationSessionId)) {
2676
+ return;
2677
+ }
2678
+ if (method === ACTIVE_WORK_NOTIFICATION_METHOD) {
2679
+ const snapshot = parseActiveWorkSnapshot(params);
2680
+ if (snapshot) {
2681
+ this.onActiveWork?.({
2682
+ v: ACTIVE_WORK_HEARTBEAT_VERSION,
2683
+ seq: snapshot.seq,
2684
+ sessions: snapshot.sessions.filter(
2685
+ (session) => this.ownsSession(session.sessionId)
2686
+ )
2687
+ });
2688
+ }
2689
+ return;
2690
+ }
2584
2691
  if (method === "_qwencode/end_turn") {
2585
2692
  const sessionId2 = params["sessionId"];
2586
2693
  const reason = params["reason"];
@@ -2846,6 +2953,9 @@ var BridgeClient = class {
2846
2953
  }
2847
2954
  }
2848
2955
  publishExtNotification(sessionId, type, data, turnScoped = false) {
2956
+ if (this.abandonedRestoreIds.has(sessionId)) {
2957
+ return;
2958
+ }
2849
2959
  const entry = this.resolveEntry(sessionId);
2850
2960
  const frame = {
2851
2961
  type,
@@ -2991,9 +3101,12 @@ var BridgeClient = class {
2991
3101
  * dropped to bound per-session memory.
2992
3102
  */
2993
3103
  bufferEarlyEvent(sessionId, frame) {
3104
+ if (this.abandonedRestoreIds.has(sessionId)) {
3105
+ return;
3106
+ }
2994
3107
  const now = Date.now();
2995
3108
  this.sweepExpiredTombstones(now);
2996
- if (this.tombstonedSessionIds.has(sessionId) && !this.inFlightRestoreIds.has(sessionId)) {
3109
+ if (this.tombstonedSessionIds.has(sessionId) && !this.inFlightSessionRegistrationIds.has(sessionId)) {
2997
3110
  writeStderrLine(
2998
3111
  `qwen serve: dropping early extNotification for tombstoned session ${JSON.stringify(sessionId)} (post-close stale event)`
2999
3112
  );
@@ -3055,8 +3168,31 @@ var BridgeClient = class {
3055
3168
  * failed restore doesn't leave a dangling allow-list entry.
3056
3169
  */
3057
3170
  markRestoreInFlight(sessionId) {
3171
+ this.markSessionRegistrationInFlight(sessionId);
3058
3172
  this.inFlightRestoreIds.add(sessionId);
3059
3173
  }
3174
+ /**
3175
+ * Transfer an id from closed/abandoned ownership to a new registration
3176
+ * attempt before its ACP call starts. The in-flight allow-list is what lets
3177
+ * legitimate early notifications bypass the ordinary close tombstone until
3178
+ * `createSessionEntry` can drain them.
3179
+ */
3180
+ markSessionRegistrationInFlight(sessionId) {
3181
+ this.clearAbandonedRestoreFence(sessionId);
3182
+ this.inFlightSessionRegistrationIds.add(sessionId);
3183
+ }
3184
+ /**
3185
+ * Drop the abandoned-restore fence for `sessionId`.
3186
+ *
3187
+ * The fence has no TTL and suppresses session updates, guardrail events,
3188
+ * and child notifications, so it must not outlive the abandoned attempt it
3189
+ * was raised for. The bridge clears it whenever a legitimate owner takes
3190
+ * the id — a new restore, or `createSessionEntry` registering a session
3191
+ * from any other route.
3192
+ */
3193
+ clearAbandonedRestoreFence(sessionId) {
3194
+ this.abandonedRestoreIds.delete(sessionId);
3195
+ }
3060
3196
  /**
3061
3197
  * Companion to `markRestoreInFlight`. Bridge factory calls this when
3062
3198
  * the restore IIFE settles — after `createSessionEntry` runs
@@ -3065,6 +3201,16 @@ var BridgeClient = class {
3065
3201
  */
3066
3202
  clearRestoreInFlight(sessionId) {
3067
3203
  this.inFlightRestoreIds.delete(sessionId);
3204
+ this.clearSessionRegistrationInFlight(sessionId);
3205
+ }
3206
+ clearSessionRegistrationInFlight(sessionId) {
3207
+ this.inFlightSessionRegistrationIds.delete(sessionId);
3208
+ }
3209
+ markRestoreAbandoned(sessionId) {
3210
+ this.inFlightRestoreIds.delete(sessionId);
3211
+ this.inFlightSessionRegistrationIds.delete(sessionId);
3212
+ this.abandonedRestoreIds.add(sessionId);
3213
+ this.earlyEvents.delete(sessionId);
3068
3214
  }
3069
3215
  /**
3070
3216
  * Drain any frames buffered for `sessionId` onto `entry.events`.
@@ -6094,6 +6240,7 @@ function createAcpSessionBridge(opts) {
6094
6240
  let liveTaskToolRequestHandler;
6095
6241
  let liveSpeakToUserHandler;
6096
6242
  const defaultSessionScope = opts.sessionScope ?? "single";
6243
+ const delegateReadTextFileToClient = opts.delegateReadTextFileToClient ?? true;
6097
6244
  let maxSessions;
6098
6245
  if (opts.maxSessions === void 0) {
6099
6246
  maxSessions = DEFAULT_MAX_SESSIONS;
@@ -6110,7 +6257,31 @@ function createAcpSessionBridge(opts) {
6110
6257
  } else {
6111
6258
  maxSessions = opts.maxSessions;
6112
6259
  }
6113
- const reserveFreshSession = /* @__PURE__ */ __name((context) => opts.freshSessionAdmission?.(context), "reserveFreshSession");
6260
+ const freshSessionBlocker = /* @__PURE__ */ __name(() => {
6261
+ for (const ci of aliveChannels) {
6262
+ if (ci.isDying) continue;
6263
+ if (ci.isQuarantined) {
6264
+ return { channel: ci, reason: "restore_cleanup_failed" };
6265
+ }
6266
+ if (ci.restoreSettlementOverdue) {
6267
+ return { channel: ci, reason: "restore_settlement_overdue" };
6268
+ }
6269
+ }
6270
+ return void 0;
6271
+ }, "freshSessionBlocker");
6272
+ const assertFreshSessionsAvailable = /* @__PURE__ */ __name(() => {
6273
+ const blocker = freshSessionBlocker();
6274
+ if (blocker) {
6275
+ throw new BridgeChannelQuarantinedError(
6276
+ blocker.reason,
6277
+ abandonedRestoreRetryAfterSeconds
6278
+ );
6279
+ }
6280
+ }, "assertFreshSessionsAvailable");
6281
+ const reserveFreshSession = /* @__PURE__ */ __name((context) => {
6282
+ assertFreshSessionsAvailable();
6283
+ return opts.freshSessionAdmission?.(context);
6284
+ }, "reserveFreshSession");
6114
6285
  const releaseFreshSessionReservation = /* @__PURE__ */ __name((reservation) => {
6115
6286
  if (!reservation) return;
6116
6287
  try {
@@ -6155,6 +6326,11 @@ function createAcpSessionBridge(opts) {
6155
6326
  `Invalid initializeTimeoutMs: ${initTimeoutMs}. Must be > 0.`
6156
6327
  );
6157
6328
  }
6329
+ const sessionRestoreTimeoutMs = resolveSessionRestoreTimeoutMs(opts);
6330
+ const restoreSettlementGraceMs = sessionRestoreTimeoutMs;
6331
+ const abandonedRestoreRetryAfterSeconds = restoreRetryAfterSeconds(
6332
+ sessionRestoreTimeoutMs
6333
+ );
6158
6334
  const permissionTimeoutRaw = opts.permissionResponseTimeoutMs ?? DEFAULT_PERMISSION_TIMEOUT_MS;
6159
6335
  const permissionTimeoutMs = permissionTimeoutRaw > 0 && Number.isFinite(permissionTimeoutRaw) ? (
6160
6336
  // Clamp to 2^31-1: Node treats setTimeout delays larger than
@@ -6206,6 +6382,153 @@ function createAcpSessionBridge(opts) {
6206
6382
  lastActivityTimestamp = Date.now();
6207
6383
  }
6208
6384
  __name(touchActivity, "touchActivity");
6385
+ function entryHasLocalWork(entry) {
6386
+ return entry.pendingPromptCount > 0 || entry.pendingAgentNotificationCount > 0;
6387
+ }
6388
+ __name(entryHasLocalWork, "entryHasLocalWork");
6389
+ function childHoldsAreFresh(entry, capability) {
6390
+ if (entry.childHoldsAt === null) return false;
6391
+ return Date.now() - entry.childHoldsAt <= capability.intervalMs * ACTIVE_WORK_STALE_INTERVALS;
6392
+ }
6393
+ __name(childHoldsAreFresh, "childHoldsAreFresh");
6394
+ function childReportsHeldWork(entry) {
6395
+ const owner = channelInfoForEntry(entry);
6396
+ if (!owner?.activeWork) return false;
6397
+ if (!childHoldsAreFresh(entry, owner.activeWork)) return false;
6398
+ return entry.childHolds !== null && entry.childHolds.size > 0;
6399
+ }
6400
+ __name(childReportsHeldWork, "childReportsHeldWork");
6401
+ function childWorkIsUnknown(entry) {
6402
+ const owner = channelInfoForEntry(entry);
6403
+ if (!owner?.activeWork) return false;
6404
+ return !childHoldsAreFresh(entry, owner.activeWork);
6405
+ }
6406
+ __name(childWorkIsUnknown, "childWorkIsUnknown");
6407
+ function entryHasActiveWork(entry) {
6408
+ return entryHasLocalWork(entry) || childReportsHeldWork(entry) || childWorkIsUnknown(entry);
6409
+ }
6410
+ __name(entryHasActiveWork, "entryHasActiveWork");
6411
+ function entryIsAutoCloseCandidate(entry) {
6412
+ if (byId.get(entry.sessionId) !== entry) return false;
6413
+ if (isClosingOrAuthorizingClose(entry)) return false;
6414
+ if (entry.events.subscriberCount > 0) return false;
6415
+ if (entryHasLocalWork(entry)) return false;
6416
+ const owner = channelInfoForEntry(entry);
6417
+ if (owner?.pendingRestoreIds.has(entry.sessionId)) return false;
6418
+ return !childReportsHeldWork(entry);
6419
+ }
6420
+ __name(entryIsAutoCloseCandidate, "entryIsAutoCloseCandidate");
6421
+ function isClosingOrAuthorizingClose(entry) {
6422
+ return entry.closing || entry.activeWorkCloseInFlight;
6423
+ }
6424
+ __name(isClosingOrAuthorizingClose, "isClosingOrAuthorizingClose");
6425
+ async function maybeCloseIdleSession(entry, reason) {
6426
+ if (!entryIsAutoCloseCandidate(entry)) return;
6427
+ if (entry.spawnOwnerWantedKill && entry.attachCount === 0) {
6428
+ await bridgeApi.killSession(entry.sessionId).catch(() => {
6429
+ });
6430
+ return;
6431
+ }
6432
+ if (entry.clientIds.size > 0) return;
6433
+ await closeIfChildUnheld(entry, {
6434
+ trigger: reason,
6435
+ closeReason: "last_client_detached"
6436
+ });
6437
+ }
6438
+ __name(maybeCloseIdleSession, "maybeCloseIdleSession");
6439
+ async function closeIfChildUnheld(entry, opts2) {
6440
+ entry.activeWorkCloseInFlight = true;
6441
+ try {
6442
+ if (!await confirmChildUnheld(entry)) return;
6443
+ if (byId.get(entry.sessionId) !== entry) return;
6444
+ const condemnedOwner = channelInfoForEntry(entry);
6445
+ const agentCloseTimeoutMs = condemnedOwner?.isQuarantined === true || condemnedOwner?.restoreSettlementOverdue === true ? ACTIVE_WORK_CLOSE_TIMEOUT_MS : void 0;
6446
+ await closeSessionImpl(entry.sessionId, void 0, {
6447
+ reason: opts2.closeReason,
6448
+ ...agentCloseTimeoutMs !== void 0 ? { agentCloseTimeoutMs } : {}
6449
+ }).catch((err) => {
6450
+ writeStderrLine(
6451
+ `qwen serve: deferred close (${opts2.trigger}) failed for ${JSON.stringify(entry.sessionId)}: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
6452
+ );
6453
+ });
6454
+ } finally {
6455
+ entry.activeWorkCloseInFlight = false;
6456
+ }
6457
+ }
6458
+ __name(closeIfChildUnheld, "closeIfChildUnheld");
6459
+ async function confirmChildUnheld(entry) {
6460
+ const info = channelInfoForEntry(entry);
6461
+ if (!info?.activeWork) return true;
6462
+ if (info.isDying) return false;
6463
+ if (info.isQuarantined || info.restoreSettlementOverdue) return true;
6464
+ try {
6465
+ const response = await withTimeout(
6466
+ entry.connection.extMethod(SERVE_CONTROL_EXT_METHODS.sessionClose, {
6467
+ sessionId: entry.sessionId,
6468
+ [ACTIVE_WORK_CLOSE_IF_UNHELD_PARAM]: true
6469
+ }),
6470
+ ACTIVE_WORK_CLOSE_TIMEOUT_MS,
6471
+ SERVE_CONTROL_EXT_METHODS.sessionClose
6472
+ );
6473
+ if (response["closed"] === true) {
6474
+ return true;
6475
+ }
6476
+ const holds = response["holds"];
6477
+ if (Array.isArray(holds)) {
6478
+ const adopted = /* @__PURE__ */ new Map();
6479
+ for (const hold of holds) {
6480
+ if (typeof hold !== "object" || hold === null) continue;
6481
+ const record = hold;
6482
+ const id = record["id"];
6483
+ const category = record["category"];
6484
+ if (typeof id === "string" && typeof category === "string" && ACTIVE_WORK_HOLD_CATEGORIES.includes(
6485
+ category
6486
+ )) {
6487
+ adopted.set(id, category);
6488
+ }
6489
+ }
6490
+ entry.childHolds = adopted;
6491
+ entry.childHoldsAt = Date.now();
6492
+ }
6493
+ return false;
6494
+ } catch (err) {
6495
+ writeStderrLine(
6496
+ `qwen serve: close-if-unheld for session ${JSON.stringify(entry.sessionId)} did not resolve (${err instanceof Error ? err.message : String(err)}); leaving it in place for the next snapshot to settle`
6497
+ );
6498
+ return false;
6499
+ }
6500
+ }
6501
+ __name(confirmChildUnheld, "confirmChildUnheld");
6502
+ function applyActiveWorkSnapshot(info, snapshot) {
6503
+ if (!info.activeWork || info.isDying) return;
6504
+ if (snapshot.seq <= info.activeWork.seq) return;
6505
+ info.activeWork.seq = snapshot.seq;
6506
+ const now = Date.now();
6507
+ const reported = /* @__PURE__ */ new Map();
6508
+ for (const session of snapshot.sessions) {
6509
+ const holds = /* @__PURE__ */ new Map();
6510
+ for (const hold of session.holds) holds.set(hold.id, hold.category);
6511
+ reported.set(session.sessionId, holds);
6512
+ }
6513
+ for (const sessionId of Array.from(info.sessionIds)) {
6514
+ const entry = byId.get(sessionId);
6515
+ if (!entry || entry.channel !== info.channel) continue;
6516
+ const holds = reported.get(sessionId) ?? /* @__PURE__ */ new Map();
6517
+ const previouslyHeld = entry.childHolds ? entry.childHolds.size > 0 : void 0;
6518
+ entry.childHolds = holds;
6519
+ entry.childHoldsAt = now;
6520
+ if (previouslyHeld !== void 0 && previouslyHeld !== holds.size > 0) {
6521
+ touchActivity();
6522
+ }
6523
+ if (holds.size === 0) {
6524
+ void maybeCloseIdleSession(
6525
+ entry,
6526
+ reported.has(sessionId) ? "child_idle" : "child_dropped"
6527
+ );
6528
+ }
6529
+ }
6530
+ }
6531
+ __name(applyActiveWorkSnapshot, "applyActiveWorkSnapshot");
6209
6532
  function settleActivePromptState(entry, promptId) {
6210
6533
  if (entry.activePromptId !== promptId) return;
6211
6534
  delete entry.activePromptId;
@@ -6301,8 +6624,35 @@ function createAcpSessionBridge(opts) {
6301
6624
  workspaceMcpResourcesCache.delete(serverName);
6302
6625
  }
6303
6626
  __name(invalidateWorkspaceMcpDetailCache, "invalidateWorkspaceMcpDetailCache");
6627
+ function channelShouldReapWhenIdle(ci) {
6628
+ return ci.emptyReapPending || ci.unsettledAbandonedRestores.size > 0 || ci.isQuarantined;
6629
+ }
6630
+ __name(channelShouldReapWhenIdle, "channelShouldReapWhenIdle");
6631
+ function armRestoreSettlementGrace(ci, sessionId, action) {
6632
+ if (ci.restoreSettlementTimers.has(sessionId)) return;
6633
+ const timer = setTimeout(() => {
6634
+ ci.restoreSettlementTimers.delete(sessionId);
6635
+ if (!ci.unsettledAbandonedRestores.has(sessionId)) return;
6636
+ if (ci.isDying || !aliveChannels.has(ci)) return;
6637
+ ci.restoreSettlementOverdue = true;
6638
+ writeStderrLine(
6639
+ `qwen serve: abandoned session/${action} for ${JSON.stringify(sessionId)} has not settled ${restoreSettlementGraceMs}ms after its deadline; refusing fresh sessions on channel ${ci.id} until it drains`
6640
+ );
6641
+ telemetry.event("session.restore.settlement_overdue", {
6642
+ "qwen-code.daemon.session_restore.action": action,
6643
+ "qwen-code.daemon.session_restore.timeout_ms": sessionRestoreTimeoutMs,
6644
+ "qwen-code.daemon.session_restore.settlement_grace_ms": restoreSettlementGraceMs,
6645
+ "qwen-code.daemon.acp_channel.id": ci.id,
6646
+ "session.id": sessionId
6647
+ });
6648
+ void reapPendingEmptyChannel(ci);
6649
+ }, restoreSettlementGraceMs);
6650
+ timer.unref();
6651
+ ci.restoreSettlementTimers.set(sessionId, timer);
6652
+ }
6653
+ __name(armRestoreSettlementGrace, "armRestoreSettlementGrace");
6304
6654
  async function reapPendingEmptyChannel(ci) {
6305
- if (!ci.emptyReapPending || !hasNoChannelWork(ci)) return;
6655
+ if (!channelShouldReapWhenIdle(ci) || !hasNoChannelWork(ci)) return;
6306
6656
  ci.emptyReapPending = false;
6307
6657
  ci.isDying = true;
6308
6658
  await ci.channel.kill().catch(() => {
@@ -6334,21 +6684,17 @@ function createAcpSessionBridge(opts) {
6334
6684
  if (shuttingDown) return;
6335
6685
  const now = Date.now();
6336
6686
  for (const [id, entry] of byId) {
6337
- if (entry.pendingPromptCount > 0) continue;
6338
- if (entry.events.subscriberCount > 0) continue;
6687
+ if (!entryIsAutoCloseCandidate(entry)) continue;
6339
6688
  const lastActive = entry.sessionLastSeenAt ?? Date.parse(entry.createdAt);
6340
6689
  const idle = now - lastActive;
6341
6690
  if (idle < sessionIdleTimeoutMs) continue;
6342
6691
  writeStderrLine(
6343
6692
  `qwen serve: reaping idle session ${JSON.stringify(id)} (idle for ${Math.round(idle / 1e3)}s, threshold ${Math.round(sessionIdleTimeoutMs / 1e3)}s)`
6344
6693
  );
6345
- void closeSessionImpl(id, void 0, { reason: "idle_timeout" }).catch(
6346
- (err) => {
6347
- writeStderrLine(
6348
- `qwen serve: session reaper failed to close ${JSON.stringify(id)}: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
6349
- );
6350
- }
6351
- );
6694
+ void closeIfChildUnheld(entry, {
6695
+ trigger: "idle_timeout",
6696
+ closeReason: "idle_timeout"
6697
+ });
6352
6698
  }
6353
6699
  }, sessionReapIntervalMs);
6354
6700
  sessionReaper.unref();
@@ -6476,6 +6822,7 @@ function createAcpSessionBridge(opts) {
6476
6822
  }
6477
6823
  }, "teeServeDebugLine");
6478
6824
  const inFlightSpawns = /* @__PURE__ */ new Map();
6825
+ const inFlightRequestedSessionSpawns = /* @__PURE__ */ new Map();
6479
6826
  const inFlightRestores = /* @__PURE__ */ new Map();
6480
6827
  const pendingRestoreEvents = /* @__PURE__ */ new Map();
6481
6828
  const createClientId = /* @__PURE__ */ __name(() => `client_${randomUUID2()}`, "createClientId");
@@ -6522,18 +6869,7 @@ function createAcpSessionBridge(opts) {
6522
6869
  entry.attachCount - (released + (attachCountDelta - 1))
6523
6870
  );
6524
6871
  unregisterClient(entry, clientId);
6525
- if (entry.spawnOwnerWantedKill && entry.attachCount === 0 && entry.events.subscriberCount === 0) {
6526
- await bridgeApi.killSession(entry.sessionId).catch(() => {
6527
- });
6528
- } else if (entry.clientIds.size === 0 && entry.events.subscriberCount === 0 && entry.pendingPromptCount === 0) {
6529
- await closeSessionImpl(entry.sessionId, void 0, {
6530
- reason: "last_client_detached"
6531
- }).catch((err) => {
6532
- writeStderrLine(
6533
- `qwen serve: close-on-attach-rollback failed for ${JSON.stringify(entry.sessionId)}: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
6534
- );
6535
- });
6536
- }
6872
+ await maybeCloseIdleSession(entry, "attach_rollback");
6537
6873
  }, "rollbackAttachRegistration");
6538
6874
  const resolveTrustedClientId = /* @__PURE__ */ __name((entry, clientId) => {
6539
6875
  if (clientId === void 0) return void 0;
@@ -6562,6 +6898,7 @@ function createAcpSessionBridge(opts) {
6562
6898
  })
6563
6899
  );
6564
6900
  const sessionIds = /* @__PURE__ */ new Set();
6901
+ const infoRef = {};
6565
6902
  let client;
6566
6903
  let connection;
6567
6904
  try {
@@ -6663,7 +7000,12 @@ function createAcpSessionBridge(opts) {
6663
7000
  () => liveScreenContextCaptureHandler,
6664
7001
  () => liveTaskToolRequestHandler,
6665
7002
  () => liveSpeakToUserHandler,
6666
- opts.externalToolGuard
7003
+ opts.externalToolGuard,
7004
+ (snapshot) => {
7005
+ const currentInfo = infoRef.current;
7006
+ if (!currentInfo) return;
7007
+ applyActiveWorkSnapshot(currentInfo, snapshot);
7008
+ }
6667
7009
  );
6668
7010
  connection = new ClientSideConnection(() => client, channel.stream);
6669
7011
  } catch (error) {
@@ -6698,9 +7040,14 @@ function createAcpSessionBridge(opts) {
6698
7040
  workspaceMcpAuthenticationServerNames: /* @__PURE__ */ new Set(),
6699
7041
  workspaceMcpAuthenticationTimers: /* @__PURE__ */ new Map(),
6700
7042
  emptyReapPending: false,
7043
+ unsettledAbandonedRestores: /* @__PURE__ */ new Set(),
7044
+ restoreSettlementOverdue: false,
7045
+ restoreSettlementTimers: /* @__PURE__ */ new Map(),
6701
7046
  isDying: false,
7047
+ isQuarantined: false,
6702
7048
  handshakeComplete: false
6703
7049
  };
7050
+ infoRef.current = info;
6704
7051
  aliveChannels.add(info);
6705
7052
  if (aliveChannels.size > 2) {
6706
7053
  writeStderrLine(
@@ -6718,6 +7065,10 @@ function createAcpSessionBridge(opts) {
6718
7065
  }
6719
7066
  info.workspaceMcpAuthenticationTimers.clear();
6720
7067
  info.workspaceMcpAuthenticationServerNames.clear();
7068
+ for (const timer of info.restoreSettlementTimers.values()) {
7069
+ clearTimeout(timer);
7070
+ }
7071
+ info.restoreSettlementTimers.clear();
6721
7072
  aliveChannels.delete(info);
6722
7073
  if (channelInfo === info) channelInfo = void 0;
6723
7074
  const sessions = Array.from(info.sessionIds);
@@ -6788,13 +7139,20 @@ function createAcpSessionBridge(opts) {
6788
7139
  connection.initialize({
6789
7140
  protocolVersion: PROTOCOL_VERSION,
6790
7141
  _meta: {
7142
+ [ACTIVE_WORK_HEARTBEAT_META_KEY]: {
7143
+ v: ACTIVE_WORK_HEARTBEAT_VERSION,
7144
+ intervalMs: ACTIVE_WORK_HEARTBEAT_INTERVAL_MS
7145
+ },
6791
7146
  [CHANNEL_STARTUP_PROFILE_META_KEY]: {
6792
7147
  v: CHANNEL_STARTUP_PROFILE_VERSION
6793
7148
  },
6794
7149
  [PRIVATE_PARENT_CAPABILITY_META_KEY]: privateParentCapability
6795
7150
  },
6796
7151
  clientCapabilities: {
6797
- fs: { readTextFile: true, writeTextFile: true }
7152
+ fs: {
7153
+ readTextFile: delegateReadTextFileToClient,
7154
+ writeTextFile: true
7155
+ }
6798
7156
  },
6799
7157
  clientInfo: { name: "qwen-serve-bridge", version: "0" }
6800
7158
  }),
@@ -6809,6 +7167,19 @@ function createAcpSessionBridge(opts) {
6809
7167
  );
6810
7168
  }
6811
7169
  }
7170
+ const activeWorkCapability = isRecord3(response._meta) ? response._meta[ACTIVE_WORK_HEARTBEAT_META_KEY] : void 0;
7171
+ if (isRecord3(activeWorkCapability) && activeWorkCapability["v"] === ACTIVE_WORK_HEARTBEAT_VERSION) {
7172
+ const advertised = activeWorkCapability["categories"];
7173
+ info.activeWork = {
7174
+ intervalMs: clampActiveWorkIntervalMs(
7175
+ activeWorkCapability["intervalMs"]
7176
+ ),
7177
+ categories: Array.isArray(advertised) ? ACTIVE_WORK_HOLD_CATEGORIES.filter(
7178
+ (category) => advertised.includes(category)
7179
+ ) : [],
7180
+ seq: 0
7181
+ };
7182
+ }
6812
7183
  try {
6813
7184
  const attributes = getChannelStartupProfileAttributes(
6814
7185
  response,
@@ -6859,6 +7230,9 @@ function createAcpSessionBridge(opts) {
6859
7230
  ensureChannel
6860
7231
  );
6861
7232
  ci.sessionSpawnsInFlight++;
7233
+ if (requestedSessionId !== void 0) {
7234
+ ci.client.markSessionRegistrationInFlight(requestedSessionId);
7235
+ }
6862
7236
  let sessionRegistered = false;
6863
7237
  let sessionRemovedDuringInitialization = false;
6864
7238
  let initializedSessionId;
@@ -7043,6 +7417,12 @@ function createAcpSessionBridge(opts) {
7043
7417
  ...entry.branch ? { branch: entry.branch } : {}
7044
7418
  };
7045
7419
  } finally {
7420
+ if (requestedSessionId !== void 0) {
7421
+ ci.client.clearSessionRegistrationInFlight(requestedSessionId);
7422
+ if (initializedSessionId !== requestedSessionId && !byId.has(requestedSessionId)) {
7423
+ ci.client.markSessionClosed(requestedSessionId);
7424
+ }
7425
+ }
7046
7426
  ci.sessionSpawnsInFlight = Math.max(0, ci.sessionSpawnsInFlight - 1);
7047
7427
  if (!sessionRegistered) {
7048
7428
  await reapPendingEmptyChannel(ci);
@@ -7457,12 +7837,17 @@ function createAcpSessionBridge(opts) {
7457
7837
  const getChildResourceSnapshot = /* @__PURE__ */ __name(() => {
7458
7838
  const info = liveChannelInfo();
7459
7839
  if (!info || info.childResourceAt === void 0) return void 0;
7460
- if (Date.now() - info.childResourceAt > STALE_CHILD_RESOURCE_MS) {
7840
+ const ageMs = Date.now() - info.childResourceAt;
7841
+ if (ageMs > STALE_CHILD_RESOURCE_MS) {
7461
7842
  return void 0;
7462
7843
  }
7463
7844
  return {
7464
7845
  rssBytes: info.childRssBytes ?? 0,
7465
- cpuPercent: info.childCpuPercent ?? 0
7846
+ cpuPercent: info.childCpuPercent ?? 0,
7847
+ // Bounded by the guard above, so a caller summing several children's
7848
+ // readings can say how far apart they were taken. Without it a sum of
7849
+ // readings up to `STALE_CHILD_RESOURCE_MS` apart looks instantaneous.
7850
+ ageMs
7466
7851
  };
7467
7852
  }, "getChildResourceSnapshot");
7468
7853
  const requestSessionStatus = /* @__PURE__ */ __name(async (sessionId, method, params = {}, timeoutMs = initTimeoutMs) => {
@@ -7688,6 +8073,7 @@ function createAcpSessionBridge(opts) {
7688
8073
  cwdChangeQueue: Promise.resolve(),
7689
8074
  promptQueue: Promise.resolve(),
7690
8075
  pendingPromptCount: 0,
8076
+ pendingAgentNotificationCount: 0,
7691
8077
  pendingPromptList: [],
7692
8078
  midTurnMessageQueue: [],
7693
8079
  modelChangeQueue: Promise.resolve(),
@@ -7702,10 +8088,14 @@ function createAcpSessionBridge(opts) {
7702
8088
  attachRefs: /* @__PURE__ */ new Map(),
7703
8089
  spawnOwnerWantedKill: false,
7704
8090
  promptActive: false,
8091
+ childHolds: null,
8092
+ childHoldsAt: null,
8093
+ activeWorkCloseInFlight: false,
7705
8094
  retryAllowed: false
7706
8095
  };
7707
8096
  ci.sessionIds.add(entry.sessionId);
7708
8097
  byId.set(entry.sessionId, entry);
8098
+ ci.client.clearAbandonedRestoreFence(entry.sessionId);
7709
8099
  touchActivity();
7710
8100
  telemetry.metrics?.sessionLifecycle("spawn");
7711
8101
  emitSessionLifecycle({
@@ -8012,7 +8402,7 @@ function createAcpSessionBridge(opts) {
8012
8402
  const hideInheritedHistory = action === "load" && req.hideInheritedHistory === true;
8013
8403
  const existing = byId.get(req.sessionId);
8014
8404
  if (existing) {
8015
- if (existing.closing) {
8405
+ if (isClosingOrAuthorizingClose(existing)) {
8016
8406
  throw new SessionNotFoundError(
8017
8407
  req.sessionId,
8018
8408
  "The session is closing; retry after close completes"
@@ -8020,7 +8410,7 @@ function createAcpSessionBridge(opts) {
8020
8410
  }
8021
8411
  const replayFields = action === "load" && req.historyPageSize !== void 0 ? await refreshedReplayFieldsFor(existing, req.historyPageSize) : replayFieldsFor(existing, action);
8022
8412
  const historyAnchorRecordId = action === "load" ? await resolveHistoryAnchorRecordId(existing, replayFields) : void 0;
8023
- if (byId.get(req.sessionId) !== existing || existing.closing) {
8413
+ if (byId.get(req.sessionId) !== existing || isClosingOrAuthorizingClose(existing)) {
8024
8414
  throw new SessionNotFoundError(req.sessionId);
8025
8415
  }
8026
8416
  existing.attachCount++;
@@ -8046,19 +8436,27 @@ function createAcpSessionBridge(opts) {
8046
8436
  ...historyAnchorRecordId !== void 0 ? { historyAnchorRecordId } : {}
8047
8437
  };
8048
8438
  }
8439
+ if (inFlightRequestedSessionSpawns.has(req.sessionId)) {
8440
+ throw new RestoreInProgressError(req.sessionId, "spawn", action);
8441
+ }
8049
8442
  const inFlight = inFlightRestores.get(req.sessionId);
8050
8443
  if (inFlight) {
8051
- if (action !== inFlight.action || historyReplay !== inFlight.historyReplay || hideInheritedHistory !== inFlight.hideInheritedHistory) {
8444
+ if (inFlight.lifecycle.phase === "abandoned" || action !== inFlight.action || historyReplay !== inFlight.historyReplay || hideInheritedHistory !== inFlight.hideInheritedHistory) {
8445
+ const abandoned = inFlight.lifecycle.phase === "abandoned";
8052
8446
  throw new RestoreInProgressError(
8053
8447
  req.sessionId,
8054
8448
  inFlight.action,
8055
- action
8449
+ action,
8450
+ abandoned ? {
8451
+ reason: "awaiting_abandoned_cleanup",
8452
+ retryAfterSeconds: abandonedRestoreRetryAfterSeconds
8453
+ } : void 0
8056
8454
  );
8057
8455
  }
8058
8456
  inFlight.coalesceState.count++;
8059
8457
  let restored;
8060
8458
  try {
8061
- restored = await inFlight.promise;
8459
+ restored = await inFlight.publicPromise;
8062
8460
  } catch (err) {
8063
8461
  inFlight.coalesceState.count--;
8064
8462
  throw err;
@@ -8084,6 +8482,7 @@ function createAcpSessionBridge(opts) {
8084
8482
  hasActivePrompt: entry.promptActive
8085
8483
  };
8086
8484
  }
8485
+ assertFreshSessionsAvailable();
8087
8486
  if (byId.size + inFlightSpawns.size + inFlightRestores.size >= maxSessions) {
8088
8487
  throw new SessionLimitExceededError(maxSessions);
8089
8488
  }
@@ -8102,12 +8501,145 @@ function createAcpSessionBridge(opts) {
8102
8501
  admissionReleased = true;
8103
8502
  releaseFreshSessionReservation(admission);
8104
8503
  }, "releaseAdmissionOnce");
8504
+ let resolveSettlement;
8505
+ const settlementPromise = new Promise((resolve2) => {
8506
+ resolveSettlement = resolve2;
8507
+ });
8508
+ const restoreLifecycle = {
8509
+ phase: "active"
8510
+ };
8511
+ const settleAbandonedRestore = /* @__PURE__ */ __name(async (channel, lateResult) => {
8512
+ telemetry.event("session.restore.late_result", {
8513
+ "qwen-code.daemon.session_restore.action": action,
8514
+ "qwen-code.daemon.session_restore.result": lateResult,
8515
+ "qwen-code.daemon.session_restore.timeout_ms": sessionRestoreTimeoutMs,
8516
+ "qwen-code.daemon.acp_channel.id": channel.id,
8517
+ "session.id": req.sessionId
8518
+ });
8519
+ const usurper = byId.get(req.sessionId);
8520
+ if (usurper) {
8521
+ writeStderrLine(
8522
+ `qwen serve: skipping abandoned session/${action} cleanup for ${JSON.stringify(req.sessionId)}: the id is now owned by a live session`
8523
+ );
8524
+ telemetry.event("session.restore.cleanup", {
8525
+ "qwen-code.daemon.session_restore.action": action,
8526
+ "qwen-code.daemon.session_restore.cleanup_result": "id_reclaimed",
8527
+ "qwen-code.daemon.session_restore.timeout_ms": sessionRestoreTimeoutMs,
8528
+ "qwen-code.daemon.acp_channel.id": channel.id,
8529
+ "session.id": req.sessionId
8530
+ });
8531
+ channel.unsettledAbandonedRestores.delete(req.sessionId);
8532
+ const reclaimedTimer = channel.restoreSettlementTimers.get(
8533
+ req.sessionId
8534
+ );
8535
+ if (reclaimedTimer !== void 0) {
8536
+ clearTimeout(reclaimedTimer);
8537
+ channel.restoreSettlementTimers.delete(req.sessionId);
8538
+ }
8539
+ if (channel.unsettledAbandonedRestores.size === 0) {
8540
+ channel.restoreSettlementOverdue = false;
8541
+ }
8542
+ releaseAdmissionOnce();
8543
+ resolveSettlement();
8544
+ return;
8545
+ }
8546
+ try {
8547
+ if (channel.isDying || !aliveChannels.has(channel)) {
8548
+ await channel.channel.exited;
8549
+ telemetry.event("session.restore.cleanup", {
8550
+ "qwen-code.daemon.session_restore.action": action,
8551
+ "qwen-code.daemon.session_restore.cleanup_result": "transport_closed",
8552
+ "qwen-code.daemon.session_restore.timeout_ms": sessionRestoreTimeoutMs,
8553
+ "qwen-code.daemon.acp_channel.id": channel.id,
8554
+ "session.id": req.sessionId
8555
+ });
8556
+ return;
8557
+ }
8558
+ try {
8559
+ await Promise.race([
8560
+ withTimeout(
8561
+ channel.connection.extMethod(
8562
+ SERVE_CONTROL_EXT_METHODS.sessionClose,
8563
+ {
8564
+ sessionId: req.sessionId,
8565
+ drainTimeoutMs: Math.max(1, Math.floor(initTimeoutMs * 0.8))
8566
+ }
8567
+ ),
8568
+ initTimeoutMs,
8569
+ "abandonedRestoreClose"
8570
+ ),
8571
+ getChannelClosedReject(channel)
8572
+ ]);
8573
+ telemetry.event("session.restore.cleanup", {
8574
+ "qwen-code.daemon.session_restore.action": action,
8575
+ "qwen-code.daemon.session_restore.cleanup_result": "closed",
8576
+ "qwen-code.daemon.session_restore.timeout_ms": sessionRestoreTimeoutMs,
8577
+ "qwen-code.daemon.acp_channel.id": channel.id,
8578
+ "session.id": req.sessionId
8579
+ });
8580
+ } catch (error) {
8581
+ if (isAcpSessionResourceNotFound(error, req.sessionId)) {
8582
+ telemetry.event("session.restore.cleanup", {
8583
+ "qwen-code.daemon.session_restore.action": action,
8584
+ "qwen-code.daemon.session_restore.cleanup_result": "not_found",
8585
+ "qwen-code.daemon.session_restore.timeout_ms": sessionRestoreTimeoutMs,
8586
+ "qwen-code.daemon.acp_channel.id": channel.id,
8587
+ "session.id": req.sessionId
8588
+ });
8589
+ return;
8590
+ }
8591
+ if (channel.isDying || !aliveChannels.has(channel)) {
8592
+ await channel.channel.exited;
8593
+ telemetry.event("session.restore.cleanup", {
8594
+ "qwen-code.daemon.session_restore.action": action,
8595
+ "qwen-code.daemon.session_restore.cleanup_result": "transport_closed",
8596
+ "qwen-code.daemon.session_restore.timeout_ms": sessionRestoreTimeoutMs,
8597
+ "qwen-code.daemon.acp_channel.id": channel.id,
8598
+ "session.id": req.sessionId
8599
+ });
8600
+ return;
8601
+ }
8602
+ channel.isQuarantined = true;
8603
+ writeStderrLine(
8604
+ `qwen serve: quarantining ACP channel after timed-out session/${action} cleanup failed for ${JSON.stringify(req.sessionId)}: ${extractErrorMessage(error)}`
8605
+ );
8606
+ telemetry.event("session.restore.cleanup", {
8607
+ "qwen-code.daemon.session_restore.action": action,
8608
+ "qwen-code.daemon.session_restore.cleanup_result": "quarantined",
8609
+ "qwen-code.daemon.session_restore.timeout_ms": sessionRestoreTimeoutMs,
8610
+ "qwen-code.daemon.acp_channel.id": channel.id,
8611
+ "session.id": req.sessionId
8612
+ });
8613
+ if (hasNoChannelWork(channel)) {
8614
+ void killChannelWithLog(
8615
+ channel,
8616
+ `abandoned session/${action} cleanup`
8617
+ );
8618
+ }
8619
+ await channel.channel.exited;
8620
+ }
8621
+ } finally {
8622
+ channel.client.markSessionClosed(req.sessionId);
8623
+ channel.unsettledAbandonedRestores.delete(req.sessionId);
8624
+ const graceTimer = channel.restoreSettlementTimers.get(req.sessionId);
8625
+ if (graceTimer !== void 0) {
8626
+ clearTimeout(graceTimer);
8627
+ channel.restoreSettlementTimers.delete(req.sessionId);
8628
+ }
8629
+ if (channel.unsettledAbandonedRestores.size === 0) {
8630
+ channel.restoreSettlementOverdue = false;
8631
+ }
8632
+ releaseAdmissionOnce();
8633
+ resolveSettlement();
8634
+ }
8635
+ }, "settleAbandonedRestore");
8105
8636
  const promise = (async () => {
8106
8637
  pendingRestoreEvents.set(req.sessionId, restoreEvents);
8107
- ci = await ensureChannel();
8108
- ci.pendingRestoreIds.add(req.sessionId);
8109
- ci.client.markRestoreInFlight(req.sessionId);
8110
- const transportClosed = ci.channel.exited.then(() => {
8638
+ const restoreChannel = await ensureChannel();
8639
+ ci = restoreChannel;
8640
+ restoreChannel.pendingRestoreIds.add(req.sessionId);
8641
+ restoreChannel.client.markRestoreInFlight(req.sessionId);
8642
+ const transportClosed = restoreChannel.channel.exited.then(() => {
8111
8643
  throw new BridgeChannelClosedError(`during session/${action}`);
8112
8644
  });
8113
8645
  transportClosed.catch(() => {
@@ -8118,10 +8650,18 @@ function createAcpSessionBridge(opts) {
8118
8650
  let replayError;
8119
8651
  let replayHasMore;
8120
8652
  try {
8121
- if (action === "load") {
8122
- state = await Promise.race([
8123
- withTimeout(
8124
- ci.connection.loadSession({
8653
+ const rawRestore = telemetry.withSpan(
8654
+ "session.restore",
8655
+ {
8656
+ "qwen-code.daemon.bridge.operation": `session.${action}`,
8657
+ "qwen-code.daemon.session_restore.action": action,
8658
+ "qwen-code.daemon.acp_channel.id": restoreChannel.id,
8659
+ "qwen-code.daemon.session_restore.timeout_ms": sessionRestoreTimeoutMs,
8660
+ "session.id": req.sessionId
8661
+ },
8662
+ async () => {
8663
+ if (action === "load") {
8664
+ const request2 = telemetry.injectPromptContext({
8125
8665
  sessionId: req.sessionId,
8126
8666
  cwd: workspaceKey,
8127
8667
  // Restore path drops per-request `mcpServers` (matches
@@ -8130,49 +8670,88 @@ function createAcpSessionBridge(opts) {
8130
8670
  // intentionally has no `mcpServers` field for the
8131
8671
  // same reason.
8132
8672
  mcpServers: [],
8133
- ...historyReplay === "response" || hideInheritedHistory || req.sourceType ? {
8134
- _meta: {
8135
- ...sessionSourceRequestMeta(
8136
- req.sourceType,
8137
- req.sourceId
8138
- ),
8139
- ...historyReplay === "response" ? {
8140
- [LOAD_REPLAY_MODE_META_KEY]: LOAD_REPLAY_BULK_MODE,
8141
- ...req.historyPageSize !== void 0 ? {
8142
- [LOAD_REPLAY_PAGE_SIZE_META_KEY]: req.historyPageSize
8143
- } : {}
8144
- } : {},
8145
- ...hideInheritedHistory ? {
8146
- [LOAD_REPLAY_HIDE_INHERITED_META_KEY]: true
8673
+ _meta: {
8674
+ ...sessionSourceRequestMeta(req.sourceType, req.sourceId),
8675
+ ...historyReplay === "response" ? {
8676
+ [LOAD_REPLAY_MODE_META_KEY]: LOAD_REPLAY_BULK_MODE,
8677
+ ...req.historyPageSize !== void 0 ? {
8678
+ [LOAD_REPLAY_PAGE_SIZE_META_KEY]: req.historyPageSize
8147
8679
  } : {}
8148
- }
8149
- } : {}
8150
- }),
8151
- initTimeoutMs,
8152
- "loadSession"
8153
- ),
8154
- transportClosed
8155
- ]);
8156
- } else {
8157
- state = await Promise.race([
8158
- withTimeout(
8159
- ci.connection.unstable_resumeSession({
8160
- sessionId: req.sessionId,
8161
- cwd: workspaceKey,
8162
- mcpServers: [],
8163
- ...req.sourceType ? {
8164
- _meta: sessionSourceRequestMeta(
8165
- req.sourceType,
8166
- req.sourceId
8167
- )
8168
- } : {}
8169
- }),
8170
- initTimeoutMs,
8171
- "resumeSession"
8172
- ),
8173
- transportClosed
8174
- ]);
8175
- }
8680
+ } : {},
8681
+ ...hideInheritedHistory ? { [LOAD_REPLAY_HIDE_INHERITED_META_KEY]: true } : {}
8682
+ }
8683
+ });
8684
+ return await restoreChannel.connection.loadSession(request2);
8685
+ }
8686
+ const request = telemetry.injectPromptContext({
8687
+ sessionId: req.sessionId,
8688
+ cwd: workspaceKey,
8689
+ mcpServers: [],
8690
+ _meta: sessionSourceRequestMeta(req.sourceType, req.sourceId)
8691
+ });
8692
+ return await restoreChannel.connection.unstable_resumeSession(
8693
+ request
8694
+ );
8695
+ }
8696
+ );
8697
+ const observedRestore = Promise.race([rawRestore, transportClosed]);
8698
+ state = await new Promise((resolve2, reject) => {
8699
+ const timer = setTimeout(() => {
8700
+ if (restoreLifecycle.phase !== "active") return;
8701
+ restoreLifecycle.phase = "abandoned";
8702
+ restoreChannel.pendingRestoreIds.delete(req.sessionId);
8703
+ restoreChannel.client.markRestoreAbandoned(req.sessionId);
8704
+ pendingRestoreEvents.delete(req.sessionId);
8705
+ restoreEvents.close();
8706
+ restoreChannel.unsettledAbandonedRestores.add(req.sessionId);
8707
+ const channelWasEmpty = hasNoChannelWork(restoreChannel);
8708
+ telemetry.event("session.restore.public_result", {
8709
+ "qwen-code.daemon.session_restore.action": action,
8710
+ "qwen-code.daemon.session_restore.result": "timeout",
8711
+ "qwen-code.daemon.session_restore.timeout_ms": sessionRestoreTimeoutMs,
8712
+ "qwen-code.daemon.acp_channel.id": restoreChannel.id,
8713
+ "qwen-code.daemon.session_restore.channel_was_empty": channelWasEmpty,
8714
+ "session.id": req.sessionId
8715
+ });
8716
+ writeStderrLine(
8717
+ `qwen serve: session/${action} timed out after ${sessionRestoreTimeoutMs}ms for ${JSON.stringify(req.sessionId)} on channel ${restoreChannel.id}; decision=${channelWasEmpty ? "kill_empty" : "fence_shared"}`
8718
+ );
8719
+ if (channelWasEmpty) {
8720
+ void killChannelWithLog(
8721
+ restoreChannel,
8722
+ `timed-out session/${action} on empty channel`
8723
+ );
8724
+ } else {
8725
+ armRestoreSettlementGrace(restoreChannel, req.sessionId, action);
8726
+ }
8727
+ reject(
8728
+ new SessionRestoreTimeoutError(
8729
+ req.sessionId,
8730
+ action,
8731
+ sessionRestoreTimeoutMs
8732
+ )
8733
+ );
8734
+ }, sessionRestoreTimeoutMs);
8735
+ timer.unref();
8736
+ void observedRestore.then(
8737
+ (value) => {
8738
+ if (restoreLifecycle.phase === "active") {
8739
+ clearTimeout(timer);
8740
+ resolve2(value);
8741
+ return;
8742
+ }
8743
+ void settleAbandonedRestore(restoreChannel, "success");
8744
+ },
8745
+ (error) => {
8746
+ if (restoreLifecycle.phase === "active") {
8747
+ clearTimeout(timer);
8748
+ reject(error);
8749
+ return;
8750
+ }
8751
+ void settleAbandonedRestore(restoreChannel, "failure");
8752
+ }
8753
+ );
8754
+ });
8176
8755
  if (action === "load" && historyReplay === "response") {
8177
8756
  const extracted = extractLoadReplayResponse(state);
8178
8757
  state = extracted.state;
@@ -8182,6 +8761,7 @@ function createAcpSessionBridge(opts) {
8182
8761
  replayHasMore = extracted.hasMore === true ? true : void 0;
8183
8762
  }
8184
8763
  } catch (err) {
8764
+ if (err instanceof SessionRestoreTimeoutError) throw err;
8185
8765
  restoreEvents.close();
8186
8766
  if (isAcpSessionResourceNotFound(err, req.sessionId)) {
8187
8767
  throw new SessionNotFoundError(req.sessionId);
@@ -8207,6 +8787,12 @@ function createAcpSessionBridge(opts) {
8207
8787
  const racedEntry = byId.get(req.sessionId);
8208
8788
  if (racedEntry) {
8209
8789
  restoreEvents.close();
8790
+ if (isClosingOrAuthorizingClose(racedEntry)) {
8791
+ throw new SessionNotFoundError(
8792
+ req.sessionId,
8793
+ "The session is closing; retry after close completes"
8794
+ );
8795
+ }
8210
8796
  racedEntry.attachCount += 1 + coalesceState.count;
8211
8797
  const clientId2 = registerClient(racedEntry, req.clientId);
8212
8798
  recordAttachRef(racedEntry, clientId2);
@@ -8312,6 +8898,7 @@ function createAcpSessionBridge(opts) {
8312
8898
  ...replayFieldsFor(entry, action)
8313
8899
  };
8314
8900
  })().finally(async () => {
8901
+ if (restoreLifecycle.phase === "abandoned") return;
8315
8902
  releaseAdmissionOnce();
8316
8903
  ci?.pendingRestoreIds.delete(req.sessionId);
8317
8904
  ci?.client.clearRestoreInFlight(req.sessionId);
@@ -8341,18 +8928,48 @@ function createAcpSessionBridge(opts) {
8341
8928
  await reapPendingEmptyChannel(ci);
8342
8929
  }
8343
8930
  });
8931
+ void promise.then(
8932
+ () => {
8933
+ if (restoreLifecycle.phase === "active") {
8934
+ telemetry.event("session.restore.public_result", {
8935
+ "qwen-code.daemon.session_restore.action": action,
8936
+ "qwen-code.daemon.session_restore.result": "success",
8937
+ "qwen-code.daemon.session_restore.timeout_ms": sessionRestoreTimeoutMs,
8938
+ ...ci ? { "qwen-code.daemon.acp_channel.id": ci.id } : {},
8939
+ "session.id": req.sessionId
8940
+ });
8941
+ resolveSettlement();
8942
+ }
8943
+ },
8944
+ () => {
8945
+ if (restoreLifecycle.phase === "active") {
8946
+ telemetry.event("session.restore.public_result", {
8947
+ "qwen-code.daemon.session_restore.action": action,
8948
+ "qwen-code.daemon.session_restore.result": "failure",
8949
+ "qwen-code.daemon.session_restore.timeout_ms": sessionRestoreTimeoutMs,
8950
+ ...ci ? { "qwen-code.daemon.acp_channel.id": ci.id } : {},
8951
+ "session.id": req.sessionId
8952
+ });
8953
+ resolveSettlement();
8954
+ }
8955
+ }
8956
+ );
8344
8957
  inFlightRestores.set(req.sessionId, {
8345
8958
  action,
8346
8959
  historyReplay,
8347
8960
  hideInheritedHistory,
8348
- promise,
8961
+ publicPromise: promise,
8962
+ settlementPromise,
8963
+ lifecycle: restoreLifecycle,
8349
8964
  coalesceState
8350
8965
  });
8351
- try {
8352
- return await promise;
8353
- } finally {
8354
- inFlightRestores.delete(req.sessionId);
8355
- }
8966
+ void settlementPromise.finally(() => {
8967
+ const current = inFlightRestores.get(req.sessionId);
8968
+ if (current?.settlementPromise === settlementPromise) {
8969
+ inFlightRestores.delete(req.sessionId);
8970
+ }
8971
+ });
8972
+ return await promise;
8356
8973
  }
8357
8974
  __name(restoreSession, "restoreSession");
8358
8975
  async function closeSessionImpl(sessionId, context, closeOpts) {
@@ -8390,7 +9007,8 @@ function createAcpSessionBridge(opts) {
8390
9007
  entry.pendingInteractions.clear();
8391
9008
  await notifyAgentSessionClose(entry, ci, "closeSession", {
8392
9009
  throwOnFailure: true,
8393
- requireFlush: closeOpts?.requireAgentClose === true
9010
+ requireFlush: closeOpts?.requireAgentClose === true,
9011
+ ...closeOpts?.agentCloseTimeoutMs !== void 0 ? { timeoutMs: closeOpts.agentCloseTimeoutMs } : {}
8394
9012
  });
8395
9013
  } catch (error) {
8396
9014
  if (isDefinitiveAcpRequestError(error)) {
@@ -8528,6 +9146,44 @@ function createAcpSessionBridge(opts) {
8528
9146
  get activePromptCount() {
8529
9147
  return activePromptCounter;
8530
9148
  },
9149
+ get activeWork() {
9150
+ for (const entry of byId.values()) {
9151
+ if (entryHasActiveWork(entry)) return true;
9152
+ }
9153
+ return false;
9154
+ },
9155
+ /**
9156
+ * Raw coverage counts rather than a pre-collapsed grade.
9157
+ *
9158
+ * The grade has to be computed over the whole daemon, not per runtime and
9159
+ * then combined: a runtime with zero Sessions is vacuously `full`, and
9160
+ * folding that in as evidence made a deployment whose only real Sessions
9161
+ * were unreported aggregate to `partial`. Counts compose; grades do not.
9162
+ */
9163
+ get activeWorkCoverage() {
9164
+ let covered = 0;
9165
+ let onNegotiatedChannel = 0;
9166
+ let total = 0;
9167
+ let oldestCoveredReportAt = null;
9168
+ for (const entry of byId.values()) {
9169
+ total++;
9170
+ const owner = channelInfoForEntry(entry);
9171
+ const capability = owner?.activeWork;
9172
+ if (!capability) continue;
9173
+ onNegotiatedChannel++;
9174
+ if (ACTIVE_WORK_HOLD_CATEGORIES.some(
9175
+ (category) => !capability.categories.includes(category)
9176
+ )) {
9177
+ continue;
9178
+ }
9179
+ if (!childHoldsAreFresh(entry, capability)) continue;
9180
+ covered++;
9181
+ if (entry.childHoldsAt !== null && (oldestCoveredReportAt === null || entry.childHoldsAt < oldestCoveredReportAt)) {
9182
+ oldestCoveredReportAt = entry.childHoldsAt;
9183
+ }
9184
+ }
9185
+ return { total, covered, onNegotiatedChannel, oldestCoveredReportAt };
9186
+ },
8531
9187
  get lastActivityAt() {
8532
9188
  return lastActivityTimestamp;
8533
9189
  },
@@ -8557,7 +9213,7 @@ function createAcpSessionBridge(opts) {
8557
9213
  if (req.sessionScope !== void 0 && req.sessionScope !== "single" && req.sessionScope !== "thread") {
8558
9214
  throw new InvalidSessionScopeError(req.sessionScope);
8559
9215
  }
8560
- const effectiveScope = req.sessionScope ?? defaultSessionScope;
9216
+ const effectiveScope = req.sessionId !== void 0 ? "thread" : req.sessionScope ?? defaultSessionScope;
8561
9217
  const source = parseSessionSource(req.sourceType, req.sourceId);
8562
9218
  if ("error" in source) {
8563
9219
  throw new InvalidSessionMetadataError("sourceType", source.error);
@@ -8570,7 +9226,7 @@ function createAcpSessionBridge(opts) {
8570
9226
  if (effectiveScope === "single") {
8571
9227
  const existing = defaultEntry;
8572
9228
  if (existing) {
8573
- if (existing.closing) {
9229
+ if (isClosingOrAuthorizingClose(existing)) {
8574
9230
  throw new SessionNotFoundError(
8575
9231
  existing.sessionId,
8576
9232
  "The session is closing; retry after close completes"
@@ -8644,13 +9300,51 @@ function createAcpSessionBridge(opts) {
8644
9300
  };
8645
9301
  }
8646
9302
  }
9303
+ if (req.sessionId !== void 0) {
9304
+ const restoreOwner = inFlightRestores.get(req.sessionId);
9305
+ if (restoreOwner) {
9306
+ const abandoned = restoreOwner.lifecycle.phase === "abandoned";
9307
+ throw new RestoreInProgressError(
9308
+ req.sessionId,
9309
+ restoreOwner.action,
9310
+ "spawn",
9311
+ abandoned ? {
9312
+ reason: "awaiting_abandoned_cleanup",
9313
+ retryAfterSeconds: abandonedRestoreRetryAfterSeconds
9314
+ } : void 0
9315
+ );
9316
+ }
9317
+ if (inFlightRequestedSessionSpawns.has(req.sessionId)) {
9318
+ throw new RestoreInProgressError(req.sessionId, "spawn", "spawn");
9319
+ }
9320
+ }
9321
+ assertFreshSessionsAvailable();
8647
9322
  if (byId.size + inFlightSpawns.size + inFlightRestores.size >= maxSessions) {
8648
9323
  throw new SessionLimitExceededError(maxSessions);
8649
9324
  }
8650
- const admission = reserveFreshSession({
8651
- operation: "spawn",
8652
- workspaceCwd: workspaceKey
8653
- });
9325
+ const requestedSessionRegistrationOwner = req.sessionId !== void 0 ? Symbol(req.sessionId) : void 0;
9326
+ if (req.sessionId !== void 0 && requestedSessionRegistrationOwner !== void 0) {
9327
+ inFlightRequestedSessionSpawns.set(
9328
+ req.sessionId,
9329
+ requestedSessionRegistrationOwner
9330
+ );
9331
+ }
9332
+ const releaseRequestedSessionRegistration = /* @__PURE__ */ __name(() => {
9333
+ if (req.sessionId !== void 0 && requestedSessionRegistrationOwner !== void 0 && inFlightRequestedSessionSpawns.get(req.sessionId) === requestedSessionRegistrationOwner) {
9334
+ inFlightRequestedSessionSpawns.delete(req.sessionId);
9335
+ }
9336
+ }, "releaseRequestedSessionRegistration");
9337
+ let admission;
9338
+ try {
9339
+ admission = reserveFreshSession({
9340
+ operation: "spawn",
9341
+ workspaceCwd: workspaceKey,
9342
+ ...req.sessionId !== void 0 ? { sessionId: req.sessionId } : {}
9343
+ });
9344
+ } catch (error) {
9345
+ releaseRequestedSessionRegistration();
9346
+ throw error;
9347
+ }
8654
9348
  let admissionReleased = false;
8655
9349
  const releaseAdmissionOnce = /* @__PURE__ */ __name(() => {
8656
9350
  if (admissionReleased) return;
@@ -8677,6 +9371,7 @@ function createAcpSessionBridge(opts) {
8677
9371
  } finally {
8678
9372
  releaseAdmissionOnce();
8679
9373
  inFlightSpawns.delete(tracker);
9374
+ releaseRequestedSessionRegistration();
8680
9375
  }
8681
9376
  },
8682
9377
  // Keep this method non-async: admission failures must throw before
@@ -8690,7 +9385,7 @@ function createAcpSessionBridge(opts) {
8690
9385
  const queuedAt = Date.now();
8691
9386
  const entry = byId.get(sessionId);
8692
9387
  if (!entry) return Promise.reject(new SessionNotFoundError(sessionId));
8693
- if (entry.closing) {
9388
+ if (isClosingOrAuthorizingClose(entry)) {
8694
9389
  return Promise.reject(
8695
9390
  new SessionNotFoundError(
8696
9391
  sessionId,
@@ -9067,15 +9762,7 @@ function createAcpSessionBridge(opts) {
9067
9762
  );
9068
9763
  entry.midTurnMessageQueue.length = 0;
9069
9764
  }
9070
- if (entry.clientIds.size === 0 && entry.events.subscriberCount === 0 && entry.pendingPromptCount === 0 && byId.get(sessionId) === entry) {
9071
- void closeSessionImpl(sessionId, void 0, {
9072
- reason: "last_client_detached"
9073
- }).catch((err) => {
9074
- writeStderrLine(
9075
- `qwen serve: deferred close-on-prompt-complete failed for ${JSON.stringify(sessionId)}: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
9076
- );
9077
- });
9078
- }
9765
+ void maybeCloseIdleSession(entry, "prompt_settled");
9079
9766
  }).catch(() => {
9080
9767
  });
9081
9768
  return result;
@@ -9266,6 +9953,7 @@ function createAcpSessionBridge(opts) {
9266
9953
  if (entry.promptActive && !isSideTask) {
9267
9954
  throw new BranchWhilePromptActiveError(sessionId);
9268
9955
  }
9956
+ assertFreshSessionsAvailable();
9269
9957
  if (byId.size + inFlightSpawns.size + inFlightRestores.size >= maxSessions) {
9270
9958
  throw new SessionLimitExceededError(maxSessions);
9271
9959
  }
@@ -10396,18 +11084,27 @@ function createAcpSessionBridge(opts) {
10396
11084
  if (!entry) throw new SessionNotFoundError(sessionId);
10397
11085
  const info = channelInfoForEntry(entry);
10398
11086
  if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
10399
- const response = await Promise.race([
10400
- withTimeout(
10401
- entry.connection.extMethod(
10402
- SERVE_CONTROL_EXT_METHODS.sessionBackgroundNotification,
10403
- { sessionId, ...notification }
11087
+ entry.pendingAgentNotificationCount++;
11088
+ try {
11089
+ const response = await Promise.race([
11090
+ withTimeout(
11091
+ entry.connection.extMethod(
11092
+ SERVE_CONTROL_EXT_METHODS.sessionBackgroundNotification,
11093
+ { sessionId, ...notification }
11094
+ ),
11095
+ initTimeoutMs,
11096
+ SERVE_CONTROL_EXT_METHODS.sessionBackgroundNotification
10404
11097
  ),
10405
- initTimeoutMs,
10406
- SERVE_CONTROL_EXT_METHODS.sessionBackgroundNotification
10407
- ),
10408
- getTransportClosedReject(entry)
10409
- ]);
10410
- return { sessionId, accepted: response["accepted"] === true };
11098
+ getTransportClosedReject(entry)
11099
+ ]);
11100
+ return { sessionId, accepted: response["accepted"] === true };
11101
+ } finally {
11102
+ entry.pendingAgentNotificationCount = Math.max(
11103
+ 0,
11104
+ entry.pendingAgentNotificationCount - 1
11105
+ );
11106
+ void maybeCloseIdleSession(entry, "agent_notification_settled");
11107
+ }
10411
11108
  },
10412
11109
  async generateSessionBtw(sessionId, question, signal, _context) {
10413
11110
  const entry = byId.get(sessionId);
@@ -10648,7 +11345,7 @@ function createAcpSessionBridge(opts) {
10648
11345
  async rewindSession(sessionId, req, context) {
10649
11346
  const entry = byId.get(sessionId);
10650
11347
  if (!entry) throw new SessionNotFoundError(sessionId);
10651
- if (entry.closing) {
11348
+ if (isClosingOrAuthorizingClose(entry)) {
10652
11349
  throw new SessionNotFoundError(sessionId, "The session is closing");
10653
11350
  }
10654
11351
  const info = channelInfoForEntry(entry);
@@ -11138,18 +11835,7 @@ function createAcpSessionBridge(opts) {
11138
11835
  if (entry.attachCount > 0) entry.attachCount--;
11139
11836
  }
11140
11837
  unregisterClient(entry, clientId);
11141
- if (entry.spawnOwnerWantedKill && entry.attachCount === 0 && entry.events.subscriberCount === 0) {
11142
- await this.killSession(sessionId).catch(() => {
11143
- });
11144
- } else if (entry.clientIds.size === 0 && entry.events.subscriberCount === 0 && entry.pendingPromptCount === 0) {
11145
- await closeSessionImpl(sessionId, void 0, {
11146
- reason: "last_client_detached"
11147
- }).catch((err) => {
11148
- writeStderrLine(
11149
- `qwen serve: close-on-last-detach failed for ${JSON.stringify(sessionId)}: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
11150
- );
11151
- });
11152
- }
11838
+ await maybeCloseIdleSession(entry, "last_client_detached");
11153
11839
  },
11154
11840
  killAllSync() {
11155
11841
  shuttingDown = true;
@@ -11226,7 +11912,7 @@ function createAcpSessionBridge(opts) {
11226
11912
  )
11227
11913
  );
11228
11914
  const inFlightRestoreAwaits = Array.from(inFlightRestores.values()).map(
11229
- (restore) => restore.promise.then(
11915
+ (restore) => restore.settlementPromise.then(
11230
11916
  () => void 0,
11231
11917
  () => void 0
11232
11918
  )