@qwen-code/qwen-code 0.22.2-preview.1 → 0.22.2

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 (379) hide show
  1. package/bundled/goal-draft/SKILL.md +109 -0
  2. package/bundled/qc-helper/docs/configuration/settings.md +10 -10
  3. package/bundled/qc-helper/docs/features/_meta.ts +1 -0
  4. package/bundled/qc-helper/docs/features/commands.md +2 -1
  5. package/bundled/qc-helper/docs/features/goals.md +58 -0
  6. package/bundled/qc-helper/docs/features/headless.md +13 -11
  7. package/bundled/qc-helper/docs/features/status-line.md +3 -3
  8. package/chunks/MaxSizedBox-X5FZSNAJ.js +112 -0
  9. package/chunks/{StandaloneSessionPicker-5D2L22SF.js → StandaloneSessionPicker-AVLI6VJP.js} +76 -76
  10. package/chunks/{acpAgent-4DCKLA32.js → acpAgent-MXEAN54C.js} +969 -255
  11. package/chunks/{agent-B4AFZL6H.js → agent-PJIPDWHB.js} +41 -41
  12. package/chunks/agent-headless-B4JVD7UK.js +87 -0
  13. package/chunks/{anthropicContentGenerator-U76GTVB6.js → anthropicContentGenerator-HGMBACVE.js} +7 -7
  14. package/chunks/{artifact-tool-XDJCZZCF.js → artifact-tool-5YT4QF7Y.js} +2 -2
  15. package/chunks/{askUserQuestion-ZXXERLEV.js → askUserQuestion-4SWJY65V.js} +2 -2
  16. package/chunks/bridge-FKZTVIS3.js +121 -0
  17. package/chunks/{channel-management-service-HXXS3HOZ.js → channel-management-service-X4AZ6IXX.js} +1 -1
  18. package/chunks/channel-settings-store-4UFCLBBG.js +117 -0
  19. package/chunks/{channel-worker-group-EVDC2P4K.js → channel-worker-group-27HMGD5S.js} +7 -6
  20. package/chunks/{channel-worker-manager-IJJZHGEI.js → channel-worker-manager-QEGX646Z.js} +9 -7
  21. package/chunks/{channel-worker-supervisor-YSKB5GVB.js → channel-worker-supervisor-UEW2WWCC.js} +5 -4
  22. package/chunks/{chunk-7QGEJI7M.js → chunk-23RFD54N.js} +1 -1
  23. package/chunks/{chunk-QR4AHMGQ.js → chunk-2K6ORIHJ.js} +13 -13
  24. package/chunks/{chunk-3D6PDLNJ.js → chunk-2NYCN6F2.js} +11 -11
  25. package/chunks/chunk-2TNL5KPM.js +49 -0
  26. package/chunks/{chunk-ZEADBZLO.js → chunk-3AFMQUTI.js} +3 -3
  27. package/chunks/{chunk-JSY43GMI.js → chunk-3DXFGHAJ.js} +185 -174
  28. package/chunks/{chunk-T45CL2NT.js → chunk-3FDFA4MZ.js} +3 -3
  29. package/chunks/{chunk-WBT3STGU.js → chunk-3I6UTTDX.js} +1 -1
  30. package/chunks/{chunk-FVDQ5E67.js → chunk-3JGZSIDA.js} +3 -3
  31. package/chunks/{chunk-J3CBIEDS.js → chunk-3SGB2UHX.js} +15 -10
  32. package/chunks/{chunk-77TOMRBG.js → chunk-43GGFFLY.js} +3 -3
  33. package/chunks/{chunk-ZJG323IA.js → chunk-43UYNAWX.js} +6 -4
  34. package/chunks/{chunk-NEZZHGEI.js → chunk-4CDSK5AZ.js} +6 -6
  35. package/chunks/chunk-4CMJKUND.js +95 -0
  36. package/chunks/{chunk-EB4VDXNI.js → chunk-4FTKQNWJ.js} +1 -1
  37. package/chunks/{chunk-PAJGKZAA.js → chunk-4HV6HH33.js} +10 -8
  38. package/chunks/{chunk-BQBUZDDH.js → chunk-4J657AJR.js} +3 -3
  39. package/chunks/{chunk-XMSMTU6H.js → chunk-4JWNNLPT.js} +4 -4
  40. package/chunks/{chunk-ALTQLFEG.js → chunk-4MQEPP3Y.js} +9 -84
  41. package/chunks/{chunk-3JKUX4PB.js → chunk-5O72BNDD.js} +8 -8
  42. package/chunks/{chunk-IKN3IUJP.js → chunk-5XBFCMWD.js} +2 -2
  43. package/chunks/{chunk-2YDYDDE2.js → chunk-66KDY3LV.js} +3 -3
  44. package/chunks/{chunk-VOEMVOM4.js → chunk-6CGL7E7G.js} +2 -2
  45. package/chunks/{chunk-SLEWBQWM.js → chunk-6DIGWMGT.js} +2 -2
  46. package/chunks/{chunk-SKNVVYIJ.js → chunk-6IUNAPLR.js} +3 -3
  47. package/chunks/{chunk-BHTJAO36.js → chunk-6LFJEACX.js} +4 -4
  48. package/chunks/{chunk-AP7WZONS.js → chunk-6LFTG244.js} +8 -8
  49. package/chunks/{chunk-PVBT5QPD.js → chunk-6PJOTWAN.js} +1 -1
  50. package/chunks/{chunk-5CMWFBML.js → chunk-77RG5SFQ.js} +1 -1
  51. package/chunks/{chunk-SDVF6A42.js → chunk-7DJCPZE3.js} +3 -3
  52. package/chunks/{chunk-ELY4QVIB.js → chunk-7EQQR46F.js} +1 -1
  53. package/chunks/{chunk-H3HZ4AWW.js → chunk-7UYV53BC.js} +1 -1
  54. package/chunks/{chunk-FCSWZAS3.js → chunk-A7YP63XN.js} +4 -4
  55. package/chunks/{chunk-E4I5MVBR.js → chunk-ABS25QER.js} +1 -1
  56. package/chunks/{chunk-WZ3QKPMQ.js → chunk-AGR5UROZ.js} +5 -5
  57. package/chunks/{chunk-5T4LWWNX.js → chunk-B7CDU2SL.js} +2 -2
  58. package/chunks/{chunk-HPECDZ6X.js → chunk-BAJKAAOY.js} +68 -64
  59. package/chunks/{chunk-WXDZZFH3.js → chunk-BBWV7ONL.js} +3 -3
  60. package/chunks/{chunk-4TPAG3O7.js → chunk-BFPESSTR.js} +1 -1
  61. package/chunks/{chunk-DID6IRGC.js → chunk-BNMF3K5X.js} +20 -19
  62. package/chunks/{chunk-IPQA4SR5.js → chunk-BWORX6FA.js} +1 -1
  63. package/chunks/{chunk-BKDAF7ES.js → chunk-BXDAPVQU.js} +2 -2
  64. package/chunks/{chunk-IOGKXPL3.js → chunk-C6K43ZDE.js} +4 -4
  65. package/chunks/{chunk-7OBH4AM2.js → chunk-CAJTKR6W.js} +5 -5
  66. package/chunks/{chunk-SUE2ZMUH.js → chunk-CCBZDVUA.js} +30 -25
  67. package/chunks/{chunk-U45D46O5.js → chunk-CMHFCLBU.js} +1 -1
  68. package/chunks/{chunk-AWFZDYSD.js → chunk-CPGJJJLU.js} +1 -1
  69. package/chunks/{chunk-LRKMQPMD.js → chunk-CQH5KTKC.js} +1 -1
  70. package/chunks/{chunk-3WBMPR3S.js → chunk-CSMI4FP5.js} +1 -1
  71. package/chunks/{chunk-JECO75XD.js → chunk-D5LUXLUH.js} +2 -2
  72. package/chunks/{chunk-UWICO5KN.js → chunk-DZWPESIF.js} +3 -3
  73. package/chunks/{chunk-I6DY753S.js → chunk-EFUM7RVY.js} +2 -2
  74. package/chunks/{chunk-3UZCLCXR.js → chunk-EKSCLBBF.js} +3 -3
  75. package/chunks/{chunk-LJP2BBMK.js → chunk-EOGELB3H.js} +4 -4
  76. package/chunks/{chunk-624NHDGB.js → chunk-ERFDKH32.js} +11 -0
  77. package/chunks/{chunk-GTUZ3R6I.js → chunk-F5SZP77N.js} +310 -78
  78. package/chunks/{chunk-SQPOJ4ZI.js → chunk-F6FV3C5K.js} +12 -7
  79. package/chunks/{chunk-TDQBVEPK.js → chunk-F7TNAPZ4.js} +9 -7
  80. package/chunks/{chunk-F7M6ZFGU.js → chunk-FBSSIAAQ.js} +6 -6
  81. package/chunks/{chunk-2FN2HNTI.js → chunk-FDY5RMGH.js} +2 -2
  82. package/chunks/{chunk-PTQF7WRA.js → chunk-FY76G3G5.js} +5 -5
  83. package/chunks/{chunk-X3XGCJ32.js → chunk-G34IR3D6.js} +9 -9
  84. package/chunks/{chunk-ZWKGZB2K.js → chunk-H2WDGQ6D.js} +3 -3
  85. package/chunks/chunk-H72BQNM3.js +645 -0
  86. package/chunks/{chunk-ILPXSKGQ.js → chunk-HVEYF6VT.js} +2 -2
  87. package/chunks/{chunk-5JCZ3UKB.js → chunk-HVQ3B7FF.js} +2 -2
  88. package/chunks/{chunk-VBF4KZRQ.js → chunk-IJKTMLBE.js} +1 -1
  89. package/chunks/{chunk-R7G7GKOX.js → chunk-IK5BW3CS.js} +2 -2
  90. package/chunks/{chunk-UCQOSV6A.js → chunk-IOPCFOTF.js} +3 -3
  91. package/chunks/{chunk-O3BUXIPM.js → chunk-IZIVM7LZ.js} +3 -3
  92. package/chunks/{chunk-I6VVOZDK.js → chunk-J2OSJFP3.js} +2 -2
  93. package/chunks/{chunk-AYVIGUUK.js → chunk-JB4JIVTJ.js} +1 -1
  94. package/chunks/{chunk-76WGY6LW.js → chunk-JCQD3INC.js} +3 -3
  95. package/chunks/{chunk-XUX6O2ZT.js → chunk-JHS74YAB.js} +4 -1
  96. package/chunks/{chunk-KPF2K55D.js → chunk-JSC3H3TD.js} +27 -4
  97. package/chunks/{chunk-CZFQ62RM.js → chunk-JSHHYW6T.js} +4 -4
  98. package/chunks/{chunk-MCKJTBZN.js → chunk-JU4EEXC7.js} +2 -2
  99. package/chunks/{chunk-T6OODF2U.js → chunk-JWALNCLT.js} +1 -1
  100. package/chunks/{chunk-CDUN63IN.js → chunk-K2NQPH5D.js} +3 -3
  101. package/chunks/{chunk-7UJTY5V3.js → chunk-KEKZFOL6.js} +1 -1
  102. package/chunks/{chunk-5URK47RN.js → chunk-KUX5D6DK.js} +1197 -336
  103. package/chunks/{chunk-OF2DPXUS.js → chunk-L2M46FP5.js} +116 -21
  104. package/chunks/{chunk-6US7BF3G.js → chunk-L6BZRIUL.js} +2 -2
  105. package/chunks/{chunk-TR7G7XFQ.js → chunk-L6YT5H34.js} +4 -4
  106. package/chunks/{chunk-W2WRNJKX.js → chunk-LDP5OD6N.js} +5 -5
  107. package/chunks/{chunk-OLS3MAXQ.js → chunk-LXIBMVRN.js} +7 -7
  108. package/chunks/{chunk-GNMJNDZE.js → chunk-M6FOJXW3.js} +1 -1
  109. package/chunks/{chunk-J5352S3S.js → chunk-MIIVI25Q.js} +3 -3
  110. package/chunks/{chunk-K3LPAED5.js → chunk-MQM5PJML.js} +24 -4
  111. package/chunks/{chunk-27ONXAFY.js → chunk-NIFYWDYN.js} +1 -1
  112. package/chunks/{chunk-IJVDHPKD.js → chunk-NU7RKEZR.js} +2 -2
  113. package/chunks/{chunk-LWLEPXKK.js → chunk-O2UN57NX.js} +2 -2
  114. package/chunks/{chunk-5SM2AK65.js → chunk-OIP7C4FE.js} +5 -5
  115. package/chunks/{chunk-UCSV2IZW.js → chunk-OIVXBW3W.js} +3 -3
  116. package/chunks/{chunk-TOF4UZHH.js → chunk-P2SU6ZTI.js} +3 -3
  117. package/chunks/{chunk-JLU3FPQR.js → chunk-P5GGM4KS.js} +5 -5
  118. package/chunks/{chunk-ZKAZP2ZG.js → chunk-PDMJ3KGS.js} +2 -2
  119. package/chunks/{chunk-RRKNX553.js → chunk-PDQGMSZK.js} +1 -1
  120. package/chunks/{chunk-KQYW2GB5.js → chunk-PFCQ62V4.js} +5 -5
  121. package/chunks/{chunk-JLGFGPZJ.js → chunk-PKAYJDB3.js} +2 -2
  122. package/chunks/{chunk-GTUKW3I6.js → chunk-PPKZ7JOE.js} +1 -1
  123. package/chunks/{chunk-HABT2Z7R.js → chunk-Q26AYA62.js} +4 -4
  124. package/chunks/{chunk-WEGXPP5E.js → chunk-QFCDF5PL.js} +1 -1
  125. package/chunks/{chunk-R3T33BG5.js → chunk-QP4C6WCE.js} +3 -3
  126. package/chunks/{chunk-Z6L3C4VF.js → chunk-R4PMVSR5.js} +27 -25
  127. package/chunks/{chunk-AKQA3WCH.js → chunk-RIEFGNTP.js} +579 -3419
  128. package/chunks/{chunk-ATLGYSYH.js → chunk-RJC5EIKY.js} +4 -4
  129. package/chunks/chunk-RKFGE7JH.js +93 -0
  130. package/chunks/{chunk-CPPYZHBN.js → chunk-RU7BK6B3.js} +1 -1
  131. package/chunks/{chunk-VYWCFWI5.js → chunk-RUGNCYNO.js} +11 -633
  132. package/chunks/{chunk-ZLTEQYNC.js → chunk-SFPGAQUL.js} +2 -2
  133. package/chunks/{chunk-TMGAS5TO.js → chunk-SJ3ZUWFK.js} +26 -4
  134. package/chunks/{chunk-DEYPMJPD.js → chunk-SMPR7SPO.js} +1 -1
  135. package/chunks/{chunk-7CWEZQ6T.js → chunk-STUJ7IMZ.js} +0 -77
  136. package/chunks/{chunk-BPMVPWBT.js → chunk-T32W2JHI.js} +6 -6
  137. package/chunks/chunk-T3WXYEZ4.js +304 -0
  138. package/chunks/{chunk-XIRDDFRS.js → chunk-TGWAQ5RB.js} +695 -294
  139. package/chunks/{chunk-EC5BONC3.js → chunk-TPI7O7HR.js} +1 -1
  140. package/chunks/{chunk-OI2DR6QB.js → chunk-TRZTNIQ6.js} +2 -2
  141. package/chunks/{chunk-T2CRBIDJ.js → chunk-TTX2JUE6.js} +1 -1
  142. package/chunks/{chunk-2NNTE2ED.js → chunk-UHQFIS7N.js} +15 -0
  143. package/chunks/{chunk-KOJ6MHD5.js → chunk-UTLCH2FK.js} +1 -1
  144. package/chunks/{chunk-7DPKPBOQ.js → chunk-V2NDP3I4.js} +2 -2
  145. package/chunks/{chunk-ZGWDHSUM.js → chunk-V4QXXQJ2.js} +3 -3
  146. package/chunks/{chunk-XR7OIAQF.js → chunk-V5J4J5TP.js} +2 -2
  147. package/chunks/{chunk-X2UOOG5V.js → chunk-VQUGEVGR.js} +1 -1
  148. package/chunks/{chunk-XJS54K4A.js → chunk-VTHREBQM.js} +2 -2
  149. package/chunks/{chunk-V3GINY6K.js → chunk-VVW4ZNFY.js} +3 -3
  150. package/chunks/{chunk-66GIFJ63.js → chunk-W5MJJ2V7.js} +1 -1
  151. package/chunks/{chunk-FPH6TXD3.js → chunk-WAOIHTJS.js} +1 -1
  152. package/chunks/{chunk-FN5C6C7G.js → chunk-WKK5BQNP.js} +151 -60
  153. package/chunks/chunk-XDFHY4R3.js +1584 -0
  154. package/chunks/{chunk-L436XFLJ.js → chunk-XDLEZ5HX.js} +1 -1
  155. package/chunks/{chunk-VL4YX3FH.js → chunk-XF63PKEN.js} +1 -1
  156. package/chunks/{chunk-3FSBMAYD.js → chunk-XQFT3QUF.js} +1 -1
  157. package/chunks/{chunk-2MHGD7EE.js → chunk-XRSOI3DL.js} +1 -1
  158. package/chunks/{chunk-FJWICLG7.js → chunk-Y3QL45LS.js} +1 -1
  159. package/chunks/{chunk-TFV4RXPU.js → chunk-Y6LSMB6J.js} +1 -1
  160. package/chunks/{chunk-2SERYG4T.js → chunk-YTDVNSUQ.js} +1 -1
  161. package/chunks/{chunk-AA7VBRBG.js → chunk-Z7FLBQPR.js} +7 -7
  162. package/chunks/{chunk-4K44OMLI.js → chunk-Z7TGYN5F.js} +3 -3
  163. package/chunks/{chunk-FJAITR4L.js → chunk-ZFKOCECU.js} +2 -2
  164. package/chunks/chunk-ZGXYO7DY.js +3416 -0
  165. package/chunks/{chunk-OT4YKUUV.js → chunk-ZU4UDIWX.js} +2 -2
  166. package/chunks/config-utils-BBOCOGZW.js +113 -0
  167. package/chunks/contextCommand-EET67MQ2.js +108 -0
  168. package/chunks/{core-runtime-CEJRHCRF.js → core-runtime-677KFX6Q.js} +53 -53
  169. package/chunks/{create-sub-session-RVHSWVAP.js → create-sub-session-23ITIELI.js} +3 -3
  170. package/chunks/{create-sub-session-DFZVIG56.js → create-sub-session-JKZAYZX7.js} +160 -79
  171. package/chunks/{cron-create-IJGVR5R2.js → cron-create-KJ4IY6PP.js} +57 -10
  172. package/chunks/{cron-delete-4N5BI6GL.js → cron-delete-MBLQCAYY.js} +4 -4
  173. package/chunks/{cron-list-2CATIPUX.js → cron-list-DWH2PQ47.js} +4 -4
  174. package/chunks/{daemon-NAYK4XXU.js → daemon-MJQUO7RA.js} +28 -1
  175. package/chunks/{daemon-git-worktree-guard-L6ZUFKF7.js → daemon-git-worktree-guard-6MVAAZZZ.js} +53 -53
  176. package/chunks/daemon-status-provider-RFKZOK3S.js +119 -0
  177. package/chunks/daemon-trust-policy-DRZPFB4Q.js +114 -0
  178. package/chunks/{daemon-trust-policy-monitor-2G3EKQEU.js → daemon-trust-policy-monitor-B5U6TQX5.js} +59 -59
  179. package/chunks/deferred-core-runtime-BBVIOP24.js +116 -0
  180. package/chunks/{display-image-6KHQNEAE.js → display-image-BE7I6ZB6.js} +5 -5
  181. package/chunks/{dist-FVJZSIO4.js → dist-ILDBUWDB.js} +71 -19
  182. package/chunks/{dist-6H6RZ5H6.js → dist-QODVOVWI.js} +58 -35
  183. package/chunks/earlyInputCapture-2JOMMNQ4.js +109 -0
  184. package/chunks/{edit-ZERGX3RV.js → edit-BSV6YRVW.js} +42 -42
  185. package/chunks/{en-6MDBWMTG.js → en-YLXBXM4Z.js} +2 -1
  186. package/chunks/{enter-worktree-UOZ6ZTP7.js → enter-worktree-MQXFFPKM.js} +7 -7
  187. package/chunks/{enterPlanMode-W2RP3P5B.js → enterPlanMode-PKNF35FE.js} +41 -41
  188. package/chunks/{environment-ZBJ23YVR.js → environment-MOZMPUHR.js} +55 -55
  189. package/chunks/errors-DMPQVYJL.js +115 -0
  190. package/chunks/{exit-worktree-G2QATUCV.js → exit-worktree-L344UY7K.js} +7 -7
  191. package/chunks/{exitPlanMode-VFJ5R5DD.js → exitPlanMode-UE4FGCY4.js} +41 -41
  192. package/chunks/{fast-path-4MWBCEUF.js → fast-path-773PV7PU.js} +5 -5
  193. package/chunks/{gemini-X5GQHQYF.js → gemini-IP4FJAP5.js} +101 -100
  194. package/chunks/{geminiContentGenerator-UDSWXZXL.js → geminiContentGenerator-PUNEBWJW.js} +8 -8
  195. package/chunks/{glob-DKTNG32L.js → glob-CZHOWUEA.js} +41 -41
  196. package/chunks/{goal-tools-CNVXMSZD.js → goal-tools-YZQJXTGH.js} +9 -8
  197. package/chunks/{grep-3NLSS32D.js → grep-5GPICQBU.js} +6 -6
  198. package/chunks/handleAutoUpdate-SGXKTQM6.js +111 -0
  199. package/chunks/{i18n-KX7FEKJJ.js → i18n-B42HKHKH.js} +54 -54
  200. package/chunks/{image-gen-63NN2H5N.js → image-gen-HAYOEQ4O.js} +9 -9
  201. package/chunks/initializer-FB66WYR3.js +115 -0
  202. package/chunks/installationInfo-Y7AULNMX.js +109 -0
  203. package/chunks/{keychain-token-storage-3MGFRYGM.js → keychain-token-storage-37XPR34V.js} +2 -2
  204. package/chunks/list-2HWGSVRA.js +118 -0
  205. package/chunks/{list-agents-EXRSHIDJ.js → list-agents-DI3YFTDT.js} +2 -2
  206. package/chunks/loadedSettingsAdapter-7Y7IEI6T.js +112 -0
  207. package/chunks/{loggingContentGenerator-73C7TKDK.js → loggingContentGenerator-2GGZNGLG.js} +35 -22
  208. package/chunks/{loop-wakeup-XBPB2BRO.js → loop-wakeup-XJFONSQ7.js} +5 -5
  209. package/chunks/{ls-WFONAND2.js → ls-3VAT2J7A.js} +4 -4
  210. package/chunks/{lsp-DUKPDUXN.js → lsp-5W7IRUBH.js} +2 -2
  211. package/chunks/{managed-npm-update-H2KOLQZU.js → managed-npm-update-5TEV2E3J.js} +54 -54
  212. package/chunks/mcp-QGG5NMJJ.js +112 -0
  213. package/chunks/{monitor-TGCMNJJH.js → monitor-FI7PY36Z.js} +41 -41
  214. package/chunks/nonInteractiveCli-4LO2TL72.js +186 -0
  215. package/chunks/{notebook-edit-YEUNZYDX.js → notebook-edit-TPGMGBST.js} +42 -42
  216. package/chunks/{openaiContentGenerator-J7LD4VSL.js → openaiContentGenerator-SUCYANUZ.js} +24 -24
  217. package/chunks/pidfile-JMF2CLMY.js +113 -0
  218. package/chunks/{processUtils-ZHFC7CSL.js → processUtils-IQZBYWSB.js} +2 -2
  219. package/chunks/prompt-terminal-ledger-DCZO6S7C.js +107 -0
  220. package/chunks/{qwenContentGenerator-YEWWK3JQ.js → qwenContentGenerator-J4TXFGJT.js} +46 -46
  221. package/chunks/{qwenOAuth2-JIYAWXTW.js → qwenOAuth2-RF33DNZW.js} +8 -8
  222. package/chunks/{read-file-ZQI3WPVK.js → read-file-QE2B5FTX.js} +12 -12
  223. package/chunks/{read-mcp-resource-7A7NFSFL.js → read-mcp-resource-XD257PGJ.js} +2 -2
  224. package/chunks/{record-artifact-CPJ4IJSD.js → record-artifact-RZGZBENU.js} +4 -4
  225. package/chunks/{report-findings-VFM5DENC.js → report-findings-Q3SYKKW5.js} +5 -5
  226. package/chunks/{request-shutdown-6IW46GD7.js → request-shutdown-BCLA3JV2.js} +7 -7
  227. package/chunks/resumeHistoryUtils-CZ4JRDVW.js +120 -0
  228. package/chunks/{ripGrep-46N4FLOS.js → ripGrep-R6B6HWPX.js} +17 -17
  229. package/chunks/{run-qwen-serve-WHWU7NZ6.js → run-qwen-serve-TPHZMSXJ.js} +148 -319
  230. package/chunks/{runtime-E7BSPWQP.js → runtime-TTIGSBM4.js} +62 -62
  231. package/chunks/scheduled-tasks-QB4ZVUQS.js +129 -0
  232. package/chunks/{scheduler-TJPATFOM.js → scheduler-YRUG22M7.js} +55 -55
  233. package/chunks/{sdk-exporters-http-XXSKBD2C.js → sdk-exporters-http-KCJ4ZFG7.js} +2 -2
  234. package/chunks/{sdk-impl-VPNBLV7I.js → sdk-impl-HLSEDAEP.js} +4 -4
  235. package/chunks/{send-message-OXZK6USO.js → send-message-Y5I6NYAM.js} +7 -7
  236. package/chunks/serve-MXXCDAQN.js +120 -0
  237. package/chunks/{server-AXCN7QBJ.js → server-DFUSSSHI.js} +19760 -19444
  238. package/chunks/{session-HO4US3AG.js → session-3TEHNENZ.js} +100 -99
  239. package/chunks/{settings-B2PFJJN4.js → settings-VOZYKLTT.js} +58 -58
  240. package/chunks/{shell-7XMSANI3.js → shell-6RTKZVMI.js} +41 -41
  241. package/chunks/{skill-NL3VH562.js → skill-FEOKFX26.js} +18 -18
  242. package/chunks/{skill-settings-SKMN5YXC.js → skill-settings-VA4XVYVL.js} +58 -58
  243. package/chunks/spawnChannel-F6WVP723.js +114 -0
  244. package/chunks/{standalone-update-XOCWGXAH.js → standalone-update-RGILUQVK.js} +55 -55
  245. package/chunks/{startInteractiveUI-G2XKSCQ7.js → startInteractiveUI-WHJPFOGH.js} +2757 -2223
  246. package/chunks/{syntheticOutput-5AJ444QT.js → syntheticOutput-ML43GEFN.js} +3 -3
  247. package/chunks/{task-create-TY7P6UHG.js → task-create-TMSV7RD5.js} +11 -11
  248. package/chunks/{task-list-BELMPA75.js → task-list-W55RBFRX.js} +5 -5
  249. package/chunks/{task-stop-U3S3KZG2.js → task-stop-CXEH5L2D.js} +2 -2
  250. package/chunks/{task-update-O4IGDPCK.js → task-update-N536I4GQ.js} +11 -11
  251. package/chunks/{team-create-IYBUOORE.js → team-create-QYSWCYCL.js} +41 -41
  252. package/chunks/{team-delete-YWFXX77L.js → team-delete-FJRFX33M.js} +5 -5
  253. package/chunks/{team-plan-approval-YKU57LBM.js → team-plan-approval-P7DAD644.js} +41 -41
  254. package/chunks/{terminal-image-renderer-NREKYTAD.js → terminal-image-renderer-SMQE46UJ.js} +55 -55
  255. package/chunks/theme-manager-RFO5WWFF.js +104 -0
  256. package/chunks/{todoWrite-435EO2CS.js → todoWrite-WDJNQEMW.js} +43 -25
  257. package/chunks/{tool-search-4Q3KASIQ.js → tool-search-F2EMIAPV.js} +17 -17
  258. package/chunks/total-session-admission-HTVTP4BS.js +116 -0
  259. package/chunks/{trustedFolders-OILZCHRD.js → trustedFolders-7OCNMUQE.js} +54 -54
  260. package/chunks/{update-relaunch-4DC2KKFS.js → update-relaunch-4GF3OXMB.js} +5 -5
  261. package/chunks/{updateCheck-AK4COHNK.js → updateCheck-7QBDXZRT.js} +56 -56
  262. package/chunks/useAutoAcceptIndicator-UI4SNL2O.js +122 -0
  263. package/chunks/{validateNonInterActiveAuth-YAE4YPO3.js → validateNonInterActiveAuth-BPPA563H.js} +96 -95
  264. package/chunks/{version-QFP7OGWX.js → version-ANP4TS6O.js} +1 -1
  265. package/chunks/{web-fetch-6T23IK6Q.js → web-fetch-QJADSAH3.js} +16 -16
  266. package/chunks/{web-search-WBWTCZK2.js → web-search-7TTQKEKM.js} +9 -9
  267. package/chunks/{workflow-FMAA3D4R.js → workflow-5XN6XSOT.js} +105 -60
  268. package/chunks/workspace-providers-status-PDFZWL5Q.js +116 -0
  269. package/chunks/{workspace-registration-store-CMMX7EPX.js → workspace-registration-store-XKVW2YVH.js} +1 -1
  270. package/chunks/{workspace-registry-WJLZPRTG.js → workspace-registry-23R7XRB5.js} +61 -59
  271. package/chunks/workspace-service-MUYO2QIC.js +131 -0
  272. package/chunks/workspace-skills-status-IKECTOOQ.js +115 -0
  273. package/chunks/{workspace-trust-reconciler-PJDQUTJL.js → workspace-trust-reconciler-KLGOB53D.js} +67 -65
  274. package/chunks/write-file-GGRBB5TZ.js +90 -0
  275. package/chunks/{zh-TW-MV4FWFB6.js → zh-TW-RPTMRTHG.js} +2 -1
  276. package/chunks/{zh-X3WGU6SI.js → zh-UNY63FR3.js} +2 -1
  277. package/chunks/{zoom-image-WVYVFRXF.js → zoom-image-6ASG6J4C.js} +12 -12
  278. package/cli.js +14 -14
  279. package/locales/en.js +2 -0
  280. package/locales/zh-TW.js +2 -0
  281. package/locales/zh.js +2 -0
  282. package/package.json +3 -3
  283. package/web-shell/assets/{abnfDiagram-VCTEODGH-d-8xa8uz.js → abnfDiagram-VCTEODGH-uS-vtfs3.js} +1 -1
  284. package/web-shell/assets/{arc-BhB7CrGK.js → arc-BSTnk5pu.js} +1 -1
  285. package/web-shell/assets/{architectureDiagram-5GKGNRK7-BDCd_IcJ.js → architectureDiagram-5GKGNRK7-DCMyiqzy.js} +1 -1
  286. package/web-shell/assets/{blockDiagram-NRAW4CY4-C-AHu6mi.js → blockDiagram-NRAW4CY4-C7_gt0r1.js} +1 -1
  287. package/web-shell/assets/{c4Diagram-UCG6FXSJ-CFjQt0vl.js → c4Diagram-UCG6FXSJ-BeQg2L8B.js} +1 -1
  288. package/web-shell/assets/channel-CENAhErJ.js +1 -0
  289. package/web-shell/assets/{chunk-2Q5K7J3B-DRjwqaDq.js → chunk-2Q5K7J3B-DnV9bKBe.js} +1 -1
  290. package/web-shell/assets/{chunk-5VM5RSS4-eHM9noJE.js → chunk-5VM5RSS4-rT1GAcT7.js} +1 -1
  291. package/web-shell/assets/{chunk-F27PBJKO-CcSWUYn1.js → chunk-F27PBJKO-BcgSxqGA.js} +1 -1
  292. package/web-shell/assets/{chunk-G27WJ6UU-DNr-RF5S.js → chunk-G27WJ6UU-Bw1ZhyZq.js} +1 -1
  293. package/web-shell/assets/{chunk-JWPE2WC7-rVJHoNF8.js → chunk-JWPE2WC7-D4iDw-96.js} +1 -1
  294. package/web-shell/assets/{chunk-LCL6LL3I-B2aX0Gzy.js → chunk-LCL6LL3I-D3MB3GTZ.js} +1 -1
  295. package/web-shell/assets/{chunk-POPQ4Y6H-DVp07n32.js → chunk-POPQ4Y6H-Cep2ndrS.js} +1 -1
  296. package/web-shell/assets/{chunk-SVP7TREG-C6QB_KUr.js → chunk-SVP7TREG-BuahMHJc.js} +1 -1
  297. package/web-shell/assets/{chunk-XXDRQBXY-CLVxDNCy.js → chunk-XXDRQBXY-BdujIvJY.js} +1 -1
  298. package/web-shell/assets/classDiagram-DTDB5LWJ-CM6FArGH.js +1 -0
  299. package/web-shell/assets/classDiagram-v2-JRS7N3AN-CM6FArGH.js +1 -0
  300. package/web-shell/assets/{cose-bilkent-JH36ORCC-B_aKX3A3.js → cose-bilkent-JH36ORCC-CGLLUbF8.js} +1 -1
  301. package/web-shell/assets/{cynefin-OW5HDTMX-BBw2pPUa.js → cynefin-OW5HDTMX-D6kDsRSh.js} +1 -1
  302. package/web-shell/assets/{cynefinDiagram-5FMLGOSQ-WWPiBTwk.js → cynefinDiagram-5FMLGOSQ-CCy78qZx.js} +1 -1
  303. package/web-shell/assets/{dagre-3AP2YEHR-CPtIdka-.js → dagre-3AP2YEHR-D5gJuJsf.js} +1 -1
  304. package/web-shell/assets/{diagram-S7CK7UJ4-Dj5og1Xi.js → diagram-S7CK7UJ4-ZwEZ5W__.js} +1 -1
  305. package/web-shell/assets/{diagram-UQ7AKVKN-CC_x70YC.js → diagram-UQ7AKVKN-B036APjv.js} +1 -1
  306. package/web-shell/assets/{diagram-VSXAHHWV-rXMfsO4n.js → diagram-VSXAHHWV-BfDJ57Fq.js} +1 -1
  307. package/web-shell/assets/{diagram-VX7I27RA-DEmLYQbh.js → diagram-VX7I27RA-Wha0p5rT.js} +1 -1
  308. package/web-shell/assets/{diagram-Z3DM3KII-B0ys6Vxx.js → diagram-Z3DM3KII-L20bz2K1.js} +1 -1
  309. package/web-shell/assets/{ebnfDiagram-PWID7BFC-Bq6y6lKH.js → ebnfDiagram-PWID7BFC-D1ZM0Cp7.js} +1 -1
  310. package/web-shell/assets/{erDiagram-SSCWMZ5O-l69JeBZ4.js → erDiagram-SSCWMZ5O-CMtA19v8.js} +1 -1
  311. package/web-shell/assets/{flowDiagram-A5DVABFB-Cm-EzGY4.js → flowDiagram-A5DVABFB-D96m326S.js} +1 -1
  312. package/web-shell/assets/{ganttDiagram-EL5Y4UJY-BRS9ZOp7.js → ganttDiagram-EL5Y4UJY-BuHiYG2o.js} +1 -1
  313. package/web-shell/assets/{gitGraphDiagram-WWUBYQGX-DAMRKgLk.js → gitGraphDiagram-WWUBYQGX-Ck3NmkSw.js} +1 -1
  314. package/web-shell/assets/index-8hXIpvo4.js +1865 -0
  315. package/web-shell/assets/{index-Bncx8E8M.js → index-CBGz4Qjl.js} +1 -1
  316. package/web-shell/assets/{index-C-XlQn24.css → index-DclEv91i.css} +1 -1
  317. package/web-shell/assets/{infoDiagram-RXCK75RN-DasffaZp.js → infoDiagram-RXCK75RN-B6DAOyXe.js} +1 -1
  318. package/web-shell/assets/{ishikawaDiagram-5VMMS53U-CmRUt3C-.js → ishikawaDiagram-5VMMS53U-Bpg-joFo.js} +1 -1
  319. package/web-shell/assets/{journeyDiagram-EYS64GPL-DzhE_c1Z.js → journeyDiagram-EYS64GPL-BclrHzGa.js} +1 -1
  320. package/web-shell/assets/{kanban-definition-3QL26DDD-BH5b6nn3.js → kanban-definition-3QL26DDD-5gJzNTYG.js} +1 -1
  321. package/web-shell/assets/{layout-BxoLK-LY.js → layout-CEz_1L8z.js} +1 -1
  322. package/web-shell/assets/{linear-CqDPvVI7.js → linear-CBjTq9Hc.js} +1 -1
  323. package/web-shell/assets/{mermaid.core-DBoN4plo.js → mermaid.core-Dzhbxx_I.js} +6 -6
  324. package/web-shell/assets/{mindmap-definition-FBJOCRG2-DJJtXPEM.js → mindmap-definition-FBJOCRG2-DQdVqlBF.js} +1 -1
  325. package/web-shell/assets/{pegDiagram-XKGWAZYB-DmgPXP5l.js → pegDiagram-XKGWAZYB-D1JZlYXL.js} +1 -1
  326. package/web-shell/assets/{pieDiagram-E7YTZNPT-1ezjzcI5.js → pieDiagram-E7YTZNPT-BmkpYL0w.js} +1 -1
  327. package/web-shell/assets/{quadrantDiagram-AXDQQJYC-ChKARz3k.js → quadrantDiagram-AXDQQJYC-DRvD6co5.js} +1 -1
  328. package/web-shell/assets/{railroadDiagram-O6MQD6OU-k7mNB_QT.js → railroadDiagram-O6MQD6OU-CEdjDuoo.js} +1 -1
  329. package/web-shell/assets/{requirementDiagram-EFPCY7ZU-B0XFsii0.js → requirementDiagram-EFPCY7ZU-BCHG-ie8.js} +1 -1
  330. package/web-shell/assets/{sankeyDiagram-P5KCCOFB-repJ2Ukk.js → sankeyDiagram-P5KCCOFB-C9cZMnye.js} +1 -1
  331. package/web-shell/assets/{sequenceDiagram-WJ2MYXX4-D0gUSKGu.js → sequenceDiagram-WJ2MYXX4-DYHRV4zL.js} +1 -1
  332. package/web-shell/assets/{sizeCapture-X5ZJPWSS-CE463F74.js → sizeCapture-X5ZJPWSS-DlK21aye.js} +1 -1
  333. package/web-shell/assets/{stateDiagram-HBIQ2CUA-003zYtJa.js → stateDiagram-HBIQ2CUA-BrmZssvk.js} +1 -1
  334. package/web-shell/assets/stateDiagram-v2-4QOOHH4V-B2Q7XDaE.js +1 -0
  335. package/web-shell/assets/{swimlanes-XN3QIQJK-DBj8MFpu.js → swimlanes-XN3QIQJK-D9xfcayR.js} +1 -1
  336. package/web-shell/assets/swimlanesDiagram-VK2B7HYN-DpZPOSbX.js +8 -0
  337. package/web-shell/assets/{timeline-definition-24CTP7MA-D2aCh4a9.js → timeline-definition-24CTP7MA-D4gdDL_d.js} +1 -1
  338. package/web-shell/assets/{vennDiagram-4TSXK5OY-BHMfWBpl.js → vennDiagram-4TSXK5OY-DdhIExjI.js} +1 -1
  339. package/web-shell/assets/{wardleyDiagram-VM6X3IG4-RdHAj66j.js → wardleyDiagram-VM6X3IG4-C9RS_Zof.js} +1 -1
  340. package/web-shell/assets/{xychartDiagram-S5SC5T6Z-BgJWOvq0.js → xychartDiagram-S5SC5T6Z-orRgmxWk.js} +1 -1
  341. package/web-shell/index.html +2 -2
  342. package/chunks/MaxSizedBox-BFVLZP2E.js +0 -112
  343. package/chunks/agent-headless-UCBCS4V4.js +0 -87
  344. package/chunks/bridge-FRNYLYY7.js +0 -119
  345. package/chunks/channel-settings-store-IV4KDL7N.js +0 -117
  346. package/chunks/chunk-JS73NNWF.js +0 -30
  347. package/chunks/chunk-VGHZT6JR.js +0 -51
  348. package/chunks/config-utils-PMX4QZ5Y.js +0 -113
  349. package/chunks/contextCommand-NZRLVAFC.js +0 -108
  350. package/chunks/daemon-status-provider-FTTYZEKN.js +0 -118
  351. package/chunks/daemon-trust-policy-WMGFPA3Z.js +0 -114
  352. package/chunks/deferred-core-runtime-BUFEFYNA.js +0 -116
  353. package/chunks/earlyInputCapture-ROPLMXA7.js +0 -109
  354. package/chunks/errors-U5KNAADB.js +0 -115
  355. package/chunks/handleAutoUpdate-W4BUYFT7.js +0 -111
  356. package/chunks/initializer-SNVVLS7Y.js +0 -115
  357. package/chunks/installationInfo-D4XGQNHJ.js +0 -109
  358. package/chunks/list-WYECMLXZ.js +0 -118
  359. package/chunks/loadedSettingsAdapter-5WZBXBCR.js +0 -112
  360. package/chunks/mcp-CBWAROY3.js +0 -112
  361. package/chunks/nonInteractiveCli-O232UUV7.js +0 -185
  362. package/chunks/pidfile-MSPQ255Y.js +0 -113
  363. package/chunks/prompt-terminal-ledger-YPGMNDXT.js +0 -107
  364. package/chunks/resumeHistoryUtils-35D4KAVT.js +0 -120
  365. package/chunks/serve-WQ54UIYG.js +0 -120
  366. package/chunks/spawnChannel-NE43EEO3.js +0 -113
  367. package/chunks/theme-manager-IJS6OICX.js +0 -104
  368. package/chunks/total-session-admission-L5RPEFZ2.js +0 -114
  369. package/chunks/useAutoAcceptIndicator-O2N2Q6KG.js +0 -122
  370. package/chunks/workspace-providers-status-OCFEWIPQ.js +0 -116
  371. package/chunks/workspace-service-5ZOBMMIB.js +0 -131
  372. package/chunks/workspace-skills-status-B6NJZD4B.js +0 -115
  373. package/chunks/write-file-B2RNX6SU.js +0 -90
  374. package/web-shell/assets/channel-CU-Zyv0r.js +0 -1
  375. package/web-shell/assets/classDiagram-DTDB5LWJ-C0J2_LKs.js +0 -1
  376. package/web-shell/assets/classDiagram-v2-JRS7N3AN-C0J2_LKs.js +0 -1
  377. package/web-shell/assets/index-gdvMN4J1.js +0 -1860
  378. package/web-shell/assets/stateDiagram-v2-4QOOHH4V-2uACDdJN.js +0 -1
  379. package/web-shell/assets/swimlanesDiagram-VK2B7HYN-C4JJkavU.js +0 -8
