@zubriq/code 0.19.8-2 → 0.19.9-1

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 (243) hide show
  1. package/dist/bundled/qc-helper/docs/features/channels/_meta.ts +1 -0
  2. package/dist/bundled/qc-helper/docs/features/channels/overview.md +23 -12
  3. package/dist/bundled/qc-helper/docs/features/channels/plugins.md +1 -0
  4. package/dist/bundled/qc-helper/docs/features/channels/wecom.md +111 -0
  5. package/dist/bundled/qc-helper/docs/features/hooks.md +36 -34
  6. package/dist/bundled/qc-helper/docs/features/memory.md +1 -1
  7. package/dist/bundled/qc-helper/docs/qwen-serve.md +17 -10
  8. package/dist/bundled/review/SKILL.md +6 -2
  9. package/dist/chunks/{MaxSizedBox-DTQF2MAY.js → MaxSizedBox-W6SAJMJC.js} +13 -11
  10. package/dist/chunks/{StandaloneSessionPicker-XYO5V6IL.js → StandaloneSessionPicker-YBKZZKIA.js} +28 -23
  11. package/dist/chunks/{acpAgent-STMSDYGP.js → acpAgent-SQJQBOTS.js} +110 -80
  12. package/dist/chunks/{agent-C6YBZNSA.js → agent-QU7Q2RZN.js} +8 -8
  13. package/dist/chunks/{agent-headless-65TQRHOY.js → agent-headless-PBHLWHND.js} +8 -8
  14. package/dist/chunks/{anthropicContentGenerator-FZQAEMYK.js → anthropicContentGenerator-52G6ZA3D.js} +31 -4
  15. package/dist/chunks/{artifact-tool-H2TNPNP5.js → artifact-tool-D233YJFH.js} +25 -6
  16. package/dist/chunks/{bridge-GHGQNQRB.js → bridge-LJLLDUMW.js} +17 -14
  17. package/dist/chunks/{ca-HCMB2I54.js → ca-MKD32FQL.js} +26 -1
  18. package/dist/chunks/{channel-worker-supervisor-B7MSMB5K.js → channel-worker-supervisor-NZQUOEFJ.js} +3 -3
  19. package/dist/chunks/{chunk-O3DVJ2JO.js → chunk-27AYMAL6.js} +506 -63
  20. package/dist/chunks/{chunk-AKSRZ5BA.js → chunk-2MDC5WDB.js} +17 -111
  21. package/dist/chunks/{chunk-GKUVMLJH.js → chunk-2PRSBVPI.js} +3 -3
  22. package/dist/chunks/chunk-332PWN27.js +19 -0
  23. package/dist/chunks/{chunk-FVMWEHWU.js → chunk-3B26UVCX.js} +114 -12
  24. package/dist/chunks/{chunk-46IFEQDM.js → chunk-3F22JT4E.js} +1 -1
  25. package/dist/chunks/{chunk-W6RH6KHR.js → chunk-3GPVYALM.js} +4 -2
  26. package/dist/chunks/{chunk-G7ZG7CA6.js → chunk-3UP777ZS.js} +8 -7
  27. package/dist/chunks/{chunk-TYOF23VK.js → chunk-43W4YSO6.js} +1 -12
  28. package/dist/chunks/{chunk-5XZ6FOTY.js → chunk-4IK56VLC.js} +1 -1
  29. package/dist/chunks/{chunk-GSNACUDM.js → chunk-5FAUIGXH.js} +5 -13
  30. package/dist/chunks/{chunk-VEB3NYSO.js → chunk-5MBO76SL.js} +5 -0
  31. package/dist/chunks/{chunk-FACTYQFS.js → chunk-5MRUKUOG.js} +5347 -4132
  32. package/dist/chunks/{chunk-DH7XPKJV.js → chunk-6EUMFVPJ.js} +5 -5
  33. package/dist/chunks/{chunk-ZCMLUE4O.js → chunk-6JL5OVLX.js} +1 -1
  34. package/dist/chunks/{chunk-A6XKBVOM.js → chunk-74E6VVF6.js} +11 -11
  35. package/dist/chunks/chunk-A2X3KLC3.js +3807 -0
  36. package/dist/chunks/{chunk-TJS7CXNX.js → chunk-B6B2MUUF.js} +5 -5
  37. package/dist/chunks/{chunk-OMO3UY57.js → chunk-BN5VYAOZ.js} +681 -1232
  38. package/dist/chunks/{chunk-AXYGF5SX.js → chunk-BYUA6YYS.js} +15 -9
  39. package/dist/chunks/{chunk-NCFY7PWS.js → chunk-CCGOFQV3.js} +51 -2
  40. package/dist/chunks/{chunk-66KNL43D.js → chunk-CD6USWHZ.js} +1 -1
  41. package/dist/chunks/{chunk-LLVYPOQV.js → chunk-CEITMQ62.js} +2 -2
  42. package/dist/chunks/{chunk-PMMJS5AM.js → chunk-EJP6Z7NU.js} +4 -3
  43. package/dist/chunks/{chunk-PY6PG4BL.js → chunk-EZTNYRHH.js} +6 -5
  44. package/dist/chunks/{chunk-PADAEB6D.js → chunk-FAATDXYS.js} +308 -81
  45. package/dist/chunks/{chunk-DNV4FSN5.js → chunk-FBET5XZ2.js} +6 -4
  46. package/dist/chunks/{chunk-3CRBBLJZ.js → chunk-FES4YXJ3.js} +9 -9
  47. package/dist/chunks/{chunk-6N76CE6T.js → chunk-FHTUTVIQ.js} +10 -5
  48. package/dist/chunks/chunk-FII3C4E7.js +168 -0
  49. package/dist/chunks/{chunk-2NL7ED5Q.js → chunk-FV7425LN.js} +22 -1
  50. package/dist/chunks/{chunk-6UBX2BIL.js → chunk-GAI76CFX.js} +4 -4
  51. package/dist/chunks/{chunk-B34OV7K2.js → chunk-GGQQA4JW.js} +13 -0
  52. package/dist/chunks/chunk-GLE2YUPU.js +34 -0
  53. package/dist/chunks/{chunk-X265R5C6.js → chunk-GN3F6PIU.js} +1 -1
  54. package/dist/chunks/{chunk-PYWGOON5.js → chunk-GWUN54UD.js} +1 -1
  55. package/dist/chunks/chunk-H3TELH3H.js +942 -0
  56. package/dist/chunks/{chunk-3SBUWSLI.js → chunk-HD4DZI2L.js} +2 -2
  57. package/dist/chunks/{chunk-HXVVCTID.js → chunk-HPQIVXSP.js} +1 -1
  58. package/dist/chunks/{chunk-WSVZFWN4.js → chunk-IBY3Q3HG.js} +693 -104
  59. package/dist/chunks/{chunk-P42OA4GP.js → chunk-JGR6W4IX.js} +14 -8
  60. package/dist/chunks/chunk-K652V4NH.js +283 -0
  61. package/dist/chunks/{chunk-QUR23Z2Q.js → chunk-KH2CZZDD.js} +2 -2
  62. package/dist/chunks/chunk-LGFAYIKX.js +115 -0
  63. package/dist/chunks/{chunk-FT5ASRN5.js → chunk-LZDW5TDV.js} +5 -5
  64. package/dist/chunks/{chunk-COPJASIO.js → chunk-MA4AZNP7.js} +10 -10
  65. package/dist/chunks/{chunk-4RQIB5UZ.js → chunk-O2YT7VB4.js} +0 -34
  66. package/dist/chunks/{chunk-4KJ2BPIT.js → chunk-OACJLMLD.js} +2 -38
  67. package/dist/chunks/{chunk-UMU5USBC.js → chunk-PCWYMX57.js} +14 -153
  68. package/dist/chunks/{chunk-Z33V6JSK.js → chunk-Q3PQ5VGJ.js} +1 -1
  69. package/dist/chunks/{chunk-GXQXFHYV.js → chunk-QK3I36QG.js} +1 -1
  70. package/dist/chunks/chunk-RA7TR75G.js +8223 -0
  71. package/dist/chunks/{chunk-KSPVDFE4.js → chunk-SAIILPQ3.js} +0 -3
  72. package/dist/chunks/{chunk-65A6UQDZ.js → chunk-SNZPA74J.js} +146 -54
  73. package/dist/chunks/{chunk-AGTU5PQ6.js → chunk-SSP3UT5C.js} +4 -4
  74. package/dist/chunks/{chunk-SWHGVEH5.js → chunk-SUCRVT5L.js} +1 -1
  75. package/dist/chunks/{chunk-SVK2TREP.js → chunk-TRR5KGDN.js} +1 -1
  76. package/dist/chunks/chunk-UIDXQNMV.js +13 -0
  77. package/dist/chunks/{chunk-PIORL4OD.js → chunk-UKV3D7WB.js} +0 -44
  78. package/dist/chunks/chunk-UYNUIGUR.js +167 -0
  79. package/dist/chunks/chunk-V2TYBEWU.js +28 -0
  80. package/dist/chunks/chunk-V35MD7KN.js +13 -0
  81. package/dist/chunks/{chunk-Z3VQSA4A.js → chunk-VR3KCBHH.js} +1 -1
  82. package/dist/chunks/{chunk-VQZSRYKB.js → chunk-W4CTWW7M.js} +423 -61
  83. package/dist/chunks/{chunk-7SZJYZPR.js → chunk-WJAW2O5H.js} +92 -465
  84. package/dist/chunks/{chunk-23TH7TAA.js → chunk-WN4YJJGH.js} +2 -2
  85. package/dist/chunks/chunk-XNXVCNAT.js +88 -0
  86. package/dist/chunks/{chunk-TDGF35EN.js → chunk-XOHNA2S6.js} +1 -1
  87. package/dist/chunks/chunk-XQPMKWSV.js +487 -0
  88. package/dist/chunks/{chunk-VEDR5V7H.js → chunk-XWDHZP7P.js} +8 -8
  89. package/dist/chunks/{chunk-VKWGD42I.js → chunk-YCKKP4QA.js} +10 -10
  90. package/dist/chunks/{chunk-3Y2SRVRF.js → chunk-YJWMOGGZ.js} +3 -3
  91. package/dist/chunks/{chunk-RFIL6REB.js → chunk-YKZIAK7C.js} +119 -29
  92. package/dist/chunks/{chunk-4KMU5RWV.js → chunk-YZDBUH6S.js} +45 -9
  93. package/dist/chunks/chunk-Z3I3QJK3.js +47 -0
  94. package/dist/chunks/{chunk-RTCBBRJK.js → chunk-ZDGPPCTW.js} +20 -8
  95. package/dist/chunks/chunk-ZL5HN75Q.js +61 -0
  96. package/dist/chunks/{chunk-J6GMF2DO.js → chunk-ZL7JCYWM.js} +66 -7
  97. package/dist/chunks/{computer-use-BH6F7LI7.js → computer-use-4B45EG2F.js} +8 -8
  98. package/dist/chunks/{contextCommand-URWDQXBJ.js → contextCommand-GIVRWNTS.js} +12 -11
  99. package/dist/chunks/{cron-create-DDYU4DIR.js → cron-create-JSXEIPVS.js} +1 -1
  100. package/dist/chunks/{cron-delete-DFKOUZWN.js → cron-delete-OIFVEPGU.js} +1 -1
  101. package/dist/chunks/{cron-list-ME5QGAR5.js → cron-list-D4ZX4IOE.js} +1 -1
  102. package/dist/chunks/{daemon-XWG2EGFX.js → daemon-WPAENEBW.js} +71 -3
  103. package/dist/chunks/{daemon-status-provider-CLNN2UY2.js → daemon-status-provider-WBOAMCHE.js} +20 -17
  104. package/dist/chunks/{de-PLLFDERI.js → de-ZFPPQVSF.js} +26 -1
  105. package/dist/chunks/{dist-LPVU3L2M.js → dist-BEXOZXLQ.js} +6 -3792
  106. package/dist/chunks/{dist-7GU63KI5.js → dist-E6UANPIM.js} +1 -1
  107. package/dist/chunks/{dist-VHBP2PIG.js → dist-H2AEJCZ5.js} +2 -2
  108. package/dist/chunks/{dist-3UDX5T4T.js → dist-O2IRTRPP.js} +1 -1
  109. package/dist/chunks/dist-SCFLLYMK.js +3217 -0
  110. package/dist/chunks/{dist-H4HHZYCI.js → dist-Z34QPAQW.js} +2 -2
  111. package/dist/chunks/{earlyInputCapture-SFINSVH2.js → earlyInputCapture-JWMCAJZR.js} +8 -8
  112. package/dist/chunks/{edit-5OJRPFGZ.js → edit-O5FUVHBE.js} +9 -9
  113. package/dist/chunks/{en-AXARKOWO.js → en-N4EPZAQI.js} +31 -2
  114. package/dist/chunks/{enter-worktree-HK54JBSR.js → enter-worktree-LOJFRC3D.js} +8 -8
  115. package/dist/chunks/{enterPlanMode-5GXOH5PQ.js → enterPlanMode-VS3FAUPX.js} +8 -8
  116. package/dist/chunks/environment-WKW5TLSH.js +90 -0
  117. package/dist/chunks/{errors-73SZF7O7.js → errors-LEZXHJMR.js} +10 -10
  118. package/dist/chunks/{exit-worktree-CBX23VFX.js → exit-worktree-RN2NXRMP.js} +8 -8
  119. package/dist/chunks/{exitPlanMode-HADS7AAV.js → exitPlanMode-EPC424I4.js} +8 -8
  120. package/dist/chunks/{fast-path-3QQW3W6W.js → fast-path-UUPAFOSQ.js} +12 -3
  121. package/dist/chunks/{fast-path-settings-RCRXH2JG.js → fast-path-settings-7RIFI3PK.js} +3 -2
  122. package/dist/chunks/{fr-MBMNQSQ6.js → fr-5WWMY53B.js} +30 -1
  123. package/dist/chunks/{gemini-Q4KKJMFH.js → gemini-QCKK4XNU.js} +438 -265
  124. package/dist/chunks/{geminiContentGenerator-SBKWTYFU.js → geminiContentGenerator-DX4SRXJA.js} +1 -1
  125. package/dist/chunks/{glob-ORVXVDJJ.js → glob-4URMJWRX.js} +8 -8
  126. package/dist/chunks/{grep-TMSN6VOF.js → grep-AEQB5KED.js} +8 -8
  127. package/dist/chunks/handleAutoUpdate-Y4YNG34Y.js +65 -0
  128. package/dist/chunks/{initializer-NFO725XQ.js → initializer-NDEBMSOG.js} +17 -13
  129. package/dist/chunks/{ja-HFV5QCHR.js → ja-CUHZOLEJ.js} +30 -1
  130. package/dist/chunks/{list-NP6QTWKD.js → list-VSCBNOWT.js} +17 -15
  131. package/dist/chunks/{loadedSettingsAdapter-5PXZC4ZO.js → loadedSettingsAdapter-FDO752BA.js} +15 -13
  132. package/dist/chunks/{loop-wakeup-ZW5P6VPY.js → loop-wakeup-OHWEMFP5.js} +2 -2
  133. package/dist/chunks/{ls-L654WOAT.js → ls-7MFU2QYW.js} +1 -1
  134. package/dist/chunks/{lsp-M35KE54Y.js → lsp-XHDTPTGV.js} +2 -4
  135. package/dist/chunks/{mcp-D3C75I47.js → mcp-MLPWTHUV.js} +15 -13
  136. package/dist/chunks/{monitor-YZFSN33Z.js → monitor-WCVHIJUJ.js} +16 -11
  137. package/dist/chunks/{nonInteractiveCli-U7ZDVLQD.js → nonInteractiveCli-32HZK274.js} +52 -44
  138. package/dist/chunks/{notebook-edit-LVCCTW65.js → notebook-edit-NPOMPKS4.js} +9 -9
  139. package/dist/chunks/{openaiContentGenerator-G4JHCIGX.js → openaiContentGenerator-6AIBSRQK.js} +6 -6
  140. package/dist/chunks/{pidfile-O37JZBNN.js → pidfile-7Y3QEDSZ.js} +8 -8
  141. package/dist/chunks/{pt-2THT3YHA.js → pt-OLYN42DS.js} +26 -1
  142. package/dist/chunks/{qwenContentGenerator-SPHKYVJF.js → qwenContentGenerator-OUYOAYT4.js} +10 -10
  143. package/dist/chunks/{read-file-MBGSR77H.js → read-file-EESFA24L.js} +3 -3
  144. package/dist/chunks/{record-artifact-PP3LKHTS.js → record-artifact-SQ2OKRJW.js} +3 -3
  145. package/dist/chunks/{ripGrep-WMM4G6QB.js → ripGrep-DLYV7ZZG.js} +8 -8
  146. package/dist/chunks/{ru-PER5I4OI.js → ru-MKM2HFDS.js} +26 -1
  147. package/dist/chunks/{run-qwen-serve-3BOGJBTS.js → run-qwen-serve-WUEHQROF.js} +602 -143
  148. package/dist/chunks/{scheduler-KWPXUSJ4.js → scheduler-SK4Y66V4.js} +8 -8
  149. package/dist/chunks/{send-message-OA6MV4EU.js → send-message-76GT3JMC.js} +1 -1
  150. package/dist/chunks/{serve-6JXYVZRD.js → serve-UCDGVZFI.js} +17 -14
  151. package/dist/chunks/{server-KOSHCREY.js → server-FVGFFVZ4.js} +1394 -328
  152. package/dist/chunks/{session-5RI6JYZ6.js → session-RIGKDMGR.js} +53 -45
  153. package/dist/chunks/{settings-URS74H6Z.js → settings-LVFFGXGE.js} +24 -20
  154. package/dist/chunks/{shell-6VOD5NUH.js → shell-PAWNAFYN.js} +8 -8
  155. package/dist/chunks/{skill-MKIISOW7.js → skill-5YIN3Z6Y.js} +15 -5
  156. package/dist/chunks/{spawnChannel-GM7HIIDH.js → spawnChannel-KIMZHWNR.js} +10 -10
  157. package/dist/chunks/{src-X2S7YA5D.js → src-BIWCD3VX.js} +21 -9
  158. package/dist/chunks/{startInteractiveUI-REEHDN4Z.js → startInteractiveUI-AFXLGUFI.js} +33759 -41620
  159. package/dist/chunks/{task-create-RPFZNB4B.js → task-create-WCHTB435.js} +2 -2
  160. package/dist/chunks/{task-update-5WUG5MVQ.js → task-update-XYWMHJGJ.js} +2 -2
  161. package/dist/chunks/{team-create-MQVOXIHZ.js → team-create-26WOMISV.js} +8 -8
  162. package/dist/chunks/{team-plan-approval-H74NA3K2.js → team-plan-approval-B5XB6UQC.js} +8 -8
  163. package/dist/chunks/{theme-manager-YMC4EJQO.js → theme-manager-4TJ5TEFD.js} +8 -8
  164. package/dist/chunks/{tool-search-VUJGWH7W.js → tool-search-FFU63E6M.js} +6 -23
  165. package/dist/chunks/total-session-admission-JOD7SMPJ.js +72 -0
  166. package/dist/chunks/{trustedFolders-UMMVN4P2.js → trustedFolders-IJ2QHXSC.js} +9 -9
  167. package/dist/chunks/updateCheck-5Z25DCBP.js +65 -0
  168. package/dist/chunks/{validateNonInterActiveAuth-ORA3KCZO.js → validateNonInterActiveAuth-G5EY47DE.js} +49 -41
  169. package/dist/chunks/{version-II46VXNW.js → version-LGO3GOKQ.js} +2 -1
  170. package/dist/chunks/{workflow-KYFPOWKK.js → workflow-ZTK3WT3D.js} +9 -9
  171. package/dist/chunks/{workspace-providers-status-QDQEYCHT.js → workspace-providers-status-YMPTRKW2.js} +18 -15
  172. package/dist/chunks/workspace-registry-NBUL6LBH.js +74 -0
  173. package/dist/chunks/{workspace-service-YBVUOSP7.js → workspace-service-V6G2RCD4.js} +21 -19
  174. package/dist/chunks/{workspace-skills-status-3ASS4VEE.js → workspace-skills-status-ZCW44BCH.js} +16 -14
  175. package/dist/chunks/{write-file-7ZHZO4K5.js → write-file-4FUTAEW6.js} +9 -9
  176. package/dist/chunks/{zh-RS3HZHFH.js → zh-J4F6QWQV.js} +31 -2
  177. package/dist/chunks/{zh-TW-L5GRQ4S4.js → zh-TW-XVDWOLSW.js} +31 -2
  178. package/dist/cli.js +11 -11
  179. package/dist/locales/ca.js +30 -0
  180. package/dist/locales/de.js +30 -0
  181. package/dist/locales/en.js +38 -2
  182. package/dist/locales/fr.js +36 -0
  183. package/dist/locales/ja.js +36 -0
  184. package/dist/locales/pt.js +30 -0
  185. package/dist/locales/ru.js +30 -0
  186. package/dist/locales/zh-TW.js +38 -2
  187. package/dist/locales/zh.js +38 -2
  188. package/dist/package.json +3 -3
  189. package/dist/web-shell/assets/{arc-C6tzvtOa.js → arc-BWyoJpY2.js} +1 -1
  190. package/dist/web-shell/assets/{architectureDiagram-3BPJPVTR-DSYQFGn9.js → architectureDiagram-3BPJPVTR-BT0mGrz2.js} +1 -1
  191. package/dist/web-shell/assets/{blockDiagram-GPEHLZMM-CHjBdXAD.js → blockDiagram-GPEHLZMM-BLgUWvyf.js} +1 -1
  192. package/dist/web-shell/assets/{c4Diagram-AAUBKEIU-BQzK6Yyb.js → c4Diagram-AAUBKEIU-DYx9M7oU.js} +1 -1
  193. package/dist/web-shell/assets/channel-wmPUGHtg.js +1 -0
  194. package/dist/web-shell/assets/{chunk-2J33WTMH-hpP2p3Ij.js → chunk-2J33WTMH-CfMHWbuZ.js} +1 -1
  195. package/dist/web-shell/assets/{chunk-4BX2VUAB-B5KtK1JJ.js → chunk-4BX2VUAB-B1URoqEV.js} +1 -1
  196. package/dist/web-shell/assets/{chunk-55IACEB6-BDv6Mfr2.js → chunk-55IACEB6-DAIaM4QF.js} +1 -1
  197. package/dist/web-shell/assets/{chunk-727SXJPM-BevPWfrX.js → chunk-727SXJPM-B2X34knw.js} +1 -1
  198. package/dist/web-shell/assets/{chunk-AQP2D5EJ-ChDrz2ad.js → chunk-AQP2D5EJ-Cif-zLTQ.js} +1 -1
  199. package/dist/web-shell/assets/{chunk-FMBD7UC4-ClMdVSDs.js → chunk-FMBD7UC4-H18WCXdY.js} +1 -1
  200. package/dist/web-shell/assets/{chunk-ND2GUHAM-BceV8kZS.js → chunk-ND2GUHAM-YFtetHi0.js} +1 -1
  201. package/dist/web-shell/assets/{chunk-QZHKN3VN-DtyK2wwc.js → chunk-QZHKN3VN-By2hiH3r.js} +1 -1
  202. package/dist/web-shell/assets/classDiagram-4FO5ZUOK-BA2ljEjW.js +1 -0
  203. package/dist/web-shell/assets/classDiagram-v2-Q7XG4LA2-BA2ljEjW.js +1 -0
  204. package/dist/web-shell/assets/{cose-bilkent-S5V4N54A-BGMsqwmE.js → cose-bilkent-S5V4N54A-VrcnnLZu.js} +1 -1
  205. package/dist/web-shell/assets/{dagre-BM42HDAG-C-71clpF.js → dagre-BM42HDAG-CGo-IaVV.js} +1 -1
  206. package/dist/web-shell/assets/{diagram-2AECGRRQ-WIzzlkec.js → diagram-2AECGRRQ-Dl4QjzIB.js} +1 -1
  207. package/dist/web-shell/assets/{diagram-5GNKFQAL-CtnhZUP1.js → diagram-5GNKFQAL-qMLmsO0u.js} +1 -1
  208. package/dist/web-shell/assets/{diagram-KO2AKTUF-CS_IUezp.js → diagram-KO2AKTUF-CHlmuxPI.js} +1 -1
  209. package/dist/web-shell/assets/{diagram-LMA3HP47-BQnGRwN9.js → diagram-LMA3HP47-BUwU3GwT.js} +1 -1
  210. package/dist/web-shell/assets/{diagram-OG6HWLK6-By2VIocq.js → diagram-OG6HWLK6-HEhwPDUM.js} +1 -1
  211. package/dist/web-shell/assets/{erDiagram-TEJ5UH35-Dy8WeNkr.js → erDiagram-TEJ5UH35-1n0llBmM.js} +1 -1
  212. package/dist/web-shell/assets/{flowDiagram-I6XJVG4X-Diu_4y0w.js → flowDiagram-I6XJVG4X-BN74uMnt.js} +1 -1
  213. package/dist/web-shell/assets/{ganttDiagram-6RSMTGT7-CH8euujP.js → ganttDiagram-6RSMTGT7-BoOq78i3.js} +1 -1
  214. package/dist/web-shell/assets/{gitGraphDiagram-PVQCEYII-DHiObZq1.js → gitGraphDiagram-PVQCEYII-CN8b00c8.js} +1 -1
  215. package/dist/web-shell/assets/index-CNwoO1Cn.js +783 -0
  216. package/dist/web-shell/assets/index-CWQ0z6kZ.css +5 -0
  217. package/dist/web-shell/assets/{infoDiagram-5YYISTIA-CvC9A6fh.js → infoDiagram-5YYISTIA-CCurnIna.js} +1 -1
  218. package/dist/web-shell/assets/{ishikawaDiagram-YF4QCWOH-BK46kC4E.js → ishikawaDiagram-YF4QCWOH-AwuRqdUP.js} +1 -1
  219. package/dist/web-shell/assets/{journeyDiagram-JHISSGLW-B2TIiKq4.js → journeyDiagram-JHISSGLW-B7IICk-E.js} +1 -1
  220. package/dist/web-shell/assets/{kanban-definition-UN3LZRKU-Bh0WAulO.js → kanban-definition-UN3LZRKU-BYF0OKhx.js} +1 -1
  221. package/dist/web-shell/assets/{linear-Bxe9SHMk.js → linear-GWWguGfD.js} +1 -1
  222. package/dist/web-shell/assets/{mermaid.core-B1Pv6D5B.js → mermaid.core-JqtkxvCl.js} +5 -5
  223. package/dist/web-shell/assets/{mindmap-definition-RKZ34NQL-Bz0joLH8.js → mindmap-definition-RKZ34NQL-B0MmHsZ6.js} +1 -1
  224. package/dist/web-shell/assets/{pieDiagram-4H26LBE5-CnN4x-VS.js → pieDiagram-4H26LBE5-DhImUZLQ.js} +1 -1
  225. package/dist/web-shell/assets/{quadrantDiagram-W4KKPZXB-BYWD4xO4.js → quadrantDiagram-W4KKPZXB-DGeSC3DT.js} +1 -1
  226. package/dist/web-shell/assets/{requirementDiagram-4Y6WPE33-B4MEhI9h.js → requirementDiagram-4Y6WPE33-DkspY6Re.js} +1 -1
  227. package/dist/web-shell/assets/{sankeyDiagram-5OEKKPKP-Dk_FgKLp.js → sankeyDiagram-5OEKKPKP-CaOofS2x.js} +1 -1
  228. package/dist/web-shell/assets/{sequenceDiagram-3UESZ5HK-B4ksiBZQ.js → sequenceDiagram-3UESZ5HK-DFQ_kFnN.js} +1 -1
  229. package/dist/web-shell/assets/{stateDiagram-AJRCARHV-CYf0xSPl.js → stateDiagram-AJRCARHV-D3snw0Tz.js} +1 -1
  230. package/dist/web-shell/assets/stateDiagram-v2-BHNVJYJU-BbSm_9d0.js +1 -0
  231. package/dist/web-shell/assets/{timeline-definition-PNZ67QCA-0CXeWHNY.js → timeline-definition-PNZ67QCA-l4zH8nLM.js} +1 -1
  232. package/dist/web-shell/assets/{vennDiagram-CIIHVFJN-DHoYY9v2.js → vennDiagram-CIIHVFJN-BnRlA98M.js} +1 -1
  233. package/dist/web-shell/assets/{wardley-L42UT6IY-D_65qshr.js → wardley-L42UT6IY-BS0xUdmd.js} +1 -1
  234. package/dist/web-shell/assets/{wardleyDiagram-YWT4CUSO-Cv90x1sd.js → wardleyDiagram-YWT4CUSO-DKGBUWyQ.js} +1 -1
  235. package/dist/web-shell/assets/{xychartDiagram-2RQKCTM6-5FQCYJyh.js → xychartDiagram-2RQKCTM6-DVb2HuOB.js} +1 -1
  236. package/dist/web-shell/index.html +2 -2
  237. package/package.json +3 -2
  238. package/dist/web-shell/assets/channel-V-ZaJrA6.js +0 -1
  239. package/dist/web-shell/assets/classDiagram-4FO5ZUOK-s-H0snGT.js +0 -1
  240. package/dist/web-shell/assets/classDiagram-v2-Q7XG4LA2-s-H0snGT.js +0 -1
  241. package/dist/web-shell/assets/index-DW75jq0K.css +0 -5
  242. package/dist/web-shell/assets/index-tiCo0xbZ.js +0 -779
  243. package/dist/web-shell/assets/stateDiagram-v2-BHNVJYJU-CMtrFRUb.js +0 -1
