@qwen-code/qwen-code 0.21.13 → 0.21.14

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 (293) hide show
  1. package/bundled/qc-helper/docs/configuration/settings.md +13 -7
  2. package/bundled/qc-helper/docs/features/code-review.md +5 -1
  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 +50 -30
  8. package/chunks/{MaxSizedBox-UQALOVVT.js → MaxSizedBox-67IDQILY.js} +24 -23
  9. package/chunks/{StandaloneSessionPicker-KEGXNUG6.js → StandaloneSessionPicker-G4QJIRMH.js} +41 -40
  10. package/chunks/{acp-startup-profiler-NMDXYGDN.js → acp-startup-profiler-HUYGO57H.js} +2 -2
  11. package/chunks/{acpAgent-XPES32S7.js → acpAgent-BS3YYO33.js} +626 -141
  12. package/chunks/{agent-DPTOZFEM.js → agent-EHHEXKWN.js} +22 -21
  13. package/chunks/{agent-headless-GJCQMURW.js → agent-headless-UT2ZF76T.js} +22 -21
  14. package/chunks/{anthropicContentGenerator-G7BVXTKN.js → anthropicContentGenerator-M7XRJMSK.js} +3 -3
  15. package/chunks/{bridge-YNQBAA4Z.js → bridge-WQ4IL2RF.js} +26 -25
  16. package/chunks/{ca-4CQ6MYNQ.js → ca-SLZJD6QH.js} +8 -0
  17. package/chunks/{channel-management-service-GWY2GAIC.js → channel-management-service-X6LCQUAD.js} +1 -1
  18. package/chunks/{channel-settings-store-WNWYFHG7.js → channel-settings-store-ZDFXY7VY.js} +30 -29
  19. package/chunks/{channel-worker-group-7LXCMZ77.js → channel-worker-group-TJ35JDPE.js} +2 -2
  20. package/chunks/{channel-worker-manager-RYEME4ZZ.js → channel-worker-manager-G5LKWGOG.js} +2 -2
  21. package/chunks/{chunk-4PKCUT7F.js → chunk-2A6VQJYK.js} +6 -6
  22. package/chunks/chunk-2C6HJMG3.js +757 -0
  23. package/chunks/chunk-3PWCMXIR.js +9169 -0
  24. package/chunks/{chunk-BVJS5BSP.js → chunk-3PWNVF6N.js} +1 -1
  25. package/chunks/{chunk-X26JYGTD.js → chunk-3SL2P3WX.js} +1 -1
  26. package/chunks/{chunk-ZYBBISBY.js → chunk-3YRVPHT3.js} +15 -12
  27. package/chunks/{chunk-AS5IN4GK.js → chunk-47L5MLB5.js} +3 -3
  28. package/chunks/{chunk-3U3VAL47.js → chunk-4SR45OM4.js} +1 -1
  29. package/chunks/{chunk-OMLJNPBE.js → chunk-4ZMS3JCR.js} +1 -0
  30. package/chunks/{chunk-UKMR3CKV.js → chunk-4ZS3BLER.js} +3 -3
  31. package/chunks/{chunk-5EYKMUUY.js → chunk-544KT7X4.js} +1 -1
  32. package/chunks/{chunk-TUGSVUJB.js → chunk-55VKMB7X.js} +5 -5
  33. package/chunks/{chunk-GO7STP6N.js → chunk-5IJSFWQO.js} +4 -4
  34. package/chunks/{chunk-A4QRWUDE.js → chunk-5IYS4WWR.js} +1 -1
  35. package/chunks/{chunk-TS6625XH.js → chunk-5MK45KZ3.js} +9 -5
  36. package/chunks/{chunk-MLPXSYXR.js → chunk-6BJSYAML.js} +25 -16
  37. package/chunks/{chunk-V57QC6WW.js → chunk-6LAPGPVA.js} +5 -3
  38. package/chunks/{chunk-NNHNLJ2X.js → chunk-6RDZLBSJ.js} +240 -42
  39. package/chunks/{chunk-ZZO74WKW.js → chunk-73JUK3ZW.js} +24 -18
  40. package/chunks/{chunk-N5YPT6PH.js → chunk-7VNHO4MN.js} +7 -4
  41. package/chunks/{chunk-OVZPVYZB.js → chunk-AKBOLMUF.js} +95 -17
  42. package/chunks/{chunk-NHDR76JX.js → chunk-BQWNIPTE.js} +1 -1
  43. package/chunks/{chunk-IWPYVAO2.js → chunk-C3T6M24E.js} +2 -2
  44. package/chunks/{chunk-NM5V5OVW.js → chunk-CEXU72GE.js} +2 -2
  45. package/chunks/{chunk-AA2BEKV3.js → chunk-CH5OFRV7.js} +1 -1
  46. package/chunks/{chunk-MS7GNABG.js → chunk-CXJHQVEK.js} +8 -0
  47. package/chunks/{chunk-E23IPNGS.js → chunk-CZK622ZK.js} +12 -3
  48. package/chunks/{chunk-K5IDV3PC.js → chunk-D34RDOLP.js} +12 -2
  49. package/chunks/{chunk-OXCMMLED.js → chunk-DGJ7O55J.js} +1 -1
  50. package/chunks/{chunk-DGK6P3BQ.js → chunk-DHUPMYMC.js} +10 -10
  51. package/chunks/{chunk-O4D7SPJN.js → chunk-DRAONPUW.js} +3 -3
  52. package/chunks/{chunk-P7MPQI75.js → chunk-DVR7XKO5.js} +1 -1
  53. package/chunks/{chunk-PP4FUZNV.js → chunk-E5WQMF3K.js} +2 -2
  54. package/chunks/{chunk-4NFY2S7N.js → chunk-E7REAOTN.js} +15 -0
  55. package/chunks/{chunk-ZYYTZUI2.js → chunk-EOPBFLA5.js} +3 -3
  56. package/chunks/{chunk-OOQUVWIG.js → chunk-FN6M5WUL.js} +4 -4
  57. package/chunks/{chunk-DGZEMW5R.js → chunk-FS5NGKWC.js} +1 -1
  58. package/chunks/{chunk-MRMJCLIY.js → chunk-G6KI5UOJ.js} +2 -2
  59. package/chunks/{chunk-6ORZ2EDC.js → chunk-H3SHU2XU.js} +1 -1
  60. package/chunks/{chunk-GMIEIY3Y.js → chunk-I3N2Z25H.js} +1 -1
  61. package/chunks/{chunk-BHILXOC3.js → chunk-J63PKMYA.js} +1 -1
  62. package/chunks/{chunk-KD5BOPLP.js → chunk-J6YS7Z25.js} +98 -4
  63. package/chunks/chunk-K2Y45XXS.js +3887 -0
  64. package/chunks/{chunk-RVOJ2FBP.js → chunk-KC4WMLZO.js} +1486 -10574
  65. package/chunks/{chunk-LJQAUZMR.js → chunk-L3T4V5GZ.js} +925 -92
  66. package/chunks/{chunk-M6NDLY2T.js → chunk-LD5VIJ7S.js} +1 -1
  67. package/chunks/{chunk-RIAABG2K.js → chunk-LSO6EL4Y.js} +1 -1
  68. package/chunks/{chunk-XHRY7BXD.js → chunk-MDIGJH5X.js} +3 -3
  69. package/chunks/{chunk-MC4TLKEI.js → chunk-MVHJZ3FZ.js} +2 -2
  70. package/chunks/{chunk-YEYY7TEP.js → chunk-N6RSOOWJ.js} +5 -5
  71. package/chunks/{chunk-OD2WU5QY.js → chunk-NRPXRNN4.js} +1 -1
  72. package/chunks/{chunk-CKSJECTU.js → chunk-NVRMCWTP.js} +8 -4
  73. package/chunks/{chunk-Y3YCSWG5.js → chunk-OBJYN4MX.js} +1 -1
  74. package/chunks/{chunk-GGDBMYGZ.js → chunk-OEYAK5O6.js} +3 -3
  75. package/chunks/{chunk-6YKYPVDV.js → chunk-OEZCXK7Y.js} +3 -3
  76. package/chunks/{chunk-4S5337WV.js → chunk-OFTPCOGR.js} +4312 -2704
  77. package/chunks/{chunk-7UXUJ5T3.js → chunk-OJB5FYVK.js} +1 -1
  78. package/chunks/{chunk-WFSTRPRO.js → chunk-OJOP26ET.js} +1 -1
  79. package/chunks/{chunk-TIGA2IVL.js → chunk-OO4FKHLO.js} +2 -2
  80. package/chunks/{chunk-BZ263IKX.js → chunk-OVY67CYC.js} +1 -1
  81. package/chunks/{chunk-PQ36L4F5.js → chunk-P3AA6S7O.js} +1 -1
  82. package/chunks/{chunk-CU6GJD55.js → chunk-PCETXVVV.js} +1 -1
  83. package/chunks/{chunk-AFSG32D5.js → chunk-PHN4R2ZV.js} +1 -1
  84. package/chunks/{chunk-QF2NHSPB.js → chunk-PJSD2FOR.js} +233 -184
  85. package/chunks/{chunk-NEFOOD54.js → chunk-QAVTWAHL.js} +2 -2
  86. package/chunks/{chunk-AW4TRXOC.js → chunk-QEHH23OM.js} +1 -1
  87. package/chunks/{chunk-CZBYX5MY.js → chunk-QEJZUXSK.js} +1 -1
  88. package/chunks/{chunk-XIT3GURH.js → chunk-QRVHGEVY.js} +3 -3
  89. package/chunks/{chunk-M732YJPW.js → chunk-RXHBTKMG.js} +2 -2
  90. package/chunks/{chunk-JOOFMXGF.js → chunk-S7N3HUIB.js} +5 -5
  91. package/chunks/{chunk-76J3IJ3M.js → chunk-SCZU65AI.js} +3 -3
  92. package/chunks/{chunk-QIH2HAEN.js → chunk-SFJMFZT5.js} +1 -1
  93. package/chunks/{chunk-JNDY2MDR.js → chunk-SFTKAX4O.js} +1 -1
  94. package/chunks/{chunk-WSH6GRKG.js → chunk-SLILKYV2.js} +2 -2
  95. package/chunks/{chunk-DXIBNLBZ.js → chunk-SNQZZ765.js} +1 -1
  96. package/chunks/{chunk-V26NVGAU.js → chunk-TSMPNGBC.js} +8 -3
  97. package/chunks/{chunk-QV5YN5YD.js → chunk-U657UZJN.js} +5 -5
  98. package/chunks/{chunk-QNQAPSN2.js → chunk-UC67T4SF.js} +3 -1
  99. package/chunks/{chunk-O565M6A4.js → chunk-UYDGFWIJ.js} +199 -14
  100. package/chunks/{chunk-OY5RNSV4.js → chunk-V6WEMZPD.js} +3 -3
  101. package/chunks/{chunk-A267JX7H.js → chunk-VFP5YOHJ.js} +6 -6
  102. package/chunks/{chunk-HUTNYWBG.js → chunk-VTOLYFWM.js} +18 -10
  103. package/chunks/{chunk-EZ5RP345.js → chunk-W7EYJGN7.js} +40 -9
  104. package/chunks/{chunk-BKPPWD33.js → chunk-WAO53ZQF.js} +1 -1
  105. package/chunks/{chunk-S5DWMTHO.js → chunk-WP4WIUOJ.js} +2 -0
  106. package/chunks/{chunk-25EYDK42.js → chunk-WPVUOVYX.js} +1 -1
  107. package/chunks/{chunk-AXN7UPYQ.js → chunk-WSSH2BNO.js} +1 -1
  108. package/chunks/chunk-WZAD4ZNJ.js +59 -0
  109. package/chunks/{chunk-JUEPY766.js → chunk-XITLNXLX.js} +2 -2
  110. package/chunks/{chunk-EL5SXY3T.js → chunk-XT4RKHVE.js} +2 -2
  111. package/chunks/{chunk-IGGGG5CX.js → chunk-XYRBK56E.js} +4635 -1655
  112. package/chunks/{chunk-UZPM4XHR.js → chunk-Y4RVDOXV.js} +29 -8
  113. package/chunks/{chunk-QRCNA3SR.js → chunk-Y7QY6DKW.js} +49 -69
  114. package/chunks/{chunk-FDGGZIYW.js → chunk-YECDICIO.js} +2 -2
  115. package/chunks/{chunk-46UV252V.js → chunk-YHC2EYYG.js} +35 -38
  116. package/chunks/{chunk-U3VNBPUO.js → chunk-YHXQK4SR.js} +2 -2
  117. package/chunks/{chunk-A4MFR4C5.js → chunk-ZIGC3MUS.js} +2 -4
  118. package/chunks/{computer-use-77C5FHMW.js → computer-use-YORZJQE5.js} +22 -21
  119. package/chunks/{config-utils-IVHRSXWI.js → config-utils-NJQA5YLJ.js} +2 -2
  120. package/chunks/{contextCommand-PJ77GBWL.js → contextCommand-PACHGIF4.js} +24 -23
  121. package/chunks/{core-runtime-7VMIKUTG.js → core-runtime-6HD5DSPX.js} +22 -21
  122. package/chunks/{create-sub-session-2L5MQRN4.js → create-sub-session-IQFIPUQA.js} +26 -24
  123. package/chunks/{cron-create-H7UEPDSN.js → cron-create-NFRZEOQX.js} +2 -2
  124. package/chunks/{cron-delete-6SZK7YFM.js → cron-delete-GFVSVO7C.js} +2 -2
  125. package/chunks/{cron-list-H25JIHEZ.js → cron-list-NRW34CLU.js} +2 -2
  126. package/chunks/{daemon-2KTG7ZBN.js → daemon-XH76IXOC.js} +356 -21
  127. package/chunks/{daemon-git-worktree-guard-DXYIMWFT.js → daemon-git-worktree-guard-6P27GKSU.js} +24 -23
  128. package/chunks/{daemon-status-provider-ZDOYJCU6.js → daemon-status-provider-SXS66ZIR.js} +31 -30
  129. package/chunks/{daemon-trust-policy-E67OYUK2.js → daemon-trust-policy-5QJHAS3N.js} +29 -28
  130. package/chunks/{daemon-trust-policy-monitor-RYYGCQ3J.js → daemon-trust-policy-monitor-JA2WZRZ3.js} +29 -28
  131. package/chunks/{de-UBEBKG6V.js → de-AH4LGCF5.js} +8 -0
  132. package/chunks/{deferred-core-runtime-3SGXFXEQ.js → deferred-core-runtime-R2VWOVG3.js} +22 -21
  133. package/chunks/{discovery-ORA5J7YP.js → discovery-X2NV535U.js} +1 -1
  134. package/chunks/{dist-WJ4BHIEW.js → dist-ESS34DHC.js} +72 -0
  135. package/chunks/{earlyInputCapture-KJMLFVQV.js → earlyInputCapture-PQEEOFKJ.js} +22 -21
  136. package/chunks/{edit-I4LQFAEC.js → edit-RASZHTU2.js} +25 -23
  137. package/chunks/{en-X6YV7GME.js → en-MD3GP5YL.js} +8 -0
  138. package/chunks/{enter-worktree-YNPOJSRH.js → enter-worktree-CH5OMHJE.js} +22 -21
  139. package/chunks/{enterPlanMode-XCPK62ER.js → enterPlanMode-AQKR3CAH.js} +22 -21
  140. package/chunks/{environment-S7CTIHMX.js → environment-7QRYIEBA.js} +25 -24
  141. package/chunks/{errors-7HB2AIDS.js → errors-P2LL5452.js} +24 -23
  142. package/chunks/{exit-worktree-AIIY7SR6.js → exit-worktree-WUDL26CY.js} +22 -21
  143. package/chunks/{exitPlanMode-R4JY7V2O.js → exitPlanMode-JM75SAUI.js} +22 -21
  144. package/chunks/{fast-path-NDJV3ZFJ.js → fast-path-KVNSOHVA.js} +3 -3
  145. package/chunks/{fast-path-settings-LNOFM2YP.js → fast-path-settings-WHX3J3ZI.js} +2 -2
  146. package/chunks/{fr-VENTC2BS.js → fr-ZEML2JWS.js} +8 -0
  147. package/chunks/{gemini-RSUFP5OE.js → gemini-ADMD2EOS.js} +62 -61
  148. package/chunks/{geminiContentGenerator-SQ5XBPZ7.js → geminiContentGenerator-CS3M7SOS.js} +3 -3
  149. package/chunks/{glob-J4RUJAHX.js → glob-WIJOGYVV.js} +22 -21
  150. package/chunks/{goal-tools-VHTNVOKK.js → goal-tools-K5OHOJ6B.js} +1 -1
  151. package/chunks/{grep-VJWPLFOT.js → grep-CIFRQBR7.js} +22 -21
  152. package/chunks/{handleAutoUpdate-POCX2MNB.js → handleAutoUpdate-KJ4SN7DM.js} +26 -25
  153. package/chunks/{i18n-RM5YOICI.js → i18n-36SZUO4C.js} +23 -22
  154. package/chunks/{image-gen-EXECB5XM.js → image-gen-R4B5VUYB.js} +2 -2
  155. package/chunks/{initializer-Z2AFGBNR.js → initializer-CW5YCAOL.js} +29 -28
  156. package/chunks/{installationInfo-4I5FXNMH.js → installationInfo-NMQIOUGY.js} +23 -22
  157. package/chunks/{ja-QHT4EQTO.js → ja-UM5B2HMD.js} +8 -0
  158. package/chunks/{list-ZMBFIGZA.js → list-HHGT3S4P.js} +32 -31
  159. package/chunks/{loadedSettingsAdapter-T6TV4YJ7.js → loadedSettingsAdapter-SJ2MJF35.js} +29 -28
  160. package/chunks/{loggingContentGenerator-WF4ZOJEZ.js → loggingContentGenerator-GR2QQ2C5.js} +12 -10
  161. package/chunks/{loop-wakeup-WQS6JYAG.js → loop-wakeup-HWVUU5CF.js} +3 -3
  162. package/chunks/{managed-npm-update-E7IW6N7V.js → managed-npm-update-ADQJHKTO.js} +23 -22
  163. package/chunks/{mcp-6UNYX4HG.js → mcp-QW3OTL5W.js} +29 -28
  164. package/chunks/{monitor-EE2UYMGE.js → monitor-RJKPVERH.js} +22 -21
  165. package/chunks/{nonInteractiveCli-WAY237O4.js → nonInteractiveCli-MROMHSTL.js} +59 -57
  166. package/chunks/{notebook-edit-NTDY4QDU.js → notebook-edit-2ENWDBZO.js} +26 -24
  167. package/chunks/{openaiContentGenerator-YNFVJMRJ.js → openaiContentGenerator-Z3GPWJU4.js} +12 -11
  168. package/chunks/{pidfile-4XSFLYBZ.js → pidfile-5G42YSTP.js} +22 -21
  169. package/chunks/{processUtils-WDA2MJFD.js → processUtils-ZDMXYKQY.js} +2 -2
  170. package/chunks/{pt-VL3I5RNY.js → pt-5BMJ3XYH.js} +8 -0
  171. package/chunks/{qwenContentGenerator-434M7QKY.js → qwenContentGenerator-NLEMBZVU.js} +25 -24
  172. package/chunks/{qwenOAuth2-GMYZOO6A.js → qwenOAuth2-EBV52DWJ.js} +3 -3
  173. package/chunks/{read-file-TAFBKMNI.js → read-file-5BYIF53O.js} +9 -7
  174. package/chunks/{record-artifact-SEYFJEJF.js → record-artifact-GLU3UWBV.js} +3 -1
  175. package/chunks/{resumeHistoryUtils-EVG3WYK2.js → resumeHistoryUtils-AFTBRS3G.js} +25 -24
  176. package/chunks/{ripGrep-WOEK2PXS.js → ripGrep-NN7AOLQY.js} +22 -21
  177. package/chunks/{ru-4RCKL4PY.js → ru-6GL66ORV.js} +8 -0
  178. package/chunks/{run-qwen-serve-3QSGRXAN.js → run-qwen-serve-UR3DYDAF.js} +49 -40
  179. package/chunks/{runtime-5MLLZ7DC.js → runtime-PR5RKGNH.js} +32 -31
  180. package/chunks/{scheduler-RPBJ63CH.js → scheduler-GPMLVKJM.js} +24 -23
  181. package/chunks/{sdk-exporters-http-XUVP63H6.js → sdk-exporters-http-NC746HLT.js} +2 -2
  182. package/chunks/{sdk-impl-YXUEN2Z2.js → sdk-impl-YVL7BP6Y.js} +2 -2
  183. package/chunks/{send-message-VZENRWRH.js → send-message-TNBG5EVA.js} +1 -1
  184. package/chunks/{serve-HS3XLPXU.js → serve-E7V2JVA2.js} +29 -30
  185. package/chunks/{server-TC4A7AAZ.js → server-YXB3DBN7.js} +1267 -165
  186. package/chunks/{session-UBOFXVMN.js → session-DZEAUT7S.js} +60 -58
  187. package/chunks/{settings-D5644OM5.js → settings-ESZ3NWAP.js} +28 -27
  188. package/chunks/{shell-3PZULQQV.js → shell-JMSK5WIL.js} +22 -21
  189. package/chunks/{skill-PEVVXQ3L.js → skill-5K5CSUJV.js} +11 -9
  190. package/chunks/{skill-settings-J65JKKES.js → skill-settings-V64NIOTI.js} +28 -27
  191. package/chunks/{spawnChannel-IYGXRHSQ.js → spawnChannel-S63APBZZ.js} +24 -23
  192. package/chunks/{standalone-update-IFM53PE7.js → standalone-update-P42HDFXX.js} +24 -23
  193. package/chunks/{startInteractiveUI-3AVH7L6H.js → startInteractiveUI-LFBLFHJ4.js} +262 -191
  194. package/chunks/{task-create-FCBDX7UH.js → task-create-EILHJJB7.js} +4 -4
  195. package/chunks/{task-list-YIYIUFUA.js → task-list-WGJR6YL5.js} +18 -4
  196. package/chunks/{task-update-MYPFVOVM.js → task-update-XZAHVXJ5.js} +79 -8
  197. package/chunks/{team-create-ZAIA7KAV.js → team-create-EDNOUM2U.js} +23 -23
  198. package/chunks/{team-delete-6UMU4XLS.js → team-delete-UEP4QOOT.js} +3 -3
  199. package/chunks/{team-plan-approval-UNIUS7OD.js → team-plan-approval-QRSNK2DX.js} +22 -21
  200. package/chunks/{terminal-image-renderer-G343P2RV.js → terminal-image-renderer-6J2NPL32.js} +22 -21
  201. package/chunks/{theme-manager-Z3RIT7DB.js → theme-manager-WJMLRBXS.js} +22 -21
  202. package/chunks/{todoWrite-U2R4NVF4.js → todoWrite-ATS62CAP.js} +1 -1
  203. package/chunks/{tool-search-5R2OE4C7.js → tool-search-MZ6WU7DM.js} +9 -7
  204. package/chunks/{total-session-admission-4KBHMQQ2.js → total-session-admission-DQVW2LIP.js} +26 -25
  205. package/chunks/{trustedFolders-LK6T4XC7.js → trustedFolders-QMH25GOX.js} +23 -22
  206. package/chunks/{update-relaunch-UBKROIC6.js → update-relaunch-EG75LLW5.js} +5 -5
  207. package/chunks/{updateCheck-W4Y2GFSD.js → updateCheck-MTZIVGT5.js} +25 -24
  208. package/chunks/{useAutoAcceptIndicator-C7BEICBF.js → useAutoAcceptIndicator-IGCW5EPO.js} +31 -30
  209. package/chunks/{validateNonInterActiveAuth-LTEI2TB6.js → validateNonInterActiveAuth-D5FPAB2C.js} +56 -54
  210. package/chunks/{version-G5A5DGVS.js → version-UOLDLLRB.js} +1 -1
  211. package/chunks/{web-fetch-RTEQTR5Z.js → web-fetch-QKKW6CWH.js} +11 -8
  212. package/chunks/{web-search-5OXHEZPV.js → web-search-JYRBZMCW.js} +2 -2
  213. package/chunks/{workflow-ISQJHPG2.js → workflow-SR7SEAU7.js} +139 -37
  214. package/chunks/{workspace-providers-status-2ZPZPUPZ.js → workspace-providers-status-OXGV4EFF.js} +32 -31
  215. package/chunks/{workspace-registration-store-CYNY6IP6.js → workspace-registration-store-V5EGKLBH.js} +1 -1
  216. package/chunks/{workspace-registry-34GTUUHB.js → workspace-registry-KJVIYTF5.js} +26 -25
  217. package/chunks/{workspace-service-XCDQLE2Y.js → workspace-service-6RLSGUB5.js} +33 -32
  218. package/chunks/{workspace-skills-status-RS4O7AIJ.js → workspace-skills-status-BHFEPDGH.js} +30 -29
  219. package/chunks/{workspace-trust-reconciler-AYGXC7A3.js → workspace-trust-reconciler-DB5ERSUN.js} +33 -32
  220. package/chunks/{write-file-EWXKVVXF.js → write-file-IUP4GTZ4.js} +22 -21
  221. package/chunks/{zh-TW-SQ4HTSCW.js → zh-TW-LVLBK4KF.js} +8 -0
  222. package/chunks/{zh-LMSTK4WD.js → zh-UBMWS2BJ.js} +8 -0
  223. package/chunks/{zoom-image-4BFYJ3HG.js → zoom-image-SRBDTIVA.js} +14 -11
  224. package/cli.js +13 -13
  225. package/locales/ca.js +13 -0
  226. package/locales/de.js +12 -0
  227. package/locales/en.js +11 -0
  228. package/locales/fr.js +13 -0
  229. package/locales/ja.js +13 -0
  230. package/locales/pt.js +12 -0
  231. package/locales/ru.js +12 -0
  232. package/locales/zh-TW.js +11 -0
  233. package/locales/zh.js +11 -0
  234. package/package.json +3 -3
  235. package/web-shell/assets/{arc-CgOpDKvm.js → arc-DItbX0DI.js} +1 -1
  236. package/web-shell/assets/{architectureDiagram-3BPJPVTR-0Ag_m34F.js → architectureDiagram-3BPJPVTR-CmnIlH8y.js} +1 -1
  237. package/web-shell/assets/{blockDiagram-GPEHLZMM-IwTwFau0.js → blockDiagram-GPEHLZMM-pObnB9kw.js} +1 -1
  238. package/web-shell/assets/{c4Diagram-AAUBKEIU-DLa5GUv8.js → c4Diagram-AAUBKEIU-BNBmMS0Y.js} +1 -1
  239. package/web-shell/assets/channel-hZnMfWA9.js +1 -0
  240. package/web-shell/assets/{chunk-2J33WTMH-DcYP-WFG.js → chunk-2J33WTMH-Dz4JBOO6.js} +1 -1
  241. package/web-shell/assets/{chunk-4BX2VUAB-C8MVPMiQ.js → chunk-4BX2VUAB-Cz2GnvAR.js} +1 -1
  242. package/web-shell/assets/{chunk-55IACEB6-BUi4Yfv3.js → chunk-55IACEB6-CC6kOQYx.js} +1 -1
  243. package/web-shell/assets/{chunk-727SXJPM-Cy3Su2YY.js → chunk-727SXJPM-Z4lY4gbh.js} +1 -1
  244. package/web-shell/assets/{chunk-AQP2D5EJ-Dp8Teqv6.js → chunk-AQP2D5EJ-Yz0hZr3D.js} +1 -1
  245. package/web-shell/assets/{chunk-FMBD7UC4-BDTDM-4j.js → chunk-FMBD7UC4-nKmTWTv9.js} +1 -1
  246. package/web-shell/assets/{chunk-ND2GUHAM-7HfDt0r2.js → chunk-ND2GUHAM-CjZz0f6Y.js} +1 -1
  247. package/web-shell/assets/{chunk-QZHKN3VN-BIBU0uCF.js → chunk-QZHKN3VN-Bab3FpuS.js} +1 -1
  248. package/web-shell/assets/classDiagram-4FO5ZUOK-BRnxm8DX.js +1 -0
  249. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BRnxm8DX.js +1 -0
  250. package/web-shell/assets/{cose-bilkent-S5V4N54A-DRLdcg9p.js → cose-bilkent-S5V4N54A-BuSAGFox.js} +1 -1
  251. package/web-shell/assets/{dagre-BM42HDAG-DCCgDwqW.js → dagre-BM42HDAG-CRZXTg3g.js} +1 -1
  252. package/web-shell/assets/{diagram-2AECGRRQ-CsBTBmk3.js → diagram-2AECGRRQ-BFx1Ayrz.js} +1 -1
  253. package/web-shell/assets/{diagram-5GNKFQAL-C9qjH8Lt.js → diagram-5GNKFQAL-B2hMZ3gS.js} +1 -1
  254. package/web-shell/assets/{diagram-KO2AKTUF-Brjs_lj2.js → diagram-KO2AKTUF-DKxAQLmc.js} +1 -1
  255. package/web-shell/assets/{diagram-LMA3HP47-DtiWCl_F.js → diagram-LMA3HP47-Bo1xGqVY.js} +1 -1
  256. package/web-shell/assets/{diagram-OG6HWLK6-DmfKLkBT.js → diagram-OG6HWLK6-B9NG78d-.js} +1 -1
  257. package/web-shell/assets/{erDiagram-TEJ5UH35-Bve_4deH.js → erDiagram-TEJ5UH35-DltQKiqQ.js} +1 -1
  258. package/web-shell/assets/{flowDiagram-I6XJVG4X-C2_kXCcV.js → flowDiagram-I6XJVG4X-5xXGmS4I.js} +1 -1
  259. package/web-shell/assets/{ganttDiagram-6RSMTGT7-BSBIjiTF.js → ganttDiagram-6RSMTGT7-bhGL5n0l.js} +1 -1
  260. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-CyebwI55.js → gitGraphDiagram-PVQCEYII-ZYnz9hgh.js} +1 -1
  261. package/web-shell/assets/{index-d0yfnV1M.js → index-CBBeUArD.js} +1 -1
  262. package/web-shell/assets/index-CbgU-jUo.css +5 -0
  263. package/web-shell/assets/index-_nhzXqab.js +1750 -0
  264. package/web-shell/assets/{infoDiagram-5YYISTIA-CSDGnWxU.js → infoDiagram-5YYISTIA-Cg-jamQf.js} +1 -1
  265. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-BK4jU13p.js → ishikawaDiagram-YF4QCWOH-CcmvKlyq.js} +1 -1
  266. package/web-shell/assets/{journeyDiagram-JHISSGLW-D_W6j-oF.js → journeyDiagram-JHISSGLW-BRsOQZp1.js} +1 -1
  267. package/web-shell/assets/{kanban-definition-UN3LZRKU-C7xf-SdC.js → kanban-definition-UN3LZRKU-xW-dmEHK.js} +1 -1
  268. package/web-shell/assets/{linear-DQmzxpxd.js → linear-ClBfEZH2.js} +1 -1
  269. package/web-shell/assets/{mermaid.core-9P7AGOLG.js → mermaid.core-D65NDkzc.js} +5 -5
  270. package/web-shell/assets/{mindmap-definition-RKZ34NQL-DgcgMyPk.js → mindmap-definition-RKZ34NQL-CyvFslYa.js} +1 -1
  271. package/web-shell/assets/{pieDiagram-4H26LBE5-3wWGb9eG.js → pieDiagram-4H26LBE5-BwSefMyG.js} +1 -1
  272. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-BeCXnQw9.js → quadrantDiagram-W4KKPZXB-GqBJspFS.js} +1 -1
  273. package/web-shell/assets/{requirementDiagram-4Y6WPE33-Bi1UOHDz.js → requirementDiagram-4Y6WPE33-B137PlTJ.js} +1 -1
  274. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-lrLUjjbb.js → sankeyDiagram-5OEKKPKP-maHkkt8i.js} +1 -1
  275. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-BFuqKz6Q.js → sequenceDiagram-3UESZ5HK-DJ1mRaG3.js} +1 -1
  276. package/web-shell/assets/{stateDiagram-AJRCARHV-8DemQLhu.js → stateDiagram-AJRCARHV-aRmP7Q4i.js} +1 -1
  277. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-Db39gvZ_.js +1 -0
  278. package/web-shell/assets/{timeline-definition-PNZ67QCA-uzEuVQXg.js → timeline-definition-PNZ67QCA-CIQ3BRPA.js} +1 -1
  279. package/web-shell/assets/{vennDiagram-CIIHVFJN-BQTeZ_-o.js → vennDiagram-CIIHVFJN-DR9VPd5h.js} +1 -1
  280. package/web-shell/assets/{wardley-L42UT6IY-B6yM5Im7.js → wardley-L42UT6IY-ZcfIbT4D.js} +1 -1
  281. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-ChM8bmgn.js → wardleyDiagram-YWT4CUSO-D0jgPi4T.js} +1 -1
  282. package/web-shell/assets/{xychartDiagram-2RQKCTM6-oNG6HyB1.js → xychartDiagram-2RQKCTM6-BGWU4s5o.js} +1 -1
  283. package/web-shell/index.html +2 -2
  284. package/chunks/chunk-MPHPFVKK.js +0 -48
  285. package/chunks/chunk-RLKC46YT.js +0 -3582
  286. package/chunks/chunk-UPDOZBWO.js +0 -358
  287. package/web-shell/assets/channel-BrGVBa2L.js +0 -1
  288. package/web-shell/assets/classDiagram-4FO5ZUOK-B5TUzJzI.js +0 -1
  289. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-B5TUzJzI.js +0 -1
  290. package/web-shell/assets/index-CIDsgxTe.js +0 -1792
  291. package/web-shell/assets/index-DiQnWi_E.css +0 -5
  292. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-DhM5ixYd.js +0 -1
  293. /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-NHDR76JX.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-QNQAPSN2.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,
