agentic-qe 3.15.2 → 3.15.4
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/.claude/helpers/brain-checkpoint.cjs +46 -6
- package/.claude/hooks/aqe-hook.cjs +280 -51
- package/.claude/skills/skills-manifest.json +1 -1
- package/CHANGELOG.md +155 -0
- package/assets/skills/skills-manifest.json +1 -1
- package/dist/adapters/ag-ui/state-delta-cache.js +14 -5
- package/dist/adapters/trajectory-adapter.js +3 -1
- package/dist/cli/bundle.js +5 -5
- package/dist/cli/chunks/adapter-T54G3TBK.js +2 -0
- package/dist/cli/chunks/{agent-booster-wasm-664M7426.js → agent-booster-wasm-MHJBRXMK.js} +2 -2
- package/dist/cli/chunks/{agent-handler-E47ITH45.js → agent-handler-G64SD7J2.js} +2 -2
- package/dist/cli/chunks/{agent-memory-branch-CFWVOJX3.js → agent-memory-branch-G4UXFPKH.js} +2 -2
- package/dist/cli/chunks/aqe-learning-engine-HLCJJ3JM.js +2 -0
- package/dist/cli/chunks/{arena-ZU5CGBGH.js → arena-63M3M6EW.js} +2 -2
- package/dist/cli/chunks/{arena-JM3OY263.js → arena-QJMKYPHS.js} +4 -4
- package/dist/cli/chunks/{audit-HVBRC5AS.js → audit-UDFVTWSX.js} +3 -3
- package/dist/cli/chunks/base-VLD755UU.js +2 -0
- package/dist/cli/chunks/{better-sqlite3-45JLBQX3.js → better-sqlite3-JLX2G56I.js} +2 -2
- package/dist/cli/chunks/{brain-handler-FKDONZYA.js → brain-handler-PWF32LLW.js} +3 -3
- package/dist/cli/chunks/{branch-enumerator-M3DPS3D5.js → branch-enumerator-PB7QWXDP.js} +2 -2
- package/dist/cli/chunks/{browser-TG2TV6NR.js → browser-CYNV2F3U.js} +2 -2
- package/dist/cli/chunks/browser-workflow-6M6VLWRF.js +2 -0
- package/dist/cli/chunks/{chunk-JFFVYCTV.js → chunk-2534PXIV.js} +2 -2
- package/dist/cli/chunks/{chunk-GHXNRF63.js → chunk-2DRMTRBM.js} +2 -2
- package/dist/cli/chunks/{chunk-L77Z3Q6L.js → chunk-2ICC4H6K.js} +2 -2
- package/dist/cli/chunks/{chunk-EATZW5T2.js → chunk-2LL35V3A.js} +2 -2
- package/dist/cli/chunks/{chunk-MEUJWTOX.js → chunk-2MU4WCMQ.js} +2 -2
- package/dist/cli/chunks/{chunk-3RC5UMRJ.js → chunk-33IUCP34.js} +2 -2
- package/dist/cli/chunks/{chunk-UX4BOI6K.js → chunk-35A22AUL.js} +1 -1
- package/dist/cli/chunks/{chunk-Z7XCXHEB.js → chunk-3BDDOFYW.js} +2 -2
- package/dist/cli/chunks/chunk-3C2BFLYC.js +54 -0
- package/dist/cli/chunks/{chunk-56CE7UO4.js → chunk-3MSBVOYW.js} +6 -6
- package/dist/cli/chunks/{chunk-EDRIROCG.js → chunk-3ZIVBTX6.js} +2 -2
- package/dist/cli/chunks/{chunk-CRYZXTSQ.js → chunk-4GLR2S2M.js} +2 -2
- package/dist/cli/chunks/{chunk-DHSGCI6H.js → chunk-54ITMOFM.js} +6 -6
- package/dist/cli/chunks/{chunk-2AWU2E6R.js → chunk-54XTJBHV.js} +1 -1
- package/dist/cli/chunks/{chunk-JSTKSPYT.js → chunk-5B5D5KCV.js} +1 -1
- package/dist/cli/chunks/{chunk-NP2WWIZ3.js → chunk-5GWTJJEO.js} +1 -1
- package/dist/cli/chunks/{chunk-D75HDHX3.js → chunk-5H7MR6HP.js} +1 -1
- package/dist/cli/chunks/{chunk-NWYAV2UG.js → chunk-5SS2Z7JC.js} +2 -2
- package/dist/cli/chunks/chunk-5XXLWJU7.js +2 -0
- package/dist/cli/chunks/{chunk-B4H66LPR.js → chunk-6EHVPFHW.js} +1 -1
- package/dist/cli/chunks/{chunk-HSXLHNSS.js → chunk-6HO3XRXE.js} +2 -2
- package/dist/cli/chunks/chunk-6HTO4BLV.js +2 -0
- package/dist/cli/chunks/{chunk-SVHFSOGT.js → chunk-6QPW4OVH.js} +1 -1
- package/dist/cli/chunks/{chunk-BKZSTZDB.js → chunk-6TMUZGM2.js} +1 -1
- package/dist/cli/chunks/{chunk-2AOAEMJG.js → chunk-73MTUM6F.js} +2 -2
- package/dist/cli/chunks/{chunk-ANI3KPB7.js → chunk-73PCVMGA.js} +2 -2
- package/dist/cli/chunks/{chunk-HADOG2PT.js → chunk-76PRW2KY.js} +2 -2
- package/dist/cli/chunks/{chunk-NL6HKEPR.js → chunk-7INP6HZH.js} +3 -3
- package/dist/cli/chunks/{chunk-QB7H62A4.js → chunk-7X2IVMXN.js} +2 -2
- package/dist/cli/chunks/{chunk-ZVQSIH7Y.js → chunk-A4OPQJ4A.js} +8 -8
- package/dist/cli/chunks/{chunk-SKQTRYE7.js → chunk-ADF6LGHZ.js} +2 -2
- package/dist/cli/chunks/{chunk-5YS2DF5G.js → chunk-AHVN3UQZ.js} +2 -2
- package/dist/cli/chunks/{chunk-3XF7YTQC.js → chunk-AOKSLWON.js} +54 -54
- package/dist/cli/chunks/{chunk-CFDIYZGA.js → chunk-ASKPTGUU.js} +2 -2
- package/dist/cli/chunks/{chunk-LH3H4V7C.js → chunk-B7GJXQD2.js} +2 -2
- package/dist/cli/chunks/{chunk-HOBBYORR.js → chunk-B7J3O2WM.js} +1 -1
- package/dist/cli/chunks/{chunk-J4EJMAH3.js → chunk-BBKLVHDQ.js} +1 -1
- package/dist/cli/chunks/{chunk-WMNVFCKY.js → chunk-BND6M2JX.js} +2 -2
- package/dist/cli/chunks/{chunk-LRVR56PD.js → chunk-BPGFCUDO.js} +1 -1
- package/dist/cli/chunks/{chunk-O6QTCUSL.js → chunk-BY3IL56R.js} +1 -1
- package/dist/cli/chunks/{chunk-3KXKJSKS.js → chunk-BY5PLEPZ.js} +32 -32
- package/dist/cli/chunks/{chunk-DGKPGRBT.js → chunk-BZWZTGMF.js} +2 -2
- package/dist/cli/chunks/{chunk-AS27JP67.js → chunk-C5EN5WB6.js} +1 -1
- package/dist/cli/chunks/chunk-CFXFGQEN.js +15 -0
- package/dist/cli/chunks/{chunk-X4CADOSD.js → chunk-CQ3WSTJ2.js} +2 -2
- package/dist/cli/chunks/{chunk-HFF5ERIE.js → chunk-D7RVZ5C2.js} +2 -2
- package/dist/cli/chunks/{chunk-HS3O763N.js → chunk-DAYMT7TH.js} +21 -21
- package/dist/cli/chunks/{chunk-W6PLBRRS.js → chunk-DBIK4OFX.js} +1 -1
- package/dist/cli/chunks/{chunk-OWZ3F7DJ.js → chunk-DEJM2P5K.js} +1 -1
- package/dist/cli/chunks/{chunk-TC4B5D4Z.js → chunk-DF4Y3HPO.js} +1 -1
- package/dist/cli/chunks/{chunk-7UA7SMM4.js → chunk-DFJGXXCL.js} +1 -1
- package/dist/cli/chunks/{chunk-SZYK5JL7.js → chunk-DPL4ZXTE.js} +1 -1
- package/dist/cli/chunks/{chunk-6WVEICL2.js → chunk-DVE5SM5G.js} +4 -4
- package/dist/cli/chunks/{chunk-QR5ILM34.js → chunk-EGHK6NMH.js} +2 -2
- package/dist/cli/chunks/{chunk-E6GANSLP.js → chunk-EWUFVWRT.js} +2 -2
- package/dist/cli/chunks/{chunk-HBMBO4FD.js → chunk-EY5IUF7U.js} +2 -2
- package/dist/cli/chunks/{chunk-JCB6K24D.js → chunk-FDK2GF4G.js} +2 -2
- package/dist/cli/chunks/{chunk-QZNFZ5IQ.js → chunk-FRBCX46J.js} +2 -2
- package/dist/cli/chunks/{chunk-FCMCQ6OX.js → chunk-G3ZUB3HD.js} +2 -2
- package/dist/cli/chunks/{chunk-QGCFMACF.js → chunk-GAXTPQCZ.js} +2 -2
- package/dist/cli/chunks/{chunk-6O4KVU4K.js → chunk-GJVRQBLI.js} +2 -2
- package/dist/cli/chunks/{chunk-NLY5NUHZ.js → chunk-GLKLEHK3.js} +2 -2
- package/dist/cli/chunks/{chunk-IM67YHDK.js → chunk-HCDTLV5J.js} +2 -2
- package/dist/cli/chunks/{chunk-MY2XZHW3.js → chunk-HEDLZWPM.js} +1 -1
- package/dist/cli/chunks/{chunk-WUSSZ2S6.js → chunk-HEKCN5XQ.js} +2 -2
- package/dist/cli/chunks/{chunk-FRZVLRVR.js → chunk-HFSBONCS.js} +2 -2
- package/dist/cli/chunks/{chunk-IAOB2G3X.js → chunk-HX5KQK7I.js} +2 -2
- package/dist/cli/chunks/{chunk-BT6TZTCL.js → chunk-I43QRLLB.js} +2 -2
- package/dist/cli/chunks/{chunk-QJN5RW2U.js → chunk-I53365HG.js} +3 -3
- package/dist/cli/chunks/{chunk-55T7PDYP.js → chunk-I5A3HGO6.js} +2 -2
- package/dist/cli/chunks/{chunk-AR5V7VNC.js → chunk-I6E4X3L3.js} +1 -1
- package/dist/cli/chunks/chunk-IKWAPHJA.js +2 -0
- package/dist/cli/chunks/{chunk-UKRK3IE3.js → chunk-ISASRHZP.js} +2 -2
- package/dist/cli/chunks/{chunk-IHKRKRAV.js → chunk-J3RNVHDH.js} +1 -1
- package/dist/cli/chunks/{chunk-T75PWWK4.js → chunk-JFQDWAU3.js} +2 -2
- package/dist/cli/chunks/{chunk-3KTAMNKI.js → chunk-JL3TI43V.js} +2 -2
- package/dist/cli/chunks/{chunk-3DCRAW43.js → chunk-K2VUYRMG.js} +2 -2
- package/dist/cli/chunks/{chunk-RIAKRZCZ.js → chunk-KOLHZSEC.js} +2 -2
- package/dist/cli/chunks/{chunk-HXT4XDQO.js → chunk-LGA6J4I4.js} +2 -2
- package/dist/cli/chunks/{chunk-MDDECCAC.js → chunk-LJRZAALA.js} +169 -168
- package/dist/cli/chunks/{chunk-2B2MGJ2W.js → chunk-LOLAEDZD.js} +2 -2
- package/dist/cli/chunks/{chunk-2JEPRQMT.js → chunk-LPSE6DOQ.js} +1 -1
- package/dist/cli/chunks/{chunk-6KEYA4EH.js → chunk-LRTF5RMG.js} +4 -4
- package/dist/cli/chunks/{chunk-CV7E627D.js → chunk-LXYTC62V.js} +2 -2
- package/dist/cli/chunks/{chunk-YYVLMW65.js → chunk-LY53L6NH.js} +2 -2
- package/dist/cli/chunks/{chunk-HMDSBWF6.js → chunk-LZSLJJII.js} +2 -2
- package/dist/cli/chunks/{chunk-IT753IPZ.js → chunk-M6SJALLA.js} +2 -2
- package/dist/cli/chunks/{chunk-GDSLQTJC.js → chunk-MBV57LKC.js} +2 -2
- package/dist/cli/chunks/{chunk-I7J2ESGR.js → chunk-MGH4XIHH.js} +2 -2
- package/dist/cli/chunks/{chunk-Q32UGPWK.js → chunk-MVTUFPM2.js} +2 -2
- package/dist/cli/chunks/{chunk-UZL4JYVW.js → chunk-N6CRIUTO.js} +2 -2
- package/dist/cli/chunks/{chunk-326V7N25.js → chunk-NBMQOP2R.js} +1 -1
- package/dist/cli/chunks/{chunk-ZIKH7U5A.js → chunk-NIJMLEAX.js} +2 -2
- package/dist/cli/chunks/{chunk-MYDZBABD.js → chunk-O7TMY4GS.js} +2 -2
- package/dist/cli/chunks/{chunk-LVDZ25VX.js → chunk-OFQDM73J.js} +2 -2
- package/dist/cli/chunks/{chunk-WBZIOTY6.js → chunk-OG4KHX7S.js} +2 -2
- package/dist/cli/chunks/{chunk-GJTH6VBH.js → chunk-OHH6BUZK.js} +1 -1
- package/dist/cli/chunks/{chunk-T5N2PIPS.js → chunk-OOK6GPJR.js} +2 -2
- package/dist/cli/chunks/{chunk-AXJ75X6I.js → chunk-P7GM3EE7.js} +1 -1
- package/dist/cli/chunks/{chunk-IQB5ZBYO.js → chunk-PB2WSCBW.js} +3 -3
- package/dist/cli/chunks/{chunk-M36DAETL.js → chunk-PCWZCXBD.js} +2 -2
- package/dist/cli/chunks/{chunk-Z3PANQUT.js → chunk-PEA4DVXW.js} +1 -1
- package/dist/cli/chunks/{chunk-WBCE6V2N.js → chunk-PJAXWCFC.js} +1 -1
- package/dist/cli/chunks/{chunk-V67UQRQN.js → chunk-PMXWDTDK.js} +2 -2
- package/dist/cli/chunks/chunk-QIAX3JJB.js +3 -0
- package/dist/cli/chunks/chunk-R47LNH5W.js +2 -0
- package/dist/cli/chunks/{chunk-A6L2OCMO.js → chunk-RA3MI5WK.js} +2 -2
- package/dist/cli/chunks/{chunk-ITM26I6O.js → chunk-RB2SL5GD.js} +2 -2
- package/dist/cli/chunks/{chunk-XQBH3366.js → chunk-RG67RZIX.js} +2 -2
- package/dist/cli/chunks/{chunk-NBV37CEK.js → chunk-RI4JPBZD.js} +2 -2
- package/dist/cli/chunks/{chunk-2Q4Y3VQ6.js → chunk-RJ5CZ7B6.js} +2 -2
- package/dist/cli/chunks/{chunk-ATGIGJSL.js → chunk-RJMBHZYL.js} +1 -1
- package/dist/cli/chunks/{chunk-MOSXT6AO.js → chunk-RWZ4F44Y.js} +2 -2
- package/dist/cli/chunks/{chunk-URWT3EQT.js → chunk-S5D3HCGN.js} +2 -2
- package/dist/cli/chunks/{chunk-KQMCCHPO.js → chunk-SCL2OYN7.js} +2 -2
- package/dist/cli/chunks/{chunk-Y5VPLOA7.js → chunk-SIUUAKCE.js} +1 -1
- package/dist/cli/chunks/{chunk-JIRX6KUR.js → chunk-SJB4KMZS.js} +3 -3
- package/dist/cli/chunks/{chunk-TZH2QAFZ.js → chunk-SOSJFEFB.js} +2 -2
- package/dist/cli/chunks/{chunk-IBAXNCR2.js → chunk-SZWXUG36.js} +2 -2
- package/dist/cli/chunks/{chunk-KTLYL3BS.js → chunk-T3BMCEQR.js} +2 -2
- package/dist/cli/chunks/chunk-TB22Y3WW.js +4 -0
- package/dist/cli/chunks/{chunk-GJ7DON5S.js → chunk-TVQPYP2Z.js} +3 -3
- package/dist/cli/chunks/chunk-U2E5F3VZ.js +14 -0
- package/dist/cli/chunks/{chunk-ZSKQPXRW.js → chunk-UK5HY6LC.js} +1 -1
- package/dist/cli/chunks/{chunk-RO6HAZLV.js → chunk-UORI2CZ4.js} +1 -1
- package/dist/cli/chunks/{chunk-VTGAPHJE.js → chunk-V3NZ23X6.js} +2 -2
- package/dist/cli/chunks/{chunk-W7P7EOJ4.js → chunk-VAXQUD5G.js} +1 -1
- package/dist/cli/chunks/{chunk-EQG3JYSV.js → chunk-VMSO6DMF.js} +2 -2
- package/dist/cli/chunks/{chunk-6UO5YSNN.js → chunk-W3PPYNHF.js} +2 -2
- package/dist/cli/chunks/{chunk-FH6N2LKG.js → chunk-W6TJXK5Y.js} +2 -2
- package/dist/cli/chunks/{chunk-WJ7657IX.js → chunk-WEDVLKRB.js} +2 -2
- package/dist/cli/chunks/{chunk-N653RGT3.js → chunk-WI5AEB7Q.js} +2 -2
- package/dist/cli/chunks/{chunk-W5EKSBS4.js → chunk-WO4NSPLX.js} +2 -2
- package/dist/cli/chunks/{chunk-YH7VOLBP.js → chunk-WW6WUMS7.js} +2 -2
- package/dist/cli/chunks/{chunk-6OL2WES5.js → chunk-WYO5VN2L.js} +1 -1
- package/dist/cli/chunks/{chunk-PZK7O445.js → chunk-X5S5UEM6.js} +2 -2
- package/dist/cli/chunks/{chunk-NKWMTXBL.js → chunk-XHCCH7UC.js} +3 -3
- package/dist/cli/chunks/{chunk-GSC6JXDE.js → chunk-XKIFNKHV.js} +1 -1
- package/dist/cli/chunks/{chunk-FNPZINHV.js → chunk-XZ5Z56OP.js} +1 -1
- package/dist/cli/chunks/{chunk-6XLCFDIB.js → chunk-YQ6BK7QC.js} +2 -2
- package/dist/cli/chunks/{chunk-DNS37AHW.js → chunk-YUWUFYHK.js} +1 -1
- package/dist/cli/chunks/{chunk-YJSKT6G6.js → chunk-YZ2AGC7O.js} +1 -1
- package/dist/cli/chunks/{chunk-5TEPPW4J.js → chunk-Z56QTRRB.js} +2 -2
- package/dist/cli/chunks/{chunk-4MCO43NX.js → chunk-ZAOQIVHG.js} +4 -4
- package/dist/cli/chunks/{chunk-TMYGGYRY.js → chunk-ZF5JTQEE.js} +1 -1
- package/dist/cli/chunks/{chunk-HYWW4A2E.js → chunk-ZKXZTIVF.js} +2 -2
- package/dist/cli/chunks/{chunk-N4MNSEKY.js → chunk-ZP6ANUP7.js} +1 -1
- package/dist/cli/chunks/{chunk-5DFPAJ5O.js → chunk-ZURYZEHX.js} +2 -2
- package/dist/cli/chunks/{ci-JNBOR6FP.js → ci-NGMGB43T.js} +3 -3
- package/dist/cli/chunks/{ci-output-FYNKONEQ.js → ci-output-BMJABAEX.js} +2 -2
- package/dist/cli/chunks/{circuit-breaker-KOIXRDAD.js → circuit-breaker-GYD5CMJQ.js} +2 -2
- package/dist/cli/chunks/{claude-flow-setup-4CO7KHRN.js → claude-flow-setup-MMOYVBVD.js} +2 -2
- package/dist/cli/chunks/client-FUHNNK5O.js +2 -0
- package/dist/cli/chunks/{cline-installer-CMYAPKK5.js → cline-installer-B2SOZ44R.js} +2 -2
- package/dist/cli/chunks/{code-WFIAEP3H.js → code-V3FIRAXD.js} +3 -3
- package/dist/cli/chunks/{code-index-extractor-IYM6HWJN.js → code-index-extractor-CV6A756Z.js} +2 -2
- package/dist/cli/chunks/{codex-installer-DORVWRSU.js → codex-installer-EUC6J47Y.js} +2 -2
- package/dist/cli/chunks/{completions-54VM2MX2.js → completions-DQQIBAHR.js} +2 -2
- package/dist/cli/chunks/{complexity-analyzer-EGZDP5N4.js → complexity-analyzer-HPXMXP5I.js} +2 -2
- package/dist/cli/chunks/{continuedev-installer-4SDVDD4T.js → continuedev-installer-RCIYXH6V.js} +2 -2
- package/dist/cli/chunks/{copilot-installer-7FJQ7PCL.js → copilot-installer-VUH32SHF.js} +2 -2
- package/dist/cli/chunks/core-handlers-F7TLX4LM.js +2 -0
- package/dist/cli/chunks/cost-tracker-YLCKLOAL.js +2 -0
- package/dist/cli/chunks/{coverage-DI5LDRJ7.js → coverage-IVKDV236.js} +3 -3
- package/dist/cli/chunks/cross-domain-router-EGWPY67O.js +2 -0
- package/dist/cli/chunks/{cursor-installer-RTSZP5YD.js → cursor-installer-B42FN62K.js} +2 -2
- package/dist/cli/chunks/{daemon-SP3APEZY.js → daemon-DECQRQII.js} +3 -3
- package/dist/cli/chunks/{daemon-LM7LETPV.js → daemon-IPTOSCHM.js} +4 -4
- package/dist/cli/chunks/{dag-attention-scheduler-C3QBODFE.js → dag-attention-scheduler-RY7CDYSC.js} +2 -2
- package/dist/cli/chunks/{detect-I4RBUFFJ.js → detect-NW53BMJN.js} +2 -2
- package/dist/cli/chunks/{dist-node-PT6TCBCO.js → dist-node-LE6KHXAB.js} +2 -2
- package/dist/cli/chunks/{domain-handler-MJMTMB5X.js → domain-handler-JGXFT4LW.js} +2 -2
- package/dist/cli/chunks/{domain-plugins-Y4A2WYJW.js → domain-plugins-GY3RYKKK.js} +2 -2
- package/dist/cli/chunks/{domain-transfer-DUO42TLA.js → domain-transfer-CGBRXNTC.js} +2 -2
- package/dist/cli/chunks/dream-V4QN6OF5.js +2 -0
- package/dist/cli/chunks/{embed-and-insert-pattern-66ZQK4AI.js → embed-and-insert-pattern-65H4XHWR.js} +2 -2
- package/dist/cli/chunks/{eval-NQ7H27UF.js → eval-YPYIZQUR.js} +2 -2
- package/dist/cli/chunks/{experience-capture-middleware-MHEYPTIG.js → experience-capture-middleware-4JXVF5PL.js} +3 -3
- package/dist/cli/chunks/{fast-paths-YYDGN6ZC.js → fast-paths-HYQTAF2S.js} +2 -2
- package/dist/cli/chunks/{feature-flags-FY4DOYS2.js → feature-flags-HNSAFFX2.js} +2 -2
- package/dist/cli/chunks/{feature-flags-YFGUORHY.js → feature-flags-YACUH6OW.js} +2 -2
- package/dist/cli/chunks/{file-discovery-TOMDY7TK.js → file-discovery-R73BYEXB.js} +2 -2
- package/dist/cli/chunks/{fleet-MO3UCWB4.js → fleet-6M3BP4OO.js} +3 -3
- package/dist/cli/chunks/{gnn-wrapper-SLW4PPXQ.js → gnn-wrapper-QLD7KHXV.js} +2 -2
- package/dist/cli/chunks/{heartbeat-handler-36RFCP4E.js → heartbeat-handler-EZP333X4.js} +4 -4
- package/dist/cli/chunks/heartbeat-scheduler-3N3L6CRM.js +2 -0
- package/dist/cli/chunks/hnsw-adapter-2BXJYDXI.js +2 -0
- package/dist/cli/chunks/hnsw-index-2DLI5OVO.js +2 -0
- package/dist/cli/chunks/{hnsw-legacy-bridge-BJLWBKHE.js → hnsw-legacy-bridge-47CUCSAU.js} +2 -2
- package/dist/cli/chunks/{hnswlib-node-RAYNVJCJ.js → hnswlib-node-SVNYDJZH.js} +2 -2
- package/dist/cli/chunks/{hooks-JMHFWVEQ.js → hooks-6V5LMWP6.js} +10 -10
- package/dist/cli/chunks/hybrid-backend-OW4J65YH.js +2 -0
- package/dist/cli/chunks/{hypergraph-engine-JUDBKPOX.js → hypergraph-engine-L2FOBIFF.js} +2 -2
- package/dist/cli/chunks/{hypergraph-handler-AXTWXOZU.js → hypergraph-handler-DU6Z57EE.js} +3 -3
- package/dist/cli/chunks/impact-analyzer-ZH5I2RG6.js +2 -0
- package/dist/cli/chunks/{init-handler-PARJNZFF.js → init-handler-HETPWU67.js} +6 -6
- package/dist/cli/chunks/init-wizard-NIKM3ZOK.js +2 -0
- package/dist/cli/chunks/kernel-2EOSVM6X.js +2 -0
- package/dist/cli/chunks/{kilocode-installer-22HX2E4V.js → kilocode-installer-NHKYGJ7Q.js} +2 -2
- package/dist/cli/chunks/{kiro-installer-OVPBLY7J.js → kiro-installer-24UK6GGR.js} +2 -2
- package/dist/cli/chunks/knowledge-graph-D3BPWNNO.js +2 -0
- package/dist/cli/chunks/{learning-DWXPJ3R6.js → learning-QLVRZNHT.js} +3 -3
- package/dist/cli/chunks/{llm-router-7PPVVKNG.js → llm-router-XRMI2VMG.js} +4 -4
- package/dist/cli/chunks/llm-router-service-TJPPZFUN.js +2 -0
- package/dist/cli/chunks/{load-VI5XHNZA.js → load-W5UWL5C7.js} +2 -2
- package/dist/cli/chunks/load-test-RFT7IDOL.js +2 -0
- package/dist/cli/chunks/{mcp-UW4Z3Z64.js → mcp-N7ALQ4BL.js} +2 -2
- package/dist/cli/chunks/{memory-UD2KCB5M.js → memory-WV52DLSA.js} +5 -5
- package/dist/cli/chunks/memory-backend-O2O4CFKZ.js +2 -0
- package/dist/cli/chunks/memory-handlers-DAEGUDBD.js +2 -0
- package/dist/cli/chunks/{multi-model-executor-5O4WWU5Z.js → multi-model-executor-3FVTZCSO.js} +2 -2
- package/dist/cli/chunks/{opencode-installer-DTRFDGM7.js → opencode-installer-NTPWGHUW.js} +2 -2
- package/dist/cli/chunks/{orchestrator-ENJIKV6Z.js → orchestrator-ORGJ6VAA.js} +85 -26
- package/dist/cli/chunks/pattern-null-store-TUBNXS34.js +2 -0
- package/dist/cli/chunks/{pattern-usage-recorder-QPNIU5PD.js → pattern-usage-recorder-WWXHUH5U.js} +2 -2
- package/dist/cli/chunks/{pipeline-G3JYDNPE.js → pipeline-ZOKZFXTH.js} +2 -2
- package/dist/cli/chunks/{platform-P4WR6N3D.js → platform-CDNL322F.js} +2 -2
- package/dist/cli/chunks/{plugin-C7763Q5W.js → plugin-ZVLFNZZ4.js} +2 -2
- package/dist/cli/chunks/{prime-radiant-advanced-wasm-DH6R3XSZ.js → prime-radiant-advanced-wasm-FVJDU2JY.js} +2 -2
- package/dist/cli/chunks/{project-root-7DTXDLEG.js → project-root-UCHQH3QL.js} +2 -2
- package/dist/cli/chunks/protocol-executor-6FQTTO3Y.js +2 -0
- package/dist/cli/chunks/{protocol-handler-EODULGOZ.js → protocol-handler-4YBN5R23.js} +2 -2
- package/dist/cli/chunks/{prove-A7G3SIOF.js → prove-BHDHY664.js} +2 -2
- package/dist/cli/chunks/qe-reasoning-bank-TKPND56C.js +2 -0
- package/dist/cli/chunks/{quality-JTURFUX3.js → quality-46AK277P.js} +2 -2
- package/dist/cli/chunks/{quality-gate-M2EI754V.js → quality-gate-WANE7TCN.js} +3 -3
- package/dist/cli/chunks/queen-coordinator-DWMXLPJG.js +2 -0
- package/dist/cli/chunks/{real-embeddings-DI7JMCG7.js → real-embeddings-56MNGJGJ.js} +2 -2
- package/dist/cli/chunks/{ref-validation-MSFMNKDR.js → ref-validation-S7UWXQIP.js} +2 -2
- package/dist/cli/chunks/{roocode-installer-XIMFFHOH.js → roocode-installer-TEYOQYL6.js} +2 -2
- package/dist/cli/chunks/router-QIMUYRCC.js +2 -0
- package/dist/cli/chunks/routing-feedback-4OH5ZNF4.js +2 -0
- package/dist/cli/chunks/{routing-handler-MCDKNBQW.js → routing-handler-KDTLMRWD.js} +2 -2
- package/dist/cli/chunks/{ruvector-commands-OBSYAK2X.js → ruvector-commands-AOUDXCYE.js} +2 -2
- package/dist/cli/chunks/rvf-dual-writer-S6JBZL54.js +2 -0
- package/dist/cli/chunks/rvf-migration-adapter-TSGMLMYR.js +2 -0
- package/dist/cli/chunks/{rvf-migration-coordinator-4DTRIHY6.js → rvf-migration-coordinator-V74MVBHS.js} +2 -2
- package/dist/cli/chunks/rvf-native-adapter-Z7QOW47S.js +2 -0
- package/dist/cli/chunks/{rvf-store-integrity-CWAVMRC4.js → rvf-store-integrity-QI47Y354.js} +2 -2
- package/dist/cli/chunks/safe-db-JLOVBJNC.js +2 -0
- package/dist/cli/chunks/schedule-IM6XOBGR.js +2 -0
- package/dist/cli/chunks/scheduler-XL2SVVK5.js +2 -0
- package/dist/cli/chunks/{security-74TWSO74.js → security-SN6MWOJT.js} +3 -3
- package/dist/cli/chunks/{shared-rvf-adapter-ZHNAGE5H.js → shared-rvf-adapter-2LB56LP4.js} +2 -2
- package/dist/cli/chunks/{shared-rvf-dual-writer-BS5WAVCC.js → shared-rvf-dual-writer-WU7PFKQM.js} +2 -2
- package/dist/cli/chunks/sqlite-persistence-LWMIIC4U.js +2 -0
- package/dist/cli/chunks/{standalone-memory-3HGHTE7S.js → standalone-memory-F6FSRFWC.js} +2 -2
- package/dist/cli/chunks/{status-handler-XWEEGNGH.js → status-handler-DHD5MS4G.js} +2 -2
- package/dist/cli/chunks/{structural-health-TB5JGMVS.js → structural-health-P6HPSQMR.js} +2 -2
- package/dist/cli/chunks/{sync-T4VLBURM.js → sync-LISJGDSE.js} +2 -2
- package/dist/cli/chunks/sync-SNAXAVCN.js +2 -0
- package/dist/cli/chunks/{task-handler-RQ65QKXX.js → task-handler-ZT44NT5Z.js} +2 -2
- package/dist/cli/chunks/{task-handlers-USYWJ4H2.js → task-handlers-R6DQ5324.js} +3 -3
- package/dist/cli/chunks/test-MUFHR4JQ.js +34 -0
- package/dist/cli/chunks/test-scheduling-TAUSLZJP.js +15 -0
- package/dist/cli/chunks/token-bootstrap-UGRH7OPU.js +2 -0
- package/dist/cli/chunks/{token-usage-CISTB5K5.js → token-usage-IX65ECBR.js} +2 -2
- package/dist/cli/chunks/{transformers-C7LV763N.js → transformers-QKPSVW2W.js} +2 -2
- package/dist/cli/chunks/{tree-sitter-wasm-parser-P26JC3YV.js → tree-sitter-wasm-parser-Z7PPA2JO.js} +2 -2
- package/dist/cli/chunks/{types-YMVMKIWB.js → types-ULNNGGGH.js} +2 -2
- package/dist/cli/chunks/unified-memory-F5IJY7W7.js +2 -0
- package/dist/cli/chunks/unified-memory-hnsw-C3WAMAQL.js +2 -0
- package/dist/cli/chunks/unified-persistence-EWXXOTCB.js +2 -0
- package/dist/cli/chunks/{upgrade-U2REOPAW.js → upgrade-ECI3YUYA.js} +2 -2
- package/dist/cli/chunks/{validate-NDTCKN7P.js → validate-3JKRUW2S.js} +2 -2
- package/dist/cli/chunks/{validate-swarm-OJUGVH5Q.js → validate-swarm-LZWANCIE.js} +2 -2
- package/dist/cli/chunks/visual-security-CKLTAIAB.js +2 -0
- package/dist/cli/chunks/{web-tree-sitter-2CLRSA7K.js → web-tree-sitter-N5V4A6XG.js} +2 -2
- package/dist/cli/chunks/{windsurf-installer-DSUJJ4PG.js → windsurf-installer-5TBM5B3C.js} +2 -2
- package/dist/cli/chunks/{witness-chain-TEGL4ZWB.js → witness-chain-MFP3HBWQ.js} +2 -2
- package/dist/cli/chunks/witness-chain-Z6LWFEZS.js +2 -0
- package/dist/cli/chunks/witness-chain-repair-5QWRFVUK.js +2 -0
- package/dist/cli/chunks/{witness-chain-verifier-SPXKFC67.js → witness-chain-verifier-SRAT7LVY.js} +2 -2
- package/dist/cli/chunks/{witness-key-manager-EALOV4OV.js → witness-key-manager-FYZXQTLJ.js} +2 -2
- package/dist/cli/chunks/{workflow-PYI6HCAG.js → workflow-M7TUYYXY.js} +5 -5
- package/dist/cli/chunks/workflow-orchestrator-OFTAR2NA.js +2 -0
- package/dist/cli/chunks/wrappers-COU73OQ6.js +2 -0
- package/dist/cli/commands/test.js +30 -12
- package/dist/cli/commands/workflow.js +3 -0
- package/dist/cli/scheduler/persistent-scheduler.d.ts +2 -0
- package/dist/cli/scheduler/persistent-scheduler.js +12 -2
- package/dist/cli/utils/generated-test-writer.d.ts +35 -0
- package/dist/cli/utils/generated-test-writer.js +44 -0
- package/dist/context/compiler.js +4 -1
- package/dist/coordination/consensus/providers/ollama-provider.js +16 -27
- package/dist/coordination/consensus/providers/openrouter-provider.js +13 -24
- package/dist/coordination/handlers/test-execution-handlers.js +4 -2
- package/dist/coordination/protocols/defect-investigation.js +4 -1
- package/dist/domains/code-intelligence/services/metric-collector/listing-process-owner.d.ts +3 -0
- package/dist/domains/code-intelligence/services/metric-collector/listing-process-owner.js +57 -0
- package/dist/domains/code-intelligence/services/metric-collector/test-counter.js +127 -39
- package/dist/domains/coverage-analysis/services/gap-detector.js +3 -1
- package/dist/domains/learning-optimization/services/learning-coordinator.js +4 -2
- package/dist/domains/test-execution/services/flaky-detector.js +8 -2
- package/dist/domains/test-execution/services/retry-handler.js +23 -20
- package/dist/domains/test-execution/services/test-executor.js +5 -2
- package/dist/domains/test-generation/services/test-generator.js +5 -2
- package/dist/governance/compliance-reporter.js +8 -7
- package/dist/governance/proof-envelope-integration.js +3 -1
- package/dist/init/phases/07-hooks.js +30 -4
- package/dist/init/phases/10-workers.js +42 -6
- package/dist/integrations/agentic-flow/onnx-embeddings/embedding-generator.d.ts +4 -1
- package/dist/integrations/agentic-flow/onnx-embeddings/embedding-generator.js +6 -2
- package/dist/integrations/browser/qe-dashboard/wasm-vector-store.js +2 -1
- package/dist/integrations/ruvector/coverage-router.d.ts +3 -1
- package/dist/integrations/ruvector/coverage-router.js +7 -6
- package/dist/integrations/ruvector/diff-risk-classifier.js +5 -4
- package/dist/integrations/ruvector/rvf-dual-writer.js +10 -1
- package/dist/integrations/vibium/runtime.js +2 -6
- package/dist/learning/dream/dream-engine.d.ts +5 -0
- package/dist/learning/dream/dream-engine.js +20 -3
- package/dist/learning/embedding-space.d.ts +18 -0
- package/dist/learning/embedding-space.js +33 -0
- package/dist/learning/rvf-pattern-store.js +19 -5
- package/dist/mcp/bundle.js +442 -441
- package/dist/mcp/handlers/domain-handler-configs.d.ts +42 -0
- package/dist/mcp/handlers/handler-factory.js +3 -1
- package/dist/mcp/handlers/save-results-option.d.ts +62 -0
- package/dist/mcp/handlers/save-results-option.js +35 -0
- package/dist/mcp/protocol-server.js +22 -11
- package/dist/mcp/security/rate-limiter.d.ts +8 -1
- package/dist/mcp/security/rate-limiter.js +33 -4
- package/dist/mcp/services/session-resume.js +11 -0
- package/dist/mcp/services/session-store.d.ts +31 -2
- package/dist/mcp/services/session-store.js +95 -16
- package/dist/mcp/services/session-write-buffer.d.ts +55 -0
- package/dist/mcp/services/session-write-buffer.js +144 -0
- package/dist/mcp/types.d.ts +18 -0
- package/dist/memory/crdt/convergence-tracker.js +14 -24
- package/dist/memory/crdt/crdt-store.js +15 -0
- package/dist/memory/crdt/g-counter.js +10 -8
- package/dist/memory/crdt/lww-register.js +3 -3
- package/dist/memory/crdt/or-set.js +18 -0
- package/dist/planning/goap-planner.js +18 -10
- package/dist/shared/embeddings/embedding-cache.d.ts +3 -3
- package/dist/shared/embeddings/embedding-cache.js +8 -8
- package/dist/shared/embeddings/nomic-embedder.js +9 -6
- package/dist/shared/embeddings/ollama-client.d.ts +1 -1
- package/dist/shared/embeddings/ollama-client.js +30 -31
- package/dist/shared/git/git-analyzer.d.ts +4 -1
- package/dist/shared/git/git-analyzer.js +9 -4
- package/dist/shared/llm/cache.js +1 -1
- package/dist/shared/llm/cost-tracker.js +8 -6
- package/dist/shared/llm/providers/azure-openai.js +5 -21
- package/dist/shared/llm/providers/bedrock.js +5 -21
- package/dist/shared/llm/providers/claude.js +5 -21
- package/dist/shared/llm/providers/cognitum.js +5 -17
- package/dist/shared/llm/providers/gemini.js +9 -23
- package/dist/shared/llm/providers/ollama.js +5 -21
- package/dist/shared/llm/providers/openai.js +5 -21
- package/dist/shared/llm/providers/openrouter.js +9 -23
- package/dist/shared/llm/providers/response-deadline.d.ts +3 -0
- package/dist/shared/llm/providers/response-deadline.js +100 -0
- package/dist/shared/llm/spend-ledger.js +3 -2
- package/dist/shared/metrics/code-metrics.d.ts +1 -0
- package/dist/shared/metrics/code-metrics.js +3 -44
- package/dist/shared/metrics/cognitive-complexity.d.ts +17 -0
- package/dist/shared/metrics/cognitive-complexity.js +108 -0
- package/dist/shared/npm-cli-invocation.d.ts +21 -0
- package/dist/shared/npm-cli-invocation.js +28 -0
- package/dist/shared/optional-module.js +24 -0
- package/dist/shared/test-runner-args.d.ts +42 -0
- package/dist/shared/test-runner-args.js +74 -0
- package/dist/shared/utils/safe-record.d.ts +18 -0
- package/dist/shared/utils/safe-record.js +28 -0
- package/dist/test-scheduling/executors/vitest-executor.js +23 -5
- package/dist/workers/quality-daemon/git-watcher.js +5 -0
- package/package.json +1 -1
- package/dist/adapters/ag-ui/json-patch-utils.d.ts +0 -136
- package/dist/adapters/ag-ui/json-patch-utils.js +0 -575
- package/dist/cli/chunks/adapter-EUIELRLI.js +0 -2
- package/dist/cli/chunks/aqe-learning-engine-LXRKHASR.js +0 -2
- package/dist/cli/chunks/base-4DSLX33Y.js +0 -2
- package/dist/cli/chunks/browser-workflow-AWCQLQZK.js +0 -2
- package/dist/cli/chunks/chunk-E2QHUMCR.js +0 -2
- package/dist/cli/chunks/chunk-I7ECEOFV.js +0 -2
- package/dist/cli/chunks/chunk-LW4BCFIV.js +0 -15
- package/dist/cli/chunks/chunk-QJGKL67W.js +0 -54
- package/dist/cli/chunks/chunk-RLQ4MYB6.js +0 -3
- package/dist/cli/chunks/chunk-X43HA3RF.js +0 -14
- package/dist/cli/chunks/chunk-Z3K4JZXQ.js +0 -4
- package/dist/cli/chunks/client-UJVHZXNQ.js +0 -2
- package/dist/cli/chunks/core-handlers-AFXZOSH3.js +0 -2
- package/dist/cli/chunks/cost-tracker-D6YSKTQC.js +0 -2
- package/dist/cli/chunks/cross-domain-router-ZLNVKLIR.js +0 -2
- package/dist/cli/chunks/dream-4OPJXXEG.js +0 -2
- package/dist/cli/chunks/heartbeat-scheduler-J5KLHO6C.js +0 -2
- package/dist/cli/chunks/hnsw-adapter-56P6XSFR.js +0 -2
- package/dist/cli/chunks/hnsw-index-GFZ6XCEW.js +0 -2
- package/dist/cli/chunks/hybrid-backend-TUBWJDDN.js +0 -2
- package/dist/cli/chunks/impact-analyzer-FXVGKS6B.js +0 -2
- package/dist/cli/chunks/init-wizard-LK73HQ7W.js +0 -2
- package/dist/cli/chunks/kernel-TCZDKRSI.js +0 -2
- package/dist/cli/chunks/knowledge-graph-TWMB2GH2.js +0 -2
- package/dist/cli/chunks/llm-router-service-HPDH3P77.js +0 -2
- package/dist/cli/chunks/load-test-HTYOUYSN.js +0 -2
- package/dist/cli/chunks/memory-backend-X5BE2MPK.js +0 -2
- package/dist/cli/chunks/memory-handlers-SQXMFX5X.js +0 -2
- package/dist/cli/chunks/pattern-null-store-OCQGQZU6.js +0 -2
- package/dist/cli/chunks/protocol-executor-CYEQHQCL.js +0 -2
- package/dist/cli/chunks/qe-reasoning-bank-GSTA5WJ4.js +0 -2
- package/dist/cli/chunks/queen-coordinator-Y2Q5RQL7.js +0 -2
- package/dist/cli/chunks/router-4QB5L4IW.js +0 -2
- package/dist/cli/chunks/routing-feedback-YBVHJY5L.js +0 -2
- package/dist/cli/chunks/rvf-dual-writer-T5B456SW.js +0 -2
- package/dist/cli/chunks/rvf-migration-adapter-2O2NL7ZF.js +0 -2
- package/dist/cli/chunks/rvf-native-adapter-RDB3GWTW.js +0 -2
- package/dist/cli/chunks/safe-db-YXVMLFU4.js +0 -2
- package/dist/cli/chunks/schedule-K6XWCCSK.js +0 -2
- package/dist/cli/chunks/scheduler-4MW22D6Q.js +0 -2
- package/dist/cli/chunks/sqlite-persistence-UVDHUSAU.js +0 -2
- package/dist/cli/chunks/sync-LSPNSVDI.js +0 -2
- package/dist/cli/chunks/test-M2XTKD62.js +0 -33
- package/dist/cli/chunks/test-scheduling-GYXJQHJL.js +0 -15
- package/dist/cli/chunks/token-bootstrap-OTEMRUFR.js +0 -2
- package/dist/cli/chunks/unified-memory-UUK77NSE.js +0 -2
- package/dist/cli/chunks/unified-memory-hnsw-JOUE6WNF.js +0 -2
- package/dist/cli/chunks/unified-persistence-3DRJGNQE.js +0 -2
- package/dist/cli/chunks/visual-security-EM7FQG5X.js +0 -2
- package/dist/cli/chunks/witness-chain-7NZ6IIUN.js +0 -2
- package/dist/cli/chunks/witness-chain-repair-ZWS5WXFL.js +0 -2
- package/dist/cli/chunks/workflow-orchestrator-3C3S5FIM.js +0 -2
- package/dist/cli/chunks/wrappers-NXJHGK3W.js +0 -2
|
@@ -6,17 +6,35 @@
|
|
|
6
6
|
* changes within a session. Supports write batching (100ms window) and
|
|
7
7
|
* lazy file creation (no file until first append).
|
|
8
8
|
*
|
|
9
|
+
* Issue #910: writes track progress byte by byte, so a write that fails partway
|
|
10
|
+
* (ENOSPC, EIO) resumes where it stopped instead of re-appending what already
|
|
11
|
+
* reached the disk. The unwritten backlog is bounded while the disk keeps
|
|
12
|
+
* failing; dropped entries become one gap marker that keeps the transcript
|
|
13
|
+
* resumable (see session-write-buffer.ts).
|
|
14
|
+
*
|
|
9
15
|
* @module mcp/services/session-store
|
|
10
16
|
*/
|
|
11
17
|
import { randomUUID } from 'crypto';
|
|
12
18
|
import * as fs from 'fs';
|
|
13
19
|
import * as path from 'path';
|
|
14
20
|
import { findProjectRoot } from '../../kernel/project-root.js';
|
|
21
|
+
import { SessionWriteBuffer, encodeEntry } from './session-write-buffer.js';
|
|
15
22
|
// ============================================================================
|
|
16
23
|
// Session Store
|
|
17
24
|
// ============================================================================
|
|
18
25
|
const DEFAULT_SESSION_DIR = '.agentic-qe/sessions';
|
|
19
26
|
const BATCH_FLUSH_MS = 100;
|
|
27
|
+
const DEFAULT_MAX_BUFFERED_ENTRIES = 10_000;
|
|
28
|
+
// Matches the largest transcript session-resume will read.
|
|
29
|
+
const DEFAULT_MAX_BUFFERED_BYTES = 16 * 1024 * 1024;
|
|
30
|
+
function positiveLimit(name, value, fallback) {
|
|
31
|
+
if (value === undefined)
|
|
32
|
+
return fallback;
|
|
33
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
34
|
+
throw new RangeError(`SessionStore ${name} must be a positive integer, got ${value}`);
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
20
38
|
export class SessionStore {
|
|
21
39
|
sessionDir;
|
|
22
40
|
sessionId = null;
|
|
@@ -27,13 +45,20 @@ export class SessionStore {
|
|
|
27
45
|
createdAt = 0;
|
|
28
46
|
lastActivityAt = 0;
|
|
29
47
|
currentState = 'idle';
|
|
30
|
-
|
|
48
|
+
/** Lines not yet fully on disk, oldest first. */
|
|
49
|
+
writeBuffer;
|
|
50
|
+
dropWarned = false;
|
|
51
|
+
maxBufferedEntries;
|
|
52
|
+
maxBufferedBytes;
|
|
31
53
|
flushTimer = null;
|
|
32
54
|
closed = false;
|
|
33
55
|
// Database-free mode (#534): keep session bookkeeping in memory but never
|
|
34
56
|
// write .agentic-qe/sessions/*.jsonl to disk.
|
|
35
57
|
persistDisabled = process.env.AQE_MEMORY_BACKEND === 'memory';
|
|
36
|
-
constructor(sessionDir) {
|
|
58
|
+
constructor(sessionDir, options = {}) {
|
|
59
|
+
this.maxBufferedEntries = positiveLimit('maxBufferedEntries', options.maxBufferedEntries, DEFAULT_MAX_BUFFERED_ENTRIES);
|
|
60
|
+
this.maxBufferedBytes = positiveLimit('maxBufferedBytes', options.maxBufferedBytes, DEFAULT_MAX_BUFFERED_BYTES);
|
|
61
|
+
this.writeBuffer = this.createWriteBuffer();
|
|
37
62
|
// #735: a cwd-relative default created `<subdir>/.agentic-qe/sessions`
|
|
38
63
|
// on the first MCP tool call, which later runs then adopted as the
|
|
39
64
|
// nearest project store. Anchor the default at the project root.
|
|
@@ -55,7 +80,8 @@ export class SessionStore {
|
|
|
55
80
|
this.createdAt = Date.now();
|
|
56
81
|
this.lastActivityAt = this.createdAt;
|
|
57
82
|
this.currentState = 'idle';
|
|
58
|
-
this.writeBuffer =
|
|
83
|
+
this.writeBuffer = this.createWriteBuffer();
|
|
84
|
+
this.dropWarned = false;
|
|
59
85
|
this.flushTimer = null;
|
|
60
86
|
this.closed = false;
|
|
61
87
|
return this.sessionId;
|
|
@@ -65,7 +91,7 @@ export class SessionStore {
|
|
|
65
91
|
* Assigns a uuid, links parentUuid to the previous entry,
|
|
66
92
|
* and writes a JSON line to the file.
|
|
67
93
|
*
|
|
68
|
-
*
|
|
94
|
+
* The first write is synchronous for a write-ahead guarantee.
|
|
69
95
|
* Subsequent writes are batched for up to 100ms before flushing.
|
|
70
96
|
*
|
|
71
97
|
* Returns the assigned uuid.
|
|
@@ -82,24 +108,37 @@ export class SessionStore {
|
|
|
82
108
|
};
|
|
83
109
|
// Database-free mode keeps bookkeeping without serialization or disk I/O.
|
|
84
110
|
if (!this.persistDisabled) {
|
|
85
|
-
const line =
|
|
111
|
+
const line = encodeEntry(fullEntry);
|
|
86
112
|
if (!this.fileCreated) {
|
|
87
113
|
// First write: synchronous for write-ahead guarantee
|
|
88
114
|
this.ensureDirectory();
|
|
89
|
-
|
|
115
|
+
this.writeBuffer.push(fullEntry, line);
|
|
116
|
+
try {
|
|
117
|
+
this.writePending();
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (!this.writeBuffer.hasPartialHead) {
|
|
121
|
+
// Nothing reached the disk: reject the entry and leave the chain as it was.
|
|
122
|
+
this.writeBuffer.popNewest();
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
// Part of the line is on disk. Keep it so a retry completes the record
|
|
126
|
+
// rather than leaving a torn prefix for the next line to glue onto.
|
|
127
|
+
this.fileCreated = true;
|
|
128
|
+
this.accept(fullEntry);
|
|
129
|
+
this.scheduleBatchFlush();
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
90
132
|
this.fileCreated = true;
|
|
91
133
|
}
|
|
92
134
|
else {
|
|
93
135
|
// Subsequent writes: buffer and batch
|
|
94
|
-
this.writeBuffer.push(line);
|
|
136
|
+
this.writeBuffer.push(fullEntry, line);
|
|
95
137
|
this.scheduleBatchFlush();
|
|
96
138
|
}
|
|
97
139
|
}
|
|
98
140
|
// Only advance the chain and metadata after the entry has been accepted.
|
|
99
|
-
this.
|
|
100
|
-
this.entryCount++;
|
|
101
|
-
this.lastActivityAt = fullEntry.timestamp;
|
|
102
|
-
this.currentState = fullEntry.state;
|
|
141
|
+
this.accept(fullEntry);
|
|
103
142
|
return uuid;
|
|
104
143
|
}
|
|
105
144
|
/**
|
|
@@ -110,12 +149,20 @@ export class SessionStore {
|
|
|
110
149
|
clearTimeout(this.flushTimer);
|
|
111
150
|
this.flushTimer = null;
|
|
112
151
|
}
|
|
113
|
-
if (this.
|
|
114
|
-
|
|
115
|
-
fs.appendFileSync(this.filePath, batch, 'utf-8');
|
|
116
|
-
this.writeBuffer = [];
|
|
152
|
+
if (this.fileCreated) {
|
|
153
|
+
this.writePending();
|
|
117
154
|
}
|
|
118
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* Size of the unwritten backlog and how many entries the cap has dropped.
|
|
158
|
+
*/
|
|
159
|
+
getWriteBufferStats() {
|
|
160
|
+
return {
|
|
161
|
+
bufferedEntries: this.writeBuffer.size,
|
|
162
|
+
bufferedBytes: this.writeBuffer.bufferedBytes,
|
|
163
|
+
droppedEntries: this.writeBuffer.droppedEntries,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
119
166
|
/**
|
|
120
167
|
* Returns metadata about the current session.
|
|
121
168
|
*/
|
|
@@ -162,11 +209,43 @@ export class SessionStore {
|
|
|
162
209
|
fs.mkdirSync(dir, { recursive: true });
|
|
163
210
|
}
|
|
164
211
|
}
|
|
212
|
+
accept(entry) {
|
|
213
|
+
this.lastUuid = entry.uuid;
|
|
214
|
+
this.entryCount++;
|
|
215
|
+
this.lastActivityAt = entry.timestamp;
|
|
216
|
+
this.currentState = entry.state;
|
|
217
|
+
}
|
|
218
|
+
writePending() {
|
|
219
|
+
if (this.filePath)
|
|
220
|
+
this.writeBuffer.writeTo(this.filePath);
|
|
221
|
+
}
|
|
222
|
+
createWriteBuffer() {
|
|
223
|
+
return new SessionWriteBuffer(this.maxBufferedEntries, this.maxBufferedBytes, () => {
|
|
224
|
+
if (this.dropWarned)
|
|
225
|
+
return;
|
|
226
|
+
this.dropWarned = true;
|
|
227
|
+
// "[AQE] WARN" passes the MCP entry's stderr filter.
|
|
228
|
+
console.error(`[AQE] WARN session journal ${this.filePath}: writes keep failing and the buffer reached its cap `
|
|
229
|
+
+ `(${this.maxBufferedEntries} entries / ${this.maxBufferedBytes} bytes). The oldest unwritten entries `
|
|
230
|
+
+ 'are replaced by one gap marker: their content is lost, but the transcript stays resumable. '
|
|
231
|
+
+ 'The dropped count is in getWriteBufferStats() and in the marker.');
|
|
232
|
+
});
|
|
233
|
+
}
|
|
165
234
|
scheduleBatchFlush() {
|
|
166
235
|
if (this.flushTimer === null) {
|
|
167
236
|
this.flushTimer = setTimeout(() => {
|
|
168
237
|
this.flushTimer = null;
|
|
169
|
-
|
|
238
|
+
try {
|
|
239
|
+
this.flush();
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// #983: a throw here has no caller and reaches 'uncaughtException',
|
|
243
|
+
// which the MCP entry logs as "[AQE] FATAL" on every tick while the
|
|
244
|
+
// disk stays full. The unwritten entries stay buffered for the next
|
|
245
|
+
// append or flush(), and the buffer's one-time WARN reports a
|
|
246
|
+
// persistent failure once it reaches its cap. An explicit flush() or
|
|
247
|
+
// close() still throws to its caller.
|
|
248
|
+
}
|
|
170
249
|
}, BATCH_FLUSH_MS);
|
|
171
250
|
}
|
|
172
251
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded, progress-tracking write buffer for the JSONL session journal (#910).
|
|
3
|
+
*
|
|
4
|
+
* - Writes track byte progress, so a write that fails partway (ENOSPC, EIO)
|
|
5
|
+
* resumes where it stopped instead of re-appending what reached the disk.
|
|
6
|
+
* - While writes keep failing, the backlog is capped. The oldest unwritten
|
|
7
|
+
* entries are replaced by one `state_change` gap marker that reuses the last
|
|
8
|
+
* dropped entry's uuid and the first dropped entry's parentUuid, so the
|
|
9
|
+
* parentUuid chain on disk stays valid and session-resume still accepts it.
|
|
10
|
+
*
|
|
11
|
+
* @module mcp/services/session-write-buffer
|
|
12
|
+
*/
|
|
13
|
+
import type { SessionEntry } from './session-store';
|
|
14
|
+
/** `result` payload of a gap marker entry. */
|
|
15
|
+
export interface JournalGap {
|
|
16
|
+
droppedEntries: number;
|
|
17
|
+
reason: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function encodeEntry(entry: SessionEntry): Buffer;
|
|
20
|
+
export declare class SessionWriteBuffer {
|
|
21
|
+
private readonly maxEntries;
|
|
22
|
+
private readonly maxBytes;
|
|
23
|
+
/** Called once per drop event with the running dropped total. */
|
|
24
|
+
private readonly onDrop;
|
|
25
|
+
private lines;
|
|
26
|
+
/** Bytes of lines[0] already on disk after a write that failed partway. */
|
|
27
|
+
private headWritten;
|
|
28
|
+
private bytes;
|
|
29
|
+
private dropped;
|
|
30
|
+
constructor(maxEntries: number, maxBytes: number,
|
|
31
|
+
/** Called once per drop event with the running dropped total. */
|
|
32
|
+
onDrop: (droppedTotal: number) => void);
|
|
33
|
+
get size(): number;
|
|
34
|
+
get bufferedBytes(): number;
|
|
35
|
+
get droppedEntries(): number;
|
|
36
|
+
/** True when part of the oldest line is already on disk. */
|
|
37
|
+
get hasPartialHead(): boolean;
|
|
38
|
+
push(entry: SessionEntry, data?: Buffer): void;
|
|
39
|
+
/** Remove the newest line (an entry whose first write put nothing on disk). */
|
|
40
|
+
popNewest(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Append every buffered line with one write loop over a single buffer, then
|
|
43
|
+
* map the bytes written back onto line boundaries. On failure only the
|
|
44
|
+
* unwritten remainder stays buffered.
|
|
45
|
+
*/
|
|
46
|
+
writeTo(filePath: string): void;
|
|
47
|
+
/** Drop the lines fully covered by `offset` bytes and keep partial progress. */
|
|
48
|
+
private settle;
|
|
49
|
+
/**
|
|
50
|
+
* Replace the oldest droppable lines with one gap marker until both caps hold.
|
|
51
|
+
* The newest line and a partially written head line are never dropped.
|
|
52
|
+
*/
|
|
53
|
+
private enforceCaps;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=session-write-buffer.d.ts.map
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounded, progress-tracking write buffer for the JSONL session journal (#910).
|
|
3
|
+
*
|
|
4
|
+
* - Writes track byte progress, so a write that fails partway (ENOSPC, EIO)
|
|
5
|
+
* resumes where it stopped instead of re-appending what reached the disk.
|
|
6
|
+
* - While writes keep failing, the backlog is capped. The oldest unwritten
|
|
7
|
+
* entries are replaced by one `state_change` gap marker that reuses the last
|
|
8
|
+
* dropped entry's uuid and the first dropped entry's parentUuid, so the
|
|
9
|
+
* parentUuid chain on disk stays valid and session-resume still accepts it.
|
|
10
|
+
*
|
|
11
|
+
* @module mcp/services/session-write-buffer
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
const GAP_REASON = 'write buffer cap reached while the session journal could not be written';
|
|
15
|
+
// Upper bound on a gap marker line, used while choosing how many lines to drop.
|
|
16
|
+
const GAP_MARKER_MAX_BYTES = 512;
|
|
17
|
+
export function encodeEntry(entry) {
|
|
18
|
+
return Buffer.from(JSON.stringify(entry) + '\n', 'utf-8');
|
|
19
|
+
}
|
|
20
|
+
export class SessionWriteBuffer {
|
|
21
|
+
maxEntries;
|
|
22
|
+
maxBytes;
|
|
23
|
+
onDrop;
|
|
24
|
+
lines = [];
|
|
25
|
+
/** Bytes of lines[0] already on disk after a write that failed partway. */
|
|
26
|
+
headWritten = 0;
|
|
27
|
+
bytes = 0;
|
|
28
|
+
dropped = 0;
|
|
29
|
+
constructor(maxEntries, maxBytes,
|
|
30
|
+
/** Called once per drop event with the running dropped total. */
|
|
31
|
+
onDrop) {
|
|
32
|
+
this.maxEntries = maxEntries;
|
|
33
|
+
this.maxBytes = maxBytes;
|
|
34
|
+
this.onDrop = onDrop;
|
|
35
|
+
}
|
|
36
|
+
get size() { return this.lines.length; }
|
|
37
|
+
get bufferedBytes() { return this.bytes; }
|
|
38
|
+
get droppedEntries() { return this.dropped; }
|
|
39
|
+
/** True when part of the oldest line is already on disk. */
|
|
40
|
+
get hasPartialHead() { return this.headWritten > 0; }
|
|
41
|
+
push(entry, data = encodeEntry(entry)) {
|
|
42
|
+
this.lines.push({
|
|
43
|
+
data, uuid: entry.uuid, parentUuid: entry.parentUuid,
|
|
44
|
+
timestamp: entry.timestamp, state: entry.state, represents: 1,
|
|
45
|
+
});
|
|
46
|
+
this.bytes += data.length;
|
|
47
|
+
this.enforceCaps();
|
|
48
|
+
}
|
|
49
|
+
/** Remove the newest line (an entry whose first write put nothing on disk). */
|
|
50
|
+
popNewest() {
|
|
51
|
+
const line = this.lines.pop();
|
|
52
|
+
if (line)
|
|
53
|
+
this.bytes -= line.data.length;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Append every buffered line with one write loop over a single buffer, then
|
|
57
|
+
* map the bytes written back onto line boundaries. On failure only the
|
|
58
|
+
* unwritten remainder stays buffered.
|
|
59
|
+
*/
|
|
60
|
+
writeTo(filePath) {
|
|
61
|
+
if (this.lines.length === 0)
|
|
62
|
+
return;
|
|
63
|
+
const pending = Buffer.concat(this.lines.map(line => line.data));
|
|
64
|
+
let offset = this.headWritten;
|
|
65
|
+
const fd = fs.openSync(filePath, 'a');
|
|
66
|
+
try {
|
|
67
|
+
while (offset < pending.length) {
|
|
68
|
+
const count = fs.writeSync(fd, pending, offset, pending.length - offset);
|
|
69
|
+
if (count <= 0)
|
|
70
|
+
throw new Error(`session journal write made no progress: ${filePath}`);
|
|
71
|
+
offset += count;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
finally {
|
|
75
|
+
this.settle(offset);
|
|
76
|
+
try {
|
|
77
|
+
fs.closeSync(fd);
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// The bytes were handed to the kernel already; a write error, if any,
|
|
81
|
+
// is the one worth reporting.
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** Drop the lines fully covered by `offset` bytes and keep partial progress. */
|
|
86
|
+
settle(offset) {
|
|
87
|
+
let completed = 0;
|
|
88
|
+
let consumed = 0;
|
|
89
|
+
while (completed < this.lines.length && consumed + this.lines[completed].data.length <= offset) {
|
|
90
|
+
consumed += this.lines[completed].data.length;
|
|
91
|
+
completed++;
|
|
92
|
+
}
|
|
93
|
+
if (completed > 0) {
|
|
94
|
+
this.lines.splice(0, completed);
|
|
95
|
+
this.bytes -= consumed;
|
|
96
|
+
}
|
|
97
|
+
this.headWritten = offset - consumed;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Replace the oldest droppable lines with one gap marker until both caps hold.
|
|
101
|
+
* The newest line and a partially written head line are never dropped.
|
|
102
|
+
*/
|
|
103
|
+
enforceCaps() {
|
|
104
|
+
const start = this.headWritten > 0 ? 1 : 0;
|
|
105
|
+
const newest = this.lines.length - 1;
|
|
106
|
+
let end = start;
|
|
107
|
+
let removedBytes = 0;
|
|
108
|
+
const over = () => {
|
|
109
|
+
const removed = end - start;
|
|
110
|
+
const marker = removed > 0 ? 1 : 0;
|
|
111
|
+
return this.lines.length - removed + marker > this.maxEntries
|
|
112
|
+
|| this.bytes - removedBytes + marker * GAP_MARKER_MAX_BYTES > this.maxBytes;
|
|
113
|
+
};
|
|
114
|
+
if (!over())
|
|
115
|
+
return;
|
|
116
|
+
while (end < newest && over()) {
|
|
117
|
+
removedBytes += this.lines[end].data.length;
|
|
118
|
+
end++;
|
|
119
|
+
}
|
|
120
|
+
// Swapping a single line for a marker frees nothing (caps below 2 lines).
|
|
121
|
+
if (end - start < 2)
|
|
122
|
+
return;
|
|
123
|
+
const run = this.lines.slice(start, end);
|
|
124
|
+
const first = run[0];
|
|
125
|
+
const last = run[run.length - 1];
|
|
126
|
+
const represents = run.reduce((sum, line) => sum + line.represents, 0);
|
|
127
|
+
// An earlier marker inside the run was already counted.
|
|
128
|
+
const newlyDropped = run.filter(line => line.represents === 1).length;
|
|
129
|
+
const gap = { droppedEntries: represents, reason: GAP_REASON };
|
|
130
|
+
const markerEntry = {
|
|
131
|
+
uuid: last.uuid, parentUuid: first.parentUuid, timestamp: last.timestamp,
|
|
132
|
+
type: 'state_change', state: last.state, result: { journalGap: gap },
|
|
133
|
+
};
|
|
134
|
+
const data = encodeEntry(markerEntry);
|
|
135
|
+
this.lines.splice(start, run.length, {
|
|
136
|
+
data, uuid: last.uuid, parentUuid: first.parentUuid,
|
|
137
|
+
timestamp: last.timestamp, state: last.state, represents,
|
|
138
|
+
});
|
|
139
|
+
this.bytes += data.length - removedBytes;
|
|
140
|
+
this.dropped += newlyDropped;
|
|
141
|
+
this.onDrop(this.dropped);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=session-write-buffer.js.map
|
package/dist/mcp/types.d.ts
CHANGED
|
@@ -199,6 +199,8 @@ export interface TestExecuteParams {
|
|
|
199
199
|
timeout?: number;
|
|
200
200
|
collectCoverage?: boolean;
|
|
201
201
|
reportFormat?: 'json' | 'junit' | 'html' | 'markdown';
|
|
202
|
+
/** #933: write result files under .agentic-qe/results/ (default true). false skips them. */
|
|
203
|
+
saveResults?: boolean;
|
|
202
204
|
}
|
|
203
205
|
/**
|
|
204
206
|
* Coverage analysis parameters
|
|
@@ -209,6 +211,8 @@ export interface CoverageAnalyzeParams {
|
|
|
209
211
|
detectGaps?: boolean;
|
|
210
212
|
mlPowered?: boolean;
|
|
211
213
|
prioritization?: 'complexity' | 'criticality' | 'change-frequency' | 'ml-confidence';
|
|
214
|
+
/** #933: write result files under .agentic-qe/results/ (default true). false skips them. */
|
|
215
|
+
saveResults?: boolean;
|
|
212
216
|
}
|
|
213
217
|
/**
|
|
214
218
|
* Quality assessment parameters
|
|
@@ -217,6 +221,8 @@ export interface QualityAssessParams {
|
|
|
217
221
|
runGate?: boolean;
|
|
218
222
|
threshold?: number;
|
|
219
223
|
metrics?: string[];
|
|
224
|
+
/** #933: write result files under .agentic-qe/results/ (default true). false skips them. */
|
|
225
|
+
saveResults?: boolean;
|
|
220
226
|
}
|
|
221
227
|
/**
|
|
222
228
|
* Security scan parameters
|
|
@@ -228,6 +234,8 @@ export interface SecurityScanParams {
|
|
|
228
234
|
target?: string;
|
|
229
235
|
/** URL for explicitly requested dynamic scanning. */
|
|
230
236
|
targetUrl?: string;
|
|
237
|
+
/** #933: write result files under .agentic-qe/results/ (default true). false skips them. */
|
|
238
|
+
saveResults?: boolean;
|
|
231
239
|
}
|
|
232
240
|
/**
|
|
233
241
|
* Contract validation parameters
|
|
@@ -237,6 +245,8 @@ export interface ContractValidateParams {
|
|
|
237
245
|
providerUrl?: string;
|
|
238
246
|
consumerName?: string;
|
|
239
247
|
checkBreakingChanges?: boolean;
|
|
248
|
+
/** #933: write result files under .agentic-qe/results/ (default true). false skips them. */
|
|
249
|
+
saveResults?: boolean;
|
|
240
250
|
}
|
|
241
251
|
/**
|
|
242
252
|
* Accessibility test parameters
|
|
@@ -245,6 +255,8 @@ export interface AccessibilityTestParams {
|
|
|
245
255
|
url?: string;
|
|
246
256
|
standard?: 'wcag21-aa' | 'wcag21-aaa' | 'wcag22-aa' | 'section508';
|
|
247
257
|
includeScreenReader?: boolean;
|
|
258
|
+
/** #933: write result files under .agentic-qe/results/ (default true). false skips them. */
|
|
259
|
+
saveResults?: boolean;
|
|
248
260
|
}
|
|
249
261
|
/**
|
|
250
262
|
* Chaos test parameters
|
|
@@ -255,6 +267,8 @@ export interface ChaosTestParams {
|
|
|
255
267
|
duration?: number;
|
|
256
268
|
intensity?: number;
|
|
257
269
|
dryRun?: boolean;
|
|
270
|
+
/** #933: write result files under .agentic-qe/results/ (default true). false skips them. */
|
|
271
|
+
saveResults?: boolean;
|
|
258
272
|
}
|
|
259
273
|
/**
|
|
260
274
|
* Protocol execute parameters
|
|
@@ -528,6 +542,10 @@ export interface CoverageAnalyzeResult {
|
|
|
528
542
|
gaps?: CoverageGap[];
|
|
529
543
|
riskScore?: number;
|
|
530
544
|
savedFiles?: string[];
|
|
545
|
+
/** #933: true when result files were written; see savedFiles for their paths. */
|
|
546
|
+
resultsSaved?: boolean;
|
|
547
|
+
/** #933: plain statement of what was (or was not) written to disk. */
|
|
548
|
+
resultsNote?: string;
|
|
531
549
|
/** #569: `null` when no figure was produced — see `lineCoverage`. */
|
|
532
550
|
statementCoverage?: number | null;
|
|
533
551
|
totalFiles?: number;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module memory/crdt/convergence-tracker
|
|
9
9
|
*/
|
|
10
|
+
import { createHash } from 'node:crypto';
|
|
10
11
|
// =============================================================================
|
|
11
12
|
// Default Configuration
|
|
12
13
|
// =============================================================================
|
|
@@ -23,7 +24,7 @@ const DEFAULT_CONFIG = {
|
|
|
23
24
|
* not the internal representations, so that stores with equivalent data produce
|
|
24
25
|
* the same hash regardless of how they arrived at that state.
|
|
25
26
|
*
|
|
26
|
-
*
|
|
27
|
+
* Fingerprints structured semantic entries; this is not authentication.
|
|
27
28
|
*/
|
|
28
29
|
function hashState(state) {
|
|
29
30
|
const parts = [];
|
|
@@ -31,7 +32,7 @@ function hashState(state) {
|
|
|
31
32
|
// compare the result - if both have same winner, they have same value)
|
|
32
33
|
for (const [key, reg] of Object.entries(state.registers)) {
|
|
33
34
|
// Include value, timestamp, and nodeId - these together determine the LWW winner
|
|
34
|
-
parts.push(
|
|
35
|
+
parts.push(JSON.stringify(['r', key, JSON.stringify(reg.value), reg.timestamp, reg.nodeId]));
|
|
35
36
|
}
|
|
36
37
|
// Hash G-Counters by their TOTAL value (not per-node breakdown)
|
|
37
38
|
// Two stores are converged if they produce the same sum, even if the internal
|
|
@@ -41,10 +42,9 @@ function hashState(state) {
|
|
|
41
42
|
// Also include non-zero counts to detect semantic differences
|
|
42
43
|
const nonZeroCounts = Object.entries(counter.counts)
|
|
43
44
|
.filter(([_, count]) => count > 0)
|
|
44
|
-
.sort(([a], [b]) => a
|
|
45
|
-
.map(([nodeId, count]) =>
|
|
46
|
-
|
|
47
|
-
parts.push(`g:${key}:${total}:{${nonZeroCounts}}`);
|
|
45
|
+
.sort(([a], [b]) => a === b ? 0 : a < b ? -1 : 1)
|
|
46
|
+
.map(([nodeId, count]) => [nodeId, count]);
|
|
47
|
+
parts.push(JSON.stringify(['g', key, total, nonZeroCounts]));
|
|
48
48
|
}
|
|
49
49
|
// Hash PN-Counters by their NET value (positive - negative)
|
|
50
50
|
// Two stores are converged if they evaluate to the same number
|
|
@@ -55,15 +55,13 @@ function hashState(state) {
|
|
|
55
55
|
// Include non-zero contributions for semantic comparison
|
|
56
56
|
const nonZeroPos = Object.entries(counter.positive.counts)
|
|
57
57
|
.filter(([_, count]) => count > 0)
|
|
58
|
-
.sort(([a], [b]) => a
|
|
59
|
-
.map(([nodeId, count]) =>
|
|
60
|
-
.join(',');
|
|
58
|
+
.sort(([a], [b]) => a === b ? 0 : a < b ? -1 : 1)
|
|
59
|
+
.map(([nodeId, count]) => [nodeId, count]);
|
|
61
60
|
const nonZeroNeg = Object.entries(counter.negative.counts)
|
|
62
61
|
.filter(([_, count]) => count > 0)
|
|
63
|
-
.sort(([a], [b]) => a
|
|
64
|
-
.map(([nodeId, count]) =>
|
|
65
|
-
|
|
66
|
-
parts.push(`p:${key}:${netValue}:{${nonZeroPos}|${nonZeroNeg}}`);
|
|
62
|
+
.sort(([a], [b]) => a === b ? 0 : a < b ? -1 : 1)
|
|
63
|
+
.map(([nodeId, count]) => [nodeId, count]);
|
|
64
|
+
parts.push(JSON.stringify(['p', key, netValue, nonZeroPos, nonZeroNeg]));
|
|
67
65
|
}
|
|
68
66
|
// Hash OR-Sets by their active elements (elements with at least one non-tombstoned tag)
|
|
69
67
|
for (const [key, set] of Object.entries(state.sets)) {
|
|
@@ -77,20 +75,12 @@ function hashState(state) {
|
|
|
77
75
|
}
|
|
78
76
|
}
|
|
79
77
|
activeElements.sort();
|
|
80
|
-
parts.push(
|
|
78
|
+
parts.push(JSON.stringify(['s', key, activeElements]));
|
|
81
79
|
}
|
|
82
80
|
// Sort for consistency
|
|
83
81
|
parts.sort();
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
let hash = 0;
|
|
87
|
-
for (let i = 0; i < joined.length; i++) {
|
|
88
|
-
const char = joined.charCodeAt(i);
|
|
89
|
-
hash = ((hash << 5) - hash) + char;
|
|
90
|
-
hash = hash & hash; // Convert to 32-bit integer
|
|
91
|
-
}
|
|
92
|
-
// Return hash without version - version is per-node and shouldn't affect convergence check
|
|
93
|
-
return hash.toString(16);
|
|
82
|
+
// Version is per-node and does not affect semantic convergence.
|
|
83
|
+
return createHash('sha256').update(JSON.stringify(parts)).digest('hex');
|
|
94
84
|
}
|
|
95
85
|
// =============================================================================
|
|
96
86
|
// Convergence Tracker Implementation
|
|
@@ -365,6 +365,21 @@ export function createCRDTStore(config) {
|
|
|
365
365
|
// For initial state, need full state - caller should use getState instead
|
|
366
366
|
return null;
|
|
367
367
|
}
|
|
368
|
+
// A retained suffix cannot cover an older checkpoint. Send complete CRDT
|
|
369
|
+
// state (including OR-set tombstones) through the existing delta merge API.
|
|
370
|
+
if (sinceVersion < deltaHistory[0].fromVersion) {
|
|
371
|
+
const snapshot = store.getState();
|
|
372
|
+
return {
|
|
373
|
+
fromVersion: sinceVersion,
|
|
374
|
+
toVersion: version,
|
|
375
|
+
timestamp: Date.now(),
|
|
376
|
+
nodeId,
|
|
377
|
+
registers: snapshot.registers,
|
|
378
|
+
gCounters: snapshot.gCounters,
|
|
379
|
+
pnCounters: snapshot.pnCounters,
|
|
380
|
+
sets: snapshot.sets,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
368
383
|
// Find deltas that contain changes after the requested version
|
|
369
384
|
// A delta is relevant if its toVersion > sinceVersion
|
|
370
385
|
const relevantDeltas = deltaHistory.filter((d) => d.toVersion > sinceVersion);
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module memory/crdt/g-counter
|
|
9
9
|
*/
|
|
10
|
+
import { addToCount, createSafeRecord, getOwn, setOwn } from '../../shared/utils/safe-record.js';
|
|
10
11
|
// =============================================================================
|
|
11
12
|
// G-Counter Implementation
|
|
12
13
|
// =============================================================================
|
|
@@ -27,12 +28,13 @@
|
|
|
27
28
|
export function createGCounter(nodeId) {
|
|
28
29
|
// Internal state: per-node counts
|
|
29
30
|
const state = {
|
|
30
|
-
|
|
31
|
+
// Node ids are data: keep them off Object.prototype (#914).
|
|
32
|
+
counts: createSafeRecord(),
|
|
31
33
|
version: 0,
|
|
32
34
|
lastUpdated: Date.now(),
|
|
33
35
|
};
|
|
34
36
|
// Initialize this node's count
|
|
35
|
-
state.counts
|
|
37
|
+
setOwn(state.counts, nodeId, 0);
|
|
36
38
|
return {
|
|
37
39
|
get() {
|
|
38
40
|
// Sum all node counts
|
|
@@ -45,7 +47,7 @@ export function createGCounter(nodeId) {
|
|
|
45
47
|
if (n === 0) {
|
|
46
48
|
return;
|
|
47
49
|
}
|
|
48
|
-
state.counts
|
|
50
|
+
addToCount(state.counts, nodeId, n);
|
|
49
51
|
state.version++;
|
|
50
52
|
state.lastUpdated = Date.now();
|
|
51
53
|
},
|
|
@@ -56,7 +58,7 @@ export function createGCounter(nodeId) {
|
|
|
56
58
|
getState() {
|
|
57
59
|
// Return a deep copy to prevent external mutation
|
|
58
60
|
return {
|
|
59
|
-
counts:
|
|
61
|
+
counts: Object.assign(createSafeRecord(), state.counts),
|
|
60
62
|
version: state.version,
|
|
61
63
|
lastUpdated: state.lastUpdated,
|
|
62
64
|
};
|
|
@@ -65,9 +67,9 @@ export function createGCounter(nodeId) {
|
|
|
65
67
|
let changed = false;
|
|
66
68
|
// Merge counts: take max for each node
|
|
67
69
|
for (const [incomingNodeId, incomingCount] of Object.entries(incoming.counts)) {
|
|
68
|
-
const currentCount = state.counts
|
|
70
|
+
const currentCount = getOwn(state.counts, incomingNodeId) ?? 0;
|
|
69
71
|
if (incomingCount > currentCount) {
|
|
70
|
-
state.counts
|
|
72
|
+
setOwn(state.counts, incomingNodeId, incomingCount);
|
|
71
73
|
changed = true;
|
|
72
74
|
}
|
|
73
75
|
}
|
|
@@ -80,7 +82,7 @@ export function createGCounter(nodeId) {
|
|
|
80
82
|
return nodeId;
|
|
81
83
|
},
|
|
82
84
|
getLocalCount() {
|
|
83
|
-
return state.counts
|
|
85
|
+
return getOwn(state.counts, nodeId) ?? 0;
|
|
84
86
|
},
|
|
85
87
|
};
|
|
86
88
|
}
|
|
@@ -123,7 +125,7 @@ export function isGCounterState(value) {
|
|
|
123
125
|
* Get the count for a specific node from state
|
|
124
126
|
*/
|
|
125
127
|
export function getNodeCountFromState(state, nodeId) {
|
|
126
|
-
return state.counts
|
|
128
|
+
return getOwn(state.counts, nodeId) ?? 0;
|
|
127
129
|
}
|
|
128
130
|
/**
|
|
129
131
|
* Get all contributing nodes from state
|
|
@@ -43,7 +43,7 @@ export function createLWWRegister(nodeId, initialValue, initialTimestamp) {
|
|
|
43
43
|
return timestampA - timestampB;
|
|
44
44
|
}
|
|
45
45
|
// Tiebreaker: lexicographic comparison of node IDs
|
|
46
|
-
return nodeIdA
|
|
46
|
+
return nodeIdA === nodeIdB ? 0 : nodeIdA > nodeIdB ? 1 : -1;
|
|
47
47
|
}
|
|
48
48
|
return {
|
|
49
49
|
get() {
|
|
@@ -52,7 +52,7 @@ export function createLWWRegister(nodeId, initialValue, initialTimestamp) {
|
|
|
52
52
|
set(value, timestamp) {
|
|
53
53
|
const ts = timestamp ?? Date.now();
|
|
54
54
|
// Only update if new timestamp wins
|
|
55
|
-
if (state.
|
|
55
|
+
if (state.version === 0 ||
|
|
56
56
|
compareTimestamps(ts, nodeId, state.timestamp, state.nodeId) > 0) {
|
|
57
57
|
state = {
|
|
58
58
|
value,
|
|
@@ -79,7 +79,7 @@ export function createLWWRegister(nodeId, initialValue, initialTimestamp) {
|
|
|
79
79
|
},
|
|
80
80
|
applyState(incoming) {
|
|
81
81
|
// Only update if incoming timestamp wins
|
|
82
|
-
if (state.
|
|
82
|
+
if (state.version === 0 ||
|
|
83
83
|
compareTimestamps(incoming.timestamp, incoming.nodeId, state.timestamp, state.nodeId) > 0) {
|
|
84
84
|
state = {
|
|
85
85
|
value: incoming.value,
|