@@ -11,7 +11,7 @@ import {
11
11
  openQwenAsrRealtimeStream,
12
12
  openVoiceStream,
13
13
  openVoiceStreamWithRetry
14
- } from "./chunk-HXVVCTID.js";
14
+ } from "./chunk-HPQIVXSP.js";
15
15
  import {
16
16
  SetupGithubError,
17
17
  collectSessionData,
@@ -22,61 +22,10 @@ import {
22
22
  toJson,
23
23
  toJsonl,
24
24
  toMarkdown
25
- } from "./chunk-6N76CE6T.js";
25
+ } from "./chunk-FHTUTVIQ.js";
26
+ import "./chunk-2MDC5WDB.js";
27
+ import "./chunk-LGFAYIKX.js";
26
28
  import "./chunk-54YZOP7P.js";
27
- import "./chunk-AKSRZ5BA.js";
28
- import {
29
- createDaemonStatusProvider
30
- } from "./chunk-UMU5USBC.js";
31
- import "./chunk-GKUVMLJH.js";
32
- import "./chunk-KHDZHZMH.js";
33
- import {
34
- createWorkspaceProvidersStatusProvider
35
- } from "./chunk-AXYGF5SX.js";
36
- import {
37
- getAuthTypeFromEnv,
38
- resolveCliGenerationConfig
39
- } from "./chunk-VEDR5V7H.js";
40
- import {
41
- createWorkspaceSkillsStatusProvider
42
- } from "./chunk-3Y2SRVRF.js";
43
- import "./chunk-6IAWPIQ6.js";
44
- import {
45
- EMPTY_WORKSPACE_VOICE_UPDATE_ERROR,
46
- WorkspaceVoiceError,
47
- buildWorkspaceVoiceSettingsWrites,
48
- buildWorkspaceVoiceStatus,
49
- createDaemonWorkspaceService,
50
- hasConfiguredBatchVoiceTranscriptionModel,
51
- transcribeWorkspaceVoiceAudio,
52
- validateWorkspaceVoiceState,
53
- voiceSettingsScopeToWire
54
- } from "./chunk-A6XKBVOM.js";
55
- import {
56
- PermissionRulesValidationError,
57
- buildPermissionSettings,
58
- isPermissionRuleType,
59
- normalizePermissionRules,
60
- readPermissionRuleSet
61
- } from "./chunk-46IFEQDM.js";
62
- import {
63
- assertVoiceBaseUrlNetworkAllowed,
64
- isStreamingVoiceModel,
65
- resolveVoiceStreamConfig,
66
- resolveVoiceTranscriptionConfig,
67
- sanitizeVoiceErrorMessage,
68
- transcribeVoiceAudio
69
- } from "./chunk-PY6PG4BL.js";
70
- import {
71
- isVoiceEnabled,
72
- isVoiceMode,
73
- readVoiceModel
74
- } from "./chunk-SWHGVEH5.js";
75
- import "./chunk-E7KNELIZ.js";
76
- import {
77
- WorkspacePermissionRulesSessionRequiredError,
78
- WorkspaceSettingsPartialPersistError
79
- } from "./chunk-BMFMJINR.js";
80
29
  import {
81
30
  CAPABILITIES_SCHEMA_VERSION,
82
31
  CLIENT_ID_HEADER,
@@ -122,28 +71,84 @@ import {
122
71
  setRateLimiter,
123
72
  unsafeRevealSecret,
124
73
  validateMcpRuntimeServerName
125
- } from "./chunk-RFIL6REB.js";
74
+ } from "./chunk-YKZIAK7C.js";
126
75
  import {
127
76
  isLoopbackBind
128
77
  } from "./chunk-RNBYOUGV.js";
129
78
  import "./chunk-3HX5LZ6R.js";
130
79
  import "./chunk-7RYW5LQV.js";
131
80
  import {
132
- getDialogSettingKeys,
133
- getNestedProperty,
134
- getSettingDefinition,
135
- loadSettings,
136
- validateSettingValue
137
- } from "./chunk-7SZJYZPR.js";
81
+ createDaemonStatusProvider
82
+ } from "./chunk-PCWYMX57.js";
83
+ import "./chunk-2PRSBVPI.js";
84
+ import "./chunk-KHDZHZMH.js";
138
85
  import {
139
- SUPPORTED_LANGUAGES
140
- } from "./chunk-COPJASIO.js";
141
- import "./chunk-TLRYABYP.js";
142
- import "./chunk-4RQIB5UZ.js";
86
+ createWorkspaceProvidersStatusProvider
87
+ } from "./chunk-BYUA6YYS.js";
88
+ import {
89
+ getAuthTypeFromEnv,
90
+ resolveCliGenerationConfig
91
+ } from "./chunk-XWDHZP7P.js";
92
+ import {
93
+ snapshotProcessEnv
94
+ } from "./chunk-FII3C4E7.js";
95
+ import {
96
+ createWorkspaceSkillsStatusProvider
97
+ } from "./chunk-YJWMOGGZ.js";
98
+ import "./chunk-6IAWPIQ6.js";
99
+ import {
100
+ EMPTY_WORKSPACE_VOICE_UPDATE_ERROR,
101
+ WorkspaceVoiceError,
102
+ buildWorkspaceVoiceSettingsWrites,
103
+ buildWorkspaceVoiceStatus,
104
+ createDaemonWorkspaceService,
105
+ hasConfiguredBatchVoiceTranscriptionModel,
106
+ transcribeWorkspaceVoiceAudio,
107
+ validateWorkspaceVoiceState,
108
+ voiceSettingsScopeToWire
109
+ } from "./chunk-74E6VVF6.js";
110
+ import {
111
+ PermissionRulesValidationError,
112
+ buildPermissionSettings,
113
+ isPermissionRuleType,
114
+ normalizePermissionRules,
115
+ readPermissionRuleSet
116
+ } from "./chunk-3F22JT4E.js";
117
+ import {
118
+ assertVoiceBaseUrlNetworkAllowed,
119
+ isStreamingVoiceModel,
120
+ resolveVoiceStreamConfig,
121
+ resolveVoiceTranscriptionConfig,
122
+ sanitizeVoiceErrorMessage,
123
+ transcribeVoiceAudio
124
+ } from "./chunk-EZTNYRHH.js";
125
+ import {
126
+ isVoiceEnabled,
127
+ isVoiceMode,
128
+ readVoiceModel
129
+ } from "./chunk-SUCRVT5L.js";
130
+ import "./chunk-E7KNELIZ.js";
131
+ import {
132
+ WorkspacePermissionRulesSessionRequiredError,
133
+ WorkspaceSettingsPartialPersistError
134
+ } from "./chunk-BMFMJINR.js";
135
+ import {
136
+ createTotalSessionAdmissionController
137
+ } from "./chunk-ZL5HN75Q.js";
138
+ import {
139
+ createSingleWorkspaceRegistry
140
+ } from "./chunk-XNXVCNAT.js";
141
+ import "./chunk-V35MD7KN.js";
143
142
  import {
144
143
  SessionArtifactValidationError,
145
144
  createAcpSessionBridge
146
- } from "./chunk-PADAEB6D.js";
145
+ } from "./chunk-FAATDXYS.js";
146
+ import "./chunk-AGHU5VRK.js";
147
+ import {
148
+ EVENT_SCHEMA_VERSION,
149
+ SubscriberLimitExceededError
150
+ } from "./chunk-3UP777ZS.js";
151
+ import "./chunk-GLE2YUPU.js";
147
152
  import "./chunk-P7M4GA57.js";
148
153
  import {
149
154
  BranchWhilePromptActiveError,
@@ -168,35 +173,49 @@ import {
168
173
  SessionNotFoundError,
169
174
  SessionShellClientRequiredError,
170
175
  SessionShellDisabledError,
176
+ TotalSessionLimitExceededError,
171
177
  WorkspaceInitConflictError,
172
178
  WorkspaceInitPathEscapeError,
173
179
  WorkspaceInitRaceError,
174
180
  WorkspaceInitSymlinkError,
175
181
  WorkspaceMismatchError
176
- } from "./chunk-B34OV7K2.js";
177
- import "./chunk-AGHU5VRK.js";
182
+ } from "./chunk-GGQQA4JW.js";
178
183
  import {
179
184
  MAX_WORKSPACE_PATH_LENGTH,
180
185
  canonicalizeWorkspace,
181
186
  canonicalizeWorkspaces
182
187
  } from "./chunk-ER3BKOLB.js";
183
188
  import {
184
- EVENT_SCHEMA_VERSION,
185
- SubscriberLimitExceededError
186
- } from "./chunk-G7ZG7CA6.js";
189
+ QWEN_SERVER_TOKEN_ENV
190
+ } from "./chunk-S6RLAIUR.js";
187
191
  import "./chunk-VRDMOSQS.js";
188
- import "./chunk-PMMJS5AM.js";
192
+ import {
193
+ getDialogSettingKeys,
194
+ getNestedProperty,
195
+ getSettingDefinition,
196
+ loadSettings,
197
+ validateSettingValue
198
+ } from "./chunk-WJAW2O5H.js";
199
+ import {
200
+ SUPPORTED_LANGUAGES
201
+ } from "./chunk-MA4AZNP7.js";
202
+ import "./chunk-TLRYABYP.js";
203
+ import "./chunk-XQPMKWSV.js";
204
+ import "./chunk-O2YT7VB4.js";
205
+ import "./chunk-Z3I3QJK3.js";
206
+ import "./chunk-EJP6Z7NU.js";
189
207
  import "./chunk-EXPMGZZV.js";
190
208
  import {
191
209
  STATUS_SCHEMA_VERSION,
192
210
  createIdleWorkspaceMemoryStatus,
193
211
  mapDomainErrorToErrorKind
194
- } from "./chunk-Z33V6JSK.js";
212
+ } from "./chunk-Q3PQ5VGJ.js";
195
213
  import "./chunk-HLKG6CZN.js";
196
- import "./chunk-TYOF23VK.js";
214
+ import "./chunk-V2TYBEWU.js";
215
+ import "./chunk-43W4YSO6.js";
197
216
  import {
198
217
  getWorkspaceTrustStatus
199
- } from "./chunk-Z3VQSA4A.js";
218
+ } from "./chunk-VR3KCBHH.js";
200
219
  import {
201
220
  writeStderrLine
202
221
  } from "./chunk-Y6Z2O3WR.js";
@@ -230,18 +249,20 @@ import {
230
249
  encodeTextFileContent,
231
250
  glob,
232
251
  loadIgnoreRules,
233
- loadUsageHistory,
252
+ loadUsageHistoryWithLive,
234
253
  parseInstallSource,
235
254
  redactUrlCredentials,
236
255
  shouldShowStep,
256
+ stripTerminalControlSequences,
237
257
  writeWorkspaceContextFile
238
- } from "./chunk-FACTYQFS.js";
258
+ } from "./chunk-5MRUKUOG.js";
239
259
  import "./chunk-5P5XGNYH.js";
240
260
  import "./chunk-SKAZWEV5.js";
241
261
  import "./chunk-K5PGHDBN.js";
242
262
  import {
243
- MAX_JOBS
244
- } from "./chunk-FVMWEHWU.js";
263
+ MAX_JOBS,
264
+ nextDurableFireMs
265
+ } from "./chunk-3B26UVCX.js";
245
266
  import "./chunk-W7EEOR7D.js";
246
267
  import "./chunk-42XGFRJS.js";
247
268
  import "./chunk-X2XNQQJI.js";
@@ -252,11 +273,12 @@ import {
252
273
  } from "./chunk-2MPVVENX.js";
253
274
  import "./chunk-ZTQ26VBE.js";
254
275
  import {
276
+ appendCronRun,
255
277
  generateCronTaskId,
278
+ getCronFilePath,
256
279
  readCronTasks,
257
- removeCronTasks,
258
280
  updateCronTasks
259
- } from "./chunk-2NL7ED5Q.js";
281
+ } from "./chunk-FV7425LN.js";
260
282
  import "./chunk-MLZQVCF3.js";
261
283
  import "./chunk-LQ7TMOCE.js";
262
284
  import "./chunk-E5A7LHNN.js";
@@ -264,7 +286,7 @@ import {
264
286
  APPROVAL_MODES,
265
287
  SubagentError,
266
288
  SubagentErrorCode
267
- } from "./chunk-VEB3NYSO.js";
289
+ } from "./chunk-5MBO76SL.js";
268
290
  import "./chunk-GO6LNQXT.js";
269
291
  import "./chunk-NQKLOAVJ.js";
270
292
  import "./chunk-WMPVYQ4P.js";
@@ -283,12 +305,12 @@ import {
283
305
  recordDaemonHttpRequest,
284
306
  recordDaemonHttpResponse,
285
307
  withDaemonRequestSpan
286
- } from "./chunk-O3DVJ2JO.js";
308
+ } from "./chunk-27AYMAL6.js";
287
309
  import "./chunk-E6US47KI.js";
288
310
  import "./chunk-UWCTAVOD.js";
289
311
  import "./chunk-OFEVLU4C.js";
290
312
  import "./chunk-OMX7CUOE.js";
291
- import "./chunk-4KJ2BPIT.js";
313
+ import "./chunk-OACJLMLD.js";
292
314
  import "./chunk-SUAKHHK7.js";
293
315
  import "./chunk-FEJ2FZ3U.js";
