agentic-qe 3.9.36 → 3.10.1
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 +147 -0
- package/README.md +35 -8
- package/assets/skills/skills-manifest.json +1 -1
- package/dist/cli/bundle.js +5 -5
- package/dist/cli/chunks/adapter-3JS2CN7C.js +2 -0
- package/dist/cli/chunks/{agent-booster-wasm-L6D36M77.js → agent-booster-wasm-FZLWOPSX.js} +2 -2
- package/dist/cli/chunks/{agent-handler-3JNKP4OI.js → agent-handler-KAGRVCKS.js} +2 -2
- package/dist/cli/chunks/{agent-memory-branch-QBRBX7O4.js → agent-memory-branch-JJKFEXQW.js} +2 -2
- package/dist/cli/chunks/aqe-learning-engine-Q4GFNGU6.js +2 -0
- package/dist/cli/chunks/{audit-M4W43TXQ.js → audit-DN5SY4JJ.js} +2 -2
- package/dist/cli/chunks/base-Q3MAL22Y.js +2 -0
- package/dist/cli/chunks/{hnswlib-node-NN6GB2YD.js → better-sqlite3-XGPW4HK5.js} +2 -2
- package/dist/cli/chunks/{brain-handler-WQN7C55Z.js → brain-handler-O7MP5BGY.js} +3 -3
- package/dist/cli/chunks/{branch-enumerator-BOTGLI7M.js → branch-enumerator-ZPTN5J6V.js} +2 -2
- package/dist/cli/chunks/{browser-ZFZCR7LP.js → browser-PAZE2JSD.js} +2 -2
- package/dist/cli/chunks/browser-workflow-JX3M2UWG.js +2 -0
- package/dist/cli/chunks/{chunk-DTJHO3WI.js → chunk-25QGPRWN.js} +2 -2
- package/dist/cli/chunks/{chunk-IF4ZZ2HD.js → chunk-2ETDES5W.js} +2 -2
- package/dist/cli/chunks/{chunk-556GYKN5.js → chunk-2KTRRSKD.js} +2 -2
- package/dist/cli/chunks/{chunk-TOZ6WUAB.js → chunk-2S4XHK26.js} +2 -2
- package/dist/cli/chunks/{chunk-O4C3JEOR.js → chunk-2TDWCXZ6.js} +2 -2
- package/dist/cli/chunks/{chunk-YUFUGGXY.js → chunk-2UN4DOBJ.js} +2 -2
- package/dist/cli/chunks/{chunk-CLATNP57.js → chunk-32R7EA7B.js} +3 -3
- package/dist/cli/chunks/{chunk-NYU4YD5V.js → chunk-3YOPJ7DY.js} +2 -2
- package/dist/cli/chunks/{chunk-SCBWRKPE.js → chunk-4AWJ5PE4.js} +1 -1
- package/dist/cli/chunks/{chunk-TCDGJYM2.js → chunk-57NJQV57.js} +1 -1
- package/dist/cli/chunks/{chunk-ZK6GXL5J.js → chunk-5EU2VQK3.js} +2 -2
- package/dist/cli/chunks/{chunk-6M2POFDF.js → chunk-5KFCH7RH.js} +1 -1
- package/dist/cli/chunks/{chunk-CSDGFQKW.js → chunk-5OTYBWRL.js} +2 -2
- package/dist/cli/chunks/{chunk-I7BXATQ6.js → chunk-5ZCVMR5E.js} +2 -2
- package/dist/cli/chunks/{chunk-MEQ4OPSL.js → chunk-622MIAOR.js} +3 -3
- package/dist/cli/chunks/{chunk-YKZ3ZJWT.js → chunk-6A2NIR2E.js} +2 -2
- package/dist/cli/chunks/{chunk-BI54GWTK.js → chunk-6D57DWQ6.js} +2 -2
- package/dist/cli/chunks/{chunk-CK5TGFII.js → chunk-6E7GI2UB.js} +1 -1
- package/dist/cli/chunks/{chunk-6BHUKCLI.js → chunk-6LRCMFLV.js} +2 -2
- package/dist/cli/chunks/{chunk-2AANIKDG.js → chunk-6QFONVEE.js} +2 -2
- package/dist/cli/chunks/{chunk-OO2JHVMS.js → chunk-6VQ5MFJ6.js} +3 -3
- package/dist/cli/chunks/{chunk-Z5A2ENBC.js → chunk-6ZRMQXVL.js} +2 -2
- package/dist/cli/chunks/{chunk-QYUWWTE3.js → chunk-7CKVAYP3.js} +1 -1
- package/dist/cli/chunks/{chunk-TG44VKO3.js → chunk-7MSYTHZM.js} +2 -2
- package/dist/cli/chunks/{chunk-EAMKH35X.js → chunk-7SQD2TTQ.js} +1 -1
- package/dist/cli/chunks/{chunk-GLI5EN6O.js → chunk-AABKTWXC.js} +2 -2
- package/dist/cli/chunks/{chunk-AJ2VFSLX.js → chunk-AUVC3DDB.js} +2 -2
- package/dist/cli/chunks/{chunk-P4VQTIJE.js → chunk-BECU52UY.js} +3 -3
- package/dist/cli/chunks/chunk-BF7MUYWC.js +2 -0
- package/dist/cli/chunks/{chunk-VENEJ3UI.js → chunk-BGBSYF3K.js} +2 -2
- package/dist/cli/chunks/{chunk-W2DF6FNE.js → chunk-BN7ERYXI.js} +1 -1
- package/dist/cli/chunks/{chunk-LPVRFVSC.js → chunk-CKECJH2A.js} +2 -2
- package/dist/cli/chunks/{chunk-FK4B2HIW.js → chunk-COZDJLIL.js} +1 -1
- package/dist/cli/chunks/{chunk-2EWOYVH3.js → chunk-CR4ERDS2.js} +2 -2
- package/dist/cli/chunks/chunk-DMIYWPQQ.js +2 -0
- package/dist/cli/chunks/{chunk-QXM3PRVR.js → chunk-DQIS6J77.js} +2 -2
- package/dist/cli/chunks/{chunk-P7IOH7L4.js → chunk-DVOH75S4.js} +1 -1
- package/dist/cli/chunks/{chunk-DKBBMZWB.js → chunk-EBQ6YKP2.js} +2 -2
- package/dist/cli/chunks/{chunk-SJ3SHZUC.js → chunk-ECPB7IAH.js} +4 -4
- package/dist/cli/chunks/{chunk-QH7K2UPP.js → chunk-EIROAH6N.js} +2 -2
- package/dist/cli/chunks/{chunk-AH3TKDVW.js → chunk-ELUERFGA.js} +2 -2
- package/dist/cli/chunks/{chunk-ZBY4HSHR.js → chunk-ELZ67OHQ.js} +2 -2
- package/dist/cli/chunks/{chunk-HHGZ4LXC.js → chunk-EO4B5GS4.js} +1 -1
- package/dist/cli/chunks/{chunk-5ZTY5JJX.js → chunk-EUJHHXE6.js} +2 -2
- package/dist/cli/chunks/{chunk-MFPVS2KP.js → chunk-F3OVFA6W.js} +9 -9
- package/dist/cli/chunks/{chunk-RDXIRE2I.js → chunk-FJSR2U65.js} +2 -2
- package/dist/cli/chunks/{chunk-TU2PSEDW.js → chunk-G3BQU3Q6.js} +2 -2
- package/dist/cli/chunks/{chunk-BNFW4LMD.js → chunk-G5DTENO3.js} +2 -2
- package/dist/cli/chunks/{chunk-IMP43AXO.js → chunk-GMKGLZJU.js} +2 -2
- package/dist/cli/chunks/{chunk-Y4ERDIPP.js → chunk-GRG3OP34.js} +2 -2
- package/dist/cli/chunks/{chunk-PYCYZYIL.js → chunk-GRYXNWPF.js} +4 -4
- package/dist/cli/chunks/{chunk-GW7HWFWQ.js → chunk-GTGONWGX.js} +1 -1
- package/dist/cli/chunks/{chunk-AH3UC4IV.js → chunk-GTRXZJNX.js} +2 -2
- package/dist/cli/chunks/{chunk-QAGAEAKM.js → chunk-GXJ4BCGC.js} +2 -2
- package/dist/cli/chunks/{chunk-EXBJCDFM.js → chunk-H22MMMYY.js} +2 -2
- package/dist/cli/chunks/{chunk-FZA674BT.js → chunk-H44WD7QX.js} +2 -2
- package/dist/cli/chunks/{chunk-NKBCAW27.js → chunk-HPNEZ7YN.js} +1 -1
- package/dist/cli/chunks/{chunk-EXUT2LCP.js → chunk-HRDGN3OK.js} +1 -1
- package/dist/cli/chunks/{chunk-O5JNHH2D.js → chunk-IHDUWPFY.js} +2 -2
- package/dist/cli/chunks/{chunk-GFMSAL5Y.js → chunk-IITKJCPK.js} +2 -2
- package/dist/cli/chunks/{chunk-AY2LLGZN.js → chunk-IJ4BUSJN.js} +4 -4
- package/dist/cli/chunks/{chunk-NKA3X6VT.js → chunk-IK6AJX3C.js} +1 -1
- package/dist/cli/chunks/{chunk-D5CLABLM.js → chunk-IPRDHKSI.js} +2 -2
- package/dist/cli/chunks/{chunk-BGD65PL4.js → chunk-JLKHPIQF.js} +1 -1
- package/dist/cli/chunks/chunk-K22TPWOJ.js +3 -0
- package/dist/cli/chunks/{chunk-6DACR7QY.js → chunk-K4T3RDCB.js} +2 -2
- package/dist/cli/chunks/{chunk-LUFY3GGI.js → chunk-KCTH5MHE.js} +2 -2
- package/dist/cli/chunks/{chunk-7ZCDG3IM.js → chunk-KEC5FBAW.js} +1 -1
- package/dist/cli/chunks/{chunk-PY4B37U3.js → chunk-KKNBYXYA.js} +1 -1
- package/dist/cli/chunks/{chunk-X6UGFKHW.js → chunk-KRXKU54J.js} +2 -2
- package/dist/cli/chunks/{provider-manager-VPS4W7KG.js → chunk-KZJXVIAW.js} +13 -13
- package/dist/cli/chunks/{chunk-C2JK5O55.js → chunk-LF7URN2X.js} +2 -2
- package/dist/cli/chunks/{chunk-CAGRQR2U.js → chunk-MQYTW2IN.js} +2 -2
- package/dist/cli/chunks/{chunk-KI6RXKP4.js → chunk-MVGVD6LS.js} +2 -2
- package/dist/cli/chunks/{chunk-U6QAKKNB.js → chunk-NDCPEXDN.js} +2 -2
- package/dist/cli/chunks/{chunk-PUZAIRMY.js → chunk-NOCYYXK4.js} +2 -2
- package/dist/cli/chunks/{chunk-L3JJ52N6.js → chunk-NPSBMDVU.js} +2 -2
- package/dist/cli/chunks/{chunk-U7B4WCBY.js → chunk-NQZYUH6S.js} +1 -1
- package/dist/cli/chunks/{chunk-4R3WXD4U.js → chunk-NRLT44YB.js} +1 -1
- package/dist/cli/chunks/{chunk-FSDUNGYM.js → chunk-OC3OJWLB.js} +2 -2
- package/dist/cli/chunks/{chunk-5ECOD4O6.js → chunk-OQP5OFRR.js} +1 -1
- package/dist/cli/chunks/{chunk-7TO7NTLT.js → chunk-OVENSL64.js} +210 -210
- package/dist/cli/chunks/{chunk-5SYH5QMQ.js → chunk-P7T5Y735.js} +2 -2
- package/dist/cli/chunks/{chunk-RCU5L7FF.js → chunk-PLRSKAFZ.js} +2 -2
- package/dist/cli/chunks/{chunk-74DA4P5K.js → chunk-PUXDXIUE.js} +1 -1
- package/dist/cli/chunks/{chunk-A3TOJ4KN.js → chunk-PW6MOFXG.js} +2 -2
- package/dist/cli/chunks/chunk-PXFQSVA2.js +12 -0
- package/dist/cli/chunks/{chunk-5MSZREOD.js → chunk-Q24OJX44.js} +2 -2
- package/dist/cli/chunks/{chunk-XNXFEMWT.js → chunk-Q5VHBI4U.js} +1 -1
- package/dist/cli/chunks/{chunk-HOZNVDFF.js → chunk-QNW335PD.js} +2 -2
- package/dist/cli/chunks/{chunk-ESIE3MT5.js → chunk-RPL6K623.js} +3 -3
- package/dist/cli/chunks/{chunk-JFDNNXIX.js → chunk-RRLQFFCO.js} +4 -4
- package/dist/cli/chunks/{chunk-G6Q66YLG.js → chunk-RWYW573C.js} +3 -3
- package/dist/cli/chunks/{chunk-CJQT3MYN.js → chunk-SLNGJW4G.js} +1 -1
- package/dist/cli/chunks/{chunk-PGRDHDEJ.js → chunk-SQBB2DC6.js} +1 -1
- package/dist/cli/chunks/{chunk-GWLLNJ64.js → chunk-T3UXMPXX.js} +1 -1
- package/dist/cli/chunks/{chunk-MJJAHA2T.js → chunk-TFNLXAK5.js} +1 -1
- package/dist/cli/chunks/{chunk-YE42UTLT.js → chunk-TL5F2S3Z.js} +2 -2
- package/dist/cli/chunks/{chunk-JA7YARLP.js → chunk-TNGGVMPQ.js} +1 -1
- package/dist/cli/chunks/{chunk-YMB4YVFX.js → chunk-TNVYKYTO.js} +3 -3
- package/dist/cli/chunks/{chunk-FYJE4QLL.js → chunk-TP77PNN6.js} +2 -2
- package/dist/cli/chunks/{chunk-2KMCRASK.js → chunk-TX7SNQKL.js} +1 -1
- package/dist/cli/chunks/{chunk-TK7TGVVQ.js → chunk-UDFLR7GR.js} +1 -1
- package/dist/cli/chunks/{chunk-G7YDYXQH.js → chunk-UDVX34ZG.js} +2 -2
- package/dist/cli/chunks/{chunk-5RPHV7Y3.js → chunk-UU2Q6XWF.js} +1 -1
- package/dist/cli/chunks/{chunk-27FNROZ2.js → chunk-VKRMVTWU.js} +49 -49
- package/dist/cli/chunks/{chunk-AWC7G2D3.js → chunk-VOIWDPIU.js} +2 -2
- package/dist/cli/chunks/chunk-VPCE5CIT.js +2 -0
- package/dist/cli/chunks/{chunk-6M44C43I.js → chunk-W2DT3CDE.js} +2 -2
- package/dist/cli/chunks/{chunk-GAC4MD6S.js → chunk-W6ZF2CPN.js} +1 -1
- package/dist/cli/chunks/{chunk-5OAAJUGO.js → chunk-WDTCCPK4.js} +1 -1
- package/dist/cli/chunks/{chunk-NMHULQAV.js → chunk-WL2J6ECN.js} +2 -2
- package/dist/cli/chunks/chunk-WMG5F6R6.js +2 -0
- package/dist/cli/chunks/{chunk-MAHLTWGV.js → chunk-WO7KYT3X.js} +2 -2
- package/dist/cli/chunks/{chunk-5M63H6RY.js → chunk-WPNZSL4S.js} +1 -1
- package/dist/cli/chunks/{chunk-X6IEQVSF.js → chunk-WQEZKAUR.js} +2 -2
- package/dist/cli/chunks/{chunk-BUVY7IGM.js → chunk-WS4XVJHI.js} +1 -1
- package/dist/cli/chunks/{chunk-66636UWH.js → chunk-XGUQYVJR.js} +2 -2
- package/dist/cli/chunks/{chunk-3CIQ6CQ4.js → chunk-XKOKMS5A.js} +2 -2
- package/dist/cli/chunks/{chunk-TXSVZYFA.js → chunk-XRK7FBTY.js} +2 -2
- package/dist/cli/chunks/{chunk-QMUTKI34.js → chunk-Y2DPXMOR.js} +1 -1
- package/dist/cli/chunks/{chunk-F57OUXHJ.js → chunk-Y357YFLF.js} +2 -2
- package/dist/cli/chunks/{chunk-EDG4NEET.js → chunk-YGRTMAWB.js} +1 -1
- package/dist/cli/chunks/{chunk-LIE24NRL.js → chunk-YPOTBXPU.js} +2 -2
- package/dist/cli/chunks/{chunk-F5IVWV2P.js → chunk-YYWIA5FX.js} +1 -1
- package/dist/cli/chunks/{chunk-OLBYILKU.js → chunk-ZEMXMDD3.js} +2 -2
- package/dist/cli/chunks/{chunk-DCXFHBHS.js → chunk-ZTMWJQTZ.js} +2 -2
- package/dist/cli/chunks/{ci-YA6SQ3PU.js → ci-EHRUN7O6.js} +2 -2
- package/dist/cli/chunks/{ci-output-KKHXCQI4.js → ci-output-EXDXVXKB.js} +2 -2
- package/dist/cli/chunks/{circuit-breaker-RTBCXGKA.js → circuit-breaker-LM4QRAAL.js} +2 -2
- package/dist/cli/chunks/{claude-flow-setup-CRV3Y6LO.js → claude-flow-setup-ZHUCZVW7.js} +2 -2
- package/dist/cli/chunks/client-PA3UJIHH.js +2 -0
- package/dist/cli/chunks/{cline-installer-IS2VLXQS.js → cline-installer-WMFLQOW2.js} +2 -2
- package/dist/cli/chunks/{code-3CSNTK5O.js → code-L7KRLU5E.js} +2 -2
- package/dist/cli/chunks/{code-index-extractor-P2YXEJGV.js → code-index-extractor-FTMUYQC6.js} +2 -2
- package/dist/cli/chunks/{codex-installer-QGDV2CAQ.js → codex-installer-B3F6WI3Z.js} +2 -2
- package/dist/cli/chunks/{completions-TYECEYHY.js → completions-3JZQRNB6.js} +2 -2
- package/dist/cli/chunks/{complexity-analyzer-GA5KSFF2.js → complexity-analyzer-LB2FFEKX.js} +2 -2
- package/dist/cli/chunks/{continuedev-installer-NW5Y4ZMI.js → continuedev-installer-WH6IMV7R.js} +2 -2
- package/dist/cli/chunks/{copilot-installer-E5BIBYG6.js → copilot-installer-NOIEOFJM.js} +2 -2
- package/dist/cli/chunks/{cost-tracker-JLSEY4ID.js → cost-tracker-IKX2VYSA.js} +2 -2
- package/dist/cli/chunks/{coverage-4TH275AN.js → coverage-TD37CYSY.js} +3 -3
- package/dist/cli/chunks/cross-domain-router-BVJRP2ZX.js +2 -0
- package/dist/cli/chunks/{cursor-installer-4SXSE5YT.js → cursor-installer-4NSVGRM5.js} +2 -2
- package/dist/cli/chunks/{daemon-7T4RZQH6.js → daemon-LYDV7NRW.js} +3 -3
- package/dist/cli/chunks/{daemon-U6MP4P6S.js → daemon-UWEBUIDT.js} +4 -4
- package/dist/cli/chunks/{dag-attention-scheduler-DT2V46XR.js → dag-attention-scheduler-NECJGCHC.js} +2 -2
- package/dist/cli/chunks/{detect-WHSTB4QT.js → detect-4XGC7ILO.js} +2 -2
- package/dist/cli/chunks/{dist-node-4LC2RJXW.js → dist-node-GGJDXRKJ.js} +2 -2
- package/dist/cli/chunks/{domain-handler-A2LOOU7Y.js → domain-handler-UOFONAUT.js} +2 -2
- package/dist/cli/chunks/{domain-transfer-4RJTMB3Y.js → domain-transfer-6JLNOYPA.js} +2 -2
- package/dist/cli/chunks/dream-BXZUEIW2.js +2 -0
- package/dist/cli/chunks/{embed-and-insert-pattern-PR3KTT24.js → embed-and-insert-pattern-ZM75DQ4J.js} +2 -2
- package/dist/cli/chunks/{eval-2HRBMOOB.js → eval-CYZJTHEB.js} +2 -2
- package/dist/cli/chunks/{experience-capture-middleware-X46IYOSD.js → experience-capture-middleware-RBOJLDUB.js} +3 -3
- package/dist/cli/chunks/{fast-paths-LYYYU362.js → fast-paths-S5BWZR3L.js} +2 -2
- package/dist/cli/chunks/{feature-flags-RNAMQPUN.js → feature-flags-6E7H3NYP.js} +2 -2
- package/dist/cli/chunks/{feature-flags-TKDXIHOH.js → feature-flags-DUNQPDU3.js} +2 -2
- package/dist/cli/chunks/{file-discovery-MKGGMDIW.js → file-discovery-PFFKDGYG.js} +2 -2
- package/dist/cli/chunks/{fleet-OY4IBCZG.js → fleet-RCDZZFXN.js} +3 -3
- package/dist/cli/chunks/{gnn-wrapper-G5OGHVAQ.js → gnn-wrapper-5AG3WDWF.js} +2 -2
- package/dist/cli/chunks/{heartbeat-handler-N7IUATSD.js → heartbeat-handler-O2KAEX4Y.js} +4 -4
- package/dist/cli/chunks/heartbeat-scheduler-LEKAWFJF.js +2 -0
- package/dist/cli/chunks/hnsw-adapter-2TCEG5M2.js +2 -0
- package/dist/cli/chunks/hnsw-index-VDPUTEES.js +2 -0
- package/dist/cli/chunks/{hnsw-legacy-bridge-TMCMZDVF.js → hnsw-legacy-bridge-C7FG6YGW.js} +2 -2
- package/dist/cli/chunks/{better-sqlite3-SPABZYYQ.js → hnswlib-node-YX6OOBN6.js} +2 -2
- package/dist/cli/chunks/{hooks-5QDXEZ25.js → hooks-FUHNE2P7.js} +10 -10
- package/dist/cli/chunks/hybrid-router-2EMDIYIG.js +2 -0
- package/dist/cli/chunks/{hypergraph-engine-QQYKS2F3.js → hypergraph-engine-LQRYBNPV.js} +2 -2
- package/dist/cli/chunks/{hypergraph-handler-YXFNXMRF.js → hypergraph-handler-AFFNLZVD.js} +3 -3
- package/dist/cli/chunks/impact-analyzer-HFD6CPWC.js +2 -0
- package/dist/cli/chunks/{init-handler-SFFSDGEF.js → init-handler-BQ6IDBX4.js} +6 -6
- package/dist/cli/chunks/init-wizard-552QIVRC.js +2 -0
- package/dist/cli/chunks/kernel-5DN6O6EE.js +2 -0
- package/dist/cli/chunks/{kilocode-installer-QFRYXQYY.js → kilocode-installer-KS72HUSG.js} +2 -2
- package/dist/cli/chunks/{kiro-installer-7L2EYEOR.js → kiro-installer-2ZEFLGRX.js} +2 -2
- package/dist/cli/chunks/knowledge-graph-76Y77MB6.js +2 -0
- package/dist/cli/chunks/{learning-7TVVL5UH.js → learning-GGHGJ6B6.js} +3 -3
- package/dist/cli/chunks/llm-router-LP6K5BJF.js +36 -0
- package/dist/cli/chunks/llm-router-service-4O6GCEFH.js +2 -0
- package/dist/cli/chunks/{load-Y46RO7BF.js → load-2MLKXOT2.js} +2 -2
- package/dist/cli/chunks/load-test-FCI2IWRJ.js +2 -0
- package/dist/cli/chunks/{mcp-PYNP2S5O.js → mcp-CDJBJG5H.js} +2 -2
- package/dist/cli/chunks/{memory-DAVXW5OP.js → memory-4LAX7JZS.js} +5 -5
- package/dist/cli/chunks/memory-backend-MKNCBNDE.js +2 -0
- package/dist/cli/chunks/memory-handlers-OBAFR4WV.js +2 -0
- package/dist/cli/chunks/{multi-model-executor-7ECJRCM3.js → multi-model-executor-SLJV73HE.js} +2 -2
- package/dist/cli/chunks/{opencode-installer-H7QOONUC.js → opencode-installer-M76SONWV.js} +2 -2
- package/dist/cli/chunks/{orchestrator-TSJ2F3MG.js → orchestrator-TJPUDJMP.js} +5 -5
- package/dist/cli/chunks/{pipeline-SXR4MRY7.js → pipeline-2PVNFT6J.js} +2 -2
- package/dist/cli/chunks/{platform-7NOS56VD.js → platform-KCSEDKEE.js} +2 -2
- package/dist/cli/chunks/{plugin-VLSWBGJN.js → plugin-QX47QF5U.js} +2 -2
- package/dist/cli/chunks/{prime-radiant-advanced-wasm-7ZHSOP7H.js → prime-radiant-advanced-wasm-L6VWL7VR.js} +2 -2
- package/dist/cli/chunks/protocol-executor-IA7WNT23.js +2 -0
- package/dist/cli/chunks/{protocol-handler-LWNYDM4D.js → protocol-handler-WDNJDEUE.js} +2 -2
- package/dist/cli/chunks/{prove-JYFW3OBB.js → prove-3B75DA3S.js} +2 -2
- package/dist/cli/chunks/provider-manager-3K5KB5A6.js +2 -0
- package/dist/cli/chunks/qe-reasoning-bank-V5Z3BBYY.js +2 -0
- package/dist/cli/chunks/{quality-FCAOFXZ2.js → quality-UGASS5WM.js} +2 -2
- package/dist/cli/chunks/queen-coordinator-QOMPA27Z.js +2 -0
- package/dist/cli/chunks/real-embeddings-COBP2LHS.js +2 -0
- package/dist/cli/chunks/{roocode-installer-ZVGJSAWD.js → roocode-installer-TOZ4VLFT.js} +2 -2
- package/dist/cli/chunks/router-P5RZUPC4.js +2 -0
- package/dist/cli/chunks/routing-feedback-3PS3OZQC.js +2 -0
- package/dist/cli/chunks/{routing-handler-AGDHK3A2.js → routing-handler-5TDVSILX.js} +2 -2
- package/dist/cli/chunks/{ruvector-commands-UGZINDCN.js → ruvector-commands-AM63KWQN.js} +2 -2
- package/dist/cli/chunks/{rvf-dual-writer-PW7H6G4V.js → rvf-dual-writer-UANIFE2M.js} +2 -2
- package/dist/cli/chunks/{rvf-migration-adapter-ZKOTVCHJ.js → rvf-migration-adapter-LNPYWAPI.js} +2 -2
- package/dist/cli/chunks/{rvf-migration-coordinator-SCBEMOP3.js → rvf-migration-coordinator-U47L63DQ.js} +2 -2
- package/dist/cli/chunks/rvf-native-adapter-TT7OJPKM.js +2 -0
- package/dist/cli/chunks/safe-db-PEW7VBAE.js +2 -0
- package/dist/cli/chunks/schedule-QHOTHBQE.js +2 -0
- package/dist/cli/chunks/scheduler-KXJBYTRT.js +2 -0
- package/dist/cli/chunks/{security-H7QX46VA.js → security-KDQ2AH7G.js} +3 -3
- package/dist/cli/chunks/{shared-rvf-adapter-LWYHWJIA.js → shared-rvf-adapter-5MAGLLYJ.js} +2 -2
- package/dist/cli/chunks/{shared-rvf-dual-writer-P2U424FB.js → shared-rvf-dual-writer-GF2OPPM5.js} +2 -2
- package/dist/cli/chunks/sqlite-persistence-TPXJK34J.js +2 -0
- package/dist/cli/chunks/{status-handler-UNX3RA3S.js → status-handler-XZKEL7LO.js} +2 -2
- package/dist/cli/chunks/{structural-health-65YV3M5E.js → structural-health-27QKWW25.js} +2 -2
- package/dist/cli/chunks/{sync-OZ7CHYYO.js → sync-INNKS6UK.js} +2 -2
- package/dist/cli/chunks/{sync-B4O3NDJ6.js → sync-V3HGPEJT.js} +2 -2
- package/dist/cli/chunks/{task-handler-NIG3USPG.js → task-handler-LDUVOM6G.js} +2 -2
- package/dist/cli/chunks/{task-handlers-Z2PI7BTO.js → task-handlers-HTCPV7OO.js} +3 -3
- package/dist/cli/chunks/{test-MNLYS3WS.js → test-PCUVGVJL.js} +4 -4
- package/dist/cli/chunks/{test-scheduling-VCL6D6T7.js → test-scheduling-GYVXWCAA.js} +3 -3
- package/dist/cli/chunks/{token-bootstrap-DGMEU3W4.js → token-bootstrap-MJ2ANC4P.js} +2 -2
- package/dist/cli/chunks/{token-usage-IXTYAV5K.js → token-usage-ZREHROTF.js} +2 -2
- package/dist/cli/chunks/{transformers-5CWLXT2M.js → transformers-6B3FWFYL.js} +2 -2
- package/dist/cli/chunks/{tree-sitter-wasm-parser-W3FKJLJG.js → tree-sitter-wasm-parser-JVV234MN.js} +2 -2
- package/dist/cli/chunks/{types-NVOCAZCQ.js → types-MVZTJI2F.js} +2 -2
- package/dist/cli/chunks/unified-memory-ROS2NKR5.js +2 -0
- package/dist/cli/chunks/unified-memory-hnsw-FLEUF3XO.js +2 -0
- package/dist/cli/chunks/unified-persistence-QC5L7UNQ.js +2 -0
- package/dist/cli/chunks/{upgrade-VJYD4U4X.js → upgrade-MKTFEILD.js} +2 -2
- package/dist/cli/chunks/{validate-CAKTCZ3T.js → validate-IABGALSW.js} +2 -2
- package/dist/cli/chunks/{validate-swarm-H24JHUTN.js → validate-swarm-RHF53RF6.js} +2 -2
- package/dist/cli/chunks/{vibium-247XN6V3.js → vibium-RAKW6FMF.js} +2 -2
- package/dist/cli/chunks/visual-security-NLIOUQCR.js +2 -0
- package/dist/cli/chunks/{web-tree-sitter-3DOMHLRH.js → web-tree-sitter-PYK7F4JZ.js} +2 -2
- package/dist/cli/chunks/{windsurf-installer-S2MOA5TQ.js → windsurf-installer-S3U2HWZ4.js} +2 -2
- package/dist/cli/chunks/{witness-chain-CKLVPFLG.js → witness-chain-435NKQLB.js} +2 -2
- package/dist/cli/chunks/witness-chain-U7X6JX5J.js +2 -0
- package/dist/cli/chunks/{workflow-GU3IV2OD.js → workflow-5UHJCZ6J.js} +4 -4
- package/dist/cli/chunks/workflow-orchestrator-APE6BAXH.js +2 -0
- package/dist/cli/chunks/{wrappers-74Y4EKW3.js → wrappers-RD3NCMLK.js} +2 -2
- package/dist/cli/commands/llm-router.js +83 -26
- package/dist/domains/chaos-resilience/coordinator.d.ts +2 -1
- package/dist/domains/chaos-resilience/coordinator.js +3 -2
- package/dist/domains/chaos-resilience/plugin.d.ts +4 -2
- package/dist/domains/chaos-resilience/plugin.js +8 -5
- package/dist/domains/code-intelligence/coordinator.d.ts +2 -1
- package/dist/domains/code-intelligence/coordinator.js +3 -2
- package/dist/domains/code-intelligence/plugin.d.ts +4 -2
- package/dist/domains/code-intelligence/plugin.js +8 -5
- package/dist/domains/contract-testing/coordinator.d.ts +2 -1
- package/dist/domains/contract-testing/coordinator.js +3 -2
- package/dist/domains/contract-testing/plugin.d.ts +4 -2
- package/dist/domains/contract-testing/plugin.js +8 -5
- package/dist/domains/coverage-analysis/coordinator.d.ts +2 -1
- package/dist/domains/coverage-analysis/coordinator.js +4 -3
- package/dist/domains/coverage-analysis/plugin.d.ts +3 -2
- package/dist/domains/coverage-analysis/plugin.js +4 -4
- package/dist/domains/defect-intelligence/coordinator.d.ts +2 -1
- package/dist/domains/defect-intelligence/coordinator.js +4 -3
- package/dist/domains/defect-intelligence/plugin.d.ts +4 -2
- package/dist/domains/defect-intelligence/plugin.js +9 -6
- package/dist/domains/learning-optimization/coordinator.d.ts +2 -1
- package/dist/domains/learning-optimization/coordinator.js +3 -2
- package/dist/domains/learning-optimization/plugin.d.ts +4 -2
- package/dist/domains/learning-optimization/plugin.js +8 -5
- package/dist/domains/quality-assessment/coordinator.d.ts +2 -1
- package/dist/domains/quality-assessment/coordinator.js +4 -3
- package/dist/domains/quality-assessment/plugin.d.ts +4 -2
- package/dist/domains/quality-assessment/plugin.js +10 -6
- package/dist/domains/requirements-validation/coordinator.d.ts +2 -1
- package/dist/domains/requirements-validation/coordinator.js +3 -2
- package/dist/domains/requirements-validation/plugin.d.ts +4 -2
- package/dist/domains/requirements-validation/plugin.js +8 -5
- package/dist/domains/security-compliance/coordinator.d.ts +2 -1
- package/dist/domains/security-compliance/coordinator.js +4 -2
- package/dist/domains/security-compliance/plugin.d.ts +4 -2
- package/dist/domains/security-compliance/plugin.js +11 -6
- package/dist/domains/test-execution/coordinator.d.ts +3 -2
- package/dist/domains/test-execution/coordinator.js +6 -5
- package/dist/domains/test-execution/plugin.d.ts +4 -2
- package/dist/domains/test-execution/plugin.js +6 -4
- package/dist/domains/test-generation/coordinator.d.ts +2 -1
- package/dist/domains/test-generation/coordinator.js +4 -3
- package/dist/domains/test-generation/plugin.d.ts +4 -2
- package/dist/domains/test-generation/plugin.js +12 -7
- package/dist/domains/visual-accessibility/coordinator.d.ts +2 -1
- package/dist/domains/visual-accessibility/coordinator.js +4 -3
- package/dist/domains/visual-accessibility/plugin.d.ts +4 -2
- package/dist/domains/visual-accessibility/plugin.js +9 -6
- package/dist/kernel/interfaces.d.ts +37 -0
- package/dist/kernel/kernel.d.ts +39 -0
- package/dist/kernel/kernel.js +157 -17
- package/dist/learning/embedder-endpoint-client.d.ts +133 -0
- package/dist/learning/embedder-endpoint-client.js +426 -0
- package/dist/learning/embedder-identity-store.d.ts +30 -0
- package/dist/learning/embedder-identity-store.js +136 -0
- package/dist/learning/qe-reasoning-bank.js +11 -3
- package/dist/learning/real-embeddings.d.ts +28 -0
- package/dist/learning/real-embeddings.js +130 -19
- package/dist/learning/sqlite-persistence.js +16 -3
- package/dist/mcp/bundle.js +455 -423
- package/dist/mcp/qe-tool-bridge.js +8 -1
- package/dist/mcp/tools/base.d.ts +56 -0
- package/dist/mcp/tools/base.js +104 -1
- package/dist/mcp/tools/chaos-resilience/inject.js +4 -2
- package/dist/mcp/tools/code-intelligence/analyze.js +5 -3
- package/dist/mcp/tools/contract-testing/validate.js +4 -2
- package/dist/mcp/tools/coverage-analysis/index.js +11 -5
- package/dist/mcp/tools/defect-intelligence/predict.js +5 -10
- package/dist/mcp/tools/learning-optimization/optimize.js +4 -2
- package/dist/mcp/tools/registry.js +8 -1
- package/dist/mcp/tools/test-generation/generate.js +10 -6
- package/dist/mcp/tools/visual-accessibility/index.js +7 -4
- package/dist/shared/llm/llm-router-service.d.ts +77 -0
- package/dist/shared/llm/llm-router-service.js +166 -0
- package/dist/shared/llm/providers/gemini.js +5 -2
- package/dist/shared/llm/router/config-store.d.ts +89 -0
- package/dist/shared/llm/router/config-store.js +261 -0
- package/dist/shared/llm/router/hybrid-router.js +33 -11
- package/package.json +1 -1
- package/dist/cli/chunks/adapter-MISKG4JY.js +0 -2
- package/dist/cli/chunks/aqe-learning-engine-NDLBNM6C.js +0 -2
- package/dist/cli/chunks/base-OMDJAHGF.js +0 -2
- package/dist/cli/chunks/browser-workflow-XUG5Z5CJ.js +0 -2
- package/dist/cli/chunks/chunk-JD3VV2BD.js +0 -2
- package/dist/cli/chunks/chunk-USLHYNYG.js +0 -2
- package/dist/cli/chunks/chunk-ZYPSXPQ5.js +0 -2
- package/dist/cli/chunks/client-VXJRAW3W.js +0 -2
- package/dist/cli/chunks/cross-domain-router-AKHC7TFO.js +0 -2
- package/dist/cli/chunks/dream-FPWIC7KF.js +0 -2
- package/dist/cli/chunks/heartbeat-scheduler-WHGMTRT7.js +0 -2
- package/dist/cli/chunks/hnsw-adapter-IA2M5KDH.js +0 -2
- package/dist/cli/chunks/hnsw-index-UBXTXXLQ.js +0 -2
- package/dist/cli/chunks/hybrid-router-IY62NN7G.js +0 -2
- package/dist/cli/chunks/impact-analyzer-6PVCOS33.js +0 -2
- package/dist/cli/chunks/init-wizard-XHP5C6RD.js +0 -2
- package/dist/cli/chunks/kernel-YM5PY5SS.js +0 -2
- package/dist/cli/chunks/knowledge-graph-LTUOUHO7.js +0 -2
- package/dist/cli/chunks/llm-router-S3HHVPH4.js +0 -36
- package/dist/cli/chunks/load-test-CYCAQI3F.js +0 -2
- package/dist/cli/chunks/memory-backend-RW2OPAJU.js +0 -2
- package/dist/cli/chunks/memory-handlers-6COHVON7.js +0 -2
- package/dist/cli/chunks/protocol-executor-CREUYOBZ.js +0 -2
- package/dist/cli/chunks/qe-reasoning-bank-J4DLHRJY.js +0 -2
- package/dist/cli/chunks/queen-coordinator-MGLNSTPY.js +0 -2
- package/dist/cli/chunks/real-embeddings-LE6YXNPZ.js +0 -2
- package/dist/cli/chunks/router-CXGPFUT2.js +0 -2
- package/dist/cli/chunks/routing-feedback-MZ73LFBP.js +0 -2
- package/dist/cli/chunks/rvf-native-adapter-T6JBPHTE.js +0 -2
- package/dist/cli/chunks/safe-db-DS4SKPYB.js +0 -2
- package/dist/cli/chunks/schedule-H2Q7XWAK.js +0 -2
- package/dist/cli/chunks/scheduler-PMZMKQIF.js +0 -2
- package/dist/cli/chunks/sqlite-persistence-ONKEAMTQ.js +0 -2
- package/dist/cli/chunks/unified-memory-PNHI6I3R.js +0 -2
- package/dist/cli/chunks/unified-memory-hnsw-CDV3UMBD.js +0 -2
- package/dist/cli/chunks/unified-persistence-FMCHCEZV.js +0 -2
- package/dist/cli/chunks/visual-security-3WRJQDUY.js +0 -2
- package/dist/cli/chunks/witness-chain-5ZKCKNRJ.js +0 -2
- package/dist/cli/chunks/workflow-orchestrator-OBSFY7YK.js +0 -2
|
@@ -0,0 +1,426 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External Embedder Endpoint Client (ADR-097)
|
|
3
|
+
*
|
|
4
|
+
* OpenAI-compatible `/v1/embeddings` client over HTTP and HTTP-over-Unix-socket.
|
|
5
|
+
* When `EmbeddingConfig.endpoint` is set, AQE skips loading `@huggingface/transformers`
|
|
6
|
+
* entirely and routes feature-extraction through this client.
|
|
7
|
+
*
|
|
8
|
+
* Design choices per ADR-097 (revised after devil's-advocate audit):
|
|
9
|
+
* - OpenAI wire format with `encoding_format: 'float'` pinned (interop with TEI /
|
|
10
|
+
* vLLM / llama.cpp / Ollama / LocalAI / LM Studio — float pin avoids the
|
|
11
|
+
* base64 default some servers ship).
|
|
12
|
+
* - HTTP + HTTP-over-Unix (one protocol, two transports — no NDJSON fork).
|
|
13
|
+
* - Hard-fail with circuit breaker on errors (no silent hash fallback).
|
|
14
|
+
* - L2-renormalize on receive (don't trust server claims).
|
|
15
|
+
* - Bearer auth from env only; URL userinfo stripped + warned on construct.
|
|
16
|
+
* - Probe + identity fingerprint required before `embed()`; breaker recovery
|
|
17
|
+
* invalidates identity so re-probe runs against a fresh endpoint.
|
|
18
|
+
* - True TCP connect timeout via socket-level setTimeout (not req.setTimeout).
|
|
19
|
+
* - TLS knobs (ca / cert / key / rejectUnauthorized / servername) for self-hosted.
|
|
20
|
+
*/
|
|
21
|
+
import * as http from 'node:http';
|
|
22
|
+
import * as https from 'node:https';
|
|
23
|
+
import { createHash } from 'node:crypto';
|
|
24
|
+
import { URL } from 'node:url';
|
|
25
|
+
import { normalize as l2Normalize } from '../shared/utils/vector-math.js';
|
|
26
|
+
const DEFAULT_OPTIONS = {
|
|
27
|
+
model: 'Xenova/all-MiniLM-L6-v2',
|
|
28
|
+
expectedDim: 384,
|
|
29
|
+
connectTimeoutMs: 5_000,
|
|
30
|
+
requestTimeoutMs: 30_000,
|
|
31
|
+
failureThreshold: 3,
|
|
32
|
+
failureWindowMs: 60_000,
|
|
33
|
+
};
|
|
34
|
+
const CANARY_TEXT = 'AQE embedder endpoint identity canary v1';
|
|
35
|
+
const EMBEDDINGS_PATH = '/v1/embeddings';
|
|
36
|
+
/**
|
|
37
|
+
* Strip user:password@ from a URL for safe logging.
|
|
38
|
+
* Returns the original string for non-URL inputs (unix paths).
|
|
39
|
+
*/
|
|
40
|
+
export function redactEndpoint(endpoint) {
|
|
41
|
+
if (endpoint.startsWith('unix:'))
|
|
42
|
+
return endpoint;
|
|
43
|
+
try {
|
|
44
|
+
const u = new URL(endpoint);
|
|
45
|
+
if (u.username || u.password) {
|
|
46
|
+
u.username = '';
|
|
47
|
+
u.password = '';
|
|
48
|
+
return u.toString().replace(/\/$/, '');
|
|
49
|
+
}
|
|
50
|
+
return endpoint;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return endpoint;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export function parseEndpoint(endpoint) {
|
|
57
|
+
if (endpoint.startsWith('unix:')) {
|
|
58
|
+
const socketPath = endpoint.slice('unix:'.length);
|
|
59
|
+
if (!socketPath.startsWith('/')) {
|
|
60
|
+
throw new Error(`unix endpoint must be an absolute path: ${endpoint}`);
|
|
61
|
+
}
|
|
62
|
+
return { transport: 'unix', socketPath, safeUrl: endpoint };
|
|
63
|
+
}
|
|
64
|
+
const url = new URL(endpoint);
|
|
65
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
66
|
+
throw new Error(`unsupported embedder endpoint protocol: ${url.protocol}`);
|
|
67
|
+
}
|
|
68
|
+
const hadUserinfo = Boolean(url.username || url.password);
|
|
69
|
+
if (hadUserinfo) {
|
|
70
|
+
// Warn loudly — userinfo in the URL bypasses the env-only token convention
|
|
71
|
+
// and leaks into Node's default error messages.
|
|
72
|
+
console.warn(`[EmbedderEndpointClient] endpoint URL contained userinfo; stripping. ` +
|
|
73
|
+
`Use AQE_EMBEDDER_TOKEN env var for credentials.`);
|
|
74
|
+
url.username = '';
|
|
75
|
+
url.password = '';
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
transport: url.protocol === 'https:' ? 'https' : 'http',
|
|
79
|
+
host: url.hostname,
|
|
80
|
+
port: url.port ? Number(url.port) : url.protocol === 'https:' ? 443 : 80,
|
|
81
|
+
protocol: url.protocol,
|
|
82
|
+
safeUrl: url.toString().replace(/\/$/, ''),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Circuit breaker state.
|
|
87
|
+
*
|
|
88
|
+
* Tracks failure timestamps within `windowMs`. Opens when count reaches `threshold`.
|
|
89
|
+
* While open, every request fast-fails until the window expires. On recovery
|
|
90
|
+
* (window elapses), the breaker calls `onRecover` so the client can invalidate
|
|
91
|
+
* cached endpoint identity — endpoint restarts often coincide with model swaps.
|
|
92
|
+
*/
|
|
93
|
+
class CircuitBreaker {
|
|
94
|
+
threshold;
|
|
95
|
+
windowMs;
|
|
96
|
+
onRecover;
|
|
97
|
+
failures = [];
|
|
98
|
+
trippedAt = null;
|
|
99
|
+
constructor(threshold, windowMs, onRecover = () => { }) {
|
|
100
|
+
this.threshold = threshold;
|
|
101
|
+
this.windowMs = windowMs;
|
|
102
|
+
this.onRecover = onRecover;
|
|
103
|
+
}
|
|
104
|
+
recordSuccess() {
|
|
105
|
+
this.failures = [];
|
|
106
|
+
this.trippedAt = null;
|
|
107
|
+
}
|
|
108
|
+
recordFailure() {
|
|
109
|
+
const now = Date.now();
|
|
110
|
+
this.failures = this.failures.filter((t) => now - t < this.windowMs);
|
|
111
|
+
this.failures.push(now);
|
|
112
|
+
if (this.failures.length >= this.threshold) {
|
|
113
|
+
this.trippedAt = now;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
isOpen() {
|
|
117
|
+
if (this.trippedAt === null)
|
|
118
|
+
return false;
|
|
119
|
+
if (Date.now() - this.trippedAt >= this.windowMs) {
|
|
120
|
+
this.failures = [];
|
|
121
|
+
this.trippedAt = null;
|
|
122
|
+
// Endpoint may have restarted with a different model — force re-probe.
|
|
123
|
+
this.onRecover();
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
/** For tests / observability */
|
|
129
|
+
getState() {
|
|
130
|
+
return { open: this.isOpen(), failures: this.failures.length, trippedAt: this.trippedAt };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* OpenAI-compatible embeddings client with keep-alive, circuit breaker, and identity probe.
|
|
135
|
+
*/
|
|
136
|
+
export class EmbedderEndpointClient {
|
|
137
|
+
parsed;
|
|
138
|
+
agent;
|
|
139
|
+
breaker;
|
|
140
|
+
opts;
|
|
141
|
+
cachedIdentity = null;
|
|
142
|
+
/** True while a probe is in flight — prevents the embed() lazy-probe race. */
|
|
143
|
+
probeInFlight = null;
|
|
144
|
+
constructor(options) {
|
|
145
|
+
this.opts = {
|
|
146
|
+
endpoint: options.endpoint,
|
|
147
|
+
token: options.token,
|
|
148
|
+
model: options.model ?? DEFAULT_OPTIONS.model,
|
|
149
|
+
expectedDim: options.expectedDim ?? DEFAULT_OPTIONS.expectedDim,
|
|
150
|
+
connectTimeoutMs: options.connectTimeoutMs ?? DEFAULT_OPTIONS.connectTimeoutMs,
|
|
151
|
+
requestTimeoutMs: options.requestTimeoutMs ?? DEFAULT_OPTIONS.requestTimeoutMs,
|
|
152
|
+
failureThreshold: options.failureThreshold ?? DEFAULT_OPTIONS.failureThreshold,
|
|
153
|
+
failureWindowMs: options.failureWindowMs ?? DEFAULT_OPTIONS.failureWindowMs,
|
|
154
|
+
tlsOptions: options.tlsOptions,
|
|
155
|
+
};
|
|
156
|
+
this.parsed = parseEndpoint(options.endpoint);
|
|
157
|
+
if (this.parsed.transport === 'https') {
|
|
158
|
+
// rejectUnauthorized defaults to true; explicit knobs allow self-hosted CAs.
|
|
159
|
+
const tls = options.tlsOptions ?? {};
|
|
160
|
+
this.agent = new https.Agent({
|
|
161
|
+
keepAlive: true,
|
|
162
|
+
ca: tls.ca,
|
|
163
|
+
cert: tls.cert,
|
|
164
|
+
key: tls.key,
|
|
165
|
+
rejectUnauthorized: tls.rejectUnauthorized !== false,
|
|
166
|
+
servername: tls.servername,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
this.agent = new http.Agent({ keepAlive: true });
|
|
171
|
+
}
|
|
172
|
+
this.breaker = new CircuitBreaker(this.opts.failureThreshold, this.opts.failureWindowMs, () => {
|
|
173
|
+
// On breaker recovery, drop identity so the next embed() re-probes.
|
|
174
|
+
// Endpoint restarts (the common cause of recovery) often change models.
|
|
175
|
+
this.cachedIdentity = null;
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/** Public, redacted endpoint URL for logs/metrics. */
|
|
179
|
+
getSafeEndpoint() {
|
|
180
|
+
return this.parsed.safeUrl;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Embed an array of texts. Returns L2-normalized vectors in input order.
|
|
184
|
+
*
|
|
185
|
+
* Gated on `probe()` — if no cached identity exists, probe is run first so the
|
|
186
|
+
* dim/identity boundary check fires on every cold path. Concurrent embed calls
|
|
187
|
+
* share a single in-flight probe.
|
|
188
|
+
*
|
|
189
|
+
* Throws on error — callers MUST NOT fall back to hash embeddings, which would
|
|
190
|
+
* poison the HNSW index with non-comparable vectors.
|
|
191
|
+
*/
|
|
192
|
+
async embed(texts) {
|
|
193
|
+
if (texts.length === 0)
|
|
194
|
+
return [];
|
|
195
|
+
if (this.breaker.isOpen()) {
|
|
196
|
+
throw new Error(`[EmbedderEndpointClient] circuit breaker open for ${this.parsed.safeUrl} — fast-failing`);
|
|
197
|
+
}
|
|
198
|
+
// Identity gate: every embed call must have a verified identity behind it.
|
|
199
|
+
if (this.cachedIdentity === null) {
|
|
200
|
+
await this.ensureProbed();
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
const data = await this.postEmbeddings(texts);
|
|
204
|
+
const vectors = this.decodeAndNormalize(data, texts.length);
|
|
205
|
+
this.breaker.recordSuccess();
|
|
206
|
+
return vectors;
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
this.breaker.recordFailure();
|
|
210
|
+
throw err;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Probe the endpoint by embedding a fixed canary. Returns identity fingerprint.
|
|
215
|
+
* Asserts dim === expectedDim. Throws loud on mismatch.
|
|
216
|
+
* Public so operators can re-probe on demand; idempotent under concurrency.
|
|
217
|
+
*/
|
|
218
|
+
async probe() {
|
|
219
|
+
if (this.probeInFlight)
|
|
220
|
+
return this.probeInFlight;
|
|
221
|
+
this.probeInFlight = this.doProbe().finally(() => {
|
|
222
|
+
this.probeInFlight = null;
|
|
223
|
+
});
|
|
224
|
+
return this.probeInFlight;
|
|
225
|
+
}
|
|
226
|
+
async ensureProbed() {
|
|
227
|
+
if (this.cachedIdentity)
|
|
228
|
+
return this.cachedIdentity;
|
|
229
|
+
return this.probe();
|
|
230
|
+
}
|
|
231
|
+
async doProbe() {
|
|
232
|
+
// Direct HTTP call (NOT via embed()) so we don't recurse on the identity gate.
|
|
233
|
+
let vec;
|
|
234
|
+
try {
|
|
235
|
+
const data = await this.postEmbeddings([CANARY_TEXT]);
|
|
236
|
+
const vectors = this.decodeAndNormalize(data, 1);
|
|
237
|
+
vec = vectors[0];
|
|
238
|
+
this.breaker.recordSuccess();
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
this.breaker.recordFailure();
|
|
242
|
+
throw err;
|
|
243
|
+
}
|
|
244
|
+
if (!vec || vec.length !== this.opts.expectedDim) {
|
|
245
|
+
throw new Error(`[EmbedderEndpointClient] dim mismatch: expected ${this.opts.expectedDim}, got ${vec?.length ?? 0} from ${this.parsed.safeUrl}`);
|
|
246
|
+
}
|
|
247
|
+
// Fingerprint = SHA-256 of the canary vector's quantized bytes (16 hex chars).
|
|
248
|
+
// Quantize to int16 first so trivial fp noise between identical (model, server)
|
|
249
|
+
// configurations doesn't shift the fingerprint.
|
|
250
|
+
const buf = Buffer.alloc(vec.length * 2);
|
|
251
|
+
for (let i = 0; i < vec.length; i++) {
|
|
252
|
+
const q = Math.max(-32768, Math.min(32767, Math.round(vec[i] * 32767)));
|
|
253
|
+
buf.writeInt16LE(q, i * 2);
|
|
254
|
+
}
|
|
255
|
+
const fingerprint = createHash('sha256').update(buf).digest('hex').slice(0, 16);
|
|
256
|
+
this.cachedIdentity = {
|
|
257
|
+
dim: vec.length,
|
|
258
|
+
fingerprint,
|
|
259
|
+
endpoint: this.parsed.safeUrl,
|
|
260
|
+
};
|
|
261
|
+
return this.cachedIdentity;
|
|
262
|
+
}
|
|
263
|
+
getCachedIdentity() {
|
|
264
|
+
return this.cachedIdentity;
|
|
265
|
+
}
|
|
266
|
+
getBreakerState() {
|
|
267
|
+
return this.breaker.getState();
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Close the underlying keep-alive agent. Tests should call this in afterEach.
|
|
271
|
+
*/
|
|
272
|
+
close() {
|
|
273
|
+
this.agent.destroy();
|
|
274
|
+
}
|
|
275
|
+
decodeAndNormalize(json, expectedCount) {
|
|
276
|
+
// OpenAI shape: { data: [{ embedding: [...], index: 0 }, ...] }
|
|
277
|
+
if (!json ||
|
|
278
|
+
typeof json !== 'object' ||
|
|
279
|
+
!Array.isArray(json.data)) {
|
|
280
|
+
throw new Error(`[EmbedderEndpointClient] response missing data array`);
|
|
281
|
+
}
|
|
282
|
+
const rawData = json
|
|
283
|
+
.data;
|
|
284
|
+
if (rawData.length !== expectedCount) {
|
|
285
|
+
throw new Error(`[EmbedderEndpointClient] response count mismatch: expected ${expectedCount}, got ${rawData.length}`);
|
|
286
|
+
}
|
|
287
|
+
// OpenAI guarantees `index` field but ordering is not guaranteed — sort defensively.
|
|
288
|
+
// Fall back to array position when index is absent.
|
|
289
|
+
const indexed = rawData.map((item, i) => ({
|
|
290
|
+
idx: typeof item.index === 'number' ? item.index : i,
|
|
291
|
+
embedding: item.embedding,
|
|
292
|
+
}));
|
|
293
|
+
indexed.sort((a, b) => a.idx - b.idx);
|
|
294
|
+
return indexed.map(({ embedding }, i) => {
|
|
295
|
+
// We pinned encoding_format: 'float' in the request, so a base64 string
|
|
296
|
+
// back means the server ignored our request. Fail loud rather than guess.
|
|
297
|
+
if (typeof embedding === 'string') {
|
|
298
|
+
throw new Error(`[EmbedderEndpointClient] received base64 embedding at position ${i} — ` +
|
|
299
|
+
`server ignored encoding_format=float. Configure the server to return float arrays.`);
|
|
300
|
+
}
|
|
301
|
+
if (!Array.isArray(embedding)) {
|
|
302
|
+
throw new Error(`[EmbedderEndpointClient] missing embedding at position ${i}`);
|
|
303
|
+
}
|
|
304
|
+
if (embedding.length !== this.opts.expectedDim) {
|
|
305
|
+
throw new Error(`[EmbedderEndpointClient] dim mismatch at position ${i}: expected ${this.opts.expectedDim}, got ${embedding.length}`);
|
|
306
|
+
}
|
|
307
|
+
// Re-normalize regardless of server claim — cheap and correct.
|
|
308
|
+
return l2Normalize(embedding);
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
postEmbeddings(texts) {
|
|
312
|
+
// Pin encoding_format=float so providers that default to base64 (newer
|
|
313
|
+
// OpenAI/Azure for dims>100) don't silently return strings we'd have to
|
|
314
|
+
// guess at. The decoder throws loud on string responses.
|
|
315
|
+
const body = JSON.stringify({
|
|
316
|
+
model: this.opts.model,
|
|
317
|
+
input: texts,
|
|
318
|
+
encoding_format: 'float',
|
|
319
|
+
});
|
|
320
|
+
return this.request(EMBEDDINGS_PATH, body);
|
|
321
|
+
}
|
|
322
|
+
request(path, body) {
|
|
323
|
+
const headers = {
|
|
324
|
+
'content-type': 'application/json',
|
|
325
|
+
'content-length': Buffer.byteLength(body),
|
|
326
|
+
accept: 'application/json',
|
|
327
|
+
};
|
|
328
|
+
if (this.opts.token) {
|
|
329
|
+
headers['authorization'] = `Bearer ${this.opts.token}`;
|
|
330
|
+
}
|
|
331
|
+
const baseOptions = {
|
|
332
|
+
method: 'POST',
|
|
333
|
+
path,
|
|
334
|
+
headers,
|
|
335
|
+
agent: this.agent,
|
|
336
|
+
};
|
|
337
|
+
let reqOptions;
|
|
338
|
+
let requestFn;
|
|
339
|
+
if (this.parsed.transport === 'unix') {
|
|
340
|
+
reqOptions = { ...baseOptions, socketPath: this.parsed.socketPath };
|
|
341
|
+
requestFn = http.request;
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
reqOptions = {
|
|
345
|
+
...baseOptions,
|
|
346
|
+
host: this.parsed.host,
|
|
347
|
+
port: this.parsed.port,
|
|
348
|
+
protocol: this.parsed.protocol,
|
|
349
|
+
};
|
|
350
|
+
requestFn = this.parsed.transport === 'https' ? https.request : http.request;
|
|
351
|
+
}
|
|
352
|
+
const safeUrl = this.parsed.safeUrl;
|
|
353
|
+
const connectTimeoutMs = this.opts.connectTimeoutMs;
|
|
354
|
+
const requestTimeoutMs = this.opts.requestTimeoutMs;
|
|
355
|
+
return new Promise((resolve, reject) => {
|
|
356
|
+
let settled = false;
|
|
357
|
+
const safeResolve = (val) => {
|
|
358
|
+
if (settled)
|
|
359
|
+
return;
|
|
360
|
+
settled = true;
|
|
361
|
+
resolve(val);
|
|
362
|
+
};
|
|
363
|
+
const safeReject = (err) => {
|
|
364
|
+
if (settled)
|
|
365
|
+
return;
|
|
366
|
+
settled = true;
|
|
367
|
+
reject(err);
|
|
368
|
+
};
|
|
369
|
+
const req = requestFn(reqOptions, (res) => {
|
|
370
|
+
const chunks = [];
|
|
371
|
+
res.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
|
|
372
|
+
res.on('end', () => {
|
|
373
|
+
const text = Buffer.concat(chunks).toString('utf-8');
|
|
374
|
+
if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) {
|
|
375
|
+
safeReject(new Error(`[EmbedderEndpointClient] HTTP ${res.statusCode} from ${safeUrl}: ${text.slice(0, 200)}`));
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
try {
|
|
379
|
+
const parsed = JSON.parse(text);
|
|
380
|
+
safeResolve(parsed);
|
|
381
|
+
}
|
|
382
|
+
catch (err) {
|
|
383
|
+
safeReject(new Error(`[EmbedderEndpointClient] invalid JSON from ${safeUrl}: ${err.message}`));
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
res.on('error', (err) => safeReject(err));
|
|
387
|
+
});
|
|
388
|
+
// True TCP connect timeout: set on the socket itself, fires before any byte
|
|
389
|
+
// is exchanged. req.setTimeout is an idle-after-write timeout — wrong tool
|
|
390
|
+
// for SYN-drop scenarios. Once connected we clear it and let
|
|
391
|
+
// requestTimeoutMs guard the request/response phase.
|
|
392
|
+
req.on('socket', (socket) => {
|
|
393
|
+
// Pre-connect phase: any idle period this long means the SYN handshake
|
|
394
|
+
// is stuck (DNS, ACL drop, blackhole).
|
|
395
|
+
socket.setTimeout(connectTimeoutMs);
|
|
396
|
+
const onConnect = () => {
|
|
397
|
+
// Switch to the post-connect (read/write idle) timeout budget.
|
|
398
|
+
socket.setTimeout(requestTimeoutMs);
|
|
399
|
+
};
|
|
400
|
+
// For brand-new sockets we wait for 'connect'. Pooled (keep-alive)
|
|
401
|
+
// sockets fire 'connect' synchronously already-connected or not at all —
|
|
402
|
+
// for those, jump straight to the request timeout.
|
|
403
|
+
if (socket.connecting) {
|
|
404
|
+
socket.once('connect', onConnect);
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
onConnect();
|
|
408
|
+
}
|
|
409
|
+
socket.on('timeout', () => {
|
|
410
|
+
// Distinguish phase for the error message.
|
|
411
|
+
const phase = socket.connecting ? 'connect' : 'request';
|
|
412
|
+
const limit = socket.connecting ? connectTimeoutMs : requestTimeoutMs;
|
|
413
|
+
const err = new Error(`[EmbedderEndpointClient] ${phase} timeout after ${limit}ms`);
|
|
414
|
+
// Reject FIRST, then destroy. If we destroy first, the 'error' event
|
|
415
|
+
// races us and we might lose the timeout-specific message.
|
|
416
|
+
safeReject(err);
|
|
417
|
+
req.destroy(err);
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
req.on('error', (err) => safeReject(err));
|
|
421
|
+
req.write(body);
|
|
422
|
+
req.end();
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
//# sourceMappingURL=embedder-endpoint-client.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent store for ADR-097 embedder endpoint identity.
|
|
3
|
+
*
|
|
4
|
+
* Identity is written into the unified memory.db `kv_store` table under
|
|
5
|
+
* namespace `_system`, key `embedder_identity:<safe_endpoint_url>`. This lets
|
|
6
|
+
* us detect cross-run model drift: if the fingerprint changes between AQE
|
|
7
|
+
* processes, vectors written before the change may not be comparable to vectors
|
|
8
|
+
* written after, and the operator should know.
|
|
9
|
+
*
|
|
10
|
+
* Conforms to the project's unified-persistence rule (no new .db files).
|
|
11
|
+
*
|
|
12
|
+
* Persistence failures are non-fatal — callers wrap us in try/catch and log.
|
|
13
|
+
* We never throw out of these helpers under expected conditions; failure
|
|
14
|
+
* reasons are surfaced as thrown errors only for genuine I/O problems.
|
|
15
|
+
*/
|
|
16
|
+
import type { EndpointIdentity } from './embedder-endpoint-client.js';
|
|
17
|
+
/**
|
|
18
|
+
* Load the last persisted identity for the given endpoint URL.
|
|
19
|
+
* Returns null when nothing has been stored yet OR when the kv store is unavailable.
|
|
20
|
+
*/
|
|
21
|
+
export declare function loadEmbedderIdentity(endpointUrl: string): EndpointIdentity | null;
|
|
22
|
+
/**
|
|
23
|
+
* Persist the current identity. Overwrites any prior entry for the same endpoint.
|
|
24
|
+
*/
|
|
25
|
+
export declare function saveEmbedderIdentity(identity: EndpointIdentity): void;
|
|
26
|
+
/**
|
|
27
|
+
* For tests: close the cached connection so a fresh DB path can be used.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resetEmbedderIdentityStore(): void;
|
|
30
|
+
//# sourceMappingURL=embedder-identity-store.d.ts.map
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent store for ADR-097 embedder endpoint identity.
|
|
3
|
+
*
|
|
4
|
+
* Identity is written into the unified memory.db `kv_store` table under
|
|
5
|
+
* namespace `_system`, key `embedder_identity:<safe_endpoint_url>`. This lets
|
|
6
|
+
* us detect cross-run model drift: if the fingerprint changes between AQE
|
|
7
|
+
* processes, vectors written before the change may not be comparable to vectors
|
|
8
|
+
* written after, and the operator should know.
|
|
9
|
+
*
|
|
10
|
+
* Conforms to the project's unified-persistence rule (no new .db files).
|
|
11
|
+
*
|
|
12
|
+
* Persistence failures are non-fatal — callers wrap us in try/catch and log.
|
|
13
|
+
* We never throw out of these helpers under expected conditions; failure
|
|
14
|
+
* reasons are surfaced as thrown errors only for genuine I/O problems.
|
|
15
|
+
*/
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
18
|
+
import { createRequire } from 'node:module';
|
|
19
|
+
// `require` is unavailable in pure ESM; createRequire gives us synchronous
|
|
20
|
+
// access to CommonJS modules like better-sqlite3 from the ESM build output.
|
|
21
|
+
const nodeRequire = createRequire(import.meta.url);
|
|
22
|
+
/**
|
|
23
|
+
* Path to the unified memory.db. Honors AQE_MEMORY_PATH env override (used by
|
|
24
|
+
* tests and platform installers), otherwise falls back to the project default.
|
|
25
|
+
*/
|
|
26
|
+
function getMemoryDbPath() {
|
|
27
|
+
const env = process.env.AQE_MEMORY_PATH;
|
|
28
|
+
if (env && env.length > 0)
|
|
29
|
+
return env;
|
|
30
|
+
return join(process.cwd(), '.agentic-qe', 'memory.db');
|
|
31
|
+
}
|
|
32
|
+
const NAMESPACE = '_system';
|
|
33
|
+
const KEY_PREFIX = 'embedder_identity:';
|
|
34
|
+
let cachedDb = null;
|
|
35
|
+
let openFailed = false;
|
|
36
|
+
/**
|
|
37
|
+
* Lazily open memory.db. We use a dedicated read/write connection (better-sqlite3
|
|
38
|
+
* supports concurrent connections to the same file under WAL mode, which the
|
|
39
|
+
* init wizard enables). Failure to open is cached so we don't retry every call.
|
|
40
|
+
*/
|
|
41
|
+
function openDb() {
|
|
42
|
+
if (cachedDb)
|
|
43
|
+
return cachedDb;
|
|
44
|
+
if (openFailed)
|
|
45
|
+
return null;
|
|
46
|
+
try {
|
|
47
|
+
// Dynamic require (via createRequire — `require` is undefined in ESM) so
|
|
48
|
+
// this module doesn't fail to import when better-sqlite3 is absent in
|
|
49
|
+
// some test environments. The endpoint feature is optional; identity
|
|
50
|
+
// persistence is a nice-to-have on top of it.
|
|
51
|
+
const Database = nodeRequire('better-sqlite3');
|
|
52
|
+
const path = getMemoryDbPath();
|
|
53
|
+
const dir = join(path, '..');
|
|
54
|
+
if (!existsSync(dir)) {
|
|
55
|
+
mkdirSync(dir, { recursive: true });
|
|
56
|
+
}
|
|
57
|
+
const db = new Database(path);
|
|
58
|
+
db.pragma('journal_mode = WAL');
|
|
59
|
+
db.pragma('busy_timeout = 5000');
|
|
60
|
+
db.exec(`
|
|
61
|
+
CREATE TABLE IF NOT EXISTS kv_store (
|
|
62
|
+
key TEXT NOT NULL,
|
|
63
|
+
namespace TEXT NOT NULL,
|
|
64
|
+
value TEXT NOT NULL,
|
|
65
|
+
expires_at INTEGER,
|
|
66
|
+
created_at INTEGER DEFAULT (strftime('%s', 'now') * 1000),
|
|
67
|
+
PRIMARY KEY (namespace, key)
|
|
68
|
+
);
|
|
69
|
+
CREATE INDEX IF NOT EXISTS idx_kv_namespace ON kv_store(namespace);
|
|
70
|
+
`);
|
|
71
|
+
cachedDb = db;
|
|
72
|
+
return db;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
openFailed = true;
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Load the last persisted identity for the given endpoint URL.
|
|
81
|
+
* Returns null when nothing has been stored yet OR when the kv store is unavailable.
|
|
82
|
+
*/
|
|
83
|
+
export function loadEmbedderIdentity(endpointUrl) {
|
|
84
|
+
const db = openDb();
|
|
85
|
+
if (!db)
|
|
86
|
+
return null;
|
|
87
|
+
try {
|
|
88
|
+
const row = db
|
|
89
|
+
.prepare('SELECT value FROM kv_store WHERE namespace = ? AND key = ?')
|
|
90
|
+
.get(NAMESPACE, KEY_PREFIX + endpointUrl);
|
|
91
|
+
if (!row)
|
|
92
|
+
return null;
|
|
93
|
+
const parsed = JSON.parse(row.value);
|
|
94
|
+
if (!parsed.fingerprint || typeof parsed.dim !== 'number')
|
|
95
|
+
return null;
|
|
96
|
+
return {
|
|
97
|
+
fingerprint: parsed.fingerprint,
|
|
98
|
+
dim: parsed.dim,
|
|
99
|
+
endpoint: parsed.endpoint,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Persist the current identity. Overwrites any prior entry for the same endpoint.
|
|
108
|
+
*/
|
|
109
|
+
export function saveEmbedderIdentity(identity) {
|
|
110
|
+
const db = openDb();
|
|
111
|
+
if (!db)
|
|
112
|
+
return;
|
|
113
|
+
const row = {
|
|
114
|
+
fingerprint: identity.fingerprint,
|
|
115
|
+
dim: identity.dim,
|
|
116
|
+
endpoint: identity.endpoint,
|
|
117
|
+
updatedAt: Date.now(),
|
|
118
|
+
};
|
|
119
|
+
db.prepare('INSERT OR REPLACE INTO kv_store (key, namespace, value) VALUES (?, ?, ?)').run(KEY_PREFIX + identity.endpoint, NAMESPACE, JSON.stringify(row));
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* For tests: close the cached connection so a fresh DB path can be used.
|
|
123
|
+
*/
|
|
124
|
+
export function resetEmbedderIdentityStore() {
|
|
125
|
+
if (cachedDb) {
|
|
126
|
+
try {
|
|
127
|
+
cachedDb.close();
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
// ignore
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
cachedDb = null;
|
|
134
|
+
openFailed = false;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=embedder-identity-store.js.map
|
|
@@ -553,7 +553,7 @@ export class QEReasoningBank {
|
|
|
553
553
|
* for ARM64 or when transformers module cannot be loaded.
|
|
554
554
|
*/
|
|
555
555
|
async embed(text) {
|
|
556
|
-
// Try ONNX embeddings if enabled
|
|
556
|
+
// Try ONNX / endpoint embeddings if enabled
|
|
557
557
|
if (this.config.useONNXEmbeddings) {
|
|
558
558
|
try {
|
|
559
559
|
const { computeRealEmbedding } = await import('./real-embeddings.js');
|
|
@@ -565,8 +565,16 @@ export class QEReasoningBank {
|
|
|
565
565
|
return embedding;
|
|
566
566
|
}
|
|
567
567
|
catch (error) {
|
|
568
|
-
//
|
|
569
|
-
//
|
|
568
|
+
// ADR-097: if the external embedder endpoint is configured, we MUST NOT
|
|
569
|
+
// silently fall back to hash embeddings — they would poison the HNSW
|
|
570
|
+
// index by mixing non-comparable vectors. Propagate the throw so the
|
|
571
|
+
// caller can decide (retry, skip the write, etc).
|
|
572
|
+
const { isUsingEndpoint } = await import('./real-embeddings.js');
|
|
573
|
+
if (isUsingEndpoint()) {
|
|
574
|
+
throw error;
|
|
575
|
+
}
|
|
576
|
+
// In-process ARM64 ONNX compatibility issue or module not available —
|
|
577
|
+
// fall through to hash-based embedding (existing pre-ADR-097 behavior).
|
|
570
578
|
if (process.env.DEBUG) {
|
|
571
579
|
logger.warn('ONNX embeddings unavailable, using hash fallback', {
|
|
572
580
|
error: toErrorMessage(error),
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* Model: all-MiniLM-L6-v2 (384-dimensional sentence embeddings)
|
|
7
7
|
*/
|
|
8
8
|
export { cosineSimilarity } from '../shared/utils/vector-math.js';
|
|
9
|
+
import { type EndpointIdentity } from './embedder-endpoint-client.js';
|
|
9
10
|
/**
|
|
10
11
|
* Embedding model configuration
|
|
11
12
|
*/
|
|
@@ -18,6 +19,24 @@ export interface EmbeddingConfig {
|
|
|
18
19
|
enableCache: boolean;
|
|
19
20
|
/** Maximum cache size */
|
|
20
21
|
maxCacheSize: number;
|
|
22
|
+
/**
|
|
23
|
+
* Optional external embedder endpoint (ADR-097).
|
|
24
|
+
*
|
|
25
|
+
* When set, AQE routes feature-extraction to an OpenAI-compatible
|
|
26
|
+
* `/v1/embeddings` service instead of loading `@huggingface/transformers`
|
|
27
|
+
* in-process. Forms:
|
|
28
|
+
* - `http(s)://host:port` — HTTP transport
|
|
29
|
+
* - `unix:/absolute/path` — HTTP-over-Unix-socket transport
|
|
30
|
+
*
|
|
31
|
+
* Defaults to `process.env.AQE_EMBEDDER_ENDPOINT` when unset.
|
|
32
|
+
* When neither is set, behavior is identical to pre-ADR-097.
|
|
33
|
+
*/
|
|
34
|
+
endpoint?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Bearer token for the embedder endpoint (ADR-097).
|
|
37
|
+
* Defaults to `process.env.AQE_EMBEDDER_TOKEN`. Env-only — never in config files.
|
|
38
|
+
*/
|
|
39
|
+
endpointToken?: string;
|
|
21
40
|
}
|
|
22
41
|
export declare const DEFAULT_EMBEDDING_CONFIG: EmbeddingConfig;
|
|
23
42
|
/**
|
|
@@ -55,4 +74,13 @@ export declare function getEmbeddingDimension(): number;
|
|
|
55
74
|
* Reset initialization state (for testing)
|
|
56
75
|
*/
|
|
57
76
|
export declare function resetInitialization(): void;
|
|
77
|
+
/**
|
|
78
|
+
* ADR-097: Returns true when the embedding pipeline is the external endpoint path,
|
|
79
|
+
* false when it's the in-process transformer (or uninitialized).
|
|
80
|
+
*/
|
|
81
|
+
export declare function isUsingEndpoint(): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* ADR-097: Returns the endpoint identity fingerprint, or null when not using an endpoint.
|
|
84
|
+
*/
|
|
85
|
+
export declare function getEndpointIdentity(): EndpointIdentity | null;
|
|
58
86
|
//# sourceMappingURL=real-embeddings.d.ts.map
|