@qwen-code/qwen-code 0.19.8 → 0.19.9-nightly.20260714.9dd8389eb
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 +61 -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 +82 -7
- package/bundled/qc-helper/docs/features/channels/wecom.md +6 -0
- package/bundled/qc-helper/docs/features/code-review.md +109 -41
- package/bundled/qc-helper/docs/features/commands.md +45 -44
- package/bundled/qc-helper/docs/features/computer-use.md +77 -0
- package/bundled/qc-helper/docs/features/hooks.md +29 -0
- 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 +60 -22
- package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +2 -2
- package/bundled/review/DESIGN.md +303 -46
- package/bundled/review/SKILL.md +658 -165
- package/chunks/MaxSizedBox-QAYHOC4F.js +71 -0
- package/chunks/{StandaloneSessionPicker-EYRGH6PJ.js → StandaloneSessionPicker-D3R6YYX3.js} +82 -79
- package/chunks/{acpAgent-GRFTTRHW.js → acpAgent-ENHBW72A.js} +1178 -421
- package/chunks/agent-57LX4KMG.js +69 -0
- package/chunks/agent-headless-AC6EVNAP.js +63 -0
- package/chunks/{anthropicContentGenerator-CPF22MA7.js → anthropicContentGenerator-56U3YCP2.js} +187 -31
- package/chunks/{artifact-tool-D233YJFH.js → artifact-tool-BWZZJ42H.js} +5 -5
- package/chunks/{askUserQuestion-MKL5ZTMC.js → askUserQuestion-WU3S5A5T.js} +6 -6
- package/chunks/bridge-4OZH44MK.js +79 -0
- package/chunks/{ca-MKD32FQL.js → ca-C2JTT2N5.js} +68 -6
- package/chunks/channel-worker-group-K7QKH6IG.js +13 -0
- package/chunks/channel-worker-manager-5RWKSGEU.js +418 -0
- package/chunks/channel-worker-supervisor-PCDAZN4D.js +20 -0
- package/chunks/{chunk-CSOTA7KX.js → chunk-2FO5GFKZ.js} +2 -2
- package/chunks/{chunk-QQDPRDVW.js → chunk-2HRYPZT5.js} +1 -1
- package/chunks/{chunk-I6WGFJJ3.js → chunk-2MF6ICOL.js} +38 -12
- package/chunks/{chunk-X3YGAX7V.js → chunk-2UUAIFQO.js} +4 -2
- package/chunks/{chunk-2PSWA5ID.js → chunk-2X56YUSZ.js} +1 -1
- package/chunks/{chunk-NQKLOAVJ.js → chunk-2Z5UX4KI.js} +2 -2
- package/chunks/chunk-3MX7D6QN.js +516 -0
- package/chunks/{chunk-YDOXPLYL.js → chunk-4DYYRLIE.js} +3890 -975
- package/chunks/{chunk-OQXDAMKY.js → chunk-4FPUAYTT.js} +8 -8
- package/chunks/{chunk-55ZMG67I.js → chunk-4LD5APFL.js} +4 -4
- package/chunks/{chunk-YKZIAK7C.js → chunk-4LJNI7MU.js} +166 -37
- package/chunks/{chunk-BOBOCR5H.js → chunk-4QOPEEL2.js} +4 -4
- package/chunks/{chunk-UIDXQNMV.js → chunk-4WGODMVR.js} +1 -1
- package/chunks/{chunk-E6US47KI.js → chunk-4WKAA4KS.js} +1 -1
- package/chunks/{chunk-3GONHQOA.js → chunk-57OAHC2Y.js} +1 -1
- package/chunks/{chunk-JZSY4WP3.js → chunk-5DTTYNB7.js} +1 -1
- package/chunks/chunk-5HBA2Z7V.js +28 -0
- package/chunks/{chunk-SKAZWEV5.js → chunk-5M6IDOMF.js} +35 -98
- package/chunks/{chunk-A4BMJM77.js → chunk-5O2XNYP6.js} +1 -0
- package/chunks/{chunk-GO6LNQXT.js → chunk-6AM5D4ME.js} +1 -1
- package/chunks/{chunk-BJBWRCSK.js → chunk-6H67XLET.js} +1 -1
- package/chunks/{chunk-AYNCLGTW.js → chunk-6HHAADP2.js} +8 -51
- package/chunks/{chunk-VZ7HY3YU.js → chunk-6OATMEYN.js} +3 -3
- package/chunks/{chunk-3UP777ZS.js → chunk-6RLROE6G.js} +1 -1
- package/chunks/{chunk-ZERZSAZL.js → chunk-75DOP5OR.js} +2 -2
- package/chunks/{chunk-RMLZAUWH.js → chunk-7BIZARI2.js} +10 -12
- package/chunks/{chunk-GN3F6PIU.js → chunk-7NAWTFVY.js} +5 -5
- package/chunks/{chunk-22MNNNXE.js → chunk-7NFB7LBQ.js} +11 -3
- package/chunks/{chunk-ZL5HN75Q.js → chunk-7R4DPSRA.js} +37 -3
- package/chunks/{chunk-BVYU3ZTI.js → chunk-7U7UIO5W.js} +1005 -942
- package/chunks/{chunk-SAIILPQ3.js → chunk-7X7TEQNX.js} +5 -3
- package/chunks/{chunk-IRJWHZWU.js → chunk-A4UCTEB5.js} +2 -2
- package/chunks/{chunk-WF4FAXDL.js → chunk-AAS2Q2TV.js} +62 -62
- package/chunks/{chunk-O7FIRWJV.js → chunk-AAWKZ5R7.js} +3 -3
- package/chunks/{chunk-JCH35JB3.js → chunk-AGBFWLA5.js} +2 -2
- package/chunks/{chunk-QYUE6W3T.js → chunk-ALGGS7UH.js} +2 -39
- package/chunks/{chunk-WMPVYQ4P.js → chunk-ARKANCNX.js} +3 -2
- package/chunks/{chunk-HDOH3QYE.js → chunk-ASVORVFT.js} +2 -2
- package/chunks/{chunk-CHB5SLZZ.js → chunk-AUGSSJX3.js} +3 -3
- package/chunks/{chunk-PUVCVELY.js → chunk-B6ABUBYY.js} +4 -4
- package/chunks/{chunk-L4AMBVQG.js → chunk-B6KHOCU7.js} +49 -21
- package/chunks/{chunk-HW4N45EE.js → chunk-B7NUUEXC.js} +38 -11
- package/chunks/{chunk-CD6USWHZ.js → chunk-BBNVFRGD.js} +2 -2
- package/chunks/{chunk-ZN5T4BHI.js → chunk-BPZALHVR.js} +2 -2
- package/chunks/{chunk-VGGOPZUL.js → chunk-C4CXFRKY.js} +12 -5
- package/chunks/{chunk-OFEVLU4C.js → chunk-CARU2RR2.js} +1 -1
- package/chunks/{chunk-SQ2UATK7.js → chunk-CLTWL6HD.js} +11 -11
- package/chunks/{chunk-GLE2YUPU.js → chunk-COH43MRJ.js} +1 -1
- package/chunks/{chunk-E5A7LHNN.js → chunk-CPBF7KYF.js} +1 -1
- package/chunks/{chunk-UWCTAVOD.js → chunk-CR3C7WXL.js} +1 -1
- package/chunks/{chunk-DIWVZ3VM.js → chunk-CSACSGAW.js} +5 -5
- package/chunks/{chunk-5P5XGNYH.js → chunk-CU3L64TP.js} +1 -1
- package/chunks/{chunk-IWKSG2AR.js → chunk-CXAOG665.js} +1 -1
- package/chunks/{chunk-64WXLC72.js → chunk-DJ2GSRLV.js} +1 -1
- package/chunks/chunk-DKXW66XA.js +38 -0
- package/chunks/{chunk-QN5NZ3UQ.js → chunk-DMTGGOSA.js} +2 -2
- package/chunks/{chunk-JZHIQJOE.js → chunk-DMTQY5GY.js} +6 -6
- package/chunks/{chunk-GSHQYYIV.js → chunk-DNKX35GS.js} +6 -26
- package/chunks/{chunk-6IAWPIQ6.js → chunk-DPSWJWZI.js} +4 -3
- package/chunks/{chunk-X2XNQQJI.js → chunk-E2UVIFI2.js} +186 -399
- package/chunks/{chunk-KHDZHZMH.js → chunk-E5Z2AVNV.js} +1 -1
- package/chunks/{chunk-3B26UVCX.js → chunk-E67NFMF4.js} +39 -10
- package/chunks/{chunk-ON3JA3RU.js → chunk-EU3U7USF.js} +7 -9
- package/chunks/{chunk-L46XKEGM.js → chunk-EUNRLYJG.js} +5 -8
- package/chunks/{chunk-OACJLMLD.js → chunk-FBU7WRZI.js} +18 -2
- package/chunks/{chunk-5IFG2VC4.js → chunk-FCMNKVXT.js} +1 -1
- package/chunks/{chunk-A5JOJ77H.js → chunk-FDL4PB5H.js} +38 -15
- package/chunks/{chunk-E7KNELIZ.js → chunk-FSA7ERJ2.js} +2 -2
- package/chunks/{chunk-TI3BWD4D.js → chunk-G6QTKQN4.js} +39 -19
- package/chunks/{chunk-44NROQYV.js → chunk-GDQQRV43.js} +4 -4
- package/chunks/{chunk-O2YT7VB4.js → chunk-GOXKNSDZ.js} +2 -65
- package/chunks/{chunk-V35MD7KN.js → chunk-GRC5HGPI.js} +1 -1
- package/chunks/chunk-GYGGUR4Q.js +253 -0
- package/chunks/{chunk-S6RLAIUR.js → chunk-GYTKQYDB.js} +1 -1
- package/chunks/{chunk-A2X3KLC3.js → chunk-GZXCZHCT.js} +3 -3
- package/chunks/{chunk-K5PGHDBN.js → chunk-H6XPXXMH.js} +1 -1
- package/chunks/{chunk-E7EMCSUG.js → chunk-HE54PGVI.js} +64 -42
- package/chunks/{chunk-KYMBIKIW.js → chunk-HTHUX2T5.js} +1 -1
- package/chunks/{chunk-LQ7TMOCE.js → chunk-HTO4JFDZ.js} +1 -1
- package/chunks/chunk-HZYUNZDQ.js +296 -0
- package/chunks/chunk-IDS7MSUP.js +74 -0
- package/chunks/chunk-J2TDDOSM.js +493 -0
- package/chunks/{chunk-4CGOWHKM.js → chunk-J4MOFEOK.js} +2 -2
- package/chunks/{chunk-22IFUCVR.js → chunk-JESCGQM3.js} +1 -1
- package/chunks/chunk-JR63TOV4.js +91 -0
- package/chunks/chunk-K3PPUB46.js +445 -0
- package/chunks/{chunk-42XGFRJS.js → chunk-KKRTYONM.js} +8 -6
- package/chunks/{chunk-GGQQA4JW.js → chunk-KSO42X3Z.js} +15 -2
- package/chunks/{chunk-MRO43B25.js → chunk-KW7NOTN6.js} +1 -1
- package/chunks/{chunk-54YZOP7P.js → chunk-L573Z2QZ.js} +11894 -5190
- package/chunks/{chunk-TLWA7TPR.js → chunk-L5U3ELZX.js} +2 -2
- package/chunks/{chunk-WHJQ3JUS.js → chunk-LAM6CGP5.js} +10 -10
- package/chunks/{chunk-H6EMK6QK.js → chunk-LQIDP6K5.js} +11913 -5207
- package/chunks/{chunk-MZ7BABX3.js → chunk-LXLTBIDP.js} +44 -7
- package/chunks/{chunk-HLKG6CZN.js → chunk-M5GB774H.js} +1 -1
- package/chunks/{chunk-EEN2TKST.js → chunk-MBJ3D6SF.js} +2 -2
- package/chunks/{chunk-PMFULMLX.js → chunk-MBP3K324.js} +2234 -377
- package/chunks/chunk-MR3PXB6E.js +48 -0
- package/chunks/{chunk-ZE6E424U.js → chunk-N7FCSERB.js} +2 -2
- package/chunks/{chunk-332PWN27.js → chunk-NHDKSOEY.js} +1 -1
- package/chunks/chunk-OATTJGPJ.js +448 -0
- package/chunks/{chunk-UX6OTBVD.js → chunk-OCC3UTWA.js} +2 -2
- package/chunks/{chunk-P7M4GA57.js → chunk-OCPBI7J5.js} +1 -1
- package/chunks/chunk-OJAMDVF5.js +66 -0
- package/chunks/{chunk-IJRL2S3D.js → chunk-OKAIGAYW.js} +6 -6
- package/chunks/{chunk-WQTHLB5T.js → chunk-P3S67A5R.js} +7 -7
- package/chunks/{chunk-FBET5XZ2.js → chunk-PEJWA3QQ.js} +29 -10
- package/chunks/{chunk-J7RCB6N5.js → chunk-PHYCWSDG.js} +2 -2
- package/chunks/{chunk-LAXNEOBG.js → chunk-PMRH4YYW.js} +18 -3
- package/chunks/{chunk-AGHU5VRK.js → chunk-PZL23GTM.js} +1 -1
- package/chunks/{chunk-PC4ZXHU2.js → chunk-Q6TUALBE.js} +3 -3
- package/chunks/{chunk-FXGLL2HL.js → chunk-Q7YDKOGY.js} +1 -1
- package/chunks/{chunk-P3XEEI3X.js → chunk-QABSSDQH.js} +36 -7
- package/chunks/{chunk-XOHNA2S6.js → chunk-QAW7PIHT.js} +3 -3
- package/chunks/{chunk-LGFAYIKX.js → chunk-QHTIBUWB.js} +1 -1
- package/chunks/{chunk-P3MYAZBY.js → chunk-QMGX2KO2.js} +1 -1
- package/chunks/{chunk-Z3I3QJK3.js → chunk-QRUIS3DN.js} +1 -1
- package/chunks/{chunk-ITOGNELQ.js → chunk-QSWDGAIC.js} +2 -2
- package/chunks/{chunk-GHOTR7HL.js → chunk-QUPXZXLV.js} +1 -1
- package/chunks/{chunk-W7EEOR7D.js → chunk-RCEEXIOO.js} +8 -6
- package/chunks/{chunk-2EM7ECZD.js → chunk-RJL5T4WJ.js} +281 -65
- package/chunks/{chunk-TLRYABYP.js → chunk-RKUWKYED.js} +1 -1
- package/chunks/{chunk-RNBYOUGV.js → chunk-RXFQM6FQ.js} +1 -1
- package/chunks/{chunk-JRQ7EVGE.js → chunk-S2QXYLUB.js} +2 -2
- package/chunks/{chunk-QXPI7FEC.js → chunk-S3TWXGKA.js} +4 -4
- package/chunks/{chunk-O33TBKDC.js → chunk-SE74CIZD.js} +155 -19
- package/chunks/{chunk-FV7425LN.js → chunk-SPJQO2CE.js} +18 -5
- package/chunks/{chunk-3GNOQZDC.js → chunk-SSFDPGFN.js} +11866 -11542
- package/chunks/{chunk-DCZWC4PF.js → chunk-SUWGUGAD.js} +5 -5
- package/chunks/{chunk-IBY3Q3HG.js → chunk-SXU3KKNI.js} +963 -181
- package/chunks/{chunk-MLZQVCF3.js → chunk-SZRN75WQ.js} +1 -1
- package/chunks/{chunk-M7S4GK5Z.js → chunk-T2L6SGQY.js} +115 -29
- package/chunks/{chunk-7RYW5LQV.js → chunk-T2NCM2ET.js} +2 -2
- package/chunks/{chunk-JPALE3PA.js → chunk-T5C2OUCI.js} +6 -6
- package/chunks/{chunk-BUZ6HGL4.js → chunk-T7FSRWQ5.js} +248 -55
- package/chunks/{chunk-Z2Z3GUXZ.js → chunk-TB6UDU4T.js} +1 -1
- package/chunks/{chunk-EXPMGZZV.js → chunk-TEGEBB2I.js} +1 -1
- package/chunks/{chunk-MX2YXRER.js → chunk-TJTKSVIV.js} +1 -1
- package/chunks/{chunk-CCGOFQV3.js → chunk-TQBODCFD.js} +8 -7
- package/chunks/chunk-TV6K2FB4.js +224 -0
- package/chunks/{chunk-5MBO76SL.js → chunk-TXY2C3D2.js} +3 -3
- package/chunks/{chunk-H6BD2ELD.js → chunk-TYAMGABM.js} +2 -2
- package/chunks/{chunk-QYNNN7BK.js → chunk-TYLOFD2U.js} +2 -2
- package/chunks/{chunk-VRDMOSQS.js → chunk-UDXSXLER.js} +2 -2
- package/chunks/{chunk-UXEW557D.js → chunk-UI3W56WF.js} +3 -3
- package/chunks/{chunk-CJCKDMWO.js → chunk-URK542T4.js} +1 -1
- package/chunks/{channel-worker-supervisor-NZQUOEFJ.js → chunk-URU5UEK6.js} +190 -34
- package/chunks/{chunk-AHNXYU4O.js → chunk-V6WHFGST.js} +1 -1
- package/chunks/chunk-V75IOTWF.js +71 -0
- package/chunks/{chunk-ZTQ26VBE.js → chunk-VDDGDZMA.js} +1 -1
- package/chunks/{chunk-OMX7CUOE.js → chunk-VGC4I5JJ.js} +1 -1
- package/chunks/{chunk-43W4YSO6.js → chunk-VJNBRRZG.js} +2 -2
- package/chunks/{chunk-ER3BKOLB.js → chunk-W7FJ3N32.js} +1 -1
- package/chunks/{chunk-3HX5LZ6R.js → chunk-WBL3FJEU.js} +2 -2
- package/chunks/{chunk-7LTB54MK.js → chunk-WIEO4CWB.js} +2 -2
- package/chunks/{chunk-BMFMJINR.js → chunk-WL4L7EZH.js} +1 -1
- package/chunks/{chunk-6VK6FIMQ.js → chunk-WM5HY72H.js} +415 -379
- package/chunks/{chunk-BOBRZZKD.js → chunk-WN4D7YCI.js} +5 -5
- package/chunks/{chunk-DHOLNQLC.js → chunk-WWQ3QIJS.js} +60 -40
- package/chunks/{chunk-FEJ2FZ3U.js → chunk-WX6SD4IQ.js} +2 -2
- package/chunks/{chunk-DICGXFAI.js → chunk-X7EBAABW.js} +11 -9
- package/chunks/{chunk-3RW4AZJV.js → chunk-XFSFZCM5.js} +1 -1
- package/chunks/{chunk-YHKAK72D.js → chunk-XHW5VUIQ.js} +102 -16
- 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-SUAKHHK7.js → chunk-Y66VOBRP.js} +3 -3
- package/chunks/chunk-YAF2MQZ6.js +19 -0
- package/chunks/{chunk-FQNC62B2.js → chunk-YH4J5XI3.js} +11 -11
- package/chunks/chunk-YHN5SUIJ.js +219 -0
- 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-XYR5RUA5.js → chunk-YWUK7OS7.js} +10 -3
- package/chunks/{chunk-27BHARDE.js → chunk-Z56JO4JZ.js} +2124 -30
- package/chunks/{chunk-TSPB77TG.js → chunk-ZLM3KXBT.js} +1015 -2967
- package/chunks/{chunk-AHNJGLFV.js → chunk-ZPAHLFZU.js} +3 -3
- package/chunks/chunk-ZRKBI3DP.js +34 -0
- package/chunks/cli-entry-path-4VJ3Y6T2.js +10 -0
- package/chunks/{computer-use-WMY3UOBX.js → computer-use-DIP5BWXP.js} +51 -50
- package/chunks/config-utils-CFT33LTP.js +24 -0
- package/chunks/contextCommand-QU4GJWWD.js +69 -0
- package/chunks/create-sub-session-TCLIYVGR.js +376 -0
- package/chunks/create-sub-session-WNX2OQVX.js +207 -0
- package/chunks/{cron-create-JSXEIPVS.js → cron-create-36TIFKRT.js} +9 -9
- package/chunks/{cron-delete-OIFVEPGU.js → cron-delete-OQD3WULU.js} +7 -7
- package/chunks/{cron-list-D4ZX4IOE.js → cron-list-4533T2FH.js} +14 -10
- package/chunks/{daemon-WPAENEBW.js → daemon-CEZ27OH4.js} +734 -24
- package/chunks/daemon-status-provider-SIBFMLBQ.js +77 -0
- package/chunks/{de-ZFPPQVSF.js → de-2VCDRDGU.js} +68 -6
- package/chunks/{devtools-FM6GJPYG.js → devtools-4QFYJT6U.js} +3 -3
- package/chunks/{dist-E6UANPIM.js → dist-3V3ZV6ML.js} +1011 -197
- package/chunks/{dist-SCFLLYMK.js → dist-ERCPDVA6.js} +12 -11
- package/chunks/{dist-Z34QPAQW.js → dist-HNN7DR2L.js} +529 -79
- package/chunks/{dist-O2IRTRPP.js → dist-JZE7F6YR.js} +10 -10
- package/chunks/{dist-BEXOZXLQ.js → dist-MOEC47TR.js} +36 -15
- package/chunks/{dist-H2AEJCZ5.js → dist-PHEPJLXD.js} +5 -5
- package/chunks/{dist-63IS3ZMI.js → dist-PZV5RKM6.js} +4 -4
- package/chunks/earlyInputCapture-L66BEG2C.js +70 -0
- package/chunks/{edit-OTYWAXGP.js → edit-Y5LAJSST.js} +52 -51
- package/chunks/{en-N4EPZAQI.js → en-NJCA3TAA.js} +78 -6
- package/chunks/{enter-worktree-HNYBDVCX.js → enter-worktree-X5LML7T4.js} +51 -50
- package/chunks/{enterPlanMode-CJCNGT63.js → enterPlanMode-XWF32QOW.js} +67 -51
- package/chunks/environment-YVMA5F4I.js +90 -0
- package/chunks/errors-4Y5SUAOC.js +76 -0
- package/chunks/{exit-worktree-DUUKOE3X.js → exit-worktree-SFHU7XYC.js} +51 -50
- package/chunks/{exitPlanMode-AMMSXOAJ.js → exitPlanMode-FP3NAN6E.js} +52 -51
- package/chunks/{fast-path-OV6NIP3K.js → fast-path-ZJNFFCBM.js} +8 -8
- package/chunks/{fast-path-settings-7RIFI3PK.js → fast-path-settings-DAOVFADC.js} +8 -6
- package/chunks/{fileFromPath-IBEHA3CO.js → fileFromPath-UDALK7FM.js} +3 -3
- package/chunks/{fr-5WWMY53B.js → fr-5F3E7WKD.js} +68 -6
- package/chunks/{gemini-NQ3WWBKB.js → gemini-5CAB22BI.js} +122 -109
- package/chunks/{geminiContentGenerator-CDUNEJQE.js → geminiContentGenerator-GE3HPIFB.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-ROV3YS4R.js → glob-YPACZAEL.js} +98 -75
- package/chunks/{grep-NRITKDCD.js → grep-IPR4XW37.js} +51 -50
- package/chunks/handleAutoUpdate-P2KEP56V.js +70 -0
- package/chunks/i18n-BRCT66AA.js +85 -0
- package/chunks/initializer-34HXSK3A.js +74 -0
- package/chunks/installationInfo-WOTDQMSC.js +66 -0
- package/chunks/{ja-CUHZOLEJ.js → ja-66J6B53L.js} +69 -7
- package/chunks/{keychain-token-storage-UJV5XP4Z.js → keychain-token-storage-AU22CQI6.js} +4 -4
- package/chunks/{kittyProtocolDetector-OJRCJLIU.js → kittyProtocolDetector-HCNNKJTI.js} +2 -2
- package/chunks/list-XOGP7UPP.js +77 -0
- package/chunks/loadedSettingsAdapter-L4YKGU6F.js +71 -0
- package/chunks/{loop-wakeup-OHWEMFP5.js → loop-wakeup-MVUKDVCP.js} +10 -10
- package/chunks/{lowlight-FYAAUU5J.js → lowlight-J2OZNPCA.js} +1 -1
- package/chunks/{ls-7MFU2QYW.js → ls-RXNIAQDE.js} +6 -6
- package/chunks/{lsp-XHDTPTGV.js → lsp-HB5LM5IL.js} +4 -4
- package/chunks/mcp-HH7DIP7Y.js +71 -0
- package/chunks/{monitor-FL2ZO33W.js → monitor-25DEBT6U.js} +51 -50
- package/chunks/{multipart-parser-AJ4WASWR.js → multipart-parser-AWZKXSQN.js} +3 -3
- package/chunks/nonInteractiveCli-CT3VSSLK.js +130 -0
- package/chunks/{notebook-edit-6PULTHL5.js → notebook-edit-TAVXSWQ6.js} +52 -51
- package/chunks/openaiContentGenerator-6I7DKUAN.js +56 -0
- package/chunks/pidfile-SIQGCFLZ.js +74 -0
- package/chunks/{pt-OLYN42DS.js → pt-TFZO5Y6T.js} +68 -6
- package/chunks/{qwenContentGenerator-NX2E7PB7.js → qwenContentGenerator-5DDIXI2N.js} +53 -52
- package/chunks/{qwenOAuth2-ZV7YLMND.js → qwenOAuth2-LEYOU7RU.js} +9 -9
- package/chunks/read-file-WDOAYRFU.js +30 -0
- package/chunks/{read-mcp-resource-CNQHBSJM.js → read-mcp-resource-6LRTZKXI.js} +5 -5
- package/chunks/{read-package-up-ER5OJUGP.js → read-package-up-TCM6I7S2.js} +2 -2
- package/chunks/{record-artifact-SQ2OKRJW.js → record-artifact-PMDTY4OS.js} +4 -4
- package/chunks/ripGrep-MI7PHN3Q.js +61 -0
- package/chunks/{ru-LDM3GWVQ.js → ru-4L4LFHIT.js} +68 -6
- package/chunks/{run-qwen-serve-JK2DL7PI.js → run-qwen-serve-PNTYF3RE.js} +1289 -256
- package/chunks/runtime-V3FBMUTG.js +100 -0
- package/chunks/{scheduler-O4IGVZKK.js → scheduler-4TAUIJBM.js} +52 -51
- package/chunks/{send-message-76GT3JMC.js → send-message-SR4WSXP2.js} +7 -7
- package/chunks/serve-WB2TR7XM.js +77 -0
- package/chunks/{server-SGNPKX2O.js → server-AF6LNFPK.js} +8094 -2800
- package/chunks/{session-5BNM25EZ.js → session-BIGK4NRW.js} +341 -135
- package/chunks/settings-IXF5FIQO.js +121 -0
- package/chunks/shell-5VWJ2PPG.js +69 -0
- package/chunks/{skill-I22QK5XS.js → skill-SI667Y3B.js} +24 -24
- package/chunks/spawnChannel-IKX6DD7J.js +71 -0
- package/chunks/{src-BBQSM46M.js → src-55DZWDAK.js} +151 -52
- package/chunks/{src-7XL4G4DC.js → src-VLI3TKEY.js} +4 -4
- package/chunks/standalone-update-MHLSFHE6.js +80 -0
- package/chunks/{startInteractiveUI-JII36UGN.js → startInteractiveUI-V2EDWLAX.js} +2239 -1846
- package/chunks/stdioHelpers-UMY72QRP.js +14 -0
- package/chunks/{syntheticOutput-F7LSDCAR.js → syntheticOutput-YKFNUQZD.js} +5 -5
- package/chunks/task-create-NUFOHU53.js +23 -0
- package/chunks/{task-list-TGGJX7EA.js → task-list-Z2IKQY3S.js} +11 -10
- package/chunks/{task-stop-W7NGTMYQ.js → task-stop-KQFIVQTJ.js} +4 -4
- package/chunks/{task-update-XYWMHJGJ.js → task-update-HKADXSTD.js} +13 -12
- package/chunks/{team-create-A7NUU5AQ.js → team-create-IZAMLBJS.js} +51 -50
- package/chunks/{team-delete-UHQQ2QQX.js → team-delete-SDNTGZLG.js} +11 -10
- package/chunks/{team-plan-approval-TOQRJ2KI.js → team-plan-approval-3R4RCKCP.js} +51 -50
- package/chunks/theme-manager-LCOXZ5DO.js +65 -0
- package/chunks/{todoWrite-C4ZAMSR3.js → todoWrite-YNXB27GB.js} +6 -6
- package/chunks/{tool-search-SZ4EFS2T.js → tool-search-NYNCD5LF.js} +24 -24
- package/chunks/total-session-admission-PJWGOMQE.js +73 -0
- package/chunks/tree-sitter-YJVE2ZUK.js +2980 -0
- package/chunks/trustedFolders-DOJDHNYP.js +81 -0
- package/chunks/{types-QX5C3CHJ.js → types-JNKGKUJT.js} +2 -2
- package/chunks/updateCheck-ZXL66KIG.js +70 -0
- package/chunks/updateEventEmitter-5MKC7BLQ.js +10 -0
- package/chunks/validateNonInterActiveAuth-JSNJZCYY.js +195 -0
- package/chunks/{version-NO2XSNNP.js → version-3NLBENG5.js} +3 -3
- package/chunks/{web-fetch-UYGMOHW4.js → web-fetch-ZSPR3PRR.js} +8 -8
- package/chunks/{workflow-WO2H6NRI.js → workflow-BQYLF7XP.js} +52 -51
- package/chunks/workspace-providers-status-FA2DGEZW.js +74 -0
- package/chunks/workspace-registration-store-TARTAGMI.js +27 -0
- package/chunks/workspace-registry-AGE7WAXX.js +77 -0
- package/chunks/workspace-service-ERC5VR2B.js +83 -0
- package/chunks/workspace-skills-status-FKYWGNCN.js +73 -0
- package/chunks/{write-file-PX2Z7IBE.js → write-file-S47KSK7Z.js} +52 -51
- package/chunks/{yargs-B64EYTCF.js → yargs-6H2AUULL.js} +2 -2
- package/chunks/{zh-J4F6QWQV.js → zh-FSAH32JB.js} +79 -7
- package/chunks/{zh-TW-XVDWOLSW.js → zh-TW-D2YIE2LO.js} +79 -7
- package/cli.js +18 -17
- package/locales/ca.js +91 -7
- package/locales/de.js +90 -7
- package/locales/en.js +109 -7
- package/locales/fr.js +91 -7
- package/locales/ja.js +93 -7
- package/locales/pt.js +90 -7
- package/locales/ru.js +90 -7
- package/locales/zh-TW.js +106 -7
- package/locales/zh.js +106 -7
- package/package.json +3 -6
- package/web-shell/assets/{arc-BWyoJpY2.js → arc-Zj4EkBUY.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-BT0mGrz2.js → architectureDiagram-3BPJPVTR-DAk1Ua3Y.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-BLgUWvyf.js → blockDiagram-GPEHLZMM-DBNqUthM.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-DYx9M7oU.js → c4Diagram-AAUBKEIU-Dq5QGJ53.js} +1 -1
- package/web-shell/assets/channel-DWfYijFS.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-CfMHWbuZ.js → chunk-2J33WTMH-DY0AeGru.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-B1URoqEV.js → chunk-4BX2VUAB-COX3OyIi.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-DAIaM4QF.js → chunk-55IACEB6-Csoyddbh.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-B2X34knw.js → chunk-727SXJPM-BJPONdxX.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-Cif-zLTQ.js → chunk-AQP2D5EJ-syLVH6fg.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-H18WCXdY.js → chunk-FMBD7UC4-Cfm59Nhw.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-YFtetHi0.js → chunk-ND2GUHAM-DZf6tAqS.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-By2hiH3r.js → chunk-QZHKN3VN-DKBp_v5q.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-DDht-78J.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-DDht-78J.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-VrcnnLZu.js → cose-bilkent-S5V4N54A-DUU3mMQ5.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-CGo-IaVV.js → dagre-BM42HDAG-BUgNfbh-.js} +1 -1
- package/web-shell/assets/{diagram-2AECGRRQ-Dl4QjzIB.js → diagram-2AECGRRQ-DT7MVfAg.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-qMLmsO0u.js → diagram-5GNKFQAL-Cv4YG-ec.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-CHlmuxPI.js → diagram-KO2AKTUF-BAe9LLkn.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-BUwU3GwT.js → diagram-LMA3HP47-DW8wqTIP.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-HEhwPDUM.js → diagram-OG6HWLK6-CmkuWbaK.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-1n0llBmM.js → erDiagram-TEJ5UH35-DwvdHjZ9.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-BN74uMnt.js → flowDiagram-I6XJVG4X-CSsXrhW_.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-BoOq78i3.js → ganttDiagram-6RSMTGT7-BtuznCdi.js} +3 -3
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-CN8b00c8.js → gitGraphDiagram-PVQCEYII-DGmAm06g.js} +1 -1
- package/web-shell/assets/index-B9Xwf4OS.css +5 -0
- package/web-shell/assets/index-BEtz1cvN.js +3 -0
- package/web-shell/assets/index-BUkM4ZXX.js +1088 -0
- package/web-shell/assets/{infoDiagram-5YYISTIA-CCurnIna.js → infoDiagram-5YYISTIA-OAiZ1q0K.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-AwuRqdUP.js → ishikawaDiagram-YF4QCWOH-DyK0KTIT.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-B7IICk-E.js → journeyDiagram-JHISSGLW-DzYJlwfO.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-BYF0OKhx.js → kanban-definition-UN3LZRKU-CU_tcDwi.js} +1 -1
- package/web-shell/assets/{linear-GWWguGfD.js → linear-DvZXjF17.js} +1 -1
- package/web-shell/assets/{mermaid.core-JqtkxvCl.js → mermaid.core-_ZFEQlOl.js} +5 -5
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-B0MmHsZ6.js → mindmap-definition-RKZ34NQL-Or4p64HB.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-DhImUZLQ.js → pieDiagram-4H26LBE5-lXE-zZ6b.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-DGeSC3DT.js → quadrantDiagram-W4KKPZXB-DUXb9mUV.js} +1 -1
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-DkspY6Re.js → requirementDiagram-4Y6WPE33-D_10uHfo.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-CaOofS2x.js → sankeyDiagram-5OEKKPKP-E8c0fPqe.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-DFQ_kFnN.js → sequenceDiagram-3UESZ5HK-CJMtFttK.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-D3snw0Tz.js → stateDiagram-AJRCARHV-DhbviRbB.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-CntljfwW.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-l4zH8nLM.js → timeline-definition-PNZ67QCA-CM5yMkFK.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-BnRlA98M.js → vennDiagram-CIIHVFJN-BCi-zWNN.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-BS0xUdmd.js → wardley-L42UT6IY-BJ_yJNjJ.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-DKGBUWyQ.js → wardleyDiagram-YWT4CUSO-TjtLk-qN.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-DVb2HuOB.js → xychartDiagram-2RQKCTM6-D1TjyoQ3.js} +1 -1
- package/web-shell/index.html +16 -4
- package/chunks/MaxSizedBox-5DT2SD7B.js +0 -71
- package/chunks/agent-H2DGKS77.js +0 -68
- package/chunks/agent-headless-KFMFIT6J.js +0 -62
- package/chunks/bridge-NA3ZQQKJ.js +0 -78
- package/chunks/chunk-6SHO7WFF.js +0 -79
- package/chunks/chunk-IFEWJXFS.js +0 -37
- package/chunks/chunk-XNXVCNAT.js +0 -88
- package/chunks/contextCommand-LXFZRP4L.js +0 -68
- package/chunks/daemon-status-provider-GJRXY5EN.js +0 -76
- package/chunks/earlyInputCapture-KNOYZ4MJ.js +0 -69
- package/chunks/environment-ATFTYMLZ.js +0 -88
- package/chunks/errors-A54HAADA.js +0 -75
- package/chunks/handleAutoUpdate-CEZJAARW.js +0 -65
- package/chunks/initializer-BRFJKRRX.js +0 -71
- package/chunks/list-KVHF7O4X.js +0 -73
- package/chunks/loadedSettingsAdapter-LL5PSFKX.js +0 -68
- package/chunks/mcp-3GCXRTOQ.js +0 -68
- package/chunks/nonInteractiveCli-PTQI3DV6.js +0 -119
- package/chunks/openaiContentGenerator-YE42PKCY.js +0 -56
- package/chunks/pidfile-NZYSCFRK.js +0 -73
- package/chunks/read-file-33XGP5HF.js +0 -30
- package/chunks/ripGrep-QWZLGTAN.js +0 -60
- package/chunks/serve-JTMIGANH.js +0 -74
- package/chunks/settings-3MI4XXP6.js +0 -118
- package/chunks/shell-YJKBE67T.js +0 -68
- package/chunks/spawnChannel-UNV6W4DU.js +0 -70
- package/chunks/task-create-WCHTB435.js +0 -22
- package/chunks/theme-manager-WZ4RRMMB.js +0 -64
- package/chunks/total-session-admission-ULXJWXFV.js +0 -72
- package/chunks/trustedFolders-LAZZPZ3D.js +0 -79
- package/chunks/updateCheck-KONLM73X.js +0 -65
- package/chunks/validateNonInterActiveAuth-MPKSRCXD.js +0 -184
- package/chunks/workspace-providers-status-3W7E7DFW.js +0 -71
- package/chunks/workspace-registry-SXJZNN3O.js +0 -74
- package/chunks/workspace-service-CZRQZ4Q4.js +0 -80
- package/chunks/workspace-skills-status-K7WFREPD.js +0 -70
- 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-wmPUGHtg.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-BA2ljEjW.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BA2ljEjW.js +0 -1
- package/web-shell/assets/index-CNwoO1Cn.js +0 -783
- package/web-shell/assets/index-CWQ0z6kZ.css +0 -5
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BbSm_9d0.js +0 -1
|
@@ -6,19 +6,19 @@ import {
|
|
|
6
6
|
LOAD_REPLAY_MODE_META_KEY,
|
|
7
7
|
LOAD_REPLAY_VERSION,
|
|
8
8
|
MID_TURN_QUEUE_DRAIN_METHOD
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-PZL23GTM.js";
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_RING_SIZE,
|
|
12
12
|
EVENT_SCHEMA_VERSION,
|
|
13
13
|
EventBus,
|
|
14
14
|
serializedBridgeEventByteLength
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-6RLROE6G.js";
|
|
16
16
|
import {
|
|
17
17
|
normalizeCompactedReplayMaxBytes
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-COH43MRJ.js";
|
|
19
19
|
import {
|
|
20
20
|
MCP_RESTART_SERVER_DEADLINE_MS
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-OCPBI7J5.js";
|
|
22
22
|
import {
|
|
23
23
|
BranchWhilePromptActiveError,
|
|
24
24
|
CancelSentinelCollisionError,
|
|
@@ -38,18 +38,10 @@ import {
|
|
|
38
38
|
SessionShellDisabledError,
|
|
39
39
|
WorkspaceMismatchError,
|
|
40
40
|
isNotCurrentlyGeneratingCancelError
|
|
41
|
-
} from "./chunk-
|
|
42
|
-
import {
|
|
43
|
-
canonicalizeWorkspace
|
|
44
|
-
} from "./chunk-ER3BKOLB.js";
|
|
45
|
-
import {
|
|
46
|
-
ClientSideConnection,
|
|
47
|
-
PROTOCOL_VERSION,
|
|
48
|
-
RequestError
|
|
49
|
-
} from "./chunk-VRDMOSQS.js";
|
|
41
|
+
} from "./chunk-KSO42X3Z.js";
|
|
50
42
|
import {
|
|
51
43
|
defaultSpawnChannelFactory
|
|
52
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-4QOPEEL2.js";
|
|
53
45
|
import {
|
|
54
46
|
BridgeChannelClosedError,
|
|
55
47
|
BridgeTimeoutError,
|
|
@@ -58,18 +50,34 @@ import {
|
|
|
58
50
|
STATUS_SCHEMA_VERSION,
|
|
59
51
|
createIdleWorkspaceExtensionsStatus,
|
|
60
52
|
createIdleWorkspaceHooksStatus
|
|
61
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-PMRH4YYW.js";
|
|
54
|
+
import {
|
|
55
|
+
canonicalizeWorkspace
|
|
56
|
+
} from "./chunk-W7FJ3N32.js";
|
|
62
57
|
import {
|
|
58
|
+
ClientSideConnection,
|
|
59
|
+
PROTOCOL_VERSION,
|
|
60
|
+
RequestError
|
|
61
|
+
} from "./chunk-UDXSXLER.js";
|
|
62
|
+
import {
|
|
63
|
+
SESSION_ARTIFACT_PERSISTENCE_VERSION,
|
|
63
64
|
ShellExecutionService,
|
|
64
|
-
TrustGateError
|
|
65
|
-
|
|
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-SSFDPGFN.js";
|
|
66
74
|
import {
|
|
67
75
|
DAEMON_TRACEPARENT_META_KEY,
|
|
68
76
|
DAEMON_TRACESTATE_META_KEY
|
|
69
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-T7FSRWQ5.js";
|
|
70
78
|
import {
|
|
71
79
|
init_esbuild_shims
|
|
72
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-5O2XNYP6.js";
|
|
73
81
|
import {
|
|
74
82
|
__name
|
|
75
83
|
} from "./chunk-J2S4EL5Y.js";
|
|
@@ -388,7 +396,7 @@ var TurnBoundaryCompactionEngine = class {
|
|
|
388
396
|
retainedEvents,
|
|
389
397
|
maxBytes: this.maxReplayBytes,
|
|
390
398
|
...this.truncatedTurns > 0 ? { truncatedTurns: this.truncatedTurns } : {},
|
|
391
|
-
fullTranscriptAvailable:
|
|
399
|
+
fullTranscriptAvailable: true
|
|
392
400
|
}
|
|
393
401
|
};
|
|
394
402
|
}
|
|
@@ -483,6 +491,11 @@ import * as path from "node:path";
|
|
|
483
491
|
init_esbuild_shims();
|
|
484
492
|
var MID_TURN_MESSAGE_INJECTED_EVENT = "mid_turn_message_injected";
|
|
485
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
|
+
|
|
486
499
|
// packages/acp-bridge/src/permissionMediator.ts
|
|
487
500
|
init_esbuild_shims();
|
|
488
501
|
|
|
@@ -1169,6 +1182,60 @@ function isRecord(value) {
|
|
|
1169
1182
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1170
1183
|
}
|
|
1171
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");
|
|
1172
1239
|
function artifactPayloadFields(artifact) {
|
|
1173
1240
|
return {
|
|
1174
1241
|
title: artifact["title"],
|
|
@@ -1180,7 +1247,8 @@ function artifactPayloadFields(artifact) {
|
|
|
1180
1247
|
url: artifact["url"],
|
|
1181
1248
|
mimeType: artifact["mimeType"],
|
|
1182
1249
|
sizeBytes: artifact["sizeBytes"],
|
|
1183
|
-
metadata: artifact["metadata"]
|
|
1250
|
+
metadata: artifact["metadata"],
|
|
1251
|
+
retention: artifact["retention"]
|
|
1184
1252
|
};
|
|
1185
1253
|
}
|
|
1186
1254
|
__name(artifactPayloadFields, "artifactPayloadFields");
|
|
@@ -1264,7 +1332,8 @@ function isTrustedArtifactToolUpdate(params, updateMeta) {
|
|
|
1264
1332
|
__name(isTrustedArtifactToolUpdate, "isTrustedArtifactToolUpdate");
|
|
1265
1333
|
function preserveFsErrorOverAcp(err) {
|
|
1266
1334
|
if (isFsErrorShape(err)) {
|
|
1267
|
-
|
|
1335
|
+
const code = err.kind === "parse_error" ? -32602 : -32603;
|
|
1336
|
+
throw new RequestError(code, err.message, {
|
|
1268
1337
|
errorKind: err.kind,
|
|
1269
1338
|
...err.hint !== void 0 ? { hint: err.hint } : {},
|
|
1270
1339
|
...err.status !== void 0 ? { status: err.status } : {}
|
|
@@ -1323,7 +1392,7 @@ function sliceLineRange(content, startLine, endLine) {
|
|
|
1323
1392
|
}
|
|
1324
1393
|
__name(sliceLineRange, "sliceLineRange");
|
|
1325
1394
|
var BridgeClient = class {
|
|
1326
|
-
constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender, ownsSession = () => true, onTokenUsage) {
|
|
1395
|
+
constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender, ownsSession = () => true, onTokenUsage, onCreateSubSession) {
|
|
1327
1396
|
this.resolveEntry = resolveEntry;
|
|
1328
1397
|
this.resolvePendingRestoreEvents = resolvePendingRestoreEvents;
|
|
1329
1398
|
this.mediator = mediator;
|
|
@@ -1335,6 +1404,7 @@ var BridgeClient = class {
|
|
|
1335
1404
|
this.clientMcpSender = clientMcpSender;
|
|
1336
1405
|
this.ownsSession = ownsSession;
|
|
1337
1406
|
this.onTokenUsage = onTokenUsage;
|
|
1407
|
+
this.onCreateSubSession = onCreateSubSession;
|
|
1338
1408
|
}
|
|
1339
1409
|
static {
|
|
1340
1410
|
__name(this, "BridgeClient");
|
|
@@ -1348,10 +1418,9 @@ var BridgeClient = class {
|
|
|
1348
1418
|
);
|
|
1349
1419
|
return { outcome: { outcome: "cancelled" } };
|
|
1350
1420
|
}
|
|
1421
|
+
const options = Array.isArray(params.options) ? params.options : [];
|
|
1351
1422
|
const allowedOptionIds = new Set(
|
|
1352
|
-
|
|
1353
|
-
(o) => String(o.optionId ?? "")
|
|
1354
|
-
)
|
|
1423
|
+
options.map((o) => String(o.optionId ?? ""))
|
|
1355
1424
|
);
|
|
1356
1425
|
allowedOptionIds.delete("");
|
|
1357
1426
|
const requestId = randomUUID();
|
|
@@ -1364,12 +1433,25 @@ var BridgeClient = class {
|
|
|
1364
1433
|
requestId,
|
|
1365
1434
|
sessionId: entry.sessionId,
|
|
1366
1435
|
toolCall: params.toolCall,
|
|
1367
|
-
options
|
|
1436
|
+
options
|
|
1368
1437
|
},
|
|
1369
1438
|
...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
|
|
1370
1439
|
});
|
|
1371
1440
|
if (!published) return { outcome: { outcome: "cancelled" } };
|
|
1441
|
+
let interaction;
|
|
1442
|
+
try {
|
|
1443
|
+
interaction = pendingInteractionFromRequest(requestId, {
|
|
1444
|
+
...params,
|
|
1445
|
+
options
|
|
1446
|
+
});
|
|
1447
|
+
} catch (error) {
|
|
1448
|
+
writeStderrLine(
|
|
1449
|
+
`qwen serve: failed to snapshot pending interaction ${requestId}: ${error instanceof Error ? error.message : String(error)}`
|
|
1450
|
+
);
|
|
1451
|
+
interaction = fallbackPendingPermissionInteraction(requestId, options);
|
|
1452
|
+
}
|
|
1372
1453
|
entry.pendingPermissionIds.add(requestId);
|
|
1454
|
+
if (interaction) entry.pendingInteractions.set(requestId, interaction);
|
|
1373
1455
|
try {
|
|
1374
1456
|
const record = {
|
|
1375
1457
|
requestId,
|
|
@@ -1385,6 +1467,7 @@ var BridgeClient = class {
|
|
|
1385
1467
|
return resolutionToAcpResponse(resolution);
|
|
1386
1468
|
} finally {
|
|
1387
1469
|
entry.pendingPermissionIds.delete(requestId);
|
|
1470
|
+
entry.pendingInteractions.delete(requestId);
|
|
1388
1471
|
}
|
|
1389
1472
|
}
|
|
1390
1473
|
async sessionUpdate(params) {
|
|
@@ -1458,7 +1541,7 @@ var BridgeClient = class {
|
|
|
1458
1541
|
trustedPublisher: isTrustedArtifactToolUpdate(params, updateMeta)
|
|
1459
1542
|
};
|
|
1460
1543
|
}
|
|
1461
|
-
async seedSessionUpdates(entry, updates) {
|
|
1544
|
+
async seedSessionUpdates(entry, updates, options = {}) {
|
|
1462
1545
|
const frames = [];
|
|
1463
1546
|
const artifactBatches = [];
|
|
1464
1547
|
for (const update of updates) {
|
|
@@ -1467,7 +1550,7 @@ var BridgeClient = class {
|
|
|
1467
1550
|
entry
|
|
1468
1551
|
);
|
|
1469
1552
|
frames.push(...prepared.frames);
|
|
1470
|
-
if (prepared.artifacts.length > 0) {
|
|
1553
|
+
if (options.ingestArtifacts !== false && prepared.artifacts.length > 0) {
|
|
1471
1554
|
artifactBatches.push({
|
|
1472
1555
|
artifacts: prepared.artifacts,
|
|
1473
1556
|
trustedPublisher: prepared.trustedPublisher
|
|
@@ -1512,11 +1595,12 @@ var BridgeClient = class {
|
|
|
1512
1595
|
);
|
|
1513
1596
|
}
|
|
1514
1597
|
/**
|
|
1515
|
-
* Bounded early-event buffer.
|
|
1516
|
-
*
|
|
1517
|
-
*
|
|
1518
|
-
*
|
|
1519
|
-
*
|
|
1598
|
+
* Bounded early-event buffer. The map is scoped to this BridgeClient and
|
|
1599
|
+
* therefore to one channel; a stale channel cannot seed a fresh channel's
|
|
1600
|
+
* future session. Frames are keyed by sessionId; each entry tracks its
|
|
1601
|
+
* `expiresAt` for lazy TTL-based eviction in `bufferEarlyEvent`. Drained by
|
|
1602
|
+
* `drainEarlyEvents` whenever the bridge registers a session with a matching
|
|
1603
|
+
* id. See MAX_EARLY_EVENT_* constants for capacity bounds.
|
|
1520
1604
|
*/
|
|
1521
1605
|
earlyEvents = /* @__PURE__ */ new Map();
|
|
1522
1606
|
/**
|
|
@@ -1541,7 +1625,7 @@ var BridgeClient = class {
|
|
|
1541
1625
|
/**
|
|
1542
1626
|
* Allow-list of sessionIds currently being restored via
|
|
1543
1627
|
* `session/load` / `session/resume`. Bypasses the tombstone check
|
|
1544
|
-
* in `bufferEarlyEvent` so restore-time
|
|
1628
|
+
* in `bufferEarlyEvent` so restore-time events for a
|
|
1545
1629
|
* previously-closed id flow through to the future
|
|
1546
1630
|
* `createSessionEntry -> drainEarlyEvents` call.
|
|
1547
1631
|
*
|
|
@@ -1556,9 +1640,12 @@ var BridgeClient = class {
|
|
|
1556
1640
|
inFlightRestoreIds = /* @__PURE__ */ new Set();
|
|
1557
1641
|
/**
|
|
1558
1642
|
* Handle child->bridge ACP `extMethod` requests (calls that expect a
|
|
1559
|
-
* response, unlike `extNotification`).
|
|
1560
|
-
* `
|
|
1561
|
-
*
|
|
1643
|
+
* response, unlike `extNotification`). Served methods:
|
|
1644
|
+
* `qwen/control/client_mcp/message` (reverse tool channel),
|
|
1645
|
+
* `qwen/control/create-sub-session` (the `create_sub_session` tool → daemon
|
|
1646
|
+
* spawns a sub-session and, for `'first-turn'`, returns its first-turn
|
|
1647
|
+
* result), and `craft/drainMidTurnQueue`: the ACP child calls the last one
|
|
1648
|
+
* between tool batches to pull any messages the browser queued mid-turn. We splice the per-session
|
|
1562
1649
|
* queue, return them to the child as the response, and — when non-empty —
|
|
1563
1650
|
* publish a `mid_turn_message_injected` SSE frame so the browser can move
|
|
1564
1651
|
* those messages out of its pending queue (a dedupe signal, not a transcript
|
|
@@ -1571,6 +1658,9 @@ var BridgeClient = class {
|
|
|
1571
1658
|
if (method === SERVE_CONTROL_EXT_METHODS.clientMcpMessage) {
|
|
1572
1659
|
return this.handleClientMcpMessage(params);
|
|
1573
1660
|
}
|
|
1661
|
+
if (method === SERVE_CONTROL_EXT_METHODS.createSubSession) {
|
|
1662
|
+
return this.handleCreateSubSession(params);
|
|
1663
|
+
}
|
|
1574
1664
|
if (method !== MID_TURN_QUEUE_DRAIN_METHOD) {
|
|
1575
1665
|
throw RequestError.methodNotFound(method);
|
|
1576
1666
|
}
|
|
@@ -1646,17 +1736,80 @@ var BridgeClient = class {
|
|
|
1646
1736
|
const response = await send(payload);
|
|
1647
1737
|
return { payload: response };
|
|
1648
1738
|
}
|
|
1739
|
+
/**
|
|
1740
|
+
* Handle the `create_sub_session` tool's request: validate, then forward to
|
|
1741
|
+
* the daemon-host `onCreateSubSession` callback (which spawns a fresh
|
|
1742
|
+
* top-level sub-session and, for `'first-turn'`, waits for its first turn and
|
|
1743
|
+
* returns the result). No host wired → `methodNotFound`, which the tool
|
|
1744
|
+
* surfaces as "daemon-only".
|
|
1745
|
+
*/
|
|
1746
|
+
async handleCreateSubSession(params) {
|
|
1747
|
+
if (!this.onCreateSubSession) {
|
|
1748
|
+
throw RequestError.methodNotFound(
|
|
1749
|
+
SERVE_CONTROL_EXT_METHODS.createSubSession
|
|
1750
|
+
);
|
|
1751
|
+
}
|
|
1752
|
+
const prompt = params["prompt"];
|
|
1753
|
+
if (typeof prompt !== "string" || prompt.length === 0) {
|
|
1754
|
+
throw RequestError.invalidParams(
|
|
1755
|
+
void 0,
|
|
1756
|
+
"`prompt` must be a non-empty string"
|
|
1757
|
+
);
|
|
1758
|
+
}
|
|
1759
|
+
if (prompt.length > MAX_SUB_SESSION_PROMPT_CHARS) {
|
|
1760
|
+
throw RequestError.invalidParams(
|
|
1761
|
+
void 0,
|
|
1762
|
+
`\`prompt\` exceeds the ${MAX_SUB_SESSION_PROMPT_CHARS}-character limit`
|
|
1763
|
+
);
|
|
1764
|
+
}
|
|
1765
|
+
const completion = params["completion"];
|
|
1766
|
+
if (completion !== "sent" && completion !== "first-turn") {
|
|
1767
|
+
throw RequestError.invalidParams(
|
|
1768
|
+
void 0,
|
|
1769
|
+
"`completion` must be 'sent' or 'first-turn'"
|
|
1770
|
+
);
|
|
1771
|
+
}
|
|
1772
|
+
const name = params["name"];
|
|
1773
|
+
if (typeof name === "string" && name.length > MAX_SUB_SESSION_NAME_CHARS) {
|
|
1774
|
+
throw RequestError.invalidParams(
|
|
1775
|
+
void 0,
|
|
1776
|
+
`\`name\` exceeds the ${MAX_SUB_SESSION_NAME_CHARS}-character limit`
|
|
1777
|
+
);
|
|
1778
|
+
}
|
|
1779
|
+
const callerSessionId = params["callerSessionId"];
|
|
1780
|
+
if (typeof callerSessionId !== "string" || callerSessionId.length === 0 || !this.ownsSession(callerSessionId)) {
|
|
1781
|
+
throw RequestError.invalidParams(
|
|
1782
|
+
void 0,
|
|
1783
|
+
"`callerSessionId` is required and must name a session owned by this connection"
|
|
1784
|
+
);
|
|
1785
|
+
}
|
|
1786
|
+
const model = params["model"];
|
|
1787
|
+
const result = await this.onCreateSubSession({
|
|
1788
|
+
prompt,
|
|
1789
|
+
completion,
|
|
1790
|
+
...typeof model === "string" && model.length > 0 && model.length <= 128 ? { model } : {},
|
|
1791
|
+
...typeof name === "string" && name.length > 0 ? { name } : {},
|
|
1792
|
+
callerSessionId
|
|
1793
|
+
});
|
|
1794
|
+
return {
|
|
1795
|
+
sessionId: result.sessionId,
|
|
1796
|
+
...result.result !== void 0 ? { result: result.result } : {},
|
|
1797
|
+
...result.stopReason !== void 0 ? { stopReason: result.stopReason } : {},
|
|
1798
|
+
...result.parentSessionPersisted !== void 0 ? { parentSessionPersisted: result.parentSessionPersisted } : {}
|
|
1799
|
+
};
|
|
1800
|
+
}
|
|
1649
1801
|
/**
|
|
1650
1802
|
* Handle child->bridge ACP `extNotification` calls. Recognized methods are
|
|
1651
1803
|
* `qwen/notify/session/model-update`,
|
|
1652
1804
|
* `qwen/notify/session/mode-update`,
|
|
1653
1805
|
* `qwen/notify/session/title-update` (auto/in-process session titles),
|
|
1806
|
+
* `qwen/notify/session/recording-degraded`,
|
|
1654
1807
|
* `qwen/notify/session/prompt-suggestion` (followup assist),
|
|
1655
1808
|
* `qwen/notify/session/artifact-event` (hook artifacts),
|
|
1656
1809
|
* `qwen/notify/session/terminal-sequence`, and
|
|
1657
1810
|
* `qwen/notify/session/mcp-budget-event` — each translated into a
|
|
1658
|
-
* session-scoped SSE frame. Unknown methods are dropped silently
|
|
1659
|
-
*
|
|
1811
|
+
* session-scoped SSE frame. Unknown methods are dropped silently for
|
|
1812
|
+
* forward-compat.
|
|
1660
1813
|
*/
|
|
1661
1814
|
async extNotification(method, params) {
|
|
1662
1815
|
if (method === "qwen/notify/session/model-update") {
|
|
@@ -1687,6 +1840,28 @@ var BridgeClient = class {
|
|
|
1687
1840
|
}
|
|
1688
1841
|
return;
|
|
1689
1842
|
}
|
|
1843
|
+
if (method === "qwen/notify/session/recording-degraded") {
|
|
1844
|
+
const sessionId2 = params["sessionId"];
|
|
1845
|
+
if (params["v"] !== 1 || typeof sessionId2 !== "string" || sessionId2.length === 0 || params["reason"] !== "write_failed") {
|
|
1846
|
+
writeStderrLine(
|
|
1847
|
+
`[demux] session=${typeof sessionId2 === "string" ? sessionId2 : "<missing>"} type=session_recording_degraded action=dropped reason=malformed`
|
|
1848
|
+
);
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
const entry = this.resolveEntry(sessionId2);
|
|
1852
|
+
if (entry && !this.ownsSession(sessionId2)) {
|
|
1853
|
+
writeStderrLine(
|
|
1854
|
+
`[demux] session=${sessionId2} type=session_recording_degraded action=dropped reason=session_not_owned`
|
|
1855
|
+
);
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1858
|
+
if (entry) entry.recordingDegraded = true;
|
|
1859
|
+
this.publishExtNotification(sessionId2, "session_recording_degraded", {
|
|
1860
|
+
sessionId: sessionId2,
|
|
1861
|
+
reason: "write_failed"
|
|
1862
|
+
});
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1690
1865
|
if (method === "qwen/notify/session/prompt-suggestion") {
|
|
1691
1866
|
const sessionId2 = params["sessionId"];
|
|
1692
1867
|
const suggestion = params["suggestion"];
|
|
@@ -1943,7 +2118,7 @@ var BridgeClient = class {
|
|
|
1943
2118
|
this.sweepExpiredTombstones(now);
|
|
1944
2119
|
if (this.tombstonedSessionIds.has(sessionId) && !this.inFlightRestoreIds.has(sessionId)) {
|
|
1945
2120
|
writeStderrLine(
|
|
1946
|
-
`qwen serve: dropping
|
|
2121
|
+
`qwen serve: dropping early extNotification for tombstoned session ${JSON.stringify(sessionId)} (post-close stale event)`
|
|
1947
2122
|
);
|
|
1948
2123
|
return;
|
|
1949
2124
|
}
|
|
@@ -1952,7 +2127,7 @@ var BridgeClient = class {
|
|
|
1952
2127
|
if (!buf) {
|
|
1953
2128
|
if (this.earlyEvents.size >= MAX_EARLY_EVENT_SESSIONS) {
|
|
1954
2129
|
writeStderrLine(
|
|
1955
|
-
`qwen serve: dropping
|
|
2130
|
+
`qwen serve: dropping early extNotification \u2014 early-event buffer at MAX_EARLY_EVENT_SESSIONS (${MAX_EARLY_EVENT_SESSIONS}); possible session-id fanout abuse`
|
|
1956
2131
|
);
|
|
1957
2132
|
return;
|
|
1958
2133
|
}
|
|
@@ -1961,7 +2136,7 @@ var BridgeClient = class {
|
|
|
1961
2136
|
}
|
|
1962
2137
|
if (buf.frames.length >= MAX_EARLY_EVENTS_PER_SESSION) {
|
|
1963
2138
|
writeStderrLine(
|
|
1964
|
-
`qwen serve: dropping
|
|
2139
|
+
`qwen serve: dropping early extNotification for session ${JSON.stringify(sessionId)} \u2014 per-session cap (${MAX_EARLY_EVENTS_PER_SESSION}) reached`
|
|
1965
2140
|
);
|
|
1966
2141
|
return;
|
|
1967
2142
|
}
|
|
@@ -1995,7 +2170,7 @@ var BridgeClient = class {
|
|
|
1995
2170
|
* Mark a sessionId as currently being restored via `session/load` /
|
|
1996
2171
|
* `session/resume`. While in this set, `bufferEarlyEvent` accepts
|
|
1997
2172
|
* frames for the id even if it's tombstoned — so restore-time
|
|
1998
|
-
*
|
|
2173
|
+
* early events from the freshly-restored child reach
|
|
1999
2174
|
* `drainEarlyEvents` instead of being rejected by the tombstone.
|
|
2000
2175
|
*
|
|
2001
2176
|
* Bridge factory calls this BEFORE awaiting the ACP restore call.
|
|
@@ -2029,7 +2204,12 @@ var BridgeClient = class {
|
|
|
2029
2204
|
this.tombstonedSessionIds.delete(sessionId);
|
|
2030
2205
|
const buf = this.earlyEvents.get(sessionId);
|
|
2031
2206
|
if (!buf) return;
|
|
2032
|
-
for (const frame of buf.frames)
|
|
2207
|
+
for (const frame of buf.frames) {
|
|
2208
|
+
if (frame.type === "session_recording_degraded") {
|
|
2209
|
+
entry.recordingDegraded = true;
|
|
2210
|
+
}
|
|
2211
|
+
entry.events.publish(frame);
|
|
2212
|
+
}
|
|
2033
2213
|
this.earlyEvents.delete(sessionId);
|
|
2034
2214
|
}
|
|
2035
2215
|
async writeTextFile(params) {
|
|
@@ -2096,6 +2276,18 @@ var BridgeClient = class {
|
|
|
2096
2276
|
if (typeof params.limit === "number" && params.limit <= 0) {
|
|
2097
2277
|
return { content: "" };
|
|
2098
2278
|
}
|
|
2279
|
+
if (typeof params.limit === "number" && params.limit > 0 && !Number.isSafeInteger(params.limit)) {
|
|
2280
|
+
throw RequestError.invalidParams(
|
|
2281
|
+
void 0,
|
|
2282
|
+
`\`limit\` must be a positive integer, got ${params.limit}`
|
|
2283
|
+
);
|
|
2284
|
+
}
|
|
2285
|
+
if (typeof params.line === "number" && params.line > 0 && !Number.isSafeInteger(params.line)) {
|
|
2286
|
+
throw RequestError.invalidParams(
|
|
2287
|
+
void 0,
|
|
2288
|
+
`\`line\` must be a positive integer, got ${params.line}`
|
|
2289
|
+
);
|
|
2290
|
+
}
|
|
2099
2291
|
const READ_FILE_SIZE_CAP = 100 * 1024 * 1024;
|
|
2100
2292
|
const stats = await fs.stat(params.path);
|
|
2101
2293
|
if (!stats.isFile()) {
|
|
@@ -2239,7 +2431,7 @@ __name(extractA2uiToolUpdate, "extractA2uiToolUpdate");
|
|
|
2239
2431
|
// packages/acp-bridge/src/sessionArtifacts.ts
|
|
2240
2432
|
init_esbuild_shims();
|
|
2241
2433
|
import { createHash } from "node:crypto";
|
|
2242
|
-
import { promises as fs2 } from "node:fs";
|
|
2434
|
+
import { constants as fsConstants, promises as fs2 } from "node:fs";
|
|
2243
2435
|
import path2 from "node:path";
|
|
2244
2436
|
var SOURCE_RESERVATIONS = {
|
|
2245
2437
|
tool: 100,
|
|
@@ -2248,6 +2440,18 @@ var SOURCE_RESERVATIONS = {
|
|
|
2248
2440
|
};
|
|
2249
2441
|
var WORKSPACE_STATUS_REFRESH_TTL_MS = 5e3;
|
|
2250
2442
|
var WORKSPACE_STATUS_REFRESH_BATCH_SIZE = 20;
|
|
2443
|
+
var SNAPSHOT_AFTER_DURABLE_EVENTS = 50;
|
|
2444
|
+
var MAX_SNAPSHOT_BACKOFF_MULTIPLIER = 4;
|
|
2445
|
+
var MAX_TOMBSTONED_IDS = 500;
|
|
2446
|
+
var MAX_STICKY_EPHEMERAL_IDS = 500;
|
|
2447
|
+
var MAX_WORKSPACE_HASH_BYTES = 100 * 1024 * 1024;
|
|
2448
|
+
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;
|
|
2449
|
+
var RESTORE_FAILED_WARNING_PREFIX = "artifact snapshot restore failed";
|
|
2450
|
+
var RESTORE_PARTIAL_FAILED_WARNING_PREFIX = "artifact snapshot restore partially failed";
|
|
2451
|
+
function isArtifactRestoreFailureWarning(warning) {
|
|
2452
|
+
return warning.startsWith(RESTORE_FAILED_WARNING_PREFIX) || warning.startsWith(RESTORE_PARTIAL_FAILED_WARNING_PREFIX);
|
|
2453
|
+
}
|
|
2454
|
+
__name(isArtifactRestoreFailureWarning, "isArtifactRestoreFailureWarning");
|
|
2251
2455
|
var SessionArtifactValidationError = class extends Error {
|
|
2252
2456
|
constructor(message, field) {
|
|
2253
2457
|
super(message);
|
|
@@ -2259,6 +2463,20 @@ var SessionArtifactValidationError = class extends Error {
|
|
|
2259
2463
|
}
|
|
2260
2464
|
code = "VALIDATION_FAILED";
|
|
2261
2465
|
};
|
|
2466
|
+
var SessionArtifactAuthorizationError = class extends Error {
|
|
2467
|
+
constructor(sessionId, artifactId, ownerClientId, requesterClientId) {
|
|
2468
|
+
super(`artifact ${artifactId} is owned by a different client`);
|
|
2469
|
+
this.sessionId = sessionId;
|
|
2470
|
+
this.artifactId = artifactId;
|
|
2471
|
+
this.ownerClientId = ownerClientId;
|
|
2472
|
+
this.requesterClientId = requesterClientId;
|
|
2473
|
+
this.name = "SessionArtifactAuthorizationError";
|
|
2474
|
+
}
|
|
2475
|
+
static {
|
|
2476
|
+
__name(this, "SessionArtifactAuthorizationError");
|
|
2477
|
+
}
|
|
2478
|
+
code = "SESSION_ARTIFACT_FORBIDDEN";
|
|
2479
|
+
};
|
|
2262
2480
|
var SessionArtifactStore = class {
|
|
2263
2481
|
static {
|
|
2264
2482
|
__name(this, "SessionArtifactStore");
|
|
@@ -2266,15 +2484,26 @@ var SessionArtifactStore = class {
|
|
|
2266
2484
|
sessionId;
|
|
2267
2485
|
workspaceCwd;
|
|
2268
2486
|
maxArtifacts;
|
|
2487
|
+
persistence;
|
|
2269
2488
|
artifacts = /* @__PURE__ */ new Map();
|
|
2270
2489
|
receivedSeq = 0;
|
|
2271
2490
|
insertSeq = 0;
|
|
2491
|
+
persistenceSeq = 0;
|
|
2492
|
+
durableEventsSinceSnapshot = 0;
|
|
2493
|
+
consecutiveSnapshotFailures = 0;
|
|
2272
2494
|
realWorkspaceCwdPromise;
|
|
2273
2495
|
operationQueue = Promise.resolve();
|
|
2496
|
+
tombstonedIds = /* @__PURE__ */ new Set();
|
|
2497
|
+
tombstonedClientIds = /* @__PURE__ */ new Map();
|
|
2498
|
+
stickyEphemeralIds = /* @__PURE__ */ new Set();
|
|
2499
|
+
markerArtifacts = /* @__PURE__ */ new Map();
|
|
2500
|
+
lastRestoreWarnings = [];
|
|
2501
|
+
lastRestoreWarningDetails = [];
|
|
2274
2502
|
constructor(options) {
|
|
2275
2503
|
this.sessionId = options.sessionId;
|
|
2276
2504
|
this.workspaceCwd = options.workspaceCwd;
|
|
2277
2505
|
this.maxArtifacts = options.maxArtifacts ?? 200;
|
|
2506
|
+
this.persistence = options.persistence;
|
|
2278
2507
|
}
|
|
2279
2508
|
inputBatchLimit() {
|
|
2280
2509
|
return this.maxArtifacts * 2;
|
|
@@ -2287,13 +2516,30 @@ var SessionArtifactStore = class {
|
|
|
2287
2516
|
sessionId: this.sessionId,
|
|
2288
2517
|
artifacts: Array.from(this.artifacts.values()).sort((a, b) => a.insertSeq - b.insertSeq).map(toPublicArtifact),
|
|
2289
2518
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2290
|
-
limits: { maxArtifacts: this.maxArtifacts }
|
|
2519
|
+
limits: { maxArtifacts: this.maxArtifacts },
|
|
2520
|
+
...this.lastRestoreWarnings.length > 0 ? { warnings: [...this.lastRestoreWarnings] } : {},
|
|
2521
|
+
...this.lastRestoreWarningDetails.length > 0 ? { warningDetails: [...this.lastRestoreWarningDetails] } : {}
|
|
2291
2522
|
};
|
|
2292
2523
|
});
|
|
2293
2524
|
}
|
|
2525
|
+
async get(artifactId) {
|
|
2526
|
+
return this.enqueue(async () => {
|
|
2527
|
+
const artifact = this.artifacts.get(artifactId);
|
|
2528
|
+
if (!artifact) return void 0;
|
|
2529
|
+
if (artifact.workspacePath && shouldRefreshWorkspaceStatus(artifact, Date.now())) {
|
|
2530
|
+
await this.refreshWorkspaceStatus(artifact, { onError: "missing" });
|
|
2531
|
+
}
|
|
2532
|
+
return toPublicArtifact(artifact);
|
|
2533
|
+
});
|
|
2534
|
+
}
|
|
2294
2535
|
async upsertMany(inputs, options = {}) {
|
|
2295
2536
|
return this.enqueue(async () => {
|
|
2537
|
+
const validationStrict = options.validationStrict ?? options.strict;
|
|
2538
|
+
const persistenceStrict = options.persistenceStrict ?? options.strict;
|
|
2539
|
+
const before = this.cloneState();
|
|
2296
2540
|
const normalizedResults = [];
|
|
2541
|
+
const warnings = [];
|
|
2542
|
+
const warningDetails = [];
|
|
2297
2543
|
for (const input of inputs) {
|
|
2298
2544
|
try {
|
|
2299
2545
|
normalizedResults.push(
|
|
@@ -2304,7 +2550,7 @@ var SessionArtifactStore = class {
|
|
|
2304
2550
|
)
|
|
2305
2551
|
);
|
|
2306
2552
|
} catch (error) {
|
|
2307
|
-
if (
|
|
2553
|
+
if (validationStrict) {
|
|
2308
2554
|
throw error;
|
|
2309
2555
|
}
|
|
2310
2556
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -2316,39 +2562,140 @@ var SessionArtifactStore = class {
|
|
|
2316
2562
|
}
|
|
2317
2563
|
}
|
|
2318
2564
|
const changes = [];
|
|
2319
|
-
|
|
2320
|
-
const
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2565
|
+
try {
|
|
2566
|
+
for (const normalized of coalesceByIdentity(normalizedResults)) {
|
|
2567
|
+
const artifact = this.applyStickyEphemeralOverride(normalized);
|
|
2568
|
+
if (this.shouldSuppressTombstonedUpsert(artifact)) {
|
|
2569
|
+
writeStderrLine(
|
|
2570
|
+
`[artifacts] session=${this.sessionId} action=tombstone_replay_suppressed artifactId=${artifact.id}`
|
|
2571
|
+
);
|
|
2572
|
+
continue;
|
|
2573
|
+
}
|
|
2574
|
+
const existing = this.artifacts.get(artifact.id) ?? this.findPublishedUpgradeTarget(artifact) ?? this.findPublishedWorkspaceTarget(artifact);
|
|
2575
|
+
if (!existing) {
|
|
2576
|
+
const stored = {
|
|
2577
|
+
...artifact,
|
|
2578
|
+
insertSeq: ++this.insertSeq
|
|
2579
|
+
};
|
|
2580
|
+
this.artifacts.set(stored.id, stored);
|
|
2581
|
+
changes.push({
|
|
2582
|
+
action: "created",
|
|
2583
|
+
artifactId: stored.id,
|
|
2584
|
+
artifact: toPublicArtifact(stored)
|
|
2585
|
+
});
|
|
2586
|
+
continue;
|
|
2587
|
+
}
|
|
2588
|
+
try {
|
|
2589
|
+
this.denyCrossClientMutation("upsert", existing.id, existing, {
|
|
2590
|
+
clientId: artifact.clientId
|
|
2591
|
+
});
|
|
2592
|
+
} catch (error) {
|
|
2593
|
+
if (validationStrict) {
|
|
2594
|
+
throw error;
|
|
2595
|
+
}
|
|
2596
|
+
if (error instanceof SessionArtifactAuthorizationError) {
|
|
2597
|
+
warnings.push(error.message);
|
|
2598
|
+
continue;
|
|
2599
|
+
}
|
|
2600
|
+
throw error;
|
|
2601
|
+
}
|
|
2602
|
+
const updated = mergeArtifact(existing, artifact);
|
|
2603
|
+
if (updated.changed) {
|
|
2604
|
+
if (updated.artifact.id !== existing.id) {
|
|
2605
|
+
const removeChange = {
|
|
2606
|
+
action: "removed",
|
|
2607
|
+
artifactId: existing.id,
|
|
2608
|
+
artifact: toPublicArtifact(existing),
|
|
2609
|
+
reason: "explicit",
|
|
2610
|
+
durableTombstoneRequired: existing.durableTombstoneRequired || existing.persistedAt !== void 0,
|
|
2611
|
+
removedClientId: existing.clientId
|
|
2612
|
+
};
|
|
2613
|
+
changes.push(removeChange);
|
|
2614
|
+
this.artifacts.delete(existing.id);
|
|
2615
|
+
} else if (shouldRecordEphemeralUnpin(existing, artifact)) {
|
|
2616
|
+
changes.push({
|
|
2617
|
+
action: "removed",
|
|
2618
|
+
artifactId: existing.id,
|
|
2619
|
+
artifact: toPublicArtifact(existing),
|
|
2620
|
+
reason: "unpin_to_ephemeral",
|
|
2621
|
+
durableTombstoneRequired: true
|
|
2622
|
+
});
|
|
2623
|
+
}
|
|
2624
|
+
this.artifacts.set(updated.artifact.id, updated.artifact);
|
|
2625
|
+
changes.push({
|
|
2626
|
+
action: "updated",
|
|
2627
|
+
artifactId: updated.artifact.id,
|
|
2628
|
+
artifact: toPublicArtifact(updated.artifact)
|
|
2629
|
+
});
|
|
2630
|
+
}
|
|
2333
2631
|
}
|
|
2334
|
-
const
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2632
|
+
const createdIds = new Set(
|
|
2633
|
+
changes.filter((change) => change.action === "created").map((change) => change.artifactId)
|
|
2634
|
+
);
|
|
2635
|
+
changes.push(
|
|
2636
|
+
...await this.evictOverflow(createdIds, changes, persistenceStrict)
|
|
2637
|
+
);
|
|
2638
|
+
const hasStrictDurableTransition = changes.some(
|
|
2639
|
+
shouldCommitBeforeDurablePersistence
|
|
2640
|
+
);
|
|
2641
|
+
let persistenceWarnings;
|
|
2642
|
+
if (hasStrictDurableTransition && !persistenceStrict) {
|
|
2643
|
+
try {
|
|
2644
|
+
persistenceWarnings = await this.persistChanges(changes, true);
|
|
2645
|
+
} catch (error) {
|
|
2646
|
+
this.restoreState(before);
|
|
2647
|
+
const artifactIds = changes.filter(shouldCommitBeforeDurablePersistence).map((change) => change.artifactId);
|
|
2648
|
+
const warning = "artifact durable removal not persisted; live changes rolled back";
|
|
2649
|
+
writeStderrLine(
|
|
2650
|
+
`[artifacts] session=${this.sessionId} action=upsert_rollback warning=${JSON.stringify(
|
|
2651
|
+
warning
|
|
2652
|
+
)} artifactIds=${JSON.stringify(artifactIds)} error=${JSON.stringify(
|
|
2653
|
+
error instanceof Error ? error.message : String(error)
|
|
2654
|
+
)}`
|
|
2655
|
+
);
|
|
2656
|
+
return {
|
|
2657
|
+
v: 1,
|
|
2658
|
+
sessionId: this.sessionId,
|
|
2659
|
+
changes: [],
|
|
2660
|
+
warnings: [warning],
|
|
2661
|
+
warningDetails: [
|
|
2662
|
+
persistenceFailureDetail({
|
|
2663
|
+
message: warning,
|
|
2664
|
+
operation: "upsert",
|
|
2665
|
+
artifactIds,
|
|
2666
|
+
error
|
|
2667
|
+
})
|
|
2668
|
+
]
|
|
2669
|
+
};
|
|
2338
2670
|
}
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2671
|
+
} else {
|
|
2672
|
+
persistenceWarnings = await this.persistChanges(
|
|
2673
|
+
changes,
|
|
2674
|
+
persistenceStrict
|
|
2675
|
+
);
|
|
2676
|
+
}
|
|
2677
|
+
warnings.push(...persistenceWarnings);
|
|
2678
|
+
warningDetails.push(
|
|
2679
|
+
...detailsForPersistenceWarnings(
|
|
2680
|
+
persistenceWarnings,
|
|
2681
|
+
changes,
|
|
2682
|
+
"upsert"
|
|
2683
|
+
)
|
|
2684
|
+
);
|
|
2685
|
+
stripDurableTombstoneMarkers(changes);
|
|
2686
|
+
} catch (error) {
|
|
2687
|
+
if (validationStrict || persistenceStrict || error instanceof SessionArtifactAuthorizationError) {
|
|
2688
|
+
this.restoreState(before);
|
|
2345
2689
|
}
|
|
2690
|
+
throw error;
|
|
2346
2691
|
}
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2692
|
+
return {
|
|
2693
|
+
v: 1,
|
|
2694
|
+
sessionId: this.sessionId,
|
|
2695
|
+
changes,
|
|
2696
|
+
...warnings.length > 0 ? { warnings } : {},
|
|
2697
|
+
...warningDetails.length > 0 ? { warningDetails } : {}
|
|
2698
|
+
};
|
|
2352
2699
|
});
|
|
2353
2700
|
}
|
|
2354
2701
|
findPublishedUpgradeTarget(artifact) {
|
|
@@ -2356,7 +2703,7 @@ var SessionArtifactStore = class {
|
|
|
2356
2703
|
return void 0;
|
|
2357
2704
|
}
|
|
2358
2705
|
const byUrl = this.artifacts.get(
|
|
2359
|
-
|
|
2706
|
+
stableSessionArtifactId(this.sessionId, `url:${artifact.url}`)
|
|
2360
2707
|
);
|
|
2361
2708
|
if (byUrl && (byUrl.storage !== "published" || byUrl.managedId === artifact.managedId)) {
|
|
2362
2709
|
return byUrl;
|
|
@@ -2392,93 +2739,648 @@ var SessionArtifactStore = class {
|
|
|
2392
2739
|
if (!existing) {
|
|
2393
2740
|
return { v: 1, sessionId: this.sessionId, changes: [] };
|
|
2394
2741
|
}
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2742
|
+
this.denyCrossClientMutation("remove", artifactId, existing, options);
|
|
2743
|
+
const removeChange = {
|
|
2744
|
+
action: "removed",
|
|
2745
|
+
artifactId,
|
|
2746
|
+
artifact: toPublicArtifact(existing),
|
|
2747
|
+
reason: "explicit",
|
|
2748
|
+
durableTombstoneRequired: existing.durableTombstoneRequired || existing.retention !== "ephemeral" ? true : void 0,
|
|
2749
|
+
removedClientId: existing.clientId
|
|
2750
|
+
};
|
|
2751
|
+
const changes = [removeChange];
|
|
2752
|
+
const needsDurableTombstone = removeChange.durableTombstoneRequired === true;
|
|
2753
|
+
if (needsDurableTombstone) {
|
|
2754
|
+
const before = this.cloneState();
|
|
2755
|
+
this.artifacts.delete(artifactId);
|
|
2756
|
+
try {
|
|
2757
|
+
await this.persistChanges(changes, true);
|
|
2758
|
+
} catch (error) {
|
|
2759
|
+
this.restoreState(before);
|
|
2760
|
+
const warning = "artifact removal not persisted; live artifact kept";
|
|
2761
|
+
return {
|
|
2762
|
+
v: 1,
|
|
2763
|
+
sessionId: this.sessionId,
|
|
2764
|
+
changes: [],
|
|
2765
|
+
warnings: [warning],
|
|
2766
|
+
warningDetails: [
|
|
2767
|
+
persistenceFailureDetail({
|
|
2768
|
+
message: warning,
|
|
2769
|
+
operation: "remove",
|
|
2770
|
+
artifactIds: [artifactId],
|
|
2771
|
+
error
|
|
2772
|
+
})
|
|
2773
|
+
]
|
|
2774
|
+
};
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
if (!needsDurableTombstone) {
|
|
2778
|
+
this.artifacts.delete(artifactId);
|
|
2400
2779
|
}
|
|
2401
|
-
this.
|
|
2780
|
+
const warnings = needsDurableTombstone ? [] : await this.persistChanges(changes, false);
|
|
2781
|
+
stripDurableTombstoneMarkers(changes);
|
|
2782
|
+
const warningDetails = detailsForPersistenceWarnings(
|
|
2783
|
+
warnings,
|
|
2784
|
+
changes,
|
|
2785
|
+
"remove"
|
|
2786
|
+
);
|
|
2402
2787
|
return {
|
|
2403
2788
|
v: 1,
|
|
2404
2789
|
sessionId: this.sessionId,
|
|
2405
|
-
changes
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
artifactId,
|
|
2409
|
-
artifact: toPublicArtifact(existing),
|
|
2410
|
-
reason: "explicit"
|
|
2411
|
-
}
|
|
2412
|
-
]
|
|
2790
|
+
changes,
|
|
2791
|
+
...warnings.length > 0 ? { warnings } : {},
|
|
2792
|
+
...warningDetails.length > 0 ? { warningDetails } : {}
|
|
2413
2793
|
};
|
|
2414
2794
|
});
|
|
2415
2795
|
}
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
this.
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2796
|
+
async restore(snapshot, options = {}) {
|
|
2797
|
+
if (!snapshot) return [];
|
|
2798
|
+
return this.enqueue(async () => {
|
|
2799
|
+
const warnings = [...snapshot?.warnings ?? []];
|
|
2800
|
+
const baselineWarnings = [...warnings];
|
|
2801
|
+
const previousState = this.cloneState();
|
|
2802
|
+
const preservedLiveEphemeralArtifacts = options.preserveLiveEphemeral ? Array.from(this.artifacts.values()).filter((artifact) => artifact.retention === "ephemeral").map(cloneStoredArtifact) : [];
|
|
2803
|
+
let restoredCount = 0;
|
|
2804
|
+
this.artifacts.clear();
|
|
2805
|
+
this.tombstonedIds.clear();
|
|
2806
|
+
this.tombstonedClientIds.clear();
|
|
2807
|
+
this.stickyEphemeralIds.clear();
|
|
2808
|
+
this.markerArtifacts.clear();
|
|
2809
|
+
if (snapshot) {
|
|
2810
|
+
this.insertSeq = 0;
|
|
2811
|
+
this.persistenceSeq = snapshot.sequence;
|
|
2812
|
+
this.durableEventsSinceSnapshot = 0;
|
|
2813
|
+
this.consecutiveSnapshotFailures = 0;
|
|
2814
|
+
for (const id of snapshot.tombstonedIds) {
|
|
2815
|
+
this.tombstonedIds.add(id);
|
|
2816
|
+
}
|
|
2817
|
+
for (const id of snapshot.stickyEphemeralIds.slice(
|
|
2818
|
+
-MAX_STICKY_EPHEMERAL_IDS
|
|
2819
|
+
)) {
|
|
2820
|
+
this.stickyEphemeralIds.add(id);
|
|
2821
|
+
}
|
|
2822
|
+
const markerIds = /* @__PURE__ */ new Set([
|
|
2823
|
+
...this.tombstonedIds,
|
|
2824
|
+
...this.stickyEphemeralIds
|
|
2825
|
+
]);
|
|
2826
|
+
for (const artifact of snapshot.markerArtifacts ?? []) {
|
|
2827
|
+
if (!markerIds.has(artifact.id)) continue;
|
|
2828
|
+
const markerArtifact = await this.normalizeRestoredMarkerArtifact(
|
|
2829
|
+
artifact,
|
|
2830
|
+
warnings
|
|
2831
|
+
);
|
|
2832
|
+
if (markerArtifact)
|
|
2833
|
+
this.markerArtifacts.set(artifact.id, markerArtifact);
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
for (const artifact of snapshot?.artifacts ?? []) {
|
|
2837
|
+
try {
|
|
2838
|
+
const input = persistedArtifactToInput(artifact);
|
|
2839
|
+
if (input.retention === "pinned") {
|
|
2840
|
+
input.retention = "restorable";
|
|
2841
|
+
warnings.push(
|
|
2842
|
+
`pinned artifact ${artifact.id} downgraded to restorable; runtime does not support pinned retention`
|
|
2843
|
+
);
|
|
2844
|
+
}
|
|
2845
|
+
let normalized = await this.normalizeInput(
|
|
2846
|
+
input,
|
|
2847
|
+
++this.receivedSeq,
|
|
2848
|
+
artifact.storage === "published" && !isFileArtifactUrl(artifact.url),
|
|
2849
|
+
{
|
|
2850
|
+
metadataBudget: "persisted",
|
|
2851
|
+
workspaceExpected: workspaceExpectedFromArtifact(artifact),
|
|
2852
|
+
hashWorkspaceContent: false
|
|
2853
|
+
}
|
|
2854
|
+
);
|
|
2855
|
+
if (this.stickyEphemeralIds.has(normalized.id) && normalized.retention !== "ephemeral") {
|
|
2856
|
+
normalized = {
|
|
2857
|
+
...normalized,
|
|
2858
|
+
retention: "ephemeral",
|
|
2859
|
+
persistenceWarning: "sticky_override_active"
|
|
2860
|
+
};
|
|
2861
|
+
}
|
|
2862
|
+
const stickyApplied = normalized.persistenceWarning === "sticky_override_active";
|
|
2863
|
+
if (normalized.id !== artifact.id) {
|
|
2864
|
+
warnings.push(`skipped artifact with mismatched id ${artifact.id}`);
|
|
2865
|
+
continue;
|
|
2866
|
+
}
|
|
2867
|
+
const retention = normalized.retention;
|
|
2868
|
+
let persistenceWarning;
|
|
2869
|
+
if (stickyApplied) {
|
|
2870
|
+
persistenceWarning = "sticky_override_active";
|
|
2871
|
+
} else if (normalized.status !== "available") {
|
|
2872
|
+
persistenceWarning = "metadata_only_restore";
|
|
2873
|
+
}
|
|
2874
|
+
const stored = {
|
|
2875
|
+
...normalized,
|
|
2876
|
+
retention,
|
|
2877
|
+
clientRetained: artifact.clientRetained,
|
|
2878
|
+
createdAt: artifact.createdAt,
|
|
2879
|
+
updatedAt: artifact.updatedAt,
|
|
2880
|
+
persistedAt: artifact.persistedAt,
|
|
2881
|
+
status: normalized.status,
|
|
2882
|
+
restoreState: "restored",
|
|
2883
|
+
persistenceWarning,
|
|
2884
|
+
durableTombstoneRequired: retention !== "ephemeral" ? true : void 0,
|
|
2885
|
+
insertSeq: ++this.insertSeq
|
|
2886
|
+
};
|
|
2887
|
+
this.artifacts.set(stored.id, stored);
|
|
2888
|
+
restoredCount++;
|
|
2889
|
+
} catch (error) {
|
|
2890
|
+
warnings.push(
|
|
2891
|
+
`skipped artifact restore: ${error instanceof Error ? error.message : String(error)}`
|
|
2892
|
+
);
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
if (snapshot.artifacts.length > 0 && restoredCount === 0) {
|
|
2896
|
+
this.restoreState(previousState);
|
|
2897
|
+
const rollbackWarnings = [
|
|
2898
|
+
...baselineWarnings,
|
|
2899
|
+
`${RESTORE_FAILED_WARNING_PREFIX}; kept existing live artifacts`
|
|
2900
|
+
];
|
|
2901
|
+
this.setLastRestoreWarnings(rollbackWarnings);
|
|
2902
|
+
return rollbackWarnings;
|
|
2903
|
+
}
|
|
2904
|
+
if (previousState.artifacts.size > 0 && baselineWarnings.some(isArtifactSnapshotCompletenessWarning)) {
|
|
2905
|
+
this.restoreState(previousState);
|
|
2906
|
+
const rollbackWarnings = [
|
|
2907
|
+
...baselineWarnings,
|
|
2908
|
+
restoredCount === 0 ? `${RESTORE_FAILED_WARNING_PREFIX}; kept existing live artifacts` : `${RESTORE_PARTIAL_FAILED_WARNING_PREFIX}; kept existing live artifacts`
|
|
2909
|
+
];
|
|
2910
|
+
this.setLastRestoreWarnings(rollbackWarnings);
|
|
2911
|
+
return rollbackWarnings;
|
|
2912
|
+
}
|
|
2913
|
+
for (const artifact of preservedLiveEphemeralArtifacts) {
|
|
2914
|
+
if (this.artifacts.has(artifact.id) || this.tombstonedIds.has(artifact.id)) {
|
|
2915
|
+
continue;
|
|
2916
|
+
}
|
|
2917
|
+
this.artifacts.set(artifact.id, {
|
|
2918
|
+
...artifact,
|
|
2919
|
+
insertSeq: ++this.insertSeq
|
|
2920
|
+
});
|
|
2921
|
+
}
|
|
2922
|
+
const evicted = await this.evictOverflow(/* @__PURE__ */ new Set(), []);
|
|
2923
|
+
if (evicted.length > 0) {
|
|
2924
|
+
warnings.push("restored artifact list pruned to live limit");
|
|
2925
|
+
warnings.push(...await this.persistChanges(evicted, false));
|
|
2926
|
+
}
|
|
2927
|
+
this.setLastRestoreWarnings(warnings);
|
|
2928
|
+
return warnings;
|
|
2929
|
+
});
|
|
2423
2930
|
}
|
|
2424
|
-
async
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
input.description,
|
|
2431
|
-
"description",
|
|
2432
|
-
1e3,
|
|
2433
|
-
false
|
|
2434
|
-
);
|
|
2435
|
-
const source = input.source ?? "tool";
|
|
2436
|
-
if (source !== "tool" && source !== "hook" && source !== "client") {
|
|
2437
|
-
throw new SessionArtifactValidationError(
|
|
2438
|
-
"source must be tool, hook, or client",
|
|
2439
|
-
"source"
|
|
2440
|
-
);
|
|
2931
|
+
async recordSnapshot() {
|
|
2932
|
+
const persistence = this.persistence;
|
|
2933
|
+
if (!persistence) {
|
|
2934
|
+
return [
|
|
2935
|
+
"artifact persistence unavailable; restored artifacts not snapshotted"
|
|
2936
|
+
];
|
|
2441
2937
|
}
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2938
|
+
return this.enqueue(async () => {
|
|
2939
|
+
const recordedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2940
|
+
const sequence = this.persistenceSeq + 1;
|
|
2941
|
+
try {
|
|
2942
|
+
await persistence.recordSnapshot(
|
|
2943
|
+
this.buildSnapshotPayload(recordedAt, sequence)
|
|
2944
|
+
);
|
|
2945
|
+
this.persistenceSeq = sequence;
|
|
2946
|
+
this.durableEventsSinceSnapshot = 0;
|
|
2947
|
+
this.consecutiveSnapshotFailures = 0;
|
|
2948
|
+
return [];
|
|
2949
|
+
} catch (error) {
|
|
2950
|
+
writeStderrLine(
|
|
2951
|
+
`[artifacts] session=${this.sessionId} action=snapshot_failed reason=${JSON.stringify(
|
|
2952
|
+
error instanceof Error ? error.message : String(error)
|
|
2953
|
+
)}`
|
|
2954
|
+
);
|
|
2955
|
+
return ["artifact snapshot not persisted"];
|
|
2956
|
+
}
|
|
2458
2957
|
});
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
"
|
|
2958
|
+
}
|
|
2959
|
+
async normalizeRestoredMarkerArtifact(artifact, warnings) {
|
|
2960
|
+
try {
|
|
2961
|
+
const input = persistedArtifactToInput(artifact);
|
|
2962
|
+
if (input.retention === "pinned") {
|
|
2963
|
+
input.retention = "restorable";
|
|
2964
|
+
warnings.push(
|
|
2965
|
+
`pinned marker artifact ${artifact.id} downgraded to restorable; runtime does not support pinned retention`
|
|
2966
|
+
);
|
|
2967
|
+
}
|
|
2968
|
+
const normalized = await this.normalizeInput(
|
|
2969
|
+
input,
|
|
2970
|
+
++this.receivedSeq,
|
|
2971
|
+
artifact.storage === "published" && !isFileArtifactUrl(artifact.url),
|
|
2972
|
+
{
|
|
2973
|
+
metadataBudget: "persisted",
|
|
2974
|
+
workspaceExpected: workspaceExpectedFromArtifact(artifact),
|
|
2975
|
+
hashWorkspaceContent: false
|
|
2976
|
+
}
|
|
2977
|
+
);
|
|
2978
|
+
if (normalized.id !== artifact.id) {
|
|
2979
|
+
warnings.push(
|
|
2980
|
+
`skipped marker artifact with mismatched id ${artifact.id}`
|
|
2981
|
+
);
|
|
2982
|
+
return void 0;
|
|
2983
|
+
}
|
|
2984
|
+
return toPersistedArtifact(
|
|
2985
|
+
{
|
|
2986
|
+
...normalized,
|
|
2987
|
+
clientRetained: artifact.clientRetained,
|
|
2988
|
+
createdAt: artifact.createdAt,
|
|
2989
|
+
updatedAt: artifact.updatedAt,
|
|
2990
|
+
persistedAt: artifact.persistedAt
|
|
2991
|
+
},
|
|
2992
|
+
artifact.persistedAt ?? artifact.updatedAt
|
|
2993
|
+
);
|
|
2994
|
+
} catch (error) {
|
|
2995
|
+
warnings.push(
|
|
2996
|
+
`skipped marker artifact ${artifact.id}: ${error instanceof Error ? error.message : String(error)}`
|
|
2465
2997
|
);
|
|
2998
|
+
return void 0;
|
|
2466
2999
|
}
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
3000
|
+
}
|
|
3001
|
+
cloneState() {
|
|
3002
|
+
return {
|
|
3003
|
+
artifacts: new Map(
|
|
3004
|
+
Array.from(this.artifacts.entries()).map(([id, artifact]) => [
|
|
3005
|
+
id,
|
|
3006
|
+
cloneStoredArtifact(artifact)
|
|
3007
|
+
])
|
|
3008
|
+
),
|
|
3009
|
+
receivedSeq: this.receivedSeq,
|
|
3010
|
+
insertSeq: this.insertSeq,
|
|
3011
|
+
persistenceSeq: this.persistenceSeq,
|
|
3012
|
+
durableEventsSinceSnapshot: this.durableEventsSinceSnapshot,
|
|
3013
|
+
consecutiveSnapshotFailures: this.consecutiveSnapshotFailures,
|
|
3014
|
+
tombstonedIds: new Set(this.tombstonedIds),
|
|
3015
|
+
tombstonedClientIds: new Map(this.tombstonedClientIds),
|
|
3016
|
+
stickyEphemeralIds: new Set(this.stickyEphemeralIds),
|
|
3017
|
+
markerArtifacts: new Map(this.markerArtifacts),
|
|
3018
|
+
lastRestoreWarnings: [...this.lastRestoreWarnings],
|
|
3019
|
+
lastRestoreWarningDetails: [...this.lastRestoreWarningDetails]
|
|
3020
|
+
};
|
|
3021
|
+
}
|
|
3022
|
+
restoreState(state) {
|
|
3023
|
+
this.artifacts.clear();
|
|
3024
|
+
for (const [id, artifact] of state.artifacts) {
|
|
3025
|
+
this.artifacts.set(id, cloneStoredArtifact(artifact));
|
|
3026
|
+
}
|
|
3027
|
+
this.receivedSeq = state.receivedSeq;
|
|
3028
|
+
this.insertSeq = state.insertSeq;
|
|
3029
|
+
this.persistenceSeq = state.persistenceSeq;
|
|
3030
|
+
this.durableEventsSinceSnapshot = state.durableEventsSinceSnapshot;
|
|
3031
|
+
this.consecutiveSnapshotFailures = state.consecutiveSnapshotFailures;
|
|
3032
|
+
this.tombstonedIds.clear();
|
|
3033
|
+
for (const id of state.tombstonedIds) {
|
|
3034
|
+
this.tombstonedIds.add(id);
|
|
3035
|
+
}
|
|
3036
|
+
this.tombstonedClientIds.clear();
|
|
3037
|
+
for (const [id, clientId] of state.tombstonedClientIds) {
|
|
3038
|
+
this.tombstonedClientIds.set(id, clientId);
|
|
3039
|
+
}
|
|
3040
|
+
this.stickyEphemeralIds.clear();
|
|
3041
|
+
for (const id of state.stickyEphemeralIds) {
|
|
3042
|
+
this.stickyEphemeralIds.add(id);
|
|
3043
|
+
}
|
|
3044
|
+
this.markerArtifacts.clear();
|
|
3045
|
+
for (const [id, artifact] of state.markerArtifacts) {
|
|
3046
|
+
this.markerArtifacts.set(id, artifact);
|
|
3047
|
+
}
|
|
3048
|
+
this.setLastRestoreWarnings(state.lastRestoreWarnings);
|
|
3049
|
+
this.lastRestoreWarningDetails = [...state.lastRestoreWarningDetails];
|
|
3050
|
+
}
|
|
3051
|
+
async persistChanges(changes, strict = false) {
|
|
3052
|
+
const durableChanges = changes.filter(isDurablePersistenceChange);
|
|
3053
|
+
if (durableChanges.length === 0) {
|
|
3054
|
+
return [];
|
|
3055
|
+
}
|
|
3056
|
+
if (!this.persistence) {
|
|
3057
|
+
if (strict) {
|
|
3058
|
+
throw new SessionArtifactValidationError(
|
|
3059
|
+
"artifact persistence is unavailable",
|
|
3060
|
+
"retention"
|
|
3061
|
+
);
|
|
3062
|
+
}
|
|
3063
|
+
const warnings = this.downgradeDurableChanges(durableChanges);
|
|
3064
|
+
this.applyDurableMarkers(durableChanges);
|
|
3065
|
+
return warnings;
|
|
3066
|
+
}
|
|
3067
|
+
const recordedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3068
|
+
for (const change of durableChanges) {
|
|
3069
|
+
if (change.action === "removed") continue;
|
|
3070
|
+
const stored = this.artifacts.get(change.artifactId);
|
|
3071
|
+
if (!stored) continue;
|
|
3072
|
+
const artifact = {
|
|
3073
|
+
...toPublicArtifact(stored),
|
|
3074
|
+
persistedAt: recordedAt
|
|
3075
|
+
};
|
|
3076
|
+
change.artifact = artifact;
|
|
3077
|
+
}
|
|
3078
|
+
const sequence = this.persistenceSeq + 1;
|
|
3079
|
+
const payload = {
|
|
3080
|
+
v: SESSION_ARTIFACT_PERSISTENCE_VERSION,
|
|
3081
|
+
sessionId: this.sessionId,
|
|
3082
|
+
sequence,
|
|
3083
|
+
recordedAt,
|
|
3084
|
+
changes: durableChanges.map(
|
|
3085
|
+
(change) => toPersistedChange(change, recordedAt)
|
|
3086
|
+
)
|
|
3087
|
+
};
|
|
3088
|
+
try {
|
|
3089
|
+
await this.persistence.recordEvent(payload);
|
|
3090
|
+
this.persistenceSeq = sequence;
|
|
3091
|
+
for (const change of durableChanges) {
|
|
3092
|
+
if (change.action === "removed") continue;
|
|
3093
|
+
const stored = this.artifacts.get(change.artifactId);
|
|
3094
|
+
if (!stored) continue;
|
|
3095
|
+
stored.persistedAt = recordedAt;
|
|
3096
|
+
stored.durableTombstoneRequired = true;
|
|
3097
|
+
change.artifact = toPublicArtifact(stored);
|
|
3098
|
+
}
|
|
3099
|
+
this.applyDurableMarkers(durableChanges);
|
|
3100
|
+
await this.maybeRecordSnapshot(recordedAt);
|
|
3101
|
+
return [];
|
|
3102
|
+
} catch (error) {
|
|
3103
|
+
if (strict) {
|
|
3104
|
+
throw error;
|
|
3105
|
+
}
|
|
3106
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
3107
|
+
const artifactIds = durableChanges.map((change) => change.artifactId);
|
|
3108
|
+
writeStderrLine(
|
|
3109
|
+
`[artifacts] session=${this.sessionId} action=persist_failed sequence=${payload.sequence} artifactIds=${JSON.stringify(
|
|
3110
|
+
artifactIds
|
|
3111
|
+
)} reason=${JSON.stringify(reason)}`
|
|
3112
|
+
);
|
|
3113
|
+
const warnings = this.downgradeDurableChanges(durableChanges);
|
|
3114
|
+
this.applyDurableMarkers(durableChanges);
|
|
3115
|
+
return warnings;
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
async maybeRecordSnapshot(recordedAt) {
|
|
3119
|
+
if (!this.persistence) return;
|
|
3120
|
+
this.durableEventsSinceSnapshot++;
|
|
3121
|
+
const snapshotThreshold = SNAPSHOT_AFTER_DURABLE_EVENTS * Math.min(
|
|
3122
|
+
MAX_SNAPSHOT_BACKOFF_MULTIPLIER,
|
|
3123
|
+
2 ** this.consecutiveSnapshotFailures
|
|
3124
|
+
);
|
|
3125
|
+
if (this.durableEventsSinceSnapshot < snapshotThreshold) {
|
|
3126
|
+
return;
|
|
3127
|
+
}
|
|
3128
|
+
const sequence = this.persistenceSeq + 1;
|
|
3129
|
+
try {
|
|
3130
|
+
await this.persistence.recordSnapshot(
|
|
3131
|
+
this.buildSnapshotPayload(recordedAt, sequence)
|
|
3132
|
+
);
|
|
3133
|
+
this.persistenceSeq = sequence;
|
|
3134
|
+
this.durableEventsSinceSnapshot = 0;
|
|
3135
|
+
this.consecutiveSnapshotFailures = 0;
|
|
3136
|
+
} catch (error) {
|
|
3137
|
+
this.consecutiveSnapshotFailures = Math.min(
|
|
3138
|
+
this.consecutiveSnapshotFailures + 1,
|
|
3139
|
+
Math.log2(MAX_SNAPSHOT_BACKOFF_MULTIPLIER)
|
|
3140
|
+
);
|
|
3141
|
+
writeStderrLine(
|
|
3142
|
+
`[artifacts] session=${this.sessionId} action=snapshot_failed reason=${JSON.stringify(
|
|
3143
|
+
error instanceof Error ? error.message : String(error)
|
|
3144
|
+
)}`
|
|
3145
|
+
);
|
|
3146
|
+
}
|
|
3147
|
+
}
|
|
3148
|
+
buildSnapshotPayload(recordedAt, sequence) {
|
|
3149
|
+
const artifacts = Array.from(this.artifacts.values()).filter((artifact) => artifact.retention !== "ephemeral").sort((a, b) => a.insertSeq - b.insertSeq).map((artifact) => toPersistedArtifact(artifact, recordedAt));
|
|
3150
|
+
const stickyEphemeralIds = Array.from(this.stickyEphemeralIds).filter(
|
|
3151
|
+
(id) => this.artifacts.has(id) || this.markerArtifacts.has(id)
|
|
3152
|
+
);
|
|
3153
|
+
const markerArtifacts = this.buildMarkerArtifacts(
|
|
3154
|
+
recordedAt,
|
|
3155
|
+
stickyEphemeralIds
|
|
3156
|
+
);
|
|
3157
|
+
return {
|
|
3158
|
+
v: SESSION_ARTIFACT_PERSISTENCE_VERSION,
|
|
3159
|
+
sessionId: this.sessionId,
|
|
3160
|
+
sequence,
|
|
3161
|
+
recordedAt,
|
|
3162
|
+
artifacts,
|
|
3163
|
+
tombstonedIds: Array.from(this.tombstonedIds),
|
|
3164
|
+
stickyEphemeralIds,
|
|
3165
|
+
...markerArtifacts.length > 0 ? { markerArtifacts } : {}
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
buildMarkerArtifacts(recordedAt, stickyEphemeralIds) {
|
|
3169
|
+
const markerIds = [...this.tombstonedIds, ...stickyEphemeralIds];
|
|
3170
|
+
const artifacts = [];
|
|
3171
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3172
|
+
for (const id of markerIds) {
|
|
3173
|
+
if (seen.has(id)) continue;
|
|
3174
|
+
seen.add(id);
|
|
3175
|
+
const live = this.artifacts.get(id);
|
|
3176
|
+
if (live) {
|
|
3177
|
+
artifacts.push(toPersistedArtifact(toPublicArtifact(live), recordedAt));
|
|
3178
|
+
continue;
|
|
3179
|
+
}
|
|
3180
|
+
const markerArtifact = this.markerArtifacts.get(id);
|
|
3181
|
+
if (markerArtifact) {
|
|
3182
|
+
artifacts.push(markerArtifact);
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
return artifacts;
|
|
3186
|
+
}
|
|
3187
|
+
downgradeDurableChanges(changes) {
|
|
3188
|
+
let downgraded = false;
|
|
3189
|
+
let removalNotPersisted = false;
|
|
3190
|
+
for (const change of changes) {
|
|
3191
|
+
if (change.action === "removed") {
|
|
3192
|
+
removalNotPersisted = true;
|
|
3193
|
+
if (change.reason === "explicit") {
|
|
3194
|
+
this.rememberTombstone(change);
|
|
3195
|
+
this.stickyEphemeralIds.delete(change.artifactId);
|
|
3196
|
+
}
|
|
3197
|
+
continue;
|
|
3198
|
+
}
|
|
3199
|
+
const stored = this.artifacts.get(change.artifactId);
|
|
3200
|
+
if (!stored) continue;
|
|
3201
|
+
stored.retention = "ephemeral";
|
|
3202
|
+
stored.persistenceWarning = "persistence_unavailable";
|
|
3203
|
+
stored.durableTombstoneRequired = stored.durableTombstoneRequired || stored.persistedAt !== void 0;
|
|
3204
|
+
delete stored.persistedAt;
|
|
3205
|
+
change.artifact = toPublicArtifact(stored);
|
|
3206
|
+
downgraded = true;
|
|
3207
|
+
}
|
|
3208
|
+
const warnings = [];
|
|
3209
|
+
if (downgraded) {
|
|
3210
|
+
warnings.push(
|
|
3211
|
+
"artifact persistence unavailable; durable artifacts kept ephemeral"
|
|
3212
|
+
);
|
|
3213
|
+
}
|
|
3214
|
+
if (removalNotPersisted) {
|
|
3215
|
+
warnings.push("artifact removal not persisted; live removal kept");
|
|
3216
|
+
}
|
|
3217
|
+
return warnings;
|
|
3218
|
+
}
|
|
3219
|
+
applyDurableMarkers(changes) {
|
|
3220
|
+
for (const change of changes) {
|
|
3221
|
+
if (change.action === "removed") {
|
|
3222
|
+
if (change.reason === "explicit") {
|
|
3223
|
+
this.rememberTombstone(change);
|
|
3224
|
+
this.stickyEphemeralIds.delete(change.artifactId);
|
|
3225
|
+
} else if (change.reason === "eviction") {
|
|
3226
|
+
this.stickyEphemeralIds.delete(change.artifactId);
|
|
3227
|
+
this.markerArtifacts.delete(change.artifactId);
|
|
3228
|
+
} else if (change.reason === "unpin_to_ephemeral") {
|
|
3229
|
+
this.rememberStickyEphemeral(change);
|
|
3230
|
+
}
|
|
3231
|
+
continue;
|
|
3232
|
+
}
|
|
3233
|
+
if (change.artifact && change.artifact.retention !== "ephemeral") {
|
|
3234
|
+
this.tombstonedIds.delete(change.artifactId);
|
|
3235
|
+
this.tombstonedClientIds.delete(change.artifactId);
|
|
3236
|
+
this.stickyEphemeralIds.delete(change.artifactId);
|
|
3237
|
+
this.markerArtifacts.delete(change.artifactId);
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
}
|
|
3241
|
+
rememberStickyEphemeral(change) {
|
|
3242
|
+
const artifactId = change.artifactId;
|
|
3243
|
+
this.stickyEphemeralIds.delete(artifactId);
|
|
3244
|
+
this.stickyEphemeralIds.add(artifactId);
|
|
3245
|
+
if (change.artifact) {
|
|
3246
|
+
this.markerArtifacts.set(
|
|
3247
|
+
artifactId,
|
|
3248
|
+
toPersistedArtifact(change.artifact, change.artifact.updatedAt)
|
|
3249
|
+
);
|
|
3250
|
+
}
|
|
3251
|
+
while (this.stickyEphemeralIds.size > MAX_STICKY_EPHEMERAL_IDS) {
|
|
3252
|
+
const oldest2 = this.stickyEphemeralIds.values().next().value;
|
|
3253
|
+
if (oldest2 === void 0) break;
|
|
3254
|
+
this.stickyEphemeralIds.delete(oldest2);
|
|
3255
|
+
this.markerArtifacts.delete(oldest2);
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
rememberTombstone(change) {
|
|
3259
|
+
this.tombstonedIds.delete(change.artifactId);
|
|
3260
|
+
this.tombstonedIds.add(change.artifactId);
|
|
3261
|
+
this.tombstonedClientIds.set(
|
|
3262
|
+
change.artifactId,
|
|
3263
|
+
change.removedClientId ?? this.artifacts.get(change.artifactId)?.clientId
|
|
3264
|
+
);
|
|
3265
|
+
if (change.artifact) {
|
|
3266
|
+
this.markerArtifacts.set(
|
|
3267
|
+
change.artifactId,
|
|
3268
|
+
toPersistedArtifact(change.artifact, change.artifact.updatedAt)
|
|
3269
|
+
);
|
|
3270
|
+
}
|
|
3271
|
+
while (this.tombstonedIds.size > MAX_TOMBSTONED_IDS) {
|
|
3272
|
+
const oldest2 = this.tombstonedIds.values().next().value;
|
|
3273
|
+
if (oldest2 === void 0) break;
|
|
3274
|
+
writeStderrLine(
|
|
3275
|
+
`[artifacts] session=${this.sessionId} action=tombstone_evicted artifactId=${JSON.stringify(
|
|
3276
|
+
oldest2
|
|
3277
|
+
)} limit=${MAX_TOMBSTONED_IDS}`
|
|
3278
|
+
);
|
|
3279
|
+
this.tombstonedIds.delete(oldest2);
|
|
3280
|
+
this.tombstonedClientIds.delete(oldest2);
|
|
3281
|
+
this.markerArtifacts.delete(oldest2);
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
setLastRestoreWarnings(warnings) {
|
|
3285
|
+
this.lastRestoreWarnings = [...warnings];
|
|
3286
|
+
this.lastRestoreWarningDetails = detailsForRestoreWarnings(warnings);
|
|
3287
|
+
}
|
|
3288
|
+
enqueue(operation) {
|
|
3289
|
+
const result = this.operationQueue.then(operation, operation);
|
|
3290
|
+
this.operationQueue = result.then(
|
|
3291
|
+
() => void 0,
|
|
3292
|
+
() => void 0
|
|
3293
|
+
);
|
|
3294
|
+
return result;
|
|
3295
|
+
}
|
|
3296
|
+
denyCrossClientMutation(action, artifactId, existing, options) {
|
|
3297
|
+
if (existing.source !== "client" || existing.clientId === void 0 || existing.clientId === options?.clientId) {
|
|
3298
|
+
return;
|
|
3299
|
+
}
|
|
3300
|
+
writeStderrLine(
|
|
3301
|
+
`[artifacts] session=${this.sessionId} action=${action}_denied artifactId=${artifactId} owner=${existing.clientId} requester=${options?.clientId ?? "<anonymous>"}`
|
|
3302
|
+
);
|
|
3303
|
+
throw new SessionArtifactAuthorizationError(
|
|
3304
|
+
this.sessionId,
|
|
3305
|
+
artifactId,
|
|
3306
|
+
existing.clientId,
|
|
3307
|
+
options?.clientId
|
|
3308
|
+
);
|
|
3309
|
+
}
|
|
3310
|
+
async normalizeInput(input, receivedSeq, trustedPublisherFromCaller, options = {}) {
|
|
3311
|
+
if (!input || typeof input !== "object") {
|
|
3312
|
+
throw new SessionArtifactValidationError("Artifact must be an object");
|
|
3313
|
+
}
|
|
3314
|
+
const title = normalizeString(input.title, "title", 200, true);
|
|
3315
|
+
const description = normalizeString(
|
|
3316
|
+
input.description,
|
|
3317
|
+
"description",
|
|
3318
|
+
1e3,
|
|
3319
|
+
false
|
|
3320
|
+
);
|
|
3321
|
+
const source = input.source ?? "tool";
|
|
3322
|
+
if (source !== "tool" && source !== "hook" && source !== "client") {
|
|
3323
|
+
throw new SessionArtifactValidationError(
|
|
3324
|
+
"source must be tool, hook, or client",
|
|
3325
|
+
"source"
|
|
3326
|
+
);
|
|
3327
|
+
}
|
|
3328
|
+
const trustedPublisher = trustedPublisherFromCaller;
|
|
3329
|
+
const workspacePath = input.workspacePath ? normalizeWorkspacePath(
|
|
3330
|
+
input.workspacePath,
|
|
3331
|
+
await this.getRealWorkspaceCwdForValidation()
|
|
3332
|
+
) : void 0;
|
|
3333
|
+
const managedId = normalizeManagedId(input.managedId);
|
|
3334
|
+
const rawStorage = input.storage;
|
|
3335
|
+
const storage = inferStorage(rawStorage, {
|
|
3336
|
+
workspacePath,
|
|
3337
|
+
managedId,
|
|
3338
|
+
url: input.url,
|
|
3339
|
+
trustedPublisher
|
|
3340
|
+
});
|
|
3341
|
+
const url = input.url ? normalizeArtifactUrl(input.url, trustedPublisher) : void 0;
|
|
3342
|
+
validateLocator(storage, {
|
|
3343
|
+
workspacePath,
|
|
3344
|
+
managedId,
|
|
3345
|
+
url,
|
|
3346
|
+
trustedPublisher
|
|
3347
|
+
});
|
|
3348
|
+
const retention = normalizeRetention(input.retention, {
|
|
3349
|
+
persistenceAvailable: this.persistence !== void 0
|
|
3350
|
+
});
|
|
3351
|
+
const workspaceStatus = workspacePath ? await this.getInitialWorkspaceStatus(
|
|
3352
|
+
workspacePath,
|
|
3353
|
+
options.workspaceExpected,
|
|
3354
|
+
{ hashContent: options.hashWorkspaceContent !== false }
|
|
3355
|
+
) : void 0;
|
|
3356
|
+
if (workspaceStatus?.escaped) {
|
|
3357
|
+
throw new SessionArtifactValidationError(
|
|
3358
|
+
"workspacePath must stay inside the workspace",
|
|
3359
|
+
"workspacePath"
|
|
3360
|
+
);
|
|
3361
|
+
}
|
|
3362
|
+
const metadata = withWorkspaceContentHashMetadata(
|
|
3363
|
+
normalizeMetadata(input.metadata, {
|
|
3364
|
+
budget: options.metadataBudget ?? "user"
|
|
3365
|
+
}),
|
|
3366
|
+
workspaceStatus
|
|
3367
|
+
);
|
|
3368
|
+
const kind = normalizeKind(
|
|
3369
|
+
input.kind ?? inferKind({ storage, workspacePath, url })
|
|
3370
|
+
);
|
|
3371
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3372
|
+
const identityKey = buildIdentityKey({
|
|
3373
|
+
storage,
|
|
3374
|
+
workspacePath,
|
|
3375
|
+
managedId,
|
|
3376
|
+
url
|
|
3377
|
+
});
|
|
3378
|
+
const id = stableSessionArtifactId(this.sessionId, identityKey);
|
|
2478
3379
|
return {
|
|
2479
3380
|
id,
|
|
2480
3381
|
identityKey,
|
|
2481
3382
|
receivedSeq,
|
|
3383
|
+
retentionExplicit: input.retention !== void 0,
|
|
2482
3384
|
retentionSource: source,
|
|
2483
3385
|
trustedPublisher,
|
|
2484
3386
|
kind,
|
|
@@ -2492,9 +3394,12 @@ var SessionArtifactStore = class {
|
|
|
2492
3394
|
managedId,
|
|
2493
3395
|
url,
|
|
2494
3396
|
mimeType: normalizeString(input.mimeType, "mimeType", 120, false),
|
|
2495
|
-
sizeBytes: input.sizeBytes !== void 0 ? normalizeSizeBytes(input.sizeBytes) :
|
|
3397
|
+
sizeBytes: workspaceStatus?.sizeBytes !== void 0 ? workspaceStatus.sizeBytes : input.sizeBytes !== void 0 ? normalizeSizeBytes(input.sizeBytes) : void 0,
|
|
2496
3398
|
metadata,
|
|
2497
|
-
|
|
3399
|
+
retention,
|
|
3400
|
+
restoreState: "live",
|
|
3401
|
+
...this.persistence === void 0 && retention !== "ephemeral" ? { persistenceWarning: "persistence_unavailable" } : {},
|
|
3402
|
+
clientRetained: source === "client" && (input.clientRetained !== void 0 ? input.clientRetained === true : true),
|
|
2498
3403
|
createdAt: now,
|
|
2499
3404
|
updatedAt: now,
|
|
2500
3405
|
toolCallId: normalizeString(input.toolCallId, "toolCallId", 200, false),
|
|
@@ -2508,6 +3413,16 @@ var SessionArtifactStore = class {
|
|
|
2508
3413
|
clientId: normalizeString(input.clientId, "clientId", 200, false)
|
|
2509
3414
|
};
|
|
2510
3415
|
}
|
|
3416
|
+
shouldSuppressTombstonedUpsert(artifact) {
|
|
3417
|
+
if (!this.tombstonedIds.has(artifact.id)) {
|
|
3418
|
+
return false;
|
|
3419
|
+
}
|
|
3420
|
+
const tombstonedClientId = this.tombstonedClientIds.get(artifact.id);
|
|
3421
|
+
if (artifact.source !== "client") {
|
|
3422
|
+
return false;
|
|
3423
|
+
}
|
|
3424
|
+
return !(artifact.retentionExplicit && artifact.clientId !== void 0 && artifact.clientId === tombstonedClientId);
|
|
3425
|
+
}
|
|
2511
3426
|
async refreshWorkspaceStatuses() {
|
|
2512
3427
|
const now = Date.now();
|
|
2513
3428
|
const staleWorkspaceArtifacts = Array.from(this.artifacts.values()).filter((artifact) => artifact.workspacePath).filter((artifact) => shouldRefreshWorkspaceStatus(artifact, now));
|
|
@@ -2520,12 +3435,24 @@ var SessionArtifactStore = class {
|
|
|
2520
3435
|
})
|
|
2521
3436
|
);
|
|
2522
3437
|
}
|
|
2523
|
-
|
|
3438
|
+
applyStickyEphemeralOverride(artifact) {
|
|
3439
|
+
if (!this.stickyEphemeralIds.has(artifact.id) || artifact.retentionExplicit || artifact.retention === "ephemeral") {
|
|
3440
|
+
return artifact;
|
|
3441
|
+
}
|
|
3442
|
+
return {
|
|
3443
|
+
...artifact,
|
|
3444
|
+
retention: "ephemeral",
|
|
3445
|
+
retentionExplicit: true,
|
|
3446
|
+
persistenceWarning: "sticky_override_active"
|
|
3447
|
+
};
|
|
3448
|
+
}
|
|
3449
|
+
async getInitialWorkspaceStatus(workspacePath, expected, options = { hashContent: true }) {
|
|
2524
3450
|
try {
|
|
2525
3451
|
return await getWorkspaceStatus(
|
|
2526
|
-
this.workspaceCwd,
|
|
2527
3452
|
workspacePath,
|
|
2528
|
-
this.getRealWorkspaceCwd()
|
|
3453
|
+
this.getRealWorkspaceCwd(),
|
|
3454
|
+
expected,
|
|
3455
|
+
options
|
|
2529
3456
|
);
|
|
2530
3457
|
} catch (error) {
|
|
2531
3458
|
const reason = error instanceof Error ? error.message : String(error);
|
|
@@ -2541,16 +3468,23 @@ var SessionArtifactStore = class {
|
|
|
2541
3468
|
}
|
|
2542
3469
|
try {
|
|
2543
3470
|
const status = await getWorkspaceStatus(
|
|
2544
|
-
this.workspaceCwd,
|
|
2545
3471
|
artifact.workspacePath,
|
|
2546
|
-
this.getRealWorkspaceCwd()
|
|
3472
|
+
this.getRealWorkspaceCwd(),
|
|
3473
|
+
{
|
|
3474
|
+
sizeBytes: artifact.sizeBytes,
|
|
3475
|
+
mtimeMs: artifact.metadata?.[WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY],
|
|
3476
|
+
sha256: artifact.metadata?.[WORKSPACE_CONTENT_SHA256_METADATA_KEY]
|
|
3477
|
+
}
|
|
2547
3478
|
);
|
|
2548
|
-
|
|
2549
|
-
artifact.
|
|
3479
|
+
const changed = isWorkspaceContentChanged(artifact, status);
|
|
3480
|
+
artifact.status = changed ? "changed" : status.status;
|
|
3481
|
+
if (!changed) {
|
|
3482
|
+
artifact.sizeBytes = status.sizeBytes;
|
|
3483
|
+
}
|
|
2550
3484
|
if (status.escaped) {
|
|
2551
3485
|
artifact.status = "missing";
|
|
2552
3486
|
artifact.sizeBytes = void 0;
|
|
2553
|
-
|
|
3487
|
+
artifact.hideWorkspacePath = true;
|
|
2554
3488
|
}
|
|
2555
3489
|
artifact.lastStatAt = options.now ?? Date.now();
|
|
2556
3490
|
} catch (error) {
|
|
@@ -2579,7 +3513,18 @@ var SessionArtifactStore = class {
|
|
|
2579
3513
|
}
|
|
2580
3514
|
return this.realWorkspaceCwdPromise;
|
|
2581
3515
|
}
|
|
2582
|
-
async
|
|
3516
|
+
async getRealWorkspaceCwdForValidation() {
|
|
3517
|
+
try {
|
|
3518
|
+
return await this.getRealWorkspaceCwd();
|
|
3519
|
+
} catch (error) {
|
|
3520
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
3521
|
+
throw new SessionArtifactValidationError(
|
|
3522
|
+
`workspacePath could not be inspected: ${reason}`,
|
|
3523
|
+
"workspacePath"
|
|
3524
|
+
);
|
|
3525
|
+
}
|
|
3526
|
+
}
|
|
3527
|
+
async evictOverflow(createdIds, changes, strict = false) {
|
|
2583
3528
|
const removed = [];
|
|
2584
3529
|
if (this.artifacts.size <= this.maxArtifacts) {
|
|
2585
3530
|
return removed;
|
|
@@ -2607,10 +3552,17 @@ var SessionArtifactStore = class {
|
|
|
2607
3552
|
action: "removed",
|
|
2608
3553
|
artifactId: artifact.id,
|
|
2609
3554
|
artifact: toPublicArtifact(artifact),
|
|
3555
|
+
durableTombstoneRequired: artifact.durableTombstoneRequired,
|
|
2610
3556
|
reason: "eviction"
|
|
2611
3557
|
});
|
|
2612
3558
|
}
|
|
2613
3559
|
const overflowCreated = Array.from(this.artifacts.values()).filter((artifact) => createdInThisBatch.has(artifact.id)).sort((a, b) => b.receivedSeq - a.receivedSeq);
|
|
3560
|
+
if (strict && overflowCreated.length > 0 && this.artifacts.size > this.maxArtifacts) {
|
|
3561
|
+
throw new SessionArtifactValidationError(
|
|
3562
|
+
"artifact store is full; no eviction candidate is available",
|
|
3563
|
+
"artifactId"
|
|
3564
|
+
);
|
|
3565
|
+
}
|
|
2614
3566
|
for (const artifact of overflowCreated) {
|
|
2615
3567
|
if (this.artifacts.size <= this.maxArtifacts) {
|
|
2616
3568
|
break;
|
|
@@ -2659,7 +3611,12 @@ function mergeBatchArtifact(existing, next) {
|
|
|
2659
3611
|
trustedPublisher: true,
|
|
2660
3612
|
createdAt: existing.createdAt,
|
|
2661
3613
|
receivedSeq: existing.receivedSeq,
|
|
3614
|
+
retentionExplicit: existing.retentionExplicit || next.retentionExplicit,
|
|
2662
3615
|
retentionSource: existing.retentionSource,
|
|
3616
|
+
retention: mergeRetention(existing, next),
|
|
3617
|
+
restoreState: "live",
|
|
3618
|
+
persistenceWarning: existing.persistenceWarning ?? next.persistenceWarning,
|
|
3619
|
+
persistedAt: existing.persistedAt ?? next.persistedAt,
|
|
2663
3620
|
clientRetained: existing.clientRetained || next.clientRetained,
|
|
2664
3621
|
lastStatAt: void 0
|
|
2665
3622
|
};
|
|
@@ -2673,6 +3630,8 @@ function mergeBatchArtifact(existing, next) {
|
|
|
2673
3630
|
metadata: mergeMetadata(existing, next),
|
|
2674
3631
|
clientRetained: existing.clientRetained || next.clientRetained,
|
|
2675
3632
|
trustedPublisher: existing.trustedPublisher || next.trustedPublisher,
|
|
3633
|
+
retentionExplicit: existing.retentionExplicit || next.retentionExplicit,
|
|
3634
|
+
retention: mergeRetention(existing, next),
|
|
2676
3635
|
lastStatAt: next.lastStatAt ?? existing.lastStatAt
|
|
2677
3636
|
};
|
|
2678
3637
|
}
|
|
@@ -2692,9 +3651,14 @@ function mergeArtifact(existing, incoming) {
|
|
|
2692
3651
|
managedId: publishedUpdate ? incoming.managedId ?? existing.managedId : existing.managedId,
|
|
2693
3652
|
url: publishedUpdate ? incoming.url ?? existing.url : existing.url,
|
|
2694
3653
|
workspacePath: publishedUpdate || existing.storage === "published" ? void 0 : existing.workspacePath ?? incoming.workspacePath,
|
|
3654
|
+
hideWorkspacePath: publishedUpdate || existing.storage === "published" || incoming.workspacePath ? void 0 : existing.hideWorkspacePath,
|
|
2695
3655
|
mimeType: publishedUpdate ? incoming.mimeType ?? existing.mimeType : existing.mimeType,
|
|
2696
3656
|
sizeBytes: existing.storage === "published" && !publishedUpdate ? existing.sizeBytes : mergeSizeBytes(existing, incoming),
|
|
2697
3657
|
metadata: existing.storage === "published" && !publishedUpdate ? existing.metadata : mergeMetadata(existing, incoming),
|
|
3658
|
+
retention: mergeRetention(existing, incoming),
|
|
3659
|
+
restoreState: "live",
|
|
3660
|
+
persistenceWarning: incoming.retentionExplicit && incoming.retention !== "ephemeral" ? incoming.persistenceWarning : existing.persistenceWarning ?? incoming.persistenceWarning,
|
|
3661
|
+
persistedAt: incoming.retentionExplicit && incoming.retention === "ephemeral" ? void 0 : existing.persistedAt ?? incoming.persistedAt,
|
|
2698
3662
|
source: existing.source,
|
|
2699
3663
|
retentionSource: existing.retentionSource,
|
|
2700
3664
|
trustedPublisher: existing.trustedPublisher || incoming.trustedPublisher,
|
|
@@ -2706,6 +3670,7 @@ function mergeArtifact(existing, incoming) {
|
|
|
2706
3670
|
next.title = incoming.title;
|
|
2707
3671
|
next.description = incoming.description;
|
|
2708
3672
|
delete next.workspacePath;
|
|
3673
|
+
delete next.hideWorkspacePath;
|
|
2709
3674
|
}
|
|
2710
3675
|
const changed = !publicArtifactsEqual(
|
|
2711
3676
|
toPublicArtifact(existing),
|
|
@@ -2717,8 +3682,12 @@ function mergeArtifact(existing, incoming) {
|
|
|
2717
3682
|
return { artifact: changed ? next : existing, changed };
|
|
2718
3683
|
}
|
|
2719
3684
|
__name(mergeArtifact, "mergeArtifact");
|
|
3685
|
+
function shouldRecordEphemeralUnpin(existing, incoming) {
|
|
3686
|
+
return incoming.retentionExplicit && incoming.retention === "ephemeral" && (existing.retention !== "ephemeral" || existing.persistedAt !== void 0 || existing.durableTombstoneRequired === true);
|
|
3687
|
+
}
|
|
3688
|
+
__name(shouldRecordEphemeralUnpin, "shouldRecordEphemeralUnpin");
|
|
2720
3689
|
function publicArtifactsEqual(a, b) {
|
|
2721
|
-
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;
|
|
3690
|
+
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;
|
|
2722
3691
|
}
|
|
2723
3692
|
__name(publicArtifactsEqual, "publicArtifactsEqual");
|
|
2724
3693
|
function metadataEqual(a, b) {
|
|
@@ -2740,6 +3709,24 @@ function mergeSizeBytes(existing, incoming) {
|
|
|
2740
3709
|
return existing.sizeBytes;
|
|
2741
3710
|
}
|
|
2742
3711
|
__name(mergeSizeBytes, "mergeSizeBytes");
|
|
3712
|
+
function strongestRetention(a, b) {
|
|
3713
|
+
const rank = {
|
|
3714
|
+
ephemeral: 0,
|
|
3715
|
+
restorable: 1
|
|
3716
|
+
};
|
|
3717
|
+
return rank[b] > rank[a] ? b : a;
|
|
3718
|
+
}
|
|
3719
|
+
__name(strongestRetention, "strongestRetention");
|
|
3720
|
+
function mergeRetention(existing, incoming) {
|
|
3721
|
+
if (incoming.retentionExplicit && incoming.retention === "ephemeral") {
|
|
3722
|
+
return "ephemeral";
|
|
3723
|
+
}
|
|
3724
|
+
if (existing.retentionExplicit && existing.retention === "ephemeral" && !incoming.retentionExplicit) {
|
|
3725
|
+
return "ephemeral";
|
|
3726
|
+
}
|
|
3727
|
+
return strongestRetention(existing.retention, incoming.retention);
|
|
3728
|
+
}
|
|
3729
|
+
__name(mergeRetention, "mergeRetention");
|
|
2743
3730
|
function mergeMetadata(existing, incoming) {
|
|
2744
3731
|
if (!incoming.metadata || incoming.source === "hook" || incoming.source !== existing.source) {
|
|
2745
3732
|
return existing.metadata;
|
|
@@ -2747,7 +3734,10 @@ function mergeMetadata(existing, incoming) {
|
|
|
2747
3734
|
const merged = { ...existing.metadata ?? {} };
|
|
2748
3735
|
let changed = false;
|
|
2749
3736
|
for (const [key, value] of Object.entries(incoming.metadata)) {
|
|
2750
|
-
if (
|
|
3737
|
+
if ((key === WORKSPACE_CONTENT_SHA256_METADATA_KEY || key === WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY) && merged[key] !== value) {
|
|
3738
|
+
merged[key] = value;
|
|
3739
|
+
changed = true;
|
|
3740
|
+
} else if (!Object.hasOwn(merged, key)) {
|
|
2751
3741
|
merged[key] = value;
|
|
2752
3742
|
changed = true;
|
|
2753
3743
|
}
|
|
@@ -2765,7 +3755,7 @@ function mergeMetadata(existing, incoming) {
|
|
|
2765
3755
|
}
|
|
2766
3756
|
__name(mergeMetadata, "mergeMetadata");
|
|
2767
3757
|
function isMetadataWithinLimit(metadata) {
|
|
2768
|
-
return
|
|
3758
|
+
return metadataBudgetBytes(metadata, "persisted") <= 4096;
|
|
2769
3759
|
}
|
|
2770
3760
|
__name(isMetadataWithinLimit, "isMetadataWithinLimit");
|
|
2771
3761
|
function countByRetentionSource(artifacts) {
|
|
@@ -2821,6 +3811,7 @@ function removePriorChange(changes, artifactId) {
|
|
|
2821
3811
|
}
|
|
2822
3812
|
__name(removePriorChange, "removePriorChange");
|
|
2823
3813
|
function toPublicArtifact(artifact) {
|
|
3814
|
+
const hideWorkspacePath = "hideWorkspacePath" in artifact && artifact.hideWorkspacePath === true;
|
|
2824
3815
|
const {
|
|
2825
3816
|
id,
|
|
2826
3817
|
kind,
|
|
@@ -2835,13 +3826,16 @@ function toPublicArtifact(artifact) {
|
|
|
2835
3826
|
mimeType,
|
|
2836
3827
|
sizeBytes,
|
|
2837
3828
|
metadata,
|
|
3829
|
+
retention,
|
|
3830
|
+
restoreState,
|
|
3831
|
+
persistenceWarning,
|
|
3832
|
+
persistedAt,
|
|
2838
3833
|
clientRetained,
|
|
2839
3834
|
createdAt,
|
|
2840
3835
|
updatedAt,
|
|
2841
3836
|
toolCallId,
|
|
2842
3837
|
toolName,
|
|
2843
|
-
hookEventName
|
|
2844
|
-
clientId
|
|
3838
|
+
hookEventName
|
|
2845
3839
|
} = artifact;
|
|
2846
3840
|
return {
|
|
2847
3841
|
id,
|
|
@@ -2851,22 +3845,205 @@ function toPublicArtifact(artifact) {
|
|
|
2851
3845
|
status,
|
|
2852
3846
|
title,
|
|
2853
3847
|
...description ? { description } : {},
|
|
2854
|
-
...workspacePath ? { workspacePath } : {},
|
|
3848
|
+
...workspacePath && !hideWorkspacePath ? { workspacePath } : {},
|
|
2855
3849
|
...managedId ? { managedId } : {},
|
|
2856
3850
|
...url ? { url } : {},
|
|
2857
3851
|
...mimeType ? { mimeType } : {},
|
|
2858
3852
|
...sizeBytes !== void 0 ? { sizeBytes } : {},
|
|
2859
3853
|
...metadata ? { metadata } : {},
|
|
3854
|
+
retention,
|
|
3855
|
+
...restoreState ? { restoreState } : {},
|
|
3856
|
+
...persistenceWarning ? { persistenceWarning } : {},
|
|
3857
|
+
...persistedAt ? { persistedAt } : {},
|
|
2860
3858
|
clientRetained,
|
|
2861
3859
|
createdAt,
|
|
2862
3860
|
updatedAt,
|
|
2863
3861
|
...toolCallId ? { toolCallId } : {},
|
|
2864
3862
|
...toolName ? { toolName } : {},
|
|
2865
|
-
...hookEventName ? { hookEventName } : {}
|
|
2866
|
-
...clientId ? { clientId } : {}
|
|
3863
|
+
...hookEventName ? { hookEventName } : {}
|
|
2867
3864
|
};
|
|
2868
3865
|
}
|
|
2869
3866
|
__name(toPublicArtifact, "toPublicArtifact");
|
|
3867
|
+
function persistedArtifactToInput(artifact) {
|
|
3868
|
+
return {
|
|
3869
|
+
title: artifact.title,
|
|
3870
|
+
kind: artifact.kind,
|
|
3871
|
+
storage: artifact.storage,
|
|
3872
|
+
description: artifact.description,
|
|
3873
|
+
workspacePath: artifact.workspacePath,
|
|
3874
|
+
managedId: artifact.managedId,
|
|
3875
|
+
url: artifact.url,
|
|
3876
|
+
mimeType: artifact.mimeType,
|
|
3877
|
+
sizeBytes: artifact.sizeBytes,
|
|
3878
|
+
metadata: artifact.metadata,
|
|
3879
|
+
source: artifact.source,
|
|
3880
|
+
retention: artifact.retention,
|
|
3881
|
+
clientRetained: artifact.clientRetained,
|
|
3882
|
+
toolCallId: artifact.toolCallId,
|
|
3883
|
+
toolName: artifact.toolName,
|
|
3884
|
+
hookEventName: artifact.hookEventName
|
|
3885
|
+
};
|
|
3886
|
+
}
|
|
3887
|
+
__name(persistedArtifactToInput, "persistedArtifactToInput");
|
|
3888
|
+
function workspaceExpectedFromArtifact(artifact) {
|
|
3889
|
+
if (!artifact.workspacePath) {
|
|
3890
|
+
return void 0;
|
|
3891
|
+
}
|
|
3892
|
+
return {
|
|
3893
|
+
sizeBytes: artifact.sizeBytes,
|
|
3894
|
+
mtimeMs: artifact.metadata?.[WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY],
|
|
3895
|
+
sha256: artifact.metadata?.[WORKSPACE_CONTENT_SHA256_METADATA_KEY]
|
|
3896
|
+
};
|
|
3897
|
+
}
|
|
3898
|
+
__name(workspaceExpectedFromArtifact, "workspaceExpectedFromArtifact");
|
|
3899
|
+
function toPersistedChange(change, recordedAt) {
|
|
3900
|
+
return {
|
|
3901
|
+
action: change.action,
|
|
3902
|
+
artifactId: change.artifactId,
|
|
3903
|
+
...change.artifact ? { artifact: toPersistedArtifact(change.artifact, recordedAt) } : {},
|
|
3904
|
+
...change.reason ? { reason: change.reason } : {}
|
|
3905
|
+
};
|
|
3906
|
+
}
|
|
3907
|
+
__name(toPersistedChange, "toPersistedChange");
|
|
3908
|
+
function toPersistedArtifact(artifact, recordedAt) {
|
|
3909
|
+
return {
|
|
3910
|
+
id: artifact.id,
|
|
3911
|
+
kind: artifact.kind,
|
|
3912
|
+
storage: artifact.storage,
|
|
3913
|
+
source: artifact.source,
|
|
3914
|
+
status: artifact.status === "changed" ? "available" : artifact.status,
|
|
3915
|
+
title: artifact.title,
|
|
3916
|
+
...artifact.description ? { description: artifact.description } : {},
|
|
3917
|
+
...artifact.workspacePath ? { workspacePath: artifact.workspacePath } : {},
|
|
3918
|
+
...artifact.managedId ? { managedId: artifact.managedId } : {},
|
|
3919
|
+
...artifact.url ? { url: artifact.url } : {},
|
|
3920
|
+
...artifact.mimeType ? { mimeType: artifact.mimeType } : {},
|
|
3921
|
+
...artifact.sizeBytes !== void 0 ? { sizeBytes: artifact.sizeBytes } : {},
|
|
3922
|
+
...artifact.metadata ? { metadata: artifact.metadata } : {},
|
|
3923
|
+
retention: artifact.retention,
|
|
3924
|
+
clientRetained: artifact.clientRetained,
|
|
3925
|
+
createdAt: artifact.createdAt,
|
|
3926
|
+
updatedAt: artifact.updatedAt,
|
|
3927
|
+
persistedAt: artifact.persistedAt ?? recordedAt,
|
|
3928
|
+
...artifact.toolCallId ? { toolCallId: artifact.toolCallId } : {},
|
|
3929
|
+
...artifact.toolName ? { toolName: artifact.toolName } : {},
|
|
3930
|
+
...artifact.hookEventName ? { hookEventName: artifact.hookEventName } : {}
|
|
3931
|
+
};
|
|
3932
|
+
}
|
|
3933
|
+
__name(toPersistedArtifact, "toPersistedArtifact");
|
|
3934
|
+
function isFileArtifactUrl(raw) {
|
|
3935
|
+
if (typeof raw !== "string") return false;
|
|
3936
|
+
try {
|
|
3937
|
+
return new URL(raw).protocol === "file:";
|
|
3938
|
+
} catch {
|
|
3939
|
+
return false;
|
|
3940
|
+
}
|
|
3941
|
+
}
|
|
3942
|
+
__name(isFileArtifactUrl, "isFileArtifactUrl");
|
|
3943
|
+
function isArtifactSnapshotCompletenessWarning(warning) {
|
|
3944
|
+
if (warning.startsWith("skipped stale event sequence ")) return false;
|
|
3945
|
+
return warning.startsWith("skipped ") || warning.includes(" list truncated to ");
|
|
3946
|
+
}
|
|
3947
|
+
__name(isArtifactSnapshotCompletenessWarning, "isArtifactSnapshotCompletenessWarning");
|
|
3948
|
+
function shouldCommitBeforeDurablePersistence(change) {
|
|
3949
|
+
return change.action === "removed" && change.durableTombstoneRequired === true;
|
|
3950
|
+
}
|
|
3951
|
+
__name(shouldCommitBeforeDurablePersistence, "shouldCommitBeforeDurablePersistence");
|
|
3952
|
+
function isDurablePersistenceChange(change) {
|
|
3953
|
+
if (change.action === "removed" && change.durableTombstoneRequired === true) {
|
|
3954
|
+
return true;
|
|
3955
|
+
}
|
|
3956
|
+
if (!change.artifact) return false;
|
|
3957
|
+
return change.artifact.retention !== "ephemeral" || change.artifact.persistenceWarning === "persistence_unavailable";
|
|
3958
|
+
}
|
|
3959
|
+
__name(isDurablePersistenceChange, "isDurablePersistenceChange");
|
|
3960
|
+
function stripDurableTombstoneMarkers(changes) {
|
|
3961
|
+
for (const change of changes) {
|
|
3962
|
+
delete change.durableTombstoneRequired;
|
|
3963
|
+
delete change.removedClientId;
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3966
|
+
__name(stripDurableTombstoneMarkers, "stripDurableTombstoneMarkers");
|
|
3967
|
+
function detailsForRestoreWarnings(warnings) {
|
|
3968
|
+
return warnings.map((warning) => {
|
|
3969
|
+
if (warning.startsWith(RESTORE_FAILED_WARNING_PREFIX)) {
|
|
3970
|
+
return {
|
|
3971
|
+
code: "ARTIFACT_RESTORE_FAILED",
|
|
3972
|
+
operation: "restore",
|
|
3973
|
+
durability: "unavailable",
|
|
3974
|
+
retryable: true,
|
|
3975
|
+
message: warning
|
|
3976
|
+
};
|
|
3977
|
+
}
|
|
3978
|
+
if (warning.startsWith(RESTORE_PARTIAL_FAILED_WARNING_PREFIX)) {
|
|
3979
|
+
return {
|
|
3980
|
+
code: "ARTIFACT_RESTORE_PARTIAL_FAILED",
|
|
3981
|
+
operation: "restore",
|
|
3982
|
+
durability: "live_only",
|
|
3983
|
+
retryable: true,
|
|
3984
|
+
message: warning
|
|
3985
|
+
};
|
|
3986
|
+
}
|
|
3987
|
+
return {
|
|
3988
|
+
code: "ARTIFACT_WARNING",
|
|
3989
|
+
operation: "restore",
|
|
3990
|
+
message: warning
|
|
3991
|
+
};
|
|
3992
|
+
});
|
|
3993
|
+
}
|
|
3994
|
+
__name(detailsForRestoreWarnings, "detailsForRestoreWarnings");
|
|
3995
|
+
function detailsForPersistenceWarnings(warnings, changes, operation) {
|
|
3996
|
+
if (warnings.length === 0) return [];
|
|
3997
|
+
const artifactIds = changes.filter(isDurablePersistenceChange).map((change) => change.artifactId);
|
|
3998
|
+
return warnings.map((warning) => {
|
|
3999
|
+
if (warning === "artifact persistence unavailable; durable artifacts kept ephemeral") {
|
|
4000
|
+
return {
|
|
4001
|
+
code: "ARTIFACT_PERSISTENCE_UNAVAILABLE",
|
|
4002
|
+
operation,
|
|
4003
|
+
artifactIds,
|
|
4004
|
+
durability: "live_only",
|
|
4005
|
+
retryable: false,
|
|
4006
|
+
message: warning
|
|
4007
|
+
};
|
|
4008
|
+
}
|
|
4009
|
+
if (warning === "artifact removal not persisted; live removal kept") {
|
|
4010
|
+
return {
|
|
4011
|
+
code: "ARTIFACT_REMOVAL_NOT_PERSISTED",
|
|
4012
|
+
operation,
|
|
4013
|
+
artifactIds,
|
|
4014
|
+
durability: "live_only",
|
|
4015
|
+
retryable: true,
|
|
4016
|
+
message: warning
|
|
4017
|
+
};
|
|
4018
|
+
}
|
|
4019
|
+
return {
|
|
4020
|
+
code: "ARTIFACT_WARNING",
|
|
4021
|
+
operation,
|
|
4022
|
+
artifactIds,
|
|
4023
|
+
message: warning
|
|
4024
|
+
};
|
|
4025
|
+
});
|
|
4026
|
+
}
|
|
4027
|
+
__name(detailsForPersistenceWarnings, "detailsForPersistenceWarnings");
|
|
4028
|
+
function persistenceFailureDetail(options) {
|
|
4029
|
+
const unavailable = options.error instanceof SessionArtifactValidationError && options.error.message === "artifact persistence is unavailable";
|
|
4030
|
+
return {
|
|
4031
|
+
code: unavailable ? "ARTIFACT_PERSISTENCE_UNAVAILABLE" : "ARTIFACT_PERSISTENCE_WRITE_FAILED",
|
|
4032
|
+
operation: options.operation,
|
|
4033
|
+
artifactIds: options.artifactIds,
|
|
4034
|
+
durability: "unavailable",
|
|
4035
|
+
retryable: !unavailable,
|
|
4036
|
+
message: options.message
|
|
4037
|
+
};
|
|
4038
|
+
}
|
|
4039
|
+
__name(persistenceFailureDetail, "persistenceFailureDetail");
|
|
4040
|
+
function cloneStoredArtifact(artifact) {
|
|
4041
|
+
return {
|
|
4042
|
+
...artifact,
|
|
4043
|
+
...artifact.metadata ? { metadata: { ...artifact.metadata } } : {}
|
|
4044
|
+
};
|
|
4045
|
+
}
|
|
4046
|
+
__name(cloneStoredArtifact, "cloneStoredArtifact");
|
|
2870
4047
|
function inferStorage(requested, locators) {
|
|
2871
4048
|
if (requested !== void 0 && typeof requested !== "string") {
|
|
2872
4049
|
throw new SessionArtifactValidationError(
|
|
@@ -2960,10 +4137,6 @@ function buildIdentityKey(input) {
|
|
|
2960
4137
|
);
|
|
2961
4138
|
}
|
|
2962
4139
|
__name(buildIdentityKey, "buildIdentityKey");
|
|
2963
|
-
function stableArtifactId(sessionId, identityKey) {
|
|
2964
|
-
return createHash("sha256").update(`${sessionId}:${identityKey}`).digest("hex").slice(0, 16);
|
|
2965
|
-
}
|
|
2966
|
-
__name(stableArtifactId, "stableArtifactId");
|
|
2967
4140
|
function managedIdForWorkspacePath(workspaceCwd, workspacePath) {
|
|
2968
4141
|
return createHash("sha1").update(path2.resolve(workspaceCwd, workspacePath)).digest("hex").slice(0, 16);
|
|
2969
4142
|
}
|
|
@@ -3077,6 +4250,12 @@ function normalizeArtifactUrl(raw, allowFile) {
|
|
|
3077
4250
|
"url"
|
|
3078
4251
|
);
|
|
3079
4252
|
}
|
|
4253
|
+
if (hasSecretLikeUrlComponent(parsed)) {
|
|
4254
|
+
throw new SessionArtifactValidationError(
|
|
4255
|
+
"url must not include secret-like components",
|
|
4256
|
+
"url"
|
|
4257
|
+
);
|
|
4258
|
+
}
|
|
3080
4259
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" && !(allowFile && parsed.protocol === "file:")) {
|
|
3081
4260
|
throw new SessionArtifactValidationError(
|
|
3082
4261
|
allowFile ? "url must use http, https, or file" : "url must use http or https",
|
|
@@ -3086,7 +4265,64 @@ function normalizeArtifactUrl(raw, allowFile) {
|
|
|
3086
4265
|
return parsed.href;
|
|
3087
4266
|
}
|
|
3088
4267
|
__name(normalizeArtifactUrl, "normalizeArtifactUrl");
|
|
3089
|
-
function
|
|
4268
|
+
function hasSecretLikeUrlComponent(parsed) {
|
|
4269
|
+
for (const [key, value] of parsed.searchParams) {
|
|
4270
|
+
if (isSecretLikeUrlText(key) || isSecretLikeUrlValue(value)) {
|
|
4271
|
+
return true;
|
|
4272
|
+
}
|
|
4273
|
+
}
|
|
4274
|
+
for (const segment of parsed.pathname.split("/").filter(Boolean)) {
|
|
4275
|
+
let decodedSegment = segment;
|
|
4276
|
+
try {
|
|
4277
|
+
decodedSegment = decodeURIComponent(segment);
|
|
4278
|
+
} catch {
|
|
4279
|
+
}
|
|
4280
|
+
if (isSecretLikeUrlValue(decodedSegment)) {
|
|
4281
|
+
return true;
|
|
4282
|
+
}
|
|
4283
|
+
}
|
|
4284
|
+
const fragment = parsed.hash.slice(1);
|
|
4285
|
+
return hasSecretLikeUrlFragment(fragment);
|
|
4286
|
+
}
|
|
4287
|
+
__name(hasSecretLikeUrlComponent, "hasSecretLikeUrlComponent");
|
|
4288
|
+
function hasSecretLikeUrlFragment(fragment) {
|
|
4289
|
+
if (!fragment) return false;
|
|
4290
|
+
const candidates = /* @__PURE__ */ new Set([fragment]);
|
|
4291
|
+
try {
|
|
4292
|
+
candidates.add(decodeURIComponent(fragment));
|
|
4293
|
+
} catch {
|
|
4294
|
+
}
|
|
4295
|
+
for (const candidate of candidates) {
|
|
4296
|
+
if (isSecretLikeUrlText(candidate) || isSecretLikeUrlValue(candidate)) {
|
|
4297
|
+
return true;
|
|
4298
|
+
}
|
|
4299
|
+
for (const [key, value] of new URLSearchParams(candidate)) {
|
|
4300
|
+
if (isSecretLikeUrlText(key) || isSecretLikeUrlValue(value)) {
|
|
4301
|
+
return true;
|
|
4302
|
+
}
|
|
4303
|
+
}
|
|
4304
|
+
}
|
|
4305
|
+
return false;
|
|
4306
|
+
}
|
|
4307
|
+
__name(hasSecretLikeUrlFragment, "hasSecretLikeUrlFragment");
|
|
4308
|
+
function isSecretLikeUrlText(value) {
|
|
4309
|
+
const normalized = value.replace(/([a-z])([A-Z])/g, "$1-$2");
|
|
4310
|
+
return /(?:^|[-_.])(token|secret|password|passwd|pwd|cookie|authorization|credential|signature|sig|api[-_]?key|access[-_]?key)(?:$|[-_.=&#])/i.test(
|
|
4311
|
+
normalized
|
|
4312
|
+
);
|
|
4313
|
+
}
|
|
4314
|
+
__name(isSecretLikeUrlText, "isSecretLikeUrlText");
|
|
4315
|
+
function isSecretLikeUrlValue(value) {
|
|
4316
|
+
return SECRET_TOKEN_VALUE_PATTERN.test(value.trim());
|
|
4317
|
+
}
|
|
4318
|
+
__name(isSecretLikeUrlValue, "isSecretLikeUrlValue");
|
|
4319
|
+
function isSecretLikeMetadataValue(value) {
|
|
4320
|
+
return /^(?:bearer\s+\S{8,}|sk-[A-Za-z0-9_-]{12,}|(?:gh[pousr]|github_pat)_[A-Za-z0-9_/-]{12,})$/i.test(
|
|
4321
|
+
value.trim()
|
|
4322
|
+
);
|
|
4323
|
+
}
|
|
4324
|
+
__name(isSecretLikeMetadataValue, "isSecretLikeMetadataValue");
|
|
4325
|
+
function normalizeMetadata(metadata, options = {}) {
|
|
3090
4326
|
if (metadata === void 0) {
|
|
3091
4327
|
return void 0;
|
|
3092
4328
|
}
|
|
@@ -3098,6 +4334,12 @@ function normalizeMetadata(metadata) {
|
|
|
3098
4334
|
}
|
|
3099
4335
|
const normalized = {};
|
|
3100
4336
|
for (const [key, value] of Object.entries(metadata)) {
|
|
4337
|
+
if (isPrototypeMetadataKey(key)) {
|
|
4338
|
+
continue;
|
|
4339
|
+
}
|
|
4340
|
+
if (options.budget !== "persisted" && isReservedWorkspaceMetadataKey(key)) {
|
|
4341
|
+
continue;
|
|
4342
|
+
}
|
|
3101
4343
|
if (!key) {
|
|
3102
4344
|
throw new SessionArtifactValidationError(
|
|
3103
4345
|
"metadata keys must not be empty",
|
|
@@ -3116,6 +4358,12 @@ function normalizeMetadata(metadata) {
|
|
|
3116
4358
|
"metadata"
|
|
3117
4359
|
);
|
|
3118
4360
|
}
|
|
4361
|
+
if (isSecretLikeUrlText(key)) {
|
|
4362
|
+
throw new SessionArtifactValidationError(
|
|
4363
|
+
"metadata keys must not contain secret-like names",
|
|
4364
|
+
"metadata"
|
|
4365
|
+
);
|
|
4366
|
+
}
|
|
3119
4367
|
if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
|
|
3120
4368
|
throw new SessionArtifactValidationError(
|
|
3121
4369
|
"metadata values must be primitive",
|
|
@@ -3134,12 +4382,18 @@ function normalizeMetadata(metadata) {
|
|
|
3134
4382
|
"metadata"
|
|
3135
4383
|
);
|
|
3136
4384
|
}
|
|
4385
|
+
if (typeof value === "string" && !isReservedWorkspaceMetadataKey(key) && isSecretLikeMetadataValue(value)) {
|
|
4386
|
+
throw new SessionArtifactValidationError(
|
|
4387
|
+
"metadata string values must not contain secret-like tokens",
|
|
4388
|
+
"metadata"
|
|
4389
|
+
);
|
|
4390
|
+
}
|
|
3137
4391
|
normalized[key] = value;
|
|
3138
4392
|
}
|
|
3139
4393
|
if (Object.keys(normalized).length === 0) {
|
|
3140
4394
|
return void 0;
|
|
3141
4395
|
}
|
|
3142
|
-
if (
|
|
4396
|
+
if (metadataBudgetBytes(normalized, options.budget ?? "user") > 4096) {
|
|
3143
4397
|
throw new SessionArtifactValidationError(
|
|
3144
4398
|
"metadata must be 4096 bytes or fewer",
|
|
3145
4399
|
"metadata"
|
|
@@ -3148,6 +4402,25 @@ function normalizeMetadata(metadata) {
|
|
|
3148
4402
|
return normalized;
|
|
3149
4403
|
}
|
|
3150
4404
|
__name(normalizeMetadata, "normalizeMetadata");
|
|
4405
|
+
function normalizeRetention(value, options) {
|
|
4406
|
+
if (value === void 0) {
|
|
4407
|
+
return options.persistenceAvailable ? "restorable" : "ephemeral";
|
|
4408
|
+
}
|
|
4409
|
+
if (value === "ephemeral" || value === "restorable") {
|
|
4410
|
+
return value;
|
|
4411
|
+
}
|
|
4412
|
+
if (value === "pinned") {
|
|
4413
|
+
throw new SessionArtifactValidationError(
|
|
4414
|
+
"pinned retention is not supported by session_artifacts_persistence",
|
|
4415
|
+
"retention"
|
|
4416
|
+
);
|
|
4417
|
+
}
|
|
4418
|
+
throw new SessionArtifactValidationError(
|
|
4419
|
+
"retention must be ephemeral or restorable",
|
|
4420
|
+
"retention"
|
|
4421
|
+
);
|
|
4422
|
+
}
|
|
4423
|
+
__name(normalizeRetention, "normalizeRetention");
|
|
3151
4424
|
function normalizeSizeBytes(value) {
|
|
3152
4425
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
3153
4426
|
throw new SessionArtifactValidationError(
|
|
@@ -3173,21 +4446,83 @@ function inferKind(input) {
|
|
|
3173
4446
|
return input.workspacePath ? "file" : "other";
|
|
3174
4447
|
}
|
|
3175
4448
|
__name(inferKind, "inferKind");
|
|
3176
|
-
async function getWorkspaceStatus(
|
|
3177
|
-
const absolutePath = path2.resolve(workspaceCwd, workspacePath);
|
|
4449
|
+
async function getWorkspaceStatus(workspacePath, realWorkspaceCwd, expected, options = { hashContent: true }) {
|
|
3178
4450
|
const realWorkspace = await realWorkspaceCwd;
|
|
4451
|
+
const absolutePath = path2.resolve(realWorkspace, workspacePath);
|
|
3179
4452
|
try {
|
|
3180
4453
|
const realPath = await fs2.realpath(absolutePath);
|
|
3181
4454
|
const relative = path2.relative(realWorkspace, realPath);
|
|
3182
4455
|
if (!relative || isOutsidePath(relative)) {
|
|
3183
4456
|
return { status: "missing", escaped: true };
|
|
3184
4457
|
}
|
|
3185
|
-
const
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
4458
|
+
const preOpenStat = await fs2.lstat(realPath);
|
|
4459
|
+
const handle = await fs2.open(
|
|
4460
|
+
realPath,
|
|
4461
|
+
fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW
|
|
4462
|
+
);
|
|
4463
|
+
try {
|
|
4464
|
+
const stat = await handle.stat();
|
|
4465
|
+
if (!isSameFile(preOpenStat, stat)) {
|
|
4466
|
+
return { status: "missing", escaped: true };
|
|
4467
|
+
}
|
|
4468
|
+
if (stat.isFile()) {
|
|
4469
|
+
const expectedMtimeMs = typeof expected?.mtimeMs === "number" ? expected.mtimeMs : void 0;
|
|
4470
|
+
const expectedSha256 = typeof expected?.sha256 === "string" ? expected.sha256 : void 0;
|
|
4471
|
+
const unchanged = expected?.sizeBytes === stat.size && expectedMtimeMs === stat.mtimeMs;
|
|
4472
|
+
const sizeChanged = expected?.sizeBytes !== void 0 && expected.sizeBytes !== stat.size;
|
|
4473
|
+
if (sizeChanged) {
|
|
4474
|
+
return {
|
|
4475
|
+
status: "changed",
|
|
4476
|
+
sizeBytes: stat.size,
|
|
4477
|
+
mtimeMs: stat.mtimeMs
|
|
4478
|
+
};
|
|
4479
|
+
}
|
|
4480
|
+
if (unchanged) {
|
|
4481
|
+
return {
|
|
4482
|
+
status: "available",
|
|
4483
|
+
sizeBytes: stat.size,
|
|
4484
|
+
mtimeMs: stat.mtimeMs
|
|
4485
|
+
};
|
|
4486
|
+
}
|
|
4487
|
+
if (stat.size > MAX_WORKSPACE_HASH_BYTES) {
|
|
4488
|
+
return {
|
|
4489
|
+
status: expectedSha256 ? "changed" : "available",
|
|
4490
|
+
sizeBytes: stat.size,
|
|
4491
|
+
mtimeMs: stat.mtimeMs
|
|
4492
|
+
};
|
|
4493
|
+
}
|
|
4494
|
+
if (!options.hashContent) {
|
|
4495
|
+
return {
|
|
4496
|
+
status: expectedSha256 ? "changed" : "available",
|
|
4497
|
+
sizeBytes: stat.size,
|
|
4498
|
+
mtimeMs: stat.mtimeMs
|
|
4499
|
+
};
|
|
4500
|
+
}
|
|
4501
|
+
const sha256 = await hashFile(handle);
|
|
4502
|
+
if (expectedSha256 && sha256 !== expectedSha256) {
|
|
4503
|
+
return {
|
|
4504
|
+
status: "changed",
|
|
4505
|
+
sizeBytes: stat.size,
|
|
4506
|
+
mtimeMs: stat.mtimeMs
|
|
4507
|
+
};
|
|
4508
|
+
}
|
|
4509
|
+
return {
|
|
4510
|
+
status: "available",
|
|
4511
|
+
sizeBytes: stat.size,
|
|
4512
|
+
mtimeMs: stat.mtimeMs,
|
|
4513
|
+
sha256
|
|
4514
|
+
};
|
|
4515
|
+
}
|
|
4516
|
+
return {
|
|
4517
|
+
status: "available"
|
|
4518
|
+
};
|
|
4519
|
+
} finally {
|
|
4520
|
+
await handle.close();
|
|
4521
|
+
}
|
|
3190
4522
|
} catch (error) {
|
|
4523
|
+
if (isNoFollowSymlinkError(error)) {
|
|
4524
|
+
return { status: "missing", escaped: true };
|
|
4525
|
+
}
|
|
3191
4526
|
if (!isNotFoundError(error)) {
|
|
3192
4527
|
throw error;
|
|
3193
4528
|
}
|
|
@@ -3198,6 +4533,41 @@ async function getWorkspaceStatus(workspaceCwd, workspacePath, realWorkspaceCwd)
|
|
|
3198
4533
|
}
|
|
3199
4534
|
}
|
|
3200
4535
|
__name(getWorkspaceStatus, "getWorkspaceStatus");
|
|
4536
|
+
function isSameFile(before, after) {
|
|
4537
|
+
return before.dev === after.dev && before.ino === after.ino;
|
|
4538
|
+
}
|
|
4539
|
+
__name(isSameFile, "isSameFile");
|
|
4540
|
+
async function hashFile(handle) {
|
|
4541
|
+
const hash = createHash("sha256");
|
|
4542
|
+
for await (const chunk of handle.createReadStream({ start: 0 })) {
|
|
4543
|
+
hash.update(chunk);
|
|
4544
|
+
}
|
|
4545
|
+
return hash.digest("hex");
|
|
4546
|
+
}
|
|
4547
|
+
__name(hashFile, "hashFile");
|
|
4548
|
+
function withWorkspaceContentHashMetadata(metadata, workspaceStatus) {
|
|
4549
|
+
if (workspaceStatus?.status !== "available" || !workspaceStatus.sha256) {
|
|
4550
|
+
return metadata;
|
|
4551
|
+
}
|
|
4552
|
+
const next = {
|
|
4553
|
+
...metadata ?? {},
|
|
4554
|
+
[WORKSPACE_CONTENT_SHA256_METADATA_KEY]: workspaceStatus.sha256,
|
|
4555
|
+
...workspaceStatus.mtimeMs !== void 0 ? { [WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY]: workspaceStatus.mtimeMs } : {}
|
|
4556
|
+
};
|
|
4557
|
+
return next;
|
|
4558
|
+
}
|
|
4559
|
+
__name(withWorkspaceContentHashMetadata, "withWorkspaceContentHashMetadata");
|
|
4560
|
+
function isWorkspaceContentChanged(artifact, status) {
|
|
4561
|
+
if (status.status !== "available") {
|
|
4562
|
+
return false;
|
|
4563
|
+
}
|
|
4564
|
+
const expectedSha256 = artifact.metadata?.[WORKSPACE_CONTENT_SHA256_METADATA_KEY];
|
|
4565
|
+
if (artifact.sizeBytes !== void 0 && status.sizeBytes !== void 0 && status.sizeBytes !== artifact.sizeBytes) {
|
|
4566
|
+
return true;
|
|
4567
|
+
}
|
|
4568
|
+
return typeof expectedSha256 === "string" && status.sha256 !== void 0 && status.sha256 !== expectedSha256;
|
|
4569
|
+
}
|
|
4570
|
+
__name(isWorkspaceContentChanged, "isWorkspaceContentChanged");
|
|
3201
4571
|
async function danglingSymlinkEscapesWorkspace(absolutePath, realWorkspace) {
|
|
3202
4572
|
try {
|
|
3203
4573
|
const stat = await fs2.lstat(absolutePath);
|
|
@@ -3230,6 +4600,13 @@ function isNotFoundError(error) {
|
|
|
3230
4600
|
return code === "ENOENT" || code === "ENOTDIR";
|
|
3231
4601
|
}
|
|
3232
4602
|
__name(isNotFoundError, "isNotFoundError");
|
|
4603
|
+
function isNoFollowSymlinkError(error) {
|
|
4604
|
+
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
4605
|
+
return false;
|
|
4606
|
+
}
|
|
4607
|
+
return error.code === "ELOOP";
|
|
4608
|
+
}
|
|
4609
|
+
__name(isNoFollowSymlinkError, "isNoFollowSymlinkError");
|
|
3233
4610
|
function isOutsidePath(relative) {
|
|
3234
4611
|
return relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative);
|
|
3235
4612
|
}
|
|
@@ -3464,6 +4841,12 @@ function parseWorkspaceMemoryDreamResult(response) {
|
|
|
3464
4841
|
};
|
|
3465
4842
|
}
|
|
3466
4843
|
__name(parseWorkspaceMemoryDreamResult, "parseWorkspaceMemoryDreamResult");
|
|
4844
|
+
function pickUserInputEchoMeta(meta) {
|
|
4845
|
+
if (!meta || typeof meta !== "object" || Array.isArray(meta)) return {};
|
|
4846
|
+
const inputAnnotations = meta["inputAnnotations"];
|
|
4847
|
+
return Array.isArray(inputAnnotations) ? { inputAnnotations } : {};
|
|
4848
|
+
}
|
|
4849
|
+
__name(pickUserInputEchoMeta, "pickUserInputEchoMeta");
|
|
3467
4850
|
function echoPromptToSessionBus(entry, req, originatorClientId) {
|
|
3468
4851
|
const prompt = req.prompt;
|
|
3469
4852
|
if (!Array.isArray(prompt) || prompt.length === 0) return;
|
|
@@ -3485,7 +4868,11 @@ function echoPromptToSessionBus(entry, req, originatorClientId) {
|
|
|
3485
4868
|
// field permitted alongside spec fields, the SDK normalizer
|
|
3486
4869
|
// reads it from `update._meta`/`data._meta`, and every other
|
|
3487
4870
|
// agent-emitted session_update carries `_meta` the same way.
|
|
3488
|
-
_meta: {
|
|
4871
|
+
_meta: {
|
|
4872
|
+
...pickUserInputEchoMeta(req._meta),
|
|
4873
|
+
serverTimestamp,
|
|
4874
|
+
source: "bridge-echo"
|
|
4875
|
+
}
|
|
3489
4876
|
}
|
|
3490
4877
|
},
|
|
3491
4878
|
...originatorClientId ? { originatorClientId } : {}
|
|
@@ -3582,6 +4969,11 @@ function broadcastTurnError(entry, sessionId, err, promptId, originatorClientId)
|
|
|
3582
4969
|
);
|
|
3583
4970
|
}
|
|
3584
4971
|
entry.retryAllowed = true;
|
|
4972
|
+
entry.turnError = {
|
|
4973
|
+
message,
|
|
4974
|
+
...code ? { code } : {},
|
|
4975
|
+
...errorKind ? { errorKind } : {}
|
|
4976
|
+
};
|
|
3585
4977
|
try {
|
|
3586
4978
|
entry.events.publish({
|
|
3587
4979
|
type: "turn_error",
|
|
@@ -3624,6 +5016,7 @@ function extractPromptText(prompt) {
|
|
|
3624
5016
|
__name(extractPromptText, "extractPromptText");
|
|
3625
5017
|
var DEFAULT_INIT_TIMEOUT_MS = 1e4;
|
|
3626
5018
|
var PERSIST_TIMEOUT_MS = 5e3;
|
|
5019
|
+
var MAX_PARENT_PERSIST_ATTEMPTS = 3;
|
|
3627
5020
|
var MCP_RESTART_TIMEOUT_MS = 3e5;
|
|
3628
5021
|
var WORKSPACE_MEMORY_REMEMBER_TIMEOUT_MS = 3e5;
|
|
3629
5022
|
var MCP_OAUTH_TIMEOUT_MS = 6e5;
|
|
@@ -3631,6 +5024,7 @@ var DAEMON_RETRY_META_KEY = "qwen.daemon.retry";
|
|
|
3631
5024
|
var DAEMON_CONTINUE_META_KEY = "qwen.daemon.continueLastTurn";
|
|
3632
5025
|
var SESSION_RECAP_TIMEOUT_MS = 6e4;
|
|
3633
5026
|
var SESSION_BTW_TIMEOUT_MS = 6e4;
|
|
5027
|
+
var SESSION_TRANSCRIPT_TIMEOUT_MS = 6e4;
|
|
3634
5028
|
var SHELL_COMMAND_TIMEOUT_MS = 12e4;
|
|
3635
5029
|
var MAX_SHELL_OUTPUT_FOR_HISTORY = 1e4;
|
|
3636
5030
|
var MAX_MID_TURN_QUEUE_DEPTH = 20;
|
|
@@ -3671,6 +5065,15 @@ function createAcpSessionBridge(opts) {
|
|
|
3671
5065
|
);
|
|
3672
5066
|
}
|
|
3673
5067
|
}, "releaseFreshSessionReservation");
|
|
5068
|
+
const emitSessionLifecycle = /* @__PURE__ */ __name((event) => {
|
|
5069
|
+
try {
|
|
5070
|
+
opts.sessionLifecycle?.(event);
|
|
5071
|
+
} catch (err) {
|
|
5072
|
+
const message = `qwen serve: session lifecycle callback failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
5073
|
+
opts.onDiagnosticLine?.(message, "warn");
|
|
5074
|
+
writeStderrLine(message);
|
|
5075
|
+
}
|
|
5076
|
+
}, "emitSessionLifecycle");
|
|
3674
5077
|
if (defaultSessionScope !== "single" && defaultSessionScope !== "thread") {
|
|
3675
5078
|
throw new TypeError(
|
|
3676
5079
|
`Invalid sessionScope: ${JSON.stringify(defaultSessionScope)}. Expected 'single' or 'thread'.`
|
|
@@ -3855,14 +5258,32 @@ function createAcpSessionBridge(opts) {
|
|
|
3855
5258
|
const aliveChannels = /* @__PURE__ */ new Set();
|
|
3856
5259
|
let inFlightChannelSpawn;
|
|
3857
5260
|
const byId = /* @__PURE__ */ new Map();
|
|
3858
|
-
const toSessionSummary = /* @__PURE__ */ __name((entry) =>
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
5261
|
+
const toSessionSummary = /* @__PURE__ */ __name((entry) => {
|
|
5262
|
+
let isWaitingForPermission = false;
|
|
5263
|
+
let isWaitingForUserQuestion = false;
|
|
5264
|
+
for (const interaction of entry.pendingInteractions.values()) {
|
|
5265
|
+
if (interaction.kind === "user_question") {
|
|
5266
|
+
isWaitingForUserQuestion = true;
|
|
5267
|
+
} else {
|
|
5268
|
+
isWaitingForPermission = true;
|
|
5269
|
+
}
|
|
5270
|
+
}
|
|
5271
|
+
return {
|
|
5272
|
+
sessionId: entry.sessionId,
|
|
5273
|
+
workspaceCwd: entry.workspaceCwd,
|
|
5274
|
+
createdAt: entry.createdAt,
|
|
5275
|
+
displayName: entry.displayName,
|
|
5276
|
+
...entry.parentSessionId ? { parentSessionId: entry.parentSessionId } : {},
|
|
5277
|
+
clientCount: entry.clientIds.size,
|
|
5278
|
+
hasActivePrompt: entry.promptActive,
|
|
5279
|
+
isWaitingForPermission,
|
|
5280
|
+
isWaitingForUserQuestion,
|
|
5281
|
+
pendingInteractionCount: entry.pendingInteractions.size,
|
|
5282
|
+
hasTurnError: entry.turnError !== void 0,
|
|
5283
|
+
...entry.turnError !== void 0 ? { turnError: entry.turnError } : {},
|
|
5284
|
+
pendingInteractions: [...entry.pendingInteractions.values()]
|
|
5285
|
+
};
|
|
5286
|
+
}, "toSessionSummary");
|
|
3866
5287
|
const permissionConsensusQuorum = opts.permissionConsensusQuorum;
|
|
3867
5288
|
if (permissionConsensusQuorum !== void 0 && (!Number.isInteger(permissionConsensusQuorum) || permissionConsensusQuorum < 1)) {
|
|
3868
5289
|
throw new Error(
|
|
@@ -3888,6 +5309,7 @@ function createAcpSessionBridge(opts) {
|
|
|
3888
5309
|
}
|
|
3889
5310
|
);
|
|
3890
5311
|
let shuttingDown = false;
|
|
5312
|
+
let shutdownPromise;
|
|
3891
5313
|
const teeServeDebugLine = /* @__PURE__ */ __name((message) => {
|
|
3892
5314
|
writeServeDebugLine(message);
|
|
3893
5315
|
if (opts.onDiagnosticLine && isServeDebugLoggingEnabled()) {
|
|
@@ -3921,6 +5343,22 @@ function createAcpSessionBridge(opts) {
|
|
|
3921
5343
|
entry.clientIds.set(clientId, count - 1);
|
|
3922
5344
|
}
|
|
3923
5345
|
}, "unregisterClient");
|
|
5346
|
+
const rollbackAttachRegistration = /* @__PURE__ */ __name(async (entry, clientId, attachCountDelta = 1) => {
|
|
5347
|
+
entry.attachCount = Math.max(0, entry.attachCount - attachCountDelta);
|
|
5348
|
+
unregisterClient(entry, clientId);
|
|
5349
|
+
if (entry.spawnOwnerWantedKill && entry.attachCount === 0 && entry.events.subscriberCount === 0) {
|
|
5350
|
+
await bridgeApi.killSession(entry.sessionId).catch(() => {
|
|
5351
|
+
});
|
|
5352
|
+
} else if (entry.clientIds.size === 0 && entry.events.subscriberCount === 0 && !entry.promptActive) {
|
|
5353
|
+
await closeSessionImpl(entry.sessionId, void 0, {
|
|
5354
|
+
reason: "last_client_detached"
|
|
5355
|
+
}).catch((err) => {
|
|
5356
|
+
writeStderrLine(
|
|
5357
|
+
`qwen serve: close-on-attach-rollback failed for ${JSON.stringify(entry.sessionId)}: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
|
|
5358
|
+
);
|
|
5359
|
+
});
|
|
5360
|
+
}
|
|
5361
|
+
}, "rollbackAttachRegistration");
|
|
3924
5362
|
const resolveTrustedClientId = /* @__PURE__ */ __name((entry, clientId) => {
|
|
3925
5363
|
if (clientId === void 0) return void 0;
|
|
3926
5364
|
if (!entry.clientIds.has(clientId)) {
|
|
@@ -4001,7 +5439,11 @@ function createAcpSessionBridge(opts) {
|
|
|
4001
5439
|
inputTokens,
|
|
4002
5440
|
outputTokens,
|
|
4003
5441
|
durationMs
|
|
4004
|
-
)
|
|
5442
|
+
),
|
|
5443
|
+
// `create_sub_session` tool: forward the request/response hook so a child
|
|
5444
|
+
// tool can ask the daemon to spawn a sub-session and (for 'first-turn')
|
|
5445
|
+
// return its result. Omitted → the method reports daemon-only.
|
|
5446
|
+
opts.onCreateSubSession
|
|
4005
5447
|
);
|
|
4006
5448
|
const connection = new ClientSideConnection(() => client, channel.stream);
|
|
4007
5449
|
const info = {
|
|
@@ -4066,6 +5508,12 @@ function createAcpSessionBridge(opts) {
|
|
|
4066
5508
|
}
|
|
4067
5509
|
byId.delete(sid);
|
|
4068
5510
|
telemetry.metrics?.sessionLifecycle("die");
|
|
5511
|
+
emitSessionLifecycle({
|
|
5512
|
+
type: "removed",
|
|
5513
|
+
sessionId: sid,
|
|
5514
|
+
workspaceCwd: sessEntry.workspaceCwd,
|
|
5515
|
+
reason: "channel_closed"
|
|
5516
|
+
});
|
|
4069
5517
|
info.client.markSessionClosed(sid);
|
|
4070
5518
|
if (defaultEntry === sessEntry) defaultEntry = void 0;
|
|
4071
5519
|
sessEntry.events.close();
|
|
@@ -4114,10 +5562,12 @@ function createAcpSessionBridge(opts) {
|
|
|
4114
5562
|
}
|
|
4115
5563
|
}
|
|
4116
5564
|
__name(ensureChannel, "ensureChannel");
|
|
4117
|
-
async function doSpawn(modelServiceId, effectiveScope, requestedClientId, onSessionRegistered) {
|
|
5565
|
+
async function doSpawn(modelServiceId, effectiveScope, approvalMode, requestedClientId, onSessionRegistered, parentSessionId) {
|
|
4118
5566
|
const ci = await ensureChannel();
|
|
4119
5567
|
ci.sessionSpawnsInFlight++;
|
|
4120
5568
|
let sessionRegistered = false;
|
|
5569
|
+
let sessionRemovedDuringInitialization = false;
|
|
5570
|
+
let initializedSessionId;
|
|
4121
5571
|
let newSessionResp;
|
|
4122
5572
|
try {
|
|
4123
5573
|
try {
|
|
@@ -4152,13 +5602,58 @@ function createAcpSessionBridge(opts) {
|
|
|
4152
5602
|
const entry = createSessionEntry(
|
|
4153
5603
|
ci,
|
|
4154
5604
|
newSessionResp.sessionId,
|
|
4155
|
-
boundWorkspace
|
|
5605
|
+
boundWorkspace,
|
|
5606
|
+
void 0,
|
|
5607
|
+
{ parentSessionId }
|
|
4156
5608
|
);
|
|
5609
|
+
initializedSessionId = entry.sessionId;
|
|
4157
5610
|
sessionRegistered = true;
|
|
4158
5611
|
onSessionRegistered?.();
|
|
4159
5612
|
seedSnapshotCaches(entry, newSessionResp);
|
|
4160
5613
|
const clientId = registerClient(entry, requestedClientId);
|
|
4161
|
-
|
|
5614
|
+
let parentSessionPersisted;
|
|
5615
|
+
if (entry.parentSessionId) {
|
|
5616
|
+
const parentDeadline = Date.now() + initTimeoutMs;
|
|
5617
|
+
let lastParentErr;
|
|
5618
|
+
for (let attempt = 1; attempt <= MAX_PARENT_PERSIST_ATTEMPTS; attempt++) {
|
|
5619
|
+
const remaining = parentDeadline - Date.now();
|
|
5620
|
+
if (remaining <= 0) {
|
|
5621
|
+
parentSessionPersisted = false;
|
|
5622
|
+
lastParentErr = "deadline exceeded";
|
|
5623
|
+
break;
|
|
5624
|
+
}
|
|
5625
|
+
try {
|
|
5626
|
+
const parentResult = await Promise.race([
|
|
5627
|
+
withTimeout(
|
|
5628
|
+
entry.connection.extMethod(
|
|
5629
|
+
SERVE_CONTROL_EXT_METHODS.sessionParent,
|
|
5630
|
+
{
|
|
5631
|
+
sessionId: entry.sessionId,
|
|
5632
|
+
parentSessionId: entry.parentSessionId
|
|
5633
|
+
}
|
|
5634
|
+
),
|
|
5635
|
+
remaining,
|
|
5636
|
+
"sessionParent"
|
|
5637
|
+
),
|
|
5638
|
+
getTransportClosedReject(entry)
|
|
5639
|
+
]);
|
|
5640
|
+
parentSessionPersisted = parentResult?.persisted === true;
|
|
5641
|
+
break;
|
|
5642
|
+
} catch (err) {
|
|
5643
|
+
lastParentErr = err instanceof Error ? err.message : String(err);
|
|
5644
|
+
const terminal = err instanceof BridgeTimeoutError || err instanceof BridgeChannelClosedError || attempt === MAX_PARENT_PERSIST_ATTEMPTS;
|
|
5645
|
+
if (terminal) {
|
|
5646
|
+
parentSessionPersisted = false;
|
|
5647
|
+
break;
|
|
5648
|
+
}
|
|
5649
|
+
}
|
|
5650
|
+
}
|
|
5651
|
+
if (parentSessionPersisted === false) {
|
|
5652
|
+
writeStderrLine(
|
|
5653
|
+
`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)`
|
|
5654
|
+
);
|
|
5655
|
+
}
|
|
5656
|
+
}
|
|
4162
5657
|
if (modelServiceId) {
|
|
4163
5658
|
await applyModelServiceId(
|
|
4164
5659
|
entry,
|
|
@@ -4168,22 +5663,51 @@ function createAcpSessionBridge(opts) {
|
|
|
4168
5663
|
).catch(() => {
|
|
4169
5664
|
});
|
|
4170
5665
|
}
|
|
5666
|
+
if (approvalMode) {
|
|
5667
|
+
try {
|
|
5668
|
+
await applyApprovalMode(entry, approvalMode, false, clientId);
|
|
5669
|
+
} catch (err) {
|
|
5670
|
+
try {
|
|
5671
|
+
await closeSessionImpl(entry.sessionId, void 0, {
|
|
5672
|
+
reason: "approval_mode_initialization_failed"
|
|
5673
|
+
});
|
|
5674
|
+
sessionRemovedDuringInitialization = true;
|
|
5675
|
+
} catch {
|
|
5676
|
+
}
|
|
5677
|
+
throw err;
|
|
5678
|
+
}
|
|
5679
|
+
}
|
|
4171
5680
|
if (!byId.has(entry.sessionId)) {
|
|
4172
5681
|
throw new Error(
|
|
4173
|
-
`Session ${entry.sessionId} died during
|
|
5682
|
+
`Session ${entry.sessionId} died during session initialization`
|
|
4174
5683
|
);
|
|
4175
5684
|
}
|
|
5685
|
+
if (effectiveScope === "single" && !defaultEntry) defaultEntry = entry;
|
|
4176
5686
|
return {
|
|
4177
5687
|
sessionId: entry.sessionId,
|
|
4178
5688
|
workspaceCwd: entry.workspaceCwd,
|
|
4179
5689
|
attached: false,
|
|
4180
5690
|
clientId,
|
|
4181
|
-
createdAt: entry.createdAt
|
|
5691
|
+
createdAt: entry.createdAt,
|
|
5692
|
+
...entry.parentSessionId ? { parentSessionPersisted: parentSessionPersisted === true } : {}
|
|
4182
5693
|
};
|
|
4183
5694
|
} finally {
|
|
4184
5695
|
ci.sessionSpawnsInFlight = Math.max(0, ci.sessionSpawnsInFlight - 1);
|
|
4185
5696
|
if (!sessionRegistered) {
|
|
4186
5697
|
await reapPendingEmptyChannel(ci);
|
|
5698
|
+
} else if (sessionRemovedDuringInitialization && hasNoChannelWork(ci)) {
|
|
5699
|
+
await reapPendingEmptyChannel(ci);
|
|
5700
|
+
if (!ci.isDying) {
|
|
5701
|
+
await startIdleTimer(
|
|
5702
|
+
ci,
|
|
5703
|
+
`approval-mode initialization failure "${initializedSessionId}"`
|
|
5704
|
+
);
|
|
5705
|
+
}
|
|
5706
|
+
} else if (sessionRegistered && hasNoChannelWork(ci) && !ci.isDying) {
|
|
5707
|
+
await startIdleTimer(
|
|
5708
|
+
ci,
|
|
5709
|
+
`session orphaned during initialization "${initializedSessionId}"`
|
|
5710
|
+
);
|
|
4187
5711
|
}
|
|
4188
5712
|
}
|
|
4189
5713
|
}
|
|
@@ -4213,41 +5737,157 @@ function createAcpSessionBridge(opts) {
|
|
|
4213
5737
|
key: "model.name",
|
|
4214
5738
|
value: modelId
|
|
4215
5739
|
},
|
|
4216
|
-
...originatorClientId ? { originatorClientId } : {}
|
|
4217
|
-
});
|
|
5740
|
+
...originatorClientId ? { originatorClientId } : {}
|
|
5741
|
+
});
|
|
5742
|
+
succeeded = true;
|
|
5743
|
+
} catch (err) {
|
|
5744
|
+
entry.events.publish({
|
|
5745
|
+
type: "model_switch_failed",
|
|
5746
|
+
data: {
|
|
5747
|
+
sessionId: entry.sessionId,
|
|
5748
|
+
requestedModelId: modelId,
|
|
5749
|
+
error: err instanceof Error ? err.message : String(err)
|
|
5750
|
+
},
|
|
5751
|
+
...originatorClientId ? { originatorClientId } : {}
|
|
5752
|
+
});
|
|
5753
|
+
throw err;
|
|
5754
|
+
} finally {
|
|
5755
|
+
entry.modelRoundtripInFlight = false;
|
|
5756
|
+
if (succeeded) {
|
|
5757
|
+
void reconcileAfterRoundtrip(entry, "model");
|
|
5758
|
+
} else {
|
|
5759
|
+
writeStderrLine(
|
|
5760
|
+
`[reconcile] session=${entry.sessionId} target=model action=skipped reason=roundtrip_failed`
|
|
5761
|
+
);
|
|
5762
|
+
}
|
|
5763
|
+
}
|
|
5764
|
+
});
|
|
5765
|
+
entry.modelChangeQueue = work.then(
|
|
5766
|
+
() => void 0,
|
|
5767
|
+
() => void 0
|
|
5768
|
+
);
|
|
5769
|
+
return work;
|
|
5770
|
+
}
|
|
5771
|
+
__name(applyModelServiceId, "applyModelServiceId");
|
|
5772
|
+
async function applyApprovalMode(entry, mode, persist, originatorClientId) {
|
|
5773
|
+
if (persist && !persistApprovalMode) {
|
|
5774
|
+
throw new Error(
|
|
5775
|
+
"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`."
|
|
5776
|
+
);
|
|
5777
|
+
}
|
|
5778
|
+
const approvalWork = entry.approvalModeQueue.then(async () => {
|
|
5779
|
+
entry.approvalModeRoundtripInFlight = true;
|
|
5780
|
+
let succeeded = false;
|
|
5781
|
+
try {
|
|
5782
|
+
const response = await Promise.race([
|
|
5783
|
+
withTimeout(
|
|
5784
|
+
entry.connection.extMethod(
|
|
5785
|
+
SERVE_CONTROL_EXT_METHODS.sessionApprovalMode,
|
|
5786
|
+
{ sessionId: entry.sessionId, mode }
|
|
5787
|
+
),
|
|
5788
|
+
initTimeoutMs,
|
|
5789
|
+
SERVE_CONTROL_EXT_METHODS.sessionApprovalMode
|
|
5790
|
+
),
|
|
5791
|
+
getTransportClosedReject(entry)
|
|
5792
|
+
]);
|
|
5793
|
+
if (typeof response.current !== "string" || !KNOWN_APPROVAL_MODES.has(response.current)) {
|
|
5794
|
+
throw new Error(
|
|
5795
|
+
`Agent returned unknown approval mode: ${JSON.stringify(response.current)}`
|
|
5796
|
+
);
|
|
5797
|
+
}
|
|
5798
|
+
let persisted = false;
|
|
5799
|
+
if (persist) {
|
|
5800
|
+
try {
|
|
5801
|
+
await withTimeout(
|
|
5802
|
+
persistApprovalMode?.(boundWorkspace, mode) ?? Promise.resolve(),
|
|
5803
|
+
PERSIST_TIMEOUT_MS,
|
|
5804
|
+
"persistApprovalMode"
|
|
5805
|
+
);
|
|
5806
|
+
persisted = persistApprovalMode !== void 0;
|
|
5807
|
+
} catch (err) {
|
|
5808
|
+
writeStderrLine(
|
|
5809
|
+
`setSessionApprovalMode: persist failed: ${err instanceof Error ? err.message : String(err)}`
|
|
5810
|
+
);
|
|
5811
|
+
}
|
|
5812
|
+
}
|
|
5813
|
+
publishApprovalModeChanged(
|
|
5814
|
+
entry,
|
|
5815
|
+
{
|
|
5816
|
+
previous: response.previous,
|
|
5817
|
+
next: response.current,
|
|
5818
|
+
persisted
|
|
5819
|
+
},
|
|
5820
|
+
originatorClientId
|
|
5821
|
+
);
|
|
5822
|
+
if (persisted) {
|
|
5823
|
+
broadcastWorkspaceEvent(
|
|
5824
|
+
{
|
|
5825
|
+
type: "approval_mode_changed",
|
|
5826
|
+
data: {
|
|
5827
|
+
sessionId: entry.sessionId,
|
|
5828
|
+
previous: response.previous,
|
|
5829
|
+
next: response.current,
|
|
5830
|
+
persisted
|
|
5831
|
+
},
|
|
5832
|
+
...originatorClientId ? { originatorClientId } : {}
|
|
5833
|
+
},
|
|
5834
|
+
entry.sessionId
|
|
5835
|
+
);
|
|
5836
|
+
for (const peer of byId.values()) {
|
|
5837
|
+
if (peer.sessionId === entry.sessionId) {
|
|
5838
|
+
continue;
|
|
5839
|
+
}
|
|
5840
|
+
peer.currentApprovalMode = response.current;
|
|
5841
|
+
}
|
|
5842
|
+
}
|
|
4218
5843
|
succeeded = true;
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
error: err instanceof Error ? err.message : String(err)
|
|
4226
|
-
},
|
|
4227
|
-
...originatorClientId ? { originatorClientId } : {}
|
|
4228
|
-
});
|
|
4229
|
-
throw err;
|
|
5844
|
+
return {
|
|
5845
|
+
sessionId: entry.sessionId,
|
|
5846
|
+
mode: response.current,
|
|
5847
|
+
previous: response.previous,
|
|
5848
|
+
persisted
|
|
5849
|
+
};
|
|
4230
5850
|
} finally {
|
|
4231
|
-
entry.
|
|
5851
|
+
entry.approvalModeRoundtripInFlight = false;
|
|
4232
5852
|
if (succeeded) {
|
|
4233
|
-
void reconcileAfterRoundtrip(entry, "
|
|
5853
|
+
void reconcileAfterRoundtrip(entry, "approvalMode");
|
|
4234
5854
|
} else {
|
|
4235
5855
|
writeStderrLine(
|
|
4236
|
-
`[reconcile] session=${entry.sessionId} target=
|
|
5856
|
+
`[reconcile] session=${entry.sessionId} target=approvalMode action=skipped reason=roundtrip_failed`
|
|
4237
5857
|
);
|
|
4238
5858
|
}
|
|
4239
5859
|
}
|
|
4240
5860
|
});
|
|
4241
|
-
entry.
|
|
5861
|
+
entry.approvalModeQueue = approvalWork.then(
|
|
4242
5862
|
() => void 0,
|
|
4243
5863
|
() => void 0
|
|
4244
5864
|
);
|
|
4245
|
-
|
|
5865
|
+
try {
|
|
5866
|
+
return await approvalWork;
|
|
5867
|
+
} catch (err) {
|
|
5868
|
+
const data = err?.data;
|
|
5869
|
+
if (data && typeof data === "object" && "errorKind" in data && data.errorKind === "trust_gate") {
|
|
5870
|
+
const rawMessage = err?.message;
|
|
5871
|
+
const message = typeof rawMessage === "string" ? rawMessage : "Trust-gate rejection from ACP child";
|
|
5872
|
+
throw new TrustGateError(message);
|
|
5873
|
+
}
|
|
5874
|
+
throw err;
|
|
5875
|
+
}
|
|
4246
5876
|
}
|
|
4247
|
-
__name(
|
|
5877
|
+
__name(applyApprovalMode, "applyApprovalMode");
|
|
5878
|
+
async function applyApprovalModeForAttach(entry, mode, clientId) {
|
|
5879
|
+
try {
|
|
5880
|
+
await applyApprovalMode(entry, mode, false, clientId);
|
|
5881
|
+
} catch (err) {
|
|
5882
|
+
await rollbackAttachRegistration(entry, clientId);
|
|
5883
|
+
throw err;
|
|
5884
|
+
}
|
|
5885
|
+
}
|
|
5886
|
+
__name(applyApprovalModeForAttach, "applyApprovalModeForAttach");
|
|
4248
5887
|
const cancelPendingForSession = /* @__PURE__ */ __name((sessionId) => {
|
|
4249
5888
|
permissionMediator.forgetSession(sessionId);
|
|
4250
5889
|
byId.get(sessionId)?.pendingPermissionIds.clear();
|
|
5890
|
+
byId.get(sessionId)?.pendingInteractions.clear();
|
|
4251
5891
|
}, "cancelPendingForSession");
|
|
4252
5892
|
const getTransportClosedReject = /* @__PURE__ */ __name((entry) => {
|
|
4253
5893
|
if (!entry.transportClosedReject) {
|
|
@@ -4529,10 +6169,16 @@ function createAcpSessionBridge(opts) {
|
|
|
4529
6169
|
sessionId,
|
|
4530
6170
|
workspaceCwd,
|
|
4531
6171
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6172
|
+
...options.parentSessionId ? { parentSessionId: options.parentSessionId } : {},
|
|
4532
6173
|
channel: ci.channel,
|
|
4533
6174
|
connection: ci.connection,
|
|
4534
6175
|
events,
|
|
4535
|
-
artifacts: new SessionArtifactStore({
|
|
6176
|
+
artifacts: new SessionArtifactStore({
|
|
6177
|
+
sessionId,
|
|
6178
|
+
workspaceCwd,
|
|
6179
|
+
persistence: createSessionArtifactPersistence(ci.connection, sessionId)
|
|
6180
|
+
}),
|
|
6181
|
+
recordingDegraded: false,
|
|
4536
6182
|
promptQueue: Promise.resolve(),
|
|
4537
6183
|
pendingPromptCount: 0,
|
|
4538
6184
|
pendingPromptList: [],
|
|
@@ -4542,6 +6188,7 @@ function createAcpSessionBridge(opts) {
|
|
|
4542
6188
|
modelPublishGeneration: 0,
|
|
4543
6189
|
approvalModePublishGeneration: 0,
|
|
4544
6190
|
pendingPermissionIds: /* @__PURE__ */ new Set(),
|
|
6191
|
+
pendingInteractions: /* @__PURE__ */ new Map(),
|
|
4545
6192
|
clientIds: /* @__PURE__ */ new Map(),
|
|
4546
6193
|
clientLastSeenAt: /* @__PURE__ */ new Map(),
|
|
4547
6194
|
attachCount: 0,
|
|
@@ -4553,6 +6200,12 @@ function createAcpSessionBridge(opts) {
|
|
|
4553
6200
|
byId.set(entry.sessionId, entry);
|
|
4554
6201
|
touchActivity();
|
|
4555
6202
|
telemetry.metrics?.sessionLifecycle("spawn");
|
|
6203
|
+
emitSessionLifecycle({
|
|
6204
|
+
type: "registered",
|
|
6205
|
+
sessionId: entry.sessionId,
|
|
6206
|
+
workspaceCwd: entry.workspaceCwd,
|
|
6207
|
+
reason: options.lifecycleReason ?? "spawn"
|
|
6208
|
+
});
|
|
4556
6209
|
if (options.drainEarlyEvents !== false) {
|
|
4557
6210
|
ci.client.drainEarlyEvents(entry.sessionId, entry);
|
|
4558
6211
|
}
|
|
@@ -4567,6 +6220,42 @@ function createAcpSessionBridge(opts) {
|
|
|
4567
6220
|
});
|
|
4568
6221
|
}
|
|
4569
6222
|
}, "publishArtifactChanges");
|
|
6223
|
+
const artifactReseedChanges = /* @__PURE__ */ __name((before, after) => {
|
|
6224
|
+
const beforeById = new Map(
|
|
6225
|
+
before.map((artifact) => [artifact.id, artifact])
|
|
6226
|
+
);
|
|
6227
|
+
const afterById = new Map(after.map((artifact) => [artifact.id, artifact]));
|
|
6228
|
+
const changes = [];
|
|
6229
|
+
for (const artifact of before) {
|
|
6230
|
+
if (!afterById.has(artifact.id)) {
|
|
6231
|
+
changes.push({
|
|
6232
|
+
action: "removed",
|
|
6233
|
+
artifactId: artifact.id,
|
|
6234
|
+
artifact,
|
|
6235
|
+
reason: "eviction"
|
|
6236
|
+
});
|
|
6237
|
+
}
|
|
6238
|
+
}
|
|
6239
|
+
for (const artifact of after) {
|
|
6240
|
+
const previous = beforeById.get(artifact.id);
|
|
6241
|
+
if (!previous) {
|
|
6242
|
+
changes.push({
|
|
6243
|
+
action: "created",
|
|
6244
|
+
artifactId: artifact.id,
|
|
6245
|
+
artifact
|
|
6246
|
+
});
|
|
6247
|
+
continue;
|
|
6248
|
+
}
|
|
6249
|
+
if (!publicArtifactsEqual(previous, artifact)) {
|
|
6250
|
+
changes.push({
|
|
6251
|
+
action: "updated",
|
|
6252
|
+
artifactId: artifact.id,
|
|
6253
|
+
artifact
|
|
6254
|
+
});
|
|
6255
|
+
}
|
|
6256
|
+
}
|
|
6257
|
+
return changes;
|
|
6258
|
+
}, "artifactReseedChanges");
|
|
4570
6259
|
const makeClientArtifactInput = /* @__PURE__ */ __name((artifact, clientId) => {
|
|
4571
6260
|
const input = {
|
|
4572
6261
|
title: artifact.title,
|
|
@@ -4579,6 +6268,8 @@ function createAcpSessionBridge(opts) {
|
|
|
4579
6268
|
mimeType: artifact.mimeType,
|
|
4580
6269
|
sizeBytes: artifact.sizeBytes,
|
|
4581
6270
|
metadata: artifact.metadata,
|
|
6271
|
+
retention: artifact.retention,
|
|
6272
|
+
clientRetained: artifact.clientRetained,
|
|
4582
6273
|
source: "client"
|
|
4583
6274
|
};
|
|
4584
6275
|
if (clientId) {
|
|
@@ -4586,6 +6277,31 @@ function createAcpSessionBridge(opts) {
|
|
|
4586
6277
|
}
|
|
4587
6278
|
return input;
|
|
4588
6279
|
}, "makeClientArtifactInput");
|
|
6280
|
+
function createSessionArtifactPersistence(connection, sessionId) {
|
|
6281
|
+
return {
|
|
6282
|
+
recordEvent: /* @__PURE__ */ __name(async (payload) => {
|
|
6283
|
+
await connection.extMethod(
|
|
6284
|
+
SERVE_CONTROL_EXT_METHODS.sessionArtifactsPersist,
|
|
6285
|
+
{
|
|
6286
|
+
sessionId,
|
|
6287
|
+
kind: "event",
|
|
6288
|
+
payload
|
|
6289
|
+
}
|
|
6290
|
+
);
|
|
6291
|
+
}, "recordEvent"),
|
|
6292
|
+
recordSnapshot: /* @__PURE__ */ __name(async (payload) => {
|
|
6293
|
+
await connection.extMethod(
|
|
6294
|
+
SERVE_CONTROL_EXT_METHODS.sessionArtifactsPersist,
|
|
6295
|
+
{
|
|
6296
|
+
sessionId,
|
|
6297
|
+
kind: "snapshot",
|
|
6298
|
+
payload
|
|
6299
|
+
}
|
|
6300
|
+
);
|
|
6301
|
+
}, "recordSnapshot")
|
|
6302
|
+
};
|
|
6303
|
+
}
|
|
6304
|
+
__name(createSessionArtifactPersistence, "createSessionArtifactPersistence");
|
|
4589
6305
|
const seedSnapshotCaches = /* @__PURE__ */ __name((entry, resp) => {
|
|
4590
6306
|
const model = resp.models?.currentModelId;
|
|
4591
6307
|
if (typeof model === "string" && model.length > 0) {
|
|
@@ -4633,16 +6349,55 @@ function createAcpSessionBridge(opts) {
|
|
|
4633
6349
|
}
|
|
4634
6350
|
return { lastEventId: snapshot.lastEventId, ...replayStatus };
|
|
4635
6351
|
}, "replayFieldsFor");
|
|
6352
|
+
const restoredArtifactSnapshotFromState = /* @__PURE__ */ __name((state) => {
|
|
6353
|
+
const candidate = state.artifactSnapshot;
|
|
6354
|
+
const warnings = [];
|
|
6355
|
+
const snapshot = normalizeSnapshotPayload(candidate, warnings);
|
|
6356
|
+
if (!snapshot) return void 0;
|
|
6357
|
+
const snapshotWarnings = isRecord2(candidate) && Array.isArray(candidate["warnings"]) ? candidate["warnings"].filter(
|
|
6358
|
+
(warning) => typeof warning === "string" && warning.length <= 1e3
|
|
6359
|
+
).slice(-500) : [];
|
|
6360
|
+
return {
|
|
6361
|
+
v: SESSION_ARTIFACT_PERSISTENCE_VERSION,
|
|
6362
|
+
sessionId: snapshot.sessionId,
|
|
6363
|
+
sequence: snapshot.sequence,
|
|
6364
|
+
artifacts: snapshot.artifacts,
|
|
6365
|
+
...snapshot.markerArtifacts ? { markerArtifacts: snapshot.markerArtifacts } : {},
|
|
6366
|
+
tombstonedIds: snapshot.tombstonedIds ?? [],
|
|
6367
|
+
stickyEphemeralIds: snapshot.stickyEphemeralIds ?? [],
|
|
6368
|
+
warnings: [...warnings, ...snapshotWarnings]
|
|
6369
|
+
};
|
|
6370
|
+
}, "restoredArtifactSnapshotFromState");
|
|
6371
|
+
const artifactSnapshotUnavailableReason = /* @__PURE__ */ __name((state) => {
|
|
6372
|
+
const reason = state.artifactSnapshotUnavailable;
|
|
6373
|
+
return typeof reason === "string" && reason ? reason : void 0;
|
|
6374
|
+
}, "artifactSnapshotUnavailableReason");
|
|
6375
|
+
const publicRestoreState = /* @__PURE__ */ __name((state) => {
|
|
6376
|
+
const {
|
|
6377
|
+
artifactSnapshot: _artifactSnapshot,
|
|
6378
|
+
artifactSnapshotUnavailable: _artifactSnapshotUnavailable,
|
|
6379
|
+
...publicState
|
|
6380
|
+
} = state;
|
|
6381
|
+
return publicState;
|
|
6382
|
+
}, "publicRestoreState");
|
|
4636
6383
|
async function restoreSession(action, req, options = {}) {
|
|
4637
6384
|
if (shuttingDown) {
|
|
4638
6385
|
throw new Error("AcpSessionBridge is shutting down");
|
|
4639
6386
|
}
|
|
4640
6387
|
const workspaceKey = resolveWorkspaceKey(req.workspaceCwd);
|
|
6388
|
+
if (req.approvalMode !== void 0 && !KNOWN_APPROVAL_MODES.has(req.approvalMode)) {
|
|
6389
|
+
throw new Error(
|
|
6390
|
+
`Invalid approvalMode: ${JSON.stringify(req.approvalMode)}`
|
|
6391
|
+
);
|
|
6392
|
+
}
|
|
4641
6393
|
const historyReplay = action === "load" ? req.historyReplay ?? "stream" : "stream";
|
|
4642
6394
|
const existing = byId.get(req.sessionId);
|
|
4643
6395
|
if (existing) {
|
|
4644
6396
|
existing.attachCount++;
|
|
4645
6397
|
const clientId = registerClient(existing, req.clientId);
|
|
6398
|
+
if (req.approvalMode) {
|
|
6399
|
+
await applyApprovalModeForAttach(existing, req.approvalMode, clientId);
|
|
6400
|
+
}
|
|
4646
6401
|
return {
|
|
4647
6402
|
sessionId: existing.sessionId,
|
|
4648
6403
|
workspaceCwd: existing.workspaceCwd,
|
|
@@ -4681,10 +6436,14 @@ function createAcpSessionBridge(opts) {
|
|
|
4681
6436
|
"the agent child likely crashed during session restore \u2014 retry to restore the session"
|
|
4682
6437
|
);
|
|
4683
6438
|
}
|
|
6439
|
+
const clientId = registerClient(entry, req.clientId);
|
|
6440
|
+
if (req.approvalMode) {
|
|
6441
|
+
await applyApprovalModeForAttach(entry, req.approvalMode, clientId);
|
|
6442
|
+
}
|
|
4684
6443
|
return {
|
|
4685
6444
|
...restored,
|
|
4686
6445
|
attached: true,
|
|
4687
|
-
clientId
|
|
6446
|
+
clientId,
|
|
4688
6447
|
createdAt: entry.createdAt,
|
|
4689
6448
|
hasActivePrompt: entry.promptActive
|
|
4690
6449
|
};
|
|
@@ -4794,6 +6553,23 @@ function createAcpSessionBridge(opts) {
|
|
|
4794
6553
|
restoreEvents.close();
|
|
4795
6554
|
racedEntry.attachCount += 1 + coalesceState.count;
|
|
4796
6555
|
const clientId2 = registerClient(racedEntry, req.clientId);
|
|
6556
|
+
if (req.approvalMode) {
|
|
6557
|
+
try {
|
|
6558
|
+
await applyApprovalMode(
|
|
6559
|
+
racedEntry,
|
|
6560
|
+
req.approvalMode,
|
|
6561
|
+
false,
|
|
6562
|
+
clientId2
|
|
6563
|
+
);
|
|
6564
|
+
} catch (err) {
|
|
6565
|
+
await rollbackAttachRegistration(
|
|
6566
|
+
racedEntry,
|
|
6567
|
+
clientId2,
|
|
6568
|
+
1 + coalesceState.count
|
|
6569
|
+
);
|
|
6570
|
+
throw err;
|
|
6571
|
+
}
|
|
6572
|
+
}
|
|
4797
6573
|
return {
|
|
4798
6574
|
sessionId: racedEntry.sessionId,
|
|
4799
6575
|
workspaceCwd: racedEntry.workspaceCwd,
|
|
@@ -4810,22 +6586,48 @@ function createAcpSessionBridge(opts) {
|
|
|
4810
6586
|
req.sessionId,
|
|
4811
6587
|
workspaceKey,
|
|
4812
6588
|
restoreEvents,
|
|
4813
|
-
{
|
|
6589
|
+
{
|
|
6590
|
+
drainEarlyEvents: replayUpdates.length === 0,
|
|
6591
|
+
lifecycleReason: action,
|
|
6592
|
+
// Re-seed the persisted parent lineage the caller recovered from the
|
|
6593
|
+
// transcript, so a restored sub-session's status reports its parent.
|
|
6594
|
+
...req.parentSessionId ? { parentSessionId: req.parentSessionId } : {}
|
|
6595
|
+
}
|
|
4814
6596
|
);
|
|
4815
6597
|
releaseAdmissionOnce();
|
|
4816
|
-
|
|
6598
|
+
const restoredArtifactSnapshot = restoredArtifactSnapshotFromState(state);
|
|
6599
|
+
const publicState = publicRestoreState(state);
|
|
6600
|
+
entry.restoreState = publicState;
|
|
4817
6601
|
if (replayPartial === true) {
|
|
4818
6602
|
entry.restoreReplayPartial = true;
|
|
4819
6603
|
}
|
|
4820
6604
|
if (replayError !== void 0) {
|
|
4821
6605
|
entry.restoreReplayError = replayError;
|
|
4822
6606
|
}
|
|
4823
|
-
seedSnapshotCaches(entry,
|
|
6607
|
+
seedSnapshotCaches(entry, publicState);
|
|
6608
|
+
const artifactRestoreWarnings = await entry.artifacts.restore(
|
|
6609
|
+
restoredArtifactSnapshot
|
|
6610
|
+
);
|
|
6611
|
+
for (const warning of artifactRestoreWarnings) {
|
|
6612
|
+
writeStderrLine(
|
|
6613
|
+
`[artifacts] session=${entry.sessionId} action=restore_warning warning=${JSON.stringify(
|
|
6614
|
+
warning
|
|
6615
|
+
)}`
|
|
6616
|
+
);
|
|
6617
|
+
}
|
|
6618
|
+
const artifactRestoreFailed = artifactRestoreWarnings.some(
|
|
6619
|
+
(warning) => isArtifactRestoreFailureWarning(warning)
|
|
6620
|
+
);
|
|
4824
6621
|
if (replayUpdates.length > 0) {
|
|
4825
|
-
await ci.client.seedSessionUpdates(entry, replayUpdates
|
|
6622
|
+
await ci.client.seedSessionUpdates(entry, replayUpdates, {
|
|
6623
|
+
ingestArtifacts: restoredArtifactSnapshot === void 0 || artifactRestoreFailed
|
|
6624
|
+
});
|
|
4826
6625
|
ci.client.drainEarlyEvents(entry.sessionId, entry);
|
|
4827
6626
|
}
|
|
4828
6627
|
const clientId = registerClient(entry, req.clientId);
|
|
6628
|
+
if (req.approvalMode) {
|
|
6629
|
+
await applyApprovalModeForAttach(entry, req.approvalMode, clientId);
|
|
6630
|
+
}
|
|
4829
6631
|
entry.attachCount = coalesceState.count;
|
|
4830
6632
|
registeredEntry = entry;
|
|
4831
6633
|
return {
|
|
@@ -4834,7 +6636,8 @@ function createAcpSessionBridge(opts) {
|
|
|
4834
6636
|
attached: false,
|
|
4835
6637
|
clientId,
|
|
4836
6638
|
createdAt: entry.createdAt,
|
|
4837
|
-
state,
|
|
6639
|
+
state: publicState,
|
|
6640
|
+
...artifactRestoreWarnings.length > 0 ? { artifactWarnings: artifactRestoreWarnings } : {},
|
|
4838
6641
|
hasActivePrompt: entry.promptActive,
|
|
4839
6642
|
...replayFieldsFor(entry, action)
|
|
4840
6643
|
};
|
|
@@ -4846,9 +6649,16 @@ function createAcpSessionBridge(opts) {
|
|
|
4846
6649
|
if (!registeredEntry) {
|
|
4847
6650
|
restoreEvents.close();
|
|
4848
6651
|
let removedRestoreEntry = false;
|
|
4849
|
-
|
|
6652
|
+
const restoreEntry = byId.get(req.sessionId);
|
|
6653
|
+
if (restoreEntry?.events === restoreEvents) {
|
|
4850
6654
|
byId.delete(req.sessionId);
|
|
4851
6655
|
ci?.sessionIds.delete(req.sessionId);
|
|
6656
|
+
emitSessionLifecycle({
|
|
6657
|
+
type: "removed",
|
|
6658
|
+
sessionId: req.sessionId,
|
|
6659
|
+
workspaceCwd: restoreEntry.workspaceCwd,
|
|
6660
|
+
reason: "restore_failed"
|
|
6661
|
+
});
|
|
4852
6662
|
removedRestoreEntry = true;
|
|
4853
6663
|
}
|
|
4854
6664
|
if (removedRestoreEntry && ci && hasNoChannelWork(ci)) {
|
|
@@ -4909,6 +6719,7 @@ function createAcpSessionBridge(opts) {
|
|
|
4909
6719
|
}
|
|
4910
6720
|
permissionMediator.forgetSession(sessionId);
|
|
4911
6721
|
entry.pendingPermissionIds.clear();
|
|
6722
|
+
entry.pendingInteractions.clear();
|
|
4912
6723
|
if (entry.promptActive) {
|
|
4913
6724
|
entry.promptActive = false;
|
|
4914
6725
|
activePromptCounter--;
|
|
@@ -4916,6 +6727,12 @@ function createAcpSessionBridge(opts) {
|
|
|
4916
6727
|
}
|
|
4917
6728
|
byId.delete(sessionId);
|
|
4918
6729
|
telemetry.metrics?.sessionLifecycle("close");
|
|
6730
|
+
emitSessionLifecycle({
|
|
6731
|
+
type: "removed",
|
|
6732
|
+
sessionId,
|
|
6733
|
+
workspaceCwd: entry.workspaceCwd,
|
|
6734
|
+
reason
|
|
6735
|
+
});
|
|
4919
6736
|
ci?.client.markSessionClosed(sessionId);
|
|
4920
6737
|
try {
|
|
4921
6738
|
entry.events.publish({
|
|
@@ -5039,6 +6856,11 @@ function createAcpSessionBridge(opts) {
|
|
|
5039
6856
|
throw new InvalidSessionScopeError(req.sessionScope);
|
|
5040
6857
|
}
|
|
5041
6858
|
const effectiveScope = req.sessionScope ?? defaultSessionScope;
|
|
6859
|
+
if (req.approvalMode !== void 0 && !KNOWN_APPROVAL_MODES.has(req.approvalMode)) {
|
|
6860
|
+
throw new Error(
|
|
6861
|
+
`Invalid approvalMode: ${JSON.stringify(req.approvalMode)}`
|
|
6862
|
+
);
|
|
6863
|
+
}
|
|
5042
6864
|
if (effectiveScope === "single") {
|
|
5043
6865
|
const existing = defaultEntry;
|
|
5044
6866
|
if (existing) {
|
|
@@ -5053,6 +6875,13 @@ function createAcpSessionBridge(opts) {
|
|
|
5053
6875
|
).catch(() => {
|
|
5054
6876
|
});
|
|
5055
6877
|
}
|
|
6878
|
+
if (req.approvalMode) {
|
|
6879
|
+
await applyApprovalModeForAttach(
|
|
6880
|
+
existing,
|
|
6881
|
+
req.approvalMode,
|
|
6882
|
+
clientId
|
|
6883
|
+
);
|
|
6884
|
+
}
|
|
5056
6885
|
return {
|
|
5057
6886
|
sessionId: existing.sessionId,
|
|
5058
6887
|
workspaceCwd: existing.workspaceCwd,
|
|
@@ -5083,6 +6912,13 @@ function createAcpSessionBridge(opts) {
|
|
|
5083
6912
|
).catch(() => {
|
|
5084
6913
|
});
|
|
5085
6914
|
}
|
|
6915
|
+
if (req.approvalMode) {
|
|
6916
|
+
await applyApprovalModeForAttach(
|
|
6917
|
+
attachedEntry,
|
|
6918
|
+
req.approvalMode,
|
|
6919
|
+
clientId
|
|
6920
|
+
);
|
|
6921
|
+
}
|
|
5086
6922
|
return {
|
|
5087
6923
|
...session,
|
|
5088
6924
|
attached: true,
|
|
@@ -5107,8 +6943,10 @@ function createAcpSessionBridge(opts) {
|
|
|
5107
6943
|
const promise = doSpawn(
|
|
5108
6944
|
req.modelServiceId,
|
|
5109
6945
|
effectiveScope,
|
|
6946
|
+
req.approvalMode,
|
|
5110
6947
|
req.clientId,
|
|
5111
|
-
releaseAdmissionOnce
|
|
6948
|
+
releaseAdmissionOnce,
|
|
6949
|
+
req.parentSessionId
|
|
5112
6950
|
);
|
|
5113
6951
|
const tracker = effectiveScope === "single" ? workspaceKey : `${workspaceKey}#${randomUUID2()}`;
|
|
5114
6952
|
inFlightSpawns.set(tracker, promise);
|
|
@@ -5249,6 +7087,7 @@ function createAcpSessionBridge(opts) {
|
|
|
5249
7087
|
return copy;
|
|
5250
7088
|
})();
|
|
5251
7089
|
entry.promptActive = true;
|
|
7090
|
+
delete entry.turnError;
|
|
5252
7091
|
activePromptCounter++;
|
|
5253
7092
|
entry.sessionLastSeenAt = Date.now();
|
|
5254
7093
|
touchActivity();
|
|
@@ -5456,7 +7295,8 @@ function createAcpSessionBridge(opts) {
|
|
|
5456
7295
|
data: {
|
|
5457
7296
|
sessionId: entry.sessionId,
|
|
5458
7297
|
currentModelId: entry.currentModelId ?? null,
|
|
5459
|
-
currentApprovalMode: entry.currentApprovalMode ?? null
|
|
7298
|
+
currentApprovalMode: entry.currentApprovalMode ?? null,
|
|
7299
|
+
recordingDegraded: entry.recordingDegraded
|
|
5460
7300
|
}
|
|
5461
7301
|
}), "snapshotFrame");
|
|
5462
7302
|
async function* withSnapshot() {
|
|
@@ -5750,7 +7590,7 @@ function createAcpSessionBridge(opts) {
|
|
|
5750
7590
|
const r = res;
|
|
5751
7591
|
if (r && r.persisted === false) {
|
|
5752
7592
|
writeStderrLine(
|
|
5753
|
-
`qwen serve: displayName for ${sessionId} was not persisted
|
|
7593
|
+
`qwen serve: displayName for ${sessionId} was not persisted`
|
|
5754
7594
|
);
|
|
5755
7595
|
}
|
|
5756
7596
|
}).catch((err) => {
|
|
@@ -5771,9 +7611,10 @@ function createAcpSessionBridge(opts) {
|
|
|
5771
7611
|
}
|
|
5772
7612
|
return { displayName: entry.displayName };
|
|
5773
7613
|
},
|
|
5774
|
-
async getSessionArtifacts(sessionId) {
|
|
7614
|
+
async getSessionArtifacts(sessionId, context) {
|
|
5775
7615
|
const entry = byId.get(sessionId);
|
|
5776
7616
|
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
7617
|
+
resolveTrustedClientId(entry, context?.clientId);
|
|
5777
7618
|
return entry.artifacts.list();
|
|
5778
7619
|
},
|
|
5779
7620
|
async addSessionArtifact(sessionId, artifact, context) {
|
|
@@ -5781,9 +7622,13 @@ function createAcpSessionBridge(opts) {
|
|
|
5781
7622
|
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
5782
7623
|
const clientId = resolveTrustedClientId(entry, context?.clientId);
|
|
5783
7624
|
const input = makeClientArtifactInput(artifact, clientId);
|
|
5784
|
-
const result = await entry.artifacts.upsertMany([input], {
|
|
7625
|
+
const result = await entry.artifacts.upsertMany([input], {
|
|
7626
|
+
validationStrict: true,
|
|
7627
|
+
persistenceStrict: false
|
|
7628
|
+
});
|
|
5785
7629
|
publishArtifactChanges(entry, result.changes, clientId);
|
|
5786
|
-
|
|
7630
|
+
const warnings = [...result.warnings ?? []];
|
|
7631
|
+
return warnings.length > 0 ? { ...result, warnings } : result;
|
|
5787
7632
|
},
|
|
5788
7633
|
async removeSessionArtifact(sessionId, artifactId, context) {
|
|
5789
7634
|
const entry = byId.get(sessionId);
|
|
@@ -5791,7 +7636,8 @@ function createAcpSessionBridge(opts) {
|
|
|
5791
7636
|
const clientId = resolveTrustedClientId(entry, context?.clientId);
|
|
5792
7637
|
const result = await entry.artifacts.remove(artifactId, { clientId });
|
|
5793
7638
|
publishArtifactChanges(entry, result.changes, clientId);
|
|
5794
|
-
|
|
7639
|
+
const warnings = [...result.warnings ?? []];
|
|
7640
|
+
return warnings.length > 0 ? { ...result, warnings } : result;
|
|
5795
7641
|
},
|
|
5796
7642
|
listWorkspaceSessions(workspaceCwd) {
|
|
5797
7643
|
if (!path3.isAbsolute(workspaceCwd)) return [];
|
|
@@ -6076,6 +7922,35 @@ function createAcpSessionBridge(opts) {
|
|
|
6076
7922
|
SERVE_STATUS_EXT_METHODS.sessionLspStatus
|
|
6077
7923
|
);
|
|
6078
7924
|
},
|
|
7925
|
+
async getSessionTranscriptPage(req) {
|
|
7926
|
+
const info = await ensureChannel();
|
|
7927
|
+
try {
|
|
7928
|
+
const response = await withWorkspaceControl(
|
|
7929
|
+
info,
|
|
7930
|
+
() => withTimeout(
|
|
7931
|
+
Promise.race([
|
|
7932
|
+
info.connection.extMethod(
|
|
7933
|
+
SERVE_STATUS_EXT_METHODS.sessionTranscript,
|
|
7934
|
+
{ ...req, cwd: boundWorkspace }
|
|
7935
|
+
),
|
|
7936
|
+
getChannelClosedReject(info)
|
|
7937
|
+
]),
|
|
7938
|
+
Math.max(initTimeoutMs, SESSION_TRANSCRIPT_TIMEOUT_MS),
|
|
7939
|
+
SERVE_STATUS_EXT_METHODS.sessionTranscript
|
|
7940
|
+
)
|
|
7941
|
+
);
|
|
7942
|
+
return response;
|
|
7943
|
+
} catch (err) {
|
|
7944
|
+
if (isAcpSessionResourceNotFound(err, req.sessionId)) {
|
|
7945
|
+
throw new SessionNotFoundError(req.sessionId);
|
|
7946
|
+
}
|
|
7947
|
+
throw err;
|
|
7948
|
+
} finally {
|
|
7949
|
+
if (hasNoChannelWork(info)) {
|
|
7950
|
+
await startIdleTimer(info, "session transcript");
|
|
7951
|
+
}
|
|
7952
|
+
}
|
|
7953
|
+
},
|
|
6079
7954
|
async cancelSessionTask(sessionId, taskId, taskKind) {
|
|
6080
7955
|
return requestSessionStatus(
|
|
6081
7956
|
sessionId,
|
|
@@ -6314,109 +8189,12 @@ function createAcpSessionBridge(opts) {
|
|
|
6314
8189
|
entry,
|
|
6315
8190
|
context?.clientId
|
|
6316
8191
|
);
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
const approvalWork = entry.approvalModeQueue.then(async () => {
|
|
6323
|
-
entry.approvalModeRoundtripInFlight = true;
|
|
6324
|
-
let succeeded = false;
|
|
6325
|
-
try {
|
|
6326
|
-
const response = await Promise.race([
|
|
6327
|
-
withTimeout(
|
|
6328
|
-
entry.connection.extMethod(
|
|
6329
|
-
SERVE_CONTROL_EXT_METHODS.sessionApprovalMode,
|
|
6330
|
-
{ sessionId, mode }
|
|
6331
|
-
),
|
|
6332
|
-
initTimeoutMs,
|
|
6333
|
-
SERVE_CONTROL_EXT_METHODS.sessionApprovalMode
|
|
6334
|
-
),
|
|
6335
|
-
getTransportClosedReject(entry)
|
|
6336
|
-
]);
|
|
6337
|
-
if (typeof response.current !== "string" || !KNOWN_APPROVAL_MODES.has(response.current)) {
|
|
6338
|
-
throw new Error(
|
|
6339
|
-
`Agent returned unknown approval mode: ${JSON.stringify(response.current)}`
|
|
6340
|
-
);
|
|
6341
|
-
}
|
|
6342
|
-
let persisted = false;
|
|
6343
|
-
if (opts2.persist) {
|
|
6344
|
-
try {
|
|
6345
|
-
await withTimeout(
|
|
6346
|
-
persistApprovalMode?.(boundWorkspace, mode) ?? Promise.resolve(),
|
|
6347
|
-
PERSIST_TIMEOUT_MS,
|
|
6348
|
-
"persistApprovalMode"
|
|
6349
|
-
);
|
|
6350
|
-
persisted = persistApprovalMode !== void 0;
|
|
6351
|
-
} catch (err) {
|
|
6352
|
-
writeStderrLine(
|
|
6353
|
-
`setSessionApprovalMode: persist failed: ${err instanceof Error ? err.message : String(err)}`
|
|
6354
|
-
);
|
|
6355
|
-
}
|
|
6356
|
-
}
|
|
6357
|
-
publishApprovalModeChanged(
|
|
6358
|
-
entry,
|
|
6359
|
-
{
|
|
6360
|
-
previous: response.previous,
|
|
6361
|
-
next: response.current,
|
|
6362
|
-
persisted
|
|
6363
|
-
},
|
|
6364
|
-
originatorClientId
|
|
6365
|
-
);
|
|
6366
|
-
if (persisted) {
|
|
6367
|
-
broadcastWorkspaceEvent(
|
|
6368
|
-
{
|
|
6369
|
-
type: "approval_mode_changed",
|
|
6370
|
-
data: {
|
|
6371
|
-
sessionId: entry.sessionId,
|
|
6372
|
-
previous: response.previous,
|
|
6373
|
-
next: response.current,
|
|
6374
|
-
persisted
|
|
6375
|
-
},
|
|
6376
|
-
...originatorClientId ? { originatorClientId } : {}
|
|
6377
|
-
},
|
|
6378
|
-
entry.sessionId
|
|
6379
|
-
);
|
|
6380
|
-
for (const peer of byId.values()) {
|
|
6381
|
-
if (peer.sessionId === entry.sessionId) {
|
|
6382
|
-
continue;
|
|
6383
|
-
}
|
|
6384
|
-
peer.currentApprovalMode = response.current;
|
|
6385
|
-
}
|
|
6386
|
-
}
|
|
6387
|
-
succeeded = true;
|
|
6388
|
-
return {
|
|
6389
|
-
sessionId: entry.sessionId,
|
|
6390
|
-
mode: response.current,
|
|
6391
|
-
previous: response.previous,
|
|
6392
|
-
persisted
|
|
6393
|
-
};
|
|
6394
|
-
} finally {
|
|
6395
|
-
entry.approvalModeRoundtripInFlight = false;
|
|
6396
|
-
if (succeeded) {
|
|
6397
|
-
void reconcileAfterRoundtrip(entry, "approvalMode");
|
|
6398
|
-
} else {
|
|
6399
|
-
writeStderrLine(
|
|
6400
|
-
`[reconcile] session=${entry.sessionId} target=approvalMode action=skipped reason=roundtrip_failed`
|
|
6401
|
-
);
|
|
6402
|
-
}
|
|
6403
|
-
}
|
|
6404
|
-
});
|
|
6405
|
-
entry.approvalModeQueue = approvalWork.then(
|
|
6406
|
-
() => void 0,
|
|
6407
|
-
() => void 0
|
|
8192
|
+
return await applyApprovalMode(
|
|
8193
|
+
entry,
|
|
8194
|
+
mode,
|
|
8195
|
+
opts2.persist,
|
|
8196
|
+
originatorClientId
|
|
6408
8197
|
);
|
|
6409
|
-
try {
|
|
6410
|
-
return await approvalWork;
|
|
6411
|
-
} catch (err) {
|
|
6412
|
-
const data = err?.data;
|
|
6413
|
-
if (data && typeof data === "object" && "errorKind" in data && data.errorKind === "trust_gate") {
|
|
6414
|
-
const rawMessage = err?.message;
|
|
6415
|
-
const message = typeof rawMessage === "string" ? rawMessage : "Trust-gate rejection from ACP child";
|
|
6416
|
-
throw new TrustGateError(message);
|
|
6417
|
-
}
|
|
6418
|
-
throw err;
|
|
6419
|
-
}
|
|
6420
8198
|
},
|
|
6421
8199
|
async generateSessionRecap(sessionId, _context) {
|
|
6422
8200
|
const entry = byId.get(sessionId);
|
|
@@ -6769,6 +8547,48 @@ function createAcpSessionBridge(opts) {
|
|
|
6769
8547
|
const targetTurnIndex = response["targetTurnIndex"] ?? 0;
|
|
6770
8548
|
const filesChanged = response["filesChanged"] ?? [];
|
|
6771
8549
|
const filesFailed = response["filesFailed"] ?? [];
|
|
8550
|
+
const artifactSnapshot = restoredArtifactSnapshotFromState(
|
|
8551
|
+
response
|
|
8552
|
+
);
|
|
8553
|
+
const artifactSnapshotUnavailable = artifactSnapshotUnavailableReason(
|
|
8554
|
+
response
|
|
8555
|
+
);
|
|
8556
|
+
const beforeArtifacts = (await entry.artifacts.list()).artifacts;
|
|
8557
|
+
const shouldRestoreArtifactSnapshot = artifactSnapshot !== void 0 && artifactSnapshotUnavailable === void 0;
|
|
8558
|
+
const artifactRestoreWarnings = artifactSnapshotUnavailable !== void 0 ? [
|
|
8559
|
+
`artifact snapshot rebuild unavailable during rewind: ${artifactSnapshotUnavailable}`
|
|
8560
|
+
] : shouldRestoreArtifactSnapshot ? await entry.artifacts.restore(artifactSnapshot, {
|
|
8561
|
+
preserveLiveEphemeral: true
|
|
8562
|
+
}) : [];
|
|
8563
|
+
const artifactRestoreFailed = artifactRestoreWarnings.some(
|
|
8564
|
+
isArtifactRestoreFailureWarning
|
|
8565
|
+
);
|
|
8566
|
+
const shouldRecordArtifactSnapshot = shouldRestoreArtifactSnapshot && !artifactRestoreFailed;
|
|
8567
|
+
const artifactSnapshotWarnings = shouldRecordArtifactSnapshot ? await entry.artifacts.recordSnapshot() : [];
|
|
8568
|
+
const artifactWarnings = [
|
|
8569
|
+
...artifactRestoreWarnings,
|
|
8570
|
+
...artifactSnapshotWarnings
|
|
8571
|
+
];
|
|
8572
|
+
for (const warning of artifactRestoreWarnings) {
|
|
8573
|
+
writeStderrLine(
|
|
8574
|
+
`[artifacts] session=${entry.sessionId} action=rewind_restore_warning warning=${JSON.stringify(
|
|
8575
|
+
warning
|
|
8576
|
+
)}`
|
|
8577
|
+
);
|
|
8578
|
+
}
|
|
8579
|
+
for (const warning of artifactSnapshotWarnings) {
|
|
8580
|
+
writeStderrLine(
|
|
8581
|
+
`[artifacts] session=${entry.sessionId} action=rewind_snapshot_warning warning=${JSON.stringify(
|
|
8582
|
+
warning
|
|
8583
|
+
)}`
|
|
8584
|
+
);
|
|
8585
|
+
}
|
|
8586
|
+
const afterArtifacts = (await entry.artifacts.list()).artifacts;
|
|
8587
|
+
publishArtifactChanges(
|
|
8588
|
+
entry,
|
|
8589
|
+
artifactReseedChanges(beforeArtifacts, afterArtifacts),
|
|
8590
|
+
originatorClientId
|
|
8591
|
+
);
|
|
6772
8592
|
try {
|
|
6773
8593
|
entry.events.publish({
|
|
6774
8594
|
type: "session_rewound",
|
|
@@ -6777,7 +8597,8 @@ function createAcpSessionBridge(opts) {
|
|
|
6777
8597
|
promptId: req.promptId,
|
|
6778
8598
|
targetTurnIndex,
|
|
6779
8599
|
filesChanged,
|
|
6780
|
-
filesFailed
|
|
8600
|
+
filesFailed,
|
|
8601
|
+
...artifactWarnings.length > 0 ? { warnings: artifactWarnings } : {}
|
|
6781
8602
|
},
|
|
6782
8603
|
...originatorClientId ? { originatorClientId } : {}
|
|
6783
8604
|
});
|
|
@@ -6787,7 +8608,8 @@ function createAcpSessionBridge(opts) {
|
|
|
6787
8608
|
rewound: filesFailed.length === 0,
|
|
6788
8609
|
targetTurnIndex,
|
|
6789
8610
|
filesChanged,
|
|
6790
|
-
filesFailed
|
|
8611
|
+
filesFailed,
|
|
8612
|
+
...artifactWarnings.length > 0 ? { warnings: artifactWarnings } : {}
|
|
6791
8613
|
};
|
|
6792
8614
|
},
|
|
6793
8615
|
async manageMcpServer(serverName, action, originatorClientId) {
|
|
@@ -6915,6 +8737,7 @@ function createAcpSessionBridge(opts) {
|
|
|
6915
8737
|
}
|
|
6916
8738
|
permissionMediator.forgetSession(sessionId);
|
|
6917
8739
|
entry.pendingPermissionIds.clear();
|
|
8740
|
+
entry.pendingInteractions.clear();
|
|
6918
8741
|
if (entry.promptActive) {
|
|
6919
8742
|
entry.promptActive = false;
|
|
6920
8743
|
activePromptCounter--;
|
|
@@ -6923,6 +8746,12 @@ function createAcpSessionBridge(opts) {
|
|
|
6923
8746
|
if (defaultEntry === entry) defaultEntry = void 0;
|
|
6924
8747
|
byId.delete(sessionId);
|
|
6925
8748
|
telemetry.metrics?.sessionLifecycle("die");
|
|
8749
|
+
emitSessionLifecycle({
|
|
8750
|
+
type: "removed",
|
|
8751
|
+
sessionId,
|
|
8752
|
+
workspaceCwd: entry.workspaceCwd,
|
|
8753
|
+
reason: "killed"
|
|
8754
|
+
});
|
|
6926
8755
|
const ci = channelInfoForEntry(entry);
|
|
6927
8756
|
if (!ci) {
|
|
6928
8757
|
writeStderrLine(
|
|
@@ -6972,8 +8801,17 @@ function createAcpSessionBridge(opts) {
|
|
|
6972
8801
|
cancelIdleTimer();
|
|
6973
8802
|
stopSessionReaper();
|
|
6974
8803
|
const channels = Array.from(aliveChannels);
|
|
8804
|
+
const entries = Array.from(byId.values());
|
|
6975
8805
|
defaultEntry = void 0;
|
|
6976
8806
|
byId.clear();
|
|
8807
|
+
for (const entry of entries) {
|
|
8808
|
+
emitSessionLifecycle({
|
|
8809
|
+
type: "removed",
|
|
8810
|
+
sessionId: entry.sessionId,
|
|
8811
|
+
workspaceCwd: entry.workspaceCwd,
|
|
8812
|
+
reason: "kill_all"
|
|
8813
|
+
});
|
|
8814
|
+
}
|
|
6977
8815
|
for (const info of channels) {
|
|
6978
8816
|
try {
|
|
6979
8817
|
info.channel.killSync();
|
|
@@ -6981,53 +8819,71 @@ function createAcpSessionBridge(opts) {
|
|
|
6981
8819
|
}
|
|
6982
8820
|
}
|
|
6983
8821
|
},
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
7000
|
-
|
|
7001
|
-
|
|
7002
|
-
|
|
8822
|
+
shutdown(options) {
|
|
8823
|
+
if (shutdownPromise) return shutdownPromise;
|
|
8824
|
+
const shutdownReason = options?.reason ?? "daemon_shutdown";
|
|
8825
|
+
let resolveShutdown;
|
|
8826
|
+
let rejectShutdown;
|
|
8827
|
+
shutdownPromise = new Promise((resolve2, reject) => {
|
|
8828
|
+
resolveShutdown = resolve2;
|
|
8829
|
+
rejectShutdown = reject;
|
|
8830
|
+
});
|
|
8831
|
+
void (async () => {
|
|
8832
|
+
shuttingDown = true;
|
|
8833
|
+
cancelIdleTimer();
|
|
8834
|
+
stopSessionReaper();
|
|
8835
|
+
const entries = Array.from(byId.values());
|
|
8836
|
+
const channels = Array.from(aliveChannels);
|
|
8837
|
+
for (const ci of channels) ci.isDying = true;
|
|
8838
|
+
for (const e of entries) {
|
|
8839
|
+
permissionMediator.forgetSession(e.sessionId);
|
|
8840
|
+
e.pendingPermissionIds.clear();
|
|
8841
|
+
e.pendingInteractions.clear();
|
|
8842
|
+
}
|
|
8843
|
+
defaultEntry = void 0;
|
|
8844
|
+
byId.clear();
|
|
8845
|
+
for (const e of entries) {
|
|
8846
|
+
telemetry.metrics?.sessionLifecycle("die");
|
|
8847
|
+
emitSessionLifecycle({
|
|
8848
|
+
type: "removed",
|
|
8849
|
+
sessionId: e.sessionId,
|
|
8850
|
+
workspaceCwd: e.workspaceCwd,
|
|
8851
|
+
reason: shutdownReason
|
|
7003
8852
|
});
|
|
7004
|
-
|
|
8853
|
+
try {
|
|
8854
|
+
e.events.publish({
|
|
8855
|
+
type: "session_died",
|
|
8856
|
+
data: { sessionId: e.sessionId, reason: shutdownReason }
|
|
8857
|
+
});
|
|
8858
|
+
} catch {
|
|
8859
|
+
}
|
|
8860
|
+
e.events.close();
|
|
7005
8861
|
}
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
)
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
8862
|
+
const inFlightSessionAwaits = Array.from(inFlightSpawns.values()).map(
|
|
8863
|
+
(p) => p.then(
|
|
8864
|
+
() => void 0,
|
|
8865
|
+
() => void 0
|
|
8866
|
+
)
|
|
8867
|
+
);
|
|
8868
|
+
const inFlightRestoreAwaits = Array.from(inFlightRestores.values()).map(
|
|
8869
|
+
(restore) => restore.promise.then(
|
|
8870
|
+
() => void 0,
|
|
8871
|
+
() => void 0
|
|
8872
|
+
)
|
|
8873
|
+
);
|
|
8874
|
+
const inFlightChannelAwait = inFlightChannelSpawn ? inFlightChannelSpawn.then(
|
|
7016
8875
|
() => void 0,
|
|
7017
8876
|
() => void 0
|
|
7018
|
-
)
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
...inFlightRestoreAwaits,
|
|
7029
|
-
inFlightChannelAwait
|
|
7030
|
-
]);
|
|
8877
|
+
) : Promise.resolve();
|
|
8878
|
+
await Promise.all([
|
|
8879
|
+
...channels.map((ci) => ci.channel.kill().catch(() => {
|
|
8880
|
+
})),
|
|
8881
|
+
...inFlightSessionAwaits,
|
|
8882
|
+
...inFlightRestoreAwaits,
|
|
8883
|
+
inFlightChannelAwait
|
|
8884
|
+
]);
|
|
8885
|
+
})().then(resolveShutdown, rejectShutdown);
|
|
8886
|
+
return shutdownPromise;
|
|
7031
8887
|
},
|
|
7032
8888
|
async preheat() {
|
|
7033
8889
|
if (shuttingDown) return;
|
|
@@ -7057,6 +8913,7 @@ var createHttpAcpBridge = createAcpSessionBridge;
|
|
|
7057
8913
|
|
|
7058
8914
|
export {
|
|
7059
8915
|
SessionArtifactValidationError,
|
|
8916
|
+
SessionArtifactAuthorizationError,
|
|
7060
8917
|
extractErrorMessage,
|
|
7061
8918
|
extractErrorCode,
|
|
7062
8919
|
classifyTurnErrorKind,
|