@remnic/core 9.45.5 → 9.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (399) hide show
  1. package/dist/access-admin-ops-surface.d.ts +10 -10
  2. package/dist/access-admin-ops-surface.js +24 -24
  3. package/dist/access-authorization-probe.d.ts +10 -10
  4. package/dist/access-authorization-probe.js +25 -25
  5. package/dist/access-boundary.d.ts +10 -10
  6. package/dist/access-boundary.js +24 -24
  7. package/dist/access-cli.js +73 -71
  8. package/dist/access-cli.js.map +1 -1
  9. package/dist/access-coding-context-resolution.d.ts +1 -1
  10. package/dist/access-extraction-force-flush.d.ts +10 -10
  11. package/dist/access-extraction-force-flush.js +24 -24
  12. package/dist/access-health-types.d.ts +1 -1
  13. package/dist/access-http-lcm-compaction.d.ts +10 -10
  14. package/dist/access-http-lifecycle-flush.d.ts +10 -10
  15. package/dist/access-http-offline-stream.d.ts +10 -10
  16. package/dist/access-http-query.js +24 -24
  17. package/dist/access-http.d.ts +10 -10
  18. package/dist/access-http.js +31 -31
  19. package/dist/access-identity-continuity-surface.d.ts +8 -8
  20. package/dist/access-identity-continuity-surface.js +24 -24
  21. package/dist/access-lcm-surface.d.ts +10 -10
  22. package/dist/access-lcm-surface.js +24 -24
  23. package/dist/access-mcp.d.ts +10 -10
  24. package/dist/access-mcp.js +28 -28
  25. package/dist/access-memory-search-fanout.d.ts +2 -2
  26. package/dist/{access-namespace-preflight-CWUDwB4M.d.ts → access-namespace-preflight--H6HwN85.d.ts} +1 -1
  27. package/dist/access-namespace-preflight.d.ts +3 -3
  28. package/dist/access-namespace-preflight.js +24 -24
  29. package/dist/access-observe-write-surface.d.ts +10 -10
  30. package/dist/access-observe-write-surface.js +24 -24
  31. package/dist/access-offline-manifest.d.ts +8 -8
  32. package/dist/access-operations-batch.js +25 -25
  33. package/dist/access-operations.d.ts +10 -10
  34. package/dist/access-operations.js +27 -27
  35. package/dist/access-recall-concurrency.d.ts +10 -10
  36. package/dist/access-recall-concurrency.js +24 -24
  37. package/dist/access-recall-response.d.ts +10 -10
  38. package/dist/access-recall-response.js +24 -24
  39. package/dist/access-recall-surface.d.ts +10 -10
  40. package/dist/access-recall-surface.js +24 -24
  41. package/dist/access-service-helpers.d.ts +10 -10
  42. package/dist/{access-service-CNN89xyn.d.ts → access-service-loJadYd7.d.ts} +7 -7
  43. package/dist/access-service.d.ts +10 -10
  44. package/dist/access-service.js +24 -24
  45. package/dist/access-surface-catalog.d.ts +10 -10
  46. package/dist/access-wearables-meetings-surface.d.ts +3 -3
  47. package/dist/action-confidence.d.ts +1 -1
  48. package/dist/active-memory-bridge.d.ts +1 -1
  49. package/dist/active-recall.d.ts +1 -1
  50. package/dist/ambient-provenance.d.ts +109 -0
  51. package/dist/ambient-provenance.js +22 -0
  52. package/dist/ambient-provenance.js.map +1 -0
  53. package/dist/{auto-sync-UPTUVE4X.js → auto-sync-7O6RP2RT.js} +3 -2
  54. package/dist/{auto-sync-UPTUVE4X.js.map → auto-sync-7O6RP2RT.js.map} +1 -1
  55. package/dist/behavior-learner.d.ts +1 -1
  56. package/dist/behavior-signals.d.ts +1 -1
  57. package/dist/bootstrap.d.ts +8 -8
  58. package/dist/briefing.d.ts +2 -2
  59. package/dist/briefing.js +5 -5
  60. package/dist/buffer-surprise-report.d.ts +1 -1
  61. package/dist/buffer-turn-helpers.d.ts +1 -1
  62. package/dist/buffer-turn-helpers.js +1 -1
  63. package/dist/buffer.d.ts +2 -2
  64. package/dist/buffer.js +2 -2
  65. package/dist/bulk-import/index.d.ts +3 -3
  66. package/dist/calibration.d.ts +1 -1
  67. package/dist/capabilities.d.ts +1 -1
  68. package/dist/{catalog-bctMURhN.d.ts → catalog-Iwbk9h5E.d.ts} +1 -1
  69. package/dist/causal-behavior.d.ts +1 -1
  70. package/dist/causal-consolidation.d.ts +1 -1
  71. package/dist/causal-consolidation.js +6 -6
  72. package/dist/causal-trajectory-graph.d.ts +1 -1
  73. package/dist/{chunk-LEPOP46W.js → chunk-227UFSEQ.js} +4 -4
  74. package/dist/{chunk-ER7W6H4T.js → chunk-2AD5SXO4.js} +5 -2
  75. package/dist/chunk-2AD5SXO4.js.map +1 -0
  76. package/dist/{chunk-CX4ZUMC6.js → chunk-2DSUT7DZ.js} +9 -9
  77. package/dist/{chunk-DZPFMQZ7.js → chunk-3S63QIFQ.js} +1 -1
  78. package/dist/chunk-3S63QIFQ.js.map +1 -0
  79. package/dist/{chunk-DAJPRXBR.js → chunk-47J4EDGR.js} +5 -5
  80. package/dist/{chunk-QVJPH7SH.js → chunk-5LRDM4R7.js} +40 -40
  81. package/dist/{chunk-XAGYS7X6.js → chunk-5R6S4DJZ.js} +7 -7
  82. package/dist/{chunk-QR6G5KF4.js → chunk-5VQPZ4GV.js} +3 -3
  83. package/dist/{chunk-E5XQIWAL.js → chunk-64BHFFKZ.js} +2 -2
  84. package/dist/{chunk-GRUYA45P.js → chunk-6H744ZBH.js} +3 -3
  85. package/dist/{chunk-6ISC4AVB.js → chunk-BDNL34CT.js} +2 -2
  86. package/dist/{chunk-GQL52GQ5.js → chunk-CDKHKZL5.js} +15 -12
  87. package/dist/chunk-CDKHKZL5.js.map +1 -0
  88. package/dist/{chunk-AC5OWJLV.js → chunk-E4FOAENZ.js} +4 -4
  89. package/dist/{chunk-UW66OZTD.js → chunk-FULKGK7P.js} +2 -2
  90. package/dist/{chunk-V2TNYF6Q.js → chunk-HJ3SVHUW.js} +3 -3
  91. package/dist/{chunk-A4IJCD2Z.js → chunk-HXXTXLKA.js} +2 -2
  92. package/dist/{chunk-FUCYDRE5.js → chunk-IDE5AR5W.js} +11 -11
  93. package/dist/{chunk-2FSTB6BJ.js → chunk-ILBLZ5Y5.js} +2 -2
  94. package/dist/chunk-J7T4MX7P.js +131 -0
  95. package/dist/chunk-J7T4MX7P.js.map +1 -0
  96. package/dist/{chunk-WZ3TOIDW.js → chunk-JFO5QZ7N.js} +2 -2
  97. package/dist/{chunk-FR27HBBA.js → chunk-KA47C7BF.js} +2 -2
  98. package/dist/{chunk-JZH4LWHB.js → chunk-KMWYZTIE.js} +2 -2
  99. package/dist/{chunk-LPYDOLSM.js → chunk-L7G72WY4.js} +2 -2
  100. package/dist/{chunk-AMAUBUKY.js → chunk-LI7OE7G6.js} +2 -2
  101. package/dist/{chunk-MO4HSXHZ.js → chunk-M2INOSTA.js} +2 -2
  102. package/dist/{chunk-GODSPTOT.js → chunk-M32URQXV.js} +652 -601
  103. package/dist/chunk-M32URQXV.js.map +1 -0
  104. package/dist/{chunk-HFPASSFV.js → chunk-P2OTGMTR.js} +2 -2
  105. package/dist/{chunk-W2V7UESE.js → chunk-PFJXPOD4.js} +2 -2
  106. package/dist/{chunk-R3KHKU7M.js → chunk-PKCLQ7KM.js} +43 -143
  107. package/dist/chunk-PKCLQ7KM.js.map +1 -0
  108. package/dist/chunk-QW27DOHG.js +84 -0
  109. package/dist/chunk-QW27DOHG.js.map +1 -0
  110. package/dist/{chunk-QUENPGWZ.js → chunk-RCU25Z66.js} +24 -24
  111. package/dist/{chunk-QUENPGWZ.js.map → chunk-RCU25Z66.js.map} +1 -1
  112. package/dist/{chunk-NEF7LR5J.js → chunk-RVPTS4U5.js} +2 -2
  113. package/dist/{chunk-W3IC7QOY.js → chunk-RVYVMQK4.js} +3 -3
  114. package/dist/{chunk-GUAL4PQH.js → chunk-ST5IEK3H.js} +2 -2
  115. package/dist/{chunk-O4KMGRXE.js → chunk-SXWHN7GC.js} +2 -2
  116. package/dist/{chunk-RNHVVMFN.js → chunk-T6JRMVOV.js} +2 -2
  117. package/dist/{chunk-5QJQHBLE.js → chunk-U6JGQ37L.js} +2 -2
  118. package/dist/{chunk-UV72DEZD.js → chunk-UYUYZ4WY.js} +36 -10
  119. package/dist/chunk-UYUYZ4WY.js.map +1 -0
  120. package/dist/{chunk-HC3PBX23.js → chunk-V5FE3UAI.js} +2 -2
  121. package/dist/{chunk-ERZ2R42X.js → chunk-W2ZSNIBP.js} +2 -2
  122. package/dist/{chunk-3COYXTGM.js → chunk-WHWDBTCJ.js} +2 -2
  123. package/dist/{chunk-FIK3CQNA.js → chunk-WOC6DXP5.js} +3 -3
  124. package/dist/{chunk-4FBVAGV3.js → chunk-XBMJ35IP.js} +2 -2
  125. package/dist/{chunk-GFCVUY6G.js → chunk-YWCOJOUI.js} +2 -2
  126. package/dist/{chunk-2X2T2UVE.js → chunk-ZDAYWJV6.js} +1 -1
  127. package/dist/chunk-ZDAYWJV6.js.map +1 -0
  128. package/dist/{cli-BHBswmUa.d.ts → cli-D_RcqXfZ.d.ts} +5 -5
  129. package/dist/cli.d.ts +12 -12
  130. package/dist/cli.js +50 -50
  131. package/dist/compat/checks.js +1 -1
  132. package/dist/compounding/engine.d.ts +2 -2
  133. package/dist/compounding/engine.js +5 -5
  134. package/dist/compounding/preference-consolidator.d.ts +1 -1
  135. package/dist/compression-optimizer.d.ts +1 -1
  136. package/dist/config.d.ts +1 -1
  137. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  138. package/dist/connectors/codex-materialize-runner.js +5 -5
  139. package/dist/connectors/codex-materialize.d.ts +1 -1
  140. package/dist/connectors/index.d.ts +1 -1
  141. package/dist/connectors/index.js +5 -5
  142. package/dist/consolidation-provenance-check.d.ts +2 -2
  143. package/dist/consolidation-undo.d.ts +2 -2
  144. package/dist/contradiction/index.d.ts +2 -2
  145. package/dist/converge-config.d.ts +1 -1
  146. package/dist/convergence-refresh.d.ts +2 -2
  147. package/dist/convergence-refresh.js +7 -7
  148. package/dist/conversation-index/backend.d.ts +1 -1
  149. package/dist/conversation-index/backend.js +2 -2
  150. package/dist/conversation-index/chunker.d.ts +1 -1
  151. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  152. package/dist/conversation-index/indexer.d.ts +1 -1
  153. package/dist/conversation-index/search.d.ts +1 -1
  154. package/dist/corpus-watermark.d.ts +1 -1
  155. package/dist/corpus-watermark.js +13 -13
  156. package/dist/day-summary.d.ts +1 -1
  157. package/dist/delinearize.d.ts +1 -1
  158. package/dist/direct-answer-wiring.d.ts +1 -1
  159. package/dist/direct-answer.d.ts +1 -1
  160. package/dist/embedding-fallback.d.ts +1 -1
  161. package/dist/enrichment/index.d.ts +1 -1
  162. package/dist/entity-retrieval.d.ts +2 -2
  163. package/dist/entity-retrieval.js +5 -5
  164. package/dist/entity-schema.d.ts +1 -1
  165. package/dist/explicit-capture.d.ts +8 -8
  166. package/dist/external-wiki-access.d.ts +10 -10
  167. package/dist/external-wiki-access.js +25 -25
  168. package/dist/external-wiki-collection-registration.d.ts +1 -1
  169. package/dist/external-wiki-collection.d.ts +1 -1
  170. package/dist/external-wiki-mcp-tools.d.ts +10 -10
  171. package/dist/extraction-error-classification.d.ts +1 -1
  172. package/dist/extraction-faithfulness.d.ts +1 -1
  173. package/dist/extraction-judge-telemetry.d.ts +1 -1
  174. package/dist/extraction-judge-training.d.ts +1 -1
  175. package/dist/extraction-judge.d.ts +1 -1
  176. package/dist/extraction-liveness.d.ts +1 -1
  177. package/dist/extraction-prompt.d.ts +36 -0
  178. package/dist/extraction-prompt.js +16 -0
  179. package/dist/extraction-prompt.js.map +1 -0
  180. package/dist/extraction-source-grounding-rules.d.ts +1 -1
  181. package/dist/extraction-source-grounding.d.ts +1 -1
  182. package/dist/extraction.d.ts +6 -14
  183. package/dist/extraction.js +9 -7
  184. package/dist/fallback-llm.d.ts +1 -1
  185. package/dist/graph-dashboard-diff.d.ts +1 -1
  186. package/dist/graph-dashboard-key.d.ts +1 -1
  187. package/dist/graph-dashboard-parser.d.ts +1 -1
  188. package/dist/graph-edge-reinforcement.d.ts +1 -1
  189. package/dist/graph-snapshot.d.ts +1 -1
  190. package/dist/graph.d.ts +1 -1
  191. package/dist/importance.d.ts +1 -1
  192. package/dist/importers/index.d.ts +1 -1
  193. package/dist/in-flight-reads.d.ts +1 -1
  194. package/dist/index.d.ts +23 -20
  195. package/dist/index.js +124 -122
  196. package/dist/intent.d.ts +1 -1
  197. package/dist/lcm/engine.d.ts +1 -1
  198. package/dist/lcm/index.d.ts +1 -1
  199. package/dist/lcm/tools.d.ts +1 -1
  200. package/dist/lifecycle.d.ts +1 -1
  201. package/dist/live-connectors-runner.d.ts +1 -1
  202. package/dist/local-llm.d.ts +1 -1
  203. package/dist/local-model-endpoint.d.ts +1 -1
  204. package/dist/maintenance/memory-governance.d.ts +1 -1
  205. package/dist/maintenance/memory-governance.js +5 -5
  206. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  207. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +5 -5
  208. package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
  209. package/dist/maintenance/rebuild-memory-projection.js +6 -6
  210. package/dist/{maintenance-CoRikzIt.d.ts → maintenance-DQrL93Uu.d.ts} +3 -3
  211. package/dist/mcp-memory-inspector-app.d.ts +10 -10
  212. package/dist/memory-action-policy.d.ts +1 -1
  213. package/dist/memory-cache.d.ts +1 -1
  214. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  215. package/dist/memory-projection-store.d.ts +1 -1
  216. package/dist/memory-provenance.d.ts +1 -1
  217. package/dist/memory-snapshot.d.ts +1 -1
  218. package/dist/memory-worth-outcomes.d.ts +2 -2
  219. package/dist/models-json.d.ts +1 -1
  220. package/dist/namespaces/migrate.d.ts +3 -3
  221. package/dist/namespaces/migrate.js +12 -12
  222. package/dist/namespaces/principal.d.ts +1 -1
  223. package/dist/namespaces/search.d.ts +1 -1
  224. package/dist/namespaces/search.js +6 -6
  225. package/dist/namespaces/storage.d.ts +3 -3
  226. package/dist/namespaces/storage.js +5 -5
  227. package/dist/native-knowledge.d.ts +1 -1
  228. package/dist/offline-sync-impression-drain.d.ts +1 -1
  229. package/dist/operator-doctor-corpus.d.ts +1 -1
  230. package/dist/operator-doctor-corpus.js +14 -14
  231. package/dist/operator-doctor-replica.d.ts +1 -1
  232. package/dist/operator-toolkit.d.ts +3 -3
  233. package/dist/operator-toolkit.js +18 -18
  234. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  235. package/dist/orchestration/maintenance.d.ts +4 -4
  236. package/dist/orchestration/maintenance.js +10 -10
  237. package/dist/{orchestrator-BJeq7Pw1.d.ts → orchestrator-LPGegii3.d.ts} +8 -8
  238. package/dist/orchestrator.d.ts +8 -8
  239. package/dist/orchestrator.js +73 -71
  240. package/dist/patterns-cli.d.ts +1 -1
  241. package/dist/{pipeline-CNjY4wk3.d.ts → pipeline-1h2GpiHP.d.ts} +1 -1
  242. package/dist/policy-runtime.d.ts +1 -1
  243. package/dist/proactive-contention.d.ts +1 -1
  244. package/dist/provenance.d.ts +1 -1
  245. package/dist/{qmd-C0UDZidt.d.ts → qmd-UdwoF71D.d.ts} +1 -1
  246. package/dist/qmd-preflight.d.ts +2 -2
  247. package/dist/qmd-recall-cache.d.ts +1 -1
  248. package/dist/qmd.d.ts +2 -2
  249. package/dist/recall-concurrency-config.d.ts +1 -1
  250. package/dist/recall-disclosure-escalation.d.ts +1 -1
  251. package/dist/recall-explain-renderer.d.ts +1 -1
  252. package/dist/recall-explain-renderer.js +3 -3
  253. package/dist/recall-memory-map.d.ts +1 -1
  254. package/dist/recall-planner-llm.d.ts +1 -1
  255. package/dist/recall-state.d.ts +1 -1
  256. package/dist/recall-tag-filter.d.ts +1 -1
  257. package/dist/recall-timings.d.ts +1 -1
  258. package/dist/recall-xray-cli.d.ts +1 -1
  259. package/dist/recall-xray-cli.js +4 -4
  260. package/dist/recall-xray-renderer.d.ts +1 -1
  261. package/dist/recall-xray-renderer.js +3 -3
  262. package/dist/recall-xray.d.ts +1 -1
  263. package/dist/recall-xray.js +2 -2
  264. package/dist/reconcile/cursor.d.ts +1 -1
  265. package/dist/reconcile/manifest.d.ts +1 -1
  266. package/dist/reconcile/plan.d.ts +1 -1
  267. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  268. package/dist/replay/normalizers/claude.d.ts +1 -1
  269. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  270. package/dist/replay/normalizers/shared.d.ts +1 -1
  271. package/dist/replay/runner.d.ts +1 -1
  272. package/dist/replay/types.d.ts +1 -1
  273. package/dist/replica-divergence.d.ts +1 -1
  274. package/dist/replica-peers-config.d.ts +1 -1
  275. package/dist/resolve-auth-token.d.ts +1 -1
  276. package/dist/retrieval-agents.d.ts +2 -2
  277. package/dist/retrieval-tiers.d.ts +1 -1
  278. package/dist/routing/engine.d.ts +1 -1
  279. package/dist/routing/store.d.ts +1 -1
  280. package/dist/salvage-envelope.d.ts +1 -1
  281. package/dist/{scope-profiles-BJzp2F5A.d.ts → scope-profiles-APjhSe-w.d.ts} +1 -1
  282. package/dist/sdk-compat.js.map +1 -1
  283. package/dist/search/embed-helper.d.ts +1 -1
  284. package/dist/search/factory.d.ts +1 -1
  285. package/dist/search/factory.js +5 -5
  286. package/dist/search/index.d.ts +1 -1
  287. package/dist/search/index.js +9 -9
  288. package/dist/search/lancedb-backend.d.ts +1 -1
  289. package/dist/search/meilisearch-backend.d.ts +1 -1
  290. package/dist/search/noop-backend.d.ts +1 -1
  291. package/dist/search/orama-backend.d.ts +1 -1
  292. package/dist/search/port.d.ts +1 -1
  293. package/dist/search/remote-backend.d.ts +1 -1
  294. package/dist/{semantic-consolidation-D4KAZO2G.d.ts → semantic-consolidation-CrySDbk3.d.ts} +1 -1
  295. package/dist/semantic-consolidation.d.ts +2 -2
  296. package/dist/semantic-consolidation.js +6 -6
  297. package/dist/semantic-rule-promotion.js +5 -5
  298. package/dist/semantic-rule-verifier.d.ts +1 -1
  299. package/dist/semantic-rule-verifier.js +5 -5
  300. package/dist/{service-DWC4tq7p.d.ts → service-DJ0LDyuN.d.ts} +2 -2
  301. package/dist/session-observer-bands.d.ts +1 -1
  302. package/dist/session-observer-state.d.ts +1 -1
  303. package/dist/shared-context/manager.d.ts +1 -1
  304. package/dist/signal.d.ts +1 -1
  305. package/dist/source-agent-qualifier.d.ts +4 -1
  306. package/dist/source-agent-qualifier.js +6 -6
  307. package/dist/{storage-DPGjB-8e.d.ts → storage-y340tYKb.d.ts} +1 -1
  308. package/dist/storage.d.ts +2 -2
  309. package/dist/storage.js +4 -4
  310. package/dist/summarizer.d.ts +1 -1
  311. package/dist/summary-snapshot.d.ts +1 -1
  312. package/dist/temporal-supersession.d.ts +2 -2
  313. package/dist/temporal-timeline-recall.d.ts +1 -1
  314. package/dist/temporal-validity.d.ts +1 -1
  315. package/dist/threading.d.ts +1 -1
  316. package/dist/tier-migration.d.ts +2 -2
  317. package/dist/tier-routing.d.ts +1 -1
  318. package/dist/topics.d.ts +1 -1
  319. package/dist/transcript.d.ts +1 -1
  320. package/dist/trust-score-stage.d.ts +1 -1
  321. package/dist/trust-score.d.ts +1 -1
  322. package/dist/{types-yX_FwERf.d.ts → types-D_Au9qJO.d.ts} +13 -2
  323. package/dist/types.d.ts +1 -1
  324. package/dist/types.js +1 -1
  325. package/dist/utility-runtime.d.ts +1 -1
  326. package/dist/verified-recall.js +5 -5
  327. package/dist/write-envelope.d.ts +1 -1
  328. package/package.json +2 -12
  329. package/src/ambient-provenance.test.ts +510 -0
  330. package/src/ambient-provenance.ts +229 -0
  331. package/src/buffer-turn-helpers.ts +17 -1
  332. package/src/compat/checks.test.ts +45 -1
  333. package/src/compat/checks.ts +16 -16
  334. package/src/extraction-prompt.ts +142 -0
  335. package/src/extraction.ts +37 -148
  336. package/src/meetings/memory-gen.test.ts +51 -0
  337. package/src/meetings/memory-gen.ts +36 -8
  338. package/src/meetings/summary-extractor.ts +4 -2
  339. package/src/orchestration/recall-internal.ts +5 -148
  340. package/src/orchestration/recall-profile-storage.ts +194 -0
  341. package/src/orchestration/recall-search-pipeline.ts +13 -182
  342. package/src/orchestration/recall-search-prefilter.ts +254 -0
  343. package/src/orchestration/session-context.ts +1 -1
  344. package/src/orchestrator.ts +1 -1
  345. package/src/sdk-compat.ts +2 -4
  346. package/src/source-agent-qualifier.ts +3 -0
  347. package/src/testing/subjects/recall-profile-storage.test.ts +362 -0
  348. package/src/testing/subjects/recall-search-prefilter.test.ts +365 -0
  349. package/src/types.ts +2 -2
  350. package/src/wearables/memory-gen.test.ts +82 -0
  351. package/src/wearables/memory-gen.ts +54 -5
  352. package/src/wearables/trust.ts +19 -2
  353. package/dist/chunk-2X2T2UVE.js.map +0 -1
  354. package/dist/chunk-DZPFMQZ7.js.map +0 -1
  355. package/dist/chunk-ER7W6H4T.js.map +0 -1
  356. package/dist/chunk-GODSPTOT.js.map +0 -1
  357. package/dist/chunk-GQL52GQ5.js.map +0 -1
  358. package/dist/chunk-R3KHKU7M.js.map +0 -1
  359. package/dist/chunk-UV72DEZD.js.map +0 -1
  360. package/dist/legacy-hook-compat.d.ts +0 -3
  361. package/dist/legacy-hook-compat.js +0 -36
  362. package/dist/legacy-hook-compat.js.map +0 -1
  363. package/src/legacy-hook-compat.test.ts +0 -20
  364. package/src/legacy-hook-compat.ts +0 -45
  365. /package/dist/{chunk-LEPOP46W.js.map → chunk-227UFSEQ.js.map} +0 -0
  366. /package/dist/{chunk-CX4ZUMC6.js.map → chunk-2DSUT7DZ.js.map} +0 -0
  367. /package/dist/{chunk-DAJPRXBR.js.map → chunk-47J4EDGR.js.map} +0 -0
  368. /package/dist/{chunk-QVJPH7SH.js.map → chunk-5LRDM4R7.js.map} +0 -0
  369. /package/dist/{chunk-XAGYS7X6.js.map → chunk-5R6S4DJZ.js.map} +0 -0
  370. /package/dist/{chunk-QR6G5KF4.js.map → chunk-5VQPZ4GV.js.map} +0 -0
  371. /package/dist/{chunk-E5XQIWAL.js.map → chunk-64BHFFKZ.js.map} +0 -0
  372. /package/dist/{chunk-GRUYA45P.js.map → chunk-6H744ZBH.js.map} +0 -0
  373. /package/dist/{chunk-6ISC4AVB.js.map → chunk-BDNL34CT.js.map} +0 -0
  374. /package/dist/{chunk-AC5OWJLV.js.map → chunk-E4FOAENZ.js.map} +0 -0
  375. /package/dist/{chunk-UW66OZTD.js.map → chunk-FULKGK7P.js.map} +0 -0
  376. /package/dist/{chunk-V2TNYF6Q.js.map → chunk-HJ3SVHUW.js.map} +0 -0
  377. /package/dist/{chunk-A4IJCD2Z.js.map → chunk-HXXTXLKA.js.map} +0 -0
  378. /package/dist/{chunk-FUCYDRE5.js.map → chunk-IDE5AR5W.js.map} +0 -0
  379. /package/dist/{chunk-2FSTB6BJ.js.map → chunk-ILBLZ5Y5.js.map} +0 -0
  380. /package/dist/{chunk-WZ3TOIDW.js.map → chunk-JFO5QZ7N.js.map} +0 -0
  381. /package/dist/{chunk-FR27HBBA.js.map → chunk-KA47C7BF.js.map} +0 -0
  382. /package/dist/{chunk-JZH4LWHB.js.map → chunk-KMWYZTIE.js.map} +0 -0
  383. /package/dist/{chunk-LPYDOLSM.js.map → chunk-L7G72WY4.js.map} +0 -0
  384. /package/dist/{chunk-AMAUBUKY.js.map → chunk-LI7OE7G6.js.map} +0 -0
  385. /package/dist/{chunk-MO4HSXHZ.js.map → chunk-M2INOSTA.js.map} +0 -0
  386. /package/dist/{chunk-HFPASSFV.js.map → chunk-P2OTGMTR.js.map} +0 -0
  387. /package/dist/{chunk-W2V7UESE.js.map → chunk-PFJXPOD4.js.map} +0 -0
  388. /package/dist/{chunk-NEF7LR5J.js.map → chunk-RVPTS4U5.js.map} +0 -0
  389. /package/dist/{chunk-W3IC7QOY.js.map → chunk-RVYVMQK4.js.map} +0 -0
  390. /package/dist/{chunk-GUAL4PQH.js.map → chunk-ST5IEK3H.js.map} +0 -0
  391. /package/dist/{chunk-O4KMGRXE.js.map → chunk-SXWHN7GC.js.map} +0 -0
  392. /package/dist/{chunk-RNHVVMFN.js.map → chunk-T6JRMVOV.js.map} +0 -0
  393. /package/dist/{chunk-5QJQHBLE.js.map → chunk-U6JGQ37L.js.map} +0 -0
  394. /package/dist/{chunk-HC3PBX23.js.map → chunk-V5FE3UAI.js.map} +0 -0
  395. /package/dist/{chunk-ERZ2R42X.js.map → chunk-W2ZSNIBP.js.map} +0 -0
  396. /package/dist/{chunk-3COYXTGM.js.map → chunk-WHWDBTCJ.js.map} +0 -0
  397. /package/dist/{chunk-FIK3CQNA.js.map → chunk-WOC6DXP5.js.map} +0 -0
  398. /package/dist/{chunk-4FBVAGV3.js.map → chunk-XBMJ35IP.js.map} +0 -0
  399. /package/dist/{chunk-GFCVUY6G.js.map → chunk-YWCOJOUI.js.map} +0 -0
