@qwen-code/qwen-code 0.22.2-preview.1 → 0.22.3

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 (437) hide show
  1. package/bundled/computer-use/SKILL.md +3 -3
  2. package/bundled/goal-draft/SKILL.md +109 -0
  3. package/bundled/qc-helper/docs/configuration/settings.md +37 -36
  4. package/bundled/qc-helper/docs/features/_meta.ts +1 -0
  5. package/bundled/qc-helper/docs/features/channels/dws.md +2 -0
  6. package/bundled/qc-helper/docs/features/channels/overview.md +34 -3
  7. package/bundled/qc-helper/docs/features/code-review.md +3 -3
  8. package/bundled/qc-helper/docs/features/commands.md +2 -1
  9. package/bundled/qc-helper/docs/features/computer-use.md +2 -2
  10. package/bundled/qc-helper/docs/features/goals.md +58 -0
  11. package/bundled/qc-helper/docs/features/headless.md +13 -11
  12. package/bundled/qc-helper/docs/features/status-line.md +3 -3
  13. package/bundled/qc-helper/docs/features/tool-use-summaries.md +1 -1
  14. package/bundled/qc-helper/docs/qwen-serve.md +1 -1
  15. package/bundled/review/SKILL.md +81 -31
  16. package/bundled/review/references/persistence.md +7 -3
  17. package/bundled/review/references/posting.md +10 -4
  18. package/chunks/MaxSizedBox-X42SXBJH.js +110 -0
  19. package/chunks/{StandaloneSessionPicker-5D2L22SF.js → StandaloneSessionPicker-2FJAOLGG.js} +81 -83
  20. package/chunks/{acpAgent-4DCKLA32.js → acpAgent-2ZMIJAFV.js} +1153 -357
  21. package/chunks/{agent-B4AFZL6H.js → agent-X5WX3L2I.js} +44 -46
  22. package/chunks/agent-headless-IZM25LJA.js +85 -0
  23. package/chunks/{anthropicContentGenerator-U76GTVB6.js → anthropicContentGenerator-YVTB6QX7.js} +46 -50
  24. package/chunks/{artifact-tool-XDJCZZCF.js → artifact-tool-XSMPHZBT.js} +11 -3
  25. package/chunks/{askUserQuestion-ZXXERLEV.js → askUserQuestion-4SWJY65V.js} +2 -2
  26. package/chunks/bridge-ME6QXZNA.js +119 -0
  27. package/chunks/{channel-management-service-HXXS3HOZ.js → channel-management-service-HQ6KDS7C.js} +6 -6
  28. package/chunks/channel-settings-store-PEOI7XCG.js +120 -0
  29. package/chunks/{channel-worker-group-EVDC2P4K.js → channel-worker-group-6NA6BBZV.js} +8 -7
  30. package/chunks/{channel-worker-manager-IJJZHGEI.js → channel-worker-manager-NEZ2J6RK.js} +10 -8
  31. package/chunks/{channel-worker-supervisor-YSKB5GVB.js → channel-worker-supervisor-FKU7DIHJ.js} +6 -5
  32. package/chunks/{chunk-7QGEJI7M.js → chunk-23RFD54N.js} +1 -1
  33. package/chunks/{chunk-JLU3FPQR.js → chunk-2HYKTO7V.js} +5 -5
  34. package/chunks/chunk-2LNNVAUV.js +1584 -0
  35. package/chunks/{chunk-FJAITR4L.js → chunk-2OBKDPZZ.js} +2 -2
  36. package/chunks/{chunk-JSY43GMI.js → chunk-2OUD5T67.js} +185 -174
  37. package/chunks/{chunk-LJP2BBMK.js → chunk-2SXSLSQE.js} +4 -4
  38. package/chunks/chunk-2TNL5KPM.js +49 -0
  39. package/chunks/{chunk-XMSMTU6H.js → chunk-2YOHKHYR.js} +4 -4
  40. package/chunks/{chunk-MCKJTBZN.js → chunk-36GSQ3MM.js} +2 -2
  41. package/chunks/chunk-36LGBEM6.js +130 -0
  42. package/chunks/{chunk-ZWKGZB2K.js → chunk-3JXM2CVW.js} +3 -3
  43. package/chunks/{chunk-X3XGCJ32.js → chunk-3SZC4VP4.js} +28 -111
  44. package/chunks/{chunk-ILPXSKGQ.js → chunk-3WK3QDNV.js} +2 -2
  45. package/chunks/{chunk-IKN3IUJP.js → chunk-3XKLXHGK.js} +17 -9
  46. package/chunks/{chunk-ZLTEQYNC.js → chunk-42IDLQWS.js} +26 -20
  47. package/chunks/chunk-4CMJKUND.js +95 -0
  48. package/chunks/{chunk-77TOMRBG.js → chunk-4K7KNVWH.js} +3 -3
  49. package/chunks/{chunk-SQPOJ4ZI.js → chunk-4NDLQAY2.js} +27 -270
  50. package/chunks/{chunk-V3GINY6K.js → chunk-4QHPPXK2.js} +16 -8
  51. package/chunks/{chunk-VBF4KZRQ.js → chunk-4TT3IVGA.js} +1 -1
  52. package/chunks/{chunk-IOGKXPL3.js → chunk-4VPCTGH6.js} +6 -6
  53. package/chunks/chunk-4VQBQLIZ.js +293 -0
  54. package/chunks/{chunk-UCSV2IZW.js → chunk-62GQFYID.js} +3 -3
  55. package/chunks/{chunk-SLEWBQWM.js → chunk-6DIGWMGT.js} +2 -2
  56. package/chunks/{chunk-BRVWYMKV.js → chunk-6MBXY6WM.js} +1 -1
  57. package/chunks/{chunk-PVBT5QPD.js → chunk-6PJOTWAN.js} +1 -1
  58. package/chunks/{chunk-WZ3QKPMQ.js → chunk-6X7EU5TI.js} +19 -31
  59. package/chunks/{chunk-KQYW2GB5.js → chunk-6ZPDLGHN.js} +6 -6
  60. package/chunks/{chunk-AKQA3WCH.js → chunk-734C6JGI.js} +723 -3399
  61. package/chunks/{chunk-F7M6ZFGU.js → chunk-73RM4POK.js} +8 -8
  62. package/chunks/{chunk-ELY4QVIB.js → chunk-7EQQR46F.js} +1 -1
  63. package/chunks/{chunk-KGJGEEVR.js → chunk-7FA2II6K.js} +10 -1
  64. package/chunks/chunk-7J6OTNGO.js +424 -0
  65. package/chunks/{chunk-LWLEPXKK.js → chunk-7LOELM2I.js} +19 -2
  66. package/chunks/{chunk-DIMEPKCW.js → chunk-7S226YTK.js} +1 -1
  67. package/chunks/{chunk-AWFZDYSD.js → chunk-7T52W4SW.js} +1 -1
  68. package/chunks/{chunk-3JKUX4PB.js → chunk-7VMIMAVB.js} +9 -9
  69. package/chunks/{chunk-2MHGD7EE.js → chunk-A3LWD5MK.js} +1 -1
  70. package/chunks/{chunk-E4I5MVBR.js → chunk-ABS25QER.js} +1 -1
  71. package/chunks/{chunk-TDQBVEPK.js → chunk-AIAOIFY4.js} +10 -8
  72. package/chunks/{chunk-NEZZHGEI.js → chunk-AYFA7MZV.js} +6 -6
  73. package/chunks/{chunk-FVDQ5E67.js → chunk-B466ZSHZ.js} +3 -3
  74. package/chunks/{chunk-6US7BF3G.js → chunk-B5PHIJGI.js} +183 -5
  75. package/chunks/{chunk-4TPAG3O7.js → chunk-BFPESSTR.js} +1 -1
  76. package/chunks/{chunk-WBT3STGU.js → chunk-BKWNKLZB.js} +20 -20
  77. package/chunks/{chunk-T45CL2NT.js → chunk-BOWFGCEU.js} +3 -3
  78. package/chunks/{chunk-IPQA4SR5.js → chunk-BWORX6FA.js} +1 -1
  79. package/chunks/{chunk-BKDAF7ES.js → chunk-BXDAPVQU.js} +2 -2
  80. package/chunks/{chunk-ZEADBZLO.js → chunk-C2X7KY45.js} +3 -3
  81. package/chunks/{chunk-PTQF7WRA.js → chunk-C3PEFRKD.js} +6 -6
  82. package/chunks/{chunk-7OBH4AM2.js → chunk-CAJTKR6W.js} +5 -5
  83. package/chunks/{chunk-2FN2HNTI.js → chunk-CFJY4DGX.js} +2 -2
  84. package/chunks/{chunk-SUE2ZMUH.js → chunk-CIZCL2MW.js} +49 -31
  85. package/chunks/{chunk-W2WRNJKX.js → chunk-CLWO3B77.js} +6 -6
  86. package/chunks/{chunk-U45D46O5.js → chunk-CMHFCLBU.js} +1 -1
  87. package/chunks/{chunk-LRKMQPMD.js → chunk-CQH5KTKC.js} +1 -1
  88. package/chunks/{chunk-JECO75XD.js → chunk-D5LUXLUH.js} +2 -2
  89. package/chunks/chunk-DJBQCO2E.js +842 -0
  90. package/chunks/{chunk-BQBUZDDH.js → chunk-DJECZIJX.js} +42 -42
  91. package/chunks/{chunk-GTUZ3R6I.js → chunk-DJEZ55D4.js} +351 -113
  92. package/chunks/{chunk-HPECDZ6X.js → chunk-DJPASAUV.js} +3866 -1077
  93. package/chunks/{chunk-VYWCFWI5.js → chunk-DMLVSW7L.js} +542 -654
  94. package/chunks/{chunk-EB4VDXNI.js → chunk-DYXFD5RO.js} +182 -3
  95. package/chunks/{chunk-ZKAZP2ZG.js → chunk-EB6QRGHM.js} +3 -3
  96. package/chunks/{chunk-UWICO5KN.js → chunk-EFWRMA2I.js} +3 -3
  97. package/chunks/{chunk-3UZCLCXR.js → chunk-EKSCLBBF.js} +3 -3
  98. package/chunks/{chunk-ALTQLFEG.js → chunk-EQSBPBZ3.js} +5 -194
  99. package/chunks/{chunk-624NHDGB.js → chunk-ERFDKH32.js} +11 -0
  100. package/chunks/{chunk-4K44OMLI.js → chunk-FROAFEFB.js} +3 -3
  101. package/chunks/{chunk-MZS7GEC7.js → chunk-FTF2YIZ5.js} +1 -1
  102. package/chunks/{chunk-Z7VOMGDC.js → chunk-FTJMWDND.js} +0 -24
  103. package/chunks/{chunk-ZJG323IA.js → chunk-G6FECKTJ.js} +7 -5
  104. package/chunks/{chunk-TMGAS5TO.js → chunk-GBJBJ4HX.js} +26 -4
  105. package/chunks/{chunk-UCQOSV6A.js → chunk-GPGU4S2N.js} +3 -3
  106. package/chunks/{chunk-BHTJAO36.js → chunk-GVIVASYK.js} +7 -7
  107. package/chunks/chunk-H72BQNM3.js +645 -0
  108. package/chunks/{chunk-Z4X5MTRT.js → chunk-HCOBAG2L.js} +2 -4
  109. package/chunks/{chunk-FCSWZAS3.js → chunk-HP5LR5HL.js} +5 -5
  110. package/chunks/{chunk-7JPW6IYH.js → chunk-HVC3PNVC.js} +1 -1
  111. package/chunks/{chunk-OI2DR6QB.js → chunk-I6LYRJPB.js} +3 -3
  112. package/chunks/{chunk-E55H6BZ7.js → chunk-ICURCKIU.js} +1335 -119
  113. package/chunks/{chunk-PAJGKZAA.js → chunk-ISKH4QX3.js} +14 -12
  114. package/chunks/{chunk-AYVIGUUK.js → chunk-JB4JIVTJ.js} +1 -1
  115. package/chunks/{chunk-FN5C6C7G.js → chunk-JHYH6PYQ.js} +192 -478
  116. package/chunks/{chunk-T6OODF2U.js → chunk-JWALNCLT.js} +1 -1
  117. package/chunks/{chunk-SKNVVYIJ.js → chunk-K5KXUXWC.js} +3 -3
  118. package/chunks/{chunk-XUX6O2ZT.js → chunk-KID24ZFN.js} +4 -1
  119. package/chunks/{chunk-I6DY753S.js → chunk-KJO2LBUK.js} +944 -18
  120. package/chunks/{chunk-5T4LWWNX.js → chunk-KYWTPLBJ.js} +18 -21
  121. package/chunks/{chunk-CZFQ62RM.js → chunk-LHBUKJG2.js} +4 -4
  122. package/chunks/{chunk-TOF4UZHH.js → chunk-LUEHOUHM.js} +3 -3
  123. package/chunks/{chunk-2YDYDDE2.js → chunk-M3QRM3DE.js} +3 -3
  124. package/chunks/{chunk-GNMJNDZE.js → chunk-M6FOJXW3.js} +1 -1
  125. package/chunks/{chunk-ZGWDHSUM.js → chunk-MF7HCQWZ.js} +4 -4
  126. package/chunks/{chunk-66GIFJ63.js → chunk-MK2GIB46.js} +1 -1
  127. package/chunks/{chunk-TFV4RXPU.js → chunk-MO3FC2WT.js} +1 -1
  128. package/chunks/{chunk-TR7G7XFQ.js → chunk-MPNBVAEU.js} +5 -5
  129. package/chunks/{chunk-K3LPAED5.js → chunk-MQM5PJML.js} +24 -4
  130. package/chunks/{chunk-3WBMPR3S.js → chunk-MSBO7X2W.js} +1 -1
  131. package/chunks/{chunk-5PA6UEYA.js → chunk-MZCJWS5Y.js} +1 -1
  132. package/chunks/{chunk-BGXZBI5B.js → chunk-N4X7G4J2.js} +2 -1
  133. package/chunks/{chunk-27ONXAFY.js → chunk-NIFYWDYN.js} +1 -1
  134. package/chunks/{chunk-AP7WZONS.js → chunk-NMOD2FXQ.js} +19 -9
  135. package/chunks/{chunk-IJVDHPKD.js → chunk-NU7RKEZR.js} +2 -2
  136. package/chunks/{chunk-5URK47RN.js → chunk-NV55WWD6.js} +11595 -10375
  137. package/chunks/{chunk-HABT2Z7R.js → chunk-OU5APVWO.js} +4 -4
  138. package/chunks/{chunk-QR4AHMGQ.js → chunk-P2G476QN.js} +13 -13
  139. package/chunks/{chunk-5SM2AK65.js → chunk-PADPMMYM.js} +5 -5
  140. package/chunks/{chunk-RRKNX553.js → chunk-PDQGMSZK.js} +1 -1
  141. package/chunks/{chunk-GTUKW3I6.js → chunk-PPKZ7JOE.js} +1 -1
  142. package/chunks/{chunk-SAH4BD2J.js → chunk-PQEISIKS.js} +67 -0
  143. package/chunks/{chunk-XIRDDFRS.js → chunk-PZ66FRIC.js} +2644 -2296
  144. package/chunks/{chunk-H3HZ4AWW.js → chunk-Q2MSPKIJ.js} +1 -1
  145. package/chunks/{chunk-WEGXPP5E.js → chunk-QFCDF5PL.js} +1 -1
  146. package/chunks/{chunk-OF2DPXUS.js → chunk-QYMAKPY5.js} +119 -24
  147. package/chunks/{chunk-JVPFTDBK.js → chunk-RFIDDFAL.js} +1 -1
  148. package/chunks/chunk-RKFGE7JH.js +93 -0
  149. package/chunks/{chunk-CPPYZHBN.js → chunk-RU7BK6B3.js} +1 -1
  150. package/chunks/chunk-SEONHUS3.js +275 -0
  151. package/chunks/{chunk-DEYPMJPD.js → chunk-SMPR7SPO.js} +1 -1
  152. package/chunks/chunk-SS4MRMKE.js +3525 -0
  153. package/chunks/{chunk-BPMVPWBT.js → chunk-SS5SDCD3.js} +6 -6
  154. package/chunks/{chunk-J3CBIEDS.js → chunk-ST5MDGH3.js} +32 -15
  155. package/chunks/{chunk-J5352S3S.js → chunk-ST5V3ZOC.js} +3 -3
  156. package/chunks/{chunk-7CWEZQ6T.js → chunk-STUJ7IMZ.js} +0 -77
  157. package/chunks/{chunk-4K7NQDCG.js → chunk-T4EFG2P5.js} +18 -9
  158. package/chunks/{chunk-TWSKM647.js → chunk-T7KMERIX.js} +1 -1
  159. package/chunks/{chunk-5CMWFBML.js → chunk-TEBDFQBZ.js} +1 -1
  160. package/chunks/{chunk-EC5BONC3.js → chunk-TPI7O7HR.js} +1 -1
  161. package/chunks/{chunk-T2CRBIDJ.js → chunk-TTX2JUE6.js} +1 -1
  162. package/chunks/{chunk-VOEMVOM4.js → chunk-TVC6HQ2U.js} +2 -2
  163. package/chunks/{chunk-WXDZZFH3.js → chunk-TZBSGJ6C.js} +6 -6
  164. package/chunks/chunk-U54AYGEC.js +337 -0
  165. package/chunks/{chunk-2NNTE2ED.js → chunk-UHQFIS7N.js} +15 -0
  166. package/chunks/{chunk-76WGY6LW.js → chunk-URE3QBBB.js} +3 -3
  167. package/chunks/{chunk-KOJ6MHD5.js → chunk-UTLCH2FK.js} +1 -1
  168. package/chunks/{chunk-7DPKPBOQ.js → chunk-V2NDP3I4.js} +2 -2
  169. package/chunks/{chunk-FJWICLG7.js → chunk-V4264FEG.js} +9 -9
  170. package/chunks/{chunk-XR7OIAQF.js → chunk-V5J4J5TP.js} +2 -2
  171. package/chunks/{chunk-7UJTY5V3.js → chunk-V7EESTJV.js} +1 -1
  172. package/chunks/{chunk-Z6L3C4VF.js → chunk-VBCPNIQD.js} +39 -39
  173. package/chunks/{chunk-ATLGYSYH.js → chunk-VGIFXISB.js} +4 -4
  174. package/chunks/{chunk-X2UOOG5V.js → chunk-VQUGEVGR.js} +1 -1
  175. package/chunks/{chunk-XJS54K4A.js → chunk-VTHREBQM.js} +2 -2
  176. package/chunks/{chunk-AA7VBRBG.js → chunk-W3JH33JE.js} +8 -8
  177. package/chunks/{chunk-FPH6TXD3.js → chunk-WAOIHTJS.js} +1 -1
  178. package/chunks/{chunk-CDUN63IN.js → chunk-WGY3AVGM.js} +3 -3
  179. package/chunks/{chunk-JLGFGPZJ.js → chunk-WNPDGUJO.js} +22 -3
  180. package/chunks/{chunk-O3BUXIPM.js → chunk-WXEA74YB.js} +3 -3
  181. package/chunks/{chunk-YRLW2MSX.js → chunk-XBVNNDPK.js} +11 -4
  182. package/chunks/{chunk-VL4YX3FH.js → chunk-XF63PKEN.js} +1 -1
  183. package/chunks/{chunk-R7G7GKOX.js → chunk-XHF7WQ6B.js} +3 -3
  184. package/chunks/{chunk-3FSBMAYD.js → chunk-XQFT3QUF.js} +1 -1
  185. package/chunks/{chunk-3D6PDLNJ.js → chunk-XSG6G3G6.js} +49 -32
  186. package/chunks/{chunk-5JCZ3UKB.js → chunk-Y5S7LBOZ.js} +44 -7
  187. package/chunks/{chunk-OLS3MAXQ.js → chunk-YLSQLSH7.js} +15 -9
  188. package/chunks/{chunk-2SERYG4T.js → chunk-YTDVNSUQ.js} +1 -1
  189. package/chunks/{chunk-KPF2K55D.js → chunk-ZEYFMJQA.js} +39 -8
  190. package/chunks/{chunk-OT4YKUUV.js → chunk-ZU4UDIWX.js} +2 -2
  191. package/chunks/chunk-ZWJDJ6RU.js +2597 -0
  192. package/chunks/{chunk-R3T33BG5.js → chunk-ZYMLPEZQ.js} +3 -3
  193. package/chunks/{chunk-DID6IRGC.js → chunk-ZZMSC3RG.js} +56 -39
  194. package/chunks/{chunk-L436XFLJ.js → chunk-ZZUKP7HV.js} +1 -1
  195. package/chunks/config-utils-R6M3TA34.js +113 -0
  196. package/chunks/contextCommand-ZCAP23J6.js +106 -0
  197. package/chunks/{core-runtime-CEJRHCRF.js → core-runtime-AXB6SK5D.js} +59 -61
  198. package/chunks/{create-sub-session-RVHSWVAP.js → create-sub-session-23ITIELI.js} +3 -3
  199. package/chunks/{create-sub-session-DFZVIG56.js → create-sub-session-4QNE64MR.js} +164 -85
  200. package/chunks/{cron-create-IJGVR5R2.js → cron-create-KJ4IY6PP.js} +57 -10
  201. package/chunks/{cron-delete-4N5BI6GL.js → cron-delete-MBLQCAYY.js} +4 -4
  202. package/chunks/{cron-list-2CATIPUX.js → cron-list-DWH2PQ47.js} +4 -4
  203. package/chunks/{daemon-NAYK4XXU.js → daemon-3GNHB3M2.js} +523 -8
  204. package/chunks/{daemon-git-worktree-guard-L6ZUFKF7.js → daemon-git-worktree-guard-IVAJJWYK.js} +56 -58
  205. package/chunks/daemon-status-provider-K3SJ5C2U.js +117 -0
  206. package/chunks/daemon-trust-policy-DOR3YG62.js +112 -0
  207. package/chunks/{daemon-trust-policy-monitor-2G3EKQEU.js → daemon-trust-policy-monitor-KGGROSIV.js} +64 -66
  208. package/chunks/deferred-core-runtime-WOYLUQQ7.js +114 -0
  209. package/chunks/{display-image-6KHQNEAE.js → display-image-BE7I6ZB6.js} +5 -5
  210. package/chunks/{dist-DJBNNPLN.js → dist-4WXLCBDR.js} +1 -1
  211. package/chunks/{dist-TJUDJTCB.js → dist-5VXFRTWU.js} +280 -48
  212. package/chunks/{dist-6H6RZ5H6.js → dist-6BPYWHVD.js} +89 -65
  213. package/chunks/{dist-ZBVOXSKF.js → dist-GFN5DCBX.js} +74 -65
  214. package/chunks/{dist-SATKXKUR.js → dist-HZNT6GQS.js} +1 -1
  215. package/chunks/{dist-FVJZSIO4.js → dist-LVZCXVPF.js} +72 -20
  216. package/chunks/{dist-7CKN54NL.js → dist-N6W6E753.js} +71 -64
  217. package/chunks/{dist-4Q7NTUNB.js → dist-NSV4NBPK.js} +31 -14
  218. package/chunks/{dist-UQUFZDIY.js → dist-SWQMYP2Q.js} +18 -16
  219. package/chunks/earlyInputCapture-OHBEVO4K.js +107 -0
  220. package/chunks/{edit-ZERGX3RV.js → edit-LEQLAGPZ.js} +45 -47
  221. package/chunks/{en-6MDBWMTG.js → en-YLXBXM4Z.js} +2 -1
  222. package/chunks/{enter-worktree-UOZ6ZTP7.js → enter-worktree-35QCAAFP.js} +7 -7
  223. package/chunks/{enterPlanMode-W2RP3P5B.js → enterPlanMode-ONQN3WHE.js} +47 -49
  224. package/chunks/{environment-ZBJ23YVR.js → environment-UANTR7A4.js} +60 -62
  225. package/chunks/errors-WAK6KFBC.js +113 -0
  226. package/chunks/{exit-worktree-G2QATUCV.js → exit-worktree-XQD6D543.js} +7 -7
  227. package/chunks/exitPlanMode-UEYBG6P5.js +83 -0
  228. package/chunks/{fast-path-4MWBCEUF.js → fast-path-WUL7UZ6H.js} +7 -7
  229. package/chunks/{fast-path-settings-XMFBYFUH.js → fast-path-settings-XH426LUU.js} +3 -3
  230. package/chunks/{glob-DKTNG32L.js → glob-Y54QUWDY.js} +44 -46
  231. package/chunks/{goal-tools-CNVXMSZD.js → goal-tools-PTD3MBA7.js} +10 -8
  232. package/chunks/{grep-3NLSS32D.js → grep-5GPICQBU.js} +6 -6
  233. package/chunks/handleAutoUpdate-FP3NWNSG.js +109 -0
  234. package/chunks/i18n-INIU5X7A.js +124 -0
  235. package/chunks/{image-gen-63NN2H5N.js → image-gen-J6FDGUZP.js} +8 -10
  236. package/chunks/initializer-TYODV6GE.js +113 -0
  237. package/chunks/installationInfo-AAPKYDSZ.js +107 -0
  238. package/chunks/{keychain-token-storage-3MGFRYGM.js → keychain-token-storage-37XPR34V.js} +2 -2
  239. package/chunks/list-TUODDN6J.js +116 -0
  240. package/chunks/{list-agents-EXRSHIDJ.js → list-agents-DI3YFTDT.js} +2 -2
  241. package/chunks/{gemini-X5GQHQYF.js → llm-NSAVK5V3.js} +117 -114
  242. package/chunks/{geminiContentGenerator-UDSWXZXL.js → llm-content-generator-AY2QKXHE.js} +29 -29
  243. package/chunks/loadedSettingsAdapter-KJCLZ3C4.js +110 -0
  244. package/chunks/{loggingContentGenerator-73C7TKDK.js → loggingContentGenerator-DSFQHXIF.js} +328 -37
  245. package/chunks/{loop-wakeup-XBPB2BRO.js → loop-wakeup-XJFONSQ7.js} +5 -5
  246. package/chunks/{ls-WFONAND2.js → ls-3VAT2J7A.js} +4 -4
  247. package/chunks/{lsp-DUKPDUXN.js → lsp-5W7IRUBH.js} +2 -2
  248. package/chunks/{managed-npm-update-H2KOLQZU.js → managed-npm-update-TSWENTK5.js} +57 -59
  249. package/chunks/mcp-MWSAPFOL.js +110 -0
  250. package/chunks/{monitor-TGCMNJJH.js → monitor-RW4GWJHB.js} +44 -46
  251. package/chunks/nonInteractiveCli-JXKL343Q.js +185 -0
  252. package/chunks/{notebook-edit-YEUNZYDX.js → notebook-edit-CQNYS5OR.js} +45 -47
  253. package/chunks/{open-with-auth-4UI3KHUR.js → open-with-auth-YN3QZHVM.js} +1 -1
  254. package/chunks/{openaiContentGenerator-J7LD4VSL.js → openaiContentGenerator-J6QIF7EX.js} +26 -28
  255. package/chunks/pidfile-QOIQZFT2.js +111 -0
  256. package/chunks/process-registry-OAEG6WGC.js +662 -0
  257. package/chunks/{processUtils-ZHFC7CSL.js → processUtils-GPHNANKZ.js} +2 -2
  258. package/chunks/prompt-terminal-ledger-FOMFTYOM.js +105 -0
  259. package/chunks/{qwenContentGenerator-YEWWK3JQ.js → qwenContentGenerator-WWDXPYQI.js} +49 -51
  260. package/chunks/{qwenOAuth2-JIYAWXTW.js → qwenOAuth2-OF5WRBQT.js} +7 -9
  261. package/chunks/{read-file-ZQI3WPVK.js → read-file-FQUQHC6N.js} +13 -13
  262. package/chunks/{read-mcp-resource-7A7NFSFL.js → read-mcp-resource-XD257PGJ.js} +2 -2
  263. package/chunks/{record-artifact-CPJ4IJSD.js → record-artifact-VW23GS4F.js} +4 -4
  264. package/chunks/{report-findings-VFM5DENC.js → report-findings-AO5VYFLR.js} +9 -5
  265. package/chunks/{request-shutdown-6IW46GD7.js → request-shutdown-BCLA3JV2.js} +7 -7
  266. package/chunks/resumeHistoryUtils-NSGF3UCB.js +118 -0
  267. package/chunks/ripGrep-4CAT6SKV.js +39 -0
  268. package/chunks/{run-qwen-serve-WHWU7NZ6.js → run-qwen-serve-5X6IDZER.js} +371 -357
  269. package/chunks/{runtime-E7BSPWQP.js → runtime-HK27GMPF.js} +69 -71
  270. package/chunks/scheduled-tasks-MN2ZZ5P6.js +127 -0
  271. package/chunks/{scheduler-TJPATFOM.js → scheduler-WD4MHMO5.js} +58 -60
  272. package/chunks/{sdk-exporters-http-XXSKBD2C.js → sdk-exporters-http-TTU2CHBU.js} +2 -2
  273. package/chunks/{sdk-impl-VPNBLV7I.js → sdk-impl-UUMVYMAL.js} +11 -5
  274. package/chunks/{send-message-OXZK6USO.js → send-message-AJ4OCUBV.js} +25 -9
  275. package/chunks/serve-2XCHPPGG.js +118 -0
  276. package/chunks/{server-AXCN7QBJ.js → server-GO2N5DH7.js} +6080 -4964
  277. package/chunks/{session-HO4US3AG.js → session-IFNHTUBT.js} +112 -112
  278. package/chunks/session-pr-refresh-I3UBWMVP.js +364 -0
  279. package/chunks/{settings-B2PFJJN4.js → settings-L6N54CU6.js} +63 -65
  280. package/chunks/{shell-7XMSANI3.js → shell-4Y5LLW6N.js} +44 -46
  281. package/chunks/{skill-NL3VH562.js → skill-YOWTRAKQ.js} +68 -24
  282. package/chunks/skill-settings-ZL4JY5UE.js +118 -0
  283. package/chunks/spawnChannel-ZST6XJVJ.js +112 -0
  284. package/chunks/standalone-update-THVU3OKY.js +118 -0
  285. package/chunks/{startInteractiveUI-G2XKSCQ7.js → startInteractiveUI-XDFSHBZD.js} +3076 -2475
  286. package/chunks/{stdioHelpers-7A2EE64Z.js → stdioHelpers-AY6XCDY2.js} +3 -1
  287. package/chunks/{syntheticOutput-5AJ444QT.js → syntheticOutput-ML43GEFN.js} +3 -3
  288. package/chunks/{task-create-TY7P6UHG.js → task-create-QN4LYRSE.js} +11 -11
  289. package/chunks/{task-list-BELMPA75.js → task-list-JPLGK45U.js} +5 -5
  290. package/chunks/{task-stop-U3S3KZG2.js → task-stop-CXEH5L2D.js} +2 -2
  291. package/chunks/{task-update-O4IGDPCK.js → task-update-IWEOJFV2.js} +11 -11
  292. package/chunks/{team-create-IYBUOORE.js → team-create-OOPXCDBA.js} +44 -46
  293. package/chunks/{team-delete-YWFXX77L.js → team-delete-RZVVSGCY.js} +5 -5
  294. package/chunks/{team-plan-approval-YKU57LBM.js → team-plan-approval-DSX5EWAT.js} +44 -46
  295. package/chunks/terminal-image-renderer-KS25G33E.js +114 -0
  296. package/chunks/theme-manager-YMZNG45F.js +102 -0
  297. package/chunks/{todoWrite-435EO2CS.js → todoWrite-WDJNQEMW.js} +43 -25
  298. package/chunks/{tool-search-4Q3KASIQ.js → tool-search-J5Q6U7DN.js} +22 -22
  299. package/chunks/total-session-admission-PVN64JVV.js +114 -0
  300. package/chunks/trustedFolders-FATSC4YI.js +122 -0
  301. package/chunks/{types-WYCFCEHR.js → types-HKLPXZB3.js} +1 -3
  302. package/chunks/{update-relaunch-4DC2KKFS.js → update-relaunch-SIKHLHVR.js} +6 -6
  303. package/chunks/updateCheck-D3YG2WCD.js +118 -0
  304. package/chunks/useAutoAcceptIndicator-ILINOSAS.js +120 -0
  305. package/chunks/{validateNonInterActiveAuth-YAE4YPO3.js → validateNonInterActiveAuth-JUDQP6V4.js} +105 -105
  306. package/chunks/{version-QFP7OGWX.js → version-C63GDDWN.js} +1 -1
  307. package/chunks/{web-fetch-6T23IK6Q.js → web-fetch-FOQZMNSC.js} +16 -18
  308. package/chunks/{web-search-WBWTCZK2.js → web-search-PUJTBFUC.js} +9 -11
  309. package/chunks/{web-shell-static-A6QMUNNV.js → web-shell-static-HNB4FDOE.js} +2 -2
  310. package/chunks/{workflow-FMAA3D4R.js → workflow-Y5IPINVH.js} +128 -94
  311. package/chunks/workspace-providers-status-IJ4VHFMB.js +114 -0
  312. package/chunks/{workspace-registration-store-CMMX7EPX.js → workspace-registration-store-SMJWI2AI.js} +1 -1
  313. package/chunks/workspace-registry-BTGZIMPK.js +123 -0
  314. package/chunks/workspace-service-SIN67ITC.js +126 -0
  315. package/chunks/workspace-skills-status-F3RKCUIX.js +113 -0
  316. package/chunks/{workspace-trust-reconciler-PJDQUTJL.js → workspace-trust-reconciler-PLIDLJZX.js} +72 -72
  317. package/chunks/write-file-PAIGB33C.js +88 -0
  318. package/chunks/{zh-TW-MV4FWFB6.js → zh-TW-RPTMRTHG.js} +2 -1
  319. package/chunks/{zh-X3WGU6SI.js → zh-UNY63FR3.js} +2 -1
  320. package/chunks/{zoom-image-WVYVFRXF.js → zoom-image-LYQD7BQA.js} +13 -13
  321. package/cli.js +16 -16
  322. package/locales/en.js +2 -0
  323. package/locales/zh-TW.js +2 -0
  324. package/locales/zh.js +2 -0
  325. package/package.json +3 -3
  326. package/web-shell/assets/{abnfDiagram-VCTEODGH-d-8xa8uz.js → abnfDiagram-VCTEODGH-TRCMSA_6.js} +1 -1
  327. package/web-shell/assets/{arc-BhB7CrGK.js → arc-D9Uls5b3.js} +1 -1
  328. package/web-shell/assets/{architectureDiagram-5GKGNRK7-BDCd_IcJ.js → architectureDiagram-5GKGNRK7-Bwb8jPXg.js} +1 -1
  329. package/web-shell/assets/{blockDiagram-NRAW4CY4-C-AHu6mi.js → blockDiagram-NRAW4CY4-DHj71TSX.js} +1 -1
  330. package/web-shell/assets/{c4Diagram-UCG6FXSJ-CFjQt0vl.js → c4Diagram-UCG6FXSJ-uKBBdbyr.js} +1 -1
  331. package/web-shell/assets/channel-CXjVLaBa.js +1 -0
  332. package/web-shell/assets/{chunk-2Q5K7J3B-DRjwqaDq.js → chunk-2Q5K7J3B-BZd0Phrl.js} +1 -1
  333. package/web-shell/assets/{chunk-5VM5RSS4-eHM9noJE.js → chunk-5VM5RSS4-7KNeNBqN.js} +1 -1
  334. package/web-shell/assets/{chunk-F27PBJKO-CcSWUYn1.js → chunk-F27PBJKO-BeboS7Fi.js} +1 -1
  335. package/web-shell/assets/{chunk-G27WJ6UU-DNr-RF5S.js → chunk-G27WJ6UU-RV3JONoj.js} +1 -1
  336. package/web-shell/assets/{chunk-JWPE2WC7-rVJHoNF8.js → chunk-JWPE2WC7-Yw9FCFsP.js} +1 -1
  337. package/web-shell/assets/{chunk-LCL6LL3I-B2aX0Gzy.js → chunk-LCL6LL3I-GIMnydDe.js} +1 -1
  338. package/web-shell/assets/{chunk-POPQ4Y6H-DVp07n32.js → chunk-POPQ4Y6H-Cfrux7bL.js} +1 -1
  339. package/web-shell/assets/{chunk-SVP7TREG-C6QB_KUr.js → chunk-SVP7TREG-BlvIN0CO.js} +1 -1
  340. package/web-shell/assets/{chunk-XXDRQBXY-CLVxDNCy.js → chunk-XXDRQBXY-DMs7Rr4t.js} +1 -1
  341. package/web-shell/assets/classDiagram-DTDB5LWJ-DANEijO3.js +1 -0
  342. package/web-shell/assets/classDiagram-v2-JRS7N3AN-DANEijO3.js +1 -0
  343. package/web-shell/assets/{cose-bilkent-JH36ORCC-B_aKX3A3.js → cose-bilkent-JH36ORCC-DNGqTNwI.js} +1 -1
  344. package/web-shell/assets/{cynefin-OW5HDTMX-BBw2pPUa.js → cynefin-OW5HDTMX-CojMpaoH.js} +1 -1
  345. package/web-shell/assets/{cynefinDiagram-5FMLGOSQ-WWPiBTwk.js → cynefinDiagram-5FMLGOSQ-DF07YXGo.js} +1 -1
  346. package/web-shell/assets/{dagre-3AP2YEHR-CPtIdka-.js → dagre-3AP2YEHR-17-4WtCH.js} +1 -1
  347. package/web-shell/assets/{diagram-S7CK7UJ4-Dj5og1Xi.js → diagram-S7CK7UJ4-G3Wuyz9m.js} +1 -1
  348. package/web-shell/assets/{diagram-UQ7AKVKN-CC_x70YC.js → diagram-UQ7AKVKN-nyWIKZ8P.js} +1 -1
  349. package/web-shell/assets/{diagram-VSXAHHWV-rXMfsO4n.js → diagram-VSXAHHWV-tKHhmYSC.js} +1 -1
  350. package/web-shell/assets/{diagram-VX7I27RA-DEmLYQbh.js → diagram-VX7I27RA-CpDf1hYk.js} +1 -1
  351. package/web-shell/assets/{diagram-Z3DM3KII-B0ys6Vxx.js → diagram-Z3DM3KII-Cah2IFXG.js} +1 -1
  352. package/web-shell/assets/{ebnfDiagram-PWID7BFC-Bq6y6lKH.js → ebnfDiagram-PWID7BFC-DNaCkgxy.js} +1 -1
  353. package/web-shell/assets/{erDiagram-SSCWMZ5O-l69JeBZ4.js → erDiagram-SSCWMZ5O-CAdRgcf1.js} +1 -1
  354. package/web-shell/assets/{flowDiagram-A5DVABFB-Cm-EzGY4.js → flowDiagram-A5DVABFB-CMUJAShL.js} +1 -1
  355. package/web-shell/assets/{ganttDiagram-EL5Y4UJY-BRS9ZOp7.js → ganttDiagram-EL5Y4UJY-o-_MUNey.js} +1 -1
  356. package/web-shell/assets/{gitGraphDiagram-WWUBYQGX-DAMRKgLk.js → gitGraphDiagram-WWUBYQGX-_aG-2Z7O.js} +1 -1
  357. package/web-shell/assets/{index-Bncx8E8M.js → index-BK_NKxiJ.js} +1 -1
  358. package/web-shell/assets/index-Ci-z4zWY.js +1913 -0
  359. package/web-shell/assets/index-LDlXZRHA.css +36 -0
  360. package/web-shell/assets/{infoDiagram-RXCK75RN-DasffaZp.js → infoDiagram-RXCK75RN-qJPeZbGP.js} +1 -1
  361. package/web-shell/assets/{ishikawaDiagram-5VMMS53U-CmRUt3C-.js → ishikawaDiagram-5VMMS53U-7Om4vNo1.js} +1 -1
  362. package/web-shell/assets/{journeyDiagram-EYS64GPL-DzhE_c1Z.js → journeyDiagram-EYS64GPL-BNtXqmBW.js} +1 -1
  363. package/web-shell/assets/{kanban-definition-3QL26DDD-BH5b6nn3.js → kanban-definition-3QL26DDD-COgFec8P.js} +1 -1
  364. package/web-shell/assets/{layout-BxoLK-LY.js → layout-B9n81PC9.js} +1 -1
  365. package/web-shell/assets/{linear-CqDPvVI7.js → linear-Bb6nXZQq.js} +1 -1
  366. package/web-shell/assets/{mermaid.core-DBoN4plo.js → mermaid.core-Cgrm3JVT.js} +6 -6
  367. package/web-shell/assets/{mindmap-definition-FBJOCRG2-DJJtXPEM.js → mindmap-definition-FBJOCRG2-TKwLTjnG.js} +1 -1
  368. package/web-shell/assets/{pegDiagram-XKGWAZYB-DmgPXP5l.js → pegDiagram-XKGWAZYB-BJPuvlpi.js} +1 -1
  369. package/web-shell/assets/{pieDiagram-E7YTZNPT-1ezjzcI5.js → pieDiagram-E7YTZNPT-D01VkNsW.js} +1 -1
  370. package/web-shell/assets/{quadrantDiagram-AXDQQJYC-ChKARz3k.js → quadrantDiagram-AXDQQJYC-C4hHsiv-.js} +1 -1
  371. package/web-shell/assets/{railroadDiagram-O6MQD6OU-k7mNB_QT.js → railroadDiagram-O6MQD6OU-CuEO4xK2.js} +1 -1
  372. package/web-shell/assets/{requirementDiagram-EFPCY7ZU-B0XFsii0.js → requirementDiagram-EFPCY7ZU-D5J_vIo5.js} +1 -1
  373. package/web-shell/assets/{sankeyDiagram-P5KCCOFB-repJ2Ukk.js → sankeyDiagram-P5KCCOFB-DQKY223V.js} +1 -1
  374. package/web-shell/assets/{sequenceDiagram-WJ2MYXX4-D0gUSKGu.js → sequenceDiagram-WJ2MYXX4-D27YV_cm.js} +1 -1
  375. package/web-shell/assets/{sizeCapture-X5ZJPWSS-CE463F74.js → sizeCapture-X5ZJPWSS-TJg8UcwV.js} +1 -1
  376. package/web-shell/assets/{stateDiagram-HBIQ2CUA-003zYtJa.js → stateDiagram-HBIQ2CUA-CmnPzUIx.js} +1 -1
  377. package/web-shell/assets/stateDiagram-v2-4QOOHH4V-DNKPyrDc.js +1 -0
  378. package/web-shell/assets/{swimlanes-XN3QIQJK-DBj8MFpu.js → swimlanes-XN3QIQJK-ByeP3_Nu.js} +1 -1
  379. package/web-shell/assets/swimlanesDiagram-VK2B7HYN-ZL1HPC6w.js +8 -0
  380. package/web-shell/assets/{timeline-definition-24CTP7MA-D2aCh4a9.js → timeline-definition-24CTP7MA-BsFA5juR.js} +1 -1
  381. package/web-shell/assets/{vennDiagram-4TSXK5OY-BHMfWBpl.js → vennDiagram-4TSXK5OY-DREP2ECo.js} +1 -1
  382. package/web-shell/assets/{wardleyDiagram-VM6X3IG4-RdHAj66j.js → wardleyDiagram-VM6X3IG4-iyd9lKi2.js} +1 -1
  383. package/web-shell/assets/{xychartDiagram-S5SC5T6Z-BgJWOvq0.js → xychartDiagram-S5SC5T6Z-CVr1_j9q.js} +1 -1
  384. package/web-shell/index.html +3 -3
  385. package/chunks/MaxSizedBox-BFVLZP2E.js +0 -112
  386. package/chunks/agent-headless-UCBCS4V4.js +0 -87
  387. package/chunks/bridge-FRNYLYY7.js +0 -119
  388. package/chunks/channel-settings-store-IV4KDL7N.js +0 -117
  389. package/chunks/chunk-I6VVOZDK.js +0 -202
  390. package/chunks/chunk-JS73NNWF.js +0 -30
  391. package/chunks/chunk-K2OJUPOE.js +0 -78
  392. package/chunks/chunk-P3QQPMQA.js +0 -19
  393. package/chunks/chunk-SDVF6A42.js +0 -945
  394. package/chunks/chunk-VGHZT6JR.js +0 -51
  395. package/chunks/config-utils-PMX4QZ5Y.js +0 -113
  396. package/chunks/contextCommand-NZRLVAFC.js +0 -108
  397. package/chunks/daemon-status-provider-FTTYZEKN.js +0 -118
  398. package/chunks/daemon-trust-policy-WMGFPA3Z.js +0 -114
  399. package/chunks/deferred-core-runtime-BUFEFYNA.js +0 -116
  400. package/chunks/earlyInputCapture-ROPLMXA7.js +0 -109
  401. package/chunks/errors-U5KNAADB.js +0 -115
  402. package/chunks/exitPlanMode-VFJ5R5DD.js +0 -85
  403. package/chunks/handleAutoUpdate-W4BUYFT7.js +0 -111
  404. package/chunks/i18n-KX7FEKJJ.js +0 -126
  405. package/chunks/initializer-SNVVLS7Y.js +0 -115
  406. package/chunks/installationInfo-D4XGQNHJ.js +0 -109
  407. package/chunks/list-WYECMLXZ.js +0 -118
  408. package/chunks/loadedSettingsAdapter-5WZBXBCR.js +0 -112
  409. package/chunks/mcp-CBWAROY3.js +0 -112
  410. package/chunks/nonInteractiveCli-O232UUV7.js +0 -185
  411. package/chunks/pidfile-MSPQ255Y.js +0 -113
  412. package/chunks/process-registry-PMJOA5CO.js +0 -172
  413. package/chunks/prompt-terminal-ledger-YPGMNDXT.js +0 -107
  414. package/chunks/resumeHistoryUtils-35D4KAVT.js +0 -120
  415. package/chunks/ripGrep-46N4FLOS.js +0 -39
  416. package/chunks/serve-WQ54UIYG.js +0 -120
  417. package/chunks/skill-settings-SKMN5YXC.js +0 -120
  418. package/chunks/spawnChannel-NE43EEO3.js +0 -113
  419. package/chunks/standalone-update-XOCWGXAH.js +0 -120
  420. package/chunks/terminal-image-renderer-NREKYTAD.js +0 -116
  421. package/chunks/theme-manager-IJS6OICX.js +0 -104
  422. package/chunks/total-session-admission-L5RPEFZ2.js +0 -114
  423. package/chunks/trustedFolders-OILZCHRD.js +0 -124
  424. package/chunks/updateCheck-AK4COHNK.js +0 -120
  425. package/chunks/useAutoAcceptIndicator-O2N2Q6KG.js +0 -122
  426. package/chunks/workspace-providers-status-OCFEWIPQ.js +0 -116
  427. package/chunks/workspace-registry-WJLZPRTG.js +0 -123
  428. package/chunks/workspace-service-5ZOBMMIB.js +0 -131
  429. package/chunks/workspace-skills-status-B6NJZD4B.js +0 -115
  430. package/chunks/write-file-B2RNX6SU.js +0 -90
  431. package/web-shell/assets/channel-CU-Zyv0r.js +0 -1
  432. package/web-shell/assets/classDiagram-DTDB5LWJ-C0J2_LKs.js +0 -1
  433. package/web-shell/assets/classDiagram-v2-JRS7N3AN-C0J2_LKs.js +0 -1
  434. package/web-shell/assets/index-C-XlQn24.css +0 -5
  435. package/web-shell/assets/index-gdvMN4J1.js +0 -1860
  436. package/web-shell/assets/stateDiagram-v2-4QOOHH4V-2uACDdJN.js +0 -1
  437. package/web-shell/assets/swimlanesDiagram-VK2B7HYN-C4JJkavU.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,11 @@ 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 approvalMode || loopsDisabled ? {
1726
+ ...approvalMode ? { approvalMode } : {},
1727
+ ...loopsDisabled ? { enableChannelLoops: false } : {}
1728
+ } : void 0;
1698
1729
  }
