agentic-qe 3.8.14 → 3.9.0
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 +28 -0
- package/dist/adapters/a2ui/integration/agui-sync.js +2 -1
- package/dist/boot/fast-paths.d.ts +24 -0
- package/dist/boot/fast-paths.js +43 -0
- package/dist/boot/parallel-prefetch.d.ts +26 -0
- package/dist/boot/parallel-prefetch.js +36 -0
- package/dist/cli/bundle.js +12 -9431
- package/dist/cli/chunks/adapter-WBR5NXS3.js +2 -0
- package/dist/cli/chunks/agent-booster-wasm-PQYB7VRU.js +2 -0
- package/dist/cli/chunks/agent-handler-QDAB5NQS.js +33 -0
- package/dist/cli/chunks/aqe-learning-engine-TK4JQCGT.js +2 -0
- package/dist/cli/chunks/audit-S7JUYYVP.js +3 -0
- package/dist/cli/chunks/base-2WXOSMBQ.js +2 -0
- package/dist/cli/chunks/better-sqlite3-7KY2RDXO.js +2 -0
- package/dist/cli/chunks/brain-handler-PPEGDCN4.js +68 -0
- package/dist/cli/chunks/branch-enumerator-XK4V5W7L.js +7 -0
- package/dist/cli/chunks/browser-PALA5PL3.js +4 -0
- package/dist/cli/chunks/browser-workflow-42F7GK5T.js +2 -0
- package/dist/cli/chunks/chunk-24FKIJNC.js +15 -0
- package/dist/cli/chunks/chunk-263XS447.js +2 -0
- package/dist/cli/chunks/chunk-2BZFNEN2.js +4 -0
- package/dist/cli/chunks/chunk-2E5NQNSU.js +180 -0
- package/dist/cli/chunks/chunk-2I7J3O6V.js +2 -0
- package/dist/cli/chunks/chunk-3ADGXLTM.js +14 -0
- package/dist/cli/chunks/chunk-3IHG3WOY.js +12 -0
- package/dist/cli/chunks/chunk-3IUNFTIF.js +2 -0
- package/dist/cli/chunks/chunk-3JPRUND5.js +2 -0
- package/dist/cli/chunks/chunk-3NZLZHJI.js +2 -0
- package/dist/cli/chunks/chunk-3THRQEZ2.js +95 -0
- package/dist/cli/chunks/chunk-3ZOONQG6.js +2 -0
- package/dist/cli/chunks/chunk-4B6NCELM.js +2 -0
- package/dist/cli/chunks/chunk-4I2IOUS4.js +2 -0
- package/dist/cli/chunks/chunk-4VBTXZRM.js +2 -0
- package/dist/cli/chunks/chunk-4VUPRTVX.js +3 -0
- package/dist/cli/chunks/chunk-4YOMLWEK.js +70 -0
- package/dist/cli/chunks/chunk-4YS3IJ45.js +2 -0
- package/dist/cli/chunks/chunk-5SKGFSKD.js +2 -0
- package/dist/cli/chunks/chunk-5T2ZQWKF.js +27 -0
- package/dist/cli/chunks/chunk-62ADTHV7.js +2 -0
- package/dist/cli/chunks/chunk-6EOS7KX2.js +2 -0
- package/dist/cli/chunks/chunk-6SVX4DJC.js +6 -0
- package/dist/cli/chunks/chunk-72WOAVK6.js +2 -0
- package/dist/cli/chunks/chunk-7R6YMLVS.js +3 -0
- package/dist/cli/chunks/chunk-7VDBAVTY.js +2 -0
- package/dist/cli/chunks/chunk-AFLLQ5PP.js +15 -0
- package/dist/cli/chunks/chunk-AKE543X2.js +2 -0
- package/dist/cli/chunks/chunk-B36CDR4U.js +2 -0
- package/dist/cli/chunks/chunk-B6LLWYQ6.js +2 -0
- package/dist/cli/chunks/chunk-BDIEMZ22.js +91 -0
- package/dist/cli/chunks/chunk-BGXNSCXX.js +4 -0
- package/dist/cli/chunks/chunk-BLBRY5UD.js +2 -0
- package/dist/cli/chunks/chunk-BPWXXEH2.js +3029 -0
- package/dist/cli/chunks/chunk-BR26T7ZS.js +180 -0
- package/dist/cli/chunks/chunk-BTREG4IW.js +2 -0
- package/dist/cli/chunks/chunk-BULKFVYX.js +2 -0
- package/dist/cli/chunks/chunk-BXAXGEFC.js +24 -0
- package/dist/cli/chunks/chunk-CL6POIX4.js +2 -0
- package/dist/cli/chunks/chunk-CS2KS7LP.js +2 -0
- package/dist/cli/chunks/chunk-CWFB6BSA.js +316 -0
- package/dist/cli/chunks/chunk-DRT3WKQW.js +2 -0
- package/dist/cli/chunks/chunk-EHDQJQ6Y.js +27 -0
- package/dist/cli/chunks/chunk-ESVQ6MEB.js +2 -0
- package/dist/cli/chunks/chunk-FEKY7T6Q.js +2 -0
- package/dist/cli/chunks/chunk-FGA7VIFR.js +7 -0
- package/dist/cli/chunks/chunk-FIA6X7UL.js +2 -0
- package/dist/cli/chunks/chunk-GAOJV3OX.js +2 -0
- package/dist/cli/chunks/chunk-GKNNSCLC.js +5 -0
- package/dist/cli/chunks/chunk-GPQ57KA4.js +2 -0
- package/dist/cli/chunks/chunk-GRUUQAR6.js +2 -0
- package/dist/cli/chunks/chunk-HRO6OZQD.js +2 -0
- package/dist/cli/chunks/chunk-HY6PMO5W.js +66 -0
- package/dist/cli/chunks/chunk-IAV2JMIX.js +167 -0
- package/dist/cli/chunks/chunk-IFNIIK34.js +21 -0
- package/dist/cli/chunks/chunk-IGJPMN4I.js +3 -0
- package/dist/cli/chunks/chunk-J3KWWR6Z.js +1 -0
- package/dist/cli/chunks/chunk-JBANAPWG.js +2 -0
- package/dist/cli/chunks/chunk-JJO7Y4H3.js +604 -0
- package/dist/cli/chunks/chunk-JRYGQO2W.js +2 -0
- package/dist/cli/chunks/chunk-JXM26HEE.js +2 -0
- package/dist/cli/chunks/chunk-JZSDOIXA.js +2 -0
- package/dist/cli/chunks/chunk-KP5NUODU.js +3 -0
- package/dist/cli/chunks/chunk-LHJQD2VU.js +750 -0
- package/dist/cli/chunks/chunk-LNQIY6BP.js +2 -0
- package/dist/cli/chunks/chunk-MDUHYUHF.js +2 -0
- package/dist/cli/chunks/chunk-MV6CMOJQ.js +65 -0
- package/dist/cli/chunks/chunk-MZOFWJTM.js +2 -0
- package/dist/cli/chunks/chunk-N2NS2PHA.js +45 -0
- package/dist/cli/chunks/chunk-N4TL73TH.js +314 -0
- package/dist/cli/chunks/chunk-N5UXCLFI.js +2 -0
- package/dist/cli/chunks/chunk-NZ2VCPN4.js +2 -0
- package/dist/cli/chunks/chunk-OF4D7MYI.js +2 -0
- package/dist/cli/chunks/chunk-OI5NGQO2.js +2 -0
- package/dist/cli/chunks/chunk-OLHKGP35.js +2 -0
- package/dist/cli/chunks/chunk-QOVHWZEP.js +1 -0
- package/dist/cli/chunks/chunk-RFSN6IDA.js +79 -0
- package/dist/cli/chunks/chunk-RTGGL7D7.js +4 -0
- package/dist/cli/chunks/chunk-RU5WAHB7.js +3 -0
- package/dist/cli/chunks/chunk-SUSEVMZT.js +2 -0
- package/dist/cli/chunks/chunk-TLHP5EII.js +2 -0
- package/dist/cli/chunks/chunk-TWUWL5EJ.js +2 -0
- package/dist/cli/chunks/chunk-U5RN7YQW.js +2 -0
- package/dist/cli/chunks/chunk-UFUVUO3J.js +2 -0
- package/dist/cli/chunks/chunk-UQHYFOBX.js +16 -0
- package/dist/cli/chunks/chunk-VOS4NQSF.js +2 -0
- package/dist/cli/chunks/chunk-VSVXUTJN.js +256 -0
- package/dist/cli/chunks/chunk-WBQSXPBI.js +2 -0
- package/dist/cli/chunks/chunk-WGMPEW2T.js +2 -0
- package/dist/cli/chunks/chunk-WIEC7VKK.js +2 -0
- package/dist/cli/chunks/chunk-WJ3DLOXF.js +14 -0
- package/dist/cli/chunks/chunk-X3KI6JOY.js +9 -0
- package/dist/cli/chunks/chunk-X5IJGWYG.js +2 -0
- package/dist/cli/chunks/chunk-XIBDETCS.js +146 -0
- package/dist/cli/chunks/chunk-XLRQYLWW.js +2 -0
- package/dist/cli/chunks/chunk-XO6PVK2P.js +3 -0
- package/dist/cli/chunks/chunk-XRE2HCWG.js +3 -0
- package/dist/cli/chunks/chunk-XT2V2322.js +2 -0
- package/dist/cli/chunks/chunk-Y7BHKZFJ.js +18 -0
- package/dist/cli/chunks/chunk-YAGODYIG.js +59 -0
- package/dist/cli/chunks/chunk-YANUP2RO.js +2 -0
- package/dist/cli/chunks/chunk-YPFOCNOE.js +30 -0
- package/dist/cli/chunks/chunk-YR6ZZGH7.js +81 -0
- package/dist/cli/chunks/chunk-YVA65UZL.js +2 -0
- package/dist/cli/chunks/chunk-YW2THB5Q.js +2 -0
- package/dist/cli/chunks/chunk-ZAPS3UGQ.js +20 -0
- package/dist/cli/chunks/chunk-ZDATDCYN.js +2 -0
- package/dist/cli/chunks/ci-J374KDLI.js +81 -0
- package/dist/cli/chunks/ci-output-7JN7F6CI.js +2 -0
- package/dist/cli/chunks/claude-flow-setup-245JLJCN.js +2 -0
- package/dist/cli/chunks/client-MCSNSH2C.js +2 -0
- package/dist/cli/chunks/cline-installer-LBA2M5N3.js +4 -0
- package/dist/cli/chunks/code-U4N4WONM.js +38 -0
- package/dist/cli/chunks/code-index-extractor-A57Z6BO4.js +3 -0
- package/dist/cli/chunks/codex-installer-UXMK2N4T.js +8 -0
- package/dist/cli/chunks/completions-W66BSCOE.js +1364 -0
- package/dist/cli/chunks/complexity-analyzer-AB4OZARV.js +2 -0
- package/dist/cli/chunks/continuedev-installer-LRFZ2SJM.js +14 -0
- package/dist/cli/chunks/copilot-installer-CQ3JYBIB.js +3 -0
- package/dist/cli/chunks/cost-tracker-4F723RB6.js +2 -0
- package/dist/cli/chunks/coverage-4PUEQXAY.js +27 -0
- package/dist/cli/chunks/cross-domain-router-OWR5IJ5G.js +2 -0
- package/dist/cli/chunks/cursor-installer-JZEDEDHA.js +3 -0
- package/dist/cli/chunks/daemon-B7TWGHXQ.js +19 -0
- package/dist/cli/chunks/dag-attention-scheduler-JWO6XI6A.js +2 -0
- package/dist/cli/chunks/detect-L6ZZHUSX.js +2 -0
- package/dist/cli/chunks/domain-handler-FT5FLZWL.js +25 -0
- package/dist/cli/chunks/domain-transfer-5Y4FGJAJ.js +2 -0
- package/dist/cli/chunks/dream-4TDBIYED.js +2 -0
- package/dist/cli/chunks/esm-node-2PKHKOTS.js +2 -0
- package/dist/cli/chunks/eval-GHMPFGWV.js +15 -0
- package/dist/cli/chunks/fast-paths-B3R647KN.js +2 -0
- package/dist/cli/chunks/feature-flags-DWS7ARSX.js +2 -0
- package/dist/cli/chunks/feature-flags-IVQ3AL4Q.js +2 -0
- package/dist/cli/chunks/file-discovery-QFPA6GMV.js +2 -0
- package/dist/cli/chunks/fleet-EKOKMOMW.js +43 -0
- package/dist/cli/chunks/gnn-wrapper-OYC55N5E.js +2 -0
- package/dist/cli/chunks/heartbeat-handler-MBBS4IBU.js +48 -0
- package/dist/cli/chunks/heartbeat-scheduler-XDGMOT7X.js +2 -0
- package/dist/cli/chunks/hnsw-index-YO7CT23I.js +2 -0
- package/dist/cli/chunks/hnswlib-node-56YWVXFE.js +2 -0
- package/dist/cli/chunks/hooks-L5VLZGEK.js +101 -0
- package/dist/cli/chunks/hypergraph-engine-A4Y2ZRAG.js +2 -0
- package/dist/cli/chunks/hypergraph-handler-3HDGB5SZ.js +35 -0
- package/dist/cli/chunks/impact-analyzer-UEIGXSZ4.js +2 -0
- package/dist/cli/chunks/init-handler-JDET6WUN.js +68 -0
- package/dist/cli/chunks/init-wizard-JWZUGIPJ.js +2 -0
- package/dist/cli/chunks/kernel-YNDTVKIW.js +2 -0
- package/dist/cli/chunks/kilocode-installer-GZZG5AFW.js +4 -0
- package/dist/cli/chunks/kiro-installer-IWNY5TKH.js +74 -0
- package/dist/cli/chunks/knowledge-graph-NGJKFTSN.js +2 -0
- package/dist/cli/chunks/learning-722ZNSZ6.js +107 -0
- package/dist/cli/chunks/llm-router-DNAV746L.js +30 -0
- package/dist/cli/chunks/load-Y3GCUFM4.js +2 -0
- package/dist/cli/chunks/load-test-GZUBXFF3.js +2 -0
- package/dist/cli/chunks/mcp-LKPIBZ3W.js +2 -0
- package/dist/cli/chunks/memory-L57MLFOP.js +32 -0
- package/dist/cli/chunks/memory-backend-3NQIZUXE.js +2 -0
- package/dist/cli/chunks/memory-handlers-MDZQ7HVW.js +2 -0
- package/dist/cli/chunks/opencode-installer-4HUB36H5.js +3 -0
- package/dist/cli/chunks/orchestrator-QHSBB2UC.js +371 -0
- package/dist/cli/chunks/pipeline-D3QER35Z.js +19 -0
- package/dist/cli/chunks/platform-T4E7Q3RD.js +2 -0
- package/dist/cli/chunks/plugin-JHW2YPRC.js +27 -0
- package/dist/cli/chunks/prime-radiant-advanced-wasm-G7CFNNQV.js +2 -0
- package/dist/cli/chunks/protocol-executor-SPUVRDWT.js +2 -0
- package/dist/cli/chunks/protocol-handler-2BQQ4HDM.js +20 -0
- package/dist/cli/chunks/prove-UQ6JFT73.js +3 -0
- package/dist/cli/chunks/qe-reasoning-bank-3HBK2FVD.js +2 -0
- package/dist/cli/chunks/quality-JRZYMC77.js +7 -0
- package/dist/cli/chunks/queen-coordinator-RW3NKO5A.js +2 -0
- package/dist/cli/chunks/real-embeddings-GK63VF35.js +2 -0
- package/dist/cli/chunks/roocode-installer-F4E2LAYR.js +4 -0
- package/dist/cli/chunks/router-RJGHWDQ3.js +2 -0
- package/dist/cli/chunks/routing-feedback-ZXBXFKX6.js +2 -0
- package/dist/cli/chunks/routing-handler-VNKFUUGB.js +20 -0
- package/dist/cli/chunks/ruvector-commands-2TLNHC3A.js +8 -0
- package/dist/cli/chunks/rvf-dual-writer-MQW2SJLT.js +2 -0
- package/dist/cli/chunks/rvf-native-adapter-LKFKTMUN.js +2 -0
- package/dist/cli/chunks/safe-db-G22E5ROA.js +2 -0
- package/dist/cli/chunks/schedule-Y7VVCPYV.js +2 -0
- package/dist/cli/chunks/scheduler-AUQIFQB7.js +2 -0
- package/dist/cli/chunks/security-EBEG2OPU.js +14 -0
- package/dist/cli/chunks/shared-rvf-dual-writer-BVSCQAFS.js +2 -0
- package/dist/cli/chunks/sqlite-persistence-JAVHUGGL.js +2 -0
- package/dist/cli/chunks/status-handler-VZ32M4G4.js +45 -0
- package/dist/cli/chunks/structural-health-K6LRCKV6.js +2 -0
- package/dist/cli/chunks/sync-MHSHNLIM.js +23 -0
- package/dist/cli/chunks/task-handler-JNOIBZ2G.js +49 -0
- package/dist/cli/chunks/task-handlers-P5DSUKND.js +2 -0
- package/dist/cli/chunks/test-DO22BNIL.js +33 -0
- package/dist/cli/chunks/test-scheduling-VLRQZEFL.js +15 -0
- package/dist/cli/chunks/token-bootstrap-4VJKGVMK.js +2 -0
- package/dist/cli/chunks/token-usage-LG3PXRXH.js +25 -0
- package/dist/cli/chunks/transformers-GY7SIKEU.js +2 -0
- package/dist/cli/chunks/tree-sitter-wasm-parser-FT2KB66N.js +2 -0
- package/dist/cli/chunks/types-QJGNBKP2.js +2 -0
- package/dist/cli/chunks/unified-memory-XYGENQUT.js +2 -0
- package/dist/cli/chunks/unified-memory-hnsw-MVEGQBF3.js +2 -0
- package/dist/cli/chunks/unified-persistence-PFRCWEUG.js +2 -0
- package/dist/cli/chunks/validate-VQCRSVNQ.js +21 -0
- package/dist/cli/chunks/validate-swarm-A5DHAWTP.js +14 -0
- package/dist/cli/chunks/vibium-RZBSL4EB.js +2 -0
- package/dist/cli/chunks/visual-security-V47BLGJM.js +2 -0
- package/dist/cli/chunks/web-tree-sitter-7C4NXEOF.js +2 -0
- package/dist/cli/chunks/windsurf-installer-ES3KPQG3.js +7 -0
- package/dist/cli/chunks/witness-chain-BR63P4A7.js +2 -0
- package/dist/cli/chunks/workflow-JETHX4ML.js +51 -0
- package/dist/cli/chunks/workflow-orchestrator-7PZMX3JZ.js +2 -0
- package/dist/cli/chunks/wrappers-WP5RH745.js +2 -0
- package/dist/cli/commands/daemon.d.ts +13 -0
- package/dist/cli/commands/daemon.js +224 -0
- package/dist/cli/commands/hooks-handlers/hooks-shared.js +2 -1
- package/dist/cli/commands/plugin.d.ts +12 -0
- package/dist/cli/commands/plugin.js +135 -0
- package/dist/cli/commands/workflow.d.ts +10 -0
- package/dist/cli/commands/workflow.js +587 -0
- package/dist/cli/handlers/brain-handler.js +13 -8
- package/dist/cli/handlers/heartbeat-handler.d.ts +1 -0
- package/dist/cli/handlers/heartbeat-handler.js +20 -10
- package/dist/cli/handlers/hypergraph-handler.js +3 -3
- package/dist/cli/handlers/init-handler.js +10 -9
- package/dist/cli/handlers/interfaces.d.ts +4 -4
- package/dist/cli/index.js +159 -638
- package/dist/cli/lazy-registry.d.ts +27 -0
- package/dist/cli/lazy-registry.js +70 -0
- package/dist/context/compaction/context-budget.d.ts +71 -0
- package/dist/context/compaction/context-budget.js +120 -0
- package/dist/context/compaction/index.d.ts +96 -0
- package/dist/context/compaction/index.js +259 -0
- package/dist/context/compaction/llm-caller-adapter.d.ts +14 -0
- package/dist/context/compaction/llm-caller-adapter.js +47 -0
- package/dist/context/compaction/tier1-microcompact.d.ts +33 -0
- package/dist/context/compaction/tier1-microcompact.js +47 -0
- package/dist/context/compaction/tier2-session-summary.d.ts +72 -0
- package/dist/context/compaction/tier2-session-summary.js +172 -0
- package/dist/context/compaction/tier3-llm-compact.d.ts +65 -0
- package/dist/context/compaction/tier3-llm-compact.js +166 -0
- package/dist/context/compaction/tier4-reactive.d.ts +54 -0
- package/dist/context/compaction/tier4-reactive.js +129 -0
- package/dist/coordination/consensus/providers/claude-provider.d.ts +1 -0
- package/dist/coordination/consensus/providers/claude-provider.js +23 -3
- package/dist/hooks/cross-phase-hooks.d.ts +11 -0
- package/dist/hooks/cross-phase-hooks.js +73 -9
- package/dist/hooks/security/config-snapshot.d.ts +21 -0
- package/dist/hooks/security/config-snapshot.js +33 -0
- package/dist/hooks/security/exit-codes.d.ts +28 -0
- package/dist/hooks/security/exit-codes.js +33 -0
- package/dist/hooks/security/index.d.ts +15 -0
- package/dist/hooks/security/index.js +15 -0
- package/dist/hooks/security/ssrf-guard.d.ts +25 -0
- package/dist/hooks/security/ssrf-guard.js +69 -0
- package/dist/kernel/kernel.js +35 -0
- package/dist/kernel/memory-backend.js +3 -1
- package/dist/mcp/bundle.js +405 -351
- package/dist/mcp/entry.js +132 -77
- package/dist/mcp/middleware/batch-executor.d.ts +46 -0
- package/dist/mcp/middleware/batch-executor.js +150 -0
- package/dist/mcp/middleware/microcompact.d.ts +97 -0
- package/dist/mcp/middleware/microcompact.js +179 -0
- package/dist/mcp/middleware/middleware-chain.d.ts +37 -0
- package/dist/mcp/middleware/middleware-chain.js +60 -0
- package/dist/mcp/protocol-server.d.ts +11 -0
- package/dist/mcp/protocol-server.js +130 -35
- package/dist/mcp/services/session-durability-middleware.d.ts +22 -0
- package/dist/mcp/services/session-durability-middleware.js +64 -0
- package/dist/mcp/services/session-resume.d.ts +29 -0
- package/dist/mcp/services/session-resume.js +221 -0
- package/dist/mcp/services/session-store.d.ts +84 -0
- package/dist/mcp/services/session-store.js +163 -0
- package/dist/mcp/tool-registry.d.ts +9 -0
- package/dist/mcp/tool-registry.js +30 -1
- package/dist/mcp/types.d.ts +1 -0
- package/dist/plugins/cache.d.ts +44 -0
- package/dist/plugins/cache.js +149 -0
- package/dist/plugins/index.d.ts +15 -0
- package/dist/plugins/index.js +15 -0
- package/dist/plugins/lifecycle.d.ts +67 -0
- package/dist/plugins/lifecycle.js +175 -0
- package/dist/plugins/manifest.d.ts +45 -0
- package/dist/plugins/manifest.js +173 -0
- package/dist/plugins/resolver.d.ts +37 -0
- package/dist/plugins/resolver.js +80 -0
- package/dist/plugins/security.d.ts +23 -0
- package/dist/plugins/security.js +125 -0
- package/dist/plugins/sources/github.d.ts +17 -0
- package/dist/plugins/sources/github.js +77 -0
- package/dist/plugins/sources/local.d.ts +20 -0
- package/dist/plugins/sources/local.js +32 -0
- package/dist/plugins/sources/npm.d.ts +18 -0
- package/dist/plugins/sources/npm.js +82 -0
- package/dist/shared/llm/retry.d.ts +5 -2
- package/dist/shared/llm/retry.js +7 -3
- package/dist/shared/prompt-cache-latch.d.ts +41 -0
- package/dist/shared/prompt-cache-latch.js +63 -0
- package/dist/shared/retry-engine.d.ts +77 -0
- package/dist/shared/retry-engine.js +194 -0
- package/dist/workers/daemon.d.ts +8 -0
- package/dist/workers/daemon.js +13 -0
- package/dist/workers/quality-daemon/ci-monitor.d.ts +55 -0
- package/dist/workers/quality-daemon/ci-monitor.js +147 -0
- package/dist/workers/quality-daemon/coverage-delta.d.ts +72 -0
- package/dist/workers/quality-daemon/coverage-delta.js +135 -0
- package/dist/workers/quality-daemon/git-watcher.d.ts +51 -0
- package/dist/workers/quality-daemon/git-watcher.js +209 -0
- package/dist/workers/quality-daemon/index.d.ts +119 -0
- package/dist/workers/quality-daemon/index.js +343 -0
- package/dist/workers/quality-daemon/nightly-consolidation.d.ts +74 -0
- package/dist/workers/quality-daemon/nightly-consolidation.js +136 -0
- package/dist/workers/quality-daemon/notification-service.d.ts +67 -0
- package/dist/workers/quality-daemon/notification-service.js +178 -0
- package/dist/workers/quality-daemon/persistent-memory.d.ts +31 -0
- package/dist/workers/quality-daemon/persistent-memory.js +30 -0
- package/dist/workers/quality-daemon/priority-queue.d.ts +97 -0
- package/dist/workers/quality-daemon/priority-queue.js +126 -0
- package/dist/workers/quality-daemon/test-suggester.d.ts +50 -0
- package/dist/workers/quality-daemon/test-suggester.js +121 -0
- package/dist/workers/worker-manager.js +2 -1
- package/package.json +1 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentic QE v3 - Tier 4: Reactive Compaction (IMP-08)
|
|
3
|
+
*
|
|
4
|
+
* Last-resort compaction triggered by 413 errors or context overflow
|
|
5
|
+
* detection. Aggressively peels the oldest conversation rounds until
|
|
6
|
+
* the estimated token count drops below the recovery target.
|
|
7
|
+
*
|
|
8
|
+
* This tier is destructive — it drops messages entirely (no summary).
|
|
9
|
+
* It exists to recover from situations where the context window is
|
|
10
|
+
* critically full and the session would otherwise fail.
|
|
11
|
+
*/
|
|
12
|
+
import { estimateTokensPadded } from '../../mcp/middleware/microcompact';
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Defaults
|
|
15
|
+
// ============================================================================
|
|
16
|
+
const DEFAULT_RECOVERY_TARGET = 30_000;
|
|
17
|
+
const DEFAULT_MIN_PRESERVED = 4;
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Tier4Reactive
|
|
20
|
+
// ============================================================================
|
|
21
|
+
export class Tier4Reactive {
|
|
22
|
+
recoveryTarget;
|
|
23
|
+
minPreserved;
|
|
24
|
+
constructor(options = {}) {
|
|
25
|
+
this.recoveryTarget = options.recoveryTarget ?? DEFAULT_RECOVERY_TARGET;
|
|
26
|
+
this.minPreserved = options.minPreservedMessages ?? DEFAULT_MIN_PRESERVED;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Aggressively peel oldest messages until under the recovery target.
|
|
30
|
+
*
|
|
31
|
+
* Algorithm:
|
|
32
|
+
* 1. Compute total tokens.
|
|
33
|
+
* 2. If already under target, return early.
|
|
34
|
+
* 3. Walk forward from the oldest message, dropping messages until
|
|
35
|
+
* remaining tokens are at or below recoveryTarget.
|
|
36
|
+
* 4. Never drop below minPreservedMessages from the tail.
|
|
37
|
+
* 5. When dropping a tool_result, also drop its paired tool_use (and vice versa).
|
|
38
|
+
*/
|
|
39
|
+
compact(messages, trigger = 'context_overflow') {
|
|
40
|
+
if (messages.length === 0) {
|
|
41
|
+
return {
|
|
42
|
+
tier: 4,
|
|
43
|
+
survivingMessages: [],
|
|
44
|
+
survivingTokens: 0,
|
|
45
|
+
droppedCount: 0,
|
|
46
|
+
tokensSaved: 0,
|
|
47
|
+
trigger,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Annotate tokens
|
|
51
|
+
const annotated = messages.map(m => ({
|
|
52
|
+
...m,
|
|
53
|
+
estimatedTokens: m.estimatedTokens ?? estimateTokensPadded(m.content),
|
|
54
|
+
}));
|
|
55
|
+
const totalTokens = annotated.reduce((s, m) => s + m.estimatedTokens, 0);
|
|
56
|
+
// Already under target?
|
|
57
|
+
if (totalTokens <= this.recoveryTarget) {
|
|
58
|
+
return {
|
|
59
|
+
tier: 4,
|
|
60
|
+
survivingMessages: messages,
|
|
61
|
+
survivingTokens: totalTokens,
|
|
62
|
+
droppedCount: 0,
|
|
63
|
+
tokensSaved: 0,
|
|
64
|
+
trigger,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// Build pair map: toolUseId -> indices of both tool_use and tool_result
|
|
68
|
+
const pairMap = new Map();
|
|
69
|
+
for (let i = 0; i < annotated.length; i++) {
|
|
70
|
+
const m = annotated[i];
|
|
71
|
+
if (m.toolUseId) {
|
|
72
|
+
const existing = pairMap.get(m.toolUseId) ?? [];
|
|
73
|
+
existing.push(i);
|
|
74
|
+
pairMap.set(m.toolUseId, existing);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Mark messages for dropping
|
|
78
|
+
const dropped = new Set();
|
|
79
|
+
let remainingTokens = totalTokens;
|
|
80
|
+
// The tail we must preserve
|
|
81
|
+
const preserveFrom = Math.max(0, annotated.length - this.minPreserved);
|
|
82
|
+
for (let i = 0; i < annotated.length; i++) {
|
|
83
|
+
if (remainingTokens <= this.recoveryTarget)
|
|
84
|
+
break;
|
|
85
|
+
if (i >= preserveFrom)
|
|
86
|
+
break; // Don't touch the preserved tail
|
|
87
|
+
if (dropped.has(i))
|
|
88
|
+
continue;
|
|
89
|
+
// Drop this message
|
|
90
|
+
dropped.add(i);
|
|
91
|
+
remainingTokens -= annotated[i].estimatedTokens;
|
|
92
|
+
// If part of a pair, drop the partner too
|
|
93
|
+
if (annotated[i].toolUseId) {
|
|
94
|
+
const partners = pairMap.get(annotated[i].toolUseId) ?? [];
|
|
95
|
+
for (const partnerIdx of partners) {
|
|
96
|
+
if (!dropped.has(partnerIdx) && partnerIdx < preserveFrom) {
|
|
97
|
+
dropped.add(partnerIdx);
|
|
98
|
+
remainingTokens -= annotated[partnerIdx].estimatedTokens;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const surviving = annotated.filter((_, i) => !dropped.has(i));
|
|
104
|
+
const survivingTokens = surviving.reduce((s, m) => s + m.estimatedTokens, 0);
|
|
105
|
+
return {
|
|
106
|
+
tier: 4,
|
|
107
|
+
survivingMessages: surviving,
|
|
108
|
+
survivingTokens,
|
|
109
|
+
droppedCount: dropped.size,
|
|
110
|
+
tokensSaved: totalTokens - survivingTokens,
|
|
111
|
+
trigger,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Convenience: check if a given HTTP status or error message indicates
|
|
116
|
+
* a context overflow that should trigger reactive compaction.
|
|
117
|
+
*/
|
|
118
|
+
static isContextOverflow(statusOrMessage) {
|
|
119
|
+
if (typeof statusOrMessage === 'number') {
|
|
120
|
+
return statusOrMessage === 413;
|
|
121
|
+
}
|
|
122
|
+
const lower = statusOrMessage.toLowerCase();
|
|
123
|
+
return lower.includes('context_length_exceeded')
|
|
124
|
+
|| lower.includes('maximum context length')
|
|
125
|
+
|| lower.includes('too many tokens')
|
|
126
|
+
|| lower.includes('413');
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=tier4-reactive.js.map
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { toErrorMessage, toError } from '../../../shared/error-utils.js';
|
|
11
11
|
import { BaseModelProvider, } from '../model-provider';
|
|
12
|
+
import { PromptCacheLatch } from '../../../shared/prompt-cache-latch.js';
|
|
12
13
|
// ============================================================================
|
|
13
14
|
// Claude Model Provider Implementation
|
|
14
15
|
// ============================================================================
|
|
@@ -49,6 +50,8 @@ export class ClaudeModelProvider extends BaseModelProvider {
|
|
|
49
50
|
config;
|
|
50
51
|
apiKey;
|
|
51
52
|
baseUrl;
|
|
53
|
+
// IMP-05: Prompt cache latch — stabilizes API params to maximize cache hits
|
|
54
|
+
cacheLatch = new PromptCacheLatch();
|
|
52
55
|
/**
|
|
53
56
|
* Create a new Claude provider
|
|
54
57
|
*
|
|
@@ -87,11 +90,28 @@ export class ClaudeModelProvider extends BaseModelProvider {
|
|
|
87
90
|
if (this.disposed) {
|
|
88
91
|
throw new Error('Provider has been disposed');
|
|
89
92
|
}
|
|
90
|
-
const
|
|
91
|
-
const
|
|
93
|
+
const requestedModel = options?.model || this.config.defaultModel;
|
|
94
|
+
const requestedMaxTokens = options?.maxTokens || 4096;
|
|
92
95
|
const temperature = options?.temperature ?? 0.7;
|
|
93
96
|
const timeout = options?.timeout || this.config.defaultTimeout;
|
|
94
|
-
const
|
|
97
|
+
const requestedSystem = options?.systemPrompt || this.getDefaultSystemPrompt();
|
|
98
|
+
// IMP-05: Latch stable params to prevent prompt cache busting.
|
|
99
|
+
// If caller explicitly overrides, reset and re-latch.
|
|
100
|
+
if (this.cacheLatch.has('model') && this.cacheLatch.get('model') !== requestedModel) {
|
|
101
|
+
this.cacheLatch.reset('model');
|
|
102
|
+
}
|
|
103
|
+
if (this.cacheLatch.has('max_tokens') && this.cacheLatch.get('max_tokens') !== requestedMaxTokens) {
|
|
104
|
+
this.cacheLatch.reset('max_tokens');
|
|
105
|
+
}
|
|
106
|
+
if (this.cacheLatch.has('system') && this.cacheLatch.get('system') !== requestedSystem) {
|
|
107
|
+
this.cacheLatch.reset('system');
|
|
108
|
+
}
|
|
109
|
+
this.cacheLatch.latch('model', requestedModel);
|
|
110
|
+
this.cacheLatch.latch('max_tokens', requestedMaxTokens);
|
|
111
|
+
this.cacheLatch.latch('system', requestedSystem);
|
|
112
|
+
const model = this.cacheLatch.get('model');
|
|
113
|
+
const maxTokens = this.cacheLatch.get('max_tokens');
|
|
114
|
+
const systemPrompt = this.cacheLatch.get('system');
|
|
95
115
|
const request = {
|
|
96
116
|
model,
|
|
97
117
|
max_tokens: maxTokens,
|
|
@@ -26,6 +26,17 @@ export declare class CrossPhaseHookExecutor {
|
|
|
26
26
|
private querySignalsForInjection;
|
|
27
27
|
formatSignalsForInjection(signals: CrossPhaseSignal[]): string;
|
|
28
28
|
private getRecommendations;
|
|
29
|
+
/**
|
|
30
|
+
* IMP-07: When AQE_HOOKS_MANAGED_ONLY is set, only hooks whose names start
|
|
31
|
+
* with "managed:" are allowed to execute. All other hooks are filtered out.
|
|
32
|
+
*/
|
|
33
|
+
private filterManagedHooks;
|
|
34
|
+
/**
|
|
35
|
+
* IMP-07: SSRF guard — if a target string looks like a URL (has a scheme),
|
|
36
|
+
* validate it against the SSRF blocklist. Returns true when the URL is
|
|
37
|
+
* blocked and the caller should abort the action.
|
|
38
|
+
*/
|
|
39
|
+
private isBlockedUrl;
|
|
29
40
|
private checkConditions;
|
|
30
41
|
private evaluateCondition;
|
|
31
42
|
private getValueFromPath;
|
|
@@ -11,6 +11,7 @@ import { readFileSync, existsSync } from 'fs';
|
|
|
11
11
|
import { join } from 'path';
|
|
12
12
|
import { parse as parseYaml } from 'yaml';
|
|
13
13
|
import { getCrossPhaseMemory, } from '../memory/cross-phase-memory.js';
|
|
14
|
+
import { captureHooksConfigSnapshot, validateHookUrl, classifyHookExit, } from './security/index.js';
|
|
14
15
|
// =============================================================================
|
|
15
16
|
// Hook Executor
|
|
16
17
|
// =============================================================================
|
|
@@ -27,17 +28,24 @@ export class CrossPhaseHookExecutor {
|
|
|
27
28
|
// Initialization
|
|
28
29
|
// ---------------------------------------------------------------------------
|
|
29
30
|
async initialize() {
|
|
31
|
+
// IMP-07: Policy flag — globally disable all hook execution
|
|
32
|
+
if (process.env.AQE_HOOKS_DISABLED === 'true') {
|
|
33
|
+
console.log('[CrossPhaseHooks] All hooks disabled via AQE_HOOKS_DISABLED');
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
30
36
|
if (!existsSync(this.configPath)) {
|
|
31
37
|
console.warn(`[CrossPhaseHooks] Config not found: ${this.configPath}`);
|
|
32
38
|
return false;
|
|
33
39
|
}
|
|
34
40
|
try {
|
|
35
41
|
const content = readFileSync(this.configPath, 'utf-8');
|
|
36
|
-
|
|
37
|
-
if (!
|
|
42
|
+
const parsed = parseYaml(content);
|
|
43
|
+
if (!parsed.enabled) {
|
|
38
44
|
console.log('[CrossPhaseHooks] Hooks disabled in config');
|
|
39
45
|
return false;
|
|
40
46
|
}
|
|
47
|
+
// IMP-07: Deep-freeze config so it cannot be mutated at runtime
|
|
48
|
+
this.config = captureHooksConfigSnapshot(parsed);
|
|
41
49
|
await this.memory.initialize();
|
|
42
50
|
console.log(`[CrossPhaseHooks] Initialized with ${Object.keys(this.config.hooks).length} hooks`);
|
|
43
51
|
return true;
|
|
@@ -53,8 +61,11 @@ export class CrossPhaseHookExecutor {
|
|
|
53
61
|
async onAgentComplete(agentName, result) {
|
|
54
62
|
if (!this.config)
|
|
55
63
|
return;
|
|
56
|
-
|
|
57
|
-
|
|
64
|
+
// IMP-07: Runtime kill-switch check
|
|
65
|
+
if (process.env.AQE_HOOKS_DISABLED === 'true')
|
|
66
|
+
return;
|
|
67
|
+
const matchingHooks = this.filterManagedHooks(Object.entries(this.config.hooks).filter(([_, hook]) => hook.trigger.event === 'agent-complete' &&
|
|
68
|
+
hook.trigger.agent === agentName));
|
|
58
69
|
for (const [hookName, hook] of matchingHooks) {
|
|
59
70
|
if (this.checkConditions(hook.trigger.conditions, result)) {
|
|
60
71
|
console.log(`[CrossPhaseHooks] Executing hook: ${hookName}`);
|
|
@@ -65,9 +76,12 @@ export class CrossPhaseHookExecutor {
|
|
|
65
76
|
async onPhaseStart(phaseName, context = {}) {
|
|
66
77
|
if (!this.config)
|
|
67
78
|
return {};
|
|
79
|
+
// IMP-07: Runtime kill-switch check
|
|
80
|
+
if (process.env.AQE_HOOKS_DISABLED === 'true')
|
|
81
|
+
return {};
|
|
68
82
|
const injectedSignals = {};
|
|
69
|
-
const matchingHooks = Object.entries(this.config.hooks).filter(([_, hook]) => hook.trigger.event === 'phase-start' &&
|
|
70
|
-
hook.trigger.phase === phaseName);
|
|
83
|
+
const matchingHooks = this.filterManagedHooks(Object.entries(this.config.hooks).filter(([_, hook]) => hook.trigger.event === 'phase-start' &&
|
|
84
|
+
hook.trigger.phase === phaseName));
|
|
71
85
|
for (const [hookName, hook] of matchingHooks) {
|
|
72
86
|
console.log(`[CrossPhaseHooks] Executing phase-start hook: ${hookName}`);
|
|
73
87
|
for (const action of hook.actions) {
|
|
@@ -87,8 +101,11 @@ export class CrossPhaseHookExecutor {
|
|
|
87
101
|
async onPhaseEnd(phaseName, result) {
|
|
88
102
|
if (!this.config)
|
|
89
103
|
return;
|
|
90
|
-
|
|
91
|
-
|
|
104
|
+
// IMP-07: Runtime kill-switch check
|
|
105
|
+
if (process.env.AQE_HOOKS_DISABLED === 'true')
|
|
106
|
+
return;
|
|
107
|
+
const matchingHooks = this.filterManagedHooks(Object.entries(this.config.hooks).filter(([_, hook]) => hook.trigger.event === 'phase-end' &&
|
|
108
|
+
hook.trigger.phase === phaseName));
|
|
92
109
|
for (const [hookName, hook] of matchingHooks) {
|
|
93
110
|
console.log(`[CrossPhaseHooks] Executing phase-end hook: ${hookName}`);
|
|
94
111
|
await this.executeActions(hook.actions, result);
|
|
@@ -103,7 +120,19 @@ export class CrossPhaseHookExecutor {
|
|
|
103
120
|
await this.executeAction(action, context);
|
|
104
121
|
}
|
|
105
122
|
catch (err) {
|
|
106
|
-
|
|
123
|
+
// IMP-07: Classify exit codes when error carries one
|
|
124
|
+
const exitCode = err?.exitCode;
|
|
125
|
+
if (typeof exitCode === 'number') {
|
|
126
|
+
const classification = classifyHookExit(exitCode);
|
|
127
|
+
if (classification === 'model_blocking') {
|
|
128
|
+
console.error(`[CrossPhaseHooks] Model-blocking hook failure (exit ${exitCode}):`, err);
|
|
129
|
+
throw err; // Propagate blocking failures
|
|
130
|
+
}
|
|
131
|
+
console.warn(`[CrossPhaseHooks] Hook ${classification} (exit ${exitCode}):`, err);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
console.error(`[CrossPhaseHooks] Action failed:`, err);
|
|
135
|
+
}
|
|
107
136
|
}
|
|
108
137
|
}
|
|
109
138
|
}
|
|
@@ -158,6 +187,9 @@ export class CrossPhaseHookExecutor {
|
|
|
158
187
|
async executeNotifyAgent(action, context) {
|
|
159
188
|
if (!action.target || !action.message)
|
|
160
189
|
return;
|
|
190
|
+
// IMP-07: SSRF guard — block if target looks like a URL pointing to private IP
|
|
191
|
+
if (await this.isBlockedUrl(action.target))
|
|
192
|
+
return;
|
|
161
193
|
console.log(`[CrossPhaseHooks] Notify ${action.target}: ${action.message}`);
|
|
162
194
|
this.emit('agent-notification', {
|
|
163
195
|
target: action.target,
|
|
@@ -169,6 +201,9 @@ export class CrossPhaseHookExecutor {
|
|
|
169
201
|
async executeInvokeAgent(action, context) {
|
|
170
202
|
if (!action.target)
|
|
171
203
|
return;
|
|
204
|
+
// IMP-07: SSRF guard — block if target looks like a URL pointing to private IP
|
|
205
|
+
if (await this.isBlockedUrl(action.target))
|
|
206
|
+
return;
|
|
172
207
|
console.log(`[CrossPhaseHooks] Invoke agent: ${action.target}`);
|
|
173
208
|
this.emit('agent-invocation', {
|
|
174
209
|
agent: action.target,
|
|
@@ -235,6 +270,35 @@ export class CrossPhaseHookExecutor {
|
|
|
235
270
|
// ---------------------------------------------------------------------------
|
|
236
271
|
// Helpers
|
|
237
272
|
// ---------------------------------------------------------------------------
|
|
273
|
+
/**
|
|
274
|
+
* IMP-07: When AQE_HOOKS_MANAGED_ONLY is set, only hooks whose names start
|
|
275
|
+
* with "managed:" are allowed to execute. All other hooks are filtered out.
|
|
276
|
+
*/
|
|
277
|
+
filterManagedHooks(hooks) {
|
|
278
|
+
if (process.env.AQE_HOOKS_MANAGED_ONLY !== 'true')
|
|
279
|
+
return hooks;
|
|
280
|
+
const filtered = hooks.filter(([name]) => name.startsWith('managed:'));
|
|
281
|
+
if (filtered.length < hooks.length) {
|
|
282
|
+
console.log(`[CrossPhaseHooks] AQE_HOOKS_MANAGED_ONLY: filtered ${hooks.length - filtered.length} non-managed hooks`);
|
|
283
|
+
}
|
|
284
|
+
return filtered;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* IMP-07: SSRF guard — if a target string looks like a URL (has a scheme),
|
|
288
|
+
* validate it against the SSRF blocklist. Returns true when the URL is
|
|
289
|
+
* blocked and the caller should abort the action.
|
|
290
|
+
*/
|
|
291
|
+
async isBlockedUrl(target) {
|
|
292
|
+
// Only validate strings that look like URLs (contain "://")
|
|
293
|
+
if (!target.includes('://'))
|
|
294
|
+
return false;
|
|
295
|
+
const result = await validateHookUrl(target);
|
|
296
|
+
if (!result.safe) {
|
|
297
|
+
console.warn(`[CrossPhaseHooks] SSRF blocked: ${result.reason}`);
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
238
302
|
checkConditions(conditions, context) {
|
|
239
303
|
if (!conditions || conditions.length === 0)
|
|
240
304
|
return true;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook Configuration Snapshot
|
|
3
|
+
*
|
|
4
|
+
* Deep-freezes hook configuration at startup so it cannot be mutated
|
|
5
|
+
* at runtime. Uses structuredClone to detach from the original, then
|
|
6
|
+
* recursively freezes every nested object and array.
|
|
7
|
+
*
|
|
8
|
+
* @module hooks/security/config-snapshot
|
|
9
|
+
* @see IMP-07 Hook Security Hardening
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Deep-freeze an object and all nested objects.
|
|
13
|
+
* Returns a Readonly<T> that throws on mutation attempts.
|
|
14
|
+
*/
|
|
15
|
+
export declare function deepFreeze<T extends object>(obj: T): Readonly<T>;
|
|
16
|
+
/**
|
|
17
|
+
* Capture an immutable snapshot of hook configuration.
|
|
18
|
+
* Uses structuredClone to detach from original, then deep-freezes.
|
|
19
|
+
*/
|
|
20
|
+
export declare function captureHooksConfigSnapshot<T extends object>(config: T): Readonly<T>;
|
|
21
|
+
//# sourceMappingURL=config-snapshot.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook Configuration Snapshot
|
|
3
|
+
*
|
|
4
|
+
* Deep-freezes hook configuration at startup so it cannot be mutated
|
|
5
|
+
* at runtime. Uses structuredClone to detach from the original, then
|
|
6
|
+
* recursively freezes every nested object and array.
|
|
7
|
+
*
|
|
8
|
+
* @module hooks/security/config-snapshot
|
|
9
|
+
* @see IMP-07 Hook Security Hardening
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Deep-freeze an object and all nested objects.
|
|
13
|
+
* Returns a Readonly<T> that throws on mutation attempts.
|
|
14
|
+
*/
|
|
15
|
+
export function deepFreeze(obj) {
|
|
16
|
+
const props = Object.getOwnPropertyNames(obj);
|
|
17
|
+
for (const prop of props) {
|
|
18
|
+
const val = obj[prop];
|
|
19
|
+
if (val && typeof val === 'object' && !Object.isFrozen(val)) {
|
|
20
|
+
deepFreeze(val);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return Object.freeze(obj);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Capture an immutable snapshot of hook configuration.
|
|
27
|
+
* Uses structuredClone to detach from original, then deep-freezes.
|
|
28
|
+
*/
|
|
29
|
+
export function captureHooksConfigSnapshot(config) {
|
|
30
|
+
const snapshot = structuredClone(config);
|
|
31
|
+
return deepFreeze(snapshot);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=config-snapshot.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook Exit Code Semantics
|
|
3
|
+
*
|
|
4
|
+
* Defines exit-code conventions for hook processes so the runtime
|
|
5
|
+
* can distinguish success, user-visible errors, and model-blocking
|
|
6
|
+
* failures that should halt agent execution.
|
|
7
|
+
*
|
|
8
|
+
* @module hooks/security/exit-codes
|
|
9
|
+
* @see IMP-07 Hook Security Hardening
|
|
10
|
+
*/
|
|
11
|
+
export declare const HOOK_EXIT_CODES: {
|
|
12
|
+
/** Hook completed successfully. */
|
|
13
|
+
readonly SUCCESS: 0;
|
|
14
|
+
/** Hook failed with user-visible error (non-blocking). */
|
|
15
|
+
readonly USER_VISIBLE: 1;
|
|
16
|
+
/** Hook failed and must block the model from proceeding. */
|
|
17
|
+
readonly MODEL_BLOCKING: 2;
|
|
18
|
+
};
|
|
19
|
+
export type HookExitCode = typeof HOOK_EXIT_CODES[keyof typeof HOOK_EXIT_CODES];
|
|
20
|
+
/**
|
|
21
|
+
* Classify a numeric process exit code into a semantic category.
|
|
22
|
+
*
|
|
23
|
+
* - 0 -> 'success'
|
|
24
|
+
* - 2 -> 'model_blocking' (halts agent execution)
|
|
25
|
+
* - anything else -> 'user_error' (logged, non-blocking)
|
|
26
|
+
*/
|
|
27
|
+
export declare function classifyHookExit(code: number): 'success' | 'user_error' | 'model_blocking';
|
|
28
|
+
//# sourceMappingURL=exit-codes.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook Exit Code Semantics
|
|
3
|
+
*
|
|
4
|
+
* Defines exit-code conventions for hook processes so the runtime
|
|
5
|
+
* can distinguish success, user-visible errors, and model-blocking
|
|
6
|
+
* failures that should halt agent execution.
|
|
7
|
+
*
|
|
8
|
+
* @module hooks/security/exit-codes
|
|
9
|
+
* @see IMP-07 Hook Security Hardening
|
|
10
|
+
*/
|
|
11
|
+
export const HOOK_EXIT_CODES = {
|
|
12
|
+
/** Hook completed successfully. */
|
|
13
|
+
SUCCESS: 0,
|
|
14
|
+
/** Hook failed with user-visible error (non-blocking). */
|
|
15
|
+
USER_VISIBLE: 1,
|
|
16
|
+
/** Hook failed and must block the model from proceeding. */
|
|
17
|
+
MODEL_BLOCKING: 2,
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Classify a numeric process exit code into a semantic category.
|
|
21
|
+
*
|
|
22
|
+
* - 0 -> 'success'
|
|
23
|
+
* - 2 -> 'model_blocking' (halts agent execution)
|
|
24
|
+
* - anything else -> 'user_error' (logged, non-blocking)
|
|
25
|
+
*/
|
|
26
|
+
export function classifyHookExit(code) {
|
|
27
|
+
if (code === 0)
|
|
28
|
+
return 'success';
|
|
29
|
+
if (code === 2)
|
|
30
|
+
return 'model_blocking';
|
|
31
|
+
return 'user_error';
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=exit-codes.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook Security Module
|
|
3
|
+
*
|
|
4
|
+
* Re-exports all security primitives for the hook system:
|
|
5
|
+
* - Config snapshot (deep-freeze at startup)
|
|
6
|
+
* - SSRF guard (private IP / DNS rebinding protection)
|
|
7
|
+
* - Exit code semantics (success / user_error / model_blocking)
|
|
8
|
+
*
|
|
9
|
+
* @module hooks/security
|
|
10
|
+
* @see IMP-07 Hook Security Hardening
|
|
11
|
+
*/
|
|
12
|
+
export { captureHooksConfigSnapshot, deepFreeze } from './config-snapshot.js';
|
|
13
|
+
export { validateHookUrl, isPrivateIp, type SsrfValidationResult } from './ssrf-guard.js';
|
|
14
|
+
export { HOOK_EXIT_CODES, classifyHookExit, type HookExitCode } from './exit-codes.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook Security Module
|
|
3
|
+
*
|
|
4
|
+
* Re-exports all security primitives for the hook system:
|
|
5
|
+
* - Config snapshot (deep-freeze at startup)
|
|
6
|
+
* - SSRF guard (private IP / DNS rebinding protection)
|
|
7
|
+
* - Exit code semantics (success / user_error / model_blocking)
|
|
8
|
+
*
|
|
9
|
+
* @module hooks/security
|
|
10
|
+
* @see IMP-07 Hook Security Hardening
|
|
11
|
+
*/
|
|
12
|
+
export { captureHooksConfigSnapshot, deepFreeze } from './config-snapshot.js';
|
|
13
|
+
export { validateHookUrl, isPrivateIp } from './ssrf-guard.js';
|
|
14
|
+
export { HOOK_EXIT_CODES, classifyHookExit } from './exit-codes.js';
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSRF Guard for Hook URLs
|
|
3
|
+
*
|
|
4
|
+
* Blocks hooks from hitting private/internal IP ranges.
|
|
5
|
+
* Validates both direct IP addresses and DNS-resolved addresses
|
|
6
|
+
* to prevent DNS rebinding attacks.
|
|
7
|
+
*
|
|
8
|
+
* @module hooks/security/ssrf-guard
|
|
9
|
+
* @see IMP-07 Hook Security Hardening
|
|
10
|
+
*/
|
|
11
|
+
export interface SsrfValidationResult {
|
|
12
|
+
safe: boolean;
|
|
13
|
+
reason?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Check if an IP address is in a private/reserved range.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isPrivateIp(ip: string): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Validate a hook URL for SSRF safety.
|
|
21
|
+
* Blocks private IPs both as direct addresses and via DNS resolution.
|
|
22
|
+
* Disabled via AQE_HOOKS_SSRF_DISABLED=true (dev mode only).
|
|
23
|
+
*/
|
|
24
|
+
export declare function validateHookUrl(url: string): Promise<SsrfValidationResult>;
|
|
25
|
+
//# sourceMappingURL=ssrf-guard.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSRF Guard for Hook URLs
|
|
3
|
+
*
|
|
4
|
+
* Blocks hooks from hitting private/internal IP ranges.
|
|
5
|
+
* Validates both direct IP addresses and DNS-resolved addresses
|
|
6
|
+
* to prevent DNS rebinding attacks.
|
|
7
|
+
*
|
|
8
|
+
* @module hooks/security/ssrf-guard
|
|
9
|
+
* @see IMP-07 Hook Security Hardening
|
|
10
|
+
*/
|
|
11
|
+
import { isIP } from 'net';
|
|
12
|
+
import { lookup } from 'dns/promises';
|
|
13
|
+
const PRIVATE_RANGES = [
|
|
14
|
+
/^10\./,
|
|
15
|
+
/^172\.(1[6-9]|2\d|3[01])\./,
|
|
16
|
+
/^192\.168\./,
|
|
17
|
+
/^127\./,
|
|
18
|
+
/^0\./,
|
|
19
|
+
/^169\.254\./,
|
|
20
|
+
/^::1$/,
|
|
21
|
+
/^fc00:/,
|
|
22
|
+
/^fe80:/,
|
|
23
|
+
/^fd[0-9a-f]{2}:/i,
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* Check if an IP address is in a private/reserved range.
|
|
27
|
+
*/
|
|
28
|
+
export function isPrivateIp(ip) {
|
|
29
|
+
return PRIVATE_RANGES.some(r => r.test(ip));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Validate a hook URL for SSRF safety.
|
|
33
|
+
* Blocks private IPs both as direct addresses and via DNS resolution.
|
|
34
|
+
* Disabled via AQE_HOOKS_SSRF_DISABLED=true (dev mode only).
|
|
35
|
+
*/
|
|
36
|
+
export async function validateHookUrl(url) {
|
|
37
|
+
if (process.env.AQE_HOOKS_SSRF_DISABLED === 'true') {
|
|
38
|
+
return { safe: true };
|
|
39
|
+
}
|
|
40
|
+
let parsed;
|
|
41
|
+
try {
|
|
42
|
+
parsed = new URL(url);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return { safe: false, reason: `Invalid URL: ${url}` };
|
|
46
|
+
}
|
|
47
|
+
// Strip IPv6 brackets — URL.hostname wraps IPv6 as "[::1]" but
|
|
48
|
+
// isIP() and our regex patterns expect the bare address "::1".
|
|
49
|
+
const hostname = parsed.hostname.replace(/^\[|\]$/g, '');
|
|
50
|
+
// Direct IP check
|
|
51
|
+
if (isIP(hostname)) {
|
|
52
|
+
if (isPrivateIp(hostname)) {
|
|
53
|
+
return { safe: false, reason: `Private IP blocked: ${hostname}` };
|
|
54
|
+
}
|
|
55
|
+
return { safe: true };
|
|
56
|
+
}
|
|
57
|
+
// DNS resolution check (prevents DNS rebinding)
|
|
58
|
+
try {
|
|
59
|
+
const resolved = await lookup(hostname);
|
|
60
|
+
if (isPrivateIp(resolved.address)) {
|
|
61
|
+
return { safe: false, reason: `DNS resolves to private IP: ${resolved.address}` };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
return { safe: false, reason: `DNS lookup failed for ${hostname}: ${err.message}` };
|
|
66
|
+
}
|
|
67
|
+
return { safe: true };
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=ssrf-guard.js.map
|
package/dist/kernel/kernel.js
CHANGED
|
@@ -15,6 +15,8 @@ import { findProjectRoot } from './unified-memory.js';
|
|
|
15
15
|
import { initializeUnifiedPersistence } from './unified-persistence.js';
|
|
16
16
|
import * as path from 'path';
|
|
17
17
|
import * as fs from 'fs';
|
|
18
|
+
import { PluginLifecycleManager } from '../plugins/lifecycle';
|
|
19
|
+
import { PluginCache } from '../plugins/cache';
|
|
18
20
|
// Import domain plugin factories
|
|
19
21
|
import { createTestGenerationPlugin } from '../domains/test-generation/plugin';
|
|
20
22
|
import { createTestExecutionPlugin } from '../domains/test-execution/plugin';
|
|
@@ -149,6 +151,39 @@ export class QEKernelImpl {
|
|
|
149
151
|
agentId: 'qe-kernel',
|
|
150
152
|
});
|
|
151
153
|
this._eventBus.registerMiddleware(antiDriftMiddleware);
|
|
154
|
+
// IMP-09: Discover and register external plugins from cache
|
|
155
|
+
try {
|
|
156
|
+
const pluginCacheDir = path.join(dataDir, 'plugins');
|
|
157
|
+
const pluginCache = new PluginCache({ cacheDir: pluginCacheDir });
|
|
158
|
+
const pluginLifecycle = new PluginLifecycleManager({ cache: pluginCache });
|
|
159
|
+
const resolution = pluginLifecycle.resolveLoadOrder();
|
|
160
|
+
for (const resolved of resolution.ordered) {
|
|
161
|
+
const manifest = resolved.manifest;
|
|
162
|
+
const cachedPlugin = pluginCache.get(manifest.name, manifest.version);
|
|
163
|
+
if (!cachedPlugin)
|
|
164
|
+
continue;
|
|
165
|
+
const entryPointPath = path.join(cachedPlugin.path, manifest.entryPoint);
|
|
166
|
+
// Register a factory for each domain the plugin provides
|
|
167
|
+
for (const domain of manifest.domains) {
|
|
168
|
+
const domainName = domain;
|
|
169
|
+
if (DOMAIN_FACTORIES[domainName])
|
|
170
|
+
continue; // don't override built-in domains
|
|
171
|
+
const loader = this._plugins;
|
|
172
|
+
loader.registerFactory(domainName, async (eventBus, memory) => {
|
|
173
|
+
// Dynamically import the plugin entry point at load time
|
|
174
|
+
const pluginModule = await import(entryPointPath);
|
|
175
|
+
const createPlugin = pluginModule.default ?? pluginModule.createPlugin;
|
|
176
|
+
if (typeof createPlugin !== 'function') {
|
|
177
|
+
throw new Error(`Plugin "${manifest.name}" entry point must export a default function or "createPlugin" function`);
|
|
178
|
+
}
|
|
179
|
+
return createPlugin(eventBus, memory, this._coordinator);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
// External plugin loading is best-effort — don't block kernel startup
|
|
186
|
+
}
|
|
152
187
|
// Load plugins based on configuration
|
|
153
188
|
if (!this._config.lazyLoading) {
|
|
154
189
|
await this._plugins.loadAll();
|
|
@@ -56,7 +56,9 @@ export class InMemoryBackend {
|
|
|
56
56
|
return value !== undefined;
|
|
57
57
|
}
|
|
58
58
|
async search(pattern, limit = MEMORY_CONSTANTS.DEFAULT_SEARCH_LIMIT) {
|
|
59
|
-
|
|
59
|
+
// Escape regex-special chars first, then convert glob wildcards to regex
|
|
60
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
|
|
61
|
+
const regex = new RegExp(escaped.replace(/\*/g, '.*'));
|
|
60
62
|
const results = [];
|
|
61
63
|
for (const key of this.store.keys()) {
|
|
62
64
|
if (regex.test(key)) {
|