@qwen-code/qwen-code 0.21.12 → 0.21.14-preview.0

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 (353) hide show
  1. package/bundled/qc-helper/docs/configuration/settings.md +13 -5
  2. package/bundled/qc-helper/docs/features/code-review.md +18 -13
  3. package/bundled/qc-helper/docs/features/commands.md +120 -8
  4. package/bundled/qc-helper/docs/features/sub-agents.md +2 -2
  5. package/bundled/qc-helper/docs/qwen-serve-deploy-local.md +1 -1
  6. package/bundled/qc-helper/docs/qwen-serve.md +20 -3
  7. package/bundled/review/SKILL.md +130 -82
  8. package/chunks/{MaxSizedBox-ZSJJ2D5S.js → MaxSizedBox-3PZITE2Y.js} +46 -45
  9. package/chunks/{StandaloneSessionPicker-WHUHQXSA.js → StandaloneSessionPicker-2C34PNGO.js} +68 -67
  10. package/chunks/{acp-startup-profiler-SVPUHLXV.js → acp-startup-profiler-HUYGO57H.js} +2 -2
  11. package/chunks/{acpAgent-7JOAESF5.js → acpAgent-3J4NHAYT.js} +1098 -366
  12. package/chunks/{agent-E6V3VXQR.js → agent-VFX4RPP5.js} +42 -41
  13. package/chunks/agent-headless-6WCF4J6N.js +88 -0
  14. package/chunks/{anthropicContentGenerator-YDT4EOKO.js → anthropicContentGenerator-M7XRJMSK.js} +8 -8
  15. package/chunks/{artifact-tool-4GCQQDZI.js → artifact-tool-GEXTOPVO.js} +2 -2
  16. package/chunks/{askUserQuestion-HZJJLR3Z.js → askUserQuestion-B33XRSB7.js} +2 -2
  17. package/chunks/{bridge-NPBV53UA.js → bridge-XR5ZSR7U.js} +46 -45
  18. package/chunks/{ca-4CQ6MYNQ.js → ca-SLZJD6QH.js} +8 -0
  19. package/chunks/{channel-management-service-OADNGQM3.js → channel-management-service-X6LCQUAD.js} +1 -1
  20. package/chunks/{channel-settings-store-4ODLJ4X2.js → channel-settings-store-Z7BUEUJE.js} +50 -49
  21. package/chunks/{channel-worker-group-IHBIHQ45.js → channel-worker-group-TJ35JDPE.js} +4 -4
  22. package/chunks/{channel-worker-manager-GEHYJ5AE.js → channel-worker-manager-G5LKWGOG.js} +4 -4
  23. package/chunks/{channel-worker-supervisor-ZJIIWM4I.js → channel-worker-supervisor-OEVWBHGT.js} +2 -2
  24. package/chunks/{chunk-47BGDLJG.js → chunk-25GHLMQQ.js} +42 -20
  25. package/chunks/{chunk-2ZWH53I4.js → chunk-2B2BF7P7.js} +3 -3
  26. package/chunks/chunk-2C6HJMG3.js +757 -0
  27. package/chunks/{chunk-WM3LUQLO.js → chunk-2JXDFSVE.js} +6 -6
  28. package/chunks/{chunk-OYWVIXHT.js → chunk-2M55OBEB.js} +1 -1
  29. package/chunks/{chunk-B4VN3VDH.js → chunk-2OA3JMRD.js} +5 -5
  30. package/chunks/{chunk-GZDWTBT3.js → chunk-3EZLLVDF.js} +2 -2
  31. package/chunks/chunk-3PWCMXIR.js +9169 -0
  32. package/chunks/{chunk-XAFIL4BD.js → chunk-4KQ2TI2K.js} +5 -5
  33. package/chunks/{chunk-OPVF453Q.js → chunk-4V4DFJBS.js} +3 -3
  34. package/chunks/{chunk-DPTMKFIH.js → chunk-4ZMS3JCR.js} +43 -12
  35. package/chunks/{chunk-6JEIRPLN.js → chunk-55PIFSP2.js} +2 -2
  36. package/chunks/{chunk-V6OBVAST.js → chunk-57KOJ2KZ.js} +1 -1
  37. package/chunks/{chunk-QLTATTPS.js → chunk-5CVO7YES.js} +1 -1
  38. package/chunks/{chunk-OK47XBYH.js → chunk-5IYS4WWR.js} +1 -1
  39. package/chunks/chunk-5MK45KZ3.js +492 -0
  40. package/chunks/{chunk-OWXFBIB6.js → chunk-6DFPR7CS.js} +1 -1
  41. package/chunks/{chunk-YIGGWOJE.js → chunk-6HY6IF3Z.js} +1 -1
  42. package/chunks/{chunk-XDRXOSAD.js → chunk-6LAPGPVA.js} +7 -5
  43. package/chunks/{chunk-Q4ZPYNIZ.js → chunk-6PDOHRN6.js} +4 -4
  44. package/chunks/{chunk-OWGRUU7F.js → chunk-6QFDQMQX.js} +1 -1
  45. package/chunks/{chunk-MUNA4QDC.js → chunk-6VDPDHIP.js} +29 -8
  46. package/chunks/{chunk-MDNUCDKZ.js → chunk-73G3Z7QS.js} +3 -3
  47. package/chunks/{chunk-VIFA5VJD.js → chunk-7OCSX7FC.js} +1 -1
  48. package/chunks/{chunk-D2RNVVH6.js → chunk-AKBOLMUF.js} +96 -18
  49. package/chunks/{chunk-S6TCTH4Z.js → chunk-AQBHJ2IG.js} +12 -12
  50. package/chunks/{chunk-WCEI6J56.js → chunk-BOJNAPUE.js} +2 -2
  51. package/chunks/{chunk-DAV4QFD5.js → chunk-BQWNIPTE.js} +44 -3
  52. package/chunks/{chunk-HOSVOUDK.js → chunk-BUQDLC2G.js} +3 -0
  53. package/chunks/{chunk-LUISTZXI.js → chunk-CH5OFRV7.js} +2 -2
  54. package/chunks/{chunk-G6MUFRNI.js → chunk-CHHABXPE.js} +1 -1
  55. package/chunks/{chunk-4AOVQPJU.js → chunk-CPHEPGAO.js} +1 -1
  56. package/chunks/{chunk-MS7GNABG.js → chunk-CXJHQVEK.js} +8 -0
  57. package/chunks/{chunk-K5IDV3PC.js → chunk-D34RDOLP.js} +12 -2
  58. package/chunks/{chunk-3GM5DLBI.js → chunk-DGJ7O55J.js} +3 -3
  59. package/chunks/{chunk-7KJPO7YH.js → chunk-DRKYT2TR.js} +2 -2
  60. package/chunks/{chunk-PZW46OTB.js → chunk-DVR7XKO5.js} +2 -2
  61. package/chunks/{chunk-CQ6IPDQW.js → chunk-DZCV3ZYU.js} +3 -3
  62. package/chunks/{chunk-ULNWG62W.js → chunk-E3DYKPYZ.js} +3 -3
  63. package/chunks/{chunk-YCHNAKC5.js → chunk-E5RS7LF4.js} +44 -13
  64. package/chunks/{chunk-4NFY2S7N.js → chunk-E7REAOTN.js} +15 -0
  65. package/chunks/chunk-EOCQCNV4.js +3887 -0
  66. package/chunks/{chunk-D7QGE3RT.js → chunk-FAAUPSY2.js} +1 -1
  67. package/chunks/{chunk-2IVYL3IF.js → chunk-FCTLOVYW.js} +1 -1
  68. package/chunks/{chunk-QPBFRWCP.js → chunk-FS5NGKWC.js} +3 -3
  69. package/chunks/{chunk-RQJA2YYO.js → chunk-G6FFFICU.js} +4 -4
  70. package/chunks/{chunk-36XKGB7E.js → chunk-GV3OIADZ.js} +3 -3
  71. package/chunks/{chunk-3AEKDXKT.js → chunk-H4A72DE5.js} +1 -1
  72. package/chunks/{chunk-IH4Z54RJ.js → chunk-HGPMOXLZ.js} +5 -5
  73. package/chunks/{chunk-DG4IQ6JG.js → chunk-I3N2Z25H.js} +3 -3
  74. package/chunks/{chunk-WI6GIM2M.js → chunk-I7NTHW4C.js} +2 -2
  75. package/chunks/{chunk-ZBB5EZZV.js → chunk-IA2K2HJE.js} +2 -2
  76. package/chunks/{chunk-IRSLAS4M.js → chunk-IOTPI425.js} +19 -11
  77. package/chunks/{chunk-ROIJYS5K.js → chunk-ISRHAMNL.js} +3 -3
  78. package/chunks/{chunk-CGM3E75C.js → chunk-J63PKMYA.js} +3 -3
  79. package/chunks/{chunk-PTSEETCO.js → chunk-J6YS7Z25.js} +107 -13
  80. package/chunks/{chunk-ZQUY3ZJU.js → chunk-K3CECR5C.js} +4 -4
  81. package/chunks/{chunk-QBJYSIQ4.js → chunk-K3LPAED5.js} +53 -12
  82. package/chunks/{chunk-FRHQQC2Z.js → chunk-KBXKR5R2.js} +1 -1
  83. package/chunks/{chunk-4KM2SYK5.js → chunk-KC4WMLZO.js} +1491 -10579
  84. package/chunks/{chunk-IX4PWUDU.js → chunk-KSGWAYNE.js} +14350 -8679
  85. package/chunks/{chunk-CPFFU46D.js → chunk-KV27IEHM.js} +1 -1
  86. package/chunks/{chunk-OR64CU46.js → chunk-L6CCLB3G.js} +1 -1
  87. package/chunks/{chunk-M6NDLY2T.js → chunk-LD5VIJ7S.js} +1 -1
  88. package/chunks/{chunk-GXTMEATA.js → chunk-LLR424CQ.js} +18 -15
  89. package/chunks/{chunk-RDVNSJQU.js → chunk-LRDB2OLB.js} +3 -3
  90. package/chunks/{chunk-LSRI3ND3.js → chunk-MA2HEDVP.js} +1 -1
  91. package/chunks/{chunk-DFVXU5YM.js → chunk-MCG53LWC.js} +1 -1
  92. package/chunks/{chunk-YBVVOSOH.js → chunk-MIPFDQAF.js} +1 -1
  93. package/chunks/{chunk-KRXPRVGL.js → chunk-MLXTMF7H.js} +0 -1
  94. package/chunks/{chunk-BSR5AXZR.js → chunk-NRPXRNN4.js} +1 -1
  95. package/chunks/{chunk-X5SLJOMF.js → chunk-NV6LW6FT.js} +4 -4
  96. package/chunks/{chunk-YILJKX2T.js → chunk-NVRMCWTP.js} +10 -6
  97. package/chunks/chunk-NYWWQ437.js +24 -0
  98. package/chunks/{chunk-AKBFSOC5.js → chunk-OBJYN4MX.js} +2 -2
  99. package/chunks/{chunk-NX4A23LY.js → chunk-OJOP26ET.js} +3 -3
  100. package/chunks/{chunk-3GDBZJD6.js → chunk-OLZSWUZR.js} +7 -7
  101. package/chunks/{chunk-K34GJQOZ.js → chunk-OO4FKHLO.js} +4 -4
  102. package/chunks/{chunk-BSSLSQEW.js → chunk-OSAFWBNR.js} +3 -3
  103. package/chunks/{chunk-TMFWI4RC.js → chunk-OVY67CYC.js} +1 -1
  104. package/chunks/{chunk-6ZKSO6K7.js → chunk-P425IVUK.js} +7 -7
  105. package/chunks/{chunk-4XA4QIUA.js → chunk-PMS7O6SA.js} +1 -1
  106. package/chunks/{chunk-LCX5B364.js → chunk-PPTM4O4R.js} +3 -3
  107. package/chunks/{chunk-VPV7WBVT.js → chunk-PT4I7NBA.js} +1 -1
  108. package/chunks/{chunk-3LDGOQXZ.js → chunk-QECSDJET.js} +6 -6
  109. package/chunks/{chunk-FMIH5KMQ.js → chunk-QEJZUXSK.js} +1 -1
  110. package/chunks/{chunk-NLFLYSS4.js → chunk-QFJ5JHQR.js} +1 -1
  111. package/chunks/{chunk-KWX56CHZ.js → chunk-QIHHJKXT.js} +7 -7
  112. package/chunks/{chunk-TDESMS7M.js → chunk-RAQBBGS6.js} +50 -70
  113. package/chunks/{chunk-KEC5FHLM.js → chunk-ROIYNTHJ.js} +3 -3
  114. package/chunks/{chunk-63FNZTWG.js → chunk-RWDNJBWN.js} +2 -2
  115. package/chunks/{chunk-56HVDG3M.js → chunk-RXHBTKMG.js} +6 -6
  116. package/chunks/{chunk-MAKIANMX.js → chunk-S6CXQKDD.js} +3 -3
  117. package/chunks/{chunk-CIDQISGM.js → chunk-SA7GBPEA.js} +5 -5
  118. package/chunks/{chunk-YX2RDGAL.js → chunk-SBRIBTYN.js} +1 -1
  119. package/chunks/{chunk-TTRH3RTQ.js → chunk-SG7ZP5PG.js} +4 -4
  120. package/chunks/{chunk-XH3KAV2G.js → chunk-SGLPDKRT.js} +3 -3
  121. package/chunks/{chunk-JRIVMPD2.js → chunk-SLILKYV2.js} +4 -4
  122. package/chunks/{chunk-NW2GIPG3.js → chunk-SNQZZ765.js} +2 -2
  123. package/chunks/{chunk-I7BT2MME.js → chunk-SR2LYSMT.js} +1 -1
  124. package/chunks/{chunk-TXP5NGG3.js → chunk-SXCOEJO4.js} +1 -1
  125. package/chunks/{chunk-MELQSJOU.js → chunk-TNPWODIZ.js} +1 -1
  126. package/chunks/{chunk-VLHR7F4E.js → chunk-U6RZRKNM.js} +247 -198
  127. package/chunks/{chunk-HTYND72J.js → chunk-UC67T4SF.js} +3 -1
  128. package/chunks/{chunk-HG3UBTJ4.js → chunk-UYDGFWIJ.js} +609 -23
  129. package/chunks/{chunk-6P5EXEBL.js → chunk-VGAAAMRD.js} +1126 -232
  130. package/chunks/{chunk-WDVDVWJ3.js → chunk-VHPMDDPT.js} +6 -6
  131. package/chunks/{chunk-H7TMVHKE.js → chunk-WGHHA7ZH.js} +1 -1
  132. package/chunks/{chunk-FQDCO3CJ.js → chunk-WP4WIUOJ.js} +7 -0
  133. package/chunks/{chunk-5RUCD4ED.js → chunk-WPVUOVYX.js} +3 -3
  134. package/chunks/{chunk-EJOGLSNJ.js → chunk-WSSH2BNO.js} +2 -2
  135. package/chunks/{chunk-5AFGJNY6.js → chunk-WVIABDXV.js} +4 -4
  136. package/chunks/chunk-WZAD4ZNJ.js +59 -0
  137. package/chunks/{chunk-62X3CM7Q.js → chunk-X3YA3YGG.js} +8 -5
  138. package/chunks/{chunk-EBPLKY6U.js → chunk-XC377TLN.js} +5483 -3168
  139. package/chunks/{chunk-KLOMACIR.js → chunk-XLLKYULU.js} +2 -2
  140. package/chunks/{chunk-QSD4UH74.js → chunk-XO24LIZC.js} +254 -56
  141. package/chunks/{chunk-N4SVADAV.js → chunk-XT4RKHVE.js} +5 -4
  142. package/chunks/{chunk-PVXEJB66.js → chunk-XUJNK7Y6.js} +1 -1
  143. package/chunks/{chunk-YBHUXEC6.js → chunk-YECDICIO.js} +3 -3
  144. package/chunks/{chunk-FA5K6YI2.js → chunk-YFPZ3NNC.js} +36 -4
  145. package/chunks/{chunk-46UV252V.js → chunk-YHC2EYYG.js} +35 -38
  146. package/chunks/{chunk-6SPD4Z7F.js → chunk-YIWH3MKK.js} +8 -3
  147. package/chunks/{chunk-ZMY2P4L3.js → chunk-YXC7JYUI.js} +4 -4
  148. package/chunks/{chunk-TB4YFWXG.js → chunk-YZHRN3CF.js} +2 -2
  149. package/chunks/{chunk-TQDFSQQF.js → chunk-YZOSNW4R.js} +1 -1
  150. package/chunks/{chunk-DD6O3ZRD.js → chunk-ZIGC3MUS.js} +3 -5
  151. package/chunks/{chunk-CZ4OWGQQ.js → chunk-ZXEAEZFN.js} +63 -19
  152. package/chunks/{computer-use-F4URV4TU.js → computer-use-ABECHPS5.js} +42 -41
  153. package/chunks/{config-utils-Z3OW3IQG.js → config-utils-NJQA5YLJ.js} +2 -2
  154. package/chunks/{contextCommand-IGSS2U5J.js → contextCommand-4CD5HZQQ.js} +44 -43
  155. package/chunks/{core-runtime-H5CZDN3A.js → core-runtime-2MOVQ4VX.js} +42 -41
  156. package/chunks/{create-sub-session-XT6DLYOE.js → create-sub-session-KRQULJE6.js} +2 -2
  157. package/chunks/{create-sub-session-PA7MFTHK.js → create-sub-session-L6NNHG5N.js} +46 -44
  158. package/chunks/{cron-create-2JZ2TWTF.js → cron-create-NFRZEOQX.js} +4 -4
  159. package/chunks/{cron-delete-CTPD2P26.js → cron-delete-GFVSVO7C.js} +4 -4
  160. package/chunks/{cron-list-V44KHAF6.js → cron-list-NRW34CLU.js} +4 -4
  161. package/chunks/{daemon-GED37WOX.js → daemon-XH76IXOC.js} +436 -29
  162. package/chunks/{daemon-git-worktree-guard-LZDCNJGV.js → daemon-git-worktree-guard-BLXFEG4Q.js} +44 -43
  163. package/chunks/daemon-status-provider-XGD3TPTO.js +103 -0
  164. package/chunks/daemon-trust-policy-THEGCWPO.js +100 -0
  165. package/chunks/{daemon-trust-policy-monitor-2K2YETMT.js → daemon-trust-policy-monitor-L7L4ACWQ.js} +49 -48
  166. package/chunks/{de-UBEBKG6V.js → de-AH4LGCF5.js} +8 -0
  167. package/chunks/{deferred-core-runtime-FKJ3OYE7.js → deferred-core-runtime-YQLHDUGE.js} +42 -41
  168. package/chunks/discovery-X2NV535U.js +27 -0
  169. package/chunks/{display-image-M2ELAVN4.js → display-image-ON3EYFRD.js} +4 -4
  170. package/chunks/{dist-WJ4BHIEW.js → dist-ESS34DHC.js} +72 -0
  171. package/chunks/{earlyInputCapture-WJBSJQUF.js → earlyInputCapture-PCTXNW7N.js} +43 -42
  172. package/chunks/{edit-X73LA4YO.js → edit-QX2EGWYW.js} +45 -43
  173. package/chunks/{en-X6YV7GME.js → en-MD3GP5YL.js} +8 -0
  174. package/chunks/{enter-worktree-CLRQTIVN.js → enter-worktree-EM3ZZ2SB.js} +42 -41
  175. package/chunks/{enterPlanMode-6BDYUQ5T.js → enterPlanMode-ESCMVAKB.js} +42 -41
  176. package/chunks/{environment-WOAJXJDJ.js → environment-TOJI56YV.js} +45 -44
  177. package/chunks/{errors-7OY5MXYY.js → errors-DZ5FWWEM.js} +44 -43
  178. package/chunks/{exit-worktree-TFFJXCRT.js → exit-worktree-KUIJ36XA.js} +42 -41
  179. package/chunks/exitPlanMode-NFVY54FV.js +86 -0
  180. package/chunks/{fast-path-QQLGC37M.js → fast-path-JLQXJSH6.js} +3 -3
  181. package/chunks/{fast-path-settings-LNOFM2YP.js → fast-path-settings-WHX3J3ZI.js} +2 -2
  182. package/chunks/{fr-VENTC2BS.js → fr-ZEML2JWS.js} +8 -0
  183. package/chunks/{gemini-VHE62IJL.js → gemini-I7OT4FWL.js} +92 -91
  184. package/chunks/{geminiContentGenerator-VSHIEGAQ.js → geminiContentGenerator-GRIS77NJ.js} +6 -6
  185. package/chunks/{glob-VMEEDDLO.js → glob-7TBUUV6R.js} +42 -41
  186. package/chunks/{goal-tools-M4UDAZXP.js → goal-tools-K5OHOJ6B.js} +56 -12
  187. package/chunks/{grep-F4UAR7KX.js → grep-743SYWCZ.js} +42 -41
  188. package/chunks/handleAutoUpdate-GAOYUYOD.js +95 -0
  189. package/chunks/{i18n-ETW24DHW.js → i18n-2ITURYX6.js} +43 -42
  190. package/chunks/{image-gen-YPK2AZT4.js → image-gen-4OIWDZD6.js} +10 -10
  191. package/chunks/initializer-OC7LWA6X.js +101 -0
  192. package/chunks/{installationInfo-P7OTIR4P.js → installationInfo-CO47QX3Q.js} +43 -42
  193. package/chunks/{ja-QHT4EQTO.js → ja-UM5B2HMD.js} +8 -0
  194. package/chunks/{keychain-token-storage-NKKCUHI2.js → keychain-token-storage-3XFJ5ZB2.js} +2 -2
  195. package/chunks/list-ATR2SFWT.js +104 -0
  196. package/chunks/{list-agents-O6WSY4CT.js → list-agents-OSVDFI6S.js} +2 -2
  197. package/chunks/loadedSettingsAdapter-NK4B7AGK.js +98 -0
  198. package/chunks/{loggingContentGenerator-J2J2AANS.js → loggingContentGenerator-3WXBU5TQ.js} +67 -41
  199. package/chunks/{loop-wakeup-GVQRMUKS.js → loop-wakeup-HWVUU5CF.js} +5 -5
  200. package/chunks/{ls-6W2D7TKU.js → ls-D3HUFHDP.js} +4 -4
  201. package/chunks/{lsp-NJ4KFBI2.js → lsp-3QDDJTCS.js} +2 -2
  202. package/chunks/{managed-npm-update-WMV5ITKU.js → managed-npm-update-4SSBWI2D.js} +43 -42
  203. package/chunks/mcp-C62AE2QI.js +98 -0
  204. package/chunks/{monitor-2MM23VDV.js → monitor-FEYEGASA.js} +42 -41
  205. package/chunks/nonInteractiveCli-AJEV5ZWO.js +165 -0
  206. package/chunks/{notebook-edit-QD4TRKLY.js → notebook-edit-SLLTEY4O.js} +46 -44
  207. package/chunks/{openaiContentGenerator-KLHSPVN2.js → openaiContentGenerator-WLUGIAPX.js} +27 -26
  208. package/chunks/{pidfile-RQGZBNXT.js → pidfile-TV2VWUMB.js} +43 -42
  209. package/chunks/{processUtils-HZZDA2VV.js → processUtils-3IN2ZR55.js} +2 -2
  210. package/chunks/proper-lockfile-3LHWIM44.js +8 -0
  211. package/chunks/{pt-VL3I5RNY.js → pt-5BMJ3XYH.js} +8 -0
  212. package/chunks/{qwenContentGenerator-T75M2Z3B.js → qwenContentGenerator-GSGRFPJE.js} +47 -46
  213. package/chunks/{qwenOAuth2-5QZU444W.js → qwenOAuth2-JLSTIS4Z.js} +9 -9
  214. package/chunks/{read-file-LOVUSBJC.js → read-file-6PZZRGZZ.js} +15 -13
  215. package/chunks/{read-mcp-resource-GWCP23WG.js → read-mcp-resource-XD54ZQMC.js} +2 -2
  216. package/chunks/{record-artifact-IYQDEYMV.js → record-artifact-GLU3UWBV.js} +5 -3
  217. package/chunks/{resumeHistoryUtils-LVKSSQII.js → resumeHistoryUtils-5GEWKOOO.js} +49 -48
  218. package/chunks/ripGrep-FX7GBGFN.js +86 -0
  219. package/chunks/{ru-4RCKL4PY.js → ru-6GL66ORV.js} +8 -0
  220. package/chunks/{run-qwen-serve-GZW7B4WK.js → run-qwen-serve-7RHU3NSV.js} +423 -212
  221. package/chunks/{runtime-MYRXEO2E.js → runtime-EPOXAMLP.js} +52 -51
  222. package/chunks/{scheduler-DPTPI6FW.js → scheduler-F4Y7ENVV.js} +44 -43
  223. package/chunks/{sdk-exporters-http-NHBYB5FZ.js → sdk-exporters-http-NC746HLT.js} +3 -3
  224. package/chunks/{sdk-impl-SK4P5WON.js → sdk-impl-YVL7BP6Y.js} +3 -3
  225. package/chunks/{send-message-O7FO63UK.js → send-message-TNBG5EVA.js} +7 -7
  226. package/chunks/{serve-XCG4XCDA.js → serve-LQV4TN5A.js} +49 -50
  227. package/chunks/{server-AWQHKXLG.js → server-IZCKLIYQ.js} +5042 -1725
  228. package/chunks/{session-P5FKNCNR.js → session-52ZGTDVB.js} +87 -85
  229. package/chunks/{settings-CSYVIP2R.js → settings-WCVB34HW.js} +48 -47
  230. package/chunks/{shell-EGTSUO2P.js → shell-33BIUBLH.js} +42 -41
  231. package/chunks/{skill-IFUD72GF.js → skill-IYVQ2QNK.js} +20 -18
  232. package/chunks/{skill-settings-YOE7LBHE.js → skill-settings-YG4ZS4OZ.js} +48 -47
  233. package/chunks/{spawnChannel-PQSZIOLY.js → spawnChannel-BLA4YT4W.js} +44 -43
  234. package/chunks/{standalone-update-RB2EGUSF.js → standalone-update-ALRIPWSM.js} +44 -43
  235. package/chunks/{startInteractiveUI-KMYUJ2MU.js → startInteractiveUI-PISBXWKA.js} +614 -294
  236. package/chunks/{syntheticOutput-DVHJMUGV.js → syntheticOutput-7GWFIDXT.js} +3 -3
  237. package/chunks/{task-create-4NC7PDI4.js → task-create-EILHJJB7.js} +11 -11
  238. package/chunks/{task-list-LBSRMOC7.js → task-list-WGJR6YL5.js} +21 -7
  239. package/chunks/{task-stop-YFNATTIM.js → task-stop-X37KYTKB.js} +2 -2
  240. package/chunks/{task-update-BPLM52GD.js → task-update-XZAHVXJ5.js} +86 -15
  241. package/chunks/{team-create-B6A6MJBT.js → team-create-CIQDCC6A.js} +43 -43
  242. package/chunks/{team-delete-MKYMYBFM.js → team-delete-UEP4QOOT.js} +6 -6
  243. package/chunks/{team-plan-approval-5WO3CXXU.js → team-plan-approval-664XCRB4.js} +42 -41
  244. package/chunks/{terminal-image-renderer-BTRE2GEU.js → terminal-image-renderer-TEBO6ONY.js} +44 -43
  245. package/chunks/theme-manager-3CIFH2JV.js +90 -0
  246. package/chunks/{todoWrite-K5K7NRE5.js → todoWrite-ATS62CAP.js} +4 -4
  247. package/chunks/{tool-search-QNBLXUGR.js → tool-search-IJX5JHJB.js} +19 -17
  248. package/chunks/{total-session-admission-N4E5SFBO.js → total-session-admission-XBK2NVRV.js} +46 -45
  249. package/chunks/{trustedFolders-MQS46LIU.js → trustedFolders-OOFNLRWK.js} +43 -42
  250. package/chunks/{update-relaunch-536KRL33.js → update-relaunch-EIG7EYAQ.js} +5 -5
  251. package/chunks/{updateCheck-QJI2SYWF.js → updateCheck-KYDCBLSF.js} +46 -45
  252. package/chunks/{useAutoAcceptIndicator-7QASHD46.js → useAutoAcceptIndicator-RFX3KNBN.js} +52 -51
  253. package/chunks/{validateNonInterActiveAuth-ZNRGWBLV.js → validateNonInterActiveAuth-MZXSAITU.js} +83 -81
  254. package/chunks/{version-DBEQAFRW.js → version-PJC6ZNXV.js} +1 -1
  255. package/chunks/{web-fetch-MBIVXKPE.js → web-fetch-XXICVGKQ.js} +21 -18
  256. package/chunks/{web-search-4LBIN6VF.js → web-search-LHPM6QWC.js} +9 -9
  257. package/chunks/{workflow-LNMA55HY.js → workflow-4CUO2BV7.js} +159 -57
  258. package/chunks/workspace-providers-status-G4MUGH3X.js +101 -0
  259. package/chunks/{workspace-registration-store-BGRKKIYR.js → workspace-registration-store-QMDMFWBE.js} +2 -2
  260. package/chunks/{workspace-registry-L4NCJ34K.js → workspace-registry-FKBB4MZK.js} +48 -46
  261. package/chunks/{workspace-service-LOXYKYTR.js → workspace-service-TIDTTA65.js} +55 -54
  262. package/chunks/workspace-skills-status-LBC2TZKM.js +101 -0
  263. package/chunks/{workspace-trust-reconciler-H36TCR2S.js → workspace-trust-reconciler-4KJ567XP.js} +58 -56
  264. package/chunks/{write-file-JJAY7JSP.js → write-file-R6LAEER5.js} +42 -41
  265. package/chunks/{zh-TW-SQ4HTSCW.js → zh-TW-LVLBK4KF.js} +8 -0
  266. package/chunks/{zh-LMSTK4WD.js → zh-UBMWS2BJ.js} +8 -0
  267. package/chunks/{zoom-image-4MVVVM7E.js → zoom-image-XLCK6DDK.js} +20 -17
  268. package/cli.js +13 -13
  269. package/locales/ca.js +13 -0
  270. package/locales/de.js +12 -0
  271. package/locales/en.js +11 -0
  272. package/locales/fr.js +13 -0
  273. package/locales/ja.js +13 -0
  274. package/locales/pt.js +12 -0
  275. package/locales/ru.js +12 -0
  276. package/locales/zh-TW.js +11 -0
  277. package/locales/zh.js +11 -0
  278. package/package.json +3 -3
  279. package/web-shell/assets/{arc-B-89zHN4.js → arc-DItbX0DI.js} +1 -1
  280. package/web-shell/assets/{architectureDiagram-3BPJPVTR-BkcOncHk.js → architectureDiagram-3BPJPVTR-CmnIlH8y.js} +1 -1
  281. package/web-shell/assets/{blockDiagram-GPEHLZMM-DQ4yboaF.js → blockDiagram-GPEHLZMM-pObnB9kw.js} +1 -1
  282. package/web-shell/assets/{c4Diagram-AAUBKEIU-C_8ZjPDI.js → c4Diagram-AAUBKEIU-BNBmMS0Y.js} +1 -1
  283. package/web-shell/assets/channel-hZnMfWA9.js +1 -0
  284. package/web-shell/assets/{chunk-2J33WTMH-naKVe1Bm.js → chunk-2J33WTMH-Dz4JBOO6.js} +1 -1
  285. package/web-shell/assets/{chunk-4BX2VUAB-DdoViKT2.js → chunk-4BX2VUAB-Cz2GnvAR.js} +1 -1
  286. package/web-shell/assets/{chunk-55IACEB6-DUDQZEwU.js → chunk-55IACEB6-CC6kOQYx.js} +1 -1
  287. package/web-shell/assets/{chunk-727SXJPM-BqpDTR5s.js → chunk-727SXJPM-Z4lY4gbh.js} +1 -1
  288. package/web-shell/assets/{chunk-AQP2D5EJ-CdmEknkT.js → chunk-AQP2D5EJ-Yz0hZr3D.js} +1 -1
  289. package/web-shell/assets/{chunk-FMBD7UC4-CH7fFTLB.js → chunk-FMBD7UC4-nKmTWTv9.js} +1 -1
  290. package/web-shell/assets/{chunk-ND2GUHAM-ObgSuFKF.js → chunk-ND2GUHAM-CjZz0f6Y.js} +1 -1
  291. package/web-shell/assets/{chunk-QZHKN3VN-D9fKdv_s.js → chunk-QZHKN3VN-Bab3FpuS.js} +1 -1
  292. package/web-shell/assets/classDiagram-4FO5ZUOK-BRnxm8DX.js +1 -0
  293. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BRnxm8DX.js +1 -0
  294. package/web-shell/assets/{cose-bilkent-S5V4N54A-CvzATMx9.js → cose-bilkent-S5V4N54A-BuSAGFox.js} +1 -1
  295. package/web-shell/assets/{dagre-BM42HDAG-M_xU21QW.js → dagre-BM42HDAG-CRZXTg3g.js} +1 -1
  296. package/web-shell/assets/{diagram-2AECGRRQ-CNrl9sW1.js → diagram-2AECGRRQ-BFx1Ayrz.js} +1 -1
  297. package/web-shell/assets/{diagram-5GNKFQAL-CUweVT6T.js → diagram-5GNKFQAL-B2hMZ3gS.js} +1 -1
  298. package/web-shell/assets/{diagram-KO2AKTUF-D3n6Lxjo.js → diagram-KO2AKTUF-DKxAQLmc.js} +1 -1
  299. package/web-shell/assets/{diagram-LMA3HP47-0SeFPK4t.js → diagram-LMA3HP47-Bo1xGqVY.js} +1 -1
  300. package/web-shell/assets/{diagram-OG6HWLK6-DAgyEwge.js → diagram-OG6HWLK6-B9NG78d-.js} +1 -1
  301. package/web-shell/assets/{erDiagram-TEJ5UH35-CzxBBDIK.js → erDiagram-TEJ5UH35-DltQKiqQ.js} +1 -1
  302. package/web-shell/assets/{flowDiagram-I6XJVG4X-B0uj0jyo.js → flowDiagram-I6XJVG4X-5xXGmS4I.js} +1 -1
  303. package/web-shell/assets/{ganttDiagram-6RSMTGT7-DFzcj5km.js → ganttDiagram-6RSMTGT7-bhGL5n0l.js} +1 -1
  304. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-DylJ_Fc9.js → gitGraphDiagram-PVQCEYII-ZYnz9hgh.js} +1 -1
  305. package/web-shell/assets/{index-BKSAnXU9.js → index-CBBeUArD.js} +1 -1
  306. package/web-shell/assets/index-CbgU-jUo.css +5 -0
  307. package/web-shell/assets/index-_nhzXqab.js +1750 -0
  308. package/web-shell/assets/{infoDiagram-5YYISTIA-BYMcJVMI.js → infoDiagram-5YYISTIA-Cg-jamQf.js} +1 -1
  309. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-t1quI1S3.js → ishikawaDiagram-YF4QCWOH-CcmvKlyq.js} +1 -1
  310. package/web-shell/assets/{journeyDiagram-JHISSGLW-YM_QVUJm.js → journeyDiagram-JHISSGLW-BRsOQZp1.js} +1 -1
  311. package/web-shell/assets/{kanban-definition-UN3LZRKU-jw8OddK1.js → kanban-definition-UN3LZRKU-xW-dmEHK.js} +1 -1
  312. package/web-shell/assets/{linear-Dlr3RVuP.js → linear-ClBfEZH2.js} +1 -1
  313. package/web-shell/assets/{mermaid.core-CqGKzNzd.js → mermaid.core-D65NDkzc.js} +5 -5
  314. package/web-shell/assets/{mindmap-definition-RKZ34NQL-BqureoOS.js → mindmap-definition-RKZ34NQL-CyvFslYa.js} +1 -1
  315. package/web-shell/assets/{pieDiagram-4H26LBE5-Da20HkK1.js → pieDiagram-4H26LBE5-BwSefMyG.js} +1 -1
  316. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-BsNlMvJy.js → quadrantDiagram-W4KKPZXB-GqBJspFS.js} +1 -1
  317. package/web-shell/assets/{requirementDiagram-4Y6WPE33-DzVCQCse.js → requirementDiagram-4Y6WPE33-B137PlTJ.js} +1 -1
  318. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-BMo16voT.js → sankeyDiagram-5OEKKPKP-maHkkt8i.js} +1 -1
  319. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-Bgi-pdHk.js → sequenceDiagram-3UESZ5HK-DJ1mRaG3.js} +1 -1
  320. package/web-shell/assets/{stateDiagram-AJRCARHV-xaPQXwGk.js → stateDiagram-AJRCARHV-aRmP7Q4i.js} +1 -1
  321. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-Db39gvZ_.js +1 -0
  322. package/web-shell/assets/{timeline-definition-PNZ67QCA-C_9RuvKG.js → timeline-definition-PNZ67QCA-CIQ3BRPA.js} +1 -1
  323. package/web-shell/assets/{vennDiagram-CIIHVFJN-Bgu6vdla.js → vennDiagram-CIIHVFJN-DR9VPd5h.js} +1 -1
  324. package/web-shell/assets/{wardley-L42UT6IY-CFTZ8IrX.js → wardley-L42UT6IY-ZcfIbT4D.js} +1 -1
  325. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-BASW9Vmt.js → wardleyDiagram-YWT4CUSO-D0jgPi4T.js} +1 -1
  326. package/web-shell/assets/{xychartDiagram-2RQKCTM6--Nh-CD3F.js → xychartDiagram-2RQKCTM6-BGWU4s5o.js} +1 -1
  327. package/web-shell/index.html +2 -2
  328. package/chunks/agent-headless-OEQNFOMH.js +0 -87
  329. package/chunks/chunk-DEDESZDL.js +0 -358
  330. package/chunks/chunk-MPHPFVKK.js +0 -48
  331. package/chunks/chunk-UI5UGEF4.js +0 -3576
  332. package/chunks/daemon-status-provider-QYM57Z44.js +0 -102
  333. package/chunks/daemon-trust-policy-TSRVEYZR.js +0 -99
  334. package/chunks/discovery-XLHITE74.js +0 -220
  335. package/chunks/exitPlanMode-WDURQMIU.js +0 -85
  336. package/chunks/handleAutoUpdate-GN7Z6QAC.js +0 -94
  337. package/chunks/initializer-UA6KQ7MS.js +0 -100
  338. package/chunks/list-GRAMIQOT.js +0 -103
  339. package/chunks/loadedSettingsAdapter-5O2TDS3I.js +0 -97
  340. package/chunks/mcp-XLP33CAI.js +0 -97
  341. package/chunks/nonInteractiveCli-KTA5K6FS.js +0 -163
  342. package/chunks/ripGrep-ZJSHKB6H.js +0 -85
  343. package/chunks/theme-manager-XL2N5SBY.js +0 -89
  344. package/chunks/workspace-providers-status-PDDMI6ST.js +0 -100
  345. package/chunks/workspace-skills-status-IJ5FOQI6.js +0 -100
  346. package/web-shell/assets/channel-Du2WFns8.js +0 -1
  347. package/web-shell/assets/classDiagram-4FO5ZUOK-Zw0HafH6.js +0 -1
  348. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-Zw0HafH6.js +0 -1
  349. package/web-shell/assets/index-BIGQygTL.css +0 -5
  350. package/web-shell/assets/index-J7OMTqAN.js +0 -1789
  351. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-D5RqimXD.js +0 -1
  352. package/chunks/{chunk-SYHXR6WM.js → chunk-4W5U2TUT.js} +3 -3
  353. /package/chunks/{chunk-NTTBXH7U.js → chunk-UGK4K2N2.js} +0 -0