1699
1730
  async resolve(channelName, senderId, chatId, threadId, cwd, isGroup, options) {
1700
1731
  const key = this.routingKey(channelName, senderId, chatId, options?.routingThreadId ?? threadId);
@@ -1881,6 +1912,120 @@ var SessionRouter = class {
1881
1912
  }
1882
1913
  return false;
1883
1914
  }
1915
+ getSessionCwd(sessionId) {
1916
+ return this.toCwd.get(sessionId);
1917
+ }
1918
+ async createManagedSession(target, cwd) {
1919
+ const loadWindow = this.beginSessionLoad();
1920
+ const lifecycleGeneration = this.lifecycleGeneration;
1921
+ const bridge = this.bridge;
1922
+ const bindingToken = {};
1923
+ try {
1924
+ let lastDeadSessionId;
1925
+ for (let attempt = 0; attempt < 2; attempt++) {
1926
+ const sessionId = await bridge.newSession(cwd, {
1927
+ ...this.sessionOptions(target.channelName),
1928
+ sourceId: target.channelName
1929
+ }, bindingToken);
1930
+ if (lifecycleGeneration !== this.lifecycleGeneration || bridge !== this.bridge) {
1931
+ this.scheduleManagedDiscard(bridge, sessionId, bindingToken);
1932
+ throw new Error("Managed session creation was invalidated");
1933
+ }
1934
+ if (typeof sessionId !== "string" || sessionId.length === 0) {
1935
+ throw new Error("Invalid session ID from bridge");
1936
+ }
1937
+ if (loadWindow.delete(sessionId)) {
1938
+ lastDeadSessionId = sessionId;
1939
+ await bridge.discardSession?.(sessionId, bindingToken);
1940
+ continue;
1941
+ }
1942
+ this.toTarget.set(sessionId, target);
1943
+ this.toCwd.set(sessionId, cwd);
1944
+ this.liveSessionIds.add(sessionId);
1945
+ return sessionId;
1946
+ }
1947
+ throw new Error(`Managed session ${lastDeadSessionId ?? "unknown"} died before creation completed`);
1948
+ } finally {
1949
+ this.endSessionLoad(loadWindow);
1950
+ }
1951
+ }
1952
+ async loadManagedSession(sessionId, target, cwd) {
1953
+ if (this.liveSessionIds.has(sessionId)) {
1954
+ this.toTarget.set(sessionId, target);
1955
+ this.toCwd.set(sessionId, cwd);
1956
+ return { loaded: false };
1957
+ }
1958
+ const loadWindow = this.beginSessionLoad();
1959
+ const lifecycleGeneration = this.lifecycleGeneration;
1960
+ const bridge = this.bridge;
1961
+ const bindingToken = {};
1962
+ let loadedSessionId;
1963
+ try {
1964
+ loadedSessionId = await bridge.loadSession(sessionId, cwd, this.sessionOptions(target.channelName), bindingToken);
1965
+ if (lifecycleGeneration !== this.lifecycleGeneration || bridge !== this.bridge) {
1966
+ this.scheduleManagedDiscard(bridge, loadedSessionId, bindingToken);
1967
+ loadedSessionId = void 0;
1968
+ throw new Error("Managed session load was invalidated");
1969
+ }
1970
+ if (loadedSessionId !== sessionId) {
1971
+ const unexpectedSessionId = loadedSessionId;
1972
+ await bridge.discardSession?.(unexpectedSessionId, bindingToken);
1973
+ loadedSessionId = void 0;
1974
+ throw new Error(`Bridge returned session ${unexpectedSessionId || "unknown"} while loading ${sessionId}`);
1975
+ }
1976
+ if (loadWindow.delete(sessionId)) {
1977
+ throw new Error(`Managed session ${sessionId} died before loading completed`);
1978
+ }
1979
+ this.toTarget.set(sessionId, target);
1980
+ this.toCwd.set(sessionId, cwd);
1981
+ this.liveSessionIds.add(sessionId);
1982
+ return { loaded: true };
1983
+ } catch (error) {
1984
+ if (loadedSessionId) {
1985
+ await bridge.discardSession?.(loadedSessionId, bindingToken).catch(() => void 0);
1986
+ }
1987
+ throw error;
1988
+ } finally {
1989
+ this.endSessionLoad(loadWindow);
1990
+ }
1991
+ }
1992
+ activateManagedSession(sessionId, target, cwd) {
1993
+ const key = this.routingKey(target.channelName, target.senderId, target.chatId, target.threadId);
1994
+ if (this.toSession.get(key) === sessionId)
1995
+ return;
1996
+ this.invalidateRouteOperation(key);
1997
+ this.toSession.set(key, sessionId);
1998
+ this.toTarget.set(sessionId, target);
1999
+ this.toCwd.set(sessionId, cwd);
2000
+ this.persist();
2001
+ }
2002
+ forgetManagedSession(sessionId) {
2003
+ let changed = false;
2004
+ for (const [key, mappedSessionId] of this.toSession) {
2005
+ if (mappedSessionId !== sessionId)
2006
+ continue;
2007
+ this.invalidateRouteOperation(key);
2008
+ this.toSession.delete(key);
2009
+ changed = true;
2010
+ }
2011
+ changed = this.toTarget.delete(sessionId) || changed;
2012
+ changed = this.toCwd.delete(sessionId) || changed;
2013
+ changed = this.liveSessionIds.delete(sessionId) || changed;
2014
+ if (changed)
2015
+ this.persist();
2016
+ }
2017
+ async detachManagedSession(sessionId) {
2018
+ try {
2019
+ if (this.liveSessionIds.has(sessionId)) {
2020
+ if (!this.bridge.discardSession) {
2021
+ throw new Error("Managed session detach is not supported");
2022
+ }
2023
+ await this.bridge.discardSession(sessionId);
2024
+ }
2025
+ } finally {
2026
+ this.forgetManagedSession(sessionId);
2027
+ }
2028
+ }
1884
2029
  /**
1885
2030
  * Remove session(s) for the given sender. Returns the removed session IDs.
1886
2031
  */
