@qwen-code/qwen-code 0.22.2-preview.1 → 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/goal-draft/SKILL.md +109 -0
- package/bundled/qc-helper/docs/configuration/settings.md +37 -36
- package/bundled/qc-helper/docs/features/_meta.ts +1 -0
- 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/commands.md +2 -1
- package/bundled/qc-helper/docs/features/computer-use.md +2 -2
- package/bundled/qc-helper/docs/features/goals.md +58 -0
- package/bundled/qc-helper/docs/features/headless.md +13 -11
- package/bundled/qc-helper/docs/features/status-line.md +3 -3
- 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-X42SXBJH.js +110 -0
- package/chunks/{StandaloneSessionPicker-5D2L22SF.js → StandaloneSessionPicker-2FJAOLGG.js} +81 -83
- package/chunks/{acpAgent-4DCKLA32.js → acpAgent-2ZMIJAFV.js} +1153 -357
- package/chunks/{agent-B4AFZL6H.js → agent-X5WX3L2I.js} +44 -46
- package/chunks/agent-headless-IZM25LJA.js +85 -0
- package/chunks/{anthropicContentGenerator-U76GTVB6.js → anthropicContentGenerator-YVTB6QX7.js} +46 -50
- package/chunks/{artifact-tool-XDJCZZCF.js → artifact-tool-XSMPHZBT.js} +11 -3
- package/chunks/{askUserQuestion-ZXXERLEV.js → askUserQuestion-4SWJY65V.js} +2 -2
- package/chunks/bridge-ME6QXZNA.js +119 -0
- package/chunks/{channel-management-service-HXXS3HOZ.js → channel-management-service-HQ6KDS7C.js} +6 -6
- package/chunks/channel-settings-store-PEOI7XCG.js +120 -0
- package/chunks/{channel-worker-group-EVDC2P4K.js → channel-worker-group-6NA6BBZV.js} +8 -7
- package/chunks/{channel-worker-manager-IJJZHGEI.js → channel-worker-manager-NEZ2J6RK.js} +10 -8
- package/chunks/{channel-worker-supervisor-YSKB5GVB.js → channel-worker-supervisor-FKU7DIHJ.js} +6 -5
- package/chunks/{chunk-7QGEJI7M.js → chunk-23RFD54N.js} +1 -1
- package/chunks/{chunk-JLU3FPQR.js → chunk-2HYKTO7V.js} +5 -5
- package/chunks/chunk-2LNNVAUV.js +1584 -0
- package/chunks/{chunk-FJAITR4L.js → chunk-2OBKDPZZ.js} +2 -2
- package/chunks/{chunk-JSY43GMI.js → chunk-2OUD5T67.js} +185 -174
- package/chunks/{chunk-LJP2BBMK.js → chunk-2SXSLSQE.js} +4 -4
- package/chunks/chunk-2TNL5KPM.js +49 -0
- package/chunks/{chunk-XMSMTU6H.js → chunk-2YOHKHYR.js} +4 -4
- package/chunks/{chunk-MCKJTBZN.js → chunk-36GSQ3MM.js} +2 -2
- package/chunks/chunk-36LGBEM6.js +130 -0
- package/chunks/{chunk-ZWKGZB2K.js → chunk-3JXM2CVW.js} +3 -3
- package/chunks/{chunk-X3XGCJ32.js → chunk-3SZC4VP4.js} +28 -111
- package/chunks/{chunk-ILPXSKGQ.js → chunk-3WK3QDNV.js} +2 -2
- package/chunks/{chunk-IKN3IUJP.js → chunk-3XKLXHGK.js} +17 -9
- package/chunks/{chunk-ZLTEQYNC.js → chunk-42IDLQWS.js} +26 -20
- package/chunks/chunk-4CMJKUND.js +95 -0
- package/chunks/{chunk-77TOMRBG.js → chunk-4K7KNVWH.js} +3 -3
- package/chunks/{chunk-SQPOJ4ZI.js → chunk-4NDLQAY2.js} +27 -270
- package/chunks/{chunk-V3GINY6K.js → chunk-4QHPPXK2.js} +16 -8
- package/chunks/{chunk-VBF4KZRQ.js → chunk-4TT3IVGA.js} +1 -1
- package/chunks/{chunk-IOGKXPL3.js → chunk-4VPCTGH6.js} +6 -6
- package/chunks/chunk-4VQBQLIZ.js +293 -0
- package/chunks/{chunk-UCSV2IZW.js → chunk-62GQFYID.js} +3 -3
- package/chunks/{chunk-SLEWBQWM.js → chunk-6DIGWMGT.js} +2 -2
- package/chunks/{chunk-BRVWYMKV.js → chunk-6MBXY6WM.js} +1 -1
- package/chunks/{chunk-PVBT5QPD.js → chunk-6PJOTWAN.js} +1 -1
- package/chunks/{chunk-WZ3QKPMQ.js → chunk-6X7EU5TI.js} +19 -31
- package/chunks/{chunk-KQYW2GB5.js → chunk-6ZPDLGHN.js} +6 -6
- package/chunks/{chunk-AKQA3WCH.js → chunk-734C6JGI.js} +723 -3399
- package/chunks/{chunk-F7M6ZFGU.js → chunk-73RM4POK.js} +8 -8
- package/chunks/{chunk-ELY4QVIB.js → chunk-7EQQR46F.js} +1 -1
- package/chunks/{chunk-KGJGEEVR.js → chunk-7FA2II6K.js} +10 -1
- package/chunks/chunk-7J6OTNGO.js +424 -0
- package/chunks/{chunk-LWLEPXKK.js → chunk-7LOELM2I.js} +19 -2
- package/chunks/{chunk-DIMEPKCW.js → chunk-7S226YTK.js} +1 -1
- package/chunks/{chunk-AWFZDYSD.js → chunk-7T52W4SW.js} +1 -1
- package/chunks/{chunk-3JKUX4PB.js → chunk-7VMIMAVB.js} +9 -9
- package/chunks/{chunk-2MHGD7EE.js → chunk-A3LWD5MK.js} +1 -1
- package/chunks/{chunk-E4I5MVBR.js → chunk-ABS25QER.js} +1 -1
- package/chunks/{chunk-TDQBVEPK.js → chunk-AIAOIFY4.js} +10 -8
- package/chunks/{chunk-NEZZHGEI.js → chunk-AYFA7MZV.js} +6 -6
- package/chunks/{chunk-FVDQ5E67.js → chunk-B466ZSHZ.js} +3 -3
- package/chunks/{chunk-6US7BF3G.js → chunk-B5PHIJGI.js} +183 -5
- package/chunks/{chunk-4TPAG3O7.js → chunk-BFPESSTR.js} +1 -1
- package/chunks/{chunk-WBT3STGU.js → chunk-BKWNKLZB.js} +20 -20
- package/chunks/{chunk-T45CL2NT.js → chunk-BOWFGCEU.js} +3 -3
- package/chunks/{chunk-IPQA4SR5.js → chunk-BWORX6FA.js} +1 -1
- package/chunks/{chunk-BKDAF7ES.js → chunk-BXDAPVQU.js} +2 -2
- package/chunks/{chunk-ZEADBZLO.js → chunk-C2X7KY45.js} +3 -3
- package/chunks/{chunk-PTQF7WRA.js → chunk-C3PEFRKD.js} +6 -6
- package/chunks/{chunk-7OBH4AM2.js → chunk-CAJTKR6W.js} +5 -5
- package/chunks/{chunk-2FN2HNTI.js → chunk-CFJY4DGX.js} +2 -2
- package/chunks/{chunk-SUE2ZMUH.js → chunk-CIZCL2MW.js} +49 -31
- package/chunks/{chunk-W2WRNJKX.js → chunk-CLWO3B77.js} +6 -6
- package/chunks/{chunk-U45D46O5.js → chunk-CMHFCLBU.js} +1 -1
- package/chunks/{chunk-LRKMQPMD.js → chunk-CQH5KTKC.js} +1 -1
- package/chunks/{chunk-JECO75XD.js → chunk-D5LUXLUH.js} +2 -2
- package/chunks/chunk-DJBQCO2E.js +842 -0
- package/chunks/{chunk-BQBUZDDH.js → chunk-DJECZIJX.js} +42 -42
- package/chunks/{chunk-GTUZ3R6I.js → chunk-DJEZ55D4.js} +351 -113
- package/chunks/{chunk-HPECDZ6X.js → chunk-DJPASAUV.js} +3866 -1077
- package/chunks/{chunk-VYWCFWI5.js → chunk-DMLVSW7L.js} +542 -654
- package/chunks/{chunk-EB4VDXNI.js → chunk-DYXFD5RO.js} +182 -3
- package/chunks/{chunk-ZKAZP2ZG.js → chunk-EB6QRGHM.js} +3 -3
- package/chunks/{chunk-UWICO5KN.js → chunk-EFWRMA2I.js} +3 -3
- package/chunks/{chunk-3UZCLCXR.js → chunk-EKSCLBBF.js} +3 -3
- package/chunks/{chunk-ALTQLFEG.js → chunk-EQSBPBZ3.js} +5 -194
- package/chunks/{chunk-624NHDGB.js → chunk-ERFDKH32.js} +11 -0
- package/chunks/{chunk-4K44OMLI.js → chunk-FROAFEFB.js} +3 -3
- package/chunks/{chunk-MZS7GEC7.js → chunk-FTF2YIZ5.js} +1 -1
- package/chunks/{chunk-Z7VOMGDC.js → chunk-FTJMWDND.js} +0 -24
- package/chunks/{chunk-ZJG323IA.js → chunk-G6FECKTJ.js} +7 -5
- package/chunks/{chunk-TMGAS5TO.js → chunk-GBJBJ4HX.js} +26 -4
- package/chunks/{chunk-UCQOSV6A.js → chunk-GPGU4S2N.js} +3 -3
- package/chunks/{chunk-BHTJAO36.js → chunk-GVIVASYK.js} +7 -7
- package/chunks/chunk-H72BQNM3.js +645 -0
- package/chunks/{chunk-Z4X5MTRT.js → chunk-HCOBAG2L.js} +2 -4
- package/chunks/{chunk-FCSWZAS3.js → chunk-HP5LR5HL.js} +5 -5
- package/chunks/{chunk-7JPW6IYH.js → chunk-HVC3PNVC.js} +1 -1
- package/chunks/{chunk-OI2DR6QB.js → chunk-I6LYRJPB.js} +3 -3
- package/chunks/{chunk-E55H6BZ7.js → chunk-ICURCKIU.js} +1335 -119
- package/chunks/{chunk-PAJGKZAA.js → chunk-ISKH4QX3.js} +14 -12
- package/chunks/{chunk-AYVIGUUK.js → chunk-JB4JIVTJ.js} +1 -1
- package/chunks/{chunk-FN5C6C7G.js → chunk-JHYH6PYQ.js} +192 -478
- package/chunks/{chunk-T6OODF2U.js → chunk-JWALNCLT.js} +1 -1
- package/chunks/{chunk-SKNVVYIJ.js → chunk-K5KXUXWC.js} +3 -3
- package/chunks/{chunk-XUX6O2ZT.js → chunk-KID24ZFN.js} +4 -1
- package/chunks/{chunk-I6DY753S.js → chunk-KJO2LBUK.js} +944 -18
- package/chunks/{chunk-5T4LWWNX.js → chunk-KYWTPLBJ.js} +18 -21
- package/chunks/{chunk-CZFQ62RM.js → chunk-LHBUKJG2.js} +4 -4
- package/chunks/{chunk-TOF4UZHH.js → chunk-LUEHOUHM.js} +3 -3
- package/chunks/{chunk-2YDYDDE2.js → chunk-M3QRM3DE.js} +3 -3
- package/chunks/{chunk-GNMJNDZE.js → chunk-M6FOJXW3.js} +1 -1
- package/chunks/{chunk-ZGWDHSUM.js → chunk-MF7HCQWZ.js} +4 -4
- package/chunks/{chunk-66GIFJ63.js → chunk-MK2GIB46.js} +1 -1
- package/chunks/{chunk-TFV4RXPU.js → chunk-MO3FC2WT.js} +1 -1
- package/chunks/{chunk-TR7G7XFQ.js → chunk-MPNBVAEU.js} +5 -5
- package/chunks/{chunk-K3LPAED5.js → chunk-MQM5PJML.js} +24 -4
- package/chunks/{chunk-3WBMPR3S.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-27ONXAFY.js → chunk-NIFYWDYN.js} +1 -1
- package/chunks/{chunk-AP7WZONS.js → chunk-NMOD2FXQ.js} +19 -9
- package/chunks/{chunk-IJVDHPKD.js → chunk-NU7RKEZR.js} +2 -2
- package/chunks/{chunk-5URK47RN.js → chunk-NV55WWD6.js} +11595 -10375
- package/chunks/{chunk-HABT2Z7R.js → chunk-OU5APVWO.js} +4 -4
- package/chunks/{chunk-QR4AHMGQ.js → chunk-P2G476QN.js} +13 -13
- package/chunks/{chunk-5SM2AK65.js → chunk-PADPMMYM.js} +5 -5
- package/chunks/{chunk-RRKNX553.js → chunk-PDQGMSZK.js} +1 -1
- package/chunks/{chunk-GTUKW3I6.js → chunk-PPKZ7JOE.js} +1 -1
- package/chunks/{chunk-SAH4BD2J.js → chunk-PQEISIKS.js} +67 -0
- package/chunks/{chunk-XIRDDFRS.js → chunk-PZ66FRIC.js} +2644 -2296
- package/chunks/{chunk-H3HZ4AWW.js → chunk-Q2MSPKIJ.js} +1 -1
- package/chunks/{chunk-WEGXPP5E.js → chunk-QFCDF5PL.js} +1 -1
- package/chunks/{chunk-OF2DPXUS.js → chunk-QYMAKPY5.js} +119 -24
- package/chunks/{chunk-JVPFTDBK.js → chunk-RFIDDFAL.js} +1 -1
- package/chunks/chunk-RKFGE7JH.js +93 -0
- package/chunks/{chunk-CPPYZHBN.js → chunk-RU7BK6B3.js} +1 -1
- package/chunks/chunk-SEONHUS3.js +275 -0
- package/chunks/{chunk-DEYPMJPD.js → chunk-SMPR7SPO.js} +1 -1
- package/chunks/chunk-SS4MRMKE.js +3525 -0
- package/chunks/{chunk-BPMVPWBT.js → chunk-SS5SDCD3.js} +6 -6
- package/chunks/{chunk-J3CBIEDS.js → chunk-ST5MDGH3.js} +32 -15
- package/chunks/{chunk-J5352S3S.js → chunk-ST5V3ZOC.js} +3 -3
- package/chunks/{chunk-7CWEZQ6T.js → chunk-STUJ7IMZ.js} +0 -77
- package/chunks/{chunk-4K7NQDCG.js → chunk-T4EFG2P5.js} +18 -9
- package/chunks/{chunk-TWSKM647.js → chunk-T7KMERIX.js} +1 -1
- package/chunks/{chunk-5CMWFBML.js → chunk-TEBDFQBZ.js} +1 -1
- package/chunks/{chunk-EC5BONC3.js → chunk-TPI7O7HR.js} +1 -1
- package/chunks/{chunk-T2CRBIDJ.js → chunk-TTX2JUE6.js} +1 -1
- package/chunks/{chunk-VOEMVOM4.js → chunk-TVC6HQ2U.js} +2 -2
- package/chunks/{chunk-WXDZZFH3.js → chunk-TZBSGJ6C.js} +6 -6
- package/chunks/chunk-U54AYGEC.js +337 -0
- package/chunks/{chunk-2NNTE2ED.js → chunk-UHQFIS7N.js} +15 -0
- package/chunks/{chunk-76WGY6LW.js → chunk-URE3QBBB.js} +3 -3
- package/chunks/{chunk-KOJ6MHD5.js → chunk-UTLCH2FK.js} +1 -1
- package/chunks/{chunk-7DPKPBOQ.js → chunk-V2NDP3I4.js} +2 -2
- package/chunks/{chunk-FJWICLG7.js → chunk-V4264FEG.js} +9 -9
- package/chunks/{chunk-XR7OIAQF.js → chunk-V5J4J5TP.js} +2 -2
- package/chunks/{chunk-7UJTY5V3.js → chunk-V7EESTJV.js} +1 -1
- package/chunks/{chunk-Z6L3C4VF.js → chunk-VBCPNIQD.js} +39 -39
- package/chunks/{chunk-ATLGYSYH.js → chunk-VGIFXISB.js} +4 -4
- package/chunks/{chunk-X2UOOG5V.js → chunk-VQUGEVGR.js} +1 -1
- package/chunks/{chunk-XJS54K4A.js → chunk-VTHREBQM.js} +2 -2
- package/chunks/{chunk-AA7VBRBG.js → chunk-W3JH33JE.js} +8 -8
- package/chunks/{chunk-FPH6TXD3.js → chunk-WAOIHTJS.js} +1 -1
- package/chunks/{chunk-CDUN63IN.js → chunk-WGY3AVGM.js} +3 -3
- package/chunks/{chunk-JLGFGPZJ.js → chunk-WNPDGUJO.js} +22 -3
- package/chunks/{chunk-O3BUXIPM.js → chunk-WXEA74YB.js} +3 -3
- package/chunks/{chunk-YRLW2MSX.js → chunk-XBVNNDPK.js} +11 -4
- package/chunks/{chunk-VL4YX3FH.js → chunk-XF63PKEN.js} +1 -1
- package/chunks/{chunk-R7G7GKOX.js → chunk-XHF7WQ6B.js} +3 -3
- package/chunks/{chunk-3FSBMAYD.js → chunk-XQFT3QUF.js} +1 -1
- package/chunks/{chunk-3D6PDLNJ.js → chunk-XSG6G3G6.js} +49 -32
- package/chunks/{chunk-5JCZ3UKB.js → chunk-Y5S7LBOZ.js} +44 -7
- package/chunks/{chunk-OLS3MAXQ.js → chunk-YLSQLSH7.js} +15 -9
- package/chunks/{chunk-2SERYG4T.js → chunk-YTDVNSUQ.js} +1 -1
- package/chunks/{chunk-KPF2K55D.js → chunk-ZEYFMJQA.js} +39 -8
- package/chunks/{chunk-OT4YKUUV.js → chunk-ZU4UDIWX.js} +2 -2
- package/chunks/chunk-ZWJDJ6RU.js +2597 -0
- package/chunks/{chunk-R3T33BG5.js → chunk-ZYMLPEZQ.js} +3 -3
- package/chunks/{chunk-DID6IRGC.js → chunk-ZZMSC3RG.js} +56 -39
- package/chunks/{chunk-L436XFLJ.js → chunk-ZZUKP7HV.js} +1 -1
- package/chunks/config-utils-R6M3TA34.js +113 -0
- package/chunks/contextCommand-ZCAP23J6.js +106 -0
- package/chunks/{core-runtime-CEJRHCRF.js → core-runtime-AXB6SK5D.js} +59 -61
- package/chunks/{create-sub-session-RVHSWVAP.js → create-sub-session-23ITIELI.js} +3 -3
- package/chunks/{create-sub-session-DFZVIG56.js → create-sub-session-4QNE64MR.js} +164 -85
- package/chunks/{cron-create-IJGVR5R2.js → cron-create-KJ4IY6PP.js} +57 -10
- package/chunks/{cron-delete-4N5BI6GL.js → cron-delete-MBLQCAYY.js} +4 -4
- package/chunks/{cron-list-2CATIPUX.js → cron-list-DWH2PQ47.js} +4 -4
- package/chunks/{daemon-NAYK4XXU.js → daemon-3GNHB3M2.js} +523 -8
- package/chunks/{daemon-git-worktree-guard-L6ZUFKF7.js → daemon-git-worktree-guard-IVAJJWYK.js} +56 -58
- package/chunks/daemon-status-provider-K3SJ5C2U.js +117 -0
- package/chunks/daemon-trust-policy-DOR3YG62.js +112 -0
- package/chunks/{daemon-trust-policy-monitor-2G3EKQEU.js → daemon-trust-policy-monitor-KGGROSIV.js} +64 -66
- package/chunks/deferred-core-runtime-WOYLUQQ7.js +114 -0
- package/chunks/{display-image-6KHQNEAE.js → display-image-BE7I6ZB6.js} +5 -5
- package/chunks/{dist-DJBNNPLN.js → dist-4WXLCBDR.js} +1 -1
- package/chunks/{dist-TJUDJTCB.js → dist-5VXFRTWU.js} +280 -48
- package/chunks/{dist-6H6RZ5H6.js → dist-6BPYWHVD.js} +89 -65
- package/chunks/{dist-ZBVOXSKF.js → dist-GFN5DCBX.js} +74 -65
- package/chunks/{dist-SATKXKUR.js → dist-HZNT6GQS.js} +1 -1
- package/chunks/{dist-FVJZSIO4.js → dist-LVZCXVPF.js} +72 -20
- 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-OHBEVO4K.js +107 -0
- package/chunks/{edit-ZERGX3RV.js → edit-LEQLAGPZ.js} +45 -47
- package/chunks/{en-6MDBWMTG.js → en-YLXBXM4Z.js} +2 -1
- package/chunks/{enter-worktree-UOZ6ZTP7.js → enter-worktree-35QCAAFP.js} +7 -7
- package/chunks/{enterPlanMode-W2RP3P5B.js → enterPlanMode-ONQN3WHE.js} +47 -49
- package/chunks/{environment-ZBJ23YVR.js → environment-UANTR7A4.js} +60 -62
- package/chunks/errors-WAK6KFBC.js +113 -0
- package/chunks/{exit-worktree-G2QATUCV.js → exit-worktree-XQD6D543.js} +7 -7
- package/chunks/exitPlanMode-UEYBG6P5.js +83 -0
- package/chunks/{fast-path-4MWBCEUF.js → fast-path-WUL7UZ6H.js} +7 -7
- package/chunks/{fast-path-settings-XMFBYFUH.js → fast-path-settings-XH426LUU.js} +3 -3
- package/chunks/{glob-DKTNG32L.js → glob-Y54QUWDY.js} +44 -46
- package/chunks/{goal-tools-CNVXMSZD.js → goal-tools-PTD3MBA7.js} +10 -8
- package/chunks/{grep-3NLSS32D.js → grep-5GPICQBU.js} +6 -6
- package/chunks/handleAutoUpdate-FP3NWNSG.js +109 -0
- package/chunks/i18n-INIU5X7A.js +124 -0
- package/chunks/{image-gen-63NN2H5N.js → image-gen-J6FDGUZP.js} +8 -10
- package/chunks/initializer-TYODV6GE.js +113 -0
- package/chunks/installationInfo-AAPKYDSZ.js +107 -0
- package/chunks/{keychain-token-storage-3MGFRYGM.js → keychain-token-storage-37XPR34V.js} +2 -2
- package/chunks/list-TUODDN6J.js +116 -0
- package/chunks/{list-agents-EXRSHIDJ.js → list-agents-DI3YFTDT.js} +2 -2
- package/chunks/{gemini-X5GQHQYF.js → llm-NSAVK5V3.js} +117 -114
- package/chunks/{geminiContentGenerator-UDSWXZXL.js → llm-content-generator-AY2QKXHE.js} +29 -29
- package/chunks/loadedSettingsAdapter-KJCLZ3C4.js +110 -0
- package/chunks/{loggingContentGenerator-73C7TKDK.js → loggingContentGenerator-DSFQHXIF.js} +328 -37
- package/chunks/{loop-wakeup-XBPB2BRO.js → loop-wakeup-XJFONSQ7.js} +5 -5
- package/chunks/{ls-WFONAND2.js → ls-3VAT2J7A.js} +4 -4
- package/chunks/{lsp-DUKPDUXN.js → lsp-5W7IRUBH.js} +2 -2
- package/chunks/{managed-npm-update-H2KOLQZU.js → managed-npm-update-TSWENTK5.js} +57 -59
- package/chunks/mcp-MWSAPFOL.js +110 -0
- package/chunks/{monitor-TGCMNJJH.js → monitor-RW4GWJHB.js} +44 -46
- package/chunks/nonInteractiveCli-JXKL343Q.js +185 -0
- package/chunks/{notebook-edit-YEUNZYDX.js → notebook-edit-CQNYS5OR.js} +45 -47
- package/chunks/{open-with-auth-4UI3KHUR.js → open-with-auth-YN3QZHVM.js} +1 -1
- package/chunks/{openaiContentGenerator-J7LD4VSL.js → openaiContentGenerator-J6QIF7EX.js} +26 -28
- package/chunks/pidfile-QOIQZFT2.js +111 -0
- package/chunks/process-registry-OAEG6WGC.js +662 -0
- package/chunks/{processUtils-ZHFC7CSL.js → processUtils-GPHNANKZ.js} +2 -2
- package/chunks/prompt-terminal-ledger-FOMFTYOM.js +105 -0
- package/chunks/{qwenContentGenerator-YEWWK3JQ.js → qwenContentGenerator-WWDXPYQI.js} +49 -51
- package/chunks/{qwenOAuth2-JIYAWXTW.js → qwenOAuth2-OF5WRBQT.js} +7 -9
- package/chunks/{read-file-ZQI3WPVK.js → read-file-FQUQHC6N.js} +13 -13
- package/chunks/{read-mcp-resource-7A7NFSFL.js → read-mcp-resource-XD257PGJ.js} +2 -2
- package/chunks/{record-artifact-CPJ4IJSD.js → record-artifact-VW23GS4F.js} +4 -4
- package/chunks/{report-findings-VFM5DENC.js → report-findings-AO5VYFLR.js} +9 -5
- package/chunks/{request-shutdown-6IW46GD7.js → request-shutdown-BCLA3JV2.js} +7 -7
- package/chunks/resumeHistoryUtils-NSGF3UCB.js +118 -0
- package/chunks/ripGrep-4CAT6SKV.js +39 -0
- package/chunks/{run-qwen-serve-WHWU7NZ6.js → run-qwen-serve-5X6IDZER.js} +371 -357
- package/chunks/{runtime-E7BSPWQP.js → runtime-HK27GMPF.js} +69 -71
- package/chunks/scheduled-tasks-MN2ZZ5P6.js +127 -0
- package/chunks/{scheduler-TJPATFOM.js → scheduler-WD4MHMO5.js} +58 -60
- package/chunks/{sdk-exporters-http-XXSKBD2C.js → sdk-exporters-http-TTU2CHBU.js} +2 -2
- package/chunks/{sdk-impl-VPNBLV7I.js → sdk-impl-UUMVYMAL.js} +11 -5
- package/chunks/{send-message-OXZK6USO.js → send-message-AJ4OCUBV.js} +25 -9
- package/chunks/serve-2XCHPPGG.js +118 -0
- package/chunks/{server-AXCN7QBJ.js → server-GO2N5DH7.js} +6080 -4964
- package/chunks/{session-HO4US3AG.js → session-IFNHTUBT.js} +112 -112
- package/chunks/session-pr-refresh-I3UBWMVP.js +364 -0
- package/chunks/{settings-B2PFJJN4.js → settings-L6N54CU6.js} +63 -65
- package/chunks/{shell-7XMSANI3.js → shell-4Y5LLW6N.js} +44 -46
- package/chunks/{skill-NL3VH562.js → skill-YOWTRAKQ.js} +68 -24
- package/chunks/skill-settings-ZL4JY5UE.js +118 -0
- package/chunks/spawnChannel-ZST6XJVJ.js +112 -0
- package/chunks/standalone-update-THVU3OKY.js +118 -0
- package/chunks/{startInteractiveUI-G2XKSCQ7.js → startInteractiveUI-XDFSHBZD.js} +3076 -2475
- package/chunks/{stdioHelpers-7A2EE64Z.js → stdioHelpers-AY6XCDY2.js} +3 -1
- package/chunks/{syntheticOutput-5AJ444QT.js → syntheticOutput-ML43GEFN.js} +3 -3
- package/chunks/{task-create-TY7P6UHG.js → task-create-QN4LYRSE.js} +11 -11
- package/chunks/{task-list-BELMPA75.js → task-list-JPLGK45U.js} +5 -5
- package/chunks/{task-stop-U3S3KZG2.js → task-stop-CXEH5L2D.js} +2 -2
- package/chunks/{task-update-O4IGDPCK.js → task-update-IWEOJFV2.js} +11 -11
- package/chunks/{team-create-IYBUOORE.js → team-create-OOPXCDBA.js} +44 -46
- package/chunks/{team-delete-YWFXX77L.js → team-delete-RZVVSGCY.js} +5 -5
- package/chunks/{team-plan-approval-YKU57LBM.js → team-plan-approval-DSX5EWAT.js} +44 -46
- package/chunks/terminal-image-renderer-KS25G33E.js +114 -0
- package/chunks/theme-manager-YMZNG45F.js +102 -0
- package/chunks/{todoWrite-435EO2CS.js → todoWrite-WDJNQEMW.js} +43 -25
- package/chunks/{tool-search-4Q3KASIQ.js → tool-search-J5Q6U7DN.js} +22 -22
- package/chunks/total-session-admission-PVN64JVV.js +114 -0
- package/chunks/trustedFolders-FATSC4YI.js +122 -0
- package/chunks/{types-WYCFCEHR.js → types-HKLPXZB3.js} +1 -3
- package/chunks/{update-relaunch-4DC2KKFS.js → update-relaunch-SIKHLHVR.js} +6 -6
- package/chunks/updateCheck-D3YG2WCD.js +118 -0
- package/chunks/useAutoAcceptIndicator-ILINOSAS.js +120 -0
- package/chunks/{validateNonInterActiveAuth-YAE4YPO3.js → validateNonInterActiveAuth-JUDQP6V4.js} +105 -105
- package/chunks/{version-QFP7OGWX.js → version-C63GDDWN.js} +1 -1
- package/chunks/{web-fetch-6T23IK6Q.js → web-fetch-FOQZMNSC.js} +16 -18
- package/chunks/{web-search-WBWTCZK2.js → web-search-PUJTBFUC.js} +9 -11
- package/chunks/{web-shell-static-A6QMUNNV.js → web-shell-static-HNB4FDOE.js} +2 -2
- package/chunks/{workflow-FMAA3D4R.js → workflow-Y5IPINVH.js} +128 -94
- package/chunks/workspace-providers-status-IJ4VHFMB.js +114 -0
- package/chunks/{workspace-registration-store-CMMX7EPX.js → workspace-registration-store-SMJWI2AI.js} +1 -1
- package/chunks/workspace-registry-BTGZIMPK.js +123 -0
- package/chunks/workspace-service-SIN67ITC.js +126 -0
- package/chunks/workspace-skills-status-F3RKCUIX.js +113 -0
- package/chunks/{workspace-trust-reconciler-PJDQUTJL.js → workspace-trust-reconciler-PLIDLJZX.js} +72 -72
- package/chunks/write-file-PAIGB33C.js +88 -0
- package/chunks/{zh-TW-MV4FWFB6.js → zh-TW-RPTMRTHG.js} +2 -1
- package/chunks/{zh-X3WGU6SI.js → zh-UNY63FR3.js} +2 -1
- package/chunks/{zoom-image-WVYVFRXF.js → zoom-image-LYQD7BQA.js} +13 -13
- package/cli.js +16 -16
- package/locales/en.js +2 -0
- package/locales/zh-TW.js +2 -0
- package/locales/zh.js +2 -0
- package/package.json +3 -3
- package/web-shell/assets/{abnfDiagram-VCTEODGH-d-8xa8uz.js → abnfDiagram-VCTEODGH-TRCMSA_6.js} +1 -1
- package/web-shell/assets/{arc-BhB7CrGK.js → arc-D9Uls5b3.js} +1 -1
- package/web-shell/assets/{architectureDiagram-5GKGNRK7-BDCd_IcJ.js → architectureDiagram-5GKGNRK7-Bwb8jPXg.js} +1 -1
- package/web-shell/assets/{blockDiagram-NRAW4CY4-C-AHu6mi.js → blockDiagram-NRAW4CY4-DHj71TSX.js} +1 -1
- package/web-shell/assets/{c4Diagram-UCG6FXSJ-CFjQt0vl.js → c4Diagram-UCG6FXSJ-uKBBdbyr.js} +1 -1
- package/web-shell/assets/channel-CXjVLaBa.js +1 -0
- package/web-shell/assets/{chunk-2Q5K7J3B-DRjwqaDq.js → chunk-2Q5K7J3B-BZd0Phrl.js} +1 -1
- package/web-shell/assets/{chunk-5VM5RSS4-eHM9noJE.js → chunk-5VM5RSS4-7KNeNBqN.js} +1 -1
- package/web-shell/assets/{chunk-F27PBJKO-CcSWUYn1.js → chunk-F27PBJKO-BeboS7Fi.js} +1 -1
- package/web-shell/assets/{chunk-G27WJ6UU-DNr-RF5S.js → chunk-G27WJ6UU-RV3JONoj.js} +1 -1
- package/web-shell/assets/{chunk-JWPE2WC7-rVJHoNF8.js → chunk-JWPE2WC7-Yw9FCFsP.js} +1 -1
- package/web-shell/assets/{chunk-LCL6LL3I-B2aX0Gzy.js → chunk-LCL6LL3I-GIMnydDe.js} +1 -1
- package/web-shell/assets/{chunk-POPQ4Y6H-DVp07n32.js → chunk-POPQ4Y6H-Cfrux7bL.js} +1 -1
- package/web-shell/assets/{chunk-SVP7TREG-C6QB_KUr.js → chunk-SVP7TREG-BlvIN0CO.js} +1 -1
- package/web-shell/assets/{chunk-XXDRQBXY-CLVxDNCy.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-B_aKX3A3.js → cose-bilkent-JH36ORCC-DNGqTNwI.js} +1 -1
- package/web-shell/assets/{cynefin-OW5HDTMX-BBw2pPUa.js → cynefin-OW5HDTMX-CojMpaoH.js} +1 -1
- package/web-shell/assets/{cynefinDiagram-5FMLGOSQ-WWPiBTwk.js → cynefinDiagram-5FMLGOSQ-DF07YXGo.js} +1 -1
- package/web-shell/assets/{dagre-3AP2YEHR-CPtIdka-.js → dagre-3AP2YEHR-17-4WtCH.js} +1 -1
- package/web-shell/assets/{diagram-S7CK7UJ4-Dj5og1Xi.js → diagram-S7CK7UJ4-G3Wuyz9m.js} +1 -1
- package/web-shell/assets/{diagram-UQ7AKVKN-CC_x70YC.js → diagram-UQ7AKVKN-nyWIKZ8P.js} +1 -1
- package/web-shell/assets/{diagram-VSXAHHWV-rXMfsO4n.js → diagram-VSXAHHWV-tKHhmYSC.js} +1 -1
- package/web-shell/assets/{diagram-VX7I27RA-DEmLYQbh.js → diagram-VX7I27RA-CpDf1hYk.js} +1 -1
- package/web-shell/assets/{diagram-Z3DM3KII-B0ys6Vxx.js → diagram-Z3DM3KII-Cah2IFXG.js} +1 -1
- package/web-shell/assets/{ebnfDiagram-PWID7BFC-Bq6y6lKH.js → ebnfDiagram-PWID7BFC-DNaCkgxy.js} +1 -1
- package/web-shell/assets/{erDiagram-SSCWMZ5O-l69JeBZ4.js → erDiagram-SSCWMZ5O-CAdRgcf1.js} +1 -1
- package/web-shell/assets/{flowDiagram-A5DVABFB-Cm-EzGY4.js → flowDiagram-A5DVABFB-CMUJAShL.js} +1 -1
- package/web-shell/assets/{ganttDiagram-EL5Y4UJY-BRS9ZOp7.js → ganttDiagram-EL5Y4UJY-o-_MUNey.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-WWUBYQGX-DAMRKgLk.js → gitGraphDiagram-WWUBYQGX-_aG-2Z7O.js} +1 -1
- package/web-shell/assets/{index-Bncx8E8M.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-DasffaZp.js → infoDiagram-RXCK75RN-qJPeZbGP.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-5VMMS53U-CmRUt3C-.js → ishikawaDiagram-5VMMS53U-7Om4vNo1.js} +1 -1
- package/web-shell/assets/{journeyDiagram-EYS64GPL-DzhE_c1Z.js → journeyDiagram-EYS64GPL-BNtXqmBW.js} +1 -1
- package/web-shell/assets/{kanban-definition-3QL26DDD-BH5b6nn3.js → kanban-definition-3QL26DDD-COgFec8P.js} +1 -1
- package/web-shell/assets/{layout-BxoLK-LY.js → layout-B9n81PC9.js} +1 -1
- package/web-shell/assets/{linear-CqDPvVI7.js → linear-Bb6nXZQq.js} +1 -1
- package/web-shell/assets/{mermaid.core-DBoN4plo.js → mermaid.core-Cgrm3JVT.js} +6 -6
- package/web-shell/assets/{mindmap-definition-FBJOCRG2-DJJtXPEM.js → mindmap-definition-FBJOCRG2-TKwLTjnG.js} +1 -1
- package/web-shell/assets/{pegDiagram-XKGWAZYB-DmgPXP5l.js → pegDiagram-XKGWAZYB-BJPuvlpi.js} +1 -1
- package/web-shell/assets/{pieDiagram-E7YTZNPT-1ezjzcI5.js → pieDiagram-E7YTZNPT-D01VkNsW.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-AXDQQJYC-ChKARz3k.js → quadrantDiagram-AXDQQJYC-C4hHsiv-.js} +1 -1
- package/web-shell/assets/{railroadDiagram-O6MQD6OU-k7mNB_QT.js → railroadDiagram-O6MQD6OU-CuEO4xK2.js} +1 -1
- package/web-shell/assets/{requirementDiagram-EFPCY7ZU-B0XFsii0.js → requirementDiagram-EFPCY7ZU-D5J_vIo5.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-P5KCCOFB-repJ2Ukk.js → sankeyDiagram-P5KCCOFB-DQKY223V.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-WJ2MYXX4-D0gUSKGu.js → sequenceDiagram-WJ2MYXX4-D27YV_cm.js} +1 -1
- package/web-shell/assets/{sizeCapture-X5ZJPWSS-CE463F74.js → sizeCapture-X5ZJPWSS-TJg8UcwV.js} +1 -1
- package/web-shell/assets/{stateDiagram-HBIQ2CUA-003zYtJa.js → stateDiagram-HBIQ2CUA-CmnPzUIx.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-4QOOHH4V-DNKPyrDc.js +1 -0
- package/web-shell/assets/{swimlanes-XN3QIQJK-DBj8MFpu.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-D2aCh4a9.js → timeline-definition-24CTP7MA-BsFA5juR.js} +1 -1
- package/web-shell/assets/{vennDiagram-4TSXK5OY-BHMfWBpl.js → vennDiagram-4TSXK5OY-DREP2ECo.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-VM6X3IG4-RdHAj66j.js → wardleyDiagram-VM6X3IG4-iyd9lKi2.js} +1 -1
- package/web-shell/assets/{xychartDiagram-S5SC5T6Z-BgJWOvq0.js → xychartDiagram-S5SC5T6Z-CVr1_j9q.js} +1 -1
- package/web-shell/index.html +3 -3
- package/chunks/MaxSizedBox-BFVLZP2E.js +0 -112
- package/chunks/agent-headless-UCBCS4V4.js +0 -87
- package/chunks/bridge-FRNYLYY7.js +0 -119
- package/chunks/channel-settings-store-IV4KDL7N.js +0 -117
- package/chunks/chunk-I6VVOZDK.js +0 -202
- package/chunks/chunk-JS73NNWF.js +0 -30
- package/chunks/chunk-K2OJUPOE.js +0 -78
- package/chunks/chunk-P3QQPMQA.js +0 -19
- package/chunks/chunk-SDVF6A42.js +0 -945
- package/chunks/chunk-VGHZT6JR.js +0 -51
- package/chunks/config-utils-PMX4QZ5Y.js +0 -113
- package/chunks/contextCommand-NZRLVAFC.js +0 -108
- package/chunks/daemon-status-provider-FTTYZEKN.js +0 -118
- package/chunks/daemon-trust-policy-WMGFPA3Z.js +0 -114
- package/chunks/deferred-core-runtime-BUFEFYNA.js +0 -116
- package/chunks/earlyInputCapture-ROPLMXA7.js +0 -109
- package/chunks/errors-U5KNAADB.js +0 -115
- package/chunks/exitPlanMode-VFJ5R5DD.js +0 -85
- package/chunks/handleAutoUpdate-W4BUYFT7.js +0 -111
- package/chunks/i18n-KX7FEKJJ.js +0 -126
- package/chunks/initializer-SNVVLS7Y.js +0 -115
- package/chunks/installationInfo-D4XGQNHJ.js +0 -109
- package/chunks/list-WYECMLXZ.js +0 -118
- package/chunks/loadedSettingsAdapter-5WZBXBCR.js +0 -112
- package/chunks/mcp-CBWAROY3.js +0 -112
- package/chunks/nonInteractiveCli-O232UUV7.js +0 -185
- package/chunks/pidfile-MSPQ255Y.js +0 -113
- package/chunks/process-registry-PMJOA5CO.js +0 -172
- package/chunks/prompt-terminal-ledger-YPGMNDXT.js +0 -107
- package/chunks/resumeHistoryUtils-35D4KAVT.js +0 -120
- package/chunks/ripGrep-46N4FLOS.js +0 -39
- package/chunks/serve-WQ54UIYG.js +0 -120
- package/chunks/skill-settings-SKMN5YXC.js +0 -120
- package/chunks/spawnChannel-NE43EEO3.js +0 -113
- package/chunks/standalone-update-XOCWGXAH.js +0 -120
- package/chunks/terminal-image-renderer-NREKYTAD.js +0 -116
- package/chunks/theme-manager-IJS6OICX.js +0 -104
- package/chunks/total-session-admission-L5RPEFZ2.js +0 -114
- package/chunks/trustedFolders-OILZCHRD.js +0 -124
- package/chunks/updateCheck-AK4COHNK.js +0 -120
- package/chunks/useAutoAcceptIndicator-O2N2Q6KG.js +0 -122
- package/chunks/workspace-providers-status-OCFEWIPQ.js +0 -116
- package/chunks/workspace-registry-WJLZPRTG.js +0 -123
- package/chunks/workspace-service-5ZOBMMIB.js +0 -131
- package/chunks/workspace-skills-status-B6NJZD4B.js +0 -115
- package/chunks/write-file-B2RNX6SU.js +0 -90
- package/web-shell/assets/channel-CU-Zyv0r.js +0 -1
- package/web-shell/assets/classDiagram-DTDB5LWJ-C0J2_LKs.js +0 -1
- package/web-shell/assets/classDiagram-v2-JRS7N3AN-C0J2_LKs.js +0 -1
- package/web-shell/assets/index-C-XlQn24.css +0 -5
- package/web-shell/assets/index-gdvMN4J1.js +0 -1860
- package/web-shell/assets/stateDiagram-v2-4QOOHH4V-2uACDdJN.js +0 -1
- package/web-shell/assets/swimlanesDiagram-VK2B7HYN-C4JJkavU.js +0 -8
|
@@ -0,0 +1,2597 @@
|
|
|
1
|
+
// Force strict mode and setup for ESM
|
|
2
|
+
"use strict";
|
|
3
|
+
import {
|
|
4
|
+
classifyTopLevelConversationSource
|
|
5
|
+
} from "./chunk-36LGBEM6.js";
|
|
6
|
+
import {
|
|
7
|
+
A1_MIN_VERSION,
|
|
8
|
+
a1VersionAtLeast,
|
|
9
|
+
isAoneCanonicalHost,
|
|
10
|
+
parseA1Version,
|
|
11
|
+
parseRemoteUrl
|
|
12
|
+
} from "./chunk-4VQBQLIZ.js";
|
|
13
|
+
import {
|
|
14
|
+
safeLogValue
|
|
15
|
+
} from "./chunk-SEONHUS3.js";
|
|
16
|
+
import {
|
|
17
|
+
normalizeSessionIdForLookup,
|
|
18
|
+
parseCallerSuppliedSessionId
|
|
19
|
+
} from "./chunk-ZEZKIS2K.js";
|
|
20
|
+
import {
|
|
21
|
+
SessionArchivedError,
|
|
22
|
+
SessionArchivingError,
|
|
23
|
+
SessionConflictError,
|
|
24
|
+
SessionNotArchivedError,
|
|
25
|
+
SessionNotFoundError
|
|
26
|
+
} from "./chunk-ERFDKH32.js";
|
|
27
|
+
import {
|
|
28
|
+
gitEnv
|
|
29
|
+
} from "./chunk-NV55WWD6.js";
|
|
30
|
+
import {
|
|
31
|
+
readWorktreeSession
|
|
32
|
+
} from "./chunk-62GQFYID.js";
|
|
33
|
+
import {
|
|
34
|
+
SESSION_PR_URL_MAX_LENGTH,
|
|
35
|
+
SessionIdCaseConflictError,
|
|
36
|
+
SessionOrganizationError,
|
|
37
|
+
SessionOrganizationService,
|
|
38
|
+
SessionService,
|
|
39
|
+
SessionTranscriptChangedError,
|
|
40
|
+
SessionWriterUnavailableError,
|
|
41
|
+
canonicalSessionPrUrl,
|
|
42
|
+
readSessionPrs
|
|
43
|
+
} from "./chunk-PZ66FRIC.js";
|
|
44
|
+
import {
|
|
45
|
+
updateCronTasks
|
|
46
|
+
} from "./chunk-D5LUXLUH.js";
|
|
47
|
+
import {
|
|
48
|
+
findGitRoot
|
|
49
|
+
} from "./chunk-SMPR7SPO.js";
|
|
50
|
+
import {
|
|
51
|
+
addDaemonRequestAttribute
|
|
52
|
+
} from "./chunk-3WK3QDNV.js";
|
|
53
|
+
import {
|
|
54
|
+
Storage
|
|
55
|
+
} from "./chunk-UHQFIS7N.js";
|
|
56
|
+
import {
|
|
57
|
+
writeStderrLine
|
|
58
|
+
} from "./chunk-7FA2II6K.js";
|
|
59
|
+
import {
|
|
60
|
+
init_esbuild_shims
|
|
61
|
+
} from "./chunk-5O2XNYP6.js";
|
|
62
|
+
import {
|
|
63
|
+
__name
|
|
64
|
+
} from "./chunk-J2S4EL5Y.js";
|
|
65
|
+
|
|
66
|
+
// packages/cli/src/serve/server/session-list.ts
|
|
67
|
+
init_esbuild_shims();
|
|
68
|
+
|
|
69
|
+
// packages/cli/src/serve/session-organization-helpers.ts
|
|
70
|
+
init_esbuild_shims();
|
|
71
|
+
function createSessionOrganizationService(workspaceCwd) {
|
|
72
|
+
return new SessionOrganizationService(workspaceCwd, (message) => {
|
|
73
|
+
writeStderrLine(`qwen serve: session-org: ${message}`);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
__name(createSessionOrganizationService, "createSessionOrganizationService");
|
|
77
|
+
|
|
78
|
+
// packages/cli/src/serve/server/persisted-session-list-cache.ts
|
|
79
|
+
init_esbuild_shims();
|
|
80
|
+
import path from "node:path";
|
|
81
|
+
var PersistedSessionListCache = class {
|
|
82
|
+
constructor(ttlMs, maxRetainedSummaries) {
|
|
83
|
+
this.ttlMs = ttlMs;
|
|
84
|
+
this.maxRetainedSummaries = maxRetainedSummaries;
|
|
85
|
+
}
|
|
86
|
+
static {
|
|
87
|
+
__name(this, "PersistedSessionListCache");
|
|
88
|
+
}
|
|
89
|
+
slots = /* @__PURE__ */ new Map();
|
|
90
|
+
retainedSummaries = 0;
|
|
91
|
+
lookup(scope, loader, options = {}) {
|
|
92
|
+
options.signal?.throwIfAborted();
|
|
93
|
+
const key = this.key(scope);
|
|
94
|
+
let slot = this.slots.get(key);
|
|
95
|
+
if (!slot) {
|
|
96
|
+
slot = { generation: 0 };
|
|
97
|
+
this.slots.set(key, slot);
|
|
98
|
+
}
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
if (slot.value) {
|
|
101
|
+
const cacheAgeMs = Math.max(0, now - slot.value.completedAt);
|
|
102
|
+
if (cacheAgeMs < this.ttlMs) {
|
|
103
|
+
const snapshot = slot.value.snapshot;
|
|
104
|
+
return {
|
|
105
|
+
status: "cache_hit",
|
|
106
|
+
promise: Promise.resolve().then(() => {
|
|
107
|
+
options.signal?.throwIfAborted();
|
|
108
|
+
return snapshot;
|
|
109
|
+
}),
|
|
110
|
+
cacheAgeMs
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
this.removeValue(slot);
|
|
114
|
+
}
|
|
115
|
+
if (slot.inFlight !== void 0 && slot.inFlight.generation === slot.generation && !slot.inFlight.controller.signal.aborted) {
|
|
116
|
+
return {
|
|
117
|
+
status: "single_flight",
|
|
118
|
+
promise: this.attachWaiter(key, slot, slot.inFlight, options.signal)
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const generation = slot.generation;
|
|
122
|
+
const controller = new AbortController();
|
|
123
|
+
const load = {
|
|
124
|
+
generation,
|
|
125
|
+
controller,
|
|
126
|
+
promise: void 0,
|
|
127
|
+
waiterCount: 0,
|
|
128
|
+
settled: false
|
|
129
|
+
};
|
|
130
|
+
const managed = Promise.resolve().then(() => {
|
|
131
|
+
controller.signal.throwIfAborted();
|
|
132
|
+
return loader(controller.signal);
|
|
133
|
+
}).then(
|
|
134
|
+
(snapshot) => {
|
|
135
|
+
load.settled = true;
|
|
136
|
+
const current = this.slots.get(key);
|
|
137
|
+
if (current === slot && current.inFlight === load) {
|
|
138
|
+
current.inFlight = void 0;
|
|
139
|
+
if (current.generation === generation && !controller.signal.aborted && snapshot.sessions.length <= this.maxRetainedSummaries) {
|
|
140
|
+
this.installValue(key, current, snapshot);
|
|
141
|
+
} else if (current.value === void 0) {
|
|
142
|
+
this.slots.delete(key);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return snapshot;
|
|
146
|
+
},
|
|
147
|
+
(error) => {
|
|
148
|
+
load.settled = true;
|
|
149
|
+
const current = this.slots.get(key);
|
|
150
|
+
if (current === slot && current.inFlight === load) {
|
|
151
|
+
current.inFlight = void 0;
|
|
152
|
+
if (current.value === void 0) this.slots.delete(key);
|
|
153
|
+
}
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
load.promise = managed;
|
|
158
|
+
slot.inFlight = load;
|
|
159
|
+
return {
|
|
160
|
+
status: "scan",
|
|
161
|
+
promise: this.attachWaiter(key, slot, load, options.signal)
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
invalidate(scope) {
|
|
165
|
+
const key = this.key(scope);
|
|
166
|
+
const slot = this.slots.get(key);
|
|
167
|
+
if (!slot) return;
|
|
168
|
+
slot.generation += 1;
|
|
169
|
+
this.removeValue(slot);
|
|
170
|
+
if (slot.inFlight === void 0) this.slots.delete(key);
|
|
171
|
+
}
|
|
172
|
+
clear() {
|
|
173
|
+
for (const slot of this.slots.values()) {
|
|
174
|
+
if (slot.value) clearTimeout(slot.value.expiryTimer);
|
|
175
|
+
}
|
|
176
|
+
this.slots.clear();
|
|
177
|
+
this.retainedSummaries = 0;
|
|
178
|
+
}
|
|
179
|
+
installValue(key, slot, snapshot) {
|
|
180
|
+
const completedAt = Date.now();
|
|
181
|
+
this.evictFor(snapshot.sessions.length);
|
|
182
|
+
const remainingTtlMs = Math.max(0, this.ttlMs - (Date.now() - completedAt));
|
|
183
|
+
const expiryTimer = setTimeout(() => {
|
|
184
|
+
const current = this.slots.get(key);
|
|
185
|
+
if (current !== slot || current.value?.expiryTimer !== expiryTimer) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
this.removeValue(current);
|
|
189
|
+
if (current.inFlight === void 0) this.slots.delete(key);
|
|
190
|
+
}, remainingTtlMs);
|
|
191
|
+
if (typeof expiryTimer.unref === "function") expiryTimer.unref();
|
|
192
|
+
const value = { snapshot, completedAt, expiryTimer };
|
|
193
|
+
slot.value = value;
|
|
194
|
+
this.retainedSummaries += snapshot.sessions.length;
|
|
195
|
+
}
|
|
196
|
+
evictFor(incomingSummaries) {
|
|
197
|
+
while (this.retainedSummaries + incomingSummaries > this.maxRetainedSummaries) {
|
|
198
|
+
let oldest;
|
|
199
|
+
for (const [key, slot] of this.slots) {
|
|
200
|
+
if (slot.value && (oldest === void 0 || slot.value.completedAt < oldest.completedAt)) {
|
|
201
|
+
oldest = { key, slot, completedAt: slot.value.completedAt };
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (!oldest) return;
|
|
205
|
+
this.removeValue(oldest.slot);
|
|
206
|
+
if (oldest.slot.inFlight === void 0) this.slots.delete(oldest.key);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
removeValue(slot) {
|
|
210
|
+
const value = slot.value;
|
|
211
|
+
if (!value) return;
|
|
212
|
+
clearTimeout(value.expiryTimer);
|
|
213
|
+
this.retainedSummaries -= value.snapshot.sessions.length;
|
|
214
|
+
slot.value = void 0;
|
|
215
|
+
}
|
|
216
|
+
attachWaiter(key, slot, load, signal) {
|
|
217
|
+
load.waiterCount += 1;
|
|
218
|
+
return new Promise((resolve, reject) => {
|
|
219
|
+
let settled = false;
|
|
220
|
+
const settle = /* @__PURE__ */ __name((finish) => {
|
|
221
|
+
if (settled) return;
|
|
222
|
+
settled = true;
|
|
223
|
+
signal?.removeEventListener("abort", onAbort);
|
|
224
|
+
load.waiterCount -= 1;
|
|
225
|
+
if (load.waiterCount === 0 && !load.settled) {
|
|
226
|
+
load.controller.abort(
|
|
227
|
+
new DOMException("No active session list waiters", "AbortError")
|
|
228
|
+
);
|
|
229
|
+
const current = this.slots.get(key);
|
|
230
|
+
if (current === slot && current.inFlight === load) {
|
|
231
|
+
current.inFlight = void 0;
|
|
232
|
+
if (current.value === void 0) this.slots.delete(key);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
finish();
|
|
236
|
+
}, "settle");
|
|
237
|
+
const onAbort = /* @__PURE__ */ __name(() => {
|
|
238
|
+
if (load.settled) return;
|
|
239
|
+
settle(() => reject(signal?.reason));
|
|
240
|
+
}, "onAbort");
|
|
241
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
242
|
+
if (signal?.aborted) onAbort();
|
|
243
|
+
void load.promise.then(
|
|
244
|
+
(snapshot) => settle(() => resolve(snapshot)),
|
|
245
|
+
(error) => settle(() => reject(error))
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
key(scope) {
|
|
250
|
+
return JSON.stringify([
|
|
251
|
+
path.resolve(scope.runtimeBaseDir),
|
|
252
|
+
scope.workspaceCwd,
|
|
253
|
+
scope.archiveState
|
|
254
|
+
]);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// packages/cli/src/serve/server/activity-timestamp.ts
|
|
259
|
+
init_esbuild_shims();
|
|
260
|
+
function laterActivityTimestamp(live, persisted) {
|
|
261
|
+
const liveTime = live === void 0 ? Number.NaN : Date.parse(live);
|
|
262
|
+
const persistedTime = persisted === void 0 ? Number.NaN : Date.parse(persisted);
|
|
263
|
+
const liveValid = Number.isFinite(liveTime);
|
|
264
|
+
const persistedValid = Number.isFinite(persistedTime);
|
|
265
|
+
if (liveValid && persistedValid) {
|
|
266
|
+
return liveTime >= persistedTime ? live : persisted;
|
|
267
|
+
}
|
|
268
|
+
if (liveValid) return live;
|
|
269
|
+
if (persistedValid) return persisted;
|
|
270
|
+
return live ?? persisted;
|
|
271
|
+
}
|
|
272
|
+
__name(laterActivityTimestamp, "laterActivityTimestamp");
|
|
273
|
+
|
|
274
|
+
// packages/cli/src/serve/server/session-list.ts
|
|
275
|
+
var DEFAULT_SESSION_PAGE_SIZE = 20;
|
|
276
|
+
var MAX_SESSION_PAGE_SIZE = 100;
|
|
277
|
+
var MAX_ORGANIZED_SESSIONS = 5e4;
|
|
278
|
+
var MAX_EMITTED_CURSOR_SESSION_IDS = 64;
|
|
279
|
+
var PERSISTED_SESSION_LIST_CACHE_TTL_MS = 2e3;
|
|
280
|
+
var persistedSessionListCache = new PersistedSessionListCache(
|
|
281
|
+
PERSISTED_SESSION_LIST_CACHE_TTL_MS,
|
|
282
|
+
MAX_ORGANIZED_SESSIONS
|
|
283
|
+
);
|
|
284
|
+
function invalidateWorkspaceSessionListCache(options) {
|
|
285
|
+
for (const archiveState of options.archiveStates) {
|
|
286
|
+
persistedSessionListCache.invalidate({
|
|
287
|
+
runtimeBaseDir: options.runtimeBaseDir,
|
|
288
|
+
workspaceCwd: options.workspaceCwd,
|
|
289
|
+
archiveState
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
__name(invalidateWorkspaceSessionListCache, "invalidateWorkspaceSessionListCache");
|
|
294
|
+
var InvalidCursorError = class extends Error {
|
|
295
|
+
static {
|
|
296
|
+
__name(this, "InvalidCursorError");
|
|
297
|
+
}
|
|
298
|
+
constructor(cursor, kind = "numeric") {
|
|
299
|
+
super(`Invalid cursor: "${cursor}" is not a valid ${kind} cursor`);
|
|
300
|
+
this.name = "InvalidCursorError";
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
function parseSessionCursor(cursor) {
|
|
304
|
+
if (cursor === "") return void 0;
|
|
305
|
+
const trimmed = cursor.trim();
|
|
306
|
+
const parsed = Number(trimmed);
|
|
307
|
+
if (trimmed === "" || !Number.isFinite(parsed) || parsed < 0 || parsed > Number.MAX_SAFE_INTEGER) {
|
|
308
|
+
throw new InvalidCursorError(cursor);
|
|
309
|
+
}
|
|
310
|
+
return parsed;
|
|
311
|
+
}
|
|
312
|
+
__name(parseSessionCursor, "parseSessionCursor");
|
|
313
|
+
function parseEmittedSessionIds(value) {
|
|
314
|
+
if (value === void 0) return [];
|
|
315
|
+
if (!Array.isArray(value) || value.some((id) => typeof id !== "string" || id.length === 0)) {
|
|
316
|
+
return void 0;
|
|
317
|
+
}
|
|
318
|
+
return value;
|
|
319
|
+
}
|
|
320
|
+
__name(parseEmittedSessionIds, "parseEmittedSessionIds");
|
|
321
|
+
function parseOrganizedCursor(cursor, expected) {
|
|
322
|
+
if (cursor === "") return void 0;
|
|
323
|
+
try {
|
|
324
|
+
const decoded = Buffer.from(cursor, "base64url").toString("utf8");
|
|
325
|
+
const parsed = JSON.parse(decoded);
|
|
326
|
+
const last = parsed.last;
|
|
327
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed) || typeof last !== "object" || last === null || Array.isArray(last) || typeof last.isPinned !== "boolean" || typeof last.activityTime !== "number" || !Number.isFinite(last.activityTime) || typeof last.sessionId !== "string" || last.sessionId.length === 0 || parsed.group !== expected.group || parsed.archiveState !== expected.archiveState || parsed.sourceType !== expected.sourceType || parsed.sourceId !== expected.sourceId || parsed.conversationKind !== expected.conversationKind) {
|
|
328
|
+
throw new Error("invalid organized cursor");
|
|
329
|
+
}
|
|
330
|
+
const emitted = parseEmittedSessionIds(parsed.emitted);
|
|
331
|
+
if (emitted === void 0) {
|
|
332
|
+
throw new Error("invalid organized cursor");
|
|
333
|
+
}
|
|
334
|
+
return { last, emitted };
|
|
335
|
+
} catch {
|
|
336
|
+
throw new InvalidCursorError(cursor, "organized");
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
__name(parseOrganizedCursor, "parseOrganizedCursor");
|
|
340
|
+
function encodeOrganizedCursor(last, group, archiveState, sourceType, sourceId, conversationKind, emitted = []) {
|
|
341
|
+
return Buffer.from(
|
|
342
|
+
JSON.stringify({
|
|
343
|
+
group,
|
|
344
|
+
archiveState,
|
|
345
|
+
sourceType,
|
|
346
|
+
sourceId,
|
|
347
|
+
conversationKind,
|
|
348
|
+
last,
|
|
349
|
+
...emitted.length > 0 ? { emitted } : {}
|
|
350
|
+
}),
|
|
351
|
+
"utf8"
|
|
352
|
+
).toString("base64url");
|
|
353
|
+
}
|
|
354
|
+
__name(encodeOrganizedCursor, "encodeOrganizedCursor");
|
|
355
|
+
function parseLiveSessionCursor(cursor) {
|
|
356
|
+
if (cursor === "") return void 0;
|
|
357
|
+
try {
|
|
358
|
+
const parsed = JSON.parse(
|
|
359
|
+
Buffer.from(cursor, "base64url").toString("utf8")
|
|
360
|
+
);
|
|
361
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed) || typeof parsed.activityTime !== "number" || !Number.isFinite(parsed.activityTime) || typeof parsed.sessionId !== "string" || parsed.sessionId.length === 0) {
|
|
362
|
+
throw new Error("invalid live cursor");
|
|
363
|
+
}
|
|
364
|
+
return parsed;
|
|
365
|
+
} catch {
|
|
366
|
+
throw new InvalidCursorError(cursor, "live");
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
__name(parseLiveSessionCursor, "parseLiveSessionCursor");
|
|
370
|
+
function encodeLiveSessionCursor(last) {
|
|
371
|
+
return Buffer.from(JSON.stringify(last), "utf8").toString("base64url");
|
|
372
|
+
}
|
|
373
|
+
__name(encodeLiveSessionCursor, "encodeLiveSessionCursor");
|
|
374
|
+
function matchesSessionMetadataSource(session, filter) {
|
|
375
|
+
if (filter.conversationKind === "standalone-top-level" && classifyTopLevelConversationSource(session)?.kind !== "standalone") {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
const sourceTypeMatches = filter.sourceType === void 0 || session.sourceType === filter.sourceType || // Legacy sessions without source metadata belong to the default catalog.
|
|
379
|
+
filter.sourceType === "default" && session.sourceType === void 0;
|
|
380
|
+
return sourceTypeMatches && // sourceId remains exact; only the default source type has legacy fallback.
|
|
381
|
+
(filter.sourceId === void 0 || session.sourceId === filter.sourceId);
|
|
382
|
+
}
|
|
383
|
+
__name(matchesSessionMetadataSource, "matchesSessionMetadataSource");
|
|
384
|
+
function parseMetadataSessionCursor(cursor, expected) {
|
|
385
|
+
if (cursor === "") return void 0;
|
|
386
|
+
try {
|
|
387
|
+
const parsed = JSON.parse(
|
|
388
|
+
Buffer.from(cursor, "base64url").toString("utf8")
|
|
389
|
+
);
|
|
390
|
+
const last = parsed.last;
|
|
391
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed) || typeof last !== "object" || last === null || Array.isArray(last) || typeof last.activityTime !== "number" || !Number.isFinite(last.activityTime) || typeof last.sessionId !== "string" || last.sessionId.length === 0 || parsed.parentSessionId !== expected.parentSessionId || parsed.sourceType !== expected.sourceType || parsed.sourceId !== expected.sourceId || parsed.conversationKind !== expected.conversationKind || parsed.archiveState !== expected.archiveState) {
|
|
392
|
+
throw new Error("invalid metadata cursor");
|
|
393
|
+
}
|
|
394
|
+
const emitted = parseEmittedSessionIds(
|
|
395
|
+
parsed.emitted
|
|
396
|
+
);
|
|
397
|
+
if (emitted === void 0) {
|
|
398
|
+
throw new Error("invalid metadata cursor");
|
|
399
|
+
}
|
|
400
|
+
return {
|
|
401
|
+
last: { activityTime: last.activityTime, sessionId: last.sessionId },
|
|
402
|
+
emitted
|
|
403
|
+
};
|
|
404
|
+
} catch {
|
|
405
|
+
throw new InvalidCursorError(
|
|
406
|
+
cursor,
|
|
407
|
+
expected.sourceType === void 0 ? "parent" : "metadata"
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
__name(parseMetadataSessionCursor, "parseMetadataSessionCursor");
|
|
412
|
+
function encodeMetadataSessionCursor(last, filter, archiveState, emitted = []) {
|
|
413
|
+
return Buffer.from(
|
|
414
|
+
JSON.stringify({
|
|
415
|
+
...filter,
|
|
416
|
+
archiveState,
|
|
417
|
+
last,
|
|
418
|
+
...emitted.length > 0 ? { emitted } : {}
|
|
419
|
+
}),
|
|
420
|
+
"utf8"
|
|
421
|
+
).toString("base64url");
|
|
422
|
+
}
|
|
423
|
+
__name(encodeMetadataSessionCursor, "encodeMetadataSessionCursor");
|
|
424
|
+
async function enrichWorktreeSidecars(bySessionId, sessionService, archiveState = "active", signal) {
|
|
425
|
+
for (const [sessionId, summary] of bySessionId) {
|
|
426
|
+
signal?.throwIfAborted();
|
|
427
|
+
if (summary.worktree) continue;
|
|
428
|
+
let sidecar;
|
|
429
|
+
try {
|
|
430
|
+
const sidecarPath = sessionService.getWorktreeSessionPathForArchiveState(
|
|
431
|
+
sessionId,
|
|
432
|
+
archiveState
|
|
433
|
+
);
|
|
434
|
+
sidecar = signal ? await readWorktreeSession(sidecarPath, { signal }) : await readWorktreeSession(sidecarPath);
|
|
435
|
+
} catch {
|
|
436
|
+
signal?.throwIfAborted();
|
|
437
|
+
sidecar = null;
|
|
438
|
+
}
|
|
439
|
+
if (sidecar) {
|
|
440
|
+
bySessionId.set(sessionId, {
|
|
441
|
+
...summary,
|
|
442
|
+
worktree: {
|
|
443
|
+
slug: sidecar.slug,
|
|
444
|
+
path: sidecar.worktreePath,
|
|
445
|
+
branch: sidecar.worktreeBranch
|
|
446
|
+
}
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
__name(enrichWorktreeSidecars, "enrichWorktreeSidecars");
|
|
452
|
+
async function enrichPrSidecars(bySessionId, sessionService, archiveState, signal) {
|
|
453
|
+
for (const [sessionId, summary] of bySessionId) {
|
|
454
|
+
signal?.throwIfAborted();
|
|
455
|
+
let sidecar;
|
|
456
|
+
try {
|
|
457
|
+
const sidecarPath = sessionService.getPrSessionPathForArchiveState(
|
|
458
|
+
sessionId,
|
|
459
|
+
archiveState
|
|
460
|
+
);
|
|
461
|
+
sidecar = signal ? await readSessionPrs(sidecarPath, { signal }) : await readSessionPrs(sidecarPath);
|
|
462
|
+
} catch {
|
|
463
|
+
signal?.throwIfAborted();
|
|
464
|
+
sidecar = null;
|
|
465
|
+
}
|
|
466
|
+
if (sidecar) {
|
|
467
|
+
bySessionId.set(sessionId, {
|
|
468
|
+
...summary,
|
|
469
|
+
prs: sidecarToPrInfos(sidecar)
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
__name(enrichPrSidecars, "enrichPrSidecars");
|
|
475
|
+
function toSummary(item) {
|
|
476
|
+
return {
|
|
477
|
+
sessionId: item.sessionId,
|
|
478
|
+
workspaceCwd: item.cwd,
|
|
479
|
+
createdAt: item.startTime,
|
|
480
|
+
updatedAt: new Date(item.mtime).toISOString(),
|
|
481
|
+
displayName: item.customTitle || item.prompt,
|
|
482
|
+
...item.parentSessionId ? { parentSessionId: item.parentSessionId } : {},
|
|
483
|
+
...item.sourceType ? { sourceType: item.sourceType } : {},
|
|
484
|
+
...item.sourceId !== void 0 ? { sourceId: item.sourceId } : {},
|
|
485
|
+
clientCount: 0,
|
|
486
|
+
hasActivePrompt: false,
|
|
487
|
+
isArchived: item.isArchived === true
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
__name(toSummary, "toSummary");
|
|
491
|
+
function mergeLiveSessionSummary(existing, live) {
|
|
492
|
+
const merged = {
|
|
493
|
+
...existing,
|
|
494
|
+
...live,
|
|
495
|
+
createdAt: existing.createdAt,
|
|
496
|
+
displayName: live.displayName ?? existing.displayName,
|
|
497
|
+
// Immutable lineage; the persisted transcript is authoritative, and a live
|
|
498
|
+
// entry only carries it when spawned this run.
|
|
499
|
+
parentSessionId: existing.parentSessionId ?? live.parentSessionId,
|
|
500
|
+
sourceType: existing.sourceType ?? live.sourceType,
|
|
501
|
+
sourceId: existing.sourceType !== void 0 ? existing.sourceId : live.sourceId,
|
|
502
|
+
updatedAt: laterActivityTimestamp(live.updatedAt, existing.updatedAt),
|
|
503
|
+
clientCount: live.clientCount,
|
|
504
|
+
hasActivePrompt: live.hasActivePrompt,
|
|
505
|
+
isArchived: false
|
|
506
|
+
};
|
|
507
|
+
if (existing.prs || live.prs) {
|
|
508
|
+
merged.prs = mergeSummaryPrs(existing.prs, live.prs);
|
|
509
|
+
}
|
|
510
|
+
return merged;
|
|
511
|
+
}
|
|
512
|
+
__name(mergeLiveSessionSummary, "mergeLiveSessionSummary");
|
|
513
|
+
function sidecarToPrInfos(sidecar) {
|
|
514
|
+
return sidecar.map(({ number, url, state }) => ({
|
|
515
|
+
number,
|
|
516
|
+
url,
|
|
517
|
+
...state ? { state } : {}
|
|
518
|
+
}));
|
|
519
|
+
}
|
|
520
|
+
__name(sidecarToPrInfos, "sidecarToPrInfos");
|
|
521
|
+
function mergeSummaryPrs(persistedPrs, livePrs) {
|
|
522
|
+
const live = livePrs ?? [];
|
|
523
|
+
const persistedByNumber = new Map(
|
|
524
|
+
(persistedPrs ?? []).map((p) => [p.number, p])
|
|
525
|
+
);
|
|
526
|
+
return [
|
|
527
|
+
...(persistedPrs ?? []).filter(
|
|
528
|
+
(p) => !live.some((l) => l.number === p.number)
|
|
529
|
+
),
|
|
530
|
+
...live.map((l) => {
|
|
531
|
+
const persisted = persistedByNumber.get(l.number);
|
|
532
|
+
return persisted?.state !== void 0 && persisted.state !== l.state ? { ...l, state: persisted.state } : l;
|
|
533
|
+
})
|
|
534
|
+
];
|
|
535
|
+
}
|
|
536
|
+
__name(mergeSummaryPrs, "mergeSummaryPrs");
|
|
537
|
+
async function liveOnlySummary(live, sessionService, signal) {
|
|
538
|
+
const summary = {
|
|
539
|
+
...live,
|
|
540
|
+
createdAt: live.createdAt,
|
|
541
|
+
clientCount: live.clientCount,
|
|
542
|
+
hasActivePrompt: live.hasActivePrompt,
|
|
543
|
+
isArchived: false
|
|
544
|
+
};
|
|
545
|
+
let sidecar;
|
|
546
|
+
try {
|
|
547
|
+
const sidecarPath = sessionService.getPrSessionPathForArchiveState(
|
|
548
|
+
live.sessionId,
|
|
549
|
+
"active"
|
|
550
|
+
);
|
|
551
|
+
sidecar = signal ? await readSessionPrs(sidecarPath, { signal }) : await readSessionPrs(sidecarPath);
|
|
552
|
+
} catch {
|
|
553
|
+
signal?.throwIfAborted();
|
|
554
|
+
sidecar = null;
|
|
555
|
+
}
|
|
556
|
+
if (sidecar) {
|
|
557
|
+
summary.prs = mergeSummaryPrs(sidecarToPrInfos(sidecar), live.prs);
|
|
558
|
+
}
|
|
559
|
+
return summary;
|
|
560
|
+
}
|
|
561
|
+
__name(liveOnlySummary, "liveOnlySummary");
|
|
562
|
+
function clonePersistedSummary(session) {
|
|
563
|
+
return {
|
|
564
|
+
...session,
|
|
565
|
+
...session.worktree ? { worktree: { ...session.worktree } } : {}
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
__name(clonePersistedSummary, "clonePersistedSummary");
|
|
569
|
+
async function loadAllPersistedSummaries(sessionService, archiveState, signal) {
|
|
570
|
+
const scanStartedAt = performance.now();
|
|
571
|
+
signal.throwIfAborted();
|
|
572
|
+
const sessions = [];
|
|
573
|
+
let truncated = false;
|
|
574
|
+
let scanPages = 0;
|
|
575
|
+
let cursor;
|
|
576
|
+
do {
|
|
577
|
+
scanPages += 1;
|
|
578
|
+
const page = await sessionService.listSessions({
|
|
579
|
+
cursor,
|
|
580
|
+
size: 1e4,
|
|
581
|
+
archiveState,
|
|
582
|
+
signal
|
|
583
|
+
});
|
|
584
|
+
signal.throwIfAborted();
|
|
585
|
+
const remaining = MAX_ORGANIZED_SESSIONS - sessions.length;
|
|
586
|
+
sessions.push(...page.items.slice(0, remaining).map(toSummary));
|
|
587
|
+
cursor = page.nextCursor;
|
|
588
|
+
if (page.items.length === 0) {
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
591
|
+
if (page.items.length > remaining || sessions.length >= MAX_ORGANIZED_SESSIONS && cursor !== void 0) {
|
|
592
|
+
writeStderrLine(
|
|
593
|
+
`qwen serve: organized session list truncated at ${MAX_ORGANIZED_SESSIONS} sessions`
|
|
594
|
+
);
|
|
595
|
+
truncated = true;
|
|
596
|
+
break;
|
|
597
|
+
}
|
|
598
|
+
} while (cursor !== void 0);
|
|
599
|
+
const bySessionId = new Map(
|
|
600
|
+
sessions.map((session) => [session.sessionId, session])
|
|
601
|
+
);
|
|
602
|
+
await enrichWorktreeSidecars(
|
|
603
|
+
bySessionId,
|
|
604
|
+
sessionService,
|
|
605
|
+
archiveState,
|
|
606
|
+
signal
|
|
607
|
+
);
|
|
608
|
+
await enrichPrSidecars(bySessionId, sessionService, archiveState, signal);
|
|
609
|
+
signal.throwIfAborted();
|
|
610
|
+
return {
|
|
611
|
+
sessions: [...bySessionId.values()],
|
|
612
|
+
truncated,
|
|
613
|
+
scanPages,
|
|
614
|
+
scanDurationMs: Math.max(0, performance.now() - scanStartedAt)
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
__name(loadAllPersistedSummaries, "loadAllPersistedSummaries");
|
|
618
|
+
async function listAllPersistedSummaries(sessionService, workspaceCwd, archiveState, runtimeBaseDir, queryKind, signal) {
|
|
619
|
+
const lookup = persistedSessionListCache.lookup(
|
|
620
|
+
{ runtimeBaseDir, workspaceCwd, archiveState },
|
|
621
|
+
(loadSignal) => loadAllPersistedSummaries(sessionService, archiveState, loadSignal),
|
|
622
|
+
{ signal }
|
|
623
|
+
);
|
|
624
|
+
addDaemonRequestAttribute(
|
|
625
|
+
"qwen-code.daemon.session_list.cache_status",
|
|
626
|
+
lookup.status
|
|
627
|
+
);
|
|
628
|
+
addDaemonRequestAttribute(
|
|
629
|
+
"qwen-code.daemon.session_list.archive_state",
|
|
630
|
+
archiveState
|
|
631
|
+
);
|
|
632
|
+
addDaemonRequestAttribute(
|
|
633
|
+
"qwen-code.daemon.session_list.query_kind",
|
|
634
|
+
queryKind
|
|
635
|
+
);
|
|
636
|
+
if (lookup.cacheAgeMs !== void 0) {
|
|
637
|
+
addDaemonRequestAttribute(
|
|
638
|
+
"qwen-code.daemon.session_list.cache_age_ms",
|
|
639
|
+
lookup.cacheAgeMs
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
const snapshot = await lookup.promise;
|
|
643
|
+
signal?.throwIfAborted();
|
|
644
|
+
addDaemonRequestAttribute(
|
|
645
|
+
"qwen-code.daemon.session_list.persisted_sessions",
|
|
646
|
+
snapshot.sessions.length
|
|
647
|
+
);
|
|
648
|
+
addDaemonRequestAttribute(
|
|
649
|
+
"qwen-code.daemon.session_list.scan_pages",
|
|
650
|
+
snapshot.scanPages
|
|
651
|
+
);
|
|
652
|
+
addDaemonRequestAttribute(
|
|
653
|
+
"qwen-code.daemon.session_list.truncated",
|
|
654
|
+
snapshot.truncated
|
|
655
|
+
);
|
|
656
|
+
if (lookup.status !== "cache_hit") {
|
|
657
|
+
addDaemonRequestAttribute(
|
|
658
|
+
"qwen-code.daemon.session_list.scan_duration_ms",
|
|
659
|
+
snapshot.scanDurationMs
|
|
660
|
+
);
|
|
661
|
+
}
|
|
662
|
+
return snapshot;
|
|
663
|
+
}
|
|
664
|
+
__name(listAllPersistedSummaries, "listAllPersistedSummaries");
|
|
665
|
+
function getSummaryActivityTime(session) {
|
|
666
|
+
const time = Date.parse(session.updatedAt ?? session.createdAt);
|
|
667
|
+
return Number.isFinite(time) ? time : 0;
|
|
668
|
+
}
|
|
669
|
+
__name(getSummaryActivityTime, "getSummaryActivityTime");
|
|
670
|
+
function getLiveSessionCursorKey(session) {
|
|
671
|
+
return {
|
|
672
|
+
activityTime: getSummaryActivityTime(session),
|
|
673
|
+
sessionId: session.sessionId
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
__name(getLiveSessionCursorKey, "getLiveSessionCursorKey");
|
|
677
|
+
function compareLiveSessionCursorKeys(a, b) {
|
|
678
|
+
const byTime = b.activityTime - a.activityTime;
|
|
679
|
+
if (byTime !== 0) return byTime;
|
|
680
|
+
return a.sessionId.localeCompare(b.sessionId);
|
|
681
|
+
}
|
|
682
|
+
__name(compareLiveSessionCursorKeys, "compareLiveSessionCursorKeys");
|
|
683
|
+
function compareOrganizedSessions(activityTimeById, a, b) {
|
|
684
|
+
return compareOrganizedCursorKeys(
|
|
685
|
+
getOrganizedCursorKey(activityTimeById, a),
|
|
686
|
+
getOrganizedCursorKey(activityTimeById, b)
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
__name(compareOrganizedSessions, "compareOrganizedSessions");
|
|
690
|
+
function getOrganizedCursorKey(activityTimeById, session) {
|
|
691
|
+
return {
|
|
692
|
+
isPinned: session.isPinned === true,
|
|
693
|
+
activityTime: activityTimeById.get(session.sessionId) ?? 0,
|
|
694
|
+
sessionId: session.sessionId
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
__name(getOrganizedCursorKey, "getOrganizedCursorKey");
|
|
698
|
+
function compareOrganizedCursorKeys(a, b) {
|
|
699
|
+
const byPinned = Number(b.isPinned) - Number(a.isPinned);
|
|
700
|
+
if (byPinned !== 0) return byPinned;
|
|
701
|
+
const byTime = b.activityTime - a.activityTime;
|
|
702
|
+
if (byTime !== 0) return byTime;
|
|
703
|
+
return a.sessionId.localeCompare(b.sessionId);
|
|
704
|
+
}
|
|
705
|
+
__name(compareOrganizedCursorKeys, "compareOrganizedCursorKeys");
|
|
706
|
+
function nextEmittedSessionIds(options) {
|
|
707
|
+
const candidates = new Set(options.carried);
|
|
708
|
+
for (const row of options.page) {
|
|
709
|
+
if (options.liveSessionIds.has(row.sessionId)) {
|
|
710
|
+
candidates.add(row.sessionId);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
const kept = [];
|
|
714
|
+
for (const sessionId of candidates) {
|
|
715
|
+
const row = options.listedById.get(sessionId);
|
|
716
|
+
if (!row) {
|
|
717
|
+
kept.push({ sessionId, persistedTime: Number.NEGATIVE_INFINITY });
|
|
718
|
+
continue;
|
|
719
|
+
}
|
|
720
|
+
const persistedTime = options.persistedTimeById.get(sessionId);
|
|
721
|
+
if (persistedTime === void 0) {
|
|
722
|
+
kept.push({ sessionId, persistedTime: Number.NEGATIVE_INFINITY });
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
if (options.reenters(row, persistedTime)) {
|
|
726
|
+
kept.push({ sessionId, persistedTime });
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
if (kept.length > MAX_EMITTED_CURSOR_SESSION_IDS) {
|
|
730
|
+
kept.sort((a, b) => a.persistedTime - b.persistedTime);
|
|
731
|
+
kept.length = MAX_EMITTED_CURSOR_SESSION_IDS;
|
|
732
|
+
}
|
|
733
|
+
return kept.map((entry) => entry.sessionId);
|
|
734
|
+
}
|
|
735
|
+
__name(nextEmittedSessionIds, "nextEmittedSessionIds");
|
|
736
|
+
function applyOrganization(session, organization) {
|
|
737
|
+
return {
|
|
738
|
+
...session,
|
|
739
|
+
groupId: organization?.groupId ?? null,
|
|
740
|
+
color: organization?.color ?? null,
|
|
741
|
+
isPinned: organization?.isPinned === true,
|
|
742
|
+
...organization?.pinnedAt !== void 0 ? { pinnedAt: organization.pinnedAt } : {}
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
__name(applyOrganization, "applyOrganization");
|
|
746
|
+
async function listOrganizedWorkspaceSessionsForResponse(bridge, workspaceCwd, options, pageSize, readOptions) {
|
|
747
|
+
const archiveState = options.archiveState ?? "active";
|
|
748
|
+
const sessionService = new SessionService(workspaceCwd);
|
|
749
|
+
const organizationService = createSessionOrganizationService(workspaceCwd);
|
|
750
|
+
readOptions.signal?.throwIfAborted();
|
|
751
|
+
const snapshot = await organizationService.readSnapshot();
|
|
752
|
+
readOptions.signal?.throwIfAborted();
|
|
753
|
+
const knownGroupIds = new Set(snapshot.groups.map((group2) => group2.id));
|
|
754
|
+
const group = options.group ?? "all";
|
|
755
|
+
if (group !== "all" && group !== "pinned" && group !== "ungrouped" && !knownGroupIds.has(group)) {
|
|
756
|
+
throw new SessionOrganizationError(
|
|
757
|
+
`Group not found: ${group}`,
|
|
758
|
+
"group_not_found",
|
|
759
|
+
"group"
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
const cursor = options.cursor !== void 0 ? parseOrganizedCursor(options.cursor, {
|
|
763
|
+
group,
|
|
764
|
+
archiveState,
|
|
765
|
+
sourceType: options.sourceType,
|
|
766
|
+
sourceId: options.sourceId,
|
|
767
|
+
conversationKind: options.conversationKind
|
|
768
|
+
}) : void 0;
|
|
769
|
+
const cursorKey = cursor?.last;
|
|
770
|
+
const emittedBeforePage = new Set(cursor?.emitted ?? []);
|
|
771
|
+
const isFirstPage = cursorKey === void 0;
|
|
772
|
+
let liveMergeFailed = false;
|
|
773
|
+
const bySessionId = /* @__PURE__ */ new Map();
|
|
774
|
+
const persisted = await listAllPersistedSummaries(
|
|
775
|
+
sessionService,
|
|
776
|
+
workspaceCwd,
|
|
777
|
+
archiveState,
|
|
778
|
+
readOptions.runtimeBaseDir,
|
|
779
|
+
"organized",
|
|
780
|
+
readOptions.signal
|
|
781
|
+
);
|
|
782
|
+
readOptions.signal?.throwIfAborted();
|
|
783
|
+
for (const session of persisted.sessions) {
|
|
784
|
+
bySessionId.set(
|
|
785
|
+
session.sessionId,
|
|
786
|
+
applyOrganization(
|
|
787
|
+
clonePersistedSummary(session),
|
|
788
|
+
snapshot.sessions.get(session.sessionId)
|
|
789
|
+
)
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
const persistedTimeById = new Map(
|
|
793
|
+
persisted.sessions.map((session) => [
|
|
794
|
+
session.sessionId,
|
|
795
|
+
getSummaryActivityTime(session)
|
|
796
|
+
])
|
|
797
|
+
);
|
|
798
|
+
const liveSessionIds = /* @__PURE__ */ new Set();
|
|
799
|
+
if (readOptions.mergeLive !== false && archiveState !== "archived") {
|
|
800
|
+
try {
|
|
801
|
+
const liveSessions = bridge.listWorkspaceSessions(workspaceCwd);
|
|
802
|
+
for (const live of liveSessions) {
|
|
803
|
+
liveSessionIds.add(live.sessionId);
|
|
804
|
+
const existing = bySessionId.get(live.sessionId);
|
|
805
|
+
const organization = snapshot.sessions.get(live.sessionId);
|
|
806
|
+
if (existing) {
|
|
807
|
+
bySessionId.set(
|
|
808
|
+
live.sessionId,
|
|
809
|
+
applyOrganization(
|
|
810
|
+
mergeLiveSessionSummary(existing, live),
|
|
811
|
+
organization
|
|
812
|
+
)
|
|
813
|
+
);
|
|
814
|
+
} else if (
|
|
815
|
+
// A live-only row has no persisted key to page by, so it stays a
|
|
816
|
+
// first-page-only insertion as before.
|
|
817
|
+
isFirstPage && // `listAllPersistedSummaries` already scanned every persisted
|
|
818
|
+
// session when the scan wasn't truncated, so a `sessionId` missing
|
|
819
|
+
// from `bySessionId` is definitively new — no disk re-check
|
|
820
|
+
// needed. Re-checking here raced a session that persists its
|
|
821
|
+
// first write (e.g. a `displayName` update) between the scan
|
|
822
|
+
// above and this point: `existing` stayed undefined but
|
|
823
|
+
// `sessionExists` flipped to true, silently dropping the live
|
|
824
|
+
// session from the response instead of merging it.
|
|
825
|
+
(!persisted.truncated || !await (readOptions.signal ? sessionService.sessionExists(live.sessionId, {
|
|
826
|
+
signal: readOptions.signal
|
|
827
|
+
}) : sessionService.sessionExists(live.sessionId)))
|
|
828
|
+
) {
|
|
829
|
+
bySessionId.set(
|
|
830
|
+
live.sessionId,
|
|
831
|
+
applyOrganization(
|
|
832
|
+
await liveOnlySummary(live, sessionService, readOptions.signal),
|
|
833
|
+
organization
|
|
834
|
+
)
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
} catch (error) {
|
|
839
|
+
readOptions.signal?.throwIfAborted();
|
|
840
|
+
liveMergeFailed = true;
|
|
841
|
+
writeStderrLine(
|
|
842
|
+
`qwen serve: organized session list live merge failed; using persisted sessions only: ${error instanceof Error ? error.message : String(error)}`
|
|
843
|
+
);
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
const filtered = [...bySessionId.values()].filter((session) => {
|
|
847
|
+
if (!matchesSessionMetadataSource(session, options)) return false;
|
|
848
|
+
if (group === "all") return true;
|
|
849
|
+
if (group === "pinned") return session.isPinned === true;
|
|
850
|
+
if (group === "ungrouped")
|
|
851
|
+
return session.groupId == null && session.color == null;
|
|
852
|
+
return session.color == null && session.groupId === group;
|
|
853
|
+
});
|
|
854
|
+
readOptions.signal?.throwIfAborted();
|
|
855
|
+
const activityTimeById = new Map(
|
|
856
|
+
filtered.map((session) => [
|
|
857
|
+
session.sessionId,
|
|
858
|
+
getSummaryActivityTime(session)
|
|
859
|
+
])
|
|
860
|
+
);
|
|
861
|
+
filtered.sort((a, b) => compareOrganizedSessions(activityTimeById, a, b));
|
|
862
|
+
readOptions.signal?.throwIfAborted();
|
|
863
|
+
const afterCursor = cursorKey === void 0 ? filtered : filtered.filter(
|
|
864
|
+
(session) => (
|
|
865
|
+
// A row already emitted at a live-derived key stays excluded even
|
|
866
|
+
// if its key regressed behind the cursor (live retirement, or a
|
|
867
|
+
// live-only row that persisted mid-pass).
|
|
868
|
+
!emittedBeforePage.has(session.sessionId) && compareOrganizedCursorKeys(
|
|
869
|
+
cursorKey,
|
|
870
|
+
getOrganizedCursorKey(activityTimeById, session)
|
|
871
|
+
) < 0
|
|
872
|
+
)
|
|
873
|
+
);
|
|
874
|
+
const page = afterCursor.slice(0, pageSize);
|
|
875
|
+
let nextCursor;
|
|
876
|
+
if (page.length < afterCursor.length) {
|
|
877
|
+
const boundary = getOrganizedCursorKey(
|
|
878
|
+
activityTimeById,
|
|
879
|
+
page[page.length - 1]
|
|
880
|
+
);
|
|
881
|
+
const emitted = nextEmittedSessionIds({
|
|
882
|
+
carried: emittedBeforePage,
|
|
883
|
+
page,
|
|
884
|
+
liveSessionIds,
|
|
885
|
+
listedById: new Map(
|
|
886
|
+
filtered.map((session) => [session.sessionId, session])
|
|
887
|
+
),
|
|
888
|
+
persistedTimeById,
|
|
889
|
+
// `isPinned` is re-read from the organization snapshot on every page
|
|
890
|
+
// request, so a pin flip between fetches can move the persisted key
|
|
891
|
+
// across the pinned/unpinned blocks. Keep the identity while the key
|
|
892
|
+
// could re-enter under either state.
|
|
893
|
+
reenters: /* @__PURE__ */ __name((row, persistedTime) => [true, false].some(
|
|
894
|
+
(isPinned) => compareOrganizedCursorKeys(boundary, {
|
|
895
|
+
isPinned,
|
|
896
|
+
activityTime: persistedTime,
|
|
897
|
+
sessionId: row.sessionId
|
|
898
|
+
}) < 0
|
|
899
|
+
), "reenters")
|
|
900
|
+
});
|
|
901
|
+
nextCursor = encodeOrganizedCursor(
|
|
902
|
+
boundary,
|
|
903
|
+
group,
|
|
904
|
+
archiveState,
|
|
905
|
+
options.sourceType,
|
|
906
|
+
options.sourceId,
|
|
907
|
+
options.conversationKind,
|
|
908
|
+
emitted
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
return {
|
|
912
|
+
sessions: page,
|
|
913
|
+
...nextCursor !== void 0 ? { nextCursor } : {},
|
|
914
|
+
...liveMergeFailed ? { liveMergeFailed: true } : {},
|
|
915
|
+
...persisted.truncated ? { truncated: true } : {}
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
__name(listOrganizedWorkspaceSessionsForResponse, "listOrganizedWorkspaceSessionsForResponse");
|
|
919
|
+
async function listWorkspaceSessionsByMetadataForResponse(bridge, workspaceCwd, options, pageSize, filter, readOptions) {
|
|
920
|
+
const archiveState = options.archiveState ?? "active";
|
|
921
|
+
const sessionService = new SessionService(workspaceCwd);
|
|
922
|
+
const bySessionId = /* @__PURE__ */ new Map();
|
|
923
|
+
const persisted = await listAllPersistedSummaries(
|
|
924
|
+
sessionService,
|
|
925
|
+
workspaceCwd,
|
|
926
|
+
archiveState,
|
|
927
|
+
readOptions.runtimeBaseDir,
|
|
928
|
+
"metadata",
|
|
929
|
+
readOptions.signal
|
|
930
|
+
);
|
|
931
|
+
readOptions.signal?.throwIfAborted();
|
|
932
|
+
const canonicalizeStandaloneIds = filter.conversationKind === "standalone-top-level";
|
|
933
|
+
const conflictedStandaloneIds = /* @__PURE__ */ new Set();
|
|
934
|
+
const persistedTimeById = /* @__PURE__ */ new Map();
|
|
935
|
+
for (const session of persisted.sessions) {
|
|
936
|
+
let sessionId = session.sessionId;
|
|
937
|
+
if (canonicalizeStandaloneIds) {
|
|
938
|
+
const parsed = parseCallerSuppliedSessionId(sessionId);
|
|
939
|
+
if (parsed.kind !== "valid") continue;
|
|
940
|
+
sessionId = parsed.sessionId;
|
|
941
|
+
if (conflictedStandaloneIds.has(sessionId)) continue;
|
|
942
|
+
if (bySessionId.has(sessionId)) {
|
|
943
|
+
bySessionId.delete(sessionId);
|
|
944
|
+
persistedTimeById.delete(sessionId);
|
|
945
|
+
conflictedStandaloneIds.add(sessionId);
|
|
946
|
+
continue;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
bySessionId.set(sessionId, {
|
|
950
|
+
...clonePersistedSummary(session),
|
|
951
|
+
sessionId
|
|
952
|
+
});
|
|
953
|
+
persistedTimeById.set(sessionId, getSummaryActivityTime(session));
|
|
954
|
+
}
|
|
955
|
+
const liveSessionIds = /* @__PURE__ */ new Set();
|
|
956
|
+
let liveMergeFailed = false;
|
|
957
|
+
if (readOptions.mergeLive !== false && archiveState !== "archived") {
|
|
958
|
+
try {
|
|
959
|
+
for (const live of bridge.listWorkspaceSessions(workspaceCwd)) {
|
|
960
|
+
let sessionId = live.sessionId;
|
|
961
|
+
if (canonicalizeStandaloneIds) {
|
|
962
|
+
const parsed = parseCallerSuppliedSessionId(sessionId);
|
|
963
|
+
if (parsed.kind !== "valid" || conflictedStandaloneIds.has(parsed.sessionId)) {
|
|
964
|
+
continue;
|
|
965
|
+
}
|
|
966
|
+
sessionId = parsed.sessionId;
|
|
967
|
+
}
|
|
968
|
+
const canonicalLive = sessionId === live.sessionId ? live : { ...live, sessionId };
|
|
969
|
+
liveSessionIds.add(sessionId);
|
|
970
|
+
const existing = bySessionId.get(sessionId);
|
|
971
|
+
if (existing) {
|
|
972
|
+
bySessionId.set(
|
|
973
|
+
sessionId,
|
|
974
|
+
mergeLiveSessionSummary(existing, canonicalLive)
|
|
975
|
+
);
|
|
976
|
+
} else if (
|
|
977
|
+
// See the matching comment in
|
|
978
|
+
// `listOrganizedWorkspaceSessionsForResponse`: an untruncated scan
|
|
979
|
+
// already covers every persisted session, so skip the racy
|
|
980
|
+
// re-check when nothing was truncated.
|
|
981
|
+
!persisted.truncated || !await (readOptions.signal ? sessionService.sessionExists(sessionId, {
|
|
982
|
+
signal: readOptions.signal
|
|
983
|
+
}) : sessionService.sessionExists(sessionId))
|
|
984
|
+
) {
|
|
985
|
+
bySessionId.set(
|
|
986
|
+
sessionId,
|
|
987
|
+
await liveOnlySummary(
|
|
988
|
+
canonicalLive,
|
|
989
|
+
sessionService,
|
|
990
|
+
readOptions.signal
|
|
991
|
+
)
|
|
992
|
+
);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
} catch (error) {
|
|
996
|
+
readOptions.signal?.throwIfAborted();
|
|
997
|
+
liveMergeFailed = true;
|
|
998
|
+
writeStderrLine(
|
|
999
|
+
`qwen serve: session metadata filter live merge failed; using persisted sessions only: ${error instanceof Error ? error.message : String(error)}`
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
const matches = [...bySessionId.values()].filter(
|
|
1004
|
+
(session) => (filter.parentSessionId === void 0 || session.parentSessionId === filter.parentSessionId) && matchesSessionMetadataSource(session, filter)
|
|
1005
|
+
).sort(
|
|
1006
|
+
(a, b) => compareLiveSessionCursorKeys(
|
|
1007
|
+
getLiveSessionCursorKey(a),
|
|
1008
|
+
getLiveSessionCursorKey(b)
|
|
1009
|
+
)
|
|
1010
|
+
);
|
|
1011
|
+
readOptions.signal?.throwIfAborted();
|
|
1012
|
+
const cursor = options.cursor !== void 0 && options.cursor !== "" ? parseMetadataSessionCursor(options.cursor, {
|
|
1013
|
+
...filter,
|
|
1014
|
+
archiveState
|
|
1015
|
+
}) : void 0;
|
|
1016
|
+
const cursorKey = cursor?.last;
|
|
1017
|
+
const emittedBeforePage = new Set(cursor?.emitted ?? []);
|
|
1018
|
+
const afterCursor = cursorKey === void 0 ? matches : matches.filter(
|
|
1019
|
+
(session) => (
|
|
1020
|
+
// See the organized path: exclude rows already emitted at a
|
|
1021
|
+
// live-derived key whose key regressed behind the cursor.
|
|
1022
|
+
!emittedBeforePage.has(session.sessionId) && compareLiveSessionCursorKeys(
|
|
1023
|
+
cursorKey,
|
|
1024
|
+
getLiveSessionCursorKey(session)
|
|
1025
|
+
) < 0
|
|
1026
|
+
)
|
|
1027
|
+
);
|
|
1028
|
+
const page = afterCursor.slice(0, pageSize);
|
|
1029
|
+
let nextCursor;
|
|
1030
|
+
if (page.length < afterCursor.length) {
|
|
1031
|
+
const boundary = getLiveSessionCursorKey(page[page.length - 1]);
|
|
1032
|
+
const emitted = nextEmittedSessionIds({
|
|
1033
|
+
carried: emittedBeforePage,
|
|
1034
|
+
page,
|
|
1035
|
+
liveSessionIds,
|
|
1036
|
+
listedById: new Map(
|
|
1037
|
+
matches.map((session) => [session.sessionId, session])
|
|
1038
|
+
),
|
|
1039
|
+
persistedTimeById,
|
|
1040
|
+
reenters: /* @__PURE__ */ __name((row, persistedTime) => compareLiveSessionCursorKeys(boundary, {
|
|
1041
|
+
activityTime: persistedTime,
|
|
1042
|
+
sessionId: row.sessionId
|
|
1043
|
+
}) < 0, "reenters")
|
|
1044
|
+
});
|
|
1045
|
+
nextCursor = encodeMetadataSessionCursor(
|
|
1046
|
+
boundary,
|
|
1047
|
+
filter,
|
|
1048
|
+
archiveState,
|
|
1049
|
+
emitted
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
1052
|
+
return {
|
|
1053
|
+
sessions: page,
|
|
1054
|
+
...nextCursor !== void 0 ? { nextCursor } : {},
|
|
1055
|
+
...liveMergeFailed ? { liveMergeFailed: true } : {},
|
|
1056
|
+
...persisted.truncated ? { truncated: true } : {}
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
__name(listWorkspaceSessionsByMetadataForResponse, "listWorkspaceSessionsByMetadataForResponse");
|
|
1060
|
+
async function listWorkspaceSessionsForResponse(bridge, workspaceCwd, options, readOptions = {}) {
|
|
1061
|
+
readOptions.signal?.throwIfAborted();
|
|
1062
|
+
const runtimeBaseDir = new Storage(
|
|
1063
|
+
workspaceCwd,
|
|
1064
|
+
readOptions.runtimeBaseDir
|
|
1065
|
+
).getRuntimeBaseDir();
|
|
1066
|
+
const result = await Storage.runWithResolvedRuntimeBaseDir(
|
|
1067
|
+
runtimeBaseDir,
|
|
1068
|
+
() => listWorkspaceSessionsForResponseInRuntime(bridge, workspaceCwd, options, {
|
|
1069
|
+
...readOptions.mergeLive !== void 0 ? { mergeLive: readOptions.mergeLive } : {},
|
|
1070
|
+
...readOptions.signal !== void 0 ? { signal: readOptions.signal } : {},
|
|
1071
|
+
runtimeBaseDir
|
|
1072
|
+
})
|
|
1073
|
+
);
|
|
1074
|
+
readOptions.signal?.throwIfAborted();
|
|
1075
|
+
return result;
|
|
1076
|
+
}
|
|
1077
|
+
__name(listWorkspaceSessionsForResponse, "listWorkspaceSessionsForResponse");
|
|
1078
|
+
async function listWorkspaceSessionsForResponseInRuntime(bridge, workspaceCwd, options, readOptions) {
|
|
1079
|
+
readOptions.signal?.throwIfAborted();
|
|
1080
|
+
const rawSize = options?.size;
|
|
1081
|
+
const requestedSize = typeof rawSize === "number" && Number.isSafeInteger(rawSize) ? rawSize : DEFAULT_SESSION_PAGE_SIZE;
|
|
1082
|
+
const pageSize = Math.min(Math.max(requestedSize, 1), MAX_SESSION_PAGE_SIZE);
|
|
1083
|
+
if (options?.view === "organized") {
|
|
1084
|
+
return listOrganizedWorkspaceSessionsForResponse(
|
|
1085
|
+
bridge,
|
|
1086
|
+
workspaceCwd,
|
|
1087
|
+
options,
|
|
1088
|
+
pageSize,
|
|
1089
|
+
readOptions
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
if (options?.parentSessionId !== void 0 || options?.sourceType !== void 0 || options?.conversationKind !== void 0) {
|
|
1093
|
+
return listWorkspaceSessionsByMetadataForResponse(
|
|
1094
|
+
bridge,
|
|
1095
|
+
workspaceCwd,
|
|
1096
|
+
options,
|
|
1097
|
+
pageSize,
|
|
1098
|
+
{
|
|
1099
|
+
...options.parentSessionId !== void 0 ? { parentSessionId: options.parentSessionId } : {},
|
|
1100
|
+
...options.sourceType !== void 0 ? { sourceType: options.sourceType } : {},
|
|
1101
|
+
...options.sourceId !== void 0 ? { sourceId: options.sourceId } : {},
|
|
1102
|
+
...options.conversationKind !== void 0 ? { conversationKind: options.conversationKind } : {}
|
|
1103
|
+
},
|
|
1104
|
+
readOptions
|
|
1105
|
+
);
|
|
1106
|
+
}
|
|
1107
|
+
let numericCursor;
|
|
1108
|
+
if (options?.cursor != null) {
|
|
1109
|
+
numericCursor = parseSessionCursor(options.cursor);
|
|
1110
|
+
}
|
|
1111
|
+
const isFirstPage = numericCursor === void 0;
|
|
1112
|
+
const sessionService = new SessionService(workspaceCwd);
|
|
1113
|
+
const archiveState = options?.archiveState ?? "active";
|
|
1114
|
+
const persisted = await sessionService.listSessions({
|
|
1115
|
+
cursor: numericCursor,
|
|
1116
|
+
size: pageSize,
|
|
1117
|
+
archiveState,
|
|
1118
|
+
...readOptions.signal ? { signal: readOptions.signal } : {}
|
|
1119
|
+
});
|
|
1120
|
+
readOptions.signal?.throwIfAborted();
|
|
1121
|
+
const bySessionId = /* @__PURE__ */ new Map();
|
|
1122
|
+
for (const item of persisted.items) {
|
|
1123
|
+
bySessionId.set(item.sessionId, toSummary(item));
|
|
1124
|
+
}
|
|
1125
|
+
await enrichWorktreeSidecars(
|
|
1126
|
+
bySessionId,
|
|
1127
|
+
sessionService,
|
|
1128
|
+
archiveState,
|
|
1129
|
+
readOptions.signal
|
|
1130
|
+
);
|
|
1131
|
+
await enrichPrSidecars(
|
|
1132
|
+
bySessionId,
|
|
1133
|
+
sessionService,
|
|
1134
|
+
archiveState,
|
|
1135
|
+
readOptions.signal
|
|
1136
|
+
);
|
|
1137
|
+
readOptions.signal?.throwIfAborted();
|
|
1138
|
+
if (archiveState === "archived" || readOptions.mergeLive === false) {
|
|
1139
|
+
const sessions2 = [...bySessionId.values()];
|
|
1140
|
+
const nextCursor2 = persisted.nextCursor != null ? String(persisted.nextCursor) : void 0;
|
|
1141
|
+
return { sessions: sessions2, nextCursor: nextCursor2 };
|
|
1142
|
+
}
|
|
1143
|
+
const liveSessions = bridge.listWorkspaceSessions(workspaceCwd);
|
|
1144
|
+
for (const live of liveSessions) {
|
|
1145
|
+
const existing = bySessionId.get(live.sessionId);
|
|
1146
|
+
if (existing) {
|
|
1147
|
+
bySessionId.set(live.sessionId, mergeLiveSessionSummary(existing, live));
|
|
1148
|
+
} else if (isFirstPage && // If this is a complete scan (no further pages), a missing
|
|
1149
|
+
// `sessionId` here is definitively new — no disk re-check needed.
|
|
1150
|
+
// Re-checking raced a session that persists its first write (e.g. a
|
|
1151
|
+
// `displayName` update) between the scan above and this point:
|
|
1152
|
+
// `existing` stayed undefined but `sessionExists` flipped to true,
|
|
1153
|
+
// silently dropping the live session from the response instead of
|
|
1154
|
+
// merging it.
|
|
1155
|
+
(persisted.nextCursor == null || !await (readOptions.signal ? sessionService.sessionExists(live.sessionId, {
|
|
1156
|
+
signal: readOptions.signal
|
|
1157
|
+
}) : sessionService.sessionExists(live.sessionId)))) {
|
|
1158
|
+
bySessionId.set(
|
|
1159
|
+
live.sessionId,
|
|
1160
|
+
await liveOnlySummary(live, sessionService, readOptions.signal)
|
|
1161
|
+
);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
const sessions = [...bySessionId.values()].sort((a, b) => {
|
|
1165
|
+
const aTime = Date.parse(a.updatedAt ?? a.createdAt);
|
|
1166
|
+
const bTime = Date.parse(b.updatedAt ?? b.createdAt);
|
|
1167
|
+
return bTime - aTime;
|
|
1168
|
+
});
|
|
1169
|
+
readOptions.signal?.throwIfAborted();
|
|
1170
|
+
const nextCursor = persisted.nextCursor != null ? String(persisted.nextCursor) : void 0;
|
|
1171
|
+
return { sessions, nextCursor };
|
|
1172
|
+
}
|
|
1173
|
+
__name(listWorkspaceSessionsForResponseInRuntime, "listWorkspaceSessionsForResponseInRuntime");
|
|
1174
|
+
async function listLiveWorkspaceSessionsForResponse(bridge, workspaceCwd, options, readOptions = {}) {
|
|
1175
|
+
const runtimeBaseDir = new Storage(
|
|
1176
|
+
workspaceCwd,
|
|
1177
|
+
readOptions.runtimeBaseDir
|
|
1178
|
+
).getRuntimeBaseDir();
|
|
1179
|
+
return Storage.runWithResolvedRuntimeBaseDir(runtimeBaseDir, async () => {
|
|
1180
|
+
const rawSize = options?.size;
|
|
1181
|
+
const requestedSize = typeof rawSize === "number" && Number.isSafeInteger(rawSize) ? rawSize : DEFAULT_SESSION_PAGE_SIZE;
|
|
1182
|
+
const pageSize = Math.min(
|
|
1183
|
+
Math.max(requestedSize, 1),
|
|
1184
|
+
MAX_SESSION_PAGE_SIZE
|
|
1185
|
+
);
|
|
1186
|
+
const cursorKey = options?.cursor !== void 0 ? parseLiveSessionCursor(options.cursor) : void 0;
|
|
1187
|
+
const sessions = bridge.listWorkspaceSessions(workspaceCwd).sort(
|
|
1188
|
+
(a, b) => compareLiveSessionCursorKeys(
|
|
1189
|
+
getLiveSessionCursorKey(a),
|
|
1190
|
+
getLiveSessionCursorKey(b)
|
|
1191
|
+
)
|
|
1192
|
+
);
|
|
1193
|
+
const afterCursor = cursorKey === void 0 ? sessions : sessions.filter(
|
|
1194
|
+
(session) => compareLiveSessionCursorKeys(
|
|
1195
|
+
cursorKey,
|
|
1196
|
+
getLiveSessionCursorKey(session)
|
|
1197
|
+
) < 0
|
|
1198
|
+
);
|
|
1199
|
+
const page = afterCursor.slice(0, pageSize);
|
|
1200
|
+
const sessionService = new SessionService(workspaceCwd);
|
|
1201
|
+
const enriched = await Promise.all(
|
|
1202
|
+
page.map(
|
|
1203
|
+
(summary) => liveOnlySummary(summary, sessionService, readOptions.signal)
|
|
1204
|
+
)
|
|
1205
|
+
);
|
|
1206
|
+
const nextCursor = page.length < afterCursor.length ? encodeLiveSessionCursor(
|
|
1207
|
+
getLiveSessionCursorKey(page[page.length - 1])
|
|
1208
|
+
) : void 0;
|
|
1209
|
+
return {
|
|
1210
|
+
sessions: enriched,
|
|
1211
|
+
...nextCursor !== void 0 ? { nextCursor } : {}
|
|
1212
|
+
};
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
__name(listLiveWorkspaceSessionsForResponse, "listLiveWorkspaceSessionsForResponse");
|
|
1216
|
+
async function getWorkspaceSessionInfoForResponse(bridge, workspaceCwd, options = {}) {
|
|
1217
|
+
const counts = await new SessionService(workspaceCwd).getSessionInfoCounts();
|
|
1218
|
+
return {
|
|
1219
|
+
active: counts.active,
|
|
1220
|
+
archived: counts.archived,
|
|
1221
|
+
total: counts.total,
|
|
1222
|
+
...options.includeLive === false ? {} : { live: bridge.listWorkspaceSessions(workspaceCwd).length },
|
|
1223
|
+
expensive: true,
|
|
1224
|
+
cost: "disk_scan",
|
|
1225
|
+
...counts.truncated ? { truncated: true } : {}
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
__name(getWorkspaceSessionInfoForResponse, "getWorkspaceSessionInfoForResponse");
|
|
1229
|
+
function parseSessionPageSizeQuery(raw) {
|
|
1230
|
+
if (typeof raw !== "string") return void 0;
|
|
1231
|
+
const trimmed = raw.trim();
|
|
1232
|
+
if (!/^[+-]?\d+$/.test(trimmed)) return void 0;
|
|
1233
|
+
const parsed = Number.parseInt(trimmed, 10);
|
|
1234
|
+
if (!Number.isFinite(parsed)) return void 0;
|
|
1235
|
+
if (Number.isSafeInteger(parsed)) return parsed;
|
|
1236
|
+
return trimmed.startsWith("-") ? 1 : MAX_SESSION_PAGE_SIZE;
|
|
1237
|
+
}
|
|
1238
|
+
__name(parseSessionPageSizeQuery, "parseSessionPageSizeQuery");
|
|
1239
|
+
|
|
1240
|
+
// packages/cli/src/serve/workspace-runtime-storage.ts
|
|
1241
|
+
init_esbuild_shims();
|
|
1242
|
+
function runWithWorkspaceRuntimeStorage(runtime, fn) {
|
|
1243
|
+
return Storage.runWithResolvedRuntimeBaseDir(
|
|
1244
|
+
runtime.sessionRuntimeBaseDir,
|
|
1245
|
+
fn
|
|
1246
|
+
);
|
|
1247
|
+
}
|
|
1248
|
+
__name(runWithWorkspaceRuntimeStorage, "runWithWorkspaceRuntimeStorage");
|
|
1249
|
+
function createWorkspaceRuntimeSessionService(runtime, options = {}) {
|
|
1250
|
+
return new SessionService(runtime.workspaceCwd, {
|
|
1251
|
+
...options,
|
|
1252
|
+
runtimeBaseDir: runtime.sessionRuntimeBaseDir
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
__name(createWorkspaceRuntimeSessionService, "createWorkspaceRuntimeSessionService");
|
|
1256
|
+
|
|
1257
|
+
// packages/cli/src/serve/server/session-archive.ts
|
|
1258
|
+
init_esbuild_shims();
|
|
1259
|
+
|
|
1260
|
+
// packages/cli/src/serve/scheduled-task-session-lifecycle.ts
|
|
1261
|
+
init_esbuild_shims();
|
|
1262
|
+
function isBoundTask(task) {
|
|
1263
|
+
return typeof task.sessionId === "string" && task.sessionId.length > 0;
|
|
1264
|
+
}
|
|
1265
|
+
__name(isBoundTask, "isBoundTask");
|
|
1266
|
+
async function disableTasksForSessions(projectRoot, sessionIds, options = {}) {
|
|
1267
|
+
if (sessionIds.length === 0) return;
|
|
1268
|
+
const targets = new Set(sessionIds);
|
|
1269
|
+
await updateCronTasks(
|
|
1270
|
+
projectRoot,
|
|
1271
|
+
(tasks) => {
|
|
1272
|
+
let changed = false;
|
|
1273
|
+
const next = tasks.map((task) => {
|
|
1274
|
+
if (isBoundTask(task) && targets.has(task.sessionId) && task.enabled !== false) {
|
|
1275
|
+
changed = true;
|
|
1276
|
+
return { ...task, enabled: false, disabledByArchive: true };
|
|
1277
|
+
}
|
|
1278
|
+
return task;
|
|
1279
|
+
});
|
|
1280
|
+
return changed ? next : tasks;
|
|
1281
|
+
},
|
|
1282
|
+
options
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1285
|
+
__name(disableTasksForSessions, "disableTasksForSessions");
|
|
1286
|
+
async function enableTasksForSessions(projectRoot, sessionIds, now = Date.now(), options = {}) {
|
|
1287
|
+
if (sessionIds.length === 0) return;
|
|
1288
|
+
const targets = new Set(sessionIds);
|
|
1289
|
+
await updateCronTasks(
|
|
1290
|
+
projectRoot,
|
|
1291
|
+
(tasks) => {
|
|
1292
|
+
let changed = false;
|
|
1293
|
+
const next = tasks.map((task) => {
|
|
1294
|
+
if (isBoundTask(task) && targets.has(task.sessionId) && task.enabled === false && task.disabledByArchive === true) {
|
|
1295
|
+
changed = true;
|
|
1296
|
+
const resumed = { ...task, enabled: true };
|
|
1297
|
+
delete resumed.disabledByArchive;
|
|
1298
|
+
const minute = now - now % 6e4;
|
|
1299
|
+
if (resumed.recurring) {
|
|
1300
|
+
resumed.lastFiredAt = minute;
|
|
1301
|
+
} else {
|
|
1302
|
+
resumed.createdAt = now;
|
|
1303
|
+
resumed.lastFiredAt = minute;
|
|
1304
|
+
}
|
|
1305
|
+
return resumed;
|
|
1306
|
+
}
|
|
1307
|
+
return task;
|
|
1308
|
+
});
|
|
1309
|
+
return changed ? next : tasks;
|
|
1310
|
+
},
|
|
1311
|
+
options
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1314
|
+
__name(enableTasksForSessions, "enableTasksForSessions");
|
|
1315
|
+
async function removeTasksForSessions(projectRoot, sessionIds, options = {}) {
|
|
1316
|
+
if (sessionIds.length === 0) return;
|
|
1317
|
+
const targets = new Set(sessionIds);
|
|
1318
|
+
await updateCronTasks(
|
|
1319
|
+
projectRoot,
|
|
1320
|
+
(tasks) => {
|
|
1321
|
+
const next = tasks.filter(
|
|
1322
|
+
(task) => !isBoundTask(task) || !targets.has(task.sessionId)
|
|
1323
|
+
);
|
|
1324
|
+
return next.length === tasks.length ? tasks : next;
|
|
1325
|
+
},
|
|
1326
|
+
options
|
|
1327
|
+
);
|
|
1328
|
+
}
|
|
1329
|
+
__name(removeTasksForSessions, "removeTasksForSessions");
|
|
1330
|
+
|
|
1331
|
+
// packages/cli/src/serve/server/session-archive.ts
|
|
1332
|
+
var DaemonDrainingError = class extends Error {
|
|
1333
|
+
static {
|
|
1334
|
+
__name(this, "DaemonDrainingError");
|
|
1335
|
+
}
|
|
1336
|
+
name = "DaemonDrainingError";
|
|
1337
|
+
code = "daemon_draining";
|
|
1338
|
+
constructor() {
|
|
1339
|
+
super("The daemon is draining and no longer accepts session maintenance.");
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
1342
|
+
var SessionArchiveCoordinator = class {
|
|
1343
|
+
static {
|
|
1344
|
+
__name(this, "SessionArchiveCoordinator");
|
|
1345
|
+
}
|
|
1346
|
+
exclusive = /* @__PURE__ */ new Set();
|
|
1347
|
+
shared = /* @__PURE__ */ new Map();
|
|
1348
|
+
sharedDrains = /* @__PURE__ */ new Map();
|
|
1349
|
+
maintenanceSealed = false;
|
|
1350
|
+
activeMaintenance = 0;
|
|
1351
|
+
maintenanceDrain;
|
|
1352
|
+
// Lock keys are canonicalized like every other session-id lookup: batch
|
|
1353
|
+
// delete/archive/unarchive lock raw caller spellings while restore locks
|
|
1354
|
+
// the request spelling, and on a case-insensitive filesystem both reach
|
|
1355
|
+
// the same transcript file — uncanonicalized keys would let a differently
|
|
1356
|
+
// cased caller id slip past a held guard and unlink it mid-restore.
|
|
1357
|
+
assertNotTransitioning(sessionId) {
|
|
1358
|
+
if (this.exclusive.has(normalizeSessionIdForLookup(sessionId))) {
|
|
1359
|
+
throw new SessionArchivingError(sessionId);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
async runExclusiveMany(sessionIds, fn) {
|
|
1363
|
+
if (this.maintenanceSealed) {
|
|
1364
|
+
throw new DaemonDrainingError();
|
|
1365
|
+
}
|
|
1366
|
+
const uniqueSessionIds = [
|
|
1367
|
+
...new Set(sessionIds.map(normalizeSessionIdForLookup))
|
|
1368
|
+
];
|
|
1369
|
+
for (const sessionId of uniqueSessionIds) {
|
|
1370
|
+
this.assertNotTransitioning(sessionId);
|
|
1371
|
+
if ((this.shared.get(sessionId) ?? 0) > 0) {
|
|
1372
|
+
throw new SessionArchivingError(sessionId, "shared");
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
for (const sessionId of uniqueSessionIds) {
|
|
1376
|
+
this.exclusive.add(sessionId);
|
|
1377
|
+
}
|
|
1378
|
+
this.activeMaintenance++;
|
|
1379
|
+
try {
|
|
1380
|
+
return await fn();
|
|
1381
|
+
} finally {
|
|
1382
|
+
for (const sessionId of uniqueSessionIds) {
|
|
1383
|
+
this.exclusive.delete(sessionId);
|
|
1384
|
+
}
|
|
1385
|
+
this.activeMaintenance--;
|
|
1386
|
+
if (this.activeMaintenance === 0) {
|
|
1387
|
+
this.maintenanceDrain?.resolve();
|
|
1388
|
+
this.maintenanceDrain = void 0;
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
async runExclusiveAfterShared(rawSessionId, fn) {
|
|
1393
|
+
if (this.maintenanceSealed) {
|
|
1394
|
+
throw new DaemonDrainingError();
|
|
1395
|
+
}
|
|
1396
|
+
const sessionId = normalizeSessionIdForLookup(rawSessionId);
|
|
1397
|
+
this.assertNotTransitioning(sessionId);
|
|
1398
|
+
this.exclusive.add(sessionId);
|
|
1399
|
+
this.activeMaintenance++;
|
|
1400
|
+
try {
|
|
1401
|
+
const sharedCount = this.shared.get(sessionId) ?? 0;
|
|
1402
|
+
if (sharedCount > 0) {
|
|
1403
|
+
let drain = this.sharedDrains.get(sessionId);
|
|
1404
|
+
if (!drain) {
|
|
1405
|
+
let resolve;
|
|
1406
|
+
const promise = new Promise((done) => {
|
|
1407
|
+
resolve = done;
|
|
1408
|
+
});
|
|
1409
|
+
drain = { promise, resolve };
|
|
1410
|
+
this.sharedDrains.set(sessionId, drain);
|
|
1411
|
+
}
|
|
1412
|
+
await drain.promise;
|
|
1413
|
+
}
|
|
1414
|
+
return await fn();
|
|
1415
|
+
} finally {
|
|
1416
|
+
this.sharedDrains.delete(sessionId);
|
|
1417
|
+
this.exclusive.delete(sessionId);
|
|
1418
|
+
this.activeMaintenance--;
|
|
1419
|
+
if (this.activeMaintenance === 0) {
|
|
1420
|
+
this.maintenanceDrain?.resolve();
|
|
1421
|
+
this.maintenanceDrain = void 0;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
sealMaintenanceAndWait() {
|
|
1426
|
+
this.maintenanceSealed = true;
|
|
1427
|
+
if (this.activeMaintenance === 0) {
|
|
1428
|
+
return Promise.resolve();
|
|
1429
|
+
}
|
|
1430
|
+
if (!this.maintenanceDrain) {
|
|
1431
|
+
let resolve;
|
|
1432
|
+
const promise = new Promise((done) => {
|
|
1433
|
+
resolve = done;
|
|
1434
|
+
});
|
|
1435
|
+
this.maintenanceDrain = { promise, resolve };
|
|
1436
|
+
}
|
|
1437
|
+
return this.maintenanceDrain.promise;
|
|
1438
|
+
}
|
|
1439
|
+
async runSharedMany(sessionIds, fn) {
|
|
1440
|
+
if (this.maintenanceSealed) {
|
|
1441
|
+
throw new DaemonDrainingError();
|
|
1442
|
+
}
|
|
1443
|
+
const uniqueSessionIds = [
|
|
1444
|
+
...new Set(sessionIds.map(normalizeSessionIdForLookup))
|
|
1445
|
+
];
|
|
1446
|
+
for (const sessionId of uniqueSessionIds) {
|
|
1447
|
+
this.assertNotTransitioning(sessionId);
|
|
1448
|
+
}
|
|
1449
|
+
for (const sessionId of uniqueSessionIds) {
|
|
1450
|
+
this.shared.set(sessionId, (this.shared.get(sessionId) ?? 0) + 1);
|
|
1451
|
+
}
|
|
1452
|
+
this.activeMaintenance++;
|
|
1453
|
+
try {
|
|
1454
|
+
return await fn();
|
|
1455
|
+
} finally {
|
|
1456
|
+
for (const sessionId of uniqueSessionIds) {
|
|
1457
|
+
const count = (this.shared.get(sessionId) ?? 1) - 1;
|
|
1458
|
+
if (count <= 0) {
|
|
1459
|
+
this.shared.delete(sessionId);
|
|
1460
|
+
this.sharedDrains.get(sessionId)?.resolve();
|
|
1461
|
+
this.sharedDrains.delete(sessionId);
|
|
1462
|
+
} else {
|
|
1463
|
+
this.shared.set(sessionId, count);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
this.activeMaintenance--;
|
|
1467
|
+
if (this.activeMaintenance === 0) {
|
|
1468
|
+
this.maintenanceDrain?.resolve();
|
|
1469
|
+
this.maintenanceDrain = void 0;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
};
|
|
1474
|
+
async function runWithDaemonWriterLease(params) {
|
|
1475
|
+
const {
|
|
1476
|
+
action,
|
|
1477
|
+
sessionId,
|
|
1478
|
+
service,
|
|
1479
|
+
mutate,
|
|
1480
|
+
mutationAppliedAfterError,
|
|
1481
|
+
afterMutationApplied
|
|
1482
|
+
} = params;
|
|
1483
|
+
let lease;
|
|
1484
|
+
try {
|
|
1485
|
+
const leaseOptions = {
|
|
1486
|
+
processKind: "daemon",
|
|
1487
|
+
reclaimPolicy: "never",
|
|
1488
|
+
takeoverPolicy: "certified"
|
|
1489
|
+
};
|
|
1490
|
+
try {
|
|
1491
|
+
lease = await service.acquireSessionWriterLease(sessionId, leaseOptions);
|
|
1492
|
+
} catch (error) {
|
|
1493
|
+
if (!(error instanceof SessionWriterUnavailableError) && !(error instanceof SessionTranscriptChangedError)) {
|
|
1494
|
+
throw error;
|
|
1495
|
+
}
|
|
1496
|
+
lease = await service.acquireSessionMaintenanceLease(
|
|
1497
|
+
sessionId,
|
|
1498
|
+
leaseOptions
|
|
1499
|
+
);
|
|
1500
|
+
}
|
|
1501
|
+
} catch (error) {
|
|
1502
|
+
return { mutationApplied: false, error };
|
|
1503
|
+
}
|
|
1504
|
+
let value;
|
|
1505
|
+
let mutationApplied = false;
|
|
1506
|
+
let mutationError;
|
|
1507
|
+
try {
|
|
1508
|
+
const mutation = await mutate(() => lease.assertOwnedAndUnchanged());
|
|
1509
|
+
value = mutation.value;
|
|
1510
|
+
mutationApplied = mutation.mutationApplied;
|
|
1511
|
+
} catch (error) {
|
|
1512
|
+
mutationError = error;
|
|
1513
|
+
try {
|
|
1514
|
+
mutationApplied = await mutationAppliedAfterError();
|
|
1515
|
+
} catch {
|
|
1516
|
+
mutationApplied = false;
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
let maintenanceError;
|
|
1520
|
+
if (mutationApplied) {
|
|
1521
|
+
try {
|
|
1522
|
+
await afterMutationApplied();
|
|
1523
|
+
} catch (error) {
|
|
1524
|
+
maintenanceError = error;
|
|
1525
|
+
logSessionArchiveWarning(
|
|
1526
|
+
`scheduled task lifecycle update failed action=${action} workspace=${safeLogValue(
|
|
1527
|
+
service.getProjectRoot()
|
|
1528
|
+
)} session=${safeLogValue(sessionId)} error=${safeLogValue(
|
|
1529
|
+
errorMessage(error)
|
|
1530
|
+
)}`
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
let releaseError;
|
|
1535
|
+
try {
|
|
1536
|
+
await lease.release();
|
|
1537
|
+
} catch (error) {
|
|
1538
|
+
releaseError = error;
|
|
1539
|
+
}
|
|
1540
|
+
if (releaseError !== void 0) {
|
|
1541
|
+
logMaintenanceLeaseReleaseFailure({
|
|
1542
|
+
action,
|
|
1543
|
+
workspace: service.getProjectRoot(),
|
|
1544
|
+
sessionId,
|
|
1545
|
+
error: releaseError,
|
|
1546
|
+
mutationApplied
|
|
1547
|
+
});
|
|
1548
|
+
if (mutationError !== void 0) {
|
|
1549
|
+
logSessionArchiveWarning(
|
|
1550
|
+
`session maintenance mutation also failed action=${action} workspace=${safeLogValue(
|
|
1551
|
+
service.getProjectRoot()
|
|
1552
|
+
)} session=${safeLogValue(sessionId)} error=${safeLogValue(
|
|
1553
|
+
errorMessage(mutationError)
|
|
1554
|
+
)}`
|
|
1555
|
+
);
|
|
1556
|
+
}
|
|
1557
|
+
return { mutationApplied, error: releaseError, maintenanceError };
|
|
1558
|
+
}
|
|
1559
|
+
if (mutationError !== void 0) {
|
|
1560
|
+
return { mutationApplied, error: mutationError, maintenanceError };
|
|
1561
|
+
}
|
|
1562
|
+
return { value, mutationApplied, maintenanceError };
|
|
1563
|
+
}
|
|
1564
|
+
__name(runWithDaemonWriterLease, "runWithDaemonWriterLease");
|
|
1565
|
+
function logMaintenanceLeaseReleaseFailure(params) {
|
|
1566
|
+
const errorKind = typeof params.error === "object" && params.error !== null && typeof params.error.errorKind === "string" ? params.error.errorKind : "unknown";
|
|
1567
|
+
logSessionArchiveWarning(
|
|
1568
|
+
`session maintenance lease release failed action=${params.action} workspace=${safeLogValue(
|
|
1569
|
+
params.workspace
|
|
1570
|
+
)} session=${safeLogValue(params.sessionId)} errorKind=${safeLogValue(
|
|
1571
|
+
errorKind
|
|
1572
|
+
)} mutationApplied=${params.mutationApplied}`
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
__name(logMaintenanceLeaseReleaseFailure, "logMaintenanceLeaseReleaseFailure");
|
|
1576
|
+
async function classifySessionLocation(service, sessionId) {
|
|
1577
|
+
return service.getMaintainableSessionLocation(sessionId);
|
|
1578
|
+
}
|
|
1579
|
+
__name(classifySessionLocation, "classifySessionLocation");
|
|
1580
|
+
function sessionLocationError(sessionId) {
|
|
1581
|
+
const error = new SessionConflictError(sessionId);
|
|
1582
|
+
error.message = `Session "${sessionId}" exists in both active and archived directories. Retry with resolveConflicts: true to keep one copy.`;
|
|
1583
|
+
return error;
|
|
1584
|
+
}
|
|
1585
|
+
__name(sessionLocationError, "sessionLocationError");
|
|
1586
|
+
function updateScheduledTaskForMaintenance(service, sessionId, action, assertCanMutate) {
|
|
1587
|
+
if (action === "archive") {
|
|
1588
|
+
return disableTasksForSessions(service.getProjectRoot(), [sessionId], {
|
|
1589
|
+
assertCanCommit: assertCanMutate
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
if (action === "unarchive") {
|
|
1593
|
+
return enableTasksForSessions(
|
|
1594
|
+
service.getProjectRoot(),
|
|
1595
|
+
[sessionId],
|
|
1596
|
+
Date.now(),
|
|
1597
|
+
{ assertCanCommit: assertCanMutate }
|
|
1598
|
+
);
|
|
1599
|
+
}
|
|
1600
|
+
return removeTasksForSessions(service.getProjectRoot(), [sessionId], {
|
|
1601
|
+
assertCanCommit: assertCanMutate
|
|
1602
|
+
});
|
|
1603
|
+
}
|
|
1604
|
+
__name(updateScheduledTaskForMaintenance, "updateScheduledTaskForMaintenance");
|
|
1605
|
+
async function deletePersistedSessionWithLease(service, sessionId, assertCanMutate) {
|
|
1606
|
+
const initialLocation = await classifySessionLocation(service, sessionId);
|
|
1607
|
+
if (initialLocation === void 0) {
|
|
1608
|
+
let maintenanceError;
|
|
1609
|
+
try {
|
|
1610
|
+
assertCanMutate?.();
|
|
1611
|
+
await updateScheduledTaskForMaintenance(
|
|
1612
|
+
service,
|
|
1613
|
+
sessionId,
|
|
1614
|
+
"delete",
|
|
1615
|
+
assertCanMutate
|
|
1616
|
+
);
|
|
1617
|
+
} catch (error) {
|
|
1618
|
+
maintenanceError = error;
|
|
1619
|
+
logSessionArchiveWarning(
|
|
1620
|
+
`scheduled task lifecycle update failed action=delete workspace=${safeLogValue(
|
|
1621
|
+
service.getProjectRoot()
|
|
1622
|
+
)} session=${safeLogValue(sessionId)} error=${safeLogValue(
|
|
1623
|
+
errorMessage(error)
|
|
1624
|
+
)}`
|
|
1625
|
+
);
|
|
1626
|
+
}
|
|
1627
|
+
return {
|
|
1628
|
+
kind: "notFound",
|
|
1629
|
+
mutationApplied: false,
|
|
1630
|
+
maintenanceError
|
|
1631
|
+
};
|
|
1632
|
+
}
|
|
1633
|
+
const mutation = await runWithDaemonWriterLease({
|
|
1634
|
+
action: "delete",
|
|
1635
|
+
sessionId,
|
|
1636
|
+
service,
|
|
1637
|
+
mutate: /* @__PURE__ */ __name(async (assertOwnedAndUnchanged) => {
|
|
1638
|
+
const lockedLocation = await classifySessionLocation(service, sessionId);
|
|
1639
|
+
if (lockedLocation === void 0) {
|
|
1640
|
+
return {
|
|
1641
|
+
value: "notFound",
|
|
1642
|
+
mutationApplied: false
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
const removed = await service.removeSession(sessionId, {
|
|
1646
|
+
assertStorageUnchanged: assertOwnedAndUnchanged,
|
|
1647
|
+
assertCanMutate
|
|
1648
|
+
});
|
|
1649
|
+
return {
|
|
1650
|
+
value: removed ? "removed" : "notFound",
|
|
1651
|
+
mutationApplied: removed
|
|
1652
|
+
};
|
|
1653
|
+
}, "mutate"),
|
|
1654
|
+
mutationAppliedAfterError: /* @__PURE__ */ __name(async () => await classifySessionLocation(service, sessionId) === void 0, "mutationAppliedAfterError"),
|
|
1655
|
+
afterMutationApplied: /* @__PURE__ */ __name(async () => {
|
|
1656
|
+
assertCanMutate?.();
|
|
1657
|
+
await updateScheduledTaskForMaintenance(
|
|
1658
|
+
service,
|
|
1659
|
+
sessionId,
|
|
1660
|
+
"delete",
|
|
1661
|
+
assertCanMutate
|
|
1662
|
+
);
|
|
1663
|
+
}, "afterMutationApplied")
|
|
1664
|
+
});
|
|
1665
|
+
if (mutation.error !== void 0) {
|
|
1666
|
+
return {
|
|
1667
|
+
kind: "error",
|
|
1668
|
+
error: mutation.error,
|
|
1669
|
+
mutationApplied: mutation.mutationApplied,
|
|
1670
|
+
maintenanceError: mutation.maintenanceError
|
|
1671
|
+
};
|
|
1672
|
+
}
|
|
1673
|
+
return {
|
|
1674
|
+
kind: mutation.value ?? "notFound",
|
|
1675
|
+
mutationApplied: mutation.mutationApplied,
|
|
1676
|
+
maintenanceError: mutation.maintenanceError
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
__name(deletePersistedSessionWithLease, "deletePersistedSessionWithLease");
|
|
1680
|
+
async function deleteDaemonSessions(params) {
|
|
1681
|
+
const {
|
|
1682
|
+
sessionIds,
|
|
1683
|
+
service,
|
|
1684
|
+
bridge,
|
|
1685
|
+
coordinator,
|
|
1686
|
+
coordinatorLockHeld = false,
|
|
1687
|
+
assertCanMutate,
|
|
1688
|
+
onError
|
|
1689
|
+
} = params;
|
|
1690
|
+
const uniqueSessionIds = [
|
|
1691
|
+
...new Set(sessionIds.map(normalizeSessionIdForLookup))
|
|
1692
|
+
];
|
|
1693
|
+
if (!coordinatorLockHeld) {
|
|
1694
|
+
for (const sessionId of uniqueSessionIds) {
|
|
1695
|
+
coordinator.assertNotTransitioning(sessionId);
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
const results = await Promise.all(
|
|
1699
|
+
uniqueSessionIds.map(async (sessionId) => {
|
|
1700
|
+
try {
|
|
1701
|
+
const mutateSession = /* @__PURE__ */ __name(async () => {
|
|
1702
|
+
assertCanMutate?.();
|
|
1703
|
+
const removePersistedSession = /* @__PURE__ */ __name(async () => {
|
|
1704
|
+
const result = await deletePersistedSessionWithLease(
|
|
1705
|
+
service,
|
|
1706
|
+
sessionId,
|
|
1707
|
+
assertCanMutate
|
|
1708
|
+
);
|
|
1709
|
+
if (result.kind === "error") {
|
|
1710
|
+
onError?.({
|
|
1711
|
+
phase: "remove",
|
|
1712
|
+
sessionId,
|
|
1713
|
+
error: errorMessage(result.error)
|
|
1714
|
+
});
|
|
1715
|
+
return result;
|
|
1716
|
+
}
|
|
1717
|
+
try {
|
|
1718
|
+
if (assertCanMutate) {
|
|
1719
|
+
await bridge.deleteSessionAttachments(sessionId, {
|
|
1720
|
+
assertCanCommit: assertCanMutate
|
|
1721
|
+
});
|
|
1722
|
+
} else {
|
|
1723
|
+
await bridge.deleteSessionAttachments(sessionId);
|
|
1724
|
+
}
|
|
1725
|
+
return result;
|
|
1726
|
+
} catch (error) {
|
|
1727
|
+
onError?.({
|
|
1728
|
+
phase: "delete",
|
|
1729
|
+
sessionId,
|
|
1730
|
+
error: errorMessage(error)
|
|
1731
|
+
});
|
|
1732
|
+
return {
|
|
1733
|
+
kind: "error",
|
|
1734
|
+
error,
|
|
1735
|
+
mutationApplied: result.mutationApplied,
|
|
1736
|
+
maintenanceError: result.maintenanceError
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
}, "removePersistedSession");
|
|
1740
|
+
try {
|
|
1741
|
+
await bridge.closeSession(sessionId);
|
|
1742
|
+
} catch (error) {
|
|
1743
|
+
if (isSessionNotFoundError(error)) {
|
|
1744
|
+
return await removePersistedSession();
|
|
1745
|
+
}
|
|
1746
|
+
onError?.({
|
|
1747
|
+
phase: "close",
|
|
1748
|
+
sessionId,
|
|
1749
|
+
error: errorMessage(error)
|
|
1750
|
+
});
|
|
1751
|
+
return {
|
|
1752
|
+
kind: "error",
|
|
1753
|
+
error,
|
|
1754
|
+
mutationApplied: false
|
|
1755
|
+
};
|
|
1756
|
+
}
|
|
1757
|
+
return await removePersistedSession();
|
|
1758
|
+
}, "mutateSession");
|
|
1759
|
+
return await (coordinatorLockHeld ? mutateSession() : coordinator.runExclusiveMany([sessionId], mutateSession));
|
|
1760
|
+
} catch (error) {
|
|
1761
|
+
if (error instanceof DaemonDrainingError) {
|
|
1762
|
+
throw error;
|
|
1763
|
+
}
|
|
1764
|
+
onError?.({
|
|
1765
|
+
phase: "delete",
|
|
1766
|
+
sessionId,
|
|
1767
|
+
error: errorMessage(error)
|
|
1768
|
+
});
|
|
1769
|
+
return {
|
|
1770
|
+
kind: "error",
|
|
1771
|
+
error,
|
|
1772
|
+
mutationApplied: false
|
|
1773
|
+
};
|
|
1774
|
+
}
|
|
1775
|
+
})
|
|
1776
|
+
);
|
|
1777
|
+
const removed = [];
|
|
1778
|
+
const notFound = [];
|
|
1779
|
+
const errors = [];
|
|
1780
|
+
for (let i = 0; i < results.length; i++) {
|
|
1781
|
+
const sessionId = uniqueSessionIds[i];
|
|
1782
|
+
const result = results[i];
|
|
1783
|
+
if (result.kind === "removed") {
|
|
1784
|
+
removed.push(sessionId);
|
|
1785
|
+
} else if (result.kind === "notFound") {
|
|
1786
|
+
notFound.push(sessionId);
|
|
1787
|
+
} else {
|
|
1788
|
+
errors.push({ sessionId, error: errorMessage(result.error) });
|
|
1789
|
+
}
|
|
1790
|
+
if (result.maintenanceError !== void 0) {
|
|
1791
|
+
errors.push({
|
|
1792
|
+
sessionId,
|
|
1793
|
+
error: "Scheduled task lifecycle update failed."
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
return { removed, notFound, errors };
|
|
1798
|
+
}
|
|
1799
|
+
__name(deleteDaemonSessions, "deleteDaemonSessions");
|
|
1800
|
+
async function deleteDaemonSessionIfOrphan(params) {
|
|
1801
|
+
const { sessionId, service, bridge, coordinator } = params;
|
|
1802
|
+
coordinator.assertNotTransitioning(sessionId);
|
|
1803
|
+
const result = await coordinator.runExclusiveMany([sessionId], async () => {
|
|
1804
|
+
let killed = false;
|
|
1805
|
+
try {
|
|
1806
|
+
killed = await bridge.killSession(sessionId, {
|
|
1807
|
+
requireZeroAttaches: true
|
|
1808
|
+
});
|
|
1809
|
+
} catch (error) {
|
|
1810
|
+
if (!isSessionNotFoundError(error)) throw error;
|
|
1811
|
+
killed = true;
|
|
1812
|
+
}
|
|
1813
|
+
if (!killed) {
|
|
1814
|
+
return void 0;
|
|
1815
|
+
}
|
|
1816
|
+
return deletePersistedSessionWithLease(service, sessionId);
|
|
1817
|
+
});
|
|
1818
|
+
if (result === void 0) {
|
|
1819
|
+
return false;
|
|
1820
|
+
}
|
|
1821
|
+
if (result.kind === "error") {
|
|
1822
|
+
throw result.error;
|
|
1823
|
+
}
|
|
1824
|
+
bridge.markSessionCatalogChanged();
|
|
1825
|
+
return true;
|
|
1826
|
+
}
|
|
1827
|
+
__name(deleteDaemonSessionIfOrphan, "deleteDaemonSessionIfOrphan");
|
|
1828
|
+
async function assertSessionLoadable(workspaceCwd, sessionId, runtimeBaseDir, options = {}) {
|
|
1829
|
+
const service = new SessionService(workspaceCwd, {
|
|
1830
|
+
runtimeBaseDir
|
|
1831
|
+
});
|
|
1832
|
+
const location = await service.getSessionLocation(sessionId);
|
|
1833
|
+
if (location === "archived") {
|
|
1834
|
+
throw new SessionArchivedError(sessionId);
|
|
1835
|
+
}
|
|
1836
|
+
if (location === "conflict") {
|
|
1837
|
+
if (options.allowActiveConflict) {
|
|
1838
|
+
try {
|
|
1839
|
+
await service.findSessionIdIgnoringCase(sessionId);
|
|
1840
|
+
} catch (error) {
|
|
1841
|
+
if (error instanceof SessionIdCaseConflictError && error.reason === "case_conflict" && error.candidateSessionId === sessionId) {
|
|
1842
|
+
return "active";
|
|
1843
|
+
}
|
|
1844
|
+
if (!(error instanceof SessionIdCaseConflictError)) throw error;
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
throw new SessionConflictError(sessionId);
|
|
1848
|
+
}
|
|
1849
|
+
return location;
|
|
1850
|
+
}
|
|
1851
|
+
__name(assertSessionLoadable, "assertSessionLoadable");
|
|
1852
|
+
async function resolveSessionIdForRestore(service, sessionId) {
|
|
1853
|
+
try {
|
|
1854
|
+
return await service.findSessionIdIgnoringCase(sessionId);
|
|
1855
|
+
} catch (error) {
|
|
1856
|
+
if (error instanceof SessionIdCaseConflictError) {
|
|
1857
|
+
if (error.candidateSessionId === sessionId) return sessionId;
|
|
1858
|
+
throw new SessionConflictError(sessionId);
|
|
1859
|
+
}
|
|
1860
|
+
throw error;
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
__name(resolveSessionIdForRestore, "resolveSessionIdForRestore");
|
|
1864
|
+
async function assertSessionRestorable(workspaceCwd, sessionId, requestedSessionId, runtimeBaseDir) {
|
|
1865
|
+
const location = await new SessionService(workspaceCwd, {
|
|
1866
|
+
runtimeBaseDir
|
|
1867
|
+
}).getSessionLocation(sessionId);
|
|
1868
|
+
if (location === "archived") {
|
|
1869
|
+
throw new SessionArchivedError(sessionId);
|
|
1870
|
+
}
|
|
1871
|
+
if (location === "conflict") {
|
|
1872
|
+
if (sessionId !== requestedSessionId) {
|
|
1873
|
+
throw new SessionConflictError(requestedSessionId);
|
|
1874
|
+
}
|
|
1875
|
+
return "active";
|
|
1876
|
+
}
|
|
1877
|
+
return location;
|
|
1878
|
+
}
|
|
1879
|
+
__name(assertSessionRestorable, "assertSessionRestorable");
|
|
1880
|
+
async function assertSessionArchived(workspaceCwd, sessionId, runtimeBaseDir) {
|
|
1881
|
+
const location = await new SessionService(workspaceCwd, {
|
|
1882
|
+
runtimeBaseDir
|
|
1883
|
+
}).getSessionLocation(sessionId);
|
|
1884
|
+
if (location === "active") {
|
|
1885
|
+
throw new SessionNotArchivedError(sessionId);
|
|
1886
|
+
}
|
|
1887
|
+
if (location === "conflict") {
|
|
1888
|
+
throw new SessionConflictError(sessionId);
|
|
1889
|
+
}
|
|
1890
|
+
if (location === void 0) {
|
|
1891
|
+
throw new SessionNotFoundError(sessionId);
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
__name(assertSessionArchived, "assertSessionArchived");
|
|
1895
|
+
function isSessionNotFoundError(err) {
|
|
1896
|
+
return err instanceof SessionNotFoundError || err instanceof Error && err.name === "SessionNotFoundError";
|
|
1897
|
+
}
|
|
1898
|
+
__name(isSessionNotFoundError, "isSessionNotFoundError");
|
|
1899
|
+
function logSessionArchiveResult(action, result) {
|
|
1900
|
+
const changedLabel = action === "archive" ? "archived" : "unarchived";
|
|
1901
|
+
const alreadyLabel = action === "archive" ? "alreadyArchived" : "alreadyActive";
|
|
1902
|
+
const details = [
|
|
1903
|
+
`requested=${result.requested.length} requestedIds=${formatSessionIds(result.requested)}`,
|
|
1904
|
+
`${changedLabel}=${result.changed.length} ${changedLabel}Ids=${formatSessionIds(result.changed)}`,
|
|
1905
|
+
`${alreadyLabel}=${result.already.length} ${alreadyLabel}Ids=${formatSessionIds(result.already)}`,
|
|
1906
|
+
`notFound=${result.notFound.length} notFoundIds=${formatSessionIds(result.notFound)}`,
|
|
1907
|
+
`errors=${result.errors.length} errorIds=${formatSessionErrors(result.errors)}`
|
|
1908
|
+
].join(" ");
|
|
1909
|
+
writeStderrLine(`qwen serve: sessions ${action} result ${details}`);
|
|
1910
|
+
}
|
|
1911
|
+
__name(logSessionArchiveResult, "logSessionArchiveResult");
|
|
1912
|
+
function formatSessionIds(sessionIds) {
|
|
1913
|
+
return `[${sessionIds.map((sessionId) => safeLogValue(sessionId)).join(",")}]`;
|
|
1914
|
+
}
|
|
1915
|
+
__name(formatSessionIds, "formatSessionIds");
|
|
1916
|
+
function formatSessionErrors(errors) {
|
|
1917
|
+
return `[${errors.map(
|
|
1918
|
+
({ sessionId, error }) => `${safeLogValue(sessionId)}:${safeLogValue(errorMessage(error))}`
|
|
1919
|
+
).join(",")}]`;
|
|
1920
|
+
}
|
|
1921
|
+
__name(formatSessionErrors, "formatSessionErrors");
|
|
1922
|
+
function errorMessage(error) {
|
|
1923
|
+
return error instanceof Error ? error.message : String(error);
|
|
1924
|
+
}
|
|
1925
|
+
__name(errorMessage, "errorMessage");
|
|
1926
|
+
function logSessionArchiveWarning(message) {
|
|
1927
|
+
writeStderrLine(`qwen serve: ${sanitizeLogLine(message)}`);
|
|
1928
|
+
}
|
|
1929
|
+
__name(logSessionArchiveWarning, "logSessionArchiveWarning");
|
|
1930
|
+
var LOG_LINE_UNSAFE_RE = /[\x00-\x1f\x7f-\x9f\u200b-\u200f\u2028-\u202e\u2066-\u2069\ufeff]/g;
|
|
1931
|
+
function sanitizeLogLine(message) {
|
|
1932
|
+
return message.replace(LOG_LINE_UNSAFE_RE, " ").slice(0, 4096);
|
|
1933
|
+
}
|
|
1934
|
+
__name(sanitizeLogLine, "sanitizeLogLine");
|
|
1935
|
+
async function archiveDaemonSessions(params) {
|
|
1936
|
+
const {
|
|
1937
|
+
sessionIds,
|
|
1938
|
+
service,
|
|
1939
|
+
bridge,
|
|
1940
|
+
coordinator,
|
|
1941
|
+
coordinatorLockHeld = false,
|
|
1942
|
+
resolveConflicts = false,
|
|
1943
|
+
assertCanMutate
|
|
1944
|
+
} = params;
|
|
1945
|
+
const uniqueSessionIds = [
|
|
1946
|
+
...new Set(sessionIds.map(normalizeSessionIdForLookup))
|
|
1947
|
+
];
|
|
1948
|
+
if (!coordinatorLockHeld) {
|
|
1949
|
+
for (const sessionId of uniqueSessionIds) {
|
|
1950
|
+
coordinator.assertNotTransitioning(sessionId);
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
const results = await Promise.all(
|
|
1954
|
+
uniqueSessionIds.map(async (sessionId) => {
|
|
1955
|
+
try {
|
|
1956
|
+
const mutateSession = /* @__PURE__ */ __name(async () => {
|
|
1957
|
+
assertCanMutate?.();
|
|
1958
|
+
try {
|
|
1959
|
+
await bridge.closeSession(sessionId, void 0, {
|
|
1960
|
+
requireAgentClose: true
|
|
1961
|
+
});
|
|
1962
|
+
} catch (error) {
|
|
1963
|
+
if (!isSessionNotFoundError(error)) {
|
|
1964
|
+
return {
|
|
1965
|
+
kind: "error",
|
|
1966
|
+
error,
|
|
1967
|
+
mutationApplied: false
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
const initialLocation = await classifySessionLocation(
|
|
1972
|
+
service,
|
|
1973
|
+
sessionId
|
|
1974
|
+
);
|
|
1975
|
+
if (initialLocation === void 0) {
|
|
1976
|
+
return { kind: "notFound", mutationApplied: false };
|
|
1977
|
+
}
|
|
1978
|
+
if (initialLocation === "archived") {
|
|
1979
|
+
let maintenanceError;
|
|
1980
|
+
try {
|
|
1981
|
+
await updateScheduledTaskForMaintenance(
|
|
1982
|
+
service,
|
|
1983
|
+
sessionId,
|
|
1984
|
+
"archive",
|
|
1985
|
+
assertCanMutate
|
|
1986
|
+
);
|
|
1987
|
+
} catch (error) {
|
|
1988
|
+
maintenanceError = error;
|
|
1989
|
+
logSessionArchiveWarning(
|
|
1990
|
+
`scheduled task lifecycle update failed action=archive workspace=${safeLogValue(
|
|
1991
|
+
service.getProjectRoot()
|
|
1992
|
+
)} session=${safeLogValue(sessionId)} error=${safeLogValue(
|
|
1993
|
+
errorMessage(error)
|
|
1994
|
+
)}`
|
|
1995
|
+
);
|
|
1996
|
+
}
|
|
1997
|
+
return {
|
|
1998
|
+
kind: "alreadyArchived",
|
|
1999
|
+
mutationApplied: false,
|
|
2000
|
+
maintenanceError
|
|
2001
|
+
};
|
|
2002
|
+
}
|
|
2003
|
+
if (initialLocation === "conflict" && !resolveConflicts) {
|
|
2004
|
+
return {
|
|
2005
|
+
kind: "error",
|
|
2006
|
+
error: sessionLocationError(sessionId),
|
|
2007
|
+
mutationApplied: false
|
|
2008
|
+
};
|
|
2009
|
+
}
|
|
2010
|
+
const mutation = await runWithDaemonWriterLease({
|
|
2011
|
+
action: "archive",
|
|
2012
|
+
sessionId,
|
|
2013
|
+
service,
|
|
2014
|
+
mutate: /* @__PURE__ */ __name(async (assertOwnedAndUnchanged) => {
|
|
2015
|
+
const lockedLocation = await classifySessionLocation(
|
|
2016
|
+
service,
|
|
2017
|
+
sessionId
|
|
2018
|
+
);
|
|
2019
|
+
if (lockedLocation === void 0) {
|
|
2020
|
+
return {
|
|
2021
|
+
value: "notFound",
|
|
2022
|
+
mutationApplied: false
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
2025
|
+
if (lockedLocation === "archived") {
|
|
2026
|
+
return {
|
|
2027
|
+
value: "alreadyArchived",
|
|
2028
|
+
mutationApplied: false
|
|
2029
|
+
};
|
|
2030
|
+
}
|
|
2031
|
+
if (lockedLocation === "conflict" && !resolveConflicts) {
|
|
2032
|
+
throw sessionLocationError(sessionId);
|
|
2033
|
+
}
|
|
2034
|
+
const result = await service.archiveSessions([sessionId], {
|
|
2035
|
+
resolveConflicts,
|
|
2036
|
+
assertStorageUnchanged: assertOwnedAndUnchanged,
|
|
2037
|
+
assertCanMutate
|
|
2038
|
+
});
|
|
2039
|
+
if (result.errors[0]) throw result.errors[0].error;
|
|
2040
|
+
if (result.archived.length > 0) {
|
|
2041
|
+
return {
|
|
2042
|
+
value: result.resolvedConflicts.length ? "resolvedConflict" : "archived",
|
|
2043
|
+
mutationApplied: true
|
|
2044
|
+
};
|
|
2045
|
+
}
|
|
2046
|
+
return {
|
|
2047
|
+
value: result.alreadyArchived.length > 0 ? "alreadyArchived" : "notFound",
|
|
2048
|
+
mutationApplied: false
|
|
2049
|
+
};
|
|
2050
|
+
}, "mutate"),
|
|
2051
|
+
mutationAppliedAfterError: /* @__PURE__ */ __name(async () => await classifySessionLocation(service, sessionId) === "archived", "mutationAppliedAfterError"),
|
|
2052
|
+
afterMutationApplied: /* @__PURE__ */ __name(async () => {
|
|
2053
|
+
assertCanMutate?.();
|
|
2054
|
+
await updateScheduledTaskForMaintenance(
|
|
2055
|
+
service,
|
|
2056
|
+
sessionId,
|
|
2057
|
+
"archive",
|
|
2058
|
+
assertCanMutate
|
|
2059
|
+
);
|
|
2060
|
+
}, "afterMutationApplied")
|
|
2061
|
+
});
|
|
2062
|
+
if (mutation.error !== void 0) {
|
|
2063
|
+
return {
|
|
2064
|
+
kind: "error",
|
|
2065
|
+
error: mutation.error,
|
|
2066
|
+
mutationApplied: mutation.mutationApplied,
|
|
2067
|
+
maintenanceError: mutation.maintenanceError
|
|
2068
|
+
};
|
|
2069
|
+
}
|
|
2070
|
+
return {
|
|
2071
|
+
kind: mutation.value ?? "notFound",
|
|
2072
|
+
mutationApplied: mutation.mutationApplied,
|
|
2073
|
+
maintenanceError: mutation.maintenanceError
|
|
2074
|
+
};
|
|
2075
|
+
}, "mutateSession");
|
|
2076
|
+
return await (coordinatorLockHeld ? mutateSession() : coordinator.runExclusiveMany([sessionId], mutateSession));
|
|
2077
|
+
} catch (error) {
|
|
2078
|
+
if (error instanceof DaemonDrainingError) {
|
|
2079
|
+
throw error;
|
|
2080
|
+
}
|
|
2081
|
+
return {
|
|
2082
|
+
kind: "error",
|
|
2083
|
+
error,
|
|
2084
|
+
mutationApplied: false,
|
|
2085
|
+
maintenanceError: void 0
|
|
2086
|
+
};
|
|
2087
|
+
}
|
|
2088
|
+
})
|
|
2089
|
+
);
|
|
2090
|
+
const archived = [];
|
|
2091
|
+
const alreadyArchived = [];
|
|
2092
|
+
const resolvedConflicts = [];
|
|
2093
|
+
const notFound = [];
|
|
2094
|
+
const errors = [];
|
|
2095
|
+
for (let i = 0; i < results.length; i++) {
|
|
2096
|
+
const sessionId = uniqueSessionIds[i];
|
|
2097
|
+
const result = results[i];
|
|
2098
|
+
if (result.kind === "archived") archived.push(sessionId);
|
|
2099
|
+
else if (result.kind === "resolvedConflict") {
|
|
2100
|
+
archived.push(sessionId);
|
|
2101
|
+
resolvedConflicts.push(sessionId);
|
|
2102
|
+
} else if (result.kind === "alreadyArchived") {
|
|
2103
|
+
alreadyArchived.push(sessionId);
|
|
2104
|
+
} else if (result.kind === "notFound") notFound.push(sessionId);
|
|
2105
|
+
else errors.push({ sessionId, error: result.error });
|
|
2106
|
+
if ("maintenanceError" in result && result.maintenanceError !== void 0) {
|
|
2107
|
+
errors.push({ sessionId, error: result.maintenanceError });
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
logSessionArchiveResult("archive", {
|
|
2111
|
+
requested: uniqueSessionIds,
|
|
2112
|
+
changed: archived,
|
|
2113
|
+
already: alreadyArchived,
|
|
2114
|
+
notFound,
|
|
2115
|
+
errors
|
|
2116
|
+
});
|
|
2117
|
+
return {
|
|
2118
|
+
archived,
|
|
2119
|
+
alreadyArchived,
|
|
2120
|
+
resolvedConflicts,
|
|
2121
|
+
notFound,
|
|
2122
|
+
errors
|
|
2123
|
+
};
|
|
2124
|
+
}
|
|
2125
|
+
__name(archiveDaemonSessions, "archiveDaemonSessions");
|
|
2126
|
+
async function unarchiveDaemonSessions(params) {
|
|
2127
|
+
const {
|
|
2128
|
+
sessionIds,
|
|
2129
|
+
service,
|
|
2130
|
+
coordinator,
|
|
2131
|
+
coordinatorLockHeld = false,
|
|
2132
|
+
resolveConflicts = false,
|
|
2133
|
+
assertCanMutate
|
|
2134
|
+
} = params;
|
|
2135
|
+
const uniqueSessionIds = [
|
|
2136
|
+
...new Set(sessionIds.map(normalizeSessionIdForLookup))
|
|
2137
|
+
];
|
|
2138
|
+
if (!coordinatorLockHeld) {
|
|
2139
|
+
for (const sessionId of uniqueSessionIds) {
|
|
2140
|
+
coordinator.assertNotTransitioning(sessionId);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
const results = await Promise.all(
|
|
2144
|
+
uniqueSessionIds.map(async (sessionId) => {
|
|
2145
|
+
try {
|
|
2146
|
+
const mutateSession = /* @__PURE__ */ __name(async () => {
|
|
2147
|
+
assertCanMutate?.();
|
|
2148
|
+
const initialLocation = await classifySessionLocation(
|
|
2149
|
+
service,
|
|
2150
|
+
sessionId
|
|
2151
|
+
);
|
|
2152
|
+
if (initialLocation === void 0) {
|
|
2153
|
+
return { kind: "notFound", mutationApplied: false };
|
|
2154
|
+
}
|
|
2155
|
+
if (initialLocation === "active") {
|
|
2156
|
+
let maintenanceError;
|
|
2157
|
+
try {
|
|
2158
|
+
await updateScheduledTaskForMaintenance(
|
|
2159
|
+
service,
|
|
2160
|
+
sessionId,
|
|
2161
|
+
"unarchive",
|
|
2162
|
+
assertCanMutate
|
|
2163
|
+
);
|
|
2164
|
+
} catch (error) {
|
|
2165
|
+
maintenanceError = error;
|
|
2166
|
+
logSessionArchiveWarning(
|
|
2167
|
+
`scheduled task lifecycle update failed action=unarchive workspace=${safeLogValue(
|
|
2168
|
+
service.getProjectRoot()
|
|
2169
|
+
)} session=${safeLogValue(sessionId)} error=${safeLogValue(
|
|
2170
|
+
errorMessage(error)
|
|
2171
|
+
)}`
|
|
2172
|
+
);
|
|
2173
|
+
}
|
|
2174
|
+
return {
|
|
2175
|
+
kind: "alreadyActive",
|
|
2176
|
+
mutationApplied: false,
|
|
2177
|
+
maintenanceError
|
|
2178
|
+
};
|
|
2179
|
+
}
|
|
2180
|
+
if (initialLocation === "conflict" && !resolveConflicts) {
|
|
2181
|
+
return {
|
|
2182
|
+
kind: "error",
|
|
2183
|
+
error: sessionLocationError(sessionId),
|
|
2184
|
+
mutationApplied: false
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
const mutation = await runWithDaemonWriterLease({
|
|
2188
|
+
action: "unarchive",
|
|
2189
|
+
sessionId,
|
|
2190
|
+
service,
|
|
2191
|
+
mutate: /* @__PURE__ */ __name(async (assertOwnedAndUnchanged) => {
|
|
2192
|
+
const lockedLocation = await classifySessionLocation(
|
|
2193
|
+
service,
|
|
2194
|
+
sessionId
|
|
2195
|
+
);
|
|
2196
|
+
if (lockedLocation === void 0) {
|
|
2197
|
+
return {
|
|
2198
|
+
value: "notFound",
|
|
2199
|
+
mutationApplied: false
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
if (lockedLocation === "active") {
|
|
2203
|
+
return {
|
|
2204
|
+
value: "alreadyActive",
|
|
2205
|
+
mutationApplied: false
|
|
2206
|
+
};
|
|
2207
|
+
}
|
|
2208
|
+
if (lockedLocation === "conflict" && !resolveConflicts) {
|
|
2209
|
+
throw sessionLocationError(sessionId);
|
|
2210
|
+
}
|
|
2211
|
+
const result = await service.unarchiveSessions([sessionId], {
|
|
2212
|
+
resolveConflicts,
|
|
2213
|
+
assertStorageUnchanged: assertOwnedAndUnchanged,
|
|
2214
|
+
assertCanMutate
|
|
2215
|
+
});
|
|
2216
|
+
if (result.errors[0]) throw result.errors[0].error;
|
|
2217
|
+
if (result.unarchived.length > 0) {
|
|
2218
|
+
return {
|
|
2219
|
+
value: result.resolvedConflicts.length ? "resolvedConflict" : "unarchived",
|
|
2220
|
+
mutationApplied: true
|
|
2221
|
+
};
|
|
2222
|
+
}
|
|
2223
|
+
return {
|
|
2224
|
+
value: result.alreadyActive.length > 0 ? "alreadyActive" : "notFound",
|
|
2225
|
+
mutationApplied: false
|
|
2226
|
+
};
|
|
2227
|
+
}, "mutate"),
|
|
2228
|
+
mutationAppliedAfterError: /* @__PURE__ */ __name(async () => await classifySessionLocation(service, sessionId) === "active", "mutationAppliedAfterError"),
|
|
2229
|
+
afterMutationApplied: /* @__PURE__ */ __name(async () => {
|
|
2230
|
+
assertCanMutate?.();
|
|
2231
|
+
await updateScheduledTaskForMaintenance(
|
|
2232
|
+
service,
|
|
2233
|
+
sessionId,
|
|
2234
|
+
"unarchive",
|
|
2235
|
+
assertCanMutate
|
|
2236
|
+
);
|
|
2237
|
+
}, "afterMutationApplied")
|
|
2238
|
+
});
|
|
2239
|
+
if (mutation.error !== void 0) {
|
|
2240
|
+
return {
|
|
2241
|
+
kind: "error",
|
|
2242
|
+
error: mutation.error,
|
|
2243
|
+
mutationApplied: mutation.mutationApplied,
|
|
2244
|
+
maintenanceError: mutation.maintenanceError
|
|
2245
|
+
};
|
|
2246
|
+
}
|
|
2247
|
+
return {
|
|
2248
|
+
kind: mutation.value ?? "notFound",
|
|
2249
|
+
mutationApplied: mutation.mutationApplied,
|
|
2250
|
+
maintenanceError: mutation.maintenanceError
|
|
2251
|
+
};
|
|
2252
|
+
}, "mutateSession");
|
|
2253
|
+
return await (coordinatorLockHeld ? mutateSession() : coordinator.runExclusiveMany([sessionId], mutateSession));
|
|
2254
|
+
} catch (error) {
|
|
2255
|
+
if (error instanceof DaemonDrainingError) {
|
|
2256
|
+
throw error;
|
|
2257
|
+
}
|
|
2258
|
+
return {
|
|
2259
|
+
kind: "error",
|
|
2260
|
+
error,
|
|
2261
|
+
mutationApplied: false,
|
|
2262
|
+
maintenanceError: void 0
|
|
2263
|
+
};
|
|
2264
|
+
}
|
|
2265
|
+
})
|
|
2266
|
+
);
|
|
2267
|
+
const unarchived = [];
|
|
2268
|
+
const alreadyActive = [];
|
|
2269
|
+
const resolvedConflicts = [];
|
|
2270
|
+
const notFound = [];
|
|
2271
|
+
const errors = [];
|
|
2272
|
+
for (let i = 0; i < results.length; i++) {
|
|
2273
|
+
const sessionId = uniqueSessionIds[i];
|
|
2274
|
+
const result = results[i];
|
|
2275
|
+
if (result.kind === "unarchived") unarchived.push(sessionId);
|
|
2276
|
+
else if (result.kind === "resolvedConflict") {
|
|
2277
|
+
unarchived.push(sessionId);
|
|
2278
|
+
resolvedConflicts.push(sessionId);
|
|
2279
|
+
} else if (result.kind === "alreadyActive") alreadyActive.push(sessionId);
|
|
2280
|
+
else if (result.kind === "notFound") notFound.push(sessionId);
|
|
2281
|
+
else errors.push({ sessionId, error: result.error });
|
|
2282
|
+
if (result.maintenanceError !== void 0) {
|
|
2283
|
+
errors.push({ sessionId, error: result.maintenanceError });
|
|
2284
|
+
}
|
|
2285
|
+
}
|
|
2286
|
+
logSessionArchiveResult("unarchive", {
|
|
2287
|
+
requested: uniqueSessionIds,
|
|
2288
|
+
changed: unarchived,
|
|
2289
|
+
already: alreadyActive,
|
|
2290
|
+
notFound,
|
|
2291
|
+
errors
|
|
2292
|
+
});
|
|
2293
|
+
return {
|
|
2294
|
+
unarchived,
|
|
2295
|
+
alreadyActive,
|
|
2296
|
+
resolvedConflicts,
|
|
2297
|
+
notFound,
|
|
2298
|
+
errors
|
|
2299
|
+
};
|
|
2300
|
+
}
|
|
2301
|
+
__name(unarchiveDaemonSessions, "unarchiveDaemonSessions");
|
|
2302
|
+
|
|
2303
|
+
// packages/cli/src/serve/server/aone-mrs.ts
|
|
2304
|
+
init_esbuild_shims();
|
|
2305
|
+
import { execFile } from "node:child_process";
|
|
2306
|
+
import { promisify } from "node:util";
|
|
2307
|
+
var execFileAsync = promisify(execFile);
|
|
2308
|
+
var a1AvailabilityVerified = false;
|
|
2309
|
+
var defaultA1Exec = /* @__PURE__ */ __name(async (args, options) => {
|
|
2310
|
+
const { stdout } = await execFileAsync("a1", [...args], {
|
|
2311
|
+
...options,
|
|
2312
|
+
encoding: "utf8",
|
|
2313
|
+
windowsHide: true
|
|
2314
|
+
// No env override: a1 authenticates through its own `a1 auth login`
|
|
2315
|
+
// config (review precedent); there is no A1_* token convention.
|
|
2316
|
+
});
|
|
2317
|
+
return { stdout };
|
|
2318
|
+
}, "defaultA1Exec");
|
|
2319
|
+
var a1Exec = defaultA1Exec;
|
|
2320
|
+
var A1_TIMEOUT_MS = 2e4;
|
|
2321
|
+
var A1_MAX_BUFFER = 16 * 1024 * 1024;
|
|
2322
|
+
var GIT_ORIGIN_TIMEOUT_MS = 5e3;
|
|
2323
|
+
var A1_VERSION_TIMEOUT_MS = 1e4;
|
|
2324
|
+
var A1_ERROR_MESSAGE_MAX = 512;
|
|
2325
|
+
var AONE_DETAIL_URL_HOST = "code.alibaba-inc.com";
|
|
2326
|
+
var AONE_MR_LIST_PAGE_SIZE = 20;
|
|
2327
|
+
var AONE_BACKFILL_PAGES_PER_STATE = 3;
|
|
2328
|
+
var AONE_MAX_MR_VIEW_CALLS_PER_RUN = 25;
|
|
2329
|
+
var AoneCliUnavailableError = class extends Error {
|
|
2330
|
+
static {
|
|
2331
|
+
__name(this, "AoneCliUnavailableError");
|
|
2332
|
+
}
|
|
2333
|
+
};
|
|
2334
|
+
var AoneCommandError = class extends Error {
|
|
2335
|
+
static {
|
|
2336
|
+
__name(this, "AoneCommandError");
|
|
2337
|
+
}
|
|
2338
|
+
};
|
|
2339
|
+
function mapAoneMrState(raw) {
|
|
2340
|
+
switch (raw?.toLowerCase()) {
|
|
2341
|
+
case "merged":
|
|
2342
|
+
return "merged";
|
|
2343
|
+
case "closed":
|
|
2344
|
+
return "closed";
|
|
2345
|
+
default:
|
|
2346
|
+
return "open";
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
__name(mapAoneMrState, "mapAoneMrState");
|
|
2350
|
+
async function resolveAoneWorkspaceRepo(workspaceCwd, env) {
|
|
2351
|
+
const gitRoot = findGitRoot(workspaceCwd);
|
|
2352
|
+
if (!gitRoot) return void 0;
|
|
2353
|
+
let origin;
|
|
2354
|
+
try {
|
|
2355
|
+
({ stdout: origin } = await execFileAsync(
|
|
2356
|
+
"git",
|
|
2357
|
+
["remote", "get-url", "origin"],
|
|
2358
|
+
{
|
|
2359
|
+
cwd: gitRoot,
|
|
2360
|
+
timeout: GIT_ORIGIN_TIMEOUT_MS,
|
|
2361
|
+
encoding: "utf8",
|
|
2362
|
+
windowsHide: true,
|
|
2363
|
+
// Same sanitization as every sibling git spawn in these paths —
|
|
2364
|
+
// an inherited GIT_DIR/GIT_CONFIG_* would resolve origin against
|
|
2365
|
+
// a different repository despite the cwd.
|
|
2366
|
+
env: gitEnv(env)
|
|
2367
|
+
}
|
|
2368
|
+
));
|
|
2369
|
+
} catch {
|
|
2370
|
+
return void 0;
|
|
2371
|
+
}
|
|
2372
|
+
const identity = parseRemoteUrl(origin.trim());
|
|
2373
|
+
if (!identity || !isAoneCanonicalHost(identity.host)) return void 0;
|
|
2374
|
+
return { gitRoot, repoPath: identity.groupPath };
|
|
2375
|
+
}
|
|
2376
|
+
__name(resolveAoneWorkspaceRepo, "resolveAoneWorkspaceRepo");
|
|
2377
|
+
function isAoneDetailUrlForRepo(repoPath, number, url) {
|
|
2378
|
+
return canonicalSessionPrUrl(url) === canonicalSessionPrUrl(
|
|
2379
|
+
`https://${AONE_DETAIL_URL_HOST}/${repoPath}/codereview/${number}`
|
|
2380
|
+
);
|
|
2381
|
+
}
|
|
2382
|
+
__name(isAoneDetailUrlForRepo, "isAoneDetailUrlForRepo");
|
|
2383
|
+
function isA1ErrorObject(value) {
|
|
2384
|
+
return typeof value === "object" && value !== null && value["schemaVersion"] === "a1.error/v1";
|
|
2385
|
+
}
|
|
2386
|
+
__name(isA1ErrorObject, "isA1ErrorObject");
|
|
2387
|
+
function a1ErrorMessage(error) {
|
|
2388
|
+
if (error?.killed === true) {
|
|
2389
|
+
return `a1 timed out after ${A1_TIMEOUT_MS / 1e3}s`;
|
|
2390
|
+
}
|
|
2391
|
+
const stderr = error?.stderr;
|
|
2392
|
+
const raw = typeof stderr === "string" && stderr.trim() ? stderr : error instanceof Error ? error.message : String(error);
|
|
2393
|
+
return raw.replace(/\s+/g, " ").trim().slice(0, A1_ERROR_MESSAGE_MAX);
|
|
2394
|
+
}
|
|
2395
|
+
__name(a1ErrorMessage, "a1ErrorMessage");
|
|
2396
|
+
function assertA1Version(stdout) {
|
|
2397
|
+
const triple = parseA1Version(stdout);
|
|
2398
|
+
const floor = parseA1Version(A1_MIN_VERSION);
|
|
2399
|
+
if (triple && floor && !a1VersionAtLeast(triple, floor)) {
|
|
2400
|
+
throw new AoneCliUnavailableError(
|
|
2401
|
+
`a1 ${A1_MIN_VERSION}+ is required (found ${stdout.trim().split("\n")[0]}); upgrade from https://code.alibaba-inc.com/aone/a1`
|
|
2402
|
+
);
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
__name(assertA1Version, "assertA1Version");
|
|
2406
|
+
async function runA1Json(args) {
|
|
2407
|
+
let stdout;
|
|
2408
|
+
try {
|
|
2409
|
+
({ stdout } = await a1Exec([...args, "--format", "json"], {
|
|
2410
|
+
timeout: A1_TIMEOUT_MS,
|
|
2411
|
+
maxBuffer: A1_MAX_BUFFER
|
|
2412
|
+
}));
|
|
2413
|
+
} catch (error) {
|
|
2414
|
+
if (error.code === "ENOENT") {
|
|
2415
|
+
throw new AoneCliUnavailableError(
|
|
2416
|
+
"a1 CLI not installed; install it and run `a1 auth login`"
|
|
2417
|
+
);
|
|
2418
|
+
}
|
|
2419
|
+
const errorStdout = error?.stdout;
|
|
2420
|
+
if (typeof errorStdout === "string" && errorStdout.trim()) {
|
|
2421
|
+
try {
|
|
2422
|
+
const parsed2 = JSON.parse(errorStdout);
|
|
2423
|
+
if (isA1ErrorObject(parsed2)) {
|
|
2424
|
+
throw new AoneCommandError(parsed2.message || "a1 command failed");
|
|
2425
|
+
}
|
|
2426
|
+
} catch (parseError) {
|
|
2427
|
+
if (parseError instanceof AoneCommandError) throw parseError;
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
throw new AoneCommandError(a1ErrorMessage(error));
|
|
2431
|
+
}
|
|
2432
|
+
let parsed;
|
|
2433
|
+
try {
|
|
2434
|
+
parsed = JSON.parse(stdout);
|
|
2435
|
+
} catch {
|
|
2436
|
+
throw new AoneCommandError("a1 returned non-JSON output");
|
|
2437
|
+
}
|
|
2438
|
+
if (isA1ErrorObject(parsed)) {
|
|
2439
|
+
throw new AoneCommandError(parsed.message || "a1 command failed");
|
|
2440
|
+
}
|
|
2441
|
+
return parsed;
|
|
2442
|
+
}
|
|
2443
|
+
__name(runA1Json, "runA1Json");
|
|
2444
|
+
async function checkA1Available() {
|
|
2445
|
+
if (a1AvailabilityVerified) return;
|
|
2446
|
+
let stdout;
|
|
2447
|
+
try {
|
|
2448
|
+
({ stdout } = await a1Exec(["--version"], {
|
|
2449
|
+
timeout: A1_VERSION_TIMEOUT_MS
|
|
2450
|
+
}));
|
|
2451
|
+
} catch (error) {
|
|
2452
|
+
if (error.code === "ENOENT") {
|
|
2453
|
+
throw new AoneCliUnavailableError(
|
|
2454
|
+
"a1 CLI not installed; install it and run `a1 auth login`"
|
|
2455
|
+
);
|
|
2456
|
+
}
|
|
2457
|
+
return;
|
|
2458
|
+
}
|
|
2459
|
+
assertA1Version(stdout);
|
|
2460
|
+
a1AvailabilityVerified = true;
|
|
2461
|
+
}
|
|
2462
|
+
__name(checkA1Available, "checkA1Available");
|
|
2463
|
+
function parseAoneMrListPage(raw) {
|
|
2464
|
+
if (!Array.isArray(raw)) {
|
|
2465
|
+
throw new AoneCommandError("unexpected a1 mr list output");
|
|
2466
|
+
}
|
|
2467
|
+
const entries = [];
|
|
2468
|
+
for (const item of raw) {
|
|
2469
|
+
if (item === null || typeof item !== "object") continue;
|
|
2470
|
+
const v = item;
|
|
2471
|
+
if (typeof v["id"] !== "number" || !Number.isInteger(v["id"]) || v["id"] <= 0) {
|
|
2472
|
+
continue;
|
|
2473
|
+
}
|
|
2474
|
+
entries.push({
|
|
2475
|
+
number: v["id"],
|
|
2476
|
+
headRefName: typeof v["sourceBranch"] === "string" ? v["sourceBranch"] : "",
|
|
2477
|
+
state: mapAoneMrState(
|
|
2478
|
+
typeof v["state"] === "string" ? v["state"] : void 0
|
|
2479
|
+
)
|
|
2480
|
+
});
|
|
2481
|
+
}
|
|
2482
|
+
return entries;
|
|
2483
|
+
}
|
|
2484
|
+
__name(parseAoneMrListPage, "parseAoneMrListPage");
|
|
2485
|
+
async function listAoneMergeRequests(repoPath, options) {
|
|
2486
|
+
await checkA1Available();
|
|
2487
|
+
const entries = [];
|
|
2488
|
+
const pages = Math.max(options.pages, 1);
|
|
2489
|
+
for (let page = 1; page <= pages; page++) {
|
|
2490
|
+
const parsed = await runA1Json([
|
|
2491
|
+
"repo",
|
|
2492
|
+
"mr",
|
|
2493
|
+
"list",
|
|
2494
|
+
"--repo",
|
|
2495
|
+
repoPath,
|
|
2496
|
+
"--state",
|
|
2497
|
+
options.state,
|
|
2498
|
+
"--page",
|
|
2499
|
+
String(page)
|
|
2500
|
+
]);
|
|
2501
|
+
const pageEntries = parseAoneMrListPage(parsed);
|
|
2502
|
+
entries.push(...pageEntries);
|
|
2503
|
+
if (Array.isArray(parsed) && parsed.length < AONE_MR_LIST_PAGE_SIZE) {
|
|
2504
|
+
break;
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
return entries;
|
|
2508
|
+
}
|
|
2509
|
+
__name(listAoneMergeRequests, "listAoneMergeRequests");
|
|
2510
|
+
function hasControlCharacter(value) {
|
|
2511
|
+
return Array.from(value).some((character) => {
|
|
2512
|
+
const code = character.charCodeAt(0);
|
|
2513
|
+
return code <= 31 || code === 127;
|
|
2514
|
+
});
|
|
2515
|
+
}
|
|
2516
|
+
__name(hasControlCharacter, "hasControlCharacter");
|
|
2517
|
+
function parseAoneMrView(raw, id) {
|
|
2518
|
+
const mr = raw !== null && typeof raw === "object" ? raw["mergeRequest"] : void 0;
|
|
2519
|
+
if (mr === null || typeof mr !== "object") {
|
|
2520
|
+
throw new AoneCommandError("unexpected a1 mr view output");
|
|
2521
|
+
}
|
|
2522
|
+
const v = mr;
|
|
2523
|
+
const url = typeof v["detailUrl"] === "string" ? v["detailUrl"] : "";
|
|
2524
|
+
if (!/^https?:\/\//i.test(url)) {
|
|
2525
|
+
throw new AoneCommandError("a1 mr view returned no detailUrl");
|
|
2526
|
+
}
|
|
2527
|
+
if (url.length > SESSION_PR_URL_MAX_LENGTH) {
|
|
2528
|
+
throw new AoneCommandError("a1 mr view returned an oversized detailUrl");
|
|
2529
|
+
}
|
|
2530
|
+
if (hasControlCharacter(url)) {
|
|
2531
|
+
throw new AoneCommandError("a1 mr view returned a malformed detailUrl");
|
|
2532
|
+
}
|
|
2533
|
+
return {
|
|
2534
|
+
number: id,
|
|
2535
|
+
url,
|
|
2536
|
+
state: mapAoneMrState(
|
|
2537
|
+
typeof v["state"] === "string" ? v["state"] : void 0
|
|
2538
|
+
)
|
|
2539
|
+
};
|
|
2540
|
+
}
|
|
2541
|
+
__name(parseAoneMrView, "parseAoneMrView");
|
|
2542
|
+
async function viewAoneMergeRequest(repoPath, id) {
|
|
2543
|
+
await checkA1Available();
|
|
2544
|
+
const parsed = await runA1Json([
|
|
2545
|
+
"repo",
|
|
2546
|
+
"mr",
|
|
2547
|
+
"view",
|
|
2548
|
+
String(id),
|
|
2549
|
+
"--repo",
|
|
2550
|
+
repoPath
|
|
2551
|
+
]);
|
|
2552
|
+
return parseAoneMrView(parsed, id);
|
|
2553
|
+
}
|
|
2554
|
+
__name(viewAoneMergeRequest, "viewAoneMergeRequest");
|
|
2555
|
+
var defaultAoneMrBackend = {
|
|
2556
|
+
list: /* @__PURE__ */ __name((repoPath, state, pages) => listAoneMergeRequests(repoPath, { state, pages }), "list"),
|
|
2557
|
+
view: viewAoneMergeRequest
|
|
2558
|
+
};
|
|
2559
|
+
|
|
2560
|
+
export {
|
|
2561
|
+
createSessionOrganizationService,
|
|
2562
|
+
laterActivityTimestamp,
|
|
2563
|
+
invalidateWorkspaceSessionListCache,
|
|
2564
|
+
InvalidCursorError,
|
|
2565
|
+
listWorkspaceSessionsForResponse,
|
|
2566
|
+
listLiveWorkspaceSessionsForResponse,
|
|
2567
|
+
getWorkspaceSessionInfoForResponse,
|
|
2568
|
+
parseSessionPageSizeQuery,
|
|
2569
|
+
runWithWorkspaceRuntimeStorage,
|
|
2570
|
+
createWorkspaceRuntimeSessionService,
|
|
2571
|
+
DaemonDrainingError,
|
|
2572
|
+
SessionArchiveCoordinator,
|
|
2573
|
+
deleteDaemonSessions,
|
|
2574
|
+
deleteDaemonSessionIfOrphan,
|
|
2575
|
+
assertSessionLoadable,
|
|
2576
|
+
resolveSessionIdForRestore,
|
|
2577
|
+
assertSessionRestorable,
|
|
2578
|
+
assertSessionArchived,
|
|
2579
|
+
logSessionArchiveWarning,
|
|
2580
|
+
archiveDaemonSessions,
|
|
2581
|
+
unarchiveDaemonSessions,
|
|
2582
|
+
AONE_BACKFILL_PAGES_PER_STATE,
|
|
2583
|
+
AONE_MAX_MR_VIEW_CALLS_PER_RUN,
|
|
2584
|
+
resolveAoneWorkspaceRepo,
|
|
2585
|
+
isAoneDetailUrlForRepo,
|
|
2586
|
+
defaultAoneMrBackend
|
|
2587
|
+
};
|
|
2588
|
+
/**
|
|
2589
|
+
* @license
|
|
2590
|
+
* Copyright 2025 Qwen Team
|
|
2591
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
2592
|
+
*/
|
|
2593
|
+
/**
|
|
2594
|
+
* @license
|
|
2595
|
+
* Copyright 2026 Qwen Team
|
|
2596
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
2597
|
+
*/
|