294
316
  import "./chunk-X3YGAX7V.js";
@@ -305,8 +327,8 @@ import "./chunk-AHNJGLFV.js";
305
327
  import "./chunk-22IFUCVR.js";
306
328
  import "./chunk-7LTB54MK.js";
307
329
  import "./chunk-SIUQ3YYX.js";
308
- import "./chunk-PIORL4OD.js";
309
- import "./chunk-NCFY7PWS.js";
330
+ import "./chunk-UKV3D7WB.js";
331
+ import "./chunk-CCGOFQV3.js";
310
332
  import "./chunk-IRJWHZWU.js";
311
333
  import "./chunk-SYCJMSIJ.js";
312
334
  import "./chunk-55ZMG67I.js";
@@ -345,9 +367,6 @@ import { createHash as createHash3, timingSafeEqual } from "node:crypto";
345
367
  init_esbuild_shims();
346
368
  import path8 from "node:path";
347
369
 
348
- // packages/cli/src/serve/acp-session-bridge.ts
349
- init_esbuild_shims();
350
-
351
370
  // packages/cli/src/serve/fs/errors.ts
352
371
  init_esbuild_shims();
353
372
  var DEFAULT_STATUS_BY_KIND = {
@@ -3247,7 +3266,8 @@ function cloneTask(task) {
3247
3266
  removedEntries: task.result.removedEntries.map((entry) => ({
3248
3267
  ...entry
3249
3268
  })),
3250
- touchedTopics: [...task.result.touchedTopics]
3269
+ touchedTopics: [...task.result.touchedTopics],
3270
+ touchedScopes: [...task.result.touchedScopes]
3251
3271
  } : void 0
3252
3272
  };
3253
3273
  }
@@ -3452,7 +3472,8 @@ var WorkspaceRememberTaskLane = class _WorkspaceRememberTaskLane {
3452
3472
  task.result = {
3453
3473
  summary: result.summary ?? formatWorkspaceMemoryForgetSummary(result.removedEntries.length),
3454
3474
  removedEntries: result.removedEntries,
3455
- touchedTopics: result.touchedTopics
3475
+ touchedTopics: result.touchedTopics,
3476
+ touchedScopes: result.touchedScopes
3456
3477
  };
3457
3478
  task.updatedAt = nowIso();
3458
3479
  } catch (err) {
@@ -3474,7 +3495,7 @@ var WorkspaceRememberTaskLane = class _WorkspaceRememberTaskLane {
3474
3495
  this.publishManagedMemoryChanged({
3475
3496
  source: "workspace_memory_forget",
3476
3497
  taskId: task.taskId,
3477
- touchedScopes: touchedScopesFromTopics(task.result.touchedTopics),
3498
+ touchedScopes: task.result.touchedScopes,
3478
3499
  ...params.originatorClientId ? { originatorClientId: params.originatorClientId } : {}
3479
3500
  });
3480
3501
  }
@@ -4893,6 +4914,67 @@ __name(createSessionOrganizationService, "createSessionOrganizationService");
4893
4914
 
4894
4915
  // packages/cli/src/serve/server/session-archive.ts
4895
4916
  init_esbuild_shims();
4917
+
4918
+ // packages/cli/src/serve/scheduled-task-session-lifecycle.ts
4919
+ init_esbuild_shims();
4920
+ function isBoundTask(task) {
4921
+ return typeof task.sessionId === "string" && task.sessionId.length > 0;
4922
+ }
4923
+ __name(isBoundTask, "isBoundTask");
4924
+ async function disableTasksForSessions(projectRoot, sessionIds) {
4925
+ if (sessionIds.length === 0) return;
4926
+ const targets = new Set(sessionIds);
4927
+ await updateCronTasks(projectRoot, (tasks) => {
4928
+ let changed = false;
4929
+ const next = tasks.map((task) => {
4930
+ if (isBoundTask(task) && targets.has(task.sessionId) && task.enabled !== false) {
4931
+ changed = true;
4932
+ return { ...task, enabled: false, disabledByArchive: true };
4933
+ }
4934
+ return task;
4935
+ });
4936
+ return changed ? next : tasks;
4937
+ });
4938
+ }
4939
+ __name(disableTasksForSessions, "disableTasksForSessions");
4940
+ async function enableTasksForSessions(projectRoot, sessionIds, now = Date.now()) {
4941
+ if (sessionIds.length === 0) return;
4942
+ const targets = new Set(sessionIds);
4943
+ await updateCronTasks(projectRoot, (tasks) => {
4944
+ let changed = false;
4945
+ const next = tasks.map((task) => {
4946
+ if (isBoundTask(task) && targets.has(task.sessionId) && task.enabled === false && task.disabledByArchive === true) {
4947
+ changed = true;
4948
+ const resumed = { ...task, enabled: true };
4949
+ delete resumed.disabledByArchive;
4950
+ const minute = now - now % 6e4;
4951
+ if (resumed.recurring) {
4952
+ resumed.lastFiredAt = minute;
4953
+ } else {
4954
+ resumed.createdAt = now;
4955
+ resumed.lastFiredAt = minute;
4956
+ }
4957
+ return resumed;
4958
+ }
4959
+ return task;
4960
+ });
4961
+ return changed ? next : tasks;
4962
+ });
4963
+ }
4964
+ __name(enableTasksForSessions, "enableTasksForSessions");
4965
+ async function removeTasksForSessions(projectRoot, sessionIds) {
4966
+ if (sessionIds.length === 0) return;
4967
+ const targets = new Set(sessionIds);
4968
+ await updateCronTasks(projectRoot, (tasks) => {
4969
+ const next = tasks.filter(
4970
+ (task) => !isBoundTask(task) || !targets.has(task.sessionId)
4971
+ );
4972
+ return next.length === tasks.length ? tasks : next;
4973
+ });
4974
+ }
4975
+ __name(removeTasksForSessions, "removeTasksForSessions");
4976
+
4977
+ // packages/cli/src/serve/server/session-archive.ts
4896
4978
  var SessionArchiveCoordinator = class {
4897
4979
  static {
4898
4980
  __name(this, "SessionArchiveCoordinator");
@@ -4995,6 +5077,13 @@ async function deleteDaemonSessions(params) {
4995
5077
  }
4996
5078
  })
4997
5079
  );
5080
+ await removeTasksForSessions(service.getProjectRoot(), removed).catch(
5081
+ (err) => {
5082
+ logSessionArchiveWarning(
5083
+ `removeTasksForSessions failed for [${removed.join(", ")}]: ${err instanceof Error ? err.message : String(err)}`
5084
+ );
5085
+ }
5086
+ );
4998
5087
  return { removed, notFound, errors: [...closeErrors, ...removeErrors] };
4999
5088
  }
5000
5089
  __name(deleteDaemonSessions, "deleteDaemonSessions");
@@ -5151,6 +5240,13 @@ async function archiveDaemonSessions(params) {
5151
5240
  notFound,
5152
5241
  errors
5153
5242
  });
5243
+ await disableTasksForSessions(service.getProjectRoot(), archived).catch(
5244
+ (err) => {
5245
+ logSessionArchiveWarning(
5246
+ `disableTasksForSessions failed for [${archived.join(", ")}]: ${err instanceof Error ? err.message : String(err)} \u2014 bound tasks may keep firing until reconciled`
5247
+ );
5248
+ }
5249
+ );
5154
5250
  return { archived, alreadyArchived, notFound, errors };
5155
5251
  }
5156
5252
  __name(archiveDaemonSessions, "archiveDaemonSessions");
@@ -5197,6 +5293,17 @@ async function unarchiveDaemonSessions(params) {
5197
5293
  notFound,
5198
5294
  errors
5199
5295
  });
5296
+ const resumeSessionIds = [.../* @__PURE__ */ new Set([...unarchived, ...alreadyActive])];
5297
+ try {
5298
+ await enableTasksForSessions(service.getProjectRoot(), resumeSessionIds);
5299
+ } catch (err) {
5300
+ logSessionArchiveWarning(
5301
+ `enableTasksForSessions failed for [${resumeSessionIds.join(", ")}]: ${err instanceof Error ? err.message : String(err)}`
5302
+ );
5303
+ for (const sessionId of resumeSessionIds) {
5304
+ errors.push({ sessionId, error: err });
5305
+ }
5306
+ }
5200
5307
  return { unarchived, alreadyActive, notFound, errors };
5201
5308
  }
5202
5309
  __name(unarchiveDaemonSessions, "unarchiveDaemonSessions");