@@ -2055,6 +2200,7 @@ var SessionRouter = class {
2055
2200
  this.toSession.set(key, sessionId);
2056
2201
  this.toTarget.set(sessionId, entry.target);
2057
2202
  this.toCwd.set(sessionId, entry.cwd);
2203
+ this.liveSessionIds.add(sessionId);
2058
2204
  reservation.resolve(sessionId);
2059
2205
  if (sessionId !== entry.sessionId) {
2060
2206
  changed = true;
@@ -2278,6 +2424,12 @@ var SessionRouter = class {
2278
2424
  } catch {
2279
2425
  }
2280
2426
  }
2427
+ scheduleManagedDiscard(bridge, sessionId, bindingToken) {
2428
+ try {
2429
+ void bridge.discardSession?.(sessionId, bindingToken).catch(() => void 0);
2430
+ } catch {
2431
+ }
2432
+ }
2281
2433
  createSessionReservation() {
2282
2434
  let resolveReservation;
2283
2435
  let rejectReservation;
@@ -2296,6 +2448,615 @@ var SessionRouter = class {
2296
2448
  }
2297
2449
  };
2298
2450
 
2451
+ // packages/channels/base/dist/named-session-manager.js
2452
+ init_esbuild_shims();
2453
+ import { randomUUID as randomUUID2 } from "node:crypto";
2454
+ 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";
2455
+ import { dirname as dirname4 } from "node:path";
2456
+ import process3 from "node:process";
2457
+ var REGISTRY_VERSION = 1;
2458
+ var MAX_OPEN_TASKS = 8;
2459
+ var TASK_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$/;
2460
+ var NamedSessionManager = class {
2461
+ static {
2462
+ __name(this, "NamedSessionManager");
2463
+ }
2464
+ channelName;
2465
+ cwd;
2466
+ canonicalCwd;
2467
+ filePath;
2468
+ router;
2469
+ isBusy;
2470
+ now;
2471
+ registry;
2472
+ ownerOperations = /* @__PURE__ */ new Map();
2473
+ constructor(options) {
2474
+ this.channelName = options.channelName;
2475
+ this.cwd = options.cwd;
2476
+ this.canonicalCwd = canonicalizeWorkspacePath(options.cwd);
2477
+ this.filePath = options.filePath;
2478
+ this.router = options.router;
2479
+ this.isBusy = options.isBusy;
2480
+ this.now = options.now ?? Date.now;
2481
+ this.registry = this.readRegistry();
2482
+ }
2483
+ resolve(input, expectedSessionId, reserve) {
2484
+ return this.withOwnerLock(input, () => this.resolveLocked(input, expectedSessionId, reserve));
2485
+ }
2486
+ resolveAfterPreparation(input, prepare, needsSession, reserve) {
2487
+ return this.withOwnerLock(input, async () => {
2488
+ if (await prepare() === false)
2489
+ return { status: "aborted" };
2490
+ if (!needsSession())
2491
+ return { status: "bypassed" };
2492
+ try {
2493
+ return {
2494
+ status: "resolved",
2495
+ sessionId: await this.resolveLocked(input, void 0, reserve)
2496
+ };
2497
+ } catch (error) {
2498
+ return { status: "resolve_error", error };
2499
+ }
2500
+ });
2501
+ }
2502
+ list(input, includeClosed) {
2503
+ return this.withOwnerLock(input, async () => {
2504
+ const owner = await this.ensureOwner(input, false);
2505
+ if (!owner)
2506
+ return [];
2507
+ return owner.tasks.filter((task) => includeClosed || task.status === "open").map((task) => this.view(owner, task));
2508
+ });
2509
+ }
2510
+ current(input) {
2511
+ return this.withOwnerLock(input, async () => {
2512
+ const owner = await this.ensureOwner(input, false);
2513
+ if (!owner?.activeTaskName)
2514
+ return void 0;
2515
+ const task = this.findTask(owner, owner.activeTaskName);
2516
+ return task ? this.selection(owner, task) : void 0;
2517
+ });
2518
+ }
2519
+ create(input, name) {
2520
+ return this.withOwnerLock(input, async () => {
2521
+ this.validateName(name);
2522
+ const existingOwner = await this.ensureOwner(input, false);
2523
+ const owner = existingOwner ?? this.createOwner(input, null, []);
2524
+ if (this.findTask(owner, name)) {
2525
+ throw new Error(`Task "${name}" already exists.`);
2526
+ }
2527
+ if (this.openTaskCount(owner) >= MAX_OPEN_TASKS) {
2528
+ throw new Error("You already have eight open tasks. Close one before creating another.");
2529
+ }
2530
+ this.assertCanLeaveSelected(owner, void 0);
2531
+ const timestamp = this.nextTimestamp(owner);
2532
+ const target = this.target(input);
2533
+ const sessionId = await this.createSession(target, this.cwd, `Could not create task "${name}".`);
2534
+ const task = {
2535
+ name,
2536
+ sessionId,
2537
+ cwd: this.cwd,
2538
+ isolation: "shared",
2539
+ status: "open",
2540
+ target,
2541
+ createdAt: timestamp,
2542
+ updatedAt: timestamp,
2543
+ lastSelectedAt: timestamp
2544
+ };
2545
+ const nextOwner = {
2546
+ ...owner,
2547
+ activeTaskName: name,
2548
+ tasks: [...owner.tasks, task]
2549
+ };
2550
+ try {
2551
+ this.commitOwner(nextOwner);
2552
+ } catch (error) {
2553
+ await this.router.detachManagedSession(sessionId).catch(() => void 0);
2554
+ throw error;
2555
+ }
2556
+ this.router.activateManagedSession(sessionId, target, this.cwd);
2557
+ return this.selection(nextOwner, task);
2558
+ });
2559
+ }
2560
+ use(input, name) {
2561
+ return this.withOwnerLock(input, async () => {
2562
+ const owner = await this.ensureOwner(input, false);
2563
+ if (!owner)
2564
+ throw new Error("No named tasks exist in this chat.");
2565
+ const task = this.findTask(owner, name);
2566
+ if (!task)
2567
+ throw new Error(`Task "${name}" was not found.`);
2568
+ this.assertCanLeaveSelected(owner, task.name);
2569
+ if (this.isBusy(task.sessionId)) {
2570
+ throw new Error(`Task "${task.name}" is still running or waiting for permission.`);
2571
+ }
2572
+ if (task.status === "closed" && this.openTaskCount(owner) >= MAX_OPEN_TASKS) {
2573
+ throw new Error("You already have eight open tasks. Close one before reopening another.");
2574
+ }
2575
+ const timestamp = this.nextTimestamp(owner);
2576
+ const loaded = await this.loadTask(task, `Could not load task "${task.name}". The current task was not changed.`);
2577
+ const updatedTask = {
2578
+ ...task,
2579
+ status: "open",
2580
+ updatedAt: timestamp,
2581
+ lastSelectedAt: timestamp
2582
+ };
2583
+ const nextOwner = this.replaceTask(owner, updatedTask, task.name);
2584
+ try {
2585
+ this.commitOwner(nextOwner);
2586
+ } catch (error) {
2587
+ if (loaded) {
2588
+ await this.router.detachManagedSession(task.sessionId).catch(() => void 0);
2589
+ }
2590
+ throw error;
2591
+ }
2592
+ this.router.activateManagedSession(task.sessionId, task.target, task.cwd);
2593
+ return this.selection(nextOwner, updatedTask);
2594
+ });
2595
+ }
2596
+ close(input, name) {
2597
+ return this.withOwnerLock(input, async () => {
2598
+ const owner = await this.ensureOwner(input, false);
2599
+ if (!owner)
2600
+ throw new Error("No named tasks exist in this chat.");
2601
+ const task = this.findTask(owner, name);
2602
+ if (!task)
2603
+ throw new Error(`Task "${name}" was not found.`);
2604
+ if (task.status === "closed") {
2605
+ throw new Error(`Task "${task.name}" is already closed.`);
2606
+ }
2607
+ if (this.isBusy(task.sessionId)) {
2608
+ throw new Error(`Task "${task.name}" is still running or waiting for permission.`);
2609
+ }
2610
+ const wasActive = this.sameName(owner.activeTaskName, task.name);
2611
+ const timestamp = this.nextTimestamp(owner);
2612
+ 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;
2613
+ let replacementLoaded = false;
2614
+ if (replacement) {
2615
+ if (this.isBusy(replacement.sessionId)) {
2616
+ throw new Error(`Task "${replacement.name}" is still running or waiting for permission.`);
2617
+ }
2618
+ replacementLoaded = await this.loadTask(replacement, `Could not load fallback task "${replacement.name}". Task "${task.name}" was not closed.`);
2619
+ }
2620
+ const closedTask = {
2621
+ ...task,
2622
+ status: "closed",
2623
+ updatedAt: timestamp
2624
+ };
2625
+ let nextOwner = this.replaceTask(owner, closedTask, wasActive ? replacement?.name ?? null : owner.activeTaskName);
2626
+ const selectedReplacement = replacement ? {
2627
+ ...replacement,
2628
+ updatedAt: timestamp,
2629
+ lastSelectedAt: timestamp
2630
+ } : void 0;
2631
+ if (selectedReplacement) {
2632
+ nextOwner = this.replaceTask(nextOwner, selectedReplacement, selectedReplacement.name);
2633
+ }
2634
+ try {
2635
+ this.commitOwner(nextOwner);
2636
+ } catch (error) {
2637
+ if (replacement && replacementLoaded) {
2638
+ await this.router.detachManagedSession(replacement.sessionId).catch(() => void 0);
2639
+ }
2640
+ throw error;
2641
+ }
2642
+ if (replacement) {
2643
+ this.router.activateManagedSession(replacement.sessionId, replacement.target, replacement.cwd);
2644
+ }
2645
+ try {
2646
+ await this.router.detachManagedSession(task.sessionId);
2647
+ } catch (error) {
2648
+ this.commitOwner(owner);
2649
+ this.router.forgetManagedSession(task.sessionId);
2650
+ await this.loadTask(task, `Task "${task.name}" could not be restored after close failed.`);
2651
+ if (wasActive) {
2652
+ this.router.activateManagedSession(task.sessionId, task.target, task.cwd);
2653
+ }
2654
+ throw new Error(`Failed to close task "${task.name}".`, {
2655
+ cause: error
2656
+ });
2657
+ }
2658
+ const activeTask = nextOwner.activeTaskName ? this.findTask(nextOwner, nextOwner.activeTaskName) : void 0;
2659
+ return {
2660
+ closed: this.view(nextOwner, closedTask),
2661
+ ...activeTask ? { active: this.view(nextOwner, activeTask) } : {}
2662
+ };
2663
+ });
2664
+ }
2665
+ reset(input) {
2666
+ return this.withOwnerLock(input, async () => {
2667
+ const owner = await this.ensureOwner(input, false);
2668
+ if (!owner?.activeTaskName)
2669
+ return void 0;
2670
+ const task = this.findTask(owner, owner.activeTaskName);
2671
+ if (!task || task.status !== "open")
2672
+ return void 0;
2673
+ const timestamp = this.nextTimestamp(owner);
2674
+ const sessionId = await this.createSession(task.target, task.cwd, `Could not reset task "${task.name}".`);
2675
+ const updatedTask = {
2676
+ ...task,
2677
+ sessionId,
2678
+ updatedAt: timestamp,
2679
+ lastSelectedAt: timestamp
2680
+ };
2681
+ const nextOwner = this.replaceTask(owner, updatedTask, task.name);
2682
+ try {
2683
+ this.commitOwner(nextOwner);
2684
+ } catch (error) {
2685
+ await this.router.detachManagedSession(sessionId).catch(() => void 0);
2686
+ throw error;
2687
+ }
2688
+ this.router.activateManagedSession(sessionId, updatedTask.target, updatedTask.cwd);
2689
+ this.router.forgetManagedSession(task.sessionId);
2690
+ return {
2691
+ name: task.name,
2692
+ previousSessionId: task.sessionId,
2693
+ sessionId
2694
+ };
2695
+ });
2696
+ }
2697
+ async ensureOwner(input, createDefault) {
2698
+ const existing = this.getOwner(input);
2699
+ if (existing)
2700
+ return existing;
2701
+ const sessionId = this.router.getSession(this.channelName, input.senderId, input.chatId);
2702
+ const routedTarget = sessionId ? this.router.getTarget(sessionId) : void 0;
2703
+ if (sessionId && routedTarget && (routedTarget.channelName !== this.channelName || routedTarget.chatId !== input.chatId || routedTarget.senderId !== input.senderId)) {
2704
+ this.adoptForeignLegacyRoute(sessionId, routedTarget);
2705
+ }
2706
+ if (sessionId && routedTarget?.channelName === this.channelName && routedTarget.chatId === input.chatId && routedTarget.senderId === input.senderId) {
2707
+ const routedCwd = this.router.getSessionCwd(sessionId);
2708
+ if (routedCwd !== void 0 && !this.isCurrentCwd(routedCwd)) {
2709
+ await this.router.detachManagedSession(sessionId).catch(() => void 0);
2710
+ } else {
2711
+ const timestamp2 = this.nextTimestamp();
2712
+ const task2 = {
2713
+ name: "default",
2714
+ sessionId,
2715
+ cwd: routedCwd ?? this.cwd,
2716
+ isolation: "shared",
2717
+ status: "open",
2718
+ target: routedTarget,
2719
+ createdAt: timestamp2,
2720
+ updatedAt: timestamp2,
2721
+ lastSelectedAt: timestamp2
2722
+ };
2723
+ const adopted = this.createOwner(input, task2.name, [task2]);
2724
+ this.commitOwner(adopted);
2725
+ return adopted;
2726
+ }
2727
+ }
2728
+ if (!createDefault)
2729
+ return void 0;
2730
+ const target = this.target(input);
2731
+ const timestamp = this.nextTimestamp();
2732
+ const createdSessionId = await this.createSession(target, this.cwd, "Could not create the default task.");
2733
+ const task = {
2734
+ name: "default",
2735
+ sessionId: createdSessionId,
2736
+ cwd: this.cwd,
2737
+ isolation: "shared",
2738
+ status: "open",
2739
+ target,
2740
+ createdAt: timestamp,
2741
+ updatedAt: timestamp,
2742
+ lastSelectedAt: timestamp
2743
+ };
2744
+ const created = this.createOwner(input, task.name, [task]);
2745
+ try {
2746
+ this.commitOwner(created);
2747
+ } catch (error) {
2748
+ await this.router.detachManagedSession(createdSessionId).catch(() => void 0);
2749
+ throw error;
2750
+ }
2751
+ this.router.activateManagedSession(createdSessionId, target, this.cwd);
2752
+ return created;
2753
+ }
2754
+ async resolveLocked(input, expectedSessionId, reserve) {
2755
+ const owner = await this.ensureOwner(input, true);
2756
+ if (!owner?.activeTaskName)
2757
+ return void 0;
2758
+ const task = this.findTask(owner, owner.activeTaskName);
2759
+ if (!task || task.status !== "open") {
2760
+ throw new Error("The selected Channel task is unavailable.");
2761
+ }
2762
+ if (expectedSessionId !== void 0 && task.sessionId !== expectedSessionId) {
2763
+ return void 0;
2764
+ }
2765
+ const release = reserve?.(task.sessionId);
2766
+ try {
2767
+ await this.loadTask(task, `Could not load task "${task.name}". No replacement session was created.`);
2768
+ this.router.activateManagedSession(task.sessionId, task.target, task.cwd);
2769
+ return task.sessionId;
2770
+ } catch (error) {
2771
+ release?.();
2772
+ throw error;
2773
+ }
2774
+ }
2775
+ adoptForeignLegacyRoute(sessionId, target) {
2776
+ const cwd = this.router.getSessionCwd(sessionId);
2777
+ if (target.channelName !== this.channelName || cwd === void 0 || !this.isCurrentCwd(cwd)) {
2778
+ this.router.forgetManagedSession(sessionId);
2779
+ return;
2780
+ }
2781
+ const existing = this.getOwner(target);
2782
+ if (existing) {
2783
+ if (existing.tasks.some((task2) => task2.sessionId === sessionId))
2784
+ return;
2785
+ this.router.forgetManagedSession(sessionId);
2786
+ return;
2787
+ }
2788
+ const timestamp = this.nextTimestamp();
2789
+ const task = {
2790
+ name: "default",
2791
+ sessionId,
2792
+ cwd,
2793
+ isolation: "shared",
2794
+ status: "open",
2795
+ target,
2796
+ createdAt: timestamp,
2797
+ updatedAt: timestamp,
2798
+ lastSelectedAt: timestamp
2799
+ };
2800
+ this.commitOwner(this.createOwner(target, task.name, [task]));
2801
+ }
2802
+ assertCanLeaveSelected(owner, nextTaskName) {
2803
+ if (!owner.activeTaskName || nextTaskName && this.sameName(owner.activeTaskName, nextTaskName)) {
2804
+ return;
2805
+ }
2806
+ const selected = this.findTask(owner, owner.activeTaskName);
2807
+ if (selected && this.isBusy(selected.sessionId)) {
2808
+ throw new Error(`Task "${selected.name}" is still running or waiting for permission.`);
2809
+ }
2810
+ }
2811
+ async loadTask(task, failureMessage) {
2812
+ try {
2813
+ return (await this.router.loadManagedSession(task.sessionId, task.target, task.cwd)).loaded;
2814
+ } catch (error) {
2815
+ throw new Error(failureMessage, { cause: error });
2816
+ }
2817
+ }
2818
+ async createSession(target, cwd, failureMessage) {
2819
+ try {
2820
+ return await this.router.createManagedSession(target, cwd);
2821
+ } catch (error) {
2822
+ throw new Error(failureMessage, { cause: error });
2823
+ }
2824
+ }
2825
+ withOwnerLock(input, operation) {
2826
+ const key = JSON.stringify([
2827
+ this.channelName,
2828
+ input.chatId,
2829
+ input.senderId
2830
+ ]);
2831
+ const previous = this.ownerOperations.get(key) ?? Promise.resolve();
2832
+ const result = previous.catch(() => void 0).then(operation);
2833
+ const tail = result.then(() => void 0, () => void 0);
2834
+ this.ownerOperations.set(key, tail);
2835
+ return result.finally(() => {
2836
+ if (this.ownerOperations.get(key) === tail) {
2837
+ this.ownerOperations.delete(key);
2838
+ }
2839
+ });
2840
+ }
2841
+ getOwner(input) {
2842
+ const owner = this.registry.owners.find((candidate) => candidate.channelName === this.channelName && candidate.chatId === input.chatId && candidate.senderId === input.senderId);
2843
+ return owner ? this.cloneOwner(owner) : void 0;
2844
+ }
2845
+ createOwner(input, activeTaskName, tasks) {
2846
+ return {
2847
+ channelName: this.channelName,
2848
+ chatId: input.chatId,
2849
+ senderId: input.senderId,
2850
+ activeTaskName,
2851
+ tasks
2852
+ };
2853
+ }
2854
+ target(input) {
2855
+ return {
2856
+ channelName: this.channelName,
2857
+ senderId: input.senderId,
2858
+ chatId: input.chatId,
2859
+ ...input.threadId !== void 0 ? { threadId: input.threadId } : {},
2860
+ ...input.isGroup !== void 0 ? { isGroup: input.isGroup } : {}
2861
+ };
2862
+ }
2863
+ findTask(owner, name) {
2864
+ const normalized = this.normalizeName(name);
2865
+ return owner.tasks.find((task) => this.normalizeName(task.name) === normalized);
2866
+ }
2867
+ replaceTask(owner, task, activeTaskName) {
2868
+ return {
2869
+ ...owner,
2870
+ activeTaskName,
2871
+ tasks: owner.tasks.map((candidate) => this.sameName(candidate.name, task.name) ? task : candidate)
2872
+ };
2873
+ }
2874
+ view(owner, task) {
2875
+ return {
2876
+ name: task.name,
2877
+ status: task.status,
2878
+ isolation: task.isolation,
2879
+ active: task.status === "open" && this.sameName(owner.activeTaskName, task.name)
2880
+ };
2881
+ }
2882
+ selection(owner, task) {
2883
+ return { ...this.view(owner, task), sessionId: task.sessionId };
2884
+ }
2885
+ openTaskCount(owner) {
2886
+ return owner.tasks.filter((task) => task.status === "open").length;
2887
+ }
2888
+ nextTimestamp(owner) {
2889
+ const now = this.now();
2890
+ if (!Number.isSafeInteger(now) || now < 0) {
2891
+ throw new Error("Invalid named-session clock value.");
2892
+ }
2893
+ let timestamp = now;
2894
+ for (const task of owner?.tasks ?? []) {
2895
+ timestamp = Math.max(timestamp, task.createdAt + 1, task.updatedAt + 1, task.lastSelectedAt + 1);
2896
+ }
2897
+ if (!Number.isSafeInteger(timestamp)) {
2898
+ throw new Error("Named-session timestamp limit reached.");
2899
+ }
2900
+ return timestamp;
2901
+ }
2902
+ validateName(name) {
2903
+ if (!TASK_NAME_PATTERN.test(name)) {
2904
+ throw new Error("Task names must be 1-32 ASCII letters, numbers, underscores, or hyphens and start with a letter or number.");
2905
+ }
2906
+ }
2907
+ normalizeName(name) {
2908
+ return name.toLowerCase();
2909
+ }
2910
+ sameName(a, b) {
2911
+ return a !== null && this.normalizeName(a) === this.normalizeName(b);
2912
+ }
2913
+ cloneOwner(owner) {
2914
+ return {
2915
+ ...owner,
2916
+ tasks: owner.tasks.map((task) => ({
2917
+ ...task,
2918
+ target: { ...task.target }
2919
+ }))
2920
+ };
2921
+ }
2922
+ commitOwner(owner) {
2923
+ const owners = this.registry.owners.map((candidate) => candidate.channelName === owner.channelName && candidate.chatId === owner.chatId && candidate.senderId === owner.senderId ? owner : candidate);
2924
+ if (!owners.some((candidate) => candidate.channelName === owner.channelName && candidate.chatId === owner.chatId && candidate.senderId === owner.senderId)) {
2925
+ owners.push(owner);
2926
+ }
2927
+ const next = { version: REGISTRY_VERSION, owners };
2928
+ if (!this.isRegistry(next)) {
2929
+ throw new Error("Invalid named-session registry update.");
2930
+ }
2931
+ this.writeRegistry(next);
2932
+ this.registry = next;
2933
+ }
2934
+ readRegistry() {
2935
+ if (!existsSync4(this.filePath)) {
2936
+ return { version: REGISTRY_VERSION, owners: [] };
2937
+ }
2938
+ let value;
2939
+ try {
2940
+ value = JSON.parse(readFileSync4(this.filePath, "utf8"));
2941
+ } catch (error) {
2942
+ throw new Error(`Failed to read named-session registry: ${this.filePath}`, {
2943
+ cause: error
2944
+ });
2945
+ }
2946
+ if (!this.isRegistry(value)) {
2947
+ if (this.isRegistry(value, false)) {
2948
+ const stalePath = `${this.filePath}.stale-${randomUUID2()}`;
2949
+ try {
2950
+ renameSync4(this.filePath, stalePath);
2951
+ } catch (error) {
2952
+ throw new Error("Failed to archive stale named-session registry.", {
2953
+ cause: error
2954
+ });
2955
+ }
2956
+ process3.stderr.write("[NamedSessionManager] Archived a stale registry after the channel working directory changed.\n");
2957
+ return { version: REGISTRY_VERSION, owners: [] };
2958
+ }
2959
+ throw new Error(`Invalid named-session registry: ${this.filePath}`);
2960
+ }
2961
+ return value;
2962
+ }
2963
+ writeRegistry(registry) {
2964
+ const dir = dirname4(this.filePath);
2965
+ const tempPath = `${this.filePath}.${process3.pid}-${randomUUID2()}.tmp`;
2966
+ try {
2967
+ mkdirSync4(dir, { recursive: true, mode: 448 });
2968
+ try {
2969
+ chmodSync3(dir, 448);
2970
+ } catch {
2971
+ }
2972
+ writeFileSync4(tempPath, JSON.stringify(registry, null, 2), {
2973
+ encoding: "utf8",
2974
+ mode: 384
2975
+ });
2976
+ renameSync4(tempPath, this.filePath);
2977
+ try {
2978
+ chmodSync3(this.filePath, 384);
2979
+ } catch {
2980
+ }
2981
+ } catch (error) {
2982
+ throw new Error("Failed to persist named-session registry.", {
2983
+ cause: error
2984
+ });
2985
+ } finally {
2986
+ try {
2987
+ rmSync2(tempPath, { force: true });
2988
+ } catch {
2989
+ }
2990
+ }
2991
+ }
2992
+ isRegistry(value, requireCurrentCwd = true) {
2993
+ if (!this.isRecord(value) || value["version"] !== REGISTRY_VERSION) {
2994
+ return false;
2995
+ }
2996
+ const owners = value["owners"];
2997
+ if (!Array.isArray(owners))
2998
+ return false;
2999
+ const ownerKeys = /* @__PURE__ */ new Set();
3000
+ const sessionIds = /* @__PURE__ */ new Set();
3001
+ for (const owner of owners) {
3002
+ if (!this.isOwner(owner, ownerKeys, sessionIds, requireCurrentCwd)) {
3003
+ return false;
3004
+ }
3005
+ }
3006
+ return true;
3007
+ }
3008
+ isOwner(value, ownerKeys, sessionIds, requireCurrentCwd) {
3009
+ if (!this.isRecord(value))
3010
+ return false;
3011
+ const channelName = value["channelName"];
3012
+ const chatId = value["chatId"];
3013
+ const senderId = value["senderId"];
3014
+ const activeTaskName = value["activeTaskName"];
3015
+ const tasks = value["tasks"];
3016
+ if (channelName !== this.channelName || typeof chatId !== "string" || typeof senderId !== "string" || activeTaskName !== null && typeof activeTaskName !== "string" || !Array.isArray(tasks)) {
3017
+ return false;
3018
+ }
3019
+ const ownerKey = JSON.stringify([channelName, chatId, senderId]);
3020
+ if (ownerKeys.has(ownerKey))
3021
+ return false;
3022
+ ownerKeys.add(ownerKey);
3023
+ const names = /* @__PURE__ */ new Set();
3024
+ let openTasks = 0;
3025
+ let activeFound = activeTaskName === null;
3026
+ for (const task of tasks) {
3027
+ if (!this.isTask(task, channelName, chatId, senderId, requireCurrentCwd)) {
3028
+ return false;
3029
+ }
3030
+ const normalized = this.normalizeName(task.name);
3031
+ if (names.has(normalized) || sessionIds.has(task.sessionId))
3032
+ return false;
3033
+ names.add(normalized);
3034
+ sessionIds.add(task.sessionId);
3035
+ if (task.status === "open")
3036
+ openTasks++;
3037
+ if (activeTaskName !== null && task.status === "open" && this.sameName(activeTaskName, task.name)) {
3038
+ activeFound = true;
3039
+ }
3040
+ }
3041
+ return openTasks <= MAX_OPEN_TASKS && activeFound;
3042
+ }
3043
+ isTask(value, channelName, chatId, senderId, requireCurrentCwd) {
3044
+ if (!this.isRecord(value) || !this.isRecord(value["target"]))
3045
+ return false;
3046
+ const target = value["target"];
3047
+ 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");
3048
+ }
3049
+ isCurrentCwd(cwd) {
3050
+ return canonicalizeWorkspacePath(cwd) === this.canonicalCwd;
3051
+ }
3052
+ isTimestamp(value) {
3053
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
3054
+ }
3055
+ isRecord(value) {
3056
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3057
+ }
3058
+ };
3059
+
2299
3060
  // packages/channels/base/dist/ChannelLoopScheduler.js
2300
3061
  init_esbuild_shims();
2301
3062
  var MAX_RESULT_PREVIEW_LENGTH = 500;
@@ -2990,6 +3751,7 @@ var ChannelBase = class {
2990
3751
  groupPairingNotified = /* @__PURE__ */ new Map();
2991
3752
  loopController;
2992
3753
  observedContacts;
3754
+ namedSessions;
2993
3755
  observedContactEnvelopes = /* @__PURE__ */ new WeakSet();
2994
3756
  instructedSessions = /* @__PURE__ */ new Set();
2995
3757
  unattendedMemorySessions = /* @__PURE__ */ new Set();
@@ -2998,6 +3760,8 @@ var ChannelBase = class {
2998
3760
  commands = /* @__PURE__ */ new Map();
2999
3761
  /** Per-session promise chain to serialize prompt + send (followup mode). */
3000
3762
  sessionQueues = /* @__PURE__ */ new Map();
3763
+ queuedTurns = /* @__PURE__ */ new Map();
3764
+ namedTurnBindings = /* @__PURE__ */ new WeakMap();
3001
3765
  registerBridgeEvents;
3002
3766
  bridgeRecovery;
3003
3767
  /**
@@ -3074,7 +3838,16 @@ var ChannelBase = class {
3074
3838
  await this.pushProactive(target, text);
3075
3839
  return;
3076
3840
  }
3077
- await this.sendResponseMessage(target.chatId, text, sessionId);
3841
+ await this.deliverBackgroundReply(target.chatId, text, sessionId);
3842
+ }
3843
+ /**
3844
+ * Fallback delivery of a background response when proactive send is
3845
+ * unavailable. Adapters whose turn replies bypass sendResponseMessage (to
3846
+ * stay out of turn-scoped streaming state, for example) override only this
3847
+ * step instead of re-implementing the whole dispatch flow.
3848
+ */
3849
+ async deliverBackgroundReply(chatId, text, sessionId) {
3850
+ await this.sendResponseMessage(chatId, text, sessionId);
3078
3851
  }
3079
3852
  async dispatchPermissionRequest(event) {
3080
3853
  const target = this.permissionTargetForEvent(event);
@@ -3295,6 +4068,21 @@ var ChannelBase = class {
3295
4068
  this.dmGate = new DmGate(config.dmPolicy);
3296
4069
  this.gate = new SenderGate(config.senderPolicy, config.allowedUsers, pairingStore);
3297
4070
  this.router = options?.router || new SessionRouter(bridge, config.cwd, config.sessionScope);
4071
+ if (config.multiSession) {
4072
+ if (config.sessionScope !== "user") {
4073
+ throw new Error(`Channel "${name}" requires sessionScope "user" when multiSession is enabled.`);
4074
+ }
4075
+ if (!options?.stateDir) {
4076
+ throw new Error(`Channel "${name}" multiSession is available only in daemon-managed mode.`);
4077
+ }
4078
+ this.namedSessions = new NamedSessionManager({
4079
+ channelName: name,
4080
+ cwd: config.cwd,
4081
+ filePath: join5(options.stateDir, "named-sessions.json"),
4082
+ router: this.router,
4083
+ isBusy: /* @__PURE__ */ __name((sessionId) => this.isNamedSessionBusy(sessionId), "isBusy")
4084
+ });
4085
+ }
3298
4086
  this.registerSharedCommands();
3299
4087
  if (this.loopController) {
3300
4088
  bridge.registerChannelLoopToolHandler?.(this.channelLoopToolHandler);
@@ -3458,7 +4246,7 @@ var ChannelBase = class {
3458
4246
  ensureOutputSegment(sessionId, active) {
3459
4247
  if (!active.owner)
3460
4248
  return void 0;
3461
- const segmentId = active.activeSegmentId ?? randomUUID2();
4249
+ const segmentId = active.activeSegmentId ?? randomUUID3();
3462
4250
  const context = this.outputSegmentContext(sessionId, active, segmentId);
3463
4251
  if (context)
3464
4252
  active.activeSegmentId = segmentId;
@@ -3687,8 +4475,11 @@ var ChannelBase = class {
3687
4475
  imageBase64: void 0,
3688
4476
  imageMimeType: void 0
3689
4477
  };
4478
+ if (this.namedSessions) {
4479
+ this.bindNamedTurn(syntheticEnvelope, sessionId);
4480
+ }
3690
4481
  this.markPreflighted(syntheticEnvelope);
3691
- this.processInbound(syntheticEnvelope).catch((err) => {
4482
+ void this.processPreflightedInbound(syntheticEnvelope).catch((err) => {
3692
4483
  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
4484
  `);
3694
4485
  });
@@ -3784,7 +4575,7 @@ ${promptText}` : promptText;
3784
4575
  doneResolve = resolve3;
3785
4576
  });
3786
4577
  const promptState = {
3787
- runId: randomUUID2(),
4578
+ runId: randomUUID3(),
3788
4579
  cancelled: false,
3789
4580
  done,
3790
4581
  resolve: doneResolve,
@@ -4000,7 +4791,7 @@ ${promptText}` : promptText;
4000
4791
  doneResolve = resolve3;
4001
4792
  });
4002
4793
  const promptState = {
4003
- runId: randomUUID2(),
4794
+ runId: randomUUID3(),
4004
4795
  cancelled: false,
4005
4796
  done,
4006
4797
  resolve: doneResolve,
@@ -4381,7 +5172,7 @@ ${promptText}` : promptText;
4381
5172
  await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can cancel requests in this shared session.");
4382
5173
  return true;
4383
5174
  }
4384
- const activeSessionId = this.findActiveSessionId(envelope);
5175
+ const activeSessionId = this.namedSessions ? await this.findNamedActiveSessionId(envelope) : this.findActiveSessionId(envelope);
4385
5176
  if (!activeSessionId) {
4386
5177
  await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No request is currently running.");
4387
5178
  return true;
@@ -4606,10 +5397,203 @@ ${requestList}`);
4606
5397
  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.");
4607
5398
  return true;
4608
5399
  }
5400
+ namedSessionOwner(envelope) {
5401
+ return {
5402
+ senderId: envelope.senderId,
5403
+ chatId: envelope.chatId,
5404
+ ...envelope.threadId !== void 0 ? { threadId: envelope.threadId } : {},
5405
+ ...envelope.isGroup !== void 0 ? { isGroup: envelope.isGroup } : {}
5406
+ };
5407
+ }
5408
+ async currentSessionId(envelope) {
5409
+ if (this.namedSessions) {
5410
+ return (await this.namedSessions.current(this.namedSessionOwner(envelope)))?.sessionId;
5411
+ }
5412
+ return this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
5413
+ }
5414
+ reserveQueuedTurn(sessionId) {
5415
+ this.queuedTurns.set(sessionId, (this.queuedTurns.get(sessionId) ?? 0) + 1);
5416
+ }
5417
+ releaseQueuedTurn(sessionId) {
5418
+ const remaining = (this.queuedTurns.get(sessionId) ?? 1) - 1;
5419
+ if (remaining === 0) {
5420
+ this.queuedTurns.delete(sessionId);
5421
+ } else {
5422
+ this.queuedTurns.set(sessionId, remaining);
5423
+ }
5424
+ }
5425
+ bindNamedTurn(envelope, sessionId) {
5426
+ const binding = {
5427
+ sessionId,
5428
+ generation: this.sessionGenerations.get(sessionId) ?? 0,
5429
+ claimed: false,
5430
+ released: false
5431
+ };
5432
+ this.namedTurnBindings.set(envelope, binding);
5433
+ this.reserveQueuedTurn(sessionId);
5434
+ return binding;
5435
+ }
5436
+ releaseNamedTurnBinding(binding) {
5437
+ if (binding.sessionId === null || binding.claimed || binding.released) {
5438
+ return;
5439
+ }
5440
+ binding.released = true;
5441
+ this.releaseQueuedTurn(binding.sessionId);
5442
+ }
5443
+ finishNamedTurnBinding(envelope) {
5444
+ const binding = this.namedTurnBindings.get(envelope);
5445
+ if (!binding)
5446
+ return;
5447
+ this.releaseNamedTurnBinding(binding);
5448
+ this.namedTurnBindings.delete(envelope);
5449
+ }
5450
+ bypassesNamedTurnBinding(envelope) {
5451
+ const parsed = this.parseCommand(envelope.text);
5452
+ if (parsed && this.commands.has(parsed.command))
5453
+ return true;
5454
+ const bangText = envelope.text.trimStart();
5455
+ return bangText.startsWith("!") && (envelope.isGroup || this.isSharedSession(envelope));
5456
+ }
5457
+ async prepareNamedTurnBinding(envelope) {
5458
+ const namedSessions = this.namedSessions;
5459
+ if (!namedSessions || this.namedTurnBindings.has(envelope))
5460
+ return true;
5461
+ if (this.bypassesNamedTurnBinding(envelope))
5462
+ return true;
5463
+ try {
5464
+ const sessionId = await namedSessions.resolve(this.namedSessionOwner(envelope), void 0, (resolvedSessionId) => {
5465
+ const binding = this.bindNamedTurn(envelope, resolvedSessionId);
5466
+ return () => this.releaseNamedTurnBinding(binding);
5467
+ });
5468
+ if (!sessionId && !this.namedTurnBindings.has(envelope)) {
5469
+ this.namedTurnBindings.set(envelope, {
5470
+ sessionId: null,
5471
+ generation: 0,
5472
+ claimed: false,
5473
+ released: true
5474
+ });
5475
+ }
5476
+ return true;
5477
+ } catch (error) {
5478
+ this.finishNamedTurnBinding(envelope);
5479
+ await this.sendNamedSessionError(envelope, error);
5480
+ return false;
5481
+ }
5482
+ }
5483
+ isNamedSessionBusy(sessionId) {
5484
+ if ((this.queuedTurns.get(sessionId) ?? 0) > 0)
5485
+ return true;
5486
+ if (this.activePrompts.has(sessionId))
5487
+ return true;
5488
+ for (const permission of this.pendingPermissions.values()) {
5489
+ if (permission.sessionId === sessionId)
5490
+ return true;
5491
+ }
5492
+ try {
5493
+ return this.bridge.listSessions?.().some((session) => session.sessionId === sessionId && session.hasActivePrompt) ?? false;
5494
+ } catch {
5495
+ return true;
5496
+ }
5497
+ }
5498
+ async sendNamedSessionError(envelope, error) {
5499
+ const message = error instanceof Error ? sanitizeDisplayText(error.message, 500) : "Named-session operation failed.";
5500
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, message || "Named-session operation failed.");
5501
+ }
5502
+ async handleNamedSessionsCommand(envelope, args) {
5503
+ const namedSessions = this.namedSessions;
5504
+ if (!namedSessions)
5505
+ return false;
5506
+ const normalized = args.trim().toLowerCase();
5507
+ if (normalized !== "" && normalized !== "all") {
5508
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Usage: /sessions [all]");
5509
+ return true;
5510
+ }
5511
+ try {
5512
+ const tasks = await namedSessions.list(this.namedSessionOwner(envelope), normalized === "all");
5513
+ if (tasks.length === 0) {
5514
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, normalized === "all" ? "No named tasks." : "No open named tasks.");
5515
+ return true;
5516
+ }
5517
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, [
5518
+ normalized === "all" ? "Tasks:" : "Open tasks:",
5519
+ ...tasks.map((task) => `${task.active ? "*" : "-"} ${task.name} (${task.status}, ${task.isolation})`)
5520
+ ].join("\n"));
5521
+ } catch (error) {
5522
+ await this.sendNamedSessionError(envelope, error);
5523
+ }
5524
+ return true;
5525
+ }
5526
+ async handleNamedSessionCommand(envelope, args) {
5527
+ const namedSessions = this.namedSessions;
5528
+ if (!namedSessions)
5529
+ return false;
5530
+ const parts = args.trim().split(/\s+/u).filter(Boolean);
5531
+ const subcommand = parts.shift()?.toLowerCase() ?? "";
5532
+ const owner = this.namedSessionOwner(envelope);
5533
+ try {
5534
+ switch (subcommand) {
5535
+ case "current": {
5536
+ if (parts.length > 0)
5537
+ break;
5538
+ const current = await namedSessions.current(owner);
5539
+ 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>.");
5540
+ return true;
5541
+ }
5542
+ case "new": {
5543
+ if (parts.includes("--worktree")) {
5544
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Worktree tasks are not available in Part 2 yet. Create a shared task with /session new <name>.");
5545
+ return true;
5546
+ }
5547
+ if (parts.length !== 1)
5548
+ break;
5549
+ const created = await namedSessions.create(owner, parts[0]);
5550
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Created and selected task "${created.name}" (shared workspace).`);
5551
+ return true;
5552
+ }
5553
+ case "use": {
5554
+ if (parts.length !== 1)
5555
+ break;
5556
+ const selected = await namedSessions.use(owner, parts[0]);
5557
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Selected task "${selected.name}" (${selected.isolation} workspace).`);
5558
+ return true;
5559
+ }
5560
+ case "close": {
5561
+ if (parts.length !== 1)
5562
+ break;
5563
+ const result = await namedSessions.close(owner, parts[0]);
5564
+ 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.`);
5565
+ return true;
5566
+ }
5567
+ case "cancel":
5568
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Named task cancellation is not available in Part 2 yet. Wait for the selected task to finish before switching; Telegram users can use /cancel.");
5569
+ return true;
5570
+ default:
5571
+ break;
5572
+ }
5573
+ } catch (error) {
5574
+ await this.sendNamedSessionError(envelope, error);
5575
+ return true;
5576
+ }
5577
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Usage: /session current | /session new <name> | /session use <name> | /session close <name> | /session cancel [<name>]");
5578
+ return true;
5579
+ }
4609
5580
  /** Register shared slash commands. Called from constructor. */
4610
5581
  registerSharedCommands() {
4611
5582
  const doClear = /* @__PURE__ */ __name(async (envelope) => {
4612
- const removedIds = this.router.removeSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
5583
+ let resetTaskName;
5584
+ let removedIds;
5585
+ if (this.namedSessions) {
5586
+ try {
5587
+ const reset = await this.namedSessions.reset(this.namedSessionOwner(envelope));
5588
+ resetTaskName = reset?.name;
5589
+ removedIds = reset ? [reset.previousSessionId] : [];
5590
+ } catch (error) {
5591
+ await this.sendNamedSessionError(envelope, error);
5592
+ return;
5593
+ }
5594
+ } else {
5595
+ removedIds = this.router.removeSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
5596
+ }
4613
5597
  this.clearPendingGroupHistory(envelope);
4614
5598
  if (removedIds.length > 0) {
4615
5599
  for (const id of removedIds) {
@@ -4655,7 +5639,7 @@ ${requestList}`);
4655
5639
  }
4656
5640
  this.discardRetiredSession(this.bridge, id, "cleared");
4657
5641
  }
4658
- await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Session cleared. The next message starts a fresh conversation.");
5642
+ 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
5643
  } else {
4660
5644
  await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No active session to clear.");
4661
5645
  }
@@ -4675,6 +5659,10 @@ ${requestList}`);
4675
5659
  this.registerCommand("clear", clearHandler);
4676
5660
  this.registerCommand("reset", clearHandler);
4677
5661
  this.registerCommand("new", clearHandler);
5662
+ if (this.namedSessions) {
5663
+ this.registerCommand("sessions", (envelope, args) => this.handleNamedSessionsCommand(envelope, args));
5664
+ this.registerCommand("session", (envelope, args) => this.handleNamedSessionCommand(envelope, args));
5665
+ }
4678
5666
  this.registerCommand("approve", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "approve"));
4679
5667
  this.registerCommand("approve-always", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "approve-always"));
4680
5668
  this.registerCommand("deny", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "deny"));
@@ -4683,7 +5671,7 @@ ${requestList}`);
4683
5671
  await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can view this shared session.");
4684
5672
  return true;
4685
5673
  }
4686
- const active = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
5674
+ const active = this.namedSessions ? Boolean(await this.currentSessionId(envelope)) : this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
4687
5675
  const scopeNote = this.config.sessionScope === "single" ? " (shared channel-wide)" : this.isSharedSession(envelope) ? envelope.isGroup ? " (shared by this group)" : "" : envelope.isGroup ? " (private to you)" : "";
4688
5676
  await this.sendThreadMessage(envelope.chatId, envelope.threadId, [
4689
5677
  `Channel: ${this.name}`,
@@ -4708,7 +5696,11 @@ ${requestList}`);
4708
5696
  "/status \u2014 Show session info",
4709
5697
  "/approve [request-id] \u2014 Approve a pending permission request",
4710
5698
  "/approve-always [request-id] \u2014 Always approve a pending permission request",
4711
- "/deny [request-id] \u2014 Deny a pending permission request"
5699
+ "/deny [request-id] \u2014 Deny a pending permission request",
5700
+ ...this.namedSessions ? [
5701
+ "/sessions [all] \u2014 List your named tasks",
5702
+ "/session current|new|use|close \u2014 Manage your named tasks"
5703
+ ] : []
4712
5704
  ];
4713
5705
  const sharedCmds = /* @__PURE__ */ new Set([
4714
5706
  "help",
@@ -4722,7 +5714,9 @@ ${requestList}`);
4722
5714
  "channel-memory",
4723
5715
  "forget-channel",
4724
5716
  "who",
4725
- "status"
5717
+ "status",
5718
+ "sessions",
5719
+ "session"
4726
5720
  ]);
4727
5721
  const platformCmds = [...this.commands.keys()].filter((c) => !sharedCmds.has(c));
4728
5722
  if (platformCmds.length > 0) {
@@ -4730,7 +5724,7 @@ ${requestList}`);
4730
5724
  lines.push(`/${cmd}`);
4731
5725
  }
4732
5726
  }
4733
- const sessionId = this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
5727
+ const sessionId = await this.currentSessionId(envelope);
4734
5728
  const agentCommands = sessionId ? this.getAgentCommandsForSession(sessionId) : this.bridge.availableCommands;
4735
5729
  if (agentCommands.length > 0) {
4736
5730
  lines.push("", "Agent commands (forwarded to Qwen Code):");
@@ -4747,7 +5741,7 @@ ${requestList}`);
4747
5741
  await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can view this shared session.");
4748
5742
  return true;
4749
5743
  }
4750
- const hasSession = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
5744
+ const hasSession = this.namedSessions ? Boolean(await this.currentSessionId(envelope)) : this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
4751
5745
  const policy = this.config.senderPolicy;
4752
5746
  const lines = [
4753
5747
  `Session: ${hasSession ? "active" : "none"}`,
@@ -5078,6 +6072,10 @@ ${requestList}`);
5078
6072
  const sessionId = this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
5079
6073
  return sessionId && this.activePrompts.has(sessionId) ? sessionId : void 0;
5080
6074
  }
6075
+ async findNamedActiveSessionId(envelope) {
6076
+ const sessionId = await this.currentSessionId(envelope);
6077
+ return sessionId && this.activePrompts.has(sessionId) ? sessionId : void 0;
6078
+ }
5081
6079
  channelMemoryTarget(envelope) {
5082
6080
  return {
5083
6081
  channelName: this.name,
@@ -5805,9 +6803,12 @@ ${formatted.join("\n")}
5805
6803
  ${CURRENT_MESSAGE_MARKER}
5806
6804
  ${promptText}`;
5807
6805
  }
5808
- preflightInbound(envelope) {
5809
- const groupResult = this.groupGate.check(envelope);
5810
- if (!groupResult.allowed) {
6806
+ preflightInbound(envelope, options = {}) {
6807
+ const groupResult = this.groupGate.check(envelope, {
6808
+ createPairingRequest: !options.deferPairingRequests
6809
+ });
6810
+ const deferredGroupPairing = options.deferPairingRequests === true && groupResult.reason === "pairing_trigger_required" && (envelope.isMentioned || envelope.isReplyToBot);
6811
+ if (!groupResult.allowed && !deferredGroupPairing) {
5811
6812
  if (groupResult.pairing !== void 0) {
5812
6813
  this.logPreflightRejected("group_pairing_required");
5813
6814
  return this.onGroupPairingRequired(envelope.chatId, groupResult.pairing, envelope.threadId).then(() => false).catch((err) => {
@@ -5834,6 +6835,10 @@ ${promptText}`;
5834
6835
  this.markPreflighted(envelope);
5835
6836
  return true;
5836
6837
  }
6838
+ if (options.deferPairingRequests === true && this.config.senderPolicy === "pairing" && !this.gate.isAllowed(envelope.senderId)) {
6839
+ this.markPreflighted(envelope);
6840
+ return true;
6841
+ }
5837
6842
  const result = this.gate.check(envelope.senderId, envelope.senderName);
5838
6843
  if (!result.allowed) {
5839
6844
  if (result.pairing !== void 0) {
@@ -5870,7 +6875,50 @@ ${promptText}`;
5870
6875
  const preflight = this.preflightInbound(envelope);
5871
6876
  if (!(isPromiseLike(preflight) ? await preflight : preflight))
5872
6877
  return;
5873
- await this.processInbound(envelope);
6878
+ await this.processPreflightedInbound(envelope);
6879
+ }
6880
+ async prepareThenHandleInbound(envelope, prepare, preflightOptions = {}) {
6881
+ const preflight = this.preflightInbound(envelope, preflightOptions);
6882
+ if (!(isPromiseLike(preflight) ? await preflight : preflight))
6883
+ return;
6884
+ if (this.namedSessions) {
6885
+ const result = await this.namedSessions.resolveAfterPreparation(this.namedSessionOwner(envelope), prepare, () => !this.bypassesNamedTurnBinding(envelope), (sessionId) => {
6886
+ const binding = this.bindNamedTurn(envelope, sessionId);
6887
+ return () => this.releaseNamedTurnBinding(binding);
6888
+ });
6889
+ if (result.status === "aborted")
6890
+ return;
6891
+ if (result.status === "resolve_error") {
6892
+ this.finishNamedTurnBinding(envelope);
6893
+ await this.sendNamedSessionError(envelope, result.error);
6894
+ return;
6895
+ }
6896
+ if (result.status === "resolved" && !result.sessionId && !this.namedTurnBindings.has(envelope)) {
6897
+ this.namedTurnBindings.set(envelope, {
6898
+ sessionId: null,
6899
+ generation: 0,
6900
+ claimed: false,
6901
+ released: true
6902
+ });
6903
+ }
6904
+ } else if (await prepare() === false) {
6905
+ return;
6906
+ }
6907
+ try {
6908
+ await this.handleInbound(envelope);
6909
+ } finally {
6910
+ this.finishNamedTurnBinding(envelope);
6911
+ }
6912
+ }
6913
+ async processPreflightedInbound(envelope, process5 = () => this.processInbound(envelope)) {
6914
+ if (this.namedSessions && !await this.prepareNamedTurnBinding(envelope)) {
6915
+ return;
6916
+ }
6917
+ try {
6918
+ await process5();
6919
+ } finally {
6920
+ this.finishNamedTurnBinding(envelope);
6921
+ }
5874
6922
  }
5875
6923
  async recordObservedContact(envelope) {
5876
6924
  if (!this.observedContacts)
@@ -5995,7 +7043,45 @@ ${promptText}`;
5995
7043
  }
5996
7044
  }
5997
7045
  await this.waitForBridgeRecovery();
5998
- const sessionId = await this.router.resolve(this.name, envelope.senderId, envelope.chatId, envelope.threadId, this.config.cwd, envelope.isGroup);
7046
+ let sessionId;
7047
+ let namedTurn = this.namedTurnBindings.get(envelope);
7048
+ if (this.namedSessions && namedTurn) {
7049
+ if (namedTurn.sessionId === null) {
7050
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No task was selected when this message was received. Select a task and send it again.");
7051
+ return;
7052
+ }
7053
+ if (this.dropQueuedTurnIfStale(namedTurn.sessionId, namedTurn.generation, envelope)) {
7054
+ return;
7055
+ }
7056
+ try {
7057
+ sessionId = await this.namedSessions.resolve(this.namedSessionOwner(envelope), namedTurn.sessionId);
7058
+ } catch (error) {
7059
+ await this.sendNamedSessionError(envelope, error);
7060
+ return;
7061
+ }
7062
+ if (!sessionId) {
7063
+ process.stderr.write(`[${this.name}] dropped collected turn from ${envelope.senderId} for session ${namedTurn.sessionId}: selected task changed before it ran
7064
+ `);
7065
+ return;
7066
+ }
7067
+ } else if (this.namedSessions) {
7068
+ try {
7069
+ sessionId = await this.namedSessions.resolve(this.namedSessionOwner(envelope), void 0, (resolvedSessionId) => {
7070
+ const binding = this.bindNamedTurn(envelope, resolvedSessionId);
7071
+ namedTurn = binding;
7072
+ return () => this.releaseNamedTurnBinding(binding);
7073
+ });
7074
+ } catch (error) {
7075
+ await this.sendNamedSessionError(envelope, error);
7076
+ return;
7077
+ }
7078
+ if (!sessionId) {
7079
+ await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No task is currently selected. Use /session new <name> or /session use <name>.");
7080
+ return;
7081
+ }
7082
+ } else {
7083
+ sessionId = await this.router.resolve(this.name, envelope.senderId, envelope.chatId, envelope.threadId, this.config.cwd, envelope.isGroup);
7084
+ }
5999
7085
  if (bangText.startsWith("!")) {
6000
7086
  const cmd = bangText.slice(1).trim();
6001
7087
  const bridgeShellCommand = this.bridge.shellCommand;
@@ -6040,12 +7126,19 @@ ${promptText}`;
6040
7126
  }
6041
7127
  let imageBase64 = envelope.imageBase64;
6042
7128
  let imageMimeType = envelope.imageMimeType;
7129
+ const images = [];
7130
+ if (imageBase64 && imageMimeType) {
7131
+ images.push({ data: imageBase64, mimeType: imageMimeType });
7132
+ }
6043
7133
  if (envelope.attachments?.length) {
6044
7134
  const filePaths = [];
6045
7135
  for (const att of envelope.attachments) {
6046
- if (att.type === "image" && att.data && !imageBase64) {
6047
- imageBase64 = att.data;
6048
- imageMimeType = att.mimeType;
7136
+ if (att.type === "image" && att.data) {
7137
+ images.push({ data: att.data, mimeType: att.mimeType });
7138
+ if (!imageBase64) {
7139
+ imageBase64 = att.data;
7140
+ imageMimeType = att.mimeType;
7141
+ }
6049
7142
  } else if (att.filePath) {
6050
7143
  const label = att.type === "file" ? "file" : att.type;
6051
7144
  const name = att.fileName ? ` "${sanitizeQuotedText(att.fileName, 128)}"` : "";
@@ -6131,8 +7224,13 @@ ${promptText}`;
6131
7224
  this.instructedSessions.add(sessionId);
6132
7225
  }
6133
7226
  const prev = this.sessionQueues.get(sessionId) ?? Promise.resolve();
6134
- const generation = this.sessionGenerations.get(sessionId) ?? 0;
7227
+ const generation = namedTurn?.generation ?? this.sessionGenerations.get(sessionId) ?? 0;
6135
7228
  const useBlockStreaming = this.config.blockStreaming === "on";
7229
+ if (namedTurn) {
7230
+ namedTurn.claimed = true;
7231
+ } else {
7232
+ this.reserveQueuedTurn(sessionId);
7233
+ }
6136
7234
  const current = prev.then(async () => {
6137
7235
  clearTimeout(steerWatchdog);
6138
7236
  if (this.dropQueuedTurnIfStale(sessionId, generation, envelope)) {
@@ -6199,7 +7297,7 @@ ${promptToSend}`;
6199
7297
  doneResolve = r;
6200
7298
  });
6201
7299
  const promptState = {
6202
- runId: randomUUID2(),
7300
+ runId: randomUUID3(),
6203
7301
  owner: {
6204
7302
  kind: "channel_user",
6205
7303
  id: envelope.senderId
@@ -6273,6 +7371,7 @@ ${promptToSend}`;
6273
7371
  promptBridge.on("responseBoundary", onResponseBoundary);
6274
7372
  try {
6275
7373
  const response = await promptBridge.prompt(sessionId, promptToSend, {
7374
+ ...images.length > 0 ? { images } : {},
6276
7375
  imageBase64,
6277
7376
  imageMimeType,
6278
7377
  displayText
@@ -6335,7 +7434,10 @@ ${promptToSend}`;
6335
7434
  } finally {
6336
7435
  promptBridge.off("textChunk", onChunk);
6337
7436
  promptBridge.off("responseBoundary", onResponseBoundary);
6338
- streamer?.stop();
7437
+ if (streamer) {
7438
+ streamer.stop();
7439
+ await streamer.drain();
7440
+ }
6339
7441
  const stillCurrent = this.activePrompts.get(sessionId) === promptState;
6340
7442
  if (!promptState.clearEvicted) {
6341
7443
  try {
@@ -6349,39 +7451,15 @@ ${promptToSend}`;
6349
7451
  this.activePrompts.delete(sessionId);
6350
7452
  }
6351
7453
  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
- }
7454
+ this.drainCollectBufferForCurrentPrompt(sessionId, stillCurrent, "prompt completion");
6380
7455
  }
6381
7456
  });
6382
- this.sessionQueues.set(sessionId, current.catch(() => {
7457
+ const tracked = current.finally(() => {
7458
+ this.releaseQueuedTurn(sessionId);
7459
+ });
7460
+ this.sessionQueues.set(sessionId, tracked.catch(() => {
6383
7461
  }));
6384
- await current;
7462
+ await tracked;
6385
7463
  }
6386
7464
  pairingRejectionMessage(rejected) {
6387
7465
  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 +7569,10 @@ var PollingChannelBase = class extends ChannelBase {
6491
7569
  }
6492
7570
  saveCursor() {
6493
7571
  const path4 = this.cursorPath();
6494
- mkdirSync4(join6(getGlobalQwenDir(), "channels"), { recursive: true });
7572
+ mkdirSync5(join6(getGlobalQwenDir(), "channels"), { recursive: true });
6495
7573
  const tmp = `${path4}.tmp`;
6496
- writeFileSync4(tmp, JSON.stringify(this.cursor) + "\n", "utf-8");
6497
- renameSync4(tmp, path4);
7574
+ writeFileSync5(tmp, JSON.stringify(this.cursor) + "\n", "utf-8");
7575
+ renameSync5(tmp, path4);
6498
7576
  }
6499
7577
  startPollLoop() {
6500
7578
  if (this.running)
@@ -6518,7 +7596,7 @@ var PollingChannelBase = class extends ChannelBase {
6518
7596
  } catch (err) {
6519
7597
  this.consecutiveErrors++;
6520
7598
  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}
7599
+ process4.stderr.write(`[Channel:${this.name}] poll error (attempt ${this.consecutiveErrors}), backing off ${backoff}ms: ${err}
6522
7600
  `);
6523
7601
  await this.abortableSleep(backoff);
6524
7602
  continue;
@@ -6528,7 +7606,7 @@ var PollingChannelBase = class extends ChannelBase {
6528
7606
  }
6529
7607
  loadCursorFromDisk() {
6530
7608
  try {
6531
- const raw = readFileSync4(this.cursorPath(), "utf-8").trim();
7609
+ const raw = readFileSync5(this.cursorPath(), "utf-8").trim();
6532
7610
  if (!raw)
6533
7611
  return null;
6534
7612
  return this.validateCursor(JSON.parse(raw));
@@ -6562,6 +7640,48 @@ function getTextContent(content) {
6562
7640
  return getString(content["text"]);
6563
7641
  }
6564
7642
  __name(getTextContent, "getTextContent");
7643
+ var CHANNEL_IMAGE_EXTENSIONS = ["bmp", "gif", "jpeg", "png", "webp"];
7644
+ var CHANNEL_IMAGE_MAX_UPLOAD_BYTES = 8 * 1024 * 1024;
7645
+ var CHANNEL_IMAGE_INLINE_MAX_BASE64_BYTES = 8 * 1024 * 1024;
7646
+ function channelImageName(mimeType, index = 0) {
7647
+ if (!mimeType.startsWith("image/")) {
7648
+ return void 0;
7649
+ }
7650
+ const extension = mimeType.slice("image/".length);
7651
+ if (!CHANNEL_IMAGE_EXTENSIONS.includes(extension)) {
7652
+ return void 0;
7653
+ }
7654
+ return index === 0 ? `image.${extension}` : `image-${index + 1}.${extension}`;
7655
+ }
7656
+ __name(channelImageName, "channelImageName");
7657
+ function decodeChannelImage(data, oversizedReason) {
7658
+ let estimatedBytes = Math.floor(data.length * 3 / 4);
7659
+ if (data.length % 4 === 0) {
7660
+ if (data.endsWith("=="))
7661
+ estimatedBytes -= 2;
7662
+ else if (data.endsWith("="))
7663
+ estimatedBytes -= 1;
7664
+ }
7665
+ if (estimatedBytes > CHANNEL_IMAGE_MAX_UPLOAD_BYTES) {
7666
+ return { skip: oversizedReason };
7667
+ }
7668
+ const bytes = Buffer.from(data, "base64");
7669
+ if (bytes.byteLength === 0) {
7670
+ return { skip: "empty once base64-decoded" };
7671
+ }
7672
+ return { bytes };
7673
+ }
7674
+ __name(decodeChannelImage, "decodeChannelImage");
7675
+ function isDefinitePromptAdmissionRejection(error) {
7676
+ if (!isRecord3(error)) {
7677
+ return false;
7678
+ }
7679
+ if (error["name"] === "DaemonPendingPromptLimitError") {
7680
+ return true;
7681
+ }
7682
+ return error["name"] === "DaemonHttpError" && typeof error["status"] === "number" && error["_daemonTurnError"] !== true;
7683
+ }
7684
+ __name(isDefinitePromptAdmissionRejection, "isDefinitePromptAdmissionRejection");
6565
7685
  function getSessionUpdate(data) {
6566
7686
  if (!isRecord3(data) || !isRecord3(data["update"])) {
6567
7687
  return void 0;
@@ -6633,8 +7753,9 @@ var DaemonChannelBridge = class extends EventEmitter2 {
6633
7753
  availableCommandsBySession = /* @__PURE__ */ new Map();
6634
7754
  turnBarriers = /* @__PURE__ */ new Map();
6635
7755
  channelLoopToolHandlers = [];
7756
+ channelLoopDisabledSessions = /* @__PURE__ */ new Set();
6636
7757
  registeredChannelLoopMcpSessions = /* @__PURE__ */ new Set();
6637
- channelLoopMcpRegistrations = /* @__PURE__ */ new Map();
7758
+ channelLoopMcpOperations = /* @__PURE__ */ new Map();
6638
7759
  channelLoopMcpServer;
6639
7760
  connected = false;
6640
7761
  lifecycleGeneration = 0;
@@ -6694,7 +7815,12 @@ var DaemonChannelBridge = class extends EventEmitter2 {
6694
7815
  await this.rejectStaleSession(session);
6695
7816
  }
6696
7817
  this.attachSession(session, bindingToken);
6697
- await this.registerChannelLoopMcpForSession(session.sessionId);
7818
+ if (options?.enableChannelLoops === false) {
7819
+ this.channelLoopDisabledSessions.add(session.sessionId);
7820
+ void this.reconcileChannelLoopMcpForSession(session.sessionId);
7821
+ } else {
7822
+ await this.reconcileChannelLoopMcpForSession(session.sessionId);
7823
+ }
6698
7824
  return session.sessionId;
6699
7825
  }
6700
7826
  async loadSession(sessionId, cwd, options, bindingToken) {
@@ -6716,7 +7842,12 @@ var DaemonChannelBridge = class extends EventEmitter2 {
6716
7842
  throw new Error(`Daemon returned session ${session.sessionId} while loading ${sessionId}`);
6717
7843
  }
6718
7844
  this.attachSession(session, bindingToken);
6719
- await this.registerChannelLoopMcpForSession(session.sessionId);
7845
+ if (options?.enableChannelLoops === false) {
7846
+ this.channelLoopDisabledSessions.add(session.sessionId);
7847
+ void this.reconcileChannelLoopMcpForSession(session.sessionId);
7848
+ } else {
7849
+ await this.reconcileChannelLoopMcpForSession(session.sessionId);
7850
+ }
6720
7851
  return session.sessionId;
6721
7852
  }
6722
7853
  registerChannelLoopToolHandler(handler) {
@@ -6729,7 +7860,9 @@ var DaemonChannelBridge = class extends EventEmitter2 {
6729
7860
  cancel: /* @__PURE__ */ __name((sessionId, id) => this.resolveChannelLoopToolHandler(sessionId).cancel(sessionId, id), "cancel")
6730
7861
  });
6731
7862
  for (const sessionId of this.sessions.keys()) {
6732
- void this.registerChannelLoopMcpForSession(sessionId);
7863
+ if (!this.channelLoopDisabledSessions.has(sessionId)) {
7864
+ void this.reconcileChannelLoopMcpForSession(sessionId);
7865
+ }
6733
7866
  }
6734
7867
  }
6735
7868
  async prompt(sessionId, text, options) {
@@ -6773,29 +7906,101 @@ var DaemonChannelBridge = class extends EventEmitter2 {
6773
7906
  this.on("responseBoundary", clearChunks);
6774
7907
  this.on("sessionDied", onSessionDied);
6775
7908
  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;
7909
+ const uploadedAttachmentIds = [];
7910
+ let rollbackUploadedAttachments = false;
7911
+ const uploadAttachment = session.uploadAttachment?.bind(session);
7912
+ const removeAttachment = session.removeAttachment?.bind(session);
6786
7913
  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
- } : {}
7914
+ const prompt = [];
7915
+ const images = resolvePromptImages(options);
7916
+ if (this.options.sessionAttachments && uploadAttachment && removeAttachment) {
7917
+ try {
7918
+ const uploads = await Promise.allSettled(images.map(async (image, index) => {
7919
+ const name = channelImageName(image.mimeType, index);
7920
+ if (!name) {
7921
+ process.stderr.write(`[DaemonChannelBridge] skipped channel image with unsupported MIME type ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
7922
+ `);
7923
+ return void 0;
7924
+ }
7925
+ const decoded = decodeChannelImage(image.data, "above the daemon attachment size limit");
7926
+ if ("skip" in decoded) {
7927
+ process.stderr.write(`[DaemonChannelBridge] skipped channel image ${decoded.skip} ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
7928
+ `);
7929
+ return void 0;
7930
+ }
7931
+ const attachment = await uploadAttachment(new Blob([decoded.bytes], {
7932
+ type: image.mimeType
7933
+ }), name, image.mimeType, controller.signal);
7934
+ const attachmentId = getString(attachment["attachmentId"]);
7935
+ if (attachmentId)
7936
+ uploadedAttachmentIds.push(attachmentId);
7937
+ return attachment;
7938
+ }));
7939
+ const failure = uploads.find((upload) => upload.status === "rejected");
7940
+ if (failure) {
7941
+ throw failure.reason;
7942
+ }
7943
+ for (const upload of uploads) {
7944
+ if (upload.status === "fulfilled" && upload.value) {
7945
+ prompt.push(upload.value);
7946
+ }
7947
+ }
7948
+ } catch (error) {
7949
+ rollbackUploadedAttachments = true;
7950
+ throw error;
6797
7951
  }
6798
- }, controller.signal);
7952
+ } else {
7953
+ let inlineBase64Bytes = 0;
7954
+ for (const image of images) {
7955
+ const decoded = decodeChannelImage(image.data, "above the inline image budget");
7956
+ if ("skip" in decoded) {
7957
+ process.stderr.write(`[DaemonChannelBridge] skipped channel image ${decoded.skip} ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
7958
+ `);
7959
+ continue;
7960
+ }
7961
+ if (inlineBase64Bytes + image.data.length > CHANNEL_IMAGE_INLINE_MAX_BASE64_BYTES) {
7962
+ 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)}
7963
+ `);
7964
+ continue;
7965
+ }
7966
+ inlineBase64Bytes += image.data.length;
7967
+ prompt.push({
7968
+ type: "image",
7969
+ data: image.data,
7970
+ mimeType: image.mimeType
7971
+ });
7972
+ }
7973
+ }
7974
+ prompt.push({ type: "text", text });
7975
+ if (controller.signal.aborted) {
7976
+ rollbackUploadedAttachments = true;
7977
+ controller.signal.throwIfAborted();
7978
+ }
7979
+ const promptAuthorization = this.options.promptAuthorization;
7980
+ if (controller.signal.aborted) {
7981
+ rollbackUploadedAttachments = true;
7982
+ throw controller.signal.reason;
7983
+ }
7984
+ let result;
7985
+ try {
7986
+ result = await session.prompt({
7987
+ prompt,
7988
+ _meta: {
7989
+ [CHANNEL_PROMPT_META_KEY]: true,
7990
+ ...promptAuthorization ? {
7991
+ [CHANNEL_PROMPT_AUTHORIZATION_META_KEY]: promptAuthorization
7992
+ } : {},
7993
+ ...options?.displayText !== void 0 ? {
7994
+ [CHANNEL_PROMPT_DISPLAY_TEXT_META_KEY]: options.displayText
7995
+ } : {}
7996
+ }
7997
+ }, controller.signal);
7998
+ } catch (error) {
7999
+ if (isDefinitePromptAdmissionRejection(error)) {
8000
+ rollbackUploadedAttachments = true;
8001
+ }
8002
+ throw error;
8003
+ }
6799
8004
  await Promise.race([
6800
8005
  turnBarrier,
6801
8006
  new Promise((resolve3) => setTimeout(resolve3, 0))
@@ -6818,6 +8023,16 @@ var DaemonChannelBridge = class extends EventEmitter2 {
6818
8023
  if (controllers.size === 0 && this.activePromptControllers.get(sessionId) === controllers) {
6819
8024
  this.activePromptControllers.delete(sessionId);
6820
8025
  }
8026
+ if (rollbackUploadedAttachments && removeAttachment) {
8027
+ const removals = await Promise.allSettled(uploadedAttachmentIds.map((attachmentId) => removeAttachment(attachmentId)));
8028
+ removals.forEach((removal, index) => {
8029
+ if (removal.status === "rejected") {
8030
+ const reason = removal.reason instanceof Error ? removal.reason.message : String(removal.reason);
8031
+ process.stderr.write(`[DaemonChannelBridge] failed to remove channel image ${sanitizeLogText(uploadedAttachmentIds[index] ?? "", 128)} for session ${sanitizeLogText(sessionId, 128)} during rollback: ${sanitizeLogText(reason, 256)}
8032
+ `);
8033
+ }
8034
+ });
8035
+ }
6821
8036
  }
6822
8037
  }
6823
8038
  async shellCommand(sessionId, command, signal) {
@@ -7175,6 +8390,7 @@ var DaemonChannelBridge = class extends EventEmitter2 {
7175
8390
  this.eventControllers.delete(sessionId);
7176
8391
  this.sessions.delete(sessionId);
7177
8392
  this.sessionBindingTokens.delete(sessionId);
8393
+ this.channelLoopDisabledSessions.delete(sessionId);
7178
8394
  this.abortActivePrompts(sessionId);
7179
8395
  this.activePrompts.delete(sessionId);
7180
8396
  this.availableCommandsBySession.delete(sessionId);
@@ -7192,45 +8408,45 @@ var DaemonChannelBridge = class extends EventEmitter2 {
7192
8408
  }
7193
8409
  }
7194
8410
  if (unregisterChannelLoopMcp) {
7195
- this.unregisterChannelLoopMcpForSession(sessionId);
8411
+ void this.reconcileChannelLoopMcpForSession(sessionId);
7196
8412
  }
7197
8413
  return session;
7198
8414
  }
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 {
8415
+ reconcileChannelLoopMcpForSession(sessionId) {
8416
+ const previous = this.channelLoopMcpOperations.get(sessionId) ?? Promise.resolve();
8417
+ const operation = previous.catch(() => void 0).then(async () => {
8418
+ const host = this.options.channelLoopMcpHost;
8419
+ const server = this.channelLoopMcpServer;
8420
+ const shouldRegister = host !== void 0 && server !== void 0 && this.sessions.has(sessionId) && !this.channelLoopDisabledSessions.has(sessionId);
8421
+ if (!shouldRegister) {
8422
+ if (host && this.registeredChannelLoopMcpSessions.has(sessionId)) {
8423
+ await host.unregister(sessionId);
8424
+ this.registeredChannelLoopMcpSessions.delete(sessionId);
8425
+ }
8426
+ return;
8427
+ }
8428
+ if (this.registeredChannelLoopMcpSessions.has(sessionId))
8429
+ return;
8430
+ await host.register(sessionId, (message) => server.handleMessage(message, { sessionId }));
8431
+ this.registeredChannelLoopMcpSessions.add(sessionId);
8432
+ if (!this.sessions.has(sessionId) || this.channelLoopDisabledSessions.has(sessionId)) {
7214
8433
  await host.unregister(sessionId);
8434
+ this.registeredChannelLoopMcpSessions.delete(sessionId);
7215
8435
  }
7216
8436
  }).catch((error) => {
7217
8437
  this.lastError = error;
7218
8438
  }).finally(() => {
7219
- if (this.channelLoopMcpRegistrations.get(sessionId) === registration) {
7220
- this.channelLoopMcpRegistrations.delete(sessionId);
8439
+ if (this.channelLoopMcpOperations.get(sessionId) === operation) {
8440
+ this.channelLoopMcpOperations.delete(sessionId);
7221
8441
  }
7222
8442
  });
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
- });
8443
+ this.channelLoopMcpOperations.set(sessionId, operation);
8444
+ return operation;
7232
8445
  }
7233
8446
  resolveChannelLoopToolHandler(sessionId) {
8447
+ if (!this.sessions.has(sessionId) || this.channelLoopDisabledSessions.has(sessionId)) {
8448
+ throw new Error("Channel loop tools are unavailable for this session");
8449
+ }
7234
8450
  const handler = this.channelLoopToolHandlers.find((candidate) => candidate.canHandle?.(sessionId) === true || this.channelLoopToolHandlers.length === 1 && !candidate.canHandle);
7235
8451
  if (handler)
7236
8452
  return handler;