agentic-qe 3.9.30 → 3.9.31
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 +120 -0
- package/assets/skills/skills-manifest.json +1 -1
- package/dist/bridge/captured-experience-bridge.js +31 -2
- package/dist/cli/bundle.js +5 -5
- package/dist/cli/chunks/adapter-CI2GZYME.js +2 -0
- package/dist/cli/chunks/{agent-booster-wasm-TOSXWTUQ.js → agent-booster-wasm-AQXZYWZY.js} +2 -2
- package/dist/cli/chunks/{agent-handler-2BU6TYZZ.js → agent-handler-7N4ZPXCY.js} +2 -2
- package/dist/cli/chunks/{agent-memory-branch-JFG3BXZY.js → agent-memory-branch-FLKF2JVX.js} +2 -2
- package/dist/cli/chunks/aqe-learning-engine-KNQKALLI.js +2 -0
- package/dist/cli/chunks/{audit-4IGZZKUW.js → audit-B4V4IKTA.js} +2 -2
- package/dist/cli/chunks/base-P7PRMQWY.js +2 -0
- package/dist/cli/chunks/{hnswlib-node-STKRZIU3.js → better-sqlite3-NYOVLWBG.js} +2 -2
- package/dist/cli/chunks/{brain-handler-RIDW27HH.js → brain-handler-OOXJ2KYY.js} +4 -4
- package/dist/cli/chunks/{branch-enumerator-GMYHLLWD.js → branch-enumerator-LFKFW3A4.js} +2 -2
- package/dist/cli/chunks/{browser-N76A3I2P.js → browser-F72IES2I.js} +2 -2
- package/dist/cli/chunks/browser-workflow-QUP4A763.js +2 -0
- package/dist/cli/chunks/{chunk-JCDEMPJS.js → chunk-2NLZXG6V.js} +1 -1
- package/dist/cli/chunks/{chunk-ETEHVG76.js → chunk-35SFAIXE.js} +2 -2
- package/dist/cli/chunks/{chunk-FG5GL26L.js → chunk-3VKB4HVT.js} +2 -2
- package/dist/cli/chunks/{chunk-XKH4E2IQ.js → chunk-462MZLJB.js} +1 -1
- package/dist/cli/chunks/{chunk-MZ7M2CDV.js → chunk-47QIAHUJ.js} +2 -2
- package/dist/cli/chunks/{chunk-IP6ZTXNJ.js → chunk-4N736EES.js} +2 -2
- package/dist/cli/chunks/{chunk-YXR5RYRE.js → chunk-52D3CYE5.js} +2 -2
- package/dist/cli/chunks/{chunk-C6TNYLD7.js → chunk-5AMAJCZS.js} +3 -3
- package/dist/cli/chunks/{chunk-ZKZTSYPU.js → chunk-5G2L4XRU.js} +2 -2
- package/dist/cli/chunks/{chunk-2MKSEL6F.js → chunk-5M4F3PAL.js} +1 -1
- package/dist/cli/chunks/{chunk-37I6K7QO.js → chunk-5WWQLPB4.js} +2 -2
- package/dist/cli/chunks/chunk-5Z6PYYWK.js +2 -0
- package/dist/cli/chunks/{chunk-MHPK4ZPK.js → chunk-62KYX5NH.js} +2 -2
- package/dist/cli/chunks/{chunk-TYZGEVP6.js → chunk-7MXQV7RB.js} +12 -6
- package/dist/cli/chunks/{chunk-XVTZXCHI.js → chunk-7OVVBBOZ.js} +2 -2
- package/dist/cli/chunks/{chunk-MOLOWMON.js → chunk-7RPEBKQZ.js} +2 -2
- package/dist/cli/chunks/{chunk-5F26LSG2.js → chunk-7UHRT5AX.js} +2 -2
- package/dist/cli/chunks/{chunk-MV3NUANS.js → chunk-7V7TP242.js} +1 -1
- package/dist/cli/chunks/{chunk-WTXRPYNN.js → chunk-A424Z7LA.js} +1 -1
- package/dist/cli/chunks/{chunk-UYLHIGNC.js → chunk-AKFRM4IO.js} +2 -2
- package/dist/cli/chunks/{chunk-OEGFOMXP.js → chunk-AKJWBJDK.js} +2 -2
- package/dist/cli/chunks/{chunk-2GBBZLXT.js → chunk-AQN6PDHI.js} +1 -1
- package/dist/cli/chunks/{chunk-ITBPDVK5.js → chunk-BMS7WFDB.js} +1 -1
- package/dist/cli/chunks/{chunk-2O5TT3UT.js → chunk-BYHUGO73.js} +3 -3
- package/dist/cli/chunks/{chunk-CNNVL5W4.js → chunk-CFLA2GBS.js} +2 -2
- package/dist/cli/chunks/{chunk-Y57V4FDT.js → chunk-CNKOIHF6.js} +2 -2
- package/dist/cli/chunks/{chunk-3EP2YZSY.js → chunk-CQWZNVIM.js} +2 -2
- package/dist/cli/chunks/{chunk-GEXVUFK4.js → chunk-CUSLB7MB.js} +2 -2
- package/dist/cli/chunks/chunk-CYUGE5ZQ.js +2 -0
- package/dist/cli/chunks/{chunk-QNSUPXUU.js → chunk-DMF4Z2M6.js} +2 -2
- package/dist/cli/chunks/{chunk-Z3TXQOS7.js → chunk-DUF733Z7.js} +1 -1
- package/dist/cli/chunks/{chunk-XK6YXCS7.js → chunk-EAIPJSKH.js} +2 -2
- package/dist/cli/chunks/{chunk-TULNR2AH.js → chunk-EJXRBAQF.js} +2 -2
- package/dist/cli/chunks/{chunk-XDYTQPJM.js → chunk-EKYPQ5DX.js} +1 -1
- package/dist/cli/chunks/{chunk-QBDXUI2H.js → chunk-ENCFLC44.js} +2 -2
- package/dist/cli/chunks/{chunk-SAVITYEX.js → chunk-FOAWHNOP.js} +2 -2
- package/dist/cli/chunks/{chunk-QOJ7EAF5.js → chunk-FUHPLXJX.js} +1 -1
- package/dist/cli/chunks/{chunk-KR2PGNXX.js → chunk-G2U7Q6V6.js} +2 -2
- package/dist/cli/chunks/{chunk-DDMFTEJP.js → chunk-G3YKCZWQ.js} +3 -3
- package/dist/cli/chunks/{chunk-YETPSL6H.js → chunk-GJSH4UZ3.js} +3 -3
- package/dist/cli/chunks/{chunk-BQLFOJ5G.js → chunk-GN7FUCJH.js} +1 -1
- package/dist/cli/chunks/{chunk-GY4EGQO3.js → chunk-GQFAVT2I.js} +1 -1
- package/dist/cli/chunks/{chunk-IHRFR5SV.js → chunk-HHEIQHLQ.js} +2 -2
- package/dist/cli/chunks/{chunk-YKFURJNP.js → chunk-I3TFGMOQ.js} +1 -1
- package/dist/cli/chunks/{chunk-LDGNVPBZ.js → chunk-IRRGUXAU.js} +1 -1
- package/dist/cli/chunks/{chunk-IWC6GR24.js → chunk-IY4P35N3.js} +2 -2
- package/dist/cli/chunks/{chunk-AWWD3EI3.js → chunk-JF3U456G.js} +2 -2
- package/dist/cli/chunks/{chunk-5E3YCZC5.js → chunk-JJRZOLDY.js} +2 -2
- package/dist/cli/chunks/{chunk-Q53UMLLC.js → chunk-JKKV5KKS.js} +2 -2
- package/dist/cli/chunks/{chunk-MYF7F3ZP.js → chunk-JMLOEW7Y.js} +2 -2
- package/dist/cli/chunks/chunk-JRIDPOEZ.js +95 -0
- package/dist/cli/chunks/{chunk-T2DIMSQF.js → chunk-K37CNLQZ.js} +1 -1
- package/dist/cli/chunks/{chunk-RNO6CE7I.js → chunk-K6MFAVXK.js} +2 -2
- package/dist/cli/chunks/{chunk-UWWX4RK7.js → chunk-KJAIE7SL.js} +4 -4
- package/dist/cli/chunks/{chunk-ND6VCNN5.js → chunk-KXRDQQGN.js} +2 -2
- package/dist/cli/chunks/{chunk-GRPEDIYG.js → chunk-L4JTTPU7.js} +2 -2
- package/dist/cli/chunks/{chunk-BM73MJLE.js → chunk-L63KB63A.js} +2 -2
- package/dist/cli/chunks/{chunk-R57J3O6I.js → chunk-L7DNPOAQ.js} +2 -2
- package/dist/cli/chunks/{chunk-SJCEPKZO.js → chunk-LF5RO6WO.js} +1 -1
- package/dist/cli/chunks/{chunk-GZ3U2QT2.js → chunk-LRWRFKQH.js} +1 -1
- package/dist/cli/chunks/{chunk-QTS2DS42.js → chunk-M6RVKGIP.js} +2 -2
- package/dist/cli/chunks/{chunk-ECX6VXMW.js → chunk-MAJ5QFVY.js} +1 -1
- package/dist/cli/chunks/chunk-MYCVU3D3.js +14 -0
- package/dist/cli/chunks/{chunk-3AICELMQ.js → chunk-N6P7ENG7.js} +2 -2
- package/dist/cli/chunks/{chunk-7D2DM23U.js → chunk-N6SIKSCO.js} +2 -2
- package/dist/cli/chunks/{chunk-FI4HRS2G.js → chunk-NBKM7NKX.js} +2 -2
- package/dist/cli/chunks/{chunk-QSV4ROZD.js → chunk-NLUBN642.js} +2 -2
- package/dist/cli/chunks/{chunk-FGDEM4HU.js → chunk-NNF3GCGF.js} +2 -2
- package/dist/cli/chunks/{chunk-5DJAJPBG.js → chunk-NTVJKQ5S.js} +2 -2
- package/dist/cli/chunks/{chunk-3WIU2E2Y.js → chunk-NZQYNUGM.js} +2 -2
- package/dist/cli/chunks/{chunk-52TRKIAQ.js → chunk-ONMJJ2C3.js} +1 -1
- package/dist/cli/chunks/{chunk-JNRRDG7O.js → chunk-OXOXNAEZ.js} +2 -2
- package/dist/cli/chunks/chunk-PSF6YQQM.js +180 -0
- package/dist/cli/chunks/{chunk-WP6X67YI.js → chunk-PTOTOBOU.js} +1 -1
- package/dist/cli/chunks/{chunk-UEXOMDRS.js → chunk-PUSZ4NBY.js} +2 -2
- package/dist/cli/chunks/chunk-PYYNY7RJ.js +2 -0
- package/dist/cli/chunks/{chunk-L7X3A36M.js → chunk-QEJBJST4.js} +1 -1
- package/dist/cli/chunks/{chunk-TA3QYAZ6.js → chunk-QGLWFZVQ.js} +2 -2
- package/dist/cli/chunks/{chunk-HB52S6IV.js → chunk-QITO7E7Y.js} +1 -1
- package/dist/cli/chunks/{chunk-KJZXBZQR.js → chunk-QMSDKKYG.js} +1 -1
- package/dist/cli/chunks/{chunk-AQJ6XS34.js → chunk-RBDAHW2M.js} +2 -2
- package/dist/cli/chunks/{chunk-VOBNJWZF.js → chunk-RDJWUKIR.js} +2 -2
- package/dist/cli/chunks/{chunk-EWNJ6OAT.js → chunk-REJEFTWX.js} +1 -1
- package/dist/cli/chunks/{chunk-H27XUYWZ.js → chunk-ROEIR3OD.js} +1 -1
- package/dist/cli/chunks/{chunk-JCWHX4XV.js → chunk-RTGNWRQI.js} +2 -2
- package/dist/cli/chunks/{chunk-FU74OETU.js → chunk-SL4TAJOE.js} +2 -2
- package/dist/cli/chunks/{chunk-3PZDXE5E.js → chunk-T4DDCMKG.js} +113 -113
- package/dist/cli/chunks/{chunk-UROPIIB2.js → chunk-TJGIIGKL.js} +2 -2
- package/dist/cli/chunks/{chunk-BFJLKG3D.js → chunk-TLCCM2AF.js} +1 -1
- package/dist/cli/chunks/{chunk-HJDHQBMJ.js → chunk-TVHWI77X.js} +1 -1
- package/dist/cli/chunks/{chunk-ER6BT4GS.js → chunk-TYUIQSSF.js} +2 -2
- package/dist/cli/chunks/{chunk-R5IW5ARI.js → chunk-UIIEZMSM.js} +1 -1
- package/dist/cli/chunks/{chunk-NMBHF7ZD.js → chunk-UIRTXM7C.js} +15 -15
- package/dist/cli/chunks/{chunk-RYMHYTOK.js → chunk-UNXUSYRL.js} +3 -3
- package/dist/cli/chunks/chunk-VA45HLBF.js +2 -0
- package/dist/cli/chunks/{chunk-DEDFPHJL.js → chunk-VAIOZDG5.js} +1 -1
- package/dist/cli/chunks/{chunk-IEGAEXQX.js → chunk-VLAGWLHF.js} +2 -2
- package/dist/cli/chunks/{chunk-WVODEWH5.js → chunk-VOOJDHLI.js} +14 -8
- package/dist/cli/chunks/{chunk-IOINZWNA.js → chunk-VSKABN3B.js} +29 -17
- package/dist/cli/chunks/{chunk-HQIWLMDS.js → chunk-VUL5HIKR.js} +2 -2
- package/dist/cli/chunks/{chunk-OOFWYDG6.js → chunk-W7XVEWKQ.js} +1 -1
- package/dist/cli/chunks/{chunk-Q4HJRYQB.js → chunk-WB6TI6Q3.js} +2 -2
- package/dist/cli/chunks/{chunk-DJRWVNHK.js → chunk-X2FLWV5C.js} +2 -2
- package/dist/cli/chunks/{chunk-QLNS6DGB.js → chunk-XE4YKDIM.js} +2 -2
- package/dist/cli/chunks/{chunk-6EP3GHED.js → chunk-XGBIXRKD.js} +2 -2
- package/dist/cli/chunks/{chunk-AUNNGKLN.js → chunk-XMMWYQJK.js} +2 -2
- package/dist/cli/chunks/{chunk-FGMHVSLV.js → chunk-XNJ6DIEW.js} +2 -2
- package/dist/cli/chunks/{chunk-4UZQSPR4.js → chunk-XTWYCMAM.js} +2 -2
- package/dist/cli/chunks/{chunk-2NFGUFYU.js → chunk-XY3WUGD3.js} +2 -2
- package/dist/cli/chunks/{chunk-VRP4GB4Y.js → chunk-YEZJE2ZW.js} +4 -4
- package/dist/cli/chunks/{chunk-MCXRS2TZ.js → chunk-YLEMSN46.js} +1 -1
- package/dist/cli/chunks/{chunk-Q26GG6WO.js → chunk-YMN4C32S.js} +3 -3
- package/dist/cli/chunks/{chunk-HA7N45KB.js → chunk-YN7HCVUP.js} +2 -2
- package/dist/cli/chunks/{chunk-E7UHOKKL.js → chunk-YQJBE6NX.js} +2 -2
- package/dist/cli/chunks/{chunk-OTFJCZNY.js → chunk-ZBJRNCWX.js} +2 -2
- package/dist/cli/chunks/{chunk-QHKK2H4H.js → chunk-ZG4EKPGV.js} +1 -1
- package/dist/cli/chunks/{chunk-USN2JKUW.js → chunk-ZWSRIJ2T.js} +2 -2
- package/dist/cli/chunks/{ci-BYCH3NPL.js → ci-LRTJSZHT.js} +2 -2
- package/dist/cli/chunks/{ci-output-P7P4XH6F.js → ci-output-LISCHUKD.js} +2 -2
- package/dist/cli/chunks/{circuit-breaker-VLX556ZI.js → circuit-breaker-7PMP25KZ.js} +2 -2
- package/dist/cli/chunks/{claude-flow-setup-JPR425PL.js → claude-flow-setup-DOW4QVMC.js} +2 -2
- package/dist/cli/chunks/client-XF6SJO2C.js +2 -0
- package/dist/cli/chunks/{cline-installer-FOY47NSH.js → cline-installer-2NXQGW73.js} +2 -2
- package/dist/cli/chunks/{code-57SN4ZDY.js → code-3X6RWAS7.js} +2 -2
- package/dist/cli/chunks/{code-index-extractor-GT7UFRYU.js → code-index-extractor-4ABP5WCP.js} +2 -2
- package/dist/cli/chunks/{codex-installer-JAGWONAV.js → codex-installer-V7PII6GL.js} +2 -2
- package/dist/cli/chunks/{completions-7YLHPGSV.js → completions-Y7LNQ63I.js} +2 -2
- package/dist/cli/chunks/{complexity-analyzer-TQUF6BEI.js → complexity-analyzer-XLYMAM6I.js} +2 -2
- package/dist/cli/chunks/{continuedev-installer-TICV6IGT.js → continuedev-installer-GOT4TKNT.js} +2 -2
- package/dist/cli/chunks/{copilot-installer-A6PDMI52.js → copilot-installer-D6BAQVIO.js} +2 -2
- package/dist/cli/chunks/{cost-tracker-NZPNHNV2.js → cost-tracker-VT7C5Q52.js} +2 -2
- package/dist/cli/chunks/{coverage-HBEB2LKS.js → coverage-C62MRP4M.js} +3 -3
- package/dist/cli/chunks/cross-domain-router-J2OFPM6R.js +2 -0
- package/dist/cli/chunks/{cursor-installer-R4FM7MGN.js → cursor-installer-WAWSS2K2.js} +2 -2
- package/dist/cli/chunks/{daemon-PHIZPZIE.js → daemon-CP2ETHRF.js} +3 -3
- package/dist/cli/chunks/{dag-attention-scheduler-2L7VCYZ3.js → dag-attention-scheduler-SZIZWPV7.js} +2 -2
- package/dist/cli/chunks/{detect-EEWB4IYE.js → detect-AJX6NJEP.js} +2 -2
- package/dist/cli/chunks/{dist-node-7RM6BB2X.js → dist-node-E5X47QTY.js} +2 -2
- package/dist/cli/chunks/{domain-handler-RVGHBDUJ.js → domain-handler-FN3PIP45.js} +2 -2
- package/dist/cli/chunks/{domain-transfer-M5MERQG5.js → domain-transfer-N3TOJEMT.js} +2 -2
- package/dist/cli/chunks/dream-D5LD5SOZ.js +2 -0
- package/dist/cli/chunks/{embed-and-insert-pattern-MS55QS73.js → embed-and-insert-pattern-PJI5TZKR.js} +2 -2
- package/dist/cli/chunks/{eval-MBTMI7KN.js → eval-OS54TDQU.js} +2 -2
- package/dist/cli/chunks/{experience-capture-middleware-Z5XB7D6A.js → experience-capture-middleware-FBM4ANZW.js} +3 -3
- package/dist/cli/chunks/{fast-paths-FSWLHI4I.js → fast-paths-2CVAAPQO.js} +2 -2
- package/dist/cli/chunks/{feature-flags-QUPNH2J5.js → feature-flags-A5KFWVFC.js} +2 -2
- package/dist/cli/chunks/{feature-flags-ATYOPR5D.js → feature-flags-GEXTW6OT.js} +2 -2
- package/dist/cli/chunks/{file-discovery-SOJJEQ2E.js → file-discovery-KWLVC7KN.js} +2 -2
- package/dist/cli/chunks/{fleet-PWBTDWVG.js → fleet-MNKH356S.js} +3 -3
- package/dist/cli/chunks/{gnn-wrapper-2N67VQOQ.js → gnn-wrapper-ALTWYFVZ.js} +2 -2
- package/dist/cli/chunks/{heartbeat-handler-5PSG2UPO.js → heartbeat-handler-MBMEPEGW.js} +4 -4
- package/dist/cli/chunks/{heartbeat-scheduler-CNJBAO5C.js → heartbeat-scheduler-OH3SS7MH.js} +2 -2
- package/dist/cli/chunks/hnsw-adapter-M2MMM3N6.js +2 -0
- package/dist/cli/chunks/hnsw-index-JO7KNF7M.js +2 -0
- package/dist/cli/chunks/{hnsw-legacy-bridge-YIXH5T4U.js → hnsw-legacy-bridge-WTD5PR5V.js} +2 -2
- package/dist/cli/chunks/{better-sqlite3-3U2AVWQ3.js → hnswlib-node-WENGW3YX.js} +2 -2
- package/dist/cli/chunks/hooks-ELHGEBFK.js +248 -0
- package/dist/cli/chunks/{hybrid-router-RL47S47Z.js → hybrid-router-D6ZMIZCE.js} +2 -2
- package/dist/cli/chunks/{hypergraph-engine-XW2IHFWW.js → hypergraph-engine-JOQ6TERZ.js} +2 -2
- package/dist/cli/chunks/{hypergraph-handler-AQXKY4UF.js → hypergraph-handler-HX45YWWN.js} +3 -3
- package/dist/cli/chunks/impact-analyzer-ROQL4J7P.js +2 -0
- package/dist/cli/chunks/{init-handler-KXYGJQI5.js → init-handler-TNVU6NQ6.js} +6 -6
- package/dist/cli/chunks/init-wizard-4BDFZX4M.js +2 -0
- package/dist/cli/chunks/kernel-37Y63WKR.js +2 -0
- package/dist/cli/chunks/{kilocode-installer-VSULDGRO.js → kilocode-installer-YC5RJIY4.js} +2 -2
- package/dist/cli/chunks/{kiro-installer-SH4D2UXW.js → kiro-installer-EAB26M55.js} +2 -2
- package/dist/cli/chunks/knowledge-graph-V3DX6ZCX.js +2 -0
- package/dist/cli/chunks/learning-A2OB7D2B.js +117 -0
- package/dist/cli/chunks/{llm-router-YXXSQX5K.js → llm-router-KEG6PA7C.js} +4 -4
- package/dist/cli/chunks/{load-S52R2SLL.js → load-D6ZDLVA3.js} +2 -2
- package/dist/cli/chunks/load-test-HPBA2CMT.js +2 -0
- package/dist/cli/chunks/{mcp-ZWMAJIU7.js → mcp-ERJHZ6FN.js} +2 -2
- package/dist/cli/chunks/{memory-4YTWWDK7.js → memory-GSRIJIVR.js} +5 -5
- package/dist/cli/chunks/memory-backend-DODDBB46.js +2 -0
- package/dist/cli/chunks/memory-handlers-MFLCRS7V.js +2 -0
- package/dist/cli/chunks/{multi-model-executor-F3VEM2UF.js → multi-model-executor-GK3V7ERC.js} +2 -2
- package/dist/cli/chunks/{opencode-installer-Z2CUJQ6D.js → opencode-installer-6TTN26RG.js} +2 -2
- package/dist/cli/chunks/{orchestrator-UPXR2XOF.js → orchestrator-YVTSM6QW.js} +37 -18
- package/dist/cli/chunks/{pipeline-6MEMAIXM.js → pipeline-EWLJCY5G.js} +2 -2
- package/dist/cli/chunks/{platform-H72FLOM7.js → platform-2ULHQQME.js} +2 -2
- package/dist/cli/chunks/{plugin-GIMQFDVR.js → plugin-PYN2KOXA.js} +2 -2
- package/dist/cli/chunks/{prime-radiant-advanced-wasm-DM5FR5SV.js → prime-radiant-advanced-wasm-IULCTOGZ.js} +2 -2
- package/dist/cli/chunks/protocol-executor-D56OYMCJ.js +2 -0
- package/dist/cli/chunks/{protocol-handler-YW3N333B.js → protocol-handler-FZYI2SBP.js} +2 -2
- package/dist/cli/chunks/{prove-IJY524KK.js → prove-DN7S74SP.js} +2 -2
- package/dist/cli/chunks/{provider-manager-FNG6YR2V.js → provider-manager-FT3MCROB.js} +2 -2
- package/dist/cli/chunks/qe-reasoning-bank-KXROIEK7.js +2 -0
- package/dist/cli/chunks/{quality-EZLAMSCP.js → quality-ITBU6LGC.js} +2 -2
- package/dist/cli/chunks/queen-coordinator-XOGATERL.js +2 -0
- package/dist/cli/chunks/{real-embeddings-MFRCC6GH.js → real-embeddings-PZUZ5RXJ.js} +2 -2
- package/dist/cli/chunks/{roocode-installer-H3ZIRGWU.js → roocode-installer-2LMVZOUZ.js} +2 -2
- package/dist/cli/chunks/router-3EHNUCOM.js +2 -0
- package/dist/cli/chunks/routing-feedback-ZHKGET22.js +2 -0
- package/dist/cli/chunks/{routing-handler-T2A7FYHW.js → routing-handler-5RB73UCF.js} +2 -2
- package/dist/cli/chunks/{ruvector-commands-TCLWBP2M.js → ruvector-commands-VBN4APMG.js} +2 -2
- package/dist/cli/chunks/{rvf-dual-writer-BE3JCPYC.js → rvf-dual-writer-BNY4AUWT.js} +2 -2
- package/dist/cli/chunks/{rvf-migration-adapter-MLUX5YGR.js → rvf-migration-adapter-73MKPBGV.js} +2 -2
- package/dist/cli/chunks/{rvf-migration-coordinator-OAQXTASZ.js → rvf-migration-coordinator-SGVB7ZAZ.js} +2 -2
- package/dist/cli/chunks/rvf-native-adapter-4YC5IQUW.js +2 -0
- package/dist/cli/chunks/safe-db-PLJRHQIC.js +2 -0
- package/dist/cli/chunks/schedule-7DGELVJE.js +2 -0
- package/dist/cli/chunks/scheduler-VWASEC2J.js +2 -0
- package/dist/cli/chunks/{security-SHWJWJ4M.js → security-XUH4H7R3.js} +3 -3
- package/dist/cli/chunks/shared-rvf-adapter-DCVDY2XW.js +2 -0
- package/dist/cli/chunks/{shared-rvf-dual-writer-BKSR2FFK.js → shared-rvf-dual-writer-GML4EDYF.js} +2 -2
- package/dist/cli/chunks/sqlite-persistence-CTEBGVOW.js +2 -0
- package/dist/cli/chunks/{status-handler-QK5KMKYC.js → status-handler-XI7GJF6Z.js} +2 -2
- package/dist/cli/chunks/{structural-health-3BRNCAYQ.js → structural-health-WPCYKOXV.js} +2 -2
- package/dist/cli/chunks/{sync-5CDYOT3H.js → sync-SFKTCRZC.js} +2 -2
- package/dist/cli/chunks/{task-handler-VH2CLUIA.js → task-handler-FZB55IEG.js} +2 -2
- package/dist/cli/chunks/{task-handlers-BUZNV2VX.js → task-handlers-EXZGFH7F.js} +3 -3
- package/dist/cli/chunks/{test-K24JQQZ2.js → test-XYRN4OVU.js} +4 -4
- package/dist/cli/chunks/{test-scheduling-MHXRV5VI.js → test-scheduling-FLVOPGOT.js} +3 -3
- package/dist/cli/chunks/{token-bootstrap-3NZDLG52.js → token-bootstrap-FDCFVRHM.js} +2 -2
- package/dist/cli/chunks/{token-usage-C4BGA2O7.js → token-usage-MHNZF3DM.js} +2 -2
- package/dist/cli/chunks/{transformers-ZIIFB2V4.js → transformers-4CRVTMWY.js} +2 -2
- package/dist/cli/chunks/{tree-sitter-wasm-parser-GES2AV7A.js → tree-sitter-wasm-parser-CI3V4AND.js} +2 -2
- package/dist/cli/chunks/{types-L3MO5VNC.js → types-RKCD4BNL.js} +2 -2
- package/dist/cli/chunks/unified-memory-YDKXKW3D.js +2 -0
- package/dist/cli/chunks/unified-memory-hnsw-6N7YZCLR.js +2 -0
- package/dist/cli/chunks/unified-persistence-UTWLPUQI.js +2 -0
- package/dist/cli/chunks/{upgrade-EKJYS5S5.js → upgrade-463W7VKH.js} +2 -2
- package/dist/cli/chunks/{validate-WYWWB5PQ.js → validate-LENSMEAY.js} +2 -2
- package/dist/cli/chunks/{validate-swarm-SBSWKJ3H.js → validate-swarm-4FEBNAWA.js} +2 -2
- package/dist/cli/chunks/{vibium-VEMTLNFV.js → vibium-TXNVIVWJ.js} +2 -2
- package/dist/cli/chunks/visual-security-DGXSOFKD.js +2 -0
- package/dist/cli/chunks/{web-tree-sitter-STW2WR2J.js → web-tree-sitter-BZEGWID4.js} +2 -2
- package/dist/cli/chunks/{windsurf-installer-6ZXMJASZ.js → windsurf-installer-7AFXJTPU.js} +2 -2
- package/dist/cli/chunks/{witness-chain-PTULB4MR.js → witness-chain-G6SUZOZG.js} +2 -2
- package/dist/cli/chunks/witness-chain-VP4MF6EU.js +2 -0
- package/dist/cli/chunks/{workflow-TEBAAHNR.js → workflow-EVQPO6FH.js} +4 -4
- package/dist/cli/chunks/workflow-orchestrator-VXSYAKQY.js +2 -0
- package/dist/cli/chunks/{wrappers-DVMVRKXK.js → wrappers-HR6RUDI2.js} +2 -2
- package/dist/cli/commands/hooks-handlers/editing-hooks.js +9 -3
- package/dist/cli/commands/hooks-handlers/hooks-dream-learning.js +16 -24
- package/dist/cli/commands/hooks-handlers/hooks-shared.d.ts +1 -1
- package/dist/cli/commands/hooks-handlers/hooks-shared.js +8 -1
- package/dist/cli/commands/hooks-handlers/routing-hooks.js +15 -3
- package/dist/cli/commands/hooks-handlers/task-hooks.js +37 -46
- package/dist/cli/commands/learning.js +194 -0
- package/dist/init/phases/10-workers.js +20 -1
- package/dist/kernel/interfaces.d.ts +12 -0
- package/dist/kernel/kernel.d.ts +1 -0
- package/dist/kernel/kernel.js +57 -0
- package/dist/kernel/unified-memory-schemas.d.ts +1 -1
- package/dist/kernel/unified-memory-schemas.js +7 -1
- package/dist/learning/agent-routing.d.ts +134 -1
- package/dist/learning/agent-routing.js +185 -2
- package/dist/learning/dream/dream-insights-pruner.d.ts +49 -0
- package/dist/learning/dream/dream-insights-pruner.js +53 -0
- package/dist/learning/dream/dream-scheduler.js +17 -0
- package/dist/learning/loop-health.d.ts +84 -0
- package/dist/learning/loop-health.js +91 -0
- package/dist/learning/pattern-usage-recorder.d.ts +58 -0
- package/dist/learning/pattern-usage-recorder.js +72 -0
- package/dist/learning/qe-reasoning-bank-types.d.ts +11 -0
- package/dist/learning/qe-reasoning-bank.d.ts +11 -0
- package/dist/learning/qe-reasoning-bank.js +86 -3
- package/dist/learning/routing-topology-gate.d.ts +40 -0
- package/dist/learning/routing-topology-gate.js +55 -0
- package/dist/learning/sqlite-persistence.d.ts +6 -1
- package/dist/learning/sqlite-persistence.js +14 -20
- package/dist/mcp/bundle.js +3831 -3805
- package/dist/routing/routing-feedback.js +7 -3
- package/dist/routing/routing-outcomes-migration.d.ts +31 -0
- package/dist/routing/routing-outcomes-migration.js +60 -0
- package/dist/workers/workers/learning-consolidation.d.ts +28 -0
- package/dist/workers/workers/learning-consolidation.js +204 -0
- package/package.json +1 -1
- package/dist/cli/chunks/adapter-HV42JOZD.js +0 -2
- package/dist/cli/chunks/aqe-learning-engine-GL22PE2V.js +0 -2
- package/dist/cli/chunks/base-4KQ2FGUX.js +0 -2
- package/dist/cli/chunks/browser-workflow-CTE7BDM4.js +0 -2
- package/dist/cli/chunks/chunk-3NGNSKL3.js +0 -14
- package/dist/cli/chunks/chunk-6HSFZ6SL.js +0 -180
- package/dist/cli/chunks/chunk-7Z3GBQNV.js +0 -2
- package/dist/cli/chunks/chunk-H2IMXQCJ.js +0 -2
- package/dist/cli/chunks/chunk-SPCANEJY.js +0 -95
- package/dist/cli/chunks/client-FRVNMXQO.js +0 -2
- package/dist/cli/chunks/cross-domain-router-BVCPAWG2.js +0 -2
- package/dist/cli/chunks/dream-77ODIFIF.js +0 -2
- package/dist/cli/chunks/hnsw-adapter-CQGQS3V7.js +0 -2
- package/dist/cli/chunks/hnsw-index-2ACF6FOJ.js +0 -2
- package/dist/cli/chunks/hooks-YROFO6PE.js +0 -259
- package/dist/cli/chunks/impact-analyzer-LWEGK23B.js +0 -2
- package/dist/cli/chunks/init-wizard-7BS3QMWR.js +0 -2
- package/dist/cli/chunks/kernel-TX67WXSI.js +0 -2
- package/dist/cli/chunks/knowledge-graph-TDSP2UE2.js +0 -2
- package/dist/cli/chunks/learning-RRWV3SEL.js +0 -107
- package/dist/cli/chunks/load-test-GEBBBUMV.js +0 -2
- package/dist/cli/chunks/memory-backend-WQS2MLW2.js +0 -2
- package/dist/cli/chunks/memory-handlers-RTY5MBA5.js +0 -2
- package/dist/cli/chunks/protocol-executor-DT7XHMLL.js +0 -2
- package/dist/cli/chunks/qe-reasoning-bank-LDNETZVA.js +0 -2
- package/dist/cli/chunks/queen-coordinator-AF7HCQSM.js +0 -2
- package/dist/cli/chunks/router-OWQ5EI72.js +0 -2
- package/dist/cli/chunks/routing-feedback-B43DEQMK.js +0 -2
- package/dist/cli/chunks/rvf-native-adapter-XBJDXHNI.js +0 -2
- package/dist/cli/chunks/safe-db-RT3LEDUG.js +0 -2
- package/dist/cli/chunks/schedule-EHUDCKS2.js +0 -2
- package/dist/cli/chunks/scheduler-GEGZ4J3C.js +0 -2
- package/dist/cli/chunks/shared-rvf-adapter-LNBUNRAM.js +0 -2
- package/dist/cli/chunks/sqlite-persistence-3DGRJH3K.js +0 -2
- package/dist/cli/chunks/unified-memory-ZSBX4LYU.js +0 -2
- package/dist/cli/chunks/unified-memory-hnsw-Y6EKAMRP.js +0 -2
- package/dist/cli/chunks/unified-persistence-2PDVU2U5.js +0 -2
- package/dist/cli/chunks/visual-security-RHMFLKVQ.js +0 -2
- package/dist/cli/chunks/witness-chain-QO237QOF.js +0 -2
- package/dist/cli/chunks/workflow-orchestrator-RHM5MIGE.js +0 -2
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agentic QE v3 - Agent Routing
|
|
3
3
|
* ADR-021: QE ReasoningBank for Pattern Learning
|
|
4
|
+
* ADR-095: ε-greedy exploration with Q-value blending and mincut safety gate
|
|
4
5
|
*
|
|
5
6
|
* Static agent capability mapping and routing score calculation
|
|
6
7
|
* used by QEReasoningBank.routeTask().
|
|
7
8
|
*/
|
|
9
|
+
import { randomInt } from 'crypto';
|
|
8
10
|
// ============================================================================
|
|
9
11
|
// Agent Capabilities Map
|
|
10
12
|
// ============================================================================
|
|
@@ -72,6 +74,159 @@ export const AGENT_CAPABILITIES = {
|
|
|
72
74
|
performanceScore: 0.75,
|
|
73
75
|
},
|
|
74
76
|
};
|
|
77
|
+
// ============================================================================
|
|
78
|
+
// ADR-095: Q-Value Blending + ε-Greedy Exploration
|
|
79
|
+
// ============================================================================
|
|
80
|
+
/**
|
|
81
|
+
* Maximum Q-value influence on the final score.
|
|
82
|
+
*
|
|
83
|
+
* 0.4 means a fully-mature Q-value can move the score by up to ±0.2 (since
|
|
84
|
+
* normalizedQ ranges over (0, 1) and the static contribution clamps the
|
|
85
|
+
* upper bound). This is intentionally bounded so Q-values inform rather
|
|
86
|
+
* than override the static features.
|
|
87
|
+
*
|
|
88
|
+
* Tuning: post-deploy telemetry will tell us whether agents are actually
|
|
89
|
+
* being separated by Q-values. If the avg `q_weight` in routing_outcomes
|
|
90
|
+
* climbs but quality_score doesn't improve, this constant is too high.
|
|
91
|
+
*/
|
|
92
|
+
export const MAX_Q_WEIGHT = 0.4;
|
|
93
|
+
/**
|
|
94
|
+
* Number of (state_key, action_key) visits at which qWeight saturates at
|
|
95
|
+
* MAX_Q_WEIGHT. Below this, qWeight ramps linearly from 0.
|
|
96
|
+
*
|
|
97
|
+
* 20 visits at the default 30-min worker tick + typical session cadence
|
|
98
|
+
* implies ~1 week of activity before a (state, agent) pair drives the
|
|
99
|
+
* decision. Adjust downward if cold-start latency proves problematic.
|
|
100
|
+
*/
|
|
101
|
+
export const QWEIGHT_RAMP_VISITS = 20;
|
|
102
|
+
/**
|
|
103
|
+
* Sigmoid mapping R → (0, 1) for Q-value normalization before blending.
|
|
104
|
+
* A fresh row with q_value = 0 contributes 0.5 (neutral); negative q_values
|
|
105
|
+
* (after a failure, given the asymmetric -1.0 penalty from ADR-061) push
|
|
106
|
+
* the contribution toward 0, positive q_values toward 1.
|
|
107
|
+
*/
|
|
108
|
+
function sigmoid(x) {
|
|
109
|
+
return 1 / (1 + Math.exp(-x));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Blend a static score with a Q-value contribution.
|
|
113
|
+
*
|
|
114
|
+
* effectiveScore = staticScore * (1 - qWeight) + normalizedQ * qWeight
|
|
115
|
+
*
|
|
116
|
+
* Returns { score, qWeight, qValue, qVisits } so callers can attach the
|
|
117
|
+
* telemetry fields to the ScoredAgent record.
|
|
118
|
+
*/
|
|
119
|
+
export function blendStaticAndQValue(staticScore, qLookup) {
|
|
120
|
+
if (!qLookup || qLookup.visits === 0) {
|
|
121
|
+
return { score: staticScore, qWeight: 0, qValue: 0, qVisits: 0 };
|
|
122
|
+
}
|
|
123
|
+
const qWeight = Math.min(qLookup.visits / QWEIGHT_RAMP_VISITS, 1) * MAX_Q_WEIGHT;
|
|
124
|
+
const normalizedQ = sigmoid(qLookup.qValue);
|
|
125
|
+
return {
|
|
126
|
+
score: staticScore * (1 - qWeight) + normalizedQ * qWeight,
|
|
127
|
+
qWeight,
|
|
128
|
+
qValue: qLookup.qValue,
|
|
129
|
+
qVisits: qLookup.visits,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Resolve the per-decision ε for the exploration policy.
|
|
134
|
+
*
|
|
135
|
+
* Priority order:
|
|
136
|
+
* 1. AQE_ROUTER_EXPLORATION_RATE env var (operator override, fixed)
|
|
137
|
+
* 2. Default base rate (0.05)
|
|
138
|
+
* The result is then multiplied by the mincut safety gate (caller passes
|
|
139
|
+
* `topologyCritical` derived from getSharedMinCutMonitor().isCritical()):
|
|
140
|
+
* critical topology → 0.2x dampening, healthy/unknown → 1.0x.
|
|
141
|
+
*
|
|
142
|
+
* Returns a number in [0, 1].
|
|
143
|
+
*/
|
|
144
|
+
export function resolveExplorationRate(opts) {
|
|
145
|
+
const envValue = opts.envOverride;
|
|
146
|
+
const parsed = envValue !== undefined ? Number.parseFloat(envValue) : NaN;
|
|
147
|
+
const baseEpsilon = Number.isFinite(parsed) && parsed >= 0 && parsed <= 1
|
|
148
|
+
? parsed
|
|
149
|
+
: 0.05;
|
|
150
|
+
const safetyMultiplier = opts.topologyCritical ? 0.2 : 1.0;
|
|
151
|
+
const epsilon = Math.min(Math.max(baseEpsilon * safetyMultiplier, 0), 1);
|
|
152
|
+
return { epsilon, baseEpsilon, safetyMultiplier };
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Apply the ε-greedy exploration policy to a sorted score list.
|
|
156
|
+
*
|
|
157
|
+
* With probability ε (computed from resolveExplorationRate), swaps the
|
|
158
|
+
* top-scored agent with a uniformly-random pick from positions 1..3 (or
|
|
159
|
+
* fewer if the list is shorter). Mutates `agentScores` in place. The
|
|
160
|
+
* promoted agent gets `exploration = true`.
|
|
161
|
+
*
|
|
162
|
+
* Uses `crypto.randomInt` for cryptographically-strong uniform sampling —
|
|
163
|
+
* matches the codebase convention (randomUUID is imported from 'crypto'
|
|
164
|
+
* in the same modules). Math.random() would work but breaks consistency.
|
|
165
|
+
*/
|
|
166
|
+
export function applyExplorationPolicy(agentScores, epsilon) {
|
|
167
|
+
if (epsilon <= 0 || agentScores.length < 2)
|
|
168
|
+
return;
|
|
169
|
+
// Compare in micro-units so randomInt can do uniform sampling without
|
|
170
|
+
// float arithmetic. ε = 0.05 → 50_000 micro-units in 1_000_000.
|
|
171
|
+
const epsilonMicro = Math.round(epsilon * 1_000_000);
|
|
172
|
+
if (epsilonMicro <= 0)
|
|
173
|
+
return;
|
|
174
|
+
if (randomInt(0, 1_000_000) >= epsilonMicro)
|
|
175
|
+
return;
|
|
176
|
+
// Pick from top alternatives (positions 1..min(3, length-1)).
|
|
177
|
+
const ceiling = Math.min(4, agentScores.length);
|
|
178
|
+
const exploreIdx = randomInt(1, ceiling);
|
|
179
|
+
const explored = agentScores[exploreIdx];
|
|
180
|
+
agentScores[exploreIdx] = agentScores[0];
|
|
181
|
+
agentScores[0] = explored;
|
|
182
|
+
agentScores[0].exploration = true;
|
|
183
|
+
agentScores[0].reasoning = [...agentScores[0].reasoning, '(exploration)'];
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Derive a structural taskType from a free-form task description. Used to
|
|
187
|
+
* build the q-learning state_key shared with the post-task Bellman update
|
|
188
|
+
* (hooks-dream-learning.ts).
|
|
189
|
+
*
|
|
190
|
+
* Exported (rather than kept private in task-hooks) so QEReasoningBank can
|
|
191
|
+
* compute the same state_key at routing time as post-task does at outcome
|
|
192
|
+
* time — without that, the Q-table writer and reader would address
|
|
193
|
+
* different keys.
|
|
194
|
+
*/
|
|
195
|
+
export function deriveTaskType(description) {
|
|
196
|
+
const d = description.toLowerCase();
|
|
197
|
+
if (/\bgenerate[- ]?test|\btest[- ]?gen|\bgenerate.+spec/.test(d))
|
|
198
|
+
return 'test-generation';
|
|
199
|
+
if (/\bcoverage|\banalyze.+cover/.test(d))
|
|
200
|
+
return 'coverage-analysis';
|
|
201
|
+
if (/\bquality|\bassess|\baudit/.test(d))
|
|
202
|
+
return 'quality-assessment';
|
|
203
|
+
if (/\bsecurity|\bvulnerab|\bcompliance/.test(d))
|
|
204
|
+
return 'security-compliance';
|
|
205
|
+
if (/\bdefect|\bbug|\bdiagnos/.test(d))
|
|
206
|
+
return 'defect-intelligence';
|
|
207
|
+
if (/\brequirement|\bspec\b/.test(d))
|
|
208
|
+
return 'requirements-validation';
|
|
209
|
+
if (/\brefactor|\brewrite|\boptim/.test(d))
|
|
210
|
+
return 'refactoring';
|
|
211
|
+
if (/\btest|\brun.+test/.test(d))
|
|
212
|
+
return 'test-execution';
|
|
213
|
+
return 'unknown';
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Build the canonical Q-learning state_key. Must match the format used by
|
|
217
|
+
* `updateHookRouterQValue` in hooks-dream-learning.ts — the writer and
|
|
218
|
+
* reader must agree on the key shape.
|
|
219
|
+
*/
|
|
220
|
+
export function buildRoutingStateKey(opts) {
|
|
221
|
+
return `${opts.taskType}|${opts.priority ?? 'normal'}|${opts.domain ?? 'any'}|${opts.complexityBucket}`;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Compute the complexity bucket [0, 10] from a task description length.
|
|
225
|
+
* Mirrors the formula in task-hooks.ts pre-task bridge.
|
|
226
|
+
*/
|
|
227
|
+
export function deriveComplexityBucket(description) {
|
|
228
|
+
return Math.max(0, Math.min(10, Math.round(Math.min(description.length / 200, 1) * 10)));
|
|
229
|
+
}
|
|
75
230
|
/**
|
|
76
231
|
* Calculate agent scores for a routing request.
|
|
77
232
|
*
|
|
@@ -83,7 +238,14 @@ export const AGENT_CAPABILITIES = {
|
|
|
83
238
|
* @param requestLanguage - Optional language to boost agents with matching language expertise
|
|
84
239
|
* @returns Sorted array of scored agents (highest score first)
|
|
85
240
|
*/
|
|
86
|
-
export function calculateAgentScores(detectedDomains, requestCapabilities, agentDomainPatternCounts, routingWeights, agentCapabilities = AGENT_CAPABILITIES, requestLanguage
|
|
241
|
+
export function calculateAgentScores(detectedDomains, requestCapabilities, agentDomainPatternCounts, routingWeights, agentCapabilities = AGENT_CAPABILITIES, requestLanguage,
|
|
242
|
+
/**
|
|
243
|
+
* ADR-095: optional per-agent Q-value lookup. When provided, the static
|
|
244
|
+
* score is blended with the Q-value contribution via blendStaticAndQValue.
|
|
245
|
+
* When omitted (or returning undefined for every agent), behavior is
|
|
246
|
+
* identical to the pre-ADR-095 deterministic scoring.
|
|
247
|
+
*/
|
|
248
|
+
qValueLookup) {
|
|
87
249
|
const agentScores = [];
|
|
88
250
|
for (const [agentType, profile] of Object.entries(agentCapabilities)) {
|
|
89
251
|
let score = 0;
|
|
@@ -128,7 +290,28 @@ export function calculateAgentScores(detectedDomains, requestCapabilities, agent
|
|
|
128
290
|
score += patternBoost;
|
|
129
291
|
reasoning.push(`Pattern matches: ${patternCount}`);
|
|
130
292
|
}
|
|
131
|
-
|
|
293
|
+
// ADR-095: blend Q-value if a lookup is provided
|
|
294
|
+
const staticScore = score;
|
|
295
|
+
if (qValueLookup) {
|
|
296
|
+
const q = qValueLookup(agentType);
|
|
297
|
+
const blended = blendStaticAndQValue(staticScore, q);
|
|
298
|
+
score = blended.score;
|
|
299
|
+
if (blended.qWeight > 0) {
|
|
300
|
+
reasoning.push(`Q-bonus (w=${blended.qWeight.toFixed(2)}, v=${blended.qVisits})`);
|
|
301
|
+
}
|
|
302
|
+
agentScores.push({
|
|
303
|
+
agent: agentType,
|
|
304
|
+
score,
|
|
305
|
+
reasoning,
|
|
306
|
+
staticScore,
|
|
307
|
+
qWeight: blended.qWeight,
|
|
308
|
+
qValue: blended.qValue,
|
|
309
|
+
qVisits: blended.qVisits,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
agentScores.push({ agent: agentType, score, reasoning, staticScore });
|
|
314
|
+
}
|
|
132
315
|
}
|
|
133
316
|
// Sort by score descending
|
|
134
317
|
agentScores.sort((a, b) => b.score - a.score);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dream Insights Pruner (#488 C.2)
|
|
3
|
+
*
|
|
4
|
+
* Periodic retention sweep for the `dream_insights` table.
|
|
5
|
+
*
|
|
6
|
+
* dream_insights accumulate forever in default deployments — the schema has
|
|
7
|
+
* `created_at` but no `expires_at`, no foreign-key cascade that fires on
|
|
8
|
+
* regular cycle delete (cycles aren't routinely purged either), and no
|
|
9
|
+
* equivalent of `pattern-lifecycle.ts`'s decay/deprecation pass.
|
|
10
|
+
*
|
|
11
|
+
* Observed in state-rich shops (per #488 reporter): 120+ rows accumulated
|
|
12
|
+
* over ~30h of activity, with most at `applied = 0`. Without pruning, this
|
|
13
|
+
* grows linearly with hook activity. Over months the table becomes a slow
|
|
14
|
+
* `getPendingInsights` target and vector embeddings (if attached) grow
|
|
15
|
+
* unbounded.
|
|
16
|
+
*
|
|
17
|
+
* Strategy: delete only insights that:
|
|
18
|
+
* - have never been applied (`applied = 0`), AND
|
|
19
|
+
* - are older than the retention window (default 30 days)
|
|
20
|
+
*
|
|
21
|
+
* Applied insights stay forever — they're part of the audit trail for
|
|
22
|
+
* pattern changes. Recently-created insights stay regardless of applied
|
|
23
|
+
* state — they might still be picked up by a future `applyInsight` pass.
|
|
24
|
+
*
|
|
25
|
+
* Called from `LearningConsolidationWorker.runContinuousLearningLoop` on
|
|
26
|
+
* the 30-min worker tick. Failures are non-fatal — the worker continues
|
|
27
|
+
* with the rest of its lifecycle work.
|
|
28
|
+
*/
|
|
29
|
+
import type { Database as DatabaseType } from 'better-sqlite3';
|
|
30
|
+
export interface PruneStaleDreamInsightsOptions {
|
|
31
|
+
/**
|
|
32
|
+
* How many days old an `applied = 0` insight must be before it's
|
|
33
|
+
* eligible for deletion. Defaults to 30 days — matches the
|
|
34
|
+
* `staleDaysThreshold` used by `pattern-lifecycle.ts` for consistency.
|
|
35
|
+
*/
|
|
36
|
+
retentionDays?: number;
|
|
37
|
+
}
|
|
38
|
+
export interface PruneResult {
|
|
39
|
+
/** Number of rows deleted. Zero if the table is empty or no rows qualify. */
|
|
40
|
+
pruned: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Delete stale unapplied insights. Idempotent — safe to call on every tick.
|
|
44
|
+
*
|
|
45
|
+
* Returns `{ pruned: 0 }` if the table doesn't exist (init hasn't created
|
|
46
|
+
* the dream schema yet) or if no rows qualify.
|
|
47
|
+
*/
|
|
48
|
+
export declare function pruneStaleDreamInsights(db: DatabaseType, options?: PruneStaleDreamInsightsOptions): PruneResult;
|
|
49
|
+
//# sourceMappingURL=dream-insights-pruner.d.ts.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dream Insights Pruner (#488 C.2)
|
|
3
|
+
*
|
|
4
|
+
* Periodic retention sweep for the `dream_insights` table.
|
|
5
|
+
*
|
|
6
|
+
* dream_insights accumulate forever in default deployments — the schema has
|
|
7
|
+
* `created_at` but no `expires_at`, no foreign-key cascade that fires on
|
|
8
|
+
* regular cycle delete (cycles aren't routinely purged either), and no
|
|
9
|
+
* equivalent of `pattern-lifecycle.ts`'s decay/deprecation pass.
|
|
10
|
+
*
|
|
11
|
+
* Observed in state-rich shops (per #488 reporter): 120+ rows accumulated
|
|
12
|
+
* over ~30h of activity, with most at `applied = 0`. Without pruning, this
|
|
13
|
+
* grows linearly with hook activity. Over months the table becomes a slow
|
|
14
|
+
* `getPendingInsights` target and vector embeddings (if attached) grow
|
|
15
|
+
* unbounded.
|
|
16
|
+
*
|
|
17
|
+
* Strategy: delete only insights that:
|
|
18
|
+
* - have never been applied (`applied = 0`), AND
|
|
19
|
+
* - are older than the retention window (default 30 days)
|
|
20
|
+
*
|
|
21
|
+
* Applied insights stay forever — they're part of the audit trail for
|
|
22
|
+
* pattern changes. Recently-created insights stay regardless of applied
|
|
23
|
+
* state — they might still be picked up by a future `applyInsight` pass.
|
|
24
|
+
*
|
|
25
|
+
* Called from `LearningConsolidationWorker.runContinuousLearningLoop` on
|
|
26
|
+
* the 30-min worker tick. Failures are non-fatal — the worker continues
|
|
27
|
+
* with the rest of its lifecycle work.
|
|
28
|
+
*/
|
|
29
|
+
const DEFAULT_RETENTION_DAYS = 30;
|
|
30
|
+
/**
|
|
31
|
+
* Delete stale unapplied insights. Idempotent — safe to call on every tick.
|
|
32
|
+
*
|
|
33
|
+
* Returns `{ pruned: 0 }` if the table doesn't exist (init hasn't created
|
|
34
|
+
* the dream schema yet) or if no rows qualify.
|
|
35
|
+
*/
|
|
36
|
+
export function pruneStaleDreamInsights(db, options = {}) {
|
|
37
|
+
const retentionDays = options.retentionDays ?? DEFAULT_RETENTION_DAYS;
|
|
38
|
+
// Guard against the table not existing yet — a freshly-init'd shop may
|
|
39
|
+
// have started the worker before the dream schema was applied.
|
|
40
|
+
const tableExists = db
|
|
41
|
+
.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='dream_insights'")
|
|
42
|
+
.get();
|
|
43
|
+
if (!tableExists) {
|
|
44
|
+
return { pruned: 0 };
|
|
45
|
+
}
|
|
46
|
+
const result = db
|
|
47
|
+
.prepare(`DELETE FROM dream_insights
|
|
48
|
+
WHERE applied = 0
|
|
49
|
+
AND created_at < datetime('now', ?)`)
|
|
50
|
+
.run(`-${retentionDays} days`);
|
|
51
|
+
return { pruned: result.changes };
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=dream-insights-pruner.js.map
|
|
@@ -17,6 +17,9 @@ import { LoggerFactory } from '../../logging/index.js';
|
|
|
17
17
|
// ADR-062: Meta-learning integration
|
|
18
18
|
import { MetaLearningEngine, DEFAULT_META_LEARNING_CONFIG, } from '../aqe-learning-engine.js';
|
|
19
19
|
import { LearningMetricsTracker, } from '../metrics-tracker.js';
|
|
20
|
+
// ADR-094 / #488 B.2: record per-tick liveness so `aqe learning loop-health`
|
|
21
|
+
// surfaces the kernel-side dream loop as live.
|
|
22
|
+
import { recordLoopHealth } from '../loop-health.js';
|
|
20
23
|
const logger = LoggerFactory.create('DreamScheduler');
|
|
21
24
|
/**
|
|
22
25
|
* Default DreamScheduler configuration
|
|
@@ -333,6 +336,7 @@ export class DreamScheduler {
|
|
|
333
336
|
}
|
|
334
337
|
this.dreaming = true;
|
|
335
338
|
logger.info('Starting dream cycle', { durationMs });
|
|
339
|
+
let dreamError;
|
|
336
340
|
try {
|
|
337
341
|
// Ensure concepts are loaded before dreaming
|
|
338
342
|
const loaded = await this.dreamEngine.ensureConceptsLoaded();
|
|
@@ -372,8 +376,21 @@ export class DreamScheduler {
|
|
|
372
376
|
logger.info('Dream completed', { insightsGenerated: result.insights.length });
|
|
373
377
|
return result;
|
|
374
378
|
}
|
|
379
|
+
catch (err) {
|
|
380
|
+
dreamError = err instanceof Error ? err : new Error(String(err));
|
|
381
|
+
throw err;
|
|
382
|
+
}
|
|
375
383
|
finally {
|
|
376
384
|
this.dreaming = false;
|
|
385
|
+
// ADR-094 / #488 B.2: record liveness so `aqe learning loop-health`
|
|
386
|
+
// can show the kernel-side dream scheduler as live. Best-effort —
|
|
387
|
+
// recordLoopHealth itself never throws.
|
|
388
|
+
if (this.memoryBackend) {
|
|
389
|
+
await recordLoopHealth(this.memoryBackend, 'dreamScheduler', {
|
|
390
|
+
success: !dreamError,
|
|
391
|
+
error: dreamError,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
377
394
|
}
|
|
378
395
|
}
|
|
379
396
|
/**
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-Learning Loop Health (#488 B.2)
|
|
3
|
+
*
|
|
4
|
+
* Records per-component liveness signals for the AQE self-learning loop:
|
|
5
|
+
*
|
|
6
|
+
* captured_experiences (SQLite)
|
|
7
|
+
* → CapturedExperienceBridge.drain (component: 'bridge')
|
|
8
|
+
* → learning:experience:* kv
|
|
9
|
+
* → LearningConsolidationWorker.tick (component: 'learningWorker')
|
|
10
|
+
* → learning:pattern:* kv
|
|
11
|
+
* → DreamScheduler.tick (component: 'dreamScheduler')
|
|
12
|
+
* → dream_insights / qe_patterns
|
|
13
|
+
*
|
|
14
|
+
* Each tick records `{ success: true }` so operators can answer "is the
|
|
15
|
+
* loop closing end-to-end?" without DB-mining `dream_cycles`, `kv_store`,
|
|
16
|
+
* and `routing_outcomes` separately. Cumulative counters in `aqe learning
|
|
17
|
+
* stats` are not enough: they grow forever and can't distinguish "healthy
|
|
18
|
+
* loop" from "loop wedged 3 hours ago with stale totals".
|
|
19
|
+
*
|
|
20
|
+
* Persists as a single JSON value under `learning:loop-health` in the
|
|
21
|
+
* unified memory. Cheap to write (~200 bytes), cheap to read.
|
|
22
|
+
*/
|
|
23
|
+
import type { MemoryBackend } from '../kernel/interfaces.js';
|
|
24
|
+
export declare const LOOP_HEALTH_KEY = "learning:loop-health";
|
|
25
|
+
export type LoopComponent = 'bridge' | 'dreamScheduler' | 'learningWorker';
|
|
26
|
+
export interface ComponentHealth {
|
|
27
|
+
/** ISO timestamp of the most recent successful tick. Empty string when never succeeded. */
|
|
28
|
+
lastSuccessAt: string;
|
|
29
|
+
/** Total ticks attempted since this loop-health record was created. */
|
|
30
|
+
ticksSinceBoot: number;
|
|
31
|
+
/** Total successful ticks since this loop-health record was created. */
|
|
32
|
+
successesSinceBoot: number;
|
|
33
|
+
/** Most recent error captured during a failed tick. Cleared on next success. */
|
|
34
|
+
lastError?: {
|
|
35
|
+
message: string;
|
|
36
|
+
at: string;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export interface LoopHealth {
|
|
40
|
+
/** Max(lastSuccessAt across components). Empty string when nothing has succeeded yet. */
|
|
41
|
+
overallLastSuccess: string;
|
|
42
|
+
/** ISO timestamp when this record was first written. */
|
|
43
|
+
bootedAt: string;
|
|
44
|
+
/** Per-component status. Components are added on first tick — absence means never invoked. */
|
|
45
|
+
components: {
|
|
46
|
+
bridge?: ComponentHealth;
|
|
47
|
+
dreamScheduler?: ComponentHealth;
|
|
48
|
+
learningWorker?: ComponentHealth;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Minimal memory interface this module needs. Both `MemoryBackend` (kernel)
|
|
53
|
+
* and `WorkerMemory` (worker context) satisfy this — the helper stays
|
|
54
|
+
* transport-agnostic so writers can call it without knowing which they hold.
|
|
55
|
+
*/
|
|
56
|
+
export interface LoopHealthMemory {
|
|
57
|
+
get<T>(key: string): Promise<T | undefined>;
|
|
58
|
+
set<T>(key: string, value: T): Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Record one tick of a component. Always best-effort: a write failure here
|
|
62
|
+
* MUST NOT propagate to the calling component (the bridge / worker should
|
|
63
|
+
* keep running even if the health key can't be persisted).
|
|
64
|
+
*/
|
|
65
|
+
export declare function recordLoopHealth(memory: LoopHealthMemory | MemoryBackend, component: LoopComponent, result: {
|
|
66
|
+
success: boolean;
|
|
67
|
+
error?: Error | string;
|
|
68
|
+
}): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Read the current loop-health record. Returns undefined if no tick has
|
|
71
|
+
* ever fired — operators reading via `aqe learning loop-health` should
|
|
72
|
+
* interpret undefined as "the loop hasn't started yet (or workers aren't
|
|
73
|
+
* running)" rather than "the loop is healthy".
|
|
74
|
+
*/
|
|
75
|
+
export declare function getLoopHealth(memory: LoopHealthMemory | MemoryBackend): Promise<LoopHealth | undefined>;
|
|
76
|
+
/**
|
|
77
|
+
* Compute a staleness verdict per component given a "stale after" threshold.
|
|
78
|
+
* Caller chooses the threshold based on the component's expected cadence:
|
|
79
|
+
* - bridge: 5s poll → stale after ~30s
|
|
80
|
+
* - learningWorker: 30 min tick → stale after ~2h
|
|
81
|
+
* - dreamScheduler: variable → caller's call
|
|
82
|
+
*/
|
|
83
|
+
export declare function isComponentStale(health: ComponentHealth | undefined, staleAfterMs: number, now?: Date): boolean;
|
|
84
|
+
//# sourceMappingURL=loop-health.d.ts.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-Learning Loop Health (#488 B.2)
|
|
3
|
+
*
|
|
4
|
+
* Records per-component liveness signals for the AQE self-learning loop:
|
|
5
|
+
*
|
|
6
|
+
* captured_experiences (SQLite)
|
|
7
|
+
* → CapturedExperienceBridge.drain (component: 'bridge')
|
|
8
|
+
* → learning:experience:* kv
|
|
9
|
+
* → LearningConsolidationWorker.tick (component: 'learningWorker')
|
|
10
|
+
* → learning:pattern:* kv
|
|
11
|
+
* → DreamScheduler.tick (component: 'dreamScheduler')
|
|
12
|
+
* → dream_insights / qe_patterns
|
|
13
|
+
*
|
|
14
|
+
* Each tick records `{ success: true }` so operators can answer "is the
|
|
15
|
+
* loop closing end-to-end?" without DB-mining `dream_cycles`, `kv_store`,
|
|
16
|
+
* and `routing_outcomes` separately. Cumulative counters in `aqe learning
|
|
17
|
+
* stats` are not enough: they grow forever and can't distinguish "healthy
|
|
18
|
+
* loop" from "loop wedged 3 hours ago with stale totals".
|
|
19
|
+
*
|
|
20
|
+
* Persists as a single JSON value under `learning:loop-health` in the
|
|
21
|
+
* unified memory. Cheap to write (~200 bytes), cheap to read.
|
|
22
|
+
*/
|
|
23
|
+
export const LOOP_HEALTH_KEY = 'learning:loop-health';
|
|
24
|
+
/**
|
|
25
|
+
* Record one tick of a component. Always best-effort: a write failure here
|
|
26
|
+
* MUST NOT propagate to the calling component (the bridge / worker should
|
|
27
|
+
* keep running even if the health key can't be persisted).
|
|
28
|
+
*/
|
|
29
|
+
export async function recordLoopHealth(memory, component, result) {
|
|
30
|
+
try {
|
|
31
|
+
const now = new Date().toISOString();
|
|
32
|
+
const state = (await memory.get(LOOP_HEALTH_KEY)) ?? {
|
|
33
|
+
overallLastSuccess: '',
|
|
34
|
+
bootedAt: now,
|
|
35
|
+
components: {},
|
|
36
|
+
};
|
|
37
|
+
const existing = state.components[component] ?? {
|
|
38
|
+
lastSuccessAt: '',
|
|
39
|
+
ticksSinceBoot: 0,
|
|
40
|
+
successesSinceBoot: 0,
|
|
41
|
+
};
|
|
42
|
+
existing.ticksSinceBoot += 1;
|
|
43
|
+
if (result.success) {
|
|
44
|
+
existing.lastSuccessAt = now;
|
|
45
|
+
existing.successesSinceBoot += 1;
|
|
46
|
+
delete existing.lastError;
|
|
47
|
+
}
|
|
48
|
+
else if (result.error) {
|
|
49
|
+
const message = result.error instanceof Error ? result.error.message : String(result.error);
|
|
50
|
+
existing.lastError = { message, at: now };
|
|
51
|
+
}
|
|
52
|
+
state.components[component] = existing;
|
|
53
|
+
// Recompute overallLastSuccess as max(component.lastSuccessAt). ISO 8601
|
|
54
|
+
// strings sort lexicographically the same as chronologically.
|
|
55
|
+
const successTimes = Object.values(state.components)
|
|
56
|
+
.map((c) => c?.lastSuccessAt ?? '')
|
|
57
|
+
.filter((t) => t !== '');
|
|
58
|
+
state.overallLastSuccess = successTimes.length > 0 ? successTimes.sort().pop() : '';
|
|
59
|
+
await memory.set(LOOP_HEALTH_KEY, state);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Loop-health is itself best-effort observability. Never throw.
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Read the current loop-health record. Returns undefined if no tick has
|
|
67
|
+
* ever fired — operators reading via `aqe learning loop-health` should
|
|
68
|
+
* interpret undefined as "the loop hasn't started yet (or workers aren't
|
|
69
|
+
* running)" rather than "the loop is healthy".
|
|
70
|
+
*/
|
|
71
|
+
export async function getLoopHealth(memory) {
|
|
72
|
+
try {
|
|
73
|
+
return await memory.get(LOOP_HEALTH_KEY);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Compute a staleness verdict per component given a "stale after" threshold.
|
|
81
|
+
* Caller chooses the threshold based on the component's expected cadence:
|
|
82
|
+
* - bridge: 5s poll → stale after ~30s
|
|
83
|
+
* - learningWorker: 30 min tick → stale after ~2h
|
|
84
|
+
* - dreamScheduler: variable → caller's call
|
|
85
|
+
*/
|
|
86
|
+
export function isComponentStale(health, staleAfterMs, now = new Date()) {
|
|
87
|
+
if (!health || !health.lastSuccessAt)
|
|
88
|
+
return true;
|
|
89
|
+
return now.getTime() - new Date(health.lastSuccessAt).getTime() > staleAfterMs;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=loop-health.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Usage Recorder
|
|
3
|
+
* ADR-021: QE ReasoningBank for Pattern Learning (single-writer extension)
|
|
4
|
+
*
|
|
5
|
+
* Single source of truth for "pattern was used in a task" accounting:
|
|
6
|
+
*
|
|
7
|
+
* - INSERTs a row into qe_pattern_usage (per-use audit trail)
|
|
8
|
+
* - UPDATEs qe_patterns.{usage_count, successful_uses, success_rate,
|
|
9
|
+
* quality_score, last_used_at, updated_at}
|
|
10
|
+
*
|
|
11
|
+
* Both writes happen inside ONE transaction so the audit trail and the
|
|
12
|
+
* aggregate columns never disagree on count.
|
|
13
|
+
*
|
|
14
|
+
* Before this helper, two parallel writers existed (#486 Gap B):
|
|
15
|
+
* - SQLitePatternStore.recordUsage — did BOTH INSERT and UPDATE atomically
|
|
16
|
+
* - hooks-dream-learning.ts inline UPDATE — did UPDATE only, skipping the
|
|
17
|
+
* audit INSERT. Hook-driven usage stayed invisible in qe_pattern_usage
|
|
18
|
+
* even as qe_patterns.usage_count incremented.
|
|
19
|
+
*
|
|
20
|
+
* Quality formula (mirrors pattern-lifecycle.ts and the legacy inline hook):
|
|
21
|
+
* quality_score = confidence * 0.3 + min(usage_count/100, 1) * 0.2 + success_rate * 0.5
|
|
22
|
+
*/
|
|
23
|
+
import type { Database as DatabaseType } from 'better-sqlite3';
|
|
24
|
+
export interface PatternUsageRecord {
|
|
25
|
+
patternId: string;
|
|
26
|
+
success: boolean;
|
|
27
|
+
metrics?: Record<string, unknown>;
|
|
28
|
+
feedback?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface PatternUsageResult {
|
|
31
|
+
/**
|
|
32
|
+
* Whether the helper found and updated the pattern row.
|
|
33
|
+
* False (no throw) if patternId doesn't exist, since hook subprocesses
|
|
34
|
+
* iterate over selectedPatternIds that may include stale UUIDs from
|
|
35
|
+
* the routing kv. Callers who want strict semantics should check this
|
|
36
|
+
* flag and throw themselves.
|
|
37
|
+
*/
|
|
38
|
+
updated: boolean;
|
|
39
|
+
/** Post-update usage_count. Undefined if !updated. */
|
|
40
|
+
usageCount?: number;
|
|
41
|
+
/** Post-update successful_uses. Undefined if !updated. */
|
|
42
|
+
successfulUses?: number;
|
|
43
|
+
/** Post-update success_rate. Undefined if !updated. */
|
|
44
|
+
successRate?: number;
|
|
45
|
+
/** Post-update quality_score. Undefined if !updated. */
|
|
46
|
+
qualityScore?: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Record one use of a pattern atomically across qe_pattern_usage and qe_patterns.
|
|
50
|
+
*
|
|
51
|
+
* Idempotency: each call inserts ONE audit row and increments usage_count by ONE.
|
|
52
|
+
* The transaction makes the two writes atomic — if either fails, neither lands.
|
|
53
|
+
*
|
|
54
|
+
* Concurrency: better-sqlite3 transactions are serialized at the connection
|
|
55
|
+
* level. Multiple concurrent callers on the same connection will queue.
|
|
56
|
+
*/
|
|
57
|
+
export declare function recordPatternUsage(db: DatabaseType, record: PatternUsageRecord): PatternUsageResult;
|
|
58
|
+
//# sourceMappingURL=pattern-usage-recorder.d.ts.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Usage Recorder
|
|
3
|
+
* ADR-021: QE ReasoningBank for Pattern Learning (single-writer extension)
|
|
4
|
+
*
|
|
5
|
+
* Single source of truth for "pattern was used in a task" accounting:
|
|
6
|
+
*
|
|
7
|
+
* - INSERTs a row into qe_pattern_usage (per-use audit trail)
|
|
8
|
+
* - UPDATEs qe_patterns.{usage_count, successful_uses, success_rate,
|
|
9
|
+
* quality_score, last_used_at, updated_at}
|
|
10
|
+
*
|
|
11
|
+
* Both writes happen inside ONE transaction so the audit trail and the
|
|
12
|
+
* aggregate columns never disagree on count.
|
|
13
|
+
*
|
|
14
|
+
* Before this helper, two parallel writers existed (#486 Gap B):
|
|
15
|
+
* - SQLitePatternStore.recordUsage — did BOTH INSERT and UPDATE atomically
|
|
16
|
+
* - hooks-dream-learning.ts inline UPDATE — did UPDATE only, skipping the
|
|
17
|
+
* audit INSERT. Hook-driven usage stayed invisible in qe_pattern_usage
|
|
18
|
+
* even as qe_patterns.usage_count incremented.
|
|
19
|
+
*
|
|
20
|
+
* Quality formula (mirrors pattern-lifecycle.ts and the legacy inline hook):
|
|
21
|
+
* quality_score = confidence * 0.3 + min(usage_count/100, 1) * 0.2 + success_rate * 0.5
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Record one use of a pattern atomically across qe_pattern_usage and qe_patterns.
|
|
25
|
+
*
|
|
26
|
+
* Idempotency: each call inserts ONE audit row and increments usage_count by ONE.
|
|
27
|
+
* The transaction makes the two writes atomic — if either fails, neither lands.
|
|
28
|
+
*
|
|
29
|
+
* Concurrency: better-sqlite3 transactions are serialized at the connection
|
|
30
|
+
* level. Multiple concurrent callers on the same connection will queue.
|
|
31
|
+
*/
|
|
32
|
+
export function recordPatternUsage(db, record) {
|
|
33
|
+
const pattern = db
|
|
34
|
+
.prepare(`SELECT confidence, usage_count, successful_uses FROM qe_patterns WHERE id = ?`)
|
|
35
|
+
.get(record.patternId);
|
|
36
|
+
if (!pattern) {
|
|
37
|
+
return { updated: false };
|
|
38
|
+
}
|
|
39
|
+
const successInc = record.success ? 1 : 0;
|
|
40
|
+
const newUsageCount = pattern.usage_count + 1;
|
|
41
|
+
const newSuccessfulUses = pattern.successful_uses + successInc;
|
|
42
|
+
const newSuccessRate = newSuccessfulUses / newUsageCount;
|
|
43
|
+
const usageScore = Math.min(1, newUsageCount / 100);
|
|
44
|
+
const newQualityScore = pattern.confidence * 0.3 + usageScore * 0.2 + newSuccessRate * 0.5;
|
|
45
|
+
const insertUsage = db.prepare(`
|
|
46
|
+
INSERT INTO qe_pattern_usage (pattern_id, success, metrics_json, feedback)
|
|
47
|
+
VALUES (?, ?, ?, ?)
|
|
48
|
+
`);
|
|
49
|
+
const updatePattern = db.prepare(`
|
|
50
|
+
UPDATE qe_patterns SET
|
|
51
|
+
usage_count = ?,
|
|
52
|
+
successful_uses = ?,
|
|
53
|
+
success_rate = ?,
|
|
54
|
+
quality_score = ?,
|
|
55
|
+
last_used_at = datetime('now'),
|
|
56
|
+
updated_at = datetime('now')
|
|
57
|
+
WHERE id = ?
|
|
58
|
+
`);
|
|
59
|
+
const txn = db.transaction(() => {
|
|
60
|
+
insertUsage.run(record.patternId, successInc, record.metrics ? JSON.stringify(record.metrics) : null, record.feedback ?? null);
|
|
61
|
+
updatePattern.run(newUsageCount, newSuccessfulUses, newSuccessRate, newQualityScore, record.patternId);
|
|
62
|
+
});
|
|
63
|
+
txn();
|
|
64
|
+
return {
|
|
65
|
+
updated: true,
|
|
66
|
+
usageCount: newUsageCount,
|
|
67
|
+
successfulUses: newSuccessfulUses,
|
|
68
|
+
successRate: newSuccessRate,
|
|
69
|
+
qualityScore: newQualityScore,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=pattern-usage-recorder.js.map
|