@@ -19,7 +19,7 @@ import {
19
19
  } from "./chunk-OCPBI7J5.js";
20
20
  import {
21
21
  defaultSpawnChannelFactory
22
- } from "./chunk-DAV4QFD5.js";
22
+ } from "./chunk-BQWNIPTE.js";
23
23
  import {
24
24
  BridgeChannelClosedError,
25
25
  BridgeTimeoutError,
@@ -29,7 +29,7 @@ import {
29
29
  SessionRestoreTimeoutError,
30
30
  createIdleWorkspaceExtensionsStatus,
31
31
  createIdleWorkspaceHooksStatus
32
- } from "./chunk-HTYND72J.js";
32
+ } from "./chunk-UC67T4SF.js";
33
33
  import {
34
34
  ACTIVE_WORK_CLOSE_IF_UNHELD_PARAM,
35
35
  ACTIVE_WORK_CLOSE_TIMEOUT_MS,
@@ -45,6 +45,7 @@ import {
45
45
  CHANNEL_STARTUP_PROFILE_META_KEY,
46
46
  CHANNEL_STARTUP_PROFILE_VERSION,
47
47
  DAEMON_CHANNEL_DELIVERY_META_KEY,
48
+ DAEMON_MEDIA_REFERENCES_META_KEY,
48
49
  DAEMON_MODEL_PROMPT_META_KEY,
49
50
  DAEMON_PROMPT_DISPLAY_TEXT_META_KEY,
50
51
  LOAD_REPLAY_BULK_MODE,
@@ -62,8 +63,9 @@ import {
62
63
  TODO_STOP_GUARD_QUEUE_RELEASE_METHOD,
63
64
  WORKTREE_MCP_DEFER_META_KEY,
64
65
  clampActiveWorkIntervalMs,
65
- isValidTrustedModelPrompt
66
- } from "./chunk-FQDCO3CJ.js";
66
+ isValidTrustedModelPrompt,
67
+ sessionCloseDrainBudgetMs
68
+ } from "./chunk-WP4WIUOJ.js";
67
69
  import {
68
70
  BranchWhilePromptActiveError,
69
71
  BridgeChannelQuarantinedError,
@@ -112,6 +114,8 @@ import {
112
114
  SESSION_ARTIFACT_PERSISTENCE_VERSION,
113
115
  SESSION_TRANSCRIPT_MAX_LIMIT,
114
116
  ShellExecutionService,
117
+ TURN_RESULT_CODE_TEXT_TRUNCATED,
118
+ TURN_RESULT_TEXT_MAX_CHARS,
115
119
  TrustGateError,
116
120
  WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY,
117
121
  WORKSPACE_CONTENT_SHA256_METADATA_KEY,
@@ -119,17 +123,18 @@ import {
119
123
  isReservedWorkspaceMetadataKey,
120
124
  metadataBudgetBytes,
121
125
  normalizeSnapshotPayload,
126
+ normalizeTurnResultError,
122
127
  stableSessionArtifactId
123
- } from "./chunk-EBPLKY6U.js";
128
+ } from "./chunk-XC377TLN.js";
124
129
  import {
125
130
  DAEMON_TRACEPARENT_META_KEY,
126
131
  DAEMON_TRACESTATE_META_KEY
127
- } from "./chunk-K34GJQOZ.js";
132
+ } from "./chunk-OO4FKHLO.js";
128
133
  import {
129
134
  INVOCATION_CONTEXT_META_KEY,
130
135
  PRIVATE_ACP_CAPABILITY_ENV,
131
136
  PRIVATE_PARENT_CAPABILITY_META_KEY
132
- } from "./chunk-4KM2SYK5.js";
137
+ } from "./chunk-KC4WMLZO.js";
133
138
  import {
134
139
  init_esbuild_shims
135
140
  } from "./chunk-5O2XNYP6.js";
