@qwen-code/qwen-code 0.19.3-nightly.20260701.a974594d7 → 0.19.4

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 (234) hide show
  1. package/bundled/loop/loop-tick-resolver.test.ts +42 -0
  2. package/bundled/loop/loop-tick-resolver.ts +1 -1
  3. package/bundled/qc-helper/docs/configuration/auth.md +1 -1
  4. package/bundled/qc-helper/docs/configuration/settings.md +23 -11
  5. package/bundled/qc-helper/docs/features/approval-mode.md +3 -0
  6. package/bundled/qc-helper/docs/features/auto-mode.md +40 -0
  7. package/bundled/qc-helper/docs/features/channels/overview.md +81 -18
  8. package/bundled/qc-helper/docs/features/code-review.md +17 -61
  9. package/bundled/qc-helper/docs/features/commands.md +6 -6
  10. package/bundled/qc-helper/docs/features/mcp.md +3 -1
  11. package/bundled/qc-helper/docs/features/scheduled-tasks.md +10 -0
  12. package/bundled/qc-helper/docs/qwen-serve.md +19 -0
  13. package/bundled/review/DESIGN.md +21 -26
  14. package/bundled/review/SKILL.md +45 -129
  15. package/chunks/{MaxSizedBox-UZHJ73JB.js → MaxSizedBox-ERTNFI22.js} +20 -18
  16. package/chunks/{StandaloneSessionPicker-GGUIZWCE.js → StandaloneSessionPicker-Y5NGRLRY.js} +32 -29
  17. package/chunks/{acpAgent-QYZFICEZ.js → acpAgent-UMN6OYTR.js} +473 -187
  18. package/chunks/{agent-2EN7YNSW.js → agent-56DZU4ZU.js} +17 -15
  19. package/chunks/{agent-headless-SLTEZ5LV.js → agent-headless-SR7N2IJG.js} +17 -15
  20. package/chunks/{anthropicContentGenerator-CA3JTAMO.js → anthropicContentGenerator-5OZFO6WQ.js} +1 -1
  21. package/chunks/{artifact-tool-RM7WINJO.js → artifact-tool-JVZMHA4B.js} +1 -1
  22. package/chunks/{askUserQuestion-J3P6QAJ7.js → askUserQuestion-CMJZ2F3D.js} +1 -1
  23. package/chunks/{bridge-HZVAUTMX.js → bridge-AUHNFOUS.js} +23 -21
  24. package/chunks/channel-worker-supervisor-A74FV522.js +308 -0
  25. package/chunks/{chunk-N4V4VBP6.js → chunk-2X2JUE47.js} +2 -2
  26. package/chunks/{chunk-JV7A66DR.js → chunk-4VZH35PN.js} +528 -358
  27. package/chunks/{chunk-M2U5NM24.js → chunk-54YZOP7P.js} +309 -72
  28. package/chunks/{chunk-WGY3GIPP.js → chunk-6AQR7732.js} +1 -1
  29. package/chunks/{chunk-FR3II765.js → chunk-6CIIP6OE.js} +4 -4
  30. package/chunks/{chunk-IKO44NAT.js → chunk-6TLY5W3B.js} +2 -2
  31. package/chunks/{chunk-VN4CKYUW.js → chunk-73H73ASU.js} +1 -1
  32. package/chunks/{chunk-IBEM64M4.js → chunk-75X2VLSH.js} +36 -25
  33. package/chunks/{chunk-PLQTLKMR.js → chunk-7KPI4LAI.js} +3 -3
  34. package/chunks/{chunk-NVTRORSK.js → chunk-7VPC7D7O.js} +2 -2
  35. package/chunks/chunk-AHNXYU4O.js +24 -0
  36. package/chunks/{chunk-MTJVHQDP.js → chunk-B7KGXIPZ.js} +302 -257
  37. package/chunks/{chunk-35FH347N.js → chunk-BFG6OZN7.js} +16097 -16236
  38. package/chunks/{chunk-T44AF4I6.js → chunk-BNJLLAQ7.js} +1 -1
  39. package/chunks/{chunk-OEY3YAL2.js → chunk-BQNJBEUA.js} +2 -2
  40. package/chunks/{chunk-Q7TDHJ2G.js → chunk-C2IHHGBZ.js} +10 -10
  41. package/chunks/chunk-CG7SVARK.js +367 -0
  42. package/chunks/{chunk-7SP275SH.js → chunk-CS5XYDBO.js} +5 -5
  43. package/chunks/{chunk-V5YJAUHW.js → chunk-CUGEBAKG.js} +1 -1
  44. package/chunks/{chunk-FQDMX5IL.js → chunk-D5KISTLW.js} +309 -72
  45. package/chunks/{chunk-I4MXURND.js → chunk-DQ74W65K.js} +356 -468
  46. package/chunks/chunk-E5A7LHNN.js +51 -0
  47. package/chunks/{chunk-3PJXIDKI.js → chunk-E6US47KI.js} +3 -27
  48. package/chunks/chunk-EDAGQADF.js +21 -0
  49. package/chunks/{chunk-HUGAEYZR.js → chunk-FNLXLI4I.js} +2 -2
  50. package/chunks/{chunk-WJAY4WXR.js → chunk-FYKX6XOT.js} +1 -1
  51. package/chunks/{chunk-CA4ML3VH.js → chunk-G7OQ3RFJ.js} +1 -1
  52. package/chunks/{chunk-VHRREANT.js → chunk-GICCHL2Q.js} +3 -1
  53. package/chunks/{chunk-QHHNFG22.js → chunk-GLPV75ES.js} +1 -1
  54. package/chunks/{chunk-LD2XBG6Z.js → chunk-GO6LNQXT.js} +1 -39
  55. package/chunks/chunk-HI55Z6PR.js +3523 -0
  56. package/chunks/{chunk-B5MPZI4K.js → chunk-HJDF3ZVU.js} +2670 -1233
  57. package/chunks/{chunk-GDIG7TMG.js → chunk-HRZB5CSC.js} +1 -1
  58. package/chunks/{chunk-LDWTYE33.js → chunk-J3F7X3LW.js} +1 -1
  59. package/chunks/{chunk-FV4SQ54H.js → chunk-KVPFPC4J.js} +227 -85
  60. package/chunks/{chunk-Q3JOGB4U.js → chunk-L7QPM2YD.js} +1 -1
  61. package/chunks/{chunk-4722VFFU.js → chunk-L7VH7HQA.js} +6 -6
  62. package/chunks/{chunk-HWG4XLD7.js → chunk-LKWAVAB5.js} +1 -1
  63. package/chunks/{chunk-YCRTJYSQ.js → chunk-MEQWZGK5.js} +1 -1
  64. package/chunks/{chunk-6FIWYVN4.js → chunk-MKHC4BYZ.js} +30 -5
  65. package/chunks/{chunk-YCEHOJZJ.js → chunk-MNDTD5E5.js} +65 -38
  66. package/chunks/{chunk-ZGSV5ORL.js → chunk-MVZXZWGT.js} +809 -759
  67. package/chunks/chunk-MX2YXRER.js +44 -0
  68. package/chunks/{chunk-TMILTLP3.js → chunk-ND25GXEU.js} +21 -4
  69. package/chunks/{chunk-AIBPG5MB.js → chunk-OAFUMTWW.js} +2 -2
  70. package/chunks/chunk-OJCECIJA.js +238 -0
  71. package/chunks/{chunk-EUURTJY7.js → chunk-PIYV6COW.js} +1 -1
  72. package/chunks/{chunk-LXEW6BHF.js → chunk-Q7VHIRDD.js} +1 -1
  73. package/chunks/{chunk-DBR7PF4B.js → chunk-QAFQLTXG.js} +1 -1
  74. package/chunks/{chunk-3TK7FSCP.js → chunk-QMIND2AS.js} +3 -3
  75. package/chunks/{chunk-ESVSWG6Z.js → chunk-QOMAHFYI.js} +1 -1
  76. package/chunks/{chunk-J37FGIOA.js → chunk-QXPI7FEC.js} +38 -4
  77. package/chunks/chunk-RM3PGSGL.js +66 -0
  78. package/chunks/chunk-RNBYOUGV.js +42 -0
  79. package/chunks/chunk-S5EQPDTM.js +91 -0
  80. package/chunks/{chunk-GZKQ36JS.js → chunk-SFN4TEHY.js} +4 -4
  81. package/chunks/{chunk-DHXWNSJA.js → chunk-T22H4V4R.js} +42 -0
  82. package/chunks/{chunk-LXGWLOCM.js → chunk-TENOUWY2.js} +1 -1
  83. package/chunks/{chunk-BLAE2TCO.js → chunk-TTFNVEO5.js} +3 -3
  84. package/chunks/{chunk-FZY7NH37.js → chunk-UANCYOOQ.js} +1 -1
  85. package/chunks/{chunk-E5I6YSNO.js → chunk-UQFP2FEF.js} +1 -1
  86. package/chunks/{chunk-3O2BJMFW.js → chunk-UQYOMM6J.js} +4 -4
  87. package/chunks/{chunk-JERW36RG.js → chunk-UT5JU5DJ.js} +15 -15
  88. package/chunks/{chunk-DJ25MIU2.js → chunk-VKGG724L.js} +8 -8
  89. package/chunks/{chunk-G525INVW.js → chunk-W2ZOAX7F.js} +3 -3
  90. package/chunks/{chunk-HIYUAXBG.js → chunk-W6X4FGWX.js} +1 -1
  91. package/chunks/{chunk-OE5J6UA7.js → chunk-XCUFHOAD.js} +1 -1
  92. package/chunks/{chunk-KXVVXAAC.js → chunk-YBDZ63KI.js} +3 -3
  93. package/chunks/{chunk-Z34RYLRO.js → chunk-ZJP5T7IA.js} +1 -1
  94. package/chunks/{chunk-NOYV2NXI.js → chunk-ZXCVPMIK.js} +411 -43
  95. package/chunks/{computer-use-FCNWHVPW.js → computer-use-VWN5HH6A.js} +20 -18
  96. package/chunks/{contextCommand-FVFMZZEU.js → contextCommand-DFFG5KDJ.js} +19 -17
  97. package/chunks/{cron-create-IY5NMIHQ.js → cron-create-CSSWJ67I.js} +1 -1
  98. package/chunks/{cron-delete-OLNEQWKO.js → cron-delete-E56CFQ7A.js} +1 -1
  99. package/chunks/{cron-list-YAUAUV24.js → cron-list-BKSINY6Y.js} +1 -1
  100. package/chunks/daemon-5T6IHWJV.js +8430 -0
  101. package/chunks/{daemon-status-provider-42JEXENZ.js → daemon-status-provider-V5UY7O7J.js} +26 -24
  102. package/chunks/{dist-4FCNM6KJ.js → dist-2HDL5SHQ.js} +2 -2
  103. package/chunks/{dist-SU6L7ZNJ.js → dist-B7SSDVWT.js} +3 -3
  104. package/chunks/{dist-VD7HXTML.js → dist-BL3SL665.js} +1 -1
  105. package/chunks/{dist-UKPWQJCL.js → dist-TAVAGZU6.js} +37 -13
  106. package/chunks/{dist-K4XZILIO.js → dist-XBOVZ3R5.js} +26 -5
  107. package/chunks/{earlyInputCapture-FFFEC6P4.js → earlyInputCapture-VVYU4FDO.js} +17 -15
  108. package/chunks/{edit-32ILSGZY.js → edit-F35PTMBA.js} +17 -15
  109. package/chunks/{en-U3LWGSFX.js → en-FZREZFPO.js} +18 -0
  110. package/chunks/{enter-worktree-XWZKIN6A.js → enter-worktree-3DIW7EGJ.js} +17 -15
  111. package/chunks/{enterPlanMode-LPFFY7BJ.js → enterPlanMode-QNXT77KP.js} +27 -15
  112. package/chunks/{errors-UXBPYPI2.js → errors-M53YG76R.js} +19 -17
  113. package/chunks/{exit-worktree-IBS4UIU7.js → exit-worktree-BSDWNWHZ.js} +17 -15
  114. package/chunks/{exitPlanMode-JA72YHII.js → exitPlanMode-EL7AY327.js} +41 -23
  115. package/chunks/{fast-path-7UU67Y2W.js → fast-path-DUCSMUFW.js} +2 -2
  116. package/chunks/{gemini-GMVKJNMX.js → gemini-F3IERB37.js} +59 -51
  117. package/chunks/{geminiContentGenerator-7NIFF2L4.js → geminiContentGenerator-6OULAKFQ.js} +1 -1
  118. package/chunks/{glob-4NJCW5HI.js → glob-ZMLFTD4T.js} +17 -15
  119. package/chunks/{grep-PAIMLQEL.js → grep-H5X66EI7.js} +17 -15
  120. package/chunks/{initializer-R6KU4VYM.js → initializer-LEOT2OLH.js} +21 -19
  121. package/chunks/{list-H7LXAO64.js → list-M3DKXML6.js} +23 -21
  122. package/chunks/{loadedSettingsAdapter-H27JMTLU.js → loadedSettingsAdapter-OKMQFGGG.js} +21 -19
  123. package/chunks/{loop-wakeup-Y3OIRQFM.js → loop-wakeup-FPBAVVPR.js} +2 -2
  124. package/chunks/{ls-TVJEYS7O.js → ls-UGWQXPTY.js} +1 -1
  125. package/chunks/{lsp-JZSDVR3F.js → lsp-24WV7NXB.js} +1 -1
  126. package/chunks/{monitor-JSIHDPXP.js → monitor-ARL5P45R.js} +19 -16
  127. package/chunks/{nonInteractiveCli-Y4CHOETG.js → nonInteractiveCli-NUVLF3YR.js} +53 -45
  128. package/chunks/{notebook-edit-SHRNL2UM.js → notebook-edit-ANTZ2DDY.js} +17 -15
  129. package/chunks/{openaiContentGenerator-ORZZ7VFO.js → openaiContentGenerator-TFGMKGHN.js} +6 -6
  130. package/chunks/pidfile-AWTL4C63.js +72 -0
  131. package/chunks/{qwenContentGenerator-H643PL2G.js → qwenContentGenerator-6QWAYTWS.js} +18 -16
  132. package/chunks/{qwenOAuth2-OI2B7PES.js → qwenOAuth2-LWLA6MQG.js} +3 -3
  133. package/chunks/{read-file-5PRNPTTY.js → read-file-EJY73XQ3.js} +4 -4
  134. package/chunks/{read-mcp-resource-QTXGKTEV.js → read-mcp-resource-NCBTPP7X.js} +1 -1
  135. package/chunks/{ripGrep-JIKZMABJ.js → ripGrep-HC4SA6CQ.js} +17 -15
  136. package/chunks/{run-qwen-serve-PQV7QOHZ.js → run-qwen-serve-AGLZ642J.js} +291 -55
  137. package/chunks/{scheduler-OS3R5MGU.js → scheduler-TURO7NGY.js} +17 -15
  138. package/chunks/{send-message-DKGHWSTF.js → send-message-3S2NYS3O.js} +5 -3
  139. package/chunks/{serve-LVYZDKCZ.js → serve-5DAMOIDW.js} +22 -19
  140. package/chunks/{server-P4YIH35I.js → server-4AGEK2HC.js} +2268 -1222
  141. package/chunks/{session-DE4TZBKH.js → session-7ZOQUC4Y.js} +54 -46
  142. package/chunks/{settings-DFTYMJYF.js → settings-DR4V6L5Y.js} +20 -18
  143. package/chunks/{shell-3GNQLNV7.js → shell-H5WFKUXH.js} +17 -15
  144. package/chunks/{skill-NCNG4KKZ.js → skill-ANHU5ZCR.js} +4 -4
  145. package/chunks/{spawnChannel-DHRIMDVL.js → spawnChannel-VN5LJV7Z.js} +19 -17
  146. package/chunks/{src-PNZYGM3R.js → src-OVZLTVVM.js} +49 -17
  147. package/chunks/{startInteractiveUI-P6GZBFQW.js → startInteractiveUI-OA22V32P.js} +11110 -10670
  148. package/chunks/{syntheticOutput-X7L5HWLN.js → syntheticOutput-SV7GBHZ5.js} +2 -2
  149. package/chunks/{task-create-AWEUI4GI.js → task-create-W2H2UJCH.js} +6 -5
  150. package/chunks/{task-list-KZ3VJLDU.js → task-list-THBOPV4G.js} +5 -4
  151. package/chunks/{task-stop-GUNOZKJ5.js → task-stop-33FVCNCJ.js} +1 -1
  152. package/chunks/{task-update-Y56GP6DU.js → task-update-LVFQLXEI.js} +6 -5
  153. package/chunks/{team-create-6JMW3KQK.js → team-create-OYL5CAQO.js} +17 -15
  154. package/chunks/{team-delete-6AFRGDJY.js → team-delete-NTUQ25UY.js} +5 -4
  155. package/chunks/{theme-manager-FEHQO22G.js → theme-manager-XCMPF2SI.js} +17 -15
  156. package/chunks/{todoWrite-JRS6NOEU.js → todoWrite-SEPO2RI3.js} +1 -1
  157. package/chunks/{tool-search-IXAEKS7J.js → tool-search-YXW55X6Z.js} +29 -5
  158. package/chunks/{trustedFolders-SJRXNHZE.js → trustedFolders-TEPGM5YL.js} +18 -16
  159. package/chunks/{validateNonInterActiveAuth-HCH7OBDH.js → validateNonInterActiveAuth-OQIS7WUE.js} +50 -42
  160. package/chunks/{web-fetch-RELNL37A.js → web-fetch-OE7LHURA.js} +4 -4
  161. package/chunks/{workflow-M2COBI7R.js → workflow-RTUWORAP.js} +48 -27
  162. package/chunks/{workspace-providers-status-GOCWYDMK.js → workspace-providers-status-DM2XANVB.js} +23 -21
  163. package/chunks/{workspace-service-34C7SUBU.js → workspace-service-VKBI2KZD.js} +27 -25
  164. package/chunks/{write-file-E52WRXZL.js → write-file-WGALQ53L.js} +17 -15
  165. package/chunks/{zh-TW-LKLBTZHE.js → zh-TW-6GIKXEAN.js} +18 -0
  166. package/chunks/{zh-F5T4WWZO.js → zh-V3B4TFWL.js} +18 -0
  167. package/cli.js +6 -6
  168. package/locales/en.js +30 -0
  169. package/locales/fr.js +1 -2
  170. package/locales/ja.js +1 -2
  171. package/locales/zh-TW.js +29 -0
  172. package/locales/zh.js +29 -0
  173. package/node_modules/@qwen-code/audio-capture/package.json +1 -1
  174. package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-arm64/@qwen-code+audio-capture.node +0 -0
  175. package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-x64/@qwen-code+audio-capture.node +0 -0
  176. package/node_modules/@qwen-code/audio-capture/prebuilds/win32-x64/@qwen-code+audio-capture.node +0 -0
  177. package/package.json +3 -3
  178. package/web-shell/assets/{arc-C2DILvUl.js → arc-Det5Ccum.js} +1 -1
  179. package/web-shell/assets/{architectureDiagram-3BPJPVTR-b8gZ2cNw.js → architectureDiagram-3BPJPVTR-DqNWVFpl.js} +1 -1
  180. package/web-shell/assets/{blockDiagram-GPEHLZMM-BuRpghMj.js → blockDiagram-GPEHLZMM-CYl7tYBi.js} +1 -1
  181. package/web-shell/assets/{c4Diagram-AAUBKEIU-2ItdhFmE.js → c4Diagram-AAUBKEIU-DaAEi0NQ.js} +1 -1
  182. package/web-shell/assets/channel-Bm0bFauD.js +1 -0
  183. package/web-shell/assets/{chunk-2J33WTMH-B1QXa0q3.js → chunk-2J33WTMH-Df2CDEE2.js} +1 -1
  184. package/web-shell/assets/{chunk-4BX2VUAB-DXfhKlNq.js → chunk-4BX2VUAB-DNBFcyL2.js} +1 -1
  185. package/web-shell/assets/{chunk-55IACEB6-ClSNjxLo.js → chunk-55IACEB6-BKjF-0HI.js} +1 -1
  186. package/web-shell/assets/{chunk-727SXJPM-Dsh2wisB.js → chunk-727SXJPM-DYwAwfIM.js} +1 -1
  187. package/web-shell/assets/{chunk-AQP2D5EJ-C1TkY5AR.js → chunk-AQP2D5EJ-C6FswLqy.js} +1 -1
  188. package/web-shell/assets/{chunk-FMBD7UC4-CGtN9XR6.js → chunk-FMBD7UC4-DRWAcZSY.js} +1 -1
  189. package/web-shell/assets/{chunk-ND2GUHAM-BHiPi1SS.js → chunk-ND2GUHAM-AoL16mNR.js} +1 -1
  190. package/web-shell/assets/{chunk-QZHKN3VN-CzG3I5o1.js → chunk-QZHKN3VN-a8PtLMck.js} +1 -1
  191. package/web-shell/assets/classDiagram-4FO5ZUOK--NgUfb55.js +1 -0
  192. package/web-shell/assets/classDiagram-v2-Q7XG4LA2--NgUfb55.js +1 -0
  193. package/web-shell/assets/{cose-bilkent-S5V4N54A-EgrIoH_K.js → cose-bilkent-S5V4N54A-C4Aeu5qf.js} +1 -1
  194. package/web-shell/assets/{dagre-BM42HDAG-BUnpdX8S.js → dagre-BM42HDAG-BOulamjo.js} +1 -1
  195. package/web-shell/assets/{diagram-2AECGRRQ-BlLUCVYu.js → diagram-2AECGRRQ-D1wt2ykO.js} +1 -1
  196. package/web-shell/assets/{diagram-5GNKFQAL-FG-enmjQ.js → diagram-5GNKFQAL-BRseus-O.js} +1 -1
  197. package/web-shell/assets/{diagram-KO2AKTUF-BlzmTONk.js → diagram-KO2AKTUF-XSMkNzpZ.js} +1 -1
  198. package/web-shell/assets/{diagram-LMA3HP47-CoBQ8mIx.js → diagram-LMA3HP47-21KNLqoi.js} +1 -1
  199. package/web-shell/assets/{diagram-OG6HWLK6-DvxV6sX8.js → diagram-OG6HWLK6-DnUI8F5G.js} +1 -1
  200. package/web-shell/assets/{erDiagram-TEJ5UH35-Jq1tpAoj.js → erDiagram-TEJ5UH35-BVSOMuM2.js} +1 -1
  201. package/web-shell/assets/{flowDiagram-I6XJVG4X-CbtYOry5.js → flowDiagram-I6XJVG4X-fYtDPJsW.js} +1 -1
  202. package/web-shell/assets/{ganttDiagram-6RSMTGT7-ZEn2VEKR.js → ganttDiagram-6RSMTGT7-CmRXosW_.js} +1 -1
  203. package/web-shell/assets/{gitGraphDiagram-PVQCEYII-BZh-LkhJ.js → gitGraphDiagram-PVQCEYII-DPrmySxO.js} +1 -1
  204. package/web-shell/assets/index-BpKnmyFV.js +717 -0
  205. package/web-shell/assets/index-fLrtxpOG.css +5 -0
  206. package/web-shell/assets/{infoDiagram-5YYISTIA-B9HuZygK.js → infoDiagram-5YYISTIA-x3VbEuCV.js} +1 -1
  207. package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-D76cNpYG.js → ishikawaDiagram-YF4QCWOH-SmitHRMC.js} +1 -1
  208. package/web-shell/assets/{journeyDiagram-JHISSGLW-6bGo7P1t.js → journeyDiagram-JHISSGLW-BxvA551S.js} +1 -1
  209. package/web-shell/assets/{kanban-definition-UN3LZRKU-DuYyRBvB.js → kanban-definition-UN3LZRKU-DNHC16jY.js} +1 -1
  210. package/web-shell/assets/{linear-CpdSW1BC.js → linear-CQ6irBL9.js} +1 -1
  211. package/web-shell/assets/{mermaid.core-s5xbZMMj.js → mermaid.core-DtB6NO1e.js} +72 -72
  212. package/web-shell/assets/{mindmap-definition-RKZ34NQL-go01fZ2y.js → mindmap-definition-RKZ34NQL-QKMAgV2v.js} +1 -1
  213. package/web-shell/assets/{pieDiagram-4H26LBE5-BfuZiEHO.js → pieDiagram-4H26LBE5-bvb6dGn6.js} +1 -1
  214. package/web-shell/assets/{quadrantDiagram-W4KKPZXB-DsoINqV6.js → quadrantDiagram-W4KKPZXB-DLgdlG4D.js} +1 -1
  215. package/web-shell/assets/{requirementDiagram-4Y6WPE33-B7FU4LiU.js → requirementDiagram-4Y6WPE33-2M9YH4GF.js} +1 -1
  216. package/web-shell/assets/{sankeyDiagram-5OEKKPKP-BEDjFnGB.js → sankeyDiagram-5OEKKPKP-DzwfclTH.js} +1 -1
  217. package/web-shell/assets/{sequenceDiagram-3UESZ5HK-gzmmL5tK.js → sequenceDiagram-3UESZ5HK-BvPmTuy2.js} +1 -1
  218. package/web-shell/assets/{stateDiagram-AJRCARHV-BtHD4IN_.js → stateDiagram-AJRCARHV-BjwUmrJF.js} +1 -1
  219. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-C4L8zXeL.js +1 -0
  220. package/web-shell/assets/{timeline-definition-PNZ67QCA-T_8gmMCf.js → timeline-definition-PNZ67QCA-CXzS6qqz.js} +1 -1
  221. package/web-shell/assets/{vennDiagram-CIIHVFJN-DdjuqnjO.js → vennDiagram-CIIHVFJN-yRSolF5B.js} +1 -1
  222. package/web-shell/assets/{wardley-L42UT6IY-Cgtrvb37.js → wardley-L42UT6IY-ClLJjHPG.js} +1 -1
  223. package/web-shell/assets/{wardleyDiagram-YWT4CUSO-BW3f3-yW.js → wardleyDiagram-YWT4CUSO-VIa6t2p2.js} +1 -1
  224. package/web-shell/assets/{xychartDiagram-2RQKCTM6-BUS9IKkc.js → xychartDiagram-2RQKCTM6-CtmrkezW.js} +1 -1
  225. package/web-shell/index.html +18 -2
  226. package/chunks/chunk-2JKEQWDZ.js +0 -1596
  227. package/chunks/chunk-32MCHZQC.js +0 -73
  228. package/chunks/chunk-UAKXW3DN.js +0 -30
  229. package/web-shell/assets/channel-eRvn2UA3.js +0 -1
  230. package/web-shell/assets/classDiagram-4FO5ZUOK-YZNwy1rx.js +0 -1
  231. package/web-shell/assets/classDiagram-v2-Q7XG4LA2-YZNwy1rx.js +0 -1
  232. package/web-shell/assets/index-5FhE0ORO.css +0 -5
  233. package/web-shell/assets/index-CDTGmqVm.js +0 -720
  234. package/web-shell/assets/stateDiagram-v2-BHNVJYJU-DFy6gLDd.js +0 -1