@@ -5629,7 +5736,27 @@ function toRpcError(err) {
5629
5736
  case "InvalidClientIdError":
5630
5737
  return { code: RPC.INVALID_PARAMS, message: errMsg(err) };
5631
5738
  case "SessionLimitExceededError":
5632
- return { code: RPC.INTERNAL_ERROR, message: errMsg(err) };
5739
+ return {
5740
+ code: RPC.INTERNAL_ERROR,
5741
+ message: errMsg(err),
5742
+ data: {
5743
+ errorKind: "session_limit_exceeded",
5744
+ limit: err.limit,
5745
+ scope: "workspace",
5746
+ retryable: true
5747
+ }
5748
+ };
5749
+ case "TotalSessionLimitExceededError":
5750
+ return {
5751
+ code: RPC.INTERNAL_ERROR,
5752
+ message: errMsg(err),
5753
+ data: {
5754
+ errorKind: "session_limit_exceeded",
5755
+ limit: err.limit,
5756
+ scope: err.scope,
5757
+ retryable: true
5758
+ }
5759
+ };
5633
5760
  case "PromptQueueFullError": {
5634
5761
  const promptErr = err;
5635
5762
  return {
@@ -9798,10 +9925,10 @@ function isCdpInboundFrame(frame) {
9798
9925
  }
9799
9926
  __name(isCdpInboundFrame, "isCdpInboundFrame");
9800
9927
  var CdpReverseLink = class {
9801
- constructor(sendToExtension, commandTimeoutMs = DEFAULT_COMMAND_TIMEOUT_MS, log) {
9928
+ constructor(sendToExtension, commandTimeoutMs = DEFAULT_COMMAND_TIMEOUT_MS, log2) {
9802
9929
  this.sendToExtension = sendToExtension;
9803
9930
  this.commandTimeoutMs = commandTimeoutMs;
9804
- this.log = log;
9931
+ this.log = log2;
9805
9932
  }
9806
9933
  static {
9807
9934
  __name(this, "CdpReverseLink");
@@ -10243,10 +10370,10 @@ var CdpBrowserEmulator = class {
10243
10370
  var CLOSE_NO_BRIDGE = 1011;
10244
10371
  var CLOSE_NORMAL = 1e3;
10245
10372
  var CDP_WS_HEARTBEAT_MS = 15e3;
10246
- function attachCdpClient(ws, registry, log) {
10373
+ function attachCdpClient(ws, registry, log2) {
10247
10374
  const bridge = registry.getActive();
10248
10375
  if (!bridge) {
10249
- log("qwen serve: /cdp rejected \u2014 no extension bridge connected");
10376
+ log2("qwen serve: /cdp rejected \u2014 no extension bridge connected");
10250
10377
  try {
10251
10378
  ws.close(
10252
10379
  CLOSE_NO_BRIDGE,
@@ -10257,7 +10384,7 @@ function attachCdpClient(ws, registry, log) {
10257
10384
  return;
10258
10385
  }
10259
10386
  if (bridge.cdpBound) {
10260
- log("qwen serve: /cdp rejected \u2014 a puppeteer client is already bound");
10387
+ log2("qwen serve: /cdp rejected \u2014 a puppeteer client is already bound");
10261
10388
  try {
10262
10389
  ws.close(CLOSE_NO_BRIDGE, "A CDP client is already connected");
10263
10390
  } catch {
@@ -10268,19 +10395,19 @@ function attachCdpClient(ws, registry, log) {
10268
10395
  const link = new CdpReverseLink(
10269
10396
  (frame) => bridge.send(frame),
10270
10397
  void 0,
10271
- log
10398
+ log2
10272
10399
  );
10273
10400
  const emulator = new CdpBrowserEmulator({
10274
10401
  reply: /* @__PURE__ */ __name((frame) => {
10275
10402
  safeWsSend(ws, JSON.stringify(frame), "CDP");
10276
10403
  }, "reply"),
10277
10404
  forwardToTab: link.forwardToTab,
10278
- log
10405
+ log: log2
10279
10406
  });
10280
10407
  link.bindEmulator(emulator);
10281
10408
  bridge.routeInbound = (frame) => link.handleInbound(frame);
10282
10409
  link.onDetach = (reason) => {
10283
- log(`qwen serve: /cdp tab detached (${reason}); closing puppeteer socket`);
10410
+ log2(`qwen serve: /cdp tab detached (${reason}); closing puppeteer socket`);
10284
10411
  try {
10285
10412
  ws.close(CLOSE_NORMAL, `tab detached: ${reason}`);
10286
10413
  } catch {
@@ -10291,7 +10418,7 @@ function attachCdpClient(ws, registry, log) {
10291
10418
  const heartbeat = setInterval(() => {
10292
10419
  if (disposed) return;
10293
10420
  if (!heartbeatAlive) {
10294
- log("qwen serve: /cdp heartbeat missed; closing puppeteer socket");
10421
+ log2("qwen serve: /cdp heartbeat missed; closing puppeteer socket");
10295
10422
  dispose("puppeteer /cdp heartbeat missed");
10296
10423
  try {
10297
10424
  ws.close(CLOSE_NORMAL, "cdp heartbeat missed");
@@ -10303,7 +10430,7 @@ function attachCdpClient(ws, registry, log) {
10303
10430
  try {
10304
10431
  ws.ping();
10305
10432
  } catch (err) {
10306
- log(
10433
+ log2(
10307
10434
  `qwen serve: /cdp heartbeat ping failed: ${err instanceof Error ? err.message : String(err)}`
10308
10435
  );
10309
10436
  dispose("puppeteer /cdp heartbeat ping failed");
@@ -10321,11 +10448,11 @@ function attachCdpClient(ws, registry, log) {
10321
10448
  if (notifyExtension && registry.getActive() === bridge) {
10322
10449
  try {
10323
10450
  bridge.send({ type: CDP_FRAME_TYPES.release });
10324
- log(
10451
+ log2(
10325
10452
  `qwen serve: /cdp sent release to extension (puppeteer disconnected: ${reason})`
10326
10453
  );
10327
10454
  } catch (err) {
10328
- log(
10455
+ log2(
10329
10456
  `qwen serve: /cdp release send failed: ${err instanceof Error ? err.message : String(err)}`
10330
10457
  );
10331
10458
  }
@@ -10338,7 +10465,7 @@ function attachCdpClient(ws, registry, log) {
10338
10465
  }
10339
10466
  }, "dispose");
10340
10467
  link.onAttachFailure = (reason) => {
10341
- log(`qwen serve: /cdp attach failed (${reason}); closing puppeteer socket`);
10468
+ log2(`qwen serve: /cdp attach failed (${reason}); closing puppeteer socket`);
10342
10469
  dispose(`cdp_attach failed: ${reason}`, false);
10343
10470
  try {
10344
10471
  ws.close(CLOSE_NO_BRIDGE, "cdp attach failed");
@@ -10349,7 +10476,7 @@ function attachCdpClient(ws, registry, log) {
10349
10476
  heartbeatAlive = true;
10350
10477
  });
10351
10478
  bridge.onExtensionGone = () => {
10352
- log("qwen serve: extension /acp dropped; closing puppeteer /cdp socket");
10479
+ log2("qwen serve: extension /acp dropped; closing puppeteer /cdp socket");
10353
10480
  dispose("extension /acp disconnected", false);
10354
10481
  try {
10355
10482
  ws.close(CLOSE_NORMAL, "extension disconnected");
@@ -10365,19 +10492,19 @@ function attachCdpClient(ws, registry, log) {
10365
10492
  return;
10366
10493
  }
10367
10494
  void emulator.handleFromClient(frame).catch((err) => {
10368
- log(
10495
+ log2(
10369
10496
  `qwen serve: /cdp emulator error: ${err instanceof Error ? err.message : String(err)}`
10370
10497
  );
10371
10498
  });
10372
10499
  });
10373
10500
  ws.on("close", () => dispose("puppeteer /cdp socket closed"));
10374
10501
  ws.on("error", (err) => {
10375
- log(
10502
+ log2(
10376
10503
  `qwen serve: /cdp WS error: ${err instanceof Error ? err.message : String(err)}`
10377
10504
  );
10378
10505
  dispose("puppeteer /cdp socket error");
10379
10506
  });
10380
- log("qwen serve: /cdp puppeteer client bound to extension bridge");
10507
+ log2("qwen serve: /cdp puppeteer client bound to extension bridge");
10381
10508
  }
10382
10509
  __name(attachCdpClient, "attachCdpClient");
10383
10510
 
@@ -10516,7 +10643,7 @@ var MAX_INFLIGHT_MCP_DISPATCH = 8;
10516
10643
  function mountAcpHttp(app, bridge, opts) {
10517
10644
  const enabled = opts.enabled ?? process.env["QWEN_SERVE_ACP_HTTP"] !== "0";
10518
10645
  if (!enabled) return void 0;
10519
- const path13 = opts.path ?? "/acp";
10646
+ const path14 = opts.path ?? "/acp";
10520
10647
  const dispatcherRef = {};
10521
10648
  const registry = new ConnectionRegistry(
10522
10649
  (req, clientId) => {
@@ -10653,7 +10780,7 @@ function mountAcpHttp(app, bridge, opts) {
10653
10780
  opts.archiveCoordinator ?? new SessionArchiveCoordinator()
10654
10781
  );
10655
10782
  dispatcherRef.current = dispatcher;
10656
- app.post(path13, async (req, res) => {
10783
+ app.post(path14, async (req, res) => {
10657
10784
  const ct = req.headers["content-type"];
10658
10785
  if (!ct || !ct.startsWith("application/json")) {
10659
10786
  res.status(415).json({ error: "Content-Type must be application/json" });
@@ -10757,7 +10884,7 @@ function mountAcpHttp(app, bridge, opts) {
10757
10884
  );
10758
10885
  });
10759
10886
  });
10760
- app.get(path13, (req, res) => {
10887
+ app.get(path14, (req, res) => {
10761
10888
  const accept = req.headers["accept"] ?? "";
10762
10889
  if (!accept.includes("text/event-stream")) {
10763
10890
  res.status(406).json({ error: "Accept header must include text/event-stream" });
@@ -10843,7 +10970,7 @@ function mountAcpHttp(app, bridge, opts) {
10843
10970
  onPumpSettled();
10844
10971
  });
10845
10972
  });
10846
- app.delete(path13, (req, res) => {
10973
+ app.delete(path14, (req, res) => {
10847
10974
  const connectionId = headerOf(req, ACP_CONNECTION_HEADER);
10848
10975
  if (!connectionId) {
10849
10976
  res.status(400).json({ error: "Missing Acp-Connection-Id" });
@@ -10906,7 +11033,7 @@ function mountAcpHttp(app, bridge, opts) {
10906
11033
  const extraRoute = opts.extraWsRoutes?.find(
10907
11034
  (route) => route.path === url.pathname
10908
11035
  );
10909
- if (url.pathname !== path13 && !isCdpPath && !extraRoute) {
11036
+ if (url.pathname !== path14 && !isCdpPath && !extraRoute) {
10910
11037
  logReject(`unknown-path ${url.pathname}`);
10911
11038
  socket.destroy();
10912
11039
  return;
@@ -11303,7 +11430,7 @@ function mountAcpHttp(app, bridge, opts) {
11303
11430
  });
11304
11431
  }, "upgradeListener");
11305
11432
  httpServer.on("upgrade", upgradeListener);
11306
- writeStderrLine(`qwen serve: /acp WebSocket transport enabled on ${path13}`);
11433
+ writeStderrLine(`qwen serve: /acp WebSocket transport enabled on ${path14}`);
11307
11434
  }
11308
11435
  __name(setupWebSocket, "setupWebSocket");
11309
11436
  return {
@@ -11356,14 +11483,14 @@ function readVoiceModel2(settings) {
11356
11483
  return trimmed.length > 0 ? trimmed : void 0;
11357
11484
  }
11358
11485
  __name(readVoiceModel2, "readVoiceModel");
11359
- function buildModelsConfig(settings) {
11486
+ function buildModelsConfig(settings, env) {
11360
11487
  const merged = settings.merged;
11361
- const selectedAuthType = merged.security?.auth?.selectedType ?? getAuthTypeFromEnv();
11488
+ const selectedAuthType = merged.security?.auth?.selectedType ?? getAuthTypeFromEnv(env);
11362
11489
  const resolvedCliConfig = resolveCliGenerationConfig({
11363
11490
  argv: {},
11364
11491
  settings: merged,
11365
11492
  selectedAuthType,
11366
- env: process.env
11493
+ env
11367
11494
  });
11368
11495
  return new ModelsConfig({
11369
11496
  initialAuthType: selectedAuthType,
@@ -11373,17 +11500,29 @@ function buildModelsConfig(settings) {
11373
11500
  });
11374
11501
  }
11375
11502
  __name(buildModelsConfig, "buildModelsConfig");
11376
- function loadDaemonVoiceContext(workspaceCwd) {
11377
- const settings = loadSettings(workspaceCwd);
11503
+ function loadDaemonVoiceContext(workspaceCwd, options = {}) {
11504
+ const settings = loadSettings(
11505
+ workspaceCwd,
11506
+ options.env ? { skipLoadEnvironment: true } : true
11507
+ );
11378
11508
  const voiceModel = readVoiceModel2(settings);
11379
11509
  if (!voiceModel) {
11380
11510
  throw new Error("No voice model is configured for this workspace.");
11381
11511
  }
11382
- const models = buildModelsConfig(settings);
11383
- resolveVoiceTranscriptionConfig({ config: models, settings, voiceModel });
11512
+ const models = buildModelsConfig(
11513
+ settings,
11514
+ options.env ?? snapshotProcessEnv()
11515
+ );
11516
+ resolveVoiceTranscriptionConfig({
11517
+ config: models,
11518
+ settings,
11519
+ voiceModel,
11520
+ env: options.env
11521
+ });
11384
11522
  return {
11385
11523
  settings,
11386
11524
  models,
11525
+ ...options.env ? { env: options.env } : {},
11387
11526
  voiceModel,
11388
11527
  streaming: isStreamingVoiceModel(voiceModel)
11389
11528
  };
@@ -11423,7 +11562,8 @@ async function defaultOpenStream(ctx, callbacks) {
11423
11562
  const cfg = resolveVoiceStreamConfig({
11424
11563
  config: ctx.models,
11425
11564
  settings: ctx.settings,
11426
- voiceModel: ctx.voiceModel
11565
+ voiceModel: ctx.voiceModel,
11566
+ env: ctx.env
11427
11567
  });
11428
11568
  await assertVoiceBaseUrlNetworkAllowed(cfg);
11429
11569
  return await openVoiceStreamWithRetry(
@@ -11438,7 +11578,12 @@ __name(defaultOpenStream, "defaultOpenStream");
11438
11578
  function defaultTranscribe(ctx, pcm) {
11439
11579
  return transcribeVoiceAudio(
11440
11580
  { data: encodeWav(pcm), mimeType: "audio/wav" },
11441
- { config: ctx.models, settings: ctx.settings, voiceModel: ctx.voiceModel }
11581
+ {
11582
+ config: ctx.models,
11583
+ settings: ctx.settings,
11584
+ voiceModel: ctx.voiceModel,
11585
+ env: ctx.env
11586
+ }
11442
11587
  ).catch((error2) => {
11443
11588
  debugLogger3.debug(
11444
11589
  `[voice-ws] batch transcription error: ${errMessage(error2)}`
@@ -11477,7 +11622,7 @@ function parseControl(text) {
11477
11622
  }
11478
11623
  __name(parseControl, "parseControl");
11479
11624
  function createVoiceWsConnectionHandler(boundWorkspace, deps = {}) {
11480
- const loadContext = deps.loadContext ?? loadDaemonVoiceContext;
11625
+ const loadContext = deps.loadContext ?? ((workspaceCwd) => loadDaemonVoiceContext(workspaceCwd, { env: deps.env }));
11481
11626
  const openStream = deps.openStream ?? defaultOpenStream;
11482
11627
  const transcribe = deps.transcribe ?? defaultTranscribe;
11483
11628
  let activeSessions = 0;
@@ -11885,6 +12030,7 @@ function registerDaemonStatusRoutes(app, deps) {
11885
12030
  opts: deps.opts,
11886
12031
  boundWorkspace: deps.boundWorkspace,
11887
12032
  bridge: deps.bridge,
12033
+ workspaceRegistry: deps.workspaceRegistry,
11888
12034
  workspace: deps.workspace,
11889
12035
  daemonLog: deps.daemonLog,
11890
12036
  startup: deps.startup,
@@ -11899,7 +12045,8 @@ function registerDaemonStatusRoutes(app, deps) {
11899
12045
  sessionShellCommandEnabled: deps.sessionShellCommandEnabled,
11900
12046
  getChannelWorkerSnapshot: deps.getChannelWorkerSnapshot,
11901
12047
  getPerfSnapshot: deps.getPerfSnapshot,
11902
- getMetricsSeries: deps.getMetricsSeries
12048
+ getMetricsSeries: deps.getMetricsSeries,
12049
+ getTotalSessionAdmissionSnapshot: deps.getTotalSessionAdmissionSnapshot
11903
12050
  })
11904
12051
  );
11905
12052
  } catch (err) {
@@ -14185,8 +14332,88 @@ __name(registerWorkspaceTrustRoutes, "registerWorkspaceTrustRoutes");
14185
14332
 
14186
14333
  // packages/cli/src/serve/routes/permission.ts
14187
14334
  init_esbuild_shims();
14335
+
14336
+ // packages/cli/src/serve/routes/session-runtime.ts
14337
+ init_esbuild_shims();
14338
+ function requireSessionRuntime(opts) {
14339
+ const {
14340
+ sessionId,
14341
+ route,
14342
+ res,
14343
+ workspaceRegistry,
14344
+ daemonLog,
14345
+ details = {}
14346
+ } = opts;
14347
+ if (workspaceRegistry.list().length === 1) {
14348
+ return workspaceRegistry.primary;
14349
+ }
14350
+ const resolution = workspaceRegistry.resolveLiveSessionOwner(sessionId);
14351
+ if (resolution.kind === "found") {
14352
+ const runtime = resolution.runtime;
14353
+ if (!runtime.primary && !runtime.trusted) {
14354
+ daemonLog?.warn("session routing failed", {
14355
+ route,
14356
+ resolutionKind: "untrusted_workspace",
14357
+ sessionId,
14358
+ workspaceId: runtime.workspaceId,
14359
+ workspaceCwd: runtime.workspaceCwd,
14360
+ ...details
14361
+ });
14362
+ res.status(403).json({
14363
+ error: `Workspace "${runtime.workspaceCwd}" is not trusted.`,
14364
+ code: "untrusted_workspace",
14365
+ sessionId,
14366
+ workspaceId: runtime.workspaceId,
14367
+ workspaceCwd: runtime.workspaceCwd
14368
+ });
14369
+ return void 0;
14370
+ }
14371
+ return runtime;
14372
+ }
14373
+ if (resolution.kind === "not_found") {
14374
+ daemonLog?.warn("session routing failed", {
14375
+ route,
14376
+ resolutionKind: "not_found",
14377
+ sessionId,
14378
+ ...details
14379
+ });
14380
+ res.status(404).json({
14381
+ error: `No session with id "${sessionId}"`,
14382
+ code: "session_not_found",
14383
+ sessionId
14384
+ });
14385
+ return void 0;
14386
+ }
14387
+ const workspaceIds = resolution.runtimes.map(
14388
+ (runtime) => runtime.workspaceId
14389
+ );
14390
+ daemonLog?.warn("session routing failed", {
14391
+ route,
14392
+ resolutionKind: "ambiguous",
14393
+ sessionId,
14394
+ workspaceIds,
14395
+ ...details
14396
+ });
14397
+ res.status(500).json({
14398
+ error: `Session owner is ambiguous for "${sessionId}"`,
14399
+ code: "ambiguous_session_owner",
14400
+ sessionId,
14401
+ route,
14402
+ workspaceIds
14403
+ });
14404
+ return void 0;
14405
+ }
14406
+ __name(requireSessionRuntime, "requireSessionRuntime");
14407
+
14408
+ // packages/cli/src/serve/routes/permission.ts
14188
14409
  function registerPermissionRoutes(app, deps) {
14189
- const { bridge, mutate, sendPermissionVoteError: sendPermissionVoteError2 } = deps;
14410
+ const {
14411
+ bridge,
14412
+ workspaceRegistry,
14413
+ daemonLog,
14414
+ mutate,
14415
+ sendPermissionVoteError: sendPermissionVoteError2
14416
+ } = deps;
14190
14417
  app.post("/session/:id/permission/:requestId", mutate(), (req, res) => {
14191
14418
  const sessionId = req.params["id"];
14192
14419
  const requestId = req.params["requestId"];
@@ -14201,7 +14428,16 @@ function registerPermissionRoutes(app, deps) {
14201
14428
  };
14202
14429
  let accepted;
14203
14430
  try {
14204
- accepted = bridge.respondToSessionPermission(
14431
+ const runtime = requireSessionRuntime({
14432
+ sessionId,
14433
+ route: "POST /session/:id/permission/:requestId",
14434
+ res,
14435
+ workspaceRegistry,
14436
+ daemonLog,
14437
+ details: { requestId }
14438
+ });
14439
+ if (!runtime) return;
14440
+ accepted = runtime.bridge.respondToSessionPermission(
14205
14441
  sessionId,
14206
14442
  requestId,
14207
14443
  response,
@@ -14328,6 +14564,25 @@ function encodeOrganizedCursor(last, group, archiveState) {
14328
14564
  ).toString("base64url");
14329
14565
  }
14330
14566
  __name(encodeOrganizedCursor, "encodeOrganizedCursor");
14567
+ function parseLiveSessionCursor(cursor) {
14568
+ if (cursor === "") return void 0;
14569
+ try {
14570
+ const parsed = JSON.parse(
14571
+ Buffer.from(cursor, "base64url").toString("utf8")
14572
+ );
14573
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed) || typeof parsed.activityTime !== "number" || !Number.isFinite(parsed.activityTime) || typeof parsed.sessionId !== "string" || parsed.sessionId.length === 0) {
14574
+ throw new Error("invalid live cursor");
14575
+ }
14576
+ return parsed;
14577
+ } catch {
14578
+ throw new InvalidCursorError(cursor, "live");
14579
+ }
14580
+ }
14581
+ __name(parseLiveSessionCursor, "parseLiveSessionCursor");
14582
+ function encodeLiveSessionCursor(last) {
14583
+ return Buffer.from(JSON.stringify(last), "utf8").toString("base64url");
14584
+ }
14585
+ __name(encodeLiveSessionCursor, "encodeLiveSessionCursor");
14331
14586
  function toSummary2(item) {
14332
14587
  return {
14333
14588
  sessionId: item.sessionId,
@@ -14373,6 +14628,19 @@ function getSummaryActivityTime(session) {
14373
14628
  return Number.isFinite(time) ? time : 0;
14374
14629
  }
14375
14630
  __name(getSummaryActivityTime, "getSummaryActivityTime");
14631
+ function getLiveSessionCursorKey(session) {
14632
+ return {
14633
+ activityTime: getSummaryActivityTime(session),
14634
+ sessionId: session.sessionId
14635
+ };
14636
+ }
14637
+ __name(getLiveSessionCursorKey, "getLiveSessionCursorKey");
14638
+ function compareLiveSessionCursorKeys(a, b) {
14639
+ const byTime = b.activityTime - a.activityTime;
14640
+ if (byTime !== 0) return byTime;
14641
+ return a.sessionId.localeCompare(b.sessionId);
14642
+ }
14643
+ __name(compareLiveSessionCursorKeys, "compareLiveSessionCursorKeys");
14376
14644
  function compareOrganizedSessions(activityTimeById, a, b) {
14377
14645
  return compareOrganizedCursorKeys(
14378
14646
  getOrganizedCursorKey(activityTimeById, a),
@@ -14580,6 +14848,31 @@ async function listWorkspaceSessionsForResponse(bridge, workspaceCwd, options) {
14580
14848
  return { sessions, nextCursor };
14581
14849
  }
14582
14850
  __name(listWorkspaceSessionsForResponse, "listWorkspaceSessionsForResponse");
14851
+ function listLiveWorkspaceSessionsForResponse(bridge, workspaceCwd, options) {
14852
+ const rawSize = options?.size;
14853
+ const requestedSize = typeof rawSize === "number" && Number.isSafeInteger(rawSize) ? rawSize : DEFAULT_SESSION_PAGE_SIZE;
14854
+ const pageSize = Math.min(Math.max(requestedSize, 1), MAX_SESSION_PAGE_SIZE);
14855
+ const cursorKey = options?.cursor !== void 0 ? parseLiveSessionCursor(options.cursor) : void 0;
14856
+ const sessions = bridge.listWorkspaceSessions(workspaceCwd).sort(
14857
+ (a, b) => compareLiveSessionCursorKeys(
14858
+ getLiveSessionCursorKey(a),
14859
+ getLiveSessionCursorKey(b)
14860
+ )
14861
+ );
14862
+ const afterCursor = cursorKey === void 0 ? sessions : sessions.filter(
14863
+ (session) => compareLiveSessionCursorKeys(
14864
+ cursorKey,
14865
+ getLiveSessionCursorKey(session)
14866
+ ) < 0
14867
+ );
14868
+ const page = afterCursor.slice(0, pageSize);
14869
+ const nextCursor = page.length < afterCursor.length ? encodeLiveSessionCursor(getLiveSessionCursorKey(page[page.length - 1])) : void 0;
14870
+ return {
14871
+ sessions: page,
14872
+ ...nextCursor !== void 0 ? { nextCursor } : {}
14873
+ };
14874
+ }
14875
+ __name(listLiveWorkspaceSessionsForResponse, "listLiveWorkspaceSessionsForResponse");
14583
14876
  function parseSessionPageSizeQuery(raw) {
14584
14877
  if (typeof raw !== "string") return void 0;
14585
14878
  const trimmed = raw.trim();
@@ -14683,6 +14976,7 @@ function registerSessionRoutes(app, deps) {
14683
14976
  const {
14684
14977
  boundWorkspace,
14685
14978
  bridge,
14979
+ workspaceRegistry,
14686
14980
  archiveCoordinator,
14687
14981
  mutate,
14688
14982
  sendBridgeError: sendBridgeError2,
@@ -14691,23 +14985,175 @@ function registerSessionRoutes(app, deps) {
14691
14985
  sessionShellCommandEnabled
14692
14986
  } = deps;
14693
14987
  const LANGUAGE_CODES = deps.languageCodes;
14694
- const parseSessionIdsBody = /* @__PURE__ */ __name((req, res) => {
14695
- const body = safeBody(req);
14696
- const sessionIds = body["sessionIds"];
14697
- if (!Array.isArray(sessionIds) || sessionIds.length === 0 || sessionIds.length > 100 || !sessionIds.every((id) => typeof id === "string")) {
14988
+ const logSessionRoutingFailure = /* @__PURE__ */ __name((route, resolutionKind, details = {}) => {
14989
+ daemonLog?.warn("session routing failed", {
14990
+ route,
14991
+ resolutionKind,
14992
+ ...details
14993
+ });
14994
+ }, "logSessionRoutingFailure");
14995
+ const sendWorkspaceMismatch = /* @__PURE__ */ __name((res, requestedWorkspace) => {
14996
+ const runtimes = workspaceRegistry.list();
14997
+ if (runtimes.length > 1) {
14698
14998
  res.status(400).json({
14699
- error: "`sessionIds` must be a non-empty string array (max 100)",
14700
- code: "invalid_request"
14999
+ error: `Workspace mismatch: daemon is bound to ${runtimes.length} workspaces; none matched the requested workspace.`,
15000
+ code: "workspace_mismatch",
15001
+ boundWorkspace,
15002
+ workspaceCount: runtimes.length,
15003
+ requestedWorkspace
14701
15004
  });
14702
- return void 0;
15005
+ return;
14703
15006
  }
14704
- return [...new Set(sessionIds)];
14705
- }, "parseSessionIdsBody");
14706
- const serializeSessionErrors = /* @__PURE__ */ __name((errors) => errors.map((e) => ({
14707
- sessionId: e.sessionId,
14708
- error: e.error instanceof Error ? e.error.message : String(e.error)
14709
- })), "serializeSessionErrors");
14710
- const resolveWorkspaceParam = /* @__PURE__ */ __name((req, res) => {
15007
+ res.status(400).json({
15008
+ error: `Workspace mismatch: daemon is bound to "${boundWorkspace}"`,
15009
+ code: "workspace_mismatch",
15010
+ boundWorkspace,
15011
+ requestedWorkspace
15012
+ });
15013
+ }, "sendWorkspaceMismatch");
15014
+ const resolveRuntimeForSessionCreation = /* @__PURE__ */ __name((body, res) => {
15015
+ const cwd = parseOptionalWorkspaceCwd(body, boundWorkspace, res);
15016
+ if (cwd === void 0) return void 0;
15017
+ let key;
15018
+ try {
15019
+ key = canonicalizeWorkspace(cwd);
15020
+ } catch (err) {
15021
+ if (workspaceRegistry.list().length > 1 && "cwd" in body) {
15022
+ logSessionRoutingFailure("POST /session", "workspace_mismatch", {
15023
+ requestedWorkspace: cwd
15024
+ });
15025
+ sendWorkspaceMismatch(res, cwd);
15026
+ return void 0;
15027
+ }
15028
+ sendBridgeError2(res, err, { route: "POST /session" });
15029
+ return void 0;
15030
+ }
15031
+ if (workspaceRegistry.list().length === 1) {
15032
+ return {
15033
+ runtime: workspaceRegistry.primary,
15034
+ workspaceCwd: "cwd" in body ? key : workspaceRegistry.primary.workspaceCwd
15035
+ };
15036
+ }
15037
+ const runtime = workspaceRegistry.resolveWorkspaceCwd(
15038
+ "cwd" in body ? key : void 0
15039
+ );
15040
+ if (!runtime) {
15041
+ logSessionRoutingFailure("POST /session", "workspace_mismatch", {
15042
+ requestedWorkspace: key
15043
+ });
15044
+ sendWorkspaceMismatch(res, key);
15045
+ return void 0;
15046
+ }
15047
+ if (!runtime.primary && !runtime.trusted) {
15048
+ logSessionRoutingFailure("POST /session", "untrusted_workspace", {
15049
+ workspaceId: runtime.workspaceId,
15050
+ workspaceCwd: runtime.workspaceCwd
15051
+ });
15052
+ res.status(403).json({
15053
+ error: `Workspace "${runtime.workspaceCwd}" is not trusted.`,
15054
+ code: "untrusted_workspace",
15055
+ workspaceCwd: runtime.workspaceCwd
15056
+ });
15057
+ return void 0;
15058
+ }
15059
+ return { runtime, workspaceCwd: runtime.workspaceCwd };
15060
+ }, "resolveRuntimeForSessionCreation");
15061
+ const resolveRuntimeFromWorkspaceParam = /* @__PURE__ */ __name((req, res) => {
15062
+ const workspaceParam = req.params["id"] ?? "";
15063
+ const byId = workspaceRegistry.getByWorkspaceId(workspaceParam);
15064
+ if (byId) return byId;
15065
+ if (!path10.isAbsolute(workspaceParam)) {
15066
+ res.status(400).json({
15067
+ error: "`:id` must decode to a workspace id or absolute path"
15068
+ });
15069
+ return null;
15070
+ }
15071
+ let key;
15072
+ try {
15073
+ key = canonicalizeWorkspace(workspaceParam);
15074
+ } catch {
15075
+ sendWorkspaceMismatch(res, workspaceParam);
15076
+ return null;
15077
+ }
15078
+ const runtime = workspaceRegistry.getByWorkspaceCwd(key);
15079
+ if (!runtime) {
15080
+ sendWorkspaceMismatch(res, key);
15081
+ return null;
15082
+ }
15083
+ return runtime;
15084
+ }, "resolveRuntimeFromWorkspaceParam");
15085
+ const resolvePrimaryOnlyWorkspaceCwd = /* @__PURE__ */ __name((cwd, res, route) => {
15086
+ let key;
15087
+ try {
15088
+ key = canonicalizeWorkspace(cwd);
15089
+ } catch (err) {
15090
+ sendBridgeError2(res, err, { route });
15091
+ return void 0;
15092
+ }
15093
+ if (key !== boundWorkspace) {
15094
+ const runtime = workspaceRegistry.getByWorkspaceCwd(key);
15095
+ if (runtime && !runtime.primary) {
15096
+ res.status(400).json({
15097
+ error: "Non-primary persisted session load/resume is not supported in Phase 2a.",
15098
+ code: "secondary_workspace_load_not_supported",
15099
+ workspaceCwd: runtime.workspaceCwd,
15100
+ workspaceId: runtime.workspaceId,
15101
+ route
15102
+ });
15103
+ return void 0;
15104
+ }
15105
+ sendWorkspaceMismatch(res, key);
15106
+ return void 0;
15107
+ }
15108
+ return key;
15109
+ }, "resolvePrimaryOnlyWorkspaceCwd");
15110
+ const resolveLiveSessionRuntime = /* @__PURE__ */ __name((sessionId, res, route) => requireSessionRuntime({
15111
+ sessionId,
15112
+ route,
15113
+ res,
15114
+ workspaceRegistry,
15115
+ daemonLog
15116
+ }), "resolveLiveSessionRuntime");
15117
+ const sendNonPrimarySessionRouteUnsupported = /* @__PURE__ */ __name((res, route, sessionId, runtime) => {
15118
+ res.status(400).json({
15119
+ error: `Route "${route}" is primary-only for non-primary workspace sessions in Phase 2a.`,
15120
+ code: "non_primary_session_route_not_supported",
15121
+ sessionId,
15122
+ workspaceId: runtime.workspaceId,
15123
+ workspaceCwd: runtime.workspaceCwd,
15124
+ route
15125
+ });
15126
+ }, "sendNonPrimarySessionRouteUnsupported");
15127
+ const withOwnerMutableSession = /* @__PURE__ */ __name((route, handler) => async (req, res) => {
15128
+ const sessionId = requireSessionId(req, res);
15129
+ if (sessionId === null) return;
15130
+ const runtime = resolveLiveSessionRuntime(sessionId, res, route);
15131
+ if (!runtime) return;
15132
+ try {
15133
+ await archiveCoordinator.runSharedMany([sessionId], async () => {
15134
+ await handler(req, res, sessionId, runtime);
15135
+ });
15136
+ } catch (err) {
15137
+ sendBridgeError2(res, err, { route, sessionId });
15138
+ }
15139
+ }, "withOwnerMutableSession");
15140
+ const parseSessionIdsBody = /* @__PURE__ */ __name((req, res) => {
15141
+ const body = safeBody(req);
15142
+ const sessionIds = body["sessionIds"];
15143
+ if (!Array.isArray(sessionIds) || sessionIds.length === 0 || sessionIds.length > 100 || !sessionIds.every((id) => typeof id === "string")) {
15144
+ res.status(400).json({
15145
+ error: "`sessionIds` must be a non-empty string array (max 100)",
15146
+ code: "invalid_request"
15147
+ });
15148
+ return void 0;
15149
+ }
15150
+ return [...new Set(sessionIds)];
15151
+ }, "parseSessionIdsBody");
15152
+ const serializeSessionErrors = /* @__PURE__ */ __name((errors) => errors.map((e) => ({
15153
+ sessionId: e.sessionId,
15154
+ error: e.error instanceof Error ? e.error.message : String(e.error)
15155
+ })), "serializeSessionErrors");
15156
+ const resolveWorkspaceParam = /* @__PURE__ */ __name((req, res) => {
14711
15157
  const workspaceCwd = req.params["id"] ?? "";
14712
15158
  if (!path10.isAbsolute(workspaceCwd)) {
14713
15159
  res.status(400).json({ error: "`:id` must decode to an absolute workspace path" });
@@ -14728,6 +15174,12 @@ function registerSessionRoutes(app, deps) {
14728
15174
  const withMutableSession = /* @__PURE__ */ __name((route, handler) => async (req, res) => {
14729
15175
  const sessionId = requireSessionId(req, res);
14730
15176
  if (sessionId === null) return;
15177
+ const runtime = resolveLiveSessionRuntime(sessionId, res, route);
15178
+ if (!runtime) return;
15179
+ if (!runtime.primary) {
15180
+ sendNonPrimarySessionRouteUnsupported(res, route, sessionId, runtime);
15181
+ return;
15182
+ }
14731
15183
  try {
14732
15184
  await archiveCoordinator.runSharedMany([sessionId], async () => {
14733
15185
  await handler(req, res, sessionId);
@@ -14738,8 +15190,9 @@ function registerSessionRoutes(app, deps) {
14738
15190
  }, "withMutableSession");
14739
15191
  app.post("/session", mutate(), async (req, res) => {
14740
15192
  const body = safeBody(req);
14741
- const cwd = parseOptionalWorkspaceCwd(body, boundWorkspace, res);
14742
- if (cwd === void 0) return;
15193
+ const resolvedRuntime = resolveRuntimeForSessionCreation(body, res);
15194
+ if (resolvedRuntime === void 0) return;
15195
+ const { runtime, workspaceCwd } = resolvedRuntime;
14743
15196
  const modelServiceId = typeof body["modelServiceId"] === "string" ? body["modelServiceId"] : void 0;
14744
15197
  const rawSessionScope = body["sessionScope"];
14745
15198
  let sessionScope;
@@ -14756,8 +15209,8 @@ function registerSessionRoutes(app, deps) {
14756
15209
  const clientId = parseClientIdHeader(req, res);
14757
15210
  if (clientId === null) return;
14758
15211
  try {
14759
- const session = await bridge.spawnOrAttach({
14760
- workspaceCwd: cwd,
15212
+ const session = await runtime.bridge.spawnOrAttach({
15213
+ workspaceCwd,
14761
15214
  modelServiceId,
14762
15215
  ...clientId !== void 0 ? { clientId } : {},
14763
15216
  ...sessionScope !== void 0 ? { sessionScope } : {}
@@ -14779,10 +15232,10 @@ function registerSessionRoutes(app, deps) {
14779
15232
  );
14780
15233
  }
14781
15234
  if (!session.attached) {
14782
- bridge.killSession(session.sessionId, { requireZeroAttaches: true }).catch(() => {
15235
+ runtime.bridge.killSession(session.sessionId, { requireZeroAttaches: true }).catch(() => {
14783
15236
  });
14784
15237
  } else {
14785
- bridge.detachClient(session.sessionId, session.clientId).catch(() => {
15238
+ runtime.bridge.detachClient(session.sessionId, session.clientId).catch(() => {
14786
15239
  });
14787
15240
  }
14788
15241
  return;
@@ -14798,21 +15251,27 @@ function registerSessionRoutes(app, deps) {
14798
15251
  const body = safeBody(req);
14799
15252
  const cwd = parseOptionalWorkspaceCwd(body, boundWorkspace, res);
14800
15253
  if (cwd === void 0) return;
15254
+ const primaryCwd = resolvePrimaryOnlyWorkspaceCwd(
15255
+ cwd,
15256
+ res,
15257
+ `POST /session/:id/${action}`
15258
+ );
15259
+ if (primaryCwd === void 0) return;
14801
15260
  const clientId = parseClientIdHeader(req, res);
14802
15261
  if (clientId === null) return;
14803
15262
  try {
14804
15263
  const session = await archiveCoordinator.runSharedMany(
14805
15264
  [sessionId],
14806
15265
  async () => {
14807
- await assertSessionLoadable(cwd, sessionId);
15266
+ await assertSessionLoadable(primaryCwd, sessionId);
14808
15267
  return action === "load" ? await bridge.loadSession({
14809
15268
  sessionId,
14810
- workspaceCwd: cwd,
15269
+ workspaceCwd: primaryCwd,
14811
15270
  historyReplay: "response",
14812
15271
  ...clientId !== void 0 ? { clientId } : {}
14813
15272
  }) : await bridge.resumeSession({
14814
15273
  sessionId,
14815
- workspaceCwd: cwd,
15274
+ workspaceCwd: primaryCwd,
14816
15275
  ...clientId !== void 0 ? { clientId } : {}
14817
15276
  });
14818
15277
  }
@@ -14930,8 +15389,14 @@ function registerSessionRoutes(app, deps) {
14930
15389
  app.get("/session/:id/status", (req, res) => {
14931
15390
  const sessionId = requireSessionId(req, res);
14932
15391
  if (sessionId === null) return;
15392
+ const runtime = resolveLiveSessionRuntime(
15393
+ sessionId,
15394
+ res,
15395
+ "GET /session/:id/status"
15396
+ );
15397
+ if (!runtime) return;
14933
15398
  try {
14934
- res.status(200).json(bridge.getSessionSummary(sessionId));
15399
+ res.status(200).json(runtime.bridge.getSessionSummary(sessionId));
14935
15400
  } catch (err) {
14936
15401
  sendBridgeError2(res, err, {
14937
15402
  route: "GET /session/:id/status",
@@ -15200,9 +15665,10 @@ function registerSessionRoutes(app, deps) {
15200
15665
  app.post(
15201
15666
  "/session/:id/prompt",
15202
15667
  mutate(),
15203
- withMutableSession(
15668
+ withOwnerMutableSession(
15204
15669
  "POST /session/:id/prompt",
15205
- async (req, res, sessionId) => {
15670
+ async (req, res, sessionId, runtime) => {
15671
+ const ownerBridge = runtime.bridge;
15206
15672
  const body = safeBody(req);
15207
15673
  const prompt = body["prompt"];
15208
15674
  if (!Array.isArray(prompt) || prompt.length === 0) {
@@ -15236,7 +15702,7 @@ function registerSessionRoutes(app, deps) {
15236
15702
  const promptId = crypto2.randomUUID();
15237
15703
  const forwardedBody = { ...body };
15238
15704
  delete forwardedBody["deadlineMs"];
15239
- const lastEventId = bridge.getSessionLastEventId(sessionId);
15705
+ const lastEventId = ownerBridge.getSessionLastEventId(sessionId);
15240
15706
  addDaemonRequestAttribute("qwen-code.prompt_id", promptId);
15241
15707
  const abort = new AbortController();
15242
15708
  let responseFinished = false;
@@ -15264,7 +15730,7 @@ function registerSessionRoutes(app, deps) {
15264
15730
  }
15265
15731
  let promptPromise;
15266
15732
  try {
15267
- promptPromise = bridge.sendPrompt(
15733
+ promptPromise = ownerBridge.sendPrompt(
15268
15734
  sessionId,
15269
15735
  {
15270
15736
  ...forwardedBody,
@@ -15337,25 +15803,28 @@ function registerSessionRoutes(app, deps) {
15337
15803
  app.post(
15338
15804
  "/session/:id/heartbeat",
15339
15805
  mutate(),
15340
- withMutableSession("POST /session/:id/heartbeat", (req, res, sessionId) => {
15341
- const clientId = parseClientIdHeader(req, res);
15342
- if (clientId === null) return;
15343
- const result = bridge.recordHeartbeat(
15344
- sessionId,
15345
- clientId !== void 0 ? { clientId } : void 0
15346
- );
15347
- res.status(200).json(result);
15348
- })
15806
+ withOwnerMutableSession(
15807
+ "POST /session/:id/heartbeat",
15808
+ (req, res, sessionId, runtime) => {
15809
+ const clientId = parseClientIdHeader(req, res);
15810
+ if (clientId === null) return;
15811
+ const result = runtime.bridge.recordHeartbeat(
15812
+ sessionId,
15813
+ clientId !== void 0 ? { clientId } : void 0
15814
+ );
15815
+ res.status(200).json(result);
15816
+ }
15817
+ )
15349
15818
  );
15350
15819
  app.post(
15351
15820
  "/session/:id/detach",
15352
15821
  mutate(),
15353
- withMutableSession(
15822
+ withOwnerMutableSession(
15354
15823
  "POST /session/:id/detach",
15355
- async (req, res, sessionId) => {
15824
+ async (req, res, sessionId, runtime) => {
15356
15825
  const clientId = parseClientIdHeader(req, res);
15357
15826
  if (clientId === null) return;
15358
- await bridge.detachClient(sessionId, clientId);
15827
+ await runtime.bridge.detachClient(sessionId, clientId);
15359
15828
  res.status(204).end();
15360
15829
  }
15361
15830
  )
@@ -15363,13 +15832,13 @@ function registerSessionRoutes(app, deps) {
15363
15832
  app.post(
15364
15833
  "/session/:id/cancel",
15365
15834
  mutate(),
15366
- withMutableSession(
15835
+ withOwnerMutableSession(
15367
15836
  "POST /session/:id/cancel",
15368
- async (req, res, sessionId) => {
15837
+ async (req, res, sessionId, runtime) => {
15369
15838
  const body = safeBody(req);
15370
15839
  const clientId = parseClientIdHeader(req, res);
15371
15840
  if (clientId === null) return;
15372
- await bridge.cancelSession(
15841
+ await runtime.bridge.cancelSession(
15373
15842
  sessionId,
15374
15843
  {
15375
15844
  ...body,
@@ -15388,10 +15857,16 @@ function registerSessionRoutes(app, deps) {
15388
15857
  const sessionId = req.params["id"];
15389
15858
  const clientId = parseClientIdHeader(req, res);
15390
15859
  if (clientId === null) return;
15860
+ const runtime = resolveLiveSessionRuntime(
15861
+ sessionId,
15862
+ res,
15863
+ "DELETE /session/:id"
15864
+ );
15865
+ if (!runtime) return;
15391
15866
  try {
15392
15867
  await archiveCoordinator.runExclusiveMany(
15393
15868
  [sessionId],
15394
- async () => bridge.closeSession(
15869
+ async () => runtime.bridge.closeSession(
15395
15870
  sessionId,
15396
15871
  clientId !== void 0 ? { clientId } : void 0
15397
15872
  )
@@ -15637,21 +16112,9 @@ function registerSessionRoutes(app, deps) {
15637
16112
  }
15638
16113
  );
15639
16114
  app.get("/workspace/:id/sessions", async (req, res) => {
15640
- const workspaceCwd = req.params["id"] ?? "";
15641
- if (!path10.isAbsolute(workspaceCwd)) {
15642
- res.status(400).json({ error: "`:id` must decode to an absolute workspace path" });
15643
- return;
15644
- }
15645
- const key = canonicalizeWorkspace(workspaceCwd);
15646
- if (key !== boundWorkspace) {
15647
- res.status(400).json({
15648
- error: `Workspace mismatch: daemon is bound to "${boundWorkspace}"`,
15649
- code: "workspace_mismatch",
15650
- boundWorkspace,
15651
- requestedWorkspace: key
15652
- });
15653
- return;
15654
- }
16115
+ const runtime = resolveRuntimeFromWorkspaceParam(req, res);
16116
+ if (runtime === null) return;
16117
+ const key = runtime.workspaceCwd;
15655
16118
  try {
15656
16119
  const cursor = typeof req.query["cursor"] === "string" ? req.query["cursor"] : void 0;
15657
16120
  const size = parseSessionPageSizeQuery(req.query["size"]);
@@ -15687,13 +16150,21 @@ function registerSessionRoutes(app, deps) {
15687
16150
  }
15688
16151
  archiveState = rawArchiveState;
15689
16152
  }
15690
- const result = await listWorkspaceSessionsForResponse(bridge, key, {
16153
+ if (!runtime.primary && (archiveState === "archived" || view)) {
16154
+ res.status(400).json({
16155
+ error: "Non-primary workspace session listing is live-only in Phase 2a.",
16156
+ code: "non_primary_live_sessions_only"
16157
+ });
16158
+ return;
16159
+ }
16160
+ const options = {
15691
16161
  ...cursor !== void 0 ? { cursor } : {},
15692
16162
  ...size !== void 0 ? { size } : {},
15693
16163
  ...archiveState !== void 0 ? { archiveState } : {},
15694
16164
  ...view !== void 0 ? { view } : {},
15695
16165
  ...group !== void 0 ? { group } : {}
15696
- });
16166
+ };
16167
+ const result = runtime.primary ? await listWorkspaceSessionsForResponse(runtime.bridge, key, options) : listLiveWorkspaceSessionsForResponse(runtime.bridge, key, options);
15697
16168
  res.status(200).json({
15698
16169
  sessions: result.sessions,
15699
16170
  ...result.nextCursor != null ? { nextCursor: result.nextCursor } : {},
@@ -15851,10 +16322,16 @@ function registerSessionRoutes(app, deps) {
15851
16322
  app.get("/session/:id/pending-prompts", (req, res) => {
15852
16323
  const sessionId = requireSessionId(req, res);
15853
16324
  if (sessionId === null) return;
16325
+ const runtime = resolveLiveSessionRuntime(
16326
+ sessionId,
16327
+ res,
16328
+ "GET /session/:id/pending-prompts"
16329
+ );
16330
+ if (!runtime) return;
15854
16331
  const clientId = parseClientIdHeader(req, res);
15855
16332
  if (clientId === null) return;
15856
16333
  try {
15857
- const pendingPrompts = bridge.getPendingPrompts(
16334
+ const pendingPrompts = runtime.bridge.getPendingPrompts(
15858
16335
  sessionId,
15859
16336
  clientId !== void 0 ? { clientId } : void 0
15860
16337
  );
@@ -15869,9 +16346,9 @@ function registerSessionRoutes(app, deps) {
15869
16346
  app.delete(
15870
16347
  "/session/:id/pending-prompts/:promptId",
15871
16348
  mutate(),
15872
- withMutableSession(
16349
+ withOwnerMutableSession(
15873
16350
  "DELETE /session/:id/pending-prompts/:promptId",
15874
- (req, res, sessionId) => {
16351
+ (req, res, sessionId, runtime) => {
15875
16352
  const clientId = parseClientIdHeader(req, res);
15876
16353
  if (clientId === null) return;
15877
16354
  const promptId = req.params["promptId"];
@@ -15879,7 +16356,7 @@ function registerSessionRoutes(app, deps) {
15879
16356
  res.status(400).json({ error: "`promptId` route parameter is required" });
15880
16357
  return;
15881
16358
  }
15882
- const result = bridge.removePendingPrompt(
16359
+ const result = runtime.bridge.removePendingPrompt(
15883
16360
  sessionId,
15884
16361
  promptId,
15885
16362
  clientId !== void 0 ? { clientId } : void 0
@@ -15997,7 +16474,7 @@ function registerSessionRoutes(app, deps) {
15997
16474
  );
15998
16475
  app.post(
15999
16476
  "/session/:id/approval-mode",
16000
- mutate({ strict: true }),
16477
+ mutate(),
16001
16478
  withMutableSession(
16002
16479
  "POST /session/:id/approval-mode",
16003
16480
  async (req, res, sessionId) => {
@@ -16078,6 +16555,24 @@ var MAX_SCHEDULED_TASKS = MAX_JOBS;
16078
16555
  var MAX_PROMPT_LENGTH = 1e5;
16079
16556
  var MAX_NAME_LENGTH2 = 200;
16080
16557
  var MAX_CRON_LENGTH = 200;
16558
+ var MAX_SESSION_NAME_LENGTH = 60;
16559
+ function scheduledTaskSessionName(label) {
16560
+ const cleaned = stripTerminalControlSequences(label).replace(/[\u061c\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, "").trim().replace(/\s+/g, " ");
16561
+ let short = cleaned;
16562
+ if (cleaned.length > MAX_SESSION_NAME_LENGTH) {
16563
+ let cut = MAX_SESSION_NAME_LENGTH - 1;
16564
+ const boundary = cleaned.charCodeAt(cut - 1);
16565
+ if (boundary >= 55296 && boundary <= 56319) cut -= 1;
16566
+ short = `${cleaned.slice(0, cut)}\u2026`;
16567
+ }
16568
+ return `\u23F0 ${short}`;
16569
+ }
16570
+ __name(scheduledTaskSessionName, "scheduledTaskSessionName");
16571
+ function computeNextRunAt(task) {
16572
+ if (task.enabled === false) return null;
16573
+ return nextDurableFireMs(task);
16574
+ }
16575
+ __name(computeNextRunAt, "computeNextRunAt");
16081
16576
  function toView(task) {
16082
16577
  return {
16083
16578
  id: task.id,
@@ -16088,7 +16583,14 @@ function toView(task) {
16088
16583
  // Absent enabled defaults to enabled — tool-created tasks never write it.
16089
16584
  enabled: task.enabled !== false,
16090
16585
  createdAt: task.createdAt,
16091
- lastFiredAt: task.lastFiredAt
16586
+ lastFiredAt: task.lastFiredAt,
16587
+ nextRunAt: computeNextRunAt(task),
16588
+ // The task's bound session (its run-history transcript), or null for an
16589
+ // unbound tool-created/legacy task.
16590
+ sessionId: typeof task.sessionId === "string" && task.sessionId.length > 0 ? task.sessionId : null,
16591
+ // Absent runs (tool-created / never-fired) normalizes to [] so the client
16592
+ // never special-cases undefined.
16593
+ runs: Array.isArray(task.runs) ? task.runs : []
16092
16594
  };
16093
16595
  }
16094
16596
  __name(toView, "toView");
@@ -16102,8 +16604,26 @@ function validateCron(cron) {
16102
16604
  }
16103
16605
  }
16104
16606
  __name(validateCron, "validateCron");
16607
+ function canonicalCron(cron) {
16608
+ try {
16609
+ const f = parseCron(cron);
16610
+ const s = /* @__PURE__ */ __name((set) => [...set].sort((a, b) => a - b).join(","), "s");
16611
+ return [
16612
+ s(f.minute),
16613
+ s(f.hour),
16614
+ s(f.dayOfMonth),
16615
+ s(f.month),
16616
+ s(f.dayOfWeek),
16617
+ f.domIsWild ? "W" : "",
16618
+ f.dowIsWild ? "W" : ""
16619
+ ].join("|");
16620
+ } catch {
16621
+ return null;
16622
+ }
16623
+ }
16624
+ __name(canonicalCron, "canonicalCron");
16105
16625
  function registerScheduledTasksRoutes(app, deps) {
16106
- const { boundWorkspace, mutate, safeBody: safeBody2 } = deps;
16626
+ const { boundWorkspace, mutate, safeBody: safeBody2, bridge } = deps;
16107
16627
  app.get("/scheduled-tasks", async (_req, res) => {
16108
16628
  try {
16109
16629
  const tasks = await readCronTasks(boundWorkspace);
@@ -16176,6 +16696,41 @@ function registerScheduledTasksRoutes(app, deps) {
16176
16696
  }
16177
16697
  const recurring = body["recurring"] !== false;
16178
16698
  const enabled = body["enabled"] !== false;
16699
+ let boundSessionId;
16700
+ if (bridge) {
16701
+ try {
16702
+ if ((await readCronTasks(boundWorkspace)).length >= MAX_SCHEDULED_TASKS) {
16703
+ res.status(409).json({
16704
+ error: `Maximum number of scheduled tasks (${MAX_SCHEDULED_TASKS}) reached`,
16705
+ code: "max_tasks_reached"
16706
+ });
16707
+ return;
16708
+ }
16709
+ } catch {
16710
+ }
16711
+ try {
16712
+ const session = await bridge.spawnOrAttach({
16713
+ workspaceCwd: boundWorkspace,
16714
+ sessionScope: "thread"
16715
+ });
16716
+ boundSessionId = session.sessionId;
16717
+ try {
16718
+ bridge.updateSessionMetadata(boundSessionId, {
16719
+ displayName: scheduledTaskSessionName(nameResult.value ?? prompt)
16720
+ });
16721
+ } catch {
16722
+ }
16723
+ } catch (err) {
16724
+ writeStderrLine(
16725
+ `qwen serve: POST /scheduled-tasks failed to create the task's session: ${err instanceof Error ? err.message : String(err)}`
16726
+ );
16727
+ res.status(500).json({
16728
+ error: "Failed to create the task's session",
16729
+ code: "scheduled_tasks_session_failed"
16730
+ });
16731
+ return;
16732
+ }
16733
+ }
16179
16734
  const now = Date.now();
16180
16735
  const task = {
16181
16736
  id: generateCronTaskId(),
@@ -16187,8 +16742,17 @@ function registerScheduledTasksRoutes(app, deps) {
16187
16742
  // minute the task was created — same guard cronScheduler.create uses.
16188
16743
  lastFiredAt: now - now % 6e4,
16189
16744
  enabled,
16745
+ ...boundSessionId !== void 0 ? { sessionId: boundSessionId } : {},
16190
16746
  ...nameResult.value !== void 0 ? { name: nameResult.value } : {}
16191
16747
  };
16748
+ const rollbackSession = /* @__PURE__ */ __name(async () => {
16749
+ if (boundSessionId !== void 0 && bridge) {
16750
+ await bridge.closeSession(boundSessionId).catch(() => {
16751
+ });
16752
+ await new SessionService(boundWorkspace).removeSession(boundSessionId).catch(() => {
16753
+ });
16754
+ }
16755
+ }, "rollbackSession");
16192
16756
  let overCap = false;
16193
16757
  try {
16194
16758
  await updateCronTasks(boundWorkspace, (tasks) => {
@@ -16199,6 +16763,7 @@ function registerScheduledTasksRoutes(app, deps) {
16199
16763
  return [...tasks, task];
16200
16764
  });
16201
16765
  } catch (err) {
16766
+ await rollbackSession();
16202
16767
  writeStderrLine(
16203
16768
  `qwen serve: POST /scheduled-tasks failed: ${err instanceof Error ? err.message : String(err)}`
16204
16769
  );
@@ -16209,6 +16774,7 @@ function registerScheduledTasksRoutes(app, deps) {
16209
16774
  return;
16210
16775
  }
16211
16776
  if (overCap) {
16777
+ await rollbackSession();
16212
16778
  res.status(409).json({
16213
16779
  error: `Maximum number of scheduled tasks (${MAX_SCHEDULED_TASKS}) reached`,
16214
16780
  code: "max_tasks_reached"
@@ -16294,17 +16860,32 @@ function registerScheduledTasksRoutes(app, deps) {
16294
16860
  }
16295
16861
  let found = false;
16296
16862
  let updated;
16863
+ let blockedByArchive = false;
16297
16864
  try {
16298
16865
  await updateCronTasks(boundWorkspace, (tasks) => {
16299
16866
  const idx = tasks.findIndex((t) => t.id === id);
16300
16867
  if (idx === -1) return tasks;
16301
16868
  found = true;
16302
16869
  const current = tasks[idx];
16870
+ if (patch.enabled === true && current.disabledByArchive === true) {
16871
+ blockedByArchive = true;
16872
+ return tasks;
16873
+ }
16303
16874
  const next = { ...current, ...patch };
16304
16875
  if (clearName) delete next.name;
16305
- if (current.enabled === false && patch.enabled === true && next.recurring) {
16876
+ const justReEnabled = current.enabled === false && patch.enabled === true;
16877
+ const cronChanged = patch.cron !== void 0 && canonicalCron(patch.cron) !== canonicalCron(current.cron);
16878
+ const becameRecurring = patch.recurring === true && current.recurring !== true;
16879
+ const becameOneShot = patch.recurring === false && current.recurring !== false;
16880
+ {
16306
16881
  const now = Date.now();
16307
- next.lastFiredAt = now - now % 6e4;
16882
+ const minute = now - now % 6e4;
16883
+ if (next.recurring && (justReEnabled || cronChanged || becameRecurring)) {
16884
+ next.lastFiredAt = minute;
16885
+ } else if (!next.recurring && (justReEnabled || cronChanged || becameOneShot)) {
16886
+ next.createdAt = now;
16887
+ next.lastFiredAt = minute;
16888
+ }
16308
16889
  }
16309
16890
  updated = next;
16310
16891
  return tasks.map((t, i) => i === idx ? next : t);
@@ -16319,10 +16900,26 @@ function registerScheduledTasksRoutes(app, deps) {
16319
16900
  });
16320
16901
  return;
16321
16902
  }
16903
+ if (blockedByArchive) {
16904
+ res.status(409).json({
16905
+ error: "This task was disabled by archiving its session; unarchive the session to re-enable it.",
16906
+ code: "task_session_archived"
16907
+ });
16908
+ return;
16909
+ }
16322
16910
  if (!found || !updated) {
16323
16911
  res.status(404).json({ error: "Task not found", code: "task_not_found" });
16324
16912
  return;
16325
16913
  }
16914
+ const effectiveLabelChanged = patch.name !== void 0 || clearName || patch.prompt !== void 0 && updated.name === void 0;
16915
+ if (bridge && updated.sessionId && effectiveLabelChanged) {
16916
+ try {
16917
+ bridge.updateSessionMetadata(updated.sessionId, {
16918
+ displayName: scheduledTaskSessionName(updated.name ?? updated.prompt)
16919
+ });
16920
+ } catch {
16921
+ }
16922
+ }
16326
16923
  res.status(200).json(toView(updated));
16327
16924
  });
16328
16925
  app.delete("/scheduled-tasks/:id", mutate(), async (req, res) => {
@@ -16331,9 +16928,19 @@ function registerScheduledTasksRoutes(app, deps) {
16331
16928
  res.status(400).json({ error: "Task id is required", code: "invalid_id" });
16332
16929
  return;
16333
16930
  }
16334
- let removed;
16931
+ let boundSessionId;
16932
+ let removed = false;
16335
16933
  try {
16336
- removed = await removeCronTasks(boundWorkspace, [id]);
16934
+ await updateCronTasks(boundWorkspace, (tasks) => {
16935
+ const idx = tasks.findIndex((t) => t.id === id);
16936
+ if (idx === -1) return tasks;
16937
+ const match = tasks[idx].sessionId;
16938
+ if (typeof match === "string" && match.length > 0) {
16939
+ boundSessionId = match;
16940
+ }
16941
+ removed = true;
16942
+ return tasks.filter((_, i) => i !== idx);
16943
+ });
16337
16944
  } catch (err) {
16338
16945
  writeStderrLine(
16339
16946
  `qwen serve: DELETE /scheduled-tasks/${id} failed: ${err instanceof Error ? err.message : String(err)}`
@@ -16344,12 +16951,76 @@ function registerScheduledTasksRoutes(app, deps) {
16344
16951
  });
16345
16952
  return;
16346
16953
  }
16347
- if (removed === 0) {
16954
+ if (!removed) {
16348
16955
  res.status(404).json({ error: "Task not found", code: "task_not_found" });
16349
16956
  return;
16350
16957
  }
16958
+ if (boundSessionId && bridge) {
16959
+ await bridge.closeSession(boundSessionId).catch(() => {
16960
+ });
16961
+ }
16351
16962
  res.status(200).json({ deleted: true, id });
16352
16963
  });
16964
+ app.post("/scheduled-tasks/:id/run", mutate(), async (req, res) => {
16965
+ const id = typeof req.params["id"] === "string" ? req.params["id"] : "";
16966
+ if (id.length === 0) {
16967
+ res.status(400).json({ error: "Task id is required", code: "invalid_id" });
16968
+ return;
16969
+ }
16970
+ const now = Date.now();
16971
+ let found = false;
16972
+ let blockedDisabled = false;
16973
+ let updated;
16974
+ try {
16975
+ await updateCronTasks(boundWorkspace, (tasks) => {
16976
+ const idx = tasks.findIndex((t) => t.id === id);
16977
+ if (idx === -1) return tasks;
16978
+ found = true;
16979
+ const current = tasks[idx];
16980
+ if (current.enabled === false) {
16981
+ blockedDisabled = true;
16982
+ return tasks;
16983
+ }
16984
+ const next = {
16985
+ ...current,
16986
+ lastFiredAt: now,
16987
+ runs: appendCronRun(current.runs, {
16988
+ at: now,
16989
+ kind: "manual",
16990
+ ...current.sessionId ? { sessionId: current.sessionId } : {}
16991
+ })
16992
+ };
16993
+ updated = next;
16994
+ if (!current.recurring) {
16995
+ return tasks.filter((_, i) => i !== idx);
16996
+ }
16997
+ return tasks.map((t, i) => i === idx ? next : t);
16998
+ });
16999
+ } catch (err) {
17000
+ writeStderrLine(
17001
+ `qwen serve: POST /scheduled-tasks/${id}/run failed: ${err instanceof Error ? err.message : String(err)}`
17002
+ );
17003
+ res.status(500).json({
17004
+ error: "Failed to record scheduled task run",
17005
+ code: "scheduled_tasks_write_failed"
17006
+ });
17007
+ return;
17008
+ }
17009
+ if (blockedDisabled) {
17010
+ res.status(409).json({
17011
+ error: "Cannot run a disabled task; enable it first (unarchive its session if it was archived).",
17012
+ code: "task_disabled"
17013
+ });
17014
+ return;
17015
+ }
17016
+ if (!found || !updated) {
17017
+ res.status(404).json({ error: "Task not found", code: "task_not_found" });
17018
+ return;
17019
+ }
17020
+ const view = toView(updated);
17021
+ if (!updated.recurring) view.nextRunAt = null;
17022
+ res.status(200).json(view);
17023
+ });
16353
17024
  }
16354
17025
  __name(registerScheduledTasksRoutes, "registerScheduledTasksRoutes");
16355
17026
  function parseNameField(raw) {
@@ -16384,7 +17055,7 @@ function parseHeatmapDays(raw) {
16384
17055
  }
16385
17056
  __name(parseHeatmapDays, "parseHeatmapDays");
16386
17057
  function registerUsageStatsRoutes(app, deps = {}) {
16387
- const loadHistory = deps.loadHistory ?? (() => loadUsageHistory(void 0, { persistRebuild: false }));
17058
+ const loadHistory = deps.loadHistory ?? (() => loadUsageHistoryWithLive());
16388
17059
  const ttlMs = deps.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
16389
17060
  let cache = null;
16390
17061
  const getRecords = /* @__PURE__ */ __name(() => {
@@ -16424,6 +17095,320 @@ function registerUsageStatsRoutes(app, deps = {}) {
16424
17095
  }
16425
17096
  __name(registerUsageStatsRoutes, "registerUsageStatsRoutes");
16426
17097
 
17098
+ // packages/cli/src/serve/scheduled-task-keepalive.ts
17099
+ init_esbuild_shims();
17100
+ import * as fsSync from "node:fs";
17101
+ import * as path11 from "node:path";
17102
+ var log = createDebugLogger("SCHED_KEEPALIVE");
17103
+ function collectBoundSessionIds(tasks) {
17104
+ const seen = /* @__PURE__ */ new Set();
17105
+ const ids = [];
17106
+ for (const task of tasks) {
17107
+ const sessionId = task.sessionId;
17108
+ if (task.enabled === false || // disabled (e.g. archived) — let it be reaped
17109
+ typeof sessionId !== "string" || sessionId.length === 0 || seen.has(sessionId)) {
17110
+ continue;
17111
+ }
17112
+ seen.add(sessionId);
17113
+ ids.push(sessionId);
17114
+ }
17115
+ return ids;
17116
+ }
17117
+ __name(collectBoundSessionIds, "collectBoundSessionIds");
17118
+ var KEEPALIVE_REVIVE_TIMEOUT_MS = 3e4;
17119
+ var KEEPALIVE_SPAWN_TIMEOUT_MS = 3e4;
17120
+ var MAX_REVIVE_BACKOFF_MS = 30 * 6e4;
17121
+ async function bindAndNameSessions(bridge, boundWorkspace, tasks, renamed, spawnTimeoutMs, binding) {
17122
+ const unbound = tasks.filter(
17123
+ (t) => !t.sessionId && t.enabled !== false && !binding.has(t.id)
17124
+ );
17125
+ const needsName = tasks.filter(
17126
+ (t) => t.sessionId && t.enabled !== false && !renamed.has(t.sessionId)
17127
+ );
17128
+ for (const task of unbound) {
17129
+ let spawnedSessionId;
17130
+ try {
17131
+ binding.add(task.id);
17132
+ const rawSpawn = bridge.spawnOrAttach({
17133
+ workspaceCwd: boundWorkspace,
17134
+ sessionScope: "thread"
17135
+ });
17136
+ let timedOut = false;
17137
+ rawSpawn.then(({ sessionId: sessionId2 }) => {
17138
+ if (timedOut) {
17139
+ log.debug(
17140
+ "keepalive: late spawn resolved, cleaning up",
17141
+ task.id,
17142
+ sessionId2
17143
+ );
17144
+ bridge.closeSession(sessionId2).catch(() => {
17145
+ });
17146
+ new SessionService(boundWorkspace).removeSession(sessionId2).catch(() => {
17147
+ });
17148
+ }
17149
+ }).catch(() => {
17150
+ }).finally(() => {
17151
+ binding.delete(task.id);
17152
+ });
17153
+ const { sessionId } = await withTimeout(
17154
+ rawSpawn,
17155
+ spawnTimeoutMs,
17156
+ `spawnOrAttach(${task.id})`
17157
+ ).catch((err) => {
17158
+ timedOut = true;
17159
+ throw err;
17160
+ });
17161
+ spawnedSessionId = sessionId;
17162
+ try {
17163
+ bridge.updateSessionMetadata(sessionId, {
17164
+ displayName: scheduledTaskSessionName(task.prompt)
17165
+ });
17166
+ renamed.add(sessionId);
17167
+ } catch {
17168
+ }
17169
+ let matched = false;
17170
+ await updateCronTasks(boundWorkspace, (list) => {
17171
+ if (!list.some(
17172
+ (t) => t.id === task.id && !t.sessionId && t.enabled !== false
17173
+ )) {
17174
+ return list;
17175
+ }
17176
+ const result = list.map(
17177
+ (t) => t.id === task.id && !t.sessionId && t.enabled !== false ? { ...t, sessionId } : t
17178
+ );
17179
+ matched = true;
17180
+ return result;
17181
+ });
17182
+ if (!matched) {
17183
+ throw new Error(`task ${task.id} no longer on disk`);
17184
+ }
17185
+ log.debug(
17186
+ "keepalive: bound task",
17187
+ task.id,
17188
+ "to dedicated session",
17189
+ sessionId
17190
+ );
17191
+ } catch (err) {
17192
+ log.debug("keepalive: failed to bind task", task.id, err);
17193
+ if (spawnedSessionId !== void 0) {
17194
+ await bridge.closeSession(spawnedSessionId).catch(() => {
17195
+ });
17196
+ await new SessionService(boundWorkspace).removeSession(spawnedSessionId).catch(() => {
17197
+ });
17198
+ }
17199
+ }
17200
+ }
17201
+ for (const task of needsName) {
17202
+ const sessionId = task.sessionId;
17203
+ try {
17204
+ bridge.updateSessionMetadata(sessionId, {
17205
+ displayName: scheduledTaskSessionName(task.prompt)
17206
+ });
17207
+ renamed.add(sessionId);
17208
+ } catch (err) {
17209
+ log.debug("keepalive: failed to name session", sessionId, err);
17210
+ }
17211
+ }
17212
+ }
17213
+ __name(bindAndNameSessions, "bindAndNameSessions");
17214
+ function startScheduledTaskKeepalive(opts) {
17215
+ const { bridge, boundWorkspace, intervalMs } = opts;
17216
+ const reviveTimeoutMs = opts.reviveTimeoutMs ?? KEEPALIVE_REVIVE_TIMEOUT_MS;
17217
+ const spawnTimeoutMs = opts.spawnTimeoutMs ?? KEEPALIVE_SPAWN_TIMEOUT_MS;
17218
+ const reviveState = /* @__PURE__ */ new Map();
17219
+ const reviving = /* @__PURE__ */ new Set();
17220
+ const binding = /* @__PURE__ */ new Set();
17221
+ const renamed = /* @__PURE__ */ new Set();
17222
+ const tick = /* @__PURE__ */ __name(async () => {
17223
+ let tasks;
17224
+ try {
17225
+ tasks = await readCronTasks(boundWorkspace);
17226
+ } catch (err) {
17227
+ log.debug("keepalive: readCronTasks failed, skipping this pass", err);
17228
+ return;
17229
+ }
17230
+ for (const sessionId of collectBoundSessionIds(tasks)) {
17231
+ try {
17232
+ bridge.recordHeartbeat(sessionId);
17233
+ reviveState.delete(sessionId);
17234
+ } catch (err) {
17235
+ const state = reviveState.get(sessionId);
17236
+ if (reviving.has(sessionId)) {
17237
+ continue;
17238
+ }
17239
+ if (state && Date.now() < state.nextAttemptAt) {
17240
+ continue;
17241
+ }
17242
+ log.debug("keepalive: recordHeartbeat failed for", sessionId, err);
17243
+ const load = bridge.loadSession({
17244
+ sessionId,
17245
+ workspaceCwd: boundWorkspace,
17246
+ historyReplay: "response"
17247
+ });
17248
+ reviving.add(sessionId);
17249
+ void load.catch(() => {
17250
+ }).finally(() => {
17251
+ reviving.delete(sessionId);
17252
+ });
17253
+ try {
17254
+ await withTimeout(load, reviveTimeoutMs, `loadSession(${sessionId})`);
17255
+ log.debug("keepalive: revived non-resident session", sessionId);
17256
+ reviveState.delete(sessionId);
17257
+ } catch (loadErr) {
17258
+ const failures = (state?.failures ?? 0) + 1;
17259
+ const backoff = Math.min(
17260
+ intervalMs * 2 ** Math.min(failures - 1, 6),
17261
+ MAX_REVIVE_BACKOFF_MS
17262
+ );
17263
+ reviveState.set(sessionId, {
17264
+ failures,
17265
+ nextAttemptAt: Date.now() + backoff
17266
+ });
17267
+ log.debug(
17268
+ "keepalive: failed to revive session",
17269
+ sessionId,
17270
+ `(failure ${failures}, next retry in ${backoff}ms)`,
17271
+ loadErr
17272
+ );
17273
+ }
17274
+ }
17275
+ }
17276
+ if (reviveState.size > 0 || renamed.size > 0) {
17277
+ const live = new Set(tasks.map((t) => t.sessionId));
17278
+ for (const id of reviveState.keys()) {
17279
+ if (!live.has(id)) reviveState.delete(id);
17280
+ }
17281
+ for (const id of renamed) {
17282
+ if (!live.has(id)) renamed.delete(id);
17283
+ }
17284
+ }
17285
+ await bindAndNameSessions(
17286
+ bridge,
17287
+ boundWorkspace,
17288
+ tasks,
17289
+ renamed,
17290
+ spawnTimeoutMs,
17291
+ binding
17292
+ );
17293
+ }, "tick");
17294
+ let running = false;
17295
+ const timer = setInterval(() => {
17296
+ if (running) return;
17297
+ running = true;
17298
+ void tick().finally(() => {
17299
+ running = false;
17300
+ });
17301
+ }, intervalMs);
17302
+ timer.unref?.();
17303
+ let bindDebounce;
17304
+ const cronFilePath = getCronFilePath(boundWorkspace);
17305
+ const cronDir = path11.dirname(cronFilePath);
17306
+ const cronFileName = path11.basename(cronFilePath);
17307
+ let fileWatcher;
17308
+ try {
17309
+ fsSync.mkdirSync(cronDir, { recursive: true });
17310
+ fileWatcher = fsSync.watch(
17311
+ cronDir,
17312
+ { persistent: false },
17313
+ (_event, filename) => {
17314
+ if (filename !== null && filename !== cronFileName) return;
17315
+ if (bindDebounce) clearTimeout(bindDebounce);
17316
+ bindDebounce = setTimeout(() => {
17317
+ if (running) return;
17318
+ running = true;
17319
+ void tick().finally(() => {
17320
+ running = false;
17321
+ });
17322
+ }, 500);
17323
+ bindDebounce.unref?.();
17324
+ }
17325
+ );
17326
+ fileWatcher.on("error", () => {
17327
+ });
17328
+ } catch {
17329
+ }
17330
+ let stopped = false;
17331
+ return {
17332
+ stop: /* @__PURE__ */ __name(() => {
17333
+ if (stopped) return;
17334
+ stopped = true;
17335
+ clearInterval(timer);
17336
+ if (bindDebounce) clearTimeout(bindDebounce);
17337
+ fileWatcher?.close();
17338
+ }, "stop"),
17339
+ tick
17340
+ };
17341
+ }
17342
+ __name(startScheduledTaskKeepalive, "startScheduledTaskKeepalive");
17343
+ var REHYDRATE_LOAD_TIMEOUT_MS = 3e4;
17344
+ var REHYDRATE_MAX_CONCURRENCY = 4;
17345
+ async function rehydrateScheduledTaskSessions(deps) {
17346
+ const { bridge, boundWorkspace } = deps;
17347
+ const timeoutMs = deps.loadTimeoutMs ?? REHYDRATE_LOAD_TIMEOUT_MS;
17348
+ let tasks;
17349
+ try {
17350
+ tasks = await readCronTasks(boundWorkspace);
17351
+ } catch (err) {
17352
+ log.debug("rehydrate: readCronTasks failed", err);
17353
+ return { loaded: [], failed: [] };
17354
+ }
17355
+ const sessionIds = collectBoundSessionIds(tasks);
17356
+ const loaded = [];
17357
+ const failed = [];
17358
+ const loadOne = /* @__PURE__ */ __name(async (sessionId) => {
17359
+ const load = bridge.loadSession({
17360
+ sessionId,
17361
+ workspaceCwd: boundWorkspace,
17362
+ historyReplay: "response"
17363
+ });
17364
+ void load.catch(() => {
17365
+ });
17366
+ try {
17367
+ await withTimeout(load, timeoutMs, `loadSession(${sessionId})`);
17368
+ loaded.push(sessionId);
17369
+ } catch (err) {
17370
+ failed.push(sessionId);
17371
+ try {
17372
+ deps.onError?.(sessionId, err);
17373
+ } catch {
17374
+ }
17375
+ }
17376
+ }, "loadOne");
17377
+ const queue = sessionIds.slice();
17378
+ const worker = /* @__PURE__ */ __name(async () => {
17379
+ for (let sessionId = queue.shift(); sessionId !== void 0; sessionId = queue.shift()) {
17380
+ await loadOne(sessionId);
17381
+ }
17382
+ }, "worker");
17383
+ await Promise.all(
17384
+ Array.from(
17385
+ { length: Math.min(REHYDRATE_MAX_CONCURRENCY, queue.length) },
17386
+ () => worker()
17387
+ )
17388
+ );
17389
+ return { loaded, failed };
17390
+ }
17391
+ __name(rehydrateScheduledTaskSessions, "rehydrateScheduledTaskSessions");
17392
+ function withTimeout(p, ms, label) {
17393
+ return new Promise((resolve4, reject) => {
17394
+ const timer = setTimeout(() => {
17395
+ reject(new Error(`${label} timed out after ${ms}ms`));
17396
+ }, ms);
17397
+ if (typeof timer.unref === "function") timer.unref();
17398
+ p.then(
17399
+ (v) => {
17400
+ clearTimeout(timer);
17401
+ resolve4(v);
17402
+ },
17403
+ (e) => {
17404
+ clearTimeout(timer);
17405
+ reject(e);
17406
+ }
17407
+ );
17408
+ });
17409
+ }
17410
+ __name(withTimeout, "withTimeout");
17411
+
16427
17412
  // packages/cli/src/serve/routes/workspace-status.ts
16428
17413
  init_esbuild_shims();
16429
17414
  function registerWorkspaceStatusRoutes(app, deps) {
@@ -16543,6 +17528,7 @@ init_esbuild_shims();
16543
17528
 
16544
17529
  // packages/cli/src/serve/server/serve-features.ts
16545
17530
  init_esbuild_shims();
17531
+ var DEFAULT_MAX_SESSIONS = 20;
16546
17532
  var DEFAULT_MAX_PENDING_PROMPTS_PER_SESSION = 5;
16547
17533
  var SERVE_LANGUAGE_CODES = [
16548
17534
  ...SUPPORTED_LANGUAGES.map((language) => language.code),
@@ -16554,13 +17540,20 @@ function advertisedMaxPendingPromptsPerSession(value) {
16554
17540
  return value;
16555
17541
  }
16556
17542
  __name(advertisedMaxPendingPromptsPerSession, "advertisedMaxPendingPromptsPerSession");
17543
+ function advertisedMaxSessions(value) {
17544
+ if (value === void 0) return DEFAULT_MAX_SESSIONS;
17545
+ if (value === 0 || value === Number.POSITIVE_INFINITY) return null;
17546
+ return value;
17547
+ }
17548
+ __name(advertisedMaxSessions, "advertisedMaxSessions");
16557
17549
  function createServeFeatures(deps) {
16558
17550
  const {
16559
17551
  opts,
16560
17552
  boundWorkspace,
16561
17553
  persistSettingAvailable,
16562
17554
  reloadAvailable,
16563
- sessionShellCommandEnabled
17555
+ sessionShellCommandEnabled,
17556
+ multiWorkspaceSessionsEnabled
16564
17557
  } = deps;
16565
17558
  let cachedVoiceTranscriptionAvailable;
16566
17559
  const invalidateServeFeaturesCache = /* @__PURE__ */ __name(() => {
@@ -16583,6 +17576,7 @@ function createServeFeatures(deps) {
16583
17576
  sessionShellCommandEnabled,
16584
17577
  rateLimit: opts.rateLimit === true,
16585
17578
  reloadAvailable,
17579
+ multiWorkspaceSessionsEnabled,
16586
17580
  clientMcpOverWsEnabled: opts.clientMcpOverWs === true,
16587
17581
  cdpTunnelOverWsEnabled: opts.cdpTunnelOverWs === true,
16588
17582
  voiceTranscriptionAvailable: getCachedVoiceTranscriptionAvailable(),
@@ -16612,6 +17606,8 @@ __name(isWorkspaceVoiceTranscriptionAvailable, "isWorkspaceVoiceTranscriptionAva
16612
17606
  // packages/cli/src/serve/routes/capabilities.ts
16613
17607
  function registerCapabilitiesRoutes(app, deps) {
16614
17608
  app.get("/capabilities", (_req, res) => {
17609
+ const runtimes = deps.workspaceRegistry.list();
17610
+ const multiWorkspace = runtimes.length > 1;
16615
17611
  const envelope = {
16616
17612
  v: CAPABILITIES_SCHEMA_VERSION,
16617
17613
  protocolVersions: getServeProtocolVersions(),
@@ -16619,8 +17615,8 @@ function registerCapabilitiesRoutes(app, deps) {
16619
17615
  mode: deps.mode,
16620
17616
  features: deps.currentServeFeatures(),
16621
17617
  modelServices: [],
16622
- // Surface the bound workspace so clients can detect mismatch pre-flight
16623
- // and omit `cwd` on `POST /session`.
17618
+ // Surface the primary workspace so clients can omit `cwd` on
17619
+ // `POST /session`; multi-workspace clients use `workspaces[]`.
16624
17620
  workspaceCwd: deps.boundWorkspace,
16625
17621
  // Advertise supported transport families so SDK clients can
16626
17622
  // auto-negotiate the best available transport via negotiateTransport().
@@ -16630,8 +17626,22 @@ function registerCapabilitiesRoutes(app, deps) {
16630
17626
  limits: {
16631
17627
  maxPendingPromptsPerSession: advertisedMaxPendingPromptsPerSession(
16632
17628
  deps.maxPendingPromptsPerSession
16633
- )
17629
+ ),
17630
+ ...multiWorkspace ? {
17631
+ maxSessionsPerWorkspace: advertisedMaxSessions(
17632
+ deps.maxSessionsPerWorkspace
17633
+ ),
17634
+ maxTotalSessions: deps.maxTotalSessions === void 0 || deps.maxTotalSessions === 0 || deps.maxTotalSessions === Number.POSITIVE_INFINITY ? null : deps.maxTotalSessions
17635
+ } : {}
16634
17636
  },
17637
+ ...multiWorkspace ? {
17638
+ workspaces: runtimes.map((runtime) => ({
17639
+ id: runtime.workspaceId,
17640
+ cwd: runtime.workspaceCwd,
17641
+ primary: runtime.primary,
17642
+ trusted: runtime.trusted
17643
+ }))
17644
+ } : {},
16635
17645
  supportedLanguages: deps.languageCodes
16636
17646
  };
16637
17647
  res.status(200).json(envelope);
@@ -17155,7 +18165,28 @@ function sendBridgeError(res, err, ctx, daemonLog) {
17155
18165
  res.status(503).json({
17156
18166
  error: err.message,
17157
18167
  code: "session_limit_exceeded",
17158
- limit: err.limit
18168
+ limit: err.limit,
18169
+ scope: "workspace"
18170
+ });
18171
+ return;
18172
+ }
18173
+ if (err instanceof TotalSessionLimitExceededError) {
18174
+ const totalSessionError = err;
18175
+ daemonLog?.warn("total session admission rejected", {
18176
+ ...ctx?.route ? { route: ctx.route } : {},
18177
+ ...ctx?.sessionId ? { sessionId: ctx.sessionId } : {},
18178
+ limit: totalSessionError.limit,
18179
+ scope: totalSessionError.scope,
18180
+ ...totalSessionError.operation ? { operation: totalSessionError.operation } : {},
18181
+ ...totalSessionError.workspaceCwd ? { workspaceCwd: totalSessionError.workspaceCwd } : {},
18182
+ ...totalSessionError.sourceSessionId ? { sourceSessionId: totalSessionError.sourceSessionId } : {}
18183
+ });
18184
+ res.set("Retry-After", "5");
18185
+ res.status(503).json({
18186
+ error: err.message,
18187
+ code: "session_limit_exceeded",
18188
+ limit: err.limit,
18189
+ scope: err.scope
17159
18190
  });
17160
18191
  return;
17161
18192
  }
@@ -17357,7 +18388,7 @@ data: ${dataJson}
17357
18388
  }
17358
18389
  __name(formatSseFrame, "formatSseFrame");
17359
18390
  function registerSseEventsRoutes(app, deps) {
17360
- const { bridge, daemonLog, sendBridgeError: sendBridgeError2, writerIdleTimeoutMs } = deps;
18391
+ const { workspaceRegistry, daemonLog, sendBridgeError: sendBridgeError2, writerIdleTimeoutMs } = deps;
17361
18392
  app.get("/session/:id/events", (req, res) => {
17362
18393
  const sessionId = req.params["id"];
17363
18394
  const lastEventId = parseLastEventId(req.headers["last-event-id"]);
@@ -17366,8 +18397,16 @@ function registerSseEventsRoutes(app, deps) {
17366
18397
  let iter;
17367
18398
  const abort = new AbortController();
17368
18399
  try {
18400
+ const runtime = requireSessionRuntime({
18401
+ sessionId,
18402
+ route: "GET /session/:id/events",
18403
+ res,
18404
+ workspaceRegistry,
18405
+ daemonLog
18406
+ });
18407
+ if (!runtime) return;
17369
18408
  const snapshot = req.query["snapshot"] === "1";
17370
- const iterable = bridge.subscribeEvents(sessionId, {
18409
+ const iterable = runtime.bridge.subscribeEvents(sessionId, {
17371
18410
  signal: abort.signal,
17372
18411
  lastEventId,
17373
18412
  ...maxQueued !== void 0 ? { maxQueued } : {},
@@ -17573,10 +18612,13 @@ __name(registerSseEventsRoutes, "registerSseEventsRoutes");
17573
18612
  // packages/cli/src/serve/routes/a2ui-action.ts
17574
18613
  init_esbuild_shims();
17575
18614
  import * as fsp4 from "node:fs/promises";
17576
- import * as path11 from "node:path";
18615
+ import * as path12 from "node:path";
17577
18616
  var A2UI_MIME = "application/a2ui+json";
17578
18617
  var ACTION_TOOL = "action";
17579
18618
  var CALL_TIMEOUT_MS = 15e3;
18619
+ var SCRUBBED_STDIO_ENV_KEYS = /* @__PURE__ */ new Set([
18620
+ QWEN_SERVER_TOKEN_ENV
18621
+ ]);
17580
18622
  function usableServerConfig(cfg) {
17581
18623
  if (!cfg) return false;
17582
18624
  if (typeof cfg.httpUrl === "string") {
@@ -17593,7 +18635,7 @@ __name(usableServerConfig, "usableServerConfig");
17593
18635
  async function findFromSettingsFile(workspaceCwd) {
17594
18636
  try {
17595
18637
  const raw = await fsp4.readFile(
17596
- path11.join(workspaceCwd, ".qwen", "settings.json"),
18638
+ path12.join(workspaceCwd, ".qwen", "settings.json"),
17597
18639
  "utf8"
17598
18640
  );
17599
18641
  const settings = JSON.parse(raw);
@@ -17607,22 +18649,34 @@ async function findFromSettingsFile(workspaceCwd) {
17607
18649
  return null;
17608
18650
  }
17609
18651
  __name(findFromSettingsFile, "findFromSettingsFile");
17610
- function buildTransport(cfg) {
18652
+ function buildTransport(cfg, baseEnv = snapshotProcessEnv()) {
17611
18653
  if (typeof cfg.httpUrl === "string") {
17612
18654
  return new StreamableHTTPClientTransport(new URL(cfg.httpUrl));
17613
18655
  }
17614
18656
  return new StdioClientTransport({
17615
18657
  command: cfg.command,
17616
18658
  args: cfg.args ?? [],
17617
- // spawn() treats `env` as a complete replacement, not a merge — a partial
17618
- // env (e.g. {API_KEY}) would strip PATH/HOME and break the child. Merge
17619
- // over process.env like packages/core/src/tools/mcp-client.ts does; when
17620
- // unset, let the SDK apply its safe default environment.
17621
- ...cfg.env ? { env: { ...process.env, ...cfg.env } } : {},
18659
+ // Passing `env` prevents the SDK from reading live process.env while
18660
+ // keeping daemon MCP stdio behavior aligned with the core CLI client.
18661
+ env: buildStdioServerEnv(baseEnv, cfg.env),
17622
18662
  cwd: cfg.cwd
17623
18663
  });
17624
18664
  }
17625
18665
  __name(buildTransport, "buildTransport");
18666
+ function buildStdioServerEnv(baseEnv, serverEnv) {
18667
+ const env = {};
18668
+ for (const [key, value] of Object.entries(baseEnv)) {
18669
+ if (value !== void 0 && !key.startsWith("BASH_FUNC_") && !value.startsWith("()") && !SCRUBBED_STDIO_ENV_KEYS.has(key)) {
18670
+ env[key] = value;
18671
+ }
18672
+ }
18673
+ const merged = { ...env, ...serverEnv ?? {} };
18674
+ for (const key of SCRUBBED_STDIO_ENV_KEYS) {
18675
+ delete merged[key];
18676
+ }
18677
+ return merged;
18678
+ }
18679
+ __name(buildStdioServerEnv, "buildStdioServerEnv");
17626
18680
  function extractA2uiActionResult(result) {
17627
18681
  if (result.isError) {
17628
18682
  const errMsg2 = (result.content ?? []).filter(
@@ -17648,8 +18702,8 @@ function extractA2uiActionResult(result) {
17648
18702
  return { commands, fallback };
17649
18703
  }
17650
18704
  __name(extractA2uiActionResult, "extractA2uiActionResult");
17651
- async function callA2uiAction(cfg, args) {
17652
- const transport = buildTransport(cfg);
18705
+ async function callA2uiAction(cfg, args, env) {
18706
+ const transport = buildTransport(cfg, env);
17653
18707
  const client = new Client({ name: "qwen-serve-a2ui", version: "0.0.1" });
17654
18708
  try {
17655
18709
  await client.connect(transport, { timeout: CALL_TIMEOUT_MS });
@@ -17669,7 +18723,7 @@ async function callA2uiAction(cfg, args) {
17669
18723
  __name(callA2uiAction, "callA2uiAction");
17670
18724
  function registerA2uiActionRoutes(app, opts) {
17671
18725
  const { boundWorkspace, mutate, safeBody: safeBody2, getMcpServers } = opts;
17672
- const callAction = opts.callAction ?? callA2uiAction;
18726
+ const callAction = opts.callAction ?? ((cfg, args) => callA2uiAction(cfg, args, opts.env));
17673
18727
  app.post(
17674
18728
  "/session/:id/a2ui-action",
17675
18729
  mutate(),
@@ -17718,7 +18772,7 @@ __name(registerA2uiActionRoutes, "registerA2uiActionRoutes");
17718
18772
 
17719
18773
  // packages/cli/src/serve/server/fs-factory.ts
17720
18774
  init_esbuild_shims();
17721
- import * as path12 from "node:path";
18775
+ import * as path13 from "node:path";
17722
18776
  var IDE_WORKSPACE_PATH_ENV_VAR = "QWEN_CODE_IDE_WORKSPACE_PATH";
17723
18777
  function createDefaultFsAuditEmit() {
17724
18778
  const WARN_EVERY = 100;
@@ -17792,14 +18846,14 @@ function parseIdeWorkspacePathEnv(value) {
17792
18846
  const parsed = JSON.parse(value);
17793
18847
  if (Array.isArray(parsed) && parsed.every((item) => typeof item === "string")) {
17794
18848
  return parsed.filter(
17795
- (workspace) => workspace.length > 0 && path12.isAbsolute(workspace)
18849
+ (workspace) => workspace.length > 0 && path13.isAbsolute(workspace)
17796
18850
  );
17797
18851
  }
17798
18852
  throw new Error("IDE workspace path JSON must be a string array");
17799
18853
  } catch {
17800
18854
  }
17801
18855
  }
17802
- return value.split(path12.delimiter).filter((workspace) => workspace.length > 0 && path12.isAbsolute(workspace));
18856
+ return value.split(path13.delimiter).filter((workspace) => workspace.length > 0 && path13.isAbsolute(workspace));
17803
18857
  }
17804
18858
  __name(parseIdeWorkspacePathEnv, "parseIdeWorkspacePathEnv");
17805
18859
  function dropNestedWorkspacesPreservingPrimary(workspaces) {
@@ -17825,17 +18879,17 @@ __name(dropNestedWorkspacesPreservingPrimary, "dropNestedWorkspacesPreservingPri
17825
18879
  // packages/cli/src/serve/server/telemetry.ts
17826
18880
  init_esbuild_shims();
17827
18881
  function resolveDaemonTelemetryRoute(req) {
17828
- const path13 = req.path.replace(/\/$/, "") || "/";
17829
- if (req.method === "POST" && path13 === "/session") {
18882
+ const path14 = req.path.replace(/\/$/, "") || "/";
18883
+ if (req.method === "POST" && path14 === "/session") {
17830
18884
  return { route: "POST /session" };
17831
18885
  }
17832
- if (req.method === "POST" && path13 === "/sessions/delete") {
18886
+ if (req.method === "POST" && path14 === "/sessions/delete") {
17833
18887
  return { route: "POST /sessions/delete" };
17834
18888
  }
17835
- if (req.method === "GET" && path13 === "/daemon/status") {
18889
+ if (req.method === "GET" && path14 === "/daemon/status") {
17836
18890
  return { route: "GET /daemon/status" };
17837
18891
  }
17838
- const sessionAction = path13.match(
18892
+ const sessionAction = path14.match(
17839
18893
  /^\/session\/([^/]+)\/(load|resume|prompt|cancel|recap|btw|mid-turn-message|model|shell|detach|rewind|approval-mode|language|a2ui-action)$/
17840
18894
  );
17841
18895
  const sessionActionId = sessionAction?.[1];
@@ -17846,14 +18900,14 @@ function resolveDaemonTelemetryRoute(req) {
17846
18900
  sessionId: sessionActionId
17847
18901
  };
17848
18902
  }
17849
- const sessionMetadata = path13.match(/^\/session\/([^/]+)\/metadata$/);
18903
+ const sessionMetadata = path14.match(/^\/session\/([^/]+)\/metadata$/);
17850
18904
  if (sessionMetadata?.[1] && req.method === "PATCH") {
17851
18905
  return {
17852
18906
  route: "PATCH /session/:id/metadata",
17853
18907
  sessionId: sessionMetadata[1]
17854
18908
  };
17855
18909
  }
17856
- const sessionArtifacts = path13.match(/^\/session\/([^/]+)\/artifacts$/);
18910
+ const sessionArtifacts = path14.match(/^\/session\/([^/]+)\/artifacts$/);
17857
18911
  if (sessionArtifacts?.[1]) {
17858
18912
  if (req.method === "GET") {
17859
18913
  return {
@@ -17868,7 +18922,7 @@ function resolveDaemonTelemetryRoute(req) {
17868
18922
  };
17869
18923
  }
17870
18924
  }
17871
- const sessionArtifact = path13.match(
18925
+ const sessionArtifact = path14.match(
17872
18926
  /^\/session\/([^/]+)\/artifacts\/([^/]+)$/
17873
18927
  );
17874
18928
  if (sessionArtifact?.[1] && req.method === "DELETE") {
@@ -17877,7 +18931,7 @@ function resolveDaemonTelemetryRoute(req) {
17877
18931
  sessionId: sessionArtifact[1]
17878
18932
  };
17879
18933
  }
17880
- const sessionPermission = path13.match(
18934
+ const sessionPermission = path14.match(
17881
18935
  /^\/session\/([^/]+)\/permission\/([^/]+)$/
17882
18936
  );
17883
18937
  if (sessionPermission?.[1] && sessionPermission?.[2] && req.method === "POST") {
@@ -17888,7 +18942,7 @@ function resolveDaemonTelemetryRoute(req) {
17888
18942
  ...rawRequestId.length <= MAX_CLIENT_ID_LENGTH && CLIENT_ID_RE.test(rawRequestId) ? { permissionRequestId: rawRequestId } : {}
17889
18943
  };
17890
18944
  }
17891
- const globalPermission = path13.match(/^\/permission\/([^/]+)$/);
18945
+ const globalPermission = path14.match(/^\/permission\/([^/]+)$/);
17892
18946
  if (globalPermission?.[1] && req.method === "POST") {
17893
18947
  const rawRequestId = globalPermission[1];
17894
18948
  return {
@@ -17896,66 +18950,66 @@ function resolveDaemonTelemetryRoute(req) {
17896
18950
  ...rawRequestId.length <= MAX_CLIENT_ID_LENGTH && CLIENT_ID_RE.test(rawRequestId) ? { permissionRequestId: rawRequestId } : {}
17897
18951
  };
17898
18952
  }
17899
- const deleteSession = path13.match(/^\/session\/([^/]+)$/);
18953
+ const deleteSession = path14.match(/^\/session\/([^/]+)$/);
17900
18954
  const deleteSessionId = deleteSession?.[1];
17901
18955
  if (deleteSessionId && req.method === "DELETE") {
17902
18956
  return { route: "DELETE /session/:id", sessionId: deleteSessionId };
17903
18957
  }
17904
- if (req.method === "GET" && /^\/workspace\/[^/]+\/sessions$/.test(path13)) {
18958
+ if (req.method === "GET" && /^\/workspace\/[^/]+\/sessions$/.test(path14)) {
17905
18959
  return { route: "GET /workspace/:id/sessions" };
17906
18960
  }
17907
- if (req.method === "POST" && path13 === "/workspace/init") {
18961
+ if (req.method === "POST" && path14 === "/workspace/init") {
17908
18962
  return { route: "POST /workspace/init" };
17909
18963
  }
17910
- if (req.method === "POST" && path13 === "/workspace/setup-github") {
18964
+ if (req.method === "POST" && path14 === "/workspace/setup-github") {
17911
18965
  return { route: "POST /workspace/setup-github" };
17912
18966
  }
17913
- if (req.method === "POST" && path13 === "/workspace/reload") {
18967
+ if (req.method === "POST" && path14 === "/workspace/reload") {
17914
18968
  return { route: "POST /workspace/reload" };
17915
18969
  }
17916
- const mcpRestart = path13.match(/^\/workspace\/mcp\/([^/]+)\/restart$/);
18970
+ const mcpRestart = path14.match(/^\/workspace\/mcp\/([^/]+)\/restart$/);
17917
18971
  if (mcpRestart?.[1] && req.method === "POST") {
17918
18972
  return { route: "POST /workspace/mcp/:server/restart" };
17919
18973
  }
17920
- if (req.method === "POST" && path13 === "/workspace/mcp/servers") {
18974
+ if (req.method === "POST" && path14 === "/workspace/mcp/servers") {
17921
18975
  return { route: "POST /workspace/mcp/servers" };
17922
18976
  }
17923
- const mcpDelete = path13.match(/^\/workspace\/mcp\/servers\/([^/]+)$/);
18977
+ const mcpDelete = path14.match(/^\/workspace\/mcp\/servers\/([^/]+)$/);
17924
18978
  if (mcpDelete?.[1] && req.method === "DELETE") {
17925
18979
  return { route: "DELETE /workspace/mcp/servers/:name" };
17926
18980
  }
17927
- if (req.method === "POST" && path13 === "/workspace/auth/device-flow") {
18981
+ if (req.method === "POST" && path14 === "/workspace/auth/device-flow") {
17928
18982
  return { route: "POST /workspace/auth/device-flow" };
17929
18983
  }
17930
- const deviceFlowDelete = path13.match(
18984
+ const deviceFlowDelete = path14.match(
17931
18985
  /^\/workspace\/auth\/device-flow\/([^/]+)$/
17932
18986
  );
17933
18987
  if (deviceFlowDelete?.[1] && req.method === "DELETE") {
17934
18988
  return { route: "DELETE /workspace/auth/device-flow/:id" };
17935
18989
  }
17936
- const toolEnable = path13.match(/^\/workspace\/tools\/([^/]+)\/enable$/);
18990
+ const toolEnable = path14.match(/^\/workspace\/tools\/([^/]+)\/enable$/);
17937
18991
  if (toolEnable?.[1] && req.method === "POST") {
17938
18992
  return { route: "POST /workspace/tools/:name/enable" };
17939
18993
  }
17940
- if (path13 === "/workspace/settings") {
18994
+ if (path14 === "/workspace/settings") {
17941
18995
  if (req.method === "GET") return { route: "GET /workspace/settings" };
17942
18996
  if (req.method === "POST") return { route: "POST /workspace/settings" };
17943
18997
  }
17944
- if (path13 === "/workspace/permissions") {
18998
+ if (path14 === "/workspace/permissions") {
17945
18999
  if (req.method === "GET") return { route: "GET /workspace/permissions" };
17946
19000
  if (req.method === "POST") return { route: "POST /workspace/permissions" };
17947
19001
  }
17948
- if (path13 === "/workspace/trust") {
19002
+ if (path14 === "/workspace/trust") {
17949
19003
  if (req.method === "GET") return { route: "GET /workspace/trust" };
17950
19004
  }
17951
- if (req.method === "POST" && path13 === "/workspace/trust/request") {
19005
+ if (req.method === "POST" && path14 === "/workspace/trust/request") {
17952
19006
  return { route: "POST /workspace/trust/request" };
17953
19007
  }
17954
- if (path13 === "/workspace/voice") {
19008
+ if (path14 === "/workspace/voice") {
17955
19009
  if (req.method === "GET") return { route: "GET /workspace/voice" };
17956
19010
  if (req.method === "POST") return { route: "POST /workspace/voice" };
17957
19011
  }
17958
- if (req.method === "POST" && path13 === "/workspace/voice/transcribe") {
19012
+ if (req.method === "POST" && path14 === "/workspace/voice/transcribe") {
17959
19013
  return { route: "POST /workspace/voice/transcribe" };
17960
19014
  }
17961
19015
  return void 0;
@@ -18323,10 +19377,10 @@ function installRateLimiter(app, opts, daemonLog) {
18323
19377
  { tier, key: key.slice(0, 64) }
18324
19378
  );
18325
19379
  } : void 0,
18326
- onError: daemonLog ? (err, path13) => {
19380
+ onError: daemonLog ? (err, path14) => {
18327
19381
  daemonLog.warn(
18328
19382
  `rate limiter error (fail-open): ${err instanceof Error ? err.message : String(err)}`,
18329
- { path: path13 }
19383
+ { path: path14 }
18330
19384
  );
18331
19385
  } : void 0
18332
19386
  });
@@ -18377,73 +19431,6 @@ function installSelfOriginStripMiddleware(app, getPort) {
18377
19431
  }
18378
19432
  __name(installSelfOriginStripMiddleware, "installSelfOriginStripMiddleware");
18379
19433
 
18380
- // packages/cli/src/serve/workspace-registry.ts
18381
- init_esbuild_shims();
18382
- function createWorkspaceRegistry(inputRuntimes) {
18383
- if (inputRuntimes.length === 0) {
18384
- throw new Error(
18385
- "WorkspaceRegistry requires at least one workspace runtime."
18386
- );
18387
- }
18388
- const primaryRuntimes = inputRuntimes.filter((runtime) => runtime.primary);
18389
- if (primaryRuntimes.length !== 1) {
18390
- throw new Error(
18391
- "WorkspaceRegistry requires exactly one primary workspace runtime."
18392
- );
18393
- }
18394
- const byCwd = /* @__PURE__ */ new Map();
18395
- const byId = /* @__PURE__ */ new Map();
18396
- for (const runtime of inputRuntimes) {
18397
- if (byCwd.has(runtime.workspaceCwd)) {
18398
- throw new Error(
18399
- `Duplicate workspace runtime cwd ${JSON.stringify(
18400
- runtime.workspaceCwd
18401
- )}.`
18402
- );
18403
- }
18404
- byCwd.set(runtime.workspaceCwd, runtime);
18405
- if (byId.has(runtime.workspaceId)) {
18406
- throw new Error(
18407
- `Duplicate workspace runtime id ${JSON.stringify(
18408
- runtime.workspaceId
18409
- )}.`
18410
- );
18411
- }
18412
- byId.set(runtime.workspaceId, runtime);
18413
- }
18414
- const runtimes = Object.freeze([...inputRuntimes]);
18415
- const primary = primaryRuntimes[0];
18416
- return {
18417
- primary,
18418
- list: /* @__PURE__ */ __name(() => runtimes, "list"),
18419
- getByWorkspaceCwd: /* @__PURE__ */ __name((workspaceCwd) => byCwd.get(workspaceCwd), "getByWorkspaceCwd"),
18420
- getByWorkspaceId: /* @__PURE__ */ __name((workspaceId) => byId.get(workspaceId), "getByWorkspaceId"),
18421
- resolveWorkspaceCwd: /* @__PURE__ */ __name((workspaceCwd) => workspaceCwd === void 0 ? primary : byCwd.get(workspaceCwd), "resolveWorkspaceCwd"),
18422
- resolveLiveSessionOwner: /* @__PURE__ */ __name((sessionId) => {
18423
- const matches = [];
18424
- for (const runtime of runtimes) {
18425
- try {
18426
- runtime.bridge.getSessionSummary(sessionId);
18427
- matches.push(runtime);
18428
- } catch (err) {
18429
- if (err instanceof SessionNotFoundError) continue;
18430
- throw err;
18431
- }
18432
- }
18433
- if (matches.length === 0) return { kind: "not_found" };
18434
- if (matches.length === 1) {
18435
- return { kind: "found", runtime: matches[0] };
18436
- }
18437
- return { kind: "ambiguous", runtimes: matches };
18438
- }, "resolveLiveSessionOwner")
18439
- };
18440
- }
18441
- __name(createWorkspaceRegistry, "createWorkspaceRegistry");
18442
- function createSingleWorkspaceRegistry(runtime) {
18443
- return createWorkspaceRegistry([runtime]);
18444
- }
18445
- __name(createSingleWorkspaceRegistry, "createSingleWorkspaceRegistry");
18446
-
18447
19434
  // packages/cli/src/serve/routes/workspace-lifecycle.ts
18448
19435
  init_esbuild_shims();
18449
19436
  function registerWorkspaceLifecycleRoutes(app, deps) {
@@ -18746,6 +19733,22 @@ function describeRegistryPrimaryForConflict(registry) {
18746
19733
  return `registry primary cwd=${JSON.stringify(registry.primary.workspaceCwd)}, workspaceId=${JSON.stringify(registry.primary.workspaceId)}`;
18747
19734
  }
18748
19735
  __name(describeRegistryPrimaryForConflict, "describeRegistryPrimaryForConflict");
19736
+ function getRuntimeEffectiveEnv(metadata) {
19737
+ return metadata?.effectiveEnv;
19738
+ }
19739
+ __name(getRuntimeEffectiveEnv, "getRuntimeEffectiveEnv");
19740
+ var DEFAULT_SESSION_IDLE_TIMEOUT_MS = 30 * 6e4;
19741
+ var KEEPALIVE_MIN_INTERVAL_MS = 3e4;
19742
+ var KEEPALIVE_MAX_INTERVAL_MS = 10 * 6e4;
19743
+ function computeKeepaliveIntervalMs(idleTimeoutMs) {
19744
+ if (idleTimeoutMs <= 0) return KEEPALIVE_MAX_INTERVAL_MS;
19745
+ const target = Math.min(
19746
+ Math.max(KEEPALIVE_MIN_INTERVAL_MS, Math.floor(idleTimeoutMs / 3)),
19747
+ KEEPALIVE_MAX_INTERVAL_MS
19748
+ );
19749
+ return Math.max(1, Math.min(target, Math.floor(idleTimeoutMs / 2)));
19750
+ }
19751
+ __name(computeKeepaliveIntervalMs, "computeKeepaliveIntervalMs");
18749
19752
  function createServeApp(opts, getPort = () => opts.port, deps = {}) {
18750
19753
  const app = (0, import_express4.default)();
18751
19754
  const injectedWorkspaceRegistry = deps.workspaceRegistry;
@@ -18814,6 +19817,8 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
18814
19817
  );
18815
19818
  }
18816
19819
  const clientMcpSenderRegistry = injectedWorkspaceRegistry?.primary.clientMcpSenderRegistry ?? deps.clientMcpSenderRegistry ?? new ClientMcpSenderRegistry();
19820
+ const primaryRuntimeEnvMetadata = injectedWorkspaceRegistry?.primary.env ?? deps.primaryRuntimeEnv;
19821
+ const primaryEffectiveEnv = getRuntimeEffectiveEnv(primaryRuntimeEnvMetadata);
18817
19822
  const { languageCodes, currentServeFeatures, invalidateServeFeaturesCache } = createServeFeatures({
18818
19823
  opts,
18819
19824
  boundWorkspace,
@@ -18821,13 +19826,23 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
18821
19826
  // Registry injection supplies the primary workspace service through the
18822
19827
  // runtime, so it has the same reload surface as legacy deps.workspace.
18823
19828
  reloadAvailable: deps.workspace !== void 0 || injectedWorkspaceRegistry !== void 0,
18824
- sessionShellCommandEnabled
19829
+ sessionShellCommandEnabled,
19830
+ multiWorkspaceSessionsEnabled: (injectedWorkspaceRegistry?.list().length ?? 1) > 1
18825
19831
  });
18826
- const statusProvider = deps.statusProvider ?? createDaemonStatusProvider();
19832
+ const statusProvider = deps.statusProvider ?? createDaemonStatusProvider(
19833
+ primaryEffectiveEnv ? { env: primaryEffectiveEnv } : {}
19834
+ );
19835
+ let defaultBridgeForAdmission;
19836
+ const totalSessionAdmission = !deps.bridge && !injectedWorkspaceRegistry ? createTotalSessionAdmissionController({
19837
+ maxTotalSessions: opts.maxTotalSessions,
19838
+ getBridges: /* @__PURE__ */ __name(() => defaultBridgeForAdmission ? [defaultBridgeForAdmission] : [], "getBridges")
19839
+ }) : void 0;
18827
19840
  const bridge = injectedWorkspaceRegistry?.primary.bridge ?? deps.bridge ?? createAcpSessionBridge({
18828
19841
  maxSessions: opts.maxSessions,
19842
+ ...totalSessionAdmission ? { freshSessionAdmission: totalSessionAdmission.admit } : {},
18829
19843
  maxPendingPromptsPerSession: opts.maxPendingPromptsPerSession,
18830
19844
  eventRingSize: opts.eventRingSize,
19845
+ compactedReplayMaxBytes: opts.compactedReplayMaxBytes,
18831
19846
  permissionResponseTimeoutMs: opts.permissionResponseTimeoutMs,
18832
19847
  boundWorkspace,
18833
19848
  sessionShellCommandEnabled,
@@ -18841,6 +19856,9 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
18841
19856
  // ext-method by reaching the WS connection that hosts the named server.
18842
19857
  clientMcpSender: clientMcpSenderRegistry.lookup
18843
19858
  });
19859
+ if (!injectedWorkspaceRegistry && !deps.bridge) {
19860
+ defaultBridgeForAdmission = bridge;
19861
+ }
18844
19862
  const archiveCoordinator = new SessionArchiveCoordinator();
18845
19863
  installSelfOriginStripMiddleware(app, getPort);
18846
19864
  app.locals.fsFactory = fsFactory;
@@ -18858,7 +19876,9 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
18858
19876
  boundWorkspace,
18859
19877
  contextFilename: deps.contextFilename ?? "QWEN.md",
18860
19878
  statusProvider,
18861
- workspaceProvidersStatusProvider: createWorkspaceProvidersStatusProvider(),
19879
+ workspaceProvidersStatusProvider: createWorkspaceProvidersStatusProvider(
19880
+ primaryEffectiveEnv ? { env: primaryEffectiveEnv } : {}
19881
+ ),
18862
19882
  workspaceSkillsStatusProvider: createWorkspaceSkillsStatusProvider(),
18863
19883
  isChannelLive: /* @__PURE__ */ __name(() => bridge.isChannelLive(), "isChannelLive"),
18864
19884
  persistDisabledTools: deps.persistDisabledTools ?? (async () => {
@@ -18883,7 +19903,10 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
18883
19903
  workspaceCwd: boundWorkspace,
18884
19904
  primary: true,
18885
19905
  trusted: deps.primaryWorkspaceTrusted ?? false,
18886
- env: { mode: "parent-process", overlayKeys: [] },
19906
+ env: primaryRuntimeEnvMetadata ?? {
19907
+ mode: "parent-process",
19908
+ overlayKeys: []
19909
+ },
18887
19910
  bridge,
18888
19911
  workspaceService: workspace,
18889
19912
  routeFileSystemFactory: fsFactory,
@@ -18949,6 +19972,7 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
18949
19972
  opts,
18950
19973
  boundWorkspace: primaryBoundWorkspace,
18951
19974
  bridge: primaryBridge,
19975
+ workspaceRegistry,
18952
19976
  workspace: primaryWorkspace,
18953
19977
  daemonLog,
18954
19978
  startup: deps.startup,
@@ -18962,14 +19986,18 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
18962
19986
  sessionShellCommandEnabled,
18963
19987
  getChannelWorkerSnapshot: deps.getChannelWorkerSnapshot,
18964
19988
  getPerfSnapshot: deps.getPerfSnapshot,
18965
- getMetricsSeries: deps.getMetricsSeries
19989
+ getMetricsSeries: deps.getMetricsSeries,
19990
+ getTotalSessionAdmissionSnapshot: deps.getTotalSessionAdmissionSnapshot ?? totalSessionAdmission?.snapshot
18966
19991
  });
18967
19992
  registerCapabilitiesRoutes(app, {
18968
19993
  qwenCodeVersion: deps.qwenCodeVersion,
18969
19994
  mode: opts.mode,
18970
19995
  currentServeFeatures,
18971
19996
  boundWorkspace: primaryBoundWorkspace,
19997
+ workspaceRegistry,
18972
19998
  permissionPolicy: primaryBridge.permissionPolicy,
19999
+ maxSessionsPerWorkspace: opts.maxSessions,
20000
+ maxTotalSessions: opts.maxTotalSessions,
18973
20001
  maxPendingPromptsPerSession: opts.maxPendingPromptsPerSession,
18974
20002
  languageCodes
18975
20003
  });
@@ -19080,6 +20108,7 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
19080
20108
  boundWorkspace: primaryBoundWorkspace,
19081
20109
  mutate,
19082
20110
  safeBody,
20111
+ env: getRuntimeEffectiveEnv(primaryRuntime.env),
19083
20112
  // UI-server discovery uses the daemon's workspace MCP status, which
19084
20113
  // includes servers registered at runtime.
19085
20114
  getMcpServers: /* @__PURE__ */ __name(async () => {
@@ -19100,6 +20129,7 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
19100
20129
  registerSessionRoutes(app, {
19101
20130
  boundWorkspace: primaryBoundWorkspace,
19102
20131
  bridge: primaryBridge,
20132
+ workspaceRegistry,
19103
20133
  archiveCoordinator,
19104
20134
  mutate,
19105
20135
  sendBridgeError: sendBridgeError2,
@@ -19137,16 +20167,49 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
19137
20167
  registerScheduledTasksRoutes(app, {
19138
20168
  boundWorkspace: primaryBoundWorkspace,
19139
20169
  mutate,
19140
- safeBody
20170
+ safeBody,
20171
+ bridge: deps.manageScheduledTaskSessions ? bridge : void 0
19141
20172
  });
19142
20173
  registerUsageStatsRoutes(app);
20174
+ if (deps.manageScheduledTaskSessions) {
20175
+ const idleTimeoutMs = opts.sessionIdleTimeoutMs ?? DEFAULT_SESSION_IDLE_TIMEOUT_MS;
20176
+ const keepalive = startScheduledTaskKeepalive({
20177
+ bridge,
20178
+ boundWorkspace,
20179
+ intervalMs: computeKeepaliveIntervalMs(idleTimeoutMs)
20180
+ });
20181
+ app.locals.stopScheduledTaskKeepalive = keepalive.stop;
20182
+ void rehydrateScheduledTaskSessions({
20183
+ bridge,
20184
+ boundWorkspace,
20185
+ onError: /* @__PURE__ */ __name((sessionId, err) => {
20186
+ process.stderr.write(
20187
+ `qwen serve: failed to rehydrate scheduled-task session ${sessionId}: ${err instanceof Error ? err.message : String(err)}
20188
+ `
20189
+ );
20190
+ }, "onError")
20191
+ // Outer catch is defense-in-depth: rehydrateScheduledTaskSessions already
20192
+ // catches readCronTasks failures and per-session load errors internally
20193
+ // (returning { loaded, failed }), so this only guards an unexpected throw
20194
+ // from the function entry itself. Log rather than swallow it — a silent
20195
+ // failure here leaves every bound task dormant with no diagnostic.
20196
+ }).catch((err) => {
20197
+ process.stderr.write(
20198
+ `qwen serve: unexpected scheduled-task rehydration failure: ${err instanceof Error ? err.message : String(err)}
20199
+ `
20200
+ );
20201
+ });
20202
+ }
19143
20203
  registerPermissionRoutes(app, {
19144
20204
  bridge: primaryBridge,
20205
+ workspaceRegistry,
20206
+ daemonLog,
19145
20207
  mutate,
19146
20208
  sendPermissionVoteError: sendPermissionVoteError2
19147
20209
  });
19148
20210
  registerSseEventsRoutes(app, {
19149
20211
  bridge: primaryBridge,
20212
+ workspaceRegistry,
19150
20213
  daemonLog,
19151
20214
  writerIdleTimeoutMs: opts.writerIdleTimeoutMs,
19152
20215
  sendBridgeError: sendBridgeError2
@@ -19189,7 +20252,9 @@ function createServeApp(opts, getPort = () => opts.port, deps = {}) {
19189
20252
  extraWsRoutes: [
19190
20253
  {
19191
20254
  path: "/voice/stream",
19192
- onConnection: createVoiceWsConnectionHandler(primaryBoundWorkspace)
20255
+ onConnection: createVoiceWsConnectionHandler(primaryBoundWorkspace, {
20256
+ env: getRuntimeEffectiveEnv(primaryRuntime.env)
20257
+ })
19193
20258
  }
19194
20259
  ]
19195
20260
  });
@@ -19209,6 +20274,7 @@ __name(createServeApp, "createServeApp");
19209
20274
  export {
19210
20275
  InvalidCursorError,
19211
20276
  PromptDeadlineExceededError,
20277
+ computeKeepaliveIntervalMs,
19212
20278
  createDefaultFsAuditEmit,
19213
20279
  createServeApp,
19214
20280
  detectFromLoopback,