@remnic/core 9.3.612 → 9.3.614
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 +58 -57
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.d.ts +4 -2
- package/dist/access-http.js +22 -22
- package/dist/access-mcp.d.ts +9 -2
- package/dist/access-mcp.js +19 -19
- package/dist/access-schema.d.ts +12 -12
- package/dist/access-schema.js +3 -3
- package/dist/{access-service-D2J9dh_9.d.ts → access-service-DGG_2xPK.d.ts} +1 -1
- package/dist/access-service.d.ts +2 -2
- package/dist/access-service.js +16 -16
- 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-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-IOTENEVL.js → chunk-7YQFWOF7.js} +57 -50
- package/dist/chunk-7YQFWOF7.js.map +1 -0
- package/dist/{chunk-2QANQKSQ.js → chunk-ADNZVFXG.js} +15 -15
- 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-TH67Q46T.js → chunk-B6FDZPCF.js} +17 -9
- package/dist/chunk-B6FDZPCF.js.map +1 -0
- 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-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-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-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-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-PPPZY2EU.js → chunk-QEMCQFDW.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-HJNQQICM.js → chunk-T5XWMMU2.js} +107 -50
- package/dist/chunk-T5XWMMU2.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-5RPTH6AU.js → chunk-VPGUMLBA.js} +8 -7
- package/dist/chunk-VPGUMLBA.js.map +1 -0
- package/dist/{chunk-KM2A35EO.js → chunk-WB3LYXC5.js} +11 -7
- package/dist/chunk-WB3LYXC5.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/{cli-OrfKXNU4.d.ts → cli-DWeu7eTY.d.ts} +6 -2
- package/dist/cli.d.ts +3 -3
- package/dist/cli.js +60 -59
- 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 +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 +94 -93
- 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 +47 -44
- 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/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/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 +3 -0
- package/src/access-mcp.test.ts +51 -0
- package/src/access-mcp.ts +26 -5
- 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/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-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-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-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-2QANQKSQ.js.map → chunk-ADNZVFXG.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-PPPZY2EU.js.map → chunk-QEMCQFDW.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/access-mcp.test.ts
CHANGED
|
@@ -721,3 +721,54 @@ test("MCP profiling report rejects invalid argument types before dispatch", asyn
|
|
|
721
721
|
assert.equal((badFormat as Record<string, unknown> & { result?: { isError?: boolean } }).result?.isError, true);
|
|
722
722
|
assert.equal((badLimit as Record<string, unknown> & { result?: { isError?: boolean } }).result?.isError, true);
|
|
723
723
|
});
|
|
724
|
+
|
|
725
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
726
|
+
// Issue #1427: opt-out of legacy engram.* tool aliases on tools/list
|
|
727
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
728
|
+
|
|
729
|
+
function listToolNames(response: unknown): string[] {
|
|
730
|
+
const tools = (response as { result?: { tools?: Array<{ name: string }> } }).result?.tools ?? [];
|
|
731
|
+
return tools.map((t) => t.name);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
const TOOLS_LIST_REQUEST = { jsonrpc: "2.0", id: 1, method: "tools/list", params: {} };
|
|
735
|
+
|
|
736
|
+
test("tools/list advertises both remnic.* and engram.* by default (back-compat)", async () => {
|
|
737
|
+
const server = new EngramMcpServer(makeMockService());
|
|
738
|
+
const names = listToolNames(await server.handleRequest(TOOLS_LIST_REQUEST));
|
|
739
|
+
assert.ok(names.includes("remnic.recall"), "canonical name present");
|
|
740
|
+
assert.ok(names.includes("engram.recall"), "legacy alias present by default");
|
|
741
|
+
const legacyCount = names.filter((n) => n.startsWith("engram.")).length;
|
|
742
|
+
assert.ok(legacyCount > 0, "legacy aliases advertised by default");
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
test("tools/list omits engram.* aliases when emitLegacyTools is false", async () => {
|
|
746
|
+
const server = new EngramMcpServer(makeMockService(), { emitLegacyTools: false });
|
|
747
|
+
const names = listToolNames(await server.handleRequest(TOOLS_LIST_REQUEST));
|
|
748
|
+
assert.ok(names.includes("remnic.recall"), "canonical name still present");
|
|
749
|
+
assert.equal(
|
|
750
|
+
names.filter((n) => n.startsWith("engram.")).length,
|
|
751
|
+
0,
|
|
752
|
+
"no engram.* aliases advertised when opted out",
|
|
753
|
+
);
|
|
754
|
+
// Every advertised tool uses the canonical prefix; the surface is halved.
|
|
755
|
+
assert.ok(names.every((n) => n.startsWith("remnic.")), "all advertised tools are canonical");
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
test("emitLegacyTools=false still allows calling tools under BOTH names (advertising-only opt-out)", async () => {
|
|
759
|
+
const server = new EngramMcpServer(makeMockService(), { emitLegacyTools: false });
|
|
760
|
+
// Canonical call works.
|
|
761
|
+
const canonical = await server.handleRequest(makeToolRequest("remnic.recall", { query: "hello" }));
|
|
762
|
+
assert.notEqual(
|
|
763
|
+
(canonical as { result?: { isError?: boolean } }).result?.isError,
|
|
764
|
+
true,
|
|
765
|
+
"canonical remnic.recall call succeeds",
|
|
766
|
+
);
|
|
767
|
+
// Legacy call still dispatches even though it is no longer advertised.
|
|
768
|
+
const legacy = await server.handleRequest(makeToolRequest("engram.recall", { query: "hello" }));
|
|
769
|
+
assert.notEqual(
|
|
770
|
+
(legacy as { result?: { isError?: boolean } }).result?.isError,
|
|
771
|
+
true,
|
|
772
|
+
"legacy engram.recall call still works (callability preserved)",
|
|
773
|
+
);
|
|
774
|
+
});
|
package/src/access-mcp.ts
CHANGED
|
@@ -81,11 +81,15 @@ function toLegacyToolName(name: string): string {
|
|
|
81
81
|
: name;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
function withToolAliases(tool: McpTool): McpTool[] {
|
|
84
|
+
function withToolAliases(tool: McpTool, emitLegacyTools = true): McpTool[] {
|
|
85
85
|
const canonicalName = toCanonicalToolName(tool.name);
|
|
86
86
|
const canonicalTool = canonicalName === tool.name ? tool : { ...tool, name: canonicalName };
|
|
87
87
|
if (canonicalName === tool.name) return [canonicalTool];
|
|
88
|
-
|
|
88
|
+
// Issue #1427: when legacy aliases are opted out, advertise only the
|
|
89
|
+
// canonical `remnic.*` name and drop the `engram.*` duplicate. Tool *calls*
|
|
90
|
+
// still accept both names (the dispatch canonicalizes), so suppressing the
|
|
91
|
+
// alias only trims `tools/list`, not callability.
|
|
92
|
+
return emitLegacyTools ? [canonicalTool, tool] : [canonicalTool];
|
|
89
93
|
}
|
|
90
94
|
|
|
91
95
|
function resolveChatGptInspectorRecallSessionKey(
|
|
@@ -212,13 +216,25 @@ export class EngramMcpServer {
|
|
|
212
216
|
private readonly citationsEnabled: boolean;
|
|
213
217
|
/** Whether to auto-enable citations for Codex adapter connections. */
|
|
214
218
|
private readonly citationsAutoDetect: boolean;
|
|
219
|
+
/**
|
|
220
|
+
* Whether to advertise legacy `engram.*` tool aliases alongside the canonical
|
|
221
|
+
* `remnic.*` names (issue #1427). Defaults to true for backward compatibility;
|
|
222
|
+
* set false to halve the advertised `tools/list` surface.
|
|
223
|
+
*/
|
|
224
|
+
private readonly emitLegacyTools: boolean;
|
|
215
225
|
|
|
216
226
|
constructor(
|
|
217
227
|
private readonly service: EngramAccessService,
|
|
218
|
-
options: {
|
|
228
|
+
options: {
|
|
229
|
+
principal?: string;
|
|
230
|
+
citationsEnabled?: boolean;
|
|
231
|
+
citationsAutoDetect?: boolean;
|
|
232
|
+
emitLegacyTools?: boolean;
|
|
233
|
+
} = {},
|
|
219
234
|
) {
|
|
220
235
|
this.citationsEnabled = options.citationsEnabled === true;
|
|
221
236
|
this.citationsAutoDetect = options.citationsAutoDetect !== false;
|
|
237
|
+
this.emitLegacyTools = options.emitLegacyTools !== false;
|
|
222
238
|
this.authenticatedPrincipal =
|
|
223
239
|
options.principal?.trim() ||
|
|
224
240
|
readEnvVar("OPENCLAW_ENGRAM_ACCESS_PRINCIPAL")?.trim() ||
|
|
@@ -1703,7 +1719,7 @@ export class EngramMcpServer {
|
|
|
1703
1719
|
additionalProperties: false,
|
|
1704
1720
|
},
|
|
1705
1721
|
},
|
|
1706
|
-
].flatMap((tool) => withToolAliases(tool));
|
|
1722
|
+
].flatMap((tool) => withToolAliases(tool, this.emitLegacyTools));
|
|
1707
1723
|
}
|
|
1708
1724
|
|
|
1709
1725
|
/** Get clientInfo for a specific MCP session. Returns undefined for non-MCP requests. */
|
|
@@ -1985,7 +2001,12 @@ export class EngramMcpServer {
|
|
|
1985
2001
|
}
|
|
1986
2002
|
|
|
1987
2003
|
private toolAcceptsArgument(name: string, key: string): boolean {
|
|
1988
|
-
|
|
2004
|
+
// Match by canonical name so argument validation resolves whether the
|
|
2005
|
+
// caller used the `engram.*` or `remnic.*` name and regardless of whether
|
|
2006
|
+
// legacy aliases are advertised (issue #1427) — a tool stays callable under
|
|
2007
|
+
// both names even when only the canonical alias appears in `tools/list`.
|
|
2008
|
+
const target = toCanonicalToolName(name);
|
|
2009
|
+
const tool = this.tools.find((entry) => toCanonicalToolName(entry.name) === target);
|
|
1989
2010
|
const inputSchema = getObjectProperties(tool?.inputSchema);
|
|
1990
2011
|
const properties = getObjectProperties(inputSchema?.properties);
|
|
1991
2012
|
if (properties && Object.prototype.hasOwnProperty.call(properties, key)) {
|
|
@@ -198,6 +198,46 @@ test("active recall cache hits report cache-hit latency instead of reusing gener
|
|
|
198
198
|
assert.equal(second.latencyMs, 1);
|
|
199
199
|
});
|
|
200
200
|
|
|
201
|
+
test("active recall cache hits append a fresh transcript entry", async () => {
|
|
202
|
+
const transcriptDir = await mkdtemp(path.join(os.tmpdir(), "active-recall-cache-transcript-"));
|
|
203
|
+
let generateCalls = 0;
|
|
204
|
+
const engine = createActiveRecallEngine(
|
|
205
|
+
{
|
|
206
|
+
async recall() {
|
|
207
|
+
return "CI worker drain after Redis reconnect storm.";
|
|
208
|
+
},
|
|
209
|
+
async generateSummary() {
|
|
210
|
+
generateCalls += 1;
|
|
211
|
+
return { text: "Redis reconnect storm caused the worker drain." };
|
|
212
|
+
},
|
|
213
|
+
now: (() => {
|
|
214
|
+
let tick = 40_000;
|
|
215
|
+
return () => tick++;
|
|
216
|
+
})(),
|
|
217
|
+
},
|
|
218
|
+
baseConfig({
|
|
219
|
+
cacheTtlMs: 5000,
|
|
220
|
+
persistTranscripts: true,
|
|
221
|
+
transcriptDir,
|
|
222
|
+
}),
|
|
223
|
+
);
|
|
224
|
+
|
|
225
|
+
const first = await engine.run(baseInput());
|
|
226
|
+
const second = await engine.run(baseInput());
|
|
227
|
+
|
|
228
|
+
assert.equal(generateCalls, 1);
|
|
229
|
+
assert.equal(first.cacheHit, false);
|
|
230
|
+
assert.equal(second.cacheHit, true);
|
|
231
|
+
assert.ok(first.transcriptPath, "expected first transcript path");
|
|
232
|
+
assert.ok(second.transcriptPath, "expected cache-hit transcript path");
|
|
233
|
+
|
|
234
|
+
const raw = await readFile(second.transcriptPath ?? "", "utf8");
|
|
235
|
+
const entries = raw.trim().split("\n").map((line) => JSON.parse(line) as { cacheHit: boolean });
|
|
236
|
+
assert.equal(entries.length, 2);
|
|
237
|
+
assert.equal(entries[0].cacheHit, false);
|
|
238
|
+
assert.equal(entries[1].cacheHit, true);
|
|
239
|
+
});
|
|
240
|
+
|
|
201
241
|
test("active recall cache stores an isolated snapshot instead of a mutable caller reference", async () => {
|
|
202
242
|
let generateCalls = 0;
|
|
203
243
|
const engine = createActiveRecallEngine(
|
package/src/active-recall.ts
CHANGED
|
@@ -361,11 +361,26 @@ export function createActiveRecallEngine(
|
|
|
361
361
|
}
|
|
362
362
|
const cached = cache.get(cacheKey);
|
|
363
363
|
if (cacheEnabled && cached) {
|
|
364
|
-
|
|
364
|
+
const result: ActiveRecallResult = {
|
|
365
365
|
...cloneRecallResult(cached.value),
|
|
366
366
|
latencyMs: Math.max(0, now() - currentTime),
|
|
367
367
|
cacheHit: true,
|
|
368
368
|
};
|
|
369
|
+
result.transcriptPath = null;
|
|
370
|
+
if (config.persistTranscripts) {
|
|
371
|
+
try {
|
|
372
|
+
result.transcriptPath = await appendActiveRecallTranscript(
|
|
373
|
+
config.transcriptDir,
|
|
374
|
+
input,
|
|
375
|
+
config,
|
|
376
|
+
result,
|
|
377
|
+
queryBundle,
|
|
378
|
+
);
|
|
379
|
+
} catch {
|
|
380
|
+
result.transcriptPath = null;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return result;
|
|
369
384
|
}
|
|
370
385
|
|
|
371
386
|
const start = currentTime;
|
|
@@ -451,9 +466,11 @@ export function createActiveRecallEngine(
|
|
|
451
466
|
|
|
452
467
|
if (cacheEnabled) {
|
|
453
468
|
const completedAt = now();
|
|
469
|
+
const cachedValue = cloneRecallResult(result);
|
|
470
|
+
cachedValue.transcriptPath = null;
|
|
454
471
|
cache.set(cacheKey, {
|
|
455
472
|
expiresAt: completedAt + config.cacheTtlMs,
|
|
456
|
-
value:
|
|
473
|
+
value: cachedValue,
|
|
457
474
|
});
|
|
458
475
|
enforceCacheLimit(cache);
|
|
459
476
|
}
|
package/src/behavior-learner.ts
CHANGED
|
@@ -68,13 +68,15 @@ function aggregateSignalPressure(signals: BehaviorSignalEvent[]): number {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
function selectRecentSignals(input: BehaviorLearnerInput): BehaviorSignalEvent[] {
|
|
71
|
-
if (input.learningWindowDays <= 0) return [...input.signals];
|
|
72
71
|
const nowMs = (input.now ?? new Date()).getTime();
|
|
73
|
-
const minTs =
|
|
72
|
+
const minTs =
|
|
73
|
+
input.learningWindowDays <= 0
|
|
74
|
+
? Number.NEGATIVE_INFINITY
|
|
75
|
+
: nowMs - input.learningWindowDays * 86_400_000;
|
|
74
76
|
return input.signals.filter((signal) => {
|
|
75
77
|
const ts = Date.parse(signal.timestamp);
|
|
76
78
|
if (!Number.isFinite(ts)) return false;
|
|
77
|
-
return ts >= minTs;
|
|
79
|
+
return ts >= minTs && ts <= nowMs;
|
|
78
80
|
});
|
|
79
81
|
}
|
|
80
82
|
|
|
@@ -2,6 +2,7 @@ import test from "node:test";
|
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { SmartBuffer } from "./buffer.js";
|
|
4
4
|
import { parseConfig } from "./config.js";
|
|
5
|
+
import type { BufferSurpriseProbe } from "./buffer.js";
|
|
5
6
|
import type { BufferState, BufferTurn } from "./types.js";
|
|
6
7
|
|
|
7
8
|
class FakeStorage {
|
|
@@ -111,6 +112,63 @@ test("SmartBuffer serializes concurrent addTurn mutations", async () => {
|
|
|
111
112
|
);
|
|
112
113
|
});
|
|
113
114
|
|
|
115
|
+
test("SmartBuffer ignores stale surprise promotion after newer turns arrive", async () => {
|
|
116
|
+
const storage = new FakeStorage({
|
|
117
|
+
turns: [],
|
|
118
|
+
lastExtractionAt: null,
|
|
119
|
+
extractionCount: 0,
|
|
120
|
+
});
|
|
121
|
+
let resolveFirstProbe = (_score: number): void => {
|
|
122
|
+
throw new Error("first surprise probe did not start");
|
|
123
|
+
};
|
|
124
|
+
let markFirstProbeStarted = (): void => {
|
|
125
|
+
throw new Error("probe start marker was not initialized");
|
|
126
|
+
};
|
|
127
|
+
const firstProbeStarted = new Promise<void>((resolve) => {
|
|
128
|
+
markFirstProbeStarted = resolve;
|
|
129
|
+
});
|
|
130
|
+
const probe: BufferSurpriseProbe = {
|
|
131
|
+
async scoreTurn(_bufferKey, turn) {
|
|
132
|
+
if (turn.content !== "turn A") return null;
|
|
133
|
+
markFirstProbeStarted();
|
|
134
|
+
return new Promise<number>((probeResolve) => {
|
|
135
|
+
resolveFirstProbe = probeResolve;
|
|
136
|
+
});
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
const buffer = new SmartBuffer(
|
|
140
|
+
parseConfig({
|
|
141
|
+
bufferSurpriseTriggerEnabled: true,
|
|
142
|
+
bufferSurpriseThreshold: 0.35,
|
|
143
|
+
bufferSurpriseProbeTimeoutMs: 10_000,
|
|
144
|
+
triggerMode: "smart",
|
|
145
|
+
}),
|
|
146
|
+
storage as any,
|
|
147
|
+
probe,
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const firstOutcomePromise = buffer.addTurnWithOutcome(
|
|
151
|
+
"thread-a",
|
|
152
|
+
makeTurn("thread-a", "turn A"),
|
|
153
|
+
);
|
|
154
|
+
await firstProbeStarted;
|
|
155
|
+
|
|
156
|
+
const secondOutcome = await buffer.addTurnWithOutcome(
|
|
157
|
+
"thread-a",
|
|
158
|
+
makeTurn("thread-a", "turn B"),
|
|
159
|
+
);
|
|
160
|
+
assert.deepEqual(secondOutcome, { decision: "keep_buffering" });
|
|
161
|
+
|
|
162
|
+
resolveFirstProbe(1);
|
|
163
|
+
const firstOutcome = await firstOutcomePromise;
|
|
164
|
+
|
|
165
|
+
assert.deepEqual(firstOutcome, { decision: "keep_buffering" });
|
|
166
|
+
assert.deepEqual(
|
|
167
|
+
buffer.getTurns("thread-a").map((turn) => turn.content),
|
|
168
|
+
["turn A", "turn B"],
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
|
|
114
172
|
test("SmartBuffer clearAfterExtraction only clears the targeted logical session", async () => {
|
|
115
173
|
const storage = new FakeStorage({
|
|
116
174
|
turns: [],
|
|
@@ -643,7 +643,7 @@ test("flag on: slow-probe promotion survives prefix clears before the scored tur
|
|
|
643
643
|
);
|
|
644
644
|
});
|
|
645
645
|
|
|
646
|
-
test("flag on: slow-probe promotion
|
|
646
|
+
test("flag on: slow-probe promotion is ignored after later appends to same buffer", async () => {
|
|
647
647
|
const storage = new FakeStorage(emptyBuffer());
|
|
648
648
|
let releaseOldProbe: (() => void) | null = null;
|
|
649
649
|
let oldProbeStarted: (() => void) | null = null;
|
|
@@ -684,8 +684,8 @@ test("flag on: slow-probe promotion survives later appends to same buffer", asyn
|
|
|
684
684
|
(releaseOldProbe as () => void)();
|
|
685
685
|
assert.equal(
|
|
686
686
|
await oldDecision,
|
|
687
|
-
"
|
|
688
|
-
"a later append must
|
|
687
|
+
"keep_buffering",
|
|
688
|
+
"a later append must suppress stale surprise promotion for the older turn",
|
|
689
689
|
);
|
|
690
690
|
const turns = buffer.getTurns("sess-1");
|
|
691
691
|
assert.equal(turns.length, 2);
|
|
@@ -695,18 +695,9 @@ test("flag on: slow-probe promotion survives later appends to same buffer", asyn
|
|
|
695
695
|
|
|
696
696
|
test("flag on: extraction clear preserves turns appended after the queued snapshot", async () => {
|
|
697
697
|
const storage = new FakeStorage(emptyBuffer());
|
|
698
|
-
let releaseOldProbe: (() => void) | null = null;
|
|
699
|
-
let oldProbeStarted: (() => void) | null = null;
|
|
700
|
-
const oldProbeStartedPromise = new Promise<void>((resolve) => {
|
|
701
|
-
oldProbeStarted = resolve;
|
|
702
|
-
});
|
|
703
698
|
const probe: BufferSurpriseProbe = {
|
|
704
699
|
async scoreTurn(_key, turn) {
|
|
705
|
-
|
|
706
|
-
oldProbeStarted?.();
|
|
707
|
-
return new Promise<number | null>((resolve) => {
|
|
708
|
-
releaseOldProbe = () => resolve(0.99);
|
|
709
|
-
});
|
|
700
|
+
return turn.content === "old surprising turn" ? 0.99 : null;
|
|
710
701
|
},
|
|
711
702
|
};
|
|
712
703
|
const config = parseConfig({
|
|
@@ -722,11 +713,6 @@ test("flag on: extraction clear preserves turns appended after the queued snapsh
|
|
|
722
713
|
"sess-1",
|
|
723
714
|
makeTurn("sess-1", "old surprising turn"),
|
|
724
715
|
);
|
|
725
|
-
await oldProbeStartedPromise;
|
|
726
|
-
|
|
727
|
-
await buffer.addTurn("sess-1", makeTurn("sess-1", "newer ordinary turn"));
|
|
728
|
-
assert.ok(releaseOldProbe);
|
|
729
|
-
(releaseOldProbe as () => void)();
|
|
730
716
|
assert.equal(await oldDecision, "extract_now");
|
|
731
717
|
|
|
732
718
|
const queuedSnapshot = buffer.getTurns("sess-1");
|
package/src/buffer.ts
CHANGED
|
@@ -85,7 +85,8 @@ interface AddTurnMutationResult {
|
|
|
85
85
|
decision: TriggerDecision;
|
|
86
86
|
signalLevel: SignalLevel;
|
|
87
87
|
priorTurns: BufferTurn[];
|
|
88
|
-
|
|
88
|
+
activeTurnsSnapshot: BufferTurn[];
|
|
89
|
+
retainedTurnsSnapshot: BufferTurn[];
|
|
89
90
|
turnCountInWindow: number;
|
|
90
91
|
}
|
|
91
92
|
|
|
@@ -298,9 +299,10 @@ export class SmartBuffer {
|
|
|
298
299
|
const shouldPromote = surprise > this.config.bufferSurpriseThreshold;
|
|
299
300
|
let triggered = false;
|
|
300
301
|
if (shouldPromote) {
|
|
301
|
-
const currentTurns = await this.
|
|
302
|
+
const currentTurns = await this.getExtractionTurnsIfBufferSnapshotStillCurrent(
|
|
302
303
|
bufferKey,
|
|
303
|
-
mutation.
|
|
304
|
+
mutation.activeTurnsSnapshot,
|
|
305
|
+
mutation.retainedTurnsSnapshot,
|
|
304
306
|
);
|
|
305
307
|
if (currentTurns) {
|
|
306
308
|
log.debug(
|
|
@@ -361,7 +363,8 @@ export class SmartBuffer {
|
|
|
361
363
|
const entry = this.entryFor(bufferKey);
|
|
362
364
|
const priorTurns = entry.turns.slice();
|
|
363
365
|
entry.turns.push(turn);
|
|
364
|
-
const
|
|
366
|
+
const activeTurnsSnapshot = entry.turns.map(copyBufferTurn);
|
|
367
|
+
const retainedTurnsSnapshot = (entry.retainedTurns ?? []).map(copyBufferTurn);
|
|
365
368
|
if (bufferKey === "default") {
|
|
366
369
|
this.state.turns = entry.turns;
|
|
367
370
|
}
|
|
@@ -376,24 +379,26 @@ export class SmartBuffer {
|
|
|
376
379
|
decision,
|
|
377
380
|
signalLevel: signal.level,
|
|
378
381
|
priorTurns,
|
|
379
|
-
|
|
382
|
+
activeTurnsSnapshot,
|
|
383
|
+
retainedTurnsSnapshot,
|
|
380
384
|
turnCountInWindow,
|
|
381
385
|
};
|
|
382
386
|
}
|
|
383
387
|
|
|
384
|
-
private async
|
|
388
|
+
private async getExtractionTurnsIfBufferSnapshotStillCurrent(
|
|
385
389
|
bufferKey: string,
|
|
386
|
-
|
|
390
|
+
activeTurnsSnapshot: readonly BufferTurn[],
|
|
391
|
+
retainedTurnsSnapshot: readonly BufferTurn[],
|
|
387
392
|
): Promise<BufferTurn[] | null> {
|
|
388
393
|
return this.enqueueMutation(async () => {
|
|
389
394
|
await this.loadUnlocked();
|
|
390
395
|
const entry = this.peekEntry(bufferKey);
|
|
391
396
|
if (!entry) return null;
|
|
392
|
-
const stillCurrent = entry.turns.some((turn) =>
|
|
393
|
-
bufferTurnsEqual(turn, turnSnapshot),
|
|
394
|
-
);
|
|
395
|
-
if (!stillCurrent) return null;
|
|
396
397
|
const retained = entry.retainedTurns ?? [];
|
|
398
|
+
const stillCurrent =
|
|
399
|
+
bufferTurnArrayIsSuffixOfSnapshot(entry.turns, activeTurnsSnapshot) &&
|
|
400
|
+
bufferTurnArraysEqual(retained, retainedTurnsSnapshot);
|
|
401
|
+
if (!stillCurrent) return null;
|
|
397
402
|
return [...retained, ...entry.turns];
|
|
398
403
|
});
|
|
399
404
|
}
|
|
@@ -802,6 +807,29 @@ function bufferTurnsEqual(left: BufferTurn | undefined, right: BufferTurn): bool
|
|
|
802
807
|
);
|
|
803
808
|
}
|
|
804
809
|
|
|
810
|
+
function bufferTurnArraysEqual(
|
|
811
|
+
left: readonly BufferTurn[],
|
|
812
|
+
right: readonly BufferTurn[],
|
|
813
|
+
): boolean {
|
|
814
|
+
return (
|
|
815
|
+
left.length === right.length &&
|
|
816
|
+
left.every((turn, index) => bufferTurnsEqual(turn, right[index]))
|
|
817
|
+
);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
function bufferTurnArrayIsSuffixOfSnapshot(
|
|
821
|
+
liveTurns: readonly BufferTurn[],
|
|
822
|
+
snapshot: readonly BufferTurn[],
|
|
823
|
+
): boolean {
|
|
824
|
+
if (liveTurns.length === 0 || liveTurns.length > snapshot.length) {
|
|
825
|
+
return false;
|
|
826
|
+
}
|
|
827
|
+
const offset = snapshot.length - liveTurns.length;
|
|
828
|
+
return liveTurns.every((turn, index) =>
|
|
829
|
+
bufferTurnsEqual(turn, snapshot[offset + index]),
|
|
830
|
+
);
|
|
831
|
+
}
|
|
832
|
+
|
|
805
833
|
function liveTurnsFromExtractionSnapshot(
|
|
806
834
|
entry: BufferEntryState,
|
|
807
835
|
extractedTurns: readonly BufferTurn[],
|
package/src/calibration.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { createHash } from "node:crypto";
|
|
|
16
16
|
import path from "node:path";
|
|
17
17
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
18
18
|
import { FallbackLlmClient } from "./fallback-llm.js";
|
|
19
|
-
import type { GatewayConfig, MemoryFile } from "./types.js";
|
|
19
|
+
import type { AgentPersonaModelConfig, GatewayConfig, MemoryFile } from "./types.js";
|
|
20
20
|
import { listJsonFiles, readJsonFile } from "./json-store.js";
|
|
21
21
|
import { isRecord } from "./store-contract.js";
|
|
22
22
|
import { log } from "./logger.js";
|
|
@@ -226,6 +226,7 @@ export async function synthesizeCalibrationRules(
|
|
|
226
226
|
llm: FallbackLlmClient,
|
|
227
227
|
existingRules: CalibrationRule[],
|
|
228
228
|
agentId?: string,
|
|
229
|
+
modelChain?: AgentPersonaModelConfig,
|
|
229
230
|
): Promise<CalibrationRule[]> {
|
|
230
231
|
if (corrections.length < 2) return [];
|
|
231
232
|
|
|
@@ -244,7 +245,7 @@ export async function synthesizeCalibrationRules(
|
|
|
244
245
|
{ role: "system", content: CLUSTER_PROMPT },
|
|
245
246
|
{ role: "user", content: `Here are ${corrections.length} corrections from this user:\n\n${correctionText}${existingRulesText}` },
|
|
246
247
|
],
|
|
247
|
-
{ temperature: 0.3, maxTokens: 3000, agentId },
|
|
248
|
+
{ temperature: 0.3, maxTokens: 3000, agentId, modelChain },
|
|
248
249
|
);
|
|
249
250
|
|
|
250
251
|
if (!response?.content) return [];
|
|
@@ -349,13 +350,14 @@ export async function runCalibrationConsolidation(options: {
|
|
|
349
350
|
memoryDir: string;
|
|
350
351
|
gatewayConfig?: GatewayConfig;
|
|
351
352
|
gatewayAgentId?: string;
|
|
353
|
+
modelChain?: AgentPersonaModelConfig;
|
|
352
354
|
workspaceDir?: string;
|
|
353
355
|
}): Promise<CalibrationRule[]> {
|
|
354
356
|
try {
|
|
355
357
|
const llm = new FallbackLlmClient(options.gatewayConfig, {
|
|
356
358
|
workspaceDir: options.workspaceDir,
|
|
357
359
|
});
|
|
358
|
-
if (!llm.isAvailable(options.gatewayAgentId)) {
|
|
360
|
+
if (!llm.isAvailable({ agentId: options.gatewayAgentId, modelChain: options.modelChain })) {
|
|
359
361
|
log.debug("[calibration] no LLM available — skipping consolidation");
|
|
360
362
|
return [];
|
|
361
363
|
}
|
|
@@ -368,7 +370,7 @@ export async function runCalibrationConsolidation(options: {
|
|
|
368
370
|
|
|
369
371
|
const existingIndex = await readCalibrationIndex(options.memoryDir);
|
|
370
372
|
|
|
371
|
-
const newRules = await synthesizeCalibrationRules(corrections, llm, existingIndex.rules, options.gatewayAgentId);
|
|
373
|
+
const newRules = await synthesizeCalibrationRules(corrections, llm, existingIndex.rules, options.gatewayAgentId, options.modelChain);
|
|
372
374
|
if (newRules.length === 0) {
|
|
373
375
|
log.debug("[calibration] no new calibration rules synthesized");
|
|
374
376
|
return existingIndex.rules;
|
|
@@ -414,6 +416,8 @@ export async function runCalibrationIfEnabled(options: {
|
|
|
414
416
|
memoryDir: string;
|
|
415
417
|
calibrationEnabled: boolean;
|
|
416
418
|
gatewayConfig?: GatewayConfig;
|
|
419
|
+
gatewayAgentId?: string;
|
|
420
|
+
modelChain?: AgentPersonaModelConfig;
|
|
417
421
|
workspaceDir?: string;
|
|
418
422
|
}): Promise<CalibrationRule[]> {
|
|
419
423
|
if (!options.calibrationEnabled) {
|
|
@@ -422,6 +426,8 @@ export async function runCalibrationIfEnabled(options: {
|
|
|
422
426
|
return runCalibrationConsolidation({
|
|
423
427
|
memoryDir: options.memoryDir,
|
|
424
428
|
gatewayConfig: options.gatewayConfig,
|
|
429
|
+
gatewayAgentId: options.gatewayAgentId,
|
|
430
|
+
modelChain: options.modelChain,
|
|
425
431
|
workspaceDir: options.workspaceDir,
|
|
426
432
|
});
|
|
427
433
|
}
|
|
@@ -49,14 +49,24 @@ async function withTrajectoryStore<T>(
|
|
|
49
49
|
|
|
50
50
|
function llmStub() {
|
|
51
51
|
let calls = 0;
|
|
52
|
+
let availableOptions: unknown;
|
|
53
|
+
let completionOptions: unknown;
|
|
52
54
|
return {
|
|
53
55
|
get calls() {
|
|
54
56
|
return calls;
|
|
55
57
|
},
|
|
56
|
-
|
|
58
|
+
get availableOptions() {
|
|
59
|
+
return availableOptions;
|
|
60
|
+
},
|
|
61
|
+
get completionOptions() {
|
|
62
|
+
return completionOptions;
|
|
63
|
+
},
|
|
64
|
+
isAvailable(options?: unknown) {
|
|
65
|
+
availableOptions = options;
|
|
57
66
|
return true;
|
|
58
67
|
},
|
|
59
|
-
async chatCompletion() {
|
|
68
|
+
async chatCompletion(_messages?: unknown, options?: unknown) {
|
|
69
|
+
completionOptions = options;
|
|
60
70
|
calls += 1;
|
|
61
71
|
return {
|
|
62
72
|
content: JSON.stringify({
|
|
@@ -154,6 +164,41 @@ test("deriveCausalPromotionCandidates calls LLM when recurrence session and succ
|
|
|
154
164
|
);
|
|
155
165
|
});
|
|
156
166
|
|
|
167
|
+
test("deriveCausalPromotionCandidates forwards task model chain to availability and chat completion", async () => {
|
|
168
|
+
const llm = llmStub();
|
|
169
|
+
const modelChain = {
|
|
170
|
+
primary: "openai/task-primary",
|
|
171
|
+
fallbacks: ["openai/task-fallback"],
|
|
172
|
+
};
|
|
173
|
+
await withTrajectoryStore(
|
|
174
|
+
[
|
|
175
|
+
trajectory("t1", "a", "success"),
|
|
176
|
+
trajectory("t2", "b", "success"),
|
|
177
|
+
trajectory("t3", "c", "partial"),
|
|
178
|
+
],
|
|
179
|
+
async ({ memoryDir, storeDir }) => {
|
|
180
|
+
const candidates = await deriveCausalPromotionCandidates({
|
|
181
|
+
memoryDir,
|
|
182
|
+
causalTrajectoryStoreDir: storeDir,
|
|
183
|
+
config: { minRecurrence: 3, minSessions: 2, successThreshold: 0.7 },
|
|
184
|
+
gatewayAgentId: "expensive-agent",
|
|
185
|
+
modelChain,
|
|
186
|
+
llmClient: llm,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
assert.equal(llm.calls, 1);
|
|
190
|
+
assert.equal(candidates.length, 1);
|
|
191
|
+
assert.deepEqual(llm.availableOptions, { agentId: "expensive-agent", modelChain });
|
|
192
|
+
assert.deepEqual(llm.completionOptions, {
|
|
193
|
+
temperature: 0.2,
|
|
194
|
+
maxTokens: 2000,
|
|
195
|
+
agentId: "expensive-agent",
|
|
196
|
+
modelChain,
|
|
197
|
+
});
|
|
198
|
+
},
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
|
|
157
202
|
test("deriveCausalPromotionCandidates validates LLM-derived rule fields", async () => {
|
|
158
203
|
const llm = llmWithContent(JSON.stringify({
|
|
159
204
|
rules: [
|
|
@@ -18,7 +18,7 @@ import { readChainIndex, resolveChainsDir, type CausalChainIndex, type CausalEdg
|
|
|
18
18
|
import { listJsonFiles, readJsonFile } from "./json-store.js";
|
|
19
19
|
import { isRecord } from "./store-contract.js";
|
|
20
20
|
import { FallbackLlmClient, fallbackLlmRuntimeContextFromConfig } from "./fallback-llm.js";
|
|
21
|
-
import type { GatewayConfig, MemoryFile, PluginConfig } from "./types.js";
|
|
21
|
+
import type { AgentPersonaModelConfig, GatewayConfig, MemoryFile, PluginConfig } from "./types.js";
|
|
22
22
|
import path from "node:path";
|
|
23
23
|
import { log } from "./logger.js";
|
|
24
24
|
import { runPostConsolidationMaterialize } from "./connectors/codex-materialize-runner.js";
|
|
@@ -64,10 +64,10 @@ export interface LlmConsolidationResult {
|
|
|
64
64
|
const CAUSAL_RULE_CATEGORIES = new Set(["rule", "principle", "preference"]);
|
|
65
65
|
|
|
66
66
|
interface ConsolidationLlmClient {
|
|
67
|
-
isAvailable(agentId?: string): boolean;
|
|
67
|
+
isAvailable(options?: { agentId?: string; modelChain?: AgentPersonaModelConfig }): boolean;
|
|
68
68
|
chatCompletion(
|
|
69
69
|
messages: Array<{ role: "system" | "user" | "assistant"; content: string }>,
|
|
70
|
-
options?: { temperature?: number; maxTokens?: number; agentId?: string },
|
|
70
|
+
options?: { temperature?: number; maxTokens?: number; agentId?: string; modelChain?: AgentPersonaModelConfig },
|
|
71
71
|
): Promise<{ content: string } | null>;
|
|
72
72
|
}
|
|
73
73
|
|
|
@@ -188,14 +188,14 @@ If no clear patterns exist, return {"rules": [], "preferences": []}.`;
|
|
|
188
188
|
async function consolidateWithLlm(
|
|
189
189
|
context: string,
|
|
190
190
|
llm: ConsolidationLlmClient,
|
|
191
|
-
agentId?: string,
|
|
191
|
+
options: { agentId?: string; modelChain?: AgentPersonaModelConfig } = {},
|
|
192
192
|
): Promise<LlmConsolidationResult> {
|
|
193
193
|
const response = await llm.chatCompletion(
|
|
194
194
|
[
|
|
195
195
|
{ role: "system", content: CONSOLIDATION_PROMPT },
|
|
196
196
|
{ role: "user", content: context },
|
|
197
197
|
],
|
|
198
|
-
{ temperature: 0.2, maxTokens: 2000, agentId },
|
|
198
|
+
{ temperature: 0.2, maxTokens: 2000, agentId: options.agentId, modelChain: options.modelChain },
|
|
199
199
|
);
|
|
200
200
|
|
|
201
201
|
if (!response?.content) {
|
|
@@ -369,6 +369,7 @@ export async function deriveCausalPromotionCandidates(options: {
|
|
|
369
369
|
config: ConsolidationConfig;
|
|
370
370
|
gatewayConfig?: GatewayConfig;
|
|
371
371
|
gatewayAgentId?: string;
|
|
372
|
+
modelChain?: AgentPersonaModelConfig;
|
|
372
373
|
workspaceDir?: string;
|
|
373
374
|
pluginConfig?: PluginConfig;
|
|
374
375
|
llmClient?: ConsolidationLlmClient;
|
|
@@ -400,13 +401,14 @@ export async function deriveCausalPromotionCandidates(options: {
|
|
|
400
401
|
})
|
|
401
402
|
: { workspaceDir: options.workspaceDir },
|
|
402
403
|
);
|
|
403
|
-
|
|
404
|
+
const llmOptions = { agentId: options.gatewayAgentId, modelChain: options.modelChain };
|
|
405
|
+
if (!llm.isAvailable(llmOptions)) {
|
|
404
406
|
log.debug("[cmc] no LLM available for consolidation — skipping");
|
|
405
407
|
return [];
|
|
406
408
|
}
|
|
407
409
|
|
|
408
410
|
// Call LLM for pattern analysis
|
|
409
|
-
const result = await consolidateWithLlm(context, llm,
|
|
411
|
+
const result = await consolidateWithLlm(context, llm, llmOptions);
|
|
410
412
|
const candidates = llmResultToCandidates(result);
|
|
411
413
|
|
|
412
414
|
log.debug(`[cmc] LLM consolidation produced ${candidates.length} rule(s) and ${result.preferences.length} preference(s)`);
|
|
@@ -426,6 +428,7 @@ export async function synthesizeCausalPreferencesViaLlm(options: {
|
|
|
426
428
|
causalTrajectoryStoreDir?: string;
|
|
427
429
|
gatewayConfig?: GatewayConfig;
|
|
428
430
|
gatewayAgentId?: string;
|
|
431
|
+
modelChain?: AgentPersonaModelConfig;
|
|
429
432
|
workspaceDir?: string;
|
|
430
433
|
minTrajectories?: number;
|
|
431
434
|
}): Promise<string | null> {
|
|
@@ -440,9 +443,10 @@ export async function synthesizeCausalPreferencesViaLlm(options: {
|
|
|
440
443
|
const llm = new FallbackLlmClient(options.gatewayConfig, {
|
|
441
444
|
workspaceDir: options.workspaceDir,
|
|
442
445
|
});
|
|
443
|
-
|
|
446
|
+
const llmOptions = { agentId: options.gatewayAgentId, modelChain: options.modelChain };
|
|
447
|
+
if (!llm.isAvailable(llmOptions)) return null;
|
|
444
448
|
|
|
445
|
-
const result = await consolidateWithLlm(context, llm,
|
|
449
|
+
const result = await consolidateWithLlm(context, llm, llmOptions);
|
|
446
450
|
if (result.preferences.length === 0 && result.rules.length === 0) return null;
|
|
447
451
|
|
|
448
452
|
const lines: string[] = ["## Behavioral Insights (from Causal Chain Analysis)", ""];
|