@qwen-code/qwen-code 0.21.13 → 0.21.14-nightly.20260822.7a4566cb3b

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 (400) hide show
  1. package/bundled/qc-helper/docs/configuration/settings.md +15 -7
  2. package/bundled/qc-helper/docs/extension/introduction.md +2 -0
  3. package/bundled/qc-helper/docs/features/_meta.ts +1 -0
  4. package/bundled/qc-helper/docs/features/channels/dingtalk.md +1 -1
  5. package/bundled/qc-helper/docs/features/code-review.md +25 -3
  6. package/bundled/qc-helper/docs/features/commands.md +120 -8
  7. package/bundled/qc-helper/docs/features/markdown-rendering.md +6 -0
  8. package/bundled/qc-helper/docs/features/sub-agents.md +3 -3
  9. package/bundled/qc-helper/docs/features/terminal-images.md +50 -0
  10. package/bundled/qc-helper/docs/qwen-serve-deploy-local.md +1 -1
  11. package/bundled/qc-helper/docs/qwen-serve.md +21 -4
  12. package/bundled/review/SKILL.md +113 -62
  13. package/chunks/MaxSizedBox-NCWYES5B.js +98 -0
  14. package/chunks/{StandaloneSessionPicker-KEGXNUG6.js → StandaloneSessionPicker-A4V4F64Y.js} +80 -79
  15. package/chunks/{acp-startup-profiler-NMDXYGDN.js → acp-startup-profiler-DDVSJKJK.js} +2 -2
  16. package/chunks/{acpAgent-XPES32S7.js → acpAgent-YYRH7HTQ.js} +1094 -278
  17. package/chunks/{agent-DPTOZFEM.js → agent-NEM254CJ.js} +52 -51
  18. package/chunks/agent-headless-AVFYIRPV.js +88 -0
  19. package/chunks/{anthropicContentGenerator-G7BVXTKN.js → anthropicContentGenerator-IWFTSBSV.js} +25 -25
  20. package/chunks/{artifact-tool-GEXTOPVO.js → artifact-tool-SBXMTVCB.js} +3 -3
  21. package/chunks/{askUserQuestion-B33XRSB7.js → askUserQuestion-KEQJRFD6.js} +3 -3
  22. package/chunks/{bridge-YNQBAA4Z.js → bridge-2CGOUO25.js} +59 -57
  23. package/chunks/{ca-4CQ6MYNQ.js → ca-SLZJD6QH.js} +8 -0
  24. package/chunks/{channel-management-service-GWY2GAIC.js → channel-management-service-FUJ2EFTF.js} +1 -1
  25. package/chunks/channel-settings-store-AZKNHHDC.js +103 -0
  26. package/chunks/{channel-worker-group-7LXCMZ77.js → channel-worker-group-CTE54QL6.js} +8 -7
  27. package/chunks/{channel-worker-manager-RYEME4ZZ.js → channel-worker-manager-4EHTD3T4.js} +8 -7
  28. package/chunks/{channel-worker-supervisor-OEVWBHGT.js → channel-worker-supervisor-F6I6WSWW.js} +6 -5
  29. package/chunks/{chunk-KD5BOPLP.js → chunk-2G4QF4VI.js} +125 -31
  30. package/chunks/{chunk-BHILXOC3.js → chunk-2MTIAN5Z.js} +3 -3
  31. package/chunks/{chunk-A4QRWUDE.js → chunk-2NWPB6T4.js} +6 -6
  32. package/chunks/{chunk-KV27IEHM.js → chunk-2TRNCJK4.js} +1 -1
  33. package/chunks/{chunk-EL5SXY3T.js → chunk-32CHFLGV.js} +198 -23
  34. package/chunks/{chunk-5CVO7YES.js → chunk-36OF5PRW.js} +1 -1
  35. package/chunks/{chunk-X26JYGTD.js → chunk-37BZSWEV.js} +1 -1
  36. package/chunks/{chunk-WFSTRPRO.js → chunk-3CLIDIVQ.js} +3 -3
  37. package/chunks/{chunk-A267JX7H.js → chunk-3HWN4E7N.js} +18 -18
  38. package/chunks/{chunk-ZYYTZUI2.js → chunk-3LYGXZ2H.js} +3 -3
  39. package/chunks/chunk-3SIGAHUX.js +3969 -0
  40. package/chunks/{chunk-BVJS5BSP.js → chunk-3XD4PT3B.js} +1 -1
  41. package/chunks/{chunk-6QFDQMQX.js → chunk-3ZFJUBTP.js} +1 -1
  42. package/chunks/{chunk-HUTNYWBG.js → chunk-45UD2TU4.js} +19 -11
  43. package/chunks/{chunk-4W5U2TUT.js → chunk-4F5WV4RF.js} +4 -4
  44. package/chunks/{chunk-I3G5JWBV.js → chunk-4GP3MNQ2.js} +2695 -1524
  45. package/chunks/{chunk-V26NVGAU.js → chunk-4MIDDJLJ.js} +8 -3
  46. package/chunks/{chunk-TS6625XH.js → chunk-5MK45KZ3.js} +9 -5
  47. package/chunks/{chunk-V57QC6WW.js → chunk-5OTMZVPL.js} +11 -9
  48. package/chunks/{chunk-MC4TLKEI.js → chunk-5PHLBWBL.js} +3 -3
  49. package/chunks/chunk-64LXOKG4.js +367 -0
  50. package/chunks/{chunk-6HY6IF3Z.js → chunk-6NFAEG54.js} +22 -2
  51. package/chunks/{chunk-TIGA2IVL.js → chunk-6NJIPX6W.js} +10 -298
  52. package/chunks/{chunk-MRMJCLIY.js → chunk-6RB2S4QP.js} +3 -3
  53. package/chunks/chunk-6S2OXVJT.js +369 -0
  54. package/chunks/{chunk-O4D7SPJN.js → chunk-6U46RZ25.js} +3 -3
  55. package/chunks/{chunk-JOOFMXGF.js → chunk-6UDHEIUT.js} +6 -6
  56. package/chunks/{chunk-VOQXFAY5.js → chunk-74TONY4F.js} +286 -0
  57. package/chunks/{chunk-LJQAUZMR.js → chunk-7CPJMZVJ.js} +1627 -180
  58. package/chunks/{chunk-T3CHYWJZ.js → chunk-7E7UCJHS.js} +100 -4
  59. package/chunks/{chunk-YZHRN3CF.js → chunk-7IV52LTO.js} +2 -2
  60. package/chunks/{chunk-4PKCUT7F.js → chunk-7JDGSJQH.js} +7 -7
  61. package/chunks/{chunk-GO7STP6N.js → chunk-7VITNOH6.js} +4 -4
  62. package/chunks/{chunk-CU6GJD55.js → chunk-7WLFSDLG.js} +1 -1
  63. package/chunks/{chunk-4WKAA4KS.js → chunk-A4N4FTMH.js} +1 -11
  64. package/chunks/{chunk-BUQDLC2G.js → chunk-A5F2YNO6.js} +3 -3
  65. package/chunks/{chunk-OY5RNSV4.js → chunk-AGCLK4G7.js} +3 -3
  66. package/chunks/{chunk-IA2K2HJE.js → chunk-AP3B7LKD.js} +2 -2
  67. package/chunks/{chunk-Y3YCSWG5.js → chunk-AZF6SME3.js} +2 -2
  68. package/chunks/{chunk-QFJ5JHQR.js → chunk-C72BXMJ5.js} +1 -1
  69. package/chunks/chunk-CETB5XY4.js +3654 -0
  70. package/chunks/{chunk-AA2BEKV3.js → chunk-CTAP6PO3.js} +3 -3
  71. package/chunks/{chunk-MS7GNABG.js → chunk-CXJHQVEK.js} +8 -0
  72. package/chunks/{chunk-QF2NHSPB.js → chunk-D534POS2.js} +274 -225
  73. package/chunks/{chunk-UKMR3CKV.js → chunk-D7QQ6RUB.js} +24 -9
  74. package/chunks/{chunk-DXIBNLBZ.js → chunk-DB2OKNTU.js} +2 -2
  75. package/chunks/{chunk-DGZEMW5R.js → chunk-DJEHYM4H.js} +3 -3
  76. package/chunks/{chunk-O565M6A4.js → chunk-DRVS6K7A.js} +260 -16
  77. package/chunks/{chunk-U3VNBPUO.js → chunk-DV4375RD.js} +5 -5
  78. package/chunks/{chunk-OXCMMLED.js → chunk-E63A23D7.js} +3 -3
  79. package/chunks/{chunk-4NFY2S7N.js → chunk-E7REAOTN.js} +15 -0
  80. package/chunks/{chunk-6ORZ2EDC.js → chunk-EW2IQ3ID.js} +3 -3
  81. package/chunks/{chunk-DGK6P3BQ.js → chunk-EXVK5R7X.js} +13 -13
  82. package/chunks/{chunk-NWNEANAT.js → chunk-FOGQO4FG.js} +953 -660
  83. package/chunks/{chunk-3U3VAL47.js → chunk-FV6PXZRO.js} +1 -1
  84. package/chunks/{chunk-76J3IJ3M.js → chunk-FX5TGHDE.js} +8 -8
  85. package/chunks/{chunk-QIH2HAEN.js → chunk-GXG4PYQB.js} +1 -1
  86. package/chunks/{chunk-FDGGZIYW.js → chunk-HDKCKCYO.js} +3 -3
  87. package/chunks/{chunk-RIAABG2K.js → chunk-HERHU33B.js} +2 -2
  88. package/chunks/{chunk-ZYBBISBY.js → chunk-HETN3NE2.js} +18 -15
  89. package/chunks/{chunk-XIT3GURH.js → chunk-HGBACZGD.js} +3 -3
  90. package/chunks/{chunk-UZPM4XHR.js → chunk-HGH7DXNI.js} +29 -8
  91. package/chunks/{chunk-CHHABXPE.js → chunk-HIK2OF33.js} +1 -1
  92. package/chunks/{chunk-FC4KQFCS.js → chunk-HMJ2P3SV.js} +1 -1
  93. package/chunks/chunk-HRVMTN37.js +26 -0
  94. package/chunks/{chunk-PQ36L4F5.js → chunk-HZZGYJIP.js} +1 -1
  95. package/chunks/{chunk-PT4I7NBA.js → chunk-I432KXWD.js} +1 -1
  96. package/chunks/{chunk-3EZLLVDF.js → chunk-IBJ5S45N.js} +3 -3
  97. package/chunks/{chunk-A4MFR4C5.js → chunk-IO257NUD.js} +3 -5
  98. package/chunks/{chunk-MIPFDQAF.js → chunk-ISJMN3ML.js} +1 -1
  99. package/chunks/chunk-IWOWEENB.js +9169 -0
  100. package/chunks/{chunk-XHRY7BXD.js → chunk-J6SIP2IT.js} +4 -4
  101. package/chunks/{chunk-OD2WU5QY.js → chunk-J7JQXYVL.js} +1 -1
  102. package/chunks/{chunk-NEFOOD54.js → chunk-J7VPB2HF.js} +4 -4
  103. package/chunks/{chunk-P7MPQI75.js → chunk-JGUBDRBC.js} +2 -2
  104. package/chunks/{chunk-SXCOEJO4.js → chunk-JOLJTKIG.js} +1 -1
  105. package/chunks/{chunk-NTTBXH7U.js → chunk-JS73NNWF.js} +8 -0
  106. package/chunks/{chunk-QRCNA3SR.js → chunk-JXLPQTMW.js} +50 -70
  107. package/chunks/{chunk-NNHNLJ2X.js → chunk-K2MSGB3W.js} +317 -72
  108. package/chunks/{chunk-KBXKR5R2.js → chunk-K52PZNU4.js} +1 -1
  109. package/chunks/{chunk-OOQUVWIG.js → chunk-K5OIW3I7.js} +4 -4
  110. package/chunks/{chunk-SOORFWKY.js → chunk-KSPRQSB4.js} +1 -1
  111. package/chunks/{chunk-WSH6GRKG.js → chunk-KVGR4MCF.js} +3 -3
  112. package/chunks/{chunk-6YKYPVDV.js → chunk-LC5M7YVE.js} +4 -4
  113. package/chunks/{chunk-M6NDLY2T.js → chunk-LD5VIJ7S.js} +1 -1
  114. package/chunks/{chunk-OMLJNPBE.js → chunk-LDGWX737.js} +84 -4
  115. package/chunks/{chunk-FAAUPSY2.js → chunk-LI4YHTQK.js} +1 -1
  116. package/chunks/{chunk-CXAOG665.js → chunk-LIKIJAUH.js} +2 -2
  117. package/chunks/chunk-LJZSMWOH.js +18 -0
  118. package/chunks/{chunk-BKPPWD33.js → chunk-M2IL2RTA.js} +22 -2
  119. package/chunks/{chunk-N5YPT6PH.js → chunk-MHOY756Q.js} +25 -13
  120. package/chunks/{chunk-YP5KH5Y7.js → chunk-ND7NOI4P.js} +1 -1
  121. package/chunks/{chunk-AW4TRXOC.js → chunk-NP2LV67M.js} +1 -1
  122. package/chunks/{chunk-GGDBMYGZ.js → chunk-NQB52P65.js} +5 -5
  123. package/chunks/{chunk-E3DYKPYZ.js → chunk-NUGZAFR2.js} +3 -3
  124. package/chunks/{chunk-GMIEIY3Y.js → chunk-O5BDEWBC.js} +3 -3
  125. package/chunks/{chunk-GDXET5XU.js → chunk-O5Z7EB6Y.js} +3 -3
  126. package/chunks/{chunk-5EYKMUUY.js → chunk-OTXXOZKW.js} +1 -1
  127. package/chunks/{chunk-M732YJPW.js → chunk-PALTIYXB.js} +6 -6
  128. package/chunks/{chunk-7UXUJ5T3.js → chunk-PLECQWDB.js} +1 -1
  129. package/chunks/{chunk-MLPXSYXR.js → chunk-PMIKPGDF.js} +66 -32
  130. package/chunks/{chunk-IWPYVAO2.js → chunk-PSQJ24ZZ.js} +3 -3
  131. package/chunks/{chunk-CPHEPGAO.js → chunk-PZRUV52H.js} +2 -2
  132. package/chunks/{chunk-PP4FUZNV.js → chunk-Q7RPLE33.js} +2 -2
  133. package/chunks/{chunk-S5DWMTHO.js → chunk-QQUVPMMZ.js} +2 -0
  134. package/chunks/{chunk-JUEPY766.js → chunk-RNGTMHKN.js} +3 -3
  135. package/chunks/{chunk-TUGSVUJB.js → chunk-ROV6B34L.js} +6 -6
  136. package/chunks/{chunk-RVOJ2FBP.js → chunk-RPJOOK6N.js} +1576 -10633
  137. package/chunks/{chunk-AXN7UPYQ.js → chunk-RQJJZQOH.js} +2 -2
  138. package/chunks/{chunk-AFSG32D5.js → chunk-RRRCWDFO.js} +1 -1
  139. package/chunks/{chunk-2M55OBEB.js → chunk-S3G6YFQC.js} +1 -1
  140. package/chunks/{chunk-4S5337WV.js → chunk-SVU5KGR7.js} +6527 -3314
  141. package/chunks/chunk-TBVSALA3.js +1109 -0
  142. package/chunks/{chunk-QUGAUQGJ.js → chunk-TBWQLLFO.js} +2 -0
  143. package/chunks/{chunk-AS5IN4GK.js → chunk-THK3QUU6.js} +5 -5
  144. package/chunks/{chunk-JNDY2MDR.js → chunk-TLDZKNZP.js} +1 -1
  145. package/chunks/{chunk-ZZO74WKW.js → chunk-TNAIZBPQ.js} +115 -28
  146. package/chunks/{chunk-3Z43GZUQ.js → chunk-TXASDT2M.js} +1 -1
  147. package/chunks/{chunk-CZBYX5MY.js → chunk-U53XZHAE.js} +1 -1
  148. package/chunks/{chunk-HIBNSEJE.js → chunk-UDG5EZJI.js} +0 -2
  149. package/chunks/{chunk-QNQAPSN2.js → chunk-URN2JIAQ.js} +4 -1
  150. package/chunks/{chunk-EZ5RP345.js → chunk-UYQTX2P6.js} +44 -13
  151. package/chunks/{chunk-BZ263IKX.js → chunk-UZPYR3EV.js} +1 -1
  152. package/chunks/{chunk-YZOSNW4R.js → chunk-VFACN2CE.js} +1 -1
  153. package/chunks/{chunk-RWDNJBWN.js → chunk-VLRBJ24J.js} +2 -2
  154. package/chunks/{chunk-NHDR76JX.js → chunk-VMJL7RH6.js} +4 -4
  155. package/chunks/{chunk-E23IPNGS.js → chunk-VOTDAZR7.js} +35 -6
  156. package/chunks/{chunk-XLLKYULU.js → chunk-VSNPOSDN.js} +2 -2
  157. package/chunks/{chunk-25EYDK42.js → chunk-VUMLT7E5.js} +2 -2
  158. package/chunks/{chunk-H4A72DE5.js → chunk-W4CRPSC5.js} +1 -1
  159. package/chunks/{chunk-NM5V5OVW.js → chunk-WJJYJVQE.js} +3 -3
  160. package/chunks/{chunk-ROIYNTHJ.js → chunk-WRT324N6.js} +3 -3
  161. package/chunks/chunk-WZAD4ZNJ.js +59 -0
  162. package/chunks/{chunk-SG7ZP5PG.js → chunk-WZDM44SB.js} +4 -4
  163. package/chunks/{chunk-JE7KQ2UP.js → chunk-XIQ5HQ2F.js} +469 -77
  164. package/chunks/{chunk-QV5YN5YD.js → chunk-XJOHLP2A.js} +5 -5
  165. package/chunks/{chunk-XUJNK7Y6.js → chunk-XL5K4SVK.js} +1 -1
  166. package/chunks/{chunk-CKSJECTU.js → chunk-XZJKSETB.js} +9 -5
  167. package/chunks/{chunk-YEYY7TEP.js → chunk-Y2FEAXLP.js} +7 -7
  168. package/chunks/{chunk-2B2BF7P7.js → chunk-YDJRMQU4.js} +3 -3
  169. package/chunks/{chunk-46UV252V.js → chunk-YHC2EYYG.js} +35 -38
  170. package/chunks/{chunk-OVZPVYZB.js → chunk-YMVFIYHV.js} +96 -18
  171. package/chunks/{chunk-WGHHA7ZH.js → chunk-Z3JMO2CH.js} +1 -1
  172. package/chunks/{chunk-MA2HEDVP.js → chunk-ZRYQMWEP.js} +3 -3
  173. package/chunks/{chunk-IGGGG5CX.js → chunk-ZTAT23TL.js} +15948 -10201
  174. package/chunks/{computer-use-77C5FHMW.js → computer-use-H2T7IFYX.js} +55 -54
  175. package/chunks/{config-utils-IVHRSXWI.js → config-utils-3DY73VIJ.js} +2 -2
  176. package/chunks/contextCommand-5AAZFFFV.js +94 -0
  177. package/chunks/{core-runtime-7VMIKUTG.js → core-runtime-JWFNKO2S.js} +57 -55
  178. package/chunks/{create-sub-session-2L5MQRN4.js → create-sub-session-33HIYIJE.js} +58 -56
  179. package/chunks/{create-sub-session-KRQULJE6.js → create-sub-session-XBCVGNFU.js} +6 -4
  180. package/chunks/{cron-create-H7UEPDSN.js → cron-create-IKA56DAF.js} +5 -5
  181. package/chunks/{cron-delete-6SZK7YFM.js → cron-delete-K5Q62EGH.js} +5 -5
  182. package/chunks/{cron-list-H25JIHEZ.js → cron-list-QBFGAMP7.js} +5 -5
  183. package/chunks/{daemon-2KTG7ZBN.js → daemon-NNCQ3HWT.js} +806 -147
  184. package/chunks/{daemon-git-worktree-guard-DXYIMWFT.js → daemon-git-worktree-guard-6233JAPJ.js} +54 -53
  185. package/chunks/daemon-status-provider-3EI2KALQ.js +104 -0
  186. package/chunks/daemon-trust-policy-QWWU7CON.js +100 -0
  187. package/chunks/{daemon-trust-policy-monitor-RYYGCQ3J.js → daemon-trust-policy-monitor-FH7GBAKN.js} +60 -59
  188. package/chunks/{de-UBEBKG6V.js → de-AH4LGCF5.js} +8 -0
  189. package/chunks/{deferred-core-runtime-3SGXFXEQ.js → deferred-core-runtime-EDUZQRME.js} +52 -51
  190. package/chunks/{discovery-ORA5J7YP.js → discovery-X2NV535U.js} +1 -1
  191. package/chunks/{display-image-ON3EYFRD.js → display-image-OEJMJV3S.js} +5 -5
  192. package/chunks/{dist-YEPL4RAW.js → dist-5EV2G6MC.js} +2 -3
  193. package/chunks/{dist-MAQXAQ2Y.js → dist-EQ3Q5XHX.js} +45337 -32756
  194. package/chunks/{dist-WJ4BHIEW.js → dist-ESS34DHC.js} +72 -0
  195. package/chunks/{dist-JWMKPWT5.js → dist-EUOUPUFO.js} +3 -4
  196. package/chunks/{dist-IYYN3RUT.js → dist-UOFRXJT6.js} +2798 -1250
  197. package/chunks/{earlyInputCapture-KJMLFVQV.js → earlyInputCapture-VSP42LUR.js} +53 -52
  198. package/chunks/{edit-I4LQFAEC.js → edit-ZTLO5ASX.js} +60 -58
  199. package/chunks/{en-X6YV7GME.js → en-MD3GP5YL.js} +8 -0
  200. package/chunks/{enter-worktree-YNPOJSRH.js → enter-worktree-TMTPDS2S.js} +55 -54
  201. package/chunks/{enterPlanMode-XCPK62ER.js → enterPlanMode-UBAXFMYI.js} +55 -54
  202. package/chunks/{environment-S7CTIHMX.js → environment-C2CZN7CB.js} +55 -54
  203. package/chunks/{errors-7HB2AIDS.js → errors-ZUNFQINF.js} +54 -53
  204. package/chunks/{exit-worktree-AIIY7SR6.js → exit-worktree-572NWEPJ.js} +55 -54
  205. package/chunks/exitPlanMode-QTIXFGZT.js +86 -0
  206. package/chunks/{fast-path-NDJV3ZFJ.js → fast-path-CEEVHQL3.js} +3 -3
  207. package/chunks/{fast-path-settings-LNOFM2YP.js → fast-path-settings-WHX3J3ZI.js} +2 -2
  208. package/chunks/{fr-VENTC2BS.js → fr-ZEML2JWS.js} +8 -0
  209. package/chunks/{gemini-RSUFP5OE.js → gemini-I2YR6GMS.js} +114 -112
  210. package/chunks/{geminiContentGenerator-SQ5XBPZ7.js → geminiContentGenerator-GQWNDZQF.js} +7 -7
  211. package/chunks/getMachineId-bsd-FBOAYNSY.js +48 -0
  212. package/chunks/{getMachineId-bsd-FG7IUY6U.js → getMachineId-bsd-YH7RURBZ.js} +4 -4
  213. package/chunks/{getMachineId-darwin-GLCJI2RA.js → getMachineId-darwin-H6USQYNI.js} +4 -4
  214. package/chunks/getMachineId-darwin-RNJLJUXY.js +47 -0
  215. package/chunks/{getMachineId-linux-O6OPPAKO.js → getMachineId-linux-HF2N63ET.js} +3 -3
  216. package/chunks/getMachineId-linux-UEVRAIAB.js +41 -0
  217. package/chunks/getMachineId-unsupported-FWXYJ7HW.js +31 -0
  218. package/chunks/{getMachineId-unsupported-S6CKYVGC.js → getMachineId-unsupported-NYA5DXRB.js} +3 -3
  219. package/chunks/getMachineId-win-LRVGMHLW.js +50 -0
  220. package/chunks/{getMachineId-win-X5SRNEH7.js → getMachineId-win-SMEGY2DW.js} +4 -4
  221. package/chunks/{glob-J4RUJAHX.js → glob-XFBL2RVT.js} +58 -57
  222. package/chunks/{goal-tools-VHTNVOKK.js → goal-tools-TLJCE7YZ.js} +6 -6
  223. package/chunks/{grep-VJWPLFOT.js → grep-U675LEXE.js} +55 -54
  224. package/chunks/handleAutoUpdate-R6RUCIA2.js +97 -0
  225. package/chunks/{i18n-RM5YOICI.js → i18n-SJCZDZ4C.js} +53 -52
  226. package/chunks/{image-gen-EXECB5XM.js → image-gen-6VY7CA7C.js} +14 -14
  227. package/chunks/initializer-CRDZXJOG.js +101 -0
  228. package/chunks/installationInfo-EW3NEJDZ.js +95 -0
  229. package/chunks/{ja-QHT4EQTO.js → ja-UM5B2HMD.js} +8 -0
  230. package/chunks/{keychain-token-storage-3XFJ5ZB2.js → keychain-token-storage-MTFTAESK.js} +3 -3
  231. package/chunks/list-TZEMTXDH.js +104 -0
  232. package/chunks/{list-agents-OSVDFI6S.js → list-agents-43XUEXJV.js} +6 -6
  233. package/chunks/loadedSettingsAdapter-47DG424L.js +98 -0
  234. package/chunks/{loggingContentGenerator-WF4ZOJEZ.js → loggingContentGenerator-77C6O5VW.js} +31 -29
  235. package/chunks/{loop-wakeup-WQS6JYAG.js → loop-wakeup-E54KDUXC.js} +6 -6
  236. package/chunks/{ls-D3HUFHDP.js → ls-CGL2UC3H.js} +7 -7
  237. package/chunks/{lsp-3QDDJTCS.js → lsp-5IKUO5DX.js} +3 -3
  238. package/chunks/{managed-npm-update-E7IW6N7V.js → managed-npm-update-AJ5GZRUA.js} +53 -52
  239. package/chunks/mcp-AQK656Y4.js +98 -0
  240. package/chunks/{monitor-EE2UYMGE.js → monitor-UDZXKYT3.js} +55 -54
  241. package/chunks/nonInteractiveCli-6NC3NLZM.js +166 -0
  242. package/chunks/{notebook-edit-NTDY4QDU.js → notebook-edit-G6WZI2YT.js} +59 -57
  243. package/chunks/{openaiContentGenerator-YNFVJMRJ.js → openaiContentGenerator-IHVHP3VB.js} +32 -32
  244. package/chunks/{pidfile-4XSFLYBZ.js → pidfile-EBKS2HBK.js} +53 -52
  245. package/chunks/{processUtils-WDA2MJFD.js → processUtils-B7M35ZLR.js} +2 -2
  246. package/chunks/prompt-terminal-ledger-5JYOPOII.js +93 -0
  247. package/chunks/{pt-VL3I5RNY.js → pt-5BMJ3XYH.js} +8 -0
  248. package/chunks/{qwenContentGenerator-434M7QKY.js → qwenContentGenerator-WCJPPTED.js} +59 -58
  249. package/chunks/{qwenOAuth2-GMYZOO6A.js → qwenOAuth2-S2CLBSEZ.js} +10 -10
  250. package/chunks/read-file-WFM53FTW.js +33 -0
  251. package/chunks/{read-mcp-resource-XD54ZQMC.js → read-mcp-resource-XYES2B42.js} +3 -3
  252. package/chunks/{record-artifact-SEYFJEJF.js → record-artifact-W3IBTGL7.js} +10 -6
  253. package/chunks/{resumeHistoryUtils-EVG3WYK2.js → resumeHistoryUtils-CK4ZC6YO.js} +59 -58
  254. package/chunks/ripGrep-B3PBLEQ3.js +86 -0
  255. package/chunks/{ru-4RCKL4PY.js → ru-6GL66ORV.js} +8 -0
  256. package/chunks/{run-qwen-serve-3QSGRXAN.js → run-qwen-serve-DC5RXI6N.js} +537 -185
  257. package/chunks/{runtime-5MLLZ7DC.js → runtime-UVEETF54.js} +63 -62
  258. package/chunks/{scheduler-RPBJ63CH.js → scheduler-MWT3UMVN.js} +54 -53
  259. package/chunks/{sdk-exporters-grpc-54L4LS5L.js → sdk-exporters-grpc-6J5CKOMO.js} +194 -75
  260. package/chunks/{sdk-exporters-http-XUVP63H6.js → sdk-exporters-http-Q3YZ4752.js} +40 -45
  261. package/chunks/{sdk-impl-YXUEN2Z2.js → sdk-impl-BOMCAN2Z.js} +7211 -5454
  262. package/chunks/{send-message-VZENRWRH.js → send-message-J4VT46OI.js} +8 -8
  263. package/chunks/{serve-HS3XLPXU.js → serve-AKM75GTE.js} +60 -61
  264. package/chunks/{server-TC4A7AAZ.js → server-KU7XIDVT.js} +2221 -399
  265. package/chunks/{session-UBOFXVMN.js → session-DKVYIRWN.js} +108 -105
  266. package/chunks/{settings-D5644OM5.js → settings-NS3PXZDP.js} +63 -62
  267. package/chunks/{shell-3PZULQQV.js → shell-UXCNM2OK.js} +52 -51
  268. package/chunks/{skill-PEVVXQ3L.js → skill-KV7YCCTM.js} +30 -28
  269. package/chunks/{skill-settings-J65JKKES.js → skill-settings-EXW7ESWJ.js} +59 -58
  270. package/chunks/{spawnChannel-IYGXRHSQ.js → spawnChannel-FCP44P4V.js} +55 -54
  271. package/chunks/{standalone-update-IFM53PE7.js → standalone-update-RGW3B2DX.js} +55 -54
  272. package/chunks/{startInteractiveUI-3AVH7L6H.js → startInteractiveUI-UZMF2BIJ.js} +615 -392
  273. package/chunks/{syntheticOutput-7GWFIDXT.js → syntheticOutput-JAXMIMZP.js} +4 -4
  274. package/chunks/{task-create-FCBDX7UH.js → task-create-I2H322E4.js} +12 -12
  275. package/chunks/{task-list-YIYIUFUA.js → task-list-3OLIWFTQ.js} +21 -7
  276. package/chunks/{task-stop-X37KYTKB.js → task-stop-JAUY6MSY.js} +3 -3
  277. package/chunks/{task-update-MYPFVOVM.js → task-update-B3XO4CPI.js} +87 -16
  278. package/chunks/{team-create-ZAIA7KAV.js → team-create-77XY3KAE.js} +56 -56
  279. package/chunks/{team-delete-6UMU4XLS.js → team-delete-Q7GE7RRI.js} +6 -6
  280. package/chunks/{team-plan-approval-UNIUS7OD.js → team-plan-approval-TAQANDQG.js} +55 -54
  281. package/chunks/{terminal-image-renderer-G343P2RV.js → terminal-image-renderer-V4LIF5NS.js} +54 -53
  282. package/chunks/theme-manager-KKBAJ574.js +90 -0
  283. package/chunks/{todoWrite-U2R4NVF4.js → todoWrite-FJJTY2TI.js} +7 -7
  284. package/chunks/{tool-search-5R2OE4C7.js → tool-search-GZJYRK4I.js} +25 -24
  285. package/chunks/{total-session-admission-4KBHMQQ2.js → total-session-admission-NIBNWXHE.js} +59 -57
  286. package/chunks/{trustedFolders-LK6T4XC7.js → trustedFolders-7GEI2NYC.js} +53 -52
  287. package/chunks/{undici-PYHBDDPN.js → undici-S7WJSKGJ.js} +962 -391
  288. package/chunks/{update-relaunch-UBKROIC6.js → update-relaunch-OA3OEBMD.js} +5 -5
  289. package/chunks/{updateCheck-W4Y2GFSD.js → updateCheck-QVHQEVIY.js} +56 -55
  290. package/chunks/{useAutoAcceptIndicator-C7BEICBF.js → useAutoAcceptIndicator-WZ27J7NY.js} +64 -63
  291. package/chunks/{validateNonInterActiveAuth-LTEI2TB6.js → validateNonInterActiveAuth-OXJA2JWG.js} +99 -96
  292. package/chunks/{version-G5A5DGVS.js → version-KJO64WD7.js} +1 -1
  293. package/chunks/{web-fetch-RTEQTR5Z.js → web-fetch-CC5INI2R.js} +25 -23
  294. package/chunks/{web-search-5OXHEZPV.js → web-search-MOAUAN6N.js} +18 -18
  295. package/chunks/{web-shell-static-O73YPVVC.js → web-shell-static-3NUTRHAH.js} +4 -5
  296. package/chunks/{workflow-ISQJHPG2.js → workflow-BN2ZEJMK.js} +366 -105
  297. package/chunks/workspace-providers-status-R5QTZFK3.js +101 -0
  298. package/chunks/{workspace-registration-store-CYNY6IP6.js → workspace-registration-store-WMSLM36M.js} +1 -1
  299. package/chunks/{workspace-registry-34GTUUHB.js → workspace-registry-J3RUCXH6.js} +59 -57
  300. package/chunks/{workspace-service-XCDQLE2Y.js → workspace-service-JJZFPKM4.js} +66 -65
  301. package/chunks/workspace-skills-status-DRIOZRIA.js +101 -0
  302. package/chunks/{workspace-trust-reconciler-AYGXC7A3.js → workspace-trust-reconciler-YAYSN6CX.js} +69 -67
  303. package/chunks/write-file-7HXHFFOY.js +88 -0
  304. package/chunks/{zh-TW-SQ4HTSCW.js → zh-TW-LVLBK4KF.js} +8 -0
  305. package/chunks/{zh-LMSTK4WD.js → zh-UBMWS2BJ.js} +8 -0
  306. package/chunks/{zoom-image-4BFYJ3HG.js → zoom-image-EFMQMU4G.js} +24 -22
  307. package/cli.js +15 -15
  308. package/locales/ca.js +13 -0
  309. package/locales/de.js +12 -0
  310. package/locales/en.js +11 -0
  311. package/locales/fr.js +13 -0
  312. package/locales/ja.js +13 -0
  313. package/locales/pt.js +12 -0
  314. package/locales/ru.js +12 -0
  315. package/locales/zh-TW.js +11 -0
  316. package/locales/zh.js +11 -0
  317. package/package.json +3 -3
  318. package/web-shell/assets/{arc-CgOpDKvm.js → arc-BCP1f-bN.js} +1 -1
  319. package/web-shell/assets/{architectureDiagram-3BPJPVTR-0Ag_m34F.js → architectureDiagram-3BPJPVTR-D-6ajMay.js} +1 -1
  320. package/web-shell/assets/{blockDiagram-GPEHLZMM-IwTwFau0.js → blockDiagram-GPEHLZMM-CcLlnVnf.js} +1 -1
  321. package/web-shell/assets/{c4Diagram-AAUBKEIU-DLa5GUv8.js → c4Diagram-AAUBKEIU-C_Sp9UwQ.js} +1 -1
  322. package/web-shell/assets/channel-BcImYzbA.js +1 -0
  323. package/web-shell/assets/{chunk-2J33WTMH-DcYP-WFG.js → chunk-2J33WTMH-CG0ngvo9.js} +1 -1
  324. package/web-shell/assets/{chunk-4BX2VUAB-C8MVPMiQ.js → chunk-4BX2VUAB-D43LsVdk.js} +1 -1
  325. package/web-shell/assets/{chunk-55IACEB6-BUi4Yfv3.js → chunk-55IACEB6-BqNIn5rz.js} +1 -1
  326. package/web-shell/assets/{chunk-727SXJPM-Cy3Su2YY.js → chunk-727SXJPM-BSHmO08Y.js} +1 -1
  327. package/web-shell/assets/{chunk-AQP2D5EJ-Dp8Teqv6.js → chunk-AQP2D5EJ-CMUbr5tQ.js} +1 -1
  328. package/web-shell/assets/{chunk-FMBD7UC4-BDTDM-4j.js → chunk-FMBD7UC4-Cm6JflnZ.js} +1 -1
  329. package/web-shell/assets/{chunk-ND2GUHAM-7HfDt0r2.js → chunk-ND2GUHAM-CBR39YQZ.js} +1 -1
  330. package/web-shell/assets/{chunk-QZHKN3VN-BIBU0uCF.js → chunk-QZHKN3VN-CWnqaLb-.js} +1 -1
  331. package/web-shell/assets/classDiagram-4FO5ZUOK-OWx0rDRW.js +1 -0
  332. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-OWx0rDRW.js +1 -0
  333. package/web-shell/assets/{cose-bilkent-S5V4N54A-DRLdcg9p.js → cose-bilkent-S5V4N54A-DJlTdnfD.js} +1 -1
  334. package/web-shell/assets/{dagre-BM42HDAG-DCCgDwqW.js → dagre-BM42HDAG-1AFN1uVc.js} +1 -1
  335. package/web-shell/assets/{diagram-2AECGRRQ-CsBTBmk3.js → diagram-2AECGRRQ-gC0GGNSW.js} +1 -1
  336. package/web-shell/assets/{diagram-5GNKFQAL-C9qjH8Lt.js → diagram-5GNKFQAL-CPmRPT1s.js} +1 -1
  337. package/web-shell/assets/{diagram-KO2AKTUF-Brjs_lj2.js → diagram-KO2AKTUF-atigp6Hr.js} +1 -1
  338. package/web-shell/assets/{diagram-LMA3HP47-DtiWCl_F.js → diagram-LMA3HP47-BJnJFSRQ.js} +1 -1
  339. package/web-shell/assets/{diagram-OG6HWLK6-DmfKLkBT.js → diagram-OG6HWLK6-CyvJ4fG2.js} +1 -1
  340. package/web-shell/assets/{erDiagram-TEJ5UH35-Bve_4deH.js → erDiagram-TEJ5UH35-CZac_jT6.js} +1 -1
  341. package/web-shell/assets/{flowDiagram-I6XJVG4X-C2_kXCcV.js → flowDiagram-I6XJVG4X-pd-LQxWB.js} +1 -1
  342. package/web-shell/assets/{ganttDiagram-6RSMTGT7-BSBIjiTF.js → ganttDiagram-6RSMTGT7-CvB1at7l.js} +1 -1
  343. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-CyebwI55.js → gitGraphDiagram-PVQCEYII-B5Y6nkil.js} +1 -1
  344. package/web-shell/assets/index-Bg3DAn8Z.js +1788 -0
  345. package/web-shell/assets/{index-d0yfnV1M.js → index-Bj3ZSUHL.js} +1 -1
  346. package/web-shell/assets/index-mp32EO88.css +5 -0
  347. package/web-shell/assets/{infoDiagram-5YYISTIA-CSDGnWxU.js → infoDiagram-5YYISTIA-BdyWvU4P.js} +1 -1
  348. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-BK4jU13p.js → ishikawaDiagram-YF4QCWOH-CaHbUdvN.js} +1 -1
  349. package/web-shell/assets/{journeyDiagram-JHISSGLW-D_W6j-oF.js → journeyDiagram-JHISSGLW-DR9F3FpG.js} +1 -1
  350. package/web-shell/assets/{kanban-definition-UN3LZRKU-C7xf-SdC.js → kanban-definition-UN3LZRKU-BoEUbkw2.js} +1 -1
  351. package/web-shell/assets/{linear-DQmzxpxd.js → linear-DUbfzV4T.js} +1 -1
  352. package/web-shell/assets/{mermaid.core-9P7AGOLG.js → mermaid.core-CLLkPcpD.js} +5 -5
  353. package/web-shell/assets/{mindmap-definition-RKZ34NQL-DgcgMyPk.js → mindmap-definition-RKZ34NQL-FuKI3Stt.js} +1 -1
  354. package/web-shell/assets/{pieDiagram-4H26LBE5-3wWGb9eG.js → pieDiagram-4H26LBE5-CVAHL7l5.js} +1 -1
  355. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-BeCXnQw9.js → quadrantDiagram-W4KKPZXB-BkGi5fRj.js} +1 -1
  356. package/web-shell/assets/{requirementDiagram-4Y6WPE33-Bi1UOHDz.js → requirementDiagram-4Y6WPE33-B5ghV8-K.js} +1 -1
  357. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-lrLUjjbb.js → sankeyDiagram-5OEKKPKP-CKFbVhkQ.js} +1 -1
  358. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-BFuqKz6Q.js → sequenceDiagram-3UESZ5HK-C5P9ujPd.js} +1 -1
  359. package/web-shell/assets/{stateDiagram-AJRCARHV-8DemQLhu.js → stateDiagram-AJRCARHV-CsDn-Rez.js} +1 -1
  360. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-DpGKD1ez.js +1 -0
  361. package/web-shell/assets/{timeline-definition-PNZ67QCA-uzEuVQXg.js → timeline-definition-PNZ67QCA-B6oLBkTr.js} +1 -1
  362. package/web-shell/assets/{vennDiagram-CIIHVFJN-BQTeZ_-o.js → vennDiagram-CIIHVFJN-DvtBkYwp.js} +1 -1
  363. package/web-shell/assets/{wardley-L42UT6IY-B6yM5Im7.js → wardley-L42UT6IY-D2UwTX16.js} +1 -1
  364. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-ChM8bmgn.js → wardleyDiagram-YWT4CUSO-FnnfCxYI.js} +1 -1
  365. package/web-shell/assets/{xychartDiagram-2RQKCTM6-oNG6HyB1.js → xychartDiagram-2RQKCTM6-C1SaMQ7d.js} +1 -1
  366. package/web-shell/index.html +2 -2
  367. package/chunks/MaxSizedBox-UQALOVVT.js +0 -97
  368. package/chunks/agent-headless-GJCQMURW.js +0 -87
  369. package/chunks/channel-settings-store-WNWYFHG7.js +0 -102
  370. package/chunks/chunk-AZQ3DT76.js +0 -10017
  371. package/chunks/chunk-CR3C7WXL.js +0 -113
  372. package/chunks/chunk-K5IDV3PC.js +0 -519
  373. package/chunks/chunk-MPHPFVKK.js +0 -48
  374. package/chunks/chunk-RLKC46YT.js +0 -3582
  375. package/chunks/chunk-UPDOZBWO.js +0 -358
  376. package/chunks/contextCommand-PJ77GBWL.js +0 -93
  377. package/chunks/daemon-status-provider-ZDOYJCU6.js +0 -102
  378. package/chunks/daemon-trust-policy-E67OYUK2.js +0 -99
  379. package/chunks/exitPlanMode-R4JY7V2O.js +0 -85
  380. package/chunks/handleAutoUpdate-POCX2MNB.js +0 -94
  381. package/chunks/initializer-Z2AFGBNR.js +0 -100
  382. package/chunks/installationInfo-4I5FXNMH.js +0 -92
  383. package/chunks/list-ZMBFIGZA.js +0 -103
  384. package/chunks/loadedSettingsAdapter-T6TV4YJ7.js +0 -97
  385. package/chunks/mcp-6UNYX4HG.js +0 -97
  386. package/chunks/nonInteractiveCli-WAY237O4.js +0 -163
  387. package/chunks/read-file-TAFBKMNI.js +0 -32
  388. package/chunks/ripGrep-WOEK2PXS.js +0 -85
  389. package/chunks/theme-manager-Z3RIT7DB.js +0 -89
  390. package/chunks/undici-3FJYTMOF.js +0 -25914
  391. package/chunks/workspace-providers-status-2ZPZPUPZ.js +0 -100
  392. package/chunks/workspace-skills-status-RS4O7AIJ.js +0 -100
  393. package/chunks/write-file-EWXKVVXF.js +0 -87
  394. package/web-shell/assets/channel-BrGVBa2L.js +0 -1
  395. package/web-shell/assets/classDiagram-4FO5ZUOK-B5TUzJzI.js +0 -1
  396. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-B5TUzJzI.js +0 -1
  397. package/web-shell/assets/index-CIDsgxTe.js +0 -1792
  398. package/web-shell/assets/index-DiQnWi_E.css +0 -5
  399. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-DhM5ixYd.js +0 -1
  400. package/chunks/{dist-BROOP2EG.js → dist-LZO24N3E.js} +1 -1
