@qwen-code/qwen-code 0.19.5 → 0.19.6-nightly.20260705.015ee4248

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 (264) hide show
  1. package/bundled/dataviz/SKILL.md +50 -0
  2. package/bundled/dataviz/references/anti-patterns.md +12 -0
  3. package/bundled/dataviz/references/choosing-a-form.md +16 -0
  4. package/bundled/dataviz/references/palette.md +57 -0
  5. package/bundled/dataviz/scripts/validate_palette.js +328 -0
  6. package/bundled/loop/SKILL.md +1 -1
  7. package/bundled/qc-helper/docs/configuration/settings.md +14 -8
  8. package/bundled/qc-helper/docs/features/headless.md +2 -2
  9. package/bundled/qc-helper/docs/features/scheduled-tasks.md +4 -2
  10. package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +1 -1
  11. package/bundled/review/DESIGN.md +23 -0
  12. package/bundled/review/SKILL.md +65 -8
  13. package/chunks/{MaxSizedBox-5JV77KRA.js → MaxSizedBox-PRXTXIGC.js} +31 -30
  14. package/chunks/{StandaloneSessionPicker-MGUGB6PH.js → StandaloneSessionPicker-PV7BSC5D.js} +42 -41
  15. package/chunks/{acpAgent-4F36XM3X.js → acpAgent-F34HNAM3.js} +939 -424
  16. package/chunks/{agent-IWBUZI6A.js → agent-XSQDKLST.js} +26 -25
  17. package/chunks/{agent-headless-JUTICAYO.js → agent-headless-B63XCKWX.js} +26 -25
  18. package/chunks/{anthropicContentGenerator-WL3LJOUB.js → anthropicContentGenerator-INXXI4PW.js} +7 -7
  19. package/chunks/{artifact-tool-W76WC2JX.js → artifact-tool-H2TNPNP5.js} +16 -3
  20. package/chunks/{askUserQuestion-J3P6TH3V.js → askUserQuestion-MKL5ZTMC.js} +2 -2
  21. package/chunks/{bridge-DHZHQ4UV.js → bridge-NCZDJBRF.js} +34 -32
  22. package/chunks/{channel-worker-supervisor-EVPAYNZ5.js → channel-worker-supervisor-JUYQ5Z4R.js} +5 -5
  23. package/chunks/{chunk-AVQDCKNF.js → chunk-22MNNNXE.js} +4 -0
  24. package/chunks/chunk-3RW4AZJV.js +5063 -0
  25. package/chunks/chunk-3VJ7MT3N.js +111 -0
  26. package/chunks/{chunk-JKGCBO3O.js → chunk-3VRMGS3A.js} +4 -4
  27. package/chunks/{chunk-TN6RT6EJ.js → chunk-3WOJO5EJ.js} +16 -4
  28. package/chunks/{chunk-G7OQ3RFJ.js → chunk-42XGFRJS.js} +3 -3
  29. package/chunks/{chunk-MOUXUCXQ.js → chunk-4KJ2BPIT.js} +1 -1
  30. package/chunks/{chunk-PPFWEV7G.js → chunk-4RQIB5UZ.js} +62 -62
  31. package/chunks/{chunk-JJ6B3ZD3.js → chunk-544UZQBD.js} +3 -3
  32. package/chunks/{chunk-B236XPGF.js → chunk-5UDYETKK.js} +1 -1
  33. package/chunks/{chunk-7DMQRPC7.js → chunk-6BEOH4KK.js} +1 -1
  34. package/chunks/{chunk-CXBG27SF.js → chunk-6IAWPIQ6.js} +3 -2
  35. package/chunks/{chunk-MDBNFHIO.js → chunk-6MDY7FX3.js} +5 -5
  36. package/chunks/{chunk-RM3PGSGL.js → chunk-6SHO7WFF.js} +15 -2
  37. package/chunks/{chunk-YCU7QV7Q.js → chunk-73WGF3NW.js} +34 -1
  38. package/chunks/{chunk-T42ACHFI.js → chunk-7LTB54MK.js} +1 -1
  39. package/chunks/{chunk-SJVPSHXU.js → chunk-7U6J224Z.js} +132 -25
  40. package/chunks/{chunk-CSPSJU5R.js → chunk-7WLJPMWU.js} +17 -17
  41. package/chunks/{chunk-R6RI2HIC.js → chunk-ACD5DFUY.js} +41 -14
  42. package/chunks/{chunk-EWALXUAD.js → chunk-AGHU5VRK.js} +8 -0
  43. package/chunks/{chunk-GLPV75ES.js → chunk-AHNJGLFV.js} +2 -2
  44. package/chunks/{chunk-DRS2HTNT.js → chunk-AOAFX4PL.js} +5 -5
  45. package/chunks/{chunk-QHYITL6P.js → chunk-CK7IUFOB.js} +63 -22
  46. package/chunks/{chunk-RSBQS6M4.js → chunk-CODWJXQA.js} +1 -1
  47. package/chunks/{chunk-LWGLBVWA.js → chunk-CSOTA7KX.js} +1 -1
  48. package/chunks/{chunk-UJT42DB6.js → chunk-DFH4T5IQ.js} +6 -6
  49. package/chunks/{chunk-DX3TKXNT.js → chunk-DJACOX4C.js} +2 -2
  50. package/chunks/{chunk-KKQWC6WS.js → chunk-DNV4FSN5.js} +1 -1
  51. package/chunks/{chunk-YPCPZSZB.js → chunk-DT3B47QP.js} +1 -1
  52. package/chunks/{chunk-NIOPRZNN.js → chunk-E7KNELIZ.js} +1 -1
  53. package/chunks/{chunk-DG573EOO.js → chunk-FEJ2FZ3U.js} +1 -1
  54. package/chunks/{chunk-M5ITDKSM.js → chunk-FHGQFG2K.js} +1 -1
  55. package/chunks/{chunk-HISPEX42.js → chunk-FSXFNDTI.js} +7 -7
  56. package/chunks/{chunk-LHMZCIKL.js → chunk-FUW7YGJU.js} +3 -1
  57. package/chunks/{chunk-Y4UQT3PM.js → chunk-FV5SBNNC.js} +1 -1
  58. package/chunks/{chunk-YTFBURQD.js → chunk-GB6HDZIK.js} +10 -2
  59. package/chunks/{chunk-D5KISTLW.js → chunk-H6EMK6QK.js} +1 -1
  60. package/chunks/{chunk-2Z43HUXT.js → chunk-HLKG6CZN.js} +3 -2
  61. package/chunks/{chunk-VXGPYF7U.js → chunk-IFEWJXFS.js} +1 -1
  62. package/chunks/{chunk-TENOUWY2.js → chunk-IJRL2S3D.js} +3 -3
  63. package/chunks/{chunk-OQHLUA2Y.js → chunk-IL6ZEDZ2.js} +4 -4
  64. package/chunks/{chunk-2CAII5HD.js → chunk-J7XUJALJ.js} +1 -1
  65. package/chunks/chunk-JFFOH2XO.js +2472 -0
  66. package/chunks/{chunk-NNEJEBCF.js → chunk-K3FZXSTO.js} +95 -107
  67. package/chunks/{chunk-NDY7OENU.js → chunk-KB4ICCA5.js} +3 -3
  68. package/chunks/{chunk-MNOXKSM2.js → chunk-LFFF2SPV.js} +3 -3
  69. package/chunks/{chunk-J3ERRYYI.js → chunk-LTMDKOZC.js} +2 -2
  70. package/chunks/{chunk-R3UWLLRQ.js → chunk-M7AGQP5D.js} +1 -1
  71. package/chunks/{chunk-CBJE2PLG.js → chunk-MLB5AB2I.js} +4 -4
  72. package/chunks/{chunk-YHTNWGRR.js → chunk-MZ7BABX3.js} +1 -1
  73. package/chunks/{chunk-SJBIFVD2.js → chunk-NFE3DQ4R.js} +7 -3
  74. package/chunks/{chunk-BC4R5C5R.js → chunk-NLD2WKE3.js} +55 -12
  75. package/chunks/{chunk-ALX6BUYW.js → chunk-NUGPZXAZ.js} +35 -1
  76. package/chunks/{chunk-NSSVORTQ.js → chunk-NYKWPUAE.js} +17 -13
  77. package/chunks/{chunk-K4I4STSL.js → chunk-OGEJMKN5.js} +2 -2
  78. package/chunks/{chunk-HTWHAN44.js → chunk-PC4ZXHU2.js} +1 -1
  79. package/chunks/{chunk-2YH27UNF.js → chunk-QLBBV7C6.js} +1 -1
  80. package/chunks/chunk-QT7XKZT7.js +293 -0
  81. package/chunks/{chunk-NPQWQYU6.js → chunk-QYNNN7BK.js} +1 -1
  82. package/chunks/chunk-R4N3OALZ.js +5631 -0
  83. package/chunks/{chunk-G4M7FPNY.js → chunk-RB5GAQR6.js} +2 -2
  84. package/chunks/{chunk-SBMHB6N5.js → chunk-RPHMD5UC.js} +15 -15
  85. package/chunks/{chunk-CHY5C5T4.js → chunk-SJ2CTIYH.js} +185 -88
  86. package/chunks/chunk-SLQXGMYY.js +949 -0
  87. package/chunks/{chunk-BZHLWC6T.js → chunk-SUAKHHK7.js} +2 -2
  88. package/chunks/{chunk-IMAMNLY6.js → chunk-SWV4CVYG.js} +158 -3
  89. package/chunks/{chunk-XKSCTXPK.js → chunk-TDGF35EN.js} +4 -3
  90. package/chunks/{chunk-TOAUDIS7.js → chunk-TLWA7TPR.js} +6 -2
  91. package/chunks/{chunk-WALEWZ44.js → chunk-UVHL5TCO.js} +1 -1
  92. package/chunks/{chunk-ZISMVA6U.js → chunk-VDMM5LVR.js} +1566 -37
  93. package/chunks/{chunk-BPFJQGY7.js → chunk-VEB3NYSO.js} +164 -5
  94. package/chunks/{chunk-L7OJ3ZO3.js → chunk-VZ7HY3YU.js} +1 -1
  95. package/chunks/{chunk-6KPZJJ6U.js → chunk-W4I6PLEG.js} +131 -38
  96. package/chunks/{chunk-QOMAHFYI.js → chunk-W7EEOR7D.js} +3 -3
  97. package/chunks/{chunk-6QVQZAW6.js → chunk-WKP5QZOP.js} +1 -1
  98. package/chunks/{chunk-YXK4GPYO.js → chunk-WMPVYQ4P.js} +30 -1
  99. package/chunks/{chunk-DM4E337C.js → chunk-WNTS5A2R.js} +5 -5
  100. package/chunks/{chunk-6QTCOQX7.js → chunk-WOG7QWIT.js} +3086 -2635
  101. package/chunks/{chunk-UY2TCIZS.js → chunk-X265R5C6.js} +3 -3
  102. package/chunks/{chunk-LKWAVAB5.js → chunk-X2XNQQJI.js} +2 -2
  103. package/chunks/{chunk-MTNTVOJR.js → chunk-X3YGAX7V.js} +6 -3
  104. package/chunks/{chunk-EROWZQN7.js → chunk-XD3OLPII.js} +6 -6
  105. package/chunks/{chunk-RNGAT2Y2.js → chunk-YYL6HROQ.js} +1 -1
  106. package/chunks/{chunk-XKZXGIRM.js → chunk-YZ2LNTHO.js} +2 -2
  107. package/chunks/{chunk-Z7XNVNPM.js → chunk-YZ5ANQMR.js} +3 -3
  108. package/chunks/{chunk-4MCC4XHH.js → chunk-Z7EYROOJ.js} +3568 -5508
  109. package/chunks/{chunk-5B2WNM6V.js → chunk-ZI4MGBB5.js} +1 -1
  110. package/chunks/{chunk-WZ724G6K.js → chunk-ZPGE42V7.js} +6 -6
  111. package/chunks/{computer-use-ON3JQUHW.js → computer-use-5I2UHVNY.js} +26 -25
  112. package/chunks/{contextCommand-OGCG7W6N.js → contextCommand-3NWY7AJG.js} +28 -27
  113. package/chunks/{cron-create-6YTCOX6P.js → cron-create-O6ZHD3JN.js} +22 -7
  114. package/chunks/{cron-delete-E3NIFSUK.js → cron-delete-BFD5B2XX.js} +4 -4
  115. package/chunks/{cron-list-Q64MNQNE.js → cron-list-KI6FV4KN.js} +4 -4
  116. package/chunks/{daemon-EEB2RYOL.js → daemon-DRUMLVFP.js} +140 -0
  117. package/chunks/daemon-status-provider-A2DK3ZTO.js +74 -0
  118. package/chunks/{dist-AP5V4WYU.js → dist-AAO65DPC.js} +2 -2
  119. package/chunks/{dist-X4ZDE726.js → dist-ABQ7VWRR.js} +3 -3
  120. package/chunks/{dist-SNXOUPYS.js → dist-AW5ZYMUI.js} +2 -2
  121. package/chunks/{dist-Z57Q4YF5.js → dist-DN6M6TJ5.js} +525 -95
  122. package/chunks/{dist-IZP2VV4C.js → dist-JPSVN367.js} +2 -2
  123. package/chunks/{earlyInputCapture-DAFHSGVW.js → earlyInputCapture-5TMSSPQ5.js} +27 -26
  124. package/chunks/{edit-3QFNCSZH.js → edit-V4ITLSUD.js} +27 -26
  125. package/chunks/{en-M4EBUBNU.js → en-AXARKOWO.js} +2 -0
  126. package/chunks/{enter-worktree-62EQQMLD.js → enter-worktree-7ULPRMTJ.js} +26 -25
  127. package/chunks/{enterPlanMode-VUQSE4EG.js → enterPlanMode-YSHMLHJZ.js} +26 -25
  128. package/chunks/{errors-PW75PZXE.js → errors-TCUTD7NF.js} +28 -27
  129. package/chunks/{exit-worktree-VVNH7OVY.js → exit-worktree-JZDCKR6A.js} +26 -25
  130. package/chunks/{exitPlanMode-RUYRCYZW.js → exitPlanMode-GOQBN6SG.js} +26 -25
  131. package/chunks/{fast-path-TFRJRTTR.js → fast-path-F2HGAKO2.js} +3 -3
  132. package/chunks/{fast-path-settings-UQLRFDQP.js → fast-path-settings-RCRXH2JG.js} +2 -2
  133. package/chunks/{gemini-XW7ZDUVR.js → gemini-3M7ECKNW.js} +86 -80
  134. package/chunks/{geminiContentGenerator-MZDVHVZH.js → geminiContentGenerator-5EG3QEJM.js} +4 -4
  135. package/chunks/{glob-WAY2PUWA.js → glob-2DBDTLR4.js} +54 -29
  136. package/chunks/{grep-W6CBKSYC.js → grep-R27DLGWT.js} +26 -25
  137. package/chunks/{initializer-HK2XE6P2.js → initializer-OXB6BJZU.js} +32 -31
  138. package/chunks/{keychain-token-storage-GPAKRDKQ.js → keychain-token-storage-UJV5XP4Z.js} +2 -2
  139. package/chunks/{list-RVLIIKKL.js → list-HLCOEOSF.js} +33 -32
  140. package/chunks/{loadedSettingsAdapter-C63OUYZC.js → loadedSettingsAdapter-JHTVH3YO.js} +31 -30
  141. package/chunks/{loop-wakeup-G2EHBRGR.js → loop-wakeup-CUVIY2JY.js} +5 -5
  142. package/chunks/{ls-IDHUVSAG.js → ls-L654WOAT.js} +4 -4
  143. package/chunks/{lsp-FSRYXGFT.js → lsp-M35KE54Y.js} +2 -2
  144. package/chunks/mcp-2JWEFAIN.js +67 -0
  145. package/chunks/{monitor-FOG36ABM.js → monitor-5QKFZFDX.js} +26 -25
  146. package/chunks/nonInteractiveCli-W5W5PIHI.js +112 -0
  147. package/chunks/{notebook-edit-GC6NXRCX.js → notebook-edit-XAFBWORI.js} +27 -26
  148. package/chunks/{openaiContentGenerator-EXR3ZL3Z.js → openaiContentGenerator-NULJROYV.js} +13 -12
  149. package/chunks/{pidfile-FNZM2DDH.js → pidfile-3LT7PZSW.js} +27 -26
  150. package/chunks/{qwenContentGenerator-A7B6J4U7.js → qwenContentGenerator-KLSURVNH.js} +28 -27
  151. package/chunks/{qwenOAuth2-LWLA6MQG.js → qwenOAuth2-ZV7YLMND.js} +5 -5
  152. package/chunks/{read-file-6JL4ULMY.js → read-file-HDAM3RHJ.js} +8 -8
  153. package/chunks/{read-mcp-resource-PIJF24RO.js → read-mcp-resource-CNQHBSJM.js} +2 -2
  154. package/chunks/record-artifact-PP3LKHTS.js +343 -0
  155. package/chunks/{ripGrep-DIUGLJE5.js → ripGrep-W36NLR7M.js} +26 -25
  156. package/chunks/{run-qwen-serve-CME3AKM4.js → run-qwen-serve-IMI2CQB5.js} +72 -28
  157. package/chunks/{scheduler-L7F6GDDT.js → scheduler-I5NKE5UJ.js} +26 -25
  158. package/chunks/{send-message-33PYFMCT.js → send-message-OA6MV4EU.js} +3 -3
  159. package/chunks/{serve-SRCBOJGP.js → serve-JMHMOYBO.js} +33 -32
  160. package/chunks/{server-QSQ45QFR.js → server-QIYIN34N.js} +1358 -492
  161. package/chunks/{session-5DG6NXHZ.js → session-BLL6HB35.js} +68 -65
  162. package/chunks/{settings-3EEQI4MN.js → settings-ABECXCTY.js} +30 -29
  163. package/chunks/{shell-B7FNYF5S.js → shell-A2TQI3U4.js} +26 -25
  164. package/chunks/{skill-AAOGHD5O.js → skill-4GTWZZWR.js} +10 -10
  165. package/chunks/{spawnChannel-3CBN3ZLK.js → spawnChannel-DFUVGP53.js} +30 -29
  166. package/chunks/{src-T5A7P7FS.js → src-ZORBMUM7.js} +77 -43
  167. package/chunks/{startInteractiveUI-2FGEQ6AS.js → startInteractiveUI-FH5MVXUE.js} +449 -175
  168. package/chunks/{syntheticOutput-YOSWIHDJ.js → syntheticOutput-F7LSDCAR.js} +3 -3
  169. package/chunks/{task-create-NHS2NGB3.js → task-create-RPFZNB4B.js} +7 -7
  170. package/chunks/{task-list-5UIB564R.js → task-list-TGGJX7EA.js} +5 -5
  171. package/chunks/{task-stop-HGXZ2VZ3.js → task-stop-W7NGTMYQ.js} +2 -2
  172. package/chunks/{task-update-IYYWUILG.js → task-update-5WUG5MVQ.js} +7 -7
  173. package/chunks/{team-create-KDF6W6PV.js → team-create-F76QOR6H.js} +26 -25
  174. package/chunks/{team-delete-FEKJCU4P.js → team-delete-UHQQ2QQX.js} +5 -5
  175. package/chunks/{team-plan-approval-PSFKQX7M.js → team-plan-approval-OKM7YZC2.js} +26 -25
  176. package/chunks/{theme-manager-5XOA2AO7.js → theme-manager-6R7EVWHW.js} +27 -26
  177. package/chunks/{todoWrite-TKMVZ2HT.js → todoWrite-C4ZAMSR3.js} +4 -4
  178. package/chunks/{tool-search-LFRILZXC.js → tool-search-UWXDKI7D.js} +9 -9
  179. package/chunks/{trustedFolders-7HQY6T6X.js → trustedFolders-DHASVVUP.js} +27 -26
  180. package/chunks/{validateNonInterActiveAuth-UC27B7BA.js → validateNonInterActiveAuth-56J7MOOP.js} +62 -59
  181. package/chunks/version-NE2I4PBN.js +10 -0
  182. package/chunks/{web-fetch-J4PIW36Q.js → web-fetch-UYGMOHW4.js} +5 -5
  183. package/chunks/{workflow-KUTESS6L.js → workflow-HGF3URV3.js} +35 -27
  184. package/chunks/{workspace-providers-status-LJT5AQDS.js → workspace-providers-status-KXB2OZFQ.js} +33 -32
  185. package/chunks/{workspace-service-YOBYVDXH.js → workspace-service-NSTI55BC.js} +39 -38
  186. package/chunks/workspace-skills-status-X5S6JGQZ.js +69 -0
  187. package/chunks/{write-file-FJYBZUHP.js → write-file-HVKT5VTX.js} +27 -26
  188. package/chunks/yargs-B64EYTCF.js +10 -0
  189. package/chunks/{zh-SLZNSSZB.js → zh-RS3HZHFH.js} +2 -0
  190. package/chunks/{zh-TW-P53C2SL5.js → zh-TW-L5GRQ4S4.js} +2 -0
  191. package/cli-entry.js +81 -8
  192. package/cli.js +348 -55
  193. package/locales/en.js +2 -0
  194. package/locales/zh-TW.js +2 -0
  195. package/locales/zh.js +2 -0
  196. package/node_modules/@qwen-code/audio-capture/package.json +1 -1
  197. package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-arm64/@qwen-code+audio-capture.node +0 -0
  198. package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-x64/@qwen-code+audio-capture.node +0 -0
  199. package/node_modules/@qwen-code/audio-capture/prebuilds/win32-x64/@qwen-code+audio-capture.node +0 -0
  200. package/package.json +3 -3
  201. package/web-shell/assets/{arc-D_rAuMDd.js → arc-Csx4m4N0.js} +1 -1
  202. package/web-shell/assets/{architectureDiagram-3BPJPVTR-DUCzbHVo.js → architectureDiagram-3BPJPVTR-BEa0vCM7.js} +1 -1
  203. package/web-shell/assets/{blockDiagram-GPEHLZMM-BEEBt1LL.js → blockDiagram-GPEHLZMM-BPzbKnn3.js} +1 -1
  204. package/web-shell/assets/{c4Diagram-AAUBKEIU-5ZJ_DeFL.js → c4Diagram-AAUBKEIU-B8YO71yP.js} +1 -1
  205. package/web-shell/assets/channel-QU1XUuVe.js +1 -0
  206. package/web-shell/assets/{chunk-2J33WTMH-Cfw7EiLn.js → chunk-2J33WTMH-32XhwSHt.js} +1 -1
  207. package/web-shell/assets/{chunk-4BX2VUAB-FGuZNCZY.js → chunk-4BX2VUAB-Bmoyo6PS.js} +1 -1
  208. package/web-shell/assets/{chunk-55IACEB6-CaBwYPBF.js → chunk-55IACEB6-TwH6PFhs.js} +1 -1
  209. package/web-shell/assets/{chunk-727SXJPM-BlR04OFz.js → chunk-727SXJPM-RI_IVUtZ.js} +1 -1
  210. package/web-shell/assets/{chunk-AQP2D5EJ-D3_mYJH5.js → chunk-AQP2D5EJ-DH6GIAZD.js} +1 -1
  211. package/web-shell/assets/{chunk-FMBD7UC4-7D4q4SbB.js → chunk-FMBD7UC4-DeD3k3D5.js} +1 -1
  212. package/web-shell/assets/{chunk-ND2GUHAM-BjX_Iwmr.js → chunk-ND2GUHAM-BLPb8nv3.js} +1 -1
  213. package/web-shell/assets/{chunk-QZHKN3VN-C3Hz_ZQh.js → chunk-QZHKN3VN-PZpyglW1.js} +1 -1
  214. package/web-shell/assets/classDiagram-4FO5ZUOK-fv1EWO-L.js +1 -0
  215. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-fv1EWO-L.js +1 -0
  216. package/web-shell/assets/{cose-bilkent-S5V4N54A-Ax4UycaT.js → cose-bilkent-S5V4N54A-Jsd0yKfz.js} +1 -1
  217. package/web-shell/assets/{dagre-BM42HDAG-C6hmPtGt.js → dagre-BM42HDAG-B-zADciZ.js} +1 -1
  218. package/web-shell/assets/{diagram-2AECGRRQ-tXZnxjmw.js → diagram-2AECGRRQ-Cn6GDxoo.js} +1 -1
  219. package/web-shell/assets/{diagram-5GNKFQAL-99dwIzQG.js → diagram-5GNKFQAL-udLgyJxO.js} +1 -1
  220. package/web-shell/assets/{diagram-KO2AKTUF-Bqhn0-2s.js → diagram-KO2AKTUF-EcLpfzy3.js} +1 -1
  221. package/web-shell/assets/{diagram-LMA3HP47-DzThVSvo.js → diagram-LMA3HP47-D-YFbYsJ.js} +1 -1
  222. package/web-shell/assets/{diagram-OG6HWLK6-DPE8vjln.js → diagram-OG6HWLK6-C8oRUATa.js} +1 -1
  223. package/web-shell/assets/{erDiagram-TEJ5UH35-KzUKgQCV.js → erDiagram-TEJ5UH35-DPH5nStc.js} +1 -1
  224. package/web-shell/assets/{flowDiagram-I6XJVG4X-DBN-DXw5.js → flowDiagram-I6XJVG4X-CSOTwkAt.js} +1 -1
  225. package/web-shell/assets/{ganttDiagram-6RSMTGT7-DuBsP9u8.js → ganttDiagram-6RSMTGT7-D0L988t0.js} +1 -1
  226. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-BjWHMyKR.js → gitGraphDiagram-PVQCEYII-B9Oo7gxS.js} +1 -1
  227. package/web-shell/assets/index-BYZZL5N7.css +5 -0
  228. package/web-shell/assets/index-C_oTyz0s.js +753 -0
  229. package/web-shell/assets/{infoDiagram-5YYISTIA-BVux0vP6.js → infoDiagram-5YYISTIA-Ddj4YN2P.js} +1 -1
  230. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-2qzw7f--.js → ishikawaDiagram-YF4QCWOH-CBjafcl7.js} +1 -1
  231. package/web-shell/assets/{journeyDiagram-JHISSGLW-Dnl65sxL.js → journeyDiagram-JHISSGLW-Bn3XD99u.js} +1 -1
  232. package/web-shell/assets/{kanban-definition-UN3LZRKU-BElSQYtY.js → kanban-definition-UN3LZRKU-D7PBPJmE.js} +1 -1
  233. package/web-shell/assets/{linear-BjoycSSY.js → linear-Bmfo22rZ.js} +1 -1
  234. package/web-shell/assets/{mermaid.core-xe6bvMRz.js → mermaid.core-xRkAI-EI.js} +5 -5
  235. package/web-shell/assets/{mindmap-definition-RKZ34NQL-D8DQgI4U.js → mindmap-definition-RKZ34NQL-B30zRinm.js} +1 -1
  236. package/web-shell/assets/{pieDiagram-4H26LBE5-Dwh7Gp7c.js → pieDiagram-4H26LBE5-DblnKR9Z.js} +1 -1
  237. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-BXKNBln2.js → quadrantDiagram-W4KKPZXB-COkHFKjF.js} +1 -1
  238. package/web-shell/assets/{requirementDiagram-4Y6WPE33-BzaC3cIA.js → requirementDiagram-4Y6WPE33-DDU3MMyn.js} +1 -1
  239. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-DP2t7eZk.js → sankeyDiagram-5OEKKPKP-Dt4lqEYv.js} +1 -1
  240. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-XlFiWEYv.js → sequenceDiagram-3UESZ5HK-BRzby5gN.js} +1 -1
  241. package/web-shell/assets/{stateDiagram-AJRCARHV-CupgUMJO.js → stateDiagram-AJRCARHV-CylzByc3.js} +1 -1
  242. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BKWZIKkM.js +1 -0
  243. package/web-shell/assets/{timeline-definition-PNZ67QCA-DstuGywa.js → timeline-definition-PNZ67QCA-BSzaNx6w.js} +1 -1
  244. package/web-shell/assets/{vennDiagram-CIIHVFJN-CnbNp6zu.js → vennDiagram-CIIHVFJN-BbzeKzCo.js} +1 -1
  245. package/web-shell/assets/{wardley-L42UT6IY-DnvoHq6R.js → wardley-L42UT6IY-BRJwCgE_.js} +1 -1
  246. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-Cz0xHEWB.js → wardleyDiagram-YWT4CUSO-BmAs0mJ-.js} +1 -1
  247. package/web-shell/assets/{xychartDiagram-2RQKCTM6-p36gfo88.js → xychartDiagram-2RQKCTM6-DqywSqVW.js} +1 -1
  248. package/web-shell/index.html +2 -2
  249. package/bundled/loop/SKILL.test.ts +0 -119
  250. package/bundled/loop/autonomous-loop.test.ts +0 -59
  251. package/bundled/loop/loop-task-file.test.ts +0 -1064
  252. package/bundled/loop/loop-tick-resolver.test.ts +0 -906
  253. package/chunks/chunk-EJ7BN3EN.js +0 -11396
  254. package/chunks/chunk-L7QPM2YD.js +0 -481
  255. package/chunks/daemon-status-provider-P72STMLB.js +0 -72
  256. package/chunks/nonInteractiveCli-6BYQSJTB.js +0 -109
  257. package/chunks/workspace-skills-status-ONEPXJV6.js +0 -62
  258. package/web-shell/assets/channel-B5ynH0q8.js +0 -1
  259. package/web-shell/assets/classDiagram-4FO5ZUOK-CK3ibywp.js +0 -1
  260. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-CK3ibywp.js +0 -1
  261. package/web-shell/assets/index-9qfvU0_r.css +0 -5
  262. package/web-shell/assets/index-CWzDaQT5.js +0 -717
  263. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-By3E4QwN.js +0 -1
  264. package/chunks/{chunk-UFC57OYT.js → chunk-VRDMOSQS.js} +54 -54