@@ -139,8 +144,8 @@ import {
139
144
 
140
145
  // packages/acp-bridge/src/bridge.ts
141
146
  init_esbuild_shims();
142
- import { randomBytes, randomUUID as randomUUID2 } from "node:crypto";
143
- import * as path3 from "node:path";
147
+ import { randomBytes, randomUUID as randomUUID3 } from "node:crypto";
148
+ import * as path4 from "node:path";
144
149
  import { inspect } from "node:util";
145
150
 
146
151
  // packages/acp-bridge/src/compactionEngine.ts
@@ -604,7 +609,7 @@ var TurnBoundaryCompactionEngine = class {
604
609
  const slot = this.slots[existingIdx];
605
610
  slot.chunks.push(text);
606
611
  if (event.id !== void 0) slot.lastEventId = event.id;
607
- slot.lastMeta = meta ?? slot.lastMeta;
612
+ slot.lastMeta = mergeTranscriptUpdateMeta(slot.lastMeta, meta);
608
613
  slot.lastEnvelopeMeta = event._meta ?? slot.lastEnvelopeMeta;
609
614
  slot.lastTurn = captureTurnFields(event, slot.lastTurn);
610
615
  slot.lastSessionId = captureSessionId(event) ?? slot.lastSessionId;
@@ -628,7 +633,7 @@ var TurnBoundaryCompactionEngine = class {
628
633
  if (lastSlot && lastSlot.kind === kind && lastSlot.parentToolCallId == null && stringArraysEqual(lastSlot.sourceRecordIds, sourceRecordIds)) {
629
634
  lastSlot.chunks.push(text);
630
635
  if (event.id !== void 0) lastSlot.lastEventId = event.id;
631
- lastSlot.lastMeta = meta ?? lastSlot.lastMeta;
636
+ lastSlot.lastMeta = mergeTranscriptUpdateMeta(lastSlot.lastMeta, meta);
632
637
  lastSlot.lastEnvelopeMeta = event._meta ?? lastSlot.lastEnvelopeMeta;
633
638
  lastSlot.lastTurn = captureTurnFields(event, lastSlot.lastTurn);
634
639
  lastSlot.lastSessionId = captureSessionId(event) ?? lastSlot.lastSessionId;
@@ -1053,13 +1058,26 @@ function mergeTranscriptUpdateMeta(existing, incoming) {
1053
1058
  ...extractSourceRecordIdsFromMeta(incomingRecord) ?? []
1054
1059
  ])
1055
1060
  ];
1061
+ const existingTranscript = extractTranscriptMeta(existingRecord);
1062
+ const incomingTranscript = extractTranscriptMeta(incomingRecord);
1056
1063
  return {
1057
1064
  ...existingRecord ?? {},
1058
1065
  ...incomingRecord ?? {},
1059
- ...sourceRecordIds.length > 0 ? { qwenTranscript: { sourceRecordIds } } : {}
1066
+ ...existingTranscript || incomingTranscript || sourceRecordIds.length > 0 ? {
1067
+ qwenTranscript: {
1068
+ ...existingTranscript ?? {},
1069
+ ...incomingTranscript ?? {},
1070
+ ...sourceRecordIds.length > 0 ? { sourceRecordIds } : {}
1071
+ }
1072
+ } : {}
1060
1073
  };
1061
1074
  }
1062
1075
  __name(mergeTranscriptUpdateMeta, "mergeTranscriptUpdateMeta");
1076
+ function extractTranscriptMeta(meta) {
1077
+ const transcript = meta?.["qwenTranscript"];
1078
+ return typeof transcript === "object" && transcript !== null ? transcript : void 0;
1079
+ }
1080
+ __name(extractTranscriptMeta, "extractTranscriptMeta");
1063
1081
 
1064
1082
  // packages/acp-bridge/src/journalGrowthPolicy.ts
1065
1083
  init_esbuild_shims();
@@ -1266,6 +1284,267 @@ function getChannelStartupProfileAttributes(response, receivedAtEpochMs, initial
1266
1284
  }
1267
1285
  __name(getChannelStartupProfileAttributes, "getChannelStartupProfileAttributes");
1268
1286
 
1287
+ // packages/acp-bridge/src/sessionMedia.ts
1288
+ init_esbuild_shims();
1289
+ import { randomUUID } from "node:crypto";
1290
+ import { promises as fs } from "node:fs";
1291
+ import { tmpdir } from "node:os";
1292
+ import * as path from "node:path";
1293
+ var SESSION_MEDIA_MAX_ITEM_BYTES = 8 * 1024 * 1024;
1294
+ var SESSION_MEDIA_MAX_TOTAL_BYTES = 100 * 1024 * 1024;
1295
+ var SESSION_MEDIA_MAX_ITEMS = 256;
1296
+ var SESSION_MEDIA_UNAVAILABLE_TEXT = "[Attached media is no longer available]";
1297
+ var SessionMediaReferenceError = class extends Error {
1298
+ constructor(message, code) {
1299
+ super(message);
1300
+ this.code = code;
1301
+ this.name = "SessionMediaReferenceError";
1302
+ }
1303
+ static {
1304
+ __name(this, "SessionMediaReferenceError");
1305
+ }
1306
+ };
1307
+ function isSessionMediaReference(value) {
1308
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1309
+ const record = value;
1310
+ return record["type"] === "image" && typeof record["mediaId"] === "string" && record["mediaId"].length > 0 && typeof record["mimeType"] === "string" && record["mimeType"].startsWith(`${record["type"]}/`) && typeof record["size"] === "number" && Number.isSafeInteger(record["size"]) && record["size"] > 0;
1311
+ }
1312
+ __name(isSessionMediaReference, "isSessionMediaReference");
1313
+ function withMediaDegradationMarker(blocks) {
1314
+ for (let i = blocks.length - 1; i >= 0; i--) {
1315
+ const block = blocks[i];
1316
+ if (block.type === "text") {
1317
+ if (block.text.endsWith(SESSION_MEDIA_UNAVAILABLE_TEXT)) {
1318
+ return [...blocks];
1319
+ }
1320
+ const next = [...blocks];
1321
+ next[i] = {
1322
+ type: "text",
1323
+ text: `${block.text}
1324
+ ${SESSION_MEDIA_UNAVAILABLE_TEXT}`
1325
+ };
1326
+ return next;
1327
+ }
1328
+ }
1329
+ return [
1330
+ ...blocks,
1331
+ { type: "text", text: SESSION_MEDIA_UNAVAILABLE_TEXT }
1332
+ ];
1333
+ }
1334
+ __name(withMediaDegradationMarker, "withMediaDegradationMarker");
1335
+ var SessionMediaStore = class {
1336
+ static {
1337
+ __name(this, "SessionMediaStore");
1338
+ }
1339
+ records = /* @__PURE__ */ new Map();
1340
+ directoryPromise;
1341
+ totalBytes = 0;
1342
+ pendingItems = 0;
1343
+ closed = false;
1344
+ async put(data, mimeType) {
1345
+ if (this.closed) throw new Error("Session media store is closed");
1346
+ const type = "image";
1347
+ if (!mimeType.startsWith("image/")) {
1348
+ throw new TypeError("Session media must be image/*");
1349
+ }
1350
+ if (data.byteLength === 0 || data.byteLength > SESSION_MEDIA_MAX_ITEM_BYTES) {
1351
+ throw new RangeError(
1352
+ `Session media must be between 1 and ${SESSION_MEDIA_MAX_ITEM_BYTES} bytes`
1353
+ );
1354
+ }
1355
+ if (this.totalBytes + data.byteLength > SESSION_MEDIA_MAX_TOTAL_BYTES) {
1356
+ throw new RangeError(
1357
+ `Session media exceeds the ${SESSION_MEDIA_MAX_TOTAL_BYTES}-byte session limit`
1358
+ );
1359
+ }
1360
+ if (this.records.size + this.pendingItems >= SESSION_MEDIA_MAX_ITEMS) {
1361
+ throw new RangeError(
1362
+ `Session media exceeds the ${SESSION_MEDIA_MAX_ITEMS}-item session limit`
1363
+ );
1364
+ }
1365
+ const mediaId = randomUUID();
1366
+ let filePath;
1367
+ this.totalBytes += data.byteLength;
1368
+ this.pendingItems += 1;
1369
+ try {
1370
+ const directory = await this.directory();
1371
+ filePath = path.join(directory, mediaId);
1372
+ await fs.writeFile(filePath, data, { flag: "wx" });
1373
+ if (this.closed) {
1374
+ throw new Error("Session media store is closed");
1375
+ }
1376
+ const record = {
1377
+ type,
1378
+ mediaId,
1379
+ mimeType,
1380
+ size: data.byteLength,
1381
+ filePath
1382
+ };
1383
+ this.records.set(mediaId, record);
1384
+ return { type, mediaId, mimeType, size: data.byteLength };
1385
+ } catch (error) {
1386
+ if (filePath) await fs.rm(filePath, { force: true }).catch(() => {
1387
+ });
1388
+ if (!this.closed) this.totalBytes -= data.byteLength;
1389
+ throw error;
1390
+ } finally {
1391
+ if (!this.closed) this.pendingItems -= 1;
1392
+ }
1393
+ }
1394
+ // Validate one block against the store. Blocks without a `mediaId` (inline
1395
+ // media, text) pass through untouched, matching `assertReferences`.
1396
+ assertReference(block) {
1397
+ if (!block || typeof block !== "object" || Array.isArray(block) || !("mediaId" in block)) {
1398
+ return;
1399
+ }
1400
+ if (!isSessionMediaReference(block)) {
1401
+ throw new SessionMediaReferenceError(
1402
+ "Invalid session media reference",
1403
+ "invalid_session_media_reference"
1404
+ );
1405
+ }
1406
+ this.assertStored(block);
1407
+ }
1408
+ assertReferences(content) {
1409
+ const seenMediaIds = /* @__PURE__ */ new Set();
1410
+ for (const block of content) {
1411
+ if (!block || typeof block !== "object" || Array.isArray(block) || !("mediaId" in block)) {
1412
+ continue;
1413
+ }
1414
+ if (!isSessionMediaReference(block)) {
1415
+ throw new SessionMediaReferenceError(
1416
+ "Invalid session media reference",
1417
+ "invalid_session_media_reference"
1418
+ );
1419
+ }
1420
+ if (seenMediaIds.has(block.mediaId)) {
1421
+ throw new SessionMediaReferenceError(
1422
+ `Session media referenced more than once: ${block.mediaId}`,
1423
+ "invalid_session_media_reference"
1424
+ );
1425
+ }
1426
+ seenMediaIds.add(block.mediaId);
1427
+ this.assertStored(block);
1428
+ }
1429
+ }
1430
+ async resolveContent(content, memo) {
1431
+ const pendingByMediaId = memo ?? /* @__PURE__ */ new Map();
1432
+ return await Promise.all(
1433
+ content.map(async (block) => {
1434
+ if (!isSessionMediaReference(block)) return block;
1435
+ let pending = pendingByMediaId.get(block.mediaId);
1436
+ if (!pending) {
1437
+ const created = this.resolve(block);
1438
+ pendingByMediaId.set(block.mediaId, created);
1439
+ void created.catch(() => {
1440
+ if (pendingByMediaId.get(block.mediaId) === created) {
1441
+ pendingByMediaId.delete(block.mediaId);
1442
+ }
1443
+ });
1444
+ pending = created;
1445
+ }
1446
+ return await pending;
1447
+ })
1448
+ );
1449
+ }
1450
+ // Per-block variant of `resolveContent` for degrade paths: one unresolvable
1451
+ // reference drops only itself, keeping the sibling blocks a wholesale
1452
+ // fallback would discard. Non-media errors still propagate.
1453
+ async resolveContentDegrading(content, memo) {
1454
+ const retainedBlocks = [];
1455
+ const resolvedBlocks = [];
1456
+ let degraded = 0;
1457
+ for (const block of content) {
1458
+ if (!isSessionMediaReference(block)) {
1459
+ retainedBlocks.push(block);
1460
+ resolvedBlocks.push(block);
1461
+ continue;
1462
+ }
1463
+ try {
1464
+ const [resolved] = await this.resolveContent([block], memo);
1465
+ if (resolved) resolvedBlocks.push(resolved);
1466
+ retainedBlocks.push(block);
1467
+ } catch (error) {
1468
+ if (!(error instanceof SessionMediaReferenceError)) throw error;
1469
+ degraded += 1;
1470
+ }
1471
+ }
1472
+ return { retainedBlocks, resolvedBlocks, degraded };
1473
+ }
1474
+ async read(mediaId) {
1475
+ const record = this.records.get(mediaId);
1476
+ if (!record) return void 0;
1477
+ try {
1478
+ return {
1479
+ data: await fs.readFile(record.filePath),
1480
+ mimeType: record.mimeType
1481
+ };
1482
+ } catch (error) {
1483
+ if (error.code === "ENOENT") {
1484
+ if (this.records.delete(mediaId)) this.totalBytes -= record.size;
1485
+ return void 0;
1486
+ }
1487
+ throw error;
1488
+ }
1489
+ }
1490
+ async remove(mediaId) {
1491
+ const record = this.records.get(mediaId);
1492
+ if (!record) return false;
1493
+ this.records.delete(mediaId);
1494
+ this.totalBytes -= record.size;
1495
+ await fs.rm(record.filePath, { force: true });
1496
+ return true;
1497
+ }
1498
+ get sizeBytes() {
1499
+ return this.totalBytes;
1500
+ }
1501
+ async close() {
1502
+ if (this.closed) return;
1503
+ this.closed = true;
1504
+ this.records.clear();
1505
+ this.totalBytes = 0;
1506
+ this.pendingItems = 0;
1507
+ if (!this.directoryPromise) return;
1508
+ const directory = await this.directoryPromise.catch(() => void 0);
1509
+ if (!directory) return;
1510
+ await fs.rm(directory, { recursive: true, force: true });
1511
+ }
1512
+ assertStored(reference) {
1513
+ const stored = this.records.get(reference.mediaId);
1514
+ if (!stored || stored.type !== reference.type || stored.mimeType !== reference.mimeType || stored.size !== reference.size) {
1515
+ throw new SessionMediaReferenceError(
1516
+ `Unknown or unavailable session media: ${reference.mediaId}`,
1517
+ "session_media_gone"
1518
+ );
1519
+ }
1520
+ }
1521
+ async resolve(reference) {
1522
+ const media = await this.read(reference.mediaId);
1523
+ if (!media || media.mimeType !== reference.mimeType) {
1524
+ throw new SessionMediaReferenceError(
1525
+ `Unknown or unavailable session media: ${reference.mediaId}`,
1526
+ "session_media_gone"
1527
+ );
1528
+ }
1529
+ return {
1530
+ type: reference.type,
1531
+ data: media.data.toString("base64"),
1532
+ mimeType: media.mimeType
1533
+ };
1534
+ }
1535
+ async directory() {
1536
+ if (!this.directoryPromise) {
1537
+ const pending = fs.mkdtemp(path.join(tmpdir(), "qwen-session-media-"));
1538
+ this.directoryPromise = pending;
1539
+ void pending.catch(() => {
1540
+ if (this.directoryPromise === pending)
1541
+ this.directoryPromise = void 0;
1542
+ });
1543
+ }
1544
+ return await this.directoryPromise;
1545
+ }
1546
+ };
1547
+
1269
1548
  // packages/acp-bridge/src/internal/stderrLine.ts
1270
1549
  init_esbuild_shims();
1271
1550
  function writeStderrLine(message) {
@@ -1276,9 +1555,9 @@ __name(writeStderrLine, "writeStderrLine");
1276
1555
 
1277
1556
  // packages/acp-bridge/src/bridgeClient.ts
1278
1557
  init_esbuild_shims();
1279
- import { randomUUID } from "node:crypto";
1280
- import { promises as fs } from "node:fs";
1281
- import * as path from "node:path";
1558
+ import { randomUUID as randomUUID2 } from "node:crypto";
1559
+ import { promises as fs2 } from "node:fs";
1560
+ import * as path2 from "node:path";
1282
1561
 
1283
1562
  // packages/acp-bridge/src/daemonEventTypes.ts
1284
1563
  init_esbuild_shims();
@@ -2264,7 +2543,7 @@ function sliceLineRange(content, startLine, endLine) {
2264
2543
  }
2265
2544
  __name(sliceLineRange, "sliceLineRange");
2266
2545
  var BridgeClient = class {
2267
- constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender, ownsSession = () => true, onTokenUsage, onCreateSubSession, onGenerationEvent, onWorkspaceGenerationEvent, onChannelDelivery, hasSessionSpawnInFlight = () => false, getLiveScreenContextCaptureHandler = () => void 0, getLiveTaskToolRequestHandler = () => void 0, getLiveSpeakToUserHandler = () => void 0, externalToolGuard, onActiveWork) {
2546
+ constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender, ownsSession = () => true, onTokenUsage, onCreateSubSession, onGenerationEvent, onWorkspaceGenerationEvent, onChannelDelivery, hasSessionSpawnInFlight = () => false, getLiveScreenContextCaptureHandler = () => void 0, getLiveTaskToolRequestHandler = () => void 0, getLiveSpeakToUserHandler = () => void 0, externalToolGuard, onActiveWork, onSessionCatalogChanged) {
2268
2547
  this.resolveEntry = resolveEntry;
2269
2548
  this.resolvePendingRestoreEvents = resolvePendingRestoreEvents;
2270
2549
  this.mediator = mediator;
@@ -2286,6 +2565,7 @@ var BridgeClient = class {
2286
2565
  this.getLiveSpeakToUserHandler = getLiveSpeakToUserHandler;
2287
2566
  this.externalToolGuard = externalToolGuard;
2288
2567
  this.onActiveWork = onActiveWork;
2568
+ this.onSessionCatalogChanged = onSessionCatalogChanged;
2289
2569
  }
2290
2570
  static {
2291
2571
  __name(this, "BridgeClient");
@@ -2304,7 +2584,7 @@ var BridgeClient = class {
2304
2584
  options.map((o) => String(o.optionId ?? ""))
2305
2585
  );
2306
2586
  allowedOptionIds.delete("");
2307
- const requestId = randomUUID();
2587
+ const requestId = randomUUID2();
2308
2588
  if (allowedOptionIds.has(CANCEL_VOTE_SENTINEL)) {
2309
2589
  throw new CancelSentinelCollisionError(requestId, CANCEL_VOTE_SENTINEL);
2310
2590
  }
@@ -2550,8 +2830,8 @@ var BridgeClient = class {
2550
2830
  * between tool batches to pull any messages the browser queued mid-turn. We splice the per-session
2551
2831
  * queue, return them to the child as the response, and — when non-empty —
2552
2832
  * publish a `mid_turn_message_injected` SSE frame so the browser can move
2553
- * those messages out of its pending queue (a dedupe signal, not a transcript
2554
- * render). Unknown methods reject with ACP `methodNotFound` (-32601), matching
2833
+ * those messages out of its pending queue and render the immediate echo.
2834
+ * Unknown methods reject with ACP `methodNotFound` (-32601), matching
2555
2835
  * the SDK's
2556
2836
  * default for an unimplemented client surface; the child's drain caller
2557
2837
  * treats that as "drain unsupported" and stops asking.
@@ -2585,9 +2865,12 @@ var BridgeClient = class {
2585
2865
  throw RequestError.methodNotFound(method);
2586
2866
  }
2587
2867
  const sessionId = typeof params["sessionId"] === "string" ? params["sessionId"] : void 0;
2588
- if (!sessionId) return { messages: [], hasQueuedPrompt: false };
2868
+ if (!sessionId) return { messages: [], items: [], hasQueuedPrompt: false };
2869
+ if (!this.ownsSession(sessionId)) {
2870
+ return { messages: [], items: [], hasQueuedPrompt: false };
2871
+ }
2589
2872
  const entry = this.resolveEntry(sessionId);
2590
- if (!entry) return { messages: [], hasQueuedPrompt: false };
2873
+ if (!entry) return { messages: [], items: [], hasQueuedPrompt: false };
2591
2874
  const drained = entry.midTurnMessageQueue.splice(0);
2592
2875
  if (drained.length > 0) {
2593
2876
  for (const item of drained) {
@@ -2600,6 +2883,64 @@ var BridgeClient = class {
2600
2883
  );
2601
2884
  }
2602
2885
  }
2886
+ const mediaMemo = /* @__PURE__ */ new Map();
2887
+ const serializedMediaIds = /* @__PURE__ */ new Set();
2888
+ const items = [];
2889
+ try {
2890
+ for (const item of drained) {
2891
+ let degraded = 0;
2892
+ const planned = (item.content ?? []).filter((block) => {
2893
+ if (!isSessionMediaReference(block)) return true;
2894
+ if (serializedMediaIds.has(block.mediaId)) {
2895
+ degraded += 1;
2896
+ return false;
2897
+ }
2898
+ serializedMediaIds.add(block.mediaId);
2899
+ return true;
2900
+ });
2901
+ let resolvedBlocks;
2902
+ let mediaReferences;
2903
+ try {
2904
+ resolvedBlocks = await entry.media.resolveContent(planned, mediaMemo);
2905
+ mediaReferences = planned.filter(isSessionMediaReference);
2906
+ } catch (error) {
2907
+ if (!(error instanceof SessionMediaReferenceError)) throw error;
2908
+ writeStderrLine(
2909
+ `[mid-turn] session=${JSON.stringify(entry.sessionId)} degraded media for message ${JSON.stringify(item.messageId)}: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
2910
+ );
2911
+ const perBlock = await entry.media.resolveContentDegrading(
2912
+ planned,
2913
+ mediaMemo
2914
+ );
2915
+ resolvedBlocks = perBlock.resolvedBlocks;
2916
+ mediaReferences = perBlock.retainedBlocks.filter(
2917
+ isSessionMediaReference
2918
+ );
2919
+ degraded += perBlock.degraded;
2920
+ }
2921
+ let content = [
2922
+ ...item.text ? [{ type: "text", text: item.text }] : [],
2923
+ ...resolvedBlocks
2924
+ ];
2925
+ if (degraded > 0) content = withMediaDegradationMarker(content);
2926
+ items.push({
2927
+ messageId: item.messageId,
2928
+ displayText: item.text,
2929
+ content,
2930
+ ...mediaReferences.length > 0 ? { mediaReferences } : {}
2931
+ });
2932
+ }
2933
+ } catch (error) {
2934
+ const requeued = new Set(drained.map((queued) => queued.messageId));
2935
+ const ring = entry.settledMidTurnMessageIds;
2936
+ const kept = ring.filter((id) => !requeued.has(id));
2937
+ ring.splice(0, ring.length, ...kept);
2938
+ entry.midTurnMessageQueue.unshift(...drained);
2939
+ writeStderrLine(
2940
+ `[mid-turn] session=${JSON.stringify(entry.sessionId)} drain failed, requeued ${drained.length} message(s): ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
2941
+ );
2942
+ throw error;
2943
+ }
2603
2944
  const echoed = drained.filter((item) => !item.queueOnly);
2604
2945
  const messages = drained.map((item) => item.text);
2605
2946
  const hasQueuedPrompt = entry.pendingPromptList.some(
@@ -2612,14 +2953,21 @@ var BridgeClient = class {
2612
2953
  data: {
2613
2954
  sessionId: entry.sessionId,
2614
2955
  messages: echoed.map((item) => item.text),
2615
- messageIds: echoed.map((item) => item.messageId)
2956
+ messageIds: echoed.map((item) => item.messageId),
2957
+ // Carry the structured `items` twin (content blocks per message) so
2958
+ // the browser-side echo renderer can show attached images alongside
2959
+ // the message text. Older consumers that don't read this field keep
2960
+ // working unchanged.
2961
+ items: echoed.map((item) => ({
2962
+ ...item.content && item.content.length > 0 ? { content: item.content } : {}
2963
+ }))
2616
2964
  }
2617
2965
  });
2618
2966
  writeStderrLine(
2619
2967
  published ? `[mid-turn] session=${entry.sessionId} drained=${messages.length} echoed=${echoed.length} injected into running turn` : `[mid-turn] session=${entry.sessionId} drained=${messages.length} echoed=${echoed.length} echo frame dropped (bus closed); reconciliation required`
2620
2968
  );
2621
2969
  }
2622
- return { messages, hasQueuedPrompt };
2970
+ return { messages, items, hasQueuedPrompt };
2623
2971
  }
2624
2972
  async handleExternalToolGuardPrepare(params) {
2625
2973
  if (!this.externalToolGuard) {
@@ -3146,6 +3494,7 @@ var BridgeClient = class {
3146
3494
  return;
3147
3495
  const entry = this.resolveEntry(sessionId2);
3148
3496
  if (!entry) return;
3497
+ this.onSessionCatalogChanged?.();
3149
3498
  try {
3150
3499
  entry.events.publish({
3151
3500
  type: "session_metadata_updated",
@@ -3588,20 +3937,20 @@ var BridgeClient = class {
3588
3937
  }
3589
3938
  let realTarget = params.path;
3590
3939
  try {
3591
- realTarget = await fs.realpath(params.path);
3940
+ realTarget = await fs2.realpath(params.path);
3592
3941
  } catch (err) {
3593
3942
  const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
3594
3943
  if (code !== "ENOENT") throw err;
3595
3944
  try {
3596
- const linkTarget = await fs.readlink(params.path);
3597
- realTarget = path.resolve(path.dirname(params.path), linkTarget);
3945
+ const linkTarget = await fs2.readlink(params.path);
3946
+ realTarget = path2.resolve(path2.dirname(params.path), linkTarget);
3598
3947
  } catch {
3599
3948
  }
3600
3949
  }
3601
- const tmp = `${realTarget}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
3950
+ const tmp = `${realTarget}.${process.pid}.${Date.now()}.${randomUUID2()}.tmp`;
3602
3951
  let preserveMode;
3603
3952
  try {
3604
- const targetStat = await fs.stat(realTarget);
3953
+ const targetStat = await fs2.stat(realTarget);
3605
3954
  preserveMode = {
3606
3955
  mode: targetStat.mode & 4095,
3607
3956
  uid: targetStat.uid,
@@ -3612,20 +3961,20 @@ var BridgeClient = class {
3612
3961
  if (code !== "ENOENT") throw err;
3613
3962
  }
3614
3963
  try {
3615
- await fs.writeFile(tmp, params.content, {
3964
+ await fs2.writeFile(tmp, params.content, {
3616
3965
  encoding: "utf8",
3617
3966
  flag: "wx",
3618
3967
  mode: preserveMode?.mode ?? 384
3619
3968
  });
3620
3969
  if (preserveMode) {
3621
- await fs.chmod(tmp, preserveMode.mode).catch(() => {
3970
+ await fs2.chmod(tmp, preserveMode.mode).catch(() => {
3622
3971
  });
3623
- await fs.chown(tmp, preserveMode.uid, preserveMode.gid).catch(() => {
3972
+ await fs2.chown(tmp, preserveMode.uid, preserveMode.gid).catch(() => {
3624
3973
  });
3625
3974
  }
3626
- await fs.rename(tmp, realTarget);
3975
+ await fs2.rename(tmp, realTarget);
3627
3976
  } catch (err) {
3628
- await fs.unlink(tmp).catch(() => {
3977
+ await fs2.unlink(tmp).catch(() => {
3629
3978
  });
3630
3979
  throw err;
3631
3980
  }
@@ -3655,7 +4004,7 @@ var BridgeClient = class {
3655
4004
  );
3656
4005
  }
3657
4006
  const READ_FILE_SIZE_CAP = 100 * 1024 * 1024;
3658
- const stats = await fs.stat(params.path);
4007
+ const stats = await fs2.stat(params.path);
3659
4008
  if (!stats.isFile()) {
3660
4009
  throw new Error(
3661
4010
  `readTextFile: ${params.path} is not a regular file (reported as ${describeStatKind(stats)}). Pipe / device / proc-like inputs can produce unbounded data and aren't supported by the bridge fs proxy.`
@@ -3666,7 +4015,7 @@ var BridgeClient = class {
3666
4015
  `readTextFile: ${params.path} is ${stats.size} bytes, exceeds the ${READ_FILE_SIZE_CAP}-byte daemon cap. Tail/grep externally and feed the relevant slice instead.`
3667
4016
  );
3668
4017
  }
3669
- const content = await fs.readFile(params.path, "utf8");
4018
+ const content = await fs2.readFile(params.path, "utf8");
3670
4019
  if (typeof params.line === "number" || typeof params.limit === "number") {
3671
4020
  const startLine = params.line ?? 1;
3672
4021
  const start = startLine > 0 ? startLine - 1 : 0;
@@ -3868,8 +4217,8 @@ var GenerationStreamQueue = class {
3868
4217
  // packages/acp-bridge/src/sessionArtifacts.ts
3869
4218
  init_esbuild_shims();
3870
4219
  import { createHash } from "node:crypto";
3871
- import { constants as fsConstants, promises as fs2 } from "node:fs";
3872
- import path2 from "node:path";
4220
+ import { constants as fsConstants, promises as fs3 } from "node:fs";
4221
+ import path3 from "node:path";
3873
4222
  var SOURCE_RESERVATIONS = {
3874
4223
  tool: 100,
3875
4224
  client: 50,
@@ -4940,7 +5289,7 @@ var SessionArtifactStore = class {
4940
5289
  }
4941
5290
  getRealWorkspaceCwd() {
4942
5291
  if (!this.realWorkspaceCwdPromise) {
4943
- const promise = fs2.realpath(this.workspaceCwd).catch((error) => {
5292
+ const promise = fs3.realpath(this.workspaceCwd).catch((error) => {
4944
5293
  if (this.realWorkspaceCwdPromise === promise) {
4945
5294
  this.realWorkspaceCwdPromise = void 0;
4946
5295
  }
@@ -5060,8 +5409,15 @@ function mergeBatchArtifact(existing, next) {
5060
5409
  delete merged.workspacePath;
5061
5410
  return merged;
5062
5411
  }
5412
+ const refreshDisplay = existing.storage === "workspace" && next.storage === "workspace" && shouldRefreshWorkspaceDisplay(next, existing);
5063
5413
  return {
5064
5414
  ...existing,
5415
+ title: refreshDisplay ? next.title : existing.title,
5416
+ description: refreshDisplay ? next.description ?? existing.description : existing.description,
5417
+ toolName: refreshDisplay ? next.toolName : existing.toolName,
5418
+ source: refreshDisplay ? next.source : existing.source,
5419
+ hookEventName: refreshDisplay ? next.hookEventName : existing.hookEventName,
5420
+ toolCallId: refreshDisplay ? next.toolCallId : existing.toolCallId,
5065
5421
  status: next.status,
5066
5422
  sizeBytes: mergeSizeBytes(existing, next),
5067
5423
  metadata: mergeMetadata(existing, next),
@@ -5108,6 +5464,13 @@ function mergeArtifact(existing, incoming) {
5108
5464
  next.description = incoming.description;
5109
5465
  delete next.workspacePath;
5110
5466
  delete next.hideWorkspacePath;
5467
+ } else if (existing.storage === "workspace" && incoming.storage === "workspace" && shouldRefreshWorkspaceDisplay(incoming, existing)) {
5468
+ next.title = incoming.title;
5469
+ next.description = incoming.description ?? existing.description;
5470
+ next.toolCallId = incoming.toolCallId;
5471
+ next.toolName = incoming.toolName;
5472
+ next.source = incoming.source;
5473
+ next.hookEventName = incoming.hookEventName;
5111
5474
  }
5112
5475
  const changed = !publicArtifactsEqual(
5113
5476
  toPublicArtifact(existing),
@@ -5123,6 +5486,16 @@ function shouldRecordEphemeralUnpin(existing, incoming) {
5123
5486
  return incoming.retentionExplicit && incoming.retention === "ephemeral" && (existing.retention !== "ephemeral" || existing.persistedAt !== void 0 || existing.durableTombstoneRequired === true);
5124
5487
  }
5125
5488
  __name(shouldRecordEphemeralUnpin, "shouldRecordEphemeralUnpin");
5489
+ function shouldRefreshWorkspaceDisplay(incoming, existing) {
5490
+ if (incoming.toolName === "record_artifact" && incoming.source !== "hook") {
5491
+ return true;
5492
+ }
5493
+ if (!incoming.toolName) {
5494
+ return true;
5495
+ }
5496
+ return incoming.toolName === existing.toolName && incoming.source === existing.source && incoming.hookEventName === existing.hookEventName;
5497
+ }
5498
+ __name(shouldRefreshWorkspaceDisplay, "shouldRefreshWorkspaceDisplay");
5126
5499
  function publicArtifactsEqual(a, b) {
5127
5500
  return a.id === b.id && a.kind === b.kind && a.storage === b.storage && a.source === b.source && a.status === b.status && a.title === b.title && a.description === b.description && a.workspacePath === b.workspacePath && a.managedId === b.managedId && a.url === b.url && a.mimeType === b.mimeType && a.sizeBytes === b.sizeBytes && metadataEqual(a.metadata, b.metadata) && a.retention === b.retention && a.restoreState === b.restoreState && a.persistenceWarning === b.persistenceWarning && a.persistedAt === b.persistedAt && a.clientRetained === b.clientRetained && a.createdAt === b.createdAt && a.updatedAt === b.updatedAt && a.toolCallId === b.toolCallId && a.toolName === b.toolName && a.hookEventName === b.hookEventName && a.clientId === b.clientId;
5128
5501
  }
@@ -5575,7 +5948,7 @@ function buildIdentityKey(input) {
5575
5948
  }
5576
5949
  __name(buildIdentityKey, "buildIdentityKey");
5577
5950
  function managedIdForWorkspacePath(workspaceCwd, workspacePath) {
5578
- return createHash("sha1").update(path2.resolve(workspaceCwd, workspacePath)).digest("hex").slice(0, 16);
5951
+ return createHash("sha1").update(path3.resolve(workspaceCwd, workspacePath)).digest("hex").slice(0, 16);
5579
5952
  }
5580
5953
  __name(managedIdForWorkspacePath, "managedIdForWorkspacePath");
5581
5954
  function normalizeString(value, field, maxLength, required) {
@@ -5622,7 +5995,7 @@ __name(normalizeString, "normalizeString");
5622
5995
  function normalizeManagedId(value) {
5623
5996
  const managedId = normalizeString(value, "managedId", 200, false);
5624
5997
  if (!managedId) return void 0;
5625
- if (managedId.includes("/") || managedId.includes("\\") || managedId.includes("..") || path2.isAbsolute(managedId) || path2.win32.isAbsolute(managedId)) {
5998
+ if (managedId.includes("/") || managedId.includes("\\") || managedId.includes("..") || path3.isAbsolute(managedId) || path3.win32.isAbsolute(managedId)) {
5626
5999
  throw new SessionArtifactValidationError(
5627
6000
  "managedId must be an opaque managed resource id",
5628
6001
  "managedId"
@@ -5656,21 +6029,21 @@ function hasUnsafeDisplayPayload(value) {
5656
6029
  __name(hasUnsafeDisplayPayload, "hasUnsafeDisplayPayload");
5657
6030
  function normalizeWorkspacePath(raw, workspaceCwd) {
5658
6031
  const trimmed = normalizeString(raw, "workspacePath", 500, true);
5659
- if (path2.isAbsolute(trimmed)) {
6032
+ if (path3.isAbsolute(trimmed)) {
5660
6033
  throw new SessionArtifactValidationError(
5661
6034
  "workspacePath must be relative to the workspace",
5662
6035
  "workspacePath"
5663
6036
  );
5664
6037
  }
5665
- const absolute = path2.resolve(workspaceCwd, trimmed);
5666
- const relative = path2.relative(workspaceCwd, absolute);
6038
+ const absolute = path3.resolve(workspaceCwd, trimmed);
6039
+ const relative = path3.relative(workspaceCwd, absolute);
5667
6040
  if (!relative || isOutsidePath(relative)) {
5668
6041
  throw new SessionArtifactValidationError(
5669
6042
  "workspacePath must stay inside the workspace",
5670
6043
  "workspacePath"
5671
6044
  );
5672
6045
  }
5673
- return relative.split(path2.sep).join("/");
6046
+ return relative.split(path3.sep).join("/");
5674
6047
  }
5675
6048
  __name(normalizeWorkspacePath, "normalizeWorkspacePath");
5676
6049
  function normalizeArtifactUrl(raw, allowFile) {
@@ -5871,7 +6244,7 @@ __name(normalizeSizeBytes, "normalizeSizeBytes");
5871
6244
  function inferKind(input) {
5872
6245
  if (input.storage === "published") return "html";
5873
6246
  if (input.url) return "link";
5874
- const ext = input.workspacePath ? path2.extname(input.workspacePath).toLowerCase() : "";
6247
+ const ext = input.workspacePath ? path3.extname(input.workspacePath).toLowerCase() : "";
5875
6248
  if (ext === ".html" || ext === ".htm") return "html";
5876
6249
  if ([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"].includes(ext)) {
5877
6250
  return "image";
@@ -5885,15 +6258,15 @@ function inferKind(input) {
5885
6258
  __name(inferKind, "inferKind");
5886
6259
  async function getWorkspaceStatus(workspacePath, realWorkspaceCwd, expected, options = { hashContent: true }) {
5887
6260
  const realWorkspace = await realWorkspaceCwd;
5888
- const absolutePath = path2.resolve(realWorkspace, workspacePath);
6261
+ const absolutePath = path3.resolve(realWorkspace, workspacePath);
5889
6262
  try {
5890
- const realPath = await fs2.realpath(absolutePath);
5891
- const relative = path2.relative(realWorkspace, realPath);
6263
+ const realPath = await fs3.realpath(absolutePath);
6264
+ const relative = path3.relative(realWorkspace, realPath);
5892
6265
  if (!relative || isOutsidePath(relative)) {
5893
6266
  return { status: "missing", escaped: true };
5894
6267
  }
5895
- const preOpenStat = await fs2.lstat(realPath);
5896
- const handle = await fs2.open(
6268
+ const preOpenStat = await fs3.lstat(realPath);
6269
+ const handle = await fs3.open(
5897
6270
  realPath,
5898
6271
  fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW
5899
6272
  );
@@ -6007,14 +6380,14 @@ function isWorkspaceContentChanged(artifact, status) {
6007
6380
  __name(isWorkspaceContentChanged, "isWorkspaceContentChanged");
6008
6381
  async function danglingSymlinkEscapesWorkspace(absolutePath, realWorkspace) {
6009
6382
  try {
6010
- const stat = await fs2.lstat(absolutePath);
6383
+ const stat = await fs3.lstat(absolutePath);
6011
6384
  if (!stat.isSymbolicLink()) {
6012
6385
  return false;
6013
6386
  }
6014
- const target = await fs2.readlink(absolutePath);
6015
- const parent = await fs2.realpath(path2.dirname(absolutePath));
6016
- const targetPath = path2.resolve(parent, target);
6017
- return isOutsidePath(path2.relative(realWorkspace, targetPath));
6387
+ const target = await fs3.readlink(absolutePath);
6388
+ const parent = await fs3.realpath(path3.dirname(absolutePath));
6389
+ const targetPath = path3.resolve(parent, target);
6390
+ return isOutsidePath(path3.relative(realWorkspace, targetPath));
6018
6391
  } catch (error) {
6019
6392
  if (isNotFoundError(error)) {
6020
6393
  return false;
@@ -6045,7 +6418,7 @@ function isNoFollowSymlinkError(error) {
6045
6418
  }
6046
6419
  __name(isNoFollowSymlinkError, "isNoFollowSymlinkError");
6047
6420
  function isOutsidePath(relative) {
6048
- return relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative);
6421
+ return relative === ".." || relative.startsWith(`..${path3.sep}`) || path3.isAbsolute(relative);
6049
6422
  }
6050
6423
  __name(isOutsidePath, "isOutsidePath");
6051
6424
 
@@ -6678,13 +7051,20 @@ function broadcastPromptCancelledOnce(entry, sessionId, promptId, originatorClie
6678
7051
  __name(broadcastPromptCancelledOnce, "broadcastPromptCancelledOnce");
6679
7052
  function broadcastTurnComplete(entry, sessionId, promptResult, promptId, originatorClientId, mutateTurnState) {
6680
7053
  try {
7054
+ const meta = promptResult["_meta"] && typeof promptResult["_meta"] === "object" ? promptResult["_meta"] : void 0;
7055
+ const rawBranchPoint = meta?.["qwen.branchPoint"] && typeof meta["qwen.branchPoint"] === "object" ? meta["qwen.branchPoint"] : void 0;
7056
+ const branchPoint = promptResult.stopReason === "end_turn" && typeof rawBranchPoint?.["assistantRecordUuid"] === "string" && CHAT_RECORD_UUID_RE.test(rawBranchPoint["assistantRecordUuid"]) && typeof rawBranchPoint["checkpointUuid"] === "string" && CHAT_RECORD_UUID_RE.test(rawBranchPoint["checkpointUuid"]) ? {
7057
+ assistantRecordUuid: rawBranchPoint["assistantRecordUuid"],
7058
+ checkpointUuid: rawBranchPoint["checkpointUuid"]
7059
+ } : void 0;
6681
7060
  const published = entry.events.publish({
6682
7061
  type: "turn_complete",
6683
7062
  ...promptId ? { promptId } : {},
6684
7063
  data: {
6685
7064
  sessionId,
6686
7065
  stopReason: promptResult.stopReason ?? "end_turn",
6687
- ...promptId ? { promptId } : {}
7066
+ ...promptId ? { promptId } : {},
7067
+ ...branchPoint ? { branchPoint } : {}
6688
7068
  },
6689
7069
  ...originatorClientId ? { originatorClientId } : {}
6690
7070
  });
@@ -6845,6 +7225,57 @@ function broadcastTurnError(entry, sessionId, err, promptId, originatorClientId,
6845
7225
  }
6846
7226
  }
6847
7227
  __name(broadcastTurnError, "broadcastTurnError");
7228
+ var TERMINAL_TURN_STATUS_OVERLAY_LIMIT = 64;
7229
+ function truncateTurnText(text) {
7230
+ const truncated = text.length > TURN_RESULT_TEXT_MAX_CHARS;
7231
+ return {
7232
+ text: truncated ? text.slice(0, TURN_RESULT_TEXT_MAX_CHARS) : text,
7233
+ truncated
7234
+ };
7235
+ }
7236
+ __name(truncateTurnText, "truncateTurnText");
7237
+ function rememberTerminalTurnStatus(entry, pending, terminal) {
7238
+ const promptText = truncateTurnText(pending.text);
7239
+ const shared = {
7240
+ sessionId: entry.sessionId,
7241
+ promptId: pending.promptId,
7242
+ promptText: promptText.text,
7243
+ ...promptText.truncated ? { promptTextTruncated: true } : {},
7244
+ queuedAt: pending.queuedAt,
7245
+ ...pending.startedAt !== void 0 ? { startedAt: pending.startedAt } : {},
7246
+ endedAt: Date.now(),
7247
+ ...pending.originatorClientId !== void 0 ? { originatorClientId: pending.originatorClientId } : {}
7248
+ };
7249
+ const status = terminal.kind === "complete" ? {
7250
+ ...shared,
7251
+ state: terminal.result.stopReason === "cancelled" ? "cancelled" : "completed",
7252
+ ...terminal.result.stopReason !== void 0 ? { stopReason: terminal.result.stopReason } : {}
7253
+ } : terminal.kind === "cancelled" ? { ...shared, state: "cancelled", stopReason: "cancelled" } : {
7254
+ ...shared,
7255
+ state: "error",
7256
+ error: normalizeTurnResultError(terminal.err)
7257
+ };
7258
+ entry.terminalTurnStatuses.set(pending.promptId, status);
7259
+ entry.enrichedTerminalPromptIds.delete(pending.promptId);
7260
+ while (entry.terminalTurnStatuses.size > TERMINAL_TURN_STATUS_OVERLAY_LIMIT) {
7261
+ const oldest2 = entry.terminalTurnStatuses.keys().next().value;
7262
+ if (oldest2 === void 0) break;
7263
+ entry.terminalTurnStatuses.delete(oldest2);
7264
+ entry.enrichedTerminalPromptIds.delete(oldest2);
7265
+ }
7266
+ }
7267
+ __name(rememberTerminalTurnStatus, "rememberTerminalTurnStatus");
7268
+ function rememberEnrichedTerminalTurnStatus(entry, promptId, status) {
7269
+ entry.terminalTurnStatuses.set(promptId, status);
7270
+ entry.enrichedTerminalPromptIds.add(promptId);
7271
+ while (entry.terminalTurnStatuses.size > TERMINAL_TURN_STATUS_OVERLAY_LIMIT) {
7272
+ const oldest2 = entry.terminalTurnStatuses.keys().next().value;
7273
+ if (oldest2 === void 0) break;
7274
+ entry.terminalTurnStatuses.delete(oldest2);
7275
+ entry.enrichedTerminalPromptIds.delete(oldest2);
7276
+ }
7277
+ }
7278
+ __name(rememberEnrichedTerminalTurnStatus, "rememberEnrichedTerminalTurnStatus");
6848
7279
  function publishPromptTerminal(entry, pendingEntry, terminal) {
6849
7280
  if (pendingEntry.terminalPublished) {
6850
7281
  writeServeDebugLine(
@@ -6853,6 +7284,7 @@ function publishPromptTerminal(entry, pendingEntry, terminal) {
6853
7284
  return;
6854
7285
  }
6855
7286
  pendingEntry.terminalPublished = true;
7287
+ rememberTerminalTurnStatus(entry, pendingEntry, terminal);
6856
7288
  const originatorClientId = pendingEntry.originatorClientId;
6857
7289
  const mutateTurnState = pendingEntry.state === "running";
6858
7290
  if (!mutateTurnState && entry.turnErrorEvent) {
@@ -6920,16 +7352,109 @@ function extractPromptText(prompt) {
6920
7352
  if (!Array.isArray(prompt)) return "";
6921
7353
  let hasImage = false;
6922
7354
  for (const block of prompt) {
6923
- if (block["type"] === "image") {
7355
+ const record = block;
7356
+ if (record["type"] === "image") {
6924
7357
  hasImage = true;
6925
7358
  }
6926
- if (block["type"] === "text" && typeof block["text"] === "string" && block["text"].length > 0) {
6927
- return block["text"];
7359
+ if (record["type"] === "text" && typeof record["text"] === "string" && record["text"].length > 0) {
7360
+ return record["text"];
6928
7361
  }
6929
7362
  }
6930
7363
  return hasImage ? "[image]" : "";
6931
7364
  }
6932
7365
  __name(extractPromptText, "extractPromptText");
7366
+ function liveTurnStatus(sessionId, pending) {
7367
+ const promptText = truncateTurnText(pending.text);
7368
+ return {
7369
+ sessionId,
7370
+ state: pending.state === "running" ? "running" : "queued",
7371
+ promptId: pending.promptId,
7372
+ promptText: promptText.text,
7373
+ ...promptText.truncated ? { promptTextTruncated: true } : {},
7374
+ queuedAt: pending.queuedAt,
7375
+ ...pending.startedAt !== void 0 ? { startedAt: pending.startedAt } : {},
7376
+ ...pending.originatorClientId !== void 0 ? { originatorClientId: pending.originatorClientId } : {}
7377
+ };
7378
+ }
7379
+ __name(liveTurnStatus, "liveTurnStatus");
7380
+ function findLiveTurnStatus(entry, promptId) {
7381
+ const live = entry.pendingPromptList.filter(
7382
+ (pending) => !pending.terminalPublished && (!pending.removed || pending.state === "running")
7383
+ );
7384
+ if (promptId !== void 0) {
7385
+ const match = live.find((pending) => pending.promptId === promptId);
7386
+ return match ? liveTurnStatus(entry.sessionId, match) : void 0;
7387
+ }
7388
+ const running = live.find((pending) => pending.state === "running");
7389
+ if (running) return liveTurnStatus(entry.sessionId, running);
7390
+ const queued = live.find((pending) => pending.state === "queued");
7391
+ return queued ? liveTurnStatus(entry.sessionId, queued) : void 0;
7392
+ }
7393
+ __name(findLiveTurnStatus, "findLiveTurnStatus");
7394
+ function settledTurnStatus(sessionId, record) {
7395
+ return {
7396
+ sessionId,
7397
+ state: record.state,
7398
+ promptId: record.promptId,
7399
+ ...record.stopReason !== void 0 ? { stopReason: record.stopReason } : {},
7400
+ ...record.error !== void 0 ? { error: record.error } : {},
7401
+ ...record.startedAt !== void 0 ? { startedAt: record.startedAt } : {},
7402
+ endedAt: record.endedAt,
7403
+ ...record.promptText !== void 0 ? { promptText: record.promptText } : {},
7404
+ ...record.promptTextTruncated !== void 0 ? { promptTextTruncated: record.promptTextTruncated } : {},
7405
+ ...record.resultText !== void 0 ? { resultText: record.resultText } : {},
7406
+ ...record.resultTruncated !== void 0 ? { resultTruncated: record.resultTruncated } : {},
7407
+ ...record.resultTruncated === true ? { resultCode: record.resultCode ?? TURN_RESULT_CODE_TEXT_TRUNCATED } : {},
7408
+ ...record.originatorClientId !== void 0 ? { originatorClientId: record.originatorClientId } : {}
7409
+ };
7410
+ }
7411
+ __name(settledTurnStatus, "settledTurnStatus");
7412
+ function enrichTerminalTurnStatus(terminal, persisted) {
7413
+ return {
7414
+ ...terminal,
7415
+ // The bridge's display projection is trusted; the child-recorded text
7416
+ // only backfills when the terminal has none, so hidden channel context
7417
+ // the child derived from raw blocks never replaces it.
7418
+ ...terminal.promptText === void 0 && persisted.promptText !== void 0 ? { promptText: persisted.promptText } : {},
7419
+ ...terminal.promptTextTruncated === void 0 && persisted.promptTextTruncated !== void 0 ? { promptTextTruncated: persisted.promptTextTruncated } : {},
7420
+ ...persisted.resultText !== void 0 ? { resultText: persisted.resultText } : {},
7421
+ ...persisted.resultTruncated !== void 0 ? { resultTruncated: persisted.resultTruncated } : {},
7422
+ ...persisted.resultCode !== void 0 ? { resultCode: persisted.resultCode } : {},
7423
+ ...terminal.originatorClientId === void 0 && persisted.originatorClientId !== void 0 ? { originatorClientId: persisted.originatorClientId } : {}
7424
+ };
7425
+ }
7426
+ __name(enrichTerminalTurnStatus, "enrichTerminalTurnStatus");
7427
+ function mergeTerminalWithPersisted(terminal, persisted) {
7428
+ if (terminal.state === "error" && persisted.state !== "error") {
7429
+ return {
7430
+ ...persisted,
7431
+ ...terminal.promptText !== void 0 ? { promptText: terminal.promptText } : {},
7432
+ ...terminal.promptTextTruncated !== void 0 ? { promptTextTruncated: terminal.promptTextTruncated } : {}
7433
+ };
7434
+ }
7435
+ return enrichTerminalTurnStatus(terminal, persisted);
7436
+ }
7437
+ __name(mergeTerminalWithPersisted, "mergeTerminalWithPersisted");
7438
+ function latestTerminalTurnStatus(entry) {
7439
+ let latest;
7440
+ for (const status of entry.terminalTurnStatuses.values()) {
7441
+ if ((status.endedAt ?? 0) >= (latest?.endedAt ?? 0)) latest = status;
7442
+ }
7443
+ return latest;
7444
+ }
7445
+ __name(latestTerminalTurnStatus, "latestTerminalTurnStatus");
7446
+ function extractMediaBlocks(prompt) {
7447
+ if (!Array.isArray(prompt)) return void 0;
7448
+ const media = [];
7449
+ for (const block of prompt) {
7450
+ if (!block || typeof block !== "object") continue;
7451
+ if (block.type === "image" || block.type === "audio") {
7452
+ media.push(block);
7453
+ }
7454
+ }
7455
+ return media.length > 0 ? media : void 0;
7456
+ }
7457
+ __name(extractMediaBlocks, "extractMediaBlocks");
6933
7458
  var DEFAULT_INIT_TIMEOUT_MS = 1e4;
6934
7459
  var PERSIST_TIMEOUT_MS = 5e3;
6935
7460
  var MAX_PARENT_PERSIST_ATTEMPTS = 3;
@@ -6947,13 +7472,25 @@ var MAX_EMPTY_TRANSCRIPT_PAGES = 20;
6947
7472
  var SHELL_COMMAND_TIMEOUT_MS = 12e4;
6948
7473
  var MAX_SHELL_OUTPUT_FOR_HISTORY = 1e4;
6949
7474
  var MAX_MID_TURN_QUEUE_DEPTH = 20;
7475
+ var inlineMediaBlockBytes = /* @__PURE__ */ __name((blocks) => {
7476
+ let total = 0;
7477
+ for (const block of blocks) {
7478
+ if (block.type === "image" && "data" in block) {
7479
+ total += block.data.length;
7480
+ }
7481
+ }
7482
+ return total;
7483
+ }, "inlineMediaBlockBytes");
6950
7484
  var DEFAULT_MAX_SESSIONS = 32;
6951
7485
  var DEFAULT_MAX_PENDING_PROMPTS_PER_SESSION = 5;
7486
+ var CHAT_RECORD_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
6952
7487
  var MAX_EVENT_RING_SIZE = 1e6;
6953
7488
  var DEFAULT_PERMISSION_TIMEOUT_MS = 5 * 60 * 1e3;
6954
7489
  var DEFAULT_MAX_PENDING_PER_SESSION = 64;
6955
7490
  var DEFAULT_SESSION_REAP_INTERVAL_MS = 6e4;
6956
7491
  var DEFAULT_SESSION_IDLE_TIMEOUT_MS = 30 * 6e4;
7492
+ var MAX_RETAINED_SESSION_MEDIA_BYTES = 512 * 1024 * 1024;
7493
+ var RETAINED_SESSION_MEDIA_TTL_MS = 3 * 60 * 6e4;
6957
7494
  function createAcpSessionBridge(opts) {
6958
7495
  let liveScreenContextCaptureHandler;
6959
7496
  let liveTaskToolRequestHandler;
@@ -7012,7 +7549,17 @@ function createAcpSessionBridge(opts) {
7012
7549
  );
7013
7550
  }
7014
7551
  }, "releaseFreshSessionReservation");
7552
+ const sessionCatalogGeneration = randomUUID3();
7553
+ let sessionCatalogRevision = 0;
7554
+ const getSessionCatalogVersion = /* @__PURE__ */ __name(() => ({
7555
+ generation: sessionCatalogGeneration,
7556
+ revision: sessionCatalogRevision
7557
+ }), "getSessionCatalogVersion");
7558
+ const markSessionCatalogChanged = /* @__PURE__ */ __name(() => {
7559
+ sessionCatalogRevision += 1;
7560
+ }, "markSessionCatalogChanged");
7015
7561
  const emitSessionLifecycle = /* @__PURE__ */ __name((event) => {
7562
+ markSessionCatalogChanged();
7016
7563
  try {
7017
7564
  opts.sessionLifecycle?.(event);
7018
7565
  } catch (err) {
@@ -7082,7 +7629,7 @@ function createAcpSessionBridge(opts) {
7082
7629
  } else {
7083
7630
  maxPendingPromptsPerSession = maxPendingPromptsRaw;
7084
7631
  }
7085
- if (!path3.isAbsolute(opts.boundWorkspace)) {
7632
+ if (!path4.isAbsolute(opts.boundWorkspace)) {
7086
7633
  throw new TypeError(
7087
7634
  `Invalid boundWorkspace: "${opts.boundWorkspace}". Must be an absolute path.`
7088
7635
  );
@@ -7105,6 +7652,7 @@ function createAcpSessionBridge(opts) {
7105
7652
  DEFAULT_SESSION_IDLE_TIMEOUT_MS
7106
7653
  );
7107
7654
  let sessionReaper;
7655
+ let mediaSweeper;
7108
7656
  let lastActivityTimestamp = null;
7109
7657
  let activePromptCounter = 0;
7110
7658
  function touchActivity() {
@@ -7158,12 +7706,15 @@ function createAcpSessionBridge(opts) {
7158
7706
  }
7159
7707
  __name(isClosingOrAuthorizingClose, "isClosingOrAuthorizingClose");
7160
7708
  async function maybeCloseIdleSession(entry, reason) {
7161
- if (!entryIsAutoCloseCandidate(entry)) return;
7162
- if (entry.spawnOwnerWantedKill && entry.attachCount === 0) {
7709
+ if (byId.get(entry.sessionId) === entry && !isClosingOrAuthorizingClose(entry) && entry.spawnOwnerWantedKill && entry.attachCount === 0) {
7710
+ writeStderrLine(
7711
+ `qwen serve: completing deferred kill of session ${JSON.stringify(entry.sessionId)} (${reason})`
7712
+ );
7163
7713
  await bridgeApi.killSession(entry.sessionId).catch(() => {
7164
7714
  });
7165
7715
  return;
7166
7716
  }
7717
+ if (!entryIsAutoCloseCandidate(entry)) return;
7167
7718
  if (entry.clientIds.size > 0) return;
7168
7719
  await closeIfChildUnheld(entry, {
7169
7720
  trigger: reason,
@@ -7200,7 +7751,10 @@ function createAcpSessionBridge(opts) {
7200
7751
  const response = await withTimeout(
7201
7752
  entry.connection.extMethod(SERVE_CONTROL_EXT_METHODS.sessionClose, {
7202
7753
  sessionId: entry.sessionId,
7203
- [ACTIVE_WORK_CLOSE_IF_UNHELD_PARAM]: true
7754
+ [ACTIVE_WORK_CLOSE_IF_UNHELD_PARAM]: true,
7755
+ drainTimeoutMs: sessionCloseDrainBudgetMs(
7756
+ ACTIVE_WORK_CLOSE_TIMEOUT_MS
7757
+ )
7204
7758
  }),
7205
7759
  ACTIVE_WORK_CLOSE_TIMEOUT_MS,
7206
7760
  SERVE_CONTROL_EXT_METHODS.sessionClose
@@ -7415,9 +7969,32 @@ function createAcpSessionBridge(opts) {
7415
7969
  }
7416
7970
  }
7417
7971
  __name(withWorkspaceControl, "withWorkspaceControl");
7972
+ function sweepRetainedMedia() {
7973
+ const now = Date.now();
7974
+ for (const [id] of retainedMedia) {
7975
+ if (byId.has(id) || inFlightRestores.has(id)) continue;
7976
+ const detachedAt = retainedMediaDetachedAt.get(id);
7977
+ if (detachedAt === void 0 || now - detachedAt < RETAINED_SESSION_MEDIA_TTL_MS) {
7978
+ continue;
7979
+ }
7980
+ void releaseSessionMedia(id).catch((error) => {
7981
+ writeStderrLine(
7982
+ `qwen serve: failed to release retained media for ${JSON.stringify(id)}: ${error instanceof Error ? error.message : String(error)}`
7983
+ );
7984
+ });
7985
+ }
7986
+ }
7987
+ __name(sweepRetainedMedia, "sweepRetainedMedia");
7418
7988
  function startSessionReaper() {
7419
- if (sessionReapIntervalMs <= 0 || sessionIdleTimeoutMs <= 0) {
7420
- writeStderrLine("qwen serve: session reaper disabled");
7989
+ if (sessionReapIntervalMs <= 0) {
7990
+ writeStderrLine(
7991
+ `qwen serve: session reaper disabled; retained media sweep runs every ${DEFAULT_SESSION_REAP_INTERVAL_MS}ms`
7992
+ );
7993
+ mediaSweeper = setInterval(() => {
7994
+ if (shuttingDown) return;
7995
+ sweepRetainedMedia();
7996
+ }, DEFAULT_SESSION_REAP_INTERVAL_MS);
7997
+ mediaSweeper.unref();
7421
7998
  return;
7422
7999
  }
7423
8000
  writeStderrLine(
@@ -7427,6 +8004,7 @@ function createAcpSessionBridge(opts) {
7427
8004
  if (shuttingDown) return;
7428
8005
  const now = Date.now();
7429
8006
  for (const [id, entry] of byId) {
8007
+ if (sessionIdleTimeoutMs <= 0) break;
7430
8008
  if (!entryIsAutoCloseCandidate(entry)) continue;
7431
8009
  const lastActive = entry.sessionLastSeenAt ?? Date.parse(entry.createdAt);
7432
8010
  const idle = now - lastActive;
@@ -7439,6 +8017,7 @@ function createAcpSessionBridge(opts) {
7439
8017
  closeReason: "idle_timeout"
7440
8018
  });
7441
8019
  }
8020
+ sweepRetainedMedia();
7442
8021
  }, sessionReapIntervalMs);
7443
8022
  sessionReaper.unref();
7444
8023
  }
@@ -7448,11 +8027,29 @@ function createAcpSessionBridge(opts) {
7448
8027
  clearInterval(sessionReaper);
7449
8028
  sessionReaper = void 0;
7450
8029
  }
8030
+ if (mediaSweeper !== void 0) {
8031
+ clearInterval(mediaSweeper);
8032
+ mediaSweeper = void 0;
8033
+ }
7451
8034
  }
7452
8035
  __name(stopSessionReaper, "stopSessionReaper");
7453
8036
  const aliveChannels = /* @__PURE__ */ new Set();
7454
8037
  let inFlightChannelSpawn;
7455
8038
  const byId = /* @__PURE__ */ new Map();
8039
+ const retainedMedia = /* @__PURE__ */ new Map();
8040
+ const retainedMediaDetachedAt = /* @__PURE__ */ new Map();
8041
+ let mediaPutQueue = Promise.resolve();
8042
+ const retainSessionMedia = /* @__PURE__ */ __name((sessionId, store = retainedMedia.get(sessionId) ?? new SessionMediaStore()) => {
8043
+ retainedMedia.set(sessionId, store);
8044
+ return store;
8045
+ }, "retainSessionMedia");
8046
+ const releaseSessionMedia = /* @__PURE__ */ __name(async (sessionId) => {
8047
+ const store = retainedMedia.get(sessionId);
8048
+ if (!store) return;
8049
+ retainedMedia.delete(sessionId);
8050
+ retainedMediaDetachedAt.delete(sessionId);
8051
+ await store.close();
8052
+ }, "releaseSessionMedia");
7456
8053
  const forwardRunningPromptCancel = /* @__PURE__ */ __name(async (entry, pending, notification) => {
7457
8054
  if (pending.cancelForwardInitial) {
7458
8055
  return pending.cancelForwardInitial;
@@ -7592,7 +8189,7 @@ function createAcpSessionBridge(opts) {
7592
8189
  return limits;
7593
8190
  }, "journalSessionLimits");
7594
8191
  const unregisterJournalGrowthSessionLimits = journalGrowthPolicy !== void 0 && opts.registerJournalGrowthSessionLimits !== void 0 ? opts.registerJournalGrowthSessionLimits(journalSessionLimits) : void 0;
7595
- const createClientId = /* @__PURE__ */ __name(() => `client_${randomUUID2()}`, "createClientId");
8192
+ const createClientId = /* @__PURE__ */ __name(() => `client_${randomUUID3()}`, "createClientId");
7596
8193
  const registerClient = /* @__PURE__ */ __name((entry, requestedClientId) => {
7597
8194
  if (requestedClientId && entry.clientIds.has(requestedClientId)) {
7598
8195
  entry.clientIds.set(
@@ -7651,7 +8248,7 @@ function createAcpSessionBridge(opts) {
7651
8248
  if (inFlightChannelSpawn) return await inFlightChannelSpawn;
7652
8249
  const promise = (async () => {
7653
8250
  const privateParentCapability = randomBytes(32).toString("base64url");
7654
- const acpChannelId = randomUUID2();
8251
+ const acpChannelId = randomUUID3();
7655
8252
  const channel = await telemetry.withSpan(
7656
8253
  "channel.spawn",
7657
8254
  {
@@ -7772,7 +8369,10 @@ function createAcpSessionBridge(opts) {
7772
8369
  const currentInfo = infoRef.current;
7773
8370
  if (!currentInfo) return;
7774
8371
  applyActiveWorkSnapshot(currentInfo, snapshot);
7775
- }
8372
+ },
8373
+ // Child-side automatic title updates change persisted catalog
8374
+ // metadata the bridge never sees; forward the catalog-clock mark.
8375
+ markSessionCatalogChanged
7776
8376
  );
7777
8377
  const rawConnection = new ClientSideConnection(
7778
8378
  () => channel.transportGuard ? createLogSafeAcpClient(client, channel.transportGuard) : client,
@@ -7910,6 +8510,9 @@ function createAcpSessionBridge(opts) {
7910
8510
  touchActivity();
7911
8511
  }
7912
8512
  byId.delete(sid);
8513
+ if (retainedMedia.has(sid)) {
8514
+ retainedMediaDetachedAt.set(sid, Date.now());
8515
+ }
7913
8516
  telemetry.metrics?.sessionLifecycle("die");
7914
8517
  emitSessionLifecycle({
7915
8518
  type: "removed",
@@ -8730,7 +9333,9 @@ function createAcpSessionBridge(opts) {
8730
9333
  SERVE_CONTROL_EXT_METHODS.sessionClose,
8731
9334
  {
8732
9335
  sessionId: entry.sessionId,
8733
- drainTimeoutMs: Math.max(1, Math.floor(initTimeoutMs * 0.8)),
9336
+ drainTimeoutMs: sessionCloseDrainBudgetMs(
9337
+ opts2?.timeoutMs ?? initTimeoutMs
9338
+ ),
8734
9339
  ...opts2?.requireFlush === true ? { requireFlush: true } : {}
8735
9340
  }
8736
9341
  );
@@ -8945,6 +9550,7 @@ function createAcpSessionBridge(opts) {
8945
9550
  workspaceCwd,
8946
9551
  persistence: createSessionArtifactPersistence(ci.connection, sessionId)
8947
9552
  }),
9553
+ media: retainSessionMedia(sessionId),
8948
9554
  recordingDegraded: false,
8949
9555
  closing: false,
8950
9556
  cwdChangeQueue: Promise.resolve(),
@@ -8952,6 +9558,9 @@ function createAcpSessionBridge(opts) {
8952
9558
  pendingPromptCount: 0,
8953
9559
  pendingAgentNotificationCount: 0,
8954
9560
  pendingPromptList: [],
9561
+ terminalTurnStatuses: /* @__PURE__ */ new Map(),
9562
+ enrichedTerminalPromptIds: /* @__PURE__ */ new Set(),
9563
+ rewindGeneration: 0,
8955
9564
  midTurnMessageQueue: [],
8956
9565
  settledMidTurnMessageIds: [],
8957
9566
  promotedMidTurnMessageIds: [],
@@ -9531,7 +10140,7 @@ function createAcpSessionBridge(opts) {
9531
10140
  SERVE_CONTROL_EXT_METHODS.sessionClose,
9532
10141
  {
9533
10142
  sessionId: req.sessionId,
9534
- drainTimeoutMs: Math.max(1, Math.floor(initTimeoutMs * 0.8))
10143
+ drainTimeoutMs: sessionCloseDrainBudgetMs(initTimeoutMs)
9535
10144
  }
9536
10145
  ),
9537
10146
  initTimeoutMs,
@@ -9901,6 +10510,9 @@ function createAcpSessionBridge(opts) {
9901
10510
  const restoreEntry = byId.get(req.sessionId);
9902
10511
  if (restoreEntry?.events === restoreEvents) {
9903
10512
  byId.delete(req.sessionId);
10513
+ if (retainedMedia.has(req.sessionId)) {
10514
+ retainedMediaDetachedAt.set(req.sessionId, Date.now());
10515
+ }
9904
10516
  ci?.sessionIds.delete(req.sessionId);
9905
10517
  emitSessionLifecycle({
9906
10518
  type: "removed",
@@ -10064,6 +10676,15 @@ function createAcpSessionBridge(opts) {
10064
10676
  } catch {
10065
10677
  }
10066
10678
  entry.events.close();
10679
+ if (reason === "last_client_detached" || reason === "idle_timeout") {
10680
+ retainedMediaDetachedAt.set(sessionId, Date.now());
10681
+ } else {
10682
+ await releaseSessionMedia(sessionId).catch((error) => {
10683
+ writeStderrLine(
10684
+ `qwen serve: failed to release media for closed session ${JSON.stringify(sessionId)}: ${error instanceof Error ? error.message : String(error)}`
10685
+ );
10686
+ });
10687
+ }
10067
10688
  if (!agentSessionClosed) {
10068
10689
  try {
10069
10690
  await telemetry.withSpan(
@@ -10092,25 +10713,76 @@ function createAcpSessionBridge(opts) {
10092
10713
  ring.splice(0, ring.length - MID_TURN_RECONCILIATION_RING_SIZE);
10093
10714
  }
10094
10715
  }, "rememberMidTurnId");
10095
- const promoteMidTurnMessage = /* @__PURE__ */ __name((entry, messageId, text, originatorClientId) => {
10096
- void bridgeApi.sendPrompt(
10716
+ const removeQueuedMedia = /* @__PURE__ */ __name((entry, content) => {
10717
+ for (const block of content ?? []) {
10718
+ if (!isSessionMediaReference(block)) continue;
10719
+ void entry.media.remove(block.mediaId).catch((error) => {
10720
+ writeStderrLine(
10721
+ `[session-media] session=${JSON.stringify(entry.sessionId)} failed to remove queued media ${JSON.stringify(block.mediaId)}: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
10722
+ );
10723
+ });
10724
+ }
10725
+ }, "removeQueuedMedia");
10726
+ const promoteMidTurnMessage = /* @__PURE__ */ __name((entry, messageId, text, originatorClientId, content) => {
10727
+ const resolvableBlocks = [];
10728
+ let degraded = 0;
10729
+ for (const block of content ?? []) {
10730
+ try {
10731
+ entry.media.assertReference(block);
10732
+ resolvableBlocks.push(block);
10733
+ } catch (error) {
10734
+ if (!(error instanceof SessionMediaReferenceError)) throw error;
10735
+ degraded += 1;
10736
+ }
10737
+ }
10738
+ let prompt = [
10739
+ ...text ? [{ type: "text", text }] : [],
10740
+ ...resolvableBlocks
10741
+ ];
10742
+ if (degraded > 0) {
10743
+ prompt = withMediaDegradationMarker(prompt);
10744
+ }
10745
+ const context = {
10746
+ promptId: messageId,
10747
+ promotedMidTurn: { originatorClientId },
10748
+ onPromptAdmitted: /* @__PURE__ */ __name(() => {
10749
+ rememberMidTurnId(entry.promotedMidTurnMessageIds, messageId);
10750
+ }, "onPromptAdmitted")
10751
+ };
10752
+ const sendFallback = /* @__PURE__ */ __name(() => bridgeApi.sendPrompt(
10097
10753
  entry.sessionId,
10098
10754
  {
10099
10755
  sessionId: entry.sessionId,
10100
- prompt: [{ type: "text", text }]
10756
+ prompt: withMediaDegradationMarker(
10757
+ text ? [{ type: "text", text }] : []
10758
+ )
10101
10759
  },
10102
10760
  void 0,
10103
- {
10104
- promptId: messageId,
10105
- promotedMidTurn: { originatorClientId },
10106
- // Record the id only once sendPrompt owns its FIFO slot: an
10107
- // admission failure must not land the id in the reconciliation
10108
- // ring, or retries would be acked for a message that never runs.
10109
- onPromptAdmitted: /* @__PURE__ */ __name(() => {
10110
- rememberMidTurnId(entry.promotedMidTurnMessageIds, messageId);
10111
- }, "onPromptAdmitted")
10112
- }
10113
- ).catch((error) => {
10761
+ context
10762
+ ), "sendFallback");
10763
+ let result;
10764
+ try {
10765
+ result = bridgeApi.sendPrompt(
10766
+ entry.sessionId,
10767
+ {
10768
+ sessionId: entry.sessionId,
10769
+ prompt
10770
+ },
10771
+ void 0,
10772
+ context
10773
+ );
10774
+ } catch (error) {
10775
+ try {
10776
+ if (!(error instanceof SessionMediaReferenceError)) throw error;
10777
+ result = sendFallback();
10778
+ } catch (fallbackError) {
10779
+ writeStderrLine(
10780
+ `[mid-turn] session=${JSON.stringify(entry.sessionId)} failed to run promoted message ${JSON.stringify(messageId)}: ${JSON.stringify(fallbackError instanceof Error ? fallbackError.message : String(fallbackError))}`
10781
+ );
10782
+ return;
10783
+ }
10784
+ }
10785
+ void result.catch((error) => {
10114
10786
  writeStderrLine(
10115
10787
  `[mid-turn] session=${JSON.stringify(entry.sessionId)} failed to run promoted message ${JSON.stringify(messageId)}: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
10116
10788
  );
@@ -10430,7 +11102,7 @@ function createAcpSessionBridge(opts) {
10430
11102
  req.branch,
10431
11103
  req.sessionId
10432
11104
  );
10433
- const tracker = effectiveScope === "single" ? workspaceKey : `${workspaceKey}#${randomUUID2()}`;
11105
+ const tracker = effectiveScope === "single" ? workspaceKey : `${workspaceKey}#${randomUUID3()}`;
10434
11106
  inFlightSpawns.set(tracker, promise);
10435
11107
  try {
10436
11108
  return await promise;
@@ -10460,9 +11132,15 @@ function createAcpSessionBridge(opts) {
10460
11132
  )
10461
11133
  );
10462
11134
  }
11135
+ if (!Array.isArray(req.prompt)) {
11136
+ return Promise.reject(
11137
+ RequestError.invalidParams(void 0, "Prompt must be an array")
11138
+ );
11139
+ }
10463
11140
  const promotedMidTurn = context?.promotedMidTurn;
10464
11141
  const isPromotedMidTurn = promotedMidTurn !== void 0;
10465
11142
  const originatorClientId = promotedMidTurn ? promotedMidTurn.originatorClientId : resolveTrustedClientId(entry, context?.clientId);
11143
+ entry.media.assertReferences(req.prompt);
10466
11144
  const modelPrompt = context?.modelPrompt;
10467
11145
  if (modelPrompt !== void 0 && !isValidTrustedModelPrompt(modelPrompt)) {
10468
11146
  throw new TypeError(
@@ -10486,7 +11164,7 @@ function createAcpSessionBridge(opts) {
10486
11164
  promptSlotReleased = true;
10487
11165
  entry.pendingPromptCount = Math.max(0, entry.pendingPromptCount - 1);
10488
11166
  }, "releasePromptSlot");
10489
- const promptId = context?.promptId ?? randomUUID2();
11167
+ const promptId = context?.promptId ?? randomUUID3();
10490
11168
  const invocationContext = Object.freeze({
10491
11169
  version: 1,
10492
11170
  sessionId,
@@ -10519,6 +11197,7 @@ function createAcpSessionBridge(opts) {
10519
11197
  ...originatorClientId !== void 0 ? { originatorClientId } : {},
10520
11198
  ...isPromotedMidTurn ? { promotedMidTurn: true } : {},
10521
11199
  text: pendingText,
11200
+ content: extractMediaBlocks(req.prompt),
10522
11201
  abortController: pendingAbort,
10523
11202
  state: isQueued ? "queued" : "running"
10524
11203
  };
@@ -10611,6 +11290,7 @@ function createAcpSessionBridge(opts) {
10611
11290
  }
10612
11291
  throw new DOMException("Prompt aborted", "AbortError");
10613
11292
  }
11293
+ pendingEntry.startedAt = Date.now();
10614
11294
  if (pendingEntry.state === "queued" || isPromotedMidTurn) {
10615
11295
  if (pendingEntry.state === "queued") {
10616
11296
  delete entry.todoStopGuardAwaitingQueuedPromptOwnerPromptId;
@@ -10638,10 +11318,25 @@ function createAcpSessionBridge(opts) {
10638
11318
  ...context?.clientId ? { "qwen-code.client_id": context.clientId } : {}
10639
11319
  },
10640
11320
  async () => {
11321
+ let dispatchBlocks = req.prompt;
11322
+ let resolvedPrompt;
11323
+ try {
11324
+ resolvedPrompt = await entry.media.resolveContent(dispatchBlocks);
11325
+ } catch (error) {
11326
+ if (!isPromotedMidTurn || !(error instanceof SessionMediaReferenceError)) {
11327
+ throw error;
11328
+ }
11329
+ const perBlock = await entry.media.resolveContentDegrading(dispatchBlocks);
11330
+ dispatchBlocks = perBlock.retainedBlocks;
11331
+ resolvedPrompt = withMediaDegradationMarker(
11332
+ perBlock.resolvedBlocks
11333
+ );
11334
+ }
10641
11335
  const normalized = telemetry.injectPromptContext(
10642
11336
  {
10643
11337
  ...req,
10644
- sessionId
11338
+ sessionId,
11339
+ prompt: resolvedPrompt
10645
11340
  }
10646
11341
  );
10647
11342
  assertLivePromptEntry(sessionId, entry);
@@ -10664,6 +11359,7 @@ function createAcpSessionBridge(opts) {
10664
11359
  delete meta[DAEMON_CHANNEL_DELIVERY_META_KEY];
10665
11360
  delete meta[DAEMON_PROMPT_DISPLAY_TEXT_META_KEY];
10666
11361
  delete meta[DAEMON_MODEL_PROMPT_META_KEY];
11362
+ delete meta[DAEMON_MEDIA_REFERENCES_META_KEY];
10667
11363
  delete meta[CHANNEL_PROMPT_META_KEY];
10668
11364
  if (isRetry) {
10669
11365
  meta[DAEMON_RETRY_META_KEY] = true;
@@ -10680,6 +11376,12 @@ function createAcpSessionBridge(opts) {
10680
11376
  if (modelPrompt !== void 0) {
10681
11377
  meta[DAEMON_MODEL_PROMPT_META_KEY] = modelPrompt;
10682
11378
  }
11379
+ const mediaReferences = dispatchBlocks.filter(
11380
+ isSessionMediaReference
11381
+ );
11382
+ if (mediaReferences.length > 0) {
11383
+ meta[DAEMON_MEDIA_REFERENCES_META_KEY] = mediaReferences;
11384
+ }
10683
11385
  if (context?.channelPrompt === true) {
10684
11386
  meta[CHANNEL_PROMPT_META_KEY] = true;
10685
11387
  }
@@ -10708,7 +11410,10 @@ function createAcpSessionBridge(opts) {
10708
11410
  if (!isRetry && !isContinue) {
10709
11411
  echoPromptToSessionBus(
10710
11412
  entry,
10711
- promptRequest,
11413
+ {
11414
+ ...promptRequest,
11415
+ prompt: dispatchBlocks
11416
+ },
10712
11417
  pendingEntry.promptId,
10713
11418
  originatorClientId,
10714
11419
  channelDisplayText
@@ -10864,7 +11569,8 @@ function createAcpSessionBridge(opts) {
10864
11569
  entry,
10865
11570
  message.messageId,
10866
11571
  message.text,
10867
- message.originatorClientId
11572
+ message.originatorClientId,
11573
+ message.content
10868
11574
  );
10869
11575
  }
10870
11576
  void maybeCloseIdleSession(entry, "prompt_settled");
@@ -11044,60 +11750,87 @@ function createAcpSessionBridge(opts) {
11044
11750
  if (shuttingDown) throw new Error("AcpSessionBridge is shutting down");
11045
11751
  const entry = byId.get(sessionId);
11046
11752
  if (!entry) throw new SessionNotFoundError(sessionId);
11753
+ if (isClosingOrAuthorizingClose(entry)) {
11754
+ throw new SessionNotFoundError(sessionId, "The session is closing");
11755
+ }
11047
11756
  const source = parseSessionSource(req.sourceType, req.sourceId);
11048
11757
  if ("error" in source) {
11049
11758
  throw new InvalidSessionMetadataError("sourceType", source.error);
11050
11759
  }
11051
11760
  const isSideTask = source.sourceType === "side_task";
11052
- let originatorClientId;
11761
+ const restoreBranch = isSideTask || req.atRecordId === void 0;
11053
11762
  if (context?.clientId !== void 0) {
11054
- originatorClientId = resolveTrustedClientId(entry, context.clientId);
11763
+ resolveTrustedClientId(entry, context.clientId);
11055
11764
  }
11056
11765
  const concurrentSideTask = isSideTask && entry.promptActive;
11766
+ if (!isSideTask && (entry.pendingPromptCount > 0 || entry.promptActive)) {
11767
+ throw new BranchWhilePromptActiveError(sessionId);
11768
+ }
11057
11769
  const branchResult = (concurrentSideTask ? Promise.resolve() : entry.promptQueue).then(async () => {
11058
- if (entry.promptActive && !isSideTask) {
11059
- throw new BranchWhilePromptActiveError(sessionId);
11770
+ if (isClosingOrAuthorizingClose(entry) || byId.get(sessionId) !== entry) {
11771
+ throw new SessionNotFoundError(sessionId, "The session is closing");
11060
11772
  }
11061
11773
  assertFreshSessionsAvailable();
11062
- if (byId.size + inFlightSpawns.size + inFlightRestores.size >= maxSessions) {
11063
- throw new SessionLimitExceededError(maxSessions);
11774
+ let admission;
11775
+ if (restoreBranch) {
11776
+ if (byId.size + inFlightSpawns.size + inFlightRestores.size >= maxSessions) {
11777
+ throw new SessionLimitExceededError(maxSessions);
11778
+ }
11779
+ admission = reserveFreshSession({
11780
+ operation: "branch",
11781
+ workspaceCwd: boundWorkspace,
11782
+ sourceSessionId: sessionId
11783
+ });
11064
11784
  }
11065
- const admission = reserveFreshSession({
11066
- operation: "branch",
11067
- workspaceCwd: boundWorkspace,
11068
- sourceSessionId: sessionId
11069
- });
11070
11785
  let admissionReleased = false;
11071
11786
  const releaseAdmissionOnce = /* @__PURE__ */ __name(() => {
11072
- if (admissionReleased) return;
11787
+ if (admissionReleased || !admission) return;
11073
11788
  admissionReleased = true;
11074
11789
  releaseFreshSessionReservation(admission);
11075
11790
  }, "releaseAdmissionOnce");
11076
11791
  try {
11077
- assertLivePromptEntry(sessionId, entry);
11078
- const ci = channelInfoForEntry(entry);
11079
- const result = await withTimeout(
11080
- Promise.race([
11081
- ci.connection.extMethod(
11082
- isSideTask ? SERVE_CONTROL_EXT_METHODS.sessionSideTask : SERVE_CONTROL_EXT_METHODS.sessionBranch,
11083
- {
11084
- sessionId,
11085
- cwd: boundWorkspace,
11086
- name: req.name
11087
- }
11088
- ),
11089
- getTransportClosedReject(entry)
11090
- ]),
11091
- initTimeoutMs,
11092
- isSideTask ? "createSideTaskSession" : "branchSession"
11792
+ const mutation = entry.connection.extMethod(
11793
+ isSideTask ? SERVE_CONTROL_EXT_METHODS.sessionSideTask : SERVE_CONTROL_EXT_METHODS.sessionBranch,
11794
+ {
11795
+ sessionId,
11796
+ cwd: boundWorkspace,
11797
+ name: req.name,
11798
+ ...req.atRecordId !== void 0 ? { atRecordId: req.atRecordId } : {}
11799
+ }
11093
11800
  );
11801
+ let result;
11802
+ try {
11803
+ result = await Promise.race([
11804
+ mutation,
11805
+ getTransportClosedReject(entry)
11806
+ ]);
11807
+ } catch (err) {
11808
+ const data = err?.data;
11809
+ if (!isSideTask && data && typeof data === "object" && data.errorKind === "session_busy") {
11810
+ const msg = err?.message ?? "Branch failed";
11811
+ throw new SessionBusyError(sessionId, msg);
11812
+ }
11813
+ throw err;
11814
+ }
11094
11815
  if (!result || typeof result.newSessionId !== "string") {
11095
11816
  throw new Error(
11096
11817
  `branchSession: agent returned invalid response: ${JSON.stringify(result)}`
11097
11818
  );
11098
11819
  }
11820
+ markSessionCatalogChanged();
11099
11821
  const rawBranchName = result.displayName ?? result.title;
11100
11822
  const branchDisplayName = typeof rawBranchName === "string" ? rawBranchName : result.newSessionId.slice(0, 8);
11823
+ if (!restoreBranch) {
11824
+ return {
11825
+ sessionId: result.newSessionId,
11826
+ displayName: branchDisplayName,
11827
+ forkedFrom: {
11828
+ sessionId,
11829
+ displayName: entry.displayName ?? sessionId.slice(0, 8)
11830
+ }
11831
+ };
11832
+ }
11833
+ const ci = await ensureChannel();
11101
11834
  let restored;
11102
11835
  try {
11103
11836
  const hideInheritedHistory = req.replayInheritedHistory === false;
@@ -11120,7 +11853,7 @@ function createAcpSessionBridge(opts) {
11120
11853
  releaseAdmissionOnce();
11121
11854
  } catch (restoreErr) {
11122
11855
  writeStderrLine(
11123
- `qwen serve: branchSession load failed for ${result.newSessionId}, attempting cleanup...`
11856
+ `qwen serve: branchSession load failed for ${result.newSessionId}; closing partial live state while preserving the committed session...`
11124
11857
  );
11125
11858
  try {
11126
11859
  if (!ci.isDying) {
@@ -11130,7 +11863,8 @@ function createAcpSessionBridge(opts) {
11130
11863
  SERVE_CONTROL_EXT_METHODS.sessionClose,
11131
11864
  {
11132
11865
  sessionId: result.newSessionId,
11133
- cwd: boundWorkspace
11866
+ cwd: boundWorkspace,
11867
+ drainTimeoutMs: sessionCloseDrainBudgetMs(initTimeoutMs)
11134
11868
  }
11135
11869
  ),
11136
11870
  channelUnavailableReject(
@@ -11144,7 +11878,7 @@ function createAcpSessionBridge(opts) {
11144
11878
  }
11145
11879
  } catch (cleanupErr) {
11146
11880
  writeStderrLine(
11147
- `qwen serve: branchSession cleanup of ${result.newSessionId} failed: ${cleanupErr instanceof Error ? cleanupErr.message : cleanupErr}`
11881
+ `qwen serve: branchSession live-state close for ${result.newSessionId} failed: ${cleanupErr instanceof Error ? cleanupErr.message : cleanupErr}`
11148
11882
  );
11149
11883
  }
11150
11884
  throw restoreErr;
@@ -11174,19 +11908,6 @@ function createAcpSessionBridge(opts) {
11174
11908
  );
11175
11909
  }
11176
11910
  }
11177
- if (!isSideTask) {
11178
- const eventData = {
11179
- sourceSessionId: sessionId,
11180
- newSessionId: result.newSessionId,
11181
- displayName: branchDisplayName
11182
- };
11183
- const branchEnvelope = {
11184
- type: "session_branched",
11185
- data: eventData,
11186
- ...originatorClientId ? { originatorClientId } : {}
11187
- };
11188
- newEntry?.events.publish(branchEnvelope);
11189
- }
11190
11911
  return {
11191
11912
  ...restored,
11192
11913
  displayName: branchDisplayName,
@@ -11219,7 +11940,8 @@ function createAcpSessionBridge(opts) {
11219
11940
  },
11220
11941
  context
11221
11942
  );
11222
- const { forkedFrom: _forkedFrom, ...sideTask } = result;
11943
+ const restoredResult = result;
11944
+ const { forkedFrom: _forkedFrom, ...sideTask } = restoredResult;
11223
11945
  return {
11224
11946
  ...sideTask,
11225
11947
  parentSessionId: sessionId
@@ -11289,6 +12011,7 @@ function createAcpSessionBridge(opts) {
11289
12011
  const entry = byId.get(sessionId);
11290
12012
  if (entry) {
11291
12013
  entry.worktree = worktree;
12014
+ markSessionCatalogChanged();
11292
12015
  }
11293
12016
  },
11294
12017
  async closeSession(sessionId, context, closeOpts) {
@@ -11314,6 +12037,7 @@ function createAcpSessionBridge(opts) {
11314
12037
  const nextDisplayName = metadata.displayName || void 0;
11315
12038
  if (entry.displayName !== nextDisplayName) {
11316
12039
  entry.displayName = nextDisplayName;
12040
+ markSessionCatalogChanged();
11317
12041
  writeStderrLine(
11318
12042
  `qwen serve: updated session metadata ${JSON.stringify(sessionId)} displayName=${entry.displayName === void 0 ? "cleared" : "set"}` + (context?.clientId ? ` by client ${JSON.stringify(context.clientId)}` : "")
11319
12043
  );
@@ -11376,7 +12100,7 @@ function createAcpSessionBridge(opts) {
11376
12100
  return warnings.length > 0 ? { ...result, warnings } : result;
11377
12101
  },
11378
12102
  listWorkspaceSessions(workspaceCwd) {
11379
- if (!path3.isAbsolute(workspaceCwd)) return [];
12103
+ if (!path4.isAbsolute(workspaceCwd)) return [];
11380
12104
  const key = workspaceCwd === boundWorkspace ? boundWorkspace : canonicalizeWorkspace(workspaceCwd);
11381
12105
  if (key !== boundWorkspace) return [];
11382
12106
  const out = [];
@@ -11387,6 +12111,8 @@ function createAcpSessionBridge(opts) {
11387
12111
  }
11388
12112
  return out;
11389
12113
  },
12114
+ getSessionCatalogVersion,
12115
+ markSessionCatalogChanged,
11390
12116
  getSessionSummary(sessionId) {
11391
12117
  const entry = byId.get(sessionId);
11392
12118
  if (!entry) throw new SessionNotFoundError(sessionId);
@@ -12079,7 +12805,7 @@ function createAcpSessionBridge(opts) {
12079
12805
  const info = channelInfoForEntry(entry);
12080
12806
  if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
12081
12807
  resolveTrustedClientId(entry, context?.clientId);
12082
- const requestId = randomUUID2();
12808
+ const requestId = randomUUID3();
12083
12809
  const queue = new GenerationStreamQueue(
12084
12810
  GENERATION_STREAM_QUEUE_CAPACITY
12085
12811
  );
@@ -12146,14 +12872,110 @@ function createAcpSessionBridge(opts) {
12146
12872
  const entry = byId.get(sessionId);
12147
12873
  if (!entry) throw new SessionNotFoundError(sessionId);
12148
12874
  resolveTrustedClientId(entry, context?.clientId);
12149
- return entry.pendingPromptList.filter((p) => !p.removed).map((p) => ({
12875
+ return entry.pendingPromptList.filter((p) => !p.removed && !p.terminalPublished).map((p) => ({
12150
12876
  promptId: p.promptId,
12151
12877
  text: p.text,
12878
+ ...p.content ? { content: p.content } : {},
12152
12879
  queuedAt: p.queuedAt,
12153
12880
  state: p.state,
12154
12881
  ...p.originatorClientId !== void 0 ? { originatorClientId: p.originatorClientId } : {}
12155
12882
  }));
12156
12883
  },
12884
+ async getSessionTurnStatus(sessionId, context, promptId) {
12885
+ const entry = byId.get(sessionId);
12886
+ if (!entry) throw new SessionNotFoundError(sessionId);
12887
+ resolveTrustedClientId(entry, context?.clientId);
12888
+ const liveBeforeRead = findLiveTurnStatus(entry, promptId);
12889
+ if (liveBeforeRead) return liveBeforeRead;
12890
+ if (promptId !== void 0 && entry.enrichedTerminalPromptIds.has(promptId)) {
12891
+ const enrichedTerminal = entry.terminalTurnStatuses.get(promptId);
12892
+ if (enrichedTerminal) return enrichedTerminal;
12893
+ }
12894
+ const rewindGenerationBeforeRead = entry.rewindGeneration;
12895
+ let result;
12896
+ try {
12897
+ result = await requestSessionStatus(
12898
+ sessionId,
12899
+ SERVE_CONTROL_EXT_METHODS.sessionTurnStatus,
12900
+ { ...promptId !== void 0 ? { promptId } : {} },
12901
+ // Transcript scans of large histories exceed the 10s init default;
12902
+ // give the read the same budget as other transcript reads.
12903
+ SESSION_TRANSCRIPT_TIMEOUT_MS
12904
+ );
12905
+ } catch (error) {
12906
+ const liveAfterFailure = findLiveTurnStatus(entry, promptId);
12907
+ if (liveAfterFailure) return liveAfterFailure;
12908
+ const terminalAfterFailure = promptId !== void 0 ? entry.terminalTurnStatuses.get(promptId) : latestTerminalTurnStatus(entry);
12909
+ if (terminalAfterFailure) return terminalAfterFailure;
12910
+ throw error;
12911
+ }
12912
+ const liveAfterRead = findLiveTurnStatus(entry, promptId);
12913
+ if (liveAfterRead) return liveAfterRead;
12914
+ const terminal = promptId !== void 0 ? entry.terminalTurnStatuses.get(promptId) : latestTerminalTurnStatus(entry);
12915
+ const persisted = result.turnResult && entry.rewindGeneration === rewindGenerationBeforeRead ? settledTurnStatus(sessionId, result.turnResult) : void 0;
12916
+ if (promptId !== void 0) {
12917
+ if (terminal && persisted) {
12918
+ const merged = mergeTerminalWithPersisted(terminal, persisted);
12919
+ rememberEnrichedTerminalTurnStatus(entry, promptId, merged);
12920
+ return merged;
12921
+ }
12922
+ if (terminal) return terminal;
12923
+ if (persisted) {
12924
+ rememberEnrichedTerminalTurnStatus(entry, promptId, persisted);
12925
+ return persisted;
12926
+ }
12927
+ } else {
12928
+ if (terminal && persisted && terminal.promptId === persisted.promptId) {
12929
+ return mergeTerminalWithPersisted(terminal, persisted);
12930
+ }
12931
+ if (terminal && persisted) {
12932
+ return (terminal.endedAt ?? 0) >= (persisted.endedAt ?? 0) ? terminal : persisted;
12933
+ }
12934
+ if (terminal) return terminal;
12935
+ if (persisted) return persisted;
12936
+ }
12937
+ if (promptId !== void 0) {
12938
+ return void 0;
12939
+ }
12940
+ return { sessionId, state: "idle" };
12941
+ },
12942
+ async storeSessionMedia(sessionId, data, mimeType, context) {
12943
+ const entry = byId.get(sessionId);
12944
+ if (!entry) throw new SessionNotFoundError(sessionId);
12945
+ resolveTrustedClientId(entry, context?.clientId);
12946
+ const operation = mediaPutQueue.then(async () => {
12947
+ if (byId.get(sessionId) !== entry) {
12948
+ throw new SessionNotFoundError(sessionId);
12949
+ }
12950
+ const retainedBytes = [...new Set(retainedMedia.values())].reduce(
12951
+ (total, store) => total + store.sizeBytes,
12952
+ 0
12953
+ );
12954
+ if (retainedBytes + data.byteLength > MAX_RETAINED_SESSION_MEDIA_BYTES) {
12955
+ throw new RangeError(
12956
+ `Session media exceeds the ${MAX_RETAINED_SESSION_MEDIA_BYTES}-byte daemon limit`
12957
+ );
12958
+ }
12959
+ return await entry.media.put(data, mimeType);
12960
+ });
12961
+ mediaPutQueue = operation.then(
12962
+ () => void 0,
12963
+ () => void 0
12964
+ );
12965
+ return await operation;
12966
+ },
12967
+ async readSessionMedia(sessionId, mediaId, context) {
12968
+ const entry = byId.get(sessionId);
12969
+ if (!entry) throw new SessionNotFoundError(sessionId);
12970
+ resolveTrustedClientId(entry, context?.clientId);
12971
+ return await entry.media.read(mediaId);
12972
+ },
12973
+ async removeSessionMedia(sessionId, mediaId, context) {
12974
+ const entry = byId.get(sessionId);
12975
+ if (!entry) throw new SessionNotFoundError(sessionId);
12976
+ resolveTrustedClientId(entry, context?.clientId);
12977
+ return await entry.media.remove(mediaId);
12978
+ },
12157
12979
  removePendingPrompt(sessionId, promptId, context) {
12158
12980
  const entry = byId.get(sessionId);
12159
12981
  if (!entry) throw new SessionNotFoundError(sessionId);
@@ -12172,6 +12994,7 @@ function createAcpSessionBridge(opts) {
12172
12994
  );
12173
12995
  if (target.state === "queued") {
12174
12996
  entry.pendingPromptList.splice(idx, 1);
12997
+ removeQueuedMedia(entry, target.content);
12175
12998
  } else {
12176
12999
  target.removed = true;
12177
13000
  }
@@ -12197,7 +13020,10 @@ function createAcpSessionBridge(opts) {
12197
13020
  context?.clientId
12198
13021
  );
12199
13022
  const trimmed = message.trim();
12200
- if (trimmed.length === 0) {
13023
+ const mediaBlocks = (options?.content ?? []).filter(
13024
+ (block) => block.type === "image"
13025
+ );
13026
+ if (trimmed.length === 0 && mediaBlocks.length === 0) {
12201
13027
  writeStderrLine(
12202
13028
  `[mid-turn] session=${entry.sessionId} rejected: empty`
12203
13029
  );
@@ -12208,11 +13034,12 @@ function createAcpSessionBridge(opts) {
12208
13034
  (queued) => queued.messageId === requestedMessageId
12209
13035
  );
12210
13036
  if (existing) {
12211
- if (existing.text === trimmed) {
13037
+ const sameMedia = JSON.stringify(existing.content ?? []) === JSON.stringify(mediaBlocks);
13038
+ if (existing.text === trimmed && sameMedia) {
12212
13039
  return { accepted: true, messageId: requestedMessageId };
12213
13040
  }
12214
13041
  writeStderrLine(
12215
- `[mid-turn] session=${JSON.stringify(entry.sessionId)} rejected id ${JSON.stringify(requestedMessageId)}: text mismatch`
13042
+ `[mid-turn] session=${JSON.stringify(entry.sessionId)} rejected id ${JSON.stringify(requestedMessageId)}: text or content mismatch`
12216
13043
  );
12217
13044
  return { accepted: false };
12218
13045
  }
@@ -12220,11 +13047,16 @@ function createAcpSessionBridge(opts) {
12220
13047
  (pending) => pending.promptId === requestedMessageId
12221
13048
  );
12222
13049
  if (promoted) {
12223
- if (promoted.text === trimmed) {
13050
+ const promotedImages = (promoted.content ?? []).filter(
13051
+ (block) => block.type === "image"
13052
+ );
13053
+ const sameMedia = JSON.stringify(promotedImages) === JSON.stringify(mediaBlocks);
13054
+ const promotedText = promoted.text === "[image]" && trimmed.length === 0 ? "" : promoted.text;
13055
+ if (promotedText === trimmed && sameMedia) {
12224
13056
  return { accepted: true, messageId: requestedMessageId };
12225
13057
  }
12226
13058
  writeStderrLine(
12227
- `[mid-turn] session=${JSON.stringify(entry.sessionId)} rejected promoted id ${JSON.stringify(requestedMessageId)}: text mismatch`
13059
+ `[mid-turn] session=${JSON.stringify(entry.sessionId)} rejected promoted id ${JSON.stringify(requestedMessageId)}: text or content mismatch`
12228
13060
  );
12229
13061
  return { accepted: false };
12230
13062
  }
@@ -12241,7 +13073,21 @@ function createAcpSessionBridge(opts) {
12241
13073
  );
12242
13074
  return { accepted: false };
12243
13075
  }
12244
- const messageId = requestedMessageId ?? randomUUID2();
13076
+ entry.media.assertReferences(mediaBlocks);
13077
+ const inlineBytes = inlineMediaBlockBytes(mediaBlocks);
13078
+ if (inlineBytes > 0) {
13079
+ const queuedInlineBytes = entry.midTurnMessageQueue.reduce(
13080
+ (total, queued) => total + inlineMediaBlockBytes(queued.content ?? []),
13081
+ 0
13082
+ );
13083
+ if (queuedInlineBytes + inlineBytes > SESSION_MEDIA_MAX_TOTAL_BYTES) {
13084
+ writeStderrLine(
13085
+ `[mid-turn] session=${entry.sessionId} rejected: queued inline media exceeds the ${SESSION_MEDIA_MAX_TOTAL_BYTES}-byte session budget`
13086
+ );
13087
+ return { accepted: false };
13088
+ }
13089
+ }
13090
+ const messageId = requestedMessageId ?? randomUUID3();
12245
13091
  if (entry.pendingPromptCount === 0) {
12246
13092
  if (options?.queueOnly) {
12247
13093
  writeStderrLine(
@@ -12249,7 +13095,13 @@ function createAcpSessionBridge(opts) {
12249
13095
  );
12250
13096
  return { accepted: false };
12251
13097
  }
12252
- promoteMidTurnMessage(entry, messageId, trimmed, originatorClientId);
13098
+ promoteMidTurnMessage(
13099
+ entry,
13100
+ messageId,
13101
+ trimmed,
13102
+ originatorClientId,
13103
+ mediaBlocks.length > 0 ? mediaBlocks : void 0
13104
+ );
12253
13105
  return { accepted: true, messageId };
12254
13106
  }
12255
13107
  if (entry.midTurnMessageQueue.length >= MAX_MID_TURN_QUEUE_DEPTH) {
@@ -12261,6 +13113,7 @@ function createAcpSessionBridge(opts) {
12261
13113
  const queuedMessage = {
12262
13114
  messageId,
12263
13115
  text: trimmed,
13116
+ ...mediaBlocks.length > 0 ? { content: mediaBlocks } : {},
12264
13117
  originatorClientId,
12265
13118
  ...options?.queueOnly ? {
12266
13119
  queueOnly: true,
@@ -12320,6 +13173,7 @@ function createAcpSessionBridge(opts) {
12320
13173
  const [removed] = entry.midTurnMessageQueue.splice(index, 1);
12321
13174
  rememberMidTurnId(entry.settledMidTurnMessageIds, messageId);
12322
13175
  if (removed) {
13176
+ removeQueuedMedia(entry, removed.content);
12323
13177
  try {
12324
13178
  entry.events.publish({
12325
13179
  type: "pending_prompt_completed",
@@ -12369,7 +13223,11 @@ function createAcpSessionBridge(opts) {
12369
13223
  // should restore into their queue views.
12370
13224
  messages: entry.midTurnMessageQueue.filter((message) => message.originatorClientId !== void 0).map((message) => ({
12371
13225
  messageId: message.messageId,
12372
- text: message.text
13226
+ text: message.text,
13227
+ // Carry the media blocks so a refreshed client can rebuild the
13228
+ // queued row with its attachments (the snapshot is the only
13229
+ // recovery source once the client's in-memory copy is gone).
13230
+ ...message.content ? { content: message.content } : {}
12373
13231
  })),
12374
13232
  settledMessageIds: [...entry.settledMidTurnMessageIds],
12375
13233
  promotedMessageIds: [...entry.promotedMidTurnMessageIds]
@@ -12634,10 +13492,19 @@ function createAcpSessionBridge(opts) {
12634
13492
  entry,
12635
13493
  context?.clientId
12636
13494
  );
12637
- let response;
12638
- try {
12639
- response = await Promise.race([
12640
- withTimeout(
13495
+ if (entry.pendingPromptCount > 0 || entry.promptActive) {
13496
+ throw new SessionBusyError(
13497
+ sessionId,
13498
+ "Cannot rewind while a prompt is running"
13499
+ );
13500
+ }
13501
+ const rewindResult = entry.promptQueue.then(async () => {
13502
+ if (entry.closing) {
13503
+ throw new SessionNotFoundError(sessionId, "The session is closing");
13504
+ }
13505
+ let response;
13506
+ try {
13507
+ response = await Promise.race([
12641
13508
  entry.connection.extMethod(
12642
13509
  SERVE_CONTROL_EXT_METHODS.sessionRewind,
12643
13510
  {
@@ -12646,92 +13513,98 @@ function createAcpSessionBridge(opts) {
12646
13513
  rewindFiles: req.rewindFiles !== false
12647
13514
  }
12648
13515
  ),
12649
- initTimeoutMs,
12650
- SERVE_CONTROL_EXT_METHODS.sessionRewind
12651
- ),
12652
- getTransportClosedReject(entry)
12653
- ]);
12654
- } catch (err) {
12655
- const data = err?.data;
12656
- if (data && typeof data === "object" && "errorKind" in data) {
12657
- const kind = data.errorKind;
12658
- const msg = err?.message ?? "Rewind failed";
12659
- if (kind === "session_busy") {
12660
- throw new SessionBusyError(sessionId, msg);
12661
- }
12662
- if (kind === "invalid_rewind_target") {
12663
- throw new InvalidRewindTargetError(sessionId, msg);
13516
+ getTransportClosedReject(entry)
13517
+ ]);
13518
+ } catch (err) {
13519
+ const data = err?.data;
13520
+ if (data && typeof data === "object" && "errorKind" in data) {
13521
+ const kind = data.errorKind;
13522
+ const msg = err?.message ?? "Rewind failed";
13523
+ if (kind === "session_busy") {
13524
+ throw new SessionBusyError(sessionId, msg);
13525
+ }
13526
+ if (kind === "invalid_rewind_target") {
13527
+ throw new InvalidRewindTargetError(sessionId, msg);
13528
+ }
12664
13529
  }
13530
+ throw err;
12665
13531
  }
12666
- throw err;
12667
- }
12668
- const targetTurnIndex = response["targetTurnIndex"] ?? 0;
12669
- const filesChanged = response["filesChanged"] ?? [];
12670
- const filesFailed = response["filesFailed"] ?? [];
12671
- const artifactSnapshot = restoredArtifactSnapshotFromState(
12672
- response
12673
- );
12674
- const artifactSnapshotUnavailable = artifactSnapshotUnavailableReason(
12675
- response
12676
- );
12677
- const beforeArtifacts = (await entry.artifacts.list()).artifacts;
12678
- const shouldRestoreArtifactSnapshot = artifactSnapshot !== void 0 && artifactSnapshotUnavailable === void 0;
12679
- const artifactRestoreWarnings = artifactSnapshotUnavailable !== void 0 ? [
12680
- `artifact snapshot rebuild unavailable during rewind: ${artifactSnapshotUnavailable}`
12681
- ] : shouldRestoreArtifactSnapshot ? await entry.artifacts.restore(artifactSnapshot, {
12682
- preserveLiveEphemeral: true
12683
- }) : [];
12684
- const artifactRestoreFailed = artifactRestoreWarnings.some(
12685
- isArtifactRestoreFailureWarning
12686
- );
12687
- const shouldRecordArtifactSnapshot = shouldRestoreArtifactSnapshot && !artifactRestoreFailed;
12688
- const artifactSnapshotWarnings = shouldRecordArtifactSnapshot ? await entry.artifacts.recordSnapshot() : [];
12689
- const artifactWarnings = [
12690
- ...artifactRestoreWarnings,
12691
- ...artifactSnapshotWarnings
12692
- ];
12693
- for (const warning of artifactRestoreWarnings) {
12694
- writeStderrLine(
12695
- `[artifacts] session=${entry.sessionId} action=rewind_restore_warning warning=${JSON.stringify(
12696
- warning
12697
- )}`
13532
+ entry.terminalTurnStatuses.clear();
13533
+ entry.enrichedTerminalPromptIds.clear();
13534
+ entry.rewindGeneration += 1;
13535
+ const targetTurnIndex = response["targetTurnIndex"] ?? 0;
13536
+ const filesChanged = response["filesChanged"] ?? [];
13537
+ const filesFailed = response["filesFailed"] ?? [];
13538
+ const artifactSnapshot = restoredArtifactSnapshotFromState(
13539
+ response
12698
13540
  );
12699
- }
12700
- for (const warning of artifactSnapshotWarnings) {
12701
- writeStderrLine(
12702
- `[artifacts] session=${entry.sessionId} action=rewind_snapshot_warning warning=${JSON.stringify(
12703
- warning
12704
- )}`
13541
+ const artifactSnapshotUnavailable = artifactSnapshotUnavailableReason(
13542
+ response
12705
13543
  );
12706
- }
12707
- const afterArtifacts = (await entry.artifacts.list()).artifacts;
12708
- publishArtifactChanges(
12709
- entry,
12710
- artifactReseedChanges(beforeArtifacts, afterArtifacts),
12711
- originatorClientId
13544
+ const beforeArtifacts = (await entry.artifacts.list()).artifacts;
13545
+ const shouldRestoreArtifactSnapshot = artifactSnapshot !== void 0 && artifactSnapshotUnavailable === void 0;
13546
+ const artifactRestoreWarnings = artifactSnapshotUnavailable !== void 0 ? [
13547
+ `artifact snapshot rebuild unavailable during rewind: ${artifactSnapshotUnavailable}`
13548
+ ] : shouldRestoreArtifactSnapshot ? await entry.artifacts.restore(artifactSnapshot, {
13549
+ preserveLiveEphemeral: true
13550
+ }) : [];
13551
+ const artifactRestoreFailed = artifactRestoreWarnings.some(
13552
+ isArtifactRestoreFailureWarning
13553
+ );
13554
+ const shouldRecordArtifactSnapshot = shouldRestoreArtifactSnapshot && !artifactRestoreFailed;
13555
+ const artifactSnapshotWarnings = shouldRecordArtifactSnapshot ? await entry.artifacts.recordSnapshot() : [];
13556
+ const artifactWarnings = [
13557
+ ...artifactRestoreWarnings,
13558
+ ...artifactSnapshotWarnings
13559
+ ];
13560
+ for (const warning of artifactRestoreWarnings) {
13561
+ writeStderrLine(
13562
+ `[artifacts] session=${entry.sessionId} action=rewind_restore_warning warning=${JSON.stringify(
13563
+ warning
13564
+ )}`
13565
+ );
13566
+ }
13567
+ for (const warning of artifactSnapshotWarnings) {
13568
+ writeStderrLine(
13569
+ `[artifacts] session=${entry.sessionId} action=rewind_snapshot_warning warning=${JSON.stringify(
13570
+ warning
13571
+ )}`
13572
+ );
13573
+ }
13574
+ const afterArtifacts = (await entry.artifacts.list()).artifacts;
13575
+ publishArtifactChanges(
13576
+ entry,
13577
+ artifactReseedChanges(beforeArtifacts, afterArtifacts),
13578
+ originatorClientId
13579
+ );
13580
+ try {
13581
+ entry.events.publish({
13582
+ type: "session_rewound",
13583
+ data: {
13584
+ sessionId,
13585
+ promptId: req.promptId,
13586
+ targetTurnIndex,
13587
+ filesChanged,
13588
+ filesFailed,
13589
+ ...artifactWarnings.length > 0 ? { warnings: artifactWarnings } : {}
13590
+ },
13591
+ ...originatorClientId ? { originatorClientId } : {}
13592
+ });
13593
+ } catch {
13594
+ }
13595
+ return {
13596
+ rewound: filesFailed.length === 0,
13597
+ targetTurnIndex,
13598
+ filesChanged,
13599
+ filesFailed,
13600
+ ...artifactWarnings.length > 0 ? { warnings: artifactWarnings } : {}
13601
+ };
13602
+ });
13603
+ entry.promptQueue = rewindResult.then(
13604
+ () => void 0,
13605
+ () => void 0
12712
13606
  );
12713
- try {
12714
- entry.events.publish({
12715
- type: "session_rewound",
12716
- data: {
12717
- sessionId,
12718
- promptId: req.promptId,
12719
- targetTurnIndex,
12720
- filesChanged,
12721
- filesFailed,
12722
- ...artifactWarnings.length > 0 ? { warnings: artifactWarnings } : {}
12723
- },
12724
- ...originatorClientId ? { originatorClientId } : {}
12725
- });
12726
- } catch {
12727
- }
12728
- return {
12729
- rewound: filesFailed.length === 0,
12730
- targetTurnIndex,
12731
- filesChanged,
12732
- filesFailed,
12733
- ...artifactWarnings.length > 0 ? { warnings: artifactWarnings } : {}
12734
- };
13607
+ return rewindResult;
12735
13608
  },
12736
13609
  async manageMcpServer(serverName, action, originatorClientId) {
12737
13610
  const info = await ensureChannel();
@@ -12862,7 +13735,7 @@ function createAcpSessionBridge(opts) {
12862
13735
  ]);
12863
13736
  },
12864
13737
  generateWorkspaceContent(prompt, signal, _originatorClientId) {
12865
- const requestId = randomUUID2();
13738
+ const requestId = randomUUID3();
12866
13739
  const queue = new GenerationStreamQueue(
12867
13740
  GENERATION_STREAM_QUEUE_CAPACITY
12868
13741
  );
@@ -13041,6 +13914,11 @@ function createAcpSessionBridge(opts) {
13041
13914
  closingChannel,
13042
13915
  `force kill closing session ${JSON.stringify(sessionId)}`
13043
13916
  );
13917
+ await releaseSessionMedia(sessionId).catch((releaseError) => {
13918
+ writeStderrLine(
13919
+ `qwen serve: failed to release media for killed session ${JSON.stringify(sessionId)}: ${releaseError instanceof Error ? releaseError.message : String(releaseError)}`
13920
+ );
13921
+ });
13044
13922
  return true;
13045
13923
  }
13046
13924
  entry.closing = true;
@@ -13059,11 +13937,20 @@ function createAcpSessionBridge(opts) {
13059
13937
  timeoutMs: initTimeoutMs
13060
13938
  });
13061
13939
  } catch (error) {
13940
+ if (isDefinitiveAcpRequestError(error)) {
13941
+ entry.closing = false;
13942
+ return false;
13943
+ }
13062
13944
  if (ci) {
13063
13945
  await killChannelWithLog(
13064
13946
  ci,
13065
13947
  `force kill session ${JSON.stringify(sessionId)}`
13066
13948
  );
13949
+ await releaseSessionMedia(sessionId).catch((releaseError) => {
13950
+ writeStderrLine(
13951
+ `qwen serve: failed to release media for killed session ${JSON.stringify(sessionId)}: ${releaseError instanceof Error ? releaseError.message : String(releaseError)}`
13952
+ );
13953
+ });
13067
13954
  return true;
13068
13955
  }
13069
13956
  entry.closing = false;
@@ -13100,6 +13987,11 @@ function createAcpSessionBridge(opts) {
13100
13987
  } catch {
13101
13988
  }
13102
13989
  entry.events.close();
13990
+ await releaseSessionMedia(sessionId).catch((error) => {
13991
+ writeStderrLine(
13992
+ `qwen serve: failed to release media for killed session ${JSON.stringify(sessionId)}: ${error instanceof Error ? error.message : String(error)}`
13993
+ );
13994
+ });
13103
13995
  if (ci && hasNoChannelWork(ci)) {
13104
13996
  await reapPendingEmptyChannel(ci);
13105
13997
  if (!ci.isDying) {
@@ -13204,6 +14096,8 @@ function createAcpSessionBridge(opts) {
13204
14096
  ) : Promise.resolve();
13205
14097
  const teardownResults = await Promise.allSettled([
13206
14098
  ...channels.map((ci) => ci.channel.kill()),
14099
+ ...[...retainedMedia.values()].map((store) => store.close()),
14100
+ mediaPutQueue,
13207
14101
  ...inFlightSessionAwaits,
13208
14102
  ...inFlightRestoreAwaits,
13209
14103
  inFlightChannelAwait