@qwen-code/qwen-code 0.21.14 → 0.21.15

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 (318) hide show
  1. package/bundled/qc-helper/docs/features/channels/dingtalk.md +1 -1
  2. package/bundled/qc-helper/docs/features/code-review.md +20 -2
  3. package/bundled/qc-helper/docs/qwen-serve.md +1 -1
  4. package/bundled/review/SKILL.md +66 -36
  5. package/chunks/{MaxSizedBox-67IDQILY.js → MaxSizedBox-E7CUU3UN.js} +43 -43
  6. package/chunks/{StandaloneSessionPicker-G4QJIRMH.js → StandaloneSessionPicker-XYUB75AQ.js} +63 -63
  7. package/chunks/{acp-startup-profiler-HUYGO57H.js → acp-startup-profiler-DDVSJKJK.js} +2 -2
  8. package/chunks/{acpAgent-BS3YYO33.js → acpAgent-5QZS4VMN.js} +507 -176
  9. package/chunks/{agent-EHHEXKWN.js → agent-242BTA6Q.js} +40 -40
  10. package/chunks/{agent-headless-UT2ZF76T.js → agent-headless-4BF7W4TJ.js} +40 -40
  11. package/chunks/{anthropicContentGenerator-M7XRJMSK.js → anthropicContentGenerator-CU3G53RX.js} +7 -7
  12. package/chunks/{artifact-tool-GEXTOPVO.js → artifact-tool-LIUSI4XK.js} +3 -3
  13. package/chunks/{askUserQuestion-B33XRSB7.js → askUserQuestion-454X3UIV.js} +3 -3
  14. package/chunks/{bridge-WQ4IL2RF.js → bridge-MG4F5NOH.js} +46 -45
  15. package/chunks/{channel-management-service-X6LCQUAD.js → channel-management-service-KHPAWCX7.js} +1 -1
  16. package/chunks/{channel-settings-store-ZDFXY7VY.js → channel-settings-store-GQUCL2YM.js} +47 -47
  17. package/chunks/{channel-worker-group-TJ35JDPE.js → channel-worker-group-IN2UPYHP.js} +6 -5
  18. package/chunks/{channel-worker-manager-G5LKWGOG.js → channel-worker-manager-4CQPHVD4.js} +6 -5
  19. package/chunks/{channel-worker-supervisor-OEVWBHGT.js → channel-worker-supervisor-CPPSL7ZH.js} +4 -3
  20. package/chunks/{chunk-3EZLLVDF.js → chunk-263JC66E.js} +3 -3
  21. package/chunks/{chunk-OO4FKHLO.js → chunk-2GM44E7D.js} +10 -298
  22. package/chunks/{chunk-LSO6EL4Y.js → chunk-2MDUS4XE.js} +2 -2
  23. package/chunks/{chunk-WGHHA7ZH.js → chunk-3C55EUJX.js} +1 -1
  24. package/chunks/{chunk-47L5MLB5.js → chunk-3EAGKRLN.js} +5 -5
  25. package/chunks/{chunk-WPVUOVYX.js → chunk-3EQHT3IC.js} +2 -2
  26. package/chunks/{chunk-OEZCXK7Y.js → chunk-3JZ2AZC6.js} +4 -4
  27. package/chunks/{chunk-PCETXVVV.js → chunk-3X6KVX37.js} +1 -1
  28. package/chunks/{chunk-J63PKMYA.js → chunk-435GZDTF.js} +2 -2
  29. package/chunks/{chunk-I3N2Z25H.js → chunk-47VTGCYK.js} +3 -3
  30. package/chunks/{chunk-3PWNVF6N.js → chunk-4APB4SP2.js} +1 -1
  31. package/chunks/{chunk-MA2HEDVP.js → chunk-4CKU5K2R.js} +1 -1
  32. package/chunks/{chunk-OJB5FYVK.js → chunk-4IOBHNV2.js} +1 -1
  33. package/chunks/{chunk-MIPFDQAF.js → chunk-4W4BROUS.js} +1 -1
  34. package/chunks/{chunk-ZIGC3MUS.js → chunk-55P2ESI6.js} +1 -1
  35. package/chunks/{chunk-CHHABXPE.js → chunk-5AAW6E3C.js} +1 -1
  36. package/chunks/{chunk-J6YS7Z25.js → chunk-5I2U4PDY.js} +12 -12
  37. package/chunks/{chunk-NWNEANAT.js → chunk-5M7YKTYX.js} +6 -6
  38. package/chunks/{chunk-RXHBTKMG.js → chunk-5MA4E4EG.js} +3 -3
  39. package/chunks/{chunk-CEXU72GE.js → chunk-5NAJDTVP.js} +3 -3
  40. package/chunks/{chunk-U657UZJN.js → chunk-5WEMSDPR.js} +5 -5
  41. package/chunks/{chunk-73JUK3ZW.js → chunk-63AIQ2IZ.js} +96 -15
  42. package/chunks/{chunk-E5WQMF3K.js → chunk-67L6PVD6.js} +2 -2
  43. package/chunks/{chunk-CZK622ZK.js → chunk-6IFWBM24.js} +3 -3
  44. package/chunks/{chunk-CPHEPGAO.js → chunk-722JBVVW.js} +1 -1
  45. package/chunks/chunk-7NLDU6J2.js +367 -0
  46. package/chunks/{chunk-AKBOLMUF.js → chunk-7S22SX2L.js} +2 -2
  47. package/chunks/{chunk-SCZU65AI.js → chunk-7VRNXKKK.js} +8 -8
  48. package/chunks/{chunk-XUJNK7Y6.js → chunk-7WVB4FMG.js} +1 -1
  49. package/chunks/{chunk-55VKMB7X.js → chunk-A7MIUCUQ.js} +6 -6
  50. package/chunks/{chunk-NVRMCWTP.js → chunk-A7NBZBGQ.js} +3 -3
  51. package/chunks/{chunk-N6RSOOWJ.js → chunk-AAG7I5CR.js} +7 -7
  52. package/chunks/{chunk-EOPBFLA5.js → chunk-AKN26LMT.js} +3 -3
  53. package/chunks/{chunk-UC67T4SF.js → chunk-AQFWV4YF.js} +2 -1
  54. package/chunks/{chunk-DVR7XKO5.js → chunk-B5LEKTE7.js} +2 -2
  55. package/chunks/{chunk-V6WEMZPD.js → chunk-B72KNVKO.js} +3 -3
  56. package/chunks/{chunk-544KT7X4.js → chunk-BBMVUTXO.js} +1 -1
  57. package/chunks/{chunk-DHUPMYMC.js → chunk-BORX4PBQ.js} +12 -12
  58. package/chunks/{chunk-5IJSFWQO.js → chunk-BUZESYJT.js} +4 -4
  59. package/chunks/{chunk-Y4RVDOXV.js → chunk-DF7KFKCL.js} +4 -4
  60. package/chunks/{chunk-K2Y45XXS.js → chunk-DFOKBPH5.js} +108 -26
  61. package/chunks/{chunk-3SL2P3WX.js → chunk-DIHJXVT4.js} +1 -1
  62. package/chunks/{chunk-WAO53ZQF.js → chunk-DITDNF3D.js} +22 -2
  63. package/chunks/{chunk-2A6VQJYK.js → chunk-DK4HL3IB.js} +7 -7
  64. package/chunks/{chunk-6BJSYAML.js → chunk-DPXZUGDQ.js} +46 -21
  65. package/chunks/{chunk-MVHJZ3FZ.js → chunk-DZNQHKZZ.js} +2 -2
  66. package/chunks/{chunk-SNQZZ765.js → chunk-EB27VF5Z.js} +2 -2
  67. package/chunks/{chunk-KC4WMLZO.js → chunk-EXY6APEA.js} +81 -50
  68. package/chunks/{chunk-7VNHO4MN.js → chunk-F3FRTY4Q.js} +16 -7
  69. package/chunks/{chunk-FS5NGKWC.js → chunk-F5JZDC4O.js} +3 -3
  70. package/chunks/{chunk-6QFDQMQX.js → chunk-F7ZK5VMA.js} +1 -1
  71. package/chunks/{chunk-2B2BF7P7.js → chunk-FAIJ5UIU.js} +3 -3
  72. package/chunks/{chunk-G6KI5UOJ.js → chunk-FQ6G527Y.js} +3 -3
  73. package/chunks/{chunk-WSSH2BNO.js → chunk-FQZBD3PY.js} +1 -1
  74. package/chunks/{chunk-OVY67CYC.js → chunk-G32ESUUC.js} +1 -1
  75. package/chunks/{chunk-OBJYN4MX.js → chunk-G64PEZZ5.js} +2 -2
  76. package/chunks/{chunk-QRVHGEVY.js → chunk-GCWILQKA.js} +3 -3
  77. package/chunks/{chunk-QUGAUQGJ.js → chunk-GX2THELY.js} +1 -0
  78. package/chunks/{chunk-L3T4V5GZ.js → chunk-GYVB4P5B.js} +721 -331
  79. package/chunks/{chunk-FAAUPSY2.js → chunk-HEMFCAHG.js} +1 -1
  80. package/chunks/{chunk-AZQ3DT76.js → chunk-HGGMEDDY.js} +2 -2
  81. package/chunks/{chunk-SG7ZP5PG.js → chunk-HHNSO2Y4.js} +4 -4
  82. package/chunks/{chunk-YHXQK4SR.js → chunk-HSCPOEBL.js} +3 -3
  83. package/chunks/{chunk-H3SHU2XU.js → chunk-I3AB5DOZ.js} +3 -3
  84. package/chunks/{chunk-SFJMFZT5.js → chunk-IBUXQJHH.js} +1 -1
  85. package/chunks/{chunk-FN6M5WUL.js → chunk-IFOR6I5C.js} +4 -4
  86. package/chunks/{chunk-PHN4R2ZV.js → chunk-IU2XWUSI.js} +1 -1
  87. package/chunks/{chunk-OJOP26ET.js → chunk-J2LZ5OCH.js} +3 -3
  88. package/chunks/{chunk-4ZMS3JCR.js → chunk-JBSW7JA6.js} +83 -4
  89. package/chunks/{chunk-QAVTWAHL.js → chunk-JIR6JDGE.js} +3 -3
  90. package/chunks/{chunk-UGK4K2N2.js → chunk-JS73NNWF.js} +8 -0
  91. package/chunks/{chunk-2M55OBEB.js → chunk-JV443R5X.js} +1 -1
  92. package/chunks/{chunk-QEHH23OM.js → chunk-K6GQS2WN.js} +1 -1
  93. package/chunks/{chunk-6LAPGPVA.js → chunk-KCUGOZPL.js} +2 -2
  94. package/chunks/{chunk-KV27IEHM.js → chunk-L5JHSPET.js} +1 -1
  95. package/chunks/{chunk-OFTPCOGR.js → chunk-L7RG5PVT.js} +2902 -1393
  96. package/chunks/{chunk-TSMPNGBC.js → chunk-LBQBFRFN.js} +2 -2
  97. package/chunks/chunk-LJZSMWOH.js +18 -0
  98. package/chunks/{chunk-IA2K2HJE.js → chunk-M53UJS6H.js} +2 -2
  99. package/chunks/{chunk-KBXKR5R2.js → chunk-M5AIO24H.js} +1 -1
  100. package/chunks/{chunk-ROIYNTHJ.js → chunk-MD7XDTW7.js} +3 -3
  101. package/chunks/{chunk-3PWCMXIR.js → chunk-MGEWDLRO.js} +1 -1
  102. package/chunks/{chunk-4ZS3BLER.js → chunk-NAGJPIPW.js} +22 -7
  103. package/chunks/{chunk-3YRVPHT3.js → chunk-NCTHX2Z3.js} +14 -14
  104. package/chunks/{chunk-YP5KH5Y7.js → chunk-ND7NOI4P.js} +1 -1
  105. package/chunks/{chunk-4W5U2TUT.js → chunk-NGY7B7VF.js} +1 -1
  106. package/chunks/{chunk-S7N3HUIB.js → chunk-NMSSH43P.js} +6 -6
  107. package/chunks/{chunk-UYDGFWIJ.js → chunk-NYN4RD3K.js} +66 -7
  108. package/chunks/{chunk-DGJ7O55J.js → chunk-O327G3QW.js} +3 -3
  109. package/chunks/{chunk-2C6HJMG3.js → chunk-O75QDGQF.js} +3 -3
  110. package/chunks/{chunk-SFTKAX4O.js → chunk-OFJ6OFIT.js} +1 -1
  111. package/chunks/{chunk-6HY6IF3Z.js → chunk-OUYRA764.js} +1 -1
  112. package/chunks/{chunk-OEYAK5O6.js → chunk-OVS33JJM.js} +5 -5
  113. package/chunks/{chunk-VFP5YOHJ.js → chunk-PRIX4D6T.js} +6 -6
  114. package/chunks/{chunk-H4A72DE5.js → chunk-PTMVPFCZ.js} +1 -1
  115. package/chunks/{chunk-W7EYJGN7.js → chunk-PWQPWISR.js} +8 -8
  116. package/chunks/{chunk-QFJ5JHQR.js → chunk-PXOIRYE5.js} +1 -1
  117. package/chunks/{chunk-4SR45OM4.js → chunk-Q5QDZXKL.js} +1 -1
  118. package/chunks/{chunk-YECDICIO.js → chunk-Q6ETBOWF.js} +1 -1
  119. package/chunks/{chunk-WP4WIUOJ.js → chunk-QQUVPMMZ.js} +2 -2
  120. package/chunks/{chunk-C3T6M24E.js → chunk-RLYXWNS3.js} +3 -3
  121. package/chunks/{chunk-XT4RKHVE.js → chunk-RZ4PE3ER.js} +512 -24
  122. package/chunks/{chunk-6RDZLBSJ.js → chunk-SGKUVDBX.js} +80 -44
  123. package/chunks/{chunk-YZHRN3CF.js → chunk-SWOKDPS5.js} +2 -2
  124. package/chunks/{chunk-SLILKYV2.js → chunk-T2LUUKRI.js} +3 -3
  125. package/chunks/{chunk-5CVO7YES.js → chunk-U4GL4WDY.js} +1 -1
  126. package/chunks/{chunk-QEJZUXSK.js → chunk-U53XZHAE.js} +1 -1
  127. package/chunks/{chunk-XYRBK56E.js → chunk-U7U3CX4K.js} +3353 -1306
  128. package/chunks/{chunk-HIBNSEJE.js → chunk-UDG5EZJI.js} +0 -2
  129. package/chunks/{chunk-BQWNIPTE.js → chunk-V4UBYT3Y.js} +1 -1
  130. package/chunks/{chunk-XITLNXLX.js → chunk-VWS7DWY6.js} +3 -3
  131. package/chunks/{chunk-PT4I7NBA.js → chunk-VX7TFATF.js} +1 -1
  132. package/chunks/{chunk-VTOLYFWM.js → chunk-W3M6P3UR.js} +2 -2
  133. package/chunks/{chunk-CH5OFRV7.js → chunk-W4MLKRZD.js} +3 -3
  134. package/chunks/chunk-W5YD3QRT.js +367 -0
  135. package/chunks/{chunk-5IYS4WWR.js → chunk-WAPL2PHU.js} +1 -1
  136. package/chunks/{chunk-NRPXRNN4.js → chunk-WXFFCRKG.js} +1 -1
  137. package/chunks/{chunk-E3DYKPYZ.js → chunk-X7EQHW5L.js} +3 -3
  138. package/chunks/{chunk-BUQDLC2G.js → chunk-XFRYCCVC.js} +3 -3
  139. package/chunks/{chunk-DRAONPUW.js → chunk-XZ3J6E5Z.js} +2 -2
  140. package/chunks/{chunk-P3AA6S7O.js → chunk-Y7JGWFTK.js} +1 -1
  141. package/chunks/{chunk-SXCOEJO4.js → chunk-YLFFVY3G.js} +1 -1
  142. package/chunks/{chunk-YZOSNW4R.js → chunk-YO7PNVBJ.js} +1 -1
  143. package/chunks/{chunk-MDIGJH5X.js → chunk-ZL3HKVEQ.js} +4 -4
  144. package/chunks/{chunk-Y7QY6DKW.js → chunk-ZTLSYXFY.js} +4 -4
  145. package/chunks/{chunk-RWDNJBWN.js → chunk-ZUISK67X.js} +2 -2
  146. package/chunks/{chunk-PJSD2FOR.js → chunk-ZVJ5R5LM.js} +33 -33
  147. package/chunks/{chunk-XLLKYULU.js → chunk-ZWWU6KNS.js} +2 -2
  148. package/chunks/{computer-use-YORZJQE5.js → computer-use-34X5XJMV.js} +40 -40
  149. package/chunks/{config-utils-NJQA5YLJ.js → config-utils-XJTGZVY3.js} +2 -2
  150. package/chunks/{contextCommand-PACHGIF4.js → contextCommand-Q54VMGPA.js} +42 -42
  151. package/chunks/{core-runtime-6HD5DSPX.js → core-runtime-TIWTFHOR.js} +45 -44
  152. package/chunks/{create-sub-session-IQFIPUQA.js → create-sub-session-DPEBT657.js} +40 -40
  153. package/chunks/{create-sub-session-KRQULJE6.js → create-sub-session-TCKWBXEM.js} +6 -4
  154. package/chunks/{cron-create-NFRZEOQX.js → cron-create-PUCRMWQ6.js} +5 -5
  155. package/chunks/{cron-delete-GFVSVO7C.js → cron-delete-EOQR5CXU.js} +5 -5
  156. package/chunks/{cron-list-NRW34CLU.js → cron-list-7O22SQBF.js} +5 -5
  157. package/chunks/{daemon-XH76IXOC.js → daemon-JE2UIWSY.js} +304 -160
  158. package/chunks/{daemon-git-worktree-guard-6P27GKSU.js → daemon-git-worktree-guard-XSZ3362V.js} +40 -40
  159. package/chunks/daemon-status-provider-JBEST4SF.js +104 -0
  160. package/chunks/{daemon-trust-policy-5QJHAS3N.js → daemon-trust-policy-S6LBG4WN.js} +46 -46
  161. package/chunks/{daemon-trust-policy-monitor-JA2WZRZ3.js → daemon-trust-policy-monitor-NFO3OIBN.js} +46 -46
  162. package/chunks/{deferred-core-runtime-R2VWOVG3.js → deferred-core-runtime-Z2TLD2L7.js} +40 -40
  163. package/chunks/{display-image-ON3EYFRD.js → display-image-6FCICTXS.js} +5 -5
  164. package/chunks/{dist-IYYN3RUT.js → dist-IELOWDOT.js} +81 -17
  165. package/chunks/{earlyInputCapture-PQEEOFKJ.js → earlyInputCapture-KCZDAQWY.js} +41 -41
  166. package/chunks/{edit-RASZHTU2.js → edit-3UADXGNC.js} +41 -41
  167. package/chunks/{enter-worktree-CH5OMHJE.js → enter-worktree-J66YDAJE.js} +40 -40
  168. package/chunks/{enterPlanMode-AQKR3CAH.js → enterPlanMode-YUZUKY5F.js} +40 -40
  169. package/chunks/{environment-7QRYIEBA.js → environment-6IEK2353.js} +42 -42
  170. package/chunks/{errors-P2LL5452.js → errors-RVLRX7XI.js} +42 -42
  171. package/chunks/{exit-worktree-WUDL26CY.js → exit-worktree-SNMPATZE.js} +40 -40
  172. package/chunks/{exitPlanMode-JM75SAUI.js → exitPlanMode-DP4TURUC.js} +40 -40
  173. package/chunks/{fast-path-KVNSOHVA.js → fast-path-5CC6RKES.js} +2 -2
  174. package/chunks/{gemini-ADMD2EOS.js → gemini-F64ZP4SG.js} +83 -82
  175. package/chunks/{geminiContentGenerator-CS3M7SOS.js → geminiContentGenerator-KAFSMFA6.js} +7 -7
  176. package/chunks/{getMachineId-bsd-FG7IUY6U.js → getMachineId-bsd-LSY4WMOF.js} +1 -1
  177. package/chunks/{getMachineId-darwin-GLCJI2RA.js → getMachineId-darwin-JOFR4UIT.js} +1 -1
  178. package/chunks/{getMachineId-linux-O6OPPAKO.js → getMachineId-linux-QEDBS6GI.js} +1 -1
  179. package/chunks/{getMachineId-unsupported-S6CKYVGC.js → getMachineId-unsupported-IPZ3YRKP.js} +1 -1
  180. package/chunks/{getMachineId-win-X5SRNEH7.js → getMachineId-win-TJ2OZ7AA.js} +1 -1
  181. package/chunks/{glob-WIJOGYVV.js → glob-OE27ZGK4.js} +40 -40
  182. package/chunks/{goal-tools-K5OHOJ6B.js → goal-tools-YIADM4RZ.js} +5 -5
  183. package/chunks/{grep-CIFRQBR7.js → grep-VBV6BWSP.js} +40 -40
  184. package/chunks/{handleAutoUpdate-KJ4SN7DM.js → handleAutoUpdate-YDMS57ZN.js} +46 -44
  185. package/chunks/{i18n-36SZUO4C.js → i18n-JIW77MSO.js} +41 -41
  186. package/chunks/{image-gen-R4B5VUYB.js → image-gen-L4A2ISEH.js} +11 -11
  187. package/chunks/{initializer-CW5YCAOL.js → initializer-F7I7QDHK.js} +47 -47
  188. package/chunks/{installationInfo-NMQIOUGY.js → installationInfo-KOUBU7MH.js} +43 -41
  189. package/chunks/{keychain-token-storage-3XFJ5ZB2.js → keychain-token-storage-7SL4ZWQQ.js} +3 -3
  190. package/chunks/{list-HHGT3S4P.js → list-AJ5BRB7C.js} +49 -49
  191. package/chunks/{list-agents-OSVDFI6S.js → list-agents-XO4EONWC.js} +6 -6
  192. package/chunks/{loadedSettingsAdapter-SJ2MJF35.js → loadedSettingsAdapter-X7FC5332.js} +46 -46
  193. package/chunks/{loggingContentGenerator-GR2QQ2C5.js → loggingContentGenerator-5G4ZR5D5.js} +22 -22
  194. package/chunks/{loop-wakeup-HWVUU5CF.js → loop-wakeup-HWSTFUTQ.js} +6 -6
  195. package/chunks/{ls-D3HUFHDP.js → ls-52HUW4B6.js} +5 -5
  196. package/chunks/{lsp-3QDDJTCS.js → lsp-VJESYEC6.js} +3 -3
  197. package/chunks/{managed-npm-update-ADQJHKTO.js → managed-npm-update-ZR2T2ST3.js} +41 -41
  198. package/chunks/{mcp-QW3OTL5W.js → mcp-TZNHG7CF.js} +46 -46
  199. package/chunks/{monitor-RJKPVERH.js → monitor-TN6Z4SXF.js} +40 -40
  200. package/chunks/{nonInteractiveCli-MROMHSTL.js → nonInteractiveCli-I7K6KH5U.js} +81 -80
  201. package/chunks/{notebook-edit-2ENWDBZO.js → notebook-edit-77V2IP3Y.js} +40 -40
  202. package/chunks/{openaiContentGenerator-Z3GPWJU4.js → openaiContentGenerator-2S2UT4RR.js} +25 -26
  203. package/chunks/{pidfile-5G42YSTP.js → pidfile-WDSDXBY3.js} +41 -41
  204. package/chunks/{processUtils-ZDMXYKQY.js → processUtils-JOJNZOW4.js} +2 -2
  205. package/chunks/prompt-terminal-ledger-RW62JIPP.js +93 -0
  206. package/chunks/{qwenContentGenerator-NLEMBZVU.js → qwenContentGenerator-G3H2TCXF.js} +45 -45
  207. package/chunks/{qwenOAuth2-EBV52DWJ.js → qwenOAuth2-UI2OC2RN.js} +10 -10
  208. package/chunks/{read-file-5BYIF53O.js → read-file-DI64TNOE.js} +13 -14
  209. package/chunks/{read-mcp-resource-XD54ZQMC.js → read-mcp-resource-6VFF47Z3.js} +3 -3
  210. package/chunks/{record-artifact-GLU3UWBV.js → record-artifact-TJTN2GBV.js} +5 -5
  211. package/chunks/{resumeHistoryUtils-AFTBRS3G.js → resumeHistoryUtils-3FIDQ6YQ.js} +46 -46
  212. package/chunks/{ripGrep-NN7AOLQY.js → ripGrep-EUTGRCN3.js} +40 -40
  213. package/chunks/{run-qwen-serve-UR3DYDAF.js → run-qwen-serve-SQBF7QZM.js} +505 -161
  214. package/chunks/{runtime-PR5RKGNH.js → runtime-7QGB6SY4.js} +49 -49
  215. package/chunks/{scheduler-GPMLVKJM.js → scheduler-V6AZCQVU.js} +42 -42
  216. package/chunks/{sdk-exporters-grpc-54L4LS5L.js → sdk-exporters-grpc-P3LA24AV.js} +3 -3
  217. package/chunks/{sdk-exporters-http-NC746HLT.js → sdk-exporters-http-K3LH2SD7.js} +9 -11
  218. package/chunks/{sdk-impl-YVL7BP6Y.js → sdk-impl-WCUNMLVX.js} +11 -6
  219. package/chunks/{send-message-TNBG5EVA.js → send-message-NUK5DTTI.js} +7 -7
  220. package/chunks/{serve-E7V2JVA2.js → serve-WVNAO2NA.js} +46 -46
  221. package/chunks/{server-YXB3DBN7.js → server-ZMZPHJEC.js} +992 -271
  222. package/chunks/{session-DZEAUT7S.js → session-JV74G6EZ.js} +82 -81
  223. package/chunks/{settings-ESZ3NWAP.js → settings-GBBVKBQ2.js} +45 -45
  224. package/chunks/{shell-JMSK5WIL.js → shell-JNNRUZPA.js} +40 -40
  225. package/chunks/{skill-5K5CSUJV.js → skill-JRGRCWD5.js} +18 -18
  226. package/chunks/{skill-settings-V64NIOTI.js → skill-settings-VEC7C44Z.js} +45 -45
  227. package/chunks/{spawnChannel-S63APBZZ.js → spawnChannel-TVKIFXXA.js} +42 -42
  228. package/chunks/{standalone-update-P42HDFXX.js → standalone-update-OSHF2CX3.js} +42 -42
  229. package/chunks/{startInteractiveUI-LFBLFHJ4.js → startInteractiveUI-2HTUHVQO.js} +310 -156
  230. package/chunks/{syntheticOutput-7GWFIDXT.js → syntheticOutput-GZV6O6UL.js} +4 -4
  231. package/chunks/{task-create-EILHJJB7.js → task-create-BXGKNND4.js} +11 -11
  232. package/chunks/{task-list-WGJR6YL5.js → task-list-4ZMIDYGD.js} +6 -6
  233. package/chunks/{task-stop-X37KYTKB.js → task-stop-WBO7LWTS.js} +3 -3
  234. package/chunks/{task-update-XZAHVXJ5.js → task-update-UFO3BI7O.js} +11 -11
  235. package/chunks/{team-create-EDNOUM2U.js → team-create-DTPGJ6RH.js} +40 -40
  236. package/chunks/{team-delete-UEP4QOOT.js → team-delete-OCFJS444.js} +6 -6
  237. package/chunks/{team-plan-approval-QRSNK2DX.js → team-plan-approval-4433RWHB.js} +40 -40
  238. package/chunks/{terminal-image-renderer-6J2NPL32.js → terminal-image-renderer-QZLIKIVA.js} +42 -42
  239. package/chunks/{theme-manager-WJMLRBXS.js → theme-manager-RM7C7Q3L.js} +41 -41
  240. package/chunks/{todoWrite-ATS62CAP.js → todoWrite-BGM5MCPY.js} +5 -5
  241. package/chunks/{tool-search-MZ6WU7DM.js → tool-search-4NQJASFC.js} +17 -18
  242. package/chunks/{total-session-admission-DQVW2LIP.js → total-session-admission-NZNCU5NU.js} +46 -45
  243. package/chunks/{trustedFolders-QMH25GOX.js → trustedFolders-FDJ2Q4ZK.js} +41 -41
  244. package/chunks/{update-relaunch-EG75LLW5.js → update-relaunch-HZMDPRTF.js} +5 -5
  245. package/chunks/{updateCheck-MTZIVGT5.js → updateCheck-ZB2CRVVG.js} +43 -43
  246. package/chunks/{useAutoAcceptIndicator-IGCW5EPO.js → useAutoAcceptIndicator-O3JI4KTR.js} +48 -48
  247. package/chunks/{validateNonInterActiveAuth-D5FPAB2C.js → validateNonInterActiveAuth-7Z4KG4N5.js} +78 -77
  248. package/chunks/{version-UOLDLLRB.js → version-RFVS6QDY.js} +1 -1
  249. package/chunks/{web-fetch-QKKW6CWH.js → web-fetch-RAN377BH.js} +17 -18
  250. package/chunks/{web-search-JYRBZMCW.js → web-search-XKS6ZPY7.js} +10 -10
  251. package/chunks/{workflow-SR7SEAU7.js → workflow-HF6G5XNX.js} +232 -73
  252. package/chunks/{workspace-providers-status-OXGV4EFF.js → workspace-providers-status-LNMP7NED.js} +49 -49
  253. package/chunks/{workspace-registration-store-V5EGKLBH.js → workspace-registration-store-QZ3P3ILN.js} +1 -1
  254. package/chunks/{workspace-registry-KJVIYTF5.js → workspace-registry-KWPGC2LG.js} +46 -45
  255. package/chunks/{workspace-service-6RLSGUB5.js → workspace-service-RWB4KMY3.js} +51 -51
  256. package/chunks/{workspace-skills-status-BHFEPDGH.js → workspace-skills-status-P3XXZY7C.js} +47 -47
  257. package/chunks/{workspace-trust-reconciler-DB5ERSUN.js → workspace-trust-reconciler-XU6WB2MF.js} +52 -51
  258. package/chunks/{write-file-IUP4GTZ4.js → write-file-V7Y6KUPR.js} +40 -40
  259. package/chunks/{zoom-image-SRBDTIVA.js → zoom-image-N4RSASH7.js} +13 -14
  260. package/cli.js +13 -13
  261. package/package.json +3 -3
  262. package/web-shell/assets/{arc-DItbX0DI.js → arc-CN-xeqBM.js} +1 -1
  263. package/web-shell/assets/{architectureDiagram-3BPJPVTR-CmnIlH8y.js → architectureDiagram-3BPJPVTR-XQJ6hBnv.js} +1 -1
  264. package/web-shell/assets/{blockDiagram-GPEHLZMM-pObnB9kw.js → blockDiagram-GPEHLZMM-n35AKogw.js} +1 -1
  265. package/web-shell/assets/{c4Diagram-AAUBKEIU-BNBmMS0Y.js → c4Diagram-AAUBKEIU-DXeDVokb.js} +1 -1
  266. package/web-shell/assets/channel-BdTBh3qf.js +1 -0
  267. package/web-shell/assets/{chunk-2J33WTMH-Dz4JBOO6.js → chunk-2J33WTMH-BYcmYjz_.js} +1 -1
  268. package/web-shell/assets/{chunk-4BX2VUAB-Cz2GnvAR.js → chunk-4BX2VUAB-w3UyeGtt.js} +1 -1
  269. package/web-shell/assets/{chunk-55IACEB6-CC6kOQYx.js → chunk-55IACEB6-Td3JbOH_.js} +1 -1
  270. package/web-shell/assets/{chunk-727SXJPM-Z4lY4gbh.js → chunk-727SXJPM-gC_NR7u6.js} +1 -1
  271. package/web-shell/assets/{chunk-AQP2D5EJ-Yz0hZr3D.js → chunk-AQP2D5EJ-aD7bI3c3.js} +1 -1
  272. package/web-shell/assets/{chunk-FMBD7UC4-nKmTWTv9.js → chunk-FMBD7UC4-sHMhD16X.js} +1 -1
  273. package/web-shell/assets/{chunk-ND2GUHAM-CjZz0f6Y.js → chunk-ND2GUHAM-DiV49BLl.js} +1 -1
  274. package/web-shell/assets/{chunk-QZHKN3VN-Bab3FpuS.js → chunk-QZHKN3VN-Dnoagqin.js} +1 -1
  275. package/web-shell/assets/classDiagram-4FO5ZUOK-qVxLMMuK.js +1 -0
  276. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-qVxLMMuK.js +1 -0
  277. package/web-shell/assets/{cose-bilkent-S5V4N54A-BuSAGFox.js → cose-bilkent-S5V4N54A-K6l_zsrZ.js} +1 -1
  278. package/web-shell/assets/{dagre-BM42HDAG-CRZXTg3g.js → dagre-BM42HDAG-Chh9hQ0a.js} +1 -1
  279. package/web-shell/assets/{diagram-2AECGRRQ-BFx1Ayrz.js → diagram-2AECGRRQ-Clrbd8SB.js} +1 -1
  280. package/web-shell/assets/{diagram-5GNKFQAL-B2hMZ3gS.js → diagram-5GNKFQAL-BkNExywD.js} +1 -1
  281. package/web-shell/assets/{diagram-KO2AKTUF-DKxAQLmc.js → diagram-KO2AKTUF-kTX14ngI.js} +1 -1
  282. package/web-shell/assets/{diagram-LMA3HP47-Bo1xGqVY.js → diagram-LMA3HP47-Ba1QOCJy.js} +1 -1
  283. package/web-shell/assets/{diagram-OG6HWLK6-B9NG78d-.js → diagram-OG6HWLK6-CtH5BVYm.js} +1 -1
  284. package/web-shell/assets/{erDiagram-TEJ5UH35-DltQKiqQ.js → erDiagram-TEJ5UH35-DCxSIC83.js} +1 -1
  285. package/web-shell/assets/{flowDiagram-I6XJVG4X-5xXGmS4I.js → flowDiagram-I6XJVG4X-C4rNAypQ.js} +1 -1
  286. package/web-shell/assets/{ganttDiagram-6RSMTGT7-bhGL5n0l.js → ganttDiagram-6RSMTGT7-s3P5tBwL.js} +1 -1
  287. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-ZYnz9hgh.js → gitGraphDiagram-PVQCEYII-C7Eg-1ok.js} +1 -1
  288. package/web-shell/assets/index-BXoIYIJg.css +5 -0
  289. package/web-shell/assets/{index-CBBeUArD.js → index-CszoC8cN.js} +1 -1
  290. package/web-shell/assets/index-Dxj7Naa0.js +1788 -0
  291. package/web-shell/assets/{infoDiagram-5YYISTIA-Cg-jamQf.js → infoDiagram-5YYISTIA-BDKsgMt6.js} +1 -1
  292. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-CcmvKlyq.js → ishikawaDiagram-YF4QCWOH-CkcSHbvL.js} +1 -1
  293. package/web-shell/assets/{journeyDiagram-JHISSGLW-BRsOQZp1.js → journeyDiagram-JHISSGLW-CDWt-t81.js} +1 -1
  294. package/web-shell/assets/{kanban-definition-UN3LZRKU-xW-dmEHK.js → kanban-definition-UN3LZRKU-FI2owcEa.js} +1 -1
  295. package/web-shell/assets/{linear-ClBfEZH2.js → linear-BQzLqkJi.js} +1 -1
  296. package/web-shell/assets/{mermaid.core-D65NDkzc.js → mermaid.core-CvV91DP1.js} +5 -5
  297. package/web-shell/assets/{mindmap-definition-RKZ34NQL-CyvFslYa.js → mindmap-definition-RKZ34NQL-Dx2kLUpN.js} +1 -1
  298. package/web-shell/assets/{pieDiagram-4H26LBE5-BwSefMyG.js → pieDiagram-4H26LBE5-CMXOmBrj.js} +1 -1
  299. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-GqBJspFS.js → quadrantDiagram-W4KKPZXB-Bziww7Co.js} +1 -1
  300. package/web-shell/assets/{requirementDiagram-4Y6WPE33-B137PlTJ.js → requirementDiagram-4Y6WPE33-DMOXAxzp.js} +1 -1
  301. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-maHkkt8i.js → sankeyDiagram-5OEKKPKP-B3y1cfVV.js} +1 -1
  302. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-DJ1mRaG3.js → sequenceDiagram-3UESZ5HK-ChrV1jiM.js} +1 -1
  303. package/web-shell/assets/{stateDiagram-AJRCARHV-aRmP7Q4i.js → stateDiagram-AJRCARHV-CWuLzLFE.js} +1 -1
  304. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-DEDPMLgq.js +1 -0
  305. package/web-shell/assets/{timeline-definition-PNZ67QCA-CIQ3BRPA.js → timeline-definition-PNZ67QCA-Cl6am_to.js} +1 -1
  306. package/web-shell/assets/{vennDiagram-CIIHVFJN-DR9VPd5h.js → vennDiagram-CIIHVFJN-OuocxGAu.js} +1 -1
  307. package/web-shell/assets/{wardley-L42UT6IY-ZcfIbT4D.js → wardley-L42UT6IY-DLYdvSJW.js} +1 -1
  308. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-D0jgPi4T.js → wardleyDiagram-YWT4CUSO-7a3DreXX.js} +1 -1
  309. package/web-shell/assets/{xychartDiagram-2RQKCTM6-BGWU4s5o.js → xychartDiagram-2RQKCTM6-CgfjUxW_.js} +1 -1
  310. package/web-shell/index.html +2 -2
  311. package/chunks/chunk-D34RDOLP.js +0 -529
  312. package/chunks/daemon-status-provider-SXS66ZIR.js +0 -103
  313. package/web-shell/assets/channel-hZnMfWA9.js +0 -1
  314. package/web-shell/assets/classDiagram-4FO5ZUOK-BRnxm8DX.js +0 -1
  315. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BRnxm8DX.js +0 -1
  316. package/web-shell/assets/index-CbgU-jUo.css +0 -5
  317. package/web-shell/assets/index-_nhzXqab.js +0 -1750
  318. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-Db39gvZ_.js +0 -1
