agentic-qe 3.9.5 → 3.9.7
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/skills/skills-manifest.json +1 -1
- package/CHANGELOG.md +56 -0
- package/dist/cli/bundle.js +5 -5
- package/dist/cli/chunks/adapter-GE3RSG32.js +2 -0
- package/dist/cli/chunks/{agent-booster-wasm-AW36VJXL.js → agent-booster-wasm-MXTWTD4D.js} +2 -2
- package/dist/cli/chunks/{agent-handler-AQFW2CMW.js → agent-handler-6NEP2BOA.js} +2 -2
- package/dist/cli/chunks/{agent-memory-branch-VA6A47U5.js → agent-memory-branch-SAPEIGE2.js} +2 -2
- package/dist/cli/chunks/aqe-learning-engine-IYL6TK6E.js +2 -0
- package/dist/cli/chunks/{audit-WVJHTVUU.js → audit-53LCI6R3.js} +2 -2
- package/dist/cli/chunks/base-L3G3GEZY.js +2 -0
- package/dist/cli/chunks/{better-sqlite3-HCMGVEDO.js → better-sqlite3-UZSKFGCK.js} +2 -2
- package/dist/cli/chunks/{brain-handler-JZISBH2W.js → brain-handler-67NDDXVO.js} +3 -3
- package/dist/cli/chunks/{branch-enumerator-3TQXIETD.js → branch-enumerator-IUIGYEK5.js} +2 -2
- package/dist/cli/chunks/{browser-4GZXU4YT.js → browser-UFUF65LQ.js} +2 -2
- package/dist/cli/chunks/browser-workflow-7KTCY5FI.js +2 -0
- package/dist/cli/chunks/{chunk-QPW2O5TR.js → chunk-2SJQ3CYN.js} +9 -9
- package/dist/cli/chunks/{chunk-WTGUS7DD.js → chunk-2TRWUJWG.js} +2 -2
- package/dist/cli/chunks/{chunk-3VNEMHOO.js → chunk-3DXO5CWI.js} +1 -1
- package/dist/cli/chunks/{chunk-QHMDETCU.js → chunk-3KX7CQEM.js} +1 -1
- package/dist/cli/chunks/{chunk-LPVQ6QCW.js → chunk-426OO3RH.js} +2 -2
- package/dist/cli/chunks/{chunk-NNNH36RU.js → chunk-4B5IYFKE.js} +2 -2
- package/dist/cli/chunks/{chunk-GFZRXQWH.js → chunk-4NHE7AJE.js} +1 -1
- package/dist/cli/chunks/{chunk-KWIDDPKG.js → chunk-52MUZGZF.js} +2 -2
- package/dist/cli/chunks/{chunk-73LG5ELL.js → chunk-5EOM3W6Q.js} +2 -2
- package/dist/cli/chunks/{chunk-N66G743A.js → chunk-6MMRZYYS.js} +3 -3
- package/dist/cli/chunks/{chunk-OQCEI7Q4.js → chunk-6OKXGDT3.js} +2 -2
- package/dist/cli/chunks/{chunk-YGZBS4RW.js → chunk-6ULPQTIE.js} +2 -2
- package/dist/cli/chunks/{chunk-DPEX4UOC.js → chunk-7GPYOMVY.js} +1 -1
- package/dist/cli/chunks/{chunk-YHSIUFFY.js → chunk-7OPVTN5W.js} +2 -2
- package/dist/cli/chunks/{chunk-4FPCCKNQ.js → chunk-7TKZ6LK6.js} +1 -1
- package/dist/cli/chunks/{chunk-TRRJP27A.js → chunk-A5VULTCD.js} +1 -1
- package/dist/cli/chunks/{chunk-E55RROAP.js → chunk-AHB2F42G.js} +1 -1
- package/dist/cli/chunks/{chunk-3KYKE7B6.js → chunk-APPVAGD5.js} +1 -1
- package/dist/cli/chunks/{chunk-7JQOOPA2.js → chunk-AT6LBLUV.js} +2 -2
- package/dist/cli/chunks/{chunk-ERV3OVUG.js → chunk-BJKRAFS2.js} +2 -2
- package/dist/cli/chunks/{chunk-OPOTKH42.js → chunk-BPYWDM2Y.js} +3 -3
- package/dist/cli/chunks/{chunk-BM7VSJTE.js → chunk-BTZEKSWH.js} +2 -2
- package/dist/cli/chunks/{chunk-4EOSDCDM.js → chunk-BWNH22V2.js} +1 -1
- package/dist/cli/chunks/{chunk-ITIYGUIL.js → chunk-CDFYF33M.js} +2 -2
- package/dist/cli/chunks/{chunk-NDWWN2YC.js → chunk-D2C4XNF5.js} +4 -4
- package/dist/cli/chunks/{chunk-NGP3DRFU.js → chunk-D3KORDD5.js} +1 -1
- package/dist/cli/chunks/{chunk-YZ3WB4TN.js → chunk-DKBXIBRS.js} +2 -2
- package/dist/cli/chunks/{chunk-WV7K5A7J.js → chunk-DL46KSFX.js} +2 -2
- package/dist/cli/chunks/{chunk-HA4COQ66.js → chunk-DS3XAGGL.js} +2 -2
- package/dist/cli/chunks/{chunk-I4HGIQC2.js → chunk-ECRYU5XG.js} +2 -2
- package/dist/cli/chunks/{chunk-KCQ2GFRN.js → chunk-EFHR7LML.js} +2 -2
- package/dist/cli/chunks/{chunk-3UFAEGSS.js → chunk-EIGXGMOR.js} +1 -1
- package/dist/cli/chunks/{chunk-GSTDQLXR.js → chunk-EL2WLSUI.js} +2 -2
- package/dist/cli/chunks/{chunk-OTOQXMP6.js → chunk-FBVEHGNE.js} +3 -3
- package/dist/cli/chunks/{chunk-53J3R7PZ.js → chunk-FHBIJBZ3.js} +2 -2
- package/dist/cli/chunks/{chunk-DSOEKRQL.js → chunk-FJEIX3IS.js} +2 -2
- package/dist/cli/chunks/{chunk-Y6XYH7EV.js → chunk-FKJ4XZ6E.js} +2 -2
- package/dist/cli/chunks/{chunk-35DP6LX6.js → chunk-FOCGOVIM.js} +2 -2
- package/dist/cli/chunks/{chunk-3QH67ZYC.js → chunk-GGPGY5QT.js} +2 -2
- package/dist/cli/chunks/{chunk-3UE5GART.js → chunk-GOG6L2F7.js} +2 -2
- package/dist/cli/chunks/chunk-HE5VY2BI.js +2 -0
- package/dist/cli/chunks/{chunk-55EAFHVH.js → chunk-HEENVPYF.js} +3 -3
- package/dist/cli/chunks/{chunk-45WEXMKC.js → chunk-HFPT7QXQ.js} +2 -2
- package/dist/cli/chunks/{chunk-6FM5DJHR.js → chunk-HVJXXGB4.js} +4 -4
- package/dist/cli/chunks/{chunk-FDFJHGRZ.js → chunk-HWDE4RB3.js} +2 -2
- package/dist/cli/chunks/{chunk-5NBYV7FL.js → chunk-INJPS7RF.js} +1 -1
- package/dist/cli/chunks/{chunk-GFXAKQ77.js → chunk-IQLFWO5U.js} +2 -2
- package/dist/cli/chunks/{chunk-3DGO7NJK.js → chunk-JJSJSQNJ.js} +1 -1
- package/dist/cli/chunks/{chunk-PTAUZ3XL.js → chunk-JNEG6DKU.js} +1 -1
- package/dist/cli/chunks/{chunk-HT5EZYPZ.js → chunk-JRHKFRUB.js} +2 -2
- package/dist/cli/chunks/{chunk-UPFCV7MX.js → chunk-JZYCZMHN.js} +2 -2
- package/dist/cli/chunks/{chunk-UO2R7ZN3.js → chunk-KHEAWN23.js} +2 -2
- package/dist/cli/chunks/{chunk-6VB4VZCR.js → chunk-KHH5UJXY.js} +2 -2
- package/dist/cli/chunks/{chunk-BYD54ZUF.js → chunk-KLLHSXIC.js} +2 -2
- package/dist/cli/chunks/{chunk-6QKGLIIY.js → chunk-KO5HO6MY.js} +2 -2
- package/dist/cli/chunks/{chunk-ZFCRDCHW.js → chunk-KZSZHP32.js} +2 -2
- package/dist/cli/chunks/{chunk-MRTDOSHR.js → chunk-LHGZRKPO.js} +2 -2
- package/dist/cli/chunks/{chunk-YQTPEQO6.js → chunk-LP6EQI2P.js} +2 -2
- package/dist/cli/chunks/{chunk-6QR7GSNJ.js → chunk-LPLCT2HH.js} +2 -2
- package/dist/cli/chunks/{chunk-WIZOYPFT.js → chunk-LZ34CKYE.js} +2 -2
- package/dist/cli/chunks/{chunk-MYIDAWGQ.js → chunk-MR7V7QVL.js} +2 -2
- package/dist/cli/chunks/{chunk-ZWS7J73Q.js → chunk-MUEFXLGX.js} +1 -1
- package/dist/cli/chunks/chunk-N5SLJOBK.js +2 -0
- package/dist/cli/chunks/{chunk-T4KMG3GJ.js → chunk-NJ5IC6ZR.js} +1 -1
- package/dist/cli/chunks/{chunk-TRPCOY3C.js → chunk-NKX2Z546.js} +1 -1
- package/dist/cli/chunks/{chunk-YHKJYBWK.js → chunk-OURDPRGM.js} +1 -1
- package/dist/cli/chunks/{chunk-ZP237D7K.js → chunk-PFTR6XAO.js} +2 -2
- package/dist/cli/chunks/{chunk-ZD2FS4Q7.js → chunk-PHBE2VBN.js} +2 -2
- package/dist/cli/chunks/{chunk-ZJWDWM6B.js → chunk-PMZJ54WX.js} +3 -3
- package/dist/cli/chunks/{chunk-MBXSHGDD.js → chunk-QI5VGEKC.js} +2 -2
- package/dist/cli/chunks/{chunk-TSA5MUTQ.js → chunk-QKJ4LUAM.js} +2 -2
- package/dist/cli/chunks/{chunk-REXMKRR2.js → chunk-RGJ4RMUR.js} +8 -8
- package/dist/cli/chunks/{chunk-CGYTM7AB.js → chunk-SIIYQLKH.js} +2 -2
- package/dist/cli/chunks/{chunk-TRO45ZLC.js → chunk-SMKRBNO7.js} +1 -1
- package/dist/cli/chunks/{chunk-MO3IIM7L.js → chunk-SP3X35XC.js} +1 -1
- package/dist/cli/chunks/{chunk-KQ7VBUO4.js → chunk-SQQ57TSJ.js} +2 -2
- package/dist/cli/chunks/{chunk-TUMXWCB7.js → chunk-SQZ3H6WR.js} +3 -3
- package/dist/cli/chunks/{chunk-ALX5RJSR.js → chunk-STASZVMY.js} +2 -2
- package/dist/cli/chunks/{chunk-CGYOMD6X.js → chunk-SURZYSSL.js} +1 -1
- package/dist/cli/chunks/{chunk-74YG34UH.js → chunk-T7R5NXJW.js} +2 -2
- package/dist/cli/chunks/{chunk-YDBMYTOV.js → chunk-TD2WOW75.js} +2 -2
- package/dist/cli/chunks/{chunk-XG2JAIQT.js → chunk-TLTRL32T.js} +1 -1
- package/dist/cli/chunks/{chunk-KUDPFLL4.js → chunk-TT2YXNHH.js} +4 -4
- package/dist/cli/chunks/{chunk-IMCYFQWF.js → chunk-UDBSEFUT.js} +2 -2
- package/dist/cli/chunks/{chunk-GHFXURNS.js → chunk-UEFAPTBV.js} +1 -1
- package/dist/cli/chunks/{chunk-PGFHFPDP.js → chunk-URBXUDY3.js} +2 -2
- package/dist/cli/chunks/{chunk-EPH2GZQM.js → chunk-UXCEJ3AV.js} +2 -2
- package/dist/cli/chunks/{chunk-PVLOT5BL.js → chunk-VAKJOTGU.js} +2 -2
- package/dist/cli/chunks/{chunk-VKT4STAE.js → chunk-VMWLVRJO.js} +2 -2
- package/dist/cli/chunks/{chunk-S4F3CNSP.js → chunk-W4HUWBPK.js} +2 -2
- package/dist/cli/chunks/{chunk-IHXDXFUX.js → chunk-W4Y4GFRB.js} +1 -1
- package/dist/cli/chunks/{chunk-J4SEGMXL.js → chunk-WAP7ROO5.js} +2 -2
- package/dist/cli/chunks/{chunk-CNGRXBKO.js → chunk-WLYSZV5U.js} +2 -2
- package/dist/cli/chunks/{chunk-CQT3IEEX.js → chunk-WS4OVAWU.js} +2 -2
- package/dist/cli/chunks/{chunk-2XJRSIM5.js → chunk-WV3R5JFE.js} +2 -2
- package/dist/cli/chunks/{chunk-673PL3DG.js → chunk-XB2GVTY6.js} +1 -1
- package/dist/cli/chunks/{chunk-IMGRYCAT.js → chunk-XD4UFF2Y.js} +1 -1
- package/dist/cli/chunks/{chunk-BQUN2NB4.js → chunk-XOHRM3M7.js} +3 -3
- package/dist/cli/chunks/{chunk-G7XBTBM7.js → chunk-XY5FMZRA.js} +2 -2
- package/dist/cli/chunks/{chunk-NHIQ5HP3.js → chunk-YHZWNT45.js} +2 -2
- package/dist/cli/chunks/{chunk-W73AYMDP.js → chunk-YIVZ5Z4V.js} +1 -1
- package/dist/cli/chunks/{chunk-4QGNSNQL.js → chunk-YR5ZNQT2.js} +2 -2
- package/dist/cli/chunks/{chunk-X6J7VYVD.js → chunk-YTF5G3RQ.js} +2 -2
- package/dist/cli/chunks/{chunk-JUBICWL2.js → chunk-YV2PVXJE.js} +1 -1
- package/dist/cli/chunks/{chunk-6AMXWB6Y.js → chunk-ZEFDSMF4.js} +2 -2
- package/dist/cli/chunks/{chunk-JYJXVTUP.js → chunk-ZETZATNY.js} +2 -2
- package/dist/cli/chunks/{chunk-LLXBL36X.js → chunk-ZLQYGO6N.js} +1 -1
- package/dist/cli/chunks/{chunk-DFOANC7Z.js → chunk-ZSXSUUBS.js} +1 -1
- package/dist/cli/chunks/{chunk-BNYHBEDE.js → chunk-ZV7FGZNJ.js} +2 -2
- package/dist/cli/chunks/{chunk-I27OTSC7.js → chunk-ZWAJV4SN.js} +2 -2
- package/dist/cli/chunks/{ci-3ATT6NMC.js → ci-F4F3SDHP.js} +2 -2
- package/dist/cli/chunks/{ci-output-2JR3J753.js → ci-output-4SUMOQGO.js} +2 -2
- package/dist/cli/chunks/{claude-flow-setup-SG2BSEED.js → claude-flow-setup-NKXPDFMJ.js} +2 -2
- package/dist/cli/chunks/client-3DPPDO2H.js +2 -0
- package/dist/cli/chunks/{cline-installer-A26SXDX6.js → cline-installer-3FMNXEKM.js} +2 -2
- package/dist/cli/chunks/{code-WIXKPGAQ.js → code-5QEDZNKI.js} +2 -2
- package/dist/cli/chunks/{code-index-extractor-W2NO6MR3.js → code-index-extractor-RD5EAOLE.js} +2 -2
- package/dist/cli/chunks/{codex-installer-VBSJHXI4.js → codex-installer-UU4O2FIE.js} +2 -2
- package/dist/cli/chunks/{completions-JFXIUS2V.js → completions-CS6RJLYZ.js} +2 -2
- package/dist/cli/chunks/{complexity-analyzer-GPO4ZAIZ.js → complexity-analyzer-U4DE2QTP.js} +2 -2
- package/dist/cli/chunks/{continuedev-installer-OULSFVLB.js → continuedev-installer-E55EG2TB.js} +2 -2
- package/dist/cli/chunks/{copilot-installer-QS7EG5DW.js → copilot-installer-TVJUX3AZ.js} +2 -2
- package/dist/cli/chunks/{cost-tracker-D7LNB633.js → cost-tracker-IAKDLDJH.js} +2 -2
- package/dist/cli/chunks/{coverage-YUQ4MQYK.js → coverage-LX7UIEKV.js} +3 -3
- package/dist/cli/chunks/cross-domain-router-JOYTUBFT.js +2 -0
- package/dist/cli/chunks/{cursor-installer-UOZGHZHD.js → cursor-installer-2NJ7MK23.js} +2 -2
- package/dist/cli/chunks/{daemon-27TMWDPY.js → daemon-T4CPOFHM.js} +3 -3
- package/dist/cli/chunks/{dag-attention-scheduler-PVAFQBDL.js → dag-attention-scheduler-GAPYYUHZ.js} +2 -2
- package/dist/cli/chunks/{detect-FUKMZ37Y.js → detect-RU5LBCCI.js} +2 -2
- package/dist/cli/chunks/{domain-handler-SP3QB5VN.js → domain-handler-LKPDVPF6.js} +2 -2
- package/dist/cli/chunks/{domain-transfer-A2WL6HEJ.js → domain-transfer-Q76ITWBK.js} +2 -2
- package/dist/cli/chunks/dream-IF2HCRW4.js +2 -0
- package/dist/cli/chunks/esm-node-LNRP5BNU.js +2 -0
- package/dist/cli/chunks/{eval-6MIZUXEO.js → eval-TFFIZGTW.js} +2 -2
- package/dist/cli/chunks/{fast-paths-XT542LOU.js → fast-paths-V6KA3VWH.js} +2 -2
- package/dist/cli/chunks/{feature-flags-WBF5B6WE.js → feature-flags-DWS4XL2P.js} +2 -2
- package/dist/cli/chunks/{feature-flags-NWACCWCB.js → feature-flags-MYSY53UU.js} +2 -2
- package/dist/cli/chunks/{file-discovery-GKCJFWTD.js → file-discovery-AMKZRFLT.js} +2 -2
- package/dist/cli/chunks/{fleet-V2MKGNMG.js → fleet-B2BJFKEV.js} +3 -3
- package/dist/cli/chunks/{gnn-wrapper-HUUMTTSV.js → gnn-wrapper-SOJHQ7VC.js} +2 -2
- package/dist/cli/chunks/{heartbeat-handler-UY3TCEN7.js → heartbeat-handler-O7FF6NRE.js} +4 -4
- package/dist/cli/chunks/{heartbeat-scheduler-YWXPSCH2.js → heartbeat-scheduler-WOGW5R7J.js} +2 -2
- package/dist/cli/chunks/hnsw-adapter-UPX4AXOQ.js +2 -0
- package/dist/cli/chunks/hnsw-index-CSI2EXXR.js +2 -0
- package/dist/cli/chunks/{hnsw-legacy-bridge-IYYYSRK5.js → hnsw-legacy-bridge-CC5YS47X.js} +2 -2
- package/dist/cli/chunks/{hnswlib-node-WDWAE74R.js → hnswlib-node-FTWYRETS.js} +2 -2
- package/dist/cli/chunks/{hooks-DTBJK6LL.js → hooks-ETOFFBMV.js} +6 -6
- package/dist/cli/chunks/{hypergraph-engine-K47IBQNO.js → hypergraph-engine-FGAHFWFO.js} +2 -2
- package/dist/cli/chunks/{hypergraph-handler-2NDPG2N4.js → hypergraph-handler-GNWJD7E3.js} +3 -3
- package/dist/cli/chunks/impact-analyzer-2YVBHMES.js +2 -0
- package/dist/cli/chunks/init-handler-BEYOLKQO.js +69 -0
- package/dist/cli/chunks/init-wizard-V6GZQMMR.js +2 -0
- package/dist/cli/chunks/kernel-EHJ4SP2Y.js +2 -0
- package/dist/cli/chunks/{kilocode-installer-XZAHWQEA.js → kilocode-installer-ED6LYSEM.js} +2 -2
- package/dist/cli/chunks/{kiro-installer-53C7DXEF.js → kiro-installer-NG77T5YR.js} +2 -2
- package/dist/cli/chunks/knowledge-graph-ZYXBWGKL.js +2 -0
- package/dist/cli/chunks/{learning-XYW2EEVX.js → learning-P3WY3LTI.js} +3 -3
- package/dist/cli/chunks/{llm-router-64SKMJ7B.js → llm-router-ETSFMOWS.js} +2 -2
- package/dist/cli/chunks/{load-HVL4O7EY.js → load-PP3GVQT7.js} +2 -2
- package/dist/cli/chunks/load-test-VDZEYGKV.js +2 -0
- package/dist/cli/chunks/{mcp-4H2NJ3EO.js → mcp-B2VX7EKL.js} +2 -2
- package/dist/cli/chunks/{memory-2CD4BAJK.js → memory-SBZQ6MZ4.js} +5 -5
- package/dist/cli/chunks/memory-backend-IB3BU4VM.js +2 -0
- package/dist/cli/chunks/{memory-handlers-XRYZBE5I.js → memory-handlers-5RMGG2CR.js} +2 -2
- package/dist/cli/chunks/{opencode-installer-I23QAZC6.js → opencode-installer-PIDIFO2L.js} +2 -2
- package/dist/cli/chunks/orchestrator-FSGUODZI.js +373 -0
- package/dist/cli/chunks/{pipeline-7UN7LP6B.js → pipeline-BQ5MWZPT.js} +2 -2
- package/dist/cli/chunks/{platform-P44YUHPK.js → platform-6EJK4QMD.js} +2 -2
- package/dist/cli/chunks/{plugin-YWYR7FKS.js → plugin-EDGOMUJY.js} +2 -2
- package/dist/cli/chunks/{prime-radiant-advanced-wasm-A7CDGUK6.js → prime-radiant-advanced-wasm-4S6FYKPP.js} +2 -2
- package/dist/cli/chunks/protocol-executor-H2ZG7JBJ.js +2 -0
- package/dist/cli/chunks/{protocol-handler-BM7RLC4Z.js → protocol-handler-ETM7PMQU.js} +2 -2
- package/dist/cli/chunks/{prove-UVBYODRV.js → prove-YUBORZAP.js} +2 -2
- package/dist/cli/chunks/qe-reasoning-bank-ZCSHSKZU.js +2 -0
- package/dist/cli/chunks/{quality-4L42LN5Q.js → quality-XPZDC5FJ.js} +2 -2
- package/dist/cli/chunks/queen-coordinator-NEC373VS.js +2 -0
- package/dist/cli/chunks/{real-embeddings-4CYZTWHT.js → real-embeddings-KTDUTUJL.js} +2 -2
- package/dist/cli/chunks/{roocode-installer-UOSJS7KT.js → roocode-installer-WADVKI3P.js} +2 -2
- package/dist/cli/chunks/router-3PP5XRTD.js +2 -0
- package/dist/cli/chunks/routing-feedback-JNUFV2X3.js +2 -0
- package/dist/cli/chunks/{routing-handler-43G4D5UP.js → routing-handler-OOPOYHEV.js} +2 -2
- package/dist/cli/chunks/ruvector-commands-UPEZL4OK.js +8 -0
- package/dist/cli/chunks/{rvf-dual-writer-USU2IWO7.js → rvf-dual-writer-TOBBCJ7K.js} +2 -2
- package/dist/cli/chunks/{rvf-migration-adapter-DZMYVJJY.js → rvf-migration-adapter-U7UM2U36.js} +2 -2
- package/dist/cli/chunks/{rvf-migration-coordinator-AZ5JDT33.js → rvf-migration-coordinator-CUX3EU2X.js} +2 -2
- package/dist/cli/chunks/rvf-native-adapter-PTOZXG4W.js +2 -0
- package/dist/cli/chunks/safe-db-CGYNYUES.js +2 -0
- package/dist/cli/chunks/schedule-NRN4WOHX.js +2 -0
- package/dist/cli/chunks/scheduler-DRAQGYLL.js +2 -0
- package/dist/cli/chunks/{security-RXG4H3UK.js → security-AQ4N5YKV.js} +3 -3
- package/dist/cli/chunks/shared-rvf-adapter-6UVVDOHK.js +2 -0
- package/dist/cli/chunks/{shared-rvf-dual-writer-2PUFIGX5.js → shared-rvf-dual-writer-OKRIHVSY.js} +2 -2
- package/dist/cli/chunks/sqlite-persistence-3WG7PBPL.js +2 -0
- package/dist/cli/chunks/{status-handler-SXQKG6VP.js → status-handler-HSJLJPNG.js} +2 -2
- package/dist/cli/chunks/{structural-health-LPXOOMG3.js → structural-health-KWZAS7ON.js} +2 -2
- package/dist/cli/chunks/{sync-5NX7GQQY.js → sync-6L5Z4IWH.js} +2 -2
- package/dist/cli/chunks/{task-handler-Y4KVNCOI.js → task-handler-KEAIPB6G.js} +2 -2
- package/dist/cli/chunks/{task-handlers-CG27Z3CP.js → task-handlers-BPDN6OSM.js} +2 -2
- package/dist/cli/chunks/{test-PWVTWOWS.js → test-PICO6RLU.js} +4 -4
- package/dist/cli/chunks/{test-scheduling-5S5EBVE7.js → test-scheduling-BI3R3DX5.js} +3 -3
- package/dist/cli/chunks/{token-bootstrap-QMPUKGQC.js → token-bootstrap-KMQC6ALU.js} +2 -2
- package/dist/cli/chunks/{token-usage-OZNRTJLE.js → token-usage-QGDX7MLB.js} +2 -2
- package/dist/cli/chunks/{transformers-DRPOBWCW.js → transformers-UNRYJSIU.js} +2 -2
- package/dist/cli/chunks/{tree-sitter-wasm-parser-VLX6HQW2.js → tree-sitter-wasm-parser-U22JYPOG.js} +2 -2
- package/dist/cli/chunks/{types-TUF6HUPE.js → types-CYXAGOY5.js} +2 -2
- package/dist/cli/chunks/unified-memory-EXRANFUS.js +2 -0
- package/dist/cli/chunks/unified-memory-hnsw-GB752D44.js +2 -0
- package/dist/cli/chunks/unified-persistence-V2B3YWFD.js +2 -0
- package/dist/cli/chunks/{validate-H7QFV2CI.js → validate-IUY5BWAV.js} +2 -2
- package/dist/cli/chunks/{validate-swarm-BGH6YM4S.js → validate-swarm-FXFKSEOI.js} +2 -2
- package/dist/cli/chunks/{vibium-DQOGR2PH.js → vibium-DKZ64NR3.js} +2 -2
- package/dist/cli/chunks/visual-security-YMPI7X7V.js +2 -0
- package/dist/cli/chunks/{web-tree-sitter-TOI4XJIL.js → web-tree-sitter-WQDTN5CV.js} +2 -2
- package/dist/cli/chunks/{windsurf-installer-EUMBFA42.js → windsurf-installer-O5WOBFOQ.js} +2 -2
- package/dist/cli/chunks/witness-chain-7LIPUVMI.js +2 -0
- package/dist/cli/chunks/{witness-chain-MWPUR4Q2.js → witness-chain-F7WXEVJA.js} +2 -2
- package/dist/cli/chunks/{workflow-V5HI3AQR.js → workflow-47524I5G.js} +4 -4
- package/dist/cli/chunks/workflow-orchestrator-QPIAR4LI.js +2 -0
- package/dist/cli/chunks/{wrappers-VOU4IVAQ.js → wrappers-MP2HWQD7.js} +2 -2
- package/dist/cli/commands/init.js +1 -1
- package/dist/cli/commands/ruvector-commands.js +5 -2
- package/dist/cli/handlers/init-handler.d.ts +53 -0
- package/dist/cli/handlers/init-handler.js +53 -3
- package/dist/cli/index.js +5 -4
- package/dist/init/phases/04-database.js +19 -0
- package/dist/init/phases/06-code-intelligence.d.ts +14 -9
- package/dist/init/phases/06-code-intelligence.js +72 -34
- package/dist/init/phases/10-workers.js +2 -0
- package/dist/init/phases/phase-interface.d.ts +16 -4
- package/dist/integrations/ruvector/feature-flags.d.ts +27 -5
- package/dist/integrations/ruvector/feature-flags.js +15 -2
- package/dist/kernel/hnsw-adapter.js +1 -1
- package/dist/kernel/native-hnsw-backend.d.ts +100 -37
- package/dist/kernel/native-hnsw-backend.js +207 -192
- package/dist/kernel/unified-memory.js +15 -10
- package/dist/mcp/bundle.js +189 -189
- package/package.json +1 -1
- package/dist/cli/chunks/adapter-HOBCL2VF.js +0 -2
- package/dist/cli/chunks/aqe-learning-engine-77FVV2ZJ.js +0 -2
- package/dist/cli/chunks/base-2IXMPGJV.js +0 -2
- package/dist/cli/chunks/browser-workflow-AQYKV2NE.js +0 -2
- package/dist/cli/chunks/chunk-PMFZHFCB.js +0 -2
- package/dist/cli/chunks/chunk-Z2GK7COI.js +0 -2
- package/dist/cli/chunks/client-3CA57H5P.js +0 -2
- package/dist/cli/chunks/cross-domain-router-GECFIALH.js +0 -2
- package/dist/cli/chunks/dream-CJ4MKM5G.js +0 -2
- package/dist/cli/chunks/esm-node-X7U3YS4M.js +0 -2
- package/dist/cli/chunks/hnsw-adapter-LJGOH772.js +0 -2
- package/dist/cli/chunks/hnsw-index-V6SST2MB.js +0 -2
- package/dist/cli/chunks/impact-analyzer-MTOVFD3O.js +0 -2
- package/dist/cli/chunks/init-handler-VRZS6KO2.js +0 -68
- package/dist/cli/chunks/init-wizard-IH6PSRJY.js +0 -2
- package/dist/cli/chunks/kernel-3MC7WV5G.js +0 -2
- package/dist/cli/chunks/knowledge-graph-HLUQJ2GI.js +0 -2
- package/dist/cli/chunks/load-test-VVVME7GZ.js +0 -2
- package/dist/cli/chunks/memory-backend-O3U2QDU3.js +0 -2
- package/dist/cli/chunks/orchestrator-7BL6R54P.js +0 -370
- package/dist/cli/chunks/protocol-executor-HLNTSRYC.js +0 -2
- package/dist/cli/chunks/qe-reasoning-bank-JK5CK4D4.js +0 -2
- package/dist/cli/chunks/queen-coordinator-FLF6XYLX.js +0 -2
- package/dist/cli/chunks/router-DUFZW3WQ.js +0 -2
- package/dist/cli/chunks/routing-feedback-6U76TI6X.js +0 -2
- package/dist/cli/chunks/ruvector-commands-H7A7LG6Q.js +0 -8
- package/dist/cli/chunks/rvf-native-adapter-4UD7SUMB.js +0 -2
- package/dist/cli/chunks/safe-db-32Q5RXHR.js +0 -2
- package/dist/cli/chunks/schedule-ZHCM3L6H.js +0 -2
- package/dist/cli/chunks/scheduler-HBSSLDOD.js +0 -2
- package/dist/cli/chunks/shared-rvf-adapter-PLZVKKW7.js +0 -2
- package/dist/cli/chunks/sqlite-persistence-YUZMB44Y.js +0 -2
- package/dist/cli/chunks/unified-memory-3D4VDY4G.js +0 -2
- package/dist/cli/chunks/unified-memory-hnsw-AAQ5IA6M.js +0 -2
- package/dist/cli/chunks/unified-persistence-PY7MVJTL.js +0 -2
- package/dist/cli/chunks/visual-security-ZBNB7LEG.js +0 -2
- package/dist/cli/chunks/witness-chain-CS76OJFL.js +0 -2
- package/dist/cli/chunks/workflow-orchestrator-FS4HGYI7.js +0 -2
|
@@ -1,28 +1,55 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Native HNSW Backend via
|
|
2
|
+
* Native HNSW Backend via hnswlib-node
|
|
3
3
|
*
|
|
4
|
-
* Provides a high-performance HNSW index backed by the
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* higher throughput.
|
|
4
|
+
* Provides a high-performance HNSW index backed by hnswlib-node, the
|
|
5
|
+
* canonical Node.js binding for the C++ Hnswlib reference implementation
|
|
6
|
+
* (Yury Malkov's HNSW library, the same code Pinecone, Weaviate, Qdrant
|
|
7
|
+
* and chromadb-default-embed are built on top of).
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* This backend implements the same IHnswIndexProvider interface as
|
|
10
|
+
* ProgressiveHnswBackend but delegates vector storage and search to
|
|
11
|
+
* native code for sublinear search latency on large indexes.
|
|
13
12
|
*
|
|
14
|
-
*
|
|
13
|
+
* History — issue #399 (April 2026):
|
|
14
|
+
* This backend previously wrapped @ruvector/router's VectorDb. Empirical
|
|
15
|
+
* verification (scripts/diagnose-issue-399*.mjs) found four serious bugs
|
|
16
|
+
* in @ruvector/router 0.1.28:
|
|
17
|
+
*
|
|
18
|
+
* 1. HNSW search returned essentially random results — recall@10 ≈ 0%
|
|
19
|
+
* on textbook unit-Gaussian random vectors at default M/efC/efS,
|
|
20
|
+
* could not find self-vectors. Pumping efSearch to N≈index-size
|
|
21
|
+
* restored correctness but defeated HNSW's purpose.
|
|
22
|
+
* 2. The VectorDb constructor unconditionally created a `vectors.db`
|
|
23
|
+
* redb file in the current working directory (NOT in .agentic-qe/),
|
|
24
|
+
* violating the unified memory architecture and polluting users'
|
|
25
|
+
* project roots with multi-MB persistence files they never asked for.
|
|
26
|
+
* 3. The redb file held a process-wide exclusive lock — only ONE
|
|
27
|
+
* VectorDb instance could exist per process. Subsequent constructors
|
|
28
|
+
* threw "Database already open. Cannot acquire lock."
|
|
29
|
+
* 4. NAPI dispose did not synchronously release the redb lock, so the
|
|
30
|
+
* lock outlived our dispose() call and caused the v3.9.5 futex
|
|
31
|
+
* deadlock when the indexer tried to recreate after reset.
|
|
32
|
+
*
|
|
33
|
+
* Comparison test on 1000 vector self-query (linux-arm64, M=16, efC=200,
|
|
34
|
+
* efS=100, cosine):
|
|
35
|
+
* - @ruvector/router 0.1.28: recall@10 = 10%, top-1 = wrong vector
|
|
36
|
+
* - hnswlib-node 3.0.0: recall@10 = 100%, top-1 = id 42 ✓
|
|
37
|
+
*
|
|
38
|
+
* The migration to hnswlib-node fixes all four bugs in one swap.
|
|
39
|
+
*
|
|
40
|
+
* @see ADR-090: hnswlib-node migration
|
|
41
|
+
* @see https://github.com/proffesor-for-testing/agentic-qe/issues/399
|
|
15
42
|
* @module kernel/native-hnsw-backend
|
|
16
43
|
*/
|
|
17
44
|
import { createRequire } from 'module';
|
|
18
45
|
import { DEFAULT_HNSW_CONFIG } from './hnsw-index-provider.js';
|
|
19
|
-
//
|
|
46
|
+
// hnswlib-node is a native CommonJS module distributed via node-gyp
|
|
20
47
|
const esmRequire = createRequire(import.meta.url);
|
|
21
48
|
// ============================================================================
|
|
22
49
|
// Error Types
|
|
23
50
|
// ============================================================================
|
|
24
51
|
/**
|
|
25
|
-
* Thrown when
|
|
52
|
+
* Thrown when hnswlib-node native binary is not available.
|
|
26
53
|
* The HnswAdapter factory catches this to fall back to the JS backend.
|
|
27
54
|
*/
|
|
28
55
|
export class NativeHnswUnavailableError extends Error {
|
|
@@ -38,7 +65,7 @@ let nativeModule = null;
|
|
|
38
65
|
let nativeLoadAttempted = false;
|
|
39
66
|
let nativeLoadError = null;
|
|
40
67
|
/**
|
|
41
|
-
* Attempt to load the
|
|
68
|
+
* Attempt to load the hnswlib-node native module.
|
|
42
69
|
* Caches the result so subsequent calls are instant.
|
|
43
70
|
*/
|
|
44
71
|
function loadNativeModule() {
|
|
@@ -49,11 +76,10 @@ function loadNativeModule() {
|
|
|
49
76
|
}
|
|
50
77
|
nativeLoadAttempted = true;
|
|
51
78
|
try {
|
|
52
|
-
//
|
|
53
|
-
const mod = esmRequire('
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
throw new Error('@ruvector/router module missing VectorDb or DistanceMetric exports');
|
|
79
|
+
// hnswlib-node ships a CommonJS default export with HierarchicalNSW on it
|
|
80
|
+
const mod = esmRequire('hnswlib-node');
|
|
81
|
+
if (!mod.HierarchicalNSW) {
|
|
82
|
+
throw new Error('hnswlib-node module missing HierarchicalNSW export');
|
|
57
83
|
}
|
|
58
84
|
nativeModule = mod;
|
|
59
85
|
return nativeModule;
|
|
@@ -88,22 +114,6 @@ export function isNativeModuleAvailable() {
|
|
|
88
114
|
// ============================================================================
|
|
89
115
|
// Vector Math Helpers
|
|
90
116
|
// ============================================================================
|
|
91
|
-
function computeNorm(v) {
|
|
92
|
-
let sum = 0;
|
|
93
|
-
for (let i = 0; i < v.length; i++)
|
|
94
|
-
sum += v[i] * v[i];
|
|
95
|
-
return Math.sqrt(sum);
|
|
96
|
-
}
|
|
97
|
-
function fastCosineSimilarity(a, b, normA, normB) {
|
|
98
|
-
const denom = normA * normB;
|
|
99
|
-
if (denom === 0)
|
|
100
|
-
return 0;
|
|
101
|
-
let dot = 0;
|
|
102
|
-
const len = Math.min(a.length, b.length);
|
|
103
|
-
for (let i = 0; i < len; i++)
|
|
104
|
-
dot += a[i] * b[i];
|
|
105
|
-
return dot / denom;
|
|
106
|
-
}
|
|
107
117
|
/**
|
|
108
118
|
* Resize a vector to the target dimensions.
|
|
109
119
|
* Shrink: average adjacent values. Grow: zero-pad.
|
|
@@ -131,29 +141,47 @@ function resizeVector(vector, targetDim) {
|
|
|
131
141
|
}
|
|
132
142
|
return result;
|
|
133
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Convert a Float32Array to a plain number[] (required by hnswlib-node API).
|
|
146
|
+
*/
|
|
147
|
+
function toNumberArray(v) {
|
|
148
|
+
const out = new Array(v.length);
|
|
149
|
+
for (let i = 0; i < v.length; i++)
|
|
150
|
+
out[i] = v[i];
|
|
151
|
+
return out;
|
|
152
|
+
}
|
|
134
153
|
// ============================================================================
|
|
135
154
|
// NativeHnswBackend
|
|
136
155
|
// ============================================================================
|
|
137
156
|
/**
|
|
138
|
-
*
|
|
157
|
+
* Initial maxElements capacity for a new HierarchicalNSW index. The index
|
|
158
|
+
* will be doubled in place via resizeIndex() each time it fills up, so this
|
|
159
|
+
* value only controls the initial allocation cost. Tuned for AQE's typical
|
|
160
|
+
* code-intelligence index size (~2.5k vectors today, expected to grow).
|
|
161
|
+
*/
|
|
162
|
+
const INITIAL_MAX_ELEMENTS = 10_000;
|
|
163
|
+
/**
|
|
164
|
+
* Native HNSW backend using hnswlib-node HierarchicalNSW.
|
|
139
165
|
*
|
|
140
166
|
* Provides the same IHnswIndexProvider interface as ProgressiveHnswBackend
|
|
141
|
-
* but delegates all vector operations to
|
|
142
|
-
* for
|
|
167
|
+
* but delegates all vector operations to the C++ Hnswlib reference
|
|
168
|
+
* implementation for sublinear search latency at scale.
|
|
143
169
|
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
170
|
+
* Key differences from the previous @ruvector/router-backed implementation:
|
|
171
|
+
* - No local vectorStore mirror — hnswlib-node returns correct distances
|
|
172
|
+
* directly, so re-scoring is unnecessary.
|
|
173
|
+
* - No process-wide singleton lock — multiple instances coexist.
|
|
174
|
+
* - No vectors.db pollution — persistence is opt-in via writeIndex().
|
|
175
|
+
* - resizeIndex() doubling on overflow — grows past initial maxElements.
|
|
148
176
|
*/
|
|
149
177
|
export class NativeHnswBackend {
|
|
150
178
|
config;
|
|
151
|
-
|
|
179
|
+
nativeIndex = null;
|
|
180
|
+
currentMaxElements = INITIAL_MAX_ELEMENTS;
|
|
181
|
+
/** Tracks live ids so size() and remove() can correctly distinguish soft-deleted slots. */
|
|
182
|
+
liveIds = new Set();
|
|
183
|
+
/** Optional metadata mirror — only stored when callers attach metadata. */
|
|
152
184
|
metadataStore = new Map();
|
|
153
|
-
vectorStore = new Map();
|
|
154
|
-
normStore = new Map();
|
|
155
|
-
operationLock = Promise.resolve();
|
|
156
|
-
highFallbackWarningEmitted = false;
|
|
157
185
|
_metrics = {
|
|
158
186
|
totalSearches: 0,
|
|
159
187
|
totalAdds: 0,
|
|
@@ -171,148 +199,161 @@ export class NativeHnswBackend {
|
|
|
171
199
|
* Create a NativeHnswBackend.
|
|
172
200
|
*
|
|
173
201
|
* @param config - HNSW configuration overrides
|
|
174
|
-
* @throws {NativeHnswUnavailableError} If
|
|
202
|
+
* @throws {NativeHnswUnavailableError} If hnswlib-node is not available
|
|
175
203
|
*/
|
|
176
204
|
constructor(config) {
|
|
177
205
|
this.config = { ...DEFAULT_HNSW_CONFIG, ...config };
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
206
|
+
this.nativeIndex = this.createFreshIndex();
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Build a fresh HierarchicalNSW with the configured parameters.
|
|
210
|
+
*
|
|
211
|
+
* Used both at construction time and on `clear()` to guarantee a clean
|
|
212
|
+
* graph. hnswlib-node's `markDelete` is a soft tombstone — it leaves the
|
|
213
|
+
* graph node in place and only hides it from search. Across many cycles
|
|
214
|
+
* of `clear() → re-add` (which happens during long test runs that share
|
|
215
|
+
* the singleton HnswAdapter registry), the tombstoned slots can interact
|
|
216
|
+
* pathologically with `addPoint(label, replaceDeleted=true)` and produce
|
|
217
|
+
* duplicate-label results from `searchKnn`. Recreating the underlying
|
|
218
|
+
* index on `clear()` is the only way to guarantee O(1) clean state
|
|
219
|
+
* regardless of how the wrapper is used over time.
|
|
220
|
+
*/
|
|
221
|
+
createFreshIndex() {
|
|
182
222
|
const native = loadNativeModule();
|
|
183
|
-
const
|
|
184
|
-
? native.DistanceMetric.Cosine
|
|
185
|
-
: native.DistanceMetric.Euclidean;
|
|
223
|
+
const space = this.config.metric === 'euclidean' ? 'l2' : 'cosine';
|
|
186
224
|
try {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
225
|
+
const idx = new native.HierarchicalNSW(space, this.config.dimensions);
|
|
226
|
+
idx.initIndex(this.currentMaxElements, this.config.M, this.config.efConstruction,
|
|
227
|
+
// randomSeed: deterministic per-config seed so test runs are reproducible.
|
|
228
|
+
// hnswlib-node uses this to randomize level assignment during graph construction.
|
|
229
|
+
100,
|
|
230
|
+
// allowReplaceDeleted: true so markDelete()-ed slots can be reused by future addPoint().
|
|
231
|
+
true);
|
|
232
|
+
idx.setEf(this.config.efSearch);
|
|
233
|
+
return idx;
|
|
194
234
|
}
|
|
195
235
|
catch (err) {
|
|
196
236
|
const message = err instanceof Error ? err.message : String(err);
|
|
197
|
-
throw new NativeHnswUnavailableError(`
|
|
237
|
+
throw new NativeHnswUnavailableError(`HierarchicalNSW init failed: ${message}`);
|
|
198
238
|
}
|
|
199
239
|
}
|
|
200
240
|
// ============================================================================
|
|
201
241
|
// IHnswIndexProvider Implementation
|
|
202
242
|
// ============================================================================
|
|
203
243
|
add(id, vector, metadata) {
|
|
244
|
+
if (!this.nativeIndex) {
|
|
245
|
+
throw new Error('NativeHnswBackend has been disposed');
|
|
246
|
+
}
|
|
204
247
|
const normalized = this.normalizeVector(vector);
|
|
205
|
-
|
|
206
|
-
//
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
248
|
+
// Grow the index if we're about to overflow. hnswlib-node's resizeIndex
|
|
249
|
+
// doubles in-place; this gives amortized O(1) growth at the cost of a
|
|
250
|
+
// single memcpy on each doubling. Required for production use cases
|
|
251
|
+
// where the index size is not known at construction time.
|
|
252
|
+
if (!this.liveIds.has(id) && this.liveIds.size >= this.currentMaxElements) {
|
|
253
|
+
const newMax = this.currentMaxElements * 2;
|
|
254
|
+
this.nativeIndex.resizeIndex(newMax);
|
|
255
|
+
this.currentMaxElements = newMax;
|
|
212
256
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
this.
|
|
257
|
+
// hnswlib-node treats addPoint with an existing label as an UPDATE.
|
|
258
|
+
// The replaceDeleted flag lets us reuse soft-deleted slots transparently.
|
|
259
|
+
this.nativeIndex.addPoint(toNumberArray(normalized), id, true);
|
|
260
|
+
this.liveIds.add(id);
|
|
216
261
|
if (metadata) {
|
|
217
262
|
this.metadataStore.set(id, metadata);
|
|
218
263
|
}
|
|
264
|
+
else {
|
|
265
|
+
this.metadataStore.delete(id);
|
|
266
|
+
}
|
|
219
267
|
this._metrics.totalAdds++;
|
|
220
268
|
}
|
|
221
269
|
search(query, k) {
|
|
222
|
-
|
|
223
|
-
|
|
270
|
+
if (!this.nativeIndex) {
|
|
271
|
+
throw new Error('NativeHnswBackend has been disposed');
|
|
272
|
+
}
|
|
273
|
+
if (this.liveIds.size === 0 || k <= 0)
|
|
224
274
|
return [];
|
|
275
|
+
const start = performance.now();
|
|
225
276
|
const normalizedQuery = this.normalizeVector(query);
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
catch {
|
|
259
|
-
// If native search fails, fall back to brute-force over stored vectors
|
|
260
|
-
this._metrics.fallbackSearchCount++;
|
|
261
|
-
this._metrics.bruteForceSearchCount++;
|
|
262
|
-
console.warn(`[NativeHNSW] FALLBACK: Using brute-force linear scan (@ruvector/router search failed). Index size: ${this.vectorStore.size}`);
|
|
263
|
-
results = this.bruteForceSearch(normalizedQuery, queryNorm, actualK);
|
|
277
|
+
const actualK = Math.min(k, this.liveIds.size);
|
|
278
|
+
// Overshoot k by 2x (capped at index size) so that defensive dedup
|
|
279
|
+
// below can drop any duplicate-label entries hnswlib may return after
|
|
280
|
+
// long add/remove churn and still leave us with at least k unique
|
|
281
|
+
// results when possible.
|
|
282
|
+
const overshoot = Math.min(actualK * 2, this.liveIds.size);
|
|
283
|
+
const native = this.nativeIndex.searchKnn(toNumberArray(normalizedQuery), overshoot);
|
|
284
|
+
// Convert hnswlib-node distances to similarity scores.
|
|
285
|
+
// cosine space: distance = 1 - cos_sim, so similarity = 1 - distance
|
|
286
|
+
// l2 space: distance = sum((x_i - y_i)^2), no clean similarity;
|
|
287
|
+
// we negate so that "higher = closer" remains the contract
|
|
288
|
+
const isCosine = this.config.metric === 'cosine';
|
|
289
|
+
// Defensive dedup: keep only the best score for each id. hnswlib-node
|
|
290
|
+
// can return duplicate labels after pathological add/remove cycles
|
|
291
|
+
// (verified empirically — see ADR-090 / issue #399 follow-up). We
|
|
292
|
+
// can't fix the C++ side from here, but we can make sure callers
|
|
293
|
+
// never see duplicates by collapsing them at the wrapper boundary.
|
|
294
|
+
const seen = new Map();
|
|
295
|
+
for (let i = 0; i < native.neighbors.length; i++) {
|
|
296
|
+
const id = native.neighbors[i];
|
|
297
|
+
// Skip ids we've already removed at the wrapper level. liveIds is
|
|
298
|
+
// the source of truth for "is this id currently in the index" —
|
|
299
|
+
// hnswlib's internal markDelete state can lag.
|
|
300
|
+
if (!this.liveIds.has(id))
|
|
301
|
+
continue;
|
|
302
|
+
const distance = native.distances[i];
|
|
303
|
+
const score = isCosine ? 1 - distance : -distance;
|
|
304
|
+
const existing = seen.get(id);
|
|
305
|
+
if (existing === undefined || score > existing.score) {
|
|
306
|
+
seen.set(id, { id, score, metadata: this.metadataStore.get(id) });
|
|
307
|
+
}
|
|
264
308
|
}
|
|
309
|
+
const results = Array.from(seen.values());
|
|
310
|
+
// hnswlib-node returns results in ascending distance order (best first
|
|
311
|
+
// for distance), but our SearchResult contract is descending score
|
|
312
|
+
// (best first for similarity). Sort defensively to guarantee the
|
|
313
|
+
// contract regardless of how the metric maps.
|
|
314
|
+
results.sort((a, b) => b.score - a.score);
|
|
315
|
+
// Truncate to the requested k after dedup.
|
|
316
|
+
if (results.length > actualK)
|
|
317
|
+
results.length = actualK;
|
|
265
318
|
const elapsed = performance.now() - start;
|
|
266
319
|
this.updateSearchMetrics(elapsed);
|
|
267
|
-
|
|
268
|
-
this._metrics.fallbackRate =
|
|
269
|
-
this._metrics.fallbackSearchCount / this._metrics.totalSearches;
|
|
270
|
-
this._metrics.allSearchesBruteForce =
|
|
271
|
-
this._metrics.nativeSearchCount === 0 && this._metrics.totalSearches > 0;
|
|
272
|
-
// One-time warning when fallback rate is dangerously high
|
|
273
|
-
if (!this.highFallbackWarningEmitted &&
|
|
274
|
-
this._metrics.fallbackRate > 0.5 &&
|
|
275
|
-
this._metrics.totalSearches >= 10) {
|
|
276
|
-
this.highFallbackWarningEmitted = true;
|
|
277
|
-
console.error(`[NativeHNSW] WARNING: ${(this._metrics.fallbackRate * 100).toFixed(0)}% of searches are using brute-force fallback. Native HNSW may not be functioning correctly. Consider rebuilding the index or checking @ruvector/router installation.`);
|
|
278
|
-
}
|
|
320
|
+
this._metrics.nativeSearchCount = this._metrics.totalSearches;
|
|
279
321
|
if (elapsed > 50) {
|
|
280
322
|
console.warn(`[NativeHNSW] search took ${elapsed.toFixed(1)}ms (k=${k}, results=${results.length})`);
|
|
281
323
|
}
|
|
282
324
|
return results;
|
|
283
325
|
}
|
|
284
326
|
remove(id) {
|
|
285
|
-
if (!this.
|
|
327
|
+
if (!this.nativeIndex)
|
|
328
|
+
return false;
|
|
329
|
+
if (!this.liveIds.has(id))
|
|
286
330
|
return false;
|
|
287
331
|
try {
|
|
288
|
-
this.
|
|
332
|
+
this.nativeIndex.markDelete(id);
|
|
289
333
|
}
|
|
290
334
|
catch {
|
|
291
|
-
//
|
|
335
|
+
// markDelete throws if the label was never added; treat as not-found.
|
|
336
|
+
return false;
|
|
292
337
|
}
|
|
293
|
-
this.
|
|
294
|
-
this.normStore.delete(id);
|
|
338
|
+
this.liveIds.delete(id);
|
|
295
339
|
this.metadataStore.delete(id);
|
|
296
340
|
this._metrics.totalRemoves++;
|
|
297
341
|
return true;
|
|
298
342
|
}
|
|
299
343
|
size() {
|
|
300
|
-
return this.
|
|
344
|
+
return this.liveIds.size;
|
|
301
345
|
}
|
|
302
346
|
dimensions() {
|
|
303
347
|
return this.config.dimensions;
|
|
304
348
|
}
|
|
305
349
|
recall() {
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
if (ratio >= 3)
|
|
314
|
-
return 0.95;
|
|
315
|
-
return 0.90;
|
|
350
|
+
// hnswlib-node with default M=16, efConstruction=200, efSearch=100 hits
|
|
351
|
+
// 100% recall@10 on the project's own qe-kernel fixture and on textbook
|
|
352
|
+
// Gaussian random vectors (verified empirically — see ADR-090). At very
|
|
353
|
+
// large N or aggressively low efSearch, recall is approximate; we report
|
|
354
|
+
// a slightly conservative 0.99 to make that property visible to callers
|
|
355
|
+
// that care about the difference between exact and approximate search.
|
|
356
|
+
return 0.99;
|
|
316
357
|
}
|
|
317
358
|
// ============================================================================
|
|
318
359
|
// Public Utilities
|
|
@@ -337,45 +378,47 @@ export class NativeHnswBackend {
|
|
|
337
378
|
}
|
|
338
379
|
/**
|
|
339
380
|
* Clear all vectors from the index.
|
|
340
|
-
*
|
|
381
|
+
*
|
|
382
|
+
* Recreates the underlying HierarchicalNSW from scratch rather than
|
|
383
|
+
* `markDelete`-ing each label. The tombstone-only approach leaks state
|
|
384
|
+
* across `clear() → re-add` cycles (the C++ graph keeps every deleted
|
|
385
|
+
* slot, and `addPoint(label, replaceDeleted=true)` can produce
|
|
386
|
+
* duplicate-label results from `searchKnn` after many cycles). A fresh
|
|
387
|
+
* index is the only guarantee of O(1) clean state and is what callers
|
|
388
|
+
* who use `clear()` actually expect.
|
|
341
389
|
*/
|
|
342
390
|
clear() {
|
|
343
|
-
if (!this.
|
|
391
|
+
if (!this.nativeIndex)
|
|
344
392
|
return;
|
|
345
|
-
|
|
346
|
-
for (const id of this.vectorStore.keys()) {
|
|
347
|
-
try {
|
|
348
|
-
this.nativeDb.delete(String(id));
|
|
349
|
-
}
|
|
350
|
-
catch { /* ignore */ }
|
|
351
|
-
}
|
|
352
|
-
this.vectorStore.clear();
|
|
353
|
-
this.normStore.clear();
|
|
393
|
+
this.liveIds.clear();
|
|
354
394
|
this.metadataStore.clear();
|
|
395
|
+
// Reset capacity to the initial value so a fresh index doesn't carry
|
|
396
|
+
// over the previous index's resize history.
|
|
397
|
+
this.currentMaxElements = INITIAL_MAX_ELEMENTS;
|
|
398
|
+
this.nativeIndex = this.createFreshIndex();
|
|
355
399
|
}
|
|
356
400
|
/**
|
|
357
|
-
* Dispose of the native
|
|
358
|
-
*
|
|
359
|
-
*
|
|
360
|
-
*
|
|
401
|
+
* Dispose of the native index handle.
|
|
402
|
+
*
|
|
403
|
+
* hnswlib-node holds the C++ index in JS-managed memory; setting the
|
|
404
|
+
* reference to null lets V8 GC reclaim it on the next collection cycle.
|
|
405
|
+
* Unlike the previous @ruvector/router VectorDb, there is no file lock
|
|
406
|
+
* to release and no synchronous teardown required.
|
|
361
407
|
*
|
|
362
|
-
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
408
|
+
* After dispose(), this backend instance must not be used. The
|
|
409
|
+
* HnswAdapter registry is expected to drop its reference as part of
|
|
410
|
+
* `HnswAdapter.close(name)`.
|
|
365
411
|
*/
|
|
366
412
|
dispose() {
|
|
367
|
-
|
|
368
|
-
this.vectorStore.clear();
|
|
369
|
-
this.normStore.clear();
|
|
413
|
+
this.liveIds.clear();
|
|
370
414
|
this.metadataStore.clear();
|
|
371
|
-
|
|
372
|
-
this.nativeDb = null;
|
|
415
|
+
this.nativeIndex = null;
|
|
373
416
|
}
|
|
374
417
|
/**
|
|
375
418
|
* Check if the native backend is operational.
|
|
376
419
|
*/
|
|
377
420
|
isNativeAvailable() {
|
|
378
|
-
return this.
|
|
421
|
+
return this.nativeIndex !== null;
|
|
379
422
|
}
|
|
380
423
|
// ============================================================================
|
|
381
424
|
// Private Helpers
|
|
@@ -396,33 +439,5 @@ export class NativeHnswBackend {
|
|
|
396
439
|
this._metrics.avgSearchLatencyMs =
|
|
397
440
|
this._metrics.avgSearchLatencyMs * ((n - 1) / n) + latencyMs / n;
|
|
398
441
|
}
|
|
399
|
-
/**
|
|
400
|
-
* Brute-force fallback search over locally stored vectors.
|
|
401
|
-
* Used when native search throws an unexpected error.
|
|
402
|
-
*/
|
|
403
|
-
bruteForceSearch(query, queryNorm, k) {
|
|
404
|
-
const scored = [];
|
|
405
|
-
for (const [id, vector] of this.vectorStore) {
|
|
406
|
-
const norm = this.normStore.get(id) ?? computeNorm(vector);
|
|
407
|
-
const score = this.config.metric === 'cosine'
|
|
408
|
-
? fastCosineSimilarity(query, vector, queryNorm, norm)
|
|
409
|
-
: -(function () {
|
|
410
|
-
let sum = 0;
|
|
411
|
-
const len = Math.min(query.length, vector.length);
|
|
412
|
-
for (let i = 0; i < len; i++) {
|
|
413
|
-
const diff = query[i] - vector[i];
|
|
414
|
-
sum += diff * diff;
|
|
415
|
-
}
|
|
416
|
-
return Math.sqrt(sum);
|
|
417
|
-
})();
|
|
418
|
-
scored.push({
|
|
419
|
-
id,
|
|
420
|
-
score,
|
|
421
|
-
metadata: this.metadataStore.get(id),
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
scored.sort((a, b) => b.score - a.score);
|
|
425
|
-
return scored.slice(0, k);
|
|
426
|
-
}
|
|
427
442
|
}
|
|
428
443
|
//# sourceMappingURL=native-hnsw-backend.js.map
|
|
@@ -807,16 +807,21 @@ export class UnifiedMemoryManager {
|
|
|
807
807
|
close() {
|
|
808
808
|
if (this.db) {
|
|
809
809
|
this.preparedStatements.clear();
|
|
810
|
-
//
|
|
811
|
-
//
|
|
812
|
-
//
|
|
813
|
-
//
|
|
814
|
-
//
|
|
815
|
-
//
|
|
816
|
-
//
|
|
817
|
-
//
|
|
818
|
-
//
|
|
819
|
-
//
|
|
810
|
+
// vectorIndex.clear() recreates the underlying HierarchicalNSW
|
|
811
|
+
// (post #399 / ADR-090 — see NativeHnswBackend.clear()), so the
|
|
812
|
+
// next UnifiedMemoryManager instance starts with a fresh graph.
|
|
813
|
+
// The HnswAdapter registry singleton is intentionally preserved
|
|
814
|
+
// across close()/re-init cycles so that consumers that hold a
|
|
815
|
+
// reference to the named adapter (e.g. PatternStore) continue to
|
|
816
|
+
// see live state. Tests and explicit shutdown paths can fully
|
|
817
|
+
// tear down via HnswAdapter.close(name).
|
|
818
|
+
//
|
|
819
|
+
// Historical context: prior to #399, NativeHnswBackend wrapped
|
|
820
|
+
// @ruvector/router's VectorDb which held a process-wide redb file
|
|
821
|
+
// lock — only ONE instance could exist per process and the lock
|
|
822
|
+
// was not released by NAPI dispose ("Database already open").
|
|
823
|
+
// hnswlib-node has none of those constraints; multiple instances
|
|
824
|
+
// coexist freely.
|
|
820
825
|
this.vectorIndex.clear();
|
|
821
826
|
this.db.close();
|
|
822
827
|
this.db = null;
|