@qwen-code/qwen-code 0.22.2 → 0.22.3-nightly.20260831.3a0c4c6108
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 +3 -3
- package/bundled/qc-helper/docs/common-workflow.md +32 -0
- package/bundled/qc-helper/docs/configuration/auth.md +6 -6
- package/bundled/qc-helper/docs/configuration/model-providers.md +4 -2
- package/bundled/qc-helper/docs/configuration/settings.md +47 -44
- package/bundled/qc-helper/docs/extension/introduction.md +1 -1
- package/bundled/qc-helper/docs/features/approval-mode.md +3 -0
- package/bundled/qc-helper/docs/features/auto-mode.md +34 -17
- package/bundled/qc-helper/docs/features/channels/dws.md +2 -0
- package/bundled/qc-helper/docs/features/channels/overview.md +36 -3
- package/bundled/qc-helper/docs/features/code-review.md +5 -5
- package/bundled/qc-helper/docs/features/commands.md +41 -0
- package/bundled/qc-helper/docs/features/computer-use.md +2 -2
- package/bundled/qc-helper/docs/features/hooks.md +6 -2
- package/bundled/qc-helper/docs/features/memory.md +1 -0
- package/bundled/qc-helper/docs/features/tool-use-summaries.md +1 -1
- package/bundled/qc-helper/docs/qwen-serve-deploy-local.md +17 -7
- package/bundled/qc-helper/docs/qwen-serve.md +59 -55
- package/bundled/review/SKILL.md +90 -31
- package/bundled/review/references/persistence.md +7 -3
- package/bundled/review/references/posting.md +11 -5
- package/chunks/MaxSizedBox-IHRTWGSY.js +115 -0
- package/chunks/{StandaloneSessionPicker-AVLI6VJP.js → StandaloneSessionPicker-TLVWVTJ7.js} +88 -85
- package/chunks/{acp-startup-profiler-GNYAL6LI.js → acp-startup-profiler-M4X5F3VK.js} +2 -2
- package/chunks/{acpAgent-MXEAN54C.js → acpAgent-7I5LLPP3.js} +1563 -407
- package/chunks/agent-4Z4NTJXD.js +99 -0
- package/chunks/agent-headless-TVUJXQWU.js +89 -0
- package/chunks/{anthropicContentGenerator-HGMBACVE.js → anthropicContentGenerator-VSSPG7XV.js} +84 -55
- package/chunks/{artifact-tool-5YT4QF7Y.js → artifact-tool-TEYW6I4A.js} +11 -3
- package/chunks/{askUserQuestion-4SWJY65V.js → askUserQuestion-PYNBWV63.js} +45 -7
- package/chunks/bridge-EVURP4NO.js +122 -0
- package/chunks/{ca-7HPYOYSZ.js → ca-BNU7YNDE.js} +3 -0
- package/chunks/{channel-management-service-X4AZ6IXX.js → channel-management-service-IDBNYSFW.js} +6 -6
- package/chunks/channel-settings-store-MZNXRKVI.js +125 -0
- package/chunks/{channel-worker-group-27HMGD5S.js → channel-worker-group-ZLP5SJHT.js} +7 -7
- package/chunks/{channel-worker-manager-QEGX646Z.js → channel-worker-manager-FG5377T2.js} +7 -7
- package/chunks/{channel-worker-supervisor-UEW2WWCC.js → channel-worker-supervisor-HDVE4ADS.js} +4 -4
- package/chunks/{chunk-PPKZ7JOE.js → chunk-22UH7VTP.js} +1 -1
- package/chunks/chunk-24FVUD5P.js +49 -0
- package/chunks/{chunk-CMHFCLBU.js → chunk-26SIKQIC.js} +1 -1
- package/chunks/{chunk-5PA6UEYA.js → chunk-3452WXOK.js} +4 -1
- package/chunks/{chunk-SFPGAQUL.js → chunk-373DQUVA.js} +72 -31
- package/chunks/{chunk-H2WDGQ6D.js → chunk-3AKCBZVW.js} +3 -3
- package/chunks/{chunk-LXIBMVRN.js → chunk-3CZGSTZX.js} +15 -9
- package/chunks/{chunk-JVPFTDBK.js → chunk-3EGEBDTW.js} +1 -1
- package/chunks/{chunk-K2NQPH5D.js → chunk-3NQCZMUB.js} +79 -5
- package/chunks/{chunk-E7REAOTN.js → chunk-47ECGEJC.js} +4 -0
- package/chunks/{chunk-WKK5BQNP.js → chunk-4P7PPAHN.js} +45 -421
- package/chunks/chunk-4VQBQLIZ.js +293 -0
- package/chunks/{chunk-4JWNNLPT.js → chunk-5FGWVPCM.js} +4 -4
- package/chunks/{chunk-ZFKOCECU.js → chunk-5ICKESC3.js} +2 -2
- package/chunks/{chunk-3I6UTTDX.js → chunk-67AY5D3M.js} +20 -20
- package/chunks/chunk-6HUXPLB5.js +110 -0
- package/chunks/{chunk-BRVWYMKV.js → chunk-6MBXY6WM.js} +1 -1
- package/chunks/{chunk-6LFTG244.js → chunk-6NWPR2EB.js} +19 -9
- package/chunks/chunk-6OO7QEWV.js +2581 -0
- package/chunks/{chunk-TWSKM647.js → chunk-6VHIWUJP.js} +1 -1
- package/chunks/{chunk-RJC5EIKY.js → chunk-6YXNJXQW.js} +7 -5
- package/chunks/{chunk-TPI7O7HR.js → chunk-7DDTI7Q3.js} +1 -1
- package/chunks/{chunk-KGJGEEVR.js → chunk-7FA2II6K.js} +10 -1
- package/chunks/chunk-7J6OTNGO.js +424 -0
- package/chunks/{chunk-NIFYWDYN.js → chunk-7TBU7PU2.js} +1 -1
- package/chunks/{chunk-3FDFA4MZ.js → chunk-7XICSE62.js} +3 -3
- package/chunks/{chunk-V2NDP3I4.js → chunk-7XL4WRXM.js} +2 -2
- package/chunks/{chunk-F7TNAPZ4.js → chunk-AAX236AR.js} +45 -11
- package/chunks/{chunk-T32W2JHI.js → chunk-ACZ4YRYK.js} +6 -6
- package/chunks/chunk-ADJD4LZF.js +210 -0
- package/chunks/{chunk-JWALNCLT.js → chunk-AF3QB6OL.js} +1 -1
- package/chunks/{chunk-P2SU6ZTI.js → chunk-AI35S34R.js} +3 -3
- package/chunks/{chunk-OIP7C4FE.js → chunk-AKNQMSL2.js} +5 -5
- package/chunks/{chunk-6CGL7E7G.js → chunk-AN3KFCNH.js} +2 -2
- package/chunks/{chunk-ABS25QER.js → chunk-ANVG3BVV.js} +1 -1
- package/chunks/chunk-AYYKUUKP.js +972 -0
- package/chunks/chunk-B2RIIK3K.js +346 -0
- package/chunks/{chunk-F5SZP77N.js → chunk-BBYTY46C.js} +153 -112
- package/chunks/{chunk-XRSOI3DL.js → chunk-BE5FGT5Z.js} +1 -1
- package/chunks/{chunk-BGXZBI5B.js → chunk-BHTYRHJF.js} +3 -2
- package/chunks/{chunk-CSMI4FP5.js → chunk-BSJOBW4U.js} +1 -1
- package/chunks/{chunk-Z7TGYN5F.js → chunk-BXNKREX7.js} +3 -3
- package/chunks/{chunk-4MQEPP3Y.js → chunk-BY24DZ4J.js} +5 -119
- package/chunks/{chunk-5XBFCMWD.js → chunk-CAZADEU7.js} +17 -9
- package/chunks/chunk-CHEIIBT3.js +40 -0
- package/chunks/{chunk-BBWV7ONL.js → chunk-CM4F4JBB.js} +6 -6
- package/chunks/{chunk-CAJTKR6W.js → chunk-D7D3HSYQ.js} +6 -5
- package/chunks/{chunk-3DXFGHAJ.js → chunk-DBTMP3P4.js} +213 -185
- package/chunks/chunk-DJBQCO2E.js +842 -0
- package/chunks/{chunk-RXFQM6FQ.js → chunk-DMEUXLSS.js} +16 -1
- package/chunks/{chunk-3AFMQUTI.js → chunk-E4JNFXNI.js} +3 -3
- package/chunks/{chunk-UHQFIS7N.js → chunk-EAHOJF6V.js} +28 -4
- package/chunks/{chunk-OIVXBW3W.js → chunk-ED7DD7MV.js} +3 -3
- package/chunks/{chunk-6DIGWMGT.js → chunk-EIJT2PQ6.js} +2 -2
- package/chunks/{chunk-XQFT3QUF.js → chunk-ENYUKMFK.js} +1 -1
- package/chunks/{chunk-CQH5KTKC.js → chunk-ERHAMGAA.js} +1 -1
- package/chunks/{chunk-IOPCFOTF.js → chunk-ERRJA23L.js} +3 -3
- package/chunks/{chunk-LDP5OD6N.js → chunk-EU2B4P3D.js} +12 -12
- package/chunks/{chunk-W5MJJ2V7.js → chunk-FHPYRXRH.js} +1 -1
- package/chunks/{chunk-IZIVM7LZ.js → chunk-FIWZOMBF.js} +3 -3
- package/chunks/{chunk-4HV6HH33.js → chunk-FPMQT2YY.js} +9 -9
- package/chunks/{chunk-Z7VOMGDC.js → chunk-FTJMWDND.js} +0 -24
- package/chunks/{chunk-L2M46FP5.js → chunk-FXUSWLBF.js} +225 -12
- package/chunks/{chunk-VVW4ZNFY.js → chunk-GB4UKWY5.js} +16 -8
- package/chunks/{chunk-PKAYJDB3.js → chunk-GCUR24KK.js} +22 -4
- package/chunks/{chunk-DIMEPKCW.js → chunk-GDXSXL3H.js} +11 -1
- package/chunks/{chunk-7JPW6IYH.js → chunk-GN422P4J.js} +1 -1
- package/chunks/{chunk-66KDY3LV.js → chunk-GS7XT345.js} +3 -3
- package/chunks/{chunk-PFCQ62V4.js → chunk-H2A2GJGL.js} +6 -6
- package/chunks/{chunk-Z7FLBQPR.js → chunk-H5GPPDGY.js} +8 -8
- package/chunks/{chunk-CPGJJJLU.js → chunk-HDMKCJHI.js} +1 -1
- package/chunks/{chunk-6PJOTWAN.js → chunk-HT5S5TRQ.js} +1 -1
- package/chunks/{chunk-Z4X5MTRT.js → chunk-HWVUSYSN.js} +5 -12
- package/chunks/{chunk-V4QXXQJ2.js → chunk-I75VKFCJ.js} +4 -4
- package/chunks/{chunk-NU7RKEZR.js → chunk-IBMUI5EV.js} +3 -3
- package/chunks/{chunk-4J657AJR.js → chunk-ID33ZRWH.js} +42 -42
- package/chunks/{chunk-R4PMVSR5.js → chunk-IIFL7QDN.js} +30 -192
- package/chunks/{chunk-MQM5PJML.js → chunk-IVUWTA2D.js} +1 -1
- package/chunks/{chunk-E55H6BZ7.js → chunk-IXEZHTDR.js} +1616 -151
- package/chunks/{chunk-XF63PKEN.js → chunk-IXHV5MIC.js} +1 -1
- package/chunks/{chunk-JHS74YAB.js → chunk-J4Y3QJHF.js} +3 -1
- package/chunks/{chunk-MIIVI25Q.js → chunk-JDIO45X7.js} +3 -3
- package/chunks/{chunk-QFCDF5PL.js → chunk-JHN7UIDW.js} +1 -1
- package/chunks/{chunk-FDY5RMGH.js → chunk-JLSZT354.js} +2 -2
- package/chunks/chunk-JMRE4WJQ.js +1849 -0
- package/chunks/{chunk-FY76G3G5.js → chunk-JOTYTYYW.js} +6 -6
- package/chunks/{chunk-TRZTNIQ6.js → chunk-JOWQD4LO.js} +3 -3
- package/chunks/{chunk-KEKZFOL6.js → chunk-JPKTAQS3.js} +1 -1
- package/chunks/chunk-JS73NNWF.js +30 -0
- package/chunks/chunk-JX6XHPTX.js +26 -0
- package/chunks/{chunk-5JTIYALA.js → chunk-JZWOCUZ3.js} +1 -1
- package/chunks/{chunk-3JGZSIDA.js → chunk-KFN2IZYR.js} +3 -3
- package/chunks/{chunk-23RFD54N.js → chunk-KHP66DFN.js} +1 -1
- package/chunks/{chunk-AGR5UROZ.js → chunk-KUDI54QQ.js} +23 -33
- package/chunks/{chunk-3SGB2UHX.js → chunk-L2OKAIUK.js} +28 -16
- package/chunks/{chunk-BXDAPVQU.js → chunk-L6CUJZDG.js} +28 -4
- package/chunks/chunk-LAUEEBDL.js +277 -0
- package/chunks/{chunk-BNMF3K5X.js → chunk-LFAR7CPW.js} +55 -39
- package/chunks/{chunk-Y3QL45LS.js → chunk-LKODFJRD.js} +9 -9
- package/chunks/{chunk-BWORX6FA.js → chunk-LMMHS3RS.js} +1 -1
- package/chunks/{chunk-UTLCH2FK.js → chunk-LYSZVCCD.js} +10 -7
- package/chunks/{chunk-F6FV3C5K.js → chunk-MBZ5ORJB.js} +264 -283
- package/chunks/{chunk-L6YT5H34.js → chunk-MJBHSZEJ.js} +5 -5
- package/chunks/{chunk-B7CDU2SL.js → chunk-MOF6VZN2.js} +18 -21
- package/chunks/{chunk-6LFJEACX.js → chunk-MZ5A5PQW.js} +7 -7
- package/chunks/chunk-MZYWNU66.js +149 -0
- package/chunks/{chunk-2TNL5KPM.js → chunk-N4QWUPDM.js} +7 -0
- package/chunks/{chunk-5O72BNDD.js → chunk-ND7TQQPE.js} +9 -9
- package/chunks/{chunk-QP4C6WCE.js → chunk-NGKLMNWN.js} +3 -3
- package/chunks/{chunk-JSC3H3TD.js → chunk-NT5XIL5H.js} +46 -18
- package/chunks/{chunk-KUX5D6DK.js → chunk-NT7LXVV5.js} +11010 -11138
- package/chunks/{chunk-PDQGMSZK.js → chunk-ODZF64DM.js} +1 -1
- package/chunks/{chunk-FBSSIAAQ.js → chunk-OE5AQVNB.js} +8 -8
- package/chunks/chunk-OHU2H5CS.js +130 -0
- package/chunks/{chunk-2K6ORIHJ.js → chunk-OKMDOATP.js} +13 -13
- package/chunks/{chunk-JB4JIVTJ.js → chunk-OKTNXDVO.js} +1 -1
- package/chunks/{chunk-XDLEZ5HX.js → chunk-OQYXJK2V.js} +1 -1
- package/chunks/{chunk-EFUM7RVY.js → chunk-OSTQZIOY.js} +944 -18
- package/chunks/{chunk-VQUGEVGR.js → chunk-OTTNXUT5.js} +1 -1
- package/chunks/{chunk-T3WXYEZ4.js → chunk-PB7JBAHF.js} +53 -54
- package/chunks/{chunk-WAOIHTJS.js → chunk-PDAKFU7M.js} +1 -1
- package/chunks/{chunk-43GGFFLY.js → chunk-PDXYZBBQ.js} +3 -3
- package/chunks/{chunk-4CMJKUND.js → chunk-PNIGAVY6.js} +67 -2
- package/chunks/{chunk-SAH4BD2J.js → chunk-PQEISIKS.js} +67 -0
- package/chunks/{chunk-4CDSK5AZ.js → chunk-PWR2NQBS.js} +6 -6
- package/chunks/{chunk-YTDVNSUQ.js → chunk-QAK5475D.js} +1 -1
- package/chunks/{chunk-IJKTMLBE.js → chunk-QE7DTCLX.js} +1 -1
- package/chunks/{chunk-SMPR7SPO.js → chunk-QJNDUTOL.js} +45 -239
- package/chunks/{chunk-C6K43ZDE.js → chunk-QTMPM6RC.js} +16 -10
- package/chunks/{chunk-P5GGM4KS.js → chunk-RAEEQEC5.js} +5 -5
- package/chunks/{chunk-XDFHY4R3.js → chunk-RGEV5AUD.js} +242 -14
- package/chunks/{chunk-M6FOJXW3.js → chunk-RKW36UWC.js} +1 -1
- package/chunks/{chunk-RIEFGNTP.js → chunk-RLSPPTWH.js} +5119 -683
- package/chunks/{chunk-PDMJ3KGS.js → chunk-RT4QNQ5P.js} +3 -3
- package/chunks/{chunk-SJ3ZUWFK.js → chunk-RVCGAHC7.js} +4 -26
- package/chunks/{chunk-77RG5SFQ.js → chunk-RY7DL5IF.js} +11 -6
- package/chunks/{chunk-4FTKQNWJ.js → chunk-S2T7H5MI.js} +182 -3
- package/chunks/{chunk-EOGELB3H.js → chunk-SDCHI64N.js} +4 -4
- package/chunks/{chunk-EKSCLBBF.js → chunk-SMJYTKTB.js} +23 -5
- package/chunks/{chunk-MZS7GEC7.js → chunk-SMRDCANI.js} +1 -1
- package/chunks/{chunk-43UYNAWX.js → chunk-T3TL6S3B.js} +4 -4
- package/chunks/{chunk-4K7NQDCG.js → chunk-T4EFG2P5.js} +18 -9
- package/chunks/{chunk-IK5BW3CS.js → chunk-T4RMUTTT.js} +16 -5
- package/chunks/{chunk-L6BZRIUL.js → chunk-TBJ3SNS3.js} +183 -5
- package/chunks/{chunk-CCBZDVUA.js → chunk-TNMOFNT4.js} +133 -122
- package/chunks/{chunk-Y6LSMB6J.js → chunk-TV7BJ7TX.js} +1 -1
- package/chunks/{chunk-G34IR3D6.js → chunk-U5RTYI7D.js} +102 -146
- package/chunks/{chunk-TTX2JUE6.js → chunk-U6D2PPMN.js} +1 -1
- package/chunks/{chunk-D5LUXLUH.js → chunk-UJ4DUYIH.js} +18 -4
- package/chunks/{chunk-A7YP63XN.js → chunk-UKB2TZAO.js} +5 -5
- package/chunks/{chunk-VTHREBQM.js → chunk-UKYXYWI2.js} +2 -2
- package/chunks/{chunk-2NYCN6F2.js → chunk-UVYKCRJZ.js} +46 -29
- package/chunks/{chunk-HVEYF6VT.js → chunk-UXEW7TQS.js} +2 -2
- package/chunks/{chunk-HVQ3B7FF.js → chunk-V4HN35IF.js} +44 -7
- package/chunks/chunk-VGHZT6JR.js +51 -0
- package/chunks/{chunk-V5J4J5TP.js → chunk-VYSZUQGU.js} +2 -20
- package/chunks/{chunk-6IUNAPLR.js → chunk-W4BDHLRT.js} +3 -3
- package/chunks/{chunk-Q26AYA62.js → chunk-W7KWB6AH.js} +7 -4
- package/chunks/chunk-WLEYH7CD.js +332 -0
- package/chunks/{chunk-RU7BK6B3.js → chunk-X7C7KFWA.js} +2 -2
- package/chunks/{chunk-YRLW2MSX.js → chunk-XBVNNDPK.js} +11 -4
- package/chunks/{chunk-BFPESSTR.js → chunk-XEAAO654.js} +1 -1
- package/chunks/{chunk-JU4EEXC7.js → chunk-XLENZQXH.js} +2 -2
- package/chunks/{chunk-JSHHYW6T.js → chunk-XPDKSOVW.js} +69 -64
- package/chunks/chunk-YDQ4OCDK.js +1222 -0
- package/chunks/{chunk-ZU4UDIWX.js → chunk-YKADANK6.js} +6 -7
- package/chunks/{chunk-JCQD3INC.js → chunk-YNG7XAJU.js} +3 -3
- package/chunks/{chunk-O2UN57NX.js → chunk-YOFULAHO.js} +19 -2
- package/chunks/{chunk-BAJKAAOY.js → chunk-YOMKOM7E.js} +4892 -1504
- package/chunks/{chunk-ERFDKH32.js → chunk-YPISTYEI.js} +8 -7
- package/chunks/{chunk-TGWAQ5RB.js → chunk-ZBDYI7VM.js} +6900 -6882
- package/chunks/{chunk-OZ6KS6KW.js → chunk-ZGCKTAL5.js} +6 -1
- package/chunks/chunk-ZHO2CW6D.js +205 -0
- package/chunks/{chunk-DZWPESIF.js → chunk-ZQ3FKEWA.js} +3 -3
- package/chunks/{chunk-7EQQR46F.js → chunk-ZVP4PWBU.js} +1 -1
- package/chunks/{chunk-7UYV53BC.js → chunk-ZYPJ7HVZ.js} +1 -1
- package/chunks/config-utils-NNUPVGEC.js +118 -0
- package/chunks/contextCommand-S3TPCDM5.js +111 -0
- package/chunks/{core-runtime-677KFX6Q.js → core-runtime-NASPCSQX.js} +66 -63
- package/chunks/{create-sub-session-23ITIELI.js → create-sub-session-36TNCHCP.js} +3 -3
- package/chunks/{create-sub-session-JKZAYZX7.js → create-sub-session-DS4BJLYZ.js} +97 -68
- package/chunks/{cron-create-KJ4IY6PP.js → cron-create-QXHTYUTO.js} +4 -4
- package/chunks/{cron-delete-MBLQCAYY.js → cron-delete-WV2BM42M.js} +4 -4
- package/chunks/{cron-list-DWH2PQ47.js → cron-list-7KVNBLZ7.js} +4 -4
- package/chunks/{daemon-MJQUO7RA.js → daemon-UCVUHPJD.js} +723 -90
- package/chunks/{daemon-git-worktree-guard-6MVAAZZZ.js → daemon-git-worktree-guard-ATXVQPS3.js} +63 -60
- package/chunks/daemon-status-provider-UI3GBTMO.js +121 -0
- package/chunks/daemon-trust-policy-SUCBJ2CA.js +117 -0
- package/chunks/{daemon-trust-policy-monitor-B5U6TQX5.js → daemon-trust-policy-monitor-3LM5RQNS.js} +71 -68
- package/chunks/{de-X6HLSIFE.js → de-T4BLOKEC.js} +3 -0
- package/chunks/deferred-core-runtime-SCOJLVYG.js +119 -0
- package/chunks/{display-image-BE7I6ZB6.js → display-image-Z4VBQDEJ.js} +5 -5
- package/chunks/{dist-TJUDJTCB.js → dist-5423MG3D.js} +411 -91
- package/chunks/{dist-ILDBUWDB.js → dist-7WDZYMWV.js} +43 -25
- package/chunks/{dist-ZBVOXSKF.js → dist-FLJLHASF.js} +219 -121
- package/chunks/{dist-QODVOVWI.js → dist-GPSRNVVP.js} +43 -35
- package/chunks/{dist-DJBNNPLN.js → dist-LY66XNUL.js} +10 -3
- package/chunks/{dist-7CKN54NL.js → dist-S3BKCTWF.js} +205 -89
- package/chunks/{dist-UQUFZDIY.js → dist-SDKHGH5O.js} +41 -20
- package/chunks/{dist-4Q7NTUNB.js → dist-WAUO5OMN.js} +46 -22
- package/chunks/{dist-SATKXKUR.js → dist-WIVVPE7M.js} +28 -14
- package/chunks/earlyInputCapture-INWJ2STR.js +112 -0
- package/chunks/{edit-BSV6YRVW.js → edit-B7ZRRGAE.js} +51 -49
- package/chunks/{en-YLXBXM4Z.js → en-E4AYTX6H.js} +3 -0
- package/chunks/{enter-worktree-MQXFFPKM.js → enter-worktree-SPZHEETQ.js} +7 -7
- package/chunks/{enterPlanMode-PKNF35FE.js → enterPlanMode-S554IKJ2.js} +55 -53
- package/chunks/environment-25ADH4Y6.js +134 -0
- package/chunks/errors-G2I6LFTW.js +118 -0
- package/chunks/{exit-worktree-L344UY7K.js → exit-worktree-AWUXAMAM.js} +7 -7
- package/chunks/exitPlanMode-ZFH4OYPM.js +87 -0
- package/chunks/{fast-path-773PV7PU.js → fast-path-WZHVZ2ER.js} +8 -7
- package/chunks/{fast-path-settings-XMFBYFUH.js → fast-path-settings-NILM6QZL.js} +3 -3
- package/chunks/{fr-FTZWF462.js → fr-BGHGYX6A.js} +3 -0
- package/chunks/{glob-CZHOWUEA.js → glob-UOVDMBUP.js} +50 -48
- package/chunks/{goal-tools-YZQJXTGH.js → goal-tools-7TNK5ZHC.js} +5 -4
- package/chunks/{grep-5GPICQBU.js → grep-LHYJ5V2N.js} +9 -7
- package/chunks/handleAutoUpdate-4MZ45DZY.js +114 -0
- package/chunks/i18n-KWUEI4DS.js +129 -0
- package/chunks/{image-gen-HAYOEQ4O.js → image-gen-YZDVO5YS.js} +8 -10
- package/chunks/initializer-2NBROGMG.js +118 -0
- package/chunks/installationInfo-YKUSY3ZB.js +112 -0
- package/chunks/{ja-2GL5FKYJ.js → ja-G3FTD5XE.js} +3 -0
- package/chunks/{keychain-token-storage-37XPR34V.js → keychain-token-storage-REZVMJA6.js} +2 -2
- package/chunks/list-W2WV3COM.js +121 -0
- package/chunks/list-agents-PMVMHNY7.js +128 -0
- package/chunks/{gemini-IP4FJAP5.js → llm-NPPHLIJI.js} +162 -133
- package/chunks/{geminiContentGenerator-PUNEBWJW.js → llm-content-generator-YK3WN6XW.js} +29 -29
- package/chunks/loadedSettingsAdapter-2P7DFWUR.js +115 -0
- package/chunks/{loggingContentGenerator-2GGZNGLG.js → loggingContentGenerator-QYYYGU6V.js} +318 -35
- package/chunks/{loop-wakeup-XJFONSQ7.js → loop-wakeup-EBM4F5MZ.js} +5 -5
- package/chunks/{ls-3VAT2J7A.js → ls-F6TRX3DD.js} +4 -4
- package/chunks/{lsp-5W7IRUBH.js → lsp-QYTOS2FI.js} +2 -2
- package/chunks/{managed-npm-update-5TEV2E3J.js → managed-npm-update-UMC4NBWW.js} +64 -61
- package/chunks/mcp-OFI6XDP2.js +115 -0
- package/chunks/{monitor-FI7PY36Z.js → monitor-QX3BM3JY.js} +50 -48
- package/chunks/nonInteractiveCli-NVRMD5MN.js +193 -0
- package/chunks/{notebook-edit-TPGMGBST.js → notebook-edit-AAILDPSV.js} +51 -49
- package/chunks/{open-with-auth-4UI3KHUR.js → open-with-auth-D6JD6LDS.js} +4 -4
- package/chunks/{openaiContentGenerator-SUCYANUZ.js → openaiContentGenerator-53PB3CMN.js} +27 -29
- package/chunks/pidfile-5ACKG2LI.js +116 -0
- package/chunks/process-registry-OAEG6WGC.js +662 -0
- package/chunks/{processUtils-IQZBYWSB.js → processUtils-2BCDO3DZ.js} +2 -2
- package/chunks/prompt-terminal-ledger-3RKW73A2.js +110 -0
- package/chunks/{pt-XN2YWJVM.js → pt-D73IFYT7.js} +3 -0
- package/chunks/{qwenContentGenerator-J4TXFGJT.js → qwenContentGenerator-N4LLWDPH.js} +56 -54
- package/chunks/{qwenOAuth2-RF33DNZW.js → qwenOAuth2-7AOCKSOQ.js} +7 -9
- package/chunks/{read-file-QE2B5FTX.js → read-file-DPAKXHYE.js} +13 -13
- package/chunks/{read-mcp-resource-XD257PGJ.js → read-mcp-resource-R4NO75DW.js} +2 -2
- package/chunks/{record-artifact-RZGZBENU.js → record-artifact-2EAYLAL5.js} +4 -4
- package/chunks/{report-findings-Q3SYKKW5.js → report-findings-Y4MSHDS3.js} +9 -5
- package/chunks/{request-shutdown-BCLA3JV2.js → request-shutdown-DWRMGTMB.js} +8 -7
- package/chunks/resumeHistoryUtils-3JQORXAK.js +123 -0
- package/chunks/ripGrep-AHF5QCI7.js +39 -0
- package/chunks/{ru-OLTMFJEW.js → ru-QMISLKRH.js} +3 -0
- package/chunks/{run-qwen-serve-TPHZMSXJ.js → run-qwen-serve-3LKV7KDC.js} +728 -256
- package/chunks/runtime-66F4W3IA.js +153 -0
- package/chunks/scheduled-tasks-2NVKS5Q2.js +132 -0
- package/chunks/scheduler-JNV7T3ZO.js +127 -0
- package/chunks/{sdk-exporters-http-KCJ4ZFG7.js → sdk-exporters-http-MFIWD23D.js} +2 -2
- package/chunks/{sdk-impl-HLSEDAEP.js → sdk-impl-2RXJ4Q6X.js} +11 -5
- package/chunks/send-message-WPZRCJOB.js +442 -0
- package/chunks/serve-GP2UNRP7.js +123 -0
- package/chunks/{server-DFUSSSHI.js → server-7W6C3SOW.js} +5233 -3623
- package/chunks/{session-3TEHNENZ.js → session-PZLBAQGG.js} +125 -118
- package/chunks/session-pr-refresh-OIVL3U3Z.js +367 -0
- package/chunks/{settings-VOZYKLTT.js → settings-ZOHIQWYK.js} +70 -67
- package/chunks/shell-FWZT6IUR.js +97 -0
- package/chunks/{skill-FEOKFX26.js → skill-RTQC2YGA.js} +121 -65
- package/chunks/skill-settings-AKMIXFSJ.js +123 -0
- package/chunks/spawnChannel-ZP23JL6O.js +116 -0
- package/chunks/standalone-update-P2Q5H4ZU.js +123 -0
- package/chunks/{startInteractiveUI-WHJPFOGH.js → startInteractiveUI-NSA6QPQZ.js} +2018 -1526
- package/chunks/{stdioHelpers-7A2EE64Z.js → stdioHelpers-AY6XCDY2.js} +3 -1
- package/chunks/{syntheticOutput-ML43GEFN.js → syntheticOutput-5URSQLIS.js} +3 -3
- package/chunks/{task-create-TMSV7RD5.js → task-create-3EXVDG3G.js} +12 -12
- package/chunks/{task-list-W55RBFRX.js → task-list-PIBYNSE7.js} +5 -6
- package/chunks/{task-stop-CXEH5L2D.js → task-stop-IV3BYWJC.js} +2 -2
- package/chunks/{task-update-N536I4GQ.js → task-update-H5BPDS5Y.js} +13 -14
- package/chunks/{team-create-QYSWCYCL.js → team-create-53T42W37.js} +56 -55
- package/chunks/{team-delete-FJRFX33M.js → team-delete-X3WPELS6.js} +25 -8
- package/chunks/{team-plan-approval-P7DAD644.js → team-plan-approval-D45ZC5OE.js} +50 -48
- package/chunks/terminal-image-renderer-FCRUWT6C.js +121 -0
- package/chunks/theme-manager-MHNPEDP7.js +107 -0
- package/chunks/{todoWrite-WDJNQEMW.js → todoWrite-A5IOZZ3P.js} +4 -4
- package/chunks/{tool-search-F2EMIAPV.js → tool-search-6RPBGGUX.js} +27 -23
- package/chunks/total-session-admission-MGV56XDK.js +117 -0
- package/chunks/trustedFolders-QJYEFMTU.js +127 -0
- package/chunks/{types-WYCFCEHR.js → types-HKLPXZB3.js} +1 -3
- package/chunks/{update-relaunch-4GF3OXMB.js → update-relaunch-ZDMQX63C.js} +6 -6
- package/chunks/updateCheck-4QZWCGP6.js +123 -0
- package/chunks/useAutoAcceptIndicator-DLIQN256.js +125 -0
- package/chunks/{validateNonInterActiveAuth-BPPA563H.js → validateNonInterActiveAuth-HGTG7AEK.js} +114 -107
- package/chunks/{version-ANP4TS6O.js → version-CPAZMC4B.js} +1 -1
- package/chunks/{web-fetch-QJADSAH3.js → web-fetch-N4K5M3M4.js} +16 -18
- package/chunks/{web-search-7TTQKEKM.js → web-search-4VZKM5SF.js} +9 -11
- package/chunks/{web-shell-static-A6QMUNNV.js → web-shell-static-PW7OEDQO.js} +3 -2
- package/chunks/{workflow-5XN6XSOT.js → workflow-2ALQKTHG.js} +229 -146
- package/chunks/workspace-providers-status-5JM4CB6R.js +119 -0
- package/chunks/{workspace-registration-store-XKVW2YVH.js → workspace-registration-store-RNMFVCH7.js} +2 -1
- package/chunks/workspace-registry-6EULAPZC.js +126 -0
- package/chunks/workspace-runtime-coordinator-IRI2DRCJ.js +127 -0
- package/chunks/workspace-service-SETHN4WE.js +131 -0
- package/chunks/workspace-skills-status-4D4EUEQU.js +118 -0
- package/chunks/{workspace-trust-reconciler-KLGOB53D.js → workspace-trust-reconciler-4LR3CDL4.js} +77 -76
- package/chunks/write-file-45GWPSOI.js +92 -0
- package/chunks/{zh-UNY63FR3.js → zh-2BQ7P6OD.js} +3 -0
- package/chunks/{zh-TW-RPTMRTHG.js → zh-TW-UFXRZTXX.js} +3 -0
- package/chunks/{zoom-image-6ASG6J4C.js → zoom-image-K6IQUKRC.js} +13 -13
- package/cli.js +129 -92
- package/locales/ca.js +6 -0
- package/locales/de.js +6 -0
- package/locales/en.js +6 -0
- package/locales/fr.js +6 -0
- package/locales/ja.js +6 -0
- package/locales/pt.js +6 -0
- package/locales/ru.js +6 -0
- package/locales/zh-TW.js +6 -0
- package/locales/zh.js +6 -0
- package/package.json +3 -3
- package/web-shell/assets/{abnfDiagram-VCTEODGH-uS-vtfs3.js → abnfDiagram-VCTEODGH-C80OZcPP.js} +1 -1
- package/web-shell/assets/{arc-BSTnk5pu.js → arc-AUqStYgf.js} +1 -1
- package/web-shell/assets/{architectureDiagram-5GKGNRK7-DCMyiqzy.js → architectureDiagram-5GKGNRK7-CQYOdYla.js} +1 -1
- package/web-shell/assets/{blockDiagram-NRAW4CY4-C7_gt0r1.js → blockDiagram-NRAW4CY4-lYKlFysy.js} +1 -1
- package/web-shell/assets/{c4Diagram-UCG6FXSJ-BeQg2L8B.js → c4Diagram-UCG6FXSJ-BoS23iIp.js} +1 -1
- package/web-shell/assets/channel-B0ovOJeK.js +1 -0
- package/web-shell/assets/{chunk-2Q5K7J3B-DnV9bKBe.js → chunk-2Q5K7J3B-DLLooL1E.js} +1 -1
- package/web-shell/assets/{chunk-5VM5RSS4-rT1GAcT7.js → chunk-5VM5RSS4-kPfJxScY.js} +1 -1
- package/web-shell/assets/{chunk-F27PBJKO-BcgSxqGA.js → chunk-F27PBJKO-CVGFBqkD.js} +1 -1
- package/web-shell/assets/{chunk-G27WJ6UU-Bw1ZhyZq.js → chunk-G27WJ6UU-CnM_kOVV.js} +1 -1
- package/web-shell/assets/{chunk-JWPE2WC7-D4iDw-96.js → chunk-JWPE2WC7-Cj-ajOkT.js} +1 -1
- package/web-shell/assets/{chunk-LCL6LL3I-D3MB3GTZ.js → chunk-LCL6LL3I-8L5c-wFo.js} +1 -1
- package/web-shell/assets/{chunk-POPQ4Y6H-Cep2ndrS.js → chunk-POPQ4Y6H-DsXTxpvL.js} +1 -1
- package/web-shell/assets/{chunk-SVP7TREG-BuahMHJc.js → chunk-SVP7TREG-DU5GE1_z.js} +1 -1
- package/web-shell/assets/{chunk-XXDRQBXY-BdujIvJY.js → chunk-XXDRQBXY-D2IIWZKt.js} +1 -1
- package/web-shell/assets/classDiagram-DTDB5LWJ-DMll2-6_.js +1 -0
- package/web-shell/assets/classDiagram-v2-JRS7N3AN-DMll2-6_.js +1 -0
- package/web-shell/assets/{cose-bilkent-JH36ORCC-CGLLUbF8.js → cose-bilkent-JH36ORCC-CF_txUJr.js} +1 -1
- package/web-shell/assets/{cynefin-OW5HDTMX-D6kDsRSh.js → cynefin-OW5HDTMX-DEZ-My-Z.js} +1 -1
- package/web-shell/assets/{cynefinDiagram-5FMLGOSQ-CCy78qZx.js → cynefinDiagram-5FMLGOSQ-BV7EKUS6.js} +1 -1
- package/web-shell/assets/{dagre-3AP2YEHR-D5gJuJsf.js → dagre-3AP2YEHR-JGIX0G4H.js} +1 -1
- package/web-shell/assets/{diagram-S7CK7UJ4-ZwEZ5W__.js → diagram-S7CK7UJ4-DdUY7q6A.js} +1 -1
- package/web-shell/assets/{diagram-UQ7AKVKN-B036APjv.js → diagram-UQ7AKVKN-fhzRZ19v.js} +1 -1
- package/web-shell/assets/{diagram-VSXAHHWV-BfDJ57Fq.js → diagram-VSXAHHWV-BOgOex6q.js} +1 -1
- package/web-shell/assets/{diagram-VX7I27RA-Wha0p5rT.js → diagram-VX7I27RA-C0NknIz6.js} +1 -1
- package/web-shell/assets/{diagram-Z3DM3KII-L20bz2K1.js → diagram-Z3DM3KII-C__3mUBX.js} +1 -1
- package/web-shell/assets/{ebnfDiagram-PWID7BFC-D1ZM0Cp7.js → ebnfDiagram-PWID7BFC-BvPhqycH.js} +1 -1
- package/web-shell/assets/{erDiagram-SSCWMZ5O-CMtA19v8.js → erDiagram-SSCWMZ5O-BqzoNoL9.js} +1 -1
- package/web-shell/assets/{flowDiagram-A5DVABFB-D96m326S.js → flowDiagram-A5DVABFB-CJKOre47.js} +1 -1
- package/web-shell/assets/{ganttDiagram-EL5Y4UJY-BuHiYG2o.js → ganttDiagram-EL5Y4UJY-C5xZjrp0.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-WWUBYQGX-Ck3NmkSw.js → gitGraphDiagram-WWUBYQGX-C0EjwuPK.js} +1 -1
- package/web-shell/assets/index-B_IPzA0h.js +2020 -0
- package/web-shell/assets/{index-CBGz4Qjl.js → index-C5M93syE.js} +1 -1
- package/web-shell/assets/index-D1OKGQlM.css +36 -0
- package/web-shell/assets/{infoDiagram-RXCK75RN-B6DAOyXe.js → infoDiagram-RXCK75RN-BUwzsYOs.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-5VMMS53U-Bpg-joFo.js → ishikawaDiagram-5VMMS53U-EDJ_DH3V.js} +1 -1
- package/web-shell/assets/{journeyDiagram-EYS64GPL-BclrHzGa.js → journeyDiagram-EYS64GPL-BnfcOsS0.js} +1 -1
- package/web-shell/assets/{kanban-definition-3QL26DDD-5gJzNTYG.js → kanban-definition-3QL26DDD-D_ofTVGu.js} +1 -1
- package/web-shell/assets/{layout-CEz_1L8z.js → layout-CBuQFdfZ.js} +1 -1
- package/web-shell/assets/{linear-CBjTq9Hc.js → linear-DRIHtmGv.js} +1 -1
- package/web-shell/assets/{mermaid.core-Dzhbxx_I.js → mermaid.core-CmR5wZH6.js} +6 -6
- package/web-shell/assets/{mindmap-definition-FBJOCRG2-DQdVqlBF.js → mindmap-definition-FBJOCRG2-BV374olC.js} +1 -1
- package/web-shell/assets/{pegDiagram-XKGWAZYB-D1JZlYXL.js → pegDiagram-XKGWAZYB-Bpy1SplK.js} +1 -1
- package/web-shell/assets/{pieDiagram-E7YTZNPT-BmkpYL0w.js → pieDiagram-E7YTZNPT-CeZx4hR2.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-AXDQQJYC-DRvD6co5.js → quadrantDiagram-AXDQQJYC-COYKs14h.js} +1 -1
- package/web-shell/assets/{railroadDiagram-O6MQD6OU-CEdjDuoo.js → railroadDiagram-O6MQD6OU-5g2pOeTs.js} +1 -1
- package/web-shell/assets/{requirementDiagram-EFPCY7ZU-BCHG-ie8.js → requirementDiagram-EFPCY7ZU-Yn8uPGtF.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-P5KCCOFB-C9cZMnye.js → sankeyDiagram-P5KCCOFB-CTayAGq6.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-WJ2MYXX4-DYHRV4zL.js → sequenceDiagram-WJ2MYXX4-BFWDKzUU.js} +1 -1
- package/web-shell/assets/{sizeCapture-X5ZJPWSS-DlK21aye.js → sizeCapture-X5ZJPWSS-CtErUbj4.js} +1 -1
- package/web-shell/assets/{stateDiagram-HBIQ2CUA-BrmZssvk.js → stateDiagram-HBIQ2CUA-Co3HFzOd.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-4QOOHH4V-C3gdRjPm.js +1 -0
- package/web-shell/assets/{swimlanes-XN3QIQJK-D9xfcayR.js → swimlanes-XN3QIQJK-CDZuxzOj.js} +1 -1
- package/web-shell/assets/swimlanesDiagram-VK2B7HYN-C34TGCPZ.js +8 -0
- package/web-shell/assets/{timeline-definition-24CTP7MA-D4gdDL_d.js → timeline-definition-24CTP7MA-UcNHj8At.js} +1 -1
- package/web-shell/assets/{vennDiagram-4TSXK5OY-DdhIExjI.js → vennDiagram-4TSXK5OY-JKR9pVFG.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-VM6X3IG4-C9RS_Zof.js → wardleyDiagram-VM6X3IG4-C4SU-yIW.js} +1 -1
- package/web-shell/assets/{xychartDiagram-S5SC5T6Z-orRgmxWk.js → xychartDiagram-S5SC5T6Z-D_yoWGZ4.js} +1 -1
- package/web-shell/index.html +197 -3
- package/chunks/MaxSizedBox-X5FZSNAJ.js +0 -112
- package/chunks/agent-PJIPDWHB.js +0 -97
- package/chunks/agent-headless-B4JVD7UK.js +0 -87
- package/chunks/bridge-FKZTVIS3.js +0 -121
- package/chunks/channel-settings-store-4UFCLBBG.js +0 -117
- package/chunks/chunk-7DJCPZE3.js +0 -945
- package/chunks/chunk-CPBF7KYF.js +0 -51
- package/chunks/chunk-H72BQNM3.js +0 -645
- package/chunks/chunk-J2OSJFP3.js +0 -202
- package/chunks/chunk-K2OJUPOE.js +0 -78
- package/chunks/chunk-P3QQPMQA.js +0 -19
- package/chunks/chunk-RUGNCYNO.js +0 -699
- package/chunks/chunk-T26EAKDL.js +0 -13
- package/chunks/chunk-ZGXYO7DY.js +0 -3416
- package/chunks/chunk-ZYNTSXWK.js +0 -32
- package/chunks/config-utils-BBOCOGZW.js +0 -113
- package/chunks/contextCommand-EET67MQ2.js +0 -108
- package/chunks/daemon-status-provider-RFKZOK3S.js +0 -119
- package/chunks/daemon-trust-policy-DRZPFB4Q.js +0 -114
- package/chunks/deferred-core-runtime-BBVIOP24.js +0 -116
- package/chunks/earlyInputCapture-2JOMMNQ4.js +0 -109
- package/chunks/environment-MOZMPUHR.js +0 -131
- package/chunks/errors-DMPQVYJL.js +0 -115
- package/chunks/exitPlanMode-UE4FGCY4.js +0 -85
- package/chunks/handleAutoUpdate-SGXKTQM6.js +0 -111
- package/chunks/i18n-B42HKHKH.js +0 -126
- package/chunks/initializer-FB66WYR3.js +0 -115
- package/chunks/installationInfo-Y7AULNMX.js +0 -109
- package/chunks/list-2HWGSVRA.js +0 -118
- package/chunks/list-agents-DI3YFTDT.js +0 -80
- package/chunks/loadedSettingsAdapter-7Y7IEI6T.js +0 -112
- package/chunks/mcp-QGG5NMJJ.js +0 -112
- package/chunks/nonInteractiveCli-4LO2TL72.js +0 -186
- package/chunks/pidfile-JMF2CLMY.js +0 -113
- package/chunks/process-registry-PMJOA5CO.js +0 -172
- package/chunks/prompt-terminal-ledger-DCZO6S7C.js +0 -107
- package/chunks/resumeHistoryUtils-CZ4JRDVW.js +0 -120
- package/chunks/ripGrep-R6B6HWPX.js +0 -39
- package/chunks/runtime-TTIGSBM4.js +0 -150
- package/chunks/scheduled-tasks-QB4ZVUQS.js +0 -129
- package/chunks/scheduler-YRUG22M7.js +0 -124
- package/chunks/send-message-Y5I6NYAM.js +0 -295
- package/chunks/serve-MXXCDAQN.js +0 -120
- package/chunks/shell-6RTKZVMI.js +0 -95
- package/chunks/skill-settings-VA4XVYVL.js +0 -120
- package/chunks/spawnChannel-F6WVP723.js +0 -114
- package/chunks/standalone-update-RGILUQVK.js +0 -120
- package/chunks/terminal-image-renderer-SMQE46UJ.js +0 -116
- package/chunks/theme-manager-RFO5WWFF.js +0 -104
- package/chunks/total-session-admission-HTVTP4BS.js +0 -116
- package/chunks/trustedFolders-7OCNMUQE.js +0 -124
- package/chunks/updateCheck-7QBDXZRT.js +0 -120
- package/chunks/useAutoAcceptIndicator-UI4SNL2O.js +0 -122
- package/chunks/workspace-providers-status-PDFZWL5Q.js +0 -116
- package/chunks/workspace-registry-23R7XRB5.js +0 -125
- package/chunks/workspace-service-MUYO2QIC.js +0 -131
- package/chunks/workspace-skills-status-IKECTOOQ.js +0 -115
- package/chunks/write-file-GGRBB5TZ.js +0 -90
- package/web-shell/assets/channel-CENAhErJ.js +0 -1
- package/web-shell/assets/classDiagram-DTDB5LWJ-CM6FArGH.js +0 -1
- package/web-shell/assets/classDiagram-v2-JRS7N3AN-CM6FArGH.js +0 -1
- package/web-shell/assets/index-8hXIpvo4.js +0 -1865
- package/web-shell/assets/index-DclEv91i.css +0 -5
- package/web-shell/assets/stateDiagram-v2-4QOOHH4V-B2Q7XDaE.js +0 -1
- package/web-shell/assets/swimlanesDiagram-VK2B7HYN-DpZPOSbX.js +0 -8
|
@@ -463,6 +463,18 @@ var CHANNEL_PROMPT_AUTHORIZATION_META_KEY = "qwen.daemon.channelPromptAuthorizat
|
|
|
463
463
|
var CHANNEL_PROMPT_META_KEY = "qwen.channel.prompt";
|
|
464
464
|
var ACP_PRIVATE_PARENT_CAPABILITY_META_KEY = "qwen-code/private-parent-capability";
|
|
465
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");
|
|
466
478
|
|
|
467
479
|
// packages/channels/base/dist/ChannelLoopTools.js
|
|
468
480
|
init_esbuild_shims();
|
|
@@ -791,11 +803,11 @@ var AcpBridge = class extends EventEmitter {
|
|
|
791
803
|
this.on("slashCommandOutput", onSlashCommandOutput);
|
|
792
804
|
this.on("responseBoundary", clearChunks);
|
|
793
805
|
const prompt = [];
|
|
794
|
-
|
|
806
|
+
for (const image of resolvePromptImages(options)) {
|
|
795
807
|
prompt.push({
|
|
796
808
|
type: "image",
|
|
797
|
-
data:
|
|
798
|
-
mimeType:
|
|
809
|
+
data: image.data,
|
|
810
|
+
mimeType: image.mimeType
|
|
799
811
|
});
|
|
800
812
|
}
|
|
801
813
|
prompt.push({ type: "text", text });
|
|
@@ -1106,14 +1118,14 @@ init_esbuild_shims();
|
|
|
1106
1118
|
// packages/channels/base/dist/PollingChannelBase.js
|
|
1107
1119
|
init_esbuild_shims();
|
|
1108
1120
|
import { createHash as createHash2 } from "node:crypto";
|
|
1109
|
-
import { mkdirSync as
|
|
1121
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, renameSync as renameSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
1110
1122
|
import { join as join6 } from "node:path";
|
|
1111
|
-
import
|
|
1123
|
+
import process4 from "node:process";
|
|
1112
1124
|
|
|
1113
1125
|
// packages/channels/base/dist/ChannelBase.js
|
|
1114
1126
|
init_esbuild_shims();
|
|
1115
1127
|
import { basename as basename3, join as join5 } from "node:path";
|
|
1116
|
-
import { randomUUID as
|
|
1128
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
1117
1129
|
|
|
1118
1130
|
// packages/channels/base/dist/BlockStreamer.js
|
|
1119
1131
|
init_esbuild_shims();
|
|
@@ -1153,6 +1165,10 @@ var BlockStreamer = class {
|
|
|
1153
1165
|
this.clearIdleTimer();
|
|
1154
1166
|
this.buffer = "";
|
|
1155
1167
|
}
|
|
1168
|
+
/** Await already-queued sends without emitting the buffered text. */
|
|
1169
|
+
async drain() {
|
|
1170
|
+
await this.sending;
|
|
1171
|
+
}
|
|
1156
1172
|
// ---------------------------------------------------------------------------
|
|
1157
1173
|
// Internal
|
|
1158
1174
|
// ---------------------------------------------------------------------------
|
|
@@ -1181,8 +1197,10 @@ var BlockStreamer = class {
|
|
|
1181
1197
|
const trimmed = text.trim();
|
|
1182
1198
|
if (!trimmed)
|
|
1183
1199
|
return;
|
|
1200
|
+
const block = /\n[^\S\n]*$/.test(text) ? `${trimmed}
|
|
1201
|
+
` : trimmed;
|
|
1184
1202
|
this.blockCount++;
|
|
1185
|
-
this.sending = this.sending.then(() => this.opts.send(
|
|
1203
|
+
this.sending = this.sending.then(() => this.opts.send(block)).catch(() => {
|
|
1186
1204
|
});
|
|
1187
1205
|
}
|
|
1188
1206
|
/**
|
|
@@ -1654,6 +1672,7 @@ var SessionRouter = class {
|
|
|
1654
1672
|
defaultScope;
|
|
1655
1673
|
channelScopes = /* @__PURE__ */ new Map();
|
|
1656
1674
|
channelApprovalModes = /* @__PURE__ */ new Map();
|
|
1675
|
+
channelsWithoutLoops = /* @__PURE__ */ new Set();
|
|
1657
1676
|
persistPath;
|
|
1658
1677
|
recoveryMode;
|
|
1659
1678
|
constructor(bridge, defaultCwd, scope = "user", persistPath, options = {}) {
|
|
@@ -1666,6 +1685,7 @@ var SessionRouter = class {
|
|
|
1666
1685
|
/** Replace the bridge instance (used after crash recovery restart). */
|
|
1667
1686
|
setBridge(bridge) {
|
|
1668
1687
|
this.bridge = bridge;
|
|
1688
|
+
this.liveSessionIds.clear();
|
|
1669
1689
|
}
|
|
1670
1690
|
/** Set scope override for a specific channel. */
|
|
1671
1691
|
setChannelScope(channelName, scope) {
|
|
@@ -1678,6 +1698,13 @@ var SessionRouter = class {
|
|
|
1678
1698
|
this.channelApprovalModes.delete(channelName);
|
|
1679
1699
|
}
|
|
1680
1700
|
}
|
|
1701
|
+
setChannelLoopsEnabled(channelName, enabled) {
|
|
1702
|
+
if (enabled) {
|
|
1703
|
+
this.channelsWithoutLoops.delete(channelName);
|
|
1704
|
+
} else {
|
|
1705
|
+
this.channelsWithoutLoops.add(channelName);
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1681
1708
|
routingKey(channelName, senderId, chatId, threadId) {
|
|
1682
1709
|
const scope = this.channelScopes.get(channelName) || this.defaultScope;
|
|
1683
1710
|
switch (scope) {
|
|
@@ -1694,7 +1721,12 @@ var SessionRouter = class {
|
|
|
1694
1721
|
}
|
|
1695
1722
|
sessionOptions(channelName) {
|
|
1696
1723
|
const approvalMode = this.channelApprovalModes.get(channelName);
|
|
1697
|
-
|
|
1724
|
+
const loopsDisabled = this.channelsWithoutLoops.has(channelName);
|
|
1725
|
+
return {
|
|
1726
|
+
...approvalMode ? { approvalMode } : {},
|
|
1727
|
+
...loopsDisabled ? { enableChannelLoops: false } : {},
|
|
1728
|
+
sourceId: channelName
|
|
1729
|
+
};
|
|
1698
1730
|
}
|
|
1699
1731
|
async resolve(channelName, senderId, chatId, threadId, cwd, isGroup, options) {
|
|
1700
1732
|
const key = this.routingKey(channelName, senderId, chatId, options?.routingThreadId ?? threadId);
|
|
@@ -1771,7 +1803,7 @@ var SessionRouter = class {
|
|
|
1771
1803
|
async createAndStoreSession(key, input, operation) {
|
|
1772
1804
|
const loadWindow = this.beginSessionLoad();
|
|
1773
1805
|
try {
|
|
1774
|
-
const sessionId = await this.createLiveSession(input.cwd, loadWindow, key, this.sessionOptions(input.channelName), operation
|
|
1806
|
+
const sessionId = await this.createLiveSession(input.cwd, loadWindow, key, this.sessionOptions(input.channelName), operation);
|
|
1775
1807
|
try {
|
|
1776
1808
|
this.assertOperationCurrent(operation);
|
|
1777
1809
|
} catch (error) {
|
|
@@ -1827,7 +1859,7 @@ var SessionRouter = class {
|
|
|
1827
1859
|
} catch (loadError) {
|
|
1828
1860
|
this.assertOperationCurrent(operation);
|
|
1829
1861
|
try {
|
|
1830
|
-
const replacement = await this.createLiveSession(input.cwd, loadWindow, key, this.sessionOptions(input.channelName), operation
|
|
1862
|
+
const replacement = await this.createLiveSession(input.cwd, loadWindow, key, this.sessionOptions(input.channelName), operation);
|
|
1831
1863
|
try {
|
|
1832
1864
|
this.assertOperationCurrent(operation);
|
|
1833
1865
|
} catch (error) {
|
|
@@ -1863,6 +1895,9 @@ var SessionRouter = class {
|
|
|
1863
1895
|
getTarget(sessionId) {
|
|
1864
1896
|
return this.toTarget.get(sessionId);
|
|
1865
1897
|
}
|
|
1898
|
+
isSessionLive(sessionId) {
|
|
1899
|
+
return this.toTarget.has(sessionId) && this.isLive(sessionId);
|
|
1900
|
+
}
|
|
1866
1901
|
getSession(channelName, senderId, chatId, threadId) {
|
|
1867
1902
|
return this.toSession.get(this.routingKey(channelName, senderId, chatId, threadId));
|
|
1868
1903
|
}
|
|
@@ -1881,6 +1916,120 @@ var SessionRouter = class {
|
|
|
1881
1916
|
}
|
|
1882
1917
|
return false;
|
|
1883
1918
|
}
|
|
1919
|
+
getSessionCwd(sessionId) {
|
|
1920
|
+
return this.toCwd.get(sessionId);
|
|
1921
|
+
}
|
|
1922
|
+
async createManagedSession(target, cwd) {
|
|
1923
|
+
const loadWindow = this.beginSessionLoad();
|
|
1924
|
+
const lifecycleGeneration = this.lifecycleGeneration;
|
|
1925
|
+
const bridge = this.bridge;
|
|
1926
|
+
const bindingToken = {};
|
|
1927
|
+
try {
|
|
1928
|
+
let lastDeadSessionId;
|
|
1929
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1930
|
+
const sessionId = await bridge.newSession(cwd, {
|
|
1931
|
+
...this.sessionOptions(target.channelName),
|
|
1932
|
+
sourceId: target.channelName
|
|
1933
|
+
}, bindingToken);
|
|
1934
|
+
if (lifecycleGeneration !== this.lifecycleGeneration || bridge !== this.bridge) {
|
|
1935
|
+
this.scheduleManagedDiscard(bridge, sessionId, bindingToken);
|
|
1936
|
+
throw new Error("Managed session creation was invalidated");
|
|
1937
|
+
}
|
|
1938
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
1939
|
+
throw new Error("Invalid session ID from bridge");
|
|
1940
|
+
}
|
|
1941
|
+
if (loadWindow.delete(sessionId)) {
|
|
1942
|
+
lastDeadSessionId = sessionId;
|
|
1943
|
+
await bridge.discardSession?.(sessionId, bindingToken);
|
|
1944
|
+
continue;
|
|
1945
|
+
}
|
|
1946
|
+
this.toTarget.set(sessionId, target);
|
|
1947
|
+
this.toCwd.set(sessionId, cwd);
|
|
1948
|
+
this.liveSessionIds.add(sessionId);
|
|
1949
|
+
return sessionId;
|
|
1950
|
+
}
|
|
1951
|
+
throw new Error(`Managed session ${lastDeadSessionId ?? "unknown"} died before creation completed`);
|
|
1952
|
+
} finally {
|
|
1953
|
+
this.endSessionLoad(loadWindow);
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
async loadManagedSession(sessionId, target, cwd) {
|
|
1957
|
+
if (this.liveSessionIds.has(sessionId)) {
|
|
1958
|
+
this.toTarget.set(sessionId, target);
|
|
1959
|
+
this.toCwd.set(sessionId, cwd);
|
|
1960
|
+
return { loaded: false };
|
|
1961
|
+
}
|
|
1962
|
+
const loadWindow = this.beginSessionLoad();
|
|
1963
|
+
const lifecycleGeneration = this.lifecycleGeneration;
|
|
1964
|
+
const bridge = this.bridge;
|
|
1965
|
+
const bindingToken = {};
|
|
1966
|
+
let loadedSessionId;
|
|
1967
|
+
try {
|
|
1968
|
+
loadedSessionId = await bridge.loadSession(sessionId, cwd, this.sessionOptions(target.channelName), bindingToken);
|
|
1969
|
+
if (lifecycleGeneration !== this.lifecycleGeneration || bridge !== this.bridge) {
|
|
1970
|
+
this.scheduleManagedDiscard(bridge, loadedSessionId, bindingToken);
|
|
1971
|
+
loadedSessionId = void 0;
|
|
1972
|
+
throw new Error("Managed session load was invalidated");
|
|
1973
|
+
}
|
|
1974
|
+
if (loadedSessionId !== sessionId) {
|
|
1975
|
+
const unexpectedSessionId = loadedSessionId;
|
|
1976
|
+
await bridge.discardSession?.(unexpectedSessionId, bindingToken);
|
|
1977
|
+
loadedSessionId = void 0;
|
|
1978
|
+
throw new Error(`Bridge returned session ${unexpectedSessionId || "unknown"} while loading ${sessionId}`);
|
|
1979
|
+
}
|
|
1980
|
+
if (loadWindow.delete(sessionId)) {
|
|
1981
|
+
throw new Error(`Managed session ${sessionId} died before loading completed`);
|
|
1982
|
+
}
|
|
1983
|
+
this.toTarget.set(sessionId, target);
|
|
1984
|
+
this.toCwd.set(sessionId, cwd);
|
|
1985
|
+
this.liveSessionIds.add(sessionId);
|
|
1986
|
+
return { loaded: true };
|
|
1987
|
+
} catch (error) {
|
|
1988
|
+
if (loadedSessionId) {
|
|
1989
|
+
await bridge.discardSession?.(loadedSessionId, bindingToken).catch(() => void 0);
|
|
1990
|
+
}
|
|
1991
|
+
throw error;
|
|
1992
|
+
} finally {
|
|
1993
|
+
this.endSessionLoad(loadWindow);
|
|
1994
|
+
}
|
|
1995
|
+
}
|
|
1996
|
+
activateManagedSession(sessionId, target, cwd) {
|
|
1997
|
+
const key = this.routingKey(target.channelName, target.senderId, target.chatId, target.threadId);
|
|
1998
|
+
if (this.toSession.get(key) === sessionId)
|
|
1999
|
+
return;
|
|
2000
|
+
this.invalidateRouteOperation(key);
|
|
2001
|
+
this.toSession.set(key, sessionId);
|
|
2002
|
+
this.toTarget.set(sessionId, target);
|
|
2003
|
+
this.toCwd.set(sessionId, cwd);
|
|
2004
|
+
this.persist();
|
|
2005
|
+
}
|
|
2006
|
+
forgetManagedSession(sessionId) {
|
|
2007
|
+
let changed = false;
|
|
2008
|
+
for (const [key, mappedSessionId] of this.toSession) {
|
|
2009
|
+
if (mappedSessionId !== sessionId)
|
|
2010
|
+
continue;
|
|
2011
|
+
this.invalidateRouteOperation(key);
|
|
2012
|
+
this.toSession.delete(key);
|
|
2013
|
+
changed = true;
|
|
2014
|
+
}
|
|
2015
|
+
changed = this.toTarget.delete(sessionId) || changed;
|
|
2016
|
+
changed = this.toCwd.delete(sessionId) || changed;
|
|
2017
|
+
changed = this.liveSessionIds.delete(sessionId) || changed;
|
|
2018
|
+
if (changed)
|
|
2019
|
+
this.persist();
|
|
2020
|
+
}
|
|
2021
|
+
async detachManagedSession(sessionId) {
|
|
2022
|
+
try {
|
|
2023
|
+
if (this.liveSessionIds.has(sessionId)) {
|
|
2024
|
+
if (!this.bridge.discardSession) {
|
|
2025
|
+
throw new Error("Managed session detach is not supported");
|
|
2026
|
+
}
|
|
2027
|
+
await this.bridge.discardSession(sessionId);
|
|
2028
|
+
}
|
|
2029
|
+
} finally {
|
|
2030
|
+
this.forgetManagedSession(sessionId);
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
1884
2033
|
/**
|
|
1885
2034
|
* Remove session(s) for the given sender. Returns the removed session IDs.
|
|
1886
2035
|
*/
|
|
@@ -2055,6 +2204,7 @@ var SessionRouter = class {
|
|
|
2055
2204
|
this.toSession.set(key, sessionId);
|
|
2056
2205
|
this.toTarget.set(sessionId, entry.target);
|
|
2057
2206
|
this.toCwd.set(sessionId, entry.cwd);
|
|
2207
|
+
this.liveSessionIds.add(sessionId);
|
|
2058
2208
|
reservation.resolve(sessionId);
|
|
2059
2209
|
if (sessionId !== entry.sessionId) {
|
|
2060
2210
|
changed = true;
|
|
@@ -2193,11 +2343,11 @@ var SessionRouter = class {
|
|
|
2193
2343
|
}
|
|
2194
2344
|
}
|
|
2195
2345
|
}
|
|
2196
|
-
async createLiveSession(cwd, loadWindow, routingKey, options, operation
|
|
2346
|
+
async createLiveSession(cwd, loadWindow, routingKey, options, operation) {
|
|
2197
2347
|
const maxAttempts = 2;
|
|
2198
2348
|
let lastDeadSessionId;
|
|
2199
2349
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
2200
|
-
const sessionId = await this.bridge.newSession(cwd,
|
|
2350
|
+
const sessionId = await this.bridge.newSession(cwd, options, operation);
|
|
2201
2351
|
try {
|
|
2202
2352
|
this.assertOperationCurrent(operation);
|
|
2203
2353
|
} catch (error) {
|
|
@@ -2278,6 +2428,12 @@ var SessionRouter = class {
|
|
|
2278
2428
|
} catch {
|
|
2279
2429
|
}
|
|
2280
2430
|
}
|
|
2431
|
+
scheduleManagedDiscard(bridge, sessionId, bindingToken) {
|
|
2432
|
+
try {
|
|
2433
|
+
void bridge.discardSession?.(sessionId, bindingToken).catch(() => void 0);
|
|
2434
|
+
} catch {
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2281
2437
|
createSessionReservation() {
|
|
2282
2438
|
let resolveReservation;
|
|
2283
2439
|
let rejectReservation;
|
|
@@ -2296,6 +2452,676 @@ var SessionRouter = class {
|
|
|
2296
2452
|
}
|
|
2297
2453
|
};
|
|
2298
2454
|
|
|
2455
|
+
// packages/channels/base/dist/named-session-manager.js
|
|
2456
|
+
init_esbuild_shims();
|
|
2457
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
2458
|
+
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";
|
|
2459
|
+
import { dirname as dirname4 } from "node:path";
|
|
2460
|
+
import process3 from "node:process";
|
|
2461
|
+
var REGISTRY_VERSION = 1;
|
|
2462
|
+
var MAX_OPEN_TASKS = 8;
|
|
2463
|
+
var TASK_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$/;
|
|
2464
|
+
var NamedSessionManager = class {
|
|
2465
|
+
static {
|
|
2466
|
+
__name(this, "NamedSessionManager");
|
|
2467
|
+
}
|
|
2468
|
+
channelName;
|
|
2469
|
+
cwd;
|
|
2470
|
+
canonicalCwd;
|
|
2471
|
+
filePath;
|
|
2472
|
+
router;
|
|
2473
|
+
isBusy;
|
|
2474
|
+
now;
|
|
2475
|
+
registry;
|
|
2476
|
+
taskBySessionId;
|
|
2477
|
+
ownerOperations = /* @__PURE__ */ new Map();
|
|
2478
|
+
constructor(options) {
|
|
2479
|
+
this.channelName = options.channelName;
|
|
2480
|
+
this.cwd = options.cwd;
|
|
2481
|
+
this.canonicalCwd = canonicalizeWorkspacePath(options.cwd);
|
|
2482
|
+
this.filePath = options.filePath;
|
|
2483
|
+
this.router = options.router;
|
|
2484
|
+
this.isBusy = options.isBusy;
|
|
2485
|
+
this.now = options.now ?? Date.now;
|
|
2486
|
+
this.registry = this.readRegistry();
|
|
2487
|
+
this.taskBySessionId = this.buildTaskIndex(this.registry);
|
|
2488
|
+
}
|
|
2489
|
+
presentation(sessionId) {
|
|
2490
|
+
const reference = this.taskBySessionId.get(sessionId);
|
|
2491
|
+
return reference ? this.cloneTaskReference(reference) : void 0;
|
|
2492
|
+
}
|
|
2493
|
+
async resolvePresentation(sessionId) {
|
|
2494
|
+
const existing = this.presentation(sessionId);
|
|
2495
|
+
if (existing)
|
|
2496
|
+
return existing;
|
|
2497
|
+
const target = this.router.getTarget(sessionId);
|
|
2498
|
+
if (target?.channelName !== this.channelName || this.router.getSession(this.channelName, target.senderId, target.chatId) !== sessionId) {
|
|
2499
|
+
return void 0;
|
|
2500
|
+
}
|
|
2501
|
+
return this.withOwnerLock(target, async () => {
|
|
2502
|
+
const concurrent = this.presentation(sessionId);
|
|
2503
|
+
if (concurrent)
|
|
2504
|
+
return concurrent;
|
|
2505
|
+
if (this.getOwner(target))
|
|
2506
|
+
return void 0;
|
|
2507
|
+
const routedTarget = this.router.getTarget(sessionId);
|
|
2508
|
+
if (routedTarget?.channelName !== this.channelName || routedTarget.chatId !== target.chatId || routedTarget.senderId !== target.senderId || this.router.getSession(this.channelName, target.senderId, target.chatId) !== sessionId) {
|
|
2509
|
+
return void 0;
|
|
2510
|
+
}
|
|
2511
|
+
const routedCwd = this.router.getSessionCwd(sessionId);
|
|
2512
|
+
if (routedCwd !== void 0 && !this.isCurrentCwd(routedCwd)) {
|
|
2513
|
+
return void 0;
|
|
2514
|
+
}
|
|
2515
|
+
const timestamp = this.nextTimestamp();
|
|
2516
|
+
const task = {
|
|
2517
|
+
name: "default",
|
|
2518
|
+
sessionId,
|
|
2519
|
+
cwd: routedCwd ?? this.cwd,
|
|
2520
|
+
isolation: "shared",
|
|
2521
|
+
status: "open",
|
|
2522
|
+
target: routedTarget,
|
|
2523
|
+
createdAt: timestamp,
|
|
2524
|
+
updatedAt: timestamp,
|
|
2525
|
+
lastSelectedAt: timestamp
|
|
2526
|
+
};
|
|
2527
|
+
this.commitOwner(this.createOwner(routedTarget, task.name, [task]));
|
|
2528
|
+
return this.presentation(sessionId);
|
|
2529
|
+
});
|
|
2530
|
+
}
|
|
2531
|
+
resolve(input, reserve) {
|
|
2532
|
+
return this.withOwnerLock(input, () => this.resolveLocked(input, reserve));
|
|
2533
|
+
}
|
|
2534
|
+
resolveAfterPreparation(input, prepare, needsSession, reserve) {
|
|
2535
|
+
return this.withOwnerLock(input, async () => {
|
|
2536
|
+
if (await prepare() === false)
|
|
2537
|
+
return { status: "aborted" };
|
|
2538
|
+
if (!needsSession())
|
|
2539
|
+
return { status: "bypassed" };
|
|
2540
|
+
try {
|
|
2541
|
+
return {
|
|
2542
|
+
status: "resolved",
|
|
2543
|
+
sessionId: await this.resolveLocked(input, reserve)
|
|
2544
|
+
};
|
|
2545
|
+
} catch (error) {
|
|
2546
|
+
return { status: "resolve_error", error };
|
|
2547
|
+
}
|
|
2548
|
+
});
|
|
2549
|
+
}
|
|
2550
|
+
list(input, includeClosed) {
|
|
2551
|
+
return this.withOwnerLock(input, async () => {
|
|
2552
|
+
const owner = await this.ensureOwner(input, false);
|
|
2553
|
+
if (!owner)
|
|
2554
|
+
return [];
|
|
2555
|
+
return owner.tasks.filter((task) => includeClosed || task.status === "open").map((task) => this.view(owner, task));
|
|
2556
|
+
});
|
|
2557
|
+
}
|
|
2558
|
+
current(input) {
|
|
2559
|
+
return this.withOwnerLock(input, async () => {
|
|
2560
|
+
const owner = await this.ensureOwner(input, false);
|
|
2561
|
+
if (!owner?.activeTaskName)
|
|
2562
|
+
return void 0;
|
|
2563
|
+
const task = this.findTask(owner, owner.activeTaskName);
|
|
2564
|
+
return task ? this.selection(owner, task) : void 0;
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2567
|
+
lookup(input, name) {
|
|
2568
|
+
return this.withOwnerLock(input, async () => {
|
|
2569
|
+
const owner = await this.ensureOwner(input, false);
|
|
2570
|
+
if (!owner)
|
|
2571
|
+
return void 0;
|
|
2572
|
+
const task = this.findTask(owner, name);
|
|
2573
|
+
return task ? this.selection(owner, task) : void 0;
|
|
2574
|
+
});
|
|
2575
|
+
}
|
|
2576
|
+
resumeReserved(input, sessionId) {
|
|
2577
|
+
return this.withOwnerLock(input, async () => {
|
|
2578
|
+
const owner = await this.ensureOwner(input, false);
|
|
2579
|
+
const task = owner?.tasks.find((candidate) => candidate.sessionId === sessionId && candidate.status === "open");
|
|
2580
|
+
if (!task)
|
|
2581
|
+
return false;
|
|
2582
|
+
await this.loadTask(task, `Could not reload reserved task "${task.name}".`);
|
|
2583
|
+
return true;
|
|
2584
|
+
});
|
|
2585
|
+
}
|
|
2586
|
+
create(input, name) {
|
|
2587
|
+
return this.withOwnerLock(input, async () => {
|
|
2588
|
+
this.validateName(name);
|
|
2589
|
+
const existingOwner = await this.ensureOwner(input, false);
|
|
2590
|
+
const owner = existingOwner ?? this.createOwner(input, null, []);
|
|
2591
|
+
if (this.findTask(owner, name)) {
|
|
2592
|
+
throw new Error(`Task "${name}" already exists.`);
|
|
2593
|
+
}
|
|
2594
|
+
if (this.openTaskCount(owner) >= MAX_OPEN_TASKS) {
|
|
2595
|
+
throw new Error("You already have eight open tasks. Close one before creating another.");
|
|
2596
|
+
}
|
|
2597
|
+
const timestamp = this.nextTimestamp(owner);
|
|
2598
|
+
const target = this.target(input);
|
|
2599
|
+
const sessionId = await this.createSession(target, this.cwd, `Could not create task "${name}".`);
|
|
2600
|
+
const task = {
|
|
2601
|
+
name,
|
|
2602
|
+
sessionId,
|
|
2603
|
+
cwd: this.cwd,
|
|
2604
|
+
isolation: "shared",
|
|
2605
|
+
status: "open",
|
|
2606
|
+
target,
|
|
2607
|
+
createdAt: timestamp,
|
|
2608
|
+
updatedAt: timestamp,
|
|
2609
|
+
lastSelectedAt: timestamp
|
|
2610
|
+
};
|
|
2611
|
+
const nextOwner = {
|
|
2612
|
+
...owner,
|
|
2613
|
+
activeTaskName: name,
|
|
2614
|
+
tasks: [...owner.tasks, task]
|
|
2615
|
+
};
|
|
2616
|
+
try {
|
|
2617
|
+
this.commitOwner(nextOwner);
|
|
2618
|
+
} catch (error) {
|
|
2619
|
+
await this.router.detachManagedSession(sessionId).catch(() => void 0);
|
|
2620
|
+
throw error;
|
|
2621
|
+
}
|
|
2622
|
+
this.router.activateManagedSession(sessionId, target, this.cwd);
|
|
2623
|
+
return this.selection(nextOwner, task);
|
|
2624
|
+
});
|
|
2625
|
+
}
|
|
2626
|
+
use(input, name) {
|
|
2627
|
+
return this.withOwnerLock(input, async () => {
|
|
2628
|
+
const owner = await this.ensureOwner(input, false);
|
|
2629
|
+
if (!owner)
|
|
2630
|
+
throw new Error("No named tasks exist in this chat.");
|
|
2631
|
+
const task = this.findTask(owner, name);
|
|
2632
|
+
if (!task)
|
|
2633
|
+
throw new Error(`Task "${name}" was not found.`);
|
|
2634
|
+
if (task.status === "closed" && this.openTaskCount(owner) >= MAX_OPEN_TASKS) {
|
|
2635
|
+
throw new Error("You already have eight open tasks. Close one before reopening another.");
|
|
2636
|
+
}
|
|
2637
|
+
const timestamp = this.nextTimestamp(owner);
|
|
2638
|
+
const loaded = await this.loadTask(task, `Could not load task "${task.name}". The current task was not changed.`);
|
|
2639
|
+
const updatedTask = {
|
|
2640
|
+
...task,
|
|
2641
|
+
status: "open",
|
|
2642
|
+
updatedAt: timestamp,
|
|
2643
|
+
lastSelectedAt: timestamp
|
|
2644
|
+
};
|
|
2645
|
+
const nextOwner = this.replaceTask(owner, updatedTask, task.name);
|
|
2646
|
+
try {
|
|
2647
|
+
this.commitOwner(nextOwner);
|
|
2648
|
+
} catch (error) {
|
|
2649
|
+
if (loaded) {
|
|
2650
|
+
await this.router.detachManagedSession(task.sessionId).catch(() => void 0);
|
|
2651
|
+
}
|
|
2652
|
+
throw error;
|
|
2653
|
+
}
|
|
2654
|
+
this.router.activateManagedSession(task.sessionId, task.target, task.cwd);
|
|
2655
|
+
return this.selection(nextOwner, updatedTask);
|
|
2656
|
+
});
|
|
2657
|
+
}
|
|
2658
|
+
close(input, name) {
|
|
2659
|
+
return this.withOwnerLock(input, async () => {
|
|
2660
|
+
const owner = await this.ensureOwner(input, false);
|
|
2661
|
+
if (!owner)
|
|
2662
|
+
throw new Error("No named tasks exist in this chat.");
|
|
2663
|
+
const task = this.findTask(owner, name);
|
|
2664
|
+
if (!task)
|
|
2665
|
+
throw new Error(`Task "${name}" was not found.`);
|
|
2666
|
+
if (task.status === "closed") {
|
|
2667
|
+
throw new Error(`Task "${task.name}" is already closed.`);
|
|
2668
|
+
}
|
|
2669
|
+
if (this.isBusy(task.sessionId)) {
|
|
2670
|
+
throw new Error(`Task "${task.name}" is still running or waiting for permission.`);
|
|
2671
|
+
}
|
|
2672
|
+
const wasActive = this.sameName(owner.activeTaskName, task.name);
|
|
2673
|
+
const timestamp = this.nextTimestamp(owner);
|
|
2674
|
+
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;
|
|
2675
|
+
let replacementLoaded = false;
|
|
2676
|
+
if (replacement) {
|
|
2677
|
+
replacementLoaded = await this.loadTask(replacement, `Could not load fallback task "${replacement.name}". Task "${task.name}" was not closed.`);
|
|
2678
|
+
}
|
|
2679
|
+
const closedTask = {
|
|
2680
|
+
...task,
|
|
2681
|
+
status: "closed",
|
|
2682
|
+
updatedAt: timestamp
|
|
2683
|
+
};
|
|
2684
|
+
let nextOwner = this.replaceTask(owner, closedTask, wasActive ? replacement?.name ?? null : owner.activeTaskName);
|
|
2685
|
+
const selectedReplacement = replacement ? {
|
|
2686
|
+
...replacement,
|
|
2687
|
+
updatedAt: timestamp,
|
|
2688
|
+
lastSelectedAt: timestamp
|
|
2689
|
+
} : void 0;
|
|
2690
|
+
if (selectedReplacement) {
|
|
2691
|
+
nextOwner = this.replaceTask(nextOwner, selectedReplacement, selectedReplacement.name);
|
|
2692
|
+
}
|
|
2693
|
+
try {
|
|
2694
|
+
this.commitOwner(nextOwner);
|
|
2695
|
+
} catch (error) {
|
|
2696
|
+
if (replacement && replacementLoaded) {
|
|
2697
|
+
await this.router.detachManagedSession(replacement.sessionId).catch(() => void 0);
|
|
2698
|
+
}
|
|
2699
|
+
throw error;
|
|
2700
|
+
}
|
|
2701
|
+
if (replacement) {
|
|
2702
|
+
this.router.activateManagedSession(replacement.sessionId, replacement.target, replacement.cwd);
|
|
2703
|
+
}
|
|
2704
|
+
try {
|
|
2705
|
+
await this.router.detachManagedSession(task.sessionId);
|
|
2706
|
+
} catch (error) {
|
|
2707
|
+
this.commitOwner(owner);
|
|
2708
|
+
this.router.forgetManagedSession(task.sessionId);
|
|
2709
|
+
await this.loadTask(task, `Task "${task.name}" could not be restored after close failed.`);
|
|
2710
|
+
if (wasActive) {
|
|
2711
|
+
this.router.activateManagedSession(task.sessionId, task.target, task.cwd);
|
|
2712
|
+
}
|
|
2713
|
+
throw new Error(`Failed to close task "${task.name}".`, {
|
|
2714
|
+
cause: error
|
|
2715
|
+
});
|
|
2716
|
+
}
|
|
2717
|
+
const activeTask = nextOwner.activeTaskName ? this.findTask(nextOwner, nextOwner.activeTaskName) : void 0;
|
|
2718
|
+
return {
|
|
2719
|
+
closed: this.view(nextOwner, closedTask),
|
|
2720
|
+
...activeTask ? { active: this.view(nextOwner, activeTask) } : {}
|
|
2721
|
+
};
|
|
2722
|
+
});
|
|
2723
|
+
}
|
|
2724
|
+
reset(input) {
|
|
2725
|
+
return this.withOwnerLock(input, async () => {
|
|
2726
|
+
const owner = await this.ensureOwner(input, false);
|
|
2727
|
+
if (!owner?.activeTaskName)
|
|
2728
|
+
return void 0;
|
|
2729
|
+
const task = this.findTask(owner, owner.activeTaskName);
|
|
2730
|
+
if (!task || task.status !== "open")
|
|
2731
|
+
return void 0;
|
|
2732
|
+
const timestamp = this.nextTimestamp(owner);
|
|
2733
|
+
const sessionId = await this.createSession(task.target, task.cwd, `Could not reset task "${task.name}".`);
|
|
2734
|
+
const updatedTask = {
|
|
2735
|
+
...task,
|
|
2736
|
+
sessionId,
|
|
2737
|
+
updatedAt: timestamp,
|
|
2738
|
+
lastSelectedAt: timestamp
|
|
2739
|
+
};
|
|
2740
|
+
const nextOwner = this.replaceTask(owner, updatedTask, task.name);
|
|
2741
|
+
try {
|
|
2742
|
+
this.commitOwner(nextOwner);
|
|
2743
|
+
} catch (error) {
|
|
2744
|
+
await this.router.detachManagedSession(sessionId).catch(() => void 0);
|
|
2745
|
+
throw error;
|
|
2746
|
+
}
|
|
2747
|
+
this.router.activateManagedSession(sessionId, updatedTask.target, updatedTask.cwd);
|
|
2748
|
+
this.router.forgetManagedSession(task.sessionId);
|
|
2749
|
+
return {
|
|
2750
|
+
name: task.name,
|
|
2751
|
+
previousSessionId: task.sessionId,
|
|
2752
|
+
sessionId
|
|
2753
|
+
};
|
|
2754
|
+
});
|
|
2755
|
+
}
|
|
2756
|
+
async ensureOwner(input, createDefault) {
|
|
2757
|
+
const existing = this.getOwner(input);
|
|
2758
|
+
if (existing)
|
|
2759
|
+
return existing;
|
|
2760
|
+
const sessionId = this.router.getSession(this.channelName, input.senderId, input.chatId);
|
|
2761
|
+
const routedTarget = sessionId ? this.router.getTarget(sessionId) : void 0;
|
|
2762
|
+
if (sessionId && routedTarget && (routedTarget.channelName !== this.channelName || routedTarget.chatId !== input.chatId || routedTarget.senderId !== input.senderId)) {
|
|
2763
|
+
this.adoptForeignLegacyRoute(sessionId, routedTarget);
|
|
2764
|
+
}
|
|
2765
|
+
if (sessionId && routedTarget?.channelName === this.channelName && routedTarget.chatId === input.chatId && routedTarget.senderId === input.senderId) {
|
|
2766
|
+
const routedCwd = this.router.getSessionCwd(sessionId);
|
|
2767
|
+
if (routedCwd !== void 0 && !this.isCurrentCwd(routedCwd)) {
|
|
2768
|
+
await this.router.detachManagedSession(sessionId).catch(() => void 0);
|
|
2769
|
+
} else {
|
|
2770
|
+
const timestamp2 = this.nextTimestamp();
|
|
2771
|
+
const task2 = {
|
|
2772
|
+
name: "default",
|
|
2773
|
+
sessionId,
|
|
2774
|
+
cwd: routedCwd ?? this.cwd,
|
|
2775
|
+
isolation: "shared",
|
|
2776
|
+
status: "open",
|
|
2777
|
+
target: routedTarget,
|
|
2778
|
+
createdAt: timestamp2,
|
|
2779
|
+
updatedAt: timestamp2,
|
|
2780
|
+
lastSelectedAt: timestamp2
|
|
2781
|
+
};
|
|
2782
|
+
const adopted = this.createOwner(input, task2.name, [task2]);
|
|
2783
|
+
this.commitOwner(adopted);
|
|
2784
|
+
return adopted;
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
if (!createDefault)
|
|
2788
|
+
return void 0;
|
|
2789
|
+
const target = this.target(input);
|
|
2790
|
+
const timestamp = this.nextTimestamp();
|
|
2791
|
+
const createdSessionId = await this.createSession(target, this.cwd, "Could not create the default task.");
|
|
2792
|
+
const task = {
|
|
2793
|
+
name: "default",
|
|
2794
|
+
sessionId: createdSessionId,
|
|
2795
|
+
cwd: this.cwd,
|
|
2796
|
+
isolation: "shared",
|
|
2797
|
+
status: "open",
|
|
2798
|
+
target,
|
|
2799
|
+
createdAt: timestamp,
|
|
2800
|
+
updatedAt: timestamp,
|
|
2801
|
+
lastSelectedAt: timestamp
|
|
2802
|
+
};
|
|
2803
|
+
const created = this.createOwner(input, task.name, [task]);
|
|
2804
|
+
try {
|
|
2805
|
+
this.commitOwner(created);
|
|
2806
|
+
} catch (error) {
|
|
2807
|
+
await this.router.detachManagedSession(createdSessionId).catch(() => void 0);
|
|
2808
|
+
throw error;
|
|
2809
|
+
}
|
|
2810
|
+
this.router.activateManagedSession(createdSessionId, target, this.cwd);
|
|
2811
|
+
return created;
|
|
2812
|
+
}
|
|
2813
|
+
async resolveLocked(input, reserve) {
|
|
2814
|
+
const owner = await this.ensureOwner(input, true);
|
|
2815
|
+
if (!owner?.activeTaskName)
|
|
2816
|
+
return void 0;
|
|
2817
|
+
const task = this.findTask(owner, owner.activeTaskName);
|
|
2818
|
+
if (!task || task.status !== "open") {
|
|
2819
|
+
throw new Error("The selected Channel task is unavailable.");
|
|
2820
|
+
}
|
|
2821
|
+
const release = reserve?.(task.sessionId);
|
|
2822
|
+
try {
|
|
2823
|
+
await this.loadTask(task, `Could not load task "${task.name}". No replacement session was created.`);
|
|
2824
|
+
this.router.activateManagedSession(task.sessionId, task.target, task.cwd);
|
|
2825
|
+
return task.sessionId;
|
|
2826
|
+
} catch (error) {
|
|
2827
|
+
release?.();
|
|
2828
|
+
throw error;
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
adoptForeignLegacyRoute(sessionId, target) {
|
|
2832
|
+
const cwd = this.router.getSessionCwd(sessionId);
|
|
2833
|
+
if (target.channelName !== this.channelName || cwd === void 0 || !this.isCurrentCwd(cwd)) {
|
|
2834
|
+
this.router.forgetManagedSession(sessionId);
|
|
2835
|
+
return;
|
|
2836
|
+
}
|
|
2837
|
+
const existing = this.getOwner(target);
|
|
2838
|
+
if (existing) {
|
|
2839
|
+
if (existing.tasks.some((task2) => task2.sessionId === sessionId))
|
|
2840
|
+
return;
|
|
2841
|
+
this.router.forgetManagedSession(sessionId);
|
|
2842
|
+
return;
|
|
2843
|
+
}
|
|
2844
|
+
const timestamp = this.nextTimestamp();
|
|
2845
|
+
const task = {
|
|
2846
|
+
name: "default",
|
|
2847
|
+
sessionId,
|
|
2848
|
+
cwd,
|
|
2849
|
+
isolation: "shared",
|
|
2850
|
+
status: "open",
|
|
2851
|
+
target,
|
|
2852
|
+
createdAt: timestamp,
|
|
2853
|
+
updatedAt: timestamp,
|
|
2854
|
+
lastSelectedAt: timestamp
|
|
2855
|
+
};
|
|
2856
|
+
this.commitOwner(this.createOwner(target, task.name, [task]));
|
|
2857
|
+
}
|
|
2858
|
+
async loadTask(task, failureMessage) {
|
|
2859
|
+
try {
|
|
2860
|
+
return (await this.router.loadManagedSession(task.sessionId, task.target, task.cwd)).loaded;
|
|
2861
|
+
} catch (error) {
|
|
2862
|
+
throw new Error(failureMessage, { cause: error });
|
|
2863
|
+
}
|
|
2864
|
+
}
|
|
2865
|
+
async createSession(target, cwd, failureMessage) {
|
|
2866
|
+
try {
|
|
2867
|
+
return await this.router.createManagedSession(target, cwd);
|
|
2868
|
+
} catch (error) {
|
|
2869
|
+
throw new Error(failureMessage, { cause: error });
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
withOwnerLock(input, operation) {
|
|
2873
|
+
const key = JSON.stringify([
|
|
2874
|
+
this.channelName,
|
|
2875
|
+
input.chatId,
|
|
2876
|
+
input.senderId
|
|
2877
|
+
]);
|
|
2878
|
+
const previous = this.ownerOperations.get(key) ?? Promise.resolve();
|
|
2879
|
+
const result = previous.catch(() => void 0).then(operation);
|
|
2880
|
+
const tail = result.then(() => void 0, () => void 0);
|
|
2881
|
+
this.ownerOperations.set(key, tail);
|
|
2882
|
+
return result.finally(() => {
|
|
2883
|
+
if (this.ownerOperations.get(key) === tail) {
|
|
2884
|
+
this.ownerOperations.delete(key);
|
|
2885
|
+
}
|
|
2886
|
+
});
|
|
2887
|
+
}
|
|
2888
|
+
getOwner(input) {
|
|
2889
|
+
const owner = this.registry.owners.find((candidate) => candidate.channelName === this.channelName && candidate.chatId === input.chatId && candidate.senderId === input.senderId);
|
|
2890
|
+
return owner ? this.cloneOwner(owner) : void 0;
|
|
2891
|
+
}
|
|
2892
|
+
createOwner(input, activeTaskName, tasks) {
|
|
2893
|
+
return {
|
|
2894
|
+
channelName: this.channelName,
|
|
2895
|
+
chatId: input.chatId,
|
|
2896
|
+
senderId: input.senderId,
|
|
2897
|
+
activeTaskName,
|
|
2898
|
+
tasks
|
|
2899
|
+
};
|
|
2900
|
+
}
|
|
2901
|
+
target(input) {
|
|
2902
|
+
return {
|
|
2903
|
+
channelName: this.channelName,
|
|
2904
|
+
senderId: input.senderId,
|
|
2905
|
+
chatId: input.chatId,
|
|
2906
|
+
...input.threadId !== void 0 ? { threadId: input.threadId } : {},
|
|
2907
|
+
...input.isGroup !== void 0 ? { isGroup: input.isGroup } : {}
|
|
2908
|
+
};
|
|
2909
|
+
}
|
|
2910
|
+
findTask(owner, name) {
|
|
2911
|
+
const normalized = this.normalizeName(name);
|
|
2912
|
+
return owner.tasks.find((task) => this.normalizeName(task.name) === normalized);
|
|
2913
|
+
}
|
|
2914
|
+
replaceTask(owner, task, activeTaskName) {
|
|
2915
|
+
return {
|
|
2916
|
+
...owner,
|
|
2917
|
+
activeTaskName,
|
|
2918
|
+
tasks: owner.tasks.map((candidate) => this.sameName(candidate.name, task.name) ? task : candidate)
|
|
2919
|
+
};
|
|
2920
|
+
}
|
|
2921
|
+
view(owner, task) {
|
|
2922
|
+
return {
|
|
2923
|
+
name: task.name,
|
|
2924
|
+
status: task.status,
|
|
2925
|
+
isolation: task.isolation,
|
|
2926
|
+
active: task.status === "open" && this.sameName(owner.activeTaskName, task.name)
|
|
2927
|
+
};
|
|
2928
|
+
}
|
|
2929
|
+
selection(owner, task) {
|
|
2930
|
+
return { ...this.view(owner, task), sessionId: task.sessionId };
|
|
2931
|
+
}
|
|
2932
|
+
openTaskCount(owner) {
|
|
2933
|
+
return owner.tasks.filter((task) => task.status === "open").length;
|
|
2934
|
+
}
|
|
2935
|
+
nextTimestamp(owner) {
|
|
2936
|
+
const now = this.now();
|
|
2937
|
+
if (!Number.isSafeInteger(now) || now < 0) {
|
|
2938
|
+
throw new Error("Invalid named-session clock value.");
|
|
2939
|
+
}
|
|
2940
|
+
let timestamp = now;
|
|
2941
|
+
for (const task of owner?.tasks ?? []) {
|
|
2942
|
+
timestamp = Math.max(timestamp, task.createdAt + 1, task.updatedAt + 1, task.lastSelectedAt + 1);
|
|
2943
|
+
}
|
|
2944
|
+
if (!Number.isSafeInteger(timestamp)) {
|
|
2945
|
+
throw new Error("Named-session timestamp limit reached.");
|
|
2946
|
+
}
|
|
2947
|
+
return timestamp;
|
|
2948
|
+
}
|
|
2949
|
+
validateName(name) {
|
|
2950
|
+
if (!TASK_NAME_PATTERN.test(name)) {
|
|
2951
|
+
throw new Error("Task names must be 1-32 ASCII letters, numbers, underscores, or hyphens and start with a letter or number.");
|
|
2952
|
+
}
|
|
2953
|
+
}
|
|
2954
|
+
normalizeName(name) {
|
|
2955
|
+
return name.toLowerCase();
|
|
2956
|
+
}
|
|
2957
|
+
sameName(a, b) {
|
|
2958
|
+
return a !== null && this.normalizeName(a) === this.normalizeName(b);
|
|
2959
|
+
}
|
|
2960
|
+
cloneOwner(owner) {
|
|
2961
|
+
return {
|
|
2962
|
+
...owner,
|
|
2963
|
+
tasks: owner.tasks.map((task) => ({
|
|
2964
|
+
...task,
|
|
2965
|
+
target: { ...task.target }
|
|
2966
|
+
}))
|
|
2967
|
+
};
|
|
2968
|
+
}
|
|
2969
|
+
cloneTaskReference(reference) {
|
|
2970
|
+
return { ...reference, target: { ...reference.target } };
|
|
2971
|
+
}
|
|
2972
|
+
buildTaskIndex(registry) {
|
|
2973
|
+
const index = /* @__PURE__ */ new Map();
|
|
2974
|
+
for (const owner of registry.owners) {
|
|
2975
|
+
for (const task of owner.tasks) {
|
|
2976
|
+
index.set(task.sessionId, {
|
|
2977
|
+
taskName: task.name,
|
|
2978
|
+
status: task.status,
|
|
2979
|
+
target: { ...task.target }
|
|
2980
|
+
});
|
|
2981
|
+
}
|
|
2982
|
+
}
|
|
2983
|
+
return index;
|
|
2984
|
+
}
|
|
2985
|
+
commitOwner(owner) {
|
|
2986
|
+
const owners = this.registry.owners.map((candidate) => candidate.channelName === owner.channelName && candidate.chatId === owner.chatId && candidate.senderId === owner.senderId ? owner : candidate);
|
|
2987
|
+
if (!owners.some((candidate) => candidate.channelName === owner.channelName && candidate.chatId === owner.chatId && candidate.senderId === owner.senderId)) {
|
|
2988
|
+
owners.push(owner);
|
|
2989
|
+
}
|
|
2990
|
+
const next = { version: REGISTRY_VERSION, owners };
|
|
2991
|
+
if (!this.isRegistry(next)) {
|
|
2992
|
+
throw new Error("Invalid named-session registry update.");
|
|
2993
|
+
}
|
|
2994
|
+
const nextTaskBySessionId = this.buildTaskIndex(next);
|
|
2995
|
+
this.writeRegistry(next);
|
|
2996
|
+
this.registry = next;
|
|
2997
|
+
this.taskBySessionId = nextTaskBySessionId;
|
|
2998
|
+
}
|
|
2999
|
+
readRegistry() {
|
|
3000
|
+
if (!existsSync4(this.filePath)) {
|
|
3001
|
+
return { version: REGISTRY_VERSION, owners: [] };
|
|
3002
|
+
}
|
|
3003
|
+
let value;
|
|
3004
|
+
try {
|
|
3005
|
+
value = JSON.parse(readFileSync4(this.filePath, "utf8"));
|
|
3006
|
+
} catch (error) {
|
|
3007
|
+
throw new Error(`Failed to read named-session registry: ${this.filePath}`, {
|
|
3008
|
+
cause: error
|
|
3009
|
+
});
|
|
3010
|
+
}
|
|
3011
|
+
if (!this.isRegistry(value)) {
|
|
3012
|
+
if (this.isRegistry(value, false)) {
|
|
3013
|
+
const stalePath = `${this.filePath}.stale-${randomUUID2()}`;
|
|
3014
|
+
try {
|
|
3015
|
+
renameSync4(this.filePath, stalePath);
|
|
3016
|
+
} catch (error) {
|
|
3017
|
+
throw new Error("Failed to archive stale named-session registry.", {
|
|
3018
|
+
cause: error
|
|
3019
|
+
});
|
|
3020
|
+
}
|
|
3021
|
+
process3.stderr.write("[NamedSessionManager] Archived a stale registry after the channel working directory changed.\n");
|
|
3022
|
+
return { version: REGISTRY_VERSION, owners: [] };
|
|
3023
|
+
}
|
|
3024
|
+
throw new Error(`Invalid named-session registry: ${this.filePath}`);
|
|
3025
|
+
}
|
|
3026
|
+
return value;
|
|
3027
|
+
}
|
|
3028
|
+
writeRegistry(registry) {
|
|
3029
|
+
const dir = dirname4(this.filePath);
|
|
3030
|
+
const tempPath = `${this.filePath}.${process3.pid}-${randomUUID2()}.tmp`;
|
|
3031
|
+
try {
|
|
3032
|
+
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
3033
|
+
try {
|
|
3034
|
+
chmodSync3(dir, 448);
|
|
3035
|
+
} catch {
|
|
3036
|
+
}
|
|
3037
|
+
writeFileSync4(tempPath, JSON.stringify(registry, null, 2), {
|
|
3038
|
+
encoding: "utf8",
|
|
3039
|
+
mode: 384
|
|
3040
|
+
});
|
|
3041
|
+
renameSync4(tempPath, this.filePath);
|
|
3042
|
+
try {
|
|
3043
|
+
chmodSync3(this.filePath, 384);
|
|
3044
|
+
} catch {
|
|
3045
|
+
}
|
|
3046
|
+
} catch (error) {
|
|
3047
|
+
throw new Error("Failed to persist named-session registry.", {
|
|
3048
|
+
cause: error
|
|
3049
|
+
});
|
|
3050
|
+
} finally {
|
|
3051
|
+
try {
|
|
3052
|
+
rmSync2(tempPath, { force: true });
|
|
3053
|
+
} catch {
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
}
|
|
3057
|
+
isRegistry(value, requireCurrentCwd = true) {
|
|
3058
|
+
if (!this.isRecord(value) || value["version"] !== REGISTRY_VERSION) {
|
|
3059
|
+
return false;
|
|
3060
|
+
}
|
|
3061
|
+
const owners = value["owners"];
|
|
3062
|
+
if (!Array.isArray(owners))
|
|
3063
|
+
return false;
|
|
3064
|
+
const ownerKeys = /* @__PURE__ */ new Set();
|
|
3065
|
+
const sessionIds = /* @__PURE__ */ new Set();
|
|
3066
|
+
for (const owner of owners) {
|
|
3067
|
+
if (!this.isOwner(owner, ownerKeys, sessionIds, requireCurrentCwd)) {
|
|
3068
|
+
return false;
|
|
3069
|
+
}
|
|
3070
|
+
}
|
|
3071
|
+
return true;
|
|
3072
|
+
}
|
|
3073
|
+
isOwner(value, ownerKeys, sessionIds, requireCurrentCwd) {
|
|
3074
|
+
if (!this.isRecord(value))
|
|
3075
|
+
return false;
|
|
3076
|
+
const channelName = value["channelName"];
|
|
3077
|
+
const chatId = value["chatId"];
|
|
3078
|
+
const senderId = value["senderId"];
|
|
3079
|
+
const activeTaskName = value["activeTaskName"];
|
|
3080
|
+
const tasks = value["tasks"];
|
|
3081
|
+
if (channelName !== this.channelName || typeof chatId !== "string" || typeof senderId !== "string" || activeTaskName !== null && typeof activeTaskName !== "string" || !Array.isArray(tasks)) {
|
|
3082
|
+
return false;
|
|
3083
|
+
}
|
|
3084
|
+
const ownerKey = JSON.stringify([channelName, chatId, senderId]);
|
|
3085
|
+
if (ownerKeys.has(ownerKey))
|
|
3086
|
+
return false;
|
|
3087
|
+
ownerKeys.add(ownerKey);
|
|
3088
|
+
const names = /* @__PURE__ */ new Set();
|
|
3089
|
+
let openTasks = 0;
|
|
3090
|
+
let activeFound = activeTaskName === null;
|
|
3091
|
+
for (const task of tasks) {
|
|
3092
|
+
if (!this.isTask(task, channelName, chatId, senderId, requireCurrentCwd)) {
|
|
3093
|
+
return false;
|
|
3094
|
+
}
|
|
3095
|
+
const normalized = this.normalizeName(task.name);
|
|
3096
|
+
if (names.has(normalized) || sessionIds.has(task.sessionId))
|
|
3097
|
+
return false;
|
|
3098
|
+
names.add(normalized);
|
|
3099
|
+
sessionIds.add(task.sessionId);
|
|
3100
|
+
if (task.status === "open")
|
|
3101
|
+
openTasks++;
|
|
3102
|
+
if (activeTaskName !== null && task.status === "open" && this.sameName(activeTaskName, task.name)) {
|
|
3103
|
+
activeFound = true;
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
return openTasks <= MAX_OPEN_TASKS && activeFound;
|
|
3107
|
+
}
|
|
3108
|
+
isTask(value, channelName, chatId, senderId, requireCurrentCwd) {
|
|
3109
|
+
if (!this.isRecord(value) || !this.isRecord(value["target"]))
|
|
3110
|
+
return false;
|
|
3111
|
+
const target = value["target"];
|
|
3112
|
+
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");
|
|
3113
|
+
}
|
|
3114
|
+
isCurrentCwd(cwd) {
|
|
3115
|
+
return canonicalizeWorkspacePath(cwd) === this.canonicalCwd;
|
|
3116
|
+
}
|
|
3117
|
+
isTimestamp(value) {
|
|
3118
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
3119
|
+
}
|
|
3120
|
+
isRecord(value) {
|
|
3121
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3122
|
+
}
|
|
3123
|
+
};
|
|
3124
|
+
|
|
2299
3125
|
// packages/channels/base/dist/ChannelLoopScheduler.js
|
|
2300
3126
|
init_esbuild_shims();
|
|
2301
3127
|
var MAX_RESULT_PREVIEW_LENGTH = 500;
|
|
@@ -2990,6 +3816,7 @@ var ChannelBase = class {
|
|
|
2990
3816
|
groupPairingNotified = /* @__PURE__ */ new Map();
|
|
2991
3817
|
loopController;
|
|
2992
3818
|
observedContacts;
|
|
3819
|
+
namedSessions;
|
|
2993
3820
|
observedContactEnvelopes = /* @__PURE__ */ new WeakSet();
|
|
2994
3821
|
instructedSessions = /* @__PURE__ */ new Set();
|
|
2995
3822
|
unattendedMemorySessions = /* @__PURE__ */ new Set();
|
|
@@ -2998,6 +3825,9 @@ var ChannelBase = class {
|
|
|
2998
3825
|
commands = /* @__PURE__ */ new Map();
|
|
2999
3826
|
/** Per-session promise chain to serialize prompt + send (followup mode). */
|
|
3000
3827
|
sessionQueues = /* @__PURE__ */ new Map();
|
|
3828
|
+
queuedTurns = /* @__PURE__ */ new Map();
|
|
3829
|
+
namedTurnBindings = /* @__PURE__ */ new WeakMap();
|
|
3830
|
+
inboundErrorSourceLabels = /* @__PURE__ */ new WeakMap();
|
|
3001
3831
|
registerBridgeEvents;
|
|
3002
3832
|
bridgeRecovery;
|
|
3003
3833
|
/**
|
|
@@ -3066,15 +3896,43 @@ var ChannelBase = class {
|
|
|
3066
3896
|
this.onToolCall(chatId, event);
|
|
3067
3897
|
}
|
|
3068
3898
|
async dispatchBackgroundResponse(sessionId, text) {
|
|
3069
|
-
|
|
3899
|
+
let target = this.router.getTarget(sessionId);
|
|
3070
3900
|
if (!target || target.channelName !== this.name || text.trim().length === 0) {
|
|
3071
3901
|
return;
|
|
3072
3902
|
}
|
|
3903
|
+
let sourceLabel;
|
|
3904
|
+
if (this.namedSessions) {
|
|
3905
|
+
const presentation = await this.namedSessions.resolvePresentation(sessionId);
|
|
3906
|
+
const currentTarget = this.router.getTarget(sessionId);
|
|
3907
|
+
const currentPresentation = this.namedSessions.presentation(sessionId);
|
|
3908
|
+
if (!presentation || presentation.status !== "open" || !currentTarget || !this.router.isSessionLive(sessionId) || !currentPresentation || currentPresentation.status !== "open" || currentPresentation.taskName !== presentation.taskName || !this.sameTaskOwner(target, currentTarget) || !this.sameTaskOwner(currentTarget, currentPresentation.target)) {
|
|
3909
|
+
throw new Error("Named background response ownership is unavailable.");
|
|
3910
|
+
}
|
|
3911
|
+
target = currentTarget;
|
|
3912
|
+
sourceLabel = this.createSourceLabel(presentation, target);
|
|
3913
|
+
}
|
|
3073
3914
|
if (this.supportsProactiveSend() && this.supportsProactiveTarget(target)) {
|
|
3074
|
-
|
|
3915
|
+
if (sourceLabel) {
|
|
3916
|
+
await this.pushProactive(target, text, sourceLabel);
|
|
3917
|
+
} else {
|
|
3918
|
+
await this.pushProactive(target, text);
|
|
3919
|
+
}
|
|
3075
3920
|
return;
|
|
3076
3921
|
}
|
|
3077
|
-
|
|
3922
|
+
if (sourceLabel) {
|
|
3923
|
+
await this.deliverBackgroundReply(target.chatId, text, sessionId, sourceLabel);
|
|
3924
|
+
} else {
|
|
3925
|
+
await this.deliverBackgroundReply(target.chatId, text, sessionId);
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3928
|
+
/**
|
|
3929
|
+
* Fallback delivery of a background response when proactive send is
|
|
3930
|
+
* unavailable. Adapters whose turn replies bypass sendResponseMessage (to
|
|
3931
|
+
* stay out of turn-scoped streaming state, for example) override only this
|
|
3932
|
+
* step instead of re-implementing the whole dispatch flow.
|
|
3933
|
+
*/
|
|
3934
|
+
async deliverBackgroundReply(chatId, text, sessionId, sourceLabel) {
|
|
3935
|
+
await this.sendResponseMessage(chatId, text, sessionId, sourceLabel);
|
|
3078
3936
|
}
|
|
3079
3937
|
async dispatchPermissionRequest(event) {
|
|
3080
3938
|
const target = this.permissionTargetForEvent(event);
|
|
@@ -3089,12 +3947,59 @@ var ChannelBase = class {
|
|
|
3089
3947
|
}
|
|
3090
3948
|
return;
|
|
3091
3949
|
}
|
|
3950
|
+
let sourceLabel;
|
|
3951
|
+
let taskName;
|
|
3952
|
+
if (this.namedSessions) {
|
|
3953
|
+
let presentation;
|
|
3954
|
+
try {
|
|
3955
|
+
presentation = await this.namedSessions.resolvePresentation(event.sessionId);
|
|
3956
|
+
} catch (err) {
|
|
3957
|
+
try {
|
|
3958
|
+
await this.bridge.respondToPermission?.(event.requestId, {
|
|
3959
|
+
outcome: { outcome: "cancelled" }
|
|
3960
|
+
});
|
|
3961
|
+
} catch (respondErr) {
|
|
3962
|
+
process.stderr.write(`[${this.name}] permission cancellation failed for request ${sanitizeLogText(event.requestId, 128)}: ${this.lifecycleError(respondErr)}
|
|
3963
|
+
`);
|
|
3964
|
+
}
|
|
3965
|
+
throw err;
|
|
3966
|
+
}
|
|
3967
|
+
const currentTarget = this.permissionTargetForEvent(event);
|
|
3968
|
+
const currentPresentation = this.namedSessions.presentation(event.sessionId);
|
|
3969
|
+
if (!presentation || presentation.status !== "open" || !currentTarget || !this.router.isSessionLive(event.sessionId) || !currentPresentation || currentPresentation.status !== "open" || currentPresentation.taskName !== presentation.taskName || !this.sameTaskOwner(target, currentTarget) || !this.sameTaskOwner(currentTarget, currentPresentation.target)) {
|
|
3970
|
+
try {
|
|
3971
|
+
await this.bridge.respondToPermission?.(event.requestId, {
|
|
3972
|
+
outcome: { outcome: "cancelled" }
|
|
3973
|
+
});
|
|
3974
|
+
} catch (respondErr) {
|
|
3975
|
+
process.stderr.write(`[${this.name}] permission cancellation failed for request ${sanitizeLogText(event.requestId, 128)}: ${this.lifecycleError(respondErr)}
|
|
3976
|
+
`);
|
|
3977
|
+
}
|
|
3978
|
+
return;
|
|
3979
|
+
}
|
|
3980
|
+
const active = this.activePrompts.get(event.sessionId);
|
|
3981
|
+
sourceLabel = active ? active.sourceLabel : this.createSourceLabel(presentation, target);
|
|
3982
|
+
if (!sourceLabel) {
|
|
3983
|
+
try {
|
|
3984
|
+
await this.bridge.respondToPermission?.(event.requestId, {
|
|
3985
|
+
outcome: { outcome: "cancelled" }
|
|
3986
|
+
});
|
|
3987
|
+
} catch (respondErr) {
|
|
3988
|
+
process.stderr.write(`[${this.name}] permission cancellation failed for request ${sanitizeLogText(event.requestId, 128)}: ${this.lifecycleError(respondErr)}
|
|
3989
|
+
`);
|
|
3990
|
+
}
|
|
3991
|
+
return;
|
|
3992
|
+
}
|
|
3993
|
+
taskName = presentation.taskName;
|
|
3994
|
+
}
|
|
3092
3995
|
this.removePendingPermission(event.requestId);
|
|
3093
3996
|
const pending = {
|
|
3094
3997
|
requestId: event.requestId,
|
|
3095
3998
|
sessionId: event.sessionId,
|
|
3096
3999
|
target,
|
|
3097
4000
|
request: event.request,
|
|
4001
|
+
...sourceLabel ? { sourceLabel } : {},
|
|
4002
|
+
...taskName ? { taskName } : {},
|
|
3098
4003
|
settlementListeners: /* @__PURE__ */ new Set()
|
|
3099
4004
|
};
|
|
3100
4005
|
this.pendingPermissions.set(event.requestId, pending);
|
|
@@ -3109,9 +4014,9 @@ var ChannelBase = class {
|
|
|
3109
4014
|
}
|
|
3110
4015
|
const text = this.formatPermissionRequest(pending);
|
|
3111
4016
|
if (target.threadId !== void 0 && this.supportsProactiveSend() && this.supportsProactiveTarget(target)) {
|
|
3112
|
-
await this.pushProactive(target, text);
|
|
4017
|
+
await this.pushProactive(target, text, pending.sourceLabel);
|
|
3113
4018
|
} else {
|
|
3114
|
-
await this.sendThreadMessage(target.chatId, target.threadId, text);
|
|
4019
|
+
await this.sendThreadMessage(target.chatId, target.threadId, text, pending.sourceLabel);
|
|
3115
4020
|
}
|
|
3116
4021
|
} catch (err) {
|
|
3117
4022
|
this.removePendingPermission(event.requestId, "cancelled");
|
|
@@ -3141,6 +4046,7 @@ var ChannelBase = class {
|
|
|
3141
4046
|
runId: active.runId,
|
|
3142
4047
|
owner: active.owner,
|
|
3143
4048
|
target: pending.target,
|
|
4049
|
+
...pending.sourceLabel ? { sourceLabel: pending.sourceLabel } : {},
|
|
3144
4050
|
...precedingSegment ? { precedingSegmentId: precedingSegment.segmentId } : {},
|
|
3145
4051
|
questions,
|
|
3146
4052
|
submitOptionId,
|
|
@@ -3295,6 +4201,21 @@ var ChannelBase = class {
|
|
|
3295
4201
|
this.dmGate = new DmGate(config.dmPolicy);
|
|
3296
4202
|
this.gate = new SenderGate(config.senderPolicy, config.allowedUsers, pairingStore);
|
|
3297
4203
|
this.router = options?.router || new SessionRouter(bridge, config.cwd, config.sessionScope);
|
|
4204
|
+
if (config.multiSession) {
|
|
4205
|
+
if (config.sessionScope !== "user") {
|
|
4206
|
+
throw new Error(`Channel "${name}" requires sessionScope "user" when multiSession is enabled.`);
|
|
4207
|
+
}
|
|
4208
|
+
if (!options?.stateDir) {
|
|
4209
|
+
throw new Error(`Channel "${name}" multiSession is available only in daemon-managed mode.`);
|
|
4210
|
+
}
|
|
4211
|
+
this.namedSessions = new NamedSessionManager({
|
|
4212
|
+
channelName: name,
|
|
4213
|
+
cwd: config.cwd,
|
|
4214
|
+
filePath: join5(options.stateDir, "named-sessions.json"),
|
|
4215
|
+
router: this.router,
|
|
4216
|
+
isBusy: /* @__PURE__ */ __name((sessionId) => this.isNamedSessionBusy(sessionId), "isBusy")
|
|
4217
|
+
});
|
|
4218
|
+
}
|
|
3298
4219
|
this.registerSharedCommands();
|
|
3299
4220
|
if (this.loopController) {
|
|
3300
4221
|
bridge.registerChannelLoopToolHandler?.(this.channelLoopToolHandler);
|
|
@@ -3309,8 +4230,8 @@ var ChannelBase = class {
|
|
|
3309
4230
|
* on a specific issue/PR. The default falls through to sendMessage(chatId,
|
|
3310
4231
|
* text), ignoring threadId — existing IM adapters are behaviorally unchanged.
|
|
3311
4232
|
*/
|
|
3312
|
-
async sendThreadMessage(chatId, _threadId, text) {
|
|
3313
|
-
await this.sendMessage(chatId, text);
|
|
4233
|
+
async sendThreadMessage(chatId, _threadId, text, sourceLabel) {
|
|
4234
|
+
await this.sendMessage(chatId, this.formatAttributedText(text, sourceLabel));
|
|
3314
4235
|
}
|
|
3315
4236
|
/**
|
|
3316
4237
|
* Adapter hook for task lifecycle events — the canonical way to track task
|
|
@@ -3452,13 +4373,14 @@ var ChannelBase = class {
|
|
|
3452
4373
|
segmentId,
|
|
3453
4374
|
owner: active.owner,
|
|
3454
4375
|
target: resolvedTarget,
|
|
4376
|
+
...active.sourceLabel ? { sourceLabel: active.sourceLabel } : {},
|
|
3455
4377
|
...active.messageId ? { messageId: active.messageId } : {}
|
|
3456
4378
|
};
|
|
3457
4379
|
}
|
|
3458
4380
|
ensureOutputSegment(sessionId, active) {
|
|
3459
4381
|
if (!active.owner)
|
|
3460
4382
|
return void 0;
|
|
3461
|
-
const segmentId = active.activeSegmentId ??
|
|
4383
|
+
const segmentId = active.activeSegmentId ?? randomUUID3();
|
|
3462
4384
|
const context = this.outputSegmentContext(sessionId, active, segmentId);
|
|
3463
4385
|
if (context)
|
|
3464
4386
|
active.activeSegmentId = segmentId;
|
|
@@ -3493,11 +4415,11 @@ var ChannelBase = class {
|
|
|
3493
4415
|
supportsProactiveWebhookTarget(target) {
|
|
3494
4416
|
return this.supportsProactiveTarget(target);
|
|
3495
4417
|
}
|
|
3496
|
-
async pushProactive(target, text) {
|
|
4418
|
+
async pushProactive(target, text, sourceLabel) {
|
|
3497
4419
|
if (target.threadId) {
|
|
3498
4420
|
throw new Error("Channel does not support proactive loop messages for threaded targets.");
|
|
3499
4421
|
}
|
|
3500
|
-
await this.sendThreadMessage(target.chatId, target.threadId, text);
|
|
4422
|
+
await this.sendThreadMessage(target.chatId, target.threadId, text, sourceLabel);
|
|
3501
4423
|
}
|
|
3502
4424
|
async pushProactiveDelivery(target, text) {
|
|
3503
4425
|
try {
|
|
@@ -3687,8 +4609,11 @@ var ChannelBase = class {
|
|
|
3687
4609
|
imageBase64: void 0,
|
|
3688
4610
|
imageMimeType: void 0
|
|
3689
4611
|
};
|
|
4612
|
+
if (this.namedSessions) {
|
|
4613
|
+
this.bindNamedTurn(syntheticEnvelope, sessionId);
|
|
4614
|
+
}
|
|
3690
4615
|
this.markPreflighted(syntheticEnvelope);
|
|
3691
|
-
this.
|
|
4616
|
+
void this.processPreflightedInbound(syntheticEnvelope).catch((err) => {
|
|
3692
4617
|
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)}
|
|
3693
4618
|
`);
|
|
3694
4619
|
});
|
|
@@ -3784,7 +4709,7 @@ ${promptText}` : promptText;
|
|
|
3784
4709
|
doneResolve = resolve3;
|
|
3785
4710
|
});
|
|
3786
4711
|
const promptState = {
|
|
3787
|
-
runId:
|
|
4712
|
+
runId: randomUUID3(),
|
|
3788
4713
|
cancelled: false,
|
|
3789
4714
|
done,
|
|
3790
4715
|
resolve: doneResolve,
|
|
@@ -4000,7 +4925,7 @@ ${promptText}` : promptText;
|
|
|
4000
4925
|
doneResolve = resolve3;
|
|
4001
4926
|
});
|
|
4002
4927
|
const promptState = {
|
|
4003
|
-
runId:
|
|
4928
|
+
runId: randomUUID3(),
|
|
4004
4929
|
cancelled: false,
|
|
4005
4930
|
done,
|
|
4006
4931
|
resolve: doneResolve,
|
|
@@ -4333,11 +5258,11 @@ ${promptText}` : promptText;
|
|
|
4333
5258
|
*/
|
|
4334
5259
|
onResponseBoundary(_chatId, _sessionId) {
|
|
4335
5260
|
}
|
|
4336
|
-
async sendResponseMessage(chatId, text, sessionId) {
|
|
5261
|
+
async sendResponseMessage(chatId, text, sessionId, sourceLabel) {
|
|
4337
5262
|
const active = this.activePrompts.get(sessionId);
|
|
4338
5263
|
const target = this.router.getTarget(sessionId);
|
|
4339
5264
|
const threadId = active?.threadId ?? target?.threadId;
|
|
4340
|
-
await this.sendThreadMessage(chatId, threadId, text);
|
|
5265
|
+
await this.sendThreadMessage(chatId, threadId, text, sourceLabel ?? active?.sourceLabel);
|
|
4341
5266
|
}
|
|
4342
5267
|
/**
|
|
4343
5268
|
* Adapter hook for delivery-only metadata. The response delivery path can read
|
|
@@ -4352,6 +5277,12 @@ ${promptText}` : promptText;
|
|
|
4352
5277
|
getResponseMetadata(sessionId) {
|
|
4353
5278
|
return this.activePrompts.get(sessionId)?.metadata;
|
|
4354
5279
|
}
|
|
5280
|
+
getResponseSourceLabel(sessionId) {
|
|
5281
|
+
return this.activePrompts.get(sessionId)?.sourceLabel;
|
|
5282
|
+
}
|
|
5283
|
+
getInboundErrorSourceLabel(envelope) {
|
|
5284
|
+
return this.inboundErrorSourceLabels.get(envelope);
|
|
5285
|
+
}
|
|
4355
5286
|
/**
|
|
4356
5287
|
* Returns the active prompt's response thread while it remains available for
|
|
4357
5288
|
* adapter delivery. Falls back to the session target after prompt cleanup.
|
|
@@ -4364,8 +5295,8 @@ ${promptText}` : promptText;
|
|
|
4364
5295
|
* Override to customize delivery (e.g., finalize an AI card).
|
|
4365
5296
|
* Default: sends the full response text.
|
|
4366
5297
|
*/
|
|
4367
|
-
async onResponseComplete(chatId, fullText, sessionId,
|
|
4368
|
-
await this.sendResponseMessage(chatId, fullText, sessionId);
|
|
5298
|
+
async onResponseComplete(chatId, fullText, sessionId, segment) {
|
|
5299
|
+
await this.sendResponseMessage(chatId, fullText, sessionId, segment?.sourceLabel);
|
|
4369
5300
|
}
|
|
4370
5301
|
/**
|
|
4371
5302
|
* Register a slash command handler. Subclasses can call this to add
|
|
@@ -4381,7 +5312,7 @@ ${promptText}` : promptText;
|
|
|
4381
5312
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can cancel requests in this shared session.");
|
|
4382
5313
|
return true;
|
|
4383
5314
|
}
|
|
4384
|
-
const activeSessionId = this.findActiveSessionId(envelope);
|
|
5315
|
+
const activeSessionId = this.namedSessions ? await this.findNamedActiveSessionId(envelope) : this.findActiveSessionId(envelope);
|
|
4385
5316
|
if (!activeSessionId) {
|
|
4386
5317
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No request is currently running.");
|
|
4387
5318
|
return true;
|
|
@@ -4470,7 +5401,7 @@ ${promptText}` : promptText;
|
|
|
4470
5401
|
}
|
|
4471
5402
|
return "resolved_outside_presenter";
|
|
4472
5403
|
}
|
|
4473
|
-
pendingPermissionForEnvelope(envelope, args) {
|
|
5404
|
+
pendingPermissionForEnvelope(envelope, args, selectedSessionId) {
|
|
4474
5405
|
const trimmed = args.trim();
|
|
4475
5406
|
if (trimmed) {
|
|
4476
5407
|
const explicit = this.pendingPermissions.get(trimmed);
|
|
@@ -4483,7 +5414,7 @@ ${promptText}` : promptText;
|
|
|
4483
5414
|
if (requestIds.length === 0) {
|
|
4484
5415
|
return { kind: "none", explicit: false };
|
|
4485
5416
|
}
|
|
4486
|
-
const matching = requestIds.map((id) => this.pendingPermissions.get(id)).filter((pending) => pending !== void 0 && this.canEnvelopeAnswerPendingPermission(envelope, pending));
|
|
5417
|
+
const matching = requestIds.map((id) => this.pendingPermissions.get(id)).filter((pending) => pending !== void 0 && (selectedSessionId === void 0 || pending.sessionId === selectedSessionId) && this.canEnvelopeAnswerPendingPermission(envelope, pending));
|
|
4487
5418
|
if (matching.length === 0) {
|
|
4488
5419
|
return { kind: "none", explicit: false };
|
|
4489
5420
|
}
|
|
@@ -4502,6 +5433,23 @@ ${promptText}` : promptText;
|
|
|
4502
5433
|
const { toolCall } = pending.request;
|
|
4503
5434
|
const title = sanitizeQuotedText(toolCall.title || "Tool use", 160);
|
|
4504
5435
|
const alwaysOption = this.approvalAlwaysOption(pending);
|
|
5436
|
+
if (pending.taskName) {
|
|
5437
|
+
const replies2 = [
|
|
5438
|
+
`/approve ${pending.requestId} allow once`,
|
|
5439
|
+
...alwaysOption ? [`/approve-always ${pending.requestId} ${alwaysOption.label}`] : [],
|
|
5440
|
+
`/deny ${pending.requestId} deny`
|
|
5441
|
+
];
|
|
5442
|
+
return [
|
|
5443
|
+
"Permission required to run a tool",
|
|
5444
|
+
`Request: ${pending.requestId}`,
|
|
5445
|
+
"",
|
|
5446
|
+
"Command:",
|
|
5447
|
+
title,
|
|
5448
|
+
"",
|
|
5449
|
+
"Reply with:",
|
|
5450
|
+
...replies2
|
|
5451
|
+
].join("\n");
|
|
5452
|
+
}
|
|
4505
5453
|
const replies = [
|
|
4506
5454
|
"/approve allow once",
|
|
4507
5455
|
...alwaysOption ? [`/approve-always ${alwaysOption.label}`] : [],
|
|
@@ -4557,28 +5505,40 @@ ${promptText}` : promptText;
|
|
|
4557
5505
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can answer permission requests in this shared session.");
|
|
4558
5506
|
return true;
|
|
4559
5507
|
}
|
|
4560
|
-
const
|
|
5508
|
+
const namedSessions = this.namedSessions;
|
|
5509
|
+
const bareNamedCommand = namedSessions !== void 0 && args.trim() === "";
|
|
5510
|
+
let selectedTask;
|
|
5511
|
+
if (bareNamedCommand) {
|
|
5512
|
+
try {
|
|
5513
|
+
selectedTask = await namedSessions.current(this.namedSessionOwner(envelope));
|
|
5514
|
+
} catch (error) {
|
|
5515
|
+
await this.sendNamedSessionError(envelope, error);
|
|
5516
|
+
return true;
|
|
5517
|
+
}
|
|
5518
|
+
}
|
|
5519
|
+
const lookup = this.pendingPermissionForEnvelope(envelope, args, bareNamedCommand ? selectedTask?.sessionId ?? null : void 0);
|
|
4561
5520
|
if (lookup.kind === "ambiguous") {
|
|
4562
5521
|
const requestList = lookup.requestIds.slice(0, 6).map((id) => {
|
|
4563
5522
|
const pending2 = this.pendingPermissions.get(id);
|
|
4564
5523
|
const title = pending2 ? `: ${sanitizeQuotedText(pending2.request.toolCall.title || "Tool use", 160)}` : "";
|
|
4565
|
-
|
|
5524
|
+
const task = pending2?.taskName ? `Task ${pending2.taskName} \u2014 ` : "";
|
|
5525
|
+
return `- ${task}${sanitizeQuotedText(id, 128)}${title}`;
|
|
4566
5526
|
}).join("\n");
|
|
4567
5527
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Multiple permission requests are pending for this chat. Reply with /${decision} <request-id>.
|
|
4568
5528
|
${requestList}`);
|
|
4569
5529
|
return true;
|
|
4570
5530
|
}
|
|
4571
5531
|
if (lookup.kind === "none") {
|
|
4572
|
-
await this.sendThreadMessage(envelope.chatId, envelope.threadId, lookup.explicit ? "No pending permission request with that id for this chat." : "No pending permission request for this chat.");
|
|
5532
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, lookup.explicit ? "No pending permission request with that id for this chat." : selectedTask ? `No pending permission request for selected task "${selectedTask.name}". Use an explicit request ID to answer another task.` : this.namedSessions ? "No task is currently selected. Use an explicit request ID to answer a named task." : "No pending permission request for this chat.");
|
|
4573
5533
|
return true;
|
|
4574
5534
|
}
|
|
5535
|
+
const { pending } = lookup;
|
|
4575
5536
|
if (!this.bridge.respondToPermission) {
|
|
4576
|
-
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Permission relay is not available for this session.");
|
|
5537
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Permission relay is not available for this session.", pending.sourceLabel);
|
|
4577
5538
|
return true;
|
|
4578
5539
|
}
|
|
4579
|
-
const { pending } = lookup;
|
|
4580
5540
|
if (pending.userInputPresented && decision !== "deny") {
|
|
4581
|
-
await this.
|
|
5541
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Submit this question through its interactive card, or use /deny [request-id] to cancel it.", pending.sourceLabel);
|
|
4582
5542
|
return true;
|
|
4583
5543
|
}
|
|
4584
5544
|
const response = (() => {
|
|
@@ -4589,7 +5549,7 @@ ${requestList}`);
|
|
|
4589
5549
|
return optionId ? { outcome: { outcome: "selected", optionId } } : void 0;
|
|
4590
5550
|
})();
|
|
4591
5551
|
if (!response) {
|
|
4592
|
-
await this.sendThreadMessage(envelope.chatId, envelope.threadId, decision === "approve-always" ? "This permission request has no always-allow option." : "This permission request has no approvable option.");
|
|
5552
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, decision === "approve-always" ? "This permission request has no always-allow option." : "This permission request has no approvable option.", pending.sourceLabel);
|
|
4593
5553
|
return true;
|
|
4594
5554
|
}
|
|
4595
5555
|
let accepted;
|
|
@@ -4599,17 +5559,228 @@ ${requestList}`);
|
|
|
4599
5559
|
this.removePendingPermission(pending.requestId);
|
|
4600
5560
|
process.stderr.write(`[${this.name}] permission response failed for request ${sanitizeLogText(pending.requestId, 128)}: ${this.lifecycleError(err)}
|
|
4601
5561
|
`);
|
|
4602
|
-
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Failed to answer the permission request.");
|
|
5562
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Failed to answer the permission request.", pending.sourceLabel);
|
|
4603
5563
|
return true;
|
|
4604
5564
|
}
|
|
4605
5565
|
this.removePendingPermission(pending.requestId);
|
|
4606
|
-
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.");
|
|
5566
|
+
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.", pending.sourceLabel);
|
|
5567
|
+
return true;
|
|
5568
|
+
}
|
|
5569
|
+
namedSessionOwner(envelope) {
|
|
5570
|
+
return {
|
|
5571
|
+
senderId: envelope.senderId,
|
|
5572
|
+
chatId: envelope.chatId,
|
|
5573
|
+
...envelope.threadId !== void 0 ? { threadId: envelope.threadId } : {},
|
|
5574
|
+
...envelope.isGroup !== void 0 ? { isGroup: envelope.isGroup } : {}
|
|
5575
|
+
};
|
|
5576
|
+
}
|
|
5577
|
+
async currentSessionId(envelope) {
|
|
5578
|
+
if (this.namedSessions) {
|
|
5579
|
+
return (await this.namedSessions.current(this.namedSessionOwner(envelope)))?.sessionId;
|
|
5580
|
+
}
|
|
5581
|
+
return this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
5582
|
+
}
|
|
5583
|
+
reserveQueuedTurn(sessionId) {
|
|
5584
|
+
this.queuedTurns.set(sessionId, (this.queuedTurns.get(sessionId) ?? 0) + 1);
|
|
5585
|
+
}
|
|
5586
|
+
releaseQueuedTurn(sessionId) {
|
|
5587
|
+
const remaining = (this.queuedTurns.get(sessionId) ?? 1) - 1;
|
|
5588
|
+
if (remaining === 0) {
|
|
5589
|
+
this.queuedTurns.delete(sessionId);
|
|
5590
|
+
} else {
|
|
5591
|
+
this.queuedTurns.set(sessionId, remaining);
|
|
5592
|
+
}
|
|
5593
|
+
}
|
|
5594
|
+
bindNamedTurn(envelope, sessionId) {
|
|
5595
|
+
const binding = {
|
|
5596
|
+
sessionId,
|
|
5597
|
+
generation: this.sessionGenerations.get(sessionId) ?? 0,
|
|
5598
|
+
claimed: false,
|
|
5599
|
+
released: false
|
|
5600
|
+
};
|
|
5601
|
+
this.namedTurnBindings.set(envelope, binding);
|
|
5602
|
+
this.reserveQueuedTurn(sessionId);
|
|
5603
|
+
return binding;
|
|
5604
|
+
}
|
|
5605
|
+
releaseNamedTurnBinding(binding) {
|
|
5606
|
+
if (binding.sessionId === null || binding.claimed || binding.released) {
|
|
5607
|
+
return;
|
|
5608
|
+
}
|
|
5609
|
+
binding.released = true;
|
|
5610
|
+
this.releaseQueuedTurn(binding.sessionId);
|
|
5611
|
+
}
|
|
5612
|
+
finishNamedTurnBinding(envelope) {
|
|
5613
|
+
const binding = this.namedTurnBindings.get(envelope);
|
|
5614
|
+
if (!binding)
|
|
5615
|
+
return;
|
|
5616
|
+
this.releaseNamedTurnBinding(binding);
|
|
5617
|
+
this.namedTurnBindings.delete(envelope);
|
|
5618
|
+
}
|
|
5619
|
+
bypassesNamedTurnBinding(envelope) {
|
|
5620
|
+
const parsed = this.parseCommand(envelope.text);
|
|
5621
|
+
if (parsed && this.commands.has(parsed.command))
|
|
5622
|
+
return true;
|
|
5623
|
+
const bangText = envelope.text.trimStart();
|
|
5624
|
+
return bangText.startsWith("!") && (envelope.isGroup || this.isSharedSession(envelope));
|
|
5625
|
+
}
|
|
5626
|
+
async prepareNamedTurnBinding(envelope) {
|
|
5627
|
+
const namedSessions = this.namedSessions;
|
|
5628
|
+
if (!namedSessions || this.namedTurnBindings.has(envelope))
|
|
5629
|
+
return true;
|
|
5630
|
+
if (this.bypassesNamedTurnBinding(envelope))
|
|
5631
|
+
return true;
|
|
5632
|
+
try {
|
|
5633
|
+
const sessionId = await namedSessions.resolve(this.namedSessionOwner(envelope), (resolvedSessionId) => {
|
|
5634
|
+
const binding = this.bindNamedTurn(envelope, resolvedSessionId);
|
|
5635
|
+
return () => this.releaseNamedTurnBinding(binding);
|
|
5636
|
+
});
|
|
5637
|
+
if (!sessionId && !this.namedTurnBindings.has(envelope)) {
|
|
5638
|
+
this.namedTurnBindings.set(envelope, {
|
|
5639
|
+
sessionId: null,
|
|
5640
|
+
generation: 0,
|
|
5641
|
+
claimed: false,
|
|
5642
|
+
released: true
|
|
5643
|
+
});
|
|
5644
|
+
}
|
|
5645
|
+
return true;
|
|
5646
|
+
} catch (error) {
|
|
5647
|
+
this.finishNamedTurnBinding(envelope);
|
|
5648
|
+
await this.sendNamedSessionError(envelope, error);
|
|
5649
|
+
return false;
|
|
5650
|
+
}
|
|
5651
|
+
}
|
|
5652
|
+
isNamedSessionBusy(sessionId) {
|
|
5653
|
+
if ((this.queuedTurns.get(sessionId) ?? 0) > 0)
|
|
5654
|
+
return true;
|
|
5655
|
+
if (this.activePrompts.has(sessionId))
|
|
5656
|
+
return true;
|
|
5657
|
+
for (const permission of this.pendingPermissions.values()) {
|
|
5658
|
+
if (permission.sessionId === sessionId)
|
|
5659
|
+
return true;
|
|
5660
|
+
}
|
|
5661
|
+
try {
|
|
5662
|
+
return this.bridge.listSessions?.().some((session) => session.sessionId === sessionId && session.hasActivePrompt) ?? false;
|
|
5663
|
+
} catch {
|
|
5664
|
+
return true;
|
|
5665
|
+
}
|
|
5666
|
+
}
|
|
5667
|
+
async sendNamedSessionError(envelope, error) {
|
|
5668
|
+
const message = error instanceof Error ? sanitizeDisplayText(error.message, 500) : "Named-session operation failed.";
|
|
5669
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, message || "Named-session operation failed.");
|
|
5670
|
+
}
|
|
5671
|
+
async handleNamedSessionsCommand(envelope, args) {
|
|
5672
|
+
const namedSessions = this.namedSessions;
|
|
5673
|
+
if (!namedSessions)
|
|
5674
|
+
return false;
|
|
5675
|
+
const normalized = args.trim().toLowerCase();
|
|
5676
|
+
if (normalized !== "" && normalized !== "all") {
|
|
5677
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Usage: /sessions [all]");
|
|
5678
|
+
return true;
|
|
5679
|
+
}
|
|
5680
|
+
try {
|
|
5681
|
+
const tasks = await namedSessions.list(this.namedSessionOwner(envelope), normalized === "all");
|
|
5682
|
+
if (tasks.length === 0) {
|
|
5683
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, normalized === "all" ? "No named tasks." : "No open named tasks.");
|
|
5684
|
+
return true;
|
|
5685
|
+
}
|
|
5686
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, [
|
|
5687
|
+
normalized === "all" ? "Tasks:" : "Open tasks:",
|
|
5688
|
+
...tasks.map((task) => `${task.active ? "*" : "-"} ${task.name} (${task.status}, ${task.isolation})`)
|
|
5689
|
+
].join("\n"));
|
|
5690
|
+
} catch (error) {
|
|
5691
|
+
await this.sendNamedSessionError(envelope, error);
|
|
5692
|
+
}
|
|
5693
|
+
return true;
|
|
5694
|
+
}
|
|
5695
|
+
async handleNamedSessionCommand(envelope, args) {
|
|
5696
|
+
const namedSessions = this.namedSessions;
|
|
5697
|
+
if (!namedSessions)
|
|
5698
|
+
return false;
|
|
5699
|
+
const parts = args.trim().split(/\s+/u).filter(Boolean);
|
|
5700
|
+
const subcommand = parts.shift()?.toLowerCase() ?? "";
|
|
5701
|
+
const owner = this.namedSessionOwner(envelope);
|
|
5702
|
+
try {
|
|
5703
|
+
switch (subcommand) {
|
|
5704
|
+
case "current": {
|
|
5705
|
+
if (parts.length > 0)
|
|
5706
|
+
break;
|
|
5707
|
+
const current = await namedSessions.current(owner);
|
|
5708
|
+
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>.");
|
|
5709
|
+
return true;
|
|
5710
|
+
}
|
|
5711
|
+
case "new": {
|
|
5712
|
+
if (parts.includes("--worktree")) {
|
|
5713
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Worktree tasks are planned for Part 4. Create a shared task with /session new <name>.");
|
|
5714
|
+
return true;
|
|
5715
|
+
}
|
|
5716
|
+
if (parts.length !== 1)
|
|
5717
|
+
break;
|
|
5718
|
+
const created = await namedSessions.create(owner, parts[0]);
|
|
5719
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Created and selected task "${created.name}" (shared workspace).`);
|
|
5720
|
+
return true;
|
|
5721
|
+
}
|
|
5722
|
+
case "use": {
|
|
5723
|
+
if (parts.length !== 1)
|
|
5724
|
+
break;
|
|
5725
|
+
const selected = await namedSessions.use(owner, parts[0]);
|
|
5726
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Selected task "${selected.name}" (${selected.isolation} workspace).`);
|
|
5727
|
+
return true;
|
|
5728
|
+
}
|
|
5729
|
+
case "close": {
|
|
5730
|
+
if (parts.length !== 1)
|
|
5731
|
+
break;
|
|
5732
|
+
const result = await namedSessions.close(owner, parts[0]);
|
|
5733
|
+
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.`);
|
|
5734
|
+
return true;
|
|
5735
|
+
}
|
|
5736
|
+
case "cancel": {
|
|
5737
|
+
if (parts.length > 1)
|
|
5738
|
+
break;
|
|
5739
|
+
const taskName = parts[0];
|
|
5740
|
+
const task = taskName ? await namedSessions.lookup(owner, taskName) : await namedSessions.current(owner);
|
|
5741
|
+
if (!task) {
|
|
5742
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, taskName ? `Task "${sanitizeQuotedText(taskName, 32)}" was not found.` : "No task is currently selected.");
|
|
5743
|
+
return true;
|
|
5744
|
+
}
|
|
5745
|
+
if (task.status !== "open") {
|
|
5746
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Task "${task.name}" is closed.`);
|
|
5747
|
+
return true;
|
|
5748
|
+
}
|
|
5749
|
+
if (!this.activePrompts.has(task.sessionId)) {
|
|
5750
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, `No request is currently running for task "${task.name}".`);
|
|
5751
|
+
return true;
|
|
5752
|
+
}
|
|
5753
|
+
const cancelled = await this.requestActivePromptCancellation(task.sessionId, "cancel_command");
|
|
5754
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, cancelled ? `Cancelled task "${task.name}".` : `Failed to cancel task "${task.name}".`);
|
|
5755
|
+
return true;
|
|
5756
|
+
}
|
|
5757
|
+
default:
|
|
5758
|
+
break;
|
|
5759
|
+
}
|
|
5760
|
+
} catch (error) {
|
|
5761
|
+
await this.sendNamedSessionError(envelope, error);
|
|
5762
|
+
return true;
|
|
5763
|
+
}
|
|
5764
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Usage: /session current | /session new <name> | /session use <name> | /session close <name> | /session cancel [<name>]");
|
|
4607
5765
|
return true;
|
|
4608
5766
|
}
|
|
4609
5767
|
/** Register shared slash commands. Called from constructor. */
|
|
4610
5768
|
registerSharedCommands() {
|
|
4611
5769
|
const doClear = /* @__PURE__ */ __name(async (envelope) => {
|
|
4612
|
-
|
|
5770
|
+
let resetTaskName;
|
|
5771
|
+
let removedIds;
|
|
5772
|
+
if (this.namedSessions) {
|
|
5773
|
+
try {
|
|
5774
|
+
const reset = await this.namedSessions.reset(this.namedSessionOwner(envelope));
|
|
5775
|
+
resetTaskName = reset?.name;
|
|
5776
|
+
removedIds = reset ? [reset.previousSessionId] : [];
|
|
5777
|
+
} catch (error) {
|
|
5778
|
+
await this.sendNamedSessionError(envelope, error);
|
|
5779
|
+
return;
|
|
5780
|
+
}
|
|
5781
|
+
} else {
|
|
5782
|
+
removedIds = this.router.removeSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
5783
|
+
}
|
|
4613
5784
|
this.clearPendingGroupHistory(envelope);
|
|
4614
5785
|
if (removedIds.length > 0) {
|
|
4615
5786
|
for (const id of removedIds) {
|
|
@@ -4655,7 +5826,7 @@ ${requestList}`);
|
|
|
4655
5826
|
}
|
|
4656
5827
|
this.discardRetiredSession(this.bridge, id, "cleared");
|
|
4657
5828
|
}
|
|
4658
|
-
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Session cleared. The next message starts a fresh conversation.");
|
|
5829
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, resetTaskName ? `Task "${resetTaskName}" reset with a fresh conversation.` : "Session cleared. The next message starts a fresh conversation.");
|
|
4659
5830
|
} else {
|
|
4660
5831
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No active session to clear.");
|
|
4661
5832
|
}
|
|
@@ -4675,6 +5846,10 @@ ${requestList}`);
|
|
|
4675
5846
|
this.registerCommand("clear", clearHandler);
|
|
4676
5847
|
this.registerCommand("reset", clearHandler);
|
|
4677
5848
|
this.registerCommand("new", clearHandler);
|
|
5849
|
+
if (this.namedSessions) {
|
|
5850
|
+
this.registerCommand("sessions", (envelope, args) => this.handleNamedSessionsCommand(envelope, args));
|
|
5851
|
+
this.registerCommand("session", (envelope, args) => this.handleNamedSessionCommand(envelope, args));
|
|
5852
|
+
}
|
|
4678
5853
|
this.registerCommand("approve", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "approve"));
|
|
4679
5854
|
this.registerCommand("approve-always", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "approve-always"));
|
|
4680
5855
|
this.registerCommand("deny", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "deny"));
|
|
@@ -4683,7 +5858,7 @@ ${requestList}`);
|
|
|
4683
5858
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can view this shared session.");
|
|
4684
5859
|
return true;
|
|
4685
5860
|
}
|
|
4686
|
-
const active = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
5861
|
+
const active = this.namedSessions ? Boolean(await this.currentSessionId(envelope)) : this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
4687
5862
|
const scopeNote = this.config.sessionScope === "single" ? " (shared channel-wide)" : this.isSharedSession(envelope) ? envelope.isGroup ? " (shared by this group)" : "" : envelope.isGroup ? " (private to you)" : "";
|
|
4688
5863
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, [
|
|
4689
5864
|
`Channel: ${this.name}`,
|
|
@@ -4708,7 +5883,11 @@ ${requestList}`);
|
|
|
4708
5883
|
"/status \u2014 Show session info",
|
|
4709
5884
|
"/approve [request-id] \u2014 Approve a pending permission request",
|
|
4710
5885
|
"/approve-always [request-id] \u2014 Always approve a pending permission request",
|
|
4711
|
-
"/deny [request-id] \u2014 Deny a pending permission request"
|
|
5886
|
+
"/deny [request-id] \u2014 Deny a pending permission request",
|
|
5887
|
+
...this.namedSessions ? [
|
|
5888
|
+
"/sessions [all] \u2014 List your named tasks",
|
|
5889
|
+
"/session current|new|use|close|cancel \u2014 Manage your named tasks"
|
|
5890
|
+
] : []
|
|
4712
5891
|
];
|
|
4713
5892
|
const sharedCmds = /* @__PURE__ */ new Set([
|
|
4714
5893
|
"help",
|
|
@@ -4722,7 +5901,9 @@ ${requestList}`);
|
|
|
4722
5901
|
"channel-memory",
|
|
4723
5902
|
"forget-channel",
|
|
4724
5903
|
"who",
|
|
4725
|
-
"status"
|
|
5904
|
+
"status",
|
|
5905
|
+
"sessions",
|
|
5906
|
+
"session"
|
|
4726
5907
|
]);
|
|
4727
5908
|
const platformCmds = [...this.commands.keys()].filter((c) => !sharedCmds.has(c));
|
|
4728
5909
|
if (platformCmds.length > 0) {
|
|
@@ -4730,7 +5911,7 @@ ${requestList}`);
|
|
|
4730
5911
|
lines.push(`/${cmd}`);
|
|
4731
5912
|
}
|
|
4732
5913
|
}
|
|
4733
|
-
const sessionId = this.
|
|
5914
|
+
const sessionId = await this.currentSessionId(envelope);
|
|
4734
5915
|
const agentCommands = sessionId ? this.getAgentCommandsForSession(sessionId) : this.bridge.availableCommands;
|
|
4735
5916
|
if (agentCommands.length > 0) {
|
|
4736
5917
|
lines.push("", "Agent commands (forwarded to Qwen Code):");
|
|
@@ -4747,7 +5928,7 @@ ${requestList}`);
|
|
|
4747
5928
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can view this shared session.");
|
|
4748
5929
|
return true;
|
|
4749
5930
|
}
|
|
4750
|
-
const hasSession = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
5931
|
+
const hasSession = this.namedSessions ? Boolean(await this.currentSessionId(envelope)) : this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
4751
5932
|
const policy = this.config.senderPolicy;
|
|
4752
5933
|
const lines = [
|
|
4753
5934
|
`Session: ${hasSession ? "active" : "none"}`,
|
|
@@ -5078,6 +6259,10 @@ ${requestList}`);
|
|
|
5078
6259
|
const sessionId = this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
5079
6260
|
return sessionId && this.activePrompts.has(sessionId) ? sessionId : void 0;
|
|
5080
6261
|
}
|
|
6262
|
+
async findNamedActiveSessionId(envelope) {
|
|
6263
|
+
const sessionId = await this.currentSessionId(envelope);
|
|
6264
|
+
return sessionId && this.activePrompts.has(sessionId) ? sessionId : void 0;
|
|
6265
|
+
}
|
|
5081
6266
|
channelMemoryTarget(envelope) {
|
|
5082
6267
|
return {
|
|
5083
6268
|
channelName: this.name,
|
|
@@ -5805,9 +6990,12 @@ ${formatted.join("\n")}
|
|
|
5805
6990
|
${CURRENT_MESSAGE_MARKER}
|
|
5806
6991
|
${promptText}`;
|
|
5807
6992
|
}
|
|
5808
|
-
preflightInbound(envelope) {
|
|
5809
|
-
const groupResult = this.groupGate.check(envelope
|
|
5810
|
-
|
|
6993
|
+
preflightInbound(envelope, options = {}) {
|
|
6994
|
+
const groupResult = this.groupGate.check(envelope, {
|
|
6995
|
+
createPairingRequest: !options.deferPairingRequests
|
|
6996
|
+
});
|
|
6997
|
+
const deferredGroupPairing = options.deferPairingRequests === true && groupResult.reason === "pairing_trigger_required" && (envelope.isMentioned || envelope.isReplyToBot);
|
|
6998
|
+
if (!groupResult.allowed && !deferredGroupPairing) {
|
|
5811
6999
|
if (groupResult.pairing !== void 0) {
|
|
5812
7000
|
this.logPreflightRejected("group_pairing_required");
|
|
5813
7001
|
return this.onGroupPairingRequired(envelope.chatId, groupResult.pairing, envelope.threadId).then(() => false).catch((err) => {
|
|
@@ -5834,6 +7022,10 @@ ${promptText}`;
|
|
|
5834
7022
|
this.markPreflighted(envelope);
|
|
5835
7023
|
return true;
|
|
5836
7024
|
}
|
|
7025
|
+
if (options.deferPairingRequests === true && this.config.senderPolicy === "pairing" && !this.gate.isAllowed(envelope.senderId)) {
|
|
7026
|
+
this.markPreflighted(envelope);
|
|
7027
|
+
return true;
|
|
7028
|
+
}
|
|
5837
7029
|
const result = this.gate.check(envelope.senderId, envelope.senderName);
|
|
5838
7030
|
if (!result.allowed) {
|
|
5839
7031
|
if (result.pairing !== void 0) {
|
|
@@ -5870,7 +7062,50 @@ ${promptText}`;
|
|
|
5870
7062
|
const preflight = this.preflightInbound(envelope);
|
|
5871
7063
|
if (!(isPromiseLike(preflight) ? await preflight : preflight))
|
|
5872
7064
|
return;
|
|
5873
|
-
await this.
|
|
7065
|
+
await this.processPreflightedInbound(envelope);
|
|
7066
|
+
}
|
|
7067
|
+
async prepareThenHandleInbound(envelope, prepare, preflightOptions = {}) {
|
|
7068
|
+
const preflight = this.preflightInbound(envelope, preflightOptions);
|
|
7069
|
+
if (!(isPromiseLike(preflight) ? await preflight : preflight))
|
|
7070
|
+
return;
|
|
7071
|
+
if (this.namedSessions) {
|
|
7072
|
+
const result = await this.namedSessions.resolveAfterPreparation(this.namedSessionOwner(envelope), prepare, () => !this.bypassesNamedTurnBinding(envelope), (sessionId) => {
|
|
7073
|
+
const binding = this.bindNamedTurn(envelope, sessionId);
|
|
7074
|
+
return () => this.releaseNamedTurnBinding(binding);
|
|
7075
|
+
});
|
|
7076
|
+
if (result.status === "aborted")
|
|
7077
|
+
return;
|
|
7078
|
+
if (result.status === "resolve_error") {
|
|
7079
|
+
this.finishNamedTurnBinding(envelope);
|
|
7080
|
+
await this.sendNamedSessionError(envelope, result.error);
|
|
7081
|
+
return;
|
|
7082
|
+
}
|
|
7083
|
+
if (result.status === "resolved" && !result.sessionId && !this.namedTurnBindings.has(envelope)) {
|
|
7084
|
+
this.namedTurnBindings.set(envelope, {
|
|
7085
|
+
sessionId: null,
|
|
7086
|
+
generation: 0,
|
|
7087
|
+
claimed: false,
|
|
7088
|
+
released: true
|
|
7089
|
+
});
|
|
7090
|
+
}
|
|
7091
|
+
} else if (await prepare() === false) {
|
|
7092
|
+
return;
|
|
7093
|
+
}
|
|
7094
|
+
try {
|
|
7095
|
+
await this.handleInbound(envelope);
|
|
7096
|
+
} finally {
|
|
7097
|
+
this.finishNamedTurnBinding(envelope);
|
|
7098
|
+
}
|
|
7099
|
+
}
|
|
7100
|
+
async processPreflightedInbound(envelope, process5 = () => this.processInbound(envelope)) {
|
|
7101
|
+
if (this.namedSessions && !await this.prepareNamedTurnBinding(envelope)) {
|
|
7102
|
+
return;
|
|
7103
|
+
}
|
|
7104
|
+
try {
|
|
7105
|
+
await process5();
|
|
7106
|
+
} finally {
|
|
7107
|
+
this.finishNamedTurnBinding(envelope);
|
|
7108
|
+
}
|
|
5874
7109
|
}
|
|
5875
7110
|
async recordObservedContact(envelope) {
|
|
5876
7111
|
if (!this.observedContacts)
|
|
@@ -5919,6 +7154,57 @@ ${promptText}`;
|
|
|
5919
7154
|
return void 0;
|
|
5920
7155
|
}
|
|
5921
7156
|
}
|
|
7157
|
+
formatAttributedText(text, sourceLabel) {
|
|
7158
|
+
if (!sourceLabel || text.trim().length === 0)
|
|
7159
|
+
return text;
|
|
7160
|
+
return `${sourceLabel} ${text}`;
|
|
7161
|
+
}
|
|
7162
|
+
formatMarkdownAttributedText(text, sourceLabel) {
|
|
7163
|
+
const escapedLabel = sourceLabel?.replace(/([\\`*_[\]{}()#+\-.!|>~])/gu, "\\$1");
|
|
7164
|
+
if (!escapedLabel || text.trim().length === 0)
|
|
7165
|
+
return text;
|
|
7166
|
+
return `${escapedLabel}
|
|
7167
|
+
${text}`;
|
|
7168
|
+
}
|
|
7169
|
+
sameTaskOwner(a, b) {
|
|
7170
|
+
return a.channelName === b.channelName && a.chatId === b.chatId && a.senderId === b.senderId;
|
|
7171
|
+
}
|
|
7172
|
+
createSourceLabel(reference, target, active) {
|
|
7173
|
+
const activeName = active?.senderName ? this.sanitizeSourceSender(active.senderName) : void 0;
|
|
7174
|
+
const senderLabel = (activeName && activeName !== "unknown" ? activeName : void 0) ?? this.observedSenderLabel(target.chatId, target.senderId) ?? this.sanitizeSourceSender(target.senderId || "unknown");
|
|
7175
|
+
const isGroup = active?.isGroup ?? target.isGroup ?? true;
|
|
7176
|
+
return isGroup ? `[${senderLabel} \xB7 ${reference.taskName}]` : `[${reference.taskName}]`;
|
|
7177
|
+
}
|
|
7178
|
+
observedSenderLabel(chatId, senderId) {
|
|
7179
|
+
const graph = this.persistedObservedContacts();
|
|
7180
|
+
if (!graph)
|
|
7181
|
+
return void 0;
|
|
7182
|
+
const sources = [
|
|
7183
|
+
graph.groups.filter((group) => group.id === chatId).flatMap((group) => group.users).filter((user) => user.id === senderId),
|
|
7184
|
+
graph.users.filter((user) => user.id === senderId)
|
|
7185
|
+
];
|
|
7186
|
+
for (const source of sources) {
|
|
7187
|
+
const candidates = source.sort((a, b) => b.lastObservedAt.localeCompare(a.lastObservedAt));
|
|
7188
|
+
for (const candidate of candidates) {
|
|
7189
|
+
const label = this.sanitizeSourceSender(candidate.label);
|
|
7190
|
+
if (label !== "unknown")
|
|
7191
|
+
return label;
|
|
7192
|
+
}
|
|
7193
|
+
}
|
|
7194
|
+
return void 0;
|
|
7195
|
+
}
|
|
7196
|
+
sanitizeSourceSender(value) {
|
|
7197
|
+
return sanitizeSenderName(value).replace(/\s+/gu, " ").trim() || "unknown";
|
|
7198
|
+
}
|
|
7199
|
+
sourceLabelForTurn(sessionId, envelope) {
|
|
7200
|
+
const reference = this.namedSessions?.presentation(sessionId);
|
|
7201
|
+
if (!reference || reference.status !== "open")
|
|
7202
|
+
return void 0;
|
|
7203
|
+
return this.createSourceLabel(reference, reference.target, {
|
|
7204
|
+
isGroup: envelope.isGroup,
|
|
7205
|
+
senderName: envelope.senderName
|
|
7206
|
+
});
|
|
7207
|
+
}
|
|
5922
7208
|
markPreflighted(envelope) {
|
|
5923
7209
|
this.preflightedEnvelopes.add(envelope);
|
|
5924
7210
|
}
|
|
@@ -5995,7 +7281,51 @@ ${promptText}`;
|
|
|
5995
7281
|
}
|
|
5996
7282
|
}
|
|
5997
7283
|
await this.waitForBridgeRecovery();
|
|
5998
|
-
|
|
7284
|
+
let sessionId;
|
|
7285
|
+
let namedTurn = this.namedTurnBindings.get(envelope);
|
|
7286
|
+
if (this.namedSessions && namedTurn) {
|
|
7287
|
+
if (namedTurn.sessionId === null) {
|
|
7288
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No task was selected when this message was received. Select a task and send it again.");
|
|
7289
|
+
return;
|
|
7290
|
+
}
|
|
7291
|
+
if (this.dropQueuedTurnIfStale(namedTurn.sessionId, namedTurn.generation, envelope)) {
|
|
7292
|
+
return;
|
|
7293
|
+
}
|
|
7294
|
+
try {
|
|
7295
|
+
const resumed = await this.namedSessions.resumeReserved(this.namedSessionOwner(envelope), namedTurn.sessionId);
|
|
7296
|
+
sessionId = resumed ? namedTurn.sessionId : void 0;
|
|
7297
|
+
} catch (error) {
|
|
7298
|
+
await this.sendNamedSessionError(envelope, error);
|
|
7299
|
+
return;
|
|
7300
|
+
}
|
|
7301
|
+
if (!sessionId) {
|
|
7302
|
+
process.stderr.write(`[${this.name}] dropped collected turn from ${envelope.senderId} for session ${namedTurn.sessionId}: reserved task is no longer available
|
|
7303
|
+
`);
|
|
7304
|
+
return;
|
|
7305
|
+
}
|
|
7306
|
+
} else if (this.namedSessions) {
|
|
7307
|
+
try {
|
|
7308
|
+
sessionId = await this.namedSessions.resolve(this.namedSessionOwner(envelope), (resolvedSessionId) => {
|
|
7309
|
+
const binding = this.bindNamedTurn(envelope, resolvedSessionId);
|
|
7310
|
+
namedTurn = binding;
|
|
7311
|
+
return () => this.releaseNamedTurnBinding(binding);
|
|
7312
|
+
});
|
|
7313
|
+
} catch (error) {
|
|
7314
|
+
await this.sendNamedSessionError(envelope, error);
|
|
7315
|
+
return;
|
|
7316
|
+
}
|
|
7317
|
+
if (!sessionId) {
|
|
7318
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No task is currently selected. Use /session new <name> or /session use <name>.");
|
|
7319
|
+
return;
|
|
7320
|
+
}
|
|
7321
|
+
} else {
|
|
7322
|
+
sessionId = await this.router.resolve(this.name, envelope.senderId, envelope.chatId, envelope.threadId, this.config.cwd, envelope.isGroup);
|
|
7323
|
+
}
|
|
7324
|
+
const sourceLabel = this.namedSessions ? this.sourceLabelForTurn(sessionId, envelope) : void 0;
|
|
7325
|
+
if (this.namedSessions && !sourceLabel) {
|
|
7326
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Could not identify the selected task. Use /sessions, select it again, and retry.");
|
|
7327
|
+
return;
|
|
7328
|
+
}
|
|
5999
7329
|
if (bangText.startsWith("!")) {
|
|
6000
7330
|
const cmd = bangText.slice(1).trim();
|
|
6001
7331
|
const bridgeShellCommand = this.bridge.shellCommand;
|
|
@@ -6010,9 +7340,9 @@ ${fence}` : "(no output)";
|
|
|
6010
7340
|
const exitLine = result.exitCode !== null && result.exitCode !== 0 ? `
|
|
6011
7341
|
Exit code: ${result.exitCode}` : "";
|
|
6012
7342
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, `$ ${cmd}
|
|
6013
|
-
${output}${exitLine}
|
|
7343
|
+
${output}${exitLine}`, sourceLabel);
|
|
6014
7344
|
} catch (error) {
|
|
6015
|
-
await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Shell command failed: ${error instanceof Error ? error.message : String(error)}
|
|
7345
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Shell command failed: ${error instanceof Error ? error.message : String(error)}`, sourceLabel);
|
|
6016
7346
|
}
|
|
6017
7347
|
return;
|
|
6018
7348
|
}
|
|
@@ -6040,12 +7370,19 @@ ${promptText}`;
|
|
|
6040
7370
|
}
|
|
6041
7371
|
let imageBase64 = envelope.imageBase64;
|
|
6042
7372
|
let imageMimeType = envelope.imageMimeType;
|
|
7373
|
+
const images = [];
|
|
7374
|
+
if (imageBase64 && imageMimeType) {
|
|
7375
|
+
images.push({ data: imageBase64, mimeType: imageMimeType });
|
|
7376
|
+
}
|
|
6043
7377
|
if (envelope.attachments?.length) {
|
|
6044
7378
|
const filePaths = [];
|
|
6045
7379
|
for (const att of envelope.attachments) {
|
|
6046
|
-
if (att.type === "image" && att.data
|
|
6047
|
-
|
|
6048
|
-
|
|
7380
|
+
if (att.type === "image" && att.data) {
|
|
7381
|
+
images.push({ data: att.data, mimeType: att.mimeType });
|
|
7382
|
+
if (!imageBase64) {
|
|
7383
|
+
imageBase64 = att.data;
|
|
7384
|
+
imageMimeType = att.mimeType;
|
|
7385
|
+
}
|
|
6049
7386
|
} else if (att.filePath) {
|
|
6050
7387
|
const label = att.type === "file" ? "file" : att.type;
|
|
6051
7388
|
const name = att.fileName ? ` "${sanitizeQuotedText(att.fileName, 128)}"` : "";
|
|
@@ -6131,8 +7468,13 @@ ${promptText}`;
|
|
|
6131
7468
|
this.instructedSessions.add(sessionId);
|
|
6132
7469
|
}
|
|
6133
7470
|
const prev = this.sessionQueues.get(sessionId) ?? Promise.resolve();
|
|
6134
|
-
const generation = this.sessionGenerations.get(sessionId) ?? 0;
|
|
7471
|
+
const generation = namedTurn?.generation ?? this.sessionGenerations.get(sessionId) ?? 0;
|
|
6135
7472
|
const useBlockStreaming = this.config.blockStreaming === "on";
|
|
7473
|
+
if (namedTurn) {
|
|
7474
|
+
namedTurn.claimed = true;
|
|
7475
|
+
} else {
|
|
7476
|
+
this.reserveQueuedTurn(sessionId);
|
|
7477
|
+
}
|
|
6136
7478
|
const current = prev.then(async () => {
|
|
6137
7479
|
clearTimeout(steerWatchdog);
|
|
6138
7480
|
if (this.dropQueuedTurnIfStale(sessionId, generation, envelope)) {
|
|
@@ -6199,7 +7541,7 @@ ${promptToSend}`;
|
|
|
6199
7541
|
doneResolve = r;
|
|
6200
7542
|
});
|
|
6201
7543
|
const promptState = {
|
|
6202
|
-
runId:
|
|
7544
|
+
runId: randomUUID3(),
|
|
6203
7545
|
owner: {
|
|
6204
7546
|
kind: "channel_user",
|
|
6205
7547
|
id: envelope.senderId
|
|
@@ -6213,7 +7555,8 @@ ${promptToSend}`;
|
|
|
6213
7555
|
messageId: envelope.messageId,
|
|
6214
7556
|
senderId: envelope.senderId,
|
|
6215
7557
|
senderName: envelope.senderName,
|
|
6216
|
-
metadata: envelope.metadata
|
|
7558
|
+
metadata: envelope.metadata,
|
|
7559
|
+
sourceLabel
|
|
6217
7560
|
};
|
|
6218
7561
|
this.activePrompts.set(sessionId, promptState);
|
|
6219
7562
|
this.emitTaskLifecycle({
|
|
@@ -6230,7 +7573,7 @@ ${promptToSend}`;
|
|
|
6230
7573
|
minChars: this.config.blockStreamingChunk?.minChars ?? 400,
|
|
6231
7574
|
maxChars: this.config.blockStreamingChunk?.maxChars ?? 1e3,
|
|
6232
7575
|
idleMs: this.config.blockStreamingCoalesce?.idleMs ?? 1500,
|
|
6233
|
-
send: /* @__PURE__ */ __name((text) => this.sendResponseMessage(envelope.chatId, text, sessionId), "send")
|
|
7576
|
+
send: /* @__PURE__ */ __name((text) => this.sendResponseMessage(envelope.chatId, text, sessionId, sourceLabel), "send")
|
|
6234
7577
|
}) : null;
|
|
6235
7578
|
promptState.stopStreaming = () => streamer?.stop();
|
|
6236
7579
|
const heldChunks = [];
|
|
@@ -6273,6 +7616,7 @@ ${promptToSend}`;
|
|
|
6273
7616
|
promptBridge.on("responseBoundary", onResponseBoundary);
|
|
6274
7617
|
try {
|
|
6275
7618
|
const response = await promptBridge.prompt(sessionId, promptToSend, {
|
|
7619
|
+
...images.length > 0 ? { images } : {},
|
|
6276
7620
|
imageBase64,
|
|
6277
7621
|
imageMimeType,
|
|
6278
7622
|
displayText
|
|
@@ -6331,11 +7675,17 @@ ${promptToSend}`;
|
|
|
6331
7675
|
if (promptState.cancelled) {
|
|
6332
7676
|
return;
|
|
6333
7677
|
}
|
|
7678
|
+
if (sourceLabel) {
|
|
7679
|
+
this.inboundErrorSourceLabels.set(envelope, sourceLabel);
|
|
7680
|
+
}
|
|
6334
7681
|
throw err;
|
|
6335
7682
|
} finally {
|
|
6336
7683
|
promptBridge.off("textChunk", onChunk);
|
|
6337
7684
|
promptBridge.off("responseBoundary", onResponseBoundary);
|
|
6338
|
-
streamer
|
|
7685
|
+
if (streamer) {
|
|
7686
|
+
streamer.stop();
|
|
7687
|
+
await streamer.drain();
|
|
7688
|
+
}
|
|
6339
7689
|
const stillCurrent = this.activePrompts.get(sessionId) === promptState;
|
|
6340
7690
|
if (!promptState.clearEvicted) {
|
|
6341
7691
|
try {
|
|
@@ -6349,39 +7699,15 @@ ${promptToSend}`;
|
|
|
6349
7699
|
this.activePrompts.delete(sessionId);
|
|
6350
7700
|
}
|
|
6351
7701
|
promptState.resolve();
|
|
6352
|
-
|
|
6353
|
-
if (stillCurrent && buffer && buffer.length > 0) {
|
|
6354
|
-
this.collectBuffers.delete(sessionId);
|
|
6355
|
-
const lost = buffer.length;
|
|
6356
|
-
const coalesced = buffer.map((b) => b.text).join("\n\n");
|
|
6357
|
-
const coalescedDisplayText = buffer.map((b) => b.displayText).join("\n\n");
|
|
6358
|
-
const lastEnvelope = buffer[buffer.length - 1].envelope;
|
|
6359
|
-
this.notifyPromptBufferDrained(lastEnvelope.chatId, sessionId, buffer);
|
|
6360
|
-
const syntheticEnvelope = {
|
|
6361
|
-
...lastEnvelope,
|
|
6362
|
-
text: coalesced,
|
|
6363
|
-
displayText: coalescedDisplayText,
|
|
6364
|
-
// Coalesced text already carries each message's [sender] prefix.
|
|
6365
|
-
alreadyPrefixed: true,
|
|
6366
|
-
// Clear attachments/references — already resolved in original text
|
|
6367
|
-
referencedText: void 0,
|
|
6368
|
-
mentionedMemberIds: void 0,
|
|
6369
|
-
attachments: void 0,
|
|
6370
|
-
metadata: void 0,
|
|
6371
|
-
imageBase64: void 0,
|
|
6372
|
-
imageMimeType: void 0
|
|
6373
|
-
};
|
|
6374
|
-
this.markPreflighted(syntheticEnvelope);
|
|
6375
|
-
this.processInbound(syntheticEnvelope).catch((err) => {
|
|
6376
|
-
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)}
|
|
6377
|
-
`);
|
|
6378
|
-
});
|
|
6379
|
-
}
|
|
7702
|
+
this.drainCollectBufferForCurrentPrompt(sessionId, stillCurrent, "prompt completion");
|
|
6380
7703
|
}
|
|
6381
7704
|
});
|
|
6382
|
-
|
|
7705
|
+
const tracked = current.finally(() => {
|
|
7706
|
+
this.releaseQueuedTurn(sessionId);
|
|
7707
|
+
});
|
|
7708
|
+
this.sessionQueues.set(sessionId, tracked.catch(() => {
|
|
6383
7709
|
}));
|
|
6384
|
-
await
|
|
7710
|
+
await tracked;
|
|
6385
7711
|
}
|
|
6386
7712
|
pairingRejectionMessage(rejected) {
|
|
6387
7713
|
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.";
|
|
@@ -6491,10 +7817,10 @@ var PollingChannelBase = class extends ChannelBase {
|
|
|
6491
7817
|
}
|
|
6492
7818
|
saveCursor() {
|
|
6493
7819
|
const path4 = this.cursorPath();
|
|
6494
|
-
|
|
7820
|
+
mkdirSync5(join6(getGlobalQwenDir(), "channels"), { recursive: true });
|
|
6495
7821
|
const tmp = `${path4}.tmp`;
|
|
6496
|
-
|
|
6497
|
-
|
|
7822
|
+
writeFileSync5(tmp, JSON.stringify(this.cursor) + "\n", "utf-8");
|
|
7823
|
+
renameSync5(tmp, path4);
|
|
6498
7824
|
}
|
|
6499
7825
|
startPollLoop() {
|
|
6500
7826
|
if (this.running)
|
|
@@ -6518,7 +7844,7 @@ var PollingChannelBase = class extends ChannelBase {
|
|
|
6518
7844
|
} catch (err) {
|
|
6519
7845
|
this.consecutiveErrors++;
|
|
6520
7846
|
const backoff = Math.min(INITIAL_BACKOFF * 2 ** (this.consecutiveErrors - 1), MAX_BACKOFF);
|
|
6521
|
-
|
|
7847
|
+
process4.stderr.write(`[Channel:${this.name}] poll error (attempt ${this.consecutiveErrors}), backing off ${backoff}ms: ${err}
|
|
6522
7848
|
`);
|
|
6523
7849
|
await this.abortableSleep(backoff);
|
|
6524
7850
|
continue;
|
|
@@ -6528,7 +7854,7 @@ var PollingChannelBase = class extends ChannelBase {
|
|
|
6528
7854
|
}
|
|
6529
7855
|
loadCursorFromDisk() {
|
|
6530
7856
|
try {
|
|
6531
|
-
const raw =
|
|
7857
|
+
const raw = readFileSync5(this.cursorPath(), "utf-8").trim();
|
|
6532
7858
|
if (!raw)
|
|
6533
7859
|
return null;
|
|
6534
7860
|
return this.validateCursor(JSON.parse(raw));
|
|
@@ -6562,6 +7888,48 @@ function getTextContent(content) {
|
|
|
6562
7888
|
return getString(content["text"]);
|
|
6563
7889
|
}
|
|
6564
7890
|
__name(getTextContent, "getTextContent");
|
|
7891
|
+
var CHANNEL_IMAGE_EXTENSIONS = ["bmp", "gif", "jpeg", "png", "webp"];
|
|
7892
|
+
var CHANNEL_IMAGE_MAX_UPLOAD_BYTES = 8 * 1024 * 1024;
|
|
7893
|
+
var CHANNEL_IMAGE_INLINE_MAX_BASE64_BYTES = 8 * 1024 * 1024;
|
|
7894
|
+
function channelImageName(mimeType, index = 0) {
|
|
7895
|
+
if (!mimeType.startsWith("image/")) {
|
|
7896
|
+
return void 0;
|
|
7897
|
+
}
|
|
7898
|
+
const extension = mimeType.slice("image/".length);
|
|
7899
|
+
if (!CHANNEL_IMAGE_EXTENSIONS.includes(extension)) {
|
|
7900
|
+
return void 0;
|
|
7901
|
+
}
|
|
7902
|
+
return index === 0 ? `image.${extension}` : `image-${index + 1}.${extension}`;
|
|
7903
|
+
}
|
|
7904
|
+
__name(channelImageName, "channelImageName");
|
|
7905
|
+
function decodeChannelImage(data, oversizedReason) {
|
|
7906
|
+
let estimatedBytes = Math.floor(data.length * 3 / 4);
|
|
7907
|
+
if (data.length % 4 === 0) {
|
|
7908
|
+
if (data.endsWith("=="))
|
|
7909
|
+
estimatedBytes -= 2;
|
|
7910
|
+
else if (data.endsWith("="))
|
|
7911
|
+
estimatedBytes -= 1;
|
|
7912
|
+
}
|
|
7913
|
+
if (estimatedBytes > CHANNEL_IMAGE_MAX_UPLOAD_BYTES) {
|
|
7914
|
+
return { skip: oversizedReason };
|
|
7915
|
+
}
|
|
7916
|
+
const bytes = Buffer.from(data, "base64");
|
|
7917
|
+
if (bytes.byteLength === 0) {
|
|
7918
|
+
return { skip: "empty once base64-decoded" };
|
|
7919
|
+
}
|
|
7920
|
+
return { bytes };
|
|
7921
|
+
}
|
|
7922
|
+
__name(decodeChannelImage, "decodeChannelImage");
|
|
7923
|
+
function isDefinitePromptAdmissionRejection(error) {
|
|
7924
|
+
if (!isRecord3(error)) {
|
|
7925
|
+
return false;
|
|
7926
|
+
}
|
|
7927
|
+
if (error["name"] === "DaemonPendingPromptLimitError") {
|
|
7928
|
+
return true;
|
|
7929
|
+
}
|
|
7930
|
+
return error["name"] === "DaemonHttpError" && typeof error["status"] === "number" && error["_daemonTurnError"] !== true;
|
|
7931
|
+
}
|
|
7932
|
+
__name(isDefinitePromptAdmissionRejection, "isDefinitePromptAdmissionRejection");
|
|
6565
7933
|
function getSessionUpdate(data) {
|
|
6566
7934
|
if (!isRecord3(data) || !isRecord3(data["update"])) {
|
|
6567
7935
|
return void 0;
|
|
@@ -6633,8 +8001,9 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6633
8001
|
availableCommandsBySession = /* @__PURE__ */ new Map();
|
|
6634
8002
|
turnBarriers = /* @__PURE__ */ new Map();
|
|
6635
8003
|
channelLoopToolHandlers = [];
|
|
8004
|
+
channelLoopDisabledSessions = /* @__PURE__ */ new Set();
|
|
6636
8005
|
registeredChannelLoopMcpSessions = /* @__PURE__ */ new Set();
|
|
6637
|
-
|
|
8006
|
+
channelLoopMcpOperations = /* @__PURE__ */ new Map();
|
|
6638
8007
|
channelLoopMcpServer;
|
|
6639
8008
|
connected = false;
|
|
6640
8009
|
lifecycleGeneration = 0;
|
|
@@ -6694,7 +8063,12 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6694
8063
|
await this.rejectStaleSession(session);
|
|
6695
8064
|
}
|
|
6696
8065
|
this.attachSession(session, bindingToken);
|
|
6697
|
-
|
|
8066
|
+
if (options?.enableChannelLoops === false) {
|
|
8067
|
+
this.channelLoopDisabledSessions.add(session.sessionId);
|
|
8068
|
+
void this.reconcileChannelLoopMcpForSession(session.sessionId);
|
|
8069
|
+
} else {
|
|
8070
|
+
await this.reconcileChannelLoopMcpForSession(session.sessionId);
|
|
8071
|
+
}
|
|
6698
8072
|
return session.sessionId;
|
|
6699
8073
|
}
|
|
6700
8074
|
async loadSession(sessionId, cwd, options, bindingToken) {
|
|
@@ -6704,7 +8078,8 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6704
8078
|
modelServiceId: this.options.modelServiceId,
|
|
6705
8079
|
sessionId,
|
|
6706
8080
|
sessionScope: this.options.sessionScope ?? "thread",
|
|
6707
|
-
...options?.approvalMode ? { approvalMode: options.approvalMode } : {}
|
|
8081
|
+
...options?.approvalMode ? { approvalMode: options.approvalMode } : {},
|
|
8082
|
+
...options?.sourceId ? { sourceId: options.sourceId } : {}
|
|
6708
8083
|
});
|
|
6709
8084
|
if (lifecycleGeneration !== this.lifecycleGeneration) {
|
|
6710
8085
|
await this.rejectStaleSession(session);
|
|
@@ -6716,7 +8091,12 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6716
8091
|
throw new Error(`Daemon returned session ${session.sessionId} while loading ${sessionId}`);
|
|
6717
8092
|
}
|
|
6718
8093
|
this.attachSession(session, bindingToken);
|
|
6719
|
-
|
|
8094
|
+
if (options?.enableChannelLoops === false) {
|
|
8095
|
+
this.channelLoopDisabledSessions.add(session.sessionId);
|
|
8096
|
+
void this.reconcileChannelLoopMcpForSession(session.sessionId);
|
|
8097
|
+
} else {
|
|
8098
|
+
await this.reconcileChannelLoopMcpForSession(session.sessionId);
|
|
8099
|
+
}
|
|
6720
8100
|
return session.sessionId;
|
|
6721
8101
|
}
|
|
6722
8102
|
registerChannelLoopToolHandler(handler) {
|
|
@@ -6729,7 +8109,9 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6729
8109
|
cancel: /* @__PURE__ */ __name((sessionId, id) => this.resolveChannelLoopToolHandler(sessionId).cancel(sessionId, id), "cancel")
|
|
6730
8110
|
});
|
|
6731
8111
|
for (const sessionId of this.sessions.keys()) {
|
|
6732
|
-
|
|
8112
|
+
if (!this.channelLoopDisabledSessions.has(sessionId)) {
|
|
8113
|
+
void this.reconcileChannelLoopMcpForSession(sessionId);
|
|
8114
|
+
}
|
|
6733
8115
|
}
|
|
6734
8116
|
}
|
|
6735
8117
|
async prompt(sessionId, text, options) {
|
|
@@ -6773,29 +8155,101 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6773
8155
|
this.on("responseBoundary", clearChunks);
|
|
6774
8156
|
this.on("sessionDied", onSessionDied);
|
|
6775
8157
|
const turnBarrier = this.createTurnBarrier(sessionId);
|
|
6776
|
-
const
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
data: options.imageBase64,
|
|
6781
|
-
mimeType: options.imageMimeType
|
|
6782
|
-
});
|
|
6783
|
-
}
|
|
6784
|
-
prompt.push({ type: "text", text });
|
|
6785
|
-
const promptAuthorization = this.options.promptAuthorization;
|
|
8158
|
+
const uploadedAttachmentIds = [];
|
|
8159
|
+
let rollbackUploadedAttachments = false;
|
|
8160
|
+
const uploadAttachment = session.uploadAttachment?.bind(session);
|
|
8161
|
+
const removeAttachment = session.removeAttachment?.bind(session);
|
|
6786
8162
|
try {
|
|
6787
|
-
const
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
8163
|
+
const prompt = [];
|
|
8164
|
+
const images = resolvePromptImages(options);
|
|
8165
|
+
if (this.options.sessionAttachments && uploadAttachment && removeAttachment) {
|
|
8166
|
+
try {
|
|
8167
|
+
const uploads = await Promise.allSettled(images.map(async (image, index) => {
|
|
8168
|
+
const name = channelImageName(image.mimeType, index);
|
|
8169
|
+
if (!name) {
|
|
8170
|
+
process.stderr.write(`[DaemonChannelBridge] skipped channel image with unsupported MIME type ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
|
|
8171
|
+
`);
|
|
8172
|
+
return void 0;
|
|
8173
|
+
}
|
|
8174
|
+
const decoded = decodeChannelImage(image.data, "above the daemon attachment size limit");
|
|
8175
|
+
if ("skip" in decoded) {
|
|
8176
|
+
process.stderr.write(`[DaemonChannelBridge] skipped channel image ${decoded.skip} ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
|
|
8177
|
+
`);
|
|
8178
|
+
return void 0;
|
|
8179
|
+
}
|
|
8180
|
+
const attachment = await uploadAttachment(new Blob([decoded.bytes], {
|
|
8181
|
+
type: image.mimeType
|
|
8182
|
+
}), name, image.mimeType, controller.signal);
|
|
8183
|
+
const attachmentId = getString(attachment["attachmentId"]);
|
|
8184
|
+
if (attachmentId)
|
|
8185
|
+
uploadedAttachmentIds.push(attachmentId);
|
|
8186
|
+
return attachment;
|
|
8187
|
+
}));
|
|
8188
|
+
const failure = uploads.find((upload) => upload.status === "rejected");
|
|
8189
|
+
if (failure) {
|
|
8190
|
+
throw failure.reason;
|
|
8191
|
+
}
|
|
8192
|
+
for (const upload of uploads) {
|
|
8193
|
+
if (upload.status === "fulfilled" && upload.value) {
|
|
8194
|
+
prompt.push(upload.value);
|
|
8195
|
+
}
|
|
8196
|
+
}
|
|
8197
|
+
} catch (error) {
|
|
8198
|
+
rollbackUploadedAttachments = true;
|
|
8199
|
+
throw error;
|
|
8200
|
+
}
|
|
8201
|
+
} else {
|
|
8202
|
+
let inlineBase64Bytes = 0;
|
|
8203
|
+
for (const image of images) {
|
|
8204
|
+
const decoded = decodeChannelImage(image.data, "above the inline image budget");
|
|
8205
|
+
if ("skip" in decoded) {
|
|
8206
|
+
process.stderr.write(`[DaemonChannelBridge] skipped channel image ${decoded.skip} ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
|
|
8207
|
+
`);
|
|
8208
|
+
continue;
|
|
8209
|
+
}
|
|
8210
|
+
if (inlineBase64Bytes + image.data.length > CHANNEL_IMAGE_INLINE_MAX_BASE64_BYTES) {
|
|
8211
|
+
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)}
|
|
8212
|
+
`);
|
|
8213
|
+
continue;
|
|
8214
|
+
}
|
|
8215
|
+
inlineBase64Bytes += image.data.length;
|
|
8216
|
+
prompt.push({
|
|
8217
|
+
type: "image",
|
|
8218
|
+
data: image.data,
|
|
8219
|
+
mimeType: image.mimeType
|
|
8220
|
+
});
|
|
8221
|
+
}
|
|
8222
|
+
}
|
|
8223
|
+
prompt.push({ type: "text", text });
|
|
8224
|
+
if (controller.signal.aborted) {
|
|
8225
|
+
rollbackUploadedAttachments = true;
|
|
8226
|
+
controller.signal.throwIfAborted();
|
|
8227
|
+
}
|
|
8228
|
+
const promptAuthorization = this.options.promptAuthorization;
|
|
8229
|
+
if (controller.signal.aborted) {
|
|
8230
|
+
rollbackUploadedAttachments = true;
|
|
8231
|
+
throw controller.signal.reason;
|
|
8232
|
+
}
|
|
8233
|
+
let result;
|
|
8234
|
+
try {
|
|
8235
|
+
result = await session.prompt({
|
|
8236
|
+
prompt,
|
|
8237
|
+
_meta: {
|
|
8238
|
+
[CHANNEL_PROMPT_META_KEY]: true,
|
|
8239
|
+
...promptAuthorization ? {
|
|
8240
|
+
[CHANNEL_PROMPT_AUTHORIZATION_META_KEY]: promptAuthorization
|
|
8241
|
+
} : {},
|
|
8242
|
+
...options?.displayText !== void 0 ? {
|
|
8243
|
+
[CHANNEL_PROMPT_DISPLAY_TEXT_META_KEY]: options.displayText
|
|
8244
|
+
} : {}
|
|
8245
|
+
}
|
|
8246
|
+
}, controller.signal);
|
|
8247
|
+
} catch (error) {
|
|
8248
|
+
if (isDefinitePromptAdmissionRejection(error)) {
|
|
8249
|
+
rollbackUploadedAttachments = true;
|
|
6797
8250
|
}
|
|
6798
|
-
|
|
8251
|
+
throw error;
|
|
8252
|
+
}
|
|
6799
8253
|
await Promise.race([
|
|
6800
8254
|
turnBarrier,
|
|
6801
8255
|
new Promise((resolve3) => setTimeout(resolve3, 0))
|
|
@@ -6818,6 +8272,16 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6818
8272
|
if (controllers.size === 0 && this.activePromptControllers.get(sessionId) === controllers) {
|
|
6819
8273
|
this.activePromptControllers.delete(sessionId);
|
|
6820
8274
|
}
|
|
8275
|
+
if (rollbackUploadedAttachments && removeAttachment) {
|
|
8276
|
+
const removals = await Promise.allSettled(uploadedAttachmentIds.map((attachmentId) => removeAttachment(attachmentId)));
|
|
8277
|
+
removals.forEach((removal, index) => {
|
|
8278
|
+
if (removal.status === "rejected") {
|
|
8279
|
+
const reason = removal.reason instanceof Error ? removal.reason.message : String(removal.reason);
|
|
8280
|
+
process.stderr.write(`[DaemonChannelBridge] failed to remove channel image ${sanitizeLogText(uploadedAttachmentIds[index] ?? "", 128)} for session ${sanitizeLogText(sessionId, 128)} during rollback: ${sanitizeLogText(reason, 256)}
|
|
8281
|
+
`);
|
|
8282
|
+
}
|
|
8283
|
+
});
|
|
8284
|
+
}
|
|
6821
8285
|
}
|
|
6822
8286
|
}
|
|
6823
8287
|
async shellCommand(sessionId, command, signal) {
|
|
@@ -7175,6 +8639,7 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
7175
8639
|
this.eventControllers.delete(sessionId);
|
|
7176
8640
|
this.sessions.delete(sessionId);
|
|
7177
8641
|
this.sessionBindingTokens.delete(sessionId);
|
|
8642
|
+
this.channelLoopDisabledSessions.delete(sessionId);
|
|
7178
8643
|
this.abortActivePrompts(sessionId);
|
|
7179
8644
|
this.activePrompts.delete(sessionId);
|
|
7180
8645
|
this.availableCommandsBySession.delete(sessionId);
|
|
@@ -7192,45 +8657,45 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
7192
8657
|
}
|
|
7193
8658
|
}
|
|
7194
8659
|
if (unregisterChannelLoopMcp) {
|
|
7195
|
-
this.
|
|
8660
|
+
void this.reconcileChannelLoopMcpForSession(sessionId);
|
|
7196
8661
|
}
|
|
7197
8662
|
return session;
|
|
7198
8663
|
}
|
|
7199
|
-
|
|
7200
|
-
const
|
|
7201
|
-
const
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
8664
|
+
reconcileChannelLoopMcpForSession(sessionId) {
|
|
8665
|
+
const previous = this.channelLoopMcpOperations.get(sessionId) ?? Promise.resolve();
|
|
8666
|
+
const operation = previous.catch(() => void 0).then(async () => {
|
|
8667
|
+
const host = this.options.channelLoopMcpHost;
|
|
8668
|
+
const server = this.channelLoopMcpServer;
|
|
8669
|
+
const shouldRegister = host !== void 0 && server !== void 0 && this.sessions.has(sessionId) && !this.channelLoopDisabledSessions.has(sessionId);
|
|
8670
|
+
if (!shouldRegister) {
|
|
8671
|
+
if (host && this.registeredChannelLoopMcpSessions.has(sessionId)) {
|
|
8672
|
+
await host.unregister(sessionId);
|
|
8673
|
+
this.registeredChannelLoopMcpSessions.delete(sessionId);
|
|
8674
|
+
}
|
|
8675
|
+
return;
|
|
8676
|
+
}
|
|
8677
|
+
if (this.registeredChannelLoopMcpSessions.has(sessionId))
|
|
8678
|
+
return;
|
|
8679
|
+
await host.register(sessionId, (message) => server.handleMessage(message, { sessionId }));
|
|
8680
|
+
this.registeredChannelLoopMcpSessions.add(sessionId);
|
|
8681
|
+
if (!this.sessions.has(sessionId) || this.channelLoopDisabledSessions.has(sessionId)) {
|
|
7214
8682
|
await host.unregister(sessionId);
|
|
8683
|
+
this.registeredChannelLoopMcpSessions.delete(sessionId);
|
|
7215
8684
|
}
|
|
7216
8685
|
}).catch((error) => {
|
|
7217
8686
|
this.lastError = error;
|
|
7218
8687
|
}).finally(() => {
|
|
7219
|
-
if (this.
|
|
7220
|
-
this.
|
|
8688
|
+
if (this.channelLoopMcpOperations.get(sessionId) === operation) {
|
|
8689
|
+
this.channelLoopMcpOperations.delete(sessionId);
|
|
7221
8690
|
}
|
|
7222
8691
|
});
|
|
7223
|
-
this.
|
|
7224
|
-
|
|
7225
|
-
}
|
|
7226
|
-
unregisterChannelLoopMcpForSession(sessionId) {
|
|
7227
|
-
if (!this.registeredChannelLoopMcpSessions.delete(sessionId))
|
|
7228
|
-
return;
|
|
7229
|
-
void this.options.channelLoopMcpHost?.unregister(sessionId).catch((error) => {
|
|
7230
|
-
this.lastError = error;
|
|
7231
|
-
});
|
|
8692
|
+
this.channelLoopMcpOperations.set(sessionId, operation);
|
|
8693
|
+
return operation;
|
|
7232
8694
|
}
|
|
7233
8695
|
resolveChannelLoopToolHandler(sessionId) {
|
|
8696
|
+
if (!this.sessions.has(sessionId) || this.channelLoopDisabledSessions.has(sessionId)) {
|
|
8697
|
+
throw new Error("Channel loop tools are unavailable for this session");
|
|
8698
|
+
}
|
|
7234
8699
|
const handler = this.channelLoopToolHandlers.find((candidate) => candidate.canHandle?.(sessionId) === true || this.channelLoopToolHandlers.length === 1 && !candidate.canHandle);
|
|
7235
8700
|
if (handler)
|
|
7236
8701
|
return handler;
|