@qwen-code/qwen-code 0.22.0 → 0.22.2-nightly.20260828.7357136dd1
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/computer-use/SKILL.md +229 -0
- package/bundled/computer-use/agents/openai.yaml +4 -0
- package/bundled/coordinate/SKILL.md +5 -5
- package/bundled/goal-draft/SKILL.md +109 -0
- package/bundled/qc-helper/docs/configuration/auth.md +7 -7
- package/bundled/qc-helper/docs/configuration/model-providers.md +70 -9
- package/bundled/qc-helper/docs/configuration/settings.md +48 -48
- package/bundled/qc-helper/docs/extension/introduction.md +3 -1
- package/bundled/qc-helper/docs/features/_meta.ts +1 -0
- package/bundled/qc-helper/docs/features/channels/_meta.ts +1 -0
- package/bundled/qc-helper/docs/features/channels/dingtalk.md +12 -0
- package/bundled/qc-helper/docs/features/channels/dws.md +120 -0
- package/bundled/qc-helper/docs/features/channels/overview.md +37 -6
- package/bundled/qc-helper/docs/features/code-review.md +27 -23
- package/bundled/qc-helper/docs/features/commands.md +4 -3
- package/bundled/qc-helper/docs/features/computer-use.md +57 -51
- package/bundled/qc-helper/docs/features/goals.md +58 -0
- package/bundled/qc-helper/docs/features/headless.md +13 -11
- package/bundled/qc-helper/docs/features/mcp.md +35 -12
- package/bundled/qc-helper/docs/features/status-line.md +3 -3
- package/bundled/qc-helper/docs/features/sub-agents.md +1 -1
- package/bundled/qc-helper/docs/features/worktree.md +1 -1
- package/bundled/qc-helper/docs/overview.md +1 -1
- package/bundled/qc-helper/docs/quickstart.md +1 -1
- package/bundled/qc-helper/docs/qwen-serve.md +29 -6
- package/bundled/review/SKILL.md +185 -435
- package/bundled/review/references/aone.md +18 -0
- package/bundled/review/references/persistence.md +109 -0
- package/bundled/review/references/posting.md +272 -0
- package/chunks/MaxSizedBox-LMWXCLDV.js +110 -0
- package/chunks/{StandaloneSessionPicker-KTHPCSR6.js → StandaloneSessionPicker-W3F7U3JL.js} +87 -88
- package/chunks/{acp-startup-profiler-T4GEAPLX.js → acp-startup-profiler-GNYAL6LI.js} +2 -2
- package/chunks/{acpAgent-CRY3JZ5O.js → acpAgent-EYZ6EEHD.js} +3223 -2145
- package/chunks/agent-RBLGBY65.js +95 -0
- package/chunks/agent-headless-L5T4Y4OZ.js +85 -0
- package/chunks/{anthropicContentGenerator-Y2OX6LS4.js → anthropicContentGenerator-GMHML35Q.js} +128 -59
- package/chunks/{artifact-tool-SBXMTVCB.js → artifact-tool-XSMPHZBT.js} +11 -3
- package/chunks/{askUserQuestion-KEQJRFD6.js → askUserQuestion-4SWJY65V.js} +2 -2
- package/chunks/bridge-5LR2F2ND.js +119 -0
- package/chunks/{ca-D3F4S6NG.js → ca-7HPYOYSZ.js} +3 -0
- package/chunks/{channel-management-service-IRTT7RNE.js → channel-management-service-KHR3N6CH.js} +6 -6
- package/chunks/channel-settings-store-4FOAZW72.js +120 -0
- package/chunks/{channel-worker-group-BJNMR2MP.js → channel-worker-group-6NA6BBZV.js} +9 -7
- package/chunks/{channel-worker-manager-EMA7CQDJ.js → channel-worker-manager-NEZ2J6RK.js} +11 -8
- package/chunks/{channel-worker-supervisor-F6I6WSWW.js → channel-worker-supervisor-FKU7DIHJ.js} +12 -6
- package/chunks/{chunk-HDKCKCYO.js → chunk-23RFD54N.js} +1 -1
- package/chunks/{read-package-up-UXSW3YPB.js → chunk-2EAZ43JQ.js} +1 -0
- package/chunks/{chunk-KM73TBQ4.js → chunk-2LD5U7Q3.js} +31 -0
- package/chunks/{chunk-GIWB5QLV.js → chunk-2SXSLSQE.js} +4 -4
- package/chunks/chunk-2TNL5KPM.js +49 -0
- package/chunks/chunk-335CJSNF.js +2320 -0
- package/chunks/chunk-36LGBEM6.js +130 -0
- package/chunks/{chunk-O5BDEWBC.js → chunk-3JXM2CVW.js} +3 -3
- package/chunks/{chunk-2E3V7G6Y.js → chunk-3K6TUZHS.js} +55 -50
- package/chunks/{chunk-WWUSHZ7M.js → chunk-3OOXT6CR.js} +5 -5
- package/chunks/{chunk-2UZJWSVD.js → chunk-3WK3QDNV.js} +2 -2
- package/chunks/{chunk-4J7OPYNS.js → chunk-3XOKEVII.js} +6 -6
- package/chunks/chunk-3Y34Q3JV.js +1584 -0
- package/chunks/{chunk-YMVFIYHV.js → chunk-42IDLQWS.js} +26 -20
- package/chunks/{chunk-7HFULI45.js → chunk-454XFGH4.js} +3 -3
- package/chunks/chunk-4CMJKUND.js +95 -0
- package/chunks/{chunk-XLDFW44M.js → chunk-4K7KNVWH.js} +3 -3
- package/chunks/{chunk-YDJRMQU4.js → chunk-4QHPPXK2.js} +16 -8
- package/chunks/{chunk-7KOFLDEP.js → chunk-5JTIYALA.js} +1 -1
- package/chunks/{chunk-CXJHQVEK.js → chunk-5PA6UEYA.js} +4 -0
- package/chunks/{chunk-7NBVNAJJ.js → chunk-62GQFYID.js} +3 -3
- package/chunks/{chunk-JGUBDRBC.js → chunk-6DIGWMGT.js} +3 -3
- package/chunks/{chunk-X7V3N6YH.js → chunk-6JCGO55P.js} +885 -3380
- package/chunks/{chunk-HIK2OF33.js → chunk-6PJOTWAN.js} +1 -1
- package/chunks/{chunk-VUMLT7E5.js → chunk-6W2IK5KC.js} +956 -17
- package/chunks/{chunk-S4C4JJKE.js → chunk-6ZZBRRKQ.js} +5 -5
- package/chunks/{chunk-W4CRPSC5.js → chunk-7EQQR46F.js} +1 -1
- package/chunks/{chunk-PHJJ3RVO.js → chunk-7GWQW5ID.js} +7 -15
- package/chunks/chunk-7J6OTNGO.js +424 -0
- package/chunks/{chunk-URN2JIAQ.js → chunk-7TUBJBBJ.js} +5 -1
- package/chunks/{chunk-A5F63FEW.js → chunk-7VD6IPQM.js} +1 -1
- package/chunks/{chunk-36OF5PRW.js → chunk-ABS25QER.js} +7 -3
- package/chunks/{chunk-UU4WC7DK.js → chunk-AJGR6E6Z.js} +117 -30
- package/chunks/{chunk-MQP3MHWN.js → chunk-AU3MMVJF.js} +43 -20
- package/chunks/{chunk-ZS5BHEPD.js → chunk-AXC26UCQ.js} +1 -1
- package/chunks/{chunk-PDL6LKB5.js → chunk-AY3BNYEQ.js} +1 -1
- package/chunks/{chunk-O5Z7EB6Y.js → chunk-AYHQZBJ4.js} +170 -207
- package/chunks/{chunk-WRT324N6.js → chunk-B466ZSHZ.js} +3 -3
- package/chunks/{chunk-5XHAOUWD.js → chunk-B5OUL5EL.js} +3 -3
- package/chunks/{chunk-GAHYKJPV.js → chunk-B5PHIJGI.js} +186 -60
- package/chunks/{chunk-IBJ5S45N.js → chunk-B7CDU2SL.js} +2 -2
- package/chunks/{chunk-UZPYR3EV.js → chunk-BFPESSTR.js} +1 -1
- package/chunks/{chunk-43T5WDQ7.js → chunk-BFSFJR3D.js} +1 -1
- package/chunks/{chunk-IWOWEENB.js → chunk-BKWNKLZB.js} +404 -44
- package/chunks/{chunk-3CLIDIVQ.js → chunk-BOWFGCEU.js} +3 -3
- package/chunks/{chunk-BB5IMQCV.js → chunk-BPAKYXQ3.js} +2 -2
- package/chunks/{chunk-ZRYQMWEP.js → chunk-BWORX6FA.js} +2 -2
- package/chunks/{chunk-AP3B7LKD.js → chunk-BXDAPVQU.js} +2 -2
- package/chunks/{chunk-NUGZAFR2.js → chunk-C2X7KY45.js} +147 -6
- package/chunks/{chunk-MZKWFWVG.js → chunk-C3KURPP7.js} +6 -6
- package/chunks/{chunk-WZDM44SB.js → chunk-CAJTKR6W.js} +7 -7
- package/chunks/{chunk-DB2OKNTU.js → chunk-CFJY4DGX.js} +2 -2
- package/chunks/{chunk-QKBYFU2Q.js → chunk-CFKIH3D3.js} +21 -1
- package/chunks/{chunk-I432KXWD.js → chunk-CMHFCLBU.js} +1 -1
- package/chunks/{create-sub-session-XBCVGNFU.js → chunk-CQH5KTKC.js} +4 -6
- package/chunks/{chunk-5HJUPACR.js → chunk-CRKKHXTI.js} +185 -174
- package/chunks/{chunk-L4HVF7LM.js → chunk-CYAUBK46.js} +3 -3
- package/chunks/{chunk-AZF6SME3.js → chunk-D5LUXLUH.js} +3 -3
- package/chunks/{chunk-RPJQ3O4M.js → chunk-DFDWJE6Z.js} +60 -53
- package/chunks/{chunk-KSPRQSB4.js → chunk-DIMEPKCW.js} +52 -12
- package/chunks/chunk-DJBQCO2E.js +842 -0
- package/chunks/{chunk-OVH5Y7KS.js → chunk-DP4MWIM3.js} +19 -3
- package/chunks/{chunk-LI4YHTQK.js → chunk-DYXFD5RO.js} +182 -3
- package/chunks/{chunk-NW35NVFN.js → chunk-E3XVSVKK.js} +26 -4
- package/chunks/{chunk-VSNPOSDN.js → chunk-EB6QRGHM.js} +108 -13
- package/chunks/{chunk-FVBVJEBG.js → chunk-EKNBU2X7.js} +10 -8
- package/chunks/{chunk-DJEHYM4H.js → chunk-EKSCLBBF.js} +3 -3
- package/chunks/{chunk-624NHDGB.js → chunk-ERFDKH32.js} +11 -0
- package/chunks/chunk-EW5VVLJT.js +3525 -0
- package/chunks/{chunk-T6XLJRQY.js → chunk-EWIQ3NAE.js} +38774 -6955
- package/chunks/{chunk-4XPSVIED.js → chunk-FALBWZF4.js} +6 -6
- package/chunks/chunk-FBX2SDQ4.js +48 -0
- package/chunks/{chunk-VMJL7RH6.js → chunk-FEHMA4RO.js} +539 -651
- package/chunks/{chunk-PXOJLJ5W.js → chunk-FMFBLTLY.js} +606 -223
- package/chunks/{chunk-ZLCT4C5A.js → chunk-FTF2YIZ5.js} +1 -1
- package/chunks/{chunk-IOSEW6KP.js → chunk-G6FECKTJ.js} +9 -6
- package/chunks/{chunk-RDG44S5I.js → chunk-GGNE3JAW.js} +2 -2
- package/chunks/chunk-GYTKQYDB.js +23 -0
- package/chunks/{chunk-B6UBOIFE.js → chunk-H33EQVMG.js} +14 -11
- package/chunks/chunk-H72BQNM3.js +645 -0
- package/chunks/{chunk-YZBDKFRQ.js → chunk-HGIMWQVT.js} +995 -892
- package/chunks/chunk-HOCJDUFY.js +20 -0
- package/chunks/{chunk-6UQSU7CQ.js → chunk-HPXV655A.js} +60 -107
- package/chunks/{chunk-45NQKMYB.js → chunk-HVC3PNVC.js} +1 -1
- package/chunks/{chunk-QJHPWLZC.js → chunk-HYDJDQGJ.js} +1 -1
- package/chunks/{chunk-7DMTAQJ6.js → chunk-IB7FQJRY.js} +163 -57
- package/chunks/{chunk-AW27A43Y.js → chunk-ICURCKIU.js} +1428 -120
- package/chunks/{chunk-4F5WV4RF.js → chunk-ISKH4QX3.js} +288 -15
- package/chunks/{chunk-SE55HQGF.js → chunk-J6S24ZJE.js} +92 -70
- package/chunks/{chunk-7DYNJFES.js → chunk-JB4JIVTJ.js} +6 -1
- package/chunks/{chunk-OARZJFZX.js → chunk-JN4IWWMN.js} +4 -4
- package/chunks/{chunk-6PCOVQO2.js → chunk-JUUBLSZU.js} +8 -8
- package/chunks/{chunk-FEMQ6Y7W.js → chunk-JWALNCLT.js} +47 -126
- package/chunks/{chunk-TBVSALA3.js → chunk-K5KXUXWC.js} +3 -3
- package/chunks/{chunk-XYQHT3AW.js → chunk-KTJFDCX4.js} +2 -2
- package/chunks/{chunk-6FRJEE2S.js → chunk-LEFCFTEX.js} +6 -6
- package/chunks/{chunk-BUGYW6FB.js → chunk-LEGTMYAE.js} +1 -1
- package/chunks/{chunk-MPHLIJYL.js → chunk-LLZA2IK3.js} +20 -8
- package/chunks/{chunk-BEGIBJAZ.js → chunk-LNETQKBE.js} +4 -4
- package/chunks/{chunk-KVGR4MCF.js → chunk-LUEHOUHM.js} +3 -3
- package/chunks/{chunk-2A4C2MEG.js → chunk-LVPZO674.js} +35 -12
- package/chunks/{chunk-25NESM4G.js → chunk-M4ORUPYI.js} +29 -5
- package/chunks/{chunk-VFACN2CE.js → chunk-M6FOJXW3.js} +1 -1
- package/chunks/{chunk-FYZMB6DB.js → chunk-MAYHGRP4.js} +12 -12
- package/chunks/{chunk-K3LPAED5.js → chunk-MQM5PJML.js} +24 -4
- package/chunks/{chunk-ZRFUT5VV.js → chunk-MTJN7FOT.js} +7 -7
- package/chunks/{chunk-BGXZBI5B.js → chunk-N4X7G4J2.js} +2 -1
- package/chunks/{chunk-2TRNCJK4.js → chunk-NIFYWDYN.js} +1 -1
- package/chunks/{chunk-SQS55KR4.js → chunk-NU7RKEZR.js} +2 -2
- package/chunks/{chunk-U2UYQMM6.js → chunk-NXMBRVMQ.js} +3 -3
- package/chunks/chunk-OI4NY7RA.js +346 -0
- package/chunks/{chunk-LURJYO2T.js → chunk-OZ6KS6KW.js} +4 -0
- package/chunks/{chunk-K52PZNU4.js → chunk-PDQGMSZK.js} +2 -2
- package/chunks/{chunk-PZRUV52H.js → chunk-PPKZ7JOE.js} +20 -2
- package/chunks/{chunk-SAH4BD2J.js → chunk-PQEISIKS.js} +67 -0
- package/chunks/{chunk-AO4M6NMD.js → chunk-PWIGAMA4.js} +146 -27
- package/chunks/{chunk-S3G6YFQC.js → chunk-QFCDF5PL.js} +1 -1
- package/chunks/chunk-QHMLYMMS.js +32 -0
- package/chunks/{chunk-5XUTPXQU.js → chunk-QPTRRPZU.js} +47 -13
- package/chunks/{chunk-UUQFJILB.js → chunk-RFI4YGMR.js} +34 -267
- package/chunks/{chunk-A6NRNRKE.js → chunk-RFIDDFAL.js} +1 -1
- package/chunks/chunk-RKFGE7JH.js +93 -0
- package/chunks/chunk-RNVFRIOO.js +275 -0
- package/chunks/{chunk-WU5E4HMO.js → chunk-RU7BK6B3.js} +1 -1
- package/chunks/{chunk-J5ZDDUKM.js → chunk-RVXDXY6I.js} +4 -4
- package/chunks/{chunk-OE52YHCZ.js → chunk-RWZB422L.js} +11 -11
- package/chunks/chunk-RXFQM6FQ.js +42 -0
- package/chunks/{chunk-MHG6NDS6.js → chunk-RYNZQGXN.js} +19 -2
- package/chunks/{chunk-4CWDWDL6.js → chunk-SJ5ERAW3.js} +1 -1
- package/chunks/{chunk-Q54XG7IX.js → chunk-SMPR7SPO.js} +1 -1
- package/chunks/{chunk-UIRLQ3N7.js → chunk-SRZZY4IY.js} +3 -3
- package/chunks/{chunk-ND7NOI4P.js → chunk-STUJ7IMZ.js} +0 -92
- package/chunks/{chunk-VYC7XFYV.js → chunk-SUX7HDNA.js} +5 -5
- package/chunks/{chunk-ZZUVC6RI.js → chunk-SVNO44PF.js} +3 -3
- package/chunks/{chunk-FDD2IUEL.js → chunk-SZLBBZH4.js} +370 -35
- package/chunks/{chunk-GHY5OIYP.js → chunk-T2NKRHZZ.js} +13 -13
- package/chunks/chunk-T4EFG2P5.js +113 -0
- package/chunks/{chunk-BMCSKS37.js → chunk-TLHUNMO7.js} +6 -6
- package/chunks/{chunk-JOLJTKIG.js → chunk-TPI7O7HR.js} +1 -1
- package/chunks/{chunk-6QSA4JHL.js → chunk-TQHGJYR3.js} +4492 -1121
- package/chunks/{chunk-ISJMN3ML.js → chunk-TTX2JUE6.js} +1 -1
- package/chunks/{chunk-EFB657PR.js → chunk-TW3E6YNO.js} +3 -3
- package/chunks/{chunk-LD5VIJ7S.js → chunk-TWSKM647.js} +1 -1
- package/chunks/chunk-U54AYGEC.js +337 -0
- package/chunks/{chunk-A5F2YNO6.js → chunk-UHQFIS7N.js} +260 -10
- package/chunks/{chunk-6NFAEG54.js → chunk-UTLCH2FK.js} +24 -13
- package/chunks/{chunk-7JBHIQS2.js → chunk-UY3PBNY3.js} +19 -37
- package/chunks/{chunk-DWCKACFL.js → chunk-V2NDP3I4.js} +2 -2
- package/chunks/{chunk-2GXYDNXP.js → chunk-V2QDDC5J.js} +19 -9
- package/chunks/{chunk-VLRBJ24J.js → chunk-V5J4J5TP.js} +2 -2
- package/chunks/{chunk-X2UOOG5V.js → chunk-VQUGEVGR.js} +1 -1
- package/chunks/{chunk-RDQ7QB4Y.js → chunk-VTHREBQM.js} +2 -2
- package/chunks/{chunk-YLFLHKDL.js → chunk-W3N5XAZ6.js} +1 -1
- package/chunks/{chunk-62JOGZL5.js → chunk-W6DUQ5GA.js} +11497 -10502
- package/chunks/{chunk-P47E4SJC.js → chunk-WALC5VJH.js} +4 -4
- package/chunks/{chunk-3ZFJUBTP.js → chunk-WAOIHTJS.js} +1 -1
- package/chunks/{chunk-KIVGVFMM.js → chunk-WBHKK3CU.js} +16 -4
- package/chunks/{chunk-XZJKSETB.js → chunk-WXEA74YB.js} +3 -3
- package/chunks/{chunk-VEND4KA4.js → chunk-X3ZRTCJD.js} +1 -1
- package/chunks/{chunk-YRLW2MSX.js → chunk-XBVNNDPK.js} +11 -4
- package/chunks/{chunk-XL5K4SVK.js → chunk-XF63PKEN.js} +1 -1
- package/chunks/{chunk-LDGWX737.js → chunk-XLLQJRAH.js} +323 -510
- package/chunks/{chunk-2Z6LSQMF.js → chunk-XQFT3QUF.js} +1 -1
- package/chunks/{chunk-P3QQPMQA.js → chunk-XZA32HII.js} +4 -4
- package/chunks/{chunk-C72BXMJ5.js → chunk-Y3QL45LS.js} +1 -1
- package/chunks/{chunk-QS4ENGVF.js → chunk-Y4TPOUEV.js} +17 -8
- package/chunks/{chunk-AVCCKIZ6.js → chunk-YRZX2PHA.js} +1 -1
- package/chunks/{chunk-Z3JMO2CH.js → chunk-YTDVNSUQ.js} +1 -1
- package/chunks/{chunk-WS7PAXXR.js → chunk-YWQ237GX.js} +15 -199
- package/chunks/chunk-ZELCTN6Y.js +29 -0
- package/chunks/{chunk-TQOF5KBL.js → chunk-ZEZKIS2K.js} +0 -31
- package/chunks/{chunk-IIWGSX4L.js → chunk-ZTP35P4B.js} +1 -1
- package/chunks/{chunk-K623ENWT.js → chunk-ZU4UDIWX.js} +23 -1
- package/chunks/chunk-ZYNTSXWK.js +32 -0
- package/chunks/config-utils-JVGAP34H.js +113 -0
- package/chunks/contextCommand-XDB7OSO7.js +106 -0
- package/chunks/{core-runtime-CES6JSVV.js → core-runtime-XHYA2RH7.js} +66 -63
- package/chunks/create-sub-session-23ITIELI.js +17 -0
- package/chunks/{create-sub-session-UJTPMVOG.js → create-sub-session-MHYCFNCM.js} +168 -87
- package/chunks/{cron-create-IKA56DAF.js → cron-create-KJ4IY6PP.js} +57 -10
- package/chunks/{cron-delete-K5Q62EGH.js → cron-delete-MBLQCAYY.js} +4 -4
- package/chunks/{cron-list-QBFGAMP7.js → cron-list-DWH2PQ47.js} +4 -4
- package/chunks/{daemon-RZZUPGOJ.js → daemon-K5XS42ID.js} +567 -27
- package/chunks/{daemon-git-worktree-guard-3QY2BQYB.js → daemon-git-worktree-guard-V3P5XLHB.js} +339 -95
- package/chunks/daemon-status-provider-KKU7ZTLM.js +117 -0
- package/chunks/daemon-trust-policy-TZGK34DW.js +112 -0
- package/chunks/{daemon-trust-policy-monitor-TK4K7M63.js → daemon-trust-policy-monitor-F23DD2M4.js} +69 -69
- package/chunks/{de-RYUHH2C6.js → de-X6HLSIFE.js} +1 -0
- package/chunks/deferred-core-runtime-LKLGBCGZ.js +114 -0
- package/chunks/{display-image-OEJMJV3S.js → display-image-BE7I6ZB6.js} +5 -5
- package/chunks/{dist-5EV2G6MC.js → dist-4WXLCBDR.js} +1 -1
- package/chunks/{dist-ES6HNUW2.js → dist-5VXFRTWU.js} +525 -64
- package/chunks/{dist-ESS34DHC.js → dist-6BPYWHVD.js} +89 -65
- package/chunks/{dist-2OG3PGKO.js → dist-GFN5DCBX.js} +76 -67
- package/chunks/{dist-K7IQB53U.js → dist-HZNT6GQS.js} +1 -1
- package/chunks/{dist-J6P3C52Z.js → dist-LVZCXVPF.js} +99 -26
- package/chunks/{dist-LZO24N3E.js → dist-N6W6E753.js} +71 -64
- package/chunks/dist-QDLFKWOL.js +2512 -0
- package/chunks/{dist-VVQBOKDH.js → dist-SWQMYP2Q.js} +18 -16
- package/chunks/earlyInputCapture-NERYFYTR.js +107 -0
- package/chunks/{edit-A2633SLR.js → edit-ACCATIXB.js} +49 -51
- package/chunks/{en-3RHLPITS.js → en-YLXBXM4Z.js} +5 -1
- package/chunks/{enter-worktree-OIAHE2TB.js → enter-worktree-35QCAAFP.js} +7 -8
- package/chunks/{enterPlanMode-SUGIKLR7.js → enterPlanMode-NEN6EPVM.js} +48 -50
- package/chunks/environment-CKWHBC5T.js +129 -0
- package/chunks/errors-XTZPNQ77.js +113 -0
- package/chunks/{exit-worktree-3UT3SSWT.js → exit-worktree-XQD6D543.js} +7 -8
- package/chunks/exitPlanMode-CY344X5J.js +83 -0
- package/chunks/{fast-path-3AVP3G35.js → fast-path-YSOX4PAM.js} +31 -10
- package/chunks/{fast-path-settings-WHX3J3ZI.js → fast-path-settings-XMFBYFUH.js} +2 -2
- package/chunks/{fr-4PL5GHG4.js → fr-FTZWF462.js} +1 -0
- package/chunks/{gemini-7PVQWHTH.js → gemini-VQY2YCPH.js} +137 -129
- package/chunks/{geminiContentGenerator-B2FOJJDJ.js → geminiContentGenerator-PAPXNIGU.js} +16 -13
- package/chunks/{glob-7YGXE6OV.js → glob-GVCUSBBF.js} +48 -50
- package/chunks/{goal-tools-TLJCE7YZ.js → goal-tools-5STK6OXW.js} +77 -31
- package/chunks/{grep-6DV6LTON.js → grep-5GPICQBU.js} +6 -6
- package/chunks/handleAutoUpdate-ZJPJN42W.js +109 -0
- package/chunks/i18n-BZYKOST7.js +124 -0
- package/chunks/{image-gen-7CSCJ73T.js → image-gen-J6FDGUZP.js} +8 -12
- package/chunks/initializer-GO2C5HE3.js +113 -0
- package/chunks/installationInfo-WM2GXQZ6.js +107 -0
- package/chunks/{ja-HZ7X7PQO.js → ja-2GL5FKYJ.js} +1 -0
- package/chunks/{keychain-token-storage-MTFTAESK.js → keychain-token-storage-37XPR34V.js} +2 -2
- package/chunks/list-ERDJB7PQ.js +116 -0
- package/chunks/{list-agents-43XUEXJV.js → list-agents-DI3YFTDT.js} +2 -2
- package/chunks/loadedSettingsAdapter-7JOCVOKV.js +110 -0
- package/chunks/{loggingContentGenerator-RNE66CKK.js → loggingContentGenerator-3ONLU3BS.js} +327 -39
- package/chunks/{loop-wakeup-E54KDUXC.js → loop-wakeup-XJFONSQ7.js} +5 -5
- package/chunks/{ls-CGL2UC3H.js → ls-3VAT2J7A.js} +4 -4
- package/chunks/{lsp-5IKUO5DX.js → lsp-5W7IRUBH.js} +2 -2
- package/chunks/{managed-npm-update-Y3SC3GLJ.js → managed-npm-update-TLRCC2GE.js} +62 -62
- package/chunks/mcp-2LH3QXR2.js +110 -0
- package/chunks/{monitor-PXYSGSUN.js → monitor-P7LDAL4W.js} +48 -50
- package/chunks/nonInteractiveCli-E5T7PZ27.js +184 -0
- package/chunks/{notebook-edit-ISIBJVBR.js → notebook-edit-F7KSG4WQ.js} +49 -51
- package/chunks/open-with-auth-4UI3KHUR.js +55 -0
- package/chunks/{openaiContentGenerator-6TBOPEGT.js → openaiContentGenerator-2EXCZUGN.js} +29 -32
- package/chunks/pidfile-P25DWA5Q.js +111 -0
- package/chunks/process-registry-OAEG6WGC.js +662 -0
- package/chunks/{processUtils-YK2TDTTU.js → processUtils-PVDKBL35.js} +2 -2
- package/chunks/prompt-terminal-ledger-AJ7T7OIV.js +105 -0
- package/chunks/{pt-QT5N2Q6Q.js → pt-XN2YWJVM.js} +1 -0
- package/chunks/{qwenContentGenerator-CLDMA2Y4.js → qwenContentGenerator-TBBRLWIT.js} +54 -62
- package/chunks/{qwenOAuth2-DLDR6UC5.js → qwenOAuth2-OF5WRBQT.js} +7 -11
- package/chunks/read-file-6IIY4RLJ.js +32 -0
- package/chunks/{read-mcp-resource-XYES2B42.js → read-mcp-resource-XD257PGJ.js} +2 -2
- package/chunks/read-package-up-6T6ICKR7.js +13 -0
- package/chunks/{record-artifact-W3IBTGL7.js → record-artifact-VW23GS4F.js} +4 -5
- package/chunks/report-findings-XA3FJG3N.js +33 -0
- package/chunks/request-shutdown-BCLA3JV2.js +133 -0
- package/chunks/resumeHistoryUtils-44IETXDY.js +118 -0
- package/chunks/ripGrep-E7ESZ7SS.js +39 -0
- package/chunks/{ru-STMG5BPD.js → ru-OLTMFJEW.js} +1 -0
- package/chunks/{run-qwen-serve-XJHOW2NY.js → run-qwen-serve-DXBCYW7V.js} +924 -356
- package/chunks/runtime-RUSPD35D.js +148 -0
- package/chunks/scheduled-tasks-DVRJZO6I.js +127 -0
- package/chunks/scheduler-TDRB2NSY.js +122 -0
- package/chunks/{sdk-exporters-http-RSIOP3G2.js → sdk-exporters-http-TTU2CHBU.js} +5 -5
- package/chunks/{sdk-impl-CB3KARSK.js → sdk-impl-UUMVYMAL.js} +12 -6
- package/chunks/{send-message-J4VT46OI.js → send-message-AJ4OCUBV.js} +24 -29
- package/chunks/serve-KITOM7OR.js +118 -0
- package/chunks/{server-74ONDK62.js → server-THCOMUC2.js} +5814 -4549
- package/chunks/{session-BO6VBWJP.js → session-XKR5MPXE.js} +152 -142
- package/chunks/session-pr-refresh-KRBH7KDA.js +278 -0
- package/chunks/{settings-35QZNFDG.js → settings-YXZ4XBKK.js} +72 -72
- package/chunks/shell-5FJ2DZ3W.js +93 -0
- package/chunks/{skill-DKEMQSNJ.js → skill-VMMJZ6CJ.js} +161 -33
- package/chunks/skill-settings-7SG4YA2W.js +118 -0
- package/chunks/spawnChannel-BPMIXRBI.js +112 -0
- package/chunks/standalone-update-QN5HFRIC.js +118 -0
- package/chunks/{startInteractiveUI-J2QSGWPU.js → startInteractiveUI-5A7RQFRP.js} +3139 -2438
- package/chunks/{syntheticOutput-JAXMIMZP.js → syntheticOutput-ML43GEFN.js} +3 -3
- package/chunks/{task-create-I2H322E4.js → task-create-QN4LYRSE.js} +11 -11
- package/chunks/{task-list-3OLIWFTQ.js → task-list-JPLGK45U.js} +5 -5
- package/chunks/{task-stop-JAUY6MSY.js → task-stop-CXEH5L2D.js} +2 -2
- package/chunks/{task-update-B3XO4CPI.js → task-update-IWEOJFV2.js} +11 -11
- package/chunks/{team-create-W534PMGX.js → team-create-HVGVUL2L.js} +49 -51
- package/chunks/{team-delete-Q7GE7RRI.js → team-delete-RZVVSGCY.js} +5 -5
- package/chunks/{team-plan-approval-WNV6P6NG.js → team-plan-approval-S5A5FQWU.js} +48 -50
- package/chunks/terminal-image-renderer-SIVPPXDI.js +114 -0
- package/chunks/theme-manager-TA33UZZX.js +102 -0
- package/chunks/{todoWrite-FJJTY2TI.js → todoWrite-WDJNQEMW.js} +43 -25
- package/chunks/{tool-search-Y4KL3LRE.js → tool-search-NX7IPDKP.js} +19 -20
- package/chunks/total-session-admission-PWXZBOVX.js +114 -0
- package/chunks/trustedFolders-V6Y4CU3F.js +122 -0
- package/chunks/{update-relaunch-OHVFN6KF.js → update-relaunch-CGSX3R37.js} +6 -6
- package/chunks/updateCheck-T5O2WGIB.js +118 -0
- package/chunks/useAutoAcceptIndicator-ULBJJWPY.js +120 -0
- package/chunks/{validateNonInterActiveAuth-WJXH426F.js → validateNonInterActiveAuth-E5S2JSRC.js} +114 -108
- package/chunks/{version-QMDRADXM.js → version-LS5JPU36.js} +2 -2
- package/chunks/{web-fetch-ROS6P4LP.js → web-fetch-RLMFZTS3.js} +25 -244
- package/chunks/{web-search-JS3E76O6.js → web-search-LMCIC4N5.js} +11 -13
- package/chunks/{web-shell-static-3NUTRHAH.js → web-shell-static-A6QMUNNV.js} +11 -4
- package/chunks/webapi-Z5OM224P.js +4118 -0
- package/chunks/{workflow-2FCMBTBZ.js → workflow-JBXU3IGA.js} +713 -138
- package/chunks/workspace-providers-status-525ORQBH.js +114 -0
- package/chunks/{workspace-registration-store-SU4EJKSD.js → workspace-registration-store-7Y44R4LV.js} +1 -1
- package/chunks/workspace-registry-HS4RWUSQ.js +123 -0
- package/chunks/workspace-service-7PXDM6EB.js +129 -0
- package/chunks/workspace-skills-status-FMJGC2XI.js +113 -0
- package/chunks/{workspace-trust-reconciler-4Y4F7XIX.js → workspace-trust-reconciler-3YTWSQBY.js} +78 -77
- package/chunks/write-file-BVQRYQQO.js +88 -0
- package/chunks/{zh-TW-NX43Z2S2.js → zh-TW-RPTMRTHG.js} +5 -1
- package/chunks/{zh-3JQ75KFU.js → zh-UNY63FR3.js} +5 -1
- package/chunks/{zoom-image-DNBKCGGF.js → zoom-image-FGCELNS4.js} +14 -15
- package/cli.js +13 -13
- package/locales/ca.js +3 -0
- package/locales/de.js +1 -0
- package/locales/en.js +5 -0
- package/locales/fr.js +1 -0
- package/locales/ja.js +1 -0
- package/locales/pt.js +1 -0
- package/locales/ru.js +1 -0
- package/locales/zh-TW.js +5 -0
- package/locales/zh.js +5 -0
- package/package.json +3 -3
- package/web-shell/assets/{abnfDiagram-VCTEODGH-DWHXEVlf.js → abnfDiagram-VCTEODGH-2XZDeWYO.js} +1 -1
- package/web-shell/assets/{arc-CXKjm86V.js → arc-B6UgMh90.js} +1 -1
- package/web-shell/assets/{architectureDiagram-5GKGNRK7-B_ynI0cX.js → architectureDiagram-5GKGNRK7-DZgNFeN1.js} +1 -1
- package/web-shell/assets/{blockDiagram-NRAW4CY4-DMebd9X0.js → blockDiagram-NRAW4CY4-DQ2wvBFN.js} +1 -1
- package/web-shell/assets/{c4Diagram-UCG6FXSJ-BZ5BlxS-.js → c4Diagram-UCG6FXSJ-DMmQMrc4.js} +1 -1
- package/web-shell/assets/channel-D_TyfiTx.js +1 -0
- package/web-shell/assets/{chunk-2Q5K7J3B-CluJKopE.js → chunk-2Q5K7J3B-C8Zh2e9y.js} +1 -1
- package/web-shell/assets/{chunk-5VM5RSS4-hzDAPFOo.js → chunk-5VM5RSS4-C3LTenP8.js} +1 -1
- package/web-shell/assets/{chunk-F27PBJKO-6vHioKJe.js → chunk-F27PBJKO-CxABOc8n.js} +1 -1
- package/web-shell/assets/{chunk-G27WJ6UU-B_5hKTGo.js → chunk-G27WJ6UU-CD_JxHDC.js} +1 -1
- package/web-shell/assets/{chunk-JWPE2WC7-CO9VoFCH.js → chunk-JWPE2WC7-BdmZPmce.js} +1 -1
- package/web-shell/assets/{chunk-LCL6LL3I-7L5JpxYu.js → chunk-LCL6LL3I-80bcHmfK.js} +1 -1
- package/web-shell/assets/{chunk-POPQ4Y6H-DHLJMAqj.js → chunk-POPQ4Y6H-CNpLZyQF.js} +1 -1
- package/web-shell/assets/{chunk-SVP7TREG-Dt-4Cqdl.js → chunk-SVP7TREG-CXlKgMv3.js} +1 -1
- package/web-shell/assets/{chunk-XXDRQBXY-QlSQ3ssG.js → chunk-XXDRQBXY-CivyYyvL.js} +1 -1
- package/web-shell/assets/classDiagram-DTDB5LWJ-Bw_H3xMd.js +1 -0
- package/web-shell/assets/classDiagram-v2-JRS7N3AN-Bw_H3xMd.js +1 -0
- package/web-shell/assets/{cose-bilkent-JH36ORCC-DMw3EOxl.js → cose-bilkent-JH36ORCC-COAjRH3w.js} +1 -1
- package/web-shell/assets/{cynefin-OW5HDTMX-s6vEA7RQ.js → cynefin-OW5HDTMX-DqU1JHO6.js} +1 -1
- package/web-shell/assets/{cynefinDiagram-5FMLGOSQ-AOKsvYLt.js → cynefinDiagram-5FMLGOSQ-BCLI1Bvj.js} +1 -1
- package/web-shell/assets/{dagre-3AP2YEHR-9QAZn3m7.js → dagre-3AP2YEHR-DmAz8nqF.js} +1 -1
- package/web-shell/assets/{diagram-S7CK7UJ4-CWpnu4FN.js → diagram-S7CK7UJ4-Ck-5QLT5.js} +1 -1
- package/web-shell/assets/{diagram-UQ7AKVKN-9l0jXD8q.js → diagram-UQ7AKVKN-C0ljX0mW.js} +1 -1
- package/web-shell/assets/{diagram-VSXAHHWV-BMUHroj4.js → diagram-VSXAHHWV-DPgvT24v.js} +1 -1
- package/web-shell/assets/{diagram-VX7I27RA-Co9x27vm.js → diagram-VX7I27RA-D7hWr1LR.js} +1 -1
- package/web-shell/assets/{diagram-Z3DM3KII-BKnTeOER.js → diagram-Z3DM3KII-D59tlOta.js} +1 -1
- package/web-shell/assets/{ebnfDiagram-PWID7BFC-QBZD9CKG.js → ebnfDiagram-PWID7BFC-CY2DDx00.js} +1 -1
- package/web-shell/assets/{erDiagram-SSCWMZ5O-dKWAEGun.js → erDiagram-SSCWMZ5O-AR4h8Q8k.js} +1 -1
- package/web-shell/assets/{flowDiagram-A5DVABFB-CR_UgVqj.js → flowDiagram-A5DVABFB-DbF0HqBz.js} +1 -1
- package/web-shell/assets/{ganttDiagram-EL5Y4UJY-DQUnjWO6.js → ganttDiagram-EL5Y4UJY-J52mrIhc.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-WWUBYQGX-C4j59wPL.js → gitGraphDiagram-WWUBYQGX-CcgMY5EV.js} +1 -1
- package/web-shell/assets/index-BjUg3vPr.css +5 -0
- package/web-shell/assets/index-CFwYYVFU.js +1873 -0
- package/web-shell/assets/{index-tYXRIPz4.js → index-MQs7l15A.js} +1 -1
- package/web-shell/assets/{infoDiagram-RXCK75RN-mzqbt50w.js → infoDiagram-RXCK75RN-Buu3ibp2.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-5VMMS53U-ChLOqhGl.js → ishikawaDiagram-5VMMS53U-CoKQxnF3.js} +1 -1
- package/web-shell/assets/{journeyDiagram-EYS64GPL-SifZE-R8.js → journeyDiagram-EYS64GPL-CxK35mTC.js} +1 -1
- package/web-shell/assets/{kanban-definition-3QL26DDD-Sft2qlbc.js → kanban-definition-3QL26DDD-tk2CHTQ1.js} +1 -1
- package/web-shell/assets/{layout-O2uHVkPd.js → layout-DEpMwb4u.js} +1 -1
- package/web-shell/assets/{linear-BVqYgjSc.js → linear-DSQsLh53.js} +1 -1
- package/web-shell/assets/{mermaid.core-CZ9MQaJ6.js → mermaid.core-DIZuPBQT.js} +6 -6
- package/web-shell/assets/{mindmap-definition-FBJOCRG2-BNQYjBqM.js → mindmap-definition-FBJOCRG2-CCAvk8r_.js} +1 -1
- package/web-shell/assets/{pegDiagram-XKGWAZYB-rh1J8uv6.js → pegDiagram-XKGWAZYB-CpJ28jxz.js} +1 -1
- package/web-shell/assets/{pieDiagram-E7YTZNPT-BQHR5mn9.js → pieDiagram-E7YTZNPT-ziJR2R32.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-AXDQQJYC-CLCbP7mK.js → quadrantDiagram-AXDQQJYC-B60zE3Mh.js} +1 -1
- package/web-shell/assets/{railroadDiagram-O6MQD6OU-CDQhrkbm.js → railroadDiagram-O6MQD6OU-M3GqQ_g1.js} +1 -1
- package/web-shell/assets/{requirementDiagram-EFPCY7ZU-BdP9uswg.js → requirementDiagram-EFPCY7ZU-ntCtYqTk.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-P5KCCOFB-CFZDFBXF.js → sankeyDiagram-P5KCCOFB-AYLoItcZ.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-WJ2MYXX4-BW9WOJXI.js → sequenceDiagram-WJ2MYXX4-DEWk4Mlj.js} +1 -1
- package/web-shell/assets/{sizeCapture-X5ZJPWSS-CPMia32z.js → sizeCapture-X5ZJPWSS-OOWvzPay.js} +1 -1
- package/web-shell/assets/{stateDiagram-HBIQ2CUA-BANp9p7F.js → stateDiagram-HBIQ2CUA-BcMmvxem.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-4QOOHH4V-DRLLqPWO.js +1 -0
- package/web-shell/assets/{swimlanes-XN3QIQJK-JroXksK-.js → swimlanes-XN3QIQJK-DuZTWWvh.js} +1 -1
- package/web-shell/assets/swimlanesDiagram-VK2B7HYN-DOfLQQYt.js +8 -0
- package/web-shell/assets/{timeline-definition-24CTP7MA-DfuVIfYY.js → timeline-definition-24CTP7MA-CjqtZXEd.js} +1 -1
- package/web-shell/assets/{vennDiagram-4TSXK5OY-BZXoSjRB.js → vennDiagram-4TSXK5OY-knVCVkQ4.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-VM6X3IG4-BdiY8avC.js → wardleyDiagram-VM6X3IG4-yoWhw0a2.js} +1 -1
- package/web-shell/assets/{xychartDiagram-S5SC5T6Z-DcXh_U4H.js → xychartDiagram-S5SC5T6Z-ue14E_gL.js} +1 -1
- package/web-shell/index.html +20 -5
- package/chunks/MaxSizedBox-3OZJCC3J.js +0 -110
- package/chunks/agent-BFE3DQM3.js +0 -93
- package/chunks/agent-headless-LLVKZZ4P.js +0 -87
- package/chunks/bridge-EBYO4SYM.js +0 -118
- package/chunks/channel-settings-store-5VP2Y2YM.js +0 -115
- package/chunks/chunk-2J3OJGTL.js +0 -98
- package/chunks/chunk-7IV52LTO.js +0 -202
- package/chunks/chunk-AXMWHKXA.js +0 -42
- package/chunks/chunk-F6WFNA7U.js +0 -22
- package/chunks/chunk-IO257NUD.js +0 -623
- package/chunks/chunk-JS73NNWF.js +0 -30
- package/chunks/chunk-SJ4HB27T.js +0 -945
- package/chunks/chunk-VGHZT6JR.js +0 -51
- package/chunks/chunk-YHC2EYYG.js +0 -125
- package/chunks/computer-use-EGPWFEX6.js +0 -2142
- package/chunks/config-utils-WZ43VOBS.js +0 -25
- package/chunks/contextCommand-5DSEQI6S.js +0 -106
- package/chunks/daemon-status-provider-EYMLPYJU.js +0 -116
- package/chunks/daemon-trust-policy-CQYKZ7VO.js +0 -112
- package/chunks/deferred-core-runtime-WFEPUXZR.js +0 -114
- package/chunks/earlyInputCapture-PN4PP7V5.js +0 -107
- package/chunks/environment-WUPHLPKZ.js +0 -127
- package/chunks/errors-WPMPVIAT.js +0 -113
- package/chunks/exitPlanMode-MLFHYZNB.js +0 -85
- package/chunks/handleAutoUpdate-F5OTWUT5.js +0 -109
- package/chunks/i18n-C562S6KN.js +0 -124
- package/chunks/initializer-7YPGOFP4.js +0 -113
- package/chunks/installationInfo-ARU5Q2H7.js +0 -107
- package/chunks/list-KTSK275C.js +0 -116
- package/chunks/loadedSettingsAdapter-F3T2EQ5J.js +0 -110
- package/chunks/mcp-6ONTVPAY.js +0 -110
- package/chunks/nonInteractiveCli-KZDWFAN6.js +0 -178
- package/chunks/pidfile-IO7ALSZF.js +0 -111
- package/chunks/process-registry-PMJOA5CO.js +0 -172
- package/chunks/prompt-terminal-ledger-TCE7PMCE.js +0 -105
- package/chunks/read-file-2HOD2ATO.js +0 -33
- package/chunks/resumeHistoryUtils-DDSBAQJS.js +0 -118
- package/chunks/ripGrep-E7P6K4QR.js +0 -40
- package/chunks/runtime-J4SPTJRQ.js +0 -148
- package/chunks/scheduler-VRAEZZ4E.js +0 -122
- package/chunks/serve-KEAWV4TI.js +0 -118
- package/chunks/shell-H6K63WG3.js +0 -95
- package/chunks/skill-settings-TCKLXIXM.js +0 -118
- package/chunks/spawnChannel-2K6NKWM5.js +0 -111
- package/chunks/standalone-update-LSCGOEFP.js +0 -118
- package/chunks/terminal-image-renderer-UHX3UD66.js +0 -114
- package/chunks/theme-manager-6B47ZPIF.js +0 -102
- package/chunks/total-session-admission-NSCFWGJH.js +0 -113
- package/chunks/trustedFolders-7XMJWGQ3.js +0 -122
- package/chunks/updateCheck-EOVLHD4T.js +0 -118
- package/chunks/useAutoAcceptIndicator-ZNMIGBTN.js +0 -120
- package/chunks/workspace-providers-status-GPGU5YMT.js +0 -113
- package/chunks/workspace-registry-BQISXT6Y.js +0 -122
- package/chunks/workspace-service-LLY6HCUC.js +0 -129
- package/chunks/workspace-skills-status-L5N2Y3ZP.js +0 -113
- package/chunks/write-file-EJHU2Z3E.js +0 -90
- package/web-shell/assets/channel-Zg3WHgq_.js +0 -1
- package/web-shell/assets/classDiagram-DTDB5LWJ-C16l9jQz.js +0 -1
- package/web-shell/assets/classDiagram-v2-JRS7N3AN-C16l9jQz.js +0 -1
- package/web-shell/assets/index-BN_m_uHJ.css +0 -5
- package/web-shell/assets/index-DrlJu79f.js +0 -1792
- package/web-shell/assets/stateDiagram-v2-4QOOHH4V-NZYAoQLU.js +0 -1
- package/web-shell/assets/swimlanesDiagram-VK2B7HYN-3YdVX3vj.js +0 -8
|
@@ -339,6 +339,20 @@ function truncateCodePoints(str, max) {
|
|
|
339
339
|
return cp.length > max ? cp.slice(0, max).join("") : str;
|
|
340
340
|
}
|
|
341
341
|
__name(truncateCodePoints, "truncateCodePoints");
|
|
342
|
+
function truncateUtf16Units(str, max) {
|
|
343
|
+
if (str.length <= max)
|
|
344
|
+
return str;
|
|
345
|
+
let kept = "";
|
|
346
|
+
let units = 0;
|
|
347
|
+
for (const ch of str) {
|
|
348
|
+
if (units + ch.length > max)
|
|
349
|
+
break;
|
|
350
|
+
kept += ch;
|
|
351
|
+
units += ch.length;
|
|
352
|
+
}
|
|
353
|
+
return kept;
|
|
354
|
+
}
|
|
355
|
+
__name(truncateUtf16Units, "truncateUtf16Units");
|
|
342
356
|
function sanitizeSenderName(name) {
|
|
343
357
|
const cleaned = name.replace(PROMPT_UNSAFE_INVISIBLES, " ").replace(/[\u0000-\u001f\u007f]/g, " ").replace(/[[\]\r\n]/g, " ");
|
|
344
358
|
return truncateCodePoints(cleaned, 64).trim() || "unknown";
|
|
@@ -350,8 +364,81 @@ function sanitizeQuotedText(text, maxLen) {
|
|
|
350
364
|
return cp.length > maxLen ? cp.slice(0, maxLen - 1).join("") + "\u2026" : cleaned;
|
|
351
365
|
}
|
|
352
366
|
__name(sanitizeQuotedText, "sanitizeQuotedText");
|
|
367
|
+
var START_OF_LINE_TAG_MAX_CONTENT = 64;
|
|
368
|
+
var LINE_TERMINATORS = "\r\n\u2028\u2029";
|
|
369
|
+
function isTagLeadBlank(ch) {
|
|
370
|
+
return ch !== "\r" && ch !== "\n" && /\s/.test(ch);
|
|
371
|
+
}
|
|
372
|
+
__name(isTagLeadBlank, "isTagLeadBlank");
|
|
373
|
+
function unwrapStartOfLineTags(text) {
|
|
374
|
+
if (!text.includes("["))
|
|
375
|
+
return text;
|
|
376
|
+
const deleted = new Uint8Array(text.length);
|
|
377
|
+
let peeled = false;
|
|
378
|
+
let lineStart = 0;
|
|
379
|
+
while (lineStart < text.length) {
|
|
380
|
+
let open = lineStart;
|
|
381
|
+
let close = lineStart;
|
|
382
|
+
let carried = 0;
|
|
383
|
+
for (; ; ) {
|
|
384
|
+
while (open < text.length) {
|
|
385
|
+
const ch = text[open];
|
|
386
|
+
if (ch === "\r" || ch === "\n")
|
|
387
|
+
break;
|
|
388
|
+
if (deleted[open] === 0 && !isTagLeadBlank(ch))
|
|
389
|
+
break;
|
|
390
|
+
if (open < close && deleted[open] === 0)
|
|
391
|
+
carried -= 1;
|
|
392
|
+
open += 1;
|
|
393
|
+
}
|
|
394
|
+
if (text[open] !== "[")
|
|
395
|
+
break;
|
|
396
|
+
let content = open < close ? carried - 1 : 0;
|
|
397
|
+
let cursor = Math.max(close, open + 1);
|
|
398
|
+
while (cursor < text.length) {
|
|
399
|
+
const ch = text[cursor];
|
|
400
|
+
if (ch === "]" || ch === "\r" || ch === "\n")
|
|
401
|
+
break;
|
|
402
|
+
content += 1;
|
|
403
|
+
if (content > START_OF_LINE_TAG_MAX_CONTENT)
|
|
404
|
+
break;
|
|
405
|
+
cursor += 1;
|
|
406
|
+
}
|
|
407
|
+
if (text[cursor] !== "]" || content < 1 || content > START_OF_LINE_TAG_MAX_CONTENT) {
|
|
408
|
+
break;
|
|
409
|
+
}
|
|
410
|
+
deleted[open] = 1;
|
|
411
|
+
deleted[cursor] = 1;
|
|
412
|
+
peeled = true;
|
|
413
|
+
open += 1;
|
|
414
|
+
close = cursor + 1;
|
|
415
|
+
carried = content;
|
|
416
|
+
}
|
|
417
|
+
let nextLine = open;
|
|
418
|
+
while (nextLine < text.length && !LINE_TERMINATORS.includes(text[nextLine])) {
|
|
419
|
+
nextLine += 1;
|
|
420
|
+
}
|
|
421
|
+
lineStart = nextLine + 1;
|
|
422
|
+
}
|
|
423
|
+
if (!peeled)
|
|
424
|
+
return text;
|
|
425
|
+
const parts = [];
|
|
426
|
+
let cut = 0;
|
|
427
|
+
for (let i = 0; i < text.length; i++) {
|
|
428
|
+
if (deleted[i] === 0)
|
|
429
|
+
continue;
|
|
430
|
+
if (i > cut)
|
|
431
|
+
parts.push(text.slice(cut, i));
|
|
432
|
+
cut = i + 1;
|
|
433
|
+
}
|
|
434
|
+
parts.push(text.slice(cut));
|
|
435
|
+
return parts.join("");
|
|
436
|
+
}
|
|
437
|
+
__name(unwrapStartOfLineTags, "unwrapStartOfLineTags");
|
|
353
438
|
function sanitizePromptText(text) {
|
|
354
|
-
|
|
439
|
+
const unwrapped = unwrapStartOfLineTags(text.replace(PROMPT_UNSAFE_INVISIBLES, " "));
|
|
440
|
+
const folded = unwrapped.replace(/[\u0000-\u001f\u007f]/g, " ");
|
|
441
|
+
return unwrapStartOfLineTags(folded);
|
|
355
442
|
}
|
|
356
443
|
__name(sanitizePromptText, "sanitizePromptText");
|
|
357
444
|
function sanitizeDisplayText(text, maxLen) {
|
|
@@ -376,6 +463,18 @@ var CHANNEL_PROMPT_AUTHORIZATION_META_KEY = "qwen.daemon.channelPromptAuthorizat
|
|
|
376
463
|
var CHANNEL_PROMPT_META_KEY = "qwen.channel.prompt";
|
|
377
464
|
var ACP_PRIVATE_PARENT_CAPABILITY_META_KEY = "qwen-code/private-parent-capability";
|
|
378
465
|
var ACP_PRIVATE_PARENT_CAPABILITY_ENV = "QWEN_CODE_PRIVATE_ACP_CAPABILITY";
|
|
466
|
+
function resolvePromptImages(options) {
|
|
467
|
+
const images = options?.images && options.images.length > 0 ? options.images : options?.imageBase64 && options.imageMimeType ? [{ data: options.imageBase64, mimeType: options.imageMimeType }] : [];
|
|
468
|
+
return images.filter((image) => !!image && typeof image.data === "string" && image.data.length > 0 && typeof image.mimeType === "string" && image.mimeType.length > 0).map((image) => {
|
|
469
|
+
const cleaned = image.mimeType.split(";", 1)[0]?.trim().toLowerCase() ?? "";
|
|
470
|
+
return {
|
|
471
|
+
data: image.data,
|
|
472
|
+
// Normalize the alias like the daemon attachment store's own naming.
|
|
473
|
+
mimeType: cleaned === "image/jpg" ? "image/jpeg" : cleaned
|
|
474
|
+
};
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
__name(resolvePromptImages, "resolvePromptImages");
|
|
379
478
|
|
|
380
479
|
// packages/channels/base/dist/ChannelLoopTools.js
|
|
381
480
|
init_esbuild_shims();
|
|
@@ -704,11 +803,11 @@ var AcpBridge = class extends EventEmitter {
|
|
|
704
803
|
this.on("slashCommandOutput", onSlashCommandOutput);
|
|
705
804
|
this.on("responseBoundary", clearChunks);
|
|
706
805
|
const prompt = [];
|
|
707
|
-
|
|
806
|
+
for (const image of resolvePromptImages(options)) {
|
|
708
807
|
prompt.push({
|
|
709
808
|
type: "image",
|
|
710
|
-
data:
|
|
711
|
-
mimeType:
|
|
809
|
+
data: image.data,
|
|
810
|
+
mimeType: image.mimeType
|
|
712
811
|
});
|
|
713
812
|
}
|
|
714
813
|
prompt.push({ type: "text", text });
|
|
@@ -794,6 +893,8 @@ var AcpBridge = class extends EventEmitter {
|
|
|
794
893
|
if (meta?.["qwenDiscreteMessage"] === true) {
|
|
795
894
|
if (meta["source"] === "background_notification_response" && meta["rewritten"] !== true && content?.type === "text" && content.text) {
|
|
796
895
|
this.emit("backgroundResponse", sessionId, content.text);
|
|
896
|
+
} else if (meta["source"] === "vision_bridge_notice" && content?.type === "text" && content.text) {
|
|
897
|
+
this.emit("textChunk", sessionId, content.text);
|
|
797
898
|
}
|
|
798
899
|
break;
|
|
799
900
|
}
|
|
@@ -1017,14 +1118,14 @@ init_esbuild_shims();
|
|
|
1017
1118
|
// packages/channels/base/dist/PollingChannelBase.js
|
|
1018
1119
|
init_esbuild_shims();
|
|
1019
1120
|
import { createHash as createHash2 } from "node:crypto";
|
|
1020
|
-
import { mkdirSync as
|
|
1121
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, renameSync as renameSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
1021
1122
|
import { join as join6 } from "node:path";
|
|
1022
|
-
import
|
|
1123
|
+
import process4 from "node:process";
|
|
1023
1124
|
|
|
1024
1125
|
// packages/channels/base/dist/ChannelBase.js
|
|
1025
1126
|
init_esbuild_shims();
|
|
1026
1127
|
import { basename as basename3, join as join5 } from "node:path";
|
|
1027
|
-
import { randomUUID as
|
|
1128
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
1028
1129
|
|
|
1029
1130
|
// packages/channels/base/dist/BlockStreamer.js
|
|
1030
1131
|
init_esbuild_shims();
|
|
@@ -1064,6 +1165,10 @@ var BlockStreamer = class {
|
|
|
1064
1165
|
this.clearIdleTimer();
|
|
1065
1166
|
this.buffer = "";
|
|
1066
1167
|
}
|
|
1168
|
+
/** Await already-queued sends without emitting the buffered text. */
|
|
1169
|
+
async drain() {
|
|
1170
|
+
await this.sending;
|
|
1171
|
+
}
|
|
1067
1172
|
// ---------------------------------------------------------------------------
|
|
1068
1173
|
// Internal
|
|
1069
1174
|
// ---------------------------------------------------------------------------
|
|
@@ -1092,8 +1197,10 @@ var BlockStreamer = class {
|
|
|
1092
1197
|
const trimmed = text.trim();
|
|
1093
1198
|
if (!trimmed)
|
|
1094
1199
|
return;
|
|
1200
|
+
const block = /\n[^\S\n]*$/.test(text) ? `${trimmed}
|
|
1201
|
+
` : trimmed;
|
|
1095
1202
|
this.blockCount++;
|
|
1096
|
-
this.sending = this.sending.then(() => this.opts.send(
|
|
1203
|
+
this.sending = this.sending.then(() => this.opts.send(block)).catch(() => {
|
|
1097
1204
|
});
|
|
1098
1205
|
}
|
|
1099
1206
|
/**
|
|
@@ -1565,6 +1672,7 @@ var SessionRouter = class {
|
|
|
1565
1672
|
defaultScope;
|
|
1566
1673
|
channelScopes = /* @__PURE__ */ new Map();
|
|
1567
1674
|
channelApprovalModes = /* @__PURE__ */ new Map();
|
|
1675
|
+
channelsWithoutLoops = /* @__PURE__ */ new Set();
|
|
1568
1676
|
persistPath;
|
|
1569
1677
|
recoveryMode;
|
|
1570
1678
|
constructor(bridge, defaultCwd, scope = "user", persistPath, options = {}) {
|
|
@@ -1577,6 +1685,7 @@ var SessionRouter = class {
|
|
|
1577
1685
|
/** Replace the bridge instance (used after crash recovery restart). */
|
|
1578
1686
|
setBridge(bridge) {
|
|
1579
1687
|
this.bridge = bridge;
|
|
1688
|
+
this.liveSessionIds.clear();
|
|
1580
1689
|
}
|
|
1581
1690
|
/** Set scope override for a specific channel. */
|
|
1582
1691
|
setChannelScope(channelName, scope) {
|
|
@@ -1589,6 +1698,13 @@ var SessionRouter = class {
|
|
|
1589
1698
|
this.channelApprovalModes.delete(channelName);
|
|
1590
1699
|
}
|
|
1591
1700
|
}
|
|
1701
|
+
setChannelLoopsEnabled(channelName, enabled) {
|
|
1702
|
+
if (enabled) {
|
|
1703
|
+
this.channelsWithoutLoops.delete(channelName);
|
|
1704
|
+
} else {
|
|
1705
|
+
this.channelsWithoutLoops.add(channelName);
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1592
1708
|
routingKey(channelName, senderId, chatId, threadId) {
|
|
1593
1709
|
const scope = this.channelScopes.get(channelName) || this.defaultScope;
|
|
1594
1710
|
switch (scope) {
|
|
@@ -1605,7 +1721,11 @@ var SessionRouter = class {
|
|
|
1605
1721
|
}
|
|
1606
1722
|
sessionOptions(channelName) {
|
|
1607
1723
|
const approvalMode = this.channelApprovalModes.get(channelName);
|
|
1608
|
-
|
|
1724
|
+
const loopsDisabled = this.channelsWithoutLoops.has(channelName);
|
|
1725
|
+
return approvalMode || loopsDisabled ? {
|
|
1726
|
+
...approvalMode ? { approvalMode } : {},
|
|
1727
|
+
...loopsDisabled ? { enableChannelLoops: false } : {}
|
|
1728
|
+
} : void 0;
|
|
1609
1729
|
}
|
|
1610
1730
|
async resolve(channelName, senderId, chatId, threadId, cwd, isGroup, options) {
|
|
1611
1731
|
const key = this.routingKey(channelName, senderId, chatId, options?.routingThreadId ?? threadId);
|
|
@@ -1792,6 +1912,120 @@ var SessionRouter = class {
|
|
|
1792
1912
|
}
|
|
1793
1913
|
return false;
|
|
1794
1914
|
}
|
|
1915
|
+
getSessionCwd(sessionId) {
|
|
1916
|
+
return this.toCwd.get(sessionId);
|
|
1917
|
+
}
|
|
1918
|
+
async createManagedSession(target, cwd) {
|
|
1919
|
+
const loadWindow = this.beginSessionLoad();
|
|
1920
|
+
const lifecycleGeneration = this.lifecycleGeneration;
|
|
1921
|
+
const bridge = this.bridge;
|
|
1922
|
+
const bindingToken = {};
|
|
1923
|
+
try {
|
|
1924
|
+
let lastDeadSessionId;
|
|
1925
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1926
|
+
const sessionId = await bridge.newSession(cwd, {
|
|
1927
|
+
...this.sessionOptions(target.channelName),
|
|
1928
|
+
sourceId: target.channelName
|
|
1929
|
+
}, bindingToken);
|
|
1930
|
+
if (lifecycleGeneration !== this.lifecycleGeneration || bridge !== this.bridge) {
|
|
1931
|
+
this.scheduleManagedDiscard(bridge, sessionId, bindingToken);
|
|
1932
|
+
throw new Error("Managed session creation was invalidated");
|
|
1933
|
+
}
|
|
1934
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
1935
|
+
throw new Error("Invalid session ID from bridge");
|
|
1936
|
+
}
|
|
1937
|
+
if (loadWindow.delete(sessionId)) {
|
|
1938
|
+
lastDeadSessionId = sessionId;
|
|
1939
|
+
await bridge.discardSession?.(sessionId, bindingToken);
|
|
1940
|
+
continue;
|
|
1941
|
+
}
|
|
1942
|
+
this.toTarget.set(sessionId, target);
|
|
1943
|
+
this.toCwd.set(sessionId, cwd);
|
|
1944
|
+
this.liveSessionIds.add(sessionId);
|
|
1945
|
+
return sessionId;
|
|
1946
|
+
}
|
|
1947
|
+
throw new Error(`Managed session ${lastDeadSessionId ?? "unknown"} died before creation completed`);
|
|
1948
|
+
} finally {
|
|
1949
|
+
this.endSessionLoad(loadWindow);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
async loadManagedSession(sessionId, target, cwd) {
|
|
1953
|
+
if (this.liveSessionIds.has(sessionId)) {
|
|
1954
|
+
this.toTarget.set(sessionId, target);
|
|
1955
|
+
this.toCwd.set(sessionId, cwd);
|
|
1956
|
+
return { loaded: false };
|
|
1957
|
+
}
|
|
1958
|
+
const loadWindow = this.beginSessionLoad();
|
|
1959
|
+
const lifecycleGeneration = this.lifecycleGeneration;
|
|
1960
|
+
const bridge = this.bridge;
|
|
1961
|
+
const bindingToken = {};
|
|
1962
|
+
let loadedSessionId;
|
|
1963
|
+
try {
|
|
1964
|
+
loadedSessionId = await bridge.loadSession(sessionId, cwd, this.sessionOptions(target.channelName), bindingToken);
|
|
1965
|
+
if (lifecycleGeneration !== this.lifecycleGeneration || bridge !== this.bridge) {
|
|
1966
|
+
this.scheduleManagedDiscard(bridge, loadedSessionId, bindingToken);
|
|
1967
|
+
loadedSessionId = void 0;
|
|
1968
|
+
throw new Error("Managed session load was invalidated");
|
|
1969
|
+
}
|
|
1970
|
+
if (loadedSessionId !== sessionId) {
|
|
1971
|
+
const unexpectedSessionId = loadedSessionId;
|
|
1972
|
+
await bridge.discardSession?.(unexpectedSessionId, bindingToken);
|
|
1973
|
+
loadedSessionId = void 0;
|
|
1974
|
+
throw new Error(`Bridge returned session ${unexpectedSessionId || "unknown"} while loading ${sessionId}`);
|
|
1975
|
+
}
|
|
1976
|
+
if (loadWindow.delete(sessionId)) {
|
|
1977
|
+
throw new Error(`Managed session ${sessionId} died before loading completed`);
|
|
1978
|
+
}
|
|
1979
|
+
this.toTarget.set(sessionId, target);
|
|
1980
|
+
this.toCwd.set(sessionId, cwd);
|
|
1981
|
+
this.liveSessionIds.add(sessionId);
|
|
1982
|
+
return { loaded: true };
|
|
1983
|
+
} catch (error) {
|
|
1984
|
+
if (loadedSessionId) {
|
|
1985
|
+
await bridge.discardSession?.(loadedSessionId, bindingToken).catch(() => void 0);
|
|
1986
|
+
}
|
|
1987
|
+
throw error;
|
|
1988
|
+
} finally {
|
|
1989
|
+
this.endSessionLoad(loadWindow);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
activateManagedSession(sessionId, target, cwd) {
|
|
1993
|
+
const key = this.routingKey(target.channelName, target.senderId, target.chatId, target.threadId);
|
|
1994
|
+
if (this.toSession.get(key) === sessionId)
|
|
1995
|
+
return;
|
|
1996
|
+
this.invalidateRouteOperation(key);
|
|
1997
|
+
this.toSession.set(key, sessionId);
|
|
1998
|
+
this.toTarget.set(sessionId, target);
|
|
1999
|
+
this.toCwd.set(sessionId, cwd);
|
|
2000
|
+
this.persist();
|
|
2001
|
+
}
|
|
2002
|
+
forgetManagedSession(sessionId) {
|
|
2003
|
+
let changed = false;
|
|
2004
|
+
for (const [key, mappedSessionId] of this.toSession) {
|
|
2005
|
+
if (mappedSessionId !== sessionId)
|
|
2006
|
+
continue;
|
|
2007
|
+
this.invalidateRouteOperation(key);
|
|
2008
|
+
this.toSession.delete(key);
|
|
2009
|
+
changed = true;
|
|
2010
|
+
}
|
|
2011
|
+
changed = this.toTarget.delete(sessionId) || changed;
|
|
2012
|
+
changed = this.toCwd.delete(sessionId) || changed;
|
|
2013
|
+
changed = this.liveSessionIds.delete(sessionId) || changed;
|
|
2014
|
+
if (changed)
|
|
2015
|
+
this.persist();
|
|
2016
|
+
}
|
|
2017
|
+
async detachManagedSession(sessionId) {
|
|
2018
|
+
try {
|
|
2019
|
+
if (this.liveSessionIds.has(sessionId)) {
|
|
2020
|
+
if (!this.bridge.discardSession) {
|
|
2021
|
+
throw new Error("Managed session detach is not supported");
|
|
2022
|
+
}
|
|
2023
|
+
await this.bridge.discardSession(sessionId);
|
|
2024
|
+
}
|
|
2025
|
+
} finally {
|
|
2026
|
+
this.forgetManagedSession(sessionId);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
1795
2029
|
/**
|
|
1796
2030
|
* Remove session(s) for the given sender. Returns the removed session IDs.
|
|
1797
2031
|
*/
|
|
@@ -1966,6 +2200,7 @@ var SessionRouter = class {
|
|
|
1966
2200
|
this.toSession.set(key, sessionId);
|
|
1967
2201
|
this.toTarget.set(sessionId, entry.target);
|
|
1968
2202
|
this.toCwd.set(sessionId, entry.cwd);
|
|
2203
|
+
this.liveSessionIds.add(sessionId);
|
|
1969
2204
|
reservation.resolve(sessionId);
|
|
1970
2205
|
if (sessionId !== entry.sessionId) {
|
|
1971
2206
|
changed = true;
|
|
@@ -2189,6 +2424,12 @@ var SessionRouter = class {
|
|
|
2189
2424
|
} catch {
|
|
2190
2425
|
}
|
|
2191
2426
|
}
|
|
2427
|
+
scheduleManagedDiscard(bridge, sessionId, bindingToken) {
|
|
2428
|
+
try {
|
|
2429
|
+
void bridge.discardSession?.(sessionId, bindingToken).catch(() => void 0);
|
|
2430
|
+
} catch {
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2192
2433
|
createSessionReservation() {
|
|
2193
2434
|
let resolveReservation;
|
|
2194
2435
|
let rejectReservation;
|
|
@@ -2207,6 +2448,615 @@ var SessionRouter = class {
|
|
|
2207
2448
|
}
|
|
2208
2449
|
};
|
|
2209
2450
|
|
|
2451
|
+
// packages/channels/base/dist/named-session-manager.js
|
|
2452
|
+
init_esbuild_shims();
|
|
2453
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
2454
|
+
import { chmodSync as chmodSync3, existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync4, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2455
|
+
import { dirname as dirname4 } from "node:path";
|
|
2456
|
+
import process3 from "node:process";
|
|
2457
|
+
var REGISTRY_VERSION = 1;
|
|
2458
|
+
var MAX_OPEN_TASKS = 8;
|
|
2459
|
+
var TASK_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$/;
|
|
2460
|
+
var NamedSessionManager = class {
|
|
2461
|
+
static {
|
|
2462
|
+
__name(this, "NamedSessionManager");
|
|
2463
|
+
}
|
|
2464
|
+
channelName;
|
|
2465
|
+
cwd;
|
|
2466
|
+
canonicalCwd;
|
|
2467
|
+
filePath;
|
|
2468
|
+
router;
|
|
2469
|
+
isBusy;
|
|
2470
|
+
now;
|
|
2471
|
+
registry;
|
|
2472
|
+
ownerOperations = /* @__PURE__ */ new Map();
|
|
2473
|
+
constructor(options) {
|
|
2474
|
+
this.channelName = options.channelName;
|
|
2475
|
+
this.cwd = options.cwd;
|
|
2476
|
+
this.canonicalCwd = canonicalizeWorkspacePath(options.cwd);
|
|
2477
|
+
this.filePath = options.filePath;
|
|
2478
|
+
this.router = options.router;
|
|
2479
|
+
this.isBusy = options.isBusy;
|
|
2480
|
+
this.now = options.now ?? Date.now;
|
|
2481
|
+
this.registry = this.readRegistry();
|
|
2482
|
+
}
|
|
2483
|
+
resolve(input, expectedSessionId, reserve) {
|
|
2484
|
+
return this.withOwnerLock(input, () => this.resolveLocked(input, expectedSessionId, reserve));
|
|
2485
|
+
}
|
|
2486
|
+
resolveAfterPreparation(input, prepare, needsSession, reserve) {
|
|
2487
|
+
return this.withOwnerLock(input, async () => {
|
|
2488
|
+
if (await prepare() === false)
|
|
2489
|
+
return { status: "aborted" };
|
|
2490
|
+
if (!needsSession())
|
|
2491
|
+
return { status: "bypassed" };
|
|
2492
|
+
try {
|
|
2493
|
+
return {
|
|
2494
|
+
status: "resolved",
|
|
2495
|
+
sessionId: await this.resolveLocked(input, void 0, reserve)
|
|
2496
|
+
};
|
|
2497
|
+
} catch (error) {
|
|
2498
|
+
return { status: "resolve_error", error };
|
|
2499
|
+
}
|
|
2500
|
+
});
|
|
2501
|
+
}
|
|
2502
|
+
list(input, includeClosed) {
|
|
2503
|
+
return this.withOwnerLock(input, async () => {
|
|
2504
|
+
const owner = await this.ensureOwner(input, false);
|
|
2505
|
+
if (!owner)
|
|
2506
|
+
return [];
|
|
2507
|
+
return owner.tasks.filter((task) => includeClosed || task.status === "open").map((task) => this.view(owner, task));
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
current(input) {
|
|
2511
|
+
return this.withOwnerLock(input, async () => {
|
|
2512
|
+
const owner = await this.ensureOwner(input, false);
|
|
2513
|
+
if (!owner?.activeTaskName)
|
|
2514
|
+
return void 0;
|
|
2515
|
+
const task = this.findTask(owner, owner.activeTaskName);
|
|
2516
|
+
return task ? this.selection(owner, task) : void 0;
|
|
2517
|
+
});
|
|
2518
|
+
}
|
|
2519
|
+
create(input, name) {
|
|
2520
|
+
return this.withOwnerLock(input, async () => {
|
|
2521
|
+
this.validateName(name);
|
|
2522
|
+
const existingOwner = await this.ensureOwner(input, false);
|
|
2523
|
+
const owner = existingOwner ?? this.createOwner(input, null, []);
|
|
2524
|
+
if (this.findTask(owner, name)) {
|
|
2525
|
+
throw new Error(`Task "${name}" already exists.`);
|
|
2526
|
+
}
|
|
2527
|
+
if (this.openTaskCount(owner) >= MAX_OPEN_TASKS) {
|
|
2528
|
+
throw new Error("You already have eight open tasks. Close one before creating another.");
|
|
2529
|
+
}
|
|
2530
|
+
this.assertCanLeaveSelected(owner, void 0);
|
|
2531
|
+
const timestamp = this.nextTimestamp(owner);
|
|
2532
|
+
const target = this.target(input);
|
|
2533
|
+
const sessionId = await this.createSession(target, this.cwd, `Could not create task "${name}".`);
|
|
2534
|
+
const task = {
|
|
2535
|
+
name,
|
|
2536
|
+
sessionId,
|
|
2537
|
+
cwd: this.cwd,
|
|
2538
|
+
isolation: "shared",
|
|
2539
|
+
status: "open",
|
|
2540
|
+
target,
|
|
2541
|
+
createdAt: timestamp,
|
|
2542
|
+
updatedAt: timestamp,
|
|
2543
|
+
lastSelectedAt: timestamp
|
|
2544
|
+
};
|
|
2545
|
+
const nextOwner = {
|
|
2546
|
+
...owner,
|
|
2547
|
+
activeTaskName: name,
|
|
2548
|
+
tasks: [...owner.tasks, task]
|
|
2549
|
+
};
|
|
2550
|
+
try {
|
|
2551
|
+
this.commitOwner(nextOwner);
|
|
2552
|
+
} catch (error) {
|
|
2553
|
+
await this.router.detachManagedSession(sessionId).catch(() => void 0);
|
|
2554
|
+
throw error;
|
|
2555
|
+
}
|
|
2556
|
+
this.router.activateManagedSession(sessionId, target, this.cwd);
|
|
2557
|
+
return this.selection(nextOwner, task);
|
|
2558
|
+
});
|
|
2559
|
+
}
|
|
2560
|
+
use(input, name) {
|
|
2561
|
+
return this.withOwnerLock(input, async () => {
|
|
2562
|
+
const owner = await this.ensureOwner(input, false);
|
|
2563
|
+
if (!owner)
|
|
2564
|
+
throw new Error("No named tasks exist in this chat.");
|
|
2565
|
+
const task = this.findTask(owner, name);
|
|
2566
|
+
if (!task)
|
|
2567
|
+
throw new Error(`Task "${name}" was not found.`);
|
|
2568
|
+
this.assertCanLeaveSelected(owner, task.name);
|
|
2569
|
+
if (this.isBusy(task.sessionId)) {
|
|
2570
|
+
throw new Error(`Task "${task.name}" is still running or waiting for permission.`);
|
|
2571
|
+
}
|
|
2572
|
+
if (task.status === "closed" && this.openTaskCount(owner) >= MAX_OPEN_TASKS) {
|
|
2573
|
+
throw new Error("You already have eight open tasks. Close one before reopening another.");
|
|
2574
|
+
}
|
|
2575
|
+
const timestamp = this.nextTimestamp(owner);
|
|
2576
|
+
const loaded = await this.loadTask(task, `Could not load task "${task.name}". The current task was not changed.`);
|
|
2577
|
+
const updatedTask = {
|
|
2578
|
+
...task,
|
|
2579
|
+
status: "open",
|
|
2580
|
+
updatedAt: timestamp,
|
|
2581
|
+
lastSelectedAt: timestamp
|
|
2582
|
+
};
|
|
2583
|
+
const nextOwner = this.replaceTask(owner, updatedTask, task.name);
|
|
2584
|
+
try {
|
|
2585
|
+
this.commitOwner(nextOwner);
|
|
2586
|
+
} catch (error) {
|
|
2587
|
+
if (loaded) {
|
|
2588
|
+
await this.router.detachManagedSession(task.sessionId).catch(() => void 0);
|
|
2589
|
+
}
|
|
2590
|
+
throw error;
|
|
2591
|
+
}
|
|
2592
|
+
this.router.activateManagedSession(task.sessionId, task.target, task.cwd);
|
|
2593
|
+
return this.selection(nextOwner, updatedTask);
|
|
2594
|
+
});
|
|
2595
|
+
}
|
|
2596
|
+
close(input, name) {
|
|
2597
|
+
return this.withOwnerLock(input, async () => {
|
|
2598
|
+
const owner = await this.ensureOwner(input, false);
|
|
2599
|
+
if (!owner)
|
|
2600
|
+
throw new Error("No named tasks exist in this chat.");
|
|
2601
|
+
const task = this.findTask(owner, name);
|
|
2602
|
+
if (!task)
|
|
2603
|
+
throw new Error(`Task "${name}" was not found.`);
|
|
2604
|
+
if (task.status === "closed") {
|
|
2605
|
+
throw new Error(`Task "${task.name}" is already closed.`);
|
|
2606
|
+
}
|
|
2607
|
+
if (this.isBusy(task.sessionId)) {
|
|
2608
|
+
throw new Error(`Task "${task.name}" is still running or waiting for permission.`);
|
|
2609
|
+
}
|
|
2610
|
+
const wasActive = this.sameName(owner.activeTaskName, task.name);
|
|
2611
|
+
const timestamp = this.nextTimestamp(owner);
|
|
2612
|
+
const replacement = wasActive ? owner.tasks.filter((candidate) => candidate.status === "open" && !this.sameName(candidate.name, task.name)).sort((a, b) => b.lastSelectedAt - a.lastSelectedAt)[0] : void 0;
|
|
2613
|
+
let replacementLoaded = false;
|
|
2614
|
+
if (replacement) {
|
|
2615
|
+
if (this.isBusy(replacement.sessionId)) {
|
|
2616
|
+
throw new Error(`Task "${replacement.name}" is still running or waiting for permission.`);
|
|
2617
|
+
}
|
|
2618
|
+
replacementLoaded = await this.loadTask(replacement, `Could not load fallback task "${replacement.name}". Task "${task.name}" was not closed.`);
|
|
2619
|
+
}
|
|
2620
|
+
const closedTask = {
|
|
2621
|
+
...task,
|
|
2622
|
+
status: "closed",
|
|
2623
|
+
updatedAt: timestamp
|
|
2624
|
+
};
|
|
2625
|
+
let nextOwner = this.replaceTask(owner, closedTask, wasActive ? replacement?.name ?? null : owner.activeTaskName);
|
|
2626
|
+
const selectedReplacement = replacement ? {
|
|
2627
|
+
...replacement,
|
|
2628
|
+
updatedAt: timestamp,
|
|
2629
|
+
lastSelectedAt: timestamp
|
|
2630
|
+
} : void 0;
|
|
2631
|
+
if (selectedReplacement) {
|
|
2632
|
+
nextOwner = this.replaceTask(nextOwner, selectedReplacement, selectedReplacement.name);
|
|
2633
|
+
}
|
|
2634
|
+
try {
|
|
2635
|
+
this.commitOwner(nextOwner);
|
|
2636
|
+
} catch (error) {
|
|
2637
|
+
if (replacement && replacementLoaded) {
|
|
2638
|
+
await this.router.detachManagedSession(replacement.sessionId).catch(() => void 0);
|
|
2639
|
+
}
|
|
2640
|
+
throw error;
|
|
2641
|
+
}
|
|
2642
|
+
if (replacement) {
|
|
2643
|
+
this.router.activateManagedSession(replacement.sessionId, replacement.target, replacement.cwd);
|
|
2644
|
+
}
|
|
2645
|
+
try {
|
|
2646
|
+
await this.router.detachManagedSession(task.sessionId);
|
|
2647
|
+
} catch (error) {
|
|
2648
|
+
this.commitOwner(owner);
|
|
2649
|
+
this.router.forgetManagedSession(task.sessionId);
|
|
2650
|
+
await this.loadTask(task, `Task "${task.name}" could not be restored after close failed.`);
|
|
2651
|
+
if (wasActive) {
|
|
2652
|
+
this.router.activateManagedSession(task.sessionId, task.target, task.cwd);
|
|
2653
|
+
}
|
|
2654
|
+
throw new Error(`Failed to close task "${task.name}".`, {
|
|
2655
|
+
cause: error
|
|
2656
|
+
});
|
|
2657
|
+
}
|
|
2658
|
+
const activeTask = nextOwner.activeTaskName ? this.findTask(nextOwner, nextOwner.activeTaskName) : void 0;
|
|
2659
|
+
return {
|
|
2660
|
+
closed: this.view(nextOwner, closedTask),
|
|
2661
|
+
...activeTask ? { active: this.view(nextOwner, activeTask) } : {}
|
|
2662
|
+
};
|
|
2663
|
+
});
|
|
2664
|
+
}
|
|
2665
|
+
reset(input) {
|
|
2666
|
+
return this.withOwnerLock(input, async () => {
|
|
2667
|
+
const owner = await this.ensureOwner(input, false);
|
|
2668
|
+
if (!owner?.activeTaskName)
|
|
2669
|
+
return void 0;
|
|
2670
|
+
const task = this.findTask(owner, owner.activeTaskName);
|
|
2671
|
+
if (!task || task.status !== "open")
|
|
2672
|
+
return void 0;
|
|
2673
|
+
const timestamp = this.nextTimestamp(owner);
|
|
2674
|
+
const sessionId = await this.createSession(task.target, task.cwd, `Could not reset task "${task.name}".`);
|
|
2675
|
+
const updatedTask = {
|
|
2676
|
+
...task,
|
|
2677
|
+
sessionId,
|
|
2678
|
+
updatedAt: timestamp,
|
|
2679
|
+
lastSelectedAt: timestamp
|
|
2680
|
+
};
|
|
2681
|
+
const nextOwner = this.replaceTask(owner, updatedTask, task.name);
|
|
2682
|
+
try {
|
|
2683
|
+
this.commitOwner(nextOwner);
|
|
2684
|
+
} catch (error) {
|
|
2685
|
+
await this.router.detachManagedSession(sessionId).catch(() => void 0);
|
|
2686
|
+
throw error;
|
|
2687
|
+
}
|
|
2688
|
+
this.router.activateManagedSession(sessionId, updatedTask.target, updatedTask.cwd);
|
|
2689
|
+
this.router.forgetManagedSession(task.sessionId);
|
|
2690
|
+
return {
|
|
2691
|
+
name: task.name,
|
|
2692
|
+
previousSessionId: task.sessionId,
|
|
2693
|
+
sessionId
|
|
2694
|
+
};
|
|
2695
|
+
});
|
|
2696
|
+
}
|
|
2697
|
+
async ensureOwner(input, createDefault) {
|
|
2698
|
+
const existing = this.getOwner(input);
|
|
2699
|
+
if (existing)
|
|
2700
|
+
return existing;
|
|
2701
|
+
const sessionId = this.router.getSession(this.channelName, input.senderId, input.chatId);
|
|
2702
|
+
const routedTarget = sessionId ? this.router.getTarget(sessionId) : void 0;
|
|
2703
|
+
if (sessionId && routedTarget && (routedTarget.channelName !== this.channelName || routedTarget.chatId !== input.chatId || routedTarget.senderId !== input.senderId)) {
|
|
2704
|
+
this.adoptForeignLegacyRoute(sessionId, routedTarget);
|
|
2705
|
+
}
|
|
2706
|
+
if (sessionId && routedTarget?.channelName === this.channelName && routedTarget.chatId === input.chatId && routedTarget.senderId === input.senderId) {
|
|
2707
|
+
const routedCwd = this.router.getSessionCwd(sessionId);
|
|
2708
|
+
if (routedCwd !== void 0 && !this.isCurrentCwd(routedCwd)) {
|
|
2709
|
+
await this.router.detachManagedSession(sessionId).catch(() => void 0);
|
|
2710
|
+
} else {
|
|
2711
|
+
const timestamp2 = this.nextTimestamp();
|
|
2712
|
+
const task2 = {
|
|
2713
|
+
name: "default",
|
|
2714
|
+
sessionId,
|
|
2715
|
+
cwd: routedCwd ?? this.cwd,
|
|
2716
|
+
isolation: "shared",
|
|
2717
|
+
status: "open",
|
|
2718
|
+
target: routedTarget,
|
|
2719
|
+
createdAt: timestamp2,
|
|
2720
|
+
updatedAt: timestamp2,
|
|
2721
|
+
lastSelectedAt: timestamp2
|
|
2722
|
+
};
|
|
2723
|
+
const adopted = this.createOwner(input, task2.name, [task2]);
|
|
2724
|
+
this.commitOwner(adopted);
|
|
2725
|
+
return adopted;
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
if (!createDefault)
|
|
2729
|
+
return void 0;
|
|
2730
|
+
const target = this.target(input);
|
|
2731
|
+
const timestamp = this.nextTimestamp();
|
|
2732
|
+
const createdSessionId = await this.createSession(target, this.cwd, "Could not create the default task.");
|
|
2733
|
+
const task = {
|
|
2734
|
+
name: "default",
|
|
2735
|
+
sessionId: createdSessionId,
|
|
2736
|
+
cwd: this.cwd,
|
|
2737
|
+
isolation: "shared",
|
|
2738
|
+
status: "open",
|
|
2739
|
+
target,
|
|
2740
|
+
createdAt: timestamp,
|
|
2741
|
+
updatedAt: timestamp,
|
|
2742
|
+
lastSelectedAt: timestamp
|
|
2743
|
+
};
|
|
2744
|
+
const created = this.createOwner(input, task.name, [task]);
|
|
2745
|
+
try {
|
|
2746
|
+
this.commitOwner(created);
|
|
2747
|
+
} catch (error) {
|
|
2748
|
+
await this.router.detachManagedSession(createdSessionId).catch(() => void 0);
|
|
2749
|
+
throw error;
|
|
2750
|
+
}
|
|
2751
|
+
this.router.activateManagedSession(createdSessionId, target, this.cwd);
|
|
2752
|
+
return created;
|
|
2753
|
+
}
|
|
2754
|
+
async resolveLocked(input, expectedSessionId, reserve) {
|
|
2755
|
+
const owner = await this.ensureOwner(input, true);
|
|
2756
|
+
if (!owner?.activeTaskName)
|
|
2757
|
+
return void 0;
|
|
2758
|
+
const task = this.findTask(owner, owner.activeTaskName);
|
|
2759
|
+
if (!task || task.status !== "open") {
|
|
2760
|
+
throw new Error("The selected Channel task is unavailable.");
|
|
2761
|
+
}
|
|
2762
|
+
if (expectedSessionId !== void 0 && task.sessionId !== expectedSessionId) {
|
|
2763
|
+
return void 0;
|
|
2764
|
+
}
|
|
2765
|
+
const release = reserve?.(task.sessionId);
|
|
2766
|
+
try {
|
|
2767
|
+
await this.loadTask(task, `Could not load task "${task.name}". No replacement session was created.`);
|
|
2768
|
+
this.router.activateManagedSession(task.sessionId, task.target, task.cwd);
|
|
2769
|
+
return task.sessionId;
|
|
2770
|
+
} catch (error) {
|
|
2771
|
+
release?.();
|
|
2772
|
+
throw error;
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
adoptForeignLegacyRoute(sessionId, target) {
|
|
2776
|
+
const cwd = this.router.getSessionCwd(sessionId);
|
|
2777
|
+
if (target.channelName !== this.channelName || cwd === void 0 || !this.isCurrentCwd(cwd)) {
|
|
2778
|
+
this.router.forgetManagedSession(sessionId);
|
|
2779
|
+
return;
|
|
2780
|
+
}
|
|
2781
|
+
const existing = this.getOwner(target);
|
|
2782
|
+
if (existing) {
|
|
2783
|
+
if (existing.tasks.some((task2) => task2.sessionId === sessionId))
|
|
2784
|
+
return;
|
|
2785
|
+
this.router.forgetManagedSession(sessionId);
|
|
2786
|
+
return;
|
|
2787
|
+
}
|
|
2788
|
+
const timestamp = this.nextTimestamp();
|
|
2789
|
+
const task = {
|
|
2790
|
+
name: "default",
|
|
2791
|
+
sessionId,
|
|
2792
|
+
cwd,
|
|
2793
|
+
isolation: "shared",
|
|
2794
|
+
status: "open",
|
|
2795
|
+
target,
|
|
2796
|
+
createdAt: timestamp,
|
|
2797
|
+
updatedAt: timestamp,
|
|
2798
|
+
lastSelectedAt: timestamp
|
|
2799
|
+
};
|
|
2800
|
+
this.commitOwner(this.createOwner(target, task.name, [task]));
|
|
2801
|
+
}
|
|
2802
|
+
assertCanLeaveSelected(owner, nextTaskName) {
|
|
2803
|
+
if (!owner.activeTaskName || nextTaskName && this.sameName(owner.activeTaskName, nextTaskName)) {
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2806
|
+
const selected = this.findTask(owner, owner.activeTaskName);
|
|
2807
|
+
if (selected && this.isBusy(selected.sessionId)) {
|
|
2808
|
+
throw new Error(`Task "${selected.name}" is still running or waiting for permission.`);
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
async loadTask(task, failureMessage) {
|
|
2812
|
+
try {
|
|
2813
|
+
return (await this.router.loadManagedSession(task.sessionId, task.target, task.cwd)).loaded;
|
|
2814
|
+
} catch (error) {
|
|
2815
|
+
throw new Error(failureMessage, { cause: error });
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
async createSession(target, cwd, failureMessage) {
|
|
2819
|
+
try {
|
|
2820
|
+
return await this.router.createManagedSession(target, cwd);
|
|
2821
|
+
} catch (error) {
|
|
2822
|
+
throw new Error(failureMessage, { cause: error });
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
withOwnerLock(input, operation) {
|
|
2826
|
+
const key = JSON.stringify([
|
|
2827
|
+
this.channelName,
|
|
2828
|
+
input.chatId,
|
|
2829
|
+
input.senderId
|
|
2830
|
+
]);
|
|
2831
|
+
const previous = this.ownerOperations.get(key) ?? Promise.resolve();
|
|
2832
|
+
const result = previous.catch(() => void 0).then(operation);
|
|
2833
|
+
const tail = result.then(() => void 0, () => void 0);
|
|
2834
|
+
this.ownerOperations.set(key, tail);
|
|
2835
|
+
return result.finally(() => {
|
|
2836
|
+
if (this.ownerOperations.get(key) === tail) {
|
|
2837
|
+
this.ownerOperations.delete(key);
|
|
2838
|
+
}
|
|
2839
|
+
});
|
|
2840
|
+
}
|
|
2841
|
+
getOwner(input) {
|
|
2842
|
+
const owner = this.registry.owners.find((candidate) => candidate.channelName === this.channelName && candidate.chatId === input.chatId && candidate.senderId === input.senderId);
|
|
2843
|
+
return owner ? this.cloneOwner(owner) : void 0;
|
|
2844
|
+
}
|
|
2845
|
+
createOwner(input, activeTaskName, tasks) {
|
|
2846
|
+
return {
|
|
2847
|
+
channelName: this.channelName,
|
|
2848
|
+
chatId: input.chatId,
|
|
2849
|
+
senderId: input.senderId,
|
|
2850
|
+
activeTaskName,
|
|
2851
|
+
tasks
|
|
2852
|
+
};
|
|
2853
|
+
}
|
|
2854
|
+
target(input) {
|
|
2855
|
+
return {
|
|
2856
|
+
channelName: this.channelName,
|
|
2857
|
+
senderId: input.senderId,
|
|
2858
|
+
chatId: input.chatId,
|
|
2859
|
+
...input.threadId !== void 0 ? { threadId: input.threadId } : {},
|
|
2860
|
+
...input.isGroup !== void 0 ? { isGroup: input.isGroup } : {}
|
|
2861
|
+
};
|
|
2862
|
+
}
|
|
2863
|
+
findTask(owner, name) {
|
|
2864
|
+
const normalized = this.normalizeName(name);
|
|
2865
|
+
return owner.tasks.find((task) => this.normalizeName(task.name) === normalized);
|
|
2866
|
+
}
|
|
2867
|
+
replaceTask(owner, task, activeTaskName) {
|
|
2868
|
+
return {
|
|
2869
|
+
...owner,
|
|
2870
|
+
activeTaskName,
|
|
2871
|
+
tasks: owner.tasks.map((candidate) => this.sameName(candidate.name, task.name) ? task : candidate)
|
|
2872
|
+
};
|
|
2873
|
+
}
|
|
2874
|
+
view(owner, task) {
|
|
2875
|
+
return {
|
|
2876
|
+
name: task.name,
|
|
2877
|
+
status: task.status,
|
|
2878
|
+
isolation: task.isolation,
|
|
2879
|
+
active: task.status === "open" && this.sameName(owner.activeTaskName, task.name)
|
|
2880
|
+
};
|
|
2881
|
+
}
|
|
2882
|
+
selection(owner, task) {
|
|
2883
|
+
return { ...this.view(owner, task), sessionId: task.sessionId };
|
|
2884
|
+
}
|
|
2885
|
+
openTaskCount(owner) {
|
|
2886
|
+
return owner.tasks.filter((task) => task.status === "open").length;
|
|
2887
|
+
}
|
|
2888
|
+
nextTimestamp(owner) {
|
|
2889
|
+
const now = this.now();
|
|
2890
|
+
if (!Number.isSafeInteger(now) || now < 0) {
|
|
2891
|
+
throw new Error("Invalid named-session clock value.");
|
|
2892
|
+
}
|
|
2893
|
+
let timestamp = now;
|
|
2894
|
+
for (const task of owner?.tasks ?? []) {
|
|
2895
|
+
timestamp = Math.max(timestamp, task.createdAt + 1, task.updatedAt + 1, task.lastSelectedAt + 1);
|
|
2896
|
+
}
|
|
2897
|
+
if (!Number.isSafeInteger(timestamp)) {
|
|
2898
|
+
throw new Error("Named-session timestamp limit reached.");
|
|
2899
|
+
}
|
|
2900
|
+
return timestamp;
|
|
2901
|
+
}
|
|
2902
|
+
validateName(name) {
|
|
2903
|
+
if (!TASK_NAME_PATTERN.test(name)) {
|
|
2904
|
+
throw new Error("Task names must be 1-32 ASCII letters, numbers, underscores, or hyphens and start with a letter or number.");
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
normalizeName(name) {
|
|
2908
|
+
return name.toLowerCase();
|
|
2909
|
+
}
|
|
2910
|
+
sameName(a, b) {
|
|
2911
|
+
return a !== null && this.normalizeName(a) === this.normalizeName(b);
|
|
2912
|
+
}
|
|
2913
|
+
cloneOwner(owner) {
|
|
2914
|
+
return {
|
|
2915
|
+
...owner,
|
|
2916
|
+
tasks: owner.tasks.map((task) => ({
|
|
2917
|
+
...task,
|
|
2918
|
+
target: { ...task.target }
|
|
2919
|
+
}))
|
|
2920
|
+
};
|
|
2921
|
+
}
|
|
2922
|
+
commitOwner(owner) {
|
|
2923
|
+
const owners = this.registry.owners.map((candidate) => candidate.channelName === owner.channelName && candidate.chatId === owner.chatId && candidate.senderId === owner.senderId ? owner : candidate);
|
|
2924
|
+
if (!owners.some((candidate) => candidate.channelName === owner.channelName && candidate.chatId === owner.chatId && candidate.senderId === owner.senderId)) {
|
|
2925
|
+
owners.push(owner);
|
|
2926
|
+
}
|
|
2927
|
+
const next = { version: REGISTRY_VERSION, owners };
|
|
2928
|
+
if (!this.isRegistry(next)) {
|
|
2929
|
+
throw new Error("Invalid named-session registry update.");
|
|
2930
|
+
}
|
|
2931
|
+
this.writeRegistry(next);
|
|
2932
|
+
this.registry = next;
|
|
2933
|
+
}
|
|
2934
|
+
readRegistry() {
|
|
2935
|
+
if (!existsSync4(this.filePath)) {
|
|
2936
|
+
return { version: REGISTRY_VERSION, owners: [] };
|
|
2937
|
+
}
|
|
2938
|
+
let value;
|
|
2939
|
+
try {
|
|
2940
|
+
value = JSON.parse(readFileSync4(this.filePath, "utf8"));
|
|
2941
|
+
} catch (error) {
|
|
2942
|
+
throw new Error(`Failed to read named-session registry: ${this.filePath}`, {
|
|
2943
|
+
cause: error
|
|
2944
|
+
});
|
|
2945
|
+
}
|
|
2946
|
+
if (!this.isRegistry(value)) {
|
|
2947
|
+
if (this.isRegistry(value, false)) {
|
|
2948
|
+
const stalePath = `${this.filePath}.stale-${randomUUID2()}`;
|
|
2949
|
+
try {
|
|
2950
|
+
renameSync4(this.filePath, stalePath);
|
|
2951
|
+
} catch (error) {
|
|
2952
|
+
throw new Error("Failed to archive stale named-session registry.", {
|
|
2953
|
+
cause: error
|
|
2954
|
+
});
|
|
2955
|
+
}
|
|
2956
|
+
process3.stderr.write("[NamedSessionManager] Archived a stale registry after the channel working directory changed.\n");
|
|
2957
|
+
return { version: REGISTRY_VERSION, owners: [] };
|
|
2958
|
+
}
|
|
2959
|
+
throw new Error(`Invalid named-session registry: ${this.filePath}`);
|
|
2960
|
+
}
|
|
2961
|
+
return value;
|
|
2962
|
+
}
|
|
2963
|
+
writeRegistry(registry) {
|
|
2964
|
+
const dir = dirname4(this.filePath);
|
|
2965
|
+
const tempPath = `${this.filePath}.${process3.pid}-${randomUUID2()}.tmp`;
|
|
2966
|
+
try {
|
|
2967
|
+
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
2968
|
+
try {
|
|
2969
|
+
chmodSync3(dir, 448);
|
|
2970
|
+
} catch {
|
|
2971
|
+
}
|
|
2972
|
+
writeFileSync4(tempPath, JSON.stringify(registry, null, 2), {
|
|
2973
|
+
encoding: "utf8",
|
|
2974
|
+
mode: 384
|
|
2975
|
+
});
|
|
2976
|
+
renameSync4(tempPath, this.filePath);
|
|
2977
|
+
try {
|
|
2978
|
+
chmodSync3(this.filePath, 384);
|
|
2979
|
+
} catch {
|
|
2980
|
+
}
|
|
2981
|
+
} catch (error) {
|
|
2982
|
+
throw new Error("Failed to persist named-session registry.", {
|
|
2983
|
+
cause: error
|
|
2984
|
+
});
|
|
2985
|
+
} finally {
|
|
2986
|
+
try {
|
|
2987
|
+
rmSync2(tempPath, { force: true });
|
|
2988
|
+
} catch {
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
isRegistry(value, requireCurrentCwd = true) {
|
|
2993
|
+
if (!this.isRecord(value) || value["version"] !== REGISTRY_VERSION) {
|
|
2994
|
+
return false;
|
|
2995
|
+
}
|
|
2996
|
+
const owners = value["owners"];
|
|
2997
|
+
if (!Array.isArray(owners))
|
|
2998
|
+
return false;
|
|
2999
|
+
const ownerKeys = /* @__PURE__ */ new Set();
|
|
3000
|
+
const sessionIds = /* @__PURE__ */ new Set();
|
|
3001
|
+
for (const owner of owners) {
|
|
3002
|
+
if (!this.isOwner(owner, ownerKeys, sessionIds, requireCurrentCwd)) {
|
|
3003
|
+
return false;
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
return true;
|
|
3007
|
+
}
|
|
3008
|
+
isOwner(value, ownerKeys, sessionIds, requireCurrentCwd) {
|
|
3009
|
+
if (!this.isRecord(value))
|
|
3010
|
+
return false;
|
|
3011
|
+
const channelName = value["channelName"];
|
|
3012
|
+
const chatId = value["chatId"];
|
|
3013
|
+
const senderId = value["senderId"];
|
|
3014
|
+
const activeTaskName = value["activeTaskName"];
|
|
3015
|
+
const tasks = value["tasks"];
|
|
3016
|
+
if (channelName !== this.channelName || typeof chatId !== "string" || typeof senderId !== "string" || activeTaskName !== null && typeof activeTaskName !== "string" || !Array.isArray(tasks)) {
|
|
3017
|
+
return false;
|
|
3018
|
+
}
|
|
3019
|
+
const ownerKey = JSON.stringify([channelName, chatId, senderId]);
|
|
3020
|
+
if (ownerKeys.has(ownerKey))
|
|
3021
|
+
return false;
|
|
3022
|
+
ownerKeys.add(ownerKey);
|
|
3023
|
+
const names = /* @__PURE__ */ new Set();
|
|
3024
|
+
let openTasks = 0;
|
|
3025
|
+
let activeFound = activeTaskName === null;
|
|
3026
|
+
for (const task of tasks) {
|
|
3027
|
+
if (!this.isTask(task, channelName, chatId, senderId, requireCurrentCwd)) {
|
|
3028
|
+
return false;
|
|
3029
|
+
}
|
|
3030
|
+
const normalized = this.normalizeName(task.name);
|
|
3031
|
+
if (names.has(normalized) || sessionIds.has(task.sessionId))
|
|
3032
|
+
return false;
|
|
3033
|
+
names.add(normalized);
|
|
3034
|
+
sessionIds.add(task.sessionId);
|
|
3035
|
+
if (task.status === "open")
|
|
3036
|
+
openTasks++;
|
|
3037
|
+
if (activeTaskName !== null && task.status === "open" && this.sameName(activeTaskName, task.name)) {
|
|
3038
|
+
activeFound = true;
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
return openTasks <= MAX_OPEN_TASKS && activeFound;
|
|
3042
|
+
}
|
|
3043
|
+
isTask(value, channelName, chatId, senderId, requireCurrentCwd) {
|
|
3044
|
+
if (!this.isRecord(value) || !this.isRecord(value["target"]))
|
|
3045
|
+
return false;
|
|
3046
|
+
const target = value["target"];
|
|
3047
|
+
return typeof value["name"] === "string" && TASK_NAME_PATTERN.test(value["name"]) && typeof value["sessionId"] === "string" && value["sessionId"].length > 0 && typeof value["cwd"] === "string" && (!requireCurrentCwd || this.isCurrentCwd(value["cwd"])) && value["isolation"] === "shared" && (value["status"] === "open" || value["status"] === "closed") && this.isTimestamp(value["createdAt"]) && this.isTimestamp(value["updatedAt"]) && this.isTimestamp(value["lastSelectedAt"]) && target["channelName"] === channelName && target["chatId"] === chatId && target["senderId"] === senderId && (target["threadId"] === void 0 || typeof target["threadId"] === "string") && (target["isGroup"] === void 0 || typeof target["isGroup"] === "boolean");
|
|
3048
|
+
}
|
|
3049
|
+
isCurrentCwd(cwd) {
|
|
3050
|
+
return canonicalizeWorkspacePath(cwd) === this.canonicalCwd;
|
|
3051
|
+
}
|
|
3052
|
+
isTimestamp(value) {
|
|
3053
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
3054
|
+
}
|
|
3055
|
+
isRecord(value) {
|
|
3056
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3057
|
+
}
|
|
3058
|
+
};
|
|
3059
|
+
|
|
2210
3060
|
// packages/channels/base/dist/ChannelLoopScheduler.js
|
|
2211
3061
|
init_esbuild_shims();
|
|
2212
3062
|
var MAX_RESULT_PREVIEW_LENGTH = 500;
|
|
@@ -2901,6 +3751,7 @@ var ChannelBase = class {
|
|
|
2901
3751
|
groupPairingNotified = /* @__PURE__ */ new Map();
|
|
2902
3752
|
loopController;
|
|
2903
3753
|
observedContacts;
|
|
3754
|
+
namedSessions;
|
|
2904
3755
|
observedContactEnvelopes = /* @__PURE__ */ new WeakSet();
|
|
2905
3756
|
instructedSessions = /* @__PURE__ */ new Set();
|
|
2906
3757
|
unattendedMemorySessions = /* @__PURE__ */ new Set();
|
|
@@ -2909,6 +3760,8 @@ var ChannelBase = class {
|
|
|
2909
3760
|
commands = /* @__PURE__ */ new Map();
|
|
2910
3761
|
/** Per-session promise chain to serialize prompt + send (followup mode). */
|
|
2911
3762
|
sessionQueues = /* @__PURE__ */ new Map();
|
|
3763
|
+
queuedTurns = /* @__PURE__ */ new Map();
|
|
3764
|
+
namedTurnBindings = /* @__PURE__ */ new WeakMap();
|
|
2912
3765
|
registerBridgeEvents;
|
|
2913
3766
|
bridgeRecovery;
|
|
2914
3767
|
/**
|
|
@@ -2985,7 +3838,16 @@ var ChannelBase = class {
|
|
|
2985
3838
|
await this.pushProactive(target, text);
|
|
2986
3839
|
return;
|
|
2987
3840
|
}
|
|
2988
|
-
await this.
|
|
3841
|
+
await this.deliverBackgroundReply(target.chatId, text, sessionId);
|
|
3842
|
+
}
|
|
3843
|
+
/**
|
|
3844
|
+
* Fallback delivery of a background response when proactive send is
|
|
3845
|
+
* unavailable. Adapters whose turn replies bypass sendResponseMessage (to
|
|
3846
|
+
* stay out of turn-scoped streaming state, for example) override only this
|
|
3847
|
+
* step instead of re-implementing the whole dispatch flow.
|
|
3848
|
+
*/
|
|
3849
|
+
async deliverBackgroundReply(chatId, text, sessionId) {
|
|
3850
|
+
await this.sendResponseMessage(chatId, text, sessionId);
|
|
2989
3851
|
}
|
|
2990
3852
|
async dispatchPermissionRequest(event) {
|
|
2991
3853
|
const target = this.permissionTargetForEvent(event);
|
|
@@ -3206,6 +4068,21 @@ var ChannelBase = class {
|
|
|
3206
4068
|
this.dmGate = new DmGate(config.dmPolicy);
|
|
3207
4069
|
this.gate = new SenderGate(config.senderPolicy, config.allowedUsers, pairingStore);
|
|
3208
4070
|
this.router = options?.router || new SessionRouter(bridge, config.cwd, config.sessionScope);
|
|
4071
|
+
if (config.multiSession) {
|
|
4072
|
+
if (config.sessionScope !== "user") {
|
|
4073
|
+
throw new Error(`Channel "${name}" requires sessionScope "user" when multiSession is enabled.`);
|
|
4074
|
+
}
|
|
4075
|
+
if (!options?.stateDir) {
|
|
4076
|
+
throw new Error(`Channel "${name}" multiSession is available only in daemon-managed mode.`);
|
|
4077
|
+
}
|
|
4078
|
+
this.namedSessions = new NamedSessionManager({
|
|
4079
|
+
channelName: name,
|
|
4080
|
+
cwd: config.cwd,
|
|
4081
|
+
filePath: join5(options.stateDir, "named-sessions.json"),
|
|
4082
|
+
router: this.router,
|
|
4083
|
+
isBusy: /* @__PURE__ */ __name((sessionId) => this.isNamedSessionBusy(sessionId), "isBusy")
|
|
4084
|
+
});
|
|
4085
|
+
}
|
|
3209
4086
|
this.registerSharedCommands();
|
|
3210
4087
|
if (this.loopController) {
|
|
3211
4088
|
bridge.registerChannelLoopToolHandler?.(this.channelLoopToolHandler);
|
|
@@ -3369,7 +4246,7 @@ var ChannelBase = class {
|
|
|
3369
4246
|
ensureOutputSegment(sessionId, active) {
|
|
3370
4247
|
if (!active.owner)
|
|
3371
4248
|
return void 0;
|
|
3372
|
-
const segmentId = active.activeSegmentId ??
|
|
4249
|
+
const segmentId = active.activeSegmentId ?? randomUUID3();
|
|
3373
4250
|
const context = this.outputSegmentContext(sessionId, active, segmentId);
|
|
3374
4251
|
if (context)
|
|
3375
4252
|
active.activeSegmentId = segmentId;
|
|
@@ -3598,8 +4475,11 @@ var ChannelBase = class {
|
|
|
3598
4475
|
imageBase64: void 0,
|
|
3599
4476
|
imageMimeType: void 0
|
|
3600
4477
|
};
|
|
4478
|
+
if (this.namedSessions) {
|
|
4479
|
+
this.bindNamedTurn(syntheticEnvelope, sessionId);
|
|
4480
|
+
}
|
|
3601
4481
|
this.markPreflighted(syntheticEnvelope);
|
|
3602
|
-
this.
|
|
4482
|
+
void this.processPreflightedInbound(syntheticEnvelope).catch((err) => {
|
|
3603
4483
|
process.stderr.write(`[${this.name}] dropped ${lost} buffered message(s) after ${taskLabel} for session ${sessionId} (last sender ${lastEnvelope.senderId}): ${err instanceof Error ? err.message : String(err)}
|
|
3604
4484
|
`);
|
|
3605
4485
|
});
|
|
@@ -3695,7 +4575,7 @@ ${promptText}` : promptText;
|
|
|
3695
4575
|
doneResolve = resolve3;
|
|
3696
4576
|
});
|
|
3697
4577
|
const promptState = {
|
|
3698
|
-
runId:
|
|
4578
|
+
runId: randomUUID3(),
|
|
3699
4579
|
cancelled: false,
|
|
3700
4580
|
done,
|
|
3701
4581
|
resolve: doneResolve,
|
|
@@ -3911,7 +4791,7 @@ ${promptText}` : promptText;
|
|
|
3911
4791
|
doneResolve = resolve3;
|
|
3912
4792
|
});
|
|
3913
4793
|
const promptState = {
|
|
3914
|
-
runId:
|
|
4794
|
+
runId: randomUUID3(),
|
|
3915
4795
|
cancelled: false,
|
|
3916
4796
|
done,
|
|
3917
4797
|
resolve: doneResolve,
|
|
@@ -4292,7 +5172,7 @@ ${promptText}` : promptText;
|
|
|
4292
5172
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can cancel requests in this shared session.");
|
|
4293
5173
|
return true;
|
|
4294
5174
|
}
|
|
4295
|
-
const activeSessionId = this.findActiveSessionId(envelope);
|
|
5175
|
+
const activeSessionId = this.namedSessions ? await this.findNamedActiveSessionId(envelope) : this.findActiveSessionId(envelope);
|
|
4296
5176
|
if (!activeSessionId) {
|
|
4297
5177
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No request is currently running.");
|
|
4298
5178
|
return true;
|
|
@@ -4517,10 +5397,203 @@ ${requestList}`);
|
|
|
4517
5397
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, accepted ? decision === "approve" ? "Permission approved." : decision === "approve-always" ? "Permission approved always." : "Permission denied." : "Permission request is no longer pending.");
|
|
4518
5398
|
return true;
|
|
4519
5399
|
}
|
|
5400
|
+
namedSessionOwner(envelope) {
|
|
5401
|
+
return {
|
|
5402
|
+
senderId: envelope.senderId,
|
|
5403
|
+
chatId: envelope.chatId,
|
|
5404
|
+
...envelope.threadId !== void 0 ? { threadId: envelope.threadId } : {},
|
|
5405
|
+
...envelope.isGroup !== void 0 ? { isGroup: envelope.isGroup } : {}
|
|
5406
|
+
};
|
|
5407
|
+
}
|
|
5408
|
+
async currentSessionId(envelope) {
|
|
5409
|
+
if (this.namedSessions) {
|
|
5410
|
+
return (await this.namedSessions.current(this.namedSessionOwner(envelope)))?.sessionId;
|
|
5411
|
+
}
|
|
5412
|
+
return this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
5413
|
+
}
|
|
5414
|
+
reserveQueuedTurn(sessionId) {
|
|
5415
|
+
this.queuedTurns.set(sessionId, (this.queuedTurns.get(sessionId) ?? 0) + 1);
|
|
5416
|
+
}
|
|
5417
|
+
releaseQueuedTurn(sessionId) {
|
|
5418
|
+
const remaining = (this.queuedTurns.get(sessionId) ?? 1) - 1;
|
|
5419
|
+
if (remaining === 0) {
|
|
5420
|
+
this.queuedTurns.delete(sessionId);
|
|
5421
|
+
} else {
|
|
5422
|
+
this.queuedTurns.set(sessionId, remaining);
|
|
5423
|
+
}
|
|
5424
|
+
}
|
|
5425
|
+
bindNamedTurn(envelope, sessionId) {
|
|
5426
|
+
const binding = {
|
|
5427
|
+
sessionId,
|
|
5428
|
+
generation: this.sessionGenerations.get(sessionId) ?? 0,
|
|
5429
|
+
claimed: false,
|
|
5430
|
+
released: false
|
|
5431
|
+
};
|
|
5432
|
+
this.namedTurnBindings.set(envelope, binding);
|
|
5433
|
+
this.reserveQueuedTurn(sessionId);
|
|
5434
|
+
return binding;
|
|
5435
|
+
}
|
|
5436
|
+
releaseNamedTurnBinding(binding) {
|
|
5437
|
+
if (binding.sessionId === null || binding.claimed || binding.released) {
|
|
5438
|
+
return;
|
|
5439
|
+
}
|
|
5440
|
+
binding.released = true;
|
|
5441
|
+
this.releaseQueuedTurn(binding.sessionId);
|
|
5442
|
+
}
|
|
5443
|
+
finishNamedTurnBinding(envelope) {
|
|
5444
|
+
const binding = this.namedTurnBindings.get(envelope);
|
|
5445
|
+
if (!binding)
|
|
5446
|
+
return;
|
|
5447
|
+
this.releaseNamedTurnBinding(binding);
|
|
5448
|
+
this.namedTurnBindings.delete(envelope);
|
|
5449
|
+
}
|
|
5450
|
+
bypassesNamedTurnBinding(envelope) {
|
|
5451
|
+
const parsed = this.parseCommand(envelope.text);
|
|
5452
|
+
if (parsed && this.commands.has(parsed.command))
|
|
5453
|
+
return true;
|
|
5454
|
+
const bangText = envelope.text.trimStart();
|
|
5455
|
+
return bangText.startsWith("!") && (envelope.isGroup || this.isSharedSession(envelope));
|
|
5456
|
+
}
|
|
5457
|
+
async prepareNamedTurnBinding(envelope) {
|
|
5458
|
+
const namedSessions = this.namedSessions;
|
|
5459
|
+
if (!namedSessions || this.namedTurnBindings.has(envelope))
|
|
5460
|
+
return true;
|
|
5461
|
+
if (this.bypassesNamedTurnBinding(envelope))
|
|
5462
|
+
return true;
|
|
5463
|
+
try {
|
|
5464
|
+
const sessionId = await namedSessions.resolve(this.namedSessionOwner(envelope), void 0, (resolvedSessionId) => {
|
|
5465
|
+
const binding = this.bindNamedTurn(envelope, resolvedSessionId);
|
|
5466
|
+
return () => this.releaseNamedTurnBinding(binding);
|
|
5467
|
+
});
|
|
5468
|
+
if (!sessionId && !this.namedTurnBindings.has(envelope)) {
|
|
5469
|
+
this.namedTurnBindings.set(envelope, {
|
|
5470
|
+
sessionId: null,
|
|
5471
|
+
generation: 0,
|
|
5472
|
+
claimed: false,
|
|
5473
|
+
released: true
|
|
5474
|
+
});
|
|
5475
|
+
}
|
|
5476
|
+
return true;
|
|
5477
|
+
} catch (error) {
|
|
5478
|
+
this.finishNamedTurnBinding(envelope);
|
|
5479
|
+
await this.sendNamedSessionError(envelope, error);
|
|
5480
|
+
return false;
|
|
5481
|
+
}
|
|
5482
|
+
}
|
|
5483
|
+
isNamedSessionBusy(sessionId) {
|
|
5484
|
+
if ((this.queuedTurns.get(sessionId) ?? 0) > 0)
|
|
5485
|
+
return true;
|
|
5486
|
+
if (this.activePrompts.has(sessionId))
|
|
5487
|
+
return true;
|
|
5488
|
+
for (const permission of this.pendingPermissions.values()) {
|
|
5489
|
+
if (permission.sessionId === sessionId)
|
|
5490
|
+
return true;
|
|
5491
|
+
}
|
|
5492
|
+
try {
|
|
5493
|
+
return this.bridge.listSessions?.().some((session) => session.sessionId === sessionId && session.hasActivePrompt) ?? false;
|
|
5494
|
+
} catch {
|
|
5495
|
+
return true;
|
|
5496
|
+
}
|
|
5497
|
+
}
|
|
5498
|
+
async sendNamedSessionError(envelope, error) {
|
|
5499
|
+
const message = error instanceof Error ? sanitizeDisplayText(error.message, 500) : "Named-session operation failed.";
|
|
5500
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, message || "Named-session operation failed.");
|
|
5501
|
+
}
|
|
5502
|
+
async handleNamedSessionsCommand(envelope, args) {
|
|
5503
|
+
const namedSessions = this.namedSessions;
|
|
5504
|
+
if (!namedSessions)
|
|
5505
|
+
return false;
|
|
5506
|
+
const normalized = args.trim().toLowerCase();
|
|
5507
|
+
if (normalized !== "" && normalized !== "all") {
|
|
5508
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Usage: /sessions [all]");
|
|
5509
|
+
return true;
|
|
5510
|
+
}
|
|
5511
|
+
try {
|
|
5512
|
+
const tasks = await namedSessions.list(this.namedSessionOwner(envelope), normalized === "all");
|
|
5513
|
+
if (tasks.length === 0) {
|
|
5514
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, normalized === "all" ? "No named tasks." : "No open named tasks.");
|
|
5515
|
+
return true;
|
|
5516
|
+
}
|
|
5517
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, [
|
|
5518
|
+
normalized === "all" ? "Tasks:" : "Open tasks:",
|
|
5519
|
+
...tasks.map((task) => `${task.active ? "*" : "-"} ${task.name} (${task.status}, ${task.isolation})`)
|
|
5520
|
+
].join("\n"));
|
|
5521
|
+
} catch (error) {
|
|
5522
|
+
await this.sendNamedSessionError(envelope, error);
|
|
5523
|
+
}
|
|
5524
|
+
return true;
|
|
5525
|
+
}
|
|
5526
|
+
async handleNamedSessionCommand(envelope, args) {
|
|
5527
|
+
const namedSessions = this.namedSessions;
|
|
5528
|
+
if (!namedSessions)
|
|
5529
|
+
return false;
|
|
5530
|
+
const parts = args.trim().split(/\s+/u).filter(Boolean);
|
|
5531
|
+
const subcommand = parts.shift()?.toLowerCase() ?? "";
|
|
5532
|
+
const owner = this.namedSessionOwner(envelope);
|
|
5533
|
+
try {
|
|
5534
|
+
switch (subcommand) {
|
|
5535
|
+
case "current": {
|
|
5536
|
+
if (parts.length > 0)
|
|
5537
|
+
break;
|
|
5538
|
+
const current = await namedSessions.current(owner);
|
|
5539
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, current ? `Current task: ${current.name} (${current.isolation})` : "No task is currently selected. Use /session new <name> or /session use <name>.");
|
|
5540
|
+
return true;
|
|
5541
|
+
}
|
|
5542
|
+
case "new": {
|
|
5543
|
+
if (parts.includes("--worktree")) {
|
|
5544
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Worktree tasks are not available in Part 2 yet. Create a shared task with /session new <name>.");
|
|
5545
|
+
return true;
|
|
5546
|
+
}
|
|
5547
|
+
if (parts.length !== 1)
|
|
5548
|
+
break;
|
|
5549
|
+
const created = await namedSessions.create(owner, parts[0]);
|
|
5550
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Created and selected task "${created.name}" (shared workspace).`);
|
|
5551
|
+
return true;
|
|
5552
|
+
}
|
|
5553
|
+
case "use": {
|
|
5554
|
+
if (parts.length !== 1)
|
|
5555
|
+
break;
|
|
5556
|
+
const selected = await namedSessions.use(owner, parts[0]);
|
|
5557
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Selected task "${selected.name}" (${selected.isolation} workspace).`);
|
|
5558
|
+
return true;
|
|
5559
|
+
}
|
|
5560
|
+
case "close": {
|
|
5561
|
+
if (parts.length !== 1)
|
|
5562
|
+
break;
|
|
5563
|
+
const result = await namedSessions.close(owner, parts[0]);
|
|
5564
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, result.active ? `Closed task "${result.closed.name}". Selected "${result.active.name}".` : `Closed task "${result.closed.name}". No task is selected.`);
|
|
5565
|
+
return true;
|
|
5566
|
+
}
|
|
5567
|
+
case "cancel":
|
|
5568
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Named task cancellation is not available in Part 2 yet. Wait for the selected task to finish before switching; Telegram users can use /cancel.");
|
|
5569
|
+
return true;
|
|
5570
|
+
default:
|
|
5571
|
+
break;
|
|
5572
|
+
}
|
|
5573
|
+
} catch (error) {
|
|
5574
|
+
await this.sendNamedSessionError(envelope, error);
|
|
5575
|
+
return true;
|
|
5576
|
+
}
|
|
5577
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Usage: /session current | /session new <name> | /session use <name> | /session close <name> | /session cancel [<name>]");
|
|
5578
|
+
return true;
|
|
5579
|
+
}
|
|
4520
5580
|
/** Register shared slash commands. Called from constructor. */
|
|
4521
5581
|
registerSharedCommands() {
|
|
4522
5582
|
const doClear = /* @__PURE__ */ __name(async (envelope) => {
|
|
4523
|
-
|
|
5583
|
+
let resetTaskName;
|
|
5584
|
+
let removedIds;
|
|
5585
|
+
if (this.namedSessions) {
|
|
5586
|
+
try {
|
|
5587
|
+
const reset = await this.namedSessions.reset(this.namedSessionOwner(envelope));
|
|
5588
|
+
resetTaskName = reset?.name;
|
|
5589
|
+
removedIds = reset ? [reset.previousSessionId] : [];
|
|
5590
|
+
} catch (error) {
|
|
5591
|
+
await this.sendNamedSessionError(envelope, error);
|
|
5592
|
+
return;
|
|
5593
|
+
}
|
|
5594
|
+
} else {
|
|
5595
|
+
removedIds = this.router.removeSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
5596
|
+
}
|
|
4524
5597
|
this.clearPendingGroupHistory(envelope);
|
|
4525
5598
|
if (removedIds.length > 0) {
|
|
4526
5599
|
for (const id of removedIds) {
|
|
@@ -4566,7 +5639,7 @@ ${requestList}`);
|
|
|
4566
5639
|
}
|
|
4567
5640
|
this.discardRetiredSession(this.bridge, id, "cleared");
|
|
4568
5641
|
}
|
|
4569
|
-
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Session cleared. The next message starts a fresh conversation.");
|
|
5642
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, resetTaskName ? `Task "${resetTaskName}" reset with a fresh conversation.` : "Session cleared. The next message starts a fresh conversation.");
|
|
4570
5643
|
} else {
|
|
4571
5644
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No active session to clear.");
|
|
4572
5645
|
}
|
|
@@ -4586,6 +5659,10 @@ ${requestList}`);
|
|
|
4586
5659
|
this.registerCommand("clear", clearHandler);
|
|
4587
5660
|
this.registerCommand("reset", clearHandler);
|
|
4588
5661
|
this.registerCommand("new", clearHandler);
|
|
5662
|
+
if (this.namedSessions) {
|
|
5663
|
+
this.registerCommand("sessions", (envelope, args) => this.handleNamedSessionsCommand(envelope, args));
|
|
5664
|
+
this.registerCommand("session", (envelope, args) => this.handleNamedSessionCommand(envelope, args));
|
|
5665
|
+
}
|
|
4589
5666
|
this.registerCommand("approve", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "approve"));
|
|
4590
5667
|
this.registerCommand("approve-always", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "approve-always"));
|
|
4591
5668
|
this.registerCommand("deny", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "deny"));
|
|
@@ -4594,7 +5671,7 @@ ${requestList}`);
|
|
|
4594
5671
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can view this shared session.");
|
|
4595
5672
|
return true;
|
|
4596
5673
|
}
|
|
4597
|
-
const active = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
5674
|
+
const active = this.namedSessions ? Boolean(await this.currentSessionId(envelope)) : this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
4598
5675
|
const scopeNote = this.config.sessionScope === "single" ? " (shared channel-wide)" : this.isSharedSession(envelope) ? envelope.isGroup ? " (shared by this group)" : "" : envelope.isGroup ? " (private to you)" : "";
|
|
4599
5676
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, [
|
|
4600
5677
|
`Channel: ${this.name}`,
|
|
@@ -4619,7 +5696,11 @@ ${requestList}`);
|
|
|
4619
5696
|
"/status \u2014 Show session info",
|
|
4620
5697
|
"/approve [request-id] \u2014 Approve a pending permission request",
|
|
4621
5698
|
"/approve-always [request-id] \u2014 Always approve a pending permission request",
|
|
4622
|
-
"/deny [request-id] \u2014 Deny a pending permission request"
|
|
5699
|
+
"/deny [request-id] \u2014 Deny a pending permission request",
|
|
5700
|
+
...this.namedSessions ? [
|
|
5701
|
+
"/sessions [all] \u2014 List your named tasks",
|
|
5702
|
+
"/session current|new|use|close \u2014 Manage your named tasks"
|
|
5703
|
+
] : []
|
|
4623
5704
|
];
|
|
4624
5705
|
const sharedCmds = /* @__PURE__ */ new Set([
|
|
4625
5706
|
"help",
|
|
@@ -4633,7 +5714,9 @@ ${requestList}`);
|
|
|
4633
5714
|
"channel-memory",
|
|
4634
5715
|
"forget-channel",
|
|
4635
5716
|
"who",
|
|
4636
|
-
"status"
|
|
5717
|
+
"status",
|
|
5718
|
+
"sessions",
|
|
5719
|
+
"session"
|
|
4637
5720
|
]);
|
|
4638
5721
|
const platformCmds = [...this.commands.keys()].filter((c) => !sharedCmds.has(c));
|
|
4639
5722
|
if (platformCmds.length > 0) {
|
|
@@ -4641,7 +5724,7 @@ ${requestList}`);
|
|
|
4641
5724
|
lines.push(`/${cmd}`);
|
|
4642
5725
|
}
|
|
4643
5726
|
}
|
|
4644
|
-
const sessionId = this.
|
|
5727
|
+
const sessionId = await this.currentSessionId(envelope);
|
|
4645
5728
|
const agentCommands = sessionId ? this.getAgentCommandsForSession(sessionId) : this.bridge.availableCommands;
|
|
4646
5729
|
if (agentCommands.length > 0) {
|
|
4647
5730
|
lines.push("", "Agent commands (forwarded to Qwen Code):");
|
|
@@ -4658,7 +5741,7 @@ ${requestList}`);
|
|
|
4658
5741
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can view this shared session.");
|
|
4659
5742
|
return true;
|
|
4660
5743
|
}
|
|
4661
|
-
const hasSession = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
5744
|
+
const hasSession = this.namedSessions ? Boolean(await this.currentSessionId(envelope)) : this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
4662
5745
|
const policy = this.config.senderPolicy;
|
|
4663
5746
|
const lines = [
|
|
4664
5747
|
`Session: ${hasSession ? "active" : "none"}`,
|
|
@@ -4989,6 +6072,10 @@ ${requestList}`);
|
|
|
4989
6072
|
const sessionId = this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
4990
6073
|
return sessionId && this.activePrompts.has(sessionId) ? sessionId : void 0;
|
|
4991
6074
|
}
|
|
6075
|
+
async findNamedActiveSessionId(envelope) {
|
|
6076
|
+
const sessionId = await this.currentSessionId(envelope);
|
|
6077
|
+
return sessionId && this.activePrompts.has(sessionId) ? sessionId : void 0;
|
|
6078
|
+
}
|
|
4992
6079
|
channelMemoryTarget(envelope) {
|
|
4993
6080
|
return {
|
|
4994
6081
|
channelName: this.name,
|
|
@@ -5716,9 +6803,12 @@ ${formatted.join("\n")}
|
|
|
5716
6803
|
${CURRENT_MESSAGE_MARKER}
|
|
5717
6804
|
${promptText}`;
|
|
5718
6805
|
}
|
|
5719
|
-
preflightInbound(envelope) {
|
|
5720
|
-
const groupResult = this.groupGate.check(envelope
|
|
5721
|
-
|
|
6806
|
+
preflightInbound(envelope, options = {}) {
|
|
6807
|
+
const groupResult = this.groupGate.check(envelope, {
|
|
6808
|
+
createPairingRequest: !options.deferPairingRequests
|
|
6809
|
+
});
|
|
6810
|
+
const deferredGroupPairing = options.deferPairingRequests === true && groupResult.reason === "pairing_trigger_required" && (envelope.isMentioned || envelope.isReplyToBot);
|
|
6811
|
+
if (!groupResult.allowed && !deferredGroupPairing) {
|
|
5722
6812
|
if (groupResult.pairing !== void 0) {
|
|
5723
6813
|
this.logPreflightRejected("group_pairing_required");
|
|
5724
6814
|
return this.onGroupPairingRequired(envelope.chatId, groupResult.pairing, envelope.threadId).then(() => false).catch((err) => {
|
|
@@ -5745,6 +6835,10 @@ ${promptText}`;
|
|
|
5745
6835
|
this.markPreflighted(envelope);
|
|
5746
6836
|
return true;
|
|
5747
6837
|
}
|
|
6838
|
+
if (options.deferPairingRequests === true && this.config.senderPolicy === "pairing" && !this.gate.isAllowed(envelope.senderId)) {
|
|
6839
|
+
this.markPreflighted(envelope);
|
|
6840
|
+
return true;
|
|
6841
|
+
}
|
|
5748
6842
|
const result = this.gate.check(envelope.senderId, envelope.senderName);
|
|
5749
6843
|
if (!result.allowed) {
|
|
5750
6844
|
if (result.pairing !== void 0) {
|
|
@@ -5781,7 +6875,50 @@ ${promptText}`;
|
|
|
5781
6875
|
const preflight = this.preflightInbound(envelope);
|
|
5782
6876
|
if (!(isPromiseLike(preflight) ? await preflight : preflight))
|
|
5783
6877
|
return;
|
|
5784
|
-
await this.
|
|
6878
|
+
await this.processPreflightedInbound(envelope);
|
|
6879
|
+
}
|
|
6880
|
+
async prepareThenHandleInbound(envelope, prepare, preflightOptions = {}) {
|
|
6881
|
+
const preflight = this.preflightInbound(envelope, preflightOptions);
|
|
6882
|
+
if (!(isPromiseLike(preflight) ? await preflight : preflight))
|
|
6883
|
+
return;
|
|
6884
|
+
if (this.namedSessions) {
|
|
6885
|
+
const result = await this.namedSessions.resolveAfterPreparation(this.namedSessionOwner(envelope), prepare, () => !this.bypassesNamedTurnBinding(envelope), (sessionId) => {
|
|
6886
|
+
const binding = this.bindNamedTurn(envelope, sessionId);
|
|
6887
|
+
return () => this.releaseNamedTurnBinding(binding);
|
|
6888
|
+
});
|
|
6889
|
+
if (result.status === "aborted")
|
|
6890
|
+
return;
|
|
6891
|
+
if (result.status === "resolve_error") {
|
|
6892
|
+
this.finishNamedTurnBinding(envelope);
|
|
6893
|
+
await this.sendNamedSessionError(envelope, result.error);
|
|
6894
|
+
return;
|
|
6895
|
+
}
|
|
6896
|
+
if (result.status === "resolved" && !result.sessionId && !this.namedTurnBindings.has(envelope)) {
|
|
6897
|
+
this.namedTurnBindings.set(envelope, {
|
|
6898
|
+
sessionId: null,
|
|
6899
|
+
generation: 0,
|
|
6900
|
+
claimed: false,
|
|
6901
|
+
released: true
|
|
6902
|
+
});
|
|
6903
|
+
}
|
|
6904
|
+
} else if (await prepare() === false) {
|
|
6905
|
+
return;
|
|
6906
|
+
}
|
|
6907
|
+
try {
|
|
6908
|
+
await this.handleInbound(envelope);
|
|
6909
|
+
} finally {
|
|
6910
|
+
this.finishNamedTurnBinding(envelope);
|
|
6911
|
+
}
|
|
6912
|
+
}
|
|
6913
|
+
async processPreflightedInbound(envelope, process5 = () => this.processInbound(envelope)) {
|
|
6914
|
+
if (this.namedSessions && !await this.prepareNamedTurnBinding(envelope)) {
|
|
6915
|
+
return;
|
|
6916
|
+
}
|
|
6917
|
+
try {
|
|
6918
|
+
await process5();
|
|
6919
|
+
} finally {
|
|
6920
|
+
this.finishNamedTurnBinding(envelope);
|
|
6921
|
+
}
|
|
5785
6922
|
}
|
|
5786
6923
|
async recordObservedContact(envelope) {
|
|
5787
6924
|
if (!this.observedContacts)
|
|
@@ -5906,7 +7043,45 @@ ${promptText}`;
|
|
|
5906
7043
|
}
|
|
5907
7044
|
}
|
|
5908
7045
|
await this.waitForBridgeRecovery();
|
|
5909
|
-
|
|
7046
|
+
let sessionId;
|
|
7047
|
+
let namedTurn = this.namedTurnBindings.get(envelope);
|
|
7048
|
+
if (this.namedSessions && namedTurn) {
|
|
7049
|
+
if (namedTurn.sessionId === null) {
|
|
7050
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No task was selected when this message was received. Select a task and send it again.");
|
|
7051
|
+
return;
|
|
7052
|
+
}
|
|
7053
|
+
if (this.dropQueuedTurnIfStale(namedTurn.sessionId, namedTurn.generation, envelope)) {
|
|
7054
|
+
return;
|
|
7055
|
+
}
|
|
7056
|
+
try {
|
|
7057
|
+
sessionId = await this.namedSessions.resolve(this.namedSessionOwner(envelope), namedTurn.sessionId);
|
|
7058
|
+
} catch (error) {
|
|
7059
|
+
await this.sendNamedSessionError(envelope, error);
|
|
7060
|
+
return;
|
|
7061
|
+
}
|
|
7062
|
+
if (!sessionId) {
|
|
7063
|
+
process.stderr.write(`[${this.name}] dropped collected turn from ${envelope.senderId} for session ${namedTurn.sessionId}: selected task changed before it ran
|
|
7064
|
+
`);
|
|
7065
|
+
return;
|
|
7066
|
+
}
|
|
7067
|
+
} else if (this.namedSessions) {
|
|
7068
|
+
try {
|
|
7069
|
+
sessionId = await this.namedSessions.resolve(this.namedSessionOwner(envelope), void 0, (resolvedSessionId) => {
|
|
7070
|
+
const binding = this.bindNamedTurn(envelope, resolvedSessionId);
|
|
7071
|
+
namedTurn = binding;
|
|
7072
|
+
return () => this.releaseNamedTurnBinding(binding);
|
|
7073
|
+
});
|
|
7074
|
+
} catch (error) {
|
|
7075
|
+
await this.sendNamedSessionError(envelope, error);
|
|
7076
|
+
return;
|
|
7077
|
+
}
|
|
7078
|
+
if (!sessionId) {
|
|
7079
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No task is currently selected. Use /session new <name> or /session use <name>.");
|
|
7080
|
+
return;
|
|
7081
|
+
}
|
|
7082
|
+
} else {
|
|
7083
|
+
sessionId = await this.router.resolve(this.name, envelope.senderId, envelope.chatId, envelope.threadId, this.config.cwd, envelope.isGroup);
|
|
7084
|
+
}
|
|
5910
7085
|
if (bangText.startsWith("!")) {
|
|
5911
7086
|
const cmd = bangText.slice(1).trim();
|
|
5912
7087
|
const bridgeShellCommand = this.bridge.shellCommand;
|
|
@@ -5951,12 +7126,19 @@ ${promptText}`;
|
|
|
5951
7126
|
}
|
|
5952
7127
|
let imageBase64 = envelope.imageBase64;
|
|
5953
7128
|
let imageMimeType = envelope.imageMimeType;
|
|
7129
|
+
const images = [];
|
|
7130
|
+
if (imageBase64 && imageMimeType) {
|
|
7131
|
+
images.push({ data: imageBase64, mimeType: imageMimeType });
|
|
7132
|
+
}
|
|
5954
7133
|
if (envelope.attachments?.length) {
|
|
5955
7134
|
const filePaths = [];
|
|
5956
7135
|
for (const att of envelope.attachments) {
|
|
5957
|
-
if (att.type === "image" && att.data
|
|
5958
|
-
|
|
5959
|
-
|
|
7136
|
+
if (att.type === "image" && att.data) {
|
|
7137
|
+
images.push({ data: att.data, mimeType: att.mimeType });
|
|
7138
|
+
if (!imageBase64) {
|
|
7139
|
+
imageBase64 = att.data;
|
|
7140
|
+
imageMimeType = att.mimeType;
|
|
7141
|
+
}
|
|
5960
7142
|
} else if (att.filePath) {
|
|
5961
7143
|
const label = att.type === "file" ? "file" : att.type;
|
|
5962
7144
|
const name = att.fileName ? ` "${sanitizeQuotedText(att.fileName, 128)}"` : "";
|
|
@@ -6042,8 +7224,13 @@ ${promptText}`;
|
|
|
6042
7224
|
this.instructedSessions.add(sessionId);
|
|
6043
7225
|
}
|
|
6044
7226
|
const prev = this.sessionQueues.get(sessionId) ?? Promise.resolve();
|
|
6045
|
-
const generation = this.sessionGenerations.get(sessionId) ?? 0;
|
|
7227
|
+
const generation = namedTurn?.generation ?? this.sessionGenerations.get(sessionId) ?? 0;
|
|
6046
7228
|
const useBlockStreaming = this.config.blockStreaming === "on";
|
|
7229
|
+
if (namedTurn) {
|
|
7230
|
+
namedTurn.claimed = true;
|
|
7231
|
+
} else {
|
|
7232
|
+
this.reserveQueuedTurn(sessionId);
|
|
7233
|
+
}
|
|
6047
7234
|
const current = prev.then(async () => {
|
|
6048
7235
|
clearTimeout(steerWatchdog);
|
|
6049
7236
|
if (this.dropQueuedTurnIfStale(sessionId, generation, envelope)) {
|
|
@@ -6110,7 +7297,7 @@ ${promptToSend}`;
|
|
|
6110
7297
|
doneResolve = r;
|
|
6111
7298
|
});
|
|
6112
7299
|
const promptState = {
|
|
6113
|
-
runId:
|
|
7300
|
+
runId: randomUUID3(),
|
|
6114
7301
|
owner: {
|
|
6115
7302
|
kind: "channel_user",
|
|
6116
7303
|
id: envelope.senderId
|
|
@@ -6184,6 +7371,7 @@ ${promptToSend}`;
|
|
|
6184
7371
|
promptBridge.on("responseBoundary", onResponseBoundary);
|
|
6185
7372
|
try {
|
|
6186
7373
|
const response = await promptBridge.prompt(sessionId, promptToSend, {
|
|
7374
|
+
...images.length > 0 ? { images } : {},
|
|
6187
7375
|
imageBase64,
|
|
6188
7376
|
imageMimeType,
|
|
6189
7377
|
displayText
|
|
@@ -6246,7 +7434,10 @@ ${promptToSend}`;
|
|
|
6246
7434
|
} finally {
|
|
6247
7435
|
promptBridge.off("textChunk", onChunk);
|
|
6248
7436
|
promptBridge.off("responseBoundary", onResponseBoundary);
|
|
6249
|
-
streamer
|
|
7437
|
+
if (streamer) {
|
|
7438
|
+
streamer.stop();
|
|
7439
|
+
await streamer.drain();
|
|
7440
|
+
}
|
|
6250
7441
|
const stillCurrent = this.activePrompts.get(sessionId) === promptState;
|
|
6251
7442
|
if (!promptState.clearEvicted) {
|
|
6252
7443
|
try {
|
|
@@ -6260,39 +7451,15 @@ ${promptToSend}`;
|
|
|
6260
7451
|
this.activePrompts.delete(sessionId);
|
|
6261
7452
|
}
|
|
6262
7453
|
promptState.resolve();
|
|
6263
|
-
|
|
6264
|
-
if (stillCurrent && buffer && buffer.length > 0) {
|
|
6265
|
-
this.collectBuffers.delete(sessionId);
|
|
6266
|
-
const lost = buffer.length;
|
|
6267
|
-
const coalesced = buffer.map((b) => b.text).join("\n\n");
|
|
6268
|
-
const coalescedDisplayText = buffer.map((b) => b.displayText).join("\n\n");
|
|
6269
|
-
const lastEnvelope = buffer[buffer.length - 1].envelope;
|
|
6270
|
-
this.notifyPromptBufferDrained(lastEnvelope.chatId, sessionId, buffer);
|
|
6271
|
-
const syntheticEnvelope = {
|
|
6272
|
-
...lastEnvelope,
|
|
6273
|
-
text: coalesced,
|
|
6274
|
-
displayText: coalescedDisplayText,
|
|
6275
|
-
// Coalesced text already carries each message's [sender] prefix.
|
|
6276
|
-
alreadyPrefixed: true,
|
|
6277
|
-
// Clear attachments/references — already resolved in original text
|
|
6278
|
-
referencedText: void 0,
|
|
6279
|
-
mentionedMemberIds: void 0,
|
|
6280
|
-
attachments: void 0,
|
|
6281
|
-
metadata: void 0,
|
|
6282
|
-
imageBase64: void 0,
|
|
6283
|
-
imageMimeType: void 0
|
|
6284
|
-
};
|
|
6285
|
-
this.markPreflighted(syntheticEnvelope);
|
|
6286
|
-
this.processInbound(syntheticEnvelope).catch((err) => {
|
|
6287
|
-
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)}
|
|
6288
|
-
`);
|
|
6289
|
-
});
|
|
6290
|
-
}
|
|
7454
|
+
this.drainCollectBufferForCurrentPrompt(sessionId, stillCurrent, "prompt completion");
|
|
6291
7455
|
}
|
|
6292
7456
|
});
|
|
6293
|
-
|
|
7457
|
+
const tracked = current.finally(() => {
|
|
7458
|
+
this.releaseQueuedTurn(sessionId);
|
|
7459
|
+
});
|
|
7460
|
+
this.sessionQueues.set(sessionId, tracked.catch(() => {
|
|
6294
7461
|
}));
|
|
6295
|
-
await
|
|
7462
|
+
await tracked;
|
|
6296
7463
|
}
|
|
6297
7464
|
pairingRejectionMessage(rejected) {
|
|
6298
7465
|
return rejected === "sender_pending" ? "You already have a pending pairing request. It must be approved or expire before another can be created." : "Too many pending pairing requests. Please try again later.";
|
|
@@ -6402,10 +7569,10 @@ var PollingChannelBase = class extends ChannelBase {
|
|
|
6402
7569
|
}
|
|
6403
7570
|
saveCursor() {
|
|
6404
7571
|
const path4 = this.cursorPath();
|
|
6405
|
-
|
|
7572
|
+
mkdirSync5(join6(getGlobalQwenDir(), "channels"), { recursive: true });
|
|
6406
7573
|
const tmp = `${path4}.tmp`;
|
|
6407
|
-
|
|
6408
|
-
|
|
7574
|
+
writeFileSync5(tmp, JSON.stringify(this.cursor) + "\n", "utf-8");
|
|
7575
|
+
renameSync5(tmp, path4);
|
|
6409
7576
|
}
|
|
6410
7577
|
startPollLoop() {
|
|
6411
7578
|
if (this.running)
|
|
@@ -6429,7 +7596,7 @@ var PollingChannelBase = class extends ChannelBase {
|
|
|
6429
7596
|
} catch (err) {
|
|
6430
7597
|
this.consecutiveErrors++;
|
|
6431
7598
|
const backoff = Math.min(INITIAL_BACKOFF * 2 ** (this.consecutiveErrors - 1), MAX_BACKOFF);
|
|
6432
|
-
|
|
7599
|
+
process4.stderr.write(`[Channel:${this.name}] poll error (attempt ${this.consecutiveErrors}), backing off ${backoff}ms: ${err}
|
|
6433
7600
|
`);
|
|
6434
7601
|
await this.abortableSleep(backoff);
|
|
6435
7602
|
continue;
|
|
@@ -6439,7 +7606,7 @@ var PollingChannelBase = class extends ChannelBase {
|
|
|
6439
7606
|
}
|
|
6440
7607
|
loadCursorFromDisk() {
|
|
6441
7608
|
try {
|
|
6442
|
-
const raw =
|
|
7609
|
+
const raw = readFileSync5(this.cursorPath(), "utf-8").trim();
|
|
6443
7610
|
if (!raw)
|
|
6444
7611
|
return null;
|
|
6445
7612
|
return this.validateCursor(JSON.parse(raw));
|
|
@@ -6473,6 +7640,48 @@ function getTextContent(content) {
|
|
|
6473
7640
|
return getString(content["text"]);
|
|
6474
7641
|
}
|
|
6475
7642
|
__name(getTextContent, "getTextContent");
|
|
7643
|
+
var CHANNEL_IMAGE_EXTENSIONS = ["bmp", "gif", "jpeg", "png", "webp"];
|
|
7644
|
+
var CHANNEL_IMAGE_MAX_UPLOAD_BYTES = 8 * 1024 * 1024;
|
|
7645
|
+
var CHANNEL_IMAGE_INLINE_MAX_BASE64_BYTES = 8 * 1024 * 1024;
|
|
7646
|
+
function channelImageName(mimeType, index = 0) {
|
|
7647
|
+
if (!mimeType.startsWith("image/")) {
|
|
7648
|
+
return void 0;
|
|
7649
|
+
}
|
|
7650
|
+
const extension = mimeType.slice("image/".length);
|
|
7651
|
+
if (!CHANNEL_IMAGE_EXTENSIONS.includes(extension)) {
|
|
7652
|
+
return void 0;
|
|
7653
|
+
}
|
|
7654
|
+
return index === 0 ? `image.${extension}` : `image-${index + 1}.${extension}`;
|
|
7655
|
+
}
|
|
7656
|
+
__name(channelImageName, "channelImageName");
|
|
7657
|
+
function decodeChannelImage(data, oversizedReason) {
|
|
7658
|
+
let estimatedBytes = Math.floor(data.length * 3 / 4);
|
|
7659
|
+
if (data.length % 4 === 0) {
|
|
7660
|
+
if (data.endsWith("=="))
|
|
7661
|
+
estimatedBytes -= 2;
|
|
7662
|
+
else if (data.endsWith("="))
|
|
7663
|
+
estimatedBytes -= 1;
|
|
7664
|
+
}
|
|
7665
|
+
if (estimatedBytes > CHANNEL_IMAGE_MAX_UPLOAD_BYTES) {
|
|
7666
|
+
return { skip: oversizedReason };
|
|
7667
|
+
}
|
|
7668
|
+
const bytes = Buffer.from(data, "base64");
|
|
7669
|
+
if (bytes.byteLength === 0) {
|
|
7670
|
+
return { skip: "empty once base64-decoded" };
|
|
7671
|
+
}
|
|
7672
|
+
return { bytes };
|
|
7673
|
+
}
|
|
7674
|
+
__name(decodeChannelImage, "decodeChannelImage");
|
|
7675
|
+
function isDefinitePromptAdmissionRejection(error) {
|
|
7676
|
+
if (!isRecord3(error)) {
|
|
7677
|
+
return false;
|
|
7678
|
+
}
|
|
7679
|
+
if (error["name"] === "DaemonPendingPromptLimitError") {
|
|
7680
|
+
return true;
|
|
7681
|
+
}
|
|
7682
|
+
return error["name"] === "DaemonHttpError" && typeof error["status"] === "number" && error["_daemonTurnError"] !== true;
|
|
7683
|
+
}
|
|
7684
|
+
__name(isDefinitePromptAdmissionRejection, "isDefinitePromptAdmissionRejection");
|
|
6476
7685
|
function getSessionUpdate(data) {
|
|
6477
7686
|
if (!isRecord3(data) || !isRecord3(data["update"])) {
|
|
6478
7687
|
return void 0;
|
|
@@ -6544,8 +7753,9 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6544
7753
|
availableCommandsBySession = /* @__PURE__ */ new Map();
|
|
6545
7754
|
turnBarriers = /* @__PURE__ */ new Map();
|
|
6546
7755
|
channelLoopToolHandlers = [];
|
|
7756
|
+
channelLoopDisabledSessions = /* @__PURE__ */ new Set();
|
|
6547
7757
|
registeredChannelLoopMcpSessions = /* @__PURE__ */ new Set();
|
|
6548
|
-
|
|
7758
|
+
channelLoopMcpOperations = /* @__PURE__ */ new Map();
|
|
6549
7759
|
channelLoopMcpServer;
|
|
6550
7760
|
connected = false;
|
|
6551
7761
|
lifecycleGeneration = 0;
|
|
@@ -6605,7 +7815,12 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6605
7815
|
await this.rejectStaleSession(session);
|
|
6606
7816
|
}
|
|
6607
7817
|
this.attachSession(session, bindingToken);
|
|
6608
|
-
|
|
7818
|
+
if (options?.enableChannelLoops === false) {
|
|
7819
|
+
this.channelLoopDisabledSessions.add(session.sessionId);
|
|
7820
|
+
void this.reconcileChannelLoopMcpForSession(session.sessionId);
|
|
7821
|
+
} else {
|
|
7822
|
+
await this.reconcileChannelLoopMcpForSession(session.sessionId);
|
|
7823
|
+
}
|
|
6609
7824
|
return session.sessionId;
|
|
6610
7825
|
}
|
|
6611
7826
|
async loadSession(sessionId, cwd, options, bindingToken) {
|
|
@@ -6627,7 +7842,12 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6627
7842
|
throw new Error(`Daemon returned session ${session.sessionId} while loading ${sessionId}`);
|
|
6628
7843
|
}
|
|
6629
7844
|
this.attachSession(session, bindingToken);
|
|
6630
|
-
|
|
7845
|
+
if (options?.enableChannelLoops === false) {
|
|
7846
|
+
this.channelLoopDisabledSessions.add(session.sessionId);
|
|
7847
|
+
void this.reconcileChannelLoopMcpForSession(session.sessionId);
|
|
7848
|
+
} else {
|
|
7849
|
+
await this.reconcileChannelLoopMcpForSession(session.sessionId);
|
|
7850
|
+
}
|
|
6631
7851
|
return session.sessionId;
|
|
6632
7852
|
}
|
|
6633
7853
|
registerChannelLoopToolHandler(handler) {
|
|
@@ -6640,7 +7860,9 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6640
7860
|
cancel: /* @__PURE__ */ __name((sessionId, id) => this.resolveChannelLoopToolHandler(sessionId).cancel(sessionId, id), "cancel")
|
|
6641
7861
|
});
|
|
6642
7862
|
for (const sessionId of this.sessions.keys()) {
|
|
6643
|
-
|
|
7863
|
+
if (!this.channelLoopDisabledSessions.has(sessionId)) {
|
|
7864
|
+
void this.reconcileChannelLoopMcpForSession(sessionId);
|
|
7865
|
+
}
|
|
6644
7866
|
}
|
|
6645
7867
|
}
|
|
6646
7868
|
async prompt(sessionId, text, options) {
|
|
@@ -6684,29 +7906,101 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6684
7906
|
this.on("responseBoundary", clearChunks);
|
|
6685
7907
|
this.on("sessionDied", onSessionDied);
|
|
6686
7908
|
const turnBarrier = this.createTurnBarrier(sessionId);
|
|
6687
|
-
const
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
data: options.imageBase64,
|
|
6692
|
-
mimeType: options.imageMimeType
|
|
6693
|
-
});
|
|
6694
|
-
}
|
|
6695
|
-
prompt.push({ type: "text", text });
|
|
6696
|
-
const promptAuthorization = this.options.promptAuthorization;
|
|
7909
|
+
const uploadedAttachmentIds = [];
|
|
7910
|
+
let rollbackUploadedAttachments = false;
|
|
7911
|
+
const uploadAttachment = session.uploadAttachment?.bind(session);
|
|
7912
|
+
const removeAttachment = session.removeAttachment?.bind(session);
|
|
6697
7913
|
try {
|
|
6698
|
-
const
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
7914
|
+
const prompt = [];
|
|
7915
|
+
const images = resolvePromptImages(options);
|
|
7916
|
+
if (this.options.sessionAttachments && uploadAttachment && removeAttachment) {
|
|
7917
|
+
try {
|
|
7918
|
+
const uploads = await Promise.allSettled(images.map(async (image, index) => {
|
|
7919
|
+
const name = channelImageName(image.mimeType, index);
|
|
7920
|
+
if (!name) {
|
|
7921
|
+
process.stderr.write(`[DaemonChannelBridge] skipped channel image with unsupported MIME type ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
|
|
7922
|
+
`);
|
|
7923
|
+
return void 0;
|
|
7924
|
+
}
|
|
7925
|
+
const decoded = decodeChannelImage(image.data, "above the daemon attachment size limit");
|
|
7926
|
+
if ("skip" in decoded) {
|
|
7927
|
+
process.stderr.write(`[DaemonChannelBridge] skipped channel image ${decoded.skip} ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
|
|
7928
|
+
`);
|
|
7929
|
+
return void 0;
|
|
7930
|
+
}
|
|
7931
|
+
const attachment = await uploadAttachment(new Blob([decoded.bytes], {
|
|
7932
|
+
type: image.mimeType
|
|
7933
|
+
}), name, image.mimeType, controller.signal);
|
|
7934
|
+
const attachmentId = getString(attachment["attachmentId"]);
|
|
7935
|
+
if (attachmentId)
|
|
7936
|
+
uploadedAttachmentIds.push(attachmentId);
|
|
7937
|
+
return attachment;
|
|
7938
|
+
}));
|
|
7939
|
+
const failure = uploads.find((upload) => upload.status === "rejected");
|
|
7940
|
+
if (failure) {
|
|
7941
|
+
throw failure.reason;
|
|
7942
|
+
}
|
|
7943
|
+
for (const upload of uploads) {
|
|
7944
|
+
if (upload.status === "fulfilled" && upload.value) {
|
|
7945
|
+
prompt.push(upload.value);
|
|
7946
|
+
}
|
|
7947
|
+
}
|
|
7948
|
+
} catch (error) {
|
|
7949
|
+
rollbackUploadedAttachments = true;
|
|
7950
|
+
throw error;
|
|
7951
|
+
}
|
|
7952
|
+
} else {
|
|
7953
|
+
let inlineBase64Bytes = 0;
|
|
7954
|
+
for (const image of images) {
|
|
7955
|
+
const decoded = decodeChannelImage(image.data, "above the inline image budget");
|
|
7956
|
+
if ("skip" in decoded) {
|
|
7957
|
+
process.stderr.write(`[DaemonChannelBridge] skipped channel image ${decoded.skip} ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
|
|
7958
|
+
`);
|
|
7959
|
+
continue;
|
|
7960
|
+
}
|
|
7961
|
+
if (inlineBase64Bytes + image.data.length > CHANNEL_IMAGE_INLINE_MAX_BASE64_BYTES) {
|
|
7962
|
+
process.stderr.write(`[DaemonChannelBridge] skipped channel image to keep the inline prompt under the daemon body limit ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
|
|
7963
|
+
`);
|
|
7964
|
+
continue;
|
|
7965
|
+
}
|
|
7966
|
+
inlineBase64Bytes += image.data.length;
|
|
7967
|
+
prompt.push({
|
|
7968
|
+
type: "image",
|
|
7969
|
+
data: image.data,
|
|
7970
|
+
mimeType: image.mimeType
|
|
7971
|
+
});
|
|
7972
|
+
}
|
|
7973
|
+
}
|
|
7974
|
+
prompt.push({ type: "text", text });
|
|
7975
|
+
if (controller.signal.aborted) {
|
|
7976
|
+
rollbackUploadedAttachments = true;
|
|
7977
|
+
controller.signal.throwIfAborted();
|
|
7978
|
+
}
|
|
7979
|
+
const promptAuthorization = this.options.promptAuthorization;
|
|
7980
|
+
if (controller.signal.aborted) {
|
|
7981
|
+
rollbackUploadedAttachments = true;
|
|
7982
|
+
throw controller.signal.reason;
|
|
7983
|
+
}
|
|
7984
|
+
let result;
|
|
7985
|
+
try {
|
|
7986
|
+
result = await session.prompt({
|
|
7987
|
+
prompt,
|
|
7988
|
+
_meta: {
|
|
7989
|
+
[CHANNEL_PROMPT_META_KEY]: true,
|
|
7990
|
+
...promptAuthorization ? {
|
|
7991
|
+
[CHANNEL_PROMPT_AUTHORIZATION_META_KEY]: promptAuthorization
|
|
7992
|
+
} : {},
|
|
7993
|
+
...options?.displayText !== void 0 ? {
|
|
7994
|
+
[CHANNEL_PROMPT_DISPLAY_TEXT_META_KEY]: options.displayText
|
|
7995
|
+
} : {}
|
|
7996
|
+
}
|
|
7997
|
+
}, controller.signal);
|
|
7998
|
+
} catch (error) {
|
|
7999
|
+
if (isDefinitePromptAdmissionRejection(error)) {
|
|
8000
|
+
rollbackUploadedAttachments = true;
|
|
6708
8001
|
}
|
|
6709
|
-
|
|
8002
|
+
throw error;
|
|
8003
|
+
}
|
|
6710
8004
|
await Promise.race([
|
|
6711
8005
|
turnBarrier,
|
|
6712
8006
|
new Promise((resolve3) => setTimeout(resolve3, 0))
|
|
@@ -6729,6 +8023,16 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6729
8023
|
if (controllers.size === 0 && this.activePromptControllers.get(sessionId) === controllers) {
|
|
6730
8024
|
this.activePromptControllers.delete(sessionId);
|
|
6731
8025
|
}
|
|
8026
|
+
if (rollbackUploadedAttachments && removeAttachment) {
|
|
8027
|
+
const removals = await Promise.allSettled(uploadedAttachmentIds.map((attachmentId) => removeAttachment(attachmentId)));
|
|
8028
|
+
removals.forEach((removal, index) => {
|
|
8029
|
+
if (removal.status === "rejected") {
|
|
8030
|
+
const reason = removal.reason instanceof Error ? removal.reason.message : String(removal.reason);
|
|
8031
|
+
process.stderr.write(`[DaemonChannelBridge] failed to remove channel image ${sanitizeLogText(uploadedAttachmentIds[index] ?? "", 128)} for session ${sanitizeLogText(sessionId, 128)} during rollback: ${sanitizeLogText(reason, 256)}
|
|
8032
|
+
`);
|
|
8033
|
+
}
|
|
8034
|
+
});
|
|
8035
|
+
}
|
|
6732
8036
|
}
|
|
6733
8037
|
}
|
|
6734
8038
|
async shellCommand(sessionId, command, signal) {
|
|
@@ -6922,6 +8226,8 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6922
8226
|
if (meta?.["qwenDiscreteMessage"] === true) {
|
|
6923
8227
|
if (meta["source"] === "background_notification_response" && meta["rewritten"] !== true && text) {
|
|
6924
8228
|
this.emit("backgroundResponse", sessionId, text);
|
|
8229
|
+
} else if (meta["source"] === "vision_bridge_notice" && text) {
|
|
8230
|
+
this.emit("textChunk", sessionId, text);
|
|
6925
8231
|
}
|
|
6926
8232
|
break;
|
|
6927
8233
|
}
|
|
@@ -7084,6 +8390,7 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
7084
8390
|
this.eventControllers.delete(sessionId);
|
|
7085
8391
|
this.sessions.delete(sessionId);
|
|
7086
8392
|
this.sessionBindingTokens.delete(sessionId);
|
|
8393
|
+
this.channelLoopDisabledSessions.delete(sessionId);
|
|
7087
8394
|
this.abortActivePrompts(sessionId);
|
|
7088
8395
|
this.activePrompts.delete(sessionId);
|
|
7089
8396
|
this.availableCommandsBySession.delete(sessionId);
|
|
@@ -7101,45 +8408,45 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
7101
8408
|
}
|
|
7102
8409
|
}
|
|
7103
8410
|
if (unregisterChannelLoopMcp) {
|
|
7104
|
-
this.
|
|
8411
|
+
void this.reconcileChannelLoopMcpForSession(sessionId);
|
|
7105
8412
|
}
|
|
7106
8413
|
return session;
|
|
7107
8414
|
}
|
|
7108
|
-
|
|
7109
|
-
const
|
|
7110
|
-
const
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
|
|
8415
|
+
reconcileChannelLoopMcpForSession(sessionId) {
|
|
8416
|
+
const previous = this.channelLoopMcpOperations.get(sessionId) ?? Promise.resolve();
|
|
8417
|
+
const operation = previous.catch(() => void 0).then(async () => {
|
|
8418
|
+
const host = this.options.channelLoopMcpHost;
|
|
8419
|
+
const server = this.channelLoopMcpServer;
|
|
8420
|
+
const shouldRegister = host !== void 0 && server !== void 0 && this.sessions.has(sessionId) && !this.channelLoopDisabledSessions.has(sessionId);
|
|
8421
|
+
if (!shouldRegister) {
|
|
8422
|
+
if (host && this.registeredChannelLoopMcpSessions.has(sessionId)) {
|
|
8423
|
+
await host.unregister(sessionId);
|
|
8424
|
+
this.registeredChannelLoopMcpSessions.delete(sessionId);
|
|
8425
|
+
}
|
|
8426
|
+
return;
|
|
8427
|
+
}
|
|
8428
|
+
if (this.registeredChannelLoopMcpSessions.has(sessionId))
|
|
8429
|
+
return;
|
|
8430
|
+
await host.register(sessionId, (message) => server.handleMessage(message, { sessionId }));
|
|
8431
|
+
this.registeredChannelLoopMcpSessions.add(sessionId);
|
|
8432
|
+
if (!this.sessions.has(sessionId) || this.channelLoopDisabledSessions.has(sessionId)) {
|
|
7123
8433
|
await host.unregister(sessionId);
|
|
8434
|
+
this.registeredChannelLoopMcpSessions.delete(sessionId);
|
|
7124
8435
|
}
|
|
7125
8436
|
}).catch((error) => {
|
|
7126
8437
|
this.lastError = error;
|
|
7127
8438
|
}).finally(() => {
|
|
7128
|
-
if (this.
|
|
7129
|
-
this.
|
|
8439
|
+
if (this.channelLoopMcpOperations.get(sessionId) === operation) {
|
|
8440
|
+
this.channelLoopMcpOperations.delete(sessionId);
|
|
7130
8441
|
}
|
|
7131
8442
|
});
|
|
7132
|
-
this.
|
|
7133
|
-
|
|
7134
|
-
}
|
|
7135
|
-
unregisterChannelLoopMcpForSession(sessionId) {
|
|
7136
|
-
if (!this.registeredChannelLoopMcpSessions.delete(sessionId))
|
|
7137
|
-
return;
|
|
7138
|
-
void this.options.channelLoopMcpHost?.unregister(sessionId).catch((error) => {
|
|
7139
|
-
this.lastError = error;
|
|
7140
|
-
});
|
|
8443
|
+
this.channelLoopMcpOperations.set(sessionId, operation);
|
|
8444
|
+
return operation;
|
|
7141
8445
|
}
|
|
7142
8446
|
resolveChannelLoopToolHandler(sessionId) {
|
|
8447
|
+
if (!this.sessions.has(sessionId) || this.channelLoopDisabledSessions.has(sessionId)) {
|
|
8448
|
+
throw new Error("Channel loop tools are unavailable for this session");
|
|
8449
|
+
}
|
|
7143
8450
|
const handler = this.channelLoopToolHandlers.find((candidate) => candidate.canHandle?.(sessionId) === true || this.channelLoopToolHandlers.length === 1 && !candidate.canHandle);
|
|
7144
8451
|
if (handler)
|
|
7145
8452
|
return handler;
|
|
@@ -7370,6 +8677,7 @@ export {
|
|
|
7370
8677
|
isChannelProactiveDeliveryError,
|
|
7371
8678
|
PairingStore,
|
|
7372
8679
|
truncateCodePoints,
|
|
8680
|
+
truncateUtf16Units,
|
|
7373
8681
|
sanitizeSenderName,
|
|
7374
8682
|
sanitizePromptText,
|
|
7375
8683
|
sanitizeDisplayText,
|