@@ -19,7 +19,7 @@ import {
19
19
  } from "./chunk-OCPBI7J5.js";
20
20
  import {
21
21
  defaultSpawnChannelFactory
22
- } from "./chunk-BQWNIPTE.js";
22
+ } from "./chunk-V4UBYT3Y.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-UC67T4SF.js";
32
+ } from "./chunk-AQFWV4YF.js";
33
33
  import {
34
34
  ACTIVE_WORK_CLOSE_IF_UNHELD_PARAM,
35
35
  ACTIVE_WORK_CLOSE_TIMEOUT_MS,
@@ -44,8 +44,8 @@ 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
- DAEMON_MEDIA_REFERENCES_META_KEY,
49
49
  DAEMON_MODEL_PROMPT_META_KEY,
50
50
  DAEMON_PROMPT_DISPLAY_TEXT_META_KEY,
51
51
  LOAD_REPLAY_BULK_MODE,
@@ -65,7 +65,7 @@ import {
65
65
  clampActiveWorkIntervalMs,
66
66
  isValidTrustedModelPrompt,
67
67
  sessionCloseDrainBudgetMs
68
- } from "./chunk-WP4WIUOJ.js";
68
+ } from "./chunk-QQUVPMMZ.js";
69
69
  import {
70
70
  BranchWhilePromptActiveError,
71
71
  BridgeChannelQuarantinedError,
@@ -97,9 +97,8 @@ import {
97
97
  LIVE_TASK_TOOL_NAMES,
98
98
  MAX_LIVE_SCREEN_CONTEXT_TEXT_CHARS,
99
99
  MAX_LIVE_SPEAK_TO_USER_MESSAGE_CHARS,
100
- MAX_SUB_SESSION_NAME_CHARS,
101
- MAX_SUB_SESSION_PROMPT_CHARS
102
- } from "./chunk-HIBNSEJE.js";
100
+ MAX_SUB_SESSION_NAME_CHARS
101
+ } from "./chunk-UDG5EZJI.js";
103
102
  import {
104
103
  EXTERNAL_TOOL_GUARD_READY_META_KEY,
105
104
  EXTERNAL_TOOL_GUARD_REQUIRED_VALUE,
@@ -110,6 +109,9 @@ import {
110
109
  PROTOCOL_VERSION,
111
110
  RequestError
112
111
  } from "./chunk-IW6RQPQB.js";
112
+ import {
113
+ MAX_SUB_SESSION_PROMPT_CHARS
114
+ } from "./chunk-LJZSMWOH.js";
113
115
  import {
114
116
  SESSION_ARTIFACT_PERSISTENCE_VERSION,
115
117
  SESSION_TRANSCRIPT_MAX_LIMIT,
@@ -125,16 +127,19 @@ import {
125
127
  normalizeSnapshotPayload,
126
128
  normalizeTurnResultError,
127
129
  stableSessionArtifactId
128
- } from "./chunk-OFTPCOGR.js";
130
+ } from "./chunk-L7RG5PVT.js";
129
131
  import {
130
132
  DAEMON_TRACEPARENT_META_KEY,
131
133
  DAEMON_TRACESTATE_META_KEY
132
- } from "./chunk-OO4FKHLO.js";
134
+ } from "./chunk-7NLDU6J2.js";
133
135
  import {
134
136
  INVOCATION_CONTEXT_META_KEY,
135
137
  PRIVATE_ACP_CAPABILITY_ENV,
136
138
  PRIVATE_PARENT_CAPABILITY_META_KEY
137
- } from "./chunk-KC4WMLZO.js";
139
+ } from "./chunk-EXY6APEA.js";
140
+ import {
141
+ getSpecificMimeType
142
+ } from "./chunk-MGEWDLRO.js";
138
143
  import {
139
144
  init_esbuild_shims
140
145
  } from "./chunk-5O2XNYP6.js";
