@qwen-code/qwen-code 0.19.7 → 0.19.8-nightly.20260711.0ef3a76bd
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/qc-helper/SKILL.md +1 -0
- package/bundled/qc-helper/docs/configuration/auth.md +40 -55
- package/bundled/qc-helper/docs/configuration/model-providers.md +224 -218
- package/bundled/qc-helper/docs/configuration/settings.md +60 -51
- package/bundled/qc-helper/docs/features/_meta.ts +1 -0
- package/bundled/qc-helper/docs/features/channels/dingtalk.md +12 -2
- package/bundled/qc-helper/docs/features/channels/overview.md +86 -12
- package/bundled/qc-helper/docs/features/channels/plugins.md +1 -0
- package/bundled/qc-helper/docs/features/channels/wecom.md +6 -0
- package/bundled/qc-helper/docs/features/code-review.md +29 -5
- package/bundled/qc-helper/docs/features/commands.md +39 -38
- package/bundled/qc-helper/docs/features/computer-use.md +77 -0
- package/bundled/qc-helper/docs/features/hooks.md +36 -34
- package/bundled/qc-helper/docs/features/memory.md +1 -1
- package/bundled/qc-helper/docs/features/tool-use-summaries.md +19 -23
- package/bundled/qc-helper/docs/integration-vscode.md +2 -2
- package/bundled/qc-helper/docs/qwen-serve.md +40 -16
- package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +1 -1
- package/bundled/review/DESIGN.md +93 -22
- package/bundled/review/SKILL.md +300 -87
- package/chunks/MaxSizedBox-4CX4IQWE.js +70 -0
- package/chunks/{StandaloneSessionPicker-APISIRJZ.js → StandaloneSessionPicker-TYUSSNJ6.js} +79 -77
- package/chunks/{acpAgent-MMOSC2YA.js → acpAgent-5HJEXOPR.js} +978 -205
- package/chunks/agent-PZF5IUUB.js +68 -0
- package/chunks/agent-headless-L4N7V2SG.js +62 -0
- package/chunks/{anthropicContentGenerator-AKYPSZIV.js → anthropicContentGenerator-L73TO3DB.js} +214 -31
- package/chunks/{artifact-tool-D233YJFH.js → artifact-tool-U7IRTLIV.js} +5 -5
- package/chunks/{askUserQuestion-MKL5ZTMC.js → askUserQuestion-2DLUUZZG.js} +6 -6
- package/chunks/bridge-OJL6LD42.js +78 -0
- package/chunks/{ca-HCMB2I54.js → ca-WTQOU43B.js} +91 -6
- package/chunks/{channel-worker-supervisor-CRCVYQIE.js → channel-worker-supervisor-LF66HCVT.js} +137 -13
- package/chunks/{chunk-QQDPRDVW.js → chunk-2HRYPZT5.js} +1 -1
- package/chunks/{chunk-JD33UY7E.js → chunk-2K56LTWA.js} +18 -12
- package/chunks/{chunk-XISQIIT7.js → chunk-2LV4JUNO.js} +4 -4
- package/chunks/chunk-2UU6DW5P.js +296 -0
- package/chunks/{chunk-2PSWA5ID.js → chunk-2X56YUSZ.js} +1 -1
- package/chunks/chunk-2ZQOQZIS.js +71 -0
- package/chunks/{chunk-Q6OEPOOC.js → chunk-3AMVHPEQ.js} +2 -2
- package/chunks/{chunk-4RQIB5UZ.js → chunk-3BTRNWWD.js} +1 -35
- package/chunks/{chunk-SD4N3QJ2.js → chunk-3QZ6GY6A.js} +66 -22
- package/chunks/{chunk-DNV4FSN5.js → chunk-3SJSQXNS.js} +24 -7
- package/chunks/{chunk-VQWAZSXN.js → chunk-3XATDZNP.js} +64 -42
- package/chunks/{chunk-LEIJKM7Q.js → chunk-3ZAIGBCP.js} +2 -2
- package/chunks/{chunk-2NPS2UDH.js → chunk-43JB7OF4.js} +905 -139
- package/chunks/chunk-43XJTCHT.js +444 -0
- package/chunks/{chunk-CNSF3WO5.js → chunk-47HXCVGG.js} +6 -6
- package/chunks/{chunk-VEB3NYSO.js → chunk-4GORAUCN.js} +8 -3
- package/chunks/{chunk-55ZMG67I.js → chunk-4LD5APFL.js} +4 -4
- package/chunks/{chunk-TLWA7TPR.js → chunk-4QLQE5WJ.js} +2 -2
- package/chunks/{chunk-UIDXQNMV.js → chunk-4WGODMVR.js} +1 -1
- package/chunks/{chunk-E6US47KI.js → chunk-4WKAA4KS.js} +1 -1
- package/chunks/{chunk-H6EMK6QK.js → chunk-4ZVWQHEQ.js} +11913 -5207
- package/chunks/{chunk-HW4N45EE.js → chunk-524RQP4Q.js} +29 -8
- package/chunks/{chunk-3GONHQOA.js → chunk-57OAHC2Y.js} +1 -1
- package/chunks/{chunk-B4U2M63D.js → chunk-5DOUQHL3.js} +2104 -14
- package/chunks/{chunk-JZSY4WP3.js → chunk-5DTTYNB7.js} +1 -1
- package/chunks/{chunk-A4BMJM77.js → chunk-5O2XNYP6.js} +1 -0
- package/chunks/{chunk-P7I3D7FM.js → chunk-5OIXALQ5.js} +2 -2
- package/chunks/chunk-5SFOMFG4.js +61 -0
- package/chunks/{chunk-QYNNN7BK.js → chunk-5XUCZNSY.js} +2 -2
- package/chunks/{chunk-FRFFNYUY.js → chunk-65F3I3PM.js} +5 -5
- package/chunks/{chunk-GO6LNQXT.js → chunk-6AM5D4ME.js} +1 -1
- package/chunks/{chunk-BWVQN2HV.js → chunk-6CR73ZIK.js} +7 -7
- package/chunks/{chunk-BJBWRCSK.js → chunk-6H67XLET.js} +1 -1
- package/chunks/{chunk-G7ZG7CA6.js → chunk-6RLROE6G.js} +9 -8
- package/chunks/{chunk-TBAZMRW3.js → chunk-6WH2WCSW.js} +2 -2
- package/chunks/{chunk-ZERZSAZL.js → chunk-75DOP5OR.js} +2 -2
- package/chunks/{chunk-FTFXDZYI.js → chunk-7KZNLIAI.js} +47 -31
- package/chunks/{chunk-HJ3GX3Y3.js → chunk-7PW5CTVR.js} +32 -18
- package/chunks/{chunk-NQKLOAVJ.js → chunk-7R3CZA3Q.js} +2 -2
- package/chunks/{chunk-SAIILPQ3.js → chunk-7X7TEQNX.js} +5 -3
- package/chunks/{chunk-IRJWHZWU.js → chunk-A4UCTEB5.js} +2 -2
- package/chunks/{chunk-XSAFNANZ.js → chunk-AHUEU3BP.js} +14 -14
- package/chunks/{chunk-X3YGAX7V.js → chunk-B5ORX3FG.js} +4 -2
- package/chunks/{chunk-ZN5T4BHI.js → chunk-BPZALHVR.js} +2 -2
- package/chunks/{chunk-IJRL2S3D.js → chunk-BZIFY5TF.js} +6 -6
- package/chunks/{chunk-AJBEAUIS.js → chunk-BZP7HZTK.js} +7 -6
- package/chunks/{chunk-OFEVLU4C.js → chunk-CARU2RR2.js} +1 -1
- package/chunks/{chunk-MIG5SQNL.js → chunk-CBKR4Q5K.js} +8 -8
- package/chunks/chunk-CC2ITGCF.js +176 -0
- package/chunks/{chunk-FV7425LN.js → chunk-CEA3E3JB.js} +18 -5
- package/chunks/{chunk-GD236YPV.js → chunk-CNOF2IZT.js} +3 -3
- package/chunks/chunk-COH43MRJ.js +34 -0
- package/chunks/{chunk-E5A7LHNN.js → chunk-CPBF7KYF.js} +1 -1
- package/chunks/{chunk-UWCTAVOD.js → chunk-CR3C7WXL.js} +1 -1
- package/chunks/{chunk-5P5XGNYH.js → chunk-CU3L64TP.js} +1 -1
- package/chunks/{chunk-IWKSG2AR.js → chunk-CXAOG665.js} +1 -1
- package/chunks/{chunk-MZ7BABX3.js → chunk-DC5Z3DU6.js} +2 -2
- package/chunks/{chunk-64WXLC72.js → chunk-DJ2GSRLV.js} +1 -1
- package/chunks/{chunk-4ZJIPVPB.js → chunk-DMN532MJ.js} +11 -11
- package/chunks/{chunk-QN5NZ3UQ.js → chunk-DMTGGOSA.js} +2 -2
- package/chunks/{chunk-KHDZHZMH.js → chunk-E5Z2AVNV.js} +1 -1
- package/chunks/chunk-F4PFZ7WP.js +239 -0
- package/chunks/{chunk-ME3YSROJ.js → chunk-FBSBT7JS.js} +25 -13
- package/chunks/{chunk-5IFG2VC4.js → chunk-FCMNKVXT.js} +1 -1
- package/chunks/{chunk-YDSYD7UI.js → chunk-FD3DA3B4.js} +3 -3
- package/chunks/{chunk-GL5TV7B5.js → chunk-GL22M2DU.js} +23 -7
- package/chunks/{chunk-AFOLR6YL.js → chunk-GQ4ZEYMI.js} +102 -16
- package/chunks/chunk-GRC5HGPI.js +13 -0
- package/chunks/{chunk-WMPVYQ4P.js → chunk-GWHYEOV2.js} +2 -2
- package/chunks/chunk-GZRTYZ2R.js +292 -0
- package/chunks/{chunk-A2X3KLC3.js → chunk-GZXCZHCT.js} +3 -3
- package/chunks/{chunk-K5PGHDBN.js → chunk-H6XPXXMH.js} +1 -1
- package/chunks/{chunk-24XYI5EI.js → chunk-HACY3IUM.js} +6 -6
- package/chunks/{chunk-DGHJTO2K.js → chunk-HIHQ3TWC.js} +38 -9
- package/chunks/{chunk-KYMBIKIW.js → chunk-HTHUX2T5.js} +1 -1
- package/chunks/{chunk-LQ7TMOCE.js → chunk-HTO4JFDZ.js} +1 -1
- package/chunks/{chunk-B34OV7K2.js → chunk-IAVB3HM3.js} +15 -2
- package/chunks/{chunk-4UCTWSTP.js → chunk-IDJEZU7C.js} +2 -2
- package/chunks/chunk-IEBIKN6A.js +38 -0
- package/chunks/{chunk-PRDFOIZF.js → chunk-IJEO2QSK.js} +73 -30
- package/chunks/{chunk-MJI36HER.js → chunk-IT6LIIVK.js} +2 -2
- package/chunks/{chunk-FEJ2FZ3U.js → chunk-J3JA76CF.js} +2 -2
- package/chunks/{chunk-4CGOWHKM.js → chunk-J4MOFEOK.js} +2 -2
- package/chunks/{chunk-22MNNNXE.js → chunk-J4TSQ5EQ.js} +3 -2
- package/chunks/{chunk-22IFUCVR.js → chunk-JESCGQM3.js} +1 -1
- package/chunks/{chunk-CCGOFQV3.js → chunk-JSG7ZSBC.js} +8 -7
- package/chunks/{chunk-X265R5C6.js → chunk-K434OPES.js} +5 -5
- package/chunks/{chunk-RFIL6REB.js → chunk-KHUQZZJ6.js} +185 -45
- package/chunks/{chunk-X2XNQQJI.js → chunk-KKNMOEZH.js} +4 -4
- package/chunks/{chunk-6BN7NZ5Z.js → chunk-KL23O5Z5.js} +7 -9
- package/chunks/{chunk-MRO43B25.js → chunk-KW7NOTN6.js} +1 -1
- package/chunks/{chunk-6IAWPIQ6.js → chunk-L36P3NBD.js} +3 -3
- package/chunks/{chunk-54YZOP7P.js → chunk-L573Z2QZ.js} +11894 -5190
- package/chunks/{chunk-STAZRKOT.js → chunk-L6GPKRH7.js} +132 -464
- package/chunks/{chunk-QAKFYMJI.js → chunk-LD5OWZ5E.js} +155 -19
- package/chunks/{chunk-E7KNELIZ.js → chunk-LK7KUE55.js} +2 -2
- package/chunks/{chunk-W7EEOR7D.js → chunk-LUODI7WD.js} +5 -5
- package/chunks/{chunk-CSOTA7KX.js → chunk-LXNEJD34.js} +2 -2
- package/chunks/{chunk-HLKG6CZN.js → chunk-M5GB774H.js} +1 -1
- package/chunks/{chunk-3B26UVCX.js → chunk-MC23XKFE.js} +39 -10
- package/chunks/{chunk-XPJH4PKR.js → chunk-MPEMTBUZ.js} +2 -2
- package/chunks/{chunk-GMOWQOQQ.js → chunk-MUCWGT7O.js} +248 -55
- package/chunks/{chunk-PC4ZXHU2.js → chunk-MXL5UP4X.js} +3 -3
- package/chunks/{chunk-PUQLHSQ2.js → chunk-N2FGNKOE.js} +10 -12
- package/chunks/{chunk-332PWN27.js → chunk-NHDKSOEY.js} +1 -1
- package/chunks/{chunk-65CFHNSE.js → chunk-NKHV3OHY.js} +62 -62
- package/chunks/{chunk-P7M4GA57.js → chunk-OCPBI7J5.js} +1 -1
- package/chunks/{chunk-42XGFRJS.js → chunk-ORBGV7NJ.js} +5 -5
- package/chunks/{chunk-ALZ75L2X.js → chunk-OZJLTZKP.js} +415 -379
- package/chunks/chunk-P3X7RYUQ.js +468 -0
- package/chunks/{chunk-AGHU5VRK.js → chunk-PZL23GTM.js} +1 -1
- package/chunks/{chunk-FXGLL2HL.js → chunk-Q7YDKOGY.js} +1 -1
- package/chunks/{chunk-LGFAYIKX.js → chunk-QHTIBUWB.js} +1 -1
- package/chunks/{chunk-AIY5UTFI.js → chunk-QK2JET6S.js} +934 -2881
- package/chunks/{chunk-P3MYAZBY.js → chunk-QMGX2KO2.js} +1 -1
- package/chunks/{chunk-QZF5U5KA.js → chunk-QNTUV3PP.js} +11 -11
- package/chunks/chunk-QRUIS3DN.js +47 -0
- package/chunks/{chunk-ITOGNELQ.js → chunk-QSWDGAIC.js} +2 -2
- package/chunks/{chunk-7JGISHHZ.js → chunk-QUJIDGIW.js} +15 -157
- package/chunks/{chunk-GHOTR7HL.js → chunk-QUPXZXLV.js} +1 -1
- package/chunks/{chunk-TDGF35EN.js → chunk-QYMQSECS.js} +3 -3
- package/chunks/{chunk-QYUE6W3T.js → chunk-RDJSGHZE.js} +1 -1
- package/chunks/{chunk-TLRYABYP.js → chunk-RKUWKYED.js} +1 -1
- package/chunks/{chunk-ADZG46HR.js → chunk-RRNR5U2U.js} +2 -2
- package/chunks/{chunk-4O344UEE.js → chunk-RW7CXRUM.js} +39 -19
- package/chunks/{chunk-RNBYOUGV.js → chunk-RXFQM6FQ.js} +1 -1
- package/chunks/{chunk-QXPI7FEC.js → chunk-S3TWXGKA.js} +4 -4
- package/chunks/{chunk-44NROQYV.js → chunk-SJHDYNHF.js} +4 -4
- package/chunks/{chunk-MLZQVCF3.js → chunk-SZRN75WQ.js} +1 -1
- package/chunks/{chunk-7RYW5LQV.js → chunk-T2NCM2ET.js} +2 -2
- package/chunks/{chunk-Z2Z3GUXZ.js → chunk-TB6UDU4T.js} +1 -1
- package/chunks/{chunk-SUAKHHK7.js → chunk-TDEXZKMT.js} +3 -3
- package/chunks/{chunk-EXPMGZZV.js → chunk-TEGEBB2I.js} +1 -1
- package/chunks/{chunk-JF7DFPQ5.js → chunk-TJCEUA3S.js} +15 -3
- package/chunks/{chunk-MX2YXRER.js → chunk-TJTKSVIV.js} +1 -1
- package/chunks/{chunk-H6BD2ELD.js → chunk-TYAMGABM.js} +2 -2
- package/chunks/{chunk-CISB3IQF.js → chunk-U3VSCGEJ.js} +100 -15
- package/chunks/{chunk-VRDMOSQS.js → chunk-UDXSXLER.js} +2 -2
- package/chunks/{chunk-AHNJGLFV.js → chunk-UOZWKTQG.js} +3 -3
- package/chunks/{chunk-CJCKDMWO.js → chunk-URK542T4.js} +1 -1
- package/chunks/{chunk-YEGT55NW.js → chunk-UUVCHN62.js} +5697 -2983
- package/chunks/{chunk-AHNXYU4O.js → chunk-V6WHFGST.js} +1 -1
- package/chunks/{chunk-U4FRHCLA.js → chunk-VAUPLGDZ.js} +2370 -364
- package/chunks/{chunk-ZTQ26VBE.js → chunk-VDDGDZMA.js} +1 -1
- package/chunks/{chunk-OMX7CUOE.js → chunk-VGC4I5JJ.js} +1 -1
- package/chunks/{chunk-IUE7YWC3.js → chunk-VIUKEWUL.js} +8 -8
- package/chunks/{chunk-43W4YSO6.js → chunk-VJNBRRZG.js} +2 -2
- package/chunks/{chunk-I4A4K72C.js → chunk-VL7GBUQA.js} +1309 -703
- package/chunks/{chunk-VZ7HY3YU.js → chunk-W2UIXQDL.js} +3 -3
- package/chunks/{chunk-ER3BKOLB.js → chunk-W7FJ3N32.js} +1 -1
- package/chunks/{chunk-3HX5LZ6R.js → chunk-WBL3FJEU.js} +2 -2
- package/chunks/{chunk-RYFZCUJO.js → chunk-WCUK2QXK.js} +17 -9
- package/chunks/{chunk-OWVIH4HY.js → chunk-WFHJDT4S.js} +421 -114
- package/chunks/{chunk-BMFMJINR.js → chunk-WL4L7EZH.js} +1 -1
- package/chunks/{chunk-NZEGNV4V.js → chunk-WPHYXKFL.js} +8 -51
- package/chunks/chunk-X4NKFGFJ.js +81 -0
- package/chunks/{chunk-3RW4AZJV.js → chunk-XFSFZCM5.js} +1 -1
- package/chunks/{chunk-2MPVVENX.js → chunk-XU457J4V.js} +2 -2
- package/chunks/{chunk-SIUQ3YYX.js → chunk-XVNQMZ2I.js} +1 -1
- package/chunks/{chunk-SYCJMSIJ.js → chunk-XWRJCPHC.js} +1 -1
- package/chunks/chunk-YAF2MQZ6.js +19 -0
- package/chunks/chunk-YF4RZPJP.js +168 -0
- package/chunks/{chunk-4KJ2BPIT.js → chunk-YLV3XXFB.js} +4 -40
- package/chunks/{chunk-BR4QREVK.js → chunk-YQ3U5MUC.js} +1 -1
- package/chunks/{chunk-Y6Z2O3WR.js → chunk-YUZI3WAC.js} +1 -1
- package/chunks/{chunk-II73RK2S.js → chunk-YVEDCAUE.js} +1 -1
- package/chunks/{chunk-SKAZWEV5.js → chunk-ZJE7NSL3.js} +1 -1
- package/chunks/{chunk-7LTB54MK.js → chunk-ZLXQN2QS.js} +2 -2
- package/chunks/cli-entry-path-4VJ3Y6T2.js +10 -0
- package/chunks/{computer-use-X4KUU3NM.js → computer-use-2ZTYDNN4.js} +50 -50
- package/chunks/config-utils-ZITWCFBY.js +23 -0
- package/chunks/contextCommand-6L4XCZZT.js +68 -0
- package/chunks/create-sub-session-INX7TXJI.js +207 -0
- package/chunks/create-sub-session-VZIOFS6N.js +375 -0
- package/chunks/{cron-create-JSXEIPVS.js → cron-create-AMBKGZX6.js} +9 -9
- package/chunks/{cron-delete-OIFVEPGU.js → cron-delete-LRU4OJYG.js} +7 -7
- package/chunks/{cron-list-D4ZX4IOE.js → cron-list-WVP2JXER.js} +14 -10
- package/chunks/{daemon-F3I2RXP6.js → daemon-QEDXW7ER.js} +554 -4
- package/chunks/daemon-status-provider-7XJZ3HPS.js +76 -0
- package/chunks/{de-PLLFDERI.js → de-XHBEYUMG.js} +91 -6
- package/chunks/{devtools-FM6GJPYG.js → devtools-4QFYJT6U.js} +3 -3
- package/chunks/{dist-573HNSVO.js → dist-EFXT7A3G.js} +12 -11
- package/chunks/{dist-PKFTNUHO.js → dist-KCRX7TGL.js} +529 -79
- package/chunks/{dist-OT7G2WNM.js → dist-MFEAEUV4.js} +1011 -197
- package/chunks/{dist-63IS3ZMI.js → dist-PZV5RKM6.js} +4 -4
- package/chunks/{dist-FQ5WRJPL.js → dist-QY3THJM5.js} +5 -5
- package/chunks/{dist-IMFFBMAT.js → dist-UJOR6KEQ.js} +32 -15
- package/chunks/{dist-YTRKAVUB.js → dist-ZWZB4CCN.js} +10 -10
- package/chunks/earlyInputCapture-TTWXFIWO.js +69 -0
- package/chunks/{edit-WZTZDGCI.js → edit-6UWFRZKY.js} +51 -51
- package/chunks/{en-GA2RMXY5.js → en-TKHUFIK7.js} +100 -6
- package/chunks/{enter-worktree-DEPXBJEL.js → enter-worktree-LS7SGAII.js} +50 -50
- package/chunks/{enterPlanMode-JFGXZHHJ.js → enterPlanMode-TSZYLEEP.js} +66 -51
- package/chunks/environment-XCVLHOXG.js +88 -0
- package/chunks/errors-KIANLUTC.js +75 -0
- package/chunks/{exit-worktree-Y7XBK3VV.js → exit-worktree-2QV6UJSW.js} +50 -50
- package/chunks/{exitPlanMode-WQQER65F.js → exitPlanMode-GVZG3F6S.js} +51 -51
- package/chunks/{fast-path-W2U6XDNO.js → fast-path-GNFZAMQK.js} +16 -7
- package/chunks/{fast-path-settings-RCRXH2JG.js → fast-path-settings-5QZE64HR.js} +6 -5
- package/chunks/{fileFromPath-IBEHA3CO.js → fileFromPath-UDALK7FM.js} +3 -3
- package/chunks/{fr-GWNQG4XH.js → fr-TBF6JZNE.js} +91 -6
- package/chunks/{gemini-T4WX6SRW.js → gemini-N3H6OLHR.js} +501 -197
- package/chunks/{geminiContentGenerator-MKQACXEJ.js → geminiContentGenerator-4C257R2N.js} +9 -9
- package/chunks/{getMachineId-bsd-4CASPIU4.js → getMachineId-bsd-EXV7SWPA.js} +3 -3
- package/chunks/{getMachineId-darwin-HPQPEMZR.js → getMachineId-darwin-IUOMFXU3.js} +3 -3
- package/chunks/{getMachineId-linux-AUARKYHL.js → getMachineId-linux-K7XJYFHL.js} +2 -2
- package/chunks/{getMachineId-unsupported-S32ZDA2T.js → getMachineId-unsupported-EA5FDRJ6.js} +2 -2
- package/chunks/{getMachineId-win-4EFLHYIJ.js → getMachineId-win-KBA6RAI5.js} +3 -3
- package/chunks/{glob-4KKSHZDF.js → glob-QTUI5I3Y.js} +95 -74
- package/chunks/{grep-GHRYXSLZ.js → grep-Z5MGLRCG.js} +50 -50
- package/chunks/handleAutoUpdate-FFF3CZGP.js +69 -0
- package/chunks/i18n-QQAJT7A2.js +84 -0
- package/chunks/initializer-LGQGC7ZT.js +71 -0
- package/chunks/installationInfo-4FEZGXIN.js +65 -0
- package/chunks/{ja-2HOTA47X.js → ja-CDXUA2H3.js} +92 -7
- package/chunks/{keychain-token-storage-UJV5XP4Z.js → keychain-token-storage-VKUBNCP4.js} +4 -4
- package/chunks/{kittyProtocolDetector-OJRCJLIU.js → kittyProtocolDetector-HCNNKJTI.js} +2 -2
- package/chunks/list-KIVFF3BB.js +74 -0
- package/chunks/loadedSettingsAdapter-EOD3RVZS.js +68 -0
- package/chunks/{loop-wakeup-OHWEMFP5.js → loop-wakeup-WX7BLWWP.js} +10 -10
- package/chunks/{lowlight-FYAAUU5J.js → lowlight-J2OZNPCA.js} +1 -1
- package/chunks/{ls-L654WOAT.js → ls-ZB2BDP6T.js} +6 -6
- package/chunks/{lsp-M35KE54Y.js → lsp-V5DN4YUJ.js} +6 -8
- package/chunks/mcp-D2P46DB4.js +68 -0
- package/chunks/{monitor-464OY2UE.js → monitor-4ZWVOL6J.js} +50 -50
- package/chunks/{multipart-parser-AJ4WASWR.js → multipart-parser-AWZKXSQN.js} +3 -3
- package/chunks/nonInteractiveCli-NAHUR2X6.js +124 -0
- package/chunks/{notebook-edit-BLJYCNDO.js → notebook-edit-RCQ2D2IE.js} +51 -51
- package/chunks/openaiContentGenerator-U655T5QC.js +56 -0
- package/chunks/pidfile-O3OO2TCP.js +73 -0
- package/chunks/{pt-2THT3YHA.js → pt-6IR2VRQY.js} +91 -6
- package/chunks/{qwenContentGenerator-SUUHZ6CR.js → qwenContentGenerator-ABGWCRS6.js} +52 -52
- package/chunks/{qwenOAuth2-ZV7YLMND.js → qwenOAuth2-PJQ27GSO.js} +9 -9
- package/chunks/read-file-X7M4GUPY.js +30 -0
- package/chunks/{read-mcp-resource-CNQHBSJM.js → read-mcp-resource-SM5DA4RG.js} +5 -5
- package/chunks/{read-package-up-ER5OJUGP.js → read-package-up-TCM6I7S2.js} +2 -2
- package/chunks/{record-artifact-PP3LKHTS.js → record-artifact-4XKZX3UW.js} +7 -7
- package/chunks/ripGrep-KMPJJK5J.js +60 -0
- package/chunks/{ru-Z67YJHBG.js → ru-DBTHQA3F.js} +91 -6
- package/chunks/{run-qwen-serve-5OKADVLE.js → run-qwen-serve-O6CMPURG.js} +958 -184
- package/chunks/runtime-THE2YO27.js +94 -0
- package/chunks/{scheduler-ZRW3AIDU.js → scheduler-XXEA6EFC.js} +51 -51
- package/chunks/{send-message-OA6MV4EU.js → send-message-F36YD6KQ.js} +7 -7
- package/chunks/serve-W6F44JQQ.js +74 -0
- package/chunks/{server-M372UENX.js → server-SAEKMY52.js} +4340 -829
- package/chunks/{session-F5KSNP2N.js → session-4GGYGOWY.js} +162 -117
- package/chunks/settings-PYEU5HVD.js +118 -0
- package/chunks/shell-JGQECKTH.js +68 -0
- package/chunks/{skill-A2TL2NPC.js → skill-XPP3C2DS.js} +24 -24
- package/chunks/spawnChannel-62QKQBQN.js +70 -0
- package/chunks/{src-XKZMNHXS.js → src-2656Q6SN.js} +126 -52
- package/chunks/{src-7XL4G4DC.js → src-VLI3TKEY.js} +4 -4
- package/chunks/standalone-update-X7AZVG5R.js +79 -0
- package/chunks/{startInteractiveUI-REKSTOHU.js → startInteractiveUI-DXR4K6T5.js} +2277 -1834
- package/chunks/stdioHelpers-UMY72QRP.js +14 -0
- package/chunks/{syntheticOutput-F7LSDCAR.js → syntheticOutput-34H7LGA7.js} +5 -5
- package/chunks/task-create-FRGOEIKY.js +22 -0
- package/chunks/{task-list-TGGJX7EA.js → task-list-OW766BXZ.js} +10 -10
- package/chunks/{task-stop-W7NGTMYQ.js → task-stop-SIR5SNBN.js} +4 -4
- package/chunks/{task-update-5WUG5MVQ.js → task-update-YRA532MT.js} +12 -12
- package/chunks/{team-create-TBQVWAG7.js → team-create-3EHCUCKA.js} +50 -50
- package/chunks/{team-delete-UHQQ2QQX.js → team-delete-6OSECUZC.js} +10 -10
- package/chunks/{team-plan-approval-FZPGUSHJ.js → team-plan-approval-KZ7NZIMX.js} +50 -50
- package/chunks/theme-manager-J7ANV4JR.js +64 -0
- package/chunks/{todoWrite-C4ZAMSR3.js → todoWrite-YDMFEV4G.js} +6 -6
- package/chunks/{tool-search-FDQ3SLNJ.js → tool-search-2XLGWG4H.js} +24 -24
- package/chunks/total-session-admission-EQIHUOL6.js +72 -0
- package/chunks/trustedFolders-BEX5334Z.js +79 -0
- package/chunks/{types-QX5C3CHJ.js → types-JNKGKUJT.js} +2 -2
- package/chunks/updateCheck-NKPJGJCR.js +69 -0
- package/chunks/updateEventEmitter-5MKC7BLQ.js +10 -0
- package/chunks/validateNonInterActiveAuth-6DGGGE4A.js +190 -0
- package/chunks/{version-CEDD6ZZR.js → version-FRVWU5ZU.js} +3 -3
- package/chunks/{web-fetch-UYGMOHW4.js → web-fetch-PAAEQKBW.js} +8 -8
- package/chunks/{workflow-CQQ4J4I6.js → workflow-LWFMECLD.js} +51 -51
- package/chunks/workspace-providers-status-NE7QWEYS.js +71 -0
- package/chunks/workspace-registry-RCWAP57G.js +76 -0
- package/chunks/workspace-service-4QUMVGBX.js +80 -0
- package/chunks/workspace-skills-status-L34BYRSY.js +70 -0
- package/chunks/{write-file-VWGY7AJP.js → write-file-WXO4M5FN.js} +51 -51
- package/chunks/{yargs-B64EYTCF.js → yargs-6H2AUULL.js} +2 -2
- package/chunks/{zh-7XVVRTDH.js → zh-LJP47LX7.js} +101 -7
- package/chunks/{zh-TW-DPH554OG.js → zh-TW-5ISMO3VJ.js} +101 -7
- package/cli.js +18 -17
- package/locales/ca.js +117 -7
- package/locales/de.js +116 -7
- package/locales/en.js +133 -7
- package/locales/fr.js +117 -7
- package/locales/ja.js +119 -7
- package/locales/pt.js +116 -7
- package/locales/ru.js +116 -7
- package/locales/zh-TW.js +130 -7
- package/locales/zh.js +130 -7
- package/package.json +3 -6
- package/web-shell/assets/{arc-C7hjaa7g.js → arc-BRDEF7h6.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-pm9KtMZY.js → architectureDiagram-3BPJPVTR-Cxnk4Ygq.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-CApElKsU.js → blockDiagram-GPEHLZMM-BZrtayqw.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-BeDe0-9N.js → c4Diagram-AAUBKEIU-Djmbi1yB.js} +1 -1
- package/web-shell/assets/channel-C9zfQCn-.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-DJEkmLdl.js → chunk-2J33WTMH-Cdokj-0U.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-DXX8gdpe.js → chunk-4BX2VUAB-BI9Hx57o.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-DORmk1kQ.js → chunk-55IACEB6-Cb17cEzj.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-DWmO4mCZ.js → chunk-727SXJPM-DY1Fc0h_.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-C7LRaYw2.js → chunk-AQP2D5EJ-B10G71Hd.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-CCdXgyC5.js → chunk-FMBD7UC4-BP6OoB6Z.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-SyL_xvWc.js → chunk-ND2GUHAM-CWIIwUFC.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-BzOuCKxa.js → chunk-QZHKN3VN-DMrqCfGO.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-BVNaf5JM.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BVNaf5JM.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-BMPWyDKc.js → cose-bilkent-S5V4N54A-CF-mpkgI.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-B_GnPWik.js → dagre-BM42HDAG-DfsRFsg7.js} +1 -1
- package/web-shell/assets/{diagram-2AECGRRQ-fJqMc3S_.js → diagram-2AECGRRQ-DBNwf6Dy.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-ximV3O9D.js → diagram-5GNKFQAL-CD1TqV1j.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-2vVyjzqw.js → diagram-KO2AKTUF-DkWlaLI2.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-Dckcx1sT.js → diagram-LMA3HP47-ClrAcxwF.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-BAVv47ga.js → diagram-OG6HWLK6-Dr6dnPLw.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-Bn3nf5J0.js → erDiagram-TEJ5UH35-Dm_vj18R.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-BIbGF9JT.js → flowDiagram-I6XJVG4X-lp3cMdZu.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-CDbhb3AH.js → ganttDiagram-6RSMTGT7-AtdCWZya.js} +3 -3
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-CbKG1a_w.js → gitGraphDiagram-PVQCEYII-BARTJafF.js} +1 -1
- package/web-shell/assets/index-BkNgyfu7.js +3 -0
- package/web-shell/assets/index-CihmnZZR.js +784 -0
- package/web-shell/assets/index-zfdXhnwH.css +5 -0
- package/web-shell/assets/{infoDiagram-5YYISTIA-DUFxTy8m.js → infoDiagram-5YYISTIA-CxbJ54_f.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-D69hrBG8.js → ishikawaDiagram-YF4QCWOH-hFoO0HQA.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-DsvEw51Y.js → journeyDiagram-JHISSGLW-Bo6cKnLe.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-lFFjZEYd.js → kanban-definition-UN3LZRKU-DIFrH-XL.js} +1 -1
- package/web-shell/assets/{linear-Bo5bWzvo.js → linear-Bjv_GjDd.js} +1 -1
- package/web-shell/assets/{mermaid.core-C_HKMJx5.js → mermaid.core-C_OiqQEP.js} +5 -5
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-DlV-NUTs.js → mindmap-definition-RKZ34NQL-B79Gk8aB.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-B2e6FH2a.js → pieDiagram-4H26LBE5-B_P0gIHr.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-D75kSk-b.js → quadrantDiagram-W4KKPZXB-BC7F0ohb.js} +1 -1
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-CQ6xakhM.js → requirementDiagram-4Y6WPE33-BrA5_NSW.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-J_XTKrcY.js → sankeyDiagram-5OEKKPKP-Q2iqrNI_.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-D7jTcBtf.js → sequenceDiagram-3UESZ5HK-6vdIWz4H.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-CrlM_OT3.js → stateDiagram-AJRCARHV-DMzXNKcB.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BWFH0OCO.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-5oZi76Ef.js → timeline-definition-PNZ67QCA-BykEawmS.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-DO5YptPK.js → vennDiagram-CIIHVFJN-B24KD2X1.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-4t1eJDvM.js → wardley-L42UT6IY-C761G_tA.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-DKZc661S.js → wardleyDiagram-YWT4CUSO-CfwGJYqw.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-su9hp08G.js → xychartDiagram-2RQKCTM6-BDu5AZ2D.js} +1 -1
- package/web-shell/index.html +16 -4
- package/chunks/MaxSizedBox-XY5LIO5O.js +0 -71
- package/chunks/agent-TKIBLLQT.js +0 -68
- package/chunks/agent-headless-VLX4C7KX.js +0 -62
- package/chunks/bridge-BCU73DZO.js +0 -77
- package/chunks/chunk-6SHO7WFF.js +0 -79
- package/chunks/chunk-IFEWJXFS.js +0 -37
- package/chunks/chunk-S6RLAIUR.js +0 -23
- package/chunks/contextCommand-LAYJZF7H.js +0 -68
- package/chunks/daemon-status-provider-XSAFKVDS.js +0 -74
- package/chunks/earlyInputCapture-AERYYGXE.js +0 -69
- package/chunks/errors-GMZL4QV7.js +0 -75
- package/chunks/handleAutoUpdate-HN7ZOSXY.js +0 -65
- package/chunks/initializer-SRN3IM42.js +0 -69
- package/chunks/list-INTVXUPA.js +0 -71
- package/chunks/loadedSettingsAdapter-QVHHQPGM.js +0 -66
- package/chunks/mcp-7V4T46RG.js +0 -66
- package/chunks/nonInteractiveCli-LJEEODU2.js +0 -116
- package/chunks/openaiContentGenerator-PFF6HYB2.js +0 -56
- package/chunks/pidfile-LIBUENIV.js +0 -73
- package/chunks/read-file-L7TBQVX7.js +0 -30
- package/chunks/ripGrep-CKKX3GRB.js +0 -60
- package/chunks/serve-36WS32W6.js +0 -71
- package/chunks/settings-WFLNS3WT.js +0 -114
- package/chunks/shell-DEDSWN56.js +0 -68
- package/chunks/spawnChannel-RQHUWTCL.js +0 -70
- package/chunks/task-create-RPFZNB4B.js +0 -22
- package/chunks/theme-manager-DQZVNSOC.js +0 -64
- package/chunks/trustedFolders-Z475EIHL.js +0 -79
- package/chunks/updateCheck-E2ZX7RL7.js +0 -65
- package/chunks/validateNonInterActiveAuth-O4ECBHQL.js +0 -181
- package/chunks/workspace-providers-status-MVYXMYDG.js +0 -68
- package/chunks/workspace-service-OMBR6ZLG.js +0 -78
- package/chunks/workspace-skills-status-IMMQ7CAY.js +0 -68
- package/node_modules/@qwen-code/audio-capture/dist/index.d.ts +0 -35
- package/node_modules/@qwen-code/audio-capture/dist/index.js +0 -48
- package/node_modules/@qwen-code/audio-capture/dist/index.js.map +0 -1
- package/node_modules/@qwen-code/audio-capture/dist/platform.d.ts +0 -7
- package/node_modules/@qwen-code/audio-capture/dist/platform.js +0 -18
- package/node_modules/@qwen-code/audio-capture/dist/platform.js.map +0 -1
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/LICENSE +0 -21
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/README.md +0 -58
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/SECURITY.md +0 -5
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/bin.js +0 -84
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/build-test.js +0 -19
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/index.js +0 -6
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/node-gyp-build.js +0 -207
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/optional.js +0 -7
- package/node_modules/@qwen-code/audio-capture/node_modules/node-gyp-build/package.json +0 -43
- package/node_modules/@qwen-code/audio-capture/package.json +0 -30
- package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-arm64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/darwin-x64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/linux-arm64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/linux-x64/@qwen-code+audio-capture.node +0 -0
- package/node_modules/@qwen-code/audio-capture/prebuilds/win32-x64/@qwen-code+audio-capture.node +0 -0
- package/web-shell/assets/channel-BNf1dW-j.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-uOIGlzgi.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-uOIGlzgi.js +0 -1
- package/web-shell/assets/index-CnE2ur2L.js +0 -779
- package/web-shell/assets/index-hSKXrlSE.css +0 -5
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-Bs4TK_84.js +0 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
import {
|
|
4
4
|
MCP_RESTART_SERVER_DEADLINE_MS
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-OCPBI7J5.js";
|
|
6
6
|
import {
|
|
7
7
|
BranchWhilePromptActiveError,
|
|
8
8
|
CancelSentinelCollisionError,
|
|
@@ -22,30 +22,29 @@ import {
|
|
|
22
22
|
SessionShellDisabledError,
|
|
23
23
|
WorkspaceMismatchError,
|
|
24
24
|
isNotCurrentlyGeneratingCancelError
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-IAVB3HM3.js";
|
|
26
26
|
import {
|
|
27
27
|
LOAD_REPLAY_BULK_MODE,
|
|
28
28
|
LOAD_REPLAY_META_KEY,
|
|
29
29
|
LOAD_REPLAY_MODE_META_KEY,
|
|
30
30
|
LOAD_REPLAY_VERSION,
|
|
31
31
|
MID_TURN_QUEUE_DRAIN_METHOD
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-PZL23GTM.js";
|
|
33
33
|
import {
|
|
34
34
|
canonicalizeWorkspace
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-W7FJ3N32.js";
|
|
36
36
|
import {
|
|
37
37
|
DEFAULT_RING_SIZE,
|
|
38
38
|
EVENT_SCHEMA_VERSION,
|
|
39
|
-
EventBus
|
|
40
|
-
|
|
39
|
+
EventBus,
|
|
40
|
+
serializedBridgeEventByteLength
|
|
41
|
+
} from "./chunk-6RLROE6G.js";
|
|
41
42
|
import {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
RequestError
|
|
45
|
-
} from "./chunk-VRDMOSQS.js";
|
|
43
|
+
normalizeCompactedReplayMaxBytes
|
|
44
|
+
} from "./chunk-COH43MRJ.js";
|
|
46
45
|
import {
|
|
47
46
|
defaultSpawnChannelFactory
|
|
48
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-BZP7HZTK.js";
|
|
49
48
|
import {
|
|
50
49
|
BridgeChannelClosedError,
|
|
51
50
|
BridgeTimeoutError,
|
|
@@ -54,18 +53,31 @@ import {
|
|
|
54
53
|
STATUS_SCHEMA_VERSION,
|
|
55
54
|
createIdleWorkspaceExtensionsStatus,
|
|
56
55
|
createIdleWorkspaceHooksStatus
|
|
57
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-TJCEUA3S.js";
|
|
57
|
+
import {
|
|
58
|
+
ClientSideConnection,
|
|
59
|
+
PROTOCOL_VERSION,
|
|
60
|
+
RequestError
|
|
61
|
+
} from "./chunk-UDXSXLER.js";
|
|
58
62
|
import {
|
|
63
|
+
SESSION_ARTIFACT_PERSISTENCE_VERSION,
|
|
59
64
|
ShellExecutionService,
|
|
60
|
-
TrustGateError
|
|
61
|
-
|
|
65
|
+
TrustGateError,
|
|
66
|
+
WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY,
|
|
67
|
+
WORKSPACE_CONTENT_SHA256_METADATA_KEY,
|
|
68
|
+
isPrototypeMetadataKey,
|
|
69
|
+
isReservedWorkspaceMetadataKey,
|
|
70
|
+
metadataBudgetBytes,
|
|
71
|
+
normalizeSnapshotPayload,
|
|
72
|
+
stableSessionArtifactId
|
|
73
|
+
} from "./chunk-UUVCHN62.js";
|
|
62
74
|
import {
|
|
63
75
|
DAEMON_TRACEPARENT_META_KEY,
|
|
64
76
|
DAEMON_TRACESTATE_META_KEY
|
|
65
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-MUCWGT7O.js";
|
|
66
78
|
import {
|
|
67
79
|
init_esbuild_shims
|
|
68
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-5O2XNYP6.js";
|
|
69
81
|
import {
|
|
70
82
|
__name
|
|
71
83
|
} from "./chunk-J2S4EL5Y.js";
|
|
@@ -79,6 +91,7 @@ import * as path3 from "node:path";
|
|
|
79
91
|
init_esbuild_shims();
|
|
80
92
|
var TURN_BOUNDARY_TYPES = /* @__PURE__ */ new Set(["turn_complete", "turn_error"]);
|
|
81
93
|
var TRANSIENT_TYPES = /* @__PURE__ */ new Set([
|
|
94
|
+
"history_truncated",
|
|
82
95
|
"slow_client_warning",
|
|
83
96
|
"client_evicted",
|
|
84
97
|
"replay_complete",
|
|
@@ -88,17 +101,28 @@ var LATEST_WINS_UPDATES = /* @__PURE__ */ new Set([
|
|
|
88
101
|
"available_commands_update",
|
|
89
102
|
"current_mode_update"
|
|
90
103
|
]);
|
|
104
|
+
var REPLAY_SEGMENT_COMPACT_THRESHOLD = 64;
|
|
91
105
|
var TurnBoundaryCompactionEngine = class {
|
|
92
106
|
static {
|
|
93
107
|
__name(this, "TurnBoundaryCompactionEngine");
|
|
94
108
|
}
|
|
95
|
-
|
|
109
|
+
maxReplayBytes;
|
|
110
|
+
onReplayWindowEviction;
|
|
111
|
+
replaySegments = [];
|
|
112
|
+
replaySegmentStart = 0;
|
|
113
|
+
replayBytes = 0;
|
|
96
114
|
liveJournal = [];
|
|
97
115
|
lastEventId = 0;
|
|
98
116
|
closed = false;
|
|
117
|
+
truncatedEvents = 0;
|
|
118
|
+
truncatedTurns = 0;
|
|
99
119
|
slots = [];
|
|
100
120
|
toolSlotIndex = /* @__PURE__ */ new Map();
|
|
101
121
|
textSlotIndex = /* @__PURE__ */ new Map();
|
|
122
|
+
constructor(opts = {}) {
|
|
123
|
+
this.maxReplayBytes = normalizeCompactedReplayMaxBytes(opts.maxReplayBytes);
|
|
124
|
+
this.onReplayWindowEviction = opts.onReplayWindowEviction;
|
|
125
|
+
}
|
|
102
126
|
ingest(event) {
|
|
103
127
|
if (this.closed) return;
|
|
104
128
|
if (event.id !== void 0) {
|
|
@@ -117,16 +141,39 @@ var TurnBoundaryCompactionEngine = class {
|
|
|
117
141
|
this.slots.push({ kind: "misc", event });
|
|
118
142
|
}
|
|
119
143
|
snapshot() {
|
|
144
|
+
const compactedTurns = this.flattenReplaySegments();
|
|
145
|
+
if (this.truncatedEvents > 0) {
|
|
146
|
+
compactedTurns.unshift(
|
|
147
|
+
this.makeHistoryTruncatedEvent(compactedTurns.length)
|
|
148
|
+
);
|
|
149
|
+
}
|
|
120
150
|
return {
|
|
121
|
-
compactedTurns
|
|
151
|
+
compactedTurns,
|
|
122
152
|
liveJournal: this.liveJournal.slice(),
|
|
123
153
|
lastEventId: this.lastEventId
|
|
124
154
|
};
|
|
125
155
|
}
|
|
126
156
|
seed(snapshot) {
|
|
127
157
|
if (this.closed) return;
|
|
128
|
-
this.
|
|
158
|
+
this.resetReplayWindow();
|
|
129
159
|
this.lastEventId = snapshot.lastEventId;
|
|
160
|
+
for (const event of snapshot.compactedTurns) {
|
|
161
|
+
if (TRANSIENT_TYPES.has(event.type)) continue;
|
|
162
|
+
this.addReplaySegment([event], 0);
|
|
163
|
+
}
|
|
164
|
+
this.liveJournal = [];
|
|
165
|
+
this.slots = [];
|
|
166
|
+
this.toolSlotIndex.clear();
|
|
167
|
+
this.textSlotIndex.clear();
|
|
168
|
+
}
|
|
169
|
+
seedReplayEvents(events) {
|
|
170
|
+
if (this.closed) return;
|
|
171
|
+
this.resetReplayWindow();
|
|
172
|
+
for (const event of events) {
|
|
173
|
+
this.recordLastEventId(event);
|
|
174
|
+
if (TRANSIENT_TYPES.has(event.type)) continue;
|
|
175
|
+
this.addReplaySegment([event], 0);
|
|
176
|
+
}
|
|
130
177
|
this.liveJournal = [];
|
|
131
178
|
this.slots = [];
|
|
132
179
|
this.toolSlotIndex.clear();
|
|
@@ -135,7 +182,7 @@ var TurnBoundaryCompactionEngine = class {
|
|
|
135
182
|
close() {
|
|
136
183
|
if (this.closed) return;
|
|
137
184
|
this.closed = true;
|
|
138
|
-
this.
|
|
185
|
+
this.resetReplayWindow();
|
|
139
186
|
this.liveJournal = [];
|
|
140
187
|
this.slots = [];
|
|
141
188
|
this.toolSlotIndex.clear();
|
|
@@ -272,12 +319,94 @@ var TurnBoundaryCompactionEngine = class {
|
|
|
272
319
|
}
|
|
273
320
|
}
|
|
274
321
|
compacted.push(boundaryEvent);
|
|
275
|
-
this.
|
|
322
|
+
this.addReplaySegment(compacted, 1);
|
|
276
323
|
this.liveJournal = [];
|
|
277
324
|
this.slots = [];
|
|
278
325
|
this.toolSlotIndex.clear();
|
|
279
326
|
this.textSlotIndex.clear();
|
|
280
327
|
}
|
|
328
|
+
recordLastEventId(event) {
|
|
329
|
+
if (event.id !== void 0) {
|
|
330
|
+
this.lastEventId = event.id;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
addReplaySegment(events, turnCount) {
|
|
334
|
+
if (events.length === 0) return;
|
|
335
|
+
const bytes = events.reduce(
|
|
336
|
+
(sum, event) => sum + serializedBridgeEventByteLength(event),
|
|
337
|
+
0
|
|
338
|
+
);
|
|
339
|
+
this.replaySegments.push({ events: events.slice(), bytes, turnCount });
|
|
340
|
+
this.replayBytes += bytes;
|
|
341
|
+
this.enforceReplayWindow();
|
|
342
|
+
}
|
|
343
|
+
enforceReplayWindow() {
|
|
344
|
+
let droppedSegmentCount = 0;
|
|
345
|
+
let droppedBytes = 0;
|
|
346
|
+
let droppedEvents = 0;
|
|
347
|
+
let droppedTurns = 0;
|
|
348
|
+
while (this.replayBytes > this.maxReplayBytes && this.activeReplaySegmentCount() > 1) {
|
|
349
|
+
const dropped = this.replaySegments[this.replaySegmentStart];
|
|
350
|
+
this.replaySegmentStart += 1;
|
|
351
|
+
droppedSegmentCount += 1;
|
|
352
|
+
droppedBytes += dropped.bytes;
|
|
353
|
+
droppedEvents += dropped.events.length;
|
|
354
|
+
droppedTurns += dropped.turnCount;
|
|
355
|
+
this.replayBytes -= dropped.bytes;
|
|
356
|
+
this.truncatedEvents += dropped.events.length;
|
|
357
|
+
this.truncatedTurns += dropped.turnCount;
|
|
358
|
+
}
|
|
359
|
+
if (droppedSegmentCount > 0) {
|
|
360
|
+
this.compactReplaySegmentQueueIfNeeded();
|
|
361
|
+
this.notifyReplayWindowEviction({
|
|
362
|
+
droppedBytes,
|
|
363
|
+
droppedEvents,
|
|
364
|
+
droppedSegments: droppedSegmentCount,
|
|
365
|
+
droppedTurns,
|
|
366
|
+
maxBytes: this.maxReplayBytes,
|
|
367
|
+
retainedBytes: this.replayBytes,
|
|
368
|
+
retainedEvents: this.flattenReplaySegments().length
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
flattenReplaySegments() {
|
|
373
|
+
return this.replaySegments.slice(this.replaySegmentStart).flatMap((segment) => segment.events);
|
|
374
|
+
}
|
|
375
|
+
activeReplaySegmentCount() {
|
|
376
|
+
return this.replaySegments.length - this.replaySegmentStart;
|
|
377
|
+
}
|
|
378
|
+
compactReplaySegmentQueueIfNeeded() {
|
|
379
|
+
if (this.replaySegmentStart < REPLAY_SEGMENT_COMPACT_THRESHOLD) return;
|
|
380
|
+
this.replaySegments.splice(0, this.replaySegmentStart);
|
|
381
|
+
this.replaySegmentStart = 0;
|
|
382
|
+
}
|
|
383
|
+
notifyReplayWindowEviction(eviction) {
|
|
384
|
+
try {
|
|
385
|
+
this.onReplayWindowEviction?.(eviction);
|
|
386
|
+
} catch {
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
makeHistoryTruncatedEvent(retainedEvents) {
|
|
390
|
+
return {
|
|
391
|
+
v: EVENT_SCHEMA_VERSION,
|
|
392
|
+
type: "history_truncated",
|
|
393
|
+
data: {
|
|
394
|
+
reason: "replay_window_exceeded",
|
|
395
|
+
truncatedEvents: this.truncatedEvents,
|
|
396
|
+
retainedEvents,
|
|
397
|
+
maxBytes: this.maxReplayBytes,
|
|
398
|
+
...this.truncatedTurns > 0 ? { truncatedTurns: this.truncatedTurns } : {},
|
|
399
|
+
fullTranscriptAvailable: true
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
resetReplayWindow() {
|
|
404
|
+
this.replaySegments = [];
|
|
405
|
+
this.replaySegmentStart = 0;
|
|
406
|
+
this.replayBytes = 0;
|
|
407
|
+
this.truncatedEvents = 0;
|
|
408
|
+
this.truncatedTurns = 0;
|
|
409
|
+
}
|
|
281
410
|
};
|
|
282
411
|
function makeMergedSessionUpdateEvent(sessionUpdate, text, eventId, meta, envelopeMeta) {
|
|
283
412
|
return {
|
|
@@ -362,6 +491,11 @@ import * as path from "node:path";
|
|
|
362
491
|
init_esbuild_shims();
|
|
363
492
|
var MID_TURN_MESSAGE_INJECTED_EVENT = "mid_turn_message_injected";
|
|
364
493
|
|
|
494
|
+
// packages/acp-bridge/src/bridgeOptions.ts
|
|
495
|
+
init_esbuild_shims();
|
|
496
|
+
var MAX_SUB_SESSION_PROMPT_CHARS = 1e5;
|
|
497
|
+
var MAX_SUB_SESSION_NAME_CHARS = 200;
|
|
498
|
+
|
|
365
499
|
// packages/acp-bridge/src/permissionMediator.ts
|
|
366
500
|
init_esbuild_shims();
|
|
367
501
|
|
|
@@ -1048,6 +1182,60 @@ function isRecord(value) {
|
|
|
1048
1182
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1049
1183
|
}
|
|
1050
1184
|
__name(isRecord, "isRecord");
|
|
1185
|
+
function pendingInteractionOptions(options) {
|
|
1186
|
+
return options.map((option) => ({
|
|
1187
|
+
optionId: String(option.optionId ?? ""),
|
|
1188
|
+
...typeof option.name === "string" ? { label: option.name } : {},
|
|
1189
|
+
...typeof option.kind === "string" ? { kind: option.kind } : {}
|
|
1190
|
+
}));
|
|
1191
|
+
}
|
|
1192
|
+
__name(pendingInteractionOptions, "pendingInteractionOptions");
|
|
1193
|
+
function pendingInteractionFromRequest(requestId, params) {
|
|
1194
|
+
const toolCall = params.toolCall;
|
|
1195
|
+
const meta = isRecord(toolCall["_meta"]) ? toolCall["_meta"] : void 0;
|
|
1196
|
+
const rawInput = toolCall["rawInput"];
|
|
1197
|
+
const options = pendingInteractionOptions(
|
|
1198
|
+
Array.isArray(params.options) ? params.options : []
|
|
1199
|
+
);
|
|
1200
|
+
const isUserQuestion = meta?.["qwenInteractionKind"] === "user_question";
|
|
1201
|
+
if (isUserQuestion) {
|
|
1202
|
+
const rawQuestions = Array.isArray(meta?.["qwenQuestions"]) ? meta["qwenQuestions"] : isRecord(rawInput) && Array.isArray(rawInput["questions"]) ? rawInput["questions"] : [];
|
|
1203
|
+
return {
|
|
1204
|
+
requestId,
|
|
1205
|
+
kind: "user_question",
|
|
1206
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1207
|
+
...typeof toolCall["title"] === "string" ? { title: toolCall["title"] } : {},
|
|
1208
|
+
questions: rawQuestions.flatMap(
|
|
1209
|
+
(question, index) => isRecord(question) ? [{ ...question, answerKey: String(index) }] : []
|
|
1210
|
+
),
|
|
1211
|
+
options
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
return {
|
|
1215
|
+
requestId,
|
|
1216
|
+
kind: "permission",
|
|
1217
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1218
|
+
action: {
|
|
1219
|
+
...typeof toolCall["kind"] === "string" ? { type: toolCall["kind"] } : {},
|
|
1220
|
+
...typeof toolCall["title"] === "string" ? { title: toolCall["title"] } : {},
|
|
1221
|
+
...toolCall["content"] !== void 0 ? { content: toolCall["content"] } : {},
|
|
1222
|
+
...toolCall["locations"] !== void 0 ? { locations: toolCall["locations"] } : {},
|
|
1223
|
+
...rawInput !== void 0 ? { input: rawInput } : {}
|
|
1224
|
+
},
|
|
1225
|
+
options
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
__name(pendingInteractionFromRequest, "pendingInteractionFromRequest");
|
|
1229
|
+
function fallbackPendingPermissionInteraction(requestId, options) {
|
|
1230
|
+
return {
|
|
1231
|
+
requestId,
|
|
1232
|
+
kind: "permission",
|
|
1233
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1234
|
+
action: {},
|
|
1235
|
+
options: pendingInteractionOptions(options)
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
__name(fallbackPendingPermissionInteraction, "fallbackPendingPermissionInteraction");
|
|
1051
1239
|
function artifactPayloadFields(artifact) {
|
|
1052
1240
|
return {
|
|
1053
1241
|
title: artifact["title"],
|
|
@@ -1059,7 +1247,8 @@ function artifactPayloadFields(artifact) {
|
|
|
1059
1247
|
url: artifact["url"],
|
|
1060
1248
|
mimeType: artifact["mimeType"],
|
|
1061
1249
|
sizeBytes: artifact["sizeBytes"],
|
|
1062
|
-
metadata: artifact["metadata"]
|
|
1250
|
+
metadata: artifact["metadata"],
|
|
1251
|
+
retention: artifact["retention"]
|
|
1063
1252
|
};
|
|
1064
1253
|
}
|
|
1065
1254
|
__name(artifactPayloadFields, "artifactPayloadFields");
|
|
@@ -1202,7 +1391,7 @@ function sliceLineRange(content, startLine, endLine) {
|
|
|
1202
1391
|
}
|
|
1203
1392
|
__name(sliceLineRange, "sliceLineRange");
|
|
1204
1393
|
var BridgeClient = class {
|
|
1205
|
-
constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender, ownsSession = () => true, onTokenUsage) {
|
|
1394
|
+
constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender, ownsSession = () => true, onTokenUsage, onCreateSubSession) {
|
|
1206
1395
|
this.resolveEntry = resolveEntry;
|
|
1207
1396
|
this.resolvePendingRestoreEvents = resolvePendingRestoreEvents;
|
|
1208
1397
|
this.mediator = mediator;
|
|
@@ -1214,6 +1403,7 @@ var BridgeClient = class {
|
|
|
1214
1403
|
this.clientMcpSender = clientMcpSender;
|
|
1215
1404
|
this.ownsSession = ownsSession;
|
|
1216
1405
|
this.onTokenUsage = onTokenUsage;
|
|
1406
|
+
this.onCreateSubSession = onCreateSubSession;
|
|
1217
1407
|
}
|
|
1218
1408
|
static {
|
|
1219
1409
|
__name(this, "BridgeClient");
|
|
@@ -1227,10 +1417,9 @@ var BridgeClient = class {
|
|
|
1227
1417
|
);
|
|
1228
1418
|
return { outcome: { outcome: "cancelled" } };
|
|
1229
1419
|
}
|
|
1420
|
+
const options = Array.isArray(params.options) ? params.options : [];
|
|
1230
1421
|
const allowedOptionIds = new Set(
|
|
1231
|
-
|
|
1232
|
-
(o) => String(o.optionId ?? "")
|
|
1233
|
-
)
|
|
1422
|
+
options.map((o) => String(o.optionId ?? ""))
|
|
1234
1423
|
);
|
|
1235
1424
|
allowedOptionIds.delete("");
|
|
1236
1425
|
const requestId = randomUUID();
|
|
@@ -1243,12 +1432,25 @@ var BridgeClient = class {
|
|
|
1243
1432
|
requestId,
|
|
1244
1433
|
sessionId: entry.sessionId,
|
|
1245
1434
|
toolCall: params.toolCall,
|
|
1246
|
-
options
|
|
1435
|
+
options
|
|
1247
1436
|
},
|
|
1248
1437
|
...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
|
|
1249
1438
|
});
|
|
1250
1439
|
if (!published) return { outcome: { outcome: "cancelled" } };
|
|
1440
|
+
let interaction;
|
|
1441
|
+
try {
|
|
1442
|
+
interaction = pendingInteractionFromRequest(requestId, {
|
|
1443
|
+
...params,
|
|
1444
|
+
options
|
|
1445
|
+
});
|
|
1446
|
+
} catch (error) {
|
|
1447
|
+
writeStderrLine(
|
|
1448
|
+
`qwen serve: failed to snapshot pending interaction ${requestId}: ${error instanceof Error ? error.message : String(error)}`
|
|
1449
|
+
);
|
|
1450
|
+
interaction = fallbackPendingPermissionInteraction(requestId, options);
|
|
1451
|
+
}
|
|
1251
1452
|
entry.pendingPermissionIds.add(requestId);
|
|
1453
|
+
if (interaction) entry.pendingInteractions.set(requestId, interaction);
|
|
1252
1454
|
try {
|
|
1253
1455
|
const record = {
|
|
1254
1456
|
requestId,
|
|
@@ -1264,6 +1466,7 @@ var BridgeClient = class {
|
|
|
1264
1466
|
return resolutionToAcpResponse(resolution);
|
|
1265
1467
|
} finally {
|
|
1266
1468
|
entry.pendingPermissionIds.delete(requestId);
|
|
1469
|
+
entry.pendingInteractions.delete(requestId);
|
|
1267
1470
|
}
|
|
1268
1471
|
}
|
|
1269
1472
|
async sessionUpdate(params) {
|
|
@@ -1337,7 +1540,7 @@ var BridgeClient = class {
|
|
|
1337
1540
|
trustedPublisher: isTrustedArtifactToolUpdate(params, updateMeta)
|
|
1338
1541
|
};
|
|
1339
1542
|
}
|
|
1340
|
-
async seedSessionUpdates(entry, updates) {
|
|
1543
|
+
async seedSessionUpdates(entry, updates, options = {}) {
|
|
1341
1544
|
const frames = [];
|
|
1342
1545
|
const artifactBatches = [];
|
|
1343
1546
|
for (const update of updates) {
|
|
@@ -1346,7 +1549,7 @@ var BridgeClient = class {
|
|
|
1346
1549
|
entry
|
|
1347
1550
|
);
|
|
1348
1551
|
frames.push(...prepared.frames);
|
|
1349
|
-
if (prepared.artifacts.length > 0) {
|
|
1552
|
+
if (options.ingestArtifacts !== false && prepared.artifacts.length > 0) {
|
|
1350
1553
|
artifactBatches.push({
|
|
1351
1554
|
artifacts: prepared.artifacts,
|
|
1352
1555
|
trustedPublisher: prepared.trustedPublisher
|
|
@@ -1435,9 +1638,12 @@ var BridgeClient = class {
|
|
|
1435
1638
|
inFlightRestoreIds = /* @__PURE__ */ new Set();
|
|
1436
1639
|
/**
|
|
1437
1640
|
* Handle child->bridge ACP `extMethod` requests (calls that expect a
|
|
1438
|
-
* response, unlike `extNotification`).
|
|
1439
|
-
* `
|
|
1440
|
-
*
|
|
1641
|
+
* response, unlike `extNotification`). Served methods:
|
|
1642
|
+
* `qwen/control/client_mcp/message` (reverse tool channel),
|
|
1643
|
+
* `qwen/control/create-sub-session` (the `create_sub_session` tool → daemon
|
|
1644
|
+
* spawns a sub-session and, for `'first-turn'`, returns its first-turn
|
|
1645
|
+
* result), and `craft/drainMidTurnQueue`: the ACP child calls the last one
|
|
1646
|
+
* between tool batches to pull any messages the browser queued mid-turn. We splice the per-session
|
|
1441
1647
|
* queue, return them to the child as the response, and — when non-empty —
|
|
1442
1648
|
* publish a `mid_turn_message_injected` SSE frame so the browser can move
|
|
1443
1649
|
* those messages out of its pending queue (a dedupe signal, not a transcript
|
|
@@ -1450,6 +1656,9 @@ var BridgeClient = class {
|
|
|
1450
1656
|
if (method === SERVE_CONTROL_EXT_METHODS.clientMcpMessage) {
|
|
1451
1657
|
return this.handleClientMcpMessage(params);
|
|
1452
1658
|
}
|
|
1659
|
+
if (method === SERVE_CONTROL_EXT_METHODS.createSubSession) {
|
|
1660
|
+
return this.handleCreateSubSession(params);
|
|
1661
|
+
}
|
|
1453
1662
|
if (method !== MID_TURN_QUEUE_DRAIN_METHOD) {
|
|
1454
1663
|
throw RequestError.methodNotFound(method);
|
|
1455
1664
|
}
|
|
@@ -1525,6 +1734,68 @@ var BridgeClient = class {
|
|
|
1525
1734
|
const response = await send(payload);
|
|
1526
1735
|
return { payload: response };
|
|
1527
1736
|
}
|
|
1737
|
+
/**
|
|
1738
|
+
* Handle the `create_sub_session` tool's request: validate, then forward to
|
|
1739
|
+
* the daemon-host `onCreateSubSession` callback (which spawns a fresh
|
|
1740
|
+
* top-level sub-session and, for `'first-turn'`, waits for its first turn and
|
|
1741
|
+
* returns the result). No host wired → `methodNotFound`, which the tool
|
|
1742
|
+
* surfaces as "daemon-only".
|
|
1743
|
+
*/
|
|
1744
|
+
async handleCreateSubSession(params) {
|
|
1745
|
+
if (!this.onCreateSubSession) {
|
|
1746
|
+
throw RequestError.methodNotFound(
|
|
1747
|
+
SERVE_CONTROL_EXT_METHODS.createSubSession
|
|
1748
|
+
);
|
|
1749
|
+
}
|
|
1750
|
+
const prompt = params["prompt"];
|
|
1751
|
+
if (typeof prompt !== "string" || prompt.length === 0) {
|
|
1752
|
+
throw RequestError.invalidParams(
|
|
1753
|
+
void 0,
|
|
1754
|
+
"`prompt` must be a non-empty string"
|
|
1755
|
+
);
|
|
1756
|
+
}
|
|
1757
|
+
if (prompt.length > MAX_SUB_SESSION_PROMPT_CHARS) {
|
|
1758
|
+
throw RequestError.invalidParams(
|
|
1759
|
+
void 0,
|
|
1760
|
+
`\`prompt\` exceeds the ${MAX_SUB_SESSION_PROMPT_CHARS}-character limit`
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1763
|
+
const completion = params["completion"];
|
|
1764
|
+
if (completion !== "sent" && completion !== "first-turn") {
|
|
1765
|
+
throw RequestError.invalidParams(
|
|
1766
|
+
void 0,
|
|
1767
|
+
"`completion` must be 'sent' or 'first-turn'"
|
|
1768
|
+
);
|
|
1769
|
+
}
|
|
1770
|
+
const name = params["name"];
|
|
1771
|
+
if (typeof name === "string" && name.length > MAX_SUB_SESSION_NAME_CHARS) {
|
|
1772
|
+
throw RequestError.invalidParams(
|
|
1773
|
+
void 0,
|
|
1774
|
+
`\`name\` exceeds the ${MAX_SUB_SESSION_NAME_CHARS}-character limit`
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
const callerSessionId = params["callerSessionId"];
|
|
1778
|
+
if (typeof callerSessionId !== "string" || callerSessionId.length === 0 || !this.ownsSession(callerSessionId)) {
|
|
1779
|
+
throw RequestError.invalidParams(
|
|
1780
|
+
void 0,
|
|
1781
|
+
"`callerSessionId` is required and must name a session owned by this connection"
|
|
1782
|
+
);
|
|
1783
|
+
}
|
|
1784
|
+
const model = params["model"];
|
|
1785
|
+
const result = await this.onCreateSubSession({
|
|
1786
|
+
prompt,
|
|
1787
|
+
completion,
|
|
1788
|
+
...typeof model === "string" && model.length > 0 && model.length <= 128 ? { model } : {},
|
|
1789
|
+
...typeof name === "string" && name.length > 0 ? { name } : {},
|
|
1790
|
+
callerSessionId
|
|
1791
|
+
});
|
|
1792
|
+
return {
|
|
1793
|
+
sessionId: result.sessionId,
|
|
1794
|
+
...result.result !== void 0 ? { result: result.result } : {},
|
|
1795
|
+
...result.stopReason !== void 0 ? { stopReason: result.stopReason } : {},
|
|
1796
|
+
...result.parentSessionPersisted !== void 0 ? { parentSessionPersisted: result.parentSessionPersisted } : {}
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1528
1799
|
/**
|
|
1529
1800
|
* Handle child->bridge ACP `extNotification` calls. Recognized methods are
|
|
1530
1801
|
* `qwen/notify/session/model-update`,
|
|
@@ -1534,8 +1805,8 @@ var BridgeClient = class {
|
|
|
1534
1805
|
* `qwen/notify/session/artifact-event` (hook artifacts),
|
|
1535
1806
|
* `qwen/notify/session/terminal-sequence`, and
|
|
1536
1807
|
* `qwen/notify/session/mcp-budget-event` — each translated into a
|
|
1537
|
-
* session-scoped SSE frame. Unknown methods are dropped silently
|
|
1538
|
-
*
|
|
1808
|
+
* session-scoped SSE frame. Unknown methods are dropped silently for
|
|
1809
|
+
* forward-compat.
|
|
1539
1810
|
*/
|
|
1540
1811
|
async extNotification(method, params) {
|
|
1541
1812
|
if (method === "qwen/notify/session/model-update") {
|
|
@@ -2118,7 +2389,7 @@ __name(extractA2uiToolUpdate, "extractA2uiToolUpdate");
|
|
|
2118
2389
|
// packages/acp-bridge/src/sessionArtifacts.ts
|
|
2119
2390
|
init_esbuild_shims();
|
|
2120
2391
|
import { createHash } from "node:crypto";
|
|
2121
|
-
import { promises as fs2 } from "node:fs";
|
|
2392
|
+
import { constants as fsConstants, promises as fs2 } from "node:fs";
|
|
2122
2393
|
import path2 from "node:path";
|
|
2123
2394
|
var SOURCE_RESERVATIONS = {
|
|
2124
2395
|
tool: 100,
|
|
@@ -2127,6 +2398,18 @@ var SOURCE_RESERVATIONS = {
|
|
|
2127
2398
|
};
|
|
2128
2399
|
var WORKSPACE_STATUS_REFRESH_TTL_MS = 5e3;
|
|
2129
2400
|
var WORKSPACE_STATUS_REFRESH_BATCH_SIZE = 20;
|
|
2401
|
+
var SNAPSHOT_AFTER_DURABLE_EVENTS = 50;
|
|
2402
|
+
var MAX_SNAPSHOT_BACKOFF_MULTIPLIER = 4;
|
|
2403
|
+
var MAX_TOMBSTONED_IDS = 500;
|
|
2404
|
+
var MAX_STICKY_EPHEMERAL_IDS = 500;
|
|
2405
|
+
var MAX_WORKSPACE_HASH_BYTES = 100 * 1024 * 1024;
|
|
2406
|
+
var SECRET_TOKEN_VALUE_PATTERN = /(?:^|\s)(?:bearer\s+\S{8,}|sk-[A-Za-z0-9_-]{12,}|(?:gh[pousr]|github_pat)_[A-Za-z0-9_/-]{12,}|[a-f0-9]{40,}|[A-Za-z0-9+/]{48,}={0,2})(?:$|\s)/i;
|
|
2407
|
+
var RESTORE_FAILED_WARNING_PREFIX = "artifact snapshot restore failed";
|
|
2408
|
+
var RESTORE_PARTIAL_FAILED_WARNING_PREFIX = "artifact snapshot restore partially failed";
|
|
2409
|
+
function isArtifactRestoreFailureWarning(warning) {
|
|
2410
|
+
return warning.startsWith(RESTORE_FAILED_WARNING_PREFIX) || warning.startsWith(RESTORE_PARTIAL_FAILED_WARNING_PREFIX);
|
|
2411
|
+
}
|
|
2412
|
+
__name(isArtifactRestoreFailureWarning, "isArtifactRestoreFailureWarning");
|
|
2130
2413
|
var SessionArtifactValidationError = class extends Error {
|
|
2131
2414
|
constructor(message, field) {
|
|
2132
2415
|
super(message);
|
|
@@ -2138,6 +2421,20 @@ var SessionArtifactValidationError = class extends Error {
|
|
|
2138
2421
|
}
|
|
2139
2422
|
code = "VALIDATION_FAILED";
|
|
2140
2423
|
};
|
|
2424
|
+
var SessionArtifactAuthorizationError = class extends Error {
|
|
2425
|
+
constructor(sessionId, artifactId, ownerClientId, requesterClientId) {
|
|
2426
|
+
super(`artifact ${artifactId} is owned by a different client`);
|
|
2427
|
+
this.sessionId = sessionId;
|
|
2428
|
+
this.artifactId = artifactId;
|
|
2429
|
+
this.ownerClientId = ownerClientId;
|
|
2430
|
+
this.requesterClientId = requesterClientId;
|
|
2431
|
+
this.name = "SessionArtifactAuthorizationError";
|
|
2432
|
+
}
|
|
2433
|
+
static {
|
|
2434
|
+
__name(this, "SessionArtifactAuthorizationError");
|
|
2435
|
+
}
|
|
2436
|
+
code = "SESSION_ARTIFACT_FORBIDDEN";
|
|
2437
|
+
};
|
|
2141
2438
|
var SessionArtifactStore = class {
|
|
2142
2439
|
static {
|
|
2143
2440
|
__name(this, "SessionArtifactStore");
|
|
@@ -2145,15 +2442,26 @@ var SessionArtifactStore = class {
|
|
|
2145
2442
|
sessionId;
|
|
2146
2443
|
workspaceCwd;
|
|
2147
2444
|
maxArtifacts;
|
|
2445
|
+
persistence;
|
|
2148
2446
|
artifacts = /* @__PURE__ */ new Map();
|
|
2149
2447
|
receivedSeq = 0;
|
|
2150
2448
|
insertSeq = 0;
|
|
2449
|
+
persistenceSeq = 0;
|
|
2450
|
+
durableEventsSinceSnapshot = 0;
|
|
2451
|
+
consecutiveSnapshotFailures = 0;
|
|
2151
2452
|
realWorkspaceCwdPromise;
|
|
2152
2453
|
operationQueue = Promise.resolve();
|
|
2454
|
+
tombstonedIds = /* @__PURE__ */ new Set();
|
|
2455
|
+
tombstonedClientIds = /* @__PURE__ */ new Map();
|
|
2456
|
+
stickyEphemeralIds = /* @__PURE__ */ new Set();
|
|
2457
|
+
markerArtifacts = /* @__PURE__ */ new Map();
|
|
2458
|
+
lastRestoreWarnings = [];
|
|
2459
|
+
lastRestoreWarningDetails = [];
|
|
2153
2460
|
constructor(options) {
|
|
2154
2461
|
this.sessionId = options.sessionId;
|
|
2155
2462
|
this.workspaceCwd = options.workspaceCwd;
|
|
2156
2463
|
this.maxArtifacts = options.maxArtifacts ?? 200;
|
|
2464
|
+
this.persistence = options.persistence;
|
|
2157
2465
|
}
|
|
2158
2466
|
inputBatchLimit() {
|
|
2159
2467
|
return this.maxArtifacts * 2;
|
|
@@ -2166,13 +2474,30 @@ var SessionArtifactStore = class {
|
|
|
2166
2474
|
sessionId: this.sessionId,
|
|
2167
2475
|
artifacts: Array.from(this.artifacts.values()).sort((a, b) => a.insertSeq - b.insertSeq).map(toPublicArtifact),
|
|
2168
2476
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2169
|
-
limits: { maxArtifacts: this.maxArtifacts }
|
|
2477
|
+
limits: { maxArtifacts: this.maxArtifacts },
|
|
2478
|
+
...this.lastRestoreWarnings.length > 0 ? { warnings: [...this.lastRestoreWarnings] } : {},
|
|
2479
|
+
...this.lastRestoreWarningDetails.length > 0 ? { warningDetails: [...this.lastRestoreWarningDetails] } : {}
|
|
2170
2480
|
};
|
|
2171
2481
|
});
|
|
2172
2482
|
}
|
|
2483
|
+
async get(artifactId) {
|
|
2484
|
+
return this.enqueue(async () => {
|
|
2485
|
+
const artifact = this.artifacts.get(artifactId);
|
|
2486
|
+
if (!artifact) return void 0;
|
|
2487
|
+
if (artifact.workspacePath && shouldRefreshWorkspaceStatus(artifact, Date.now())) {
|
|
2488
|
+
await this.refreshWorkspaceStatus(artifact, { onError: "missing" });
|
|
2489
|
+
}
|
|
2490
|
+
return toPublicArtifact(artifact);
|
|
2491
|
+
});
|
|
2492
|
+
}
|
|
2173
2493
|
async upsertMany(inputs, options = {}) {
|
|
2174
2494
|
return this.enqueue(async () => {
|
|
2495
|
+
const validationStrict = options.validationStrict ?? options.strict;
|
|
2496
|
+
const persistenceStrict = options.persistenceStrict ?? options.strict;
|
|
2497
|
+
const before = this.cloneState();
|
|
2175
2498
|
const normalizedResults = [];
|
|
2499
|
+
const warnings = [];
|
|
2500
|
+
const warningDetails = [];
|
|
2176
2501
|
for (const input of inputs) {
|
|
2177
2502
|
try {
|
|
2178
2503
|
normalizedResults.push(
|
|
@@ -2183,7 +2508,7 @@ var SessionArtifactStore = class {
|
|
|
2183
2508
|
)
|
|
2184
2509
|
);
|
|
2185
2510
|
} catch (error) {
|
|
2186
|
-
if (
|
|
2511
|
+
if (validationStrict) {
|
|
2187
2512
|
throw error;
|
|
2188
2513
|
}
|
|
2189
2514
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -2195,39 +2520,140 @@ var SessionArtifactStore = class {
|
|
|
2195
2520
|
}
|
|
2196
2521
|
}
|
|
2197
2522
|
const changes = [];
|
|
2198
|
-
|
|
2199
|
-
const
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2523
|
+
try {
|
|
2524
|
+
for (const normalized of coalesceByIdentity(normalizedResults)) {
|
|
2525
|
+
const artifact = this.applyStickyEphemeralOverride(normalized);
|
|
2526
|
+
if (this.shouldSuppressTombstonedUpsert(artifact)) {
|
|
2527
|
+
writeStderrLine(
|
|
2528
|
+
`[artifacts] session=${this.sessionId} action=tombstone_replay_suppressed artifactId=${artifact.id}`
|
|
2529
|
+
);
|
|
2530
|
+
continue;
|
|
2531
|
+
}
|
|
2532
|
+
const existing = this.artifacts.get(artifact.id) ?? this.findPublishedUpgradeTarget(artifact) ?? this.findPublishedWorkspaceTarget(artifact);
|
|
2533
|
+
if (!existing) {
|
|
2534
|
+
const stored = {
|
|
2535
|
+
...artifact,
|
|
2536
|
+
insertSeq: ++this.insertSeq
|
|
2537
|
+
};
|
|
2538
|
+
this.artifacts.set(stored.id, stored);
|
|
2539
|
+
changes.push({
|
|
2540
|
+
action: "created",
|
|
2541
|
+
artifactId: stored.id,
|
|
2542
|
+
artifact: toPublicArtifact(stored)
|
|
2543
|
+
});
|
|
2544
|
+
continue;
|
|
2545
|
+
}
|
|
2546
|
+
try {
|
|
2547
|
+
this.denyCrossClientMutation("upsert", existing.id, existing, {
|
|
2548
|
+
clientId: artifact.clientId
|
|
2549
|
+
});
|
|
2550
|
+
} catch (error) {
|
|
2551
|
+
if (validationStrict) {
|
|
2552
|
+
throw error;
|
|
2553
|
+
}
|
|
2554
|
+
if (error instanceof SessionArtifactAuthorizationError) {
|
|
2555
|
+
warnings.push(error.message);
|
|
2556
|
+
continue;
|
|
2557
|
+
}
|
|
2558
|
+
throw error;
|
|
2559
|
+
}
|
|
2560
|
+
const updated = mergeArtifact(existing, artifact);
|
|
2561
|
+
if (updated.changed) {
|
|
2562
|
+
if (updated.artifact.id !== existing.id) {
|
|
2563
|
+
const removeChange = {
|
|
2564
|
+
action: "removed",
|
|
2565
|
+
artifactId: existing.id,
|
|
2566
|
+
artifact: toPublicArtifact(existing),
|
|
2567
|
+
reason: "explicit",
|
|
2568
|
+
durableTombstoneRequired: existing.durableTombstoneRequired || existing.persistedAt !== void 0,
|
|
2569
|
+
removedClientId: existing.clientId
|
|
2570
|
+
};
|
|
2571
|
+
changes.push(removeChange);
|
|
2572
|
+
this.artifacts.delete(existing.id);
|
|
2573
|
+
} else if (shouldRecordEphemeralUnpin(existing, artifact)) {
|
|
2574
|
+
changes.push({
|
|
2575
|
+
action: "removed",
|
|
2576
|
+
artifactId: existing.id,
|
|
2577
|
+
artifact: toPublicArtifact(existing),
|
|
2578
|
+
reason: "unpin_to_ephemeral",
|
|
2579
|
+
durableTombstoneRequired: true
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2582
|
+
this.artifacts.set(updated.artifact.id, updated.artifact);
|
|
2583
|
+
changes.push({
|
|
2584
|
+
action: "updated",
|
|
2585
|
+
artifactId: updated.artifact.id,
|
|
2586
|
+
artifact: toPublicArtifact(updated.artifact)
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2212
2589
|
}
|
|
2213
|
-
const
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2590
|
+
const createdIds = new Set(
|
|
2591
|
+
changes.filter((change) => change.action === "created").map((change) => change.artifactId)
|
|
2592
|
+
);
|
|
2593
|
+
changes.push(
|
|
2594
|
+
...await this.evictOverflow(createdIds, changes, persistenceStrict)
|
|
2595
|
+
);
|
|
2596
|
+
const hasStrictDurableTransition = changes.some(
|
|
2597
|
+
shouldCommitBeforeDurablePersistence
|
|
2598
|
+
);
|
|
2599
|
+
let persistenceWarnings;
|
|
2600
|
+
if (hasStrictDurableTransition && !persistenceStrict) {
|
|
2601
|
+
try {
|
|
2602
|
+
persistenceWarnings = await this.persistChanges(changes, true);
|
|
2603
|
+
} catch (error) {
|
|
2604
|
+
this.restoreState(before);
|
|
2605
|
+
const artifactIds = changes.filter(shouldCommitBeforeDurablePersistence).map((change) => change.artifactId);
|
|
2606
|
+
const warning = "artifact durable removal not persisted; live changes rolled back";
|
|
2607
|
+
writeStderrLine(
|
|
2608
|
+
`[artifacts] session=${this.sessionId} action=upsert_rollback warning=${JSON.stringify(
|
|
2609
|
+
warning
|
|
2610
|
+
)} artifactIds=${JSON.stringify(artifactIds)} error=${JSON.stringify(
|
|
2611
|
+
error instanceof Error ? error.message : String(error)
|
|
2612
|
+
)}`
|
|
2613
|
+
);
|
|
2614
|
+
return {
|
|
2615
|
+
v: 1,
|
|
2616
|
+
sessionId: this.sessionId,
|
|
2617
|
+
changes: [],
|
|
2618
|
+
warnings: [warning],
|
|
2619
|
+
warningDetails: [
|
|
2620
|
+
persistenceFailureDetail({
|
|
2621
|
+
message: warning,
|
|
2622
|
+
operation: "upsert",
|
|
2623
|
+
artifactIds,
|
|
2624
|
+
error
|
|
2625
|
+
})
|
|
2626
|
+
]
|
|
2627
|
+
};
|
|
2217
2628
|
}
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2629
|
+
} else {
|
|
2630
|
+
persistenceWarnings = await this.persistChanges(
|
|
2631
|
+
changes,
|
|
2632
|
+
persistenceStrict
|
|
2633
|
+
);
|
|
2634
|
+
}
|
|
2635
|
+
warnings.push(...persistenceWarnings);
|
|
2636
|
+
warningDetails.push(
|
|
2637
|
+
...detailsForPersistenceWarnings(
|
|
2638
|
+
persistenceWarnings,
|
|
2639
|
+
changes,
|
|
2640
|
+
"upsert"
|
|
2641
|
+
)
|
|
2642
|
+
);
|
|
2643
|
+
stripDurableTombstoneMarkers(changes);
|
|
2644
|
+
} catch (error) {
|
|
2645
|
+
if (validationStrict || persistenceStrict || error instanceof SessionArtifactAuthorizationError) {
|
|
2646
|
+
this.restoreState(before);
|
|
2224
2647
|
}
|
|
2648
|
+
throw error;
|
|
2225
2649
|
}
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2650
|
+
return {
|
|
2651
|
+
v: 1,
|
|
2652
|
+
sessionId: this.sessionId,
|
|
2653
|
+
changes,
|
|
2654
|
+
...warnings.length > 0 ? { warnings } : {},
|
|
2655
|
+
...warningDetails.length > 0 ? { warningDetails } : {}
|
|
2656
|
+
};
|
|
2231
2657
|
});
|
|
2232
2658
|
}
|
|
2233
2659
|
findPublishedUpgradeTarget(artifact) {
|
|
@@ -2235,7 +2661,7 @@ var SessionArtifactStore = class {
|
|
|
2235
2661
|
return void 0;
|
|
2236
2662
|
}
|
|
2237
2663
|
const byUrl = this.artifacts.get(
|
|
2238
|
-
|
|
2664
|
+
stableSessionArtifactId(this.sessionId, `url:${artifact.url}`)
|
|
2239
2665
|
);
|
|
2240
2666
|
if (byUrl && (byUrl.storage !== "published" || byUrl.managedId === artifact.managedId)) {
|
|
2241
2667
|
return byUrl;
|
|
@@ -2271,93 +2697,648 @@ var SessionArtifactStore = class {
|
|
|
2271
2697
|
if (!existing) {
|
|
2272
2698
|
return { v: 1, sessionId: this.sessionId, changes: [] };
|
|
2273
2699
|
}
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2700
|
+
this.denyCrossClientMutation("remove", artifactId, existing, options);
|
|
2701
|
+
const removeChange = {
|
|
2702
|
+
action: "removed",
|
|
2703
|
+
artifactId,
|
|
2704
|
+
artifact: toPublicArtifact(existing),
|
|
2705
|
+
reason: "explicit",
|
|
2706
|
+
durableTombstoneRequired: existing.durableTombstoneRequired || existing.retention !== "ephemeral" ? true : void 0,
|
|
2707
|
+
removedClientId: existing.clientId
|
|
2708
|
+
};
|
|
2709
|
+
const changes = [removeChange];
|
|
2710
|
+
const needsDurableTombstone = removeChange.durableTombstoneRequired === true;
|
|
2711
|
+
if (needsDurableTombstone) {
|
|
2712
|
+
const before = this.cloneState();
|
|
2713
|
+
this.artifacts.delete(artifactId);
|
|
2714
|
+
try {
|
|
2715
|
+
await this.persistChanges(changes, true);
|
|
2716
|
+
} catch (error) {
|
|
2717
|
+
this.restoreState(before);
|
|
2718
|
+
const warning = "artifact removal not persisted; live artifact kept";
|
|
2719
|
+
return {
|
|
2720
|
+
v: 1,
|
|
2721
|
+
sessionId: this.sessionId,
|
|
2722
|
+
changes: [],
|
|
2723
|
+
warnings: [warning],
|
|
2724
|
+
warningDetails: [
|
|
2725
|
+
persistenceFailureDetail({
|
|
2726
|
+
message: warning,
|
|
2727
|
+
operation: "remove",
|
|
2728
|
+
artifactIds: [artifactId],
|
|
2729
|
+
error
|
|
2730
|
+
})
|
|
2731
|
+
]
|
|
2732
|
+
};
|
|
2733
|
+
}
|
|
2279
2734
|
}
|
|
2280
|
-
|
|
2735
|
+
if (!needsDurableTombstone) {
|
|
2736
|
+
this.artifacts.delete(artifactId);
|
|
2737
|
+
}
|
|
2738
|
+
const warnings = needsDurableTombstone ? [] : await this.persistChanges(changes, false);
|
|
2739
|
+
stripDurableTombstoneMarkers(changes);
|
|
2740
|
+
const warningDetails = detailsForPersistenceWarnings(
|
|
2741
|
+
warnings,
|
|
2742
|
+
changes,
|
|
2743
|
+
"remove"
|
|
2744
|
+
);
|
|
2281
2745
|
return {
|
|
2282
2746
|
v: 1,
|
|
2283
2747
|
sessionId: this.sessionId,
|
|
2284
|
-
changes
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
artifactId,
|
|
2288
|
-
artifact: toPublicArtifact(existing),
|
|
2289
|
-
reason: "explicit"
|
|
2290
|
-
}
|
|
2291
|
-
]
|
|
2748
|
+
changes,
|
|
2749
|
+
...warnings.length > 0 ? { warnings } : {},
|
|
2750
|
+
...warningDetails.length > 0 ? { warningDetails } : {}
|
|
2292
2751
|
};
|
|
2293
2752
|
});
|
|
2294
2753
|
}
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
this.
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2754
|
+
async restore(snapshot, options = {}) {
|
|
2755
|
+
if (!snapshot) return [];
|
|
2756
|
+
return this.enqueue(async () => {
|
|
2757
|
+
const warnings = [...snapshot?.warnings ?? []];
|
|
2758
|
+
const baselineWarnings = [...warnings];
|
|
2759
|
+
const previousState = this.cloneState();
|
|
2760
|
+
const preservedLiveEphemeralArtifacts = options.preserveLiveEphemeral ? Array.from(this.artifacts.values()).filter((artifact) => artifact.retention === "ephemeral").map(cloneStoredArtifact) : [];
|
|
2761
|
+
let restoredCount = 0;
|
|
2762
|
+
this.artifacts.clear();
|
|
2763
|
+
this.tombstonedIds.clear();
|
|
2764
|
+
this.tombstonedClientIds.clear();
|
|
2765
|
+
this.stickyEphemeralIds.clear();
|
|
2766
|
+
this.markerArtifacts.clear();
|
|
2767
|
+
if (snapshot) {
|
|
2768
|
+
this.insertSeq = 0;
|
|
2769
|
+
this.persistenceSeq = snapshot.sequence;
|
|
2770
|
+
this.durableEventsSinceSnapshot = 0;
|
|
2771
|
+
this.consecutiveSnapshotFailures = 0;
|
|
2772
|
+
for (const id of snapshot.tombstonedIds) {
|
|
2773
|
+
this.tombstonedIds.add(id);
|
|
2774
|
+
}
|
|
2775
|
+
for (const id of snapshot.stickyEphemeralIds.slice(
|
|
2776
|
+
-MAX_STICKY_EPHEMERAL_IDS
|
|
2777
|
+
)) {
|
|
2778
|
+
this.stickyEphemeralIds.add(id);
|
|
2779
|
+
}
|
|
2780
|
+
const markerIds = /* @__PURE__ */ new Set([
|
|
2781
|
+
...this.tombstonedIds,
|
|
2782
|
+
...this.stickyEphemeralIds
|
|
2783
|
+
]);
|
|
2784
|
+
for (const artifact of snapshot.markerArtifacts ?? []) {
|
|
2785
|
+
if (!markerIds.has(artifact.id)) continue;
|
|
2786
|
+
const markerArtifact = await this.normalizeRestoredMarkerArtifact(
|
|
2787
|
+
artifact,
|
|
2788
|
+
warnings
|
|
2789
|
+
);
|
|
2790
|
+
if (markerArtifact)
|
|
2791
|
+
this.markerArtifacts.set(artifact.id, markerArtifact);
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
for (const artifact of snapshot?.artifacts ?? []) {
|
|
2795
|
+
try {
|
|
2796
|
+
const input = persistedArtifactToInput(artifact);
|
|
2797
|
+
if (input.retention === "pinned") {
|
|
2798
|
+
input.retention = "restorable";
|
|
2799
|
+
warnings.push(
|
|
2800
|
+
`pinned artifact ${artifact.id} downgraded to restorable; runtime does not support pinned retention`
|
|
2801
|
+
);
|
|
2802
|
+
}
|
|
2803
|
+
let normalized = await this.normalizeInput(
|
|
2804
|
+
input,
|
|
2805
|
+
++this.receivedSeq,
|
|
2806
|
+
artifact.storage === "published" && !isFileArtifactUrl(artifact.url),
|
|
2807
|
+
{
|
|
2808
|
+
metadataBudget: "persisted",
|
|
2809
|
+
workspaceExpected: workspaceExpectedFromArtifact(artifact),
|
|
2810
|
+
hashWorkspaceContent: false
|
|
2811
|
+
}
|
|
2812
|
+
);
|
|
2813
|
+
if (this.stickyEphemeralIds.has(normalized.id) && normalized.retention !== "ephemeral") {
|
|
2814
|
+
normalized = {
|
|
2815
|
+
...normalized,
|
|
2816
|
+
retention: "ephemeral",
|
|
2817
|
+
persistenceWarning: "sticky_override_active"
|
|
2818
|
+
};
|
|
2819
|
+
}
|
|
2820
|
+
const stickyApplied = normalized.persistenceWarning === "sticky_override_active";
|
|
2821
|
+
if (normalized.id !== artifact.id) {
|
|
2822
|
+
warnings.push(`skipped artifact with mismatched id ${artifact.id}`);
|
|
2823
|
+
continue;
|
|
2824
|
+
}
|
|
2825
|
+
const retention = normalized.retention;
|
|
2826
|
+
let persistenceWarning;
|
|
2827
|
+
if (stickyApplied) {
|
|
2828
|
+
persistenceWarning = "sticky_override_active";
|
|
2829
|
+
} else if (normalized.status !== "available") {
|
|
2830
|
+
persistenceWarning = "metadata_only_restore";
|
|
2831
|
+
}
|
|
2832
|
+
const stored = {
|
|
2833
|
+
...normalized,
|
|
2834
|
+
retention,
|
|
2835
|
+
clientRetained: artifact.clientRetained,
|
|
2836
|
+
createdAt: artifact.createdAt,
|
|
2837
|
+
updatedAt: artifact.updatedAt,
|
|
2838
|
+
persistedAt: artifact.persistedAt,
|
|
2839
|
+
status: normalized.status,
|
|
2840
|
+
restoreState: "restored",
|
|
2841
|
+
persistenceWarning,
|
|
2842
|
+
durableTombstoneRequired: retention !== "ephemeral" ? true : void 0,
|
|
2843
|
+
insertSeq: ++this.insertSeq
|
|
2844
|
+
};
|
|
2845
|
+
this.artifacts.set(stored.id, stored);
|
|
2846
|
+
restoredCount++;
|
|
2847
|
+
} catch (error) {
|
|
2848
|
+
warnings.push(
|
|
2849
|
+
`skipped artifact restore: ${error instanceof Error ? error.message : String(error)}`
|
|
2850
|
+
);
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
if (snapshot.artifacts.length > 0 && restoredCount === 0) {
|
|
2854
|
+
this.restoreState(previousState);
|
|
2855
|
+
const rollbackWarnings = [
|
|
2856
|
+
...baselineWarnings,
|
|
2857
|
+
`${RESTORE_FAILED_WARNING_PREFIX}; kept existing live artifacts`
|
|
2858
|
+
];
|
|
2859
|
+
this.setLastRestoreWarnings(rollbackWarnings);
|
|
2860
|
+
return rollbackWarnings;
|
|
2861
|
+
}
|
|
2862
|
+
if (previousState.artifacts.size > 0 && baselineWarnings.some(isArtifactSnapshotCompletenessWarning)) {
|
|
2863
|
+
this.restoreState(previousState);
|
|
2864
|
+
const rollbackWarnings = [
|
|
2865
|
+
...baselineWarnings,
|
|
2866
|
+
restoredCount === 0 ? `${RESTORE_FAILED_WARNING_PREFIX}; kept existing live artifacts` : `${RESTORE_PARTIAL_FAILED_WARNING_PREFIX}; kept existing live artifacts`
|
|
2867
|
+
];
|
|
2868
|
+
this.setLastRestoreWarnings(rollbackWarnings);
|
|
2869
|
+
return rollbackWarnings;
|
|
2870
|
+
}
|
|
2871
|
+
for (const artifact of preservedLiveEphemeralArtifacts) {
|
|
2872
|
+
if (this.artifacts.has(artifact.id) || this.tombstonedIds.has(artifact.id)) {
|
|
2873
|
+
continue;
|
|
2874
|
+
}
|
|
2875
|
+
this.artifacts.set(artifact.id, {
|
|
2876
|
+
...artifact,
|
|
2877
|
+
insertSeq: ++this.insertSeq
|
|
2878
|
+
});
|
|
2879
|
+
}
|
|
2880
|
+
const evicted = await this.evictOverflow(/* @__PURE__ */ new Set(), []);
|
|
2881
|
+
if (evicted.length > 0) {
|
|
2882
|
+
warnings.push("restored artifact list pruned to live limit");
|
|
2883
|
+
warnings.push(...await this.persistChanges(evicted, false));
|
|
2884
|
+
}
|
|
2885
|
+
this.setLastRestoreWarnings(warnings);
|
|
2886
|
+
return warnings;
|
|
2887
|
+
});
|
|
2302
2888
|
}
|
|
2303
|
-
async
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
input.description,
|
|
2310
|
-
"description",
|
|
2311
|
-
1e3,
|
|
2312
|
-
false
|
|
2313
|
-
);
|
|
2314
|
-
const source = input.source ?? "tool";
|
|
2315
|
-
if (source !== "tool" && source !== "hook" && source !== "client") {
|
|
2316
|
-
throw new SessionArtifactValidationError(
|
|
2317
|
-
"source must be tool, hook, or client",
|
|
2318
|
-
"source"
|
|
2319
|
-
);
|
|
2889
|
+
async recordSnapshot() {
|
|
2890
|
+
const persistence = this.persistence;
|
|
2891
|
+
if (!persistence) {
|
|
2892
|
+
return [
|
|
2893
|
+
"artifact persistence unavailable; restored artifacts not snapshotted"
|
|
2894
|
+
];
|
|
2320
2895
|
}
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2896
|
+
return this.enqueue(async () => {
|
|
2897
|
+
const recordedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2898
|
+
const sequence = this.persistenceSeq + 1;
|
|
2899
|
+
try {
|
|
2900
|
+
await persistence.recordSnapshot(
|
|
2901
|
+
this.buildSnapshotPayload(recordedAt, sequence)
|
|
2902
|
+
);
|
|
2903
|
+
this.persistenceSeq = sequence;
|
|
2904
|
+
this.durableEventsSinceSnapshot = 0;
|
|
2905
|
+
this.consecutiveSnapshotFailures = 0;
|
|
2906
|
+
return [];
|
|
2907
|
+
} catch (error) {
|
|
2908
|
+
writeStderrLine(
|
|
2909
|
+
`[artifacts] session=${this.sessionId} action=snapshot_failed reason=${JSON.stringify(
|
|
2910
|
+
error instanceof Error ? error.message : String(error)
|
|
2911
|
+
)}`
|
|
2912
|
+
);
|
|
2913
|
+
return ["artifact snapshot not persisted"];
|
|
2914
|
+
}
|
|
2337
2915
|
});
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
"
|
|
2916
|
+
}
|
|
2917
|
+
async normalizeRestoredMarkerArtifact(artifact, warnings) {
|
|
2918
|
+
try {
|
|
2919
|
+
const input = persistedArtifactToInput(artifact);
|
|
2920
|
+
if (input.retention === "pinned") {
|
|
2921
|
+
input.retention = "restorable";
|
|
2922
|
+
warnings.push(
|
|
2923
|
+
`pinned marker artifact ${artifact.id} downgraded to restorable; runtime does not support pinned retention`
|
|
2924
|
+
);
|
|
2925
|
+
}
|
|
2926
|
+
const normalized = await this.normalizeInput(
|
|
2927
|
+
input,
|
|
2928
|
+
++this.receivedSeq,
|
|
2929
|
+
artifact.storage === "published" && !isFileArtifactUrl(artifact.url),
|
|
2930
|
+
{
|
|
2931
|
+
metadataBudget: "persisted",
|
|
2932
|
+
workspaceExpected: workspaceExpectedFromArtifact(artifact),
|
|
2933
|
+
hashWorkspaceContent: false
|
|
2934
|
+
}
|
|
2935
|
+
);
|
|
2936
|
+
if (normalized.id !== artifact.id) {
|
|
2937
|
+
warnings.push(
|
|
2938
|
+
`skipped marker artifact with mismatched id ${artifact.id}`
|
|
2939
|
+
);
|
|
2940
|
+
return void 0;
|
|
2941
|
+
}
|
|
2942
|
+
return toPersistedArtifact(
|
|
2943
|
+
{
|
|
2944
|
+
...normalized,
|
|
2945
|
+
clientRetained: artifact.clientRetained,
|
|
2946
|
+
createdAt: artifact.createdAt,
|
|
2947
|
+
updatedAt: artifact.updatedAt,
|
|
2948
|
+
persistedAt: artifact.persistedAt
|
|
2949
|
+
},
|
|
2950
|
+
artifact.persistedAt ?? artifact.updatedAt
|
|
2951
|
+
);
|
|
2952
|
+
} catch (error) {
|
|
2953
|
+
warnings.push(
|
|
2954
|
+
`skipped marker artifact ${artifact.id}: ${error instanceof Error ? error.message : String(error)}`
|
|
2344
2955
|
);
|
|
2956
|
+
return void 0;
|
|
2345
2957
|
}
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2958
|
+
}
|
|
2959
|
+
cloneState() {
|
|
2960
|
+
return {
|
|
2961
|
+
artifacts: new Map(
|
|
2962
|
+
Array.from(this.artifacts.entries()).map(([id, artifact]) => [
|
|
2963
|
+
id,
|
|
2964
|
+
cloneStoredArtifact(artifact)
|
|
2965
|
+
])
|
|
2966
|
+
),
|
|
2967
|
+
receivedSeq: this.receivedSeq,
|
|
2968
|
+
insertSeq: this.insertSeq,
|
|
2969
|
+
persistenceSeq: this.persistenceSeq,
|
|
2970
|
+
durableEventsSinceSnapshot: this.durableEventsSinceSnapshot,
|
|
2971
|
+
consecutiveSnapshotFailures: this.consecutiveSnapshotFailures,
|
|
2972
|
+
tombstonedIds: new Set(this.tombstonedIds),
|
|
2973
|
+
tombstonedClientIds: new Map(this.tombstonedClientIds),
|
|
2974
|
+
stickyEphemeralIds: new Set(this.stickyEphemeralIds),
|
|
2975
|
+
markerArtifacts: new Map(this.markerArtifacts),
|
|
2976
|
+
lastRestoreWarnings: [...this.lastRestoreWarnings],
|
|
2977
|
+
lastRestoreWarningDetails: [...this.lastRestoreWarningDetails]
|
|
2978
|
+
};
|
|
2979
|
+
}
|
|
2980
|
+
restoreState(state) {
|
|
2981
|
+
this.artifacts.clear();
|
|
2982
|
+
for (const [id, artifact] of state.artifacts) {
|
|
2983
|
+
this.artifacts.set(id, cloneStoredArtifact(artifact));
|
|
2984
|
+
}
|
|
2985
|
+
this.receivedSeq = state.receivedSeq;
|
|
2986
|
+
this.insertSeq = state.insertSeq;
|
|
2987
|
+
this.persistenceSeq = state.persistenceSeq;
|
|
2988
|
+
this.durableEventsSinceSnapshot = state.durableEventsSinceSnapshot;
|
|
2989
|
+
this.consecutiveSnapshotFailures = state.consecutiveSnapshotFailures;
|
|
2990
|
+
this.tombstonedIds.clear();
|
|
2991
|
+
for (const id of state.tombstonedIds) {
|
|
2992
|
+
this.tombstonedIds.add(id);
|
|
2993
|
+
}
|
|
2994
|
+
this.tombstonedClientIds.clear();
|
|
2995
|
+
for (const [id, clientId] of state.tombstonedClientIds) {
|
|
2996
|
+
this.tombstonedClientIds.set(id, clientId);
|
|
2997
|
+
}
|
|
2998
|
+
this.stickyEphemeralIds.clear();
|
|
2999
|
+
for (const id of state.stickyEphemeralIds) {
|
|
3000
|
+
this.stickyEphemeralIds.add(id);
|
|
3001
|
+
}
|
|
3002
|
+
this.markerArtifacts.clear();
|
|
3003
|
+
for (const [id, artifact] of state.markerArtifacts) {
|
|
3004
|
+
this.markerArtifacts.set(id, artifact);
|
|
3005
|
+
}
|
|
3006
|
+
this.setLastRestoreWarnings(state.lastRestoreWarnings);
|
|
3007
|
+
this.lastRestoreWarningDetails = [...state.lastRestoreWarningDetails];
|
|
3008
|
+
}
|
|
3009
|
+
async persistChanges(changes, strict = false) {
|
|
3010
|
+
const durableChanges = changes.filter(isDurablePersistenceChange);
|
|
3011
|
+
if (durableChanges.length === 0) {
|
|
3012
|
+
return [];
|
|
3013
|
+
}
|
|
3014
|
+
if (!this.persistence) {
|
|
3015
|
+
if (strict) {
|
|
3016
|
+
throw new SessionArtifactValidationError(
|
|
3017
|
+
"artifact persistence is unavailable",
|
|
3018
|
+
"retention"
|
|
3019
|
+
);
|
|
3020
|
+
}
|
|
3021
|
+
const warnings = this.downgradeDurableChanges(durableChanges);
|
|
3022
|
+
this.applyDurableMarkers(durableChanges);
|
|
3023
|
+
return warnings;
|
|
3024
|
+
}
|
|
3025
|
+
const recordedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3026
|
+
for (const change of durableChanges) {
|
|
3027
|
+
if (change.action === "removed") continue;
|
|
3028
|
+
const stored = this.artifacts.get(change.artifactId);
|
|
3029
|
+
if (!stored) continue;
|
|
3030
|
+
const artifact = {
|
|
3031
|
+
...toPublicArtifact(stored),
|
|
3032
|
+
persistedAt: recordedAt
|
|
3033
|
+
};
|
|
3034
|
+
change.artifact = artifact;
|
|
3035
|
+
}
|
|
3036
|
+
const sequence = this.persistenceSeq + 1;
|
|
3037
|
+
const payload = {
|
|
3038
|
+
v: SESSION_ARTIFACT_PERSISTENCE_VERSION,
|
|
3039
|
+
sessionId: this.sessionId,
|
|
3040
|
+
sequence,
|
|
3041
|
+
recordedAt,
|
|
3042
|
+
changes: durableChanges.map(
|
|
3043
|
+
(change) => toPersistedChange(change, recordedAt)
|
|
3044
|
+
)
|
|
3045
|
+
};
|
|
3046
|
+
try {
|
|
3047
|
+
await this.persistence.recordEvent(payload);
|
|
3048
|
+
this.persistenceSeq = sequence;
|
|
3049
|
+
for (const change of durableChanges) {
|
|
3050
|
+
if (change.action === "removed") continue;
|
|
3051
|
+
const stored = this.artifacts.get(change.artifactId);
|
|
3052
|
+
if (!stored) continue;
|
|
3053
|
+
stored.persistedAt = recordedAt;
|
|
3054
|
+
stored.durableTombstoneRequired = true;
|
|
3055
|
+
change.artifact = toPublicArtifact(stored);
|
|
3056
|
+
}
|
|
3057
|
+
this.applyDurableMarkers(durableChanges);
|
|
3058
|
+
await this.maybeRecordSnapshot(recordedAt);
|
|
3059
|
+
return [];
|
|
3060
|
+
} catch (error) {
|
|
3061
|
+
if (strict) {
|
|
3062
|
+
throw error;
|
|
3063
|
+
}
|
|
3064
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
3065
|
+
const artifactIds = durableChanges.map((change) => change.artifactId);
|
|
3066
|
+
writeStderrLine(
|
|
3067
|
+
`[artifacts] session=${this.sessionId} action=persist_failed sequence=${payload.sequence} artifactIds=${JSON.stringify(
|
|
3068
|
+
artifactIds
|
|
3069
|
+
)} reason=${JSON.stringify(reason)}`
|
|
3070
|
+
);
|
|
3071
|
+
const warnings = this.downgradeDurableChanges(durableChanges);
|
|
3072
|
+
this.applyDurableMarkers(durableChanges);
|
|
3073
|
+
return warnings;
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
async maybeRecordSnapshot(recordedAt) {
|
|
3077
|
+
if (!this.persistence) return;
|
|
3078
|
+
this.durableEventsSinceSnapshot++;
|
|
3079
|
+
const snapshotThreshold = SNAPSHOT_AFTER_DURABLE_EVENTS * Math.min(
|
|
3080
|
+
MAX_SNAPSHOT_BACKOFF_MULTIPLIER,
|
|
3081
|
+
2 ** this.consecutiveSnapshotFailures
|
|
3082
|
+
);
|
|
3083
|
+
if (this.durableEventsSinceSnapshot < snapshotThreshold) {
|
|
3084
|
+
return;
|
|
3085
|
+
}
|
|
3086
|
+
const sequence = this.persistenceSeq + 1;
|
|
3087
|
+
try {
|
|
3088
|
+
await this.persistence.recordSnapshot(
|
|
3089
|
+
this.buildSnapshotPayload(recordedAt, sequence)
|
|
3090
|
+
);
|
|
3091
|
+
this.persistenceSeq = sequence;
|
|
3092
|
+
this.durableEventsSinceSnapshot = 0;
|
|
3093
|
+
this.consecutiveSnapshotFailures = 0;
|
|
3094
|
+
} catch (error) {
|
|
3095
|
+
this.consecutiveSnapshotFailures = Math.min(
|
|
3096
|
+
this.consecutiveSnapshotFailures + 1,
|
|
3097
|
+
Math.log2(MAX_SNAPSHOT_BACKOFF_MULTIPLIER)
|
|
3098
|
+
);
|
|
3099
|
+
writeStderrLine(
|
|
3100
|
+
`[artifacts] session=${this.sessionId} action=snapshot_failed reason=${JSON.stringify(
|
|
3101
|
+
error instanceof Error ? error.message : String(error)
|
|
3102
|
+
)}`
|
|
3103
|
+
);
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
buildSnapshotPayload(recordedAt, sequence) {
|
|
3107
|
+
const artifacts = Array.from(this.artifacts.values()).filter((artifact) => artifact.retention !== "ephemeral").sort((a, b) => a.insertSeq - b.insertSeq).map((artifact) => toPersistedArtifact(artifact, recordedAt));
|
|
3108
|
+
const stickyEphemeralIds = Array.from(this.stickyEphemeralIds).filter(
|
|
3109
|
+
(id) => this.artifacts.has(id) || this.markerArtifacts.has(id)
|
|
3110
|
+
);
|
|
3111
|
+
const markerArtifacts = this.buildMarkerArtifacts(
|
|
3112
|
+
recordedAt,
|
|
3113
|
+
stickyEphemeralIds
|
|
3114
|
+
);
|
|
3115
|
+
return {
|
|
3116
|
+
v: SESSION_ARTIFACT_PERSISTENCE_VERSION,
|
|
3117
|
+
sessionId: this.sessionId,
|
|
3118
|
+
sequence,
|
|
3119
|
+
recordedAt,
|
|
3120
|
+
artifacts,
|
|
3121
|
+
tombstonedIds: Array.from(this.tombstonedIds),
|
|
3122
|
+
stickyEphemeralIds,
|
|
3123
|
+
...markerArtifacts.length > 0 ? { markerArtifacts } : {}
|
|
3124
|
+
};
|
|
3125
|
+
}
|
|
3126
|
+
buildMarkerArtifacts(recordedAt, stickyEphemeralIds) {
|
|
3127
|
+
const markerIds = [...this.tombstonedIds, ...stickyEphemeralIds];
|
|
3128
|
+
const artifacts = [];
|
|
3129
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3130
|
+
for (const id of markerIds) {
|
|
3131
|
+
if (seen.has(id)) continue;
|
|
3132
|
+
seen.add(id);
|
|
3133
|
+
const live = this.artifacts.get(id);
|
|
3134
|
+
if (live) {
|
|
3135
|
+
artifacts.push(toPersistedArtifact(toPublicArtifact(live), recordedAt));
|
|
3136
|
+
continue;
|
|
3137
|
+
}
|
|
3138
|
+
const markerArtifact = this.markerArtifacts.get(id);
|
|
3139
|
+
if (markerArtifact) {
|
|
3140
|
+
artifacts.push(markerArtifact);
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
return artifacts;
|
|
3144
|
+
}
|
|
3145
|
+
downgradeDurableChanges(changes) {
|
|
3146
|
+
let downgraded = false;
|
|
3147
|
+
let removalNotPersisted = false;
|
|
3148
|
+
for (const change of changes) {
|
|
3149
|
+
if (change.action === "removed") {
|
|
3150
|
+
removalNotPersisted = true;
|
|
3151
|
+
if (change.reason === "explicit") {
|
|
3152
|
+
this.rememberTombstone(change);
|
|
3153
|
+
this.stickyEphemeralIds.delete(change.artifactId);
|
|
3154
|
+
}
|
|
3155
|
+
continue;
|
|
3156
|
+
}
|
|
3157
|
+
const stored = this.artifacts.get(change.artifactId);
|
|
3158
|
+
if (!stored) continue;
|
|
3159
|
+
stored.retention = "ephemeral";
|
|
3160
|
+
stored.persistenceWarning = "persistence_unavailable";
|
|
3161
|
+
stored.durableTombstoneRequired = stored.durableTombstoneRequired || stored.persistedAt !== void 0;
|
|
3162
|
+
delete stored.persistedAt;
|
|
3163
|
+
change.artifact = toPublicArtifact(stored);
|
|
3164
|
+
downgraded = true;
|
|
3165
|
+
}
|
|
3166
|
+
const warnings = [];
|
|
3167
|
+
if (downgraded) {
|
|
3168
|
+
warnings.push(
|
|
3169
|
+
"artifact persistence unavailable; durable artifacts kept ephemeral"
|
|
3170
|
+
);
|
|
3171
|
+
}
|
|
3172
|
+
if (removalNotPersisted) {
|
|
3173
|
+
warnings.push("artifact removal not persisted; live removal kept");
|
|
3174
|
+
}
|
|
3175
|
+
return warnings;
|
|
3176
|
+
}
|
|
3177
|
+
applyDurableMarkers(changes) {
|
|
3178
|
+
for (const change of changes) {
|
|
3179
|
+
if (change.action === "removed") {
|
|
3180
|
+
if (change.reason === "explicit") {
|
|
3181
|
+
this.rememberTombstone(change);
|
|
3182
|
+
this.stickyEphemeralIds.delete(change.artifactId);
|
|
3183
|
+
} else if (change.reason === "eviction") {
|
|
3184
|
+
this.stickyEphemeralIds.delete(change.artifactId);
|
|
3185
|
+
this.markerArtifacts.delete(change.artifactId);
|
|
3186
|
+
} else if (change.reason === "unpin_to_ephemeral") {
|
|
3187
|
+
this.rememberStickyEphemeral(change);
|
|
3188
|
+
}
|
|
3189
|
+
continue;
|
|
3190
|
+
}
|
|
3191
|
+
if (change.artifact && change.artifact.retention !== "ephemeral") {
|
|
3192
|
+
this.tombstonedIds.delete(change.artifactId);
|
|
3193
|
+
this.tombstonedClientIds.delete(change.artifactId);
|
|
3194
|
+
this.stickyEphemeralIds.delete(change.artifactId);
|
|
3195
|
+
this.markerArtifacts.delete(change.artifactId);
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
rememberStickyEphemeral(change) {
|
|
3200
|
+
const artifactId = change.artifactId;
|
|
3201
|
+
this.stickyEphemeralIds.delete(artifactId);
|
|
3202
|
+
this.stickyEphemeralIds.add(artifactId);
|
|
3203
|
+
if (change.artifact) {
|
|
3204
|
+
this.markerArtifacts.set(
|
|
3205
|
+
artifactId,
|
|
3206
|
+
toPersistedArtifact(change.artifact, change.artifact.updatedAt)
|
|
3207
|
+
);
|
|
3208
|
+
}
|
|
3209
|
+
while (this.stickyEphemeralIds.size > MAX_STICKY_EPHEMERAL_IDS) {
|
|
3210
|
+
const oldest2 = this.stickyEphemeralIds.values().next().value;
|
|
3211
|
+
if (oldest2 === void 0) break;
|
|
3212
|
+
this.stickyEphemeralIds.delete(oldest2);
|
|
3213
|
+
this.markerArtifacts.delete(oldest2);
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
3216
|
+
rememberTombstone(change) {
|
|
3217
|
+
this.tombstonedIds.delete(change.artifactId);
|
|
3218
|
+
this.tombstonedIds.add(change.artifactId);
|
|
3219
|
+
this.tombstonedClientIds.set(
|
|
3220
|
+
change.artifactId,
|
|
3221
|
+
change.removedClientId ?? this.artifacts.get(change.artifactId)?.clientId
|
|
3222
|
+
);
|
|
3223
|
+
if (change.artifact) {
|
|
3224
|
+
this.markerArtifacts.set(
|
|
3225
|
+
change.artifactId,
|
|
3226
|
+
toPersistedArtifact(change.artifact, change.artifact.updatedAt)
|
|
3227
|
+
);
|
|
3228
|
+
}
|
|
3229
|
+
while (this.tombstonedIds.size > MAX_TOMBSTONED_IDS) {
|
|
3230
|
+
const oldest2 = this.tombstonedIds.values().next().value;
|
|
3231
|
+
if (oldest2 === void 0) break;
|
|
3232
|
+
writeStderrLine(
|
|
3233
|
+
`[artifacts] session=${this.sessionId} action=tombstone_evicted artifactId=${JSON.stringify(
|
|
3234
|
+
oldest2
|
|
3235
|
+
)} limit=${MAX_TOMBSTONED_IDS}`
|
|
3236
|
+
);
|
|
3237
|
+
this.tombstonedIds.delete(oldest2);
|
|
3238
|
+
this.tombstonedClientIds.delete(oldest2);
|
|
3239
|
+
this.markerArtifacts.delete(oldest2);
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
setLastRestoreWarnings(warnings) {
|
|
3243
|
+
this.lastRestoreWarnings = [...warnings];
|
|
3244
|
+
this.lastRestoreWarningDetails = detailsForRestoreWarnings(warnings);
|
|
3245
|
+
}
|
|
3246
|
+
enqueue(operation) {
|
|
3247
|
+
const result = this.operationQueue.then(operation, operation);
|
|
3248
|
+
this.operationQueue = result.then(
|
|
3249
|
+
() => void 0,
|
|
3250
|
+
() => void 0
|
|
3251
|
+
);
|
|
3252
|
+
return result;
|
|
3253
|
+
}
|
|
3254
|
+
denyCrossClientMutation(action, artifactId, existing, options) {
|
|
3255
|
+
if (existing.source !== "client" || existing.clientId === void 0 || existing.clientId === options?.clientId) {
|
|
3256
|
+
return;
|
|
3257
|
+
}
|
|
3258
|
+
writeStderrLine(
|
|
3259
|
+
`[artifacts] session=${this.sessionId} action=${action}_denied artifactId=${artifactId} owner=${existing.clientId} requester=${options?.clientId ?? "<anonymous>"}`
|
|
3260
|
+
);
|
|
3261
|
+
throw new SessionArtifactAuthorizationError(
|
|
3262
|
+
this.sessionId,
|
|
3263
|
+
artifactId,
|
|
3264
|
+
existing.clientId,
|
|
3265
|
+
options?.clientId
|
|
3266
|
+
);
|
|
3267
|
+
}
|
|
3268
|
+
async normalizeInput(input, receivedSeq, trustedPublisherFromCaller, options = {}) {
|
|
3269
|
+
if (!input || typeof input !== "object") {
|
|
3270
|
+
throw new SessionArtifactValidationError("Artifact must be an object");
|
|
3271
|
+
}
|
|
3272
|
+
const title = normalizeString(input.title, "title", 200, true);
|
|
3273
|
+
const description = normalizeString(
|
|
3274
|
+
input.description,
|
|
3275
|
+
"description",
|
|
3276
|
+
1e3,
|
|
3277
|
+
false
|
|
3278
|
+
);
|
|
3279
|
+
const source = input.source ?? "tool";
|
|
3280
|
+
if (source !== "tool" && source !== "hook" && source !== "client") {
|
|
3281
|
+
throw new SessionArtifactValidationError(
|
|
3282
|
+
"source must be tool, hook, or client",
|
|
3283
|
+
"source"
|
|
3284
|
+
);
|
|
3285
|
+
}
|
|
3286
|
+
const trustedPublisher = trustedPublisherFromCaller;
|
|
3287
|
+
const workspacePath = input.workspacePath ? normalizeWorkspacePath(
|
|
3288
|
+
input.workspacePath,
|
|
3289
|
+
await this.getRealWorkspaceCwdForValidation()
|
|
3290
|
+
) : void 0;
|
|
3291
|
+
const managedId = normalizeManagedId(input.managedId);
|
|
3292
|
+
const rawStorage = input.storage;
|
|
3293
|
+
const storage = inferStorage(rawStorage, {
|
|
3294
|
+
workspacePath,
|
|
3295
|
+
managedId,
|
|
3296
|
+
url: input.url,
|
|
3297
|
+
trustedPublisher
|
|
3298
|
+
});
|
|
3299
|
+
const url = input.url ? normalizeArtifactUrl(input.url, trustedPublisher) : void 0;
|
|
3300
|
+
validateLocator(storage, {
|
|
3301
|
+
workspacePath,
|
|
3302
|
+
managedId,
|
|
3303
|
+
url,
|
|
3304
|
+
trustedPublisher
|
|
3305
|
+
});
|
|
3306
|
+
const retention = normalizeRetention(input.retention, {
|
|
3307
|
+
persistenceAvailable: this.persistence !== void 0
|
|
3308
|
+
});
|
|
3309
|
+
const workspaceStatus = workspacePath ? await this.getInitialWorkspaceStatus(
|
|
3310
|
+
workspacePath,
|
|
3311
|
+
options.workspaceExpected,
|
|
3312
|
+
{ hashContent: options.hashWorkspaceContent !== false }
|
|
3313
|
+
) : void 0;
|
|
3314
|
+
if (workspaceStatus?.escaped) {
|
|
3315
|
+
throw new SessionArtifactValidationError(
|
|
3316
|
+
"workspacePath must stay inside the workspace",
|
|
3317
|
+
"workspacePath"
|
|
3318
|
+
);
|
|
3319
|
+
}
|
|
3320
|
+
const metadata = withWorkspaceContentHashMetadata(
|
|
3321
|
+
normalizeMetadata(input.metadata, {
|
|
3322
|
+
budget: options.metadataBudget ?? "user"
|
|
3323
|
+
}),
|
|
3324
|
+
workspaceStatus
|
|
3325
|
+
);
|
|
3326
|
+
const kind = normalizeKind(
|
|
3327
|
+
input.kind ?? inferKind({ storage, workspacePath, url })
|
|
3328
|
+
);
|
|
3329
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3330
|
+
const identityKey = buildIdentityKey({
|
|
3331
|
+
storage,
|
|
3332
|
+
workspacePath,
|
|
2353
3333
|
managedId,
|
|
2354
3334
|
url
|
|
2355
3335
|
});
|
|
2356
|
-
const id =
|
|
3336
|
+
const id = stableSessionArtifactId(this.sessionId, identityKey);
|
|
2357
3337
|
return {
|
|
2358
3338
|
id,
|
|
2359
3339
|
identityKey,
|
|
2360
3340
|
receivedSeq,
|
|
3341
|
+
retentionExplicit: input.retention !== void 0,
|
|
2361
3342
|
retentionSource: source,
|
|
2362
3343
|
trustedPublisher,
|
|
2363
3344
|
kind,
|
|
@@ -2371,9 +3352,12 @@ var SessionArtifactStore = class {
|
|
|
2371
3352
|
managedId,
|
|
2372
3353
|
url,
|
|
2373
3354
|
mimeType: normalizeString(input.mimeType, "mimeType", 120, false),
|
|
2374
|
-
sizeBytes: input.sizeBytes !== void 0 ? normalizeSizeBytes(input.sizeBytes) :
|
|
3355
|
+
sizeBytes: workspaceStatus?.sizeBytes !== void 0 ? workspaceStatus.sizeBytes : input.sizeBytes !== void 0 ? normalizeSizeBytes(input.sizeBytes) : void 0,
|
|
2375
3356
|
metadata,
|
|
2376
|
-
|
|
3357
|
+
retention,
|
|
3358
|
+
restoreState: "live",
|
|
3359
|
+
...this.persistence === void 0 && retention !== "ephemeral" ? { persistenceWarning: "persistence_unavailable" } : {},
|
|
3360
|
+
clientRetained: source === "client" && (input.clientRetained !== void 0 ? input.clientRetained === true : true),
|
|
2377
3361
|
createdAt: now,
|
|
2378
3362
|
updatedAt: now,
|
|
2379
3363
|
toolCallId: normalizeString(input.toolCallId, "toolCallId", 200, false),
|
|
@@ -2387,6 +3371,16 @@ var SessionArtifactStore = class {
|
|
|
2387
3371
|
clientId: normalizeString(input.clientId, "clientId", 200, false)
|
|
2388
3372
|
};
|
|
2389
3373
|
}
|
|
3374
|
+
shouldSuppressTombstonedUpsert(artifact) {
|
|
3375
|
+
if (!this.tombstonedIds.has(artifact.id)) {
|
|
3376
|
+
return false;
|
|
3377
|
+
}
|
|
3378
|
+
const tombstonedClientId = this.tombstonedClientIds.get(artifact.id);
|
|
3379
|
+
if (artifact.source !== "client") {
|
|
3380
|
+
return false;
|
|
3381
|
+
}
|
|
3382
|
+
return !(artifact.retentionExplicit && artifact.clientId !== void 0 && artifact.clientId === tombstonedClientId);
|
|
3383
|
+
}
|
|
2390
3384
|
async refreshWorkspaceStatuses() {
|
|
2391
3385
|
const now = Date.now();
|
|
2392
3386
|
const staleWorkspaceArtifacts = Array.from(this.artifacts.values()).filter((artifact) => artifact.workspacePath).filter((artifact) => shouldRefreshWorkspaceStatus(artifact, now));
|
|
@@ -2399,12 +3393,24 @@ var SessionArtifactStore = class {
|
|
|
2399
3393
|
})
|
|
2400
3394
|
);
|
|
2401
3395
|
}
|
|
2402
|
-
|
|
3396
|
+
applyStickyEphemeralOverride(artifact) {
|
|
3397
|
+
if (!this.stickyEphemeralIds.has(artifact.id) || artifact.retentionExplicit || artifact.retention === "ephemeral") {
|
|
3398
|
+
return artifact;
|
|
3399
|
+
}
|
|
3400
|
+
return {
|
|
3401
|
+
...artifact,
|
|
3402
|
+
retention: "ephemeral",
|
|
3403
|
+
retentionExplicit: true,
|
|
3404
|
+
persistenceWarning: "sticky_override_active"
|
|
3405
|
+
};
|
|
3406
|
+
}
|
|
3407
|
+
async getInitialWorkspaceStatus(workspacePath, expected, options = { hashContent: true }) {
|
|
2403
3408
|
try {
|
|
2404
3409
|
return await getWorkspaceStatus(
|
|
2405
|
-
this.workspaceCwd,
|
|
2406
3410
|
workspacePath,
|
|
2407
|
-
this.getRealWorkspaceCwd()
|
|
3411
|
+
this.getRealWorkspaceCwd(),
|
|
3412
|
+
expected,
|
|
3413
|
+
options
|
|
2408
3414
|
);
|
|
2409
3415
|
} catch (error) {
|
|
2410
3416
|
const reason = error instanceof Error ? error.message : String(error);
|
|
@@ -2420,16 +3426,23 @@ var SessionArtifactStore = class {
|
|
|
2420
3426
|
}
|
|
2421
3427
|
try {
|
|
2422
3428
|
const status = await getWorkspaceStatus(
|
|
2423
|
-
this.workspaceCwd,
|
|
2424
3429
|
artifact.workspacePath,
|
|
2425
|
-
this.getRealWorkspaceCwd()
|
|
3430
|
+
this.getRealWorkspaceCwd(),
|
|
3431
|
+
{
|
|
3432
|
+
sizeBytes: artifact.sizeBytes,
|
|
3433
|
+
mtimeMs: artifact.metadata?.[WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY],
|
|
3434
|
+
sha256: artifact.metadata?.[WORKSPACE_CONTENT_SHA256_METADATA_KEY]
|
|
3435
|
+
}
|
|
2426
3436
|
);
|
|
2427
|
-
|
|
2428
|
-
artifact.
|
|
3437
|
+
const changed = isWorkspaceContentChanged(artifact, status);
|
|
3438
|
+
artifact.status = changed ? "changed" : status.status;
|
|
3439
|
+
if (!changed) {
|
|
3440
|
+
artifact.sizeBytes = status.sizeBytes;
|
|
3441
|
+
}
|
|
2429
3442
|
if (status.escaped) {
|
|
2430
3443
|
artifact.status = "missing";
|
|
2431
3444
|
artifact.sizeBytes = void 0;
|
|
2432
|
-
|
|
3445
|
+
artifact.hideWorkspacePath = true;
|
|
2433
3446
|
}
|
|
2434
3447
|
artifact.lastStatAt = options.now ?? Date.now();
|
|
2435
3448
|
} catch (error) {
|
|
@@ -2458,7 +3471,18 @@ var SessionArtifactStore = class {
|
|
|
2458
3471
|
}
|
|
2459
3472
|
return this.realWorkspaceCwdPromise;
|
|
2460
3473
|
}
|
|
2461
|
-
async
|
|
3474
|
+
async getRealWorkspaceCwdForValidation() {
|
|
3475
|
+
try {
|
|
3476
|
+
return await this.getRealWorkspaceCwd();
|
|
3477
|
+
} catch (error) {
|
|
3478
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
3479
|
+
throw new SessionArtifactValidationError(
|
|
3480
|
+
`workspacePath could not be inspected: ${reason}`,
|
|
3481
|
+
"workspacePath"
|
|
3482
|
+
);
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
async evictOverflow(createdIds, changes, strict = false) {
|
|
2462
3486
|
const removed = [];
|
|
2463
3487
|
if (this.artifacts.size <= this.maxArtifacts) {
|
|
2464
3488
|
return removed;
|
|
@@ -2486,10 +3510,17 @@ var SessionArtifactStore = class {
|
|
|
2486
3510
|
action: "removed",
|
|
2487
3511
|
artifactId: artifact.id,
|
|
2488
3512
|
artifact: toPublicArtifact(artifact),
|
|
3513
|
+
durableTombstoneRequired: artifact.durableTombstoneRequired,
|
|
2489
3514
|
reason: "eviction"
|
|
2490
3515
|
});
|
|
2491
3516
|
}
|
|
2492
3517
|
const overflowCreated = Array.from(this.artifacts.values()).filter((artifact) => createdInThisBatch.has(artifact.id)).sort((a, b) => b.receivedSeq - a.receivedSeq);
|
|
3518
|
+
if (strict && overflowCreated.length > 0 && this.artifacts.size > this.maxArtifacts) {
|
|
3519
|
+
throw new SessionArtifactValidationError(
|
|
3520
|
+
"artifact store is full; no eviction candidate is available",
|
|
3521
|
+
"artifactId"
|
|
3522
|
+
);
|
|
3523
|
+
}
|
|
2493
3524
|
for (const artifact of overflowCreated) {
|
|
2494
3525
|
if (this.artifacts.size <= this.maxArtifacts) {
|
|
2495
3526
|
break;
|
|
@@ -2538,7 +3569,12 @@ function mergeBatchArtifact(existing, next) {
|
|
|
2538
3569
|
trustedPublisher: true,
|
|
2539
3570
|
createdAt: existing.createdAt,
|
|
2540
3571
|
receivedSeq: existing.receivedSeq,
|
|
3572
|
+
retentionExplicit: existing.retentionExplicit || next.retentionExplicit,
|
|
2541
3573
|
retentionSource: existing.retentionSource,
|
|
3574
|
+
retention: mergeRetention(existing, next),
|
|
3575
|
+
restoreState: "live",
|
|
3576
|
+
persistenceWarning: existing.persistenceWarning ?? next.persistenceWarning,
|
|
3577
|
+
persistedAt: existing.persistedAt ?? next.persistedAt,
|
|
2542
3578
|
clientRetained: existing.clientRetained || next.clientRetained,
|
|
2543
3579
|
lastStatAt: void 0
|
|
2544
3580
|
};
|
|
@@ -2552,6 +3588,8 @@ function mergeBatchArtifact(existing, next) {
|
|
|
2552
3588
|
metadata: mergeMetadata(existing, next),
|
|
2553
3589
|
clientRetained: existing.clientRetained || next.clientRetained,
|
|
2554
3590
|
trustedPublisher: existing.trustedPublisher || next.trustedPublisher,
|
|
3591
|
+
retentionExplicit: existing.retentionExplicit || next.retentionExplicit,
|
|
3592
|
+
retention: mergeRetention(existing, next),
|
|
2555
3593
|
lastStatAt: next.lastStatAt ?? existing.lastStatAt
|
|
2556
3594
|
};
|
|
2557
3595
|
}
|
|
@@ -2571,9 +3609,14 @@ function mergeArtifact(existing, incoming) {
|
|
|
2571
3609
|
managedId: publishedUpdate ? incoming.managedId ?? existing.managedId : existing.managedId,
|
|
2572
3610
|
url: publishedUpdate ? incoming.url ?? existing.url : existing.url,
|
|
2573
3611
|
workspacePath: publishedUpdate || existing.storage === "published" ? void 0 : existing.workspacePath ?? incoming.workspacePath,
|
|
3612
|
+
hideWorkspacePath: publishedUpdate || existing.storage === "published" || incoming.workspacePath ? void 0 : existing.hideWorkspacePath,
|
|
2574
3613
|
mimeType: publishedUpdate ? incoming.mimeType ?? existing.mimeType : existing.mimeType,
|
|
2575
3614
|
sizeBytes: existing.storage === "published" && !publishedUpdate ? existing.sizeBytes : mergeSizeBytes(existing, incoming),
|
|
2576
3615
|
metadata: existing.storage === "published" && !publishedUpdate ? existing.metadata : mergeMetadata(existing, incoming),
|
|
3616
|
+
retention: mergeRetention(existing, incoming),
|
|
3617
|
+
restoreState: "live",
|
|
3618
|
+
persistenceWarning: incoming.retentionExplicit && incoming.retention !== "ephemeral" ? incoming.persistenceWarning : existing.persistenceWarning ?? incoming.persistenceWarning,
|
|
3619
|
+
persistedAt: incoming.retentionExplicit && incoming.retention === "ephemeral" ? void 0 : existing.persistedAt ?? incoming.persistedAt,
|
|
2577
3620
|
source: existing.source,
|
|
2578
3621
|
retentionSource: existing.retentionSource,
|
|
2579
3622
|
trustedPublisher: existing.trustedPublisher || incoming.trustedPublisher,
|
|
@@ -2585,6 +3628,7 @@ function mergeArtifact(existing, incoming) {
|
|
|
2585
3628
|
next.title = incoming.title;
|
|
2586
3629
|
next.description = incoming.description;
|
|
2587
3630
|
delete next.workspacePath;
|
|
3631
|
+
delete next.hideWorkspacePath;
|
|
2588
3632
|
}
|
|
2589
3633
|
const changed = !publicArtifactsEqual(
|
|
2590
3634
|
toPublicArtifact(existing),
|
|
@@ -2596,8 +3640,12 @@ function mergeArtifact(existing, incoming) {
|
|
|
2596
3640
|
return { artifact: changed ? next : existing, changed };
|
|
2597
3641
|
}
|
|
2598
3642
|
__name(mergeArtifact, "mergeArtifact");
|
|
3643
|
+
function shouldRecordEphemeralUnpin(existing, incoming) {
|
|
3644
|
+
return incoming.retentionExplicit && incoming.retention === "ephemeral" && (existing.retention !== "ephemeral" || existing.persistedAt !== void 0 || existing.durableTombstoneRequired === true);
|
|
3645
|
+
}
|
|
3646
|
+
__name(shouldRecordEphemeralUnpin, "shouldRecordEphemeralUnpin");
|
|
2599
3647
|
function publicArtifactsEqual(a, b) {
|
|
2600
|
-
return a.id === b.id && a.kind === b.kind && a.storage === b.storage && a.source === b.source && a.status === b.status && a.title === b.title && a.description === b.description && a.workspacePath === b.workspacePath && a.managedId === b.managedId && a.url === b.url && a.mimeType === b.mimeType && a.sizeBytes === b.sizeBytes && metadataEqual(a.metadata, b.metadata) && a.clientRetained === b.clientRetained && a.createdAt === b.createdAt && a.toolCallId === b.toolCallId && a.toolName === b.toolName && a.hookEventName === b.hookEventName && a.clientId === b.clientId;
|
|
3648
|
+
return a.id === b.id && a.kind === b.kind && a.storage === b.storage && a.source === b.source && a.status === b.status && a.title === b.title && a.description === b.description && a.workspacePath === b.workspacePath && a.managedId === b.managedId && a.url === b.url && a.mimeType === b.mimeType && a.sizeBytes === b.sizeBytes && metadataEqual(a.metadata, b.metadata) && a.retention === b.retention && a.restoreState === b.restoreState && a.persistenceWarning === b.persistenceWarning && a.persistedAt === b.persistedAt && a.clientRetained === b.clientRetained && a.createdAt === b.createdAt && a.updatedAt === b.updatedAt && a.toolCallId === b.toolCallId && a.toolName === b.toolName && a.hookEventName === b.hookEventName && a.clientId === b.clientId;
|
|
2601
3649
|
}
|
|
2602
3650
|
__name(publicArtifactsEqual, "publicArtifactsEqual");
|
|
2603
3651
|
function metadataEqual(a, b) {
|
|
@@ -2619,6 +3667,24 @@ function mergeSizeBytes(existing, incoming) {
|
|
|
2619
3667
|
return existing.sizeBytes;
|
|
2620
3668
|
}
|
|
2621
3669
|
__name(mergeSizeBytes, "mergeSizeBytes");
|
|
3670
|
+
function strongestRetention(a, b) {
|
|
3671
|
+
const rank = {
|
|
3672
|
+
ephemeral: 0,
|
|
3673
|
+
restorable: 1
|
|
3674
|
+
};
|
|
3675
|
+
return rank[b] > rank[a] ? b : a;
|
|
3676
|
+
}
|
|
3677
|
+
__name(strongestRetention, "strongestRetention");
|
|
3678
|
+
function mergeRetention(existing, incoming) {
|
|
3679
|
+
if (incoming.retentionExplicit && incoming.retention === "ephemeral") {
|
|
3680
|
+
return "ephemeral";
|
|
3681
|
+
}
|
|
3682
|
+
if (existing.retentionExplicit && existing.retention === "ephemeral" && !incoming.retentionExplicit) {
|
|
3683
|
+
return "ephemeral";
|
|
3684
|
+
}
|
|
3685
|
+
return strongestRetention(existing.retention, incoming.retention);
|
|
3686
|
+
}
|
|
3687
|
+
__name(mergeRetention, "mergeRetention");
|
|
2622
3688
|
function mergeMetadata(existing, incoming) {
|
|
2623
3689
|
if (!incoming.metadata || incoming.source === "hook" || incoming.source !== existing.source) {
|
|
2624
3690
|
return existing.metadata;
|
|
@@ -2626,7 +3692,10 @@ function mergeMetadata(existing, incoming) {
|
|
|
2626
3692
|
const merged = { ...existing.metadata ?? {} };
|
|
2627
3693
|
let changed = false;
|
|
2628
3694
|
for (const [key, value] of Object.entries(incoming.metadata)) {
|
|
2629
|
-
if (
|
|
3695
|
+
if ((key === WORKSPACE_CONTENT_SHA256_METADATA_KEY || key === WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY) && merged[key] !== value) {
|
|
3696
|
+
merged[key] = value;
|
|
3697
|
+
changed = true;
|
|
3698
|
+
} else if (!Object.hasOwn(merged, key)) {
|
|
2630
3699
|
merged[key] = value;
|
|
2631
3700
|
changed = true;
|
|
2632
3701
|
}
|
|
@@ -2644,7 +3713,7 @@ function mergeMetadata(existing, incoming) {
|
|
|
2644
3713
|
}
|
|
2645
3714
|
__name(mergeMetadata, "mergeMetadata");
|
|
2646
3715
|
function isMetadataWithinLimit(metadata) {
|
|
2647
|
-
return
|
|
3716
|
+
return metadataBudgetBytes(metadata, "persisted") <= 4096;
|
|
2648
3717
|
}
|
|
2649
3718
|
__name(isMetadataWithinLimit, "isMetadataWithinLimit");
|
|
2650
3719
|
function countByRetentionSource(artifacts) {
|
|
@@ -2700,6 +3769,7 @@ function removePriorChange(changes, artifactId) {
|
|
|
2700
3769
|
}
|
|
2701
3770
|
__name(removePriorChange, "removePriorChange");
|
|
2702
3771
|
function toPublicArtifact(artifact) {
|
|
3772
|
+
const hideWorkspacePath = "hideWorkspacePath" in artifact && artifact.hideWorkspacePath === true;
|
|
2703
3773
|
const {
|
|
2704
3774
|
id,
|
|
2705
3775
|
kind,
|
|
@@ -2714,13 +3784,16 @@ function toPublicArtifact(artifact) {
|
|
|
2714
3784
|
mimeType,
|
|
2715
3785
|
sizeBytes,
|
|
2716
3786
|
metadata,
|
|
3787
|
+
retention,
|
|
3788
|
+
restoreState,
|
|
3789
|
+
persistenceWarning,
|
|
3790
|
+
persistedAt,
|
|
2717
3791
|
clientRetained,
|
|
2718
3792
|
createdAt,
|
|
2719
3793
|
updatedAt,
|
|
2720
3794
|
toolCallId,
|
|
2721
3795
|
toolName,
|
|
2722
|
-
hookEventName
|
|
2723
|
-
clientId
|
|
3796
|
+
hookEventName
|
|
2724
3797
|
} = artifact;
|
|
2725
3798
|
return {
|
|
2726
3799
|
id,
|
|
@@ -2730,22 +3803,205 @@ function toPublicArtifact(artifact) {
|
|
|
2730
3803
|
status,
|
|
2731
3804
|
title,
|
|
2732
3805
|
...description ? { description } : {},
|
|
2733
|
-
...workspacePath ? { workspacePath } : {},
|
|
3806
|
+
...workspacePath && !hideWorkspacePath ? { workspacePath } : {},
|
|
2734
3807
|
...managedId ? { managedId } : {},
|
|
2735
3808
|
...url ? { url } : {},
|
|
2736
3809
|
...mimeType ? { mimeType } : {},
|
|
2737
3810
|
...sizeBytes !== void 0 ? { sizeBytes } : {},
|
|
2738
3811
|
...metadata ? { metadata } : {},
|
|
3812
|
+
retention,
|
|
3813
|
+
...restoreState ? { restoreState } : {},
|
|
3814
|
+
...persistenceWarning ? { persistenceWarning } : {},
|
|
3815
|
+
...persistedAt ? { persistedAt } : {},
|
|
2739
3816
|
clientRetained,
|
|
2740
3817
|
createdAt,
|
|
2741
3818
|
updatedAt,
|
|
2742
3819
|
...toolCallId ? { toolCallId } : {},
|
|
2743
3820
|
...toolName ? { toolName } : {},
|
|
2744
|
-
...hookEventName ? { hookEventName } : {}
|
|
2745
|
-
...clientId ? { clientId } : {}
|
|
3821
|
+
...hookEventName ? { hookEventName } : {}
|
|
2746
3822
|
};
|
|
2747
3823
|
}
|
|
2748
3824
|
__name(toPublicArtifact, "toPublicArtifact");
|
|
3825
|
+
function persistedArtifactToInput(artifact) {
|
|
3826
|
+
return {
|
|
3827
|
+
title: artifact.title,
|
|
3828
|
+
kind: artifact.kind,
|
|
3829
|
+
storage: artifact.storage,
|
|
3830
|
+
description: artifact.description,
|
|
3831
|
+
workspacePath: artifact.workspacePath,
|
|
3832
|
+
managedId: artifact.managedId,
|
|
3833
|
+
url: artifact.url,
|
|
3834
|
+
mimeType: artifact.mimeType,
|
|
3835
|
+
sizeBytes: artifact.sizeBytes,
|
|
3836
|
+
metadata: artifact.metadata,
|
|
3837
|
+
source: artifact.source,
|
|
3838
|
+
retention: artifact.retention,
|
|
3839
|
+
clientRetained: artifact.clientRetained,
|
|
3840
|
+
toolCallId: artifact.toolCallId,
|
|
3841
|
+
toolName: artifact.toolName,
|
|
3842
|
+
hookEventName: artifact.hookEventName
|
|
3843
|
+
};
|
|
3844
|
+
}
|
|
3845
|
+
__name(persistedArtifactToInput, "persistedArtifactToInput");
|
|
3846
|
+
function workspaceExpectedFromArtifact(artifact) {
|
|
3847
|
+
if (!artifact.workspacePath) {
|
|
3848
|
+
return void 0;
|
|
3849
|
+
}
|
|
3850
|
+
return {
|
|
3851
|
+
sizeBytes: artifact.sizeBytes,
|
|
3852
|
+
mtimeMs: artifact.metadata?.[WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY],
|
|
3853
|
+
sha256: artifact.metadata?.[WORKSPACE_CONTENT_SHA256_METADATA_KEY]
|
|
3854
|
+
};
|
|
3855
|
+
}
|
|
3856
|
+
__name(workspaceExpectedFromArtifact, "workspaceExpectedFromArtifact");
|
|
3857
|
+
function toPersistedChange(change, recordedAt) {
|
|
3858
|
+
return {
|
|
3859
|
+
action: change.action,
|
|
3860
|
+
artifactId: change.artifactId,
|
|
3861
|
+
...change.artifact ? { artifact: toPersistedArtifact(change.artifact, recordedAt) } : {},
|
|
3862
|
+
...change.reason ? { reason: change.reason } : {}
|
|
3863
|
+
};
|
|
3864
|
+
}
|
|
3865
|
+
__name(toPersistedChange, "toPersistedChange");
|
|
3866
|
+
function toPersistedArtifact(artifact, recordedAt) {
|
|
3867
|
+
return {
|
|
3868
|
+
id: artifact.id,
|
|
3869
|
+
kind: artifact.kind,
|
|
3870
|
+
storage: artifact.storage,
|
|
3871
|
+
source: artifact.source,
|
|
3872
|
+
status: artifact.status === "changed" ? "available" : artifact.status,
|
|
3873
|
+
title: artifact.title,
|
|
3874
|
+
...artifact.description ? { description: artifact.description } : {},
|
|
3875
|
+
...artifact.workspacePath ? { workspacePath: artifact.workspacePath } : {},
|
|
3876
|
+
...artifact.managedId ? { managedId: artifact.managedId } : {},
|
|
3877
|
+
...artifact.url ? { url: artifact.url } : {},
|
|
3878
|
+
...artifact.mimeType ? { mimeType: artifact.mimeType } : {},
|
|
3879
|
+
...artifact.sizeBytes !== void 0 ? { sizeBytes: artifact.sizeBytes } : {},
|
|
3880
|
+
...artifact.metadata ? { metadata: artifact.metadata } : {},
|
|
3881
|
+
retention: artifact.retention,
|
|
3882
|
+
clientRetained: artifact.clientRetained,
|
|
3883
|
+
createdAt: artifact.createdAt,
|
|
3884
|
+
updatedAt: artifact.updatedAt,
|
|
3885
|
+
persistedAt: artifact.persistedAt ?? recordedAt,
|
|
3886
|
+
...artifact.toolCallId ? { toolCallId: artifact.toolCallId } : {},
|
|
3887
|
+
...artifact.toolName ? { toolName: artifact.toolName } : {},
|
|
3888
|
+
...artifact.hookEventName ? { hookEventName: artifact.hookEventName } : {}
|
|
3889
|
+
};
|
|
3890
|
+
}
|
|
3891
|
+
__name(toPersistedArtifact, "toPersistedArtifact");
|
|
3892
|
+
function isFileArtifactUrl(raw) {
|
|
3893
|
+
if (typeof raw !== "string") return false;
|
|
3894
|
+
try {
|
|
3895
|
+
return new URL(raw).protocol === "file:";
|
|
3896
|
+
} catch {
|
|
3897
|
+
return false;
|
|
3898
|
+
}
|
|
3899
|
+
}
|
|
3900
|
+
__name(isFileArtifactUrl, "isFileArtifactUrl");
|
|
3901
|
+
function isArtifactSnapshotCompletenessWarning(warning) {
|
|
3902
|
+
if (warning.startsWith("skipped stale event sequence ")) return false;
|
|
3903
|
+
return warning.startsWith("skipped ") || warning.includes(" list truncated to ");
|
|
3904
|
+
}
|
|
3905
|
+
__name(isArtifactSnapshotCompletenessWarning, "isArtifactSnapshotCompletenessWarning");
|
|
3906
|
+
function shouldCommitBeforeDurablePersistence(change) {
|
|
3907
|
+
return change.action === "removed" && change.durableTombstoneRequired === true;
|
|
3908
|
+
}
|
|
3909
|
+
__name(shouldCommitBeforeDurablePersistence, "shouldCommitBeforeDurablePersistence");
|
|
3910
|
+
function isDurablePersistenceChange(change) {
|
|
3911
|
+
if (change.action === "removed" && change.durableTombstoneRequired === true) {
|
|
3912
|
+
return true;
|
|
3913
|
+
}
|
|
3914
|
+
if (!change.artifact) return false;
|
|
3915
|
+
return change.artifact.retention !== "ephemeral" || change.artifact.persistenceWarning === "persistence_unavailable";
|
|
3916
|
+
}
|
|
3917
|
+
__name(isDurablePersistenceChange, "isDurablePersistenceChange");
|
|
3918
|
+
function stripDurableTombstoneMarkers(changes) {
|
|
3919
|
+
for (const change of changes) {
|
|
3920
|
+
delete change.durableTombstoneRequired;
|
|
3921
|
+
delete change.removedClientId;
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
__name(stripDurableTombstoneMarkers, "stripDurableTombstoneMarkers");
|
|
3925
|
+
function detailsForRestoreWarnings(warnings) {
|
|
3926
|
+
return warnings.map((warning) => {
|
|
3927
|
+
if (warning.startsWith(RESTORE_FAILED_WARNING_PREFIX)) {
|
|
3928
|
+
return {
|
|
3929
|
+
code: "ARTIFACT_RESTORE_FAILED",
|
|
3930
|
+
operation: "restore",
|
|
3931
|
+
durability: "unavailable",
|
|
3932
|
+
retryable: true,
|
|
3933
|
+
message: warning
|
|
3934
|
+
};
|
|
3935
|
+
}
|
|
3936
|
+
if (warning.startsWith(RESTORE_PARTIAL_FAILED_WARNING_PREFIX)) {
|
|
3937
|
+
return {
|
|
3938
|
+
code: "ARTIFACT_RESTORE_PARTIAL_FAILED",
|
|
3939
|
+
operation: "restore",
|
|
3940
|
+
durability: "live_only",
|
|
3941
|
+
retryable: true,
|
|
3942
|
+
message: warning
|
|
3943
|
+
};
|
|
3944
|
+
}
|
|
3945
|
+
return {
|
|
3946
|
+
code: "ARTIFACT_WARNING",
|
|
3947
|
+
operation: "restore",
|
|
3948
|
+
message: warning
|
|
3949
|
+
};
|
|
3950
|
+
});
|
|
3951
|
+
}
|
|
3952
|
+
__name(detailsForRestoreWarnings, "detailsForRestoreWarnings");
|
|
3953
|
+
function detailsForPersistenceWarnings(warnings, changes, operation) {
|
|
3954
|
+
if (warnings.length === 0) return [];
|
|
3955
|
+
const artifactIds = changes.filter(isDurablePersistenceChange).map((change) => change.artifactId);
|
|
3956
|
+
return warnings.map((warning) => {
|
|
3957
|
+
if (warning === "artifact persistence unavailable; durable artifacts kept ephemeral") {
|
|
3958
|
+
return {
|
|
3959
|
+
code: "ARTIFACT_PERSISTENCE_UNAVAILABLE",
|
|
3960
|
+
operation,
|
|
3961
|
+
artifactIds,
|
|
3962
|
+
durability: "live_only",
|
|
3963
|
+
retryable: false,
|
|
3964
|
+
message: warning
|
|
3965
|
+
};
|
|
3966
|
+
}
|
|
3967
|
+
if (warning === "artifact removal not persisted; live removal kept") {
|
|
3968
|
+
return {
|
|
3969
|
+
code: "ARTIFACT_REMOVAL_NOT_PERSISTED",
|
|
3970
|
+
operation,
|
|
3971
|
+
artifactIds,
|
|
3972
|
+
durability: "live_only",
|
|
3973
|
+
retryable: true,
|
|
3974
|
+
message: warning
|
|
3975
|
+
};
|
|
3976
|
+
}
|
|
3977
|
+
return {
|
|
3978
|
+
code: "ARTIFACT_WARNING",
|
|
3979
|
+
operation,
|
|
3980
|
+
artifactIds,
|
|
3981
|
+
message: warning
|
|
3982
|
+
};
|
|
3983
|
+
});
|
|
3984
|
+
}
|
|
3985
|
+
__name(detailsForPersistenceWarnings, "detailsForPersistenceWarnings");
|
|
3986
|
+
function persistenceFailureDetail(options) {
|
|
3987
|
+
const unavailable = options.error instanceof SessionArtifactValidationError && options.error.message === "artifact persistence is unavailable";
|
|
3988
|
+
return {
|
|
3989
|
+
code: unavailable ? "ARTIFACT_PERSISTENCE_UNAVAILABLE" : "ARTIFACT_PERSISTENCE_WRITE_FAILED",
|
|
3990
|
+
operation: options.operation,
|
|
3991
|
+
artifactIds: options.artifactIds,
|
|
3992
|
+
durability: "unavailable",
|
|
3993
|
+
retryable: !unavailable,
|
|
3994
|
+
message: options.message
|
|
3995
|
+
};
|
|
3996
|
+
}
|
|
3997
|
+
__name(persistenceFailureDetail, "persistenceFailureDetail");
|
|
3998
|
+
function cloneStoredArtifact(artifact) {
|
|
3999
|
+
return {
|
|
4000
|
+
...artifact,
|
|
4001
|
+
...artifact.metadata ? { metadata: { ...artifact.metadata } } : {}
|
|
4002
|
+
};
|
|
4003
|
+
}
|
|
4004
|
+
__name(cloneStoredArtifact, "cloneStoredArtifact");
|
|
2749
4005
|
function inferStorage(requested, locators) {
|
|
2750
4006
|
if (requested !== void 0 && typeof requested !== "string") {
|
|
2751
4007
|
throw new SessionArtifactValidationError(
|
|
@@ -2839,10 +4095,6 @@ function buildIdentityKey(input) {
|
|
|
2839
4095
|
);
|
|
2840
4096
|
}
|
|
2841
4097
|
__name(buildIdentityKey, "buildIdentityKey");
|
|
2842
|
-
function stableArtifactId(sessionId, identityKey) {
|
|
2843
|
-
return createHash("sha256").update(`${sessionId}:${identityKey}`).digest("hex").slice(0, 16);
|
|
2844
|
-
}
|
|
2845
|
-
__name(stableArtifactId, "stableArtifactId");
|
|
2846
4098
|
function managedIdForWorkspacePath(workspaceCwd, workspacePath) {
|
|
2847
4099
|
return createHash("sha1").update(path2.resolve(workspaceCwd, workspacePath)).digest("hex").slice(0, 16);
|
|
2848
4100
|
}
|
|
@@ -2956,6 +4208,12 @@ function normalizeArtifactUrl(raw, allowFile) {
|
|
|
2956
4208
|
"url"
|
|
2957
4209
|
);
|
|
2958
4210
|
}
|
|
4211
|
+
if (hasSecretLikeUrlComponent(parsed)) {
|
|
4212
|
+
throw new SessionArtifactValidationError(
|
|
4213
|
+
"url must not include secret-like components",
|
|
4214
|
+
"url"
|
|
4215
|
+
);
|
|
4216
|
+
}
|
|
2959
4217
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" && !(allowFile && parsed.protocol === "file:")) {
|
|
2960
4218
|
throw new SessionArtifactValidationError(
|
|
2961
4219
|
allowFile ? "url must use http, https, or file" : "url must use http or https",
|
|
@@ -2965,7 +4223,64 @@ function normalizeArtifactUrl(raw, allowFile) {
|
|
|
2965
4223
|
return parsed.href;
|
|
2966
4224
|
}
|
|
2967
4225
|
__name(normalizeArtifactUrl, "normalizeArtifactUrl");
|
|
2968
|
-
function
|
|
4226
|
+
function hasSecretLikeUrlComponent(parsed) {
|
|
4227
|
+
for (const [key, value] of parsed.searchParams) {
|
|
4228
|
+
if (isSecretLikeUrlText(key) || isSecretLikeUrlValue(value)) {
|
|
4229
|
+
return true;
|
|
4230
|
+
}
|
|
4231
|
+
}
|
|
4232
|
+
for (const segment of parsed.pathname.split("/").filter(Boolean)) {
|
|
4233
|
+
let decodedSegment = segment;
|
|
4234
|
+
try {
|
|
4235
|
+
decodedSegment = decodeURIComponent(segment);
|
|
4236
|
+
} catch {
|
|
4237
|
+
}
|
|
4238
|
+
if (isSecretLikeUrlValue(decodedSegment)) {
|
|
4239
|
+
return true;
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
const fragment = parsed.hash.slice(1);
|
|
4243
|
+
return hasSecretLikeUrlFragment(fragment);
|
|
4244
|
+
}
|
|
4245
|
+
__name(hasSecretLikeUrlComponent, "hasSecretLikeUrlComponent");
|
|
4246
|
+
function hasSecretLikeUrlFragment(fragment) {
|
|
4247
|
+
if (!fragment) return false;
|
|
4248
|
+
const candidates = /* @__PURE__ */ new Set([fragment]);
|
|
4249
|
+
try {
|
|
4250
|
+
candidates.add(decodeURIComponent(fragment));
|
|
4251
|
+
} catch {
|
|
4252
|
+
}
|
|
4253
|
+
for (const candidate of candidates) {
|
|
4254
|
+
if (isSecretLikeUrlText(candidate) || isSecretLikeUrlValue(candidate)) {
|
|
4255
|
+
return true;
|
|
4256
|
+
}
|
|
4257
|
+
for (const [key, value] of new URLSearchParams(candidate)) {
|
|
4258
|
+
if (isSecretLikeUrlText(key) || isSecretLikeUrlValue(value)) {
|
|
4259
|
+
return true;
|
|
4260
|
+
}
|
|
4261
|
+
}
|
|
4262
|
+
}
|
|
4263
|
+
return false;
|
|
4264
|
+
}
|
|
4265
|
+
__name(hasSecretLikeUrlFragment, "hasSecretLikeUrlFragment");
|
|
4266
|
+
function isSecretLikeUrlText(value) {
|
|
4267
|
+
const normalized = value.replace(/([a-z])([A-Z])/g, "$1-$2");
|
|
4268
|
+
return /(?:^|[-_.])(token|secret|password|passwd|pwd|cookie|authorization|credential|signature|sig|api[-_]?key|access[-_]?key)(?:$|[-_.=&#])/i.test(
|
|
4269
|
+
normalized
|
|
4270
|
+
);
|
|
4271
|
+
}
|
|
4272
|
+
__name(isSecretLikeUrlText, "isSecretLikeUrlText");
|
|
4273
|
+
function isSecretLikeUrlValue(value) {
|
|
4274
|
+
return SECRET_TOKEN_VALUE_PATTERN.test(value.trim());
|
|
4275
|
+
}
|
|
4276
|
+
__name(isSecretLikeUrlValue, "isSecretLikeUrlValue");
|
|
4277
|
+
function isSecretLikeMetadataValue(value) {
|
|
4278
|
+
return /^(?:bearer\s+\S{8,}|sk-[A-Za-z0-9_-]{12,}|(?:gh[pousr]|github_pat)_[A-Za-z0-9_/-]{12,})$/i.test(
|
|
4279
|
+
value.trim()
|
|
4280
|
+
);
|
|
4281
|
+
}
|
|
4282
|
+
__name(isSecretLikeMetadataValue, "isSecretLikeMetadataValue");
|
|
4283
|
+
function normalizeMetadata(metadata, options = {}) {
|
|
2969
4284
|
if (metadata === void 0) {
|
|
2970
4285
|
return void 0;
|
|
2971
4286
|
}
|
|
@@ -2977,6 +4292,12 @@ function normalizeMetadata(metadata) {
|
|
|
2977
4292
|
}
|
|
2978
4293
|
const normalized = {};
|
|
2979
4294
|
for (const [key, value] of Object.entries(metadata)) {
|
|
4295
|
+
if (isPrototypeMetadataKey(key)) {
|
|
4296
|
+
continue;
|
|
4297
|
+
}
|
|
4298
|
+
if (options.budget !== "persisted" && isReservedWorkspaceMetadataKey(key)) {
|
|
4299
|
+
continue;
|
|
4300
|
+
}
|
|
2980
4301
|
if (!key) {
|
|
2981
4302
|
throw new SessionArtifactValidationError(
|
|
2982
4303
|
"metadata keys must not be empty",
|
|
@@ -2995,6 +4316,12 @@ function normalizeMetadata(metadata) {
|
|
|
2995
4316
|
"metadata"
|
|
2996
4317
|
);
|
|
2997
4318
|
}
|
|
4319
|
+
if (isSecretLikeUrlText(key)) {
|
|
4320
|
+
throw new SessionArtifactValidationError(
|
|
4321
|
+
"metadata keys must not contain secret-like names",
|
|
4322
|
+
"metadata"
|
|
4323
|
+
);
|
|
4324
|
+
}
|
|
2998
4325
|
if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
|
|
2999
4326
|
throw new SessionArtifactValidationError(
|
|
3000
4327
|
"metadata values must be primitive",
|
|
@@ -3013,12 +4340,18 @@ function normalizeMetadata(metadata) {
|
|
|
3013
4340
|
"metadata"
|
|
3014
4341
|
);
|
|
3015
4342
|
}
|
|
4343
|
+
if (typeof value === "string" && !isReservedWorkspaceMetadataKey(key) && isSecretLikeMetadataValue(value)) {
|
|
4344
|
+
throw new SessionArtifactValidationError(
|
|
4345
|
+
"metadata string values must not contain secret-like tokens",
|
|
4346
|
+
"metadata"
|
|
4347
|
+
);
|
|
4348
|
+
}
|
|
3016
4349
|
normalized[key] = value;
|
|
3017
4350
|
}
|
|
3018
4351
|
if (Object.keys(normalized).length === 0) {
|
|
3019
4352
|
return void 0;
|
|
3020
4353
|
}
|
|
3021
|
-
if (
|
|
4354
|
+
if (metadataBudgetBytes(normalized, options.budget ?? "user") > 4096) {
|
|
3022
4355
|
throw new SessionArtifactValidationError(
|
|
3023
4356
|
"metadata must be 4096 bytes or fewer",
|
|
3024
4357
|
"metadata"
|
|
@@ -3027,6 +4360,25 @@ function normalizeMetadata(metadata) {
|
|
|
3027
4360
|
return normalized;
|
|
3028
4361
|
}
|
|
3029
4362
|
__name(normalizeMetadata, "normalizeMetadata");
|
|
4363
|
+
function normalizeRetention(value, options) {
|
|
4364
|
+
if (value === void 0) {
|
|
4365
|
+
return options.persistenceAvailable ? "restorable" : "ephemeral";
|
|
4366
|
+
}
|
|
4367
|
+
if (value === "ephemeral" || value === "restorable") {
|
|
4368
|
+
return value;
|
|
4369
|
+
}
|
|
4370
|
+
if (value === "pinned") {
|
|
4371
|
+
throw new SessionArtifactValidationError(
|
|
4372
|
+
"pinned retention is not supported by session_artifacts_persistence",
|
|
4373
|
+
"retention"
|
|
4374
|
+
);
|
|
4375
|
+
}
|
|
4376
|
+
throw new SessionArtifactValidationError(
|
|
4377
|
+
"retention must be ephemeral or restorable",
|
|
4378
|
+
"retention"
|
|
4379
|
+
);
|
|
4380
|
+
}
|
|
4381
|
+
__name(normalizeRetention, "normalizeRetention");
|
|
3030
4382
|
function normalizeSizeBytes(value) {
|
|
3031
4383
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
3032
4384
|
throw new SessionArtifactValidationError(
|
|
@@ -3052,21 +4404,83 @@ function inferKind(input) {
|
|
|
3052
4404
|
return input.workspacePath ? "file" : "other";
|
|
3053
4405
|
}
|
|
3054
4406
|
__name(inferKind, "inferKind");
|
|
3055
|
-
async function getWorkspaceStatus(
|
|
3056
|
-
const absolutePath = path2.resolve(workspaceCwd, workspacePath);
|
|
4407
|
+
async function getWorkspaceStatus(workspacePath, realWorkspaceCwd, expected, options = { hashContent: true }) {
|
|
3057
4408
|
const realWorkspace = await realWorkspaceCwd;
|
|
4409
|
+
const absolutePath = path2.resolve(realWorkspace, workspacePath);
|
|
3058
4410
|
try {
|
|
3059
4411
|
const realPath = await fs2.realpath(absolutePath);
|
|
3060
4412
|
const relative = path2.relative(realWorkspace, realPath);
|
|
3061
4413
|
if (!relative || isOutsidePath(relative)) {
|
|
3062
4414
|
return { status: "missing", escaped: true };
|
|
3063
4415
|
}
|
|
3064
|
-
const
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
4416
|
+
const preOpenStat = await fs2.lstat(realPath);
|
|
4417
|
+
const handle = await fs2.open(
|
|
4418
|
+
realPath,
|
|
4419
|
+
fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW
|
|
4420
|
+
);
|
|
4421
|
+
try {
|
|
4422
|
+
const stat = await handle.stat();
|
|
4423
|
+
if (!isSameFile(preOpenStat, stat)) {
|
|
4424
|
+
return { status: "missing", escaped: true };
|
|
4425
|
+
}
|
|
4426
|
+
if (stat.isFile()) {
|
|
4427
|
+
const expectedMtimeMs = typeof expected?.mtimeMs === "number" ? expected.mtimeMs : void 0;
|
|
4428
|
+
const expectedSha256 = typeof expected?.sha256 === "string" ? expected.sha256 : void 0;
|
|
4429
|
+
const unchanged = expected?.sizeBytes === stat.size && expectedMtimeMs === stat.mtimeMs;
|
|
4430
|
+
const sizeChanged = expected?.sizeBytes !== void 0 && expected.sizeBytes !== stat.size;
|
|
4431
|
+
if (sizeChanged) {
|
|
4432
|
+
return {
|
|
4433
|
+
status: "changed",
|
|
4434
|
+
sizeBytes: stat.size,
|
|
4435
|
+
mtimeMs: stat.mtimeMs
|
|
4436
|
+
};
|
|
4437
|
+
}
|
|
4438
|
+
if (unchanged) {
|
|
4439
|
+
return {
|
|
4440
|
+
status: "available",
|
|
4441
|
+
sizeBytes: stat.size,
|
|
4442
|
+
mtimeMs: stat.mtimeMs
|
|
4443
|
+
};
|
|
4444
|
+
}
|
|
4445
|
+
if (stat.size > MAX_WORKSPACE_HASH_BYTES) {
|
|
4446
|
+
return {
|
|
4447
|
+
status: expectedSha256 ? "changed" : "available",
|
|
4448
|
+
sizeBytes: stat.size,
|
|
4449
|
+
mtimeMs: stat.mtimeMs
|
|
4450
|
+
};
|
|
4451
|
+
}
|
|
4452
|
+
if (!options.hashContent) {
|
|
4453
|
+
return {
|
|
4454
|
+
status: expectedSha256 ? "changed" : "available",
|
|
4455
|
+
sizeBytes: stat.size,
|
|
4456
|
+
mtimeMs: stat.mtimeMs
|
|
4457
|
+
};
|
|
4458
|
+
}
|
|
4459
|
+
const sha256 = await hashFile(handle);
|
|
4460
|
+
if (expectedSha256 && sha256 !== expectedSha256) {
|
|
4461
|
+
return {
|
|
4462
|
+
status: "changed",
|
|
4463
|
+
sizeBytes: stat.size,
|
|
4464
|
+
mtimeMs: stat.mtimeMs
|
|
4465
|
+
};
|
|
4466
|
+
}
|
|
4467
|
+
return {
|
|
4468
|
+
status: "available",
|
|
4469
|
+
sizeBytes: stat.size,
|
|
4470
|
+
mtimeMs: stat.mtimeMs,
|
|
4471
|
+
sha256
|
|
4472
|
+
};
|
|
4473
|
+
}
|
|
4474
|
+
return {
|
|
4475
|
+
status: "available"
|
|
4476
|
+
};
|
|
4477
|
+
} finally {
|
|
4478
|
+
await handle.close();
|
|
4479
|
+
}
|
|
3069
4480
|
} catch (error) {
|
|
4481
|
+
if (isNoFollowSymlinkError(error)) {
|
|
4482
|
+
return { status: "missing", escaped: true };
|
|
4483
|
+
}
|
|
3070
4484
|
if (!isNotFoundError(error)) {
|
|
3071
4485
|
throw error;
|
|
3072
4486
|
}
|
|
@@ -3077,6 +4491,41 @@ async function getWorkspaceStatus(workspaceCwd, workspacePath, realWorkspaceCwd)
|
|
|
3077
4491
|
}
|
|
3078
4492
|
}
|
|
3079
4493
|
__name(getWorkspaceStatus, "getWorkspaceStatus");
|
|
4494
|
+
function isSameFile(before, after) {
|
|
4495
|
+
return before.dev === after.dev && before.ino === after.ino;
|
|
4496
|
+
}
|
|
4497
|
+
__name(isSameFile, "isSameFile");
|
|
4498
|
+
async function hashFile(handle) {
|
|
4499
|
+
const hash = createHash("sha256");
|
|
4500
|
+
for await (const chunk of handle.createReadStream({ start: 0 })) {
|
|
4501
|
+
hash.update(chunk);
|
|
4502
|
+
}
|
|
4503
|
+
return hash.digest("hex");
|
|
4504
|
+
}
|
|
4505
|
+
__name(hashFile, "hashFile");
|
|
4506
|
+
function withWorkspaceContentHashMetadata(metadata, workspaceStatus) {
|
|
4507
|
+
if (workspaceStatus?.status !== "available" || !workspaceStatus.sha256) {
|
|
4508
|
+
return metadata;
|
|
4509
|
+
}
|
|
4510
|
+
const next = {
|
|
4511
|
+
...metadata ?? {},
|
|
4512
|
+
[WORKSPACE_CONTENT_SHA256_METADATA_KEY]: workspaceStatus.sha256,
|
|
4513
|
+
...workspaceStatus.mtimeMs !== void 0 ? { [WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY]: workspaceStatus.mtimeMs } : {}
|
|
4514
|
+
};
|
|
4515
|
+
return next;
|
|
4516
|
+
}
|
|
4517
|
+
__name(withWorkspaceContentHashMetadata, "withWorkspaceContentHashMetadata");
|
|
4518
|
+
function isWorkspaceContentChanged(artifact, status) {
|
|
4519
|
+
if (status.status !== "available") {
|
|
4520
|
+
return false;
|
|
4521
|
+
}
|
|
4522
|
+
const expectedSha256 = artifact.metadata?.[WORKSPACE_CONTENT_SHA256_METADATA_KEY];
|
|
4523
|
+
if (artifact.sizeBytes !== void 0 && status.sizeBytes !== void 0 && status.sizeBytes !== artifact.sizeBytes) {
|
|
4524
|
+
return true;
|
|
4525
|
+
}
|
|
4526
|
+
return typeof expectedSha256 === "string" && status.sha256 !== void 0 && status.sha256 !== expectedSha256;
|
|
4527
|
+
}
|
|
4528
|
+
__name(isWorkspaceContentChanged, "isWorkspaceContentChanged");
|
|
3080
4529
|
async function danglingSymlinkEscapesWorkspace(absolutePath, realWorkspace) {
|
|
3081
4530
|
try {
|
|
3082
4531
|
const stat = await fs2.lstat(absolutePath);
|
|
@@ -3109,6 +4558,13 @@ function isNotFoundError(error) {
|
|
|
3109
4558
|
return code === "ENOENT" || code === "ENOTDIR";
|
|
3110
4559
|
}
|
|
3111
4560
|
__name(isNotFoundError, "isNotFoundError");
|
|
4561
|
+
function isNoFollowSymlinkError(error) {
|
|
4562
|
+
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
4563
|
+
return false;
|
|
4564
|
+
}
|
|
4565
|
+
return error.code === "ELOOP";
|
|
4566
|
+
}
|
|
4567
|
+
__name(isNoFollowSymlinkError, "isNoFollowSymlinkError");
|
|
3112
4568
|
function isOutsidePath(relative) {
|
|
3113
4569
|
return relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative);
|
|
3114
4570
|
}
|
|
@@ -3276,6 +4732,18 @@ function isBridgeAutoMemoryTopic(value) {
|
|
|
3276
4732
|
return value === "user" || value === "feedback" || value === "project" || value === "reference";
|
|
3277
4733
|
}
|
|
3278
4734
|
__name(isBridgeAutoMemoryTopic, "isBridgeAutoMemoryTopic");
|
|
4735
|
+
function touchedScopesFromTopics(topics) {
|
|
4736
|
+
const scopes = /* @__PURE__ */ new Set();
|
|
4737
|
+
for (const topic of topics) {
|
|
4738
|
+
scopes.add(topic === "user" || topic === "feedback" ? "user" : "project");
|
|
4739
|
+
}
|
|
4740
|
+
return ["user", "project"].filter((scope) => scopes.has(scope));
|
|
4741
|
+
}
|
|
4742
|
+
__name(touchedScopesFromTopics, "touchedScopesFromTopics");
|
|
4743
|
+
function isBridgeMemoryScope(value) {
|
|
4744
|
+
return value === "user" || value === "project";
|
|
4745
|
+
}
|
|
4746
|
+
__name(isBridgeMemoryScope, "isBridgeMemoryScope");
|
|
3279
4747
|
function parseWorkspaceMemoryForgetMatch(value) {
|
|
3280
4748
|
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
3281
4749
|
return null;
|
|
@@ -3299,14 +4767,17 @@ function parseWorkspaceMemoryForgetResult(response) {
|
|
|
3299
4767
|
const summary = record["summary"];
|
|
3300
4768
|
const removedEntries = record["removedEntries"];
|
|
3301
4769
|
const touchedTopics = record["touchedTopics"];
|
|
4770
|
+
const touchedScopes = record["touchedScopes"];
|
|
3302
4771
|
const parsedRemovedEntries = Array.isArray(removedEntries) ? removedEntries.map(parseWorkspaceMemoryForgetMatch) : [];
|
|
3303
|
-
if (summary !== void 0 && typeof summary !== "string" || !Array.isArray(removedEntries) || parsedRemovedEntries.some((entry) => entry === null) || !Array.isArray(touchedTopics) || !touchedTopics.every(isBridgeAutoMemoryTopic)) {
|
|
4772
|
+
if (summary !== void 0 && typeof summary !== "string" || !Array.isArray(removedEntries) || parsedRemovedEntries.some((entry) => entry === null) || !Array.isArray(touchedTopics) || !touchedTopics.every(isBridgeAutoMemoryTopic) || touchedScopes !== void 0 && (!Array.isArray(touchedScopes) || !touchedScopes.every(isBridgeMemoryScope))) {
|
|
3304
4773
|
throw new Error("Malformed workspace memory forget response");
|
|
3305
4774
|
}
|
|
4775
|
+
const parsedTouchedTopics = touchedTopics;
|
|
3306
4776
|
return {
|
|
3307
4777
|
...summary === void 0 ? {} : { summary },
|
|
3308
4778
|
removedEntries: parsedRemovedEntries,
|
|
3309
|
-
touchedTopics
|
|
4779
|
+
touchedTopics: parsedTouchedTopics,
|
|
4780
|
+
touchedScopes: touchedScopes === void 0 ? touchedScopesFromTopics(parsedTouchedTopics) : touchedScopes
|
|
3310
4781
|
};
|
|
3311
4782
|
}
|
|
3312
4783
|
__name(parseWorkspaceMemoryForgetResult, "parseWorkspaceMemoryForgetResult");
|
|
@@ -3446,6 +4917,11 @@ function broadcastTurnError(entry, sessionId, err, promptId, originatorClientId)
|
|
|
3446
4917
|
);
|
|
3447
4918
|
}
|
|
3448
4919
|
entry.retryAllowed = true;
|
|
4920
|
+
entry.turnError = {
|
|
4921
|
+
message,
|
|
4922
|
+
...code ? { code } : {},
|
|
4923
|
+
...errorKind ? { errorKind } : {}
|
|
4924
|
+
};
|
|
3449
4925
|
try {
|
|
3450
4926
|
entry.events.publish({
|
|
3451
4927
|
type: "turn_error",
|
|
@@ -3488,6 +4964,7 @@ function extractPromptText(prompt) {
|
|
|
3488
4964
|
__name(extractPromptText, "extractPromptText");
|
|
3489
4965
|
var DEFAULT_INIT_TIMEOUT_MS = 1e4;
|
|
3490
4966
|
var PERSIST_TIMEOUT_MS = 5e3;
|
|
4967
|
+
var MAX_PARENT_PERSIST_ATTEMPTS = 3;
|
|
3491
4968
|
var MCP_RESTART_TIMEOUT_MS = 3e5;
|
|
3492
4969
|
var WORKSPACE_MEMORY_REMEMBER_TIMEOUT_MS = 3e5;
|
|
3493
4970
|
var MCP_OAUTH_TIMEOUT_MS = 6e5;
|
|
@@ -3495,6 +4972,7 @@ var DAEMON_RETRY_META_KEY = "qwen.daemon.retry";
|
|
|
3495
4972
|
var DAEMON_CONTINUE_META_KEY = "qwen.daemon.continueLastTurn";
|
|
3496
4973
|
var SESSION_RECAP_TIMEOUT_MS = 6e4;
|
|
3497
4974
|
var SESSION_BTW_TIMEOUT_MS = 6e4;
|
|
4975
|
+
var SESSION_TRANSCRIPT_TIMEOUT_MS = 6e4;
|
|
3498
4976
|
var SHELL_COMMAND_TIMEOUT_MS = 12e4;
|
|
3499
4977
|
var MAX_SHELL_OUTPUT_FOR_HISTORY = 1e4;
|
|
3500
4978
|
var MAX_MID_TURN_QUEUE_DEPTH = 20;
|
|
@@ -3523,6 +5001,27 @@ function createAcpSessionBridge(opts) {
|
|
|
3523
5001
|
} else {
|
|
3524
5002
|
maxSessions = opts.maxSessions;
|
|
3525
5003
|
}
|
|
5004
|
+
const reserveFreshSession = /* @__PURE__ */ __name((context) => opts.freshSessionAdmission?.(context), "reserveFreshSession");
|
|
5005
|
+
const releaseFreshSessionReservation = /* @__PURE__ */ __name((reservation) => {
|
|
5006
|
+
if (!reservation) return;
|
|
5007
|
+
try {
|
|
5008
|
+
reservation.release();
|
|
5009
|
+
} catch (err) {
|
|
5010
|
+
opts.onDiagnosticLine?.(
|
|
5011
|
+
`qwen serve: fresh session admission release failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
5012
|
+
"warn"
|
|
5013
|
+
);
|
|
5014
|
+
}
|
|
5015
|
+
}, "releaseFreshSessionReservation");
|
|
5016
|
+
const emitSessionLifecycle = /* @__PURE__ */ __name((event) => {
|
|
5017
|
+
try {
|
|
5018
|
+
opts.sessionLifecycle?.(event);
|
|
5019
|
+
} catch (err) {
|
|
5020
|
+
const message = `qwen serve: session lifecycle callback failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
5021
|
+
opts.onDiagnosticLine?.(message, "warn");
|
|
5022
|
+
writeStderrLine(message);
|
|
5023
|
+
}
|
|
5024
|
+
}, "emitSessionLifecycle");
|
|
3526
5025
|
if (defaultSessionScope !== "single" && defaultSessionScope !== "thread") {
|
|
3527
5026
|
throw new TypeError(
|
|
3528
5027
|
`Invalid sessionScope: ${JSON.stringify(defaultSessionScope)}. Expected 'single' or 'thread'.`
|
|
@@ -3534,6 +5033,9 @@ function createAcpSessionBridge(opts) {
|
|
|
3534
5033
|
`Invalid eventRingSize: ${opts.eventRingSize}. Must be a positive integer in [1, ${MAX_EVENT_RING_SIZE}].`
|
|
3535
5034
|
);
|
|
3536
5035
|
}
|
|
5036
|
+
const compactedReplayMaxBytes = normalizeCompactedReplayMaxBytes(
|
|
5037
|
+
opts.compactedReplayMaxBytes
|
|
5038
|
+
);
|
|
3537
5039
|
const channelFactory = opts.channelFactory ?? defaultSpawnChannelFactory;
|
|
3538
5040
|
const childEnvOverrides = opts.childEnvOverrides ? Object.freeze({ ...opts.childEnvOverrides }) : Object.freeze({});
|
|
3539
5041
|
const initTimeoutMs = opts.initializeTimeoutMs ?? DEFAULT_INIT_TIMEOUT_MS;
|
|
@@ -3704,14 +5206,32 @@ function createAcpSessionBridge(opts) {
|
|
|
3704
5206
|
const aliveChannels = /* @__PURE__ */ new Set();
|
|
3705
5207
|
let inFlightChannelSpawn;
|
|
3706
5208
|
const byId = /* @__PURE__ */ new Map();
|
|
3707
|
-
const toSessionSummary = /* @__PURE__ */ __name((entry) =>
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
5209
|
+
const toSessionSummary = /* @__PURE__ */ __name((entry) => {
|
|
5210
|
+
let isWaitingForPermission = false;
|
|
5211
|
+
let isWaitingForUserQuestion = false;
|
|
5212
|
+
for (const interaction of entry.pendingInteractions.values()) {
|
|
5213
|
+
if (interaction.kind === "user_question") {
|
|
5214
|
+
isWaitingForUserQuestion = true;
|
|
5215
|
+
} else {
|
|
5216
|
+
isWaitingForPermission = true;
|
|
5217
|
+
}
|
|
5218
|
+
}
|
|
5219
|
+
return {
|
|
5220
|
+
sessionId: entry.sessionId,
|
|
5221
|
+
workspaceCwd: entry.workspaceCwd,
|
|
5222
|
+
createdAt: entry.createdAt,
|
|
5223
|
+
displayName: entry.displayName,
|
|
5224
|
+
...entry.parentSessionId ? { parentSessionId: entry.parentSessionId } : {},
|
|
5225
|
+
clientCount: entry.clientIds.size,
|
|
5226
|
+
hasActivePrompt: entry.promptActive,
|
|
5227
|
+
isWaitingForPermission,
|
|
5228
|
+
isWaitingForUserQuestion,
|
|
5229
|
+
pendingInteractionCount: entry.pendingInteractions.size,
|
|
5230
|
+
hasTurnError: entry.turnError !== void 0,
|
|
5231
|
+
...entry.turnError !== void 0 ? { turnError: entry.turnError } : {},
|
|
5232
|
+
pendingInteractions: [...entry.pendingInteractions.values()]
|
|
5233
|
+
};
|
|
5234
|
+
}, "toSessionSummary");
|
|
3715
5235
|
const permissionConsensusQuorum = opts.permissionConsensusQuorum;
|
|
3716
5236
|
if (permissionConsensusQuorum !== void 0 && (!Number.isInteger(permissionConsensusQuorum) || permissionConsensusQuorum < 1)) {
|
|
3717
5237
|
throw new Error(
|
|
@@ -3770,6 +5290,22 @@ function createAcpSessionBridge(opts) {
|
|
|
3770
5290
|
entry.clientIds.set(clientId, count - 1);
|
|
3771
5291
|
}
|
|
3772
5292
|
}, "unregisterClient");
|
|
5293
|
+
const rollbackAttachRegistration = /* @__PURE__ */ __name(async (entry, clientId, attachCountDelta = 1) => {
|
|
5294
|
+
entry.attachCount = Math.max(0, entry.attachCount - attachCountDelta);
|
|
5295
|
+
unregisterClient(entry, clientId);
|
|
5296
|
+
if (entry.spawnOwnerWantedKill && entry.attachCount === 0 && entry.events.subscriberCount === 0) {
|
|
5297
|
+
await bridgeApi.killSession(entry.sessionId).catch(() => {
|
|
5298
|
+
});
|
|
5299
|
+
} else if (entry.clientIds.size === 0 && entry.events.subscriberCount === 0 && !entry.promptActive) {
|
|
5300
|
+
await closeSessionImpl(entry.sessionId, void 0, {
|
|
5301
|
+
reason: "last_client_detached"
|
|
5302
|
+
}).catch((err) => {
|
|
5303
|
+
writeStderrLine(
|
|
5304
|
+
`qwen serve: close-on-attach-rollback failed for ${JSON.stringify(entry.sessionId)}: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
|
|
5305
|
+
);
|
|
5306
|
+
});
|
|
5307
|
+
}
|
|
5308
|
+
}, "rollbackAttachRegistration");
|
|
3773
5309
|
const resolveTrustedClientId = /* @__PURE__ */ __name((entry, clientId) => {
|
|
3774
5310
|
if (clientId === void 0) return void 0;
|
|
3775
5311
|
if (!entry.clientIds.has(clientId)) {
|
|
@@ -3850,7 +5386,11 @@ function createAcpSessionBridge(opts) {
|
|
|
3850
5386
|
inputTokens,
|
|
3851
5387
|
outputTokens,
|
|
3852
5388
|
durationMs
|
|
3853
|
-
)
|
|
5389
|
+
),
|
|
5390
|
+
// `create_sub_session` tool: forward the request/response hook so a child
|
|
5391
|
+
// tool can ask the daemon to spawn a sub-session and (for 'first-turn')
|
|
5392
|
+
// return its result. Omitted → the method reports daemon-only.
|
|
5393
|
+
opts.onCreateSubSession
|
|
3854
5394
|
);
|
|
3855
5395
|
const connection = new ClientSideConnection(() => client, channel.stream);
|
|
3856
5396
|
const info = {
|
|
@@ -3915,6 +5455,12 @@ function createAcpSessionBridge(opts) {
|
|
|
3915
5455
|
}
|
|
3916
5456
|
byId.delete(sid);
|
|
3917
5457
|
telemetry.metrics?.sessionLifecycle("die");
|
|
5458
|
+
emitSessionLifecycle({
|
|
5459
|
+
type: "removed",
|
|
5460
|
+
sessionId: sid,
|
|
5461
|
+
workspaceCwd: sessEntry.workspaceCwd,
|
|
5462
|
+
reason: "channel_closed"
|
|
5463
|
+
});
|
|
3918
5464
|
info.client.markSessionClosed(sid);
|
|
3919
5465
|
if (defaultEntry === sessEntry) defaultEntry = void 0;
|
|
3920
5466
|
sessEntry.events.close();
|
|
@@ -3963,10 +5509,12 @@ function createAcpSessionBridge(opts) {
|
|
|
3963
5509
|
}
|
|
3964
5510
|
}
|
|
3965
5511
|
__name(ensureChannel, "ensureChannel");
|
|
3966
|
-
async function doSpawn(modelServiceId, effectiveScope, requestedClientId) {
|
|
5512
|
+
async function doSpawn(modelServiceId, effectiveScope, approvalMode, requestedClientId, onSessionRegistered, parentSessionId) {
|
|
3967
5513
|
const ci = await ensureChannel();
|
|
3968
5514
|
ci.sessionSpawnsInFlight++;
|
|
3969
5515
|
let sessionRegistered = false;
|
|
5516
|
+
let sessionRemovedDuringInitialization = false;
|
|
5517
|
+
let initializedSessionId;
|
|
3970
5518
|
let newSessionResp;
|
|
3971
5519
|
try {
|
|
3972
5520
|
try {
|
|
@@ -4001,12 +5549,58 @@ function createAcpSessionBridge(opts) {
|
|
|
4001
5549
|
const entry = createSessionEntry(
|
|
4002
5550
|
ci,
|
|
4003
5551
|
newSessionResp.sessionId,
|
|
4004
|
-
boundWorkspace
|
|
5552
|
+
boundWorkspace,
|
|
5553
|
+
void 0,
|
|
5554
|
+
{ parentSessionId }
|
|
4005
5555
|
);
|
|
5556
|
+
initializedSessionId = entry.sessionId;
|
|
4006
5557
|
sessionRegistered = true;
|
|
5558
|
+
onSessionRegistered?.();
|
|
4007
5559
|
seedSnapshotCaches(entry, newSessionResp);
|
|
4008
5560
|
const clientId = registerClient(entry, requestedClientId);
|
|
4009
|
-
|
|
5561
|
+
let parentSessionPersisted;
|
|
5562
|
+
if (entry.parentSessionId) {
|
|
5563
|
+
const parentDeadline = Date.now() + initTimeoutMs;
|
|
5564
|
+
let lastParentErr;
|
|
5565
|
+
for (let attempt = 1; attempt <= MAX_PARENT_PERSIST_ATTEMPTS; attempt++) {
|
|
5566
|
+
const remaining = parentDeadline - Date.now();
|
|
5567
|
+
if (remaining <= 0) {
|
|
5568
|
+
parentSessionPersisted = false;
|
|
5569
|
+
lastParentErr = "deadline exceeded";
|
|
5570
|
+
break;
|
|
5571
|
+
}
|
|
5572
|
+
try {
|
|
5573
|
+
const parentResult = await Promise.race([
|
|
5574
|
+
withTimeout(
|
|
5575
|
+
entry.connection.extMethod(
|
|
5576
|
+
SERVE_CONTROL_EXT_METHODS.sessionParent,
|
|
5577
|
+
{
|
|
5578
|
+
sessionId: entry.sessionId,
|
|
5579
|
+
parentSessionId: entry.parentSessionId
|
|
5580
|
+
}
|
|
5581
|
+
),
|
|
5582
|
+
remaining,
|
|
5583
|
+
"sessionParent"
|
|
5584
|
+
),
|
|
5585
|
+
getTransportClosedReject(entry)
|
|
5586
|
+
]);
|
|
5587
|
+
parentSessionPersisted = parentResult?.persisted === true;
|
|
5588
|
+
break;
|
|
5589
|
+
} catch (err) {
|
|
5590
|
+
lastParentErr = err instanceof Error ? err.message : String(err);
|
|
5591
|
+
const terminal = err instanceof BridgeTimeoutError || err instanceof BridgeChannelClosedError || attempt === MAX_PARENT_PERSIST_ATTEMPTS;
|
|
5592
|
+
if (terminal) {
|
|
5593
|
+
parentSessionPersisted = false;
|
|
5594
|
+
break;
|
|
5595
|
+
}
|
|
5596
|
+
}
|
|
5597
|
+
}
|
|
5598
|
+
if (parentSessionPersisted === false) {
|
|
5599
|
+
writeStderrLine(
|
|
5600
|
+
`qwen serve: parentSessionId for ${entry.sessionId} was not persisted (${lastParentErr ?? "unknown"}) \u2014 the parent link is live-only until restart (reported to the caller via parentSessionPersisted=false)`
|
|
5601
|
+
);
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
4010
5604
|
if (modelServiceId) {
|
|
4011
5605
|
await applyModelServiceId(
|
|
4012
5606
|
entry,
|
|
@@ -4016,22 +5610,51 @@ function createAcpSessionBridge(opts) {
|
|
|
4016
5610
|
).catch(() => {
|
|
4017
5611
|
});
|
|
4018
5612
|
}
|
|
5613
|
+
if (approvalMode) {
|
|
5614
|
+
try {
|
|
5615
|
+
await applyApprovalMode(entry, approvalMode, false, clientId);
|
|
5616
|
+
} catch (err) {
|
|
5617
|
+
try {
|
|
5618
|
+
await closeSessionImpl(entry.sessionId, void 0, {
|
|
5619
|
+
reason: "approval_mode_initialization_failed"
|
|
5620
|
+
});
|
|
5621
|
+
sessionRemovedDuringInitialization = true;
|
|
5622
|
+
} catch {
|
|
5623
|
+
}
|
|
5624
|
+
throw err;
|
|
5625
|
+
}
|
|
5626
|
+
}
|
|
4019
5627
|
if (!byId.has(entry.sessionId)) {
|
|
4020
5628
|
throw new Error(
|
|
4021
|
-
`Session ${entry.sessionId} died during
|
|
5629
|
+
`Session ${entry.sessionId} died during session initialization`
|
|
4022
5630
|
);
|
|
4023
5631
|
}
|
|
5632
|
+
if (effectiveScope === "single" && !defaultEntry) defaultEntry = entry;
|
|
4024
5633
|
return {
|
|
4025
5634
|
sessionId: entry.sessionId,
|
|
4026
5635
|
workspaceCwd: entry.workspaceCwd,
|
|
4027
5636
|
attached: false,
|
|
4028
5637
|
clientId,
|
|
4029
|
-
createdAt: entry.createdAt
|
|
5638
|
+
createdAt: entry.createdAt,
|
|
5639
|
+
...entry.parentSessionId ? { parentSessionPersisted: parentSessionPersisted === true } : {}
|
|
4030
5640
|
};
|
|
4031
5641
|
} finally {
|
|
4032
5642
|
ci.sessionSpawnsInFlight = Math.max(0, ci.sessionSpawnsInFlight - 1);
|
|
4033
5643
|
if (!sessionRegistered) {
|
|
4034
5644
|
await reapPendingEmptyChannel(ci);
|
|
5645
|
+
} else if (sessionRemovedDuringInitialization && hasNoChannelWork(ci)) {
|
|
5646
|
+
await reapPendingEmptyChannel(ci);
|
|
5647
|
+
if (!ci.isDying) {
|
|
5648
|
+
await startIdleTimer(
|
|
5649
|
+
ci,
|
|
5650
|
+
`approval-mode initialization failure "${initializedSessionId}"`
|
|
5651
|
+
);
|
|
5652
|
+
}
|
|
5653
|
+
} else if (sessionRegistered && hasNoChannelWork(ci) && !ci.isDying) {
|
|
5654
|
+
await startIdleTimer(
|
|
5655
|
+
ci,
|
|
5656
|
+
`session orphaned during initialization "${initializedSessionId}"`
|
|
5657
|
+
);
|
|
4035
5658
|
}
|
|
4036
5659
|
}
|
|
4037
5660
|
}
|
|
@@ -4072,30 +5695,146 @@ function createAcpSessionBridge(opts) {
|
|
|
4072
5695
|
requestedModelId: modelId,
|
|
4073
5696
|
error: err instanceof Error ? err.message : String(err)
|
|
4074
5697
|
},
|
|
4075
|
-
...originatorClientId ? { originatorClientId } : {}
|
|
4076
|
-
});
|
|
4077
|
-
throw err;
|
|
5698
|
+
...originatorClientId ? { originatorClientId } : {}
|
|
5699
|
+
});
|
|
5700
|
+
throw err;
|
|
5701
|
+
} finally {
|
|
5702
|
+
entry.modelRoundtripInFlight = false;
|
|
5703
|
+
if (succeeded) {
|
|
5704
|
+
void reconcileAfterRoundtrip(entry, "model");
|
|
5705
|
+
} else {
|
|
5706
|
+
writeStderrLine(
|
|
5707
|
+
`[reconcile] session=${entry.sessionId} target=model action=skipped reason=roundtrip_failed`
|
|
5708
|
+
);
|
|
5709
|
+
}
|
|
5710
|
+
}
|
|
5711
|
+
});
|
|
5712
|
+
entry.modelChangeQueue = work.then(
|
|
5713
|
+
() => void 0,
|
|
5714
|
+
() => void 0
|
|
5715
|
+
);
|
|
5716
|
+
return work;
|
|
5717
|
+
}
|
|
5718
|
+
__name(applyModelServiceId, "applyModelServiceId");
|
|
5719
|
+
async function applyApprovalMode(entry, mode, persist, originatorClientId) {
|
|
5720
|
+
if (persist && !persistApprovalMode) {
|
|
5721
|
+
throw new Error(
|
|
5722
|
+
"setSessionApprovalMode called with `persist: true` but no `persistApprovalMode` callback wired in BridgeOptions. runQwenServe wires the production callback; direct embeds and tests must opt in or omit `persist`."
|
|
5723
|
+
);
|
|
5724
|
+
}
|
|
5725
|
+
const approvalWork = entry.approvalModeQueue.then(async () => {
|
|
5726
|
+
entry.approvalModeRoundtripInFlight = true;
|
|
5727
|
+
let succeeded = false;
|
|
5728
|
+
try {
|
|
5729
|
+
const response = await Promise.race([
|
|
5730
|
+
withTimeout(
|
|
5731
|
+
entry.connection.extMethod(
|
|
5732
|
+
SERVE_CONTROL_EXT_METHODS.sessionApprovalMode,
|
|
5733
|
+
{ sessionId: entry.sessionId, mode }
|
|
5734
|
+
),
|
|
5735
|
+
initTimeoutMs,
|
|
5736
|
+
SERVE_CONTROL_EXT_METHODS.sessionApprovalMode
|
|
5737
|
+
),
|
|
5738
|
+
getTransportClosedReject(entry)
|
|
5739
|
+
]);
|
|
5740
|
+
if (typeof response.current !== "string" || !KNOWN_APPROVAL_MODES.has(response.current)) {
|
|
5741
|
+
throw new Error(
|
|
5742
|
+
`Agent returned unknown approval mode: ${JSON.stringify(response.current)}`
|
|
5743
|
+
);
|
|
5744
|
+
}
|
|
5745
|
+
let persisted = false;
|
|
5746
|
+
if (persist) {
|
|
5747
|
+
try {
|
|
5748
|
+
await withTimeout(
|
|
5749
|
+
persistApprovalMode?.(boundWorkspace, mode) ?? Promise.resolve(),
|
|
5750
|
+
PERSIST_TIMEOUT_MS,
|
|
5751
|
+
"persistApprovalMode"
|
|
5752
|
+
);
|
|
5753
|
+
persisted = persistApprovalMode !== void 0;
|
|
5754
|
+
} catch (err) {
|
|
5755
|
+
writeStderrLine(
|
|
5756
|
+
`setSessionApprovalMode: persist failed: ${err instanceof Error ? err.message : String(err)}`
|
|
5757
|
+
);
|
|
5758
|
+
}
|
|
5759
|
+
}
|
|
5760
|
+
publishApprovalModeChanged(
|
|
5761
|
+
entry,
|
|
5762
|
+
{
|
|
5763
|
+
previous: response.previous,
|
|
5764
|
+
next: response.current,
|
|
5765
|
+
persisted
|
|
5766
|
+
},
|
|
5767
|
+
originatorClientId
|
|
5768
|
+
);
|
|
5769
|
+
if (persisted) {
|
|
5770
|
+
broadcastWorkspaceEvent(
|
|
5771
|
+
{
|
|
5772
|
+
type: "approval_mode_changed",
|
|
5773
|
+
data: {
|
|
5774
|
+
sessionId: entry.sessionId,
|
|
5775
|
+
previous: response.previous,
|
|
5776
|
+
next: response.current,
|
|
5777
|
+
persisted
|
|
5778
|
+
},
|
|
5779
|
+
...originatorClientId ? { originatorClientId } : {}
|
|
5780
|
+
},
|
|
5781
|
+
entry.sessionId
|
|
5782
|
+
);
|
|
5783
|
+
for (const peer of byId.values()) {
|
|
5784
|
+
if (peer.sessionId === entry.sessionId) {
|
|
5785
|
+
continue;
|
|
5786
|
+
}
|
|
5787
|
+
peer.currentApprovalMode = response.current;
|
|
5788
|
+
}
|
|
5789
|
+
}
|
|
5790
|
+
succeeded = true;
|
|
5791
|
+
return {
|
|
5792
|
+
sessionId: entry.sessionId,
|
|
5793
|
+
mode: response.current,
|
|
5794
|
+
previous: response.previous,
|
|
5795
|
+
persisted
|
|
5796
|
+
};
|
|
4078
5797
|
} finally {
|
|
4079
|
-
entry.
|
|
5798
|
+
entry.approvalModeRoundtripInFlight = false;
|
|
4080
5799
|
if (succeeded) {
|
|
4081
|
-
void reconcileAfterRoundtrip(entry, "
|
|
5800
|
+
void reconcileAfterRoundtrip(entry, "approvalMode");
|
|
4082
5801
|
} else {
|
|
4083
5802
|
writeStderrLine(
|
|
4084
|
-
`[reconcile] session=${entry.sessionId} target=
|
|
5803
|
+
`[reconcile] session=${entry.sessionId} target=approvalMode action=skipped reason=roundtrip_failed`
|
|
4085
5804
|
);
|
|
4086
5805
|
}
|
|
4087
5806
|
}
|
|
4088
5807
|
});
|
|
4089
|
-
entry.
|
|
5808
|
+
entry.approvalModeQueue = approvalWork.then(
|
|
4090
5809
|
() => void 0,
|
|
4091
5810
|
() => void 0
|
|
4092
5811
|
);
|
|
4093
|
-
|
|
5812
|
+
try {
|
|
5813
|
+
return await approvalWork;
|
|
5814
|
+
} catch (err) {
|
|
5815
|
+
const data = err?.data;
|
|
5816
|
+
if (data && typeof data === "object" && "errorKind" in data && data.errorKind === "trust_gate") {
|
|
5817
|
+
const rawMessage = err?.message;
|
|
5818
|
+
const message = typeof rawMessage === "string" ? rawMessage : "Trust-gate rejection from ACP child";
|
|
5819
|
+
throw new TrustGateError(message);
|
|
5820
|
+
}
|
|
5821
|
+
throw err;
|
|
5822
|
+
}
|
|
4094
5823
|
}
|
|
4095
|
-
__name(
|
|
5824
|
+
__name(applyApprovalMode, "applyApprovalMode");
|
|
5825
|
+
async function applyApprovalModeForAttach(entry, mode, clientId) {
|
|
5826
|
+
try {
|
|
5827
|
+
await applyApprovalMode(entry, mode, false, clientId);
|
|
5828
|
+
} catch (err) {
|
|
5829
|
+
await rollbackAttachRegistration(entry, clientId);
|
|
5830
|
+
throw err;
|
|
5831
|
+
}
|
|
5832
|
+
}
|
|
5833
|
+
__name(applyApprovalModeForAttach, "applyApprovalModeForAttach");
|
|
4096
5834
|
const cancelPendingForSession = /* @__PURE__ */ __name((sessionId) => {
|
|
4097
5835
|
permissionMediator.forgetSession(sessionId);
|
|
4098
5836
|
byId.get(sessionId)?.pendingPermissionIds.clear();
|
|
5837
|
+
byId.get(sessionId)?.pendingInteractions.clear();
|
|
4099
5838
|
}, "cancelPendingForSession");
|
|
4100
5839
|
const getTransportClosedReject = /* @__PURE__ */ __name((entry) => {
|
|
4101
5840
|
if (!entry.transportClosedReject) {
|
|
@@ -4280,7 +6019,18 @@ function createAcpSessionBridge(opts) {
|
|
|
4280
6019
|
);
|
|
4281
6020
|
}
|
|
4282
6021
|
}, "broadcastWorkspaceEvent");
|
|
4283
|
-
const createSessionEventBus = /* @__PURE__ */ __name(() => new EventBus(
|
|
6022
|
+
const createSessionEventBus = /* @__PURE__ */ __name(() => new EventBus(
|
|
6023
|
+
eventRingSize,
|
|
6024
|
+
void 0,
|
|
6025
|
+
new TurnBoundaryCompactionEngine({
|
|
6026
|
+
maxReplayBytes: compactedReplayMaxBytes,
|
|
6027
|
+
onReplayWindowEviction: /* @__PURE__ */ __name((eviction) => {
|
|
6028
|
+
teeServeDebugLine(
|
|
6029
|
+
`replay window evicted ${JSON.stringify(eviction)}`
|
|
6030
|
+
);
|
|
6031
|
+
}, "onReplayWindowEviction")
|
|
6032
|
+
})
|
|
6033
|
+
), "createSessionEventBus");
|
|
4284
6034
|
const publishModelSwitched = /* @__PURE__ */ __name((entry, modelId, originatorClientId) => {
|
|
4285
6035
|
entry.currentModelId = modelId;
|
|
4286
6036
|
entry.modelPublishGeneration++;
|
|
@@ -4366,10 +6116,15 @@ function createAcpSessionBridge(opts) {
|
|
|
4366
6116
|
sessionId,
|
|
4367
6117
|
workspaceCwd,
|
|
4368
6118
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6119
|
+
...options.parentSessionId ? { parentSessionId: options.parentSessionId } : {},
|
|
4369
6120
|
channel: ci.channel,
|
|
4370
6121
|
connection: ci.connection,
|
|
4371
6122
|
events,
|
|
4372
|
-
artifacts: new SessionArtifactStore({
|
|
6123
|
+
artifacts: new SessionArtifactStore({
|
|
6124
|
+
sessionId,
|
|
6125
|
+
workspaceCwd,
|
|
6126
|
+
persistence: createSessionArtifactPersistence(ci.connection, sessionId)
|
|
6127
|
+
}),
|
|
4373
6128
|
promptQueue: Promise.resolve(),
|
|
4374
6129
|
pendingPromptCount: 0,
|
|
4375
6130
|
pendingPromptList: [],
|
|
@@ -4379,6 +6134,7 @@ function createAcpSessionBridge(opts) {
|
|
|
4379
6134
|
modelPublishGeneration: 0,
|
|
4380
6135
|
approvalModePublishGeneration: 0,
|
|
4381
6136
|
pendingPermissionIds: /* @__PURE__ */ new Set(),
|
|
6137
|
+
pendingInteractions: /* @__PURE__ */ new Map(),
|
|
4382
6138
|
clientIds: /* @__PURE__ */ new Map(),
|
|
4383
6139
|
clientLastSeenAt: /* @__PURE__ */ new Map(),
|
|
4384
6140
|
attachCount: 0,
|
|
@@ -4390,6 +6146,12 @@ function createAcpSessionBridge(opts) {
|
|
|
4390
6146
|
byId.set(entry.sessionId, entry);
|
|
4391
6147
|
touchActivity();
|
|
4392
6148
|
telemetry.metrics?.sessionLifecycle("spawn");
|
|
6149
|
+
emitSessionLifecycle({
|
|
6150
|
+
type: "registered",
|
|
6151
|
+
sessionId: entry.sessionId,
|
|
6152
|
+
workspaceCwd: entry.workspaceCwd,
|
|
6153
|
+
reason: options.lifecycleReason ?? "spawn"
|
|
6154
|
+
});
|
|
4393
6155
|
if (options.drainEarlyEvents !== false) {
|
|
4394
6156
|
ci.client.drainEarlyEvents(entry.sessionId, entry);
|
|
4395
6157
|
}
|
|
@@ -4404,6 +6166,42 @@ function createAcpSessionBridge(opts) {
|
|
|
4404
6166
|
});
|
|
4405
6167
|
}
|
|
4406
6168
|
}, "publishArtifactChanges");
|
|
6169
|
+
const artifactReseedChanges = /* @__PURE__ */ __name((before, after) => {
|
|
6170
|
+
const beforeById = new Map(
|
|
6171
|
+
before.map((artifact) => [artifact.id, artifact])
|
|
6172
|
+
);
|
|
6173
|
+
const afterById = new Map(after.map((artifact) => [artifact.id, artifact]));
|
|
6174
|
+
const changes = [];
|
|
6175
|
+
for (const artifact of before) {
|
|
6176
|
+
if (!afterById.has(artifact.id)) {
|
|
6177
|
+
changes.push({
|
|
6178
|
+
action: "removed",
|
|
6179
|
+
artifactId: artifact.id,
|
|
6180
|
+
artifact,
|
|
6181
|
+
reason: "eviction"
|
|
6182
|
+
});
|
|
6183
|
+
}
|
|
6184
|
+
}
|
|
6185
|
+
for (const artifact of after) {
|
|
6186
|
+
const previous = beforeById.get(artifact.id);
|
|
6187
|
+
if (!previous) {
|
|
6188
|
+
changes.push({
|
|
6189
|
+
action: "created",
|
|
6190
|
+
artifactId: artifact.id,
|
|
6191
|
+
artifact
|
|
6192
|
+
});
|
|
6193
|
+
continue;
|
|
6194
|
+
}
|
|
6195
|
+
if (!publicArtifactsEqual(previous, artifact)) {
|
|
6196
|
+
changes.push({
|
|
6197
|
+
action: "updated",
|
|
6198
|
+
artifactId: artifact.id,
|
|
6199
|
+
artifact
|
|
6200
|
+
});
|
|
6201
|
+
}
|
|
6202
|
+
}
|
|
6203
|
+
return changes;
|
|
6204
|
+
}, "artifactReseedChanges");
|
|
4407
6205
|
const makeClientArtifactInput = /* @__PURE__ */ __name((artifact, clientId) => {
|
|
4408
6206
|
const input = {
|
|
4409
6207
|
title: artifact.title,
|
|
@@ -4416,6 +6214,8 @@ function createAcpSessionBridge(opts) {
|
|
|
4416
6214
|
mimeType: artifact.mimeType,
|
|
4417
6215
|
sizeBytes: artifact.sizeBytes,
|
|
4418
6216
|
metadata: artifact.metadata,
|
|
6217
|
+
retention: artifact.retention,
|
|
6218
|
+
clientRetained: artifact.clientRetained,
|
|
4419
6219
|
source: "client"
|
|
4420
6220
|
};
|
|
4421
6221
|
if (clientId) {
|
|
@@ -4423,6 +6223,31 @@ function createAcpSessionBridge(opts) {
|
|
|
4423
6223
|
}
|
|
4424
6224
|
return input;
|
|
4425
6225
|
}, "makeClientArtifactInput");
|
|
6226
|
+
function createSessionArtifactPersistence(connection, sessionId) {
|
|
6227
|
+
return {
|
|
6228
|
+
recordEvent: /* @__PURE__ */ __name(async (payload) => {
|
|
6229
|
+
await connection.extMethod(
|
|
6230
|
+
SERVE_CONTROL_EXT_METHODS.sessionArtifactsPersist,
|
|
6231
|
+
{
|
|
6232
|
+
sessionId,
|
|
6233
|
+
kind: "event",
|
|
6234
|
+
payload
|
|
6235
|
+
}
|
|
6236
|
+
);
|
|
6237
|
+
}, "recordEvent"),
|
|
6238
|
+
recordSnapshot: /* @__PURE__ */ __name(async (payload) => {
|
|
6239
|
+
await connection.extMethod(
|
|
6240
|
+
SERVE_CONTROL_EXT_METHODS.sessionArtifactsPersist,
|
|
6241
|
+
{
|
|
6242
|
+
sessionId,
|
|
6243
|
+
kind: "snapshot",
|
|
6244
|
+
payload
|
|
6245
|
+
}
|
|
6246
|
+
);
|
|
6247
|
+
}, "recordSnapshot")
|
|
6248
|
+
};
|
|
6249
|
+
}
|
|
6250
|
+
__name(createSessionArtifactPersistence, "createSessionArtifactPersistence");
|
|
4426
6251
|
const seedSnapshotCaches = /* @__PURE__ */ __name((entry, resp) => {
|
|
4427
6252
|
const model = resp.models?.currentModelId;
|
|
4428
6253
|
if (typeof model === "string" && model.length > 0) {
|
|
@@ -4470,16 +6295,55 @@ function createAcpSessionBridge(opts) {
|
|
|
4470
6295
|
}
|
|
4471
6296
|
return { lastEventId: snapshot.lastEventId, ...replayStatus };
|
|
4472
6297
|
}, "replayFieldsFor");
|
|
4473
|
-
|
|
6298
|
+
const restoredArtifactSnapshotFromState = /* @__PURE__ */ __name((state) => {
|
|
6299
|
+
const candidate = state.artifactSnapshot;
|
|
6300
|
+
const warnings = [];
|
|
6301
|
+
const snapshot = normalizeSnapshotPayload(candidate, warnings);
|
|
6302
|
+
if (!snapshot) return void 0;
|
|
6303
|
+
const snapshotWarnings = isRecord2(candidate) && Array.isArray(candidate["warnings"]) ? candidate["warnings"].filter(
|
|
6304
|
+
(warning) => typeof warning === "string" && warning.length <= 1e3
|
|
6305
|
+
).slice(-500) : [];
|
|
6306
|
+
return {
|
|
6307
|
+
v: SESSION_ARTIFACT_PERSISTENCE_VERSION,
|
|
6308
|
+
sessionId: snapshot.sessionId,
|
|
6309
|
+
sequence: snapshot.sequence,
|
|
6310
|
+
artifacts: snapshot.artifacts,
|
|
6311
|
+
...snapshot.markerArtifacts ? { markerArtifacts: snapshot.markerArtifacts } : {},
|
|
6312
|
+
tombstonedIds: snapshot.tombstonedIds ?? [],
|
|
6313
|
+
stickyEphemeralIds: snapshot.stickyEphemeralIds ?? [],
|
|
6314
|
+
warnings: [...warnings, ...snapshotWarnings]
|
|
6315
|
+
};
|
|
6316
|
+
}, "restoredArtifactSnapshotFromState");
|
|
6317
|
+
const artifactSnapshotUnavailableReason = /* @__PURE__ */ __name((state) => {
|
|
6318
|
+
const reason = state.artifactSnapshotUnavailable;
|
|
6319
|
+
return typeof reason === "string" && reason ? reason : void 0;
|
|
6320
|
+
}, "artifactSnapshotUnavailableReason");
|
|
6321
|
+
const publicRestoreState = /* @__PURE__ */ __name((state) => {
|
|
6322
|
+
const {
|
|
6323
|
+
artifactSnapshot: _artifactSnapshot,
|
|
6324
|
+
artifactSnapshotUnavailable: _artifactSnapshotUnavailable,
|
|
6325
|
+
...publicState
|
|
6326
|
+
} = state;
|
|
6327
|
+
return publicState;
|
|
6328
|
+
}, "publicRestoreState");
|
|
6329
|
+
async function restoreSession(action, req, options = {}) {
|
|
4474
6330
|
if (shuttingDown) {
|
|
4475
6331
|
throw new Error("AcpSessionBridge is shutting down");
|
|
4476
6332
|
}
|
|
4477
6333
|
const workspaceKey = resolveWorkspaceKey(req.workspaceCwd);
|
|
6334
|
+
if (req.approvalMode !== void 0 && !KNOWN_APPROVAL_MODES.has(req.approvalMode)) {
|
|
6335
|
+
throw new Error(
|
|
6336
|
+
`Invalid approvalMode: ${JSON.stringify(req.approvalMode)}`
|
|
6337
|
+
);
|
|
6338
|
+
}
|
|
4478
6339
|
const historyReplay = action === "load" ? req.historyReplay ?? "stream" : "stream";
|
|
4479
6340
|
const existing = byId.get(req.sessionId);
|
|
4480
6341
|
if (existing) {
|
|
4481
6342
|
existing.attachCount++;
|
|
4482
6343
|
const clientId = registerClient(existing, req.clientId);
|
|
6344
|
+
if (req.approvalMode) {
|
|
6345
|
+
await applyApprovalModeForAttach(existing, req.approvalMode, clientId);
|
|
6346
|
+
}
|
|
4483
6347
|
return {
|
|
4484
6348
|
sessionId: existing.sessionId,
|
|
4485
6349
|
workspaceCwd: existing.workspaceCwd,
|
|
@@ -4518,10 +6382,14 @@ function createAcpSessionBridge(opts) {
|
|
|
4518
6382
|
"the agent child likely crashed during session restore \u2014 retry to restore the session"
|
|
4519
6383
|
);
|
|
4520
6384
|
}
|
|
6385
|
+
const clientId = registerClient(entry, req.clientId);
|
|
6386
|
+
if (req.approvalMode) {
|
|
6387
|
+
await applyApprovalModeForAttach(entry, req.approvalMode, clientId);
|
|
6388
|
+
}
|
|
4521
6389
|
return {
|
|
4522
6390
|
...restored,
|
|
4523
6391
|
attached: true,
|
|
4524
|
-
clientId
|
|
6392
|
+
clientId,
|
|
4525
6393
|
createdAt: entry.createdAt,
|
|
4526
6394
|
hasActivePrompt: entry.promptActive
|
|
4527
6395
|
};
|
|
@@ -4533,6 +6401,17 @@ function createAcpSessionBridge(opts) {
|
|
|
4533
6401
|
let registeredEntry;
|
|
4534
6402
|
let ci;
|
|
4535
6403
|
const coalesceState = { count: 0 };
|
|
6404
|
+
const admission = options.skipFreshSessionAdmission === true ? void 0 : reserveFreshSession({
|
|
6405
|
+
operation: action,
|
|
6406
|
+
workspaceCwd: workspaceKey,
|
|
6407
|
+
sessionId: req.sessionId
|
|
6408
|
+
});
|
|
6409
|
+
let admissionReleased = false;
|
|
6410
|
+
const releaseAdmissionOnce = /* @__PURE__ */ __name(() => {
|
|
6411
|
+
if (admissionReleased) return;
|
|
6412
|
+
admissionReleased = true;
|
|
6413
|
+
releaseFreshSessionReservation(admission);
|
|
6414
|
+
}, "releaseAdmissionOnce");
|
|
4536
6415
|
const promise = (async () => {
|
|
4537
6416
|
pendingRestoreEvents.set(req.sessionId, restoreEvents);
|
|
4538
6417
|
ci = await ensureChannel();
|
|
@@ -4620,6 +6499,23 @@ function createAcpSessionBridge(opts) {
|
|
|
4620
6499
|
restoreEvents.close();
|
|
4621
6500
|
racedEntry.attachCount += 1 + coalesceState.count;
|
|
4622
6501
|
const clientId2 = registerClient(racedEntry, req.clientId);
|
|
6502
|
+
if (req.approvalMode) {
|
|
6503
|
+
try {
|
|
6504
|
+
await applyApprovalMode(
|
|
6505
|
+
racedEntry,
|
|
6506
|
+
req.approvalMode,
|
|
6507
|
+
false,
|
|
6508
|
+
clientId2
|
|
6509
|
+
);
|
|
6510
|
+
} catch (err) {
|
|
6511
|
+
await rollbackAttachRegistration(
|
|
6512
|
+
racedEntry,
|
|
6513
|
+
clientId2,
|
|
6514
|
+
1 + coalesceState.count
|
|
6515
|
+
);
|
|
6516
|
+
throw err;
|
|
6517
|
+
}
|
|
6518
|
+
}
|
|
4623
6519
|
return {
|
|
4624
6520
|
sessionId: racedEntry.sessionId,
|
|
4625
6521
|
workspaceCwd: racedEntry.workspaceCwd,
|
|
@@ -4636,21 +6532,48 @@ function createAcpSessionBridge(opts) {
|
|
|
4636
6532
|
req.sessionId,
|
|
4637
6533
|
workspaceKey,
|
|
4638
6534
|
restoreEvents,
|
|
4639
|
-
{
|
|
6535
|
+
{
|
|
6536
|
+
drainEarlyEvents: replayUpdates.length === 0,
|
|
6537
|
+
lifecycleReason: action,
|
|
6538
|
+
// Re-seed the persisted parent lineage the caller recovered from the
|
|
6539
|
+
// transcript, so a restored sub-session's status reports its parent.
|
|
6540
|
+
...req.parentSessionId ? { parentSessionId: req.parentSessionId } : {}
|
|
6541
|
+
}
|
|
4640
6542
|
);
|
|
4641
|
-
|
|
6543
|
+
releaseAdmissionOnce();
|
|
6544
|
+
const restoredArtifactSnapshot = restoredArtifactSnapshotFromState(state);
|
|
6545
|
+
const publicState = publicRestoreState(state);
|
|
6546
|
+
entry.restoreState = publicState;
|
|
4642
6547
|
if (replayPartial === true) {
|
|
4643
6548
|
entry.restoreReplayPartial = true;
|
|
4644
6549
|
}
|
|
4645
6550
|
if (replayError !== void 0) {
|
|
4646
6551
|
entry.restoreReplayError = replayError;
|
|
4647
6552
|
}
|
|
4648
|
-
seedSnapshotCaches(entry,
|
|
6553
|
+
seedSnapshotCaches(entry, publicState);
|
|
6554
|
+
const artifactRestoreWarnings = await entry.artifacts.restore(
|
|
6555
|
+
restoredArtifactSnapshot
|
|
6556
|
+
);
|
|
6557
|
+
for (const warning of artifactRestoreWarnings) {
|
|
6558
|
+
writeStderrLine(
|
|
6559
|
+
`[artifacts] session=${entry.sessionId} action=restore_warning warning=${JSON.stringify(
|
|
6560
|
+
warning
|
|
6561
|
+
)}`
|
|
6562
|
+
);
|
|
6563
|
+
}
|
|
6564
|
+
const artifactRestoreFailed = artifactRestoreWarnings.some(
|
|
6565
|
+
(warning) => isArtifactRestoreFailureWarning(warning)
|
|
6566
|
+
);
|
|
4649
6567
|
if (replayUpdates.length > 0) {
|
|
4650
|
-
await ci.client.seedSessionUpdates(entry, replayUpdates
|
|
6568
|
+
await ci.client.seedSessionUpdates(entry, replayUpdates, {
|
|
6569
|
+
ingestArtifacts: restoredArtifactSnapshot === void 0 || artifactRestoreFailed
|
|
6570
|
+
});
|
|
4651
6571
|
ci.client.drainEarlyEvents(entry.sessionId, entry);
|
|
4652
6572
|
}
|
|
4653
6573
|
const clientId = registerClient(entry, req.clientId);
|
|
6574
|
+
if (req.approvalMode) {
|
|
6575
|
+
await applyApprovalModeForAttach(entry, req.approvalMode, clientId);
|
|
6576
|
+
}
|
|
4654
6577
|
entry.attachCount = coalesceState.count;
|
|
4655
6578
|
registeredEntry = entry;
|
|
4656
6579
|
return {
|
|
@@ -4659,20 +6582,29 @@ function createAcpSessionBridge(opts) {
|
|
|
4659
6582
|
attached: false,
|
|
4660
6583
|
clientId,
|
|
4661
6584
|
createdAt: entry.createdAt,
|
|
4662
|
-
state,
|
|
6585
|
+
state: publicState,
|
|
6586
|
+
...artifactRestoreWarnings.length > 0 ? { artifactWarnings: artifactRestoreWarnings } : {},
|
|
4663
6587
|
hasActivePrompt: entry.promptActive,
|
|
4664
6588
|
...replayFieldsFor(entry, action)
|
|
4665
6589
|
};
|
|
4666
6590
|
})().finally(async () => {
|
|
6591
|
+
releaseAdmissionOnce();
|
|
4667
6592
|
ci?.pendingRestoreIds.delete(req.sessionId);
|
|
4668
6593
|
ci?.client.clearRestoreInFlight(req.sessionId);
|
|
4669
6594
|
pendingRestoreEvents.delete(req.sessionId);
|
|
4670
6595
|
if (!registeredEntry) {
|
|
4671
6596
|
restoreEvents.close();
|
|
4672
6597
|
let removedRestoreEntry = false;
|
|
4673
|
-
|
|
6598
|
+
const restoreEntry = byId.get(req.sessionId);
|
|
6599
|
+
if (restoreEntry?.events === restoreEvents) {
|
|
4674
6600
|
byId.delete(req.sessionId);
|
|
4675
6601
|
ci?.sessionIds.delete(req.sessionId);
|
|
6602
|
+
emitSessionLifecycle({
|
|
6603
|
+
type: "removed",
|
|
6604
|
+
sessionId: req.sessionId,
|
|
6605
|
+
workspaceCwd: restoreEntry.workspaceCwd,
|
|
6606
|
+
reason: "restore_failed"
|
|
6607
|
+
});
|
|
4676
6608
|
removedRestoreEntry = true;
|
|
4677
6609
|
}
|
|
4678
6610
|
if (removedRestoreEntry && ci && hasNoChannelWork(ci)) {
|
|
@@ -4733,6 +6665,7 @@ function createAcpSessionBridge(opts) {
|
|
|
4733
6665
|
}
|
|
4734
6666
|
permissionMediator.forgetSession(sessionId);
|
|
4735
6667
|
entry.pendingPermissionIds.clear();
|
|
6668
|
+
entry.pendingInteractions.clear();
|
|
4736
6669
|
if (entry.promptActive) {
|
|
4737
6670
|
entry.promptActive = false;
|
|
4738
6671
|
activePromptCounter--;
|
|
@@ -4740,6 +6673,12 @@ function createAcpSessionBridge(opts) {
|
|
|
4740
6673
|
}
|
|
4741
6674
|
byId.delete(sessionId);
|
|
4742
6675
|
telemetry.metrics?.sessionLifecycle("close");
|
|
6676
|
+
emitSessionLifecycle({
|
|
6677
|
+
type: "removed",
|
|
6678
|
+
sessionId,
|
|
6679
|
+
workspaceCwd: entry.workspaceCwd,
|
|
6680
|
+
reason
|
|
6681
|
+
});
|
|
4743
6682
|
ci?.client.markSessionClosed(sessionId);
|
|
4744
6683
|
try {
|
|
4745
6684
|
entry.events.publish({
|
|
@@ -4788,6 +6727,7 @@ function createAcpSessionBridge(opts) {
|
|
|
4788
6727
|
maxSessions: maxSessions === Infinity ? null : maxSessions,
|
|
4789
6728
|
maxPendingPromptsPerSession: maxPendingPromptsPerSession === Infinity ? null : maxPendingPromptsPerSession,
|
|
4790
6729
|
eventRingSize,
|
|
6730
|
+
compactedReplayMaxBytes,
|
|
4791
6731
|
channelIdleTimeoutMs: resolvedChannelIdleTimeoutMs(),
|
|
4792
6732
|
sessionIdleTimeoutMs
|
|
4793
6733
|
},
|
|
@@ -4862,6 +6802,11 @@ function createAcpSessionBridge(opts) {
|
|
|
4862
6802
|
throw new InvalidSessionScopeError(req.sessionScope);
|
|
4863
6803
|
}
|
|
4864
6804
|
const effectiveScope = req.sessionScope ?? defaultSessionScope;
|
|
6805
|
+
if (req.approvalMode !== void 0 && !KNOWN_APPROVAL_MODES.has(req.approvalMode)) {
|
|
6806
|
+
throw new Error(
|
|
6807
|
+
`Invalid approvalMode: ${JSON.stringify(req.approvalMode)}`
|
|
6808
|
+
);
|
|
6809
|
+
}
|
|
4865
6810
|
if (effectiveScope === "single") {
|
|
4866
6811
|
const existing = defaultEntry;
|
|
4867
6812
|
if (existing) {
|
|
@@ -4876,6 +6821,13 @@ function createAcpSessionBridge(opts) {
|
|
|
4876
6821
|
).catch(() => {
|
|
4877
6822
|
});
|
|
4878
6823
|
}
|
|
6824
|
+
if (req.approvalMode) {
|
|
6825
|
+
await applyApprovalModeForAttach(
|
|
6826
|
+
existing,
|
|
6827
|
+
req.approvalMode,
|
|
6828
|
+
clientId
|
|
6829
|
+
);
|
|
6830
|
+
}
|
|
4879
6831
|
return {
|
|
4880
6832
|
sessionId: existing.sessionId,
|
|
4881
6833
|
workspaceCwd: existing.workspaceCwd,
|
|
@@ -4906,6 +6858,13 @@ function createAcpSessionBridge(opts) {
|
|
|
4906
6858
|
).catch(() => {
|
|
4907
6859
|
});
|
|
4908
6860
|
}
|
|
6861
|
+
if (req.approvalMode) {
|
|
6862
|
+
await applyApprovalModeForAttach(
|
|
6863
|
+
attachedEntry,
|
|
6864
|
+
req.approvalMode,
|
|
6865
|
+
clientId
|
|
6866
|
+
);
|
|
6867
|
+
}
|
|
4909
6868
|
return {
|
|
4910
6869
|
...session,
|
|
4911
6870
|
attached: true,
|
|
@@ -4917,12 +6876,30 @@ function createAcpSessionBridge(opts) {
|
|
|
4917
6876
|
if (byId.size + inFlightSpawns.size + inFlightRestores.size >= maxSessions) {
|
|
4918
6877
|
throw new SessionLimitExceededError(maxSessions);
|
|
4919
6878
|
}
|
|
4920
|
-
const
|
|
6879
|
+
const admission = reserveFreshSession({
|
|
6880
|
+
operation: "spawn",
|
|
6881
|
+
workspaceCwd: workspaceKey
|
|
6882
|
+
});
|
|
6883
|
+
let admissionReleased = false;
|
|
6884
|
+
const releaseAdmissionOnce = /* @__PURE__ */ __name(() => {
|
|
6885
|
+
if (admissionReleased) return;
|
|
6886
|
+
admissionReleased = true;
|
|
6887
|
+
releaseFreshSessionReservation(admission);
|
|
6888
|
+
}, "releaseAdmissionOnce");
|
|
6889
|
+
const promise = doSpawn(
|
|
6890
|
+
req.modelServiceId,
|
|
6891
|
+
effectiveScope,
|
|
6892
|
+
req.approvalMode,
|
|
6893
|
+
req.clientId,
|
|
6894
|
+
releaseAdmissionOnce,
|
|
6895
|
+
req.parentSessionId
|
|
6896
|
+
);
|
|
4921
6897
|
const tracker = effectiveScope === "single" ? workspaceKey : `${workspaceKey}#${randomUUID2()}`;
|
|
4922
6898
|
inFlightSpawns.set(tracker, promise);
|
|
4923
6899
|
try {
|
|
4924
6900
|
return await promise;
|
|
4925
6901
|
} finally {
|
|
6902
|
+
releaseAdmissionOnce();
|
|
4926
6903
|
inFlightSpawns.delete(tracker);
|
|
4927
6904
|
}
|
|
4928
6905
|
},
|
|
@@ -5056,6 +7033,7 @@ function createAcpSessionBridge(opts) {
|
|
|
5056
7033
|
return copy;
|
|
5057
7034
|
})();
|
|
5058
7035
|
entry.promptActive = true;
|
|
7036
|
+
delete entry.turnError;
|
|
5059
7037
|
activePromptCounter++;
|
|
5060
7038
|
entry.sessionLastSeenAt = Date.now();
|
|
5061
7039
|
touchActivity();
|
|
@@ -5379,67 +7357,89 @@ function createAcpSessionBridge(opts) {
|
|
|
5379
7357
|
if (byId.size + inFlightSpawns.size + inFlightRestores.size >= maxSessions) {
|
|
5380
7358
|
throw new SessionLimitExceededError(maxSessions);
|
|
5381
7359
|
}
|
|
5382
|
-
const
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
throw new Error(
|
|
5394
|
-
`branchSession: agent returned invalid response: ${JSON.stringify(result)}`
|
|
5395
|
-
);
|
|
5396
|
-
}
|
|
5397
|
-
const rawBranchName = result.displayName ?? result.title;
|
|
5398
|
-
const branchDisplayName = typeof rawBranchName === "string" ? rawBranchName : result.newSessionId.slice(0, 8);
|
|
5399
|
-
let restored;
|
|
7360
|
+
const admission = reserveFreshSession({
|
|
7361
|
+
operation: "branch",
|
|
7362
|
+
workspaceCwd: boundWorkspace,
|
|
7363
|
+
sourceSessionId: sessionId
|
|
7364
|
+
});
|
|
7365
|
+
let admissionReleased = false;
|
|
7366
|
+
const releaseAdmissionOnce = /* @__PURE__ */ __name(() => {
|
|
7367
|
+
if (admissionReleased) return;
|
|
7368
|
+
admissionReleased = true;
|
|
7369
|
+
releaseFreshSessionReservation(admission);
|
|
7370
|
+
}, "releaseAdmissionOnce");
|
|
5400
7371
|
try {
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
7372
|
+
const ci = await ensureChannel();
|
|
7373
|
+
const result = await withTimeout(
|
|
7374
|
+
ci.connection.extMethod(SERVE_CONTROL_EXT_METHODS.sessionBranch, {
|
|
7375
|
+
sessionId,
|
|
7376
|
+
cwd: boundWorkspace,
|
|
7377
|
+
name: req.name
|
|
7378
|
+
}),
|
|
7379
|
+
initTimeoutMs,
|
|
7380
|
+
"branchSession"
|
|
5409
7381
|
);
|
|
7382
|
+
if (!result || typeof result.newSessionId !== "string") {
|
|
7383
|
+
throw new Error(
|
|
7384
|
+
`branchSession: agent returned invalid response: ${JSON.stringify(result)}`
|
|
7385
|
+
);
|
|
7386
|
+
}
|
|
7387
|
+
const rawBranchName = result.displayName ?? result.title;
|
|
7388
|
+
const branchDisplayName = typeof rawBranchName === "string" ? rawBranchName : result.newSessionId.slice(0, 8);
|
|
7389
|
+
let restored;
|
|
5410
7390
|
try {
|
|
5411
|
-
await
|
|
5412
|
-
|
|
5413
|
-
{
|
|
7391
|
+
restored = await restoreSession(
|
|
7392
|
+
"load",
|
|
7393
|
+
{
|
|
7394
|
+
sessionId: result.newSessionId,
|
|
7395
|
+
workspaceCwd: boundWorkspace,
|
|
7396
|
+
clientId: context?.clientId
|
|
7397
|
+
},
|
|
7398
|
+
{
|
|
7399
|
+
skipFreshSessionAdmission: true
|
|
7400
|
+
}
|
|
5414
7401
|
);
|
|
5415
|
-
|
|
7402
|
+
releaseAdmissionOnce();
|
|
7403
|
+
} catch (restoreErr) {
|
|
5416
7404
|
writeStderrLine(
|
|
5417
|
-
`qwen serve: branchSession
|
|
7405
|
+
`qwen serve: branchSession load failed for ${result.newSessionId}, attempting cleanup...`
|
|
5418
7406
|
);
|
|
7407
|
+
try {
|
|
7408
|
+
await ci.connection.extMethod(
|
|
7409
|
+
SERVE_CONTROL_EXT_METHODS.sessionClose,
|
|
7410
|
+
{ sessionId: result.newSessionId, cwd: boundWorkspace }
|
|
7411
|
+
);
|
|
7412
|
+
} catch (cleanupErr) {
|
|
7413
|
+
writeStderrLine(
|
|
7414
|
+
`qwen serve: branchSession cleanup of ${result.newSessionId} failed: ${cleanupErr instanceof Error ? cleanupErr.message : cleanupErr}`
|
|
7415
|
+
);
|
|
7416
|
+
}
|
|
7417
|
+
throw restoreErr;
|
|
5419
7418
|
}
|
|
5420
|
-
|
|
7419
|
+
const newEntry = byId.get(result.newSessionId);
|
|
7420
|
+
if (newEntry) newEntry.displayName = branchDisplayName;
|
|
7421
|
+
const eventData = {
|
|
7422
|
+
sourceSessionId: sessionId,
|
|
7423
|
+
newSessionId: result.newSessionId,
|
|
7424
|
+
displayName: branchDisplayName
|
|
7425
|
+
};
|
|
7426
|
+
const branchEnvelope = {
|
|
7427
|
+
type: "session_branched",
|
|
7428
|
+
data: eventData,
|
|
7429
|
+
...originatorClientId ? { originatorClientId } : {}
|
|
7430
|
+
};
|
|
7431
|
+
newEntry?.events.publish(branchEnvelope);
|
|
7432
|
+
return {
|
|
7433
|
+
...restored,
|
|
7434
|
+
displayName: branchDisplayName,
|
|
7435
|
+
forkedFrom: {
|
|
7436
|
+
sessionId,
|
|
7437
|
+
displayName: entry.displayName ?? sessionId.slice(0, 8)
|
|
7438
|
+
}
|
|
7439
|
+
};
|
|
7440
|
+
} finally {
|
|
7441
|
+
releaseAdmissionOnce();
|
|
5421
7442
|
}
|
|
5422
|
-
const newEntry = byId.get(result.newSessionId);
|
|
5423
|
-
if (newEntry) newEntry.displayName = branchDisplayName;
|
|
5424
|
-
const eventData = {
|
|
5425
|
-
sourceSessionId: sessionId,
|
|
5426
|
-
newSessionId: result.newSessionId,
|
|
5427
|
-
displayName: branchDisplayName
|
|
5428
|
-
};
|
|
5429
|
-
const branchEnvelope = {
|
|
5430
|
-
type: "session_branched",
|
|
5431
|
-
data: eventData,
|
|
5432
|
-
...originatorClientId ? { originatorClientId } : {}
|
|
5433
|
-
};
|
|
5434
|
-
newEntry?.events.publish(branchEnvelope);
|
|
5435
|
-
return {
|
|
5436
|
-
...restored,
|
|
5437
|
-
displayName: branchDisplayName,
|
|
5438
|
-
forkedFrom: {
|
|
5439
|
-
sessionId,
|
|
5440
|
-
displayName: entry.displayName ?? sessionId.slice(0, 8)
|
|
5441
|
-
}
|
|
5442
|
-
};
|
|
5443
7443
|
});
|
|
5444
7444
|
entry.promptQueue = branchResult.then(
|
|
5445
7445
|
() => void 0,
|
|
@@ -5556,9 +7556,10 @@ function createAcpSessionBridge(opts) {
|
|
|
5556
7556
|
}
|
|
5557
7557
|
return { displayName: entry.displayName };
|
|
5558
7558
|
},
|
|
5559
|
-
async getSessionArtifacts(sessionId) {
|
|
7559
|
+
async getSessionArtifacts(sessionId, context) {
|
|
5560
7560
|
const entry = byId.get(sessionId);
|
|
5561
7561
|
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
7562
|
+
resolveTrustedClientId(entry, context?.clientId);
|
|
5562
7563
|
return entry.artifacts.list();
|
|
5563
7564
|
},
|
|
5564
7565
|
async addSessionArtifact(sessionId, artifact, context) {
|
|
@@ -5566,9 +7567,13 @@ function createAcpSessionBridge(opts) {
|
|
|
5566
7567
|
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
5567
7568
|
const clientId = resolveTrustedClientId(entry, context?.clientId);
|
|
5568
7569
|
const input = makeClientArtifactInput(artifact, clientId);
|
|
5569
|
-
const result = await entry.artifacts.upsertMany([input], {
|
|
7570
|
+
const result = await entry.artifacts.upsertMany([input], {
|
|
7571
|
+
validationStrict: true,
|
|
7572
|
+
persistenceStrict: false
|
|
7573
|
+
});
|
|
5570
7574
|
publishArtifactChanges(entry, result.changes, clientId);
|
|
5571
|
-
|
|
7575
|
+
const warnings = [...result.warnings ?? []];
|
|
7576
|
+
return warnings.length > 0 ? { ...result, warnings } : result;
|
|
5572
7577
|
},
|
|
5573
7578
|
async removeSessionArtifact(sessionId, artifactId, context) {
|
|
5574
7579
|
const entry = byId.get(sessionId);
|
|
@@ -5576,7 +7581,8 @@ function createAcpSessionBridge(opts) {
|
|
|
5576
7581
|
const clientId = resolveTrustedClientId(entry, context?.clientId);
|
|
5577
7582
|
const result = await entry.artifacts.remove(artifactId, { clientId });
|
|
5578
7583
|
publishArtifactChanges(entry, result.changes, clientId);
|
|
5579
|
-
|
|
7584
|
+
const warnings = [...result.warnings ?? []];
|
|
7585
|
+
return warnings.length > 0 ? { ...result, warnings } : result;
|
|
5580
7586
|
},
|
|
5581
7587
|
listWorkspaceSessions(workspaceCwd) {
|
|
5582
7588
|
if (!path3.isAbsolute(workspaceCwd)) return [];
|
|
@@ -5861,6 +7867,35 @@ function createAcpSessionBridge(opts) {
|
|
|
5861
7867
|
SERVE_STATUS_EXT_METHODS.sessionLspStatus
|
|
5862
7868
|
);
|
|
5863
7869
|
},
|
|
7870
|
+
async getSessionTranscriptPage(req) {
|
|
7871
|
+
const info = await ensureChannel();
|
|
7872
|
+
try {
|
|
7873
|
+
const response = await withWorkspaceControl(
|
|
7874
|
+
info,
|
|
7875
|
+
() => withTimeout(
|
|
7876
|
+
Promise.race([
|
|
7877
|
+
info.connection.extMethod(
|
|
7878
|
+
SERVE_STATUS_EXT_METHODS.sessionTranscript,
|
|
7879
|
+
{ ...req, cwd: boundWorkspace }
|
|
7880
|
+
),
|
|
7881
|
+
getChannelClosedReject(info)
|
|
7882
|
+
]),
|
|
7883
|
+
Math.max(initTimeoutMs, SESSION_TRANSCRIPT_TIMEOUT_MS),
|
|
7884
|
+
SERVE_STATUS_EXT_METHODS.sessionTranscript
|
|
7885
|
+
)
|
|
7886
|
+
);
|
|
7887
|
+
return response;
|
|
7888
|
+
} catch (err) {
|
|
7889
|
+
if (isAcpSessionResourceNotFound(err, req.sessionId)) {
|
|
7890
|
+
throw new SessionNotFoundError(req.sessionId);
|
|
7891
|
+
}
|
|
7892
|
+
throw err;
|
|
7893
|
+
} finally {
|
|
7894
|
+
if (hasNoChannelWork(info)) {
|
|
7895
|
+
await startIdleTimer(info, "session transcript");
|
|
7896
|
+
}
|
|
7897
|
+
}
|
|
7898
|
+
},
|
|
5864
7899
|
async cancelSessionTask(sessionId, taskId, taskKind) {
|
|
5865
7900
|
return requestSessionStatus(
|
|
5866
7901
|
sessionId,
|
|
@@ -6099,109 +8134,12 @@ function createAcpSessionBridge(opts) {
|
|
|
6099
8134
|
entry,
|
|
6100
8135
|
context?.clientId
|
|
6101
8136
|
);
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
const approvalWork = entry.approvalModeQueue.then(async () => {
|
|
6108
|
-
entry.approvalModeRoundtripInFlight = true;
|
|
6109
|
-
let succeeded = false;
|
|
6110
|
-
try {
|
|
6111
|
-
const response = await Promise.race([
|
|
6112
|
-
withTimeout(
|
|
6113
|
-
entry.connection.extMethod(
|
|
6114
|
-
SERVE_CONTROL_EXT_METHODS.sessionApprovalMode,
|
|
6115
|
-
{ sessionId, mode }
|
|
6116
|
-
),
|
|
6117
|
-
initTimeoutMs,
|
|
6118
|
-
SERVE_CONTROL_EXT_METHODS.sessionApprovalMode
|
|
6119
|
-
),
|
|
6120
|
-
getTransportClosedReject(entry)
|
|
6121
|
-
]);
|
|
6122
|
-
if (typeof response.current !== "string" || !KNOWN_APPROVAL_MODES.has(response.current)) {
|
|
6123
|
-
throw new Error(
|
|
6124
|
-
`Agent returned unknown approval mode: ${JSON.stringify(response.current)}`
|
|
6125
|
-
);
|
|
6126
|
-
}
|
|
6127
|
-
let persisted = false;
|
|
6128
|
-
if (opts2.persist) {
|
|
6129
|
-
try {
|
|
6130
|
-
await withTimeout(
|
|
6131
|
-
persistApprovalMode?.(boundWorkspace, mode) ?? Promise.resolve(),
|
|
6132
|
-
PERSIST_TIMEOUT_MS,
|
|
6133
|
-
"persistApprovalMode"
|
|
6134
|
-
);
|
|
6135
|
-
persisted = persistApprovalMode !== void 0;
|
|
6136
|
-
} catch (err) {
|
|
6137
|
-
writeStderrLine(
|
|
6138
|
-
`setSessionApprovalMode: persist failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6139
|
-
);
|
|
6140
|
-
}
|
|
6141
|
-
}
|
|
6142
|
-
publishApprovalModeChanged(
|
|
6143
|
-
entry,
|
|
6144
|
-
{
|
|
6145
|
-
previous: response.previous,
|
|
6146
|
-
next: response.current,
|
|
6147
|
-
persisted
|
|
6148
|
-
},
|
|
6149
|
-
originatorClientId
|
|
6150
|
-
);
|
|
6151
|
-
if (persisted) {
|
|
6152
|
-
broadcastWorkspaceEvent(
|
|
6153
|
-
{
|
|
6154
|
-
type: "approval_mode_changed",
|
|
6155
|
-
data: {
|
|
6156
|
-
sessionId: entry.sessionId,
|
|
6157
|
-
previous: response.previous,
|
|
6158
|
-
next: response.current,
|
|
6159
|
-
persisted
|
|
6160
|
-
},
|
|
6161
|
-
...originatorClientId ? { originatorClientId } : {}
|
|
6162
|
-
},
|
|
6163
|
-
entry.sessionId
|
|
6164
|
-
);
|
|
6165
|
-
for (const peer of byId.values()) {
|
|
6166
|
-
if (peer.sessionId === entry.sessionId) {
|
|
6167
|
-
continue;
|
|
6168
|
-
}
|
|
6169
|
-
peer.currentApprovalMode = response.current;
|
|
6170
|
-
}
|
|
6171
|
-
}
|
|
6172
|
-
succeeded = true;
|
|
6173
|
-
return {
|
|
6174
|
-
sessionId: entry.sessionId,
|
|
6175
|
-
mode: response.current,
|
|
6176
|
-
previous: response.previous,
|
|
6177
|
-
persisted
|
|
6178
|
-
};
|
|
6179
|
-
} finally {
|
|
6180
|
-
entry.approvalModeRoundtripInFlight = false;
|
|
6181
|
-
if (succeeded) {
|
|
6182
|
-
void reconcileAfterRoundtrip(entry, "approvalMode");
|
|
6183
|
-
} else {
|
|
6184
|
-
writeStderrLine(
|
|
6185
|
-
`[reconcile] session=${entry.sessionId} target=approvalMode action=skipped reason=roundtrip_failed`
|
|
6186
|
-
);
|
|
6187
|
-
}
|
|
6188
|
-
}
|
|
6189
|
-
});
|
|
6190
|
-
entry.approvalModeQueue = approvalWork.then(
|
|
6191
|
-
() => void 0,
|
|
6192
|
-
() => void 0
|
|
8137
|
+
return await applyApprovalMode(
|
|
8138
|
+
entry,
|
|
8139
|
+
mode,
|
|
8140
|
+
opts2.persist,
|
|
8141
|
+
originatorClientId
|
|
6193
8142
|
);
|
|
6194
|
-
try {
|
|
6195
|
-
return await approvalWork;
|
|
6196
|
-
} catch (err) {
|
|
6197
|
-
const data = err?.data;
|
|
6198
|
-
if (data && typeof data === "object" && "errorKind" in data && data.errorKind === "trust_gate") {
|
|
6199
|
-
const rawMessage = err?.message;
|
|
6200
|
-
const message = typeof rawMessage === "string" ? rawMessage : "Trust-gate rejection from ACP child";
|
|
6201
|
-
throw new TrustGateError(message);
|
|
6202
|
-
}
|
|
6203
|
-
throw err;
|
|
6204
|
-
}
|
|
6205
8143
|
},
|
|
6206
8144
|
async generateSessionRecap(sessionId, _context) {
|
|
6207
8145
|
const entry = byId.get(sessionId);
|
|
@@ -6554,6 +8492,48 @@ function createAcpSessionBridge(opts) {
|
|
|
6554
8492
|
const targetTurnIndex = response["targetTurnIndex"] ?? 0;
|
|
6555
8493
|
const filesChanged = response["filesChanged"] ?? [];
|
|
6556
8494
|
const filesFailed = response["filesFailed"] ?? [];
|
|
8495
|
+
const artifactSnapshot = restoredArtifactSnapshotFromState(
|
|
8496
|
+
response
|
|
8497
|
+
);
|
|
8498
|
+
const artifactSnapshotUnavailable = artifactSnapshotUnavailableReason(
|
|
8499
|
+
response
|
|
8500
|
+
);
|
|
8501
|
+
const beforeArtifacts = (await entry.artifacts.list()).artifacts;
|
|
8502
|
+
const shouldRestoreArtifactSnapshot = artifactSnapshot !== void 0 && artifactSnapshotUnavailable === void 0;
|
|
8503
|
+
const artifactRestoreWarnings = artifactSnapshotUnavailable !== void 0 ? [
|
|
8504
|
+
`artifact snapshot rebuild unavailable during rewind: ${artifactSnapshotUnavailable}`
|
|
8505
|
+
] : shouldRestoreArtifactSnapshot ? await entry.artifacts.restore(artifactSnapshot, {
|
|
8506
|
+
preserveLiveEphemeral: true
|
|
8507
|
+
}) : [];
|
|
8508
|
+
const artifactRestoreFailed = artifactRestoreWarnings.some(
|
|
8509
|
+
isArtifactRestoreFailureWarning
|
|
8510
|
+
);
|
|
8511
|
+
const shouldRecordArtifactSnapshot = shouldRestoreArtifactSnapshot && !artifactRestoreFailed;
|
|
8512
|
+
const artifactSnapshotWarnings = shouldRecordArtifactSnapshot ? await entry.artifacts.recordSnapshot() : [];
|
|
8513
|
+
const artifactWarnings = [
|
|
8514
|
+
...artifactRestoreWarnings,
|
|
8515
|
+
...artifactSnapshotWarnings
|
|
8516
|
+
];
|
|
8517
|
+
for (const warning of artifactRestoreWarnings) {
|
|
8518
|
+
writeStderrLine(
|
|
8519
|
+
`[artifacts] session=${entry.sessionId} action=rewind_restore_warning warning=${JSON.stringify(
|
|
8520
|
+
warning
|
|
8521
|
+
)}`
|
|
8522
|
+
);
|
|
8523
|
+
}
|
|
8524
|
+
for (const warning of artifactSnapshotWarnings) {
|
|
8525
|
+
writeStderrLine(
|
|
8526
|
+
`[artifacts] session=${entry.sessionId} action=rewind_snapshot_warning warning=${JSON.stringify(
|
|
8527
|
+
warning
|
|
8528
|
+
)}`
|
|
8529
|
+
);
|
|
8530
|
+
}
|
|
8531
|
+
const afterArtifacts = (await entry.artifacts.list()).artifacts;
|
|
8532
|
+
publishArtifactChanges(
|
|
8533
|
+
entry,
|
|
8534
|
+
artifactReseedChanges(beforeArtifacts, afterArtifacts),
|
|
8535
|
+
originatorClientId
|
|
8536
|
+
);
|
|
6557
8537
|
try {
|
|
6558
8538
|
entry.events.publish({
|
|
6559
8539
|
type: "session_rewound",
|
|
@@ -6562,7 +8542,8 @@ function createAcpSessionBridge(opts) {
|
|
|
6562
8542
|
promptId: req.promptId,
|
|
6563
8543
|
targetTurnIndex,
|
|
6564
8544
|
filesChanged,
|
|
6565
|
-
filesFailed
|
|
8545
|
+
filesFailed,
|
|
8546
|
+
...artifactWarnings.length > 0 ? { warnings: artifactWarnings } : {}
|
|
6566
8547
|
},
|
|
6567
8548
|
...originatorClientId ? { originatorClientId } : {}
|
|
6568
8549
|
});
|
|
@@ -6572,7 +8553,8 @@ function createAcpSessionBridge(opts) {
|
|
|
6572
8553
|
rewound: filesFailed.length === 0,
|
|
6573
8554
|
targetTurnIndex,
|
|
6574
8555
|
filesChanged,
|
|
6575
|
-
filesFailed
|
|
8556
|
+
filesFailed,
|
|
8557
|
+
...artifactWarnings.length > 0 ? { warnings: artifactWarnings } : {}
|
|
6576
8558
|
};
|
|
6577
8559
|
},
|
|
6578
8560
|
async manageMcpServer(serverName, action, originatorClientId) {
|
|
@@ -6700,6 +8682,7 @@ function createAcpSessionBridge(opts) {
|
|
|
6700
8682
|
}
|
|
6701
8683
|
permissionMediator.forgetSession(sessionId);
|
|
6702
8684
|
entry.pendingPermissionIds.clear();
|
|
8685
|
+
entry.pendingInteractions.clear();
|
|
6703
8686
|
if (entry.promptActive) {
|
|
6704
8687
|
entry.promptActive = false;
|
|
6705
8688
|
activePromptCounter--;
|
|
@@ -6708,6 +8691,12 @@ function createAcpSessionBridge(opts) {
|
|
|
6708
8691
|
if (defaultEntry === entry) defaultEntry = void 0;
|
|
6709
8692
|
byId.delete(sessionId);
|
|
6710
8693
|
telemetry.metrics?.sessionLifecycle("die");
|
|
8694
|
+
emitSessionLifecycle({
|
|
8695
|
+
type: "removed",
|
|
8696
|
+
sessionId,
|
|
8697
|
+
workspaceCwd: entry.workspaceCwd,
|
|
8698
|
+
reason: "killed"
|
|
8699
|
+
});
|
|
6711
8700
|
const ci = channelInfoForEntry(entry);
|
|
6712
8701
|
if (!ci) {
|
|
6713
8702
|
writeStderrLine(
|
|
@@ -6757,8 +8746,17 @@ function createAcpSessionBridge(opts) {
|
|
|
6757
8746
|
cancelIdleTimer();
|
|
6758
8747
|
stopSessionReaper();
|
|
6759
8748
|
const channels = Array.from(aliveChannels);
|
|
8749
|
+
const entries = Array.from(byId.values());
|
|
6760
8750
|
defaultEntry = void 0;
|
|
6761
8751
|
byId.clear();
|
|
8752
|
+
for (const entry of entries) {
|
|
8753
|
+
emitSessionLifecycle({
|
|
8754
|
+
type: "removed",
|
|
8755
|
+
sessionId: entry.sessionId,
|
|
8756
|
+
workspaceCwd: entry.workspaceCwd,
|
|
8757
|
+
reason: "kill_all"
|
|
8758
|
+
});
|
|
8759
|
+
}
|
|
6762
8760
|
for (const info of channels) {
|
|
6763
8761
|
try {
|
|
6764
8762
|
info.channel.killSync();
|
|
@@ -6776,11 +8774,18 @@ function createAcpSessionBridge(opts) {
|
|
|
6776
8774
|
for (const e of entries) {
|
|
6777
8775
|
permissionMediator.forgetSession(e.sessionId);
|
|
6778
8776
|
e.pendingPermissionIds.clear();
|
|
8777
|
+
e.pendingInteractions.clear();
|
|
6779
8778
|
}
|
|
6780
8779
|
defaultEntry = void 0;
|
|
6781
8780
|
byId.clear();
|
|
6782
8781
|
for (const e of entries) {
|
|
6783
8782
|
telemetry.metrics?.sessionLifecycle("die");
|
|
8783
|
+
emitSessionLifecycle({
|
|
8784
|
+
type: "removed",
|
|
8785
|
+
sessionId: e.sessionId,
|
|
8786
|
+
workspaceCwd: e.workspaceCwd,
|
|
8787
|
+
reason: "daemon_shutdown"
|
|
8788
|
+
});
|
|
6784
8789
|
try {
|
|
6785
8790
|
e.events.publish({
|
|
6786
8791
|
type: "session_died",
|
|
@@ -6842,6 +8847,7 @@ var createHttpAcpBridge = createAcpSessionBridge;
|
|
|
6842
8847
|
|
|
6843
8848
|
export {
|
|
6844
8849
|
SessionArtifactValidationError,
|
|
8850
|
+
SessionArtifactAuthorizationError,
|
|
6845
8851
|
extractErrorMessage,
|
|
6846
8852
|
extractErrorCode,
|
|
6847
8853
|
classifyTurnErrorKind,
|