@zubriq/code 0.19.8-1 → 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-OWTCJQNO.js → MaxSizedBox-W6SAJMJC.js} +13 -11
  10. package/dist/chunks/{StandaloneSessionPicker-6NGEQFBH.js → StandaloneSessionPicker-YBKZZKIA.js} +28 -23
  11. package/dist/chunks/{acpAgent-XPRWSNOW.js → acpAgent-SQJQBOTS.js} +110 -80
  12. package/dist/chunks/{agent-TB6S4VXU.js → agent-QU7Q2RZN.js} +8 -8
  13. package/dist/chunks/{agent-headless-R3XYK5TT.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-HVRG6SMU.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-YRM6BC6H.js → chunk-27AYMAL6.js} +506 -63
  20. package/dist/chunks/{chunk-536GZ6UF.js → chunk-2MDC5WDB.js} +17 -111
  21. package/dist/chunks/{chunk-H3D6XAOL.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-ZM5BPDXV.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-PA7QS4X5.js → chunk-43W4YSO6.js} +1 -12
  28. package/dist/chunks/{chunk-5EB2HK3X.js → chunk-4IK56VLC.js} +1 -1
  29. package/dist/chunks/{chunk-UE5TCZE6.js → chunk-5FAUIGXH.js} +5 -13
  30. package/dist/chunks/{chunk-VEB3NYSO.js → chunk-5MBO76SL.js} +5 -0
  31. package/dist/chunks/{chunk-PDITONOB.js → chunk-5MRUKUOG.js} +5347 -4132
  32. package/dist/chunks/{chunk-4UC3U66K.js → chunk-6EUMFVPJ.js} +5 -5
  33. package/dist/chunks/{chunk-YOGGIAGF.js → chunk-6JL5OVLX.js} +1 -1
  34. package/dist/chunks/{chunk-QG4ZO2EB.js → chunk-74E6VVF6.js} +11 -11
  35. package/dist/chunks/chunk-A2X3KLC3.js +3807 -0
  36. package/dist/chunks/{chunk-BA4HLTG2.js → chunk-B6B2MUUF.js} +5 -5
  37. package/dist/chunks/{chunk-XKYOIERV.js → chunk-BN5VYAOZ.js} +681 -1239
  38. package/dist/chunks/{chunk-IO76BGJ3.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-ZWSTIPSW.js → chunk-CEITMQ62.js} +2 -2
  42. package/dist/chunks/{chunk-7AQTBTVY.js → chunk-EJP6Z7NU.js} +4 -3
  43. package/dist/chunks/{chunk-RTW5ZXHO.js → chunk-EZTNYRHH.js} +6 -5
  44. package/dist/chunks/{chunk-MJQFA66Z.js → chunk-FAATDXYS.js} +308 -81
  45. package/dist/chunks/{chunk-DNV4FSN5.js → chunk-FBET5XZ2.js} +6 -4
  46. package/dist/chunks/{chunk-HTC4R67V.js → chunk-FES4YXJ3.js} +9 -9
  47. package/dist/chunks/{chunk-NBKE6QRZ.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-J6OET6C3.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-SM624MOZ.js → chunk-GWUN54UD.js} +1 -1
  55. package/dist/chunks/chunk-H3TELH3H.js +942 -0
  56. package/dist/chunks/{chunk-XZULYFIX.js → chunk-HD4DZI2L.js} +2 -2
  57. package/dist/chunks/{chunk-KLLLWQM2.js → chunk-HPQIVXSP.js} +1 -1
  58. package/dist/chunks/{chunk-WSVZFWN4.js → chunk-IBY3Q3HG.js} +693 -104
  59. package/dist/chunks/{chunk-JSEOX4CM.js → chunk-JGR6W4IX.js} +14 -8
  60. package/dist/chunks/chunk-K652V4NH.js +283 -0
  61. package/dist/chunks/{chunk-OCBJKHF2.js → chunk-KH2CZZDD.js} +2 -2
  62. package/dist/chunks/chunk-LGFAYIKX.js +115 -0
  63. package/dist/chunks/{chunk-QG5KDB6X.js → chunk-LZDW5TDV.js} +5 -5
  64. package/dist/chunks/{chunk-ENKWIF75.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-ML2ZWH2K.js → chunk-PCWYMX57.js} +14 -153
  68. package/dist/chunks/{chunk-NHFBLJOI.js → chunk-Q3PQ5VGJ.js} +1 -1
  69. package/dist/chunks/{chunk-LGXBB2R2.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-DHKDK57Q.js → chunk-SNZPA74J.js} +146 -54
  73. package/dist/chunks/{chunk-PWR5MVM3.js → chunk-SSP3UT5C.js} +4 -4
  74. package/dist/chunks/{chunk-4LETJLG2.js → chunk-SUCRVT5L.js} +1 -1
  75. package/dist/chunks/{chunk-5YLZHETW.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-N4HTVZDZ.js → chunk-VR3KCBHH.js} +1 -1
  82. package/dist/chunks/{chunk-APYUOROB.js → chunk-W4CTWW7M.js} +423 -61
  83. package/dist/chunks/{chunk-ABANKQYY.js → chunk-WJAW2O5H.js} +92 -465
  84. package/dist/chunks/{chunk-6RSBRKF5.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-4FQL7KUS.js → chunk-XWDHZP7P.js} +8 -8
  89. package/dist/chunks/{chunk-REPRY4F7.js → chunk-YCKKP4QA.js} +10 -10
  90. package/dist/chunks/{chunk-YNWQ3PUQ.js → chunk-YJWMOGGZ.js} +3 -3
  91. package/dist/chunks/{chunk-RFIL6REB.js → chunk-YKZIAK7C.js} +119 -29
  92. package/dist/chunks/{chunk-45YCXM2H.js → chunk-YZDBUH6S.js} +45 -9
  93. package/dist/chunks/chunk-Z3I3QJK3.js +47 -0
  94. package/dist/chunks/{chunk-G23FFNLT.js → chunk-ZDGPPCTW.js} +20 -8
  95. package/dist/chunks/chunk-ZL5HN75Q.js +61 -0
  96. package/dist/chunks/{chunk-IQX5S2RS.js → chunk-ZL7JCYWM.js} +66 -7
  97. package/dist/chunks/{computer-use-A72BECH4.js → computer-use-4B45EG2F.js} +8 -8
  98. package/dist/chunks/{contextCommand-RREXA4B7.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-Y6AFXTET.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-OAHBOGOO.js → earlyInputCapture-JWMCAJZR.js} +8 -8
  112. package/dist/chunks/{edit-SFYW3FOV.js → edit-O5FUVHBE.js} +9 -9
  113. package/dist/chunks/{en-AXARKOWO.js → en-N4EPZAQI.js} +31 -2
  114. package/dist/chunks/{enter-worktree-LUPPQ2NH.js → enter-worktree-LOJFRC3D.js} +8 -8
  115. package/dist/chunks/{enterPlanMode-44GEN5U6.js → enterPlanMode-VS3FAUPX.js} +8 -8
  116. package/dist/chunks/environment-WKW5TLSH.js +90 -0
  117. package/dist/chunks/{errors-H3Q3W3DL.js → errors-LEZXHJMR.js} +10 -10
  118. package/dist/chunks/{exit-worktree-34CVVWT4.js → exit-worktree-RN2NXRMP.js} +8 -8
  119. package/dist/chunks/{exitPlanMode-5G5BHNUP.js → exitPlanMode-EPC424I4.js} +8 -8
  120. package/dist/chunks/{fast-path-7PKGFBRM.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-2II6JH4H.js → gemini-QCKK4XNU.js} +438 -265
  124. package/dist/chunks/{geminiContentGenerator-SWEJVOC4.js → geminiContentGenerator-DX4SRXJA.js} +1 -1
  125. package/dist/chunks/{glob-5DFC7DXW.js → glob-4URMJWRX.js} +8 -8
  126. package/dist/chunks/{grep-QYVXGRRJ.js → grep-AEQB5KED.js} +8 -8
  127. package/dist/chunks/handleAutoUpdate-Y4YNG34Y.js +65 -0
  128. package/dist/chunks/{initializer-XX3UGUHE.js → initializer-NDEBMSOG.js} +17 -13
  129. package/dist/chunks/{ja-HFV5QCHR.js → ja-CUHZOLEJ.js} +30 -1
  130. package/dist/chunks/{list-AX5FWBVC.js → list-VSCBNOWT.js} +17 -15
  131. package/dist/chunks/{loadedSettingsAdapter-ISOM6GJ7.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-74F6725E.js → mcp-MLPWTHUV.js} +15 -13
  136. package/dist/chunks/{monitor-W4WRSC2C.js → monitor-WCVHIJUJ.js} +16 -11
  137. package/dist/chunks/{nonInteractiveCli-4QMV6C4J.js → nonInteractiveCli-32HZK274.js} +52 -44
  138. package/dist/chunks/{notebook-edit-XBZEIP42.js → notebook-edit-NPOMPKS4.js} +9 -9
  139. package/dist/chunks/{openaiContentGenerator-RTUJDESE.js → openaiContentGenerator-6AIBSRQK.js} +6 -6
  140. package/dist/chunks/{pidfile-YWSGNTPK.js → pidfile-7Y3QEDSZ.js} +8 -8
  141. package/dist/chunks/{pt-2THT3YHA.js → pt-OLYN42DS.js} +26 -1
  142. package/dist/chunks/{qwenContentGenerator-4ZFYAZLY.js → qwenContentGenerator-OUYOAYT4.js} +10 -10
  143. package/dist/chunks/{read-file-D7DKDIAC.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-KG7JYNER.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-XGJSNF6S.js → run-qwen-serve-WUEHQROF.js} +602 -143
  148. package/dist/chunks/{scheduler-MOLWOE4G.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-4ZNVPYND.js → serve-UCDGVZFI.js} +17 -14
  151. package/dist/chunks/{server-ACWDFHIF.js → server-FVGFFVZ4.js} +1394 -328
  152. package/dist/chunks/{session-VYO2GRO2.js → session-RIGKDMGR.js} +53 -45
  153. package/dist/chunks/{settings-4J2O3K22.js → settings-LVFFGXGE.js} +24 -20
  154. package/dist/chunks/{shell-RKERJYXR.js → shell-PAWNAFYN.js} +8 -8
  155. package/dist/chunks/{skill-JYACZJ5V.js → skill-5YIN3Z6Y.js} +15 -5
  156. package/dist/chunks/{spawnChannel-BA3U3RGX.js → spawnChannel-KIMZHWNR.js} +10 -10
  157. package/dist/chunks/{src-3P337V4P.js → src-BIWCD3VX.js} +21 -9
  158. package/dist/chunks/{startInteractiveUI-SUSZ6FWB.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-YVWGRZEA.js → team-create-26WOMISV.js} +8 -8
  162. package/dist/chunks/{team-plan-approval-VRF54TTI.js → team-plan-approval-B5XB6UQC.js} +8 -8
  163. package/dist/chunks/{theme-manager-JW2KOACL.js → theme-manager-4TJ5TEFD.js} +8 -8
  164. package/dist/chunks/{tool-search-KUSMAZBN.js → tool-search-FFU63E6M.js} +6 -23
  165. package/dist/chunks/total-session-admission-JOD7SMPJ.js +72 -0
  166. package/dist/chunks/{trustedFolders-ERGSUCDK.js → trustedFolders-IJ2QHXSC.js} +9 -9
  167. package/dist/chunks/updateCheck-5Z25DCBP.js +65 -0
  168. package/dist/chunks/{validateNonInterActiveAuth-DDI62H5X.js → validateNonInterActiveAuth-G5EY47DE.js} +49 -41
  169. package/dist/chunks/{version-VB3AHVUF.js → version-LGO3GOKQ.js} +2 -1
  170. package/dist/chunks/{workflow-WINKRCI4.js → workflow-ZTK3WT3D.js} +9 -9
  171. package/dist/chunks/{workspace-providers-status-ZVZPXKSO.js → workspace-providers-status-YMPTRKW2.js} +18 -15
  172. package/dist/chunks/workspace-registry-NBUL6LBH.js +74 -0
  173. package/dist/chunks/{workspace-service-GAS2LV2P.js → workspace-service-V6G2RCD4.js} +21 -19
  174. package/dist/chunks/{workspace-skills-status-4WGKYJVA.js → workspace-skills-status-ZCW44BCH.js} +16 -14
  175. package/dist/chunks/{write-file-UTLKVZNN.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
@@ -77,6 +77,7 @@ __name(getGlobalQwenDir, "getGlobalQwenDir");
77
77
  // packages/channels/base/dist/AcpBridge.js
78
78
  init_esbuild_shims();
79
79
  import { spawn } from "node:child_process";
80
+ import { randomUUID } from "node:crypto";
80
81
  import { Readable, Writable } from "node:stream";
81
82
  import { EventEmitter } from "node:events";
82
83
 
@@ -240,6 +241,7 @@ __name(readId, "readId");
240
241
  // packages/channels/base/dist/AcpBridge.js
241
242
  var MID_TURN_QUEUE_DRAIN_METHOD = "craft/drainMidTurnQueue";
242
243
  var ACP_EVENT_LOOP_STALL_RESTART_MS = 5 * 60 * 1e3;
244
+ var ACP_PERMISSION_RESPONSE_TIMEOUT_MS = 5 * 60 * 1e3;
243
245
  var ACP_EVENT_LOOP_STALL_RE = /^\[perf\] acp agent event loop stall: max=(\d+(?:\.\d+)?)ms/m;
244
246
  function readAvailableCommandAltNames(raw) {
245
247
  if (typeof raw !== "object" || raw === null)
@@ -266,6 +268,7 @@ var AcpBridge = class extends EventEmitter {
266
268
  channelLoopToolHandlers = [];
267
269
  channelLoopMcpRegistered = false;
268
270
  channelLoopMcpRegistration = null;
271
+ pendingPermissions = /* @__PURE__ */ new Map();
269
272
  constructor(options) {
270
273
  super();
271
274
  this.options = options;
@@ -292,7 +295,7 @@ var AcpBridge = class extends EventEmitter {
292
295
  this.child.stderr?.on("data", (data) => {
293
296
  const msg = data.toString().trim();
294
297
  if (msg) {
295
- process.stderr.write(`[AcpBridge] ${msg}
298
+ process.stderr.write(`[AcpBridge] ${sanitizeLogText(msg, 4096)}
296
299
  `);
297
300
  this.maybeKillOnEventLoopStall(msg);
298
301
  }
@@ -300,6 +303,7 @@ var AcpBridge = class extends EventEmitter {
300
303
  this.child.on("exit", (code, signal) => {
301
304
  process.stderr.write(`[AcpBridge] Process exited (code=${code}, signal=${signal})
302
305
  `);
306
+ this.resolvePendingPermissions();
303
307
  this.connection = null;
304
308
  this.child = null;
305
309
  this.emit("disconnected", code, signal);
@@ -316,11 +320,7 @@ var AcpBridge = class extends EventEmitter {
316
320
  this.handleSessionUpdate(params);
317
321
  return Promise.resolve();
318
322
  }, "sessionUpdate"),
319
- requestPermission: /* @__PURE__ */ __name(async (params) => {
320
- const options = Array.isArray(params.options) ? params.options : [];
321
- const optionId = options.find((o) => o.optionId === "proceed_once")?.optionId || options[0]?.optionId || "proceed_once";
322
- return { outcome: { outcome: "selected", optionId } };
323
- }, "requestPermission"),
323
+ requestPermission: /* @__PURE__ */ __name(async (params) => this.requestPermission(params), "requestPermission"),
324
324
  extMethod: /* @__PURE__ */ __name(async (method, params) => this.handleExtMethod(method, params), "extMethod"),
325
325
  extNotification: /* @__PURE__ */ __name(async () => {
326
326
  }, "extNotification")
@@ -387,9 +387,28 @@ var AcpBridge = class extends EventEmitter {
387
387
  }
388
388
  async cancelSession(sessionId) {
389
389
  const conn = this.ensureConnection();
390
- await conn.cancel({ sessionId });
390
+ try {
391
+ await conn.cancel({ sessionId });
392
+ } finally {
393
+ this.resolvePendingPermissions(sessionId);
394
+ }
395
+ }
396
+ async respondToPermission(requestId, response) {
397
+ const pending = this.pendingPermissions.get(requestId);
398
+ if (!pending) {
399
+ return false;
400
+ }
401
+ clearTimeout(pending.timeout);
402
+ this.pendingPermissions.delete(requestId);
403
+ pending.resolve(response);
404
+ this.emit("permissionResolved", {
405
+ requestId,
406
+ outcome: response.outcome
407
+ });
408
+ return true;
391
409
  }
392
410
  stop() {
411
+ this.resolvePendingPermissions();
393
412
  if (this.child) {
394
413
  this.child.kill();
395
414
  this.child = null;
@@ -445,6 +464,53 @@ var AcpBridge = class extends EventEmitter {
445
464
  }
446
465
  return this.connection;
447
466
  }
467
+ requestPermission(request) {
468
+ const requestId = `acp-permission-${randomUUID()}`;
469
+ const sessionId = typeof request.sessionId === "string" && request.sessionId.length > 0 ? request.sessionId : request.toolCall.toolCallId;
470
+ return new Promise((resolve2) => {
471
+ const timeout = setTimeout(() => {
472
+ const pending = this.pendingPermissions.get(requestId);
473
+ if (!pending) {
474
+ return;
475
+ }
476
+ process.stderr.write(`[AcpBridge] permission request ${sanitizeLogText(requestId, 128)} timed out after ${ACP_PERMISSION_RESPONSE_TIMEOUT_MS}ms (session=${sanitizeLogText(pending.sessionId, 128)})
477
+ `);
478
+ this.pendingPermissions.delete(requestId);
479
+ const response = {
480
+ outcome: { outcome: "cancelled" }
481
+ };
482
+ pending.resolve(response);
483
+ this.emit("permissionResolved", {
484
+ requestId,
485
+ outcome: response.outcome
486
+ });
487
+ }, ACP_PERMISSION_RESPONSE_TIMEOUT_MS);
488
+ timeout.unref?.();
489
+ this.pendingPermissions.set(requestId, { sessionId, resolve: resolve2, timeout });
490
+ this.emit("permissionRequest", {
491
+ requestId,
492
+ sessionId,
493
+ request
494
+ });
495
+ });
496
+ }
497
+ resolvePendingPermissions(sessionId) {
498
+ const response = {
499
+ outcome: { outcome: "cancelled" }
500
+ };
501
+ for (const [requestId, pending] of this.pendingPermissions) {
502
+ if (sessionId !== void 0 && pending.sessionId !== sessionId) {
503
+ continue;
504
+ }
505
+ clearTimeout(pending.timeout);
506
+ this.pendingPermissions.delete(requestId);
507
+ pending.resolve(response);
508
+ this.emit("permissionResolved", {
509
+ requestId,
510
+ outcome: response.outcome
511
+ });
512
+ }
513
+ }
448
514
  maybeKillOnEventLoopStall(stderr) {
449
515
  const match = ACP_EVENT_LOOP_STALL_RE.exec(stderr);
450
516
  if (!match)
@@ -1237,6 +1303,36 @@ var GroupGate = class {
1237
1303
  }
1238
1304
  };
1239
1305
 
1306
+ // packages/channels/base/dist/DmGate.js
1307
+ init_esbuild_shims();
1308
+ var DmGate = class {
1309
+ static {
1310
+ __name(this, "DmGate");
1311
+ }
1312
+ policy;
1313
+ constructor(policy = "open") {
1314
+ this.policy = policy;
1315
+ }
1316
+ /**
1317
+ * DM check: policy gating for private/non-group messages.
1318
+ * Evaluation order:
1319
+ * 1. Group messages bypass this gate (handled by GroupGate)
1320
+ * 2. dmPolicy (disabled → drop)
1321
+ *
1322
+ * Symmetric with GroupGate — GroupGate owns group messages,
1323
+ * DmGate owns DM messages.
1324
+ */
1325
+ check(envelope) {
1326
+ if (envelope.isGroup) {
1327
+ return { allowed: true };
1328
+ }
1329
+ if (this.policy === "disabled") {
1330
+ return { allowed: false, reason: "disabled" };
1331
+ }
1332
+ return { allowed: true };
1333
+ }
1334
+ };
1335
+
1240
1336
  // packages/channels/base/dist/group-history-store.js
1241
1337
  init_esbuild_shims();
1242
1338
  import { appendFileSync, chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
@@ -2267,6 +2363,66 @@ function truncateError(message) {
2267
2363
  }
2268
2364
  __name(truncateError, "truncateError");
2269
2365
 
2366
+ // packages/channels/base/dist/channel-memory-intent.js
2367
+ init_esbuild_shims();
2368
+ var REMEMBER_PATTERNS = [
2369
+ /^记住[::]\s*(.+)$/su,
2370
+ /^记一下[::,,]?\s*(.+)$/su,
2371
+ /^帮我记一下[::,,]?\s*(.+)$/su,
2372
+ /^帮我记住[::,,]?\s*(.+)$/su,
2373
+ /^以后记住[::,,]?\s*(.+)$/su,
2374
+ /^remember:\s*(.+)$/isu
2375
+ ];
2376
+ var LIST_PATTERNS = [
2377
+ /^你现在记住了什么[??]?$/u,
2378
+ /^查看记忆$/u,
2379
+ /^当前记忆$/u,
2380
+ /^这个聊天你记住了什么[??]?$/u,
2381
+ /^what do you remember[??]?$/iu
2382
+ ];
2383
+ var CLEAR_REQUEST_PATTERNS = [
2384
+ /^清空记忆$/u,
2385
+ /^清除记忆$/u,
2386
+ /^忘掉这个聊天的所有记忆$/u,
2387
+ /^把.+的?记忆清空$/u,
2388
+ /^clear memory$/iu
2389
+ ];
2390
+ var CLEAR_CONFIRM_PATTERNS = [
2391
+ /^确认清空记忆$/u,
2392
+ /^确认清除记忆$/u,
2393
+ /^confirm clear memory$/iu
2394
+ ];
2395
+ function parseChannelMemoryIntent(text) {
2396
+ const trimmed = text.replace(PROMPT_UNSAFE_INVISIBLES, "").trim();
2397
+ if (!trimmed || trimmed.startsWith("/")) {
2398
+ return null;
2399
+ }
2400
+ for (const pattern of CLEAR_CONFIRM_PATTERNS) {
2401
+ if (pattern.test(trimmed)) {
2402
+ return { kind: "clear_confirm" };
2403
+ }
2404
+ }
2405
+ for (const pattern of CLEAR_REQUEST_PATTERNS) {
2406
+ if (pattern.test(trimmed)) {
2407
+ return { kind: "clear_request" };
2408
+ }
2409
+ }
2410
+ for (const pattern of LIST_PATTERNS) {
2411
+ if (pattern.test(trimmed)) {
2412
+ return { kind: "list" };
2413
+ }
2414
+ }
2415
+ for (const pattern of REMEMBER_PATTERNS) {
2416
+ const match = trimmed.match(pattern);
2417
+ const remembered = match?.[1]?.replace(PROMPT_UNSAFE_INVISIBLES, "").trim();
2418
+ if (remembered) {
2419
+ return { kind: "remember", text: remembered };
2420
+ }
2421
+ }
2422
+ return null;
2423
+ }
2424
+ __name(parseChannelMemoryIntent, "parseChannelMemoryIntent");
2425
+
2270
2426
  // packages/channels/base/dist/ChannelBase.js
2271
2427
  var CLEAR_CANCEL_TIMEOUT_MS = 3e3;
2272
2428
  var GROUP_HISTORY_CONTEXT_MARKER = "[Chat messages since your last reply - for context]";
@@ -2274,6 +2430,8 @@ var CURRENT_MESSAGE_MARKER = "[Current message - respond to this]";
2274
2430
  var GROUP_HISTORY_ENTRY_TEXT_LIMIT = 1e3;
2275
2431
  var GROUP_HISTORY_ENTRY_METADATA_LIMIT = 256;
2276
2432
  var LOOP_CANCEL_GRACE_MS = 5e3;
2433
+ var CHANNEL_MEMORY_CLASSIFIER_MIN_CONFIDENCE = 0.7;
2434
+ var CHANNEL_MEMORY_CLASSIFIER_TRIGGER_RE = /(记住|记得|记一下|记忆|忘掉|忘记|清空|清除|删除|保存|remember|memory|forget)/iu;
2277
2435
  var LOOP_TIMED_OUT_MESSAGE = "loop timed out";
2278
2436
  var COMMAND_TOKEN_CHARS = "a-zA-Z0-9_:-";
2279
2437
  var PARSE_COMMAND_RE = new RegExp(`^\\/([${COMMAND_TOKEN_CHARS}]+)(?:@\\S+)?\\s*(.*)`, "s");
@@ -2297,6 +2455,7 @@ var ChannelBase = class {
2297
2455
  config;
2298
2456
  bridge;
2299
2457
  groupGate;
2458
+ dmGate;
2300
2459
  gate;
2301
2460
  router;
2302
2461
  name;
@@ -2306,6 +2465,7 @@ var ChannelBase = class {
2306
2465
  /** Resolved proxy URL, available to subclasses for adapter-specific clients. */
2307
2466
  proxy;
2308
2467
  channelMemory;
2468
+ memoryIntentClassifier;
2309
2469
  groupHistory;
2310
2470
  loopController;
2311
2471
  instructedSessions = /* @__PURE__ */ new Set();
@@ -2319,16 +2479,29 @@ var ChannelBase = class {
2319
2479
  * so a cleared session can't be resurrected by an already-queued prompt.
2320
2480
  */
2321
2481
  sessionGenerations = /* @__PURE__ */ new Map();
2482
+ pendingClears = /* @__PURE__ */ new Map();
2322
2483
  /** Per-session active prompt tracking for dispatch modes. */
2323
2484
  activePrompts = /* @__PURE__ */ new Map();
2324
2485
  /** Per-session message buffer for collect mode. */
2325
2486
  collectBuffers = /* @__PURE__ */ new Map();
2487
+ preflightedEnvelopes = /* @__PURE__ */ new WeakSet();
2326
2488
  bridgeToolCallListener = /* @__PURE__ */ __name((event) => {
2327
2489
  this.dispatchToolCall(event);
2328
2490
  }, "bridgeToolCallListener");
2329
2491
  bridgeSessionDiedListener = /* @__PURE__ */ __name((event) => {
2330
2492
  this.onSessionDied(event.sessionId);
2331
2493
  }, "bridgeSessionDiedListener");
2494
+ bridgePermissionRequestListener = /* @__PURE__ */ __name((event) => {
2495
+ void this.dispatchPermissionRequest(event).catch((err) => {
2496
+ process.stderr.write(`[${this.name}] permission relay failed for request ${sanitizeLogText(event.requestId, 128)}: ${this.lifecycleError(err)}
2497
+ `);
2498
+ });
2499
+ }, "bridgePermissionRequestListener");
2500
+ bridgePermissionResolvedListener = /* @__PURE__ */ __name((event) => {
2501
+ this.dispatchPermissionResolved(event);
2502
+ }, "bridgePermissionResolvedListener");
2503
+ pendingPermissions = /* @__PURE__ */ new Map();
2504
+ pendingPermissionsByChat = /* @__PURE__ */ new Map();
2332
2505
  channelLoopToolHandler = {
2333
2506
  canHandle: /* @__PURE__ */ __name((sessionId) => this.router.getTarget(sessionId)?.channelName === this.name, "canHandle"),
2334
2507
  create: /* @__PURE__ */ __name((sessionId, input) => this.createLoopFromTool(sessionId, input), "create"),
@@ -2358,6 +2531,78 @@ var ChannelBase = class {
2358
2531
  }
2359
2532
  this.onToolCall(chatId, event);
2360
2533
  }
2534
+ async dispatchPermissionRequest(event) {
2535
+ const target = this.permissionTargetForEvent(event);
2536
+ if (!target) {
2537
+ try {
2538
+ await this.bridge.respondToPermission?.(event.requestId, {
2539
+ outcome: { outcome: "cancelled" }
2540
+ });
2541
+ } catch (respondErr) {
2542
+ process.stderr.write(`[${this.name}] permission cancellation failed for request ${sanitizeLogText(event.requestId, 128)}: ${this.lifecycleError(respondErr)}
2543
+ `);
2544
+ }
2545
+ return;
2546
+ }
2547
+ this.removePendingPermission(event.requestId);
2548
+ const pending = {
2549
+ requestId: event.requestId,
2550
+ sessionId: event.sessionId,
2551
+ target,
2552
+ request: event.request
2553
+ };
2554
+ this.pendingPermissions.set(event.requestId, pending);
2555
+ const chatKey = this.permissionChatKey(target);
2556
+ const requestIds = this.pendingPermissionsByChat.get(chatKey) ?? [];
2557
+ requestIds.push(event.requestId);
2558
+ this.pendingPermissionsByChat.set(chatKey, requestIds);
2559
+ try {
2560
+ const text = this.formatPermissionRequest(pending);
2561
+ if (target.threadId !== void 0 && this.supportsProactiveSend() && this.supportsProactiveTarget(target)) {
2562
+ await this.pushProactive(target, text);
2563
+ } else {
2564
+ await this.sendMessage(target.chatId, text);
2565
+ }
2566
+ } catch (err) {
2567
+ this.removePendingPermission(event.requestId);
2568
+ try {
2569
+ await this.bridge.respondToPermission?.(event.requestId, {
2570
+ outcome: { outcome: "cancelled" }
2571
+ });
2572
+ } catch (respondErr) {
2573
+ process.stderr.write(`[${this.name}] permission cancellation failed for request ${sanitizeLogText(event.requestId, 128)}: ${this.lifecycleError(respondErr)}
2574
+ `);
2575
+ }
2576
+ throw err;
2577
+ }
2578
+ }
2579
+ permissionTargetForEvent(event) {
2580
+ const routeTarget = this.router.getTarget(event.sessionId);
2581
+ if (!routeTarget || routeTarget.channelName !== this.name) {
2582
+ return void 0;
2583
+ }
2584
+ const active = this.activePrompts.get(event.sessionId);
2585
+ if (!active) {
2586
+ return routeTarget;
2587
+ }
2588
+ const target = {
2589
+ channelName: routeTarget.channelName,
2590
+ senderId: active.senderId ?? routeTarget.senderId,
2591
+ chatId: active.chatId
2592
+ };
2593
+ if (active.threadId !== void 0) {
2594
+ target.threadId = active.threadId;
2595
+ }
2596
+ if (active.isGroup !== void 0) {
2597
+ target.isGroup = active.isGroup;
2598
+ } else if (routeTarget.isGroup !== void 0) {
2599
+ target.isGroup = routeTarget.isGroup;
2600
+ }
2601
+ return target;
2602
+ }
2603
+ dispatchPermissionResolved(event) {
2604
+ this.removePendingPermission(event.requestId);
2605
+ }
2361
2606
  constructor(name, config, bridge, options) {
2362
2607
  this.name = name;
2363
2608
  this.config = config;
@@ -2366,9 +2611,11 @@ var ChannelBase = class {
2366
2611
  this.identity = Object.freeze(this.resolveIdentity(name, config));
2367
2612
  this.memoryScope = Object.freeze(this.resolveMemoryScope(name, config));
2368
2613
  this.channelMemory = options?.channelMemory;
2614
+ this.memoryIntentClassifier = options?.memoryIntentClassifier;
2369
2615
  this.groupHistory = new GroupHistoryStore(options?.groupHistoryPath ?? join4(getGlobalQwenDir(), "channels", `${encodeURIComponent(name)}-group-history.jsonl`));
2370
2616
  this.loopController = options?.loopController;
2371
2617
  this.groupGate = new GroupGate(config.groupPolicy, config.groups);
2618
+ this.dmGate = new DmGate(config.dmPolicy);
2372
2619
  const pairingStore = config.senderPolicy === "pairing" ? new PairingStore(name) : void 0;
2373
2620
  this.gate = new SenderGate(config.senderPolicy, config.allowedUsers, pairingStore);
2374
2621
  this.router = options?.router || new SessionRouter(bridge, config.cwd, config.sessionScope);
@@ -2488,6 +2735,7 @@ var ChannelBase = class {
2488
2735
  if (this.registerBridgeEvents) {
2489
2736
  this.detachBridgeEvents(this.bridge);
2490
2737
  }
2738
+ this.clearPendingPermissions();
2491
2739
  this.router.setBridge(bridge);
2492
2740
  this.bridge = bridge;
2493
2741
  if (this.loopController) {
@@ -2588,6 +2836,8 @@ ${promptText}`;
2588
2836
  done,
2589
2837
  resolve: doneResolve,
2590
2838
  chatId: job.target.chatId,
2839
+ threadId: job.target.threadId,
2840
+ isGroup: job.target.isGroup,
2591
2841
  messageId: job.id,
2592
2842
  senderId: job.target.senderId,
2593
2843
  senderName: job.createdBy,
@@ -2701,6 +2951,7 @@ ${promptText}`;
2701
2951
  const lost = buffer.length;
2702
2952
  const coalesced = buffer.map((b) => b.text).join("\n\n");
2703
2953
  const lastEnvelope = buffer[buffer.length - 1].envelope;
2954
+ this.notifyPromptBufferDrained(lastEnvelope.chatId, sessionId, buffer);
2704
2955
  const syntheticEnvelope = {
2705
2956
  ...lastEnvelope,
2706
2957
  text: coalesced,
@@ -2710,7 +2961,8 @@ ${promptText}`;
2710
2961
  imageBase64: void 0,
2711
2962
  imageMimeType: void 0
2712
2963
  };
2713
- this.handleInbound(syntheticEnvelope).catch((err) => {
2964
+ this.markPreflighted(syntheticEnvelope);
2965
+ this.processInbound(syntheticEnvelope).catch((err) => {
2714
2966
  process.stderr.write(`[${this.name}] dropped ${lost} buffered message(s) after loop ${job.id} for session ${sessionId} (last sender ${lastEnvelope.senderId}): ${err instanceof Error ? err.message : String(err)}
2715
2967
  `);
2716
2968
  });
@@ -2800,11 +3052,39 @@ ${promptText}`;
2800
3052
  }
2801
3053
  active.cancelled = true;
2802
3054
  this.stopActiveStreaming(active, sessionId, reason);
2803
- this.collectBuffers.delete(sessionId);
3055
+ this.dropCollectBuffer(sessionId);
2804
3056
  this.emitTaskCancellation(active, sessionId, reason);
2805
3057
  return true;
2806
3058
  });
2807
3059
  }
3060
+ dropCollectBuffer(sessionId) {
3061
+ const buffer = this.collectBuffers.get(sessionId);
3062
+ if (!buffer)
3063
+ return;
3064
+ this.collectBuffers.delete(sessionId);
3065
+ const chatId = buffer[0]?.envelope.chatId ?? "";
3066
+ const messageIds = this.collectBufferMessageIds(buffer);
3067
+ try {
3068
+ this.onPromptBufferDropped(chatId, sessionId, messageIds);
3069
+ } catch (err) {
3070
+ process.stderr.write(`[${this.name}] onPromptBufferDropped threw for session ${sessionId}: ${err instanceof Error ? err.message : err}
3071
+ `);
3072
+ }
3073
+ }
3074
+ notifyPromptBufferDrained(chatId, sessionId, buffer) {
3075
+ const messageIds = this.collectBufferMessageIds(buffer);
3076
+ if (messageIds.length === 0)
3077
+ return;
3078
+ try {
3079
+ this.onPromptBufferDrained(chatId, sessionId, messageIds);
3080
+ } catch (err) {
3081
+ process.stderr.write(`[${this.name}] onPromptBufferDrained threw for session ${sessionId}: ${err instanceof Error ? err.message : err}
3082
+ `);
3083
+ }
3084
+ }
3085
+ collectBufferMessageIds(buffer) {
3086
+ return buffer.map((entry) => entry.envelope.messageId).filter((id) => typeof id === "string" && id.length > 0);
3087
+ }
2808
3088
  logCancelSessionFailure(sessionId, err) {
2809
3089
  process.stderr.write(`[${sanitizeLogText(this.name, 64)}] cancelSession failed for session=${sanitizeLogText(sessionId, 64)}: ${this.lifecycleError(err)}
2810
3090
  `);
@@ -2834,14 +3114,19 @@ ${promptText}`;
2834
3114
  onSessionDied(sessionId) {
2835
3115
  this.router.removeSessionId(sessionId);
2836
3116
  this.instructedSessions.delete(sessionId);
3117
+ this.removePendingPermissionsForSession(sessionId);
2837
3118
  }
2838
3119
  attachBridgeEvents(bridge) {
2839
3120
  bridge.on("toolCall", this.bridgeToolCallListener);
2840
3121
  bridge.on("sessionDied", this.bridgeSessionDiedListener);
3122
+ bridge.on("permissionRequest", this.bridgePermissionRequestListener);
3123
+ bridge.on("permissionResolved", this.bridgePermissionResolvedListener);
2841
3124
  }
2842
3125
  detachBridgeEvents(bridge) {
2843
3126
  bridge.off("toolCall", this.bridgeToolCallListener);
2844
3127
  bridge.off("sessionDied", this.bridgeSessionDiedListener);
3128
+ bridge.off("permissionRequest", this.bridgePermissionRequestListener);
3129
+ bridge.off("permissionResolved", this.bridgePermissionResolvedListener);
2845
3130
  }
2846
3131
  /**
2847
3132
  * Called when a prompt actually begins processing (inside the session queue).
@@ -2850,6 +3135,12 @@ ${promptText}`;
2850
3135
  */
2851
3136
  onPromptStart(_chatId, _sessionId, _messageId) {
2852
3137
  }
3138
+ onPromptBuffered(_chatId, _sessionId, _messageId) {
3139
+ }
3140
+ onPromptBufferDrained(_chatId, _sessionId, _messageIds) {
3141
+ }
3142
+ onPromptBufferDropped(_chatId, _sessionId, _messageIds) {
3143
+ }
2853
3144
  /**
2854
3145
  * Called when a prompt finishes (response sent or cancelled).
2855
3146
  * Override to hide the working indicator.
@@ -2900,6 +3191,182 @@ ${promptText}`;
2900
3191
  return true;
2901
3192
  });
2902
3193
  }
3194
+ permissionChatKey(target) {
3195
+ return `${target.chatId}\0${target.threadId ?? ""}`;
3196
+ }
3197
+ pendingPermissionIdsForChatKey(chatKey) {
3198
+ const requestIds = this.pendingPermissionsByChat.get(chatKey);
3199
+ if (!requestIds) {
3200
+ return [];
3201
+ }
3202
+ const live = requestIds.filter((id) => this.pendingPermissions.has(id));
3203
+ if (live.length === 0) {
3204
+ this.pendingPermissionsByChat.delete(chatKey);
3205
+ } else if (live.length !== requestIds.length) {
3206
+ this.pendingPermissionsByChat.set(chatKey, live);
3207
+ }
3208
+ return live;
3209
+ }
3210
+ removePendingPermission(requestId) {
3211
+ const pending = this.pendingPermissions.get(requestId);
3212
+ if (!pending) {
3213
+ return;
3214
+ }
3215
+ this.pendingPermissions.delete(requestId);
3216
+ const chatKey = this.permissionChatKey(pending.target);
3217
+ const requestIds = this.pendingPermissionsByChat.get(chatKey);
3218
+ if (!requestIds) {
3219
+ return;
3220
+ }
3221
+ const remaining = requestIds.filter((id) => id !== requestId);
3222
+ if (remaining.length === 0) {
3223
+ this.pendingPermissionsByChat.delete(chatKey);
3224
+ } else {
3225
+ this.pendingPermissionsByChat.set(chatKey, remaining);
3226
+ }
3227
+ }
3228
+ removePendingPermissionsForSession(sessionId) {
3229
+ const requestIds = Array.from(this.pendingPermissions).filter(([, pending]) => pending.sessionId === sessionId).map(([requestId]) => requestId);
3230
+ for (const requestId of requestIds) {
3231
+ this.removePendingPermission(requestId);
3232
+ }
3233
+ }
3234
+ clearPendingPermissions() {
3235
+ this.pendingPermissions.clear();
3236
+ this.pendingPermissionsByChat.clear();
3237
+ }
3238
+ pendingPermissionForEnvelope(envelope, args) {
3239
+ const trimmed = args.trim();
3240
+ if (trimmed) {
3241
+ const explicit = this.pendingPermissions.get(trimmed);
3242
+ if (explicit && this.canEnvelopeAnswerPendingPermission(envelope, explicit)) {
3243
+ return { kind: "found", pending: explicit };
3244
+ }
3245
+ return { kind: "none", explicit: true };
3246
+ }
3247
+ const requestIds = this.pendingPermissionIdsForChatKey(this.permissionChatKey(envelope));
3248
+ if (requestIds.length === 0) {
3249
+ return { kind: "none", explicit: false };
3250
+ }
3251
+ const matching = requestIds.map((id) => this.pendingPermissions.get(id)).filter((pending) => pending !== void 0 && this.canEnvelopeAnswerPendingPermission(envelope, pending));
3252
+ if (matching.length === 0) {
3253
+ return { kind: "none", explicit: false };
3254
+ }
3255
+ if (matching.length > 1) {
3256
+ return {
3257
+ kind: "ambiguous",
3258
+ requestIds: matching.map((pending) => pending.requestId)
3259
+ };
3260
+ }
3261
+ return { kind: "found", pending: matching[0] };
3262
+ }
3263
+ canEnvelopeAnswerPendingPermission(envelope, pending) {
3264
+ return pending.target.chatId === envelope.chatId && pending.target.threadId === envelope.threadId && (this.isSharedSessionTarget(pending.target) || pending.target.senderId === envelope.senderId);
3265
+ }
3266
+ formatPermissionRequest(pending) {
3267
+ const { toolCall } = pending.request;
3268
+ const title = sanitizeQuotedText(toolCall.title || "Tool use", 160);
3269
+ const alwaysOption = this.approvalAlwaysOption(pending);
3270
+ const replies = [
3271
+ "/approve allow once",
3272
+ ...alwaysOption ? [`/approve-always ${alwaysOption.label}`] : [],
3273
+ "/deny deny"
3274
+ ];
3275
+ const lines = [
3276
+ "Permission required to run a tool",
3277
+ "",
3278
+ "Command:",
3279
+ title,
3280
+ "",
3281
+ "Reply with:",
3282
+ ...replies
3283
+ ];
3284
+ return lines.join("\n");
3285
+ }
3286
+ approvalOptionId(pending) {
3287
+ const options = pending.request.options;
3288
+ return options.find((option) => option.kind === "allow_once")?.optionId ?? options.find((option) => option.optionId === "proceed_once" && option.kind === void 0)?.optionId;
3289
+ }
3290
+ approvalAlwaysOption(pending) {
3291
+ const options = pending.request.options.filter((option2) => option2.kind === "allow_always");
3292
+ const option = this.findScopedAlwaysOption(options, "project") ?? this.findScopedAlwaysOption(options, "user") ?? options[0];
3293
+ if (!option) {
3294
+ return void 0;
3295
+ }
3296
+ return {
3297
+ optionId: option.optionId,
3298
+ label: this.approvalAlwaysLabel(option)
3299
+ };
3300
+ }
3301
+ findScopedAlwaysOption(options, scope) {
3302
+ return options.find((option) => option.optionId === `proceed_always_${scope}`);
3303
+ }
3304
+ approvalAlwaysLabel(option) {
3305
+ if (option.optionId === "proceed_always_project") {
3306
+ return "always allow for this project";
3307
+ }
3308
+ if (option.optionId === "proceed_always_user") {
3309
+ return "always allow for this user";
3310
+ }
3311
+ return "always allow";
3312
+ }
3313
+ denialResponse(pending) {
3314
+ const option = pending.request.options.find((candidate) => candidate.kind === "reject_once") ?? pending.request.options.find((candidate) => candidate.optionId === "cancel" && candidate.kind === void 0);
3315
+ if (option) {
3316
+ return { outcome: { outcome: "selected", optionId: option.optionId } };
3317
+ }
3318
+ return { outcome: { outcome: "cancelled" } };
3319
+ }
3320
+ async handlePermissionResponseCommand(envelope, args, decision) {
3321
+ if (!this.isAuthorizedForSharedSession(envelope)) {
3322
+ await this.sendMessage(envelope.chatId, "Only authorized members can answer permission requests in this shared session.");
3323
+ return true;
3324
+ }
3325
+ const lookup = this.pendingPermissionForEnvelope(envelope, args);
3326
+ if (lookup.kind === "ambiguous") {
3327
+ const requestList = lookup.requestIds.slice(0, 6).map((id) => {
3328
+ const pending2 = this.pendingPermissions.get(id);
3329
+ const title = pending2 ? `: ${sanitizeQuotedText(pending2.request.toolCall.title || "Tool use", 160)}` : "";
3330
+ return `- ${sanitizeQuotedText(id, 128)}${title}`;
3331
+ }).join("\n");
3332
+ await this.sendMessage(envelope.chatId, `Multiple permission requests are pending for this chat. Reply with /${decision} <request-id>.
3333
+ ${requestList}`);
3334
+ return true;
3335
+ }
3336
+ if (lookup.kind === "none") {
3337
+ await this.sendMessage(envelope.chatId, lookup.explicit ? "No pending permission request with that id for this chat." : "No pending permission request for this chat.");
3338
+ return true;
3339
+ }
3340
+ if (!this.bridge.respondToPermission) {
3341
+ await this.sendMessage(envelope.chatId, "Permission relay is not available for this session.");
3342
+ return true;
3343
+ }
3344
+ const { pending } = lookup;
3345
+ const response = (() => {
3346
+ if (decision === "deny") {
3347
+ return this.denialResponse(pending);
3348
+ }
3349
+ const optionId = decision === "approve" ? this.approvalOptionId(pending) : this.approvalAlwaysOption(pending)?.optionId;
3350
+ return optionId ? { outcome: { outcome: "selected", optionId } } : void 0;
3351
+ })();
3352
+ if (!response) {
3353
+ await this.sendMessage(envelope.chatId, decision === "approve-always" ? "This permission request has no always-allow option." : "This permission request has no approvable option.");
3354
+ return true;
3355
+ }
3356
+ let accepted;
3357
+ try {
3358
+ accepted = await this.bridge.respondToPermission(pending.requestId, response);
3359
+ } catch (err) {
3360
+ this.removePendingPermission(pending.requestId);
3361
+ process.stderr.write(`[${this.name}] permission response failed for request ${sanitizeLogText(pending.requestId, 128)}: ${this.lifecycleError(err)}
3362
+ `);
3363
+ await this.sendMessage(envelope.chatId, "Failed to answer the permission request.");
3364
+ return true;
3365
+ }
3366
+ this.removePendingPermission(pending.requestId);
3367
+ await this.sendMessage(envelope.chatId, accepted ? decision === "approve" ? "Permission approved." : decision === "approve-always" ? "Permission approved always." : "Permission denied." : "Permission request is no longer pending.");
3368
+ return true;
3369
+ }
2903
3370
  /** Register shared slash commands. Called from constructor. */
2904
3371
  registerSharedCommands() {
2905
3372
  const doClear = /* @__PURE__ */ __name(async (envelope) => {
@@ -2913,8 +3380,9 @@ ${promptText}`;
2913
3380
  `);
2914
3381
  }
2915
3382
  this.sessionGenerations.set(id, (this.sessionGenerations.get(id) ?? 0) + 1);
3383
+ this.removePendingPermissionsForSession(id);
2916
3384
  const active = this.activePrompts.get(id);
2917
- this.collectBuffers.delete(id);
3385
+ this.dropCollectBuffer(id);
2918
3386
  if (active) {
2919
3387
  const settled = await this.cancelAndAwaitActive(active, id);
2920
3388
  if (!settled) {
@@ -2966,77 +3434,23 @@ ${promptText}`;
2966
3434
  this.registerCommand("clear", clearHandler);
2967
3435
  this.registerCommand("reset", clearHandler);
2968
3436
  this.registerCommand("new", clearHandler);
2969
- this.registerCommand("who", async (envelope) => {
2970
- if (!this.isAuthorizedForSharedSession(envelope)) {
2971
- await this.sendMessage(envelope.chatId, "Only authorized members can view this shared session.");
2972
- return true;
2973
- }
2974
- const active = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
2975
- const scopeNote = this.config.sessionScope === "single" ? " (shared channel-wide)" : this.isSharedSession(envelope) ? envelope.isGroup ? " (shared by this group)" : "" : envelope.isGroup ? " (private to you)" : "";
2976
- await this.sendMessage(envelope.chatId, [
2977
- `Channel: ${this.name}`,
2978
- // Identity/memory lines only for channels that opted in — keep
2979
- // unconfigured channels' output unchanged.
2980
- ...this.shouldPrependChannelBoundaryPrompt() ? [
2981
- `Identity: ${sanitizeQuotedText(this.identity.displayName, 128)}`,
2982
- `Memory: ${sanitizeQuotedText(this.memoryScope.namespace, 128)}`
2983
- ] : [],
2984
- // Only the basename — don't leak the absolute cwd to group members.
2985
- `Workspace: ${basename(this.config.cwd)}`,
2986
- `Session: ${active ? "active" : "none"}${scopeNote}`
2987
- ].join("\n"));
2988
- return true;
2989
- });
3437
+ this.registerCommand("approve", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "approve"));
3438
+ this.registerCommand("approve-always", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "approve-always"));
3439
+ this.registerCommand("deny", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "deny"));
2990
3440
  this.registerCommand("remember-channel", async (envelope, args) => {
2991
- if (!await this.ensureChannelMemoryAuthorized(envelope)) {
2992
- return true;
2993
- }
2994
- if (envelope.isGroup) {
2995
- await this.sendMessage(envelope.chatId, "Channel memory cannot be changed in group chats.");
2996
- return true;
2997
- }
2998
- if (args.trim() === "") {
3441
+ const text = args.trim();
3442
+ if (text === "") {
2999
3443
  await this.sendMessage(envelope.chatId, "Usage: /remember-channel <text>");
3000
3444
  return true;
3001
3445
  }
3002
- const channelMemory = await this.getChannelMemory(envelope);
3003
- if (!channelMemory) {
3004
- return true;
3005
- }
3006
- try {
3007
- await channelMemory.appendChannelMemory(this.channelMemoryTarget(envelope), args.trim());
3008
- } catch (error) {
3009
- const message = this.channelMemoryErrorMessage(error);
3010
- this.logChannelMemoryError("save", envelope, message);
3011
- await this.sendMessage(envelope.chatId, `Failed to save channel memory: ${this.channelMemoryUserErrorMessage()}`);
3012
- return true;
3013
- }
3014
- this.invalidateSessionContext(envelope);
3015
- await this.sendMessage(envelope.chatId, "Channel memory updated.");
3446
+ await this.handleChannelMemoryIntent(envelope, {
3447
+ kind: "remember",
3448
+ text
3449
+ });
3016
3450
  return true;
3017
3451
  });
3018
3452
  this.registerCommand("channel-memory", async (envelope) => {
3019
- if (!await this.ensureChannelMemoryAuthorized(envelope)) {
3020
- return true;
3021
- }
3022
- if (envelope.isGroup) {
3023
- await this.sendMessage(envelope.chatId, "Channel memory cannot be shown in group chats.");
3024
- return true;
3025
- }
3026
- const channelMemory = await this.getChannelMemory(envelope);
3027
- if (!channelMemory) {
3028
- return true;
3029
- }
3030
- let text;
3031
- try {
3032
- text = (await channelMemory.readChannelMemory(this.channelMemoryTarget(envelope))).trim();
3033
- } catch (error) {
3034
- const message = this.channelMemoryErrorMessage(error);
3035
- this.logChannelMemoryError("read", envelope, message);
3036
- await this.sendMessage(envelope.chatId, `Failed to read channel memory: ${this.channelMemoryUserErrorMessage()}`);
3037
- return true;
3038
- }
3039
- await this.sendMessage(envelope.chatId, text === "" ? "No channel memory saved." : sanitizePromptText(text));
3453
+ await this.handleChannelMemoryIntent(envelope, { kind: "list" });
3040
3454
  return true;
3041
3455
  });
3042
3456
  this.registerCommand("forget-channel", async (envelope, args) => {
@@ -3051,21 +3465,28 @@ ${promptText}`;
3051
3465
  await this.sendMessage(envelope.chatId, 'This clears channel memory for this chat. Re-send with "confirm" (e.g. /forget-channel confirm) to proceed.');
3052
3466
  return true;
3053
3467
  }
3054
- const channelMemory = await this.getChannelMemory(envelope);
3055
- if (!channelMemory) {
3056
- return true;
3057
- }
3058
- let result;
3059
- try {
3060
- result = await channelMemory.clearChannelMemory(this.channelMemoryTarget(envelope));
3061
- } catch (error) {
3062
- const message = this.channelMemoryErrorMessage(error);
3063
- this.logChannelMemoryError("clear", envelope, message);
3064
- await this.sendMessage(envelope.chatId, `Failed to clear channel memory: ${this.channelMemoryUserErrorMessage()}`);
3468
+ await this.handleChannelMemoryIntent(envelope, { kind: "clear_confirm" }, { skipPendingClear: true });
3469
+ return true;
3470
+ });
3471
+ this.registerCommand("who", async (envelope) => {
3472
+ if (!this.isAuthorizedForSharedSession(envelope)) {
3473
+ await this.sendMessage(envelope.chatId, "Only authorized members can view this shared session.");
3065
3474
  return true;
3066
3475
  }
3067
- this.invalidateSessionContext(envelope);
3068
- await this.sendMessage(envelope.chatId, result.changed ? "Channel memory cleared." : "No channel memory saved.");
3476
+ const active = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
3477
+ const scopeNote = this.config.sessionScope === "single" ? " (shared channel-wide)" : this.isSharedSession(envelope) ? envelope.isGroup ? " (shared by this group)" : "" : envelope.isGroup ? " (private to you)" : "";
3478
+ await this.sendMessage(envelope.chatId, [
3479
+ `Channel: ${this.name}`,
3480
+ // Identity/memory lines only for channels that opted in — keep
3481
+ // unconfigured channels' output unchanged.
3482
+ ...this.shouldPrependChannelBoundaryPrompt() ? [
3483
+ `Identity: ${sanitizeQuotedText(this.identity.displayName, 128)}`,
3484
+ `Memory: ${sanitizeQuotedText(this.memoryScope.namespace, 128)}`
3485
+ ] : [],
3486
+ // Only the basename — don't leak the absolute cwd to group members.
3487
+ `Workspace: ${basename(this.config.cwd)}`,
3488
+ `Session: ${active ? "active" : "none"}${scopeNote}`
3489
+ ].join("\n"));
3069
3490
  return true;
3070
3491
  });
3071
3492
  this.registerCommand("help", async (envelope) => {
@@ -3075,20 +3496,23 @@ ${promptText}`;
3075
3496
  this.isSharedSession(envelope) ? "/clear confirm \u2014 Clear the shared session (aliases: /reset, /new)" : "/clear \u2014 Clear your session (aliases: /reset, /new)",
3076
3497
  "/who \u2014 Show current session & workspace",
3077
3498
  "/status \u2014 Show session info",
3078
- "/remember-channel <text> \u2014 Save memory for this chat",
3079
- "/channel-memory \u2014 Show memory for this chat",
3080
- "/forget-channel confirm \u2014 Clear memory for this chat"
3499
+ "/approve [request-id] \u2014 Approve a pending permission request",
3500
+ "/approve-always [request-id] \u2014 Always approve a pending permission request",
3501
+ "/deny [request-id] \u2014 Deny a pending permission request"
3081
3502
  ];
3082
3503
  const sharedCmds = /* @__PURE__ */ new Set([
3083
3504
  "help",
3084
3505
  "clear",
3085
3506
  "reset",
3086
3507
  "new",
3087
- "who",
3088
- "status",
3508
+ "approve",
3509
+ "approve-always",
3510
+ "deny",
3089
3511
  "remember-channel",
3090
3512
  "channel-memory",
3091
- "forget-channel"
3513
+ "forget-channel",
3514
+ "who",
3515
+ "status"
3092
3516
  ]);
3093
3517
  const platformCmds = [...this.commands.keys()].filter((c) => !sharedCmds.has(c));
3094
3518
  if (platformCmds.length > 0) {
@@ -3433,7 +3857,7 @@ ${promptText}`;
3433
3857
  isMentioned: true,
3434
3858
  isReplyToBot: true
3435
3859
  };
3436
- return this.groupGate.check(envelope).allowed && this.gate.isAllowed(normalizedTarget.senderId) && this.isAuthorizedForSharedSession(envelope);
3860
+ return this.groupGate.check(envelope).allowed && this.dmGate.check(envelope).allowed && this.gate.isAllowed(normalizedTarget.senderId) && this.isAuthorizedForSharedSession(envelope);
3437
3861
  }
3438
3862
  /** Check if a message text matches a registered local command. */
3439
3863
  isLocalCommand(text) {
@@ -3486,6 +3910,118 @@ ${promptText}`;
3486
3910
  }
3487
3911
  return this.channelMemory;
3488
3912
  }
3913
+ async handleChannelMemoryIntent(envelope, intent, options = {}) {
3914
+ if (!await this.ensureChannelMemoryAuthorized(envelope)) {
3915
+ return;
3916
+ }
3917
+ if (envelope.isGroup && intent.kind !== "list") {
3918
+ await this.sendMessage(envelope.chatId, "Channel memory cannot be changed in group chats.");
3919
+ return;
3920
+ }
3921
+ if (intent.kind === "clear_request") {
3922
+ this.setPendingClear(this.clearPendingKey(envelope));
3923
+ await this.sendMessage(envelope.chatId, 'This clears channel memory for this chat. Say "\u786E\u8BA4\u6E05\u7A7A\u8BB0\u5FC6" or "confirm clear memory" to proceed.');
3924
+ return;
3925
+ }
3926
+ const channelMemory = await this.getChannelMemory(envelope);
3927
+ if (!channelMemory) {
3928
+ return;
3929
+ }
3930
+ if (intent.kind === "remember") {
3931
+ try {
3932
+ await channelMemory.appendChannelMemory(this.channelMemoryTarget(envelope), intent.text);
3933
+ } catch (error) {
3934
+ const message = this.channelMemoryErrorMessage(error);
3935
+ this.logChannelMemoryError("save", envelope, message);
3936
+ await this.sendMessage(envelope.chatId, `Failed to save channel memory: ${this.channelMemoryUserErrorMessage()}`);
3937
+ return;
3938
+ }
3939
+ this.invalidateSessionContext(envelope);
3940
+ await this.sendMessage(envelope.chatId, "Channel memory updated.");
3941
+ return;
3942
+ }
3943
+ if (intent.kind === "list") {
3944
+ let text;
3945
+ try {
3946
+ text = (await channelMemory.readChannelMemory(this.channelMemoryTarget(envelope))).trim();
3947
+ } catch (error) {
3948
+ const message = this.channelMemoryErrorMessage(error);
3949
+ this.logChannelMemoryError("read", envelope, message);
3950
+ await this.sendMessage(envelope.chatId, `Failed to read channel memory: ${this.channelMemoryUserErrorMessage()}`);
3951
+ return;
3952
+ }
3953
+ await this.sendMessage(envelope.chatId, text === "" ? "No channel memory saved." : sanitizePromptText(text));
3954
+ return;
3955
+ }
3956
+ if (intent.kind === "clear_confirm") {
3957
+ if (!options.skipPendingClear) {
3958
+ const pendingKey = this.clearPendingKey(envelope);
3959
+ const expiresAt = this.pendingClears.get(pendingKey);
3960
+ this.pendingClears.delete(pendingKey);
3961
+ if (expiresAt === void 0 || expiresAt < Date.now()) {
3962
+ await this.sendMessage(envelope.chatId, 'No pending clear request. Say "\u6E05\u7A7A\u8BB0\u5FC6" first.');
3963
+ return;
3964
+ }
3965
+ }
3966
+ let result;
3967
+ try {
3968
+ result = await channelMemory.clearChannelMemory(this.channelMemoryTarget(envelope));
3969
+ } catch (error) {
3970
+ const message = this.channelMemoryErrorMessage(error);
3971
+ this.logChannelMemoryError("clear", envelope, message);
3972
+ await this.sendMessage(envelope.chatId, `Failed to clear channel memory: ${this.channelMemoryUserErrorMessage()}`);
3973
+ return;
3974
+ }
3975
+ this.invalidateSessionContext(envelope);
3976
+ await this.sendMessage(envelope.chatId, result.changed ? "Channel memory cleared." : "No channel memory saved.");
3977
+ return;
3978
+ }
3979
+ const unhandled = intent;
3980
+ throw new Error(`Unhandled channel memory intent: ${JSON.stringify(unhandled)}`);
3981
+ }
3982
+ shouldClassifyChannelMemoryIntent(text) {
3983
+ const normalized = text.replace(PROMPT_UNSAFE_INVISIBLES, "").trim();
3984
+ return this.channelMemory !== void 0 && this.memoryIntentClassifier !== void 0 && !normalized.startsWith("/") && CHANNEL_MEMORY_CLASSIFIER_TRIGGER_RE.test(normalized);
3985
+ }
3986
+ clearPendingKey(envelope) {
3987
+ return `${this.name}:${envelope.chatId}:${envelope.threadId ?? ""}:${envelope.senderId ?? ""}`;
3988
+ }
3989
+ setPendingClear(key) {
3990
+ const now = Date.now();
3991
+ for (const [pendingKey, expiresAt] of this.pendingClears) {
3992
+ if (expiresAt < now) {
3993
+ this.pendingClears.delete(pendingKey);
3994
+ }
3995
+ }
3996
+ this.pendingClears.set(key, now + 6e4);
3997
+ }
3998
+ async classifyChannelMemoryIntent(text) {
3999
+ if (!this.memoryIntentClassifier) {
4000
+ return null;
4001
+ }
4002
+ let classified;
4003
+ try {
4004
+ classified = await this.memoryIntentClassifier.classifyChannelMemoryIntent(text);
4005
+ } catch (error) {
4006
+ process.stderr.write(`[${this.name}] channel memory intent classifier failed: ${sanitizeLogText(this.channelMemoryErrorMessage(error), 200)}
4007
+ `);
4008
+ return null;
4009
+ }
4010
+ if (classified.confidence < CHANNEL_MEMORY_CLASSIFIER_MIN_CONFIDENCE) {
4011
+ return null;
4012
+ }
4013
+ if (classified.intent === "remember") {
4014
+ const memory = classified.memory?.trim();
4015
+ return memory ? { kind: "remember", text: memory } : null;
4016
+ }
4017
+ if (classified.intent === "list") {
4018
+ return { kind: "list" };
4019
+ }
4020
+ if (classified.intent === "clear_all") {
4021
+ return { kind: "clear_request" };
4022
+ }
4023
+ return null;
4024
+ }
3489
4025
  channelMemoryErrorMessage(error) {
3490
4026
  return error instanceof Error ? error.message : String(error);
3491
4027
  }
@@ -3746,19 +4282,58 @@ ${formatted.join("\n")}
3746
4282
  ${CURRENT_MESSAGE_MARKER}
3747
4283
  ${promptText}`;
3748
4284
  }
3749
- async handleInbound(envelope) {
4285
+ preflightInbound(envelope) {
3750
4286
  const groupResult = this.groupGate.check(envelope);
3751
4287
  if (!groupResult.allowed) {
3752
4288
  if (groupResult.reason === "mention_required") {
3753
4289
  this.recordPendingGroupHistory(envelope);
3754
4290
  }
3755
- return;
4291
+ return false;
4292
+ }
4293
+ const dmResult = this.dmGate.check(envelope);
4294
+ if (!dmResult.allowed) {
4295
+ return false;
3756
4296
  }
3757
4297
  const result = this.gate.check(envelope.senderId, envelope.senderName);
3758
4298
  if (!result.allowed) {
3759
4299
  if (result.pairingCode !== void 0) {
3760
- await this.onPairingRequired(envelope.chatId, result.pairingCode);
4300
+ return this.onPairingRequired(envelope.chatId, result.pairingCode).then(() => false).catch((err) => {
4301
+ process.stderr.write(`[Channel:${this.name}] pairing notification failed: ${sanitizeLogText(err instanceof Error ? err.message : String(err), 200)}
4302
+ `);
4303
+ return false;
4304
+ });
3761
4305
  }
4306
+ return false;
4307
+ }
4308
+ this.markPreflighted(envelope);
4309
+ return true;
4310
+ }
4311
+ async handleInbound(envelope) {
4312
+ const preflight = this.preflightInbound(envelope);
4313
+ if (!(isPromiseLike(preflight) ? await preflight : preflight))
4314
+ return;
4315
+ await this.processInbound(envelope);
4316
+ }
4317
+ markPreflighted(envelope) {
4318
+ this.preflightedEnvelopes.add(envelope);
4319
+ }
4320
+ /**
4321
+ * Process an inbound message after preflight gates have passed.
4322
+ *
4323
+ * This method does not run group gating, sender allowlisting, or pairing
4324
+ * checks. Callers must run preflightInbound() first unless the envelope was
4325
+ * already preflighted, such as during collect-buffer drain.
4326
+ */
4327
+ async processInbound(envelope) {
4328
+ if (!this.preflightedEnvelopes.delete(envelope)) {
4329
+ throw new Error("processInbound called without a successful preflightInbound check.");
4330
+ }
4331
+ let memoryIntent = parseChannelMemoryIntent(envelope.text);
4332
+ if (!memoryIntent && this.shouldClassifyChannelMemoryIntent(envelope.text)) {
4333
+ memoryIntent = await this.classifyChannelMemoryIntent(envelope.text);
4334
+ }
4335
+ if (memoryIntent) {
4336
+ await this.handleChannelMemoryIntent(envelope, memoryIntent);
3762
4337
  return;
3763
4338
  }
3764
4339
  const parsed = this.parseCommand(envelope.text);
@@ -3792,14 +4367,14 @@ ${promptText}`;
3792
4367
  const bridgeShellCommand = this.bridge.shellCommand;
3793
4368
  if (cmd && bridgeShellCommand) {
3794
4369
  try {
3795
- const result2 = await bridgeShellCommand(sessionId, cmd);
3796
- const longestRun = Math.max(0, ...Array.from((result2.output || "").matchAll(/`+/g), (m) => m[0].length));
4370
+ const result = await bridgeShellCommand(sessionId, cmd);
4371
+ const longestRun = Math.max(0, ...Array.from((result.output || "").matchAll(/`+/g), (m) => m[0].length));
3797
4372
  const fence = "`".repeat(Math.max(3, longestRun + 1));
3798
- const output = result2.output ? `${fence}
3799
- ${result2.output}
4373
+ const output = result.output ? `${fence}
4374
+ ${result.output}
3800
4375
  ${fence}` : "(no output)";
3801
- const exitLine = result2.exitCode !== null && result2.exitCode !== 0 ? `
3802
- Exit code: ${result2.exitCode}` : "";
4376
+ const exitLine = result.exitCode !== null && result.exitCode !== 0 ? `
4377
+ Exit code: ${result.exitCode}` : "";
3803
4378
  await this.sendMessage(envelope.chatId, `$ ${cmd}
3804
4379
  ${output}${exitLine}`);
3805
4380
  } catch (error) {
@@ -3852,6 +4427,12 @@ ${promptText}`;
3852
4427
  this.collectBuffers.set(sessionId, buffer);
3853
4428
  }
3854
4429
  buffer.push({ text: promptText, envelope });
4430
+ try {
4431
+ this.onPromptBuffered(envelope.chatId, sessionId, envelope.messageId);
4432
+ } catch (err) {
4433
+ process.stderr.write(`[${this.name}] onPromptBuffered threw for session ${sessionId}: ${err instanceof Error ? err.message : err}
4434
+ `);
4435
+ }
3855
4436
  return;
3856
4437
  }
3857
4438
  case "steer": {
@@ -3951,6 +4532,8 @@ ${promptToSend}`;
3951
4532
  done,
3952
4533
  resolve: doneResolve,
3953
4534
  chatId: envelope.chatId,
4535
+ threadId: envelope.threadId,
4536
+ isGroup: envelope.isGroup,
3954
4537
  messageId: envelope.messageId,
3955
4538
  senderId: envelope.senderId,
3956
4539
  senderName: envelope.senderName
@@ -4064,6 +4647,7 @@ ${promptToSend}`;
4064
4647
  const lost = buffer.length;
4065
4648
  const coalesced = buffer.map((b) => b.text).join("\n\n");
4066
4649
  const lastEnvelope = buffer[buffer.length - 1].envelope;
4650
+ this.notifyPromptBufferDrained(lastEnvelope.chatId, sessionId, buffer);
4067
4651
  const syntheticEnvelope = {
4068
4652
  ...lastEnvelope,
4069
4653
  text: coalesced,
@@ -4075,7 +4659,8 @@ ${promptToSend}`;
4075
4659
  imageBase64: void 0,
4076
4660
  imageMimeType: void 0
4077
4661
  };
4078
- this.handleInbound(syntheticEnvelope).catch((err) => {
4662
+ this.markPreflighted(syntheticEnvelope);
4663
+ this.processInbound(syntheticEnvelope).catch((err) => {
4079
4664
  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)}
4080
4665
  `);
4081
4666
  });
@@ -4101,6 +4686,10 @@ function truncateGroupHistoryField(value) {
4101
4686
  return value.slice(0, GROUP_HISTORY_ENTRY_METADATA_LIMIT);
4102
4687
  }
4103
4688
  __name(truncateGroupHistoryField, "truncateGroupHistoryField");
4689
+ function isPromiseLike(value) {
4690
+ return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
4691
+ }
4692
+ __name(isPromiseLike, "isPromiseLike");
4104
4693
  function truncateLoopLabel(prompt) {
4105
4694
  const chars = Array.from(prompt);
4106
4695
  return chars.length > 60 ? `${chars.slice(0, 57).join("")}...` : prompt;