@@ -144,7 +149,7 @@ import {
144
149
 
145
150
  // packages/acp-bridge/src/bridge.ts
146
151
  init_esbuild_shims();
147
- import { randomBytes, randomUUID as randomUUID3 } from "node:crypto";
152
+ import { randomBytes, randomUUID as randomUUID2 } from "node:crypto";
148
153
  import * as path4 from "node:path";
149
154
  import { inspect } from "node:util";
150
155
 
@@ -1284,161 +1289,276 @@ function getChannelStartupProfileAttributes(response, receivedAtEpochMs, initial
1284
1289
  }
1285
1290
  __name(getChannelStartupProfileAttributes, "getChannelStartupProfileAttributes");
1286
1291
 
1287
- // packages/acp-bridge/src/sessionMedia.ts
1292
+ // packages/acp-bridge/src/sessionAttachments.ts
1288
1293
  init_esbuild_shims();
1289
- import { randomUUID } from "node:crypto";
1290
- import { promises as fs } from "node:fs";
1294
+ import { promises as fs, statSync } from "node:fs";
1291
1295
  import { tmpdir } from "node:os";
1292
1296
  import * as path from "node:path";
1293
- var SESSION_MEDIA_MAX_ITEM_BYTES = 8 * 1024 * 1024;
1294
- var SESSION_MEDIA_MAX_TOTAL_BYTES = 100 * 1024 * 1024;
1295
- var SESSION_MEDIA_MAX_ITEMS = 256;
1296
- var SESSION_MEDIA_UNAVAILABLE_TEXT = "[Attached media is no longer available]";
1297
- var SessionMediaReferenceError = class extends Error {
1297
+ var SESSION_ATTACHMENT_MAX_ITEM_BYTES = 8 * 1024 * 1024;
1298
+ var SESSION_ATTACHMENT_MAX_NAME_BYTES = 255;
1299
+ var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
1300
+ "image/bmp",
1301
+ "image/gif",
1302
+ "image/jpeg",
1303
+ "image/png",
1304
+ "image/webp"
1305
+ ]);
1306
+ var SESSION_ATTACHMENT_UNAVAILABLE_TEXT = "[Attachment is no longer available]";
1307
+ var SessionAttachmentReferenceError = class extends Error {
1298
1308
  constructor(message, code) {
1299
1309
  super(message);
1300
1310
  this.code = code;
1301
- this.name = "SessionMediaReferenceError";
1311
+ this.name = "SessionAttachmentReferenceError";
1302
1312
  }
1303
1313
  static {
1304
- __name(this, "SessionMediaReferenceError");
1314
+ __name(this, "SessionAttachmentReferenceError");
1305
1315
  }
1306
1316
  };
