@qwen-code/qwen-code 0.22.2 → 0.22.3-nightly.20260831.3a0c4c6108

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 (480) hide show
  1. package/bundled/computer-use/SKILL.md +3 -3
  2. package/bundled/qc-helper/docs/common-workflow.md +32 -0
  3. package/bundled/qc-helper/docs/configuration/auth.md +6 -6
  4. package/bundled/qc-helper/docs/configuration/model-providers.md +4 -2
  5. package/bundled/qc-helper/docs/configuration/settings.md +47 -44
  6. package/bundled/qc-helper/docs/extension/introduction.md +1 -1
  7. package/bundled/qc-helper/docs/features/approval-mode.md +3 -0
  8. package/bundled/qc-helper/docs/features/auto-mode.md +34 -17
  9. package/bundled/qc-helper/docs/features/channels/dws.md +2 -0
  10. package/bundled/qc-helper/docs/features/channels/overview.md +36 -3
  11. package/bundled/qc-helper/docs/features/code-review.md +5 -5
  12. package/bundled/qc-helper/docs/features/commands.md +41 -0
  13. package/bundled/qc-helper/docs/features/computer-use.md +2 -2
  14. package/bundled/qc-helper/docs/features/hooks.md +6 -2
  15. package/bundled/qc-helper/docs/features/memory.md +1 -0
  16. package/bundled/qc-helper/docs/features/tool-use-summaries.md +1 -1
  17. package/bundled/qc-helper/docs/qwen-serve-deploy-local.md +17 -7
  18. package/bundled/qc-helper/docs/qwen-serve.md +59 -55
  19. package/bundled/review/SKILL.md +90 -31
  20. package/bundled/review/references/persistence.md +7 -3
  21. package/bundled/review/references/posting.md +11 -5
  22. package/chunks/MaxSizedBox-IHRTWGSY.js +115 -0
  23. package/chunks/{StandaloneSessionPicker-AVLI6VJP.js → StandaloneSessionPicker-TLVWVTJ7.js} +88 -85
  24. package/chunks/{acp-startup-profiler-GNYAL6LI.js → acp-startup-profiler-M4X5F3VK.js} +2 -2
  25. package/chunks/{acpAgent-MXEAN54C.js → acpAgent-7I5LLPP3.js} +1563 -407
  26. package/chunks/agent-4Z4NTJXD.js +99 -0
  27. package/chunks/agent-headless-TVUJXQWU.js +89 -0
  28. package/chunks/{anthropicContentGenerator-HGMBACVE.js → anthropicContentGenerator-VSSPG7XV.js} +84 -55
  29. package/chunks/{artifact-tool-5YT4QF7Y.js → artifact-tool-TEYW6I4A.js} +11 -3
  30. package/chunks/{askUserQuestion-4SWJY65V.js → askUserQuestion-PYNBWV63.js} +45 -7
  31. package/chunks/bridge-EVURP4NO.js +122 -0
  32. package/chunks/{ca-7HPYOYSZ.js → ca-BNU7YNDE.js} +3 -0
  33. package/chunks/{channel-management-service-X4AZ6IXX.js → channel-management-service-IDBNYSFW.js} +6 -6
  34. package/chunks/channel-settings-store-MZNXRKVI.js +125 -0
  35. package/chunks/{channel-worker-group-27HMGD5S.js → channel-worker-group-ZLP5SJHT.js} +7 -7
  36. package/chunks/{channel-worker-manager-QEGX646Z.js → channel-worker-manager-FG5377T2.js} +7 -7
  37. package/chunks/{channel-worker-supervisor-UEW2WWCC.js → channel-worker-supervisor-HDVE4ADS.js} +4 -4
  38. package/chunks/{chunk-PPKZ7JOE.js → chunk-22UH7VTP.js} +1 -1
  39. package/chunks/chunk-24FVUD5P.js +49 -0
  40. package/chunks/{chunk-CMHFCLBU.js → chunk-26SIKQIC.js} +1 -1
  41. package/chunks/{chunk-5PA6UEYA.js → chunk-3452WXOK.js} +4 -1
  42. package/chunks/{chunk-SFPGAQUL.js → chunk-373DQUVA.js} +72 -31
  43. package/chunks/{chunk-H2WDGQ6D.js → chunk-3AKCBZVW.js} +3 -3
  44. package/chunks/{chunk-LXIBMVRN.js → chunk-3CZGSTZX.js} +15 -9
  45. package/chunks/{chunk-JVPFTDBK.js → chunk-3EGEBDTW.js} +1 -1
  46. package/chunks/{chunk-K2NQPH5D.js → chunk-3NQCZMUB.js} +79 -5
  47. package/chunks/{chunk-E7REAOTN.js → chunk-47ECGEJC.js} +4 -0
  48. package/chunks/{chunk-WKK5BQNP.js → chunk-4P7PPAHN.js} +45 -421
  49. package/chunks/chunk-4VQBQLIZ.js +293 -0
  50. package/chunks/{chunk-4JWNNLPT.js → chunk-5FGWVPCM.js} +4 -4
  51. package/chunks/{chunk-ZFKOCECU.js → chunk-5ICKESC3.js} +2 -2
  52. package/chunks/{chunk-3I6UTTDX.js → chunk-67AY5D3M.js} +20 -20
  53. package/chunks/chunk-6HUXPLB5.js +110 -0
  54. package/chunks/{chunk-BRVWYMKV.js → chunk-6MBXY6WM.js} +1 -1
  55. package/chunks/{chunk-6LFTG244.js → chunk-6NWPR2EB.js} +19 -9
  56. package/chunks/chunk-6OO7QEWV.js +2581 -0
  57. package/chunks/{chunk-TWSKM647.js → chunk-6VHIWUJP.js} +1 -1
  58. package/chunks/{chunk-RJC5EIKY.js → chunk-6YXNJXQW.js} +7 -5
  59. package/chunks/{chunk-TPI7O7HR.js → chunk-7DDTI7Q3.js} +1 -1
  60. package/chunks/{chunk-KGJGEEVR.js → chunk-7FA2II6K.js} +10 -1
  61. package/chunks/chunk-7J6OTNGO.js +424 -0
  62. package/chunks/{chunk-NIFYWDYN.js → chunk-7TBU7PU2.js} +1 -1
  63. package/chunks/{chunk-3FDFA4MZ.js → chunk-7XICSE62.js} +3 -3
  64. package/chunks/{chunk-V2NDP3I4.js → chunk-7XL4WRXM.js} +2 -2
  65. package/chunks/{chunk-F7TNAPZ4.js → chunk-AAX236AR.js} +45 -11
  66. package/chunks/{chunk-T32W2JHI.js → chunk-ACZ4YRYK.js} +6 -6
  67. package/chunks/chunk-ADJD4LZF.js +210 -0
  68. package/chunks/{chunk-JWALNCLT.js → chunk-AF3QB6OL.js} +1 -1
  69. package/chunks/{chunk-P2SU6ZTI.js → chunk-AI35S34R.js} +3 -3
  70. package/chunks/{chunk-OIP7C4FE.js → chunk-AKNQMSL2.js} +5 -5
  71. package/chunks/{chunk-6CGL7E7G.js → chunk-AN3KFCNH.js} +2 -2
  72. package/chunks/{chunk-ABS25QER.js → chunk-ANVG3BVV.js} +1 -1
  73. package/chunks/chunk-AYYKUUKP.js +972 -0
  74. package/chunks/chunk-B2RIIK3K.js +346 -0
  75. package/chunks/{chunk-F5SZP77N.js → chunk-BBYTY46C.js} +153 -112
  76. package/chunks/{chunk-XRSOI3DL.js → chunk-BE5FGT5Z.js} +1 -1
  77. package/chunks/{chunk-BGXZBI5B.js → chunk-BHTYRHJF.js} +3 -2
  78. package/chunks/{chunk-CSMI4FP5.js → chunk-BSJOBW4U.js} +1 -1
  79. package/chunks/{chunk-Z7TGYN5F.js → chunk-BXNKREX7.js} +3 -3
  80. package/chunks/{chunk-4MQEPP3Y.js → chunk-BY24DZ4J.js} +5 -119
  81. package/chunks/{chunk-5XBFCMWD.js → chunk-CAZADEU7.js} +17 -9
  82. package/chunks/chunk-CHEIIBT3.js +40 -0
  83. package/chunks/{chunk-BBWV7ONL.js → chunk-CM4F4JBB.js} +6 -6
  84. package/chunks/{chunk-CAJTKR6W.js → chunk-D7D3HSYQ.js} +6 -5
  85. package/chunks/{chunk-3DXFGHAJ.js → chunk-DBTMP3P4.js} +213 -185
  86. package/chunks/chunk-DJBQCO2E.js +842 -0
  87. package/chunks/{chunk-RXFQM6FQ.js → chunk-DMEUXLSS.js} +16 -1
  88. package/chunks/{chunk-3AFMQUTI.js → chunk-E4JNFXNI.js} +3 -3
  89. package/chunks/{chunk-UHQFIS7N.js → chunk-EAHOJF6V.js} +28 -4
  90. package/chunks/{chunk-OIVXBW3W.js → chunk-ED7DD7MV.js} +3 -3
  91. package/chunks/{chunk-6DIGWMGT.js → chunk-EIJT2PQ6.js} +2 -2
  92. package/chunks/{chunk-XQFT3QUF.js → chunk-ENYUKMFK.js} +1 -1
  93. package/chunks/{chunk-CQH5KTKC.js → chunk-ERHAMGAA.js} +1 -1
  94. package/chunks/{chunk-IOPCFOTF.js → chunk-ERRJA23L.js} +3 -3
  95. package/chunks/{chunk-LDP5OD6N.js → chunk-EU2B4P3D.js} +12 -12
  96. package/chunks/{chunk-W5MJJ2V7.js → chunk-FHPYRXRH.js} +1 -1
  97. package/chunks/{chunk-IZIVM7LZ.js → chunk-FIWZOMBF.js} +3 -3
  98. package/chunks/{chunk-4HV6HH33.js → chunk-FPMQT2YY.js} +9 -9
  99. package/chunks/{chunk-Z7VOMGDC.js → chunk-FTJMWDND.js} +0 -24
  100. package/chunks/{chunk-L2M46FP5.js → chunk-FXUSWLBF.js} +225 -12
  101. package/chunks/{chunk-VVW4ZNFY.js → chunk-GB4UKWY5.js} +16 -8
  102. package/chunks/{chunk-PKAYJDB3.js → chunk-GCUR24KK.js} +22 -4
  103. package/chunks/{chunk-DIMEPKCW.js → chunk-GDXSXL3H.js} +11 -1
  104. package/chunks/{chunk-7JPW6IYH.js → chunk-GN422P4J.js} +1 -1
  105. package/chunks/{chunk-66KDY3LV.js → chunk-GS7XT345.js} +3 -3
  106. package/chunks/{chunk-PFCQ62V4.js → chunk-H2A2GJGL.js} +6 -6
  107. package/chunks/{chunk-Z7FLBQPR.js → chunk-H5GPPDGY.js} +8 -8
  108. package/chunks/{chunk-CPGJJJLU.js → chunk-HDMKCJHI.js} +1 -1
  109. package/chunks/{chunk-6PJOTWAN.js → chunk-HT5S5TRQ.js} +1 -1
  110. package/chunks/{chunk-Z4X5MTRT.js → chunk-HWVUSYSN.js} +5 -12
  111. package/chunks/{chunk-V4QXXQJ2.js → chunk-I75VKFCJ.js} +4 -4
  112. package/chunks/{chunk-NU7RKEZR.js → chunk-IBMUI5EV.js} +3 -3
  113. package/chunks/{chunk-4J657AJR.js → chunk-ID33ZRWH.js} +42 -42
  114. package/chunks/{chunk-R4PMVSR5.js → chunk-IIFL7QDN.js} +30 -192
  115. package/chunks/{chunk-MQM5PJML.js → chunk-IVUWTA2D.js} +1 -1
  116. package/chunks/{chunk-E55H6BZ7.js → chunk-IXEZHTDR.js} +1616 -151
  117. package/chunks/{chunk-XF63PKEN.js → chunk-IXHV5MIC.js} +1 -1
  118. package/chunks/{chunk-JHS74YAB.js → chunk-J4Y3QJHF.js} +3 -1
  119. package/chunks/{chunk-MIIVI25Q.js → chunk-JDIO45X7.js} +3 -3
  120. package/chunks/{chunk-QFCDF5PL.js → chunk-JHN7UIDW.js} +1 -1
  121. package/chunks/{chunk-FDY5RMGH.js → chunk-JLSZT354.js} +2 -2
  122. package/chunks/chunk-JMRE4WJQ.js +1849 -0
  123. package/chunks/{chunk-FY76G3G5.js → chunk-JOTYTYYW.js} +6 -6
  124. package/chunks/{chunk-TRZTNIQ6.js → chunk-JOWQD4LO.js} +3 -3
  125. package/chunks/{chunk-KEKZFOL6.js → chunk-JPKTAQS3.js} +1 -1
  126. package/chunks/chunk-JS73NNWF.js +30 -0
  127. package/chunks/chunk-JX6XHPTX.js +26 -0
  128. package/chunks/{chunk-5JTIYALA.js → chunk-JZWOCUZ3.js} +1 -1
  129. package/chunks/{chunk-3JGZSIDA.js → chunk-KFN2IZYR.js} +3 -3
  130. package/chunks/{chunk-23RFD54N.js → chunk-KHP66DFN.js} +1 -1
  131. package/chunks/{chunk-AGR5UROZ.js → chunk-KUDI54QQ.js} +23 -33
  132. package/chunks/{chunk-3SGB2UHX.js → chunk-L2OKAIUK.js} +28 -16
  133. package/chunks/{chunk-BXDAPVQU.js → chunk-L6CUJZDG.js} +28 -4
  134. package/chunks/chunk-LAUEEBDL.js +277 -0
  135. package/chunks/{chunk-BNMF3K5X.js → chunk-LFAR7CPW.js} +55 -39
  136. package/chunks/{chunk-Y3QL45LS.js → chunk-LKODFJRD.js} +9 -9
  137. package/chunks/{chunk-BWORX6FA.js → chunk-LMMHS3RS.js} +1 -1
  138. package/chunks/{chunk-UTLCH2FK.js → chunk-LYSZVCCD.js} +10 -7
  139. package/chunks/{chunk-F6FV3C5K.js → chunk-MBZ5ORJB.js} +264 -283
  140. package/chunks/{chunk-L6YT5H34.js → chunk-MJBHSZEJ.js} +5 -5
  141. package/chunks/{chunk-B7CDU2SL.js → chunk-MOF6VZN2.js} +18 -21
  142. package/chunks/{chunk-6LFJEACX.js → chunk-MZ5A5PQW.js} +7 -7
  143. package/chunks/chunk-MZYWNU66.js +149 -0
  144. package/chunks/{chunk-2TNL5KPM.js → chunk-N4QWUPDM.js} +7 -0
  145. package/chunks/{chunk-5O72BNDD.js → chunk-ND7TQQPE.js} +9 -9
  146. package/chunks/{chunk-QP4C6WCE.js → chunk-NGKLMNWN.js} +3 -3
  147. package/chunks/{chunk-JSC3H3TD.js → chunk-NT5XIL5H.js} +46 -18
  148. package/chunks/{chunk-KUX5D6DK.js → chunk-NT7LXVV5.js} +11010 -11138
  149. package/chunks/{chunk-PDQGMSZK.js → chunk-ODZF64DM.js} +1 -1
  150. package/chunks/{chunk-FBSSIAAQ.js → chunk-OE5AQVNB.js} +8 -8
  151. package/chunks/chunk-OHU2H5CS.js +130 -0
  152. package/chunks/{chunk-2K6ORIHJ.js → chunk-OKMDOATP.js} +13 -13
  153. package/chunks/{chunk-JB4JIVTJ.js → chunk-OKTNXDVO.js} +1 -1
  154. package/chunks/{chunk-XDLEZ5HX.js → chunk-OQYXJK2V.js} +1 -1
  155. package/chunks/{chunk-EFUM7RVY.js → chunk-OSTQZIOY.js} +944 -18
  156. package/chunks/{chunk-VQUGEVGR.js → chunk-OTTNXUT5.js} +1 -1
  157. package/chunks/{chunk-T3WXYEZ4.js → chunk-PB7JBAHF.js} +53 -54
  158. package/chunks/{chunk-WAOIHTJS.js → chunk-PDAKFU7M.js} +1 -1
  159. package/chunks/{chunk-43GGFFLY.js → chunk-PDXYZBBQ.js} +3 -3
  160. package/chunks/{chunk-4CMJKUND.js → chunk-PNIGAVY6.js} +67 -2
  161. package/chunks/{chunk-SAH4BD2J.js → chunk-PQEISIKS.js} +67 -0
  162. package/chunks/{chunk-4CDSK5AZ.js → chunk-PWR2NQBS.js} +6 -6
  163. package/chunks/{chunk-YTDVNSUQ.js → chunk-QAK5475D.js} +1 -1
  164. package/chunks/{chunk-IJKTMLBE.js → chunk-QE7DTCLX.js} +1 -1
  165. package/chunks/{chunk-SMPR7SPO.js → chunk-QJNDUTOL.js} +45 -239
  166. package/chunks/{chunk-C6K43ZDE.js → chunk-QTMPM6RC.js} +16 -10
  167. package/chunks/{chunk-P5GGM4KS.js → chunk-RAEEQEC5.js} +5 -5
  168. package/chunks/{chunk-XDFHY4R3.js → chunk-RGEV5AUD.js} +242 -14
  169. package/chunks/{chunk-M6FOJXW3.js → chunk-RKW36UWC.js} +1 -1
  170. package/chunks/{chunk-RIEFGNTP.js → chunk-RLSPPTWH.js} +5119 -683
  171. package/chunks/{chunk-PDMJ3KGS.js → chunk-RT4QNQ5P.js} +3 -3
  172. package/chunks/{chunk-SJ3ZUWFK.js → chunk-RVCGAHC7.js} +4 -26
  173. package/chunks/{chunk-77RG5SFQ.js → chunk-RY7DL5IF.js} +11 -6
  174. package/chunks/{chunk-4FTKQNWJ.js → chunk-S2T7H5MI.js} +182 -3
  175. package/chunks/{chunk-EOGELB3H.js → chunk-SDCHI64N.js} +4 -4
  176. package/chunks/{chunk-EKSCLBBF.js → chunk-SMJYTKTB.js} +23 -5
  177. package/chunks/{chunk-MZS7GEC7.js → chunk-SMRDCANI.js} +1 -1
  178. package/chunks/{chunk-43UYNAWX.js → chunk-T3TL6S3B.js} +4 -4
  179. package/chunks/{chunk-4K7NQDCG.js → chunk-T4EFG2P5.js} +18 -9
  180. package/chunks/{chunk-IK5BW3CS.js → chunk-T4RMUTTT.js} +16 -5
  181. package/chunks/{chunk-L6BZRIUL.js → chunk-TBJ3SNS3.js} +183 -5
  182. package/chunks/{chunk-CCBZDVUA.js → chunk-TNMOFNT4.js} +133 -122
  183. package/chunks/{chunk-Y6LSMB6J.js → chunk-TV7BJ7TX.js} +1 -1
  184. package/chunks/{chunk-G34IR3D6.js → chunk-U5RTYI7D.js} +102 -146
  185. package/chunks/{chunk-TTX2JUE6.js → chunk-U6D2PPMN.js} +1 -1
  186. package/chunks/{chunk-D5LUXLUH.js → chunk-UJ4DUYIH.js} +18 -4
  187. package/chunks/{chunk-A7YP63XN.js → chunk-UKB2TZAO.js} +5 -5
  188. package/chunks/{chunk-VTHREBQM.js → chunk-UKYXYWI2.js} +2 -2
  189. package/chunks/{chunk-2NYCN6F2.js → chunk-UVYKCRJZ.js} +46 -29
  190. package/chunks/{chunk-HVEYF6VT.js → chunk-UXEW7TQS.js} +2 -2
  191. package/chunks/{chunk-HVQ3B7FF.js → chunk-V4HN35IF.js} +44 -7
  192. package/chunks/chunk-VGHZT6JR.js +51 -0
  193. package/chunks/{chunk-V5J4J5TP.js → chunk-VYSZUQGU.js} +2 -20
  194. package/chunks/{chunk-6IUNAPLR.js → chunk-W4BDHLRT.js} +3 -3
  195. package/chunks/{chunk-Q26AYA62.js → chunk-W7KWB6AH.js} +7 -4
  196. package/chunks/chunk-WLEYH7CD.js +332 -0
  197. package/chunks/{chunk-RU7BK6B3.js → chunk-X7C7KFWA.js} +2 -2
  198. package/chunks/{chunk-YRLW2MSX.js → chunk-XBVNNDPK.js} +11 -4
  199. package/chunks/{chunk-BFPESSTR.js → chunk-XEAAO654.js} +1 -1
  200. package/chunks/{chunk-JU4EEXC7.js → chunk-XLENZQXH.js} +2 -2
  201. package/chunks/{chunk-JSHHYW6T.js → chunk-XPDKSOVW.js} +69 -64
  202. package/chunks/chunk-YDQ4OCDK.js +1222 -0
  203. package/chunks/{chunk-ZU4UDIWX.js → chunk-YKADANK6.js} +6 -7
  204. package/chunks/{chunk-JCQD3INC.js → chunk-YNG7XAJU.js} +3 -3
  205. package/chunks/{chunk-O2UN57NX.js → chunk-YOFULAHO.js} +19 -2
  206. package/chunks/{chunk-BAJKAAOY.js → chunk-YOMKOM7E.js} +4892 -1504
  207. package/chunks/{chunk-ERFDKH32.js → chunk-YPISTYEI.js} +8 -7
  208. package/chunks/{chunk-TGWAQ5RB.js → chunk-ZBDYI7VM.js} +6900 -6882
  209. package/chunks/{chunk-OZ6KS6KW.js → chunk-ZGCKTAL5.js} +6 -1
  210. package/chunks/chunk-ZHO2CW6D.js +205 -0
  211. package/chunks/{chunk-DZWPESIF.js → chunk-ZQ3FKEWA.js} +3 -3
  212. package/chunks/{chunk-7EQQR46F.js → chunk-ZVP4PWBU.js} +1 -1
  213. package/chunks/{chunk-7UYV53BC.js → chunk-ZYPJ7HVZ.js} +1 -1
  214. package/chunks/config-utils-NNUPVGEC.js +118 -0
  215. package/chunks/contextCommand-S3TPCDM5.js +111 -0
  216. package/chunks/{core-runtime-677KFX6Q.js → core-runtime-NASPCSQX.js} +66 -63
  217. package/chunks/{create-sub-session-23ITIELI.js → create-sub-session-36TNCHCP.js} +3 -3
  218. package/chunks/{create-sub-session-JKZAYZX7.js → create-sub-session-DS4BJLYZ.js} +97 -68
  219. package/chunks/{cron-create-KJ4IY6PP.js → cron-create-QXHTYUTO.js} +4 -4
  220. package/chunks/{cron-delete-MBLQCAYY.js → cron-delete-WV2BM42M.js} +4 -4
  221. package/chunks/{cron-list-DWH2PQ47.js → cron-list-7KVNBLZ7.js} +4 -4
  222. package/chunks/{daemon-MJQUO7RA.js → daemon-UCVUHPJD.js} +723 -90
  223. package/chunks/{daemon-git-worktree-guard-6MVAAZZZ.js → daemon-git-worktree-guard-ATXVQPS3.js} +63 -60
  224. package/chunks/daemon-status-provider-UI3GBTMO.js +121 -0
  225. package/chunks/daemon-trust-policy-SUCBJ2CA.js +117 -0
  226. package/chunks/{daemon-trust-policy-monitor-B5U6TQX5.js → daemon-trust-policy-monitor-3LM5RQNS.js} +71 -68
  227. package/chunks/{de-X6HLSIFE.js → de-T4BLOKEC.js} +3 -0
  228. package/chunks/deferred-core-runtime-SCOJLVYG.js +119 -0
  229. package/chunks/{display-image-BE7I6ZB6.js → display-image-Z4VBQDEJ.js} +5 -5
  230. package/chunks/{dist-TJUDJTCB.js → dist-5423MG3D.js} +411 -91
  231. package/chunks/{dist-ILDBUWDB.js → dist-7WDZYMWV.js} +43 -25
  232. package/chunks/{dist-ZBVOXSKF.js → dist-FLJLHASF.js} +219 -121
  233. package/chunks/{dist-QODVOVWI.js → dist-GPSRNVVP.js} +43 -35
  234. package/chunks/{dist-DJBNNPLN.js → dist-LY66XNUL.js} +10 -3
  235. package/chunks/{dist-7CKN54NL.js → dist-S3BKCTWF.js} +205 -89
  236. package/chunks/{dist-UQUFZDIY.js → dist-SDKHGH5O.js} +41 -20
  237. package/chunks/{dist-4Q7NTUNB.js → dist-WAUO5OMN.js} +46 -22
  238. package/chunks/{dist-SATKXKUR.js → dist-WIVVPE7M.js} +28 -14
  239. package/chunks/earlyInputCapture-INWJ2STR.js +112 -0
  240. package/chunks/{edit-BSV6YRVW.js → edit-B7ZRRGAE.js} +51 -49
  241. package/chunks/{en-YLXBXM4Z.js → en-E4AYTX6H.js} +3 -0
  242. package/chunks/{enter-worktree-MQXFFPKM.js → enter-worktree-SPZHEETQ.js} +7 -7
  243. package/chunks/{enterPlanMode-PKNF35FE.js → enterPlanMode-S554IKJ2.js} +55 -53
  244. package/chunks/environment-25ADH4Y6.js +134 -0
  245. package/chunks/errors-G2I6LFTW.js +118 -0
  246. package/chunks/{exit-worktree-L344UY7K.js → exit-worktree-AWUXAMAM.js} +7 -7
  247. package/chunks/exitPlanMode-ZFH4OYPM.js +87 -0
  248. package/chunks/{fast-path-773PV7PU.js → fast-path-WZHVZ2ER.js} +8 -7
  249. package/chunks/{fast-path-settings-XMFBYFUH.js → fast-path-settings-NILM6QZL.js} +3 -3
  250. package/chunks/{fr-FTZWF462.js → fr-BGHGYX6A.js} +3 -0
  251. package/chunks/{glob-CZHOWUEA.js → glob-UOVDMBUP.js} +50 -48
  252. package/chunks/{goal-tools-YZQJXTGH.js → goal-tools-7TNK5ZHC.js} +5 -4
  253. package/chunks/{grep-5GPICQBU.js → grep-LHYJ5V2N.js} +9 -7
  254. package/chunks/handleAutoUpdate-4MZ45DZY.js +114 -0
  255. package/chunks/i18n-KWUEI4DS.js +129 -0
  256. package/chunks/{image-gen-HAYOEQ4O.js → image-gen-YZDVO5YS.js} +8 -10
  257. package/chunks/initializer-2NBROGMG.js +118 -0
  258. package/chunks/installationInfo-YKUSY3ZB.js +112 -0
  259. package/chunks/{ja-2GL5FKYJ.js → ja-G3FTD5XE.js} +3 -0
  260. package/chunks/{keychain-token-storage-37XPR34V.js → keychain-token-storage-REZVMJA6.js} +2 -2
  261. package/chunks/list-W2WV3COM.js +121 -0
  262. package/chunks/list-agents-PMVMHNY7.js +128 -0
  263. package/chunks/{gemini-IP4FJAP5.js → llm-NPPHLIJI.js} +162 -133
  264. package/chunks/{geminiContentGenerator-PUNEBWJW.js → llm-content-generator-YK3WN6XW.js} +29 -29
  265. package/chunks/loadedSettingsAdapter-2P7DFWUR.js +115 -0
  266. package/chunks/{loggingContentGenerator-2GGZNGLG.js → loggingContentGenerator-QYYYGU6V.js} +318 -35
  267. package/chunks/{loop-wakeup-XJFONSQ7.js → loop-wakeup-EBM4F5MZ.js} +5 -5
  268. package/chunks/{ls-3VAT2J7A.js → ls-F6TRX3DD.js} +4 -4
  269. package/chunks/{lsp-5W7IRUBH.js → lsp-QYTOS2FI.js} +2 -2
  270. package/chunks/{managed-npm-update-5TEV2E3J.js → managed-npm-update-UMC4NBWW.js} +64 -61
  271. package/chunks/mcp-OFI6XDP2.js +115 -0
  272. package/chunks/{monitor-FI7PY36Z.js → monitor-QX3BM3JY.js} +50 -48
  273. package/chunks/nonInteractiveCli-NVRMD5MN.js +193 -0
  274. package/chunks/{notebook-edit-TPGMGBST.js → notebook-edit-AAILDPSV.js} +51 -49
  275. package/chunks/{open-with-auth-4UI3KHUR.js → open-with-auth-D6JD6LDS.js} +4 -4
  276. package/chunks/{openaiContentGenerator-SUCYANUZ.js → openaiContentGenerator-53PB3CMN.js} +27 -29
  277. package/chunks/pidfile-5ACKG2LI.js +116 -0
  278. package/chunks/process-registry-OAEG6WGC.js +662 -0
  279. package/chunks/{processUtils-IQZBYWSB.js → processUtils-2BCDO3DZ.js} +2 -2
  280. package/chunks/prompt-terminal-ledger-3RKW73A2.js +110 -0
  281. package/chunks/{pt-XN2YWJVM.js → pt-D73IFYT7.js} +3 -0
  282. package/chunks/{qwenContentGenerator-J4TXFGJT.js → qwenContentGenerator-N4LLWDPH.js} +56 -54
  283. package/chunks/{qwenOAuth2-RF33DNZW.js → qwenOAuth2-7AOCKSOQ.js} +7 -9
  284. package/chunks/{read-file-QE2B5FTX.js → read-file-DPAKXHYE.js} +13 -13
  285. package/chunks/{read-mcp-resource-XD257PGJ.js → read-mcp-resource-R4NO75DW.js} +2 -2
  286. package/chunks/{record-artifact-RZGZBENU.js → record-artifact-2EAYLAL5.js} +4 -4
  287. package/chunks/{report-findings-Q3SYKKW5.js → report-findings-Y4MSHDS3.js} +9 -5
  288. package/chunks/{request-shutdown-BCLA3JV2.js → request-shutdown-DWRMGTMB.js} +8 -7
  289. package/chunks/resumeHistoryUtils-3JQORXAK.js +123 -0
  290. package/chunks/ripGrep-AHF5QCI7.js +39 -0
  291. package/chunks/{ru-OLTMFJEW.js → ru-QMISLKRH.js} +3 -0
  292. package/chunks/{run-qwen-serve-TPHZMSXJ.js → run-qwen-serve-3LKV7KDC.js} +728 -256
  293. package/chunks/runtime-66F4W3IA.js +153 -0
  294. package/chunks/scheduled-tasks-2NVKS5Q2.js +132 -0
  295. package/chunks/scheduler-JNV7T3ZO.js +127 -0
  296. package/chunks/{sdk-exporters-http-KCJ4ZFG7.js → sdk-exporters-http-MFIWD23D.js} +2 -2
  297. package/chunks/{sdk-impl-HLSEDAEP.js → sdk-impl-2RXJ4Q6X.js} +11 -5
  298. package/chunks/send-message-WPZRCJOB.js +442 -0
  299. package/chunks/serve-GP2UNRP7.js +123 -0
  300. package/chunks/{server-DFUSSSHI.js → server-7W6C3SOW.js} +5233 -3623
  301. package/chunks/{session-3TEHNENZ.js → session-PZLBAQGG.js} +125 -118
  302. package/chunks/session-pr-refresh-OIVL3U3Z.js +367 -0
  303. package/chunks/{settings-VOZYKLTT.js → settings-ZOHIQWYK.js} +70 -67
  304. package/chunks/shell-FWZT6IUR.js +97 -0
  305. package/chunks/{skill-FEOKFX26.js → skill-RTQC2YGA.js} +121 -65
  306. package/chunks/skill-settings-AKMIXFSJ.js +123 -0
  307. package/chunks/spawnChannel-ZP23JL6O.js +116 -0
  308. package/chunks/standalone-update-P2Q5H4ZU.js +123 -0
  309. package/chunks/{startInteractiveUI-WHJPFOGH.js → startInteractiveUI-NSA6QPQZ.js} +2018 -1526
  310. package/chunks/{stdioHelpers-7A2EE64Z.js → stdioHelpers-AY6XCDY2.js} +3 -1
  311. package/chunks/{syntheticOutput-ML43GEFN.js → syntheticOutput-5URSQLIS.js} +3 -3
  312. package/chunks/{task-create-TMSV7RD5.js → task-create-3EXVDG3G.js} +12 -12
  313. package/chunks/{task-list-W55RBFRX.js → task-list-PIBYNSE7.js} +5 -6
  314. package/chunks/{task-stop-CXEH5L2D.js → task-stop-IV3BYWJC.js} +2 -2
  315. package/chunks/{task-update-N536I4GQ.js → task-update-H5BPDS5Y.js} +13 -14
  316. package/chunks/{team-create-QYSWCYCL.js → team-create-53T42W37.js} +56 -55
  317. package/chunks/{team-delete-FJRFX33M.js → team-delete-X3WPELS6.js} +25 -8
  318. package/chunks/{team-plan-approval-P7DAD644.js → team-plan-approval-D45ZC5OE.js} +50 -48
  319. package/chunks/terminal-image-renderer-FCRUWT6C.js +121 -0
  320. package/chunks/theme-manager-MHNPEDP7.js +107 -0
  321. package/chunks/{todoWrite-WDJNQEMW.js → todoWrite-A5IOZZ3P.js} +4 -4
  322. package/chunks/{tool-search-F2EMIAPV.js → tool-search-6RPBGGUX.js} +27 -23
  323. package/chunks/total-session-admission-MGV56XDK.js +117 -0
  324. package/chunks/trustedFolders-QJYEFMTU.js +127 -0
  325. package/chunks/{types-WYCFCEHR.js → types-HKLPXZB3.js} +1 -3
  326. package/chunks/{update-relaunch-4GF3OXMB.js → update-relaunch-ZDMQX63C.js} +6 -6
  327. package/chunks/updateCheck-4QZWCGP6.js +123 -0
  328. package/chunks/useAutoAcceptIndicator-DLIQN256.js +125 -0
  329. package/chunks/{validateNonInterActiveAuth-BPPA563H.js → validateNonInterActiveAuth-HGTG7AEK.js} +114 -107
  330. package/chunks/{version-ANP4TS6O.js → version-CPAZMC4B.js} +1 -1
  331. package/chunks/{web-fetch-QJADSAH3.js → web-fetch-N4K5M3M4.js} +16 -18
  332. package/chunks/{web-search-7TTQKEKM.js → web-search-4VZKM5SF.js} +9 -11
  333. package/chunks/{web-shell-static-A6QMUNNV.js → web-shell-static-PW7OEDQO.js} +3 -2
  334. package/chunks/{workflow-5XN6XSOT.js → workflow-2ALQKTHG.js} +229 -146
  335. package/chunks/workspace-providers-status-5JM4CB6R.js +119 -0
  336. package/chunks/{workspace-registration-store-XKVW2YVH.js → workspace-registration-store-RNMFVCH7.js} +2 -1
  337. package/chunks/workspace-registry-6EULAPZC.js +126 -0
  338. package/chunks/workspace-runtime-coordinator-IRI2DRCJ.js +127 -0
  339. package/chunks/workspace-service-SETHN4WE.js +131 -0
  340. package/chunks/workspace-skills-status-4D4EUEQU.js +118 -0
  341. package/chunks/{workspace-trust-reconciler-KLGOB53D.js → workspace-trust-reconciler-4LR3CDL4.js} +77 -76
  342. package/chunks/write-file-45GWPSOI.js +92 -0
  343. package/chunks/{zh-UNY63FR3.js → zh-2BQ7P6OD.js} +3 -0
  344. package/chunks/{zh-TW-RPTMRTHG.js → zh-TW-UFXRZTXX.js} +3 -0
  345. package/chunks/{zoom-image-6ASG6J4C.js → zoom-image-K6IQUKRC.js} +13 -13
  346. package/cli.js +129 -92
  347. package/locales/ca.js +6 -0
  348. package/locales/de.js +6 -0
  349. package/locales/en.js +6 -0
  350. package/locales/fr.js +6 -0
  351. package/locales/ja.js +6 -0
  352. package/locales/pt.js +6 -0
  353. package/locales/ru.js +6 -0
  354. package/locales/zh-TW.js +6 -0
  355. package/locales/zh.js +6 -0
  356. package/package.json +3 -3
  357. package/web-shell/assets/{abnfDiagram-VCTEODGH-uS-vtfs3.js → abnfDiagram-VCTEODGH-C80OZcPP.js} +1 -1
  358. package/web-shell/assets/{arc-BSTnk5pu.js → arc-AUqStYgf.js} +1 -1
  359. package/web-shell/assets/{architectureDiagram-5GKGNRK7-DCMyiqzy.js → architectureDiagram-5GKGNRK7-CQYOdYla.js} +1 -1
  360. package/web-shell/assets/{blockDiagram-NRAW4CY4-C7_gt0r1.js → blockDiagram-NRAW4CY4-lYKlFysy.js} +1 -1
  361. package/web-shell/assets/{c4Diagram-UCG6FXSJ-BeQg2L8B.js → c4Diagram-UCG6FXSJ-BoS23iIp.js} +1 -1
  362. package/web-shell/assets/channel-B0ovOJeK.js +1 -0
  363. package/web-shell/assets/{chunk-2Q5K7J3B-DnV9bKBe.js → chunk-2Q5K7J3B-DLLooL1E.js} +1 -1
  364. package/web-shell/assets/{chunk-5VM5RSS4-rT1GAcT7.js → chunk-5VM5RSS4-kPfJxScY.js} +1 -1
  365. package/web-shell/assets/{chunk-F27PBJKO-BcgSxqGA.js → chunk-F27PBJKO-CVGFBqkD.js} +1 -1
  366. package/web-shell/assets/{chunk-G27WJ6UU-Bw1ZhyZq.js → chunk-G27WJ6UU-CnM_kOVV.js} +1 -1
  367. package/web-shell/assets/{chunk-JWPE2WC7-D4iDw-96.js → chunk-JWPE2WC7-Cj-ajOkT.js} +1 -1
  368. package/web-shell/assets/{chunk-LCL6LL3I-D3MB3GTZ.js → chunk-LCL6LL3I-8L5c-wFo.js} +1 -1
  369. package/web-shell/assets/{chunk-POPQ4Y6H-Cep2ndrS.js → chunk-POPQ4Y6H-DsXTxpvL.js} +1 -1
  370. package/web-shell/assets/{chunk-SVP7TREG-BuahMHJc.js → chunk-SVP7TREG-DU5GE1_z.js} +1 -1
  371. package/web-shell/assets/{chunk-XXDRQBXY-BdujIvJY.js → chunk-XXDRQBXY-D2IIWZKt.js} +1 -1
  372. package/web-shell/assets/classDiagram-DTDB5LWJ-DMll2-6_.js +1 -0
  373. package/web-shell/assets/classDiagram-v2-JRS7N3AN-DMll2-6_.js +1 -0
  374. package/web-shell/assets/{cose-bilkent-JH36ORCC-CGLLUbF8.js → cose-bilkent-JH36ORCC-CF_txUJr.js} +1 -1
  375. package/web-shell/assets/{cynefin-OW5HDTMX-D6kDsRSh.js → cynefin-OW5HDTMX-DEZ-My-Z.js} +1 -1
  376. package/web-shell/assets/{cynefinDiagram-5FMLGOSQ-CCy78qZx.js → cynefinDiagram-5FMLGOSQ-BV7EKUS6.js} +1 -1
  377. package/web-shell/assets/{dagre-3AP2YEHR-D5gJuJsf.js → dagre-3AP2YEHR-JGIX0G4H.js} +1 -1
  378. package/web-shell/assets/{diagram-S7CK7UJ4-ZwEZ5W__.js → diagram-S7CK7UJ4-DdUY7q6A.js} +1 -1
  379. package/web-shell/assets/{diagram-UQ7AKVKN-B036APjv.js → diagram-UQ7AKVKN-fhzRZ19v.js} +1 -1
  380. package/web-shell/assets/{diagram-VSXAHHWV-BfDJ57Fq.js → diagram-VSXAHHWV-BOgOex6q.js} +1 -1
  381. package/web-shell/assets/{diagram-VX7I27RA-Wha0p5rT.js → diagram-VX7I27RA-C0NknIz6.js} +1 -1
  382. package/web-shell/assets/{diagram-Z3DM3KII-L20bz2K1.js → diagram-Z3DM3KII-C__3mUBX.js} +1 -1
  383. package/web-shell/assets/{ebnfDiagram-PWID7BFC-D1ZM0Cp7.js → ebnfDiagram-PWID7BFC-BvPhqycH.js} +1 -1
  384. package/web-shell/assets/{erDiagram-SSCWMZ5O-CMtA19v8.js → erDiagram-SSCWMZ5O-BqzoNoL9.js} +1 -1
  385. package/web-shell/assets/{flowDiagram-A5DVABFB-D96m326S.js → flowDiagram-A5DVABFB-CJKOre47.js} +1 -1
  386. package/web-shell/assets/{ganttDiagram-EL5Y4UJY-BuHiYG2o.js → ganttDiagram-EL5Y4UJY-C5xZjrp0.js} +1 -1
  387. package/web-shell/assets/{gitGraphDiagram-WWUBYQGX-Ck3NmkSw.js → gitGraphDiagram-WWUBYQGX-C0EjwuPK.js} +1 -1
  388. package/web-shell/assets/index-B_IPzA0h.js +2020 -0
  389. package/web-shell/assets/{index-CBGz4Qjl.js → index-C5M93syE.js} +1 -1
  390. package/web-shell/assets/index-D1OKGQlM.css +36 -0
  391. package/web-shell/assets/{infoDiagram-RXCK75RN-B6DAOyXe.js → infoDiagram-RXCK75RN-BUwzsYOs.js} +1 -1
  392. package/web-shell/assets/{ishikawaDiagram-5VMMS53U-Bpg-joFo.js → ishikawaDiagram-5VMMS53U-EDJ_DH3V.js} +1 -1
  393. package/web-shell/assets/{journeyDiagram-EYS64GPL-BclrHzGa.js → journeyDiagram-EYS64GPL-BnfcOsS0.js} +1 -1
  394. package/web-shell/assets/{kanban-definition-3QL26DDD-5gJzNTYG.js → kanban-definition-3QL26DDD-D_ofTVGu.js} +1 -1
  395. package/web-shell/assets/{layout-CEz_1L8z.js → layout-CBuQFdfZ.js} +1 -1
  396. package/web-shell/assets/{linear-CBjTq9Hc.js → linear-DRIHtmGv.js} +1 -1
  397. package/web-shell/assets/{mermaid.core-Dzhbxx_I.js → mermaid.core-CmR5wZH6.js} +6 -6
  398. package/web-shell/assets/{mindmap-definition-FBJOCRG2-DQdVqlBF.js → mindmap-definition-FBJOCRG2-BV374olC.js} +1 -1
  399. package/web-shell/assets/{pegDiagram-XKGWAZYB-D1JZlYXL.js → pegDiagram-XKGWAZYB-Bpy1SplK.js} +1 -1
  400. package/web-shell/assets/{pieDiagram-E7YTZNPT-BmkpYL0w.js → pieDiagram-E7YTZNPT-CeZx4hR2.js} +1 -1
  401. package/web-shell/assets/{quadrantDiagram-AXDQQJYC-DRvD6co5.js → quadrantDiagram-AXDQQJYC-COYKs14h.js} +1 -1
  402. package/web-shell/assets/{railroadDiagram-O6MQD6OU-CEdjDuoo.js → railroadDiagram-O6MQD6OU-5g2pOeTs.js} +1 -1
  403. package/web-shell/assets/{requirementDiagram-EFPCY7ZU-BCHG-ie8.js → requirementDiagram-EFPCY7ZU-Yn8uPGtF.js} +1 -1
  404. package/web-shell/assets/{sankeyDiagram-P5KCCOFB-C9cZMnye.js → sankeyDiagram-P5KCCOFB-CTayAGq6.js} +1 -1
  405. package/web-shell/assets/{sequenceDiagram-WJ2MYXX4-DYHRV4zL.js → sequenceDiagram-WJ2MYXX4-BFWDKzUU.js} +1 -1
  406. package/web-shell/assets/{sizeCapture-X5ZJPWSS-DlK21aye.js → sizeCapture-X5ZJPWSS-CtErUbj4.js} +1 -1
  407. package/web-shell/assets/{stateDiagram-HBIQ2CUA-BrmZssvk.js → stateDiagram-HBIQ2CUA-Co3HFzOd.js} +1 -1
  408. package/web-shell/assets/stateDiagram-v2-4QOOHH4V-C3gdRjPm.js +1 -0
  409. package/web-shell/assets/{swimlanes-XN3QIQJK-D9xfcayR.js → swimlanes-XN3QIQJK-CDZuxzOj.js} +1 -1
  410. package/web-shell/assets/swimlanesDiagram-VK2B7HYN-C34TGCPZ.js +8 -0
  411. package/web-shell/assets/{timeline-definition-24CTP7MA-D4gdDL_d.js → timeline-definition-24CTP7MA-UcNHj8At.js} +1 -1
  412. package/web-shell/assets/{vennDiagram-4TSXK5OY-DdhIExjI.js → vennDiagram-4TSXK5OY-JKR9pVFG.js} +1 -1
  413. package/web-shell/assets/{wardleyDiagram-VM6X3IG4-C9RS_Zof.js → wardleyDiagram-VM6X3IG4-C4SU-yIW.js} +1 -1
  414. package/web-shell/assets/{xychartDiagram-S5SC5T6Z-orRgmxWk.js → xychartDiagram-S5SC5T6Z-D_yoWGZ4.js} +1 -1
  415. package/web-shell/index.html +197 -3
  416. package/chunks/MaxSizedBox-X5FZSNAJ.js +0 -112
  417. package/chunks/agent-PJIPDWHB.js +0 -97
  418. package/chunks/agent-headless-B4JVD7UK.js +0 -87
  419. package/chunks/bridge-FKZTVIS3.js +0 -121
  420. package/chunks/channel-settings-store-4UFCLBBG.js +0 -117
  421. package/chunks/chunk-7DJCPZE3.js +0 -945
  422. package/chunks/chunk-CPBF7KYF.js +0 -51
  423. package/chunks/chunk-H72BQNM3.js +0 -645
  424. package/chunks/chunk-J2OSJFP3.js +0 -202
  425. package/chunks/chunk-K2OJUPOE.js +0 -78
  426. package/chunks/chunk-P3QQPMQA.js +0 -19
  427. package/chunks/chunk-RUGNCYNO.js +0 -699
  428. package/chunks/chunk-T26EAKDL.js +0 -13
  429. package/chunks/chunk-ZGXYO7DY.js +0 -3416
  430. package/chunks/chunk-ZYNTSXWK.js +0 -32
  431. package/chunks/config-utils-BBOCOGZW.js +0 -113
  432. package/chunks/contextCommand-EET67MQ2.js +0 -108
  433. package/chunks/daemon-status-provider-RFKZOK3S.js +0 -119
  434. package/chunks/daemon-trust-policy-DRZPFB4Q.js +0 -114
  435. package/chunks/deferred-core-runtime-BBVIOP24.js +0 -116
  436. package/chunks/earlyInputCapture-2JOMMNQ4.js +0 -109
  437. package/chunks/environment-MOZMPUHR.js +0 -131
  438. package/chunks/errors-DMPQVYJL.js +0 -115
  439. package/chunks/exitPlanMode-UE4FGCY4.js +0 -85
  440. package/chunks/handleAutoUpdate-SGXKTQM6.js +0 -111
  441. package/chunks/i18n-B42HKHKH.js +0 -126
  442. package/chunks/initializer-FB66WYR3.js +0 -115
  443. package/chunks/installationInfo-Y7AULNMX.js +0 -109
  444. package/chunks/list-2HWGSVRA.js +0 -118
  445. package/chunks/list-agents-DI3YFTDT.js +0 -80
  446. package/chunks/loadedSettingsAdapter-7Y7IEI6T.js +0 -112
  447. package/chunks/mcp-QGG5NMJJ.js +0 -112
  448. package/chunks/nonInteractiveCli-4LO2TL72.js +0 -186
  449. package/chunks/pidfile-JMF2CLMY.js +0 -113
  450. package/chunks/process-registry-PMJOA5CO.js +0 -172
  451. package/chunks/prompt-terminal-ledger-DCZO6S7C.js +0 -107
  452. package/chunks/resumeHistoryUtils-CZ4JRDVW.js +0 -120
  453. package/chunks/ripGrep-R6B6HWPX.js +0 -39
  454. package/chunks/runtime-TTIGSBM4.js +0 -150
  455. package/chunks/scheduled-tasks-QB4ZVUQS.js +0 -129
  456. package/chunks/scheduler-YRUG22M7.js +0 -124
  457. package/chunks/send-message-Y5I6NYAM.js +0 -295
  458. package/chunks/serve-MXXCDAQN.js +0 -120
  459. package/chunks/shell-6RTKZVMI.js +0 -95
  460. package/chunks/skill-settings-VA4XVYVL.js +0 -120
  461. package/chunks/spawnChannel-F6WVP723.js +0 -114
  462. package/chunks/standalone-update-RGILUQVK.js +0 -120
  463. package/chunks/terminal-image-renderer-SMQE46UJ.js +0 -116
  464. package/chunks/theme-manager-RFO5WWFF.js +0 -104
  465. package/chunks/total-session-admission-HTVTP4BS.js +0 -116
  466. package/chunks/trustedFolders-7OCNMUQE.js +0 -124
  467. package/chunks/updateCheck-7QBDXZRT.js +0 -120
  468. package/chunks/useAutoAcceptIndicator-UI4SNL2O.js +0 -122
  469. package/chunks/workspace-providers-status-PDFZWL5Q.js +0 -116
  470. package/chunks/workspace-registry-23R7XRB5.js +0 -125
  471. package/chunks/workspace-service-MUYO2QIC.js +0 -131
  472. package/chunks/workspace-skills-status-IKECTOOQ.js +0 -115
  473. package/chunks/write-file-GGRBB5TZ.js +0 -90
  474. package/web-shell/assets/channel-CENAhErJ.js +0 -1
  475. package/web-shell/assets/classDiagram-DTDB5LWJ-CM6FArGH.js +0 -1
  476. package/web-shell/assets/classDiagram-v2-JRS7N3AN-CM6FArGH.js +0 -1
  477. package/web-shell/assets/index-8hXIpvo4.js +0 -1865
  478. package/web-shell/assets/index-DclEv91i.css +0 -5
  479. package/web-shell/assets/stateDiagram-v2-4QOOHH4V-B2Q7XDaE.js +0 -1
  480. package/web-shell/assets/swimlanesDiagram-VK2B7HYN-DpZPOSbX.js +0 -8