@@ -0,0 +1,254 @@
1
+ import path from "node:path";
2
+ import { resolveIndexingCapabilities, resolveMemoryLifecycleCapabilities } from "../capabilities.js";
3
+ import type { EmbeddingFallback } from "../embedding-fallback.js";
4
+ import type { StorageManager } from "../index.js";
5
+ import type { NamespaceStorageRouter } from "../namespaces/storage.js";
6
+ import {
7
+ computeArtifactCandidateFetchLimit,
8
+ throwIfRecallAborted,
9
+ type QueryAwarePrefilter,
10
+ } from "../orchestrator.js";
11
+ import {
12
+ extractTagsFromPrompt,
13
+ isTemporalQuery,
14
+ queryByDateRangeAsync,
15
+ readIndexSnapshotAsync,
16
+ recencyWindowFromPrompt,
17
+ resolvePromptTagPrefilterAsync,
18
+ } from "../temporal-index.js";
19
+ import type { MemoryFile, PluginConfig, QmdSearchResult } from "../types.js";
20
+ import { isGenericRecallExcludedPath } from "./generic-recall-paths.js";
21
+
22
+ export interface PrefilterAndArtifactDeps {
23
+ readonly config: PluginConfig;
24
+ readonly storageRouter: NamespaceStorageRouter;
25
+ readonly storage: StorageManager;
26
+ readonly embeddingFallback: EmbeddingFallback;
27
+ resolveArtifactSourceStatuses(
28
+ storage: StorageManager,
29
+ sourceIds: string[],
30
+ ): Promise<Map<string, "active" | "superseded" | "archived" | "missing">>;
31
+ scopeQueryAwarePaths(
32
+ paths: Set<string> | null,
33
+ recallNamespaces: string[],
34
+ ): Set<string> | null;
35
+ searchScopedMemoryCandidates(
36
+ candidatePaths: Set<string>,
37
+ query: string,
38
+ limit: number,
39
+ options?: {
40
+ allowArchived?: boolean;
41
+ },
42
+ ): Promise<QmdSearchResult[]>;
43
+ }
44
+
45
+ export async function fetchActiveArtifactsForNamespace(
46
+ deps: Pick<PrefilterAndArtifactDeps, "storageRouter" | "resolveArtifactSourceStatuses">,
47
+ namespace: string,
48
+ prompt: string,
49
+ targetCount: number,
50
+ ): Promise<MemoryFile[]> {
51
+ const storage = await deps.storageRouter.storageFor(namespace);
52
+ let fetchLimit = computeArtifactCandidateFetchLimit(targetCount);
53
+ const maxFetchLimit = Math.min(800, Math.max(fetchLimit, targetCount * 8));
54
+ const MAX_ATTEMPTS = 4;
55
+ let bestFiltered: MemoryFile[] = [];
56
+
57
+ for (let attempt = 0; attempt < MAX_ATTEMPTS; attempt += 1) {
58
+ const rawResults = await storage.searchArtifacts(prompt, fetchLimit);
59
+ const sourceIds = Array.from(
60
+ new Set(
61
+ rawResults
62
+ .map((a) => a.frontmatter.sourceMemoryId)
63
+ .filter(
64
+ (id): id is string => typeof id === "string" && id.length > 0,
65
+ ),
66
+ ),
67
+ );
68
+ const sourceStatus =
69
+ sourceIds.length > 0
70
+ ? await deps.resolveArtifactSourceStatuses(storage, sourceIds)
71
+ : new Map<string, "active" | "superseded" | "archived" | "missing">();
72
+
73
+ const filtered: MemoryFile[] = [];
74
+ for (const artifact of rawResults) {
75
+ const sourceId = artifact.frontmatter.sourceMemoryId;
76
+ if (!sourceId) {
77
+ filtered.push(artifact);
78
+ if (filtered.length >= targetCount) break;
79
+ continue;
80
+ }
81
+ const status = sourceStatus.get(sourceId) ?? "missing";
82
+ if (status !== "active") continue;
83
+ filtered.push(artifact);
84
+ if (filtered.length >= targetCount) break;
85
+ }
86
+
87
+ if (filtered.length >= targetCount) return filtered.slice(0, targetCount);
88
+ if (filtered.length > bestFiltered.length) {
89
+ bestFiltered = filtered;
90
+ }
91
+ if (rawResults.length === 0) return filtered;
92
+ if (rawResults.length < fetchLimit && filtered.length > 0)
93
+ return filtered;
94
+ if (fetchLimit >= maxFetchLimit) return filtered;
95
+
96
+ const growth = Math.max(targetCount * 2, 12);
97
+ fetchLimit = Math.min(maxFetchLimit, fetchLimit + growth);
98
+ }
99
+
100
+ return bestFiltered;
101
+ }
102
+
103
+ export async function buildQueryAwarePrefilter(
104
+ deps: Pick<PrefilterAndArtifactDeps, "config" | "scopeQueryAwarePaths">,
105
+ prompt: string,
106
+ recallNamespaces: string[],
107
+ ): Promise<QueryAwarePrefilter> {
108
+ if (!resolveIndexingCapabilities(deps.config).queryAwareIndexing || !prompt.trim()) {
109
+ return {
110
+ candidatePaths: null,
111
+ temporalFromDate: null,
112
+ matchedTags: [],
113
+ expandedTags: [],
114
+ combination: "none",
115
+ filteredToFullSearch: false,
116
+ };
117
+ }
118
+
119
+ const temporalFromDate = isTemporalQuery(prompt)
120
+ ? recencyWindowFromPrompt(prompt, Date.now())
121
+ : null;
122
+ // Read the temporal + tag prefilter indexes as ONE consistent snapshot
123
+ // (issue #1911, Codex Medium) so a concurrent async index mutation can't be
124
+ // observed half-applied (new temporal row, tag membership not yet written),
125
+ // which would drop the current memory from the tag prefilter.
126
+ const [rawTemporal, tagSignals] = await readIndexSnapshotAsync(
127
+ deps.config.memoryDir,
128
+ () =>
129
+ Promise.all([
130
+ temporalFromDate
131
+ ? queryByDateRangeAsync(deps.config.memoryDir, temporalFromDate)
132
+ : Promise.resolve<Set<string> | null>(null),
133
+ resolvePromptTagPrefilterAsync(deps.config.memoryDir, prompt).catch(
134
+ () => ({
135
+ matchedTags: extractTagsFromPrompt(prompt),
136
+ expandedTags: extractTagsFromPrompt(prompt),
137
+ paths: null,
138
+ }),
139
+ ),
140
+ ]),
141
+ );
142
+
143
+ const temporalCandidates = deps.scopeQueryAwarePaths(
144
+ rawTemporal,
145
+ recallNamespaces,
146
+ );
147
+ const tagCandidates = deps.scopeQueryAwarePaths(
148
+ tagSignals.paths,
149
+ recallNamespaces,
150
+ );
151
+ const maxCandidates = deps.config.queryAwareIndexingMaxCandidates;
152
+
153
+ let candidatePaths: Set<string> | null = null;
154
+ let combination: QueryAwarePrefilter["combination"] = "none";
155
+ let filteredToFullSearch = false;
156
+
157
+ if (
158
+ tagSignals.matchedTags.length > 0 &&
159
+ tagCandidates !== null &&
160
+ tagCandidates.size === 0
161
+ ) {
162
+ candidatePaths = tagCandidates;
163
+ combination = "tag";
164
+ } else if (temporalCandidates !== null && tagCandidates !== null) {
165
+ const intersection = new Set(
166
+ Array.from(temporalCandidates).filter((memoryPath) =>
167
+ tagCandidates.has(memoryPath),
168
+ ),
169
+ );
170
+ if (intersection.size > 0) {
171
+ candidatePaths = intersection;
172
+ combination = "intersection";
173
+ } else {
174
+ candidatePaths = new Set([...temporalCandidates, ...tagCandidates]);
175
+ combination = "union";
176
+ }
177
+ } else if (temporalCandidates !== null) {
178
+ candidatePaths = temporalCandidates;
179
+ combination = "temporal";
180
+ } else if (tagCandidates !== null) {
181
+ candidatePaths = tagCandidates;
182
+ combination = "tag";
183
+ }
184
+
185
+ if (
186
+ candidatePaths &&
187
+ maxCandidates > 0 &&
188
+ candidatePaths.size > maxCandidates
189
+ ) {
190
+ filteredToFullSearch = true;
191
+ candidatePaths = null;
192
+ }
193
+
194
+ return {
195
+ candidatePaths,
196
+ temporalFromDate,
197
+ matchedTags: tagSignals.matchedTags,
198
+ expandedTags: tagSignals.expandedTags,
199
+ combination,
200
+ filteredToFullSearch,
201
+ };
202
+ }
203
+
204
+ export async function searchEmbeddingFallback(
205
+ deps: Pick<PrefilterAndArtifactDeps, "config" | "embeddingFallback" | "storage">,
206
+ query: string,
207
+ limit: number,
208
+ ): Promise<QmdSearchResult[]> {
209
+ if (!resolveMemoryLifecycleCapabilities(deps.config).embeddingFallback) return [];
210
+ if (!(await deps.embeddingFallback.isAvailable())) return [];
211
+ const hits = await deps.embeddingFallback.search(query, limit);
212
+ if (hits.length === 0) return [];
213
+
214
+ const results: QmdSearchResult[] = [];
215
+ for (const hit of hits) {
216
+ const fullPath = path.isAbsolute(hit.path)
217
+ ? hit.path
218
+ : path.join(deps.config.memoryDir, hit.path);
219
+ const memory = await deps.storage.readMemoryByPath(fullPath);
220
+ if (!memory) continue;
221
+ results.push({
222
+ docid: hit.id,
223
+ path: fullPath,
224
+ score: hit.score,
225
+ snippet: memory.content.slice(0, 400).replace(/\n/g, " "),
226
+ });
227
+ }
228
+ return results;
229
+ }
230
+
231
+ export async function searchQueryAwareFallback(
232
+ deps: Pick<PrefilterAndArtifactDeps, "config" | "searchScopedMemoryCandidates">,
233
+ prompt: string,
234
+ limit: number,
235
+ queryAwarePrefilter?: QueryAwarePrefilter,
236
+ abortSignal?: AbortSignal,
237
+ ): Promise<QmdSearchResult[]> {
238
+ throwIfRecallAborted(abortSignal);
239
+ const cappedLimit = Math.max(0, limit);
240
+ if (cappedLimit === 0 || queryAwarePrefilter?.candidatePaths?.size === 0) return [];
241
+
242
+ const candidatePaths = queryAwarePrefilter?.candidatePaths;
243
+ const scopedSeedResults = (
244
+ candidatePaths?.size
245
+ ? await deps.searchScopedMemoryCandidates(
246
+ candidatePaths,
247
+ prompt,
248
+ candidatePaths.size,
249
+ { allowArchived: true },
250
+ )
251
+ : []
252
+ ).filter((result) => !isGenericRecallExcludedPath(result.path, deps.config, "qmd"));
253
+ return scopedSeedResults.slice(0, cappedLimit);
254
+ }
@@ -306,7 +306,7 @@ export class SessionContextCoordinator {
306
306
  * Register a peer ID for a session so recall can inject the peer's
307
307
  * profile into context (issue #679 PR 3/5). Pass `null` to clear.
308
308
  *
309
- * Connectors and the `before_agent_start` hook call this when the
309
+ * Connectors and the `before_prompt_build` hook call this when the
310
310
  * session's counter-party is known. The ID is validated against
311
311
  * `PEER_ID_PATTERN` before storing.
312
312
  *
@@ -656,7 +656,7 @@ export class Orchestrator {
656
656
 
657
657
  /**
658
658
  * Per-session workspace selections keyed by sessionKey.
659
- * Set by the before_agent_start hook so recall() uses the correct
659
+ * Set by the before_prompt_build hook so recall() uses the correct
660
660
  * agent workspace for BOOT.md injection. Cleared after each recall.
661
661
  * Using a Map prevents concurrent sessions from overwriting each other.
662
662
  */
package/src/sdk-compat.ts CHANGED
@@ -64,10 +64,8 @@ export function detectSdkCapabilities(api: Record<string, unknown>): SdkCapabili
64
64
  // - registerMemoryCapability (capability-based SDKs, ≥2026.4.5; the
65
65
  // deprecated registerMemoryPromptSection may be absent)
66
66
  // - registrationMode (explicit registration lifecycle signal)
67
- // Just having runtime.version is NOT sufficient some legacy builds
68
- // expose it. Omitting registerMemoryCapability here would cause the
69
- // legacy before_agent_start hook to be registered on new SDKs that only
70
- // expose registerMemoryCapability, silently breaking memory injection.
67
+ // Runtime version alone is not authoritative because some transitional
68
+ // builds expose it without the modern prompt-hook surfaces.
71
69
  const hasNewHookSystem =
72
70
  hasRegisterMemoryPromptSection || hasRegisterMemoryCapability || hasRegistrationMode;
73
71
 
@@ -48,6 +48,9 @@ export interface ExtractionGroundingContext {
48
48
  roleAssertionSources?: ExtractionGroundingRoleSources;
49
49
  messageTimestamp: Date | undefined;
50
50
  sourceConnector?: string;
51
+ /** Input came from an always-on recorder (issue #2294); the proactive
52
+ * second pass reads it so its prompts carry the same media warning. */
53
+ ambientCapture?: boolean;
51
54
  scopeClassificationEnabled?: boolean;
52
55
  }
53
56
 
@@ -0,0 +1,362 @@
1
+ /**
2
+ * Recall-profile-storage contract lifecycle subject for the scenario-matrix harness.
3
+ * Exercises resolveCompositeProfileStorage across single-storage pass-through,
4
+ * empty storage fallback, multi-storage precedence, and aggregation deduplication.
5
+ */
6
+
7
+ import assert from "node:assert/strict";
8
+ import path from "node:path";
9
+
10
+ import {
11
+ type CompositeProfileStorageResult,
12
+ resolveCompositeProfileStorage,
13
+ } from "../../orchestration/recall-profile-storage.js";
14
+ import { StorageManager } from "../../storage.js";
15
+ import { type LifecycleSubject, type MatrixRow, runLifecycleMatrix } from "../lifecycle-matrix.js";
16
+ import { cleanupDir, mkTempMemoryDir } from "../orchestrator-lite.js";
17
+
18
+ interface RecallProfileStorageState {
19
+ readonly memoryDir: string;
20
+ readonly storages: StorageManager[];
21
+ readonly extraDirs: string[];
22
+ result?: CompositeProfileStorageResult;
23
+ readProfileResult?: string;
24
+ readQuestionsResult?: unknown[];
25
+ readIdentityAnchorResult?: string | null;
26
+ readIdentityImprovementLoopsResult?: string | null;
27
+ readContinuityIncidentsResult?: unknown[];
28
+ listEntityNamesResult?: string[];
29
+ readEntityResult?: unknown;
30
+ readMemoryByPathResult?: unknown;
31
+ readAllMemoriesResult?: unknown[];
32
+ }
33
+
34
+ const subject: LifecycleSubject<RecallProfileStorageState> = {
35
+ appliesTo(row: MatrixRow): boolean | string {
36
+ if (row.id === "compaction-flush") {
37
+ return "profile storage proxying has no compaction flush behavior; compaction operates on transcripts";
38
+ }
39
+ if (row.id === "before-reset") {
40
+ return "profile storage proxying has no before_reset lifecycle hook; reset operates on session state";
41
+ }
42
+ return true;
43
+ },
44
+
45
+ async setup(row: MatrixRow): Promise<RecallProfileStorageState> {
46
+ const memoryDir = await mkTempMemoryDir(`recall-profile-storage-${row.id}`);
47
+ const extraDirs: string[] = [];
48
+
49
+ try {
50
+ if (row.id === "sparse-metadata-without-binding") {
51
+ return { memoryDir, storages: [], extraDirs };
52
+ }
53
+
54
+ if (row.id === "sparse-metadata-with-binding") {
55
+ const s1 = new StorageManager(memoryDir);
56
+ await s1.writeProfile("# Single Profile\n- Fact 1");
57
+ return { memoryDir, storages: [s1], extraDirs };
58
+ }
59
+
60
+ if (row.id === "explicit-provider-identity") {
61
+ const dir2 = await mkTempMemoryDir(`recall-profile-storage-${row.id}-2`);
62
+ extraDirs.push(dir2);
63
+ const dir3 = await mkTempMemoryDir(`recall-profile-storage-${row.id}-3`);
64
+ extraDirs.push(dir3);
65
+
66
+ const s1 = new StorageManager(memoryDir);
67
+ const s2 = new StorageManager(dir2);
68
+ const s3 = new StorageManager(dir3);
69
+
70
+ await s1.writeEntity("Alice", "person", ["Alice works on Remnic"]);
71
+
72
+ await s2.writeProfile("# Profile 2\n- User prefers TypeScript");
73
+ await s2.writeIdentityAnchor("# Identity Anchor 2\n- Value: Integrity");
74
+ await s2.writeEntity("Bob", "person", ["Bob works on Remnic"]);
75
+
76
+ await s3.writeProfile("# Profile 3\n- User prefers Rust");
77
+ await s3.writeIdentityAnchor("# Identity Anchor 3\n- Value: Speed");
78
+
79
+ return { memoryDir, storages: [s1, s2, s3], extraDirs };
80
+ }
81
+
82
+ if (row.id === "provider-rebinding") {
83
+ const dir2 = await mkTempMemoryDir(`recall-profile-storage-${row.id}-2`);
84
+ extraDirs.push(dir2);
85
+
86
+ const s1 = new StorageManager(memoryDir);
87
+ const s2 = new StorageManager(memoryDir);
88
+ const s3 = new StorageManager(dir2);
89
+ const s4 = { dir: "" } as unknown as StorageManager;
90
+
91
+ return { memoryDir, storages: [s1, s2, s3, s4], extraDirs };
92
+ }
93
+
94
+ if (row.id === "restart-reload-recovery") {
95
+ const dir2 = await mkTempMemoryDir(`recall-profile-storage-${row.id}-2`);
96
+ extraDirs.push(dir2);
97
+
98
+ const s1Seed = new StorageManager(memoryDir);
99
+ const s2Seed = new StorageManager(dir2);
100
+ await s2Seed.writeProfile("# Recovered Profile\n- Persisted value");
101
+ await s2Seed.writeIdentityAnchor("# Recovered Anchor\n- Persisted anchor");
102
+
103
+ const seedResult = resolveCompositeProfileStorage({
104
+ profileStorages: [s1Seed, s2Seed],
105
+ memoryDir,
106
+ });
107
+ const seedProfile = await seedResult.profileStorage.readProfile();
108
+ assert.ok(seedProfile.includes("Recovered Profile"));
109
+
110
+ const s1Recovered = new StorageManager(memoryDir);
111
+ const s2Recovered = new StorageManager(dir2);
112
+
113
+ return { memoryDir, storages: [s1Recovered, s2Recovered], extraDirs };
114
+ }
115
+
116
+ if (row.id === "session-end") {
117
+ const dir2 = await mkTempMemoryDir(`recall-profile-storage-${row.id}-2`);
118
+ extraDirs.push(dir2);
119
+
120
+ const s1 = new StorageManager(memoryDir);
121
+ const s2 = new StorageManager(dir2);
122
+
123
+ await s2.writeProfile("# Final Session Profile\n- Summary");
124
+
125
+ return { memoryDir, storages: [s1, s2], extraDirs };
126
+ }
127
+
128
+ if (row.id === "dedupe-replay") {
129
+ const dir2 = await mkTempMemoryDir(`recall-profile-storage-${row.id}-2`);
130
+ extraDirs.push(dir2);
131
+
132
+ const s1 = new StorageManager(memoryDir);
133
+ const s2 = new StorageManager(memoryDir);
134
+ const s3 = new StorageManager(dir2);
135
+
136
+ await s1.writeQuestion("What is Remnic?", "context 1", 5);
137
+ await s1.writeQuestion("How does recall work?", "context 2", 10);
138
+ await s1.appendContinuityIncident({
139
+ symptom: "Incident 1 symptom",
140
+ suspectedCause: "Cause 1",
141
+ triggerWindow: "2026-08-01",
142
+ });
143
+ await s1.writeIdentityImprovementLoops("# Improvement Loop A");
144
+ await s1.writeEntity("ProjectX", "project", ["Fact X1"]);
145
+ await s1.writeMemory("fact", "Memory fact 1");
146
+
147
+ await s3.writeQuestion("Where are memories saved?", "context 3", 1);
148
+ await s3.appendContinuityIncident({
149
+ symptom: "Incident 2 symptom",
150
+ suspectedCause: "Cause 2",
151
+ triggerWindow: "2026-08-01",
152
+ });
153
+ await s3.writeIdentityImprovementLoops("# Improvement Loop B");
154
+ await s3.writeEntity("ProjectY", "project", ["Fact Y1"]);
155
+ await s3.writeMemory("fact", "Memory fact 2");
156
+
157
+ return { memoryDir, storages: [s1, s2, s3], extraDirs };
158
+ }
159
+
160
+ return { memoryDir, storages: [], extraDirs };
161
+ } catch (error) {
162
+ await cleanupDir(memoryDir);
163
+ for (const dir of extraDirs) {
164
+ await cleanupDir(dir);
165
+ }
166
+ throw error;
167
+ }
168
+ },
169
+
170
+ async exercise(state: RecallProfileStorageState, row: MatrixRow): Promise<void> {
171
+ const result = resolveCompositeProfileStorage({
172
+ profileStorages: state.storages,
173
+ memoryDir: state.memoryDir,
174
+ });
175
+ state.result = result;
176
+
177
+ const storage = result.profileStorage;
178
+
179
+ if (row.id === "sparse-metadata-without-binding") {
180
+ state.readProfileResult = await storage.readProfile();
181
+ state.readQuestionsResult = await storage.readQuestions();
182
+ state.readIdentityAnchorResult = await storage.readIdentityAnchor();
183
+ state.readIdentityImprovementLoopsResult = await storage.readIdentityImprovementLoops();
184
+ state.readContinuityIncidentsResult = await storage.readContinuityIncidents();
185
+ state.listEntityNamesResult = await storage.listEntityNames();
186
+ state.readEntityResult = await storage.readEntity("Alice");
187
+ state.readMemoryByPathResult = await storage.readMemoryByPath("fact/1.md");
188
+ state.readAllMemoriesResult = await storage.readAllMemories();
189
+ return;
190
+ }
191
+
192
+ if (row.id === "sparse-metadata-with-binding") {
193
+ state.readProfileResult = await storage.readProfile();
194
+ return;
195
+ }
196
+
197
+ if (row.id === "explicit-provider-identity") {
198
+ state.readProfileResult = await storage.readProfile();
199
+ state.readIdentityAnchorResult = await storage.readIdentityAnchor();
200
+ state.readEntityResult = await storage.readEntity("person-bob");
201
+ state.readMemoryByPathResult = await storage.readMemoryByPath("nonexistent.md");
202
+ return;
203
+ }
204
+
205
+ if (row.id === "restart-reload-recovery") {
206
+ state.readProfileResult = await storage.readProfile();
207
+ state.readIdentityAnchorResult = await storage.readIdentityAnchor();
208
+ return;
209
+ }
210
+
211
+ if (row.id === "session-end") {
212
+ state.readProfileResult = await storage.readProfile();
213
+ return;
214
+ }
215
+
216
+ if (row.id === "dedupe-replay") {
217
+ state.readQuestionsResult = await storage.readQuestions();
218
+ state.readIdentityImprovementLoopsResult = await storage.readIdentityImprovementLoops();
219
+ state.readContinuityIncidentsResult = await storage.readContinuityIncidents(10);
220
+ state.listEntityNamesResult = await storage.listEntityNames();
221
+ state.readAllMemoriesResult = await storage.readAllMemories();
222
+ return;
223
+ }
224
+ },
225
+
226
+ async invariants(state: RecallProfileStorageState, row: MatrixRow): Promise<void> {
227
+ assert.ok(state.result, "composite profile storage result was populated");
228
+
229
+ if (row.id === "sparse-metadata-without-binding") {
230
+ assert.equal(state.result.profileStorageDirs.length, 0, "empty storage list returns empty storage dirs");
231
+ assert.equal(
232
+ state.result.profileStorage.dir,
233
+ path.join(state.memoryDir, ".empty-scope-profile"),
234
+ "empty storage fallback has .empty-scope-profile dir"
235
+ );
236
+ assert.equal(state.readProfileResult, "", "empty storage readProfile returns empty string");
237
+ assert.deepEqual(state.readQuestionsResult, [], "empty storage readQuestions returns empty array");
238
+ assert.equal(state.readIdentityAnchorResult, "", "empty storage readIdentityAnchor returns empty string");
239
+ assert.equal(
240
+ state.readIdentityImprovementLoopsResult,
241
+ "",
242
+ "empty storage readIdentityImprovementLoops returns empty string"
243
+ );
244
+ assert.deepEqual(
245
+ state.readContinuityIncidentsResult,
246
+ [],
247
+ "empty storage readContinuityIncidents returns empty array"
248
+ );
249
+ assert.deepEqual(state.listEntityNamesResult, [], "empty storage listEntityNames returns empty array");
250
+ assert.equal(state.readEntityResult, null, "empty storage readEntity returns null");
251
+ assert.equal(state.readMemoryByPathResult, null, "empty storage readMemoryByPath returns null");
252
+ assert.deepEqual(state.readAllMemoriesResult, [], "empty storage readAllMemories returns empty array");
253
+ return;
254
+ }
255
+
256
+ if (row.id === "sparse-metadata-with-binding") {
257
+ assert.strictEqual(
258
+ state.result.profileStorage,
259
+ state.storages[0],
260
+ "single storage pass-through returns unproxied target instance"
261
+ );
262
+ assert.deepEqual(
263
+ state.result.profileStorageDirs,
264
+ [state.memoryDir],
265
+ "single storage pass-through returns target storage directory"
266
+ );
267
+ assert.ok(
268
+ state.readProfileResult?.includes("Single Profile"),
269
+ "single storage readProfile delegates to target storage"
270
+ );
271
+ return;
272
+ }
273
+
274
+ if (row.id === "explicit-provider-identity") {
275
+ assert.ok(
276
+ state.readProfileResult?.includes("Profile 2"),
277
+ "readProfile returns first non-empty profile in composite order"
278
+ );
279
+ assert.ok(
280
+ state.readIdentityAnchorResult?.includes("Identity Anchor 2"),
281
+ "readIdentityAnchor returns first non-empty anchor in composite order"
282
+ );
283
+ assert.ok(typeof state.readEntityResult === "string", "readEntity returns raw entity string");
284
+ assert.ok(
285
+ (state.readEntityResult as string).includes("Bob"),
286
+ "readEntity returns entity content from matching storage"
287
+ );
288
+ assert.equal(state.readMemoryByPathResult, null, "readMemoryByPath returns null when absent in all storages");
289
+ return;
290
+ }
291
+
292
+ if (row.id === "provider-rebinding") {
293
+ assert.deepEqual(
294
+ state.result.profileStorageDirs,
295
+ [state.memoryDir, state.extraDirs[0]],
296
+ "profileStorageDirs deduplicates directories and filters out empty strings"
297
+ );
298
+ return;
299
+ }
300
+
301
+ if (row.id === "restart-reload-recovery") {
302
+ assert.ok(
303
+ state.readProfileResult?.includes("Recovered Profile"),
304
+ "composite profile storage recovers state over fresh instances after restart"
305
+ );
306
+ assert.ok(
307
+ state.readIdentityAnchorResult?.includes("Recovered Anchor"),
308
+ "composite identity anchor recovers state over fresh instances after restart"
309
+ );
310
+ return;
311
+ }
312
+
313
+ if (row.id === "session-end") {
314
+ assert.ok(
315
+ state.readProfileResult?.includes("Final Session Profile"),
316
+ "session-end readProfile correctly falls back past blank profile to non-empty profile"
317
+ );
318
+ return;
319
+ }
320
+
321
+ if (row.id === "dedupe-replay") {
322
+ const questions = state.readQuestionsResult;
323
+ assert.ok(Array.isArray(questions), "readQuestions returns an array");
324
+ assert.equal(questions.length, 3, "readQuestions deduplicates repeated storage records");
325
+ const firstQ = questions[0];
326
+ assert.ok(firstQ && typeof firstQ === "object" && "question" in firstQ && "priority" in firstQ);
327
+ assert.equal(firstQ.question, "How does recall work?", "readQuestions sorts by priority descending");
328
+ assert.equal(firstQ.priority, 10, "highest priority question comes first");
329
+
330
+ const loops = state.readIdentityImprovementLoopsResult;
331
+ assert.equal(
332
+ loops,
333
+ "# Improvement Loop A\n\n# Improvement Loop B",
334
+ "readIdentityImprovementLoops deduplicates identical sections and joins with double newline"
335
+ );
336
+
337
+ const incidents = state.readContinuityIncidentsResult as unknown[];
338
+ assert.ok(Array.isArray(incidents), "readContinuityIncidents returns an array");
339
+ assert.equal(incidents.length, 2, "readContinuityIncidents deduplicates repeated storage records");
340
+
341
+ const entityNames = state.listEntityNamesResult;
342
+ assert.ok(Array.isArray(entityNames), "listEntityNames returns an array");
343
+ assert.deepEqual(new Set(entityNames), new Set(["project-projectx", "project-projecty"]));
344
+
345
+ const memories = state.readAllMemoriesResult as unknown[];
346
+ assert.ok(Array.isArray(memories), "readAllMemories returns an array");
347
+ assert.equal(memories.length, 2, "readAllMemories aggregates memories across composite storages");
348
+ return;
349
+ }
350
+ },
351
+
352
+ async teardown(state: RecallProfileStorageState): Promise<void> {
353
+ if (state.memoryDir) {
354
+ await cleanupDir(state.memoryDir);
355
+ }
356
+ for (const dir of state.extraDirs) {
357
+ await cleanupDir(dir);
358
+ }
359
+ },
360
+ };
361
+
362
+ runLifecycleMatrix("recall-profile-storage", subject);