@qwen-code/qwen-code 0.21.13 → 0.21.14-nightly.20260822.7a4566cb3b
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/docs/configuration/settings.md +15 -7
- package/bundled/qc-helper/docs/extension/introduction.md +2 -0
- package/bundled/qc-helper/docs/features/_meta.ts +1 -0
- package/bundled/qc-helper/docs/features/channels/dingtalk.md +1 -1
- package/bundled/qc-helper/docs/features/code-review.md +25 -3
- package/bundled/qc-helper/docs/features/commands.md +120 -8
- package/bundled/qc-helper/docs/features/markdown-rendering.md +6 -0
- package/bundled/qc-helper/docs/features/sub-agents.md +3 -3
- package/bundled/qc-helper/docs/features/terminal-images.md +50 -0
- package/bundled/qc-helper/docs/qwen-serve-deploy-local.md +1 -1
- package/bundled/qc-helper/docs/qwen-serve.md +21 -4
- package/bundled/review/SKILL.md +113 -62
- package/chunks/MaxSizedBox-NCWYES5B.js +98 -0
- package/chunks/{StandaloneSessionPicker-KEGXNUG6.js → StandaloneSessionPicker-A4V4F64Y.js} +80 -79
- package/chunks/{acp-startup-profiler-NMDXYGDN.js → acp-startup-profiler-DDVSJKJK.js} +2 -2
- package/chunks/{acpAgent-XPES32S7.js → acpAgent-YYRH7HTQ.js} +1094 -278
- package/chunks/{agent-DPTOZFEM.js → agent-NEM254CJ.js} +52 -51
- package/chunks/agent-headless-AVFYIRPV.js +88 -0
- package/chunks/{anthropicContentGenerator-G7BVXTKN.js → anthropicContentGenerator-IWFTSBSV.js} +25 -25
- package/chunks/{artifact-tool-GEXTOPVO.js → artifact-tool-SBXMTVCB.js} +3 -3
- package/chunks/{askUserQuestion-B33XRSB7.js → askUserQuestion-KEQJRFD6.js} +3 -3
- package/chunks/{bridge-YNQBAA4Z.js → bridge-2CGOUO25.js} +59 -57
- package/chunks/{ca-4CQ6MYNQ.js → ca-SLZJD6QH.js} +8 -0
- package/chunks/{channel-management-service-GWY2GAIC.js → channel-management-service-FUJ2EFTF.js} +1 -1
- package/chunks/channel-settings-store-AZKNHHDC.js +103 -0
- package/chunks/{channel-worker-group-7LXCMZ77.js → channel-worker-group-CTE54QL6.js} +8 -7
- package/chunks/{channel-worker-manager-RYEME4ZZ.js → channel-worker-manager-4EHTD3T4.js} +8 -7
- package/chunks/{channel-worker-supervisor-OEVWBHGT.js → channel-worker-supervisor-F6I6WSWW.js} +6 -5
- package/chunks/{chunk-KD5BOPLP.js → chunk-2G4QF4VI.js} +125 -31
- package/chunks/{chunk-BHILXOC3.js → chunk-2MTIAN5Z.js} +3 -3
- package/chunks/{chunk-A4QRWUDE.js → chunk-2NWPB6T4.js} +6 -6
- package/chunks/{chunk-KV27IEHM.js → chunk-2TRNCJK4.js} +1 -1
- package/chunks/{chunk-EL5SXY3T.js → chunk-32CHFLGV.js} +198 -23
- package/chunks/{chunk-5CVO7YES.js → chunk-36OF5PRW.js} +1 -1
- package/chunks/{chunk-X26JYGTD.js → chunk-37BZSWEV.js} +1 -1
- package/chunks/{chunk-WFSTRPRO.js → chunk-3CLIDIVQ.js} +3 -3
- package/chunks/{chunk-A267JX7H.js → chunk-3HWN4E7N.js} +18 -18
- package/chunks/{chunk-ZYYTZUI2.js → chunk-3LYGXZ2H.js} +3 -3
- package/chunks/chunk-3SIGAHUX.js +3969 -0
- package/chunks/{chunk-BVJS5BSP.js → chunk-3XD4PT3B.js} +1 -1
- package/chunks/{chunk-6QFDQMQX.js → chunk-3ZFJUBTP.js} +1 -1
- package/chunks/{chunk-HUTNYWBG.js → chunk-45UD2TU4.js} +19 -11
- package/chunks/{chunk-4W5U2TUT.js → chunk-4F5WV4RF.js} +4 -4
- package/chunks/{chunk-I3G5JWBV.js → chunk-4GP3MNQ2.js} +2695 -1524
- package/chunks/{chunk-V26NVGAU.js → chunk-4MIDDJLJ.js} +8 -3
- package/chunks/{chunk-TS6625XH.js → chunk-5MK45KZ3.js} +9 -5
- package/chunks/{chunk-V57QC6WW.js → chunk-5OTMZVPL.js} +11 -9
- package/chunks/{chunk-MC4TLKEI.js → chunk-5PHLBWBL.js} +3 -3
- package/chunks/chunk-64LXOKG4.js +367 -0
- package/chunks/{chunk-6HY6IF3Z.js → chunk-6NFAEG54.js} +22 -2
- package/chunks/{chunk-TIGA2IVL.js → chunk-6NJIPX6W.js} +10 -298
- package/chunks/{chunk-MRMJCLIY.js → chunk-6RB2S4QP.js} +3 -3
- package/chunks/chunk-6S2OXVJT.js +369 -0
- package/chunks/{chunk-O4D7SPJN.js → chunk-6U46RZ25.js} +3 -3
- package/chunks/{chunk-JOOFMXGF.js → chunk-6UDHEIUT.js} +6 -6
- package/chunks/{chunk-VOQXFAY5.js → chunk-74TONY4F.js} +286 -0
- package/chunks/{chunk-LJQAUZMR.js → chunk-7CPJMZVJ.js} +1627 -180
- package/chunks/{chunk-T3CHYWJZ.js → chunk-7E7UCJHS.js} +100 -4
- package/chunks/{chunk-YZHRN3CF.js → chunk-7IV52LTO.js} +2 -2
- package/chunks/{chunk-4PKCUT7F.js → chunk-7JDGSJQH.js} +7 -7
- package/chunks/{chunk-GO7STP6N.js → chunk-7VITNOH6.js} +4 -4
- package/chunks/{chunk-CU6GJD55.js → chunk-7WLFSDLG.js} +1 -1
- package/chunks/{chunk-4WKAA4KS.js → chunk-A4N4FTMH.js} +1 -11
- package/chunks/{chunk-BUQDLC2G.js → chunk-A5F2YNO6.js} +3 -3
- package/chunks/{chunk-OY5RNSV4.js → chunk-AGCLK4G7.js} +3 -3
- package/chunks/{chunk-IA2K2HJE.js → chunk-AP3B7LKD.js} +2 -2
- package/chunks/{chunk-Y3YCSWG5.js → chunk-AZF6SME3.js} +2 -2
- package/chunks/{chunk-QFJ5JHQR.js → chunk-C72BXMJ5.js} +1 -1
- package/chunks/chunk-CETB5XY4.js +3654 -0
- package/chunks/{chunk-AA2BEKV3.js → chunk-CTAP6PO3.js} +3 -3
- package/chunks/{chunk-MS7GNABG.js → chunk-CXJHQVEK.js} +8 -0
- package/chunks/{chunk-QF2NHSPB.js → chunk-D534POS2.js} +274 -225
- package/chunks/{chunk-UKMR3CKV.js → chunk-D7QQ6RUB.js} +24 -9
- package/chunks/{chunk-DXIBNLBZ.js → chunk-DB2OKNTU.js} +2 -2
- package/chunks/{chunk-DGZEMW5R.js → chunk-DJEHYM4H.js} +3 -3
- package/chunks/{chunk-O565M6A4.js → chunk-DRVS6K7A.js} +260 -16
- package/chunks/{chunk-U3VNBPUO.js → chunk-DV4375RD.js} +5 -5
- package/chunks/{chunk-OXCMMLED.js → chunk-E63A23D7.js} +3 -3
- package/chunks/{chunk-4NFY2S7N.js → chunk-E7REAOTN.js} +15 -0
- package/chunks/{chunk-6ORZ2EDC.js → chunk-EW2IQ3ID.js} +3 -3
- package/chunks/{chunk-DGK6P3BQ.js → chunk-EXVK5R7X.js} +13 -13
- package/chunks/{chunk-NWNEANAT.js → chunk-FOGQO4FG.js} +953 -660
- package/chunks/{chunk-3U3VAL47.js → chunk-FV6PXZRO.js} +1 -1
- package/chunks/{chunk-76J3IJ3M.js → chunk-FX5TGHDE.js} +8 -8
- package/chunks/{chunk-QIH2HAEN.js → chunk-GXG4PYQB.js} +1 -1
- package/chunks/{chunk-FDGGZIYW.js → chunk-HDKCKCYO.js} +3 -3
- package/chunks/{chunk-RIAABG2K.js → chunk-HERHU33B.js} +2 -2
- package/chunks/{chunk-ZYBBISBY.js → chunk-HETN3NE2.js} +18 -15
- package/chunks/{chunk-XIT3GURH.js → chunk-HGBACZGD.js} +3 -3
- package/chunks/{chunk-UZPM4XHR.js → chunk-HGH7DXNI.js} +29 -8
- package/chunks/{chunk-CHHABXPE.js → chunk-HIK2OF33.js} +1 -1
- package/chunks/{chunk-FC4KQFCS.js → chunk-HMJ2P3SV.js} +1 -1
- package/chunks/chunk-HRVMTN37.js +26 -0
- package/chunks/{chunk-PQ36L4F5.js → chunk-HZZGYJIP.js} +1 -1
- package/chunks/{chunk-PT4I7NBA.js → chunk-I432KXWD.js} +1 -1
- package/chunks/{chunk-3EZLLVDF.js → chunk-IBJ5S45N.js} +3 -3
- package/chunks/{chunk-A4MFR4C5.js → chunk-IO257NUD.js} +3 -5
- package/chunks/{chunk-MIPFDQAF.js → chunk-ISJMN3ML.js} +1 -1
- package/chunks/chunk-IWOWEENB.js +9169 -0
- package/chunks/{chunk-XHRY7BXD.js → chunk-J6SIP2IT.js} +4 -4
- package/chunks/{chunk-OD2WU5QY.js → chunk-J7JQXYVL.js} +1 -1
- package/chunks/{chunk-NEFOOD54.js → chunk-J7VPB2HF.js} +4 -4
- package/chunks/{chunk-P7MPQI75.js → chunk-JGUBDRBC.js} +2 -2
- package/chunks/{chunk-SXCOEJO4.js → chunk-JOLJTKIG.js} +1 -1
- package/chunks/{chunk-NTTBXH7U.js → chunk-JS73NNWF.js} +8 -0
- package/chunks/{chunk-QRCNA3SR.js → chunk-JXLPQTMW.js} +50 -70
- package/chunks/{chunk-NNHNLJ2X.js → chunk-K2MSGB3W.js} +317 -72
- package/chunks/{chunk-KBXKR5R2.js → chunk-K52PZNU4.js} +1 -1
- package/chunks/{chunk-OOQUVWIG.js → chunk-K5OIW3I7.js} +4 -4
- package/chunks/{chunk-SOORFWKY.js → chunk-KSPRQSB4.js} +1 -1
- package/chunks/{chunk-WSH6GRKG.js → chunk-KVGR4MCF.js} +3 -3
- package/chunks/{chunk-6YKYPVDV.js → chunk-LC5M7YVE.js} +4 -4
- package/chunks/{chunk-M6NDLY2T.js → chunk-LD5VIJ7S.js} +1 -1
- package/chunks/{chunk-OMLJNPBE.js → chunk-LDGWX737.js} +84 -4
- package/chunks/{chunk-FAAUPSY2.js → chunk-LI4YHTQK.js} +1 -1
- package/chunks/{chunk-CXAOG665.js → chunk-LIKIJAUH.js} +2 -2
- package/chunks/chunk-LJZSMWOH.js +18 -0
- package/chunks/{chunk-BKPPWD33.js → chunk-M2IL2RTA.js} +22 -2
- package/chunks/{chunk-N5YPT6PH.js → chunk-MHOY756Q.js} +25 -13
- package/chunks/{chunk-YP5KH5Y7.js → chunk-ND7NOI4P.js} +1 -1
- package/chunks/{chunk-AW4TRXOC.js → chunk-NP2LV67M.js} +1 -1
- package/chunks/{chunk-GGDBMYGZ.js → chunk-NQB52P65.js} +5 -5
- package/chunks/{chunk-E3DYKPYZ.js → chunk-NUGZAFR2.js} +3 -3
- package/chunks/{chunk-GMIEIY3Y.js → chunk-O5BDEWBC.js} +3 -3
- package/chunks/{chunk-GDXET5XU.js → chunk-O5Z7EB6Y.js} +3 -3
- package/chunks/{chunk-5EYKMUUY.js → chunk-OTXXOZKW.js} +1 -1
- package/chunks/{chunk-M732YJPW.js → chunk-PALTIYXB.js} +6 -6
- package/chunks/{chunk-7UXUJ5T3.js → chunk-PLECQWDB.js} +1 -1
- package/chunks/{chunk-MLPXSYXR.js → chunk-PMIKPGDF.js} +66 -32
- package/chunks/{chunk-IWPYVAO2.js → chunk-PSQJ24ZZ.js} +3 -3
- package/chunks/{chunk-CPHEPGAO.js → chunk-PZRUV52H.js} +2 -2
- package/chunks/{chunk-PP4FUZNV.js → chunk-Q7RPLE33.js} +2 -2
- package/chunks/{chunk-S5DWMTHO.js → chunk-QQUVPMMZ.js} +2 -0
- package/chunks/{chunk-JUEPY766.js → chunk-RNGTMHKN.js} +3 -3
- package/chunks/{chunk-TUGSVUJB.js → chunk-ROV6B34L.js} +6 -6
- package/chunks/{chunk-RVOJ2FBP.js → chunk-RPJOOK6N.js} +1576 -10633
- package/chunks/{chunk-AXN7UPYQ.js → chunk-RQJJZQOH.js} +2 -2
- package/chunks/{chunk-AFSG32D5.js → chunk-RRRCWDFO.js} +1 -1
- package/chunks/{chunk-2M55OBEB.js → chunk-S3G6YFQC.js} +1 -1
- package/chunks/{chunk-4S5337WV.js → chunk-SVU5KGR7.js} +6527 -3314
- package/chunks/chunk-TBVSALA3.js +1109 -0
- package/chunks/{chunk-QUGAUQGJ.js → chunk-TBWQLLFO.js} +2 -0
- package/chunks/{chunk-AS5IN4GK.js → chunk-THK3QUU6.js} +5 -5
- package/chunks/{chunk-JNDY2MDR.js → chunk-TLDZKNZP.js} +1 -1
- package/chunks/{chunk-ZZO74WKW.js → chunk-TNAIZBPQ.js} +115 -28
- package/chunks/{chunk-3Z43GZUQ.js → chunk-TXASDT2M.js} +1 -1
- package/chunks/{chunk-CZBYX5MY.js → chunk-U53XZHAE.js} +1 -1
- package/chunks/{chunk-HIBNSEJE.js → chunk-UDG5EZJI.js} +0 -2
- package/chunks/{chunk-QNQAPSN2.js → chunk-URN2JIAQ.js} +4 -1
- package/chunks/{chunk-EZ5RP345.js → chunk-UYQTX2P6.js} +44 -13
- package/chunks/{chunk-BZ263IKX.js → chunk-UZPYR3EV.js} +1 -1
- package/chunks/{chunk-YZOSNW4R.js → chunk-VFACN2CE.js} +1 -1
- package/chunks/{chunk-RWDNJBWN.js → chunk-VLRBJ24J.js} +2 -2
- package/chunks/{chunk-NHDR76JX.js → chunk-VMJL7RH6.js} +4 -4
- package/chunks/{chunk-E23IPNGS.js → chunk-VOTDAZR7.js} +35 -6
- package/chunks/{chunk-XLLKYULU.js → chunk-VSNPOSDN.js} +2 -2
- package/chunks/{chunk-25EYDK42.js → chunk-VUMLT7E5.js} +2 -2
- package/chunks/{chunk-H4A72DE5.js → chunk-W4CRPSC5.js} +1 -1
- package/chunks/{chunk-NM5V5OVW.js → chunk-WJJYJVQE.js} +3 -3
- package/chunks/{chunk-ROIYNTHJ.js → chunk-WRT324N6.js} +3 -3
- package/chunks/chunk-WZAD4ZNJ.js +59 -0
- package/chunks/{chunk-SG7ZP5PG.js → chunk-WZDM44SB.js} +4 -4
- package/chunks/{chunk-JE7KQ2UP.js → chunk-XIQ5HQ2F.js} +469 -77
- package/chunks/{chunk-QV5YN5YD.js → chunk-XJOHLP2A.js} +5 -5
- package/chunks/{chunk-XUJNK7Y6.js → chunk-XL5K4SVK.js} +1 -1
- package/chunks/{chunk-CKSJECTU.js → chunk-XZJKSETB.js} +9 -5
- package/chunks/{chunk-YEYY7TEP.js → chunk-Y2FEAXLP.js} +7 -7
- package/chunks/{chunk-2B2BF7P7.js → chunk-YDJRMQU4.js} +3 -3
- package/chunks/{chunk-46UV252V.js → chunk-YHC2EYYG.js} +35 -38
- package/chunks/{chunk-OVZPVYZB.js → chunk-YMVFIYHV.js} +96 -18
- package/chunks/{chunk-WGHHA7ZH.js → chunk-Z3JMO2CH.js} +1 -1
- package/chunks/{chunk-MA2HEDVP.js → chunk-ZRYQMWEP.js} +3 -3
- package/chunks/{chunk-IGGGG5CX.js → chunk-ZTAT23TL.js} +15948 -10201
- package/chunks/{computer-use-77C5FHMW.js → computer-use-H2T7IFYX.js} +55 -54
- package/chunks/{config-utils-IVHRSXWI.js → config-utils-3DY73VIJ.js} +2 -2
- package/chunks/contextCommand-5AAZFFFV.js +94 -0
- package/chunks/{core-runtime-7VMIKUTG.js → core-runtime-JWFNKO2S.js} +57 -55
- package/chunks/{create-sub-session-2L5MQRN4.js → create-sub-session-33HIYIJE.js} +58 -56
- package/chunks/{create-sub-session-KRQULJE6.js → create-sub-session-XBCVGNFU.js} +6 -4
- package/chunks/{cron-create-H7UEPDSN.js → cron-create-IKA56DAF.js} +5 -5
- package/chunks/{cron-delete-6SZK7YFM.js → cron-delete-K5Q62EGH.js} +5 -5
- package/chunks/{cron-list-H25JIHEZ.js → cron-list-QBFGAMP7.js} +5 -5
- package/chunks/{daemon-2KTG7ZBN.js → daemon-NNCQ3HWT.js} +806 -147
- package/chunks/{daemon-git-worktree-guard-DXYIMWFT.js → daemon-git-worktree-guard-6233JAPJ.js} +54 -53
- package/chunks/daemon-status-provider-3EI2KALQ.js +104 -0
- package/chunks/daemon-trust-policy-QWWU7CON.js +100 -0
- package/chunks/{daemon-trust-policy-monitor-RYYGCQ3J.js → daemon-trust-policy-monitor-FH7GBAKN.js} +60 -59
- package/chunks/{de-UBEBKG6V.js → de-AH4LGCF5.js} +8 -0
- package/chunks/{deferred-core-runtime-3SGXFXEQ.js → deferred-core-runtime-EDUZQRME.js} +52 -51
- package/chunks/{discovery-ORA5J7YP.js → discovery-X2NV535U.js} +1 -1
- package/chunks/{display-image-ON3EYFRD.js → display-image-OEJMJV3S.js} +5 -5
- package/chunks/{dist-YEPL4RAW.js → dist-5EV2G6MC.js} +2 -3
- package/chunks/{dist-MAQXAQ2Y.js → dist-EQ3Q5XHX.js} +45337 -32756
- package/chunks/{dist-WJ4BHIEW.js → dist-ESS34DHC.js} +72 -0
- package/chunks/{dist-JWMKPWT5.js → dist-EUOUPUFO.js} +3 -4
- package/chunks/{dist-IYYN3RUT.js → dist-UOFRXJT6.js} +2798 -1250
- package/chunks/{earlyInputCapture-KJMLFVQV.js → earlyInputCapture-VSP42LUR.js} +53 -52
- package/chunks/{edit-I4LQFAEC.js → edit-ZTLO5ASX.js} +60 -58
- package/chunks/{en-X6YV7GME.js → en-MD3GP5YL.js} +8 -0
- package/chunks/{enter-worktree-YNPOJSRH.js → enter-worktree-TMTPDS2S.js} +55 -54
- package/chunks/{enterPlanMode-XCPK62ER.js → enterPlanMode-UBAXFMYI.js} +55 -54
- package/chunks/{environment-S7CTIHMX.js → environment-C2CZN7CB.js} +55 -54
- package/chunks/{errors-7HB2AIDS.js → errors-ZUNFQINF.js} +54 -53
- package/chunks/{exit-worktree-AIIY7SR6.js → exit-worktree-572NWEPJ.js} +55 -54
- package/chunks/exitPlanMode-QTIXFGZT.js +86 -0
- package/chunks/{fast-path-NDJV3ZFJ.js → fast-path-CEEVHQL3.js} +3 -3
- package/chunks/{fast-path-settings-LNOFM2YP.js → fast-path-settings-WHX3J3ZI.js} +2 -2
- package/chunks/{fr-VENTC2BS.js → fr-ZEML2JWS.js} +8 -0
- package/chunks/{gemini-RSUFP5OE.js → gemini-I2YR6GMS.js} +114 -112
- package/chunks/{geminiContentGenerator-SQ5XBPZ7.js → geminiContentGenerator-GQWNDZQF.js} +7 -7
- package/chunks/getMachineId-bsd-FBOAYNSY.js +48 -0
- package/chunks/{getMachineId-bsd-FG7IUY6U.js → getMachineId-bsd-YH7RURBZ.js} +4 -4
- package/chunks/{getMachineId-darwin-GLCJI2RA.js → getMachineId-darwin-H6USQYNI.js} +4 -4
- package/chunks/getMachineId-darwin-RNJLJUXY.js +47 -0
- package/chunks/{getMachineId-linux-O6OPPAKO.js → getMachineId-linux-HF2N63ET.js} +3 -3
- package/chunks/getMachineId-linux-UEVRAIAB.js +41 -0
- package/chunks/getMachineId-unsupported-FWXYJ7HW.js +31 -0
- package/chunks/{getMachineId-unsupported-S6CKYVGC.js → getMachineId-unsupported-NYA5DXRB.js} +3 -3
- package/chunks/getMachineId-win-LRVGMHLW.js +50 -0
- package/chunks/{getMachineId-win-X5SRNEH7.js → getMachineId-win-SMEGY2DW.js} +4 -4
- package/chunks/{glob-J4RUJAHX.js → glob-XFBL2RVT.js} +58 -57
- package/chunks/{goal-tools-VHTNVOKK.js → goal-tools-TLJCE7YZ.js} +6 -6
- package/chunks/{grep-VJWPLFOT.js → grep-U675LEXE.js} +55 -54
- package/chunks/handleAutoUpdate-R6RUCIA2.js +97 -0
- package/chunks/{i18n-RM5YOICI.js → i18n-SJCZDZ4C.js} +53 -52
- package/chunks/{image-gen-EXECB5XM.js → image-gen-6VY7CA7C.js} +14 -14
- package/chunks/initializer-CRDZXJOG.js +101 -0
- package/chunks/installationInfo-EW3NEJDZ.js +95 -0
- package/chunks/{ja-QHT4EQTO.js → ja-UM5B2HMD.js} +8 -0
- package/chunks/{keychain-token-storage-3XFJ5ZB2.js → keychain-token-storage-MTFTAESK.js} +3 -3
- package/chunks/list-TZEMTXDH.js +104 -0
- package/chunks/{list-agents-OSVDFI6S.js → list-agents-43XUEXJV.js} +6 -6
- package/chunks/loadedSettingsAdapter-47DG424L.js +98 -0
- package/chunks/{loggingContentGenerator-WF4ZOJEZ.js → loggingContentGenerator-77C6O5VW.js} +31 -29
- package/chunks/{loop-wakeup-WQS6JYAG.js → loop-wakeup-E54KDUXC.js} +6 -6
- package/chunks/{ls-D3HUFHDP.js → ls-CGL2UC3H.js} +7 -7
- package/chunks/{lsp-3QDDJTCS.js → lsp-5IKUO5DX.js} +3 -3
- package/chunks/{managed-npm-update-E7IW6N7V.js → managed-npm-update-AJ5GZRUA.js} +53 -52
- package/chunks/mcp-AQK656Y4.js +98 -0
- package/chunks/{monitor-EE2UYMGE.js → monitor-UDZXKYT3.js} +55 -54
- package/chunks/nonInteractiveCli-6NC3NLZM.js +166 -0
- package/chunks/{notebook-edit-NTDY4QDU.js → notebook-edit-G6WZI2YT.js} +59 -57
- package/chunks/{openaiContentGenerator-YNFVJMRJ.js → openaiContentGenerator-IHVHP3VB.js} +32 -32
- package/chunks/{pidfile-4XSFLYBZ.js → pidfile-EBKS2HBK.js} +53 -52
- package/chunks/{processUtils-WDA2MJFD.js → processUtils-B7M35ZLR.js} +2 -2
- package/chunks/prompt-terminal-ledger-5JYOPOII.js +93 -0
- package/chunks/{pt-VL3I5RNY.js → pt-5BMJ3XYH.js} +8 -0
- package/chunks/{qwenContentGenerator-434M7QKY.js → qwenContentGenerator-WCJPPTED.js} +59 -58
- package/chunks/{qwenOAuth2-GMYZOO6A.js → qwenOAuth2-S2CLBSEZ.js} +10 -10
- package/chunks/read-file-WFM53FTW.js +33 -0
- package/chunks/{read-mcp-resource-XD54ZQMC.js → read-mcp-resource-XYES2B42.js} +3 -3
- package/chunks/{record-artifact-SEYFJEJF.js → record-artifact-W3IBTGL7.js} +10 -6
- package/chunks/{resumeHistoryUtils-EVG3WYK2.js → resumeHistoryUtils-CK4ZC6YO.js} +59 -58
- package/chunks/ripGrep-B3PBLEQ3.js +86 -0
- package/chunks/{ru-4RCKL4PY.js → ru-6GL66ORV.js} +8 -0
- package/chunks/{run-qwen-serve-3QSGRXAN.js → run-qwen-serve-DC5RXI6N.js} +537 -185
- package/chunks/{runtime-5MLLZ7DC.js → runtime-UVEETF54.js} +63 -62
- package/chunks/{scheduler-RPBJ63CH.js → scheduler-MWT3UMVN.js} +54 -53
- package/chunks/{sdk-exporters-grpc-54L4LS5L.js → sdk-exporters-grpc-6J5CKOMO.js} +194 -75
- package/chunks/{sdk-exporters-http-XUVP63H6.js → sdk-exporters-http-Q3YZ4752.js} +40 -45
- package/chunks/{sdk-impl-YXUEN2Z2.js → sdk-impl-BOMCAN2Z.js} +7211 -5454
- package/chunks/{send-message-VZENRWRH.js → send-message-J4VT46OI.js} +8 -8
- package/chunks/{serve-HS3XLPXU.js → serve-AKM75GTE.js} +60 -61
- package/chunks/{server-TC4A7AAZ.js → server-KU7XIDVT.js} +2221 -399
- package/chunks/{session-UBOFXVMN.js → session-DKVYIRWN.js} +108 -105
- package/chunks/{settings-D5644OM5.js → settings-NS3PXZDP.js} +63 -62
- package/chunks/{shell-3PZULQQV.js → shell-UXCNM2OK.js} +52 -51
- package/chunks/{skill-PEVVXQ3L.js → skill-KV7YCCTM.js} +30 -28
- package/chunks/{skill-settings-J65JKKES.js → skill-settings-EXW7ESWJ.js} +59 -58
- package/chunks/{spawnChannel-IYGXRHSQ.js → spawnChannel-FCP44P4V.js} +55 -54
- package/chunks/{standalone-update-IFM53PE7.js → standalone-update-RGW3B2DX.js} +55 -54
- package/chunks/{startInteractiveUI-3AVH7L6H.js → startInteractiveUI-UZMF2BIJ.js} +615 -392
- package/chunks/{syntheticOutput-7GWFIDXT.js → syntheticOutput-JAXMIMZP.js} +4 -4
- package/chunks/{task-create-FCBDX7UH.js → task-create-I2H322E4.js} +12 -12
- package/chunks/{task-list-YIYIUFUA.js → task-list-3OLIWFTQ.js} +21 -7
- package/chunks/{task-stop-X37KYTKB.js → task-stop-JAUY6MSY.js} +3 -3
- package/chunks/{task-update-MYPFVOVM.js → task-update-B3XO4CPI.js} +87 -16
- package/chunks/{team-create-ZAIA7KAV.js → team-create-77XY3KAE.js} +56 -56
- package/chunks/{team-delete-6UMU4XLS.js → team-delete-Q7GE7RRI.js} +6 -6
- package/chunks/{team-plan-approval-UNIUS7OD.js → team-plan-approval-TAQANDQG.js} +55 -54
- package/chunks/{terminal-image-renderer-G343P2RV.js → terminal-image-renderer-V4LIF5NS.js} +54 -53
- package/chunks/theme-manager-KKBAJ574.js +90 -0
- package/chunks/{todoWrite-U2R4NVF4.js → todoWrite-FJJTY2TI.js} +7 -7
- package/chunks/{tool-search-5R2OE4C7.js → tool-search-GZJYRK4I.js} +25 -24
- package/chunks/{total-session-admission-4KBHMQQ2.js → total-session-admission-NIBNWXHE.js} +59 -57
- package/chunks/{trustedFolders-LK6T4XC7.js → trustedFolders-7GEI2NYC.js} +53 -52
- package/chunks/{undici-PYHBDDPN.js → undici-S7WJSKGJ.js} +962 -391
- package/chunks/{update-relaunch-UBKROIC6.js → update-relaunch-OA3OEBMD.js} +5 -5
- package/chunks/{updateCheck-W4Y2GFSD.js → updateCheck-QVHQEVIY.js} +56 -55
- package/chunks/{useAutoAcceptIndicator-C7BEICBF.js → useAutoAcceptIndicator-WZ27J7NY.js} +64 -63
- package/chunks/{validateNonInterActiveAuth-LTEI2TB6.js → validateNonInterActiveAuth-OXJA2JWG.js} +99 -96
- package/chunks/{version-G5A5DGVS.js → version-KJO64WD7.js} +1 -1
- package/chunks/{web-fetch-RTEQTR5Z.js → web-fetch-CC5INI2R.js} +25 -23
- package/chunks/{web-search-5OXHEZPV.js → web-search-MOAUAN6N.js} +18 -18
- package/chunks/{web-shell-static-O73YPVVC.js → web-shell-static-3NUTRHAH.js} +4 -5
- package/chunks/{workflow-ISQJHPG2.js → workflow-BN2ZEJMK.js} +366 -105
- package/chunks/workspace-providers-status-R5QTZFK3.js +101 -0
- package/chunks/{workspace-registration-store-CYNY6IP6.js → workspace-registration-store-WMSLM36M.js} +1 -1
- package/chunks/{workspace-registry-34GTUUHB.js → workspace-registry-J3RUCXH6.js} +59 -57
- package/chunks/{workspace-service-XCDQLE2Y.js → workspace-service-JJZFPKM4.js} +66 -65
- package/chunks/workspace-skills-status-DRIOZRIA.js +101 -0
- package/chunks/{workspace-trust-reconciler-AYGXC7A3.js → workspace-trust-reconciler-YAYSN6CX.js} +69 -67
- package/chunks/write-file-7HXHFFOY.js +88 -0
- package/chunks/{zh-TW-SQ4HTSCW.js → zh-TW-LVLBK4KF.js} +8 -0
- package/chunks/{zh-LMSTK4WD.js → zh-UBMWS2BJ.js} +8 -0
- package/chunks/{zoom-image-4BFYJ3HG.js → zoom-image-EFMQMU4G.js} +24 -22
- package/cli.js +15 -15
- package/locales/ca.js +13 -0
- package/locales/de.js +12 -0
- package/locales/en.js +11 -0
- package/locales/fr.js +13 -0
- package/locales/ja.js +13 -0
- package/locales/pt.js +12 -0
- package/locales/ru.js +12 -0
- package/locales/zh-TW.js +11 -0
- package/locales/zh.js +11 -0
- package/package.json +3 -3
- package/web-shell/assets/{arc-CgOpDKvm.js → arc-BCP1f-bN.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-0Ag_m34F.js → architectureDiagram-3BPJPVTR-D-6ajMay.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-IwTwFau0.js → blockDiagram-GPEHLZMM-CcLlnVnf.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-DLa5GUv8.js → c4Diagram-AAUBKEIU-C_Sp9UwQ.js} +1 -1
- package/web-shell/assets/channel-BcImYzbA.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-DcYP-WFG.js → chunk-2J33WTMH-CG0ngvo9.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-C8MVPMiQ.js → chunk-4BX2VUAB-D43LsVdk.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-BUi4Yfv3.js → chunk-55IACEB6-BqNIn5rz.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-Cy3Su2YY.js → chunk-727SXJPM-BSHmO08Y.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-Dp8Teqv6.js → chunk-AQP2D5EJ-CMUbr5tQ.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-BDTDM-4j.js → chunk-FMBD7UC4-Cm6JflnZ.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-7HfDt0r2.js → chunk-ND2GUHAM-CBR39YQZ.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-BIBU0uCF.js → chunk-QZHKN3VN-CWnqaLb-.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-OWx0rDRW.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-OWx0rDRW.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-DRLdcg9p.js → cose-bilkent-S5V4N54A-DJlTdnfD.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-DCCgDwqW.js → dagre-BM42HDAG-1AFN1uVc.js} +1 -1
- package/web-shell/assets/{diagram-2AECGRRQ-CsBTBmk3.js → diagram-2AECGRRQ-gC0GGNSW.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-C9qjH8Lt.js → diagram-5GNKFQAL-CPmRPT1s.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-Brjs_lj2.js → diagram-KO2AKTUF-atigp6Hr.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-DtiWCl_F.js → diagram-LMA3HP47-BJnJFSRQ.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-DmfKLkBT.js → diagram-OG6HWLK6-CyvJ4fG2.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-Bve_4deH.js → erDiagram-TEJ5UH35-CZac_jT6.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-C2_kXCcV.js → flowDiagram-I6XJVG4X-pd-LQxWB.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-BSBIjiTF.js → ganttDiagram-6RSMTGT7-CvB1at7l.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-CyebwI55.js → gitGraphDiagram-PVQCEYII-B5Y6nkil.js} +1 -1
- package/web-shell/assets/index-Bg3DAn8Z.js +1788 -0
- package/web-shell/assets/{index-d0yfnV1M.js → index-Bj3ZSUHL.js} +1 -1
- package/web-shell/assets/index-mp32EO88.css +5 -0
- package/web-shell/assets/{infoDiagram-5YYISTIA-CSDGnWxU.js → infoDiagram-5YYISTIA-BdyWvU4P.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-BK4jU13p.js → ishikawaDiagram-YF4QCWOH-CaHbUdvN.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-D_W6j-oF.js → journeyDiagram-JHISSGLW-DR9F3FpG.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-C7xf-SdC.js → kanban-definition-UN3LZRKU-BoEUbkw2.js} +1 -1
- package/web-shell/assets/{linear-DQmzxpxd.js → linear-DUbfzV4T.js} +1 -1
- package/web-shell/assets/{mermaid.core-9P7AGOLG.js → mermaid.core-CLLkPcpD.js} +5 -5
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-DgcgMyPk.js → mindmap-definition-RKZ34NQL-FuKI3Stt.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-3wWGb9eG.js → pieDiagram-4H26LBE5-CVAHL7l5.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-BeCXnQw9.js → quadrantDiagram-W4KKPZXB-BkGi5fRj.js} +1 -1
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-Bi1UOHDz.js → requirementDiagram-4Y6WPE33-B5ghV8-K.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-lrLUjjbb.js → sankeyDiagram-5OEKKPKP-CKFbVhkQ.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-BFuqKz6Q.js → sequenceDiagram-3UESZ5HK-C5P9ujPd.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-8DemQLhu.js → stateDiagram-AJRCARHV-CsDn-Rez.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-DpGKD1ez.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-uzEuVQXg.js → timeline-definition-PNZ67QCA-B6oLBkTr.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-BQTeZ_-o.js → vennDiagram-CIIHVFJN-DvtBkYwp.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-B6yM5Im7.js → wardley-L42UT6IY-D2UwTX16.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-ChM8bmgn.js → wardleyDiagram-YWT4CUSO-FnnfCxYI.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-oNG6HyB1.js → xychartDiagram-2RQKCTM6-C1SaMQ7d.js} +1 -1
- package/web-shell/index.html +2 -2
- package/chunks/MaxSizedBox-UQALOVVT.js +0 -97
- package/chunks/agent-headless-GJCQMURW.js +0 -87
- package/chunks/channel-settings-store-WNWYFHG7.js +0 -102
- package/chunks/chunk-AZQ3DT76.js +0 -10017
- package/chunks/chunk-CR3C7WXL.js +0 -113
- package/chunks/chunk-K5IDV3PC.js +0 -519
- package/chunks/chunk-MPHPFVKK.js +0 -48
- package/chunks/chunk-RLKC46YT.js +0 -3582
- package/chunks/chunk-UPDOZBWO.js +0 -358
- package/chunks/contextCommand-PJ77GBWL.js +0 -93
- package/chunks/daemon-status-provider-ZDOYJCU6.js +0 -102
- package/chunks/daemon-trust-policy-E67OYUK2.js +0 -99
- package/chunks/exitPlanMode-R4JY7V2O.js +0 -85
- package/chunks/handleAutoUpdate-POCX2MNB.js +0 -94
- package/chunks/initializer-Z2AFGBNR.js +0 -100
- package/chunks/installationInfo-4I5FXNMH.js +0 -92
- package/chunks/list-ZMBFIGZA.js +0 -103
- package/chunks/loadedSettingsAdapter-T6TV4YJ7.js +0 -97
- package/chunks/mcp-6UNYX4HG.js +0 -97
- package/chunks/nonInteractiveCli-WAY237O4.js +0 -163
- package/chunks/read-file-TAFBKMNI.js +0 -32
- package/chunks/ripGrep-WOEK2PXS.js +0 -85
- package/chunks/theme-manager-Z3RIT7DB.js +0 -89
- package/chunks/undici-3FJYTMOF.js +0 -25914
- package/chunks/workspace-providers-status-2ZPZPUPZ.js +0 -100
- package/chunks/workspace-skills-status-RS4O7AIJ.js +0 -100
- package/chunks/write-file-EWXKVVXF.js +0 -87
- package/web-shell/assets/channel-BrGVBa2L.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-B5TUzJzI.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-B5TUzJzI.js +0 -1
- package/web-shell/assets/index-CIDsgxTe.js +0 -1792
- package/web-shell/assets/index-DiQnWi_E.css +0 -5
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-DhM5ixYd.js +0 -1
- package/chunks/{dist-BROOP2EG.js → dist-LZO24N3E.js} +1 -1
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
} from "./chunk-OCPBI7J5.js";
|
|
20
20
|
import {
|
|
21
21
|
defaultSpawnChannelFactory
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-VMJL7RH6.js";
|
|
23
23
|
import {
|
|
24
24
|
BridgeChannelClosedError,
|
|
25
25
|
BridgeTimeoutError,
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
SessionRestoreTimeoutError,
|
|
30
30
|
createIdleWorkspaceExtensionsStatus,
|
|
31
31
|
createIdleWorkspaceHooksStatus
|
|
32
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-URN2JIAQ.js";
|
|
33
33
|
import {
|
|
34
34
|
ACTIVE_WORK_CLOSE_IF_UNHELD_PARAM,
|
|
35
35
|
ACTIVE_WORK_CLOSE_TIMEOUT_MS,
|
|
@@ -44,6 +44,7 @@ import {
|
|
|
44
44
|
CHANNEL_PROMPT_META_KEY,
|
|
45
45
|
CHANNEL_STARTUP_PROFILE_META_KEY,
|
|
46
46
|
CHANNEL_STARTUP_PROFILE_VERSION,
|
|
47
|
+
DAEMON_ATTACHMENT_REFERENCES_META_KEY,
|
|
47
48
|
DAEMON_CHANNEL_DELIVERY_META_KEY,
|
|
48
49
|
DAEMON_MODEL_PROMPT_META_KEY,
|
|
49
50
|
DAEMON_PROMPT_DISPLAY_TEXT_META_KEY,
|
|
@@ -64,7 +65,7 @@ import {
|
|
|
64
65
|
clampActiveWorkIntervalMs,
|
|
65
66
|
isValidTrustedModelPrompt,
|
|
66
67
|
sessionCloseDrainBudgetMs
|
|
67
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-QQUVPMMZ.js";
|
|
68
69
|
import {
|
|
69
70
|
BranchWhilePromptActiveError,
|
|
70
71
|
BridgeChannelQuarantinedError,
|
|
@@ -96,9 +97,8 @@ import {
|
|
|
96
97
|
LIVE_TASK_TOOL_NAMES,
|
|
97
98
|
MAX_LIVE_SCREEN_CONTEXT_TEXT_CHARS,
|
|
98
99
|
MAX_LIVE_SPEAK_TO_USER_MESSAGE_CHARS,
|
|
99
|
-
MAX_SUB_SESSION_NAME_CHARS
|
|
100
|
-
|
|
101
|
-
} from "./chunk-HIBNSEJE.js";
|
|
100
|
+
MAX_SUB_SESSION_NAME_CHARS
|
|
101
|
+
} from "./chunk-UDG5EZJI.js";
|
|
102
102
|
import {
|
|
103
103
|
EXTERNAL_TOOL_GUARD_READY_META_KEY,
|
|
104
104
|
EXTERNAL_TOOL_GUARD_REQUIRED_VALUE,
|
|
@@ -109,10 +109,15 @@ import {
|
|
|
109
109
|
PROTOCOL_VERSION,
|
|
110
110
|
RequestError
|
|
111
111
|
} from "./chunk-IW6RQPQB.js";
|
|
112
|
+
import {
|
|
113
|
+
MAX_SUB_SESSION_PROMPT_CHARS
|
|
114
|
+
} from "./chunk-LJZSMWOH.js";
|
|
112
115
|
import {
|
|
113
116
|
SESSION_ARTIFACT_PERSISTENCE_VERSION,
|
|
114
117
|
SESSION_TRANSCRIPT_MAX_LIMIT,
|
|
115
118
|
ShellExecutionService,
|
|
119
|
+
TURN_RESULT_CODE_TEXT_TRUNCATED,
|
|
120
|
+
TURN_RESULT_TEXT_MAX_CHARS,
|
|
116
121
|
TrustGateError,
|
|
117
122
|
WORKSPACE_CONTENT_MTIME_MS_METADATA_KEY,
|
|
118
123
|
WORKSPACE_CONTENT_SHA256_METADATA_KEY,
|
|
@@ -120,17 +125,29 @@ import {
|
|
|
120
125
|
isReservedWorkspaceMetadataKey,
|
|
121
126
|
metadataBudgetBytes,
|
|
122
127
|
normalizeSnapshotPayload,
|
|
128
|
+
normalizeTurnResultError,
|
|
123
129
|
stableSessionArtifactId
|
|
124
|
-
} from "./chunk-
|
|
130
|
+
} from "./chunk-SVU5KGR7.js";
|
|
131
|
+
import {
|
|
132
|
+
MAX_DIRECTORY_ARTIFACT_DEPTH,
|
|
133
|
+
MAX_DIRECTORY_ARTIFACT_FILES,
|
|
134
|
+
collectRecordableWorkspaceFiles,
|
|
135
|
+
isOfficeDocumentExtension,
|
|
136
|
+
isRecordableDerivedChild,
|
|
137
|
+
pathHasSkippedDirectoryComponent
|
|
138
|
+
} from "./chunk-TBVSALA3.js";
|
|
125
139
|
import {
|
|
126
140
|
DAEMON_TRACEPARENT_META_KEY,
|
|
127
141
|
DAEMON_TRACESTATE_META_KEY
|
|
128
|
-
} from "./chunk-
|
|
142
|
+
} from "./chunk-6S2OXVJT.js";
|
|
129
143
|
import {
|
|
130
144
|
INVOCATION_CONTEXT_META_KEY,
|
|
131
145
|
PRIVATE_ACP_CAPABILITY_ENV,
|
|
132
146
|
PRIVATE_PARENT_CAPABILITY_META_KEY
|
|
133
|
-
} from "./chunk-
|
|
147
|
+
} from "./chunk-RPJOOK6N.js";
|
|
148
|
+
import {
|
|
149
|
+
getSpecificMimeType
|
|
150
|
+
} from "./chunk-IWOWEENB.js";
|
|
134
151
|
import {
|
|
135
152
|
init_esbuild_shims
|
|
136
153
|
} from "./chunk-5O2XNYP6.js";
|
|
@@ -141,7 +158,7 @@ import {
|
|
|
141
158
|
// packages/acp-bridge/src/bridge.ts
|
|
142
159
|
init_esbuild_shims();
|
|
143
160
|
import { randomBytes, randomUUID as randomUUID2 } from "node:crypto";
|
|
144
|
-
import * as
|
|
161
|
+
import * as path4 from "node:path";
|
|
145
162
|
import { inspect } from "node:util";
|
|
146
163
|
|
|
147
164
|
// packages/acp-bridge/src/compactionEngine.ts
|
|
@@ -1280,6 +1297,523 @@ function getChannelStartupProfileAttributes(response, receivedAtEpochMs, initial
|
|
|
1280
1297
|
}
|
|
1281
1298
|
__name(getChannelStartupProfileAttributes, "getChannelStartupProfileAttributes");
|
|
1282
1299
|
|
|
1300
|
+
// packages/acp-bridge/src/sessionAttachments.ts
|
|
1301
|
+
init_esbuild_shims();
|
|
1302
|
+
import { promises as fs, statSync } from "node:fs";
|
|
1303
|
+
import { tmpdir } from "node:os";
|
|
1304
|
+
import * as path from "node:path";
|
|
1305
|
+
var SESSION_ATTACHMENT_MAX_ITEM_BYTES = 8 * 1024 * 1024;
|
|
1306
|
+
var SESSION_ATTACHMENT_MAX_NAME_BYTES = 255;
|
|
1307
|
+
var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
1308
|
+
"image/bmp",
|
|
1309
|
+
"image/gif",
|
|
1310
|
+
"image/jpeg",
|
|
1311
|
+
"image/png",
|
|
1312
|
+
"image/webp"
|
|
1313
|
+
]);
|
|
1314
|
+
var SESSION_ATTACHMENT_UNAVAILABLE_TEXT = "[Attachment is no longer available]";
|
|
1315
|
+
var SessionAttachmentReferenceError = class extends Error {
|
|
1316
|
+
constructor(message, code) {
|
|
1317
|
+
super(message);
|
|
1318
|
+
this.code = code;
|
|
1319
|
+
this.name = "SessionAttachmentReferenceError";
|
|
1320
|
+
}
|
|
1321
|
+
static {
|
|
1322
|
+
__name(this, "SessionAttachmentReferenceError");
|
|
1323
|
+
}
|
|
1324
|
+
};
|
|
1325
|
+
function isSessionAttachmentReference(value) {
|
|
1326
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1327
|
+
const record = value;
|
|
1328
|
+
return (record["type"] === "image" || record["type"] === "resource") && typeof record["attachmentId"] === "string" && record["attachmentId"].length > 0 && typeof record["mimeType"] === "string" && record["mimeType"].length > 0 && (record["type"] !== "image" || record["mimeType"].startsWith("image/")) && typeof record["size"] === "number" && Number.isSafeInteger(record["size"]) && record["size"] >= 0 && (record["type"] !== "image" || record["size"] > 0);
|
|
1329
|
+
}
|
|
1330
|
+
__name(isSessionAttachmentReference, "isSessionAttachmentReference");
|
|
1331
|
+
function safeAttachmentName(name) {
|
|
1332
|
+
const safeName = path.basename(name.replaceAll("\\", "/")).trim();
|
|
1333
|
+
const isWindowsReserved = /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(safeName);
|
|
1334
|
+
const hasInvalidCharacter = Array.from(safeName).some((character) => {
|
|
1335
|
+
const codePoint = character.codePointAt(0);
|
|
1336
|
+
return codePoint !== void 0 && (codePoint <= 31 || codePoint === 127 || codePoint >= 55296 && codePoint <= 57343);
|
|
1337
|
+
});
|
|
1338
|
+
return !safeName || safeName === "." || safeName === ".." || safeName.endsWith(".") || isWindowsReserved || /[<>:"|?*]/.test(safeName) || hasInvalidCharacter || Buffer.byteLength(safeName) > SESSION_ATTACHMENT_MAX_NAME_BYTES ? void 0 : safeName;
|
|
1339
|
+
}
|
|
1340
|
+
__name(safeAttachmentName, "safeAttachmentName");
|
|
1341
|
+
function truncateUtf8(value, maxBytes) {
|
|
1342
|
+
let bytes = 0;
|
|
1343
|
+
let result = "";
|
|
1344
|
+
for (const character of value) {
|
|
1345
|
+
const characterBytes = Buffer.byteLength(character);
|
|
1346
|
+
if (bytes + characterBytes > maxBytes) break;
|
|
1347
|
+
result += character;
|
|
1348
|
+
bytes += characterBytes;
|
|
1349
|
+
}
|
|
1350
|
+
return result;
|
|
1351
|
+
}
|
|
1352
|
+
__name(truncateUtf8, "truncateUtf8");
|
|
1353
|
+
function deduplicatedName(name, suffix) {
|
|
1354
|
+
if (suffix === 0) return name;
|
|
1355
|
+
const extension = path.extname(name);
|
|
1356
|
+
const suffixText = ` (${suffix})`;
|
|
1357
|
+
const stem = name.slice(0, -extension.length || void 0);
|
|
1358
|
+
const extensionBudget = SESSION_ATTACHMENT_MAX_NAME_BYTES - Buffer.byteLength(suffixText) - 1;
|
|
1359
|
+
const safeExtension = truncateUtf8(extension, extensionBudget).replace(
|
|
1360
|
+
/[. ]+$/u,
|
|
1361
|
+
""
|
|
1362
|
+
);
|
|
1363
|
+
const stemBudget = SESSION_ATTACHMENT_MAX_NAME_BYTES - Buffer.byteLength(suffixText) - Buffer.byteLength(safeExtension);
|
|
1364
|
+
return `${truncateUtf8(stem, stemBudget)}${suffixText}${safeExtension}`;
|
|
1365
|
+
}
|
|
1366
|
+
__name(deduplicatedName, "deduplicatedName");
|
|
1367
|
+
function imageName(mimeType) {
|
|
1368
|
+
const extension = mimeType.slice("image/".length).split(/[;+]/, 1)[0];
|
|
1369
|
+
return `image.${extension === "jpg" ? "jpeg" : extension || "img"}`;
|
|
1370
|
+
}
|
|
1371
|
+
__name(imageName, "imageName");
|
|
1372
|
+
function mimeTypeForName(name) {
|
|
1373
|
+
if ([".ts", ".mts", ".cts", ".tsx"].includes(path.extname(name).toLowerCase())) {
|
|
1374
|
+
return "text/plain";
|
|
1375
|
+
}
|
|
1376
|
+
return getSpecificMimeType(name) ?? "application/octet-stream";
|
|
1377
|
+
}
|
|
1378
|
+
__name(mimeTypeForName, "mimeTypeForName");
|
|
1379
|
+
function isSupportedImageMimeType(mimeType) {
|
|
1380
|
+
return SUPPORTED_IMAGE_MIME_TYPES.has(mimeType);
|
|
1381
|
+
}
|
|
1382
|
+
__name(isSupportedImageMimeType, "isSupportedImageMimeType");
|
|
1383
|
+
function isTextMimeType(mimeType) {
|
|
1384
|
+
return mimeType.startsWith("text/") || mimeType === "application/json" || mimeType.endsWith("+json") || mimeType === "application/xml" || mimeType.endsWith("+xml") || mimeType === "application/javascript" || mimeType === "application/typescript" || mimeType === "application/yaml" || mimeType === "application/x-yaml" || mimeType === "application/toml";
|
|
1385
|
+
}
|
|
1386
|
+
__name(isTextMimeType, "isTextMimeType");
|
|
1387
|
+
function isTextAttachment(data, mimeType) {
|
|
1388
|
+
if (isTextMimeType(mimeType)) return true;
|
|
1389
|
+
if (mimeType.startsWith("image/") || mimeType.startsWith("audio/") || mimeType.startsWith("video/") || mimeType.startsWith("font/") || mimeType === "application/pdf" || data.includes(0)) {
|
|
1390
|
+
return false;
|
|
1391
|
+
}
|
|
1392
|
+
return Buffer.from(data.toString("utf8"), "utf8").equals(data);
|
|
1393
|
+
}
|
|
1394
|
+
__name(isTextAttachment, "isTextAttachment");
|
|
1395
|
+
function withAttachmentDegradationMarker(blocks) {
|
|
1396
|
+
for (let i = blocks.length - 1; i >= 0; i--) {
|
|
1397
|
+
const block = blocks[i];
|
|
1398
|
+
if (block.type === "text") {
|
|
1399
|
+
if (block.text.endsWith(SESSION_ATTACHMENT_UNAVAILABLE_TEXT)) {
|
|
1400
|
+
return [...blocks];
|
|
1401
|
+
}
|
|
1402
|
+
const next = [...blocks];
|
|
1403
|
+
next[i] = {
|
|
1404
|
+
type: "text",
|
|
1405
|
+
text: `${block.text}
|
|
1406
|
+
${SESSION_ATTACHMENT_UNAVAILABLE_TEXT}`
|
|
1407
|
+
};
|
|
1408
|
+
return next;
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
return [
|
|
1412
|
+
...blocks,
|
|
1413
|
+
{ type: "text", text: SESSION_ATTACHMENT_UNAVAILABLE_TEXT }
|
|
1414
|
+
];
|
|
1415
|
+
}
|
|
1416
|
+
__name(withAttachmentDegradationMarker, "withAttachmentDegradationMarker");
|
|
1417
|
+
var SessionAttachmentStore = class {
|
|
1418
|
+
constructor(directoryRoot, sessionId) {
|
|
1419
|
+
this.directoryRoot = directoryRoot;
|
|
1420
|
+
if (!directoryRoot || !sessionId) return;
|
|
1421
|
+
this.persistentDirectory = path.join(
|
|
1422
|
+
directoryRoot,
|
|
1423
|
+
`session-${encodeURIComponent(sessionId)}`
|
|
1424
|
+
);
|
|
1425
|
+
}
|
|
1426
|
+
static {
|
|
1427
|
+
__name(this, "SessionAttachmentStore");
|
|
1428
|
+
}
|
|
1429
|
+
directoryPromise;
|
|
1430
|
+
persistentDirectory;
|
|
1431
|
+
activeDirectory;
|
|
1432
|
+
pendingItems = 0;
|
|
1433
|
+
pendingNames = /* @__PURE__ */ new Map();
|
|
1434
|
+
pendingDrainWaiters = [];
|
|
1435
|
+
copyDrainWaiters = [];
|
|
1436
|
+
copying = false;
|
|
1437
|
+
closing = false;
|
|
1438
|
+
closed = false;
|
|
1439
|
+
async putAttachment(data, mimeType, name) {
|
|
1440
|
+
const isImage = isSupportedImageMimeType(mimeType);
|
|
1441
|
+
const safeName = safeAttachmentName(
|
|
1442
|
+
name ?? (isImage ? imageName(mimeType) : "")
|
|
1443
|
+
);
|
|
1444
|
+
if (!safeName) {
|
|
1445
|
+
throw new TypeError("Session attachment name is invalid");
|
|
1446
|
+
}
|
|
1447
|
+
const storedMimeType = mimeTypeForName(safeName);
|
|
1448
|
+
if (isImage && storedMimeType !== mimeType || isSupportedImageMimeType(storedMimeType) && !isImage) {
|
|
1449
|
+
throw new TypeError("Attachment name and Content-Type do not match");
|
|
1450
|
+
}
|
|
1451
|
+
if (this.closed || this.closing) {
|
|
1452
|
+
throw new Error("Session attachment store is closed");
|
|
1453
|
+
}
|
|
1454
|
+
if (this.copying) throw new Error("Session attachments are being copied");
|
|
1455
|
+
if (isImage && data.byteLength === 0 || data.byteLength > SESSION_ATTACHMENT_MAX_ITEM_BYTES) {
|
|
1456
|
+
throw new RangeError(
|
|
1457
|
+
`Session attachment must be at most ${SESSION_ATTACHMENT_MAX_ITEM_BYTES} bytes and images cannot be empty`
|
|
1458
|
+
);
|
|
1459
|
+
}
|
|
1460
|
+
let filePath;
|
|
1461
|
+
let pendingName = safeName;
|
|
1462
|
+
let removeFileOnFailure = false;
|
|
1463
|
+
this.pendingItems += 1;
|
|
1464
|
+
this.reservePendingName(safeName);
|
|
1465
|
+
try {
|
|
1466
|
+
const directory = await this.directory();
|
|
1467
|
+
let suffix = 0;
|
|
1468
|
+
for (; ; ) {
|
|
1469
|
+
const candidateName = deduplicatedName(safeName, suffix);
|
|
1470
|
+
if (safeAttachmentName(candidateName) !== candidateName) {
|
|
1471
|
+
throw new TypeError("Session attachment name is invalid");
|
|
1472
|
+
}
|
|
1473
|
+
if (pendingName !== candidateName) {
|
|
1474
|
+
if (pendingName) this.releasePendingName(pendingName);
|
|
1475
|
+
this.reservePendingName(candidateName);
|
|
1476
|
+
pendingName = candidateName;
|
|
1477
|
+
}
|
|
1478
|
+
filePath = path.join(directory, candidateName);
|
|
1479
|
+
removeFileOnFailure = true;
|
|
1480
|
+
try {
|
|
1481
|
+
await fs.writeFile(filePath, data, { flag: "wx" });
|
|
1482
|
+
break;
|
|
1483
|
+
} catch (error) {
|
|
1484
|
+
if (error.code !== "EEXIST") throw error;
|
|
1485
|
+
removeFileOnFailure = false;
|
|
1486
|
+
this.releasePendingName(candidateName);
|
|
1487
|
+
pendingName = void 0;
|
|
1488
|
+
filePath = void 0;
|
|
1489
|
+
suffix += 1;
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
if (this.closed || this.closing) {
|
|
1493
|
+
throw new Error("Session attachment store is closed");
|
|
1494
|
+
}
|
|
1495
|
+
const name2 = path.basename(filePath);
|
|
1496
|
+
const storedMimeType2 = mimeTypeForName(name2);
|
|
1497
|
+
const reference = {
|
|
1498
|
+
type: isSupportedImageMimeType(storedMimeType2) ? "image" : "resource",
|
|
1499
|
+
attachmentId: name2,
|
|
1500
|
+
mimeType: storedMimeType2,
|
|
1501
|
+
size: data.byteLength
|
|
1502
|
+
};
|
|
1503
|
+
return reference;
|
|
1504
|
+
} catch (error) {
|
|
1505
|
+
if (removeFileOnFailure && filePath) {
|
|
1506
|
+
await fs.rm(filePath, { force: true }).catch(() => {
|
|
1507
|
+
});
|
|
1508
|
+
}
|
|
1509
|
+
throw error;
|
|
1510
|
+
} finally {
|
|
1511
|
+
if (pendingName) this.releasePendingName(pendingName);
|
|
1512
|
+
if (!this.closed) {
|
|
1513
|
+
this.pendingItems -= 1;
|
|
1514
|
+
if (this.pendingItems === 0) {
|
|
1515
|
+
this.resolvePendingDrainWaiters();
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
// Validate one block against the store. Ordinary ACP content passes through
|
|
1521
|
+
// untouched, matching `assertReferences`.
|
|
1522
|
+
assertReference(block) {
|
|
1523
|
+
if (!block || typeof block !== "object" || Array.isArray(block) || !("attachmentId" in block)) {
|
|
1524
|
+
return;
|
|
1525
|
+
}
|
|
1526
|
+
if (!isSessionAttachmentReference(block)) {
|
|
1527
|
+
throw new SessionAttachmentReferenceError(
|
|
1528
|
+
"Invalid session attachment reference",
|
|
1529
|
+
"invalid_session_attachment_reference"
|
|
1530
|
+
);
|
|
1531
|
+
}
|
|
1532
|
+
this.assertStored(block);
|
|
1533
|
+
}
|
|
1534
|
+
assertReferences(content) {
|
|
1535
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
1536
|
+
for (const block of content) {
|
|
1537
|
+
if (!block || typeof block !== "object" || Array.isArray(block) || !("attachmentId" in block)) {
|
|
1538
|
+
continue;
|
|
1539
|
+
}
|
|
1540
|
+
if (!isSessionAttachmentReference(block)) {
|
|
1541
|
+
throw new SessionAttachmentReferenceError(
|
|
1542
|
+
"Invalid session attachment reference",
|
|
1543
|
+
"invalid_session_attachment_reference"
|
|
1544
|
+
);
|
|
1545
|
+
}
|
|
1546
|
+
const id = block.attachmentId;
|
|
1547
|
+
if (seenIds.has(id)) {
|
|
1548
|
+
throw new SessionAttachmentReferenceError(
|
|
1549
|
+
`Session attachment referenced more than once: ${id}`,
|
|
1550
|
+
"invalid_session_attachment_reference"
|
|
1551
|
+
);
|
|
1552
|
+
}
|
|
1553
|
+
seenIds.add(id);
|
|
1554
|
+
this.assertStored(block);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
async resolveContent(content, memo) {
|
|
1558
|
+
const pendingById = memo ?? /* @__PURE__ */ new Map();
|
|
1559
|
+
return await Promise.all(
|
|
1560
|
+
content.map(async (block) => {
|
|
1561
|
+
if (!isSessionAttachmentReference(block)) return block;
|
|
1562
|
+
const id = block.attachmentId;
|
|
1563
|
+
let pending = pendingById.get(id);
|
|
1564
|
+
if (!pending) {
|
|
1565
|
+
const created = this.resolve(block);
|
|
1566
|
+
pendingById.set(id, created);
|
|
1567
|
+
void created.catch(() => {
|
|
1568
|
+
if (pendingById.get(id) === created) {
|
|
1569
|
+
pendingById.delete(id);
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1572
|
+
pending = created;
|
|
1573
|
+
}
|
|
1574
|
+
return await pending;
|
|
1575
|
+
})
|
|
1576
|
+
);
|
|
1577
|
+
}
|
|
1578
|
+
// Per-block variant of `resolveContent` for degrade paths: one unresolvable
|
|
1579
|
+
// reference drops only itself, keeping the sibling blocks a wholesale
|
|
1580
|
+
// fallback would discard. Other errors still propagate.
|
|
1581
|
+
async resolveContentDegrading(content, memo) {
|
|
1582
|
+
const retainedBlocks = [];
|
|
1583
|
+
const resolvedBlocks = [];
|
|
1584
|
+
let degraded = 0;
|
|
1585
|
+
for (const block of content) {
|
|
1586
|
+
if (!isSessionAttachmentReference(block)) {
|
|
1587
|
+
retainedBlocks.push(block);
|
|
1588
|
+
resolvedBlocks.push(block);
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1591
|
+
try {
|
|
1592
|
+
const [resolved] = await this.resolveContent([block], memo);
|
|
1593
|
+
if (resolved) resolvedBlocks.push(resolved);
|
|
1594
|
+
retainedBlocks.push(block);
|
|
1595
|
+
} catch (error) {
|
|
1596
|
+
if (!(error instanceof SessionAttachmentReferenceError)) throw error;
|
|
1597
|
+
degraded += 1;
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
return { retainedBlocks, resolvedBlocks, degraded };
|
|
1601
|
+
}
|
|
1602
|
+
async read(attachmentId) {
|
|
1603
|
+
const name = safeAttachmentName(attachmentId);
|
|
1604
|
+
if (!name || name !== attachmentId) return void 0;
|
|
1605
|
+
const filePath = path.join(await this.directory(), name);
|
|
1606
|
+
try {
|
|
1607
|
+
return {
|
|
1608
|
+
data: await fs.readFile(filePath),
|
|
1609
|
+
mimeType: mimeTypeForName(name)
|
|
1610
|
+
};
|
|
1611
|
+
} catch (error) {
|
|
1612
|
+
if (error.code === "ENOENT") {
|
|
1613
|
+
return void 0;
|
|
1614
|
+
}
|
|
1615
|
+
throw error;
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
async copyFrom(source) {
|
|
1619
|
+
if (source === this) return;
|
|
1620
|
+
if (this.closed || this.closing) {
|
|
1621
|
+
throw new Error("Session attachment store is closed");
|
|
1622
|
+
}
|
|
1623
|
+
if (this.copying) throw new Error("Session attachments are being copied");
|
|
1624
|
+
if (source.closed || source.closing) {
|
|
1625
|
+
throw new Error("Session attachment store is closed");
|
|
1626
|
+
}
|
|
1627
|
+
if (source.copying) {
|
|
1628
|
+
throw new Error("Session attachments are being copied");
|
|
1629
|
+
}
|
|
1630
|
+
this.copying = true;
|
|
1631
|
+
source.copying = true;
|
|
1632
|
+
try {
|
|
1633
|
+
if (this.pendingItems > 0) {
|
|
1634
|
+
await new Promise(
|
|
1635
|
+
(resolve2) => this.pendingDrainWaiters.push(resolve2)
|
|
1636
|
+
);
|
|
1637
|
+
}
|
|
1638
|
+
if (source.pendingItems > 0) {
|
|
1639
|
+
await new Promise(
|
|
1640
|
+
(resolve2) => source.pendingDrainWaiters.push(resolve2)
|
|
1641
|
+
);
|
|
1642
|
+
}
|
|
1643
|
+
if (this.closed) throw new Error("Session attachment store is closed");
|
|
1644
|
+
const sourceDirectory = source.persistentDirectory ?? source.activeDirectory;
|
|
1645
|
+
if (!sourceDirectory) return;
|
|
1646
|
+
let entries;
|
|
1647
|
+
try {
|
|
1648
|
+
entries = await fs.readdir(sourceDirectory, { withFileTypes: true });
|
|
1649
|
+
} catch (error) {
|
|
1650
|
+
if (error.code === "ENOENT") return;
|
|
1651
|
+
throw error;
|
|
1652
|
+
}
|
|
1653
|
+
const targetDirectory = await this.directory();
|
|
1654
|
+
await Promise.all(
|
|
1655
|
+
entries.filter(
|
|
1656
|
+
(entry) => entry.isFile() && !source.pendingNames.has(entry.name)
|
|
1657
|
+
).map(async (entry) => {
|
|
1658
|
+
const sourcePath = path.join(sourceDirectory, entry.name);
|
|
1659
|
+
try {
|
|
1660
|
+
await fs.copyFile(
|
|
1661
|
+
sourcePath,
|
|
1662
|
+
path.join(targetDirectory, entry.name)
|
|
1663
|
+
);
|
|
1664
|
+
} catch (error) {
|
|
1665
|
+
if (error.code === "ENOENT") {
|
|
1666
|
+
try {
|
|
1667
|
+
await fs.stat(sourcePath);
|
|
1668
|
+
} catch (sourceError) {
|
|
1669
|
+
if (sourceError.code === "ENOENT") {
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
throw error;
|
|
1675
|
+
}
|
|
1676
|
+
})
|
|
1677
|
+
);
|
|
1678
|
+
} finally {
|
|
1679
|
+
source.copying = false;
|
|
1680
|
+
this.copying = false;
|
|
1681
|
+
source.resolveCopyDrainWaiters();
|
|
1682
|
+
this.resolveCopyDrainWaiters();
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
async remove(attachmentId) {
|
|
1686
|
+
const name = safeAttachmentName(attachmentId);
|
|
1687
|
+
if (!name || name !== attachmentId || this.copying || this.pendingNames.has(name)) {
|
|
1688
|
+
return false;
|
|
1689
|
+
}
|
|
1690
|
+
const directory = await this.directory();
|
|
1691
|
+
const filePath = path.join(directory, name);
|
|
1692
|
+
try {
|
|
1693
|
+
await fs.unlink(filePath);
|
|
1694
|
+
return true;
|
|
1695
|
+
} catch (error) {
|
|
1696
|
+
if (error.code === "ENOENT") {
|
|
1697
|
+
return false;
|
|
1698
|
+
}
|
|
1699
|
+
throw error;
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
async close() {
|
|
1703
|
+
this.closing = true;
|
|
1704
|
+
await this.waitForCopy();
|
|
1705
|
+
if (this.closed) return;
|
|
1706
|
+
this.closed = true;
|
|
1707
|
+
this.pendingItems = 0;
|
|
1708
|
+
this.pendingNames.clear();
|
|
1709
|
+
this.resolvePendingDrainWaiters();
|
|
1710
|
+
if (this.persistentDirectory || !this.directoryPromise) return;
|
|
1711
|
+
const directory = await this.directoryPromise.catch(() => void 0);
|
|
1712
|
+
if (!directory) return;
|
|
1713
|
+
await fs.rm(directory, { recursive: true, force: true });
|
|
1714
|
+
}
|
|
1715
|
+
async delete() {
|
|
1716
|
+
this.closing = true;
|
|
1717
|
+
await this.waitForCopy();
|
|
1718
|
+
if (!this.closed) {
|
|
1719
|
+
this.closed = true;
|
|
1720
|
+
this.pendingItems = 0;
|
|
1721
|
+
this.pendingNames.clear();
|
|
1722
|
+
this.resolvePendingDrainWaiters();
|
|
1723
|
+
}
|
|
1724
|
+
const directory = this.persistentDirectory ?? await this.directoryPromise?.catch(() => void 0);
|
|
1725
|
+
if (directory) await fs.rm(directory, { recursive: true, force: true });
|
|
1726
|
+
}
|
|
1727
|
+
assertStored(reference) {
|
|
1728
|
+
const id = reference.attachmentId;
|
|
1729
|
+
const name = safeAttachmentName(id);
|
|
1730
|
+
let size;
|
|
1731
|
+
const directory = this.persistentDirectory ?? this.activeDirectory;
|
|
1732
|
+
if (name && name === id && directory) {
|
|
1733
|
+
try {
|
|
1734
|
+
size = statSync(path.join(directory, name)).size;
|
|
1735
|
+
} catch {
|
|
1736
|
+
size = void 0;
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
const storedMimeType = name ? mimeTypeForName(name) : void 0;
|
|
1740
|
+
const storedType = storedMimeType ? isSupportedImageMimeType(storedMimeType) ? "image" : "resource" : void 0;
|
|
1741
|
+
if (size !== reference.size || storedMimeType !== reference.mimeType || storedType !== reference.type) {
|
|
1742
|
+
throw new SessionAttachmentReferenceError(
|
|
1743
|
+
`Unknown or unavailable session attachment: ${id}`,
|
|
1744
|
+
"session_attachment_gone"
|
|
1745
|
+
);
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
releasePendingName(name) {
|
|
1749
|
+
const count = this.pendingNames.get(name) ?? 0;
|
|
1750
|
+
if (count <= 1) this.pendingNames.delete(name);
|
|
1751
|
+
else this.pendingNames.set(name, count - 1);
|
|
1752
|
+
}
|
|
1753
|
+
reservePendingName(name) {
|
|
1754
|
+
this.pendingNames.set(name, (this.pendingNames.get(name) ?? 0) + 1);
|
|
1755
|
+
}
|
|
1756
|
+
async waitForCopy() {
|
|
1757
|
+
while (this.copying) {
|
|
1758
|
+
await new Promise((resolve2) => this.copyDrainWaiters.push(resolve2));
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
resolveCopyDrainWaiters() {
|
|
1762
|
+
for (const resolve2 of this.copyDrainWaiters.splice(0)) resolve2();
|
|
1763
|
+
}
|
|
1764
|
+
resolvePendingDrainWaiters() {
|
|
1765
|
+
for (const resolve2 of this.pendingDrainWaiters.splice(0)) resolve2();
|
|
1766
|
+
}
|
|
1767
|
+
async resolve(reference) {
|
|
1768
|
+
const id = reference.attachmentId;
|
|
1769
|
+
const attachment = await this.read(id);
|
|
1770
|
+
if (!attachment) {
|
|
1771
|
+
throw new SessionAttachmentReferenceError(
|
|
1772
|
+
`Unknown or unavailable session attachment: ${id}`,
|
|
1773
|
+
"session_attachment_gone"
|
|
1774
|
+
);
|
|
1775
|
+
}
|
|
1776
|
+
if (reference.type === "resource") {
|
|
1777
|
+
const resource = {
|
|
1778
|
+
uri: `attachment:///${encodeURIComponent(reference.attachmentId)}`,
|
|
1779
|
+
mimeType: attachment.mimeType,
|
|
1780
|
+
...isTextAttachment(attachment.data, attachment.mimeType) ? { text: attachment.data.toString("utf8") } : { blob: attachment.data.toString("base64") }
|
|
1781
|
+
};
|
|
1782
|
+
return {
|
|
1783
|
+
type: "resource",
|
|
1784
|
+
resource
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
return {
|
|
1788
|
+
type: "image",
|
|
1789
|
+
data: attachment.data.toString("base64"),
|
|
1790
|
+
mimeType: attachment.mimeType
|
|
1791
|
+
};
|
|
1792
|
+
}
|
|
1793
|
+
async directory() {
|
|
1794
|
+
if (!this.directoryPromise) {
|
|
1795
|
+
const pending = this.persistentDirectory ? fs.mkdir(this.persistentDirectory, {
|
|
1796
|
+
recursive: true,
|
|
1797
|
+
mode: 448
|
|
1798
|
+
}).then(() => this.persistentDirectory) : this.directoryRoot ? fs.mkdir(this.directoryRoot, { recursive: true, mode: 448 }).then(
|
|
1799
|
+
() => fs.mkdtemp(
|
|
1800
|
+
path.join(this.directoryRoot, "session-attachment-")
|
|
1801
|
+
)
|
|
1802
|
+
) : fs.mkdtemp(path.join(tmpdir(), "qwen-session-attachment-"));
|
|
1803
|
+
const directoryPromise = pending.then((directory) => {
|
|
1804
|
+
this.activeDirectory = directory;
|
|
1805
|
+
return directory;
|
|
1806
|
+
});
|
|
1807
|
+
this.directoryPromise = directoryPromise;
|
|
1808
|
+
void directoryPromise.catch(() => {
|
|
1809
|
+
if (this.directoryPromise === directoryPromise)
|
|
1810
|
+
this.directoryPromise = void 0;
|
|
1811
|
+
});
|
|
1812
|
+
}
|
|
1813
|
+
return await this.directoryPromise;
|
|
1814
|
+
}
|
|
1815
|
+
};
|
|
1816
|
+
|
|
1283
1817
|
// packages/acp-bridge/src/internal/stderrLine.ts
|
|
1284
1818
|
init_esbuild_shims();
|
|
1285
1819
|
function writeStderrLine(message) {
|
|
@@ -1291,8 +1825,8 @@ __name(writeStderrLine, "writeStderrLine");
|
|
|
1291
1825
|
// packages/acp-bridge/src/bridgeClient.ts
|
|
1292
1826
|
init_esbuild_shims();
|
|
1293
1827
|
import { randomUUID } from "node:crypto";
|
|
1294
|
-
import { promises as
|
|
1295
|
-
import * as
|
|
1828
|
+
import { promises as fs2 } from "node:fs";
|
|
1829
|
+
import * as path2 from "node:path";
|
|
1296
1830
|
|
|
1297
1831
|
// packages/acp-bridge/src/daemonEventTypes.ts
|
|
1298
1832
|
init_esbuild_shims();
|
|
@@ -2278,7 +2812,7 @@ function sliceLineRange(content, startLine, endLine) {
|
|
|
2278
2812
|
}
|
|
2279
2813
|
__name(sliceLineRange, "sliceLineRange");
|
|
2280
2814
|
var BridgeClient = class {
|
|
2281
|
-
constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender, ownsSession = () => true, onTokenUsage, onCreateSubSession, onGenerationEvent, onWorkspaceGenerationEvent, onChannelDelivery, hasSessionSpawnInFlight = () => false, getLiveScreenContextCaptureHandler = () => void 0, getLiveTaskToolRequestHandler = () => void 0, getLiveSpeakToUserHandler = () => void 0, externalToolGuard, onActiveWork) {
|
|
2815
|
+
constructor(resolveEntry, resolvePendingRestoreEvents, mediator, permissionTimeoutMs, maxPendingPerSession, fileSystem, onModelPromoted, onModePromoted, clientMcpSender, ownsSession = () => true, onTokenUsage, onCreateSubSession, onGenerationEvent, onWorkspaceGenerationEvent, onChannelDelivery, hasSessionSpawnInFlight = () => false, getLiveScreenContextCaptureHandler = () => void 0, getLiveTaskToolRequestHandler = () => void 0, getLiveSpeakToUserHandler = () => void 0, externalToolGuard, onActiveWork, onSessionCatalogChanged, onGoalTurnEnded) {
|
|
2282
2816
|
this.resolveEntry = resolveEntry;
|
|
2283
2817
|
this.resolvePendingRestoreEvents = resolvePendingRestoreEvents;
|
|
2284
2818
|
this.mediator = mediator;
|
|
@@ -2300,6 +2834,8 @@ var BridgeClient = class {
|
|
|
2300
2834
|
this.getLiveSpeakToUserHandler = getLiveSpeakToUserHandler;
|
|
2301
2835
|
this.externalToolGuard = externalToolGuard;
|
|
2302
2836
|
this.onActiveWork = onActiveWork;
|
|
2837
|
+
this.onSessionCatalogChanged = onSessionCatalogChanged;
|
|
2838
|
+
this.onGoalTurnEnded = onGoalTurnEnded;
|
|
2303
2839
|
}
|
|
2304
2840
|
static {
|
|
2305
2841
|
__name(this, "BridgeClient");
|
|
@@ -2564,8 +3100,8 @@ var BridgeClient = class {
|
|
|
2564
3100
|
* between tool batches to pull any messages the browser queued mid-turn. We splice the per-session
|
|
2565
3101
|
* queue, return them to the child as the response, and — when non-empty —
|
|
2566
3102
|
* publish a `mid_turn_message_injected` SSE frame so the browser can move
|
|
2567
|
-
* those messages out of its pending queue
|
|
2568
|
-
*
|
|
3103
|
+
* those messages out of its pending queue and render the immediate echo.
|
|
3104
|
+
* Unknown methods reject with ACP `methodNotFound` (-32601), matching
|
|
2569
3105
|
* the SDK's
|
|
2570
3106
|
* default for an unimplemented client surface; the child's drain caller
|
|
2571
3107
|
* treats that as "drain unsupported" and stops asking.
|
|
@@ -2599,9 +3135,12 @@ var BridgeClient = class {
|
|
|
2599
3135
|
throw RequestError.methodNotFound(method);
|
|
2600
3136
|
}
|
|
2601
3137
|
const sessionId = typeof params["sessionId"] === "string" ? params["sessionId"] : void 0;
|
|
2602
|
-
if (!sessionId) return { messages: [], hasQueuedPrompt: false };
|
|
3138
|
+
if (!sessionId) return { messages: [], items: [], hasQueuedPrompt: false };
|
|
3139
|
+
if (!this.ownsSession(sessionId)) {
|
|
3140
|
+
return { messages: [], items: [], hasQueuedPrompt: false };
|
|
3141
|
+
}
|
|
2603
3142
|
const entry = this.resolveEntry(sessionId);
|
|
2604
|
-
if (!entry) return { messages: [], hasQueuedPrompt: false };
|
|
3143
|
+
if (!entry) return { messages: [], items: [], hasQueuedPrompt: false };
|
|
2605
3144
|
const drained = entry.midTurnMessageQueue.splice(0);
|
|
2606
3145
|
if (drained.length > 0) {
|
|
2607
3146
|
for (const item of drained) {
|
|
@@ -2614,6 +3153,67 @@ var BridgeClient = class {
|
|
|
2614
3153
|
);
|
|
2615
3154
|
}
|
|
2616
3155
|
}
|
|
3156
|
+
const attachmentMemo = /* @__PURE__ */ new Map();
|
|
3157
|
+
const serializedAttachmentIds = /* @__PURE__ */ new Set();
|
|
3158
|
+
const items = [];
|
|
3159
|
+
try {
|
|
3160
|
+
for (const item of drained) {
|
|
3161
|
+
let degraded = 0;
|
|
3162
|
+
const planned = (item.content ?? []).filter((block) => {
|
|
3163
|
+
if (!isSessionAttachmentReference(block)) return true;
|
|
3164
|
+
if (serializedAttachmentIds.has(block.attachmentId)) {
|
|
3165
|
+
degraded += 1;
|
|
3166
|
+
return false;
|
|
3167
|
+
}
|
|
3168
|
+
serializedAttachmentIds.add(block.attachmentId);
|
|
3169
|
+
return true;
|
|
3170
|
+
});
|
|
3171
|
+
let resolvedBlocks;
|
|
3172
|
+
let attachmentReferences;
|
|
3173
|
+
try {
|
|
3174
|
+
resolvedBlocks = await entry.attachments.resolveContent(
|
|
3175
|
+
planned,
|
|
3176
|
+
attachmentMemo
|
|
3177
|
+
);
|
|
3178
|
+
attachmentReferences = planned.filter(isSessionAttachmentReference);
|
|
3179
|
+
} catch (error) {
|
|
3180
|
+
if (!(error instanceof SessionAttachmentReferenceError)) throw error;
|
|
3181
|
+
writeStderrLine(
|
|
3182
|
+
`[mid-turn] session=${JSON.stringify(entry.sessionId)} degraded attachment for message ${JSON.stringify(item.messageId)}: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
|
|
3183
|
+
);
|
|
3184
|
+
const perBlock = await entry.attachments.resolveContentDegrading(
|
|
3185
|
+
planned,
|
|
3186
|
+
attachmentMemo
|
|
3187
|
+
);
|
|
3188
|
+
resolvedBlocks = perBlock.resolvedBlocks;
|
|
3189
|
+
attachmentReferences = perBlock.retainedBlocks.filter(
|
|
3190
|
+
isSessionAttachmentReference
|
|
3191
|
+
);
|
|
3192
|
+
degraded += perBlock.degraded;
|
|
3193
|
+
}
|
|
3194
|
+
let content = [
|
|
3195
|
+
...item.text ? [{ type: "text", text: item.text }] : [],
|
|
3196
|
+
...resolvedBlocks
|
|
3197
|
+
];
|
|
3198
|
+
if (degraded > 0) content = withAttachmentDegradationMarker(content);
|
|
3199
|
+
items.push({
|
|
3200
|
+
messageId: item.messageId,
|
|
3201
|
+
displayText: item.text,
|
|
3202
|
+
content,
|
|
3203
|
+
...attachmentReferences.length > 0 ? { attachmentReferences } : {}
|
|
3204
|
+
});
|
|
3205
|
+
}
|
|
3206
|
+
} catch (error) {
|
|
3207
|
+
const requeued = new Set(drained.map((queued) => queued.messageId));
|
|
3208
|
+
const ring = entry.settledMidTurnMessageIds;
|
|
3209
|
+
const kept = ring.filter((id) => !requeued.has(id));
|
|
3210
|
+
ring.splice(0, ring.length, ...kept);
|
|
3211
|
+
entry.midTurnMessageQueue.unshift(...drained);
|
|
3212
|
+
writeStderrLine(
|
|
3213
|
+
`[mid-turn] session=${JSON.stringify(entry.sessionId)} drain failed, requeued ${drained.length} message(s): ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
|
|
3214
|
+
);
|
|
3215
|
+
throw error;
|
|
3216
|
+
}
|
|
2617
3217
|
const echoed = drained.filter((item) => !item.queueOnly);
|
|
2618
3218
|
const messages = drained.map((item) => item.text);
|
|
2619
3219
|
const hasQueuedPrompt = entry.pendingPromptList.some(
|
|
@@ -2626,14 +3226,21 @@ var BridgeClient = class {
|
|
|
2626
3226
|
data: {
|
|
2627
3227
|
sessionId: entry.sessionId,
|
|
2628
3228
|
messages: echoed.map((item) => item.text),
|
|
2629
|
-
messageIds: echoed.map((item) => item.messageId)
|
|
3229
|
+
messageIds: echoed.map((item) => item.messageId),
|
|
3230
|
+
// Carry the structured `items` twin (content blocks per message) so
|
|
3231
|
+
// the browser-side echo renderer can show attached images alongside
|
|
3232
|
+
// the message text. Older consumers that don't read this field keep
|
|
3233
|
+
// working unchanged.
|
|
3234
|
+
items: echoed.map((item) => ({
|
|
3235
|
+
...item.content && item.content.length > 0 ? { content: item.content } : {}
|
|
3236
|
+
}))
|
|
2630
3237
|
}
|
|
2631
3238
|
});
|
|
2632
3239
|
writeStderrLine(
|
|
2633
3240
|
published ? `[mid-turn] session=${entry.sessionId} drained=${messages.length} echoed=${echoed.length} injected into running turn` : `[mid-turn] session=${entry.sessionId} drained=${messages.length} echoed=${echoed.length} echo frame dropped (bus closed); reconciliation required`
|
|
2634
3241
|
);
|
|
2635
3242
|
}
|
|
2636
|
-
return { messages, hasQueuedPrompt };
|
|
3243
|
+
return { messages, items, hasQueuedPrompt };
|
|
2637
3244
|
}
|
|
2638
3245
|
async handleExternalToolGuardPrepare(params) {
|
|
2639
3246
|
if (!this.externalToolGuard) {
|
|
@@ -3022,7 +3629,7 @@ var BridgeClient = class {
|
|
|
3022
3629
|
* `qwen/notify/session/prompt-suggestion` (followup assist),
|
|
3023
3630
|
* `qwen/notify/session/artifact-event` (hook artifacts),
|
|
3024
3631
|
* `qwen/notify/session/terminal-sequence`, and
|
|
3025
|
-
* `_qwencode/end_turn` (background-notification turns), and
|
|
3632
|
+
* `_qwencode/end_turn` (background-notification and goal turns), and
|
|
3026
3633
|
* `qwen/notify/session/mcp-budget-event` — each translated into a
|
|
3027
3634
|
* session-scoped SSE frame. Unknown methods are dropped silently for
|
|
3028
3635
|
* forward-compat.
|
|
@@ -3045,14 +3652,39 @@ var BridgeClient = class {
|
|
|
3045
3652
|
}
|
|
3046
3653
|
return;
|
|
3047
3654
|
}
|
|
3655
|
+
if (method === "_qwencode/start_turn") {
|
|
3656
|
+
const sessionId2 = params["sessionId"];
|
|
3657
|
+
if (typeof sessionId2 !== "string" || sessionId2.length === 0 || params["source"] !== "goal") {
|
|
3658
|
+
return;
|
|
3659
|
+
}
|
|
3660
|
+
const entry = this.resolveEntry(sessionId2);
|
|
3661
|
+
if (!entry || !this.ownsSession(sessionId2)) return;
|
|
3662
|
+
entry.goalTurnActive = true;
|
|
3663
|
+
return;
|
|
3664
|
+
}
|
|
3048
3665
|
if (method === "_qwencode/end_turn") {
|
|
3049
3666
|
const sessionId2 = params["sessionId"];
|
|
3050
3667
|
const reason = params["reason"];
|
|
3051
|
-
|
|
3668
|
+
const source = params["source"];
|
|
3669
|
+
if (typeof sessionId2 !== "string" || sessionId2.length === 0 || typeof reason !== "string" || reason.length === 0 || reason.length > 128 || source !== "background_notification" && source !== "goal") {
|
|
3052
3670
|
return;
|
|
3053
3671
|
}
|
|
3054
3672
|
const entry = this.resolveEntry(sessionId2);
|
|
3055
3673
|
if (!entry || !this.ownsSession(sessionId2)) return;
|
|
3674
|
+
if (source === "goal") {
|
|
3675
|
+
entry.goalTurnActive = false;
|
|
3676
|
+
this.onGoalTurnEnded?.(sessionId2);
|
|
3677
|
+
const promptId = params["promptId"];
|
|
3678
|
+
if (typeof promptId !== "string" || promptId.length === 0 || promptId.length > 256) {
|
|
3679
|
+
return;
|
|
3680
|
+
}
|
|
3681
|
+
entry.events.publish({
|
|
3682
|
+
type: "turn_complete",
|
|
3683
|
+
promptId,
|
|
3684
|
+
data: { sessionId: sessionId2, stopReason: reason, promptId }
|
|
3685
|
+
});
|
|
3686
|
+
return;
|
|
3687
|
+
}
|
|
3056
3688
|
entry.events.publish({
|
|
3057
3689
|
type: "background_notification_turn_complete",
|
|
3058
3690
|
data: { sessionId: sessionId2, reason }
|
|
@@ -3160,6 +3792,7 @@ var BridgeClient = class {
|
|
|
3160
3792
|
return;
|
|
3161
3793
|
const entry = this.resolveEntry(sessionId2);
|
|
3162
3794
|
if (!entry) return;
|
|
3795
|
+
this.onSessionCatalogChanged?.();
|
|
3163
3796
|
try {
|
|
3164
3797
|
entry.events.publish({
|
|
3165
3798
|
type: "session_metadata_updated",
|
|
@@ -3291,6 +3924,13 @@ var BridgeClient = class {
|
|
|
3291
3924
|
async upsertAndPublishArtifacts(entry, artifacts, options, turn = {}) {
|
|
3292
3925
|
try {
|
|
3293
3926
|
const result = await entry.artifacts.upsertMany(artifacts, options);
|
|
3927
|
+
for (const warning of result.warnings ?? []) {
|
|
3928
|
+
writeStderrLine(
|
|
3929
|
+
`[artifacts] session=${entry.sessionId} action=warning reason=${JSON.stringify(
|
|
3930
|
+
warning
|
|
3931
|
+
)}`
|
|
3932
|
+
);
|
|
3933
|
+
}
|
|
3294
3934
|
this.publishArtifactChanges(entry, result.changes, turn);
|
|
3295
3935
|
} catch (error) {
|
|
3296
3936
|
writeStderrLine(
|
|
@@ -3602,20 +4242,20 @@ var BridgeClient = class {
|
|
|
3602
4242
|
}
|
|
3603
4243
|
let realTarget = params.path;
|
|
3604
4244
|
try {
|
|
3605
|
-
realTarget = await
|
|
4245
|
+
realTarget = await fs2.realpath(params.path);
|
|
3606
4246
|
} catch (err) {
|
|
3607
4247
|
const code = err && typeof err === "object" && "code" in err ? err.code : void 0;
|
|
3608
4248
|
if (code !== "ENOENT") throw err;
|
|
3609
4249
|
try {
|
|
3610
|
-
const linkTarget = await
|
|
3611
|
-
realTarget =
|
|
4250
|
+
const linkTarget = await fs2.readlink(params.path);
|
|
4251
|
+
realTarget = path2.resolve(path2.dirname(params.path), linkTarget);
|
|
3612
4252
|
} catch {
|
|
3613
4253
|
}
|
|
3614
4254
|
}
|
|
3615
4255
|
const tmp = `${realTarget}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
|
|
3616
4256
|
let preserveMode;
|
|
3617
4257
|
try {
|
|
3618
|
-
const targetStat = await
|
|
4258
|
+
const targetStat = await fs2.stat(realTarget);
|
|
3619
4259
|
preserveMode = {
|
|
3620
4260
|
mode: targetStat.mode & 4095,
|
|
3621
4261
|
uid: targetStat.uid,
|
|
@@ -3626,20 +4266,20 @@ var BridgeClient = class {
|
|
|
3626
4266
|
if (code !== "ENOENT") throw err;
|
|
3627
4267
|
}
|
|
3628
4268
|
try {
|
|
3629
|
-
await
|
|
4269
|
+
await fs2.writeFile(tmp, params.content, {
|
|
3630
4270
|
encoding: "utf8",
|
|
3631
4271
|
flag: "wx",
|
|
3632
4272
|
mode: preserveMode?.mode ?? 384
|
|
3633
4273
|
});
|
|
3634
4274
|
if (preserveMode) {
|
|
3635
|
-
await
|
|
4275
|
+
await fs2.chmod(tmp, preserveMode.mode).catch(() => {
|
|
3636
4276
|
});
|
|
3637
|
-
await
|
|
4277
|
+
await fs2.chown(tmp, preserveMode.uid, preserveMode.gid).catch(() => {
|
|
3638
4278
|
});
|
|
3639
4279
|
}
|
|
3640
|
-
await
|
|
4280
|
+
await fs2.rename(tmp, realTarget);
|
|
3641
4281
|
} catch (err) {
|
|
3642
|
-
await
|
|
4282
|
+
await fs2.unlink(tmp).catch(() => {
|
|
3643
4283
|
});
|
|
3644
4284
|
throw err;
|
|
3645
4285
|
}
|
|
@@ -3669,7 +4309,7 @@ var BridgeClient = class {
|
|
|
3669
4309
|
);
|
|
3670
4310
|
}
|
|
3671
4311
|
const READ_FILE_SIZE_CAP = 100 * 1024 * 1024;
|
|
3672
|
-
const stats = await
|
|
4312
|
+
const stats = await fs2.stat(params.path);
|
|
3673
4313
|
if (!stats.isFile()) {
|
|
3674
4314
|
throw new Error(
|
|
3675
4315
|
`readTextFile: ${params.path} is not a regular file (reported as ${describeStatKind(stats)}). Pipe / device / proc-like inputs can produce unbounded data and aren't supported by the bridge fs proxy.`
|
|
@@ -3680,7 +4320,7 @@ var BridgeClient = class {
|
|
|
3680
4320
|
`readTextFile: ${params.path} is ${stats.size} bytes, exceeds the ${READ_FILE_SIZE_CAP}-byte daemon cap. Tail/grep externally and feed the relevant slice instead.`
|
|
3681
4321
|
);
|
|
3682
4322
|
}
|
|
3683
|
-
const content = await
|
|
4323
|
+
const content = await fs2.readFile(params.path, "utf8");
|
|
3684
4324
|
if (typeof params.line === "number" || typeof params.limit === "number") {
|
|
3685
4325
|
const startLine = params.line ?? 1;
|
|
3686
4326
|
const start = startLine > 0 ? startLine - 1 : 0;
|
|
@@ -3882,8 +4522,8 @@ var GenerationStreamQueue = class {
|
|
|
3882
4522
|
// packages/acp-bridge/src/sessionArtifacts.ts
|
|
3883
4523
|
init_esbuild_shims();
|
|
3884
4524
|
import { createHash } from "node:crypto";
|
|
3885
|
-
import { constants as fsConstants, promises as
|
|
3886
|
-
import
|
|
4525
|
+
import { constants as fsConstants, promises as fs3 } from "node:fs";
|
|
4526
|
+
import path3 from "node:path";
|
|
3887
4527
|
var SOURCE_RESERVATIONS = {
|
|
3888
4528
|
tool: 100,
|
|
3889
4529
|
client: 50,
|
|
@@ -3993,13 +4633,31 @@ var SessionArtifactStore = class {
|
|
|
3993
4633
|
const warningDetails = [];
|
|
3994
4634
|
for (const input of inputs) {
|
|
3995
4635
|
try {
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4636
|
+
const expanded = await this.expandWorkspaceDirectoryInput(input);
|
|
4637
|
+
for (const item of expanded.inputs) {
|
|
4638
|
+
try {
|
|
4639
|
+
normalizedResults.push(
|
|
4640
|
+
await this.normalizeInput(
|
|
4641
|
+
item,
|
|
4642
|
+
++this.receivedSeq,
|
|
4643
|
+
options.trustedPublisher === true
|
|
4644
|
+
)
|
|
4645
|
+
);
|
|
4646
|
+
} catch (error) {
|
|
4647
|
+
if (validationStrict) {
|
|
4648
|
+
throw error;
|
|
4649
|
+
}
|
|
4650
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4651
|
+
writeStderrLine(
|
|
4652
|
+
`[artifacts] session=${this.sessionId} action=dropped reason=${JSON.stringify(
|
|
4653
|
+
message
|
|
4654
|
+
)}`
|
|
4655
|
+
);
|
|
4656
|
+
}
|
|
4657
|
+
}
|
|
4658
|
+
if (expanded.warning) {
|
|
4659
|
+
warnings.push(expanded.warning);
|
|
4660
|
+
}
|
|
4003
4661
|
} catch (error) {
|
|
4004
4662
|
if (validationStrict) {
|
|
4005
4663
|
throw error;
|
|
@@ -4083,9 +4741,17 @@ var SessionArtifactStore = class {
|
|
|
4083
4741
|
const createdIds = new Set(
|
|
4084
4742
|
changes.filter((change) => change.action === "created").map((change) => change.artifactId)
|
|
4085
4743
|
);
|
|
4086
|
-
|
|
4087
|
-
|
|
4744
|
+
const overflowRemoved = await this.evictOverflow(
|
|
4745
|
+
createdIds,
|
|
4746
|
+
changes,
|
|
4747
|
+
persistenceStrict
|
|
4088
4748
|
);
|
|
4749
|
+
changes.push(...overflowRemoved.removed);
|
|
4750
|
+
if (overflowRemoved.droppedCreated > 0) {
|
|
4751
|
+
warnings.push(
|
|
4752
|
+
`dropped ${overflowRemoved.droppedCreated} newly created artifacts because the store is full`
|
|
4753
|
+
);
|
|
4754
|
+
}
|
|
4089
4755
|
const hasStrictDurableTransition = changes.some(
|
|
4090
4756
|
shouldCommitBeforeDurablePersistence
|
|
4091
4757
|
);
|
|
@@ -4371,9 +5037,9 @@ var SessionArtifactStore = class {
|
|
|
4371
5037
|
});
|
|
4372
5038
|
}
|
|
4373
5039
|
const evicted = await this.evictOverflow(/* @__PURE__ */ new Set(), []);
|
|
4374
|
-
if (evicted.length > 0) {
|
|
5040
|
+
if (evicted.removed.length > 0) {
|
|
4375
5041
|
warnings.push("restored artifact list pruned to live limit");
|
|
4376
|
-
warnings.push(...await this.persistChanges(evicted, false));
|
|
5042
|
+
warnings.push(...await this.persistChanges(evicted.removed, false));
|
|
4377
5043
|
}
|
|
4378
5044
|
this.setLastRestoreWarnings(warnings);
|
|
4379
5045
|
return warnings;
|
|
@@ -4758,6 +5424,171 @@ var SessionArtifactStore = class {
|
|
|
4758
5424
|
options?.clientId
|
|
4759
5425
|
);
|
|
4760
5426
|
}
|
|
5427
|
+
async expandWorkspaceDirectoryInput(input) {
|
|
5428
|
+
const workspacePath = typeof input.workspacePath === "string" ? input.workspacePath.trim() : void 0;
|
|
5429
|
+
if (!workspacePath) {
|
|
5430
|
+
return { inputs: [input] };
|
|
5431
|
+
}
|
|
5432
|
+
const realWorkspace = await this.getRealWorkspaceCwdForValidation();
|
|
5433
|
+
const normalizedPath = normalizeWorkspacePath(workspacePath, realWorkspace);
|
|
5434
|
+
const absolutePath = path3.resolve(realWorkspace, normalizedPath);
|
|
5435
|
+
let stat;
|
|
5436
|
+
try {
|
|
5437
|
+
stat = await fs3.lstat(absolutePath);
|
|
5438
|
+
} catch (error) {
|
|
5439
|
+
if (isNotFoundError(error)) {
|
|
5440
|
+
return { inputs: [input] };
|
|
5441
|
+
}
|
|
5442
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
5443
|
+
throw new SessionArtifactValidationError(
|
|
5444
|
+
`workspacePath could not be inspected: ${reason}`,
|
|
5445
|
+
"workspacePath"
|
|
5446
|
+
);
|
|
5447
|
+
}
|
|
5448
|
+
let walkDir = absolutePath;
|
|
5449
|
+
let walkRelative = normalizedPath;
|
|
5450
|
+
if (stat.isSymbolicLink()) {
|
|
5451
|
+
let realPath;
|
|
5452
|
+
try {
|
|
5453
|
+
realPath = await fs3.realpath(absolutePath);
|
|
5454
|
+
} catch {
|
|
5455
|
+
return { inputs: [input] };
|
|
5456
|
+
}
|
|
5457
|
+
let realStat;
|
|
5458
|
+
try {
|
|
5459
|
+
realStat = await fs3.lstat(realPath);
|
|
5460
|
+
} catch (error) {
|
|
5461
|
+
if (isNotFoundError(error)) {
|
|
5462
|
+
return { inputs: [input] };
|
|
5463
|
+
}
|
|
5464
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
5465
|
+
throw new SessionArtifactValidationError(
|
|
5466
|
+
`workspacePath could not be inspected: ${reason}`,
|
|
5467
|
+
"workspacePath"
|
|
5468
|
+
);
|
|
5469
|
+
}
|
|
5470
|
+
if (!realStat.isDirectory()) {
|
|
5471
|
+
return { inputs: [input] };
|
|
5472
|
+
}
|
|
5473
|
+
walkDir = realPath;
|
|
5474
|
+
walkRelative = normalizedPath;
|
|
5475
|
+
} else if (!stat.isDirectory()) {
|
|
5476
|
+
return { inputs: [input] };
|
|
5477
|
+
} else {
|
|
5478
|
+
try {
|
|
5479
|
+
walkDir = await fs3.realpath(absolutePath);
|
|
5480
|
+
} catch (error) {
|
|
5481
|
+
if (isNotFoundError(error)) {
|
|
5482
|
+
return { inputs: [input] };
|
|
5483
|
+
}
|
|
5484
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
5485
|
+
throw new SessionArtifactValidationError(
|
|
5486
|
+
`workspacePath could not be inspected: ${reason}`,
|
|
5487
|
+
"workspacePath"
|
|
5488
|
+
);
|
|
5489
|
+
}
|
|
5490
|
+
}
|
|
5491
|
+
const resolvedRelative = path3.relative(realWorkspace, walkDir);
|
|
5492
|
+
if (!resolvedRelative || isOutsidePath(resolvedRelative)) {
|
|
5493
|
+
throw new SessionArtifactValidationError(
|
|
5494
|
+
"workspacePath must stay inside the workspace",
|
|
5495
|
+
"workspacePath"
|
|
5496
|
+
);
|
|
5497
|
+
}
|
|
5498
|
+
if (pathHasSkippedDirectoryComponent(
|
|
5499
|
+
resolvedRelative.split(path3.sep).join("/")
|
|
5500
|
+
) || walkRelative && pathHasSkippedDirectoryComponent(walkRelative)) {
|
|
5501
|
+
throw new SessionArtifactValidationError(
|
|
5502
|
+
"workspacePath is a skipped directory and cannot be recorded",
|
|
5503
|
+
"workspacePath"
|
|
5504
|
+
);
|
|
5505
|
+
}
|
|
5506
|
+
if (input.metadata !== void 0 && !isPlainMetadataObject(input.metadata)) {
|
|
5507
|
+
throw new SessionArtifactValidationError(
|
|
5508
|
+
"metadata must be an object",
|
|
5509
|
+
"metadata"
|
|
5510
|
+
);
|
|
5511
|
+
}
|
|
5512
|
+
const childMetadata = {
|
|
5513
|
+
...isPlainMetadataObject(input.metadata) ? input.metadata : {},
|
|
5514
|
+
expandedFromDirectory: true
|
|
5515
|
+
};
|
|
5516
|
+
if (Buffer.byteLength(JSON.stringify(childMetadata), "utf8") > 4096) {
|
|
5517
|
+
throw new SessionArtifactValidationError(
|
|
5518
|
+
"metadata is too large to expand a directory",
|
|
5519
|
+
"metadata"
|
|
5520
|
+
);
|
|
5521
|
+
}
|
|
5522
|
+
const parentTitle = normalizeString(input.title, "title", 200, true);
|
|
5523
|
+
const parentDescription = normalizeString(
|
|
5524
|
+
input.description,
|
|
5525
|
+
"description",
|
|
5526
|
+
1e3,
|
|
5527
|
+
false
|
|
5528
|
+
);
|
|
5529
|
+
let collected;
|
|
5530
|
+
try {
|
|
5531
|
+
collected = await collectRecordableWorkspaceFiles(
|
|
5532
|
+
walkDir,
|
|
5533
|
+
walkRelative,
|
|
5534
|
+
realWorkspace,
|
|
5535
|
+
(filePath) => isRecordableDerivedChild(path3.posix.basename(filePath), filePath)
|
|
5536
|
+
);
|
|
5537
|
+
} catch (error) {
|
|
5538
|
+
if (isNotFoundError(error)) {
|
|
5539
|
+
return { inputs: [input] };
|
|
5540
|
+
}
|
|
5541
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
5542
|
+
throw new SessionArtifactValidationError(
|
|
5543
|
+
`workspacePath could not be inspected: ${reason}`,
|
|
5544
|
+
"workspacePath"
|
|
5545
|
+
);
|
|
5546
|
+
}
|
|
5547
|
+
if (collected.files.length === 0) {
|
|
5548
|
+
throw new SessionArtifactValidationError(
|
|
5549
|
+
collected.depthLimited ? `workspacePath is a directory whose recordable files are deeper than ${MAX_DIRECTORY_ARTIFACT_DEPTH} levels` : "workspacePath is a directory with no recordable files",
|
|
5550
|
+
"workspacePath"
|
|
5551
|
+
);
|
|
5552
|
+
}
|
|
5553
|
+
const warnings = [];
|
|
5554
|
+
if (collected.truncated) {
|
|
5555
|
+
warnings.push(
|
|
5556
|
+
`workspacePath "${normalizedPath}" contained more than ${MAX_DIRECTORY_ARTIFACT_FILES} files; recorded the first ${collected.files.length}`
|
|
5557
|
+
);
|
|
5558
|
+
}
|
|
5559
|
+
if (collected.depthLimited) {
|
|
5560
|
+
warnings.push(
|
|
5561
|
+
`workspacePath "${normalizedPath}" exceeded ${MAX_DIRECTORY_ARTIFACT_DEPTH} directory levels; some files were not recorded`
|
|
5562
|
+
);
|
|
5563
|
+
}
|
|
5564
|
+
if (collected.unreadable) {
|
|
5565
|
+
warnings.push(
|
|
5566
|
+
`workspacePath "${normalizedPath}" contained subdirectories that could not be read`
|
|
5567
|
+
);
|
|
5568
|
+
}
|
|
5569
|
+
if (collected.skippedUnrecordable > 0) {
|
|
5570
|
+
warnings.push(
|
|
5571
|
+
`workspacePath "${normalizedPath}" skipped ${collected.skippedUnrecordable} files whose names cannot be recorded as artifact titles`
|
|
5572
|
+
);
|
|
5573
|
+
}
|
|
5574
|
+
return {
|
|
5575
|
+
inputs: collected.files.map((filePath) => {
|
|
5576
|
+
const title = path3.posix.basename(filePath).trim();
|
|
5577
|
+
const description = parentDescription || (parentTitle && parentTitle !== title ? parentTitle : void 0);
|
|
5578
|
+
return {
|
|
5579
|
+
...input,
|
|
5580
|
+
title,
|
|
5581
|
+
workspacePath: filePath,
|
|
5582
|
+
kind: void 0,
|
|
5583
|
+
mimeType: void 0,
|
|
5584
|
+
sizeBytes: void 0,
|
|
5585
|
+
metadata: childMetadata,
|
|
5586
|
+
...description ? { description } : { description: void 0 }
|
|
5587
|
+
};
|
|
5588
|
+
}),
|
|
5589
|
+
...warnings.length > 0 ? { warning: warnings.join("; ") } : {}
|
|
5590
|
+
};
|
|
5591
|
+
}
|
|
4761
5592
|
async normalizeInput(input, receivedSeq, trustedPublisherFromCaller, options = {}) {
|
|
4762
5593
|
if (!input || typeof input !== "object") {
|
|
4763
5594
|
throw new SessionArtifactValidationError("Artifact must be an object");
|
|
@@ -4954,7 +5785,7 @@ var SessionArtifactStore = class {
|
|
|
4954
5785
|
}
|
|
4955
5786
|
getRealWorkspaceCwd() {
|
|
4956
5787
|
if (!this.realWorkspaceCwdPromise) {
|
|
4957
|
-
const promise =
|
|
5788
|
+
const promise = fs3.realpath(this.workspaceCwd).catch((error) => {
|
|
4958
5789
|
if (this.realWorkspaceCwdPromise === promise) {
|
|
4959
5790
|
this.realWorkspaceCwdPromise = void 0;
|
|
4960
5791
|
}
|
|
@@ -4978,7 +5809,7 @@ var SessionArtifactStore = class {
|
|
|
4978
5809
|
async evictOverflow(createdIds, changes, strict = false) {
|
|
4979
5810
|
const removed = [];
|
|
4980
5811
|
if (this.artifacts.size <= this.maxArtifacts) {
|
|
4981
|
-
return removed;
|
|
5812
|
+
return { removed, droppedCreated: 0 };
|
|
4982
5813
|
}
|
|
4983
5814
|
const createdInThisBatch = new Set(createdIds);
|
|
4984
5815
|
const candidates = Array.from(this.artifacts.values()).filter(
|
|
@@ -5014,17 +5845,19 @@ var SessionArtifactStore = class {
|
|
|
5014
5845
|
"artifactId"
|
|
5015
5846
|
);
|
|
5016
5847
|
}
|
|
5848
|
+
let droppedCreated = 0;
|
|
5017
5849
|
for (const artifact of overflowCreated) {
|
|
5018
5850
|
if (this.artifacts.size <= this.maxArtifacts) {
|
|
5019
5851
|
break;
|
|
5020
5852
|
}
|
|
5021
5853
|
this.artifacts.delete(artifact.id);
|
|
5854
|
+
droppedCreated++;
|
|
5022
5855
|
writeStderrLine(
|
|
5023
5856
|
`[artifacts] session=${this.sessionId} action=dropped reason="max artifacts exceeded" artifactId=${artifact.id}`
|
|
5024
5857
|
);
|
|
5025
5858
|
removePriorChange(changes, artifact.id);
|
|
5026
5859
|
}
|
|
5027
|
-
return removed;
|
|
5860
|
+
return { removed, droppedCreated };
|
|
5028
5861
|
}
|
|
5029
5862
|
};
|
|
5030
5863
|
function coalesceByIdentity(artifacts) {
|
|
@@ -5074,11 +5907,19 @@ function mergeBatchArtifact(existing, next) {
|
|
|
5074
5907
|
delete merged.workspacePath;
|
|
5075
5908
|
return merged;
|
|
5076
5909
|
}
|
|
5910
|
+
const refreshDisplay = existing.storage === "workspace" && next.storage === "workspace" && shouldRefreshWorkspaceDisplay(next, existing);
|
|
5911
|
+
const metadata = mergeMetadata(existing, next);
|
|
5077
5912
|
return {
|
|
5078
5913
|
...existing,
|
|
5914
|
+
title: refreshDisplay ? next.title : existing.title,
|
|
5915
|
+
description: refreshDisplay ? next.description ?? existing.description : existing.description,
|
|
5916
|
+
toolName: refreshDisplay ? next.toolName : existing.toolName,
|
|
5917
|
+
source: refreshDisplay ? next.source : existing.source,
|
|
5918
|
+
hookEventName: refreshDisplay ? next.hookEventName : existing.hookEventName,
|
|
5919
|
+
toolCallId: refreshDisplay ? next.toolCallId : existing.toolCallId,
|
|
5079
5920
|
status: next.status,
|
|
5080
5921
|
sizeBytes: mergeSizeBytes(existing, next),
|
|
5081
|
-
metadata:
|
|
5922
|
+
metadata: refreshDisplay ? stripExpandedFromDirectoryMarker(metadata) : metadata,
|
|
5082
5923
|
clientRetained: existing.clientRetained || next.clientRetained,
|
|
5083
5924
|
trustedPublisher: existing.trustedPublisher || next.trustedPublisher,
|
|
5084
5925
|
retentionExplicit: existing.retentionExplicit || next.retentionExplicit,
|
|
@@ -5122,6 +5963,14 @@ function mergeArtifact(existing, incoming) {
|
|
|
5122
5963
|
next.description = incoming.description;
|
|
5123
5964
|
delete next.workspacePath;
|
|
5124
5965
|
delete next.hideWorkspacePath;
|
|
5966
|
+
} else if (existing.storage === "workspace" && incoming.storage === "workspace" && shouldRefreshWorkspaceDisplay(incoming, existing)) {
|
|
5967
|
+
next.title = incoming.title;
|
|
5968
|
+
next.description = incoming.description ?? existing.description;
|
|
5969
|
+
next.toolCallId = incoming.toolCallId;
|
|
5970
|
+
next.toolName = incoming.toolName;
|
|
5971
|
+
next.source = incoming.source;
|
|
5972
|
+
next.hookEventName = incoming.hookEventName;
|
|
5973
|
+
next.metadata = stripExpandedFromDirectoryMarker(next.metadata);
|
|
5125
5974
|
}
|
|
5126
5975
|
const changed = !publicArtifactsEqual(
|
|
5127
5976
|
toPublicArtifact(existing),
|
|
@@ -5130,13 +5979,34 @@ function mergeArtifact(existing, incoming) {
|
|
|
5130
5979
|
if (changed) {
|
|
5131
5980
|
next.updatedAt = now;
|
|
5132
5981
|
}
|
|
5133
|
-
return { artifact: changed ? next : existing, changed };
|
|
5134
|
-
}
|
|
5135
|
-
__name(mergeArtifact, "mergeArtifact");
|
|
5136
|
-
function shouldRecordEphemeralUnpin(existing, incoming) {
|
|
5137
|
-
return incoming.retentionExplicit && incoming.retention === "ephemeral" && (existing.retention !== "ephemeral" || existing.persistedAt !== void 0 || existing.durableTombstoneRequired === true);
|
|
5982
|
+
return { artifact: changed ? next : existing, changed };
|
|
5983
|
+
}
|
|
5984
|
+
__name(mergeArtifact, "mergeArtifact");
|
|
5985
|
+
function shouldRecordEphemeralUnpin(existing, incoming) {
|
|
5986
|
+
return incoming.retentionExplicit && incoming.retention === "ephemeral" && (existing.retention !== "ephemeral" || existing.persistedAt !== void 0 || existing.durableTombstoneRequired === true);
|
|
5987
|
+
}
|
|
5988
|
+
__name(shouldRecordEphemeralUnpin, "shouldRecordEphemeralUnpin");
|
|
5989
|
+
function stripExpandedFromDirectoryMarker(metadata) {
|
|
5990
|
+
if (metadata?.["expandedFromDirectory"] !== true) {
|
|
5991
|
+
return metadata;
|
|
5992
|
+
}
|
|
5993
|
+
const { expandedFromDirectory: _dropped, ...rest } = metadata;
|
|
5994
|
+
return Object.keys(rest).length > 0 ? rest : void 0;
|
|
5995
|
+
}
|
|
5996
|
+
__name(stripExpandedFromDirectoryMarker, "stripExpandedFromDirectoryMarker");
|
|
5997
|
+
function shouldRefreshWorkspaceDisplay(incoming, existing) {
|
|
5998
|
+
if (incoming.metadata?.["expandedFromDirectory"] === true) {
|
|
5999
|
+
return false;
|
|
6000
|
+
}
|
|
6001
|
+
if (incoming.toolName === "record_artifact" && incoming.source !== "hook") {
|
|
6002
|
+
return true;
|
|
6003
|
+
}
|
|
6004
|
+
if (!incoming.toolName) {
|
|
6005
|
+
return true;
|
|
6006
|
+
}
|
|
6007
|
+
return incoming.toolName === existing.toolName && incoming.source === existing.source && incoming.hookEventName === existing.hookEventName;
|
|
5138
6008
|
}
|
|
5139
|
-
__name(
|
|
6009
|
+
__name(shouldRefreshWorkspaceDisplay, "shouldRefreshWorkspaceDisplay");
|
|
5140
6010
|
function publicArtifactsEqual(a, b) {
|
|
5141
6011
|
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;
|
|
5142
6012
|
}
|
|
@@ -5518,7 +6388,7 @@ function inferStorage(requested, locators) {
|
|
|
5518
6388
|
}
|
|
5519
6389
|
__name(inferStorage, "inferStorage");
|
|
5520
6390
|
function normalizeKind(kind) {
|
|
5521
|
-
if (kind === "file" || kind === "link" || kind === "html" || kind === "image" || kind === "video" || kind === "audio" || kind === "pdf" || kind === "notebook" || kind === "other") {
|
|
6391
|
+
if (kind === "file" || kind === "link" || kind === "html" || kind === "image" || kind === "video" || kind === "audio" || kind === "pdf" || kind === "notebook" || kind === "document" || kind === "other") {
|
|
5522
6392
|
return kind;
|
|
5523
6393
|
}
|
|
5524
6394
|
throw new SessionArtifactValidationError(
|
|
@@ -5589,7 +6459,7 @@ function buildIdentityKey(input) {
|
|
|
5589
6459
|
}
|
|
5590
6460
|
__name(buildIdentityKey, "buildIdentityKey");
|
|
5591
6461
|
function managedIdForWorkspacePath(workspaceCwd, workspacePath) {
|
|
5592
|
-
return createHash("sha1").update(
|
|
6462
|
+
return createHash("sha1").update(path3.resolve(workspaceCwd, workspacePath)).digest("hex").slice(0, 16);
|
|
5593
6463
|
}
|
|
5594
6464
|
__name(managedIdForWorkspacePath, "managedIdForWorkspacePath");
|
|
5595
6465
|
function normalizeString(value, field, maxLength, required) {
|
|
@@ -5636,7 +6506,7 @@ __name(normalizeString, "normalizeString");
|
|
|
5636
6506
|
function normalizeManagedId(value) {
|
|
5637
6507
|
const managedId = normalizeString(value, "managedId", 200, false);
|
|
5638
6508
|
if (!managedId) return void 0;
|
|
5639
|
-
if (managedId.includes("/") || managedId.includes("\\") || managedId.includes("..") ||
|
|
6509
|
+
if (managedId.includes("/") || managedId.includes("\\") || managedId.includes("..") || path3.isAbsolute(managedId) || path3.win32.isAbsolute(managedId)) {
|
|
5640
6510
|
throw new SessionArtifactValidationError(
|
|
5641
6511
|
"managedId must be an opaque managed resource id",
|
|
5642
6512
|
"managedId"
|
|
@@ -5670,21 +6540,21 @@ function hasUnsafeDisplayPayload(value) {
|
|
|
5670
6540
|
__name(hasUnsafeDisplayPayload, "hasUnsafeDisplayPayload");
|
|
5671
6541
|
function normalizeWorkspacePath(raw, workspaceCwd) {
|
|
5672
6542
|
const trimmed = normalizeString(raw, "workspacePath", 500, true);
|
|
5673
|
-
if (
|
|
6543
|
+
if (path3.isAbsolute(trimmed)) {
|
|
5674
6544
|
throw new SessionArtifactValidationError(
|
|
5675
6545
|
"workspacePath must be relative to the workspace",
|
|
5676
6546
|
"workspacePath"
|
|
5677
6547
|
);
|
|
5678
6548
|
}
|
|
5679
|
-
const absolute =
|
|
5680
|
-
const relative =
|
|
6549
|
+
const absolute = path3.resolve(workspaceCwd, trimmed);
|
|
6550
|
+
const relative = path3.relative(workspaceCwd, absolute);
|
|
5681
6551
|
if (!relative || isOutsidePath(relative)) {
|
|
5682
6552
|
throw new SessionArtifactValidationError(
|
|
5683
6553
|
"workspacePath must stay inside the workspace",
|
|
5684
6554
|
"workspacePath"
|
|
5685
6555
|
);
|
|
5686
6556
|
}
|
|
5687
|
-
return relative.split(
|
|
6557
|
+
return relative.split(path3.sep).join("/");
|
|
5688
6558
|
}
|
|
5689
6559
|
__name(normalizeWorkspacePath, "normalizeWorkspacePath");
|
|
5690
6560
|
function normalizeArtifactUrl(raw, allowFile) {
|
|
@@ -5885,7 +6755,7 @@ __name(normalizeSizeBytes, "normalizeSizeBytes");
|
|
|
5885
6755
|
function inferKind(input) {
|
|
5886
6756
|
if (input.storage === "published") return "html";
|
|
5887
6757
|
if (input.url) return "link";
|
|
5888
|
-
const ext = input.workspacePath ?
|
|
6758
|
+
const ext = input.workspacePath ? path3.extname(input.workspacePath).toLowerCase() : "";
|
|
5889
6759
|
if (ext === ".html" || ext === ".htm") return "html";
|
|
5890
6760
|
if ([".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"].includes(ext)) {
|
|
5891
6761
|
return "image";
|
|
@@ -5894,20 +6764,21 @@ function inferKind(input) {
|
|
|
5894
6764
|
if ([".mp3", ".wav", ".m4a", ".ogg"].includes(ext)) return "audio";
|
|
5895
6765
|
if (ext === ".pdf") return "pdf";
|
|
5896
6766
|
if (ext === ".ipynb") return "notebook";
|
|
6767
|
+
if (isOfficeDocumentExtension(ext)) return "document";
|
|
5897
6768
|
return input.workspacePath ? "file" : "other";
|
|
5898
6769
|
}
|
|
5899
6770
|
__name(inferKind, "inferKind");
|
|
5900
6771
|
async function getWorkspaceStatus(workspacePath, realWorkspaceCwd, expected, options = { hashContent: true }) {
|
|
5901
6772
|
const realWorkspace = await realWorkspaceCwd;
|
|
5902
|
-
const absolutePath =
|
|
6773
|
+
const absolutePath = path3.resolve(realWorkspace, workspacePath);
|
|
5903
6774
|
try {
|
|
5904
|
-
const realPath = await
|
|
5905
|
-
const relative =
|
|
6775
|
+
const realPath = await fs3.realpath(absolutePath);
|
|
6776
|
+
const relative = path3.relative(realWorkspace, realPath);
|
|
5906
6777
|
if (!relative || isOutsidePath(relative)) {
|
|
5907
6778
|
return { status: "missing", escaped: true };
|
|
5908
6779
|
}
|
|
5909
|
-
const preOpenStat = await
|
|
5910
|
-
const handle = await
|
|
6780
|
+
const preOpenStat = await fs3.lstat(realPath);
|
|
6781
|
+
const handle = await fs3.open(
|
|
5911
6782
|
realPath,
|
|
5912
6783
|
fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW
|
|
5913
6784
|
);
|
|
@@ -5916,56 +6787,54 @@ async function getWorkspaceStatus(workspacePath, realWorkspaceCwd, expected, opt
|
|
|
5916
6787
|
if (!isSameFile(preOpenStat, stat)) {
|
|
5917
6788
|
return { status: "missing", escaped: true };
|
|
5918
6789
|
}
|
|
5919
|
-
if (stat.isFile()) {
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
sizeBytes: stat.size,
|
|
5935
|
-
mtimeMs: stat.mtimeMs
|
|
5936
|
-
};
|
|
5937
|
-
}
|
|
5938
|
-
if (stat.size > MAX_WORKSPACE_HASH_BYTES) {
|
|
5939
|
-
return {
|
|
5940
|
-
status: expectedSha256 ? "changed" : "available",
|
|
5941
|
-
sizeBytes: stat.size,
|
|
5942
|
-
mtimeMs: stat.mtimeMs
|
|
5943
|
-
};
|
|
5944
|
-
}
|
|
5945
|
-
if (!options.hashContent) {
|
|
5946
|
-
return {
|
|
5947
|
-
status: expectedSha256 ? "changed" : "available",
|
|
5948
|
-
sizeBytes: stat.size,
|
|
5949
|
-
mtimeMs: stat.mtimeMs
|
|
5950
|
-
};
|
|
5951
|
-
}
|
|
5952
|
-
const sha256 = await hashFile(handle);
|
|
5953
|
-
if (expectedSha256 && sha256 !== expectedSha256) {
|
|
5954
|
-
return {
|
|
5955
|
-
status: "changed",
|
|
5956
|
-
sizeBytes: stat.size,
|
|
5957
|
-
mtimeMs: stat.mtimeMs
|
|
5958
|
-
};
|
|
5959
|
-
}
|
|
6790
|
+
if (!stat.isFile()) {
|
|
6791
|
+
throw new Error("path is not a regular file");
|
|
6792
|
+
}
|
|
6793
|
+
const expectedMtimeMs = typeof expected?.mtimeMs === "number" ? expected.mtimeMs : void 0;
|
|
6794
|
+
const expectedSha256 = typeof expected?.sha256 === "string" ? expected.sha256 : void 0;
|
|
6795
|
+
const unchanged = expected?.sizeBytes === stat.size && expectedMtimeMs === stat.mtimeMs;
|
|
6796
|
+
const sizeChanged = expected?.sizeBytes !== void 0 && expected.sizeBytes !== stat.size;
|
|
6797
|
+
if (sizeChanged) {
|
|
6798
|
+
return {
|
|
6799
|
+
status: "changed",
|
|
6800
|
+
sizeBytes: stat.size,
|
|
6801
|
+
mtimeMs: stat.mtimeMs
|
|
6802
|
+
};
|
|
6803
|
+
}
|
|
6804
|
+
if (unchanged) {
|
|
5960
6805
|
return {
|
|
5961
6806
|
status: "available",
|
|
5962
6807
|
sizeBytes: stat.size,
|
|
5963
|
-
mtimeMs: stat.mtimeMs
|
|
5964
|
-
|
|
6808
|
+
mtimeMs: stat.mtimeMs
|
|
6809
|
+
};
|
|
6810
|
+
}
|
|
6811
|
+
if (stat.size > MAX_WORKSPACE_HASH_BYTES) {
|
|
6812
|
+
return {
|
|
6813
|
+
status: expectedSha256 ? "changed" : "available",
|
|
6814
|
+
sizeBytes: stat.size,
|
|
6815
|
+
mtimeMs: stat.mtimeMs
|
|
6816
|
+
};
|
|
6817
|
+
}
|
|
6818
|
+
if (!options.hashContent) {
|
|
6819
|
+
return {
|
|
6820
|
+
status: expectedSha256 ? "changed" : "available",
|
|
6821
|
+
sizeBytes: stat.size,
|
|
6822
|
+
mtimeMs: stat.mtimeMs
|
|
6823
|
+
};
|
|
6824
|
+
}
|
|
6825
|
+
const sha256 = await hashFile(handle);
|
|
6826
|
+
if (expectedSha256 && sha256 !== expectedSha256) {
|
|
6827
|
+
return {
|
|
6828
|
+
status: "changed",
|
|
6829
|
+
sizeBytes: stat.size,
|
|
6830
|
+
mtimeMs: stat.mtimeMs
|
|
5965
6831
|
};
|
|
5966
6832
|
}
|
|
5967
6833
|
return {
|
|
5968
|
-
status: "available"
|
|
6834
|
+
status: "available",
|
|
6835
|
+
sizeBytes: stat.size,
|
|
6836
|
+
mtimeMs: stat.mtimeMs,
|
|
6837
|
+
sha256
|
|
5969
6838
|
};
|
|
5970
6839
|
} finally {
|
|
5971
6840
|
await handle.close();
|
|
@@ -6021,14 +6890,14 @@ function isWorkspaceContentChanged(artifact, status) {
|
|
|
6021
6890
|
__name(isWorkspaceContentChanged, "isWorkspaceContentChanged");
|
|
6022
6891
|
async function danglingSymlinkEscapesWorkspace(absolutePath, realWorkspace) {
|
|
6023
6892
|
try {
|
|
6024
|
-
const stat = await
|
|
6893
|
+
const stat = await fs3.lstat(absolutePath);
|
|
6025
6894
|
if (!stat.isSymbolicLink()) {
|
|
6026
6895
|
return false;
|
|
6027
6896
|
}
|
|
6028
|
-
const target = await
|
|
6029
|
-
const parent = await
|
|
6030
|
-
const targetPath =
|
|
6031
|
-
return isOutsidePath(
|
|
6897
|
+
const target = await fs3.readlink(absolutePath);
|
|
6898
|
+
const parent = await fs3.realpath(path3.dirname(absolutePath));
|
|
6899
|
+
const targetPath = path3.resolve(parent, target);
|
|
6900
|
+
return isOutsidePath(path3.relative(realWorkspace, targetPath));
|
|
6032
6901
|
} catch (error) {
|
|
6033
6902
|
if (isNotFoundError(error)) {
|
|
6034
6903
|
return false;
|
|
@@ -6051,6 +6920,10 @@ function isNotFoundError(error) {
|
|
|
6051
6920
|
return code === "ENOENT" || code === "ENOTDIR";
|
|
6052
6921
|
}
|
|
6053
6922
|
__name(isNotFoundError, "isNotFoundError");
|
|
6923
|
+
function isPlainMetadataObject(value) {
|
|
6924
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6925
|
+
}
|
|
6926
|
+
__name(isPlainMetadataObject, "isPlainMetadataObject");
|
|
6054
6927
|
function isNoFollowSymlinkError(error) {
|
|
6055
6928
|
if (typeof error !== "object" || error === null || !("code" in error)) {
|
|
6056
6929
|
return false;
|
|
@@ -6059,7 +6932,7 @@ function isNoFollowSymlinkError(error) {
|
|
|
6059
6932
|
}
|
|
6060
6933
|
__name(isNoFollowSymlinkError, "isNoFollowSymlinkError");
|
|
6061
6934
|
function isOutsidePath(relative) {
|
|
6062
|
-
return relative === ".." || relative.startsWith(`..${
|
|
6935
|
+
return relative === ".." || relative.startsWith(`..${path3.sep}`) || path3.isAbsolute(relative);
|
|
6063
6936
|
}
|
|
6064
6937
|
__name(isOutsidePath, "isOutsidePath");
|
|
6065
6938
|
|
|
@@ -6866,6 +7739,107 @@ function broadcastTurnError(entry, sessionId, err, promptId, originatorClientId,
|
|
|
6866
7739
|
}
|
|
6867
7740
|
}
|
|
6868
7741
|
__name(broadcastTurnError, "broadcastTurnError");
|
|
7742
|
+
var TERMINAL_TURN_STATUS_OVERLAY_LIMIT = 64;
|
|
7743
|
+
function truncateTurnText(text) {
|
|
7744
|
+
const truncated = text.length > TURN_RESULT_TEXT_MAX_CHARS;
|
|
7745
|
+
return {
|
|
7746
|
+
text: truncated ? text.slice(0, TURN_RESULT_TEXT_MAX_CHARS) : text,
|
|
7747
|
+
truncated
|
|
7748
|
+
};
|
|
7749
|
+
}
|
|
7750
|
+
__name(truncateTurnText, "truncateTurnText");
|
|
7751
|
+
function rememberTerminalTurnStatus(entry, pending, terminal) {
|
|
7752
|
+
const promptText = truncateTurnText(pending.text);
|
|
7753
|
+
const shared = {
|
|
7754
|
+
sessionId: entry.sessionId,
|
|
7755
|
+
promptId: pending.promptId,
|
|
7756
|
+
promptText: promptText.text,
|
|
7757
|
+
...promptText.truncated ? { promptTextTruncated: true } : {},
|
|
7758
|
+
queuedAt: pending.queuedAt,
|
|
7759
|
+
...pending.startedAt !== void 0 ? { startedAt: pending.startedAt } : {},
|
|
7760
|
+
endedAt: Date.now(),
|
|
7761
|
+
...pending.originatorClientId !== void 0 ? { originatorClientId: pending.originatorClientId } : {}
|
|
7762
|
+
};
|
|
7763
|
+
const status = terminal.kind === "complete" ? {
|
|
7764
|
+
...shared,
|
|
7765
|
+
state: terminal.result.stopReason === "cancelled" ? "cancelled" : "completed",
|
|
7766
|
+
...terminal.result.stopReason !== void 0 ? { stopReason: terminal.result.stopReason } : {}
|
|
7767
|
+
} : terminal.kind === "cancelled" ? { ...shared, state: "cancelled", stopReason: "cancelled" } : {
|
|
7768
|
+
...shared,
|
|
7769
|
+
state: "error",
|
|
7770
|
+
error: normalizeTurnResultError(terminal.err)
|
|
7771
|
+
};
|
|
7772
|
+
entry.terminalTurnStatuses.set(pending.promptId, status);
|
|
7773
|
+
entry.enrichedTerminalPromptIds.delete(pending.promptId);
|
|
7774
|
+
while (entry.terminalTurnStatuses.size > TERMINAL_TURN_STATUS_OVERLAY_LIMIT) {
|
|
7775
|
+
const oldest2 = entry.terminalTurnStatuses.keys().next().value;
|
|
7776
|
+
if (oldest2 === void 0) break;
|
|
7777
|
+
entry.terminalTurnStatuses.delete(oldest2);
|
|
7778
|
+
entry.enrichedTerminalPromptIds.delete(oldest2);
|
|
7779
|
+
}
|
|
7780
|
+
}
|
|
7781
|
+
__name(rememberTerminalTurnStatus, "rememberTerminalTurnStatus");
|
|
7782
|
+
function rememberEnrichedTerminalTurnStatus(entry, promptId, status) {
|
|
7783
|
+
entry.terminalTurnStatuses.set(promptId, status);
|
|
7784
|
+
entry.enrichedTerminalPromptIds.add(promptId);
|
|
7785
|
+
while (entry.terminalTurnStatuses.size > TERMINAL_TURN_STATUS_OVERLAY_LIMIT) {
|
|
7786
|
+
const oldest2 = entry.terminalTurnStatuses.keys().next().value;
|
|
7787
|
+
if (oldest2 === void 0) break;
|
|
7788
|
+
entry.terminalTurnStatuses.delete(oldest2);
|
|
7789
|
+
entry.enrichedTerminalPromptIds.delete(oldest2);
|
|
7790
|
+
}
|
|
7791
|
+
}
|
|
7792
|
+
__name(rememberEnrichedTerminalTurnStatus, "rememberEnrichedTerminalTurnStatus");
|
|
7793
|
+
function advanceTurnActivity(entry) {
|
|
7794
|
+
const createdAtMs = Date.parse(entry.createdAt);
|
|
7795
|
+
const previous = entry.lastTurnEndedAtMs ?? (Number.isFinite(createdAtMs) ? createdAtMs : void 0);
|
|
7796
|
+
entry.lastTurnEndedAtMs = previous === void 0 ? Date.now() : Math.max(Date.now(), previous + 1);
|
|
7797
|
+
}
|
|
7798
|
+
__name(advanceTurnActivity, "advanceTurnActivity");
|
|
7799
|
+
function appendPromptLedgerBestEffort(entry, record) {
|
|
7800
|
+
const ledger = entry.promptLedger;
|
|
7801
|
+
if (!ledger) return;
|
|
7802
|
+
try {
|
|
7803
|
+
ledger.appendSync(entry.sessionId, record);
|
|
7804
|
+
} catch (error) {
|
|
7805
|
+
writeStderrLine(
|
|
7806
|
+
`qwen serve: prompt ledger append failed for session=${entry.sessionId} promptId=${record.promptId}: ${error instanceof Error ? error.message : String(error)}`
|
|
7807
|
+
);
|
|
7808
|
+
}
|
|
7809
|
+
}
|
|
7810
|
+
__name(appendPromptLedgerBestEffort, "appendPromptLedgerBestEffort");
|
|
7811
|
+
function promptLedgerTerminalRecord(pendingEntry, terminal) {
|
|
7812
|
+
const at = Date.now();
|
|
7813
|
+
if (terminal.kind === "complete") {
|
|
7814
|
+
if (terminal.result.stopReason === "cancelled") {
|
|
7815
|
+
return {
|
|
7816
|
+
v: 1,
|
|
7817
|
+
promptId: pendingEntry.promptId,
|
|
7818
|
+
terminal: "cancelled",
|
|
7819
|
+
at
|
|
7820
|
+
};
|
|
7821
|
+
}
|
|
7822
|
+
return {
|
|
7823
|
+
v: 1,
|
|
7824
|
+
promptId: pendingEntry.promptId,
|
|
7825
|
+
terminal: "completed",
|
|
7826
|
+
...terminal.result.stopReason !== void 0 ? { stopReason: terminal.result.stopReason } : {},
|
|
7827
|
+
at
|
|
7828
|
+
};
|
|
7829
|
+
}
|
|
7830
|
+
if (terminal.kind === "cancelled") {
|
|
7831
|
+
return { v: 1, promptId: pendingEntry.promptId, terminal: "cancelled", at };
|
|
7832
|
+
}
|
|
7833
|
+
const normalized = normalizeTurnResultError(terminal.err);
|
|
7834
|
+
return {
|
|
7835
|
+
v: 1,
|
|
7836
|
+
promptId: pendingEntry.promptId,
|
|
7837
|
+
terminal: "error",
|
|
7838
|
+
...normalized.code !== void 0 ? { code: normalized.code } : {},
|
|
7839
|
+
at
|
|
7840
|
+
};
|
|
7841
|
+
}
|
|
7842
|
+
__name(promptLedgerTerminalRecord, "promptLedgerTerminalRecord");
|
|
6869
7843
|
function publishPromptTerminal(entry, pendingEntry, terminal) {
|
|
6870
7844
|
if (pendingEntry.terminalPublished) {
|
|
6871
7845
|
writeServeDebugLine(
|
|
@@ -6874,8 +7848,16 @@ function publishPromptTerminal(entry, pendingEntry, terminal) {
|
|
|
6874
7848
|
return;
|
|
6875
7849
|
}
|
|
6876
7850
|
pendingEntry.terminalPublished = true;
|
|
7851
|
+
appendPromptLedgerBestEffort(
|
|
7852
|
+
entry,
|
|
7853
|
+
promptLedgerTerminalRecord(pendingEntry, terminal)
|
|
7854
|
+
);
|
|
7855
|
+
rememberTerminalTurnStatus(entry, pendingEntry, terminal);
|
|
6877
7856
|
const originatorClientId = pendingEntry.originatorClientId;
|
|
6878
7857
|
const mutateTurnState = pendingEntry.state === "running";
|
|
7858
|
+
if (mutateTurnState) {
|
|
7859
|
+
advanceTurnActivity(entry);
|
|
7860
|
+
}
|
|
6879
7861
|
if (!mutateTurnState && entry.turnErrorEvent) {
|
|
6880
7862
|
const journal = entry.events.liveJournalSnapshot() ?? [];
|
|
6881
7863
|
if (journal.some(isRefreshAppendTurnContent)) {
|
|
@@ -6941,16 +7923,109 @@ function extractPromptText(prompt) {
|
|
|
6941
7923
|
if (!Array.isArray(prompt)) return "";
|
|
6942
7924
|
let hasImage = false;
|
|
6943
7925
|
for (const block of prompt) {
|
|
6944
|
-
|
|
7926
|
+
const record = block;
|
|
7927
|
+
if (record["type"] === "image") {
|
|
6945
7928
|
hasImage = true;
|
|
6946
7929
|
}
|
|
6947
|
-
if (
|
|
6948
|
-
return
|
|
7930
|
+
if (record["type"] === "text" && typeof record["text"] === "string" && record["text"].length > 0) {
|
|
7931
|
+
return record["text"];
|
|
6949
7932
|
}
|
|
6950
7933
|
}
|
|
6951
7934
|
return hasImage ? "[image]" : "";
|
|
6952
7935
|
}
|
|
6953
7936
|
__name(extractPromptText, "extractPromptText");
|
|
7937
|
+
function liveTurnStatus(sessionId, pending) {
|
|
7938
|
+
const promptText = truncateTurnText(pending.text);
|
|
7939
|
+
return {
|
|
7940
|
+
sessionId,
|
|
7941
|
+
state: pending.state === "running" ? "running" : "queued",
|
|
7942
|
+
promptId: pending.promptId,
|
|
7943
|
+
promptText: promptText.text,
|
|
7944
|
+
...promptText.truncated ? { promptTextTruncated: true } : {},
|
|
7945
|
+
queuedAt: pending.queuedAt,
|
|
7946
|
+
...pending.startedAt !== void 0 ? { startedAt: pending.startedAt } : {},
|
|
7947
|
+
...pending.originatorClientId !== void 0 ? { originatorClientId: pending.originatorClientId } : {}
|
|
7948
|
+
};
|
|
7949
|
+
}
|
|
7950
|
+
__name(liveTurnStatus, "liveTurnStatus");
|
|
7951
|
+
function findLiveTurnStatus(entry, promptId) {
|
|
7952
|
+
const live = entry.pendingPromptList.filter(
|
|
7953
|
+
(pending) => !pending.terminalPublished && (!pending.removed || pending.state === "running")
|
|
7954
|
+
);
|
|
7955
|
+
if (promptId !== void 0) {
|
|
7956
|
+
const match = live.find((pending) => pending.promptId === promptId);
|
|
7957
|
+
return match ? liveTurnStatus(entry.sessionId, match) : void 0;
|
|
7958
|
+
}
|
|
7959
|
+
const running = live.find((pending) => pending.state === "running");
|
|
7960
|
+
if (running) return liveTurnStatus(entry.sessionId, running);
|
|
7961
|
+
const queued = live.find((pending) => pending.state === "queued");
|
|
7962
|
+
return queued ? liveTurnStatus(entry.sessionId, queued) : void 0;
|
|
7963
|
+
}
|
|
7964
|
+
__name(findLiveTurnStatus, "findLiveTurnStatus");
|
|
7965
|
+
function settledTurnStatus(sessionId, record) {
|
|
7966
|
+
return {
|
|
7967
|
+
sessionId,
|
|
7968
|
+
state: record.state,
|
|
7969
|
+
promptId: record.promptId,
|
|
7970
|
+
...record.stopReason !== void 0 ? { stopReason: record.stopReason } : {},
|
|
7971
|
+
...record.error !== void 0 ? { error: record.error } : {},
|
|
7972
|
+
...record.startedAt !== void 0 ? { startedAt: record.startedAt } : {},
|
|
7973
|
+
endedAt: record.endedAt,
|
|
7974
|
+
...record.promptText !== void 0 ? { promptText: record.promptText } : {},
|
|
7975
|
+
...record.promptTextTruncated !== void 0 ? { promptTextTruncated: record.promptTextTruncated } : {},
|
|
7976
|
+
...record.resultText !== void 0 ? { resultText: record.resultText } : {},
|
|
7977
|
+
...record.resultTruncated !== void 0 ? { resultTruncated: record.resultTruncated } : {},
|
|
7978
|
+
...record.resultTruncated === true ? { resultCode: record.resultCode ?? TURN_RESULT_CODE_TEXT_TRUNCATED } : {},
|
|
7979
|
+
...record.originatorClientId !== void 0 ? { originatorClientId: record.originatorClientId } : {}
|
|
7980
|
+
};
|
|
7981
|
+
}
|
|
7982
|
+
__name(settledTurnStatus, "settledTurnStatus");
|
|
7983
|
+
function enrichTerminalTurnStatus(terminal, persisted) {
|
|
7984
|
+
return {
|
|
7985
|
+
...terminal,
|
|
7986
|
+
// The bridge's display projection is trusted; the child-recorded text
|
|
7987
|
+
// only backfills when the terminal has none, so hidden channel context
|
|
7988
|
+
// the child derived from raw blocks never replaces it.
|
|
7989
|
+
...terminal.promptText === void 0 && persisted.promptText !== void 0 ? { promptText: persisted.promptText } : {},
|
|
7990
|
+
...terminal.promptTextTruncated === void 0 && persisted.promptTextTruncated !== void 0 ? { promptTextTruncated: persisted.promptTextTruncated } : {},
|
|
7991
|
+
...persisted.resultText !== void 0 ? { resultText: persisted.resultText } : {},
|
|
7992
|
+
...persisted.resultTruncated !== void 0 ? { resultTruncated: persisted.resultTruncated } : {},
|
|
7993
|
+
...persisted.resultCode !== void 0 ? { resultCode: persisted.resultCode } : {},
|
|
7994
|
+
...terminal.originatorClientId === void 0 && persisted.originatorClientId !== void 0 ? { originatorClientId: persisted.originatorClientId } : {}
|
|
7995
|
+
};
|
|
7996
|
+
}
|
|
7997
|
+
__name(enrichTerminalTurnStatus, "enrichTerminalTurnStatus");
|
|
7998
|
+
function mergeTerminalWithPersisted(terminal, persisted) {
|
|
7999
|
+
if (terminal.state === "error" && persisted.state !== "error") {
|
|
8000
|
+
return {
|
|
8001
|
+
...persisted,
|
|
8002
|
+
...terminal.promptText !== void 0 ? { promptText: terminal.promptText } : {},
|
|
8003
|
+
...terminal.promptTextTruncated !== void 0 ? { promptTextTruncated: terminal.promptTextTruncated } : {}
|
|
8004
|
+
};
|
|
8005
|
+
}
|
|
8006
|
+
return enrichTerminalTurnStatus(terminal, persisted);
|
|
8007
|
+
}
|
|
8008
|
+
__name(mergeTerminalWithPersisted, "mergeTerminalWithPersisted");
|
|
8009
|
+
function latestTerminalTurnStatus(entry) {
|
|
8010
|
+
let latest;
|
|
8011
|
+
for (const status of entry.terminalTurnStatuses.values()) {
|
|
8012
|
+
if ((status.endedAt ?? 0) >= (latest?.endedAt ?? 0)) latest = status;
|
|
8013
|
+
}
|
|
8014
|
+
return latest;
|
|
8015
|
+
}
|
|
8016
|
+
__name(latestTerminalTurnStatus, "latestTerminalTurnStatus");
|
|
8017
|
+
function extractMediaBlocks(prompt) {
|
|
8018
|
+
if (!Array.isArray(prompt)) return void 0;
|
|
8019
|
+
const media = [];
|
|
8020
|
+
for (const block of prompt) {
|
|
8021
|
+
if (!block || typeof block !== "object") continue;
|
|
8022
|
+
if (block.type === "image" || block.type === "audio" || block.type === "resource" && "attachmentId" in block) {
|
|
8023
|
+
media.push(block);
|
|
8024
|
+
}
|
|
8025
|
+
}
|
|
8026
|
+
return media.length > 0 ? media : void 0;
|
|
8027
|
+
}
|
|
8028
|
+
__name(extractMediaBlocks, "extractMediaBlocks");
|
|
6954
8029
|
var DEFAULT_INIT_TIMEOUT_MS = 1e4;
|
|
6955
8030
|
var PERSIST_TIMEOUT_MS = 5e3;
|
|
6956
8031
|
var MAX_PARENT_PERSIST_ATTEMPTS = 3;
|
|
@@ -6968,6 +8043,22 @@ var MAX_EMPTY_TRANSCRIPT_PAGES = 20;
|
|
|
6968
8043
|
var SHELL_COMMAND_TIMEOUT_MS = 12e4;
|
|
6969
8044
|
var MAX_SHELL_OUTPUT_FOR_HISTORY = 1e4;
|
|
6970
8045
|
var MAX_MID_TURN_QUEUE_DEPTH = 20;
|
|
8046
|
+
var MAX_QUEUED_INLINE_ATTACHMENT_BYTES = 100 * 1024 * 1024;
|
|
8047
|
+
function inlineAttachmentBlockBytes(blocks) {
|
|
8048
|
+
let total = 0;
|
|
8049
|
+
for (const block of blocks) {
|
|
8050
|
+
if (block.type === "image" && "data" in block) {
|
|
8051
|
+
total += Buffer.byteLength(block.data);
|
|
8052
|
+
continue;
|
|
8053
|
+
}
|
|
8054
|
+
if (block.type !== "resource" || !("resource" in block)) continue;
|
|
8055
|
+
const resource = block.resource;
|
|
8056
|
+
if ("blob" in resource) total += Buffer.byteLength(resource.blob);
|
|
8057
|
+
else if ("text" in resource) total += Buffer.byteLength(resource.text);
|
|
8058
|
+
}
|
|
8059
|
+
return total;
|
|
8060
|
+
}
|
|
8061
|
+
__name(inlineAttachmentBlockBytes, "inlineAttachmentBlockBytes");
|
|
6971
8062
|
var DEFAULT_MAX_SESSIONS = 32;
|
|
6972
8063
|
var DEFAULT_MAX_PENDING_PROMPTS_PER_SESSION = 5;
|
|
6973
8064
|
var CHAT_RECORD_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
@@ -7034,7 +8125,17 @@ function createAcpSessionBridge(opts) {
|
|
|
7034
8125
|
);
|
|
7035
8126
|
}
|
|
7036
8127
|
}, "releaseFreshSessionReservation");
|
|
8128
|
+
const sessionCatalogGeneration = randomUUID2();
|
|
8129
|
+
let sessionCatalogRevision = 0;
|
|
8130
|
+
const getSessionCatalogVersion = /* @__PURE__ */ __name(() => ({
|
|
8131
|
+
generation: sessionCatalogGeneration,
|
|
8132
|
+
revision: sessionCatalogRevision
|
|
8133
|
+
}), "getSessionCatalogVersion");
|
|
8134
|
+
const markSessionCatalogChanged = /* @__PURE__ */ __name(() => {
|
|
8135
|
+
sessionCatalogRevision += 1;
|
|
8136
|
+
}, "markSessionCatalogChanged");
|
|
7037
8137
|
const emitSessionLifecycle = /* @__PURE__ */ __name((event) => {
|
|
8138
|
+
markSessionCatalogChanged();
|
|
7038
8139
|
try {
|
|
7039
8140
|
opts.sessionLifecycle?.(event);
|
|
7040
8141
|
} catch (err) {
|
|
@@ -7104,7 +8205,7 @@ function createAcpSessionBridge(opts) {
|
|
|
7104
8205
|
} else {
|
|
7105
8206
|
maxPendingPromptsPerSession = maxPendingPromptsRaw;
|
|
7106
8207
|
}
|
|
7107
|
-
if (!
|
|
8208
|
+
if (!path4.isAbsolute(opts.boundWorkspace)) {
|
|
7108
8209
|
throw new TypeError(
|
|
7109
8210
|
`Invalid boundWorkspace: "${opts.boundWorkspace}". Must be an absolute path.`
|
|
7110
8211
|
);
|
|
@@ -7444,10 +8545,7 @@ function createAcpSessionBridge(opts) {
|
|
|
7444
8545
|
}
|
|
7445
8546
|
__name(withWorkspaceControl, "withWorkspaceControl");
|
|
7446
8547
|
function startSessionReaper() {
|
|
7447
|
-
if (sessionReapIntervalMs <= 0
|
|
7448
|
-
writeStderrLine("qwen serve: session reaper disabled");
|
|
7449
|
-
return;
|
|
7450
|
-
}
|
|
8548
|
+
if (sessionReapIntervalMs <= 0) return;
|
|
7451
8549
|
writeStderrLine(
|
|
7452
8550
|
`qwen serve: session reaper started (interval ${sessionReapIntervalMs}ms, idle threshold ${sessionIdleTimeoutMs}ms)`
|
|
7453
8551
|
);
|
|
@@ -7455,6 +8553,7 @@ function createAcpSessionBridge(opts) {
|
|
|
7455
8553
|
if (shuttingDown) return;
|
|
7456
8554
|
const now = Date.now();
|
|
7457
8555
|
for (const [id, entry] of byId) {
|
|
8556
|
+
if (sessionIdleTimeoutMs <= 0) break;
|
|
7458
8557
|
if (!entryIsAutoCloseCandidate(entry)) continue;
|
|
7459
8558
|
const lastActive = entry.sessionLastSeenAt ?? Date.parse(entry.createdAt);
|
|
7460
8559
|
const idle = now - lastActive;
|
|
@@ -7552,12 +8651,13 @@ function createAcpSessionBridge(opts) {
|
|
|
7552
8651
|
sessionId: entry.sessionId,
|
|
7553
8652
|
workspaceCwd: entry.workspaceCwd,
|
|
7554
8653
|
createdAt: entry.createdAt,
|
|
8654
|
+
...entry.lastTurnEndedAtMs !== void 0 ? { updatedAt: new Date(entry.lastTurnEndedAtMs).toISOString() } : {},
|
|
7555
8655
|
displayName: entry.displayName,
|
|
7556
8656
|
...entry.parentSessionId ? { parentSessionId: entry.parentSessionId } : {},
|
|
7557
8657
|
...entry.sourceType ? { sourceType: entry.sourceType } : {},
|
|
7558
8658
|
...entry.sourceId !== void 0 ? { sourceId: entry.sourceId } : {},
|
|
7559
8659
|
clientCount: entry.clientIds.size,
|
|
7560
|
-
hasActivePrompt: entry.promptActive,
|
|
8660
|
+
hasActivePrompt: entry.promptActive || entry.goalTurnActive === true,
|
|
7561
8661
|
isWaitingForPermission,
|
|
7562
8662
|
isWaitingForUserQuestion,
|
|
7563
8663
|
pendingInteractionCount: entry.pendingInteractions.size,
|
|
@@ -7800,7 +8900,13 @@ function createAcpSessionBridge(opts) {
|
|
|
7800
8900
|
const currentInfo = infoRef.current;
|
|
7801
8901
|
if (!currentInfo) return;
|
|
7802
8902
|
applyActiveWorkSnapshot(currentInfo, snapshot);
|
|
7803
|
-
}
|
|
8903
|
+
},
|
|
8904
|
+
// Child-side automatic title updates change persisted catalog
|
|
8905
|
+
// metadata the bridge never sees; forward the catalog-clock mark.
|
|
8906
|
+
markSessionCatalogChanged,
|
|
8907
|
+
// A Goal turn drains the mid-turn queue but owns no prompt slot, so
|
|
8908
|
+
// nothing else would settle what its last drain missed.
|
|
8909
|
+
settleMidTurnQueueAfterGoalTurn
|
|
7804
8910
|
);
|
|
7805
8911
|
const rawConnection = new ClientSideConnection(
|
|
7806
8912
|
() => channel.transportGuard ? createLogSafeAcpClient(client, channel.transportGuard) : client,
|
|
@@ -7938,6 +9044,11 @@ function createAcpSessionBridge(opts) {
|
|
|
7938
9044
|
touchActivity();
|
|
7939
9045
|
}
|
|
7940
9046
|
byId.delete(sid);
|
|
9047
|
+
void sessEntry.attachments.close().catch((error) => {
|
|
9048
|
+
writeStderrLine(
|
|
9049
|
+
`qwen serve: failed to close attachments for closed channel session ${JSON.stringify(sid)}: ${error instanceof Error ? error.message : String(error)}`
|
|
9050
|
+
);
|
|
9051
|
+
});
|
|
7941
9052
|
telemetry.metrics?.sessionLifecycle("die");
|
|
7942
9053
|
emitSessionLifecycle({
|
|
7943
9054
|
type: "removed",
|
|
@@ -8685,6 +9796,41 @@ function createAcpSessionBridge(opts) {
|
|
|
8685
9796
|
}
|
|
8686
9797
|
return response;
|
|
8687
9798
|
}, "requestWorkspaceStatus");
|
|
9799
|
+
const MAX_CHILD_HEAP_UNCLASSIFIED_NAMES = 64;
|
|
9800
|
+
const MAX_CHILD_HEAP_SPACE_NAME_LENGTH = 64;
|
|
9801
|
+
const parseChildHeapReport = /* @__PURE__ */ __name((value) => {
|
|
9802
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
9803
|
+
const raw = value;
|
|
9804
|
+
const counts = [
|
|
9805
|
+
"peakOldGenerationBytes",
|
|
9806
|
+
"peakLiveSetBytes",
|
|
9807
|
+
"peakTotalHeapBytes",
|
|
9808
|
+
"majorGcCount",
|
|
9809
|
+
"majorGcMs"
|
|
9810
|
+
];
|
|
9811
|
+
const parsed = {};
|
|
9812
|
+
for (const key of counts) {
|
|
9813
|
+
const n = raw[key];
|
|
9814
|
+
if (typeof n !== "number" || !Number.isFinite(n) || n < 0) {
|
|
9815
|
+
return void 0;
|
|
9816
|
+
}
|
|
9817
|
+
parsed[key] = n;
|
|
9818
|
+
}
|
|
9819
|
+
const names = raw["unclassifiedSpaceNames"];
|
|
9820
|
+
if (!Array.isArray(names) || names.length > MAX_CHILD_HEAP_UNCLASSIFIED_NAMES || names.some(
|
|
9821
|
+
(name) => typeof name !== "string" || name.length > MAX_CHILD_HEAP_SPACE_NAME_LENGTH
|
|
9822
|
+
)) {
|
|
9823
|
+
return void 0;
|
|
9824
|
+
}
|
|
9825
|
+
return {
|
|
9826
|
+
peakOldGenerationBytes: parsed["peakOldGenerationBytes"],
|
|
9827
|
+
peakLiveSetBytes: parsed["peakLiveSetBytes"],
|
|
9828
|
+
peakTotalHeapBytes: parsed["peakTotalHeapBytes"],
|
|
9829
|
+
majorGcCount: parsed["majorGcCount"],
|
|
9830
|
+
majorGcMs: parsed["majorGcMs"],
|
|
9831
|
+
unclassifiedSpaceNames: names
|
|
9832
|
+
};
|
|
9833
|
+
}, "parseChildHeapReport");
|
|
8688
9834
|
const STALE_CHILD_RESOURCE_MS = 3e4;
|
|
8689
9835
|
let childResourceRefreshing = false;
|
|
8690
9836
|
const refreshChildResource = /* @__PURE__ */ __name(async () => {
|
|
@@ -8701,6 +9847,8 @@ function createAcpSessionBridge(opts) {
|
|
|
8701
9847
|
if (typeof res.cpuPercent === "number" && Number.isFinite(res.cpuPercent)) {
|
|
8702
9848
|
info.childCpuPercent = Math.min(100, Math.max(0, res.cpuPercent));
|
|
8703
9849
|
}
|
|
9850
|
+
const heap = parseChildHeapReport(res.heap);
|
|
9851
|
+
if (heap) info.childHeap = heap;
|
|
8704
9852
|
info.childResourceAt = Date.now();
|
|
8705
9853
|
} catch (err) {
|
|
8706
9854
|
teeServeDebugLine(
|
|
@@ -8720,6 +9868,10 @@ function createAcpSessionBridge(opts) {
|
|
|
8720
9868
|
return {
|
|
8721
9869
|
rssBytes: info.childRssBytes ?? 0,
|
|
8722
9870
|
cpuPercent: info.childCpuPercent ?? 0,
|
|
9871
|
+
// Deliberately not defaulted. Unlike rss/cpu, where 0 is a plausible
|
|
9872
|
+
// reading, a zeroed heap report would assert the child needed no old
|
|
9873
|
+
// generation — the one conclusion that must never be manufactured.
|
|
9874
|
+
heap: info.childHeap,
|
|
8723
9875
|
// Bounded by the guard above, so a caller summing several children's
|
|
8724
9876
|
// readings can say how far apart they were taken. Without it a sum of
|
|
8725
9877
|
// readings up to `STALE_CHILD_RESOURCE_MS` apart looks instantaneous.
|
|
@@ -8975,13 +10127,21 @@ function createAcpSessionBridge(opts) {
|
|
|
8975
10127
|
workspaceCwd,
|
|
8976
10128
|
persistence: createSessionArtifactPersistence(ci.connection, sessionId)
|
|
8977
10129
|
}),
|
|
10130
|
+
attachments: new SessionAttachmentStore(
|
|
10131
|
+
opts.sessionAttachmentsRoot,
|
|
10132
|
+
sessionId
|
|
10133
|
+
),
|
|
8978
10134
|
recordingDegraded: false,
|
|
8979
10135
|
closing: false,
|
|
8980
10136
|
cwdChangeQueue: Promise.resolve(),
|
|
8981
10137
|
promptQueue: Promise.resolve(),
|
|
8982
10138
|
pendingPromptCount: 0,
|
|
8983
10139
|
pendingAgentNotificationCount: 0,
|
|
10140
|
+
...opts.promptLedger ? { promptLedger: opts.promptLedger } : {},
|
|
8984
10141
|
pendingPromptList: [],
|
|
10142
|
+
terminalTurnStatuses: /* @__PURE__ */ new Map(),
|
|
10143
|
+
enrichedTerminalPromptIds: /* @__PURE__ */ new Set(),
|
|
10144
|
+
rewindGeneration: 0,
|
|
8985
10145
|
midTurnMessageQueue: [],
|
|
8986
10146
|
settledMidTurnMessageIds: [],
|
|
8987
10147
|
promotedMidTurnMessageIds: [],
|
|
@@ -9395,7 +10555,7 @@ function createAcpSessionBridge(opts) {
|
|
|
9395
10555
|
// Late attachers get the same ACP state the original restore
|
|
9396
10556
|
// caller saw; spawn-only sessions don't carry a state payload.
|
|
9397
10557
|
state: existing.restoreState ?? {},
|
|
9398
|
-
hasActivePrompt: existing.promptActive,
|
|
10558
|
+
hasActivePrompt: existing.promptActive || existing.goalTurnActive === true,
|
|
9399
10559
|
...replayFields,
|
|
9400
10560
|
...historyAnchorRecordId !== void 0 ? { historyAnchorRecordId } : {}
|
|
9401
10561
|
};
|
|
@@ -9477,7 +10637,7 @@ function createAcpSessionBridge(opts) {
|
|
|
9477
10637
|
attached: true,
|
|
9478
10638
|
clientId,
|
|
9479
10639
|
createdAt: entry.createdAt,
|
|
9480
|
-
hasActivePrompt: entry.promptActive,
|
|
10640
|
+
hasActivePrompt: entry.promptActive || entry.goalTurnActive === true,
|
|
9481
10641
|
...waiterReplayFields ?? {}
|
|
9482
10642
|
};
|
|
9483
10643
|
}
|
|
@@ -9840,7 +11000,7 @@ function createAcpSessionBridge(opts) {
|
|
|
9840
11000
|
...racedEntry.sourceType ? { sourceType: racedEntry.sourceType } : {},
|
|
9841
11001
|
...racedEntry.sourceId !== void 0 ? { sourceId: racedEntry.sourceId } : {},
|
|
9842
11002
|
state: racedEntry.restoreState ?? {},
|
|
9843
|
-
hasActivePrompt: racedEntry.promptActive,
|
|
11003
|
+
hasActivePrompt: racedEntry.promptActive || racedEntry.goalTurnActive === true,
|
|
9844
11004
|
...replayFieldsFor(racedEntry, action, liveReplayMode)
|
|
9845
11005
|
};
|
|
9846
11006
|
}
|
|
@@ -9916,7 +11076,7 @@ function createAcpSessionBridge(opts) {
|
|
|
9916
11076
|
...entry.sourceId !== void 0 ? { sourceId: entry.sourceId } : {},
|
|
9917
11077
|
state: publicState,
|
|
9918
11078
|
...artifactRestoreWarnings.length > 0 ? { artifactWarnings: artifactRestoreWarnings } : {},
|
|
9919
|
-
hasActivePrompt: entry.promptActive,
|
|
11079
|
+
hasActivePrompt: entry.promptActive || entry.goalTurnActive === true,
|
|
9920
11080
|
...replayFieldsFor(entry, action, liveReplayMode)
|
|
9921
11081
|
};
|
|
9922
11082
|
})().finally(async () => {
|
|
@@ -9931,6 +11091,11 @@ function createAcpSessionBridge(opts) {
|
|
|
9931
11091
|
const restoreEntry = byId.get(req.sessionId);
|
|
9932
11092
|
if (restoreEntry?.events === restoreEvents) {
|
|
9933
11093
|
byId.delete(req.sessionId);
|
|
11094
|
+
await restoreEntry.attachments.close().catch((error) => {
|
|
11095
|
+
writeStderrLine(
|
|
11096
|
+
`qwen serve: failed to close attachments after restoring session ${JSON.stringify(req.sessionId)}: ${error instanceof Error ? error.message : String(error)}`
|
|
11097
|
+
);
|
|
11098
|
+
});
|
|
9934
11099
|
ci?.sessionIds.delete(req.sessionId);
|
|
9935
11100
|
emitSessionLifecycle({
|
|
9936
11101
|
type: "removed",
|
|
@@ -10094,6 +11259,11 @@ function createAcpSessionBridge(opts) {
|
|
|
10094
11259
|
} catch {
|
|
10095
11260
|
}
|
|
10096
11261
|
entry.events.close();
|
|
11262
|
+
await entry.attachments.close().catch((error) => {
|
|
11263
|
+
writeStderrLine(
|
|
11264
|
+
`qwen serve: failed to close attachments for session ${JSON.stringify(sessionId)}: ${error instanceof Error ? error.message : String(error)}`
|
|
11265
|
+
);
|
|
11266
|
+
});
|
|
10097
11267
|
if (!agentSessionClosed) {
|
|
10098
11268
|
try {
|
|
10099
11269
|
await telemetry.withSpan(
|
|
@@ -10122,30 +11292,102 @@ function createAcpSessionBridge(opts) {
|
|
|
10122
11292
|
ring.splice(0, ring.length - MID_TURN_RECONCILIATION_RING_SIZE);
|
|
10123
11293
|
}
|
|
10124
11294
|
}, "rememberMidTurnId");
|
|
10125
|
-
const promoteMidTurnMessage = /* @__PURE__ */ __name((entry, messageId, text, originatorClientId) => {
|
|
10126
|
-
|
|
11295
|
+
const promoteMidTurnMessage = /* @__PURE__ */ __name((entry, messageId, text, originatorClientId, content) => {
|
|
11296
|
+
const resolvableBlocks = [];
|
|
11297
|
+
let degraded = 0;
|
|
11298
|
+
for (const block of content ?? []) {
|
|
11299
|
+
try {
|
|
11300
|
+
entry.attachments.assertReference(block);
|
|
11301
|
+
resolvableBlocks.push(block);
|
|
11302
|
+
} catch (error) {
|
|
11303
|
+
if (!(error instanceof SessionAttachmentReferenceError)) throw error;
|
|
11304
|
+
degraded += 1;
|
|
11305
|
+
}
|
|
11306
|
+
}
|
|
11307
|
+
let prompt = [
|
|
11308
|
+
...text ? [{ type: "text", text }] : [],
|
|
11309
|
+
...resolvableBlocks
|
|
11310
|
+
];
|
|
11311
|
+
if (degraded > 0) {
|
|
11312
|
+
prompt = withAttachmentDegradationMarker(prompt);
|
|
11313
|
+
}
|
|
11314
|
+
const context = {
|
|
11315
|
+
promptId: messageId,
|
|
11316
|
+
promotedMidTurn: { originatorClientId },
|
|
11317
|
+
onPromptAdmitted: /* @__PURE__ */ __name(() => {
|
|
11318
|
+
rememberMidTurnId(entry.promotedMidTurnMessageIds, messageId);
|
|
11319
|
+
}, "onPromptAdmitted")
|
|
11320
|
+
};
|
|
11321
|
+
const sendFallback = /* @__PURE__ */ __name(() => bridgeApi.sendPrompt(
|
|
10127
11322
|
entry.sessionId,
|
|
10128
11323
|
{
|
|
10129
11324
|
sessionId: entry.sessionId,
|
|
10130
|
-
prompt:
|
|
11325
|
+
prompt: withAttachmentDegradationMarker(
|
|
11326
|
+
text ? [{ type: "text", text }] : []
|
|
11327
|
+
)
|
|
10131
11328
|
},
|
|
10132
11329
|
void 0,
|
|
10133
|
-
|
|
10134
|
-
|
|
10135
|
-
|
|
10136
|
-
|
|
10137
|
-
|
|
10138
|
-
|
|
10139
|
-
|
|
10140
|
-
|
|
10141
|
-
|
|
10142
|
-
|
|
10143
|
-
|
|
11330
|
+
context
|
|
11331
|
+
), "sendFallback");
|
|
11332
|
+
let result;
|
|
11333
|
+
try {
|
|
11334
|
+
result = bridgeApi.sendPrompt(
|
|
11335
|
+
entry.sessionId,
|
|
11336
|
+
{
|
|
11337
|
+
sessionId: entry.sessionId,
|
|
11338
|
+
prompt
|
|
11339
|
+
},
|
|
11340
|
+
void 0,
|
|
11341
|
+
context
|
|
11342
|
+
);
|
|
11343
|
+
} catch (error) {
|
|
11344
|
+
try {
|
|
11345
|
+
if (!(error instanceof SessionAttachmentReferenceError)) throw error;
|
|
11346
|
+
result = sendFallback();
|
|
11347
|
+
} catch (fallbackError) {
|
|
11348
|
+
writeStderrLine(
|
|
11349
|
+
`[mid-turn] session=${JSON.stringify(entry.sessionId)} failed to run promoted message ${JSON.stringify(messageId)}: ${JSON.stringify(fallbackError instanceof Error ? fallbackError.message : String(fallbackError))}`
|
|
11350
|
+
);
|
|
11351
|
+
return;
|
|
11352
|
+
}
|
|
11353
|
+
}
|
|
11354
|
+
void result.catch((error) => {
|
|
10144
11355
|
writeStderrLine(
|
|
10145
11356
|
`[mid-turn] session=${JSON.stringify(entry.sessionId)} failed to run promoted message ${JSON.stringify(messageId)}: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
|
|
10146
11357
|
);
|
|
10147
11358
|
});
|
|
10148
11359
|
}, "promoteMidTurnMessage");
|
|
11360
|
+
const settleUndrainedMidTurnMessages = /* @__PURE__ */ __name((entry, messages) => {
|
|
11361
|
+
for (const message of messages) {
|
|
11362
|
+
if (message.queueOnly) {
|
|
11363
|
+
try {
|
|
11364
|
+
message.onSettledWithoutDrain?.();
|
|
11365
|
+
} catch (error) {
|
|
11366
|
+
writeStderrLine(
|
|
11367
|
+
`[mid-turn] session=${JSON.stringify(entry.sessionId)} failed to hand undrained queue-only message ${JSON.stringify(message.messageId)} back to its caller: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
|
|
11368
|
+
);
|
|
11369
|
+
}
|
|
11370
|
+
continue;
|
|
11371
|
+
}
|
|
11372
|
+
promoteMidTurnMessage(
|
|
11373
|
+
entry,
|
|
11374
|
+
message.messageId,
|
|
11375
|
+
message.text,
|
|
11376
|
+
message.originatorClientId,
|
|
11377
|
+
message.content
|
|
11378
|
+
);
|
|
11379
|
+
}
|
|
11380
|
+
}, "settleUndrainedMidTurnMessages");
|
|
11381
|
+
const settleMidTurnQueueAfterGoalTurn = /* @__PURE__ */ __name((sessionId) => {
|
|
11382
|
+
const entry = byId.get(sessionId);
|
|
11383
|
+
if (!entry) return;
|
|
11384
|
+
if (entry.goalTurnActive === true || entry.pendingPromptCount > 0 || entry.closing) {
|
|
11385
|
+
return;
|
|
11386
|
+
}
|
|
11387
|
+
const undrained = entry.midTurnMessageQueue.splice(0);
|
|
11388
|
+
if (undrained.length === 0) return;
|
|
11389
|
+
settleUndrainedMidTurnMessages(entry, undrained);
|
|
11390
|
+
}, "settleMidTurnQueueAfterGoalTurn");
|
|
10149
11391
|
const bridgeApi = {
|
|
10150
11392
|
setLiveScreenContextCaptureHandler(handler) {
|
|
10151
11393
|
liveScreenContextCaptureHandler = handler;
|
|
@@ -10188,7 +11430,7 @@ function createAcpSessionBridge(opts) {
|
|
|
10188
11430
|
attachCount: entry.attachCount,
|
|
10189
11431
|
pendingPromptCount: entry.pendingPromptCount,
|
|
10190
11432
|
pendingPermissionCount: entry.pendingPermissionIds.size,
|
|
10191
|
-
hasActivePrompt: entry.promptActive,
|
|
11433
|
+
hasActivePrompt: entry.promptActive || entry.goalTurnActive === true,
|
|
10192
11434
|
lastEventId: entry.events.lastEventId,
|
|
10193
11435
|
...entry.sessionLastSeenAt !== void 0 ? { lastSeenAt: entry.sessionLastSeenAt } : {},
|
|
10194
11436
|
...entry.currentModelId ? { currentModelId: entry.currentModelId } : {},
|
|
@@ -10341,7 +11583,7 @@ function createAcpSessionBridge(opts) {
|
|
|
10341
11583
|
createdAt: existing.createdAt,
|
|
10342
11584
|
...existing.sourceType ? { sourceType: existing.sourceType } : {},
|
|
10343
11585
|
...existing.sourceId !== void 0 ? { sourceId: existing.sourceId } : {},
|
|
10344
|
-
hasActivePrompt: existing.promptActive
|
|
11586
|
+
hasActivePrompt: existing.promptActive || existing.goalTurnActive === true
|
|
10345
11587
|
};
|
|
10346
11588
|
}
|
|
10347
11589
|
const inFlight = inFlightSpawns.get(workspaceKey);
|
|
@@ -10392,7 +11634,7 @@ function createAcpSessionBridge(opts) {
|
|
|
10392
11634
|
...session,
|
|
10393
11635
|
attached: true,
|
|
10394
11636
|
clientId,
|
|
10395
|
-
hasActivePrompt: attachedEntry.promptActive
|
|
11637
|
+
hasActivePrompt: attachedEntry.promptActive || attachedEntry.goalTurnActive === true
|
|
10396
11638
|
};
|
|
10397
11639
|
}
|
|
10398
11640
|
}
|
|
@@ -10490,9 +11732,15 @@ function createAcpSessionBridge(opts) {
|
|
|
10490
11732
|
)
|
|
10491
11733
|
);
|
|
10492
11734
|
}
|
|
11735
|
+
if (!Array.isArray(req.prompt)) {
|
|
11736
|
+
return Promise.reject(
|
|
11737
|
+
RequestError.invalidParams(void 0, "Prompt must be an array")
|
|
11738
|
+
);
|
|
11739
|
+
}
|
|
10493
11740
|
const promotedMidTurn = context?.promotedMidTurn;
|
|
10494
11741
|
const isPromotedMidTurn = promotedMidTurn !== void 0;
|
|
10495
11742
|
const originatorClientId = promotedMidTurn ? promotedMidTurn.originatorClientId : resolveTrustedClientId(entry, context?.clientId);
|
|
11743
|
+
entry.attachments.assertReferences(req.prompt);
|
|
10496
11744
|
const modelPrompt = context?.modelPrompt;
|
|
10497
11745
|
if (modelPrompt !== void 0 && !isValidTrustedModelPrompt(modelPrompt)) {
|
|
10498
11746
|
throw new TypeError(
|
|
@@ -10549,10 +11797,27 @@ function createAcpSessionBridge(opts) {
|
|
|
10549
11797
|
...originatorClientId !== void 0 ? { originatorClientId } : {},
|
|
10550
11798
|
...isPromotedMidTurn ? { promotedMidTurn: true } : {},
|
|
10551
11799
|
text: pendingText,
|
|
11800
|
+
content: extractMediaBlocks(req.prompt),
|
|
10552
11801
|
abortController: pendingAbort,
|
|
10553
11802
|
state: isQueued ? "queued" : "running"
|
|
10554
11803
|
};
|
|
10555
11804
|
entry.pendingPromptList.push(pendingEntry);
|
|
11805
|
+
let tailUuid;
|
|
11806
|
+
try {
|
|
11807
|
+
tailUuid = entry.promptLedger?.transcriptTailUuid?.(sessionId);
|
|
11808
|
+
} catch (error) {
|
|
11809
|
+
opts.onDiagnosticLine?.(
|
|
11810
|
+
`qwen serve: prompt ledger dispatch marker failed for session=${sessionId}: ${error instanceof Error ? error.message : String(error)}`,
|
|
11811
|
+
"warn"
|
|
11812
|
+
);
|
|
11813
|
+
}
|
|
11814
|
+
appendPromptLedgerBestEffort(entry, {
|
|
11815
|
+
v: 1,
|
|
11816
|
+
promptId,
|
|
11817
|
+
state: "in_flight",
|
|
11818
|
+
...tailUuid !== void 0 ? { tailUuid } : {},
|
|
11819
|
+
at: queuedAt
|
|
11820
|
+
});
|
|
10556
11821
|
try {
|
|
10557
11822
|
context?.onPromptAdmitted?.();
|
|
10558
11823
|
} catch (error) {
|
|
@@ -10641,6 +11906,7 @@ function createAcpSessionBridge(opts) {
|
|
|
10641
11906
|
}
|
|
10642
11907
|
throw new DOMException("Prompt aborted", "AbortError");
|
|
10643
11908
|
}
|
|
11909
|
+
pendingEntry.startedAt = Date.now();
|
|
10644
11910
|
if (pendingEntry.state === "queued" || isPromotedMidTurn) {
|
|
10645
11911
|
if (pendingEntry.state === "queued") {
|
|
10646
11912
|
delete entry.todoStopGuardAwaitingQueuedPromptOwnerPromptId;
|
|
@@ -10668,10 +11934,27 @@ function createAcpSessionBridge(opts) {
|
|
|
10668
11934
|
...context?.clientId ? { "qwen-code.client_id": context.clientId } : {}
|
|
10669
11935
|
},
|
|
10670
11936
|
async () => {
|
|
11937
|
+
let dispatchBlocks = req.prompt;
|
|
11938
|
+
let resolvedPrompt;
|
|
11939
|
+
try {
|
|
11940
|
+
resolvedPrompt = await entry.attachments.resolveContent(dispatchBlocks);
|
|
11941
|
+
} catch (error) {
|
|
11942
|
+
if (!isPromotedMidTurn || !(error instanceof SessionAttachmentReferenceError)) {
|
|
11943
|
+
throw error;
|
|
11944
|
+
}
|
|
11945
|
+
const perBlock = await entry.attachments.resolveContentDegrading(
|
|
11946
|
+
dispatchBlocks
|
|
11947
|
+
);
|
|
11948
|
+
dispatchBlocks = perBlock.retainedBlocks;
|
|
11949
|
+
resolvedPrompt = withAttachmentDegradationMarker(
|
|
11950
|
+
perBlock.resolvedBlocks
|
|
11951
|
+
);
|
|
11952
|
+
}
|
|
10671
11953
|
const normalized = telemetry.injectPromptContext(
|
|
10672
11954
|
{
|
|
10673
11955
|
...req,
|
|
10674
|
-
sessionId
|
|
11956
|
+
sessionId,
|
|
11957
|
+
prompt: resolvedPrompt
|
|
10675
11958
|
}
|
|
10676
11959
|
);
|
|
10677
11960
|
assertLivePromptEntry(sessionId, entry);
|
|
@@ -10694,6 +11977,7 @@ function createAcpSessionBridge(opts) {
|
|
|
10694
11977
|
delete meta[DAEMON_CHANNEL_DELIVERY_META_KEY];
|
|
10695
11978
|
delete meta[DAEMON_PROMPT_DISPLAY_TEXT_META_KEY];
|
|
10696
11979
|
delete meta[DAEMON_MODEL_PROMPT_META_KEY];
|
|
11980
|
+
delete meta[DAEMON_ATTACHMENT_REFERENCES_META_KEY];
|
|
10697
11981
|
delete meta[CHANNEL_PROMPT_META_KEY];
|
|
10698
11982
|
if (isRetry) {
|
|
10699
11983
|
meta[DAEMON_RETRY_META_KEY] = true;
|
|
@@ -10710,6 +11994,12 @@ function createAcpSessionBridge(opts) {
|
|
|
10710
11994
|
if (modelPrompt !== void 0) {
|
|
10711
11995
|
meta[DAEMON_MODEL_PROMPT_META_KEY] = modelPrompt;
|
|
10712
11996
|
}
|
|
11997
|
+
const attachmentReferences = dispatchBlocks.filter(
|
|
11998
|
+
isSessionAttachmentReference
|
|
11999
|
+
);
|
|
12000
|
+
if (attachmentReferences.length > 0) {
|
|
12001
|
+
meta[DAEMON_ATTACHMENT_REFERENCES_META_KEY] = attachmentReferences;
|
|
12002
|
+
}
|
|
10713
12003
|
if (context?.channelPrompt === true) {
|
|
10714
12004
|
meta[CHANNEL_PROMPT_META_KEY] = true;
|
|
10715
12005
|
}
|
|
@@ -10722,6 +12012,7 @@ function createAcpSessionBridge(opts) {
|
|
|
10722
12012
|
return copy;
|
|
10723
12013
|
})();
|
|
10724
12014
|
entry.promptActive = true;
|
|
12015
|
+
entry.goalTurnActive = false;
|
|
10725
12016
|
entry.activePromptId = pendingEntry.promptId;
|
|
10726
12017
|
delete entry.cancelBroadcastWithoutPrompt;
|
|
10727
12018
|
delete entry.turnError;
|
|
@@ -10738,7 +12029,10 @@ function createAcpSessionBridge(opts) {
|
|
|
10738
12029
|
if (!isRetry && !isContinue) {
|
|
10739
12030
|
echoPromptToSessionBus(
|
|
10740
12031
|
entry,
|
|
10741
|
-
|
|
12032
|
+
{
|
|
12033
|
+
...promptRequest,
|
|
12034
|
+
prompt: dispatchBlocks
|
|
12035
|
+
},
|
|
10742
12036
|
pendingEntry.promptId,
|
|
10743
12037
|
originatorClientId,
|
|
10744
12038
|
channelDisplayText
|
|
@@ -10879,24 +12173,7 @@ function createAcpSessionBridge(opts) {
|
|
|
10879
12173
|
const shouldSettleMidTurnQueue = entry.pendingPromptCount === 1 && !entry.closing && byId.get(entry.sessionId) === entry;
|
|
10880
12174
|
const undrainedMessages = shouldSettleMidTurnQueue ? entry.midTurnMessageQueue.splice(0) : [];
|
|
10881
12175
|
releasePromptSlot();
|
|
10882
|
-
|
|
10883
|
-
if (message.queueOnly) {
|
|
10884
|
-
try {
|
|
10885
|
-
message.onSettledWithoutDrain?.();
|
|
10886
|
-
} catch (error) {
|
|
10887
|
-
writeStderrLine(
|
|
10888
|
-
`[mid-turn] session=${JSON.stringify(entry.sessionId)} failed to hand undrained queue-only message ${JSON.stringify(message.messageId)} back to its caller: ${JSON.stringify(error instanceof Error ? error.message : String(error))}`
|
|
10889
|
-
);
|
|
10890
|
-
}
|
|
10891
|
-
continue;
|
|
10892
|
-
}
|
|
10893
|
-
promoteMidTurnMessage(
|
|
10894
|
-
entry,
|
|
10895
|
-
message.messageId,
|
|
10896
|
-
message.text,
|
|
10897
|
-
message.originatorClientId
|
|
10898
|
-
);
|
|
10899
|
-
}
|
|
12176
|
+
settleUndrainedMidTurnMessages(entry, undrainedMessages);
|
|
10900
12177
|
void maybeCloseIdleSession(entry, "prompt_settled");
|
|
10901
12178
|
}).catch(() => {
|
|
10902
12179
|
});
|
|
@@ -11141,6 +12418,22 @@ function createAcpSessionBridge(opts) {
|
|
|
11141
12418
|
`branchSession: agent returned invalid response: ${JSON.stringify(result)}`
|
|
11142
12419
|
);
|
|
11143
12420
|
}
|
|
12421
|
+
markSessionCatalogChanged();
|
|
12422
|
+
if (opts.sessionAttachmentsRoot) {
|
|
12423
|
+
const branchAttachments = new SessionAttachmentStore(
|
|
12424
|
+
opts.sessionAttachmentsRoot,
|
|
12425
|
+
result.newSessionId
|
|
12426
|
+
);
|
|
12427
|
+
try {
|
|
12428
|
+
await branchAttachments.copyFrom(entry.attachments);
|
|
12429
|
+
} catch (error) {
|
|
12430
|
+
writeStderrLine(
|
|
12431
|
+
`qwen serve: failed to copy attachments for branched session ${result.newSessionId}: ${error instanceof Error ? error.message : String(error)}`
|
|
12432
|
+
);
|
|
12433
|
+
} finally {
|
|
12434
|
+
await branchAttachments.close();
|
|
12435
|
+
}
|
|
12436
|
+
}
|
|
11144
12437
|
const rawBranchName = result.displayName ?? result.title;
|
|
11145
12438
|
const branchDisplayName = typeof rawBranchName === "string" ? rawBranchName : result.newSessionId.slice(0, 8);
|
|
11146
12439
|
if (!restoreBranch) {
|
|
@@ -11207,6 +12500,15 @@ function createAcpSessionBridge(opts) {
|
|
|
11207
12500
|
throw restoreErr;
|
|
11208
12501
|
}
|
|
11209
12502
|
const newEntry = byId.get(result.newSessionId);
|
|
12503
|
+
if (newEntry && !opts.sessionAttachmentsRoot) {
|
|
12504
|
+
try {
|
|
12505
|
+
await newEntry.attachments.copyFrom(entry.attachments);
|
|
12506
|
+
} catch (error) {
|
|
12507
|
+
writeStderrLine(
|
|
12508
|
+
`qwen serve: failed to copy attachments for branched session ${result.newSessionId}: ${error instanceof Error ? error.message : String(error)}`
|
|
12509
|
+
);
|
|
12510
|
+
}
|
|
12511
|
+
}
|
|
11210
12512
|
if (newEntry) newEntry.displayName = branchDisplayName;
|
|
11211
12513
|
let sourcePersisted;
|
|
11212
12514
|
if (newEntry?.sourceType) {
|
|
@@ -11334,6 +12636,7 @@ function createAcpSessionBridge(opts) {
|
|
|
11334
12636
|
const entry = byId.get(sessionId);
|
|
11335
12637
|
if (entry) {
|
|
11336
12638
|
entry.worktree = worktree;
|
|
12639
|
+
markSessionCatalogChanged();
|
|
11337
12640
|
}
|
|
11338
12641
|
},
|
|
11339
12642
|
async closeSession(sessionId, context, closeOpts) {
|
|
@@ -11359,6 +12662,7 @@ function createAcpSessionBridge(opts) {
|
|
|
11359
12662
|
const nextDisplayName = metadata.displayName || void 0;
|
|
11360
12663
|
if (entry.displayName !== nextDisplayName) {
|
|
11361
12664
|
entry.displayName = nextDisplayName;
|
|
12665
|
+
markSessionCatalogChanged();
|
|
11362
12666
|
writeStderrLine(
|
|
11363
12667
|
`qwen serve: updated session metadata ${JSON.stringify(sessionId)} displayName=${entry.displayName === void 0 ? "cleared" : "set"}` + (context?.clientId ? ` by client ${JSON.stringify(context.clientId)}` : "")
|
|
11364
12668
|
);
|
|
@@ -11421,7 +12725,7 @@ function createAcpSessionBridge(opts) {
|
|
|
11421
12725
|
return warnings.length > 0 ? { ...result, warnings } : result;
|
|
11422
12726
|
},
|
|
11423
12727
|
listWorkspaceSessions(workspaceCwd) {
|
|
11424
|
-
if (!
|
|
12728
|
+
if (!path4.isAbsolute(workspaceCwd)) return [];
|
|
11425
12729
|
const key = workspaceCwd === boundWorkspace ? boundWorkspace : canonicalizeWorkspace(workspaceCwd);
|
|
11426
12730
|
if (key !== boundWorkspace) return [];
|
|
11427
12731
|
const out = [];
|
|
@@ -11432,6 +12736,8 @@ function createAcpSessionBridge(opts) {
|
|
|
11432
12736
|
}
|
|
11433
12737
|
return out;
|
|
11434
12738
|
},
|
|
12739
|
+
getSessionCatalogVersion,
|
|
12740
|
+
markSessionCatalogChanged,
|
|
11435
12741
|
getSessionSummary(sessionId) {
|
|
11436
12742
|
const entry = byId.get(sessionId);
|
|
11437
12743
|
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
@@ -11748,6 +13054,18 @@ function createAcpSessionBridge(opts) {
|
|
|
11748
13054
|
{ taskId, taskKind }
|
|
11749
13055
|
);
|
|
11750
13056
|
},
|
|
13057
|
+
async controlSessionGoal(sessionId, request, context) {
|
|
13058
|
+
const entry = byId.get(sessionId);
|
|
13059
|
+
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
13060
|
+
const info = channelInfoForEntry(entry);
|
|
13061
|
+
if (!info || info.isDying) throw new SessionNotFoundError(sessionId);
|
|
13062
|
+
resolveTrustedClientId(entry, context?.clientId);
|
|
13063
|
+
return requestSessionStatus(
|
|
13064
|
+
sessionId,
|
|
13065
|
+
SERVE_CONTROL_EXT_METHODS.sessionGoalControl,
|
|
13066
|
+
{ request }
|
|
13067
|
+
);
|
|
13068
|
+
},
|
|
11751
13069
|
async clearSessionGoal(sessionId) {
|
|
11752
13070
|
return requestSessionStatus(
|
|
11753
13071
|
sessionId,
|
|
@@ -12191,14 +13509,95 @@ function createAcpSessionBridge(opts) {
|
|
|
12191
13509
|
const entry = byId.get(sessionId);
|
|
12192
13510
|
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
12193
13511
|
resolveTrustedClientId(entry, context?.clientId);
|
|
12194
|
-
return entry.pendingPromptList.filter((p) => !p.removed).map((p) => ({
|
|
13512
|
+
return entry.pendingPromptList.filter((p) => !p.removed && !p.terminalPublished).map((p) => ({
|
|
12195
13513
|
promptId: p.promptId,
|
|
12196
13514
|
text: p.text,
|
|
13515
|
+
...p.content ? { content: p.content } : {},
|
|
12197
13516
|
queuedAt: p.queuedAt,
|
|
12198
13517
|
state: p.state,
|
|
12199
13518
|
...p.originatorClientId !== void 0 ? { originatorClientId: p.originatorClientId } : {}
|
|
12200
13519
|
}));
|
|
12201
13520
|
},
|
|
13521
|
+
async getSessionTurnStatus(sessionId, context, promptId) {
|
|
13522
|
+
const entry = byId.get(sessionId);
|
|
13523
|
+
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
13524
|
+
resolveTrustedClientId(entry, context?.clientId);
|
|
13525
|
+
const liveBeforeRead = findLiveTurnStatus(entry, promptId);
|
|
13526
|
+
if (liveBeforeRead) return liveBeforeRead;
|
|
13527
|
+
if (promptId !== void 0 && entry.enrichedTerminalPromptIds.has(promptId)) {
|
|
13528
|
+
const enrichedTerminal = entry.terminalTurnStatuses.get(promptId);
|
|
13529
|
+
if (enrichedTerminal) return enrichedTerminal;
|
|
13530
|
+
}
|
|
13531
|
+
const rewindGenerationBeforeRead = entry.rewindGeneration;
|
|
13532
|
+
let result;
|
|
13533
|
+
try {
|
|
13534
|
+
result = await requestSessionStatus(
|
|
13535
|
+
sessionId,
|
|
13536
|
+
SERVE_CONTROL_EXT_METHODS.sessionTurnStatus,
|
|
13537
|
+
{ ...promptId !== void 0 ? { promptId } : {} },
|
|
13538
|
+
// Transcript scans of large histories exceed the 10s init default;
|
|
13539
|
+
// give the read the same budget as other transcript reads.
|
|
13540
|
+
SESSION_TRANSCRIPT_TIMEOUT_MS
|
|
13541
|
+
);
|
|
13542
|
+
} catch (error) {
|
|
13543
|
+
const liveAfterFailure = findLiveTurnStatus(entry, promptId);
|
|
13544
|
+
if (liveAfterFailure) return liveAfterFailure;
|
|
13545
|
+
const terminalAfterFailure = promptId !== void 0 ? entry.terminalTurnStatuses.get(promptId) : latestTerminalTurnStatus(entry);
|
|
13546
|
+
if (terminalAfterFailure) return terminalAfterFailure;
|
|
13547
|
+
throw error;
|
|
13548
|
+
}
|
|
13549
|
+
const liveAfterRead = findLiveTurnStatus(entry, promptId);
|
|
13550
|
+
if (liveAfterRead) return liveAfterRead;
|
|
13551
|
+
const terminal = promptId !== void 0 ? entry.terminalTurnStatuses.get(promptId) : latestTerminalTurnStatus(entry);
|
|
13552
|
+
const persisted = result.turnResult && entry.rewindGeneration === rewindGenerationBeforeRead ? settledTurnStatus(sessionId, result.turnResult) : void 0;
|
|
13553
|
+
if (promptId !== void 0) {
|
|
13554
|
+
if (terminal && persisted) {
|
|
13555
|
+
const merged = mergeTerminalWithPersisted(terminal, persisted);
|
|
13556
|
+
rememberEnrichedTerminalTurnStatus(entry, promptId, merged);
|
|
13557
|
+
return merged;
|
|
13558
|
+
}
|
|
13559
|
+
if (terminal) return terminal;
|
|
13560
|
+
if (persisted) {
|
|
13561
|
+
rememberEnrichedTerminalTurnStatus(entry, promptId, persisted);
|
|
13562
|
+
return persisted;
|
|
13563
|
+
}
|
|
13564
|
+
} else {
|
|
13565
|
+
if (terminal && persisted && terminal.promptId === persisted.promptId) {
|
|
13566
|
+
return mergeTerminalWithPersisted(terminal, persisted);
|
|
13567
|
+
}
|
|
13568
|
+
if (terminal && persisted) {
|
|
13569
|
+
return (terminal.endedAt ?? 0) >= (persisted.endedAt ?? 0) ? terminal : persisted;
|
|
13570
|
+
}
|
|
13571
|
+
if (terminal) return terminal;
|
|
13572
|
+
if (persisted) return persisted;
|
|
13573
|
+
}
|
|
13574
|
+
if (promptId !== void 0) {
|
|
13575
|
+
return void 0;
|
|
13576
|
+
}
|
|
13577
|
+
return { sessionId, state: "idle" };
|
|
13578
|
+
},
|
|
13579
|
+
async storeSessionAttachment(sessionId, data, mimeType, context, name) {
|
|
13580
|
+
const entry = byId.get(sessionId);
|
|
13581
|
+
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
13582
|
+
resolveTrustedClientId(entry, context?.clientId);
|
|
13583
|
+
return await entry.attachments.putAttachment(data, mimeType, name);
|
|
13584
|
+
},
|
|
13585
|
+
async readSessionAttachment(sessionId, attachmentId, context) {
|
|
13586
|
+
const entry = byId.get(sessionId);
|
|
13587
|
+
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
13588
|
+
resolveTrustedClientId(entry, context?.clientId);
|
|
13589
|
+
return await entry.attachments.read(attachmentId);
|
|
13590
|
+
},
|
|
13591
|
+
async removeSessionAttachment(sessionId, attachmentId, context) {
|
|
13592
|
+
const entry = byId.get(sessionId);
|
|
13593
|
+
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
13594
|
+
resolveTrustedClientId(entry, context?.clientId);
|
|
13595
|
+
return await entry.attachments.remove(attachmentId);
|
|
13596
|
+
},
|
|
13597
|
+
async deleteSessionAttachments(sessionId) {
|
|
13598
|
+
const store = byId.get(sessionId)?.attachments ?? new SessionAttachmentStore(opts.sessionAttachmentsRoot, sessionId);
|
|
13599
|
+
await store.delete();
|
|
13600
|
+
},
|
|
12202
13601
|
removePendingPrompt(sessionId, promptId, context) {
|
|
12203
13602
|
const entry = byId.get(sessionId);
|
|
12204
13603
|
if (!entry) throw new SessionNotFoundError(sessionId);
|
|
@@ -12242,7 +13641,10 @@ function createAcpSessionBridge(opts) {
|
|
|
12242
13641
|
context?.clientId
|
|
12243
13642
|
);
|
|
12244
13643
|
const trimmed = message.trim();
|
|
12245
|
-
|
|
13644
|
+
const mediaBlocks = (options?.content ?? []).filter(
|
|
13645
|
+
(block) => block.type === "image" || block.type === "resource"
|
|
13646
|
+
);
|
|
13647
|
+
if (trimmed.length === 0 && mediaBlocks.length === 0) {
|
|
12246
13648
|
writeStderrLine(
|
|
12247
13649
|
`[mid-turn] session=${entry.sessionId} rejected: empty`
|
|
12248
13650
|
);
|
|
@@ -12253,11 +13655,12 @@ function createAcpSessionBridge(opts) {
|
|
|
12253
13655
|
(queued) => queued.messageId === requestedMessageId
|
|
12254
13656
|
);
|
|
12255
13657
|
if (existing) {
|
|
12256
|
-
|
|
13658
|
+
const sameMedia = JSON.stringify(existing.content ?? []) === JSON.stringify(mediaBlocks);
|
|
13659
|
+
if (existing.text === trimmed && sameMedia) {
|
|
12257
13660
|
return { accepted: true, messageId: requestedMessageId };
|
|
12258
13661
|
}
|
|
12259
13662
|
writeStderrLine(
|
|
12260
|
-
`[mid-turn] session=${JSON.stringify(entry.sessionId)} rejected id ${JSON.stringify(requestedMessageId)}: text mismatch`
|
|
13663
|
+
`[mid-turn] session=${JSON.stringify(entry.sessionId)} rejected id ${JSON.stringify(requestedMessageId)}: text or content mismatch`
|
|
12261
13664
|
);
|
|
12262
13665
|
return { accepted: false };
|
|
12263
13666
|
}
|
|
@@ -12265,11 +13668,16 @@ function createAcpSessionBridge(opts) {
|
|
|
12265
13668
|
(pending) => pending.promptId === requestedMessageId
|
|
12266
13669
|
);
|
|
12267
13670
|
if (promoted) {
|
|
12268
|
-
|
|
13671
|
+
const promotedMedia = (promoted.content ?? []).filter(
|
|
13672
|
+
(block) => block.type === "image" || block.type === "resource"
|
|
13673
|
+
);
|
|
13674
|
+
const sameMedia = JSON.stringify(promotedMedia) === JSON.stringify(mediaBlocks);
|
|
13675
|
+
const promotedText = promoted.text === "[image]" && trimmed.length === 0 ? "" : promoted.text;
|
|
13676
|
+
if (promotedText === trimmed && sameMedia) {
|
|
12269
13677
|
return { accepted: true, messageId: requestedMessageId };
|
|
12270
13678
|
}
|
|
12271
13679
|
writeStderrLine(
|
|
12272
|
-
`[mid-turn] session=${JSON.stringify(entry.sessionId)} rejected promoted id ${JSON.stringify(requestedMessageId)}: text mismatch`
|
|
13680
|
+
`[mid-turn] session=${JSON.stringify(entry.sessionId)} rejected promoted id ${JSON.stringify(requestedMessageId)}: text or content mismatch`
|
|
12273
13681
|
);
|
|
12274
13682
|
return { accepted: false };
|
|
12275
13683
|
}
|
|
@@ -12286,15 +13694,35 @@ function createAcpSessionBridge(opts) {
|
|
|
12286
13694
|
);
|
|
12287
13695
|
return { accepted: false };
|
|
12288
13696
|
}
|
|
13697
|
+
entry.attachments.assertReferences(mediaBlocks);
|
|
13698
|
+
const inlineBytes = inlineAttachmentBlockBytes(mediaBlocks);
|
|
13699
|
+
if (inlineBytes > 0) {
|
|
13700
|
+
const queuedInlineBytes = entry.midTurnMessageQueue.reduce(
|
|
13701
|
+
(total, queued) => total + inlineAttachmentBlockBytes(queued.content ?? []),
|
|
13702
|
+
0
|
|
13703
|
+
);
|
|
13704
|
+
if (queuedInlineBytes + inlineBytes > MAX_QUEUED_INLINE_ATTACHMENT_BYTES) {
|
|
13705
|
+
writeStderrLine(
|
|
13706
|
+
`[mid-turn] session=${entry.sessionId} rejected: queued inline attachments exceed the ${MAX_QUEUED_INLINE_ATTACHMENT_BYTES}-byte session budget`
|
|
13707
|
+
);
|
|
13708
|
+
return { accepted: false };
|
|
13709
|
+
}
|
|
13710
|
+
}
|
|
12289
13711
|
const messageId = requestedMessageId ?? randomUUID2();
|
|
12290
|
-
if (entry.pendingPromptCount === 0) {
|
|
12291
|
-
if (options?.queueOnly) {
|
|
13712
|
+
if (entry.pendingPromptCount === 0 && entry.goalTurnActive !== true) {
|
|
13713
|
+
if (options?.queueOnly || options?.rejectIfIdle) {
|
|
12292
13714
|
writeStderrLine(
|
|
12293
13715
|
`[mid-turn] session=${JSON.stringify(entry.sessionId)} rejected id ${JSON.stringify(messageId)}: session idle`
|
|
12294
13716
|
);
|
|
12295
13717
|
return { accepted: false };
|
|
12296
13718
|
}
|
|
12297
|
-
promoteMidTurnMessage(
|
|
13719
|
+
promoteMidTurnMessage(
|
|
13720
|
+
entry,
|
|
13721
|
+
messageId,
|
|
13722
|
+
trimmed,
|
|
13723
|
+
originatorClientId,
|
|
13724
|
+
mediaBlocks.length > 0 ? mediaBlocks : void 0
|
|
13725
|
+
);
|
|
12298
13726
|
return { accepted: true, messageId };
|
|
12299
13727
|
}
|
|
12300
13728
|
if (entry.midTurnMessageQueue.length >= MAX_MID_TURN_QUEUE_DEPTH) {
|
|
@@ -12306,6 +13734,7 @@ function createAcpSessionBridge(opts) {
|
|
|
12306
13734
|
const queuedMessage = {
|
|
12307
13735
|
messageId,
|
|
12308
13736
|
text: trimmed,
|
|
13737
|
+
...mediaBlocks.length > 0 ? { content: mediaBlocks } : {},
|
|
12309
13738
|
originatorClientId,
|
|
12310
13739
|
...options?.queueOnly ? {
|
|
12311
13740
|
queueOnly: true,
|
|
@@ -12414,7 +13843,11 @@ function createAcpSessionBridge(opts) {
|
|
|
12414
13843
|
// should restore into their queue views.
|
|
12415
13844
|
messages: entry.midTurnMessageQueue.filter((message) => message.originatorClientId !== void 0).map((message) => ({
|
|
12416
13845
|
messageId: message.messageId,
|
|
12417
|
-
text: message.text
|
|
13846
|
+
text: message.text,
|
|
13847
|
+
// Carry the media blocks so a refreshed client can rebuild the
|
|
13848
|
+
// queued row with its attachments (the snapshot is the only
|
|
13849
|
+
// recovery source once the client's in-memory copy is gone).
|
|
13850
|
+
...message.content ? { content: message.content } : {}
|
|
12418
13851
|
})),
|
|
12419
13852
|
settledMessageIds: [...entry.settledMidTurnMessageIds],
|
|
12420
13853
|
promotedMessageIds: [...entry.promotedMidTurnMessageIds]
|
|
@@ -12716,6 +14149,9 @@ function createAcpSessionBridge(opts) {
|
|
|
12716
14149
|
}
|
|
12717
14150
|
throw err;
|
|
12718
14151
|
}
|
|
14152
|
+
entry.terminalTurnStatuses.clear();
|
|
14153
|
+
entry.enrichedTerminalPromptIds.clear();
|
|
14154
|
+
entry.rewindGeneration += 1;
|
|
12719
14155
|
const targetTurnIndex = response["targetTurnIndex"] ?? 0;
|
|
12720
14156
|
const filesChanged = response["filesChanged"] ?? [];
|
|
12721
14157
|
const filesFailed = response["filesFailed"] ?? [];
|
|
@@ -13098,6 +14534,11 @@ function createAcpSessionBridge(opts) {
|
|
|
13098
14534
|
closingChannel,
|
|
13099
14535
|
`force kill closing session ${JSON.stringify(sessionId)}`
|
|
13100
14536
|
);
|
|
14537
|
+
await entry.attachments.close().catch((releaseError) => {
|
|
14538
|
+
writeStderrLine(
|
|
14539
|
+
`qwen serve: failed to close attachments for killed session ${JSON.stringify(sessionId)}: ${releaseError instanceof Error ? releaseError.message : String(releaseError)}`
|
|
14540
|
+
);
|
|
14541
|
+
});
|
|
13101
14542
|
return true;
|
|
13102
14543
|
}
|
|
13103
14544
|
entry.closing = true;
|
|
@@ -13161,6 +14602,11 @@ function createAcpSessionBridge(opts) {
|
|
|
13161
14602
|
} catch {
|
|
13162
14603
|
}
|
|
13163
14604
|
entry.events.close();
|
|
14605
|
+
await entry.attachments.close().catch((error) => {
|
|
14606
|
+
writeStderrLine(
|
|
14607
|
+
`qwen serve: failed to close attachments for killed session ${JSON.stringify(sessionId)}: ${error instanceof Error ? error.message : String(error)}`
|
|
14608
|
+
);
|
|
14609
|
+
});
|
|
13164
14610
|
if (ci && hasNoChannelWork(ci)) {
|
|
13165
14611
|
await reapPendingEmptyChannel(ci);
|
|
13166
14612
|
if (!ci.isDying) {
|
|
@@ -13265,6 +14711,7 @@ function createAcpSessionBridge(opts) {
|
|
|
13265
14711
|
) : Promise.resolve();
|
|
13266
14712
|
const teardownResults = await Promise.allSettled([
|
|
13267
14713
|
...channels.map((ci) => ci.channel.kill()),
|
|
14714
|
+
...[...byId.values()].map((entry) => entry.attachments.close()),
|
|
13268
14715
|
...inFlightSessionAwaits,
|
|
13269
14716
|
...inFlightRestoreAwaits,
|
|
13270
14717
|
inFlightChannelAwait
|