@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/access-service.ts
CHANGED
|
@@ -7,7 +7,11 @@ import { AccessIdempotencyStore, hashAccessIdempotencyPayload } from "./access-i
|
|
|
7
7
|
import { AccessAuditAdapter, type AccessAuditConfig, type AccessAuditResult } from "./access-audit.js";
|
|
8
8
|
import type { AnomalyDetectorResult } from "./recall-audit-anomaly.js";
|
|
9
9
|
import { resolveGitContext } from "./coding/git-context.js";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
combineNamespaces,
|
|
12
|
+
projectTagProjectId,
|
|
13
|
+
resolveCodingNamespaceOverlay,
|
|
14
|
+
} from "./coding/coding-namespace.js";
|
|
11
15
|
import { WorkStorage } from "./work/storage.js";
|
|
12
16
|
import {
|
|
13
17
|
exportWorkBoardMarkdown,
|
|
@@ -91,6 +95,7 @@ import type {
|
|
|
91
95
|
EntityFile,
|
|
92
96
|
MemoryFile,
|
|
93
97
|
MemoryActionOutcome,
|
|
98
|
+
CodingContext,
|
|
94
99
|
MemoryActionType,
|
|
95
100
|
MemoryLifecycleEvent,
|
|
96
101
|
MemoryStatus,
|
|
@@ -755,9 +760,25 @@ export interface EngramAccessWriteEnvelope {
|
|
|
755
760
|
authenticatedPrincipal?: string;
|
|
756
761
|
}
|
|
757
762
|
|
|
758
|
-
|
|
763
|
+
/**
|
|
764
|
+
* Optional git/project context for project-scoped writes (#1434). When no
|
|
765
|
+
* explicit `namespace` is supplied, these route the write to the same project
|
|
766
|
+
* namespace recall/observe resolve from `cwd`/`projectTag` (rule 42 symmetry).
|
|
767
|
+
*/
|
|
768
|
+
export interface CodingScopedWriteInput {
|
|
769
|
+
cwd?: string;
|
|
770
|
+
projectTag?: string;
|
|
771
|
+
}
|
|
759
772
|
|
|
760
|
-
export interface
|
|
773
|
+
export interface EngramAccessMemoryStoreRequest
|
|
774
|
+
extends EngramAccessWriteEnvelope,
|
|
775
|
+
ExplicitCaptureInput,
|
|
776
|
+
CodingScopedWriteInput {}
|
|
777
|
+
|
|
778
|
+
export interface EngramAccessSuggestionSubmitRequest
|
|
779
|
+
extends EngramAccessWriteEnvelope,
|
|
780
|
+
ExplicitCaptureInput,
|
|
781
|
+
CodingScopedWriteInput {}
|
|
761
782
|
|
|
762
783
|
export interface EngramAccessWriteResponse {
|
|
763
784
|
schemaVersion: 1;
|
|
@@ -1115,6 +1136,144 @@ export class EngramAccessService {
|
|
|
1115
1136
|
return resolved;
|
|
1116
1137
|
}
|
|
1117
1138
|
|
|
1139
|
+
/**
|
|
1140
|
+
* Resolve a coding context from `cwd`/`projectTag` WITHOUT persisting it to
|
|
1141
|
+
* any session — the read-only half of `maybeAttachCodingContext`. Returns
|
|
1142
|
+
* null when project scoping is off or nothing resolves. `projectTag` takes
|
|
1143
|
+
* priority over `cwd` (matching `maybeAttachCodingContext`).
|
|
1144
|
+
*/
|
|
1145
|
+
private async resolveCodingContextFromOptions(
|
|
1146
|
+
options: CodingScopedWriteInput,
|
|
1147
|
+
): Promise<CodingContext | null> {
|
|
1148
|
+
if (!this.orchestrator.config.codingMode?.projectScope) return null;
|
|
1149
|
+
if (typeof options.projectTag === "string" && options.projectTag.trim().length > 0) {
|
|
1150
|
+
const projectId = projectTagProjectId(options.projectTag);
|
|
1151
|
+
return { projectId, branch: null, rootPath: projectId, defaultBranch: null };
|
|
1152
|
+
}
|
|
1153
|
+
if (typeof options.cwd === "string" && options.cwd.trim().length > 0) {
|
|
1154
|
+
try {
|
|
1155
|
+
const gitCtx = await resolveGitContext(options.cwd);
|
|
1156
|
+
if (gitCtx) {
|
|
1157
|
+
return {
|
|
1158
|
+
projectId: gitCtx.projectId,
|
|
1159
|
+
branch: gitCtx.branch,
|
|
1160
|
+
rootPath: gitCtx.rootPath,
|
|
1161
|
+
defaultBranch: gitCtx.defaultBranch,
|
|
1162
|
+
};
|
|
1163
|
+
}
|
|
1164
|
+
} catch {
|
|
1165
|
+
// resolveGitContext never throws, but stay defensive — not being in a
|
|
1166
|
+
// repo is normal and must not break the write.
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
return null;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
/**
|
|
1173
|
+
* Resolve the write namespace for explicit-write tools (memory_store /
|
|
1174
|
+
* suggestion_submit), project-scoping the write the same way recall does so a
|
|
1175
|
+
* memory stored with a client-injected `cwd`/`projectTag` is discoverable by
|
|
1176
|
+
* project-scoped recall (#1434, rule 42).
|
|
1177
|
+
*
|
|
1178
|
+
* Precedence:
|
|
1179
|
+
* - An explicit `namespace` always wins and is authorized strictly via
|
|
1180
|
+
* `resolveWritableNamespace` → `canWriteNamespace`. A coding-overlay
|
|
1181
|
+
* namespace string (`<base>-project-*`) is NOT a writable target via the
|
|
1182
|
+
* explicit field — project scoping is requested with `cwd`/`projectTag`,
|
|
1183
|
+
* never by naming the derived namespace — so there is no way to bypass the
|
|
1184
|
+
* policy allow-list by guessing/forging an overlay name (Codex review).
|
|
1185
|
+
* - With NO coding overlay, the write stays on `config.defaultNamespace` —
|
|
1186
|
+
* exactly the pre-#1434 behavior, so an unqualified write is NOT silently
|
|
1187
|
+
* moved to a principal self namespace (Codex review).
|
|
1188
|
+
* - WITH a coding overlay, the base is the principal self namespace
|
|
1189
|
+
* (`defaultNamespaceForPrincipal`, write-checked) — the SAME base recall,
|
|
1190
|
+
* observe, and the orchestrator buffer-flush write path overlay onto
|
|
1191
|
+
* (rule 42 / Cursor) — so a project-scoped store lands exactly where
|
|
1192
|
+
* project-scoped recall searches. The overlay namespace is always REBUILT
|
|
1193
|
+
* from the authenticated principal's base, never accepted as a caller
|
|
1194
|
+
* string, so a caller can never reach another principal's subtree.
|
|
1195
|
+
*
|
|
1196
|
+
* Read-only: this NEVER mutates session coding context, so the idempotency
|
|
1197
|
+
* peeks and dryRun preflights that call it stay side-effect free (Codex
|
|
1198
|
+
* review). It prefers the per-call `cwd`/`projectTag` (the project explicitly
|
|
1199
|
+
* identified for this write), else the session's existing context. The HTTP
|
|
1200
|
+
* surface lets the peek and the write each resolve independently; the peek's
|
|
1201
|
+
* namespace only gates rate-limiting (memory_store/suggestion_submit run their
|
|
1202
|
+
* own idempotency check), so a benign session-context change between the two
|
|
1203
|
+
* never fails a write — there is no namespace to "pin".
|
|
1204
|
+
*/
|
|
1205
|
+
private async resolveCodingScopedWriteNamespace(
|
|
1206
|
+
request: CodingScopedWriteInput & {
|
|
1207
|
+
namespace?: string;
|
|
1208
|
+
sessionKey?: string;
|
|
1209
|
+
authenticatedPrincipal?: string;
|
|
1210
|
+
},
|
|
1211
|
+
): Promise<string> {
|
|
1212
|
+
const hasExplicitNamespace =
|
|
1213
|
+
typeof request.namespace === "string" && request.namespace.trim().length > 0;
|
|
1214
|
+
if (hasExplicitNamespace) {
|
|
1215
|
+
return this.resolveWritableNamespace(
|
|
1216
|
+
request.namespace,
|
|
1217
|
+
request.sessionKey,
|
|
1218
|
+
request.authenticatedPrincipal,
|
|
1219
|
+
);
|
|
1220
|
+
}
|
|
1221
|
+
// Project scoping only applies when namespaces are enabled (else overlaying
|
|
1222
|
+
// would create false isolation over a single storage dir) and projectScope
|
|
1223
|
+
// is on. The coding context MUST be resolved exactly as the recall path
|
|
1224
|
+
// resolves it, or a scoped store won't be discoverable by scoped recall
|
|
1225
|
+
// (the whole point of #1434). Recall calls `maybeAttachCodingContext`, which
|
|
1226
|
+
// returns early when the session already has a context — so recall is
|
|
1227
|
+
// SESSION-FIRST: an existing session binding wins, and the per-call
|
|
1228
|
+
// cwd/projectTag is only used to seed a context when none is attached yet.
|
|
1229
|
+
// Mirror that precedence here: session context first, per-call as fallback
|
|
1230
|
+
// (Codex review — a per-call-wins write would land in a project that the
|
|
1231
|
+
// same session's recall, still on the bound project, never searches).
|
|
1232
|
+
//
|
|
1233
|
+
// A sessionKey is REQUIRED to apply the overlay. The recall path can only
|
|
1234
|
+
// attach/look up coding context per session (`maybeAttachCodingContext` and
|
|
1235
|
+
// `applyCodingNamespaceOverlay` both no-op without a sessionKey), so a
|
|
1236
|
+
// sessionless recall always searches the base namespace. A sessionless
|
|
1237
|
+
// write must therefore also stay on the base — otherwise a client that
|
|
1238
|
+
// injects cwd/projectTag but no sessionKey would store into
|
|
1239
|
+
// `default-project-*` that its own recall never searches (Codex review).
|
|
1240
|
+
const hasSession =
|
|
1241
|
+
typeof request.sessionKey === "string" && request.sessionKey.length > 0;
|
|
1242
|
+
const overlay =
|
|
1243
|
+
hasSession &&
|
|
1244
|
+
this.orchestrator.config.namespacesEnabled &&
|
|
1245
|
+
this.orchestrator.config.codingMode?.projectScope
|
|
1246
|
+
? resolveCodingNamespaceOverlay(
|
|
1247
|
+
this.orchestrator.getCodingContextForSession(request.sessionKey) ??
|
|
1248
|
+
(await this.resolveCodingContextFromOptions(request)),
|
|
1249
|
+
this.orchestrator.config.codingMode,
|
|
1250
|
+
this.orchestrator.config.defaultNamespace,
|
|
1251
|
+
)
|
|
1252
|
+
: null;
|
|
1253
|
+
if (!overlay) {
|
|
1254
|
+
// No coding overlay → unqualified write stays on config.defaultNamespace,
|
|
1255
|
+
// exactly the pre-#1434 behavior (auth-checked, like the legacy path).
|
|
1256
|
+
return this.resolveWritableNamespace(
|
|
1257
|
+
undefined,
|
|
1258
|
+
request.sessionKey,
|
|
1259
|
+
request.authenticatedPrincipal,
|
|
1260
|
+
);
|
|
1261
|
+
}
|
|
1262
|
+
// Coding overlay → overlay onto the principal self base, the SAME namespace
|
|
1263
|
+
// recall/observe/buffer-flush use. The result is a principal-owned
|
|
1264
|
+
// `project-*` sub-namespace derived from this authorized base, so it needs
|
|
1265
|
+
// no separate write policy.
|
|
1266
|
+
const principal = this.resolveRequestPrincipal(
|
|
1267
|
+
request.sessionKey,
|
|
1268
|
+
request.authenticatedPrincipal,
|
|
1269
|
+
);
|
|
1270
|
+
const base = defaultNamespaceForPrincipal(principal, this.orchestrator.config);
|
|
1271
|
+
if (!canWriteNamespace(principal, base, this.orchestrator.config)) {
|
|
1272
|
+
throw new EngramAccessInputError(`namespace is not writable: ${base}`);
|
|
1273
|
+
}
|
|
1274
|
+
return combineNamespaces(base, overlay.namespace);
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1118
1277
|
private async objectiveStateStoreLocationForNamespace(namespace: string): Promise<{
|
|
1119
1278
|
memoryDir: string;
|
|
1120
1279
|
objectiveStateStoreDir?: string;
|
|
@@ -1384,6 +1543,16 @@ export class EngramAccessService {
|
|
|
1384
1543
|
idempotencyKey?: string;
|
|
1385
1544
|
requestFingerprint: unknown;
|
|
1386
1545
|
skip?: boolean;
|
|
1546
|
+
/**
|
|
1547
|
+
* Invoked exactly once, immediately before an ACTUAL (non-replay, non-skip)
|
|
1548
|
+
* write is committed — atomically with the idempotency miss determination.
|
|
1549
|
+
* The HTTP surface uses this to enforce the write rate limit against the
|
|
1550
|
+
* real write/miss (and the real resolved namespace), so a namespace-divergent
|
|
1551
|
+
* idempotency peek can never let a fresh write skip the quota check (#1434
|
|
1552
|
+
* Codex review). It is NOT called on dryRun (skip) or replay, preserving the
|
|
1553
|
+
* replay-bypasses-a-full-window behavior.
|
|
1554
|
+
*/
|
|
1555
|
+
beforeExecute?: () => void | Promise<void>;
|
|
1387
1556
|
execute: () => Promise<T>;
|
|
1388
1557
|
}): Promise<T> {
|
|
1389
1558
|
if (options.skip === true) {
|
|
@@ -1391,6 +1560,7 @@ export class EngramAccessService {
|
|
|
1391
1560
|
}
|
|
1392
1561
|
const key = options.idempotencyKey?.trim();
|
|
1393
1562
|
if (!key) {
|
|
1563
|
+
if (options.beforeExecute) await options.beforeExecute();
|
|
1394
1564
|
return options.execute();
|
|
1395
1565
|
}
|
|
1396
1566
|
return this.withIdempotencyLock(key, async () => {
|
|
@@ -1409,6 +1579,7 @@ export class EngramAccessService {
|
|
|
1409
1579
|
idempotencyReplay: true,
|
|
1410
1580
|
};
|
|
1411
1581
|
}
|
|
1582
|
+
if (options.beforeExecute) await options.beforeExecute();
|
|
1412
1583
|
const response = await options.execute();
|
|
1413
1584
|
await this.idempotency.put(key, requestHash, response);
|
|
1414
1585
|
return response;
|
|
@@ -1757,6 +1928,28 @@ export class EngramAccessService {
|
|
|
1757
1928
|
}
|
|
1758
1929
|
}
|
|
1759
1930
|
|
|
1931
|
+
/**
|
|
1932
|
+
* Seed the session's coding binding AFTER a committed, project-scoped explicit
|
|
1933
|
+
* write (memory_store / suggestion_submit), mirroring the recall path's
|
|
1934
|
+
* `maybeAttachCodingContext` so a later bare recall/write on the same session
|
|
1935
|
+
* is scoped to the same project. Called only from the post-persist path, so it
|
|
1936
|
+
* never fires on dryRun, replay/conflict, or quota-rejected requests. Skips
|
|
1937
|
+
* when an explicit `namespace` was supplied — that write bypassed the coding
|
|
1938
|
+
* overlay, so binding the session to a project it never wrote to would make
|
|
1939
|
+
* later bare recalls miss (Codex review).
|
|
1940
|
+
*/
|
|
1941
|
+
private async attachCodingContextAfterScopedWrite(
|
|
1942
|
+
request: CodingScopedWriteInput & { namespace?: string; sessionKey?: string },
|
|
1943
|
+
): Promise<void> {
|
|
1944
|
+
const hasExplicitNamespace =
|
|
1945
|
+
typeof request.namespace === "string" && request.namespace.trim().length > 0;
|
|
1946
|
+
if (hasExplicitNamespace) return;
|
|
1947
|
+
await this.maybeAttachCodingContext(request.sessionKey, {
|
|
1948
|
+
cwd: request.cwd,
|
|
1949
|
+
projectTag: request.projectTag,
|
|
1950
|
+
});
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1760
1953
|
async recall(request: EngramAccessRecallRequest): Promise<EngramAccessRecallResponse> {
|
|
1761
1954
|
const query = request.query.trim();
|
|
1762
1955
|
if (query.length === 0) {
|
|
@@ -2662,12 +2855,11 @@ export class EngramAccessService {
|
|
|
2662
2855
|
// per-tenant) do not block each other.
|
|
2663
2856
|
private xrayQueue: Promise<void> = Promise.resolve();
|
|
2664
2857
|
|
|
2665
|
-
async memoryStore(
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
);
|
|
2858
|
+
async memoryStore(
|
|
2859
|
+
request: EngramAccessMemoryStoreRequest,
|
|
2860
|
+
hooks?: { enforceWriteQuota?: () => void | Promise<void> },
|
|
2861
|
+
): Promise<EngramAccessWriteResponse> {
|
|
2862
|
+
const namespace = await this.resolveCodingScopedWriteNamespace(request);
|
|
2671
2863
|
const schemaVersion = request.schemaVersion ?? ENGRAM_ACCESS_WRITE_SCHEMA_VERSION;
|
|
2672
2864
|
if (schemaVersion !== ENGRAM_ACCESS_WRITE_SCHEMA_VERSION) {
|
|
2673
2865
|
throw new EngramAccessInputError(`unsupported schemaVersion: ${schemaVersion}`);
|
|
@@ -2687,6 +2879,14 @@ export class EngramAccessService {
|
|
|
2687
2879
|
};
|
|
2688
2880
|
}
|
|
2689
2881
|
const result = await persistExplicitCapture(this.orchestrator, candidate, "memory_store");
|
|
2882
|
+
// Seed the session's coding binding ONLY after a real write commits, and
|
|
2883
|
+
// only when the namespace came from project scoping (no explicit
|
|
2884
|
+
// namespace). This mirrors recall's maybeAttachCodingContext so a LATER
|
|
2885
|
+
// bare recall/write on the same session is scoped to the same project —
|
|
2886
|
+
// but never binds the session on a dryRun, replay/conflict, quota
|
|
2887
|
+
// rejection, or an explicit-namespace write (which bypasses the overlay),
|
|
2888
|
+
// since those don't reach this point or aren't project-scoped (Codex review).
|
|
2889
|
+
await this.attachCodingContextAfterScopedWrite(request);
|
|
2690
2890
|
const response: EngramAccessWriteResponse = {
|
|
2691
2891
|
schemaVersion: ENGRAM_ACCESS_WRITE_SCHEMA_VERSION,
|
|
2692
2892
|
operation: "memory_store",
|
|
@@ -2719,16 +2919,13 @@ export class EngramAccessService {
|
|
|
2719
2919
|
sourceReason: request.sourceReason,
|
|
2720
2920
|
},
|
|
2721
2921
|
skip: request.dryRun === true,
|
|
2922
|
+
beforeExecute: hooks?.enforceWriteQuota,
|
|
2722
2923
|
execute,
|
|
2723
2924
|
});
|
|
2724
2925
|
}
|
|
2725
2926
|
|
|
2726
2927
|
async peekMemoryStoreIdempotency(request: EngramAccessMemoryStoreRequest): Promise<EngramAccessIdempotencyStatus> {
|
|
2727
|
-
const namespace = this.
|
|
2728
|
-
request.namespace,
|
|
2729
|
-
request.sessionKey,
|
|
2730
|
-
request.authenticatedPrincipal,
|
|
2731
|
-
);
|
|
2928
|
+
const namespace = await this.resolveCodingScopedWriteNamespace(request);
|
|
2732
2929
|
const schemaVersion = request.schemaVersion ?? ENGRAM_ACCESS_WRITE_SCHEMA_VERSION;
|
|
2733
2930
|
if (schemaVersion !== ENGRAM_ACCESS_WRITE_SCHEMA_VERSION) {
|
|
2734
2931
|
throw new EngramAccessInputError(`unsupported schemaVersion: ${schemaVersion}`);
|
|
@@ -2751,12 +2948,11 @@ export class EngramAccessService {
|
|
|
2751
2948
|
});
|
|
2752
2949
|
}
|
|
2753
2950
|
|
|
2754
|
-
async suggestionSubmit(
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
);
|
|
2951
|
+
async suggestionSubmit(
|
|
2952
|
+
request: EngramAccessSuggestionSubmitRequest,
|
|
2953
|
+
hooks?: { enforceWriteQuota?: () => void | Promise<void> },
|
|
2954
|
+
): Promise<EngramAccessWriteResponse> {
|
|
2955
|
+
const namespace = await this.resolveCodingScopedWriteNamespace(request);
|
|
2760
2956
|
const schemaVersion = request.schemaVersion ?? ENGRAM_ACCESS_WRITE_SCHEMA_VERSION;
|
|
2761
2957
|
if (schemaVersion !== ENGRAM_ACCESS_WRITE_SCHEMA_VERSION) {
|
|
2762
2958
|
throw new EngramAccessInputError(`unsupported schemaVersion: ${schemaVersion}`);
|
|
@@ -2781,6 +2977,10 @@ export class EngramAccessService {
|
|
|
2781
2977
|
"suggestion_submit",
|
|
2782
2978
|
new Error(request.sourceReason?.trim() || "submitted via engram suggestion_submit"),
|
|
2783
2979
|
);
|
|
2980
|
+
// Seed the session binding only after a real, project-scoped submit commits
|
|
2981
|
+
// (mirrors memory_store / recall; skips dryRun, replay, quota-reject, and
|
|
2982
|
+
// explicit-namespace writes — Codex review).
|
|
2983
|
+
await this.attachCodingContextAfterScopedWrite(request);
|
|
2784
2984
|
const response: EngramAccessWriteResponse = {
|
|
2785
2985
|
schemaVersion: ENGRAM_ACCESS_WRITE_SCHEMA_VERSION,
|
|
2786
2986
|
operation: "suggestion_submit",
|
|
@@ -2813,6 +3013,7 @@ export class EngramAccessService {
|
|
|
2813
3013
|
sourceReason: request.sourceReason,
|
|
2814
3014
|
},
|
|
2815
3015
|
skip: request.dryRun === true,
|
|
3016
|
+
beforeExecute: hooks?.enforceWriteQuota,
|
|
2816
3017
|
execute,
|
|
2817
3018
|
});
|
|
2818
3019
|
}
|
|
@@ -2820,11 +3021,7 @@ export class EngramAccessService {
|
|
|
2820
3021
|
async peekSuggestionSubmitIdempotency(
|
|
2821
3022
|
request: EngramAccessSuggestionSubmitRequest,
|
|
2822
3023
|
): Promise<EngramAccessIdempotencyStatus> {
|
|
2823
|
-
const namespace = this.
|
|
2824
|
-
request.namespace,
|
|
2825
|
-
request.sessionKey,
|
|
2826
|
-
request.authenticatedPrincipal,
|
|
2827
|
-
);
|
|
3024
|
+
const namespace = await this.resolveCodingScopedWriteNamespace(request);
|
|
2828
3025
|
const schemaVersion = request.schemaVersion ?? ENGRAM_ACCESS_WRITE_SCHEMA_VERSION;
|
|
2829
3026
|
if (schemaVersion !== ENGRAM_ACCESS_WRITE_SCHEMA_VERSION) {
|
|
2830
3027
|
throw new EngramAccessInputError(`unsupported schemaVersion: ${schemaVersion}`);
|
|
@@ -2852,13 +3049,21 @@ export class EngramAccessService {
|
|
|
2852
3049
|
namespace: string,
|
|
2853
3050
|
): ValidExplicitCapture {
|
|
2854
3051
|
try {
|
|
2855
|
-
return
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
3052
|
+
return {
|
|
3053
|
+
...validateExplicitCaptureInput(
|
|
3054
|
+
{
|
|
3055
|
+
...request,
|
|
3056
|
+
namespace,
|
|
3057
|
+
},
|
|
3058
|
+
"legacy_tool",
|
|
3059
|
+
),
|
|
3060
|
+
// The namespace was resolved AND authorized by
|
|
3061
|
+
// resolveCodingScopedWriteNamespace (explicit namespaces via
|
|
3062
|
+
// resolveWritableNamespace; otherwise an auth-checked base + a
|
|
3063
|
+
// session-owned project overlay), so the persist/queue layer must not
|
|
3064
|
+
// re-reject a legitimately-derived dynamic project namespace (#1434).
|
|
3065
|
+
namespacePreResolved: true,
|
|
3066
|
+
};
|
|
2862
3067
|
} catch (error) {
|
|
2863
3068
|
const message = error instanceof Error ? error.message : String(error);
|
|
2864
3069
|
throw new EngramAccessInputError(message);
|
|
@@ -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");
|