@qwen-code/qwen-code 0.19.8 → 0.19.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled/qc-helper/SKILL.md +1 -0
- package/bundled/qc-helper/docs/configuration/auth.md +40 -55
- package/bundled/qc-helper/docs/configuration/model-providers.md +224 -218
- package/bundled/qc-helper/docs/configuration/settings.md +60 -51
- package/bundled/qc-helper/docs/features/_meta.ts +1 -0
- package/bundled/qc-helper/docs/features/channels/dingtalk.md +9 -2
- package/bundled/qc-helper/docs/features/channels/overview.md +71 -4
- package/bundled/qc-helper/docs/features/channels/wecom.md +6 -0
- package/bundled/qc-helper/docs/features/code-review.md +29 -5
- package/bundled/qc-helper/docs/features/commands.md +39 -38
- package/bundled/qc-helper/docs/features/computer-use.md +77 -0
- package/bundled/qc-helper/docs/features/tool-use-summaries.md +19 -23
- package/bundled/qc-helper/docs/integration-vscode.md +2 -2
- package/bundled/qc-helper/docs/qwen-serve.md +29 -12
- package/bundled/qc-helper/docs/reference/keyboard-shortcuts.md +1 -1
- package/bundled/review/DESIGN.md +93 -22
- package/bundled/review/SKILL.md +295 -86
- package/chunks/MaxSizedBox-6HYJ3ZPJ.js +70 -0
- package/chunks/{StandaloneSessionPicker-EYRGH6PJ.js → StandaloneSessionPicker-WS4GXYOG.js} +79 -79
- package/chunks/{acpAgent-GRFTTRHW.js → acpAgent-5FWSOZL5.js} +968 -200
- package/chunks/agent-HOLHB3RT.js +68 -0
- package/chunks/agent-headless-SXL6H56A.js +62 -0
- package/chunks/{anthropicContentGenerator-CPF22MA7.js → anthropicContentGenerator-L73TO3DB.js} +187 -31
- package/chunks/{artifact-tool-D233YJFH.js → artifact-tool-U7IRTLIV.js} +5 -5
- package/chunks/{askUserQuestion-MKL5ZTMC.js → askUserQuestion-2DLUUZZG.js} +6 -6
- package/chunks/bridge-BCJQ7BBB.js +78 -0
- package/chunks/{ca-MKD32FQL.js → ca-UJ6HC4H2.js} +64 -5
- package/chunks/{channel-worker-supervisor-NZQUOEFJ.js → channel-worker-supervisor-WSOAAYUO.js} +137 -13
- package/chunks/{chunk-QQDPRDVW.js → chunk-2HRYPZT5.js} +1 -1
- package/chunks/{chunk-2PSWA5ID.js → chunk-2X56YUSZ.js} +1 -1
- package/chunks/{chunk-DIWVZ3VM.js → chunk-34MW4KTI.js} +5 -5
- package/chunks/{chunk-IBY3Q3HG.js → chunk-34Z3U5RQ.js} +508 -126
- package/chunks/{chunk-UXEW557D.js → chunk-3AHXV67U.js} +3 -3
- package/chunks/{chunk-O2YT7VB4.js → chunk-3BTRNWWD.js} +1 -1
- package/chunks/chunk-3G7CIHVV.js +71 -0
- package/chunks/{chunk-FBET5XZ2.js → chunk-3SJSQXNS.js} +20 -5
- package/chunks/{chunk-BUZ6HGL4.js → chunk-3VAGG5T5.js} +248 -55
- package/chunks/{chunk-M7S4GK5Z.js → chunk-4C5BQ2D2.js} +74 -27
- package/chunks/{chunk-5MBO76SL.js → chunk-4GORAUCN.js} +3 -3
- package/chunks/{chunk-55ZMG67I.js → chunk-4LD5APFL.js} +4 -4
- package/chunks/{chunk-TLWA7TPR.js → chunk-4QLQE5WJ.js} +2 -2
- package/chunks/{chunk-UIDXQNMV.js → chunk-4WGODMVR.js} +1 -1
- package/chunks/{chunk-E6US47KI.js → chunk-4WKAA4KS.js} +1 -1
- package/chunks/{chunk-H6EMK6QK.js → chunk-4ZVWQHEQ.js} +11913 -5207
- package/chunks/{chunk-HW4N45EE.js → chunk-524RQP4Q.js} +29 -8
- package/chunks/{chunk-3GONHQOA.js → chunk-57OAHC2Y.js} +1 -1
- package/chunks/{chunk-JZSY4WP3.js → chunk-5DTTYNB7.js} +1 -1
- package/chunks/{chunk-YDOXPLYL.js → chunk-5HOMWMCL.js} +1147 -738
- package/chunks/{chunk-A4BMJM77.js → chunk-5O2XNYP6.js} +1 -0
- package/chunks/{chunk-ZL5HN75Q.js → chunk-5SFOMFG4.js} +2 -2
- package/chunks/{chunk-2EM7ECZD.js → chunk-5U27BZ72.js} +179 -63
- package/chunks/{chunk-JRQ7EVGE.js → chunk-5VXNZWGF.js} +2 -2
- package/chunks/{chunk-QYNNN7BK.js → chunk-5XUCZNSY.js} +2 -2
- package/chunks/{chunk-GO6LNQXT.js → chunk-6AM5D4ME.js} +1 -1
- package/chunks/{chunk-BJBWRCSK.js → chunk-6H67XLET.js} +1 -1
- package/chunks/{chunk-PUVCVELY.js → chunk-6PESIEKJ.js} +4 -4
- 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-77XJ3SWN.js} +10 -12
- package/chunks/{chunk-L46XKEGM.js → chunk-7PU4FYLG.js} +5 -8
- package/chunks/{chunk-NQKLOAVJ.js → chunk-7R3CZA3Q.js} +2 -2
- package/chunks/{chunk-SAIILPQ3.js → chunk-7X7TEQNX.js} +5 -3
- package/chunks/{chunk-OQXDAMKY.js → chunk-A3XKDTSH.js} +8 -8
- package/chunks/{chunk-IRJWHZWU.js → chunk-A4UCTEB5.js} +2 -2
- package/chunks/{chunk-TSPB77TG.js → chunk-AKIVHSJR.js} +923 -2892
- package/chunks/{chunk-FQNC62B2.js → chunk-ATAKVRIF.js} +11 -11
- package/chunks/{chunk-GSHQYYIV.js → chunk-AURZZYMD.js} +6 -26
- package/chunks/{chunk-CHB5SLZZ.js → chunk-B4G2WOFA.js} +3 -3
- package/chunks/{chunk-X3YGAX7V.js → chunk-B5ORX3FG.js} +4 -2
- package/chunks/chunk-BAPGSC7U.js +239 -0
- package/chunks/{chunk-UX6OTBVD.js → chunk-BMGVRZN4.js} +2 -2
- package/chunks/{chunk-ZN5T4BHI.js → chunk-BPZALHVR.js} +2 -2
- package/chunks/{chunk-IJRL2S3D.js → chunk-BZIFY5TF.js} +6 -6
- package/chunks/{chunk-OFEVLU4C.js → chunk-CARU2RR2.js} +1 -1
- package/chunks/chunk-CC2ITGCF.js +176 -0
- package/chunks/{chunk-HDOH3QYE.js → chunk-CCDMCZPP.js} +2 -2
- package/chunks/{chunk-FV7425LN.js → chunk-CEA3E3JB.js} +18 -5
- package/chunks/{chunk-BOBOCR5H.js → chunk-CLFKOZEK.js} +4 -4
- package/chunks/{chunk-P3XEEI3X.js → chunk-CNEIYWSA.js} +38 -9
- 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-5P5XGNYH.js → chunk-CU3L64TP.js} +1 -1
- package/chunks/{chunk-CD6USWHZ.js → chunk-CUMMSHH6.js} +2 -2
- package/chunks/{chunk-IWKSG2AR.js → chunk-CXAOG665.js} +1 -1
- package/chunks/{chunk-WQTHLB5T.js → chunk-D2GFWEXB.js} +7 -7
- package/chunks/{chunk-I6WGFJJ3.js → chunk-D5SKC4YG.js} +38 -12
- package/chunks/{chunk-MZ7BABX3.js → chunk-DC5Z3DU6.js} +2 -2
- package/chunks/{chunk-64WXLC72.js → chunk-DJ2GSRLV.js} +1 -1
- package/chunks/{chunk-QN5NZ3UQ.js → chunk-DMTGGOSA.js} +2 -2
- package/chunks/{chunk-A5JOJ77H.js → chunk-E2IY465S.js} +38 -15
- package/chunks/{chunk-KHDZHZMH.js → chunk-E5Z2AVNV.js} +1 -1
- package/chunks/{chunk-SQ2UATK7.js → chunk-EAXLGSFP.js} +11 -11
- package/chunks/{chunk-JCH35JB3.js → chunk-ECNYL4AA.js} +2 -2
- package/chunks/{chunk-E7EMCSUG.js → chunk-EGYZ275M.js} +64 -42
- package/chunks/{chunk-6VK6FIMQ.js → chunk-EMDMPBJ4.js} +415 -379
- package/chunks/{chunk-5IFG2VC4.js → chunk-FCMNKVXT.js} +1 -1
- package/chunks/{chunk-V35MD7KN.js → chunk-GRC5HGPI.js} +1 -1
- package/chunks/{chunk-WMPVYQ4P.js → chunk-GWHYEOV2.js} +2 -2
- package/chunks/{chunk-27BHARDE.js → chunk-GWLSNBG4.js} +2103 -13
- package/chunks/chunk-GZRTYZ2R.js +292 -0
- package/chunks/{chunk-A2X3KLC3.js → chunk-GZXCZHCT.js} +3 -3
- package/chunks/{chunk-K5PGHDBN.js → chunk-H6XPXXMH.js} +1 -1
- package/chunks/{chunk-DICGXFAI.js → chunk-HKMAENAO.js} +8 -8
- package/chunks/{chunk-ZE6E424U.js → chunk-HPDUL5MF.js} +2 -2
- package/chunks/{chunk-KYMBIKIW.js → chunk-HTHUX2T5.js} +1 -1
- package/chunks/{chunk-LQ7TMOCE.js → chunk-HTO4JFDZ.js} +1 -1
- package/chunks/{chunk-ON3JA3RU.js → chunk-HVY356M2.js} +7 -9
- package/chunks/{chunk-GGQQA4JW.js → chunk-IAVB3HM3.js} +2 -2
- package/chunks/{chunk-FEJ2FZ3U.js → chunk-J3JA76CF.js} +2 -2
- package/chunks/{chunk-4CGOWHKM.js → chunk-J4MOFEOK.js} +2 -2
- package/chunks/{chunk-22MNNNXE.js → chunk-J4TSQ5EQ.js} +3 -2
- package/chunks/{chunk-22IFUCVR.js → chunk-JESCGQM3.js} +1 -1
- package/chunks/{chunk-CCGOFQV3.js → chunk-JSG7ZSBC.js} +8 -7
- package/chunks/{chunk-WF4FAXDL.js → chunk-JZETEN3Z.js} +62 -62
- package/chunks/{chunk-GN3F6PIU.js → chunk-K434OPES.js} +5 -5
- package/chunks/{chunk-YKZIAK7C.js → chunk-KHUQZZJ6.js} +66 -16
- package/chunks/{chunk-X2XNQQJI.js → chunk-KKNMOEZH.js} +4 -4
- package/chunks/{chunk-MRO43B25.js → chunk-KW7NOTN6.js} +1 -1
- package/chunks/{chunk-6IAWPIQ6.js → chunk-L36P3NBD.js} +3 -3
- package/chunks/{chunk-54YZOP7P.js → chunk-L573Z2QZ.js} +11894 -5190
- package/chunks/{chunk-DHOLNQLC.js → chunk-L6P4KPHR.js} +12 -12
- package/chunks/{chunk-E7KNELIZ.js → chunk-LK7KUE55.js} +2 -2
- package/chunks/{chunk-W7EEOR7D.js → chunk-LUODI7WD.js} +5 -5
- package/chunks/{chunk-CSOTA7KX.js → chunk-LXNEJD34.js} +2 -2
- package/chunks/{chunk-HLKG6CZN.js → chunk-M5GB774H.js} +1 -1
- package/chunks/{chunk-3B26UVCX.js → chunk-MC23XKFE.js} +39 -10
- package/chunks/{chunk-PC4ZXHU2.js → chunk-MXL5UP4X.js} +3 -3
- package/chunks/{chunk-BVYU3ZTI.js → chunk-N3EMS75V.js} +73 -30
- package/chunks/{chunk-332PWN27.js → chunk-NHDKSOEY.js} +1 -1
- package/chunks/{chunk-3GNOQZDC.js → chunk-NVYTCB5Z.js} +4797 -2620
- package/chunks/{chunk-P7M4GA57.js → chunk-OCPBI7J5.js} +1 -1
- package/chunks/chunk-OQ4DRIOA.js +296 -0
- package/chunks/{chunk-42XGFRJS.js → chunk-ORBGV7NJ.js} +5 -5
- package/chunks/{chunk-AYNCLGTW.js → chunk-P67PFTUT.js} +8 -51
- package/chunks/{chunk-VGGOPZUL.js → chunk-PDZ7MBDG.js} +12 -5
- package/chunks/{chunk-WHJQ3JUS.js → chunk-PODM4SZ4.js} +12 -12
- package/chunks/{chunk-JPALE3PA.js → chunk-PVZM22CK.js} +6 -6
- package/chunks/{chunk-AGHU5VRK.js → chunk-PZL23GTM.js} +1 -1
- package/chunks/{chunk-L4AMBVQG.js → chunk-Q4YQW46J.js} +31 -17
- package/chunks/{chunk-XYR5RUA5.js → chunk-Q6I5QHMH.js} +10 -3
- package/chunks/{chunk-FXGLL2HL.js → chunk-Q7YDKOGY.js} +1 -1
- 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-XOHNA2S6.js → chunk-QYMQSECS.js} +3 -3
- package/chunks/{chunk-QYUE6W3T.js → chunk-RDJSGHZE.js} +1 -1
- package/chunks/{chunk-TLRYABYP.js → chunk-RKUWKYED.js} +1 -1
- package/chunks/{chunk-RNBYOUGV.js → chunk-RXFQM6FQ.js} +1 -1
- package/chunks/{chunk-QXPI7FEC.js → chunk-S3TWXGKA.js} +4 -4
- package/chunks/{chunk-BOBRZZKD.js → chunk-SCGXUJBT.js} +5 -5
- package/chunks/{chunk-44NROQYV.js → chunk-SJHDYNHF.js} +4 -4
- package/chunks/{chunk-MLZQVCF3.js → chunk-SZRN75WQ.js} +1 -1
- package/chunks/{chunk-7RYW5LQV.js → chunk-T2NCM2ET.js} +2 -2
- package/chunks/{chunk-Z2Z3GUXZ.js → chunk-TB6UDU4T.js} +1 -1
- package/chunks/{chunk-SUAKHHK7.js → chunk-TDEXZKMT.js} +3 -3
- package/chunks/{chunk-EXPMGZZV.js → chunk-TEGEBB2I.js} +1 -1
- package/chunks/{chunk-MX2YXRER.js → chunk-TJTKSVIV.js} +1 -1
- package/chunks/chunk-TO6QE22P.js +444 -0
- package/chunks/{chunk-H6BD2ELD.js → chunk-TYAMGABM.js} +2 -2
- package/chunks/chunk-U2NNEY4R.js +38 -0
- package/chunks/{chunk-VRDMOSQS.js → chunk-UDXSXLER.js} +2 -2
- package/chunks/{chunk-AHNJGLFV.js → chunk-UOZWKTQG.js} +3 -3
- package/chunks/{chunk-CJCKDMWO.js → chunk-URK542T4.js} +1 -1
- package/chunks/{chunk-AHNXYU4O.js → chunk-V6WHFGST.js} +1 -1
- package/chunks/{chunk-DCZWC4PF.js → chunk-VA4KQY6V.js} +5 -5
- 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-J7RCB6N5.js → chunk-VTU57BWZ.js} +2 -2
- package/chunks/{chunk-LAXNEOBG.js → chunk-VWPVHAMO.js} +15 -3
- package/chunks/{chunk-VZ7HY3YU.js → chunk-W2UIXQDL.js} +3 -3
- package/chunks/{chunk-JZHIQJOE.js → chunk-W3JHSWRP.js} +6 -6
- package/chunks/{chunk-ER3BKOLB.js → chunk-W7FJ3N32.js} +1 -1
- package/chunks/{chunk-3HX5LZ6R.js → chunk-WBL3FJEU.js} +2 -2
- package/chunks/{chunk-BMFMJINR.js → chunk-WL4L7EZH.js} +1 -1
- package/chunks/{chunk-PMFULMLX.js → chunk-WQUQVGTF.js} +2057 -266
- package/chunks/chunk-X4NKFGFJ.js +81 -0
- package/chunks/{chunk-YHKAK72D.js → chunk-X6TJ3YZV.js} +102 -16
- package/chunks/{chunk-3RW4AZJV.js → chunk-XFSFZCM5.js} +1 -1
- package/chunks/{chunk-O7FIRWJV.js → chunk-XLA62MYB.js} +3 -3
- package/chunks/{chunk-2MPVVENX.js → chunk-XU457J4V.js} +2 -2
- package/chunks/{chunk-EEN2TKST.js → chunk-XUOOWUZS.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-TI3BWD4D.js → chunk-XYQSMABS.js} +39 -19
- package/chunks/chunk-YAF2MQZ6.js +19 -0
- package/chunks/{chunk-O33TBKDC.js → chunk-YAUFFXDS.js} +154 -19
- package/chunks/{chunk-OACJLMLD.js → chunk-YLV3XXFB.js} +2 -2
- package/chunks/{chunk-BR4QREVK.js → chunk-YQ3U5MUC.js} +1 -1
- package/chunks/{chunk-Y6Z2O3WR.js → chunk-YUZI3WAC.js} +1 -1
- package/chunks/{chunk-II73RK2S.js → chunk-YVEDCAUE.js} +1 -1
- package/chunks/{chunk-SKAZWEV5.js → chunk-ZJE7NSL3.js} +1 -1
- package/chunks/{chunk-7LTB54MK.js → chunk-ZLXQN2QS.js} +2 -2
- package/chunks/cli-entry-path-4VJ3Y6T2.js +10 -0
- package/chunks/{computer-use-WMY3UOBX.js → computer-use-HM37RX6A.js} +50 -50
- package/chunks/config-utils-NV2SVGPB.js +23 -0
- package/chunks/contextCommand-OF5SFGH2.js +68 -0
- package/chunks/create-sub-session-INX7TXJI.js +207 -0
- package/chunks/create-sub-session-KPRT7ECJ.js +375 -0
- package/chunks/{cron-create-JSXEIPVS.js → cron-create-AMBKGZX6.js} +9 -9
- package/chunks/{cron-delete-OIFVEPGU.js → cron-delete-LRU4OJYG.js} +7 -7
- package/chunks/{cron-list-D4ZX4IOE.js → cron-list-WVP2JXER.js} +14 -10
- package/chunks/{daemon-WPAENEBW.js → daemon-ARHTSTXP.js} +478 -6
- package/chunks/daemon-status-provider-B2UTZUJ2.js +76 -0
- package/chunks/{de-ZFPPQVSF.js → de-EBJN3OVN.js} +64 -5
- package/chunks/{devtools-FM6GJPYG.js → devtools-4QFYJT6U.js} +3 -3
- package/chunks/{dist-Z34QPAQW.js → dist-PNVLTKTL.js} +374 -78
- package/chunks/{dist-63IS3ZMI.js → dist-PZV5RKM6.js} +4 -4
- package/chunks/{dist-BEXOZXLQ.js → dist-QYCAEZIT.js} +32 -15
- package/chunks/{dist-H2AEJCZ5.js → dist-R7LN5AZE.js} +5 -5
- package/chunks/{dist-E6UANPIM.js → dist-VHV4EVHG.js} +1011 -197
- package/chunks/{dist-O2IRTRPP.js → dist-WKPOYU7O.js} +10 -10
- package/chunks/{dist-SCFLLYMK.js → dist-XKU3ABM5.js} +12 -11
- package/chunks/earlyInputCapture-3VTHRC4C.js +69 -0
- package/chunks/{edit-OTYWAXGP.js → edit-VNI4NJ73.js} +51 -51
- package/chunks/{en-N4EPZAQI.js → en-5A6LA7RK.js} +74 -6
- package/chunks/{enter-worktree-HNYBDVCX.js → enter-worktree-IYYBKPZT.js} +50 -50
- package/chunks/{enterPlanMode-CJCNGT63.js → enterPlanMode-GMC5FD5A.js} +50 -50
- package/chunks/environment-5HSSRMJ7.js +88 -0
- package/chunks/errors-HMFCJJ7P.js +75 -0
- package/chunks/{exit-worktree-DUUKOE3X.js → exit-worktree-6BHCMP5G.js} +50 -50
- package/chunks/{exitPlanMode-AMMSXOAJ.js → exitPlanMode-CJW7G74W.js} +51 -51
- package/chunks/{fast-path-OV6NIP3K.js → fast-path-FRDMDVHG.js} +8 -8
- package/chunks/{fast-path-settings-7RIFI3PK.js → fast-path-settings-5QZE64HR.js} +6 -6
- package/chunks/{fileFromPath-IBEHA3CO.js → fileFromPath-UDALK7FM.js} +3 -3
- package/chunks/{fr-5WWMY53B.js → fr-YXRABLYZ.js} +64 -5
- package/chunks/{gemini-NQ3WWBKB.js → gemini-QYSEGHAX.js} +119 -111
- package/chunks/{geminiContentGenerator-CDUNEJQE.js → geminiContentGenerator-J7YNZDYP.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-BRCSGFOT.js} +95 -74
- package/chunks/{grep-NRITKDCD.js → grep-EE2DUOSY.js} +50 -50
- package/chunks/handleAutoUpdate-5NWTLR67.js +69 -0
- package/chunks/i18n-BASPENNQ.js +84 -0
- package/chunks/initializer-UTQ7FB7G.js +71 -0
- package/chunks/installationInfo-W6V4DT7W.js +65 -0
- package/chunks/{ja-CUHZOLEJ.js → ja-7VQOANVG.js} +64 -5
- package/chunks/{keychain-token-storage-UJV5XP4Z.js → keychain-token-storage-VKUBNCP4.js} +4 -4
- package/chunks/{kittyProtocolDetector-OJRCJLIU.js → kittyProtocolDetector-HCNNKJTI.js} +2 -2
- package/chunks/list-DEICDL65.js +74 -0
- package/chunks/loadedSettingsAdapter-NOWEGDYJ.js +68 -0
- package/chunks/{loop-wakeup-OHWEMFP5.js → loop-wakeup-WX7BLWWP.js} +10 -10
- package/chunks/{lowlight-FYAAUU5J.js → lowlight-J2OZNPCA.js} +1 -1
- package/chunks/{ls-7MFU2QYW.js → ls-ZB2BDP6T.js} +6 -6
- package/chunks/{lsp-XHDTPTGV.js → lsp-V5DN4YUJ.js} +4 -4
- package/chunks/mcp-L5JXEEIA.js +68 -0
- package/chunks/{monitor-FL2ZO33W.js → monitor-X5Z3H4FY.js} +50 -50
- package/chunks/{multipart-parser-AJ4WASWR.js → multipart-parser-AWZKXSQN.js} +3 -3
- package/chunks/nonInteractiveCli-LS2K4X6A.js +124 -0
- package/chunks/{notebook-edit-6PULTHL5.js → notebook-edit-TRM2QIJK.js} +51 -51
- package/chunks/openaiContentGenerator-AAYONPMF.js +56 -0
- package/chunks/pidfile-D4I4FTVA.js +73 -0
- package/chunks/{pt-OLYN42DS.js → pt-2C6YCSHL.js} +64 -5
- package/chunks/{qwenContentGenerator-NX2E7PB7.js → qwenContentGenerator-K3HZ2H76.js} +52 -52
- package/chunks/{qwenOAuth2-ZV7YLMND.js → qwenOAuth2-PJQ27GSO.js} +9 -9
- package/chunks/read-file-7SZSIZOL.js +30 -0
- package/chunks/{read-mcp-resource-CNQHBSJM.js → read-mcp-resource-SM5DA4RG.js} +5 -5
- package/chunks/{read-package-up-ER5OJUGP.js → read-package-up-TCM6I7S2.js} +2 -2
- package/chunks/{record-artifact-SQ2OKRJW.js → record-artifact-4XKZX3UW.js} +4 -4
- package/chunks/ripGrep-YNPFVCZP.js +60 -0
- package/chunks/{ru-LDM3GWVQ.js → ru-6BBHVZZV.js} +64 -5
- package/chunks/{run-qwen-serve-JK2DL7PI.js → run-qwen-serve-DMWV5N6L.js} +395 -74
- package/chunks/runtime-57WWRVHN.js +94 -0
- package/chunks/{scheduler-O4IGVZKK.js → scheduler-YBLGWSIL.js} +51 -51
- package/chunks/{send-message-76GT3JMC.js → send-message-F36YD6KQ.js} +7 -7
- package/chunks/serve-R63NK3CV.js +74 -0
- package/chunks/{server-SGNPKX2O.js → server-Z22GCAXP.js} +3956 -869
- package/chunks/{session-5BNM25EZ.js → session-HKAAI7BN.js} +117 -112
- package/chunks/settings-3EYN66GK.js +118 -0
- package/chunks/shell-6YXINTWJ.js +68 -0
- package/chunks/{skill-I22QK5XS.js → skill-OO57P4X6.js} +24 -24
- package/chunks/spawnChannel-JD7G4VU6.js +70 -0
- package/chunks/{src-BBQSM46M.js → src-G5UPLZSZ.js} +122 -52
- package/chunks/{src-7XL4G4DC.js → src-VLI3TKEY.js} +4 -4
- package/chunks/standalone-update-OB7F4JPZ.js +79 -0
- package/chunks/{startInteractiveUI-JII36UGN.js → startInteractiveUI-73EGLIIH.js} +1775 -1663
- package/chunks/stdioHelpers-UMY72QRP.js +14 -0
- package/chunks/{syntheticOutput-F7LSDCAR.js → syntheticOutput-34H7LGA7.js} +5 -5
- package/chunks/task-create-FRGOEIKY.js +22 -0
- package/chunks/{task-list-TGGJX7EA.js → task-list-OW766BXZ.js} +10 -10
- package/chunks/{task-stop-W7NGTMYQ.js → task-stop-SIR5SNBN.js} +4 -4
- package/chunks/{task-update-XYWMHJGJ.js → task-update-YRA532MT.js} +12 -12
- package/chunks/{team-create-A7NUU5AQ.js → team-create-PECAAPXK.js} +50 -50
- package/chunks/{team-delete-UHQQ2QQX.js → team-delete-6OSECUZC.js} +10 -10
- package/chunks/{team-plan-approval-TOQRJ2KI.js → team-plan-approval-L6F434RY.js} +50 -50
- package/chunks/theme-manager-ZG4RY6GU.js +64 -0
- package/chunks/{todoWrite-C4ZAMSR3.js → todoWrite-YDMFEV4G.js} +6 -6
- package/chunks/{tool-search-SZ4EFS2T.js → tool-search-THVIWAPB.js} +24 -24
- package/chunks/total-session-admission-JOKNRIIA.js +72 -0
- package/chunks/trustedFolders-EOGT3UVS.js +79 -0
- package/chunks/{types-QX5C3CHJ.js → types-JNKGKUJT.js} +2 -2
- package/chunks/updateCheck-2KYEYVSB.js +69 -0
- package/chunks/updateEventEmitter-5MKC7BLQ.js +10 -0
- package/chunks/validateNonInterActiveAuth-EN6ZQSR2.js +190 -0
- package/chunks/{version-NO2XSNNP.js → version-3UEA6ZIR.js} +3 -3
- package/chunks/{web-fetch-UYGMOHW4.js → web-fetch-PAAEQKBW.js} +8 -8
- package/chunks/{workflow-WO2H6NRI.js → workflow-QCCCMYP3.js} +51 -51
- package/chunks/workspace-providers-status-4KMGETOJ.js +71 -0
- package/chunks/workspace-registry-24QFLQYH.js +76 -0
- package/chunks/workspace-service-RVLBMSMZ.js +80 -0
- package/chunks/workspace-skills-status-DS4ZVFTO.js +70 -0
- package/chunks/{write-file-PX2Z7IBE.js → write-file-I3MUDRIE.js} +51 -51
- package/chunks/{yargs-B64EYTCF.js → yargs-6H2AUULL.js} +2 -2
- package/chunks/{zh-J4F6QWQV.js → zh-RQE7I22T.js} +74 -6
- package/chunks/{zh-TW-XVDWOLSW.js → zh-TW-TBPQGHKH.js} +74 -6
- package/cli.js +18 -17
- package/locales/ca.js +85 -7
- package/locales/de.js +84 -7
- package/locales/en.js +101 -7
- package/locales/fr.js +85 -7
- package/locales/ja.js +86 -6
- package/locales/pt.js +84 -7
- package/locales/ru.js +84 -7
- package/locales/zh-TW.js +97 -6
- package/locales/zh.js +97 -6
- package/package.json +3 -6
- package/web-shell/assets/{arc-BWyoJpY2.js → arc-C15hqnJe.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-BT0mGrz2.js → architectureDiagram-3BPJPVTR-B-At37J_.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-BLgUWvyf.js → blockDiagram-GPEHLZMM-B51wjcU7.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-DYx9M7oU.js → c4Diagram-AAUBKEIU-xBK7BAwN.js} +1 -1
- package/web-shell/assets/channel-CLiQD50f.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-CfMHWbuZ.js → chunk-2J33WTMH-xKmLg7Yr.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-B1URoqEV.js → chunk-4BX2VUAB-BfkdKZCM.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-DAIaM4QF.js → chunk-55IACEB6-DT713sZf.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-B2X34knw.js → chunk-727SXJPM-CYEeAamv.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-Cif-zLTQ.js → chunk-AQP2D5EJ-DBN-5CFd.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-H18WCXdY.js → chunk-FMBD7UC4-CGZKk4PH.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-YFtetHi0.js → chunk-ND2GUHAM-qQbC2O-y.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-By2hiH3r.js → chunk-QZHKN3VN-B3zeXwsX.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-D3sYqow5.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-D3sYqow5.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-VrcnnLZu.js → cose-bilkent-S5V4N54A-BKsl1Wvy.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-CGo-IaVV.js → dagre-BM42HDAG-CuuuLiBK.js} +1 -1
- package/web-shell/assets/{diagram-2AECGRRQ-Dl4QjzIB.js → diagram-2AECGRRQ-CG4_eKiV.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-qMLmsO0u.js → diagram-5GNKFQAL-DSFTmZB1.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-CHlmuxPI.js → diagram-KO2AKTUF-BxwlEHn8.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-BUwU3GwT.js → diagram-LMA3HP47-BUAg-jMG.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-HEhwPDUM.js → diagram-OG6HWLK6-BFM2jlO5.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-1n0llBmM.js → erDiagram-TEJ5UH35-CerbFPa2.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-BN74uMnt.js → flowDiagram-I6XJVG4X-BNeZz5Zz.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-BoOq78i3.js → ganttDiagram-6RSMTGT7-CTOogLaM.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-CN8b00c8.js → gitGraphDiagram-PVQCEYII-yxSFkXPj.js} +1 -1
- package/web-shell/assets/index-D5IWFVk1.css +5 -0
- package/web-shell/assets/index-KFJRDhX7.js +783 -0
- package/web-shell/assets/{infoDiagram-5YYISTIA-CCurnIna.js → infoDiagram-5YYISTIA-DYdAUevm.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-AwuRqdUP.js → ishikawaDiagram-YF4QCWOH-CNuijZ90.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-B7IICk-E.js → journeyDiagram-JHISSGLW-BTyiuonf.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-BYF0OKhx.js → kanban-definition-UN3LZRKU-DPJo0Uow.js} +1 -1
- package/web-shell/assets/{linear-GWWguGfD.js → linear-C8wKCbIC.js} +1 -1
- package/web-shell/assets/{mermaid.core-JqtkxvCl.js → mermaid.core-BV5o7nW5.js} +5 -5
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-B0MmHsZ6.js → mindmap-definition-RKZ34NQL-BtLcBmSt.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-DhImUZLQ.js → pieDiagram-4H26LBE5-C22J7A9Q.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-DGeSC3DT.js → quadrantDiagram-W4KKPZXB-DSL10wiL.js} +1 -1
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-DkspY6Re.js → requirementDiagram-4Y6WPE33-feqh0UpX.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-CaOofS2x.js → sankeyDiagram-5OEKKPKP-Z4OVaWaZ.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-DFQ_kFnN.js → sequenceDiagram-3UESZ5HK-DIluIcb1.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-D3snw0Tz.js → stateDiagram-AJRCARHV-hlhnuoYH.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-BhQMwgpl.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-l4zH8nLM.js → timeline-definition-PNZ67QCA-Dqf_CqgL.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-BnRlA98M.js → vennDiagram-CIIHVFJN-DD2QhaTX.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-BS0xUdmd.js → wardley-L42UT6IY-B2uI85U_.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-DKGBUWyQ.js → wardleyDiagram-YWT4CUSO-U1aJX71g.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-DVb2HuOB.js → xychartDiagram-2RQKCTM6-C0ODHUee.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-S6RLAIUR.js +0 -23
- 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
|
@@ -1,24 +1,8 @@
|
|
|
1
1
|
// Force strict mode and setup for ESM
|
|
2
2
|
"use strict";
|
|
3
|
-
import {
|
|
4
|
-
LOAD_REPLAY_BULK_MODE,
|
|
5
|
-
LOAD_REPLAY_META_KEY,
|
|
6
|
-
LOAD_REPLAY_MODE_META_KEY,
|
|
7
|
-
LOAD_REPLAY_VERSION,
|
|
8
|
-
MID_TURN_QUEUE_DRAIN_METHOD
|
|
9
|
-
} from "./chunk-AGHU5VRK.js";
|
|
10
|
-
import {
|
|
11
|
-
DEFAULT_RING_SIZE,
|
|
12
|
-
EVENT_SCHEMA_VERSION,
|
|
13
|
-
EventBus,
|
|
14
|
-
serializedBridgeEventByteLength
|
|
15
|
-
} from "./chunk-3UP777ZS.js";
|
|
16
|
-
import {
|
|
17
|
-
normalizeCompactedReplayMaxBytes
|
|
18
|
-
} from "./chunk-GLE2YUPU.js";
|
|
19
3
|
import {
|
|
20
4
|
MCP_RESTART_SERVER_DEADLINE_MS
|
|
21
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-OCPBI7J5.js";
|
|
22
6
|
import {
|
|
23
7
|
BranchWhilePromptActiveError,
|
|
24
8
|
CancelSentinelCollisionError,
|
|
@@ -38,18 +22,29 @@ import {
|
|
|
38
22
|
SessionShellDisabledError,
|
|
39
23
|
WorkspaceMismatchError,
|
|
40
24
|
isNotCurrentlyGeneratingCancelError
|
|
41
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-IAVB3HM3.js";
|
|
26
|
+
import {
|
|
27
|
+
LOAD_REPLAY_BULK_MODE,
|
|
28
|
+
LOAD_REPLAY_META_KEY,
|
|
29
|
+
LOAD_REPLAY_MODE_META_KEY,
|
|
30
|
+
LOAD_REPLAY_VERSION,
|
|
31
|
+
MID_TURN_QUEUE_DRAIN_METHOD
|
|
32
|
+
} from "./chunk-PZL23GTM.js";
|
|
42
33
|
import {
|
|
43
34
|
canonicalizeWorkspace
|
|
44
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-W7FJ3N32.js";
|
|
45
36
|
import {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
37
|
+
DEFAULT_RING_SIZE,
|
|
38
|
+
EVENT_SCHEMA_VERSION,
|
|
39
|
+
EventBus,
|
|
40
|
+
serializedBridgeEventByteLength
|
|
41
|
+
} from "./chunk-6RLROE6G.js";
|
|
42
|
+
import {
|
|
43
|
+
normalizeCompactedReplayMaxBytes
|
|
44
|
+
} from "./chunk-COH43MRJ.js";
|
|
50
45
|
import {
|
|
51
46
|
defaultSpawnChannelFactory
|
|
52
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-CLFKOZEK.js";
|
|
53
48
|
import {
|
|
54
49
|
BridgeChannelClosedError,
|
|
55
50
|
BridgeTimeoutError,
|
|
@@ -58,18 +53,31 @@ import {
|
|
|
58
53
|
STATUS_SCHEMA_VERSION,
|
|
59
54
|
createIdleWorkspaceExtensionsStatus,
|
|
60
55
|
createIdleWorkspaceHooksStatus
|
|
61
|
-
} from "./chunk-
|
|
56
|
+
} from "./chunk-VWPVHAMO.js";
|
|
57
|
+
import {
|
|
58
|
+
ClientSideConnection,
|
|
59
|
+
PROTOCOL_VERSION,
|
|
60
|
+
RequestError
|
|
61
|
+
} from "./chunk-UDXSXLER.js";
|
|
62
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-NVYTCB5Z.js";
|
|
66
74
|
import {
|
|
67
75
|
DAEMON_TRACEPARENT_META_KEY,
|
|
68
76
|
DAEMON_TRACESTATE_META_KEY
|
|
69
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-3VAGG5T5.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");
|
|
@@ -1323,7 +1391,7 @@ function sliceLineRange(content, startLine, endLine) {
|
|
|
1323
1391
|
}
|
|
1324
1392
|
__name(sliceLineRange, "sliceLineRange");
|
|
1325
1393
|
var BridgeClient = class {
|
|
1326
|
-
constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender, ownsSession = () => true, onTokenUsage) {
|
|
1394
|
+
constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender, ownsSession = () => true, onTokenUsage, onCreateSubSession) {
|
|
1327
1395
|
this.resolveEntry = resolveEntry;
|
|
1328
1396
|
this.resolvePendingRestoreEvents = resolvePendingRestoreEvents;
|
|
1329
1397
|
this.mediator = mediator;
|
|
@@ -1335,6 +1403,7 @@ var BridgeClient = class {
|
|
|
1335
1403
|
this.clientMcpSender = clientMcpSender;
|
|
1336
1404
|
this.ownsSession = ownsSession;
|
|
1337
1405
|
this.onTokenUsage = onTokenUsage;
|
|
1406
|
+
this.onCreateSubSession = onCreateSubSession;
|
|
1338
1407
|
}
|
|
1339
1408
|
static {
|
|
1340
1409
|
__name(this, "BridgeClient");
|
|
@@ -1348,10 +1417,9 @@ var BridgeClient = class {
|
|
|
1348
1417
|
);
|
|
1349
1418
|
return { outcome: { outcome: "cancelled" } };
|
|
1350
1419
|
}
|
|
1420
|
+
const options = Array.isArray(params.options) ? params.options : [];
|
|
1351
1421
|
const allowedOptionIds = new Set(
|
|
1352
|
-
|
|
1353
|
-
(o) => String(o.optionId ?? "")
|
|
1354
|
-
)
|
|
1422
|
+
options.map((o) => String(o.optionId ?? ""))
|
|
1355
1423
|
);
|
|
1356
1424
|
allowedOptionIds.delete("");
|
|
1357
1425
|
const requestId = randomUUID();
|
|
@@ -1364,12 +1432,25 @@ var BridgeClient = class {
|
|
|
1364
1432
|
requestId,
|
|
1365
1433
|
sessionId: entry.sessionId,
|
|
1366
1434
|
toolCall: params.toolCall,
|
|
1367
|
-
options
|
|
1435
|
+
options
|
|
1368
1436
|
},
|
|
1369
1437
|
...entry.activePromptOriginatorClientId ? { originatorClientId: entry.activePromptOriginatorClientId } : {}
|
|
1370
1438
|
});
|
|
1371
1439
|
if (!published) return { outcome: { outcome: "cancelled" } };
|
|
1440
|
+
let interaction;
|
|
1441
|
+
try {
|
|
1442
|
+
interaction = pendingInteractionFromRequest(requestId, {
|
|
1443
|
+
...params,
|
|
1444
|
+
options
|
|
1445
|
+
});
|
|
1446
|
+
} catch (error) {
|
|
1447
|
+
writeStderrLine(
|
|
1448
|
+
`qwen serve: failed to snapshot pending interaction ${requestId}: ${error instanceof Error ? error.message : String(error)}`
|
|
1449
|
+
);
|
|
1450
|
+
interaction = fallbackPendingPermissionInteraction(requestId, options);
|
|
1451
|
+
}
|
|
1372
1452
|
entry.pendingPermissionIds.add(requestId);
|
|
1453
|
+
if (interaction) entry.pendingInteractions.set(requestId, interaction);
|
|
1373
1454
|
try {
|
|
1374
1455
|
const record = {
|
|
1375
1456
|
requestId,
|
|
@@ -1385,6 +1466,7 @@ var BridgeClient = class {
|
|
|
1385
1466
|
return resolutionToAcpResponse(resolution);
|
|
1386
1467
|
} finally {
|
|
1387
1468
|
entry.pendingPermissionIds.delete(requestId);
|
|
1469
|
+
entry.pendingInteractions.delete(requestId);
|
|
1388
1470
|
}
|
|
1389
1471
|
}
|
|
1390
1472
|
async sessionUpdate(params) {
|
|
@@ -1458,7 +1540,7 @@ var BridgeClient = class {
|
|
|
1458
1540
|
trustedPublisher: isTrustedArtifactToolUpdate(params, updateMeta)
|
|
1459
1541
|
};
|
|
1460
1542
|
}
|
|
1461
|
-
async seedSessionUpdates(entry, updates) {
|
|
1543
|
+
async seedSessionUpdates(entry, updates, options = {}) {
|
|
1462
1544
|
const frames = [];
|
|
1463
1545
|
const artifactBatches = [];
|
|
1464
1546
|
for (const update of updates) {
|
|
@@ -1467,7 +1549,7 @@ var BridgeClient = class {
|
|
|
1467
1549
|
entry
|
|
1468
1550
|
);
|
|
1469
1551
|
frames.push(...prepared.frames);
|
|
1470
|
-
if (prepared.artifacts.length > 0) {
|
|
1552
|
+
if (options.ingestArtifacts !== false && prepared.artifacts.length > 0) {
|
|
1471
1553
|
artifactBatches.push({
|
|
1472
1554
|
artifacts: prepared.artifacts,
|
|
1473
1555
|
trustedPublisher: prepared.trustedPublisher
|
|
@@ -1556,9 +1638,12 @@ var BridgeClient = class {
|
|
|
1556
1638
|
inFlightRestoreIds = /* @__PURE__ */ new Set();
|
|
1557
1639
|
/**
|
|
1558
1640
|
* Handle child->bridge ACP `extMethod` requests (calls that expect a
|
|
1559
|
-
* response, unlike `extNotification`).
|
|
1560
|
-
* `
|
|
1561
|
-
*
|
|
1641
|
+
* response, unlike `extNotification`). Served methods:
|
|
1642
|
+
* `qwen/control/client_mcp/message` (reverse tool channel),
|
|
1643
|
+
* `qwen/control/create-sub-session` (the `create_sub_session` tool → daemon
|
|
1644
|
+
* spawns a sub-session and, for `'first-turn'`, returns its first-turn
|
|
1645
|
+
* result), and `craft/drainMidTurnQueue`: the ACP child calls the last one
|
|
1646
|
+
* between tool batches to pull any messages the browser queued mid-turn. We splice the per-session
|
|
1562
1647
|
* queue, return them to the child as the response, and — when non-empty —
|
|
1563
1648
|
* publish a `mid_turn_message_injected` SSE frame so the browser can move
|
|
1564
1649
|
* those messages out of its pending queue (a dedupe signal, not a transcript
|
|
@@ -1571,6 +1656,9 @@ var BridgeClient = class {
|
|
|
1571
1656
|
if (method === SERVE_CONTROL_EXT_METHODS.clientMcpMessage) {
|
|
1572
1657
|
return this.handleClientMcpMessage(params);
|
|
1573
1658
|
}
|
|
1659
|
+
if (method === SERVE_CONTROL_EXT_METHODS.createSubSession) {
|
|
1660
|
+
return this.handleCreateSubSession(params);
|
|
1661
|
+
}
|
|
1574
1662
|
if (method !== MID_TURN_QUEUE_DRAIN_METHOD) {
|
|
1575
1663
|
throw RequestError.methodNotFound(method);
|
|
1576
1664
|
}
|
|
@@ -1646,6 +1734,68 @@ var BridgeClient = class {
|
|
|
1646
1734
|
const response = await send(payload);
|
|
1647
1735
|
return { payload: response };
|
|
1648
1736
|
}
|
|
1737
|
+
/**
|
|
1738
|
+
* Handle the `create_sub_session` tool's request: validate, then forward to
|
|
1739
|
+
* the daemon-host `onCreateSubSession` callback (which spawns a fresh
|
|
1740
|
+
* top-level sub-session and, for `'first-turn'`, waits for its first turn and
|
|
1741
|
+
* returns the result). No host wired → `methodNotFound`, which the tool
|
|
1742
|
+
* surfaces as "daemon-only".
|
|
1743
|
+
*/
|
|
1744
|
+
async handleCreateSubSession(params) {
|
|
1745
|
+
if (!this.onCreateSubSession) {
|
|
1746
|
+
throw RequestError.methodNotFound(
|
|
1747
|
+
SERVE_CONTROL_EXT_METHODS.createSubSession
|
|
1748
|
+
);
|
|
1749
|
+
}
|
|
1750
|
+
const prompt = params["prompt"];
|
|
1751
|
+
if (typeof prompt !== "string" || prompt.length === 0) {
|
|
1752
|
+
throw RequestError.invalidParams(
|
|
1753
|
+
void 0,
|
|
1754
|
+
"`prompt` must be a non-empty string"
|
|
1755
|
+
);
|
|
1756
|
+
}
|
|
1757
|
+
if (prompt.length > MAX_SUB_SESSION_PROMPT_CHARS) {
|
|
1758
|
+
throw RequestError.invalidParams(
|
|
1759
|
+
void 0,
|
|
1760
|
+
`\`prompt\` exceeds the ${MAX_SUB_SESSION_PROMPT_CHARS}-character limit`
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1763
|
+
const completion = params["completion"];
|
|
1764
|
+
if (completion !== "sent" && completion !== "first-turn") {
|
|
1765
|
+
throw RequestError.invalidParams(
|
|
1766
|
+
void 0,
|
|
1767
|
+
"`completion` must be 'sent' or 'first-turn'"
|
|
1768
|
+
);
|
|
1769
|
+
}
|
|
1770
|
+
const name = params["name"];
|
|
1771
|
+
if (typeof name === "string" && name.length > MAX_SUB_SESSION_NAME_CHARS) {
|
|
1772
|
+
throw RequestError.invalidParams(
|
|
1773
|
+
void 0,
|
|
1774
|
+
`\`name\` exceeds the ${MAX_SUB_SESSION_NAME_CHARS}-character limit`
|
|
1775
|
+
);
|
|
1776
|
+
}
|
|
1777
|
+
const callerSessionId = params["callerSessionId"];
|
|
1778
|
+
if (typeof callerSessionId !== "string" || callerSessionId.length === 0 || !this.ownsSession(callerSessionId)) {
|
|
1779
|
+
throw RequestError.invalidParams(
|
|
1780
|
+
void 0,
|
|
1781
|
+
"`callerSessionId` is required and must name a session owned by this connection"
|
|
1782
|
+
);
|
|
1783
|
+
}
|
|
1784
|
+
const model = params["model"];
|
|
1785
|
+
const result = await this.onCreateSubSession({
|
|
1786
|
+
prompt,
|
|
1787
|
+
completion,
|
|
1788
|
+
...typeof model === "string" && model.length > 0 && model.length <= 128 ? { model } : {},
|
|
1789
|
+
...typeof name === "string" && name.length > 0 ? { name } : {},
|
|
1790
|
+
callerSessionId
|
|
1791
|
+
});
|
|
1792
|
+
return {
|
|
1793
|
+
sessionId: result.sessionId,
|
|
1794
|
+
...result.result !== void 0 ? { result: result.result } : {},
|
|
1795
|
+
...result.stopReason !== void 0 ? { stopReason: result.stopReason } : {},
|
|
1796
|
+
...result.parentSessionPersisted !== void 0 ? { parentSessionPersisted: result.parentSessionPersisted } : {}
|
|
1797
|
+
};
|
|
1798
|
+
}
|
|
1649
1799
|
/**
|
|
1650
1800
|
* Handle child->bridge ACP `extNotification` calls. Recognized methods are
|
|
1651
1801
|
* `qwen/notify/session/model-update`,
|
|
@@ -1655,8 +1805,8 @@ var BridgeClient = class {
|
|
|
1655
1805
|
* `qwen/notify/session/artifact-event` (hook artifacts),
|
|
1656
1806
|
* `qwen/notify/session/terminal-sequence`, and
|
|
1657
1807
|
* `qwen/notify/session/mcp-budget-event` — each translated into a
|
|
1658
|
-
* session-scoped SSE frame. Unknown methods are dropped silently
|
|
1659
|
-
*
|
|
1808
|
+
* session-scoped SSE frame. Unknown methods are dropped silently for
|
|
1809
|
+
* forward-compat.
|
|
1660
1810
|
*/
|
|
1661
1811
|
async extNotification(method, params) {
|
|
1662
1812
|
if (method === "qwen/notify/session/model-update") {
|
|
@@ -2239,7 +2389,7 @@ __name(extractA2uiToolUpdate, "extractA2uiToolUpdate");
|
|
|
2239
2389
|
// packages/acp-bridge/src/sessionArtifacts.ts
|
|
2240
2390
|
init_esbuild_shims();
|
|
2241
2391
|
import { createHash } from "node:crypto";
|
|
2242
|
-
import { promises as fs2 } from "node:fs";
|
|
2392
|
+
import { constants as fsConstants, promises as fs2 } from "node:fs";
|
|
2243
2393
|
import path2 from "node:path";
|
|
2244
2394
|
var SOURCE_RESERVATIONS = {
|
|
2245
2395
|
tool: 100,
|
|
@@ -2248,6 +2398,18 @@ var SOURCE_RESERVATIONS = {
|
|
|
2248
2398
|
};
|
|
2249
2399
|
var WORKSPACE_STATUS_REFRESH_TTL_MS = 5e3;
|
|
2250
2400
|
var WORKSPACE_STATUS_REFRESH_BATCH_SIZE = 20;
|
|
2401
|
+
var SNAPSHOT_AFTER_DURABLE_EVENTS = 50;
|
|
2402
|
+
var MAX_SNAPSHOT_BACKOFF_MULTIPLIER = 4;
|
|
2403
|
+
var MAX_TOMBSTONED_IDS = 500;
|
|
2404
|
+
var MAX_STICKY_EPHEMERAL_IDS = 500;
|
|
2405
|
+
var MAX_WORKSPACE_HASH_BYTES = 100 * 1024 * 1024;
|
|
2406
|
+
var SECRET_TOKEN_VALUE_PATTERN = /(?:^|\s)(?:bearer\s+\S{8,}|sk-[A-Za-z0-9_-]{12,}|(?:gh[pousr]|github_pat)_[A-Za-z0-9_/-]{12,}|[a-f0-9]{40,}|[A-Za-z0-9+/]{48,}={0,2})(?:$|\s)/i;
|
|
2407
|
+
var RESTORE_FAILED_WARNING_PREFIX = "artifact snapshot restore failed";
|
|
2408
|
+
var RESTORE_PARTIAL_FAILED_WARNING_PREFIX = "artifact snapshot restore partially failed";
|
|
2409
|
+
function isArtifactRestoreFailureWarning(warning) {
|
|
2410
|
+
return warning.startsWith(RESTORE_FAILED_WARNING_PREFIX) || warning.startsWith(RESTORE_PARTIAL_FAILED_WARNING_PREFIX);
|
|
2411
|
+
}
|
|
2412
|
+
__name(isArtifactRestoreFailureWarning, "isArtifactRestoreFailureWarning");
|
|
2251
2413
|
var SessionArtifactValidationError = class extends Error {
|
|
2252
2414
|
constructor(message, field) {
|
|
2253
2415
|
super(message);
|
|
@@ -2259,6 +2421,20 @@ var SessionArtifactValidationError = class extends Error {
|
|
|
2259
2421
|
}
|
|
2260
2422
|
code = "VALIDATION_FAILED";
|
|
2261
2423
|
};
|
|
2424
|
+
var SessionArtifactAuthorizationError = class extends Error {
|
|
2425
|
+
constructor(sessionId, artifactId, ownerClientId, requesterClientId) {
|
|
2426
|
+
super(`artifact ${artifactId} is owned by a different client`);
|
|
2427
|
+
this.sessionId = sessionId;
|
|
2428
|
+
this.artifactId = artifactId;
|
|
2429
|
+
this.ownerClientId = ownerClientId;
|
|
2430
|
+
this.requesterClientId = requesterClientId;
|
|
2431
|
+
this.name = "SessionArtifactAuthorizationError";
|
|
2432
|
+
}
|
|
2433
|
+
static {
|
|
2434
|
+
__name(this, "SessionArtifactAuthorizationError");
|
|
2435
|
+
}
|
|
2436
|
+
code = "SESSION_ARTIFACT_FORBIDDEN";
|
|
2437
|
+
};
|
|
2262
2438
|
var SessionArtifactStore = class {
|
|
2263
2439
|
static {
|
|
2264
2440
|
__name(this, "SessionArtifactStore");
|
|
@@ -2266,15 +2442,26 @@ var SessionArtifactStore = class {
|
|
|
2266
2442
|
sessionId;
|
|
2267
2443
|
workspaceCwd;
|
|
2268
2444
|
maxArtifacts;
|
|
2445
|
+
persistence;
|
|
2269
2446
|
artifacts = /* @__PURE__ */ new Map();
|
|
2270
2447
|
receivedSeq = 0;
|
|
2271
2448
|
insertSeq = 0;
|
|
2449
|
+
persistenceSeq = 0;
|
|
2450
|
+
durableEventsSinceSnapshot = 0;
|
|
2451
|
+
consecutiveSnapshotFailures = 0;
|
|
2272
2452
|
realWorkspaceCwdPromise;
|
|
2273
2453
|
operationQueue = Promise.resolve();
|
|
2454
|
+
tombstonedIds = /* @__PURE__ */ new Set();
|
|
2455
|
+
tombstonedClientIds = /* @__PURE__ */ new Map();
|
|
2456
|
+
stickyEphemeralIds = /* @__PURE__ */ new Set();
|
|
2457
|
+
markerArtifacts = /* @__PURE__ */ new Map();
|
|
2458
|
+
lastRestoreWarnings = [];
|
|
2459
|
+
lastRestoreWarningDetails = [];
|
|
2274
2460
|
constructor(options) {
|
|
2275
2461
|
this.sessionId = options.sessionId;
|
|
2276
2462
|
this.workspaceCwd = options.workspaceCwd;
|
|
2277
2463
|
this.maxArtifacts = options.maxArtifacts ?? 200;
|
|
2464
|
+
this.persistence = options.persistence;
|
|
2278
2465
|
}
|
|
2279
2466
|
inputBatchLimit() {
|
|
2280
2467
|
return this.maxArtifacts * 2;
|
|
@@ -2287,13 +2474,30 @@ var SessionArtifactStore = class {
|
|
|
2287
2474
|
sessionId: this.sessionId,
|
|
2288
2475
|
artifacts: Array.from(this.artifacts.values()).sort((a, b) => a.insertSeq - b.insertSeq).map(toPublicArtifact),
|
|
2289
2476
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2290
|
-
limits: { maxArtifacts: this.maxArtifacts }
|
|
2477
|
+
limits: { maxArtifacts: this.maxArtifacts },
|
|
2478
|
+
...this.lastRestoreWarnings.length > 0 ? { warnings: [...this.lastRestoreWarnings] } : {},
|
|
2479
|
+
...this.lastRestoreWarningDetails.length > 0 ? { warningDetails: [...this.lastRestoreWarningDetails] } : {}
|
|
2291
2480
|
};
|
|
2292
2481
|
});
|
|
2293
2482
|
}
|
|
2483
|
+
async get(artifactId) {
|
|
2484
|
+
return this.enqueue(async () => {
|
|
2485
|
+
const artifact = this.artifacts.get(artifactId);
|
|
2486
|
+
if (!artifact) return void 0;
|
|
2487
|
+
if (artifact.workspacePath && shouldRefreshWorkspaceStatus(artifact, Date.now())) {
|
|
2488
|
+
await this.refreshWorkspaceStatus(artifact, { onError: "missing" });
|
|
2489
|
+
}
|
|
2490
|
+
return toPublicArtifact(artifact);
|
|
2491
|
+
});
|
|
2492
|
+
}
|
|
2294
2493
|
async upsertMany(inputs, options = {}) {
|
|
2295
2494
|
return this.enqueue(async () => {
|
|
2495
|
+
const validationStrict = options.validationStrict ?? options.strict;
|
|
2496
|
+
const persistenceStrict = options.persistenceStrict ?? options.strict;
|
|
2497
|
+
const before = this.cloneState();
|
|
2296
2498
|
const normalizedResults = [];
|
|
2499
|
+
const warnings = [];
|
|
2500
|
+
const warningDetails = [];
|
|
2297
2501
|
for (const input of inputs) {
|
|
2298
2502
|
try {
|
|
2299
2503
|
normalizedResults.push(
|
|
@@ -2304,7 +2508,7 @@ var SessionArtifactStore = class {
|
|
|
2304
2508
|
)
|
|
2305
2509
|
);
|
|
2306
2510
|
} catch (error) {
|
|
2307
|
-
if (
|
|
2511
|
+
if (validationStrict) {
|
|
2308
2512
|
throw error;
|
|
2309
2513
|
}
|
|
2310
2514
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -2316,39 +2520,140 @@ var SessionArtifactStore = class {
|
|
|
2316
2520
|
}
|
|
2317
2521
|
}
|
|
2318
2522
|
const changes = [];
|
|
2319
|
-
|
|
2320
|
-
const
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2523
|
+
try {
|
|
2524
|
+
for (const normalized of coalesceByIdentity(normalizedResults)) {
|
|
2525
|
+
const artifact = this.applyStickyEphemeralOverride(normalized);
|
|
2526
|
+
if (this.shouldSuppressTombstonedUpsert(artifact)) {
|
|
2527
|
+
writeStderrLine(
|
|
2528
|
+
`[artifacts] session=${this.sessionId} action=tombstone_replay_suppressed artifactId=${artifact.id}`
|
|
2529
|
+
);
|
|
2530
|
+
continue;
|
|
2531
|
+
}
|
|
2532
|
+
const existing = this.artifacts.get(artifact.id) ?? this.findPublishedUpgradeTarget(artifact) ?? this.findPublishedWorkspaceTarget(artifact);
|
|
2533
|
+
if (!existing) {
|
|
2534
|
+
const stored = {
|
|
2535
|
+
...artifact,
|
|
2536
|
+
insertSeq: ++this.insertSeq
|
|
2537
|
+
};
|
|
2538
|
+
this.artifacts.set(stored.id, stored);
|
|
2539
|
+
changes.push({
|
|
2540
|
+
action: "created",
|
|
2541
|
+
artifactId: stored.id,
|
|
2542
|
+
artifact: toPublicArtifact(stored)
|
|
2543
|
+
});
|
|
2544
|
+
continue;
|
|
2545
|
+
}
|
|
2546
|
+
try {
|
|
2547
|
+
this.denyCrossClientMutation("upsert", existing.id, existing, {
|
|
2548
|
+
clientId: artifact.clientId
|
|
2549
|
+
});
|
|
2550
|
+
} catch (error) {
|
|
2551
|
+
if (validationStrict) {
|
|
2552
|
+
throw error;
|
|
2553
|
+
}
|
|
2554
|
+
if (error instanceof SessionArtifactAuthorizationError) {
|
|
2555
|
+
warnings.push(error.message);
|
|
2556
|
+
continue;
|
|
2557
|
+
}
|
|
2558
|
+
throw error;
|
|
2559
|
+
}
|
|
2560
|
+
const updated = mergeArtifact(existing, artifact);
|
|
2561
|
+
if (updated.changed) {
|
|
2562
|
+
if (updated.artifact.id !== existing.id) {
|
|
2563
|
+
const removeChange = {
|
|
2564
|
+
action: "removed",
|
|
2565
|
+
artifactId: existing.id,
|
|
2566
|
+
artifact: toPublicArtifact(existing),
|
|
2567
|
+
reason: "explicit",
|
|
2568
|
+
durableTombstoneRequired: existing.durableTombstoneRequired || existing.persistedAt !== void 0,
|
|
2569
|
+
removedClientId: existing.clientId
|
|
2570
|
+
};
|
|
2571
|
+
changes.push(removeChange);
|
|
2572
|
+
this.artifacts.delete(existing.id);
|
|
2573
|
+
} else if (shouldRecordEphemeralUnpin(existing, artifact)) {
|
|
2574
|
+
changes.push({
|
|
2575
|
+
action: "removed",
|
|
2576
|
+
artifactId: existing.id,
|
|
2577
|
+
artifact: toPublicArtifact(existing),
|
|
2578
|
+
reason: "unpin_to_ephemeral",
|
|
2579
|
+
durableTombstoneRequired: true
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2582
|
+
this.artifacts.set(updated.artifact.id, updated.artifact);
|
|
2583
|
+
changes.push({
|
|
2584
|
+
action: "updated",
|
|
2585
|
+
artifactId: updated.artifact.id,
|
|
2586
|
+
artifact: toPublicArtifact(updated.artifact)
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2333
2589
|
}
|
|
2334
|
-
const
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2590
|
+
const createdIds = new Set(
|
|
2591
|
+
changes.filter((change) => change.action === "created").map((change) => change.artifactId)
|
|
2592
|
+
);
|
|
2593
|
+
changes.push(
|
|
2594
|
+
...await this.evictOverflow(createdIds, changes, persistenceStrict)
|
|
2595
|
+
);
|
|
2596
|
+
const hasStrictDurableTransition = changes.some(
|
|
2597
|
+
shouldCommitBeforeDurablePersistence
|
|
2598
|
+
);
|
|
2599
|
+
let persistenceWarnings;
|
|
2600
|
+
if (hasStrictDurableTransition && !persistenceStrict) {
|
|
2601
|
+
try {
|
|
2602
|
+
persistenceWarnings = await this.persistChanges(changes, true);
|
|
2603
|
+
} catch (error) {
|
|
2604
|
+
this.restoreState(before);
|
|
2605
|
+
const artifactIds = changes.filter(shouldCommitBeforeDurablePersistence).map((change) => change.artifactId);
|
|
2606
|
+
const warning = "artifact durable removal not persisted; live changes rolled back";
|
|
2607
|
+
writeStderrLine(
|
|
2608
|
+
`[artifacts] session=${this.sessionId} action=upsert_rollback warning=${JSON.stringify(
|
|
2609
|
+
warning
|
|
2610
|
+
)} artifactIds=${JSON.stringify(artifactIds)} error=${JSON.stringify(
|
|
2611
|
+
error instanceof Error ? error.message : String(error)
|
|
2612
|
+
)}`
|
|
2613
|
+
);
|
|
2614
|
+
return {
|
|
2615
|
+
v: 1,
|
|
2616
|
+
sessionId: this.sessionId,
|
|
2617
|
+
changes: [],
|
|
2618
|
+
warnings: [warning],
|
|
2619
|
+
warningDetails: [
|
|
2620
|
+
persistenceFailureDetail({
|
|
2621
|
+
message: warning,
|
|
2622
|
+
operation: "upsert",
|
|
2623
|
+
artifactIds,
|
|
2624
|
+
error
|
|
2625
|
+
})
|
|
2626
|
+
]
|
|
2627
|
+
};
|
|
2338
2628
|
}
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2629
|
+
} else {
|
|
2630
|
+
persistenceWarnings = await this.persistChanges(
|
|
2631
|
+
changes,
|
|
2632
|
+
persistenceStrict
|
|
2633
|
+
);
|
|
2634
|
+
}
|
|
2635
|
+
warnings.push(...persistenceWarnings);
|
|
2636
|
+
warningDetails.push(
|
|
2637
|
+
...detailsForPersistenceWarnings(
|
|
2638
|
+
persistenceWarnings,
|
|
2639
|
+
changes,
|
|
2640
|
+
"upsert"
|
|
2641
|
+
)
|
|
2642
|
+
);
|
|
2643
|
+
stripDurableTombstoneMarkers(changes);
|
|
2644
|
+
} catch (error) {
|
|
2645
|
+
if (validationStrict || persistenceStrict || error instanceof SessionArtifactAuthorizationError) {
|
|
2646
|
+
this.restoreState(before);
|
|
2345
2647
|
}
|
|
2648
|
+
throw error;
|
|
2346
2649
|
}
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2650
|
+
return {
|
|
2651
|
+
v: 1,
|
|
2652
|
+
sessionId: this.sessionId,
|
|
2653
|
+
changes,
|
|
2654
|
+
...warnings.length > 0 ? { warnings } : {},
|
|
2655
|
+
...warningDetails.length > 0 ? { warningDetails } : {}
|
|
2656
|
+
};
|
|
2352
2657
|
});
|
|
2353
2658
|
}
|
|
2354
2659
|
findPublishedUpgradeTarget(artifact) {
|
|
@@ -2356,7 +2661,7 @@ var SessionArtifactStore = class {
|
|
|
2356
2661
|
return void 0;
|
|
2357
2662
|
}
|
|
2358
2663
|
const byUrl = this.artifacts.get(
|
|
2359
|
-
|
|
2664
|
+
stableSessionArtifactId(this.sessionId, `url:${artifact.url}`)
|
|
2360
2665
|
);
|
|
2361
2666
|
if (byUrl && (byUrl.storage !== "published" || byUrl.managedId === artifact.managedId)) {
|
|
2362
2667
|
return byUrl;
|
|
@@ -2392,27 +2697,552 @@ var SessionArtifactStore = class {
|
|
|
2392
2697
|
if (!existing) {
|
|
2393
2698
|
return { v: 1, sessionId: this.sessionId, changes: [] };
|
|
2394
2699
|
}
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2700
|
+
this.denyCrossClientMutation("remove", artifactId, existing, options);
|
|
2701
|
+
const removeChange = {
|
|
2702
|
+
action: "removed",
|
|
2703
|
+
artifactId,
|
|
2704
|
+
artifact: toPublicArtifact(existing),
|
|
2705
|
+
reason: "explicit",
|
|
2706
|
+
durableTombstoneRequired: existing.durableTombstoneRequired || existing.retention !== "ephemeral" ? true : void 0,
|
|
2707
|
+
removedClientId: existing.clientId
|
|
2708
|
+
};
|
|
2709
|
+
const changes = [removeChange];
|
|
2710
|
+
const needsDurableTombstone = removeChange.durableTombstoneRequired === true;
|
|
2711
|
+
if (needsDurableTombstone) {
|
|
2712
|
+
const before = this.cloneState();
|
|
2713
|
+
this.artifacts.delete(artifactId);
|
|
2714
|
+
try {
|
|
2715
|
+
await this.persistChanges(changes, true);
|
|
2716
|
+
} catch (error) {
|
|
2717
|
+
this.restoreState(before);
|
|
2718
|
+
const warning = "artifact removal not persisted; live artifact kept";
|
|
2719
|
+
return {
|
|
2720
|
+
v: 1,
|
|
2721
|
+
sessionId: this.sessionId,
|
|
2722
|
+
changes: [],
|
|
2723
|
+
warnings: [warning],
|
|
2724
|
+
warningDetails: [
|
|
2725
|
+
persistenceFailureDetail({
|
|
2726
|
+
message: warning,
|
|
2727
|
+
operation: "remove",
|
|
2728
|
+
artifactIds: [artifactId],
|
|
2729
|
+
error
|
|
2730
|
+
})
|
|
2731
|
+
]
|
|
2732
|
+
};
|
|
2733
|
+
}
|
|
2400
2734
|
}
|
|
2401
|
-
|
|
2735
|
+
if (!needsDurableTombstone) {
|
|
2736
|
+
this.artifacts.delete(artifactId);
|
|
2737
|
+
}
|
|
2738
|
+
const warnings = needsDurableTombstone ? [] : await this.persistChanges(changes, false);
|
|
2739
|
+
stripDurableTombstoneMarkers(changes);
|
|
2740
|
+
const warningDetails = detailsForPersistenceWarnings(
|
|
2741
|
+
warnings,
|
|
2742
|
+
changes,
|
|
2743
|
+
"remove"
|
|
2744
|
+
);
|
|
2402
2745
|
return {
|
|
2403
2746
|
v: 1,
|
|
2404
2747
|
sessionId: this.sessionId,
|
|
2405
|
-
changes
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
artifactId,
|
|
2409
|
-
artifact: toPublicArtifact(existing),
|
|
2410
|
-
reason: "explicit"
|
|
2411
|
-
}
|
|
2412
|
-
]
|
|
2748
|
+
changes,
|
|
2749
|
+
...warnings.length > 0 ? { warnings } : {},
|
|
2750
|
+
...warningDetails.length > 0 ? { warningDetails } : {}
|
|
2413
2751
|
};
|
|
2414
2752
|
});
|
|
2415
2753
|
}
|
|
2754
|
+
async restore(snapshot, options = {}) {
|
|
2755
|
+
if (!snapshot) return [];
|
|
2756
|
+
return this.enqueue(async () => {
|
|
2757
|
+
const warnings = [...snapshot?.warnings ?? []];
|
|
2758
|
+
const baselineWarnings = [...warnings];
|
|
2759
|
+
const previousState = this.cloneState();
|
|
2760
|
+
const preservedLiveEphemeralArtifacts = options.preserveLiveEphemeral ? Array.from(this.artifacts.values()).filter((artifact) => artifact.retention === "ephemeral").map(cloneStoredArtifact) : [];
|
|
2761
|
+
let restoredCount = 0;
|
|
2762
|
+
this.artifacts.clear();
|
|
2763
|
+
this.tombstonedIds.clear();
|
|
2764
|
+
this.tombstonedClientIds.clear();
|
|
2765
|
+
this.stickyEphemeralIds.clear();
|
|
2766
|
+
this.markerArtifacts.clear();
|
|
2767
|
+
if (snapshot) {
|
|
2768
|
+
this.insertSeq = 0;
|
|
2769
|
+
this.persistenceSeq = snapshot.sequence;
|
|
2770
|
+
this.durableEventsSinceSnapshot = 0;
|
|
2771
|
+
this.consecutiveSnapshotFailures = 0;
|
|
2772
|
+
for (const id of snapshot.tombstonedIds) {
|
|
2773
|
+
this.tombstonedIds.add(id);
|
|
2774
|
+
}
|
|
2775
|
+
for (const id of snapshot.stickyEphemeralIds.slice(
|
|
2776
|
+
-MAX_STICKY_EPHEMERAL_IDS
|
|
2777
|
+
)) {
|
|
2778
|
+
this.stickyEphemeralIds.add(id);
|
|
2779
|
+
}
|
|
2780
|
+
const markerIds = /* @__PURE__ */ new Set([
|
|
2781
|
+
...this.tombstonedIds,
|
|
2782
|
+
...this.stickyEphemeralIds
|
|
2783
|
+
]);
|
|
2784
|
+
for (const artifact of snapshot.markerArtifacts ?? []) {
|
|
2785
|
+
if (!markerIds.has(artifact.id)) continue;
|
|
2786
|
+
const markerArtifact = await this.normalizeRestoredMarkerArtifact(
|
|
2787
|
+
artifact,
|
|
2788
|
+
warnings
|
|
2789
|
+
);
|
|
2790
|
+
if (markerArtifact)
|
|
2791
|
+
this.markerArtifacts.set(artifact.id, markerArtifact);
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
for (const artifact of snapshot?.artifacts ?? []) {
|
|
2795
|
+
try {
|
|
2796
|
+
const input = persistedArtifactToInput(artifact);
|
|
2797
|
+
if (input.retention === "pinned") {
|
|
2798
|
+
input.retention = "restorable";
|
|
2799
|
+
warnings.push(
|
|
2800
|
+
`pinned artifact ${artifact.id} downgraded to restorable; runtime does not support pinned retention`
|
|
2801
|
+
);
|
|
2802
|
+
}
|
|
2803
|
+
let normalized = await this.normalizeInput(
|
|
2804
|
+
input,
|
|
2805
|
+
++this.receivedSeq,
|
|
2806
|
+
artifact.storage === "published" && !isFileArtifactUrl(artifact.url),
|
|
2807
|
+
{
|
|
2808
|
+
metadataBudget: "persisted",
|
|
2809
|
+
workspaceExpected: workspaceExpectedFromArtifact(artifact),
|
|
2810
|
+
hashWorkspaceContent: false
|
|
2811
|
+
}
|
|
2812
|
+
);
|
|
2813
|
+
if (this.stickyEphemeralIds.has(normalized.id) && normalized.retention !== "ephemeral") {
|
|
2814
|
+
normalized = {
|
|
2815
|
+
...normalized,
|
|
2816
|
+
retention: "ephemeral",
|
|
2817
|
+
persistenceWarning: "sticky_override_active"
|
|
2818
|
+
};
|
|
2819
|
+
}
|
|
2820
|
+
const stickyApplied = normalized.persistenceWarning === "sticky_override_active";
|
|
2821
|
+
if (normalized.id !== artifact.id) {
|
|
2822
|
+
warnings.push(`skipped artifact with mismatched id ${artifact.id}`);
|
|
2823
|
+
continue;
|
|
2824
|
+
}
|
|
2825
|
+
const retention = normalized.retention;
|
|
2826
|
+
let persistenceWarning;
|
|
2827
|
+
if (stickyApplied) {
|
|
2828
|
+
persistenceWarning = "sticky_override_active";
|
|
2829
|
+
} else if (normalized.status !== "available") {
|
|
2830
|
+
persistenceWarning = "metadata_only_restore";
|
|
2831
|
+
}
|
|
2832
|
+
const stored = {
|
|
2833
|
+
...normalized,
|
|
2834
|
+
retention,
|
|
2835
|
+
clientRetained: artifact.clientRetained,
|
|
2836
|
+
createdAt: artifact.createdAt,
|
|
2837
|
+
updatedAt: artifact.updatedAt,
|
|
2838
|
+
persistedAt: artifact.persistedAt,
|
|
2839
|
+
status: normalized.status,
|
|
2840
|
+
restoreState: "restored",
|
|
2841
|
+
persistenceWarning,
|
|
2842
|
+
durableTombstoneRequired: retention !== "ephemeral" ? true : void 0,
|
|
2843
|
+
insertSeq: ++this.insertSeq
|
|
2844
|
+
};
|
|
2845
|
+
this.artifacts.set(stored.id, stored);
|
|
2846
|
+
restoredCount++;
|
|
2847
|
+
} catch (error) {
|
|
2848
|
+
warnings.push(
|
|
2849
|
+
`skipped artifact restore: ${error instanceof Error ? error.message : String(error)}`
|
|
2850
|
+
);
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
if (snapshot.artifacts.length > 0 && restoredCount === 0) {
|
|
2854
|
+
this.restoreState(previousState);
|
|
2855
|
+
const rollbackWarnings = [
|
|
2856
|
+
...baselineWarnings,
|
|
2857
|
+
`${RESTORE_FAILED_WARNING_PREFIX}; kept existing live artifacts`
|
|
2858
|
+
];
|
|
2859
|
+
this.setLastRestoreWarnings(rollbackWarnings);
|
|
2860
|
+
return rollbackWarnings;
|
|
2861
|
+
}
|
|
2862
|
+
if (previousState.artifacts.size > 0 && baselineWarnings.some(isArtifactSnapshotCompletenessWarning)) {
|
|
2863
|
+
this.restoreState(previousState);
|
|
2864
|
+
const rollbackWarnings = [
|
|
2865
|
+
...baselineWarnings,
|
|
2866
|
+
restoredCount === 0 ? `${RESTORE_FAILED_WARNING_PREFIX}; kept existing live artifacts` : `${RESTORE_PARTIAL_FAILED_WARNING_PREFIX}; kept existing live artifacts`
|
|
2867
|
+
];
|
|
2868
|
+
this.setLastRestoreWarnings(rollbackWarnings);
|
|
2869
|
+
return rollbackWarnings;
|
|
2870
|
+
}
|
|
2871
|
+
for (const artifact of preservedLiveEphemeralArtifacts) {
|
|
2872
|
+
if (this.artifacts.has(artifact.id) || this.tombstonedIds.has(artifact.id)) {
|
|
2873
|
+
continue;
|
|
2874
|
+
}
|
|
2875
|
+
this.artifacts.set(artifact.id, {
|
|
2876
|
+
...artifact,
|
|
2877
|
+
insertSeq: ++this.insertSeq
|
|
2878
|
+
});
|
|
2879
|
+
}
|
|
2880
|
+
const evicted = await this.evictOverflow(/* @__PURE__ */ new Set(), []);
|
|
2881
|
+
if (evicted.length > 0) {
|
|
2882
|
+
warnings.push("restored artifact list pruned to live limit");
|
|
2883
|
+
warnings.push(...await this.persistChanges(evicted, false));
|
|
2884
|
+
}
|
|
2885
|
+
this.setLastRestoreWarnings(warnings);
|
|
2886
|
+
return warnings;
|
|
2887
|
+
});
|
|
2888
|
+
}
|
|
2889
|
+
async recordSnapshot() {
|
|
2890
|
+
const persistence = this.persistence;
|
|
2891
|
+
if (!persistence) {
|
|
2892
|
+
return [
|
|
2893
|
+
"artifact persistence unavailable; restored artifacts not snapshotted"
|
|
2894
|
+
];
|
|
2895
|
+
}
|
|
2896
|
+
return this.enqueue(async () => {
|
|
2897
|
+
const recordedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2898
|
+
const sequence = this.persistenceSeq + 1;
|
|
2899
|
+
try {
|
|
2900
|
+
await persistence.recordSnapshot(
|
|
2901
|
+
this.buildSnapshotPayload(recordedAt, sequence)
|
|
2902
|
+
);
|
|
2903
|
+
this.persistenceSeq = sequence;
|
|
2904
|
+
this.durableEventsSinceSnapshot = 0;
|
|
2905
|
+
this.consecutiveSnapshotFailures = 0;
|
|
2906
|
+
return [];
|
|
2907
|
+
} catch (error) {
|
|
2908
|
+
writeStderrLine(
|
|
2909
|
+
`[artifacts] session=${this.sessionId} action=snapshot_failed reason=${JSON.stringify(
|
|
2910
|
+
error instanceof Error ? error.message : String(error)
|
|
2911
|
+
)}`
|
|
2912
|
+
);
|
|
2913
|
+
return ["artifact snapshot not persisted"];
|
|
2914
|
+
}
|
|
2915
|
+
});
|
|
2916
|
+
}
|
|
2917
|
+
async normalizeRestoredMarkerArtifact(artifact, warnings) {
|
|
2918
|
+
try {
|
|
2919
|
+
const input = persistedArtifactToInput(artifact);
|
|
2920
|
+
if (input.retention === "pinned") {
|
|
2921
|
+
input.retention = "restorable";
|
|
2922
|
+
warnings.push(
|
|
2923
|
+
`pinned marker artifact ${artifact.id} downgraded to restorable; runtime does not support pinned retention`
|
|
2924
|
+
);
|
|
2925
|
+
}
|
|
2926
|
+
const normalized = await this.normalizeInput(
|
|
2927
|
+
input,
|
|
2928
|
+
++this.receivedSeq,
|
|
2929
|
+
artifact.storage === "published" && !isFileArtifactUrl(artifact.url),
|
|
2930
|
+
{
|
|
2931
|
+
metadataBudget: "persisted",
|
|
2932
|
+
workspaceExpected: workspaceExpectedFromArtifact(artifact),
|
|
2933
|
+
hashWorkspaceContent: false
|
|
2934
|
+
}
|
|
2935
|
+
);
|
|
2936
|
+
if (normalized.id !== artifact.id) {
|
|
2937
|
+
warnings.push(
|
|
2938
|
+
`skipped marker artifact with mismatched id ${artifact.id}`
|
|
2939
|
+
);
|
|
2940
|
+
return void 0;
|
|
2941
|
+
}
|
|
2942
|
+
return toPersistedArtifact(
|
|
2943
|
+
{
|
|
2944
|
+
...normalized,
|
|
2945
|
+
clientRetained: artifact.clientRetained,
|
|
2946
|
+
createdAt: artifact.createdAt,
|
|
2947
|
+
updatedAt: artifact.updatedAt,
|
|
2948
|
+
persistedAt: artifact.persistedAt
|
|
2949
|
+
},
|
|
2950
|
+
artifact.persistedAt ?? artifact.updatedAt
|
|
2951
|
+
);
|
|
2952
|
+
} catch (error) {
|
|
2953
|
+
warnings.push(
|
|
2954
|
+
`skipped marker artifact ${artifact.id}: ${error instanceof Error ? error.message : String(error)}`
|
|
2955
|
+
);
|
|
2956
|
+
return void 0;
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
cloneState() {
|
|
2960
|
+
return {
|
|
2961
|
+
artifacts: new Map(
|
|
2962
|
+
Array.from(this.artifacts.entries()).map(([id, artifact]) => [
|
|
2963
|
+
id,
|
|
2964
|
+
cloneStoredArtifact(artifact)
|
|
2965
|
+
])
|
|
2966
|
+
),
|
|
2967
|
+
receivedSeq: this.receivedSeq,
|
|
2968
|
+
insertSeq: this.insertSeq,
|
|
2969
|
+
persistenceSeq: this.persistenceSeq,
|
|
2970
|
+
durableEventsSinceSnapshot: this.durableEventsSinceSnapshot,
|
|
2971
|
+
consecutiveSnapshotFailures: this.consecutiveSnapshotFailures,
|
|
2972
|
+
tombstonedIds: new Set(this.tombstonedIds),
|
|
2973
|
+
tombstonedClientIds: new Map(this.tombstonedClientIds),
|
|
2974
|
+
stickyEphemeralIds: new Set(this.stickyEphemeralIds),
|
|
2975
|
+
markerArtifacts: new Map(this.markerArtifacts),
|
|
2976
|
+
lastRestoreWarnings: [...this.lastRestoreWarnings],
|
|
2977
|
+
lastRestoreWarningDetails: [...this.lastRestoreWarningDetails]
|
|
2978
|
+
};
|
|
2979
|
+
}
|
|
2980
|
+
restoreState(state) {
|
|
2981
|
+
this.artifacts.clear();
|
|
2982
|
+
for (const [id, artifact] of state.artifacts) {
|
|
2983
|
+
this.artifacts.set(id, cloneStoredArtifact(artifact));
|
|
2984
|
+
}
|
|
2985
|
+
this.receivedSeq = state.receivedSeq;
|
|
2986
|
+
this.insertSeq = state.insertSeq;
|
|
2987
|
+
this.persistenceSeq = state.persistenceSeq;
|
|
2988
|
+
this.durableEventsSinceSnapshot = state.durableEventsSinceSnapshot;
|
|
2989
|
+
this.consecutiveSnapshotFailures = state.consecutiveSnapshotFailures;
|
|
2990
|
+
this.tombstonedIds.clear();
|
|
2991
|
+
for (const id of state.tombstonedIds) {
|
|
2992
|
+
this.tombstonedIds.add(id);
|
|
2993
|
+
}
|
|
2994
|
+
this.tombstonedClientIds.clear();
|
|
2995
|
+
for (const [id, clientId] of state.tombstonedClientIds) {
|
|
2996
|
+
this.tombstonedClientIds.set(id, clientId);
|
|
2997
|
+
}
|
|
2998
|
+
this.stickyEphemeralIds.clear();
|
|
2999
|
+
for (const id of state.stickyEphemeralIds) {
|
|
3000
|
+
this.stickyEphemeralIds.add(id);
|
|
3001
|
+
}
|
|
3002
|
+
this.markerArtifacts.clear();
|
|
3003
|
+
for (const [id, artifact] of state.markerArtifacts) {
|
|
3004
|
+
this.markerArtifacts.set(id, artifact);
|
|
3005
|
+
}
|
|
3006
|
+
this.setLastRestoreWarnings(state.lastRestoreWarnings);
|
|
3007
|
+
this.lastRestoreWarningDetails = [...state.lastRestoreWarningDetails];
|
|
3008
|
+
}
|
|
3009
|
+
async persistChanges(changes, strict = false) {
|
|
3010
|
+
const durableChanges = changes.filter(isDurablePersistenceChange);
|
|
3011
|
+
if (durableChanges.length === 0) {
|
|
3012
|
+
return [];
|
|
3013
|
+
}
|
|
3014
|
+
if (!this.persistence) {
|
|
3015
|
+
if (strict) {
|
|
3016
|
+
throw new SessionArtifactValidationError(
|
|
3017
|
+
"artifact persistence is unavailable",
|
|
3018
|
+
"retention"
|
|
3019
|
+
);
|
|
3020
|
+
}
|
|
3021
|
+
const warnings = this.downgradeDurableChanges(durableChanges);
|
|
3022
|
+
this.applyDurableMarkers(durableChanges);
|
|
3023
|
+
return warnings;
|
|
3024
|
+
}
|
|
3025
|
+
const recordedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3026
|
+
for (const change of durableChanges) {
|
|
3027
|
+
if (change.action === "removed") continue;
|
|
3028
|
+
const stored = this.artifacts.get(change.artifactId);
|
|
3029
|
+
if (!stored) continue;
|
|
3030
|
+
const artifact = {
|
|
3031
|
+
...toPublicArtifact(stored),
|
|
3032
|
+
persistedAt: recordedAt
|
|
3033
|
+
};
|
|
3034
|
+
change.artifact = artifact;
|
|
3035
|
+
}
|
|
3036
|
+
const sequence = this.persistenceSeq + 1;
|
|
3037
|
+
const payload = {
|
|
3038
|
+
v: SESSION_ARTIFACT_PERSISTENCE_VERSION,
|
|
3039
|
+
sessionId: this.sessionId,
|
|
3040
|
+
sequence,
|
|
3041
|
+
recordedAt,
|
|
3042
|
+
changes: durableChanges.map(
|
|
3043
|
+
(change) => toPersistedChange(change, recordedAt)
|
|
3044
|
+
)
|
|
3045
|
+
};
|
|
3046
|
+
try {
|
|
3047
|
+
await this.persistence.recordEvent(payload);
|
|
3048
|
+
this.persistenceSeq = sequence;
|
|
3049
|
+
for (const change of durableChanges) {
|
|
3050
|
+
if (change.action === "removed") continue;
|
|
3051
|
+
const stored = this.artifacts.get(change.artifactId);
|
|
3052
|
+
if (!stored) continue;
|
|
3053
|
+
stored.persistedAt = recordedAt;
|
|
3054
|
+
stored.durableTombstoneRequired = true;
|
|
3055
|
+
change.artifact = toPublicArtifact(stored);
|
|
3056
|
+
}
|
|
3057
|
+
this.applyDurableMarkers(durableChanges);
|
|
3058
|
+
await this.maybeRecordSnapshot(recordedAt);
|
|
3059
|
+
return [];
|
|
3060
|
+
} catch (error) {
|
|
3061
|
+
if (strict) {
|
|
3062
|
+
throw error;
|
|
3063
|
+
}
|
|
3064
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
3065
|
+
const artifactIds = durableChanges.map((change) => change.artifactId);
|
|
3066
|
+
writeStderrLine(
|
|
3067
|
+
`[artifacts] session=${this.sessionId} action=persist_failed sequence=${payload.sequence} artifactIds=${JSON.stringify(
|
|
3068
|
+
artifactIds
|
|
3069
|
+
)} reason=${JSON.stringify(reason)}`
|
|
3070
|
+
);
|
|
3071
|
+
const warnings = this.downgradeDurableChanges(durableChanges);
|
|
3072
|
+
this.applyDurableMarkers(durableChanges);
|
|
3073
|
+
return warnings;
|
|
3074
|
+
}
|
|
3075
|
+
}
|
|
3076
|
+
async maybeRecordSnapshot(recordedAt) {
|
|
3077
|
+
if (!this.persistence) return;
|
|
3078
|
+
this.durableEventsSinceSnapshot++;
|
|
3079
|
+
const snapshotThreshold = SNAPSHOT_AFTER_DURABLE_EVENTS * Math.min(
|
|
3080
|
+
MAX_SNAPSHOT_BACKOFF_MULTIPLIER,
|
|
3081
|
+
2 ** this.consecutiveSnapshotFailures
|
|
3082
|
+
);
|
|
3083
|
+
if (this.durableEventsSinceSnapshot < snapshotThreshold) {
|
|
3084
|
+
return;
|
|
3085
|
+
}
|
|
3086
|
+
const sequence = this.persistenceSeq + 1;
|
|
3087
|
+
try {
|
|
3088
|
+
await this.persistence.recordSnapshot(
|
|
3089
|
+
this.buildSnapshotPayload(recordedAt, sequence)
|
|
3090
|
+
);
|
|
3091
|
+
this.persistenceSeq = sequence;
|
|
3092
|
+
this.durableEventsSinceSnapshot = 0;
|
|
3093
|
+
this.consecutiveSnapshotFailures = 0;
|
|
3094
|
+
} catch (error) {
|
|
3095
|
+
this.consecutiveSnapshotFailures = Math.min(
|
|
3096
|
+
this.consecutiveSnapshotFailures + 1,
|
|
3097
|
+
Math.log2(MAX_SNAPSHOT_BACKOFF_MULTIPLIER)
|
|
3098
|
+
);
|
|
3099
|
+
writeStderrLine(
|
|
3100
|
+
`[artifacts] session=${this.sessionId} action=snapshot_failed reason=${JSON.stringify(
|
|
3101
|
+
error instanceof Error ? error.message : String(error)
|
|
3102
|
+
)}`
|
|
3103
|
+
);
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
buildSnapshotPayload(recordedAt, sequence) {
|
|
3107
|
+
const artifacts = Array.from(this.artifacts.values()).filter((artifact) => artifact.retention !== "ephemeral").sort((a, b) => a.insertSeq - b.insertSeq).map((artifact) => toPersistedArtifact(artifact, recordedAt));
|
|
3108
|
+
const stickyEphemeralIds = Array.from(this.stickyEphemeralIds).filter(
|
|
3109
|
+
(id) => this.artifacts.has(id) || this.markerArtifacts.has(id)
|
|
3110
|
+
);
|
|
3111
|
+
const markerArtifacts = this.buildMarkerArtifacts(
|
|
3112
|
+
recordedAt,
|
|
3113
|
+
stickyEphemeralIds
|
|
3114
|
+
);
|
|
3115
|
+
return {
|
|
3116
|
+
v: SESSION_ARTIFACT_PERSISTENCE_VERSION,
|
|
3117
|
+
sessionId: this.sessionId,
|
|
3118
|
+
sequence,
|
|
3119
|
+
recordedAt,
|
|
3120
|
+
artifacts,
|
|
3121
|
+
tombstonedIds: Array.from(this.tombstonedIds),
|
|
3122
|
+
stickyEphemeralIds,
|
|
3123
|
+
...markerArtifacts.length > 0 ? { markerArtifacts } : {}
|
|
3124
|
+
};
|
|
3125
|
+
}
|
|
3126
|
+
buildMarkerArtifacts(recordedAt, stickyEphemeralIds) {
|
|
3127
|
+
const markerIds = [...this.tombstonedIds, ...stickyEphemeralIds];
|
|
3128
|
+
const artifacts = [];
|
|
3129
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3130
|
+
for (const id of markerIds) {
|
|
3131
|
+
if (seen.has(id)) continue;
|
|
3132
|
+
seen.add(id);
|
|
3133
|
+
const live = this.artifacts.get(id);
|
|
3134
|
+
if (live) {
|
|
3135
|
+
artifacts.push(toPersistedArtifact(toPublicArtifact(live), recordedAt));
|
|
3136
|
+
continue;
|
|
3137
|
+
}
|
|
3138
|
+
const markerArtifact = this.markerArtifacts.get(id);
|
|
3139
|
+
if (markerArtifact) {
|
|
3140
|
+
artifacts.push(markerArtifact);
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
return artifacts;
|
|
3144
|
+
}
|
|
3145
|
+
downgradeDurableChanges(changes) {
|
|
3146
|
+
let downgraded = false;
|
|
3147
|
+
let removalNotPersisted = false;
|
|
3148
|
+
for (const change of changes) {
|
|
3149
|
+
if (change.action === "removed") {
|
|
3150
|
+
removalNotPersisted = true;
|
|
3151
|
+
if (change.reason === "explicit") {
|
|
3152
|
+
this.rememberTombstone(change);
|
|
3153
|
+
this.stickyEphemeralIds.delete(change.artifactId);
|
|
3154
|
+
}
|
|
3155
|
+
continue;
|
|
3156
|
+
}
|
|
3157
|
+
const stored = this.artifacts.get(change.artifactId);
|
|
3158
|
+
if (!stored) continue;
|
|
3159
|
+
stored.retention = "ephemeral";
|
|
3160
|
+
stored.persistenceWarning = "persistence_unavailable";
|
|
3161
|
+
stored.durableTombstoneRequired = stored.durableTombstoneRequired || stored.persistedAt !== void 0;
|
|
3162
|
+
delete stored.persistedAt;
|
|
3163
|
+
change.artifact = toPublicArtifact(stored);
|
|
3164
|
+
downgraded = true;
|
|
3165
|
+
}
|
|
3166
|
+
const warnings = [];
|
|
3167
|
+
if (downgraded) {
|
|
3168
|
+
warnings.push(
|
|
3169
|
+
"artifact persistence unavailable; durable artifacts kept ephemeral"
|
|
3170
|
+
);
|
|
3171
|
+
}
|
|
3172
|
+
if (removalNotPersisted) {
|
|
3173
|
+
warnings.push("artifact removal not persisted; live removal kept");
|
|
3174
|
+
}
|
|
3175
|
+
return warnings;
|
|
3176
|
+
}
|
|
3177
|
+
applyDurableMarkers(changes) {
|
|
3178
|
+
for (const change of changes) {
|
|
3179
|
+
if (change.action === "removed") {
|
|
3180
|
+
if (change.reason === "explicit") {
|
|
3181
|
+
this.rememberTombstone(change);
|
|
3182
|
+
this.stickyEphemeralIds.delete(change.artifactId);
|
|
3183
|
+
} else if (change.reason === "eviction") {
|
|
3184
|
+
this.stickyEphemeralIds.delete(change.artifactId);
|
|
3185
|
+
this.markerArtifacts.delete(change.artifactId);
|
|
3186
|
+
} else if (change.reason === "unpin_to_ephemeral") {
|
|
3187
|
+
this.rememberStickyEphemeral(change);
|
|
3188
|
+
}
|
|
3189
|
+
continue;
|
|
3190
|
+
}
|
|
3191
|
+
if (change.artifact && change.artifact.retention !== "ephemeral") {
|
|
3192
|
+
this.tombstonedIds.delete(change.artifactId);
|
|
3193
|
+
this.tombstonedClientIds.delete(change.artifactId);
|
|
3194
|
+
this.stickyEphemeralIds.delete(change.artifactId);
|
|
3195
|
+
this.markerArtifacts.delete(change.artifactId);
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
rememberStickyEphemeral(change) {
|
|
3200
|
+
const artifactId = change.artifactId;
|
|
3201
|
+
this.stickyEphemeralIds.delete(artifactId);
|
|
3202
|
+
this.stickyEphemeralIds.add(artifactId);
|
|
3203
|
+
if (change.artifact) {
|
|
3204
|
+
this.markerArtifacts.set(
|
|
3205
|
+
artifactId,
|
|
3206
|
+
toPersistedArtifact(change.artifact, change.artifact.updatedAt)
|
|
3207
|
+
);
|
|
3208
|
+
}
|
|
3209
|
+
while (this.stickyEphemeralIds.size > MAX_STICKY_EPHEMERAL_IDS) {
|
|
3210
|
+
const oldest2 = this.stickyEphemeralIds.values().next().value;
|
|
3211
|
+
if (oldest2 === void 0) break;
|
|
3212
|
+
this.stickyEphemeralIds.delete(oldest2);
|
|
3213
|
+
this.markerArtifacts.delete(oldest2);
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
3216
|
+
rememberTombstone(change) {
|
|
3217
|
+
this.tombstonedIds.delete(change.artifactId);
|
|
3218
|
+
this.tombstonedIds.add(change.artifactId);
|
|
3219
|
+
this.tombstonedClientIds.set(
|
|
3220
|
+
change.artifactId,
|
|
3221
|
+
change.removedClientId ?? this.artifacts.get(change.artifactId)?.clientId
|
|
3222
|
+
);
|
|
3223
|
+
if (change.artifact) {
|
|
3224
|
+
this.markerArtifacts.set(
|
|
3225
|
+
change.artifactId,
|
|
3226
|
+
toPersistedArtifact(change.artifact, change.artifact.updatedAt)
|
|
3227
|
+
);
|
|
3228
|
+
}
|
|
3229
|
+
while (this.tombstonedIds.size > MAX_TOMBSTONED_IDS) {
|
|
3230
|
+
const oldest2 = this.tombstonedIds.values().next().value;
|
|
3231
|
+
if (oldest2 === void 0) break;
|
|
3232
|
+
writeStderrLine(
|
|
3233
|
+
`[artifacts] session=${this.sessionId} action=tombstone_evicted artifactId=${JSON.stringify(
|
|
3234
|
+
oldest2
|
|
3235
|
+
)} limit=${MAX_TOMBSTONED_IDS}`
|
|
3236
|
+
);
|
|
3237
|
+
this.tombstonedIds.delete(oldest2);
|
|
3238
|
+
this.tombstonedClientIds.delete(oldest2);
|
|
3239
|
+
this.markerArtifacts.delete(oldest2);
|
|
3240
|
+
}
|
|
3241
|
+
}
|
|
3242
|
+
setLastRestoreWarnings(warnings) {
|
|
3243
|
+
this.lastRestoreWarnings = [...warnings];
|
|
3244
|
+
this.lastRestoreWarningDetails = detailsForRestoreWarnings(warnings);
|
|
3245
|
+
}
|
|
2416
3246
|
enqueue(operation) {
|
|
2417
3247
|
const result = this.operationQueue.then(operation, operation);
|
|
2418
3248
|
this.operationQueue = result.then(
|
|
@@ -2421,7 +3251,21 @@ var SessionArtifactStore = class {
|
|
|
2421
3251
|
);
|
|
2422
3252
|
return result;
|
|
2423
3253
|
}
|
|
2424
|
-
|
|
3254
|
+
denyCrossClientMutation(action, artifactId, existing, options) {
|
|
3255
|
+
if (existing.source !== "client" || existing.clientId === void 0 || existing.clientId === options?.clientId) {
|
|
3256
|
+
return;
|
|
3257
|
+
}
|
|
3258
|
+
writeStderrLine(
|
|
3259
|
+
`[artifacts] session=${this.sessionId} action=${action}_denied artifactId=${artifactId} owner=${existing.clientId} requester=${options?.clientId ?? "<anonymous>"}`
|
|
3260
|
+
);
|
|
3261
|
+
throw new SessionArtifactAuthorizationError(
|
|
3262
|
+
this.sessionId,
|
|
3263
|
+
artifactId,
|
|
3264
|
+
existing.clientId,
|
|
3265
|
+
options?.clientId
|
|
3266
|
+
);
|
|
3267
|
+
}
|
|
3268
|
+
async normalizeInput(input, receivedSeq, trustedPublisherFromCaller, options = {}) {
|
|
2425
3269
|
if (!input || typeof input !== "object") {
|
|
2426
3270
|
throw new SessionArtifactValidationError("Artifact must be an object");
|
|
2427
3271
|
}
|
|
@@ -2440,7 +3284,10 @@ var SessionArtifactStore = class {
|
|
|
2440
3284
|
);
|
|
2441
3285
|
}
|
|
2442
3286
|
const trustedPublisher = trustedPublisherFromCaller;
|
|
2443
|
-
const workspacePath = input.workspacePath ? normalizeWorkspacePath(
|
|
3287
|
+
const workspacePath = input.workspacePath ? normalizeWorkspacePath(
|
|
3288
|
+
input.workspacePath,
|
|
3289
|
+
await this.getRealWorkspaceCwdForValidation()
|
|
3290
|
+
) : void 0;
|
|
2444
3291
|
const managedId = normalizeManagedId(input.managedId);
|
|
2445
3292
|
const rawStorage = input.storage;
|
|
2446
3293
|
const storage = inferStorage(rawStorage, {
|
|
@@ -2456,14 +3303,26 @@ var SessionArtifactStore = class {
|
|
|
2456
3303
|
url,
|
|
2457
3304
|
trustedPublisher
|
|
2458
3305
|
});
|
|
2459
|
-
const
|
|
2460
|
-
|
|
3306
|
+
const retention = normalizeRetention(input.retention, {
|
|
3307
|
+
persistenceAvailable: this.persistence !== void 0
|
|
3308
|
+
});
|
|
3309
|
+
const workspaceStatus = workspacePath ? await this.getInitialWorkspaceStatus(
|
|
3310
|
+
workspacePath,
|
|
3311
|
+
options.workspaceExpected,
|
|
3312
|
+
{ hashContent: options.hashWorkspaceContent !== false }
|
|
3313
|
+
) : void 0;
|
|
2461
3314
|
if (workspaceStatus?.escaped) {
|
|
2462
3315
|
throw new SessionArtifactValidationError(
|
|
2463
3316
|
"workspacePath must stay inside the workspace",
|
|
2464
3317
|
"workspacePath"
|
|
2465
3318
|
);
|
|
2466
3319
|
}
|
|
3320
|
+
const metadata = withWorkspaceContentHashMetadata(
|
|
3321
|
+
normalizeMetadata(input.metadata, {
|
|
3322
|
+
budget: options.metadataBudget ?? "user"
|
|
3323
|
+
}),
|
|
3324
|
+
workspaceStatus
|
|
3325
|
+
);
|
|
2467
3326
|
const kind = normalizeKind(
|
|
2468
3327
|
input.kind ?? inferKind({ storage, workspacePath, url })
|
|
2469
3328
|
);
|
|
@@ -2474,11 +3333,12 @@ var SessionArtifactStore = class {
|
|
|
2474
3333
|
managedId,
|
|
2475
3334
|
url
|
|
2476
3335
|
});
|
|
2477
|
-
const id =
|
|
3336
|
+
const id = stableSessionArtifactId(this.sessionId, identityKey);
|
|
2478
3337
|
return {
|
|
2479
3338
|
id,
|
|
2480
3339
|
identityKey,
|
|
2481
3340
|
receivedSeq,
|
|
3341
|
+
retentionExplicit: input.retention !== void 0,
|
|
2482
3342
|
retentionSource: source,
|
|
2483
3343
|
trustedPublisher,
|
|
2484
3344
|
kind,
|
|
@@ -2492,9 +3352,12 @@ var SessionArtifactStore = class {
|
|
|
2492
3352
|
managedId,
|
|
2493
3353
|
url,
|
|
2494
3354
|
mimeType: normalizeString(input.mimeType, "mimeType", 120, false),
|
|
2495
|
-
sizeBytes: input.sizeBytes !== void 0 ? normalizeSizeBytes(input.sizeBytes) :
|
|
3355
|
+
sizeBytes: workspaceStatus?.sizeBytes !== void 0 ? workspaceStatus.sizeBytes : input.sizeBytes !== void 0 ? normalizeSizeBytes(input.sizeBytes) : void 0,
|
|
2496
3356
|
metadata,
|
|
2497
|
-
|
|
3357
|
+
retention,
|
|
3358
|
+
restoreState: "live",
|
|
3359
|
+
...this.persistence === void 0 && retention !== "ephemeral" ? { persistenceWarning: "persistence_unavailable" } : {},
|
|
3360
|
+
clientRetained: source === "client" && (input.clientRetained !== void 0 ? input.clientRetained === true : true),
|
|
2498
3361
|
createdAt: now,
|
|
2499
3362
|
updatedAt: now,
|
|
2500
3363
|
toolCallId: normalizeString(input.toolCallId, "toolCallId", 200, false),
|
|
@@ -2508,6 +3371,16 @@ var SessionArtifactStore = class {
|
|
|
2508
3371
|
clientId: normalizeString(input.clientId, "clientId", 200, false)
|
|
2509
3372
|
};
|
|
2510
3373
|
}
|
|
3374
|
+
shouldSuppressTombstonedUpsert(artifact) {
|
|
3375
|
+
if (!this.tombstonedIds.has(artifact.id)) {
|
|
3376
|
+
return false;
|
|
3377
|
+
}
|
|
3378
|
+
const tombstonedClientId = this.tombstonedClientIds.get(artifact.id);
|
|
3379
|
+
if (artifact.source !== "client") {
|
|
3380
|
+
return false;
|
|
3381
|
+
}
|
|
3382
|
+
return !(artifact.retentionExplicit && artifact.clientId !== void 0 && artifact.clientId === tombstonedClientId);
|
|
3383
|
+
}
|
|
2511
3384
|
async refreshWorkspaceStatuses() {
|
|
2512
3385
|
const now = Date.now();
|
|
2513
3386
|
const staleWorkspaceArtifacts = Array.from(this.artifacts.values()).filter((artifact) => artifact.workspacePath).filter((artifact) => shouldRefreshWorkspaceStatus(artifact, now));
|
|
@@ -2520,12 +3393,24 @@ var SessionArtifactStore = class {
|
|
|
2520
3393
|
})
|
|
2521
3394
|
);
|
|
2522
3395
|
}
|
|
2523
|
-
|
|
3396
|
+
applyStickyEphemeralOverride(artifact) {
|
|
3397
|
+
if (!this.stickyEphemeralIds.has(artifact.id) || artifact.retentionExplicit || artifact.retention === "ephemeral") {
|
|
3398
|
+
return artifact;
|
|
3399
|
+
}
|
|
3400
|
+
return {
|
|
3401
|
+
...artifact,
|
|
3402
|
+
retention: "ephemeral",
|
|
3403
|
+
retentionExplicit: true,
|
|
3404
|
+
persistenceWarning: "sticky_override_active"
|
|
3405
|
+
};
|
|
3406
|
+
}
|
|
3407
|
+
async getInitialWorkspaceStatus(workspacePath, expected, options = { hashContent: true }) {
|
|
2524
3408
|
try {
|
|
2525
3409
|
return await getWorkspaceStatus(
|
|
2526
|
-
this.workspaceCwd,
|
|
2527
3410
|
workspacePath,
|
|
2528
|
-
this.getRealWorkspaceCwd()
|
|
3411
|
+
this.getRealWorkspaceCwd(),
|
|
3412
|
+
expected,
|
|
3413
|
+
options
|
|
2529
3414
|
);
|
|
2530
3415
|
} catch (error) {
|
|
2531
3416
|
const reason = error instanceof Error ? error.message : String(error);
|
|
@@ -2541,16 +3426,23 @@ var SessionArtifactStore = class {
|
|
|
2541
3426
|
}
|
|
2542
3427
|
try {
|
|
2543
3428
|
const status = await getWorkspaceStatus(
|
|
2544
|
-
this.workspaceCwd,
|
|
2545
3429
|
artifact.workspacePath,
|
|
2546
|
-
this.getRealWorkspaceCwd()
|
|
3430
|
+
this.getRealWorkspaceCwd(),
|
|
3431
|
+
{
|
|
3432
|
+
sizeBytes: artifact.sizeBytes,
|
|
3433
|
+
mtimeMs: artifact.metadata?.[WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY],
|
|
3434
|
+
sha256: artifact.metadata?.[WORKSPACE_CONTENT_SHA256_METADATA_KEY]
|
|
3435
|
+
}
|
|
2547
3436
|
);
|
|
2548
|
-
|
|
2549
|
-
artifact.
|
|
3437
|
+
const changed = isWorkspaceContentChanged(artifact, status);
|
|
3438
|
+
artifact.status = changed ? "changed" : status.status;
|
|
3439
|
+
if (!changed) {
|
|
3440
|
+
artifact.sizeBytes = status.sizeBytes;
|
|
3441
|
+
}
|
|
2550
3442
|
if (status.escaped) {
|
|
2551
3443
|
artifact.status = "missing";
|
|
2552
3444
|
artifact.sizeBytes = void 0;
|
|
2553
|
-
|
|
3445
|
+
artifact.hideWorkspacePath = true;
|
|
2554
3446
|
}
|
|
2555
3447
|
artifact.lastStatAt = options.now ?? Date.now();
|
|
2556
3448
|
} catch (error) {
|
|
@@ -2579,7 +3471,18 @@ var SessionArtifactStore = class {
|
|
|
2579
3471
|
}
|
|
2580
3472
|
return this.realWorkspaceCwdPromise;
|
|
2581
3473
|
}
|
|
2582
|
-
async
|
|
3474
|
+
async getRealWorkspaceCwdForValidation() {
|
|
3475
|
+
try {
|
|
3476
|
+
return await this.getRealWorkspaceCwd();
|
|
3477
|
+
} catch (error) {
|
|
3478
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
3479
|
+
throw new SessionArtifactValidationError(
|
|
3480
|
+
`workspacePath could not be inspected: ${reason}`,
|
|
3481
|
+
"workspacePath"
|
|
3482
|
+
);
|
|
3483
|
+
}
|
|
3484
|
+
}
|
|
3485
|
+
async evictOverflow(createdIds, changes, strict = false) {
|
|
2583
3486
|
const removed = [];
|
|
2584
3487
|
if (this.artifacts.size <= this.maxArtifacts) {
|
|
2585
3488
|
return removed;
|
|
@@ -2607,10 +3510,17 @@ var SessionArtifactStore = class {
|
|
|
2607
3510
|
action: "removed",
|
|
2608
3511
|
artifactId: artifact.id,
|
|
2609
3512
|
artifact: toPublicArtifact(artifact),
|
|
3513
|
+
durableTombstoneRequired: artifact.durableTombstoneRequired,
|
|
2610
3514
|
reason: "eviction"
|
|
2611
3515
|
});
|
|
2612
3516
|
}
|
|
2613
3517
|
const overflowCreated = Array.from(this.artifacts.values()).filter((artifact) => createdInThisBatch.has(artifact.id)).sort((a, b) => b.receivedSeq - a.receivedSeq);
|
|
3518
|
+
if (strict && overflowCreated.length > 0 && this.artifacts.size > this.maxArtifacts) {
|
|
3519
|
+
throw new SessionArtifactValidationError(
|
|
3520
|
+
"artifact store is full; no eviction candidate is available",
|
|
3521
|
+
"artifactId"
|
|
3522
|
+
);
|
|
3523
|
+
}
|
|
2614
3524
|
for (const artifact of overflowCreated) {
|
|
2615
3525
|
if (this.artifacts.size <= this.maxArtifacts) {
|
|
2616
3526
|
break;
|
|
@@ -2659,7 +3569,12 @@ function mergeBatchArtifact(existing, next) {
|
|
|
2659
3569
|
trustedPublisher: true,
|
|
2660
3570
|
createdAt: existing.createdAt,
|
|
2661
3571
|
receivedSeq: existing.receivedSeq,
|
|
3572
|
+
retentionExplicit: existing.retentionExplicit || next.retentionExplicit,
|
|
2662
3573
|
retentionSource: existing.retentionSource,
|
|
3574
|
+
retention: mergeRetention(existing, next),
|
|
3575
|
+
restoreState: "live",
|
|
3576
|
+
persistenceWarning: existing.persistenceWarning ?? next.persistenceWarning,
|
|
3577
|
+
persistedAt: existing.persistedAt ?? next.persistedAt,
|
|
2663
3578
|
clientRetained: existing.clientRetained || next.clientRetained,
|
|
2664
3579
|
lastStatAt: void 0
|
|
2665
3580
|
};
|
|
@@ -2673,6 +3588,8 @@ function mergeBatchArtifact(existing, next) {
|
|
|
2673
3588
|
metadata: mergeMetadata(existing, next),
|
|
2674
3589
|
clientRetained: existing.clientRetained || next.clientRetained,
|
|
2675
3590
|
trustedPublisher: existing.trustedPublisher || next.trustedPublisher,
|
|
3591
|
+
retentionExplicit: existing.retentionExplicit || next.retentionExplicit,
|
|
3592
|
+
retention: mergeRetention(existing, next),
|
|
2676
3593
|
lastStatAt: next.lastStatAt ?? existing.lastStatAt
|
|
2677
3594
|
};
|
|
2678
3595
|
}
|
|
@@ -2692,9 +3609,14 @@ function mergeArtifact(existing, incoming) {
|
|
|
2692
3609
|
managedId: publishedUpdate ? incoming.managedId ?? existing.managedId : existing.managedId,
|
|
2693
3610
|
url: publishedUpdate ? incoming.url ?? existing.url : existing.url,
|
|
2694
3611
|
workspacePath: publishedUpdate || existing.storage === "published" ? void 0 : existing.workspacePath ?? incoming.workspacePath,
|
|
3612
|
+
hideWorkspacePath: publishedUpdate || existing.storage === "published" || incoming.workspacePath ? void 0 : existing.hideWorkspacePath,
|
|
2695
3613
|
mimeType: publishedUpdate ? incoming.mimeType ?? existing.mimeType : existing.mimeType,
|
|
2696
3614
|
sizeBytes: existing.storage === "published" && !publishedUpdate ? existing.sizeBytes : mergeSizeBytes(existing, incoming),
|
|
2697
3615
|
metadata: existing.storage === "published" && !publishedUpdate ? existing.metadata : mergeMetadata(existing, incoming),
|
|
3616
|
+
retention: mergeRetention(existing, incoming),
|
|
3617
|
+
restoreState: "live",
|
|
3618
|
+
persistenceWarning: incoming.retentionExplicit && incoming.retention !== "ephemeral" ? incoming.persistenceWarning : existing.persistenceWarning ?? incoming.persistenceWarning,
|
|
3619
|
+
persistedAt: incoming.retentionExplicit && incoming.retention === "ephemeral" ? void 0 : existing.persistedAt ?? incoming.persistedAt,
|
|
2698
3620
|
source: existing.source,
|
|
2699
3621
|
retentionSource: existing.retentionSource,
|
|
2700
3622
|
trustedPublisher: existing.trustedPublisher || incoming.trustedPublisher,
|
|
@@ -2706,6 +3628,7 @@ function mergeArtifact(existing, incoming) {
|
|
|
2706
3628
|
next.title = incoming.title;
|
|
2707
3629
|
next.description = incoming.description;
|
|
2708
3630
|
delete next.workspacePath;
|
|
3631
|
+
delete next.hideWorkspacePath;
|
|
2709
3632
|
}
|
|
2710
3633
|
const changed = !publicArtifactsEqual(
|
|
2711
3634
|
toPublicArtifact(existing),
|
|
@@ -2717,8 +3640,12 @@ function mergeArtifact(existing, incoming) {
|
|
|
2717
3640
|
return { artifact: changed ? next : existing, changed };
|
|
2718
3641
|
}
|
|
2719
3642
|
__name(mergeArtifact, "mergeArtifact");
|
|
3643
|
+
function shouldRecordEphemeralUnpin(existing, incoming) {
|
|
3644
|
+
return incoming.retentionExplicit && incoming.retention === "ephemeral" && (existing.retention !== "ephemeral" || existing.persistedAt !== void 0 || existing.durableTombstoneRequired === true);
|
|
3645
|
+
}
|
|
3646
|
+
__name(shouldRecordEphemeralUnpin, "shouldRecordEphemeralUnpin");
|
|
2720
3647
|
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;
|
|
3648
|
+
return a.id === b.id && a.kind === b.kind && a.storage === b.storage && a.source === b.source && a.status === b.status && a.title === b.title && a.description === b.description && a.workspacePath === b.workspacePath && a.managedId === b.managedId && a.url === b.url && a.mimeType === b.mimeType && a.sizeBytes === b.sizeBytes && metadataEqual(a.metadata, b.metadata) && a.retention === b.retention && a.restoreState === b.restoreState && a.persistenceWarning === b.persistenceWarning && a.persistedAt === b.persistedAt && a.clientRetained === b.clientRetained && a.createdAt === b.createdAt && a.updatedAt === b.updatedAt && a.toolCallId === b.toolCallId && a.toolName === b.toolName && a.hookEventName === b.hookEventName && a.clientId === b.clientId;
|
|
2722
3649
|
}
|
|
2723
3650
|
__name(publicArtifactsEqual, "publicArtifactsEqual");
|
|
2724
3651
|
function metadataEqual(a, b) {
|
|
@@ -2740,6 +3667,24 @@ function mergeSizeBytes(existing, incoming) {
|
|
|
2740
3667
|
return existing.sizeBytes;
|
|
2741
3668
|
}
|
|
2742
3669
|
__name(mergeSizeBytes, "mergeSizeBytes");
|
|
3670
|
+
function strongestRetention(a, b) {
|
|
3671
|
+
const rank = {
|
|
3672
|
+
ephemeral: 0,
|
|
3673
|
+
restorable: 1
|
|
3674
|
+
};
|
|
3675
|
+
return rank[b] > rank[a] ? b : a;
|
|
3676
|
+
}
|
|
3677
|
+
__name(strongestRetention, "strongestRetention");
|
|
3678
|
+
function mergeRetention(existing, incoming) {
|
|
3679
|
+
if (incoming.retentionExplicit && incoming.retention === "ephemeral") {
|
|
3680
|
+
return "ephemeral";
|
|
3681
|
+
}
|
|
3682
|
+
if (existing.retentionExplicit && existing.retention === "ephemeral" && !incoming.retentionExplicit) {
|
|
3683
|
+
return "ephemeral";
|
|
3684
|
+
}
|
|
3685
|
+
return strongestRetention(existing.retention, incoming.retention);
|
|
3686
|
+
}
|
|
3687
|
+
__name(mergeRetention, "mergeRetention");
|
|
2743
3688
|
function mergeMetadata(existing, incoming) {
|
|
2744
3689
|
if (!incoming.metadata || incoming.source === "hook" || incoming.source !== existing.source) {
|
|
2745
3690
|
return existing.metadata;
|
|
@@ -2747,7 +3692,10 @@ function mergeMetadata(existing, incoming) {
|
|
|
2747
3692
|
const merged = { ...existing.metadata ?? {} };
|
|
2748
3693
|
let changed = false;
|
|
2749
3694
|
for (const [key, value] of Object.entries(incoming.metadata)) {
|
|
2750
|
-
if (
|
|
3695
|
+
if ((key === WORKSPACE_CONTENT_SHA256_METADATA_KEY || key === WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY) && merged[key] !== value) {
|
|
3696
|
+
merged[key] = value;
|
|
3697
|
+
changed = true;
|
|
3698
|
+
} else if (!Object.hasOwn(merged, key)) {
|
|
2751
3699
|
merged[key] = value;
|
|
2752
3700
|
changed = true;
|
|
2753
3701
|
}
|
|
@@ -2765,7 +3713,7 @@ function mergeMetadata(existing, incoming) {
|
|
|
2765
3713
|
}
|
|
2766
3714
|
__name(mergeMetadata, "mergeMetadata");
|
|
2767
3715
|
function isMetadataWithinLimit(metadata) {
|
|
2768
|
-
return
|
|
3716
|
+
return metadataBudgetBytes(metadata, "persisted") <= 4096;
|
|
2769
3717
|
}
|
|
2770
3718
|
__name(isMetadataWithinLimit, "isMetadataWithinLimit");
|
|
2771
3719
|
function countByRetentionSource(artifacts) {
|
|
@@ -2821,6 +3769,7 @@ function removePriorChange(changes, artifactId) {
|
|
|
2821
3769
|
}
|
|
2822
3770
|
__name(removePriorChange, "removePriorChange");
|
|
2823
3771
|
function toPublicArtifact(artifact) {
|
|
3772
|
+
const hideWorkspacePath = "hideWorkspacePath" in artifact && artifact.hideWorkspacePath === true;
|
|
2824
3773
|
const {
|
|
2825
3774
|
id,
|
|
2826
3775
|
kind,
|
|
@@ -2835,13 +3784,16 @@ function toPublicArtifact(artifact) {
|
|
|
2835
3784
|
mimeType,
|
|
2836
3785
|
sizeBytes,
|
|
2837
3786
|
metadata,
|
|
3787
|
+
retention,
|
|
3788
|
+
restoreState,
|
|
3789
|
+
persistenceWarning,
|
|
3790
|
+
persistedAt,
|
|
2838
3791
|
clientRetained,
|
|
2839
3792
|
createdAt,
|
|
2840
3793
|
updatedAt,
|
|
2841
3794
|
toolCallId,
|
|
2842
3795
|
toolName,
|
|
2843
|
-
hookEventName
|
|
2844
|
-
clientId
|
|
3796
|
+
hookEventName
|
|
2845
3797
|
} = artifact;
|
|
2846
3798
|
return {
|
|
2847
3799
|
id,
|
|
@@ -2851,22 +3803,205 @@ function toPublicArtifact(artifact) {
|
|
|
2851
3803
|
status,
|
|
2852
3804
|
title,
|
|
2853
3805
|
...description ? { description } : {},
|
|
2854
|
-
...workspacePath ? { workspacePath } : {},
|
|
3806
|
+
...workspacePath && !hideWorkspacePath ? { workspacePath } : {},
|
|
2855
3807
|
...managedId ? { managedId } : {},
|
|
2856
3808
|
...url ? { url } : {},
|
|
2857
3809
|
...mimeType ? { mimeType } : {},
|
|
2858
3810
|
...sizeBytes !== void 0 ? { sizeBytes } : {},
|
|
2859
3811
|
...metadata ? { metadata } : {},
|
|
3812
|
+
retention,
|
|
3813
|
+
...restoreState ? { restoreState } : {},
|
|
3814
|
+
...persistenceWarning ? { persistenceWarning } : {},
|
|
3815
|
+
...persistedAt ? { persistedAt } : {},
|
|
2860
3816
|
clientRetained,
|
|
2861
3817
|
createdAt,
|
|
2862
3818
|
updatedAt,
|
|
2863
3819
|
...toolCallId ? { toolCallId } : {},
|
|
2864
3820
|
...toolName ? { toolName } : {},
|
|
2865
|
-
...hookEventName ? { hookEventName } : {}
|
|
2866
|
-
...clientId ? { clientId } : {}
|
|
3821
|
+
...hookEventName ? { hookEventName } : {}
|
|
2867
3822
|
};
|
|
2868
3823
|
}
|
|
2869
3824
|
__name(toPublicArtifact, "toPublicArtifact");
|
|
3825
|
+
function persistedArtifactToInput(artifact) {
|
|
3826
|
+
return {
|
|
3827
|
+
title: artifact.title,
|
|
3828
|
+
kind: artifact.kind,
|
|
3829
|
+
storage: artifact.storage,
|
|
3830
|
+
description: artifact.description,
|
|
3831
|
+
workspacePath: artifact.workspacePath,
|
|
3832
|
+
managedId: artifact.managedId,
|
|
3833
|
+
url: artifact.url,
|
|
3834
|
+
mimeType: artifact.mimeType,
|
|
3835
|
+
sizeBytes: artifact.sizeBytes,
|
|
3836
|
+
metadata: artifact.metadata,
|
|
3837
|
+
source: artifact.source,
|
|
3838
|
+
retention: artifact.retention,
|
|
3839
|
+
clientRetained: artifact.clientRetained,
|
|
3840
|
+
toolCallId: artifact.toolCallId,
|
|
3841
|
+
toolName: artifact.toolName,
|
|
3842
|
+
hookEventName: artifact.hookEventName
|
|
3843
|
+
};
|
|
3844
|
+
}
|
|
3845
|
+
__name(persistedArtifactToInput, "persistedArtifactToInput");
|
|
3846
|
+
function workspaceExpectedFromArtifact(artifact) {
|
|
3847
|
+
if (!artifact.workspacePath) {
|
|
3848
|
+
return void 0;
|
|
3849
|
+
}
|
|
3850
|
+
return {
|
|
3851
|
+
sizeBytes: artifact.sizeBytes,
|
|
3852
|
+
mtimeMs: artifact.metadata?.[WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY],
|
|
3853
|
+
sha256: artifact.metadata?.[WORKSPACE_CONTENT_SHA256_METADATA_KEY]
|
|
3854
|
+
};
|
|
3855
|
+
}
|
|
3856
|
+
__name(workspaceExpectedFromArtifact, "workspaceExpectedFromArtifact");
|
|
3857
|
+
function toPersistedChange(change, recordedAt) {
|
|
3858
|
+
return {
|
|
3859
|
+
action: change.action,
|
|
3860
|
+
artifactId: change.artifactId,
|
|
3861
|
+
...change.artifact ? { artifact: toPersistedArtifact(change.artifact, recordedAt) } : {},
|
|
3862
|
+
...change.reason ? { reason: change.reason } : {}
|
|
3863
|
+
};
|
|
3864
|
+
}
|
|
3865
|
+
__name(toPersistedChange, "toPersistedChange");
|
|
3866
|
+
function toPersistedArtifact(artifact, recordedAt) {
|
|
3867
|
+
return {
|
|
3868
|
+
id: artifact.id,
|
|
3869
|
+
kind: artifact.kind,
|
|
3870
|
+
storage: artifact.storage,
|
|
3871
|
+
source: artifact.source,
|
|
3872
|
+
status: artifact.status === "changed" ? "available" : artifact.status,
|
|
3873
|
+
title: artifact.title,
|
|
3874
|
+
...artifact.description ? { description: artifact.description } : {},
|
|
3875
|
+
...artifact.workspacePath ? { workspacePath: artifact.workspacePath } : {},
|
|
3876
|
+
...artifact.managedId ? { managedId: artifact.managedId } : {},
|
|
3877
|
+
...artifact.url ? { url: artifact.url } : {},
|
|
3878
|
+
...artifact.mimeType ? { mimeType: artifact.mimeType } : {},
|
|
3879
|
+
...artifact.sizeBytes !== void 0 ? { sizeBytes: artifact.sizeBytes } : {},
|
|
3880
|
+
...artifact.metadata ? { metadata: artifact.metadata } : {},
|
|
3881
|
+
retention: artifact.retention,
|
|
3882
|
+
clientRetained: artifact.clientRetained,
|
|
3883
|
+
createdAt: artifact.createdAt,
|
|
3884
|
+
updatedAt: artifact.updatedAt,
|
|
3885
|
+
persistedAt: artifact.persistedAt ?? recordedAt,
|
|
3886
|
+
...artifact.toolCallId ? { toolCallId: artifact.toolCallId } : {},
|
|
3887
|
+
...artifact.toolName ? { toolName: artifact.toolName } : {},
|
|
3888
|
+
...artifact.hookEventName ? { hookEventName: artifact.hookEventName } : {}
|
|
3889
|
+
};
|
|
3890
|
+
}
|
|
3891
|
+
__name(toPersistedArtifact, "toPersistedArtifact");
|
|
3892
|
+
function isFileArtifactUrl(raw) {
|
|
3893
|
+
if (typeof raw !== "string") return false;
|
|
3894
|
+
try {
|
|
3895
|
+
return new URL(raw).protocol === "file:";
|
|
3896
|
+
} catch {
|
|
3897
|
+
return false;
|
|
3898
|
+
}
|
|
3899
|
+
}
|
|
3900
|
+
__name(isFileArtifactUrl, "isFileArtifactUrl");
|
|
3901
|
+
function isArtifactSnapshotCompletenessWarning(warning) {
|
|
3902
|
+
if (warning.startsWith("skipped stale event sequence ")) return false;
|
|
3903
|
+
return warning.startsWith("skipped ") || warning.includes(" list truncated to ");
|
|
3904
|
+
}
|
|
3905
|
+
__name(isArtifactSnapshotCompletenessWarning, "isArtifactSnapshotCompletenessWarning");
|
|
3906
|
+
function shouldCommitBeforeDurablePersistence(change) {
|
|
3907
|
+
return change.action === "removed" && change.durableTombstoneRequired === true;
|
|
3908
|
+
}
|
|
3909
|
+
__name(shouldCommitBeforeDurablePersistence, "shouldCommitBeforeDurablePersistence");
|
|
3910
|
+
function isDurablePersistenceChange(change) {
|
|
3911
|
+
if (change.action === "removed" && change.durableTombstoneRequired === true) {
|
|
3912
|
+
return true;
|
|
3913
|
+
}
|
|
3914
|
+
if (!change.artifact) return false;
|
|
3915
|
+
return change.artifact.retention !== "ephemeral" || change.artifact.persistenceWarning === "persistence_unavailable";
|
|
3916
|
+
}
|
|
3917
|
+
__name(isDurablePersistenceChange, "isDurablePersistenceChange");
|
|
3918
|
+
function stripDurableTombstoneMarkers(changes) {
|
|
3919
|
+
for (const change of changes) {
|
|
3920
|
+
delete change.durableTombstoneRequired;
|
|
3921
|
+
delete change.removedClientId;
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3924
|
+
__name(stripDurableTombstoneMarkers, "stripDurableTombstoneMarkers");
|
|
3925
|
+
function detailsForRestoreWarnings(warnings) {
|
|
3926
|
+
return warnings.map((warning) => {
|
|
3927
|
+
if (warning.startsWith(RESTORE_FAILED_WARNING_PREFIX)) {
|
|
3928
|
+
return {
|
|
3929
|
+
code: "ARTIFACT_RESTORE_FAILED",
|
|
3930
|
+
operation: "restore",
|
|
3931
|
+
durability: "unavailable",
|
|
3932
|
+
retryable: true,
|
|
3933
|
+
message: warning
|
|
3934
|
+
};
|
|
3935
|
+
}
|
|
3936
|
+
if (warning.startsWith(RESTORE_PARTIAL_FAILED_WARNING_PREFIX)) {
|
|
3937
|
+
return {
|
|
3938
|
+
code: "ARTIFACT_RESTORE_PARTIAL_FAILED",
|
|
3939
|
+
operation: "restore",
|
|
3940
|
+
durability: "live_only",
|
|
3941
|
+
retryable: true,
|
|
3942
|
+
message: warning
|
|
3943
|
+
};
|
|
3944
|
+
}
|
|
3945
|
+
return {
|
|
3946
|
+
code: "ARTIFACT_WARNING",
|
|
3947
|
+
operation: "restore",
|
|
3948
|
+
message: warning
|
|
3949
|
+
};
|
|
3950
|
+
});
|
|
3951
|
+
}
|
|
3952
|
+
__name(detailsForRestoreWarnings, "detailsForRestoreWarnings");
|
|
3953
|
+
function detailsForPersistenceWarnings(warnings, changes, operation) {
|
|
3954
|
+
if (warnings.length === 0) return [];
|
|
3955
|
+
const artifactIds = changes.filter(isDurablePersistenceChange).map((change) => change.artifactId);
|
|
3956
|
+
return warnings.map((warning) => {
|
|
3957
|
+
if (warning === "artifact persistence unavailable; durable artifacts kept ephemeral") {
|
|
3958
|
+
return {
|
|
3959
|
+
code: "ARTIFACT_PERSISTENCE_UNAVAILABLE",
|
|
3960
|
+
operation,
|
|
3961
|
+
artifactIds,
|
|
3962
|
+
durability: "live_only",
|
|
3963
|
+
retryable: false,
|
|
3964
|
+
message: warning
|
|
3965
|
+
};
|
|
3966
|
+
}
|
|
3967
|
+
if (warning === "artifact removal not persisted; live removal kept") {
|
|
3968
|
+
return {
|
|
3969
|
+
code: "ARTIFACT_REMOVAL_NOT_PERSISTED",
|
|
3970
|
+
operation,
|
|
3971
|
+
artifactIds,
|
|
3972
|
+
durability: "live_only",
|
|
3973
|
+
retryable: true,
|
|
3974
|
+
message: warning
|
|
3975
|
+
};
|
|
3976
|
+
}
|
|
3977
|
+
return {
|
|
3978
|
+
code: "ARTIFACT_WARNING",
|
|
3979
|
+
operation,
|
|
3980
|
+
artifactIds,
|
|
3981
|
+
message: warning
|
|
3982
|
+
};
|
|
3983
|
+
});
|
|
3984
|
+
}
|
|
3985
|
+
__name(detailsForPersistenceWarnings, "detailsForPersistenceWarnings");
|
|
3986
|
+
function persistenceFailureDetail(options) {
|
|
3987
|
+
const unavailable = options.error instanceof SessionArtifactValidationError && options.error.message === "artifact persistence is unavailable";
|
|
3988
|
+
return {
|
|
3989
|
+
code: unavailable ? "ARTIFACT_PERSISTENCE_UNAVAILABLE" : "ARTIFACT_PERSISTENCE_WRITE_FAILED",
|
|
3990
|
+
operation: options.operation,
|
|
3991
|
+
artifactIds: options.artifactIds,
|
|
3992
|
+
durability: "unavailable",
|
|
3993
|
+
retryable: !unavailable,
|
|
3994
|
+
message: options.message
|
|
3995
|
+
};
|
|
3996
|
+
}
|
|
3997
|
+
__name(persistenceFailureDetail, "persistenceFailureDetail");
|
|
3998
|
+
function cloneStoredArtifact(artifact) {
|
|
3999
|
+
return {
|
|
4000
|
+
...artifact,
|
|
4001
|
+
...artifact.metadata ? { metadata: { ...artifact.metadata } } : {}
|
|
4002
|
+
};
|
|
4003
|
+
}
|
|
4004
|
+
__name(cloneStoredArtifact, "cloneStoredArtifact");
|
|
2870
4005
|
function inferStorage(requested, locators) {
|
|
2871
4006
|
if (requested !== void 0 && typeof requested !== "string") {
|
|
2872
4007
|
throw new SessionArtifactValidationError(
|
|
@@ -2960,10 +4095,6 @@ function buildIdentityKey(input) {
|
|
|
2960
4095
|
);
|
|
2961
4096
|
}
|
|
2962
4097
|
__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
4098
|
function managedIdForWorkspacePath(workspaceCwd, workspacePath) {
|
|
2968
4099
|
return createHash("sha1").update(path2.resolve(workspaceCwd, workspacePath)).digest("hex").slice(0, 16);
|
|
2969
4100
|
}
|
|
@@ -3077,6 +4208,12 @@ function normalizeArtifactUrl(raw, allowFile) {
|
|
|
3077
4208
|
"url"
|
|
3078
4209
|
);
|
|
3079
4210
|
}
|
|
4211
|
+
if (hasSecretLikeUrlComponent(parsed)) {
|
|
4212
|
+
throw new SessionArtifactValidationError(
|
|
4213
|
+
"url must not include secret-like components",
|
|
4214
|
+
"url"
|
|
4215
|
+
);
|
|
4216
|
+
}
|
|
3080
4217
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:" && !(allowFile && parsed.protocol === "file:")) {
|
|
3081
4218
|
throw new SessionArtifactValidationError(
|
|
3082
4219
|
allowFile ? "url must use http, https, or file" : "url must use http or https",
|
|
@@ -3086,7 +4223,64 @@ function normalizeArtifactUrl(raw, allowFile) {
|
|
|
3086
4223
|
return parsed.href;
|
|
3087
4224
|
}
|
|
3088
4225
|
__name(normalizeArtifactUrl, "normalizeArtifactUrl");
|
|
3089
|
-
function
|
|
4226
|
+
function hasSecretLikeUrlComponent(parsed) {
|
|
4227
|
+
for (const [key, value] of parsed.searchParams) {
|
|
4228
|
+
if (isSecretLikeUrlText(key) || isSecretLikeUrlValue(value)) {
|
|
4229
|
+
return true;
|
|
4230
|
+
}
|
|
4231
|
+
}
|
|
4232
|
+
for (const segment of parsed.pathname.split("/").filter(Boolean)) {
|
|
4233
|
+
let decodedSegment = segment;
|
|
4234
|
+
try {
|
|
4235
|
+
decodedSegment = decodeURIComponent(segment);
|
|
4236
|
+
} catch {
|
|
4237
|
+
}
|
|
4238
|
+
if (isSecretLikeUrlValue(decodedSegment)) {
|
|
4239
|
+
return true;
|
|
4240
|
+
}
|
|
4241
|
+
}
|
|
4242
|
+
const fragment = parsed.hash.slice(1);
|
|
4243
|
+
return hasSecretLikeUrlFragment(fragment);
|
|
4244
|
+
}
|
|
4245
|
+
__name(hasSecretLikeUrlComponent, "hasSecretLikeUrlComponent");
|
|
4246
|
+
function hasSecretLikeUrlFragment(fragment) {
|
|
4247
|
+
if (!fragment) return false;
|
|
4248
|
+
const candidates = /* @__PURE__ */ new Set([fragment]);
|
|
4249
|
+
try {
|
|
4250
|
+
candidates.add(decodeURIComponent(fragment));
|
|
4251
|
+
} catch {
|
|
4252
|
+
}
|
|
4253
|
+
for (const candidate of candidates) {
|
|
4254
|
+
if (isSecretLikeUrlText(candidate) || isSecretLikeUrlValue(candidate)) {
|
|
4255
|
+
return true;
|
|
4256
|
+
}
|
|
4257
|
+
for (const [key, value] of new URLSearchParams(candidate)) {
|
|
4258
|
+
if (isSecretLikeUrlText(key) || isSecretLikeUrlValue(value)) {
|
|
4259
|
+
return true;
|
|
4260
|
+
}
|
|
4261
|
+
}
|
|
4262
|
+
}
|
|
4263
|
+
return false;
|
|
4264
|
+
}
|
|
4265
|
+
__name(hasSecretLikeUrlFragment, "hasSecretLikeUrlFragment");
|
|
4266
|
+
function isSecretLikeUrlText(value) {
|
|
4267
|
+
const normalized = value.replace(/([a-z])([A-Z])/g, "$1-$2");
|
|
4268
|
+
return /(?:^|[-_.])(token|secret|password|passwd|pwd|cookie|authorization|credential|signature|sig|api[-_]?key|access[-_]?key)(?:$|[-_.=&#])/i.test(
|
|
4269
|
+
normalized
|
|
4270
|
+
);
|
|
4271
|
+
}
|
|
4272
|
+
__name(isSecretLikeUrlText, "isSecretLikeUrlText");
|
|
4273
|
+
function isSecretLikeUrlValue(value) {
|
|
4274
|
+
return SECRET_TOKEN_VALUE_PATTERN.test(value.trim());
|
|
4275
|
+
}
|
|
4276
|
+
__name(isSecretLikeUrlValue, "isSecretLikeUrlValue");
|
|
4277
|
+
function isSecretLikeMetadataValue(value) {
|
|
4278
|
+
return /^(?:bearer\s+\S{8,}|sk-[A-Za-z0-9_-]{12,}|(?:gh[pousr]|github_pat)_[A-Za-z0-9_/-]{12,})$/i.test(
|
|
4279
|
+
value.trim()
|
|
4280
|
+
);
|
|
4281
|
+
}
|
|
4282
|
+
__name(isSecretLikeMetadataValue, "isSecretLikeMetadataValue");
|
|
4283
|
+
function normalizeMetadata(metadata, options = {}) {
|
|
3090
4284
|
if (metadata === void 0) {
|
|
3091
4285
|
return void 0;
|
|
3092
4286
|
}
|
|
@@ -3098,6 +4292,12 @@ function normalizeMetadata(metadata) {
|
|
|
3098
4292
|
}
|
|
3099
4293
|
const normalized = {};
|
|
3100
4294
|
for (const [key, value] of Object.entries(metadata)) {
|
|
4295
|
+
if (isPrototypeMetadataKey(key)) {
|
|
4296
|
+
continue;
|
|
4297
|
+
}
|
|
4298
|
+
if (options.budget !== "persisted" && isReservedWorkspaceMetadataKey(key)) {
|
|
4299
|
+
continue;
|
|
4300
|
+
}
|
|
3101
4301
|
if (!key) {
|
|
3102
4302
|
throw new SessionArtifactValidationError(
|
|
3103
4303
|
"metadata keys must not be empty",
|
|
@@ -3116,6 +4316,12 @@ function normalizeMetadata(metadata) {
|
|
|
3116
4316
|
"metadata"
|
|
3117
4317
|
);
|
|
3118
4318
|
}
|
|
4319
|
+
if (isSecretLikeUrlText(key)) {
|
|
4320
|
+
throw new SessionArtifactValidationError(
|
|
4321
|
+
"metadata keys must not contain secret-like names",
|
|
4322
|
+
"metadata"
|
|
4323
|
+
);
|
|
4324
|
+
}
|
|
3119
4325
|
if (value !== null && typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
|
|
3120
4326
|
throw new SessionArtifactValidationError(
|
|
3121
4327
|
"metadata values must be primitive",
|
|
@@ -3134,12 +4340,18 @@ function normalizeMetadata(metadata) {
|
|
|
3134
4340
|
"metadata"
|
|
3135
4341
|
);
|
|
3136
4342
|
}
|
|
4343
|
+
if (typeof value === "string" && !isReservedWorkspaceMetadataKey(key) && isSecretLikeMetadataValue(value)) {
|
|
4344
|
+
throw new SessionArtifactValidationError(
|
|
4345
|
+
"metadata string values must not contain secret-like tokens",
|
|
4346
|
+
"metadata"
|
|
4347
|
+
);
|
|
4348
|
+
}
|
|
3137
4349
|
normalized[key] = value;
|
|
3138
4350
|
}
|
|
3139
4351
|
if (Object.keys(normalized).length === 0) {
|
|
3140
4352
|
return void 0;
|
|
3141
4353
|
}
|
|
3142
|
-
if (
|
|
4354
|
+
if (metadataBudgetBytes(normalized, options.budget ?? "user") > 4096) {
|
|
3143
4355
|
throw new SessionArtifactValidationError(
|
|
3144
4356
|
"metadata must be 4096 bytes or fewer",
|
|
3145
4357
|
"metadata"
|
|
@@ -3148,6 +4360,25 @@ function normalizeMetadata(metadata) {
|
|
|
3148
4360
|
return normalized;
|
|
3149
4361
|
}
|
|
3150
4362
|
__name(normalizeMetadata, "normalizeMetadata");
|
|
4363
|
+
function normalizeRetention(value, options) {
|
|
4364
|
+
if (value === void 0) {
|
|
4365
|
+
return options.persistenceAvailable ? "restorable" : "ephemeral";
|
|
4366
|
+
}
|
|
4367
|
+
if (value === "ephemeral" || value === "restorable") {
|
|
4368
|
+
return value;
|
|
4369
|
+
}
|
|
4370
|
+
if (value === "pinned") {
|
|
4371
|
+
throw new SessionArtifactValidationError(
|
|
4372
|
+
"pinned retention is not supported by session_artifacts_persistence",
|
|
4373
|
+
"retention"
|
|
4374
|
+
);
|
|
4375
|
+
}
|
|
4376
|
+
throw new SessionArtifactValidationError(
|
|
4377
|
+
"retention must be ephemeral or restorable",
|
|
4378
|
+
"retention"
|
|
4379
|
+
);
|
|
4380
|
+
}
|
|
4381
|
+
__name(normalizeRetention, "normalizeRetention");
|
|
3151
4382
|
function normalizeSizeBytes(value) {
|
|
3152
4383
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
3153
4384
|
throw new SessionArtifactValidationError(
|
|
@@ -3173,21 +4404,83 @@ function inferKind(input) {
|
|
|
3173
4404
|
return input.workspacePath ? "file" : "other";
|
|
3174
4405
|
}
|
|
3175
4406
|
__name(inferKind, "inferKind");
|
|
3176
|
-
async function getWorkspaceStatus(
|
|
3177
|
-
const absolutePath = path2.resolve(workspaceCwd, workspacePath);
|
|
4407
|
+
async function getWorkspaceStatus(workspacePath, realWorkspaceCwd, expected, options = { hashContent: true }) {
|
|
3178
4408
|
const realWorkspace = await realWorkspaceCwd;
|
|
4409
|
+
const absolutePath = path2.resolve(realWorkspace, workspacePath);
|
|
3179
4410
|
try {
|
|
3180
4411
|
const realPath = await fs2.realpath(absolutePath);
|
|
3181
4412
|
const relative = path2.relative(realWorkspace, realPath);
|
|
3182
4413
|
if (!relative || isOutsidePath(relative)) {
|
|
3183
4414
|
return { status: "missing", escaped: true };
|
|
3184
4415
|
}
|
|
3185
|
-
const
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
4416
|
+
const preOpenStat = await fs2.lstat(realPath);
|
|
4417
|
+
const handle = await fs2.open(
|
|
4418
|
+
realPath,
|
|
4419
|
+
fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW
|
|
4420
|
+
);
|
|
4421
|
+
try {
|
|
4422
|
+
const stat = await handle.stat();
|
|
4423
|
+
if (!isSameFile(preOpenStat, stat)) {
|
|
4424
|
+
return { status: "missing", escaped: true };
|
|
4425
|
+
}
|
|
4426
|
+
if (stat.isFile()) {
|
|
4427
|
+
const expectedMtimeMs = typeof expected?.mtimeMs === "number" ? expected.mtimeMs : void 0;
|
|
4428
|
+
const expectedSha256 = typeof expected?.sha256 === "string" ? expected.sha256 : void 0;
|
|
4429
|
+
const unchanged = expected?.sizeBytes === stat.size && expectedMtimeMs === stat.mtimeMs;
|
|
4430
|
+
const sizeChanged = expected?.sizeBytes !== void 0 && expected.sizeBytes !== stat.size;
|
|
4431
|
+
if (sizeChanged) {
|
|
4432
|
+
return {
|
|
4433
|
+
status: "changed",
|
|
4434
|
+
sizeBytes: stat.size,
|
|
4435
|
+
mtimeMs: stat.mtimeMs
|
|
4436
|
+
};
|
|
4437
|
+
}
|
|
4438
|
+
if (unchanged) {
|
|
4439
|
+
return {
|
|
4440
|
+
status: "available",
|
|
4441
|
+
sizeBytes: stat.size,
|
|
4442
|
+
mtimeMs: stat.mtimeMs
|
|
4443
|
+
};
|
|
4444
|
+
}
|
|
4445
|
+
if (stat.size > MAX_WORKSPACE_HASH_BYTES) {
|
|
4446
|
+
return {
|
|
4447
|
+
status: expectedSha256 ? "changed" : "available",
|
|
4448
|
+
sizeBytes: stat.size,
|
|
4449
|
+
mtimeMs: stat.mtimeMs
|
|
4450
|
+
};
|
|
4451
|
+
}
|
|
4452
|
+
if (!options.hashContent) {
|
|
4453
|
+
return {
|
|
4454
|
+
status: expectedSha256 ? "changed" : "available",
|
|
4455
|
+
sizeBytes: stat.size,
|
|
4456
|
+
mtimeMs: stat.mtimeMs
|
|
4457
|
+
};
|
|
4458
|
+
}
|
|
4459
|
+
const sha256 = await hashFile(handle);
|
|
4460
|
+
if (expectedSha256 && sha256 !== expectedSha256) {
|
|
4461
|
+
return {
|
|
4462
|
+
status: "changed",
|
|
4463
|
+
sizeBytes: stat.size,
|
|
4464
|
+
mtimeMs: stat.mtimeMs
|
|
4465
|
+
};
|
|
4466
|
+
}
|
|
4467
|
+
return {
|
|
4468
|
+
status: "available",
|
|
4469
|
+
sizeBytes: stat.size,
|
|
4470
|
+
mtimeMs: stat.mtimeMs,
|
|
4471
|
+
sha256
|
|
4472
|
+
};
|
|
4473
|
+
}
|
|
4474
|
+
return {
|
|
4475
|
+
status: "available"
|
|
4476
|
+
};
|
|
4477
|
+
} finally {
|
|
4478
|
+
await handle.close();
|
|
4479
|
+
}
|
|
3190
4480
|
} catch (error) {
|
|
4481
|
+
if (isNoFollowSymlinkError(error)) {
|
|
4482
|
+
return { status: "missing", escaped: true };
|
|
4483
|
+
}
|
|
3191
4484
|
if (!isNotFoundError(error)) {
|
|
3192
4485
|
throw error;
|
|
3193
4486
|
}
|
|
@@ -3198,6 +4491,41 @@ async function getWorkspaceStatus(workspaceCwd, workspacePath, realWorkspaceCwd)
|
|
|
3198
4491
|
}
|
|
3199
4492
|
}
|
|
3200
4493
|
__name(getWorkspaceStatus, "getWorkspaceStatus");
|
|
4494
|
+
function isSameFile(before, after) {
|
|
4495
|
+
return before.dev === after.dev && before.ino === after.ino;
|
|
4496
|
+
}
|
|
4497
|
+
__name(isSameFile, "isSameFile");
|
|
4498
|
+
async function hashFile(handle) {
|
|
4499
|
+
const hash = createHash("sha256");
|
|
4500
|
+
for await (const chunk of handle.createReadStream({ start: 0 })) {
|
|
4501
|
+
hash.update(chunk);
|
|
4502
|
+
}
|
|
4503
|
+
return hash.digest("hex");
|
|
4504
|
+
}
|
|
4505
|
+
__name(hashFile, "hashFile");
|
|
4506
|
+
function withWorkspaceContentHashMetadata(metadata, workspaceStatus) {
|
|
4507
|
+
if (workspaceStatus?.status !== "available" || !workspaceStatus.sha256) {
|
|
4508
|
+
return metadata;
|
|
4509
|
+
}
|
|
4510
|
+
const next = {
|
|
4511
|
+
...metadata ?? {},
|
|
4512
|
+
[WORKSPACE_CONTENT_SHA256_METADATA_KEY]: workspaceStatus.sha256,
|
|
4513
|
+
...workspaceStatus.mtimeMs !== void 0 ? { [WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY]: workspaceStatus.mtimeMs } : {}
|
|
4514
|
+
};
|
|
4515
|
+
return next;
|
|
4516
|
+
}
|
|
4517
|
+
__name(withWorkspaceContentHashMetadata, "withWorkspaceContentHashMetadata");
|
|
4518
|
+
function isWorkspaceContentChanged(artifact, status) {
|
|
4519
|
+
if (status.status !== "available") {
|
|
4520
|
+
return false;
|
|
4521
|
+
}
|
|
4522
|
+
const expectedSha256 = artifact.metadata?.[WORKSPACE_CONTENT_SHA256_METADATA_KEY];
|
|
4523
|
+
if (artifact.sizeBytes !== void 0 && status.sizeBytes !== void 0 && status.sizeBytes !== artifact.sizeBytes) {
|
|
4524
|
+
return true;
|
|
4525
|
+
}
|
|
4526
|
+
return typeof expectedSha256 === "string" && status.sha256 !== void 0 && status.sha256 !== expectedSha256;
|
|
4527
|
+
}
|
|
4528
|
+
__name(isWorkspaceContentChanged, "isWorkspaceContentChanged");
|
|
3201
4529
|
async function danglingSymlinkEscapesWorkspace(absolutePath, realWorkspace) {
|
|
3202
4530
|
try {
|
|
3203
4531
|
const stat = await fs2.lstat(absolutePath);
|
|
@@ -3230,6 +4558,13 @@ function isNotFoundError(error) {
|
|
|
3230
4558
|
return code === "ENOENT" || code === "ENOTDIR";
|
|
3231
4559
|
}
|
|
3232
4560
|
__name(isNotFoundError, "isNotFoundError");
|
|
4561
|
+
function isNoFollowSymlinkError(error) {
|
|
4562
|
+
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
4563
|
+
return false;
|
|
4564
|
+
}
|
|
4565
|
+
return error.code === "ELOOP";
|
|
4566
|
+
}
|
|
4567
|
+
__name(isNoFollowSymlinkError, "isNoFollowSymlinkError");
|
|
3233
4568
|
function isOutsidePath(relative) {
|
|
3234
4569
|
return relative === ".." || relative.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative);
|
|
3235
4570
|
}
|
|
@@ -3582,6 +4917,11 @@ function broadcastTurnError(entry, sessionId, err, promptId, originatorClientId)
|
|
|
3582
4917
|
);
|
|
3583
4918
|
}
|
|
3584
4919
|
entry.retryAllowed = true;
|
|
4920
|
+
entry.turnError = {
|
|
4921
|
+
message,
|
|
4922
|
+
...code ? { code } : {},
|
|
4923
|
+
...errorKind ? { errorKind } : {}
|
|
4924
|
+
};
|
|
3585
4925
|
try {
|
|
3586
4926
|
entry.events.publish({
|
|
3587
4927
|
type: "turn_error",
|
|
@@ -3624,6 +4964,7 @@ function extractPromptText(prompt) {
|
|
|
3624
4964
|
__name(extractPromptText, "extractPromptText");
|
|
3625
4965
|
var DEFAULT_INIT_TIMEOUT_MS = 1e4;
|
|
3626
4966
|
var PERSIST_TIMEOUT_MS = 5e3;
|
|
4967
|
+
var MAX_PARENT_PERSIST_ATTEMPTS = 3;
|
|
3627
4968
|
var MCP_RESTART_TIMEOUT_MS = 3e5;
|
|
3628
4969
|
var WORKSPACE_MEMORY_REMEMBER_TIMEOUT_MS = 3e5;
|
|
3629
4970
|
var MCP_OAUTH_TIMEOUT_MS = 6e5;
|
|
@@ -3631,6 +4972,7 @@ var DAEMON_RETRY_META_KEY = "qwen.daemon.retry";
|
|
|
3631
4972
|
var DAEMON_CONTINUE_META_KEY = "qwen.daemon.continueLastTurn";
|
|
3632
4973
|
var SESSION_RECAP_TIMEOUT_MS = 6e4;
|
|
3633
4974
|
var SESSION_BTW_TIMEOUT_MS = 6e4;
|
|
4975
|
+
var SESSION_TRANSCRIPT_TIMEOUT_MS = 6e4;
|
|
3634
4976
|
var SHELL_COMMAND_TIMEOUT_MS = 12e4;
|
|
3635
4977
|
var MAX_SHELL_OUTPUT_FOR_HISTORY = 1e4;
|
|
3636
4978
|
var MAX_MID_TURN_QUEUE_DEPTH = 20;
|
|
@@ -3671,6 +5013,15 @@ function createAcpSessionBridge(opts) {
|
|
|
3671
5013
|
);
|
|
3672
5014
|
}
|
|
3673
5015
|
}, "releaseFreshSessionReservation");
|
|
5016
|
+
const emitSessionLifecycle = /* @__PURE__ */ __name((event) => {
|
|
5017
|
+
try {
|
|
5018
|
+
opts.sessionLifecycle?.(event);
|
|
5019
|
+
} catch (err) {
|
|
5020
|
+
const message = `qwen serve: session lifecycle callback failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
5021
|
+
opts.onDiagnosticLine?.(message, "warn");
|
|
5022
|
+
writeStderrLine(message);
|
|
5023
|
+
}
|
|
5024
|
+
}, "emitSessionLifecycle");
|
|
3674
5025
|
if (defaultSessionScope !== "single" && defaultSessionScope !== "thread") {
|
|
3675
5026
|
throw new TypeError(
|
|
3676
5027
|
`Invalid sessionScope: ${JSON.stringify(defaultSessionScope)}. Expected 'single' or 'thread'.`
|
|
@@ -3855,14 +5206,32 @@ function createAcpSessionBridge(opts) {
|
|
|
3855
5206
|
const aliveChannels = /* @__PURE__ */ new Set();
|
|
3856
5207
|
let inFlightChannelSpawn;
|
|
3857
5208
|
const byId = /* @__PURE__ */ new Map();
|
|
3858
|
-
const toSessionSummary = /* @__PURE__ */ __name((entry) =>
|
|
3859
|
-
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
5209
|
+
const toSessionSummary = /* @__PURE__ */ __name((entry) => {
|
|
5210
|
+
let isWaitingForPermission = false;
|
|
5211
|
+
let isWaitingForUserQuestion = false;
|
|
5212
|
+
for (const interaction of entry.pendingInteractions.values()) {
|
|
5213
|
+
if (interaction.kind === "user_question") {
|
|
5214
|
+
isWaitingForUserQuestion = true;
|
|
5215
|
+
} else {
|
|
5216
|
+
isWaitingForPermission = true;
|
|
5217
|
+
}
|
|
5218
|
+
}
|
|
5219
|
+
return {
|
|
5220
|
+
sessionId: entry.sessionId,
|
|
5221
|
+
workspaceCwd: entry.workspaceCwd,
|
|
5222
|
+
createdAt: entry.createdAt,
|
|
5223
|
+
displayName: entry.displayName,
|
|
5224
|
+
...entry.parentSessionId ? { parentSessionId: entry.parentSessionId } : {},
|
|
5225
|
+
clientCount: entry.clientIds.size,
|
|
5226
|
+
hasActivePrompt: entry.promptActive,
|
|
5227
|
+
isWaitingForPermission,
|
|
5228
|
+
isWaitingForUserQuestion,
|
|
5229
|
+
pendingInteractionCount: entry.pendingInteractions.size,
|
|
5230
|
+
hasTurnError: entry.turnError !== void 0,
|
|
5231
|
+
...entry.turnError !== void 0 ? { turnError: entry.turnError } : {},
|
|
5232
|
+
pendingInteractions: [...entry.pendingInteractions.values()]
|
|
5233
|
+
};
|
|
5234
|
+
}, "toSessionSummary");
|
|
3866
5235
|
const permissionConsensusQuorum = opts.permissionConsensusQuorum;
|
|
3867
5236
|
if (permissionConsensusQuorum !== void 0 && (!Number.isInteger(permissionConsensusQuorum) || permissionConsensusQuorum < 1)) {
|
|
3868
5237
|
throw new Error(
|
|
@@ -3921,6 +5290,22 @@ function createAcpSessionBridge(opts) {
|
|
|
3921
5290
|
entry.clientIds.set(clientId, count - 1);
|
|
3922
5291
|
}
|
|
3923
5292
|
}, "unregisterClient");
|
|
5293
|
+
const rollbackAttachRegistration = /* @__PURE__ */ __name(async (entry, clientId, attachCountDelta = 1) => {
|
|
5294
|
+
entry.attachCount = Math.max(0, entry.attachCount - attachCountDelta);
|
|
5295
|
+
unregisterClient(entry, clientId);
|
|
5296
|
+
if (entry.spawnOwnerWantedKill && entry.attachCount === 0 && entry.events.subscriberCount === 0) {
|
|
5297
|
+
await bridgeApi.killSession(entry.sessionId).catch(() => {
|
|
5298
|
+
});
|
|
5299
|
+
} else if (entry.clientIds.size === 0 && entry.events.subscriberCount === 0 && !entry.promptActive) {
|
|
5300
|
+
await closeSessionImpl(entry.sessionId, void 0, {
|
|
5301
|
+
reason: "last_client_detached"
|
|
5302
|
+
}).catch((err) => {
|
|
5303
|
+
writeStderrLine(
|
|
5304
|
+
`qwen serve: close-on-attach-rollback failed for ${JSON.stringify(entry.sessionId)}: ${err instanceof Error ? err.stack ?? err.message : String(err)}`
|
|
5305
|
+
);
|
|
5306
|
+
});
|
|
5307
|
+
}
|
|
5308
|
+
}, "rollbackAttachRegistration");
|
|
3924
5309
|
const resolveTrustedClientId = /* @__PURE__ */ __name((entry, clientId) => {
|
|
3925
5310
|
if (clientId === void 0) return void 0;
|
|
3926
5311
|
if (!entry.clientIds.has(clientId)) {
|
|
@@ -4001,7 +5386,11 @@ function createAcpSessionBridge(opts) {
|
|
|
4001
5386
|
inputTokens,
|
|
4002
5387
|
outputTokens,
|
|
4003
5388
|
durationMs
|
|
4004
|
-
)
|
|
5389
|
+
),
|
|
5390
|
+
// `create_sub_session` tool: forward the request/response hook so a child
|
|
5391
|
+
// tool can ask the daemon to spawn a sub-session and (for 'first-turn')
|
|
5392
|
+
// return its result. Omitted → the method reports daemon-only.
|
|
5393
|
+
opts.onCreateSubSession
|
|
4005
5394
|
);
|
|
4006
5395
|
const connection = new ClientSideConnection(() => client, channel.stream);
|
|
4007
5396
|
const info = {
|
|
@@ -4066,6 +5455,12 @@ function createAcpSessionBridge(opts) {
|
|
|
4066
5455
|
}
|
|
4067
5456
|
byId.delete(sid);
|
|
4068
5457
|
telemetry.metrics?.sessionLifecycle("die");
|
|
5458
|
+
emitSessionLifecycle({
|
|
5459
|
+
type: "removed",
|
|
5460
|
+
sessionId: sid,
|
|
5461
|
+
workspaceCwd: sessEntry.workspaceCwd,
|
|
5462
|
+
reason: "channel_closed"
|
|
5463
|
+
});
|
|
4069
5464
|
info.client.markSessionClosed(sid);
|
|
4070
5465
|
if (defaultEntry === sessEntry) defaultEntry = void 0;
|
|
4071
5466
|
sessEntry.events.close();
|
|
@@ -4114,10 +5509,12 @@ function createAcpSessionBridge(opts) {
|
|
|
4114
5509
|
}
|
|
4115
5510
|
}
|
|
4116
5511
|
__name(ensureChannel, "ensureChannel");
|
|
4117
|
-
async function doSpawn(modelServiceId, effectiveScope, requestedClientId, onSessionRegistered) {
|
|
5512
|
+
async function doSpawn(modelServiceId, effectiveScope, approvalMode, requestedClientId, onSessionRegistered, parentSessionId) {
|
|
4118
5513
|
const ci = await ensureChannel();
|
|
4119
5514
|
ci.sessionSpawnsInFlight++;
|
|
4120
5515
|
let sessionRegistered = false;
|
|
5516
|
+
let sessionRemovedDuringInitialization = false;
|
|
5517
|
+
let initializedSessionId;
|
|
4121
5518
|
let newSessionResp;
|
|
4122
5519
|
try {
|
|
4123
5520
|
try {
|
|
@@ -4149,16 +5546,61 @@ function createAcpSessionBridge(opts) {
|
|
|
4149
5546
|
if (shuttingDown) {
|
|
4150
5547
|
throw new Error("AcpSessionBridge is shutting down");
|
|
4151
5548
|
}
|
|
4152
|
-
const entry = createSessionEntry(
|
|
4153
|
-
ci,
|
|
4154
|
-
newSessionResp.sessionId,
|
|
4155
|
-
boundWorkspace
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
5549
|
+
const entry = createSessionEntry(
|
|
5550
|
+
ci,
|
|
5551
|
+
newSessionResp.sessionId,
|
|
5552
|
+
boundWorkspace,
|
|
5553
|
+
void 0,
|
|
5554
|
+
{ parentSessionId }
|
|
5555
|
+
);
|
|
5556
|
+
initializedSessionId = entry.sessionId;
|
|
5557
|
+
sessionRegistered = true;
|
|
5558
|
+
onSessionRegistered?.();
|
|
5559
|
+
seedSnapshotCaches(entry, newSessionResp);
|
|
5560
|
+
const clientId = registerClient(entry, requestedClientId);
|
|
5561
|
+
let parentSessionPersisted;
|
|
5562
|
+
if (entry.parentSessionId) {
|
|
5563
|
+
const parentDeadline = Date.now() + initTimeoutMs;
|
|
5564
|
+
let lastParentErr;
|
|
5565
|
+
for (let attempt = 1; attempt <= MAX_PARENT_PERSIST_ATTEMPTS; attempt++) {
|
|
5566
|
+
const remaining = parentDeadline - Date.now();
|
|
5567
|
+
if (remaining <= 0) {
|
|
5568
|
+
parentSessionPersisted = false;
|
|
5569
|
+
lastParentErr = "deadline exceeded";
|
|
5570
|
+
break;
|
|
5571
|
+
}
|
|
5572
|
+
try {
|
|
5573
|
+
const parentResult = await Promise.race([
|
|
5574
|
+
withTimeout(
|
|
5575
|
+
entry.connection.extMethod(
|
|
5576
|
+
SERVE_CONTROL_EXT_METHODS.sessionParent,
|
|
5577
|
+
{
|
|
5578
|
+
sessionId: entry.sessionId,
|
|
5579
|
+
parentSessionId: entry.parentSessionId
|
|
5580
|
+
}
|
|
5581
|
+
),
|
|
5582
|
+
remaining,
|
|
5583
|
+
"sessionParent"
|
|
5584
|
+
),
|
|
5585
|
+
getTransportClosedReject(entry)
|
|
5586
|
+
]);
|
|
5587
|
+
parentSessionPersisted = parentResult?.persisted === true;
|
|
5588
|
+
break;
|
|
5589
|
+
} catch (err) {
|
|
5590
|
+
lastParentErr = err instanceof Error ? err.message : String(err);
|
|
5591
|
+
const terminal = err instanceof BridgeTimeoutError || err instanceof BridgeChannelClosedError || attempt === MAX_PARENT_PERSIST_ATTEMPTS;
|
|
5592
|
+
if (terminal) {
|
|
5593
|
+
parentSessionPersisted = false;
|
|
5594
|
+
break;
|
|
5595
|
+
}
|
|
5596
|
+
}
|
|
5597
|
+
}
|
|
5598
|
+
if (parentSessionPersisted === false) {
|
|
5599
|
+
writeStderrLine(
|
|
5600
|
+
`qwen serve: parentSessionId for ${entry.sessionId} was not persisted (${lastParentErr ?? "unknown"}) \u2014 the parent link is live-only until restart (reported to the caller via parentSessionPersisted=false)`
|
|
5601
|
+
);
|
|
5602
|
+
}
|
|
5603
|
+
}
|
|
4162
5604
|
if (modelServiceId) {
|
|
4163
5605
|
await applyModelServiceId(
|
|
4164
5606
|
entry,
|
|
@@ -4168,22 +5610,51 @@ function createAcpSessionBridge(opts) {
|
|
|
4168
5610
|
).catch(() => {
|
|
4169
5611
|
});
|
|
4170
5612
|
}
|
|
5613
|
+
if (approvalMode) {
|
|
5614
|
+
try {
|
|
5615
|
+
await applyApprovalMode(entry, approvalMode, false, clientId);
|
|
5616
|
+
} catch (err) {
|
|
5617
|
+
try {
|
|
5618
|
+
await closeSessionImpl(entry.sessionId, void 0, {
|
|
5619
|
+
reason: "approval_mode_initialization_failed"
|
|
5620
|
+
});
|
|
5621
|
+
sessionRemovedDuringInitialization = true;
|
|
5622
|
+
} catch {
|
|
5623
|
+
}
|
|
5624
|
+
throw err;
|
|
5625
|
+
}
|
|
5626
|
+
}
|
|
4171
5627
|
if (!byId.has(entry.sessionId)) {
|
|
4172
5628
|
throw new Error(
|
|
4173
|
-
`Session ${entry.sessionId} died during
|
|
5629
|
+
`Session ${entry.sessionId} died during session initialization`
|
|
4174
5630
|
);
|
|
4175
5631
|
}
|
|
5632
|
+
if (effectiveScope === "single" && !defaultEntry) defaultEntry = entry;
|
|
4176
5633
|
return {
|
|
4177
5634
|
sessionId: entry.sessionId,
|
|
4178
5635
|
workspaceCwd: entry.workspaceCwd,
|
|
4179
5636
|
attached: false,
|
|
4180
5637
|
clientId,
|
|
4181
|
-
createdAt: entry.createdAt
|
|
5638
|
+
createdAt: entry.createdAt,
|
|
5639
|
+
...entry.parentSessionId ? { parentSessionPersisted: parentSessionPersisted === true } : {}
|
|
4182
5640
|
};
|
|
4183
5641
|
} finally {
|
|
4184
5642
|
ci.sessionSpawnsInFlight = Math.max(0, ci.sessionSpawnsInFlight - 1);
|
|
4185
5643
|
if (!sessionRegistered) {
|
|
4186
5644
|
await reapPendingEmptyChannel(ci);
|
|
5645
|
+
} else if (sessionRemovedDuringInitialization && hasNoChannelWork(ci)) {
|
|
5646
|
+
await reapPendingEmptyChannel(ci);
|
|
5647
|
+
if (!ci.isDying) {
|
|
5648
|
+
await startIdleTimer(
|
|
5649
|
+
ci,
|
|
5650
|
+
`approval-mode initialization failure "${initializedSessionId}"`
|
|
5651
|
+
);
|
|
5652
|
+
}
|
|
5653
|
+
} else if (sessionRegistered && hasNoChannelWork(ci) && !ci.isDying) {
|
|
5654
|
+
await startIdleTimer(
|
|
5655
|
+
ci,
|
|
5656
|
+
`session orphaned during initialization "${initializedSessionId}"`
|
|
5657
|
+
);
|
|
4187
5658
|
}
|
|
4188
5659
|
}
|
|
4189
5660
|
}
|
|
@@ -4245,9 +5716,125 @@ function createAcpSessionBridge(opts) {
|
|
|
4245
5716
|
return work;
|
|
4246
5717
|
}
|
|
4247
5718
|
__name(applyModelServiceId, "applyModelServiceId");
|
|
5719
|
+
async function applyApprovalMode(entry, mode, persist, originatorClientId) {
|
|
5720
|
+
if (persist && !persistApprovalMode) {
|
|
5721
|
+
throw new Error(
|
|
5722
|
+
"setSessionApprovalMode called with `persist: true` but no `persistApprovalMode` callback wired in BridgeOptions. runQwenServe wires the production callback; direct embeds and tests must opt in or omit `persist`."
|
|
5723
|
+
);
|
|
5724
|
+
}
|
|
5725
|
+
const approvalWork = entry.approvalModeQueue.then(async () => {
|
|
5726
|
+
entry.approvalModeRoundtripInFlight = true;
|
|
5727
|
+
let succeeded = false;
|
|
5728
|
+
try {
|
|
5729
|
+
const response = await Promise.race([
|
|
5730
|
+
withTimeout(
|
|
5731
|
+
entry.connection.extMethod(
|
|
5732
|
+
SERVE_CONTROL_EXT_METHODS.sessionApprovalMode,
|
|
5733
|
+
{ sessionId: entry.sessionId, mode }
|
|
5734
|
+
),
|
|
5735
|
+
initTimeoutMs,
|
|
5736
|
+
SERVE_CONTROL_EXT_METHODS.sessionApprovalMode
|
|
5737
|
+
),
|
|
5738
|
+
getTransportClosedReject(entry)
|
|
5739
|
+
]);
|
|
5740
|
+
if (typeof response.current !== "string" || !KNOWN_APPROVAL_MODES.has(response.current)) {
|
|
5741
|
+
throw new Error(
|
|
5742
|
+
`Agent returned unknown approval mode: ${JSON.stringify(response.current)}`
|
|
5743
|
+
);
|
|
5744
|
+
}
|
|
5745
|
+
let persisted = false;
|
|
5746
|
+
if (persist) {
|
|
5747
|
+
try {
|
|
5748
|
+
await withTimeout(
|
|
5749
|
+
persistApprovalMode?.(boundWorkspace, mode) ?? Promise.resolve(),
|
|
5750
|
+
PERSIST_TIMEOUT_MS,
|
|
5751
|
+
"persistApprovalMode"
|
|
5752
|
+
);
|
|
5753
|
+
persisted = persistApprovalMode !== void 0;
|
|
5754
|
+
} catch (err) {
|
|
5755
|
+
writeStderrLine(
|
|
5756
|
+
`setSessionApprovalMode: persist failed: ${err instanceof Error ? err.message : String(err)}`
|
|
5757
|
+
);
|
|
5758
|
+
}
|
|
5759
|
+
}
|
|
5760
|
+
publishApprovalModeChanged(
|
|
5761
|
+
entry,
|
|
5762
|
+
{
|
|
5763
|
+
previous: response.previous,
|
|
5764
|
+
next: response.current,
|
|
5765
|
+
persisted
|
|
5766
|
+
},
|
|
5767
|
+
originatorClientId
|
|
5768
|
+
);
|
|
5769
|
+
if (persisted) {
|
|
5770
|
+
broadcastWorkspaceEvent(
|
|
5771
|
+
{
|
|
5772
|
+
type: "approval_mode_changed",
|
|
5773
|
+
data: {
|
|
5774
|
+
sessionId: entry.sessionId,
|
|
5775
|
+
previous: response.previous,
|
|
5776
|
+
next: response.current,
|
|
5777
|
+
persisted
|
|
5778
|
+
},
|
|
5779
|
+
...originatorClientId ? { originatorClientId } : {}
|
|
5780
|
+
},
|
|
5781
|
+
entry.sessionId
|
|
5782
|
+
);
|
|
5783
|
+
for (const peer of byId.values()) {
|
|
5784
|
+
if (peer.sessionId === entry.sessionId) {
|
|
5785
|
+
continue;
|
|
5786
|
+
}
|
|
5787
|
+
peer.currentApprovalMode = response.current;
|
|
5788
|
+
}
|
|
5789
|
+
}
|
|
5790
|
+
succeeded = true;
|
|
5791
|
+
return {
|
|
5792
|
+
sessionId: entry.sessionId,
|
|
5793
|
+
mode: response.current,
|
|
5794
|
+
previous: response.previous,
|
|
5795
|
+
persisted
|
|
5796
|
+
};
|
|
5797
|
+
} finally {
|
|
5798
|
+
entry.approvalModeRoundtripInFlight = false;
|
|
5799
|
+
if (succeeded) {
|
|
5800
|
+
void reconcileAfterRoundtrip(entry, "approvalMode");
|
|
5801
|
+
} else {
|
|
5802
|
+
writeStderrLine(
|
|
5803
|
+
`[reconcile] session=${entry.sessionId} target=approvalMode action=skipped reason=roundtrip_failed`
|
|
5804
|
+
);
|
|
5805
|
+
}
|
|
5806
|
+
}
|
|
5807
|
+
});
|
|
5808
|
+
entry.approvalModeQueue = approvalWork.then(
|
|
5809
|
+
() => void 0,
|
|
5810
|
+
() => void 0
|
|
5811
|
+
);
|
|
5812
|
+
try {
|
|
5813
|
+
return await approvalWork;
|
|
5814
|
+
} catch (err) {
|
|
5815
|
+
const data = err?.data;
|
|
5816
|
+
if (data && typeof data === "object" && "errorKind" in data && data.errorKind === "trust_gate") {
|
|
5817
|
+
const rawMessage = err?.message;
|
|
5818
|
+
const message = typeof rawMessage === "string" ? rawMessage : "Trust-gate rejection from ACP child";
|
|
5819
|
+
throw new TrustGateError(message);
|
|
5820
|
+
}
|
|
5821
|
+
throw err;
|
|
5822
|
+
}
|
|
5823
|
+
}
|
|
5824
|
+
__name(applyApprovalMode, "applyApprovalMode");
|
|
5825
|
+
async function applyApprovalModeForAttach(entry, mode, clientId) {
|
|
5826
|
+
try {
|
|
5827
|
+
await applyApprovalMode(entry, mode, false, clientId);
|
|
5828
|
+
} catch (err) {
|
|
5829
|
+
await rollbackAttachRegistration(entry, clientId);
|
|
5830
|
+
throw err;
|
|
5831
|
+
}
|
|
5832
|
+
}
|
|
5833
|
+
__name(applyApprovalModeForAttach, "applyApprovalModeForAttach");
|
|
4248
5834
|
const cancelPendingForSession = /* @__PURE__ */ __name((sessionId) => {
|
|
4249
5835
|
permissionMediator.forgetSession(sessionId);
|
|
4250
5836
|
byId.get(sessionId)?.pendingPermissionIds.clear();
|
|
5837
|
+
byId.get(sessionId)?.pendingInteractions.clear();
|
|
4251
5838
|
}, "cancelPendingForSession");
|
|
4252
5839
|
const getTransportClosedReject = /* @__PURE__ */ __name((entry) => {
|
|
4253
5840
|
if (!entry.transportClosedReject) {
|
|
@@ -4529,10 +6116,15 @@ function createAcpSessionBridge(opts) {
|
|
|
4529
6116
|
sessionId,
|
|
4530
6117
|
workspaceCwd,
|
|
4531
6118
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6119
|
+
...options.parentSessionId ? { parentSessionId: options.parentSessionId } : {},
|
|
4532
6120
|
channel: ci.channel,
|
|
4533
6121
|
connection: ci.connection,
|
|
4534
6122
|
events,
|
|
4535
|
-
artifacts: new SessionArtifactStore({
|
|
6123
|
+
artifacts: new SessionArtifactStore({
|
|
6124
|
+
sessionId,
|
|
6125
|
+
workspaceCwd,
|
|
6126
|
+
persistence: createSessionArtifactPersistence(ci.connection, sessionId)
|
|
6127
|
+
}),
|
|
4536
6128
|
promptQueue: Promise.resolve(),
|
|
4537
6129
|
pendingPromptCount: 0,
|
|
4538
6130
|
pendingPromptList: [],
|
|
@@ -4542,6 +6134,7 @@ function createAcpSessionBridge(opts) {
|
|
|
4542
6134
|
modelPublishGeneration: 0,
|
|
4543
6135
|
approvalModePublishGeneration: 0,
|
|
4544
6136
|
pendingPermissionIds: /* @__PURE__ */ new Set(),
|
|
6137
|
+
pendingInteractions: /* @__PURE__ */ new Map(),
|
|
4545
6138
|
clientIds: /* @__PURE__ */ new Map(),
|
|
4546
6139
|
clientLastSeenAt: /* @__PURE__ */ new Map(),
|
|
4547
6140
|
attachCount: 0,
|
|
@@ -4553,6 +6146,12 @@ function createAcpSessionBridge(opts) {
|
|
|
4553
6146
|
byId.set(entry.sessionId, entry);
|
|
4554
6147
|
touchActivity();
|
|
4555
6148
|
telemetry.metrics?.sessionLifecycle("spawn");
|
|
6149
|
+
emitSessionLifecycle({
|
|
6150
|
+
type: "registered",
|
|
6151
|
+
sessionId: entry.sessionId,
|
|
6152
|
+
workspaceCwd: entry.workspaceCwd,
|
|
6153
|
+
reason: options.lifecycleReason ?? "spawn"
|
|
6154
|
+
});
|
|
4556
6155
|
if (options.drainEarlyEvents !== false) {
|
|
4557
6156
|
ci.client.drainEarlyEvents(entry.sessionId, entry);
|
|
4558
6157
|
}
|
|
@@ -4567,6 +6166,42 @@ function createAcpSessionBridge(opts) {
|
|
|
4567
6166
|
});
|
|
4568
6167
|
}
|
|
4569
6168
|
}, "publishArtifactChanges");
|
|
6169
|
+
const artifactReseedChanges = /* @__PURE__ */ __name((before, after) => {
|
|
6170
|
+
const beforeById = new Map(
|
|
6171
|
+
before.map((artifact) => [artifact.id, artifact])
|
|
6172
|
+
);
|
|
6173
|
+
const afterById = new Map(after.map((artifact) => [artifact.id, artifact]));
|
|
6174
|
+
const changes = [];
|
|
6175
|
+
for (const artifact of before) {
|
|
6176
|
+
if (!afterById.has(artifact.id)) {
|
|
6177
|
+
changes.push({
|
|
6178
|
+
action: "removed",
|
|
6179
|
+
artifactId: artifact.id,
|
|
6180
|
+
artifact,
|
|
6181
|
+
reason: "eviction"
|
|
6182
|
+
});
|
|
6183
|
+
}
|
|
6184
|
+
}
|
|
6185
|
+
for (const artifact of after) {
|
|
6186
|
+
const previous = beforeById.get(artifact.id);
|
|
6187
|
+
if (!previous) {
|
|
6188
|
+
changes.push({
|
|
6189
|
+
action: "created",
|
|
6190
|
+
artifactId: artifact.id,
|
|
6191
|
+
artifact
|
|
6192
|
+
});
|
|
6193
|
+
continue;
|
|
6194
|
+
}
|
|
6195
|
+
if (!publicArtifactsEqual(previous, artifact)) {
|
|
6196
|
+
changes.push({
|
|
6197
|
+
action: "updated",
|
|
6198
|
+
artifactId: artifact.id,
|
|
6199
|
+
artifact
|
|
6200
|
+
});
|
|
6201
|
+
}
|
|
6202
|
+
}
|
|
6203
|
+
return changes;
|
|
6204
|
+
}, "artifactReseedChanges");
|
|
4570
6205
|
const makeClientArtifactInput = /* @__PURE__ */ __name((artifact, clientId) => {
|
|
4571
6206
|
const input = {
|
|
4572
6207
|
title: artifact.title,
|
|
@@ -4579,6 +6214,8 @@ function createAcpSessionBridge(opts) {
|
|
|
4579
6214
|
mimeType: artifact.mimeType,
|
|
4580
6215
|
sizeBytes: artifact.sizeBytes,
|
|
4581
6216
|
metadata: artifact.metadata,
|
|
6217
|
+
retention: artifact.retention,
|
|
6218
|
+
clientRetained: artifact.clientRetained,
|
|
4582
6219
|
source: "client"
|
|
4583
6220
|
};
|
|
4584
6221
|
if (clientId) {
|
|
@@ -4586,6 +6223,31 @@ function createAcpSessionBridge(opts) {
|
|
|
4586
6223
|
}
|
|
4587
6224
|
return input;
|
|
4588
6225
|
}, "makeClientArtifactInput");
|
|
6226
|
+
function createSessionArtifactPersistence(connection, sessionId) {
|
|
6227
|
+
return {
|
|
6228
|
+
recordEvent: /* @__PURE__ */ __name(async (payload) => {
|
|
6229
|
+
await connection.extMethod(
|
|
6230
|
+
SERVE_CONTROL_EXT_METHODS.sessionArtifactsPersist,
|
|
6231
|
+
{
|
|
6232
|
+
sessionId,
|
|
6233
|
+
kind: "event",
|
|
6234
|
+
payload
|
|
6235
|
+
}
|
|
6236
|
+
);
|
|
6237
|
+
}, "recordEvent"),
|
|
6238
|
+
recordSnapshot: /* @__PURE__ */ __name(async (payload) => {
|
|
6239
|
+
await connection.extMethod(
|
|
6240
|
+
SERVE_CONTROL_EXT_METHODS.sessionArtifactsPersist,
|
|
6241
|
+
{
|
|
6242
|
+
sessionId,
|
|
6243
|
+
kind: "snapshot",
|
|
6244
|
+
payload
|
|
6245
|
+
}
|
|
6246
|
+
);
|
|
6247
|
+
}, "recordSnapshot")
|
|
6248
|
+
};
|
|
6249
|
+
}
|
|
6250
|
+
__name(createSessionArtifactPersistence, "createSessionArtifactPersistence");
|
|
4589
6251
|
const seedSnapshotCaches = /* @__PURE__ */ __name((entry, resp) => {
|
|
4590
6252
|
const model = resp.models?.currentModelId;
|
|
4591
6253
|
if (typeof model === "string" && model.length > 0) {
|
|
@@ -4633,16 +6295,55 @@ function createAcpSessionBridge(opts) {
|
|
|
4633
6295
|
}
|
|
4634
6296
|
return { lastEventId: snapshot.lastEventId, ...replayStatus };
|
|
4635
6297
|
}, "replayFieldsFor");
|
|
6298
|
+
const restoredArtifactSnapshotFromState = /* @__PURE__ */ __name((state) => {
|
|
6299
|
+
const candidate = state.artifactSnapshot;
|
|
6300
|
+
const warnings = [];
|
|
6301
|
+
const snapshot = normalizeSnapshotPayload(candidate, warnings);
|
|
6302
|
+
if (!snapshot) return void 0;
|
|
6303
|
+
const snapshotWarnings = isRecord2(candidate) && Array.isArray(candidate["warnings"]) ? candidate["warnings"].filter(
|
|
6304
|
+
(warning) => typeof warning === "string" && warning.length <= 1e3
|
|
6305
|
+
).slice(-500) : [];
|
|
6306
|
+
return {
|
|
6307
|
+
v: SESSION_ARTIFACT_PERSISTENCE_VERSION,
|
|
6308
|
+
sessionId: snapshot.sessionId,
|
|
6309
|
+
sequence: snapshot.sequence,
|
|
6310
|
+
artifacts: snapshot.artifacts,
|
|
6311
|
+
...snapshot.markerArtifacts ? { markerArtifacts: snapshot.markerArtifacts } : {},
|
|
6312
|
+
tombstonedIds: snapshot.tombstonedIds ?? [],
|
|
6313
|
+
stickyEphemeralIds: snapshot.stickyEphemeralIds ?? [],
|
|
6314
|
+
warnings: [...warnings, ...snapshotWarnings]
|
|
6315
|
+
};
|
|
6316
|
+
}, "restoredArtifactSnapshotFromState");
|
|
6317
|
+
const artifactSnapshotUnavailableReason = /* @__PURE__ */ __name((state) => {
|
|
6318
|
+
const reason = state.artifactSnapshotUnavailable;
|
|
6319
|
+
return typeof reason === "string" && reason ? reason : void 0;
|
|
6320
|
+
}, "artifactSnapshotUnavailableReason");
|
|
6321
|
+
const publicRestoreState = /* @__PURE__ */ __name((state) => {
|
|
6322
|
+
const {
|
|
6323
|
+
artifactSnapshot: _artifactSnapshot,
|
|
6324
|
+
artifactSnapshotUnavailable: _artifactSnapshotUnavailable,
|
|
6325
|
+
...publicState
|
|
6326
|
+
} = state;
|
|
6327
|
+
return publicState;
|
|
6328
|
+
}, "publicRestoreState");
|
|
4636
6329
|
async function restoreSession(action, req, options = {}) {
|
|
4637
6330
|
if (shuttingDown) {
|
|
4638
6331
|
throw new Error("AcpSessionBridge is shutting down");
|
|
4639
6332
|
}
|
|
4640
6333
|
const workspaceKey = resolveWorkspaceKey(req.workspaceCwd);
|
|
6334
|
+
if (req.approvalMode !== void 0 && !KNOWN_APPROVAL_MODES.has(req.approvalMode)) {
|
|
6335
|
+
throw new Error(
|
|
6336
|
+
`Invalid approvalMode: ${JSON.stringify(req.approvalMode)}`
|
|
6337
|
+
);
|
|
6338
|
+
}
|
|
4641
6339
|
const historyReplay = action === "load" ? req.historyReplay ?? "stream" : "stream";
|
|
4642
6340
|
const existing = byId.get(req.sessionId);
|
|
4643
6341
|
if (existing) {
|
|
4644
6342
|
existing.attachCount++;
|
|
4645
6343
|
const clientId = registerClient(existing, req.clientId);
|
|
6344
|
+
if (req.approvalMode) {
|
|
6345
|
+
await applyApprovalModeForAttach(existing, req.approvalMode, clientId);
|
|
6346
|
+
}
|
|
4646
6347
|
return {
|
|
4647
6348
|
sessionId: existing.sessionId,
|
|
4648
6349
|
workspaceCwd: existing.workspaceCwd,
|
|
@@ -4681,10 +6382,14 @@ function createAcpSessionBridge(opts) {
|
|
|
4681
6382
|
"the agent child likely crashed during session restore \u2014 retry to restore the session"
|
|
4682
6383
|
);
|
|
4683
6384
|
}
|
|
6385
|
+
const clientId = registerClient(entry, req.clientId);
|
|
6386
|
+
if (req.approvalMode) {
|
|
6387
|
+
await applyApprovalModeForAttach(entry, req.approvalMode, clientId);
|
|
6388
|
+
}
|
|
4684
6389
|
return {
|
|
4685
6390
|
...restored,
|
|
4686
6391
|
attached: true,
|
|
4687
|
-
clientId
|
|
6392
|
+
clientId,
|
|
4688
6393
|
createdAt: entry.createdAt,
|
|
4689
6394
|
hasActivePrompt: entry.promptActive
|
|
4690
6395
|
};
|
|
@@ -4794,6 +6499,23 @@ function createAcpSessionBridge(opts) {
|
|
|
4794
6499
|
restoreEvents.close();
|
|
4795
6500
|
racedEntry.attachCount += 1 + coalesceState.count;
|
|
4796
6501
|
const clientId2 = registerClient(racedEntry, req.clientId);
|
|
6502
|
+
if (req.approvalMode) {
|
|
6503
|
+
try {
|
|
6504
|
+
await applyApprovalMode(
|
|
6505
|
+
racedEntry,
|
|
6506
|
+
req.approvalMode,
|
|
6507
|
+
false,
|
|
6508
|
+
clientId2
|
|
6509
|
+
);
|
|
6510
|
+
} catch (err) {
|
|
6511
|
+
await rollbackAttachRegistration(
|
|
6512
|
+
racedEntry,
|
|
6513
|
+
clientId2,
|
|
6514
|
+
1 + coalesceState.count
|
|
6515
|
+
);
|
|
6516
|
+
throw err;
|
|
6517
|
+
}
|
|
6518
|
+
}
|
|
4797
6519
|
return {
|
|
4798
6520
|
sessionId: racedEntry.sessionId,
|
|
4799
6521
|
workspaceCwd: racedEntry.workspaceCwd,
|
|
@@ -4810,22 +6532,48 @@ function createAcpSessionBridge(opts) {
|
|
|
4810
6532
|
req.sessionId,
|
|
4811
6533
|
workspaceKey,
|
|
4812
6534
|
restoreEvents,
|
|
4813
|
-
{
|
|
6535
|
+
{
|
|
6536
|
+
drainEarlyEvents: replayUpdates.length === 0,
|
|
6537
|
+
lifecycleReason: action,
|
|
6538
|
+
// Re-seed the persisted parent lineage the caller recovered from the
|
|
6539
|
+
// transcript, so a restored sub-session's status reports its parent.
|
|
6540
|
+
...req.parentSessionId ? { parentSessionId: req.parentSessionId } : {}
|
|
6541
|
+
}
|
|
4814
6542
|
);
|
|
4815
6543
|
releaseAdmissionOnce();
|
|
4816
|
-
|
|
6544
|
+
const restoredArtifactSnapshot = restoredArtifactSnapshotFromState(state);
|
|
6545
|
+
const publicState = publicRestoreState(state);
|
|
6546
|
+
entry.restoreState = publicState;
|
|
4817
6547
|
if (replayPartial === true) {
|
|
4818
6548
|
entry.restoreReplayPartial = true;
|
|
4819
6549
|
}
|
|
4820
6550
|
if (replayError !== void 0) {
|
|
4821
6551
|
entry.restoreReplayError = replayError;
|
|
4822
6552
|
}
|
|
4823
|
-
seedSnapshotCaches(entry,
|
|
6553
|
+
seedSnapshotCaches(entry, publicState);
|
|
6554
|
+
const artifactRestoreWarnings = await entry.artifacts.restore(
|
|
6555
|
+
restoredArtifactSnapshot
|
|
6556
|
+
);
|
|
6557
|
+
for (const warning of artifactRestoreWarnings) {
|
|
6558
|
+
writeStderrLine(
|
|
6559
|
+
`[artifacts] session=${entry.sessionId} action=restore_warning warning=${JSON.stringify(
|
|
6560
|
+
warning
|
|
6561
|
+
)}`
|
|
6562
|
+
);
|
|
6563
|
+
}
|
|
6564
|
+
const artifactRestoreFailed = artifactRestoreWarnings.some(
|
|
6565
|
+
(warning) => isArtifactRestoreFailureWarning(warning)
|
|
6566
|
+
);
|
|
4824
6567
|
if (replayUpdates.length > 0) {
|
|
4825
|
-
await ci.client.seedSessionUpdates(entry, replayUpdates
|
|
6568
|
+
await ci.client.seedSessionUpdates(entry, replayUpdates, {
|
|
6569
|
+
ingestArtifacts: restoredArtifactSnapshot === void 0 || artifactRestoreFailed
|
|
6570
|
+
});
|
|
4826
6571
|
ci.client.drainEarlyEvents(entry.sessionId, entry);
|
|
4827
6572
|
}
|
|
4828
6573
|
const clientId = registerClient(entry, req.clientId);
|
|
6574
|
+
if (req.approvalMode) {
|
|
6575
|
+
await applyApprovalModeForAttach(entry, req.approvalMode, clientId);
|
|
6576
|
+
}
|
|
4829
6577
|
entry.attachCount = coalesceState.count;
|
|
4830
6578
|
registeredEntry = entry;
|
|
4831
6579
|
return {
|
|
@@ -4834,7 +6582,8 @@ function createAcpSessionBridge(opts) {
|
|
|
4834
6582
|
attached: false,
|
|
4835
6583
|
clientId,
|
|
4836
6584
|
createdAt: entry.createdAt,
|
|
4837
|
-
state,
|
|
6585
|
+
state: publicState,
|
|
6586
|
+
...artifactRestoreWarnings.length > 0 ? { artifactWarnings: artifactRestoreWarnings } : {},
|
|
4838
6587
|
hasActivePrompt: entry.promptActive,
|
|
4839
6588
|
...replayFieldsFor(entry, action)
|
|
4840
6589
|
};
|
|
@@ -4846,9 +6595,16 @@ function createAcpSessionBridge(opts) {
|
|
|
4846
6595
|
if (!registeredEntry) {
|
|
4847
6596
|
restoreEvents.close();
|
|
4848
6597
|
let removedRestoreEntry = false;
|
|
4849
|
-
|
|
6598
|
+
const restoreEntry = byId.get(req.sessionId);
|
|
6599
|
+
if (restoreEntry?.events === restoreEvents) {
|
|
4850
6600
|
byId.delete(req.sessionId);
|
|
4851
6601
|
ci?.sessionIds.delete(req.sessionId);
|
|
6602
|
+
emitSessionLifecycle({
|
|
6603
|
+
type: "removed",
|
|
6604
|
+
sessionId: req.sessionId,
|
|
6605
|
+
workspaceCwd: restoreEntry.workspaceCwd,
|
|
6606
|
+
reason: "restore_failed"
|
|
6607
|
+
});
|
|
4852
6608
|
removedRestoreEntry = true;
|
|
4853
6609
|
}
|
|
4854
6610
|
if (removedRestoreEntry && ci && hasNoChannelWork(ci)) {
|
|
@@ -4909,6 +6665,7 @@ function createAcpSessionBridge(opts) {
|
|
|
4909
6665
|
}
|
|
4910
6666
|
permissionMediator.forgetSession(sessionId);
|
|
4911
6667
|
entry.pendingPermissionIds.clear();
|
|
6668
|
+
entry.pendingInteractions.clear();
|
|
4912
6669
|
if (entry.promptActive) {
|
|
4913
6670
|
entry.promptActive = false;
|
|
4914
6671
|
activePromptCounter--;
|
|
@@ -4916,6 +6673,12 @@ function createAcpSessionBridge(opts) {
|
|
|
4916
6673
|
}
|
|
4917
6674
|
byId.delete(sessionId);
|
|
4918
6675
|
telemetry.metrics?.sessionLifecycle("close");
|
|
6676
|
+
emitSessionLifecycle({
|
|
6677
|
+
type: "removed",
|
|
6678
|
+
sessionId,
|
|
6679
|
+
workspaceCwd: entry.workspaceCwd,
|
|
6680
|
+
reason
|
|
6681
|
+
});
|
|
4919
6682
|
ci?.client.markSessionClosed(sessionId);
|
|
4920
6683
|
try {
|
|
4921
6684
|
entry.events.publish({
|
|
@@ -5039,6 +6802,11 @@ function createAcpSessionBridge(opts) {
|
|
|
5039
6802
|
throw new InvalidSessionScopeError(req.sessionScope);
|
|
5040
6803
|
}
|
|
5041
6804
|
const effectiveScope = req.sessionScope ?? defaultSessionScope;
|
|
6805
|
+
if (req.approvalMode !== void 0 && !KNOWN_APPROVAL_MODES.has(req.approvalMode)) {
|
|
6806
|
+
throw new Error(
|
|
6807
|
+
`Invalid approvalMode: ${JSON.stringify(req.approvalMode)}`
|
|
6808
|
+
);
|
|
6809
|
+
}
|
|
5042
6810
|
if (effectiveScope === "single") {
|
|
5043
6811
|
const existing = defaultEntry;
|
|
5044
6812
|
if (existing) {
|
|
@@ -5053,6 +6821,13 @@ function createAcpSessionBridge(opts) {
|
|
|
5053
6821
|
).catch(() => {
|
|
5054
6822
|
});
|
|
5055
6823
|
}
|
|
6824
|
+
if (req.approvalMode) {
|
|
6825
|
+
await applyApprovalModeForAttach(
|
|
6826
|
+
existing,
|
|
6827
|
+
req.approvalMode,
|
|
6828
|
+
clientId
|
|
6829
|
+
);
|
|
6830
|
+
}
|
|
5056
6831
|
return {
|
|
5057
6832
|
sessionId: existing.sessionId,
|
|
5058
6833
|
workspaceCwd: existing.workspaceCwd,
|
|
@@ -5083,6 +6858,13 @@ function createAcpSessionBridge(opts) {
|
|
|
5083
6858
|
).catch(() => {
|
|
5084
6859
|
});
|
|
5085
6860
|
}
|
|
6861
|
+
if (req.approvalMode) {
|
|
6862
|
+
await applyApprovalModeForAttach(
|
|
6863
|
+
attachedEntry,
|
|
6864
|
+
req.approvalMode,
|
|
6865
|
+
clientId
|
|
6866
|
+
);
|
|
6867
|
+
}
|
|
5086
6868
|
return {
|
|
5087
6869
|
...session,
|
|
5088
6870
|
attached: true,
|
|
@@ -5107,8 +6889,10 @@ function createAcpSessionBridge(opts) {
|
|
|
5107
6889
|
const promise = doSpawn(
|
|
5108
6890
|
req.modelServiceId,
|
|
5109
6891
|
effectiveScope,
|
|
6892
|
+
req.approvalMode,
|
|
5110
6893
|
req.clientId,
|
|
5111
|
-
releaseAdmissionOnce
|
|
6894
|
+
releaseAdmissionOnce,
|
|
6895
|
+
req.parentSessionId
|
|
5112
6896
|
);
|
|
5113
6897
|
const tracker = effectiveScope === "single" ? workspaceKey : `${workspaceKey}#${randomUUID2()}`;
|
|
5114
6898
|
inFlightSpawns.set(tracker, promise);
|
|
@@ -5249,6 +7033,7 @@ function createAcpSessionBridge(opts) {
|
|
|
5249
7033
|
return copy;
|
|
5250
7034
|
})();
|
|
5251
7035
|
entry.promptActive = true;
|
|
7036
|
+
delete entry.turnError;
|
|
5252
7037
|
activePromptCounter++;
|
|
5253
7038
|
entry.sessionLastSeenAt = Date.now();
|
|
5254
7039
|
touchActivity();
|
|
@@ -5771,9 +7556,10 @@ function createAcpSessionBridge(opts) {
|
|
|
5771
7556
|
}
|
|
5772
7557
|
return { displayName: entry.displayName };
|
|
5773
7558
|
},
|
|
5774
|
-
async getSessionArtifacts(sessionId) {
|
|
7559
|
+
async getSessionArtifacts(sessionId, context) {
|
|
5775
7560
|
const entry = byId.get(sessionId);
|
|
5776
7561
|
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
7562
|
+
resolveTrustedClientId(entry, context?.clientId);
|
|
5777
7563
|
return entry.artifacts.list();
|
|
5778
7564
|
},
|
|
5779
7565
|
async addSessionArtifact(sessionId, artifact, context) {
|
|
@@ -5781,9 +7567,13 @@ function createAcpSessionBridge(opts) {
|
|
|
5781
7567
|
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
5782
7568
|
const clientId = resolveTrustedClientId(entry, context?.clientId);
|
|
5783
7569
|
const input = makeClientArtifactInput(artifact, clientId);
|
|
5784
|
-
const result = await entry.artifacts.upsertMany([input], {
|
|
7570
|
+
const result = await entry.artifacts.upsertMany([input], {
|
|
7571
|
+
validationStrict: true,
|
|
7572
|
+
persistenceStrict: false
|
|
7573
|
+
});
|
|
5785
7574
|
publishArtifactChanges(entry, result.changes, clientId);
|
|
5786
|
-
|
|
7575
|
+
const warnings = [...result.warnings ?? []];
|
|
7576
|
+
return warnings.length > 0 ? { ...result, warnings } : result;
|
|
5787
7577
|
},
|
|
5788
7578
|
async removeSessionArtifact(sessionId, artifactId, context) {
|
|
5789
7579
|
const entry = byId.get(sessionId);
|
|
@@ -5791,7 +7581,8 @@ function createAcpSessionBridge(opts) {
|
|
|
5791
7581
|
const clientId = resolveTrustedClientId(entry, context?.clientId);
|
|
5792
7582
|
const result = await entry.artifacts.remove(artifactId, { clientId });
|
|
5793
7583
|
publishArtifactChanges(entry, result.changes, clientId);
|
|
5794
|
-
|
|
7584
|
+
const warnings = [...result.warnings ?? []];
|
|
7585
|
+
return warnings.length > 0 ? { ...result, warnings } : result;
|
|
5795
7586
|
},
|
|
5796
7587
|
listWorkspaceSessions(workspaceCwd) {
|
|
5797
7588
|
if (!path3.isAbsolute(workspaceCwd)) return [];
|
|
@@ -6076,6 +7867,35 @@ function createAcpSessionBridge(opts) {
|
|
|
6076
7867
|
SERVE_STATUS_EXT_METHODS.sessionLspStatus
|
|
6077
7868
|
);
|
|
6078
7869
|
},
|
|
7870
|
+
async getSessionTranscriptPage(req) {
|
|
7871
|
+
const info = await ensureChannel();
|
|
7872
|
+
try {
|
|
7873
|
+
const response = await withWorkspaceControl(
|
|
7874
|
+
info,
|
|
7875
|
+
() => withTimeout(
|
|
7876
|
+
Promise.race([
|
|
7877
|
+
info.connection.extMethod(
|
|
7878
|
+
SERVE_STATUS_EXT_METHODS.sessionTranscript,
|
|
7879
|
+
{ ...req, cwd: boundWorkspace }
|
|
7880
|
+
),
|
|
7881
|
+
getChannelClosedReject(info)
|
|
7882
|
+
]),
|
|
7883
|
+
Math.max(initTimeoutMs, SESSION_TRANSCRIPT_TIMEOUT_MS),
|
|
7884
|
+
SERVE_STATUS_EXT_METHODS.sessionTranscript
|
|
7885
|
+
)
|
|
7886
|
+
);
|
|
7887
|
+
return response;
|
|
7888
|
+
} catch (err) {
|
|
7889
|
+
if (isAcpSessionResourceNotFound(err, req.sessionId)) {
|
|
7890
|
+
throw new SessionNotFoundError(req.sessionId);
|
|
7891
|
+
}
|
|
7892
|
+
throw err;
|
|
7893
|
+
} finally {
|
|
7894
|
+
if (hasNoChannelWork(info)) {
|
|
7895
|
+
await startIdleTimer(info, "session transcript");
|
|
7896
|
+
}
|
|
7897
|
+
}
|
|
7898
|
+
},
|
|
6079
7899
|
async cancelSessionTask(sessionId, taskId, taskKind) {
|
|
6080
7900
|
return requestSessionStatus(
|
|
6081
7901
|
sessionId,
|
|
@@ -6314,109 +8134,12 @@ function createAcpSessionBridge(opts) {
|
|
|
6314
8134
|
entry,
|
|
6315
8135
|
context?.clientId
|
|
6316
8136
|
);
|
|
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
|
|
8137
|
+
return await applyApprovalMode(
|
|
8138
|
+
entry,
|
|
8139
|
+
mode,
|
|
8140
|
+
opts2.persist,
|
|
8141
|
+
originatorClientId
|
|
6408
8142
|
);
|
|
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
8143
|
},
|
|
6421
8144
|
async generateSessionRecap(sessionId, _context) {
|
|
6422
8145
|
const entry = byId.get(sessionId);
|
|
@@ -6769,6 +8492,48 @@ function createAcpSessionBridge(opts) {
|
|
|
6769
8492
|
const targetTurnIndex = response["targetTurnIndex"] ?? 0;
|
|
6770
8493
|
const filesChanged = response["filesChanged"] ?? [];
|
|
6771
8494
|
const filesFailed = response["filesFailed"] ?? [];
|
|
8495
|
+
const artifactSnapshot = restoredArtifactSnapshotFromState(
|
|
8496
|
+
response
|
|
8497
|
+
);
|
|
8498
|
+
const artifactSnapshotUnavailable = artifactSnapshotUnavailableReason(
|
|
8499
|
+
response
|
|
8500
|
+
);
|
|
8501
|
+
const beforeArtifacts = (await entry.artifacts.list()).artifacts;
|
|
8502
|
+
const shouldRestoreArtifactSnapshot = artifactSnapshot !== void 0 && artifactSnapshotUnavailable === void 0;
|
|
8503
|
+
const artifactRestoreWarnings = artifactSnapshotUnavailable !== void 0 ? [
|
|
8504
|
+
`artifact snapshot rebuild unavailable during rewind: ${artifactSnapshotUnavailable}`
|
|
8505
|
+
] : shouldRestoreArtifactSnapshot ? await entry.artifacts.restore(artifactSnapshot, {
|
|
8506
|
+
preserveLiveEphemeral: true
|
|
8507
|
+
}) : [];
|
|
8508
|
+
const artifactRestoreFailed = artifactRestoreWarnings.some(
|
|
8509
|
+
isArtifactRestoreFailureWarning
|
|
8510
|
+
);
|
|
8511
|
+
const shouldRecordArtifactSnapshot = shouldRestoreArtifactSnapshot && !artifactRestoreFailed;
|
|
8512
|
+
const artifactSnapshotWarnings = shouldRecordArtifactSnapshot ? await entry.artifacts.recordSnapshot() : [];
|
|
8513
|
+
const artifactWarnings = [
|
|
8514
|
+
...artifactRestoreWarnings,
|
|
8515
|
+
...artifactSnapshotWarnings
|
|
8516
|
+
];
|
|
8517
|
+
for (const warning of artifactRestoreWarnings) {
|
|
8518
|
+
writeStderrLine(
|
|
8519
|
+
`[artifacts] session=${entry.sessionId} action=rewind_restore_warning warning=${JSON.stringify(
|
|
8520
|
+
warning
|
|
8521
|
+
)}`
|
|
8522
|
+
);
|
|
8523
|
+
}
|
|
8524
|
+
for (const warning of artifactSnapshotWarnings) {
|
|
8525
|
+
writeStderrLine(
|
|
8526
|
+
`[artifacts] session=${entry.sessionId} action=rewind_snapshot_warning warning=${JSON.stringify(
|
|
8527
|
+
warning
|
|
8528
|
+
)}`
|
|
8529
|
+
);
|
|
8530
|
+
}
|
|
8531
|
+
const afterArtifacts = (await entry.artifacts.list()).artifacts;
|
|
8532
|
+
publishArtifactChanges(
|
|
8533
|
+
entry,
|
|
8534
|
+
artifactReseedChanges(beforeArtifacts, afterArtifacts),
|
|
8535
|
+
originatorClientId
|
|
8536
|
+
);
|
|
6772
8537
|
try {
|
|
6773
8538
|
entry.events.publish({
|
|
6774
8539
|
type: "session_rewound",
|
|
@@ -6777,7 +8542,8 @@ function createAcpSessionBridge(opts) {
|
|
|
6777
8542
|
promptId: req.promptId,
|
|
6778
8543
|
targetTurnIndex,
|
|
6779
8544
|
filesChanged,
|
|
6780
|
-
filesFailed
|
|
8545
|
+
filesFailed,
|
|
8546
|
+
...artifactWarnings.length > 0 ? { warnings: artifactWarnings } : {}
|
|
6781
8547
|
},
|
|
6782
8548
|
...originatorClientId ? { originatorClientId } : {}
|
|
6783
8549
|
});
|
|
@@ -6787,7 +8553,8 @@ function createAcpSessionBridge(opts) {
|
|
|
6787
8553
|
rewound: filesFailed.length === 0,
|
|
6788
8554
|
targetTurnIndex,
|
|
6789
8555
|
filesChanged,
|
|
6790
|
-
filesFailed
|
|
8556
|
+
filesFailed,
|
|
8557
|
+
...artifactWarnings.length > 0 ? { warnings: artifactWarnings } : {}
|
|
6791
8558
|
};
|
|
6792
8559
|
},
|
|
6793
8560
|
async manageMcpServer(serverName, action, originatorClientId) {
|
|
@@ -6915,6 +8682,7 @@ function createAcpSessionBridge(opts) {
|
|
|
6915
8682
|
}
|
|
6916
8683
|
permissionMediator.forgetSession(sessionId);
|
|
6917
8684
|
entry.pendingPermissionIds.clear();
|
|
8685
|
+
entry.pendingInteractions.clear();
|
|
6918
8686
|
if (entry.promptActive) {
|
|
6919
8687
|
entry.promptActive = false;
|
|
6920
8688
|
activePromptCounter--;
|
|
@@ -6923,6 +8691,12 @@ function createAcpSessionBridge(opts) {
|
|
|
6923
8691
|
if (defaultEntry === entry) defaultEntry = void 0;
|
|
6924
8692
|
byId.delete(sessionId);
|
|
6925
8693
|
telemetry.metrics?.sessionLifecycle("die");
|
|
8694
|
+
emitSessionLifecycle({
|
|
8695
|
+
type: "removed",
|
|
8696
|
+
sessionId,
|
|
8697
|
+
workspaceCwd: entry.workspaceCwd,
|
|
8698
|
+
reason: "killed"
|
|
8699
|
+
});
|
|
6926
8700
|
const ci = channelInfoForEntry(entry);
|
|
6927
8701
|
if (!ci) {
|
|
6928
8702
|
writeStderrLine(
|
|
@@ -6972,8 +8746,17 @@ function createAcpSessionBridge(opts) {
|
|
|
6972
8746
|
cancelIdleTimer();
|
|
6973
8747
|
stopSessionReaper();
|
|
6974
8748
|
const channels = Array.from(aliveChannels);
|
|
8749
|
+
const entries = Array.from(byId.values());
|
|
6975
8750
|
defaultEntry = void 0;
|
|
6976
8751
|
byId.clear();
|
|
8752
|
+
for (const entry of entries) {
|
|
8753
|
+
emitSessionLifecycle({
|
|
8754
|
+
type: "removed",
|
|
8755
|
+
sessionId: entry.sessionId,
|
|
8756
|
+
workspaceCwd: entry.workspaceCwd,
|
|
8757
|
+
reason: "kill_all"
|
|
8758
|
+
});
|
|
8759
|
+
}
|
|
6977
8760
|
for (const info of channels) {
|
|
6978
8761
|
try {
|
|
6979
8762
|
info.channel.killSync();
|
|
@@ -6991,11 +8774,18 @@ function createAcpSessionBridge(opts) {
|
|
|
6991
8774
|
for (const e of entries) {
|
|
6992
8775
|
permissionMediator.forgetSession(e.sessionId);
|
|
6993
8776
|
e.pendingPermissionIds.clear();
|
|
8777
|
+
e.pendingInteractions.clear();
|
|
6994
8778
|
}
|
|
6995
8779
|
defaultEntry = void 0;
|
|
6996
8780
|
byId.clear();
|
|
6997
8781
|
for (const e of entries) {
|
|
6998
8782
|
telemetry.metrics?.sessionLifecycle("die");
|
|
8783
|
+
emitSessionLifecycle({
|
|
8784
|
+
type: "removed",
|
|
8785
|
+
sessionId: e.sessionId,
|
|
8786
|
+
workspaceCwd: e.workspaceCwd,
|
|
8787
|
+
reason: "daemon_shutdown"
|
|
8788
|
+
});
|
|
6999
8789
|
try {
|
|
7000
8790
|
e.events.publish({
|
|
7001
8791
|
type: "session_died",
|
|
@@ -7057,6 +8847,7 @@ var createHttpAcpBridge = createAcpSessionBridge;
|
|
|
7057
8847
|
|
|
7058
8848
|
export {
|
|
7059
8849
|
SessionArtifactValidationError,
|
|
8850
|
+
SessionArtifactAuthorizationError,
|
|
7060
8851
|
extractErrorMessage,
|
|
7061
8852
|
extractErrorCode,
|
|
7062
8853
|
classifyTurnErrorKind,
|