@@ -463,6 +463,18 @@ var CHANNEL_PROMPT_AUTHORIZATION_META_KEY = "qwen.daemon.channelPromptAuthorizat
463
463
  var CHANNEL_PROMPT_META_KEY = "qwen.channel.prompt";
464
464
  var ACP_PRIVATE_PARENT_CAPABILITY_META_KEY = "qwen-code/private-parent-capability";
465
465
  var ACP_PRIVATE_PARENT_CAPABILITY_ENV = "QWEN_CODE_PRIVATE_ACP_CAPABILITY";
466
+ function resolvePromptImages(options) {
467
+ const images = options?.images && options.images.length > 0 ? options.images : options?.imageBase64 && options.imageMimeType ? [{ data: options.imageBase64, mimeType: options.imageMimeType }] : [];
468
+ return images.filter((image) => !!image && typeof image.data === "string" && image.data.length > 0 && typeof image.mimeType === "string" && image.mimeType.length > 0).map((image) => {
469
+ const cleaned = image.mimeType.split(";", 1)[0]?.trim().toLowerCase() ?? "";
470
+ return {
471
+ data: image.data,
472
+ // Normalize the alias like the daemon attachment store's own naming.
473
+ mimeType: cleaned === "image/jpg" ? "image/jpeg" : cleaned
474
+ };
475
+ });
476
+ }
477
+ __name(resolvePromptImages, "resolvePromptImages");
466
478
 