@@ -24,8 +24,12 @@ import {
24
24
  isNotCurrentlyGeneratingCancelError
25
25
  } from "./chunk-T22H4V4R.js";
26
26
  import {
27
+ LOAD_REPLAY_BULK_MODE,
28
+ LOAD_REPLAY_META_KEY,
29
+ LOAD_REPLAY_MODE_META_KEY,
30
+ LOAD_REPLAY_VERSION,
27
31
  MID_TURN_QUEUE_DRAIN_METHOD
28
- } from "./chunk-EWALXUAD.js";
32
+ } from "./chunk-AGHU5VRK.js";
29
33
  import {
30
34
  canonicalizeWorkspace
31
35
  } from "./chunk-SEZC2725.js";
@@ -33,10 +37,15 @@ import {
33
37
  DEFAULT_RING_SIZE,
34
38
  EVENT_SCHEMA_VERSION,
35
39
  EventBus
36
- } from "./chunk-ALX6BUYW.js";
40
+ } from "./chunk-NUGPZXAZ.js";
41
+ import {
42
+ ClientSideConnection,
43
+ PROTOCOL_VERSION,
44
+ RequestError
45
+ } from "./chunk-VRDMOSQS.js";
37
46
  import {
38
47
  defaultSpawnChannelFactory
39
- } from "./chunk-EROWZQN7.js";
48
+ } from "./chunk-XD3OLPII.js";
40
49
  import {
41
50
  BridgeChannelClosedError,
42
51
  BridgeTimeoutError,
@@ -45,20 +54,15 @@ import {
45
54
  STATUS_SCHEMA_VERSION,
46
55
  createIdleWorkspaceExtensionsStatus,
47
56
  createIdleWorkspaceHooksStatus
48
- } from "./chunk-LHMZCIKL.js";
49
- import {
50
- ClientSideConnection,
51
- PROTOCOL_VERSION,
52
- RequestError
53
- } from "./chunk-UFC57OYT.js";
57
+ } from "./chunk-FUW7YGJU.js";
54
58
  import {
55
59
  ShellExecutionService,
56
60
  TrustGateError
57
- } from "./chunk-6QTCOQX7.js";
61
+ } from "./chunk-WOG7QWIT.js";
58
62
  import {
59
63
  DAEMON_TRACEPARENT_META_KEY,
60
64
  DAEMON_TRACESTATE_META_KEY
61
- } from "./chunk-CHY5C5T4.js";
65
+ } from "./chunk-SJ2CTIYH.js";
62
66
  import {
63
67
  init_esbuild_shims
64
68
  } from "./chunk-A4BMJM77.js";