@@ -1,1596 +0,0 @@
1
- // Force strict mode and setup for ESM
2
- "use strict";
3
- import {
4
- ClientSideConnection,
5
- PROTOCOL_VERSION,
6
- ndJsonStream
7
- } from "./chunk-UFC57OYT.js";
8
- import {
9
- init_esbuild_shims
10
- } from "./chunk-A4BMJM77.js";
11
- import {
12
- __name
13
- } from "./chunk-J2S4EL5Y.js";
14
-
15
- // packages/channels/base/dist/paths.js
16
- init_esbuild_shims();
17
- import * as path from "node:path";
18
- import * as os from "node:os";
19
- function resolvePath(dir) {
20
- let resolved = dir;
21
- if (resolved === "~" || resolved.startsWith("~/") || resolved.startsWith("~\\")) {
22
- const relativeSegments = resolved === "~" ? [] : resolved.slice(2).split(/[/\\]+/).filter(Boolean);
23
- resolved = path.join(os.homedir(), ...relativeSegments);
24
- }
25
- if (!path.isAbsolute(resolved)) {
26
- resolved = path.resolve(resolved);
27
- }
28
- return resolved;
29
- }
30
- __name(resolvePath, "resolvePath");
31
- function getGlobalQwenDir() {
32
- const envDir = process.env["QWEN_HOME"];
33
- if (envDir) {
34
- return resolvePath(envDir);
35
- }
36
- const homeDir = os.homedir();
37
- return homeDir ? path.join(homeDir, ".qwen") : path.join(os.tmpdir(), ".qwen");
38
- }
39
- __name(getGlobalQwenDir, "getGlobalQwenDir");
40
-
41
- // packages/channels/base/dist/SessionRouter.js
42
- init_esbuild_shims();
43
- import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
44
- import process2 from "node:process";
45
- var SessionRouter = class {
46
- static {
47
- __name(this, "SessionRouter");
48
- }
49
- toSession = /* @__PURE__ */ new Map();
50
- // routing key → session ID
51
- toTarget = /* @__PURE__ */ new Map();
52
- // session ID → target
53
- toCwd = /* @__PURE__ */ new Map();
54
- // session ID → cwd
55
- creatingSessions = /* @__PURE__ */ new Map();
56
- sessionLoadWindows = /* @__PURE__ */ new Set();
57
- bridge;
58
- defaultCwd;
59
- defaultScope;
60
- channelScopes = /* @__PURE__ */ new Map();
61
- persistPath;
62
- constructor(bridge, defaultCwd, scope = "user", persistPath) {
63
- this.bridge = bridge;
64
- this.defaultCwd = defaultCwd;
65
- this.defaultScope = scope;
66
- this.persistPath = persistPath;
67
- }
68
- /** Replace the bridge instance (used after crash recovery restart). */
69
- setBridge(bridge) {
70
- this.bridge = bridge;
71
- }
72
- /** Set scope override for a specific channel. */
73
- setChannelScope(channelName, scope) {
74
- this.channelScopes.set(channelName, scope);
75
- }
76
- routingKey(channelName, senderId, chatId, threadId) {
77
- const scope = this.channelScopes.get(channelName) || this.defaultScope;
78
- switch (scope) {
79
- case "thread":
80
- return `${channelName}:${threadId || chatId}`;
81
- case "single":
82
- return `${channelName}:__single__`;
83
- case "user":
84
- default:
85
- return `${channelName}:${senderId}:${chatId}`;
86
- }
87
- }
88
- async resolve(channelName, senderId, chatId, threadId, cwd) {
89
- const key = this.routingKey(channelName, senderId, chatId, threadId);
90
- let failedCreateWaits = 0;
91
- for (; ; ) {
92
- const existing = this.toSession.get(key);
93
- if (existing) {
94
- return existing;
95
- }
96
- const creating = this.creatingSessions.get(key);
97
- if (creating) {
98
- try {
99
- return await creating;
100
- } catch (err) {
101
- if (this.creatingSessions.get(key) === creating) {
102
- this.creatingSessions.delete(key);
103
- }
104
- failedCreateWaits++;
105
- if (failedCreateWaits > 3) {
106
- throw err;
107
- }
108
- continue;
109
- }
110
- }
111
- const created = Promise.resolve().then(async () => {
112
- const sessionCwd = cwd || this.defaultCwd;
113
- const loadWindow = this.beginSessionLoad();
114
- try {
115
- const sessionId = await this.createLiveSession(sessionCwd, loadWindow, key);
116
- this.toSession.set(key, sessionId);
117
- this.toTarget.set(sessionId, {
118
- channelName,
119
- senderId,
120
- chatId,
121
- threadId
122
- });
123
- this.toCwd.set(sessionId, sessionCwd);
124
- this.persist();
125
- return sessionId;
126
- } finally {
127
- this.endSessionLoad(loadWindow);
128
- }
129
- });
130
- this.creatingSessions.set(key, created);
131
- try {
132
- return await created;
133
- } finally {
134
- if (this.creatingSessions.get(key) === created) {
135
- this.creatingSessions.delete(key);
136
- }
137
- }
138
- }
139
- }
140
- getTarget(sessionId) {
141
- return this.toTarget.get(sessionId);
142
- }
143
- getSession(channelName, senderId, chatId, threadId) {
144
- return this.toSession.get(this.routingKey(channelName, senderId, chatId, threadId));
145
- }
146
- hasSession(channelName, senderId, chatId, threadId) {
147
- if (chatId) {
148
- return this.toSession.has(this.routingKey(channelName, senderId, chatId, threadId));
149
- }
150
- for (const target of this.toTarget.values()) {
151
- if (target.channelName === channelName && target.senderId === senderId) {
152
- return true;
153
- }
154
- }
155
- return false;
156
- }
157
- /**
158
- * Remove session(s) for the given sender. Returns the removed session IDs.
159
- */
160
- removeSession(channelName, senderId, chatId, threadId) {
161
- const removedIds = [];
162
- if (chatId) {
163
- const key = this.routingKey(channelName, senderId, chatId, threadId);
164
- const sessionId = this.deleteByKey(key);
165
- if (sessionId)
166
- removedIds.push(sessionId);
167
- } else {
168
- for (const [k, mappedSessionId] of [...this.toSession.entries()]) {
169
- const target = this.toTarget.get(mappedSessionId);
170
- if (target?.channelName === channelName && target.senderId === senderId) {
171
- const sessionId = this.deleteByKey(k);
172
- if (sessionId)
173
- removedIds.push(sessionId);
174
- }
175
- }
176
- }
177
- if (removedIds.length > 0)
178
- this.persist();
179
- return removedIds;
180
- }
181
- /** Remove a session mapping by daemon/ACP session ID. */
182
- removeSessionId(sessionId) {
183
- let removed = false;
184
- for (const [key, mappedSessionId] of [...this.toSession.entries()]) {
185
- if (mappedSessionId === sessionId) {
186
- this.toSession.delete(key);
187
- removed = true;
188
- }
189
- }
190
- if (this.toTarget.delete(sessionId)) {
191
- removed = true;
192
- }
193
- if (this.toCwd.delete(sessionId)) {
194
- removed = true;
195
- }
196
- if (!removed && this.sessionLoadWindows.size > 0) {
197
- for (const loadWindow of this.sessionLoadWindows) {
198
- loadWindow.add(sessionId);
199
- }
200
- }
201
- if (removed) {
202
- this.persist();
203
- }
204
- return removed;
205
- }
206
- deleteByKey(key) {
207
- const sessionId = this.toSession.get(key);
208
- if (!sessionId)
209
- return null;
210
- this.toSession.delete(key);
211
- this.toTarget.delete(sessionId);
212
- this.toCwd.delete(sessionId);
213
- return sessionId;
214
- }
215
- /** Get all session entries for crash recovery. */
216
- getAll() {
217
- const entries = [];
218
- for (const [key, sessionId] of this.toSession) {
219
- const target = this.toTarget.get(sessionId);
220
- if (target) {
221
- entries.push({ key, sessionId, target });
222
- }
223
- }
224
- return entries;
225
- }
226
- /**
227
- * Restore session mappings from a previous bridge.
228
- * Called after bridge restart — attempts loadSession for each saved mapping.
229
- * Failed loads are dropped (new session on next message).
230
- */
231
- async restoreSessions() {
232
- if (!this.persistPath || !existsSync(this.persistPath)) {
233
- return { restored: 0, failed: 0 };
234
- }
235
- let entries;
236
- try {
237
- entries = JSON.parse(readFileSync(this.persistPath, "utf-8"));
238
- } catch {
239
- return { restored: 0, failed: 0 };
240
- }
241
- let restored = 0;
242
- let failed = 0;
243
- let changed = false;
244
- const reservations = /* @__PURE__ */ new Map();
245
- for (const key of Object.keys(entries)) {
246
- this.deleteByKey(key);
247
- const reservation = this.createSessionReservation();
248
- reservation.promise.catch(() => void 0);
249
- this.creatingSessions.set(key, reservation.promise);
250
- reservations.set(key, reservation);
251
- }
252
- const loadWindow = this.beginSessionLoad();
253
- try {
254
- for (const [key, entry] of Object.entries(entries)) {
255
- const reservation = reservations.get(key);
256
- if (!reservation)
257
- continue;
258
- try {
259
- const sessionId = await this.bridge.loadSession(entry.sessionId, entry.cwd);
260
- if (typeof sessionId !== "string" || sessionId.length === 0) {
261
- throw new Error("Invalid restored session ID");
262
- }
263
- if (loadWindow.delete(sessionId)) {
264
- throw new Error("Restored session died before routing completed");
265
- }
266
- this.toSession.set(key, sessionId);
267
- this.toTarget.set(sessionId, entry.target);
268
- this.toCwd.set(sessionId, entry.cwd);
269
- reservation.resolve(sessionId);
270
- if (sessionId !== entry.sessionId) {
271
- changed = true;
272
- }
273
- restored++;
274
- } catch (err) {
275
- const reason = err instanceof Error ? err.message : String(err);
276
- process2.stderr.write(`[SessionRouter] Failed to restore session ${entry.sessionId} for key ${key}: ${reason}
277
- `);
278
- reservation.reject(new Error("Session restore failed", { cause: err }));
279
- failed++;
280
- changed = true;
281
- } finally {
282
- if (this.creatingSessions.get(key) === reservation.promise) {
283
- this.creatingSessions.delete(key);
284
- }
285
- }
286
- }
287
- } finally {
288
- this.endSessionLoad(loadWindow);
289
- }
290
- if (changed) {
291
- this.persist();
292
- }
293
- return { restored, failed };
294
- }
295
- /** Clear in-memory state and delete persist file. Used on clean shutdown. */
296
- clearAll() {
297
- this.toSession.clear();
298
- this.toTarget.clear();
299
- this.toCwd.clear();
300
- this.creatingSessions.clear();
301
- this.sessionLoadWindows.clear();
302
- if (this.persistPath && existsSync(this.persistPath)) {
303
- try {
304
- unlinkSync(this.persistPath);
305
- } catch {
306
- }
307
- }
308
- }
309
- persist() {
310
- if (!this.persistPath)
311
- return;
312
- const data = {};
313
- for (const [key, sessionId] of this.toSession) {
314
- const target = this.toTarget.get(sessionId);
315
- if (target) {
316
- data[key] = {
317
- sessionId,
318
- target,
319
- cwd: this.toCwd.get(sessionId) || this.defaultCwd
320
- };
321
- }
322
- }
323
- try {
324
- writeFileSync(this.persistPath, JSON.stringify(data, null, 2), "utf-8");
325
- } catch {
326
- }
327
- }
328
- async createLiveSession(cwd, loadWindow, routingKey) {
329
- const maxAttempts = 2;
330
- let lastDeadSessionId;
331
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
332
- const sessionId = await this.bridge.newSession(cwd);
333
- if (typeof sessionId !== "string" || sessionId.length === 0) {
334
- throw new Error("Invalid session ID from bridge");
335
- }
336
- if (!loadWindow.delete(sessionId)) {
337
- return sessionId;
338
- }
339
- lastDeadSessionId = sessionId;
340
- }
341
- throw new Error(`Session ${lastDeadSessionId ?? "unknown"} died before routing completed (${maxAttempts}/${maxAttempts} attempts, key ${routingKey})`);
342
- }
343
- beginSessionLoad() {
344
- const loadWindow = /* @__PURE__ */ new Set();
345
- this.sessionLoadWindows.add(loadWindow);
346
- return loadWindow;
347
- }
348
- createSessionReservation() {
349
- let resolveReservation;
350
- let rejectReservation;
351
- const promise = new Promise((resolve2, reject) => {
352
- resolveReservation = resolve2;
353
- rejectReservation = reject;
354
- });
355
- return {
356
- promise,
357
- resolve: resolveReservation,
358
- reject: rejectReservation
359
- };
360
- }
361
- endSessionLoad(loadWindow) {
362
- this.sessionLoadWindows.delete(loadWindow);
363
- }
364
- };
365
-
366
- // packages/channels/base/dist/sanitize.js
367
- init_esbuild_shims();
368
- var PROMPT_UNSAFE_INVISIBLES = /[\u0080-\u009f\u200b-\u200d\u2028\u2029\u202a-\u202e\u2060\u2066-\u2069\ufeff]/g;
369
- function truncateCodePoints(str, max) {
370
- const cp = Array.from(str);
371
- return cp.length > max ? cp.slice(0, max).join("") : str;
372
- }
373
- __name(truncateCodePoints, "truncateCodePoints");
374
- function sanitizeSenderName(name) {
375
- const cleaned = name.replace(PROMPT_UNSAFE_INVISIBLES, " ").replace(/[\u0000-\u001f\u007f]/g, " ").replace(/[[\]\r\n]/g, " ");
376
- return truncateCodePoints(cleaned, 64).trim() || "unknown";
377
- }
378
- __name(sanitizeSenderName, "sanitizeSenderName");
379
- function sanitizeQuotedText(text, maxLen) {
380
- const cleaned = text.replace(PROMPT_UNSAFE_INVISIBLES, " ").replace(/[\u0000-\u001f\u007f]/g, " ").replace(/["[\]]/g, " ");
381
- const cp = Array.from(cleaned);
382
- return cp.length > maxLen ? cp.slice(0, maxLen - 1).join("") + "\u2026" : cleaned;
383
- }
384
- __name(sanitizeQuotedText, "sanitizeQuotedText");
385
- function sanitizePromptText(text) {
386
- return text.replace(PROMPT_UNSAFE_INVISIBLES, " ").replace(/^([ \t]*)\[([^\]\r\n]{1,64})\](:?)/gm, "$1$2$3").replace(/[\u0000-\u001f\u007f]/g, " ");
387
- }
388
- __name(sanitizePromptText, "sanitizePromptText");
389
- function sanitizePromptPath(path3) {
390
- const cleaned = path3.replace(PROMPT_UNSAFE_INVISIBLES, " ").replace(/[\u0000-\u001f\u007f]/g, " ");
391
- return truncateCodePoints(cleaned, 1024);
392
- }
393
- __name(sanitizePromptPath, "sanitizePromptPath");
394
- function sanitizeLogText(text, maxLen) {
395
- return truncateCodePoints(text, maxLen).replace(/\n/g, "\\n").replace(PROMPT_UNSAFE_INVISIBLES, " ").replace(/[\u0000-\u001f\u007f]/g, " ");
396
- }
397
- __name(sanitizeLogText, "sanitizeLogText");
398
-
399
- // packages/channels/base/dist/ChannelBase.js
400
- init_esbuild_shims();
401
- import { basename } from "node:path";
402
-
403
- // packages/channels/base/dist/BlockStreamer.js
404
- init_esbuild_shims();
405
- var BlockStreamer = class {
406
- static {
407
- __name(this, "BlockStreamer");
408
- }
409
- buffer = "";
410
- idleTimer = null;
411
- sending = Promise.resolve();
412
- opts;
413
- /** Number of blocks emitted so far. */
414
- blockCount = 0;
415
- constructor(opts) {
416
- this.opts = opts;
417
- }
418
- /** Feed a new text chunk from the agent stream. */
419
- push(chunk) {
420
- this.buffer += chunk;
421
- this.clearIdleTimer();
422
- this.checkEmit();
423
- if (this.buffer.length > 0 && this.opts.idleMs > 0) {
424
- this.idleTimer = setTimeout(() => this.onIdle(), this.opts.idleMs);
425
- }
426
- }
427
- /** Flush all remaining buffered text. Awaits all pending sends. */
428
- async flush() {
429
- this.clearIdleTimer();
430
- if (this.buffer.length > 0) {
431
- this.emitBlock(this.buffer);
432
- this.buffer = "";
433
- }
434
- await this.sending;
435
- }
436
- /** Drop buffered text and cancel future idle emission. */
437
- stop() {
438
- this.clearIdleTimer();
439
- this.buffer = "";
440
- }
441
- // ---------------------------------------------------------------------------
442
- // Internal
443
- // ---------------------------------------------------------------------------
444
- checkEmit() {
445
- while (this.buffer.length >= this.opts.maxChars) {
446
- const bp = this.findBreakPoint(this.buffer, this.opts.maxChars);
447
- this.emitBlock(this.buffer.slice(0, bp));
448
- this.buffer = this.buffer.slice(bp);
449
- }
450
- if (this.buffer.length >= this.opts.minChars) {
451
- const bp = this.findBlockBoundary(this.buffer);
452
- if (bp > 0) {
453
- this.emitBlock(this.buffer.slice(0, bp));
454
- this.buffer = this.buffer.slice(bp);
455
- }
456
- }
457
- }
458
- onIdle() {
459
- this.idleTimer = null;
460
- if (this.buffer.length >= this.opts.minChars) {
461
- this.emitBlock(this.buffer);
462
- this.buffer = "";
463
- }
464
- }
465
- emitBlock(text) {
466
- const trimmed = text.trim();
467
- if (!trimmed)
468
- return;
469
- this.blockCount++;
470
- this.sending = this.sending.then(() => this.opts.send(trimmed)).catch(() => {
471
- });
472
- }
473
- /**
474
- * Find the last paragraph boundary (\n\n) in the buffer.
475
- * Returns the position after the boundary, or -1 if no suitable boundary
476
- * exists at or after minChars.
477
- */
478
- findBlockBoundary(text) {
479
- const last = text.lastIndexOf("\n\n");
480
- if (last < 0 || last < this.opts.minChars)
481
- return -1;
482
- return last + 2;
483
- }
484
- /**
485
- * Find the best break point at or before maxPos.
486
- * Prefers paragraph break > newline > space > maxPos.
487
- */
488
- findBreakPoint(text, maxPos) {
489
- const sub = text.slice(0, maxPos);
490
- const para = sub.lastIndexOf("\n\n");
491
- if (para > 0)
492
- return para + 2;
493
- const nl = sub.lastIndexOf("\n");
494
- if (nl > 0)
495
- return nl + 1;
496
- const sp = sub.lastIndexOf(" ");
497
- if (sp > 0)
498
- return sp + 1;
499
- return maxPos;
500
- }
501
- clearIdleTimer() {
502
- if (this.idleTimer !== null) {
503
- clearTimeout(this.idleTimer);
504
- this.idleTimer = null;
505
- }
506
- }
507
- };
508
-
509
- // packages/channels/base/dist/GroupGate.js
510
- init_esbuild_shims();
511
- var GroupGate = class {
512
- static {
513
- __name(this, "GroupGate");
514
- }
515
- policy;
516
- groups;
517
- constructor(policy = "disabled", groups = {}) {
518
- this.policy = policy;
519
- this.groups = groups;
520
- }
521
- /**
522
- * Full group check: policy + allowlist + mention gating.
523
- * Evaluation order:
524
- * 1. groupPolicy (disabled → drop)
525
- * 2. group allowlist (allowlist mode, no match → drop)
526
- * 3. mention gating (requireMention + not mentioned → drop silently)
527
- *
528
- * Mention gating runs before sender gate so that unmentioned messages
529
- * in groups don't trigger pairing flows.
530
- */
531
- check(envelope) {
532
- if (!envelope.isGroup) {
533
- return { allowed: true };
534
- }
535
- if (this.policy === "disabled") {
536
- return { allowed: false, reason: "disabled" };
537
- }
538
- if (this.policy === "allowlist") {
539
- if (!this.groups[envelope.chatId]) {
540
- return { allowed: false, reason: "not_allowlisted" };
541
- }
542
- }
543
- const groupConfig = this.groups[envelope.chatId] || this.groups["*"] || {};
544
- const requireMention = groupConfig.requireMention ?? true;
545
- if (requireMention && !envelope.isMentioned && !envelope.isReplyToBot) {
546
- return { allowed: false, reason: "mention_required" };
547
- }
548
- return { allowed: true };
549
- }
550
- };
551
-
552
- // packages/channels/base/dist/SenderGate.js
553
- init_esbuild_shims();
554
- var SenderGate = class {
555
- static {
556
- __name(this, "SenderGate");
557
- }
558
- policy;
559
- allowedUsers;
560
- pairingStore;
561
- constructor(policy, allowedUsers = [], pairingStore) {
562
- this.policy = policy;
563
- this.allowedUsers = new Set(allowedUsers);
564
- this.pairingStore = pairingStore || null;
565
- }
566
- check(senderId, senderName) {
567
- switch (this.policy) {
568
- case "open":
569
- return { allowed: true };
570
- case "allowlist":
571
- return { allowed: this.allowedUsers.has(senderId) };
572
- case "pairing": {
573
- if (this.allowedUsers.has(senderId)) {
574
- return { allowed: true };
575
- }
576
- if (this.pairingStore?.isApproved(senderId)) {
577
- return { allowed: true };
578
- }
579
- const code = this.pairingStore?.createRequest(senderId, senderName || senderId);
580
- return { allowed: false, pairingCode: code ?? null };
581
- }
582
- default:
583
- throw new Error(`Unknown sender policy: ${this.policy}`);
584
- }
585
- }
586
- };
587
-
588
- // packages/channels/base/dist/PairingStore.js
589
- init_esbuild_shims();
590
- import * as crypto from "node:crypto";
591
- import * as fs from "node:fs";
592
- import * as path2 from "node:path";
593
- var SAFE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
594
- var CODE_LENGTH = 8;
595
- var EXPIRY_MS = 60 * 60 * 1e3;
596
- var MAX_PENDING = 3;
597
- var PairingStore = class {
598
- static {
599
- __name(this, "PairingStore");
600
- }
601
- dir;
602
- pendingPath;
603
- allowlistPath;
604
- constructor(channelName) {
605
- this.dir = path2.join(getGlobalQwenDir(), "channels");
606
- this.pendingPath = path2.join(this.dir, `${channelName}-pairing.json`);
607
- this.allowlistPath = path2.join(this.dir, `${channelName}-allowlist.json`);
608
- }
609
- isApproved(senderId) {
610
- const list = this.readAllowlist();
611
- return list.includes(senderId);
612
- }
613
- /**
614
- * Create a pairing request for an unknown sender.
615
- * Returns the code if created, or null if the pending cap is reached.
616
- * If the sender already has a non-expired pending request, returns that code.
617
- */
618
- createRequest(senderId, senderName) {
619
- const pending = this.readPending();
620
- const now = Date.now();
621
- const active = pending.filter((r) => now - r.createdAt < EXPIRY_MS);
622
- const existing = active.find((r) => r.senderId === senderId);
623
- if (existing) {
624
- return existing.code;
625
- }
626
- if (active.length >= MAX_PENDING) {
627
- return null;
628
- }
629
- const code = generateCode();
630
- active.push({ senderId, senderName, code, createdAt: now });
631
- this.writePending(active);
632
- return code;
633
- }
634
- /**
635
- * Approve a pairing request by code.
636
- * Returns the sender ID if found, or null if not found / expired.
637
- */
638
- approve(code) {
639
- const pending = this.readPending();
640
- const now = Date.now();
641
- const idx = pending.findIndex((r) => r.code === code.toUpperCase() && now - r.createdAt < EXPIRY_MS);
642
- if (idx === -1)
643
- return null;
644
- const request = pending[idx];
645
- pending.splice(idx, 1);
646
- this.writePending(pending);
647
- const list = this.readAllowlist();
648
- if (!list.includes(request.senderId)) {
649
- list.push(request.senderId);
650
- this.writeAllowlist(list);
651
- }
652
- return request;
653
- }
654
- listPending() {
655
- const pending = this.readPending();
656
- const now = Date.now();
657
- return pending.filter((r) => now - r.createdAt < EXPIRY_MS);
658
- }
659
- getAllowlist() {
660
- return this.readAllowlist();
661
- }
662
- ensureDir() {
663
- if (!fs.existsSync(this.dir)) {
664
- fs.mkdirSync(this.dir, { recursive: true });
665
- }
666
- }
667
- readPending() {
668
- try {
669
- const data = fs.readFileSync(this.pendingPath, "utf-8");
670
- return JSON.parse(data);
671
- } catch {
672
- return [];
673
- }
674
- }
675
- writePending(requests) {
676
- this.ensureDir();
677
- fs.writeFileSync(this.pendingPath, JSON.stringify(requests, null, 2));
678
- }
679
- readAllowlist() {
680
- try {
681
- const data = fs.readFileSync(this.allowlistPath, "utf-8");
682
- return JSON.parse(data);
683
- } catch {
684
- return [];
685
- }
686
- }
687
- writeAllowlist(list) {
688
- this.ensureDir();
689
- fs.writeFileSync(this.allowlistPath, JSON.stringify(list, null, 2));
690
- }
691
- };
692
- function generateCode() {
693
- let code = "";
694
- for (let i = 0; i < CODE_LENGTH; i++) {
695
- code += SAFE_ALPHABET[crypto.randomInt(SAFE_ALPHABET.length)];
696
- }
697
- return code;
698
- }
699
- __name(generateCode, "generateCode");
700
-
701
- // packages/channels/base/dist/ChannelBase.js
702
- var CLEAR_CANCEL_TIMEOUT_MS = 3e3;
703
- var COMMAND_TOKEN_CHARS = "a-zA-Z0-9_:-";
704
- var PARSE_COMMAND_RE = new RegExp(`^\\/([${COMMAND_TOKEN_CHARS}]+)(?:@\\S+)?\\s*(.*)`, "s");
705
- var COMMAND_TOKEN_RE = new RegExp(`^[${COMMAND_TOKEN_CHARS}]+(?:@\\S+)?$`);
706
- var ChannelBase = class {
707
- static {
708
- __name(this, "ChannelBase");
709
- }
710
- config;
711
- bridge;
712
- groupGate;
713
- gate;
714
- router;
715
- name;
716
- /** Resolved proxy URL, available to subclasses for adapter-specific clients. */
717
- proxy;
718
- instructedSessions = /* @__PURE__ */ new Set();
719
- commands = /* @__PURE__ */ new Map();
720
- /** Per-session promise chain to serialize prompt + send (followup mode). */
721
- sessionQueues = /* @__PURE__ */ new Map();
722
- registerBridgeEvents;
723
- /**
724
- * Per-session generation, bumped by /clear. A queued followup turn captures the
725
- * generation when it enqueues and bails if /clear bumped it before the turn ran,
726
- * so a cleared session can't be resurrected by an already-queued prompt.
727
- */
728
- sessionGenerations = /* @__PURE__ */ new Map();
729
- /** Per-session active prompt tracking for dispatch modes. */
730
- activePrompts = /* @__PURE__ */ new Map();
731
- /** Per-session message buffer for collect mode. */
732
- collectBuffers = /* @__PURE__ */ new Map();
733
- bridgeToolCallListener = /* @__PURE__ */ __name((event) => {
734
- const target = this.router.getTarget(event.sessionId);
735
- if (target) {
736
- this.onToolCall(target.chatId, event);
737
- }
738
- }, "bridgeToolCallListener");
739
- bridgeSessionDiedListener = /* @__PURE__ */ __name((event) => {
740
- this.onSessionDied(event.sessionId);
741
- }, "bridgeSessionDiedListener");
742
- constructor(name, config, bridge, options) {
743
- this.name = name;
744
- this.config = config;
745
- this.bridge = bridge;
746
- this.proxy = options?.proxy;
747
- this.groupGate = new GroupGate(config.groupPolicy, config.groups);
748
- const pairingStore = config.senderPolicy === "pairing" ? new PairingStore(name) : void 0;
749
- this.gate = new SenderGate(config.senderPolicy, config.allowedUsers, pairingStore);
750
- this.router = options?.router || new SessionRouter(bridge, config.cwd, config.sessionScope);
751
- this.registerSharedCommands();
752
- this.registerBridgeEvents = options?.registerBridgeEvents ?? !options?.router;
753
- if (this.registerBridgeEvents) {
754
- this.attachBridgeEvents(bridge);
755
- }
756
- }
757
- /** Replace the bridge instance (used after crash recovery restart). */
758
- setBridge(bridge) {
759
- if (this.registerBridgeEvents) {
760
- this.detachBridgeEvents(this.bridge);
761
- }
762
- this.router.setBridge(bridge);
763
- this.bridge = bridge;
764
- if (this.registerBridgeEvents) {
765
- this.attachBridgeEvents(bridge);
766
- }
767
- }
768
- onToolCall(_chatId, _event) {
769
- }
770
- onSessionDied(sessionId) {
771
- this.router.removeSessionId(sessionId);
772
- this.instructedSessions.delete(sessionId);
773
- }
774
- attachBridgeEvents(bridge) {
775
- bridge.on("toolCall", this.bridgeToolCallListener);
776
- bridge.on("sessionDied", this.bridgeSessionDiedListener);
777
- }
778
- detachBridgeEvents(bridge) {
779
- bridge.off("toolCall", this.bridgeToolCallListener);
780
- bridge.off("sessionDied", this.bridgeSessionDiedListener);
781
- }
782
- /**
783
- * Called when a prompt actually begins processing (inside the session queue).
784
- * Override to show a platform-specific working indicator (e.g., typing, reaction).
785
- * Not called for buffered messages (collect mode) or gated/blocked messages.
786
- */
787
- onPromptStart(_chatId, _sessionId, _messageId) {
788
- }
789
- /**
790
- * Called when a prompt finishes (response sent or cancelled).
791
- * Override to hide the working indicator.
792
- */
793
- onPromptEnd(_chatId, _sessionId, _messageId) {
794
- }
795
- /**
796
- * Called for each text chunk as the agent streams its response.
797
- * Override to implement progressive display (e.g., updating an AI card in-place).
798
- * Default: no-op (chunks are collected internally and delivered via onResponseComplete).
799
- */
800
- onResponseChunk(_chatId, _chunk, _sessionId) {
801
- }
802
- /**
803
- * Called when the agent's full response is ready.
804
- * Override to customize delivery (e.g., finalize an AI card).
805
- * Default: calls sendMessage() with the full response text.
806
- */
807
- async onResponseComplete(chatId, fullText, _sessionId) {
808
- await this.sendMessage(chatId, fullText);
809
- }
810
- /**
811
- * Register a slash command handler. Subclasses can call this to add
812
- * platform-specific commands (e.g., /start for Telegram).
813
- * Overrides shared commands if the same name is registered.
814
- */
815
- registerCommand(name, handler) {
816
- this.commands.set(name.toLowerCase(), handler);
817
- }
818
- registerCancelCommand(name = "cancel") {
819
- this.registerCommand(name, async (envelope) => {
820
- if (!this.isAuthorizedForSharedSession(envelope)) {
821
- await this.sendMessage(envelope.chatId, "Only authorized members can cancel requests in this shared session.");
822
- return true;
823
- }
824
- const activeSessionId = this.findActiveSessionId(envelope);
825
- if (!activeSessionId) {
826
- await this.sendMessage(envelope.chatId, "No request is currently running.");
827
- return true;
828
- }
829
- const active = this.activePrompts.get(activeSessionId);
830
- if (!active) {
831
- await this.sendMessage(envelope.chatId, "No request is currently running.");
832
- return true;
833
- }
834
- const cancelRequested = active.cancelRequested ?? this.bridge.cancelSession(activeSessionId).then(() => true, (err) => {
835
- process.stderr.write(`[${this.name}] cancelSession failed for session=${activeSessionId}: ${err instanceof Error ? err.message : err}
836
- `);
837
- active.cancelRequested = void 0;
838
- return false;
839
- });
840
- active.cancelRequested = cancelRequested;
841
- const cancelSucceeded = await cancelRequested;
842
- if (!cancelSucceeded) {
843
- await this.sendMessage(envelope.chatId, "Failed to cancel current request.");
844
- return true;
845
- }
846
- active.cancelled = true;
847
- active.stopStreaming?.();
848
- this.collectBuffers.delete(activeSessionId);
849
- await this.sendMessage(envelope.chatId, "Cancelled current request.");
850
- return true;
851
- });
852
- }
853
- /** Register shared slash commands. Called from constructor. */
854
- registerSharedCommands() {
855
- const doClear = /* @__PURE__ */ __name(async (envelope) => {
856
- const removedIds = this.router.removeSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
857
- if (removedIds.length > 0) {
858
- for (const id of removedIds) {
859
- if (this.isSharedSession(envelope)) {
860
- const who = sanitizeSenderName(envelope.senderName || envelope.senderId || "unknown");
861
- process.stderr.write(`[${this.name}] shared session ${id} cleared by ${who} (sender ${envelope.senderId})
862
- `);
863
- }
864
- this.sessionGenerations.set(id, (this.sessionGenerations.get(id) ?? 0) + 1);
865
- const active = this.activePrompts.get(id);
866
- this.collectBuffers.delete(id);
867
- if (active) {
868
- const settled = await this.cancelAndAwaitActive(active, id);
869
- if (!settled) {
870
- const wedgedChat = active.chatId ? sanitizeLogText(active.chatId, 64) : "unknown";
871
- const wedgedMessage = active.messageId ? `, message ${sanitizeLogText(active.messageId, 64)}` : "";
872
- process.stderr.write(`[${this.name}] /clear abandoned a wedged turn for session ${id} (chat ${wedgedChat}${wedgedMessage}): it did not wind down within ${CLEAR_CANCEL_TIMEOUT_MS}ms
873
- `);
874
- active.clearEvicted = true;
875
- try {
876
- this.onPromptEnd(active.chatId, id, active.messageId);
877
- } catch (err) {
878
- process.stderr.write(`[${this.name}] onPromptEnd threw during /clear eviction for session ${id}: ${err instanceof Error ? err.message : err}
879
- `);
880
- }
881
- }
882
- }
883
- this.instructedSessions.delete(id);
884
- const drained = this.sessionQueues.get(id);
885
- const bumpedGeneration = this.sessionGenerations.get(id);
886
- this.sessionQueues.delete(id);
887
- this.activePrompts.delete(id);
888
- if (drained) {
889
- void drained.then(() => {
890
- if (!this.sessionQueues.has(id) && this.sessionGenerations.get(id) === bumpedGeneration) {
891
- this.sessionGenerations.delete(id);
892
- }
893
- });
894
- } else {
895
- this.sessionGenerations.delete(id);
896
- }
897
- }
898
- await this.sendMessage(envelope.chatId, "Session cleared. The next message starts a fresh conversation.");
899
- } else {
900
- await this.sendMessage(envelope.chatId, "No active session to clear.");
901
- }
902
- }, "doClear");
903
- const clearHandler = /* @__PURE__ */ __name(async (envelope, args) => {
904
- if (!this.isAuthorizedForSharedSession(envelope)) {
905
- await this.sendMessage(envelope.chatId, "Only authorized members can clear this shared session.");
906
- return true;
907
- }
908
- if (this.isSharedSession(envelope) && args.toLowerCase() !== "confirm") {
909
- await this.sendMessage(envelope.chatId, 'This clears the shared session for everyone who shares it. Re-send with "confirm" (e.g. /clear confirm) to proceed.');
910
- return true;
911
- }
912
- await doClear(envelope);
913
- return true;
914
- }, "clearHandler");
915
- this.registerCommand("clear", clearHandler);
916
- this.registerCommand("reset", clearHandler);
917
- this.registerCommand("new", clearHandler);
918
- this.registerCommand("who", async (envelope) => {
919
- if (!this.isAuthorizedForSharedSession(envelope)) {
920
- await this.sendMessage(envelope.chatId, "Only authorized members can view this shared session.");
921
- return true;
922
- }
923
- const active = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
924
- const scopeNote = this.config.sessionScope === "single" ? " (shared channel-wide)" : this.isSharedSession(envelope) ? envelope.isGroup ? " (shared by this group)" : "" : envelope.isGroup ? " (private to you)" : "";
925
- await this.sendMessage(envelope.chatId, [
926
- `Channel: ${this.name}`,
927
- // Only the basename — don't leak the absolute cwd to group members.
928
- `Workspace: ${basename(this.config.cwd)}`,
929
- `Session: ${active ? "active" : "none"}${scopeNote}`
930
- ].join("\n"));
931
- return true;
932
- });
933
- this.registerCommand("help", async (envelope) => {
934
- const lines = [
935
- "Commands:",
936
- "/help \u2014 Show this help",
937
- this.isSharedSession(envelope) ? "/clear confirm \u2014 Clear the shared session (aliases: /reset, /new)" : "/clear \u2014 Clear your session (aliases: /reset, /new)",
938
- "/who \u2014 Show current session & workspace",
939
- "/status \u2014 Show session info"
940
- ];
941
- const sharedCmds = /* @__PURE__ */ new Set([
942
- "help",
943
- "clear",
944
- "reset",
945
- "new",
946
- "who",
947
- "status"
948
- ]);
949
- const platformCmds = [...this.commands.keys()].filter((c) => !sharedCmds.has(c));
950
- if (platformCmds.length > 0) {
951
- for (const cmd of platformCmds) {
952
- lines.push(`/${cmd}`);
953
- }
954
- }
955
- const sessionId = this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
956
- const agentCommands = sessionId ? this.getAgentCommandsForSession(sessionId) : this.bridge.availableCommands;
957
- if (agentCommands.length > 0) {
958
- lines.push("", "Agent commands (forwarded to Qwen Code):");
959
- for (const cmd of agentCommands) {
960
- lines.push(`/${cmd.name} \u2014 ${cmd.description}`);
961
- }
962
- }
963
- lines.push("", "Send any text to chat with the agent.");
964
- await this.sendMessage(envelope.chatId, lines.join("\n"));
965
- return true;
966
- });
967
- this.registerCommand("status", async (envelope) => {
968
- if (!this.isAuthorizedForSharedSession(envelope)) {
969
- await this.sendMessage(envelope.chatId, "Only authorized members can view this shared session.");
970
- return true;
971
- }
972
- const hasSession = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
973
- const policy = this.config.senderPolicy;
974
- const lines = [
975
- `Session: ${hasSession ? "active" : "none"}`,
976
- `Access: ${policy}`,
977
- `Channel: ${this.name}`
978
- ];
979
- await this.sendMessage(envelope.chatId, lines.join("\n"));
980
- return true;
981
- });
982
- }
983
- /** Check if a message text matches a registered local command. */
984
- isLocalCommand(text) {
985
- const parsed = this.parseCommand(text);
986
- return parsed !== null && this.commands.has(parsed.command);
987
- }
988
- findActiveSessionId(envelope) {
989
- const sessionId = this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
990
- return sessionId && this.activePrompts.has(sessionId) ? sessionId : void 0;
991
- }
992
- /**
993
- * Whether the resolved session is SHARED across senders. `single` collapses
994
- * the whole channel to one `__single__` session for EVERY sender — group OR
995
- * DM — so it is ALWAYS shared (even a DM maps to `__single__`). `thread` is
996
- * shared only in a group (a DM maps to the lone caller's own chat). `user` is
997
- * per-sender, never shared. Drives both the destructive-/clear confirm gate
998
- * and the host-shell (`!`) gate.
999
- */
1000
- isSharedSession(envelope) {
1001
- return this.config.sessionScope === "single" || envelope.isGroup && this.config.sessionScope === "thread";
1002
- }
1003
- /**
1004
- * Whether `envelope.senderId` may act on the resolved session's destructive or
1005
- * workspace-leaking commands (/clear, /who). A SHARED session with a non-empty
1006
- * allowedUsers list is restricted to those members; a per-user session, or one
1007
- * with no allowlist, is unrestricted. Shared verbatim by /clear and /who so the
1008
- * gate can't drift; each caller sends its own rejection wording.
1009
- */
1010
- isAuthorizedForSharedSession(envelope) {
1011
- if (!this.isSharedSession(envelope))
1012
- return true;
1013
- const authorized = this.config.allowedUsers;
1014
- return authorized.length === 0 || authorized.includes(envelope.senderId);
1015
- }
1016
- stopActiveStreaming(active, sessionId, reason) {
1017
- try {
1018
- active.stopStreaming?.();
1019
- } catch (err) {
1020
- process.stderr.write(`[${this.name}] stopStreaming threw during ${reason} for session ${sessionId}: ${err instanceof Error ? err.message : err}
1021
- `);
1022
- }
1023
- }
1024
- /**
1025
- * Cancel the active turn and wait (bounded) for it to wind down. Stops the
1026
- * BlockStreamer so buffered text can't leak via the idle timer, then fires a
1027
- * best-effort cancelSession (NOT awaited — a wedged child/daemon can leave the
1028
- * request pending forever). Returns true if active.done settled first, false
1029
- * if the CLEAR_CANCEL_TIMEOUT_MS bound won (the turn never wound down). Used by
1030
- * /clear, which genuinely EVICTS the session and so must proceed even when the
1031
- * turn is wedged. Steer no longer uses this: it best-effort cancels then chains
1032
- * the new turn behind the old one (see handleInbound), so it never needs to
1033
- * proceed past a still-active turn.
1034
- */
1035
- async cancelAndAwaitActive(active, sessionId) {
1036
- active.cancelled = true;
1037
- this.stopActiveStreaming(active, sessionId, "cancel");
1038
- void this.bridge.cancelSession(sessionId).catch((err) => {
1039
- process.stderr.write(`[${this.name}] cancelSession failed for session=${sessionId} (clear/await): ${err instanceof Error ? err.message : err}
1040
- `);
1041
- });
1042
- let timer;
1043
- const settled = await Promise.race([
1044
- active.done.then(() => true),
1045
- new Promise((resolve2) => {
1046
- timer = setTimeout(() => resolve2(false), CLEAR_CANCEL_TIMEOUT_MS);
1047
- })
1048
- ]);
1049
- clearTimeout(timer);
1050
- return settled;
1051
- }
1052
- /**
1053
- * Parse a slash command from message text.
1054
- * Returns { command, raw, args } or null if not a slash command. `command` is
1055
- * lowercased for case-insensitive LOCAL dispatch (registerCommand lowercases the
1056
- * names it stores); `raw` keeps the typed case so agent-command matching can be
1057
- * CASE-SENSITIVE, mirroring the CLI's parseSlashCommand (`cmd.name === part`).
1058
- */
1059
- parseCommand(text) {
1060
- const trimmed = text.trim();
1061
- if (!trimmed.startsWith("/"))
1062
- return null;
1063
- const match = trimmed.match(PARSE_COMMAND_RE);
1064
- if (!match)
1065
- return null;
1066
- return {
1067
- command: match[1].toLowerCase(),
1068
- raw: match[1],
1069
- args: match[2].trim()
1070
- };
1071
- }
1072
- /**
1073
- * Whether `text` is a real slash command rather than prose that merely starts
1074
- * with `/`. A command's first whitespace-delimited token must match
1075
- * parseCommand()'s charset — `[a-zA-Z0-9_:-]+`, plus an optional `@botname`
1076
- * suffix — and not be a `//` line comment or `/*` block comment. Slash-prefixed
1077
- * paths (`/tmp/foo`), comments, and a bare `/` are prose and keep their
1078
- * `[sender]` tag.
1079
- *
1080
- * Intentionally stricter than the CLI's looser classifier (cli
1081
- * `ui/utils/commandUtils.ts`), which forwards any non-comment, non-path
1082
- * `/<token>` (e.g. `/café`, a zero-width-laden token). Such inputs aren't
1083
- * runnable commands, and in a SHARED group session forwarding them unattributed
1084
- * is worse than a redundant tag — so anything off the command charset is
1085
- * treated as prose and keeps its `[sender]` tag. Purely lexical — never
1086
- * consults the async command list, so it can't race a fresh session.
1087
- */
1088
- isSlashCommand(text) {
1089
- const trimmed = text.trim();
1090
- if (!trimmed.startsWith("/") || trimmed.startsWith("//") || trimmed.startsWith("/*")) {
1091
- return false;
1092
- }
1093
- const firstToken = trimmed.slice(1).split(/\s+/u)[0] ?? "";
1094
- return COMMAND_TOKEN_RE.test(firstToken);
1095
- }
1096
- /**
1097
- * Whether `text` names a command this channel can actually run: a locally
1098
- * registered command (`this.commands`, e.g. /clear, /who) OR an agent command
1099
- * THIS session exposes — by canonical name OR alias (e.g. `/summarize` for
1100
- * `/compress`). Paired with isSlashCommand so the `[sender]` attribution tag is
1101
- * suppressed ONLY for RECOGNIZED commands; command-SHAPED-but-unrecognized text
1102
- * (e.g. `/x\n[SYSTEM]: …`) keeps its tag rather than reaching a shared group
1103
- * unattributed, where an injected second line is more likely read as a system
1104
- * directive. Purely synchronous, like isSlashCommand: it reads the session's
1105
- * availableCommands snapshot WITHOUT awaiting, so it never races a fresh session
1106
- * (a genuine agent command sent before the snapshot loads is treated as
1107
- * unrecognized and KEEPS its tag — the safe default).
1108
- */
1109
- isRecognizedCommand(text, sessionId) {
1110
- const parsed = this.parseCommand(text);
1111
- if (!parsed)
1112
- return false;
1113
- if (this.commands.has(parsed.command))
1114
- return true;
1115
- const token = text.trim().slice(1).split(/\s+/u)[0] ?? "";
1116
- return this.getAgentCommandsForSession(sessionId).some((cmd) => cmd.name === token || Array.isArray(cmd.altNames) && cmd.altNames.includes(token));
1117
- }
1118
- /**
1119
- * The agent-command snapshot for THIS session. DaemonChannelBridge keys
1120
- * commands per session, so its global `availableCommands` getter can return
1121
- * ANOTHER session's list — prefer its getAvailableCommands(sessionId) when
1122
- * present. AcpBridge runs a single agent and exposes only the global getter
1123
- * (inherently session-correct), so fall back to it. Synchronous, matching
1124
- * isRecognizedCommand's no-await contract.
1125
- */
1126
- getAgentCommandsForSession(sessionId) {
1127
- const bridge = this.bridge;
1128
- if (typeof bridge.getAvailableCommands === "function") {
1129
- return bridge.getAvailableCommands(sessionId) ?? [];
1130
- }
1131
- return bridge.availableCommands ?? [];
1132
- }
1133
- async handleInbound(envelope) {
1134
- const groupResult = this.groupGate.check(envelope);
1135
- if (!groupResult.allowed) {
1136
- return;
1137
- }
1138
- const result = this.gate.check(envelope.senderId, envelope.senderName);
1139
- if (!result.allowed) {
1140
- if (result.pairingCode !== void 0) {
1141
- await this.onPairingRequired(envelope.chatId, result.pairingCode);
1142
- }
1143
- return;
1144
- }
1145
- const parsed = this.parseCommand(envelope.text);
1146
- if (parsed) {
1147
- const handler = this.commands.get(parsed.command);
1148
- if (handler) {
1149
- const handled = await handler(envelope, parsed.args);
1150
- if (handled)
1151
- return;
1152
- }
1153
- }
1154
- const bangText = envelope.text.trimStart();
1155
- if (bangText.startsWith("!")) {
1156
- if (envelope.isGroup || this.isSharedSession(envelope)) {
1157
- const who = sanitizeSenderName(envelope.senderName || envelope.senderId || "unknown");
1158
- process.stderr.write(`[${this.name}] blocked ! shell command from ${who} (sender ${envelope.senderId}) in chat ${sanitizeLogText(envelope.chatId, 64)}
1159
- `);
1160
- }
1161
- if (envelope.isGroup) {
1162
- await this.sendMessage(envelope.chatId, "Shell commands (`!`) are disabled in group chats.");
1163
- return;
1164
- }
1165
- if (this.isSharedSession(envelope)) {
1166
- await this.sendMessage(envelope.chatId, "Shell commands (`!`) are disabled in shared sessions.");
1167
- return;
1168
- }
1169
- }
1170
- const sessionId = await this.router.resolve(this.name, envelope.senderId, envelope.chatId, envelope.threadId, this.config.cwd);
1171
- if (bangText.startsWith("!")) {
1172
- const cmd = bangText.slice(1).trim();
1173
- const bridgeShellCommand = this.bridge.shellCommand;
1174
- if (cmd && bridgeShellCommand) {
1175
- try {
1176
- const result2 = await this.bridge.shellCommand(sessionId, cmd);
1177
- const longestRun = Math.max(0, ...Array.from((result2.output || "").matchAll(/`+/g), (m) => m[0].length));
1178
- const fence = "`".repeat(Math.max(3, longestRun + 1));
1179
- const output = result2.output ? `${fence}
1180
- ${result2.output}
1181
- ${fence}` : "(no output)";
1182
- const exitLine = result2.exitCode !== null && result2.exitCode !== 0 ? `
1183
- Exit code: ${result2.exitCode}` : "";
1184
- await this.sendMessage(envelope.chatId, `$ ${cmd}
1185
- ${output}${exitLine}`);
1186
- } catch (error) {
1187
- await this.sendMessage(envelope.chatId, `Shell command failed: ${error instanceof Error ? error.message : String(error)}`);
1188
- }
1189
- return;
1190
- }
1191
- }
1192
- let promptText = envelope.text;
1193
- if ((envelope.isGroup || this.config.sessionScope === "single") && !envelope.alreadyPrefixed && !(this.isSlashCommand(envelope.text) && this.isRecognizedCommand(envelope.text, sessionId))) {
1194
- const who = sanitizeSenderName(envelope.senderName || envelope.senderId || "unknown");
1195
- promptText = `[${who}] ${sanitizePromptText(promptText)}`;
1196
- }
1197
- if (envelope.referencedText) {
1198
- const quoted = sanitizeQuotedText(envelope.referencedText, 500);
1199
- promptText = `[Replying to: "${quoted}"]
1200
-
1201
- ${promptText}`;
1202
- }
1203
- let imageBase64 = envelope.imageBase64;
1204
- let imageMimeType = envelope.imageMimeType;
1205
- if (envelope.attachments?.length) {
1206
- const filePaths = [];
1207
- for (const att of envelope.attachments) {
1208
- if (att.type === "image" && att.data && !imageBase64) {
1209
- imageBase64 = att.data;
1210
- imageMimeType = att.mimeType;
1211
- } else if (att.filePath) {
1212
- const label = att.type === "file" ? "file" : att.type;
1213
- const name = att.fileName ? ` "${sanitizeQuotedText(att.fileName, 128)}"` : "";
1214
- const renderedPath = sanitizePromptPath(att.filePath);
1215
- filePaths.push(`User sent a ${label}${name}. It has been saved to: ${renderedPath}`);
1216
- }
1217
- }
1218
- if (filePaths.length > 0) {
1219
- promptText = promptText + "\n\n" + filePaths.join("\n");
1220
- }
1221
- }
1222
- if (this.config.instructions && !this.instructedSessions.has(sessionId)) {
1223
- promptText = `${this.config.instructions}
1224
-
1225
- ${promptText}`;
1226
- this.instructedSessions.add(sessionId);
1227
- }
1228
- const groupCfg = envelope.isGroup ? this.config.groups[envelope.chatId] || this.config.groups["*"] : void 0;
1229
- const mode = groupCfg?.dispatchMode || this.config.dispatchMode || "steer";
1230
- const active = this.activePrompts.get(sessionId);
1231
- let steerWatchdog;
1232
- if (active) {
1233
- switch (mode) {
1234
- case "collect": {
1235
- let buffer = this.collectBuffers.get(sessionId);
1236
- if (!buffer) {
1237
- buffer = [];
1238
- this.collectBuffers.set(sessionId, buffer);
1239
- }
1240
- buffer.push({ text: promptText, envelope });
1241
- return;
1242
- }
1243
- case "steer": {
1244
- if (!this.isAuthorizedForSharedSession(envelope)) {
1245
- process.stderr.write(`[${this.name}] steer denied for ${envelope.senderId} in shared session (chat=${sanitizeLogText(envelope.chatId, 64)}); queuing instead
1246
- `);
1247
- break;
1248
- }
1249
- active.cancelled = true;
1250
- process.stderr.write(`[${this.name}] steer: cancelled active turn for ${envelope.senderId} in session ${sessionId}
1251
- `);
1252
- this.stopActiveStreaming(active, sessionId, "steer");
1253
- void this.bridge.cancelSession(sessionId).catch((err) => {
1254
- process.stderr.write(`[${this.name}] cancelSession failed for session=${sessionId} (steer): ${err instanceof Error ? err.message : err}
1255
- `);
1256
- });
1257
- steerWatchdog = setTimeout(() => {
1258
- if (this.activePrompts.get(sessionId) === active) {
1259
- process.stderr.write(`[${this.name}] steer queued behind active turn for session ${sessionId}: still waiting after ${CLEAR_CANCEL_TIMEOUT_MS}ms (use /clear to recover)
1260
- `);
1261
- }
1262
- }, CLEAR_CANCEL_TIMEOUT_MS);
1263
- steerWatchdog.unref?.();
1264
- promptText = `[The user sent a new message while you were working. Their previous request has been cancelled.]
1265
-
1266
- ${promptText}`;
1267
- break;
1268
- }
1269
- case "followup": {
1270
- break;
1271
- }
1272
- default: {
1273
- const _exhaustive = mode;
1274
- throw new Error(`Unknown dispatch mode: ${_exhaustive}`);
1275
- }
1276
- }
1277
- }
1278
- const prev = this.sessionQueues.get(sessionId) ?? Promise.resolve();
1279
- const generation = this.sessionGenerations.get(sessionId) ?? 0;
1280
- const useBlockStreaming = this.config.blockStreaming === "on";
1281
- const current = prev.then(async () => {
1282
- clearTimeout(steerWatchdog);
1283
- if ((this.sessionGenerations.get(sessionId) ?? 0) !== generation) {
1284
- const loggedText = sanitizeLogText(envelope.text, 80);
1285
- process.stderr.write(`[${this.name}] dropped queued turn from ${envelope.senderId} for session ${sessionId}: session was cleared before it ran (text: ${loggedText})
1286
- `);
1287
- return;
1288
- }
1289
- let doneResolve = /* @__PURE__ */ __name(() => {
1290
- }, "doneResolve");
1291
- const done = new Promise((r) => {
1292
- doneResolve = r;
1293
- });
1294
- const promptState = {
1295
- cancelled: false,
1296
- done,
1297
- resolve: doneResolve,
1298
- chatId: envelope.chatId,
1299
- messageId: envelope.messageId
1300
- };
1301
- this.activePrompts.set(sessionId, promptState);
1302
- this.onPromptStart(envelope.chatId, sessionId, envelope.messageId);
1303
- const streamer = useBlockStreaming ? new BlockStreamer({
1304
- minChars: this.config.blockStreamingChunk?.minChars ?? 400,
1305
- maxChars: this.config.blockStreamingChunk?.maxChars ?? 1e3,
1306
- idleMs: this.config.blockStreamingCoalesce?.idleMs ?? 1500,
1307
- send: /* @__PURE__ */ __name((text) => this.sendMessage(envelope.chatId, text), "send")
1308
- }) : null;
1309
- promptState.stopStreaming = () => streamer?.stop();
1310
- const onChunk = /* @__PURE__ */ __name((sid, chunk) => {
1311
- if (sid === sessionId && !promptState.cancelled) {
1312
- this.onResponseChunk(envelope.chatId, chunk, sessionId);
1313
- streamer?.push(chunk);
1314
- }
1315
- }, "onChunk");
1316
- const promptBridge = this.bridge;
1317
- promptBridge.on("textChunk", onChunk);
1318
- try {
1319
- const response = await promptBridge.prompt(sessionId, promptText, {
1320
- imageBase64,
1321
- imageMimeType
1322
- });
1323
- if (promptState.cancelRequested && !promptState.cancelled) {
1324
- promptState.cancelled = await promptState.cancelRequested;
1325
- }
1326
- if (!promptState.cancelled && response) {
1327
- if (streamer) {
1328
- await streamer.flush();
1329
- } else {
1330
- await this.onResponseComplete(envelope.chatId, response, sessionId);
1331
- }
1332
- }
1333
- } finally {
1334
- promptBridge.off("textChunk", onChunk);
1335
- streamer?.stop();
1336
- const stillCurrent = this.activePrompts.get(sessionId) === promptState;
1337
- if (!promptState.clearEvicted) {
1338
- try {
1339
- this.onPromptEnd(envelope.chatId, sessionId, envelope.messageId);
1340
- } catch (err) {
1341
- process.stderr.write(`[${this.name}] onPromptEnd threw in finally for session ${sessionId}: ${err instanceof Error ? err.message : err}
1342
- `);
1343
- }
1344
- }
1345
- if (stillCurrent) {
1346
- this.activePrompts.delete(sessionId);
1347
- }
1348
- promptState.resolve();
1349
- const buffer = this.collectBuffers.get(sessionId);
1350
- if (stillCurrent && buffer && buffer.length > 0) {
1351
- this.collectBuffers.delete(sessionId);
1352
- const lost = buffer.length;
1353
- const coalesced = buffer.map((b) => b.text).join("\n\n");
1354
- const lastEnvelope = buffer[buffer.length - 1].envelope;
1355
- const syntheticEnvelope = {
1356
- ...lastEnvelope,
1357
- text: coalesced,
1358
- // Coalesced text already carries each message's [sender] prefix.
1359
- alreadyPrefixed: true,
1360
- // Clear attachments/references — already resolved in original text
1361
- referencedText: void 0,
1362
- attachments: void 0,
1363
- imageBase64: void 0,
1364
- imageMimeType: void 0
1365
- };
1366
- this.handleInbound(syntheticEnvelope).catch((err) => {
1367
- process.stderr.write(`[${this.name}] dropped ${lost} buffered message(s) on collect re-entry for session ${sessionId} (last sender ${lastEnvelope.senderId}): ${err instanceof Error ? err.message : String(err)}
1368
- `);
1369
- });
1370
- }
1371
- }
1372
- });
1373
- this.sessionQueues.set(sessionId, current.catch(() => {
1374
- }));
1375
- await current;
1376
- }
1377
- async onPairingRequired(chatId, code) {
1378
- if (code) {
1379
- await this.sendMessage(chatId, `Your pairing code is: ${code}
1380
-
1381
- Ask the bot operator to approve you with:
1382
- qwen channel pairing approve ${this.name} ${code}`);
1383
- } else {
1384
- await this.sendMessage(chatId, "Too many pending pairing requests. Please try again later.");
1385
- }
1386
- }
1387
- };
1388
-
1389
- // packages/channels/base/dist/index.js
1390
- init_esbuild_shims();
1391
-
1392
- // packages/channels/base/dist/AcpBridge.js
1393
- init_esbuild_shims();
1394
- import { spawn } from "node:child_process";
1395
- import { Readable, Writable } from "node:stream";
1396
- import { EventEmitter } from "node:events";
1397
- function readAvailableCommandAltNames(raw) {
1398
- if (typeof raw !== "object" || raw === null)
1399
- return void 0;
1400
- const record = raw;
1401
- const meta = record["_meta"];
1402
- const fromMeta = typeof meta === "object" && meta !== null ? meta["altNames"] : void 0;
1403
- const source = Array.isArray(record["altNames"]) ? record["altNames"] : Array.isArray(fromMeta) ? fromMeta : void 0;
1404
- if (!source)
1405
- return void 0;
1406
- const names = source.filter((n) => typeof n === "string");
1407
- return names.length > 0 ? names : void 0;
1408
- }
1409
- __name(readAvailableCommandAltNames, "readAvailableCommandAltNames");
1410
- var AcpBridge = class extends EventEmitter {
1411
- static {
1412
- __name(this, "AcpBridge");
1413
- }
1414
- child = null;
1415
- connection = null;
1416
- options;
1417
- _availableCommands = [];
1418
- constructor(options) {
1419
- super();
1420
- this.options = options;
1421
- }
1422
- get availableCommands() {
1423
- return this._availableCommands;
1424
- }
1425
- async start() {
1426
- const { cliEntryPath, cwd } = this.options;
1427
- const args = [
1428
- ...process.execArgv.filter((a) => !/^--inspect(-brk)?($|=)/.test(a)),
1429
- cliEntryPath,
1430
- "--acp"
1431
- ];
1432
- if (this.options.model) {
1433
- args.push("--model", this.options.model);
1434
- }
1435
- this.child = spawn(process.execPath, args, {
1436
- cwd,
1437
- stdio: ["pipe", "pipe", "pipe"],
1438
- env: { ...process.env },
1439
- shell: false
1440
- });
1441
- this.child.stderr?.on("data", (data) => {
1442
- const msg = data.toString().trim();
1443
- if (msg) {
1444
- process.stderr.write(`[AcpBridge] ${msg}
1445
- `);
1446
- }
1447
- });
1448
- this.child.on("exit", (code, signal) => {
1449
- process.stderr.write(`[AcpBridge] Process exited (code=${code}, signal=${signal})
1450
- `);
1451
- this.connection = null;
1452
- this.child = null;
1453
- this.emit("disconnected", code, signal);
1454
- });
1455
- await new Promise((resolve2) => setTimeout(resolve2, 1e3));
1456
- if (!this.child || this.child.killed) {
1457
- throw new Error("ACP process failed to start");
1458
- }
1459
- const stdout = Readable.toWeb(this.child.stdout);
1460
- const stdin = Writable.toWeb(this.child.stdin);
1461
- const stream = ndJsonStream(stdin, stdout);
1462
- this.connection = new ClientSideConnection(() => ({
1463
- sessionUpdate: /* @__PURE__ */ __name((params) => {
1464
- this.handleSessionUpdate(params);
1465
- return Promise.resolve();
1466
- }, "sessionUpdate"),
1467
- requestPermission: /* @__PURE__ */ __name(async (params) => {
1468
- const options = Array.isArray(params.options) ? params.options : [];
1469
- const optionId = options.find((o) => o.optionId === "proceed_once")?.optionId || options[0]?.optionId || "proceed_once";
1470
- return { outcome: { outcome: "selected", optionId } };
1471
- }, "requestPermission"),
1472
- extNotification: /* @__PURE__ */ __name(async () => {
1473
- }, "extNotification")
1474
- }), stream);
1475
- await this.connection.initialize({
1476
- protocolVersion: PROTOCOL_VERSION,
1477
- clientCapabilities: {}
1478
- });
1479
- }
1480
- async newSession(cwd) {
1481
- const conn = this.ensureConnection();
1482
- const response = await conn.newSession({ cwd, mcpServers: [] });
1483
- return response.sessionId;
1484
- }
1485
- async loadSession(sessionId, cwd) {
1486
- const conn = this.ensureConnection();
1487
- const response = await conn.loadSession({
1488
- sessionId,
1489
- cwd,
1490
- mcpServers: []
1491
- });
1492
- return response.sessionId;
1493
- }
1494
- async prompt(sessionId, text, options) {
1495
- const conn = this.ensureConnection();
1496
- const chunks = [];
1497
- const onChunk = /* @__PURE__ */ __name((sid, chunk) => {
1498
- if (sid === sessionId)
1499
- chunks.push(chunk);
1500
- }, "onChunk");
1501
- this.on("textChunk", onChunk);
1502
- const prompt = [];
1503
- if (options?.imageBase64 && options.imageMimeType) {
1504
- prompt.push({
1505
- type: "image",
1506
- data: options.imageBase64,
1507
- mimeType: options.imageMimeType
1508
- });
1509
- }
1510
- prompt.push({ type: "text", text });
1511
- try {
1512
- await conn.prompt({
1513
- sessionId,
1514
- prompt
1515
- });
1516
- } finally {
1517
- this.off("textChunk", onChunk);
1518
- }
1519
- return chunks.join("");
1520
- }
1521
- async cancelSession(sessionId) {
1522
- const conn = this.ensureConnection();
1523
- await conn.cancel({ sessionId });
1524
- }
1525
- stop() {
1526
- if (this.child) {
1527
- this.child.kill();
1528
- this.child = null;
1529
- }
1530
- this.connection = null;
1531
- }
1532
- get isConnected() {
1533
- return this.child !== null && !this.child.killed && this.child.exitCode === null;
1534
- }
1535
- handleSessionUpdate(params) {
1536
- const { sessionId } = params;
1537
- const update = params["update"];
1538
- if (!update)
1539
- return;
1540
- const type = update["sessionUpdate"];
1541
- switch (type) {
1542
- case "agent_message_chunk": {
1543
- const content = update["content"];
1544
- if (content?.type === "text" && content.text) {
1545
- this.emit("textChunk", sessionId, content.text);
1546
- }
1547
- break;
1548
- }
1549
- case "tool_call": {
1550
- const event = {
1551
- sessionId,
1552
- toolCallId: update["toolCallId"],
1553
- kind: update["kind"] || "",
1554
- title: update["title"] || "",
1555
- status: update["status"] || "pending",
1556
- rawInput: update["rawInput"]
1557
- };
1558
- this.emit("toolCall", event);
1559
- break;
1560
- }
1561
- case "available_commands_update": {
1562
- if (Array.isArray(update["availableCommands"])) {
1563
- this._availableCommands = update["availableCommands"].map((cmd) => {
1564
- const altNames = readAvailableCommandAltNames(cmd);
1565
- return altNames ? { ...cmd, altNames } : cmd;
1566
- });
1567
- }
1568
- break;
1569
- }
1570
- default:
1571
- break;
1572
- }
1573
- this.emit("sessionUpdate", params);
1574
- }
1575
- ensureConnection() {
1576
- if (!this.connection || !this.isConnected) {
1577
- throw new Error("Not connected to ACP agent");
1578
- }
1579
- return this.connection;
1580
- }
1581
- };
1582
-
1583
- // packages/channels/base/dist/DaemonChannelBridge.js
1584
- init_esbuild_shims();
1585
-
1586
- export {
1587
- resolvePath,
1588
- getGlobalQwenDir,
1589
- AcpBridge,
1590
- PairingStore,
1591
- SessionRouter,
1592
- sanitizeSenderName,
1593
- sanitizePromptText,
1594
- sanitizeLogText,
1595
- ChannelBase
1596
- };