@@ -64,7 +65,7 @@ import {
64
65
  clampActiveWorkIntervalMs,
65
66
  isValidTrustedModelPrompt,
66
67
  sessionCloseDrainBudgetMs
67
- } from "./chunk-S5DWMTHO.js";
68
+ } from "./chunk-WP4WIUOJ.js";
68
69
  import {
69
70
  BranchWhilePromptActiveError,
70
71
  BridgeChannelQuarantinedError,
@@ -113,6 +114,8 @@ import {
113
114
  SESSION_ARTIFACT_PERSISTENCE_VERSION,
114
115
  SESSION_TRANSCRIPT_MAX_LIMIT,
115
116
  ShellExecutionService,
117
+ TURN_RESULT_CODE_TEXT_TRUNCATED,
118
+ TURN_RESULT_TEXT_MAX_CHARS,
116
119
  TrustGateError,
117
120
  WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY,
118
121
  WORKSPACE_CONTENT_SHA256_METADATA_KEY,
@@ -120,17 +123,18 @@ import {
120
123
  isReservedWorkspaceMetadataKey,
121
124
  metadataBudgetBytes,
122
125
  normalizeSnapshotPayload,
126
+ normalizeTurnResultError,
123
127
  stableSessionArtifactId
124
- } from "./chunk-4S5337WV.js";
128
+ } from "./chunk-OFTPCOGR.js";
125
129
  import {
126
130
  DAEMON_TRACEPARENT_META_KEY,
127
131
  DAEMON_TRACESTATE_META_KEY
128
- } from "./chunk-TIGA2IVL.js";
132
+ } from "./chunk-OO4FKHLO.js";
129
133
  import {
130
134
  INVOCATION_CONTEXT_META_KEY,
131
135
  PRIVATE_ACP_CAPABILITY_ENV,
132
136
  PRIVATE_PARENT_CAPABILITY_META_KEY
133
- } from "./chunk-RVOJ2FBP.js";
137
+ } from "./chunk-KC4WMLZO.js";
134
138
  import {
135
139
  init_esbuild_shims
136
140
  } from "./chunk-5O2XNYP6.js";