@@ -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-VMJL7RH6.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-URN2JIAQ.js";
33
33
  import {
34
34
  ACTIVE_WORK_CLOSE_IF_UNHELD_PARAM,
35
35
  ACTIVE_WORK_CLOSE_TIMEOUT_MS,
@@ -44,6 +44,7 @@ import {
44
44
  CHANNEL_PROMPT_META_KEY,
45
45
  CHANNEL_STARTUP_PROFILE_META_KEY,
46
46
  CHANNEL_STARTUP_PROFILE_VERSION,
47
+ DAEMON_ATTACHMENT_REFERENCES_META_KEY,
47
48
  DAEMON_CHANNEL_DELIVERY_META_KEY,
48
49
  DAEMON_MODEL_PROMPT_META_KEY,
49
50
  DAEMON_PROMPT_DISPLAY_TEXT_META_KEY,
@@ -64,7 +65,7 @@ import {
64
65
  clampActiveWorkIntervalMs,
65
66
  isValidTrustedModelPrompt,
66
67
  sessionCloseDrainBudgetMs
67
- } from "./chunk-S5DWMTHO.js";
68
+ } from "./chunk-QQUVPMMZ.js";
68
69
  import {
69
70
  BranchWhilePromptActiveError,
70
71
  BridgeChannelQuarantinedError,
@@ -96,9 +97,8 @@ import {
96
97
  LIVE_TASK_TOOL_NAMES,
97
98
  MAX_LIVE_SCREEN_CONTEXT_TEXT_CHARS,
98
99
  MAX_LIVE_SPEAK_TO_USER_MESSAGE_CHARS,
99
- MAX_SUB_SESSION_NAME_CHARS,
100
- MAX_SUB_SESSION_PROMPT_CHARS
101
- } from "./chunk-HIBNSEJE.js";
100
+ MAX_SUB_SESSION_NAME_CHARS
101
+ } from "./chunk-UDG5EZJI.js";
102
102
  import {
103
103
  EXTERNAL_TOOL_GUARD_READY_META_KEY,
104
104
  EXTERNAL_TOOL_GUARD_REQUIRED_VALUE,
@@ -109,10 +109,15 @@ import {
109
109
  PROTOCOL_VERSION,
110
110
  RequestError
111
111
  } from "./chunk-IW6RQPQB.js";
112
+ import {
113
+ MAX_SUB_SESSION_PROMPT_CHARS
114
+ } from "./chunk-LJZSMWOH.js";
112
115
  import {
113
116
  SESSION_ARTIFACT_PERSISTENCE_VERSION,
114
117
  SESSION_TRANSCRIPT_MAX_LIMIT,
115
118
  ShellExecutionService,
119
+ TURN_RESULT_CODE_TEXT_TRUNCATED,
120
+ TURN_RESULT_TEXT_MAX_CHARS,
116
121
  TrustGateError,
117
122
  WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY,
118
123
  WORKSPACE_CONTENT_SHA256_METADATA_KEY,
@@ -120,17 +125,29 @@ import {
120
125
  isReservedWorkspaceMetadataKey,
121
126
  metadataBudgetBytes,
122
127
  normalizeSnapshotPayload,
128
+ normalizeTurnResultError,
123
129
  stableSessionArtifactId
124
- } from "./chunk-4S5337WV.js";
130
+ } from "./chunk-SVU5KGR7.js";
131
+ import {
132
+ MAX_DIRECTORY_ARTIFACT_DEPTH,
133
+ MAX_DIRECTORY_ARTIFACT_FILES,
134
+ collectRecordableWorkspaceFiles,
135
+ isOfficeDocumentExtension,
136
+ isRecordableDerivedChild,
137
+ pathHasSkippedDirectoryComponent
138
+ } from "./chunk-TBVSALA3.js";
125
139
  import {
126
140
  DAEMON_TRACEPARENT_META_KEY,
127
141
  DAEMON_TRACESTATE_META_KEY
128
- } from "./chunk-TIGA2IVL.js";
142
+ } from "./chunk-6S2OXVJT.js";
129
143
  import {
130
144
  INVOCATION_CONTEXT_META_KEY,
131
145
  PRIVATE_ACP_CAPABILITY_ENV,
132
146
  PRIVATE_PARENT_CAPABILITY_META_KEY
133
- } from "./chunk-RVOJ2FBP.js";
147
+ } from "./chunk-RPJOOK6N.js";
148
+ import {
149
+ getSpecificMimeType
150
+ } from "./chunk-IWOWEENB.js";
134
151
  import {
135
152
  init_esbuild_shims
136
153
  } from "./chunk-5O2XNYP6.js";