@@ -1,5 +1,17 @@
1
1
  // Force strict mode and setup for ESM
2
2
  "use strict";
3
+ import {
4
+ BridgeClient,
5
+ CANCEL_VOTE_SENTINEL,
6
+ KNOWN_APPROVAL_MODES,
7
+ MultiClientPermissionMediator,
8
+ SessionAttachmentReferenceError,
9
+ SessionAttachmentStore,
10
+ createNoOpPermissionAuditPublisher,
11
+ isSessionAttachmentReference,
12
+ withAttachmentDegradationMarker,
13
+ writeStderrLine
14
+ } from "./chunk-ZGXYO7DY.js";
3
15
  import {
4
16
  DEFAULT_RING_SIZE,
5
17
  EVENT_SCHEMA_VERSION,
@@ -19,7 +31,7 @@ import {
19
31
  } from "./chunk-OCPBI7J5.js";
20
32
  import {
21
33
  defaultSpawnChannelFactory
22
- } from "./chunk-VYWCFWI5.js";
34
+ } from "./chunk-RUGNCYNO.js";
23
35
  import {
24
36
  BridgeChannelClosedError,
25
37
  BridgeTimeoutError,
@@ -29,7 +41,7 @@ import {
29
41
  SessionRestoreTimeoutError,
30
42
  createIdleWorkspaceExtensionsStatus,
31
43
  createIdleWorkspaceHooksStatus
32
- } from "./chunk-XUX6O2ZT.js";
44
+ } from "./chunk-JHS74YAB.js";
33
45
  import {
34
46
  ACTIVE_WORK_CLOSE_IF_UNHELD_PARAM,
35
47
  ACTIVE_WORK_CLOSE_TIMEOUT_MS,
@@ -38,8 +50,6 @@ import {
38
50
  ACTIVE_WORK_HEARTBEAT_VERSION,
39
51
  ACTIVE_WORK_HOLD_CATEGORIES,
40
52
  ACTIVE_WORK_MAX_SESSION_HOLDS,
41
- ACTIVE_WORK_MAX_SNAPSHOT_SESSIONS,
42
- ACTIVE_WORK_NOTIFICATION_METHOD,
43
53
  ACTIVE_WORK_STALE_INTERVALS,
44
54
  CHANNEL_LIVENESS_META_KEY,
45
55
  CHANNEL_LIVENESS_VERSION,
@@ -49,7 +59,6 @@ import {
49
59
  DAEMON_ATTACHMENT_REFERENCES_META_KEY,
50
60
  DAEMON_CHANNEL_DELIVERY_META_KEY,
51
61
  DAEMON_MODEL_PROMPT_META_KEY,
52
- DAEMON_PERMISSION_CANCEL_REASON_META_KEY,
53
62
  DAEMON_PROMPT_DISPLAY_TEXT_META_KEY,
54
63
  DAEMON_RESTORE_ASK_USER_QUESTION_META_KEY,
55
64
  DAEMON_SUPPRESS_RESTORE_ASK_USER_QUESTION_META_KEY,
@@ -60,11 +69,9 @@ import {
60
69
  LOAD_REPLAY_MODE_META_KEY,
61
70
  LOAD_REPLAY_PAGE_SIZE_META_KEY,
62
71
  LOAD_REPLAY_VERSION,
63
- MID_TURN_QUEUE_DRAIN_METHOD,
64
72
  MID_TURN_RECONCILIATION_RING_SIZE,
65
73
  PROMPT_CANCEL_METHOD,
66
74
  REQUESTED_SESSION_ID_META_KEY,
67
- TODO_STOP_GUARD_CONTINUATION_CLAIM_METHOD,
68
75
  TODO_STOP_GUARD_QUEUE_RELEASE_METHOD,
69
76
  WORKTREE_MCP_DEFER_META_KEY,
70
77
  clampActiveWorkIntervalMs,
@@ -74,7 +81,6 @@ import {
74
81
  import {
75
82
  BranchWhilePromptActiveError,
76
83
  BridgeChannelQuarantinedError,
77
- CancelSentinelCollisionError,
78
84
  CdWhilePromptActiveError,
79
85
  InvalidClientIdError,
80
86
  InvalidPermissionOptionError,
@@ -90,33 +96,23 @@ import {
90
96
  SessionNotFoundError,
91
97
  SessionShellClientRequiredError,
92
98
  SessionShellDisabledError,
99
+ StandaloneSessionSpawnError,
93
100
  WorkspaceMismatchError,
94
101
  isNotCurrentlyGeneratingCancelError
95
- } from "./chunk-624NHDGB.js";
102
+ } from "./chunk-ERFDKH32.js";
96
103
  import {
97
104
  canonicalizeWorkspace,
98
105
  translateAndCheckAbsoluteWorkspacePath
99
106
  } from "./chunk-6PLDPT2C.js";
100
- import {
101
- CHANNEL_DELIVERY_ERROR_CODES,
102
- LIVE_TASK_TOOL_NAMES,
103
- MAX_LIVE_SCREEN_CONTEXT_TEXT_CHARS,
104
- MAX_LIVE_SPEAK_TO_USER_MESSAGE_CHARS,
105
- MAX_SUB_SESSION_NAME_CHARS
106
- } from "./chunk-UDG5EZJI.js";
107
107
  import {
108
108
  EXTERNAL_TOOL_GUARD_READY_META_KEY,
109
- EXTERNAL_TOOL_GUARD_REQUIRED_VALUE,
110
- isValidExternalToolGuardDenialReason
109
+ EXTERNAL_TOOL_GUARD_REQUIRED_VALUE
111
110
  } from "./chunk-3VUENPWF.js";
112
111
  import {
113
112
  ClientSideConnection,
114
113
  PROTOCOL_VERSION,
115
114
  RequestError
116
115
  } from "./chunk-IW6RQPQB.js";
117
- import {
118
- MAX_SUB_SESSION_PROMPT_CHARS
119
- } from "./chunk-LJZSMWOH.js";
120
116
  import {
121
117
  MAX_DIRECTORY_ARTIFACT_DEPTH,
122
118
  MAX_DIRECTORY_ARTIFACT_FILES,
@@ -124,7 +120,7 @@ import {
124
120
  isOfficeDocumentExtension,
125
121
  isRecordableDerivedChild,
126
122
  pathHasSkippedDirectoryComponent
127
- } from "./chunk-SKNVVYIJ.js";
123
+ } from "./chunk-6IUNAPLR.js";
128
124
  import {
129
125
  SESSION_ARTIFACT_PERSISTENCE_VERSION,
130
126
  SESSION_PR_LIST_LIMIT,
@@ -142,22 +138,16 @@ import {
142
138
  normalizeSnapshotPayload,
143
139
  normalizeTurnResultError,
144
140
  stableSessionArtifactId
145
- } from "./chunk-XIRDDFRS.js";
146
- import {
147
- APPROVAL_MODES
148
- } from "./chunk-XR7OIAQF.js";
141
+ } from "./chunk-TGWAQ5RB.js";
149
142
  import {
150
143
  DAEMON_TRACEPARENT_META_KEY,
151
144
  DAEMON_TRACESTATE_META_KEY
152
- } from "./chunk-ILPXSKGQ.js";
145
+ } from "./chunk-HVEYF6VT.js";
153
146
  import {
154
147
  INVOCATION_CONTEXT_META_KEY,
155
148
  PRIVATE_ACP_CAPABILITY_ENV,
156
149
  PRIVATE_PARENT_CAPABILITY_META_KEY
157
- } from "./chunk-OF2DPXUS.js";
158
- import {
159
- getSpecificMimeType
160
- } from "./chunk-WBT3STGU.js";
150
+ } from "./chunk-L2M46FP5.js";
161
151
  import {
162
152
  init_esbuild_shims
163
153
  } from "./chunk-5O2XNYP6.js";
@@ -167,8 +157,8 @@ import {
167
157
 
168
158
  // packages/acp-bridge/src/bridge.ts
169
159
  init_esbuild_shims();
170
- import { randomBytes, randomUUID as randomUUID3 } from "node:crypto";
171
- import * as path4 from "node:path";
160
+ import { randomBytes, randomUUID } from "node:crypto";
161
+ import * as path2 from "node:path";
172
162
  import { inspect } from "node:util";
173
163
 
174
164
  // packages/acp-bridge/src/compactionEngine.ts
@@ -1185,6 +1175,12 @@ init_esbuild_shims();
1185
1175
  var SESSION_SOURCE_META_KEY = "qwen.session.source";
1186
1176
  var SESSION_SOURCE_TYPE_PATTERN = /^[a-z][a-z0-9_-]{0,63}$/;
1187
1177
  var MAX_SESSION_SOURCE_ID_LENGTH = 256;
1178
+ var STANDALONE_SESSION_SOURCE_TYPE = "standalone";
1179
+ var DAEMON_OWNED_STANDALONE_CREATION_KEY = "daemonOwnedStandaloneCreation";
1180
+ function isReservedStandaloneSessionSourceType(sourceType) {
1181
+ return sourceType === STANDALONE_SESSION_SOURCE_TYPE;
1182
+ }
1183
+ __name(isReservedStandaloneSessionSourceType, "isReservedStandaloneSessionSourceType");
1188
1184
  function parseSessionSource(sourceType, sourceId) {
1189
1185
  if (sourceType === void 0 && sourceId === void 0) return {};
1190
1186
  if (typeof sourceType !== "string" || !SESSION_SOURCE_TYPE_PATTERN.test(sourceType)) {
@@ -1237,8 +1233,8 @@ function startChannelLivenessMonitor(options) {
1237
1233
  let consecutiveTimeouts = 0;
1238
1234
  let nextNonce = 0;
1239
1235
  let resolveStopped;
1240
- const stoppedOutcome = new Promise((resolve2) => {
1241
- resolveStopped = resolve2;
1236
+ const stoppedOutcome = new Promise((resolve) => {
1237
+ resolveStopped = resolve;
1242
1238
  });
1243
1239
  const isActive = /* @__PURE__ */ __name(() => !stopped && options.isActive(), "isActive");
1244
1240
  const stop = /* @__PURE__ */ __name(() => {
@@ -1266,10 +1262,10 @@ function startChannelLivenessMonitor(options) {
1266
1262
  let outcome;
1267
1263
  while (true) {
1268
1264
  const expectedAt = now() + CHANNEL_LIVENESS_PROBE_TIMEOUT_MS;
1269
- const timeout = new Promise((resolve2) => {
1265
+ const timeout = new Promise((resolve) => {
1270
1266
  timer = setTimeout(() => {
1271
1267
  timer = void 0;
1272
- resolve2(
1268
+ resolve(
1273
1269
  timerWasLate(expectedAt) ? { kind: "local_delay" } : { kind: "timeout" }
1274
1270
  );
1275
1271
  }, CHANNEL_LIVENESS_PROBE_TIMEOUT_MS);
@@ -1329,3273 +1325,104 @@ __name(startChannelLivenessMonitor, "startChannelLivenessMonitor");
1329
1325
  // packages/acp-bridge/src/channel-startup-profile.ts
1330
1326
  init_esbuild_shims();
1331
1327
  var MAX_PROFILE_DURATION_MS = 6e5;
1332
- var ATTRIBUTE_PREFIX = "qwen-code.daemon.acp_startup";
1333
- function isRecord(value) {
1334
- return value !== null && typeof value === "object" && !Array.isArray(value);
1335
- }
1336
- __name(isRecord, "isRecord");
1337
- function readDuration(source, key) {
1338
- const value = source[key];
1339
- return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= MAX_PROFILE_DURATION_MS ? value : void 0;
1340
- }
1341
- __name(readDuration, "readDuration");
1342
- function readDurations(source, keys) {
1343
- if (!isRecord(source)) {
1344
- return { values: {}, complete: false };
1345
- }
1346
- const values = {};
1347
- let complete = true;
1348
- for (const key of keys) {
1349
- const value = readDuration(source, key);
1350
- if (value === void 0) {
1351
- complete = false;
1352
- } else {
1353
- values[key] = value;
1354
- }
1355
- }
1356
- return { values, complete };
1357
- }
1358
- __name(readDurations, "readDurations");
1359
- function addDurationAttributes(attributes, group, values) {
1360
- for (const [key, value] of Object.entries(values)) {
1361
- if (group === "phase" && key === "unattributedMs") continue;
1362
- const attributeKey = key.replace(
1363
- /[A-Z]/g,
1364
- (letter) => `_${letter.toLowerCase()}`
1365
- );
1366
- attributes[`${ATTRIBUTE_PREFIX}.${group}.${attributeKey}`] = value;
1367
- }
1368
- }
1369
- __name(addDurationAttributes, "addDurationAttributes");
1370
- var PHASE_KEYS = [
1371
- "processToProfilerReadyMs",
1372
- "geminiImportMs",
1373
- "argsParseMs",
1374
- "settingsLoadMs",
1375
- "configConstructionMs",
1376
- "appInitializationMs",
1377
- "acpImportMs",
1378
- "bootstrapConfigInitializationMs",
1379
- "transportSetupMs",
1380
- "initializeHandlerMs",
1381
- "unattributedMs"
1382
- ];
1383
- var CONFIG_KEYS = [
1384
- "extensionsInitialMs",
1385
- "hooksMs",
1386
- "skillsMs",
1387
- "extensionsFinalMs",
1388
- "hierarchicalMemoryMs",
1389
- "toolRegistryMs",
1390
- "ripgrepProbeMs",
1391
- "toolWarmupMs",
1392
- "otherMs"
1393
- ];
1394
- function getChannelStartupProfileAttributes(response, receivedAtEpochMs, initializeTimeoutMs) {
1395
- if (!isRecord(response) || !isRecord(response["_meta"])) {
1396
- return void 0;
1397
- }
1398
- const profile = response["_meta"][CHANNEL_STARTUP_PROFILE_META_KEY];
1399
- if (!isRecord(profile) || profile["v"] !== CHANNEL_STARTUP_PROFILE_VERSION) {
1400
- return void 0;
1401
- }
1402
- const phases = readDurations(profile["phases"], PHASE_KEYS);
1403
- const config = readDurations(profile["config"], CONFIG_KEYS);
1404
- const processToResponseMs = readDuration(profile, "processToResponseMs");
1405
- const responseBuiltAtEpochMs = profile["responseBuiltAtEpochMs"];
1406
- const childComplete = profile["complete"] === true;
1407
- const validResponseEpoch = typeof responseBuiltAtEpochMs === "number" && Number.isFinite(responseBuiltAtEpochMs) && responseBuiltAtEpochMs >= 0;
1408
- const effectiveComplete = childComplete && phases.complete && config.complete && processToResponseMs !== void 0 && validResponseEpoch;
1409
- const attributes = {
1410
- [`${ATTRIBUTE_PREFIX}.profile.version`]: CHANNEL_STARTUP_PROFILE_VERSION,
1411
- [`${ATTRIBUTE_PREFIX}.profile.complete`]: effectiveComplete
1412
- };
1413
- if (processToResponseMs !== void 0) {
1414
- attributes[`${ATTRIBUTE_PREFIX}.child.process_to_response_ms`] = processToResponseMs;
1415
- }
1416
- if (phases.values["unattributedMs"] !== void 0) {
1417
- attributes[`${ATTRIBUTE_PREFIX}.child.unattributed_ms`] = phases.values["unattributedMs"];
1418
- }
1419
- addDurationAttributes(attributes, "phase", phases.values);
1420
- addDurationAttributes(attributes, "config", config.values);
1421
- if (validResponseEpoch) {
1422
- const transportMs = receivedAtEpochMs - responseBuiltAtEpochMs;
1423
- if (Number.isFinite(transportMs) && transportMs >= 0 && transportMs <= initializeTimeoutMs) {
1424
- attributes[`${ATTRIBUTE_PREFIX}.response_transport_ms`] = transportMs;
1425
- }
1426
- }
1427
- return attributes;
1428
- }
1429
- __name(getChannelStartupProfileAttributes, "getChannelStartupProfileAttributes");
1430
-
1431
- // packages/acp-bridge/src/sessionAttachments.ts
1432
- init_esbuild_shims();
1433
- import { randomUUID } from "node:crypto";
1434
- import { promises as fs, statSync } from "node:fs";
1435
- import { tmpdir } from "node:os";
1436
- import * as path from "node:path";
1437
- var SESSION_ATTACHMENT_MAX_ITEM_BYTES = 8 * 1024 * 1024;
1438
- var SESSION_ATTACHMENT_MAX_NAME_BYTES = 255;
1439
- var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
1440
- "image/bmp",
1441
- "image/gif",
1442
- "image/jpeg",
1443
- "image/png",
1444
- "image/webp"
1445
- ]);
1446
- var SESSION_ATTACHMENT_UNAVAILABLE_TEXT = "[Attachment is no longer available]";
1447
- var SessionAttachmentReferenceError = class extends Error {
1448
- constructor(message, code) {
1449
- super(message);
1450
- this.code = code;
1451
- this.name = "SessionAttachmentReferenceError";
1452
- }
1453
- static {
1454
- __name(this, "SessionAttachmentReferenceError");
1455
- }
1456
- };
1457
- function isSessionAttachmentReference(value) {
1458
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1459
- const record = value;
1460
- return (record["type"] === "image" || record["type"] === "resource") && typeof record["attachmentId"] === "string" && record["attachmentId"].length > 0 && typeof record["mimeType"] === "string" && record["mimeType"].length > 0 && (record["type"] !== "image" || record["mimeType"].startsWith("image/")) && typeof record["size"] === "number" && Number.isSafeInteger(record["size"]) && record["size"] >= 0 && (record["type"] !== "image" || record["size"] > 0);
1461
- }
1462
- __name(isSessionAttachmentReference, "isSessionAttachmentReference");
1463
- function safeAttachmentName(name) {
1464
- const safeName = path.basename(name.replaceAll("\\", "/")).trim();
1465
- const isWindowsReserved = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(safeName);
1466
- const hasInvalidCharacter = Array.from(safeName).some((character) => {
1467
- const codePoint = character.codePointAt(0);
1468
- return codePoint !== void 0 && (codePoint <= 31 || codePoint === 127 || codePoint >= 55296 && codePoint <= 57343);
1469
- });
1470
- return !safeName || safeName === "." || safeName === ".." || safeName.endsWith(".") || isWindowsReserved || /[<>:"|?*]/.test(safeName) || hasInvalidCharacter || Buffer.byteLength(safeName) > SESSION_ATTACHMENT_MAX_NAME_BYTES ? void 0 : safeName;
1471
- }
1472
- __name(safeAttachmentName, "safeAttachmentName");
1473
- function truncateUtf8(value, maxBytes) {
1474
- let bytes = 0;
1475
- let result = "";
1476
- for (const character of value) {
1477
- const characterBytes = Buffer.byteLength(character);
1478
- if (bytes + characterBytes > maxBytes) break;
1479
- result += character;
1480
- bytes += characterBytes;
1481
- }
1482
- return result;
1483
- }
1484
- __name(truncateUtf8, "truncateUtf8");
1485
- function deduplicatedName(name, suffix) {
1486
- if (suffix === 0) return name;
1487
- const extension = path.extname(name);
1488
- const suffixText = ` (${suffix})`;
1489
- const stem = name.slice(0, -extension.length || void 0);
1490
- const extensionBudget = SESSION_ATTACHMENT_MAX_NAME_BYTES - Buffer.byteLength(suffixText) - 1;
1491
- const safeExtension = truncateUtf8(extension, extensionBudget).replace(
1492
- /[. ]+$/u,
1493
- ""
1494
- );
1495
- const stemBudget = SESSION_ATTACHMENT_MAX_NAME_BYTES - Buffer.byteLength(suffixText) - Buffer.byteLength(safeExtension);
1496
- return `${truncateUtf8(stem, stemBudget)}${suffixText}${safeExtension}`;
1497
- }
1498
- __name(deduplicatedName, "deduplicatedName");
1499
- function imageName(mimeType) {
1500
- const extension = mimeType.slice("image/".length).split(/[;+]/, 1)[0];
1501
- return `image.${extension === "jpg" ? "jpeg" : extension || "img"}`;
1502
- }
1503
- __name(imageName, "imageName");
1504
- function mimeTypeForName(name) {
1505
- if ([".ts", ".mts", ".cts", ".tsx"].includes(path.extname(name).toLowerCase())) {
1506
- return "text/plain";
1507
- }
1508
- return getSpecificMimeType(name) ?? "application/octet-stream";
1509
- }
1510
- __name(mimeTypeForName, "mimeTypeForName");
1511
- function isSupportedImageMimeType(mimeType) {
1512
- return SUPPORTED_IMAGE_MIME_TYPES.has(mimeType);
1513
- }
1514
- __name(isSupportedImageMimeType, "isSupportedImageMimeType");
1515
- function isTextMimeType(mimeType) {
1516
- return mimeType.startsWith("text/") || mimeType === "application/json" || mimeType.endsWith("+json") || mimeType === "application/xml" || mimeType.endsWith("+xml") || mimeType === "application/javascript" || mimeType === "application/typescript" || mimeType === "application/yaml" || mimeType === "application/x-yaml" || mimeType === "application/toml";
1517
- }
1518
- __name(isTextMimeType, "isTextMimeType");
1519
- function isTextAttachment(data, mimeType) {
1520
- if (isTextMimeType(mimeType)) return true;
1521
- if (mimeType.startsWith("image/") || mimeType.startsWith("audio/") || mimeType.startsWith("video/") || mimeType.startsWith("font/") || mimeType === "application/pdf" || data.includes(0)) {
1522
- return false;
1523
- }
1524
- return Buffer.from(data.toString("utf8"), "utf8").equals(data);
1525
- }
1526
- __name(isTextAttachment, "isTextAttachment");
1527
- function withAttachmentDegradationMarker(blocks) {
1528
- for (let i = blocks.length - 1; i >= 0; i--) {
1529
- const block = blocks[i];
1530
- if (block.type === "text") {
1531
- if (block.text.endsWith(SESSION_ATTACHMENT_UNAVAILABLE_TEXT)) {
1532
- return [...blocks];
1533
- }
1534
- const next = [...blocks];
1535
- next[i] = {
1536
- type: "text",
1537
- text: `${block.text}
1538
- ${SESSION_ATTACHMENT_UNAVAILABLE_TEXT}`
1539
- };
1540
- return next;
1541
- }
1542
- }
1543
- return [
1544
- ...blocks,
1545
- { type: "text", text: SESSION_ATTACHMENT_UNAVAILABLE_TEXT }
1546
- ];
1547
- }
1548
- __name(withAttachmentDegradationMarker, "withAttachmentDegradationMarker");
1549
- var SessionAttachmentStore = class {
1550
- constructor(directoryRoot, sessionId) {
1551
- this.directoryRoot = directoryRoot;
1552
- if (!directoryRoot || !sessionId) return;
1553
- this.persistentDirectory = path.join(
1554
- directoryRoot,
1555
- `session-${encodeURIComponent(sessionId)}`
1556
- );
1557
- }
1558
- static {
1559
- __name(this, "SessionAttachmentStore");
1560
- }
1561
- directoryPromise;
1562
- persistentDirectory;
1563
- activeDirectory;
1564
- pendingItems = 0;
1565
- pendingNames = /* @__PURE__ */ new Map();
1566
- pendingDrainWaiters = [];
1567
- copyDrainWaiters = [];
1568
- copying = false;
1569
- closing = false;
1570
- closed = false;
1571
- async putAttachment(data, mimeType, name) {
1572
- const isImage = isSupportedImageMimeType(mimeType);
1573
- const safeName = safeAttachmentName(
1574
- name ?? (isImage ? imageName(mimeType) : "")
1575
- );
1576
- if (!safeName) {
1577
- throw new TypeError("Session attachment name is invalid");
1578
- }
1579
- const storedMimeType = mimeTypeForName(safeName);
1580
- if (isImage && storedMimeType !== mimeType || isSupportedImageMimeType(storedMimeType) && !isImage) {
1581
- throw new TypeError("Attachment name and Content-Type do not match");
1582
- }
1583
- if (this.closed || this.closing) {
1584
- throw new Error("Session attachment store is closed");
1585
- }
1586
- if (this.copying) throw new Error("Session attachments are being copied");
1587
- if (isImage && data.byteLength === 0 || data.byteLength > SESSION_ATTACHMENT_MAX_ITEM_BYTES) {
1588
- throw new RangeError(
1589
- `Session attachment must be at most ${SESSION_ATTACHMENT_MAX_ITEM_BYTES} bytes and images cannot be empty`
1590
- );
1591
- }
1592
- let filePath;
1593
- let pendingName = safeName;
1594
- let removeFileOnFailure = false;
1595
- this.pendingItems += 1;
1596
- this.reservePendingName(safeName);
1597
- try {
1598
- const directory = await this.directory();
1599
- let suffix = 0;
1600
- for (; ; ) {
1601
- const candidateName = deduplicatedName(safeName, suffix);
1602
- if (safeAttachmentName(candidateName) !== candidateName) {
1603
- throw new TypeError("Session attachment name is invalid");
1604
- }
1605
- if (pendingName !== candidateName) {
1606
- if (pendingName) this.releasePendingName(pendingName);
1607
- this.reservePendingName(candidateName);
1608
- pendingName = candidateName;
1609
- }
1610
- filePath = path.join(directory, candidateName);
1611
- removeFileOnFailure = true;
1612
- try {
1613
- await fs.writeFile(filePath, data, { flag: "wx" });
1614
- break;
1615
- } catch (error) {
1616
- if (error.code !== "EEXIST") throw error;
1617
- removeFileOnFailure = false;
1618
- this.releasePendingName(candidateName);
1619
- pendingName = void 0;
1620
- filePath = void 0;
1621
- suffix += 1;
1622
- }
1623
- }
1624
- if (this.closed || this.closing) {
1625
- throw new Error("Session attachment store is closed");
1626
- }
1627
- const name2 = path.basename(filePath);
1628
- const storedMimeType2 = mimeTypeForName(name2);
1629
- const reference = {
1630
- type: isSupportedImageMimeType(storedMimeType2) ? "image" : "resource",
1631
- attachmentId: name2,
1632
- mimeType: storedMimeType2,
1633
- size: data.byteLength
1634
- };
1635
- return reference;
1636
- } catch (error) {
1637
- if (removeFileOnFailure && filePath) {
1638
- await fs.rm(filePath, { force: true }).catch(() => {
1639
- });
1640
- }
1641
- throw error;
1642
- } finally {
1643
- if (pendingName) this.releasePendingName(pendingName);
1644
- if (!this.closed) {
1645
- this.pendingItems -= 1;
1646
- if (this.pendingItems === 0) {
1647
- this.resolvePendingDrainWaiters();
1648
- }
1649
- }
1650
- }
1651
- }
1652
- // Validate one block against the store. Ordinary ACP content passes through
1653
- // untouched, matching `assertReferences`.
1654
- assertReference(block) {
1655
- if (!block || typeof block !== "object" || Array.isArray(block) || !("attachmentId" in block)) {
1656
- return;
1657
- }
1658
- if (!isSessionAttachmentReference(block)) {
1659
- throw new SessionAttachmentReferenceError(
1660
- "Invalid session attachment reference",
1661
- "invalid_session_attachment_reference"
1662
- );
1663
- }
1664
- this.assertStored(block);
1665
- }
1666
- assertReferences(content) {
1667
- const seenIds = /* @__PURE__ */ new Set();
1668
- for (const block of content) {
1669
- if (!block || typeof block !== "object" || Array.isArray(block) || !("attachmentId" in block)) {
1670
- continue;
1671
- }
1672
- if (!isSessionAttachmentReference(block)) {
1673
- throw new SessionAttachmentReferenceError(
1674
- "Invalid session attachment reference",
1675
- "invalid_session_attachment_reference"
1676
- );
1677
- }
1678
- const id = block.attachmentId;
1679
- if (seenIds.has(id)) {
1680
- throw new SessionAttachmentReferenceError(
1681
- `Session attachment referenced more than once: ${id}`,
1682
- "invalid_session_attachment_reference"
1683
- );
1684
- }
1685
- seenIds.add(id);
1686
- this.assertStored(block);
1687
- }
1688
- }
1689
- async resolveContent(content, memo) {
1690
- const pendingById = memo ?? /* @__PURE__ */ new Map();
1691
- return await Promise.all(
1692
- content.map(async (block) => {
1693
- if (!isSessionAttachmentReference(block)) return block;
1694
- const id = block.attachmentId;
1695
- let pending = pendingById.get(id);
1696
- if (!pending) {
1697
- const created = this.resolve(block);
1698
- pendingById.set(id, created);
1699
- void created.catch(() => {
1700
- if (pendingById.get(id) === created) {
1701
- pendingById.delete(id);
1702
- }
1703
- });
1704
- pending = created;
1705
- }
1706
- return await pending;
1707
- })
1708
- );
1709
- }
1710
- // Per-block variant of `resolveContent` for degrade paths: one unresolvable
1711
- // reference drops only itself, keeping the sibling blocks a wholesale
1712
- // fallback would discard. Other errors still propagate.
1713
- async resolveContentDegrading(content, memo) {
1714
- const retainedBlocks = [];
1715
- const resolvedBlocks = [];
1716
- let degraded = 0;
1717
- for (const block of content) {
1718
- if (!isSessionAttachmentReference(block)) {
1719
- retainedBlocks.push(block);
1720
- resolvedBlocks.push(block);
1721
- continue;
1722
- }
1723
- try {
1724
- const [resolved] = await this.resolveContent([block], memo);
1725
- if (resolved) resolvedBlocks.push(resolved);
1726
- retainedBlocks.push(block);
1727
- } catch (error) {
1728
- if (!(error instanceof SessionAttachmentReferenceError)) throw error;
1729
- degraded += 1;
1730
- }
1731
- }
1732
- return { retainedBlocks, resolvedBlocks, degraded };
1733
- }
1734
- async read(attachmentId) {
1735
- const name = safeAttachmentName(attachmentId);
1736
- if (!name || name !== attachmentId) return void 0;
1737
- const filePath = path.join(await this.directory(), name);
1738
- try {
1739
- return {
1740
- data: await fs.readFile(filePath),
1741
- mimeType: mimeTypeForName(name)
1742
- };
1743
- } catch (error) {
1744
- if (error.code === "ENOENT") {
1745
- return void 0;
1746
- }
1747
- throw error;
1748
- }
1749
- }
1750
- async copyFrom(source) {
1751
- if (source === this) return;
1752
- if (this.closed || this.closing) {
1753
- throw new Error("Session attachment store is closed");
1754
- }
1755
- if (this.copying) throw new Error("Session attachments are being copied");
1756
- if (source.closed || source.closing) {
1757
- throw new Error("Session attachment store is closed");
1758
- }
1759
- if (source.copying) {
1760
- throw new Error("Session attachments are being copied");
1761
- }
1762
- this.copying = true;
1763
- source.copying = true;
1764
- try {
1765
- if (this.pendingItems > 0) {
1766
- await new Promise(
1767
- (resolve2) => this.pendingDrainWaiters.push(resolve2)
1768
- );
1769
- }
1770
- if (source.pendingItems > 0) {
1771
- await new Promise(
1772
- (resolve2) => source.pendingDrainWaiters.push(resolve2)
1773
- );
1774
- }
1775
- if (this.closed) throw new Error("Session attachment store is closed");
1776
- const sourceDirectory = source.persistentDirectory ?? source.activeDirectory;
1777
- if (!sourceDirectory) return;
1778
- let entries;
1779
- try {
1780
- entries = await fs.readdir(sourceDirectory, { withFileTypes: true });
1781
- } catch (error) {
1782
- if (error.code === "ENOENT") return;
1783
- throw error;
1784
- }
1785
- const targetDirectory = await this.directory();
1786
- await Promise.all(
1787
- entries.filter(
1788
- (entry) => entry.isFile() && !source.pendingNames.has(entry.name)
1789
- ).map(async (entry) => {
1790
- const sourcePath = path.join(sourceDirectory, entry.name);
1791
- try {
1792
- await fs.copyFile(
1793
- sourcePath,
1794
- path.join(targetDirectory, entry.name)
1795
- );
1796
- } catch (error) {
1797
- if (error.code === "ENOENT") {
1798
- try {
1799
- await fs.stat(sourcePath);
1800
- } catch (sourceError) {
1801
- if (sourceError.code === "ENOENT") {
1802
- return;
1803
- }
1804
- }
1805
- }
1806
- throw error;
1807
- }
1808
- })
1809
- );
1810
- } finally {
1811
- source.copying = false;
1812
- this.copying = false;
1813
- source.resolveCopyDrainWaiters();
1814
- this.resolveCopyDrainWaiters();
1815
- }
1816
- }
1817
- async remove(attachmentId) {
1818
- const name = safeAttachmentName(attachmentId);
1819
- if (!name || name !== attachmentId || this.copying || this.pendingNames.has(name)) {
1820
- return false;
1821
- }
1822
- const directory = await this.directory();
1823
- const filePath = path.join(directory, name);
1824
- try {
1825
- await fs.unlink(filePath);
1826
- return true;
1827
- } catch (error) {
1828
- if (error.code === "ENOENT") {
1829
- return false;
1830
- }
1831
- throw error;
1832
- }
1833
- }
1834
- async close() {
1835
- this.closing = true;
1836
- await this.waitForCopy();
1837
- if (this.closed) return;
1838
- this.closed = true;
1839
- this.pendingItems = 0;
1840
- this.pendingNames.clear();
1841
- this.resolvePendingDrainWaiters();
1842
- if (this.persistentDirectory || !this.directoryPromise) return;
1843
- const directory = await this.directoryPromise.catch(() => void 0);
1844
- if (!directory) return;
1845
- await fs.rm(directory, { recursive: true, force: true });
1846
- }
1847
- async delete(options = {}) {
1848
- options.assertCanCommit?.();
1849
- this.closing = true;
1850
- await this.waitForCopy();
1851
- options.assertCanCommit?.();
1852
- if (!this.closed) {
1853
- this.closed = true;
1854
- this.pendingItems = 0;
1855
- this.pendingNames.clear();
1856
- this.resolvePendingDrainWaiters();
1857
- }
1858
- const directory = this.persistentDirectory ?? await this.directoryPromise?.catch(() => void 0);
1859
- if (directory) {
1860
- options.assertCanCommit?.();
1861
- const tombstone = path.join(
1862
- path.dirname(directory),
1863
- `.${path.basename(directory)}.deleting-${randomUUID()}`
1864
- );
1865
- try {
1866
- await fs.rename(directory, tombstone);
1867
- } catch (error) {
1868
- if (error.code === "ENOENT") return;
1869
- throw error;
1870
- }
1871
- await fs.rm(tombstone, { recursive: true, force: true });
1872
- }
1873
- }
1874
- assertStored(reference) {
1875
- const id = reference.attachmentId;
1876
- const name = safeAttachmentName(id);
1877
- let size;
1878
- const directory = this.persistentDirectory ?? this.activeDirectory;
1879
- if (name && name === id && directory) {
1880
- try {
1881
- size = statSync(path.join(directory, name)).size;
1882
- } catch {
1883
- size = void 0;
1884
- }
1885
- }
1886
- const storedMimeType = name ? mimeTypeForName(name) : void 0;
1887
- const storedType = storedMimeType ? isSupportedImageMimeType(storedMimeType) ? "image" : "resource" : void 0;
1888
- if (size !== reference.size || storedMimeType !== reference.mimeType || storedType !== reference.type) {
1889
- throw new SessionAttachmentReferenceError(
1890
- `Unknown or unavailable session attachment: ${id}`,
1891
- "session_attachment_gone"
1892
- );
1893
- }
1894
- }
1895
- releasePendingName(name) {
1896
- const count = this.pendingNames.get(name) ?? 0;
1897
- if (count <= 1) this.pendingNames.delete(name);
1898
- else this.pendingNames.set(name, count - 1);
1899
- }
1900
- reservePendingName(name) {
1901
- this.pendingNames.set(name, (this.pendingNames.get(name) ?? 0) + 1);
1902
- }
1903
- async waitForCopy() {
1904
- while (this.copying) {
1905
- await new Promise((resolve2) => this.copyDrainWaiters.push(resolve2));
1906
- }
1907
- }
1908
- resolveCopyDrainWaiters() {
1909
- for (const resolve2 of this.copyDrainWaiters.splice(0)) resolve2();
1910
- }
1911
- resolvePendingDrainWaiters() {
1912
- for (const resolve2 of this.pendingDrainWaiters.splice(0)) resolve2();
1913
- }
1914
- async resolve(reference) {
1915
- const id = reference.attachmentId;
1916
- const attachment = await this.read(id);
1917
- if (!attachment) {
1918
- throw new SessionAttachmentReferenceError(
1919
- `Unknown or unavailable session attachment: ${id}`,
1920
- "session_attachment_gone"
1921
- );
1922
- }
1923
- if (reference.type === "resource") {
1924
- const resource = {
1925
- uri: `attachment:///${encodeURIComponent(reference.attachmentId)}`,
1926
- mimeType: attachment.mimeType,
1927
- ...isTextAttachment(attachment.data, attachment.mimeType) ? { text: attachment.data.toString("utf8") } : { blob: attachment.data.toString("base64") }
1928
- };
1929
- return {
1930
- type: "resource",
1931
- resource
1932
- };
1933
- }
1934
- return {
1935
- type: "image",
1936
- data: attachment.data.toString("base64"),
1937
- mimeType: attachment.mimeType
1938
- };
1939
- }
1940
- async directory() {
1941
- if (!this.directoryPromise) {
1942
- const pending = this.persistentDirectory ? fs.mkdir(this.persistentDirectory, {
1943
- recursive: true,
1944
- mode: 448
1945
- }).then(() => this.persistentDirectory) : this.directoryRoot ? fs.mkdir(this.directoryRoot, { recursive: true, mode: 448 }).then(
1946
- () => fs.mkdtemp(
1947
- path.join(this.directoryRoot, "session-attachment-")
1948
- )
1949
- ) : fs.mkdtemp(path.join(tmpdir(), "qwen-session-attachment-"));
1950
- const directoryPromise = pending.then((directory) => {
1951
- this.activeDirectory = directory;
1952
- return directory;
1953
- });
1954
- this.directoryPromise = directoryPromise;
1955
- void directoryPromise.catch(() => {
1956
- if (this.directoryPromise === directoryPromise)
1957
- this.directoryPromise = void 0;
1958
- });
1959
- }
1960
- return await this.directoryPromise;
1961
- }
1962
- };
1963
-
1964
- // packages/acp-bridge/src/internal/stderrLine.ts
1965
- init_esbuild_shims();
1966
- function writeStderrLine(message) {
1967
- process.stderr.write(message.endsWith("\n") ? message : `${message}
1968
- `);
1969
- }
1970
- __name(writeStderrLine, "writeStderrLine");
1971
-
1972
- // packages/acp-bridge/src/bridgeClient.ts
1973
- init_esbuild_shims();
1974
- import { randomUUID as randomUUID2 } from "node:crypto";
1975
- import { promises as fs2 } from "node:fs";
1976
- import * as path2 from "node:path";
1977
-
1978
- // packages/acp-bridge/src/daemonEventTypes.ts
1979
- init_esbuild_shims();
1980
- var MID_TURN_MESSAGE_INJECTED_EVENT = "mid_turn_message_injected";
1981
-
1982
- // packages/acp-bridge/src/permissionMediator.ts
1983
- init_esbuild_shims();
1984
-
1985
- // packages/acp-bridge/src/permission.ts
1986
- init_esbuild_shims();
1987
-
1988
- // packages/acp-bridge/src/permissionMediator.ts
1989
- var CANCEL_VOTE_SENTINEL = "__cancelled__";
1990
- var MAX_RESOLVED_PERMISSION_RECORDS = 512;
1991
- function stringifyError(err) {
1992
- try {
1993
- if (err instanceof Error) return `${err.name}: ${err.message}`;
1994
- return String(err);
1995
- } catch {
1996
- return "[unstringifiable error]";
1997
- }
1998
- }
1999
- __name(stringifyError, "stringifyError");
2000
- function createNoOpPermissionAuditPublisher() {
2001
- return {
2002
- recordRequested() {
2003
- },
2004
- recordVoted() {
2005
- },
2006
- recordForbidden() {
2007
- },
2008
- recordResolved() {
2009
- },
2010
- recordTimeout() {
2011
- }
2012
- };
2013
- }
2014
- __name(createNoOpPermissionAuditPublisher, "createNoOpPermissionAuditPublisher");
2015
- var MultiClientPermissionMediator = class {
2016
- static {
2017
- __name(this, "MultiClientPermissionMediator");
2018
- }
2019
- policy;
2020
- deps;
2021
- pending = /* @__PURE__ */ new Map();
2022
- resolved = /* @__PURE__ */ new Map();
2023
- resolvedOrder = [];
2024
- /**
2025
- * Dedup flag for the
2026
- * unanimity-required stderr breadcrumb. Without this, every
2027
- * permission request on a 2-client consensus session would emit
2028
- * an identical line (the unanimity condition is the NORMAL
2029
- * operating mode for M=2, not a rare edge); a busy session with
2030
- * many tool calls would produce dozens of duplicate stderr lines
2031
- * within seconds. One emit per mediator (= per bridge/runtime lifetime
2032
- * since each bridge constructs one) is enough to make the
2033
- * configuration visible without spam.
2034
- */
2035
- unanimityBreadcrumbEmitted = false;
2036
- constructor(policy, deps) {
2037
- this.policy = policy;
2038
- this.deps = deps;
2039
- }
2040
- /**
2041
- * Register a fresh permission request from the agent.
2042
- *
2043
- * **Promise contract — once the Promise is returned, it never
2044
- * rejects.** All runtime failure modes (timeout, session closure,
2045
- * voter cancel, emit/audit publisher exceptions) are encoded as
2046
- * `PermissionResolution { kind:'cancelled', reason:... }`.
2047
- * Consumers can `await` the returned Promise and forward the
2048
- * result without a `.catch()` block.
2049
- *
2050
- * **Synchronous-throw exception**:
2051
- * when the agent's `allowedOptionIds` contains the
2052
- * cancel-vote sentinel string, this method throws
2053
- * `CancelSentinelCollisionError` synchronously BEFORE constructing
2054
- * the Promise. The synchronous shape is intentional — a
2055
- * never-settling Promise alongside a thrown error would be worse
2056
- * than a clean fail-fast — but callers must wrap this method
2057
- * itself in `try/catch` (or call it from an `async` function so
2058
- * the throw bubbles via the function's own Promise machinery).
2059
- * `bridgeClient.ts` currently has its own pre-check at the bridge
2060
- * layer; embedded callers must do the same. See `@throws` below.
2061
- *
2062
- * **Synchronous-register invariant**: pending entry, audit
2063
- * record, and timer setup all happen inside the Promise executor
2064
- * without `await`. The bridge's `publish → mediator.request → await`
2065
- * sequence relies on this — a `forgetSession` between publish and
2066
- * await would otherwise miss the new pending and leak it until
2067
- * timeout.
2068
- *
2069
- * @throws `CancelSentinelCollisionError` SYNCHRONOUSLY (not as a
2070
- * Promise rejection) if `record.allowedOptionIds` contains the
2071
- * cancel-vote sentinel string. This is a contract violation
2072
- * between agent and daemon and fails loudly at issue time
2073
- * rather than silently miscounting votes downstream. Callers
2074
- * inside an `async` function get the thrown error through the
2075
- * function's own Promise; synchronous callers must use
2076
- * `try/catch`.
2077
- */
2078
- request(record, timeoutMs) {
2079
- if (record.allowedOptionIds.has(CANCEL_VOTE_SENTINEL)) {
2080
- throw new CancelSentinelCollisionError(
2081
- record.requestId,
2082
- CANCEL_VOTE_SENTINEL
2083
- );
2084
- }
2085
- return new Promise((resolve2) => {
2086
- const policy = this.policy;
2087
- const votersAtIssue = this.deps.votersForSession(record.sessionId);
2088
- const pending = {
2089
- requestId: record.requestId,
2090
- sessionId: record.sessionId,
2091
- promptId: record.promptId,
2092
- policy,
2093
- originatorClientId: record.originatorClientId,
2094
- allowedOptionIds: record.allowedOptionIds,
2095
- issuedAtMs: record.issuedAtMs,
2096
- timeoutMs,
2097
- resolve: resolve2,
2098
- tallies: /* @__PURE__ */ new Map(),
2099
- votersAtIssue,
2100
- timer: void 0,
2101
- consensusQuorumCapNoted: false
2102
- };
2103
- this.pending.set(record.requestId, pending);
2104
- this.safeAudit(
2105
- () => this.deps.audit.recordRequested(record, policy, votersAtIssue)
2106
- );
2107
- const unresolvedWithoutDecision = timeoutMs > 0 ? `voter cancellation, session cancellation, or permissionTimeoutMs (${timeoutMs}ms)` : "voter or session cancellation because permissionTimeoutMs is disabled";
2108
- if (policy === "consensus" && votersAtIssue.size === 0) {
2109
- try {
2110
- process.stderr.write(
2111
- `permissionMediator: consensus request ${record.requestId} for session ${record.sessionId} issued with empty votersAtIssue; can only resolve via ${unresolvedWithoutDecision}
2112
- `
2113
- );
2114
- } catch {
2115
- }
2116
- }
2117
- if (policy === "consensus" && this.deps.consensusQuorum === void 0 && votersAtIssue.size >= 2 && Math.floor(votersAtIssue.size / 2) + 1 === votersAtIssue.size && !this.unanimityBreadcrumbEmitted) {
2118
- this.unanimityBreadcrumbEmitted = true;
2119
- try {
2120
- process.stderr.write(
2121
- `permissionMediator: consensus request ${record.requestId} for session ${record.sessionId} requires unanimity (votersAtIssue.size=${votersAtIssue.size}, default quorum=floor(M/2)+1=${votersAtIssue.size}); split votes will only resolve via ${unresolvedWithoutDecision}. This breadcrumb fires once per mediator lifetime; subsequent unanimity-required requests are silent.
2122
- `
2123
- );
2124
- } catch {
2125
- }
2126
- }
2127
- if (timeoutMs > 0) {
2128
- pending.timer = setTimeout(() => {
2129
- if (this.pending.get(record.requestId) !== pending) return;
2130
- const firedAtMs = this.deps.now();
2131
- try {
2132
- process.stderr.write(
2133
- `qwen serve: permission ${record.requestId} (session ${record.sessionId}) timed out after ${timeoutMs}ms
2134
- `
2135
- );
2136
- } catch {
2137
- }
2138
- this.safeAudit(() => this.deps.audit.recordTimeout(record));
2139
- this.resolveEntry(
2140
- pending,
2141
- { kind: "cancelled", reason: "timeout" },
2142
- {
2143
- type: "timeout",
2144
- issuedAtMs: pending.issuedAtMs,
2145
- timeoutMs: pending.timeoutMs,
2146
- firedAtMs
2147
- },
2148
- void 0
2149
- );
2150
- }, timeoutMs);
2151
- const t = pending.timer;
2152
- if (t && typeof t === "object" && "unref" in t) {
2153
- t.unref();
2154
- }
2155
- }
2156
- });
2157
- }
2158
- vote(vote) {
2159
- const pending = this.pending.get(vote.requestId);
2160
- if (!pending) {
2161
- const prior = this.resolved.get(vote.requestId);
2162
- if (prior && prior.sessionId === vote.sessionId) {
2163
- const optionId = prior.resolution.kind === "option" ? prior.resolution.optionId : CANCEL_VOTE_SENTINEL;
2164
- this.safeEmit(prior.sessionId, {
2165
- type: "permission_already_resolved",
2166
- ...prior.promptId ? { promptId: prior.promptId } : {},
2167
- data: {
2168
- requestId: prior.requestId,
2169
- sessionId: prior.sessionId,
2170
- outcome: this.toAcpOutcome(prior.resolution)
2171
- }
2172
- });
2173
- return { kind: "already_resolved", resolvedOptionId: optionId };
2174
- }
2175
- return { kind: "unknown_request" };
2176
- }
2177
- if (pending.sessionId !== vote.sessionId) {
2178
- return { kind: "unknown_request" };
2179
- }
2180
- if (vote.optionId === CANCEL_VOTE_SENTINEL) {
2181
- const outcome = {
2182
- kind: "resolved",
2183
- resolvedOptionId: CANCEL_VOTE_SENTINEL
2184
- };
2185
- this.safeAudit(
2186
- () => this.deps.audit.recordVoted(this.toRecord(pending), vote, outcome)
2187
- );
2188
- this.resolveEntry(
2189
- pending,
2190
- { kind: "cancelled", reason: "agent_cancelled" },
2191
- {
2192
- type: "voter-cancelled",
2193
- resolverClientId: vote.clientId
2194
- },
2195
- vote.clientId
2196
- );
2197
- return outcome;
2198
- }
2199
- if (!pending.allowedOptionIds.has(vote.optionId)) {
2200
- throw new InvalidPermissionOptionError(vote.requestId, vote.optionId);
2201
- }
2202
- switch (pending.policy) {
2203
- case "first-responder":
2204
- return this.voteFirstResponder(pending, vote);
2205
- case "designated":
2206
- return this.voteDesignated(pending, vote);
2207
- case "consensus":
2208
- return this.voteConsensus(pending, vote);
2209
- case "local-only":
2210
- return this.voteLocalOnly(pending, vote);
2211
- default: {
2212
- const _exhaustive = pending.policy;
2213
- void _exhaustive;
2214
- throw new Error(`Unknown permission policy "${pending.policy}"`);
2215
- }
2216
- }
2217
- }
2218
- forgetSession(sessionId) {
2219
- const requestIds = [];
2220
- for (const [id, pending] of this.pending) {
2221
- if (pending.sessionId === sessionId) requestIds.push(id);
2222
- }
2223
- for (const id of requestIds) {
2224
- const pending = this.pending.get(id);
2225
- if (!pending) continue;
2226
- this.resolveEntry(
2227
- pending,
2228
- { kind: "cancelled", reason: "session_closed" },
2229
- { type: "session-closed" },
2230
- void 0
2231
- );
2232
- }
2233
- }
2234
- /**
2235
- * Lookup the sessionId for a given requestId. Used by the legacy
2236
- * `bridge.respondToPermission(requestId, ...)` route which doesn't
2237
- * carry a sessionId in the URL. NOT part of the
2238
- * `PermissionMediator` interface contract — bridge holds the
2239
- * concrete class reference and calls this directly.
2240
- */
2241
- peekSessionFor(requestId) {
2242
- const pending = this.pending.get(requestId);
2243
- if (pending) return pending.sessionId;
2244
- const prior = this.resolved.get(requestId);
2245
- return prior?.sessionId;
2246
- }
2247
- /**
2248
- * Daemon-wide in-flight pending count for diagnostics. The bridge
2249
- * exposes this through its `pendingPermissionCount` getter so
2250
- * operators can spot stuck FIFOs without reaching into mediator
2251
- * internals. NOT part of the `PermissionMediator` interface
2252
- * contract.
2253
- */
2254
- get pendingCount() {
2255
- return this.pending.size;
2256
- }
2257
- // ===========================================================
2258
- // Per-policy vote handlers
2259
- // ===========================================================
2260
- voteFirstResponder(pending, vote) {
2261
- return this.resolveWithVote(pending, vote, {
2262
- type: "first-responder",
2263
- resolverClientId: vote.clientId
2264
- });
2265
- }
2266
- voteDesignated(pending, vote) {
2267
- if (pending.originatorClientId === void 0) {
2268
- return this.voteFirstResponder(pending, vote);
2269
- }
2270
- if (vote.clientId !== pending.originatorClientId) {
2271
- return this.rejectForbidden(
2272
- pending,
2273
- vote,
2274
- "designated_mismatch",
2275
- "designated_mismatch (voter is not the prompt originator)"
2276
- );
2277
- }
2278
- return this.resolveWithVote(pending, vote, {
2279
- type: "designated-originator",
2280
- originatorClientId: pending.originatorClientId
2281
- });
2282
- }
2283
- voteConsensus(pending, vote) {
2284
- if (vote.clientId === void 0 || !pending.votersAtIssue.has(vote.clientId)) {
2285
- return this.rejectForbidden(
2286
- pending,
2287
- vote,
2288
- "designated_mismatch",
2289
- "designated_mismatch (voter not in consensus votersAtIssue snapshot)"
2290
- );
2291
- }
2292
- for (const [originalOptionId, set] of pending.tallies.entries()) {
2293
- if (vote.clientId !== void 0 && set.has(vote.clientId)) {
2294
- const outcome2 = {
2295
- kind: "recorded",
2296
- votesNeeded: this.votesNeededFor(pending)
2297
- };
2298
- this.safeAudit(
2299
- () => this.deps.audit.recordVoted(
2300
- this.toRecord(pending),
2301
- { ...vote, optionId: originalOptionId },
2302
- outcome2
2303
- )
2304
- );
2305
- return outcome2;
2306
- }
2307
- }
2308
- let bucket = pending.tallies.get(vote.optionId);
2309
- if (!bucket) {
2310
- bucket = /* @__PURE__ */ new Set();
2311
- pending.tallies.set(vote.optionId, bucket);
2312
- }
2313
- bucket.add(vote.clientId);
2314
- const quorum = this.consensusQuorumFor(pending);
2315
- if (bucket.size >= quorum) {
2316
- return this.resolveWithVote(pending, vote, {
2317
- type: "consensus-quorum",
2318
- resolvedOptionId: vote.optionId,
2319
- quorum,
2320
- tally: bucket.size
2321
- });
2322
- }
2323
- const outcome = {
2324
- kind: "recorded",
2325
- votesNeeded: this.votesNeededFor(pending)
2326
- };
2327
- this.safeAudit(
2328
- () => this.deps.audit.recordVoted(this.toRecord(pending), vote, outcome)
2329
- );
2330
- this.safeEmit(pending.sessionId, {
2331
- type: "permission_partial_vote",
2332
- ...pending.promptId ? { promptId: pending.promptId } : {},
2333
- data: {
2334
- requestId: pending.requestId,
2335
- sessionId: pending.sessionId,
2336
- votesReceived: this.totalTalliedFor(pending),
2337
- votesNeeded: outcome.votesNeeded,
2338
- quorum,
2339
- optionTallies: this.optionTalliesFor(pending)
2340
- },
2341
- ...pending.originatorClientId !== void 0 ? { originatorClientId: pending.originatorClientId } : {}
2342
- });
2343
- return outcome;
2344
- }
2345
- /**
2346
- * Vote dispatch for `local-only` policy: only `fromLoopback: true`
2347
- * voters can resolve a permission.
2348
- *
2349
- * **Cancel-sentinel asymmetry** (cancel-sentinel note).
2350
- * `vote()` recognizes the cancel sentinel BEFORE calling this
2351
- * method (cross-policy escape hatch — see the
2352
- * `CANCEL_VOTE_SENTINEL` JSDoc for the rationale), so a remote
2353
- * voter under `local-only` CAN abort a pending permission via
2354
- * `{outcome:'cancelled'}` even though they cannot RESOLVE one. The
2355
- * settings-side description for `local-only` and the design doc call
2356
- * out this gap explicitly. Operators who want strict-cancel-too
2357
- * semantics must (a) deploy a dedicated daemon process at
2358
- * loopback bind, OR (b) wait for the follow-up PR that lifts
2359
- * cancel into per-policy gating; This version keeps the current
2360
- * cross-policy cancel for consistency with first-responder /
2361
- * designated / consensus.
2362
- */
2363
- voteLocalOnly(pending, vote) {
2364
- if (!vote.fromLoopback) {
2365
- return this.rejectForbidden(
2366
- pending,
2367
- vote,
2368
- "remote_not_allowed",
2369
- "remote_not_allowed (local-only policy; vote not from loopback)"
2370
- );
2371
- }
2372
- return this.resolveWithVote(pending, vote, {
2373
- type: "local-only-loopback",
2374
- resolverClientId: vote.clientId
2375
- });
2376
- }
2377
- // ===========================================================
2378
- // Shared vote-resolution helpers
2379
- // ===========================================================
2380
- resolveWithVote(pending, vote, decisionReason) {
2381
- const outcome = {
2382
- kind: "resolved",
2383
- resolvedOptionId: vote.optionId
2384
- };
2385
- this.safeAudit(
2386
- () => this.deps.audit.recordVoted(this.toRecord(pending), vote, outcome)
2387
- );
2388
- this.resolveEntry(
2389
- pending,
2390
- {
2391
- kind: "option",
2392
- optionId: vote.optionId,
2393
- ...vote.metadata ? { metadata: vote.metadata } : {}
2394
- },
2395
- decisionReason,
2396
- vote.clientId
2397
- );
2398
- return outcome;
2399
- }
2400
- rejectForbidden(pending, vote, reason, stderrDetail) {
2401
- this.safeAudit(
2402
- () => this.deps.audit.recordForbidden(this.toRecord(pending), vote, reason)
2403
- );
2404
- this.safeEmit(pending.sessionId, {
2405
- type: "permission_forbidden",
2406
- ...pending.promptId ? { promptId: pending.promptId } : {},
2407
- data: {
2408
- requestId: pending.requestId,
2409
- sessionId: pending.sessionId,
2410
- ...vote.clientId !== void 0 ? { clientId: vote.clientId } : {},
2411
- reason
2412
- },
2413
- ...pending.originatorClientId !== void 0 ? { originatorClientId: pending.originatorClientId } : {}
2414
- });
2415
- this.writeForbiddenStderr(pending, vote, stderrDetail);
2416
- return { kind: "forbidden", reason };
2417
- }
2418
- // ===========================================================
2419
- // Consensus tally helpers
2420
- // ===========================================================
2421
- /**
2422
- * Compute the quorum size for a `consensus` request. Default
2423
- * `floor(M/2) + 1` of `votersAtIssue.size`; overridden by
2424
- * `deps.consensusQuorum` when set, capped to `M` so an operator
2425
- * misconfig (N > M) can't deadlock.
2426
- *
2427
- * When the cap fires, write
2428
- * a one-time stderr breadcrumb per request so operators don't
2429
- * have to diff their `policy.consensusQuorum` against
2430
- * `votersAtIssue.size` manually to understand why a quorum
2431
- * resolved sooner than configured. Tracked on `MediatorPending`
2432
- * so the breadcrumb fires once even though `consensusQuorumFor`
2433
- * may be called multiple times per request (vote tally + final
2434
- * resolution).
2435
- */
2436
- consensusQuorumFor(pending) {
2437
- const m = pending.votersAtIssue.size;
2438
- const override = this.deps.consensusQuorum;
2439
- if (override !== void 0) {
2440
- const capped = Math.min(override, Math.max(m, 1));
2441
- if (capped < override && !pending.consensusQuorumCapNoted) {
2442
- pending.consensusQuorumCapNoted = true;
2443
- try {
2444
- process.stderr.write(
2445
- `permissionMediator: consensusQuorum override ${override} capped to ${capped} (votersAtIssue.size=${m}) for request ${pending.requestId} session ${pending.sessionId}
2446
- `
2447
- );
2448
- } catch {
2449
- }
2450
- }
2451
- return capped;
2452
- }
2453
- return Math.max(1, Math.floor(m / 2) + 1);
2454
- }
2455
- totalTalliedFor(pending) {
2456
- let total = 0;
2457
- for (const set of pending.tallies.values()) total += set.size;
2458
- return total;
2459
- }
2460
- /**
2461
- * `votesNeeded` = `quorum - max(tally per option)`. When no
2462
- * option has any votes (degenerate; `permission_partial_vote`
2463
- * is only emitted AFTER the first vote, so this should never
2464
- * appear on the wire), returns `quorum` itself. Always ≥ 1
2465
- * because the resolved-on-quorum path returns before this
2466
- * helper runs.
2467
- */
2468
- votesNeededFor(pending) {
2469
- const quorum = this.consensusQuorumFor(pending);
2470
- let max = 0;
2471
- for (const set of pending.tallies.values()) {
2472
- if (set.size > max) max = set.size;
2473
- }
2474
- return Math.max(quorum - max, 1);
2475
- }
2476
- optionTalliesFor(pending) {
2477
- const out = {};
2478
- for (const [optionId, set] of pending.tallies) {
2479
- out[optionId] = set.size;
2480
- }
2481
- return out;
2482
- }
2483
- // ===========================================================
2484
- // Resolution + cleanup
2485
- // ===========================================================
2486
- /**
2487
- * Settle a pending entry. Cleanup order is hardened (cleanup-order invariant):
2488
- * 1. clearTimeout (so a timer can never fire on a half-cleaned entry).
2489
- * 2. Delete from `pending` (state-first half — entry no longer
2490
- * reachable for new votes).
2491
- * 3. emit wire `permission_resolved` (best-effort — emit failures
2492
- * do not block the Promise settle). MUST come before step 4
2493
- * so a re-entrant subscriber synchronously casting another
2494
- * vote during emit sees `pending === undefined && resolved
2495
- * === undefined` (silent false), matching the previous ordering.
2496
- *
2497
- * 4. write to `resolved` (the second half of state move — late
2498
- * voters arriving after this see `permission_already_resolved`).
2499
- * 5. audit.recordResolved (best-effort, same).
2500
- * 6. Settle the Promise (LAST — callbacks running re-entrantly
2501
- * see consistent state).
2502
- *
2503
- * Previously the spec bundled
2504
- * "delete pending + write resolved" into step 2 ahead of emit,
2505
- * which contradicted the code. The fix
2506
- * splits the two halves of the state move around the emit so
2507
- * the spec faithfully describes the ordering invariant.
2508
- *
2509
- * @param resolverClientId wire compat: the
2510
- * `permission_resolved` SSE frame stamps this as
2511
- * `originatorClientId`. The previous `resolvePending` in
2512
- * `httpAcpBridge.ts:1518-1523` filled it from the voter's
2513
- * trusted clientId. We preserve byte-for-byte; vote-driven
2514
- * paths pass `vote.clientId` (which may be undefined for
2515
- * loopback no-header voters); timer + session-closed paths
2516
- * pass undefined (no voter).
2517
- */
2518
- resolveEntry(pending, resolution, decisionReason, resolverClientId) {
2519
- if (this.pending.get(pending.requestId) !== pending) {
2520
- return;
2521
- }
2522
- if (pending.timer !== void 0) {
2523
- clearTimeout(pending.timer);
2524
- pending.timer = void 0;
2525
- }
2526
- this.pending.delete(pending.requestId);
2527
- this.safeEmit(pending.sessionId, {
2528
- type: "permission_resolved",
2529
- ...pending.promptId ? { promptId: pending.promptId } : {},
2530
- data: {
2531
- requestId: pending.requestId,
2532
- outcome: this.toAcpOutcome(resolution),
2533
- // Note: `voterClientId` is the canonical,
2534
- // unambiguous name for "who cast the resolving vote". The envelope
2535
- // `originatorClientId` below carries the SAME value for wire
2536
- // compat (it is semantically the voter on `permission_resolved`,
2537
- // unlike on `permission_request` where it is the prompt originator).
2538
- // Both are optional and omitted together for no-voter resolutions
2539
- // (timer expiry / session-closed / loopback voter with no clientId).
2540
- ...resolverClientId !== void 0 ? { voterClientId: resolverClientId } : {}
2541
- },
2542
- // Preserve pre-extraction behavior: voter's clientId is stamped
2543
- // here (not the prompt originator's). Documented inconsistency
2544
- // with `permission_request.originatorClientId` (which IS the
2545
- // prompt originator); we do not fix the inconsistency to
2546
- // avoid breaking the wire shape. A4 keeps it as a deprecated
2547
- // alias of `data.voterClientId`.
2548
- ...resolverClientId !== void 0 ? { originatorClientId: resolverClientId } : {}
2549
- });
2550
- this.rememberResolved({
2551
- requestId: pending.requestId,
2552
- sessionId: pending.sessionId,
2553
- promptId: pending.promptId,
2554
- resolution,
2555
- resolverClientId
2556
- });
2557
- if (decisionReason !== void 0) {
2558
- this.safeAudit(
2559
- () => this.deps.audit.recordResolved(
2560
- this.toRecord(pending),
2561
- resolution,
2562
- decisionReason
2563
- )
2564
- );
2565
- }
2566
- pending.resolve(resolution);
2567
- }
2568
- rememberResolved(record) {
2569
- if (!this.resolved.has(record.requestId)) {
2570
- this.resolvedOrder.push(record.requestId);
2571
- }
2572
- this.resolved.set(record.requestId, record);
2573
- while (this.resolvedOrder.length > MAX_RESOLVED_PERMISSION_RECORDS) {
2574
- const oldest2 = this.resolvedOrder.shift();
2575
- if (oldest2 !== void 0) this.resolved.delete(oldest2);
2576
- }
2577
- }
2578
- safeEmit(sessionId, event) {
2579
- try {
2580
- this.deps.emit(sessionId, event);
2581
- } catch (err) {
2582
- try {
2583
- process.stderr.write(
2584
- `permissionMediator: emit failed for session=${JSON.stringify(sessionId)} type=${JSON.stringify(event.type)}: ${stringifyError(err)}
2585
- `
2586
- );
2587
- } catch {
2588
- }
2589
- }
2590
- }
2591
- /**
2592
- * Emit a stderr breadcrumb
2593
- * for every vote rejection (the three forbidden paths in
2594
- * voteDesignated / voteConsensus / voteLocalOnly). Mirrors the
2595
- * timeout breadcrumb pattern: audit ring + SSE event are
2596
- * transient observability surfaces (no v1 query route, SSE drops
2597
- * on disconnect), so an operator tailing daemon stderr would see
2598
- * zero indication of permission rejections without this.
2599
- *
2600
- * Wrapped in `try/catch` because `process.stderr.write` can
2601
- * synchronously throw on EPIPE during shutdown — a stderr
2602
- * unavailability must not propagate up through `safeEmit` /
2603
- * `safeAudit` and break the resolveEntry cleanup ladder. Mirrors
2604
- * the safeEmit/safeAudit defensive posture (see the
2605
- * matching hang scenario in safeEmit).
2606
- */
2607
- writeForbiddenStderr(pending, vote, reasonDetail) {
2608
- try {
2609
- const voterDescriptor = vote.clientId === void 0 ? "<anonymous>" : JSON.stringify(vote.clientId);
2610
- process.stderr.write(
2611
- `qwen serve: permission ${pending.requestId} (session ${pending.sessionId}): vote rejected (${reasonDetail}) by client ${voterDescriptor}
2612
- `
2613
- );
2614
- } catch {
2615
- }
2616
- }
2617
- /**
2618
- * Run an audit-publisher call defensively. The audit ring is
2619
- * best-effort observability — a publisher exception (ring full,
2620
- * host bug, transient I/O) MUST NOT throw out of `request()`,
2621
- * `vote()`, or the timer callback. Without this guard, the
2622
- * Promise the agent is awaiting would be left unsettled and the
2623
- * pending entry would leak.
2624
- *
2625
- * Single helper used at all five audit call sites so the
2626
- * "audit is best-effort" invariant is uniformly enforced (the
2627
- * pre-fix asymmetric `try/catch` at 2 of 5 sites was a real
2628
- * silent-failure hole.
2629
- *
2630
- * doc placement — JSDoc was previously
2631
- * stacked above `writeForbiddenStderr` so IDE hover and API
2632
- * doc generation showed the wrong attribution. Moved adjacent
2633
- * to its actual definition.
2634
- */
2635
- safeAudit(fn) {
2636
- try {
2637
- fn();
2638
- } catch (err) {
2639
- try {
2640
- process.stderr.write(
2641
- `permissionMediator: audit publisher threw: ${stringifyError(err)}
2642
- `
2643
- );
2644
- } catch {
2645
- }
2646
- }
2647
- }
2648
- toRecord(pending) {
2649
- return {
2650
- requestId: pending.requestId,
2651
- sessionId: pending.sessionId,
2652
- ...pending.promptId ? { promptId: pending.promptId } : {},
2653
- originatorClientId: pending.originatorClientId,
2654
- allowedOptionIds: pending.allowedOptionIds,
2655
- issuedAtMs: pending.issuedAtMs
2656
- };
2657
- }
2658
- toAcpOutcome(resolution) {
2659
- if (resolution.kind === "option") {
2660
- return { outcome: "selected", optionId: resolution.optionId };
2661
- }
2662
- return { outcome: "cancelled" };
2663
- }
2664
- };
2665
-
2666
- // packages/acp-bridge/src/bridgeClient.ts
2667
- function parseActiveWorkSnapshot(params) {
2668
- const seq = params["seq"];
2669
- const sessions = params["sessions"];
2670
- if (params["v"] !== ACTIVE_WORK_HEARTBEAT_VERSION || typeof seq !== "number" || !Number.isSafeInteger(seq) || seq <= 0 || !Array.isArray(sessions) || sessions.length > ACTIVE_WORK_MAX_SNAPSHOT_SESSIONS) {
2671
- return void 0;
2672
- }
2673
- const parsed = [];
2674
- for (const raw of sessions) {
2675
- if (typeof raw !== "object" || raw === null) return void 0;
2676
- const entry = raw;
2677
- const sessionId = entry["sessionId"];
2678
- const holds = entry["holds"];
2679
- if (typeof sessionId !== "string" || !Array.isArray(holds) || holds.length > ACTIVE_WORK_MAX_SESSION_HOLDS) {
2680
- return void 0;
2681
- }
2682
- const parsedHolds = [];
2683
- for (const rawHold of holds) {
2684
- if (typeof rawHold !== "object" || rawHold === null) return void 0;
2685
- const hold = rawHold;
2686
- const category = hold["category"];
2687
- const id = hold["id"];
2688
- if (typeof id !== "string" || typeof category !== "string" || !ACTIVE_WORK_HOLD_CATEGORIES.includes(
2689
- category
2690
- )) {
2691
- return void 0;
2692
- }
2693
- parsedHolds.push({
2694
- category,
2695
- id
2696
- });
2697
- }
2698
- parsed.push({ sessionId, holds: parsedHolds });
2699
- }
2700
- return { v: ACTIVE_WORK_HEARTBEAT_VERSION, seq, sessions: parsed };
2701
- }
2702
- __name(parseActiveWorkSnapshot, "parseActiveWorkSnapshot");
2703
- var PUBLISH_ARTIFACT_TOOL_NAME = "artifact";
2704
- var MAX_CHANNEL_DELIVERY_TEXT_CHARS = 1e5;
2705
- var MAX_CHANNEL_DELIVERY_FIELD_CHARS = 2048;
2706
- var MAX_CHANNEL_DELIVERY_ERROR_CHARS = 500;
2707
- function isFsErrorShape(err) {
2708
- return err instanceof Error && err.name === "FsError" && typeof err.kind === "string";
2709
- }
2710
- __name(isFsErrorShape, "isFsErrorShape");
2711
- function isRecord2(value) {
2712
- return typeof value === "object" && value !== null && !Array.isArray(value);
2713
- }
2714
- __name(isRecord2, "isRecord");
2715
- function normalizeExternalToolGuardResult(value) {
2716
- if (!isRecord2(value)) {
2717
- throw new Error("External tool guard handler returned an invalid result.");
2718
- }
2719
- const keys = Object.keys(value);
2720
- if (value["allowed"] === true && keys.length === 1 && keys[0] === "allowed") {
2721
- return { allowed: true };
2722
- }
2723
- if (value["allowed"] !== false || keys.some((key) => key !== "allowed" && key !== "reason")) {
2724
- throw new Error("External tool guard handler returned an invalid result.");
2725
- }
2726
- if (!Object.hasOwn(value, "reason")) return { allowed: false };
2727
- const reason = value["reason"];
2728
- if (!isValidExternalToolGuardDenialReason(reason)) {
2729
- throw new Error("External tool guard handler returned an invalid result.");
2730
- }
2731
- return { allowed: false, reason };
2732
- }
2733
- __name(normalizeExternalToolGuardResult, "normalizeExternalToolGuardResult");
2734
- function isBoundedChannelDeliveryString(value) {
2735
- return typeof value === "string" && value.trim().length > 0 && value.length <= MAX_CHANNEL_DELIVERY_FIELD_CHARS;
2736
- }
2737
- __name(isBoundedChannelDeliveryString, "isBoundedChannelDeliveryString");
2738
- function isChannelDeliveryTarget(value) {
2739
- if (!isRecord2(value)) return false;
2740
- return isBoundedChannelDeliveryString(value["channelName"]) && (value["type"] === "user" || value["type"] === "chat") && isBoundedChannelDeliveryString(value["id"]) && Object.keys(value).every(
2741
- (key) => key === "channelName" || key === "type" || key === "id"
2742
- );
2743
- }
2744
- __name(isChannelDeliveryTarget, "isChannelDeliveryTarget");
2745
- function normalizeChannelDeliveryHostResult(value) {
2746
- if (value.status === "delivered") return { status: "delivered" };
2747
- if (value.status === "skipped") return { status: "skipped" };
2748
- const code = CHANNEL_DELIVERY_ERROR_CODES.has(value.code) ? value.code : "channel_delivery_failed";
2749
- const error = typeof value.error === "string" && value.error.length > 0 ? value.error.slice(0, MAX_CHANNEL_DELIVERY_ERROR_CHARS) : "Channel delivery failed.";
2750
- return { status: "failed", code, error };
2751
- }
2752
- __name(normalizeChannelDeliveryHostResult, "normalizeChannelDeliveryHostResult");
2753
- function pendingInteractionOptions(options) {
2754
- return options.map((option) => ({
2755
- optionId: String(option.optionId ?? ""),
2756
- ...typeof option.name === "string" ? { label: option.name } : {},
2757
- ...typeof option.kind === "string" ? { kind: option.kind } : {}
2758
- }));
2759
- }
2760
- __name(pendingInteractionOptions, "pendingInteractionOptions");
2761
- function pendingInteractionFromRequest(requestId, params) {
2762
- const toolCall = params.toolCall;
2763
- const meta = isRecord2(toolCall["_meta"]) ? toolCall["_meta"] : void 0;
2764
- const rawInput = toolCall["rawInput"];
2765
- const options = pendingInteractionOptions(
2766
- Array.isArray(params.options) ? params.options : []
2767
- );
2768
- const isUserQuestion = meta?.["qwenInteractionKind"] === "user_question";
2769
- if (isUserQuestion) {
2770
- const rawQuestions = Array.isArray(meta?.["qwenQuestions"]) ? meta["qwenQuestions"] : isRecord2(rawInput) && Array.isArray(rawInput["questions"]) ? rawInput["questions"] : [];
2771
- return {
2772
- requestId,
2773
- kind: "user_question",
2774
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2775
- ...typeof toolCall["title"] === "string" ? { title: toolCall["title"] } : {},
2776
- questions: rawQuestions.flatMap(
2777
- (question, index) => isRecord2(question) ? [{ ...question, answerKey: String(index) }] : []
2778
- ),
2779
- options
2780
- };
2781
- }
2782
- return {
2783
- requestId,
2784
- kind: "permission",
2785
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2786
- action: {
2787
- ...typeof toolCall["kind"] === "string" ? { type: toolCall["kind"] } : {},
2788
- ...typeof toolCall["title"] === "string" ? { title: toolCall["title"] } : {},
2789
- ...toolCall["content"] !== void 0 ? { content: toolCall["content"] } : {},
2790
- ...toolCall["locations"] !== void 0 ? { locations: toolCall["locations"] } : {},
2791
- ...rawInput !== void 0 ? { input: rawInput } : {}
2792
- },
2793
- options
2794
- };
2795
- }
2796
- __name(pendingInteractionFromRequest, "pendingInteractionFromRequest");
2797
- function fallbackPendingPermissionInteraction(requestId, options) {
2798
- return {
2799
- requestId,
2800
- kind: "permission",
2801
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
2802
- action: {},
2803
- options: pendingInteractionOptions(options)
2804
- };
2805
- }
2806
- __name(fallbackPendingPermissionInteraction, "fallbackPendingPermissionInteraction");
2807
- function artifactPayloadFields(artifact) {
2808
- return {
2809
- title: artifact["title"],
2810
- kind: artifact["kind"],
2811
- storage: artifact["storage"],
2812
- description: artifact["description"],
2813
- workspacePath: artifact["workspacePath"],
2814
- managedId: artifact["managedId"],
2815
- url: artifact["url"],
2816
- mimeType: artifact["mimeType"],
2817
- sizeBytes: artifact["sizeBytes"],
2818
- metadata: artifact["metadata"],
2819
- retention: artifact["retention"]
2820
- };
2821
- }
2822
- __name(artifactPayloadFields, "artifactPayloadFields");
2823
- function extractCappedArtifactInputs(rawArtifacts, limit, sessionId, source, toInput) {
2824
- const artifacts = [];
2825
- for (let index = 0; index < rawArtifacts.length; index++) {
2826
- const artifact = rawArtifacts[index];
2827
- if (!isRecord2(artifact)) {
2828
- writeStderrLine(
2829
- `[artifacts] session=${sessionId} action=dropped reason=malformed source=${source} index=${index}`
2830
- );
2831
- continue;
2832
- }
2833
- if (artifacts.length >= limit) {
2834
- writeStderrLine(
2835
- `[artifacts] session=${sessionId} action=dropped reason="artifact batch limit exceeded" source=${source} dropped=${rawArtifacts.length - index}`
2836
- );
2837
- break;
2838
- }
2839
- artifacts.push(toInput(artifact));
2840
- }
2841
- return artifacts;
2842
- }
2843
- __name(extractCappedArtifactInputs, "extractCappedArtifactInputs");
2844
- function artifactIngestionErrorReason(error) {
2845
- if (!(error instanceof Error)) {
2846
- return String(error);
2847
- }
2848
- return {
2849
- name: error.name,
2850
- message: error.message,
2851
- stack: error.stack?.split("\n").slice(0, 4).join("\n")
2852
- };
2853
- }
2854
- __name(artifactIngestionErrorReason, "artifactIngestionErrorReason");
2855
- function extractSessionUpdateArtifacts(params, updateMeta, limit, sessionId) {
2856
- const rawArtifacts = updateMeta?.["artifacts"];
2857
- if (!Array.isArray(rawArtifacts)) {
2858
- return [];
2859
- }
2860
- const update = params.update;
2861
- if (update.sessionUpdate !== "tool_call_update" || update.status !== "completed" && update.status !== "failed" && update.status !== "cancelled") {
2862
- return [];
2863
- }
2864
- const toolCallId = typeof update.toolCallId === "string" ? update.toolCallId : void 0;
2865
- const toolName = typeof updateMeta?.["toolName"] === "string" ? updateMeta["toolName"] : void 0;
2866
- return extractCappedArtifactInputs(
2867
- rawArtifacts,
2868
- limit,
2869
- sessionId,
2870
- "tool",
2871
- (artifact) => ({
2872
- ...artifactPayloadFields(artifact),
2873
- source: "tool",
2874
- toolCallId,
2875
- toolName
2876
- })
2877
- );
2878
- }
2879
- __name(extractSessionUpdateArtifacts, "extractSessionUpdateArtifacts");
2880
- function sanitizeSessionUpdateArtifacts(params, updateMeta) {
2881
- if (!Array.isArray(updateMeta?.["artifacts"])) {
2882
- return params;
2883
- }
2884
- const sanitizedMeta = { ...updateMeta };
2885
- delete sanitizedMeta["artifacts"];
2886
- const update = {
2887
- ...params.update,
2888
- _meta: sanitizedMeta
2889
- };
2890
- return {
2891
- ...params,
2892
- update
2893
- };
2894
- }
2895
- __name(sanitizeSessionUpdateArtifacts, "sanitizeSessionUpdateArtifacts");
2896
- function isTrustedArtifactToolUpdate(params, updateMeta) {
2897
- const update = params.update;
2898
- return update.sessionUpdate === "tool_call_update" && update.status === "completed" && updateMeta?.["toolName"] === PUBLISH_ARTIFACT_TOOL_NAME;
2899
- }
2900
- __name(isTrustedArtifactToolUpdate, "isTrustedArtifactToolUpdate");
2901
- function preserveFsErrorOverAcp(err) {
2902
- if (isFsErrorShape(err)) {
2903
- const code = err.kind === "parse_error" ? -32602 : -32603;
2904
- throw new RequestError(code, err.message, {
2905
- errorKind: err.kind,
2906
- ...err.hint !== void 0 ? { hint: err.hint } : {},
2907
- ...err.status !== void 0 ? { status: err.status } : {}
2908
- });
2909
- }
2910
- throw err;
2911
- }
2912
- __name(preserveFsErrorOverAcp, "preserveFsErrorOverAcp");
2913
- function resolutionToAcpResponse(resolution) {
2914
- if (resolution.kind === "option") {
2915
- return {
2916
- outcome: { outcome: "selected", optionId: resolution.optionId },
2917
- ...resolution.metadata ?? {}
2918
- };
2919
- }
2920
- return {
2921
- outcome: { outcome: "cancelled" },
2922
- _meta: {
2923
- [DAEMON_PERMISSION_CANCEL_REASON_META_KEY]: resolution.reason
2924
- }
2925
- };
2926
- }
2927
- __name(resolutionToAcpResponse, "resolutionToAcpResponse");
2928
- var MAX_EARLY_EVENT_SESSIONS = 64;
2929
- var MAX_EARLY_EVENTS_PER_SESSION = 32;
2930
- var MAX_SUGGESTION_LENGTH = 500;
2931
- var EARLY_EVENT_TTL_MS = 6e4;
2932
- var KNOWN_APPROVAL_MODES = new Set(
2933
- APPROVAL_MODES
2934
- );
2935
- function describeStatKind(stats) {
2936
- if (stats.isDirectory()) return "directory";
2937
- if (stats.isSymbolicLink()) return "symlink";
2938
- if (stats.isCharacterDevice()) return "character device";
2939
- if (stats.isBlockDevice()) return "block device";
2940
- if (stats.isFIFO()) return "named pipe (FIFO)";
2941
- if (stats.isSocket()) return "socket";
2942
- return "non-regular file";
2943
- }
2944
- __name(describeStatKind, "describeStatKind");
2945
- function sliceLineRange(content, startLine, endLine) {
2946
- let offset = 0;
2947
- for (let i = 0; i < startLine; i++) {
2948
- const nl = content.indexOf("\n", offset);
2949
- if (nl === -1) return "";
2950
- offset = nl + 1;
2951
- }
2952
- if (endLine === void 0) return content.slice(offset);
2953
- let end = offset;
2954
- const want = endLine - startLine;
2955
- for (let i = 0; i < want; i++) {
2956
- const nl = content.indexOf("\n", end);
2957
- if (nl === -1) return content.slice(offset);
2958
- end = nl + 1;
2959
- }
2960
- return content.slice(offset, end > offset ? end - 1 : end);
2961
- }
2962
- __name(sliceLineRange, "sliceLineRange");
2963
- var BridgeClient = class {
2964
- constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender, ownsSession = () => true, onTokenUsage, onCreateSubSession, onGenerationEvent, onWorkspaceGenerationEvent, onChannelDelivery, hasSessionSpawnInFlight = () => false, getLiveScreenContextCaptureHandler = () => void 0, getLiveTaskToolRequestHandler = () => void 0, getLiveSpeakToUserHandler = () => void 0, externalToolGuard, onActiveWork, onSessionCatalogChanged, onGoalTurnEnded) {
2965
- this.resolveEntry = resolveEntry;
2966
- this.resolvePendingRestoreEvents = resolvePendingRestoreEvents;
2967
- this.mediator = mediator;
2968
- this.permissionTimeoutMs = permissionTimeoutMs;
2969
- this.maxPendingPerSession = maxPendingPerSession;
2970
- this.fileSystem = fileSystem;
2971
- this.onModelPromoted = onModelPromoted;
2972
- this.onModePromoted = onModePromoted;
2973
- this.clientMcpSender = clientMcpSender;
2974
- this.ownsSession = ownsSession;
2975
- this.onTokenUsage = onTokenUsage;
2976
- this.onCreateSubSession = onCreateSubSession;
2977
- this.onGenerationEvent = onGenerationEvent;
2978
- this.onWorkspaceGenerationEvent = onWorkspaceGenerationEvent;
2979
- this.onChannelDelivery = onChannelDelivery;
2980
- this.hasSessionSpawnInFlight = hasSessionSpawnInFlight;
2981
- this.getLiveScreenContextCaptureHandler = getLiveScreenContextCaptureHandler;
2982
- this.getLiveTaskToolRequestHandler = getLiveTaskToolRequestHandler;
2983
- this.getLiveSpeakToUserHandler = getLiveSpeakToUserHandler;
2984
- this.externalToolGuard = externalToolGuard;
2985
- this.onActiveWork = onActiveWork;
2986
- this.onSessionCatalogChanged = onSessionCatalogChanged;
2987
- this.onGoalTurnEnded = onGoalTurnEnded;
2988
- }
2989
- static {
2990
- __name(this, "BridgeClient");
2991
- }
2992
- async requestPermission(params) {
2993
- const entry = this.resolveEntry(params.sessionId);
2994
- if (!entry) return { outcome: { outcome: "cancelled" } };
2995
- if (entry.pendingPermissionIds.size >= this.maxPendingPerSession) {
2996
- writeStderrLine(
2997
- `qwen serve: session ${entry.sessionId} exceeded maxPendingPermissionsPerSession (${this.maxPendingPerSession}) \u2014 resolving new permission as cancelled.`
2998
- );
2999
- return { outcome: { outcome: "cancelled" } };
3000
- }
3001
- const options = Array.isArray(params.options) ? params.options : [];
3002
- const allowedOptionIds = new Set(
3003
- options.map((o) => String(o.optionId ?? ""))
3004
- );
3005
- allowedOptionIds.delete("");
3006
- const requestId = randomUUID2();
3007
- if (allowedOptionIds.has(CANCEL_VOTE_SENTINEL)) {
3008
- throw new CancelSentinelCollisionError(requestId, CANCEL_VOTE_SENTINEL);
3009
- }
3010
- const published = entry.events.publish({
3011
- type: "permission_request",
3012
- ...entry.activePromptId ? { promptId: entry.activePromptId } : {},
3013
- data: {
3014
- requestId,
3015
- sessionId: entry.sessionId,
3016
- toolCall: params.toolCall,
3017
- options
3018
- },
3019
- ...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
3020
- });
3021
- if (!published) return { outcome: { outcome: "cancelled" } };
3022
- let interaction;
3023
- try {
3024
- interaction = pendingInteractionFromRequest(requestId, {
3025
- ...params,
3026
- options
3027
- });
3028
- } catch (error) {
3029
- writeStderrLine(
3030
- `qwen serve: failed to snapshot pending interaction ${requestId}: ${error instanceof Error ? error.message : String(error)}`
3031
- );
3032
- interaction = fallbackPendingPermissionInteraction(requestId, options);
3033
- }
3034
- entry.pendingPermissionIds.add(requestId);
3035
- if (interaction) entry.pendingInteractions.set(requestId, interaction);
3036
- try {
3037
- const record = {
3038
- requestId,
3039
- sessionId: entry.sessionId,
3040
- promptId: entry.activePromptId,
3041
- originatorClientId: entry.activePromptOriginatorClientId,
3042
- allowedOptionIds,
3043
- issuedAtMs: Date.now()
3044
- };
3045
- const resolution = await this.mediator.request(
3046
- record,
3047
- this.permissionTimeoutMs
3048
- );
3049
- return resolutionToAcpResponse(resolution);
3050
- } finally {
3051
- entry.pendingPermissionIds.delete(requestId);
3052
- entry.pendingInteractions.delete(requestId);
3053
- }
3054
- }
3055
- async sessionUpdate(params) {
3056
- if (this.abandonedRestoreIds.has(params.sessionId)) {
3057
- return;
3058
- }
3059
- if (!this.ownsSession(params.sessionId) && !this.inFlightRestoreIds.has(params.sessionId)) {
3060
- writeStderrLine(
3061
- `[demux] session=${params.sessionId} type=session_update action=dropped reason=session_not_owned`
3062
- );
3063
- return;
3064
- }
3065
- const entry = this.resolveEntry(params.sessionId);
3066
- const events = entry?.events ?? this.resolvePendingRestoreEvents(params.sessionId);
3067
- if (!events) return;
3068
- const prepared = this.prepareSessionUpdateFrames(params, entry);
3069
- for (const frame of prepared.frames) {
3070
- events.publish(frame);
3071
- }
3072
- try {
3073
- this.recordLiveTokenUsage(params, entry);
3074
- } catch {
3075
- }
3076
- if (entry && prepared.artifacts.length > 0) {
3077
- await this.upsertAndPublishArtifacts(
3078
- entry,
3079
- prepared.artifacts,
3080
- {
3081
- trustedPublisher: prepared.trustedPublisher
3082
- },
3083
- prepared.turn
3084
- );
3085
- }
3086
- }
3087
- prepareSessionUpdateFrames(params, entry) {
3088
- const turn = {
3089
- ...entry?.activePromptId ? { promptId: entry.activePromptId } : {},
3090
- ...entry?.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
3091
- };
3092
- const frames = [];
3093
- const a2ui = extractA2uiToolUpdate(params);
3094
- if (a2ui) {
3095
- for (const surface of a2ui.surfaces) {
3096
- frames.push({
3097
- type: "session_update",
3098
- data: {
3099
- sessionId: params.sessionId,
3100
- update: {
3101
- sessionUpdate: "a2ui",
3102
- a2ui: {
3103
- surfaceId: surface.surfaceId,
3104
- callId: a2ui.callId,
3105
- commands: surface.commands
3106
- },
3107
- _meta: { serverTimestamp: Date.now(), source: "a2ui-bridge" }
3108
- }
3109
- },
3110
- ...turn
3111
- });
3112
- }
3113
- params = a2ui.sanitizedParams;
3114
- }
3115
- const updateMeta = params.update._meta;
3116
- const originalTs = updateMeta?.["serverTimestamp"] ?? updateMeta?.["timestamp"];
3117
- const serverTimestamp = typeof originalTs === "number" && Number.isFinite(originalTs) ? originalTs : void 0;
3118
- const artifacts = entry?.artifacts ? extractSessionUpdateArtifacts(
3119
- params,
3120
- updateMeta,
3121
- entry.artifacts.inputBatchLimit(),
3122
- entry.sessionId
3123
- ) : [];
3124
- const publishParams = sanitizeSessionUpdateArtifacts(params, updateMeta);
3125
- frames.push({
3126
- type: "session_update",
3127
- data: publishParams,
3128
- ...turn,
3129
- ...serverTimestamp !== void 0 ? { _meta: { serverTimestamp } } : {}
3130
- });
3131
- return {
3132
- frames,
3133
- artifacts,
3134
- trustedPublisher: isTrustedArtifactToolUpdate(params, updateMeta),
3135
- turn
3136
- };
3137
- }
3138
- async seedSessionUpdates(entry, updates, options = {}) {
3139
- const frames = [];
3140
- const artifactBatches = [];
3141
- for (const update of updates) {
3142
- const prepared = this.prepareSessionUpdateFrames(
3143
- { sessionId: entry.sessionId, update },
3144
- entry
3145
- );
3146
- frames.push(...prepared.frames);
3147
- if (options.ingestArtifacts !== false && prepared.artifacts.length > 0) {
3148
- artifactBatches.push({
3149
- artifacts: prepared.artifacts,
3150
- trustedPublisher: prepared.trustedPublisher,
3151
- turn: prepared.turn
3152
- });
3153
- }
3154
- }
3155
- entry.events.seedReplayEvents(frames);
3156
- for (const batch of artifactBatches) {
3157
- await this.upsertAndPublishArtifacts(
3158
- entry,
3159
- batch.artifacts,
3160
- {
3161
- trustedPublisher: batch.trustedPublisher
3162
- },
3163
- batch.turn
3164
- );
3165
- }
3166
- }
3167
- /**
3168
- * Daemon token-burn accounting for LIVE model turns. Called only from
3169
- * `sessionUpdate` (the live session/update fan-in), never from
3170
- * `seedSessionUpdates` — so batch load-replay never lands historical usage in
3171
- * the current metrics window. Additionally guarded on a live `entry`: a stray
3172
- * pending-restore frame (entry not yet registered) is skipped too, so replayed
3173
- * history can't post a phantom burn spike with no model call.
3174
- *
3175
- * Usage rides an otherwise-empty `agent_message_chunk` as `update._meta.usage`
3176
- * with per-round camelCase increments; subagent frames carry their own usage
3177
- * (tagged `parentToolCallId`) and are independent turns, so counting each
3178
- * frame once is the correct total. `_meta`/`usage` are optional and untyped.
3179
- */
3180
- recordLiveTokenUsage(params, entry) {
3181
- if (!this.onTokenUsage || !entry) return;
3182
- const updateMeta = params.update._meta;
3183
- const usage = updateMeta?.["usage"];
3184
- if (usage === null || typeof usage !== "object") return;
3185
- const inputTokens = usage.inputTokens;
3186
- const outputTokens = usage.outputTokens;
3187
- if (typeof inputTokens !== "number" && typeof outputTokens !== "number") {
3188
- return;
3189
- }
3190
- const durationMs = updateMeta?.["durationMs"];
3191
- const apiErrors = updateMeta?.["apiErrors"];
3192
- const apiRetries = updateMeta?.["apiRetries"];
3193
- this.onTokenUsage(
3194
- typeof inputTokens === "number" ? inputTokens : 0,
3195
- typeof outputTokens === "number" ? outputTokens : 0,
3196
- typeof durationMs === "number" ? durationMs : void 0,
3197
- typeof apiErrors === "number" ? apiErrors : 0,
3198
- typeof apiRetries === "number" ? apiRetries : 0
3199
- );
3200
- }
3201
- /**
3202
- * Bounded early-event buffer. The map is scoped to this BridgeClient and
3203
- * therefore to one channel; a stale channel cannot seed a fresh channel's
3204
- * future session. Frames are keyed by sessionId; each entry tracks its
3205
- * `expiresAt` for lazy TTL-based eviction in `bufferEarlyEvent`. Drained by
3206
- * `drainEarlyEvents` whenever the bridge registers a session with a matching
3207
- * id. See MAX_EARLY_EVENT_* constants for capacity bounds.
3208
- */
3209
- earlyEvents = /* @__PURE__ */ new Map();
3210
- /**
3211
- * Tombstone for closed/killed session ids. Prevents late
3212
- * `extNotification` from a dying child from leaking into the
3213
- * early-event buffer and being replayed onto a future session
3214
- * that reuses the same id via `session/load` or `session/resume`.
3215
- *
3216
- * Tombstone semantics:
3217
- * - Marked when the bridge removes a sessionId from `byId` (kill
3218
- * path, channel.exited handler, closeSession).
3219
- * - Concurrently purges any in-flight `earlyEvents[id]`.
3220
- * - `bufferEarlyEvent` rejects tombstoned ids.
3221
- * - `drainEarlyEvents` clears the tombstone — a fresh
3222
- * `createSessionEntry` for the same id is a legitimate
3223
- * "load/resume of a persisted session id" case.
3224
- * - TTL = `EARLY_EVENT_TTL_MS` (60s) — same as the early-event
3225
- * buffer, so by the time a tombstone expires there can be no
3226
- * stale frame for that id anywhere in the system.
3227
- */
3228
- tombstonedSessionIds = /* @__PURE__ */ new Map();
3229
- /** Restore ownership for `sessionUpdate` and artifact demultiplexing. */
3230
- inFlightRestoreIds = /* @__PURE__ */ new Set();
3231
- /**
3232
- * Registrations allowed to buffer early extended notifications through an
3233
- * ordinary close tombstone. This includes restores and caller-supplied-id
3234
- * spawns, and lasts only until their ACP registration attempt settles.
3235
- */
3236
- inFlightSessionRegistrationIds = /* @__PURE__ */ new Set();
3237
- /**
3238
- * Restore ids whose caller timed out while the non-cancellable ACP request
3239
- * continues.
3240
- */
3241
- abandonedRestoreIds = /* @__PURE__ */ new Set();
3242
- /**
3243
- * Handle child->bridge ACP `extMethod` requests (calls that expect a
3244
- * response, unlike `extNotification`). Served methods:
3245
- * `qwen/control/client_mcp/message` (reverse tool channel),
3246
- * `qwen/control/create-sub-session` (the `create_sub_session` tool → daemon
3247
- * spawns a sub-session and, for `'first-turn'`, returns its first-turn
3248
- * result), and `craft/drainMidTurnQueue`: the ACP child calls the last one
3249
- * between tool batches to pull any messages the browser queued mid-turn. We splice the per-session
3250
- * queue, return them to the child as the response, and — when non-empty —
3251
- * publish a `mid_turn_message_injected` SSE frame so the browser can move
3252
- * those messages out of its pending queue and render the immediate echo.
3253
- * Unknown methods reject with ACP `methodNotFound` (-32601), matching
3254
- * the SDK's
3255
- * default for an unimplemented client surface; the child's drain caller
3256
- * treats that as "drain unsupported" and stops asking.
3257
- */
3258
- async extMethod(method, params) {
3259
- if (method === SERVE_CONTROL_EXT_METHODS.clientMcpMessage) {
3260
- return this.handleClientMcpMessage(params);
3261
- }
3262
- if (method === SERVE_CONTROL_EXT_METHODS.createSubSession) {
3263
- return this.handleCreateSubSession(params);
3264
- }
3265
- if (method === SERVE_CONTROL_EXT_METHODS.liveCaptureScreenContext) {
3266
- return this.handleLiveScreenContextCapture(params);
3267
- }
3268
- if (method === SERVE_CONTROL_EXT_METHODS.liveTaskTool) {
3269
- return this.handleLiveTaskTool(params);
3270
- }
3271
- if (method === SERVE_CONTROL_EXT_METHODS.liveSpeakToUser) {
3272
- return this.handleLiveSpeakToUser(params);
3273
- }
3274
- if (method === SERVE_CONTROL_EXT_METHODS.channelDelivery) {
3275
- return this.handleChannelDelivery(params);
3276
- }
3277
- if (method === SERVE_CONTROL_EXT_METHODS.externalToolGuardPrepare) {
3278
- return this.handleExternalToolGuardPrepare(params);
3279
- }
3280
- if (method === TODO_STOP_GUARD_CONTINUATION_CLAIM_METHOD) {
3281
- return this.handleTodoStopGuardContinuationClaim(params);
3282
- }
3283
- if (method !== MID_TURN_QUEUE_DRAIN_METHOD) {
3284
- throw RequestError.methodNotFound(method);
3285
- }
3286
- const sessionId = typeof params["sessionId"] === "string" ? params["sessionId"] : void 0;
3287
- if (!sessionId) return { messages: [], items: [], hasQueuedPrompt: false };
3288
- if (!this.ownsSession(sessionId)) {
3289
- return { messages: [], items: [], hasQueuedPrompt: false };
3290
- }
3291
- const entry = this.resolveEntry(sessionId);
3292
- if (!entry) return { messages: [], items: [], hasQueuedPrompt: false };
3293
- const drained = entry.midTurnMessageQueue.splice(0);
3294
- if (drained.length > 0) {
3295
- for (const item of drained) {
3296
- entry.settledMidTurnMessageIds.push(item.messageId);
3297
- }
3298
- if (entry.settledMidTurnMessageIds.length > MID_TURN_RECONCILIATION_RING_SIZE) {
3299
- entry.settledMidTurnMessageIds.splice(
3300
- 0,
3301
- entry.settledMidTurnMessageIds.length - MID_TURN_RECONCILIATION_RING_SIZE
3302
- );
3303
- }
3304
- }
3305
- const attachmentMemo = /* @__PURE__ */ new Map();
3306
- const serializedAttachmentIds = /* @__PURE__ */ new Set();
3307
- const items = [];
3308
- try {
3309
- for (const item of drained) {
3310
- let degraded = 0;
3311
- const planned = (item.content ?? []).filter((block) => {
3312
- if (!isSessionAttachmentReference(block)) return true;
3313
- if (serializedAttachmentIds.has(block.attachmentId)) {
3314
- degraded += 1;
3315
- return false;
3316
- }
3317
- serializedAttachmentIds.add(block.attachmentId);
3318
- return true;
3319
- });
3320
- let resolvedBlocks;
3321
- let attachmentReferences;
3322
- try {
3323
- resolvedBlocks = await entry.attachments.resolveContent(
3324
- planned,
3325
- attachmentMemo
3326
- );
3327
- attachmentReferences = planned.filter(isSessionAttachmentReference);
3328
- } catch (error) {
3329
- if (!(error instanceof SessionAttachmentReferenceError)) throw error;
3330
- writeStderrLine(
3331
- `[mid-turn] session=${JSON.stringify(entry.sessionId)} degraded attachment for message ${JSON.stringify(item.messageId)}: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
3332
- );
3333
- const perBlock = await entry.attachments.resolveContentDegrading(
3334
- planned,
3335
- attachmentMemo
3336
- );
3337
- resolvedBlocks = perBlock.resolvedBlocks;
3338
- attachmentReferences = perBlock.retainedBlocks.filter(
3339
- isSessionAttachmentReference
3340
- );
3341
- degraded += perBlock.degraded;
3342
- }
3343
- let content = [
3344
- ...item.text ? [{ type: "text", text: item.text }] : [],
3345
- ...resolvedBlocks
3346
- ];
3347
- if (degraded > 0) content = withAttachmentDegradationMarker(content);
3348
- items.push({
3349
- messageId: item.messageId,
3350
- displayText: item.text,
3351
- content,
3352
- ...attachmentReferences.length > 0 ? { attachmentReferences } : {}
3353
- });
3354
- }
3355
- } catch (error) {
3356
- const requeued = new Set(drained.map((queued) => queued.messageId));
3357
- const ring = entry.settledMidTurnMessageIds;
3358
- const kept = ring.filter((id) => !requeued.has(id));
3359
- ring.splice(0, ring.length, ...kept);
3360
- entry.midTurnMessageQueue.unshift(...drained);
3361
- writeStderrLine(
3362
- `[mid-turn] session=${JSON.stringify(entry.sessionId)} drain failed, requeued ${drained.length} message(s): ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
3363
- );
3364
- throw error;
3365
- }
3366
- const echoed = drained.filter((item) => !item.queueOnly);
3367
- const messages = drained.map((item) => item.text);
3368
- const hasQueuedPrompt = entry.pendingPromptList.some(
3369
- (prompt) => prompt.state === "queued" && !prompt.abortController.signal.aborted
3370
- );
3371
- if (echoed.length > 0) {
3372
- const published = entry.events.publish({
3373
- type: MID_TURN_MESSAGE_INJECTED_EVENT,
3374
- ...entry.activePromptId ? { promptId: entry.activePromptId } : {},
3375
- data: {
3376
- sessionId: entry.sessionId,
3377
- messages: echoed.map((item) => item.text),
3378
- messageIds: echoed.map((item) => item.messageId),
3379
- // Carry the structured `items` twin (content blocks per message) so
3380
- // the browser-side echo renderer can show attached images alongside
3381
- // the message text. Older consumers that don't read this field keep
3382
- // working unchanged.
3383
- items: echoed.map((item) => ({
3384
- ...item.content && item.content.length > 0 ? { content: item.content } : {}
3385
- }))
3386
- }
3387
- });
3388
- writeStderrLine(
3389
- published ? `[mid-turn] session=${entry.sessionId} drained=${messages.length} echoed=${echoed.length} injected into running turn` : `[mid-turn] session=${entry.sessionId} drained=${messages.length} echoed=${echoed.length} echo frame dropped (bus closed); reconciliation required`
3390
- );
3391
- }
3392
- return { messages, items, hasQueuedPrompt };
3393
- }
3394
- async handleExternalToolGuardPrepare(params) {
3395
- if (!this.externalToolGuard) {
3396
- throw RequestError.methodNotFound(
3397
- SERVE_CONTROL_EXT_METHODS.externalToolGuardPrepare
3398
- );
3399
- }
3400
- const sessionId = params["sessionId"];
3401
- const promptId = params["promptId"];
3402
- const toolCallId = params["toolCallId"];
3403
- const toolName = params["toolName"];
3404
- const args = params["arguments"];
3405
- if (typeof sessionId !== "string" || sessionId.length === 0 || promptId !== void 0 && (typeof promptId !== "string" || promptId.length === 0) || typeof toolCallId !== "string" || toolCallId.length === 0 || typeof toolName !== "string" || toolName.length === 0 || !isRecord2(args)) {
3406
- throw RequestError.invalidParams(
3407
- void 0,
3408
- "Invalid external tool guard request"
3409
- );
3410
- }
3411
- const promptScoped = promptId !== void 0;
3412
- if (!this.ownsSession(sessionId)) {
3413
- throw RequestError.invalidParams(
3414
- void 0,
3415
- "External tool guard session is not owned by this connection"
3416
- );
3417
- }
3418
- const entry = this.resolveEntry(sessionId);
3419
- if (!entry || promptScoped && (!entry.promptActive || entry.activePromptId !== promptId)) {
3420
- throw RequestError.invalidParams(
3421
- void 0,
3422
- "External tool guard prompt is not the active prompt"
3423
- );
3424
- }
3425
- const invocationCwd = params["invocationCwd"];
3426
- const decision = await this.externalToolGuard({
3427
- sessionId: entry.sessionId,
3428
- ...promptScoped ? { promptId } : {},
3429
- toolCallId,
3430
- toolName,
3431
- arguments: args,
3432
- effectiveCwd: entry.effectiveCwd,
3433
- // Forwarded verbatim and explicitly untrusted: the host policy decides
3434
- // whether it can establish this scope from state it owns.
3435
- ...typeof invocationCwd === "string" && invocationCwd.length > 0 ? { invocationCwd } : {}
3436
- });
3437
- const currentEntry = this.resolveEntry(sessionId);
3438
- if (!this.ownsSession(sessionId) || currentEntry !== entry || promptScoped && (!currentEntry.promptActive || currentEntry.activePromptId !== promptId)) {
3439
- throw RequestError.invalidParams(
3440
- void 0,
3441
- "External tool guard prompt is no longer active"
3442
- );
3443
- }
3444
- return normalizeExternalToolGuardResult(decision);
3445
- }
3446
- handleTodoStopGuardContinuationClaim(params) {
3447
- const sessionId = typeof params["sessionId"] === "string" && params["sessionId"].length > 0 ? params["sessionId"] : void 0;
3448
- if (!sessionId) return { claimed: false, hasQueuedPrompt: false };
3449
- if (!this.ownsSession(sessionId)) {
3450
- return { claimed: false, hasQueuedPrompt: false };
3451
- }
3452
- const entry = this.resolveEntry(sessionId);
3453
- if (!entry) return { claimed: false, hasQueuedPrompt: false };
3454
- const livePrompts = entry.pendingPromptList.filter(
3455
- (prompt) => !prompt.abortController.signal.aborted
3456
- );
3457
- const promptId = typeof params["promptId"] === "string" && params["promptId"].length > 0 ? params["promptId"] : void 0;
3458
- if (promptId) {
3459
- const ownsRunningPrompt = entry.activePromptId === promptId && livePrompts.some(
3460
- (prompt) => prompt.promptId === promptId && prompt.state === "running"
3461
- );
3462
- const hasCompetingRunningPrompt = livePrompts.some(
3463
- (prompt) => prompt.promptId !== promptId && prompt.state === "running"
3464
- );
3465
- if (!ownsRunningPrompt || hasCompetingRunningPrompt) {
3466
- return { claimed: false, hasQueuedPrompt: false };
3467
- }
3468
- const hasQueuedPrompt = livePrompts.some(
3469
- (prompt) => prompt.state === "queued"
3470
- );
3471
- if (hasQueuedPrompt) {
3472
- entry.todoStopGuardAwaitingQueuedPromptOwnerPromptId = promptId;
3473
- return { claimed: false, hasQueuedPrompt: true };
3474
- }
3475
- if (entry.todoStopGuardAwaitingQueuedPromptOwnerPromptId === promptId) {
3476
- delete entry.todoStopGuardAwaitingQueuedPromptOwnerPromptId;
3477
- }
3478
- return { claimed: true, hasQueuedPrompt: false };
3479
- }
3480
- if (entry.promptActive || livePrompts.length > 0) {
3481
- return { claimed: false, hasQueuedPrompt: false };
3482
- }
3483
- return { claimed: true, hasQueuedPrompt: false };
3484
- }
3485
- async handleChannelDelivery(params) {
3486
- if (!this.onChannelDelivery) {
3487
- throw RequestError.methodNotFound(
3488
- SERVE_CONTROL_EXT_METHODS.channelDelivery
3489
- );
3490
- }
3491
- const sessionId = params["sessionId"];
3492
- if (typeof sessionId !== "string" || sessionId.length === 0 || !this.ownsSession(sessionId)) {
3493
- throw RequestError.invalidParams(
3494
- void 0,
3495
- "`sessionId` must name a session owned by this connection"
3496
- );
3497
- }
3498
- const entry = this.resolveEntry(sessionId);
3499
- if (!entry) {
3500
- throw RequestError.invalidParams(void 0, "Unknown `sessionId`");
3501
- }
3502
- const deliveryId = params["deliveryId"];
3503
- const source = params["source"];
3504
- const text = params["text"];
3505
- const rawTarget = params["target"];
3506
- if (!isBoundedChannelDeliveryString(deliveryId) || source !== "prompt" && source !== "scheduled" || typeof text !== "string" || text.length > MAX_CHANNEL_DELIVERY_TEXT_CHARS || !isChannelDeliveryTarget(rawTarget)) {
3507
- throw RequestError.invalidParams(
3508
- void 0,
3509
- "Invalid channel delivery request"
3510
- );
3511
- }
3512
- const promptId = params["promptId"];
3513
- const taskId = params["taskId"];
3514
- const firedAt = params["firedAt"];
3515
- const allowedKeys = source === "prompt" ? /* @__PURE__ */ new Set([
3516
- "sessionId",
3517
- "deliveryId",
3518
- "source",
3519
- "target",
3520
- "text",
3521
- "promptId"
3522
- ]) : /* @__PURE__ */ new Set([
3523
- "sessionId",
3524
- "deliveryId",
3525
- "source",
3526
- "target",
3527
- "text",
3528
- "taskId",
3529
- "firedAt"
3530
- ]);
3531
- const correlationValid = source === "prompt" ? isBoundedChannelDeliveryString(promptId) && promptId === deliveryId : isBoundedChannelDeliveryString(taskId) && typeof firedAt === "number" && Number.isFinite(firedAt) && deliveryId === `${taskId}:${firedAt}`;
3532
- if (!correlationValid || !Object.keys(params).every((key) => allowedKeys.has(key))) {
3533
- throw RequestError.invalidParams(
3534
- void 0,
3535
- "Invalid channel delivery correlation"
3536
- );
3537
- }
3538
- const info = {
3539
- sessionId,
3540
- deliveryId,
3541
- source,
3542
- target: rawTarget,
3543
- text,
3544
- ...source === "prompt" ? { promptId } : {},
3545
- ...source === "scheduled" ? { taskId, firedAt } : {}
3546
- };
3547
- let result;
3548
- try {
3549
- result = normalizeChannelDeliveryHostResult(
3550
- await this.onChannelDelivery(info)
3551
- );
3552
- } catch {
3553
- result = {
3554
- status: "failed",
3555
- code: "channel_delivery_failed",
3556
- error: "Channel delivery failed."
3557
- };
3558
- }
3559
- try {
3560
- entry.events.publish({
3561
- type: "channel_delivery_result",
3562
- ...source === "prompt" ? { promptId } : {},
3563
- data: {
3564
- sessionId,
3565
- deliveryId,
3566
- source,
3567
- status: result.status,
3568
- ...source === "prompt" ? { promptId } : {},
3569
- ...source === "scheduled" ? { taskId, firedAt } : {},
3570
- ...result.status === "failed" ? { code: result.code, error: result.error } : {}
3571
- }
3572
- });
3573
- } catch {
3574
- }
3575
- return result;
3576
- }
3577
- /**
3578
- * Reverse tool channel (issue #5626, Phase 2) — answer the child's
3579
- * `qwen/control/client_mcp/message` ext-method. The child's session
3580
- * `McpClientManager` calls this when its agent drives a client-hosted
3581
- * (extension) MCP server: `params` carries the advertised `server` name and
3582
- * the JSON-RPC `payload` (initialize / tools/list / tools/call / a
3583
- * notification). We resolve the per-WS-connection sender via the injected
3584
- * `clientMcpSender` lookup, deliver the payload over the daemon WS, and
3585
- * return the correlated response as `{ payload }`.
3586
- *
3587
- * Rejects with ACP `methodNotFound` when no `clientMcpSender` is wired (Mode
3588
- * A / tests can't host a client MCP server), and `invalidParams` when the
3589
- * frame is malformed or the named server is no longer hosted (e.g. the
3590
- * extension disconnected mid-turn) — the agent's `SdkControlClientTransport`
3591
- * surfaces that as a transport error rather than hanging.
3592
- */
3593
- async handleClientMcpMessage(params) {
3594
- if (!this.clientMcpSender) {
3595
- throw RequestError.methodNotFound(
3596
- SERVE_CONTROL_EXT_METHODS.clientMcpMessage
3597
- );
3598
- }
3599
- const server = params["server"];
3600
- if (typeof server !== "string" || server.length === 0) {
3601
- throw RequestError.invalidParams(
3602
- void 0,
3603
- "`server` must be a non-empty string"
3604
- );
3605
- }
3606
- const payload = params["payload"];
3607
- if (payload === null || typeof payload !== "object") {
3608
- throw RequestError.invalidParams(
3609
- void 0,
3610
- "`payload` must be a JSON-RPC message object"
3611
- );
3612
- }
3613
- const send = this.clientMcpSender(server);
3614
- if (!send) {
3615
- throw RequestError.invalidParams(
3616
- void 0,
3617
- `client-hosted MCP server '${server}' is not currently connected`
3618
- );
3619
- }
3620
- const sessionId = params["sessionId"];
3621
- if (sessionId !== void 0 && (typeof sessionId !== "string" || sessionId.length === 0)) {
3622
- throw RequestError.invalidParams(
3623
- void 0,
3624
- "`sessionId` must be a non-empty string when provided"
3625
- );
3626
- }
3627
- const ownsSession = typeof sessionId === "string" && this.ownsSession(sessionId);
3628
- if (typeof sessionId === "string" && !ownsSession && !this.hasSessionSpawnInFlight()) {
3629
- throw RequestError.invalidParams(
3630
- void 0,
3631
- `Session not owned by this channel: ${sessionId}`
3632
- );
3633
- }
3634
- if (typeof sessionId === "string" && !ownsSession) {
3635
- writeStderrLine(
3636
- `[demux] session=${sessionId} type=client_mcp_message action=forwarded_without_session reason=session_registration_pending`
3637
- );
3638
- }
3639
- const response = await send(
3640
- payload,
3641
- typeof sessionId === "string" && ownsSession ? { sessionId } : void 0
3642
- );
3643
- return { payload: response };
3644
- }
3645
- /**
3646
- * Handle the `create_sub_session` tool's request: validate, then forward to
3647
- * the daemon-host `onCreateSubSession` callback (which spawns a fresh
3648
- * top-level sub-session and, for `'first-turn'`, waits for its first turn and
3649
- * returns the result). No host wired → `methodNotFound`, which the tool
3650
- * surfaces as "daemon-only".
3651
- */
3652
- async handleCreateSubSession(params) {
3653
- if (!this.onCreateSubSession) {
3654
- throw RequestError.methodNotFound(
3655
- SERVE_CONTROL_EXT_METHODS.createSubSession
3656
- );
3657
- }
3658
- const prompt = params["prompt"];
3659
- if (typeof prompt !== "string" || prompt.length === 0) {
3660
- throw RequestError.invalidParams(
3661
- void 0,
3662
- "`prompt` must be a non-empty string"
3663
- );
3664
- }
3665
- if (prompt.length > MAX_SUB_SESSION_PROMPT_CHARS) {
3666
- throw RequestError.invalidParams(
3667
- void 0,
3668
- `\`prompt\` exceeds the ${MAX_SUB_SESSION_PROMPT_CHARS}-character limit`
3669
- );
3670
- }
3671
- const completion = params["completion"];
3672
- if (completion !== "sent" && completion !== "first-turn") {
3673
- throw RequestError.invalidParams(
3674
- void 0,
3675
- "`completion` must be 'sent' or 'first-turn'"
3676
- );
3677
- }
3678
- const name = params["name"];
3679
- if (typeof name === "string" && name.length > MAX_SUB_SESSION_NAME_CHARS) {
3680
- throw RequestError.invalidParams(
3681
- void 0,
3682
- `\`name\` exceeds the ${MAX_SUB_SESSION_NAME_CHARS}-character limit`
3683
- );
3684
- }
3685
- const callerSessionId = params["callerSessionId"];
3686
- if (typeof callerSessionId !== "string" || callerSessionId.length === 0 || !this.ownsSession(callerSessionId)) {
3687
- throw RequestError.invalidParams(
3688
- void 0,
3689
- "`callerSessionId` is required and must name a session owned by this connection"
3690
- );
3691
- }
3692
- const model = params["model"];
3693
- const result = await this.onCreateSubSession({
3694
- prompt,
3695
- completion,
3696
- ...typeof model === "string" && model.length > 0 && model.length <= 128 ? { model } : {},
3697
- ...typeof name === "string" && name.length > 0 ? { name } : {},
3698
- callerSessionId
3699
- });
3700
- return {
3701
- sessionId: result.sessionId,
3702
- ...result.result !== void 0 ? { result: result.result } : {},
3703
- ...result.stopReason !== void 0 ? { stopReason: result.stopReason } : {},
3704
- ...result.parentSessionPersisted !== void 0 ? { parentSessionPersisted: result.parentSessionPersisted } : {}
3705
- };
3706
- }
3707
- async handleLiveScreenContextCapture(params) {
3708
- const handler = this.getLiveScreenContextCaptureHandler();
3709
- if (!handler) {
3710
- throw RequestError.methodNotFound(
3711
- SERVE_CONTROL_EXT_METHODS.liveCaptureScreenContext
3712
- );
3713
- }
3714
- const callerSessionId = params["callerSessionId"];
3715
- if (typeof callerSessionId !== "string" || callerSessionId.length === 0 || !this.ownsSession(callerSessionId)) {
3716
- throw RequestError.invalidParams(
3717
- void 0,
3718
- "`callerSessionId` is required and must name a session owned by this connection"
3719
- );
3720
- }
3721
- const result = await handler({ callerSessionId });
3722
- if (result.appName.length === 0 || result.appName.length > 512 || result.windowTitle !== void 0 && result.windowTitle.length > 2048 || result.accessibilityText.length > MAX_LIVE_SCREEN_CONTEXT_TEXT_CHARS || result.screenshotPath.length === 0 || result.screenshotPath.length > 4096) {
3723
- throw RequestError.internalError(void 0, "Invalid Appshot result.");
3724
- }
3725
- return {
3726
- appName: result.appName,
3727
- ...result.windowTitle ? { windowTitle: result.windowTitle } : {},
3728
- accessibilityText: result.accessibilityText,
3729
- screenshotPath: result.screenshotPath
3730
- };
3731
- }
3732
- async handleLiveTaskTool(params) {
3733
- const handler = this.getLiveTaskToolRequestHandler();
3734
- if (!handler) {
3735
- throw RequestError.methodNotFound(SERVE_CONTROL_EXT_METHODS.liveTaskTool);
3736
- }
3737
- const callerSessionId = params["callerSessionId"];
3738
- const name = params["name"];
3739
- const args = params["arguments"];
3740
- if (typeof callerSessionId !== "string" || callerSessionId.length === 0 || !this.ownsSession(callerSessionId) || typeof name !== "string" || !LIVE_TASK_TOOL_NAMES.includes(
3741
- name
3742
- ) || typeof args !== "object" || args === null || Array.isArray(args)) {
3743
- throw RequestError.invalidParams(
3744
- void 0,
3745
- "Invalid Live task-tool request."
3746
- );
3747
- }
3748
- return handler({
3749
- callerSessionId,
3750
- name,
3751
- arguments: args
3752
- });
3753
- }
3754
- async handleLiveSpeakToUser(params) {
3755
- const handler = this.getLiveSpeakToUserHandler();
3756
- if (!handler) {
3757
- throw RequestError.methodNotFound(
3758
- SERVE_CONTROL_EXT_METHODS.liveSpeakToUser
3759
- );
3760
- }
3761
- const callerSessionId = params["callerSessionId"];
3762
- const message = params["message"];
3763
- if (typeof callerSessionId !== "string" || callerSessionId.length === 0 || !this.ownsSession(callerSessionId) || typeof message !== "string" || message.trim().length === 0 || message.length > MAX_LIVE_SPEAK_TO_USER_MESSAGE_CHARS) {
3764
- throw RequestError.invalidParams(
3765
- void 0,
3766
- "Invalid Live speak-to-user request."
3767
- );
3768
- }
3769
- await handler({ callerSessionId, message });
3770
- return { accepted: true };
3771
- }
3772
- /**
3773
- * Handle child->bridge ACP `extNotification` calls. Recognized methods are
3774
- * `qwen/notify/session/model-update`,
3775
- * `qwen/notify/session/mode-update`,
3776
- * `qwen/notify/session/title-update` (auto/in-process session titles),
3777
- * `qwen/notify/session/recording-degraded`,
3778
- * `qwen/notify/session/prompt-suggestion` (followup assist),
3779
- * `qwen/notify/session/artifact-event` (hook artifacts),
3780
- * `qwen/notify/session/terminal-sequence`, and
3781
- * `_qwencode/end_turn` (background-notification and goal turns), and
3782
- * `qwen/notify/session/mcp-budget-event` — each translated into a
3783
- * session-scoped SSE frame. Unknown methods are dropped silently for
3784
- * forward-compat.
3785
- */
3786
- async extNotification(method, params) {
3787
- const notificationSessionId = params["sessionId"];
3788
- if (typeof notificationSessionId === "string" && this.abandonedRestoreIds.has(notificationSessionId)) {
3789
- return;
3790
- }
3791
- if (method === ACTIVE_WORK_NOTIFICATION_METHOD) {
3792
- const snapshot = parseActiveWorkSnapshot(params);
3793
- if (snapshot) {
3794
- this.onActiveWork?.({
3795
- v: ACTIVE_WORK_HEARTBEAT_VERSION,
3796
- seq: snapshot.seq,
3797
- sessions: snapshot.sessions.filter(
3798
- (session) => this.ownsSession(session.sessionId)
3799
- )
3800
- });
3801
- }
3802
- return;
3803
- }
3804
- if (method === "_qwencode/start_turn") {
3805
- const sessionId2 = params["sessionId"];
3806
- if (typeof sessionId2 !== "string" || sessionId2.length === 0 || params["source"] !== "goal") {
3807
- return;
3808
- }
3809
- const entry = this.resolveEntry(sessionId2);
3810
- if (!entry || !this.ownsSession(sessionId2)) return;
3811
- entry.goalTurnActive = true;
3812
- return;
3813
- }
3814
- if (method === "_qwencode/end_turn") {
3815
- const sessionId2 = params["sessionId"];
3816
- const reason = params["reason"];
3817
- const source = params["source"];
3818
- if (typeof sessionId2 !== "string" || sessionId2.length === 0 || typeof reason !== "string" || reason.length === 0 || reason.length > 128 || source !== "background_notification" && source !== "goal") {
3819
- return;
3820
- }
3821
- const entry = this.resolveEntry(sessionId2);
3822
- if (!entry || !this.ownsSession(sessionId2)) return;
3823
- if (source === "goal") {
3824
- entry.goalTurnActive = false;
3825
- this.onGoalTurnEnded?.(sessionId2);
3826
- const promptId = params["promptId"];
3827
- if (typeof promptId !== "string" || promptId.length === 0 || promptId.length > 256) {
3828
- return;
3829
- }
3830
- entry.events.publish({
3831
- type: "turn_complete",
3832
- promptId,
3833
- data: { sessionId: sessionId2, stopReason: reason, promptId }
3834
- });
3835
- return;
3836
- }
3837
- entry.events.publish({
3838
- type: "background_notification_turn_complete",
3839
- data: { sessionId: sessionId2, reason }
3840
- });
3841
- return;
3842
- }
3843
- if (method === "qwen/notify/session/generation/event") {
3844
- const sessionId2 = params["sessionId"];
3845
- const requestId = params["requestId"];
3846
- const event = params["event"];
3847
- if (params["v"] !== 1 || typeof sessionId2 !== "string" || typeof requestId !== "string" || !event || typeof event !== "object" || Array.isArray(event)) {
3848
- return;
3849
- }
3850
- const record = event;
3851
- if (record["type"] === "started") {
3852
- const model = record["model"];
3853
- const modelSource = record["modelSource"];
3854
- if (typeof model !== "string" || modelSource !== "fast" && modelSource !== "main") {
3855
- return;
3856
- }
3857
- this.onGenerationEvent?.(sessionId2, {
3858
- type: "started",
3859
- requestId,
3860
- model,
3861
- modelSource
3862
- });
3863
- return;
3864
- }
3865
- if (record["type"] === "thinking") {
3866
- this.onGenerationEvent?.(sessionId2, {
3867
- type: "thinking",
3868
- requestId
3869
- });
3870
- return;
3871
- }
3872
- if (record["type"] === "delta") {
3873
- const seq = record["seq"];
3874
- const text = record["text"];
3875
- if (typeof seq !== "number" || !Number.isSafeInteger(seq) || seq < 0 || typeof text !== "string" || text.length === 0) {
3876
- return;
3877
- }
3878
- this.onGenerationEvent?.(sessionId2, {
3879
- type: "delta",
3880
- requestId,
3881
- seq,
3882
- text
3883
- });
3884
- return;
3885
- }
3886
- return;
3887
- }
3888
- if (method === "qwen/notify/workspace/generation/event") {
3889
- const requestId = params["requestId"];
3890
- const event = params["event"];
3891
- if (params["v"] !== 1 || typeof requestId !== "string" || !event || typeof event !== "object" || Array.isArray(event)) {
3892
- return;
3893
- }
3894
- const record = event;
3895
- if (record["type"] === "started") {
3896
- const model = record["model"];
3897
- const modelSource = record["modelSource"];
3898
- if (typeof model !== "string" || modelSource !== "fast" && modelSource !== "main") {
3899
- return;
3900
- }
3901
- this.onWorkspaceGenerationEvent?.({
3902
- type: "started",
3903
- requestId,
3904
- model,
3905
- modelSource
3906
- });
3907
- return;
3908
- }
3909
- if (record["type"] === "thinking") {
3910
- this.onWorkspaceGenerationEvent?.({ type: "thinking", requestId });
3911
- return;
3912
- }
3913
- if (record["type"] === "delta") {
3914
- const seq = record["seq"];
3915
- const text = record["text"];
3916
- if (typeof seq !== "number" || !Number.isSafeInteger(seq) || seq < 0 || typeof text !== "string" || text.length === 0) {
3917
- return;
3918
- }
3919
- this.onWorkspaceGenerationEvent?.({
3920
- type: "delta",
3921
- requestId,
3922
- seq,
3923
- text
3924
- });
3925
- return;
3926
- }
3927
- return;
3928
- }
3929
- if (method === "qwen/notify/session/model-update") {
3930
- this.handleInSessionModelUpdate(params);
3931
- return;
3932
- }
3933
- if (method === "qwen/notify/session/mode-update") {
3934
- this.handleInSessionModeUpdate(params);
3935
- return;
3936
- }
3937
- if (method === "qwen/notify/session/title-update") {
3938
- const sessionId2 = params["sessionId"];
3939
- const title = params["title"];
3940
- if (typeof sessionId2 !== "string" || typeof title !== "string" || !title)
3941
- return;
3942
- const entry = this.resolveEntry(sessionId2);
3943
- if (!entry) return;
3944
- this.onSessionCatalogChanged?.();
3945
- try {
3946
- entry.events.publish({
3947
- type: "session_metadata_updated",
3948
- data: {
3949
- sessionId: sessionId2,
3950
- displayName: title,
3951
- ...typeof params["titleSource"] === "string" ? { titleSource: params["titleSource"] } : {}
3952
- }
3953
- });
3954
- } catch {
3955
- }
3956
- return;
3957
- }
3958
- if (method === "qwen/notify/session/recording-degraded") {
3959
- const sessionId2 = params["sessionId"];
3960
- if (params["v"] !== 1 || typeof sessionId2 !== "string" || sessionId2.length === 0 || params["reason"] !== "write_failed") {
3961
- writeStderrLine(
3962
- `[demux] session=${typeof sessionId2 === "string" ? sessionId2 : "<missing>"} type=session_recording_degraded action=dropped reason=malformed`
3963
- );
3964
- return;
3965
- }
3966
- const entry = this.resolveEntry(sessionId2);
3967
- if (entry && !this.ownsSession(sessionId2)) {
3968
- writeStderrLine(
3969
- `[demux] session=${sessionId2} type=session_recording_degraded action=dropped reason=session_not_owned`
3970
- );
3971
- return;
3972
- }
3973
- if (entry) entry.recordingDegraded = true;
3974
- this.publishExtNotification(sessionId2, "session_recording_degraded", {
3975
- sessionId: sessionId2,
3976
- reason: "write_failed"
3977
- });
3978
- return;
3979
- }
3980
- if (method === "qwen/notify/session/prompt-suggestion") {
3981
- const sessionId2 = params["sessionId"];
3982
- const suggestion = params["suggestion"];
3983
- const promptId = params["promptId"];
3984
- if (typeof sessionId2 !== "string" || typeof suggestion !== "string" || suggestion.length === 0 || suggestion.length > MAX_SUGGESTION_LENGTH || typeof promptId !== "string") {
3985
- writeStderrLine(
3986
- `[demux] session=${typeof sessionId2 === "string" ? sessionId2 : "<missing>"} type=prompt_suggestion action=dropped reason=malformed`
3987
- );
3988
- return;
3989
- }
3990
- const entry = this.resolveEntry(sessionId2);
3991
- if (!entry) return;
3992
- entry.events.publish({
3993
- type: "followup_suggestion",
3994
- data: { sessionId: sessionId2, suggestion, promptId }
3995
- });
3996
- return;
3997
- }
3998
- if (method === "qwen/notify/session/terminal-sequence") {
3999
- const sessionId2 = params["sessionId"];
4000
- if (typeof sessionId2 !== "string") return;
4001
- const { v: _v2, sessionId: _sid2, ...rest2 } = params;
4002
- void _v2;
4003
- void _sid2;
4004
- this.publishExtNotification(sessionId2, "terminal_sequence", rest2, true);
4005
- return;
4006
- }
4007
- if (method === "qwen/notify/session/artifact-event") {
4008
- await this.handleArtifactEvent(params);
4009
- return;
4010
- }
4011
- if (method !== "qwen/notify/session/mcp-budget-event") return;
4012
- const sessionId = params["sessionId"];
4013
- if (typeof sessionId !== "string") return;
4014
- const kind = params["kind"];
4015
- let type;
4016
- if (kind === "budget_warning") {
4017
- type = "mcp_budget_warning";
4018
- } else if (kind === "refused_batch") {
4019
- type = "mcp_child_refused_batch";
4020
- } else {
4021
- return;
4022
- }
4023
- const { v: _v, sessionId: _sid, kind: _kind, ...rest } = params;
4024
- void _v;
4025
- void _sid;
4026
- void _kind;
4027
- this.publishExtNotification(sessionId, type, rest);
4028
- }
4029
- async handleArtifactEvent(params) {
4030
- const sessionId = params["sessionId"];
4031
- const rawArtifacts = params["artifacts"];
4032
- if (typeof sessionId !== "string" || !Array.isArray(rawArtifacts)) {
4033
- writeStderrLine(
4034
- `[demux] session=${typeof sessionId === "string" ? sessionId : "<missing>"} type=artifact_event action=dropped reason=malformed`
4035
- );
4036
- return;
4037
- }
4038
- if (!this.ownsSession(sessionId) && !this.inFlightRestoreIds.has(sessionId)) {
4039
- writeStderrLine(
4040
- `[demux] session=${sessionId} type=artifact_event action=dropped reason=session_not_owned`
4041
- );
4042
- return;
4043
- }
4044
- const entry = this.resolveEntry(sessionId);
4045
- if (!entry) {
4046
- writeStderrLine(
4047
- `[demux] session=${sessionId} type=artifact_event action=dropped reason=session_not_found`
4048
- );
4049
- return;
4050
- }
4051
- const hookEventName = typeof params["hookEventName"] === "string" ? params["hookEventName"] : void 0;
4052
- const toolName = typeof params["toolName"] === "string" ? params["toolName"] : void 0;
4053
- const toolCallId = typeof params["toolCallId"] === "string" ? params["toolCallId"] : void 0;
4054
- const artifacts = extractCappedArtifactInputs(
4055
- rawArtifacts,
4056
- entry.artifacts.inputBatchLimit(),
4057
- entry.sessionId,
4058
- "hook",
4059
- (artifact) => ({
4060
- ...artifactPayloadFields(artifact),
4061
- source: "hook",
4062
- hookEventName,
4063
- toolName,
4064
- toolCallId
4065
- })
4066
- );
4067
- const turn = {
4068
- ...entry.activePromptId ? { promptId: entry.activePromptId } : {},
4069
- ...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
4070
- };
4071
- await this.upsertAndPublishArtifacts(entry, artifacts, void 0, turn);
4072
- }
4073
- async upsertAndPublishArtifacts(entry, artifacts, options, turn = {}) {
4074
- try {
4075
- const result = await entry.artifacts.upsertMany(artifacts, options);
4076
- for (const warning of result.warnings ?? []) {
4077
- writeStderrLine(
4078
- `[artifacts] session=${entry.sessionId} action=warning reason=${JSON.stringify(
4079
- warning
4080
- )}`
4081
- );
4082
- }
4083
- this.publishArtifactChanges(entry, result.changes, turn);
4084
- } catch (error) {
4085
- writeStderrLine(
4086
- `[artifacts] session=${entry.sessionId} action=dropped reason=${JSON.stringify(
4087
- artifactIngestionErrorReason(error)
4088
- )}`
4089
- );
4090
- }
4091
- }
4092
- publishArtifactChanges(entry, changes, turn) {
4093
- for (const change of changes) {
4094
- entry.events.publish({
4095
- type: "artifact_changed",
4096
- data: { sessionId: entry.sessionId, change },
4097
- ...turn
4098
- });
4099
- }
4100
- }
4101
- publishExtNotification(sessionId, type, data, turnScoped = false) {
4102
- if (this.abandonedRestoreIds.has(sessionId)) {
4103
- return;
4104
- }
4105
- const entry = this.resolveEntry(sessionId);
4106
- const frame = {
4107
- type,
4108
- data,
4109
- ...turnScoped && entry?.activePromptId ? { promptId: entry.activePromptId } : {},
4110
- ...entry?.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
4111
- };
4112
- if (entry) {
4113
- entry.events.publish(frame);
4114
- return;
4115
- }
4116
- this.bufferEarlyEvent(sessionId, frame);
4117
- }
4118
- /**
4119
- * Promote an in-session `current_model_update` extNotification to a
4120
- * `model_switched` bus event. Suppressed while the bridge is driving
4121
- * its own model roundtrip (`entry.modelRoundtripInFlight`) — there the
4122
- * bridge publishes the authoritative `model_switched`, so promoting
4123
- * here too would double-publish. A structured log records the decision
4124
- * so the `dropped` case is observable.
4125
- */
4126
- handleInSessionModelUpdate(params) {
4127
- const sessionId = params["sessionId"];
4128
- const currentModelId = params["currentModelId"];
4129
- if (typeof sessionId !== "string" || typeof currentModelId !== "string") {
4130
- return;
4131
- }
4132
- const entry = this.resolveEntry(sessionId);
4133
- if (!entry) {
4134
- writeStderrLine(
4135
- `[demux] session=${sessionId} type=current_model_update action=dropped reason=no_entry`
4136
- );
4137
- return;
4138
- }
4139
- if (entry.modelRoundtripInFlight) {
4140
- writeStderrLine(
4141
- `[demux] session=${sessionId} type=current_model_update action=suppressed reason=bridge_roundtrip_in_flight`
4142
- );
4143
- return;
4144
- }
4145
- if (this.onModelPromoted) {
4146
- this.onModelPromoted(
4147
- entry,
4148
- currentModelId,
4149
- entry.activePromptOriginatorClientId
4150
- );
4151
- } else {
4152
- entry.events.publish({
4153
- type: "model_switched",
4154
- ...entry.activePromptId ? { promptId: entry.activePromptId } : {},
4155
- data: { sessionId, modelId: currentModelId },
4156
- ...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
4157
- });
4158
- }
4159
- writeStderrLine(
4160
- `[demux] session=${sessionId} type=current_model_update action=promoted model=${currentModelId}`
4161
- );
4162
- }
4163
- /**
4164
- * A2: promote an in-session `current_mode_update` extNotification to
4165
- * `approval_mode_changed`. Uses the same suppression pattern as
4166
- * `handleInSessionModelUpdate` — suppressed while the bridge is driving
4167
- * its own approval-mode roundtrip (`entry.approvalModeRoundtripInFlight`)
4168
- * — but diverges with two additions the model handler lacks: enum
4169
- * validation against `KNOWN_APPROVAL_MODES`, and a legacy
4170
- * `session_update{current_mode_update}` dual-emit for IDE companion
4171
- * compat (transition — see §6 of the design doc), itself deduped via the
4172
- * `legacyFrameSent` flag.
4173
- */
4174
- handleInSessionModeUpdate(params) {
4175
- const sessionId = params["sessionId"];
4176
- const currentModeId = params["currentModeId"];
4177
- if (typeof sessionId !== "string" || typeof currentModeId !== "string") {
4178
- return;
4179
- }
4180
- if (!KNOWN_APPROVAL_MODES.has(currentModeId)) {
4181
- writeStderrLine(
4182
- `[demux] session=${sessionId} type=current_mode_update action=dropped reason=unknown_mode mode=${currentModeId}`
4183
- );
4184
- return;
4185
- }
4186
- const entry = this.resolveEntry(sessionId);
4187
- if (!entry) {
4188
- writeStderrLine(
4189
- `[demux] session=${sessionId} type=current_mode_update action=dropped reason=no_entry`
4190
- );
4191
- return;
4192
- }
4193
- if (entry.approvalModeRoundtripInFlight) {
4194
- writeStderrLine(
4195
- `[demux] session=${sessionId} type=current_mode_update action=suppressed reason=bridge_roundtrip_in_flight`
4196
- );
4197
- return;
4198
- }
4199
- if (this.onModePromoted) {
4200
- this.onModePromoted(
4201
- entry,
4202
- currentModeId,
4203
- entry.activePromptOriginatorClientId
4204
- );
4205
- } else {
4206
- entry.events.publish({
4207
- type: "approval_mode_changed",
4208
- ...entry.activePromptId ? { promptId: entry.activePromptId } : {},
4209
- data: {
4210
- sessionId,
4211
- previous: "default",
4212
- next: currentModeId,
4213
- persisted: false
4214
- },
4215
- ...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
4216
- });
4217
- }
4218
- if (params["legacyFrameSent"] === true) {
4219
- writeStderrLine(
4220
- `[demux] session=${sessionId} type=current_mode_update action=promoted mode=${currentModeId} legacy_frame=skipped`
4221
- );
4222
- return;
4223
- }
4224
- entry.events.publish({
4225
- type: "session_update",
4226
- ...entry.activePromptId ? { promptId: entry.activePromptId } : {},
4227
- data: {
4228
- sessionId,
4229
- update: {
4230
- sessionUpdate: "current_mode_update",
4231
- currentModeId
4232
- }
4233
- },
4234
- ...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
4235
- });
4236
- writeStderrLine(
4237
- `[demux] session=${sessionId} type=current_mode_update action=promoted mode=${currentModeId}`
4238
- );
4239
- }
4240
- /**
4241
- * Enqueue `frame` for `sessionId`. Lazy TTL sweep runs first so
4242
- * caller doesn't pay for stale entries before deciding whether
4243
- * the session-cap is reached. New sessionIds past
4244
- * `MAX_EARLY_EVENT_SESSIONS` are dropped (defense against a
4245
- * malicious / buggy child fanning out fake sessionIds); same-
4246
- * sessionId frames past `MAX_EARLY_EVENTS_PER_SESSION` are
4247
- * dropped to bound per-session memory.
4248
- */
4249
- bufferEarlyEvent(sessionId, frame) {
4250
- if (this.abandonedRestoreIds.has(sessionId)) {
4251
- return;
4252
- }
4253
- const now = Date.now();
4254
- this.sweepExpiredTombstones(now);
4255
- if (this.tombstonedSessionIds.has(sessionId) && !this.inFlightSessionRegistrationIds.has(sessionId)) {
4256
- writeStderrLine(
4257
- `qwen serve: dropping early extNotification for tombstoned session ${JSON.stringify(sessionId)} (post-close stale event)`
4258
- );
4259
- return;
4260
- }
4261
- this.sweepExpiredEarlyEvents(now);
4262
- let buf = this.earlyEvents.get(sessionId);
4263
- if (!buf) {
4264
- if (this.earlyEvents.size >= MAX_EARLY_EVENT_SESSIONS) {
4265
- writeStderrLine(
4266
- `qwen serve: dropping early extNotification \u2014 early-event buffer at MAX_EARLY_EVENT_SESSIONS (${MAX_EARLY_EVENT_SESSIONS}); possible session-id fanout abuse`
4267
- );
4268
- return;
4269
- }
4270
- buf = { frames: [], expiresAt: now + EARLY_EVENT_TTL_MS };
4271
- this.earlyEvents.set(sessionId, buf);
4272
- }
4273
- if (buf.frames.length >= MAX_EARLY_EVENTS_PER_SESSION) {
4274
- writeStderrLine(
4275
- `qwen serve: dropping early extNotification for session ${JSON.stringify(sessionId)} \u2014 per-session cap (${MAX_EARLY_EVENTS_PER_SESSION}) reached`
4276
- );
4277
- return;
4278
- }
4279
- buf.frames.push(frame);
4280
- }
4281
- sweepExpiredEarlyEvents(now) {
4282
- for (const [sid, buf] of this.earlyEvents) {
4283
- if (buf.expiresAt <= now) this.earlyEvents.delete(sid);
4284
- }
4285
- }
4286
- sweepExpiredTombstones(now) {
4287
- for (const [sid, expiresAt] of this.tombstonedSessionIds) {
4288
- if (expiresAt <= now) this.tombstonedSessionIds.delete(sid);
4289
- }
4290
- }
4291
- /**
4292
- * Mark a sessionId as closed so a late `extNotification` from the
4293
- * dying child can't leak into the early-event buffer. Bridge factory
4294
- * calls this from every `byId.delete(sid)` site (kill path,
4295
- * channel.exited handler, closeSession). Idempotent on already-
4296
- * tombstoned ids — refreshes the TTL so a recently-killed id stays
4297
- * dead long enough for any in-flight stale frames to expire.
4298
- */
4299
- markSessionClosed(sessionId) {
4300
- const now = Date.now();
4301
- this.sweepExpiredTombstones(now);
4302
- this.tombstonedSessionIds.set(sessionId, now + EARLY_EVENT_TTL_MS);
4303
- this.earlyEvents.delete(sessionId);
4304
- }
4305
- /**
4306
- * Mark a sessionId as currently being restored via `session/load` /
4307
- * `session/resume`. While in this set, `bufferEarlyEvent` accepts
4308
- * frames for the id even if it's tombstoned — so restore-time
4309
- * early events from the freshly-restored child reach
4310
- * `drainEarlyEvents` instead of being rejected by the tombstone.
4311
- *
4312
- * Bridge factory calls this BEFORE awaiting the ACP restore call.
4313
- * `clearRestoreInFlight` is paired in the matching `finally` so a
4314
- * failed restore doesn't leave a dangling allow-list entry.
4315
- */
4316
- markRestoreInFlight(sessionId) {
4317
- this.markSessionRegistrationInFlight(sessionId);
4318
- this.inFlightRestoreIds.add(sessionId);
4319
- }
4320
- /**
4321
- * Transfer an id from closed/abandoned ownership to a new registration
4322
- * attempt before its ACP call starts. The in-flight allow-list is what lets
4323
- * legitimate early notifications bypass the ordinary close tombstone until
4324
- * `createSessionEntry` can drain them.
4325
- */
4326
- markSessionRegistrationInFlight(sessionId) {
4327
- this.clearAbandonedRestoreFence(sessionId);
4328
- this.inFlightSessionRegistrationIds.add(sessionId);
4329
- }
4330
- /**
4331
- * Drop the abandoned-restore fence for `sessionId`.
4332
- *
4333
- * The fence has no TTL and suppresses session updates, guardrail events,
4334
- * and child notifications, so it must not outlive the abandoned attempt it
4335
- * was raised for. The bridge clears it whenever a legitimate owner takes
4336
- * the id — a new restore, or `createSessionEntry` registering a session
4337
- * from any other route.
4338
- */
4339
- clearAbandonedRestoreFence(sessionId) {
4340
- this.abandonedRestoreIds.delete(sessionId);
4341
- }
4342
- /**
4343
- * Companion to `markRestoreInFlight`. Bridge factory calls this when
4344
- * the restore IIFE settles — after `createSessionEntry` runs
4345
- * (success) or after the ACP restore call fails (error). Cleared to
4346
- * prevent the Set from growing forever under high restore churn.
4347
- */
4348
- clearRestoreInFlight(sessionId) {
4349
- this.inFlightRestoreIds.delete(sessionId);
4350
- this.clearSessionRegistrationInFlight(sessionId);
4351
- }
4352
- clearSessionRegistrationInFlight(sessionId) {
4353
- this.inFlightSessionRegistrationIds.delete(sessionId);
4354
- }
4355
- markRestoreAbandoned(sessionId) {
4356
- this.inFlightRestoreIds.delete(sessionId);
4357
- this.inFlightSessionRegistrationIds.delete(sessionId);
4358
- this.abandonedRestoreIds.add(sessionId);
4359
- this.earlyEvents.delete(sessionId);
4360
- }
4361
- /**
4362
- * Drain any frames buffered for `sessionId` onto `entry.events`.
4363
- * Bridge calls this immediately after `byId.set(sessionId, entry)`
4364
- * in `createSessionEntry`. The frames were captured before the
4365
- * entry existed (e.g. MCP discovery during the child's `newSession`
4366
- * handler), so draining them now lands them in the replay ring as
4367
- * the FIRST events of this session.
4368
- *
4369
- * Public so the bridge factory can call it directly. Idempotent on
4370
- * unknown sessionIds.
4371
- */
4372
- drainEarlyEvents(sessionId, entry) {
4373
- this.tombstonedSessionIds.delete(sessionId);
4374
- const buf = this.earlyEvents.get(sessionId);
4375
- if (!buf) return;
4376
- for (const frame of buf.frames) {
4377
- if (frame.type === "session_recording_degraded") {
4378
- entry.recordingDegraded = true;
4379
- }
4380
- entry.events.publish(frame);
4381
- }
4382
- this.earlyEvents.delete(sessionId);
4383
- }
4384
- async writeTextFile(params) {
4385
- if (this.fileSystem) {
4386
- try {
4387
- return await this.fileSystem.writeText(params);
4388
- } catch (err) {
4389
- preserveFsErrorOverAcp(err);
4390
- }
4391
- }
4392
- let realTarget = params.path;
4393
- try {
4394
- realTarget = await fs2.realpath(params.path);
4395
- } catch (err) {
4396
- const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
4397
- if (code !== "ENOENT") throw err;
4398
- try {
4399
- const linkTarget = await fs2.readlink(params.path);
4400
- realTarget = path2.resolve(path2.dirname(params.path), linkTarget);
4401
- } catch {
4402
- }
4403
- }
4404
- const tmp = `${realTarget}.${process.pid}.${Date.now()}.${randomUUID2()}.tmp`;
4405
- let preserveMode;
4406
- try {
4407
- const targetStat = await fs2.stat(realTarget);
4408
- preserveMode = {
4409
- mode: targetStat.mode & 4095,
4410
- uid: targetStat.uid,
4411
- gid: targetStat.gid
4412
- };
4413
- } catch (err) {
4414
- const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
4415
- if (code !== "ENOENT") throw err;
4416
- }
4417
- try {
4418
- await fs2.writeFile(tmp, params.content, {
4419
- encoding: "utf8",
4420
- flag: "wx",
4421
- mode: preserveMode?.mode ?? 384
4422
- });
4423
- if (preserveMode) {
4424
- await fs2.chmod(tmp, preserveMode.mode).catch(() => {
4425
- });
4426
- await fs2.chown(tmp, preserveMode.uid, preserveMode.gid).catch(() => {
4427
- });
4428
- }
4429
- await fs2.rename(tmp, realTarget);
4430
- } catch (err) {
4431
- await fs2.unlink(tmp).catch(() => {
4432
- });
4433
- throw err;
4434
- }
4435
- return {};
4436
- }
4437
- async readTextFile(params) {
4438
- if (this.fileSystem) {
4439
- try {
4440
- return await this.fileSystem.readText(params);
4441
- } catch (err) {
4442
- preserveFsErrorOverAcp(err);
4443
- }
4444
- }
4445
- if (typeof params.limit === "number" && params.limit <= 0) {
4446
- return { content: "" };
4447
- }
4448
- if (typeof params.limit === "number" && params.limit > 0 && !Number.isSafeInteger(params.limit)) {
4449
- throw RequestError.invalidParams(
4450
- void 0,
4451
- `\`limit\` must be a positive integer, got ${params.limit}`
4452
- );
4453
- }
4454
- if (typeof params.line === "number" && params.line > 0 && !Number.isSafeInteger(params.line)) {
4455
- throw RequestError.invalidParams(
4456
- void 0,
4457
- `\`line\` must be a positive integer, got ${params.line}`
4458
- );
4459
- }
4460
- const READ_FILE_SIZE_CAP = 100 * 1024 * 1024;
4461
- const stats = await fs2.stat(params.path);
4462
- if (!stats.isFile()) {
4463
- throw new Error(
4464
- `readTextFile: ${params.path} is not a regular file (reported as ${describeStatKind(stats)}). Pipe / device / proc-like inputs can produce unbounded data and aren't supported by the bridge fs proxy.`
4465
- );
4466
- }
4467
- if (stats.size > READ_FILE_SIZE_CAP) {
4468
- throw new Error(
4469
- `readTextFile: ${params.path} is ${stats.size} bytes, exceeds the ${READ_FILE_SIZE_CAP}-byte daemon cap. Tail/grep externally and feed the relevant slice instead.`
4470
- );
4471
- }
4472
- const content = await fs2.readFile(params.path, "utf8");
4473
- if (typeof params.line === "number" || typeof params.limit === "number") {
4474
- const startLine = params.line ?? 1;
4475
- const start = startLine > 0 ? startLine - 1 : 0;
4476
- const end = params.limit != null ? start + params.limit : void 0;
4477
- return { content: sliceLineRange(content, start, end) };
4478
- }
4479
- return { content };
1328
+ var ATTRIBUTE_PREFIX = "qwen-code.daemon.acp_startup";
1329
+ function isRecord(value) {
1330
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1331
+ }
1332
+ __name(isRecord, "isRecord");
1333
+ function readDuration(source, key) {
1334
+ const value = source[key];
1335
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= MAX_PROFILE_DURATION_MS ? value : void 0;
1336
+ }
1337
+ __name(readDuration, "readDuration");
1338
+ function readDurations(source, keys) {
1339
+ if (!isRecord(source)) {
1340
+ return { values: {}, complete: false };
4480
1341
  }
4481
- };
4482
- var A2UI_TOOL_RE = /(^|__)(present_ui|present_choices|a2ui_action)$/;
4483
- function isA2uiToolMeta(meta) {
4484
- if (!meta) return false;
4485
- if (typeof meta.serverId === "string" && meta.serverId.toLowerCase().includes("a2ui"))
4486
- return true;
4487
- return typeof meta.toolName === "string" && A2UI_TOOL_RE.test(meta.toolName);
4488
- }
4489
- __name(isA2uiToolMeta, "isA2uiToolMeta");
4490
- function splitA2uiText(raw) {
4491
- const s = raw.replace(/^\s+/, "");
4492
- if (s[0] !== "[") return null;
4493
- let depth = 0;
4494
- let inStr = false;
4495
- let esc = false;
4496
- let end = -1;
4497
- for (let i = 0; i < s.length; i++) {
4498
- const c = s[i];
4499
- if (inStr) {
4500
- if (esc) esc = false;
4501
- else if (c === "\\") esc = true;
4502
- else if (c === '"') inStr = false;
4503
- continue;
4504
- }
4505
- if (c === '"') inStr = true;
4506
- else if (c === "[") depth++;
4507
- else if (c === "]") {
4508
- depth--;
4509
- if (depth === 0) {
4510
- end = i + 1;
4511
- break;
4512
- }
1342
+ const values = {};
1343
+ let complete = true;
1344
+ for (const key of keys) {
1345
+ const value = readDuration(source, key);
1346
+ if (value === void 0) {
1347
+ complete = false;
1348
+ } else {
1349
+ values[key] = value;
4513
1350
  }
4514
1351
  }
4515
- if (end < 0) return null;
4516
- try {
4517
- const arr = JSON.parse(s.slice(0, end));
4518
- if (!Array.isArray(arr) || arr.length === 0) return null;
4519
- return [arr, s.slice(end).trim()];
4520
- } catch {
4521
- return null;
4522
- }
1352
+ return { values, complete };
4523
1353
  }
4524
- __name(splitA2uiText, "splitA2uiText");
4525
- function surfaceIdOf(c) {
4526
- const cmd = c;
4527
- return cmd?.createSurface?.surfaceId ?? cmd?.updateComponents?.surfaceId ?? cmd?.updateDataModel?.surfaceId ?? cmd?.deleteSurface?.surfaceId;
1354
+ __name(readDurations, "readDurations");
1355
+ function addDurationAttributes(attributes, group, values) {
1356
+ for (const [key, value] of Object.entries(values)) {
1357
+ if (group === "phase" && key === "unattributedMs") continue;
1358
+ const attributeKey = key.replace(
1359
+ /[A-Z]/g,
1360
+ (letter) => `_${letter.toLowerCase()}`
1361
+ );
1362
+ attributes[`${ATTRIBUTE_PREFIX}.${group}.${attributeKey}`] = value;
1363
+ }
4528
1364
  }
4529
- __name(surfaceIdOf, "surfaceIdOf");
4530
- function extractA2uiToolUpdate(params) {
4531
- const update = params.update;
4532
- if (!update || update["sessionUpdate"] !== "tool_call_update") return null;
4533
- const meta = update["_meta"];
4534
- if (!isA2uiToolMeta(meta)) return null;
4535
- const content = update["content"];
4536
- if (!Array.isArray(content)) return null;
4537
- let split = null;
4538
- let hitIndex = -1;
4539
- for (let i = 0; i < content.length; i++) {
4540
- const inner = content[i]?.content;
4541
- if (typeof inner?.text === "string") {
4542
- split = splitA2uiText(inner.text);
4543
- if (split) {
4544
- hitIndex = i;
4545
- break;
4546
- }
4547
- }
1365
+ __name(addDurationAttributes, "addDurationAttributes");
1366
+ var PHASE_KEYS = [
1367
+ "processToProfilerReadyMs",
1368
+ "geminiImportMs",
1369
+ "argsParseMs",
1370
+ "settingsLoadMs",
1371
+ "configConstructionMs",
1372
+ "appInitializationMs",
1373
+ "acpImportMs",
1374
+ "bootstrapConfigInitializationMs",
1375
+ "transportSetupMs",
1376
+ "initializeHandlerMs",
1377
+ "unattributedMs"
1378
+ ];
1379
+ var CONFIG_KEYS = [
1380
+ "extensionsInitialMs",
1381
+ "hooksMs",
1382
+ "skillsMs",
1383
+ "extensionsFinalMs",
1384
+ "hierarchicalMemoryMs",
1385
+ "toolRegistryMs",
1386
+ "ripgrepProbeMs",
1387
+ "toolWarmupMs",
1388
+ "otherMs"
1389
+ ];
1390
+ function getChannelStartupProfileAttributes(response, receivedAtEpochMs, initializeTimeoutMs) {
1391
+ if (!isRecord(response) || !isRecord(response["_meta"])) {
1392
+ return void 0;
4548
1393
  }
4549
- if (!split) return null;
4550
- const [commands, fallback] = split;
4551
- const order = [];
4552
- const grouped = /* @__PURE__ */ new Map();
4553
- for (const c of commands) {
4554
- const sid = surfaceIdOf(c);
4555
- if (!sid) {
4556
- const shape = c && typeof c === "object" ? Object.keys(c).join(",") || "empty object" : typeof c;
4557
- writeStderrLine(
4558
- `a2ui: dropping command with unrecognized shape (${shape})`
4559
- );
4560
- continue;
4561
- }
4562
- if (!grouped.has(sid)) {
4563
- grouped.set(sid, []);
4564
- order.push(sid);
4565
- }
4566
- grouped.get(sid).push(c);
4567
- }
4568
- const surfaces = order.map((sid) => ({
4569
- surfaceId: sid,
4570
- commands: grouped.get(sid)
4571
- }));
4572
- const sanitizedText = fallback || "[A2UI surface rendered]";
4573
- const sanitizedContent = content.map(
4574
- (block, i) => i === hitIndex ? {
4575
- ...block,
4576
- content: {
4577
- ...block.content ?? {},
4578
- text: sanitizedText
4579
- }
4580
- } : block
4581
- );
4582
- const sanitizedUpdate = {
4583
- ...update,
4584
- content: sanitizedContent
1394
+ const profile = response["_meta"][CHANNEL_STARTUP_PROFILE_META_KEY];
1395
+ if (!isRecord(profile) || profile["v"] !== CHANNEL_STARTUP_PROFILE_VERSION) {
1396
+ return void 0;
1397
+ }
1398
+ const phases = readDurations(profile["phases"], PHASE_KEYS);
1399
+ const config = readDurations(profile["config"], CONFIG_KEYS);
1400
+ const processToResponseMs = readDuration(profile, "processToResponseMs");
1401
+ const responseBuiltAtEpochMs = profile["responseBuiltAtEpochMs"];
1402
+ const childComplete = profile["complete"] === true;
1403
+ const validResponseEpoch = typeof responseBuiltAtEpochMs === "number" && Number.isFinite(responseBuiltAtEpochMs) && responseBuiltAtEpochMs >= 0;
1404
+ const effectiveComplete = childComplete && phases.complete && config.complete && processToResponseMs !== void 0 && validResponseEpoch;
1405
+ const attributes = {
1406
+ [`${ATTRIBUTE_PREFIX}.profile.version`]: CHANNEL_STARTUP_PROFILE_VERSION,
1407
+ [`${ATTRIBUTE_PREFIX}.profile.complete`]: effectiveComplete
4585
1408
  };
4586
- if (typeof update["rawOutput"] === "string") {
4587
- sanitizedUpdate["rawOutput"] = sanitizedText;
1409
+ if (processToResponseMs !== void 0) {
1410
+ attributes[`${ATTRIBUTE_PREFIX}.child.process_to_response_ms`] = processToResponseMs;
4588
1411
  }
4589
- return {
4590
- surfaces,
4591
- callId: typeof update["toolCallId"] === "string" ? update["toolCallId"] : void 0,
4592
- sanitizedParams: {
4593
- ...params,
4594
- update: sanitizedUpdate
1412
+ if (phases.values["unattributedMs"] !== void 0) {
1413
+ attributes[`${ATTRIBUTE_PREFIX}.child.unattributed_ms`] = phases.values["unattributedMs"];
1414
+ }
1415
+ addDurationAttributes(attributes, "phase", phases.values);
1416
+ addDurationAttributes(attributes, "config", config.values);
1417
+ if (validResponseEpoch) {
1418
+ const transportMs = receivedAtEpochMs - responseBuiltAtEpochMs;
1419
+ if (Number.isFinite(transportMs) && transportMs >= 0 && transportMs <= initializeTimeoutMs) {
1420
+ attributes[`${ATTRIBUTE_PREFIX}.response_transport_ms`] = transportMs;
4595
1421
  }
4596
- };
1422
+ }
1423
+ return attributes;
4597
1424
  }
4598
- __name(extractA2uiToolUpdate, "extractA2uiToolUpdate");
1425
+ __name(getChannelStartupProfileAttributes, "getChannelStartupProfileAttributes");
4599
1426
 
4600
1427
  // packages/acp-bridge/src/generation-stream.ts
4601
1428
  init_esbuild_shims();
@@ -4653,8 +1480,8 @@ var GenerationStreamQueue = class {
4653
1480
  new Error("GenerationStreamQueue supports only one pending reader")
4654
1481
  );
4655
1482
  }
4656
- return new Promise((resolve2, reject) => {
4657
- this.waiter = { resolve: resolve2, reject };
1483
+ return new Promise((resolve, reject) => {
1484
+ this.waiter = { resolve, reject };
4658
1485
  });
4659
1486
  }
4660
1487
  [Symbol.asyncIterator]() {
@@ -4671,8 +1498,8 @@ var GenerationStreamQueue = class {
4671
1498
  // packages/acp-bridge/src/sessionArtifacts.ts
4672
1499
  init_esbuild_shims();
4673
1500
  import { createHash } from "node:crypto";
4674
- import { constants as fsConstants, promises as fs3 } from "node:fs";
4675
- import path3 from "node:path";
1501
+ import { constants as fsConstants, promises as fs } from "node:fs";
1502
+ import path from "node:path";
4676
1503
  var SOURCE_RESERVATIONS = {
4677
1504
  tool: 100,
4678
1505
  client: 50,
@@ -4748,6 +1575,9 @@ var SessionArtifactStore = class {
4748
1575
  inputBatchLimit() {
4749
1576
  return this.maxArtifacts * 2;
4750
1577
  }
1578
+ resetWorkspaceResolutionCache() {
1579
+ this.realWorkspaceCwdPromise = void 0;
1580
+ }
4751
1581
  async list() {
4752
1582
  return this.enqueue(async () => {
4753
1583
  await this.refreshWorkspaceStatuses();
@@ -5093,7 +1923,8 @@ var SessionArtifactStore = class {
5093
1923
  if (!markerIds.has(artifact.id)) continue;
5094
1924
  const markerArtifact = await this.normalizeRestoredMarkerArtifact(
5095
1925
  artifact,
5096
- warnings
1926
+ warnings,
1927
+ options.workspaceAccess === "metadata-only"
5097
1928
  );
5098
1929
  if (markerArtifact)
5099
1930
  this.markerArtifacts.set(artifact.id, markerArtifact);
@@ -5115,7 +1946,11 @@ var SessionArtifactStore = class {
5115
1946
  {
5116
1947
  metadataBudget: "persisted",
5117
1948
  workspaceExpected: workspaceExpectedFromArtifact(artifact),
5118
- hashWorkspaceContent: false
1949
+ hashWorkspaceContent: false,
1950
+ ...options.workspaceAccess === "metadata-only" ? {
1951
+ workspaceAccess: "metadata-only",
1952
+ workspaceStatus: artifact.status
1953
+ } : {}
5119
1954
  }
5120
1955
  );
5121
1956
  if (this.stickyEphemeralIds.has(normalized.id) && normalized.retention !== "ephemeral") {
@@ -5222,7 +2057,7 @@ var SessionArtifactStore = class {
5222
2057
  }
5223
2058
  });
5224
2059
  }
5225
- async normalizeRestoredMarkerArtifact(artifact, warnings) {
2060
+ async normalizeRestoredMarkerArtifact(artifact, warnings, metadataOnly = false) {
5226
2061
  try {
5227
2062
  const input = persistedArtifactToInput(artifact);
5228
2063
  if (input.retention === "pinned") {
@@ -5238,7 +2073,11 @@ var SessionArtifactStore = class {
5238
2073
  {
5239
2074
  metadataBudget: "persisted",
5240
2075
  workspaceExpected: workspaceExpectedFromArtifact(artifact),
5241
- hashWorkspaceContent: false
2076
+ hashWorkspaceContent: false,
2077
+ ...metadataOnly ? {
2078
+ workspaceAccess: "metadata-only",
2079
+ workspaceStatus: artifact.status
2080
+ } : {}
5242
2081
  }
5243
2082
  );
5244
2083
  if (normalized.id !== artifact.id) {
@@ -5580,10 +2419,10 @@ var SessionArtifactStore = class {
5580
2419
  }
5581
2420
  const realWorkspace = await this.getRealWorkspaceCwdForValidation();
5582
2421
  const normalizedPath = normalizeWorkspacePath(workspacePath, realWorkspace);
5583
- const absolutePath = path3.resolve(realWorkspace, normalizedPath);
2422
+ const absolutePath = path.resolve(realWorkspace, normalizedPath);
5584
2423
  let stat;
5585
2424
  try {
5586
- stat = await fs3.lstat(absolutePath);
2425
+ stat = await fs.lstat(absolutePath);
5587
2426
  } catch (error) {
5588
2427
  if (isNotFoundError(error)) {
5589
2428
  return { inputs: [input] };
@@ -5599,13 +2438,13 @@ var SessionArtifactStore = class {
5599
2438
  if (stat.isSymbolicLink()) {
5600
2439
  let realPath;
5601
2440
  try {
5602
- realPath = await fs3.realpath(absolutePath);
2441
+ realPath = await fs.realpath(absolutePath);
5603
2442
  } catch {
5604
2443
  return { inputs: [input] };
5605
2444
  }
5606
2445
  let realStat;
5607
2446
  try {
5608
- realStat = await fs3.lstat(realPath);
2447
+ realStat = await fs.lstat(realPath);
5609
2448
  } catch (error) {
5610
2449
  if (isNotFoundError(error)) {
5611
2450
  return { inputs: [input] };
@@ -5625,7 +2464,7 @@ var SessionArtifactStore = class {
5625
2464
  return { inputs: [input] };
5626
2465
  } else {
5627
2466
  try {
5628
- walkDir = await fs3.realpath(absolutePath);
2467
+ walkDir = await fs.realpath(absolutePath);
5629
2468
  } catch (error) {
5630
2469
  if (isNotFoundError(error)) {
5631
2470
  return { inputs: [input] };
@@ -5637,7 +2476,7 @@ var SessionArtifactStore = class {
5637
2476
  );
5638
2477
  }
5639
2478
  }
5640
- const resolvedRelative = path3.relative(realWorkspace, walkDir);
2479
+ const resolvedRelative = path.relative(realWorkspace, walkDir);
5641
2480
  if (!resolvedRelative || isOutsidePath(resolvedRelative)) {
5642
2481
  throw new SessionArtifactValidationError(
5643
2482
  "workspacePath must stay inside the workspace",
@@ -5645,7 +2484,7 @@ var SessionArtifactStore = class {
5645
2484
  );
5646
2485
  }
5647
2486
  if (pathHasSkippedDirectoryComponent(
5648
- resolvedRelative.split(path3.sep).join("/")
2487
+ resolvedRelative.split(path.sep).join("/")
5649
2488
  ) || walkRelative && pathHasSkippedDirectoryComponent(walkRelative)) {
5650
2489
  throw new SessionArtifactValidationError(
5651
2490
  "workspacePath is a skipped directory and cannot be recorded",
@@ -5681,7 +2520,7 @@ var SessionArtifactStore = class {
5681
2520
  walkDir,
5682
2521
  walkRelative,
5683
2522
  realWorkspace,
5684
- (filePath) => isRecordableDerivedChild(path3.posix.basename(filePath), filePath)
2523
+ (filePath) => isRecordableDerivedChild(path.posix.basename(filePath), filePath)
5685
2524
  );
5686
2525
  } catch (error) {
5687
2526
  if (isNotFoundError(error)) {
@@ -5722,7 +2561,7 @@ var SessionArtifactStore = class {
5722
2561
  }
5723
2562
  return {
5724
2563
  inputs: collected.files.map((filePath) => {
5725
- const title = path3.posix.basename(filePath).trim();
2564
+ const title = path.posix.basename(filePath).trim();
5726
2565
  const description = parentDescription || (parentTitle && parentTitle !== title ? parentTitle : void 0);
5727
2566
  return {
5728
2567
  ...input,
@@ -5759,7 +2598,7 @@ var SessionArtifactStore = class {
5759
2598
  const trustedPublisher = trustedPublisherFromCaller;
5760
2599
  const workspacePath = input.workspacePath ? normalizeWorkspacePath(
5761
2600
  input.workspacePath,
5762
- await this.getRealWorkspaceCwdForValidation()
2601
+ options.workspaceAccess === "metadata-only" ? this.workspaceCwd : await this.getRealWorkspaceCwdForValidation()
5763
2602
  ) : void 0;
5764
2603
  const managedId = normalizeManagedId(input.managedId);
5765
2604
  const rawStorage = input.storage;
@@ -5779,7 +2618,10 @@ var SessionArtifactStore = class {
5779
2618
  const retention = normalizeRetention(input.retention, {
5780
2619
  persistenceAvailable: this.persistence !== void 0
5781
2620
  });
5782
- const workspaceStatus = workspacePath ? await this.getInitialWorkspaceStatus(
2621
+ const workspaceStatus = workspacePath ? options.workspaceAccess === "metadata-only" ? {
2622
+ status: options.workspaceStatus ?? "missing",
2623
+ ...options.workspaceExpected?.sizeBytes !== void 0 ? { sizeBytes: options.workspaceExpected.sizeBytes } : {}
2624
+ } : await this.getInitialWorkspaceStatus(
5783
2625
  workspacePath,
5784
2626
  options.workspaceExpected,
5785
2627
  { hashContent: options.hashWorkspaceContent !== false }
@@ -5818,7 +2660,7 @@ var SessionArtifactStore = class {
5818
2660
  storage,
5819
2661
  source,
5820
2662
  status: workspaceStatus?.status ?? "available",
5821
- ...workspacePath ? { lastStatAt: Date.now() } : {},
2663
+ ...workspacePath && options.workspaceAccess !== "metadata-only" ? { lastStatAt: Date.now() } : {},
5822
2664
  title,
5823
2665
  description,
5824
2666
  workspacePath,
@@ -5934,7 +2776,7 @@ var SessionArtifactStore = class {
5934
2776
  }
5935
2777
  getRealWorkspaceCwd() {
5936
2778
  if (!this.realWorkspaceCwdPromise) {
5937
- const promise = fs3.realpath(this.workspaceCwd).catch((error) => {
2779
+ const promise = fs.realpath(this.workspaceCwd).catch((error) => {
5938
2780
  if (this.realWorkspaceCwdPromise === promise) {
5939
2781
  this.realWorkspaceCwdPromise = void 0;
5940
2782
  }
@@ -6608,7 +3450,7 @@ function buildIdentityKey(input) {
6608
3450
  }
6609
3451
  __name(buildIdentityKey, "buildIdentityKey");
6610
3452
  function managedIdForWorkspacePath(workspaceCwd, workspacePath) {
6611
- return createHash("sha1").update(path3.resolve(workspaceCwd, workspacePath)).digest("hex").slice(0, 16);
3453
+ return createHash("sha1").update(path.resolve(workspaceCwd, workspacePath)).digest("hex").slice(0, 16);
6612
3454
  }
6613
3455
  __name(managedIdForWorkspacePath, "managedIdForWorkspacePath");
6614
3456
  function normalizeString(value, field, maxLength, required) {
@@ -6655,7 +3497,7 @@ __name(normalizeString, "normalizeString");
6655
3497
  function normalizeManagedId(value) {
6656
3498
  const managedId = normalizeString(value, "managedId", 200, false);
6657
3499
  if (!managedId) return void 0;
6658
- if (managedId.includes("/") || managedId.includes("\\") || managedId.includes("..") || path3.isAbsolute(managedId) || path3.win32.isAbsolute(managedId)) {
3500
+ if (managedId.includes("/") || managedId.includes("\\") || managedId.includes("..") || path.isAbsolute(managedId) || path.win32.isAbsolute(managedId)) {
6659
3501
  throw new SessionArtifactValidationError(
6660
3502
  "managedId must be an opaque managed resource id",
6661
3503
  "managedId"
@@ -6689,21 +3531,21 @@ function hasUnsafeDisplayPayload(value) {
6689
3531
  __name(hasUnsafeDisplayPayload, "hasUnsafeDisplayPayload");
6690
3532
  function normalizeWorkspacePath(raw, workspaceCwd) {
6691
3533
  const trimmed = normalizeString(raw, "workspacePath", 500, true);
6692
- if (path3.isAbsolute(trimmed)) {
3534
+ if (path.isAbsolute(trimmed)) {
6693
3535
  throw new SessionArtifactValidationError(
6694
3536
  "workspacePath must be relative to the workspace",
6695
3537
  "workspacePath"
6696
3538
  );
6697
3539
  }
6698
- const absolute = path3.resolve(workspaceCwd, trimmed);
6699
- const relative = path3.relative(workspaceCwd, absolute);
3540
+ const absolute = path.resolve(workspaceCwd, trimmed);
3541
+ const relative = path.relative(workspaceCwd, absolute);
6700
3542
  if (!relative || isOutsidePath(relative)) {
6701
3543
  throw new SessionArtifactValidationError(
6702
3544
  "workspacePath must stay inside the workspace",
6703
3545
  "workspacePath"
6704
3546
  );
6705
3547
  }
6706
- return relative.split(path3.sep).join("/");
3548
+ return relative.split(path.sep).join("/");
6707
3549
  }
6708
3550
  __name(normalizeWorkspacePath, "normalizeWorkspacePath");
6709
3551
  function normalizeArtifactUrl(raw, allowFile) {
@@ -6904,7 +3746,7 @@ __name(normalizeSizeBytes, "normalizeSizeBytes");
6904
3746
  function inferKind(input) {
6905
3747
  if (input.storage === "published") return "html";
6906
3748
  if (input.url) return "link";
6907
- const ext = input.workspacePath ? path3.extname(input.workspacePath).toLowerCase() : "";
3749
+ const ext = input.workspacePath ? path.extname(input.workspacePath).toLowerCase() : "";
6908
3750
  if (ext === ".html" || ext === ".htm") return "html";
6909
3751
  if ([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"].includes(ext)) {
6910
3752
  return "image";
@@ -6919,15 +3761,15 @@ function inferKind(input) {
6919
3761
  __name(inferKind, "inferKind");
6920
3762
  async function getWorkspaceStatus(workspacePath, realWorkspaceCwd, expected, options = { hashContent: true }) {
6921
3763
  const realWorkspace = await realWorkspaceCwd;
6922
- const absolutePath = path3.resolve(realWorkspace, workspacePath);
3764
+ const absolutePath = path.resolve(realWorkspace, workspacePath);
6923
3765
  try {
6924
- const realPath = await fs3.realpath(absolutePath);
6925
- const relative = path3.relative(realWorkspace, realPath);
3766
+ const realPath = await fs.realpath(absolutePath);
3767
+ const relative = path.relative(realWorkspace, realPath);
6926
3768
  if (!relative || isOutsidePath(relative)) {
6927
3769
  return { status: "missing", escaped: true };
6928
3770
  }
6929
- const preOpenStat = await fs3.lstat(realPath);
6930
- const handle = await fs3.open(
3771
+ const preOpenStat = await fs.lstat(realPath);
3772
+ const handle = await fs.open(
6931
3773
  realPath,
6932
3774
  fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW
6933
3775
  );
@@ -7039,14 +3881,14 @@ function isWorkspaceContentChanged(artifact, status) {
7039
3881
  __name(isWorkspaceContentChanged, "isWorkspaceContentChanged");
7040
3882
  async function danglingSymlinkEscapesWorkspace(absolutePath, realWorkspace) {
7041
3883
  try {
7042
- const stat = await fs3.lstat(absolutePath);
3884
+ const stat = await fs.lstat(absolutePath);
7043
3885
  if (!stat.isSymbolicLink()) {
7044
3886
  return false;
7045
3887
  }
7046
- const target = await fs3.readlink(absolutePath);
7047
- const parent = await fs3.realpath(path3.dirname(absolutePath));
7048
- const targetPath = path3.resolve(parent, target);
7049
- return isOutsidePath(path3.relative(realWorkspace, targetPath));
3888
+ const target = await fs.readlink(absolutePath);
3889
+ const parent = await fs.realpath(path.dirname(absolutePath));
3890
+ const targetPath = path.resolve(parent, target);
3891
+ return isOutsidePath(path.relative(realWorkspace, targetPath));
7050
3892
  } catch (error) {
7051
3893
  if (isNotFoundError(error)) {
7052
3894
  return false;
@@ -7081,7 +3923,7 @@ function isNoFollowSymlinkError(error) {
7081
3923
  }
7082
3924
  __name(isNoFollowSymlinkError, "isNoFollowSymlinkError");
7083
3925
  function isOutsidePath(relative) {
7084
- return relative === ".." || relative.startsWith(`..${path3.sep}`) || path3.isAbsolute(relative);
3926
+ return relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative);
7085
3927
  }
7086
3928
  __name(isOutsidePath, "isOutsidePath");
7087
3929
 
@@ -7124,32 +3966,45 @@ var KNOWN_SESSION_UPDATE_TYPES = /* @__PURE__ */ new Set([
7124
3966
  "session_info_update",
7125
3967
  "usage_update"
7126
3968
  ]);
7127
- function isRecord3(value) {
3969
+ function isRecord2(value) {
7128
3970
  return typeof value === "object" && value !== null && !Array.isArray(value);
7129
3971
  }
7130
- __name(isRecord3, "isRecord");
3972
+ __name(isRecord2, "isRecord");
7131
3973
  function safeTransportFailureCode(error) {
7132
- if (!isRecord3(error)) return void 0;
3974
+ if (!isRecord2(error)) return void 0;
7133
3975
  const code = error["code"];
7134
3976
  return typeof code === "string" && /^[a-z0-9_.-]{1,64}$/iu.test(code) ? code : void 0;
7135
3977
  }
7136
3978
  __name(safeTransportFailureCode, "safeTransportFailureCode");
7137
- function sessionSourceRequestMeta(sourceType, sourceId) {
3979
+ function sessionSourceRequestMeta(sourceType, sourceId, daemonOwnedStandaloneCreation = false) {
7138
3980
  return sourceType ? {
7139
3981
  [SESSION_SOURCE_META_KEY]: {
7140
3982
  sourceType,
7141
- ...sourceId !== void 0 ? { sourceId } : {}
3983
+ ...sourceId !== void 0 ? { sourceId } : {},
3984
+ ...daemonOwnedStandaloneCreation ? { [DAEMON_OWNED_STANDALONE_CREATION_KEY]: true } : {}
7142
3985
  }
7143
3986
  } : {};
7144
3987
  }
7145
3988
  __name(sessionSourceRequestMeta, "sessionSourceRequestMeta");
3989
+ function sameConversationDirectoryExpectation(left, right) {
3990
+ return left.canonicalSessionId === right.canonicalSessionId && left.root.canonicalPath === right.root.canonicalPath && left.root.device === right.root.device && left.root.inode === right.root.inode && left.child.name === right.child.name && left.child.canonicalPath === right.child.canonicalPath && left.child.device === right.child.device && left.child.inode === right.child.inode;
3991
+ }
3992
+ __name(sameConversationDirectoryExpectation, "sameConversationDirectoryExpectation");
3993
+ function standaloneWorkingDirectoryMissingError() {
3994
+ return new RequestError(
3995
+ -32004,
3996
+ "The standalone working directory is missing.",
3997
+ { errorKind: "working_directory_missing" }
3998
+ );
3999
+ }
4000
+ __name(standaloneWorkingDirectoryMissingError, "standaloneWorkingDirectoryMissingError");
7146
4001
  function getChannelPromptDisplayText(entry, displayText) {
7147
4002
  return entry.sourceType === "channel" && typeof displayText === "string" ? displayText : void 0;
7148
4003
  }
7149
4004
  __name(getChannelPromptDisplayText, "getChannelPromptDisplayText");
7150
4005
  function isDefinitiveAcpRequestError(error) {
7151
4006
  if (error instanceof RequestError) return true;
7152
- if (!isRecord3(error)) return false;
4007
+ if (!isRecord2(error)) return false;
7153
4008
  return typeof error["code"] === "number" && Number.isInteger(error["code"]) && typeof error["message"] === "string";
7154
4009
  }
7155
4010
  __name(isDefinitiveAcpRequestError, "isDefinitiveAcpRequestError");
@@ -7180,7 +4035,7 @@ var MAX_LOG_SAFE_ACP_ERROR_DETAILS_CHARS = 1024;
7180
4035
  var MAX_LOG_SAFE_ACP_ERROR_KIND_CHARS = 128;
7181
4036
  var MAX_LOG_SAFE_ACP_ERROR_HINT_CHARS = 512;
7182
4037
  function logSafeAcpErrorDetails(error) {
7183
- const details = error instanceof Error ? error.message : isRecord3(error) && typeof error["message"] === "string" ? error["message"] : void 0;
4038
+ const details = error instanceof Error ? error.message : isRecord2(error) && typeof error["message"] === "string" ? error["message"] : void 0;
7184
4039
  if (!details) return void 0;
7185
4040
  return {
7186
4041
  details: details.length <= MAX_LOG_SAFE_ACP_ERROR_DETAILS_CHARS ? details : `${details.slice(0, MAX_LOG_SAFE_ACP_ERROR_DETAILS_CHARS)}\u2026`
@@ -7213,7 +4068,7 @@ function logSafeRequestErrorMessage(code) {
7213
4068
  }
7214
4069
  __name(logSafeRequestErrorMessage, "logSafeRequestErrorMessage");
7215
4070
  function logSafeRequestErrorData(data) {
7216
- if (!isRecord3(data) || typeof data["errorKind"] !== "string") {
4071
+ if (!isRecord2(data) || typeof data["errorKind"] !== "string") {
7217
4072
  return void 0;
7218
4073
  }
7219
4074
  const status = data["status"];
@@ -7266,7 +4121,7 @@ function estimateAcpHandlerBytes(value, limitBytes) {
7266
4121
  for (let index = current.length - 1; index >= 0; index--) {
7267
4122
  stack.push(current[index]);
7268
4123
  }
7269
- } else if (isRecord3(current)) {
4124
+ } else if (isRecord2(current)) {
7270
4125
  if (seen.has(current)) return limitBytes + 1;
7271
4126
  seen.add(current);
7272
4127
  const entries = Object.entries(current);
@@ -7418,15 +4273,15 @@ function createOutboundGuardedConnection(connection, transportGuard) {
7418
4273
  }
7419
4274
  __name(createOutboundGuardedConnection, "createOutboundGuardedConnection");
7420
4275
  function getCanonicalModelId(response, fallback) {
7421
- if (!isRecord3(response) || !isRecord3(response["_meta"])) return fallback;
4276
+ if (!isRecord2(response) || !isRecord2(response["_meta"])) return fallback;
7422
4277
  const modelSwitch = response["_meta"]["qwenModelSwitch"];
7423
- if (!isRecord3(modelSwitch)) return fallback;
4278
+ if (!isRecord2(modelSwitch)) return fallback;
7424
4279
  const modelId = modelSwitch["modelId"];
7425
4280
  return typeof modelId === "string" ? modelId : fallback;
7426
4281
  }
7427
4282
  __name(getCanonicalModelId, "getCanonicalModelId");
7428
4283
  function isBulkReplayUpdate(value) {
7429
- if (!isRecord3(value)) return false;
4284
+ if (!isRecord2(value)) return false;
7430
4285
  const updateType = value["sessionUpdate"];
7431
4286
  return typeof updateType === "string" && KNOWN_SESSION_UPDATE_TYPES.has(updateType);
7432
4287
  }
@@ -7438,11 +4293,11 @@ function describeLoadReplayValue(value) {
7438
4293
  }
7439
4294
  __name(describeLoadReplayValue, "describeLoadReplayValue");
7440
4295
  function extractLoadReplayResponse(state) {
7441
- const meta = isRecord3(state._meta) ? state._meta : void 0;
4296
+ const meta = isRecord2(state._meta) ? state._meta : void 0;
7442
4297
  const replay = meta?.[LOAD_REPLAY_META_KEY];
7443
4298
  if (replay === void 0) return { state, updates: [] };
7444
- if (!isRecord3(replay) || replay["v"] !== LOAD_REPLAY_VERSION) {
7445
- const version = isRecord3(replay) ? replay["v"] : void 0;
4299
+ if (!isRecord2(replay) || replay["v"] !== LOAD_REPLAY_VERSION) {
4300
+ const version = isRecord2(replay) ? replay["v"] : void 0;
7446
4301
  throw new Error(
7447
4302
  `Invalid qwen.session.loadReplay payload (type=${describeLoadReplayValue(replay)}, version=${JSON.stringify(version)})`
7448
4303
  );
@@ -7487,7 +4342,7 @@ function extractLoadReplayResponse(state) {
7487
4342
  );
7488
4343
  if (invalidUpdateIndex !== -1) {
7489
4344
  const invalidUpdate = rawUpdates[invalidUpdateIndex];
7490
- const discriminator = isRecord3(invalidUpdate) ? invalidUpdate["sessionUpdate"] : void 0;
4345
+ const discriminator = isRecord2(invalidUpdate) ? invalidUpdate["sessionUpdate"] : void 0;
7491
4346
  throw new Error(
7492
4347
  `Invalid qwen.session.loadReplay update at index ${invalidUpdateIndex} (version=${LOAD_REPLAY_VERSION}, count=${rawUpdates.length}, sessionUpdate=${JSON.stringify(discriminator)})`
7493
4348
  );
@@ -7511,7 +4366,7 @@ function extractLoadReplayResponse(state) {
7511
4366
  }
7512
4367
  __name(extractLoadReplayResponse, "extractLoadReplayResponse");
7513
4368
  function takeRestoreAskUserQuestionHint(state) {
7514
- const meta = isRecord3(state._meta) ? state._meta : void 0;
4369
+ const meta = isRecord2(state._meta) ? state._meta : void 0;
7515
4370
  const hint = meta?.[DAEMON_RESTORE_ASK_USER_QUESTION_META_KEY] === true;
7516
4371
  if (!hint || !meta) return { hint: false, state };
7517
4372
  const nextMeta = { ...meta };
@@ -7651,8 +4506,8 @@ function echoPromptToSessionBus(entry, req, promptId, originatorClientId, displa
7651
4506
  let displayTextPublished = false;
7652
4507
  const serverTimestamp = Date.now();
7653
4508
  const echoPrompt = prompt.slice(0, MAX_ECHO_CONTENT_BLOCKS);
7654
- if (displayText && !echoPrompt.some((part) => isRecord3(part) && part["type"] === "text")) {
7655
- const textPart = prompt.slice(MAX_ECHO_CONTENT_BLOCKS).find((part) => isRecord3(part) && part["type"] === "text");
4509
+ if (displayText && !echoPrompt.some((part) => isRecord2(part) && part["type"] === "text")) {
4510
+ const textPart = prompt.slice(MAX_ECHO_CONTENT_BLOCKS).find((part) => isRecord2(part) && part["type"] === "text");
7656
4511
  if (textPart) echoPrompt[echoPrompt.length - 1] = textPart;
7657
4512
  }
7658
4513
  const blockCount = echoPrompt.length;
@@ -8289,7 +5144,7 @@ function createAcpSessionBridge(opts) {
8289
5144
  );
8290
5145
  }
8291
5146
  }, "releaseFreshSessionReservation");
8292
- const sessionCatalogGeneration = randomUUID3();
5147
+ const sessionCatalogGeneration = randomUUID();
8293
5148
  let sessionCatalogRevision = 0;
8294
5149
  const getSessionCatalogVersion = /* @__PURE__ */ __name(() => ({
8295
5150
  generation: sessionCatalogGeneration,
@@ -8369,7 +5224,7 @@ function createAcpSessionBridge(opts) {
8369
5224
  } else {
8370
5225
  maxPendingPromptsPerSession = maxPendingPromptsRaw;
8371
5226
  }
8372
- if (!path4.isAbsolute(opts.boundWorkspace)) {
5227
+ if (!path2.isAbsolute(opts.boundWorkspace)) {
8373
5228
  throw new TypeError(
8374
5229
  `Invalid boundWorkspace: "${opts.boundWorkspace}". Must be an absolute path.`
8375
5230
  );
@@ -8887,7 +5742,7 @@ function createAcpSessionBridge(opts) {
8887
5742
  return limits;
8888
5743
  }, "journalSessionLimits");
8889
5744
  const unregisterJournalGrowthSessionLimits = journalGrowthPolicy !== void 0 && opts.registerJournalGrowthSessionLimits !== void 0 ? opts.registerJournalGrowthSessionLimits(journalSessionLimits) : void 0;
8890
- const createClientId = /* @__PURE__ */ __name(() => `client_${randomUUID3()}`, "createClientId");
5745
+ const createClientId = /* @__PURE__ */ __name(() => `client_${randomUUID()}`, "createClientId");
8891
5746
  const registerClient = /* @__PURE__ */ __name((entry, requestedClientId) => {
8892
5747
  if (requestedClientId && entry.clientIds.has(requestedClientId)) {
8893
5748
  entry.clientIds.set(
@@ -8946,7 +5801,7 @@ function createAcpSessionBridge(opts) {
8946
5801
  if (inFlightChannelSpawn) return await inFlightChannelSpawn;
8947
5802
  const promise = (async () => {
8948
5803
  const privateParentCapability = randomBytes(32).toString("base64url");
8949
- const acpChannelId = randomUUID3();
5804
+ const acpChannelId = randomUUID();
8950
5805
  const channel = await telemetry.withSpan(
8951
5806
  "channel.spawn",
8952
5807
  {
@@ -9073,7 +5928,8 @@ function createAcpSessionBridge(opts) {
9073
5928
  markSessionCatalogChanged,
9074
5929
  // A Goal turn drains the mid-turn queue but owns no prompt slot, so
9075
5930
  // nothing else would settle what its last drain missed.
9076
- settleMidTurnQueueAfterGoalTurn
5931
+ settleMidTurnQueueAfterGoalTurn,
5932
+ opts.onCreateCurrentSessionScheduledTask
9077
5933
  );
9078
5934
  const rawConnection = new ClientSideConnection(
9079
5935
  () => channel.transportGuard ? createLogSafeAcpClient(client, channel.transportGuard) : client,
@@ -9278,8 +6134,8 @@ function createAcpSessionBridge(opts) {
9278
6134
  );
9279
6135
  }
9280
6136
  }
9281
- const activeWorkCapability = isRecord3(response._meta) ? response._meta[ACTIVE_WORK_HEARTBEAT_META_KEY] : void 0;
9282
- if (isRecord3(activeWorkCapability) && activeWorkCapability["v"] === ACTIVE_WORK_HEARTBEAT_VERSION) {
6137
+ const activeWorkCapability = isRecord2(response._meta) ? response._meta[ACTIVE_WORK_HEARTBEAT_META_KEY] : void 0;
6138
+ if (isRecord2(activeWorkCapability) && activeWorkCapability["v"] === ACTIVE_WORK_HEARTBEAT_VERSION) {
9283
6139
  const advertised = activeWorkCapability["categories"];
9284
6140
  info.activeWork = {
9285
6141
  intervalMs: clampActiveWorkIntervalMs(
@@ -9291,8 +6147,8 @@ function createAcpSessionBridge(opts) {
9291
6147
  seq: 0
9292
6148
  };
9293
6149
  }
9294
- const channelLivenessCapability = isRecord3(response._meta) ? response._meta[CHANNEL_LIVENESS_META_KEY] : void 0;
9295
- channelLivenessNegotiated = isRecord3(channelLivenessCapability) && channelLivenessCapability["v"] === CHANNEL_LIVENESS_VERSION;
6150
+ const channelLivenessCapability = isRecord2(response._meta) ? response._meta[CHANNEL_LIVENESS_META_KEY] : void 0;
6151
+ channelLivenessNegotiated = isRecord2(channelLivenessCapability) && channelLivenessCapability["v"] === CHANNEL_LIVENESS_VERSION;
9296
6152
  try {
9297
6153
  const attributes = getChannelStartupProfileAttributes(
9298
6154
  response,
@@ -9364,7 +6220,7 @@ function createAcpSessionBridge(opts) {
9364
6220
  }
9365
6221
  }
9366
6222
  __name(ensureChannel, "ensureChannel");
9367
- async function doSpawn(modelServiceId, effectiveScope, approvalMode, requestedClientId, onSessionRegistered, parentSessionId, sourceType, sourceId, worktree, branch, requestedSessionId) {
6223
+ async function doSpawn(modelServiceId, effectiveScope, approvalMode, requestedClientId, onSessionRegistered, parentSessionId, sourceType, sourceId, worktree, branch, requestedSessionId, daemonOwnedStandaloneCreation = false, onNewSessionDispatch) {
9368
6224
  const channelPath = channelInfo && !channelInfo.isDying ? "reused" : inFlightChannelSpawn ? "joined" : "spawned_on_request";
9369
6225
  const ci = await telemetry.withSpan(
9370
6226
  "channel.wait",
@@ -9401,24 +6257,28 @@ function createAcpSessionBridge(opts) {
9401
6257
  mcpServers: [],
9402
6258
  ...requestedSessionId || sourceType ? {
9403
6259
  _meta: {
9404
- ...sessionSourceRequestMeta(sourceType, sourceId),
6260
+ ...sessionSourceRequestMeta(
6261
+ sourceType,
6262
+ sourceId,
6263
+ daemonOwnedStandaloneCreation
6264
+ ),
9405
6265
  ...requestedSessionId ? {
9406
6266
  [REQUESTED_SESSION_ID_META_KEY]: requestedSessionId
9407
6267
  } : {}
9408
6268
  }
9409
6269
  } : {}
9410
6270
  });
6271
+ const newSessionRequest = worktree ? {
6272
+ ...request,
6273
+ _meta: {
6274
+ ...isRecord2(request._meta) ? request._meta : {},
6275
+ [WORKTREE_MCP_DEFER_META_KEY]: true
6276
+ }
6277
+ } : request;
6278
+ onNewSessionDispatch?.();
9411
6279
  const response = await withTimeout(
9412
6280
  Promise.race([
9413
- ci.connection.newSession(
9414
- worktree ? {
9415
- ...request,
9416
- _meta: {
9417
- ...isRecord3(request._meta) ? request._meta : {},
9418
- [WORKTREE_MCP_DEFER_META_KEY]: true
9419
- }
9420
- } : request
9421
- ),
6281
+ ci.connection.newSession(newSessionRequest),
9422
6282
  channelUnavailableReject(ci.channel, "during newSession")
9423
6283
  ]),
9424
6284
  initTimeoutMs,
@@ -9514,7 +6374,8 @@ function createAcpSessionBridge(opts) {
9514
6374
  {
9515
6375
  sessionId: entry.sessionId,
9516
6376
  sourceType: entry.sourceType,
9517
- ...entry.sourceId !== void 0 ? { sourceId: entry.sourceId } : {}
6377
+ ...entry.sourceId !== void 0 ? { sourceId: entry.sourceId } : {},
6378
+ ...daemonOwnedStandaloneCreation ? { [DAEMON_OWNED_STANDALONE_CREATION_KEY]: true } : {}
9518
6379
  }
9519
6380
  ),
9520
6381
  initTimeoutMs,
@@ -9618,14 +6479,16 @@ function createAcpSessionBridge(opts) {
9618
6479
  transportClosed
9619
6480
  ]);
9620
6481
  publishModelSwitched(entry, modelId, originatorClientId);
9621
- broadcastWorkspaceEvent({
9622
- type: "settings_changed",
9623
- data: {
9624
- key: "model.name",
9625
- value: getCanonicalModelId(result, modelId)
9626
- },
9627
- ...originatorClientId ? { originatorClientId } : {}
9628
- });
6482
+ if (!isReservedStandaloneSessionSourceType(entry.sourceType)) {
6483
+ broadcastWorkspaceEvent({
6484
+ type: "settings_changed",
6485
+ data: {
6486
+ key: "model.name",
6487
+ value: getCanonicalModelId(result, modelId)
6488
+ },
6489
+ ...originatorClientId ? { originatorClientId } : {}
6490
+ });
6491
+ }
9629
6492
  succeeded = true;
9630
6493
  } catch (err) {
9631
6494
  entry.events.publish({
@@ -9861,7 +6724,7 @@ function createAcpSessionBridge(opts) {
9861
6724
  const cacheWorkspaceMcpDetails = /* @__PURE__ */ __name(async (info, status) => {
9862
6725
  if (!Array.isArray(status.servers)) return;
9863
6726
  const serverNames = status.servers.flatMap(
9864
- (server) => isRecord3(server) && typeof server["name"] === "string" && server["mcpStatus"] === "connected" ? [server["name"]] : []
6727
+ (server) => isRecord2(server) && typeof server["name"] === "string" && server["mcpStatus"] === "connected" ? [server["name"]] : []
9865
6728
  );
9866
6729
  const cacheDetail = /* @__PURE__ */ __name(async (serverName, method, cache) => {
9867
6730
  try {
@@ -10330,6 +7193,12 @@ function createAcpSessionBridge(opts) {
10330
7193
  workspaceCwd,
10331
7194
  persistence: createSessionArtifactPersistence(ci.connection, sessionId)
10332
7195
  }),
7196
+ artifactWorkspaceCwd: workspaceCwd,
7197
+ artifactWorkspaceReady: !isReservedStandaloneSessionSourceType(
7198
+ options.sourceType
7199
+ ),
7200
+ deferredArtifactBatches: [],
7201
+ deferredArtifactInputCount: 0,
10333
7202
  attachments: new SessionAttachmentStore(
10334
7203
  opts.sessionAttachmentsRoot,
10335
7204
  sessionId
@@ -10365,6 +7234,9 @@ function createAcpSessionBridge(opts) {
10365
7234
  activeWorkCloseInFlight: false,
10366
7235
  retryAllowed: false
10367
7236
  };
7237
+ if (isReservedStandaloneSessionSourceType(options.sourceType)) {
7238
+ entry.prepareArtifactWorkspace = () => prepareStandaloneArtifactWorkspace(entry);
7239
+ }
10368
7240
  ci.sessionIds.add(entry.sessionId);
10369
7241
  byId.set(entry.sessionId, entry);
10370
7242
  ci.client.clearAbandonedRestoreFence(entry.sessionId);
@@ -10381,6 +7253,40 @@ function createAcpSessionBridge(opts) {
10381
7253
  }
10382
7254
  return entry;
10383
7255
  }, "createSessionEntry");
7256
+ const prepareStandaloneArtifactWorkspace = /* @__PURE__ */ __name(async (entry) => {
7257
+ if (!entry.artifactWorkspaceReady) {
7258
+ throw standaloneWorkingDirectoryMissingError();
7259
+ }
7260
+ if (entry.artifactWorkspacePreparation) {
7261
+ return entry.artifactWorkspacePreparation;
7262
+ }
7263
+ const owner = channelInfoForEntry(entry);
7264
+ if (!owner) throw standaloneWorkingDirectoryMissingError();
7265
+ const preparation = (async () => {
7266
+ const pending = entry.pendingArtifactRestore;
7267
+ if (pending) {
7268
+ delete entry.pendingArtifactRestore;
7269
+ try {
7270
+ await owner.client.ingestSessionUpdateArtifactsReady(
7271
+ entry,
7272
+ pending.replayUpdates
7273
+ );
7274
+ } catch (error) {
7275
+ entry.pendingArtifactRestore = pending;
7276
+ throw error;
7277
+ }
7278
+ }
7279
+ await owner.client.drainDeferredSessionArtifacts(entry);
7280
+ })();
7281
+ entry.artifactWorkspacePreparation = preparation;
7282
+ try {
7283
+ await preparation;
7284
+ } finally {
7285
+ if (entry.artifactWorkspacePreparation === preparation) {
7286
+ entry.artifactWorkspacePreparation = void 0;
7287
+ }
7288
+ }
7289
+ }, "prepareStandaloneArtifactWorkspace");
10384
7290
  const publishArtifactChanges = /* @__PURE__ */ __name((entry, changes, originatorClientId) => {
10385
7291
  for (const change of changes) {
10386
7292
  entry.events.publish({
@@ -10540,7 +7446,7 @@ function createAcpSessionBridge(opts) {
10540
7446
  const warnings = [];
10541
7447
  const snapshot = normalizeSnapshotPayload(candidate, warnings);
10542
7448
  if (!snapshot) return void 0;
10543
- const snapshotWarnings = isRecord3(candidate) && Array.isArray(candidate["warnings"]) ? candidate["warnings"].filter(
7449
+ const snapshotWarnings = isRecord2(candidate) && Array.isArray(candidate["warnings"]) ? candidate["warnings"].filter(
10544
7450
  (warning) => typeof warning === "string" && warning.length <= 1e3
10545
7451
  ).slice(-500) : [];
10546
7452
  return {
@@ -10672,7 +7578,7 @@ function createAcpSessionBridge(opts) {
10672
7578
  const hasMarker = events.some((e) => e.type === "history_truncated");
10673
7579
  if (!hasMarker) return void 0;
10674
7580
  const hasRecordId = events.some(
10675
- (e) => transcriptEventRecordId(e) !== void 0 || e.type === "history_truncated" && isRecord3(e.data) && typeof e.data["recordId"] === "string"
7581
+ (e) => transcriptEventRecordId(e) !== void 0 || e.type === "history_truncated" && isRecord2(e.data) && typeof e.data["recordId"] === "string"
10676
7582
  );
10677
7583
  if (hasRecordId) return void 0;
10678
7584
  try {
@@ -10726,6 +7632,13 @@ function createAcpSessionBridge(opts) {
10726
7632
  if (shuttingDown) {
10727
7633
  throw new Error("AcpSessionBridge is shutting down");
10728
7634
  }
7635
+ const daemonOwnedStandaloneRestore = options.daemonOwnedStandaloneRestore === true;
7636
+ if (isReservedStandaloneSessionSourceType(req.sourceType) && !daemonOwnedStandaloneRestore) {
7637
+ throw new InvalidSessionMetadataError(
7638
+ "sourceType",
7639
+ "`standalone` is reserved for daemon-owned session restore"
7640
+ );
7641
+ }
10729
7642
  const workspaceKey = resolveWorkspaceKey(req.workspaceCwd);
10730
7643
  if (req.approvalMode !== void 0 && !KNOWN_APPROVAL_MODES.has(req.approvalMode)) {
10731
7644
  throw new Error(
@@ -10896,8 +7809,8 @@ function createAcpSessionBridge(opts) {
10896
7809
  releaseFreshSessionReservation(admission);
10897
7810
  }, "releaseAdmissionOnce");
10898
7811
  let resolveSettlement;
10899
- const settlementPromise = new Promise((resolve2) => {
10900
- resolveSettlement = resolve2;
7812
+ const settlementPromise = new Promise((resolve) => {
7813
+ resolveSettlement = resolve;
10901
7814
  });
10902
7815
  const restoreLifecycle = {
10903
7816
  phase: "active"
@@ -11071,7 +7984,11 @@ function createAcpSessionBridge(opts) {
11071
7984
  // same reason.
11072
7985
  mcpServers: [],
11073
7986
  _meta: {
11074
- ...sessionSourceRequestMeta(req.sourceType, req.sourceId),
7987
+ ...sessionSourceRequestMeta(
7988
+ req.sourceType,
7989
+ req.sourceId,
7990
+ daemonOwnedStandaloneRestore
7991
+ ),
11075
7992
  // Decline decisions known before the child RPC: keep the
11076
7993
  // child's replay finalize-skip aligned with the re-hang.
11077
7994
  ...opts.restoreAskUserQuestion === true && (req.clientId === void 0 || options.suppressRestorePrompt === true) ? {
@@ -11093,7 +8010,11 @@ function createAcpSessionBridge(opts) {
11093
8010
  cwd: workspaceKey,
11094
8011
  mcpServers: [],
11095
8012
  _meta: {
11096
- ...sessionSourceRequestMeta(req.sourceType, req.sourceId),
8013
+ ...sessionSourceRequestMeta(
8014
+ req.sourceType,
8015
+ req.sourceId,
8016
+ daemonOwnedStandaloneRestore
8017
+ ),
11097
8018
  ...opts.restoreAskUserQuestion === true && (req.clientId === void 0 || options.suppressRestorePrompt === true) ? {
11098
8019
  [DAEMON_SUPPRESS_RESTORE_ASK_USER_QUESTION_META_KEY]: true
11099
8020
  } : {}
@@ -11105,7 +8026,7 @@ function createAcpSessionBridge(opts) {
11105
8026
  }
11106
8027
  );
11107
8028
  const observedRestore = Promise.race([rawRestore, transportClosed]);
11108
- state = await new Promise((resolve2, reject) => {
8029
+ state = await new Promise((resolve, reject) => {
11109
8030
  const timer = setTimeout(() => {
11110
8031
  if (restoreLifecycle.phase !== "active") return;
11111
8032
  restoreLifecycle.phase = "abandoned";
@@ -11147,7 +8068,7 @@ function createAcpSessionBridge(opts) {
11147
8068
  (value) => {
11148
8069
  if (restoreLifecycle.phase === "active") {
11149
8070
  clearTimeout(timer);
11150
- resolve2(value);
8071
+ resolve(value);
11151
8072
  return;
11152
8073
  }
11153
8074
  void settleAbandonedRestore(restoreChannel, "success");
@@ -11292,9 +8213,29 @@ function createAcpSessionBridge(opts) {
11292
8213
  entry.restoreHistoryAnchorRecordId = replayAnchorRecordId;
11293
8214
  }
11294
8215
  seedSnapshotCaches(entry, publicState);
11295
- const artifactRestoreWarnings = await entry.artifacts.restore(
11296
- restoredArtifactSnapshot
11297
- );
8216
+ const deferArtifactWorkspace = isReservedStandaloneSessionSourceType(
8217
+ entry.sourceType
8218
+ );
8219
+ const artifactRestoreWarnings = [];
8220
+ if (deferArtifactWorkspace) {
8221
+ artifactRestoreWarnings.push(
8222
+ ...await entry.artifacts.restore(restoredArtifactSnapshot, {
8223
+ workspaceAccess: "metadata-only"
8224
+ })
8225
+ );
8226
+ const artifactRestoreFailed2 = artifactRestoreWarnings.some(
8227
+ (warning) => isArtifactRestoreFailureWarning(warning)
8228
+ );
8229
+ entry.pendingArtifactRestore = {
8230
+ ...restoredArtifactSnapshot !== void 0 ? { snapshot: restoredArtifactSnapshot } : {},
8231
+ replayUpdates: restoredArtifactSnapshot === void 0 || artifactRestoreFailed2 ? replayUpdates : [],
8232
+ warnings: artifactRestoreWarnings
8233
+ };
8234
+ } else {
8235
+ artifactRestoreWarnings.push(
8236
+ ...await entry.artifacts.restore(restoredArtifactSnapshot)
8237
+ );
8238
+ }
11298
8239
  for (const warning of artifactRestoreWarnings) {
11299
8240
  writeStderrLine(
11300
8241
  `[artifacts] session=${entry.sessionId} action=restore_warning warning=${JSON.stringify(
@@ -11307,7 +8248,7 @@ function createAcpSessionBridge(opts) {
11307
8248
  );
11308
8249
  if (replayUpdates.length > 0) {
11309
8250
  await ci.client.seedSessionUpdates(entry, replayUpdates, {
11310
- ingestArtifacts: restoredArtifactSnapshot === void 0 || artifactRestoreFailed
8251
+ ingestArtifacts: !deferArtifactWorkspace && (restoredArtifactSnapshot === void 0 || artifactRestoreFailed)
11311
8252
  });
11312
8253
  if (historyPageSize !== void 0 && entry.events.snapshotReplay()?.compactedTurns.some((event) => event.type === "history_truncated")) {
11313
8254
  entry.restoreHistoryHasMore = true;
@@ -11338,7 +8279,7 @@ function createAcpSessionBridge(opts) {
11338
8279
  ...entry.sourceType ? { sourceType: entry.sourceType } : {},
11339
8280
  ...entry.sourceId !== void 0 ? { sourceId: entry.sourceId } : {},
11340
8281
  state: publicState,
11341
- ...artifactRestoreWarnings.length > 0 ? { artifactWarnings: artifactRestoreWarnings } : {},
8282
+ ...deferArtifactWorkspace || artifactRestoreWarnings.length > 0 ? { artifactWarnings: artifactRestoreWarnings } : {},
11342
8283
  hasActivePrompt: restorePromptAdmitted || entry.promptActive || entry.goalTurnActive === true,
11343
8284
  ...replayFieldsFor(entry, action, liveReplayMode)
11344
8285
  };
@@ -11651,6 +8592,7 @@ function createAcpSessionBridge(opts) {
11651
8592
  if (undrained.length === 0) return;
11652
8593
  settleUndrainedMidTurnMessages(entry, undrained);
11653
8594
  }, "settleMidTurnQueueAfterGoalTurn");
8595
+ const trustedStandaloneSpawnRequests = /* @__PURE__ */ new WeakMap();
11654
8596
  const bridgeApi = {
11655
8597
  setLiveScreenContextCaptureHandler(handler) {
11656
8598
  liveScreenContextCaptureHandler = handler;
@@ -11782,11 +8724,42 @@ function createAcpSessionBridge(opts) {
11782
8724
  async resumeSession(req) {
11783
8725
  return restoreSession("resume", req);
11784
8726
  },
8727
+ async spawnStandaloneSession(req) {
8728
+ const spawnRequest = {
8729
+ workspaceCwd: req.workspaceCwd,
8730
+ sessionId: req.sessionId,
8731
+ sessionScope: "thread",
8732
+ sourceType: STANDALONE_SESSION_SOURCE_TYPE,
8733
+ ...req.parentSessionId !== void 0 ? { parentSessionId: req.parentSessionId } : {},
8734
+ ...req.modelServiceId !== void 0 ? { modelServiceId: req.modelServiceId } : {},
8735
+ ...req.approvalMode !== void 0 ? { approvalMode: req.approvalMode } : {}
8736
+ };
8737
+ const state = { dispatched: false };
8738
+ trustedStandaloneSpawnRequests.set(spawnRequest, state);
8739
+ try {
8740
+ return await bridgeApi.spawnOrAttach(spawnRequest);
8741
+ } catch (error) {
8742
+ throw new StandaloneSessionSpawnError(state.dispatched, error);
8743
+ }
8744
+ },
8745
+ async restoreStandaloneSession(action, req) {
8746
+ return restoreSession(
8747
+ action,
8748
+ {
8749
+ ...req,
8750
+ sourceType: STANDALONE_SESSION_SOURCE_TYPE
8751
+ },
8752
+ { daemonOwnedStandaloneRestore: true }
8753
+ );
8754
+ },
11785
8755
  async spawnOrAttach(req) {
11786
8756
  if (shuttingDown) {
11787
8757
  throw new Error("AcpSessionBridge is shutting down");
11788
8758
  }
11789
8759
  const workspaceKey = resolveWorkspaceKey(req.workspaceCwd);
8760
+ const trustedStandaloneSpawn = trustedStandaloneSpawnRequests.get(req);
8761
+ trustedStandaloneSpawnRequests.delete(req);
8762
+ const daemonOwnedStandaloneCreation = trustedStandaloneSpawn !== void 0;
11790
8763
  if (req.sessionScope !== void 0 && req.sessionScope !== "single" && req.sessionScope !== "thread") {
11791
8764
  throw new InvalidSessionScopeError(req.sessionScope);
11792
8765
  }
@@ -11795,6 +8768,12 @@ function createAcpSessionBridge(opts) {
11795
8768
  if ("error" in source) {
11796
8769
  throw new InvalidSessionMetadataError("sourceType", source.error);
11797
8770
  }
8771
+ if (isReservedStandaloneSessionSourceType(source.sourceType) && !daemonOwnedStandaloneCreation) {
8772
+ throw new InvalidSessionMetadataError(
8773
+ "sourceType",
8774
+ "`standalone` is reserved for daemon-owned session creation"
8775
+ );
8776
+ }
11798
8777
  if (req.approvalMode !== void 0 && !KNOWN_APPROVAL_MODES.has(req.approvalMode)) {
11799
8778
  throw new Error(
11800
8779
  `Invalid approvalMode: ${JSON.stringify(req.approvalMode)}`
@@ -11963,9 +8942,13 @@ function createAcpSessionBridge(opts) {
11963
8942
  source.sourceId,
11964
8943
  req.worktree,
11965
8944
  req.branch,
11966
- req.sessionId
8945
+ req.sessionId,
8946
+ daemonOwnedStandaloneCreation,
8947
+ trustedStandaloneSpawn ? () => {
8948
+ trustedStandaloneSpawn.dispatched = true;
8949
+ } : void 0
11967
8950
  );
11968
- const tracker = effectiveScope === "single" ? workspaceKey : `${workspaceKey}#${randomUUID3()}`;
8951
+ const tracker = effectiveScope === "single" ? workspaceKey : `${workspaceKey}#${randomUUID()}`;
11969
8952
  inFlightSpawns.set(tracker, promise);
11970
8953
  try {
11971
8954
  return await promise;
@@ -11995,6 +8978,9 @@ function createAcpSessionBridge(opts) {
11995
8978
  )
11996
8979
  );
11997
8980
  }
8981
+ if (isReservedStandaloneSessionSourceType(entry.sourceType) && entry.managedConversationBinding?.released !== true) {
8982
+ return Promise.reject(standaloneWorkingDirectoryMissingError());
8983
+ }
11998
8984
  if (!Array.isArray(req.prompt)) {
11999
8985
  return Promise.reject(
12000
8986
  RequestError.invalidParams(void 0, "Prompt must be an array")
@@ -12027,7 +9013,7 @@ function createAcpSessionBridge(opts) {
12027
9013
  promptSlotReleased = true;
12028
9014
  entry.pendingPromptCount = Math.max(0, entry.pendingPromptCount - 1);
12029
9015
  }, "releasePromptSlot");
12030
- const promptId = context?.promptId ?? randomUUID3();
9016
+ const promptId = context?.promptId ?? randomUUID();
12031
9017
  const invocationContext = Object.freeze({
12032
9018
  version: 1,
12033
9019
  sessionId,
@@ -12052,7 +9038,7 @@ function createAcpSessionBridge(opts) {
12052
9038
  context?.promptDisplayText
12053
9039
  );
12054
9040
  const pendingText = channelDisplayText === void 0 ? extractPromptText(req.prompt) : channelDisplayText || (req.prompt.some(
12055
- (block) => isRecord3(block) && block["type"] === "image"
9041
+ (block) => isRecord2(block) && block["type"] === "image"
12056
9042
  ) ? "[image]" : "");
12057
9043
  const pendingEntry = {
12058
9044
  promptId,
@@ -12169,6 +9155,9 @@ function createAcpSessionBridge(opts) {
12169
9155
  }
12170
9156
  throw new DOMException("Prompt aborted", "AbortError");
12171
9157
  }
9158
+ if (isReservedStandaloneSessionSourceType(entry.sourceType) && entry.managedConversationBinding?.released !== true) {
9159
+ throw standaloneWorkingDirectoryMissingError();
9160
+ }
12172
9161
  pendingEntry.startedAt = Date.now();
12173
9162
  if (pendingEntry.state === "queued" || isPromotedMidTurn) {
12174
9163
  if (pendingEntry.state === "queued") {
@@ -12539,6 +9528,11 @@ function createAcpSessionBridge(opts) {
12539
9528
  if (!entry) throw new SessionNotFoundError(sessionId);
12540
9529
  return entry.events.epoch;
12541
9530
  },
9531
+ getSessionCurrentCwd(sessionId) {
9532
+ const entry = byId.get(sessionId);
9533
+ if (!entry) throw new SessionNotFoundError(sessionId);
9534
+ return entry.effectiveCwd;
9535
+ },
12542
9536
  getSessionReplaySnapshot(sessionId) {
12543
9537
  const entry = byId.get(sessionId);
12544
9538
  if (!entry) throw new SessionNotFoundError(sessionId);
@@ -12622,10 +9616,22 @@ function createAcpSessionBridge(opts) {
12622
9616
  if (isClosingOrAuthorizingClose(entry)) {
12623
9617
  throw new SessionNotFoundError(sessionId, "The session is closing");
12624
9618
  }
9619
+ if (isReservedStandaloneSessionSourceType(entry.sourceType)) {
9620
+ throw new InvalidSessionMetadataError(
9621
+ "sourceType",
9622
+ "Standalone sessions cannot be branched through the generic session API"
9623
+ );
9624
+ }
12625
9625
  const source = parseSessionSource(req.sourceType, req.sourceId);
12626
9626
  if ("error" in source) {
12627
9627
  throw new InvalidSessionMetadataError("sourceType", source.error);
12628
9628
  }
9629
+ if (isReservedStandaloneSessionSourceType(source.sourceType)) {
9630
+ throw new InvalidSessionMetadataError(
9631
+ "sourceType",
9632
+ "`standalone` is reserved for daemon-owned session creation"
9633
+ );
9634
+ }
12629
9635
  const isSideTask = source.sourceType === "side_task";
12630
9636
  const restoreBranch = isSideTask || req.atRecordId === void 0;
12631
9637
  if (context?.clientId !== void 0) {
@@ -12848,6 +9854,9 @@ function createAcpSessionBridge(opts) {
12848
9854
  if (shuttingDown) throw new Error("AcpSessionBridge is shutting down");
12849
9855
  const entry = byId.get(sessionId);
12850
9856
  if (!entry) throw new SessionNotFoundError(sessionId);
9857
+ if (isReservedStandaloneSessionSourceType(entry.sourceType) && (req.managedRelocation !== "live-conversation" || req.conversationDirectoryExpectation === void 0)) {
9858
+ throw standaloneWorkingDirectoryMissingError();
9859
+ }
12851
9860
  const originatorClientId = resolveTrustedClientId(
12852
9861
  entry,
12853
9862
  context?.clientId
@@ -12862,7 +9871,10 @@ function createAcpSessionBridge(opts) {
12862
9871
  sessionId,
12863
9872
  path: req.path,
12864
9873
  ...req.allowedRoots ? { allowedRoots: req.allowedRoots } : {},
12865
- ...req.managedRelocation ? { managedRelocation: req.managedRelocation } : {}
9874
+ ...req.managedRelocation ? { managedRelocation: req.managedRelocation } : {},
9875
+ ...req.conversationDirectoryExpectation ? {
9876
+ conversationDirectoryExpectation: req.conversationDirectoryExpectation
9877
+ } : {}
12866
9878
  }),
12867
9879
  getTransportClosedReject(entry)
12868
9880
  ]);
@@ -12872,7 +9884,17 @@ function createAcpSessionBridge(opts) {
12872
9884
  `changeSessionCwd: unexpected response shape from agent: ${JSON.stringify(raw)}`
12873
9885
  );
12874
9886
  }
9887
+ if (isReservedStandaloneSessionSourceType(entry.sourceType) && (req.conversationDirectoryExpectation === void 0 || extResult.newCwd !== req.conversationDirectoryExpectation.child.canonicalPath)) {
9888
+ throw standaloneWorkingDirectoryMissingError();
9889
+ }
12875
9890
  entry.effectiveCwd = extResult.newCwd;
9891
+ if (isReservedStandaloneSessionSourceType(entry.sourceType) && req.conversationDirectoryExpectation !== void 0) {
9892
+ entry.artifactWorkspaceReady = false;
9893
+ entry.managedConversationBinding = {
9894
+ expectation: req.conversationDirectoryExpectation,
9895
+ released: false
9896
+ };
9897
+ }
12876
9898
  if (extResult.previousCwd !== extResult.newCwd) {
12877
9899
  entry.events.publish({
12878
9900
  type: "session_cwd_changed",
@@ -12904,6 +9926,108 @@ function createAcpSessionBridge(opts) {
12904
9926
  );
12905
9927
  return { sessionId, ...result };
12906
9928
  },
9929
+ async commitManagedConversationBinding(sessionId, expectation) {
9930
+ let entry = byId.get(sessionId);
9931
+ if (!entry) throw standaloneWorkingDirectoryMissingError();
9932
+ await entry.cwdChangeQueue;
9933
+ if (byId.get(sessionId) !== entry) {
9934
+ throw standaloneWorkingDirectoryMissingError();
9935
+ }
9936
+ const binding = entry.managedConversationBinding;
9937
+ if (!isReservedStandaloneSessionSourceType(entry.sourceType) || !binding || !sameConversationDirectoryExpectation(binding.expectation, expectation)) {
9938
+ throw standaloneWorkingDirectoryMissingError();
9939
+ }
9940
+ const response = await withTimeout(
9941
+ Promise.race([
9942
+ entry.connection.extMethod(
9943
+ SERVE_CONTROL_EXT_METHODS.sessionManagedConversationBindingCommit,
9944
+ {
9945
+ sessionId,
9946
+ conversationDirectoryExpectation: expectation
9947
+ }
9948
+ ),
9949
+ getTransportClosedReject(entry)
9950
+ ]),
9951
+ initTimeoutMs,
9952
+ "commitManagedConversationBinding"
9953
+ );
9954
+ if (response?.committed !== true) {
9955
+ throw new Error(
9956
+ "commitManagedConversationBinding returned an invalid acknowledgement"
9957
+ );
9958
+ }
9959
+ entry = byId.get(sessionId);
9960
+ if (!entry || entry.managedConversationBinding !== binding || !sameConversationDirectoryExpectation(binding.expectation, expectation)) {
9961
+ throw standaloneWorkingDirectoryMissingError();
9962
+ }
9963
+ if (entry.artifactWorkspaceCwd !== expectation.child.canonicalPath) {
9964
+ const artifacts = new SessionArtifactStore({
9965
+ sessionId,
9966
+ workspaceCwd: expectation.child.canonicalPath,
9967
+ persistence: createSessionArtifactPersistence(
9968
+ entry.connection,
9969
+ sessionId
9970
+ )
9971
+ });
9972
+ const pending = entry.pendingArtifactRestore;
9973
+ if (pending) {
9974
+ const warnings = await artifacts.restore(pending.snapshot, {
9975
+ workspaceAccess: "metadata-only"
9976
+ });
9977
+ for (const warning of warnings) {
9978
+ if (!pending.warnings.includes(warning)) {
9979
+ pending.warnings.push(warning);
9980
+ writeStderrLine(
9981
+ `[artifacts] session=${entry.sessionId} action=restore_warning warning=${JSON.stringify(
9982
+ warning
9983
+ )}`
9984
+ );
9985
+ }
9986
+ }
9987
+ }
9988
+ entry.artifacts = artifacts;
9989
+ entry.artifactWorkspaceCwd = expectation.child.canonicalPath;
9990
+ } else {
9991
+ entry.artifacts.resetWorkspaceResolutionCache();
9992
+ }
9993
+ },
9994
+ async releaseManagedConversationBinding(sessionId, expectation) {
9995
+ let entry = byId.get(sessionId);
9996
+ if (!entry) throw standaloneWorkingDirectoryMissingError();
9997
+ await entry.cwdChangeQueue;
9998
+ if (byId.get(sessionId) !== entry) {
9999
+ throw standaloneWorkingDirectoryMissingError();
10000
+ }
10001
+ const binding = entry.managedConversationBinding;
10002
+ if (!isReservedStandaloneSessionSourceType(entry.sourceType) || !binding || !sameConversationDirectoryExpectation(binding.expectation, expectation)) {
10003
+ throw standaloneWorkingDirectoryMissingError();
10004
+ }
10005
+ const response = await withTimeout(
10006
+ Promise.race([
10007
+ entry.connection.extMethod(
10008
+ SERVE_CONTROL_EXT_METHODS.sessionManagedConversationBindingRelease,
10009
+ {
10010
+ sessionId,
10011
+ conversationDirectoryExpectation: expectation
10012
+ }
10013
+ ),
10014
+ getTransportClosedReject(entry)
10015
+ ]),
10016
+ initTimeoutMs,
10017
+ "releaseManagedConversationBinding"
10018
+ );
10019
+ if (response?.released !== true) {
10020
+ throw new Error(
10021
+ "releaseManagedConversationBinding returned an invalid acknowledgement"
10022
+ );
10023
+ }
10024
+ entry = byId.get(sessionId);
10025
+ if (!entry || entry.managedConversationBinding !== binding || !sameConversationDirectoryExpectation(binding.expectation, expectation)) {
10026
+ throw standaloneWorkingDirectoryMissingError();
10027
+ }
10028
+ binding.released = true;
10029
+ entry.artifactWorkspaceReady = true;
10030
+ },
12907
10031
  setSessionWorktree(sessionId, worktree) {
12908
10032
  const entry = byId.get(sessionId);
12909
10033
  if (entry) {
@@ -12914,6 +10038,24 @@ function createAcpSessionBridge(opts) {
12914
10038
  async closeSession(sessionId, context, closeOpts) {
12915
10039
  return closeSessionImpl(sessionId, context, closeOpts);
12916
10040
  },
10041
+ async ensureDefaultSessionPersisted(sessionId) {
10042
+ const entry = byId.get(sessionId);
10043
+ if (!entry) throw new SessionNotFoundError(sessionId);
10044
+ const result = await withTimeout(
10045
+ Promise.race([
10046
+ entry.connection.extMethod(SERVE_CONTROL_EXT_METHODS.sessionSource, {
10047
+ sessionId,
10048
+ sourceType: "default"
10049
+ }),
10050
+ getTransportClosedReject(entry)
10051
+ ]),
10052
+ initTimeoutMs,
10053
+ "ensureDefaultSessionPersisted"
10054
+ );
10055
+ if (result?.persisted !== true) {
10056
+ throw new Error(`Session '${sessionId}' could not be persisted`);
10057
+ }
10058
+ },
12917
10059
  updateSessionMetadata(sessionId, metadata, context) {
12918
10060
  const entry = byId.get(sessionId);
12919
10061
  if (!entry) throw new SessionNotFoundError(sessionId);
@@ -13021,13 +10163,21 @@ function createAcpSessionBridge(opts) {
13021
10163
  async getSessionArtifacts(sessionId, context) {
13022
10164
  const entry = byId.get(sessionId);
13023
10165
  if (!entry) throw new SessionNotFoundError(sessionId);
10166
+ if (isReservedStandaloneSessionSourceType(entry.sourceType) && entry.managedConversationBinding?.released !== true) {
10167
+ throw standaloneWorkingDirectoryMissingError();
10168
+ }
13024
10169
  resolveTrustedClientId(entry, context?.clientId);
10170
+ await entry.prepareArtifactWorkspace?.();
13025
10171
  return entry.artifacts.list();
13026
10172
  },
13027
10173
  async addSessionArtifact(sessionId, artifact, context) {
13028
10174
  const entry = byId.get(sessionId);
13029
10175
  if (!entry) throw new SessionNotFoundError(sessionId);
10176
+ if (isReservedStandaloneSessionSourceType(entry.sourceType) && entry.managedConversationBinding?.released !== true) {
10177
+ throw standaloneWorkingDirectoryMissingError();
10178
+ }
13030
10179
  const clientId = resolveTrustedClientId(entry, context?.clientId);
10180
+ await entry.prepareArtifactWorkspace?.();
13031
10181
  const input = makeClientArtifactInput(artifact, clientId);
13032
10182
  const result = await entry.artifacts.upsertMany([input], {
13033
10183
  validationStrict: true,
@@ -13040,14 +10190,18 @@ function createAcpSessionBridge(opts) {
13040
10190
  async removeSessionArtifact(sessionId, artifactId, context) {
13041
10191
  const entry = byId.get(sessionId);
13042
10192
  if (!entry) throw new SessionNotFoundError(sessionId);
10193
+ if (isReservedStandaloneSessionSourceType(entry.sourceType) && entry.managedConversationBinding?.released !== true) {
10194
+ throw standaloneWorkingDirectoryMissingError();
10195
+ }
13043
10196
  const clientId = resolveTrustedClientId(entry, context?.clientId);
10197
+ await entry.prepareArtifactWorkspace?.();
13044
10198
  const result = await entry.artifacts.remove(artifactId, { clientId });
13045
10199
  publishArtifactChanges(entry, result.changes, clientId);
13046
10200
  const warnings = [...result.warnings ?? []];
13047
10201
  return warnings.length > 0 ? { ...result, warnings } : result;
13048
10202
  },
13049
10203
  listWorkspaceSessions(workspaceCwd) {
13050
- if (!path4.isAbsolute(workspaceCwd)) return [];
10204
+ if (!path2.isAbsolute(workspaceCwd)) return [];
13051
10205
  const key = workspaceCwd === boundWorkspace ? boundWorkspace : canonicalizeWorkspace(workspaceCwd);
13052
10206
  if (key !== boundWorkspace) return [];
13053
10207
  const out = [];
@@ -13589,14 +10743,16 @@ function createAcpSessionBridge(opts) {
13589
10743
  transportClosed
13590
10744
  ]);
13591
10745
  publishModelSwitched(entry, req.modelId, originatorClientId);
13592
- broadcastWorkspaceEvent({
13593
- type: "settings_changed",
13594
- data: {
13595
- key: "model.name",
13596
- value: getCanonicalModelId(result, req.modelId)
13597
- },
13598
- ...originatorClientId ? { originatorClientId } : {}
13599
- });
10746
+ if (!isReservedStandaloneSessionSourceType(entry.sourceType)) {
10747
+ broadcastWorkspaceEvent({
10748
+ type: "settings_changed",
10749
+ data: {
10750
+ key: "model.name",
10751
+ value: getCanonicalModelId(result, req.modelId)
10752
+ },
10753
+ ...originatorClientId ? { originatorClientId } : {}
10754
+ });
10755
+ }
13600
10756
  succeeded = true;
13601
10757
  return result;
13602
10758
  } finally {
@@ -13717,6 +10873,12 @@ function createAcpSessionBridge(opts) {
13717
10873
  async setSessionApprovalMode(sessionId, mode, opts2, context) {
13718
10874
  const entry = byId.get(sessionId);
13719
10875
  if (!entry) throw new SessionNotFoundError(sessionId);
10876
+ if (opts2.persist && isReservedStandaloneSessionSourceType(entry.sourceType)) {
10877
+ throw new InvalidSessionMetadataError(
10878
+ "persist",
10879
+ "Standalone approval mode changes are session-scoped"
10880
+ );
10881
+ }
13720
10882
  const info = channelInfoForEntry(entry);
13721
10883
  if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
13722
10884
  const originatorClientId = resolveTrustedClientId(
@@ -13764,7 +10926,7 @@ function createAcpSessionBridge(opts) {
13764
10926
  const info = channelInfoForEntry(entry);
13765
10927
  if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
13766
10928
  resolveTrustedClientId(entry, context?.clientId);
13767
- const requestId = randomUUID3();
10929
+ const requestId = randomUUID();
13768
10930
  const queue = new GenerationStreamQueue(
13769
10931
  GENERATION_STREAM_QUEUE_CAPACITY
13770
10932
  );
@@ -14030,7 +11192,7 @@ function createAcpSessionBridge(opts) {
14030
11192
  return { accepted: false };
14031
11193
  }
14032
11194
  }
14033
- const messageId = requestedMessageId ?? randomUUID3();
11195
+ const messageId = requestedMessageId ?? randomUUID();
14034
11196
  if (entry.pendingPromptCount === 0 && entry.goalTurnActive !== true) {
14035
11197
  if (options?.queueOnly || options?.rejectIfIdle) {
14036
11198
  writeStderrLine(
@@ -14300,9 +11462,9 @@ function createAcpSessionBridge(opts) {
14300
11462
  try {
14301
11463
  await Promise.race([
14302
11464
  entry.cwdChangeQueue,
14303
- new Promise((resolve2) => {
14304
- abortResolve = resolve2;
14305
- if (signal?.aborted) return resolve2();
11465
+ new Promise((resolve) => {
11466
+ abortResolve = resolve;
11467
+ if (signal?.aborted) return resolve();
14306
11468
  signal?.addEventListener("abort", onAbort, { once: true });
14307
11469
  })
14308
11470
  ]);
@@ -14677,7 +11839,7 @@ function createAcpSessionBridge(opts) {
14677
11839
  ]);
14678
11840
  },
14679
11841
  generateWorkspaceContent(prompt, signal, _originatorClientId) {
14680
- const requestId = randomUUID3();
11842
+ const requestId = randomUUID();
14681
11843
  const queue = new GenerationStreamQueue(
14682
11844
  GENERATION_STREAM_QUEUE_CAPACITY
14683
11845
  );
@@ -14975,8 +12137,8 @@ function createAcpSessionBridge(opts) {
14975
12137
  const shutdownReason = options?.reason ?? "daemon_shutdown";
14976
12138
  let resolveShutdown;
14977
12139
  let rejectShutdown;
14978
- shutdownPromise = new Promise((resolve2, reject) => {
14979
- resolveShutdown = resolve2;
12140
+ shutdownPromise = new Promise((resolve, reject) => {
12141
+ resolveShutdown = resolve;
14980
12142
  rejectShutdown = reject;
14981
12143
  });
14982
12144
  void (async () => {
@@ -15091,9 +12253,7 @@ var createHttpAcpBridge = createAcpSessionBridge;
15091
12253
  export {
15092
12254
  SessionArtifactValidationError,
15093
12255
  SessionArtifactAuthorizationError,
15094
- SESSION_SOURCE_META_KEY,
15095
12256
  parseSessionSource,
15096
- isA2uiToolMeta,
15097
12257
  extractErrorMessage,
15098
12258
  extractErrorCode,
15099
12259
  classifyTurnErrorKind,