@@ -140,8 +144,8 @@ import {
140
144
 
141
145
  // packages/acp-bridge/src/bridge.ts
142
146
  init_esbuild_shims();
143
- import { randomBytes, randomUUID as randomUUID2 } from "node:crypto";
144
- import * as path3 from "node:path";
147
+ import { randomBytes, randomUUID as randomUUID3 } from "node:crypto";
148
+ import * as path4 from "node:path";
145
149
  import { inspect } from "node:util";
146
150
 
147
151
  // packages/acp-bridge/src/compactionEngine.ts
@@ -1280,6 +1284,267 @@ function getChannelStartupProfileAttributes(response, receivedAtEpochMs, initial
1280
1284
  }
1281
1285
  __name(getChannelStartupProfileAttributes, "getChannelStartupProfileAttributes");
1282
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
+
1283
1548
  // packages/acp-bridge/src/internal/stderrLine.ts
1284
1549
  init_esbuild_shims();
1285
1550
  function writeStderrLine(message) {
@@ -1290,9 +1555,9 @@ __name(writeStderrLine, "writeStderrLine");
1290
1555
 
1291
1556
  // packages/acp-bridge/src/bridgeClient.ts
1292
1557
  init_esbuild_shims();
1293
- import { randomUUID } from "node:crypto";
1294
- import { promises as fs } from "node:fs";
1295
- 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";
1296
1561
 
1297
1562
  // packages/acp-bridge/src/daemonEventTypes.ts
1298
1563
  init_esbuild_shims();
@@ -2278,7 +2543,7 @@ function sliceLineRange(content, startLine, endLine) {
2278
2543
  }
2279
2544
  __name(sliceLineRange, "sliceLineRange");
2280
2545
  var BridgeClient = class {
2281
- 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) {
2282
2547
  this.resolveEntry = resolveEntry;
2283
2548
  this.resolvePendingRestoreEvents = resolvePendingRestoreEvents;
2284
2549
  this.mediator = mediator;
@@ -2300,6 +2565,7 @@ var BridgeClient = class {
2300
2565
  this.getLiveSpeakToUserHandler = getLiveSpeakToUserHandler;
2301
2566
  this.externalToolGuard = externalToolGuard;
2302
2567
  this.onActiveWork = onActiveWork;
2568
+ this.onSessionCatalogChanged = onSessionCatalogChanged;
2303
2569
  }
2304
2570
  static {
2305
2571
  __name(this, "BridgeClient");
@@ -2318,7 +2584,7 @@ var BridgeClient = class {
2318
2584
  options.map((o) => String(o.optionId ?? ""))
2319
2585
  );
2320
2586
  allowedOptionIds.delete("");
2321
- const requestId = randomUUID();
2587
+ const requestId = randomUUID2();
2322
2588
  if (allowedOptionIds.has(CANCEL_VOTE_SENTINEL)) {
2323
2589
  throw new CancelSentinelCollisionError(requestId, CANCEL_VOTE_SENTINEL);
2324
2590
  }
@@ -2564,8 +2830,8 @@ var BridgeClient = class {
2564
2830
  * between tool batches to pull any messages the browser queued mid-turn. We splice the per-session
2565
2831
  * queue, return them to the child as the response, and — when non-empty —
2566
2832
  * publish a `mid_turn_message_injected` SSE frame so the browser can move
2567
- * those messages out of its pending queue (a dedupe signal, not a transcript
2568
- * 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
2569
2835
  * the SDK's
2570
2836
  * default for an unimplemented client surface; the child's drain caller
2571
2837
  * treats that as "drain unsupported" and stops asking.
@@ -2599,9 +2865,12 @@ var BridgeClient = class {
2599
2865
  throw RequestError.methodNotFound(method);
2600
2866
  }
2601
2867
  const sessionId = typeof params["sessionId"] === "string" ? params["sessionId"] : void 0;
2602
- 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
+ }
2603
2872
  const entry = this.resolveEntry(sessionId);
2604
- if (!entry) return { messages: [], hasQueuedPrompt: false };
2873
+ if (!entry) return { messages: [], items: [], hasQueuedPrompt: false };
2605
2874
  const drained = entry.midTurnMessageQueue.splice(0);
2606
2875
  if (drained.length > 0) {
2607
2876
  for (const item of drained) {
@@ -2614,6 +2883,64 @@ var BridgeClient = class {
2614
2883
  );
2615
2884
  }
2616
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
+ }
2617
2944
  const echoed = drained.filter((item) => !item.queueOnly);
2618
2945
  const messages = drained.map((item) => item.text);
2619
2946
  const hasQueuedPrompt = entry.pendingPromptList.some(
@@ -2626,14 +2953,21 @@ var BridgeClient = class {
2626
2953
  data: {
2627
2954
  sessionId: entry.sessionId,
2628
2955
  messages: echoed.map((item) => item.text),
2629
- 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
+ }))
2630
2964
  }
2631
2965
  });
2632
2966
  writeStderrLine(
2633
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`
2634
2968
  );
2635
2969
  }
2636
- return { messages, hasQueuedPrompt };
2970
+ return { messages, items, hasQueuedPrompt };
2637
2971
  }
2638
2972
  async handleExternalToolGuardPrepare(params) {
2639
2973
  if (!this.externalToolGuard) {
@@ -3160,6 +3494,7 @@ var BridgeClient = class {
3160
3494
  return;
3161
3495
  const entry = this.resolveEntry(sessionId2);
3162
3496
  if (!entry) return;
3497
+ this.onSessionCatalogChanged?.();
3163
3498
  try {
3164
3499
  entry.events.publish({
3165
3500
  type: "session_metadata_updated",
@@ -3602,20 +3937,20 @@ var BridgeClient = class {
3602
3937
  }
3603
3938
  let realTarget = params.path;
3604
3939
  try {
3605
- realTarget = await fs.realpath(params.path);
3940
+ realTarget = await fs2.realpath(params.path);
3606
3941
  } catch (err) {
3607
3942
  const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
3608
3943
  if (code !== "ENOENT") throw err;
3609
3944
  try {
3610
- const linkTarget = await fs.readlink(params.path);
3611
- 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);
3612
3947
  } catch {
3613
3948
  }
3614
3949
  }
3615
- const tmp = `${realTarget}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
3950
+ const tmp = `${realTarget}.${process.pid}.${Date.now()}.${randomUUID2()}.tmp`;
3616
3951
  let preserveMode;
3617
3952
  try {
3618
- const targetStat = await fs.stat(realTarget);
3953
+ const targetStat = await fs2.stat(realTarget);
3619
3954
  preserveMode = {
3620
3955
  mode: targetStat.mode & 4095,
3621
3956
  uid: targetStat.uid,
@@ -3626,20 +3961,20 @@ var BridgeClient = class {
3626
3961
  if (code !== "ENOENT") throw err;
3627
3962
  }
3628
3963
  try {
3629
- await fs.writeFile(tmp, params.content, {
3964
+ await fs2.writeFile(tmp, params.content, {
3630
3965
  encoding: "utf8",
3631
3966
  flag: "wx",
3632
3967
  mode: preserveMode?.mode ?? 384
3633
3968
  });
3634
3969
  if (preserveMode) {
3635
- await fs.chmod(tmp, preserveMode.mode).catch(() => {
3970
+ await fs2.chmod(tmp, preserveMode.mode).catch(() => {
3636
3971
  });
3637
- await fs.chown(tmp, preserveMode.uid, preserveMode.gid).catch(() => {
3972
+ await fs2.chown(tmp, preserveMode.uid, preserveMode.gid).catch(() => {
3638
3973
  });
3639
3974
  }
3640
- await fs.rename(tmp, realTarget);
3975
+ await fs2.rename(tmp, realTarget);
3641
3976
  } catch (err) {
3642
- await fs.unlink(tmp).catch(() => {
3977
+ await fs2.unlink(tmp).catch(() => {
3643
3978
  });
3644
3979
  throw err;
3645
3980
  }
@@ -3669,7 +4004,7 @@ var BridgeClient = class {
3669
4004
  );
3670
4005
  }
3671
4006
  const READ_FILE_SIZE_CAP = 100 * 1024 * 1024;
3672
- const stats = await fs.stat(params.path);
4007
+ const stats = await fs2.stat(params.path);
3673
4008
  if (!stats.isFile()) {
3674
4009
  throw new Error(
3675
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.`
@@ -3680,7 +4015,7 @@ var BridgeClient = class {
3680
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.`
3681
4016
  );
3682
4017
  }
3683
- const content = await fs.readFile(params.path, "utf8");
4018
+ const content = await fs2.readFile(params.path, "utf8");
3684
4019
  if (typeof params.line === "number" || typeof params.limit === "number") {
3685
4020
  const startLine = params.line ?? 1;
3686
4021
  const start = startLine > 0 ? startLine - 1 : 0;
@@ -3882,8 +4217,8 @@ var GenerationStreamQueue = class {
3882
4217
  // packages/acp-bridge/src/sessionArtifacts.ts
3883
4218
  init_esbuild_shims();
3884
4219
  import { createHash } from "node:crypto";
3885
- import { constants as fsConstants, promises as fs2 } from "node:fs";
3886
- import path2 from "node:path";
4220
+ import { constants as fsConstants, promises as fs3 } from "node:fs";
4221
+ import path3 from "node:path";
3887
4222
  var SOURCE_RESERVATIONS = {
3888
4223
  tool: 100,
3889
4224
  client: 50,
@@ -4954,7 +5289,7 @@ var SessionArtifactStore = class {
4954
5289
  }
4955
5290
  getRealWorkspaceCwd() {
4956
5291
  if (!this.realWorkspaceCwdPromise) {
4957
- const promise = fs2.realpath(this.workspaceCwd).catch((error) => {
5292
+ const promise = fs3.realpath(this.workspaceCwd).catch((error) => {
4958
5293
  if (this.realWorkspaceCwdPromise === promise) {
4959
5294
  this.realWorkspaceCwdPromise = void 0;
4960
5295
  }
@@ -5074,8 +5409,15 @@ function mergeBatchArtifact(existing, next) {
5074
5409
  delete merged.workspacePath;
5075
5410
  return merged;
5076
5411
  }
5412
+ const refreshDisplay = existing.storage === "workspace" && next.storage === "workspace" && shouldRefreshWorkspaceDisplay(next, existing);
5077
5413
  return {
5078
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,
5079
5421
  status: next.status,
5080
5422
  sizeBytes: mergeSizeBytes(existing, next),
5081
5423
  metadata: mergeMetadata(existing, next),
@@ -5122,6 +5464,13 @@ function mergeArtifact(existing, incoming) {
5122
5464
  next.description = incoming.description;
5123
5465
  delete next.workspacePath;
5124
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;
5125
5474
  }
5126
5475
  const changed = !publicArtifactsEqual(
5127
5476
  toPublicArtifact(existing),
@@ -5137,6 +5486,16 @@ function shouldRecordEphemeralUnpin(existing, incoming) {
5137
5486
  return incoming.retentionExplicit && incoming.retention === "ephemeral" && (existing.retention !== "ephemeral" || existing.persistedAt !== void 0 || existing.durableTombstoneRequired === true);
5138
5487
  }
5139
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");
5140
5499
  function publicArtifactsEqual(a, b) {
5141
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;
5142
5501
  }
@@ -5589,7 +5948,7 @@ function buildIdentityKey(input) {
5589
5948
  }
5590
5949
  __name(buildIdentityKey, "buildIdentityKey");
5591
5950
  function managedIdForWorkspacePath(workspaceCwd, workspacePath) {
5592
- 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);
5593
5952
  }
5594
5953
  __name(managedIdForWorkspacePath, "managedIdForWorkspacePath");
5595
5954
  function normalizeString(value, field, maxLength, required) {
@@ -5636,7 +5995,7 @@ __name(normalizeString, "normalizeString");
5636
5995
  function normalizeManagedId(value) {
5637
5996
  const managedId = normalizeString(value, "managedId", 200, false);
5638
5997
  if (!managedId) return void 0;
5639
- 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)) {
5640
5999
  throw new SessionArtifactValidationError(
5641
6000
  "managedId must be an opaque managed resource id",
5642
6001
  "managedId"
@@ -5670,21 +6029,21 @@ function hasUnsafeDisplayPayload(value) {
5670
6029
  __name(hasUnsafeDisplayPayload, "hasUnsafeDisplayPayload");
5671
6030
  function normalizeWorkspacePath(raw, workspaceCwd) {
5672
6031
  const trimmed = normalizeString(raw, "workspacePath", 500, true);
5673
- if (path2.isAbsolute(trimmed)) {
6032
+ if (path3.isAbsolute(trimmed)) {
5674
6033
  throw new SessionArtifactValidationError(
5675
6034
  "workspacePath must be relative to the workspace",
5676
6035
  "workspacePath"
5677
6036
  );
5678
6037
  }
5679
- const absolute = path2.resolve(workspaceCwd, trimmed);
5680
- const relative = path2.relative(workspaceCwd, absolute);
6038
+ const absolute = path3.resolve(workspaceCwd, trimmed);
6039
+ const relative = path3.relative(workspaceCwd, absolute);
5681
6040
  if (!relative || isOutsidePath(relative)) {
5682
6041
  throw new SessionArtifactValidationError(
5683
6042
  "workspacePath must stay inside the workspace",
5684
6043
  "workspacePath"
5685
6044
  );
5686
6045
  }
5687
- return relative.split(path2.sep).join("/");
6046
+ return relative.split(path3.sep).join("/");
5688
6047
  }
5689
6048
  __name(normalizeWorkspacePath, "normalizeWorkspacePath");
5690
6049
  function normalizeArtifactUrl(raw, allowFile) {
@@ -5885,7 +6244,7 @@ __name(normalizeSizeBytes, "normalizeSizeBytes");
5885
6244
  function inferKind(input) {
5886
6245
  if (input.storage === "published") return "html";
5887
6246
  if (input.url) return "link";
5888
- const ext = input.workspacePath ? path2.extname(input.workspacePath).toLowerCase() : "";
6247
+ const ext = input.workspacePath ? path3.extname(input.workspacePath).toLowerCase() : "";
5889
6248
  if (ext === ".html" || ext === ".htm") return "html";
5890
6249
  if ([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"].includes(ext)) {
5891
6250
  return "image";
@@ -5899,15 +6258,15 @@ function inferKind(input) {
5899
6258
  __name(inferKind, "inferKind");
5900
6259
  async function getWorkspaceStatus(workspacePath, realWorkspaceCwd, expected, options = { hashContent: true }) {
5901
6260
  const realWorkspace = await realWorkspaceCwd;
5902
- const absolutePath = path2.resolve(realWorkspace, workspacePath);
6261
+ const absolutePath = path3.resolve(realWorkspace, workspacePath);
5903
6262
  try {
5904
- const realPath = await fs2.realpath(absolutePath);
5905
- const relative = path2.relative(realWorkspace, realPath);
6263
+ const realPath = await fs3.realpath(absolutePath);
6264
+ const relative = path3.relative(realWorkspace, realPath);
5906
6265
  if (!relative || isOutsidePath(relative)) {
5907
6266
  return { status: "missing", escaped: true };
5908
6267
  }
5909
- const preOpenStat = await fs2.lstat(realPath);
5910
- const handle = await fs2.open(
6268
+ const preOpenStat = await fs3.lstat(realPath);
6269
+ const handle = await fs3.open(
5911
6270
  realPath,
5912
6271
  fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW
5913
6272
  );
@@ -6021,14 +6380,14 @@ function isWorkspaceContentChanged(artifact, status) {
6021
6380
  __name(isWorkspaceContentChanged, "isWorkspaceContentChanged");
6022
6381
  async function danglingSymlinkEscapesWorkspace(absolutePath, realWorkspace) {
6023
6382
  try {
6024
- const stat = await fs2.lstat(absolutePath);
6383
+ const stat = await fs3.lstat(absolutePath);
6025
6384
  if (!stat.isSymbolicLink()) {
6026
6385
  return false;
6027
6386
  }
6028
- const target = await fs2.readlink(absolutePath);
6029
- const parent = await fs2.realpath(path2.dirname(absolutePath));
6030
- const targetPath = path2.resolve(parent, target);
6031
- 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));
6032
6391
  } catch (error) {
6033
6392
  if (isNotFoundError(error)) {
6034
6393
  return false;
@@ -6059,7 +6418,7 @@ function isNoFollowSymlinkError(error) {
6059
6418
  }
6060
6419
  __name(isNoFollowSymlinkError, "isNoFollowSymlinkError");
6061
6420
  function isOutsidePath(relative) {
6062
- return relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative);
6421
+ return relative === ".." || relative.startsWith(`..${path3.sep}`) || path3.isAbsolute(relative);
6063
6422
  }
6064
6423
  __name(isOutsidePath, "isOutsidePath");
6065
6424
 
@@ -6866,6 +7225,57 @@ function broadcastTurnError(entry, sessionId, err, promptId, originatorClientId,
6866
7225
  }
6867
7226
  }
6868
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");
6869
7279
  function publishPromptTerminal(entry, pendingEntry, terminal) {
6870
7280
  if (pendingEntry.terminalPublished) {
6871
7281
  writeServeDebugLine(
@@ -6874,6 +7284,7 @@ function publishPromptTerminal(entry, pendingEntry, terminal) {
6874
7284
  return;
6875
7285
  }
6876
7286
  pendingEntry.terminalPublished = true;
7287
+ rememberTerminalTurnStatus(entry, pendingEntry, terminal);
6877
7288
  const originatorClientId = pendingEntry.originatorClientId;
6878
7289
  const mutateTurnState = pendingEntry.state === "running";
6879
7290
  if (!mutateTurnState && entry.turnErrorEvent) {
@@ -6941,16 +7352,109 @@ function extractPromptText(prompt) {
6941
7352
  if (!Array.isArray(prompt)) return "";
6942
7353
  let hasImage = false;
6943
7354
  for (const block of prompt) {
6944
- if (block["type"] === "image") {
7355
+ const record = block;
7356
+ if (record["type"] === "image") {
6945
7357
  hasImage = true;
6946
7358
  }
6947
- if (block["type"] === "text" && typeof block["text"] === "string" && block["text"].length > 0) {
6948
- return block["text"];
7359
+ if (record["type"] === "text" && typeof record["text"] === "string" && record["text"].length > 0) {
7360
+ return record["text"];
6949
7361
  }
6950
7362
  }
6951
7363
  return hasImage ? "[image]" : "";
6952
7364
  }
6953
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");
6954
7458
  var DEFAULT_INIT_TIMEOUT_MS = 1e4;
6955
7459
  var PERSIST_TIMEOUT_MS = 5e3;
6956
7460
  var MAX_PARENT_PERSIST_ATTEMPTS = 3;
@@ -6968,6 +7472,15 @@ var MAX_EMPTY_TRANSCRIPT_PAGES = 20;
6968
7472
  var SHELL_COMMAND_TIMEOUT_MS = 12e4;
6969
7473
  var MAX_SHELL_OUTPUT_FOR_HISTORY = 1e4;
6970
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");
6971
7484
  var DEFAULT_MAX_SESSIONS = 32;
6972
7485
  var DEFAULT_MAX_PENDING_PROMPTS_PER_SESSION = 5;
6973
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;
@@ -6976,6 +7489,8 @@ var DEFAULT_PERMISSION_TIMEOUT_MS = 5 * 60 * 1e3;
6976
7489
  var DEFAULT_MAX_PENDING_PER_SESSION = 64;
6977
7490
  var DEFAULT_SESSION_REAP_INTERVAL_MS = 6e4;
6978
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;
6979
7494
  function createAcpSessionBridge(opts) {
6980
7495
  let liveScreenContextCaptureHandler;
6981
7496
  let liveTaskToolRequestHandler;
@@ -7034,7 +7549,17 @@ function createAcpSessionBridge(opts) {
7034
7549
  );
7035
7550
  }
7036
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");
7037
7561
  const emitSessionLifecycle = /* @__PURE__ */ __name((event) => {
7562
+ markSessionCatalogChanged();
7038
7563
  try {
7039
7564
  opts.sessionLifecycle?.(event);
7040
7565
  } catch (err) {
@@ -7104,7 +7629,7 @@ function createAcpSessionBridge(opts) {
7104
7629
  } else {
7105
7630
  maxPendingPromptsPerSession = maxPendingPromptsRaw;
7106
7631
  }
7107
- if (!path3.isAbsolute(opts.boundWorkspace)) {
7632
+ if (!path4.isAbsolute(opts.boundWorkspace)) {
7108
7633
  throw new TypeError(
7109
7634
  `Invalid boundWorkspace: "${opts.boundWorkspace}". Must be an absolute path.`
7110
7635
  );
@@ -7127,6 +7652,7 @@ function createAcpSessionBridge(opts) {
7127
7652
  DEFAULT_SESSION_IDLE_TIMEOUT_MS
7128
7653
  );
7129
7654
  let sessionReaper;
7655
+ let mediaSweeper;
7130
7656
  let lastActivityTimestamp = null;
7131
7657
  let activePromptCounter = 0;
7132
7658
  function touchActivity() {
@@ -7443,9 +7969,32 @@ function createAcpSessionBridge(opts) {
7443
7969
  }
7444
7970
  }
7445
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");
7446
7988
  function startSessionReaper() {
7447
- if (sessionReapIntervalMs <= 0 || sessionIdleTimeoutMs <= 0) {
7448
- 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();
7449
7998
  return;
7450
7999
  }
7451
8000
  writeStderrLine(
@@ -7455,6 +8004,7 @@ function createAcpSessionBridge(opts) {
7455
8004
  if (shuttingDown) return;
7456
8005
  const now = Date.now();
7457
8006
  for (const [id, entry] of byId) {
8007
+ if (sessionIdleTimeoutMs <= 0) break;
7458
8008
  if (!entryIsAutoCloseCandidate(entry)) continue;
7459
8009
  const lastActive = entry.sessionLastSeenAt ?? Date.parse(entry.createdAt);
7460
8010
  const idle = now - lastActive;
@@ -7467,6 +8017,7 @@ function createAcpSessionBridge(opts) {
7467
8017
  closeReason: "idle_timeout"
7468
8018
  });
7469
8019
  }
8020
+ sweepRetainedMedia();
7470
8021
  }, sessionReapIntervalMs);
7471
8022
  sessionReaper.unref();
7472
8023
  }
@@ -7476,11 +8027,29 @@ function createAcpSessionBridge(opts) {
7476
8027
  clearInterval(sessionReaper);
7477
8028
  sessionReaper = void 0;
7478
8029
  }
8030
+ if (mediaSweeper !== void 0) {
8031
+ clearInterval(mediaSweeper);
8032
+ mediaSweeper = void 0;
8033
+ }
7479
8034
  }
7480
8035
  __name(stopSessionReaper, "stopSessionReaper");
7481
8036
  const aliveChannels = /* @__PURE__ */ new Set();
7482
8037
  let inFlightChannelSpawn;
7483
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");
7484
8053
  const forwardRunningPromptCancel = /* @__PURE__ */ __name(async (entry, pending, notification) => {
7485
8054
  if (pending.cancelForwardInitial) {
7486
8055
  return pending.cancelForwardInitial;
@@ -7620,7 +8189,7 @@ function createAcpSessionBridge(opts) {
7620
8189
  return limits;
7621
8190
  }, "journalSessionLimits");
7622
8191
  const unregisterJournalGrowthSessionLimits = journalGrowthPolicy !== void 0 && opts.registerJournalGrowthSessionLimits !== void 0 ? opts.registerJournalGrowthSessionLimits(journalSessionLimits) : void 0;
7623
- const createClientId = /* @__PURE__ */ __name(() => `client_${randomUUID2()}`, "createClientId");
8192
+ const createClientId = /* @__PURE__ */ __name(() => `client_${randomUUID3()}`, "createClientId");
7624
8193
  const registerClient = /* @__PURE__ */ __name((entry, requestedClientId) => {
7625
8194
  if (requestedClientId && entry.clientIds.has(requestedClientId)) {
7626
8195
  entry.clientIds.set(
@@ -7679,7 +8248,7 @@ function createAcpSessionBridge(opts) {
7679
8248
  if (inFlightChannelSpawn) return await inFlightChannelSpawn;
7680
8249
  const promise = (async () => {
7681
8250
  const privateParentCapability = randomBytes(32).toString("base64url");
7682
- const acpChannelId = randomUUID2();
8251
+ const acpChannelId = randomUUID3();
7683
8252
  const channel = await telemetry.withSpan(
7684
8253
  "channel.spawn",
7685
8254
  {
@@ -7800,7 +8369,10 @@ function createAcpSessionBridge(opts) {
7800
8369
  const currentInfo = infoRef.current;
7801
8370
  if (!currentInfo) return;
7802
8371
  applyActiveWorkSnapshot(currentInfo, snapshot);
7803
- }
8372
+ },
8373
+ // Child-side automatic title updates change persisted catalog
8374
+ // metadata the bridge never sees; forward the catalog-clock mark.
8375
+ markSessionCatalogChanged
7804
8376
  );
7805
8377
  const rawConnection = new ClientSideConnection(
7806
8378
  () => channel.transportGuard ? createLogSafeAcpClient(client, channel.transportGuard) : client,
@@ -7938,6 +8510,9 @@ function createAcpSessionBridge(opts) {
7938
8510
  touchActivity();
7939
8511
  }
7940
8512
  byId.delete(sid);
8513
+ if (retainedMedia.has(sid)) {
8514
+ retainedMediaDetachedAt.set(sid, Date.now());
8515
+ }
7941
8516
  telemetry.metrics?.sessionLifecycle("die");
7942
8517
  emitSessionLifecycle({
7943
8518
  type: "removed",
@@ -8975,6 +9550,7 @@ function createAcpSessionBridge(opts) {
8975
9550
  workspaceCwd,
8976
9551
  persistence: createSessionArtifactPersistence(ci.connection, sessionId)
8977
9552
  }),
9553
+ media: retainSessionMedia(sessionId),
8978
9554
  recordingDegraded: false,
8979
9555
  closing: false,
8980
9556
  cwdChangeQueue: Promise.resolve(),
@@ -8982,6 +9558,9 @@ function createAcpSessionBridge(opts) {
8982
9558
  pendingPromptCount: 0,
8983
9559
  pendingAgentNotificationCount: 0,
8984
9560
  pendingPromptList: [],
9561
+ terminalTurnStatuses: /* @__PURE__ */ new Map(),
9562
+ enrichedTerminalPromptIds: /* @__PURE__ */ new Set(),
9563
+ rewindGeneration: 0,
8985
9564
  midTurnMessageQueue: [],
8986
9565
  settledMidTurnMessageIds: [],
8987
9566
  promotedMidTurnMessageIds: [],
@@ -9931,6 +10510,9 @@ function createAcpSessionBridge(opts) {
9931
10510
  const restoreEntry = byId.get(req.sessionId);
9932
10511
  if (restoreEntry?.events === restoreEvents) {
9933
10512
  byId.delete(req.sessionId);
10513
+ if (retainedMedia.has(req.sessionId)) {
10514
+ retainedMediaDetachedAt.set(req.sessionId, Date.now());
10515
+ }
9934
10516
  ci?.sessionIds.delete(req.sessionId);
9935
10517
  emitSessionLifecycle({
9936
10518
  type: "removed",
@@ -10094,6 +10676,15 @@ function createAcpSessionBridge(opts) {
10094
10676
  } catch {
10095
10677
  }
10096
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
+ }
10097
10688
  if (!agentSessionClosed) {
10098
10689
  try {
10099
10690
  await telemetry.withSpan(
@@ -10122,25 +10713,76 @@ function createAcpSessionBridge(opts) {
10122
10713
  ring.splice(0, ring.length - MID_TURN_RECONCILIATION_RING_SIZE);
10123
10714
  }
10124
10715
  }, "rememberMidTurnId");
10125
- const promoteMidTurnMessage = /* @__PURE__ */ __name((entry, messageId, text, originatorClientId) => {
10126
- 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(
10127
10753
  entry.sessionId,
10128
10754
  {
10129
10755
  sessionId: entry.sessionId,
10130
- prompt: [{ type: "text", text }]
10756
+ prompt: withMediaDegradationMarker(
10757
+ text ? [{ type: "text", text }] : []
10758
+ )
10131
10759
  },
10132
10760
  void 0,
10133
- {
10134
- promptId: messageId,
10135
- promotedMidTurn: { originatorClientId },
10136
- // Record the id only once sendPrompt owns its FIFO slot: an
10137
- // admission failure must not land the id in the reconciliation
10138
- // ring, or retries would be acked for a message that never runs.
10139
- onPromptAdmitted: /* @__PURE__ */ __name(() => {
10140
- rememberMidTurnId(entry.promotedMidTurnMessageIds, messageId);
10141
- }, "onPromptAdmitted")
10142
- }
10143
- ).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) => {
10144
10786
  writeStderrLine(
10145
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))}`
10146
10788
  );
@@ -10460,7 +11102,7 @@ function createAcpSessionBridge(opts) {
10460
11102
  req.branch,
10461
11103
  req.sessionId
10462
11104
  );
10463
- const tracker = effectiveScope === "single" ? workspaceKey : `${workspaceKey}#${randomUUID2()}`;
11105
+ const tracker = effectiveScope === "single" ? workspaceKey : `${workspaceKey}#${randomUUID3()}`;
10464
11106
  inFlightSpawns.set(tracker, promise);
10465
11107
  try {
10466
11108
  return await promise;
@@ -10490,9 +11132,15 @@ function createAcpSessionBridge(opts) {
10490
11132
  )
10491
11133
  );
10492
11134
  }
11135
+ if (!Array.isArray(req.prompt)) {
11136
+ return Promise.reject(
11137
+ RequestError.invalidParams(void 0, "Prompt must be an array")
11138
+ );
11139
+ }
10493
11140
  const promotedMidTurn = context?.promotedMidTurn;
10494
11141
  const isPromotedMidTurn = promotedMidTurn !== void 0;
10495
11142
  const originatorClientId = promotedMidTurn ? promotedMidTurn.originatorClientId : resolveTrustedClientId(entry, context?.clientId);
11143
+ entry.media.assertReferences(req.prompt);
10496
11144
  const modelPrompt = context?.modelPrompt;
10497
11145
  if (modelPrompt !== void 0 && !isValidTrustedModelPrompt(modelPrompt)) {
10498
11146
  throw new TypeError(
@@ -10516,7 +11164,7 @@ function createAcpSessionBridge(opts) {
10516
11164
  promptSlotReleased = true;
10517
11165
  entry.pendingPromptCount = Math.max(0, entry.pendingPromptCount - 1);
10518
11166
  }, "releasePromptSlot");
10519
- const promptId = context?.promptId ?? randomUUID2();
11167
+ const promptId = context?.promptId ?? randomUUID3();
10520
11168
  const invocationContext = Object.freeze({
10521
11169
  version: 1,
10522
11170
  sessionId,
@@ -10549,6 +11197,7 @@ function createAcpSessionBridge(opts) {
10549
11197
  ...originatorClientId !== void 0 ? { originatorClientId } : {},
10550
11198
  ...isPromotedMidTurn ? { promotedMidTurn: true } : {},
10551
11199
  text: pendingText,
11200
+ content: extractMediaBlocks(req.prompt),
10552
11201
  abortController: pendingAbort,
10553
11202
  state: isQueued ? "queued" : "running"
10554
11203
  };
@@ -10641,6 +11290,7 @@ function createAcpSessionBridge(opts) {
10641
11290
  }
10642
11291
  throw new DOMException("Prompt aborted", "AbortError");
10643
11292
  }
11293
+ pendingEntry.startedAt = Date.now();
10644
11294
  if (pendingEntry.state === "queued" || isPromotedMidTurn) {
10645
11295
  if (pendingEntry.state === "queued") {
10646
11296
  delete entry.todoStopGuardAwaitingQueuedPromptOwnerPromptId;
@@ -10668,10 +11318,25 @@ function createAcpSessionBridge(opts) {
10668
11318
  ...context?.clientId ? { "qwen-code.client_id": context.clientId } : {}
10669
11319
  },
10670
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
+ }
10671
11335
  const normalized = telemetry.injectPromptContext(
10672
11336
  {
10673
11337
  ...req,
10674
- sessionId
11338
+ sessionId,
11339
+ prompt: resolvedPrompt
10675
11340
  }
10676
11341
  );
10677
11342
  assertLivePromptEntry(sessionId, entry);
@@ -10694,6 +11359,7 @@ function createAcpSessionBridge(opts) {
10694
11359
  delete meta[DAEMON_CHANNEL_DELIVERY_META_KEY];
10695
11360
  delete meta[DAEMON_PROMPT_DISPLAY_TEXT_META_KEY];
10696
11361
  delete meta[DAEMON_MODEL_PROMPT_META_KEY];
11362
+ delete meta[DAEMON_MEDIA_REFERENCES_META_KEY];
10697
11363
  delete meta[CHANNEL_PROMPT_META_KEY];
10698
11364
  if (isRetry) {
10699
11365
  meta[DAEMON_RETRY_META_KEY] = true;
@@ -10710,6 +11376,12 @@ function createAcpSessionBridge(opts) {
10710
11376
  if (modelPrompt !== void 0) {
10711
11377
  meta[DAEMON_MODEL_PROMPT_META_KEY] = modelPrompt;
10712
11378
  }
11379
+ const mediaReferences = dispatchBlocks.filter(
11380
+ isSessionMediaReference
11381
+ );
11382
+ if (mediaReferences.length > 0) {
11383
+ meta[DAEMON_MEDIA_REFERENCES_META_KEY] = mediaReferences;
11384
+ }
10713
11385
  if (context?.channelPrompt === true) {
10714
11386
  meta[CHANNEL_PROMPT_META_KEY] = true;
10715
11387
  }
@@ -10738,7 +11410,10 @@ function createAcpSessionBridge(opts) {
10738
11410
  if (!isRetry && !isContinue) {
10739
11411
  echoPromptToSessionBus(
10740
11412
  entry,
10741
- promptRequest,
11413
+ {
11414
+ ...promptRequest,
11415
+ prompt: dispatchBlocks
11416
+ },
10742
11417
  pendingEntry.promptId,
10743
11418
  originatorClientId,
10744
11419
  channelDisplayText
@@ -10894,7 +11569,8 @@ function createAcpSessionBridge(opts) {
10894
11569
  entry,
10895
11570
  message.messageId,
10896
11571
  message.text,
10897
- message.originatorClientId
11572
+ message.originatorClientId,
11573
+ message.content
10898
11574
  );
10899
11575
  }
10900
11576
  void maybeCloseIdleSession(entry, "prompt_settled");
@@ -11141,6 +11817,7 @@ function createAcpSessionBridge(opts) {
11141
11817
  `branchSession: agent returned invalid response: ${JSON.stringify(result)}`
11142
11818
  );
11143
11819
  }
11820
+ markSessionCatalogChanged();
11144
11821
  const rawBranchName = result.displayName ?? result.title;
11145
11822
  const branchDisplayName = typeof rawBranchName === "string" ? rawBranchName : result.newSessionId.slice(0, 8);
11146
11823
  if (!restoreBranch) {
@@ -11334,6 +12011,7 @@ function createAcpSessionBridge(opts) {
11334
12011
  const entry = byId.get(sessionId);
11335
12012
  if (entry) {
11336
12013
  entry.worktree = worktree;
12014
+ markSessionCatalogChanged();
11337
12015
  }
11338
12016
  },
11339
12017
  async closeSession(sessionId, context, closeOpts) {
@@ -11359,6 +12037,7 @@ function createAcpSessionBridge(opts) {
11359
12037
  const nextDisplayName = metadata.displayName || void 0;
11360
12038
  if (entry.displayName !== nextDisplayName) {
11361
12039
  entry.displayName = nextDisplayName;
12040
+ markSessionCatalogChanged();
11362
12041
  writeStderrLine(
11363
12042
  `qwen serve: updated session metadata ${JSON.stringify(sessionId)} displayName=${entry.displayName === void 0 ? "cleared" : "set"}` + (context?.clientId ? ` by client ${JSON.stringify(context.clientId)}` : "")
11364
12043
  );
@@ -11421,7 +12100,7 @@ function createAcpSessionBridge(opts) {
11421
12100
  return warnings.length > 0 ? { ...result, warnings } : result;
11422
12101
  },
11423
12102
  listWorkspaceSessions(workspaceCwd) {
11424
- if (!path3.isAbsolute(workspaceCwd)) return [];
12103
+ if (!path4.isAbsolute(workspaceCwd)) return [];
11425
12104
  const key = workspaceCwd === boundWorkspace ? boundWorkspace : canonicalizeWorkspace(workspaceCwd);
11426
12105
  if (key !== boundWorkspace) return [];
11427
12106
  const out = [];
@@ -11432,6 +12111,8 @@ function createAcpSessionBridge(opts) {
11432
12111
  }
11433
12112
  return out;
11434
12113
  },
12114
+ getSessionCatalogVersion,
12115
+ markSessionCatalogChanged,
11435
12116
  getSessionSummary(sessionId) {
11436
12117
  const entry = byId.get(sessionId);
11437
12118
  if (!entry) throw new SessionNotFoundError(sessionId);
@@ -12124,7 +12805,7 @@ function createAcpSessionBridge(opts) {
12124
12805
  const info = channelInfoForEntry(entry);
12125
12806
  if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
12126
12807
  resolveTrustedClientId(entry, context?.clientId);
12127
- const requestId = randomUUID2();
12808
+ const requestId = randomUUID3();
12128
12809
  const queue = new GenerationStreamQueue(
12129
12810
  GENERATION_STREAM_QUEUE_CAPACITY
12130
12811
  );
@@ -12191,14 +12872,110 @@ function createAcpSessionBridge(opts) {
12191
12872
  const entry = byId.get(sessionId);
12192
12873
  if (!entry) throw new SessionNotFoundError(sessionId);
12193
12874
  resolveTrustedClientId(entry, context?.clientId);
12194
- return entry.pendingPromptList.filter((p) => !p.removed).map((p) => ({
12875
+ return entry.pendingPromptList.filter((p) => !p.removed && !p.terminalPublished).map((p) => ({
12195
12876
  promptId: p.promptId,
12196
12877
  text: p.text,
12878
+ ...p.content ? { content: p.content } : {},
12197
12879
  queuedAt: p.queuedAt,
12198
12880
  state: p.state,
12199
12881
  ...p.originatorClientId !== void 0 ? { originatorClientId: p.originatorClientId } : {}
12200
12882
  }));
12201
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
+ },
12202
12979
  removePendingPrompt(sessionId, promptId, context) {
12203
12980
  const entry = byId.get(sessionId);
12204
12981
  if (!entry) throw new SessionNotFoundError(sessionId);
@@ -12217,6 +12994,7 @@ function createAcpSessionBridge(opts) {
12217
12994
  );
12218
12995
  if (target.state === "queued") {
12219
12996
  entry.pendingPromptList.splice(idx, 1);
12997
+ removeQueuedMedia(entry, target.content);
12220
12998
  } else {
12221
12999
  target.removed = true;
12222
13000
  }
@@ -12242,7 +13020,10 @@ function createAcpSessionBridge(opts) {
12242
13020
  context?.clientId
12243
13021
  );
12244
13022
  const trimmed = message.trim();
12245
- if (trimmed.length === 0) {
13023
+ const mediaBlocks = (options?.content ?? []).filter(
13024
+ (block) => block.type === "image"
13025
+ );
13026
+ if (trimmed.length === 0 && mediaBlocks.length === 0) {
12246
13027
  writeStderrLine(
12247
13028
  `[mid-turn] session=${entry.sessionId} rejected: empty`
12248
13029
  );
@@ -12253,11 +13034,12 @@ function createAcpSessionBridge(opts) {
12253
13034
  (queued) => queued.messageId === requestedMessageId
12254
13035
  );
12255
13036
  if (existing) {
12256
- if (existing.text === trimmed) {
13037
+ const sameMedia = JSON.stringify(existing.content ?? []) === JSON.stringify(mediaBlocks);
13038
+ if (existing.text === trimmed && sameMedia) {
12257
13039
  return { accepted: true, messageId: requestedMessageId };
12258
13040
  }
12259
13041
  writeStderrLine(
12260
- `[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`
12261
13043
  );
12262
13044
  return { accepted: false };
12263
13045
  }
@@ -12265,11 +13047,16 @@ function createAcpSessionBridge(opts) {
12265
13047
  (pending) => pending.promptId === requestedMessageId
12266
13048
  );
12267
13049
  if (promoted) {
12268
- 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) {
12269
13056
  return { accepted: true, messageId: requestedMessageId };
12270
13057
  }
12271
13058
  writeStderrLine(
12272
- `[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`
12273
13060
  );
12274
13061
  return { accepted: false };
12275
13062
  }
@@ -12286,7 +13073,21 @@ function createAcpSessionBridge(opts) {
12286
13073
  );
12287
13074
  return { accepted: false };
12288
13075
  }
12289
- 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();
12290
13091
  if (entry.pendingPromptCount === 0) {
12291
13092
  if (options?.queueOnly) {
12292
13093
  writeStderrLine(
@@ -12294,7 +13095,13 @@ function createAcpSessionBridge(opts) {
12294
13095
  );
12295
13096
  return { accepted: false };
12296
13097
  }
12297
- promoteMidTurnMessage(entry, messageId, trimmed, originatorClientId);
13098
+ promoteMidTurnMessage(
13099
+ entry,
13100
+ messageId,
13101
+ trimmed,
13102
+ originatorClientId,
13103
+ mediaBlocks.length > 0 ? mediaBlocks : void 0
13104
+ );
12298
13105
  return { accepted: true, messageId };
12299
13106
  }
12300
13107
  if (entry.midTurnMessageQueue.length >= MAX_MID_TURN_QUEUE_DEPTH) {
@@ -12306,6 +13113,7 @@ function createAcpSessionBridge(opts) {
12306
13113
  const queuedMessage = {
12307
13114
  messageId,
12308
13115
  text: trimmed,
13116
+ ...mediaBlocks.length > 0 ? { content: mediaBlocks } : {},
12309
13117
  originatorClientId,
12310
13118
  ...options?.queueOnly ? {
12311
13119
  queueOnly: true,
@@ -12365,6 +13173,7 @@ function createAcpSessionBridge(opts) {
12365
13173
  const [removed] = entry.midTurnMessageQueue.splice(index, 1);
12366
13174
  rememberMidTurnId(entry.settledMidTurnMessageIds, messageId);
12367
13175
  if (removed) {
13176
+ removeQueuedMedia(entry, removed.content);
12368
13177
  try {
12369
13178
  entry.events.publish({
12370
13179
  type: "pending_prompt_completed",
@@ -12414,7 +13223,11 @@ function createAcpSessionBridge(opts) {
12414
13223
  // should restore into their queue views.
12415
13224
  messages: entry.midTurnMessageQueue.filter((message) => message.originatorClientId !== void 0).map((message) => ({
12416
13225
  messageId: message.messageId,
12417
- 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 } : {}
12418
13231
  })),
12419
13232
  settledMessageIds: [...entry.settledMidTurnMessageIds],
12420
13233
  promotedMessageIds: [...entry.promotedMidTurnMessageIds]
@@ -12716,6 +13529,9 @@ function createAcpSessionBridge(opts) {
12716
13529
  }
12717
13530
  throw err;
12718
13531
  }
13532
+ entry.terminalTurnStatuses.clear();
13533
+ entry.enrichedTerminalPromptIds.clear();
13534
+ entry.rewindGeneration += 1;
12719
13535
  const targetTurnIndex = response["targetTurnIndex"] ?? 0;
12720
13536
  const filesChanged = response["filesChanged"] ?? [];
12721
13537
  const filesFailed = response["filesFailed"] ?? [];
@@ -12919,7 +13735,7 @@ function createAcpSessionBridge(opts) {
12919
13735
  ]);
12920
13736
  },
12921
13737
  generateWorkspaceContent(prompt, signal, _originatorClientId) {
12922
- const requestId = randomUUID2();
13738
+ const requestId = randomUUID3();
12923
13739
  const queue = new GenerationStreamQueue(
12924
13740
  GENERATION_STREAM_QUEUE_CAPACITY
12925
13741
  );
@@ -13098,6 +13914,11 @@ function createAcpSessionBridge(opts) {
13098
13914
  closingChannel,
13099
13915
  `force kill closing session ${JSON.stringify(sessionId)}`
13100
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
+ });
13101
13922
  return true;
13102
13923
  }
13103
13924
  entry.closing = true;
@@ -13125,6 +13946,11 @@ function createAcpSessionBridge(opts) {
13125
13946
  ci,
13126
13947
  `force kill session ${JSON.stringify(sessionId)}`
13127
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
+ });
13128
13954
  return true;
13129
13955
  }
13130
13956
  entry.closing = false;
@@ -13161,6 +13987,11 @@ function createAcpSessionBridge(opts) {
13161
13987
  } catch {
13162
13988
  }
13163
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
+ });
13164
13995
  if (ci && hasNoChannelWork(ci)) {
13165
13996
  await reapPendingEmptyChannel(ci);
13166
13997
  if (!ci.isDying) {
@@ -13265,6 +14096,8 @@ function createAcpSessionBridge(opts) {
13265
14096
  ) : Promise.resolve();
13266
14097
  const teardownResults = await Promise.allSettled([
13267
14098
  ...channels.map((ci) => ci.channel.kill()),
14099
+ ...[...retainedMedia.values()].map((store) => store.close()),
14100
+ mediaPutQueue,
13268
14101
  ...inFlightSessionAwaits,
13269
14102
  ...inFlightRestoreAwaits,
13270
14103
  inFlightChannelAwait