@qwen-code/qwen-code 0.19.6 → 0.19.7-nightly.20260708.394c1a289
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.
- package/bundled/qc-helper/docs/configuration/model-providers.md +5 -1
- package/bundled/qc-helper/docs/configuration/settings.md +33 -1
- package/bundled/qc-helper/docs/features/channels/_meta.ts +1 -0
- package/bundled/qc-helper/docs/features/channels/overview.md +41 -31
- package/bundled/qc-helper/docs/features/channels/wecom.md +111 -0
- package/bundled/qc-helper/docs/features/code-review.md +16 -6
- package/bundled/qc-helper/docs/features/commands.md +1 -1
- package/bundled/qc-helper/docs/features/hooks.md +6 -0
- package/bundled/qc-helper/docs/features/skills.md +5 -3
- package/bundled/qc-helper/docs/integration-github-action.md +1 -1
- package/bundled/review/DESIGN.md +50 -18
- package/bundled/review/SKILL.md +102 -12
- package/chunks/{MaxSizedBox-SK7CUWZQ.js → MaxSizedBox-XS37LWTD.js} +31 -29
- package/chunks/{StandaloneSessionPicker-G44HGSAP.js → StandaloneSessionPicker-LPALJTKN.js} +44 -41
- package/chunks/{acpAgent-PKCTYWJV.js → acpAgent-4VE3FGJX.js} +769 -366
- package/chunks/{agent-IYMHUXWB.js → agent-CCHERW6L.js} +24 -24
- package/chunks/{agent-headless-L2DAW6I5.js → agent-headless-55N6UBUU.js} +24 -24
- package/chunks/{anthropicContentGenerator-H5ZOEENQ.js → anthropicContentGenerator-AKYPSZIV.js} +7 -7
- package/chunks/{artifact-tool-SLYPWG3C.js → artifact-tool-D233YJFH.js} +27 -8
- package/chunks/{askUserQuestion-OKPTECON.js → askUserQuestion-MKL5ZTMC.js} +2 -2
- package/chunks/{bridge-DF3IJQ5V.js → bridge-U6HXOHE7.js} +34 -32
- package/chunks/{channel-worker-supervisor-JUYQ5Z4R.js → channel-worker-supervisor-CRCVYQIE.js} +1 -1
- package/chunks/{chunk-AVQDCKNF.js → chunk-22MNNNXE.js} +4 -0
- package/chunks/chunk-2DYY5XUZ.js +8223 -0
- package/chunks/chunk-2FHWCKWA.js +262 -0
- package/chunks/{chunk-6BEOH4KK.js → chunk-2NPS2UDH.js} +767 -122
- package/chunks/{chunk-N2IJLN2L.js → chunk-2SWMCXVR.js} +13 -5
- package/chunks/{chunk-OBVBTAX5.js → chunk-2YELHTUE.js} +2 -2
- package/chunks/chunk-332PWN27.js +19 -0
- package/chunks/{chunk-CLQPBI4H.js → chunk-3B26UVCX.js} +132 -26
- package/chunks/{chunk-HCFSKO6K.js → chunk-3QDZDAJG.js} +1 -1
- package/chunks/{chunk-G7OQ3RFJ.js → chunk-42XGFRJS.js} +3 -3
- package/chunks/{chunk-SL2P3JUA.js → chunk-43W4YSO6.js} +1 -12
- package/chunks/{chunk-JA5KWZTJ.js → chunk-44NROQYV.js} +5 -3
- package/chunks/{chunk-BOAWGUY6.js → chunk-4AFOWB47.js} +121 -49
- package/chunks/{chunk-MOUXUCXQ.js → chunk-4KJ2BPIT.js} +1 -1
- package/chunks/{chunk-YTWX5TT2.js → chunk-5FL2K3ZB.js} +1 -1
- package/chunks/{chunk-UNXGEDX2.js → chunk-5OSSOBZC.js} +133 -25
- package/chunks/{chunk-JN2VJ33O.js → chunk-7I4MGFBN.js} +966 -121
- package/chunks/{chunk-T42ACHFI.js → chunk-7LTB54MK.js} +1 -1
- package/chunks/chunk-A2X3KLC3.js +3807 -0
- package/chunks/{chunk-EWALXUAD.js → chunk-AGHU5VRK.js} +8 -0
- package/chunks/{chunk-GLPV75ES.js → chunk-AHNJGLFV.js} +2 -2
- package/chunks/{chunk-IUSKVKH5.js → chunk-AXTPFMRS.js} +1 -1
- package/chunks/{chunk-T22H4V4R.js → chunk-B34OV7K2.js} +1 -1
- package/chunks/{chunk-FZ5NVRVK.js → chunk-B5FOCTEZ.js} +3 -3
- package/chunks/{chunk-3UC7ZC43.js → chunk-B6WUEODT.js} +7 -7
- package/chunks/{chunk-MJKEQSZI.js → chunk-BGL43SJ3.js} +1 -1
- package/chunks/{chunk-OAYOHSGP.js → chunk-BHRU4SDF.js} +4 -4
- package/chunks/{chunk-YPCPZSZB.js → chunk-CCGOFQV3.js} +56 -4
- package/chunks/{chunk-FDY3XOTM.js → chunk-CR5IU2RX.js} +4 -4
- package/chunks/{chunk-LWGLBVWA.js → chunk-CSOTA7KX.js} +1 -1
- package/chunks/{chunk-AGZF43UV.js → chunk-E7HM76PR.js} +7153 -4406
- package/chunks/{chunk-NIOPRZNN.js → chunk-E7KNELIZ.js} +1 -1
- package/chunks/{chunk-YSU33ACT.js → chunk-ELMPRVCB.js} +5 -13
- package/chunks/{chunk-SEZC2725.js → chunk-ER3BKOLB.js} +13 -0
- package/chunks/{chunk-3VJ7MT3N.js → chunk-EXPMGZZV.js} +14 -3
- package/chunks/{chunk-DBF3HTLT.js → chunk-FB2ULV4M.js} +4 -4
- package/chunks/{chunk-RZORUN6X.js → chunk-FEJ2FZ3U.js} +1 -1
- package/chunks/chunk-FK77ILF5.js +2477 -0
- package/chunks/{chunk-DMSNJDWG.js → chunk-FV7425LN.js} +38 -3
- package/chunks/{chunk-ALX6BUYW.js → chunk-G7ZG7CA6.js} +186 -27
- package/chunks/{chunk-2INJBYLJ.js → chunk-GIY6JTTC.js} +6 -6
- package/chunks/{chunk-D5KISTLW.js → chunk-H6EMK6QK.js} +1 -1
- package/chunks/{chunk-KYTQOCDE.js → chunk-HW4N45EE.js} +17 -46
- package/chunks/{chunk-VXGPYF7U.js → chunk-IFEWJXFS.js} +1 -1
- package/chunks/chunk-II73RK2S.js +158 -0
- package/chunks/{chunk-TENOUWY2.js → chunk-IJRL2S3D.js} +3 -3
- package/chunks/{chunk-GQBRGBLS.js → chunk-J43WPR6U.js} +9 -9
- package/chunks/{chunk-SRNPMG7J.js → chunk-JYOWO2F7.js} +16 -10
- package/chunks/{chunk-57NRQM3P.js → chunk-K34DD7FH.js} +2 -2
- package/chunks/{chunk-O2RJARYU.js → chunk-KKWO4IX2.js} +1409 -4088
- package/chunks/{chunk-SOT7OMGL.js → chunk-KT3SI3CQ.js} +2 -2
- package/chunks/{chunk-NUK2PFSU.js → chunk-L7IWK7JY.js} +1 -1
- package/chunks/{chunk-MHQKYQDS.js → chunk-LC4SMGYY.js} +158 -3
- package/chunks/chunk-LGFAYIKX.js +115 -0
- package/chunks/{chunk-MBWLP5OT.js → chunk-LKXDC3AW.js} +162 -40
- package/chunks/{chunk-7Z6UEJA4.js → chunk-ME344634.js} +5 -2
- package/chunks/{chunk-V6NH3CUC.js → chunk-MOQOAH7N.js} +3 -3
- package/chunks/{chunk-FPHA7WIZ.js → chunk-MYZWWRVW.js} +3 -3
- package/chunks/{chunk-YHTNWGRR.js → chunk-MZ7BABX3.js} +1 -1
- package/chunks/chunk-N74BA4VP.js +949 -0
- package/chunks/{chunk-B236XPGF.js → chunk-NQKLOAVJ.js} +29 -2
- package/chunks/{chunk-LXFCHX4X.js → chunk-OU6FPTJZ.js} +312 -25
- package/chunks/{chunk-HTWHAN44.js → chunk-PC4ZXHU2.js} +1 -1
- package/chunks/{chunk-3X76L4UF.js → chunk-PLGONYEL.js} +5 -5
- package/chunks/{chunk-WXUDZHOJ.js → chunk-PVXPDMGP.js} +10 -2
- package/chunks/{chunk-ZO5EA3XJ.js → chunk-QCSLZO6J.js} +1 -1
- package/chunks/{chunk-CEYA7K7C.js → chunk-QHT5USLK.js} +62 -12
- package/chunks/{chunk-NPQWQYU6.js → chunk-QYNNN7BK.js} +1 -1
- package/chunks/{chunk-N4YJQTRL.js → chunk-RFIL6REB.js} +71 -2
- package/chunks/{chunk-KSPVDFE4.js → chunk-SAIILPQ3.js} +0 -3
- package/chunks/{chunk-OIFSBXSP.js → chunk-SJHAUELE.js} +3 -3
- package/chunks/{chunk-BZHLWC6T.js → chunk-SUAKHHK7.js} +2 -2
- package/chunks/{chunk-QOKV6AO4.js → chunk-T2EJHSE3.js} +184 -147
- package/chunks/{chunk-ZI4MGBB5.js → chunk-TBAZMRW3.js} +1 -1
- package/chunks/{chunk-6QVRLKTZ.js → chunk-TBLJNMI7.js} +7 -7
- package/chunks/{chunk-J6ABRKAB.js → chunk-TDGF35EN.js} +2 -2
- package/chunks/chunk-TEYGKLIZ.js +167 -0
- package/chunks/{chunk-TOAUDIS7.js → chunk-TLWA7TPR.js} +6 -2
- package/chunks/chunk-UIDXQNMV.js +13 -0
- package/chunks/{chunk-MX7CTJHR.js → chunk-UZCT5OLN.js} +9 -9
- package/chunks/{chunk-LM3YCCZJ.js → chunk-VEB3NYSO.js} +1 -1
- package/chunks/{chunk-UTL7KABL.js → chunk-VQJ64BIQ.js} +7 -7
- package/chunks/{chunk-C2QKQZEQ.js → chunk-VTCPREC7.js} +3 -3
- package/chunks/{chunk-5ES5YPED.js → chunk-VZ7HY3YU.js} +1 -1
- package/chunks/{chunk-QOMAHFYI.js → chunk-W7EEOR7D.js} +3 -3
- package/chunks/{chunk-NVFJSIAX.js → chunk-WMPVYQ4P.js} +1 -1
- package/chunks/{chunk-V5VTULL5.js → chunk-X265R5C6.js} +3 -3
- package/chunks/{chunk-LKWAVAB5.js → chunk-X2XNQQJI.js} +2 -2
- package/chunks/{chunk-MJKMIQMK.js → chunk-X3YGAX7V.js} +1 -1
- package/chunks/{chunk-5WQF27GW.js → chunk-XQ2GVLNJ.js} +1 -1
- package/chunks/chunk-XUSONNJ4.js +28 -0
- package/chunks/{chunk-DXXNMPJH.js → chunk-YMWIKAEE.js} +460 -93
- package/chunks/{chunk-A34EPIUV.js → chunk-YYU6H66M.js} +18 -15
- package/chunks/{chunk-XH7H6YG4.js → chunk-ZK5EI6NR.js} +4 -107
- package/chunks/{computer-use-5S3NVXRA.js → computer-use-4CTF6WS3.js} +24 -24
- package/chunks/{contextCommand-DPBALZFK.js → contextCommand-Z3XWQCY6.js} +28 -27
- package/chunks/{cron-create-7JYUDTUY.js → cron-create-JSXEIPVS.js} +4 -4
- package/chunks/{cron-delete-YPULQQAK.js → cron-delete-OIFVEPGU.js} +4 -4
- package/chunks/{cron-list-HJIW5MHP.js → cron-list-D4ZX4IOE.js} +11 -7
- package/chunks/{daemon-7XYW4BC3.js → daemon-F3I2RXP6.js} +208 -63
- package/chunks/daemon-status-provider-6A4QCG6Q.js +74 -0
- package/chunks/dist-573HNSVO.js +3217 -0
- package/chunks/{dist-ABQ7VWRR.js → dist-FQ5WRJPL.js} +2 -2
- package/chunks/{dist-JPSVN367.js → dist-IMFFBMAT.js} +6 -3792
- package/chunks/{dist-BV56HW3W.js → dist-OT7G2WNM.js} +250 -1
- package/chunks/{dist-AW5ZYMUI.js → dist-PKFTNUHO.js} +10 -11
- package/chunks/{dist-AAO65DPC.js → dist-YTRKAVUB.js} +1 -1
- package/chunks/{earlyInputCapture-GGPV4IFT.js → earlyInputCapture-7O47HW62.js} +25 -25
- package/chunks/{edit-HJ7ISN4C.js → edit-E32KWLF2.js} +25 -25
- package/chunks/{en-XR3S5VWY.js → en-GA2RMXY5.js} +6 -1
- package/chunks/{enter-worktree-2BQT675D.js → enter-worktree-ZKFFMHUD.js} +24 -24
- package/chunks/{enterPlanMode-SJXHJ3WD.js → enterPlanMode-HLDC6NGX.js} +24 -24
- package/chunks/{errors-EVGER4AZ.js → errors-MTIDDXX7.js} +26 -26
- package/chunks/{exit-worktree-RPDDOSPM.js → exit-worktree-YHY72XA2.js} +24 -24
- package/chunks/{exitPlanMode-BQEROAJ7.js → exitPlanMode-JNXKBZ2C.js} +24 -24
- package/chunks/{fast-path-FX2ICJ2O.js → fast-path-DVAGBROV.js} +5 -2
- package/chunks/{fr-MBMNQSQ6.js → fr-GWNQG4XH.js} +4 -0
- package/chunks/{gemini-EHUXCOUI.js → gemini-2TMHJVRC.js} +444 -256
- package/chunks/{geminiContentGenerator-YDUUKAM2.js → geminiContentGenerator-WCH4GWD3.js} +4 -4
- package/chunks/{glob-3VIGV2DL.js → glob-I6Y4AGKO.js} +52 -28
- package/chunks/{grep-4D3S4NUC.js → grep-3P2J3LU5.js} +24 -24
- package/chunks/handleAutoUpdate-4IVRMRG4.js +65 -0
- package/chunks/{initializer-QZTZSSLW.js → initializer-DVNCPVTC.js} +31 -29
- package/chunks/{ja-HFV5QCHR.js → ja-2HOTA47X.js} +4 -0
- package/chunks/{keychain-token-storage-GPAKRDKQ.js → keychain-token-storage-UJV5XP4Z.js} +2 -2
- package/chunks/{list-WQ24EAMK.js → list-4D6QK2KZ.js} +30 -30
- package/chunks/{loadedSettingsAdapter-XV74KOSR.js → loadedSettingsAdapter-GNGELH6Q.js} +28 -28
- package/chunks/{loop-wakeup-34YK7T7O.js → loop-wakeup-OHWEMFP5.js} +5 -5
- package/chunks/{ls-MPD5OJUX.js → ls-L654WOAT.js} +4 -4
- package/chunks/{lsp-72PWTDW7.js → lsp-M35KE54Y.js} +2 -2
- package/chunks/{mcp-CO3MKM7N.js → mcp-2IL72YJP.js} +28 -28
- package/chunks/{monitor-GB56T65A.js → monitor-BLVCZAVX.js} +32 -27
- package/chunks/nonInteractiveCli-UD3V7UOO.js +116 -0
- package/chunks/{notebook-edit-VMXAAE3R.js → notebook-edit-5TVMWIWY.js} +25 -25
- package/chunks/{openaiContentGenerator-GM77FLII.js → openaiContentGenerator-5LJ5A7YY.js} +12 -12
- package/chunks/{pidfile-HFXSHR64.js → pidfile-AN5JQJ2T.js} +25 -25
- package/chunks/{qwenContentGenerator-QSCXQIKJ.js → qwenContentGenerator-E2JSB4FJ.js} +26 -26
- package/chunks/{qwenOAuth2-LWLA6MQG.js → qwenOAuth2-ZV7YLMND.js} +5 -5
- package/chunks/{read-file-SVH6CGQY.js → read-file-N5DEFUDU.js} +8 -8
- package/chunks/{read-mcp-resource-OWOBJY72.js → read-mcp-resource-CNQHBSJM.js} +2 -2
- package/chunks/{record-artifact-URK3YUDJ.js → record-artifact-PP3LKHTS.js} +2 -2
- package/chunks/{ripGrep-JD5O2FLX.js → ripGrep-BLJHTHKE.js} +24 -24
- package/chunks/{run-qwen-serve-FADLRJUN.js → run-qwen-serve-4JDO64RN.js} +785 -35
- package/chunks/{scheduler-Q65SB7DJ.js → scheduler-FQR4XA7J.js} +24 -24
- package/chunks/{send-message-L7OLQEKH.js → send-message-OA6MV4EU.js} +3 -3
- package/chunks/{serve-6ZXAMQ4M.js → serve-ZOCFC7YM.js} +29 -29
- package/chunks/{server-WKDQA6XR.js → server-GEOYEPYT.js} +2504 -439
- package/chunks/{session-STWOX7RA.js → session-NJCONHGX.js} +66 -61
- package/chunks/{settings-LC4CCWQQ.js → settings-QBGGXANI.js} +27 -27
- package/chunks/{shell-FEG3RLPW.js → shell-2DVPSEKT.js} +24 -24
- package/chunks/{skill-XTXAMDAP.js → skill-HIPXUHV7.js} +24 -12
- package/chunks/{spawnChannel-B6JJPBPV.js → spawnChannel-3JYXY52G.js} +27 -27
- package/chunks/{src-HITDGVRM.js → src-NLEX4CUC.js} +91 -36
- package/chunks/{startInteractiveUI-IT423PW4.js → startInteractiveUI-TYGZIU27.js} +6213 -14273
- package/chunks/{syntheticOutput-J3MLCQAR.js → syntheticOutput-F7LSDCAR.js} +3 -3
- package/chunks/{task-create-ELEBZ7H2.js → task-create-RPFZNB4B.js} +7 -7
- package/chunks/{task-list-IEUZLF3Y.js → task-list-TGGJX7EA.js} +5 -5
- package/chunks/{task-stop-QEZW3UDY.js → task-stop-W7NGTMYQ.js} +2 -2
- package/chunks/{task-update-DW36J5P5.js → task-update-5WUG5MVQ.js} +7 -7
- package/chunks/{team-create-DAG6SNHZ.js → team-create-MGX57SZJ.js} +24 -24
- package/chunks/{team-delete-SVITIMBO.js → team-delete-UHQQ2QQX.js} +5 -5
- package/chunks/{team-plan-approval-DRDY5OPC.js → team-plan-approval-BO2N7J3E.js} +24 -24
- package/chunks/{theme-manager-N67EZC6B.js → theme-manager-WU7LOA64.js} +25 -25
- package/chunks/{todoWrite-SFFQHZYY.js → todoWrite-C4ZAMSR3.js} +4 -4
- package/chunks/{tool-search-CMDJ5HGM.js → tool-search-CLBKVEVK.js} +11 -28
- package/chunks/{trustedFolders-JD4QX6UJ.js → trustedFolders-GAQM2GHB.js} +25 -25
- package/chunks/updateCheck-RIK2YIQE.js +65 -0
- package/chunks/{validateNonInterActiveAuth-ADDCGZ6A.js → validateNonInterActiveAuth-NS3GBIJZ.js} +61 -56
- package/chunks/{version-VAONYVE5.js → version-B3RSLP54.js} +2 -1
- package/chunks/{web-fetch-SXAXJHIO.js → web-fetch-UYGMOHW4.js} +5 -5
- package/chunks/{workflow-DG2BR42M.js → workflow-7APPIGND.js} +25 -25
- package/chunks/{workspace-providers-status-XGW5OZTD.js → workspace-providers-status-SC67GLMY.js} +30 -30
- package/chunks/{workspace-service-GX3GGZLU.js → workspace-service-ZJHF22WA.js} +35 -35
- package/chunks/{workspace-skills-status-LFK5WV2V.js → workspace-skills-status-NJEK6P6Y.js} +29 -29
- package/chunks/{write-file-UR7FXWOU.js → write-file-R6Z3VLRY.js} +25 -25
- package/chunks/{zh-FSVJTXA4.js → zh-7XVVRTDH.js} +6 -1
- package/chunks/{zh-TW-66MG4LWL.js → zh-TW-DPH554OG.js} +6 -1
- package/cli.js +19 -11
- package/locales/en.js +9 -2
- package/locales/fr.js +6 -0
- package/locales/ja.js +6 -0
- package/locales/zh-TW.js +9 -2
- package/locales/zh.js +9 -2
- package/node_modules/@qwen-code/audio-capture/package.json +1 -1
- package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-arm64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-x64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/win32-x64/@qwen-code+audio-capture.node +0 -0
- package/package.json +3 -3
- package/web-shell/assets/{arc-CUpe0ibu.js → arc-C7hjaa7g.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-JvyrY_mK.js → architectureDiagram-3BPJPVTR-pm9KtMZY.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-CK3Y_vom.js → blockDiagram-GPEHLZMM-CApElKsU.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-DprSnyrf.js → c4Diagram-AAUBKEIU-BeDe0-9N.js} +1 -1
- package/web-shell/assets/channel-BNf1dW-j.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-DJtRJPmu.js → chunk-2J33WTMH-DJEkmLdl.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-ON6Q2-BG.js → chunk-4BX2VUAB-DXX8gdpe.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-c60ou5HZ.js → chunk-55IACEB6-DORmk1kQ.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-BamtPKxx.js → chunk-727SXJPM-DWmO4mCZ.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-CsVgpu-T.js → chunk-AQP2D5EJ-C7LRaYw2.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-vZrpjAx4.js → chunk-FMBD7UC4-CCdXgyC5.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-qxyNOd5t.js → chunk-ND2GUHAM-SyL_xvWc.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-BvTJSM_S.js → chunk-QZHKN3VN-BzOuCKxa.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-uOIGlzgi.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-uOIGlzgi.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-1tXB4pju.js → cose-bilkent-S5V4N54A-BMPWyDKc.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-DglBzme8.js → dagre-BM42HDAG-B_GnPWik.js} +1 -1
- package/web-shell/assets/{diagram-2AECGRRQ-DEJpqsWz.js → diagram-2AECGRRQ-fJqMc3S_.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-CsQ1NrMG.js → diagram-5GNKFQAL-ximV3O9D.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-Z-3codAi.js → diagram-KO2AKTUF-2vVyjzqw.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-mBuPTA2i.js → diagram-LMA3HP47-Dckcx1sT.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-tcA3K3s9.js → diagram-OG6HWLK6-BAVv47ga.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-D9bXFRyZ.js → erDiagram-TEJ5UH35-Bn3nf5J0.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-Ck0Py04s.js → flowDiagram-I6XJVG4X-BIbGF9JT.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-rOevCekE.js → ganttDiagram-6RSMTGT7-CDbhb3AH.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-CoPQaLj0.js → gitGraphDiagram-PVQCEYII-CbKG1a_w.js} +1 -1
- package/web-shell/assets/index-CnE2ur2L.js +779 -0
- package/web-shell/assets/index-hSKXrlSE.css +5 -0
- package/web-shell/assets/{infoDiagram-5YYISTIA-a77CUKoI.js → infoDiagram-5YYISTIA-DUFxTy8m.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-DlLgUAO-.js → ishikawaDiagram-YF4QCWOH-D69hrBG8.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-BXR5kVAZ.js → journeyDiagram-JHISSGLW-DsvEw51Y.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-h5CoHZg7.js → kanban-definition-UN3LZRKU-lFFjZEYd.js} +1 -1
- package/web-shell/assets/{linear-BkaREbyD.js → linear-Bo5bWzvo.js} +1 -1
- package/web-shell/assets/{mermaid.core-Bkagz0ng.js → mermaid.core-C_HKMJx5.js} +5 -5
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-D7EMKR_5.js → mindmap-definition-RKZ34NQL-DlV-NUTs.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-DAmPOjsL.js → pieDiagram-4H26LBE5-B2e6FH2a.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-e4WFQkQu.js → quadrantDiagram-W4KKPZXB-D75kSk-b.js} +1 -1
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-v1zo7OiV.js → requirementDiagram-4Y6WPE33-CQ6xakhM.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-Bstm4aW_.js → sankeyDiagram-5OEKKPKP-J_XTKrcY.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-DzcZy6xN.js → sequenceDiagram-3UESZ5HK-D7jTcBtf.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-BEJH6YbH.js → stateDiagram-AJRCARHV-CrlM_OT3.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-Bs4TK_84.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-TI1wH-Pm.js → timeline-definition-PNZ67QCA-5oZi76Ef.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-YaH8VDMS.js → vennDiagram-CIIHVFJN-DO5YptPK.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-B80co7OP.js → wardley-L42UT6IY-4t1eJDvM.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-4fHNDEMl.js → wardleyDiagram-YWT4CUSO-DKZc661S.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-DT3q4Z3J.js → xychartDiagram-2RQKCTM6-su9hp08G.js} +1 -1
- package/web-shell/index.html +2 -2
- package/chunks/chunk-L7QPM2YD.js +0 -481
- package/chunks/daemon-status-provider-2EWTHT7Y.js +0 -73
- package/chunks/nonInteractiveCli-HNZRIM5I.js +0 -111
- package/web-shell/assets/channel-BAxQ2-aZ.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-Cvd_xhZI.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-Cvd_xhZI.js +0 -1
- package/web-shell/assets/index-Cr7MYW6T.css +0 -5
- package/web-shell/assets/index-DCHPAUp3.js +0 -730
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BopifCBI.js +0 -1
|
@@ -79,6 +79,168 @@ init_esbuild_shims();
|
|
|
79
79
|
import { spawn } from "node:child_process";
|
|
80
80
|
import { Readable, Writable } from "node:stream";
|
|
81
81
|
import { EventEmitter } from "node:events";
|
|
82
|
+
|
|
83
|
+
// packages/channels/base/dist/ChannelLoopTools.js
|
|
84
|
+
init_esbuild_shims();
|
|
85
|
+
var CHANNEL_LOOP_MCP_SERVER_NAME = "channel_loop";
|
|
86
|
+
var CLIENT_MCP_MESSAGE_METHOD = "qwen/control/client_mcp/message";
|
|
87
|
+
var WORKSPACE_MCP_RUNTIME_ADD_METHOD = "qwen/control/workspace/mcp/runtime-add";
|
|
88
|
+
var CLIENT_MCP_OVER_WS_CONFIG_FLAG = "__clientMcpOverWs";
|
|
89
|
+
var createTool = {
|
|
90
|
+
name: "channel_loop_create",
|
|
91
|
+
description: "Create a recurring proactive reminder or scheduled prompt for the current channel chat. Use this in channel sessions instead of cron_create.",
|
|
92
|
+
inputSchema: {
|
|
93
|
+
type: "object",
|
|
94
|
+
properties: {
|
|
95
|
+
cron: {
|
|
96
|
+
type: "string",
|
|
97
|
+
description: 'Standard 5-field cron expression in local time, for example "*/5 * * * *".'
|
|
98
|
+
},
|
|
99
|
+
prompt: {
|
|
100
|
+
type: "string",
|
|
101
|
+
description: "The message or instruction to run and proactively push to this channel chat."
|
|
102
|
+
},
|
|
103
|
+
recurring: {
|
|
104
|
+
type: "boolean",
|
|
105
|
+
description: "Whether the loop recurs. Defaults to true."
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
required: ["cron", "prompt"]
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
var listTool = {
|
|
112
|
+
name: "channel_loop_list",
|
|
113
|
+
description: "List proactive loops for the current channel chat.",
|
|
114
|
+
inputSchema: { type: "object", properties: {} }
|
|
115
|
+
};
|
|
116
|
+
var cancelTool = {
|
|
117
|
+
name: "channel_loop_cancel",
|
|
118
|
+
description: "Cancel a proactive loop for the current channel chat.",
|
|
119
|
+
inputSchema: {
|
|
120
|
+
type: "object",
|
|
121
|
+
properties: {
|
|
122
|
+
id: { type: "string", description: "Loop id to cancel." }
|
|
123
|
+
},
|
|
124
|
+
required: ["id"]
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
var CHANNEL_LOOP_MCP_TOOLS = [createTool, listTool, cancelTool];
|
|
128
|
+
var ChannelLoopMcpServer = class {
|
|
129
|
+
static {
|
|
130
|
+
__name(this, "ChannelLoopMcpServer");
|
|
131
|
+
}
|
|
132
|
+
handler;
|
|
133
|
+
constructor(handler) {
|
|
134
|
+
this.handler = handler;
|
|
135
|
+
}
|
|
136
|
+
async handleMessage(message, context) {
|
|
137
|
+
const id = message["id"];
|
|
138
|
+
if (id === void 0 || id === null) {
|
|
139
|
+
return void 0;
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const result = await this.dispatch(message, context);
|
|
143
|
+
return { jsonrpc: "2.0", id, result };
|
|
144
|
+
} catch (error) {
|
|
145
|
+
return {
|
|
146
|
+
jsonrpc: "2.0",
|
|
147
|
+
id,
|
|
148
|
+
error: {
|
|
149
|
+
code: -32603,
|
|
150
|
+
message: error instanceof Error ? error.message : String(error)
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async dispatch(message, context) {
|
|
156
|
+
switch (message["method"]) {
|
|
157
|
+
case "initialize":
|
|
158
|
+
return {
|
|
159
|
+
protocolVersion: "2024-11-05",
|
|
160
|
+
capabilities: { tools: {} },
|
|
161
|
+
serverInfo: { name: CHANNEL_LOOP_MCP_SERVER_NAME, version: "0.0.1" }
|
|
162
|
+
};
|
|
163
|
+
case "tools/list":
|
|
164
|
+
return { tools: CHANNEL_LOOP_MCP_TOOLS };
|
|
165
|
+
case "tools/call":
|
|
166
|
+
return this.callTool(message["params"], context);
|
|
167
|
+
case "ping":
|
|
168
|
+
return {};
|
|
169
|
+
default:
|
|
170
|
+
throw new Error(`Method not found: ${String(message["method"])}`);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async callTool(rawParams, context) {
|
|
174
|
+
if (!context.sessionId) {
|
|
175
|
+
throw new Error("Missing channel session id.");
|
|
176
|
+
}
|
|
177
|
+
if (typeof rawParams !== "object" || rawParams === null) {
|
|
178
|
+
throw new Error("Invalid tools/call params.");
|
|
179
|
+
}
|
|
180
|
+
const params = rawParams;
|
|
181
|
+
const name = params["name"];
|
|
182
|
+
const args = typeof params["arguments"] === "object" && params["arguments"] !== null ? params["arguments"] : {};
|
|
183
|
+
let toolResult;
|
|
184
|
+
switch (name) {
|
|
185
|
+
case createTool.name:
|
|
186
|
+
toolResult = await this.handler.create(context.sessionId, readCreateInput(args));
|
|
187
|
+
break;
|
|
188
|
+
case listTool.name:
|
|
189
|
+
toolResult = await this.handler.list(context.sessionId);
|
|
190
|
+
break;
|
|
191
|
+
case cancelTool.name:
|
|
192
|
+
toolResult = await this.handler.cancel(context.sessionId, readId(args));
|
|
193
|
+
break;
|
|
194
|
+
default:
|
|
195
|
+
throw new Error(`Unknown channel loop tool: ${String(name)}`);
|
|
196
|
+
}
|
|
197
|
+
const result = typeof toolResult === "string" ? { text: toolResult } : toolResult;
|
|
198
|
+
return {
|
|
199
|
+
content: [{ type: "text", text: result.text }],
|
|
200
|
+
...result.isError ? { isError: true } : {}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
function readCreateInput(args) {
|
|
205
|
+
const cron = args["cron"];
|
|
206
|
+
const prompt = args["prompt"];
|
|
207
|
+
if (typeof cron !== "string" || cron.trim().length === 0) {
|
|
208
|
+
throw new Error("cron must be a non-empty string.");
|
|
209
|
+
}
|
|
210
|
+
if (typeof prompt !== "string" || prompt.trim().length === 0) {
|
|
211
|
+
throw new Error("prompt must be a non-empty string.");
|
|
212
|
+
}
|
|
213
|
+
const recurring = readRecurring(args["recurring"]);
|
|
214
|
+
return {
|
|
215
|
+
cron: cron.trim(),
|
|
216
|
+
prompt: prompt.trim(),
|
|
217
|
+
...recurring !== void 0 ? { recurring } : {}
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
__name(readCreateInput, "readCreateInput");
|
|
221
|
+
function readRecurring(value) {
|
|
222
|
+
if (typeof value === "boolean")
|
|
223
|
+
return value;
|
|
224
|
+
if (value === "false" || value === 0)
|
|
225
|
+
return false;
|
|
226
|
+
if (value === "true" || value === 1)
|
|
227
|
+
return true;
|
|
228
|
+
return void 0;
|
|
229
|
+
}
|
|
230
|
+
__name(readRecurring, "readRecurring");
|
|
231
|
+
function readId(args) {
|
|
232
|
+
const id = args["id"];
|
|
233
|
+
if (typeof id !== "string" || id.trim().length === 0) {
|
|
234
|
+
throw new Error("id must be a non-empty string.");
|
|
235
|
+
}
|
|
236
|
+
return id.trim();
|
|
237
|
+
}
|
|
238
|
+
__name(readId, "readId");
|
|
239
|
+
|
|
240
|
+
// packages/channels/base/dist/AcpBridge.js
|
|
241
|
+
var MID_TURN_QUEUE_DRAIN_METHOD = "craft/drainMidTurnQueue";
|
|
242
|
+
var ACP_EVENT_LOOP_STALL_RESTART_MS = 5 * 60 * 1e3;
|
|
243
|
+
var ACP_EVENT_LOOP_STALL_RE = /^\[perf\] acp agent event loop stall: max=(\d+(?:\.\d+)?)ms/m;
|
|
82
244
|
function readAvailableCommandAltNames(raw) {
|
|
83
245
|
if (typeof raw !== "object" || raw === null)
|
|
84
246
|
return void 0;
|
|
@@ -100,6 +262,10 @@ var AcpBridge = class extends EventEmitter {
|
|
|
100
262
|
connection = null;
|
|
101
263
|
options;
|
|
102
264
|
_availableCommands = [];
|
|
265
|
+
channelLoopMcpServer;
|
|
266
|
+
channelLoopToolHandlers = [];
|
|
267
|
+
channelLoopMcpRegistered = false;
|
|
268
|
+
channelLoopMcpRegistration = null;
|
|
103
269
|
constructor(options) {
|
|
104
270
|
super();
|
|
105
271
|
this.options = options;
|
|
@@ -120,7 +286,7 @@ var AcpBridge = class extends EventEmitter {
|
|
|
120
286
|
this.child = spawn(process.execPath, args, {
|
|
121
287
|
cwd,
|
|
122
288
|
stdio: ["pipe", "pipe", "pipe"],
|
|
123
|
-
env: { ...process.env },
|
|
289
|
+
env: { ...process.env, QWEN_CODE_DISABLE_CRON: "1" },
|
|
124
290
|
shell: false
|
|
125
291
|
});
|
|
126
292
|
this.child.stderr?.on("data", (data) => {
|
|
@@ -128,6 +294,7 @@ var AcpBridge = class extends EventEmitter {
|
|
|
128
294
|
if (msg) {
|
|
129
295
|
process.stderr.write(`[AcpBridge] ${msg}
|
|
130
296
|
`);
|
|
297
|
+
this.maybeKillOnEventLoopStall(msg);
|
|
131
298
|
}
|
|
132
299
|
});
|
|
133
300
|
this.child.on("exit", (code, signal) => {
|
|
@@ -154,6 +321,7 @@ var AcpBridge = class extends EventEmitter {
|
|
|
154
321
|
const optionId = options.find((o) => o.optionId === "proceed_once")?.optionId || options[0]?.optionId || "proceed_once";
|
|
155
322
|
return { outcome: { outcome: "selected", optionId } };
|
|
156
323
|
}, "requestPermission"),
|
|
324
|
+
extMethod: /* @__PURE__ */ __name(async (method, params) => this.handleExtMethod(method, params), "extMethod"),
|
|
157
325
|
extNotification: /* @__PURE__ */ __name(async () => {
|
|
158
326
|
}, "extNotification")
|
|
159
327
|
}), stream);
|
|
@@ -161,14 +329,28 @@ var AcpBridge = class extends EventEmitter {
|
|
|
161
329
|
protocolVersion: PROTOCOL_VERSION,
|
|
162
330
|
clientCapabilities: {}
|
|
163
331
|
});
|
|
332
|
+
await this.registerChannelLoopMcpServer();
|
|
333
|
+
}
|
|
334
|
+
registerChannelLoopToolHandler(handler) {
|
|
335
|
+
if (!this.channelLoopToolHandlers.includes(handler)) {
|
|
336
|
+
this.channelLoopToolHandlers.push(handler);
|
|
337
|
+
}
|
|
338
|
+
this.channelLoopMcpServer ??= new ChannelLoopMcpServer({
|
|
339
|
+
create: /* @__PURE__ */ __name((sessionId, input) => this.resolveChannelLoopToolHandler(sessionId).create(sessionId, input), "create"),
|
|
340
|
+
list: /* @__PURE__ */ __name((sessionId) => this.resolveChannelLoopToolHandler(sessionId).list(sessionId), "list"),
|
|
341
|
+
cancel: /* @__PURE__ */ __name((sessionId, id) => this.resolveChannelLoopToolHandler(sessionId).cancel(sessionId, id), "cancel")
|
|
342
|
+
});
|
|
343
|
+
void this.registerChannelLoopMcpServer();
|
|
164
344
|
}
|
|
165
345
|
async newSession(cwd) {
|
|
166
346
|
const conn = this.ensureConnection();
|
|
347
|
+
await this.registerChannelLoopMcpServer();
|
|
167
348
|
const response = await conn.newSession({ cwd, mcpServers: [] });
|
|
168
349
|
return response.sessionId;
|
|
169
350
|
}
|
|
170
351
|
async loadSession(sessionId, cwd) {
|
|
171
352
|
const conn = this.ensureConnection();
|
|
353
|
+
await this.registerChannelLoopMcpServer();
|
|
172
354
|
const response = await conn.loadSession({
|
|
173
355
|
sessionId,
|
|
174
356
|
cwd,
|
|
@@ -263,7 +445,103 @@ var AcpBridge = class extends EventEmitter {
|
|
|
263
445
|
}
|
|
264
446
|
return this.connection;
|
|
265
447
|
}
|
|
448
|
+
maybeKillOnEventLoopStall(stderr) {
|
|
449
|
+
const match = ACP_EVENT_LOOP_STALL_RE.exec(stderr);
|
|
450
|
+
if (!match)
|
|
451
|
+
return;
|
|
452
|
+
const maxMs = Number(match[1]);
|
|
453
|
+
if (!Number.isFinite(maxMs) || maxMs < ACP_EVENT_LOOP_STALL_RESTART_MS) {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
456
|
+
const child = this.child;
|
|
457
|
+
if (!child || child.killed || child.exitCode !== null) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
process.stderr.write(`[AcpBridge] ACP agent event loop stalled for ${Math.round(maxMs)}ms; killing child process to trigger restart
|
|
461
|
+
`);
|
|
462
|
+
child.kill("SIGKILL");
|
|
463
|
+
}
|
|
464
|
+
async registerChannelLoopMcpServer() {
|
|
465
|
+
if (!this.connection || !this.channelLoopMcpServer || this.channelLoopMcpRegistered) {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
if (this.channelLoopMcpRegistration) {
|
|
469
|
+
await this.channelLoopMcpRegistration;
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
this.channelLoopMcpRegistration = this.connection.extMethod(WORKSPACE_MCP_RUNTIME_ADD_METHOD, {
|
|
473
|
+
name: CHANNEL_LOOP_MCP_SERVER_NAME,
|
|
474
|
+
originatorClientId: "channel",
|
|
475
|
+
config: {
|
|
476
|
+
type: "sdk",
|
|
477
|
+
[CLIENT_MCP_OVER_WS_CONFIG_FLAG]: true
|
|
478
|
+
}
|
|
479
|
+
}).then((result) => {
|
|
480
|
+
if (isSkippedMcpRegistration(result)) {
|
|
481
|
+
this.channelLoopMcpRegistered = false;
|
|
482
|
+
process.stderr.write(`[AcpBridge] Channel loop MCP server registration skipped${formatSkippedRegistrationReason(result)}
|
|
483
|
+
`);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
this.channelLoopMcpRegistered = true;
|
|
487
|
+
}).catch((error) => {
|
|
488
|
+
this.channelLoopMcpRegistered = false;
|
|
489
|
+
process.stderr.write(`[AcpBridge] Failed to register channel loop MCP server: ${error instanceof Error ? error.message : String(error)}
|
|
490
|
+
`);
|
|
491
|
+
}).finally(() => {
|
|
492
|
+
this.channelLoopMcpRegistration = null;
|
|
493
|
+
});
|
|
494
|
+
await this.channelLoopMcpRegistration;
|
|
495
|
+
}
|
|
496
|
+
async handleExtMethod(method, params) {
|
|
497
|
+
if (method === CLIENT_MCP_MESSAGE_METHOD) {
|
|
498
|
+
return this.handleClientMcpMessage(params);
|
|
499
|
+
}
|
|
500
|
+
if (method === MID_TURN_QUEUE_DRAIN_METHOD) {
|
|
501
|
+
return { messages: [] };
|
|
502
|
+
}
|
|
503
|
+
throw new Error(`Method not found: ${method}`);
|
|
504
|
+
}
|
|
505
|
+
async handleClientMcpMessage(params) {
|
|
506
|
+
if (!this.channelLoopMcpServer) {
|
|
507
|
+
throw new Error("Channel loop MCP server is not registered.");
|
|
508
|
+
}
|
|
509
|
+
const server = params["server"];
|
|
510
|
+
if (server !== CHANNEL_LOOP_MCP_SERVER_NAME) {
|
|
511
|
+
throw new Error(`Unknown client MCP server: ${String(server)}`);
|
|
512
|
+
}
|
|
513
|
+
const payload = params["payload"];
|
|
514
|
+
if (typeof payload !== "object" || payload === null) {
|
|
515
|
+
throw new Error("Invalid client MCP payload.");
|
|
516
|
+
}
|
|
517
|
+
const sessionId = typeof params["sessionId"] === "string" ? params["sessionId"] : void 0;
|
|
518
|
+
const response = await this.channelLoopMcpServer.handleMessage(payload, { sessionId });
|
|
519
|
+
if (!response) {
|
|
520
|
+
return { payload: { jsonrpc: "2.0", id: 0, result: {} } };
|
|
521
|
+
}
|
|
522
|
+
return { payload: response };
|
|
523
|
+
}
|
|
524
|
+
resolveChannelLoopToolHandler(sessionId) {
|
|
525
|
+
if (this.channelLoopToolHandlers.length === 1 && !this.channelLoopToolHandlers[0].canHandle) {
|
|
526
|
+
return this.channelLoopToolHandlers[0];
|
|
527
|
+
}
|
|
528
|
+
const handler = this.channelLoopToolHandlers.find((candidate) => candidate.canHandle?.(sessionId) === true);
|
|
529
|
+
if (handler)
|
|
530
|
+
return handler;
|
|
531
|
+
throw new Error(this.channelLoopToolHandlers.length === 0 ? "No channel loop tool handler is registered." : `No channel loop handler matched session ${sessionId}.`);
|
|
532
|
+
}
|
|
266
533
|
};
|
|
534
|
+
function isSkippedMcpRegistration(result) {
|
|
535
|
+
return typeof result === "object" && result !== null && result.skipped === true;
|
|
536
|
+
}
|
|
537
|
+
__name(isSkippedMcpRegistration, "isSkippedMcpRegistration");
|
|
538
|
+
function formatSkippedRegistrationReason(result) {
|
|
539
|
+
if (typeof result !== "object" || result === null)
|
|
540
|
+
return ".";
|
|
541
|
+
const reason = result.reason;
|
|
542
|
+
return typeof reason === "string" && reason.length > 0 ? `: ${sanitizeLogText(reason, 256)}` : ".";
|
|
543
|
+
}
|
|
544
|
+
__name(formatSkippedRegistrationReason, "formatSkippedRegistrationReason");
|
|
267
545
|
|
|
268
546
|
// packages/channels/base/dist/DaemonChannelBridge.js
|
|
269
547
|
init_esbuild_shims();
|
|
@@ -1404,18 +1682,21 @@ var SessionRouter = class {
|
|
|
1404
1682
|
return `${channelName}:${senderId}:${chatId}`;
|
|
1405
1683
|
}
|
|
1406
1684
|
}
|
|
1407
|
-
async resolve(channelName, senderId, chatId, threadId, cwd) {
|
|
1685
|
+
async resolve(channelName, senderId, chatId, threadId, cwd, isGroup) {
|
|
1408
1686
|
const key = this.routingKey(channelName, senderId, chatId, threadId);
|
|
1409
1687
|
let failedCreateWaits = 0;
|
|
1410
1688
|
for (; ; ) {
|
|
1411
1689
|
const existing = this.toSession.get(key);
|
|
1412
1690
|
if (existing) {
|
|
1691
|
+
this.promoteTargetToGroup(existing, isGroup);
|
|
1413
1692
|
return existing;
|
|
1414
1693
|
}
|
|
1415
1694
|
const creating = this.creatingSessions.get(key);
|
|
1416
1695
|
if (creating) {
|
|
1417
1696
|
try {
|
|
1418
|
-
|
|
1697
|
+
const sessionId = await creating;
|
|
1698
|
+
this.promoteTargetToGroup(sessionId, isGroup);
|
|
1699
|
+
return sessionId;
|
|
1419
1700
|
} catch (err) {
|
|
1420
1701
|
if (this.creatingSessions.get(key) === creating) {
|
|
1421
1702
|
this.creatingSessions.delete(key);
|
|
@@ -1437,7 +1718,8 @@ var SessionRouter = class {
|
|
|
1437
1718
|
channelName,
|
|
1438
1719
|
senderId,
|
|
1439
1720
|
chatId,
|
|
1440
|
-
threadId
|
|
1721
|
+
threadId,
|
|
1722
|
+
isGroup
|
|
1441
1723
|
});
|
|
1442
1724
|
this.toCwd.set(sessionId, sessionCwd);
|
|
1443
1725
|
this.persist();
|
|
@@ -1538,6 +1820,15 @@ var SessionRouter = class {
|
|
|
1538
1820
|
this.toCwd.delete(sessionId);
|
|
1539
1821
|
return sessionId;
|
|
1540
1822
|
}
|
|
1823
|
+
promoteTargetToGroup(sessionId, isGroup) {
|
|
1824
|
+
const current = this.toTarget.get(sessionId);
|
|
1825
|
+
if (!current)
|
|
1826
|
+
return;
|
|
1827
|
+
if (current.isGroup === true || isGroup !== true)
|
|
1828
|
+
return;
|
|
1829
|
+
this.toTarget.set(sessionId, { ...current, isGroup: true });
|
|
1830
|
+
this.persist();
|
|
1831
|
+
}
|
|
1541
1832
|
/** Get all session entries for crash recovery. */
|
|
1542
1833
|
getAll() {
|
|
1543
1834
|
const entries = [];
|
|
@@ -1976,6 +2267,66 @@ function truncateError(message) {
|
|
|
1976
2267
|
}
|
|
1977
2268
|
__name(truncateError, "truncateError");
|
|
1978
2269
|
|
|
2270
|
+
// packages/channels/base/dist/channel-memory-intent.js
|
|
2271
|
+
init_esbuild_shims();
|
|
2272
|
+
var REMEMBER_PATTERNS = [
|
|
2273
|
+
/^记住[::]\s*(.+)$/su,
|
|
2274
|
+
/^记一下[::,,]?\s*(.+)$/su,
|
|
2275
|
+
/^帮我记一下[::,,]?\s*(.+)$/su,
|
|
2276
|
+
/^帮我记住[::,,]?\s*(.+)$/su,
|
|
2277
|
+
/^以后记住[::,,]?\s*(.+)$/su,
|
|
2278
|
+
/^remember:\s*(.+)$/isu
|
|
2279
|
+
];
|
|
2280
|
+
var LIST_PATTERNS = [
|
|
2281
|
+
/^你现在记住了什么[??]?$/u,
|
|
2282
|
+
/^查看记忆$/u,
|
|
2283
|
+
/^当前记忆$/u,
|
|
2284
|
+
/^这个聊天你记住了什么[??]?$/u,
|
|
2285
|
+
/^what do you remember[??]?$/iu
|
|
2286
|
+
];
|
|
2287
|
+
var CLEAR_REQUEST_PATTERNS = [
|
|
2288
|
+
/^清空记忆$/u,
|
|
2289
|
+
/^清除记忆$/u,
|
|
2290
|
+
/^忘掉这个聊天的所有记忆$/u,
|
|
2291
|
+
/^把.+的?记忆清空$/u,
|
|
2292
|
+
/^clear memory$/iu
|
|
2293
|
+
];
|
|
2294
|
+
var CLEAR_CONFIRM_PATTERNS = [
|
|
2295
|
+
/^确认清空记忆$/u,
|
|
2296
|
+
/^确认清除记忆$/u,
|
|
2297
|
+
/^confirm clear memory$/iu
|
|
2298
|
+
];
|
|
2299
|
+
function parseChannelMemoryIntent(text) {
|
|
2300
|
+
const trimmed = text.replace(PROMPT_UNSAFE_INVISIBLES, "").trim();
|
|
2301
|
+
if (!trimmed || trimmed.startsWith("/")) {
|
|
2302
|
+
return null;
|
|
2303
|
+
}
|
|
2304
|
+
for (const pattern of CLEAR_CONFIRM_PATTERNS) {
|
|
2305
|
+
if (pattern.test(trimmed)) {
|
|
2306
|
+
return { kind: "clear_confirm" };
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
for (const pattern of CLEAR_REQUEST_PATTERNS) {
|
|
2310
|
+
if (pattern.test(trimmed)) {
|
|
2311
|
+
return { kind: "clear_request" };
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
for (const pattern of LIST_PATTERNS) {
|
|
2315
|
+
if (pattern.test(trimmed)) {
|
|
2316
|
+
return { kind: "list" };
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
for (const pattern of REMEMBER_PATTERNS) {
|
|
2320
|
+
const match = trimmed.match(pattern);
|
|
2321
|
+
const remembered = match?.[1]?.replace(PROMPT_UNSAFE_INVISIBLES, "").trim();
|
|
2322
|
+
if (remembered) {
|
|
2323
|
+
return { kind: "remember", text: remembered };
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
return null;
|
|
2327
|
+
}
|
|
2328
|
+
__name(parseChannelMemoryIntent, "parseChannelMemoryIntent");
|
|
2329
|
+
|
|
1979
2330
|
// packages/channels/base/dist/ChannelBase.js
|
|
1980
2331
|
var CLEAR_CANCEL_TIMEOUT_MS = 3e3;
|
|
1981
2332
|
var GROUP_HISTORY_CONTEXT_MARKER = "[Chat messages since your last reply - for context]";
|
|
@@ -1983,6 +2334,8 @@ var CURRENT_MESSAGE_MARKER = "[Current message - respond to this]";
|
|
|
1983
2334
|
var GROUP_HISTORY_ENTRY_TEXT_LIMIT = 1e3;
|
|
1984
2335
|
var GROUP_HISTORY_ENTRY_METADATA_LIMIT = 256;
|
|
1985
2336
|
var LOOP_CANCEL_GRACE_MS = 5e3;
|
|
2337
|
+
var CHANNEL_MEMORY_CLASSIFIER_MIN_CONFIDENCE = 0.7;
|
|
2338
|
+
var CHANNEL_MEMORY_CLASSIFIER_TRIGGER_RE = /(记住|记得|记一下|记忆|忘掉|忘记|清空|清除|删除|保存|remember|memory|forget)/iu;
|
|
1986
2339
|
var LOOP_TIMED_OUT_MESSAGE = "loop timed out";
|
|
1987
2340
|
var COMMAND_TOKEN_CHARS = "a-zA-Z0-9_:-";
|
|
1988
2341
|
var PARSE_COMMAND_RE = new RegExp(`^\\/([${COMMAND_TOKEN_CHARS}]+)(?:@\\S+)?\\s*(.*)`, "s");
|
|
@@ -2015,6 +2368,7 @@ var ChannelBase = class {
|
|
|
2015
2368
|
/** Resolved proxy URL, available to subclasses for adapter-specific clients. */
|
|
2016
2369
|
proxy;
|
|
2017
2370
|
channelMemory;
|
|
2371
|
+
memoryIntentClassifier;
|
|
2018
2372
|
groupHistory;
|
|
2019
2373
|
loopController;
|
|
2020
2374
|
instructedSessions = /* @__PURE__ */ new Set();
|
|
@@ -2028,16 +2382,24 @@ var ChannelBase = class {
|
|
|
2028
2382
|
* so a cleared session can't be resurrected by an already-queued prompt.
|
|
2029
2383
|
*/
|
|
2030
2384
|
sessionGenerations = /* @__PURE__ */ new Map();
|
|
2385
|
+
pendingClears = /* @__PURE__ */ new Map();
|
|
2031
2386
|
/** Per-session active prompt tracking for dispatch modes. */
|
|
2032
2387
|
activePrompts = /* @__PURE__ */ new Map();
|
|
2033
2388
|
/** Per-session message buffer for collect mode. */
|
|
2034
2389
|
collectBuffers = /* @__PURE__ */ new Map();
|
|
2390
|
+
preflightedEnvelopes = /* @__PURE__ */ new WeakSet();
|
|
2035
2391
|
bridgeToolCallListener = /* @__PURE__ */ __name((event) => {
|
|
2036
2392
|
this.dispatchToolCall(event);
|
|
2037
2393
|
}, "bridgeToolCallListener");
|
|
2038
2394
|
bridgeSessionDiedListener = /* @__PURE__ */ __name((event) => {
|
|
2039
2395
|
this.onSessionDied(event.sessionId);
|
|
2040
2396
|
}, "bridgeSessionDiedListener");
|
|
2397
|
+
channelLoopToolHandler = {
|
|
2398
|
+
canHandle: /* @__PURE__ */ __name((sessionId) => this.router.getTarget(sessionId)?.channelName === this.name, "canHandle"),
|
|
2399
|
+
create: /* @__PURE__ */ __name((sessionId, input) => this.createLoopFromTool(sessionId, input), "create"),
|
|
2400
|
+
list: /* @__PURE__ */ __name((sessionId) => this.listLoopsFromTool(sessionId), "list"),
|
|
2401
|
+
cancel: /* @__PURE__ */ __name((sessionId, id) => this.cancelLoopFromTool(sessionId, id), "cancel")
|
|
2402
|
+
};
|
|
2041
2403
|
dispatchToolCall(event) {
|
|
2042
2404
|
const target = this.router.getTarget(event.sessionId);
|
|
2043
2405
|
const active = this.activePrompts.get(event.sessionId);
|
|
@@ -2069,6 +2431,7 @@ var ChannelBase = class {
|
|
|
2069
2431
|
this.identity = Object.freeze(this.resolveIdentity(name, config));
|
|
2070
2432
|
this.memoryScope = Object.freeze(this.resolveMemoryScope(name, config));
|
|
2071
2433
|
this.channelMemory = options?.channelMemory;
|
|
2434
|
+
this.memoryIntentClassifier = options?.memoryIntentClassifier;
|
|
2072
2435
|
this.groupHistory = new GroupHistoryStore(options?.groupHistoryPath ?? join4(getGlobalQwenDir(), "channels", `${encodeURIComponent(name)}-group-history.jsonl`));
|
|
2073
2436
|
this.loopController = options?.loopController;
|
|
2074
2437
|
this.groupGate = new GroupGate(config.groupPolicy, config.groups);
|
|
@@ -2076,6 +2439,9 @@ var ChannelBase = class {
|
|
|
2076
2439
|
this.gate = new SenderGate(config.senderPolicy, config.allowedUsers, pairingStore);
|
|
2077
2440
|
this.router = options?.router || new SessionRouter(bridge, config.cwd, config.sessionScope);
|
|
2078
2441
|
this.registerSharedCommands();
|
|
2442
|
+
if (this.loopController) {
|
|
2443
|
+
bridge.registerChannelLoopToolHandler?.(this.channelLoopToolHandler);
|
|
2444
|
+
}
|
|
2079
2445
|
this.registerBridgeEvents = options?.registerBridgeEvents ?? !options?.router;
|
|
2080
2446
|
if (this.registerBridgeEvents) {
|
|
2081
2447
|
this.attachBridgeEvents(bridge);
|
|
@@ -2190,6 +2556,9 @@ var ChannelBase = class {
|
|
|
2190
2556
|
}
|
|
2191
2557
|
this.router.setBridge(bridge);
|
|
2192
2558
|
this.bridge = bridge;
|
|
2559
|
+
if (this.loopController) {
|
|
2560
|
+
bridge.registerChannelLoopToolHandler?.(this.channelLoopToolHandler);
|
|
2561
|
+
}
|
|
2193
2562
|
if (this.registerBridgeEvents) {
|
|
2194
2563
|
this.attachBridgeEvents(bridge);
|
|
2195
2564
|
}
|
|
@@ -2212,7 +2581,7 @@ var ChannelBase = class {
|
|
|
2212
2581
|
await this.loopController?.disable(job.id);
|
|
2213
2582
|
throw new Error(`Loop ${job.id} target is no longer authorized.`);
|
|
2214
2583
|
}
|
|
2215
|
-
const sessionId = await this.router.resolve(this.name, job.target.senderId, job.target.chatId, job.target.threadId, job.cwd);
|
|
2584
|
+
const sessionId = await this.router.resolve(this.name, job.target.senderId, job.target.chatId, job.target.threadId, job.cwd, job.target.isGroup);
|
|
2216
2585
|
const label = sanitizeQuotedText(job.label || job.id, 80);
|
|
2217
2586
|
const createdBy = sanitizeSenderName(job.createdBy || "unknown");
|
|
2218
2587
|
let promptText = `[Loop "${label}" created by ${createdBy}] Scheduled task running unattended: no one is present to answer questions, and your final response is delivered to this chat automatically \u2014 do whatever work the task requires, then put the result in your final response instead of trying to deliver it to this chat yourself.
|
|
@@ -2286,6 +2655,8 @@ ${promptText}`;
|
|
|
2286
2655
|
resolve: doneResolve,
|
|
2287
2656
|
chatId: job.target.chatId,
|
|
2288
2657
|
messageId: job.id,
|
|
2658
|
+
senderId: job.target.senderId,
|
|
2659
|
+
senderName: job.createdBy,
|
|
2289
2660
|
loopPrompt: true
|
|
2290
2661
|
};
|
|
2291
2662
|
this.activePrompts.set(sessionId, promptState);
|
|
@@ -2396,6 +2767,7 @@ ${promptText}`;
|
|
|
2396
2767
|
const lost = buffer.length;
|
|
2397
2768
|
const coalesced = buffer.map((b) => b.text).join("\n\n");
|
|
2398
2769
|
const lastEnvelope = buffer[buffer.length - 1].envelope;
|
|
2770
|
+
this.notifyPromptBufferDrained(lastEnvelope.chatId, sessionId, buffer);
|
|
2399
2771
|
const syntheticEnvelope = {
|
|
2400
2772
|
...lastEnvelope,
|
|
2401
2773
|
text: coalesced,
|
|
@@ -2405,7 +2777,8 @@ ${promptText}`;
|
|
|
2405
2777
|
imageBase64: void 0,
|
|
2406
2778
|
imageMimeType: void 0
|
|
2407
2779
|
};
|
|
2408
|
-
this.
|
|
2780
|
+
this.markPreflighted(syntheticEnvelope);
|
|
2781
|
+
this.processInbound(syntheticEnvelope).catch((err) => {
|
|
2409
2782
|
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)}
|
|
2410
2783
|
`);
|
|
2411
2784
|
});
|
|
@@ -2495,11 +2868,39 @@ ${promptText}`;
|
|
|
2495
2868
|
}
|
|
2496
2869
|
active.cancelled = true;
|
|
2497
2870
|
this.stopActiveStreaming(active, sessionId, reason);
|
|
2498
|
-
this.
|
|
2871
|
+
this.dropCollectBuffer(sessionId);
|
|
2499
2872
|
this.emitTaskCancellation(active, sessionId, reason);
|
|
2500
2873
|
return true;
|
|
2501
2874
|
});
|
|
2502
2875
|
}
|
|
2876
|
+
dropCollectBuffer(sessionId) {
|
|
2877
|
+
const buffer = this.collectBuffers.get(sessionId);
|
|
2878
|
+
if (!buffer)
|
|
2879
|
+
return;
|
|
2880
|
+
this.collectBuffers.delete(sessionId);
|
|
2881
|
+
const chatId = buffer[0]?.envelope.chatId ?? "";
|
|
2882
|
+
const messageIds = this.collectBufferMessageIds(buffer);
|
|
2883
|
+
try {
|
|
2884
|
+
this.onPromptBufferDropped(chatId, sessionId, messageIds);
|
|
2885
|
+
} catch (err) {
|
|
2886
|
+
process.stderr.write(`[${this.name}] onPromptBufferDropped threw for session ${sessionId}: ${err instanceof Error ? err.message : err}
|
|
2887
|
+
`);
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
notifyPromptBufferDrained(chatId, sessionId, buffer) {
|
|
2891
|
+
const messageIds = this.collectBufferMessageIds(buffer);
|
|
2892
|
+
if (messageIds.length === 0)
|
|
2893
|
+
return;
|
|
2894
|
+
try {
|
|
2895
|
+
this.onPromptBufferDrained(chatId, sessionId, messageIds);
|
|
2896
|
+
} catch (err) {
|
|
2897
|
+
process.stderr.write(`[${this.name}] onPromptBufferDrained threw for session ${sessionId}: ${err instanceof Error ? err.message : err}
|
|
2898
|
+
`);
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
collectBufferMessageIds(buffer) {
|
|
2902
|
+
return buffer.map((entry) => entry.envelope.messageId).filter((id) => typeof id === "string" && id.length > 0);
|
|
2903
|
+
}
|
|
2503
2904
|
logCancelSessionFailure(sessionId, err) {
|
|
2504
2905
|
process.stderr.write(`[${sanitizeLogText(this.name, 64)}] cancelSession failed for session=${sanitizeLogText(sessionId, 64)}: ${this.lifecycleError(err)}
|
|
2505
2906
|
`);
|
|
@@ -2545,6 +2946,12 @@ ${promptText}`;
|
|
|
2545
2946
|
*/
|
|
2546
2947
|
onPromptStart(_chatId, _sessionId, _messageId) {
|
|
2547
2948
|
}
|
|
2949
|
+
onPromptBuffered(_chatId, _sessionId, _messageId) {
|
|
2950
|
+
}
|
|
2951
|
+
onPromptBufferDrained(_chatId, _sessionId, _messageIds) {
|
|
2952
|
+
}
|
|
2953
|
+
onPromptBufferDropped(_chatId, _sessionId, _messageIds) {
|
|
2954
|
+
}
|
|
2548
2955
|
/**
|
|
2549
2956
|
* Called when a prompt finishes (response sent or cancelled).
|
|
2550
2957
|
* Override to hide the working indicator.
|
|
@@ -2609,7 +3016,7 @@ ${promptText}`;
|
|
|
2609
3016
|
}
|
|
2610
3017
|
this.sessionGenerations.set(id, (this.sessionGenerations.get(id) ?? 0) + 1);
|
|
2611
3018
|
const active = this.activePrompts.get(id);
|
|
2612
|
-
this.
|
|
3019
|
+
this.dropCollectBuffer(id);
|
|
2613
3020
|
if (active) {
|
|
2614
3021
|
const settled = await this.cancelAndAwaitActive(active, id);
|
|
2615
3022
|
if (!settled) {
|
|
@@ -2661,77 +3068,20 @@ ${promptText}`;
|
|
|
2661
3068
|
this.registerCommand("clear", clearHandler);
|
|
2662
3069
|
this.registerCommand("reset", clearHandler);
|
|
2663
3070
|
this.registerCommand("new", clearHandler);
|
|
2664
|
-
this.registerCommand("who", async (envelope) => {
|
|
2665
|
-
if (!this.isAuthorizedForSharedSession(envelope)) {
|
|
2666
|
-
await this.sendMessage(envelope.chatId, "Only authorized members can view this shared session.");
|
|
2667
|
-
return true;
|
|
2668
|
-
}
|
|
2669
|
-
const active = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
2670
|
-
const scopeNote = this.config.sessionScope === "single" ? " (shared channel-wide)" : this.isSharedSession(envelope) ? envelope.isGroup ? " (shared by this group)" : "" : envelope.isGroup ? " (private to you)" : "";
|
|
2671
|
-
await this.sendMessage(envelope.chatId, [
|
|
2672
|
-
`Channel: ${this.name}`,
|
|
2673
|
-
// Identity/memory lines only for channels that opted in — keep
|
|
2674
|
-
// unconfigured channels' output unchanged.
|
|
2675
|
-
...this.shouldPrependChannelBoundaryPrompt() ? [
|
|
2676
|
-
`Identity: ${sanitizeQuotedText(this.identity.displayName, 128)}`,
|
|
2677
|
-
`Memory: ${sanitizeQuotedText(this.memoryScope.namespace, 128)}`
|
|
2678
|
-
] : [],
|
|
2679
|
-
// Only the basename — don't leak the absolute cwd to group members.
|
|
2680
|
-
`Workspace: ${basename(this.config.cwd)}`,
|
|
2681
|
-
`Session: ${active ? "active" : "none"}${scopeNote}`
|
|
2682
|
-
].join("\n"));
|
|
2683
|
-
return true;
|
|
2684
|
-
});
|
|
2685
3071
|
this.registerCommand("remember-channel", async (envelope, args) => {
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
}
|
|
2689
|
-
if (envelope.isGroup) {
|
|
2690
|
-
await this.sendMessage(envelope.chatId, "Channel memory cannot be changed in group chats.");
|
|
2691
|
-
return true;
|
|
2692
|
-
}
|
|
2693
|
-
if (args.trim() === "") {
|
|
3072
|
+
const text = args.trim();
|
|
3073
|
+
if (text === "") {
|
|
2694
3074
|
await this.sendMessage(envelope.chatId, "Usage: /remember-channel <text>");
|
|
2695
3075
|
return true;
|
|
2696
3076
|
}
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
}
|
|
2701
|
-
try {
|
|
2702
|
-
await channelMemory.appendChannelMemory(this.channelMemoryTarget(envelope), args.trim());
|
|
2703
|
-
} catch (error) {
|
|
2704
|
-
const message = this.channelMemoryErrorMessage(error);
|
|
2705
|
-
this.logChannelMemoryError("save", envelope, message);
|
|
2706
|
-
await this.sendMessage(envelope.chatId, `Failed to save channel memory: ${this.channelMemoryUserErrorMessage()}`);
|
|
2707
|
-
return true;
|
|
2708
|
-
}
|
|
2709
|
-
this.invalidateSessionContext(envelope);
|
|
2710
|
-
await this.sendMessage(envelope.chatId, "Channel memory updated.");
|
|
3077
|
+
await this.handleChannelMemoryIntent(envelope, {
|
|
3078
|
+
kind: "remember",
|
|
3079
|
+
text
|
|
3080
|
+
});
|
|
2711
3081
|
return true;
|
|
2712
3082
|
});
|
|
2713
3083
|
this.registerCommand("channel-memory", async (envelope) => {
|
|
2714
|
-
|
|
2715
|
-
return true;
|
|
2716
|
-
}
|
|
2717
|
-
if (envelope.isGroup) {
|
|
2718
|
-
await this.sendMessage(envelope.chatId, "Channel memory cannot be shown in group chats.");
|
|
2719
|
-
return true;
|
|
2720
|
-
}
|
|
2721
|
-
const channelMemory = await this.getChannelMemory(envelope);
|
|
2722
|
-
if (!channelMemory) {
|
|
2723
|
-
return true;
|
|
2724
|
-
}
|
|
2725
|
-
let text;
|
|
2726
|
-
try {
|
|
2727
|
-
text = (await channelMemory.readChannelMemory(this.channelMemoryTarget(envelope))).trim();
|
|
2728
|
-
} catch (error) {
|
|
2729
|
-
const message = this.channelMemoryErrorMessage(error);
|
|
2730
|
-
this.logChannelMemoryError("read", envelope, message);
|
|
2731
|
-
await this.sendMessage(envelope.chatId, `Failed to read channel memory: ${this.channelMemoryUserErrorMessage()}`);
|
|
2732
|
-
return true;
|
|
2733
|
-
}
|
|
2734
|
-
await this.sendMessage(envelope.chatId, text === "" ? "No channel memory saved." : sanitizePromptText(text));
|
|
3084
|
+
await this.handleChannelMemoryIntent(envelope, { kind: "list" });
|
|
2735
3085
|
return true;
|
|
2736
3086
|
});
|
|
2737
3087
|
this.registerCommand("forget-channel", async (envelope, args) => {
|
|
@@ -2746,21 +3096,28 @@ ${promptText}`;
|
|
|
2746
3096
|
await this.sendMessage(envelope.chatId, 'This clears channel memory for this chat. Re-send with "confirm" (e.g. /forget-channel confirm) to proceed.');
|
|
2747
3097
|
return true;
|
|
2748
3098
|
}
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
result = await channelMemory.clearChannelMemory(this.channelMemoryTarget(envelope));
|
|
2756
|
-
} catch (error) {
|
|
2757
|
-
const message = this.channelMemoryErrorMessage(error);
|
|
2758
|
-
this.logChannelMemoryError("clear", envelope, message);
|
|
2759
|
-
await this.sendMessage(envelope.chatId, `Failed to clear channel memory: ${this.channelMemoryUserErrorMessage()}`);
|
|
3099
|
+
await this.handleChannelMemoryIntent(envelope, { kind: "clear_confirm" }, { skipPendingClear: true });
|
|
3100
|
+
return true;
|
|
3101
|
+
});
|
|
3102
|
+
this.registerCommand("who", async (envelope) => {
|
|
3103
|
+
if (!this.isAuthorizedForSharedSession(envelope)) {
|
|
3104
|
+
await this.sendMessage(envelope.chatId, "Only authorized members can view this shared session.");
|
|
2760
3105
|
return true;
|
|
2761
3106
|
}
|
|
2762
|
-
this.
|
|
2763
|
-
|
|
3107
|
+
const active = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
3108
|
+
const scopeNote = this.config.sessionScope === "single" ? " (shared channel-wide)" : this.isSharedSession(envelope) ? envelope.isGroup ? " (shared by this group)" : "" : envelope.isGroup ? " (private to you)" : "";
|
|
3109
|
+
await this.sendMessage(envelope.chatId, [
|
|
3110
|
+
`Channel: ${this.name}`,
|
|
3111
|
+
// Identity/memory lines only for channels that opted in — keep
|
|
3112
|
+
// unconfigured channels' output unchanged.
|
|
3113
|
+
...this.shouldPrependChannelBoundaryPrompt() ? [
|
|
3114
|
+
`Identity: ${sanitizeQuotedText(this.identity.displayName, 128)}`,
|
|
3115
|
+
`Memory: ${sanitizeQuotedText(this.memoryScope.namespace, 128)}`
|
|
3116
|
+
] : [],
|
|
3117
|
+
// Only the basename — don't leak the absolute cwd to group members.
|
|
3118
|
+
`Workspace: ${basename(this.config.cwd)}`,
|
|
3119
|
+
`Session: ${active ? "active" : "none"}${scopeNote}`
|
|
3120
|
+
].join("\n"));
|
|
2764
3121
|
return true;
|
|
2765
3122
|
});
|
|
2766
3123
|
this.registerCommand("help", async (envelope) => {
|
|
@@ -2769,21 +3126,18 @@ ${promptText}`;
|
|
|
2769
3126
|
"/help \u2014 Show this help",
|
|
2770
3127
|
this.isSharedSession(envelope) ? "/clear confirm \u2014 Clear the shared session (aliases: /reset, /new)" : "/clear \u2014 Clear your session (aliases: /reset, /new)",
|
|
2771
3128
|
"/who \u2014 Show current session & workspace",
|
|
2772
|
-
"/status \u2014 Show session info"
|
|
2773
|
-
"/remember-channel <text> \u2014 Save memory for this chat",
|
|
2774
|
-
"/channel-memory \u2014 Show memory for this chat",
|
|
2775
|
-
"/forget-channel confirm \u2014 Clear memory for this chat"
|
|
3129
|
+
"/status \u2014 Show session info"
|
|
2776
3130
|
];
|
|
2777
3131
|
const sharedCmds = /* @__PURE__ */ new Set([
|
|
2778
3132
|
"help",
|
|
2779
3133
|
"clear",
|
|
2780
3134
|
"reset",
|
|
2781
3135
|
"new",
|
|
2782
|
-
"who",
|
|
2783
|
-
"status",
|
|
2784
3136
|
"remember-channel",
|
|
2785
3137
|
"channel-memory",
|
|
2786
|
-
"forget-channel"
|
|
3138
|
+
"forget-channel",
|
|
3139
|
+
"who",
|
|
3140
|
+
"status"
|
|
2787
3141
|
]);
|
|
2788
3142
|
const platformCmds = [...this.commands.keys()].filter((c) => !sharedCmds.has(c));
|
|
2789
3143
|
if (platformCmds.length > 0) {
|
|
@@ -2906,6 +3260,100 @@ ${promptText}`;
|
|
|
2906
3260
|
await this.sendMessage(envelope.chatId, `Loop ${job.id}: ${job.cron}`);
|
|
2907
3261
|
return true;
|
|
2908
3262
|
}
|
|
3263
|
+
async createLoopFromTool(sessionId, input) {
|
|
3264
|
+
if (!this.loopController) {
|
|
3265
|
+
return { text: "Channel loops are not configured.", isError: true };
|
|
3266
|
+
}
|
|
3267
|
+
if (!this.supportsProactiveSend()) {
|
|
3268
|
+
return {
|
|
3269
|
+
text: "This channel does not support proactive loop messages.",
|
|
3270
|
+
isError: true
|
|
3271
|
+
};
|
|
3272
|
+
}
|
|
3273
|
+
if (this.config.sessionScope === "single") {
|
|
3274
|
+
return {
|
|
3275
|
+
text: "Loops are not supported when sessionScope is single.",
|
|
3276
|
+
isError: true
|
|
3277
|
+
};
|
|
3278
|
+
}
|
|
3279
|
+
const target = this.loopToolTarget(sessionId);
|
|
3280
|
+
if (typeof target === "string")
|
|
3281
|
+
return { text: target, isError: true };
|
|
3282
|
+
if (!this.supportsProactiveTarget(target)) {
|
|
3283
|
+
return {
|
|
3284
|
+
text: "This channel does not support proactive loop messages for this chat target.",
|
|
3285
|
+
isError: true
|
|
3286
|
+
};
|
|
3287
|
+
}
|
|
3288
|
+
const cron = input.cron.trim();
|
|
3289
|
+
try {
|
|
3290
|
+
this.loopController.validateCron(cron);
|
|
3291
|
+
} catch (err) {
|
|
3292
|
+
return {
|
|
3293
|
+
text: `Invalid cron expression: ${err instanceof Error ? err.message : String(err)}`,
|
|
3294
|
+
isError: true
|
|
3295
|
+
};
|
|
3296
|
+
}
|
|
3297
|
+
const prompt = sanitizePromptText(input.prompt.trim());
|
|
3298
|
+
if (Array.from(prompt).length > MAX_LOOP_PROMPT_CHARS) {
|
|
3299
|
+
return {
|
|
3300
|
+
text: `Loop prompt is too long; keep it under ${MAX_LOOP_PROMPT_CHARS} characters.`,
|
|
3301
|
+
isError: true
|
|
3302
|
+
};
|
|
3303
|
+
}
|
|
3304
|
+
const loopInput = {
|
|
3305
|
+
channelName: this.name,
|
|
3306
|
+
target,
|
|
3307
|
+
cwd: this.config.cwd,
|
|
3308
|
+
cron,
|
|
3309
|
+
prompt,
|
|
3310
|
+
label: truncateLoopLabel(prompt),
|
|
3311
|
+
recurring: input.recurring !== false,
|
|
3312
|
+
createdBy: sanitizeSenderName(this.toolCallerName(sessionId, target))
|
|
3313
|
+
};
|
|
3314
|
+
let job;
|
|
3315
|
+
if (this.loopController.createForTarget) {
|
|
3316
|
+
job = await this.loopController.createForTarget(loopInput, MAX_LOOP_JOBS_PER_TARGET);
|
|
3317
|
+
} else {
|
|
3318
|
+
const existingJobs = await this.loopController.listForTarget(this.name, target);
|
|
3319
|
+
if (existingJobs.filter((existingJob) => existingJob.enabled).length < MAX_LOOP_JOBS_PER_TARGET) {
|
|
3320
|
+
job = await this.loopController.create(loopInput);
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
if (!job) {
|
|
3324
|
+
return {
|
|
3325
|
+
text: "Too many loops for this chat. Cancel an existing loop before adding another.",
|
|
3326
|
+
isError: true
|
|
3327
|
+
};
|
|
3328
|
+
}
|
|
3329
|
+
return `Loop ${job.id}: ${job.cron}`;
|
|
3330
|
+
}
|
|
3331
|
+
async listLoopsFromTool(sessionId) {
|
|
3332
|
+
if (!this.loopController) {
|
|
3333
|
+
return { text: "Channel loops are not configured.", isError: true };
|
|
3334
|
+
}
|
|
3335
|
+
const target = this.loopToolTarget(sessionId);
|
|
3336
|
+
if (typeof target === "string")
|
|
3337
|
+
return { text: target, isError: true };
|
|
3338
|
+
const jobs = await this.loopController.listForTarget(this.name, target);
|
|
3339
|
+
if (jobs.length === 0)
|
|
3340
|
+
return "No loops.";
|
|
3341
|
+
return jobs.map((job) => this.formatLoopListLine(job)).join("\n");
|
|
3342
|
+
}
|
|
3343
|
+
async cancelLoopFromTool(sessionId, id) {
|
|
3344
|
+
if (!this.loopController) {
|
|
3345
|
+
return { text: "Channel loops are not configured.", isError: true };
|
|
3346
|
+
}
|
|
3347
|
+
const target = this.loopToolTarget(sessionId);
|
|
3348
|
+
if (typeof target === "string")
|
|
3349
|
+
return { text: target, isError: true };
|
|
3350
|
+
const jobs = await this.loopController.listForTarget(this.name, target);
|
|
3351
|
+
const match = jobs.find((job) => job.id === id);
|
|
3352
|
+
if (!match)
|
|
3353
|
+
return { text: `No loop ${id}.`, isError: true };
|
|
3354
|
+
const disabled = await this.loopController.disable(id);
|
|
3355
|
+
return disabled ? `Cancelled loop ${id}.` : { text: `Failed to cancel loop ${id}.`, isError: true };
|
|
3356
|
+
}
|
|
2909
3357
|
async handleLoopList(envelope) {
|
|
2910
3358
|
if (!this.loopController)
|
|
2911
3359
|
return true;
|
|
@@ -2986,35 +3434,55 @@ ${promptText}`;
|
|
|
2986
3434
|
}
|
|
2987
3435
|
const jobs = await this.loopController.listForTarget(this.name, this.loopTargetFromEnvelope(envelope));
|
|
2988
3436
|
const match = jobs.find((job) => job.id === id);
|
|
2989
|
-
|
|
2990
|
-
|
|
3437
|
+
if (!match) {
|
|
3438
|
+
await this.sendMessage(envelope.chatId, `No loop ${id}.`);
|
|
3439
|
+
return true;
|
|
3440
|
+
}
|
|
3441
|
+
const disabled = await this.loopController.disable(id);
|
|
3442
|
+
await this.sendMessage(envelope.chatId, disabled ? `Cancelled loop ${id}.` : `Failed to cancel loop ${id}.`);
|
|
2991
3443
|
return true;
|
|
2992
3444
|
}
|
|
2993
3445
|
loopTargetFromEnvelope(envelope) {
|
|
2994
|
-
return {
|
|
3446
|
+
return this.normalizeLoopTarget({
|
|
2995
3447
|
channelName: this.name,
|
|
2996
3448
|
senderId: envelope.senderId,
|
|
2997
3449
|
chatId: envelope.chatId,
|
|
2998
3450
|
threadId: envelope.threadId,
|
|
2999
3451
|
isGroup: envelope.isGroup === true
|
|
3000
|
-
};
|
|
3452
|
+
});
|
|
3001
3453
|
}
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3454
|
+
normalizeLoopTarget(target) {
|
|
3455
|
+
return { ...target, isGroup: target.isGroup === true };
|
|
3456
|
+
}
|
|
3457
|
+
loopToolTarget(sessionId) {
|
|
3458
|
+
const target = this.router.getTarget(sessionId);
|
|
3459
|
+
if (!target || target.channelName !== this.name) {
|
|
3460
|
+
return "No channel target is bound to this session.";
|
|
3005
3461
|
}
|
|
3462
|
+
if (!this.isAuthorizedForSharedSessionToolCall(target, sessionId)) {
|
|
3463
|
+
return "Only authorized members can use loops in this shared session.";
|
|
3464
|
+
}
|
|
3465
|
+
const senderId = this.activePrompts.get(sessionId)?.senderId;
|
|
3466
|
+
const normalizedTarget = this.normalizeLoopTarget(target);
|
|
3467
|
+
if (senderId && this.isSharedSessionTarget(normalizedTarget)) {
|
|
3468
|
+
return { ...normalizedTarget, senderId };
|
|
3469
|
+
}
|
|
3470
|
+
return normalizedTarget;
|
|
3471
|
+
}
|
|
3472
|
+
isStoredLoopTargetAuthorized(target, senderName) {
|
|
3473
|
+
const normalizedTarget = this.normalizeLoopTarget(target);
|
|
3006
3474
|
const envelope = {
|
|
3007
3475
|
channelName: this.name,
|
|
3008
|
-
senderId:
|
|
3476
|
+
senderId: normalizedTarget.senderId,
|
|
3009
3477
|
senderName,
|
|
3010
|
-
chatId:
|
|
3478
|
+
chatId: normalizedTarget.chatId,
|
|
3011
3479
|
text: "",
|
|
3012
|
-
threadId:
|
|
3013
|
-
isGroup:
|
|
3480
|
+
threadId: normalizedTarget.threadId,
|
|
3481
|
+
isGroup: normalizedTarget.isGroup,
|
|
3014
3482
|
isMentioned: true,
|
|
3015
3483
|
isReplyToBot: true
|
|
3016
3484
|
};
|
|
3017
|
-
return this.groupGate.check(envelope).allowed && this.gate.isAllowed(
|
|
3485
|
+
return this.groupGate.check(envelope).allowed && this.gate.isAllowed(normalizedTarget.senderId) && this.isAuthorizedForSharedSession(envelope);
|
|
3018
3486
|
}
|
|
3019
3487
|
/** Check if a message text matches a registered local command. */
|
|
3020
3488
|
isLocalCommand(text) {
|
|
@@ -3067,6 +3535,118 @@ ${promptText}`;
|
|
|
3067
3535
|
}
|
|
3068
3536
|
return this.channelMemory;
|
|
3069
3537
|
}
|
|
3538
|
+
async handleChannelMemoryIntent(envelope, intent, options = {}) {
|
|
3539
|
+
if (!await this.ensureChannelMemoryAuthorized(envelope)) {
|
|
3540
|
+
return;
|
|
3541
|
+
}
|
|
3542
|
+
if (envelope.isGroup && intent.kind !== "list") {
|
|
3543
|
+
await this.sendMessage(envelope.chatId, "Channel memory cannot be changed in group chats.");
|
|
3544
|
+
return;
|
|
3545
|
+
}
|
|
3546
|
+
if (intent.kind === "clear_request") {
|
|
3547
|
+
this.setPendingClear(this.clearPendingKey(envelope));
|
|
3548
|
+
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.');
|
|
3549
|
+
return;
|
|
3550
|
+
}
|
|
3551
|
+
const channelMemory = await this.getChannelMemory(envelope);
|
|
3552
|
+
if (!channelMemory) {
|
|
3553
|
+
return;
|
|
3554
|
+
}
|
|
3555
|
+
if (intent.kind === "remember") {
|
|
3556
|
+
try {
|
|
3557
|
+
await channelMemory.appendChannelMemory(this.channelMemoryTarget(envelope), intent.text);
|
|
3558
|
+
} catch (error) {
|
|
3559
|
+
const message = this.channelMemoryErrorMessage(error);
|
|
3560
|
+
this.logChannelMemoryError("save", envelope, message);
|
|
3561
|
+
await this.sendMessage(envelope.chatId, `Failed to save channel memory: ${this.channelMemoryUserErrorMessage()}`);
|
|
3562
|
+
return;
|
|
3563
|
+
}
|
|
3564
|
+
this.invalidateSessionContext(envelope);
|
|
3565
|
+
await this.sendMessage(envelope.chatId, "Channel memory updated.");
|
|
3566
|
+
return;
|
|
3567
|
+
}
|
|
3568
|
+
if (intent.kind === "list") {
|
|
3569
|
+
let text;
|
|
3570
|
+
try {
|
|
3571
|
+
text = (await channelMemory.readChannelMemory(this.channelMemoryTarget(envelope))).trim();
|
|
3572
|
+
} catch (error) {
|
|
3573
|
+
const message = this.channelMemoryErrorMessage(error);
|
|
3574
|
+
this.logChannelMemoryError("read", envelope, message);
|
|
3575
|
+
await this.sendMessage(envelope.chatId, `Failed to read channel memory: ${this.channelMemoryUserErrorMessage()}`);
|
|
3576
|
+
return;
|
|
3577
|
+
}
|
|
3578
|
+
await this.sendMessage(envelope.chatId, text === "" ? "No channel memory saved." : sanitizePromptText(text));
|
|
3579
|
+
return;
|
|
3580
|
+
}
|
|
3581
|
+
if (intent.kind === "clear_confirm") {
|
|
3582
|
+
if (!options.skipPendingClear) {
|
|
3583
|
+
const pendingKey = this.clearPendingKey(envelope);
|
|
3584
|
+
const expiresAt = this.pendingClears.get(pendingKey);
|
|
3585
|
+
this.pendingClears.delete(pendingKey);
|
|
3586
|
+
if (expiresAt === void 0 || expiresAt < Date.now()) {
|
|
3587
|
+
await this.sendMessage(envelope.chatId, 'No pending clear request. Say "\u6E05\u7A7A\u8BB0\u5FC6" first.');
|
|
3588
|
+
return;
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
let result;
|
|
3592
|
+
try {
|
|
3593
|
+
result = await channelMemory.clearChannelMemory(this.channelMemoryTarget(envelope));
|
|
3594
|
+
} catch (error) {
|
|
3595
|
+
const message = this.channelMemoryErrorMessage(error);
|
|
3596
|
+
this.logChannelMemoryError("clear", envelope, message);
|
|
3597
|
+
await this.sendMessage(envelope.chatId, `Failed to clear channel memory: ${this.channelMemoryUserErrorMessage()}`);
|
|
3598
|
+
return;
|
|
3599
|
+
}
|
|
3600
|
+
this.invalidateSessionContext(envelope);
|
|
3601
|
+
await this.sendMessage(envelope.chatId, result.changed ? "Channel memory cleared." : "No channel memory saved.");
|
|
3602
|
+
return;
|
|
3603
|
+
}
|
|
3604
|
+
const unhandled = intent;
|
|
3605
|
+
throw new Error(`Unhandled channel memory intent: ${JSON.stringify(unhandled)}`);
|
|
3606
|
+
}
|
|
3607
|
+
shouldClassifyChannelMemoryIntent(text) {
|
|
3608
|
+
const normalized = text.replace(PROMPT_UNSAFE_INVISIBLES, "").trim();
|
|
3609
|
+
return this.channelMemory !== void 0 && this.memoryIntentClassifier !== void 0 && !normalized.startsWith("/") && CHANNEL_MEMORY_CLASSIFIER_TRIGGER_RE.test(normalized);
|
|
3610
|
+
}
|
|
3611
|
+
clearPendingKey(envelope) {
|
|
3612
|
+
return `${this.name}:${envelope.chatId}:${envelope.threadId ?? ""}:${envelope.senderId ?? ""}`;
|
|
3613
|
+
}
|
|
3614
|
+
setPendingClear(key) {
|
|
3615
|
+
const now = Date.now();
|
|
3616
|
+
for (const [pendingKey, expiresAt] of this.pendingClears) {
|
|
3617
|
+
if (expiresAt < now) {
|
|
3618
|
+
this.pendingClears.delete(pendingKey);
|
|
3619
|
+
}
|
|
3620
|
+
}
|
|
3621
|
+
this.pendingClears.set(key, now + 6e4);
|
|
3622
|
+
}
|
|
3623
|
+
async classifyChannelMemoryIntent(text) {
|
|
3624
|
+
if (!this.memoryIntentClassifier) {
|
|
3625
|
+
return null;
|
|
3626
|
+
}
|
|
3627
|
+
let classified;
|
|
3628
|
+
try {
|
|
3629
|
+
classified = await this.memoryIntentClassifier.classifyChannelMemoryIntent(text);
|
|
3630
|
+
} catch (error) {
|
|
3631
|
+
process.stderr.write(`[${this.name}] channel memory intent classifier failed: ${sanitizeLogText(this.channelMemoryErrorMessage(error), 200)}
|
|
3632
|
+
`);
|
|
3633
|
+
return null;
|
|
3634
|
+
}
|
|
3635
|
+
if (classified.confidence < CHANNEL_MEMORY_CLASSIFIER_MIN_CONFIDENCE) {
|
|
3636
|
+
return null;
|
|
3637
|
+
}
|
|
3638
|
+
if (classified.intent === "remember") {
|
|
3639
|
+
const memory = classified.memory?.trim();
|
|
3640
|
+
return memory ? { kind: "remember", text: memory } : null;
|
|
3641
|
+
}
|
|
3642
|
+
if (classified.intent === "list") {
|
|
3643
|
+
return { kind: "list" };
|
|
3644
|
+
}
|
|
3645
|
+
if (classified.intent === "clear_all") {
|
|
3646
|
+
return { kind: "clear_request" };
|
|
3647
|
+
}
|
|
3648
|
+
return null;
|
|
3649
|
+
}
|
|
3070
3650
|
channelMemoryErrorMessage(error) {
|
|
3071
3651
|
return error instanceof Error ? error.message : String(error);
|
|
3072
3652
|
}
|
|
@@ -3099,10 +3679,26 @@ ${promptText}`;
|
|
|
3099
3679
|
* gate can't drift; each caller sends its own rejection wording.
|
|
3100
3680
|
*/
|
|
3101
3681
|
isAuthorizedForSharedSession(envelope) {
|
|
3102
|
-
|
|
3682
|
+
return this.isAuthorizedForSharedSessionTarget(envelope);
|
|
3683
|
+
}
|
|
3684
|
+
isAuthorizedForSharedSessionTarget(target) {
|
|
3685
|
+
if (!this.isSharedSessionTarget(target))
|
|
3103
3686
|
return true;
|
|
3104
3687
|
const authorized = this.config.allowedUsers;
|
|
3105
|
-
return authorized.length === 0 || authorized.includes(
|
|
3688
|
+
return authorized.length === 0 || authorized.includes(target.senderId);
|
|
3689
|
+
}
|
|
3690
|
+
isAuthorizedForSharedSessionToolCall(target, sessionId) {
|
|
3691
|
+
if (!this.isSharedSessionTarget(target))
|
|
3692
|
+
return true;
|
|
3693
|
+
const authorized = this.config.allowedUsers;
|
|
3694
|
+
if (authorized.length === 0)
|
|
3695
|
+
return true;
|
|
3696
|
+
const senderId = this.activePrompts.get(sessionId)?.senderId;
|
|
3697
|
+
return senderId !== void 0 && authorized.includes(senderId);
|
|
3698
|
+
}
|
|
3699
|
+
toolCallerName(sessionId, target) {
|
|
3700
|
+
const active = this.activePrompts.get(sessionId);
|
|
3701
|
+
return active?.senderName || active?.senderId || target.senderId || "agent";
|
|
3106
3702
|
}
|
|
3107
3703
|
stopActiveStreaming(active, sessionId, reason) {
|
|
3108
3704
|
try {
|
|
@@ -3311,19 +3907,54 @@ ${formatted.join("\n")}
|
|
|
3311
3907
|
${CURRENT_MESSAGE_MARKER}
|
|
3312
3908
|
${promptText}`;
|
|
3313
3909
|
}
|
|
3314
|
-
|
|
3910
|
+
preflightInbound(envelope) {
|
|
3315
3911
|
const groupResult = this.groupGate.check(envelope);
|
|
3316
3912
|
if (!groupResult.allowed) {
|
|
3317
3913
|
if (groupResult.reason === "mention_required") {
|
|
3318
3914
|
this.recordPendingGroupHistory(envelope);
|
|
3319
3915
|
}
|
|
3320
|
-
return;
|
|
3916
|
+
return false;
|
|
3321
3917
|
}
|
|
3322
3918
|
const result = this.gate.check(envelope.senderId, envelope.senderName);
|
|
3323
3919
|
if (!result.allowed) {
|
|
3324
3920
|
if (result.pairingCode !== void 0) {
|
|
3325
|
-
|
|
3921
|
+
return this.onPairingRequired(envelope.chatId, result.pairingCode).then(() => false).catch((err) => {
|
|
3922
|
+
process.stderr.write(`[Channel:${this.name}] pairing notification failed: ${sanitizeLogText(err instanceof Error ? err.message : String(err), 200)}
|
|
3923
|
+
`);
|
|
3924
|
+
return false;
|
|
3925
|
+
});
|
|
3326
3926
|
}
|
|
3927
|
+
return false;
|
|
3928
|
+
}
|
|
3929
|
+
this.markPreflighted(envelope);
|
|
3930
|
+
return true;
|
|
3931
|
+
}
|
|
3932
|
+
async handleInbound(envelope) {
|
|
3933
|
+
const preflight = this.preflightInbound(envelope);
|
|
3934
|
+
if (!(isPromiseLike(preflight) ? await preflight : preflight))
|
|
3935
|
+
return;
|
|
3936
|
+
await this.processInbound(envelope);
|
|
3937
|
+
}
|
|
3938
|
+
markPreflighted(envelope) {
|
|
3939
|
+
this.preflightedEnvelopes.add(envelope);
|
|
3940
|
+
}
|
|
3941
|
+
/**
|
|
3942
|
+
* Process an inbound message after preflight gates have passed.
|
|
3943
|
+
*
|
|
3944
|
+
* This method does not run group gating, sender allowlisting, or pairing
|
|
3945
|
+
* checks. Callers must run preflightInbound() first unless the envelope was
|
|
3946
|
+
* already preflighted, such as during collect-buffer drain.
|
|
3947
|
+
*/
|
|
3948
|
+
async processInbound(envelope) {
|
|
3949
|
+
if (!this.preflightedEnvelopes.delete(envelope)) {
|
|
3950
|
+
throw new Error("processInbound called without a successful preflightInbound check.");
|
|
3951
|
+
}
|
|
3952
|
+
let memoryIntent = parseChannelMemoryIntent(envelope.text);
|
|
3953
|
+
if (!memoryIntent && this.shouldClassifyChannelMemoryIntent(envelope.text)) {
|
|
3954
|
+
memoryIntent = await this.classifyChannelMemoryIntent(envelope.text);
|
|
3955
|
+
}
|
|
3956
|
+
if (memoryIntent) {
|
|
3957
|
+
await this.handleChannelMemoryIntent(envelope, memoryIntent);
|
|
3327
3958
|
return;
|
|
3328
3959
|
}
|
|
3329
3960
|
const parsed = this.parseCommand(envelope.text);
|
|
@@ -3351,20 +3982,20 @@ ${promptText}`;
|
|
|
3351
3982
|
return;
|
|
3352
3983
|
}
|
|
3353
3984
|
}
|
|
3354
|
-
const sessionId = await this.router.resolve(this.name, envelope.senderId, envelope.chatId, envelope.threadId, this.config.cwd);
|
|
3985
|
+
const sessionId = await this.router.resolve(this.name, envelope.senderId, envelope.chatId, envelope.threadId, this.config.cwd, envelope.isGroup);
|
|
3355
3986
|
if (bangText.startsWith("!")) {
|
|
3356
3987
|
const cmd = bangText.slice(1).trim();
|
|
3357
3988
|
const bridgeShellCommand = this.bridge.shellCommand;
|
|
3358
3989
|
if (cmd && bridgeShellCommand) {
|
|
3359
3990
|
try {
|
|
3360
|
-
const
|
|
3361
|
-
const longestRun = Math.max(0, ...Array.from((
|
|
3991
|
+
const result = await bridgeShellCommand(sessionId, cmd);
|
|
3992
|
+
const longestRun = Math.max(0, ...Array.from((result.output || "").matchAll(/`+/g), (m) => m[0].length));
|
|
3362
3993
|
const fence = "`".repeat(Math.max(3, longestRun + 1));
|
|
3363
|
-
const output =
|
|
3364
|
-
${
|
|
3994
|
+
const output = result.output ? `${fence}
|
|
3995
|
+
${result.output}
|
|
3365
3996
|
${fence}` : "(no output)";
|
|
3366
|
-
const exitLine =
|
|
3367
|
-
Exit code: ${
|
|
3997
|
+
const exitLine = result.exitCode !== null && result.exitCode !== 0 ? `
|
|
3998
|
+
Exit code: ${result.exitCode}` : "";
|
|
3368
3999
|
await this.sendMessage(envelope.chatId, `$ ${cmd}
|
|
3369
4000
|
${output}${exitLine}`);
|
|
3370
4001
|
} catch (error) {
|
|
@@ -3417,6 +4048,12 @@ ${promptText}`;
|
|
|
3417
4048
|
this.collectBuffers.set(sessionId, buffer);
|
|
3418
4049
|
}
|
|
3419
4050
|
buffer.push({ text: promptText, envelope });
|
|
4051
|
+
try {
|
|
4052
|
+
this.onPromptBuffered(envelope.chatId, sessionId, envelope.messageId);
|
|
4053
|
+
} catch (err) {
|
|
4054
|
+
process.stderr.write(`[${this.name}] onPromptBuffered threw for session ${sessionId}: ${err instanceof Error ? err.message : err}
|
|
4055
|
+
`);
|
|
4056
|
+
}
|
|
3420
4057
|
return;
|
|
3421
4058
|
}
|
|
3422
4059
|
case "steer": {
|
|
@@ -3516,7 +4153,9 @@ ${promptToSend}`;
|
|
|
3516
4153
|
done,
|
|
3517
4154
|
resolve: doneResolve,
|
|
3518
4155
|
chatId: envelope.chatId,
|
|
3519
|
-
messageId: envelope.messageId
|
|
4156
|
+
messageId: envelope.messageId,
|
|
4157
|
+
senderId: envelope.senderId,
|
|
4158
|
+
senderName: envelope.senderName
|
|
3520
4159
|
};
|
|
3521
4160
|
this.activePrompts.set(sessionId, promptState);
|
|
3522
4161
|
this.emitTaskLifecycle({
|
|
@@ -3627,6 +4266,7 @@ ${promptToSend}`;
|
|
|
3627
4266
|
const lost = buffer.length;
|
|
3628
4267
|
const coalesced = buffer.map((b) => b.text).join("\n\n");
|
|
3629
4268
|
const lastEnvelope = buffer[buffer.length - 1].envelope;
|
|
4269
|
+
this.notifyPromptBufferDrained(lastEnvelope.chatId, sessionId, buffer);
|
|
3630
4270
|
const syntheticEnvelope = {
|
|
3631
4271
|
...lastEnvelope,
|
|
3632
4272
|
text: coalesced,
|
|
@@ -3638,7 +4278,8 @@ ${promptToSend}`;
|
|
|
3638
4278
|
imageBase64: void 0,
|
|
3639
4279
|
imageMimeType: void 0
|
|
3640
4280
|
};
|
|
3641
|
-
this.
|
|
4281
|
+
this.markPreflighted(syntheticEnvelope);
|
|
4282
|
+
this.processInbound(syntheticEnvelope).catch((err) => {
|
|
3642
4283
|
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)}
|
|
3643
4284
|
`);
|
|
3644
4285
|
});
|
|
@@ -3664,6 +4305,10 @@ function truncateGroupHistoryField(value) {
|
|
|
3664
4305
|
return value.slice(0, GROUP_HISTORY_ENTRY_METADATA_LIMIT);
|
|
3665
4306
|
}
|
|
3666
4307
|
__name(truncateGroupHistoryField, "truncateGroupHistoryField");
|
|
4308
|
+
function isPromiseLike(value) {
|
|
4309
|
+
return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
|
|
4310
|
+
}
|
|
4311
|
+
__name(isPromiseLike, "isPromiseLike");
|
|
3667
4312
|
function truncateLoopLabel(prompt) {
|
|
3668
4313
|
const chars = Array.from(prompt);
|
|
3669
4314
|
return chars.length > 60 ? `${chars.slice(0, 57).join("")}...` : prompt;
|
|
@@ -3809,7 +4454,7 @@ var ChannelLoopStore = class {
|
|
|
3809
4454
|
}
|
|
3810
4455
|
};
|
|
3811
4456
|
function sameTarget(a, b) {
|
|
3812
|
-
return a.channelName === b.channelName && a.senderId === b.senderId && a.chatId === b.chatId && a.threadId === b.threadId
|
|
4457
|
+
return a.channelName === b.channelName && a.senderId === b.senderId && a.chatId === b.chatId && a.threadId === b.threadId;
|
|
3813
4458
|
}
|
|
3814
4459
|
__name(sameTarget, "sameTarget");
|
|
3815
4460
|
function normalizeTarget(target) {
|
|
@@ -3852,12 +4497,12 @@ __name(isTerminalTaskLifecycleType, "isTerminalTaskLifecycleType");
|
|
|
3852
4497
|
export {
|
|
3853
4498
|
resolvePath,
|
|
3854
4499
|
getGlobalQwenDir,
|
|
3855
|
-
AcpBridge,
|
|
3856
|
-
DaemonChannelBridge,
|
|
3857
|
-
PairingStore,
|
|
3858
4500
|
sanitizeSenderName,
|
|
3859
4501
|
sanitizePromptText,
|
|
3860
4502
|
sanitizeLogText,
|
|
4503
|
+
AcpBridge,
|
|
4504
|
+
DaemonChannelBridge,
|
|
4505
|
+
PairingStore,
|
|
3861
4506
|
SessionRouter,
|
|
3862
4507
|
ChannelLoopScheduler,
|
|
3863
4508
|
ChannelBase,
|