@remnic/core 9.3.613 → 9.3.615
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/dist/access-cli.js +59 -58
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +4 -2
- package/dist/access-http.js +23 -23
- package/dist/access-mcp.d.ts +9 -2
- package/dist/access-mcp.js +20 -20
- package/dist/access-schema.d.ts +26 -14
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-D2J9dh_9.d.ts → access-service-CBNEKjzN.d.ts} +71 -6
- package/dist/access-service.d.ts +2 -2
- package/dist/access-service.js +17 -17
- package/dist/active-recall.js +20 -3
- package/dist/active-recall.js.map +1 -1
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/behavior-learner.js +2 -3
- package/dist/behavior-learner.js.map +1 -1
- package/dist/bootstrap.d.ts +1 -1
- package/dist/briefing.js +3 -3
- package/dist/buffer.d.ts +1 -1
- package/dist/buffer.js +1 -1
- package/dist/calibration.d.ts +5 -2
- package/dist/calibration.js +7 -5
- package/dist/calibration.js.map +1 -1
- package/dist/{capsule-crypto-7FJQINUR.js → capsule-crypto-YO5QJ6L3.js} +2 -2
- package/dist/causal-consolidation.d.ts +8 -2
- package/dist/causal-consolidation.js +13 -11
- package/dist/causal-consolidation.js.map +1 -1
- package/dist/{chunk-3BP57I6J.js → chunk-2F6NP3NT.js} +2 -1
- package/dist/{chunk-3BP57I6J.js.map → chunk-2F6NP3NT.js.map} +1 -1
- package/dist/{chunk-AU7Q3LSC.js → chunk-2QSZNTDO.js} +4 -4
- package/dist/{chunk-HSVJGWYS.js → chunk-2ROPI5OE.js} +2 -2
- package/dist/{chunk-C4SQJZAF.js → chunk-2SGJY2UY.js} +6 -3
- package/dist/chunk-2SGJY2UY.js.map +1 -0
- package/dist/{chunk-ZDTVJXIP.js → chunk-3MAONBX3.js} +13 -5
- package/dist/chunk-3MAONBX3.js.map +1 -0
- package/dist/{chunk-G3Z3QEF5.js → chunk-3PY7VHV7.js} +2 -2
- package/dist/chunk-3PY7VHV7.js.map +1 -0
- package/dist/{chunk-CF3ZF2YU.js → chunk-3QSU4NFF.js} +3 -3
- package/dist/{chunk-AJA46VX5.js → chunk-3T74IZB3.js} +11 -2
- package/dist/chunk-3T74IZB3.js.map +1 -0
- package/dist/{chunk-KVEVLBKC.js → chunk-4HFJQCJZ.js} +13 -8
- package/dist/chunk-4HFJQCJZ.js.map +1 -0
- package/dist/{chunk-KGK2QKWL.js → chunk-4R4KTDIE.js} +1 -1
- package/dist/chunk-4R4KTDIE.js.map +1 -0
- package/dist/{chunk-OI27U2HT.js → chunk-5BTCT236.js} +2 -2
- package/dist/{chunk-TH67Q46T.js → chunk-5OHHEORR.js} +64 -21
- package/dist/chunk-5OHHEORR.js.map +1 -0
- package/dist/{chunk-CO7ZO4TU.js → chunk-5VDJMYTF.js} +2 -2
- package/dist/{chunk-BFBF3XEF.js → chunk-6BDVBBBY.js} +33 -25
- package/dist/{chunk-BFBF3XEF.js.map → chunk-6BDVBBBY.js.map} +1 -1
- package/dist/{chunk-EAZGEEG2.js → chunk-6L46YAEZ.js} +45 -9
- package/dist/chunk-6L46YAEZ.js.map +1 -0
- package/dist/{chunk-YFS5OEKO.js → chunk-7MLB4NCL.js} +2 -2
- package/dist/{chunk-LZ3VEOU5.js → chunk-AL4RAJL5.js} +22 -5
- package/dist/chunk-AL4RAJL5.js.map +1 -0
- package/dist/{chunk-557IAFPD.js → chunk-APRRL26Q.js} +2 -2
- package/dist/{chunk-QDDHYAKV.js → chunk-AZDOWD2L.js} +2 -2
- package/dist/{chunk-MLT75J5S.js → chunk-B6SU7YSE.js} +3 -3
- package/dist/{chunk-FXKPZ3H6.js → chunk-BPSGLMQ4.js} +2 -2
- package/dist/{chunk-2NLLXCJG.js → chunk-BXLOS5AJ.js} +2 -2
- package/dist/{chunk-NOMEVTUD.js → chunk-C6C7XVKG.js} +5 -4
- package/dist/chunk-C6C7XVKG.js.map +1 -0
- package/dist/{chunk-XKIQZXUB.js → chunk-CI7RKSRE.js} +7 -1
- package/dist/chunk-CI7RKSRE.js.map +1 -0
- package/dist/{chunk-IK34DVAC.js → chunk-CIOMS6DI.js} +2 -2
- package/dist/{chunk-2I5JGH3M.js → chunk-CYEPCZN5.js} +2 -2
- package/dist/{chunk-2I5JGH3M.js.map → chunk-CYEPCZN5.js.map} +1 -1
- package/dist/{chunk-JHMFYY7L.js → chunk-DCGT4FPP.js} +13 -5
- package/dist/chunk-DCGT4FPP.js.map +1 -0
- package/dist/{chunk-7DZRO2DC.js → chunk-DEPRLVLK.js} +2 -2
- package/dist/{chunk-CSKLPDN6.js → chunk-DEVUWMME.js} +52 -19
- package/dist/chunk-DEVUWMME.js.map +1 -0
- package/dist/{chunk-DHGSZ3UD.js → chunk-DGNQRNLL.js} +2 -2
- package/dist/{chunk-X7Y7WX73.js → chunk-DQEMWVMT.js} +1 -1
- package/dist/{chunk-HJNQQICM.js → chunk-EXUAP5LH.js} +108 -51
- package/dist/chunk-EXUAP5LH.js.map +1 -0
- package/dist/chunk-FAV25DUZ.js +12 -0
- package/dist/chunk-FAV25DUZ.js.map +1 -0
- package/dist/{chunk-ETUPBUHB.js → chunk-GDASG7NC.js} +2 -2
- package/dist/{chunk-L227SKTB.js → chunk-GDB4J2H3.js} +17 -1
- package/dist/chunk-GDB4J2H3.js.map +1 -0
- package/dist/{chunk-IP73YCZP.js → chunk-GLPBYIXN.js} +4 -2
- package/dist/chunk-GLPBYIXN.js.map +1 -0
- package/dist/{chunk-4HP7HIE3.js → chunk-HP5FMB6L.js} +2 -2
- package/dist/{chunk-EVZFIAPG.js → chunk-IBTZEBUD.js} +23 -10
- package/dist/chunk-IBTZEBUD.js.map +1 -0
- package/dist/{chunk-DOX2CG6Y.js → chunk-IEUU7O4F.js} +2 -2
- package/dist/{chunk-EUML3N6B.js → chunk-IMA6GU4Y.js} +3 -3
- package/dist/chunk-IMA6GU4Y.js.map +1 -0
- package/dist/{chunk-JNANKJLN.js → chunk-JOASJWQR.js} +2 -2
- package/dist/chunk-JOASJWQR.js.map +1 -0
- package/dist/{chunk-WSGF57U2.js → chunk-JQDZQ4TB.js} +2 -2
- package/dist/{chunk-HINSGUA7.js → chunk-KBL3JJR6.js} +9 -13
- package/dist/chunk-KBL3JJR6.js.map +1 -0
- package/dist/{chunk-IOTENEVL.js → chunk-KGLPJROV.js} +57 -50
- package/dist/chunk-KGLPJROV.js.map +1 -0
- package/dist/{chunk-W7L6HXUC.js → chunk-LXOM6IQU.js} +2 -2
- package/dist/{chunk-G6R5UD3Q.js → chunk-MGN7VHWQ.js} +42 -1
- package/dist/{chunk-G6R5UD3Q.js.map → chunk-MGN7VHWQ.js.map} +1 -1
- package/dist/{chunk-DLJ4IR6M.js → chunk-MHQC2WU2.js} +2 -2
- package/dist/chunk-MHQC2WU2.js.map +1 -0
- package/dist/{chunk-5RPTH6AU.js → chunk-NM5NQYJE.js} +20 -19
- package/dist/chunk-NM5NQYJE.js.map +1 -0
- package/dist/{chunk-6JGNHWCI.js → chunk-OBIRVF36.js} +3 -3
- package/dist/{chunk-CHCA44C3.js → chunk-ODPLEWB6.js} +3 -3
- package/dist/chunk-ODPLEWB6.js.map +1 -0
- package/dist/{chunk-HENLZHIT.js → chunk-OIF36KGD.js} +7 -4
- package/dist/chunk-OIF36KGD.js.map +1 -0
- package/dist/{chunk-GUPISBV2.js → chunk-PP2JH3GP.js} +2 -2
- package/dist/{chunk-OXJBNGBK.js → chunk-PSUB67YB.js} +2 -2
- package/dist/{chunk-UWY7GIVS.js → chunk-PYIFUBRK.js} +45 -13
- package/dist/chunk-PYIFUBRK.js.map +1 -0
- package/dist/{chunk-KIB7SDIJ.js → chunk-Q6YIJGXJ.js} +2 -2
- package/dist/{chunk-ZT3EGNLR.js → chunk-QPD426WT.js} +2 -2
- package/dist/{chunk-RLV3PQGH.js → chunk-QVO4YOB7.js} +6 -6
- package/dist/{chunk-GMAG2HS4.js → chunk-RG3LBSGH.js} +46 -9
- package/dist/chunk-RG3LBSGH.js.map +1 -0
- package/dist/{chunk-XSWKORGM.js → chunk-S53OYO3F.js} +3 -1
- package/dist/chunk-S53OYO3F.js.map +1 -0
- package/dist/{chunk-YCN4BVDK.js → chunk-SCPFRKIT.js} +4 -2
- package/dist/chunk-SCPFRKIT.js.map +1 -0
- package/dist/{chunk-NZPF2SYV.js → chunk-T7N6KQGS.js} +138 -5
- package/dist/chunk-T7N6KQGS.js.map +1 -0
- package/dist/{chunk-VJXSUAO7.js → chunk-TNOWU6RP.js} +13 -10
- package/dist/chunk-TNOWU6RP.js.map +1 -0
- package/dist/{chunk-PCI747N2.js → chunk-TZVQQTG4.js} +48 -19
- package/dist/chunk-TZVQQTG4.js.map +1 -0
- package/dist/{chunk-KQAFEZQX.js → chunk-VDX2J7OX.js} +2 -2
- package/dist/{chunk-IK7DCC5H.js → chunk-VMGLYN42.js} +2 -2
- package/dist/{chunk-KM2A35EO.js → chunk-WB3LYXC5.js} +11 -7
- package/dist/chunk-WB3LYXC5.js.map +1 -0
- package/dist/{chunk-PPPZY2EU.js → chunk-WD2W4234.js} +9 -3
- package/dist/chunk-WD2W4234.js.map +1 -0
- package/dist/{chunk-NSKYFGDL.js → chunk-X4QQB7O6.js} +2 -2
- package/dist/{chunk-HPWVAEET.js → chunk-X6IRLNOO.js} +3 -7
- package/dist/chunk-X6IRLNOO.js.map +1 -0
- package/dist/{chunk-46GJIW5M.js → chunk-XAZOWLW4.js} +5 -5
- package/dist/{chunk-46GJIW5M.js.map → chunk-XAZOWLW4.js.map} +1 -1
- package/dist/{chunk-XPSVGJYA.js → chunk-YRMKDTKF.js} +12 -9
- package/dist/chunk-YRMKDTKF.js.map +1 -0
- package/dist/{chunk-6ZZP4EJF.js → chunk-ZJR7VG5L.js} +3 -3
- package/dist/{chunk-6ZZP4EJF.js.map → chunk-ZJR7VG5L.js.map} +1 -1
- package/dist/{chunk-2QANQKSQ.js → chunk-ZK32E74R.js} +156 -45
- package/dist/chunk-ZK32E74R.js.map +1 -0
- package/dist/{cli-OrfKXNU4.d.ts → cli-Cw729yLf.d.ts} +6 -2
- package/dist/cli.d.ts +3 -3
- package/dist/cli.js +61 -60
- package/dist/compounding/engine.js +3 -3
- package/dist/compounding/preference-consolidator.js +39 -11
- package/dist/compounding/preference-consolidator.js.map +1 -1
- package/dist/config.js +1 -1
- package/dist/connectors/codex-materialize-runner.js +3 -3
- package/dist/connectors/index.js +3 -3
- package/dist/consolidation-provenance-check.js +1 -1
- package/dist/contradiction/index.js +4 -4
- package/dist/conversation-index/backend.js +2 -2
- package/dist/conversation-index/indexer.js +1 -1
- package/dist/cross-namespace-budget.js +1 -1
- package/dist/enrichment/index.js +1 -1
- package/dist/entity-retrieval.js +3 -3
- package/dist/evals.js +1 -1
- package/dist/explicit-capture.d.ts +11 -1
- package/dist/explicit-capture.js +1 -1
- package/dist/extraction-judge.js +8 -1
- package/dist/extraction.js +2 -2
- package/dist/fallback-llm.d.ts +23 -6
- package/dist/fallback-llm.js +5 -3
- package/dist/{first-start-migration-GYJWIH36.js → first-start-migration-FF7YFGRP.js} +6 -6
- package/dist/index.d.ts +3 -3
- package/dist/index.js +95 -94
- package/dist/index.js.map +1 -1
- package/dist/lcm/archive.js +2 -2
- package/dist/lcm/engine.js +5 -5
- package/dist/lcm/index.js +7 -7
- package/dist/lcm/summarizer.js +3 -3
- package/dist/maintenance/memory-governance-cron.d.ts +6 -4
- package/dist/maintenance/memory-governance-cron.js +1 -1
- package/dist/maintenance/memory-governance.js +3 -3
- package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
- package/dist/maintenance/rebuild-memory-projection.js +4 -4
- package/dist/mcp-memory-inspector-app.d.ts +2 -2
- package/dist/mcp-memory-inspector-app.js +1 -1
- package/dist/migrate/from-engram.js +1 -1
- package/dist/namespaces/migrate.js +16 -15
- package/dist/namespaces/search.js +12 -11
- package/dist/namespaces/storage.js +3 -3
- package/dist/network/webdav.d.ts +2 -0
- package/dist/network/webdav.js +1 -1
- package/dist/objective-state-writers.js +2 -2
- package/dist/operator-toolkit.d.ts +3 -1
- package/dist/operator-toolkit.js +21 -20
- package/dist/{orchestrator-DTRQG75J.d.ts → orchestrator-CqWOjfgl.d.ts} +46 -3
- package/dist/orchestrator.d.ts +1 -1
- package/dist/orchestrator.js +48 -45
- package/dist/patterns-cli.js +1 -1
- package/dist/qmd-recall-cache.d.ts +2 -0
- package/dist/qmd-recall-cache.js +1 -1
- package/dist/qmd.d.ts +37 -2
- package/dist/qmd.js +4 -1
- package/dist/recall-explain-renderer.js +3 -3
- package/dist/recall-planner-llm.d.ts +57 -0
- package/dist/recall-planner-llm.js +167 -0
- package/dist/recall-planner-llm.js.map +1 -0
- package/dist/recall-xray-cli.js +4 -4
- package/dist/recall-xray-renderer.js +3 -3
- package/dist/recall-xray.js +2 -2
- package/dist/resume-bundles.js +2 -2
- package/dist/retrieval-agents.js +2 -2
- package/dist/routing/store.js +1 -1
- package/dist/schemas.d.ts +22 -22
- package/dist/search/factory.js +11 -10
- package/dist/search/index.js +11 -10
- package/dist/search/lancedb-backend.d.ts +1 -1
- package/dist/search/lancedb-backend.js +3 -2
- package/dist/search/meilisearch-backend.d.ts +1 -1
- package/dist/search/meilisearch-backend.js +3 -2
- package/dist/search/noop-backend.d.ts +1 -1
- package/dist/search/noop-backend.js +1 -1
- package/dist/search/orama-backend.d.ts +1 -1
- package/dist/search/orama-backend.js +3 -2
- package/dist/search/port.d.ts +6 -1
- package/dist/search/port.js +7 -0
- package/dist/search/remote-backend.d.ts +1 -1
- package/dist/search/remote-backend.js +1 -1
- package/dist/semantic-consolidation.js +4 -4
- package/dist/semantic-rule-promotion.js +3 -3
- package/dist/semantic-rule-verifier.js +3 -3
- package/dist/session-observer-state.js +1 -1
- package/dist/storage.js +2 -2
- package/dist/summarizer.js +2 -2
- package/dist/temporal-index.js +1 -1
- package/dist/{tier-stats-SKML2OSF.js → tier-stats-3LYQ3VV5.js} +3 -3
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +2 -2
- package/dist/transfer/export-sqlite.js +1 -1
- package/dist/transfer/types.d.ts +12 -12
- package/dist/types.d.ts +32 -0
- package/dist/types.js +1 -1
- package/dist/utility-learner.js +1 -1
- package/dist/utility-runtime.js +2 -2
- package/dist/verified-recall.js +3 -3
- package/dist/work/board.js +2 -2
- package/dist/work/storage.d.ts +2 -0
- package/dist/work/storage.js +1 -1
- package/package.json +1 -1
- package/src/access-http.ts +24 -10
- package/src/access-mcp.test.ts +160 -0
- package/src/access-mcp.ts +72 -7
- package/src/access-schema.ts +11 -0
- package/src/access-service-coding-write.test.ts +478 -0
- package/src/access-service.ts +237 -32
- package/src/active-recall.test.ts +40 -0
- package/src/active-recall.ts +19 -2
- package/src/behavior-learner.ts +5 -3
- package/src/buffer-session.test.ts +58 -0
- package/src/buffer-surprise-trigger.test.ts +4 -18
- package/src/buffer.ts +39 -11
- package/src/calibration.ts +10 -4
- package/src/causal-consolidation.test.ts +47 -2
- package/src/causal-consolidation.ts +13 -9
- package/src/cli.ts +19 -4
- package/src/compounding/engine.ts +2 -0
- package/src/compounding/preference-consolidator.test.ts +292 -0
- package/src/compounding/preference-consolidator.ts +55 -19
- package/src/config.test.ts +213 -0
- package/src/config.ts +175 -4
- package/src/connectors/codex-materialize-runner.ts +7 -4
- package/src/consolidation-provenance-check.ts +24 -5
- package/src/conversation-index/indexer.test.ts +22 -0
- package/src/conversation-index/indexer.ts +7 -3
- package/src/cross-namespace-budget.test.ts +44 -21
- package/src/cross-namespace-budget.ts +2 -2
- package/src/enrichment/pipeline.ts +11 -16
- package/src/evals.ts +1 -1
- package/src/explicit-capture.ts +19 -2
- package/src/extraction-judge-chain.test.ts +55 -0
- package/src/extraction-judge.ts +7 -9
- package/src/extraction.ts +16 -5
- package/src/fallback-llm.test.ts +600 -1
- package/src/fallback-llm.ts +91 -22
- package/src/maintenance/memory-governance-cron.ts +39 -29
- package/src/mcp-memory-inspector-app.ts +54 -12
- package/src/message-parts/index.ts +6 -0
- package/src/message-parts/message-parts.test.ts +30 -0
- package/src/migrate/from-engram.ts +19 -5
- package/src/namespaces/search.test.ts +15 -2
- package/src/namespaces/search.ts +1 -1
- package/src/network/webdav.ts +61 -21
- package/src/operator-toolkit.ts +6 -2
- package/src/orchestrator.ts +173 -20
- package/src/qmd-client.test.ts +85 -0
- package/src/qmd-recall-cache.test.ts +16 -0
- package/src/qmd-recall-cache.ts +7 -0
- package/src/qmd.test.ts +54 -0
- package/src/qmd.ts +119 -19
- package/src/recall-planner-llm.test.ts +224 -0
- package/src/recall-planner-llm.ts +289 -0
- package/src/routing/store.ts +4 -8
- package/src/search/factory.ts +3 -0
- package/src/search/lancedb-backend.ts +15 -3
- package/src/search/meilisearch-backend.ts +70 -7
- package/src/search/noop-backend.ts +5 -1
- package/src/search/orama-backend.ts +15 -3
- package/src/search/port.ts +15 -0
- package/src/search/remote-backend.ts +5 -1
- package/src/session-observer-state.ts +1 -1
- package/src/summarizer.ts +3 -3
- package/src/temporal-index.test.ts +18 -0
- package/src/temporal-index.ts +45 -0
- package/src/training-export/cli-date-validation.test.ts +36 -0
- package/src/training-export/date-parse.ts +21 -2
- package/src/transfer/export-sqlite.ts +3 -0
- package/src/types.ts +35 -0
- package/src/utility-learner.ts +1 -0
- package/src/work/storage.ts +23 -0
- package/dist/chunk-2QANQKSQ.js.map +0 -1
- package/dist/chunk-5RPTH6AU.js.map +0 -1
- package/dist/chunk-AJA46VX5.js.map +0 -1
- package/dist/chunk-C4SQJZAF.js.map +0 -1
- package/dist/chunk-CHCA44C3.js.map +0 -1
- package/dist/chunk-CSKLPDN6.js.map +0 -1
- package/dist/chunk-DLJ4IR6M.js.map +0 -1
- package/dist/chunk-EAZGEEG2.js.map +0 -1
- package/dist/chunk-EUML3N6B.js.map +0 -1
- package/dist/chunk-EVZFIAPG.js.map +0 -1
- package/dist/chunk-G3Z3QEF5.js.map +0 -1
- package/dist/chunk-GMAG2HS4.js.map +0 -1
- package/dist/chunk-HENLZHIT.js.map +0 -1
- package/dist/chunk-HINSGUA7.js.map +0 -1
- package/dist/chunk-HJNQQICM.js.map +0 -1
- package/dist/chunk-HPWVAEET.js.map +0 -1
- package/dist/chunk-IOTENEVL.js.map +0 -1
- package/dist/chunk-IP73YCZP.js.map +0 -1
- package/dist/chunk-JHMFYY7L.js.map +0 -1
- package/dist/chunk-JNANKJLN.js.map +0 -1
- package/dist/chunk-KGK2QKWL.js.map +0 -1
- package/dist/chunk-KM2A35EO.js.map +0 -1
- package/dist/chunk-KVEVLBKC.js.map +0 -1
- package/dist/chunk-L227SKTB.js.map +0 -1
- package/dist/chunk-LZ3VEOU5.js.map +0 -1
- package/dist/chunk-NOMEVTUD.js.map +0 -1
- package/dist/chunk-NZPF2SYV.js.map +0 -1
- package/dist/chunk-PCI747N2.js.map +0 -1
- package/dist/chunk-PPPZY2EU.js.map +0 -1
- package/dist/chunk-TH67Q46T.js.map +0 -1
- package/dist/chunk-UWY7GIVS.js.map +0 -1
- package/dist/chunk-VJXSUAO7.js.map +0 -1
- package/dist/chunk-XKIQZXUB.js.map +0 -1
- package/dist/chunk-XPSVGJYA.js.map +0 -1
- package/dist/chunk-XSWKORGM.js.map +0 -1
- package/dist/chunk-YCN4BVDK.js.map +0 -1
- package/dist/chunk-ZDTVJXIP.js.map +0 -1
- /package/dist/{capsule-crypto-7FJQINUR.js.map → capsule-crypto-YO5QJ6L3.js.map} +0 -0
- /package/dist/{chunk-AU7Q3LSC.js.map → chunk-2QSZNTDO.js.map} +0 -0
- /package/dist/{chunk-HSVJGWYS.js.map → chunk-2ROPI5OE.js.map} +0 -0
- /package/dist/{chunk-CF3ZF2YU.js.map → chunk-3QSU4NFF.js.map} +0 -0
- /package/dist/{chunk-OI27U2HT.js.map → chunk-5BTCT236.js.map} +0 -0
- /package/dist/{chunk-CO7ZO4TU.js.map → chunk-5VDJMYTF.js.map} +0 -0
- /package/dist/{chunk-YFS5OEKO.js.map → chunk-7MLB4NCL.js.map} +0 -0
- /package/dist/{chunk-557IAFPD.js.map → chunk-APRRL26Q.js.map} +0 -0
- /package/dist/{chunk-QDDHYAKV.js.map → chunk-AZDOWD2L.js.map} +0 -0
- /package/dist/{chunk-MLT75J5S.js.map → chunk-B6SU7YSE.js.map} +0 -0
- /package/dist/{chunk-FXKPZ3H6.js.map → chunk-BPSGLMQ4.js.map} +0 -0
- /package/dist/{chunk-2NLLXCJG.js.map → chunk-BXLOS5AJ.js.map} +0 -0
- /package/dist/{chunk-IK34DVAC.js.map → chunk-CIOMS6DI.js.map} +0 -0
- /package/dist/{chunk-7DZRO2DC.js.map → chunk-DEPRLVLK.js.map} +0 -0
- /package/dist/{chunk-DHGSZ3UD.js.map → chunk-DGNQRNLL.js.map} +0 -0
- /package/dist/{chunk-X7Y7WX73.js.map → chunk-DQEMWVMT.js.map} +0 -0
- /package/dist/{chunk-ETUPBUHB.js.map → chunk-GDASG7NC.js.map} +0 -0
- /package/dist/{chunk-4HP7HIE3.js.map → chunk-HP5FMB6L.js.map} +0 -0
- /package/dist/{chunk-DOX2CG6Y.js.map → chunk-IEUU7O4F.js.map} +0 -0
- /package/dist/{chunk-WSGF57U2.js.map → chunk-JQDZQ4TB.js.map} +0 -0
- /package/dist/{chunk-W7L6HXUC.js.map → chunk-LXOM6IQU.js.map} +0 -0
- /package/dist/{chunk-6JGNHWCI.js.map → chunk-OBIRVF36.js.map} +0 -0
- /package/dist/{chunk-GUPISBV2.js.map → chunk-PP2JH3GP.js.map} +0 -0
- /package/dist/{chunk-OXJBNGBK.js.map → chunk-PSUB67YB.js.map} +0 -0
- /package/dist/{chunk-KIB7SDIJ.js.map → chunk-Q6YIJGXJ.js.map} +0 -0
- /package/dist/{chunk-ZT3EGNLR.js.map → chunk-QPD426WT.js.map} +0 -0
- /package/dist/{chunk-RLV3PQGH.js.map → chunk-QVO4YOB7.js.map} +0 -0
- /package/dist/{chunk-KQAFEZQX.js.map → chunk-VDX2J7OX.js.map} +0 -0
- /package/dist/{chunk-IK7DCC5H.js.map → chunk-VMGLYN42.js.map} +0 -0
- /package/dist/{chunk-NSKYFGDL.js.map → chunk-X4QQB7O6.js.map} +0 -0
- /package/dist/{first-start-migration-GYJWIH36.js.map → first-start-migration-FF7YFGRP.js.map} +0 -0
- /package/dist/{tier-stats-SKML2OSF.js.map → tier-stats-3LYQ3VV5.js.map} +0 -0
package/src/operator-toolkit.ts
CHANGED
|
@@ -89,6 +89,7 @@ interface QmdRuntimeLike {
|
|
|
89
89
|
isAvailable(): boolean;
|
|
90
90
|
ensureCollection(
|
|
91
91
|
memoryDir: string,
|
|
92
|
+
collectionOrExecution?: string | { signal?: AbortSignal },
|
|
92
93
|
execution?: { signal?: AbortSignal },
|
|
93
94
|
): Promise<"present" | "missing" | "unknown" | "skipped">;
|
|
94
95
|
debugStatus(): string;
|
|
@@ -730,7 +731,10 @@ export async function runOperatorSetup(options: OperatorSetupOptions): Promise<O
|
|
|
730
731
|
|
|
731
732
|
const qmdAvailable = await options.orchestrator.qmd.probe();
|
|
732
733
|
const collectionState = options.orchestrator.config.qmdEnabled
|
|
733
|
-
? await options.orchestrator.qmd.ensureCollection(
|
|
734
|
+
? await options.orchestrator.qmd.ensureCollection(
|
|
735
|
+
options.orchestrator.config.memoryDir,
|
|
736
|
+
options.orchestrator.config.qmdCollection,
|
|
737
|
+
)
|
|
734
738
|
: "skipped";
|
|
735
739
|
const nativeKnowledgeStatus = await summarizeNativeKnowledgeStatus(options.orchestrator.config);
|
|
736
740
|
|
|
@@ -1108,7 +1112,7 @@ export async function runOperatorDoctor(options: OperatorDoctorOptions): Promise
|
|
|
1108
1112
|
|
|
1109
1113
|
const qmdAvailable = await options.orchestrator.qmd.probe();
|
|
1110
1114
|
const collectionState = config.qmdEnabled
|
|
1111
|
-
? await options.orchestrator.qmd.ensureCollection(config.memoryDir)
|
|
1115
|
+
? await options.orchestrator.qmd.ensureCollection(config.memoryDir, config.qmdCollection)
|
|
1112
1116
|
: "skipped";
|
|
1113
1117
|
checks.push({
|
|
1114
1118
|
key: "qmd",
|
package/src/orchestrator.ts
CHANGED
|
@@ -263,6 +263,7 @@ import {
|
|
|
263
263
|
import { normalizeReplaySessionKey, type ReplayTurn } from "./replay/types.js";
|
|
264
264
|
import type { ImportTurn } from "./bulk-import/types.js";
|
|
265
265
|
import {
|
|
266
|
+
type AgentPersonaModelConfig,
|
|
266
267
|
confidenceTier,
|
|
267
268
|
type MemoryIntent,
|
|
268
269
|
type MemorySummary,
|
|
@@ -529,6 +530,32 @@ export interface RecallModeDecision {
|
|
|
529
530
|
effectiveMode: RecallPlanMode;
|
|
530
531
|
graphExpandedIntentDetected: boolean;
|
|
531
532
|
graphReason?: string;
|
|
533
|
+
/**
|
|
534
|
+
* Where `plannedMode` came from (issue #1367 / Option C). `"heuristic"` for
|
|
535
|
+
* the regex planner; `"llm"` when the LLM planner classified it; and
|
|
536
|
+
* `"heuristic-fallback"` when the LLM was enabled but errored/timed out and we
|
|
537
|
+
* fell back. Absent on the synchronous heuristic-only path.
|
|
538
|
+
*/
|
|
539
|
+
plannerSource?: "heuristic" | "llm" | "heuristic-fallback";
|
|
540
|
+
/** Short rationale from the planner (for telemetry / x-ray). */
|
|
541
|
+
plannerReason?: string;
|
|
542
|
+
/** Wall-clock spent in the LLM planner call, when one was made. */
|
|
543
|
+
plannerLatencyMs?: number;
|
|
544
|
+
/** True when the LLM planner was enabled but fell back to the heuristic. */
|
|
545
|
+
plannerFallbackUsed?: boolean;
|
|
546
|
+
/** Model that served the LLM classification, when one was used. */
|
|
547
|
+
plannerModelUsed?: string;
|
|
548
|
+
/**
|
|
549
|
+
* The regex-heuristic baseline mode, captured whenever the LLM planner ran
|
|
550
|
+
* (any source). Lets operators compare planned-vs-heuristic during rollout —
|
|
551
|
+
* distinct from `plannedMode`, which on the LLM path is the LLM's choice.
|
|
552
|
+
*/
|
|
553
|
+
plannerHeuristicMode?: RecallPlanMode;
|
|
554
|
+
/**
|
|
555
|
+
* In shadow mode, the mode the LLM *would* have chosen (recorded for
|
|
556
|
+
* comparison) while `effectiveMode` stays on the heuristic decision.
|
|
557
|
+
*/
|
|
558
|
+
shadowLlmMode?: RecallPlanMode;
|
|
532
559
|
}
|
|
533
560
|
|
|
534
561
|
/**
|
|
@@ -1052,16 +1079,27 @@ export function resolveEffectiveRecallMode(options: {
|
|
|
1052
1079
|
return resolveRecallModeDecision(options).effectiveMode;
|
|
1053
1080
|
}
|
|
1054
1081
|
|
|
1055
|
-
|
|
1082
|
+
interface RecallModeGraphOptions {
|
|
1056
1083
|
plannerEnabled: boolean;
|
|
1057
1084
|
graphRecallEnabled: boolean;
|
|
1058
1085
|
multiGraphMemoryEnabled: boolean;
|
|
1059
1086
|
graphExpandedIntentEnabled?: boolean;
|
|
1060
1087
|
prompt: string;
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
/**
|
|
1091
|
+
* Apply the graph-mode overlay + gating to a planner-produced mode.
|
|
1092
|
+
*
|
|
1093
|
+
* Shared by the heuristic ({@link resolveRecallModeDecision}) and LLM
|
|
1094
|
+
* ({@link resolveRecallModeDecisionAsync}) paths so graph promotion and the
|
|
1095
|
+
* "graph disabled → fall back to full" gating behave identically regardless of
|
|
1096
|
+
* which planner produced `plannedModeRaw` (gotcha #39).
|
|
1097
|
+
*/
|
|
1098
|
+
function finalizeRecallModeDecision(
|
|
1099
|
+
plannedModeRaw: RecallPlanMode,
|
|
1100
|
+
options: RecallModeGraphOptions,
|
|
1101
|
+
): RecallModeDecision {
|
|
1102
|
+
let plannedMode: RecallPlanMode = plannedModeRaw;
|
|
1065
1103
|
const graphExpandedIntentDetected =
|
|
1066
1104
|
options.plannerEnabled &&
|
|
1067
1105
|
options.graphExpandedIntentEnabled === true &&
|
|
@@ -1089,6 +1127,74 @@ export function resolveRecallModeDecision(options: {
|
|
|
1089
1127
|
};
|
|
1090
1128
|
}
|
|
1091
1129
|
|
|
1130
|
+
export function resolveRecallModeDecision(options: RecallModeGraphOptions): RecallModeDecision {
|
|
1131
|
+
const plannedMode: RecallPlanMode = options.plannerEnabled
|
|
1132
|
+
? planRecallMode(options.prompt)
|
|
1133
|
+
: "full";
|
|
1134
|
+
return finalizeRecallModeDecision(plannedMode, options);
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/**
|
|
1138
|
+
* Async recall-mode decision with optional LLM-based planning (issue #1367 /
|
|
1139
|
+
* Option C). Falls back to the heuristic decision when the LLM planner is
|
|
1140
|
+
* disabled, in shadow mode, or unavailable/failed — so this is always safe to
|
|
1141
|
+
* await on the recall hot path. Provider-agnostic: the LLM call routes through
|
|
1142
|
+
* the gateway/fallback chain.
|
|
1143
|
+
*
|
|
1144
|
+
* `recallPlannerEnabled === false` keeps the legacy "always full" behavior and
|
|
1145
|
+
* skips the LLM entirely (the planner as a whole is off).
|
|
1146
|
+
*/
|
|
1147
|
+
export async function resolveRecallModeDecisionAsync(
|
|
1148
|
+
options: RecallModeGraphOptions & {
|
|
1149
|
+
config: PluginConfig;
|
|
1150
|
+
hints?: string[];
|
|
1151
|
+
llm?: FallbackLlmClient;
|
|
1152
|
+
signal?: AbortSignal;
|
|
1153
|
+
},
|
|
1154
|
+
): Promise<RecallModeDecision> {
|
|
1155
|
+
const heuristicDecision = resolveRecallModeDecision(options);
|
|
1156
|
+
|
|
1157
|
+
// Planner globally off, or LLM planning not opted into → heuristic only.
|
|
1158
|
+
if (!options.plannerEnabled || !options.config.recallPlannerLlmEnabled) {
|
|
1159
|
+
return heuristicDecision;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
const { planRecallModeLLM } = await import("./recall-planner-llm.js");
|
|
1163
|
+
const planned = await planRecallModeLLM(
|
|
1164
|
+
options.prompt,
|
|
1165
|
+
options.hints,
|
|
1166
|
+
options.config,
|
|
1167
|
+
options.llm,
|
|
1168
|
+
options.signal,
|
|
1169
|
+
);
|
|
1170
|
+
|
|
1171
|
+
// Shadow mode: record what the LLM would have chosen but keep the heuristic
|
|
1172
|
+
// effective decision (safe rollout / comparison — gotcha #30).
|
|
1173
|
+
if (options.config.recallPlannerShadowMode) {
|
|
1174
|
+
return {
|
|
1175
|
+
...heuristicDecision,
|
|
1176
|
+
plannerSource: planned.source,
|
|
1177
|
+
plannerReason: `shadow:${planned.reason}`,
|
|
1178
|
+
plannerLatencyMs: planned.latencyMs,
|
|
1179
|
+
plannerFallbackUsed: planned.fallbackUsed,
|
|
1180
|
+
plannerModelUsed: planned.modelUsed,
|
|
1181
|
+
plannerHeuristicMode: planned.heuristicMode,
|
|
1182
|
+
shadowLlmMode: planned.mode,
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
const llmDecision = finalizeRecallModeDecision(planned.mode, options);
|
|
1187
|
+
return {
|
|
1188
|
+
...llmDecision,
|
|
1189
|
+
plannerSource: planned.source,
|
|
1190
|
+
plannerReason: planned.reason,
|
|
1191
|
+
plannerLatencyMs: planned.latencyMs,
|
|
1192
|
+
plannerFallbackUsed: planned.fallbackUsed,
|
|
1193
|
+
plannerModelUsed: planned.modelUsed,
|
|
1194
|
+
plannerHeuristicMode: planned.heuristicMode,
|
|
1195
|
+
};
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1092
1198
|
export function hasIdentityRecoveryIntent(prompt: string): boolean {
|
|
1093
1199
|
const text = typeof prompt === "string" ? prompt.toLowerCase() : "";
|
|
1094
1200
|
if (!text) return false;
|
|
@@ -2512,9 +2618,11 @@ export class Orchestrator {
|
|
|
2512
2618
|
namespace,
|
|
2513
2619
|
{ signal: collectionCheckAbort.signal },
|
|
2514
2620
|
)
|
|
2515
|
-
: this.qmd.ensureCollection(
|
|
2516
|
-
|
|
2517
|
-
|
|
2621
|
+
: this.qmd.ensureCollection(
|
|
2622
|
+
this.config.memoryDir,
|
|
2623
|
+
this.config.qmdCollection,
|
|
2624
|
+
{ signal: collectionCheckAbort.signal },
|
|
2625
|
+
),
|
|
2518
2626
|
collectionCheckAbort,
|
|
2519
2627
|
namespace,
|
|
2520
2628
|
);
|
|
@@ -2860,7 +2968,7 @@ export class Orchestrator {
|
|
|
2860
2968
|
namespace,
|
|
2861
2969
|
state: this.config.namespacesEnabled
|
|
2862
2970
|
? await this.namespaceSearchRouter.ensureNamespaceCollection(namespace, { signal })
|
|
2863
|
-
: await this.qmd.ensureCollection(this.config.memoryDir, { signal }),
|
|
2971
|
+
: await this.qmd.ensureCollection(this.config.memoryDir, this.config.qmdCollection, { signal }),
|
|
2864
2972
|
})),
|
|
2865
2973
|
);
|
|
2866
2974
|
|
|
@@ -3390,22 +3498,31 @@ export class Orchestrator {
|
|
|
3390
3498
|
|
|
3391
3499
|
// Use FallbackLlmClient for LLM calls (same pattern as causal-consolidation.ts)
|
|
3392
3500
|
// Honor semanticConsolidationModel: "auto" = primary, "fast" = local fast, or specific model
|
|
3393
|
-
const { FallbackLlmClient } = await import("./fallback-llm.js");
|
|
3501
|
+
const { FallbackLlmClient, gatewayTaskChainOptions } = await import("./fallback-llm.js");
|
|
3394
3502
|
const useGateway = this.config.modelSource === "gateway";
|
|
3395
3503
|
const modelSetting = this.config.semanticConsolidationModel;
|
|
3396
3504
|
if (modelSetting === "fast" && this.fastLlm && !useGateway) {
|
|
3397
3505
|
log.info("[semantic-consolidation] using fast local LLM for synthesis");
|
|
3398
3506
|
}
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3507
|
+
// Gateway routing: an explicit "fast" setting keeps the fast persona chain
|
|
3508
|
+
// (the operator's deliberate fast-tier choice). Otherwise route through the
|
|
3509
|
+
// shared task-chain resolution so taskModelChain applies to semantic
|
|
3510
|
+
// consolidation like every other background task (gotcha #22). Issue #1365.
|
|
3511
|
+
const gatewayChainOptions: { modelChain?: AgentPersonaModelConfig; agentId?: string } =
|
|
3512
|
+
!useGateway
|
|
3513
|
+
? {}
|
|
3514
|
+
: modelSetting === "fast"
|
|
3515
|
+
? (this.config.fastGatewayAgentId
|
|
3516
|
+
? { agentId: this.config.fastGatewayAgentId }
|
|
3517
|
+
: this.config.gatewayAgentId
|
|
3518
|
+
? { agentId: this.config.gatewayAgentId }
|
|
3519
|
+
: {})
|
|
3520
|
+
: gatewayTaskChainOptions(this.config);
|
|
3404
3521
|
const llm = new FallbackLlmClient(
|
|
3405
3522
|
this.config.gatewayConfig,
|
|
3406
3523
|
fallbackLlmRuntimeContextFromConfig(this.config),
|
|
3407
3524
|
);
|
|
3408
|
-
if (!llm.isAvailable(
|
|
3525
|
+
if (!llm.isAvailable(gatewayChainOptions) && !(modelSetting === "fast" && this.fastLlm && !useGateway)) {
|
|
3409
3526
|
log.warn(
|
|
3410
3527
|
"[semantic-consolidation] no LLM available — skipping synthesis",
|
|
3411
3528
|
);
|
|
@@ -3454,7 +3571,7 @@ export class Orchestrator {
|
|
|
3454
3571
|
let response: { content: string } | null = null;
|
|
3455
3572
|
if (useGateway) {
|
|
3456
3573
|
// Gateway model source — use the appropriate agent chain
|
|
3457
|
-
response = await llm.chatCompletion(messages, { ...llmOpts,
|
|
3574
|
+
response = await llm.chatCompletion(messages, { ...llmOpts, ...gatewayChainOptions });
|
|
3458
3575
|
} else if (modelSetting === "fast" && this.fastLlm) {
|
|
3459
3576
|
const fastResult = await this.fastLlm.chatCompletion(messages, {
|
|
3460
3577
|
operation: "semantic-consolidation",
|
|
@@ -5628,6 +5745,12 @@ export class Orchestrator {
|
|
|
5628
5745
|
) {
|
|
5629
5746
|
if (debugSearchOptions?.intent) {
|
|
5630
5747
|
lastHybridTopUpSkippedReason = "intent_hint_active";
|
|
5748
|
+
} else if (this.config.qmdSearchStrategy === "lex") {
|
|
5749
|
+
// BM25-only strategy: a hybrid top-up runs vectorSearch (see
|
|
5750
|
+
// QmdClient.hybridSearch), which would reintroduce the vector path the
|
|
5751
|
+
// operator opted out of. Keep "lex" BM25-only end-to-end so the gate is
|
|
5752
|
+
// uniform across primary + top-up (gotcha #39). Issue #1335 (codex review #1422).
|
|
5753
|
+
lastHybridTopUpSkippedReason = "lex_strategy";
|
|
5631
5754
|
} else {
|
|
5632
5755
|
const hybridResults = options.collection
|
|
5633
5756
|
? await this.qmd.hybridSearch(
|
|
@@ -6382,16 +6505,44 @@ export class Orchestrator {
|
|
|
6382
6505
|
let identityInjectedChars = 0;
|
|
6383
6506
|
let identityInjectionTruncated = false;
|
|
6384
6507
|
timings.queryPolicy = `${queryPolicy.promptShape}/${queryPolicy.retrievalBudgetMode}${queryPolicy.skipConversationRecall ? "/skip-conv" : ""}`;
|
|
6385
|
-
const
|
|
6508
|
+
const recallModeDecisionOptions = {
|
|
6386
6509
|
plannerEnabled: this.config.recallPlannerEnabled,
|
|
6387
6510
|
graphRecallEnabled: this.config.graphRecallEnabled,
|
|
6388
6511
|
multiGraphMemoryEnabled: this.config.multiGraphMemoryEnabled,
|
|
6389
6512
|
graphExpandedIntentEnabled:
|
|
6390
6513
|
this.config.graphExpandedIntentEnabled === true,
|
|
6391
6514
|
prompt,
|
|
6392
|
-
}
|
|
6393
|
-
this.profiler.endSpan("planning", profileTraceId);
|
|
6515
|
+
};
|
|
6394
6516
|
const requestedMode = options.mode;
|
|
6517
|
+
// When the caller forces a mode, skip the (async, possibly LLM-backed)
|
|
6518
|
+
// planner entirely — the decision is overridden anyway. Otherwise consult
|
|
6519
|
+
// the LLM planner when opted in (issue #1367 / Option C); it falls back to
|
|
6520
|
+
// the heuristic on disable / shadow / timeout / error.
|
|
6521
|
+
const recallDecision =
|
|
6522
|
+
requestedMode !== undefined
|
|
6523
|
+
? resolveRecallModeDecision(recallModeDecisionOptions)
|
|
6524
|
+
: await resolveRecallModeDecisionAsync({
|
|
6525
|
+
...recallModeDecisionOptions,
|
|
6526
|
+
config: this.config,
|
|
6527
|
+
signal: options.abortSignal,
|
|
6528
|
+
});
|
|
6529
|
+
if (
|
|
6530
|
+
this.config.recallPlannerTelemetryEnabled &&
|
|
6531
|
+
recallDecision.plannerSource &&
|
|
6532
|
+
recallDecision.plannerSource !== "heuristic"
|
|
6533
|
+
) {
|
|
6534
|
+
log.debug(
|
|
6535
|
+
`[recall-planner] mode=${recallDecision.shadowLlmMode ?? recallDecision.effectiveMode} ` +
|
|
6536
|
+
`source=${recallDecision.plannerSource} ` +
|
|
6537
|
+
`planned=${recallDecision.plannedMode} ` +
|
|
6538
|
+
`heuristic=${recallDecision.plannerHeuristicMode ?? recallDecision.plannedMode} ` +
|
|
6539
|
+
`model=${recallDecision.plannerModelUsed ?? "n/a"} ` +
|
|
6540
|
+
`latencyMs=${recallDecision.plannerLatencyMs ?? 0} ` +
|
|
6541
|
+
`fallback=${recallDecision.plannerFallbackUsed ?? false}` +
|
|
6542
|
+
(recallDecision.shadowLlmMode ? " (shadow)" : ""),
|
|
6543
|
+
);
|
|
6544
|
+
}
|
|
6545
|
+
this.profiler.endSpan("planning", profileTraceId);
|
|
6395
6546
|
const recallMode: RecallPlanMode =
|
|
6396
6547
|
requestedMode ?? recallDecision.effectiveMode;
|
|
6397
6548
|
const queryIntent = inferIntentFromText(retrievalQuery);
|
|
@@ -7780,6 +7931,8 @@ export class Orchestrator {
|
|
|
7780
7931
|
maxResults: qmdFetchLimit,
|
|
7781
7932
|
memoryDir: this.config.memoryDir,
|
|
7782
7933
|
searchOptions: qmdSearchOptions,
|
|
7934
|
+
searchStrategy: this.config.qmdSearchStrategy,
|
|
7935
|
+
subprocessStrategy: this.config.qmdSubprocessStrategy,
|
|
7783
7936
|
});
|
|
7784
7937
|
const cachedQmd = getCachedQmdRecall<Exclude<QmdPhaseResult, null>>(
|
|
7785
7938
|
qmdCacheKey,
|
package/src/qmd-client.test.ts
CHANGED
|
@@ -43,3 +43,88 @@ test("QmdClient rechecks daemon availability before returning unavailable", asyn
|
|
|
43
43
|
assert.equal(results.length, 1);
|
|
44
44
|
assert.equal(results[0]?.transport, "daemon");
|
|
45
45
|
});
|
|
46
|
+
|
|
47
|
+
type SubprocessInternals = {
|
|
48
|
+
available: boolean;
|
|
49
|
+
runQmdCommand: (args: string[]) => Promise<{ stdout: string; stderr: string }>;
|
|
50
|
+
searchViaSubprocess: (
|
|
51
|
+
query: string,
|
|
52
|
+
collection: string,
|
|
53
|
+
maxResults: number,
|
|
54
|
+
) => Promise<QmdSearchResult[]>;
|
|
55
|
+
searchGlobalViaSubprocess: (query: string, maxResults: number) => Promise<QmdSearchResult[]>;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
function captureSubprocessArgs(client: QmdClient): string[][] {
|
|
59
|
+
const calls: string[][] = [];
|
|
60
|
+
const internals = client as unknown as SubprocessInternals;
|
|
61
|
+
internals.available = true;
|
|
62
|
+
internals.runQmdCommand = async (args: string[]) => {
|
|
63
|
+
calls.push(args);
|
|
64
|
+
return { stdout: "[]", stderr: "" };
|
|
65
|
+
};
|
|
66
|
+
return calls;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
test("subprocess fallback defaults to `qmd query` for scoped and global recall", async () => {
|
|
70
|
+
const client = new QmdClient("memories", 3, {});
|
|
71
|
+
const calls = captureSubprocessArgs(client);
|
|
72
|
+
const internals = client as unknown as SubprocessInternals;
|
|
73
|
+
|
|
74
|
+
await internals.searchViaSubprocess("hermes deployment", "memories", 3);
|
|
75
|
+
await internals.searchGlobalViaSubprocess("hermes deployment", 3);
|
|
76
|
+
|
|
77
|
+
assert.equal(calls[0]?.[0], "query", "scoped fallback must default to `qmd query`");
|
|
78
|
+
assert.equal(calls[1]?.[0], "query", "global fallback must default to `qmd query`");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("qmdSubprocessStrategy 'search' applies BM25 to scoped AND global recall (gotcha #39)", async () => {
|
|
82
|
+
// Cursor #1422 review: the gate must be uniform across every subprocess path,
|
|
83
|
+
// not just the scoped one.
|
|
84
|
+
const client = new QmdClient("memories", 3, { qmdSubprocessStrategy: "search" });
|
|
85
|
+
const calls = captureSubprocessArgs(client);
|
|
86
|
+
const internals = client as unknown as SubprocessInternals;
|
|
87
|
+
|
|
88
|
+
await internals.searchViaSubprocess("hermes deployment", "memories", 3);
|
|
89
|
+
await internals.searchGlobalViaSubprocess("hermes deployment", 3);
|
|
90
|
+
|
|
91
|
+
assert.equal(calls[0]?.[0], "search", "scoped fallback must honor BM25 opt-in");
|
|
92
|
+
assert.equal(calls[1]?.[0], "search", "global fallback must honor BM25 opt-in");
|
|
93
|
+
// Global BM25 must NOT pass a collection flag.
|
|
94
|
+
assert.ok(!calls[1]?.includes("-c"), "global BM25 search must not include -c");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("QMD search cache key isolates results by strategy (codex review on #1422)", async () => {
|
|
98
|
+
// Two clients with different strategies must not serve each other's cached
|
|
99
|
+
// results for the same query/collection within the global cache TTL.
|
|
100
|
+
function makeClient(opts: Record<string, unknown>): {
|
|
101
|
+
client: QmdClient;
|
|
102
|
+
calls: string[][];
|
|
103
|
+
} {
|
|
104
|
+
const client = new QmdClient("memories", 3, opts);
|
|
105
|
+
const internals = client as unknown as SubprocessInternals & {
|
|
106
|
+
daemonAvailable: boolean;
|
|
107
|
+
};
|
|
108
|
+
internals.available = true;
|
|
109
|
+
internals.daemonAvailable = false;
|
|
110
|
+
const calls: string[][] = [];
|
|
111
|
+
internals.runQmdCommand = async (args: string[]) => {
|
|
112
|
+
calls.push(args);
|
|
113
|
+
return { stdout: "[]", stderr: "" };
|
|
114
|
+
};
|
|
115
|
+
return { client, calls };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Unique query avoids colliding with cache entries from other tests.
|
|
119
|
+
const query = "strategy-cache-isolation-probe-xyz";
|
|
120
|
+
const a = makeClient({ qmdSearchStrategy: "hybrid" });
|
|
121
|
+
const b = makeClient({ qmdSearchStrategy: "lex" });
|
|
122
|
+
|
|
123
|
+
await a.client.search(query, "memories", 3);
|
|
124
|
+
await b.client.search(query, "memories", 3);
|
|
125
|
+
|
|
126
|
+
// If the cache key ignored strategy, b would hit a's cached entry and never
|
|
127
|
+
// invoke the subprocess. Both must register their own subprocess call.
|
|
128
|
+
assert.equal(a.calls.length, 1, "first strategy populates its own cache entry");
|
|
129
|
+
assert.equal(b.calls.length, 1, "second strategy must NOT reuse the first's cached result");
|
|
130
|
+
});
|
|
@@ -97,6 +97,22 @@ test("qmd recall cache key reflects all defined search options", () => {
|
|
|
97
97
|
assert.notEqual(left, right);
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
+
test("qmd recall cache key reflects search and subprocess strategy (codex review #1422)", () => {
|
|
101
|
+
const base = {
|
|
102
|
+
query: "api rate limit",
|
|
103
|
+
namespaces: ["a", "b"],
|
|
104
|
+
recallMode: "minimal" as const,
|
|
105
|
+
maxResults: 4,
|
|
106
|
+
memoryDir: "/tmp/engram-a",
|
|
107
|
+
};
|
|
108
|
+
const hybrid = buildQmdRecallCacheKey({ ...base, searchStrategy: "hybrid", subprocessStrategy: "query" });
|
|
109
|
+
const lex = buildQmdRecallCacheKey({ ...base, searchStrategy: "lex", subprocessStrategy: "query" });
|
|
110
|
+
const bm25Fallback = buildQmdRecallCacheKey({ ...base, searchStrategy: "hybrid", subprocessStrategy: "search" });
|
|
111
|
+
|
|
112
|
+
assert.notEqual(hybrid, lex, "different search strategies must not share a recall cache entry");
|
|
113
|
+
assert.notEqual(hybrid, bm25Fallback, "different subprocess strategies must not share a recall cache entry");
|
|
114
|
+
});
|
|
115
|
+
|
|
100
116
|
test("qmd recall cache returns cloned values so callers cannot mutate cached entries", () => {
|
|
101
117
|
clearQmdRecallCache();
|
|
102
118
|
const key = buildQmdRecallCacheKey({
|
package/src/qmd-recall-cache.ts
CHANGED
|
@@ -22,6 +22,11 @@ export interface QmdRecallCacheKeyOptions {
|
|
|
22
22
|
memoryDir?: string;
|
|
23
23
|
collection?: string;
|
|
24
24
|
searchOptions?: SearchQueryOptions;
|
|
25
|
+
// QMD search/subprocess strategies change the recalled results, so they must
|
|
26
|
+
// participate in the cache key — otherwise a different strategy's cached QMD
|
|
27
|
+
// phase is served within the TTL (gotcha #37). Issue #1335 (codex review #1422).
|
|
28
|
+
searchStrategy?: string;
|
|
29
|
+
subprocessStrategy?: string;
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
const qmdRecallCache = new Map<string, QmdRecallCacheEntry>();
|
|
@@ -61,6 +66,8 @@ export function buildQmdRecallCacheKey(
|
|
|
61
66
|
memoryDir: normalizePathScope(options.memoryDir),
|
|
62
67
|
collection: options.collection ?? "",
|
|
63
68
|
searchOptions: normalizeSearchOptions(options.searchOptions),
|
|
69
|
+
searchStrategy: options.searchStrategy ?? "",
|
|
70
|
+
subprocessStrategy: options.subprocessStrategy ?? "",
|
|
64
71
|
});
|
|
65
72
|
}
|
|
66
73
|
|
package/src/qmd.test.ts
CHANGED
|
@@ -3,6 +3,7 @@ import test from "node:test";
|
|
|
3
3
|
|
|
4
4
|
import { parseConfig } from "./config.js";
|
|
5
5
|
import {
|
|
6
|
+
buildDefaultStructuredSearches,
|
|
6
7
|
getQmdCommandName,
|
|
7
8
|
getQmdPostInstallProbeTargets,
|
|
8
9
|
parseQmdVersionOutput,
|
|
@@ -67,6 +68,59 @@ test("resolveQmdCapabilities gates qmd 2.5 features by installed version", () =>
|
|
|
67
68
|
assert.equal(v201.legacySkillInstall, true);
|
|
68
69
|
});
|
|
69
70
|
|
|
71
|
+
test("buildDefaultStructuredSearches defaults to the full hybrid lex+vec+hyde plan", () => {
|
|
72
|
+
// Default (no strategy argument) must preserve historical behavior so an
|
|
73
|
+
// upgrade never silently drops vector/HyDE recall. Issue #1335.
|
|
74
|
+
const searches = buildDefaultStructuredSearches("hermes deployment");
|
|
75
|
+
assert.deepEqual(
|
|
76
|
+
searches.map((s) => s.type),
|
|
77
|
+
["lex", "vec", "hyde"],
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("buildDefaultStructuredSearches honors the hybrid strategy explicitly", () => {
|
|
82
|
+
const searches = buildDefaultStructuredSearches("hermes deployment", undefined, "hybrid");
|
|
83
|
+
assert.deepEqual(
|
|
84
|
+
searches.map((s) => s.type),
|
|
85
|
+
["lex", "vec", "hyde"],
|
|
86
|
+
);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("buildDefaultStructuredSearches lex-vec strategy drops the expensive HyDE leg", () => {
|
|
90
|
+
const searches = buildDefaultStructuredSearches("hermes deployment", undefined, "lex-vec");
|
|
91
|
+
assert.deepEqual(
|
|
92
|
+
searches.map((s) => s.type),
|
|
93
|
+
["lex", "vec"],
|
|
94
|
+
);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("buildDefaultStructuredSearches lex strategy is BM25-only", () => {
|
|
98
|
+
const searches = buildDefaultStructuredSearches("hermes deployment", undefined, "lex");
|
|
99
|
+
assert.deepEqual(
|
|
100
|
+
searches.map((s) => s.type),
|
|
101
|
+
["lex"],
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("buildDefaultStructuredSearches lets an explicit per-call override win over the strategy", () => {
|
|
106
|
+
// Per-call structuredSearches override must still take precedence regardless
|
|
107
|
+
// of the configured default strategy.
|
|
108
|
+
const searches = buildDefaultStructuredSearches(
|
|
109
|
+
"hermes deployment",
|
|
110
|
+
{ structuredSearches: [{ type: "vec", query: "hermes deployment" }] },
|
|
111
|
+
"lex",
|
|
112
|
+
);
|
|
113
|
+
assert.deepEqual(
|
|
114
|
+
searches.map((s) => s.type),
|
|
115
|
+
["vec"],
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("buildDefaultStructuredSearches returns empty for blank queries under any strategy", () => {
|
|
120
|
+
assert.deepEqual(buildDefaultStructuredSearches(" ", undefined, "hybrid"), []);
|
|
121
|
+
assert.deepEqual(buildDefaultStructuredSearches(" ", undefined, "lex"), []);
|
|
122
|
+
});
|
|
123
|
+
|
|
70
124
|
test("shouldAutoUpgradeQmd only upgrades below Remnic supported version", () => {
|
|
71
125
|
assert.equal(shouldAutoUpgradeQmd("qmd 2.1.0", "2.5.3"), true);
|
|
72
126
|
assert.equal(shouldAutoUpgradeQmd("qmd 2.5.3", "2.5.3"), false);
|