467
479
  // packages/channels/base/dist/ChannelLoopTools.js
468
480
  init_esbuild_shims();
@@ -791,11 +803,11 @@ var AcpBridge = class extends EventEmitter {
791
803
  this.on("slashCommandOutput", onSlashCommandOutput);
792
804
  this.on("responseBoundary", clearChunks);
793
805
  const prompt = [];
794
- if (options?.imageBase64 && options.imageMimeType) {
806
+ for (const image of resolvePromptImages(options)) {
795
807
  prompt.push({
796
808
  type: "image",
797
- data: options.imageBase64,
798
- mimeType: options.imageMimeType
809
+ data: image.data,
810
+ mimeType: image.mimeType
799
811
  });
800
812
  }
801
813
  prompt.push({ type: "text", text });
@@ -1106,14 +1118,14 @@ init_esbuild_shims();
1106
1118
  // packages/channels/base/dist/PollingChannelBase.js
1107
1119
  init_esbuild_shims();
1108
1120
  import { createHash as createHash2 } from "node:crypto";
1109
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync4, writeFileSync as writeFileSync4 } from "node:fs";
1121
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, renameSync as renameSync5, writeFileSync as writeFileSync5 } from "node:fs";
1110
1122
  import { join as join6 } from "node:path";
1111
- import process3 from "node:process";
1123
+ import process4 from "node:process";
1112
1124
 
1113
1125
  // packages/channels/base/dist/ChannelBase.js
1114
1126
  init_esbuild_shims();
1115
1127
  import { basename as basename3, join as join5 } from "node:path";
1116
- import { randomUUID as randomUUID2 } from "node:crypto";
1128
+ import { randomUUID as randomUUID3 } from "node:crypto";
1117
1129
 
1118
1130
  // packages/channels/base/dist/BlockStreamer.js
1119
1131
  init_esbuild_shims();
@@ -1153,6 +1165,10 @@ var BlockStreamer = class {
1153
1165
  this.clearIdleTimer();
1154
1166
  this.buffer = "";
1155
1167
  }
1168
+ /** Await already-queued sends without emitting the buffered text. */
1169
+ async drain() {
1170
+ await this.sending;
1171
+ }
1156
1172
  // ---------------------------------------------------------------------------
1157
1173
  // Internal
1158
1174
  // ---------------------------------------------------------------------------
