@qwen-code/qwen-code 0.22.2 → 0.22.3
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/configuration/settings.md +37 -36
- package/bundled/qc-helper/docs/features/channels/dws.md +2 -0
- package/bundled/qc-helper/docs/features/channels/overview.md +34 -3
- package/bundled/qc-helper/docs/features/code-review.md +3 -3
- package/bundled/qc-helper/docs/features/computer-use.md +2 -2
- package/bundled/qc-helper/docs/features/tool-use-summaries.md +1 -1
- package/bundled/qc-helper/docs/qwen-serve.md +1 -1
- package/bundled/review/SKILL.md +81 -31
- package/bundled/review/references/persistence.md +7 -3
- package/bundled/review/references/posting.md +10 -4
- package/chunks/{MaxSizedBox-X5FZSNAJ.js → MaxSizedBox-X42SXBJH.js} +33 -35
- package/chunks/{StandaloneSessionPicker-AVLI6VJP.js → StandaloneSessionPicker-2FJAOLGG.js} +51 -53
- package/chunks/{acpAgent-MXEAN54C.js → acpAgent-2ZMIJAFV.js} +268 -186
- package/chunks/{agent-PJIPDWHB.js → agent-X5WX3L2I.js} +24 -26
- package/chunks/{agent-headless-B4JVD7UK.js → agent-headless-IZM25LJA.js} +24 -26
- package/chunks/{anthropicContentGenerator-HGMBACVE.js → anthropicContentGenerator-YVTB6QX7.js} +43 -47
- package/chunks/{artifact-tool-5YT4QF7Y.js → artifact-tool-XSMPHZBT.js} +9 -1
- package/chunks/{bridge-FKZTVIS3.js → bridge-ME6QXZNA.js} +37 -39
- package/chunks/{channel-management-service-X4AZ6IXX.js → channel-management-service-HQ6KDS7C.js} +6 -6
- package/chunks/{channel-settings-store-4UFCLBBG.js → channel-settings-store-PEOI7XCG.js} +47 -44
- package/chunks/{channel-worker-group-27HMGD5S.js → channel-worker-group-6NA6BBZV.js} +5 -5
- package/chunks/{channel-worker-manager-QEGX646Z.js → channel-worker-manager-NEZ2J6RK.js} +5 -5
- package/chunks/{channel-worker-supervisor-UEW2WWCC.js → channel-worker-supervisor-FKU7DIHJ.js} +4 -4
- package/chunks/{chunk-P5GGM4KS.js → chunk-2HYKTO7V.js} +3 -3
- package/chunks/{chunk-XDFHY4R3.js → chunk-2LNNVAUV.js} +2 -2
- package/chunks/{chunk-ZFKOCECU.js → chunk-2OBKDPZZ.js} +2 -2
- package/chunks/{chunk-3DXFGHAJ.js → chunk-2OUD5T67.js} +1 -1
- package/chunks/{chunk-EOGELB3H.js → chunk-2SXSLSQE.js} +1 -1
- package/chunks/{chunk-4JWNNLPT.js → chunk-2YOHKHYR.js} +2 -2
- package/chunks/{chunk-JU4EEXC7.js → chunk-36GSQ3MM.js} +2 -2
- package/chunks/chunk-36LGBEM6.js +130 -0
- package/chunks/{chunk-H2WDGQ6D.js → chunk-3JXM2CVW.js} +1 -1
- package/chunks/{chunk-G34IR3D6.js → chunk-3SZC4VP4.js} +26 -109
- package/chunks/{chunk-HVEYF6VT.js → chunk-3WK3QDNV.js} +1 -1
- package/chunks/{chunk-5XBFCMWD.js → chunk-3XKLXHGK.js} +17 -9
- package/chunks/{chunk-SFPGAQUL.js → chunk-42IDLQWS.js} +24 -18
- package/chunks/{chunk-43GGFFLY.js → chunk-4K7KNVWH.js} +1 -1
- package/chunks/{chunk-F6FV3C5K.js → chunk-4NDLQAY2.js} +24 -272
- package/chunks/{chunk-VVW4ZNFY.js → chunk-4QHPPXK2.js} +15 -7
- package/chunks/{chunk-IJKTMLBE.js → chunk-4TT3IVGA.js} +1 -1
- package/chunks/{chunk-C6K43ZDE.js → chunk-4VPCTGH6.js} +5 -5
- package/chunks/chunk-4VQBQLIZ.js +293 -0
- package/chunks/{chunk-OIVXBW3W.js → chunk-62GQFYID.js} +1 -1
- package/chunks/{chunk-BRVWYMKV.js → chunk-6MBXY6WM.js} +1 -1
- package/chunks/{chunk-AGR5UROZ.js → chunk-6X7EU5TI.js} +17 -29
- package/chunks/{chunk-PFCQ62V4.js → chunk-6ZPDLGHN.js} +6 -6
- package/chunks/{chunk-RIEFGNTP.js → chunk-734C6JGI.js} +207 -43
- package/chunks/{chunk-FBSSIAAQ.js → chunk-73RM4POK.js} +7 -7
- package/chunks/{chunk-KGJGEEVR.js → chunk-7FA2II6K.js} +10 -1
- package/chunks/chunk-7J6OTNGO.js +424 -0
- package/chunks/{chunk-O2UN57NX.js → chunk-7LOELM2I.js} +19 -2
- package/chunks/{chunk-DIMEPKCW.js → chunk-7S226YTK.js} +1 -1
- package/chunks/{chunk-CPGJJJLU.js → chunk-7T52W4SW.js} +1 -1
- package/chunks/{chunk-5O72BNDD.js → chunk-7VMIMAVB.js} +7 -7
- package/chunks/{chunk-XRSOI3DL.js → chunk-A3LWD5MK.js} +1 -1
- package/chunks/{chunk-F7TNAPZ4.js → chunk-AIAOIFY4.js} +5 -5
- package/chunks/{chunk-4CDSK5AZ.js → chunk-AYFA7MZV.js} +2 -2
- package/chunks/{chunk-3JGZSIDA.js → chunk-B466ZSHZ.js} +1 -1
- package/chunks/{chunk-L6BZRIUL.js → chunk-B5PHIJGI.js} +182 -4
- package/chunks/{chunk-3I6UTTDX.js → chunk-BKWNKLZB.js} +19 -19
- package/chunks/{chunk-3FDFA4MZ.js → chunk-BOWFGCEU.js} +1 -1
- package/chunks/{chunk-3AFMQUTI.js → chunk-C2X7KY45.js} +1 -1
- package/chunks/{chunk-FY76G3G5.js → chunk-C3PEFRKD.js} +4 -4
- package/chunks/{chunk-FDY5RMGH.js → chunk-CFJY4DGX.js} +1 -1
- package/chunks/{chunk-CCBZDVUA.js → chunk-CIZCL2MW.js} +33 -20
- package/chunks/{chunk-LDP5OD6N.js → chunk-CLWO3B77.js} +2 -2
- package/chunks/chunk-DJBQCO2E.js +842 -0
- package/chunks/{chunk-4J657AJR.js → chunk-DJECZIJX.js} +40 -40
- package/chunks/{chunk-F5SZP77N.js → chunk-DJEZ55D4.js} +71 -65
- package/chunks/{chunk-BAJKAAOY.js → chunk-DJPASAUV.js} +3840 -1055
- package/chunks/{chunk-RUGNCYNO.js → chunk-DMLVSW7L.js} +532 -22
- package/chunks/{chunk-4FTKQNWJ.js → chunk-DYXFD5RO.js} +181 -2
- package/chunks/{chunk-PDMJ3KGS.js → chunk-EB6QRGHM.js} +1 -1
- package/chunks/{chunk-DZWPESIF.js → chunk-EFWRMA2I.js} +3 -3
- package/chunks/{chunk-4MQEPP3Y.js → chunk-EQSBPBZ3.js} +5 -119
- package/chunks/{chunk-Z7TGYN5F.js → chunk-FROAFEFB.js} +2 -2
- package/chunks/{chunk-MZS7GEC7.js → chunk-FTF2YIZ5.js} +1 -1
- package/chunks/{chunk-Z7VOMGDC.js → chunk-FTJMWDND.js} +0 -24
- package/chunks/{chunk-43UYNAWX.js → chunk-G6FECKTJ.js} +2 -2
- package/chunks/{chunk-SJ3ZUWFK.js → chunk-GBJBJ4HX.js} +4 -4
- package/chunks/{chunk-IOPCFOTF.js → chunk-GPGU4S2N.js} +2 -2
- package/chunks/{chunk-6LFJEACX.js → chunk-GVIVASYK.js} +7 -7
- package/chunks/{chunk-Z4X5MTRT.js → chunk-HCOBAG2L.js} +2 -4
- package/chunks/{chunk-A7YP63XN.js → chunk-HP5LR5HL.js} +5 -5
- package/chunks/{chunk-7JPW6IYH.js → chunk-HVC3PNVC.js} +1 -1
- package/chunks/{chunk-TRZTNIQ6.js → chunk-I6LYRJPB.js} +2 -2
- package/chunks/{chunk-E55H6BZ7.js → chunk-ICURCKIU.js} +1335 -119
- package/chunks/{chunk-4HV6HH33.js → chunk-ISKH4QX3.js} +9 -9
- package/chunks/{chunk-WKK5BQNP.js → chunk-JHYH6PYQ.js} +43 -420
- package/chunks/{chunk-6IUNAPLR.js → chunk-K5KXUXWC.js} +1 -1
- package/chunks/{chunk-JHS74YAB.js → chunk-KID24ZFN.js} +1 -1
- package/chunks/{chunk-EFUM7RVY.js → chunk-KJO2LBUK.js} +942 -16
- package/chunks/{chunk-B7CDU2SL.js → chunk-KYWTPLBJ.js} +17 -20
- package/chunks/{chunk-JSHHYW6T.js → chunk-LHBUKJG2.js} +3 -3
- package/chunks/{chunk-P2SU6ZTI.js → chunk-LUEHOUHM.js} +1 -1
- package/chunks/{chunk-66KDY3LV.js → chunk-M3QRM3DE.js} +1 -1
- package/chunks/{chunk-V4QXXQJ2.js → chunk-MF7HCQWZ.js} +2 -2
- package/chunks/{chunk-W5MJJ2V7.js → chunk-MK2GIB46.js} +1 -1
- package/chunks/{chunk-Y6LSMB6J.js → chunk-MO3FC2WT.js} +1 -1
- package/chunks/{chunk-L6YT5H34.js → chunk-MPNBVAEU.js} +4 -4
- package/chunks/{chunk-CSMI4FP5.js → chunk-MSBO7X2W.js} +1 -1
- package/chunks/{chunk-5PA6UEYA.js → chunk-MZCJWS5Y.js} +1 -1
- package/chunks/{chunk-BGXZBI5B.js → chunk-N4X7G4J2.js} +2 -1
- package/chunks/{chunk-6LFTG244.js → chunk-NMOD2FXQ.js} +15 -5
- package/chunks/{chunk-KUX5D6DK.js → chunk-NV55WWD6.js} +11330 -10971
- package/chunks/{chunk-Q26AYA62.js → chunk-OU5APVWO.js} +2 -2
- package/chunks/{chunk-2K6ORIHJ.js → chunk-P2G476QN.js} +11 -11
- package/chunks/{chunk-OIP7C4FE.js → chunk-PADPMMYM.js} +4 -4
- package/chunks/{chunk-SAH4BD2J.js → chunk-PQEISIKS.js} +67 -0
- package/chunks/{chunk-TGWAQ5RB.js → chunk-PZ66FRIC.js} +2071 -2124
- package/chunks/{chunk-7UYV53BC.js → chunk-Q2MSPKIJ.js} +1 -1
- package/chunks/{chunk-L2M46FP5.js → chunk-QYMAKPY5.js} +5 -5
- package/chunks/{chunk-JVPFTDBK.js → chunk-RFIDDFAL.js} +1 -1
- package/chunks/chunk-SEONHUS3.js +275 -0
- package/chunks/{chunk-ZGXYO7DY.js → chunk-SS4MRMKE.js} +122 -13
- package/chunks/{chunk-T32W2JHI.js → chunk-SS5SDCD3.js} +2 -2
- package/chunks/{chunk-3SGB2UHX.js → chunk-ST5MDGH3.js} +23 -11
- package/chunks/{chunk-MIIVI25Q.js → chunk-ST5V3ZOC.js} +2 -2
- package/chunks/{chunk-4K7NQDCG.js → chunk-T4EFG2P5.js} +18 -9
- package/chunks/{chunk-TWSKM647.js → chunk-T7KMERIX.js} +1 -1
- package/chunks/{chunk-77RG5SFQ.js → chunk-TEBDFQBZ.js} +1 -1
- package/chunks/{chunk-6CGL7E7G.js → chunk-TVC6HQ2U.js} +2 -2
- package/chunks/{chunk-BBWV7ONL.js → chunk-TZBSGJ6C.js} +6 -6
- package/chunks/{chunk-T3WXYEZ4.js → chunk-U54AYGEC.js} +38 -5
- package/chunks/{chunk-JCQD3INC.js → chunk-URE3QBBB.js} +1 -1
- package/chunks/{chunk-Y3QL45LS.js → chunk-V4264FEG.js} +8 -8
- package/chunks/{chunk-KEKZFOL6.js → chunk-V7EESTJV.js} +1 -1
- package/chunks/{chunk-R4PMVSR5.js → chunk-VBCPNIQD.js} +18 -20
- package/chunks/{chunk-RJC5EIKY.js → chunk-VGIFXISB.js} +3 -3
- package/chunks/{chunk-Z7FLBQPR.js → chunk-W3JH33JE.js} +7 -7
- package/chunks/{chunk-K2NQPH5D.js → chunk-WGY3AVGM.js} +3 -3
- package/chunks/{chunk-PKAYJDB3.js → chunk-WNPDGUJO.js} +21 -2
- package/chunks/{chunk-IZIVM7LZ.js → chunk-WXEA74YB.js} +1 -1
- package/chunks/{chunk-YRLW2MSX.js → chunk-XBVNNDPK.js} +11 -4
- package/chunks/{chunk-IK5BW3CS.js → chunk-XHF7WQ6B.js} +2 -2
- package/chunks/{chunk-2NYCN6F2.js → chunk-XSG6G3G6.js} +43 -26
- package/chunks/{chunk-HVQ3B7FF.js → chunk-Y5S7LBOZ.js} +43 -6
- package/chunks/{chunk-LXIBMVRN.js → chunk-YLSQLSH7.js} +13 -7
- package/chunks/{chunk-JSC3H3TD.js → chunk-ZEYFMJQA.js} +14 -6
- package/chunks/chunk-ZWJDJ6RU.js +2597 -0
- package/chunks/{chunk-QP4C6WCE.js → chunk-ZYMLPEZQ.js} +2 -2
- package/chunks/{chunk-BNMF3K5X.js → chunk-ZZMSC3RG.js} +52 -36
- package/chunks/{chunk-XDLEZ5HX.js → chunk-ZZUKP7HV.js} +1 -1
- package/chunks/{config-utils-BBOCOGZW.js → config-utils-R6M3TA34.js} +39 -39
- package/chunks/{contextCommand-EET67MQ2.js → contextCommand-ZCAP23J6.js} +36 -38
- package/chunks/{core-runtime-677KFX6Q.js → core-runtime-AXB6SK5D.js} +41 -43
- package/chunks/{create-sub-session-JKZAYZX7.js → create-sub-session-4QNE64MR.js} +34 -36
- package/chunks/{daemon-MJQUO7RA.js → daemon-3GNHB3M2.js} +495 -7
- package/chunks/{daemon-git-worktree-guard-6MVAAZZZ.js → daemon-git-worktree-guard-IVAJJWYK.js} +33 -35
- package/chunks/{daemon-status-provider-RFKZOK3S.js → daemon-status-provider-K3SJ5C2U.js} +42 -44
- package/chunks/{daemon-trust-policy-DRZPFB4Q.js → daemon-trust-policy-DOR3YG62.js} +41 -43
- package/chunks/{daemon-trust-policy-monitor-B5U6TQX5.js → daemon-trust-policy-monitor-KGGROSIV.js} +41 -43
- package/chunks/{deferred-core-runtime-BBVIOP24.js → deferred-core-runtime-WOYLUQQ7.js} +33 -35
- package/chunks/{dist-DJBNNPLN.js → dist-4WXLCBDR.js} +1 -1
- package/chunks/{dist-TJUDJTCB.js → dist-5VXFRTWU.js} +280 -48
- package/chunks/{dist-QODVOVWI.js → dist-6BPYWHVD.js} +31 -30
- package/chunks/{dist-ZBVOXSKF.js → dist-GFN5DCBX.js} +74 -65
- package/chunks/{dist-SATKXKUR.js → dist-HZNT6GQS.js} +1 -1
- package/chunks/{dist-ILDBUWDB.js → dist-LVZCXVPF.js} +1 -1
- package/chunks/{dist-7CKN54NL.js → dist-N6W6E753.js} +71 -64
- package/chunks/{dist-4Q7NTUNB.js → dist-NSV4NBPK.js} +31 -14
- package/chunks/{dist-UQUFZDIY.js → dist-SWQMYP2Q.js} +18 -16
- package/chunks/{earlyInputCapture-2JOMMNQ4.js → earlyInputCapture-OHBEVO4K.js} +33 -35
- package/chunks/{edit-BSV6YRVW.js → edit-LEQLAGPZ.js} +24 -26
- package/chunks/{enter-worktree-MQXFFPKM.js → enter-worktree-35QCAAFP.js} +3 -3
- package/chunks/{enterPlanMode-PKNF35FE.js → enterPlanMode-ONQN3WHE.js} +27 -29
- package/chunks/{environment-MOZMPUHR.js → environment-UANTR7A4.js} +37 -39
- package/chunks/{errors-DMPQVYJL.js → errors-WAK6KFBC.js} +36 -38
- package/chunks/{exit-worktree-L344UY7K.js → exit-worktree-XQD6D543.js} +3 -3
- package/chunks/{exitPlanMode-UE4FGCY4.js → exitPlanMode-UEYBG6P5.js} +24 -26
- package/chunks/{fast-path-773PV7PU.js → fast-path-WUL7UZ6H.js} +4 -4
- package/chunks/{fast-path-settings-XMFBYFUH.js → fast-path-settings-XH426LUU.js} +3 -3
- package/chunks/{glob-CZHOWUEA.js → glob-Y54QUWDY.js} +24 -26
- package/chunks/{goal-tools-YZQJXTGH.js → goal-tools-PTD3MBA7.js} +2 -1
- package/chunks/{handleAutoUpdate-SGXKTQM6.js → handleAutoUpdate-FP3NWNSG.js} +37 -39
- package/chunks/{i18n-B42HKHKH.js → i18n-INIU5X7A.js} +35 -37
- package/chunks/{image-gen-HAYOEQ4O.js → image-gen-J6FDGUZP.js} +4 -6
- package/chunks/{initializer-FB66WYR3.js → initializer-TYODV6GE.js} +41 -43
- package/chunks/{installationInfo-Y7AULNMX.js → installationInfo-AAPKYDSZ.js} +33 -35
- package/chunks/{list-2HWGSVRA.js → list-TUODDN6J.js} +44 -46
- package/chunks/{gemini-IP4FJAP5.js → llm-NSAVK5V3.js} +84 -82
- package/chunks/{geminiContentGenerator-PUNEBWJW.js → llm-content-generator-AY2QKXHE.js} +25 -25
- package/chunks/{loadedSettingsAdapter-7Y7IEI6T.js → loadedSettingsAdapter-KJCLZ3C4.js} +41 -43
- package/chunks/{loggingContentGenerator-2GGZNGLG.js → loggingContentGenerator-DSFQHXIF.js} +305 -27
- package/chunks/{managed-npm-update-5TEV2E3J.js → managed-npm-update-TSWENTK5.js} +33 -35
- package/chunks/{mcp-QGG5NMJJ.js → mcp-MWSAPFOL.js} +41 -43
- package/chunks/{monitor-FI7PY36Z.js → monitor-RW4GWJHB.js} +24 -26
- package/chunks/{nonInteractiveCli-4LO2TL72.js → nonInteractiveCli-JXKL343Q.js} +77 -78
- package/chunks/{notebook-edit-TPGMGBST.js → notebook-edit-CQNYS5OR.js} +24 -26
- package/chunks/{open-with-auth-4UI3KHUR.js → open-with-auth-YN3QZHVM.js} +1 -1
- package/chunks/{openaiContentGenerator-SUCYANUZ.js → openaiContentGenerator-J6QIF7EX.js} +13 -15
- package/chunks/{pidfile-JMF2CLMY.js → pidfile-QOIQZFT2.js} +33 -35
- package/chunks/process-registry-OAEG6WGC.js +662 -0
- package/chunks/{processUtils-IQZBYWSB.js → processUtils-GPHNANKZ.js} +2 -2
- package/chunks/{prompt-terminal-ledger-DCZO6S7C.js → prompt-terminal-ledger-FOMFTYOM.js} +34 -36
- package/chunks/{qwenContentGenerator-J4TXFGJT.js → qwenContentGenerator-WWDXPYQI.js} +28 -30
- package/chunks/{qwenOAuth2-RF33DNZW.js → qwenOAuth2-OF5WRBQT.js} +4 -6
- package/chunks/{read-file-QE2B5FTX.js → read-file-FQUQHC6N.js} +5 -5
- package/chunks/{record-artifact-RZGZBENU.js → record-artifact-VW23GS4F.js} +2 -2
- package/chunks/{report-findings-Q3SYKKW5.js → report-findings-AO5VYFLR.js} +7 -3
- package/chunks/{resumeHistoryUtils-CZ4JRDVW.js → resumeHistoryUtils-NSGF3UCB.js} +37 -39
- package/chunks/{ripGrep-R6B6HWPX.js → ripGrep-4CAT6SKV.js} +7 -7
- package/chunks/{run-qwen-serve-TPHZMSXJ.js → run-qwen-serve-5X6IDZER.js} +271 -86
- package/chunks/{runtime-TTIGSBM4.js → runtime-HK27GMPF.js} +46 -48
- package/chunks/{scheduled-tasks-QB4ZVUQS.js → scheduled-tasks-MN2ZZ5P6.js} +39 -41
- package/chunks/{scheduler-YRUG22M7.js → scheduler-WD4MHMO5.js} +35 -37
- package/chunks/{sdk-exporters-http-KCJ4ZFG7.js → sdk-exporters-http-TTU2CHBU.js} +1 -1
- package/chunks/{sdk-impl-HLSEDAEP.js → sdk-impl-UUMVYMAL.js} +10 -4
- package/chunks/{send-message-Y5I6NYAM.js → send-message-AJ4OCUBV.js} +18 -2
- package/chunks/{serve-MXXCDAQN.js → serve-2XCHPPGG.js} +41 -43
- package/chunks/{server-DFUSSSHI.js → server-GO2N5DH7.js} +4148 -3348
- package/chunks/{session-3TEHNENZ.js → session-IFNHTUBT.js} +81 -82
- package/chunks/session-pr-refresh-I3UBWMVP.js +364 -0
- package/chunks/{settings-VOZYKLTT.js → settings-L6N54CU6.js} +40 -42
- package/chunks/{shell-6RTKZVMI.js → shell-4Y5LLW6N.js} +24 -26
- package/chunks/{skill-FEOKFX26.js → skill-YOWTRAKQ.js} +58 -14
- package/chunks/{skill-settings-VA4XVYVL.js → skill-settings-ZL4JY5UE.js} +41 -43
- package/chunks/{spawnChannel-F6WVP723.js → spawnChannel-ZST6XJVJ.js} +35 -37
- package/chunks/{standalone-update-RGILUQVK.js → standalone-update-THVU3OKY.js} +36 -38
- package/chunks/{startInteractiveUI-WHJPFOGH.js → startInteractiveUI-XDFSHBZD.js} +401 -334
- package/chunks/{stdioHelpers-7A2EE64Z.js → stdioHelpers-AY6XCDY2.js} +3 -1
- package/chunks/{task-create-TMSV7RD5.js → task-create-QN4LYRSE.js} +3 -3
- package/chunks/{task-list-W55RBFRX.js → task-list-JPLGK45U.js} +2 -2
- package/chunks/{task-update-N536I4GQ.js → task-update-IWEOJFV2.js} +3 -3
- package/chunks/{team-create-QYSWCYCL.js → team-create-OOPXCDBA.js} +24 -26
- package/chunks/{team-delete-FJRFX33M.js → team-delete-RZVVSGCY.js} +2 -2
- package/chunks/{team-plan-approval-P7DAD644.js → team-plan-approval-DSX5EWAT.js} +24 -26
- package/chunks/{terminal-image-renderer-SMQE46UJ.js → terminal-image-renderer-KS25G33E.js} +33 -35
- package/chunks/{theme-manager-RFO5WWFF.js → theme-manager-YMZNG45F.js} +33 -35
- package/chunks/{tool-search-F2EMIAPV.js → tool-search-J5Q6U7DN.js} +9 -9
- package/chunks/{total-session-admission-HTVTP4BS.js → total-session-admission-PVN64JVV.js} +37 -39
- package/chunks/{trustedFolders-7OCNMUQE.js → trustedFolders-FATSC4YI.js} +35 -37
- package/chunks/{types-WYCFCEHR.js → types-HKLPXZB3.js} +1 -3
- package/chunks/{update-relaunch-4GF3OXMB.js → update-relaunch-SIKHLHVR.js} +6 -6
- package/chunks/{updateCheck-7QBDXZRT.js → updateCheck-D3YG2WCD.js} +36 -38
- package/chunks/{useAutoAcceptIndicator-UI4SNL2O.js → useAutoAcceptIndicator-ILINOSAS.js} +43 -45
- package/chunks/{validateNonInterActiveAuth-BPPA563H.js → validateNonInterActiveAuth-JUDQP6V4.js} +74 -75
- package/chunks/{version-ANP4TS6O.js → version-C63GDDWN.js} +1 -1
- package/chunks/{web-fetch-QJADSAH3.js → web-fetch-FOQZMNSC.js} +8 -10
- package/chunks/{web-search-7TTQKEKM.js → web-search-PUJTBFUC.js} +5 -7
- package/chunks/{web-shell-static-A6QMUNNV.js → web-shell-static-HNB4FDOE.js} +2 -2
- package/chunks/{workflow-5XN6XSOT.js → workflow-Y5IPINVH.js} +47 -58
- package/chunks/{workspace-providers-status-PDFZWL5Q.js → workspace-providers-status-IJ4VHFMB.js} +45 -47
- package/chunks/{workspace-registration-store-XKVW2YVH.js → workspace-registration-store-SMJWI2AI.js} +1 -1
- package/chunks/{workspace-registry-23R7XRB5.js → workspace-registry-BTGZIMPK.js} +37 -39
- package/chunks/{workspace-service-MUYO2QIC.js → workspace-service-SIN67ITC.js} +46 -51
- package/chunks/{workspace-skills-status-IKECTOOQ.js → workspace-skills-status-F3RKCUIX.js} +44 -46
- package/chunks/{workspace-trust-reconciler-KLGOB53D.js → workspace-trust-reconciler-PLIDLJZX.js} +45 -47
- package/chunks/{write-file-GGRBB5TZ.js → write-file-PAIGB33C.js} +26 -28
- package/chunks/{zoom-image-6ASG6J4C.js → zoom-image-LYQD7BQA.js} +5 -5
- package/cli.js +14 -14
- package/package.json +3 -3
- package/web-shell/assets/{abnfDiagram-VCTEODGH-uS-vtfs3.js → abnfDiagram-VCTEODGH-TRCMSA_6.js} +1 -1
- package/web-shell/assets/{arc-BSTnk5pu.js → arc-D9Uls5b3.js} +1 -1
- package/web-shell/assets/{architectureDiagram-5GKGNRK7-DCMyiqzy.js → architectureDiagram-5GKGNRK7-Bwb8jPXg.js} +1 -1
- package/web-shell/assets/{blockDiagram-NRAW4CY4-C7_gt0r1.js → blockDiagram-NRAW4CY4-DHj71TSX.js} +1 -1
- package/web-shell/assets/{c4Diagram-UCG6FXSJ-BeQg2L8B.js → c4Diagram-UCG6FXSJ-uKBBdbyr.js} +1 -1
- package/web-shell/assets/channel-CXjVLaBa.js +1 -0
- package/web-shell/assets/{chunk-2Q5K7J3B-DnV9bKBe.js → chunk-2Q5K7J3B-BZd0Phrl.js} +1 -1
- package/web-shell/assets/{chunk-5VM5RSS4-rT1GAcT7.js → chunk-5VM5RSS4-7KNeNBqN.js} +1 -1
- package/web-shell/assets/{chunk-F27PBJKO-BcgSxqGA.js → chunk-F27PBJKO-BeboS7Fi.js} +1 -1
- package/web-shell/assets/{chunk-G27WJ6UU-Bw1ZhyZq.js → chunk-G27WJ6UU-RV3JONoj.js} +1 -1
- package/web-shell/assets/{chunk-JWPE2WC7-D4iDw-96.js → chunk-JWPE2WC7-Yw9FCFsP.js} +1 -1
- package/web-shell/assets/{chunk-LCL6LL3I-D3MB3GTZ.js → chunk-LCL6LL3I-GIMnydDe.js} +1 -1
- package/web-shell/assets/{chunk-POPQ4Y6H-Cep2ndrS.js → chunk-POPQ4Y6H-Cfrux7bL.js} +1 -1
- package/web-shell/assets/{chunk-SVP7TREG-BuahMHJc.js → chunk-SVP7TREG-BlvIN0CO.js} +1 -1
- package/web-shell/assets/{chunk-XXDRQBXY-BdujIvJY.js → chunk-XXDRQBXY-DMs7Rr4t.js} +1 -1
- package/web-shell/assets/classDiagram-DTDB5LWJ-DANEijO3.js +1 -0
- package/web-shell/assets/classDiagram-v2-JRS7N3AN-DANEijO3.js +1 -0
- package/web-shell/assets/{cose-bilkent-JH36ORCC-CGLLUbF8.js → cose-bilkent-JH36ORCC-DNGqTNwI.js} +1 -1
- package/web-shell/assets/{cynefin-OW5HDTMX-D6kDsRSh.js → cynefin-OW5HDTMX-CojMpaoH.js} +1 -1
- package/web-shell/assets/{cynefinDiagram-5FMLGOSQ-CCy78qZx.js → cynefinDiagram-5FMLGOSQ-DF07YXGo.js} +1 -1
- package/web-shell/assets/{dagre-3AP2YEHR-D5gJuJsf.js → dagre-3AP2YEHR-17-4WtCH.js} +1 -1
- package/web-shell/assets/{diagram-S7CK7UJ4-ZwEZ5W__.js → diagram-S7CK7UJ4-G3Wuyz9m.js} +1 -1
- package/web-shell/assets/{diagram-UQ7AKVKN-B036APjv.js → diagram-UQ7AKVKN-nyWIKZ8P.js} +1 -1
- package/web-shell/assets/{diagram-VSXAHHWV-BfDJ57Fq.js → diagram-VSXAHHWV-tKHhmYSC.js} +1 -1
- package/web-shell/assets/{diagram-VX7I27RA-Wha0p5rT.js → diagram-VX7I27RA-CpDf1hYk.js} +1 -1
- package/web-shell/assets/{diagram-Z3DM3KII-L20bz2K1.js → diagram-Z3DM3KII-Cah2IFXG.js} +1 -1
- package/web-shell/assets/{ebnfDiagram-PWID7BFC-D1ZM0Cp7.js → ebnfDiagram-PWID7BFC-DNaCkgxy.js} +1 -1
- package/web-shell/assets/{erDiagram-SSCWMZ5O-CMtA19v8.js → erDiagram-SSCWMZ5O-CAdRgcf1.js} +1 -1
- package/web-shell/assets/{flowDiagram-A5DVABFB-D96m326S.js → flowDiagram-A5DVABFB-CMUJAShL.js} +1 -1
- package/web-shell/assets/{ganttDiagram-EL5Y4UJY-BuHiYG2o.js → ganttDiagram-EL5Y4UJY-o-_MUNey.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-WWUBYQGX-Ck3NmkSw.js → gitGraphDiagram-WWUBYQGX-_aG-2Z7O.js} +1 -1
- package/web-shell/assets/{index-CBGz4Qjl.js → index-BK_NKxiJ.js} +1 -1
- package/web-shell/assets/index-Ci-z4zWY.js +1913 -0
- package/web-shell/assets/index-LDlXZRHA.css +36 -0
- package/web-shell/assets/{infoDiagram-RXCK75RN-B6DAOyXe.js → infoDiagram-RXCK75RN-qJPeZbGP.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-5VMMS53U-Bpg-joFo.js → ishikawaDiagram-5VMMS53U-7Om4vNo1.js} +1 -1
- package/web-shell/assets/{journeyDiagram-EYS64GPL-BclrHzGa.js → journeyDiagram-EYS64GPL-BNtXqmBW.js} +1 -1
- package/web-shell/assets/{kanban-definition-3QL26DDD-5gJzNTYG.js → kanban-definition-3QL26DDD-COgFec8P.js} +1 -1
- package/web-shell/assets/{layout-CEz_1L8z.js → layout-B9n81PC9.js} +1 -1
- package/web-shell/assets/{linear-CBjTq9Hc.js → linear-Bb6nXZQq.js} +1 -1
- package/web-shell/assets/{mermaid.core-Dzhbxx_I.js → mermaid.core-Cgrm3JVT.js} +6 -6
- package/web-shell/assets/{mindmap-definition-FBJOCRG2-DQdVqlBF.js → mindmap-definition-FBJOCRG2-TKwLTjnG.js} +1 -1
- package/web-shell/assets/{pegDiagram-XKGWAZYB-D1JZlYXL.js → pegDiagram-XKGWAZYB-BJPuvlpi.js} +1 -1
- package/web-shell/assets/{pieDiagram-E7YTZNPT-BmkpYL0w.js → pieDiagram-E7YTZNPT-D01VkNsW.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-AXDQQJYC-DRvD6co5.js → quadrantDiagram-AXDQQJYC-C4hHsiv-.js} +1 -1
- package/web-shell/assets/{railroadDiagram-O6MQD6OU-CEdjDuoo.js → railroadDiagram-O6MQD6OU-CuEO4xK2.js} +1 -1
- package/web-shell/assets/{requirementDiagram-EFPCY7ZU-BCHG-ie8.js → requirementDiagram-EFPCY7ZU-D5J_vIo5.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-P5KCCOFB-C9cZMnye.js → sankeyDiagram-P5KCCOFB-DQKY223V.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-WJ2MYXX4-DYHRV4zL.js → sequenceDiagram-WJ2MYXX4-D27YV_cm.js} +1 -1
- package/web-shell/assets/{sizeCapture-X5ZJPWSS-DlK21aye.js → sizeCapture-X5ZJPWSS-TJg8UcwV.js} +1 -1
- package/web-shell/assets/{stateDiagram-HBIQ2CUA-BrmZssvk.js → stateDiagram-HBIQ2CUA-CmnPzUIx.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-4QOOHH4V-DNKPyrDc.js +1 -0
- package/web-shell/assets/{swimlanes-XN3QIQJK-D9xfcayR.js → swimlanes-XN3QIQJK-ByeP3_Nu.js} +1 -1
- package/web-shell/assets/swimlanesDiagram-VK2B7HYN-ZL1HPC6w.js +8 -0
- package/web-shell/assets/{timeline-definition-24CTP7MA-D4gdDL_d.js → timeline-definition-24CTP7MA-BsFA5juR.js} +1 -1
- package/web-shell/assets/{vennDiagram-4TSXK5OY-DdhIExjI.js → vennDiagram-4TSXK5OY-DREP2ECo.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-VM6X3IG4-C9RS_Zof.js → wardleyDiagram-VM6X3IG4-iyd9lKi2.js} +1 -1
- package/web-shell/assets/{xychartDiagram-S5SC5T6Z-orRgmxWk.js → xychartDiagram-S5SC5T6Z-CVr1_j9q.js} +1 -1
- package/web-shell/index.html +3 -3
- package/chunks/chunk-7DJCPZE3.js +0 -945
- package/chunks/chunk-J2OSJFP3.js +0 -202
- package/chunks/chunk-K2OJUPOE.js +0 -78
- package/chunks/chunk-P3QQPMQA.js +0 -19
- package/chunks/process-registry-PMJOA5CO.js +0 -172
- 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,11 @@ 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 approvalMode || loopsDisabled ? {
|
|
1726
|
+
...approvalMode ? { approvalMode } : {},
|
|
1727
|
+
...loopsDisabled ? { enableChannelLoops: false } : {}
|
|
1728
|
+
} : void 0;
|
|
1698
1729
|
}
|
|
1699
1730
|
async resolve(channelName, senderId, chatId, threadId, cwd, isGroup, options) {
|
|
1700
1731
|
const key = this.routingKey(channelName, senderId, chatId, options?.routingThreadId ?? threadId);
|
|
@@ -1881,6 +1912,120 @@ var SessionRouter = class {
|
|
|
1881
1912
|
}
|
|
1882
1913
|
return false;
|
|
1883
1914
|
}
|
|
1915
|
+
getSessionCwd(sessionId) {
|
|
1916
|
+
return this.toCwd.get(sessionId);
|
|
1917
|
+
}
|
|
1918
|
+
async createManagedSession(target, cwd) {
|
|
1919
|
+
const loadWindow = this.beginSessionLoad();
|
|
1920
|
+
const lifecycleGeneration = this.lifecycleGeneration;
|
|
1921
|
+
const bridge = this.bridge;
|
|
1922
|
+
const bindingToken = {};
|
|
1923
|
+
try {
|
|
1924
|
+
let lastDeadSessionId;
|
|
1925
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
1926
|
+
const sessionId = await bridge.newSession(cwd, {
|
|
1927
|
+
...this.sessionOptions(target.channelName),
|
|
1928
|
+
sourceId: target.channelName
|
|
1929
|
+
}, bindingToken);
|
|
1930
|
+
if (lifecycleGeneration !== this.lifecycleGeneration || bridge !== this.bridge) {
|
|
1931
|
+
this.scheduleManagedDiscard(bridge, sessionId, bindingToken);
|
|
1932
|
+
throw new Error("Managed session creation was invalidated");
|
|
1933
|
+
}
|
|
1934
|
+
if (typeof sessionId !== "string" || sessionId.length === 0) {
|
|
1935
|
+
throw new Error("Invalid session ID from bridge");
|
|
1936
|
+
}
|
|
1937
|
+
if (loadWindow.delete(sessionId)) {
|
|
1938
|
+
lastDeadSessionId = sessionId;
|
|
1939
|
+
await bridge.discardSession?.(sessionId, bindingToken);
|
|
1940
|
+
continue;
|
|
1941
|
+
}
|
|
1942
|
+
this.toTarget.set(sessionId, target);
|
|
1943
|
+
this.toCwd.set(sessionId, cwd);
|
|
1944
|
+
this.liveSessionIds.add(sessionId);
|
|
1945
|
+
return sessionId;
|
|
1946
|
+
}
|
|
1947
|
+
throw new Error(`Managed session ${lastDeadSessionId ?? "unknown"} died before creation completed`);
|
|
1948
|
+
} finally {
|
|
1949
|
+
this.endSessionLoad(loadWindow);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
async loadManagedSession(sessionId, target, cwd) {
|
|
1953
|
+
if (this.liveSessionIds.has(sessionId)) {
|
|
1954
|
+
this.toTarget.set(sessionId, target);
|
|
1955
|
+
this.toCwd.set(sessionId, cwd);
|
|
1956
|
+
return { loaded: false };
|
|
1957
|
+
}
|
|
1958
|
+
const loadWindow = this.beginSessionLoad();
|
|
1959
|
+
const lifecycleGeneration = this.lifecycleGeneration;
|
|
1960
|
+
const bridge = this.bridge;
|
|
1961
|
+
const bindingToken = {};
|
|
1962
|
+
let loadedSessionId;
|
|
1963
|
+
try {
|
|
1964
|
+
loadedSessionId = await bridge.loadSession(sessionId, cwd, this.sessionOptions(target.channelName), bindingToken);
|
|
1965
|
+
if (lifecycleGeneration !== this.lifecycleGeneration || bridge !== this.bridge) {
|
|
1966
|
+
this.scheduleManagedDiscard(bridge, loadedSessionId, bindingToken);
|
|
1967
|
+
loadedSessionId = void 0;
|
|
1968
|
+
throw new Error("Managed session load was invalidated");
|
|
1969
|
+
}
|
|
1970
|
+
if (loadedSessionId !== sessionId) {
|
|
1971
|
+
const unexpectedSessionId = loadedSessionId;
|
|
1972
|
+
await bridge.discardSession?.(unexpectedSessionId, bindingToken);
|
|
1973
|
+
loadedSessionId = void 0;
|
|
1974
|
+
throw new Error(`Bridge returned session ${unexpectedSessionId || "unknown"} while loading ${sessionId}`);
|
|
1975
|
+
}
|
|
1976
|
+
if (loadWindow.delete(sessionId)) {
|
|
1977
|
+
throw new Error(`Managed session ${sessionId} died before loading completed`);
|
|
1978
|
+
}
|
|
1979
|
+
this.toTarget.set(sessionId, target);
|
|
1980
|
+
this.toCwd.set(sessionId, cwd);
|
|
1981
|
+
this.liveSessionIds.add(sessionId);
|
|
1982
|
+
return { loaded: true };
|
|
1983
|
+
} catch (error) {
|
|
1984
|
+
if (loadedSessionId) {
|
|
1985
|
+
await bridge.discardSession?.(loadedSessionId, bindingToken).catch(() => void 0);
|
|
1986
|
+
}
|
|
1987
|
+
throw error;
|
|
1988
|
+
} finally {
|
|
1989
|
+
this.endSessionLoad(loadWindow);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
activateManagedSession(sessionId, target, cwd) {
|
|
1993
|
+
const key = this.routingKey(target.channelName, target.senderId, target.chatId, target.threadId);
|
|
1994
|
+
if (this.toSession.get(key) === sessionId)
|
|
1995
|
+
return;
|
|
1996
|
+
this.invalidateRouteOperation(key);
|
|
1997
|
+
this.toSession.set(key, sessionId);
|
|
1998
|
+
this.toTarget.set(sessionId, target);
|
|
1999
|
+
this.toCwd.set(sessionId, cwd);
|
|
2000
|
+
this.persist();
|
|
2001
|
+
}
|
|
2002
|
+
forgetManagedSession(sessionId) {
|
|
2003
|
+
let changed = false;
|
|
2004
|
+
for (const [key, mappedSessionId] of this.toSession) {
|
|
2005
|
+
if (mappedSessionId !== sessionId)
|
|
2006
|
+
continue;
|
|
2007
|
+
this.invalidateRouteOperation(key);
|
|
2008
|
+
this.toSession.delete(key);
|
|
2009
|
+
changed = true;
|
|
2010
|
+
}
|
|
2011
|
+
changed = this.toTarget.delete(sessionId) || changed;
|
|
2012
|
+
changed = this.toCwd.delete(sessionId) || changed;
|
|
2013
|
+
changed = this.liveSessionIds.delete(sessionId) || changed;
|
|
2014
|
+
if (changed)
|
|
2015
|
+
this.persist();
|
|
2016
|
+
}
|
|
2017
|
+
async detachManagedSession(sessionId) {
|
|
2018
|
+
try {
|
|
2019
|
+
if (this.liveSessionIds.has(sessionId)) {
|
|
2020
|
+
if (!this.bridge.discardSession) {
|
|
2021
|
+
throw new Error("Managed session detach is not supported");
|
|
2022
|
+
}
|
|
2023
|
+
await this.bridge.discardSession(sessionId);
|
|
2024
|
+
}
|
|
2025
|
+
} finally {
|
|
2026
|
+
this.forgetManagedSession(sessionId);
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
1884
2029
|
/**
|
|
1885
2030
|
* Remove session(s) for the given sender. Returns the removed session IDs.
|
|
1886
2031
|
*/
|
|
@@ -2055,6 +2200,7 @@ var SessionRouter = class {
|
|
|
2055
2200
|
this.toSession.set(key, sessionId);
|
|
2056
2201
|
this.toTarget.set(sessionId, entry.target);
|
|
2057
2202
|
this.toCwd.set(sessionId, entry.cwd);
|
|
2203
|
+
this.liveSessionIds.add(sessionId);
|
|
2058
2204
|
reservation.resolve(sessionId);
|
|
2059
2205
|
if (sessionId !== entry.sessionId) {
|
|
2060
2206
|
changed = true;
|
|
@@ -2278,6 +2424,12 @@ var SessionRouter = class {
|
|
|
2278
2424
|
} catch {
|
|
2279
2425
|
}
|
|
2280
2426
|
}
|
|
2427
|
+
scheduleManagedDiscard(bridge, sessionId, bindingToken) {
|
|
2428
|
+
try {
|
|
2429
|
+
void bridge.discardSession?.(sessionId, bindingToken).catch(() => void 0);
|
|
2430
|
+
} catch {
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2281
2433
|
createSessionReservation() {
|
|
2282
2434
|
let resolveReservation;
|
|
2283
2435
|
let rejectReservation;
|
|
@@ -2296,6 +2448,615 @@ var SessionRouter = class {
|
|
|
2296
2448
|
}
|
|
2297
2449
|
};
|
|
2298
2450
|
|
|
2451
|
+
// packages/channels/base/dist/named-session-manager.js
|
|
2452
|
+
init_esbuild_shims();
|
|
2453
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
2454
|
+
import { chmodSync as chmodSync3, existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync4, renameSync as renameSync4, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
2455
|
+
import { dirname as dirname4 } from "node:path";
|
|
2456
|
+
import process3 from "node:process";
|
|
2457
|
+
var REGISTRY_VERSION = 1;
|
|
2458
|
+
var MAX_OPEN_TASKS = 8;
|
|
2459
|
+
var TASK_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]{0,31}$/;
|
|
2460
|
+
var NamedSessionManager = class {
|
|
2461
|
+
static {
|
|
2462
|
+
__name(this, "NamedSessionManager");
|
|
2463
|
+
}
|
|
2464
|
+
channelName;
|
|
2465
|
+
cwd;
|
|
2466
|
+
canonicalCwd;
|
|
2467
|
+
filePath;
|
|
2468
|
+
router;
|
|
2469
|
+
isBusy;
|
|
2470
|
+
now;
|
|
2471
|
+
registry;
|
|
2472
|
+
ownerOperations = /* @__PURE__ */ new Map();
|
|
2473
|
+
constructor(options) {
|
|
2474
|
+
this.channelName = options.channelName;
|
|
2475
|
+
this.cwd = options.cwd;
|
|
2476
|
+
this.canonicalCwd = canonicalizeWorkspacePath(options.cwd);
|
|
2477
|
+
this.filePath = options.filePath;
|
|
2478
|
+
this.router = options.router;
|
|
2479
|
+
this.isBusy = options.isBusy;
|
|
2480
|
+
this.now = options.now ?? Date.now;
|
|
2481
|
+
this.registry = this.readRegistry();
|
|
2482
|
+
}
|
|
2483
|
+
resolve(input, expectedSessionId, reserve) {
|
|
2484
|
+
return this.withOwnerLock(input, () => this.resolveLocked(input, expectedSessionId, reserve));
|
|
2485
|
+
}
|
|
2486
|
+
resolveAfterPreparation(input, prepare, needsSession, reserve) {
|
|
2487
|
+
return this.withOwnerLock(input, async () => {
|
|
2488
|
+
if (await prepare() === false)
|
|
2489
|
+
return { status: "aborted" };
|
|
2490
|
+
if (!needsSession())
|
|
2491
|
+
return { status: "bypassed" };
|
|
2492
|
+
try {
|
|
2493
|
+
return {
|
|
2494
|
+
status: "resolved",
|
|
2495
|
+
sessionId: await this.resolveLocked(input, void 0, reserve)
|
|
2496
|
+
};
|
|
2497
|
+
} catch (error) {
|
|
2498
|
+
return { status: "resolve_error", error };
|
|
2499
|
+
}
|
|
2500
|
+
});
|
|
2501
|
+
}
|
|
2502
|
+
list(input, includeClosed) {
|
|
2503
|
+
return this.withOwnerLock(input, async () => {
|
|
2504
|
+
const owner = await this.ensureOwner(input, false);
|
|
2505
|
+
if (!owner)
|
|
2506
|
+
return [];
|
|
2507
|
+
return owner.tasks.filter((task) => includeClosed || task.status === "open").map((task) => this.view(owner, task));
|
|
2508
|
+
});
|
|
2509
|
+
}
|
|
2510
|
+
current(input) {
|
|
2511
|
+
return this.withOwnerLock(input, async () => {
|
|
2512
|
+
const owner = await this.ensureOwner(input, false);
|
|
2513
|
+
if (!owner?.activeTaskName)
|
|
2514
|
+
return void 0;
|
|
2515
|
+
const task = this.findTask(owner, owner.activeTaskName);
|
|
2516
|
+
return task ? this.selection(owner, task) : void 0;
|
|
2517
|
+
});
|
|
2518
|
+
}
|
|
2519
|
+
create(input, name) {
|
|
2520
|
+
return this.withOwnerLock(input, async () => {
|
|
2521
|
+
this.validateName(name);
|
|
2522
|
+
const existingOwner = await this.ensureOwner(input, false);
|
|
2523
|
+
const owner = existingOwner ?? this.createOwner(input, null, []);
|
|
2524
|
+
if (this.findTask(owner, name)) {
|
|
2525
|
+
throw new Error(`Task "${name}" already exists.`);
|
|
2526
|
+
}
|
|
2527
|
+
if (this.openTaskCount(owner) >= MAX_OPEN_TASKS) {
|
|
2528
|
+
throw new Error("You already have eight open tasks. Close one before creating another.");
|
|
2529
|
+
}
|
|
2530
|
+
this.assertCanLeaveSelected(owner, void 0);
|
|
2531
|
+
const timestamp = this.nextTimestamp(owner);
|
|
2532
|
+
const target = this.target(input);
|
|
2533
|
+
const sessionId = await this.createSession(target, this.cwd, `Could not create task "${name}".`);
|
|
2534
|
+
const task = {
|
|
2535
|
+
name,
|
|
2536
|
+
sessionId,
|
|
2537
|
+
cwd: this.cwd,
|
|
2538
|
+
isolation: "shared",
|
|
2539
|
+
status: "open",
|
|
2540
|
+
target,
|
|
2541
|
+
createdAt: timestamp,
|
|
2542
|
+
updatedAt: timestamp,
|
|
2543
|
+
lastSelectedAt: timestamp
|
|
2544
|
+
};
|
|
2545
|
+
const nextOwner = {
|
|
2546
|
+
...owner,
|
|
2547
|
+
activeTaskName: name,
|
|
2548
|
+
tasks: [...owner.tasks, task]
|
|
2549
|
+
};
|
|
2550
|
+
try {
|
|
2551
|
+
this.commitOwner(nextOwner);
|
|
2552
|
+
} catch (error) {
|
|
2553
|
+
await this.router.detachManagedSession(sessionId).catch(() => void 0);
|
|
2554
|
+
throw error;
|
|
2555
|
+
}
|
|
2556
|
+
this.router.activateManagedSession(sessionId, target, this.cwd);
|
|
2557
|
+
return this.selection(nextOwner, task);
|
|
2558
|
+
});
|
|
2559
|
+
}
|
|
2560
|
+
use(input, name) {
|
|
2561
|
+
return this.withOwnerLock(input, async () => {
|
|
2562
|
+
const owner = await this.ensureOwner(input, false);
|
|
2563
|
+
if (!owner)
|
|
2564
|
+
throw new Error("No named tasks exist in this chat.");
|
|
2565
|
+
const task = this.findTask(owner, name);
|
|
2566
|
+
if (!task)
|
|
2567
|
+
throw new Error(`Task "${name}" was not found.`);
|
|
2568
|
+
this.assertCanLeaveSelected(owner, task.name);
|
|
2569
|
+
if (this.isBusy(task.sessionId)) {
|
|
2570
|
+
throw new Error(`Task "${task.name}" is still running or waiting for permission.`);
|
|
2571
|
+
}
|
|
2572
|
+
if (task.status === "closed" && this.openTaskCount(owner) >= MAX_OPEN_TASKS) {
|
|
2573
|
+
throw new Error("You already have eight open tasks. Close one before reopening another.");
|
|
2574
|
+
}
|
|
2575
|
+
const timestamp = this.nextTimestamp(owner);
|
|
2576
|
+
const loaded = await this.loadTask(task, `Could not load task "${task.name}". The current task was not changed.`);
|
|
2577
|
+
const updatedTask = {
|
|
2578
|
+
...task,
|
|
2579
|
+
status: "open",
|
|
2580
|
+
updatedAt: timestamp,
|
|
2581
|
+
lastSelectedAt: timestamp
|
|
2582
|
+
};
|
|
2583
|
+
const nextOwner = this.replaceTask(owner, updatedTask, task.name);
|
|
2584
|
+
try {
|
|
2585
|
+
this.commitOwner(nextOwner);
|
|
2586
|
+
} catch (error) {
|
|
2587
|
+
if (loaded) {
|
|
2588
|
+
await this.router.detachManagedSession(task.sessionId).catch(() => void 0);
|
|
2589
|
+
}
|
|
2590
|
+
throw error;
|
|
2591
|
+
}
|
|
2592
|
+
this.router.activateManagedSession(task.sessionId, task.target, task.cwd);
|
|
2593
|
+
return this.selection(nextOwner, updatedTask);
|
|
2594
|
+
});
|
|
2595
|
+
}
|
|
2596
|
+
close(input, name) {
|
|
2597
|
+
return this.withOwnerLock(input, async () => {
|
|
2598
|
+
const owner = await this.ensureOwner(input, false);
|
|
2599
|
+
if (!owner)
|
|
2600
|
+
throw new Error("No named tasks exist in this chat.");
|
|
2601
|
+
const task = this.findTask(owner, name);
|
|
2602
|
+
if (!task)
|
|
2603
|
+
throw new Error(`Task "${name}" was not found.`);
|
|
2604
|
+
if (task.status === "closed") {
|
|
2605
|
+
throw new Error(`Task "${task.name}" is already closed.`);
|
|
2606
|
+
}
|
|
2607
|
+
if (this.isBusy(task.sessionId)) {
|
|
2608
|
+
throw new Error(`Task "${task.name}" is still running or waiting for permission.`);
|
|
2609
|
+
}
|
|
2610
|
+
const wasActive = this.sameName(owner.activeTaskName, task.name);
|
|
2611
|
+
const timestamp = this.nextTimestamp(owner);
|
|
2612
|
+
const replacement = wasActive ? owner.tasks.filter((candidate) => candidate.status === "open" && !this.sameName(candidate.name, task.name)).sort((a, b) => b.lastSelectedAt - a.lastSelectedAt)[0] : void 0;
|
|
2613
|
+
let replacementLoaded = false;
|
|
2614
|
+
if (replacement) {
|
|
2615
|
+
if (this.isBusy(replacement.sessionId)) {
|
|
2616
|
+
throw new Error(`Task "${replacement.name}" is still running or waiting for permission.`);
|
|
2617
|
+
}
|
|
2618
|
+
replacementLoaded = await this.loadTask(replacement, `Could not load fallback task "${replacement.name}". Task "${task.name}" was not closed.`);
|
|
2619
|
+
}
|
|
2620
|
+
const closedTask = {
|
|
2621
|
+
...task,
|
|
2622
|
+
status: "closed",
|
|
2623
|
+
updatedAt: timestamp
|
|
2624
|
+
};
|
|
2625
|
+
let nextOwner = this.replaceTask(owner, closedTask, wasActive ? replacement?.name ?? null : owner.activeTaskName);
|
|
2626
|
+
const selectedReplacement = replacement ? {
|
|
2627
|
+
...replacement,
|
|
2628
|
+
updatedAt: timestamp,
|
|
2629
|
+
lastSelectedAt: timestamp
|
|
2630
|
+
} : void 0;
|
|
2631
|
+
if (selectedReplacement) {
|
|
2632
|
+
nextOwner = this.replaceTask(nextOwner, selectedReplacement, selectedReplacement.name);
|
|
2633
|
+
}
|
|
2634
|
+
try {
|
|
2635
|
+
this.commitOwner(nextOwner);
|
|
2636
|
+
} catch (error) {
|
|
2637
|
+
if (replacement && replacementLoaded) {
|
|
2638
|
+
await this.router.detachManagedSession(replacement.sessionId).catch(() => void 0);
|
|
2639
|
+
}
|
|
2640
|
+
throw error;
|
|
2641
|
+
}
|
|
2642
|
+
if (replacement) {
|
|
2643
|
+
this.router.activateManagedSession(replacement.sessionId, replacement.target, replacement.cwd);
|
|
2644
|
+
}
|
|
2645
|
+
try {
|
|
2646
|
+
await this.router.detachManagedSession(task.sessionId);
|
|
2647
|
+
} catch (error) {
|
|
2648
|
+
this.commitOwner(owner);
|
|
2649
|
+
this.router.forgetManagedSession(task.sessionId);
|
|
2650
|
+
await this.loadTask(task, `Task "${task.name}" could not be restored after close failed.`);
|
|
2651
|
+
if (wasActive) {
|
|
2652
|
+
this.router.activateManagedSession(task.sessionId, task.target, task.cwd);
|
|
2653
|
+
}
|
|
2654
|
+
throw new Error(`Failed to close task "${task.name}".`, {
|
|
2655
|
+
cause: error
|
|
2656
|
+
});
|
|
2657
|
+
}
|
|
2658
|
+
const activeTask = nextOwner.activeTaskName ? this.findTask(nextOwner, nextOwner.activeTaskName) : void 0;
|
|
2659
|
+
return {
|
|
2660
|
+
closed: this.view(nextOwner, closedTask),
|
|
2661
|
+
...activeTask ? { active: this.view(nextOwner, activeTask) } : {}
|
|
2662
|
+
};
|
|
2663
|
+
});
|
|
2664
|
+
}
|
|
2665
|
+
reset(input) {
|
|
2666
|
+
return this.withOwnerLock(input, async () => {
|
|
2667
|
+
const owner = await this.ensureOwner(input, false);
|
|
2668
|
+
if (!owner?.activeTaskName)
|
|
2669
|
+
return void 0;
|
|
2670
|
+
const task = this.findTask(owner, owner.activeTaskName);
|
|
2671
|
+
if (!task || task.status !== "open")
|
|
2672
|
+
return void 0;
|
|
2673
|
+
const timestamp = this.nextTimestamp(owner);
|
|
2674
|
+
const sessionId = await this.createSession(task.target, task.cwd, `Could not reset task "${task.name}".`);
|
|
2675
|
+
const updatedTask = {
|
|
2676
|
+
...task,
|
|
2677
|
+
sessionId,
|
|
2678
|
+
updatedAt: timestamp,
|
|
2679
|
+
lastSelectedAt: timestamp
|
|
2680
|
+
};
|
|
2681
|
+
const nextOwner = this.replaceTask(owner, updatedTask, task.name);
|
|
2682
|
+
try {
|
|
2683
|
+
this.commitOwner(nextOwner);
|
|
2684
|
+
} catch (error) {
|
|
2685
|
+
await this.router.detachManagedSession(sessionId).catch(() => void 0);
|
|
2686
|
+
throw error;
|
|
2687
|
+
}
|
|
2688
|
+
this.router.activateManagedSession(sessionId, updatedTask.target, updatedTask.cwd);
|
|
2689
|
+
this.router.forgetManagedSession(task.sessionId);
|
|
2690
|
+
return {
|
|
2691
|
+
name: task.name,
|
|
2692
|
+
previousSessionId: task.sessionId,
|
|
2693
|
+
sessionId
|
|
2694
|
+
};
|
|
2695
|
+
});
|
|
2696
|
+
}
|
|
2697
|
+
async ensureOwner(input, createDefault) {
|
|
2698
|
+
const existing = this.getOwner(input);
|
|
2699
|
+
if (existing)
|
|
2700
|
+
return existing;
|
|
2701
|
+
const sessionId = this.router.getSession(this.channelName, input.senderId, input.chatId);
|
|
2702
|
+
const routedTarget = sessionId ? this.router.getTarget(sessionId) : void 0;
|
|
2703
|
+
if (sessionId && routedTarget && (routedTarget.channelName !== this.channelName || routedTarget.chatId !== input.chatId || routedTarget.senderId !== input.senderId)) {
|
|
2704
|
+
this.adoptForeignLegacyRoute(sessionId, routedTarget);
|
|
2705
|
+
}
|
|
2706
|
+
if (sessionId && routedTarget?.channelName === this.channelName && routedTarget.chatId === input.chatId && routedTarget.senderId === input.senderId) {
|
|
2707
|
+
const routedCwd = this.router.getSessionCwd(sessionId);
|
|
2708
|
+
if (routedCwd !== void 0 && !this.isCurrentCwd(routedCwd)) {
|
|
2709
|
+
await this.router.detachManagedSession(sessionId).catch(() => void 0);
|
|
2710
|
+
} else {
|
|
2711
|
+
const timestamp2 = this.nextTimestamp();
|
|
2712
|
+
const task2 = {
|
|
2713
|
+
name: "default",
|
|
2714
|
+
sessionId,
|
|
2715
|
+
cwd: routedCwd ?? this.cwd,
|
|
2716
|
+
isolation: "shared",
|
|
2717
|
+
status: "open",
|
|
2718
|
+
target: routedTarget,
|
|
2719
|
+
createdAt: timestamp2,
|
|
2720
|
+
updatedAt: timestamp2,
|
|
2721
|
+
lastSelectedAt: timestamp2
|
|
2722
|
+
};
|
|
2723
|
+
const adopted = this.createOwner(input, task2.name, [task2]);
|
|
2724
|
+
this.commitOwner(adopted);
|
|
2725
|
+
return adopted;
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
if (!createDefault)
|
|
2729
|
+
return void 0;
|
|
2730
|
+
const target = this.target(input);
|
|
2731
|
+
const timestamp = this.nextTimestamp();
|
|
2732
|
+
const createdSessionId = await this.createSession(target, this.cwd, "Could not create the default task.");
|
|
2733
|
+
const task = {
|
|
2734
|
+
name: "default",
|
|
2735
|
+
sessionId: createdSessionId,
|
|
2736
|
+
cwd: this.cwd,
|
|
2737
|
+
isolation: "shared",
|
|
2738
|
+
status: "open",
|
|
2739
|
+
target,
|
|
2740
|
+
createdAt: timestamp,
|
|
2741
|
+
updatedAt: timestamp,
|
|
2742
|
+
lastSelectedAt: timestamp
|
|
2743
|
+
};
|
|
2744
|
+
const created = this.createOwner(input, task.name, [task]);
|
|
2745
|
+
try {
|
|
2746
|
+
this.commitOwner(created);
|
|
2747
|
+
} catch (error) {
|
|
2748
|
+
await this.router.detachManagedSession(createdSessionId).catch(() => void 0);
|
|
2749
|
+
throw error;
|
|
2750
|
+
}
|
|
2751
|
+
this.router.activateManagedSession(createdSessionId, target, this.cwd);
|
|
2752
|
+
return created;
|
|
2753
|
+
}
|
|
2754
|
+
async resolveLocked(input, expectedSessionId, reserve) {
|
|
2755
|
+
const owner = await this.ensureOwner(input, true);
|
|
2756
|
+
if (!owner?.activeTaskName)
|
|
2757
|
+
return void 0;
|
|
2758
|
+
const task = this.findTask(owner, owner.activeTaskName);
|
|
2759
|
+
if (!task || task.status !== "open") {
|
|
2760
|
+
throw new Error("The selected Channel task is unavailable.");
|
|
2761
|
+
}
|
|
2762
|
+
if (expectedSessionId !== void 0 && task.sessionId !== expectedSessionId) {
|
|
2763
|
+
return void 0;
|
|
2764
|
+
}
|
|
2765
|
+
const release = reserve?.(task.sessionId);
|
|
2766
|
+
try {
|
|
2767
|
+
await this.loadTask(task, `Could not load task "${task.name}". No replacement session was created.`);
|
|
2768
|
+
this.router.activateManagedSession(task.sessionId, task.target, task.cwd);
|
|
2769
|
+
return task.sessionId;
|
|
2770
|
+
} catch (error) {
|
|
2771
|
+
release?.();
|
|
2772
|
+
throw error;
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
adoptForeignLegacyRoute(sessionId, target) {
|
|
2776
|
+
const cwd = this.router.getSessionCwd(sessionId);
|
|
2777
|
+
if (target.channelName !== this.channelName || cwd === void 0 || !this.isCurrentCwd(cwd)) {
|
|
2778
|
+
this.router.forgetManagedSession(sessionId);
|
|
2779
|
+
return;
|
|
2780
|
+
}
|
|
2781
|
+
const existing = this.getOwner(target);
|
|
2782
|
+
if (existing) {
|
|
2783
|
+
if (existing.tasks.some((task2) => task2.sessionId === sessionId))
|
|
2784
|
+
return;
|
|
2785
|
+
this.router.forgetManagedSession(sessionId);
|
|
2786
|
+
return;
|
|
2787
|
+
}
|
|
2788
|
+
const timestamp = this.nextTimestamp();
|
|
2789
|
+
const task = {
|
|
2790
|
+
name: "default",
|
|
2791
|
+
sessionId,
|
|
2792
|
+
cwd,
|
|
2793
|
+
isolation: "shared",
|
|
2794
|
+
status: "open",
|
|
2795
|
+
target,
|
|
2796
|
+
createdAt: timestamp,
|
|
2797
|
+
updatedAt: timestamp,
|
|
2798
|
+
lastSelectedAt: timestamp
|
|
2799
|
+
};
|
|
2800
|
+
this.commitOwner(this.createOwner(target, task.name, [task]));
|
|
2801
|
+
}
|
|
2802
|
+
assertCanLeaveSelected(owner, nextTaskName) {
|
|
2803
|
+
if (!owner.activeTaskName || nextTaskName && this.sameName(owner.activeTaskName, nextTaskName)) {
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2806
|
+
const selected = this.findTask(owner, owner.activeTaskName);
|
|
2807
|
+
if (selected && this.isBusy(selected.sessionId)) {
|
|
2808
|
+
throw new Error(`Task "${selected.name}" is still running or waiting for permission.`);
|
|
2809
|
+
}
|
|
2810
|
+
}
|
|
2811
|
+
async loadTask(task, failureMessage) {
|
|
2812
|
+
try {
|
|
2813
|
+
return (await this.router.loadManagedSession(task.sessionId, task.target, task.cwd)).loaded;
|
|
2814
|
+
} catch (error) {
|
|
2815
|
+
throw new Error(failureMessage, { cause: error });
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
async createSession(target, cwd, failureMessage) {
|
|
2819
|
+
try {
|
|
2820
|
+
return await this.router.createManagedSession(target, cwd);
|
|
2821
|
+
} catch (error) {
|
|
2822
|
+
throw new Error(failureMessage, { cause: error });
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
withOwnerLock(input, operation) {
|
|
2826
|
+
const key = JSON.stringify([
|
|
2827
|
+
this.channelName,
|
|
2828
|
+
input.chatId,
|
|
2829
|
+
input.senderId
|
|
2830
|
+
]);
|
|
2831
|
+
const previous = this.ownerOperations.get(key) ?? Promise.resolve();
|
|
2832
|
+
const result = previous.catch(() => void 0).then(operation);
|
|
2833
|
+
const tail = result.then(() => void 0, () => void 0);
|
|
2834
|
+
this.ownerOperations.set(key, tail);
|
|
2835
|
+
return result.finally(() => {
|
|
2836
|
+
if (this.ownerOperations.get(key) === tail) {
|
|
2837
|
+
this.ownerOperations.delete(key);
|
|
2838
|
+
}
|
|
2839
|
+
});
|
|
2840
|
+
}
|
|
2841
|
+
getOwner(input) {
|
|
2842
|
+
const owner = this.registry.owners.find((candidate) => candidate.channelName === this.channelName && candidate.chatId === input.chatId && candidate.senderId === input.senderId);
|
|
2843
|
+
return owner ? this.cloneOwner(owner) : void 0;
|
|
2844
|
+
}
|
|
2845
|
+
createOwner(input, activeTaskName, tasks) {
|
|
2846
|
+
return {
|
|
2847
|
+
channelName: this.channelName,
|
|
2848
|
+
chatId: input.chatId,
|
|
2849
|
+
senderId: input.senderId,
|
|
2850
|
+
activeTaskName,
|
|
2851
|
+
tasks
|
|
2852
|
+
};
|
|
2853
|
+
}
|
|
2854
|
+
target(input) {
|
|
2855
|
+
return {
|
|
2856
|
+
channelName: this.channelName,
|
|
2857
|
+
senderId: input.senderId,
|
|
2858
|
+
chatId: input.chatId,
|
|
2859
|
+
...input.threadId !== void 0 ? { threadId: input.threadId } : {},
|
|
2860
|
+
...input.isGroup !== void 0 ? { isGroup: input.isGroup } : {}
|
|
2861
|
+
};
|
|
2862
|
+
}
|
|
2863
|
+
findTask(owner, name) {
|
|
2864
|
+
const normalized = this.normalizeName(name);
|
|
2865
|
+
return owner.tasks.find((task) => this.normalizeName(task.name) === normalized);
|
|
2866
|
+
}
|
|
2867
|
+
replaceTask(owner, task, activeTaskName) {
|
|
2868
|
+
return {
|
|
2869
|
+
...owner,
|
|
2870
|
+
activeTaskName,
|
|
2871
|
+
tasks: owner.tasks.map((candidate) => this.sameName(candidate.name, task.name) ? task : candidate)
|
|
2872
|
+
};
|
|
2873
|
+
}
|
|
2874
|
+
view(owner, task) {
|
|
2875
|
+
return {
|
|
2876
|
+
name: task.name,
|
|
2877
|
+
status: task.status,
|
|
2878
|
+
isolation: task.isolation,
|
|
2879
|
+
active: task.status === "open" && this.sameName(owner.activeTaskName, task.name)
|
|
2880
|
+
};
|
|
2881
|
+
}
|
|
2882
|
+
selection(owner, task) {
|
|
2883
|
+
return { ...this.view(owner, task), sessionId: task.sessionId };
|
|
2884
|
+
}
|
|
2885
|
+
openTaskCount(owner) {
|
|
2886
|
+
return owner.tasks.filter((task) => task.status === "open").length;
|
|
2887
|
+
}
|
|
2888
|
+
nextTimestamp(owner) {
|
|
2889
|
+
const now = this.now();
|
|
2890
|
+
if (!Number.isSafeInteger(now) || now < 0) {
|
|
2891
|
+
throw new Error("Invalid named-session clock value.");
|
|
2892
|
+
}
|
|
2893
|
+
let timestamp = now;
|
|
2894
|
+
for (const task of owner?.tasks ?? []) {
|
|
2895
|
+
timestamp = Math.max(timestamp, task.createdAt + 1, task.updatedAt + 1, task.lastSelectedAt + 1);
|
|
2896
|
+
}
|
|
2897
|
+
if (!Number.isSafeInteger(timestamp)) {
|
|
2898
|
+
throw new Error("Named-session timestamp limit reached.");
|
|
2899
|
+
}
|
|
2900
|
+
return timestamp;
|
|
2901
|
+
}
|
|
2902
|
+
validateName(name) {
|
|
2903
|
+
if (!TASK_NAME_PATTERN.test(name)) {
|
|
2904
|
+
throw new Error("Task names must be 1-32 ASCII letters, numbers, underscores, or hyphens and start with a letter or number.");
|
|
2905
|
+
}
|
|
2906
|
+
}
|
|
2907
|
+
normalizeName(name) {
|
|
2908
|
+
return name.toLowerCase();
|
|
2909
|
+
}
|
|
2910
|
+
sameName(a, b) {
|
|
2911
|
+
return a !== null && this.normalizeName(a) === this.normalizeName(b);
|
|
2912
|
+
}
|
|
2913
|
+
cloneOwner(owner) {
|
|
2914
|
+
return {
|
|
2915
|
+
...owner,
|
|
2916
|
+
tasks: owner.tasks.map((task) => ({
|
|
2917
|
+
...task,
|
|
2918
|
+
target: { ...task.target }
|
|
2919
|
+
}))
|
|
2920
|
+
};
|
|
2921
|
+
}
|
|
2922
|
+
commitOwner(owner) {
|
|
2923
|
+
const owners = this.registry.owners.map((candidate) => candidate.channelName === owner.channelName && candidate.chatId === owner.chatId && candidate.senderId === owner.senderId ? owner : candidate);
|
|
2924
|
+
if (!owners.some((candidate) => candidate.channelName === owner.channelName && candidate.chatId === owner.chatId && candidate.senderId === owner.senderId)) {
|
|
2925
|
+
owners.push(owner);
|
|
2926
|
+
}
|
|
2927
|
+
const next = { version: REGISTRY_VERSION, owners };
|
|
2928
|
+
if (!this.isRegistry(next)) {
|
|
2929
|
+
throw new Error("Invalid named-session registry update.");
|
|
2930
|
+
}
|
|
2931
|
+
this.writeRegistry(next);
|
|
2932
|
+
this.registry = next;
|
|
2933
|
+
}
|
|
2934
|
+
readRegistry() {
|
|
2935
|
+
if (!existsSync4(this.filePath)) {
|
|
2936
|
+
return { version: REGISTRY_VERSION, owners: [] };
|
|
2937
|
+
}
|
|
2938
|
+
let value;
|
|
2939
|
+
try {
|
|
2940
|
+
value = JSON.parse(readFileSync4(this.filePath, "utf8"));
|
|
2941
|
+
} catch (error) {
|
|
2942
|
+
throw new Error(`Failed to read named-session registry: ${this.filePath}`, {
|
|
2943
|
+
cause: error
|
|
2944
|
+
});
|
|
2945
|
+
}
|
|
2946
|
+
if (!this.isRegistry(value)) {
|
|
2947
|
+
if (this.isRegistry(value, false)) {
|
|
2948
|
+
const stalePath = `${this.filePath}.stale-${randomUUID2()}`;
|
|
2949
|
+
try {
|
|
2950
|
+
renameSync4(this.filePath, stalePath);
|
|
2951
|
+
} catch (error) {
|
|
2952
|
+
throw new Error("Failed to archive stale named-session registry.", {
|
|
2953
|
+
cause: error
|
|
2954
|
+
});
|
|
2955
|
+
}
|
|
2956
|
+
process3.stderr.write("[NamedSessionManager] Archived a stale registry after the channel working directory changed.\n");
|
|
2957
|
+
return { version: REGISTRY_VERSION, owners: [] };
|
|
2958
|
+
}
|
|
2959
|
+
throw new Error(`Invalid named-session registry: ${this.filePath}`);
|
|
2960
|
+
}
|
|
2961
|
+
return value;
|
|
2962
|
+
}
|
|
2963
|
+
writeRegistry(registry) {
|
|
2964
|
+
const dir = dirname4(this.filePath);
|
|
2965
|
+
const tempPath = `${this.filePath}.${process3.pid}-${randomUUID2()}.tmp`;
|
|
2966
|
+
try {
|
|
2967
|
+
mkdirSync4(dir, { recursive: true, mode: 448 });
|
|
2968
|
+
try {
|
|
2969
|
+
chmodSync3(dir, 448);
|
|
2970
|
+
} catch {
|
|
2971
|
+
}
|
|
2972
|
+
writeFileSync4(tempPath, JSON.stringify(registry, null, 2), {
|
|
2973
|
+
encoding: "utf8",
|
|
2974
|
+
mode: 384
|
|
2975
|
+
});
|
|
2976
|
+
renameSync4(tempPath, this.filePath);
|
|
2977
|
+
try {
|
|
2978
|
+
chmodSync3(this.filePath, 384);
|
|
2979
|
+
} catch {
|
|
2980
|
+
}
|
|
2981
|
+
} catch (error) {
|
|
2982
|
+
throw new Error("Failed to persist named-session registry.", {
|
|
2983
|
+
cause: error
|
|
2984
|
+
});
|
|
2985
|
+
} finally {
|
|
2986
|
+
try {
|
|
2987
|
+
rmSync2(tempPath, { force: true });
|
|
2988
|
+
} catch {
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
}
|
|
2992
|
+
isRegistry(value, requireCurrentCwd = true) {
|
|
2993
|
+
if (!this.isRecord(value) || value["version"] !== REGISTRY_VERSION) {
|
|
2994
|
+
return false;
|
|
2995
|
+
}
|
|
2996
|
+
const owners = value["owners"];
|
|
2997
|
+
if (!Array.isArray(owners))
|
|
2998
|
+
return false;
|
|
2999
|
+
const ownerKeys = /* @__PURE__ */ new Set();
|
|
3000
|
+
const sessionIds = /* @__PURE__ */ new Set();
|
|
3001
|
+
for (const owner of owners) {
|
|
3002
|
+
if (!this.isOwner(owner, ownerKeys, sessionIds, requireCurrentCwd)) {
|
|
3003
|
+
return false;
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
return true;
|
|
3007
|
+
}
|
|
3008
|
+
isOwner(value, ownerKeys, sessionIds, requireCurrentCwd) {
|
|
3009
|
+
if (!this.isRecord(value))
|
|
3010
|
+
return false;
|
|
3011
|
+
const channelName = value["channelName"];
|
|
3012
|
+
const chatId = value["chatId"];
|
|
3013
|
+
const senderId = value["senderId"];
|
|
3014
|
+
const activeTaskName = value["activeTaskName"];
|
|
3015
|
+
const tasks = value["tasks"];
|
|
3016
|
+
if (channelName !== this.channelName || typeof chatId !== "string" || typeof senderId !== "string" || activeTaskName !== null && typeof activeTaskName !== "string" || !Array.isArray(tasks)) {
|
|
3017
|
+
return false;
|
|
3018
|
+
}
|
|
3019
|
+
const ownerKey = JSON.stringify([channelName, chatId, senderId]);
|
|
3020
|
+
if (ownerKeys.has(ownerKey))
|
|
3021
|
+
return false;
|
|
3022
|
+
ownerKeys.add(ownerKey);
|
|
3023
|
+
const names = /* @__PURE__ */ new Set();
|
|
3024
|
+
let openTasks = 0;
|
|
3025
|
+
let activeFound = activeTaskName === null;
|
|
3026
|
+
for (const task of tasks) {
|
|
3027
|
+
if (!this.isTask(task, channelName, chatId, senderId, requireCurrentCwd)) {
|
|
3028
|
+
return false;
|
|
3029
|
+
}
|
|
3030
|
+
const normalized = this.normalizeName(task.name);
|
|
3031
|
+
if (names.has(normalized) || sessionIds.has(task.sessionId))
|
|
3032
|
+
return false;
|
|
3033
|
+
names.add(normalized);
|
|
3034
|
+
sessionIds.add(task.sessionId);
|
|
3035
|
+
if (task.status === "open")
|
|
3036
|
+
openTasks++;
|
|
3037
|
+
if (activeTaskName !== null && task.status === "open" && this.sameName(activeTaskName, task.name)) {
|
|
3038
|
+
activeFound = true;
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
return openTasks <= MAX_OPEN_TASKS && activeFound;
|
|
3042
|
+
}
|
|
3043
|
+
isTask(value, channelName, chatId, senderId, requireCurrentCwd) {
|
|
3044
|
+
if (!this.isRecord(value) || !this.isRecord(value["target"]))
|
|
3045
|
+
return false;
|
|
3046
|
+
const target = value["target"];
|
|
3047
|
+
return typeof value["name"] === "string" && TASK_NAME_PATTERN.test(value["name"]) && typeof value["sessionId"] === "string" && value["sessionId"].length > 0 && typeof value["cwd"] === "string" && (!requireCurrentCwd || this.isCurrentCwd(value["cwd"])) && value["isolation"] === "shared" && (value["status"] === "open" || value["status"] === "closed") && this.isTimestamp(value["createdAt"]) && this.isTimestamp(value["updatedAt"]) && this.isTimestamp(value["lastSelectedAt"]) && target["channelName"] === channelName && target["chatId"] === chatId && target["senderId"] === senderId && (target["threadId"] === void 0 || typeof target["threadId"] === "string") && (target["isGroup"] === void 0 || typeof target["isGroup"] === "boolean");
|
|
3048
|
+
}
|
|
3049
|
+
isCurrentCwd(cwd) {
|
|
3050
|
+
return canonicalizeWorkspacePath(cwd) === this.canonicalCwd;
|
|
3051
|
+
}
|
|
3052
|
+
isTimestamp(value) {
|
|
3053
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
3054
|
+
}
|
|
3055
|
+
isRecord(value) {
|
|
3056
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3057
|
+
}
|
|
3058
|
+
};
|
|
3059
|
+
|
|
2299
3060
|
// packages/channels/base/dist/ChannelLoopScheduler.js
|
|
2300
3061
|
init_esbuild_shims();
|
|
2301
3062
|
var MAX_RESULT_PREVIEW_LENGTH = 500;
|
|
@@ -2990,6 +3751,7 @@ var ChannelBase = class {
|
|
|
2990
3751
|
groupPairingNotified = /* @__PURE__ */ new Map();
|
|
2991
3752
|
loopController;
|
|
2992
3753
|
observedContacts;
|
|
3754
|
+
namedSessions;
|
|
2993
3755
|
observedContactEnvelopes = /* @__PURE__ */ new WeakSet();
|
|
2994
3756
|
instructedSessions = /* @__PURE__ */ new Set();
|
|
2995
3757
|
unattendedMemorySessions = /* @__PURE__ */ new Set();
|
|
@@ -2998,6 +3760,8 @@ var ChannelBase = class {
|
|
|
2998
3760
|
commands = /* @__PURE__ */ new Map();
|
|
2999
3761
|
/** Per-session promise chain to serialize prompt + send (followup mode). */
|
|
3000
3762
|
sessionQueues = /* @__PURE__ */ new Map();
|
|
3763
|
+
queuedTurns = /* @__PURE__ */ new Map();
|
|
3764
|
+
namedTurnBindings = /* @__PURE__ */ new WeakMap();
|
|
3001
3765
|
registerBridgeEvents;
|
|
3002
3766
|
bridgeRecovery;
|
|
3003
3767
|
/**
|
|
@@ -3074,7 +3838,16 @@ var ChannelBase = class {
|
|
|
3074
3838
|
await this.pushProactive(target, text);
|
|
3075
3839
|
return;
|
|
3076
3840
|
}
|
|
3077
|
-
await this.
|
|
3841
|
+
await this.deliverBackgroundReply(target.chatId, text, sessionId);
|
|
3842
|
+
}
|
|
3843
|
+
/**
|
|
3844
|
+
* Fallback delivery of a background response when proactive send is
|
|
3845
|
+
* unavailable. Adapters whose turn replies bypass sendResponseMessage (to
|
|
3846
|
+
* stay out of turn-scoped streaming state, for example) override only this
|
|
3847
|
+
* step instead of re-implementing the whole dispatch flow.
|
|
3848
|
+
*/
|
|
3849
|
+
async deliverBackgroundReply(chatId, text, sessionId) {
|
|
3850
|
+
await this.sendResponseMessage(chatId, text, sessionId);
|
|
3078
3851
|
}
|
|
3079
3852
|
async dispatchPermissionRequest(event) {
|
|
3080
3853
|
const target = this.permissionTargetForEvent(event);
|
|
@@ -3295,6 +4068,21 @@ var ChannelBase = class {
|
|
|
3295
4068
|
this.dmGate = new DmGate(config.dmPolicy);
|
|
3296
4069
|
this.gate = new SenderGate(config.senderPolicy, config.allowedUsers, pairingStore);
|
|
3297
4070
|
this.router = options?.router || new SessionRouter(bridge, config.cwd, config.sessionScope);
|
|
4071
|
+
if (config.multiSession) {
|
|
4072
|
+
if (config.sessionScope !== "user") {
|
|
4073
|
+
throw new Error(`Channel "${name}" requires sessionScope "user" when multiSession is enabled.`);
|
|
4074
|
+
}
|
|
4075
|
+
if (!options?.stateDir) {
|
|
4076
|
+
throw new Error(`Channel "${name}" multiSession is available only in daemon-managed mode.`);
|
|
4077
|
+
}
|
|
4078
|
+
this.namedSessions = new NamedSessionManager({
|
|
4079
|
+
channelName: name,
|
|
4080
|
+
cwd: config.cwd,
|
|
4081
|
+
filePath: join5(options.stateDir, "named-sessions.json"),
|
|
4082
|
+
router: this.router,
|
|
4083
|
+
isBusy: /* @__PURE__ */ __name((sessionId) => this.isNamedSessionBusy(sessionId), "isBusy")
|
|
4084
|
+
});
|
|
4085
|
+
}
|
|
3298
4086
|
this.registerSharedCommands();
|
|
3299
4087
|
if (this.loopController) {
|
|
3300
4088
|
bridge.registerChannelLoopToolHandler?.(this.channelLoopToolHandler);
|
|
@@ -3458,7 +4246,7 @@ var ChannelBase = class {
|
|
|
3458
4246
|
ensureOutputSegment(sessionId, active) {
|
|
3459
4247
|
if (!active.owner)
|
|
3460
4248
|
return void 0;
|
|
3461
|
-
const segmentId = active.activeSegmentId ??
|
|
4249
|
+
const segmentId = active.activeSegmentId ?? randomUUID3();
|
|
3462
4250
|
const context = this.outputSegmentContext(sessionId, active, segmentId);
|
|
3463
4251
|
if (context)
|
|
3464
4252
|
active.activeSegmentId = segmentId;
|
|
@@ -3687,8 +4475,11 @@ var ChannelBase = class {
|
|
|
3687
4475
|
imageBase64: void 0,
|
|
3688
4476
|
imageMimeType: void 0
|
|
3689
4477
|
};
|
|
4478
|
+
if (this.namedSessions) {
|
|
4479
|
+
this.bindNamedTurn(syntheticEnvelope, sessionId);
|
|
4480
|
+
}
|
|
3690
4481
|
this.markPreflighted(syntheticEnvelope);
|
|
3691
|
-
this.
|
|
4482
|
+
void this.processPreflightedInbound(syntheticEnvelope).catch((err) => {
|
|
3692
4483
|
process.stderr.write(`[${this.name}] dropped ${lost} buffered message(s) after ${taskLabel} for session ${sessionId} (last sender ${lastEnvelope.senderId}): ${err instanceof Error ? err.message : String(err)}
|
|
3693
4484
|
`);
|
|
3694
4485
|
});
|
|
@@ -3784,7 +4575,7 @@ ${promptText}` : promptText;
|
|
|
3784
4575
|
doneResolve = resolve3;
|
|
3785
4576
|
});
|
|
3786
4577
|
const promptState = {
|
|
3787
|
-
runId:
|
|
4578
|
+
runId: randomUUID3(),
|
|
3788
4579
|
cancelled: false,
|
|
3789
4580
|
done,
|
|
3790
4581
|
resolve: doneResolve,
|
|
@@ -4000,7 +4791,7 @@ ${promptText}` : promptText;
|
|
|
4000
4791
|
doneResolve = resolve3;
|
|
4001
4792
|
});
|
|
4002
4793
|
const promptState = {
|
|
4003
|
-
runId:
|
|
4794
|
+
runId: randomUUID3(),
|
|
4004
4795
|
cancelled: false,
|
|
4005
4796
|
done,
|
|
4006
4797
|
resolve: doneResolve,
|
|
@@ -4381,7 +5172,7 @@ ${promptText}` : promptText;
|
|
|
4381
5172
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can cancel requests in this shared session.");
|
|
4382
5173
|
return true;
|
|
4383
5174
|
}
|
|
4384
|
-
const activeSessionId = this.findActiveSessionId(envelope);
|
|
5175
|
+
const activeSessionId = this.namedSessions ? await this.findNamedActiveSessionId(envelope) : this.findActiveSessionId(envelope);
|
|
4385
5176
|
if (!activeSessionId) {
|
|
4386
5177
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No request is currently running.");
|
|
4387
5178
|
return true;
|
|
@@ -4606,10 +5397,203 @@ ${requestList}`);
|
|
|
4606
5397
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, accepted ? decision === "approve" ? "Permission approved." : decision === "approve-always" ? "Permission approved always." : "Permission denied." : "Permission request is no longer pending.");
|
|
4607
5398
|
return true;
|
|
4608
5399
|
}
|
|
5400
|
+
namedSessionOwner(envelope) {
|
|
5401
|
+
return {
|
|
5402
|
+
senderId: envelope.senderId,
|
|
5403
|
+
chatId: envelope.chatId,
|
|
5404
|
+
...envelope.threadId !== void 0 ? { threadId: envelope.threadId } : {},
|
|
5405
|
+
...envelope.isGroup !== void 0 ? { isGroup: envelope.isGroup } : {}
|
|
5406
|
+
};
|
|
5407
|
+
}
|
|
5408
|
+
async currentSessionId(envelope) {
|
|
5409
|
+
if (this.namedSessions) {
|
|
5410
|
+
return (await this.namedSessions.current(this.namedSessionOwner(envelope)))?.sessionId;
|
|
5411
|
+
}
|
|
5412
|
+
return this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
5413
|
+
}
|
|
5414
|
+
reserveQueuedTurn(sessionId) {
|
|
5415
|
+
this.queuedTurns.set(sessionId, (this.queuedTurns.get(sessionId) ?? 0) + 1);
|
|
5416
|
+
}
|
|
5417
|
+
releaseQueuedTurn(sessionId) {
|
|
5418
|
+
const remaining = (this.queuedTurns.get(sessionId) ?? 1) - 1;
|
|
5419
|
+
if (remaining === 0) {
|
|
5420
|
+
this.queuedTurns.delete(sessionId);
|
|
5421
|
+
} else {
|
|
5422
|
+
this.queuedTurns.set(sessionId, remaining);
|
|
5423
|
+
}
|
|
5424
|
+
}
|
|
5425
|
+
bindNamedTurn(envelope, sessionId) {
|
|
5426
|
+
const binding = {
|
|
5427
|
+
sessionId,
|
|
5428
|
+
generation: this.sessionGenerations.get(sessionId) ?? 0,
|
|
5429
|
+
claimed: false,
|
|
5430
|
+
released: false
|
|
5431
|
+
};
|
|
5432
|
+
this.namedTurnBindings.set(envelope, binding);
|
|
5433
|
+
this.reserveQueuedTurn(sessionId);
|
|
5434
|
+
return binding;
|
|
5435
|
+
}
|
|
5436
|
+
releaseNamedTurnBinding(binding) {
|
|
5437
|
+
if (binding.sessionId === null || binding.claimed || binding.released) {
|
|
5438
|
+
return;
|
|
5439
|
+
}
|
|
5440
|
+
binding.released = true;
|
|
5441
|
+
this.releaseQueuedTurn(binding.sessionId);
|
|
5442
|
+
}
|
|
5443
|
+
finishNamedTurnBinding(envelope) {
|
|
5444
|
+
const binding = this.namedTurnBindings.get(envelope);
|
|
5445
|
+
if (!binding)
|
|
5446
|
+
return;
|
|
5447
|
+
this.releaseNamedTurnBinding(binding);
|
|
5448
|
+
this.namedTurnBindings.delete(envelope);
|
|
5449
|
+
}
|
|
5450
|
+
bypassesNamedTurnBinding(envelope) {
|
|
5451
|
+
const parsed = this.parseCommand(envelope.text);
|
|
5452
|
+
if (parsed && this.commands.has(parsed.command))
|
|
5453
|
+
return true;
|
|
5454
|
+
const bangText = envelope.text.trimStart();
|
|
5455
|
+
return bangText.startsWith("!") && (envelope.isGroup || this.isSharedSession(envelope));
|
|
5456
|
+
}
|
|
5457
|
+
async prepareNamedTurnBinding(envelope) {
|
|
5458
|
+
const namedSessions = this.namedSessions;
|
|
5459
|
+
if (!namedSessions || this.namedTurnBindings.has(envelope))
|
|
5460
|
+
return true;
|
|
5461
|
+
if (this.bypassesNamedTurnBinding(envelope))
|
|
5462
|
+
return true;
|
|
5463
|
+
try {
|
|
5464
|
+
const sessionId = await namedSessions.resolve(this.namedSessionOwner(envelope), void 0, (resolvedSessionId) => {
|
|
5465
|
+
const binding = this.bindNamedTurn(envelope, resolvedSessionId);
|
|
5466
|
+
return () => this.releaseNamedTurnBinding(binding);
|
|
5467
|
+
});
|
|
5468
|
+
if (!sessionId && !this.namedTurnBindings.has(envelope)) {
|
|
5469
|
+
this.namedTurnBindings.set(envelope, {
|
|
5470
|
+
sessionId: null,
|
|
5471
|
+
generation: 0,
|
|
5472
|
+
claimed: false,
|
|
5473
|
+
released: true
|
|
5474
|
+
});
|
|
5475
|
+
}
|
|
5476
|
+
return true;
|
|
5477
|
+
} catch (error) {
|
|
5478
|
+
this.finishNamedTurnBinding(envelope);
|
|
5479
|
+
await this.sendNamedSessionError(envelope, error);
|
|
5480
|
+
return false;
|
|
5481
|
+
}
|
|
5482
|
+
}
|
|
5483
|
+
isNamedSessionBusy(sessionId) {
|
|
5484
|
+
if ((this.queuedTurns.get(sessionId) ?? 0) > 0)
|
|
5485
|
+
return true;
|
|
5486
|
+
if (this.activePrompts.has(sessionId))
|
|
5487
|
+
return true;
|
|
5488
|
+
for (const permission of this.pendingPermissions.values()) {
|
|
5489
|
+
if (permission.sessionId === sessionId)
|
|
5490
|
+
return true;
|
|
5491
|
+
}
|
|
5492
|
+
try {
|
|
5493
|
+
return this.bridge.listSessions?.().some((session) => session.sessionId === sessionId && session.hasActivePrompt) ?? false;
|
|
5494
|
+
} catch {
|
|
5495
|
+
return true;
|
|
5496
|
+
}
|
|
5497
|
+
}
|
|
5498
|
+
async sendNamedSessionError(envelope, error) {
|
|
5499
|
+
const message = error instanceof Error ? sanitizeDisplayText(error.message, 500) : "Named-session operation failed.";
|
|
5500
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, message || "Named-session operation failed.");
|
|
5501
|
+
}
|
|
5502
|
+
async handleNamedSessionsCommand(envelope, args) {
|
|
5503
|
+
const namedSessions = this.namedSessions;
|
|
5504
|
+
if (!namedSessions)
|
|
5505
|
+
return false;
|
|
5506
|
+
const normalized = args.trim().toLowerCase();
|
|
5507
|
+
if (normalized !== "" && normalized !== "all") {
|
|
5508
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Usage: /sessions [all]");
|
|
5509
|
+
return true;
|
|
5510
|
+
}
|
|
5511
|
+
try {
|
|
5512
|
+
const tasks = await namedSessions.list(this.namedSessionOwner(envelope), normalized === "all");
|
|
5513
|
+
if (tasks.length === 0) {
|
|
5514
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, normalized === "all" ? "No named tasks." : "No open named tasks.");
|
|
5515
|
+
return true;
|
|
5516
|
+
}
|
|
5517
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, [
|
|
5518
|
+
normalized === "all" ? "Tasks:" : "Open tasks:",
|
|
5519
|
+
...tasks.map((task) => `${task.active ? "*" : "-"} ${task.name} (${task.status}, ${task.isolation})`)
|
|
5520
|
+
].join("\n"));
|
|
5521
|
+
} catch (error) {
|
|
5522
|
+
await this.sendNamedSessionError(envelope, error);
|
|
5523
|
+
}
|
|
5524
|
+
return true;
|
|
5525
|
+
}
|
|
5526
|
+
async handleNamedSessionCommand(envelope, args) {
|
|
5527
|
+
const namedSessions = this.namedSessions;
|
|
5528
|
+
if (!namedSessions)
|
|
5529
|
+
return false;
|
|
5530
|
+
const parts = args.trim().split(/\s+/u).filter(Boolean);
|
|
5531
|
+
const subcommand = parts.shift()?.toLowerCase() ?? "";
|
|
5532
|
+
const owner = this.namedSessionOwner(envelope);
|
|
5533
|
+
try {
|
|
5534
|
+
switch (subcommand) {
|
|
5535
|
+
case "current": {
|
|
5536
|
+
if (parts.length > 0)
|
|
5537
|
+
break;
|
|
5538
|
+
const current = await namedSessions.current(owner);
|
|
5539
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, current ? `Current task: ${current.name} (${current.isolation})` : "No task is currently selected. Use /session new <name> or /session use <name>.");
|
|
5540
|
+
return true;
|
|
5541
|
+
}
|
|
5542
|
+
case "new": {
|
|
5543
|
+
if (parts.includes("--worktree")) {
|
|
5544
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Worktree tasks are not available in Part 2 yet. Create a shared task with /session new <name>.");
|
|
5545
|
+
return true;
|
|
5546
|
+
}
|
|
5547
|
+
if (parts.length !== 1)
|
|
5548
|
+
break;
|
|
5549
|
+
const created = await namedSessions.create(owner, parts[0]);
|
|
5550
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Created and selected task "${created.name}" (shared workspace).`);
|
|
5551
|
+
return true;
|
|
5552
|
+
}
|
|
5553
|
+
case "use": {
|
|
5554
|
+
if (parts.length !== 1)
|
|
5555
|
+
break;
|
|
5556
|
+
const selected = await namedSessions.use(owner, parts[0]);
|
|
5557
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, `Selected task "${selected.name}" (${selected.isolation} workspace).`);
|
|
5558
|
+
return true;
|
|
5559
|
+
}
|
|
5560
|
+
case "close": {
|
|
5561
|
+
if (parts.length !== 1)
|
|
5562
|
+
break;
|
|
5563
|
+
const result = await namedSessions.close(owner, parts[0]);
|
|
5564
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, result.active ? `Closed task "${result.closed.name}". Selected "${result.active.name}".` : `Closed task "${result.closed.name}". No task is selected.`);
|
|
5565
|
+
return true;
|
|
5566
|
+
}
|
|
5567
|
+
case "cancel":
|
|
5568
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Named task cancellation is not available in Part 2 yet. Wait for the selected task to finish before switching; Telegram users can use /cancel.");
|
|
5569
|
+
return true;
|
|
5570
|
+
default:
|
|
5571
|
+
break;
|
|
5572
|
+
}
|
|
5573
|
+
} catch (error) {
|
|
5574
|
+
await this.sendNamedSessionError(envelope, error);
|
|
5575
|
+
return true;
|
|
5576
|
+
}
|
|
5577
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Usage: /session current | /session new <name> | /session use <name> | /session close <name> | /session cancel [<name>]");
|
|
5578
|
+
return true;
|
|
5579
|
+
}
|
|
4609
5580
|
/** Register shared slash commands. Called from constructor. */
|
|
4610
5581
|
registerSharedCommands() {
|
|
4611
5582
|
const doClear = /* @__PURE__ */ __name(async (envelope) => {
|
|
4612
|
-
|
|
5583
|
+
let resetTaskName;
|
|
5584
|
+
let removedIds;
|
|
5585
|
+
if (this.namedSessions) {
|
|
5586
|
+
try {
|
|
5587
|
+
const reset = await this.namedSessions.reset(this.namedSessionOwner(envelope));
|
|
5588
|
+
resetTaskName = reset?.name;
|
|
5589
|
+
removedIds = reset ? [reset.previousSessionId] : [];
|
|
5590
|
+
} catch (error) {
|
|
5591
|
+
await this.sendNamedSessionError(envelope, error);
|
|
5592
|
+
return;
|
|
5593
|
+
}
|
|
5594
|
+
} else {
|
|
5595
|
+
removedIds = this.router.removeSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
5596
|
+
}
|
|
4613
5597
|
this.clearPendingGroupHistory(envelope);
|
|
4614
5598
|
if (removedIds.length > 0) {
|
|
4615
5599
|
for (const id of removedIds) {
|
|
@@ -4655,7 +5639,7 @@ ${requestList}`);
|
|
|
4655
5639
|
}
|
|
4656
5640
|
this.discardRetiredSession(this.bridge, id, "cleared");
|
|
4657
5641
|
}
|
|
4658
|
-
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Session cleared. The next message starts a fresh conversation.");
|
|
5642
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, resetTaskName ? `Task "${resetTaskName}" reset with a fresh conversation.` : "Session cleared. The next message starts a fresh conversation.");
|
|
4659
5643
|
} else {
|
|
4660
5644
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No active session to clear.");
|
|
4661
5645
|
}
|
|
@@ -4675,6 +5659,10 @@ ${requestList}`);
|
|
|
4675
5659
|
this.registerCommand("clear", clearHandler);
|
|
4676
5660
|
this.registerCommand("reset", clearHandler);
|
|
4677
5661
|
this.registerCommand("new", clearHandler);
|
|
5662
|
+
if (this.namedSessions) {
|
|
5663
|
+
this.registerCommand("sessions", (envelope, args) => this.handleNamedSessionsCommand(envelope, args));
|
|
5664
|
+
this.registerCommand("session", (envelope, args) => this.handleNamedSessionCommand(envelope, args));
|
|
5665
|
+
}
|
|
4678
5666
|
this.registerCommand("approve", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "approve"));
|
|
4679
5667
|
this.registerCommand("approve-always", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "approve-always"));
|
|
4680
5668
|
this.registerCommand("deny", (envelope, args) => this.handlePermissionResponseCommand(envelope, args, "deny"));
|
|
@@ -4683,7 +5671,7 @@ ${requestList}`);
|
|
|
4683
5671
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can view this shared session.");
|
|
4684
5672
|
return true;
|
|
4685
5673
|
}
|
|
4686
|
-
const active = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
5674
|
+
const active = this.namedSessions ? Boolean(await this.currentSessionId(envelope)) : this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
4687
5675
|
const scopeNote = this.config.sessionScope === "single" ? " (shared channel-wide)" : this.isSharedSession(envelope) ? envelope.isGroup ? " (shared by this group)" : "" : envelope.isGroup ? " (private to you)" : "";
|
|
4688
5676
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, [
|
|
4689
5677
|
`Channel: ${this.name}`,
|
|
@@ -4708,7 +5696,11 @@ ${requestList}`);
|
|
|
4708
5696
|
"/status \u2014 Show session info",
|
|
4709
5697
|
"/approve [request-id] \u2014 Approve a pending permission request",
|
|
4710
5698
|
"/approve-always [request-id] \u2014 Always approve a pending permission request",
|
|
4711
|
-
"/deny [request-id] \u2014 Deny a pending permission request"
|
|
5699
|
+
"/deny [request-id] \u2014 Deny a pending permission request",
|
|
5700
|
+
...this.namedSessions ? [
|
|
5701
|
+
"/sessions [all] \u2014 List your named tasks",
|
|
5702
|
+
"/session current|new|use|close \u2014 Manage your named tasks"
|
|
5703
|
+
] : []
|
|
4712
5704
|
];
|
|
4713
5705
|
const sharedCmds = /* @__PURE__ */ new Set([
|
|
4714
5706
|
"help",
|
|
@@ -4722,7 +5714,9 @@ ${requestList}`);
|
|
|
4722
5714
|
"channel-memory",
|
|
4723
5715
|
"forget-channel",
|
|
4724
5716
|
"who",
|
|
4725
|
-
"status"
|
|
5717
|
+
"status",
|
|
5718
|
+
"sessions",
|
|
5719
|
+
"session"
|
|
4726
5720
|
]);
|
|
4727
5721
|
const platformCmds = [...this.commands.keys()].filter((c) => !sharedCmds.has(c));
|
|
4728
5722
|
if (platformCmds.length > 0) {
|
|
@@ -4730,7 +5724,7 @@ ${requestList}`);
|
|
|
4730
5724
|
lines.push(`/${cmd}`);
|
|
4731
5725
|
}
|
|
4732
5726
|
}
|
|
4733
|
-
const sessionId = this.
|
|
5727
|
+
const sessionId = await this.currentSessionId(envelope);
|
|
4734
5728
|
const agentCommands = sessionId ? this.getAgentCommandsForSession(sessionId) : this.bridge.availableCommands;
|
|
4735
5729
|
if (agentCommands.length > 0) {
|
|
4736
5730
|
lines.push("", "Agent commands (forwarded to Qwen Code):");
|
|
@@ -4747,7 +5741,7 @@ ${requestList}`);
|
|
|
4747
5741
|
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "Only authorized members can view this shared session.");
|
|
4748
5742
|
return true;
|
|
4749
5743
|
}
|
|
4750
|
-
const hasSession = this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
5744
|
+
const hasSession = this.namedSessions ? Boolean(await this.currentSessionId(envelope)) : this.router.hasSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
4751
5745
|
const policy = this.config.senderPolicy;
|
|
4752
5746
|
const lines = [
|
|
4753
5747
|
`Session: ${hasSession ? "active" : "none"}`,
|
|
@@ -5078,6 +6072,10 @@ ${requestList}`);
|
|
|
5078
6072
|
const sessionId = this.router.getSession(this.name, envelope.senderId, envelope.chatId, envelope.threadId);
|
|
5079
6073
|
return sessionId && this.activePrompts.has(sessionId) ? sessionId : void 0;
|
|
5080
6074
|
}
|
|
6075
|
+
async findNamedActiveSessionId(envelope) {
|
|
6076
|
+
const sessionId = await this.currentSessionId(envelope);
|
|
6077
|
+
return sessionId && this.activePrompts.has(sessionId) ? sessionId : void 0;
|
|
6078
|
+
}
|
|
5081
6079
|
channelMemoryTarget(envelope) {
|
|
5082
6080
|
return {
|
|
5083
6081
|
channelName: this.name,
|
|
@@ -5805,9 +6803,12 @@ ${formatted.join("\n")}
|
|
|
5805
6803
|
${CURRENT_MESSAGE_MARKER}
|
|
5806
6804
|
${promptText}`;
|
|
5807
6805
|
}
|
|
5808
|
-
preflightInbound(envelope) {
|
|
5809
|
-
const groupResult = this.groupGate.check(envelope
|
|
5810
|
-
|
|
6806
|
+
preflightInbound(envelope, options = {}) {
|
|
6807
|
+
const groupResult = this.groupGate.check(envelope, {
|
|
6808
|
+
createPairingRequest: !options.deferPairingRequests
|
|
6809
|
+
});
|
|
6810
|
+
const deferredGroupPairing = options.deferPairingRequests === true && groupResult.reason === "pairing_trigger_required" && (envelope.isMentioned || envelope.isReplyToBot);
|
|
6811
|
+
if (!groupResult.allowed && !deferredGroupPairing) {
|
|
5811
6812
|
if (groupResult.pairing !== void 0) {
|
|
5812
6813
|
this.logPreflightRejected("group_pairing_required");
|
|
5813
6814
|
return this.onGroupPairingRequired(envelope.chatId, groupResult.pairing, envelope.threadId).then(() => false).catch((err) => {
|
|
@@ -5834,6 +6835,10 @@ ${promptText}`;
|
|
|
5834
6835
|
this.markPreflighted(envelope);
|
|
5835
6836
|
return true;
|
|
5836
6837
|
}
|
|
6838
|
+
if (options.deferPairingRequests === true && this.config.senderPolicy === "pairing" && !this.gate.isAllowed(envelope.senderId)) {
|
|
6839
|
+
this.markPreflighted(envelope);
|
|
6840
|
+
return true;
|
|
6841
|
+
}
|
|
5837
6842
|
const result = this.gate.check(envelope.senderId, envelope.senderName);
|
|
5838
6843
|
if (!result.allowed) {
|
|
5839
6844
|
if (result.pairing !== void 0) {
|
|
@@ -5870,7 +6875,50 @@ ${promptText}`;
|
|
|
5870
6875
|
const preflight = this.preflightInbound(envelope);
|
|
5871
6876
|
if (!(isPromiseLike(preflight) ? await preflight : preflight))
|
|
5872
6877
|
return;
|
|
5873
|
-
await this.
|
|
6878
|
+
await this.processPreflightedInbound(envelope);
|
|
6879
|
+
}
|
|
6880
|
+
async prepareThenHandleInbound(envelope, prepare, preflightOptions = {}) {
|
|
6881
|
+
const preflight = this.preflightInbound(envelope, preflightOptions);
|
|
6882
|
+
if (!(isPromiseLike(preflight) ? await preflight : preflight))
|
|
6883
|
+
return;
|
|
6884
|
+
if (this.namedSessions) {
|
|
6885
|
+
const result = await this.namedSessions.resolveAfterPreparation(this.namedSessionOwner(envelope), prepare, () => !this.bypassesNamedTurnBinding(envelope), (sessionId) => {
|
|
6886
|
+
const binding = this.bindNamedTurn(envelope, sessionId);
|
|
6887
|
+
return () => this.releaseNamedTurnBinding(binding);
|
|
6888
|
+
});
|
|
6889
|
+
if (result.status === "aborted")
|
|
6890
|
+
return;
|
|
6891
|
+
if (result.status === "resolve_error") {
|
|
6892
|
+
this.finishNamedTurnBinding(envelope);
|
|
6893
|
+
await this.sendNamedSessionError(envelope, result.error);
|
|
6894
|
+
return;
|
|
6895
|
+
}
|
|
6896
|
+
if (result.status === "resolved" && !result.sessionId && !this.namedTurnBindings.has(envelope)) {
|
|
6897
|
+
this.namedTurnBindings.set(envelope, {
|
|
6898
|
+
sessionId: null,
|
|
6899
|
+
generation: 0,
|
|
6900
|
+
claimed: false,
|
|
6901
|
+
released: true
|
|
6902
|
+
});
|
|
6903
|
+
}
|
|
6904
|
+
} else if (await prepare() === false) {
|
|
6905
|
+
return;
|
|
6906
|
+
}
|
|
6907
|
+
try {
|
|
6908
|
+
await this.handleInbound(envelope);
|
|
6909
|
+
} finally {
|
|
6910
|
+
this.finishNamedTurnBinding(envelope);
|
|
6911
|
+
}
|
|
6912
|
+
}
|
|
6913
|
+
async processPreflightedInbound(envelope, process5 = () => this.processInbound(envelope)) {
|
|
6914
|
+
if (this.namedSessions && !await this.prepareNamedTurnBinding(envelope)) {
|
|
6915
|
+
return;
|
|
6916
|
+
}
|
|
6917
|
+
try {
|
|
6918
|
+
await process5();
|
|
6919
|
+
} finally {
|
|
6920
|
+
this.finishNamedTurnBinding(envelope);
|
|
6921
|
+
}
|
|
5874
6922
|
}
|
|
5875
6923
|
async recordObservedContact(envelope) {
|
|
5876
6924
|
if (!this.observedContacts)
|
|
@@ -5995,7 +7043,45 @@ ${promptText}`;
|
|
|
5995
7043
|
}
|
|
5996
7044
|
}
|
|
5997
7045
|
await this.waitForBridgeRecovery();
|
|
5998
|
-
|
|
7046
|
+
let sessionId;
|
|
7047
|
+
let namedTurn = this.namedTurnBindings.get(envelope);
|
|
7048
|
+
if (this.namedSessions && namedTurn) {
|
|
7049
|
+
if (namedTurn.sessionId === null) {
|
|
7050
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No task was selected when this message was received. Select a task and send it again.");
|
|
7051
|
+
return;
|
|
7052
|
+
}
|
|
7053
|
+
if (this.dropQueuedTurnIfStale(namedTurn.sessionId, namedTurn.generation, envelope)) {
|
|
7054
|
+
return;
|
|
7055
|
+
}
|
|
7056
|
+
try {
|
|
7057
|
+
sessionId = await this.namedSessions.resolve(this.namedSessionOwner(envelope), namedTurn.sessionId);
|
|
7058
|
+
} catch (error) {
|
|
7059
|
+
await this.sendNamedSessionError(envelope, error);
|
|
7060
|
+
return;
|
|
7061
|
+
}
|
|
7062
|
+
if (!sessionId) {
|
|
7063
|
+
process.stderr.write(`[${this.name}] dropped collected turn from ${envelope.senderId} for session ${namedTurn.sessionId}: selected task changed before it ran
|
|
7064
|
+
`);
|
|
7065
|
+
return;
|
|
7066
|
+
}
|
|
7067
|
+
} else if (this.namedSessions) {
|
|
7068
|
+
try {
|
|
7069
|
+
sessionId = await this.namedSessions.resolve(this.namedSessionOwner(envelope), void 0, (resolvedSessionId) => {
|
|
7070
|
+
const binding = this.bindNamedTurn(envelope, resolvedSessionId);
|
|
7071
|
+
namedTurn = binding;
|
|
7072
|
+
return () => this.releaseNamedTurnBinding(binding);
|
|
7073
|
+
});
|
|
7074
|
+
} catch (error) {
|
|
7075
|
+
await this.sendNamedSessionError(envelope, error);
|
|
7076
|
+
return;
|
|
7077
|
+
}
|
|
7078
|
+
if (!sessionId) {
|
|
7079
|
+
await this.sendThreadMessage(envelope.chatId, envelope.threadId, "No task is currently selected. Use /session new <name> or /session use <name>.");
|
|
7080
|
+
return;
|
|
7081
|
+
}
|
|
7082
|
+
} else {
|
|
7083
|
+
sessionId = await this.router.resolve(this.name, envelope.senderId, envelope.chatId, envelope.threadId, this.config.cwd, envelope.isGroup);
|
|
7084
|
+
}
|
|
5999
7085
|
if (bangText.startsWith("!")) {
|
|
6000
7086
|
const cmd = bangText.slice(1).trim();
|
|
6001
7087
|
const bridgeShellCommand = this.bridge.shellCommand;
|
|
@@ -6040,12 +7126,19 @@ ${promptText}`;
|
|
|
6040
7126
|
}
|
|
6041
7127
|
let imageBase64 = envelope.imageBase64;
|
|
6042
7128
|
let imageMimeType = envelope.imageMimeType;
|
|
7129
|
+
const images = [];
|
|
7130
|
+
if (imageBase64 && imageMimeType) {
|
|
7131
|
+
images.push({ data: imageBase64, mimeType: imageMimeType });
|
|
7132
|
+
}
|
|
6043
7133
|
if (envelope.attachments?.length) {
|
|
6044
7134
|
const filePaths = [];
|
|
6045
7135
|
for (const att of envelope.attachments) {
|
|
6046
|
-
if (att.type === "image" && att.data
|
|
6047
|
-
|
|
6048
|
-
|
|
7136
|
+
if (att.type === "image" && att.data) {
|
|
7137
|
+
images.push({ data: att.data, mimeType: att.mimeType });
|
|
7138
|
+
if (!imageBase64) {
|
|
7139
|
+
imageBase64 = att.data;
|
|
7140
|
+
imageMimeType = att.mimeType;
|
|
7141
|
+
}
|
|
6049
7142
|
} else if (att.filePath) {
|
|
6050
7143
|
const label = att.type === "file" ? "file" : att.type;
|
|
6051
7144
|
const name = att.fileName ? ` "${sanitizeQuotedText(att.fileName, 128)}"` : "";
|
|
@@ -6131,8 +7224,13 @@ ${promptText}`;
|
|
|
6131
7224
|
this.instructedSessions.add(sessionId);
|
|
6132
7225
|
}
|
|
6133
7226
|
const prev = this.sessionQueues.get(sessionId) ?? Promise.resolve();
|
|
6134
|
-
const generation = this.sessionGenerations.get(sessionId) ?? 0;
|
|
7227
|
+
const generation = namedTurn?.generation ?? this.sessionGenerations.get(sessionId) ?? 0;
|
|
6135
7228
|
const useBlockStreaming = this.config.blockStreaming === "on";
|
|
7229
|
+
if (namedTurn) {
|
|
7230
|
+
namedTurn.claimed = true;
|
|
7231
|
+
} else {
|
|
7232
|
+
this.reserveQueuedTurn(sessionId);
|
|
7233
|
+
}
|
|
6136
7234
|
const current = prev.then(async () => {
|
|
6137
7235
|
clearTimeout(steerWatchdog);
|
|
6138
7236
|
if (this.dropQueuedTurnIfStale(sessionId, generation, envelope)) {
|
|
@@ -6199,7 +7297,7 @@ ${promptToSend}`;
|
|
|
6199
7297
|
doneResolve = r;
|
|
6200
7298
|
});
|
|
6201
7299
|
const promptState = {
|
|
6202
|
-
runId:
|
|
7300
|
+
runId: randomUUID3(),
|
|
6203
7301
|
owner: {
|
|
6204
7302
|
kind: "channel_user",
|
|
6205
7303
|
id: envelope.senderId
|
|
@@ -6273,6 +7371,7 @@ ${promptToSend}`;
|
|
|
6273
7371
|
promptBridge.on("responseBoundary", onResponseBoundary);
|
|
6274
7372
|
try {
|
|
6275
7373
|
const response = await promptBridge.prompt(sessionId, promptToSend, {
|
|
7374
|
+
...images.length > 0 ? { images } : {},
|
|
6276
7375
|
imageBase64,
|
|
6277
7376
|
imageMimeType,
|
|
6278
7377
|
displayText
|
|
@@ -6335,7 +7434,10 @@ ${promptToSend}`;
|
|
|
6335
7434
|
} finally {
|
|
6336
7435
|
promptBridge.off("textChunk", onChunk);
|
|
6337
7436
|
promptBridge.off("responseBoundary", onResponseBoundary);
|
|
6338
|
-
streamer
|
|
7437
|
+
if (streamer) {
|
|
7438
|
+
streamer.stop();
|
|
7439
|
+
await streamer.drain();
|
|
7440
|
+
}
|
|
6339
7441
|
const stillCurrent = this.activePrompts.get(sessionId) === promptState;
|
|
6340
7442
|
if (!promptState.clearEvicted) {
|
|
6341
7443
|
try {
|
|
@@ -6349,39 +7451,15 @@ ${promptToSend}`;
|
|
|
6349
7451
|
this.activePrompts.delete(sessionId);
|
|
6350
7452
|
}
|
|
6351
7453
|
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
|
-
}
|
|
7454
|
+
this.drainCollectBufferForCurrentPrompt(sessionId, stillCurrent, "prompt completion");
|
|
6380
7455
|
}
|
|
6381
7456
|
});
|
|
6382
|
-
|
|
7457
|
+
const tracked = current.finally(() => {
|
|
7458
|
+
this.releaseQueuedTurn(sessionId);
|
|
7459
|
+
});
|
|
7460
|
+
this.sessionQueues.set(sessionId, tracked.catch(() => {
|
|
6383
7461
|
}));
|
|
6384
|
-
await
|
|
7462
|
+
await tracked;
|
|
6385
7463
|
}
|
|
6386
7464
|
pairingRejectionMessage(rejected) {
|
|
6387
7465
|
return rejected === "sender_pending" ? "You already have a pending pairing request. It must be approved or expire before another can be created." : "Too many pending pairing requests. Please try again later.";
|
|
@@ -6491,10 +7569,10 @@ var PollingChannelBase = class extends ChannelBase {
|
|
|
6491
7569
|
}
|
|
6492
7570
|
saveCursor() {
|
|
6493
7571
|
const path4 = this.cursorPath();
|
|
6494
|
-
|
|
7572
|
+
mkdirSync5(join6(getGlobalQwenDir(), "channels"), { recursive: true });
|
|
6495
7573
|
const tmp = `${path4}.tmp`;
|
|
6496
|
-
|
|
6497
|
-
|
|
7574
|
+
writeFileSync5(tmp, JSON.stringify(this.cursor) + "\n", "utf-8");
|
|
7575
|
+
renameSync5(tmp, path4);
|
|
6498
7576
|
}
|
|
6499
7577
|
startPollLoop() {
|
|
6500
7578
|
if (this.running)
|
|
@@ -6518,7 +7596,7 @@ var PollingChannelBase = class extends ChannelBase {
|
|
|
6518
7596
|
} catch (err) {
|
|
6519
7597
|
this.consecutiveErrors++;
|
|
6520
7598
|
const backoff = Math.min(INITIAL_BACKOFF * 2 ** (this.consecutiveErrors - 1), MAX_BACKOFF);
|
|
6521
|
-
|
|
7599
|
+
process4.stderr.write(`[Channel:${this.name}] poll error (attempt ${this.consecutiveErrors}), backing off ${backoff}ms: ${err}
|
|
6522
7600
|
`);
|
|
6523
7601
|
await this.abortableSleep(backoff);
|
|
6524
7602
|
continue;
|
|
@@ -6528,7 +7606,7 @@ var PollingChannelBase = class extends ChannelBase {
|
|
|
6528
7606
|
}
|
|
6529
7607
|
loadCursorFromDisk() {
|
|
6530
7608
|
try {
|
|
6531
|
-
const raw =
|
|
7609
|
+
const raw = readFileSync5(this.cursorPath(), "utf-8").trim();
|
|
6532
7610
|
if (!raw)
|
|
6533
7611
|
return null;
|
|
6534
7612
|
return this.validateCursor(JSON.parse(raw));
|
|
@@ -6562,6 +7640,48 @@ function getTextContent(content) {
|
|
|
6562
7640
|
return getString(content["text"]);
|
|
6563
7641
|
}
|
|
6564
7642
|
__name(getTextContent, "getTextContent");
|
|
7643
|
+
var CHANNEL_IMAGE_EXTENSIONS = ["bmp", "gif", "jpeg", "png", "webp"];
|
|
7644
|
+
var CHANNEL_IMAGE_MAX_UPLOAD_BYTES = 8 * 1024 * 1024;
|
|
7645
|
+
var CHANNEL_IMAGE_INLINE_MAX_BASE64_BYTES = 8 * 1024 * 1024;
|
|
7646
|
+
function channelImageName(mimeType, index = 0) {
|
|
7647
|
+
if (!mimeType.startsWith("image/")) {
|
|
7648
|
+
return void 0;
|
|
7649
|
+
}
|
|
7650
|
+
const extension = mimeType.slice("image/".length);
|
|
7651
|
+
if (!CHANNEL_IMAGE_EXTENSIONS.includes(extension)) {
|
|
7652
|
+
return void 0;
|
|
7653
|
+
}
|
|
7654
|
+
return index === 0 ? `image.${extension}` : `image-${index + 1}.${extension}`;
|
|
7655
|
+
}
|
|
7656
|
+
__name(channelImageName, "channelImageName");
|
|
7657
|
+
function decodeChannelImage(data, oversizedReason) {
|
|
7658
|
+
let estimatedBytes = Math.floor(data.length * 3 / 4);
|
|
7659
|
+
if (data.length % 4 === 0) {
|
|
7660
|
+
if (data.endsWith("=="))
|
|
7661
|
+
estimatedBytes -= 2;
|
|
7662
|
+
else if (data.endsWith("="))
|
|
7663
|
+
estimatedBytes -= 1;
|
|
7664
|
+
}
|
|
7665
|
+
if (estimatedBytes > CHANNEL_IMAGE_MAX_UPLOAD_BYTES) {
|
|
7666
|
+
return { skip: oversizedReason };
|
|
7667
|
+
}
|
|
7668
|
+
const bytes = Buffer.from(data, "base64");
|
|
7669
|
+
if (bytes.byteLength === 0) {
|
|
7670
|
+
return { skip: "empty once base64-decoded" };
|
|
7671
|
+
}
|
|
7672
|
+
return { bytes };
|
|
7673
|
+
}
|
|
7674
|
+
__name(decodeChannelImage, "decodeChannelImage");
|
|
7675
|
+
function isDefinitePromptAdmissionRejection(error) {
|
|
7676
|
+
if (!isRecord3(error)) {
|
|
7677
|
+
return false;
|
|
7678
|
+
}
|
|
7679
|
+
if (error["name"] === "DaemonPendingPromptLimitError") {
|
|
7680
|
+
return true;
|
|
7681
|
+
}
|
|
7682
|
+
return error["name"] === "DaemonHttpError" && typeof error["status"] === "number" && error["_daemonTurnError"] !== true;
|
|
7683
|
+
}
|
|
7684
|
+
__name(isDefinitePromptAdmissionRejection, "isDefinitePromptAdmissionRejection");
|
|
6565
7685
|
function getSessionUpdate(data) {
|
|
6566
7686
|
if (!isRecord3(data) || !isRecord3(data["update"])) {
|
|
6567
7687
|
return void 0;
|
|
@@ -6633,8 +7753,9 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6633
7753
|
availableCommandsBySession = /* @__PURE__ */ new Map();
|
|
6634
7754
|
turnBarriers = /* @__PURE__ */ new Map();
|
|
6635
7755
|
channelLoopToolHandlers = [];
|
|
7756
|
+
channelLoopDisabledSessions = /* @__PURE__ */ new Set();
|
|
6636
7757
|
registeredChannelLoopMcpSessions = /* @__PURE__ */ new Set();
|
|
6637
|
-
|
|
7758
|
+
channelLoopMcpOperations = /* @__PURE__ */ new Map();
|
|
6638
7759
|
channelLoopMcpServer;
|
|
6639
7760
|
connected = false;
|
|
6640
7761
|
lifecycleGeneration = 0;
|
|
@@ -6694,7 +7815,12 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6694
7815
|
await this.rejectStaleSession(session);
|
|
6695
7816
|
}
|
|
6696
7817
|
this.attachSession(session, bindingToken);
|
|
6697
|
-
|
|
7818
|
+
if (options?.enableChannelLoops === false) {
|
|
7819
|
+
this.channelLoopDisabledSessions.add(session.sessionId);
|
|
7820
|
+
void this.reconcileChannelLoopMcpForSession(session.sessionId);
|
|
7821
|
+
} else {
|
|
7822
|
+
await this.reconcileChannelLoopMcpForSession(session.sessionId);
|
|
7823
|
+
}
|
|
6698
7824
|
return session.sessionId;
|
|
6699
7825
|
}
|
|
6700
7826
|
async loadSession(sessionId, cwd, options, bindingToken) {
|
|
@@ -6716,7 +7842,12 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6716
7842
|
throw new Error(`Daemon returned session ${session.sessionId} while loading ${sessionId}`);
|
|
6717
7843
|
}
|
|
6718
7844
|
this.attachSession(session, bindingToken);
|
|
6719
|
-
|
|
7845
|
+
if (options?.enableChannelLoops === false) {
|
|
7846
|
+
this.channelLoopDisabledSessions.add(session.sessionId);
|
|
7847
|
+
void this.reconcileChannelLoopMcpForSession(session.sessionId);
|
|
7848
|
+
} else {
|
|
7849
|
+
await this.reconcileChannelLoopMcpForSession(session.sessionId);
|
|
7850
|
+
}
|
|
6720
7851
|
return session.sessionId;
|
|
6721
7852
|
}
|
|
6722
7853
|
registerChannelLoopToolHandler(handler) {
|
|
@@ -6729,7 +7860,9 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6729
7860
|
cancel: /* @__PURE__ */ __name((sessionId, id) => this.resolveChannelLoopToolHandler(sessionId).cancel(sessionId, id), "cancel")
|
|
6730
7861
|
});
|
|
6731
7862
|
for (const sessionId of this.sessions.keys()) {
|
|
6732
|
-
|
|
7863
|
+
if (!this.channelLoopDisabledSessions.has(sessionId)) {
|
|
7864
|
+
void this.reconcileChannelLoopMcpForSession(sessionId);
|
|
7865
|
+
}
|
|
6733
7866
|
}
|
|
6734
7867
|
}
|
|
6735
7868
|
async prompt(sessionId, text, options) {
|
|
@@ -6773,29 +7906,101 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6773
7906
|
this.on("responseBoundary", clearChunks);
|
|
6774
7907
|
this.on("sessionDied", onSessionDied);
|
|
6775
7908
|
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;
|
|
7909
|
+
const uploadedAttachmentIds = [];
|
|
7910
|
+
let rollbackUploadedAttachments = false;
|
|
7911
|
+
const uploadAttachment = session.uploadAttachment?.bind(session);
|
|
7912
|
+
const removeAttachment = session.removeAttachment?.bind(session);
|
|
6786
7913
|
try {
|
|
6787
|
-
const
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
7914
|
+
const prompt = [];
|
|
7915
|
+
const images = resolvePromptImages(options);
|
|
7916
|
+
if (this.options.sessionAttachments && uploadAttachment && removeAttachment) {
|
|
7917
|
+
try {
|
|
7918
|
+
const uploads = await Promise.allSettled(images.map(async (image, index) => {
|
|
7919
|
+
const name = channelImageName(image.mimeType, index);
|
|
7920
|
+
if (!name) {
|
|
7921
|
+
process.stderr.write(`[DaemonChannelBridge] skipped channel image with unsupported MIME type ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
|
|
7922
|
+
`);
|
|
7923
|
+
return void 0;
|
|
7924
|
+
}
|
|
7925
|
+
const decoded = decodeChannelImage(image.data, "above the daemon attachment size limit");
|
|
7926
|
+
if ("skip" in decoded) {
|
|
7927
|
+
process.stderr.write(`[DaemonChannelBridge] skipped channel image ${decoded.skip} ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
|
|
7928
|
+
`);
|
|
7929
|
+
return void 0;
|
|
7930
|
+
}
|
|
7931
|
+
const attachment = await uploadAttachment(new Blob([decoded.bytes], {
|
|
7932
|
+
type: image.mimeType
|
|
7933
|
+
}), name, image.mimeType, controller.signal);
|
|
7934
|
+
const attachmentId = getString(attachment["attachmentId"]);
|
|
7935
|
+
if (attachmentId)
|
|
7936
|
+
uploadedAttachmentIds.push(attachmentId);
|
|
7937
|
+
return attachment;
|
|
7938
|
+
}));
|
|
7939
|
+
const failure = uploads.find((upload) => upload.status === "rejected");
|
|
7940
|
+
if (failure) {
|
|
7941
|
+
throw failure.reason;
|
|
7942
|
+
}
|
|
7943
|
+
for (const upload of uploads) {
|
|
7944
|
+
if (upload.status === "fulfilled" && upload.value) {
|
|
7945
|
+
prompt.push(upload.value);
|
|
7946
|
+
}
|
|
7947
|
+
}
|
|
7948
|
+
} catch (error) {
|
|
7949
|
+
rollbackUploadedAttachments = true;
|
|
7950
|
+
throw error;
|
|
6797
7951
|
}
|
|
6798
|
-
}
|
|
7952
|
+
} else {
|
|
7953
|
+
let inlineBase64Bytes = 0;
|
|
7954
|
+
for (const image of images) {
|
|
7955
|
+
const decoded = decodeChannelImage(image.data, "above the inline image budget");
|
|
7956
|
+
if ("skip" in decoded) {
|
|
7957
|
+
process.stderr.write(`[DaemonChannelBridge] skipped channel image ${decoded.skip} ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
|
|
7958
|
+
`);
|
|
7959
|
+
continue;
|
|
7960
|
+
}
|
|
7961
|
+
if (inlineBase64Bytes + image.data.length > CHANNEL_IMAGE_INLINE_MAX_BASE64_BYTES) {
|
|
7962
|
+
process.stderr.write(`[DaemonChannelBridge] skipped channel image to keep the inline prompt under the daemon body limit ${sanitizeLogText(image.mimeType, 128)} for session ${sanitizeLogText(sessionId, 128)}
|
|
7963
|
+
`);
|
|
7964
|
+
continue;
|
|
7965
|
+
}
|
|
7966
|
+
inlineBase64Bytes += image.data.length;
|
|
7967
|
+
prompt.push({
|
|
7968
|
+
type: "image",
|
|
7969
|
+
data: image.data,
|
|
7970
|
+
mimeType: image.mimeType
|
|
7971
|
+
});
|
|
7972
|
+
}
|
|
7973
|
+
}
|
|
7974
|
+
prompt.push({ type: "text", text });
|
|
7975
|
+
if (controller.signal.aborted) {
|
|
7976
|
+
rollbackUploadedAttachments = true;
|
|
7977
|
+
controller.signal.throwIfAborted();
|
|
7978
|
+
}
|
|
7979
|
+
const promptAuthorization = this.options.promptAuthorization;
|
|
7980
|
+
if (controller.signal.aborted) {
|
|
7981
|
+
rollbackUploadedAttachments = true;
|
|
7982
|
+
throw controller.signal.reason;
|
|
7983
|
+
}
|
|
7984
|
+
let result;
|
|
7985
|
+
try {
|
|
7986
|
+
result = await session.prompt({
|
|
7987
|
+
prompt,
|
|
7988
|
+
_meta: {
|
|
7989
|
+
[CHANNEL_PROMPT_META_KEY]: true,
|
|
7990
|
+
...promptAuthorization ? {
|
|
7991
|
+
[CHANNEL_PROMPT_AUTHORIZATION_META_KEY]: promptAuthorization
|
|
7992
|
+
} : {},
|
|
7993
|
+
...options?.displayText !== void 0 ? {
|
|
7994
|
+
[CHANNEL_PROMPT_DISPLAY_TEXT_META_KEY]: options.displayText
|
|
7995
|
+
} : {}
|
|
7996
|
+
}
|
|
7997
|
+
}, controller.signal);
|
|
7998
|
+
} catch (error) {
|
|
7999
|
+
if (isDefinitePromptAdmissionRejection(error)) {
|
|
8000
|
+
rollbackUploadedAttachments = true;
|
|
8001
|
+
}
|
|
8002
|
+
throw error;
|
|
8003
|
+
}
|
|
6799
8004
|
await Promise.race([
|
|
6800
8005
|
turnBarrier,
|
|
6801
8006
|
new Promise((resolve3) => setTimeout(resolve3, 0))
|
|
@@ -6818,6 +8023,16 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
6818
8023
|
if (controllers.size === 0 && this.activePromptControllers.get(sessionId) === controllers) {
|
|
6819
8024
|
this.activePromptControllers.delete(sessionId);
|
|
6820
8025
|
}
|
|
8026
|
+
if (rollbackUploadedAttachments && removeAttachment) {
|
|
8027
|
+
const removals = await Promise.allSettled(uploadedAttachmentIds.map((attachmentId) => removeAttachment(attachmentId)));
|
|
8028
|
+
removals.forEach((removal, index) => {
|
|
8029
|
+
if (removal.status === "rejected") {
|
|
8030
|
+
const reason = removal.reason instanceof Error ? removal.reason.message : String(removal.reason);
|
|
8031
|
+
process.stderr.write(`[DaemonChannelBridge] failed to remove channel image ${sanitizeLogText(uploadedAttachmentIds[index] ?? "", 128)} for session ${sanitizeLogText(sessionId, 128)} during rollback: ${sanitizeLogText(reason, 256)}
|
|
8032
|
+
`);
|
|
8033
|
+
}
|
|
8034
|
+
});
|
|
8035
|
+
}
|
|
6821
8036
|
}
|
|
6822
8037
|
}
|
|
6823
8038
|
async shellCommand(sessionId, command, signal) {
|
|
@@ -7175,6 +8390,7 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
7175
8390
|
this.eventControllers.delete(sessionId);
|
|
7176
8391
|
this.sessions.delete(sessionId);
|
|
7177
8392
|
this.sessionBindingTokens.delete(sessionId);
|
|
8393
|
+
this.channelLoopDisabledSessions.delete(sessionId);
|
|
7178
8394
|
this.abortActivePrompts(sessionId);
|
|
7179
8395
|
this.activePrompts.delete(sessionId);
|
|
7180
8396
|
this.availableCommandsBySession.delete(sessionId);
|
|
@@ -7192,45 +8408,45 @@ var DaemonChannelBridge = class extends EventEmitter2 {
|
|
|
7192
8408
|
}
|
|
7193
8409
|
}
|
|
7194
8410
|
if (unregisterChannelLoopMcp) {
|
|
7195
|
-
this.
|
|
8411
|
+
void this.reconcileChannelLoopMcpForSession(sessionId);
|
|
7196
8412
|
}
|
|
7197
8413
|
return session;
|
|
7198
8414
|
}
|
|
7199
|
-
|
|
7200
|
-
const
|
|
7201
|
-
const
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
8415
|
+
reconcileChannelLoopMcpForSession(sessionId) {
|
|
8416
|
+
const previous = this.channelLoopMcpOperations.get(sessionId) ?? Promise.resolve();
|
|
8417
|
+
const operation = previous.catch(() => void 0).then(async () => {
|
|
8418
|
+
const host = this.options.channelLoopMcpHost;
|
|
8419
|
+
const server = this.channelLoopMcpServer;
|
|
8420
|
+
const shouldRegister = host !== void 0 && server !== void 0 && this.sessions.has(sessionId) && !this.channelLoopDisabledSessions.has(sessionId);
|
|
8421
|
+
if (!shouldRegister) {
|
|
8422
|
+
if (host && this.registeredChannelLoopMcpSessions.has(sessionId)) {
|
|
8423
|
+
await host.unregister(sessionId);
|
|
8424
|
+
this.registeredChannelLoopMcpSessions.delete(sessionId);
|
|
8425
|
+
}
|
|
8426
|
+
return;
|
|
8427
|
+
}
|
|
8428
|
+
if (this.registeredChannelLoopMcpSessions.has(sessionId))
|
|
8429
|
+
return;
|
|
8430
|
+
await host.register(sessionId, (message) => server.handleMessage(message, { sessionId }));
|
|
8431
|
+
this.registeredChannelLoopMcpSessions.add(sessionId);
|
|
8432
|
+
if (!this.sessions.has(sessionId) || this.channelLoopDisabledSessions.has(sessionId)) {
|
|
7214
8433
|
await host.unregister(sessionId);
|
|
8434
|
+
this.registeredChannelLoopMcpSessions.delete(sessionId);
|
|
7215
8435
|
}
|
|
7216
8436
|
}).catch((error) => {
|
|
7217
8437
|
this.lastError = error;
|
|
7218
8438
|
}).finally(() => {
|
|
7219
|
-
if (this.
|
|
7220
|
-
this.
|
|
8439
|
+
if (this.channelLoopMcpOperations.get(sessionId) === operation) {
|
|
8440
|
+
this.channelLoopMcpOperations.delete(sessionId);
|
|
7221
8441
|
}
|
|
7222
8442
|
});
|
|
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
|
-
});
|
|
8443
|
+
this.channelLoopMcpOperations.set(sessionId, operation);
|
|
8444
|
+
return operation;
|
|
7232
8445
|
}
|
|
7233
8446
|
resolveChannelLoopToolHandler(sessionId) {
|
|
8447
|
+
if (!this.sessions.has(sessionId) || this.channelLoopDisabledSessions.has(sessionId)) {
|
|
8448
|
+
throw new Error("Channel loop tools are unavailable for this session");
|
|
8449
|
+
}
|
|
7234
8450
|
const handler = this.channelLoopToolHandlers.find((candidate) => candidate.canHandle?.(sessionId) === true || this.channelLoopToolHandlers.length === 1 && !candidate.canHandle);
|
|
7235
8451
|
if (handler)
|
|
7236
8452
|
return handler;
|