@qwen-code/qwen-code 0.19.2 → 0.19.3-nightly.20260630.e00fe6a27
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/extension-creator/SKILL.md +357 -0
- package/bundled/loop/SKILL.md +21 -7
- package/bundled/loop/SKILL.test.ts +41 -0
- package/bundled/loop/loop-task-file.test.ts +1064 -0
- package/bundled/loop/loop-task-file.ts +404 -0
- package/bundled/loop/loop-tick-resolver.test.ts +665 -0
- package/bundled/loop/loop-tick-resolver.ts +347 -0
- package/bundled/qc-helper/SKILL.md +26 -19
- package/bundled/qc-helper/docs/configuration/auth.md +11 -11
- package/bundled/qc-helper/docs/configuration/model-providers.md +13 -13
- package/bundled/qc-helper/docs/configuration/settings.md +76 -75
- package/bundled/qc-helper/docs/features/channels/plugins.md +1 -1
- package/bundled/qc-helper/docs/features/commands.md +108 -77
- package/bundled/qc-helper/docs/features/hooks.md +6 -1
- package/bundled/qc-helper/docs/features/memory.md +52 -0
- package/bundled/qc-helper/docs/qwen-serve.md +24 -2
- package/bundled/qc-helper/docs/support/tos-privacy.md +16 -3
- package/bundled/qc-helper/docs/support/troubleshooting.md +6 -0
- package/chunks/{MaxSizedBox-7RERNLFK.js → MaxSizedBox-T5X5MN5Q.js} +29 -27
- package/chunks/{StandaloneSessionPicker-S22XBSN6.js → StandaloneSessionPicker-P3E3WTOQ.js} +42 -37
- package/chunks/{acpAgent-4MCETHF2.js → acpAgent-QEP6DXFW.js} +864 -175
- package/chunks/{agent-K3ENVYSB.js → agent-UDANDUF7.js} +24 -23
- package/chunks/{agent-headless-HO3V3A3A.js → agent-headless-KYVSZHSO.js} +24 -23
- package/chunks/{anthropicContentGenerator-E3TQ5XJJ.js → anthropicContentGenerator-KWSJAQ7B.js} +33 -12
- package/chunks/{artifact-tool-QA4KRO3B.js → artifact-tool-D2X7G3C3.js} +2 -2
- package/chunks/{askUserQuestion-HMWXW6VN.js → askUserQuestion-UGETICJQ.js} +2 -2
- package/chunks/{bridge-4LV2HBH2.js → bridge-X42LXPTA.js} +31 -30
- package/chunks/{ca-7WC7ELX6.js → ca-IUFWESZL.js} +1 -0
- package/chunks/{chunk-JXQ23SNT.js → chunk-2JKEQWDZ.js} +623 -78
- package/chunks/{chunk-2TJ3YN6Q.js → chunk-2RJA6J57.js} +416 -401
- package/chunks/{chunk-3UALQ56H.js → chunk-3227DZO4.js} +1 -1
- package/chunks/{chunk-BGUDAULQ.js → chunk-3CPCDXFR.js} +5 -5
- package/chunks/{chunk-5RVSM6NW.js → chunk-3HUHAQF3.js} +1 -1
- package/chunks/{chunk-D5E5TMYH.js → chunk-3M4W4U5G.js} +16 -2
- package/chunks/{chunk-DBZUXSSV.js → chunk-4X7NIEQJ.js} +3 -3
- package/chunks/chunk-5GOVVXEV.js +73 -0
- package/chunks/{chunk-2BAQ3ACS.js → chunk-5SM4VT6V.js} +405 -5
- package/chunks/{chunk-NYOHZYHM.js → chunk-5XQ4A26L.js} +45 -3
- package/chunks/{chunk-CI5O6SQL.js → chunk-6FKEVYCD.js} +4 -4
- package/chunks/{chunk-ZD3NW5KL.js → chunk-7LM4XHPE.js} +63 -9
- package/chunks/chunk-7UNP2XM7.js +840 -0
- package/chunks/{chunk-OQHQHJRK.js → chunk-ARYCHZ4B.js} +151 -4775
- package/chunks/chunk-BC3C4F3Q.js +40 -0
- package/chunks/{chunk-CGEGHF2C.js → chunk-BKXBARJZ.js} +1 -1
- package/chunks/chunk-BMFMJINR.js +45 -0
- package/chunks/chunk-BOJWHRMX.js +11273 -0
- package/chunks/{chunk-CGOMICBE.js → chunk-BSZEQNUA.js} +34 -1
- package/chunks/{chunk-OIC6S73E.js → chunk-BUUQ5HPX.js} +1 -1
- package/chunks/{chunk-2BIF2NVV.js → chunk-BVSVIN45.js} +2 -2
- package/chunks/{chunk-TOEGRAVX.js → chunk-BX5YO7UW.js} +17 -5
- package/chunks/{chunk-4W4T77MO.js → chunk-BXVWV4GI.js} +109 -6
- package/chunks/{chunk-VQRYPFCV.js → chunk-CYHVPQYT.js} +10 -460
- package/chunks/{chunk-PRYMDRBV.js → chunk-DDMOU62Q.js} +1 -1
- package/chunks/{chunk-S566HKUS.js → chunk-DHXWNSJA.js} +15 -1
- package/chunks/{chunk-F6XNTYNE.js → chunk-E6XQPQXW.js} +30 -307
- package/chunks/{chunk-QBZ22PQX.js → chunk-EWALXUAD.js} +3 -1
- package/chunks/{chunk-EL5IHQON.js → chunk-EWYQFWYF.js} +6 -27
- package/chunks/{chunk-HG6YKEBI.js → chunk-F2DOT6UZ.js} +161 -30
- package/chunks/{chunk-JTYRZODB.js → chunk-FOOIZCWL.js} +16 -7
- package/chunks/{chunk-EX4W6ZYG.js → chunk-G5G4TFZ7.js} +1 -1
- package/chunks/{chunk-EW6C27XX.js → chunk-GEHVIWAT.js} +9 -3
- package/chunks/chunk-HEVBZSXG.js +4923 -0
- package/chunks/chunk-HFQ2XSE7.js +690 -0
- package/chunks/{chunk-PF523XJX.js → chunk-HPGTGMP3.js} +1 -1
- package/chunks/{chunk-OTBK43JR.js → chunk-HYCC3XM3.js} +1 -1
- package/chunks/{chunk-TMBPDHXD.js → chunk-I4H7PRWQ.js} +1 -1
- package/chunks/{chunk-EEEZIVFJ.js → chunk-JOLIREYR.js} +1 -1
- package/chunks/{chunk-TZM5JIAX.js → chunk-JYPQTRA3.js} +10 -7
- package/chunks/chunk-K5XU6E4T.js +167 -0
- package/chunks/{chunk-BS64PXY3.js → chunk-KFVU7ODN.js} +1 -1
- package/chunks/{chunk-3RJ3ZPPR.js → chunk-KKQWC6WS.js} +7 -7
- package/chunks/{chunk-3QFE4OTE.js → chunk-LBW7NW7X.js} +4 -4
- package/chunks/{chunk-Z7XXFPGP.js → chunk-LDWTYE33.js} +1 -1
- package/chunks/{chunk-EXMX5YHG.js → chunk-LP5E4YIH.js} +1 -1
- package/chunks/{chunk-ZUAXI2OA.js → chunk-M2U5NM24.js} +188 -11366
- package/chunks/{chunk-I5T6PIBQ.js → chunk-MBV2CTRL.js} +1010 -925
- package/chunks/{chunk-5JZLXFPD.js → chunk-MTHMXSNT.js} +11 -11
- package/chunks/{chunk-FPACXT7T.js → chunk-MTJVHQDP.js} +534 -164
- package/chunks/chunk-NDOC6M5S.js +481 -0
- package/chunks/{chunk-HGNSRCRB.js → chunk-NL4C3H6C.js} +1 -1
- package/chunks/{chunk-GZCJS5WH.js → chunk-O2VBQ3Y2.js} +2 -2
- package/chunks/{chunk-UN3C6GOR.js → chunk-PPFWEV7G.js} +10 -108
- package/chunks/{chunk-RS4HRC75.js → chunk-QI2CFVRF.js} +85 -8
- package/chunks/{chunk-XU5P23GN.js → chunk-QLDPRILK.js} +3 -3
- package/chunks/chunk-QYUE6W3T.js +123 -0
- package/chunks/{chunk-USGMPVOY.js → chunk-R3LHDWF2.js} +21 -12
- package/chunks/{chunk-BNXANYLH.js → chunk-RD6YJJJ6.js} +81 -1
- package/chunks/{chunk-PXFFKNCO.js → chunk-RIP623NO.js} +4877 -2469
- package/chunks/{chunk-XKBP7M7A.js → chunk-RYBSUGOR.js} +142 -30
- package/chunks/{chunk-BG4BIP7F.js → chunk-SBBN4BWR.js} +4 -2
- package/chunks/{chunk-T2VDEFUU.js → chunk-SKQZZVAV.js} +2 -2
- package/chunks/{chunk-5R4SBCX6.js → chunk-SL5MOGQH.js} +3 -3
- package/chunks/chunk-SMXULRAN.js +37 -0
- package/chunks/{chunk-WA5OJGFL.js → chunk-SS6WJMQK.js} +363 -76
- package/chunks/{chunk-UAARVVBI.js → chunk-TNWFHGN6.js} +1 -1
- package/chunks/{chunk-I2RFAW6Q.js → chunk-TT6Q3EN4.js} +7 -5
- package/chunks/chunk-UAKXW3DN.js +30 -0
- package/chunks/{chunk-C4K3FEQ2.js → chunk-UX4SGCGN.js} +32 -3
- package/chunks/{chunk-J6AW7O5D.js → chunk-WHDQZHRU.js} +2 -2
- package/chunks/{chunk-WGAEWQTF.js → chunk-WN6K47NR.js} +1 -2
- package/chunks/{chunk-UAARJR2G.js → chunk-WQT5SOI6.js} +2 -2
- package/chunks/{chunk-VYFMN7C6.js → chunk-WUMVIRTN.js} +225 -37
- package/chunks/{chunk-R36VMVEP.js → chunk-XJMKOTVV.js} +5 -5
- package/chunks/{chunk-TXVGVZYR.js → chunk-XJYUPHRC.js} +3 -3
- package/chunks/{chunk-2JD55C6G.js → chunk-Y6YMRS3U.js} +2 -2
- package/chunks/{chunk-LLFFKXZZ.js → chunk-YALKC62D.js} +56 -29
- package/chunks/{chunk-GYZQA2FO.js → chunk-YLZJCUH6.js} +1 -1
- package/chunks/chunk-YTFBURQD.js +313 -0
- package/chunks/{chunk-KBXLIVWQ.js → chunk-ZFPRYHWR.js} +2 -2
- package/chunks/{chunk-X6EDWN44.js → chunk-ZP6UKOPB.js} +1 -1
- package/chunks/{computer-use-6BBRNT6S.js → computer-use-LF2S6LUZ.js} +103 -49
- package/chunks/{contextCommand-UJAJ6N57.js → contextCommand-VPX5UG55.js} +26 -25
- package/chunks/{cron-create-4KQE6YLH.js → cron-create-SKEY55RG.js} +4 -4
- package/chunks/{cron-delete-THME66UP.js → cron-delete-I3NAT32Z.js} +6 -6
- package/chunks/{cron-list-7A54SJ5N.js → cron-list-6XRRL6TH.js} +5 -5
- package/chunks/{daemon-status-provider-CSL3BUOY.js → daemon-status-provider-5JAWZS4V.js} +34 -34
- package/chunks/{de-ACHALDED.js → de-EOW5PVHF.js} +1 -0
- package/chunks/{dist-BD27IA3L.js → dist-4FCNM6KJ.js} +2 -2
- package/chunks/{dist-OD2FY543.js → dist-O3H4PABL.js} +2 -2
- package/chunks/{dist-BOOH5JWN.js → dist-SCAAUWPS.js} +18 -4
- package/chunks/{dist-QHJRBCMP.js → dist-UKPWQJCL.js} +44 -2
- package/chunks/{dist-QFFZWACU.js → dist-VD7HXTML.js} +20 -9
- package/chunks/{earlyInputCapture-X6KMCSHD.js → earlyInputCapture-7FRQMU4P.js} +25 -24
- package/chunks/{edit-7J6WXARF.js → edit-REKJDIRL.js} +68 -27
- package/chunks/{en-NCUBOJOY.js → en-HPWEJBTR.js} +8 -0
- package/chunks/{enter-worktree-KULCKJPJ.js → enter-worktree-GBKUDCD3.js} +24 -23
- package/chunks/{enterPlanMode-H2CIOLMH.js → enterPlanMode-WB3Y4FGT.js} +24 -23
- package/chunks/{errors-6G2JYR54.js → errors-2XJDYO3M.js} +26 -25
- package/chunks/{exit-worktree-ECBWBVC5.js → exit-worktree-KKC7O2ZV.js} +24 -23
- package/chunks/{exitPlanMode-DQV56GTW.js → exitPlanMode-7HMS3TF7.js} +24 -23
- package/chunks/{fast-path-35JJFS3C.js → fast-path-XVNIYWGG.js} +4 -4
- package/chunks/{fast-path-settings-TSVI53EB.js → fast-path-settings-UQLRFDQP.js} +3 -2
- package/chunks/{fr-GVKZ27MF.js → fr-BN5NKSAC.js} +1 -0
- package/chunks/{gemini-NDTG7WAX.js → gemini-U5TPU3IQ.js} +210 -104
- package/chunks/{geminiContentGenerator-XTG6SFLA.js → geminiContentGenerator-ZVWRHXOO.js} +4 -4
- package/chunks/{glob-SJ2JBZK4.js → glob-FEJWIH2W.js} +24 -23
- package/chunks/{grep-BRMWFEWR.js → grep-KMP25IIA.js} +24 -23
- package/chunks/{headlessSafetyWarnings-LJ6FJX77.js → headlessSafetyWarnings-5DEDET4M.js} +24 -23
- package/chunks/{initializer-P5AXF7PV.js → initializer-FJTACLOH.js} +31 -28
- package/chunks/{ja-FKZ2WZ4D.js → ja-7DEUB2TS.js} +1 -0
- package/chunks/{keychain-token-storage-CSYMNRPP.js → keychain-token-storage-YLMDLKXT.js} +2 -2
- package/chunks/{list-6COK34BP.js → list-SCQXYJUW.js} +32 -29
- package/chunks/{loadedSettingsAdapter-XIUTN245.js → loadedSettingsAdapter-P6KPNAG7.js} +30 -27
- package/chunks/{loop-wakeup-33GPXJLA.js → loop-wakeup-FYEQEJUA.js} +7 -7
- package/chunks/{ls-BYQY7RVV.js → ls-R5WP4BGZ.js} +4 -4
- package/chunks/{lsp-X54RO4IV.js → lsp-GSICJMMW.js} +2 -2
- package/chunks/{monitor-DH7KFSJV.js → monitor-JU3HHHVY.js} +24 -23
- package/chunks/nonInteractiveCli-IZEKUHU3.js +100 -0
- package/chunks/{notebook-edit-D4R4ZLMA.js → notebook-edit-YHY3Z2QQ.js} +52 -23
- package/chunks/{openaiContentGenerator-F74IPQ6E.js → openaiContentGenerator-4WUVA3Q2.js} +13 -12
- package/chunks/{pt-SY5NCFGR.js → pt-TNZRNLQB.js} +1 -0
- package/chunks/{qwenContentGenerator-ATTK5P4D.js → qwenContentGenerator-MVYYR44B.js} +26 -25
- package/chunks/{qwenOAuth2-K4AASPCV.js → qwenOAuth2-6HNGHALT.js} +5 -4
- package/chunks/{read-file-767G23OY.js → read-file-NABEO2X4.js} +8 -8
- package/chunks/{read-mcp-resource-I5HCZJ6X.js → read-mcp-resource-DQQW7H7T.js} +2 -2
- package/chunks/{ripGrep-4OMMGSWX.js → ripGrep-YXLK6UTX.js} +24 -23
- package/chunks/{ru-FWNXJG4F.js → ru-NIAUBKCN.js} +1 -0
- package/chunks/{run-qwen-serve-REKSTYRA.js → run-qwen-serve-4B5QBMLQ.js} +144 -41
- package/chunks/{scheduler-UNTFDQLG.js → scheduler-XAKIYBDL.js} +24 -23
- package/chunks/{send-message-2DAFXEYP.js → send-message-EE4TWKVU.js} +2 -2
- package/chunks/{serve-KZ56QB5S.js → serve-4RXVLUKD.js} +30 -28
- package/chunks/{server-BV5TZUMU.js → server-7PA5QGIA.js} +11037 -8747
- package/chunks/{session-OIBXFSGD.js → session-WNVWRYAH.js} +194 -57
- package/chunks/{settings-WJTCBIRA.js → settings-YJH32TFF.js} +30 -27
- package/chunks/{shell-WC52IBHW.js → shell-YFLABOS5.js} +24 -23
- package/chunks/{skill-U7F7JR4M.js → skill-N3J7FUAY.js} +38 -11
- package/chunks/{spawnChannel-EI2E5BCW.js → spawnChannel-D6WVMFZJ.js} +26 -25
- package/chunks/{src-4F6FBZFI.js → src-IU5GDBEH.js} +88 -28
- package/chunks/{startInteractiveUI-SFRVB3CE.js → startInteractiveUI-QIPQTRT4.js} +2784 -2474
- package/chunks/{syntheticOutput-PPS3HDVA.js → syntheticOutput-T5YHQ6S2.js} +3 -3
- package/chunks/{task-create-OUYCA553.js → task-create-BLY7RHXX.js} +6 -6
- package/chunks/{task-list-WAX7I2IA.js → task-list-3QUYNVYR.js} +5 -5
- package/chunks/{task-stop-CAIL6FNH.js → task-stop-U4RATK2V.js} +2 -2
- package/chunks/{task-update-AHEO5OUT.js → task-update-U54O7KJX.js} +6 -6
- package/chunks/{team-create-3WC7CIIW.js → team-create-TP4CLLM3.js} +24 -23
- package/chunks/{team-delete-HGHFG23B.js → team-delete-LKBYPASD.js} +5 -5
- package/chunks/{theme-manager-DICFCT3P.js → theme-manager-N2TBIBZT.js} +25 -24
- package/chunks/{todoWrite-IFBMN3FH.js → todoWrite-OYXKTVYN.js} +4 -4
- package/chunks/{tool-search-L5K7HLER.js → tool-search-GBBSICYB.js} +101 -16
- package/chunks/trustedFolders-F7TBU5CD.js +76 -0
- package/chunks/types-QX5C3CHJ.js +12 -0
- package/chunks/{validateNonInterActiveAuth-GCH4Z6KI.js → validateNonInterActiveAuth-EQDQPSMI.js} +55 -50
- package/chunks/{web-fetch-B5IIQ5TT.js → web-fetch-PCVCML2X.js} +29 -15
- package/chunks/{workflow-OB3AND7I.js → workflow-ANJ4FYBK.js} +32 -30
- package/chunks/{workspace-providers-status-ZIR5EPOO.js → workspace-providers-status-462QQKQM.js} +32 -29
- package/chunks/workspace-service-VTG4G6CW.js +75 -0
- package/chunks/{write-file-SEXBNDZM.js → write-file-3P3WMQQW.js} +63 -27
- package/chunks/{zh-RBEGQVQT.js → zh-5EUYXASN.js} +8 -0
- package/chunks/{zh-TW-QGAVRXT3.js → zh-TW-TR3XYGE5.js} +8 -0
- package/cli-entry.js +20 -9
- package/cli.js +6 -6
- package/locales/ca.js +2 -0
- package/locales/de.js +2 -0
- package/locales/en.js +14 -0
- package/locales/fr.js +2 -0
- package/locales/ja.js +1 -0
- package/locales/pt.js +2 -0
- package/locales/ru.js +2 -0
- package/locales/zh-TW.js +12 -0
- package/locales/zh.js +12 -0
- package/node_modules/@qwen-code/audio-capture/dist/index.d.ts +35 -0
- package/node_modules/@qwen-code/audio-capture/dist/index.js +48 -0
- package/node_modules/@qwen-code/audio-capture/dist/index.js.map +1 -0
- package/node_modules/@qwen-code/audio-capture/dist/platform.d.ts +7 -0
- package/node_modules/@qwen-code/audio-capture/dist/platform.js +18 -0
- package/node_modules/@qwen-code/audio-capture/dist/platform.js.map +1 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/LICENSE +21 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/README.md +58 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/SECURITY.md +5 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/bin.js +84 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/build-test.js +19 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/index.js +6 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/node-gyp-build.js +207 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/optional.js +7 -0
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/package.json +43 -0
- package/node_modules/@qwen-code/audio-capture/package.json +30 -0
- 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/linux-arm64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/linux-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 +18 -6
- package/patches/chrome-devtools-mcp+1.4.0.patch +31 -0
- package/postinstall.js +50 -0
- package/web-shell/assets/{arc-D_ae_Y9k.js → arc-DIemsO_F.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-BMLRjppa.js → architectureDiagram-3BPJPVTR-CTlUvIQv.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-qVDQC8KL.js → blockDiagram-GPEHLZMM-DG0jmIZK.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-CxWS6MBd.js → c4Diagram-AAUBKEIU-WljDBi55.js} +1 -1
- package/web-shell/assets/channel-CbzRbdpA.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-BtNuY5Yp.js → chunk-2J33WTMH-8nc6onr2.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-BU-x368d.js → chunk-4BX2VUAB-EopXix8Z.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-DRgTLvBu.js → chunk-55IACEB6-BE4Jz3N3.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-D2aElv7q.js → chunk-727SXJPM-C_eErgDN.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-BwHCMRQ0.js → chunk-AQP2D5EJ-DFtVW-Is.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-BMyPH-ku.js → chunk-FMBD7UC4-CJPyxIg9.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-BNKTstUf.js → chunk-ND2GUHAM-Bk-xgj6M.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-gD3vsFId.js → chunk-QZHKN3VN-p3Y4yaFB.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-BFdyQ52h.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BFdyQ52h.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-BG9FAsXE.js → cose-bilkent-S5V4N54A-BCMO6Js3.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-Cy1sR7om.js → dagre-BM42HDAG-DUMrwASf.js} +1 -1
- package/web-shell/assets/default-aEtf0G7-.svg +1 -0
- package/web-shell/assets/{diagram-2AECGRRQ-Bxi1I_Vu.js → diagram-2AECGRRQ-C5d7qJbL.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-BGCJjO6y.js → diagram-5GNKFQAL-CjLzMhbT.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-CMTeLXp7.js → diagram-KO2AKTUF-RCoNWeAm.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-BN1wyAGh.js → diagram-LMA3HP47-KThXjaNV.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-UYSG_zcp.js → diagram-OG6HWLK6-DKsC2BWz.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-DqXL08lw.js → erDiagram-TEJ5UH35-DJ2ukmq0.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-BPJ9SFji.js → flowDiagram-I6XJVG4X-zzqaARRX.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-BEd9Pnms.js → ganttDiagram-6RSMTGT7-kFUg-gqT.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-DrNRH3oi.js → gitGraphDiagram-PVQCEYII-C6JnMVzD.js} +1 -1
- package/web-shell/assets/index-B3XUm7dw.js +713 -0
- package/web-shell/assets/index-CHmzAWys.css +5 -0
- package/web-shell/assets/{infoDiagram-5YYISTIA-CFYBrkVI.js → infoDiagram-5YYISTIA-XqPVhp89.js} +1 -1
- package/web-shell/assets/insert-DmRbh9Rv.svg +1 -0
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-Dxsddth7.js → ishikawaDiagram-YF4QCWOH-D2k8sGpf.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-DxaDK80t.js → journeyDiagram-JHISSGLW-CQEST1GE.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-Q0jQCOct.js → kanban-definition-UN3LZRKU-DZ7kqVBC.js} +1 -1
- package/web-shell/assets/{linear-DGGM2kcP.js → linear-B6C_6q_E.js} +1 -1
- package/web-shell/assets/{mermaid.core-ByoxnWut.js → mermaid.core-CsyNP1qt.js} +5 -5
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-C0ZseHsi.js → mindmap-definition-RKZ34NQL-B1VDIzJq.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-MQXNhl5A.js → pieDiagram-4H26LBE5-DN0E7Jwu.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-DIR4ee2A.js → quadrantDiagram-W4KKPZXB-UKTQdHKm.js} +1 -1
- package/web-shell/assets/queue-DWpnpFgW.svg +1 -0
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-BTR0UdYQ.js → requirementDiagram-4Y6WPE33-BRGceQvN.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-sfnhMELN.js → sankeyDiagram-5OEKKPKP-2DDyu41v.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-DXczHOHH.js → sequenceDiagram-3UESZ5HK-CT5iE8ZU.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-DdE_KQFH.js → stateDiagram-AJRCARHV-CqMQ_s4G.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-CamhV5pM.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-DIc61uE8.js → timeline-definition-PNZ67QCA-Di37QSZU.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-BF7ODmxB.js → vennDiagram-CIIHVFJN-DCXvpz5S.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-3pc1MEaw.js → wardley-L42UT6IY-CpUCk6OX.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-9jnPcZuP.js → wardleyDiagram-YWT4CUSO-D01pVEBg.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-DcthaZK6.js → xychartDiagram-2RQKCTM6-DOu5JDoz.js} +1 -1
- package/web-shell/index.html +2 -2
- package/chunks/chunk-B6HQGTYR.js +0 -410
- package/chunks/chunk-R2BXK7SX.js +0 -13
- package/chunks/chunk-RKIZXJMB.js +0 -13
- package/chunks/nonInteractiveCli-7575LBXP.js +0 -91
- package/chunks/workspace-service-OM5D4OI2.js +0 -60
- package/web-shell/assets/channel-DYPNiTww.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-p_g25Tni.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-p_g25Tni.js +0 -1
- package/web-shell/assets/index-CpOXWDPh.js +0 -714
- package/web-shell/assets/index-oVK7qhre.css +0 -5
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BHUSEndU.js +0 -1
|
@@ -41,6 +41,7 @@ __name(getGlobalQwenDir, "getGlobalQwenDir");
|
|
|
41
41
|
// packages/channels/base/dist/SessionRouter.js
|
|
42
42
|
init_esbuild_shims();
|
|
43
43
|
import { existsSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
44
|
+
import process2 from "node:process";
|
|
44
45
|
var SessionRouter = class {
|
|
45
46
|
static {
|
|
46
47
|
__name(this, "SessionRouter");
|
|
@@ -51,6 +52,8 @@ var SessionRouter = class {
|
|
|
51
52
|
// session ID → target
|
|
52
53
|
toCwd = /* @__PURE__ */ new Map();
|
|
53
54
|
// session ID → cwd
|
|
55
|
+
creatingSessions = /* @__PURE__ */ new Map();
|
|
56
|
+
sessionLoadWindows = /* @__PURE__ */ new Set();
|
|
54
57
|
bridge;
|
|
55
58
|
defaultCwd;
|
|
56
59
|
defaultScope;
|
|
@@ -82,57 +85,89 @@ var SessionRouter = class {
|
|
|
82
85
|
return `${channelName}:${senderId}:${chatId}`;
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
|
-
/**
|
|
86
|
-
* Sender-scoped key prefix for by-sender (no chatId) scans. The trailing
|
|
87
|
-
* ':' delimiter ensures a sender id matches as a whole segment — without it
|
|
88
|
-
* sender "bob" would also match another sender "bobby"
|
|
89
|
-
* (key `${channelName}:bobby:${chatId}`).
|
|
90
|
-
*/
|
|
91
|
-
senderPrefix(channelName, senderId) {
|
|
92
|
-
return `${channelName}:${senderId}:`;
|
|
93
|
-
}
|
|
94
88
|
async resolve(channelName, senderId, chatId, threadId, cwd) {
|
|
95
89
|
const key = this.routingKey(channelName, senderId, chatId, threadId);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
90
|
+
let failedCreateWaits = 0;
|
|
91
|
+
for (; ; ) {
|
|
92
|
+
const existing = this.toSession.get(key);
|
|
93
|
+
if (existing) {
|
|
94
|
+
return existing;
|
|
95
|
+
}
|
|
96
|
+
const creating = this.creatingSessions.get(key);
|
|
97
|
+
if (creating) {
|
|
98
|
+
try {
|
|
99
|
+
return await creating;
|
|
100
|
+
} catch (err) {
|
|
101
|
+
if (this.creatingSessions.get(key) === creating) {
|
|
102
|
+
this.creatingSessions.delete(key);
|
|
103
|
+
}
|
|
104
|
+
failedCreateWaits++;
|
|
105
|
+
if (failedCreateWaits > 3) {
|
|
106
|
+
throw err;
|
|
107
|
+
}
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
const created = Promise.resolve().then(async () => {
|
|
112
|
+
const sessionCwd = cwd || this.defaultCwd;
|
|
113
|
+
const loadWindow = this.beginSessionLoad();
|
|
114
|
+
try {
|
|
115
|
+
const sessionId = await this.createLiveSession(sessionCwd, loadWindow, key);
|
|
116
|
+
this.toSession.set(key, sessionId);
|
|
117
|
+
this.toTarget.set(sessionId, {
|
|
118
|
+
channelName,
|
|
119
|
+
senderId,
|
|
120
|
+
chatId,
|
|
121
|
+
threadId
|
|
122
|
+
});
|
|
123
|
+
this.toCwd.set(sessionId, sessionCwd);
|
|
124
|
+
this.persist();
|
|
125
|
+
return sessionId;
|
|
126
|
+
} finally {
|
|
127
|
+
this.endSessionLoad(loadWindow);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
this.creatingSessions.set(key, created);
|
|
131
|
+
try {
|
|
132
|
+
return await created;
|
|
133
|
+
} finally {
|
|
134
|
+
if (this.creatingSessions.get(key) === created) {
|
|
135
|
+
this.creatingSessions.delete(key);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
99
138
|
}
|
|
100
|
-
const sessionCwd = cwd || this.defaultCwd;
|
|
101
|
-
const sessionId = await this.bridge.newSession(sessionCwd);
|
|
102
|
-
this.toSession.set(key, sessionId);
|
|
103
|
-
this.toTarget.set(sessionId, { channelName, senderId, chatId, threadId });
|
|
104
|
-
this.toCwd.set(sessionId, sessionCwd);
|
|
105
|
-
this.persist();
|
|
106
|
-
return sessionId;
|
|
107
139
|
}
|
|
108
140
|
getTarget(sessionId) {
|
|
109
141
|
return this.toTarget.get(sessionId);
|
|
110
142
|
}
|
|
111
|
-
|
|
143
|
+
getSession(channelName, senderId, chatId, threadId) {
|
|
144
|
+
return this.toSession.get(this.routingKey(channelName, senderId, chatId, threadId));
|
|
145
|
+
}
|
|
146
|
+
hasSession(channelName, senderId, chatId, threadId) {
|
|
112
147
|
if (chatId) {
|
|
113
|
-
return this.toSession.has(this.routingKey(channelName, senderId, chatId));
|
|
148
|
+
return this.toSession.has(this.routingKey(channelName, senderId, chatId, threadId));
|
|
114
149
|
}
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
if (k.startsWith(prefix))
|
|
150
|
+
for (const target of this.toTarget.values()) {
|
|
151
|
+
if (target.channelName === channelName && target.senderId === senderId) {
|
|
118
152
|
return true;
|
|
153
|
+
}
|
|
119
154
|
}
|
|
120
155
|
return false;
|
|
121
156
|
}
|
|
122
157
|
/**
|
|
123
158
|
* Remove session(s) for the given sender. Returns the removed session IDs.
|
|
124
159
|
*/
|
|
125
|
-
removeSession(channelName, senderId, chatId) {
|
|
160
|
+
removeSession(channelName, senderId, chatId, threadId) {
|
|
126
161
|
const removedIds = [];
|
|
127
162
|
if (chatId) {
|
|
128
|
-
const key = this.routingKey(channelName, senderId, chatId);
|
|
163
|
+
const key = this.routingKey(channelName, senderId, chatId, threadId);
|
|
129
164
|
const sessionId = this.deleteByKey(key);
|
|
130
165
|
if (sessionId)
|
|
131
166
|
removedIds.push(sessionId);
|
|
132
167
|
} else {
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
if (
|
|
168
|
+
for (const [k, mappedSessionId] of [...this.toSession.entries()]) {
|
|
169
|
+
const target = this.toTarget.get(mappedSessionId);
|
|
170
|
+
if (target?.channelName === channelName && target.senderId === senderId) {
|
|
136
171
|
const sessionId = this.deleteByKey(k);
|
|
137
172
|
if (sessionId)
|
|
138
173
|
removedIds.push(sessionId);
|
|
@@ -143,6 +178,31 @@ var SessionRouter = class {
|
|
|
143
178
|
this.persist();
|
|
144
179
|
return removedIds;
|
|
145
180
|
}
|
|
181
|
+
/** Remove a session mapping by daemon/ACP session ID. */
|
|
182
|
+
removeSessionId(sessionId) {
|
|
183
|
+
let removed = false;
|
|
184
|
+
for (const [key, mappedSessionId] of [...this.toSession.entries()]) {
|
|
185
|
+
if (mappedSessionId === sessionId) {
|
|
186
|
+
this.toSession.delete(key);
|
|
187
|
+
removed = true;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (this.toTarget.delete(sessionId)) {
|
|
191
|
+
removed = true;
|
|
192
|
+
}
|
|
193
|
+
if (this.toCwd.delete(sessionId)) {
|
|
194
|
+
removed = true;
|
|
195
|
+
}
|
|
196
|
+
if (!removed && this.sessionLoadWindows.size > 0) {
|
|
197
|
+
for (const loadWindow of this.sessionLoadWindows) {
|
|
198
|
+
loadWindow.add(sessionId);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (removed) {
|
|
202
|
+
this.persist();
|
|
203
|
+
}
|
|
204
|
+
return removed;
|
|
205
|
+
}
|
|
146
206
|
deleteByKey(key) {
|
|
147
207
|
const sessionId = this.toSession.get(key);
|
|
148
208
|
if (!sessionId)
|
|
@@ -166,7 +226,7 @@ var SessionRouter = class {
|
|
|
166
226
|
/**
|
|
167
227
|
* Restore session mappings from a previous bridge.
|
|
168
228
|
* Called after bridge restart — attempts loadSession for each saved mapping.
|
|
169
|
-
* Failed loads are
|
|
229
|
+
* Failed loads are dropped (new session on next message).
|
|
170
230
|
*/
|
|
171
231
|
async restoreSessions() {
|
|
172
232
|
if (!this.persistPath || !existsSync(this.persistPath)) {
|
|
@@ -180,18 +240,54 @@ var SessionRouter = class {
|
|
|
180
240
|
}
|
|
181
241
|
let restored = 0;
|
|
182
242
|
let failed = 0;
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
243
|
+
let changed = false;
|
|
244
|
+
const reservations = /* @__PURE__ */ new Map();
|
|
245
|
+
for (const key of Object.keys(entries)) {
|
|
246
|
+
this.deleteByKey(key);
|
|
247
|
+
const reservation = this.createSessionReservation();
|
|
248
|
+
reservation.promise.catch(() => void 0);
|
|
249
|
+
this.creatingSessions.set(key, reservation.promise);
|
|
250
|
+
reservations.set(key, reservation);
|
|
251
|
+
}
|
|
252
|
+
const loadWindow = this.beginSessionLoad();
|
|
253
|
+
try {
|
|
254
|
+
for (const [key, entry] of Object.entries(entries)) {
|
|
255
|
+
const reservation = reservations.get(key);
|
|
256
|
+
if (!reservation)
|
|
257
|
+
continue;
|
|
258
|
+
try {
|
|
259
|
+
const sessionId = await this.bridge.loadSession(entry.sessionId, entry.cwd);
|
|
260
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
261
|
+
throw new Error("Invalid restored session ID");
|
|
262
|
+
}
|
|
263
|
+
if (loadWindow.delete(sessionId)) {
|
|
264
|
+
throw new Error("Restored session died before routing completed");
|
|
265
|
+
}
|
|
266
|
+
this.toSession.set(key, sessionId);
|
|
267
|
+
this.toTarget.set(sessionId, entry.target);
|
|
268
|
+
this.toCwd.set(sessionId, entry.cwd);
|
|
269
|
+
reservation.resolve(sessionId);
|
|
270
|
+
if (sessionId !== entry.sessionId) {
|
|
271
|
+
changed = true;
|
|
272
|
+
}
|
|
273
|
+
restored++;
|
|
274
|
+
} catch (err) {
|
|
275
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
276
|
+
process2.stderr.write(`[SessionRouter] Failed to restore session ${entry.sessionId} for key ${key}: ${reason}
|
|
277
|
+
`);
|
|
278
|
+
reservation.reject(new Error("Session restore failed", { cause: err }));
|
|
279
|
+
failed++;
|
|
280
|
+
changed = true;
|
|
281
|
+
} finally {
|
|
282
|
+
if (this.creatingSessions.get(key) === reservation.promise) {
|
|
283
|
+
this.creatingSessions.delete(key);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
192
286
|
}
|
|
287
|
+
} finally {
|
|
288
|
+
this.endSessionLoad(loadWindow);
|
|
193
289
|
}
|
|
194
|
-
if (
|
|
290
|
+
if (changed) {
|
|
195
291
|
this.persist();
|
|
196
292
|
}
|
|
197
293
|
return { restored, failed };
|
|
@@ -201,6 +297,8 @@ var SessionRouter = class {
|
|
|
201
297
|
this.toSession.clear();
|
|
202
298
|
this.toTarget.clear();
|
|
203
299
|
this.toCwd.clear();
|
|
300
|
+
this.creatingSessions.clear();
|
|
301
|
+
this.sessionLoadWindows.clear();
|
|
204
302
|
if (this.persistPath && existsSync(this.persistPath)) {
|
|
205
303
|
try {
|
|
206
304
|
unlinkSync(this.persistPath);
|
|
@@ -227,10 +325,80 @@ var SessionRouter = class {
|
|
|
227
325
|
} catch {
|
|
228
326
|
}
|
|
229
327
|
}
|
|
328
|
+
async createLiveSession(cwd, loadWindow, routingKey) {
|
|
329
|
+
const maxAttempts = 2;
|
|
330
|
+
let lastDeadSessionId;
|
|
331
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
332
|
+
const sessionId = await this.bridge.newSession(cwd);
|
|
333
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
334
|
+
throw new Error("Invalid session ID from bridge");
|
|
335
|
+
}
|
|
336
|
+
if (!loadWindow.delete(sessionId)) {
|
|
337
|
+
return sessionId;
|
|
338
|
+
}
|
|
339
|
+
lastDeadSessionId = sessionId;
|
|
340
|
+
}
|
|
341
|
+
throw new Error(`Session ${lastDeadSessionId ?? "unknown"} died before routing completed (${maxAttempts}/${maxAttempts} attempts, key ${routingKey})`);
|
|
342
|
+
}
|
|
343
|
+
beginSessionLoad() {
|
|
344
|
+
const loadWindow = /* @__PURE__ */ new Set();
|
|
345
|
+
this.sessionLoadWindows.add(loadWindow);
|
|
346
|
+
return loadWindow;
|
|
347
|
+
}
|
|
348
|
+
createSessionReservation() {
|
|
349
|
+
let resolveReservation;
|
|
350
|
+
let rejectReservation;
|
|
351
|
+
const promise = new Promise((resolve2, reject) => {
|
|
352
|
+
resolveReservation = resolve2;
|
|
353
|
+
rejectReservation = reject;
|
|
354
|
+
});
|
|
355
|
+
return {
|
|
356
|
+
promise,
|
|
357
|
+
resolve: resolveReservation,
|
|
358
|
+
reject: rejectReservation
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
endSessionLoad(loadWindow) {
|
|
362
|
+
this.sessionLoadWindows.delete(loadWindow);
|
|
363
|
+
}
|
|
230
364
|
};
|
|
231
365
|
|
|
366
|
+
// packages/channels/base/dist/sanitize.js
|
|
367
|
+
init_esbuild_shims();
|
|
368
|
+
var PROMPT_UNSAFE_INVISIBLES = /[\u0080-\u009f\u200b-\u200d\u2028\u2029\u202a-\u202e\u2060\u2066-\u2069\ufeff]/g;
|
|
369
|
+
function truncateCodePoints(str, max) {
|
|
370
|
+
const cp = Array.from(str);
|
|
371
|
+
return cp.length > max ? cp.slice(0, max).join("") : str;
|
|
372
|
+
}
|
|
373
|
+
__name(truncateCodePoints, "truncateCodePoints");
|
|
374
|
+
function sanitizeSenderName(name) {
|
|
375
|
+
const cleaned = name.replace(PROMPT_UNSAFE_INVISIBLES, " ").replace(/[\u0000-\u001f\u007f]/g, " ").replace(/[[\]\r\n]/g, " ");
|
|
376
|
+
return truncateCodePoints(cleaned, 64).trim() || "unknown";
|
|
377
|
+
}
|
|
378
|
+
__name(sanitizeSenderName, "sanitizeSenderName");
|
|
379
|
+
function sanitizeQuotedText(text, maxLen) {
|
|
380
|
+
const cleaned = text.replace(PROMPT_UNSAFE_INVISIBLES, " ").replace(/[\u0000-\u001f\u007f]/g, " ").replace(/["[\]]/g, " ");
|
|
381
|
+
const cp = Array.from(cleaned);
|
|
382
|
+
return cp.length > maxLen ? cp.slice(0, maxLen - 1).join("") + "\u2026" : cleaned;
|
|
383
|
+
}
|
|
384
|
+
__name(sanitizeQuotedText, "sanitizeQuotedText");
|
|
385
|
+
function sanitizePromptText(text) {
|
|
386
|
+
return text.replace(PROMPT_UNSAFE_INVISIBLES, " ").replace(/^([ \t]*)\[([^\]\r\n]{1,64})\](:?)/gm, "$1$2$3").replace(/[\u0000-\u001f\u007f]/g, " ");
|
|
387
|
+
}
|
|
388
|
+
__name(sanitizePromptText, "sanitizePromptText");
|
|
389
|
+
function sanitizePromptPath(path3) {
|
|
390
|
+
const cleaned = path3.replace(PROMPT_UNSAFE_INVISIBLES, " ").replace(/[\u0000-\u001f\u007f]/g, " ");
|
|
391
|
+
return truncateCodePoints(cleaned, 1024);
|
|
392
|
+
}
|
|
393
|
+
__name(sanitizePromptPath, "sanitizePromptPath");
|
|
394
|
+
function sanitizeLogText(text, maxLen) {
|
|
395
|
+
return truncateCodePoints(text, maxLen).replace(/\n/g, "\\n").replace(PROMPT_UNSAFE_INVISIBLES, " ").replace(/[\u0000-\u001f\u007f]/g, " ");
|
|
396
|
+
}
|
|
397
|
+
__name(sanitizeLogText, "sanitizeLogText");
|
|
398
|
+
|
|
232
399
|
// packages/channels/base/dist/ChannelBase.js
|
|
233
400
|
init_esbuild_shims();
|
|
401
|
+
import { basename } from "node:path";
|
|
234
402
|
|
|
235
403
|
// packages/channels/base/dist/BlockStreamer.js
|
|
236
404
|
init_esbuild_shims();
|
|
@@ -265,6 +433,11 @@ var BlockStreamer = class {
|
|
|
265
433
|
}
|
|
266
434
|
await this.sending;
|
|
267
435
|
}
|
|
436
|
+
/** Drop buffered text and cancel future idle emission. */
|
|
437
|
+
stop() {
|
|
438
|
+
this.clearIdleTimer();
|
|
439
|
+
this.buffer = "";
|
|
440
|
+
}
|
|
268
441
|
// ---------------------------------------------------------------------------
|
|
269
442
|
// Internal
|
|
270
443
|
// ---------------------------------------------------------------------------
|
|
@@ -526,6 +699,10 @@ function generateCode() {
|
|
|
526
699
|
__name(generateCode, "generateCode");
|
|
527
700
|
|
|
528
701
|
// packages/channels/base/dist/ChannelBase.js
|
|
702
|
+
var CLEAR_CANCEL_TIMEOUT_MS = 3e3;
|
|
703
|
+
var COMMAND_TOKEN_CHARS = "a-zA-Z0-9_:-";
|
|
704
|
+
var PARSE_COMMAND_RE = new RegExp(`^\\/([${COMMAND_TOKEN_CHARS}]+)(?:@\\S+)?\\s*(.*)`, "s");
|
|
705
|
+
var COMMAND_TOKEN_RE = new RegExp(`^[${COMMAND_TOKEN_CHARS}]+(?:@\\S+)?$`);
|
|
529
706
|
var ChannelBase = class {
|
|
530
707
|
static {
|
|
531
708
|
__name(this, "ChannelBase");
|
|
@@ -542,10 +719,26 @@ var ChannelBase = class {
|
|
|
542
719
|
commands = /* @__PURE__ */ new Map();
|
|
543
720
|
/** Per-session promise chain to serialize prompt + send (followup mode). */
|
|
544
721
|
sessionQueues = /* @__PURE__ */ new Map();
|
|
722
|
+
registerBridgeEvents;
|
|
723
|
+
/**
|
|
724
|
+
* Per-session generation, bumped by /clear. A queued followup turn captures the
|
|
725
|
+
* generation when it enqueues and bails if /clear bumped it before the turn ran,
|
|
726
|
+
* so a cleared session can't be resurrected by an already-queued prompt.
|
|
727
|
+
*/
|
|
728
|
+
sessionGenerations = /* @__PURE__ */ new Map();
|
|
545
729
|
/** Per-session active prompt tracking for dispatch modes. */
|
|
546
730
|
activePrompts = /* @__PURE__ */ new Map();
|
|
547
731
|
/** Per-session message buffer for collect mode. */
|
|
548
732
|
collectBuffers = /* @__PURE__ */ new Map();
|
|
733
|
+
bridgeToolCallListener = /* @__PURE__ */ __name((event) => {
|
|
734
|
+
const target = this.router.getTarget(event.sessionId);
|
|
735
|
+
if (target) {
|
|
736
|
+
this.onToolCall(target.chatId, event);
|
|
737
|
+
}
|
|
738
|
+
}, "bridgeToolCallListener");
|
|
739
|
+
bridgeSessionDiedListener = /* @__PURE__ */ __name((event) => {
|
|
740
|
+
this.onSessionDied(event.sessionId);
|
|
741
|
+
}, "bridgeSessionDiedListener");
|
|
549
742
|
constructor(name, config, bridge, options) {
|
|
550
743
|
this.name = name;
|
|
551
744
|
this.config = config;
|
|
@@ -556,21 +749,36 @@ var ChannelBase = class {
|
|
|
556
749
|
this.gate = new SenderGate(config.senderPolicy, config.allowedUsers, pairingStore);
|
|
557
750
|
this.router = options?.router || new SessionRouter(bridge, config.cwd, config.sessionScope);
|
|
558
751
|
this.registerSharedCommands();
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
if (target) {
|
|
563
|
-
this.onToolCall(target.chatId, event);
|
|
564
|
-
}
|
|
565
|
-
});
|
|
752
|
+
this.registerBridgeEvents = options?.registerBridgeEvents ?? !options?.router;
|
|
753
|
+
if (this.registerBridgeEvents) {
|
|
754
|
+
this.attachBridgeEvents(bridge);
|
|
566
755
|
}
|
|
567
756
|
}
|
|
568
757
|
/** Replace the bridge instance (used after crash recovery restart). */
|
|
569
758
|
setBridge(bridge) {
|
|
759
|
+
if (this.registerBridgeEvents) {
|
|
760
|
+
this.detachBridgeEvents(this.bridge);
|
|
761
|
+
}
|
|
762
|
+
this.router.setBridge(bridge);
|
|
570
763
|
this.bridge = bridge;
|
|
764
|
+
if (this.registerBridgeEvents) {
|
|
765
|
+
this.attachBridgeEvents(bridge);
|
|
766
|
+
}
|
|
571
767
|
}
|
|
572
768
|
onToolCall(_chatId, _event) {
|
|
573
769
|
}
|
|
770
|
+
onSessionDied(sessionId) {
|
|
771
|
+
this.router.removeSessionId(sessionId);
|
|
772
|
+
this.instructedSessions.delete(sessionId);
|
|
773
|
+
}
|
|
774
|
+
attachBridgeEvents(bridge) {
|
|
775
|
+
bridge.on("toolCall", this.bridgeToolCallListener);
|
|
776
|
+
bridge.on("sessionDied", this.bridgeSessionDiedListener);
|
|
777
|
+
}
|
|
778
|
+
detachBridgeEvents(bridge) {
|
|
779
|
+
bridge.off("toolCall", this.bridgeToolCallListener);
|
|
780
|
+
bridge.off("sessionDied", this.bridgeSessionDiedListener);
|
|
781
|
+
}
|
|
574
782
|
/**
|
|
575
783
|
* Called when a prompt actually begins processing (inside the session queue).
|
|
576
784
|
* Override to show a platform-specific working indicator (e.g., typing, reaction).
|
|
@@ -607,38 +815,145 @@ var ChannelBase = class {
|
|
|
607
815
|
registerCommand(name, handler) {
|
|
608
816
|
this.commands.set(name.toLowerCase(), handler);
|
|
609
817
|
}
|
|
818
|
+
registerCancelCommand(name = "cancel") {
|
|
819
|
+
this.registerCommand(name, async (envelope) => {
|
|
820
|
+
if (!this.isAuthorizedForSharedSession(envelope)) {
|
|
821
|
+
await this.sendMessage(envelope.chatId, "Only authorized members can cancel requests in this shared session.");
|
|
822
|
+
return true;
|
|
823
|
+
}
|
|
824
|
+
const activeSessionId = this.findActiveSessionId(envelope);
|
|
825
|
+
if (!activeSessionId) {
|
|
826
|
+
await this.sendMessage(envelope.chatId, "No request is currently running.");
|
|
827
|
+
return true;
|
|
828
|
+
}
|
|
829
|
+
const active = this.activePrompts.get(activeSessionId);
|
|
830
|
+
if (!active) {
|
|
831
|
+
await this.sendMessage(envelope.chatId, "No request is currently running.");
|
|
832
|
+
return true;
|
|
833
|
+
}
|
|
834
|
+
const cancelRequested = active.cancelRequested ?? this.bridge.cancelSession(activeSessionId).then(() => true, (err) => {
|
|
835
|
+
process.stderr.write(`[${this.name}] cancelSession failed for session=${activeSessionId}: ${err instanceof Error ? err.message : err}
|
|
836
|
+
`);
|
|
837
|
+
active.cancelRequested = void 0;
|
|
838
|
+
return false;
|
|
839
|
+
});
|
|
840
|
+
active.cancelRequested = cancelRequested;
|
|
841
|
+
const cancelSucceeded = await cancelRequested;
|
|
842
|
+
if (!cancelSucceeded) {
|
|
843
|
+
await this.sendMessage(envelope.chatId, "Failed to cancel current request.");
|
|
844
|
+
return true;
|
|
845
|
+
}
|
|
846
|
+
active.cancelled = true;
|
|
847
|
+
active.stopStreaming?.();
|
|
848
|
+
this.collectBuffers.delete(activeSessionId);
|
|
849
|
+
await this.sendMessage(envelope.chatId, "Cancelled current request.");
|
|
850
|
+
return true;
|
|
851
|
+
});
|
|
852
|
+
}
|
|
610
853
|
/** Register shared slash commands. Called from constructor. */
|
|
611
854
|
registerSharedCommands() {
|
|
612
|
-
const
|
|
613
|
-
const removedIds = this.router.removeSession(this.name, envelope.senderId, envelope.chatId);
|
|
855
|
+
const doClear = /* @__PURE__ */ __name(async (envelope) => {
|
|
856
|
+
const removedIds = this.router.removeSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
614
857
|
if (removedIds.length > 0) {
|
|
615
858
|
for (const id of removedIds) {
|
|
859
|
+
if (this.isSharedSession(envelope)) {
|
|
860
|
+
const who = sanitizeSenderName(envelope.senderName || envelope.senderId || "unknown");
|
|
861
|
+
process.stderr.write(`[${this.name}] shared session ${id} cleared by ${who} (sender ${envelope.senderId})
|
|
862
|
+
`);
|
|
863
|
+
}
|
|
864
|
+
this.sessionGenerations.set(id, (this.sessionGenerations.get(id) ?? 0) + 1);
|
|
865
|
+
const active = this.activePrompts.get(id);
|
|
866
|
+
this.collectBuffers.delete(id);
|
|
867
|
+
if (active) {
|
|
868
|
+
const settled = await this.cancelAndAwaitActive(active, id);
|
|
869
|
+
if (!settled) {
|
|
870
|
+
const wedgedChat = active.chatId ? sanitizeLogText(active.chatId, 64) : "unknown";
|
|
871
|
+
const wedgedMessage = active.messageId ? `, message ${sanitizeLogText(active.messageId, 64)}` : "";
|
|
872
|
+
process.stderr.write(`[${this.name}] /clear abandoned a wedged turn for session ${id} (chat ${wedgedChat}${wedgedMessage}): it did not wind down within ${CLEAR_CANCEL_TIMEOUT_MS}ms
|
|
873
|
+
`);
|
|
874
|
+
active.clearEvicted = true;
|
|
875
|
+
try {
|
|
876
|
+
this.onPromptEnd(active.chatId, id, active.messageId);
|
|
877
|
+
} catch (err) {
|
|
878
|
+
process.stderr.write(`[${this.name}] onPromptEnd threw during /clear eviction for session ${id}: ${err instanceof Error ? err.message : err}
|
|
879
|
+
`);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
616
883
|
this.instructedSessions.delete(id);
|
|
884
|
+
const drained = this.sessionQueues.get(id);
|
|
885
|
+
const bumpedGeneration = this.sessionGenerations.get(id);
|
|
886
|
+
this.sessionQueues.delete(id);
|
|
887
|
+
this.activePrompts.delete(id);
|
|
888
|
+
if (drained) {
|
|
889
|
+
void drained.then(() => {
|
|
890
|
+
if (!this.sessionQueues.has(id) && this.sessionGenerations.get(id) === bumpedGeneration) {
|
|
891
|
+
this.sessionGenerations.delete(id);
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
} else {
|
|
895
|
+
this.sessionGenerations.delete(id);
|
|
896
|
+
}
|
|
617
897
|
}
|
|
618
|
-
await this.sendMessage(envelope.chatId, "Session cleared.
|
|
898
|
+
await this.sendMessage(envelope.chatId, "Session cleared. The next message starts a fresh conversation.");
|
|
619
899
|
} else {
|
|
620
900
|
await this.sendMessage(envelope.chatId, "No active session to clear.");
|
|
621
901
|
}
|
|
902
|
+
}, "doClear");
|
|
903
|
+
const clearHandler = /* @__PURE__ */ __name(async (envelope, args) => {
|
|
904
|
+
if (!this.isAuthorizedForSharedSession(envelope)) {
|
|
905
|
+
await this.sendMessage(envelope.chatId, "Only authorized members can clear this shared session.");
|
|
906
|
+
return true;
|
|
907
|
+
}
|
|
908
|
+
if (this.isSharedSession(envelope) && args.toLowerCase() !== "confirm") {
|
|
909
|
+
await this.sendMessage(envelope.chatId, 'This clears the shared session for everyone who shares it. Re-send with "confirm" (e.g. /clear confirm) to proceed.');
|
|
910
|
+
return true;
|
|
911
|
+
}
|
|
912
|
+
await doClear(envelope);
|
|
622
913
|
return true;
|
|
623
914
|
}, "clearHandler");
|
|
624
915
|
this.registerCommand("clear", clearHandler);
|
|
625
916
|
this.registerCommand("reset", clearHandler);
|
|
626
917
|
this.registerCommand("new", clearHandler);
|
|
918
|
+
this.registerCommand("who", async (envelope) => {
|
|
919
|
+
if (!this.isAuthorizedForSharedSession(envelope)) {
|
|
920
|
+
await this.sendMessage(envelope.chatId, "Only authorized members can view this shared session.");
|
|
921
|
+
return true;
|
|
922
|
+
}
|
|
923
|
+
const active = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
924
|
+
const scopeNote = this.config.sessionScope === "single" ? " (shared channel-wide)" : this.isSharedSession(envelope) ? envelope.isGroup ? " (shared by this group)" : "" : envelope.isGroup ? " (private to you)" : "";
|
|
925
|
+
await this.sendMessage(envelope.chatId, [
|
|
926
|
+
`Channel: ${this.name}`,
|
|
927
|
+
// Only the basename — don't leak the absolute cwd to group members.
|
|
928
|
+
`Workspace: ${basename(this.config.cwd)}`,
|
|
929
|
+
`Session: ${active ? "active" : "none"}${scopeNote}`
|
|
930
|
+
].join("\n"));
|
|
931
|
+
return true;
|
|
932
|
+
});
|
|
627
933
|
this.registerCommand("help", async (envelope) => {
|
|
628
934
|
const lines = [
|
|
629
935
|
"Commands:",
|
|
630
936
|
"/help \u2014 Show this help",
|
|
631
|
-
"/clear \u2014 Clear your session (aliases: /reset, /new)",
|
|
937
|
+
this.isSharedSession(envelope) ? "/clear confirm \u2014 Clear the shared session (aliases: /reset, /new)" : "/clear \u2014 Clear your session (aliases: /reset, /new)",
|
|
938
|
+
"/who \u2014 Show current session & workspace",
|
|
632
939
|
"/status \u2014 Show session info"
|
|
633
940
|
];
|
|
634
|
-
const sharedCmds = /* @__PURE__ */ new Set([
|
|
941
|
+
const sharedCmds = /* @__PURE__ */ new Set([
|
|
942
|
+
"help",
|
|
943
|
+
"clear",
|
|
944
|
+
"reset",
|
|
945
|
+
"new",
|
|
946
|
+
"who",
|
|
947
|
+
"status"
|
|
948
|
+
]);
|
|
635
949
|
const platformCmds = [...this.commands.keys()].filter((c) => !sharedCmds.has(c));
|
|
636
950
|
if (platformCmds.length > 0) {
|
|
637
951
|
for (const cmd of platformCmds) {
|
|
638
952
|
lines.push(`/${cmd}`);
|
|
639
953
|
}
|
|
640
954
|
}
|
|
641
|
-
const
|
|
955
|
+
const sessionId = this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
956
|
+
const agentCommands = sessionId ? this.getAgentCommandsForSession(sessionId) : this.bridge.availableCommands;
|
|
642
957
|
if (agentCommands.length > 0) {
|
|
643
958
|
lines.push("", "Agent commands (forwarded to Qwen Code):");
|
|
644
959
|
for (const cmd of agentCommands) {
|
|
@@ -650,7 +965,11 @@ var ChannelBase = class {
|
|
|
650
965
|
return true;
|
|
651
966
|
});
|
|
652
967
|
this.registerCommand("status", async (envelope) => {
|
|
653
|
-
|
|
968
|
+
if (!this.isAuthorizedForSharedSession(envelope)) {
|
|
969
|
+
await this.sendMessage(envelope.chatId, "Only authorized members can view this shared session.");
|
|
970
|
+
return true;
|
|
971
|
+
}
|
|
972
|
+
const hasSession = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
654
973
|
const policy = this.config.senderPolicy;
|
|
655
974
|
const lines = [
|
|
656
975
|
`Session: ${hasSession ? "active" : "none"}`,
|
|
@@ -666,17 +985,150 @@ var ChannelBase = class {
|
|
|
666
985
|
const parsed = this.parseCommand(text);
|
|
667
986
|
return parsed !== null && this.commands.has(parsed.command);
|
|
668
987
|
}
|
|
988
|
+
findActiveSessionId(envelope) {
|
|
989
|
+
const sessionId = this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
990
|
+
return sessionId && this.activePrompts.has(sessionId) ? sessionId : void 0;
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Whether the resolved session is SHARED across senders. `single` collapses
|
|
994
|
+
* the whole channel to one `__single__` session for EVERY sender — group OR
|
|
995
|
+
* DM — so it is ALWAYS shared (even a DM maps to `__single__`). `thread` is
|
|
996
|
+
* shared only in a group (a DM maps to the lone caller's own chat). `user` is
|
|
997
|
+
* per-sender, never shared. Drives both the destructive-/clear confirm gate
|
|
998
|
+
* and the host-shell (`!`) gate.
|
|
999
|
+
*/
|
|
1000
|
+
isSharedSession(envelope) {
|
|
1001
|
+
return this.config.sessionScope === "single" || envelope.isGroup && this.config.sessionScope === "thread";
|
|
1002
|
+
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Whether `envelope.senderId` may act on the resolved session's destructive or
|
|
1005
|
+
* workspace-leaking commands (/clear, /who). A SHARED session with a non-empty
|
|
1006
|
+
* allowedUsers list is restricted to those members; a per-user session, or one
|
|
1007
|
+
* with no allowlist, is unrestricted. Shared verbatim by /clear and /who so the
|
|
1008
|
+
* gate can't drift; each caller sends its own rejection wording.
|
|
1009
|
+
*/
|
|
1010
|
+
isAuthorizedForSharedSession(envelope) {
|
|
1011
|
+
if (!this.isSharedSession(envelope))
|
|
1012
|
+
return true;
|
|
1013
|
+
const authorized = this.config.allowedUsers;
|
|
1014
|
+
return authorized.length === 0 || authorized.includes(envelope.senderId);
|
|
1015
|
+
}
|
|
1016
|
+
stopActiveStreaming(active, sessionId, reason) {
|
|
1017
|
+
try {
|
|
1018
|
+
active.stopStreaming?.();
|
|
1019
|
+
} catch (err) {
|
|
1020
|
+
process.stderr.write(`[${this.name}] stopStreaming threw during ${reason} for session ${sessionId}: ${err instanceof Error ? err.message : err}
|
|
1021
|
+
`);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* Cancel the active turn and wait (bounded) for it to wind down. Stops the
|
|
1026
|
+
* BlockStreamer so buffered text can't leak via the idle timer, then fires a
|
|
1027
|
+
* best-effort cancelSession (NOT awaited — a wedged child/daemon can leave the
|
|
1028
|
+
* request pending forever). Returns true if active.done settled first, false
|
|
1029
|
+
* if the CLEAR_CANCEL_TIMEOUT_MS bound won (the turn never wound down). Used by
|
|
1030
|
+
* /clear, which genuinely EVICTS the session and so must proceed even when the
|
|
1031
|
+
* turn is wedged. Steer no longer uses this: it best-effort cancels then chains
|
|
1032
|
+
* the new turn behind the old one (see handleInbound), so it never needs to
|
|
1033
|
+
* proceed past a still-active turn.
|
|
1034
|
+
*/
|
|
1035
|
+
async cancelAndAwaitActive(active, sessionId) {
|
|
1036
|
+
active.cancelled = true;
|
|
1037
|
+
this.stopActiveStreaming(active, sessionId, "cancel");
|
|
1038
|
+
void this.bridge.cancelSession(sessionId).catch((err) => {
|
|
1039
|
+
process.stderr.write(`[${this.name}] cancelSession failed for session=${sessionId} (clear/await): ${err instanceof Error ? err.message : err}
|
|
1040
|
+
`);
|
|
1041
|
+
});
|
|
1042
|
+
let timer;
|
|
1043
|
+
const settled = await Promise.race([
|
|
1044
|
+
active.done.then(() => true),
|
|
1045
|
+
new Promise((resolve2) => {
|
|
1046
|
+
timer = setTimeout(() => resolve2(false), CLEAR_CANCEL_TIMEOUT_MS);
|
|
1047
|
+
})
|
|
1048
|
+
]);
|
|
1049
|
+
clearTimeout(timer);
|
|
1050
|
+
return settled;
|
|
1051
|
+
}
|
|
669
1052
|
/**
|
|
670
1053
|
* Parse a slash command from message text.
|
|
671
|
-
* Returns { command, args } or null if not a slash command.
|
|
1054
|
+
* Returns { command, raw, args } or null if not a slash command. `command` is
|
|
1055
|
+
* lowercased for case-insensitive LOCAL dispatch (registerCommand lowercases the
|
|
1056
|
+
* names it stores); `raw` keeps the typed case so agent-command matching can be
|
|
1057
|
+
* CASE-SENSITIVE, mirroring the CLI's parseSlashCommand (`cmd.name === part`).
|
|
672
1058
|
*/
|
|
673
1059
|
parseCommand(text) {
|
|
674
|
-
|
|
1060
|
+
const trimmed = text.trim();
|
|
1061
|
+
if (!trimmed.startsWith("/"))
|
|
675
1062
|
return null;
|
|
676
|
-
const match =
|
|
1063
|
+
const match = trimmed.match(PARSE_COMMAND_RE);
|
|
677
1064
|
if (!match)
|
|
678
1065
|
return null;
|
|
679
|
-
return {
|
|
1066
|
+
return {
|
|
1067
|
+
command: match[1].toLowerCase(),
|
|
1068
|
+
raw: match[1],
|
|
1069
|
+
args: match[2].trim()
|
|
1070
|
+
};
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* Whether `text` is a real slash command rather than prose that merely starts
|
|
1074
|
+
* with `/`. A command's first whitespace-delimited token must match
|
|
1075
|
+
* parseCommand()'s charset — `[a-zA-Z0-9_:-]+`, plus an optional `@botname`
|
|
1076
|
+
* suffix — and not be a `//` line comment or `/*` block comment. Slash-prefixed
|
|
1077
|
+
* paths (`/tmp/foo`), comments, and a bare `/` are prose and keep their
|
|
1078
|
+
* `[sender]` tag.
|
|
1079
|
+
*
|
|
1080
|
+
* Intentionally stricter than the CLI's looser classifier (cli
|
|
1081
|
+
* `ui/utils/commandUtils.ts`), which forwards any non-comment, non-path
|
|
1082
|
+
* `/<token>` (e.g. `/café`, a zero-width-laden token). Such inputs aren't
|
|
1083
|
+
* runnable commands, and in a SHARED group session forwarding them unattributed
|
|
1084
|
+
* is worse than a redundant tag — so anything off the command charset is
|
|
1085
|
+
* treated as prose and keeps its `[sender]` tag. Purely lexical — never
|
|
1086
|
+
* consults the async command list, so it can't race a fresh session.
|
|
1087
|
+
*/
|
|
1088
|
+
isSlashCommand(text) {
|
|
1089
|
+
const trimmed = text.trim();
|
|
1090
|
+
if (!trimmed.startsWith("/") || trimmed.startsWith("//") || trimmed.startsWith("/*")) {
|
|
1091
|
+
return false;
|
|
1092
|
+
}
|
|
1093
|
+
const firstToken = trimmed.slice(1).split(/\s+/u)[0] ?? "";
|
|
1094
|
+
return COMMAND_TOKEN_RE.test(firstToken);
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* Whether `text` names a command this channel can actually run: a locally
|
|
1098
|
+
* registered command (`this.commands`, e.g. /clear, /who) OR an agent command
|
|
1099
|
+
* THIS session exposes — by canonical name OR alias (e.g. `/summarize` for
|
|
1100
|
+
* `/compress`). Paired with isSlashCommand so the `[sender]` attribution tag is
|
|
1101
|
+
* suppressed ONLY for RECOGNIZED commands; command-SHAPED-but-unrecognized text
|
|
1102
|
+
* (e.g. `/x\n[SYSTEM]: …`) keeps its tag rather than reaching a shared group
|
|
1103
|
+
* unattributed, where an injected second line is more likely read as a system
|
|
1104
|
+
* directive. Purely synchronous, like isSlashCommand: it reads the session's
|
|
1105
|
+
* availableCommands snapshot WITHOUT awaiting, so it never races a fresh session
|
|
1106
|
+
* (a genuine agent command sent before the snapshot loads is treated as
|
|
1107
|
+
* unrecognized and KEEPS its tag — the safe default).
|
|
1108
|
+
*/
|
|
1109
|
+
isRecognizedCommand(text, sessionId) {
|
|
1110
|
+
const parsed = this.parseCommand(text);
|
|
1111
|
+
if (!parsed)
|
|
1112
|
+
return false;
|
|
1113
|
+
if (this.commands.has(parsed.command))
|
|
1114
|
+
return true;
|
|
1115
|
+
const token = text.trim().slice(1).split(/\s+/u)[0] ?? "";
|
|
1116
|
+
return this.getAgentCommandsForSession(sessionId).some((cmd) => cmd.name === token || Array.isArray(cmd.altNames) && cmd.altNames.includes(token));
|
|
1117
|
+
}
|
|
1118
|
+
/**
|
|
1119
|
+
* The agent-command snapshot for THIS session. DaemonChannelBridge keys
|
|
1120
|
+
* commands per session, so its global `availableCommands` getter can return
|
|
1121
|
+
* ANOTHER session's list — prefer its getAvailableCommands(sessionId) when
|
|
1122
|
+
* present. AcpBridge runs a single agent and exposes only the global getter
|
|
1123
|
+
* (inherently session-correct), so fall back to it. Synchronous, matching
|
|
1124
|
+
* isRecognizedCommand's no-await contract.
|
|
1125
|
+
*/
|
|
1126
|
+
getAgentCommandsForSession(sessionId) {
|
|
1127
|
+
const bridge = this.bridge;
|
|
1128
|
+
if (typeof bridge.getAvailableCommands === "function") {
|
|
1129
|
+
return bridge.getAvailableCommands(sessionId) ?? [];
|
|
1130
|
+
}
|
|
1131
|
+
return bridge.availableCommands ?? [];
|
|
680
1132
|
}
|
|
681
1133
|
async handleInbound(envelope) {
|
|
682
1134
|
const groupResult = this.groupGate.check(envelope);
|
|
@@ -699,13 +1151,29 @@ var ChannelBase = class {
|
|
|
699
1151
|
return;
|
|
700
1152
|
}
|
|
701
1153
|
}
|
|
1154
|
+
const bangText = envelope.text.trimStart();
|
|
1155
|
+
if (bangText.startsWith("!")) {
|
|
1156
|
+
if (envelope.isGroup || this.isSharedSession(envelope)) {
|
|
1157
|
+
const who = sanitizeSenderName(envelope.senderName || envelope.senderId || "unknown");
|
|
1158
|
+
process.stderr.write(`[${this.name}] blocked ! shell command from ${who} (sender ${envelope.senderId}) in chat ${sanitizeLogText(envelope.chatId, 64)}
|
|
1159
|
+
`);
|
|
1160
|
+
}
|
|
1161
|
+
if (envelope.isGroup) {
|
|
1162
|
+
await this.sendMessage(envelope.chatId, "Shell commands (`!`) are disabled in group chats.");
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
if (this.isSharedSession(envelope)) {
|
|
1166
|
+
await this.sendMessage(envelope.chatId, "Shell commands (`!`) are disabled in shared sessions.");
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
702
1170
|
const sessionId = await this.router.resolve(this.name, envelope.senderId, envelope.chatId, envelope.threadId, this.config.cwd);
|
|
703
|
-
if (
|
|
704
|
-
const cmd =
|
|
705
|
-
const bridgeShellCommand = this.bridge
|
|
706
|
-
if (cmd &&
|
|
1171
|
+
if (bangText.startsWith("!")) {
|
|
1172
|
+
const cmd = bangText.slice(1).trim();
|
|
1173
|
+
const bridgeShellCommand = this.bridge.shellCommand;
|
|
1174
|
+
if (cmd && bridgeShellCommand) {
|
|
707
1175
|
try {
|
|
708
|
-
const result2 = await
|
|
1176
|
+
const result2 = await this.bridge.shellCommand(sessionId, cmd);
|
|
709
1177
|
const longestRun = Math.max(0, ...Array.from((result2.output || "").matchAll(/`+/g), (m) => m[0].length));
|
|
710
1178
|
const fence = "`".repeat(Math.max(3, longestRun + 1));
|
|
711
1179
|
const output = result2.output ? `${fence}
|
|
@@ -722,8 +1190,13 @@ ${output}${exitLine}`);
|
|
|
722
1190
|
}
|
|
723
1191
|
}
|
|
724
1192
|
let promptText = envelope.text;
|
|
1193
|
+
if ((envelope.isGroup || this.config.sessionScope === "single") && !envelope.alreadyPrefixed && !(this.isSlashCommand(envelope.text) && this.isRecognizedCommand(envelope.text, sessionId))) {
|
|
1194
|
+
const who = sanitizeSenderName(envelope.senderName || envelope.senderId || "unknown");
|
|
1195
|
+
promptText = `[${who}] ${sanitizePromptText(promptText)}`;
|
|
1196
|
+
}
|
|
725
1197
|
if (envelope.referencedText) {
|
|
726
|
-
|
|
1198
|
+
const quoted = sanitizeQuotedText(envelope.referencedText, 500);
|
|
1199
|
+
promptText = `[Replying to: "${quoted}"]
|
|
727
1200
|
|
|
728
1201
|
${promptText}`;
|
|
729
1202
|
}
|
|
@@ -737,8 +1210,9 @@ ${promptText}`;
|
|
|
737
1210
|
imageMimeType = att.mimeType;
|
|
738
1211
|
} else if (att.filePath) {
|
|
739
1212
|
const label = att.type === "file" ? "file" : att.type;
|
|
740
|
-
const name = att.fileName ? ` "${att.fileName}"` : "";
|
|
741
|
-
|
|
1213
|
+
const name = att.fileName ? ` "${sanitizeQuotedText(att.fileName, 128)}"` : "";
|
|
1214
|
+
const renderedPath = sanitizePromptPath(att.filePath);
|
|
1215
|
+
filePaths.push(`User sent a ${label}${name}. It has been saved to: ${renderedPath}`);
|
|
742
1216
|
}
|
|
743
1217
|
}
|
|
744
1218
|
if (filePaths.length > 0) {
|
|
@@ -754,6 +1228,7 @@ ${promptText}`;
|
|
|
754
1228
|
const groupCfg = envelope.isGroup ? this.config.groups[envelope.chatId] || this.config.groups["*"] : void 0;
|
|
755
1229
|
const mode = groupCfg?.dispatchMode || this.config.dispatchMode || "steer";
|
|
756
1230
|
const active = this.activePrompts.get(sessionId);
|
|
1231
|
+
let steerWatchdog;
|
|
757
1232
|
if (active) {
|
|
758
1233
|
switch (mode) {
|
|
759
1234
|
case "collect": {
|
|
@@ -766,10 +1241,26 @@ ${promptText}`;
|
|
|
766
1241
|
return;
|
|
767
1242
|
}
|
|
768
1243
|
case "steer": {
|
|
1244
|
+
if (!this.isAuthorizedForSharedSession(envelope)) {
|
|
1245
|
+
process.stderr.write(`[${this.name}] steer denied for ${envelope.senderId} in shared session (chat=${sanitizeLogText(envelope.chatId, 64)}); queuing instead
|
|
1246
|
+
`);
|
|
1247
|
+
break;
|
|
1248
|
+
}
|
|
769
1249
|
active.cancelled = true;
|
|
770
|
-
|
|
1250
|
+
process.stderr.write(`[${this.name}] steer: cancelled active turn for ${envelope.senderId} in session ${sessionId}
|
|
1251
|
+
`);
|
|
1252
|
+
this.stopActiveStreaming(active, sessionId, "steer");
|
|
1253
|
+
void this.bridge.cancelSession(sessionId).catch((err) => {
|
|
1254
|
+
process.stderr.write(`[${this.name}] cancelSession failed for session=${sessionId} (steer): ${err instanceof Error ? err.message : err}
|
|
1255
|
+
`);
|
|
771
1256
|
});
|
|
772
|
-
|
|
1257
|
+
steerWatchdog = setTimeout(() => {
|
|
1258
|
+
if (this.activePrompts.get(sessionId) === active) {
|
|
1259
|
+
process.stderr.write(`[${this.name}] steer queued behind active turn for session ${sessionId}: still waiting after ${CLEAR_CANCEL_TIMEOUT_MS}ms (use /clear to recover)
|
|
1260
|
+
`);
|
|
1261
|
+
}
|
|
1262
|
+
}, CLEAR_CANCEL_TIMEOUT_MS);
|
|
1263
|
+
steerWatchdog.unref?.();
|
|
773
1264
|
promptText = `[The user sent a new message while you were working. Their previous request has been cancelled.]
|
|
774
1265
|
|
|
775
1266
|
${promptText}`;
|
|
@@ -785,14 +1276,28 @@ ${promptText}`;
|
|
|
785
1276
|
}
|
|
786
1277
|
}
|
|
787
1278
|
const prev = this.sessionQueues.get(sessionId) ?? Promise.resolve();
|
|
1279
|
+
const generation = this.sessionGenerations.get(sessionId) ?? 0;
|
|
788
1280
|
const useBlockStreaming = this.config.blockStreaming === "on";
|
|
789
1281
|
const current = prev.then(async () => {
|
|
1282
|
+
clearTimeout(steerWatchdog);
|
|
1283
|
+
if ((this.sessionGenerations.get(sessionId) ?? 0) !== generation) {
|
|
1284
|
+
const loggedText = sanitizeLogText(envelope.text, 80);
|
|
1285
|
+
process.stderr.write(`[${this.name}] dropped queued turn from ${envelope.senderId} for session ${sessionId}: session was cleared before it ran (text: ${loggedText})
|
|
1286
|
+
`);
|
|
1287
|
+
return;
|
|
1288
|
+
}
|
|
790
1289
|
let doneResolve = /* @__PURE__ */ __name(() => {
|
|
791
1290
|
}, "doneResolve");
|
|
792
1291
|
const done = new Promise((r) => {
|
|
793
1292
|
doneResolve = r;
|
|
794
1293
|
});
|
|
795
|
-
const promptState = {
|
|
1294
|
+
const promptState = {
|
|
1295
|
+
cancelled: false,
|
|
1296
|
+
done,
|
|
1297
|
+
resolve: doneResolve,
|
|
1298
|
+
chatId: envelope.chatId,
|
|
1299
|
+
messageId: envelope.messageId
|
|
1300
|
+
};
|
|
796
1301
|
this.activePrompts.set(sessionId, promptState);
|
|
797
1302
|
this.onPromptStart(envelope.chatId, sessionId, envelope.messageId);
|
|
798
1303
|
const streamer = useBlockStreaming ? new BlockStreamer({
|
|
@@ -801,18 +1306,23 @@ ${promptText}`;
|
|
|
801
1306
|
idleMs: this.config.blockStreamingCoalesce?.idleMs ?? 1500,
|
|
802
1307
|
send: /* @__PURE__ */ __name((text) => this.sendMessage(envelope.chatId, text), "send")
|
|
803
1308
|
}) : null;
|
|
1309
|
+
promptState.stopStreaming = () => streamer?.stop();
|
|
804
1310
|
const onChunk = /* @__PURE__ */ __name((sid, chunk) => {
|
|
805
|
-
if (sid === sessionId) {
|
|
1311
|
+
if (sid === sessionId && !promptState.cancelled) {
|
|
806
1312
|
this.onResponseChunk(envelope.chatId, chunk, sessionId);
|
|
807
1313
|
streamer?.push(chunk);
|
|
808
1314
|
}
|
|
809
1315
|
}, "onChunk");
|
|
810
|
-
this.bridge
|
|
1316
|
+
const promptBridge = this.bridge;
|
|
1317
|
+
promptBridge.on("textChunk", onChunk);
|
|
811
1318
|
try {
|
|
812
|
-
const response = await
|
|
1319
|
+
const response = await promptBridge.prompt(sessionId, promptText, {
|
|
813
1320
|
imageBase64,
|
|
814
1321
|
imageMimeType
|
|
815
1322
|
});
|
|
1323
|
+
if (promptState.cancelRequested && !promptState.cancelled) {
|
|
1324
|
+
promptState.cancelled = await promptState.cancelRequested;
|
|
1325
|
+
}
|
|
816
1326
|
if (!promptState.cancelled && response) {
|
|
817
1327
|
if (streamer) {
|
|
818
1328
|
await streamer.flush();
|
|
@@ -821,25 +1331,41 @@ ${promptText}`;
|
|
|
821
1331
|
}
|
|
822
1332
|
}
|
|
823
1333
|
} finally {
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
this.activePrompts.
|
|
1334
|
+
promptBridge.off("textChunk", onChunk);
|
|
1335
|
+
streamer?.stop();
|
|
1336
|
+
const stillCurrent = this.activePrompts.get(sessionId) === promptState;
|
|
1337
|
+
if (!promptState.clearEvicted) {
|
|
1338
|
+
try {
|
|
1339
|
+
this.onPromptEnd(envelope.chatId, sessionId, envelope.messageId);
|
|
1340
|
+
} catch (err) {
|
|
1341
|
+
process.stderr.write(`[${this.name}] onPromptEnd threw in finally for session ${sessionId}: ${err instanceof Error ? err.message : err}
|
|
1342
|
+
`);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
if (stillCurrent) {
|
|
1346
|
+
this.activePrompts.delete(sessionId);
|
|
1347
|
+
}
|
|
827
1348
|
promptState.resolve();
|
|
828
1349
|
const buffer = this.collectBuffers.get(sessionId);
|
|
829
|
-
if (buffer && buffer.length > 0) {
|
|
1350
|
+
if (stillCurrent && buffer && buffer.length > 0) {
|
|
830
1351
|
this.collectBuffers.delete(sessionId);
|
|
1352
|
+
const lost = buffer.length;
|
|
831
1353
|
const coalesced = buffer.map((b) => b.text).join("\n\n");
|
|
832
1354
|
const lastEnvelope = buffer[buffer.length - 1].envelope;
|
|
833
1355
|
const syntheticEnvelope = {
|
|
834
1356
|
...lastEnvelope,
|
|
835
1357
|
text: coalesced,
|
|
1358
|
+
// Coalesced text already carries each message's [sender] prefix.
|
|
1359
|
+
alreadyPrefixed: true,
|
|
836
1360
|
// Clear attachments/references — already resolved in original text
|
|
837
1361
|
referencedText: void 0,
|
|
838
1362
|
attachments: void 0,
|
|
839
1363
|
imageBase64: void 0,
|
|
840
1364
|
imageMimeType: void 0
|
|
841
1365
|
};
|
|
842
|
-
this.handleInbound(syntheticEnvelope).catch(() => {
|
|
1366
|
+
this.handleInbound(syntheticEnvelope).catch((err) => {
|
|
1367
|
+
process.stderr.write(`[${this.name}] dropped ${lost} buffered message(s) on collect re-entry for session ${sessionId} (last sender ${lastEnvelope.senderId}): ${err instanceof Error ? err.message : String(err)}
|
|
1368
|
+
`);
|
|
843
1369
|
});
|
|
844
1370
|
}
|
|
845
1371
|
}
|
|
@@ -868,6 +1394,19 @@ init_esbuild_shims();
|
|
|
868
1394
|
import { spawn } from "node:child_process";
|
|
869
1395
|
import { Readable, Writable } from "node:stream";
|
|
870
1396
|
import { EventEmitter } from "node:events";
|
|
1397
|
+
function readAvailableCommandAltNames(raw) {
|
|
1398
|
+
if (typeof raw !== "object" || raw === null)
|
|
1399
|
+
return void 0;
|
|
1400
|
+
const record = raw;
|
|
1401
|
+
const meta = record["_meta"];
|
|
1402
|
+
const fromMeta = typeof meta === "object" && meta !== null ? meta["altNames"] : void 0;
|
|
1403
|
+
const source = Array.isArray(record["altNames"]) ? record["altNames"] : Array.isArray(fromMeta) ? fromMeta : void 0;
|
|
1404
|
+
if (!source)
|
|
1405
|
+
return void 0;
|
|
1406
|
+
const names = source.filter((n) => typeof n === "string");
|
|
1407
|
+
return names.length > 0 ? names : void 0;
|
|
1408
|
+
}
|
|
1409
|
+
__name(readAvailableCommandAltNames, "readAvailableCommandAltNames");
|
|
871
1410
|
var AcpBridge = class extends EventEmitter {
|
|
872
1411
|
static {
|
|
873
1412
|
__name(this, "AcpBridge");
|
|
@@ -1021,7 +1560,10 @@ var AcpBridge = class extends EventEmitter {
|
|
|
1021
1560
|
}
|
|
1022
1561
|
case "available_commands_update": {
|
|
1023
1562
|
if (Array.isArray(update["availableCommands"])) {
|
|
1024
|
-
this._availableCommands = update["availableCommands"]
|
|
1563
|
+
this._availableCommands = update["availableCommands"].map((cmd) => {
|
|
1564
|
+
const altNames = readAvailableCommandAltNames(cmd);
|
|
1565
|
+
return altNames ? { ...cmd, altNames } : cmd;
|
|
1566
|
+
});
|
|
1025
1567
|
}
|
|
1026
1568
|
break;
|
|
1027
1569
|
}
|
|
@@ -1047,5 +1589,8 @@ export {
|
|
|
1047
1589
|
AcpBridge,
|
|
1048
1590
|
PairingStore,
|
|
1049
1591
|
SessionRouter,
|
|
1592
|
+
sanitizeSenderName,
|
|
1593
|
+
sanitizePromptText,
|
|
1594
|
+
sanitizeLogText,
|
|
1050
1595
|
ChannelBase
|
|
1051
1596
|
};
|