@@ -1181,8 +1197,10 @@ var BlockStreamer = class {
1181
1197
  const trimmed = text.trim();
1182
1198
  if (!trimmed)
1183
1199
  return;
1200
+ const block = /\n[^\S\n]*$/.test(text) ? `${trimmed}
1201
+ ` : trimmed;
1184
1202
  this.blockCount++;
1185
- this.sending = this.sending.then(() => this.opts.send(trimmed)).catch(() => {
1203
+ this.sending = this.sending.then(() => this.opts.send(block)).catch(() => {
1186
1204
  });
1187
1205
  }
1188
1206
  /**
@@ -1654,6 +1672,7 @@ var SessionRouter = class {
1654
1672
  defaultScope;
1655
1673
  channelScopes = /* @__PURE__ */ new Map();
1656
1674
  channelApprovalModes = /* @__PURE__ */ new Map();
1675
+ channelsWithoutLoops = /* @__PURE__ */ new Set();
1657
1676
  persistPath;
1658
1677
  recoveryMode;
1659
1678
  constructor(bridge, defaultCwd, scope = "user", persistPath, options = {}) {
@@ -1666,6 +1685,7 @@ var SessionRouter = class {
1666
1685
  /** Replace the bridge instance (used after crash recovery restart). */
1667
1686
  setBridge(bridge) {
1668
1687
  this.bridge = bridge;
1688
+ this.liveSessionIds.clear();
1669
1689
  }
1670
1690
  /** Set scope override for a specific channel. */
1671
1691
  setChannelScope(channelName, scope) {
@@ -1678,6 +1698,13 @@ var SessionRouter = class {
1678
1698
  this.channelApprovalModes.delete(channelName);
1679
1699
  }
1680
1700
  }
1701
+ setChannelLoopsEnabled(channelName, enabled) {
1702
+ if (enabled) {
1703
+ this.channelsWithoutLoops.delete(channelName);
1704
+ } else {
1705
+ this.channelsWithoutLoops.add(channelName);
1706
+ }
1707
+ }
1681
1708
  routingKey(channelName, senderId, chatId, threadId) {
1682
1709
  const scope = this.channelScopes.get(channelName) || this.defaultScope;
1683
1710
  switch (scope) {
@@ -1694,7 +1721,12 @@ var SessionRouter = class {
1694
1721
  }
1695
1722
  sessionOptions(channelName) {
1696
1723
  const approvalMode = this.channelApprovalModes.get(channelName);
1697
- return approvalMode ? { approvalMode } : void 0;
1724
+ const loopsDisabled = this.channelsWithoutLoops.has(channelName);
1725
+ return {
1726
+ ...approvalMode ? { approvalMode } : {},
1727
+ ...loopsDisabled ? { enableChannelLoops: false } : {},
1728
+ sourceId: channelName
1729
+ };
1698
1730
  }
1699
1731
  async resolve(channelName, senderId, chatId, threadId, cwd, isGroup, options) {
1700
1732
  const key = this.routingKey(channelName, senderId, chatId, options?.routingThreadId ?? threadId);
@@ -1771,7 +1803,7 @@ var SessionRouter = class {
1771
1803
  async createAndStoreSession(key, input, operation) {
1772
1804
  const loadWindow = this.beginSessionLoad();
1773
1805
  try {
1774
- const sessionId = await this.createLiveSession(input.cwd, loadWindow, key, this.sessionOptions(input.channelName), operation, input.channelName);
1806
+ const sessionId = await this.createLiveSession(input.cwd, loadWindow, key, this.sessionOptions(input.channelName), operation);
1775
1807
  try {
1776
1808
  this.assertOperationCurrent(operation);
1777
1809
  } catch (error) {
@@ -1827,7 +1859,7 @@ var SessionRouter = class {
1827
1859
  } catch (loadError) {
1828
1860
  this.assertOperationCurrent(operation);
1829
1861
  try {
1830
- const replacement = await this.createLiveSession(input.cwd, loadWindow, key, this.sessionOptions(input.channelName), operation, input.channelName);
1862
+ const replacement = await this.createLiveSession(input.cwd, loadWindow, key, this.sessionOptions(input.channelName), operation);
1831
1863
  try {
1832
1864
  this.assertOperationCurrent(operation);
1833
1865
  } catch (error) {
@@ -1863,6 +1895,9 @@ var SessionRouter = class {
1863
1895
  getTarget(sessionId) {
1864
1896
  return this.toTarget.get(sessionId);
1865
1897
  }
1898
+ isSessionLive(sessionId) {
1899
+ return this.toTarget.has(sessionId) && this.isLive(sessionId);
1900
+ }
1866
1901
  getSession(channelName, senderId, chatId, threadId) {
1867
1902
  return this.toSession.get(this.routingKey(channelName, senderId, chatId, threadId));
1868
1903
  }
@@ -1881,6 +1916,120 @@ var SessionRouter = class {
1881
1916
  }
1882
1917
  return false;
1883
1918
  }
1919
+ getSessionCwd(sessionId) {
1920
+ return this.toCwd.get(sessionId);
1921
+ }
1922
+ async createManagedSession(target, cwd) {
1923
+ const loadWindow = this.beginSessionLoad();
1924
+ const lifecycleGeneration = this.lifecycleGeneration;
1925
+ const bridge = this.bridge;
1926
+ const bindingToken = {};
1927
+ try {
1928
+ let lastDeadSessionId;
1929
+ for (let attempt = 0; attempt < 2; attempt++) {
1930
+ const sessionId = await bridge.newSession(cwd, {
1931
+ ...this.sessionOptions(target.channelName),
1932
+ sourceId: target.channelName
1933
+ }, bindingToken);
1934
+ if (lifecycleGeneration !== this.lifecycleGeneration || bridge !== this.bridge) {
1935
+ this.scheduleManagedDiscard(bridge, sessionId, bindingToken);
1936
+ throw new Error("Managed session creation was invalidated");
1937
+ }
1938
+ if (typeof sessionId !== "string" || sessionId.length === 0) {
1939
+ throw new Error("Invalid session ID from bridge");
1940
+ }
1941
+ if (loadWindow.delete(sessionId)) {
1942
+ lastDeadSessionId = sessionId;
1943
+ await bridge.discardSession?.(sessionId, bindingToken);
1944
+ continue;
1945
+ }
1946
+ this.toTarget.set(sessionId, target);
1947
+ this.toCwd.set(sessionId, cwd);
1948
+ this.liveSessionIds.add(sessionId);
1949
+ return sessionId;
1950
+ }
1951
+ throw new Error(`Managed session ${lastDeadSessionId ?? "unknown"} died before creation completed`);
1952
+ } finally {
1953
+ this.endSessionLoad(loadWindow);
1954
+ }
1955
+ }
1956
+ async loadManagedSession(sessionId, target, cwd) {
1957
+ if (this.liveSessionIds.has(sessionId)) {
1958
+ this.toTarget.set(sessionId, target);
1959
+ this.toCwd.set(sessionId, cwd);
1960
+ return { loaded: false };
1961
+ }
1962
+ const loadWindow = this.beginSessionLoad();
1963
+ const lifecycleGeneration = this.lifecycleGeneration;
1964
+ const bridge = this.bridge;
1965
+ const bindingToken = {};
1966
+ let loadedSessionId;
1967
+ try {
1968
+ loadedSessionId = await bridge.loadSession(sessionId, cwd, this.sessionOptions(target.channelName), bindingToken);
1969
+ if (lifecycleGeneration !== this.lifecycleGeneration || bridge !== this.bridge) {
1970
+ this.scheduleManagedDiscard(bridge, loadedSessionId, bindingToken);
1971
+ loadedSessionId = void 0;
1972
+ throw new Error("Managed session load was invalidated");
1973
+ }
1974
+ if (loadedSessionId !== sessionId) {
1975
+ const unexpectedSessionId = loadedSessionId;
1976
+ await bridge.discardSession?.(unexpectedSessionId, bindingToken);
1977
+ loadedSessionId = void 0;
1978
+ throw new Error(`Bridge returned session ${unexpectedSessionId || "unknown"} while loading ${sessionId}`);
1979
+ }
1980
+ if (loadWindow.delete(sessionId)) {
1981
+ throw new Error(`Managed session ${sessionId} died before loading completed`);
1982
+ }
1983
+ this.toTarget.set(sessionId, target);
1984
+ this.toCwd.set(sessionId, cwd);
1985
+ this.liveSessionIds.add(sessionId);
1986
+ return { loaded: true };
1987
+ } catch (error) {
1988
+ if (loadedSessionId) {
1989
+ await bridge.discardSession?.(loadedSessionId, bindingToken).catch(() => void 0);
1990
+ }
1991
+ throw error;
1992
+ } finally {
1993
+ this.endSessionLoad(loadWindow);
1994
+ }
1995
+ }
1996
+ activateManagedSession(sessionId, target, cwd) {
1997
+ const key = this.routingKey(target.channelName, target.senderId, target.chatId, target.threadId);
1998
+ if (this.toSession.get(key) === sessionId)
1999
+ return;
2000
+ this.invalidateRouteOperation(key);
2001
+ this.toSession.set(key, sessionId);
2002
+ this.toTarget.set(sessionId, target);
2003
+ this.toCwd.set(sessionId, cwd);
2004
+ this.persist();
2005
+ }
2006
+ forgetManagedSession(sessionId) {
2007
+ let changed = false;
2008
+ for (const [key, mappedSessionId] of this.toSession) {
2009
+ if (mappedSessionId !== sessionId)
2010
+ continue;
2011
+ this.invalidateRouteOperation(key);
2012
+ this.toSession.delete(key);
2013
+ changed = true;
2014
+ }
2015
+ changed = this.toTarget.delete(sessionId) || changed;
2016
+ changed = this.toCwd.delete(sessionId) || changed;
2017
+ changed = this.liveSessionIds.delete(sessionId) || changed;
2018
+ if (changed)
2019
+ this.persist();
2020
+ }
2021
+ async detachManagedSession(sessionId) {
2022
+ try {
2023
+ if (this.liveSessionIds.has(sessionId)) {
2024
+ if (!this.bridge.discardSession) {
2025
+ throw new Error("Managed session detach is not supported");
2026
+ }
2027
+ await this.bridge.discardSession(sessionId);
2028
+ }
2029
+ } finally {
2030
+ this.forgetManagedSession(sessionId);
2031
+ }
2032
+ }
1884
2033
  /**
1885
2034
  * Remove session(s) for the given sender. Returns the removed session IDs.
1886
2035
  */
@@ -2055,6 +2204,7 @@ var SessionRouter = class {
2055
2204
  this.toSession.set(key, sessionId);
2056
2205
  this.toTarget.set(sessionId, entry.target);
2057
2206
  this.toCwd.set(sessionId, entry.cwd);
2207
+ this.liveSessionIds.add(sessionId);
2058
2208
  reservation.resolve(sessionId);
2059
2209
  if (sessionId !== entry.sessionId) {
2060
2210
  changed = true;
@@ -2193,11 +2343,11 @@ var SessionRouter = class {
2193
2343
  }
2194
2344
  }
2195
2345
  }
2196
- async createLiveSession(cwd, loadWindow, routingKey, options, operation, sourceId) {
2346
+ async createLiveSession(cwd, loadWindow, routingKey, options, operation) {
2197
2347
  const maxAttempts = 2;
2198
2348
  let lastDeadSessionId;
2199
2349
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
2200
- const sessionId = await this.bridge.newSession(cwd, { ...options, sourceId }, operation);
2350
+ const sessionId = await this.bridge.newSession(cwd, options, operation);
2201
2351
  try {
2202
2352
  this.assertOperationCurrent(operation);
2203
2353
  } catch (error) {
@@ -2278,6 +2428,12 @@ var SessionRouter = class {
2278
2428
  } catch {
2279
2429
  }
2280
2430
  }
2431
+ scheduleManagedDiscard(bridge, sessionId, bindingToken) {
2432
+ try {
2433
+ void bridge.discardSession?.(sessionId, bindingToken).catch(() => void 0);
2434
+ } catch {
2435
+ }
2436
+ }
2281
2437
  createSessionReservation() {
2282
2438
  let resolveReservation;
2283
2439
  let rejectReservation;
@@ -2296,6 +2452,676 @@ var SessionRouter = class {
2296
2452
  }
2297
2453
  };
2298
2454
 
2455
+ // packages/channels/base/dist/named-session-manager.js
2456
+ init_esbuild_shims();
2457
+ import { randomUUID as randomUUID2 } from "node:crypto";
2458
+ import { chmodSync as chmodSync3, existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync4, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
2459
+ import { dirname as dirname4 } from "node:path";
2460
+ import process3 from "node:process";
2461
+ var REGISTRY_VERSION = 1;
2462
+ var MAX_OPEN_TASKS = 8;
2463
+ var TASK_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$/;
2464
+ var NamedSessionManager = class {
2465
+ static {
2466
+ __name(this, "NamedSessionManager");
2467
+ }
2468
+ channelName;
2469
+ cwd;
2470
+ canonicalCwd;
2471
+ filePath;
2472
+ router;
2473
+ isBusy;
2474
+ now;
2475
+ registry;
2476
+ taskBySessionId;
2477
+ ownerOperations = /* @__PURE__ */ new Map();
2478
+ constructor(options) {
2479
+ this.channelName = options.channelName;
2480
+ this.cwd = options.cwd;
2481
+ this.canonicalCwd = canonicalizeWorkspacePath(options.cwd);
2482
+ this.filePath = options.filePath;
2483
+ this.router = options.router;
2484
+ this.isBusy = options.isBusy;
2485
+ this.now = options.now ?? Date.now;
2486
+ this.registry = this.readRegistry();
2487
+ this.taskBySessionId = this.buildTaskIndex(this.registry);
2488
+ }
2489
+ presentation(sessionId) {
2490
+ const reference = this.taskBySessionId.get(sessionId);
2491
+ return reference ? this.cloneTaskReference(reference) : void 0;
2492
+ }
2493
+ async resolvePresentation(sessionId) {
2494
+ const existing = this.presentation(sessionId);
2495
+ if (existing)
2496
+ return existing;
2497
+ const target = this.router.getTarget(sessionId);
2498
+ if (target?.channelName !== this.channelName || this.router.getSession(this.channelName, target.senderId, target.chatId) !== sessionId) {
2499
+ return void 0;
2500
+ }
2501
+ return this.withOwnerLock(target, async () => {
2502
+ const concurrent = this.presentation(sessionId);
2503
+ if (concurrent)
2504
+ return concurrent;
2505
+ if (this.getOwner(target))
2506
+ return void 0;
2507
+ const routedTarget = this.router.getTarget(sessionId);
2508
+ if (routedTarget?.channelName !== this.channelName || routedTarget.chatId !== target.chatId || routedTarget.senderId !== target.senderId || this.router.getSession(this.channelName, target.senderId, target.chatId) !== sessionId) {
2509
+ return void 0;
2510
+ }
2511
+ const routedCwd = this.router.getSessionCwd(sessionId);
2512
+ if (routedCwd !== void 0 && !this.isCurrentCwd(routedCwd)) {
2513
+ return void 0;
2514
+ }
2515
+ const timestamp = this.nextTimestamp();
2516
+ const task = {
2517
+ name: "default",
2518
+ sessionId,
2519
+ cwd: routedCwd ?? this.cwd,
2520
+ isolation: "shared",
2521
+ status: "open",
2522
+ target: routedTarget,
2523
+ createdAt: timestamp,
2524
+ updatedAt: timestamp,
2525
+ lastSelectedAt: timestamp
2526
+ };
2527
+ this.commitOwner(this.createOwner(routedTarget, task.name, [task]));
2528
+ return this.presentation(sessionId);
2529
+ });
2530
+ }
2531
+ resolve(input, reserve) {
2532
+ return this.withOwnerLock(input, () => this.resolveLocked(input, reserve));
2533
+ }
2534
+ resolveAfterPreparation(input, prepare, needsSession, reserve) {
2535
+ return this.withOwnerLock(input, async () => {
2536
+ if (await prepare() === false)
2537
+ return { status: "aborted" };
2538
+ if (!needsSession())
2539
+ return { status: "bypassed" };
2540
+ try {
2541
+ return {
2542
+ status: "resolved",
2543
+ sessionId: await this.resolveLocked(input, reserve)
2544
+ };
2545
+ } catch (error) {
2546
+ return { status: "resolve_error", error };
2547
+ }
2548
+ });
2549
+ }
2550
+ list(input, includeClosed) {
2551
+ return this.withOwnerLock(input, async () => {
2552
+ const owner = await this.ensureOwner(input, false);
2553
+ if (!owner)
2554
+ return [];
2555
+ return owner.tasks.filter((task) => includeClosed || task.status === "open").map((task) => this.view(owner, task));
2556
+ });
2557
+ }
2558
+ current(input) {
2559
+ return this.withOwnerLock(input, async () => {
2560
+ const owner = await this.ensureOwner(input, false);
2561
+ if (!owner?.activeTaskName)
2562
+ return void 0;
2563
+ const task = this.findTask(owner, owner.activeTaskName);
2564
+ return task ? this.selection(owner, task) : void 0;
2565
+ });
2566
+ }
2567
+ lookup(input, name) {
2568
+ return this.withOwnerLock(input, async () => {
2569
+ const owner = await this.ensureOwner(input, false);
2570
+ if (!owner)
2571
+ return void 0;
2572
+ const task = this.findTask(owner, name);
2573
+ return task ? this.selection(owner, task) : void 0;
2574
+ });
2575
+ }
2576
+ resumeReserved(input, sessionId) {
2577
+ return this.withOwnerLock(input, async () => {
2578
+ const owner = await this.ensureOwner(input, false);
2579
+ const task = owner?.tasks.find((candidate) => candidate.sessionId === sessionId && candidate.status === "open");
2580
+ if (!task)
2581
+ return false;
2582
+ await this.loadTask(task, `Could not reload reserved task "${task.name}".`);
2583
+ return true;
2584
+ });
2585
+ }
2586
+ create(input, name) {
2587
+ return this.withOwnerLock(input, async () => {
2588
+ this.validateName(name);
2589
+ const existingOwner = await this.ensureOwner(input, false);
2590
+ const owner = existingOwner ?? this.createOwner(input, null, []);
2591
+ if (this.findTask(owner, name)) {
2592
+ throw new Error(`Task "${name}" already exists.`);
2593
+ }
2594
+ if (this.openTaskCount(owner) >= MAX_OPEN_TASKS) {
2595
+ throw new Error("You already have eight open tasks. Close one before creating another.");
2596
+ }
2597
+ const timestamp = this.nextTimestamp(owner);
2598
+ const target = this.target(input);
2599
+ const sessionId = await this.createSession(target, this.cwd, `Could not create task "${name}".`);
2600
+ const task = {
2601
+ name,
2602
+ sessionId,
2603
+ cwd: this.cwd,
2604
+ isolation: "shared",
2605
+ status: "open",
2606
+ target,
2607
+ createdAt: timestamp,
2608
+ updatedAt: timestamp,
2609
+ lastSelectedAt: timestamp
2610
+ };
2611
+ const nextOwner = {
2612
+ ...owner,
2613
+ activeTaskName: name,
2614
+ tasks: [...owner.tasks, task]
2615
+ };
2616
+ try {
2617
+ this.commitOwner(nextOwner);
2618
+ } catch (error) {
2619
+ await this.router.detachManagedSession(sessionId).catch(() => void 0);
2620
+ throw error;
2621
+ }
2622
+ this.router.activateManagedSession(sessionId, target, this.cwd);
2623
+ return this.selection(nextOwner, task);
2624
+ });
2625
+ }
2626
+ use(input, name) {
2627
+ return this.withOwnerLock(input, async () => {
2628
+ const owner = await this.ensureOwner(input, false);
2629
+ if (!owner)
2630
+ throw new Error("No named tasks exist in this chat.");
2631
+ const task = this.findTask(owner, name);
2632
+ if (!task)
2633
+ throw new Error(`Task "${name}" was not found.`);
2634
+ if (task.status === "closed" && this.openTaskCount(owner) >= MAX_OPEN_TASKS) {
2635
+ throw new Error("You already have eight open tasks. Close one before reopening another.");
2636
+ }
2637
+ const timestamp = this.nextTimestamp(owner);
2638
+ const loaded = await this.loadTask(task, `Could not load task "${task.name}". The current task was not changed.`);
2639
+ const updatedTask = {
2640
+ ...task,
2641
+ status: "open",
2642
+ updatedAt: timestamp,
2643
+ lastSelectedAt: timestamp
2644
+ };
2645
+ const nextOwner = this.replaceTask(owner, updatedTask, task.name);
2646
+ try {
2647
+ this.commitOwner(nextOwner);
2648
+ } catch (error) {
2649
+ if (loaded) {
2650
+ await this.router.detachManagedSession(task.sessionId).catch(() => void 0);
2651
+ }
2652
+ throw error;
2653
+ }
2654
+ this.router.activateManagedSession(task.sessionId, task.target, task.cwd);
2655
+ return this.selection(nextOwner, updatedTask);
2656
+ });
2657
+ }
2658
+ close(input, name) {
2659
+ return this.withOwnerLock(input, async () => {
2660
+ const owner = await this.ensureOwner(input, false);
2661
+ if (!owner)
2662
+ throw new Error("No named tasks exist in this chat.");
2663
+ const task = this.findTask(owner, name);
2664
+ if (!task)
2665
+ throw new Error(`Task "${name}" was not found.`);
2666
+ if (task.status === "closed") {
2667
+ throw new Error(`Task "${task.name}" is already closed.`);
2668
+ }
2669
+ if (this.isBusy(task.sessionId)) {
2670
+ throw new Error(`Task "${task.name}" is still running or waiting for permission.`);
2671
+ }
2672
+ const wasActive = this.sameName(owner.activeTaskName, task.name);
2673
+ const timestamp = this.nextTimestamp(owner);
2674
+ const replacement = wasActive ? owner.tasks.filter((candidate) => candidate.status === "open" && !this.sameName(candidate.name, task.name)).sort((a, b) => b.lastSelectedAt - a.lastSelectedAt)[0] : void 0;
2675
+ let replacementLoaded = false;
2676
+ if (replacement) {
2677
+ replacementLoaded = await this.loadTask(replacement, `Could not load fallback task "${replacement.name}". Task "${task.name}" was not closed.`);
2678
+ }
2679
+ const closedTask = {
2680
+ ...task,
2681
+ status: "closed",
2682
+ updatedAt: timestamp
2683
+ };
2684
+ let nextOwner = this.replaceTask(owner, closedTask, wasActive ? replacement?.name ?? null : owner.activeTaskName);
2685
+ const selectedReplacement = replacement ? {
2686
+ ...replacement,
2687
+ updatedAt: timestamp,
2688
+ lastSelectedAt: timestamp
2689
+ } : void 0;
2690
+ if (selectedReplacement) {
2691
+ nextOwner = this.replaceTask(nextOwner, selectedReplacement, selectedReplacement.name);
2692
+ }
2693
+ try {
2694
+ this.commitOwner(nextOwner);
2695
+ } catch (error) {
2696
+ if (replacement && replacementLoaded) {
2697
+ await this.router.detachManagedSession(replacement.sessionId).catch(() => void 0);
2698
+ }
2699
+ throw error;
2700
+ }
2701
+ if (replacement) {
2702
+ this.router.activateManagedSession(replacement.sessionId, replacement.target, replacement.cwd);
2703
+ }
2704
+ try {
2705
+ await this.router.detachManagedSession(task.sessionId);
2706
+ } catch (error) {
2707
+ this.commitOwner(owner);
2708
+ this.router.forgetManagedSession(task.sessionId);
2709
+ await this.loadTask(task, `Task "${task.name}" could not be restored after close failed.`);
2710
+ if (wasActive) {
2711
+ this.router.activateManagedSession(task.sessionId, task.target, task.cwd);
2712
+ }
2713
+ throw new Error(`Failed to close task "${task.name}".`, {
2714
+ cause: error
2715
+ });
2716
+ }
2717
+ const activeTask = nextOwner.activeTaskName ? this.findTask(nextOwner, nextOwner.activeTaskName) : void 0;
2718
+ return {
2719
+ closed: this.view(nextOwner, closedTask),
2720
+ ...activeTask ? { active: this.view(nextOwner, activeTask) } : {}
2721
+ };
2722
+ });
2723
+ }
2724
+ reset(input) {
2725
+ return this.withOwnerLock(input, async () => {
2726
+ const owner = await this.ensureOwner(input, false);
2727
+ if (!owner?.activeTaskName)
2728
+ return void 0;
2729
+ const task = this.findTask(owner, owner.activeTaskName);
2730
+ if (!task || task.status !== "open")
2731
+ return void 0;
2732
+ const timestamp = this.nextTimestamp(owner);
2733
+ const sessionId = await this.createSession(task.target, task.cwd, `Could not reset task "${task.name}".`);
2734
+ const updatedTask = {
2735
+ ...task,
2736
+ sessionId,
2737
+ updatedAt: timestamp,
2738
+ lastSelectedAt: timestamp
2739
+ };
2740
+ const nextOwner = this.replaceTask(owner, updatedTask, task.name);
2741
+ try {
2742
+ this.commitOwner(nextOwner);
2743
+ } catch (error) {
2744
+ await this.router.detachManagedSession(sessionId).catch(() => void 0);
2745
+ throw error;
2746
+ }
2747
+ this.router.activateManagedSession(sessionId, updatedTask.target, updatedTask.cwd);
2748
+ this.router.forgetManagedSession(task.sessionId);
2749
+ return {
2750
+ name: task.name,
2751
+ previousSessionId: task.sessionId,
2752
+ sessionId
2753
+ };
2754
+ });
2755
+ }
2756
+ async ensureOwner(input, createDefault) {
2757
+ const existing = this.getOwner(input);
2758
+ if (existing)
2759
+ return existing;
2760
+ const sessionId = this.router.getSession(this.channelName, input.senderId, input.chatId);
2761
+ const routedTarget = sessionId ? this.router.getTarget(sessionId) : void 0;
2762
+ if (sessionId && routedTarget && (routedTarget.channelName !== this.channelName || routedTarget.chatId !== input.chatId || routedTarget.senderId !== input.senderId)) {
2763
+ this.adoptForeignLegacyRoute(sessionId, routedTarget);
2764
+ }
2765
+ if (sessionId && routedTarget?.channelName === this.channelName && routedTarget.chatId === input.chatId && routedTarget.senderId === input.senderId) {
2766
+ const routedCwd = this.router.getSessionCwd(sessionId);
2767
+ if (routedCwd !== void 0 && !this.isCurrentCwd(routedCwd)) {
2768
+ await this.router.detachManagedSession(sessionId).catch(() => void 0);
2769
+ } else {
2770
+ const timestamp2 = this.nextTimestamp();
2771
+ const task2 = {
2772
+ name: "default",
2773
+ sessionId,
2774
+ cwd: routedCwd ?? this.cwd,
2775
+ isolation: "shared",
2776
+ status: "open",
2777
+ target: routedTarget,
2778
+ createdAt: timestamp2,
2779
+ updatedAt: timestamp2,
2780
+ lastSelectedAt: timestamp2
2781
+ };
2782
+ const adopted = this.createOwner(input, task2.name, [task2]);
2783
+ this.commitOwner(adopted);
2784
+ return adopted;
2785
+ }
2786
+ }
2787
+ if (!createDefault)
2788
+ return void 0;
2789
+ const target = this.target(input);
2790
+ const timestamp = this.nextTimestamp();
2791
+ const createdSessionId = await this.createSession(target, this.cwd, "Could not create the default task.");
2792
+ const task = {
2793
+ name: "default",
2794
+ sessionId: createdSessionId,
2795
+ cwd: this.cwd,
2796
+ isolation: "shared",
2797
+ status: "open",
2798
+ target,
2799
+ createdAt: timestamp,
2800
+ updatedAt: timestamp,
2801
+ lastSelectedAt: timestamp
2802
+ };
2803
+ const created = this.createOwner(input, task.name, [task]);
2804
+ try {
2805
+ this.commitOwner(created);
2806
+ } catch (error) {
2807
+ await this.router.detachManagedSession(createdSessionId).catch(() => void 0);
2808
+ throw error;
2809
+ }
2810
+ this.router.activateManagedSession(createdSessionId, target, this.cwd);
2811
+ return created;
2812
+ }
2813
+ async resolveLocked(input, reserve) {
2814
+ const owner = await this.ensureOwner(input, true);
2815
+ if (!owner?.activeTaskName)
2816
+ return void 0;
2817
+ const task = this.findTask(owner, owner.activeTaskName);
2818
+ if (!task || task.status !== "open") {
2819
+ throw new Error("The selected Channel task is unavailable.");
2820
+ }
2821
+ const release = reserve?.(task.sessionId);
2822
+ try {
2823
+ await this.loadTask(task, `Could not load task "${task.name}". No replacement session was created.`);
2824
+ this.router.activateManagedSession(task.sessionId, task.target, task.cwd);
2825
+ return task.sessionId;
2826
+ } catch (error) {
2827
+ release?.();
2828
+ throw error;
2829
+ }
2830
+ }
2831
+ adoptForeignLegacyRoute(sessionId, target) {
2832
+ const cwd = this.router.getSessionCwd(sessionId);
2833
+ if (target.channelName !== this.channelName || cwd === void 0 || !this.isCurrentCwd(cwd)) {
2834
+ this.router.forgetManagedSession(sessionId);
2835
+ return;
2836
+ }
2837
+ const existing = this.getOwner(target);
2838
+ if (existing) {
2839
+ if (existing.tasks.some((task2) => task2.sessionId === sessionId))
2840
+ return;
2841
+ this.router.forgetManagedSession(sessionId);
2842
+ return;
2843
+ }
2844
+ const timestamp = this.nextTimestamp();
2845
+ const task = {
2846
+ name: "default",
2847
+ sessionId,
2848
+ cwd,
2849
+ isolation: "shared",
2850
+ status: "open",
2851
+ target,
2852
+ createdAt: timestamp,
2853
+ updatedAt: timestamp,
2854
+ lastSelectedAt: timestamp
2855
+ };
2856
+ this.commitOwner(this.createOwner(target, task.name, [task]));
2857
+ }
2858
+ async loadTask(task, failureMessage) {
2859
+ try {
2860
+ return (await this.router.loadManagedSession(task.sessionId, task.target, task.cwd)).loaded;
2861
+ } catch (error) {
2862
+ throw new Error(failureMessage, { cause: error });
2863
+ }
2864
+ }
2865
+ async createSession(target, cwd, failureMessage) {
2866
+ try {
2867
+ return await this.router.createManagedSession(target, cwd);
2868
+ } catch (error) {
2869
+ throw new Error(failureMessage, { cause: error });
2870
+ }
2871
+ }
2872
+ withOwnerLock(input, operation) {
2873
+ const key = JSON.stringify([
2874
+ this.channelName,
2875
+ input.chatId,
2876
+ input.senderId
2877
+ ]);
2878
+ const previous = this.ownerOperations.get(key) ?? Promise.resolve();
2879
+ const result = previous.catch(() => void 0).then(operation);
2880
+ const tail = result.then(() => void 0, () => void 0);
2881
+ this.ownerOperations.set(key, tail);
2882
+ return result.finally(() => {
2883
+ if (this.ownerOperations.get(key) === tail) {
2884
+ this.ownerOperations.delete(key);
2885
+ }
2886
+ });
2887
+ }
2888
+ getOwner(input) {
2889
+ const owner = this.registry.owners.find((candidate) => candidate.channelName === this.channelName && candidate.chatId === input.chatId && candidate.senderId === input.senderId);
2890
+ return owner ? this.cloneOwner(owner) : void 0;
2891
+ }
2892
+ createOwner(input, activeTaskName, tasks) {
2893
+ return {
2894
+ channelName: this.channelName,
2895
+ chatId: input.chatId,
2896
+ senderId: input.senderId,
2897
+ activeTaskName,
2898
+ tasks
2899
+ };
2900
+ }
2901
+ target(input) {
2902
+ return {
2903
+ channelName: this.channelName,
2904
+ senderId: input.senderId,
2905
+ chatId: input.chatId,
2906
+ ...input.threadId !== void 0 ? { threadId: input.threadId } : {},
2907
+ ...input.isGroup !== void 0 ? { isGroup: input.isGroup } : {}
2908
+ };
2909
+ }
2910
+ findTask(owner, name) {
2911
+ const normalized = this.normalizeName(name);
2912
+ return owner.tasks.find((task) => this.normalizeName(task.name) === normalized);
2913
+ }
2914
+ replaceTask(owner, task, activeTaskName) {
2915
+ return {
2916
+ ...owner,
2917
+ activeTaskName,
2918
+ tasks: owner.tasks.map((candidate) => this.sameName(candidate.name, task.name) ? task : candidate)
2919
+ };
2920
+ }
2921
+ view(owner, task) {
2922
+ return {
2923
+ name: task.name,
2924
+ status: task.status,
2925
+ isolation: task.isolation,
2926
+ active: task.status === "open" && this.sameName(owner.activeTaskName, task.name)
2927
+ };
2928
+ }
2929
+ selection(owner, task) {
2930
+ return { ...this.view(owner, task), sessionId: task.sessionId };
2931
+ }
2932
+ openTaskCount(owner) {
2933
+ return owner.tasks.filter((task) => task.status === "open").length;
2934
+ }
2935
+ nextTimestamp(owner) {
2936
+ const now = this.now();
2937
+ if (!Number.isSafeInteger(now) || now < 0) {
2938
+ throw new Error("Invalid named-session clock value.");
2939
+ }
2940
+ let timestamp = now;
2941
+ for (const task of owner?.tasks ?? []) {
2942
+ timestamp = Math.max(timestamp, task.createdAt + 1, task.updatedAt + 1, task.lastSelectedAt + 1);
2943
+ }
2944
+ if (!Number.isSafeInteger(timestamp)) {
2945
+ throw new Error("Named-session timestamp limit reached.");
2946
+ }
2947
+ return timestamp;
2948
+ }
2949
+ validateName(name) {
2950
+ if (!TASK_NAME_PATTERN.test(name)) {
2951
+ throw new Error("Task names must be 1-32 ASCII letters, numbers, underscores, or hyphens and start with a letter or number.");
2952
+ }
2953
+ }
2954
+ normalizeName(name) {
2955
+ return name.toLowerCase();
2956
+ }
2957
+ sameName(a, b) {
2958
+ return a !== null && this.normalizeName(a) === this.normalizeName(b);
2959
+ }
2960
+ cloneOwner(owner) {
2961
+ return {
2962
+ ...owner,
2963
+ tasks: owner.tasks.map((task) => ({
2964
+ ...task,
2965
+ target: { ...task.target }
2966
+ }))
2967
+ };
2968
+ }
2969
+ cloneTaskReference(reference) {
2970
+ return { ...reference, target: { ...reference.target } };
2971
+ }
2972
+ buildTaskIndex(registry) {
2973
+ const index = /* @__PURE__ */ new Map();
2974
+ for (const owner of registry.owners) {
2975
+ for (const task of owner.tasks) {
2976
+ index.set(task.sessionId, {
2977
+ taskName: task.name,
2978
+ status: task.status,
2979
+ target: { ...task.target }
2980
+ });
2981
+ }
2982
+ }
2983
+ return index;
2984
+ }
2985
+ commitOwner(owner) {
2986
+ const owners = this.registry.owners.map((candidate) => candidate.channelName === owner.channelName && candidate.chatId === owner.chatId && candidate.senderId === owner.senderId ? owner : candidate);
2987
+ if (!owners.some((candidate) => candidate.channelName === owner.channelName && candidate.chatId === owner.chatId && candidate.senderId === owner.senderId)) {
2988
+ owners.push(owner);
2989
+ }
2990
+ const next = { version: REGISTRY_VERSION, owners };
2991
+ if (!this.isRegistry(next)) {
2992
+ throw new Error("Invalid named-session registry update.");
2993
+ }
2994
+ const nextTaskBySessionId = this.buildTaskIndex(next);
2995
+ this.writeRegistry(next);
2996
+ this.registry = next;
2997
+ this.taskBySessionId = nextTaskBySessionId;
2998
+ }
2999
+ readRegistry() {
3000
+ if (!existsSync4(this.filePath)) {
3001
+ return { version: REGISTRY_VERSION, owners: [] };
3002
+ }
3003
+ let value;
3004
+ try {
3005
+ value = JSON.parse(readFileSync4(this.filePath, "utf8"));
3006
+ } catch (error) {
3007
+ throw new Error(`Failed to read named-session registry: ${this.filePath}`, {
3008
+ cause: error
3009
+ });
3010
+ }
3011
+ if (!this.isRegistry(value)) {
3012
+ if (this.isRegistry(value, false)) {
3013
+ const stalePath = `${this.filePath}.stale-${randomUUID2()}`;
3014
+ try {
3015
+ renameSync4(this.filePath, stalePath);
3016
+ } catch (error) {
3017
+ throw new Error("Failed to archive stale named-session registry.", {
3018
+ cause: error
3019
+ });
3020
+ }
3021
+ process3.stderr.write("[NamedSessionManager] Archived a stale registry after the channel working directory changed.\n");
3022
+ return { version: REGISTRY_VERSION, owners: [] };
3023
+ }
3024
+ throw new Error(`Invalid named-session registry: ${this.filePath}`);
3025
+ }
3026
+ return value;
3027
+ }
3028
+ writeRegistry(registry) {
3029
+ const dir = dirname4(this.filePath);
3030
+ const tempPath = `${this.filePath}.${process3.pid}-${randomUUID2()}.tmp`;
3031
+ try {
3032
+ mkdirSync4(dir, { recursive: true, mode: 448 });
3033
+ try {
3034
+ chmodSync3(dir, 448);
3035
+ } catch {
3036
+ }
3037
+ writeFileSync4(tempPath, JSON.stringify(registry, null, 2), {
3038
+ encoding: "utf8",
3039
+ mode: 384
3040
+ });
3041
+ renameSync4(tempPath, this.filePath);
3042
+ try {
3043
+ chmodSync3(this.filePath, 384);
3044
+ } catch {
3045
+ }
3046
+ } catch (error) {
3047
+ throw new Error("Failed to persist named-session registry.", {
3048
+ cause: error
3049
+ });
3050
+ } finally {
3051
+ try {
3052
+ rmSync2(tempPath, { force: true });
3053
+ } catch {
3054
+ }
3055
+ }
3056
+ }
3057
+ isRegistry(value, requireCurrentCwd = true) {
3058
+ if (!this.isRecord(value) || value["version"] !== REGISTRY_VERSION) {
3059
+ return false;
3060
+ }
3061
+ const owners = value["owners"];
3062
+ if (!Array.isArray(owners))
3063
+ return false;
3064
+ const ownerKeys = /* @__PURE__ */ new Set();
3065
+ const sessionIds = /* @__PURE__ */ new Set();
3066
+ for (const owner of owners) {
3067
+ if (!this.isOwner(owner, ownerKeys, sessionIds, requireCurrentCwd)) {
3068
+ return false;
3069
+ }
3070
+ }
3071
+ return true;
3072
+ }
3073
+ isOwner(value, ownerKeys, sessionIds, requireCurrentCwd) {
3074
+ if (!this.isRecord(value))
3075
+ return false;
3076
+ const channelName = value["channelName"];
3077
+ const chatId = value["chatId"];
3078
+ const senderId = value["senderId"];
3079
+ const activeTaskName = value["activeTaskName"];
3080
+ const tasks = value["tasks"];
3081
+ if (channelName !== this.channelName || typeof chatId !== "string" || typeof senderId !== "string" || activeTaskName !== null && typeof activeTaskName !== "string" || !Array.isArray(tasks)) {
3082
+ return false;
3083
+ }
3084
+ const ownerKey = JSON.stringify([channelName, chatId, senderId]);
3085
+ if (ownerKeys.has(ownerKey))
3086
+ return false;
3087
+ ownerKeys.add(ownerKey);
3088
+ const names = /* @__PURE__ */ new Set();
3089
+ let openTasks = 0;
3090
+ let activeFound = activeTaskName === null;
3091
+ for (const task of tasks) {
3092
+ if (!this.isTask(task, channelName, chatId, senderId, requireCurrentCwd)) {
3093
+ return false;
3094
+ }
3095
+ const normalized = this.normalizeName(task.name);
3096
+ if (names.has(normalized) || sessionIds.has(task.sessionId))
3097
+ return false;
3098
+ names.add(normalized);
3099
+ sessionIds.add(task.sessionId);
3100
+ if (task.status === "open")
3101
+ openTasks++;
3102
+ if (activeTaskName !== null && task.status === "open" && this.sameName(activeTaskName, task.name)) {
3103
+ activeFound = true;
3104
+ }
3105
+ }
3106
+ return openTasks <= MAX_OPEN_TASKS && activeFound;
3107
+ }
3108
+ isTask(value, channelName, chatId, senderId, requireCurrentCwd) {
3109
+ if (!this.isRecord(value) || !this.isRecord(value["target"]))
3110
+ return false;
3111
+ const target = value["target"];
3112
+ return typeof value["name"] === "string" && TASK_NAME_PATTERN.test(value["name"]) && typeof value["sessionId"] === "string" && value["sessionId"].length > 0 && typeof value["cwd"] === "string" && (!requireCurrentCwd || this.isCurrentCwd(value["cwd"])) && value["isolation"] === "shared" && (value["status"] === "open" || value["status"] === "closed") && this.isTimestamp(value["createdAt"]) && this.isTimestamp(value["updatedAt"]) && this.isTimestamp(value["lastSelectedAt"]) && target["channelName"] === channelName && target["chatId"] === chatId && target["senderId"] === senderId && (target["threadId"] === void 0 || typeof target["threadId"] === "string") && (target["isGroup"] === void 0 || typeof target["isGroup"] === "boolean");
3113
+ }
3114
+ isCurrentCwd(cwd) {
3115
+ return canonicalizeWorkspacePath(cwd) === this.canonicalCwd;
3116
+ }
3117
+ isTimestamp(value) {
3118
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
3119
+ }
3120
+ isRecord(value) {
3121
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3122
+ }
3123
+ };
3124
+
2299
3125
  // packages/channels/base/dist/ChannelLoopScheduler.js
2300
3126
  init_esbuild_shims();
2301
3127
  var MAX_RESULT_PREVIEW_LENGTH = 500;
@@ -2990,6 +3816,7 @@ var ChannelBase = class {
2990
3816
  groupPairingNotified = /* @__PURE__ */ new Map();
2991
3817
  loopController;
2992
3818
  observedContacts;
3819
+ namedSessions;
2993
3820
  observedContactEnvelopes = /* @__PURE__ */ new WeakSet();
2994
3821
  instructedSessions = /* @__PURE__ */ new Set();
2995
3822
  unattendedMemorySessions = /* @__PURE__ */ new Set();
@@ -2998,6 +3825,9 @@ var ChannelBase = class {
2998
3825
  commands = /* @__PURE__ */ new Map();
2999
3826
  /** Per-session promise chain to serialize prompt + send (followup mode). */
3000
3827
  sessionQueues = /* @__PURE__ */ new Map();
3828
+ queuedTurns = /* @__PURE__ */ new Map();
3829
+ namedTurnBindings = /* @__PURE__ */ new WeakMap();
3830
+ inboundErrorSourceLabels = /* @__PURE__ */ new WeakMap();
3001
3831
  registerBridgeEvents;
3002
3832
  bridgeRecovery;
3003
3833
  /**
@@ -3066,15 +3896,43 @@ var ChannelBase = class {
3066
3896
  this.onToolCall(chatId, event);
3067
3897
  }
3068
3898
  async dispatchBackgroundResponse(sessionId, text) {
3069
- const target = this.router.getTarget(sessionId);
3899
+ let target = this.router.getTarget(sessionId);
3070
3900
  if (!target || target.channelName !== this.name || text.trim().length === 0) {
3071
3901
  return;
3072
3902
  }
3903
+ let sourceLabel;
3904
+ if (this.namedSessions) {
3905
+ const presentation = await this.namedSessions.resolvePresentation(sessionId);
3906
+ const currentTarget = this.router.getTarget(sessionId);
3907
+ const currentPresentation = this.namedSessions.presentation(sessionId);
3908
+ if (!presentation || presentation.status !== "open" || !currentTarget || !this.router.isSessionLive(sessionId) || !currentPresentation || currentPresentation.status !== "open" || currentPresentation.taskName !== presentation.taskName || !this.sameTaskOwner(target, currentTarget) || !this.sameTaskOwner(currentTarget, currentPresentation.target)) {
3909
+ throw new Error("Named background response ownership is unavailable.");
3910
+ }
3911
+ target = currentTarget;
3912
+ sourceLabel = this.createSourceLabel(presentation, target);
3913
+ }
3073
3914
  if (this.supportsProactiveSend() && this.supportsProactiveTarget(target)) {
3074
- await this.pushProactive(target, text);
3915
+ if (sourceLabel) {
3916
+ await this.pushProactive(target, text, sourceLabel);
3917
+ } else {
3918
+ await this.pushProactive(target, text);
3919
+ }
3075
3920
  return;
3076
3921
  }
3077
- await this.sendResponseMessage(target.chatId, text, sessionId);
3922
+ if (sourceLabel) {
3923
+ await this.deliverBackgroundReply(target.chatId, text, sessionId, sourceLabel);
3924
+ } else {
3925
+ await this.deliverBackgroundReply(target.chatId, text, sessionId);
3926
+ }
3927
+ }
3928
+ /**
3929
+ * Fallback delivery of a background response when proactive send is
3930
+ * unavailable. Adapters whose turn replies bypass sendResponseMessage (to
3931
+ * stay out of turn-scoped streaming state, for example) override only this
3932
+ * step instead of re-implementing the whole dispatch flow.
3933
+ */
3934
+ async deliverBackgroundReply(chatId, text, sessionId, sourceLabel) {
3935
+ await this.sendResponseMessage(chatId, text, sessionId, sourceLabel);
3078
3936
  }
3079
3937
  async dispatchPermissionRequest(event) {
3080
3938
  const target = this.permissionTargetForEvent(event);
@@ -3089,12 +3947,59 @@ var ChannelBase = class {
3089
3947
  }
3090
3948
  return;
3091
3949
  }
3950
+ let sourceLabel;
3951
+ let taskName;
3952
+ if (this.namedSessions) {
3953
+ let presentation;
3954
+ try {
3955
+ presentation = await this.namedSessions.resolvePresentation(event.sessionId);
3956
+ } catch (err) {
3957
+ try {
3958
+ await this.bridge.respondToPermission?.(event.requestId, {
3959
+ outcome: { outcome: "cancelled" }
3960
+ });
3961
+ } catch (respondErr) {
3962
+ process.stderr.write(`[${this.name}] permission cancellation failed for request ${sanitizeLogText(event.requestId, 128)}: ${this.lifecycleError(respondErr)}
3963
+ `);
3964
+ }
3965
+ throw err;
3966
+ }
3967
+ const currentTarget = this.permissionTargetForEvent(event);
3968
+ const currentPresentation = this.namedSessions.presentation(event.sessionId);
3969
+ if (!presentation || presentation.status !== "open" || !currentTarget || !this.router.isSessionLive(event.sessionId) || !currentPresentation || currentPresentation.status !== "open" || currentPresentation.taskName !== presentation.taskName || !this.sameTaskOwner(target, currentTarget) || !this.sameTaskOwner(currentTarget, currentPresentation.target)) {
3970
+ try {
3971
+ await this.bridge.respondToPermission?.(event.requestId, {
3972
+ outcome: { outcome: "cancelled" }
3973
+ });
3974
+ } catch (respondErr) {
3975
+ process.stderr.write(`[${this.name}] permission cancellation failed for request ${sanitizeLogText(event.requestId, 128)}: ${this.lifecycleError(respondErr)}
3976
+ `);
3977
+ }
3978
+ return;
3979
+ }
3980
+ const active = this.activePrompts.get(event.sessionId);
3981
+ sourceLabel = active ? active.sourceLabel : this.createSourceLabel(presentation, target);
3982
+ if (!sourceLabel) {
3983
+ try {
3984
+ await this.bridge.respondToPermission?.(event.requestId, {
3985
+ outcome: { outcome: "cancelled" }
3986
+ });
3987
+ } catch (respondErr) {
3988
+ process.stderr.write(`[${this.name}] permission cancellation failed for request ${sanitizeLogText(event.requestId, 128)}: ${this.lifecycleError(respondErr)}
3989
+ `);
3990
+ }
3991
+ return;
3992
+ }
3993
+ taskName = presentation.taskName;
3994
+ }
3092
3995
  this.removePendingPermission(event.requestId);
3093
3996
  const pending = {
3094
3997
  requestId: event.requestId,
3095
3998
  sessionId: event.sessionId,
3096
3999
  target,
3097
4000
  request: event.request,
4001
+ ...sourceLabel ? { sourceLabel } : {},
4002
+ ...taskName ? { taskName } : {},
3098
4003
  settlementListeners: /* @__PURE__ */ new Set()
3099
4004
  };
3100
4005
  this.pendingPermissions.set(event.requestId, pending);
@@ -3109,9 +4014,9 @@ var ChannelBase = class {
3109
4014
  }
3110
4015
  const text = this.formatPermissionRequest(pending);
3111
4016
  if (target.threadId !== void 0 && this.supportsProactiveSend() && this.supportsProactiveTarget(target)) {
3112
- await this.pushProactive(target, text);
4017
+ await this.pushProactive(target, text, pending.sourceLabel);
3113
4018
  } else {
3114
- await this.sendThreadMessage(target.chatId, target.threadId, text);
4019
+ await this.sendThreadMessage(target.chatId, target.threadId, text, pending.sourceLabel);
3115
4020
  }
3116
4021
  } catch (err) {
3117
4022
  this.removePendingPermission(event.requestId, "cancelled");
@@ -3141,6 +4046,7 @@ var ChannelBase = class {
3141
4046
  runId: active.runId,
3142
4047
  owner: active.owner,
3143
4048
  target: pending.target,
4049
+ ...pending.sourceLabel ? { sourceLabel: pending.sourceLabel } : {},
3144
4050
  ...precedingSegment ? { precedingSegmentId: precedingSegment.segmentId } : {},
3145
4051
  questions,
3146
4052
  submitOptionId,
@@ -3295,6 +4201,21 @@ var ChannelBase = class {
3295
4201
  this.dmGate = new DmGate(config.dmPolicy);
3296
4202
  this.gate = new SenderGate(config.senderPolicy, config.allowedUsers, pairingStore);
3297
4203
  this.router = options?.router || new SessionRouter(bridge, config.cwd, config.sessionScope);
4204
+ if (config.multiSession) {
4205
+ if (config.sessionScope !== "user") {
4206
+ throw new Error(`Channel "${name}" requires sessionScope "user" when multiSession is enabled.`);
4207
+ }
4208
+ if (!options?.stateDir) {
4209
+ throw new Error(`Channel "${name}" multiSession is available only in daemon-managed mode.`);
4210
+ }
4211
+ this.namedSessions = new NamedSessionManager({
4212
+ channelName: name,
4213
+ cwd: config.cwd,
4214
+ filePath: join5(options.stateDir, "named-sessions.json"),
4215
+ router: this.router,
4216
+ isBusy: /* @__PURE__ */ __name((sessionId) => this.isNamedSessionBusy(sessionId), "isBusy")
4217
+ });
4218
+ }
3298
4219
  this.registerSharedCommands();
3299
4220
  if (this.loopController) {
3300
4221
  bridge.registerChannelLoopToolHandler?.(this.channelLoopToolHandler);
@@ -3309,8 +4230,8 @@ var ChannelBase = class {
3309
4230
  * on a specific issue/PR. The default falls through to sendMessage(chatId,
3310
4231
  * text), ignoring threadId — existing IM adapters are behaviorally unchanged.
3311
4232
  */
3312
- async sendThreadMessage(chatId, _threadId, text) {
3313
- await this.sendMessage(chatId, text);
4233
+ async sendThreadMessage(chatId, _threadId, text, sourceLabel) {
4234
+ await this.sendMessage(chatId, this.formatAttributedText(text, sourceLabel));
3314
4235
  }
3315
4236
  /**
3316
4237
  * Adapter hook for task lifecycle events — the canonical way to track task
@@ -3452,13 +4373,14 @@ var ChannelBase = class {
3452
4373
  segmentId,
3453
4374
  owner: active.owner,
3454
4375
  target: resolvedTarget,
4376
+ ...active.sourceLabel ? { sourceLabel: active.sourceLabel } : {},
3455
4377
  ...active.messageId ? { messageId: active.messageId } : {}
3456
4378
  };
3457
4379
  }
3458
4380
  ensureOutputSegment(sessionId, active) {
3459
4381
  if (!active.owner)
3460
4382
  return void 0;
3461
- const segmentId = active.activeSegmentId ?? randomUUID2();
4383
+ const segmentId = active.activeSegmentId ?? randomUUID3();
3462
4384
  const context = this.outputSegmentContext(sessionId, active, segmentId);
3463
4385
  if (context)
3464
4386
  active.activeSegmentId = segmentId;
@@ -3493,11 +4415,11 @@ var ChannelBase = class {
3493
4415
  supportsProactiveWebhookTarget(target) {
3494
4416
  return this.supportsProactiveTarget(target);
3495
4417
  }
3496
- async pushProactive(target, text) {
4418
+ async pushProactive(target, text, sourceLabel) {
3497
4419
  if (target.threadId) {
3498
4420
  throw new Error("Channel does not support proactive loop messages for threaded targets.");
3499
4421
  }
3500
- await this.sendThreadMessage(target.chatId, target.threadId, text);
4422
+ await this.sendThreadMessage(target.chatId, target.threadId, text, sourceLabel);
3501
4423
  }
3502
4424
  async pushProactiveDelivery(target, text) {
3503
4425
  try {
@@ -3687,8 +4609,11 @@ var ChannelBase = class {
3687
4609
  imageBase64: void 0,
3688
4610
  imageMimeType: void 0
3689
4611
  };
4612
+ if (this.namedSessions) {
4613
+ this.bindNamedTurn(syntheticEnvelope, sessionId);
4614
+ }
3690
4615
  this.markPreflighted(syntheticEnvelope);
3691
- this.processInbound(syntheticEnvelope).catch((err) => {
4616
+ void this.processPreflightedInbound(syntheticEnvelope).catch((err) => {
3692
4617
  process.stderr.write(`[${this.name}] dropped ${lost} buffered message(s) after ${taskLabel} for session ${sessionId} (last sender ${lastEnvelope.senderId}): ${err instanceof Error ? err.message : String(err)}
3693
4618
  `);
3694
4619
  });
@@ -3784,7 +4709,7 @@ ${promptText}` : promptText;
3784
4709
  doneResolve = resolve3;
3785
4710
  });
3786
4711
  const promptState = {
3787
- runId: randomUUID2(),
4712
+ runId: randomUUID3(),
3788
4713
  cancelled: false,
3789
4714
  done,
3790
4715
  resolve: doneResolve,
@@ -4000,7 +4925,7 @@ ${promptText}` : promptText;
4000
4925
  doneResolve = resolve3;
4001
4926
  });
4002
4927
  const promptState = {
4003
- runId: randomUUID2(),
4928
+ runId: randomUUID3(),
4004
4929
  cancelled: false,
4005
4930
  done,
4006
4931
  resolve: doneResolve,
@@ -4333,11 +5258,11 @@ ${promptText}` : promptText;
4333
5258
  */
4334
5259
  onResponseBoundary(_chatId, _sessionId) {
4335
5260
  }
4336
- async sendResponseMessage(chatId, text, sessionId) {
5261
+ async sendResponseMessage(chatId, text, sessionId, sourceLabel) {
4337
5262
  const active = this.activePrompts.get(sessionId);
4338
5263
  const target = this.router.getTarget(sessionId);
4339
5264
  const threadId = active?.threadId ?? target?.threadId;
4340
- await this.sendThreadMessage(chatId, threadId, text);
5265
+ await this.sendThreadMessage(chatId, threadId, text, sourceLabel ?? active?.sourceLabel);
4341
5266
  }
4342
5267
  /**
4343
5268
  * Adapter hook for delivery-only metadata. The response delivery path can read
@@ -4352,6 +5277,12 @@ ${promptText}` : promptText;
4352
5277
  getResponseMetadata(sessionId) {
4353
5278
  return this.activePrompts.get(sessionId)?.metadata;
4354
5279
  }
5280
+ getResponseSourceLabel(sessionId) {
5281
+ return this.activePrompts.get(sessionId)?.sourceLabel;
5282
+ }
5283
+ getInboundErrorSourceLabel(envelope) {
5284
+ return this.inboundErrorSourceLabels.get(envelope);
5285
+ }
4355
5286
  /**
4356
5287
  * Returns the active prompt's response thread while it remains available for
4357
5288
  * adapter delivery. Falls back to the session target after prompt cleanup.
@@ -4364,8 +5295,8 @@ ${promptText}` : promptText;
4364
5295
  * Override to customize delivery (e.g., finalize an AI card).
4365
5296
  * Default: sends the full response text.
4366
5297
  */
4367
- async onResponseComplete(chatId, fullText, sessionId, _segment) {
4368
- await this.sendResponseMessage(chatId, fullText, sessionId);
5298
+ async onResponseComplete(chatId, fullText, sessionId, segment) {
5299
+ await this.sendResponseMessage(chatId, fullText, sessionId, segment?.sourceLabel);
4369
5300
  }
4370
5301
  /**
4371
5302
  * Register a slash command handler. Subclasses can call this to add
@@ -4381,7 +5312,7 @@ ${promptText}` : promptText;
4381
5312
  await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can cancel requests in this shared session.");
4382
5313
  return true;
4383
5314
  }
4384
- const activeSessionId = this.findActiveSessionId(envelope);
5315
+ const activeSessionId = this.namedSessions ? await this.findNamedActiveSessionId(envelope) : this.findActiveSessionId(envelope);
4385
5316
  if (!activeSessionId) {
4386
5317
  await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No request is currently running.");
4387
5318
  return true;
@@ -4470,7 +5401,7 @@ ${promptText}` : promptText;
4470
5401
  }
4471
5402
  return "resolved_outside_presenter";
4472
5403
  }
4473
- pendingPermissionForEnvelope(envelope, args) {
5404
+ pendingPermissionForEnvelope(envelope, args, selectedSessionId) {
4474
5405
  const trimmed = args.trim();
4475
5406
  if (trimmed) {
4476
5407
  const explicit = this.pendingPermissions.get(trimmed);
@@ -4483,7 +5414,7 @@ ${promptText}` : promptText;
4483
5414
  if (requestIds.length === 0) {
4484
5415
  return { kind: "none", explicit: false };
4485
5416
  }
4486
- const matching = requestIds.map((id) => this.pendingPermissions.get(id)).filter((pending) => pending !== void 0 && this.canEnvelopeAnswerPendingPermission(envelope, pending));
5417
+ const matching = requestIds.map((id) => this.pendingPermissions.get(id)).filter((pending) => pending !== void 0 && (selectedSessionId === void 0 || pending.sessionId === selectedSessionId) && this.canEnvelopeAnswerPendingPermission(envelope, pending));
4487
5418
  if (matching.length === 0) {
4488
5419
  return { kind: "none", explicit: false };
4489
5420
  }
@@ -4502,6 +5433,23 @@ ${promptText}` : promptText;
4502
5433
  const { toolCall } = pending.request;
4503
5434
  const title = sanitizeQuotedText(toolCall.title || "Tool use", 160);
4504
5435
  const alwaysOption = this.approvalAlwaysOption(pending);
5436
+ if (pending.taskName) {
5437
+ const replies2 = [
5438
+ `/approve ${pending.requestId} allow once`,
5439
+ ...alwaysOption ? [`/approve-always ${pending.requestId} ${alwaysOption.label}`] : [],
5440
+ `/deny ${pending.requestId} deny`
5441
+ ];
5442
+ return [
5443
+ "Permission required to run a tool",
5444
+ `Request: ${pending.requestId}`,
5445
+ "",
5446
+ "Command:",
5447
+ title,
5448
+ "",
5449
+ "Reply with:",
5450
+ ...replies2
5451
+ ].join("\n");
5452
+ }
4505
5453
  const replies = [
4506
5454
  "/approve allow once",
4507
5455
  ...alwaysOption ? [`/approve-always ${alwaysOption.label}`] : [],
@@ -4557,28 +5505,40 @@ ${promptText}` : promptText;
4557
5505
  await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can answer permission requests in this shared session.");
4558
5506
  return true;
4559
5507
  }
4560
- const lookup = this.pendingPermissionForEnvelope(envelope, args);
5508
+ const namedSessions = this.namedSessions;
5509
+ const bareNamedCommand = namedSessions !== void 0 && args.trim() === "";
5510
+ let selectedTask;
5511
+ if (bareNamedCommand) {
5512
+ try {
5513
+ selectedTask = await namedSessions.current(this.namedSessionOwner(envelope));
5514
+ } catch (error) {
5515
+ await this.sendNamedSessionError(envelope, error);
5516
+ return true;
5517
+ }
5518
+ }
5519
+ const lookup = this.pendingPermissionForEnvelope(envelope, args, bareNamedCommand ? selectedTask?.sessionId ?? null : void 0);
4561
5520
  if (lookup.kind === "ambiguous") {
4562
5521
  const requestList = lookup.requestIds.slice(0, 6).map((id) => {
4563
5522
  const pending2 = this.pendingPermissions.get(id);
4564
5523
  const title = pending2 ? `: ${sanitizeQuotedText(pending2.request.toolCall.title || "Tool use", 160)}` : "";
4565
- return `- ${sanitizeQuotedText(id, 128)}${title}`;
5524
+ const task = pending2?.taskName ? `Task ${pending2.taskName} \u2014 ` : "";
5525
+ return `- ${task}${sanitizeQuotedText(id, 128)}${title}`;
4566
5526
  }).join("\n");
4567
5527
  await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Multiple permission requests are pending for this chat. Reply with /${decision} <request-id>.
4568
5528
  ${requestList}`);
4569
5529
  return true;
4570
5530
  }
4571
5531
  if (lookup.kind === "none") {
4572
- await this.sendThreadMessage(envelope.chatId, envelope.threadId, lookup.explicit ? "No pending permission request with that id for this chat." : "No pending permission request for this chat.");
5532
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, lookup.explicit ? "No pending permission request with that id for this chat." : selectedTask ? `No pending permission request for selected task "${selectedTask.name}". Use an explicit request ID to answer another task.` : this.namedSessions ? "No task is currently selected. Use an explicit request ID to answer a named task." : "No pending permission request for this chat.");
4573
5533
  return true;
4574
5534
  }
5535
+ const { pending } = lookup;
4575
5536
  if (!this.bridge.respondToPermission) {
4576
- await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Permission relay is not available for this session.");
5537
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Permission relay is not available for this session.", pending.sourceLabel);
4577
5538
  return true;
4578
5539
  }
4579
- const { pending } = lookup;
4580
5540
  if (pending.userInputPresented && decision !== "deny") {
4581
- await this.sendMessage(envelope.chatId, "Submit this question through its interactive card, or use /deny [request-id] to cancel it.");
5541
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Submit this question through its interactive card, or use /deny [request-id] to cancel it.", pending.sourceLabel);
4582
5542
  return true;
4583
5543
  }
4584
5544
  const response = (() => {
@@ -4589,7 +5549,7 @@ ${requestList}`);
4589
5549
  return optionId ? { outcome: { outcome: "selected", optionId } } : void 0;
4590
5550
  })();
4591
5551
  if (!response) {
4592
- await this.sendThreadMessage(envelope.chatId, envelope.threadId, decision === "approve-always" ? "This permission request has no always-allow option." : "This permission request has no approvable option.");
5552
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, decision === "approve-always" ? "This permission request has no always-allow option." : "This permission request has no approvable option.", pending.sourceLabel);
4593
5553
  return true;
4594
5554
  }
4595
5555
  let accepted;
@@ -4599,17 +5559,228 @@ ${requestList}`);
4599
5559
  this.removePendingPermission(pending.requestId);
4600
5560
  process.stderr.write(`[${this.name}] permission response failed for request ${sanitizeLogText(pending.requestId, 128)}: ${this.lifecycleError(err)}
4601
5561
  `);
4602
- await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Failed to answer the permission request.");
5562
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Failed to answer the permission request.", pending.sourceLabel);
4603
5563
  return true;
4604
5564
  }
4605
5565
  this.removePendingPermission(pending.requestId);
4606
- await this.sendThreadMessage(envelope.chatId, envelope.threadId, accepted ? decision === "approve" ? "Permission approved." : decision === "approve-always" ? "Permission approved always." : "Permission denied." : "Permission request is no longer pending.");
5566
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, accepted ? decision === "approve" ? "Permission approved." : decision === "approve-always" ? "Permission approved always." : "Permission denied." : "Permission request is no longer pending.", pending.sourceLabel);
5567
+ return true;
5568
+ }
5569
+ namedSessionOwner(envelope) {
5570
+ return {
5571
+ senderId: envelope.senderId,
5572
+ chatId: envelope.chatId,
5573
+ ...envelope.threadId !== void 0 ? { threadId: envelope.threadId } : {},
5574
+ ...envelope.isGroup !== void 0 ? { isGroup: envelope.isGroup } : {}
5575
+ };
5576
+ }
5577
+ async currentSessionId(envelope) {
5578
+ if (this.namedSessions) {
5579
+ return (await this.namedSessions.current(this.namedSessionOwner(envelope)))?.sessionId;
5580
+ }
5581
+ return this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
5582
+ }
5583
+ reserveQueuedTurn(sessionId) {
5584
+ this.queuedTurns.set(sessionId, (this.queuedTurns.get(sessionId) ?? 0) + 1);
5585
+ }
5586
+ releaseQueuedTurn(sessionId) {
5587
+ const remaining = (this.queuedTurns.get(sessionId) ?? 1) - 1;
5588
+ if (remaining === 0) {
5589
+ this.queuedTurns.delete(sessionId);
5590
+ } else {
5591
+ this.queuedTurns.set(sessionId, remaining);
5592
+ }
5593
+ }
5594
+ bindNamedTurn(envelope, sessionId) {
5595
+ const binding = {
5596
+ sessionId,
5597
+ generation: this.sessionGenerations.get(sessionId) ?? 0,
5598
+ claimed: false,
5599
+ released: false
5600
+ };
5601
+ this.namedTurnBindings.set(envelope, binding);
5602
+ this.reserveQueuedTurn(sessionId);
5603
+ return binding;
5604
+ }
5605
+ releaseNamedTurnBinding(binding) {
5606
+ if (binding.sessionId === null || binding.claimed || binding.released) {
5607
+ return;
5608
+ }
5609
+ binding.released = true;
5610
+ this.releaseQueuedTurn(binding.sessionId);
5611
+ }
5612
+ finishNamedTurnBinding(envelope) {
5613
+ const binding = this.namedTurnBindings.get(envelope);
5614
+ if (!binding)
5615
+ return;
5616
+ this.releaseNamedTurnBinding(binding);
5617
+ this.namedTurnBindings.delete(envelope);
5618
+ }
5619
+ bypassesNamedTurnBinding(envelope) {
5620
+ const parsed = this.parseCommand(envelope.text);
5621
+ if (parsed && this.commands.has(parsed.command))
5622
+ return true;
5623
+ const bangText = envelope.text.trimStart();
5624
+ return bangText.startsWith("!") && (envelope.isGroup || this.isSharedSession(envelope));
5625
+ }
5626
+ async prepareNamedTurnBinding(envelope) {
5627
+ const namedSessions = this.namedSessions;
5628
+ if (!namedSessions || this.namedTurnBindings.has(envelope))
5629
+ return true;
5630
+ if (this.bypassesNamedTurnBinding(envelope))
5631
+ return true;
5632
+ try {
5633
+ const sessionId = await namedSessions.resolve(this.namedSessionOwner(envelope), (resolvedSessionId) => {
5634
+ const binding = this.bindNamedTurn(envelope, resolvedSessionId);
5635
+ return () => this.releaseNamedTurnBinding(binding);
5636
+ });
5637
+ if (!sessionId && !this.namedTurnBindings.has(envelope)) {
5638
+ this.namedTurnBindings.set(envelope, {
5639
+ sessionId: null,
5640
+ generation: 0,
5641
+ claimed: false,
5642
+ released: true
5643
+ });
5644
+ }
5645
+ return true;
5646
+ } catch (error) {
5647
+ this.finishNamedTurnBinding(envelope);
5648
+ await this.sendNamedSessionError(envelope, error);
5649
+ return false;
5650
+ }
5651
+ }
5652
+ isNamedSessionBusy(sessionId) {
5653
+ if ((this.queuedTurns.get(sessionId) ?? 0) > 0)
5654
+ return true;
5655
+ if (this.activePrompts.has(sessionId))
5656
+ return true;
5657
+ for (const permission of this.pendingPermissions.values()) {
5658
+ if (permission.sessionId === sessionId)
5659
+ return true;
5660
+ }
5661
+ try {
5662
+ return this.bridge.listSessions?.().some((session) => session.sessionId === sessionId && session.hasActivePrompt) ?? false;
5663
+ } catch {
5664
+ return true;
5665
+ }
5666
+ }
5667
+ async sendNamedSessionError(envelope, error) {
5668
+ const message = error instanceof Error ? sanitizeDisplayText(error.message, 500) : "Named-session operation failed.";
5669
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, message || "Named-session operation failed.");
5670
+ }
5671
+ async handleNamedSessionsCommand(envelope, args) {
5672
+ const namedSessions = this.namedSessions;
5673
+ if (!namedSessions)
5674
+ return false;
5675
+ const normalized = args.trim().toLowerCase();
5676
+ if (normalized !== "" && normalized !== "all") {
5677
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Usage: /sessions [all]");
5678
+ return true;
5679
+ }
5680
+ try {
5681
+ const tasks = await namedSessions.list(this.namedSessionOwner(envelope), normalized === "all");
5682
+ if (tasks.length === 0) {
5683
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, normalized === "all" ? "No named tasks." : "No open named tasks.");
5684
+ return true;
5685
+ }
5686
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, [
5687
+ normalized === "all" ? "Tasks:" : "Open tasks:",
5688
+ ...tasks.map((task) => `${task.active ? "*" : "-"} ${task.name} (${task.status}, ${task.isolation})`)
5689
+ ].join("\n"));
5690
+ } catch (error) {
5691
+ await this.sendNamedSessionError(envelope, error);
5692
+ }
5693
+ return true;
5694
+ }
5695
+ async handleNamedSessionCommand(envelope, args) {
5696
+ const namedSessions = this.namedSessions;
5697
+ if (!namedSessions)
5698
+ return false;
5699
+ const parts = args.trim().split(/\s+/u).filter(Boolean);
5700
+ const subcommand = parts.shift()?.toLowerCase() ?? "";
5701
+ const owner = this.namedSessionOwner(envelope);
5702
+ try {
5703
+ switch (subcommand) {
5704
+ case "current": {
5705
+ if (parts.length > 0)
5706
+ break;
5707
+ const current = await namedSessions.current(owner);
5708
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, current ? `Current task: ${current.name} (${current.isolation})` : "No task is currently selected. Use /session new <name> or /session use <name>.");
5709
+ return true;
5710
+ }
5711
+ case "new": {
5712
+ if (parts.includes("--worktree")) {
5713
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Worktree tasks are planned for Part 4. Create a shared task with /session new <name>.");
5714
+ return true;
5715
+ }
5716
+ if (parts.length !== 1)
5717
+ break;
5718
+ const created = await namedSessions.create(owner, parts[0]);
5719
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Created and selected task "${created.name}" (shared workspace).`);
5720
+ return true;
5721
+ }
5722
+ case "use": {
5723
+ if (parts.length !== 1)
5724
+ break;
5725
+ const selected = await namedSessions.use(owner, parts[0]);
5726
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Selected task "${selected.name}" (${selected.isolation} workspace).`);
5727
+ return true;
5728
+ }
5729
+ case "close": {
5730
+ if (parts.length !== 1)
5731
+ break;
5732
+ const result = await namedSessions.close(owner, parts[0]);
5733
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, result.active ? `Closed task "${result.closed.name}". Selected "${result.active.name}".` : `Closed task "${result.closed.name}". No task is selected.`);
5734
+ return true;
5735
+ }
5736
+ case "cancel": {
5737
+ if (parts.length > 1)
5738
+ break;
5739
+ const taskName = parts[0];
5740
+ const task = taskName ? await namedSessions.lookup(owner, taskName) : await namedSessions.current(owner);
5741
+ if (!task) {
5742
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, taskName ? `Task "${sanitizeQuotedText(taskName, 32)}" was not found.` : "No task is currently selected.");
5743
+ return true;
5744
+ }
5745
+ if (task.status !== "open") {
5746
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Task "${task.name}" is closed.`);
5747
+ return true;
5748
+ }
5749
+ if (!this.activePrompts.has(task.sessionId)) {
5750
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, `No request is currently running for task "${task.name}".`);
5751
+ return true;
5752
+ }
5753
+ const cancelled = await this.requestActivePromptCancellation(task.sessionId, "cancel_command");
5754
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, cancelled ? `Cancelled task "${task.name}".` : `Failed to cancel task "${task.name}".`);
5755
+ return true;
5756
+ }
5757
+ default:
5758
+ break;
5759
+ }
5760
+ } catch (error) {
5761
+ await this.sendNamedSessionError(envelope, error);
5762
+ return true;
5763
+ }
5764
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Usage: /session current | /session new <name> | /session use <name> | /session close <name> | /session cancel [<name>]");
4607
5765
  return true;
4608
5766
  }
4609
5767
  /** Register shared slash commands. Called from constructor. */
4610
5768
  registerSharedCommands() {
4611
5769
  const doClear = /* @__PURE__ */ __name(async (envelope) => {
4612
- const removedIds = this.router.removeSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
5770
+ let resetTaskName;
5771
+ let removedIds;
5772
+ if (this.namedSessions) {
5773
+ try {
5774
+ const reset = await this.namedSessions.reset(this.namedSessionOwner(envelope));
5775
+ resetTaskName = reset?.name;
5776
+ removedIds = reset ? [reset.previousSessionId] : [];
5777
+ } catch (error) {
5778
+ await this.sendNamedSessionError(envelope, error);
5779
+ return;
5780
+ }
5781
+ } else {
5782
+ removedIds = this.router.removeSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
5783
+ }
4613
5784
  this.clearPendingGroupHistory(envelope);
4614
5785
  if (removedIds.length > 0) {
4615
5786
  for (const id of removedIds) {
@@ -4655,7 +5826,7 @@ ${requestList}`);
4655
5826
  }
4656
5827
  this.discardRetiredSession(this.bridge, id, "cleared");
4657
5828
  }
4658
- await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Session cleared. The next message starts a fresh conversation.");
5829
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, resetTaskName ? `Task "${resetTaskName}" reset with a fresh conversation.` : "Session cleared. The next message starts a fresh conversation.");
4659
5830
  } else {
4660
5831
  await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No active session to clear.");
4661
5832
  }
@@ -4675,6 +5846,10 @@ ${requestList}`);
4675
5846
  this.registerCommand("clear", clearHandler);
4676
5847
  this.registerCommand("reset", clearHandler);
4677
5848
  this.registerCommand("new", clearHandler);
5849
+ if (this.namedSessions) {
5850
+ this.registerCommand("sessions", (envelope, args) => this.handleNamedSessionsCommand(envelope, args));
5851
+ this.registerCommand("session", (envelope, args) => this.handleNamedSessionCommand(envelope, args));
5852
+ }
4678
5853
  this.registerCommand("approve", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "approve"));
4679
5854
  this.registerCommand("approve-always", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "approve-always"));
4680
5855
  this.registerCommand("deny", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "deny"));
@@ -4683,7 +5858,7 @@ ${requestList}`);
4683
5858
  await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can view this shared session.");
4684
5859
  return true;
4685
5860
  }
4686
- const active = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
5861
+ const active = this.namedSessions ? Boolean(await this.currentSessionId(envelope)) : this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
4687
5862
  const scopeNote = this.config.sessionScope === "single" ? " (shared channel-wide)" : this.isSharedSession(envelope) ? envelope.isGroup ? " (shared by this group)" : "" : envelope.isGroup ? " (private to you)" : "";
4688
5863
  await this.sendThreadMessage(envelope.chatId, envelope.threadId, [
4689
5864
  `Channel: ${this.name}`,
@@ -4708,7 +5883,11 @@ ${requestList}`);
4708
5883
  "/status \u2014 Show session info",
4709
5884
  "/approve [request-id] \u2014 Approve a pending permission request",
4710
5885
  "/approve-always [request-id] \u2014 Always approve a pending permission request",
4711
- "/deny [request-id] \u2014 Deny a pending permission request"
5886
+ "/deny [request-id] \u2014 Deny a pending permission request",
5887
+ ...this.namedSessions ? [
5888
+ "/sessions [all] \u2014 List your named tasks",
5889
+ "/session current|new|use|close|cancel \u2014 Manage your named tasks"
5890
+ ] : []
4712
5891
  ];
4713
5892
  const sharedCmds = /* @__PURE__ */ new Set([
4714
5893
  "help",
@@ -4722,7 +5901,9 @@ ${requestList}`);
4722
5901
  "channel-memory",
4723
5902
  "forget-channel",
4724
5903
  "who",
4725
- "status"
5904
+ "status",
5905
+ "sessions",
5906
+ "session"
4726
5907
  ]);
4727
5908
  const platformCmds = [...this.commands.keys()].filter((c) => !sharedCmds.has(c));
4728
5909
  if (platformCmds.length > 0) {
@@ -4730,7 +5911,7 @@ ${requestList}`);
4730
5911
  lines.push(`/${cmd}`);
4731
5912
  }
4732
5913
  }
4733
- const sessionId = this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
5914
+ const sessionId = await this.currentSessionId(envelope);
4734
5915
  const agentCommands = sessionId ? this.getAgentCommandsForSession(sessionId) : this.bridge.availableCommands;
4735
5916
  if (agentCommands.length > 0) {
4736
5917
  lines.push("", "Agent commands (forwarded to Qwen Code):");
@@ -4747,7 +5928,7 @@ ${requestList}`);
4747
5928
  await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can view this shared session.");
4748
5929
  return true;
4749
5930
  }
4750
- const hasSession = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
5931
+ const hasSession = this.namedSessions ? Boolean(await this.currentSessionId(envelope)) : this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
4751
5932
  const policy = this.config.senderPolicy;
4752
5933
  const lines = [
4753
5934
  `Session: ${hasSession ? "active" : "none"}`,
@@ -5078,6 +6259,10 @@ ${requestList}`);
5078
6259
  const sessionId = this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
5079
6260
  return sessionId && this.activePrompts.has(sessionId) ? sessionId : void 0;
5080
6261
  }
6262
+ async findNamedActiveSessionId(envelope) {
6263
+ const sessionId = await this.currentSessionId(envelope);
6264
+ return sessionId && this.activePrompts.has(sessionId) ? sessionId : void 0;
6265
+ }
5081
6266
  channelMemoryTarget(envelope) {
5082
6267
  return {
5083
6268
  channelName: this.name,
@@ -5805,9 +6990,12 @@ ${formatted.join("\n")}
5805
6990
  ${CURRENT_MESSAGE_MARKER}
5806
6991
  ${promptText}`;
5807
6992
  }
5808
- preflightInbound(envelope) {
5809
- const groupResult = this.groupGate.check(envelope);
5810
- if (!groupResult.allowed) {
6993
+ preflightInbound(envelope, options = {}) {
6994
+ const groupResult = this.groupGate.check(envelope, {
6995
+ createPairingRequest: !options.deferPairingRequests
6996
+ });
6997
+ const deferredGroupPairing = options.deferPairingRequests === true && groupResult.reason === "pairing_trigger_required" && (envelope.isMentioned || envelope.isReplyToBot);
6998
+ if (!groupResult.allowed && !deferredGroupPairing) {
5811
6999
  if (groupResult.pairing !== void 0) {
5812
7000
  this.logPreflightRejected("group_pairing_required");
5813
7001
  return this.onGroupPairingRequired(envelope.chatId, groupResult.pairing, envelope.threadId).then(() => false).catch((err) => {
@@ -5834,6 +7022,10 @@ ${promptText}`;
5834
7022
  this.markPreflighted(envelope);
5835
7023
  return true;
5836
7024
  }
7025
+ if (options.deferPairingRequests === true && this.config.senderPolicy === "pairing" && !this.gate.isAllowed(envelope.senderId)) {
7026
+ this.markPreflighted(envelope);
7027
+ return true;
7028
+ }
5837
7029
  const result = this.gate.check(envelope.senderId, envelope.senderName);
5838
7030
  if (!result.allowed) {
5839
7031
  if (result.pairing !== void 0) {
@@ -5870,7 +7062,50 @@ ${promptText}`;
5870
7062
  const preflight = this.preflightInbound(envelope);
5871
7063
  if (!(isPromiseLike(preflight) ? await preflight : preflight))
5872
7064
  return;
5873
- await this.processInbound(envelope);
7065
+ await this.processPreflightedInbound(envelope);
7066
+ }
7067
+ async prepareThenHandleInbound(envelope, prepare, preflightOptions = {}) {
7068
+ const preflight = this.preflightInbound(envelope, preflightOptions);
7069
+ if (!(isPromiseLike(preflight) ? await preflight : preflight))
7070
+ return;
7071
+ if (this.namedSessions) {
7072
+ const result = await this.namedSessions.resolveAfterPreparation(this.namedSessionOwner(envelope), prepare, () => !this.bypassesNamedTurnBinding(envelope), (sessionId) => {
7073
+ const binding = this.bindNamedTurn(envelope, sessionId);
7074
+ return () => this.releaseNamedTurnBinding(binding);
7075
+ });
7076
+ if (result.status === "aborted")
7077
+ return;
7078
+ if (result.status === "resolve_error") {
7079
+ this.finishNamedTurnBinding(envelope);
7080
+ await this.sendNamedSessionError(envelope, result.error);
7081
+ return;
7082
+ }
7083
+ if (result.status === "resolved" && !result.sessionId && !this.namedTurnBindings.has(envelope)) {
7084
+ this.namedTurnBindings.set(envelope, {
7085
+ sessionId: null,
7086
+ generation: 0,
7087
+ claimed: false,
7088
+ released: true
7089
+ });
7090
+ }
7091
+ } else if (await prepare() === false) {
7092
+ return;
7093
+ }
7094
+ try {
7095
+ await this.handleInbound(envelope);
7096
+ } finally {
7097
+ this.finishNamedTurnBinding(envelope);
7098
+ }
7099
+ }
7100
+ async processPreflightedInbound(envelope, process5 = () => this.processInbound(envelope)) {
7101
+ if (this.namedSessions && !await this.prepareNamedTurnBinding(envelope)) {
7102
+ return;
7103
+ }
7104
+ try {
7105
+ await process5();
7106
+ } finally {
7107
+ this.finishNamedTurnBinding(envelope);
7108
+ }
5874
7109
  }
5875
7110
  async recordObservedContact(envelope) {
5876
7111
  if (!this.observedContacts)
@@ -5919,6 +7154,57 @@ ${promptText}`;
5919
7154
  return void 0;
5920
7155
  }
5921
7156
  }
7157
+ formatAttributedText(text, sourceLabel) {
7158
+ if (!sourceLabel || text.trim().length === 0)
7159
+ return text;
7160
+ return `${sourceLabel} ${text}`;
7161
+ }
7162
+ formatMarkdownAttributedText(text, sourceLabel) {
7163
+ const escapedLabel = sourceLabel?.replace(/([\\`*_[\]{}()#+\-.!|>~])/gu, "\\$1");
7164
+ if (!escapedLabel || text.trim().length === 0)
7165
+ return text;
7166
+ return `${escapedLabel}
7167
+ ${text}`;
7168
+ }
7169
+ sameTaskOwner(a, b) {
7170
+ return a.channelName === b.channelName && a.chatId === b.chatId && a.senderId === b.senderId;
7171
+ }
7172
+ createSourceLabel(reference, target, active) {
7173
+ const activeName = active?.senderName ? this.sanitizeSourceSender(active.senderName) : void 0;
7174
+ const senderLabel = (activeName && activeName !== "unknown" ? activeName : void 0) ?? this.observedSenderLabel(target.chatId, target.senderId) ?? this.sanitizeSourceSender(target.senderId || "unknown");
7175
+ const isGroup = active?.isGroup ?? target.isGroup ?? true;
7176
+ return isGroup ? `[${senderLabel} \xB7 ${reference.taskName}]` : `[${reference.taskName}]`;
7177
+ }
7178
+ observedSenderLabel(chatId, senderId) {
7179
+ const graph = this.persistedObservedContacts();
7180
+ if (!graph)
7181
+ return void 0;
7182
+ const sources = [
7183
+ graph.groups.filter((group) => group.id === chatId).flatMap((group) => group.users).filter((user) => user.id === senderId),
7184
+ graph.users.filter((user) => user.id === senderId)
7185
+ ];
7186
+ for (const source of sources) {
7187
+ const candidates = source.sort((a, b) => b.lastObservedAt.localeCompare(a.lastObservedAt));
7188
+ for (const candidate of candidates) {
7189
+ const label = this.sanitizeSourceSender(candidate.label);
7190
+ if (label !== "unknown")
7191
+ return label;
7192
+ }
7193
+ }
7194
+ return void 0;
7195
+ }
7196
+ sanitizeSourceSender(value) {
7197
+ return sanitizeSenderName(value).replace(/\s+/gu, " ").trim() || "unknown";
7198
+ }
7199
+ sourceLabelForTurn(sessionId, envelope) {
7200
+ const reference = this.namedSessions?.presentation(sessionId);
7201
+ if (!reference || reference.status !== "open")
7202
+ return void 0;
7203
+ return this.createSourceLabel(reference, reference.target, {
7204
+ isGroup: envelope.isGroup,
7205
+ senderName: envelope.senderName
7206
+ });
7207
+ }
5922
7208
  markPreflighted(envelope) {
5923
7209
  this.preflightedEnvelopes.add(envelope);
5924
7210
  }
@@ -5995,7 +7281,51 @@ ${promptText}`;
5995
7281
  }
5996
7282
  }
5997
7283
  await this.waitForBridgeRecovery();
5998
- const sessionId = await this.router.resolve(this.name, envelope.senderId, envelope.chatId, envelope.threadId, this.config.cwd, envelope.isGroup);
7284
+ let sessionId;
7285
+ let namedTurn = this.namedTurnBindings.get(envelope);
7286
+ if (this.namedSessions && namedTurn) {
7287
+ if (namedTurn.sessionId === null) {
7288
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No task was selected when this message was received. Select a task and send it again.");
7289
+ return;
7290
+ }
7291
+ if (this.dropQueuedTurnIfStale(namedTurn.sessionId, namedTurn.generation, envelope)) {
7292
+ return;
7293
+ }
7294
+ try {
7295
+ const resumed = await this.namedSessions.resumeReserved(this.namedSessionOwner(envelope), namedTurn.sessionId);
7296
+ sessionId = resumed ? namedTurn.sessionId : void 0;
7297
+ } catch (error) {
7298
+ await this.sendNamedSessionError(envelope, error);
7299
+ return;
7300
+ }
7301
+ if (!sessionId) {
7302
+ process.stderr.write(`[${this.name}] dropped collected turn from ${envelope.senderId} for session ${namedTurn.sessionId}: reserved task is no longer available
7303
+ `);
7304
+ return;
7305
+ }
7306
+ } else if (this.namedSessions) {
7307
+ try {
7308
+ sessionId = await this.namedSessions.resolve(this.namedSessionOwner(envelope), (resolvedSessionId) => {
7309
+ const binding = this.bindNamedTurn(envelope, resolvedSessionId);
7310
+ namedTurn = binding;
7311
+ return () => this.releaseNamedTurnBinding(binding);
7312
+ });
7313
+ } catch (error) {
7314
+ await this.sendNamedSessionError(envelope, error);
7315
+ return;
7316
+ }
7317
+ if (!sessionId) {
7318
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No task is currently selected. Use /session new <name> or /session use <name>.");
7319
+ return;
7320
+ }
7321
+ } else {
7322
+ sessionId = await this.router.resolve(this.name, envelope.senderId, envelope.chatId, envelope.threadId, this.config.cwd, envelope.isGroup);
7323
+ }
7324
+ const sourceLabel = this.namedSessions ? this.sourceLabelForTurn(sessionId, envelope) : void 0;
7325
+ if (this.namedSessions && !sourceLabel) {
7326
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Could not identify the selected task. Use /sessions, select it again, and retry.");
7327
+ return;
7328
+ }
5999
7329
  if (bangText.startsWith("!")) {
6000
7330
  const cmd = bangText.slice(1).trim();
6001
7331
  const bridgeShellCommand = this.bridge.shellCommand;
@@ -6010,9 +7340,9 @@ ${fence}` : "(no output)";
6010
7340
  const exitLine = result.exitCode !== null && result.exitCode !== 0 ? `
6011
7341
  Exit code: ${result.exitCode}` : "";
6012
7342
  await this.sendThreadMessage(envelope.chatId, envelope.threadId, `$ ${cmd}
6013
- ${output}${exitLine}`);
7343
+ ${output}${exitLine}`, sourceLabel);
6014
7344
  } catch (error) {
6015
- await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Shell command failed: ${error instanceof Error ? error.message : String(error)}`);
7345
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Shell command failed: ${error instanceof Error ? error.message : String(error)}`, sourceLabel);
6016
7346
  }
6017
7347
  return;
6018
7348
  }
@@ -6040,12 +7370,19 @@ ${promptText}`;
6040
7370
  }
6041
7371
  let imageBase64 = envelope.imageBase64;
6042
7372
  let imageMimeType = envelope.imageMimeType;
7373
+ const images = [];
7374
+ if (imageBase64 && imageMimeType) {
7375
+ images.push({ data: imageBase64, mimeType: imageMimeType });
7376
+ }
6043
7377
  if (envelope.attachments?.length) {
6044
7378
  const filePaths = [];
6045
7379
  for (const att of envelope.attachments) {
6046
- if (att.type === "image" && att.data && !imageBase64) {
6047
- imageBase64 = att.data;
6048
- imageMimeType = att.mimeType;
7380
+ if (att.type === "image" && att.data) {
7381
+ images.push({ data: att.data, mimeType: att.mimeType });
7382
+ if (!imageBase64) {
7383
+ imageBase64 = att.data;
7384
+ imageMimeType = att.mimeType;
7385
+ }
6049
7386
  } else if (att.filePath) {
6050
7387
  const label = att.type === "file" ? "file" : att.type;
6051
7388
  const name = att.fileName ? ` "${sanitizeQuotedText(att.fileName, 128)}"` : "";
@@ -6131,8 +7468,13 @@ ${promptText}`;
6131
7468
  this.instructedSessions.add(sessionId);
6132
7469
  }
6133
7470
  const prev = this.sessionQueues.get(sessionId) ?? Promise.resolve();
6134
- const generation = this.sessionGenerations.get(sessionId) ?? 0;
7471
+ const generation = namedTurn?.generation ?? this.sessionGenerations.get(sessionId) ?? 0;
6135
7472
  const useBlockStreaming = this.config.blockStreaming === "on";
7473
+ if (namedTurn) {
7474
+ namedTurn.claimed = true;
7475
+ } else {
7476
+ this.reserveQueuedTurn(sessionId);
7477
+ }
6136
7478
  const current = prev.then(async () => {
6137
7479
  clearTimeout(steerWatchdog);
6138
7480
  if (this.dropQueuedTurnIfStale(sessionId, generation, envelope)) {
@@ -6199,7 +7541,7 @@ ${promptToSend}`;
6199
7541
  doneResolve = r;
6200
7542
  });
6201
7543
  const promptState = {
6202
- runId: randomUUID2(),
7544
+ runId: randomUUID3(),
6203
7545
  owner: {
6204
7546
  kind: "channel_user",
6205
7547
  id: envelope.senderId
@@ -6213,7 +7555,8 @@ ${promptToSend}`;
6213
7555
  messageId: envelope.messageId,
6214
7556
  senderId: envelope.senderId,
6215
7557
  senderName: envelope.senderName,
6216
- metadata: envelope.metadata
7558
+ metadata: envelope.metadata,
7559
+ sourceLabel
6217
7560
  };
6218
7561
  this.activePrompts.set(sessionId, promptState);
6219
7562
  this.emitTaskLifecycle({
@@ -6230,7 +7573,7 @@ ${promptToSend}`;
6230
7573
  minChars: this.config.blockStreamingChunk?.minChars ?? 400,
6231
7574
  maxChars: this.config.blockStreamingChunk?.maxChars ?? 1e3,
6232
7575
  idleMs: this.config.blockStreamingCoalesce?.idleMs ?? 1500,
6233
- send: /* @__PURE__ */ __name((text) => this.sendResponseMessage(envelope.chatId, text, sessionId), "send")
7576
+ send: /* @__PURE__ */ __name((text) => this.sendResponseMessage(envelope.chatId, text, sessionId, sourceLabel), "send")
6234
7577
  }) : null;
6235
7578
  promptState.stopStreaming = () => streamer?.stop();
6236
7579
  const heldChunks = [];
@@ -6273,6 +7616,7 @@ ${promptToSend}`;
6273
7616
  promptBridge.on("responseBoundary", onResponseBoundary);
6274
7617
  try {
6275
7618
  const response = await promptBridge.prompt(sessionId, promptToSend, {
7619
+ ...images.length > 0 ? { images } : {},
6276
7620
  imageBase64,
6277
7621
  imageMimeType,
6278
7622
  displayText
@@ -6331,11 +7675,17 @@ ${promptToSend}`;
6331
7675
  if (promptState.cancelled) {
6332
7676
  return;
6333
7677
  }
7678
+ if (sourceLabel) {
7679
+ this.inboundErrorSourceLabels.set(envelope, sourceLabel);
7680
+ }
6334
7681
  throw err;
6335
7682
  } finally {
6336
7683
  promptBridge.off("textChunk", onChunk);
6337
7684
  promptBridge.off("responseBoundary", onResponseBoundary);
6338
- streamer?.stop();
7685
+ if (streamer) {
7686
+ streamer.stop();
7687
+ await streamer.drain();
7688
+ }
6339
7689
  const stillCurrent = this.activePrompts.get(sessionId) === promptState;
6340
7690
  if (!promptState.clearEvicted) {
6341
7691
  try {
@@ -6349,39 +7699,15 @@ ${promptToSend}`;
6349
7699
  this.activePrompts.delete(sessionId);
6350
7700
  }
6351
7701
  promptState.resolve();
6352
- const buffer = this.collectBuffers.get(sessionId);
6353
- if (stillCurrent && buffer && buffer.length > 0) {
6354
- this.collectBuffers.delete(sessionId);
6355
- const lost = buffer.length;
6356
- const coalesced = buffer.map((b) => b.text).join("\n\n");
6357
- const coalescedDisplayText = buffer.map((b) => b.displayText).join("\n\n");
6358
- const lastEnvelope = buffer[buffer.length - 1].envelope;
6359
- this.notifyPromptBufferDrained(lastEnvelope.chatId, sessionId, buffer);
6360
- const syntheticEnvelope = {
6361
- ...lastEnvelope,
6362
- text: coalesced,
6363
- displayText: coalescedDisplayText,
6364
- // Coalesced text already carries each message's [sender] prefix.
6365
- alreadyPrefixed: true,
6366
- // Clear attachments/references — already resolved in original text
6367
- referencedText: void 0,
6368
- mentionedMemberIds: void 0,
6369
- attachments: void 0,
6370
- metadata: void 0,
6371
- imageBase64: void 0,
6372
- imageMimeType: void 0
6373
- };
6374
- this.markPreflighted(syntheticEnvelope);
6375
- this.processInbound(syntheticEnvelope).catch((err) => {
6376
- process.stderr.write(`[${this.name}] dropped ${lost} buffered message(s) on collect re-entry for session ${sessionId} (last sender ${lastEnvelope.senderId}): ${err instanceof Error ? err.message : String(err)}
6377
- `);
6378
- });
6379
- }
7702
+ this.drainCollectBufferForCurrentPrompt(sessionId, stillCurrent, "prompt completion");
6380
7703
  }
6381
7704
  });
6382
- this.sessionQueues.set(sessionId, current.catch(() => {
7705
+ const tracked = current.finally(() => {
7706
+ this.releaseQueuedTurn(sessionId);
7707
+ });
7708
+ this.sessionQueues.set(sessionId, tracked.catch(() => {
6383
7709
  }));
6384
- await current;
7710
+ await tracked;
6385
7711
  }
6386
7712
  pairingRejectionMessage(rejected) {
6387
7713
  return rejected === "sender_pending" ? "You already have a pending pairing request. It must be approved or expire before another can be created." : "Too many pending pairing requests. Please try again later.";
@@ -6491,10 +7817,10 @@ var PollingChannelBase = class extends ChannelBase {
6491
7817
  }
6492
7818
  saveCursor() {
6493
7819
  const path4 = this.cursorPath();
6494
- mkdirSync4(join6(getGlobalQwenDir(), "channels"), { recursive: true });
7820
+ mkdirSync5(join6(getGlobalQwenDir(), "channels"), { recursive: true });
6495
7821
  const tmp = `${path4}.tmp`;
6496
- writeFileSync4(tmp, JSON.stringify(this.cursor) + "\n", "utf-8");
6497
- renameSync4(tmp, path4);
7822
+ writeFileSync5(tmp, JSON.stringify(this.cursor) + "\n", "utf-8");
7823
+ renameSync5(tmp, path4);
6498
7824
  }
6499
7825
  startPollLoop() {
6500
7826
  if (this.running)
@@ -6518,7 +7844,7 @@ var PollingChannelBase = class extends ChannelBase {
6518
7844
  } catch (err) {
6519
7845
  this.consecutiveErrors++;
6520
7846
  const backoff = Math.min(INITIAL_BACKOFF * 2 ** (this.consecutiveErrors - 1), MAX_BACKOFF);
6521
- process3.stderr.write(`[Channel:${this.name}] poll error (attempt ${this.consecutiveErrors}), backing off ${backoff}ms: ${err}
7847
+ process4.stderr.write(`[Channel:${this.name}] poll error (attempt ${this.consecutiveErrors}), backing off ${backoff}ms: ${err}
6522
7848
  `);
6523
7849
  await this.abortableSleep(backoff);
6524
7850
  continue;
@@ -6528,7 +7854,7 @@ var PollingChannelBase = class extends ChannelBase {
6528
7854
  }
6529
7855
  loadCursorFromDisk() {
6530
7856
  try {
6531
- const raw = readFileSync4(this.cursorPath(), "utf-8").trim();
7857
+ const raw = readFileSync5(this.cursorPath(), "utf-8").trim();
6532
7858
  if (!raw)
6533
7859
  return null;
6534
7860
  return this.validateCursor(JSON.parse(raw));
@@ -6562,6 +7888,48 @@ function getTextContent(content) {
6562
7888
  return getString(content["text"]);
6563
7889
  }
6564
7890
  __name(getTextContent, "getTextContent");
7891
+ var CHANNEL_IMAGE_EXTENSIONS = ["bmp", "gif", "jpeg", "png", "webp"];
7892
+ var CHANNEL_IMAGE_MAX_UPLOAD_BYTES = 8 * 1024 * 1024;
7893
+ var CHANNEL_IMAGE_INLINE_MAX_BASE64_BYTES = 8 * 1024 * 1024;
7894
+ function channelImageName(mimeType, index = 0) {
7895
+ if (!mimeType.startsWith("image/")) {
7896
+ return void 0;
7897
+ }
7898
+ const extension = mimeType.slice("image/".length);
7899
+ if (!CHANNEL_IMAGE_EXTENSIONS.includes(extension)) {
7900
+ return void 0;
7901
+ }
7902
+ return index === 0 ? `image.${extension}` : `image-${index + 1}.${extension}`;
7903
+ }
7904
+ __name(channelImageName, "channelImageName");
7905
+ function decodeChannelImage(data, oversizedReason) {
7906
+ let estimatedBytes = Math.floor(data.length * 3 / 4);
7907
+ if (data.length % 4 === 0) {
7908
+ if (data.endsWith("=="))
7909
+ estimatedBytes -= 2;
7910
+ else if (data.endsWith("="))
7911
+ estimatedBytes -= 1;
7912
+ }
7913
+ if (estimatedBytes > CHANNEL_IMAGE_MAX_UPLOAD_BYTES) {
7914
+ return { skip: oversizedReason };
7915
+ }
7916
+ const bytes = Buffer.from(data, "base64");
7917
+ if (bytes.byteLength === 0) {
7918
+ return { skip: "empty once base64-decoded" };
7919
+ }
7920
+ return { bytes };
7921
+ }
7922
+ __name(decodeChannelImage, "decodeChannelImage");
7923
+ function isDefinitePromptAdmissionRejection(error) {
7924
+ if (!isRecord3(error)) {
7925
+ return false;
7926
+ }
7927
+ if (error["name"] === "DaemonPendingPromptLimitError") {
7928
+ return true;
7929
+ }
7930
+ return error["name"] === "DaemonHttpError" && typeof error["status"] === "number" && error["_daemonTurnError"] !== true;
7931
+ }
7932
+ __name(isDefinitePromptAdmissionRejection, "isDefinitePromptAdmissionRejection");
6565
7933
  function getSessionUpdate(data) {
6566
7934
  if (!isRecord3(data) || !isRecord3(data["update"])) {
6567
7935
  return void 0;
@@ -6633,8 +8001,9 @@ var DaemonChannelBridge = class extends EventEmitter2 {
6633
8001
  availableCommandsBySession = /* @__PURE__ */ new Map();
6634
8002
  turnBarriers = /* @__PURE__ */ new Map();
6635
8003
  channelLoopToolHandlers = [];
8004
+ channelLoopDisabledSessions = /* @__PURE__ */ new Set();
6636
8005
  registeredChannelLoopMcpSessions = /* @__PURE__ */ new Set();
6637
- channelLoopMcpRegistrations = /* @__PURE__ */ new Map();
8006
+ channelLoopMcpOperations = /* @__PURE__ */ new Map();
6638
8007
  channelLoopMcpServer;
6639
8008
  connected = false;
6640
8009
  lifecycleGeneration = 0;
@@ -6694,7 +8063,12 @@ var DaemonChannelBridge = class extends EventEmitter2 {
6694
8063
  await this.rejectStaleSession(session);
6695
8064
  }
6696
8065
  this.attachSession(session, bindingToken);
6697
- await this.registerChannelLoopMcpForSession(session.sessionId);
8066
+ if (options?.enableChannelLoops === false) {
8067
+ this.channelLoopDisabledSessions.add(session.sessionId);
8068
+ void this.reconcileChannelLoopMcpForSession(session.sessionId);
8069
+ } else {
8070
+ await this.reconcileChannelLoopMcpForSession(session.sessionId);
8071
+ }
6698
8072
  return session.sessionId;
6699
8073
  }
6700
8074
  async loadSession(sessionId, cwd, options, bindingToken) {
@@ -6704,7 +8078,8 @@ var DaemonChannelBridge = class extends EventEmitter2 {
6704
8078
  modelServiceId: this.options.modelServiceId,
6705
8079
  sessionId,
6706
8080
  sessionScope: this.options.sessionScope ?? "thread",
6707
- ...options?.approvalMode ? { approvalMode: options.approvalMode } : {}
8081
+ ...options?.approvalMode ? { approvalMode: options.approvalMode } : {},
8082
+ ...options?.sourceId ? { sourceId: options.sourceId } : {}
6708
8083
  });
6709
8084
  if (lifecycleGeneration !== this.lifecycleGeneration) {
6710
8085
  await this.rejectStaleSession(session);
@@ -6716,7 +8091,12 @@ var DaemonChannelBridge = class extends EventEmitter2 {
6716
8091
  throw new Error(`Daemon returned session ${session.sessionId} while loading ${sessionId}`);
6717
8092
  }
6718
8093
  this.attachSession(session, bindingToken);
6719
- await this.registerChannelLoopMcpForSession(session.sessionId);
8094
+ if (options?.enableChannelLoops === false) {
8095
+ this.channelLoopDisabledSessions.add(session.sessionId);
8096
+ void this.reconcileChannelLoopMcpForSession(session.sessionId);
8097
+ } else {
8098
+ await this.reconcileChannelLoopMcpForSession(session.sessionId);
8099
+ }
6720
8100
  return session.sessionId;
6721
8101
  }
6722
8102
  registerChannelLoopToolHandler(handler) {
@@ -6729,7 +8109,9 @@ var DaemonChannelBridge = class extends EventEmitter2 {
6729
8109
  cancel: /* @__PURE__ */ __name((sessionId, id) => this.resolveChannelLoopToolHandler(sessionId).cancel(sessionId, id), "cancel")
6730
8110
  });
6731
8111
  for (const sessionId of this.sessions.keys()) {
6732
- void this.registerChannelLoopMcpForSession(sessionId);
8112
+ if (!this.channelLoopDisabledSessions.has(sessionId)) {
8113
+ void this.reconcileChannelLoopMcpForSession(sessionId);
8114
+ }
6733
8115
  }
6734
8116
  }
6735
8117
  async prompt(sessionId, text, options) {
@@ -6773,29 +8155,101 @@ var DaemonChannelBridge = class extends EventEmitter2 {
6773
8155
  this.on("responseBoundary", clearChunks);
6774
8156
  this.on("sessionDied", onSessionDied);
6775
8157
  const turnBarrier = this.createTurnBarrier(sessionId);
6776
- const prompt = [];
6777
- if (options?.imageBase64 && options.imageMimeType) {
6778
- prompt.push({
6779
- type: "image",
6780
- data: options.imageBase64,
6781
- mimeType: options.imageMimeType
6782
- });
6783
- }
6784
- prompt.push({ type: "text", text });
6785
- const promptAuthorization = this.options.promptAuthorization;
8158
+ const uploadedAttachmentIds = [];
8159
+ let rollbackUploadedAttachments = false;
8160
+ const uploadAttachment = session.uploadAttachment?.bind(session);
8161
+ const removeAttachment = session.removeAttachment?.bind(session);
6786
8162
  try {
6787
- const result = await session.prompt({
6788
- prompt,
6789
- _meta: {
6790
- [CHANNEL_PROMPT_META_KEY]: true,
6791
- ...promptAuthorization ? {
6792
- [CHANNEL_PROMPT_AUTHORIZATION_META_KEY]: promptAuthorization
6793
- } : {},
6794
- ...options?.displayText !== void 0 ? {
6795
- [CHANNEL_PROMPT_DISPLAY_TEXT_META_KEY]: options.displayText
6796
- } : {}
8163
+ const prompt = [];
8164
+ const images = resolvePromptImages(options);
8165
+ if (this.options.sessionAttachments && uploadAttachment && removeAttachment) {
8166
+ try {
8167
+ const uploads = await Promise.allSettled(images.map(async (image, index) => {
8168
+ const name = channelImageName(image.mimeType, index);
8169
+ if (!name) {
8170
+ process.stderr.write(`[DaemonChannelBridge] skipped channel image with unsupported MIME type ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
8171
+ `);
8172
+ return void 0;
8173
+ }
8174
+ const decoded = decodeChannelImage(image.data, "above the daemon attachment size limit");
8175
+ if ("skip" in decoded) {
8176
+ process.stderr.write(`[DaemonChannelBridge] skipped channel image ${decoded.skip} ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
8177
+ `);
8178
+ return void 0;
8179
+ }
8180
+ const attachment = await uploadAttachment(new Blob([decoded.bytes], {
8181
+ type: image.mimeType
8182
+ }), name, image.mimeType, controller.signal);
8183
+ const attachmentId = getString(attachment["attachmentId"]);
8184
+ if (attachmentId)
8185
+ uploadedAttachmentIds.push(attachmentId);
8186
+ return attachment;
8187
+ }));
8188
+ const failure = uploads.find((upload) => upload.status === "rejected");
8189
+ if (failure) {
8190
+ throw failure.reason;
8191
+ }
8192
+ for (const upload of uploads) {
8193
+ if (upload.status === "fulfilled" && upload.value) {
8194
+ prompt.push(upload.value);
8195
+ }
8196
+ }
8197
+ } catch (error) {
8198
+ rollbackUploadedAttachments = true;
8199
+ throw error;
8200
+ }
8201
+ } else {
8202
+ let inlineBase64Bytes = 0;
8203
+ for (const image of images) {
8204
+ const decoded = decodeChannelImage(image.data, "above the inline image budget");
8205
+ if ("skip" in decoded) {
8206
+ process.stderr.write(`[DaemonChannelBridge] skipped channel image ${decoded.skip} ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
8207
+ `);
8208
+ continue;
8209
+ }
8210
+ if (inlineBase64Bytes + image.data.length > CHANNEL_IMAGE_INLINE_MAX_BASE64_BYTES) {
8211
+ process.stderr.write(`[DaemonChannelBridge] skipped channel image to keep the inline prompt under the daemon body limit ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
8212
+ `);
8213
+ continue;
8214
+ }
8215
+ inlineBase64Bytes += image.data.length;
8216
+ prompt.push({
8217
+ type: "image",
8218
+ data: image.data,
8219
+ mimeType: image.mimeType
8220
+ });
8221
+ }
8222
+ }
8223
+ prompt.push({ type: "text", text });
8224
+ if (controller.signal.aborted) {
8225
+ rollbackUploadedAttachments = true;
8226
+ controller.signal.throwIfAborted();
8227
+ }
8228
+ const promptAuthorization = this.options.promptAuthorization;
8229
+ if (controller.signal.aborted) {
8230
+ rollbackUploadedAttachments = true;
8231
+ throw controller.signal.reason;
8232
+ }
8233
+ let result;
8234
+ try {
8235
+ result = await session.prompt({
8236
+ prompt,
8237
+ _meta: {
8238
+ [CHANNEL_PROMPT_META_KEY]: true,
8239
+ ...promptAuthorization ? {
8240
+ [CHANNEL_PROMPT_AUTHORIZATION_META_KEY]: promptAuthorization
8241
+ } : {},
8242
+ ...options?.displayText !== void 0 ? {
8243
+ [CHANNEL_PROMPT_DISPLAY_TEXT_META_KEY]: options.displayText
8244
+ } : {}
8245
+ }
8246
+ }, controller.signal);
8247
+ } catch (error) {
8248
+ if (isDefinitePromptAdmissionRejection(error)) {
8249
+ rollbackUploadedAttachments = true;
6797
8250
  }
6798
- }, controller.signal);
8251
+ throw error;
8252
+ }
6799
8253
  await Promise.race([
6800
8254
  turnBarrier,
6801
8255
  new Promise((resolve3) => setTimeout(resolve3, 0))
@@ -6818,6 +8272,16 @@ var DaemonChannelBridge = class extends EventEmitter2 {
6818
8272
  if (controllers.size === 0 && this.activePromptControllers.get(sessionId) === controllers) {
6819
8273
  this.activePromptControllers.delete(sessionId);
6820
8274
  }
8275
+ if (rollbackUploadedAttachments && removeAttachment) {
8276
+ const removals = await Promise.allSettled(uploadedAttachmentIds.map((attachmentId) => removeAttachment(attachmentId)));
8277
+ removals.forEach((removal, index) => {
8278
+ if (removal.status === "rejected") {
8279
+ const reason = removal.reason instanceof Error ? removal.reason.message : String(removal.reason);
8280
+ process.stderr.write(`[DaemonChannelBridge] failed to remove channel image ${sanitizeLogText(uploadedAttachmentIds[index] ?? "", 128)} for session ${sanitizeLogText(sessionId, 128)} during rollback: ${sanitizeLogText(reason, 256)}
8281
+ `);
8282
+ }
8283
+ });
8284
+ }
6821
8285
  }
6822
8286
  }
6823
8287
  async shellCommand(sessionId, command, signal) {
@@ -7175,6 +8639,7 @@ var DaemonChannelBridge = class extends EventEmitter2 {
7175
8639
  this.eventControllers.delete(sessionId);
7176
8640
  this.sessions.delete(sessionId);
7177
8641
  this.sessionBindingTokens.delete(sessionId);
8642
+ this.channelLoopDisabledSessions.delete(sessionId);
7178
8643
  this.abortActivePrompts(sessionId);
7179
8644
  this.activePrompts.delete(sessionId);
7180
8645
  this.availableCommandsBySession.delete(sessionId);
@@ -7192,45 +8657,45 @@ var DaemonChannelBridge = class extends EventEmitter2 {
7192
8657
  }
7193
8658
  }
7194
8659
  if (unregisterChannelLoopMcp) {
7195
- this.unregisterChannelLoopMcpForSession(sessionId);
8660
+ void this.reconcileChannelLoopMcpForSession(sessionId);
7196
8661
  }
7197
8662
  return session;
7198
8663
  }
7199
- async registerChannelLoopMcpForSession(sessionId) {
7200
- const host = this.options.channelLoopMcpHost;
7201
- const server = this.channelLoopMcpServer;
7202
- if (!host || !server || this.registeredChannelLoopMcpSessions.has(sessionId)) {
7203
- return;
7204
- }
7205
- const pending = this.channelLoopMcpRegistrations.get(sessionId);
7206
- if (pending) {
7207
- await pending;
7208
- return;
7209
- }
7210
- const registration = host.register(sessionId, (message) => server.handleMessage(message, { sessionId })).then(async () => {
7211
- if (this.sessions.has(sessionId)) {
7212
- this.registeredChannelLoopMcpSessions.add(sessionId);
7213
- } else {
8664
+ reconcileChannelLoopMcpForSession(sessionId) {
8665
+ const previous = this.channelLoopMcpOperations.get(sessionId) ?? Promise.resolve();
8666
+ const operation = previous.catch(() => void 0).then(async () => {
8667
+ const host = this.options.channelLoopMcpHost;
8668
+ const server = this.channelLoopMcpServer;
8669
+ const shouldRegister = host !== void 0 && server !== void 0 && this.sessions.has(sessionId) && !this.channelLoopDisabledSessions.has(sessionId);
8670
+ if (!shouldRegister) {
8671
+ if (host && this.registeredChannelLoopMcpSessions.has(sessionId)) {
8672
+ await host.unregister(sessionId);
8673
+ this.registeredChannelLoopMcpSessions.delete(sessionId);
8674
+ }
8675
+ return;
8676
+ }
8677
+ if (this.registeredChannelLoopMcpSessions.has(sessionId))
8678
+ return;
8679
+ await host.register(sessionId, (message) => server.handleMessage(message, { sessionId }));
8680
+ this.registeredChannelLoopMcpSessions.add(sessionId);
8681
+ if (!this.sessions.has(sessionId) || this.channelLoopDisabledSessions.has(sessionId)) {
7214
8682
  await host.unregister(sessionId);
8683
+ this.registeredChannelLoopMcpSessions.delete(sessionId);
7215
8684
  }
7216
8685
  }).catch((error) => {
7217
8686
  this.lastError = error;
7218
8687
  }).finally(() => {
7219
- if (this.channelLoopMcpRegistrations.get(sessionId) === registration) {
7220
- this.channelLoopMcpRegistrations.delete(sessionId);
8688
+ if (this.channelLoopMcpOperations.get(sessionId) === operation) {
8689
+ this.channelLoopMcpOperations.delete(sessionId);
7221
8690
  }
7222
8691
  });
7223
- this.channelLoopMcpRegistrations.set(sessionId, registration);
7224
- await registration;
7225
- }
7226
- unregisterChannelLoopMcpForSession(sessionId) {
7227
- if (!this.registeredChannelLoopMcpSessions.delete(sessionId))
7228
- return;
7229
- void this.options.channelLoopMcpHost?.unregister(sessionId).catch((error) => {
7230
- this.lastError = error;
7231
- });
8692
+ this.channelLoopMcpOperations.set(sessionId, operation);
8693
+ return operation;
7232
8694
  }
7233
8695
  resolveChannelLoopToolHandler(sessionId) {
8696
+ if (!this.sessions.has(sessionId) || this.channelLoopDisabledSessions.has(sessionId)) {
8697
+ throw new Error("Channel loop tools are unavailable for this session");
8698
+ }
7234
8699
  const handler = this.channelLoopToolHandlers.find((candidate) => candidate.canHandle?.(sessionId) === true || this.channelLoopToolHandlers.length === 1 && !candidate.canHandle);
7235
8700
  if (handler)
7236
8701
  return handler;