@@ -141,7 +158,7 @@ import {
141
158
  // packages/acp-bridge/src/bridge.ts
142
159
  init_esbuild_shims();
143
160
  import { randomBytes, randomUUID as randomUUID2 } from "node:crypto";
144
- import * as path3 from "node:path";
161
+ import * as path4 from "node:path";
145
162
  import { inspect } from "node:util";
146
163
 
147
164
  // packages/acp-bridge/src/compactionEngine.ts
@@ -1280,6 +1297,523 @@ function getChannelStartupProfileAttributes(response, receivedAtEpochMs, initial
1280
1297
  }
1281
1298
  __name(getChannelStartupProfileAttributes, "getChannelStartupProfileAttributes");
1282
1299
 
1300
+ // packages/acp-bridge/src/sessionAttachments.ts
1301
+ init_esbuild_shims();
1302
+ import { promises as fs, statSync } from "node:fs";
1303
+ import { tmpdir } from "node:os";
1304
+ import * as path from "node:path";
1305
+ var SESSION_ATTACHMENT_MAX_ITEM_BYTES = 8 * 1024 * 1024;
1306
+ var SESSION_ATTACHMENT_MAX_NAME_BYTES = 255;
1307
+ var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
1308
+ "image/bmp",
1309
+ "image/gif",
1310
+ "image/jpeg",
1311
+ "image/png",
1312
+ "image/webp"
1313
+ ]);
1314
+ var SESSION_ATTACHMENT_UNAVAILABLE_TEXT = "[Attachment is no longer available]";
1315
+ var SessionAttachmentReferenceError = class extends Error {
1316
+ constructor(message, code) {
1317
+ super(message);
1318
+ this.code = code;
1319
+ this.name = "SessionAttachmentReferenceError";
1320
+ }
1321
+ static {
1322
+ __name(this, "SessionAttachmentReferenceError");
1323
+ }
1324
+ };
1325
+ function isSessionAttachmentReference(value) {
1326
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1327
+ const record = value;
1328
+ return (record["type"] === "image" || record["type"] === "resource") && typeof record["attachmentId"] === "string" && record["attachmentId"].length > 0 && typeof record["mimeType"] === "string" && record["mimeType"].length > 0 && (record["type"] !== "image" || record["mimeType"].startsWith("image/")) && typeof record["size"] === "number" && Number.isSafeInteger(record["size"]) && record["size"] >= 0 && (record["type"] !== "image" || record["size"] > 0);
1329
+ }
1330
+ __name(isSessionAttachmentReference, "isSessionAttachmentReference");
1331
+ function safeAttachmentName(name) {
1332
+ const safeName = path.basename(name.replaceAll("\\", "/")).trim();
1333
+ const isWindowsReserved = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(safeName);
1334
+ const hasInvalidCharacter = Array.from(safeName).some((character) => {
1335
+ const codePoint = character.codePointAt(0);
1336
+ return codePoint !== void 0 && (codePoint <= 31 || codePoint === 127 || codePoint >= 55296 && codePoint <= 57343);
1337
+ });
1338
+ return !safeName || safeName === "." || safeName === ".." || safeName.endsWith(".") || isWindowsReserved || /[<>:"|?*]/.test(safeName) || hasInvalidCharacter || Buffer.byteLength(safeName) > SESSION_ATTACHMENT_MAX_NAME_BYTES ? void 0 : safeName;
1339
+ }
1340
+ __name(safeAttachmentName, "safeAttachmentName");
1341
+ function truncateUtf8(value, maxBytes) {
1342
+ let bytes = 0;
1343
+ let result = "";
1344
+ for (const character of value) {
1345
+ const characterBytes = Buffer.byteLength(character);
1346
+ if (bytes + characterBytes > maxBytes) break;
1347
+ result += character;
1348
+ bytes += characterBytes;
1349
+ }
1350
+ return result;
1351
+ }
1352
+ __name(truncateUtf8, "truncateUtf8");
1353
+ function deduplicatedName(name, suffix) {
1354
+ if (suffix === 0) return name;
1355
+ const extension = path.extname(name);
1356
+ const suffixText = ` (${suffix})`;
1357
+ const stem = name.slice(0, -extension.length || void 0);
1358
+ const extensionBudget = SESSION_ATTACHMENT_MAX_NAME_BYTES - Buffer.byteLength(suffixText) - 1;
1359
+ const safeExtension = truncateUtf8(extension, extensionBudget).replace(
1360
+ /[. ]+$/u,
1361
+ ""
1362
+ );
1363
+ const stemBudget = SESSION_ATTACHMENT_MAX_NAME_BYTES - Buffer.byteLength(suffixText) - Buffer.byteLength(safeExtension);
1364
+ return `${truncateUtf8(stem, stemBudget)}${suffixText}${safeExtension}`;
1365
+ }
1366
+ __name(deduplicatedName, "deduplicatedName");
1367
+ function imageName(mimeType) {
1368
+ const extension = mimeType.slice("image/".length).split(/[;+]/, 1)[0];
1369
+ return `image.${extension === "jpg" ? "jpeg" : extension || "img"}`;
1370
+ }
1371
+ __name(imageName, "imageName");
1372
+ function mimeTypeForName(name) {
1373
+ if ([".ts", ".mts", ".cts", ".tsx"].includes(path.extname(name).toLowerCase())) {
1374
+ return "text/plain";
1375
+ }
1376
+ return getSpecificMimeType(name) ?? "application/octet-stream";
1377
+ }
1378
+ __name(mimeTypeForName, "mimeTypeForName");
1379
+ function isSupportedImageMimeType(mimeType) {
1380
+ return SUPPORTED_IMAGE_MIME_TYPES.has(mimeType);
1381
+ }
1382
+ __name(isSupportedImageMimeType, "isSupportedImageMimeType");
1383
+ function isTextMimeType(mimeType) {
1384
+ return mimeType.startsWith("text/") || mimeType === "application/json" || mimeType.endsWith("+json") || mimeType === "application/xml" || mimeType.endsWith("+xml") || mimeType === "application/javascript" || mimeType === "application/typescript" || mimeType === "application/yaml" || mimeType === "application/x-yaml" || mimeType === "application/toml";
1385
+ }
1386
+ __name(isTextMimeType, "isTextMimeType");
1387
+ function isTextAttachment(data, mimeType) {
1388
+ if (isTextMimeType(mimeType)) return true;
1389
+ if (mimeType.startsWith("image/") || mimeType.startsWith("audio/") || mimeType.startsWith("video/") || mimeType.startsWith("font/") || mimeType === "application/pdf" || data.includes(0)) {
1390
+ return false;
1391
+ }
1392
+ return Buffer.from(data.toString("utf8"), "utf8").equals(data);
1393
+ }
1394
+ __name(isTextAttachment, "isTextAttachment");
1395
+ function withAttachmentDegradationMarker(blocks) {
1396
+ for (let i = blocks.length - 1; i >= 0; i--) {
1397
+ const block = blocks[i];
1398
+ if (block.type === "text") {
1399
+ if (block.text.endsWith(SESSION_ATTACHMENT_UNAVAILABLE_TEXT)) {
1400
+ return [...blocks];
1401
+ }
1402
+ const next = [...blocks];
1403
+ next[i] = {
1404
+ type: "text",
1405
+ text: `${block.text}
1406
+ ${SESSION_ATTACHMENT_UNAVAILABLE_TEXT}`
1407
+ };
1408
+ return next;
1409
+ }
1410
+ }
1411
+ return [
1412
+ ...blocks,
1413
+ { type: "text", text: SESSION_ATTACHMENT_UNAVAILABLE_TEXT }
1414
+ ];
1415
+ }
1416
+ __name(withAttachmentDegradationMarker, "withAttachmentDegradationMarker");
1417
+ var SessionAttachmentStore = class {
1418
+ constructor(directoryRoot, sessionId) {
1419
+ this.directoryRoot = directoryRoot;
1420
+ if (!directoryRoot || !sessionId) return;
1421
+ this.persistentDirectory = path.join(
1422
+ directoryRoot,
1423
+ `session-${encodeURIComponent(sessionId)}`
1424
+ );
1425
+ }
1426
+ static {
1427
+ __name(this, "SessionAttachmentStore");
1428
+ }
1429
+ directoryPromise;
1430
+ persistentDirectory;
1431
+ activeDirectory;
1432
+ pendingItems = 0;
1433
+ pendingNames = /* @__PURE__ */ new Map();
1434
+ pendingDrainWaiters = [];
1435
+ copyDrainWaiters = [];
1436
+ copying = false;
1437
+ closing = false;
1438
+ closed = false;
1439
+ async putAttachment(data, mimeType, name) {
1440
+ const isImage = isSupportedImageMimeType(mimeType);
1441
+ const safeName = safeAttachmentName(
1442
+ name ?? (isImage ? imageName(mimeType) : "")
1443
+ );
1444
+ if (!safeName) {
1445
+ throw new TypeError("Session attachment name is invalid");
1446
+ }
1447
+ const storedMimeType = mimeTypeForName(safeName);
1448
+ if (isImage && storedMimeType !== mimeType || isSupportedImageMimeType(storedMimeType) && !isImage) {
1449
+ throw new TypeError("Attachment name and Content-Type do not match");
1450
+ }
1451
+ if (this.closed || this.closing) {
1452
+ throw new Error("Session attachment store is closed");
1453
+ }
1454
+ if (this.copying) throw new Error("Session attachments are being copied");
1455
+ if (isImage && data.byteLength === 0 || data.byteLength > SESSION_ATTACHMENT_MAX_ITEM_BYTES) {
1456
+ throw new RangeError(
1457
+ `Session attachment must be at most ${SESSION_ATTACHMENT_MAX_ITEM_BYTES} bytes and images cannot be empty`
1458
+ );
1459
+ }
1460
+ let filePath;
1461
+ let pendingName = safeName;
1462
+ let removeFileOnFailure = false;
1463
+ this.pendingItems += 1;
1464
+ this.reservePendingName(safeName);
1465
+ try {
1466
+ const directory = await this.directory();
1467
+ let suffix = 0;
1468
+ for (; ; ) {
1469
+ const candidateName = deduplicatedName(safeName, suffix);
1470
+ if (safeAttachmentName(candidateName) !== candidateName) {
1471
+ throw new TypeError("Session attachment name is invalid");
1472
+ }
1473
+ if (pendingName !== candidateName) {
1474
+ if (pendingName) this.releasePendingName(pendingName);
1475
+ this.reservePendingName(candidateName);
1476
+ pendingName = candidateName;
1477
+ }
1478
+ filePath = path.join(directory, candidateName);
1479
+ removeFileOnFailure = true;
1480
+ try {
1481
+ await fs.writeFile(filePath, data, { flag: "wx" });
1482
+ break;
1483
+ } catch (error) {
1484
+ if (error.code !== "EEXIST") throw error;
1485
+ removeFileOnFailure = false;
1486
+ this.releasePendingName(candidateName);
1487
+ pendingName = void 0;
1488
+ filePath = void 0;
1489
+ suffix += 1;
1490
+ }
1491
+ }
1492
+ if (this.closed || this.closing) {
1493
+ throw new Error("Session attachment store is closed");
1494
+ }
1495
+ const name2 = path.basename(filePath);
1496
+ const storedMimeType2 = mimeTypeForName(name2);
1497
+ const reference = {
1498
+ type: isSupportedImageMimeType(storedMimeType2) ? "image" : "resource",
1499
+ attachmentId: name2,
1500
+ mimeType: storedMimeType2,
1501
+ size: data.byteLength
1502
+ };
1503
+ return reference;
1504
+ } catch (error) {
1505
+ if (removeFileOnFailure && filePath) {
1506
+ await fs.rm(filePath, { force: true }).catch(() => {
1507
+ });
1508
+ }
1509
+ throw error;
1510
+ } finally {
1511
+ if (pendingName) this.releasePendingName(pendingName);
1512
+ if (!this.closed) {
1513
+ this.pendingItems -= 1;
1514
+ if (this.pendingItems === 0) {
1515
+ this.resolvePendingDrainWaiters();
1516
+ }
1517
+ }
1518
+ }
1519
+ }
1520
+ // Validate one block against the store. Ordinary ACP content passes through
1521
+ // untouched, matching `assertReferences`.
1522
+ assertReference(block) {
1523
+ if (!block || typeof block !== "object" || Array.isArray(block) || !("attachmentId" in block)) {
1524
+ return;
1525
+ }
1526
+ if (!isSessionAttachmentReference(block)) {
1527
+ throw new SessionAttachmentReferenceError(
1528
+ "Invalid session attachment reference",
1529
+ "invalid_session_attachment_reference"
1530
+ );
1531
+ }
1532
+ this.assertStored(block);
1533
+ }
1534
+ assertReferences(content) {
1535
+ const seenIds = /* @__PURE__ */ new Set();
1536
+ for (const block of content) {
1537
+ if (!block || typeof block !== "object" || Array.isArray(block) || !("attachmentId" in block)) {
1538
+ continue;
1539
+ }
1540
+ if (!isSessionAttachmentReference(block)) {
1541
+ throw new SessionAttachmentReferenceError(
1542
+ "Invalid session attachment reference",
1543
+ "invalid_session_attachment_reference"
1544
+ );
1545
+ }
1546
+ const id = block.attachmentId;
1547
+ if (seenIds.has(id)) {
1548
+ throw new SessionAttachmentReferenceError(
1549
+ `Session attachment referenced more than once: ${id}`,
1550
+ "invalid_session_attachment_reference"
1551
+ );
1552
+ }
1553
+ seenIds.add(id);
1554
+ this.assertStored(block);
1555
+ }
1556
+ }
1557
+ async resolveContent(content, memo) {
1558
+ const pendingById = memo ?? /* @__PURE__ */ new Map();
1559
+ return await Promise.all(
1560
+ content.map(async (block) => {
1561
+ if (!isSessionAttachmentReference(block)) return block;
1562
+ const id = block.attachmentId;
1563
+ let pending = pendingById.get(id);
1564
+ if (!pending) {
1565
+ const created = this.resolve(block);
1566
+ pendingById.set(id, created);
1567
+ void created.catch(() => {
1568
+ if (pendingById.get(id) === created) {
1569
+ pendingById.delete(id);
1570
+ }
1571
+ });
1572
+ pending = created;
1573
+ }
1574
+ return await pending;
1575
+ })
1576
+ );
1577
+ }
1578
+ // Per-block variant of `resolveContent` for degrade paths: one unresolvable
1579
+ // reference drops only itself, keeping the sibling blocks a wholesale
1580
+ // fallback would discard. Other errors still propagate.
1581
+ async resolveContentDegrading(content, memo) {
1582
+ const retainedBlocks = [];
1583
+ const resolvedBlocks = [];
1584
+ let degraded = 0;
1585
+ for (const block of content) {
1586
+ if (!isSessionAttachmentReference(block)) {
1587
+ retainedBlocks.push(block);
1588
+ resolvedBlocks.push(block);
1589
+ continue;
1590
+ }
1591
+ try {
1592
+ const [resolved] = await this.resolveContent([block], memo);
1593
+ if (resolved) resolvedBlocks.push(resolved);
1594
+ retainedBlocks.push(block);
1595
+ } catch (error) {
1596
+ if (!(error instanceof SessionAttachmentReferenceError)) throw error;
1597
+ degraded += 1;
1598
+ }
1599
+ }
1600
+ return { retainedBlocks, resolvedBlocks, degraded };
1601
+ }
1602
+ async read(attachmentId) {
1603
+ const name = safeAttachmentName(attachmentId);
1604
+ if (!name || name !== attachmentId) return void 0;
1605
+ const filePath = path.join(await this.directory(), name);
1606
+ try {
1607
+ return {
1608
+ data: await fs.readFile(filePath),
1609
+ mimeType: mimeTypeForName(name)
1610
+ };
1611
+ } catch (error) {
1612
+ if (error.code === "ENOENT") {
1613
+ return void 0;
1614
+ }
1615
+ throw error;
1616
+ }
1617
+ }
1618
+ async copyFrom(source) {
1619
+ if (source === this) return;
1620
+ if (this.closed || this.closing) {
1621
+ throw new Error("Session attachment store is closed");
1622
+ }
1623
+ if (this.copying) throw new Error("Session attachments are being copied");
1624
+ if (source.closed || source.closing) {
1625
+ throw new Error("Session attachment store is closed");
1626
+ }
1627
+ if (source.copying) {
1628
+ throw new Error("Session attachments are being copied");
1629
+ }
1630
+ this.copying = true;
1631
+ source.copying = true;
1632
+ try {
1633
+ if (this.pendingItems > 0) {
1634
+ await new Promise(
1635
+ (resolve2) => this.pendingDrainWaiters.push(resolve2)
1636
+ );
1637
+ }
1638
+ if (source.pendingItems > 0) {
1639
+ await new Promise(
1640
+ (resolve2) => source.pendingDrainWaiters.push(resolve2)
1641
+ );
1642
+ }
1643
+ if (this.closed) throw new Error("Session attachment store is closed");
1644
+ const sourceDirectory = source.persistentDirectory ?? source.activeDirectory;
1645
+ if (!sourceDirectory) return;
1646
+ let entries;
1647
+ try {
1648
+ entries = await fs.readdir(sourceDirectory, { withFileTypes: true });
1649
+ } catch (error) {
1650
+ if (error.code === "ENOENT") return;
1651
+ throw error;
1652
+ }
1653
+ const targetDirectory = await this.directory();
1654
+ await Promise.all(
1655
+ entries.filter(
1656
+ (entry) => entry.isFile() && !source.pendingNames.has(entry.name)
1657
+ ).map(async (entry) => {
1658
+ const sourcePath = path.join(sourceDirectory, entry.name);
1659
+ try {
1660
+ await fs.copyFile(
1661
+ sourcePath,
1662
+ path.join(targetDirectory, entry.name)
1663
+ );
1664
+ } catch (error) {
1665
+ if (error.code === "ENOENT") {
1666
+ try {
1667
+ await fs.stat(sourcePath);
1668
+ } catch (sourceError) {
1669
+ if (sourceError.code === "ENOENT") {
1670
+ return;
1671
+ }
1672
+ }
1673
+ }
1674
+ throw error;
1675
+ }
1676
+ })
1677
+ );
1678
+ } finally {
1679
+ source.copying = false;
1680
+ this.copying = false;
1681
+ source.resolveCopyDrainWaiters();
1682
+ this.resolveCopyDrainWaiters();
1683
+ }
1684
+ }
1685
+ async remove(attachmentId) {
1686
+ const name = safeAttachmentName(attachmentId);
1687
+ if (!name || name !== attachmentId || this.copying || this.pendingNames.has(name)) {
1688
+ return false;
1689
+ }
1690
+ const directory = await this.directory();
1691
+ const filePath = path.join(directory, name);
1692
+ try {
1693
+ await fs.unlink(filePath);
1694
+ return true;
1695
+ } catch (error) {
1696
+ if (error.code === "ENOENT") {
1697
+ return false;
1698
+ }
1699
+ throw error;
1700
+ }
1701
+ }
1702
+ async close() {
1703
+ this.closing = true;
1704
+ await this.waitForCopy();
1705
+ if (this.closed) return;
1706
+ this.closed = true;
1707
+ this.pendingItems = 0;
1708
+ this.pendingNames.clear();
1709
+ this.resolvePendingDrainWaiters();
1710
+ if (this.persistentDirectory || !this.directoryPromise) return;
1711
+ const directory = await this.directoryPromise.catch(() => void 0);
1712
+ if (!directory) return;
1713
+ await fs.rm(directory, { recursive: true, force: true });
1714
+ }
1715
+ async delete() {
1716
+ this.closing = true;
1717
+ await this.waitForCopy();
1718
+ if (!this.closed) {
1719
+ this.closed = true;
1720
+ this.pendingItems = 0;
1721
+ this.pendingNames.clear();
1722
+ this.resolvePendingDrainWaiters();
1723
+ }
1724
+ const directory = this.persistentDirectory ?? await this.directoryPromise?.catch(() => void 0);
1725
+ if (directory) await fs.rm(directory, { recursive: true, force: true });
1726
+ }
1727
+ assertStored(reference) {
1728
+ const id = reference.attachmentId;
1729
+ const name = safeAttachmentName(id);
1730
+ let size;
1731
+ const directory = this.persistentDirectory ?? this.activeDirectory;
1732
+ if (name && name === id && directory) {
1733
+ try {
1734
+ size = statSync(path.join(directory, name)).size;
1735
+ } catch {
1736
+ size = void 0;
1737
+ }
1738
+ }
1739
+ const storedMimeType = name ? mimeTypeForName(name) : void 0;
1740
+ const storedType = storedMimeType ? isSupportedImageMimeType(storedMimeType) ? "image" : "resource" : void 0;
1741
+ if (size !== reference.size || storedMimeType !== reference.mimeType || storedType !== reference.type) {
1742
+ throw new SessionAttachmentReferenceError(
1743
+ `Unknown or unavailable session attachment: ${id}`,
1744
+ "session_attachment_gone"
1745
+ );
1746
+ }
1747
+ }
1748
+ releasePendingName(name) {
1749
+ const count = this.pendingNames.get(name) ?? 0;
1750
+ if (count <= 1) this.pendingNames.delete(name);
1751
+ else this.pendingNames.set(name, count - 1);
1752
+ }
1753
+ reservePendingName(name) {
1754
+ this.pendingNames.set(name, (this.pendingNames.get(name) ?? 0) + 1);
1755
+ }
1756
+ async waitForCopy() {
1757
+ while (this.copying) {
1758
+ await new Promise((resolve2) => this.copyDrainWaiters.push(resolve2));
1759
+ }
1760
+ }
1761
+ resolveCopyDrainWaiters() {
1762
+ for (const resolve2 of this.copyDrainWaiters.splice(0)) resolve2();
1763
+ }
1764
+ resolvePendingDrainWaiters() {
1765
+ for (const resolve2 of this.pendingDrainWaiters.splice(0)) resolve2();
1766
+ }
1767
+ async resolve(reference) {
1768
+ const id = reference.attachmentId;
1769
+ const attachment = await this.read(id);
1770
+ if (!attachment) {
1771
+ throw new SessionAttachmentReferenceError(
1772
+ `Unknown or unavailable session attachment: ${id}`,
1773
+ "session_attachment_gone"
1774
+ );
1775
+ }
1776
+ if (reference.type === "resource") {
1777
+ const resource = {
1778
+ uri: `attachment:///${encodeURIComponent(reference.attachmentId)}`,
1779
+ mimeType: attachment.mimeType,
1780
+ ...isTextAttachment(attachment.data, attachment.mimeType) ? { text: attachment.data.toString("utf8") } : { blob: attachment.data.toString("base64") }
1781
+ };
1782
+ return {
1783
+ type: "resource",
1784
+ resource
1785
+ };
1786
+ }
1787
+ return {
1788
+ type: "image",
1789
+ data: attachment.data.toString("base64"),
1790
+ mimeType: attachment.mimeType
1791
+ };
1792
+ }
1793
+ async directory() {
1794
+ if (!this.directoryPromise) {
1795
+ const pending = this.persistentDirectory ? fs.mkdir(this.persistentDirectory, {
1796
+ recursive: true,
1797
+ mode: 448
1798
+ }).then(() => this.persistentDirectory) : this.directoryRoot ? fs.mkdir(this.directoryRoot, { recursive: true, mode: 448 }).then(
1799
+ () => fs.mkdtemp(
1800
+ path.join(this.directoryRoot, "session-attachment-")
1801
+ )
1802
+ ) : fs.mkdtemp(path.join(tmpdir(), "qwen-session-attachment-"));
1803
+ const directoryPromise = pending.then((directory) => {
1804
+ this.activeDirectory = directory;
1805
+ return directory;
1806
+ });
1807
+ this.directoryPromise = directoryPromise;
1808
+ void directoryPromise.catch(() => {
1809
+ if (this.directoryPromise === directoryPromise)
1810
+ this.directoryPromise = void 0;
1811
+ });
1812
+ }
1813
+ return await this.directoryPromise;
1814
+ }
1815
+ };
1816
+
1283
1817
  // packages/acp-bridge/src/internal/stderrLine.ts
1284
1818
  init_esbuild_shims();
1285
1819
  function writeStderrLine(message) {
@@ -1291,8 +1825,8 @@ __name(writeStderrLine, "writeStderrLine");
1291
1825
  // packages/acp-bridge/src/bridgeClient.ts
1292
1826
  init_esbuild_shims();
1293
1827
  import { randomUUID } from "node:crypto";
1294
- import { promises as fs } from "node:fs";
1295
- import * as path from "node:path";
1828
+ import { promises as fs2 } from "node:fs";
1829
+ import * as path2 from "node:path";
1296
1830
 
1297
1831
  // packages/acp-bridge/src/daemonEventTypes.ts
1298
1832
  init_esbuild_shims();
@@ -2278,7 +2812,7 @@ function sliceLineRange(content, startLine, endLine) {
2278
2812
  }
2279
2813
  __name(sliceLineRange, "sliceLineRange");
2280
2814
  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) {
2815
+ 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, onGoalTurnEnded) {
2282
2816
  this.resolveEntry = resolveEntry;
2283
2817
  this.resolvePendingRestoreEvents = resolvePendingRestoreEvents;
2284
2818
  this.mediator = mediator;
@@ -2300,6 +2834,8 @@ var BridgeClient = class {
2300
2834
  this.getLiveSpeakToUserHandler = getLiveSpeakToUserHandler;
2301
2835
  this.externalToolGuard = externalToolGuard;
2302
2836
  this.onActiveWork = onActiveWork;
2837
+ this.onSessionCatalogChanged = onSessionCatalogChanged;
2838
+ this.onGoalTurnEnded = onGoalTurnEnded;
2303
2839
  }
2304
2840
  static {
2305
2841
  __name(this, "BridgeClient");
@@ -2564,8 +3100,8 @@ var BridgeClient = class {
2564
3100
  * between tool batches to pull any messages the browser queued mid-turn. We splice the per-session
2565
3101
  * queue, return them to the child as the response, and — when non-empty —
2566
3102
  * 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
3103
+ * those messages out of its pending queue and render the immediate echo.
3104
+ * Unknown methods reject with ACP `methodNotFound` (-32601), matching
2569
3105
  * the SDK's
2570
3106
  * default for an unimplemented client surface; the child's drain caller
2571
3107
  * treats that as "drain unsupported" and stops asking.
@@ -2599,9 +3135,12 @@ var BridgeClient = class {
2599
3135
  throw RequestError.methodNotFound(method);
2600
3136
  }
2601
3137
  const sessionId = typeof params["sessionId"] === "string" ? params["sessionId"] : void 0;
2602
- if (!sessionId) return { messages: [], hasQueuedPrompt: false };
3138
+ if (!sessionId) return { messages: [], items: [], hasQueuedPrompt: false };
3139
+ if (!this.ownsSession(sessionId)) {
3140
+ return { messages: [], items: [], hasQueuedPrompt: false };
3141
+ }
2603
3142
  const entry = this.resolveEntry(sessionId);
2604
- if (!entry) return { messages: [], hasQueuedPrompt: false };
3143
+ if (!entry) return { messages: [], items: [], hasQueuedPrompt: false };
2605
3144
  const drained = entry.midTurnMessageQueue.splice(0);
2606
3145
  if (drained.length > 0) {
2607
3146
  for (const item of drained) {
@@ -2614,6 +3153,67 @@ var BridgeClient = class {
2614
3153
  );
2615
3154
  }
2616
3155
  }
3156
+ const attachmentMemo = /* @__PURE__ */ new Map();
3157
+ const serializedAttachmentIds = /* @__PURE__ */ new Set();
3158
+ const items = [];
3159
+ try {
3160
+ for (const item of drained) {
3161
+ let degraded = 0;
3162
+ const planned = (item.content ?? []).filter((block) => {
3163
+ if (!isSessionAttachmentReference(block)) return true;
3164
+ if (serializedAttachmentIds.has(block.attachmentId)) {
3165
+ degraded += 1;
3166
+ return false;
3167
+ }
3168
+ serializedAttachmentIds.add(block.attachmentId);
3169
+ return true;
3170
+ });
3171
+ let resolvedBlocks;
3172
+ let attachmentReferences;
3173
+ try {
3174
+ resolvedBlocks = await entry.attachments.resolveContent(
3175
+ planned,
3176
+ attachmentMemo
3177
+ );
3178
+ attachmentReferences = planned.filter(isSessionAttachmentReference);
3179
+ } catch (error) {
3180
+ if (!(error instanceof SessionAttachmentReferenceError)) throw error;
3181
+ writeStderrLine(
3182
+ `[mid-turn] session=${JSON.stringify(entry.sessionId)} degraded attachment for message ${JSON.stringify(item.messageId)}: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
3183
+ );
3184
+ const perBlock = await entry.attachments.resolveContentDegrading(
3185
+ planned,
3186
+ attachmentMemo
3187
+ );
3188
+ resolvedBlocks = perBlock.resolvedBlocks;
3189
+ attachmentReferences = perBlock.retainedBlocks.filter(
3190
+ isSessionAttachmentReference
3191
+ );
3192
+ degraded += perBlock.degraded;
3193
+ }
3194
+ let content = [
3195
+ ...item.text ? [{ type: "text", text: item.text }] : [],
3196
+ ...resolvedBlocks
3197
+ ];
3198
+ if (degraded > 0) content = withAttachmentDegradationMarker(content);
3199
+ items.push({
3200
+ messageId: item.messageId,
3201
+ displayText: item.text,
3202
+ content,
3203
+ ...attachmentReferences.length > 0 ? { attachmentReferences } : {}
3204
+ });
3205
+ }
3206
+ } catch (error) {
3207
+ const requeued = new Set(drained.map((queued) => queued.messageId));
3208
+ const ring = entry.settledMidTurnMessageIds;
3209
+ const kept = ring.filter((id) => !requeued.has(id));
3210
+ ring.splice(0, ring.length, ...kept);
3211
+ entry.midTurnMessageQueue.unshift(...drained);
3212
+ writeStderrLine(
3213
+ `[mid-turn] session=${JSON.stringify(entry.sessionId)} drain failed, requeued ${drained.length} message(s): ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
3214
+ );
3215
+ throw error;
3216
+ }
2617
3217
  const echoed = drained.filter((item) => !item.queueOnly);
2618
3218
  const messages = drained.map((item) => item.text);
2619
3219
  const hasQueuedPrompt = entry.pendingPromptList.some(
@@ -2626,14 +3226,21 @@ var BridgeClient = class {
2626
3226
  data: {
2627
3227
  sessionId: entry.sessionId,
2628
3228
  messages: echoed.map((item) => item.text),
2629
- messageIds: echoed.map((item) => item.messageId)
3229
+ messageIds: echoed.map((item) => item.messageId),
3230
+ // Carry the structured `items` twin (content blocks per message) so
3231
+ // the browser-side echo renderer can show attached images alongside
3232
+ // the message text. Older consumers that don't read this field keep
3233
+ // working unchanged.
3234
+ items: echoed.map((item) => ({
3235
+ ...item.content && item.content.length > 0 ? { content: item.content } : {}
3236
+ }))
2630
3237
  }
2631
3238
  });
2632
3239
  writeStderrLine(
2633
3240
  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
3241
  );
2635
3242
  }
2636
- return { messages, hasQueuedPrompt };
3243
+ return { messages, items, hasQueuedPrompt };
2637
3244
  }
2638
3245
  async handleExternalToolGuardPrepare(params) {
2639
3246
  if (!this.externalToolGuard) {
@@ -3022,7 +3629,7 @@ var BridgeClient = class {
3022
3629
  * `qwen/notify/session/prompt-suggestion` (followup assist),
3023
3630
  * `qwen/notify/session/artifact-event` (hook artifacts),
3024
3631
  * `qwen/notify/session/terminal-sequence`, and
3025
- * `_qwencode/end_turn` (background-notification turns), and
3632
+ * `_qwencode/end_turn` (background-notification and goal turns), and
3026
3633
  * `qwen/notify/session/mcp-budget-event` — each translated into a
3027
3634
  * session-scoped SSE frame. Unknown methods are dropped silently for
3028
3635
  * forward-compat.
@@ -3045,14 +3652,39 @@ var BridgeClient = class {
3045
3652
  }
3046
3653
  return;
3047
3654
  }
3655
+ if (method === "_qwencode/start_turn") {
3656
+ const sessionId2 = params["sessionId"];
3657
+ if (typeof sessionId2 !== "string" || sessionId2.length === 0 || params["source"] !== "goal") {
3658
+ return;
3659
+ }
3660
+ const entry = this.resolveEntry(sessionId2);
3661
+ if (!entry || !this.ownsSession(sessionId2)) return;
3662
+ entry.goalTurnActive = true;
3663
+ return;
3664
+ }
3048
3665
  if (method === "_qwencode/end_turn") {
3049
3666
  const sessionId2 = params["sessionId"];
3050
3667
  const reason = params["reason"];
3051
- if (typeof sessionId2 !== "string" || sessionId2.length === 0 || typeof reason !== "string" || reason.length === 0 || reason.length > 128 || params["source"] !== "background_notification") {
3668
+ const source = params["source"];
3669
+ if (typeof sessionId2 !== "string" || sessionId2.length === 0 || typeof reason !== "string" || reason.length === 0 || reason.length > 128 || source !== "background_notification" && source !== "goal") {
3052
3670
  return;
3053
3671
  }
3054
3672
  const entry = this.resolveEntry(sessionId2);
3055
3673
  if (!entry || !this.ownsSession(sessionId2)) return;
3674
+ if (source === "goal") {
3675
+ entry.goalTurnActive = false;
3676
+ this.onGoalTurnEnded?.(sessionId2);
3677
+ const promptId = params["promptId"];
3678
+ if (typeof promptId !== "string" || promptId.length === 0 || promptId.length > 256) {
3679
+ return;
3680
+ }
3681
+ entry.events.publish({
3682
+ type: "turn_complete",
3683
+ promptId,
3684
+ data: { sessionId: sessionId2, stopReason: reason, promptId }
3685
+ });
3686
+ return;
3687
+ }
3056
3688
  entry.events.publish({
3057
3689
  type: "background_notification_turn_complete",
3058
3690
  data: { sessionId: sessionId2, reason }
@@ -3160,6 +3792,7 @@ var BridgeClient = class {
3160
3792
  return;
3161
3793
  const entry = this.resolveEntry(sessionId2);
3162
3794
  if (!entry) return;
3795
+ this.onSessionCatalogChanged?.();
3163
3796
  try {
3164
3797
  entry.events.publish({
3165
3798
  type: "session_metadata_updated",
@@ -3291,6 +3924,13 @@ var BridgeClient = class {
3291
3924
  async upsertAndPublishArtifacts(entry, artifacts, options, turn = {}) {
3292
3925
  try {
3293
3926
  const result = await entry.artifacts.upsertMany(artifacts, options);
3927
+ for (const warning of result.warnings ?? []) {
3928
+ writeStderrLine(
3929
+ `[artifacts] session=${entry.sessionId} action=warning reason=${JSON.stringify(
3930
+ warning
3931
+ )}`
3932
+ );
3933
+ }
3294
3934
  this.publishArtifactChanges(entry, result.changes, turn);
3295
3935
  } catch (error) {
3296
3936
  writeStderrLine(
@@ -3602,20 +4242,20 @@ var BridgeClient = class {
3602
4242
  }
3603
4243
  let realTarget = params.path;
3604
4244
  try {
3605
- realTarget = await fs.realpath(params.path);
4245
+ realTarget = await fs2.realpath(params.path);
3606
4246
  } catch (err) {
3607
4247
  const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
3608
4248
  if (code !== "ENOENT") throw err;
3609
4249
  try {
3610
- const linkTarget = await fs.readlink(params.path);
3611
- realTarget = path.resolve(path.dirname(params.path), linkTarget);
4250
+ const linkTarget = await fs2.readlink(params.path);
4251
+ realTarget = path2.resolve(path2.dirname(params.path), linkTarget);
3612
4252
  } catch {
3613
4253
  }
3614
4254
  }
3615
4255
  const tmp = `${realTarget}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
3616
4256
  let preserveMode;
3617
4257
  try {
3618
- const targetStat = await fs.stat(realTarget);
4258
+ const targetStat = await fs2.stat(realTarget);
3619
4259
  preserveMode = {
3620
4260
  mode: targetStat.mode & 4095,
3621
4261
  uid: targetStat.uid,
@@ -3626,20 +4266,20 @@ var BridgeClient = class {
3626
4266
  if (code !== "ENOENT") throw err;
3627
4267
  }
3628
4268
  try {
3629
- await fs.writeFile(tmp, params.content, {
4269
+ await fs2.writeFile(tmp, params.content, {
3630
4270
  encoding: "utf8",
3631
4271
  flag: "wx",
3632
4272
  mode: preserveMode?.mode ?? 384
3633
4273
  });
3634
4274
  if (preserveMode) {
3635
- await fs.chmod(tmp, preserveMode.mode).catch(() => {
4275
+ await fs2.chmod(tmp, preserveMode.mode).catch(() => {
3636
4276
  });
3637
- await fs.chown(tmp, preserveMode.uid, preserveMode.gid).catch(() => {
4277
+ await fs2.chown(tmp, preserveMode.uid, preserveMode.gid).catch(() => {
3638
4278
  });
3639
4279
  }
3640
- await fs.rename(tmp, realTarget);
4280
+ await fs2.rename(tmp, realTarget);
3641
4281
  } catch (err) {
3642
- await fs.unlink(tmp).catch(() => {
4282
+ await fs2.unlink(tmp).catch(() => {
3643
4283
  });
3644
4284
  throw err;
3645
4285
  }
@@ -3669,7 +4309,7 @@ var BridgeClient = class {
3669
4309
  );
3670
4310
  }
3671
4311
  const READ_FILE_SIZE_CAP = 100 * 1024 * 1024;
3672
- const stats = await fs.stat(params.path);
4312
+ const stats = await fs2.stat(params.path);
3673
4313
  if (!stats.isFile()) {
3674
4314
  throw new Error(
3675
4315
  `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 +4320,7 @@ var BridgeClient = class {
3680
4320
  `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
4321
  );
3682
4322
  }
3683
- const content = await fs.readFile(params.path, "utf8");
4323
+ const content = await fs2.readFile(params.path, "utf8");
3684
4324
  if (typeof params.line === "number" || typeof params.limit === "number") {
3685
4325
  const startLine = params.line ?? 1;
3686
4326
  const start = startLine > 0 ? startLine - 1 : 0;
@@ -3882,8 +4522,8 @@ var GenerationStreamQueue = class {
3882
4522
  // packages/acp-bridge/src/sessionArtifacts.ts
3883
4523
  init_esbuild_shims();
3884
4524
  import { createHash } from "node:crypto";
3885
- import { constants as fsConstants, promises as fs2 } from "node:fs";
3886
- import path2 from "node:path";
4525
+ import { constants as fsConstants, promises as fs3 } from "node:fs";
4526
+ import path3 from "node:path";
3887
4527
  var SOURCE_RESERVATIONS = {
3888
4528
  tool: 100,
3889
4529
  client: 50,
@@ -3993,13 +4633,31 @@ var SessionArtifactStore = class {
3993
4633
  const warningDetails = [];
3994
4634
  for (const input of inputs) {
3995
4635
  try {
3996
- normalizedResults.push(
3997
- await this.normalizeInput(
3998
- input,
3999
- ++this.receivedSeq,
4000
- options.trustedPublisher === true
4001
- )
4002
- );
4636
+ const expanded = await this.expandWorkspaceDirectoryInput(input);
4637
+ for (const item of expanded.inputs) {
4638
+ try {
4639
+ normalizedResults.push(
4640
+ await this.normalizeInput(
4641
+ item,
4642
+ ++this.receivedSeq,
4643
+ options.trustedPublisher === true
4644
+ )
4645
+ );
4646
+ } catch (error) {
4647
+ if (validationStrict) {
4648
+ throw error;
4649
+ }
4650
+ const message = error instanceof Error ? error.message : String(error);
4651
+ writeStderrLine(
4652
+ `[artifacts] session=${this.sessionId} action=dropped reason=${JSON.stringify(
4653
+ message
4654
+ )}`
4655
+ );
4656
+ }
4657
+ }
4658
+ if (expanded.warning) {
4659
+ warnings.push(expanded.warning);
4660
+ }
4003
4661
  } catch (error) {
4004
4662
  if (validationStrict) {
4005
4663
  throw error;
@@ -4083,9 +4741,17 @@ var SessionArtifactStore = class {
4083
4741
  const createdIds = new Set(
4084
4742
  changes.filter((change) => change.action === "created").map((change) => change.artifactId)
4085
4743
  );
4086
- changes.push(
4087
- ...await this.evictOverflow(createdIds, changes, persistenceStrict)
4744
+ const overflowRemoved = await this.evictOverflow(
4745
+ createdIds,
4746
+ changes,
4747
+ persistenceStrict
4088
4748
  );
4749
+ changes.push(...overflowRemoved.removed);
4750
+ if (overflowRemoved.droppedCreated > 0) {
4751
+ warnings.push(
4752
+ `dropped ${overflowRemoved.droppedCreated} newly created artifacts because the store is full`
4753
+ );
4754
+ }
4089
4755
  const hasStrictDurableTransition = changes.some(
4090
4756
  shouldCommitBeforeDurablePersistence
4091
4757
  );
@@ -4371,9 +5037,9 @@ var SessionArtifactStore = class {
4371
5037
  });
4372
5038
  }
4373
5039
  const evicted = await this.evictOverflow(/* @__PURE__ */ new Set(), []);
4374
- if (evicted.length > 0) {
5040
+ if (evicted.removed.length > 0) {
4375
5041
  warnings.push("restored artifact list pruned to live limit");
4376
- warnings.push(...await this.persistChanges(evicted, false));
5042
+ warnings.push(...await this.persistChanges(evicted.removed, false));
4377
5043
  }
4378
5044
  this.setLastRestoreWarnings(warnings);
4379
5045
  return warnings;
@@ -4758,6 +5424,171 @@ var SessionArtifactStore = class {
4758
5424
  options?.clientId
4759
5425
  );
4760
5426
  }
5427
+ async expandWorkspaceDirectoryInput(input) {
5428
+ const workspacePath = typeof input.workspacePath === "string" ? input.workspacePath.trim() : void 0;
5429
+ if (!workspacePath) {
5430
+ return { inputs: [input] };
5431
+ }
5432
+ const realWorkspace = await this.getRealWorkspaceCwdForValidation();
5433
+ const normalizedPath = normalizeWorkspacePath(workspacePath, realWorkspace);
5434
+ const absolutePath = path3.resolve(realWorkspace, normalizedPath);
5435
+ let stat;
5436
+ try {
5437
+ stat = await fs3.lstat(absolutePath);
5438
+ } catch (error) {
5439
+ if (isNotFoundError(error)) {
5440
+ return { inputs: [input] };
5441
+ }
5442
+ const reason = error instanceof Error ? error.message : String(error);
5443
+ throw new SessionArtifactValidationError(
5444
+ `workspacePath could not be inspected: ${reason}`,
5445
+ "workspacePath"
5446
+ );
5447
+ }
5448
+ let walkDir = absolutePath;
5449
+ let walkRelative = normalizedPath;
5450
+ if (stat.isSymbolicLink()) {
5451
+ let realPath;
5452
+ try {
5453
+ realPath = await fs3.realpath(absolutePath);
5454
+ } catch {
5455
+ return { inputs: [input] };
5456
+ }
5457
+ let realStat;
5458
+ try {
5459
+ realStat = await fs3.lstat(realPath);
5460
+ } catch (error) {
5461
+ if (isNotFoundError(error)) {
5462
+ return { inputs: [input] };
5463
+ }
5464
+ const reason = error instanceof Error ? error.message : String(error);
5465
+ throw new SessionArtifactValidationError(
5466
+ `workspacePath could not be inspected: ${reason}`,
5467
+ "workspacePath"
5468
+ );
5469
+ }
5470
+ if (!realStat.isDirectory()) {
5471
+ return { inputs: [input] };
5472
+ }
5473
+ walkDir = realPath;
5474
+ walkRelative = normalizedPath;
5475
+ } else if (!stat.isDirectory()) {
5476
+ return { inputs: [input] };
5477
+ } else {
5478
+ try {
5479
+ walkDir = await fs3.realpath(absolutePath);
5480
+ } catch (error) {
5481
+ if (isNotFoundError(error)) {
5482
+ return { inputs: [input] };
5483
+ }
5484
+ const reason = error instanceof Error ? error.message : String(error);
5485
+ throw new SessionArtifactValidationError(
5486
+ `workspacePath could not be inspected: ${reason}`,
5487
+ "workspacePath"
5488
+ );
5489
+ }
5490
+ }
5491
+ const resolvedRelative = path3.relative(realWorkspace, walkDir);
5492
+ if (!resolvedRelative || isOutsidePath(resolvedRelative)) {
5493
+ throw new SessionArtifactValidationError(
5494
+ "workspacePath must stay inside the workspace",
5495
+ "workspacePath"
5496
+ );
5497
+ }
5498
+ if (pathHasSkippedDirectoryComponent(
5499
+ resolvedRelative.split(path3.sep).join("/")
5500
+ ) || walkRelative && pathHasSkippedDirectoryComponent(walkRelative)) {
5501
+ throw new SessionArtifactValidationError(
5502
+ "workspacePath is a skipped directory and cannot be recorded",
5503
+ "workspacePath"
5504
+ );
5505
+ }
5506
+ if (input.metadata !== void 0 && !isPlainMetadataObject(input.metadata)) {
5507
+ throw new SessionArtifactValidationError(
5508
+ "metadata must be an object",
5509
+ "metadata"
5510
+ );
5511
+ }
5512
+ const childMetadata = {
5513
+ ...isPlainMetadataObject(input.metadata) ? input.metadata : {},
5514
+ expandedFromDirectory: true
5515
+ };
5516
+ if (Buffer.byteLength(JSON.stringify(childMetadata), "utf8") > 4096) {
5517
+ throw new SessionArtifactValidationError(
5518
+ "metadata is too large to expand a directory",
5519
+ "metadata"
5520
+ );
5521
+ }
5522
+ const parentTitle = normalizeString(input.title, "title", 200, true);
5523
+ const parentDescription = normalizeString(
5524
+ input.description,
5525
+ "description",
5526
+ 1e3,
5527
+ false
5528
+ );
5529
+ let collected;
5530
+ try {
5531
+ collected = await collectRecordableWorkspaceFiles(
5532
+ walkDir,
5533
+ walkRelative,
5534
+ realWorkspace,
5535
+ (filePath) => isRecordableDerivedChild(path3.posix.basename(filePath), filePath)
5536
+ );
5537
+ } catch (error) {
5538
+ if (isNotFoundError(error)) {
5539
+ return { inputs: [input] };
5540
+ }
5541
+ const reason = error instanceof Error ? error.message : String(error);
5542
+ throw new SessionArtifactValidationError(
5543
+ `workspacePath could not be inspected: ${reason}`,
5544
+ "workspacePath"
5545
+ );
5546
+ }
5547
+ if (collected.files.length === 0) {
5548
+ throw new SessionArtifactValidationError(
5549
+ collected.depthLimited ? `workspacePath is a directory whose recordable files are deeper than ${MAX_DIRECTORY_ARTIFACT_DEPTH} levels` : "workspacePath is a directory with no recordable files",
5550
+ "workspacePath"
5551
+ );
5552
+ }
5553
+ const warnings = [];
5554
+ if (collected.truncated) {
5555
+ warnings.push(
5556
+ `workspacePath "${normalizedPath}" contained more than ${MAX_DIRECTORY_ARTIFACT_FILES} files; recorded the first ${collected.files.length}`
5557
+ );
5558
+ }
5559
+ if (collected.depthLimited) {
5560
+ warnings.push(
5561
+ `workspacePath "${normalizedPath}" exceeded ${MAX_DIRECTORY_ARTIFACT_DEPTH} directory levels; some files were not recorded`
5562
+ );
5563
+ }
5564
+ if (collected.unreadable) {
5565
+ warnings.push(
5566
+ `workspacePath "${normalizedPath}" contained subdirectories that could not be read`
5567
+ );
5568
+ }
5569
+ if (collected.skippedUnrecordable > 0) {
5570
+ warnings.push(
5571
+ `workspacePath "${normalizedPath}" skipped ${collected.skippedUnrecordable} files whose names cannot be recorded as artifact titles`
5572
+ );
5573
+ }
5574
+ return {
5575
+ inputs: collected.files.map((filePath) => {
5576
+ const title = path3.posix.basename(filePath).trim();
5577
+ const description = parentDescription || (parentTitle && parentTitle !== title ? parentTitle : void 0);
5578
+ return {
5579
+ ...input,
5580
+ title,
5581
+ workspacePath: filePath,
5582
+ kind: void 0,
5583
+ mimeType: void 0,
5584
+ sizeBytes: void 0,
5585
+ metadata: childMetadata,
5586
+ ...description ? { description } : { description: void 0 }
5587
+ };
5588
+ }),
5589
+ ...warnings.length > 0 ? { warning: warnings.join("; ") } : {}
5590
+ };
5591
+ }
4761
5592
  async normalizeInput(input, receivedSeq, trustedPublisherFromCaller, options = {}) {
4762
5593
  if (!input || typeof input !== "object") {
4763
5594
  throw new SessionArtifactValidationError("Artifact must be an object");
@@ -4954,7 +5785,7 @@ var SessionArtifactStore = class {
4954
5785
  }
4955
5786
  getRealWorkspaceCwd() {
4956
5787
  if (!this.realWorkspaceCwdPromise) {
4957
- const promise = fs2.realpath(this.workspaceCwd).catch((error) => {
5788
+ const promise = fs3.realpath(this.workspaceCwd).catch((error) => {
4958
5789
  if (this.realWorkspaceCwdPromise === promise) {
4959
5790
  this.realWorkspaceCwdPromise = void 0;
4960
5791
  }
@@ -4978,7 +5809,7 @@ var SessionArtifactStore = class {
4978
5809
  async evictOverflow(createdIds, changes, strict = false) {
4979
5810
  const removed = [];
4980
5811
  if (this.artifacts.size <= this.maxArtifacts) {
4981
- return removed;
5812
+ return { removed, droppedCreated: 0 };
4982
5813
  }
4983
5814
  const createdInThisBatch = new Set(createdIds);
4984
5815
  const candidates = Array.from(this.artifacts.values()).filter(
@@ -5014,17 +5845,19 @@ var SessionArtifactStore = class {
5014
5845
  "artifactId"
5015
5846
  );
5016
5847
  }
5848
+ let droppedCreated = 0;
5017
5849
  for (const artifact of overflowCreated) {
5018
5850
  if (this.artifacts.size <= this.maxArtifacts) {
5019
5851
  break;
5020
5852
  }
5021
5853
  this.artifacts.delete(artifact.id);
5854
+ droppedCreated++;
5022
5855
  writeStderrLine(
5023
5856
  `[artifacts] session=${this.sessionId} action=dropped reason="max artifacts exceeded" artifactId=${artifact.id}`
5024
5857
  );
5025
5858
  removePriorChange(changes, artifact.id);
5026
5859
  }
5027
- return removed;
5860
+ return { removed, droppedCreated };
5028
5861
  }
5029
5862
  };
5030
5863
  function coalesceByIdentity(artifacts) {
@@ -5074,11 +5907,19 @@ function mergeBatchArtifact(existing, next) {
5074
5907
  delete merged.workspacePath;
5075
5908
  return merged;
5076
5909
  }
5910
+ const refreshDisplay = existing.storage === "workspace" && next.storage === "workspace" && shouldRefreshWorkspaceDisplay(next, existing);
5911
+ const metadata = mergeMetadata(existing, next);
5077
5912
  return {
5078
5913
  ...existing,
5914
+ title: refreshDisplay ? next.title : existing.title,
5915
+ description: refreshDisplay ? next.description ?? existing.description : existing.description,
5916
+ toolName: refreshDisplay ? next.toolName : existing.toolName,
5917
+ source: refreshDisplay ? next.source : existing.source,
5918
+ hookEventName: refreshDisplay ? next.hookEventName : existing.hookEventName,
5919
+ toolCallId: refreshDisplay ? next.toolCallId : existing.toolCallId,
5079
5920
  status: next.status,
5080
5921
  sizeBytes: mergeSizeBytes(existing, next),
5081
- metadata: mergeMetadata(existing, next),
5922
+ metadata: refreshDisplay ? stripExpandedFromDirectoryMarker(metadata) : metadata,
5082
5923
  clientRetained: existing.clientRetained || next.clientRetained,
5083
5924
  trustedPublisher: existing.trustedPublisher || next.trustedPublisher,
5084
5925
  retentionExplicit: existing.retentionExplicit || next.retentionExplicit,
@@ -5122,6 +5963,14 @@ function mergeArtifact(existing, incoming) {
5122
5963
  next.description = incoming.description;
5123
5964
  delete next.workspacePath;
5124
5965
  delete next.hideWorkspacePath;
5966
+ } else if (existing.storage === "workspace" && incoming.storage === "workspace" && shouldRefreshWorkspaceDisplay(incoming, existing)) {
5967
+ next.title = incoming.title;
5968
+ next.description = incoming.description ?? existing.description;
5969
+ next.toolCallId = incoming.toolCallId;
5970
+ next.toolName = incoming.toolName;
5971
+ next.source = incoming.source;
5972
+ next.hookEventName = incoming.hookEventName;
5973
+ next.metadata = stripExpandedFromDirectoryMarker(next.metadata);
5125
5974
  }
5126
5975
  const changed = !publicArtifactsEqual(
5127
5976
  toPublicArtifact(existing),
@@ -5130,13 +5979,34 @@ function mergeArtifact(existing, incoming) {
5130
5979
  if (changed) {
5131
5980
  next.updatedAt = now;
5132
5981
  }
5133
- return { artifact: changed ? next : existing, changed };
5134
- }
5135
- __name(mergeArtifact, "mergeArtifact");
5136
- function shouldRecordEphemeralUnpin(existing, incoming) {
5137
- return incoming.retentionExplicit && incoming.retention === "ephemeral" && (existing.retention !== "ephemeral" || existing.persistedAt !== void 0 || existing.durableTombstoneRequired === true);
5982
+ return { artifact: changed ? next : existing, changed };
5983
+ }
5984
+ __name(mergeArtifact, "mergeArtifact");
5985
+ function shouldRecordEphemeralUnpin(existing, incoming) {
5986
+ return incoming.retentionExplicit && incoming.retention === "ephemeral" && (existing.retention !== "ephemeral" || existing.persistedAt !== void 0 || existing.durableTombstoneRequired === true);
5987
+ }
5988
+ __name(shouldRecordEphemeralUnpin, "shouldRecordEphemeralUnpin");
5989
+ function stripExpandedFromDirectoryMarker(metadata) {
5990
+ if (metadata?.["expandedFromDirectory"] !== true) {
5991
+ return metadata;
5992
+ }
5993
+ const { expandedFromDirectory: _dropped, ...rest } = metadata;
5994
+ return Object.keys(rest).length > 0 ? rest : void 0;
5995
+ }
5996
+ __name(stripExpandedFromDirectoryMarker, "stripExpandedFromDirectoryMarker");
5997
+ function shouldRefreshWorkspaceDisplay(incoming, existing) {
5998
+ if (incoming.metadata?.["expandedFromDirectory"] === true) {
5999
+ return false;
6000
+ }
6001
+ if (incoming.toolName === "record_artifact" && incoming.source !== "hook") {
6002
+ return true;
6003
+ }
6004
+ if (!incoming.toolName) {
6005
+ return true;
6006
+ }
6007
+ return incoming.toolName === existing.toolName && incoming.source === existing.source && incoming.hookEventName === existing.hookEventName;
5138
6008
  }
5139
- __name(shouldRecordEphemeralUnpin, "shouldRecordEphemeralUnpin");
6009
+ __name(shouldRefreshWorkspaceDisplay, "shouldRefreshWorkspaceDisplay");
5140
6010
  function publicArtifactsEqual(a, b) {
5141
6011
  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
6012
  }
@@ -5518,7 +6388,7 @@ function inferStorage(requested, locators) {
5518
6388
  }
5519
6389
  __name(inferStorage, "inferStorage");
5520
6390
  function normalizeKind(kind) {
5521
- if (kind === "file" || kind === "link" || kind === "html" || kind === "image" || kind === "video" || kind === "audio" || kind === "pdf" || kind === "notebook" || kind === "other") {
6391
+ if (kind === "file" || kind === "link" || kind === "html" || kind === "image" || kind === "video" || kind === "audio" || kind === "pdf" || kind === "notebook" || kind === "document" || kind === "other") {
5522
6392
  return kind;
5523
6393
  }
5524
6394
  throw new SessionArtifactValidationError(
@@ -5589,7 +6459,7 @@ function buildIdentityKey(input) {
5589
6459
  }
5590
6460
  __name(buildIdentityKey, "buildIdentityKey");
5591
6461
  function managedIdForWorkspacePath(workspaceCwd, workspacePath) {
5592
- return createHash("sha1").update(path2.resolve(workspaceCwd, workspacePath)).digest("hex").slice(0, 16);
6462
+ return createHash("sha1").update(path3.resolve(workspaceCwd, workspacePath)).digest("hex").slice(0, 16);
5593
6463
  }
5594
6464
  __name(managedIdForWorkspacePath, "managedIdForWorkspacePath");
5595
6465
  function normalizeString(value, field, maxLength, required) {
@@ -5636,7 +6506,7 @@ __name(normalizeString, "normalizeString");
5636
6506
  function normalizeManagedId(value) {
5637
6507
  const managedId = normalizeString(value, "managedId", 200, false);
5638
6508
  if (!managedId) return void 0;
5639
- if (managedId.includes("/") || managedId.includes("\\") || managedId.includes("..") || path2.isAbsolute(managedId) || path2.win32.isAbsolute(managedId)) {
6509
+ if (managedId.includes("/") || managedId.includes("\\") || managedId.includes("..") || path3.isAbsolute(managedId) || path3.win32.isAbsolute(managedId)) {
5640
6510
  throw new SessionArtifactValidationError(
5641
6511
  "managedId must be an opaque managed resource id",
5642
6512
  "managedId"
@@ -5670,21 +6540,21 @@ function hasUnsafeDisplayPayload(value) {
5670
6540
  __name(hasUnsafeDisplayPayload, "hasUnsafeDisplayPayload");
5671
6541
  function normalizeWorkspacePath(raw, workspaceCwd) {
5672
6542
  const trimmed = normalizeString(raw, "workspacePath", 500, true);
5673
- if (path2.isAbsolute(trimmed)) {
6543
+ if (path3.isAbsolute(trimmed)) {
5674
6544
  throw new SessionArtifactValidationError(
5675
6545
  "workspacePath must be relative to the workspace",
5676
6546
  "workspacePath"
5677
6547
  );
5678
6548
  }
5679
- const absolute = path2.resolve(workspaceCwd, trimmed);
5680
- const relative = path2.relative(workspaceCwd, absolute);
6549
+ const absolute = path3.resolve(workspaceCwd, trimmed);
6550
+ const relative = path3.relative(workspaceCwd, absolute);
5681
6551
  if (!relative || isOutsidePath(relative)) {
5682
6552
  throw new SessionArtifactValidationError(
5683
6553
  "workspacePath must stay inside the workspace",
5684
6554
  "workspacePath"
5685
6555
  );
5686
6556
  }
5687
- return relative.split(path2.sep).join("/");
6557
+ return relative.split(path3.sep).join("/");
5688
6558
  }
5689
6559
  __name(normalizeWorkspacePath, "normalizeWorkspacePath");
5690
6560
  function normalizeArtifactUrl(raw, allowFile) {
@@ -5885,7 +6755,7 @@ __name(normalizeSizeBytes, "normalizeSizeBytes");
5885
6755
  function inferKind(input) {
5886
6756
  if (input.storage === "published") return "html";
5887
6757
  if (input.url) return "link";
5888
- const ext = input.workspacePath ? path2.extname(input.workspacePath).toLowerCase() : "";
6758
+ const ext = input.workspacePath ? path3.extname(input.workspacePath).toLowerCase() : "";
5889
6759
  if (ext === ".html" || ext === ".htm") return "html";
5890
6760
  if ([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"].includes(ext)) {
5891
6761
  return "image";
@@ -5894,20 +6764,21 @@ function inferKind(input) {
5894
6764
  if ([".mp3", ".wav", ".m4a", ".ogg"].includes(ext)) return "audio";
5895
6765
  if (ext === ".pdf") return "pdf";
5896
6766
  if (ext === ".ipynb") return "notebook";
6767
+ if (isOfficeDocumentExtension(ext)) return "document";
5897
6768
  return input.workspacePath ? "file" : "other";
5898
6769
  }
5899
6770
  __name(inferKind, "inferKind");
5900
6771
  async function getWorkspaceStatus(workspacePath, realWorkspaceCwd, expected, options = { hashContent: true }) {
5901
6772
  const realWorkspace = await realWorkspaceCwd;
5902
- const absolutePath = path2.resolve(realWorkspace, workspacePath);
6773
+ const absolutePath = path3.resolve(realWorkspace, workspacePath);
5903
6774
  try {
5904
- const realPath = await fs2.realpath(absolutePath);
5905
- const relative = path2.relative(realWorkspace, realPath);
6775
+ const realPath = await fs3.realpath(absolutePath);
6776
+ const relative = path3.relative(realWorkspace, realPath);
5906
6777
  if (!relative || isOutsidePath(relative)) {
5907
6778
  return { status: "missing", escaped: true };
5908
6779
  }
5909
- const preOpenStat = await fs2.lstat(realPath);
5910
- const handle = await fs2.open(
6780
+ const preOpenStat = await fs3.lstat(realPath);
6781
+ const handle = await fs3.open(
5911
6782
  realPath,
5912
6783
  fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW
5913
6784
  );
@@ -5916,56 +6787,54 @@ async function getWorkspaceStatus(workspacePath, realWorkspaceCwd, expected, opt
5916
6787
  if (!isSameFile(preOpenStat, stat)) {
5917
6788
  return { status: "missing", escaped: true };
5918
6789
  }
5919
- if (stat.isFile()) {
5920
- const expectedMtimeMs = typeof expected?.mtimeMs === "number" ? expected.mtimeMs : void 0;
5921
- const expectedSha256 = typeof expected?.sha256 === "string" ? expected.sha256 : void 0;
5922
- const unchanged = expected?.sizeBytes === stat.size && expectedMtimeMs === stat.mtimeMs;
5923
- const sizeChanged = expected?.sizeBytes !== void 0 && expected.sizeBytes !== stat.size;
5924
- if (sizeChanged) {
5925
- return {
5926
- status: "changed",
5927
- sizeBytes: stat.size,
5928
- mtimeMs: stat.mtimeMs
5929
- };
5930
- }
5931
- if (unchanged) {
5932
- return {
5933
- status: "available",
5934
- sizeBytes: stat.size,
5935
- mtimeMs: stat.mtimeMs
5936
- };
5937
- }
5938
- if (stat.size > MAX_WORKSPACE_HASH_BYTES) {
5939
- return {
5940
- status: expectedSha256 ? "changed" : "available",
5941
- sizeBytes: stat.size,
5942
- mtimeMs: stat.mtimeMs
5943
- };
5944
- }
5945
- if (!options.hashContent) {
5946
- return {
5947
- status: expectedSha256 ? "changed" : "available",
5948
- sizeBytes: stat.size,
5949
- mtimeMs: stat.mtimeMs
5950
- };
5951
- }
5952
- const sha256 = await hashFile(handle);
5953
- if (expectedSha256 && sha256 !== expectedSha256) {
5954
- return {
5955
- status: "changed",
5956
- sizeBytes: stat.size,
5957
- mtimeMs: stat.mtimeMs
5958
- };
5959
- }
6790
+ if (!stat.isFile()) {
6791
+ throw new Error("path is not a regular file");
6792
+ }
6793
+ const expectedMtimeMs = typeof expected?.mtimeMs === "number" ? expected.mtimeMs : void 0;
6794
+ const expectedSha256 = typeof expected?.sha256 === "string" ? expected.sha256 : void 0;
6795
+ const unchanged = expected?.sizeBytes === stat.size && expectedMtimeMs === stat.mtimeMs;
6796
+ const sizeChanged = expected?.sizeBytes !== void 0 && expected.sizeBytes !== stat.size;
6797
+ if (sizeChanged) {
6798
+ return {
6799
+ status: "changed",
6800
+ sizeBytes: stat.size,
6801
+ mtimeMs: stat.mtimeMs
6802
+ };
6803
+ }
6804
+ if (unchanged) {
5960
6805
  return {
5961
6806
  status: "available",
5962
6807
  sizeBytes: stat.size,
5963
- mtimeMs: stat.mtimeMs,
5964
- sha256
6808
+ mtimeMs: stat.mtimeMs
6809
+ };
6810
+ }
6811
+ if (stat.size > MAX_WORKSPACE_HASH_BYTES) {
6812
+ return {
6813
+ status: expectedSha256 ? "changed" : "available",
6814
+ sizeBytes: stat.size,
6815
+ mtimeMs: stat.mtimeMs
6816
+ };
6817
+ }
6818
+ if (!options.hashContent) {
6819
+ return {
6820
+ status: expectedSha256 ? "changed" : "available",
6821
+ sizeBytes: stat.size,
6822
+ mtimeMs: stat.mtimeMs
6823
+ };
6824
+ }
6825
+ const sha256 = await hashFile(handle);
6826
+ if (expectedSha256 && sha256 !== expectedSha256) {
6827
+ return {
6828
+ status: "changed",
6829
+ sizeBytes: stat.size,
6830
+ mtimeMs: stat.mtimeMs
5965
6831
  };
5966
6832
  }
5967
6833
  return {
5968
- status: "available"
6834
+ status: "available",
6835
+ sizeBytes: stat.size,
6836
+ mtimeMs: stat.mtimeMs,
6837
+ sha256
5969
6838
  };
5970
6839
  } finally {
5971
6840
  await handle.close();
@@ -6021,14 +6890,14 @@ function isWorkspaceContentChanged(artifact, status) {
6021
6890
  __name(isWorkspaceContentChanged, "isWorkspaceContentChanged");
6022
6891
  async function danglingSymlinkEscapesWorkspace(absolutePath, realWorkspace) {
6023
6892
  try {
6024
- const stat = await fs2.lstat(absolutePath);
6893
+ const stat = await fs3.lstat(absolutePath);
6025
6894
  if (!stat.isSymbolicLink()) {
6026
6895
  return false;
6027
6896
  }
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));
6897
+ const target = await fs3.readlink(absolutePath);
6898
+ const parent = await fs3.realpath(path3.dirname(absolutePath));
6899
+ const targetPath = path3.resolve(parent, target);
6900
+ return isOutsidePath(path3.relative(realWorkspace, targetPath));
6032
6901
  } catch (error) {
6033
6902
  if (isNotFoundError(error)) {
6034
6903
  return false;
@@ -6051,6 +6920,10 @@ function isNotFoundError(error) {
6051
6920
  return code === "ENOENT" || code === "ENOTDIR";
6052
6921
  }
6053
6922
  __name(isNotFoundError, "isNotFoundError");
6923
+ function isPlainMetadataObject(value) {
6924
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6925
+ }
6926
+ __name(isPlainMetadataObject, "isPlainMetadataObject");
6054
6927
  function isNoFollowSymlinkError(error) {
6055
6928
  if (typeof error !== "object" || error === null || !("code" in error)) {
6056
6929
  return false;
@@ -6059,7 +6932,7 @@ function isNoFollowSymlinkError(error) {
6059
6932
  }
6060
6933
  __name(isNoFollowSymlinkError, "isNoFollowSymlinkError");
6061
6934
  function isOutsidePath(relative) {
6062
- return relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative);
6935
+ return relative === ".." || relative.startsWith(`..${path3.sep}`) || path3.isAbsolute(relative);
6063
6936
  }
6064
6937
  __name(isOutsidePath, "isOutsidePath");
6065
6938
 
@@ -6866,6 +7739,107 @@ function broadcastTurnError(entry, sessionId, err, promptId, originatorClientId,
6866
7739
  }
6867
7740
  }
6868
7741
  __name(broadcastTurnError, "broadcastTurnError");
7742
+ var TERMINAL_TURN_STATUS_OVERLAY_LIMIT = 64;
7743
+ function truncateTurnText(text) {
7744
+ const truncated = text.length > TURN_RESULT_TEXT_MAX_CHARS;
7745
+ return {
7746
+ text: truncated ? text.slice(0, TURN_RESULT_TEXT_MAX_CHARS) : text,
7747
+ truncated
7748
+ };
7749
+ }
7750
+ __name(truncateTurnText, "truncateTurnText");
7751
+ function rememberTerminalTurnStatus(entry, pending, terminal) {
7752
+ const promptText = truncateTurnText(pending.text);
7753
+ const shared = {
7754
+ sessionId: entry.sessionId,
7755
+ promptId: pending.promptId,
7756
+ promptText: promptText.text,
7757
+ ...promptText.truncated ? { promptTextTruncated: true } : {},
7758
+ queuedAt: pending.queuedAt,
7759
+ ...pending.startedAt !== void 0 ? { startedAt: pending.startedAt } : {},
7760
+ endedAt: Date.now(),
7761
+ ...pending.originatorClientId !== void 0 ? { originatorClientId: pending.originatorClientId } : {}
7762
+ };
7763
+ const status = terminal.kind === "complete" ? {
7764
+ ...shared,
7765
+ state: terminal.result.stopReason === "cancelled" ? "cancelled" : "completed",
7766
+ ...terminal.result.stopReason !== void 0 ? { stopReason: terminal.result.stopReason } : {}
7767
+ } : terminal.kind === "cancelled" ? { ...shared, state: "cancelled", stopReason: "cancelled" } : {
7768
+ ...shared,
7769
+ state: "error",
7770
+ error: normalizeTurnResultError(terminal.err)
7771
+ };
7772
+ entry.terminalTurnStatuses.set(pending.promptId, status);
7773
+ entry.enrichedTerminalPromptIds.delete(pending.promptId);
7774
+ while (entry.terminalTurnStatuses.size > TERMINAL_TURN_STATUS_OVERLAY_LIMIT) {
7775
+ const oldest2 = entry.terminalTurnStatuses.keys().next().value;
7776
+ if (oldest2 === void 0) break;
7777
+ entry.terminalTurnStatuses.delete(oldest2);
7778
+ entry.enrichedTerminalPromptIds.delete(oldest2);
7779
+ }
7780
+ }
7781
+ __name(rememberTerminalTurnStatus, "rememberTerminalTurnStatus");
7782
+ function rememberEnrichedTerminalTurnStatus(entry, promptId, status) {
7783
+ entry.terminalTurnStatuses.set(promptId, status);
7784
+ entry.enrichedTerminalPromptIds.add(promptId);
7785
+ while (entry.terminalTurnStatuses.size > TERMINAL_TURN_STATUS_OVERLAY_LIMIT) {
7786
+ const oldest2 = entry.terminalTurnStatuses.keys().next().value;
7787
+ if (oldest2 === void 0) break;
7788
+ entry.terminalTurnStatuses.delete(oldest2);
7789
+ entry.enrichedTerminalPromptIds.delete(oldest2);
7790
+ }
7791
+ }
7792
+ __name(rememberEnrichedTerminalTurnStatus, "rememberEnrichedTerminalTurnStatus");
7793
+ function advanceTurnActivity(entry) {
7794
+ const createdAtMs = Date.parse(entry.createdAt);
7795
+ const previous = entry.lastTurnEndedAtMs ?? (Number.isFinite(createdAtMs) ? createdAtMs : void 0);
7796
+ entry.lastTurnEndedAtMs = previous === void 0 ? Date.now() : Math.max(Date.now(), previous + 1);
7797
+ }
7798
+ __name(advanceTurnActivity, "advanceTurnActivity");
7799
+ function appendPromptLedgerBestEffort(entry, record) {
7800
+ const ledger = entry.promptLedger;
7801
+ if (!ledger) return;
7802
+ try {
7803
+ ledger.appendSync(entry.sessionId, record);
7804
+ } catch (error) {
7805
+ writeStderrLine(
7806
+ `qwen serve: prompt ledger append failed for session=${entry.sessionId} promptId=${record.promptId}: ${error instanceof Error ? error.message : String(error)}`
7807
+ );
7808
+ }
7809
+ }
7810
+ __name(appendPromptLedgerBestEffort, "appendPromptLedgerBestEffort");
7811
+ function promptLedgerTerminalRecord(pendingEntry, terminal) {
7812
+ const at = Date.now();
7813
+ if (terminal.kind === "complete") {
7814
+ if (terminal.result.stopReason === "cancelled") {
7815
+ return {
7816
+ v: 1,
7817
+ promptId: pendingEntry.promptId,
7818
+ terminal: "cancelled",
7819
+ at
7820
+ };
7821
+ }
7822
+ return {
7823
+ v: 1,
7824
+ promptId: pendingEntry.promptId,
7825
+ terminal: "completed",
7826
+ ...terminal.result.stopReason !== void 0 ? { stopReason: terminal.result.stopReason } : {},
7827
+ at
7828
+ };
7829
+ }
7830
+ if (terminal.kind === "cancelled") {
7831
+ return { v: 1, promptId: pendingEntry.promptId, terminal: "cancelled", at };
7832
+ }
7833
+ const normalized = normalizeTurnResultError(terminal.err);
7834
+ return {
7835
+ v: 1,
7836
+ promptId: pendingEntry.promptId,
7837
+ terminal: "error",
7838
+ ...normalized.code !== void 0 ? { code: normalized.code } : {},
7839
+ at
7840
+ };
7841
+ }
7842
+ __name(promptLedgerTerminalRecord, "promptLedgerTerminalRecord");
6869
7843
  function publishPromptTerminal(entry, pendingEntry, terminal) {
6870
7844
  if (pendingEntry.terminalPublished) {
6871
7845
  writeServeDebugLine(
@@ -6874,8 +7848,16 @@ function publishPromptTerminal(entry, pendingEntry, terminal) {
6874
7848
  return;
6875
7849
  }
6876
7850
  pendingEntry.terminalPublished = true;
7851
+ appendPromptLedgerBestEffort(
7852
+ entry,
7853
+ promptLedgerTerminalRecord(pendingEntry, terminal)
7854
+ );
7855
+ rememberTerminalTurnStatus(entry, pendingEntry, terminal);
6877
7856
  const originatorClientId = pendingEntry.originatorClientId;
6878
7857
  const mutateTurnState = pendingEntry.state === "running";
7858
+ if (mutateTurnState) {
7859
+ advanceTurnActivity(entry);
7860
+ }
6879
7861
  if (!mutateTurnState && entry.turnErrorEvent) {
6880
7862
  const journal = entry.events.liveJournalSnapshot() ?? [];
6881
7863
  if (journal.some(isRefreshAppendTurnContent)) {
@@ -6941,16 +7923,109 @@ function extractPromptText(prompt) {
6941
7923
  if (!Array.isArray(prompt)) return "";
6942
7924
  let hasImage = false;
6943
7925
  for (const block of prompt) {
6944
- if (block["type"] === "image") {
7926
+ const record = block;
7927
+ if (record["type"] === "image") {
6945
7928
  hasImage = true;
6946
7929
  }
6947
- if (block["type"] === "text" && typeof block["text"] === "string" && block["text"].length > 0) {
6948
- return block["text"];
7930
+ if (record["type"] === "text" && typeof record["text"] === "string" && record["text"].length > 0) {
7931
+ return record["text"];
6949
7932
  }
6950
7933
  }
6951
7934
  return hasImage ? "[image]" : "";
6952
7935
  }
6953
7936
  __name(extractPromptText, "extractPromptText");
7937
+ function liveTurnStatus(sessionId, pending) {
7938
+ const promptText = truncateTurnText(pending.text);
7939
+ return {
7940
+ sessionId,
7941
+ state: pending.state === "running" ? "running" : "queued",
7942
+ promptId: pending.promptId,
7943
+ promptText: promptText.text,
7944
+ ...promptText.truncated ? { promptTextTruncated: true } : {},
7945
+ queuedAt: pending.queuedAt,
7946
+ ...pending.startedAt !== void 0 ? { startedAt: pending.startedAt } : {},
7947
+ ...pending.originatorClientId !== void 0 ? { originatorClientId: pending.originatorClientId } : {}
7948
+ };
7949
+ }
7950
+ __name(liveTurnStatus, "liveTurnStatus");
7951
+ function findLiveTurnStatus(entry, promptId) {
7952
+ const live = entry.pendingPromptList.filter(
7953
+ (pending) => !pending.terminalPublished && (!pending.removed || pending.state === "running")
7954
+ );
7955
+ if (promptId !== void 0) {
7956
+ const match = live.find((pending) => pending.promptId === promptId);
7957
+ return match ? liveTurnStatus(entry.sessionId, match) : void 0;
7958
+ }
7959
+ const running = live.find((pending) => pending.state === "running");
7960
+ if (running) return liveTurnStatus(entry.sessionId, running);
7961
+ const queued = live.find((pending) => pending.state === "queued");
7962
+ return queued ? liveTurnStatus(entry.sessionId, queued) : void 0;
7963
+ }
7964
+ __name(findLiveTurnStatus, "findLiveTurnStatus");
7965
+ function settledTurnStatus(sessionId, record) {
7966
+ return {
7967
+ sessionId,
7968
+ state: record.state,
7969
+ promptId: record.promptId,
7970
+ ...record.stopReason !== void 0 ? { stopReason: record.stopReason } : {},
7971
+ ...record.error !== void 0 ? { error: record.error } : {},
7972
+ ...record.startedAt !== void 0 ? { startedAt: record.startedAt } : {},
7973
+ endedAt: record.endedAt,
7974
+ ...record.promptText !== void 0 ? { promptText: record.promptText } : {},
7975
+ ...record.promptTextTruncated !== void 0 ? { promptTextTruncated: record.promptTextTruncated } : {},
7976
+ ...record.resultText !== void 0 ? { resultText: record.resultText } : {},
7977
+ ...record.resultTruncated !== void 0 ? { resultTruncated: record.resultTruncated } : {},
7978
+ ...record.resultTruncated === true ? { resultCode: record.resultCode ?? TURN_RESULT_CODE_TEXT_TRUNCATED } : {},
7979
+ ...record.originatorClientId !== void 0 ? { originatorClientId: record.originatorClientId } : {}
7980
+ };
7981
+ }
7982
+ __name(settledTurnStatus, "settledTurnStatus");
7983
+ function enrichTerminalTurnStatus(terminal, persisted) {
7984
+ return {
7985
+ ...terminal,
7986
+ // The bridge's display projection is trusted; the child-recorded text
7987
+ // only backfills when the terminal has none, so hidden channel context
7988
+ // the child derived from raw blocks never replaces it.
7989
+ ...terminal.promptText === void 0 && persisted.promptText !== void 0 ? { promptText: persisted.promptText } : {},
7990
+ ...terminal.promptTextTruncated === void 0 && persisted.promptTextTruncated !== void 0 ? { promptTextTruncated: persisted.promptTextTruncated } : {},
7991
+ ...persisted.resultText !== void 0 ? { resultText: persisted.resultText } : {},
7992
+ ...persisted.resultTruncated !== void 0 ? { resultTruncated: persisted.resultTruncated } : {},
7993
+ ...persisted.resultCode !== void 0 ? { resultCode: persisted.resultCode } : {},
7994
+ ...terminal.originatorClientId === void 0 && persisted.originatorClientId !== void 0 ? { originatorClientId: persisted.originatorClientId } : {}
7995
+ };
7996
+ }
7997
+ __name(enrichTerminalTurnStatus, "enrichTerminalTurnStatus");
7998
+ function mergeTerminalWithPersisted(terminal, persisted) {
7999
+ if (terminal.state === "error" && persisted.state !== "error") {
8000
+ return {
8001
+ ...persisted,
8002
+ ...terminal.promptText !== void 0 ? { promptText: terminal.promptText } : {},
8003
+ ...terminal.promptTextTruncated !== void 0 ? { promptTextTruncated: terminal.promptTextTruncated } : {}
8004
+ };
8005
+ }
8006
+ return enrichTerminalTurnStatus(terminal, persisted);
8007
+ }
8008
+ __name(mergeTerminalWithPersisted, "mergeTerminalWithPersisted");
8009
+ function latestTerminalTurnStatus(entry) {
8010
+ let latest;
8011
+ for (const status of entry.terminalTurnStatuses.values()) {
8012
+ if ((status.endedAt ?? 0) >= (latest?.endedAt ?? 0)) latest = status;
8013
+ }
8014
+ return latest;
8015
+ }
8016
+ __name(latestTerminalTurnStatus, "latestTerminalTurnStatus");
8017
+ function extractMediaBlocks(prompt) {
8018
+ if (!Array.isArray(prompt)) return void 0;
8019
+ const media = [];
8020
+ for (const block of prompt) {
8021
+ if (!block || typeof block !== "object") continue;
8022
+ if (block.type === "image" || block.type === "audio" || block.type === "resource" && "attachmentId" in block) {
8023
+ media.push(block);
8024
+ }
8025
+ }
8026
+ return media.length > 0 ? media : void 0;
8027
+ }
8028
+ __name(extractMediaBlocks, "extractMediaBlocks");
6954
8029
  var DEFAULT_INIT_TIMEOUT_MS = 1e4;
6955
8030
  var PERSIST_TIMEOUT_MS = 5e3;
6956
8031
  var MAX_PARENT_PERSIST_ATTEMPTS = 3;
@@ -6968,6 +8043,22 @@ var MAX_EMPTY_TRANSCRIPT_PAGES = 20;
6968
8043
  var SHELL_COMMAND_TIMEOUT_MS = 12e4;
6969
8044
  var MAX_SHELL_OUTPUT_FOR_HISTORY = 1e4;
6970
8045
  var MAX_MID_TURN_QUEUE_DEPTH = 20;
8046
+ var MAX_QUEUED_INLINE_ATTACHMENT_BYTES = 100 * 1024 * 1024;
8047
+ function inlineAttachmentBlockBytes(blocks) {
8048
+ let total = 0;
8049
+ for (const block of blocks) {
8050
+ if (block.type === "image" && "data" in block) {
8051
+ total += Buffer.byteLength(block.data);
8052
+ continue;
8053
+ }
8054
+ if (block.type !== "resource" || !("resource" in block)) continue;
8055
+ const resource = block.resource;
8056
+ if ("blob" in resource) total += Buffer.byteLength(resource.blob);
8057
+ else if ("text" in resource) total += Buffer.byteLength(resource.text);
8058
+ }
8059
+ return total;
8060
+ }
8061
+ __name(inlineAttachmentBlockBytes, "inlineAttachmentBlockBytes");
6971
8062
  var DEFAULT_MAX_SESSIONS = 32;
6972
8063
  var DEFAULT_MAX_PENDING_PROMPTS_PER_SESSION = 5;
6973
8064
  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;
@@ -7034,7 +8125,17 @@ function createAcpSessionBridge(opts) {
7034
8125
  );
7035
8126
  }
7036
8127
  }, "releaseFreshSessionReservation");
8128
+ const sessionCatalogGeneration = randomUUID2();
8129
+ let sessionCatalogRevision = 0;
8130
+ const getSessionCatalogVersion = /* @__PURE__ */ __name(() => ({
8131
+ generation: sessionCatalogGeneration,
8132
+ revision: sessionCatalogRevision
8133
+ }), "getSessionCatalogVersion");
8134
+ const markSessionCatalogChanged = /* @__PURE__ */ __name(() => {
8135
+ sessionCatalogRevision += 1;
8136
+ }, "markSessionCatalogChanged");
7037
8137
  const emitSessionLifecycle = /* @__PURE__ */ __name((event) => {
8138
+ markSessionCatalogChanged();
7038
8139
  try {
7039
8140
  opts.sessionLifecycle?.(event);
7040
8141
  } catch (err) {
@@ -7104,7 +8205,7 @@ function createAcpSessionBridge(opts) {
7104
8205
  } else {
7105
8206
  maxPendingPromptsPerSession = maxPendingPromptsRaw;
7106
8207
  }
7107
- if (!path3.isAbsolute(opts.boundWorkspace)) {
8208
+ if (!path4.isAbsolute(opts.boundWorkspace)) {
7108
8209
  throw new TypeError(
7109
8210
  `Invalid boundWorkspace: "${opts.boundWorkspace}". Must be an absolute path.`
7110
8211
  );
@@ -7444,10 +8545,7 @@ function createAcpSessionBridge(opts) {
7444
8545
  }
7445
8546
  __name(withWorkspaceControl, "withWorkspaceControl");
7446
8547
  function startSessionReaper() {
7447
- if (sessionReapIntervalMs <= 0 || sessionIdleTimeoutMs <= 0) {
7448
- writeStderrLine("qwen serve: session reaper disabled");
7449
- return;
7450
- }
8548
+ if (sessionReapIntervalMs <= 0) return;
7451
8549
  writeStderrLine(
7452
8550
  `qwen serve: session reaper started (interval ${sessionReapIntervalMs}ms, idle threshold ${sessionIdleTimeoutMs}ms)`
7453
8551
  );
@@ -7455,6 +8553,7 @@ function createAcpSessionBridge(opts) {
7455
8553
  if (shuttingDown) return;
7456
8554
  const now = Date.now();
7457
8555
  for (const [id, entry] of byId) {
8556
+ if (sessionIdleTimeoutMs <= 0) break;
7458
8557
  if (!entryIsAutoCloseCandidate(entry)) continue;
7459
8558
  const lastActive = entry.sessionLastSeenAt ?? Date.parse(entry.createdAt);
7460
8559
  const idle = now - lastActive;
@@ -7552,12 +8651,13 @@ function createAcpSessionBridge(opts) {
7552
8651
  sessionId: entry.sessionId,
7553
8652
  workspaceCwd: entry.workspaceCwd,
7554
8653
  createdAt: entry.createdAt,
8654
+ ...entry.lastTurnEndedAtMs !== void 0 ? { updatedAt: new Date(entry.lastTurnEndedAtMs).toISOString() } : {},
7555
8655
  displayName: entry.displayName,
7556
8656
  ...entry.parentSessionId ? { parentSessionId: entry.parentSessionId } : {},
7557
8657
  ...entry.sourceType ? { sourceType: entry.sourceType } : {},
7558
8658
  ...entry.sourceId !== void 0 ? { sourceId: entry.sourceId } : {},
7559
8659
  clientCount: entry.clientIds.size,
7560
- hasActivePrompt: entry.promptActive,
8660
+ hasActivePrompt: entry.promptActive || entry.goalTurnActive === true,
7561
8661
  isWaitingForPermission,
7562
8662
  isWaitingForUserQuestion,
7563
8663
  pendingInteractionCount: entry.pendingInteractions.size,
@@ -7800,7 +8900,13 @@ function createAcpSessionBridge(opts) {
7800
8900
  const currentInfo = infoRef.current;
7801
8901
  if (!currentInfo) return;
7802
8902
  applyActiveWorkSnapshot(currentInfo, snapshot);
7803
- }
8903
+ },
8904
+ // Child-side automatic title updates change persisted catalog
8905
+ // metadata the bridge never sees; forward the catalog-clock mark.
8906
+ markSessionCatalogChanged,
8907
+ // A Goal turn drains the mid-turn queue but owns no prompt slot, so
8908
+ // nothing else would settle what its last drain missed.
8909
+ settleMidTurnQueueAfterGoalTurn
7804
8910
  );
7805
8911
  const rawConnection = new ClientSideConnection(
7806
8912
  () => channel.transportGuard ? createLogSafeAcpClient(client, channel.transportGuard) : client,
@@ -7938,6 +9044,11 @@ function createAcpSessionBridge(opts) {
7938
9044
  touchActivity();
7939
9045
  }
7940
9046
  byId.delete(sid);
9047
+ void sessEntry.attachments.close().catch((error) => {
9048
+ writeStderrLine(
9049
+ `qwen serve: failed to close attachments for closed channel session ${JSON.stringify(sid)}: ${error instanceof Error ? error.message : String(error)}`
9050
+ );
9051
+ });
7941
9052
  telemetry.metrics?.sessionLifecycle("die");
7942
9053
  emitSessionLifecycle({
7943
9054
  type: "removed",
@@ -8685,6 +9796,41 @@ function createAcpSessionBridge(opts) {
8685
9796
  }
8686
9797
  return response;
8687
9798
  }, "requestWorkspaceStatus");
9799
+ const MAX_CHILD_HEAP_UNCLASSIFIED_NAMES = 64;
9800
+ const MAX_CHILD_HEAP_SPACE_NAME_LENGTH = 64;
9801
+ const parseChildHeapReport = /* @__PURE__ */ __name((value) => {
9802
+ if (typeof value !== "object" || value === null) return void 0;
9803
+ const raw = value;
9804
+ const counts = [
9805
+ "peakOldGenerationBytes",
9806
+ "peakLiveSetBytes",
9807
+ "peakTotalHeapBytes",
9808
+ "majorGcCount",
9809
+ "majorGcMs"
9810
+ ];
9811
+ const parsed = {};
9812
+ for (const key of counts) {
9813
+ const n = raw[key];
9814
+ if (typeof n !== "number" || !Number.isFinite(n) || n < 0) {
9815
+ return void 0;
9816
+ }
9817
+ parsed[key] = n;
9818
+ }
9819
+ const names = raw["unclassifiedSpaceNames"];
9820
+ if (!Array.isArray(names) || names.length > MAX_CHILD_HEAP_UNCLASSIFIED_NAMES || names.some(
9821
+ (name) => typeof name !== "string" || name.length > MAX_CHILD_HEAP_SPACE_NAME_LENGTH
9822
+ )) {
9823
+ return void 0;
9824
+ }
9825
+ return {
9826
+ peakOldGenerationBytes: parsed["peakOldGenerationBytes"],
9827
+ peakLiveSetBytes: parsed["peakLiveSetBytes"],
9828
+ peakTotalHeapBytes: parsed["peakTotalHeapBytes"],
9829
+ majorGcCount: parsed["majorGcCount"],
9830
+ majorGcMs: parsed["majorGcMs"],
9831
+ unclassifiedSpaceNames: names
9832
+ };
9833
+ }, "parseChildHeapReport");
8688
9834
  const STALE_CHILD_RESOURCE_MS = 3e4;
8689
9835
  let childResourceRefreshing = false;
8690
9836
  const refreshChildResource = /* @__PURE__ */ __name(async () => {
@@ -8701,6 +9847,8 @@ function createAcpSessionBridge(opts) {
8701
9847
  if (typeof res.cpuPercent === "number" && Number.isFinite(res.cpuPercent)) {
8702
9848
  info.childCpuPercent = Math.min(100, Math.max(0, res.cpuPercent));
8703
9849
  }
9850
+ const heap = parseChildHeapReport(res.heap);
9851
+ if (heap) info.childHeap = heap;
8704
9852
  info.childResourceAt = Date.now();
8705
9853
  } catch (err) {
8706
9854
  teeServeDebugLine(
@@ -8720,6 +9868,10 @@ function createAcpSessionBridge(opts) {
8720
9868
  return {
8721
9869
  rssBytes: info.childRssBytes ?? 0,
8722
9870
  cpuPercent: info.childCpuPercent ?? 0,
9871
+ // Deliberately not defaulted. Unlike rss/cpu, where 0 is a plausible
9872
+ // reading, a zeroed heap report would assert the child needed no old
9873
+ // generation — the one conclusion that must never be manufactured.
9874
+ heap: info.childHeap,
8723
9875
  // Bounded by the guard above, so a caller summing several children's
8724
9876
  // readings can say how far apart they were taken. Without it a sum of
8725
9877
  // readings up to `STALE_CHILD_RESOURCE_MS` apart looks instantaneous.
@@ -8975,13 +10127,21 @@ function createAcpSessionBridge(opts) {
8975
10127
  workspaceCwd,
8976
10128
  persistence: createSessionArtifactPersistence(ci.connection, sessionId)
8977
10129
  }),
10130
+ attachments: new SessionAttachmentStore(
10131
+ opts.sessionAttachmentsRoot,
10132
+ sessionId
10133
+ ),
8978
10134
  recordingDegraded: false,
8979
10135
  closing: false,
8980
10136
  cwdChangeQueue: Promise.resolve(),
8981
10137
  promptQueue: Promise.resolve(),
8982
10138
  pendingPromptCount: 0,
8983
10139
  pendingAgentNotificationCount: 0,
10140
+ ...opts.promptLedger ? { promptLedger: opts.promptLedger } : {},
8984
10141
  pendingPromptList: [],
10142
+ terminalTurnStatuses: /* @__PURE__ */ new Map(),
10143
+ enrichedTerminalPromptIds: /* @__PURE__ */ new Set(),
10144
+ rewindGeneration: 0,
8985
10145
  midTurnMessageQueue: [],
8986
10146
  settledMidTurnMessageIds: [],
8987
10147
  promotedMidTurnMessageIds: [],
@@ -9395,7 +10555,7 @@ function createAcpSessionBridge(opts) {
9395
10555
  // Late attachers get the same ACP state the original restore
9396
10556
  // caller saw; spawn-only sessions don't carry a state payload.
9397
10557
  state: existing.restoreState ?? {},
9398
- hasActivePrompt: existing.promptActive,
10558
+ hasActivePrompt: existing.promptActive || existing.goalTurnActive === true,
9399
10559
  ...replayFields,
9400
10560
  ...historyAnchorRecordId !== void 0 ? { historyAnchorRecordId } : {}
9401
10561
  };
@@ -9477,7 +10637,7 @@ function createAcpSessionBridge(opts) {
9477
10637
  attached: true,
9478
10638
  clientId,
9479
10639
  createdAt: entry.createdAt,
9480
- hasActivePrompt: entry.promptActive,
10640
+ hasActivePrompt: entry.promptActive || entry.goalTurnActive === true,
9481
10641
  ...waiterReplayFields ?? {}
9482
10642
  };
9483
10643
  }
@@ -9840,7 +11000,7 @@ function createAcpSessionBridge(opts) {
9840
11000
  ...racedEntry.sourceType ? { sourceType: racedEntry.sourceType } : {},
9841
11001
  ...racedEntry.sourceId !== void 0 ? { sourceId: racedEntry.sourceId } : {},
9842
11002
  state: racedEntry.restoreState ?? {},
9843
- hasActivePrompt: racedEntry.promptActive,
11003
+ hasActivePrompt: racedEntry.promptActive || racedEntry.goalTurnActive === true,
9844
11004
  ...replayFieldsFor(racedEntry, action, liveReplayMode)
9845
11005
  };
9846
11006
  }
@@ -9916,7 +11076,7 @@ function createAcpSessionBridge(opts) {
9916
11076
  ...entry.sourceId !== void 0 ? { sourceId: entry.sourceId } : {},
9917
11077
  state: publicState,
9918
11078
  ...artifactRestoreWarnings.length > 0 ? { artifactWarnings: artifactRestoreWarnings } : {},
9919
- hasActivePrompt: entry.promptActive,
11079
+ hasActivePrompt: entry.promptActive || entry.goalTurnActive === true,
9920
11080
  ...replayFieldsFor(entry, action, liveReplayMode)
9921
11081
  };
9922
11082
  })().finally(async () => {
@@ -9931,6 +11091,11 @@ function createAcpSessionBridge(opts) {
9931
11091
  const restoreEntry = byId.get(req.sessionId);
9932
11092
  if (restoreEntry?.events === restoreEvents) {
9933
11093
  byId.delete(req.sessionId);
11094
+ await restoreEntry.attachments.close().catch((error) => {
11095
+ writeStderrLine(
11096
+ `qwen serve: failed to close attachments after restoring session ${JSON.stringify(req.sessionId)}: ${error instanceof Error ? error.message : String(error)}`
11097
+ );
11098
+ });
9934
11099
  ci?.sessionIds.delete(req.sessionId);
9935
11100
  emitSessionLifecycle({
9936
11101
  type: "removed",
@@ -10094,6 +11259,11 @@ function createAcpSessionBridge(opts) {
10094
11259
  } catch {
10095
11260
  }
10096
11261
  entry.events.close();
11262
+ await entry.attachments.close().catch((error) => {
11263
+ writeStderrLine(
11264
+ `qwen serve: failed to close attachments for session ${JSON.stringify(sessionId)}: ${error instanceof Error ? error.message : String(error)}`
11265
+ );
11266
+ });
10097
11267
  if (!agentSessionClosed) {
10098
11268
  try {
10099
11269
  await telemetry.withSpan(
@@ -10122,30 +11292,102 @@ function createAcpSessionBridge(opts) {
10122
11292
  ring.splice(0, ring.length - MID_TURN_RECONCILIATION_RING_SIZE);
10123
11293
  }
10124
11294
  }, "rememberMidTurnId");
10125
- const promoteMidTurnMessage = /* @__PURE__ */ __name((entry, messageId, text, originatorClientId) => {
10126
- void bridgeApi.sendPrompt(
11295
+ const promoteMidTurnMessage = /* @__PURE__ */ __name((entry, messageId, text, originatorClientId, content) => {
11296
+ const resolvableBlocks = [];
11297
+ let degraded = 0;
11298
+ for (const block of content ?? []) {
11299
+ try {
11300
+ entry.attachments.assertReference(block);
11301
+ resolvableBlocks.push(block);
11302
+ } catch (error) {
11303
+ if (!(error instanceof SessionAttachmentReferenceError)) throw error;
11304
+ degraded += 1;
11305
+ }
11306
+ }
11307
+ let prompt = [
11308
+ ...text ? [{ type: "text", text }] : [],
11309
+ ...resolvableBlocks
11310
+ ];
11311
+ if (degraded > 0) {
11312
+ prompt = withAttachmentDegradationMarker(prompt);
11313
+ }
11314
+ const context = {
11315
+ promptId: messageId,
11316
+ promotedMidTurn: { originatorClientId },
11317
+ onPromptAdmitted: /* @__PURE__ */ __name(() => {
11318
+ rememberMidTurnId(entry.promotedMidTurnMessageIds, messageId);
11319
+ }, "onPromptAdmitted")
11320
+ };
11321
+ const sendFallback = /* @__PURE__ */ __name(() => bridgeApi.sendPrompt(
10127
11322
  entry.sessionId,
10128
11323
  {
10129
11324
  sessionId: entry.sessionId,
10130
- prompt: [{ type: "text", text }]
11325
+ prompt: withAttachmentDegradationMarker(
11326
+ text ? [{ type: "text", text }] : []
11327
+ )
10131
11328
  },
10132
11329
  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) => {
11330
+ context
11331
+ ), "sendFallback");
11332
+ let result;
11333
+ try {
11334
+ result = bridgeApi.sendPrompt(
11335
+ entry.sessionId,
11336
+ {
11337
+ sessionId: entry.sessionId,
11338
+ prompt
11339
+ },
11340
+ void 0,
11341
+ context
11342
+ );
11343
+ } catch (error) {
11344
+ try {
11345
+ if (!(error instanceof SessionAttachmentReferenceError)) throw error;
11346
+ result = sendFallback();
11347
+ } catch (fallbackError) {
11348
+ writeStderrLine(
11349
+ `[mid-turn] session=${JSON.stringify(entry.sessionId)} failed to run promoted message ${JSON.stringify(messageId)}: ${JSON.stringify(fallbackError instanceof Error ? fallbackError.message : String(fallbackError))}`
11350
+ );
11351
+ return;
11352
+ }
11353
+ }
11354
+ void result.catch((error) => {
10144
11355
  writeStderrLine(
10145
11356
  `[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
11357
  );
10147
11358
  });
10148
11359
  }, "promoteMidTurnMessage");
11360
+ const settleUndrainedMidTurnMessages = /* @__PURE__ */ __name((entry, messages) => {
11361
+ for (const message of messages) {
11362
+ if (message.queueOnly) {
11363
+ try {
11364
+ message.onSettledWithoutDrain?.();
11365
+ } catch (error) {
11366
+ writeStderrLine(
11367
+ `[mid-turn] session=${JSON.stringify(entry.sessionId)} failed to hand undrained queue-only message ${JSON.stringify(message.messageId)} back to its caller: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
11368
+ );
11369
+ }
11370
+ continue;
11371
+ }
11372
+ promoteMidTurnMessage(
11373
+ entry,
11374
+ message.messageId,
11375
+ message.text,
11376
+ message.originatorClientId,
11377
+ message.content
11378
+ );
11379
+ }
11380
+ }, "settleUndrainedMidTurnMessages");
11381
+ const settleMidTurnQueueAfterGoalTurn = /* @__PURE__ */ __name((sessionId) => {
11382
+ const entry = byId.get(sessionId);
11383
+ if (!entry) return;
11384
+ if (entry.goalTurnActive === true || entry.pendingPromptCount > 0 || entry.closing) {
11385
+ return;
11386
+ }
11387
+ const undrained = entry.midTurnMessageQueue.splice(0);
11388
+ if (undrained.length === 0) return;
11389
+ settleUndrainedMidTurnMessages(entry, undrained);
11390
+ }, "settleMidTurnQueueAfterGoalTurn");
10149
11391
  const bridgeApi = {
10150
11392
  setLiveScreenContextCaptureHandler(handler) {
10151
11393
  liveScreenContextCaptureHandler = handler;
@@ -10188,7 +11430,7 @@ function createAcpSessionBridge(opts) {
10188
11430
  attachCount: entry.attachCount,
10189
11431
  pendingPromptCount: entry.pendingPromptCount,
10190
11432
  pendingPermissionCount: entry.pendingPermissionIds.size,
10191
- hasActivePrompt: entry.promptActive,
11433
+ hasActivePrompt: entry.promptActive || entry.goalTurnActive === true,
10192
11434
  lastEventId: entry.events.lastEventId,
10193
11435
  ...entry.sessionLastSeenAt !== void 0 ? { lastSeenAt: entry.sessionLastSeenAt } : {},
10194
11436
  ...entry.currentModelId ? { currentModelId: entry.currentModelId } : {},
@@ -10341,7 +11583,7 @@ function createAcpSessionBridge(opts) {
10341
11583
  createdAt: existing.createdAt,
10342
11584
  ...existing.sourceType ? { sourceType: existing.sourceType } : {},
10343
11585
  ...existing.sourceId !== void 0 ? { sourceId: existing.sourceId } : {},
10344
- hasActivePrompt: existing.promptActive
11586
+ hasActivePrompt: existing.promptActive || existing.goalTurnActive === true
10345
11587
  };
10346
11588
  }
10347
11589
  const inFlight = inFlightSpawns.get(workspaceKey);
@@ -10392,7 +11634,7 @@ function createAcpSessionBridge(opts) {
10392
11634
  ...session,
10393
11635
  attached: true,
10394
11636
  clientId,
10395
- hasActivePrompt: attachedEntry.promptActive
11637
+ hasActivePrompt: attachedEntry.promptActive || attachedEntry.goalTurnActive === true
10396
11638
  };
10397
11639
  }
10398
11640
  }
@@ -10490,9 +11732,15 @@ function createAcpSessionBridge(opts) {
10490
11732
  )
10491
11733
  );
10492
11734
  }
11735
+ if (!Array.isArray(req.prompt)) {
11736
+ return Promise.reject(
11737
+ RequestError.invalidParams(void 0, "Prompt must be an array")
11738
+ );
11739
+ }
10493
11740
  const promotedMidTurn = context?.promotedMidTurn;
10494
11741
  const isPromotedMidTurn = promotedMidTurn !== void 0;
10495
11742
  const originatorClientId = promotedMidTurn ? promotedMidTurn.originatorClientId : resolveTrustedClientId(entry, context?.clientId);
11743
+ entry.attachments.assertReferences(req.prompt);
10496
11744
  const modelPrompt = context?.modelPrompt;
10497
11745
  if (modelPrompt !== void 0 && !isValidTrustedModelPrompt(modelPrompt)) {
10498
11746
  throw new TypeError(
@@ -10549,10 +11797,27 @@ function createAcpSessionBridge(opts) {
10549
11797
  ...originatorClientId !== void 0 ? { originatorClientId } : {},
10550
11798
  ...isPromotedMidTurn ? { promotedMidTurn: true } : {},
10551
11799
  text: pendingText,
11800
+ content: extractMediaBlocks(req.prompt),
10552
11801
  abortController: pendingAbort,
10553
11802
  state: isQueued ? "queued" : "running"
10554
11803
  };
10555
11804
  entry.pendingPromptList.push(pendingEntry);
11805
+ let tailUuid;
11806
+ try {
11807
+ tailUuid = entry.promptLedger?.transcriptTailUuid?.(sessionId);
11808
+ } catch (error) {
11809
+ opts.onDiagnosticLine?.(
11810
+ `qwen serve: prompt ledger dispatch marker failed for session=${sessionId}: ${error instanceof Error ? error.message : String(error)}`,
11811
+ "warn"
11812
+ );
11813
+ }
11814
+ appendPromptLedgerBestEffort(entry, {
11815
+ v: 1,
11816
+ promptId,
11817
+ state: "in_flight",
11818
+ ...tailUuid !== void 0 ? { tailUuid } : {},
11819
+ at: queuedAt
11820
+ });
10556
11821
  try {
10557
11822
  context?.onPromptAdmitted?.();
10558
11823
  } catch (error) {
@@ -10641,6 +11906,7 @@ function createAcpSessionBridge(opts) {
10641
11906
  }
10642
11907
  throw new DOMException("Prompt aborted", "AbortError");
10643
11908
  }
11909
+ pendingEntry.startedAt = Date.now();
10644
11910
  if (pendingEntry.state === "queued" || isPromotedMidTurn) {
10645
11911
  if (pendingEntry.state === "queued") {
10646
11912
  delete entry.todoStopGuardAwaitingQueuedPromptOwnerPromptId;
@@ -10668,10 +11934,27 @@ function createAcpSessionBridge(opts) {
10668
11934
  ...context?.clientId ? { "qwen-code.client_id": context.clientId } : {}
10669
11935
  },
10670
11936
  async () => {
11937
+ let dispatchBlocks = req.prompt;
11938
+ let resolvedPrompt;
11939
+ try {
11940
+ resolvedPrompt = await entry.attachments.resolveContent(dispatchBlocks);
11941
+ } catch (error) {
11942
+ if (!isPromotedMidTurn || !(error instanceof SessionAttachmentReferenceError)) {
11943
+ throw error;
11944
+ }
11945
+ const perBlock = await entry.attachments.resolveContentDegrading(
11946
+ dispatchBlocks
11947
+ );
11948
+ dispatchBlocks = perBlock.retainedBlocks;
11949
+ resolvedPrompt = withAttachmentDegradationMarker(
11950
+ perBlock.resolvedBlocks
11951
+ );
11952
+ }
10671
11953
  const normalized = telemetry.injectPromptContext(
10672
11954
  {
10673
11955
  ...req,
10674
- sessionId
11956
+ sessionId,
11957
+ prompt: resolvedPrompt
10675
11958
  }
10676
11959
  );
10677
11960
  assertLivePromptEntry(sessionId, entry);
@@ -10694,6 +11977,7 @@ function createAcpSessionBridge(opts) {
10694
11977
  delete meta[DAEMON_CHANNEL_DELIVERY_META_KEY];
10695
11978
  delete meta[DAEMON_PROMPT_DISPLAY_TEXT_META_KEY];
10696
11979
  delete meta[DAEMON_MODEL_PROMPT_META_KEY];
11980
+ delete meta[DAEMON_ATTACHMENT_REFERENCES_META_KEY];
10697
11981
  delete meta[CHANNEL_PROMPT_META_KEY];
10698
11982
  if (isRetry) {
10699
11983
  meta[DAEMON_RETRY_META_KEY] = true;
@@ -10710,6 +11994,12 @@ function createAcpSessionBridge(opts) {
10710
11994
  if (modelPrompt !== void 0) {
10711
11995
  meta[DAEMON_MODEL_PROMPT_META_KEY] = modelPrompt;
10712
11996
  }
11997
+ const attachmentReferences = dispatchBlocks.filter(
11998
+ isSessionAttachmentReference
11999
+ );
12000
+ if (attachmentReferences.length > 0) {
12001
+ meta[DAEMON_ATTACHMENT_REFERENCES_META_KEY] = attachmentReferences;
12002
+ }
10713
12003
  if (context?.channelPrompt === true) {
10714
12004
  meta[CHANNEL_PROMPT_META_KEY] = true;
10715
12005
  }
@@ -10722,6 +12012,7 @@ function createAcpSessionBridge(opts) {
10722
12012
  return copy;
10723
12013
  })();
10724
12014
  entry.promptActive = true;
12015
+ entry.goalTurnActive = false;
10725
12016
  entry.activePromptId = pendingEntry.promptId;
10726
12017
  delete entry.cancelBroadcastWithoutPrompt;
10727
12018
  delete entry.turnError;
@@ -10738,7 +12029,10 @@ function createAcpSessionBridge(opts) {
10738
12029
  if (!isRetry && !isContinue) {
10739
12030
  echoPromptToSessionBus(
10740
12031
  entry,
10741
- promptRequest,
12032
+ {
12033
+ ...promptRequest,
12034
+ prompt: dispatchBlocks
12035
+ },
10742
12036
  pendingEntry.promptId,
10743
12037
  originatorClientId,
10744
12038
  channelDisplayText
@@ -10879,24 +12173,7 @@ function createAcpSessionBridge(opts) {
10879
12173
  const shouldSettleMidTurnQueue = entry.pendingPromptCount === 1 && !entry.closing && byId.get(entry.sessionId) === entry;
10880
12174
  const undrainedMessages = shouldSettleMidTurnQueue ? entry.midTurnMessageQueue.splice(0) : [];
10881
12175
  releasePromptSlot();
10882
- for (const message of undrainedMessages) {
10883
- if (message.queueOnly) {
10884
- try {
10885
- message.onSettledWithoutDrain?.();
10886
- } catch (error) {
10887
- writeStderrLine(
10888
- `[mid-turn] session=${JSON.stringify(entry.sessionId)} failed to hand undrained queue-only message ${JSON.stringify(message.messageId)} back to its caller: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
10889
- );
10890
- }
10891
- continue;
10892
- }
10893
- promoteMidTurnMessage(
10894
- entry,
10895
- message.messageId,
10896
- message.text,
10897
- message.originatorClientId
10898
- );
10899
- }
12176
+ settleUndrainedMidTurnMessages(entry, undrainedMessages);
10900
12177
  void maybeCloseIdleSession(entry, "prompt_settled");
10901
12178
  }).catch(() => {
10902
12179
  });
@@ -11141,6 +12418,22 @@ function createAcpSessionBridge(opts) {
11141
12418
  `branchSession: agent returned invalid response: ${JSON.stringify(result)}`
11142
12419
  );
11143
12420
  }
12421
+ markSessionCatalogChanged();
12422
+ if (opts.sessionAttachmentsRoot) {
12423
+ const branchAttachments = new SessionAttachmentStore(
12424
+ opts.sessionAttachmentsRoot,
12425
+ result.newSessionId
12426
+ );
12427
+ try {
12428
+ await branchAttachments.copyFrom(entry.attachments);
12429
+ } catch (error) {
12430
+ writeStderrLine(
12431
+ `qwen serve: failed to copy attachments for branched session ${result.newSessionId}: ${error instanceof Error ? error.message : String(error)}`
12432
+ );
12433
+ } finally {
12434
+ await branchAttachments.close();
12435
+ }
12436
+ }
11144
12437
  const rawBranchName = result.displayName ?? result.title;
11145
12438
  const branchDisplayName = typeof rawBranchName === "string" ? rawBranchName : result.newSessionId.slice(0, 8);
11146
12439
  if (!restoreBranch) {
@@ -11207,6 +12500,15 @@ function createAcpSessionBridge(opts) {
11207
12500
  throw restoreErr;
11208
12501
  }
11209
12502
  const newEntry = byId.get(result.newSessionId);
12503
+ if (newEntry && !opts.sessionAttachmentsRoot) {
12504
+ try {
12505
+ await newEntry.attachments.copyFrom(entry.attachments);
12506
+ } catch (error) {
12507
+ writeStderrLine(
12508
+ `qwen serve: failed to copy attachments for branched session ${result.newSessionId}: ${error instanceof Error ? error.message : String(error)}`
12509
+ );
12510
+ }
12511
+ }
11210
12512
  if (newEntry) newEntry.displayName = branchDisplayName;
11211
12513
  let sourcePersisted;
11212
12514
  if (newEntry?.sourceType) {
@@ -11334,6 +12636,7 @@ function createAcpSessionBridge(opts) {
11334
12636
  const entry = byId.get(sessionId);
11335
12637
  if (entry) {
11336
12638
  entry.worktree = worktree;
12639
+ markSessionCatalogChanged();
11337
12640
  }
11338
12641
  },
11339
12642
  async closeSession(sessionId, context, closeOpts) {
@@ -11359,6 +12662,7 @@ function createAcpSessionBridge(opts) {
11359
12662
  const nextDisplayName = metadata.displayName || void 0;
11360
12663
  if (entry.displayName !== nextDisplayName) {
11361
12664
  entry.displayName = nextDisplayName;
12665
+ markSessionCatalogChanged();
11362
12666
  writeStderrLine(
11363
12667
  `qwen serve: updated session metadata ${JSON.stringify(sessionId)} displayName=${entry.displayName === void 0 ? "cleared" : "set"}` + (context?.clientId ? ` by client ${JSON.stringify(context.clientId)}` : "")
11364
12668
  );
@@ -11421,7 +12725,7 @@ function createAcpSessionBridge(opts) {
11421
12725
  return warnings.length > 0 ? { ...result, warnings } : result;
11422
12726
  },
11423
12727
  listWorkspaceSessions(workspaceCwd) {
11424
- if (!path3.isAbsolute(workspaceCwd)) return [];
12728
+ if (!path4.isAbsolute(workspaceCwd)) return [];
11425
12729
  const key = workspaceCwd === boundWorkspace ? boundWorkspace : canonicalizeWorkspace(workspaceCwd);
11426
12730
  if (key !== boundWorkspace) return [];
11427
12731
  const out = [];
@@ -11432,6 +12736,8 @@ function createAcpSessionBridge(opts) {
11432
12736
  }
11433
12737
  return out;
11434
12738
  },
12739
+ getSessionCatalogVersion,
12740
+ markSessionCatalogChanged,
11435
12741
  getSessionSummary(sessionId) {
11436
12742
  const entry = byId.get(sessionId);
11437
12743
  if (!entry) throw new SessionNotFoundError(sessionId);
@@ -11748,6 +13054,18 @@ function createAcpSessionBridge(opts) {
11748
13054
  { taskId, taskKind }
11749
13055
  );
11750
13056
  },
13057
+ async controlSessionGoal(sessionId, request, context) {
13058
+ const entry = byId.get(sessionId);
13059
+ if (!entry) throw new SessionNotFoundError(sessionId);
13060
+ const info = channelInfoForEntry(entry);
13061
+ if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
13062
+ resolveTrustedClientId(entry, context?.clientId);
13063
+ return requestSessionStatus(
13064
+ sessionId,
13065
+ SERVE_CONTROL_EXT_METHODS.sessionGoalControl,
13066
+ { request }
13067
+ );
13068
+ },
11751
13069
  async clearSessionGoal(sessionId) {
11752
13070
  return requestSessionStatus(
11753
13071
  sessionId,
@@ -12191,14 +13509,95 @@ function createAcpSessionBridge(opts) {
12191
13509
  const entry = byId.get(sessionId);
12192
13510
  if (!entry) throw new SessionNotFoundError(sessionId);
12193
13511
  resolveTrustedClientId(entry, context?.clientId);
12194
- return entry.pendingPromptList.filter((p) => !p.removed).map((p) => ({
13512
+ return entry.pendingPromptList.filter((p) => !p.removed && !p.terminalPublished).map((p) => ({
12195
13513
  promptId: p.promptId,
12196
13514
  text: p.text,
13515
+ ...p.content ? { content: p.content } : {},
12197
13516
  queuedAt: p.queuedAt,
12198
13517
  state: p.state,
12199
13518
  ...p.originatorClientId !== void 0 ? { originatorClientId: p.originatorClientId } : {}
12200
13519
  }));
12201
13520
  },
13521
+ async getSessionTurnStatus(sessionId, context, promptId) {
13522
+ const entry = byId.get(sessionId);
13523
+ if (!entry) throw new SessionNotFoundError(sessionId);
13524
+ resolveTrustedClientId(entry, context?.clientId);
13525
+ const liveBeforeRead = findLiveTurnStatus(entry, promptId);
13526
+ if (liveBeforeRead) return liveBeforeRead;
13527
+ if (promptId !== void 0 && entry.enrichedTerminalPromptIds.has(promptId)) {
13528
+ const enrichedTerminal = entry.terminalTurnStatuses.get(promptId);
13529
+ if (enrichedTerminal) return enrichedTerminal;
13530
+ }
13531
+ const rewindGenerationBeforeRead = entry.rewindGeneration;
13532
+ let result;
13533
+ try {
13534
+ result = await requestSessionStatus(
13535
+ sessionId,
13536
+ SERVE_CONTROL_EXT_METHODS.sessionTurnStatus,
13537
+ { ...promptId !== void 0 ? { promptId } : {} },
13538
+ // Transcript scans of large histories exceed the 10s init default;
13539
+ // give the read the same budget as other transcript reads.
13540
+ SESSION_TRANSCRIPT_TIMEOUT_MS
13541
+ );
13542
+ } catch (error) {
13543
+ const liveAfterFailure = findLiveTurnStatus(entry, promptId);
13544
+ if (liveAfterFailure) return liveAfterFailure;
13545
+ const terminalAfterFailure = promptId !== void 0 ? entry.terminalTurnStatuses.get(promptId) : latestTerminalTurnStatus(entry);
13546
+ if (terminalAfterFailure) return terminalAfterFailure;
13547
+ throw error;
13548
+ }
13549
+ const liveAfterRead = findLiveTurnStatus(entry, promptId);
13550
+ if (liveAfterRead) return liveAfterRead;
13551
+ const terminal = promptId !== void 0 ? entry.terminalTurnStatuses.get(promptId) : latestTerminalTurnStatus(entry);
13552
+ const persisted = result.turnResult && entry.rewindGeneration === rewindGenerationBeforeRead ? settledTurnStatus(sessionId, result.turnResult) : void 0;
13553
+ if (promptId !== void 0) {
13554
+ if (terminal && persisted) {
13555
+ const merged = mergeTerminalWithPersisted(terminal, persisted);
13556
+ rememberEnrichedTerminalTurnStatus(entry, promptId, merged);
13557
+ return merged;
13558
+ }
13559
+ if (terminal) return terminal;
13560
+ if (persisted) {
13561
+ rememberEnrichedTerminalTurnStatus(entry, promptId, persisted);
13562
+ return persisted;
13563
+ }
13564
+ } else {
13565
+ if (terminal && persisted && terminal.promptId === persisted.promptId) {
13566
+ return mergeTerminalWithPersisted(terminal, persisted);
13567
+ }
13568
+ if (terminal && persisted) {
13569
+ return (terminal.endedAt ?? 0) >= (persisted.endedAt ?? 0) ? terminal : persisted;
13570
+ }
13571
+ if (terminal) return terminal;
13572
+ if (persisted) return persisted;
13573
+ }
13574
+ if (promptId !== void 0) {
13575
+ return void 0;
13576
+ }
13577
+ return { sessionId, state: "idle" };
13578
+ },
13579
+ async storeSessionAttachment(sessionId, data, mimeType, context, name) {
13580
+ const entry = byId.get(sessionId);
13581
+ if (!entry) throw new SessionNotFoundError(sessionId);
13582
+ resolveTrustedClientId(entry, context?.clientId);
13583
+ return await entry.attachments.putAttachment(data, mimeType, name);
13584
+ },
13585
+ async readSessionAttachment(sessionId, attachmentId, context) {
13586
+ const entry = byId.get(sessionId);
13587
+ if (!entry) throw new SessionNotFoundError(sessionId);
13588
+ resolveTrustedClientId(entry, context?.clientId);
13589
+ return await entry.attachments.read(attachmentId);
13590
+ },
13591
+ async removeSessionAttachment(sessionId, attachmentId, context) {
13592
+ const entry = byId.get(sessionId);
13593
+ if (!entry) throw new SessionNotFoundError(sessionId);
13594
+ resolveTrustedClientId(entry, context?.clientId);
13595
+ return await entry.attachments.remove(attachmentId);
13596
+ },
13597
+ async deleteSessionAttachments(sessionId) {
13598
+ const store = byId.get(sessionId)?.attachments ?? new SessionAttachmentStore(opts.sessionAttachmentsRoot, sessionId);
13599
+ await store.delete();
13600
+ },
12202
13601
  removePendingPrompt(sessionId, promptId, context) {
12203
13602
  const entry = byId.get(sessionId);
12204
13603
  if (!entry) throw new SessionNotFoundError(sessionId);
@@ -12242,7 +13641,10 @@ function createAcpSessionBridge(opts) {
12242
13641
  context?.clientId
12243
13642
  );
12244
13643
  const trimmed = message.trim();
12245
- if (trimmed.length === 0) {
13644
+ const mediaBlocks = (options?.content ?? []).filter(
13645
+ (block) => block.type === "image" || block.type === "resource"
13646
+ );
13647
+ if (trimmed.length === 0 && mediaBlocks.length === 0) {
12246
13648
  writeStderrLine(
12247
13649
  `[mid-turn] session=${entry.sessionId} rejected: empty`
12248
13650
  );
@@ -12253,11 +13655,12 @@ function createAcpSessionBridge(opts) {
12253
13655
  (queued) => queued.messageId === requestedMessageId
12254
13656
  );
12255
13657
  if (existing) {
12256
- if (existing.text === trimmed) {
13658
+ const sameMedia = JSON.stringify(existing.content ?? []) === JSON.stringify(mediaBlocks);
13659
+ if (existing.text === trimmed && sameMedia) {
12257
13660
  return { accepted: true, messageId: requestedMessageId };
12258
13661
  }
12259
13662
  writeStderrLine(
12260
- `[mid-turn] session=${JSON.stringify(entry.sessionId)} rejected id ${JSON.stringify(requestedMessageId)}: text mismatch`
13663
+ `[mid-turn] session=${JSON.stringify(entry.sessionId)} rejected id ${JSON.stringify(requestedMessageId)}: text or content mismatch`
12261
13664
  );
12262
13665
  return { accepted: false };
12263
13666
  }
@@ -12265,11 +13668,16 @@ function createAcpSessionBridge(opts) {
12265
13668
  (pending) => pending.promptId === requestedMessageId
12266
13669
  );
12267
13670
  if (promoted) {
12268
- if (promoted.text === trimmed) {
13671
+ const promotedMedia = (promoted.content ?? []).filter(
13672
+ (block) => block.type === "image" || block.type === "resource"
13673
+ );
13674
+ const sameMedia = JSON.stringify(promotedMedia) === JSON.stringify(mediaBlocks);
13675
+ const promotedText = promoted.text === "[image]" && trimmed.length === 0 ? "" : promoted.text;
13676
+ if (promotedText === trimmed && sameMedia) {
12269
13677
  return { accepted: true, messageId: requestedMessageId };
12270
13678
  }
12271
13679
  writeStderrLine(
12272
- `[mid-turn] session=${JSON.stringify(entry.sessionId)} rejected promoted id ${JSON.stringify(requestedMessageId)}: text mismatch`
13680
+ `[mid-turn] session=${JSON.stringify(entry.sessionId)} rejected promoted id ${JSON.stringify(requestedMessageId)}: text or content mismatch`
12273
13681
  );
12274
13682
  return { accepted: false };
12275
13683
  }
@@ -12286,15 +13694,35 @@ function createAcpSessionBridge(opts) {
12286
13694
  );
12287
13695
  return { accepted: false };
12288
13696
  }
13697
+ entry.attachments.assertReferences(mediaBlocks);
13698
+ const inlineBytes = inlineAttachmentBlockBytes(mediaBlocks);
13699
+ if (inlineBytes > 0) {
13700
+ const queuedInlineBytes = entry.midTurnMessageQueue.reduce(
13701
+ (total, queued) => total + inlineAttachmentBlockBytes(queued.content ?? []),
13702
+ 0
13703
+ );
13704
+ if (queuedInlineBytes + inlineBytes > MAX_QUEUED_INLINE_ATTACHMENT_BYTES) {
13705
+ writeStderrLine(
13706
+ `[mid-turn] session=${entry.sessionId} rejected: queued inline attachments exceed the ${MAX_QUEUED_INLINE_ATTACHMENT_BYTES}-byte session budget`
13707
+ );
13708
+ return { accepted: false };
13709
+ }
13710
+ }
12289
13711
  const messageId = requestedMessageId ?? randomUUID2();
12290
- if (entry.pendingPromptCount === 0) {
12291
- if (options?.queueOnly) {
13712
+ if (entry.pendingPromptCount === 0 && entry.goalTurnActive !== true) {
13713
+ if (options?.queueOnly || options?.rejectIfIdle) {
12292
13714
  writeStderrLine(
12293
13715
  `[mid-turn] session=${JSON.stringify(entry.sessionId)} rejected id ${JSON.stringify(messageId)}: session idle`
12294
13716
  );
12295
13717
  return { accepted: false };
12296
13718
  }
12297
- promoteMidTurnMessage(entry, messageId, trimmed, originatorClientId);
13719
+ promoteMidTurnMessage(
13720
+ entry,
13721
+ messageId,
13722
+ trimmed,
13723
+ originatorClientId,
13724
+ mediaBlocks.length > 0 ? mediaBlocks : void 0
13725
+ );
12298
13726
  return { accepted: true, messageId };
12299
13727
  }
12300
13728
  if (entry.midTurnMessageQueue.length >= MAX_MID_TURN_QUEUE_DEPTH) {
@@ -12306,6 +13734,7 @@ function createAcpSessionBridge(opts) {
12306
13734
  const queuedMessage = {
12307
13735
  messageId,
12308
13736
  text: trimmed,
13737
+ ...mediaBlocks.length > 0 ? { content: mediaBlocks } : {},
12309
13738
  originatorClientId,
12310
13739
  ...options?.queueOnly ? {
12311
13740
  queueOnly: true,
@@ -12414,7 +13843,11 @@ function createAcpSessionBridge(opts) {
12414
13843
  // should restore into their queue views.
12415
13844
  messages: entry.midTurnMessageQueue.filter((message) => message.originatorClientId !== void 0).map((message) => ({
12416
13845
  messageId: message.messageId,
12417
- text: message.text
13846
+ text: message.text,
13847
+ // Carry the media blocks so a refreshed client can rebuild the
13848
+ // queued row with its attachments (the snapshot is the only
13849
+ // recovery source once the client's in-memory copy is gone).
13850
+ ...message.content ? { content: message.content } : {}
12418
13851
  })),
12419
13852
  settledMessageIds: [...entry.settledMidTurnMessageIds],
12420
13853
  promotedMessageIds: [...entry.promotedMidTurnMessageIds]
@@ -12716,6 +14149,9 @@ function createAcpSessionBridge(opts) {
12716
14149
  }
12717
14150
  throw err;
12718
14151
  }
14152
+ entry.terminalTurnStatuses.clear();
14153
+ entry.enrichedTerminalPromptIds.clear();
14154
+ entry.rewindGeneration += 1;
12719
14155
  const targetTurnIndex = response["targetTurnIndex"] ?? 0;
12720
14156
  const filesChanged = response["filesChanged"] ?? [];
12721
14157
  const filesFailed = response["filesFailed"] ?? [];
@@ -13098,6 +14534,11 @@ function createAcpSessionBridge(opts) {
13098
14534
  closingChannel,
13099
14535
  `force kill closing session ${JSON.stringify(sessionId)}`
13100
14536
  );
14537
+ await entry.attachments.close().catch((releaseError) => {
14538
+ writeStderrLine(
14539
+ `qwen serve: failed to close attachments for killed session ${JSON.stringify(sessionId)}: ${releaseError instanceof Error ? releaseError.message : String(releaseError)}`
14540
+ );
14541
+ });
13101
14542
  return true;
13102
14543
  }
13103
14544
  entry.closing = true;
@@ -13161,6 +14602,11 @@ function createAcpSessionBridge(opts) {
13161
14602
  } catch {
13162
14603
  }
13163
14604
  entry.events.close();
14605
+ await entry.attachments.close().catch((error) => {
14606
+ writeStderrLine(
14607
+ `qwen serve: failed to close attachments for killed session ${JSON.stringify(sessionId)}: ${error instanceof Error ? error.message : String(error)}`
14608
+ );
14609
+ });
13164
14610
  if (ci && hasNoChannelWork(ci)) {
13165
14611
  await reapPendingEmptyChannel(ci);
13166
14612
  if (!ci.isDying) {
@@ -13265,6 +14711,7 @@ function createAcpSessionBridge(opts) {
13265
14711
  ) : Promise.resolve();
13266
14712
  const teardownResults = await Promise.allSettled([
13267
14713
  ...channels.map((ci) => ci.channel.kill()),
14714
+ ...[...byId.values()].map((entry) => entry.attachments.close()),
13268
14715
  ...inFlightSessionAwaits,
13269
14716
  ...inFlightRestoreAwaits,
13270
14717
  inFlightChannelAwait