@remnic/core 9.3.689 → 9.3.691

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 (322) hide show
  1. package/dist/access-boundary.d.ts +7 -6
  2. package/dist/access-boundary.js +19 -18
  3. package/dist/access-cli.js +111 -47
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +6 -5
  6. package/dist/access-http.js +25 -24
  7. package/dist/access-mcp.d.ts +13 -5
  8. package/dist/access-mcp.js +22 -21
  9. package/dist/access-operations.d.ts +13 -7
  10. package/dist/access-operations.js +23 -20
  11. package/dist/access-schema.js +3 -3
  12. package/dist/{access-service-DmCHJ4cH.d.ts → access-service-CKnFdxH-.d.ts} +63 -3
  13. package/dist/access-service.d.ts +6 -5
  14. package/dist/access-service.js +18 -17
  15. package/dist/access-surface-catalog.d.ts +6 -5
  16. package/dist/access-surface-catalog.js +2 -0
  17. package/dist/access-surface-catalog.js.map +1 -1
  18. package/dist/action-confidence.d.ts +1 -1
  19. package/dist/active-memory-bridge.d.ts +1 -1
  20. package/dist/active-recall.d.ts +1 -1
  21. package/dist/active-recall.js +1 -1
  22. package/dist/adapters/index.js +4 -4
  23. package/dist/adapters/registry.js +2 -2
  24. package/dist/{auto-sync-NUQWSFQD.js → auto-sync-RWXT6P5M.js} +5 -5
  25. package/dist/behavior-learner.d.ts +1 -1
  26. package/dist/behavior-signals.d.ts +1 -1
  27. package/dist/bootstrap.d.ts +5 -4
  28. package/dist/briefing.d.ts +2 -1
  29. package/dist/briefing.js +6 -5
  30. package/dist/buffer-surprise-report.d.ts +1 -1
  31. package/dist/buffer.d.ts +2 -1
  32. package/dist/calibration.d.ts +1 -1
  33. package/dist/calibration.js +2 -2
  34. package/dist/capabilities.d.ts +1 -1
  35. package/dist/{catalog-COqWZlZ6.d.ts → catalog-BEWLjdOr.d.ts} +2 -2
  36. package/dist/causal-behavior.d.ts +1 -1
  37. package/dist/causal-consolidation.d.ts +1 -1
  38. package/dist/causal-consolidation.js +9 -8
  39. package/dist/causal-consolidation.js.map +1 -1
  40. package/dist/{chunk-GTDH3IUH.js → chunk-2D27ODNX.js} +2 -2
  41. package/dist/{chunk-3K37VPM3.js → chunk-3BRA3N5A.js} +2 -2
  42. package/dist/{chunk-4FE2K57M.js → chunk-3VVH6ZQW.js} +2 -2
  43. package/dist/{chunk-FIVDN2SM.js → chunk-4RGWCRIJ.js} +4 -4
  44. package/dist/{chunk-AGJH5ISO.js → chunk-5KBNNUXS.js} +2 -2
  45. package/dist/{chunk-OV4D5T7V.js → chunk-66HROLW5.js} +2 -2
  46. package/dist/{chunk-Z2M6YTAJ.js → chunk-7UVDIWLL.js} +34 -5
  47. package/dist/chunk-7UVDIWLL.js.map +1 -0
  48. package/dist/{chunk-QANVLERJ.js → chunk-ADUAF5EV.js} +2 -2
  49. package/dist/{chunk-LLONI6PY.js → chunk-AFWEUTMS.js} +2 -2
  50. package/dist/{chunk-W63OY3J7.js → chunk-D3DNGQNQ.js} +3 -3
  51. package/dist/{chunk-T422SYM6.js → chunk-DCWIQFNA.js} +5 -5
  52. package/dist/{chunk-NGFEWFNK.js → chunk-DQHZA7Y3.js} +1 -1
  53. package/dist/chunk-DQHZA7Y3.js.map +1 -0
  54. package/dist/{chunk-OBXTMFZQ.js → chunk-ECZPWSB5.js} +2 -2
  55. package/dist/{chunk-BFVPIKDY.js → chunk-GCOSQBQ2.js} +3 -3
  56. package/dist/{chunk-SAEZIIID.js → chunk-IPJD7WJH.js} +2 -2
  57. package/dist/{chunk-KQAFEZQX.js → chunk-IPLYGWQF.js} +5 -5
  58. package/dist/{chunk-MDYG7VI7.js → chunk-IREVVFTM.js} +10 -1
  59. package/dist/{chunk-MDYG7VI7.js.map → chunk-IREVVFTM.js.map} +1 -1
  60. package/dist/{chunk-RKNJBZ55.js → chunk-JBPKEARU.js} +4 -4
  61. package/dist/{chunk-RHXFYIHA.js → chunk-K7I24ZO2.js} +42 -27
  62. package/dist/chunk-K7I24ZO2.js.map +1 -0
  63. package/dist/{chunk-XU7363OX.js → chunk-KO6OL4I7.js} +617 -23
  64. package/dist/chunk-KO6OL4I7.js.map +1 -0
  65. package/dist/{chunk-UTYBJR7M.js → chunk-MCV2DD6F.js} +2 -2
  66. package/dist/{chunk-3FC6LW6T.js → chunk-MRFVMSTR.js} +2 -2
  67. package/dist/{chunk-WDXCNJSF.js → chunk-MXZWKOOR.js} +2 -2
  68. package/dist/{chunk-QSQW54U5.js → chunk-NCRDCTEJ.js} +33 -10
  69. package/dist/chunk-NCRDCTEJ.js.map +1 -0
  70. package/dist/{chunk-6O6A6YUO.js → chunk-NUZ3N445.js} +7 -7
  71. package/dist/{chunk-UHUZXWDX.js → chunk-OOO7ZKOD.js} +16 -5
  72. package/dist/chunk-OOO7ZKOD.js.map +1 -0
  73. package/dist/{chunk-GS55WYRL.js → chunk-QFG23CRE.js} +2 -2
  74. package/dist/{chunk-UDJLF3BO.js → chunk-QLRYXOAD.js} +2 -2
  75. package/dist/chunk-QNG35H27.js +2190 -0
  76. package/dist/chunk-QNG35H27.js.map +1 -0
  77. package/dist/{chunk-A4HH2EWA.js → chunk-QVG4LAQO.js} +2 -2
  78. package/dist/{chunk-4N3TFFPH.js → chunk-SAC5V6JT.js} +2 -2
  79. package/dist/chunk-SVOZFLIQ.js +16 -0
  80. package/dist/chunk-SVOZFLIQ.js.map +1 -0
  81. package/dist/{chunk-JZBFL7RI.js → chunk-TJ5IIQIR.js} +468 -2265
  82. package/dist/chunk-TJ5IIQIR.js.map +1 -0
  83. package/dist/{chunk-UXLZOVCN.js → chunk-TSGREOYY.js} +38 -6
  84. package/dist/chunk-TSGREOYY.js.map +1 -0
  85. package/dist/{chunk-B4XVLHJA.js → chunk-U2OBZSGG.js} +2 -2
  86. package/dist/{chunk-STOEE37X.js → chunk-UOCLRM74.js} +2 -2
  87. package/dist/{chunk-A7UZR6RA.js → chunk-UTUYAPLY.js} +14 -1
  88. package/dist/chunk-UTUYAPLY.js.map +1 -0
  89. package/dist/{chunk-JPETDXED.js → chunk-UUMYB3FR.js} +2 -2
  90. package/dist/{chunk-BLIWOONZ.js → chunk-VFVG4NS2.js} +33 -20
  91. package/dist/chunk-VFVG4NS2.js.map +1 -0
  92. package/dist/{chunk-OWFY6NGQ.js → chunk-VH2DHBHG.js} +2 -2
  93. package/dist/chunk-VH2DHBHG.js.map +1 -0
  94. package/dist/{chunk-WIHPNY65.js → chunk-W4ELOUNX.js} +2 -2
  95. package/dist/{chunk-VX6OBUDW.js → chunk-WIKNVFBS.js} +2 -2
  96. package/dist/{chunk-2SNKUSQC.js → chunk-X5J3JZR3.js} +4 -4
  97. package/dist/{chunk-PH3HOKYW.js → chunk-ZLXGFAML.js} +2 -2
  98. package/dist/{cli-D8nZ2MPH.d.ts → cli-CrSW_KRq.d.ts} +3 -3
  99. package/dist/cli.d.ts +7 -6
  100. package/dist/cli.js +40 -38
  101. package/dist/compounding/engine.d.ts +2 -1
  102. package/dist/compounding/engine.js +6 -5
  103. package/dist/compounding/preference-consolidator.d.ts +1 -1
  104. package/dist/compression-optimizer.d.ts +1 -1
  105. package/dist/config.d.ts +1 -1
  106. package/dist/config.js +1 -1
  107. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  108. package/dist/connectors/codex-materialize-runner.js +6 -5
  109. package/dist/connectors/codex-materialize.d.ts +1 -1
  110. package/dist/connectors/index.d.ts +1 -1
  111. package/dist/connectors/index.js +6 -5
  112. package/dist/consolidation-provenance-check.d.ts +2 -1
  113. package/dist/consolidation-undo.d.ts +2 -1
  114. package/dist/contradiction/index.d.ts +2 -1
  115. package/dist/contradiction/index.js +4 -4
  116. package/dist/conversation-index/backend.d.ts +1 -1
  117. package/dist/conversation-index/backend.js +2 -2
  118. package/dist/conversation-index/chunker.d.ts +1 -1
  119. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  120. package/dist/conversation-index/indexer.d.ts +1 -1
  121. package/dist/conversation-index/search.d.ts +1 -1
  122. package/dist/day-summary.d.ts +1 -1
  123. package/dist/delinearize.d.ts +1 -1
  124. package/dist/direct-answer-wiring.d.ts +1 -1
  125. package/dist/direct-answer.d.ts +1 -1
  126. package/dist/embedding-fallback.d.ts +1 -1
  127. package/dist/enrichment/index.d.ts +1 -1
  128. package/dist/entity-retrieval.d.ts +2 -1
  129. package/dist/entity-retrieval.js +6 -5
  130. package/dist/entity-schema.d.ts +1 -1
  131. package/dist/event-time.d.ts +35 -0
  132. package/dist/event-time.js +325 -0
  133. package/dist/event-time.js.map +1 -0
  134. package/dist/explicit-capture.d.ts +5 -4
  135. package/dist/extraction-judge-telemetry.d.ts +1 -1
  136. package/dist/extraction-judge-training.d.ts +1 -1
  137. package/dist/extraction-judge.d.ts +1 -1
  138. package/dist/extraction-judge.js +3 -3
  139. package/dist/extraction.d.ts +1 -1
  140. package/dist/extraction.js +3 -3
  141. package/dist/fallback-llm.d.ts +1 -1
  142. package/dist/fallback-llm.js +2 -2
  143. package/dist/identity-continuity.d.ts +1 -1
  144. package/dist/importance.d.ts +1 -1
  145. package/dist/index.d.ts +10 -9
  146. package/dist/index.js +78 -75
  147. package/dist/index.js.map +1 -1
  148. package/dist/intent.d.ts +1 -1
  149. package/dist/lcm/engine.d.ts +1 -1
  150. package/dist/lcm/engine.js +2 -2
  151. package/dist/lcm/index.d.ts +1 -1
  152. package/dist/lcm/index.js +2 -2
  153. package/dist/lcm/tools.d.ts +1 -1
  154. package/dist/lifecycle.d.ts +1 -1
  155. package/dist/live-connectors-runner.d.ts +1 -1
  156. package/dist/local-llm.d.ts +1 -1
  157. package/dist/maintenance/memory-governance.d.ts +1 -1
  158. package/dist/maintenance/memory-governance.js +6 -5
  159. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +6 -5
  160. package/dist/maintenance/rebuild-memory-projection.js +7 -6
  161. package/dist/mcp-memory-inspector-app.d.ts +6 -5
  162. package/dist/memory-action-policy.d.ts +1 -1
  163. package/dist/memory-cache.d.ts +1 -1
  164. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  165. package/dist/memory-projection-store.d.ts +1 -1
  166. package/dist/memory-provenance.d.ts +1 -1
  167. package/dist/memory-worth-outcomes.d.ts +2 -1
  168. package/dist/models-json.d.ts +1 -1
  169. package/dist/namespaces/migrate.d.ts +3 -2
  170. package/dist/namespaces/migrate.js +11 -10
  171. package/dist/namespaces/principal.d.ts +1 -1
  172. package/dist/namespaces/search.d.ts +1 -1
  173. package/dist/namespaces/search.js +4 -4
  174. package/dist/namespaces/storage.d.ts +3 -2
  175. package/dist/namespaces/storage.js +6 -5
  176. package/dist/native-knowledge.d.ts +1 -1
  177. package/dist/operator-toolkit.d.ts +2 -1
  178. package/dist/operator-toolkit.js +15 -13
  179. package/dist/{orchestrator-CA6ouzBn.d.ts → orchestrator-DvswGK76.d.ts} +147 -3
  180. package/dist/orchestrator.d.ts +5 -4
  181. package/dist/orchestrator.js +37 -35
  182. package/dist/patterns-cli.d.ts +1 -1
  183. package/dist/policy-runtime.d.ts +1 -1
  184. package/dist/provenance.d.ts +1 -1
  185. package/dist/qmd-recall-cache.d.ts +1 -1
  186. package/dist/qmd.d.ts +1 -1
  187. package/dist/recall-disclosure-escalation.d.ts +1 -1
  188. package/dist/recall-explain-renderer.d.ts +1 -1
  189. package/dist/recall-explain-renderer.js +3 -3
  190. package/dist/recall-planner-llm.d.ts +1 -1
  191. package/dist/recall-planner-llm.js +2 -2
  192. package/dist/recall-state.d.ts +1 -1
  193. package/dist/recall-tag-filter.d.ts +1 -1
  194. package/dist/recall-xray-cli.d.ts +1 -1
  195. package/dist/recall-xray-cli.js +4 -4
  196. package/dist/recall-xray-renderer.d.ts +1 -1
  197. package/dist/recall-xray-renderer.js +3 -3
  198. package/dist/recall-xray.d.ts +1 -1
  199. package/dist/recall-xray.js +2 -2
  200. package/dist/resolve-auth-token.d.ts +1 -1
  201. package/dist/resume-bundles.js +2 -2
  202. package/dist/retrieval-agents.d.ts +1 -1
  203. package/dist/retrieval-tiers.d.ts +1 -1
  204. package/dist/routing/engine.d.ts +1 -1
  205. package/dist/routing/store.d.ts +1 -1
  206. package/dist/schemas.d.ts +10 -10
  207. package/dist/search/embed-helper.d.ts +1 -1
  208. package/dist/search/factory.d.ts +1 -1
  209. package/dist/search/factory.js +3 -3
  210. package/dist/search/index.d.ts +1 -1
  211. package/dist/search/index.js +3 -3
  212. package/dist/search/lancedb-backend.d.ts +1 -1
  213. package/dist/search/meilisearch-backend.d.ts +1 -1
  214. package/dist/search/noop-backend.d.ts +1 -1
  215. package/dist/search/orama-backend.d.ts +1 -1
  216. package/dist/search/port.d.ts +1 -1
  217. package/dist/search/remote-backend.d.ts +1 -1
  218. package/dist/{semantic-consolidation-CWch5uM7.d.ts → semantic-consolidation-D5U0Mrdb.d.ts} +1 -1
  219. package/dist/semantic-consolidation.d.ts +2 -2
  220. package/dist/semantic-consolidation.js +7 -6
  221. package/dist/semantic-rule-promotion.js +6 -5
  222. package/dist/semantic-rule-verifier.d.ts +1 -1
  223. package/dist/semantic-rule-verifier.js +6 -5
  224. package/dist/session-observer-bands.d.ts +1 -1
  225. package/dist/session-observer-state.d.ts +1 -1
  226. package/dist/shared-context/manager.d.ts +1 -1
  227. package/dist/signal.d.ts +1 -1
  228. package/dist/storage.d.ts +6 -28
  229. package/dist/storage.js +8 -6
  230. package/dist/structured-attributes.d.ts +29 -0
  231. package/dist/structured-attributes.js +8 -0
  232. package/dist/structured-attributes.js.map +1 -0
  233. package/dist/summarizer.d.ts +1 -1
  234. package/dist/summarizer.js +3 -3
  235. package/dist/summary-snapshot.d.ts +1 -1
  236. package/dist/temporal-supersession.d.ts +2 -1
  237. package/dist/temporal-supersession.js +2 -2
  238. package/dist/temporal-validity.d.ts +22 -2
  239. package/dist/temporal-validity.js +3 -1
  240. package/dist/threading.d.ts +1 -1
  241. package/dist/tier-migration.d.ts +2 -1
  242. package/dist/tier-routing.d.ts +1 -1
  243. package/dist/topics.d.ts +1 -1
  244. package/dist/transcript.d.ts +1 -1
  245. package/dist/transfer/autodetect.js +1 -1
  246. package/dist/transfer/backup.js +1 -1
  247. package/dist/transfer/capsule-export.js +2 -2
  248. package/dist/{types-Dm5xxVrr.d.ts → types-2NoBoRBM.d.ts} +38 -0
  249. package/dist/types.d.ts +1 -1
  250. package/dist/types.js +1 -1
  251. package/dist/utility-runtime.d.ts +1 -1
  252. package/dist/verified-recall.js +6 -5
  253. package/package.json +2 -2
  254. package/src/access-boundary.ts +2 -1
  255. package/src/access-cli.test.ts +40 -0
  256. package/src/access-cli.ts +90 -2
  257. package/src/access-http.ts +35 -6
  258. package/src/access-mcp.ts +34 -0
  259. package/src/access-operations.ts +45 -0
  260. package/src/access-service.ts +60 -0
  261. package/src/access-surface-catalog.test.ts +1 -1
  262. package/src/access-surface-catalog.ts +2 -0
  263. package/src/bitemporal-frontmatter.test.ts +177 -0
  264. package/src/cli.ts +18 -0
  265. package/src/coding/architecture-card.test.ts +544 -0
  266. package/src/coding/architecture-card.ts +687 -0
  267. package/src/coding/architecture-surfaces.test.ts +579 -0
  268. package/src/coding/architecture-surfaces.ts +457 -0
  269. package/src/config.ts +13 -0
  270. package/src/event-time.test.ts +514 -0
  271. package/src/event-time.ts +548 -0
  272. package/src/maintenance/namespace-maintenance-fanout.test.ts +595 -0
  273. package/src/maintenance/namespace-maintenance-fanout.ts +318 -0
  274. package/src/maintenance/namespace-planner.ts +74 -16
  275. package/src/operator-toolkit.ts +25 -0
  276. package/src/orchestrator.ts +171 -1
  277. package/src/storage.ts +40 -20
  278. package/src/structured-attributes.ts +39 -0
  279. package/src/temporal-validity.ts +34 -0
  280. package/src/types.ts +38 -0
  281. package/dist/chunk-A7UZR6RA.js.map +0 -1
  282. package/dist/chunk-BLIWOONZ.js.map +0 -1
  283. package/dist/chunk-JZBFL7RI.js.map +0 -1
  284. package/dist/chunk-NGFEWFNK.js.map +0 -1
  285. package/dist/chunk-OWFY6NGQ.js.map +0 -1
  286. package/dist/chunk-QSQW54U5.js.map +0 -1
  287. package/dist/chunk-RHXFYIHA.js.map +0 -1
  288. package/dist/chunk-UHUZXWDX.js.map +0 -1
  289. package/dist/chunk-UXLZOVCN.js.map +0 -1
  290. package/dist/chunk-XU7363OX.js.map +0 -1
  291. package/dist/chunk-Z2M6YTAJ.js.map +0 -1
  292. /package/dist/{auto-sync-NUQWSFQD.js.map → auto-sync-RWXT6P5M.js.map} +0 -0
  293. /package/dist/{chunk-GTDH3IUH.js.map → chunk-2D27ODNX.js.map} +0 -0
  294. /package/dist/{chunk-3K37VPM3.js.map → chunk-3BRA3N5A.js.map} +0 -0
  295. /package/dist/{chunk-4FE2K57M.js.map → chunk-3VVH6ZQW.js.map} +0 -0
  296. /package/dist/{chunk-FIVDN2SM.js.map → chunk-4RGWCRIJ.js.map} +0 -0
  297. /package/dist/{chunk-AGJH5ISO.js.map → chunk-5KBNNUXS.js.map} +0 -0
  298. /package/dist/{chunk-OV4D5T7V.js.map → chunk-66HROLW5.js.map} +0 -0
  299. /package/dist/{chunk-QANVLERJ.js.map → chunk-ADUAF5EV.js.map} +0 -0
  300. /package/dist/{chunk-LLONI6PY.js.map → chunk-AFWEUTMS.js.map} +0 -0
  301. /package/dist/{chunk-W63OY3J7.js.map → chunk-D3DNGQNQ.js.map} +0 -0
  302. /package/dist/{chunk-T422SYM6.js.map → chunk-DCWIQFNA.js.map} +0 -0
  303. /package/dist/{chunk-OBXTMFZQ.js.map → chunk-ECZPWSB5.js.map} +0 -0
  304. /package/dist/{chunk-BFVPIKDY.js.map → chunk-GCOSQBQ2.js.map} +0 -0
  305. /package/dist/{chunk-SAEZIIID.js.map → chunk-IPJD7WJH.js.map} +0 -0
  306. /package/dist/{chunk-KQAFEZQX.js.map → chunk-IPLYGWQF.js.map} +0 -0
  307. /package/dist/{chunk-RKNJBZ55.js.map → chunk-JBPKEARU.js.map} +0 -0
  308. /package/dist/{chunk-UTYBJR7M.js.map → chunk-MCV2DD6F.js.map} +0 -0
  309. /package/dist/{chunk-3FC6LW6T.js.map → chunk-MRFVMSTR.js.map} +0 -0
  310. /package/dist/{chunk-WDXCNJSF.js.map → chunk-MXZWKOOR.js.map} +0 -0
  311. /package/dist/{chunk-6O6A6YUO.js.map → chunk-NUZ3N445.js.map} +0 -0
  312. /package/dist/{chunk-GS55WYRL.js.map → chunk-QFG23CRE.js.map} +0 -0
  313. /package/dist/{chunk-UDJLF3BO.js.map → chunk-QLRYXOAD.js.map} +0 -0
  314. /package/dist/{chunk-A4HH2EWA.js.map → chunk-QVG4LAQO.js.map} +0 -0
  315. /package/dist/{chunk-4N3TFFPH.js.map → chunk-SAC5V6JT.js.map} +0 -0
  316. /package/dist/{chunk-B4XVLHJA.js.map → chunk-U2OBZSGG.js.map} +0 -0
  317. /package/dist/{chunk-STOEE37X.js.map → chunk-UOCLRM74.js.map} +0 -0
  318. /package/dist/{chunk-JPETDXED.js.map → chunk-UUMYB3FR.js.map} +0 -0
  319. /package/dist/{chunk-WIHPNY65.js.map → chunk-W4ELOUNX.js.map} +0 -0
  320. /package/dist/{chunk-VX6OBUDW.js.map → chunk-WIKNVFBS.js.map} +0 -0
  321. /package/dist/{chunk-2SNKUSQC.js.map → chunk-X5J3JZR3.js.map} +0 -0
  322. /package/dist/{chunk-PH3HOKYW.js.map → chunk-ZLXGFAML.js.map} +0 -0