@@ -69,7 +73,7 @@ import {
69
73
  // packages/acp-bridge/src/bridge.ts
70
74
  init_esbuild_shims();
71
75
  import { randomUUID as randomUUID2 } from "node:crypto";
72
- import * as path2 from "node:path";
76
+ import * as path3 from "node:path";
73
77
 
74
78
  // packages/acp-bridge/src/compactionEngine.ts
75
79
  init_esbuild_shims();
@@ -943,8 +947,8 @@ var MultiClientPermissionMediator = class {
943
947
  }
944
948
  this.resolved.set(record.requestId, record);
945
949
  while (this.resolvedOrder.length > MAX_RESOLVED_PERMISSION_RECORDS) {
946
- const oldest = this.resolvedOrder.shift();
947
- if (oldest !== void 0) this.resolved.delete(oldest);
950
+ const oldest2 = this.resolvedOrder.shift();
951
+ if (oldest2 !== void 0) this.resolved.delete(oldest2);
948
952
  }
949
953
  }
950
954
  safeEmit(sessionId, event) {
@@ -1035,10 +1039,108 @@ var MultiClientPermissionMediator = class {
1035
1039
  };
1036
1040
 
1037
1041
  // packages/acp-bridge/src/bridgeClient.ts
1042
+ var PUBLISH_ARTIFACT_TOOL_NAME = "artifact";
1038
1043
  function isFsErrorShape(err) {
1039
1044
  return err instanceof Error && err.name === "FsError" && typeof err.kind === "string";
1040
1045
  }
1041
1046
  __name(isFsErrorShape, "isFsErrorShape");
1047
+ function isRecord(value) {
1048
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1049
+ }
1050
+ __name(isRecord, "isRecord");
1051
+ function artifactPayloadFields(artifact) {
1052
+ return {
1053
+ title: artifact["title"],
1054
+ kind: artifact["kind"],
1055
+ storage: artifact["storage"],
1056
+ description: artifact["description"],
1057
+ workspacePath: artifact["workspacePath"],
1058
+ managedId: artifact["managedId"],
1059
+ url: artifact["url"],
1060
+ mimeType: artifact["mimeType"],
1061
+ sizeBytes: artifact["sizeBytes"],
1062
+ metadata: artifact["metadata"]
1063
+ };
1064
+ }
1065
+ __name(artifactPayloadFields, "artifactPayloadFields");
1066
+ function extractCappedArtifactInputs(rawArtifacts, limit, sessionId, source, toInput) {
1067
+ const artifacts = [];
1068
+ for (let index = 0; index < rawArtifacts.length; index++) {
1069
+ const artifact = rawArtifacts[index];
1070
+ if (!isRecord(artifact)) {
1071
+ writeStderrLine(
1072
+ `[artifacts] session=${sessionId} action=dropped reason=malformed source=${source} index=${index}`
1073
+ );
1074
+ continue;
1075
+ }
1076
+ if (artifacts.length >= limit) {
1077
+ writeStderrLine(
1078
+ `[artifacts] session=${sessionId} action=dropped reason="artifact batch limit exceeded" source=${source} dropped=${rawArtifacts.length - index}`
1079
+ );
1080
+ break;
1081
+ }
1082
+ artifacts.push(toInput(artifact));
1083
+ }
1084
+ return artifacts;
1085
+ }
1086
+ __name(extractCappedArtifactInputs, "extractCappedArtifactInputs");
1087
+ function artifactIngestionErrorReason(error) {
1088
+ if (!(error instanceof Error)) {
1089
+ return String(error);
1090
+ }
1091
+ return {
1092
+ name: error.name,
1093
+ message: error.message,
1094
+ stack: error.stack?.split("\n").slice(0, 4).join("\n")
1095
+ };
1096
+ }
1097
+ __name(artifactIngestionErrorReason, "artifactIngestionErrorReason");
1098
+ function extractSessionUpdateArtifacts(params, updateMeta, limit, sessionId) {
1099
+ const rawArtifacts = updateMeta?.["artifacts"];
1100
+ if (!Array.isArray(rawArtifacts)) {
1101
+ return [];
1102
+ }
1103
+ const update = params.update;
1104
+ if (update.sessionUpdate !== "tool_call_update" || update.status !== "completed" && update.status !== "failed" && update.status !== "cancelled") {
1105
+ return [];
1106
+ }
1107
+ const toolCallId = typeof update.toolCallId === "string" ? update.toolCallId : void 0;
1108
+ const toolName = typeof updateMeta?.["toolName"] === "string" ? updateMeta["toolName"] : void 0;
1109
+ return extractCappedArtifactInputs(
1110
+ rawArtifacts,
1111
+ limit,
1112
+ sessionId,
1113
+ "tool",
1114
+ (artifact) => ({
1115
+ ...artifactPayloadFields(artifact),
1116
+ source: "tool",
1117
+ toolCallId,
1118
+ toolName
1119
+ })
1120
+ );
1121
+ }
1122
+ __name(extractSessionUpdateArtifacts, "extractSessionUpdateArtifacts");
1123
+ function sanitizeSessionUpdateArtifacts(params, updateMeta) {
1124
+ if (!Array.isArray(updateMeta?.["artifacts"])) {
1125
+ return params;
1126
+ }
1127
+ const sanitizedMeta = { ...updateMeta };
1128
+ delete sanitizedMeta["artifacts"];
1129
+ const update = {
1130
+ ...params.update,
1131
+ _meta: sanitizedMeta
1132
+ };
1133
+ return {
1134
+ ...params,
1135
+ update
1136
+ };
1137
+ }
1138
+ __name(sanitizeSessionUpdateArtifacts, "sanitizeSessionUpdateArtifacts");
1139
+ function isTrustedArtifactToolUpdate(params, updateMeta) {
1140
+ const update = params.update;
1141
+ return update.sessionUpdate === "tool_call_update" && update.status === "completed" && updateMeta?.["toolName"] === PUBLISH_ARTIFACT_TOOL_NAME;
1142
+ }
1143
+ __name(isTrustedArtifactToolUpdate, "isTrustedArtifactToolUpdate");
1042
1144
  function preserveFsErrorOverAcp(err) {
1043
1145
  if (isFsErrorShape(err)) {
1044
1146
  throw new RequestError(-32603, err.message, {
@@ -1100,7 +1202,7 @@ function sliceLineRange(content, startLine, endLine) {
1100
1202
  }
1101
1203
  __name(sliceLineRange, "sliceLineRange");
1102
1204
  var BridgeClient = class {
1103
- constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender) {
1205
+ constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender, ownsSession = () => true) {
1104
1206
  this.resolveEntry = resolveEntry;
1105
1207
  this.resolvePendingRestoreEvents = resolvePendingRestoreEvents;
1106
1208
  this.mediator = mediator;
@@ -1110,6 +1212,7 @@ var BridgeClient = class {
1110
1212
  this.onModelPromoted = onModelPromoted;
1111
1213
  this.onModePromoted = onModePromoted;
1112
1214
  this.clientMcpSender = clientMcpSender;
1215
+ this.ownsSession = ownsSession;
1113
1216
  }
1114
1217
  static {
1115
1218
  __name(this, "BridgeClient");
@@ -1163,14 +1266,32 @@ var BridgeClient = class {
1163
1266
  }
1164
1267
  }
1165
1268
  async sessionUpdate(params) {
1269
+ if (!this.ownsSession(params.sessionId) && !this.inFlightRestoreIds.has(params.sessionId)) {
1270
+ writeStderrLine(
1271
+ `[demux] session=${params.sessionId} type=session_update action=dropped reason=session_not_owned`
1272
+ );
1273
+ return;
1274
+ }
1166
1275
  const entry = this.resolveEntry(params.sessionId);
1167
1276
  const events = entry?.events ?? this.resolvePendingRestoreEvents(params.sessionId);
1168
1277
  if (!events) return;
1278
+ const prepared = this.prepareSessionUpdateFrames(params, entry);
1279
+ for (const frame of prepared.frames) {
1280
+ events.publish(frame);
1281
+ }
1282
+ if (entry && prepared.artifacts.length > 0) {
1283
+ await this.upsertAndPublishArtifacts(entry, prepared.artifacts, {
1284
+ trustedPublisher: prepared.trustedPublisher
1285
+ });
1286
+ }
1287
+ }
1288
+ prepareSessionUpdateFrames(params, entry) {
1169
1289
  const originator = entry?.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {};
1290
+ const frames = [];
1170
1291
  const a2ui = extractA2uiToolUpdate(params);
1171
1292
  if (a2ui) {
1172
1293
  for (const surface of a2ui.surfaces) {
1173
- events.publish({
1294
+ frames.push({
1174
1295
  type: "session_update",
1175
1296
  data: {
1176
1297
  sessionId: params.sessionId,
@@ -1192,12 +1313,47 @@ var BridgeClient = class {
1192
1313
  const updateMeta = params.update._meta;
1193
1314
  const originalTs = updateMeta?.["serverTimestamp"] ?? updateMeta?.["timestamp"];
1194
1315
  const serverTimestamp = typeof originalTs === "number" && Number.isFinite(originalTs) ? originalTs : void 0;
1195
- events.publish({
1316
+ const artifacts = entry?.artifacts ? extractSessionUpdateArtifacts(
1317
+ params,
1318
+ updateMeta,
1319
+ entry.artifacts.inputBatchLimit(),
1320
+ entry.sessionId
1321
+ ) : [];
1322
+ const publishParams = sanitizeSessionUpdateArtifacts(params, updateMeta);
1323
+ frames.push({
1196
1324
  type: "session_update",
1197
- data: params,
1325
+ data: publishParams,
1198
1326
  ...originator,
1199
1327
  ...serverTimestamp !== void 0 ? { _meta: { serverTimestamp } } : {}
1200
1328
  });
1329
+ return {
1330
+ frames,
1331
+ artifacts,
1332
+ trustedPublisher: isTrustedArtifactToolUpdate(params, updateMeta)
1333
+ };
1334
+ }
1335
+ async seedSessionUpdates(entry, updates) {
1336
+ const frames = [];
1337
+ const artifactBatches = [];
1338
+ for (const update of updates) {
1339
+ const prepared = this.prepareSessionUpdateFrames(
1340
+ { sessionId: entry.sessionId, update },
1341
+ entry
1342
+ );
1343
+ frames.push(...prepared.frames);
1344
+ if (prepared.artifacts.length > 0) {
1345
+ artifactBatches.push({
1346
+ artifacts: prepared.artifacts,
1347
+ trustedPublisher: prepared.trustedPublisher
1348
+ });
1349
+ }
1350
+ }
1351
+ entry.events.seedReplayEvents(frames);
1352
+ for (const batch of artifactBatches) {
1353
+ await this.upsertAndPublishArtifacts(entry, batch.artifacts, {
1354
+ trustedPublisher: batch.trustedPublisher
1355
+ });
1356
+ }
1201
1357
  }
1202
1358
  /**
1203
1359
  * Bounded early-event buffer. Frames are keyed by sessionId; each
@@ -1335,11 +1491,12 @@ var BridgeClient = class {
1335
1491
  return { payload: response };
1336
1492
  }
1337
1493
  /**
1338
- * Handle child->bridge ACP `extNotification` calls. Six methods are
1339
- * recognized — `qwen/notify/session/model-update`,
1494
+ * Handle child->bridge ACP `extNotification` calls. Recognized methods are
1495
+ * `qwen/notify/session/model-update`,
1340
1496
  * `qwen/notify/session/mode-update`,
1341
1497
  * `qwen/notify/session/title-update` (auto/in-process session titles),
1342
1498
  * `qwen/notify/session/prompt-suggestion` (followup assist),
1499
+ * `qwen/notify/session/artifact-event` (hook artifacts),
1343
1500
  * `qwen/notify/session/terminal-sequence`, and
1344
1501
  * `qwen/notify/session/mcp-budget-event` — each translated into a
1345
1502
  * session-scoped SSE frame. Unknown methods are dropped silently
@@ -1401,6 +1558,10 @@ var BridgeClient = class {
1401
1558
  this.publishExtNotification(sessionId2, "terminal_sequence", rest2);
1402
1559
  return;
1403
1560
  }
1561
+ if (method === "qwen/notify/session/artifact-event") {
1562
+ await this.handleArtifactEvent(params);
1563
+ return;
1564
+ }
1404
1565
  if (method !== "qwen/notify/session/mcp-budget-event") return;
1405
1566
  const sessionId = params["sessionId"];
1406
1567
  if (typeof sessionId !== "string") return;
@@ -1419,6 +1580,67 @@ var BridgeClient = class {
1419
1580
  void _kind;
1420
1581
  this.publishExtNotification(sessionId, type, rest);
1421
1582
  }
1583
+ async handleArtifactEvent(params) {
1584
+ const sessionId = params["sessionId"];
1585
+ const rawArtifacts = params["artifacts"];
1586
+ if (typeof sessionId !== "string" || !Array.isArray(rawArtifacts)) {
1587
+ writeStderrLine(
1588
+ `[demux] session=${typeof sessionId === "string" ? sessionId : "<missing>"} type=artifact_event action=dropped reason=malformed`
1589
+ );
1590
+ return;
1591
+ }
1592
+ if (!this.ownsSession(sessionId) && !this.inFlightRestoreIds.has(sessionId)) {
1593
+ writeStderrLine(
1594
+ `[demux] session=${sessionId} type=artifact_event action=dropped reason=session_not_owned`
1595
+ );
1596
+ return;
1597
+ }
1598
+ const entry = this.resolveEntry(sessionId);
1599
+ if (!entry) {
1600
+ writeStderrLine(
1601
+ `[demux] session=${sessionId} type=artifact_event action=dropped reason=session_not_found`
1602
+ );
1603
+ return;
1604
+ }
1605
+ const hookEventName = typeof params["hookEventName"] === "string" ? params["hookEventName"] : void 0;
1606
+ const toolName = typeof params["toolName"] === "string" ? params["toolName"] : void 0;
1607
+ const toolCallId = typeof params["toolCallId"] === "string" ? params["toolCallId"] : void 0;
1608
+ const artifacts = extractCappedArtifactInputs(
1609
+ rawArtifacts,
1610
+ entry.artifacts.inputBatchLimit(),
1611
+ entry.sessionId,
1612
+ "hook",
1613
+ (artifact) => ({
1614
+ ...artifactPayloadFields(artifact),
1615
+ source: "hook",
1616
+ hookEventName,
1617
+ toolName,
1618
+ toolCallId
1619
+ })
1620
+ );
1621
+ await this.upsertAndPublishArtifacts(entry, artifacts);
1622
+ }
1623
+ async upsertAndPublishArtifacts(entry, artifacts, options) {
1624
+ try {
1625
+ const result = await entry.artifacts.upsertMany(artifacts, options);
1626
+ this.publishArtifactChanges(entry, result.changes);
1627
+ } catch (error) {
1628
+ writeStderrLine(
1629
+ `[artifacts] session=${entry.sessionId} action=dropped reason=${JSON.stringify(
1630
+ artifactIngestionErrorReason(error)
1631
+ )}`
1632
+ );
1633
+ }
1634
+ }
1635
+ publishArtifactChanges(entry, changes) {
1636
+ for (const change of changes) {
1637
+ entry.events.publish({
1638
+ type: "artifact_changed",
1639
+ data: { sessionId: entry.sessionId, change },
1640
+ ...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
1641
+ });
1642
+ }
1643
+ }
1422
1644
  publishExtNotification(sessionId, type, data) {
1423
1645
  const entry = this.resolveEntry(sessionId);
1424
1646
  const frame = {
@@ -1858,6 +2080,1005 @@ function extractA2uiToolUpdate(params) {
1858
2080
  }
1859
2081
  __name(extractA2uiToolUpdate, "extractA2uiToolUpdate");
1860
2082
 
2083
+ // packages/acp-bridge/src/sessionArtifacts.ts
2084
+ init_esbuild_shims();
2085
+ import { createHash } from "node:crypto";
2086
+ import { promises as fs2 } from "node:fs";
2087
+ import path2 from "node:path";
2088
+ var SOURCE_RESERVATIONS = {
2089
+ tool: 100,
2090
+ client: 50,
2091
+ hook: 50
2092
+ };
2093
+ var WORKSPACE_STATUS_REFRESH_TTL_MS = 5e3;
2094
+ var WORKSPACE_STATUS_REFRESH_BATCH_SIZE = 20;
2095
+ var SessionArtifactValidationError = class extends Error {
2096
+ constructor(message, field) {
2097
+ super(message);
2098
+ this.field = field;
2099
+ this.name = "SessionArtifactValidationError";
2100
+ }
2101
+ static {
2102
+ __name(this, "SessionArtifactValidationError");
2103
+ }
2104
+ code = "VALIDATION_FAILED";
2105
+ };
2106
+ var SessionArtifactStore = class {
2107
+ static {
2108
+ __name(this, "SessionArtifactStore");
2109
+ }
2110
+ sessionId;
2111
+ workspaceCwd;
2112
+ maxArtifacts;
2113
+ artifacts = /* @__PURE__ */ new Map();
2114
+ receivedSeq = 0;
2115
+ insertSeq = 0;
2116
+ realWorkspaceCwdPromise;
2117
+ operationQueue = Promise.resolve();
2118
+ constructor(options) {
2119
+ this.sessionId = options.sessionId;
2120
+ this.workspaceCwd = options.workspaceCwd;
2121
+ this.maxArtifacts = options.maxArtifacts ?? 200;
2122
+ }
2123
+ inputBatchLimit() {
2124
+ return this.maxArtifacts * 2;
2125
+ }
2126
+ async list() {
2127
+ return this.enqueue(async () => {
2128
+ await this.refreshWorkspaceStatuses();
2129
+ return {
2130
+ v: 1,
2131
+ sessionId: this.sessionId,
2132
+ artifacts: Array.from(this.artifacts.values()).sort((a, b) => a.insertSeq - b.insertSeq).map(toPublicArtifact),
2133
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2134
+ limits: { maxArtifacts: this.maxArtifacts }
2135
+ };
2136
+ });
2137
+ }
2138
+ async upsertMany(inputs, options = {}) {
2139
+ return this.enqueue(async () => {
2140
+ const normalizedResults = [];
2141
+ for (const input of inputs) {
2142
+ try {
2143
+ normalizedResults.push(
2144
+ await this.normalizeInput(
2145
+ input,
2146
+ ++this.receivedSeq,
2147
+ options.trustedPublisher === true
2148
+ )
2149
+ );
2150
+ } catch (error) {
2151
+ if (options.strict) {
2152
+ throw error;
2153
+ }
2154
+ const message = error instanceof Error ? error.message : String(error);
2155
+ writeStderrLine(
2156
+ `[artifacts] session=${this.sessionId} action=dropped reason=${JSON.stringify(
2157
+ message
2158
+ )}`
2159
+ );
2160
+ }
2161
+ }
2162
+ const changes = [];
2163
+ for (const artifact of coalesceByIdentity(normalizedResults)) {
2164
+ const existing = this.artifacts.get(artifact.id) ?? this.findPublishedUpgradeTarget(artifact) ?? this.findPublishedWorkspaceTarget(artifact);
2165
+ if (!existing) {
2166
+ const stored = {
2167
+ ...artifact,
2168
+ insertSeq: ++this.insertSeq
2169
+ };
2170
+ this.artifacts.set(stored.id, stored);
2171
+ changes.push({
2172
+ action: "created",
2173
+ artifactId: stored.id,
2174
+ artifact: toPublicArtifact(stored)
2175
+ });
2176
+ continue;
2177
+ }
2178
+ const updated = mergeArtifact(existing, artifact);
2179
+ if (updated.changed) {
2180
+ if (updated.artifact.id !== existing.id) {
2181
+ this.artifacts.delete(existing.id);
2182
+ }
2183
+ this.artifacts.set(updated.artifact.id, updated.artifact);
2184
+ changes.push({
2185
+ action: "updated",
2186
+ artifactId: updated.artifact.id,
2187
+ artifact: toPublicArtifact(updated.artifact)
2188
+ });
2189
+ }
2190
+ }
2191
+ const createdIds = new Set(
2192
+ changes.filter((change) => change.action === "created").map((change) => change.artifactId)
2193
+ );
2194
+ changes.push(...await this.evictOverflow(createdIds, changes));
2195
+ return { v: 1, sessionId: this.sessionId, changes };
2196
+ });
2197
+ }
2198
+ findPublishedUpgradeTarget(artifact) {
2199
+ if (artifact.storage !== "published" || !artifact.trustedPublisher || !artifact.managedId || !artifact.url) {
2200
+ return void 0;
2201
+ }
2202
+ const byUrl = this.artifacts.get(
2203
+ stableArtifactId(this.sessionId, `url:${artifact.url}`)
2204
+ );
2205
+ if (byUrl && (byUrl.storage !== "published" || byUrl.managedId === artifact.managedId)) {
2206
+ return byUrl;
2207
+ }
2208
+ for (const existing of this.artifacts.values()) {
2209
+ if (existing.storage === "workspace" && existing.workspacePath && artifact.managedId === managedIdForWorkspacePath(
2210
+ this.workspaceCwd,
2211
+ existing.workspacePath
2212
+ ) || existing.storage === "published" && existing.managedId === artifact.managedId) {
2213
+ return existing;
2214
+ }
2215
+ }
2216
+ return void 0;
2217
+ }
2218
+ findPublishedWorkspaceTarget(artifact) {
2219
+ if (artifact.storage !== "workspace" || !artifact.workspacePath) {
2220
+ return void 0;
2221
+ }
2222
+ const managedId = managedIdForWorkspacePath(
2223
+ this.workspaceCwd,
2224
+ artifact.workspacePath
2225
+ );
2226
+ for (const existing of this.artifacts.values()) {
2227
+ if (existing.storage === "published" && existing.managedId === managedId) {
2228
+ return existing;
2229
+ }
2230
+ }
2231
+ return void 0;
2232
+ }
2233
+ async remove(artifactId, options) {
2234
+ return this.enqueue(async () => {
2235
+ const existing = this.artifacts.get(artifactId);
2236
+ if (!existing) {
2237
+ return { v: 1, sessionId: this.sessionId, changes: [] };
2238
+ }
2239
+ if (existing.source === "client" && existing.clientId !== void 0 && existing.clientId !== options?.clientId) {
2240
+ writeStderrLine(
2241
+ `[artifacts] session=${this.sessionId} action=remove_denied artifactId=${artifactId} owner=${existing.clientId} requester=${options?.clientId ?? "<anonymous>"}`
2242
+ );
2243
+ return { v: 1, sessionId: this.sessionId, changes: [] };
2244
+ }
2245
+ this.artifacts.delete(artifactId);
2246
+ return {
2247
+ v: 1,
2248
+ sessionId: this.sessionId,
2249
+ changes: [
2250
+ {
2251
+ action: "removed",
2252
+ artifactId,
2253
+ artifact: toPublicArtifact(existing),
2254
+ reason: "explicit"
2255
+ }
2256
+ ]
2257
+ };
2258
+ });
2259
+ }
2260
+ enqueue(operation) {
2261
+ const result = this.operationQueue.then(operation, operation);
2262
+ this.operationQueue = result.then(
2263
+ () => void 0,
2264
+ () => void 0
2265
+ );
2266
+ return result;
2267
+ }
2268
+ async normalizeInput(input, receivedSeq, trustedPublisherFromCaller) {
2269
+ if (!input || typeof input !== "object") {
2270
+ throw new SessionArtifactValidationError("Artifact must be an object");
2271
+ }
2272
+ const title = normalizeString(input.title, "title", 200, true);
2273
+ const description = normalizeString(
2274
+ input.description,
2275
+ "description",
2276
+ 1e3,
2277
+ false
2278
+ );
2279
+ const source = input.source ?? "tool";
2280
+ if (source !== "tool" && source !== "hook" && source !== "client") {
2281
+ throw new SessionArtifactValidationError(
2282
+ "source must be tool, hook, or client",
2283
+ "source"
2284
+ );
2285
+ }
2286
+ const trustedPublisher = trustedPublisherFromCaller;
2287
+ const workspacePath = input.workspacePath ? normalizeWorkspacePath(input.workspacePath, this.workspaceCwd) : void 0;
2288
+ const managedId = normalizeManagedId(input.managedId);
2289
+ const rawStorage = input.storage;
2290
+ const storage = inferStorage(rawStorage, {
2291
+ workspacePath,
2292
+ managedId,
2293
+ url: input.url,
2294
+ trustedPublisher
2295
+ });
2296
+ const url = input.url ? normalizeArtifactUrl(input.url, trustedPublisher) : void 0;
2297
+ validateLocator(storage, {
2298
+ workspacePath,
2299
+ managedId,
2300
+ url,
2301
+ trustedPublisher
2302
+ });
2303
+ const metadata = normalizeMetadata(input.metadata);
2304
+ const workspaceStatus = workspacePath ? await this.getInitialWorkspaceStatus(workspacePath) : void 0;
2305
+ if (workspaceStatus?.escaped) {
2306
+ throw new SessionArtifactValidationError(
2307
+ "workspacePath must stay inside the workspace",
2308
+ "workspacePath"
2309
+ );
2310
+ }
2311
+ const kind = normalizeKind(
2312
+ input.kind ?? inferKind({ storage, workspacePath, url })
2313
+ );
2314
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2315
+ const identityKey = buildIdentityKey({
2316
+ storage,
2317
+ workspacePath,
2318
+ managedId,
2319
+ url
2320
+ });
2321
+ const id = stableArtifactId(this.sessionId, identityKey);
2322
+ return {
2323
+ id,
2324
+ identityKey,
2325
+ receivedSeq,
2326
+ retentionSource: source,
2327
+ trustedPublisher,
2328
+ kind,
2329
+ storage,
2330
+ source,
2331
+ status: workspaceStatus?.status ?? "available",
2332
+ ...workspacePath ? { lastStatAt: Date.now() } : {},
2333
+ title,
2334
+ description,
2335
+ workspacePath,
2336
+ managedId,
2337
+ url,
2338
+ mimeType: normalizeString(input.mimeType, "mimeType", 120, false),
2339
+ sizeBytes: input.sizeBytes !== void 0 ? normalizeSizeBytes(input.sizeBytes) : workspaceStatus?.sizeBytes,
2340
+ metadata,
2341
+ clientRetained: source === "client",
2342
+ createdAt: now,
2343
+ updatedAt: now,
2344
+ toolCallId: normalizeString(input.toolCallId, "toolCallId", 200, false),
2345
+ toolName: normalizeString(input.toolName, "toolName", 200, false),
2346
+ hookEventName: normalizeString(
2347
+ input.hookEventName,
2348
+ "hookEventName",
2349
+ 200,
2350
+ false
2351
+ ),
2352
+ clientId: normalizeString(input.clientId, "clientId", 200, false)
2353
+ };
2354
+ }
2355
+ async refreshWorkspaceStatuses() {
2356
+ const now = Date.now();
2357
+ const staleWorkspaceArtifacts = Array.from(this.artifacts.values()).filter((artifact) => artifact.workspacePath).filter((artifact) => shouldRefreshWorkspaceStatus(artifact, now));
2358
+ await runInBatches(
2359
+ staleWorkspaceArtifacts,
2360
+ WORKSPACE_STATUS_REFRESH_BATCH_SIZE,
2361
+ (artifact) => this.refreshWorkspaceStatus(artifact, {
2362
+ onError: "missing",
2363
+ now
2364
+ })
2365
+ );
2366
+ }
2367
+ async getInitialWorkspaceStatus(workspacePath) {
2368
+ try {
2369
+ return await getWorkspaceStatus(
2370
+ this.workspaceCwd,
2371
+ workspacePath,
2372
+ this.getRealWorkspaceCwd()
2373
+ );
2374
+ } catch (error) {
2375
+ const reason = error instanceof Error ? error.message : String(error);
2376
+ throw new SessionArtifactValidationError(
2377
+ `workspacePath could not be inspected: ${reason}`,
2378
+ "workspacePath"
2379
+ );
2380
+ }
2381
+ }
2382
+ async refreshWorkspaceStatus(artifact, options) {
2383
+ if (!artifact.workspacePath) {
2384
+ return;
2385
+ }
2386
+ try {
2387
+ const status = await getWorkspaceStatus(
2388
+ this.workspaceCwd,
2389
+ artifact.workspacePath,
2390
+ this.getRealWorkspaceCwd()
2391
+ );
2392
+ artifact.status = status.status;
2393
+ artifact.sizeBytes = status.sizeBytes;
2394
+ if (status.escaped) {
2395
+ artifact.status = "missing";
2396
+ artifact.sizeBytes = void 0;
2397
+ delete artifact.workspacePath;
2398
+ }
2399
+ artifact.lastStatAt = options.now ?? Date.now();
2400
+ } catch (error) {
2401
+ writeStderrLine(
2402
+ `[artifacts] session=${this.sessionId} action=status_refresh_failed artifactId=${artifact.id} reason=${JSON.stringify(
2403
+ error instanceof Error ? error.message : String(error)
2404
+ )}`
2405
+ );
2406
+ if (options.onError === "missing") {
2407
+ artifact.status = "missing";
2408
+ artifact.sizeBytes = void 0;
2409
+ artifact.lastStatAt = options.now ?? Date.now();
2410
+ }
2411
+ return;
2412
+ }
2413
+ }
2414
+ getRealWorkspaceCwd() {
2415
+ if (!this.realWorkspaceCwdPromise) {
2416
+ const promise = fs2.realpath(this.workspaceCwd).catch((error) => {
2417
+ if (this.realWorkspaceCwdPromise === promise) {
2418
+ this.realWorkspaceCwdPromise = void 0;
2419
+ }
2420
+ throw error;
2421
+ });
2422
+ this.realWorkspaceCwdPromise = promise;
2423
+ }
2424
+ return this.realWorkspaceCwdPromise;
2425
+ }
2426
+ async evictOverflow(createdIds, changes) {
2427
+ const removed = [];
2428
+ if (this.artifacts.size <= this.maxArtifacts) {
2429
+ return removed;
2430
+ }
2431
+ const createdInThisBatch = new Set(createdIds);
2432
+ const candidates = Array.from(this.artifacts.values()).filter(
2433
+ (artifact) => !createdInThisBatch.has(artifact.id)
2434
+ );
2435
+ const now = Date.now();
2436
+ const staleWorkspaceCandidates = candidates.filter((artifact) => artifact.workspacePath).filter((artifact) => shouldRefreshWorkspaceStatus(artifact, now));
2437
+ await runInBatches(
2438
+ staleWorkspaceCandidates,
2439
+ WORKSPACE_STATUS_REFRESH_BATCH_SIZE,
2440
+ (artifact) => this.refreshWorkspaceStatus(artifact, { onError: "preserve", now })
2441
+ );
2442
+ const sourceCounts = countByRetentionSource(this.artifacts.values());
2443
+ while (this.artifacts.size > this.maxArtifacts) {
2444
+ const artifact = selectEvictionCandidate(candidates, sourceCounts);
2445
+ if (!artifact) break;
2446
+ this.artifacts.delete(artifact.id);
2447
+ candidates.splice(candidates.indexOf(artifact), 1);
2448
+ sourceCounts[artifact.retentionSource]--;
2449
+ removePriorChange(changes, artifact.id);
2450
+ removed.push({
2451
+ action: "removed",
2452
+ artifactId: artifact.id,
2453
+ artifact: toPublicArtifact(artifact),
2454
+ reason: "eviction"
2455
+ });
2456
+ }
2457
+ const overflowCreated = Array.from(this.artifacts.values()).filter((artifact) => createdInThisBatch.has(artifact.id)).sort((a, b) => b.receivedSeq - a.receivedSeq);
2458
+ for (const artifact of overflowCreated) {
2459
+ if (this.artifacts.size <= this.maxArtifacts) {
2460
+ break;
2461
+ }
2462
+ this.artifacts.delete(artifact.id);
2463
+ writeStderrLine(
2464
+ `[artifacts] session=${this.sessionId} action=dropped reason="max artifacts exceeded" artifactId=${artifact.id}`
2465
+ );
2466
+ removePriorChange(changes, artifact.id);
2467
+ }
2468
+ return removed;
2469
+ }
2470
+ };
2471
+ function coalesceByIdentity(artifacts) {
2472
+ const byId = /* @__PURE__ */ new Map();
2473
+ for (const artifact of artifacts) {
2474
+ const existing = byId.get(artifact.id);
2475
+ if (!existing) {
2476
+ byId.set(artifact.id, artifact);
2477
+ continue;
2478
+ }
2479
+ byId.set(artifact.id, mergeBatchArtifact(existing, artifact));
2480
+ }
2481
+ return Array.from(byId.values()).sort(
2482
+ (a, b) => a.receivedSeq - b.receivedSeq
2483
+ );
2484
+ }
2485
+ __name(coalesceByIdentity, "coalesceByIdentity");
2486
+ function mergeBatchArtifact(existing, next) {
2487
+ const publishedUpgrade = existing.storage !== "published" && next.storage === "published" && next.trustedPublisher;
2488
+ if (publishedUpgrade) {
2489
+ const merged = {
2490
+ ...existing,
2491
+ id: next.id,
2492
+ identityKey: next.identityKey,
2493
+ kind: next.kind,
2494
+ storage: "published",
2495
+ status: next.status,
2496
+ title: next.title,
2497
+ description: next.description,
2498
+ managedId: next.managedId ?? existing.managedId,
2499
+ url: next.url ?? existing.url,
2500
+ mimeType: next.mimeType ?? existing.mimeType,
2501
+ sizeBytes: next.sizeBytes ?? existing.sizeBytes,
2502
+ metadata: mergeMetadata(existing, next),
2503
+ trustedPublisher: true,
2504
+ createdAt: existing.createdAt,
2505
+ receivedSeq: existing.receivedSeq,
2506
+ retentionSource: existing.retentionSource,
2507
+ clientRetained: existing.clientRetained || next.clientRetained,
2508
+ lastStatAt: void 0
2509
+ };
2510
+ delete merged.workspacePath;
2511
+ return merged;
2512
+ }
2513
+ return {
2514
+ ...existing,
2515
+ status: next.status,
2516
+ sizeBytes: mergeSizeBytes(existing, next),
2517
+ metadata: mergeMetadata(existing, next),
2518
+ clientRetained: existing.clientRetained || next.clientRetained,
2519
+ trustedPublisher: existing.trustedPublisher || next.trustedPublisher,
2520
+ lastStatAt: next.lastStatAt ?? existing.lastStatAt
2521
+ };
2522
+ }
2523
+ __name(mergeBatchArtifact, "mergeBatchArtifact");
2524
+ function mergeArtifact(existing, incoming) {
2525
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2526
+ const publishedUpgrade = existing.storage !== "published" && incoming.storage === "published" && incoming.trustedPublisher;
2527
+ const publishedRefresh = existing.storage === "published" && incoming.storage === "published" && incoming.trustedPublisher && incoming.managedId !== void 0 && incoming.managedId === existing.managedId;
2528
+ const publishedUpdate = publishedUpgrade || publishedRefresh;
2529
+ const next = {
2530
+ ...existing,
2531
+ id: publishedUpdate ? incoming.id : existing.id,
2532
+ identityKey: publishedUpdate ? incoming.identityKey : existing.identityKey,
2533
+ kind: publishedUpdate ? incoming.kind : existing.kind,
2534
+ storage: publishedUpgrade ? "published" : existing.storage,
2535
+ status: existing.storage === "published" && !publishedUpdate ? existing.status : incoming.status,
2536
+ managedId: publishedUpdate ? incoming.managedId ?? existing.managedId : existing.managedId,
2537
+ url: publishedUpdate ? incoming.url ?? existing.url : existing.url,
2538
+ workspacePath: publishedUpdate || existing.storage === "published" ? void 0 : existing.workspacePath ?? incoming.workspacePath,
2539
+ mimeType: publishedUpdate ? incoming.mimeType ?? existing.mimeType : existing.mimeType,
2540
+ sizeBytes: existing.storage === "published" && !publishedUpdate ? existing.sizeBytes : mergeSizeBytes(existing, incoming),
2541
+ metadata: existing.storage === "published" && !publishedUpdate ? existing.metadata : mergeMetadata(existing, incoming),
2542
+ source: existing.source,
2543
+ retentionSource: existing.retentionSource,
2544
+ trustedPublisher: existing.trustedPublisher || incoming.trustedPublisher,
2545
+ clientRetained: existing.clientRetained || incoming.clientRetained,
2546
+ lastStatAt: publishedUpdate || existing.storage === "published" ? void 0 : incoming.lastStatAt ?? existing.lastStatAt,
2547
+ updatedAt: existing.updatedAt
2548
+ };
2549
+ if (publishedUpdate) {
2550
+ next.title = incoming.title;
2551
+ next.description = incoming.description;
2552
+ delete next.workspacePath;
2553
+ }
2554
+ const changed = !publicArtifactsEqual(
2555
+ toPublicArtifact(existing),
2556
+ toPublicArtifact(next)
2557
+ );
2558
+ if (changed) {
2559
+ next.updatedAt = now;
2560
+ }
2561
+ return { artifact: changed ? next : existing, changed };
2562
+ }
2563
+ __name(mergeArtifact, "mergeArtifact");
2564
+ function publicArtifactsEqual(a, b) {
2565
+ return a.id === b.id && a.kind === b.kind && a.storage === b.storage && a.source === b.source && a.status === b.status && a.title === b.title && a.description === b.description && a.workspacePath === b.workspacePath && a.managedId === b.managedId && a.url === b.url && a.mimeType === b.mimeType && a.sizeBytes === b.sizeBytes && metadataEqual(a.metadata, b.metadata) && a.clientRetained === b.clientRetained && a.createdAt === b.createdAt && a.toolCallId === b.toolCallId && a.toolName === b.toolName && a.hookEventName === b.hookEventName && a.clientId === b.clientId;
2566
+ }
2567
+ __name(publicArtifactsEqual, "publicArtifactsEqual");
2568
+ function metadataEqual(a, b) {
2569
+ if (a === b) return true;
2570
+ if (!a || !b) return false;
2571
+ const aKeys = Object.keys(a);
2572
+ const bKeys = Object.keys(b);
2573
+ if (aKeys.length !== bKeys.length) return false;
2574
+ return aKeys.every((key) => Object.hasOwn(b, key) && a[key] === b[key]);
2575
+ }
2576
+ __name(metadataEqual, "metadataEqual");
2577
+ function mergeSizeBytes(existing, incoming) {
2578
+ if (incoming.sizeBytes !== void 0) {
2579
+ return incoming.sizeBytes;
2580
+ }
2581
+ if (incoming.workspacePath && incoming.status === "missing") {
2582
+ return void 0;
2583
+ }
2584
+ return existing.sizeBytes;
2585
+ }
2586
+ __name(mergeSizeBytes, "mergeSizeBytes");
2587
+ function mergeMetadata(existing, incoming) {
2588
+ if (!incoming.metadata || incoming.source === "hook" || incoming.source !== existing.source) {
2589
+ return existing.metadata;
2590
+ }
2591
+ const merged = { ...existing.metadata ?? {} };
2592
+ let changed = false;
2593
+ for (const [key, value] of Object.entries(incoming.metadata)) {
2594
+ if (!Object.hasOwn(merged, key)) {
2595
+ merged[key] = value;
2596
+ changed = true;
2597
+ }
2598
+ }
2599
+ if (!changed) {
2600
+ return existing.metadata;
2601
+ }
2602
+ if (!isMetadataWithinLimit(merged)) {
2603
+ writeStderrLine(
2604
+ `[artifacts] action=metadata_merge_dropped artifactId=${incoming.id} reason="metadata limit exceeded"`
2605
+ );
2606
+ return existing.metadata;
2607
+ }
2608
+ return merged;
2609
+ }
2610
+ __name(mergeMetadata, "mergeMetadata");
2611
+ function isMetadataWithinLimit(metadata) {
2612
+ return Buffer.byteLength(JSON.stringify(metadata), "utf8") <= 4096;
2613
+ }
2614
+ __name(isMetadataWithinLimit, "isMetadataWithinLimit");
2615
+ function countByRetentionSource(artifacts) {
2616
+ const counts = {
2617
+ tool: 0,
2618
+ client: 0,
2619
+ hook: 0
2620
+ };
2621
+ for (const artifact of artifacts) {
2622
+ counts[artifact.retentionSource]++;
2623
+ }
2624
+ return counts;
2625
+ }
2626
+ __name(countByRetentionSource, "countByRetentionSource");
2627
+ function shouldRefreshWorkspaceStatus(artifact, now) {
2628
+ return artifact.lastStatAt === void 0 || now - artifact.lastStatAt >= WORKSPACE_STATUS_REFRESH_TTL_MS;
2629
+ }
2630
+ __name(shouldRefreshWorkspaceStatus, "shouldRefreshWorkspaceStatus");
2631
+ function selectEvictionCandidate(candidates, sourceCounts) {
2632
+ return oldest(
2633
+ candidates,
2634
+ (artifact) => artifact.status === "missing" && !artifact.clientRetained
2635
+ ) ?? oldest(
2636
+ candidates,
2637
+ (artifact) => !artifact.clientRetained && sourceCounts[artifact.retentionSource] > SOURCE_RESERVATIONS[artifact.retentionSource]
2638
+ ) ?? oldest(candidates, (artifact) => !artifact.clientRetained) ?? oldest(candidates);
2639
+ }
2640
+ __name(selectEvictionCandidate, "selectEvictionCandidate");
2641
+ function oldest(artifacts, predicate = () => true) {
2642
+ let selected;
2643
+ for (const artifact of artifacts) {
2644
+ if (!predicate(artifact)) {
2645
+ continue;
2646
+ }
2647
+ if (!selected || compareOldest(artifact, selected) < 0) {
2648
+ selected = artifact;
2649
+ }
2650
+ }
2651
+ return selected;
2652
+ }
2653
+ __name(oldest, "oldest");
2654
+ function compareOldest(a, b) {
2655
+ const created = a.createdAt.localeCompare(b.createdAt);
2656
+ if (created !== 0) return created;
2657
+ return a.insertSeq - b.insertSeq;
2658
+ }
2659
+ __name(compareOldest, "compareOldest");
2660
+ function removePriorChange(changes, artifactId) {
2661
+ const index = changes.findIndex((change) => change.artifactId === artifactId);
2662
+ if (index >= 0) {
2663
+ changes.splice(index, 1);
2664
+ }
2665
+ }
2666
+ __name(removePriorChange, "removePriorChange");
2667
+ function toPublicArtifact(artifact) {
2668
+ const {
2669
+ id,
2670
+ kind,
2671
+ storage,
2672
+ source,
2673
+ status,
2674
+ title,
2675
+ description,
2676
+ workspacePath,
2677
+ managedId,
2678
+ url,
2679
+ mimeType,
2680
+ sizeBytes,
2681
+ metadata,
2682
+ clientRetained,
2683
+ createdAt,
2684
+ updatedAt,
2685
+ toolCallId,
2686
+ toolName,
2687
+ hookEventName,
2688
+ clientId
2689
+ } = artifact;
2690
+ return {
2691
+ id,
2692
+ kind,
2693
+ storage,
2694
+ source,
2695
+ status,
2696
+ title,
2697
+ ...description ? { description } : {},
2698
+ ...workspacePath ? { workspacePath } : {},
2699
+ ...managedId ? { managedId } : {},
2700
+ ...url ? { url } : {},
2701
+ ...mimeType ? { mimeType } : {},
2702
+ ...sizeBytes !== void 0 ? { sizeBytes } : {},
2703
+ ...metadata ? { metadata } : {},
2704
+ clientRetained,
2705
+ createdAt,
2706
+ updatedAt,
2707
+ ...toolCallId ? { toolCallId } : {},
2708
+ ...toolName ? { toolName } : {},
2709
+ ...hookEventName ? { hookEventName } : {},
2710
+ ...clientId ? { clientId } : {}
2711
+ };
2712
+ }
2713
+ __name(toPublicArtifact, "toPublicArtifact");
2714
+ function inferStorage(requested, locators) {
2715
+ if (requested !== void 0 && typeof requested !== "string") {
2716
+ throw new SessionArtifactValidationError(
2717
+ "storage must be a string",
2718
+ "storage"
2719
+ );
2720
+ }
2721
+ if (requested) {
2722
+ if (requested !== "workspace" && requested !== "external_url" && requested !== "managed" && requested !== "published") {
2723
+ throw new SessionArtifactValidationError(
2724
+ "storage must be workspace, external_url, managed, or published",
2725
+ "storage"
2726
+ );
2727
+ }
2728
+ return requested;
2729
+ }
2730
+ if (locators.workspacePath) return "workspace";
2731
+ if (locators.managedId) return "managed";
2732
+ if (locators.url && locators.trustedPublisher) return "published";
2733
+ return "external_url";
2734
+ }
2735
+ __name(inferStorage, "inferStorage");
2736
+ function normalizeKind(kind) {
2737
+ if (kind === "file" || kind === "link" || kind === "html" || kind === "image" || kind === "video" || kind === "audio" || kind === "pdf" || kind === "notebook" || kind === "other") {
2738
+ return kind;
2739
+ }
2740
+ throw new SessionArtifactValidationError(
2741
+ "kind must be a supported artifact kind",
2742
+ "kind"
2743
+ );
2744
+ }
2745
+ __name(normalizeKind, "normalizeKind");
2746
+ function validateLocator(storage, locators) {
2747
+ if (storage === "published") {
2748
+ if (!locators.trustedPublisher) {
2749
+ throw new SessionArtifactValidationError(
2750
+ "published artifacts are reserved for trusted publishers",
2751
+ "storage"
2752
+ );
2753
+ }
2754
+ if (!locators.url) {
2755
+ throw new SessionArtifactValidationError(
2756
+ "published artifacts require url",
2757
+ "url"
2758
+ );
2759
+ }
2760
+ if (locators.workspacePath) {
2761
+ throw new SessionArtifactValidationError(
2762
+ "published artifacts cannot include workspacePath",
2763
+ "workspacePath"
2764
+ );
2765
+ }
2766
+ return;
2767
+ }
2768
+ const locatorCount = [
2769
+ locators.workspacePath,
2770
+ locators.managedId,
2771
+ locators.url
2772
+ ].filter(Boolean).length;
2773
+ if (locatorCount !== 1) {
2774
+ throw new SessionArtifactValidationError(
2775
+ "provide exactly one of workspacePath, managedId, or url"
2776
+ );
2777
+ }
2778
+ if (storage === "workspace" && !locators.workspacePath) {
2779
+ throw new SessionArtifactValidationError(
2780
+ "workspace storage requires workspacePath",
2781
+ "workspacePath"
2782
+ );
2783
+ }
2784
+ if (storage === "managed" && !locators.managedId) {
2785
+ throw new SessionArtifactValidationError(
2786
+ "managed storage requires managedId",
2787
+ "managedId"
2788
+ );
2789
+ }
2790
+ if (storage === "external_url" && !locators.url) {
2791
+ throw new SessionArtifactValidationError(
2792
+ "external_url storage requires url",
2793
+ "url"
2794
+ );
2795
+ }
2796
+ }
2797
+ __name(validateLocator, "validateLocator");
2798
+ function buildIdentityKey(input) {
2799
+ if (input.workspacePath) return `workspace:${input.workspacePath}`;
2800
+ if (input.managedId) return `managed:${input.managedId}`;
2801
+ if (input.url) return `url:${input.url}`;
2802
+ throw new SessionArtifactValidationError(
2803
+ "artifact identity requires workspacePath, managedId, or url"
2804
+ );
2805
+ }
2806
+ __name(buildIdentityKey, "buildIdentityKey");
2807
+ function stableArtifactId(sessionId, identityKey) {
2808
+ return createHash("sha256").update(`${sessionId}:${identityKey}`).digest("hex").slice(0, 16);
2809
+ }
2810
+ __name(stableArtifactId, "stableArtifactId");
2811
+ function managedIdForWorkspacePath(workspaceCwd, workspacePath) {
2812
+ return createHash("sha1").update(path2.resolve(workspaceCwd, workspacePath)).digest("hex").slice(0, 16);
2813
+ }
2814
+ __name(managedIdForWorkspacePath, "managedIdForWorkspacePath");
2815
+ function normalizeString(value, field, maxLength, required) {
2816
+ if (value === void 0 || value === null) {
2817
+ if (required) {
2818
+ throw new SessionArtifactValidationError(`${field} is required`, field);
2819
+ }
2820
+ return void 0;
2821
+ }
2822
+ if (typeof value !== "string") {
2823
+ throw new SessionArtifactValidationError(
2824
+ `${field} must be a string`,
2825
+ field
2826
+ );
2827
+ }
2828
+ const trimmed = value.trim();
2829
+ if (!trimmed) {
2830
+ if (required) {
2831
+ throw new SessionArtifactValidationError(`${field} is required`, field);
2832
+ }
2833
+ return void 0;
2834
+ }
2835
+ if (trimmed.length > maxLength) {
2836
+ throw new SessionArtifactValidationError(
2837
+ `${field} exceeds ${maxLength} characters`,
2838
+ field
2839
+ );
2840
+ }
2841
+ if (hasControlCharacter(trimmed, field === "description")) {
2842
+ throw new SessionArtifactValidationError(
2843
+ `${field} contains control characters`,
2844
+ field
2845
+ );
2846
+ }
2847
+ if (isDisplayField(field) && hasUnsafeDisplayPayload(trimmed)) {
2848
+ throw new SessionArtifactValidationError(
2849
+ `${field} contains unsafe markup`,
2850
+ field
2851
+ );
2852
+ }
2853
+ return trimmed;
2854
+ }
2855
+ __name(normalizeString, "normalizeString");
2856
+ function normalizeManagedId(value) {
2857
+ const managedId = normalizeString(value, "managedId", 200, false);
2858
+ if (!managedId) return void 0;
2859
+ if (managedId.includes("/") || managedId.includes("\\") || managedId.includes("..") || path2.isAbsolute(managedId) || path2.win32.isAbsolute(managedId)) {
2860
+ throw new SessionArtifactValidationError(
2861
+ "managedId must be an opaque managed resource id",
2862
+ "managedId"
2863
+ );
2864
+ }
2865
+ return managedId;
2866
+ }
2867
+ __name(normalizeManagedId, "normalizeManagedId");
2868
+ function isDisplayField(field) {
2869
+ return field === "title" || field === "description" || field === "mimeType" || field === "workspacePath" || field === "managedId";
2870
+ }
2871
+ __name(isDisplayField, "isDisplayField");
2872
+ function hasControlCharacter(value, allowLineWhitespace = false) {
2873
+ for (let i = 0; i < value.length; i++) {
2874
+ const code = value.charCodeAt(i);
2875
+ if (allowLineWhitespace && (code === 9 || code === 10 || code === 13)) {
2876
+ continue;
2877
+ }
2878
+ if (code <= 31 || code === 127 || code >= 8203 && code <= 8207 || code === 8232 || code === 8233 || code >= 8234 && code <= 8238 || code >= 8294 && code <= 8297 || code === 65279) {
2879
+ return true;
2880
+ }
2881
+ }
2882
+ return false;
2883
+ }
2884
+ __name(hasControlCharacter, "hasControlCharacter");
2885
+ function hasUnsafeDisplayPayload(value) {
2886
+ return /<\s*\/?[a-z!]|&(?:#[0-9]+|#x[0-9a-f]+|[a-z][a-z0-9]+);|javascript\s*:|data\s*:\s*(?:text\/(?:html|javascript)|application\/javascript|image\/svg\+xml)/i.test(
2887
+ value
2888
+ ) || /(?:^|[\s"'`<])on[a-z][a-z0-9-]*\s*=/i.test(value);
2889
+ }
2890
+ __name(hasUnsafeDisplayPayload, "hasUnsafeDisplayPayload");
2891
+ function normalizeWorkspacePath(raw, workspaceCwd) {
2892
+ const trimmed = normalizeString(raw, "workspacePath", 500, true);
2893
+ if (path2.isAbsolute(trimmed)) {
2894
+ throw new SessionArtifactValidationError(
2895
+ "workspacePath must be relative to the workspace",
2896
+ "workspacePath"
2897
+ );
2898
+ }
2899
+ const absolute = path2.resolve(workspaceCwd, trimmed);
2900
+ const relative = path2.relative(workspaceCwd, absolute);
2901
+ if (!relative || isOutsidePath(relative)) {
2902
+ throw new SessionArtifactValidationError(
2903
+ "workspacePath must stay inside the workspace",
2904
+ "workspacePath"
2905
+ );
2906
+ }
2907
+ return relative.split(path2.sep).join("/");
2908
+ }
2909
+ __name(normalizeWorkspacePath, "normalizeWorkspacePath");
2910
+ function normalizeArtifactUrl(raw, allowFile) {
2911
+ const trimmed = normalizeString(raw, "url", 2048, true);
2912
+ let parsed;
2913
+ try {
2914
+ parsed = new URL(trimmed);
2915
+ } catch {
2916
+ throw new SessionArtifactValidationError("url must be valid", "url");
2917
+ }
2918
+ if (parsed.username || parsed.password) {
2919
+ throw new SessionArtifactValidationError(
2920
+ "url must not include credentials",
2921
+ "url"
2922
+ );
2923
+ }
2924
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:" && !(allowFile && parsed.protocol === "file:")) {
2925
+ throw new SessionArtifactValidationError(
2926
+ allowFile ? "url must use http, https, or file" : "url must use http or https",
2927
+ "url"
2928
+ );
2929
+ }
2930
+ return parsed.href;
2931
+ }
2932
+ __name(normalizeArtifactUrl, "normalizeArtifactUrl");
2933
+ function normalizeMetadata(metadata) {
2934
+ if (metadata === void 0) {
2935
+ return void 0;
2936
+ }
2937
+ if (typeof metadata !== "object" || metadata === null || Array.isArray(metadata)) {
2938
+ throw new SessionArtifactValidationError(
2939
+ "metadata must be an object",
2940
+ "metadata"
2941
+ );
2942
+ }
2943
+ const normalized = {};
2944
+ for (const [key, value] of Object.entries(metadata)) {
2945
+ if (!key) {
2946
+ throw new SessionArtifactValidationError(
2947
+ "metadata keys must not be empty",
2948
+ "metadata"
2949
+ );
2950
+ }
2951
+ if (key.length > 120) {
2952
+ throw new SessionArtifactValidationError(
2953
+ "metadata keys must be 120 characters or fewer",
2954
+ "metadata"
2955
+ );
2956
+ }
2957
+ if (hasControlCharacter(key) || hasUnsafeDisplayPayload(key)) {
2958
+ throw new SessionArtifactValidationError(
2959
+ "metadata keys contain unsafe content",
2960
+ "metadata"
2961
+ );
2962
+ }
2963
+ if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
2964
+ throw new SessionArtifactValidationError(
2965
+ "metadata values must be primitive",
2966
+ "metadata"
2967
+ );
2968
+ }
2969
+ if (typeof value === "number" && !Number.isFinite(value)) {
2970
+ throw new SessionArtifactValidationError(
2971
+ "metadata numbers must be finite",
2972
+ "metadata"
2973
+ );
2974
+ }
2975
+ if (typeof value === "string" && (hasControlCharacter(value) || hasUnsafeDisplayPayload(value))) {
2976
+ throw new SessionArtifactValidationError(
2977
+ "metadata string values contain unsafe content",
2978
+ "metadata"
2979
+ );
2980
+ }
2981
+ normalized[key] = value;
2982
+ }
2983
+ if (Object.keys(normalized).length === 0) {
2984
+ return void 0;
2985
+ }
2986
+ if (Buffer.byteLength(JSON.stringify(normalized), "utf8") > 4096) {
2987
+ throw new SessionArtifactValidationError(
2988
+ "metadata must be 4096 bytes or fewer",
2989
+ "metadata"
2990
+ );
2991
+ }
2992
+ return normalized;
2993
+ }
2994
+ __name(normalizeMetadata, "normalizeMetadata");
2995
+ function normalizeSizeBytes(value) {
2996
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
2997
+ throw new SessionArtifactValidationError(
2998
+ "sizeBytes must be a non-negative safe integer",
2999
+ "sizeBytes"
3000
+ );
3001
+ }
3002
+ return value;
3003
+ }
3004
+ __name(normalizeSizeBytes, "normalizeSizeBytes");
3005
+ function inferKind(input) {
3006
+ if (input.storage === "published") return "html";
3007
+ if (input.url) return "link";
3008
+ const ext = input.workspacePath ? path2.extname(input.workspacePath).toLowerCase() : "";
3009
+ if (ext === ".html" || ext === ".htm") return "html";
3010
+ if ([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"].includes(ext)) {
3011
+ return "image";
3012
+ }
3013
+ if ([".mp4", ".mov", ".webm"].includes(ext)) return "video";
3014
+ if ([".mp3", ".wav", ".m4a", ".ogg"].includes(ext)) return "audio";
3015
+ if (ext === ".pdf") return "pdf";
3016
+ if (ext === ".ipynb") return "notebook";
3017
+ return input.workspacePath ? "file" : "other";
3018
+ }
3019
+ __name(inferKind, "inferKind");
3020
+ async function getWorkspaceStatus(workspaceCwd, workspacePath, realWorkspaceCwd) {
3021
+ const absolutePath = path2.resolve(workspaceCwd, workspacePath);
3022
+ const realWorkspace = await realWorkspaceCwd;
3023
+ try {
3024
+ const realPath = await fs2.realpath(absolutePath);
3025
+ const relative = path2.relative(realWorkspace, realPath);
3026
+ if (!relative || isOutsidePath(relative)) {
3027
+ return { status: "missing", escaped: true };
3028
+ }
3029
+ const stat = await fs2.stat(realPath);
3030
+ return {
3031
+ status: "available",
3032
+ ...stat.isFile() ? { sizeBytes: stat.size } : {}
3033
+ };
3034
+ } catch (error) {
3035
+ if (!isNotFoundError(error)) {
3036
+ throw error;
3037
+ }
3038
+ if (await danglingSymlinkEscapesWorkspace(absolutePath, realWorkspace)) {
3039
+ return { status: "missing", escaped: true };
3040
+ }
3041
+ return { status: "missing" };
3042
+ }
3043
+ }
3044
+ __name(getWorkspaceStatus, "getWorkspaceStatus");
3045
+ async function danglingSymlinkEscapesWorkspace(absolutePath, realWorkspace) {
3046
+ try {
3047
+ const stat = await fs2.lstat(absolutePath);
3048
+ if (!stat.isSymbolicLink()) {
3049
+ return false;
3050
+ }
3051
+ const target = await fs2.readlink(absolutePath);
3052
+ const parent = await fs2.realpath(path2.dirname(absolutePath));
3053
+ const targetPath = path2.resolve(parent, target);
3054
+ return isOutsidePath(path2.relative(realWorkspace, targetPath));
3055
+ } catch (error) {
3056
+ if (isNotFoundError(error)) {
3057
+ return false;
3058
+ }
3059
+ throw error;
3060
+ }
3061
+ }
3062
+ __name(danglingSymlinkEscapesWorkspace, "danglingSymlinkEscapesWorkspace");
3063
+ async function runInBatches(items, batchSize, fn) {
3064
+ for (let index = 0; index < items.length; index += batchSize) {
3065
+ await Promise.all(items.slice(index, index + batchSize).map(fn));
3066
+ }
3067
+ }
3068
+ __name(runInBatches, "runInBatches");
3069
+ function isNotFoundError(error) {
3070
+ if (typeof error !== "object" || error === null || !("code" in error)) {
3071
+ return false;
3072
+ }
3073
+ const code = error.code;
3074
+ return code === "ENOENT" || code === "ENOTDIR";
3075
+ }
3076
+ __name(isNotFoundError, "isNotFoundError");
3077
+ function isOutsidePath(relative) {
3078
+ return relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative);
3079
+ }
3080
+ __name(isOutsidePath, "isOutsidePath");
3081
+
1861
3082
  // packages/acp-bridge/src/bridge.ts
1862
3083
  var NOOP_BRIDGE_TELEMETRY = {
1863
3084
  captureContext: /* @__PURE__ */ __name(() => void 0, "captureContext"),
@@ -1884,6 +3105,95 @@ var NOOP_BRIDGE_TELEMETRY = {
1884
3105
  return { ...request, _meta: nextMeta };
1885
3106
  }
1886
3107
  };
3108
+ var KNOWN_SESSION_UPDATE_TYPES = /* @__PURE__ */ new Set([
3109
+ "user_message_chunk",
3110
+ "agent_message_chunk",
3111
+ "agent_thought_chunk",
3112
+ "tool_call",
3113
+ "tool_call_update",
3114
+ "plan",
3115
+ "available_commands_update",
3116
+ "current_mode_update",
3117
+ "config_option_update",
3118
+ "session_info_update",
3119
+ "usage_update"
3120
+ ]);
3121
+ var MAX_BULK_REPLAY_UPDATES = 1e4;
3122
+ function isRecord2(value) {
3123
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3124
+ }
3125
+ __name(isRecord2, "isRecord");
3126
+ function isBulkReplayUpdate(value) {
3127
+ if (!isRecord2(value)) return false;
3128
+ const updateType = value["sessionUpdate"];
3129
+ return typeof updateType === "string" && KNOWN_SESSION_UPDATE_TYPES.has(updateType);
3130
+ }
3131
+ __name(isBulkReplayUpdate, "isBulkReplayUpdate");
3132
+ function describeLoadReplayValue(value) {
3133
+ if (Array.isArray(value)) return "array";
3134
+ if (value === null) return "null";
3135
+ return typeof value;
3136
+ }
3137
+ __name(describeLoadReplayValue, "describeLoadReplayValue");
3138
+ function extractLoadReplayResponse(state) {
3139
+ const meta = isRecord2(state._meta) ? state._meta : void 0;
3140
+ const replay = meta?.[LOAD_REPLAY_META_KEY];
3141
+ if (replay === void 0) return { state, updates: [] };
3142
+ if (!isRecord2(replay) || replay["v"] !== LOAD_REPLAY_VERSION) {
3143
+ const version = isRecord2(replay) ? replay["v"] : void 0;
3144
+ throw new Error(
3145
+ `Invalid qwen.session.loadReplay payload (type=${describeLoadReplayValue(replay)}, version=${JSON.stringify(version)})`
3146
+ );
3147
+ }
3148
+ const rawUpdates = replay["updates"];
3149
+ if (!Array.isArray(rawUpdates)) {
3150
+ throw new Error(
3151
+ `Invalid qwen.session.loadReplay updates (version=${LOAD_REPLAY_VERSION}, count=not-array)`
3152
+ );
3153
+ }
3154
+ if (rawUpdates.length > MAX_BULK_REPLAY_UPDATES) {
3155
+ throw new Error(
3156
+ `qwen.session.loadReplay updates exceed limit (${rawUpdates.length} > ${MAX_BULK_REPLAY_UPDATES})`
3157
+ );
3158
+ }
3159
+ const partial = replay["partial"];
3160
+ if (partial !== void 0 && partial !== true) {
3161
+ throw new Error(
3162
+ `Invalid qwen.session.loadReplay partial (version=${LOAD_REPLAY_VERSION}, partial=${JSON.stringify(partial)})`
3163
+ );
3164
+ }
3165
+ const replayError = replay["replayError"];
3166
+ if (replayError !== void 0 && typeof replayError !== "string") {
3167
+ throw new Error(
3168
+ `Invalid qwen.session.loadReplay replayError (version=${LOAD_REPLAY_VERSION}, replayError=${describeLoadReplayValue(replayError)})`
3169
+ );
3170
+ }
3171
+ const invalidUpdateIndex = rawUpdates.findIndex(
3172
+ (update) => !isBulkReplayUpdate(update)
3173
+ );
3174
+ if (invalidUpdateIndex !== -1) {
3175
+ const invalidUpdate = rawUpdates[invalidUpdateIndex];
3176
+ const discriminator = isRecord2(invalidUpdate) ? invalidUpdate["sessionUpdate"] : void 0;
3177
+ throw new Error(
3178
+ `Invalid qwen.session.loadReplay update at index ${invalidUpdateIndex} (version=${LOAD_REPLAY_VERSION}, count=${rawUpdates.length}, sessionUpdate=${JSON.stringify(discriminator)})`
3179
+ );
3180
+ }
3181
+ const nextMeta = { ...meta ?? {} };
3182
+ delete nextMeta[LOAD_REPLAY_META_KEY];
3183
+ const cleanState = { ...state };
3184
+ if (Object.keys(nextMeta).length > 0) {
3185
+ cleanState._meta = nextMeta;
3186
+ } else {
3187
+ delete cleanState._meta;
3188
+ }
3189
+ return {
3190
+ state: cleanState,
3191
+ updates: rawUpdates,
3192
+ ...partial === true ? { partial: true } : {},
3193
+ ...typeof replayError === "string" ? { replayError } : {}
3194
+ };
3195
+ }
3196
+ __name(extractLoadReplayResponse, "extractLoadReplayResponse");
1887
3197
  function isServeDebugLoggingEnabled() {
1888
3198
  const value = process.env["QWEN_SERVE_DEBUG"];
1889
3199
  if (!value) return false;
@@ -1927,6 +3237,62 @@ function parseWorkspaceMemoryRememberResult(response) {
1927
3237
  };
1928
3238
  }
1929
3239
  __name(parseWorkspaceMemoryRememberResult, "parseWorkspaceMemoryRememberResult");
3240
+ function isBridgeAutoMemoryTopic(value) {
3241
+ return value === "user" || value === "feedback" || value === "project" || value === "reference";
3242
+ }
3243
+ __name(isBridgeAutoMemoryTopic, "isBridgeAutoMemoryTopic");
3244
+ function parseWorkspaceMemoryForgetMatch(value) {
3245
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
3246
+ return null;
3247
+ }
3248
+ const record = value;
3249
+ if (!isBridgeAutoMemoryTopic(record["topic"]) || typeof record["summary"] !== "string" || typeof record["filePath"] !== "string") {
3250
+ return null;
3251
+ }
3252
+ return {
3253
+ topic: record["topic"],
3254
+ summary: record["summary"],
3255
+ filePath: record["filePath"]
3256
+ };
3257
+ }
3258
+ __name(parseWorkspaceMemoryForgetMatch, "parseWorkspaceMemoryForgetMatch");
3259
+ function parseWorkspaceMemoryForgetResult(response) {
3260
+ if (response === null || typeof response !== "object" || Array.isArray(response)) {
3261
+ throw new Error("Malformed workspace memory forget response");
3262
+ }
3263
+ const record = response;
3264
+ const summary = record["summary"];
3265
+ const removedEntries = record["removedEntries"];
3266
+ const touchedTopics = record["touchedTopics"];
3267
+ const parsedRemovedEntries = Array.isArray(removedEntries) ? removedEntries.map(parseWorkspaceMemoryForgetMatch) : [];
3268
+ if (summary !== void 0 && typeof summary !== "string" || !Array.isArray(removedEntries) || parsedRemovedEntries.some((entry) => entry === null) || !Array.isArray(touchedTopics) || !touchedTopics.every(isBridgeAutoMemoryTopic)) {
3269
+ throw new Error("Malformed workspace memory forget response");
3270
+ }
3271
+ return {
3272
+ ...summary === void 0 ? {} : { summary },
3273
+ removedEntries: parsedRemovedEntries,
3274
+ touchedTopics
3275
+ };
3276
+ }
3277
+ __name(parseWorkspaceMemoryForgetResult, "parseWorkspaceMemoryForgetResult");
3278
+ function parseWorkspaceMemoryDreamResult(response) {
3279
+ if (response === null || typeof response !== "object" || Array.isArray(response)) {
3280
+ throw new Error("Malformed workspace memory dream response");
3281
+ }
3282
+ const record = response;
3283
+ const summary = record["summary"];
3284
+ const touchedTopics = record["touchedTopics"];
3285
+ const dedupedEntries = record["dedupedEntries"];
3286
+ if (summary !== void 0 && typeof summary !== "string" || !Array.isArray(touchedTopics) || !touchedTopics.every(isBridgeAutoMemoryTopic) || typeof dedupedEntries !== "number" || !Number.isFinite(dedupedEntries)) {
3287
+ throw new Error("Malformed workspace memory dream response");
3288
+ }
3289
+ return {
3290
+ ...summary === void 0 ? {} : { summary },
3291
+ touchedTopics,
3292
+ dedupedEntries
3293
+ };
3294
+ }
3295
+ __name(parseWorkspaceMemoryDreamResult, "parseWorkspaceMemoryDreamResult");
1930
3296
  function echoPromptToSessionBus(entry, req, originatorClientId) {
1931
3297
  const prompt = req.prompt;
1932
3298
  if (!Array.isArray(prompt) || prompt.length === 0) return;
@@ -2050,7 +3416,7 @@ function broadcastTurnError(entry, sessionId, err, promptId, originatorClientId)
2050
3416
  }
2051
3417
  }
2052
3418
  __name(broadcastTurnError, "broadcastTurnError");
2053
- function hasControlCharacter(value) {
3419
+ function hasControlCharacter2(value) {
2054
3420
  for (let i = 0; i < value.length; i += 1) {
2055
3421
  const code = value.charCodeAt(i);
2056
3422
  if (code <= 31 || code === 127) {
@@ -2059,7 +3425,7 @@ function hasControlCharacter(value) {
2059
3425
  }
2060
3426
  return false;
2061
3427
  }
2062
- __name(hasControlCharacter, "hasControlCharacter");
3428
+ __name(hasControlCharacter2, "hasControlCharacter");
2063
3429
  function extractPromptText(prompt) {
2064
3430
  if (!Array.isArray(prompt)) return "";
2065
3431
  let hasImage = false;
@@ -2152,7 +3518,7 @@ function createAcpSessionBridge(opts) {
2152
3518
  } else {
2153
3519
  maxPendingPromptsPerSession = maxPendingPromptsRaw;
2154
3520
  }
2155
- if (!path2.isAbsolute(opts.boundWorkspace)) {
3521
+ if (!path3.isAbsolute(opts.boundWorkspace)) {
2156
3522
  throw new TypeError(
2157
3523
  `Invalid boundWorkspace: "${opts.boundWorkspace}". Must be an absolute path.`
2158
3524
  );
@@ -2378,6 +3744,7 @@ function createAcpSessionBridge(opts) {
2378
3744
  },
2379
3745
  async () => await channelFactory(boundWorkspace, childEnvOverrides)
2380
3746
  );
3747
+ const sessionIds = /* @__PURE__ */ new Set();
2381
3748
  const client = new BridgeClient(
2382
3749
  // BfFut: ACP today carries a sessionId on every per-session
2383
3750
  // notification / request, so the no-sessionId branch is
@@ -2427,14 +3794,15 @@ function createAcpSessionBridge(opts) {
2427
3794
  // the per-WS-connection `ClientMcpRegistrar`. Omitted callers (tests,
2428
3795
  // Mode A) never host a client MCP server, so the method stays
2429
3796
  // unreachable.
2430
- opts.clientMcpSender
3797
+ opts.clientMcpSender,
3798
+ (sessionId) => sessionIds.has(sessionId)
2431
3799
  );
2432
3800
  const connection = new ClientSideConnection(() => client, channel.stream);
2433
3801
  const info = {
2434
3802
  channel,
2435
3803
  connection,
2436
3804
  client,
2437
- sessionIds: /* @__PURE__ */ new Set(),
3805
+ sessionIds,
2438
3806
  pendingRestoreIds: /* @__PURE__ */ new Set(),
2439
3807
  sessionSpawnsInFlight: 0,
2440
3808
  workspaceControlInFlight: 0,
@@ -2685,7 +4053,7 @@ function createAcpSessionBridge(opts) {
2685
4053
  return entry.transportClosedReject;
2686
4054
  }, "getTransportClosedReject");
2687
4055
  const resolveWorkspaceKey = /* @__PURE__ */ __name((workspaceCwd) => {
2688
- if (!path2.isAbsolute(workspaceCwd)) {
4056
+ if (!path3.isAbsolute(workspaceCwd)) {
2689
4057
  throw new Error(
2690
4058
  `workspaceCwd must be an absolute path; got "${workspaceCwd}"`
2691
4059
  );
@@ -2902,7 +4270,7 @@ function createAcpSessionBridge(opts) {
2902
4270
  if (rerun) void reconcileAfterRoundtrip(entry, target);
2903
4271
  }
2904
4272
  }, "reconcileAfterRoundtrip");
2905
- const createSessionEntry = /* @__PURE__ */ __name((ci, sessionId, workspaceCwd, events = createSessionEventBus()) => {
4273
+ const createSessionEntry = /* @__PURE__ */ __name((ci, sessionId, workspaceCwd, events = createSessionEventBus(), options = {}) => {
2906
4274
  const entry = {
2907
4275
  sessionId,
2908
4276
  workspaceCwd,
@@ -2910,6 +4278,7 @@ function createAcpSessionBridge(opts) {
2910
4278
  channel: ci.channel,
2911
4279
  connection: ci.connection,
2912
4280
  events,
4281
+ artifacts: new SessionArtifactStore({ sessionId, workspaceCwd }),
2913
4282
  promptQueue: Promise.resolve(),
2914
4283
  pendingPromptCount: 0,
2915
4284
  pendingPromptList: [],
@@ -2930,9 +4299,39 @@ function createAcpSessionBridge(opts) {
2930
4299
  byId.set(entry.sessionId, entry);
2931
4300
  touchActivity();
2932
4301
  telemetry.metrics?.sessionLifecycle("spawn");
2933
- ci.client.drainEarlyEvents(entry.sessionId, entry);
4302
+ if (options.drainEarlyEvents !== false) {
4303
+ ci.client.drainEarlyEvents(entry.sessionId, entry);
4304
+ }
2934
4305
  return entry;
2935
4306
  }, "createSessionEntry");
4307
+ const publishArtifactChanges = /* @__PURE__ */ __name((entry, changes, originatorClientId) => {
4308
+ for (const change of changes) {
4309
+ entry.events.publish({
4310
+ type: "artifact_changed",
4311
+ data: { sessionId: entry.sessionId, change },
4312
+ ...originatorClientId ? { originatorClientId } : {}
4313
+ });
4314
+ }
4315
+ }, "publishArtifactChanges");
4316
+ const makeClientArtifactInput = /* @__PURE__ */ __name((artifact, clientId) => {
4317
+ const input = {
4318
+ title: artifact.title,
4319
+ kind: artifact.kind,
4320
+ storage: artifact.storage,
4321
+ description: artifact.description,
4322
+ workspacePath: artifact.workspacePath,
4323
+ managedId: artifact.managedId,
4324
+ url: artifact.url,
4325
+ mimeType: artifact.mimeType,
4326
+ sizeBytes: artifact.sizeBytes,
4327
+ metadata: artifact.metadata,
4328
+ source: "client"
4329
+ };
4330
+ if (clientId) {
4331
+ input.clientId = clientId;
4332
+ }
4333
+ return input;
4334
+ }, "makeClientArtifactInput");
2936
4335
  const seedSnapshotCaches = /* @__PURE__ */ __name((entry, resp) => {
2937
4336
  const model = resp.models?.currentModelId;
2938
4337
  if (typeof model === "string" && model.length > 0) {
@@ -2962,22 +4361,30 @@ function createAcpSessionBridge(opts) {
2962
4361
  return typeof maybe.message === "string" && maybe.message === `Resource not found: ${expectedUri}`;
2963
4362
  }, "isAcpSessionResourceNotFound");
2964
4363
  const replayFieldsFor = /* @__PURE__ */ __name((entry, action) => {
4364
+ const replayStatus = action === "load" && entry.restoreReplayPartial === true ? {
4365
+ partial: true,
4366
+ ...typeof entry.restoreReplayError === "string" ? { replayError: entry.restoreReplayError } : {}
4367
+ } : {};
2965
4368
  const snapshot = entry.events.snapshotReplay();
2966
- if (!snapshot) return { lastEventId: entry.events.lastEventId };
4369
+ if (!snapshot) {
4370
+ return { lastEventId: entry.events.lastEventId, ...replayStatus };
4371
+ }
2967
4372
  if (action === "load") {
2968
4373
  return {
2969
4374
  compactedReplay: snapshot.compactedTurns,
2970
4375
  liveJournal: snapshot.liveJournal,
2971
- lastEventId: snapshot.lastEventId
4376
+ lastEventId: snapshot.lastEventId,
4377
+ ...replayStatus
2972
4378
  };
2973
4379
  }
2974
- return { lastEventId: snapshot.lastEventId };
4380
+ return { lastEventId: snapshot.lastEventId, ...replayStatus };
2975
4381
  }, "replayFieldsFor");
2976
4382
  async function restoreSession(action, req) {
2977
4383
  if (shuttingDown) {
2978
4384
  throw new Error("AcpSessionBridge is shutting down");
2979
4385
  }
2980
4386
  const workspaceKey = resolveWorkspaceKey(req.workspaceCwd);
4387
+ const historyReplay = action === "load" ? req.historyReplay ?? "stream" : "stream";
2981
4388
  const existing = byId.get(req.sessionId);
2982
4389
  if (existing) {
2983
4390
  existing.attachCount++;
@@ -2997,7 +4404,7 @@ function createAcpSessionBridge(opts) {
2997
4404
  }
2998
4405
  const inFlight = inFlightRestores.get(req.sessionId);
2999
4406
  if (inFlight) {
3000
- if (action !== inFlight.action) {
4407
+ if (action !== inFlight.action || historyReplay !== inFlight.historyReplay) {
3001
4408
  throw new RestoreInProgressError(
3002
4409
  req.sessionId,
3003
4410
  inFlight.action,
@@ -3046,6 +4453,9 @@ function createAcpSessionBridge(opts) {
3046
4453
  transportClosed.catch(() => {
3047
4454
  });
3048
4455
  let state;
4456
+ let replayUpdates = [];
4457
+ let replayPartial;
4458
+ let replayError;
3049
4459
  try {
3050
4460
  if (action === "load") {
3051
4461
  state = await Promise.race([
@@ -3058,7 +4468,12 @@ function createAcpSessionBridge(opts) {
3058
4468
  // the agent side. The SDK's `RestoreSessionRequest`
3059
4469
  // intentionally has no `mcpServers` field for the
3060
4470
  // same reason.
3061
- mcpServers: []
4471
+ mcpServers: [],
4472
+ ...historyReplay === "response" ? {
4473
+ _meta: {
4474
+ [LOAD_REPLAY_MODE_META_KEY]: LOAD_REPLAY_BULK_MODE
4475
+ }
4476
+ } : {}
3062
4477
  }),
3063
4478
  initTimeoutMs,
3064
4479
  "loadSession"
@@ -3079,6 +4494,13 @@ function createAcpSessionBridge(opts) {
3079
4494
  transportClosed
3080
4495
  ]);
3081
4496
  }
4497
+ if (action === "load" && historyReplay === "response") {
4498
+ const extracted = extractLoadReplayResponse(state);
4499
+ state = extracted.state;
4500
+ replayUpdates = extracted.updates;
4501
+ replayPartial = extracted.partial;
4502
+ replayError = extracted.replayError;
4503
+ }
3082
4504
  } catch (err) {
3083
4505
  restoreEvents.close();
3084
4506
  if (isAcpSessionResourceNotFound(err, req.sessionId)) {
@@ -3122,10 +4544,21 @@ function createAcpSessionBridge(opts) {
3122
4544
  ci,
3123
4545
  req.sessionId,
3124
4546
  workspaceKey,
3125
- restoreEvents
4547
+ restoreEvents,
4548
+ { drainEarlyEvents: replayUpdates.length === 0 }
3126
4549
  );
3127
4550
  entry.restoreState = state;
4551
+ if (replayPartial === true) {
4552
+ entry.restoreReplayPartial = true;
4553
+ }
4554
+ if (replayError !== void 0) {
4555
+ entry.restoreReplayError = replayError;
4556
+ }
3128
4557
  seedSnapshotCaches(entry, state);
4558
+ if (replayUpdates.length > 0) {
4559
+ await ci.client.seedSessionUpdates(entry, replayUpdates);
4560
+ ci.client.drainEarlyEvents(entry.sessionId, entry);
4561
+ }
3129
4562
  const clientId = registerClient(entry, req.clientId);
3130
4563
  entry.attachCount = coalesceState.count;
3131
4564
  registeredEntry = entry;
@@ -3145,13 +4578,28 @@ function createAcpSessionBridge(opts) {
3145
4578
  pendingRestoreEvents.delete(req.sessionId);
3146
4579
  if (!registeredEntry) {
3147
4580
  restoreEvents.close();
4581
+ let removedRestoreEntry = false;
4582
+ if (byId.get(req.sessionId)?.events === restoreEvents) {
4583
+ byId.delete(req.sessionId);
4584
+ ci?.sessionIds.delete(req.sessionId);
4585
+ removedRestoreEntry = true;
4586
+ }
4587
+ if (removedRestoreEntry && ci && hasNoChannelWork(ci)) {
4588
+ ci.emptyReapPending = true;
4589
+ ci.isDying = true;
4590
+ }
3148
4591
  ci?.client.markSessionClosed(req.sessionId);
3149
4592
  }
3150
4593
  if (ci) {
3151
4594
  await reapPendingEmptyChannel(ci);
3152
4595
  }
3153
4596
  });
3154
- inFlightRestores.set(req.sessionId, { action, promise, coalesceState });
4597
+ inFlightRestores.set(req.sessionId, {
4598
+ action,
4599
+ historyReplay,
4600
+ promise,
4601
+ coalesceState
4602
+ });
3155
4603
  try {
3156
4604
  return await promise;
3157
4605
  } finally {
@@ -3736,6 +5184,11 @@ function createAcpSessionBridge(opts) {
3736
5184
  if (!entry) throw new SessionNotFoundError(sessionId);
3737
5185
  return entry.events.lastEventId;
3738
5186
  },
5187
+ getSessionReplaySnapshot(sessionId) {
5188
+ const entry = byId.get(sessionId);
5189
+ if (!entry) throw new SessionNotFoundError(sessionId);
5190
+ return entry.events.snapshotReplay();
5191
+ },
3739
5192
  respondToPermission(requestId, response, context) {
3740
5193
  const sessionId = permissionMediator.peekSessionFor(requestId);
3741
5194
  if (sessionId === void 0 || !byId.has(sessionId)) {
@@ -3957,7 +5410,7 @@ function createAcpSessionBridge(opts) {
3957
5410
  `must be a string of at most ${MAX_DISPLAY_NAME_LENGTH} characters`
3958
5411
  );
3959
5412
  }
3960
- if (hasControlCharacter(metadata.displayName)) {
5413
+ if (hasControlCharacter2(metadata.displayName)) {
3961
5414
  throw new InvalidSessionMetadataError(
3962
5415
  "displayName",
3963
5416
  "must not contain control characters"
@@ -3999,8 +5452,30 @@ function createAcpSessionBridge(opts) {
3999
5452
  }
4000
5453
  return { displayName: entry.displayName };
4001
5454
  },
5455
+ async getSessionArtifacts(sessionId) {
5456
+ const entry = byId.get(sessionId);
5457
+ if (!entry) throw new SessionNotFoundError(sessionId);
5458
+ return entry.artifacts.list();
5459
+ },
5460
+ async addSessionArtifact(sessionId, artifact, context) {
5461
+ const entry = byId.get(sessionId);
5462
+ if (!entry) throw new SessionNotFoundError(sessionId);
5463
+ const clientId = resolveTrustedClientId(entry, context?.clientId);
5464
+ const input = makeClientArtifactInput(artifact, clientId);
5465
+ const result = await entry.artifacts.upsertMany([input], { strict: true });
5466
+ publishArtifactChanges(entry, result.changes, clientId);
5467
+ return result;
5468
+ },
5469
+ async removeSessionArtifact(sessionId, artifactId, context) {
5470
+ const entry = byId.get(sessionId);
5471
+ if (!entry) throw new SessionNotFoundError(sessionId);
5472
+ const clientId = resolveTrustedClientId(entry, context?.clientId);
5473
+ const result = await entry.artifacts.remove(artifactId, { clientId });
5474
+ publishArtifactChanges(entry, result.changes, clientId);
5475
+ return result;
5476
+ },
4002
5477
  listWorkspaceSessions(workspaceCwd) {
4003
- if (!path2.isAbsolute(workspaceCwd)) return [];
5478
+ if (!path3.isAbsolute(workspaceCwd)) return [];
4004
5479
  const key = workspaceCwd === boundWorkspace ? boundWorkspace : canonicalizeWorkspace(workspaceCwd);
4005
5480
  if (key !== boundWorkspace) return [];
4006
5481
  const out = [];
@@ -4142,6 +5617,54 @@ function createAcpSessionBridge(opts) {
4142
5617
  }
4143
5618
  }
4144
5619
  },
5620
+ async runWorkspaceMemoryForget(request) {
5621
+ const info = await ensureChannel();
5622
+ try {
5623
+ const response = await withWorkspaceControl(
5624
+ info,
5625
+ () => withTimeout(
5626
+ Promise.race([
5627
+ info.connection.extMethod(
5628
+ SERVE_CONTROL_EXT_METHODS.workspaceMemoryForget,
5629
+ { ...request, cwd: boundWorkspace }
5630
+ ),
5631
+ getChannelClosedReject(info)
5632
+ ]),
5633
+ WORKSPACE_MEMORY_REMEMBER_TIMEOUT_MS,
5634
+ SERVE_CONTROL_EXT_METHODS.workspaceMemoryForget
5635
+ )
5636
+ );
5637
+ return parseWorkspaceMemoryForgetResult(response);
5638
+ } finally {
5639
+ if (hasNoChannelWork(info)) {
5640
+ await startIdleTimer(info, "workspace memory forget");
5641
+ }
5642
+ }
5643
+ },
5644
+ async runWorkspaceMemoryDream() {
5645
+ const info = await ensureChannel();
5646
+ try {
5647
+ const response = await withWorkspaceControl(
5648
+ info,
5649
+ () => withTimeout(
5650
+ Promise.race([
5651
+ info.connection.extMethod(
5652
+ SERVE_CONTROL_EXT_METHODS.workspaceMemoryDream,
5653
+ { cwd: boundWorkspace }
5654
+ ),
5655
+ getChannelClosedReject(info)
5656
+ ]),
5657
+ WORKSPACE_MEMORY_REMEMBER_TIMEOUT_MS,
5658
+ SERVE_CONTROL_EXT_METHODS.workspaceMemoryDream
5659
+ )
5660
+ );
5661
+ return parseWorkspaceMemoryDreamResult(response);
5662
+ } finally {
5663
+ if (hasNoChannelWork(info)) {
5664
+ await startIdleTimer(info, "workspace memory dream");
5665
+ }
5666
+ }
5667
+ },
4145
5668
  async getWorkspaceMcpToolsStatus(serverName) {
4146
5669
  return requestWorkspaceStatus(
4147
5670
  SERVE_STATUS_EXT_METHODS.workspaceMcpTools,
@@ -5214,6 +6737,7 @@ __name(withTimeout, "withTimeout");
5214
6737
  var createHttpAcpBridge = createAcpSessionBridge;
5215
6738
 
5216
6739
  export {
6740
+ SessionArtifactValidationError,
5217
6741
  extractErrorMessage,
5218
6742
  extractErrorCode,
5219
6743
  createAcpSessionBridge,
@@ -5224,3 +6748,8 @@ export {
5224
6748
  * Copyright 2025 Qwen Team
5225
6749
  * SPDX-License-Identifier: Apache-2.0
5226
6750
  */
6751
+ /**
6752
+ * @license
6753
+ * Copyright 2026 Qwen Team
6754
+ * SPDX-License-Identifier: Apache-2.0
6755
+ */