1307
- function isSessionMediaReference(value) {
1317
+ function isSessionAttachmentReference(value) {
1308
1318
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1309
1319
  const record = value;
1310
- return record["type"] === "image" && typeof record["mediaId"] === "string" && record["mediaId"].length > 0 && typeof record["mimeType"] === "string" && record["mimeType"].startsWith(`${record["type"]}/`) && typeof record["size"] === "number" && Number.isSafeInteger(record["size"]) && record["size"] > 0;
1320
+ 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);
1321
+ }
1322
+ __name(isSessionAttachmentReference, "isSessionAttachmentReference");
1323
+ function safeAttachmentName(name) {
1324
+ const safeName = path.basename(name.replaceAll("\\", "/")).trim();
1325
+ const isWindowsReserved = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(safeName);
1326
+ const hasInvalidCharacter = Array.from(safeName).some((character) => {
1327
+ const codePoint = character.codePointAt(0);
1328
+ return codePoint !== void 0 && (codePoint <= 31 || codePoint === 127 || codePoint >= 55296 && codePoint <= 57343);
1329
+ });
1330
+ return !safeName || safeName === "." || safeName === ".." || safeName.endsWith(".") || isWindowsReserved || /[<>:"|?*]/.test(safeName) || hasInvalidCharacter || Buffer.byteLength(safeName) > SESSION_ATTACHMENT_MAX_NAME_BYTES ? void 0 : safeName;
1331
+ }
1332
+ __name(safeAttachmentName, "safeAttachmentName");
1333
+ function truncateUtf8(value, maxBytes) {
1334
+ let bytes = 0;
1335
+ let result = "";
1336
+ for (const character of value) {
1337
+ const characterBytes = Buffer.byteLength(character);
1338
+ if (bytes + characterBytes > maxBytes) break;
1339
+ result += character;
1340
+ bytes += characterBytes;
1341
+ }
1342
+ return result;
1343
+ }
1344
+ __name(truncateUtf8, "truncateUtf8");
1345
+ function deduplicatedName(name, suffix) {
1346
+ if (suffix === 0) return name;
1347
+ const extension = path.extname(name);
1348
+ const suffixText = ` (${suffix})`;
1349
+ const stem = name.slice(0, -extension.length || void 0);
1350
+ const extensionBudget = SESSION_ATTACHMENT_MAX_NAME_BYTES - Buffer.byteLength(suffixText) - 1;
1351
+ const safeExtension = truncateUtf8(extension, extensionBudget).replace(
1352
+ /[. ]+$/u,
1353
+ ""
1354
+ );
1355
+ const stemBudget = SESSION_ATTACHMENT_MAX_NAME_BYTES - Buffer.byteLength(suffixText) - Buffer.byteLength(safeExtension);
1356
+ return `${truncateUtf8(stem, stemBudget)}${suffixText}${safeExtension}`;
1357
+ }
1358
+ __name(deduplicatedName, "deduplicatedName");
1359
+ function imageName(mimeType) {
1360
+ const extension = mimeType.slice("image/".length).split(/[;+]/, 1)[0];
1361
+ return `image.${extension === "jpg" ? "jpeg" : extension || "img"}`;
1362
+ }
1363
+ __name(imageName, "imageName");
1364
+ function mimeTypeForName(name) {
1365
+ if ([".ts", ".mts", ".cts", ".tsx"].includes(path.extname(name).toLowerCase())) {
1366
+ return "text/plain";
1367
+ }
1368
+ return getSpecificMimeType(name) ?? "application/octet-stream";
1369
+ }
1370
+ __name(mimeTypeForName, "mimeTypeForName");
1371
+ function isSupportedImageMimeType(mimeType) {
1372
+ return SUPPORTED_IMAGE_MIME_TYPES.has(mimeType);
1311
1373
  }
1312
- __name(isSessionMediaReference, "isSessionMediaReference");
1313
- function withMediaDegradationMarker(blocks) {
1374
+ __name(isSupportedImageMimeType, "isSupportedImageMimeType");
1375
+ function isTextMimeType(mimeType) {
1376
+ 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";
1377
+ }
1378
+ __name(isTextMimeType, "isTextMimeType");
1379
+ function isTextAttachment(data, mimeType) {
1380
+ if (isTextMimeType(mimeType)) return true;
1381
+ if (mimeType.startsWith("image/") || mimeType.startsWith("audio/") || mimeType.startsWith("video/") || mimeType.startsWith("font/") || mimeType === "application/pdf" || data.includes(0)) {
1382
+ return false;
1383
+ }
1384
+ return Buffer.from(data.toString("utf8"), "utf8").equals(data);
1385
+ }
1386
+ __name(isTextAttachment, "isTextAttachment");
1387
+ function withAttachmentDegradationMarker(blocks) {
1314
1388
  for (let i = blocks.length - 1; i >= 0; i--) {
1315
1389
  const block = blocks[i];
1316
1390
  if (block.type === "text") {
1317
- if (block.text.endsWith(SESSION_MEDIA_UNAVAILABLE_TEXT)) {
1391
+ if (block.text.endsWith(SESSION_ATTACHMENT_UNAVAILABLE_TEXT)) {
1318
1392
  return [...blocks];
1319
1393
  }
1320
1394
  const next = [...blocks];
1321
1395
  next[i] = {
1322
1396
  type: "text",
1323
1397
  text: `${block.text}
1324
- ${SESSION_MEDIA_UNAVAILABLE_TEXT}`
1398
+ ${SESSION_ATTACHMENT_UNAVAILABLE_TEXT}`
1325
1399
  };
1326
1400
  return next;
1327
1401
  }
1328
1402
  }
1329
1403
  return [
1330
1404
  ...blocks,
1331
- { type: "text", text: SESSION_MEDIA_UNAVAILABLE_TEXT }
1405
+ { type: "text", text: SESSION_ATTACHMENT_UNAVAILABLE_TEXT }
1332
1406
  ];
1333
1407
  }
1334
- __name(withMediaDegradationMarker, "withMediaDegradationMarker");
1335
- var SessionMediaStore = class {
1408
+ __name(withAttachmentDegradationMarker, "withAttachmentDegradationMarker");
1409
+ var SessionAttachmentStore = class {
1410
+ constructor(directoryRoot, sessionId) {
1411
+ this.directoryRoot = directoryRoot;
1412
+ if (!directoryRoot || !sessionId) return;
1413
+ this.persistentDirectory = path.join(
1414
+ directoryRoot,
1415
+ `session-${encodeURIComponent(sessionId)}`
1416
+ );
1417
+ }
1336
1418
  static {
1337
- __name(this, "SessionMediaStore");
1419
+ __name(this, "SessionAttachmentStore");
1338
1420
  }
1339
- records = /* @__PURE__ */ new Map();
1340
1421
  directoryPromise;
1341
- totalBytes = 0;
1422
+ persistentDirectory;
1423
+ activeDirectory;
1342
1424
  pendingItems = 0;
1425
+ pendingNames = /* @__PURE__ */ new Map();
1426
+ pendingDrainWaiters = [];
1427
+ copyDrainWaiters = [];
1428
+ copying = false;
1429
+ closing = false;
1343
1430
  closed = false;
1344
- async put(data, mimeType) {
1345
- if (this.closed) throw new Error("Session media store is closed");
1346
- const type = "image";
1347
- if (!mimeType.startsWith("image/")) {
1348
- throw new TypeError("Session media must be image/*");
1431
+ async putAttachment(data, mimeType, name) {
1432
+ const isImage = isSupportedImageMimeType(mimeType);
1433
+ const safeName = safeAttachmentName(
1434
+ name ?? (isImage ? imageName(mimeType) : "")
1435
+ );
1436
+ if (!safeName) {
1437
+ throw new TypeError("Session attachment name is invalid");
1349
1438
  }
1350
- if (data.byteLength === 0 || data.byteLength > SESSION_MEDIA_MAX_ITEM_BYTES) {
1351
- throw new RangeError(
1352
- `Session media must be between 1 and ${SESSION_MEDIA_MAX_ITEM_BYTES} bytes`
1353
- );
1439
+ const storedMimeType = mimeTypeForName(safeName);
1440
+ if (isImage && storedMimeType !== mimeType || isSupportedImageMimeType(storedMimeType) && !isImage) {
1441
+ throw new TypeError("Attachment name and Content-Type do not match");
1354
1442
  }
1355
- if (this.totalBytes + data.byteLength > SESSION_MEDIA_MAX_TOTAL_BYTES) {
1356
- throw new RangeError(
1357
- `Session media exceeds the ${SESSION_MEDIA_MAX_TOTAL_BYTES}-byte session limit`
1358
- );
1443
+ if (this.closed || this.closing) {
1444
+ throw new Error("Session attachment store is closed");
1359
1445
  }
1360
- if (this.records.size + this.pendingItems >= SESSION_MEDIA_MAX_ITEMS) {
1446
+ if (this.copying) throw new Error("Session attachments are being copied");
1447
+ if (isImage && data.byteLength === 0 || data.byteLength > SESSION_ATTACHMENT_MAX_ITEM_BYTES) {
1361
1448
  throw new RangeError(
1362
- `Session media exceeds the ${SESSION_MEDIA_MAX_ITEMS}-item session limit`
1449
+ `Session attachment must be at most ${SESSION_ATTACHMENT_MAX_ITEM_BYTES} bytes and images cannot be empty`
1363
1450
  );
1364
1451
  }
1365
- const mediaId = randomUUID();
1366
1452
  let filePath;
1367
- this.totalBytes += data.byteLength;
1453
+ let pendingName = safeName;
1454
+ let removeFileOnFailure = false;
1368
1455
  this.pendingItems += 1;
1456
+ this.reservePendingName(safeName);
1369
1457
  try {
1370
1458
  const directory = await this.directory();
1371
- filePath = path.join(directory, mediaId);
1372
- await fs.writeFile(filePath, data, { flag: "wx" });
1373
- if (this.closed) {
1374
- throw new Error("Session media store is closed");
1375
- }
1376
- const record = {
1377
- type,
1378
- mediaId,
1379
- mimeType,
1380
- size: data.byteLength,
1381
- filePath
1459
+ let suffix = 0;
1460
+ for (; ; ) {
1461
+ const candidateName = deduplicatedName(safeName, suffix);
1462
+ if (safeAttachmentName(candidateName) !== candidateName) {
1463
+ throw new TypeError("Session attachment name is invalid");
1464
+ }
1465
+ if (pendingName !== candidateName) {
1466
+ if (pendingName) this.releasePendingName(pendingName);
1467
+ this.reservePendingName(candidateName);
1468
+ pendingName = candidateName;
1469
+ }
1470
+ filePath = path.join(directory, candidateName);
1471
+ removeFileOnFailure = true;
1472
+ try {
1473
+ await fs.writeFile(filePath, data, { flag: "wx" });
1474
+ break;
1475
+ } catch (error) {
1476
+ if (error.code !== "EEXIST") throw error;
1477
+ removeFileOnFailure = false;
1478
+ this.releasePendingName(candidateName);
1479
+ pendingName = void 0;
1480
+ filePath = void 0;
1481
+ suffix += 1;
1482
+ }
1483
+ }
1484
+ if (this.closed || this.closing) {
1485
+ throw new Error("Session attachment store is closed");
1486
+ }
1487
+ const name2 = path.basename(filePath);
1488
+ const storedMimeType2 = mimeTypeForName(name2);
1489
+ const reference = {
1490
+ type: isSupportedImageMimeType(storedMimeType2) ? "image" : "resource",
1491
+ attachmentId: name2,
1492
+ mimeType: storedMimeType2,
1493
+ size: data.byteLength
1382
1494
  };
1383
- this.records.set(mediaId, record);
1384
- return { type, mediaId, mimeType, size: data.byteLength };
1495
+ return reference;
1385
1496
  } catch (error) {
1386
- if (filePath) await fs.rm(filePath, { force: true }).catch(() => {
1387
- });
1388
- if (!this.closed) this.totalBytes -= data.byteLength;
1497
+ if (removeFileOnFailure && filePath) {
1498
+ await fs.rm(filePath, { force: true }).catch(() => {
1499
+ });
1500
+ }
1389
1501
  throw error;
1390
1502
  } finally {
1391
- if (!this.closed) this.pendingItems -= 1;
1503
+ if (pendingName) this.releasePendingName(pendingName);
1504
+ if (!this.closed) {
1505
+ this.pendingItems -= 1;
1506
+ if (this.pendingItems === 0) {
1507
+ this.resolvePendingDrainWaiters();
1508
+ }
1509
+ }
1392
1510
  }
1393
1511
  }
1394
- // Validate one block against the store. Blocks without a `mediaId` (inline
1395
- // media, text) pass through untouched, matching `assertReferences`.
1512
+ // Validate one block against the store. Ordinary ACP content passes through
1513
+ // untouched, matching `assertReferences`.
1396
1514
  assertReference(block) {
1397
- if (!block || typeof block !== "object" || Array.isArray(block) || !("mediaId" in block)) {
1515
+ if (!block || typeof block !== "object" || Array.isArray(block) || !("attachmentId" in block)) {
1398
1516
  return;
1399
1517
  }
1400
- if (!isSessionMediaReference(block)) {
1401
- throw new SessionMediaReferenceError(
1402
- "Invalid session media reference",
1403
- "invalid_session_media_reference"
1518
+ if (!isSessionAttachmentReference(block)) {
1519
+ throw new SessionAttachmentReferenceError(
1520
+ "Invalid session attachment reference",
1521
+ "invalid_session_attachment_reference"
1404
1522
  );
1405
1523
  }
1406
1524
  this.assertStored(block);
1407
1525
  }
1408
1526
  assertReferences(content) {
1409
- const seenMediaIds = /* @__PURE__ */ new Set();
1527
+ const seenIds = /* @__PURE__ */ new Set();
1410
1528
  for (const block of content) {
1411
- if (!block || typeof block !== "object" || Array.isArray(block) || !("mediaId" in block)) {
1529
+ if (!block || typeof block !== "object" || Array.isArray(block) || !("attachmentId" in block)) {
1412
1530
  continue;
1413
1531
  }
1414
- if (!isSessionMediaReference(block)) {
1415
- throw new SessionMediaReferenceError(
1416
- "Invalid session media reference",
1417
- "invalid_session_media_reference"
1532
+ if (!isSessionAttachmentReference(block)) {
1533
+ throw new SessionAttachmentReferenceError(
1534
+ "Invalid session attachment reference",
1535
+ "invalid_session_attachment_reference"
1418
1536
  );
1419
1537
  }
1420
- if (seenMediaIds.has(block.mediaId)) {
1421
- throw new SessionMediaReferenceError(
1422
- `Session media referenced more than once: ${block.mediaId}`,
1423
- "invalid_session_media_reference"
1538
+ const id = block.attachmentId;
1539
+ if (seenIds.has(id)) {
1540
+ throw new SessionAttachmentReferenceError(
1541
+ `Session attachment referenced more than once: ${id}`,
1542
+ "invalid_session_attachment_reference"
1424
1543
  );
1425
1544
  }
1426
- seenMediaIds.add(block.mediaId);
1545
+ seenIds.add(id);
1427
1546
  this.assertStored(block);
1428
1547
  }
1429
1548
  }
1430
1549
  async resolveContent(content, memo) {
1431
- const pendingByMediaId = memo ?? /* @__PURE__ */ new Map();
1550
+ const pendingById = memo ?? /* @__PURE__ */ new Map();
1432
1551
  return await Promise.all(
1433
1552
  content.map(async (block) => {
1434
- if (!isSessionMediaReference(block)) return block;
1435
- let pending = pendingByMediaId.get(block.mediaId);
1553
+ if (!isSessionAttachmentReference(block)) return block;
1554
+ const id = block.attachmentId;
1555
+ let pending = pendingById.get(id);
1436
1556
  if (!pending) {
1437
1557
  const created = this.resolve(block);
1438
- pendingByMediaId.set(block.mediaId, created);
1558
+ pendingById.set(id, created);
1439
1559
  void created.catch(() => {
1440
- if (pendingByMediaId.get(block.mediaId) === created) {
1441
- pendingByMediaId.delete(block.mediaId);
1560
+ if (pendingById.get(id) === created) {
1561
+ pendingById.delete(id);
1442
1562
  }
1443
1563
  });
1444
1564
  pending = created;
@@ -1449,13 +1569,13 @@ var SessionMediaStore = class {
1449
1569
  }
1450
1570
  // Per-block variant of `resolveContent` for degrade paths: one unresolvable
1451
1571
  // reference drops only itself, keeping the sibling blocks a wholesale
1452
- // fallback would discard. Non-media errors still propagate.
1572
+ // fallback would discard. Other errors still propagate.
1453
1573
  async resolveContentDegrading(content, memo) {
1454
1574
  const retainedBlocks = [];
1455
1575
  const resolvedBlocks = [];
1456
1576
  let degraded = 0;
1457
1577
  for (const block of content) {
1458
- if (!isSessionMediaReference(block)) {
1578
+ if (!isSessionAttachmentReference(block)) {
1459
1579
  retainedBlocks.push(block);
1460
1580
  resolvedBlocks.push(block);
1461
1581
  continue;
@@ -1465,79 +1585,220 @@ var SessionMediaStore = class {
1465
1585
  if (resolved) resolvedBlocks.push(resolved);
1466
1586
  retainedBlocks.push(block);
1467
1587
  } catch (error) {
1468
- if (!(error instanceof SessionMediaReferenceError)) throw error;
1588
+ if (!(error instanceof SessionAttachmentReferenceError)) throw error;
1469
1589
  degraded += 1;
1470
1590
  }
1471
1591
  }
1472
1592
  return { retainedBlocks, resolvedBlocks, degraded };
1473
1593
  }
1474
- async read(mediaId) {
1475
- const record = this.records.get(mediaId);
1476
- if (!record) return void 0;
1594
+ async read(attachmentId) {
1595
+ const name = safeAttachmentName(attachmentId);
1596
+ if (!name || name !== attachmentId) return void 0;
1597
+ const filePath = path.join(await this.directory(), name);
1477
1598
  try {
1478
1599
  return {
1479
- data: await fs.readFile(record.filePath),
1480
- mimeType: record.mimeType
1600
+ data: await fs.readFile(filePath),
1601
+ mimeType: mimeTypeForName(name)
1481
1602
  };
1482
1603
  } catch (error) {
1483
1604
  if (error.code === "ENOENT") {
1484
- if (this.records.delete(mediaId)) this.totalBytes -= record.size;
1485
1605
  return void 0;
1486
1606
  }
1487
1607
  throw error;
1488
1608
  }
1489
1609
  }
1490
- async remove(mediaId) {
1491
- const record = this.records.get(mediaId);
1492
- if (!record) return false;
1493
- this.records.delete(mediaId);
1494
- this.totalBytes -= record.size;
1495
- await fs.rm(record.filePath, { force: true });
1496
- return true;
1610
+ async copyFrom(source) {
1611
+ if (source === this) return;
1612
+ if (this.closed || this.closing) {
1613
+ throw new Error("Session attachment store is closed");
1614
+ }
1615
+ if (this.copying) throw new Error("Session attachments are being copied");
1616
+ if (source.closed || source.closing) {
1617
+ throw new Error("Session attachment store is closed");
1618
+ }
1619
+ if (source.copying) {
1620
+ throw new Error("Session attachments are being copied");
1621
+ }
1622
+ this.copying = true;
1623
+ source.copying = true;
1624
+ try {
1625
+ if (this.pendingItems > 0) {
1626
+ await new Promise(
1627
+ (resolve2) => this.pendingDrainWaiters.push(resolve2)
1628
+ );
1629
+ }
1630
+ if (source.pendingItems > 0) {
1631
+ await new Promise(
1632
+ (resolve2) => source.pendingDrainWaiters.push(resolve2)
1633
+ );
1634
+ }
1635
+ if (this.closed) throw new Error("Session attachment store is closed");
1636
+ const sourceDirectory = source.persistentDirectory ?? source.activeDirectory;
1637
+ if (!sourceDirectory) return;
1638
+ let entries;
1639
+ try {
1640
+ entries = await fs.readdir(sourceDirectory, { withFileTypes: true });
1641
+ } catch (error) {
1642
+ if (error.code === "ENOENT") return;
1643
+ throw error;
1644
+ }
1645
+ const targetDirectory = await this.directory();
1646
+ await Promise.all(
1647
+ entries.filter(
1648
+ (entry) => entry.isFile() && !source.pendingNames.has(entry.name)
1649
+ ).map(async (entry) => {
1650
+ const sourcePath = path.join(sourceDirectory, entry.name);
1651
+ try {
1652
+ await fs.copyFile(
1653
+ sourcePath,
1654
+ path.join(targetDirectory, entry.name)
1655
+ );
1656
+ } catch (error) {
1657
+ if (error.code === "ENOENT") {
1658
+ try {
1659
+ await fs.stat(sourcePath);
1660
+ } catch (sourceError) {
1661
+ if (sourceError.code === "ENOENT") {
1662
+ return;
1663
+ }
1664
+ }
1665
+ }
1666
+ throw error;
1667
+ }
1668
+ })
1669
+ );
1670
+ } finally {
1671
+ source.copying = false;
1672
+ this.copying = false;
1673
+ source.resolveCopyDrainWaiters();
1674
+ this.resolveCopyDrainWaiters();
1675
+ }
1497
1676
  }
1498
- get sizeBytes() {
1499
- return this.totalBytes;
1677
+ async remove(attachmentId) {
1678
+ const name = safeAttachmentName(attachmentId);
1679
+ if (!name || name !== attachmentId || this.copying || this.pendingNames.has(name)) {
1680
+ return false;
1681
+ }
1682
+ const directory = await this.directory();
1683
+ const filePath = path.join(directory, name);
1684
+ try {
1685
+ await fs.unlink(filePath);
1686
+ return true;
1687
+ } catch (error) {
1688
+ if (error.code === "ENOENT") {
1689
+ return false;
1690
+ }
1691
+ throw error;
1692
+ }
1500
1693
  }
1501
1694
  async close() {
1695
+ this.closing = true;
1696
+ await this.waitForCopy();
1502
1697
  if (this.closed) return;
1503
1698
  this.closed = true;
1504
- this.records.clear();
1505
- this.totalBytes = 0;
1506
1699
  this.pendingItems = 0;
1507
- if (!this.directoryPromise) return;
1700
+ this.pendingNames.clear();
1701
+ this.resolvePendingDrainWaiters();
1702
+ if (this.persistentDirectory || !this.directoryPromise) return;
1508
1703
  const directory = await this.directoryPromise.catch(() => void 0);
1509
1704
  if (!directory) return;
1510
1705
  await fs.rm(directory, { recursive: true, force: true });
1511
1706
  }
1707
+ async delete() {
1708
+ this.closing = true;
1709
+ await this.waitForCopy();
1710
+ if (!this.closed) {
1711
+ this.closed = true;
1712
+ this.pendingItems = 0;
1713
+ this.pendingNames.clear();
1714
+ this.resolvePendingDrainWaiters();
1715
+ }
1716
+ const directory = this.persistentDirectory ?? await this.directoryPromise?.catch(() => void 0);
1717
+ if (directory) await fs.rm(directory, { recursive: true, force: true });
1718
+ }
1512
1719
  assertStored(reference) {
1513
- const stored = this.records.get(reference.mediaId);
1514
- if (!stored || stored.type !== reference.type || stored.mimeType !== reference.mimeType || stored.size !== reference.size) {
1515
- throw new SessionMediaReferenceError(
1516
- `Unknown or unavailable session media: ${reference.mediaId}`,
1517
- "session_media_gone"
1720
+ const id = reference.attachmentId;
1721
+ const name = safeAttachmentName(id);
1722
+ let size;
1723
+ const directory = this.persistentDirectory ?? this.activeDirectory;
1724
+ if (name && name === id && directory) {
1725
+ try {
1726
+ size = statSync(path.join(directory, name)).size;
1727
+ } catch {
1728
+ size = void 0;
1729
+ }
1730
+ }
1731
+ const storedMimeType = name ? mimeTypeForName(name) : void 0;
1732
+ const storedType = storedMimeType ? isSupportedImageMimeType(storedMimeType) ? "image" : "resource" : void 0;
1733
+ if (size !== reference.size || storedMimeType !== reference.mimeType || storedType !== reference.type) {
1734
+ throw new SessionAttachmentReferenceError(
1735
+ `Unknown or unavailable session attachment: ${id}`,
1736
+ "session_attachment_gone"
1518
1737
  );
1519
1738
  }
1520
1739
  }
1740
+ releasePendingName(name) {
1741
+ const count = this.pendingNames.get(name) ?? 0;
1742
+ if (count <= 1) this.pendingNames.delete(name);
1743
+ else this.pendingNames.set(name, count - 1);
1744
+ }
1745
+ reservePendingName(name) {
1746
+ this.pendingNames.set(name, (this.pendingNames.get(name) ?? 0) + 1);
1747
+ }
1748
+ async waitForCopy() {
1749
+ while (this.copying) {
1750
+ await new Promise((resolve2) => this.copyDrainWaiters.push(resolve2));
1751
+ }
1752
+ }
1753
+ resolveCopyDrainWaiters() {
1754
+ for (const resolve2 of this.copyDrainWaiters.splice(0)) resolve2();
1755
+ }
1756
+ resolvePendingDrainWaiters() {
1757
+ for (const resolve2 of this.pendingDrainWaiters.splice(0)) resolve2();
1758
+ }
1521
1759
  async resolve(reference) {
1522
- const media = await this.read(reference.mediaId);
1523
- if (!media || media.mimeType !== reference.mimeType) {
1524
- throw new SessionMediaReferenceError(
1525
- `Unknown or unavailable session media: ${reference.mediaId}`,
1526
- "session_media_gone"
1527
- );
1760
+ const id = reference.attachmentId;
1761
+ const attachment = await this.read(id);
1762
+ if (!attachment) {
1763
+ throw new SessionAttachmentReferenceError(
1764
+ `Unknown or unavailable session attachment: ${id}`,
1765
+ "session_attachment_gone"
1766
+ );
1767
+ }
1768
+ if (reference.type === "resource") {
1769
+ const resource = {
1770
+ uri: `attachment:///${encodeURIComponent(reference.attachmentId)}`,
1771
+ mimeType: attachment.mimeType,
1772
+ ...isTextAttachment(attachment.data, attachment.mimeType) ? { text: attachment.data.toString("utf8") } : { blob: attachment.data.toString("base64") }
1773
+ };
1774
+ return {
1775
+ type: "resource",
1776
+ resource
1777
+ };
1528
1778
  }
1529
1779
  return {
1530
- type: reference.type,
1531
- data: media.data.toString("base64"),
1532
- mimeType: media.mimeType
1780
+ type: "image",
1781
+ data: attachment.data.toString("base64"),
1782
+ mimeType: attachment.mimeType
1533
1783
  };
1534
1784
  }
1535
1785
  async directory() {
1536
1786
  if (!this.directoryPromise) {
1537
- const pending = fs.mkdtemp(path.join(tmpdir(), "qwen-session-media-"));
1538
- this.directoryPromise = pending;
1539
- void pending.catch(() => {
1540
- if (this.directoryPromise === pending)
1787
+ const pending = this.persistentDirectory ? fs.mkdir(this.persistentDirectory, {
1788
+ recursive: true,
1789
+ mode: 448
1790
+ }).then(() => this.persistentDirectory) : this.directoryRoot ? fs.mkdir(this.directoryRoot, { recursive: true, mode: 448 }).then(
1791
+ () => fs.mkdtemp(
1792
+ path.join(this.directoryRoot, "session-attachment-")
1793
+ )
1794
+ ) : fs.mkdtemp(path.join(tmpdir(), "qwen-session-attachment-"));
1795
+ const directoryPromise = pending.then((directory) => {
1796
+ this.activeDirectory = directory;
1797
+ return directory;
1798
+ });
1799
+ this.directoryPromise = directoryPromise;
1800
+ void directoryPromise.catch(() => {
1801
+ if (this.directoryPromise === directoryPromise)
1541
1802
  this.directoryPromise = void 0;
1542
1803
  });
1543
1804
  }
@@ -1555,7 +1816,7 @@ __name(writeStderrLine, "writeStderrLine");
1555
1816
 
1556
1817
  // packages/acp-bridge/src/bridgeClient.ts
1557
1818
  init_esbuild_shims();
1558
- import { randomUUID as randomUUID2 } from "node:crypto";
1819
+ import { randomUUID } from "node:crypto";
1559
1820
  import { promises as fs2 } from "node:fs";
1560
1821
  import * as path2 from "node:path";
1561
1822
 
@@ -2543,7 +2804,7 @@ function sliceLineRange(content, startLine, endLine) {
2543
2804
  }
2544
2805
  __name(sliceLineRange, "sliceLineRange");
2545
2806
  var BridgeClient = class {
2546
- constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender, ownsSession = () => true, onTokenUsage, onCreateSubSession, onGenerationEvent, onWorkspaceGenerationEvent, onChannelDelivery, hasSessionSpawnInFlight = () => false, getLiveScreenContextCaptureHandler = () => void 0, getLiveTaskToolRequestHandler = () => void 0, getLiveSpeakToUserHandler = () => void 0, externalToolGuard, onActiveWork, onSessionCatalogChanged) {
2807
+ 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) {
2547
2808
  this.resolveEntry = resolveEntry;
2548
2809
  this.resolvePendingRestoreEvents = resolvePendingRestoreEvents;
2549
2810
  this.mediator = mediator;
@@ -2566,6 +2827,7 @@ var BridgeClient = class {
2566
2827
  this.externalToolGuard = externalToolGuard;
2567
2828
  this.onActiveWork = onActiveWork;
2568
2829
  this.onSessionCatalogChanged = onSessionCatalogChanged;
2830
+ this.onGoalTurnEnded = onGoalTurnEnded;
2569
2831
  }
2570
2832
  static {
2571
2833
  __name(this, "BridgeClient");
@@ -2584,7 +2846,7 @@ var BridgeClient = class {
2584
2846
  options.map((o) => String(o.optionId ?? ""))
2585
2847
  );
2586
2848
  allowedOptionIds.delete("");
2587
- const requestId = randomUUID2();
2849
+ const requestId = randomUUID();
2588
2850
  if (allowedOptionIds.has(CANCEL_VOTE_SENTINEL)) {
2589
2851
  throw new CancelSentinelCollisionError(requestId, CANCEL_VOTE_SENTINEL);
2590
2852
  }
@@ -2883,38 +3145,41 @@ var BridgeClient = class {
2883
3145
  );
2884
3146
  }
2885
3147
  }
2886
- const mediaMemo = /* @__PURE__ */ new Map();
2887
- const serializedMediaIds = /* @__PURE__ */ new Set();
3148
+ const attachmentMemo = /* @__PURE__ */ new Map();
3149
+ const serializedAttachmentIds = /* @__PURE__ */ new Set();
2888
3150
  const items = [];
2889
3151
  try {
2890
3152
  for (const item of drained) {
2891
3153
  let degraded = 0;
2892
3154
  const planned = (item.content ?? []).filter((block) => {
2893
- if (!isSessionMediaReference(block)) return true;
2894
- if (serializedMediaIds.has(block.mediaId)) {
3155
+ if (!isSessionAttachmentReference(block)) return true;
3156
+ if (serializedAttachmentIds.has(block.attachmentId)) {
2895
3157
  degraded += 1;
2896
3158
  return false;
2897
3159
  }
2898
- serializedMediaIds.add(block.mediaId);
3160
+ serializedAttachmentIds.add(block.attachmentId);
2899
3161
  return true;
2900
3162
  });
2901
3163
  let resolvedBlocks;
2902
- let mediaReferences;
3164
+ let attachmentReferences;
2903
3165
  try {
2904
- resolvedBlocks = await entry.media.resolveContent(planned, mediaMemo);
2905
- mediaReferences = planned.filter(isSessionMediaReference);
3166
+ resolvedBlocks = await entry.attachments.resolveContent(
3167
+ planned,
3168
+ attachmentMemo
3169
+ );
3170
+ attachmentReferences = planned.filter(isSessionAttachmentReference);
2906
3171
  } catch (error) {
2907
- if (!(error instanceof SessionMediaReferenceError)) throw error;
3172
+ if (!(error instanceof SessionAttachmentReferenceError)) throw error;
2908
3173
  writeStderrLine(
2909
- `[mid-turn] session=${JSON.stringify(entry.sessionId)} degraded media for message ${JSON.stringify(item.messageId)}: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
3174
+ `[mid-turn] session=${JSON.stringify(entry.sessionId)} degraded attachment for message ${JSON.stringify(item.messageId)}: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
2910
3175
  );
2911
- const perBlock = await entry.media.resolveContentDegrading(
3176
+ const perBlock = await entry.attachments.resolveContentDegrading(
2912
3177
  planned,
2913
- mediaMemo
3178
+ attachmentMemo
2914
3179
  );
2915
3180
  resolvedBlocks = perBlock.resolvedBlocks;
2916
- mediaReferences = perBlock.retainedBlocks.filter(
2917
- isSessionMediaReference
3181
+ attachmentReferences = perBlock.retainedBlocks.filter(
3182
+ isSessionAttachmentReference
2918
3183
  );
2919
3184
  degraded += perBlock.degraded;
2920
3185
  }
@@ -2922,12 +3187,12 @@ var BridgeClient = class {
2922
3187
  ...item.text ? [{ type: "text", text: item.text }] : [],
2923
3188
  ...resolvedBlocks
2924
3189
  ];
2925
- if (degraded > 0) content = withMediaDegradationMarker(content);
3190
+ if (degraded > 0) content = withAttachmentDegradationMarker(content);
2926
3191
  items.push({
2927
3192
  messageId: item.messageId,
2928
3193
  displayText: item.text,
2929
3194
  content,
2930
- ...mediaReferences.length > 0 ? { mediaReferences } : {}
3195
+ ...attachmentReferences.length > 0 ? { attachmentReferences } : {}
2931
3196
  });
2932
3197
  }
2933
3198
  } catch (error) {
@@ -3356,7 +3621,7 @@ var BridgeClient = class {
3356
3621
  * `qwen/notify/session/prompt-suggestion` (followup assist),
3357
3622
  * `qwen/notify/session/artifact-event` (hook artifacts),
3358
3623
  * `qwen/notify/session/terminal-sequence`, and
3359
- * `_qwencode/end_turn` (background-notification turns), and
3624
+ * `_qwencode/end_turn` (background-notification and goal turns), and
3360
3625
  * `qwen/notify/session/mcp-budget-event` — each translated into a
3361
3626
  * session-scoped SSE frame. Unknown methods are dropped silently for
3362
3627
  * forward-compat.
@@ -3379,14 +3644,39 @@ var BridgeClient = class {
3379
3644
  }
3380
3645
  return;
3381
3646
  }
3647
+ if (method === "_qwencode/start_turn") {
3648
+ const sessionId2 = params["sessionId"];
3649
+ if (typeof sessionId2 !== "string" || sessionId2.length === 0 || params["source"] !== "goal") {
3650
+ return;
3651
+ }
3652
+ const entry = this.resolveEntry(sessionId2);
3653
+ if (!entry || !this.ownsSession(sessionId2)) return;
3654
+ entry.goalTurnActive = true;
3655
+ return;
3656
+ }
3382
3657
  if (method === "_qwencode/end_turn") {
3383
3658
  const sessionId2 = params["sessionId"];
3384
3659
  const reason = params["reason"];
3385
- if (typeof sessionId2 !== "string" || sessionId2.length === 0 || typeof reason !== "string" || reason.length === 0 || reason.length > 128 || params["source"] !== "background_notification") {
3660
+ const source = params["source"];
3661
+ if (typeof sessionId2 !== "string" || sessionId2.length === 0 || typeof reason !== "string" || reason.length === 0 || reason.length > 128 || source !== "background_notification" && source !== "goal") {
3386
3662
  return;
3387
3663
  }
3388
3664
  const entry = this.resolveEntry(sessionId2);
3389
3665
  if (!entry || !this.ownsSession(sessionId2)) return;
3666
+ if (source === "goal") {
3667
+ entry.goalTurnActive = false;
3668
+ this.onGoalTurnEnded?.(sessionId2);
3669
+ const promptId = params["promptId"];
3670
+ if (typeof promptId !== "string" || promptId.length === 0 || promptId.length > 256) {
3671
+ return;
3672
+ }
3673
+ entry.events.publish({
3674
+ type: "turn_complete",
3675
+ promptId,
3676
+ data: { sessionId: sessionId2, stopReason: reason, promptId }
3677
+ });
3678
+ return;
3679
+ }
3390
3680
  entry.events.publish({
3391
3681
  type: "background_notification_turn_complete",
3392
3682
  data: { sessionId: sessionId2, reason }
@@ -3947,7 +4237,7 @@ var BridgeClient = class {
3947
4237
  } catch {
3948
4238
  }
3949
4239
  }
3950
- const tmp = `${realTarget}.${process.pid}.${Date.now()}.${randomUUID2()}.tmp`;
4240
+ const tmp = `${realTarget}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
3951
4241
  let preserveMode;
3952
4242
  try {
3953
4243
  const targetStat = await fs2.stat(realTarget);
@@ -7276,6 +7566,56 @@ function rememberEnrichedTerminalTurnStatus(entry, promptId, status) {
7276
7566
  }
7277
7567
  }
7278
7568
  __name(rememberEnrichedTerminalTurnStatus, "rememberEnrichedTerminalTurnStatus");
7569
+ function advanceTurnActivity(entry) {
7570
+ const createdAtMs = Date.parse(entry.createdAt);
7571
+ const previous = entry.lastTurnEndedAtMs ?? (Number.isFinite(createdAtMs) ? createdAtMs : void 0);
7572
+ entry.lastTurnEndedAtMs = previous === void 0 ? Date.now() : Math.max(Date.now(), previous + 1);
7573
+ }
7574
+ __name(advanceTurnActivity, "advanceTurnActivity");
7575
+ function appendPromptLedgerBestEffort(entry, record) {
7576
+ const ledger = entry.promptLedger;
7577
+ if (!ledger) return;
7578
+ try {
7579
+ ledger.appendSync(entry.sessionId, record);
7580
+ } catch (error) {
7581
+ writeStderrLine(
7582
+ `qwen serve: prompt ledger append failed for session=${entry.sessionId} promptId=${record.promptId}: ${error instanceof Error ? error.message : String(error)}`
7583
+ );
7584
+ }
7585
+ }
7586
+ __name(appendPromptLedgerBestEffort, "appendPromptLedgerBestEffort");
7587
+ function promptLedgerTerminalRecord(pendingEntry, terminal) {
7588
+ const at = Date.now();
7589
+ if (terminal.kind === "complete") {
7590
+ if (terminal.result.stopReason === "cancelled") {
7591
+ return {
7592
+ v: 1,
7593
+ promptId: pendingEntry.promptId,
7594
+ terminal: "cancelled",
7595
+ at
7596
+ };
7597
+ }
7598
+ return {
7599
+ v: 1,
7600
+ promptId: pendingEntry.promptId,
7601
+ terminal: "completed",
7602
+ ...terminal.result.stopReason !== void 0 ? { stopReason: terminal.result.stopReason } : {},
7603
+ at
7604
+ };
7605
+ }
7606
+ if (terminal.kind === "cancelled") {
7607
+ return { v: 1, promptId: pendingEntry.promptId, terminal: "cancelled", at };
7608
+ }
7609
+ const normalized = normalizeTurnResultError(terminal.err);
7610
+ return {
7611
+ v: 1,
7612
+ promptId: pendingEntry.promptId,
7613
+ terminal: "error",
7614
+ ...normalized.code !== void 0 ? { code: normalized.code } : {},
7615
+ at
7616
+ };
7617
+ }
7618
+ __name(promptLedgerTerminalRecord, "promptLedgerTerminalRecord");
7279
7619
  function publishPromptTerminal(entry, pendingEntry, terminal) {
7280
7620
  if (pendingEntry.terminalPublished) {
7281
7621
  writeServeDebugLine(
@@ -7284,9 +7624,16 @@ function publishPromptTerminal(entry, pendingEntry, terminal) {
7284
7624
  return;
7285
7625
  }
7286
7626
  pendingEntry.terminalPublished = true;
7627
+ appendPromptLedgerBestEffort(
7628
+ entry,
7629
+ promptLedgerTerminalRecord(pendingEntry, terminal)
7630
+ );
7287
7631
  rememberTerminalTurnStatus(entry, pendingEntry, terminal);
7288
7632
  const originatorClientId = pendingEntry.originatorClientId;
7289
7633
  const mutateTurnState = pendingEntry.state === "running";
7634
+ if (mutateTurnState) {
7635
+ advanceTurnActivity(entry);
7636
+ }
7290
7637
  if (!mutateTurnState && entry.turnErrorEvent) {
7291
7638
  const journal = entry.events.liveJournalSnapshot() ?? [];
7292
7639
  if (journal.some(isRefreshAppendTurnContent)) {
@@ -7448,7 +7795,7 @@ function extractMediaBlocks(prompt) {
7448
7795
  const media = [];
7449
7796
  for (const block of prompt) {
7450
7797
  if (!block || typeof block !== "object") continue;
7451
- if (block.type === "image" || block.type === "audio") {
7798
+ if (block.type === "image" || block.type === "audio" || block.type === "resource" && "attachmentId" in block) {
7452
7799
  media.push(block);
7453
7800
  }
7454
7801
  }
@@ -7472,15 +7819,22 @@ var MAX_EMPTY_TRANSCRIPT_PAGES = 20;
7472
7819
  var SHELL_COMMAND_TIMEOUT_MS = 12e4;
7473
7820
  var MAX_SHELL_OUTPUT_FOR_HISTORY = 1e4;
7474
7821
  var MAX_MID_TURN_QUEUE_DEPTH = 20;
7475
- var inlineMediaBlockBytes = /* @__PURE__ */ __name((blocks) => {
7822
+ var MAX_QUEUED_INLINE_ATTACHMENT_BYTES = 100 * 1024 * 1024;
7823
+ function inlineAttachmentBlockBytes(blocks) {
7476
7824
  let total = 0;
7477
7825
  for (const block of blocks) {
7478
7826
  if (block.type === "image" && "data" in block) {
7479
- total += block.data.length;
7827
+ total += Buffer.byteLength(block.data);
7828
+ continue;
7480
7829
  }
7830
+ if (block.type !== "resource" || !("resource" in block)) continue;
7831
+ const resource = block.resource;
7832
+ if ("blob" in resource) total += Buffer.byteLength(resource.blob);
7833
+ else if ("text" in resource) total += Buffer.byteLength(resource.text);
7481
7834
  }
7482
7835
  return total;
7483
- }, "inlineMediaBlockBytes");
7836
+ }
7837
+ __name(inlineAttachmentBlockBytes, "inlineAttachmentBlockBytes");
7484
7838
  var DEFAULT_MAX_SESSIONS = 32;
7485
7839
  var DEFAULT_MAX_PENDING_PROMPTS_PER_SESSION = 5;
7486
7840
  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;
@@ -7489,8 +7843,6 @@ var DEFAULT_PERMISSION_TIMEOUT_MS = 5 * 60 * 1e3;
7489
7843
  var DEFAULT_MAX_PENDING_PER_SESSION = 64;
7490
7844
  var DEFAULT_SESSION_REAP_INTERVAL_MS = 6e4;
7491
7845
  var DEFAULT_SESSION_IDLE_TIMEOUT_MS = 30 * 6e4;
7492
- var MAX_RETAINED_SESSION_MEDIA_BYTES = 512 * 1024 * 1024;
7493
- var RETAINED_SESSION_MEDIA_TTL_MS = 3 * 60 * 6e4;
7494
7846
  function createAcpSessionBridge(opts) {
7495
7847
  let liveScreenContextCaptureHandler;
7496
7848
  let liveTaskToolRequestHandler;
@@ -7549,7 +7901,7 @@ function createAcpSessionBridge(opts) {
7549
7901
  );
7550
7902
  }
7551
7903
  }, "releaseFreshSessionReservation");
7552
- const sessionCatalogGeneration = randomUUID3();
7904
+ const sessionCatalogGeneration = randomUUID2();
7553
7905
  let sessionCatalogRevision = 0;
7554
7906
  const getSessionCatalogVersion = /* @__PURE__ */ __name(() => ({
7555
7907
  generation: sessionCatalogGeneration,
@@ -7652,7 +8004,6 @@ function createAcpSessionBridge(opts) {
7652
8004
  DEFAULT_SESSION_IDLE_TIMEOUT_MS
7653
8005
  );
7654
8006
  let sessionReaper;
7655
- let mediaSweeper;
7656
8007
  let lastActivityTimestamp = null;
7657
8008
  let activePromptCounter = 0;
7658
8009
  function touchActivity() {
@@ -7969,34 +8320,8 @@ function createAcpSessionBridge(opts) {
7969
8320
  }
7970
8321
  }
7971
8322
  __name(withWorkspaceControl, "withWorkspaceControl");
7972
- function sweepRetainedMedia() {
7973
- const now = Date.now();
7974
- for (const [id] of retainedMedia) {
7975
- if (byId.has(id) || inFlightRestores.has(id)) continue;
7976
- const detachedAt = retainedMediaDetachedAt.get(id);
7977
- if (detachedAt === void 0 || now - detachedAt < RETAINED_SESSION_MEDIA_TTL_MS) {
7978
- continue;
7979
- }
7980
- void releaseSessionMedia(id).catch((error) => {
7981
- writeStderrLine(
7982
- `qwen serve: failed to release retained media for ${JSON.stringify(id)}: ${error instanceof Error ? error.message : String(error)}`
7983
- );
7984
- });
7985
- }
7986
- }
7987
- __name(sweepRetainedMedia, "sweepRetainedMedia");
7988
8323
  function startSessionReaper() {
7989
- if (sessionReapIntervalMs <= 0) {
7990
- writeStderrLine(
7991
- `qwen serve: session reaper disabled; retained media sweep runs every ${DEFAULT_SESSION_REAP_INTERVAL_MS}ms`
7992
- );
7993
- mediaSweeper = setInterval(() => {
7994
- if (shuttingDown) return;
7995
- sweepRetainedMedia();
7996
- }, DEFAULT_SESSION_REAP_INTERVAL_MS);
7997
- mediaSweeper.unref();
7998
- return;
7999
- }
8324
+ if (sessionReapIntervalMs <= 0) return;
8000
8325
  writeStderrLine(
8001
8326
  `qwen serve: session reaper started (interval ${sessionReapIntervalMs}ms, idle threshold ${sessionIdleTimeoutMs}ms)`
8002
8327
  );
@@ -8017,7 +8342,6 @@ function createAcpSessionBridge(opts) {
8017
8342
  closeReason: "idle_timeout"
8018
8343
  });
8019
8344
  }
8020
- sweepRetainedMedia();
8021
8345
  }, sessionReapIntervalMs);
8022
8346
  sessionReaper.unref();
8023
8347
  }
@@ -8027,29 +8351,11 @@ function createAcpSessionBridge(opts) {
8027
8351
  clearInterval(sessionReaper);
8028
8352
  sessionReaper = void 0;
8029
8353
  }
8030
- if (mediaSweeper !== void 0) {
8031
- clearInterval(mediaSweeper);
8032
- mediaSweeper = void 0;
8033
- }
8034
8354
  }
8035
8355
  __name(stopSessionReaper, "stopSessionReaper");
8036
8356
  const aliveChannels = /* @__PURE__ */ new Set();
8037
8357
  let inFlightChannelSpawn;
8038
8358
  const byId = /* @__PURE__ */ new Map();
8039
- const retainedMedia = /* @__PURE__ */ new Map();
8040
- const retainedMediaDetachedAt = /* @__PURE__ */ new Map();
8041
- let mediaPutQueue = Promise.resolve();
8042
- const retainSessionMedia = /* @__PURE__ */ __name((sessionId, store = retainedMedia.get(sessionId) ?? new SessionMediaStore()) => {
8043
- retainedMedia.set(sessionId, store);
8044
- return store;
8045
- }, "retainSessionMedia");
8046
- const releaseSessionMedia = /* @__PURE__ */ __name(async (sessionId) => {
8047
- const store = retainedMedia.get(sessionId);
8048
- if (!store) return;
8049
- retainedMedia.delete(sessionId);
8050
- retainedMediaDetachedAt.delete(sessionId);
8051
- await store.close();
8052
- }, "releaseSessionMedia");
8053
8359
  const forwardRunningPromptCancel = /* @__PURE__ */ __name(async (entry, pending, notification) => {
8054
8360
  if (pending.cancelForwardInitial) {
8055
8361
  return pending.cancelForwardInitial;
@@ -8121,12 +8427,13 @@ function createAcpSessionBridge(opts) {
8121
8427
  sessionId: entry.sessionId,
8122
8428
  workspaceCwd: entry.workspaceCwd,
8123
8429
  createdAt: entry.createdAt,
8430
+ ...entry.lastTurnEndedAtMs !== void 0 ? { updatedAt: new Date(entry.lastTurnEndedAtMs).toISOString() } : {},
8124
8431
  displayName: entry.displayName,
8125
8432
  ...entry.parentSessionId ? { parentSessionId: entry.parentSessionId } : {},
8126
8433
  ...entry.sourceType ? { sourceType: entry.sourceType } : {},
8127
8434
  ...entry.sourceId !== void 0 ? { sourceId: entry.sourceId } : {},
8128
8435
  clientCount: entry.clientIds.size,
8129
- hasActivePrompt: entry.promptActive,
8436
+ hasActivePrompt: entry.promptActive || entry.goalTurnActive === true,
8130
8437
  isWaitingForPermission,
8131
8438
  isWaitingForUserQuestion,
8132
8439
  pendingInteractionCount: entry.pendingInteractions.size,
@@ -8189,7 +8496,7 @@ function createAcpSessionBridge(opts) {
8189
8496
  return limits;
8190
8497
  }, "journalSessionLimits");
8191
8498
  const unregisterJournalGrowthSessionLimits = journalGrowthPolicy !== void 0 && opts.registerJournalGrowthSessionLimits !== void 0 ? opts.registerJournalGrowthSessionLimits(journalSessionLimits) : void 0;
8192
- const createClientId = /* @__PURE__ */ __name(() => `client_${randomUUID3()}`, "createClientId");
8499
+ const createClientId = /* @__PURE__ */ __name(() => `client_${randomUUID2()}`, "createClientId");
8193
8500
  const registerClient = /* @__PURE__ */ __name((entry, requestedClientId) => {
8194
8501
  if (requestedClientId && entry.clientIds.has(requestedClientId)) {
8195
8502
  entry.clientIds.set(
@@ -8248,7 +8555,7 @@ function createAcpSessionBridge(opts) {
8248
8555
  if (inFlightChannelSpawn) return await inFlightChannelSpawn;
8249
8556
  const promise = (async () => {
8250
8557
  const privateParentCapability = randomBytes(32).toString("base64url");
8251
- const acpChannelId = randomUUID3();
8558
+ const acpChannelId = randomUUID2();
8252
8559
  const channel = await telemetry.withSpan(
8253
8560
  "channel.spawn",
8254
8561
  {
@@ -8372,7 +8679,10 @@ function createAcpSessionBridge(opts) {
8372
8679
  },
8373
8680
  // Child-side automatic title updates change persisted catalog
8374
8681
  // metadata the bridge never sees; forward the catalog-clock mark.
8375
- markSessionCatalogChanged
8682
+ markSessionCatalogChanged,
8683
+ // A Goal turn drains the mid-turn queue but owns no prompt slot, so
8684
+ // nothing else would settle what its last drain missed.
8685
+ settleMidTurnQueueAfterGoalTurn
8376
8686
  );
8377
8687
  const rawConnection = new ClientSideConnection(
8378
8688
  () => channel.transportGuard ? createLogSafeAcpClient(client, channel.transportGuard) : client,
@@ -8510,9 +8820,11 @@ function createAcpSessionBridge(opts) {
8510
8820
  touchActivity();
8511
8821
  }
8512
8822
  byId.delete(sid);
8513
- if (retainedMedia.has(sid)) {
8514
- retainedMediaDetachedAt.set(sid, Date.now());
8515
- }
8823
+ void sessEntry.attachments.close().catch((error) => {
8824
+ writeStderrLine(
8825
+ `qwen serve: failed to close attachments for closed channel session ${JSON.stringify(sid)}: ${error instanceof Error ? error.message : String(error)}`
8826
+ );
8827
+ });
8516
8828
  telemetry.metrics?.sessionLifecycle("die");
8517
8829
  emitSessionLifecycle({
8518
8830
  type: "removed",
@@ -9260,6 +9572,41 @@ function createAcpSessionBridge(opts) {
9260
9572
  }
9261
9573
  return response;
9262
9574
  }, "requestWorkspaceStatus");
9575
+ const MAX_CHILD_HEAP_UNCLASSIFIED_NAMES = 64;
9576
+ const MAX_CHILD_HEAP_SPACE_NAME_LENGTH = 64;
9577
+ const parseChildHeapReport = /* @__PURE__ */ __name((value) => {
9578
+ if (typeof value !== "object" || value === null) return void 0;
9579
+ const raw = value;
9580
+ const counts = [
9581
+ "peakOldGenerationBytes",
9582
+ "peakLiveSetBytes",
9583
+ "peakTotalHeapBytes",
9584
+ "majorGcCount",
9585
+ "majorGcMs"
9586
+ ];
9587
+ const parsed = {};
9588
+ for (const key of counts) {
9589
+ const n = raw[key];
9590
+ if (typeof n !== "number" || !Number.isFinite(n) || n < 0) {
9591
+ return void 0;
9592
+ }
9593
+ parsed[key] = n;
9594
+ }
9595
+ const names = raw["unclassifiedSpaceNames"];
9596
+ if (!Array.isArray(names) || names.length > MAX_CHILD_HEAP_UNCLASSIFIED_NAMES || names.some(
9597
+ (name) => typeof name !== "string" || name.length > MAX_CHILD_HEAP_SPACE_NAME_LENGTH
9598
+ )) {
9599
+ return void 0;
9600
+ }
9601
+ return {
9602
+ peakOldGenerationBytes: parsed["peakOldGenerationBytes"],
9603
+ peakLiveSetBytes: parsed["peakLiveSetBytes"],
9604
+ peakTotalHeapBytes: parsed["peakTotalHeapBytes"],
9605
+ majorGcCount: parsed["majorGcCount"],
9606
+ majorGcMs: parsed["majorGcMs"],
9607
+ unclassifiedSpaceNames: names
9608
+ };
9609
+ }, "parseChildHeapReport");
9263
9610
  const STALE_CHILD_RESOURCE_MS = 3e4;
9264
9611
  let childResourceRefreshing = false;
9265
9612
  const refreshChildResource = /* @__PURE__ */ __name(async () => {
@@ -9276,6 +9623,8 @@ function createAcpSessionBridge(opts) {
9276
9623
  if (typeof res.cpuPercent === "number" && Number.isFinite(res.cpuPercent)) {
9277
9624
  info.childCpuPercent = Math.min(100, Math.max(0, res.cpuPercent));
9278
9625
  }
9626
+ const heap = parseChildHeapReport(res.heap);
9627
+ if (heap) info.childHeap = heap;
9279
9628
  info.childResourceAt = Date.now();
9280
9629
  } catch (err) {
9281
9630
  teeServeDebugLine(
@@ -9295,6 +9644,10 @@ function createAcpSessionBridge(opts) {
9295
9644
  return {
9296
9645
  rssBytes: info.childRssBytes ?? 0,
9297
9646
  cpuPercent: info.childCpuPercent ?? 0,
9647
+ // Deliberately not defaulted. Unlike rss/cpu, where 0 is a plausible
9648
+ // reading, a zeroed heap report would assert the child needed no old
9649
+ // generation — the one conclusion that must never be manufactured.
9650
+ heap: info.childHeap,
9298
9651
  // Bounded by the guard above, so a caller summing several children's
9299
9652
  // readings can say how far apart they were taken. Without it a sum of
9300
9653
  // readings up to `STALE_CHILD_RESOURCE_MS` apart looks instantaneous.
@@ -9550,13 +9903,17 @@ function createAcpSessionBridge(opts) {
9550
9903
  workspaceCwd,
9551
9904
  persistence: createSessionArtifactPersistence(ci.connection, sessionId)
9552
9905
  }),
9553
- media: retainSessionMedia(sessionId),
9906
+ attachments: new SessionAttachmentStore(
9907
+ opts.sessionAttachmentsRoot,
9908
+ sessionId
9909
+ ),
9554
9910
  recordingDegraded: false,
9555
9911
  closing: false,
9556
9912
  cwdChangeQueue: Promise.resolve(),
9557
9913
  promptQueue: Promise.resolve(),
9558
9914
  pendingPromptCount: 0,
9559
9915
  pendingAgentNotificationCount: 0,
9916
+ ...opts.promptLedger ? { promptLedger: opts.promptLedger } : {},
9560
9917
  pendingPromptList: [],
9561
9918
  terminalTurnStatuses: /* @__PURE__ */ new Map(),
9562
9919
  enrichedTerminalPromptIds: /* @__PURE__ */ new Set(),
@@ -9974,7 +10331,7 @@ function createAcpSessionBridge(opts) {
9974
10331
  // Late attachers get the same ACP state the original restore
9975
10332
  // caller saw; spawn-only sessions don't carry a state payload.
9976
10333
  state: existing.restoreState ?? {},
9977
- hasActivePrompt: existing.promptActive,
10334
+ hasActivePrompt: existing.promptActive || existing.goalTurnActive === true,
9978
10335
  ...replayFields,
9979
10336
  ...historyAnchorRecordId !== void 0 ? { historyAnchorRecordId } : {}
9980
10337
  };
@@ -10056,7 +10413,7 @@ function createAcpSessionBridge(opts) {
10056
10413
  attached: true,
10057
10414
  clientId,
10058
10415
  createdAt: entry.createdAt,
10059
- hasActivePrompt: entry.promptActive,
10416
+ hasActivePrompt: entry.promptActive || entry.goalTurnActive === true,
10060
10417
  ...waiterReplayFields ?? {}
10061
10418
  };
10062
10419
  }
@@ -10419,7 +10776,7 @@ function createAcpSessionBridge(opts) {
10419
10776
  ...racedEntry.sourceType ? { sourceType: racedEntry.sourceType } : {},
10420
10777
  ...racedEntry.sourceId !== void 0 ? { sourceId: racedEntry.sourceId } : {},
10421
10778
  state: racedEntry.restoreState ?? {},
10422
- hasActivePrompt: racedEntry.promptActive,
10779
+ hasActivePrompt: racedEntry.promptActive || racedEntry.goalTurnActive === true,
10423
10780
  ...replayFieldsFor(racedEntry, action, liveReplayMode)
10424
10781
  };
10425
10782
  }
@@ -10495,7 +10852,7 @@ function createAcpSessionBridge(opts) {
10495
10852
  ...entry.sourceId !== void 0 ? { sourceId: entry.sourceId } : {},
10496
10853
  state: publicState,
10497
10854
  ...artifactRestoreWarnings.length > 0 ? { artifactWarnings: artifactRestoreWarnings } : {},
10498
- hasActivePrompt: entry.promptActive,
10855
+ hasActivePrompt: entry.promptActive || entry.goalTurnActive === true,
10499
10856
  ...replayFieldsFor(entry, action, liveReplayMode)
10500
10857
  };
10501
10858
  })().finally(async () => {
@@ -10510,9 +10867,11 @@ function createAcpSessionBridge(opts) {
10510
10867
  const restoreEntry = byId.get(req.sessionId);
10511
10868
  if (restoreEntry?.events === restoreEvents) {
10512
10869
  byId.delete(req.sessionId);
10513
- if (retainedMedia.has(req.sessionId)) {
10514
- retainedMediaDetachedAt.set(req.sessionId, Date.now());
10515
- }
10870
+ await restoreEntry.attachments.close().catch((error) => {
10871
+ writeStderrLine(
10872
+ `qwen serve: failed to close attachments after restoring session ${JSON.stringify(req.sessionId)}: ${error instanceof Error ? error.message : String(error)}`
10873
+ );
10874
+ });
10516
10875
  ci?.sessionIds.delete(req.sessionId);
10517
10876
  emitSessionLifecycle({
10518
10877
  type: "removed",
@@ -10676,15 +11035,11 @@ function createAcpSessionBridge(opts) {
10676
11035
  } catch {
10677
11036
  }
10678
11037
  entry.events.close();
10679
- if (reason === "last_client_detached" || reason === "idle_timeout") {
10680
- retainedMediaDetachedAt.set(sessionId, Date.now());
10681
- } else {
10682
- await releaseSessionMedia(sessionId).catch((error) => {
10683
- writeStderrLine(
10684
- `qwen serve: failed to release media for closed session ${JSON.stringify(sessionId)}: ${error instanceof Error ? error.message : String(error)}`
10685
- );
10686
- });
10687
- }
11038
+ await entry.attachments.close().catch((error) => {
11039
+ writeStderrLine(
11040
+ `qwen serve: failed to close attachments for session ${JSON.stringify(sessionId)}: ${error instanceof Error ? error.message : String(error)}`
11041
+ );
11042
+ });
10688
11043
  if (!agentSessionClosed) {
10689
11044
  try {
10690
11045
  await telemetry.withSpan(
@@ -10713,25 +11068,15 @@ function createAcpSessionBridge(opts) {
10713
11068
  ring.splice(0, ring.length - MID_TURN_RECONCILIATION_RING_SIZE);
10714
11069
  }
10715
11070
  }, "rememberMidTurnId");
10716
- const removeQueuedMedia = /* @__PURE__ */ __name((entry, content) => {
10717
- for (const block of content ?? []) {
10718
- if (!isSessionMediaReference(block)) continue;
10719
- void entry.media.remove(block.mediaId).catch((error) => {
10720
- writeStderrLine(
10721
- `[session-media] session=${JSON.stringify(entry.sessionId)} failed to remove queued media ${JSON.stringify(block.mediaId)}: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
10722
- );
10723
- });
10724
- }
10725
- }, "removeQueuedMedia");
10726
11071
  const promoteMidTurnMessage = /* @__PURE__ */ __name((entry, messageId, text, originatorClientId, content) => {
10727
11072
  const resolvableBlocks = [];
10728
11073
  let degraded = 0;
10729
11074
  for (const block of content ?? []) {
10730
11075
  try {
10731
- entry.media.assertReference(block);
11076
+ entry.attachments.assertReference(block);
10732
11077
  resolvableBlocks.push(block);
10733
11078
  } catch (error) {
10734
- if (!(error instanceof SessionMediaReferenceError)) throw error;
11079
+ if (!(error instanceof SessionAttachmentReferenceError)) throw error;
10735
11080
  degraded += 1;
10736
11081
  }
10737
11082
  }
@@ -10740,7 +11085,7 @@ function createAcpSessionBridge(opts) {
10740
11085
  ...resolvableBlocks
10741
11086
  ];
10742
11087
  if (degraded > 0) {
10743
- prompt = withMediaDegradationMarker(prompt);
11088
+ prompt = withAttachmentDegradationMarker(prompt);
10744
11089
  }
10745
11090
  const context = {
10746
11091
  promptId: messageId,
@@ -10753,7 +11098,7 @@ function createAcpSessionBridge(opts) {
10753
11098
  entry.sessionId,
10754
11099
  {
10755
11100
  sessionId: entry.sessionId,
10756
- prompt: withMediaDegradationMarker(
11101
+ prompt: withAttachmentDegradationMarker(
10757
11102
  text ? [{ type: "text", text }] : []
10758
11103
  )
10759
11104
  },
@@ -10773,7 +11118,7 @@ function createAcpSessionBridge(opts) {
10773
11118
  );
10774
11119
  } catch (error) {
10775
11120
  try {
10776
- if (!(error instanceof SessionMediaReferenceError)) throw error;
11121
+ if (!(error instanceof SessionAttachmentReferenceError)) throw error;
10777
11122
  result = sendFallback();
10778
11123
  } catch (fallbackError) {
10779
11124
  writeStderrLine(
@@ -10788,6 +11133,37 @@ function createAcpSessionBridge(opts) {
10788
11133
  );
10789
11134
  });
10790
11135
  }, "promoteMidTurnMessage");
11136
+ const settleUndrainedMidTurnMessages = /* @__PURE__ */ __name((entry, messages) => {
11137
+ for (const message of messages) {
11138
+ if (message.queueOnly) {
11139
+ try {
11140
+ message.onSettledWithoutDrain?.();
11141
+ } catch (error) {
11142
+ writeStderrLine(
11143
+ `[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))}`
11144
+ );
11145
+ }
11146
+ continue;
11147
+ }
11148
+ promoteMidTurnMessage(
11149
+ entry,
11150
+ message.messageId,
11151
+ message.text,
11152
+ message.originatorClientId,
11153
+ message.content
11154
+ );
11155
+ }
11156
+ }, "settleUndrainedMidTurnMessages");
11157
+ const settleMidTurnQueueAfterGoalTurn = /* @__PURE__ */ __name((sessionId) => {
11158
+ const entry = byId.get(sessionId);
11159
+ if (!entry) return;
11160
+ if (entry.goalTurnActive === true || entry.pendingPromptCount > 0 || entry.closing) {
11161
+ return;
11162
+ }
11163
+ const undrained = entry.midTurnMessageQueue.splice(0);
11164
+ if (undrained.length === 0) return;
11165
+ settleUndrainedMidTurnMessages(entry, undrained);
11166
+ }, "settleMidTurnQueueAfterGoalTurn");
10791
11167
  const bridgeApi = {
10792
11168
  setLiveScreenContextCaptureHandler(handler) {
10793
11169
  liveScreenContextCaptureHandler = handler;
@@ -10830,7 +11206,7 @@ function createAcpSessionBridge(opts) {
10830
11206
  attachCount: entry.attachCount,
10831
11207
  pendingPromptCount: entry.pendingPromptCount,
10832
11208
  pendingPermissionCount: entry.pendingPermissionIds.size,
10833
- hasActivePrompt: entry.promptActive,
11209
+ hasActivePrompt: entry.promptActive || entry.goalTurnActive === true,
10834
11210
  lastEventId: entry.events.lastEventId,
10835
11211
  ...entry.sessionLastSeenAt !== void 0 ? { lastSeenAt: entry.sessionLastSeenAt } : {},
10836
11212
  ...entry.currentModelId ? { currentModelId: entry.currentModelId } : {},
@@ -10983,7 +11359,7 @@ function createAcpSessionBridge(opts) {
10983
11359
  createdAt: existing.createdAt,
10984
11360
  ...existing.sourceType ? { sourceType: existing.sourceType } : {},
10985
11361
  ...existing.sourceId !== void 0 ? { sourceId: existing.sourceId } : {},
10986
- hasActivePrompt: existing.promptActive
11362
+ hasActivePrompt: existing.promptActive || existing.goalTurnActive === true
10987
11363
  };
10988
11364
  }
10989
11365
  const inFlight = inFlightSpawns.get(workspaceKey);
@@ -11034,7 +11410,7 @@ function createAcpSessionBridge(opts) {
11034
11410
  ...session,
11035
11411
  attached: true,
11036
11412
  clientId,
11037
- hasActivePrompt: attachedEntry.promptActive
11413
+ hasActivePrompt: attachedEntry.promptActive || attachedEntry.goalTurnActive === true
11038
11414
  };
11039
11415
  }
11040
11416
  }
@@ -11102,7 +11478,7 @@ function createAcpSessionBridge(opts) {
11102
11478
  req.branch,
11103
11479
  req.sessionId
11104
11480
  );
11105
- const tracker = effectiveScope === "single" ? workspaceKey : `${workspaceKey}#${randomUUID3()}`;
11481
+ const tracker = effectiveScope === "single" ? workspaceKey : `${workspaceKey}#${randomUUID2()}`;
11106
11482
  inFlightSpawns.set(tracker, promise);
11107
11483
  try {
11108
11484
  return await promise;
@@ -11140,7 +11516,7 @@ function createAcpSessionBridge(opts) {
11140
11516
  const promotedMidTurn = context?.promotedMidTurn;
11141
11517
  const isPromotedMidTurn = promotedMidTurn !== void 0;
11142
11518
  const originatorClientId = promotedMidTurn ? promotedMidTurn.originatorClientId : resolveTrustedClientId(entry, context?.clientId);
11143
- entry.media.assertReferences(req.prompt);
11519
+ entry.attachments.assertReferences(req.prompt);
11144
11520
  const modelPrompt = context?.modelPrompt;
11145
11521
  if (modelPrompt !== void 0 && !isValidTrustedModelPrompt(modelPrompt)) {
11146
11522
  throw new TypeError(
@@ -11164,7 +11540,7 @@ function createAcpSessionBridge(opts) {
11164
11540
  promptSlotReleased = true;
11165
11541
  entry.pendingPromptCount = Math.max(0, entry.pendingPromptCount - 1);
11166
11542
  }, "releasePromptSlot");
11167
- const promptId = context?.promptId ?? randomUUID3();
11543
+ const promptId = context?.promptId ?? randomUUID2();
11168
11544
  const invocationContext = Object.freeze({
11169
11545
  version: 1,
11170
11546
  sessionId,
@@ -11202,6 +11578,22 @@ function createAcpSessionBridge(opts) {
11202
11578
  state: isQueued ? "queued" : "running"
11203
11579
  };
11204
11580
  entry.pendingPromptList.push(pendingEntry);
11581
+ let tailUuid;
11582
+ try {
11583
+ tailUuid = entry.promptLedger?.transcriptTailUuid?.(sessionId);
11584
+ } catch (error) {
11585
+ opts.onDiagnosticLine?.(
11586
+ `qwen serve: prompt ledger dispatch marker failed for session=${sessionId}: ${error instanceof Error ? error.message : String(error)}`,
11587
+ "warn"
11588
+ );
11589
+ }
11590
+ appendPromptLedgerBestEffort(entry, {
11591
+ v: 1,
11592
+ promptId,
11593
+ state: "in_flight",
11594
+ ...tailUuid !== void 0 ? { tailUuid } : {},
11595
+ at: queuedAt
11596
+ });
11205
11597
  try {
11206
11598
  context?.onPromptAdmitted?.();
11207
11599
  } catch (error) {
@@ -11321,14 +11713,16 @@ function createAcpSessionBridge(opts) {
11321
11713
  let dispatchBlocks = req.prompt;
11322
11714
  let resolvedPrompt;
11323
11715
  try {
11324
- resolvedPrompt = await entry.media.resolveContent(dispatchBlocks);
11716
+ resolvedPrompt = await entry.attachments.resolveContent(dispatchBlocks);
11325
11717
  } catch (error) {
11326
- if (!isPromotedMidTurn || !(error instanceof SessionMediaReferenceError)) {
11718
+ if (!isPromotedMidTurn || !(error instanceof SessionAttachmentReferenceError)) {
11327
11719
  throw error;
11328
11720
  }
11329
- const perBlock = await entry.media.resolveContentDegrading(dispatchBlocks);
11721
+ const perBlock = await entry.attachments.resolveContentDegrading(
11722
+ dispatchBlocks
11723
+ );
11330
11724
  dispatchBlocks = perBlock.retainedBlocks;
11331
- resolvedPrompt = withMediaDegradationMarker(
11725
+ resolvedPrompt = withAttachmentDegradationMarker(
11332
11726
  perBlock.resolvedBlocks
11333
11727
  );
11334
11728
  }
@@ -11359,7 +11753,7 @@ function createAcpSessionBridge(opts) {
11359
11753
  delete meta[DAEMON_CHANNEL_DELIVERY_META_KEY];
11360
11754
  delete meta[DAEMON_PROMPT_DISPLAY_TEXT_META_KEY];
11361
11755
  delete meta[DAEMON_MODEL_PROMPT_META_KEY];
11362
- delete meta[DAEMON_MEDIA_REFERENCES_META_KEY];
11756
+ delete meta[DAEMON_ATTACHMENT_REFERENCES_META_KEY];
11363
11757
  delete meta[CHANNEL_PROMPT_META_KEY];
11364
11758
  if (isRetry) {
11365
11759
  meta[DAEMON_RETRY_META_KEY] = true;
@@ -11376,11 +11770,11 @@ function createAcpSessionBridge(opts) {
11376
11770
  if (modelPrompt !== void 0) {
11377
11771
  meta[DAEMON_MODEL_PROMPT_META_KEY] = modelPrompt;
11378
11772
  }
11379
- const mediaReferences = dispatchBlocks.filter(
11380
- isSessionMediaReference
11773
+ const attachmentReferences = dispatchBlocks.filter(
11774
+ isSessionAttachmentReference
11381
11775
  );
11382
- if (mediaReferences.length > 0) {
11383
- meta[DAEMON_MEDIA_REFERENCES_META_KEY] = mediaReferences;
11776
+ if (attachmentReferences.length > 0) {
11777
+ meta[DAEMON_ATTACHMENT_REFERENCES_META_KEY] = attachmentReferences;
11384
11778
  }
11385
11779
  if (context?.channelPrompt === true) {
11386
11780
  meta[CHANNEL_PROMPT_META_KEY] = true;
@@ -11394,6 +11788,7 @@ function createAcpSessionBridge(opts) {
11394
11788
  return copy;
11395
11789
  })();
11396
11790
  entry.promptActive = true;
11791
+ entry.goalTurnActive = false;
11397
11792
  entry.activePromptId = pendingEntry.promptId;
11398
11793
  delete entry.cancelBroadcastWithoutPrompt;
11399
11794
  delete entry.turnError;
@@ -11554,25 +11949,7 @@ function createAcpSessionBridge(opts) {
11554
11949
  const shouldSettleMidTurnQueue = entry.pendingPromptCount === 1 && !entry.closing && byId.get(entry.sessionId) === entry;
11555
11950
  const undrainedMessages = shouldSettleMidTurnQueue ? entry.midTurnMessageQueue.splice(0) : [];
11556
11951
  releasePromptSlot();
11557
- for (const message of undrainedMessages) {
11558
- if (message.queueOnly) {
11559
- try {
11560
- message.onSettledWithoutDrain?.();
11561
- } catch (error) {
11562
- writeStderrLine(
11563
- `[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))}`
11564
- );
11565
- }
11566
- continue;
11567
- }
11568
- promoteMidTurnMessage(
11569
- entry,
11570
- message.messageId,
11571
- message.text,
11572
- message.originatorClientId,
11573
- message.content
11574
- );
11575
- }
11952
+ settleUndrainedMidTurnMessages(entry, undrainedMessages);
11576
11953
  void maybeCloseIdleSession(entry, "prompt_settled");
11577
11954
  }).catch(() => {
11578
11955
  });
@@ -11818,6 +12195,21 @@ function createAcpSessionBridge(opts) {
11818
12195
  );
11819
12196
  }
11820
12197
  markSessionCatalogChanged();
12198
+ if (opts.sessionAttachmentsRoot) {
12199
+ const branchAttachments = new SessionAttachmentStore(
12200
+ opts.sessionAttachmentsRoot,
12201
+ result.newSessionId
12202
+ );
12203
+ try {
12204
+ await branchAttachments.copyFrom(entry.attachments);
12205
+ } catch (error) {
12206
+ writeStderrLine(
12207
+ `qwen serve: failed to copy attachments for branched session ${result.newSessionId}: ${error instanceof Error ? error.message : String(error)}`
12208
+ );
12209
+ } finally {
12210
+ await branchAttachments.close();
12211
+ }
12212
+ }
11821
12213
  const rawBranchName = result.displayName ?? result.title;
11822
12214
  const branchDisplayName = typeof rawBranchName === "string" ? rawBranchName : result.newSessionId.slice(0, 8);
11823
12215
  if (!restoreBranch) {
@@ -11884,6 +12276,15 @@ function createAcpSessionBridge(opts) {
11884
12276
  throw restoreErr;
11885
12277
  }
11886
12278
  const newEntry = byId.get(result.newSessionId);
12279
+ if (newEntry && !opts.sessionAttachmentsRoot) {
12280
+ try {
12281
+ await newEntry.attachments.copyFrom(entry.attachments);
12282
+ } catch (error) {
12283
+ writeStderrLine(
12284
+ `qwen serve: failed to copy attachments for branched session ${result.newSessionId}: ${error instanceof Error ? error.message : String(error)}`
12285
+ );
12286
+ }
12287
+ }
11887
12288
  if (newEntry) newEntry.displayName = branchDisplayName;
11888
12289
  let sourcePersisted;
11889
12290
  if (newEntry?.sourceType) {
@@ -12429,6 +12830,18 @@ function createAcpSessionBridge(opts) {
12429
12830
  { taskId, taskKind }
12430
12831
  );
12431
12832
  },
12833
+ async controlSessionGoal(sessionId, request, context) {
12834
+ const entry = byId.get(sessionId);
12835
+ if (!entry) throw new SessionNotFoundError(sessionId);
12836
+ const info = channelInfoForEntry(entry);
12837
+ if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
12838
+ resolveTrustedClientId(entry, context?.clientId);
12839
+ return requestSessionStatus(
12840
+ sessionId,
12841
+ SERVE_CONTROL_EXT_METHODS.sessionGoalControl,
12842
+ { request }
12843
+ );
12844
+ },
12432
12845
  async clearSessionGoal(sessionId) {
12433
12846
  return requestSessionStatus(
12434
12847
  sessionId,
@@ -12805,7 +13218,7 @@ function createAcpSessionBridge(opts) {
12805
13218
  const info = channelInfoForEntry(entry);
12806
13219
  if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
12807
13220
  resolveTrustedClientId(entry, context?.clientId);
12808
- const requestId = randomUUID3();
13221
+ const requestId = randomUUID2();
12809
13222
  const queue = new GenerationStreamQueue(
12810
13223
  GENERATION_STREAM_QUEUE_CAPACITY
12811
13224
  );
@@ -12939,42 +13352,27 @@ function createAcpSessionBridge(opts) {
12939
13352
  }
12940
13353
  return { sessionId, state: "idle" };
12941
13354
  },
12942
- async storeSessionMedia(sessionId, data, mimeType, context) {
13355
+ async storeSessionAttachment(sessionId, data, mimeType, context, name) {
12943
13356
  const entry = byId.get(sessionId);
12944
13357
  if (!entry) throw new SessionNotFoundError(sessionId);
12945
13358
  resolveTrustedClientId(entry, context?.clientId);
12946
- const operation = mediaPutQueue.then(async () => {
12947
- if (byId.get(sessionId) !== entry) {
12948
- throw new SessionNotFoundError(sessionId);
12949
- }
12950
- const retainedBytes = [...new Set(retainedMedia.values())].reduce(
12951
- (total, store) => total + store.sizeBytes,
12952
- 0
12953
- );
12954
- if (retainedBytes + data.byteLength > MAX_RETAINED_SESSION_MEDIA_BYTES) {
12955
- throw new RangeError(
12956
- `Session media exceeds the ${MAX_RETAINED_SESSION_MEDIA_BYTES}-byte daemon limit`
12957
- );
12958
- }
12959
- return await entry.media.put(data, mimeType);
12960
- });
12961
- mediaPutQueue = operation.then(
12962
- () => void 0,
12963
- () => void 0
12964
- );
12965
- return await operation;
13359
+ return await entry.attachments.putAttachment(data, mimeType, name);
12966
13360
  },
12967
- async readSessionMedia(sessionId, mediaId, context) {
13361
+ async readSessionAttachment(sessionId, attachmentId, context) {
12968
13362
  const entry = byId.get(sessionId);
12969
13363
  if (!entry) throw new SessionNotFoundError(sessionId);
12970
13364
  resolveTrustedClientId(entry, context?.clientId);
12971
- return await entry.media.read(mediaId);
13365
+ return await entry.attachments.read(attachmentId);
12972
13366
  },
12973
- async removeSessionMedia(sessionId, mediaId, context) {
13367
+ async removeSessionAttachment(sessionId, attachmentId, context) {
12974
13368
  const entry = byId.get(sessionId);
12975
13369
  if (!entry) throw new SessionNotFoundError(sessionId);
12976
13370
  resolveTrustedClientId(entry, context?.clientId);
12977
- return await entry.media.remove(mediaId);
13371
+ return await entry.attachments.remove(attachmentId);
13372
+ },
13373
+ async deleteSessionAttachments(sessionId) {
13374
+ const store = byId.get(sessionId)?.attachments ?? new SessionAttachmentStore(opts.sessionAttachmentsRoot, sessionId);
13375
+ await store.delete();
12978
13376
  },
12979
13377
  removePendingPrompt(sessionId, promptId, context) {
12980
13378
  const entry = byId.get(sessionId);
@@ -12994,7 +13392,6 @@ function createAcpSessionBridge(opts) {
12994
13392
  );
12995
13393
  if (target.state === "queued") {
12996
13394
  entry.pendingPromptList.splice(idx, 1);
12997
- removeQueuedMedia(entry, target.content);
12998
13395
  } else {
12999
13396
  target.removed = true;
13000
13397
  }
@@ -13021,7 +13418,7 @@ function createAcpSessionBridge(opts) {
13021
13418
  );
13022
13419
  const trimmed = message.trim();
13023
13420
  const mediaBlocks = (options?.content ?? []).filter(
13024
- (block) => block.type === "image"
13421
+ (block) => block.type === "image" || block.type === "resource"
13025
13422
  );
13026
13423
  if (trimmed.length === 0 && mediaBlocks.length === 0) {
13027
13424
  writeStderrLine(
@@ -13047,10 +13444,10 @@ function createAcpSessionBridge(opts) {
13047
13444
  (pending) => pending.promptId === requestedMessageId
13048
13445
  );
13049
13446
  if (promoted) {
13050
- const promotedImages = (promoted.content ?? []).filter(
13051
- (block) => block.type === "image"
13447
+ const promotedMedia = (promoted.content ?? []).filter(
13448
+ (block) => block.type === "image" || block.type === "resource"
13052
13449
  );
13053
- const sameMedia = JSON.stringify(promotedImages) === JSON.stringify(mediaBlocks);
13450
+ const sameMedia = JSON.stringify(promotedMedia) === JSON.stringify(mediaBlocks);
13054
13451
  const promotedText = promoted.text === "[image]" && trimmed.length === 0 ? "" : promoted.text;
13055
13452
  if (promotedText === trimmed && sameMedia) {
13056
13453
  return { accepted: true, messageId: requestedMessageId };
@@ -13073,23 +13470,23 @@ function createAcpSessionBridge(opts) {
13073
13470
  );
13074
13471
  return { accepted: false };
13075
13472
  }
13076
- entry.media.assertReferences(mediaBlocks);
13077
- const inlineBytes = inlineMediaBlockBytes(mediaBlocks);
13473
+ entry.attachments.assertReferences(mediaBlocks);
13474
+ const inlineBytes = inlineAttachmentBlockBytes(mediaBlocks);
13078
13475
  if (inlineBytes > 0) {
13079
13476
  const queuedInlineBytes = entry.midTurnMessageQueue.reduce(
13080
- (total, queued) => total + inlineMediaBlockBytes(queued.content ?? []),
13477
+ (total, queued) => total + inlineAttachmentBlockBytes(queued.content ?? []),
13081
13478
  0
13082
13479
  );
13083
- if (queuedInlineBytes + inlineBytes > SESSION_MEDIA_MAX_TOTAL_BYTES) {
13480
+ if (queuedInlineBytes + inlineBytes > MAX_QUEUED_INLINE_ATTACHMENT_BYTES) {
13084
13481
  writeStderrLine(
13085
- `[mid-turn] session=${entry.sessionId} rejected: queued inline media exceeds the ${SESSION_MEDIA_MAX_TOTAL_BYTES}-byte session budget`
13482
+ `[mid-turn] session=${entry.sessionId} rejected: queued inline attachments exceed the ${MAX_QUEUED_INLINE_ATTACHMENT_BYTES}-byte session budget`
13086
13483
  );
13087
13484
  return { accepted: false };
13088
13485
  }
13089
13486
  }
13090
- const messageId = requestedMessageId ?? randomUUID3();
13091
- if (entry.pendingPromptCount === 0) {
13092
- if (options?.queueOnly) {
13487
+ const messageId = requestedMessageId ?? randomUUID2();
13488
+ if (entry.pendingPromptCount === 0 && entry.goalTurnActive !== true) {
13489
+ if (options?.queueOnly || options?.rejectIfIdle) {
13093
13490
  writeStderrLine(
13094
13491
  `[mid-turn] session=${JSON.stringify(entry.sessionId)} rejected id ${JSON.stringify(messageId)}: session idle`
13095
13492
  );
@@ -13173,7 +13570,6 @@ function createAcpSessionBridge(opts) {
13173
13570
  const [removed] = entry.midTurnMessageQueue.splice(index, 1);
13174
13571
  rememberMidTurnId(entry.settledMidTurnMessageIds, messageId);
13175
13572
  if (removed) {
13176
- removeQueuedMedia(entry, removed.content);
13177
13573
  try {
13178
13574
  entry.events.publish({
13179
13575
  type: "pending_prompt_completed",
@@ -13735,7 +14131,7 @@ function createAcpSessionBridge(opts) {
13735
14131
  ]);
13736
14132
  },
13737
14133
  generateWorkspaceContent(prompt, signal, _originatorClientId) {
13738
- const requestId = randomUUID3();
14134
+ const requestId = randomUUID2();
13739
14135
  const queue = new GenerationStreamQueue(
13740
14136
  GENERATION_STREAM_QUEUE_CAPACITY
13741
14137
  );
@@ -13914,9 +14310,9 @@ function createAcpSessionBridge(opts) {
13914
14310
  closingChannel,
13915
14311
  `force kill closing session ${JSON.stringify(sessionId)}`
13916
14312
  );
13917
- await releaseSessionMedia(sessionId).catch((releaseError) => {
14313
+ await entry.attachments.close().catch((releaseError) => {
13918
14314
  writeStderrLine(
13919
- `qwen serve: failed to release media for killed session ${JSON.stringify(sessionId)}: ${releaseError instanceof Error ? releaseError.message : String(releaseError)}`
14315
+ `qwen serve: failed to close attachments for killed session ${JSON.stringify(sessionId)}: ${releaseError instanceof Error ? releaseError.message : String(releaseError)}`
13920
14316
  );
13921
14317
  });
13922
14318
  return true;
@@ -13946,11 +14342,6 @@ function createAcpSessionBridge(opts) {
13946
14342
  ci,
13947
14343
  `force kill session ${JSON.stringify(sessionId)}`
13948
14344
  );
13949
- await releaseSessionMedia(sessionId).catch((releaseError) => {
13950
- writeStderrLine(
13951
- `qwen serve: failed to release media for killed session ${JSON.stringify(sessionId)}: ${releaseError instanceof Error ? releaseError.message : String(releaseError)}`
13952
- );
13953
- });
13954
14345
  return true;
13955
14346
  }
13956
14347
  entry.closing = false;
@@ -13987,9 +14378,9 @@ function createAcpSessionBridge(opts) {
13987
14378
  } catch {
13988
14379
  }
13989
14380
  entry.events.close();
13990
- await releaseSessionMedia(sessionId).catch((error) => {
14381
+ await entry.attachments.close().catch((error) => {
13991
14382
  writeStderrLine(
13992
- `qwen serve: failed to release media for killed session ${JSON.stringify(sessionId)}: ${error instanceof Error ? error.message : String(error)}`
14383
+ `qwen serve: failed to close attachments for killed session ${JSON.stringify(sessionId)}: ${error instanceof Error ? error.message : String(error)}`
13993
14384
  );
13994
14385
  });
13995
14386
  if (ci && hasNoChannelWork(ci)) {
@@ -14096,8 +14487,7 @@ function createAcpSessionBridge(opts) {
14096
14487
  ) : Promise.resolve();
14097
14488
  const teardownResults = await Promise.allSettled([
14098
14489
  ...channels.map((ci) => ci.channel.kill()),
14099
- ...[...retainedMedia.values()].map((store) => store.close()),
14100
- mediaPutQueue,
14490
+ ...[...byId.values()].map((entry) => entry.attachments.close()),
14101
14491
  ...inFlightSessionAwaits,
14102
14492
  ...inFlightRestoreAwaits,
14103
14493
  inFlightChannelAwait