package/src/storage.ts CHANGED
@@ -321,6 +321,22 @@ function serializeFrontmatter(fm: MemoryFrontmatter): string {
321
321
  // memories round-trip unchanged; readers default `valid_at` to `created`.
322
322
  if (fm.valid_at) lines.push(`validAt: ${fm.valid_at}`);
323
323
  if (fm.invalid_at) lines.push(`invalidAt: ${fm.invalid_at}`);
324
+ // Issue #1578 — bi-temporal ingestion provenance. Validate on write so a
325
+ // corrupt `observedAt` (non-ISO / overflowed) cannot leak onto disk; reads
326
+ // are permissive, mirroring the `valid_at` precedent. `eventTimeSource` is
327
+ // a closed enum — reject anything outside it rather than persisting garbage.
328
+ if (fm.observedAt) {
329
+ const validated = normalizeMemoryWriteTimestamp("observedAt", fm.observedAt);
330
+ if (validated) lines.push(`observedAt: ${validated}`);
331
+ }
332
+ if (fm.eventTimeSource) {
333
+ if (fm.eventTimeSource !== "extracted" && fm.eventTimeSource !== "assumed") {
334
+ throw new Error(
335
+ `serializeFrontmatter: invalid eventTimeSource ${JSON.stringify(fm.eventTimeSource)} — expected "extracted" | "assumed"`,
336
+ );
337
+ }
338
+ lines.push(`eventTimeSource: ${fm.eventTimeSource}`);
339
+ }
324
340
  if (fm.forgottenAt) lines.push(`forgottenAt: ${fm.forgottenAt}`);
325
341
  if (fm.forgottenReason) lines.push(`forgottenReason: ${JSON.stringify(fm.forgottenReason)}`);
326
342
  // Lifecycle policy fields
@@ -808,6 +824,12 @@ function parseFrontmatter(
808
824
  // Issue #680 — explicit fact lifecycle round-trip.
809
825
  valid_at: fm.validAt || undefined,
810
826
  invalid_at: fm.invalidAt || undefined,
827
+ // Issue #1578 — bi-temporal ingestion provenance round-trip.
828
+ observedAt: fm.observedAt || undefined,
829
+ eventTimeSource:
830
+ fm.eventTimeSource === "extracted" || fm.eventTimeSource === "assumed"
831
+ ? fm.eventTimeSource
832
+ : undefined,
811
833
  forgottenAt: fm.forgottenAt || undefined,
812
834
  forgottenReason: parseFrontmatterStringValue(fm.forgottenReason),
813
835
  lifecycleState: (fm.lifecycleState as LifecycleState) || undefined,
@@ -1175,26 +1197,12 @@ export class ContentHashIndex {
1175
1197
  * normalizeAttributePairs({ foo: "bar", BAZ: "qux" })
1176
1198
  * // → "baz: qux; foo: bar"
1177
1199
  */
1178
- /**
1179
- * Remove the "[Attributes: ...]" suffix `writeMemory` appends to the
1180
- * stored body when structuredAttributes are present, yielding the raw
1181
- * fact text for content comparison. Inverse companion of
1182
- * `normalizeAttributePairs` enrichment. String operations, not regex —
1183
- * CodeQL js/polynomial-redos on a suffix-anchored pattern over
1184
- * library-supplied content.
1185
- */
1186
- export function stripAttributesSuffix(content: string): string {
1187
- const trimmed = content.trimEnd();
1188
- if (!trimmed.endsWith("]")) return content.trim();
1189
- const marker = "\n[Attributes: ";
1190
- const markerIndex = trimmed.lastIndexOf(marker);
1191
- if (markerIndex === -1) return content.trim();
1192
- // The block must be the FINAL line and contain no "]" before the
1193
- // closing bracket (mirrors the shape normalizeAttributePairs emits).
1194
- const inner = trimmed.slice(markerIndex + marker.length, -1);
1195
- if (inner.includes("]") || inner.includes("\n")) return content.trim();
1196
- return trimmed.slice(0, markerIndex).trim();
1197
- }
1200
+ // `stripAttributesSuffix` now lives in ./structured-attributes.ts (shared with
1201
+ // the coding surfaces + wearable service). Imported here so internal callers
1202
+ // (snapshotBeforeWrite, snapshotForProvenance) resolve, and re-exported to
1203
+ // keep the public storage API stable for existing callers (wearables, dist).
1204
+ import { stripAttributesSuffix } from "./structured-attributes.js";
1205
+ export { stripAttributesSuffix };
1198
1206
 
1199
1207
  export function normalizeAttributePairs(pairs: Record<string, string>): string {
1200
1208
  return Object.entries(pairs)
@@ -3347,6 +3355,12 @@ export class StorageManager {
3347
3355
  memoryKind?: MemoryFrontmatter["memoryKind"];
3348
3356
  expiresAt?: string;
3349
3357
  validAt?: string;
3358
+ // Issue #1578 — bi-temporal ingestion provenance. `observedAt` is the
3359
+ // ingestion time (when Remnic learned the fact); `eventTimeSource`
3360
+ // records whether `validAt` was resolved from an extracted expression
3361
+ // or assumed from the ingestion anchor. Both validate on serialize.
3362
+ observedAt?: string;
3363
+ eventTimeSource?: "extracted" | "assumed";
3350
3364
  structuredAttributes?: Record<string, string>;
3351
3365
  /**
3352
3366
  * When provided, this string is used as the source for the fact-content
@@ -3379,6 +3393,10 @@ export class StorageManager {
3379
3393
  const conf = options.confidence ?? 0.8;
3380
3394
  const tier = confidenceTier(conf);
3381
3395
  const validAt = normalizeMemoryWriteTimestamp("validAt", options.validAt);
3396
+ const observedAt = normalizeMemoryWriteTimestamp(
3397
+ "observedAt",
3398
+ options.observedAt,
3399
+ );
3382
3400
 
3383
3401
  // Auto-set TTL for speculative memories
3384
3402
  let expiresAt: string | undefined;
@@ -3412,6 +3430,8 @@ export class StorageManager {
3412
3430
  sourceTurnId: options.sourceTurnId,
3413
3431
  memoryKind: options.memoryKind,
3414
3432
  valid_at: validAt,
3433
+ observedAt,
3434
+ eventTimeSource: options.eventTimeSource,
3415
3435
  structuredAttributes: options.structuredAttributes,
3416
3436
  };
3417
3437
  if (options.status !== undefined) {
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Shared helpers for the `\n[Attributes: …]` enrichment suffix that
3
+ * `writeMemory` appends to a stored body when `structuredAttributes` are
4
+ * present.
5
+ *
6
+ * Extracted here (out of `storage.ts`) so every reader — the storage
7
+ * manager, the wearable service, and the coding surfaces — strips the suffix
8
+ * with ONE definition. Previously `coding/architecture-surfaces.ts` carried a
9
+ * weaker local copy that only checked the trailing `]`, which could truncate
10
+ * card markdown that legitimately ends in a `[Attributes: …]` line (cursor
11
+ * review: duplicate attribute suffix stripper).
12
+ *
13
+ * String operations, not regex — CodeQL `js/polynomial-redos` flags
14
+ * suffix-anchored patterns over library-supplied content.
15
+ */
16
+
17
+ /**
18
+ * Remove the `[Attributes: …]` suffix `writeMemory` appends to the stored
19
+ * body when structuredAttributes are present, yielding the raw fact/card text
20
+ * for content comparison or client display. Inverse companion of
21
+ * `normalizeAttributePairs` enrichment in `storage.ts`.
22
+ *
23
+ * Returns the stripped (trimmed) body when the marker opens the final
24
+ * non-empty line and its inner payload contains no premature `]` or newline
25
+ * (mirrors the exact shape `normalizeAttributePairs` emits). Otherwise
26
+ * returns the trimmed input unchanged — never truncates legitimate content.
27
+ */
28
+ export function stripAttributesSuffix(content: string): string {
29
+ const trimmed = content.trimEnd();
30
+ if (!trimmed.endsWith("]")) return content.trim();
31
+ const marker = "\n[Attributes: ";
32
+ const markerIndex = trimmed.lastIndexOf(marker);
33
+ if (markerIndex === -1) return content.trim();
34
+ // The block must be the FINAL line and contain no "]" before the
35
+ // closing bracket (mirrors the shape normalizeAttributePairs emits).
36
+ const inner = trimmed.slice(markerIndex + marker.length, -1);
37
+ if (inner.includes("]") || inner.includes("\n")) return content.trim();
38
+ return trimmed.slice(0, markerIndex).trim();
39
+ }
@@ -101,6 +101,40 @@ export function isValidAsOf(
101
101
  return invalidAtMs > asOfMs;
102
102
  }
103
103
 
104
+ /**
105
+ * Bi-temporal injection filter (issue #1578).
106
+ *
107
+ * Returns `true` when the fact's event-time interval has already ended before
108
+ * `nowMs` — i.e. it is no longer *true in the world* and should leave the
109
+ * default injection candidate set (it remains findable by explicit search
110
+ * and `as_of` queries, mirroring the lifecycle-filter escape-hatch principle
111
+ * in AGENTS.md §16). Gated by `temporalBiTemporal`; the escape hatch
112
+ * `temporalExpiredInInjection` re-admits expired facts when a deployment
113
+ * prefers the older always-inject behavior.
114
+ *
115
+ * Semantics: a fact is expired-now when `invalid_at` is present and
116
+ * `invalid_at <= nowMs` (the exclusive upper bound has passed). Absent
117
+ * `invalid_at` means "still valid" — never expired. This is the
118
+ * status-orthogonal complement of `isValidAsOf` for the wall-clock instant:
119
+ * an `active` memory can be validity-expired, and a `superseded` one can
120
+ * still be validity-valid within its historical window (issue pitfall: the
121
+ * filter is status-orthogonal).
122
+ */
123
+ export function isValidityExpiredNow(
124
+ fm: Pick<MemoryFrontmatter, "invalid_at">,
125
+ nowMs: number,
126
+ ): boolean {
127
+ if (!Number.isFinite(nowMs)) return false;
128
+ const invalidAt = fm.invalid_at?.trim();
129
+ if (!invalidAt || invalidAt.length === 0) return false;
130
+ const invalidAtMs = Date.parse(invalidAt);
131
+ // Unparseable invalid_at — conservatively keep the fact (do not filter on
132
+ // corrupt data; the as_of path already handles malformed bounds).
133
+ if (!Number.isFinite(invalidAtMs)) return false;
134
+ // Half-open interval end has passed: [valid_at, invalid_at) ended.
135
+ return invalidAtMs <= nowMs;
136
+ }
137
+
104
138
  /**
105
139
  * Parse and validate an `as_of` value supplied at an input boundary
106
140
  * (CLI flag, HTTP query param, MCP field). Returns parsed milliseconds
package/src/types.ts CHANGED
@@ -788,6 +788,25 @@ export interface PluginConfig {
788
788
  * filtered out.
789
789
  */
790
790
  temporalSupersessionIncludeInRecall: boolean;
791
+ /**
792
+ * Bi-temporal validity master gate (issue #1578). When `false` (default),
793
+ * the recall validity-injection filter and `asOf` semantics behave
794
+ * byte-identically to pre-#1578.
795
+ *
796
+ * Turn on to exclude validity-expired facts from default injection
797
+ * candidates (they remain findable by explicit search and `as_of`
798
+ * queries). Storage round-trips `observedAt`/`eventTimeSource`; the
799
+ * extraction prompt → `resolveEventTime` write-time wiring lands in a
800
+ * follow-on PR, so new extractions are not yet event-time-resolved.
801
+ */
802
+ temporalBiTemporal: boolean;
803
+ /**
804
+ * Escape hatch for the bi-temporal injection filter (issue #1578). When
805
+ * `true`, facts whose `[valid_at, invalid_at)` interval ended before now
806
+ * are kept in injection candidates even with `temporalBiTemporal` on — some
807
+ * deployments prefer the older (always-inject) behavior. Default `false`.
808
+ */
809
+ temporalExpiredInInjection: boolean;
791
810
  // Direct-answer retrieval tier (issue #518)
792
811
  /**
793
812
  * When true, recall checks whether a single validated memory in a
@@ -2423,6 +2442,25 @@ export interface MemoryFrontmatter {
2423
2442
  * point in time.
2424
2443
  */
2425
2444
  invalid_at?: string;
2445
+ /**
2446
+ * Ingestion time (issue #1578) — when Remnic *learned* this fact.
2447
+ * Distinct from `created` (file creation) and `valid_at` (event-time
2448
+ * start). Aligns with the source turn timestamp (#1575
2449
+ * `sources[].observedAt`). When `eventTimeSource === "assumed"`,
2450
+ * `valid_at` is copied from this value.
2451
+ */
2452
+ observedAt?: string;
2453
+ /**
2454
+ * Provenance of the event-time interval (issue #1578).
2455
+ * - `"extracted"` — a per-fact `eventTime` expression was resolved
2456
+ * against the source turn's timestamp and produced `valid_at` /
2457
+ * `invalid_at`.
2458
+ * - `"assumed"` — no resolvable expression; `valid_at` was copied from
2459
+ * `observedAt` (the ingestion anchor).
2460
+ * Absent on memories written before #1578; readers treat absent as
2461
+ * `"assumed"` for display purposes.
2462
+ */
2463
+ eventTimeSource?: "extracted" | "assumed";
2426
2464
  /**
2427
2465
  * Timestamp when the operator explicitly forgot this memory
2428
2466
  * (issue #686 PR 4/6). Set by `remnic forget <id>`. Memories with