@remnic/core 9.3.704 → 9.3.706

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 (332) hide show
  1. package/dist/access-boundary.d.ts +6 -6
  2. package/dist/access-boundary.js +29 -29
  3. package/dist/access-cli.js +59 -57
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +5 -5
  6. package/dist/access-http.js +34 -33
  7. package/dist/access-mcp.d.ts +5 -5
  8. package/dist/access-mcp.js +33 -32
  9. package/dist/access-operations-batch.d.ts +2 -0
  10. package/dist/access-operations-batch.js +120 -0
  11. package/dist/access-operations.d.ts +6 -5
  12. package/dist/access-operations.js +32 -31
  13. package/dist/access-schema.js +3 -3
  14. package/dist/{access-service-C8hc8N2i.d.ts → access-service-j1c1gptF.d.ts} +3 -3
  15. package/dist/access-service.d.ts +5 -5
  16. package/dist/access-service.js +28 -28
  17. package/dist/access-surface-catalog.d.ts +5 -5
  18. package/dist/access-surface-catalog.js +57 -54
  19. package/dist/access-surface-catalog.js.map +1 -1
  20. package/dist/action-confidence.d.ts +1 -1
  21. package/dist/active-memory-bridge.d.ts +1 -1
  22. package/dist/active-recall.d.ts +1 -1
  23. package/dist/{auto-sync-3AAP25FV.js → auto-sync-24NNS3GU.js} +3 -3
  24. package/dist/behavior-learner.d.ts +1 -1
  25. package/dist/behavior-signals.d.ts +1 -1
  26. package/dist/bootstrap.d.ts +4 -4
  27. package/dist/briefing.d.ts +1 -1
  28. package/dist/briefing.js +7 -7
  29. package/dist/buffer-surprise-report.d.ts +1 -1
  30. package/dist/buffer.d.ts +1 -1
  31. package/dist/calibration.d.ts +1 -1
  32. package/dist/capabilities.d.ts +1 -1
  33. package/dist/{capsule-crypto-GWVG7LGC.js → capsule-crypto-7FJQINUR.js} +2 -2
  34. package/dist/capsule-crypto-7FJQINUR.js.map +1 -0
  35. package/dist/{catalog-CAeU0e1C.d.ts → catalog-CKxilpzS.d.ts} +1 -1
  36. package/dist/causal-behavior.d.ts +1 -1
  37. package/dist/causal-behavior.js +5 -5
  38. package/dist/causal-chain.js +3 -3
  39. package/dist/causal-consolidation.d.ts +1 -1
  40. package/dist/causal-consolidation.js +11 -11
  41. package/dist/causal-retrieval.js +3 -3
  42. package/dist/causal-trajectory-graph.js +2 -2
  43. package/dist/causal-trajectory.js +1 -1
  44. package/dist/{chunk-CTY6QSLZ.js → chunk-24FGNOQS.js} +4 -4
  45. package/dist/{chunk-CTY6QSLZ.js.map → chunk-24FGNOQS.js.map} +1 -1
  46. package/dist/{chunk-OWHERGF2.js → chunk-2NLLXCJG.js} +2 -2
  47. package/dist/{chunk-NMW3T6T6.js → chunk-2VQYHHWB.js} +17 -11
  48. package/dist/chunk-2VQYHHWB.js.map +1 -0
  49. package/dist/{chunk-7G4KPGR3.js → chunk-3PTOZJOI.js} +2 -2
  50. package/dist/{chunk-PJ5MDXOF.js → chunk-4DZATVK5.js} +10 -10
  51. package/dist/{chunk-UOIRJ45Z.js → chunk-4E2QCH46.js} +2 -2
  52. package/dist/{chunk-KKK7YTYN.js → chunk-5GSUBCZM.js} +6 -2
  53. package/dist/chunk-5GSUBCZM.js.map +1 -0
  54. package/dist/{chunk-RYDT7LAI.js → chunk-66TSLESZ.js} +2 -2
  55. package/dist/chunk-66TSLESZ.js.map +1 -0
  56. package/dist/{chunk-IREVVFTM.js → chunk-6CIAGZ6X.js} +9 -1
  57. package/dist/chunk-6CIAGZ6X.js.map +1 -0
  58. package/dist/{chunk-QHNJVWF6.js → chunk-6HPJMR5I.js} +6 -6
  59. package/dist/{chunk-4NFVPDIL.js → chunk-74MYJSKB.js} +5 -5
  60. package/dist/{chunk-PKMQK2WY.js → chunk-773DPI6T.js} +4 -4
  61. package/dist/{chunk-AGNBY3VG.js → chunk-APJQ6UEA.js} +4 -4
  62. package/dist/{chunk-UNNDFYUX.js → chunk-ARV3AUOM.js} +5 -5
  63. package/dist/{chunk-XXDOJ5NL.js → chunk-BGAHTI4C.js} +7 -7
  64. package/dist/{chunk-6GHWF4QH.js → chunk-C63MK3WL.js} +2 -2
  65. package/dist/{chunk-VKJHM6PL.js → chunk-DGTDRIUO.js} +5 -5
  66. package/dist/{chunk-25FHSOEB.js → chunk-EJDAXR7O.js} +60 -6
  67. package/dist/chunk-EJDAXR7O.js.map +1 -0
  68. package/dist/{chunk-K7QRDDSR.js → chunk-ESMY4RJ4.js} +5 -5
  69. package/dist/{chunk-YLB7NMLD.js → chunk-GKI6LX5L.js} +2 -2
  70. package/dist/{chunk-TUPNAHHQ.js → chunk-HIV5E57C.js} +2 -2
  71. package/dist/{chunk-YOI3ELXF.js → chunk-IF362TF6.js} +2 -2
  72. package/dist/{chunk-SFOAQQDJ.js → chunk-IONFO7UK.js} +3 -3
  73. package/dist/{chunk-AE3C4PY6.js → chunk-IVCQW4C4.js} +2 -2
  74. package/dist/{chunk-6AFVIOVZ.js → chunk-K22IJO4R.js} +2 -2
  75. package/dist/{chunk-5W7CTZ2E.js → chunk-KQAFEZQX.js} +4 -4
  76. package/dist/chunk-M7R5R6OJ.js +355 -0
  77. package/dist/chunk-M7R5R6OJ.js.map +1 -0
  78. package/dist/{chunk-IX3UQT4H.js → chunk-MLLTU5FX.js} +1 -1
  79. package/dist/chunk-MLLTU5FX.js.map +1 -0
  80. package/dist/{chunk-ORXVO6TH.js → chunk-MRX6ZXHZ.js} +6 -6
  81. package/dist/chunk-N75N5SNX.js +1303 -0
  82. package/dist/chunk-N75N5SNX.js.map +1 -0
  83. package/dist/{chunk-S24J5LLQ.js → chunk-NM6TSEGQ.js} +3 -3
  84. package/dist/{chunk-QDIVMS25.js → chunk-NUWZFSKC.js} +2 -2
  85. package/dist/{chunk-53EEQYE5.js → chunk-OMSTU233.js} +4 -4
  86. package/dist/{chunk-R64MNFPV.js → chunk-PDJQVAGM.js} +114 -77
  87. package/dist/chunk-PDJQVAGM.js.map +1 -0
  88. package/dist/{chunk-WAHI7SPK.js → chunk-RH2OSRQY.js} +2 -2
  89. package/dist/{chunk-YTMDF6S7.js → chunk-SPETAWFE.js} +2 -2
  90. package/dist/{chunk-ZXFNSKPY.js → chunk-STYMKPFT.js} +34 -34
  91. package/dist/{chunk-OMNTQPZO.js → chunk-TKZ3AL5V.js} +2 -2
  92. package/dist/{chunk-ABCGDW3A.js → chunk-VYZNWH4J.js} +40 -9
  93. package/dist/chunk-VYZNWH4J.js.map +1 -0
  94. package/dist/{chunk-QV5WFNZ2.js → chunk-W2S3Z5MT.js} +2 -2
  95. package/dist/{chunk-OCFNG4LB.js → chunk-W2WQ4LE7.js} +2 -2
  96. package/dist/{chunk-DE2OPIQD.js → chunk-WDH3KUZU.js} +6 -6
  97. package/dist/{chunk-3EHCV6YV.js → chunk-WQADZ3ZY.js} +67 -67
  98. package/dist/{chunk-OOEVMJK4.js → chunk-WSWYIKXX.js} +6 -6
  99. package/dist/{chunk-UVYI6VIX.js → chunk-X7Y7WX73.js} +1 -1
  100. package/dist/{chunk-WN4GHSDH.js → chunk-XKMDDM7P.js} +2 -2
  101. package/dist/{chunk-6Q7JFY4P.js → chunk-Y6YHAGQP.js} +2 -2
  102. package/dist/{chunk-KF74X62T.js → chunk-YGKUAX2B.js} +4 -4
  103. package/dist/{chunk-YN4ZT4CW.js → chunk-YI7RZO7Y.js} +1 -1
  104. package/dist/{cli-ChE0XlJY.d.ts → cli-CbT-pyM4.d.ts} +3 -3
  105. package/dist/cli.d.ts +6 -6
  106. package/dist/cli.js +58 -57
  107. package/dist/compounding/engine.d.ts +1 -1
  108. package/dist/compounding/engine.js +7 -7
  109. package/dist/compounding/preference-consolidator.d.ts +1 -1
  110. package/dist/compression-optimizer.d.ts +1 -1
  111. package/dist/config.d.ts +1 -1
  112. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  113. package/dist/connectors/codex-materialize-runner.js +7 -7
  114. package/dist/connectors/codex-materialize.d.ts +1 -1
  115. package/dist/connectors/index.d.ts +1 -1
  116. package/dist/connectors/index.js +7 -7
  117. package/dist/consolidation-provenance-check.d.ts +1 -1
  118. package/dist/consolidation-undo.d.ts +1 -1
  119. package/dist/contradiction/index.d.ts +1 -1
  120. package/dist/conversation-index/backend.d.ts +1 -1
  121. package/dist/conversation-index/chunker.d.ts +1 -1
  122. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  123. package/dist/conversation-index/indexer.d.ts +1 -1
  124. package/dist/conversation-index/search.d.ts +1 -1
  125. package/dist/day-summary.d.ts +1 -1
  126. package/dist/delinearize.d.ts +1 -1
  127. package/dist/direct-answer-wiring.d.ts +1 -1
  128. package/dist/direct-answer.d.ts +1 -1
  129. package/dist/embedding-fallback.d.ts +1 -1
  130. package/dist/enrichment/index.d.ts +1 -1
  131. package/dist/entity-retrieval.d.ts +1 -1
  132. package/dist/entity-retrieval.js +7 -7
  133. package/dist/entity-schema.d.ts +1 -1
  134. package/dist/event-time.d.ts +35 -1
  135. package/dist/event-time.js +6 -320
  136. package/dist/event-time.js.map +1 -1
  137. package/dist/explicit-capture.d.ts +4 -4
  138. package/dist/extraction-faithfulness.d.ts +1 -1
  139. package/dist/extraction-judge-telemetry.d.ts +1 -1
  140. package/dist/extraction-judge-training.d.ts +1 -1
  141. package/dist/extraction-judge.d.ts +1 -1
  142. package/dist/extraction.d.ts +10 -1
  143. package/dist/extraction.js +3 -3
  144. package/dist/fallback-llm.d.ts +1 -1
  145. package/dist/{forget-6SOIPUMQ.js → forget-U66KPDPY.js} +3 -3
  146. package/dist/{graph-edge-decay-D7OESCBR.js → graph-edge-decay-JLOF3YW4.js} +3 -3
  147. package/dist/graph-snapshot.js +3 -3
  148. package/dist/graph.js +2 -2
  149. package/dist/identity-continuity.d.ts +1 -1
  150. package/dist/importance.d.ts +1 -1
  151. package/dist/index.d.ts +9 -9
  152. package/dist/index.js +103 -101
  153. package/dist/index.js.map +1 -1
  154. package/dist/intent.d.ts +1 -1
  155. package/dist/lcm/engine.d.ts +1 -1
  156. package/dist/lcm/engine.js +2 -2
  157. package/dist/lcm/index.d.ts +1 -1
  158. package/dist/lcm/index.js +4 -4
  159. package/dist/lcm/tools.d.ts +1 -1
  160. package/dist/lifecycle.d.ts +1 -1
  161. package/dist/live-connectors-runner.d.ts +1 -1
  162. package/dist/local-llm.d.ts +1 -1
  163. package/dist/maintenance/memory-governance.d.ts +1 -1
  164. package/dist/maintenance/memory-governance.js +8 -8
  165. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +7 -7
  166. package/dist/maintenance/rebuild-memory-projection.js +9 -9
  167. package/dist/mcp-memory-inspector-app.d.ts +5 -5
  168. package/dist/memory-action-policy.d.ts +1 -1
  169. package/dist/memory-cache.d.ts +1 -1
  170. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  171. package/dist/memory-projection-store.d.ts +1 -1
  172. package/dist/memory-provenance.d.ts +1 -1
  173. package/dist/memory-worth-outcomes.d.ts +1 -1
  174. package/dist/models-json.d.ts +1 -1
  175. package/dist/namespaces/migrate.d.ts +2 -2
  176. package/dist/namespaces/migrate.js +11 -11
  177. package/dist/namespaces/principal.d.ts +1 -1
  178. package/dist/namespaces/search.d.ts +1 -1
  179. package/dist/namespaces/search.js +3 -3
  180. package/dist/namespaces/storage.d.ts +2 -2
  181. package/dist/namespaces/storage.js +7 -7
  182. package/dist/native-knowledge.d.ts +1 -1
  183. package/dist/operator-toolkit.d.ts +1 -1
  184. package/dist/operator-toolkit.js +18 -18
  185. package/dist/orchestration/maintenance.d.ts +2 -2
  186. package/dist/orchestration/maintenance.js +9 -9
  187. package/dist/{orchestrator-m_PnhyHj.d.ts → orchestrator-D4ovYV3x.d.ts} +3 -3
  188. package/dist/orchestrator.d.ts +4 -4
  189. package/dist/orchestrator.js +46 -45
  190. package/dist/patterns-cli.d.ts +1 -1
  191. package/dist/policy-runtime.d.ts +1 -1
  192. package/dist/provenance.d.ts +1 -1
  193. package/dist/qmd-recall-cache.d.ts +1 -1
  194. package/dist/qmd.d.ts +1 -1
  195. package/dist/recall-disclosure-escalation.d.ts +1 -1
  196. package/dist/recall-explain-renderer.d.ts +1 -1
  197. package/dist/recall-explain-renderer.js +3 -3
  198. package/dist/recall-planner-llm.d.ts +1 -1
  199. package/dist/recall-state.d.ts +1 -1
  200. package/dist/recall-tag-filter.d.ts +1 -1
  201. package/dist/recall-xray-cli.d.ts +1 -1
  202. package/dist/recall-xray-cli.js +4 -4
  203. package/dist/recall-xray-renderer.d.ts +1 -1
  204. package/dist/recall-xray-renderer.js +3 -3
  205. package/dist/recall-xray.d.ts +1 -1
  206. package/dist/recall-xray.js +2 -2
  207. package/dist/resolve-auth-token.d.ts +1 -1
  208. package/dist/resume-bundles.js +1 -1
  209. package/dist/retrieval-agents.d.ts +1 -1
  210. package/dist/retrieval-tiers.d.ts +1 -1
  211. package/dist/routing/engine.d.ts +1 -1
  212. package/dist/routing/store.d.ts +1 -1
  213. package/dist/schemas.d.ts +38 -0
  214. package/dist/schemas.js +1 -1
  215. package/dist/search/embed-helper.d.ts +1 -1
  216. package/dist/search/factory.d.ts +1 -1
  217. package/dist/search/factory.js +2 -2
  218. package/dist/search/index.d.ts +1 -1
  219. package/dist/search/index.js +4 -4
  220. package/dist/search/lancedb-backend.d.ts +1 -1
  221. package/dist/search/meilisearch-backend.d.ts +1 -1
  222. package/dist/search/noop-backend.d.ts +1 -1
  223. package/dist/search/orama-backend.d.ts +1 -1
  224. package/dist/search/port.d.ts +1 -1
  225. package/dist/search/remote-backend.d.ts +1 -1
  226. package/dist/{semantic-consolidation-DVBbq6Wr.d.ts → semantic-consolidation-DyMUCsfN.d.ts} +1 -1
  227. package/dist/semantic-consolidation.d.ts +2 -2
  228. package/dist/semantic-consolidation.js +9 -9
  229. package/dist/semantic-rule-promotion.js +7 -7
  230. package/dist/semantic-rule-verifier.d.ts +1 -1
  231. package/dist/semantic-rule-verifier.js +8 -8
  232. package/dist/session-observer-bands.d.ts +1 -1
  233. package/dist/session-observer-state.d.ts +1 -1
  234. package/dist/shared-context/manager.d.ts +1 -1
  235. package/dist/signal.d.ts +1 -1
  236. package/dist/storage.d.ts +5 -1
  237. package/dist/storage.js +6 -6
  238. package/dist/summarizer.d.ts +1 -1
  239. package/dist/summary-snapshot.d.ts +1 -1
  240. package/dist/temporal-supersession.d.ts +1 -1
  241. package/dist/temporal-supersession.js +2 -2
  242. package/dist/temporal-validity.d.ts +2 -2
  243. package/dist/temporal-validity.js +1 -1
  244. package/dist/threading.d.ts +1 -1
  245. package/dist/tier-migration.d.ts +1 -1
  246. package/dist/tier-routing.d.ts +1 -1
  247. package/dist/topics.d.ts +1 -1
  248. package/dist/transcript.d.ts +1 -1
  249. package/dist/transfer/backup.js +2 -2
  250. package/dist/transfer/capsule-export.js +2 -2
  251. package/dist/transfer/capsule-import.js +2 -2
  252. package/dist/transfer/import-sqlite.js +2 -2
  253. package/dist/{types-CJrvHJjx.d.ts → types-Couvz-L3.d.ts} +12 -2
  254. package/dist/types.d.ts +1 -1
  255. package/dist/types.js +1 -1
  256. package/dist/utility-runtime.d.ts +1 -1
  257. package/dist/verified-recall.js +8 -8
  258. package/package.json +2 -2
  259. package/src/access-boundary.ts +102 -1
  260. package/src/access-http.ts +54 -0
  261. package/src/access-operations-batch.ts +1468 -0
  262. package/src/access-operations.ts +4 -0
  263. package/src/access-surface-catalog.test.ts +17 -1
  264. package/src/access-surface-catalog.ts +57 -54
  265. package/src/event-time.ts +73 -0
  266. package/src/extraction-eventtime-wiring.test.ts +269 -0
  267. package/src/extraction.ts +50 -3
  268. package/src/orchestrator.ts +71 -17
  269. package/src/schemas.ts +12 -0
  270. package/src/storage-contract/atomicity.test.ts +113 -0
  271. package/src/storage-contract/cache-coherence.test.ts +100 -0
  272. package/src/storage-contract/content-hash.test.ts +102 -0
  273. package/src/storage-contract/failure-semantics.test.ts +108 -0
  274. package/src/storage-contract/harness.ts +148 -0
  275. package/src/storage-contract/namespace-routing.test.ts +135 -0
  276. package/src/storage-contract/round-trip.test.ts +226 -0
  277. package/src/storage-contract/surface.test.ts +90 -0
  278. package/src/storage-contract/versioning.test.ts +139 -0
  279. package/src/storage.ts +13 -2
  280. package/src/temporal-validity.test.ts +33 -0
  281. package/src/temporal-validity.ts +24 -1
  282. package/src/types.ts +12 -2
  283. package/dist/chunk-25FHSOEB.js.map +0 -1
  284. package/dist/chunk-ABCGDW3A.js.map +0 -1
  285. package/dist/chunk-IREVVFTM.js.map +0 -1
  286. package/dist/chunk-IX3UQT4H.js.map +0 -1
  287. package/dist/chunk-KKK7YTYN.js.map +0 -1
  288. package/dist/chunk-NMW3T6T6.js.map +0 -1
  289. package/dist/chunk-R64MNFPV.js.map +0 -1
  290. package/dist/chunk-RYDT7LAI.js.map +0 -1
  291. /package/dist/{capsule-crypto-GWVG7LGC.js.map → access-operations-batch.js.map} +0 -0
  292. /package/dist/{auto-sync-3AAP25FV.js.map → auto-sync-24NNS3GU.js.map} +0 -0
  293. /package/dist/{chunk-OWHERGF2.js.map → chunk-2NLLXCJG.js.map} +0 -0
  294. /package/dist/{chunk-7G4KPGR3.js.map → chunk-3PTOZJOI.js.map} +0 -0
  295. /package/dist/{chunk-PJ5MDXOF.js.map → chunk-4DZATVK5.js.map} +0 -0
  296. /package/dist/{chunk-UOIRJ45Z.js.map → chunk-4E2QCH46.js.map} +0 -0
  297. /package/dist/{chunk-QHNJVWF6.js.map → chunk-6HPJMR5I.js.map} +0 -0
  298. /package/dist/{chunk-4NFVPDIL.js.map → chunk-74MYJSKB.js.map} +0 -0
  299. /package/dist/{chunk-PKMQK2WY.js.map → chunk-773DPI6T.js.map} +0 -0
  300. /package/dist/{chunk-AGNBY3VG.js.map → chunk-APJQ6UEA.js.map} +0 -0
  301. /package/dist/{chunk-UNNDFYUX.js.map → chunk-ARV3AUOM.js.map} +0 -0
  302. /package/dist/{chunk-XXDOJ5NL.js.map → chunk-BGAHTI4C.js.map} +0 -0
  303. /package/dist/{chunk-6GHWF4QH.js.map → chunk-C63MK3WL.js.map} +0 -0
  304. /package/dist/{chunk-VKJHM6PL.js.map → chunk-DGTDRIUO.js.map} +0 -0
  305. /package/dist/{chunk-K7QRDDSR.js.map → chunk-ESMY4RJ4.js.map} +0 -0
  306. /package/dist/{chunk-YLB7NMLD.js.map → chunk-GKI6LX5L.js.map} +0 -0
  307. /package/dist/{chunk-TUPNAHHQ.js.map → chunk-HIV5E57C.js.map} +0 -0
  308. /package/dist/{chunk-YOI3ELXF.js.map → chunk-IF362TF6.js.map} +0 -0
  309. /package/dist/{chunk-SFOAQQDJ.js.map → chunk-IONFO7UK.js.map} +0 -0
  310. /package/dist/{chunk-AE3C4PY6.js.map → chunk-IVCQW4C4.js.map} +0 -0
  311. /package/dist/{chunk-6AFVIOVZ.js.map → chunk-K22IJO4R.js.map} +0 -0
  312. /package/dist/{chunk-5W7CTZ2E.js.map → chunk-KQAFEZQX.js.map} +0 -0
  313. /package/dist/{chunk-ORXVO6TH.js.map → chunk-MRX6ZXHZ.js.map} +0 -0
  314. /package/dist/{chunk-S24J5LLQ.js.map → chunk-NM6TSEGQ.js.map} +0 -0
  315. /package/dist/{chunk-QDIVMS25.js.map → chunk-NUWZFSKC.js.map} +0 -0
  316. /package/dist/{chunk-53EEQYE5.js.map → chunk-OMSTU233.js.map} +0 -0
  317. /package/dist/{chunk-WAHI7SPK.js.map → chunk-RH2OSRQY.js.map} +0 -0
  318. /package/dist/{chunk-YTMDF6S7.js.map → chunk-SPETAWFE.js.map} +0 -0
  319. /package/dist/{chunk-ZXFNSKPY.js.map → chunk-STYMKPFT.js.map} +0 -0
  320. /package/dist/{chunk-OMNTQPZO.js.map → chunk-TKZ3AL5V.js.map} +0 -0
  321. /package/dist/{chunk-QV5WFNZ2.js.map → chunk-W2S3Z5MT.js.map} +0 -0
  322. /package/dist/{chunk-OCFNG4LB.js.map → chunk-W2WQ4LE7.js.map} +0 -0
  323. /package/dist/{chunk-DE2OPIQD.js.map → chunk-WDH3KUZU.js.map} +0 -0
  324. /package/dist/{chunk-3EHCV6YV.js.map → chunk-WQADZ3ZY.js.map} +0 -0
  325. /package/dist/{chunk-OOEVMJK4.js.map → chunk-WSWYIKXX.js.map} +0 -0
  326. /package/dist/{chunk-UVYI6VIX.js.map → chunk-X7Y7WX73.js.map} +0 -0
  327. /package/dist/{chunk-WN4GHSDH.js.map → chunk-XKMDDM7P.js.map} +0 -0
  328. /package/dist/{chunk-6Q7JFY4P.js.map → chunk-Y6YHAGQP.js.map} +0 -0
  329. /package/dist/{chunk-KF74X62T.js.map → chunk-YGKUAX2B.js.map} +0 -0
  330. /package/dist/{chunk-YN4ZT4CW.js.map → chunk-YI7RZO7Y.js.map} +0 -0
  331. /package/dist/{forget-6SOIPUMQ.js.map → forget-U66KPDPY.js.map} +0 -0
  332. /package/dist/{graph-edge-decay-D7OESCBR.js.map → graph-edge-decay-JLOF3YW4.js.map} +0 -0
@@ -121,6 +121,7 @@ import {
121
121
  shouldFilterSupersededFromRecall,
122
122
  } from "./temporal-supersession.js";
123
123
  import { isValidAsOf, isValidityExpiredNow } from "./temporal-validity.js";
124
+ import { resolveFactEventTime } from "./event-time.js";
124
125
  import { RelevanceStore } from "./relevance.js";
125
126
  import { NegativeExampleStore } from "./negative.js";
126
127
  import {
@@ -12150,10 +12151,16 @@ export class Orchestrator {
12150
12151
  : typeof sessionKey === "string" && sessionKey.length > 0
12151
12152
  ? sessionKey
12152
12153
  : "default";
12154
+ const captureTimestamp = new Date().toISOString();
12153
12155
  const turn: BufferTurn = {
12154
12156
  role,
12155
12157
  content,
12156
- timestamp: new Date().toISOString(),
12158
+ timestamp: captureTimestamp,
12159
+ // #1578: anchor live-capture turns to wall-clock when bi-temporal is on;
12160
+ // replay/import turns carry sourceValidAt explicitly (codex P1).
12161
+ ...(this.config.temporalBiTemporal
12162
+ ? { sourceValidAt: captureTimestamp }
12163
+ : {}),
12157
12164
  sessionKey,
12158
12165
  logicalSessionKey: options.logicalSessionKey ?? bufferKey,
12159
12166
  providerThreadId: options.providerThreadId ?? null,
@@ -13922,6 +13929,11 @@ export class Orchestrator {
13922
13929
  intentEntityTypes?: string[];
13923
13930
  memoryKind?: MemoryFrontmatter["memoryKind"];
13924
13931
  validAt?: string;
13932
+ // #1578 — bi-temporal bounds + ingestion provenance forwarded to profile-
13933
+ // target copies (same defect class as shared promotion; cursor bugbot).
13934
+ invalidAt?: string;
13935
+ observedAt?: string;
13936
+ eventTimeSource?: "extracted" | "assumed";
13925
13937
  source: string;
13926
13938
  sources?: ProvenanceSource[];
13927
13939
  provenance?: "verified" | "unverified" | "none";
@@ -13980,6 +13992,10 @@ export class Orchestrator {
13980
13992
  intentEntityTypes: options.intentEntityTypes,
13981
13993
  memoryKind: options.memoryKind,
13982
13994
  validAt: options.validAt,
13995
+ // #1578 — forward bi-temporal bounds + ingestion provenance.
13996
+ ...(options.invalidAt ? { invalidAt: options.invalidAt } : {}),
13997
+ ...(options.observedAt ? { observedAt: options.observedAt } : {}),
13998
+ ...(options.eventTimeSource ? { eventTimeSource: options.eventTimeSource } : {}),
13983
13999
  contentHashSource: options.category === "fact" ? dedupContent : rawContent,
13984
14000
  ...(options.sources && options.sources.length > 0 ? { sources: options.sources } : {}),
13985
14001
  ...(options.provenance ? { provenance: options.provenance } : {}),
@@ -13999,7 +14015,7 @@ export class Orchestrator {
13999
14015
  entityRef: options.entityRef,
14000
14016
  structuredAttributes: options.structuredAttributes,
14001
14017
  createdAt: supersessionOrderingAt(options.validAt),
14002
- enabled: true,
14018
+ enabled: !(options.eventTimeSource === "extracted" && !options.validAt),
14003
14019
  });
14004
14020
  } catch (profileSupersessionErr) {
14005
14021
  log.warn(
@@ -14042,6 +14058,12 @@ export class Orchestrator {
14042
14058
  intentEntityTypes?: string[];
14043
14059
  memoryKind?: MemoryFrontmatter["memoryKind"];
14044
14060
  validAt?: string;
14061
+ // #1578 — bi-temporal bounds + ingestion provenance forwarded to the
14062
+ // shared-namespace copy so shared recall honours the same invalid_at
14063
+ // window as the source fact (cursor bugbot).
14064
+ invalidAt?: string;
14065
+ observedAt?: string;
14066
+ eventTimeSource?: "extracted" | "assumed";
14045
14067
  source: string;
14046
14068
  /** Claim-level provenance spans (issue #1575 PR 2). */
14047
14069
  sources?: ProvenanceSource[];
@@ -14196,7 +14218,7 @@ export class Orchestrator {
14196
14218
  entityRef: options.entityRef,
14197
14219
  structuredAttributes: options.structuredAttributes,
14198
14220
  createdAt: supersessionOrderingAt(options.validAt),
14199
- enabled: true,
14221
+ enabled: !(options.eventTimeSource === "extracted" && !options.validAt),
14200
14222
  useCallerTimestamp: true,
14201
14223
  });
14202
14224
  // Catalog touch (issue #1499 — codex P2 NElSf): this dedup branch
@@ -14271,6 +14293,10 @@ export class Orchestrator {
14271
14293
  intentEntityTypes: options.intentEntityTypes,
14272
14294
  memoryKind: options.memoryKind,
14273
14295
  validAt: options.validAt,
14296
+ // #1578 — forward bi-temporal bounds + ingestion provenance.
14297
+ ...(options.invalidAt ? { invalidAt: options.invalidAt } : {}),
14298
+ ...(options.observedAt ? { observedAt: options.observedAt } : {}),
14299
+ ...(options.eventTimeSource ? { eventTimeSource: options.eventTimeSource } : {}),
14274
14300
  contentHashSource: options.category === "fact" ? dedupContent : rawContent,
14275
14301
  // Claim-level provenance spans (issue #1575 PR 2).
14276
14302
  ...(options.sources && options.sources.length > 0 ? { sources: options.sources } : {}),
@@ -14296,7 +14322,7 @@ export class Orchestrator {
14296
14322
  entityRef: options.entityRef,
14297
14323
  structuredAttributes: options.structuredAttributes,
14298
14324
  createdAt: supersessionOrderingAt(options.validAt),
14299
- enabled: true,
14325
+ enabled: !(options.eventTimeSource === "extracted" && !options.validAt),
14300
14326
  });
14301
14327
  } catch (sharedSupersessionErr) {
14302
14328
  log.warn(
@@ -14677,7 +14703,7 @@ export class Orchestrator {
14677
14703
  typeof (fact as any).confidence === "number"
14678
14704
  ? (fact as any).confidence
14679
14705
  : 0.7;
14680
-
14706
+ const biTemporal = this.config.temporalBiTemporal && sourceContext?.validAt ? resolveFactEventTime(fact.eventTime, sourceContext.validAt) : undefined;
14681
14707
  // Content-hash dedup check (v6.0)
14682
14708
  //
14683
14709
  // Canonicalize pre-tagged facts before hashing (Codex P2 — issue #369).
@@ -15159,7 +15185,6 @@ export class Orchestrator {
15159
15185
  // back, leaving a dangling deindex with no replacement reference.
15160
15186
  // Child chunks intentionally do NOT carry supersedes; only the
15161
15187
  // parent represents the logical memory unit.
15162
- //
15163
15188
  // Canonicalize contentHashSource before writing (Thread 3 — Codex P2,
15164
15189
  // issue #369). If fact.content already carries an inline citation
15165
15190
  // (e.g. re-processed or relayed fact), strip it so contentHashSource
@@ -15187,7 +15212,8 @@ export class Orchestrator {
15187
15212
  intentEntityTypes: inferredIntent?.entityTypes,
15188
15213
  memoryKind,
15189
15214
  structuredAttributes: fact.structuredAttributes,
15190
- validAt: sourceContext?.validAt,
15215
+ validAt: biTemporal ? biTemporal.validFrom : sourceContext?.validAt,
15216
+ ...(biTemporal ? { observedAt: biTemporal.observedAt, eventTimeSource: biTemporal.eventTimeSource, ...(biTemporal.validUntil ? { invalidAt: biTemporal.validUntil } : {}) } : {}),
15191
15217
  contentHashSource: rawChunkedContent,
15192
15218
  // Faithfulness gate (issue #1576).
15193
15219
  ...(faithfulnessFm ? { faithfulness: faithfulnessFm } : {}),
@@ -15229,7 +15255,17 @@ export class Orchestrator {
15229
15255
  intentActionType: inferredIntent?.actionType,
15230
15256
  intentEntityTypes: inferredIntent?.entityTypes,
15231
15257
  memoryKind,
15232
- validAt: sourceContext?.validAt,
15258
+ validAt: biTemporal ? biTemporal.validFrom : sourceContext?.validAt,
15259
+ // #1578: propagate end bound + provenance to chunks (cursor bugbot).
15260
+ ...(biTemporal
15261
+ ? {
15262
+ observedAt: biTemporal.observedAt,
15263
+ eventTimeSource: biTemporal.eventTimeSource,
15264
+ ...(biTemporal.validUntil
15265
+ ? { invalidAt: biTemporal.validUntil }
15266
+ : {}),
15267
+ }
15268
+ : {}),
15233
15269
  // Faithfulness gate (issue #1576): propagate the parent
15234
15270
  // fact's verdict + enforce status so a pending_review fact
15235
15271
  // is not indexed as active through its chunks (chatgpt P2).
@@ -15287,8 +15323,12 @@ export class Orchestrator {
15287
15323
  newMemoryId: parentId,
15288
15324
  entityRef: supersessionEntityRef,
15289
15325
  structuredAttributes: fact.structuredAttributes,
15290
- createdAt: supersessionOrderingAt(sourceContext?.validAt),
15291
- enabled: this.config.temporalSupersessionEnabled,
15326
+ createdAt: supersessionOrderingAt(biTemporal?.validFrom ?? sourceContext?.validAt),
15327
+ // #1578 r3: an extracted end-only bound (validFrom absent) is
15328
+ // historical, not a new authoritative state — never let it
15329
+ // supersede a later active fact (codex P1 on :15534).
15330
+ enabled: this.config.temporalSupersessionEnabled &&
15331
+ !(biTemporal && !biTemporal.validFrom),
15292
15332
  });
15293
15333
  } catch (err) {
15294
15334
  log.warn(`temporal-supersession (chunked): unexpected error: ${err}`);
@@ -15311,7 +15351,14 @@ export class Orchestrator {
15311
15351
  intentActionType: inferredIntent?.actionType,
15312
15352
  intentEntityTypes: inferredIntent?.entityTypes,
15313
15353
  memoryKind,
15314
- validAt: sourceContext?.validAt,
15354
+ validAt: biTemporal ? biTemporal.validFrom : sourceContext?.validAt,
15355
+ ...(biTemporal
15356
+ ? {
15357
+ observedAt: biTemporal.observedAt,
15358
+ eventTimeSource: biTemporal.eventTimeSource,
15359
+ ...(biTemporal.validUntil ? { invalidAt: biTemporal.validUntil } : {}),
15360
+ }
15361
+ : {}),
15315
15362
  source: extractionWriteSource,
15316
15363
  ...(fact.sources && fact.sources.length > 0 ? { sources: fact.sources } : {}),
15317
15364
  ...(fact.provenance ? { provenance: fact.provenance } : {}),
@@ -15444,13 +15491,11 @@ export class Orchestrator {
15444
15491
  : undefined;
15445
15492
 
15446
15493
  // Normal write (no chunking)
15447
- //
15448
15494
  // Compute the cited content once so that writeMemory and writeArtifact
15449
15495
  // (when verbatim artifacts are enabled) share the same citation timestamp.
15450
15496
  // Calling applyInlineCitation twice on the same raw content would produce
15451
15497
  // two different timestamps, creating duplicate citations with divergent
15452
15498
  // provenance metadata on the memory and artifact copies of the same fact.
15453
- //
15454
15499
  // Pass the RAW (pre-citation) fact as `contentHashSource` so the
15455
15500
  // fact-content hash index records the hash of the canonical fact text
15456
15501
  // rather than the citation-annotated variant. When inline attribution is
@@ -15483,7 +15528,8 @@ export class Orchestrator {
15483
15528
  intentEntityTypes: inferredIntent?.entityTypes,
15484
15529
  memoryKind,
15485
15530
  structuredAttributes: fact.structuredAttributes,
15486
- validAt: sourceContext?.validAt,
15531
+ validAt: biTemporal ? biTemporal.validFrom : sourceContext?.validAt,
15532
+ ...(biTemporal ? { observedAt: biTemporal.observedAt, eventTimeSource: biTemporal.eventTimeSource, ...(biTemporal.validUntil ? { invalidAt: biTemporal.validUntil } : {}) } : {}),
15487
15533
  contentHashSource: writeCategory === "fact" ? fact.content : undefined,
15488
15534
  // Faithfulness gate (issue #1576).
15489
15535
  ...(faithfulnessFm ? { faithfulness: faithfulnessFm } : {}),
@@ -15516,8 +15562,9 @@ export class Orchestrator {
15516
15562
  newMemoryId: memoryId,
15517
15563
  entityRef: supersessionEntityRef,
15518
15564
  structuredAttributes: fact.structuredAttributes,
15519
- createdAt: supersessionOrderingAt(sourceContext?.validAt),
15520
- enabled: this.config.temporalSupersessionEnabled,
15565
+ createdAt: supersessionOrderingAt(biTemporal?.validFrom ?? sourceContext?.validAt),
15566
+ enabled: this.config.temporalSupersessionEnabled &&
15567
+ !(biTemporal && !biTemporal.validFrom),
15521
15568
  });
15522
15569
  } catch (err) {
15523
15570
  log.warn(`temporal-supersession: unexpected error: ${err}`);
@@ -15564,7 +15611,14 @@ export class Orchestrator {
15564
15611
  intentActionType: inferredIntent?.actionType,
15565
15612
  intentEntityTypes: inferredIntent?.entityTypes,
15566
15613
  memoryKind,
15567
- validAt: sourceContext?.validAt,
15614
+ validAt: biTemporal ? biTemporal.validFrom : sourceContext?.validAt,
15615
+ ...(biTemporal
15616
+ ? {
15617
+ observedAt: biTemporal.observedAt,
15618
+ eventTimeSource: biTemporal.eventTimeSource,
15619
+ ...(biTemporal.validUntil ? { invalidAt: biTemporal.validUntil } : {}),
15620
+ }
15621
+ : {}),
15568
15622
  source: extractionWriteSource,
15569
15623
  ...(fact.sources && fact.sources.length > 0 ? { sources: fact.sources } : {}),
15570
15624
  ...(fact.provenance ? { provenance: fact.provenance } : {}),
package/src/schemas.ts CHANGED
@@ -166,6 +166,18 @@ export const ExtractedFactSchema = z.object({
166
166
  .describe(
167
167
  'For category "reasoning_trace" only: a stored solution chain with ordered steps, a final answer, and an optional observed outcome. Require at least two steps.',
168
168
  ),
169
+ eventTime: z
170
+ .string()
171
+ .optional()
172
+ .nullable()
173
+ .describe(
174
+ 'Optional event-time expression for bi-temporal validity (#1578). An ISO date ("2025-03-01") or a relative expression verbatim ("last March", "yesterday", "since 2024", "until 2025-06-01"). Resolved against the source turn timestamp at write time — NOT wall-clock. Omit when the fact has no explicit temporal anchor.',
175
+ ),
176
+ event_time: z
177
+ .string()
178
+ .optional()
179
+ .nullable()
180
+ .describe("Alias for eventTime (snake_case). Gateway-tolerance shim (#1578)."),
169
181
  }).superRefine((value, ctx) => {
170
182
  if (value.category === "procedure" && (!Array.isArray(value.procedureSteps) || value.procedureSteps.length < 2)) {
171
183
  ctx.addIssue({
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Issue #1533 — Phase A contract test: atomic write semantics.
3
+ *
4
+ * Rule 54 (CLAUDE.md): replace operations MUST write temp-then-rename, NEVER
5
+ * delete-before-write. This test pins that invariant by making rename fail
6
+ * (read-only target directory) and asserting the original file survives the
7
+ * failed overwrite.
8
+ *
9
+ * The atomic write primitive lives in secure-fs (`writeMaybeEncryptedFile`)
10
+ * and is consumed by StorageManager via `writeStorageSecureFile`. We test the
11
+ * primitive directly here because it is the single atomicity seam every write
12
+ * entry point depends on.
13
+ */
14
+
15
+ import assert from "node:assert/strict";
16
+ import { chmod, mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
17
+ import os from "node:os";
18
+ import path from "node:path";
19
+ import test from "node:test";
20
+
21
+ import { writeMaybeEncryptedFile, readMaybeEncryptedFile } from "../secure-store/secure-fs.js";
22
+
23
+ test("atomicity: writeMaybeEncryptedFile writes temp-then-rename (no temp files left behind on success)", async () => {
24
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-atomic-"));
25
+ try {
26
+ const target = path.join(dir, "target.md");
27
+ await writeMaybeEncryptedFile(target, "first content", null, {}, dir);
28
+
29
+ // No temp files left behind
30
+ const { readdir } = await import("node:fs/promises");
31
+ const entries = await readdir(dir);
32
+ assert.deepEqual(entries.sort(), ["target.md"]);
33
+
34
+ // Overwrite — still only the target, no leftover temp
35
+ await writeMaybeEncryptedFile(target, "second content", null, {}, dir);
36
+ const entries2 = await readdir(dir);
37
+ assert.deepEqual(entries2.sort(), ["target.md"]);
38
+
39
+ const read = await readMaybeEncryptedFile(target, null, dir);
40
+ assert.equal(read, "second content");
41
+ } finally {
42
+ await rm(dir, { recursive: true, force: true });
43
+ }
44
+ });
45
+
46
+ test("atomicity: a failed rename leaves the original file intact (never delete-before-write)", async (t) => {
47
+ // Skip on root — chmod restrictions don't apply.
48
+ if (process.getuid && process.getuid() === 0) {
49
+ t.skip("chmod-based test is unreliable when running as root");
50
+ return;
51
+ }
52
+
53
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-atomic-fail-"));
54
+ try {
55
+ const target = path.join(dir, "survivor.md");
56
+ await writeMaybeEncryptedFile(target, "original", null, {}, dir);
57
+
58
+ // Make the directory read-only so the rename into place fails.
59
+ await chmod(dir, 0o500);
60
+ try {
61
+ await assert.rejects(
62
+ () => writeMaybeEncryptedFile(target, "would-be-replacement", null, {}, dir),
63
+ // rename (or temp write) fails — the exact error varies by platform
64
+ );
65
+
66
+ // THE CONTRACT: the original file must still be readable with its
67
+ // original content. Delete-before-write would have destroyed it before
68
+ // the failed rename.
69
+ const survived = await readMaybeEncryptedFile(target, null, dir);
70
+ assert.equal(survived, "original", "original file must survive a failed atomic replace");
71
+ } finally {
72
+ // Restore perms so cleanup works.
73
+ await chmod(dir, 0o700);
74
+ }
75
+ } finally {
76
+ await rm(dir, { recursive: true, force: true });
77
+ }
78
+ });
79
+
80
+ test("atomicity: StorageManager.updateMemory preserves the original on overwrite success", async (t) => {
81
+ const { StorageManager } = await import("../storage.js");
82
+ const { makeStorage } = await import("./harness.js");
83
+ const { storage, cleanup } = await makeStorage();
84
+ try {
85
+ const id = await storage.writeMemory("fact", "version one");
86
+ const before = await storage.getMemoryById(id);
87
+ assert.ok(before);
88
+ assert.equal(before!.content, "version one");
89
+
90
+ const updated = await storage.updateMemory(id, "version two");
91
+ assert.equal(updated, true);
92
+
93
+ const after = await storage.getMemoryById(id);
94
+ assert.ok(after);
95
+ assert.equal(after!.content, "version two");
96
+ // The id is stable across the update (same file path, not a new memory)
97
+ assert.equal(after!.frontmatter.id, id);
98
+ } finally {
99
+ await cleanup();
100
+ }
101
+ });
102
+
103
+ test("atomicity: mkdir is implicit — writeMaybeEncryptedFile creates parent dirs", async () => {
104
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-atomic-mkdir-"));
105
+ try {
106
+ const target = path.join(dir, "nested", "deep", "file.md");
107
+ await writeMaybeEncryptedFile(target, "content", null, {}, dir);
108
+ const read = await readMaybeEncryptedFile(target, null, dir);
109
+ assert.equal(read, "content");
110
+ } finally {
111
+ await rm(dir, { recursive: true, force: true });
112
+ }
113
+ });
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Issue #1533 — Phase A contract test: cache coherence across instances.
3
+ *
4
+ * Rule 37 (memory-cache): write via instance A, read via a FRESH instance B
5
+ * over the same dir → B sees the write. The module-level caches
6
+ * (readAllMemories in-flight, version sentinels) are keyed by baseDir and
7
+ * shared/static — this test pins that a write invalidates correctly so a
8
+ * second instance never serves stale data.
9
+ */
10
+
11
+ import assert from "node:assert/strict";
12
+ import test from "node:test";
13
+
14
+ import { StorageManager } from "../storage.js";
15
+ import { resetStaticCaches } from "./harness.js";
16
+
17
+ test("cache-coherence: write via instance A, read via fresh instance B → B sees the write", async () => {
18
+ const { mkdtemp, rm } = await import("node:fs/promises");
19
+ const os = await import("node:os");
20
+ const path = await import("node:path");
21
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-cache-coherence-"));
22
+ try {
23
+ resetStaticCaches();
24
+ const smA = new StorageManager(dir);
25
+ await smA.ensureDirectories();
26
+
27
+ const id = await smA.writeMemory("fact", "written by A");
28
+
29
+ // Fresh instance over the same dir
30
+ const smB = new StorageManager(dir);
31
+ await smB.ensureDirectories();
32
+ const found = await smB.getMemoryById(id);
33
+ assert.ok(found, "instance B must see the memory written by instance A");
34
+ assert.equal(found!.content, "written by A");
35
+ } finally {
36
+ resetStaticCaches();
37
+ await rm(dir, { recursive: true, force: true });
38
+ }
39
+ });
40
+
41
+ test("cache-coherence: delete via instance A is reflected in fresh instance B", async () => {
42
+ const { mkdtemp, rm } = await import("node:fs/promises");
43
+ const os = await import("node:os");
44
+ const path = await import("node:path");
45
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-cache-del-"));
46
+ try {
47
+ resetStaticCaches();
48
+ const smA = new StorageManager(dir);
49
+ await smA.ensureDirectories();
50
+ const id = await smA.writeMemory("fact", "to be deleted");
51
+
52
+ // Confirm B sees it
53
+ const smB = new StorageManager(dir);
54
+ await smB.ensureDirectories();
55
+ assert.ok(await smB.getMemoryById(id));
56
+
57
+ // A deletes
58
+ await smA.invalidateMemory(id);
59
+
60
+ // Fresh instance C must NOT see it
61
+ const smC = new StorageManager(dir);
62
+ await smC.ensureDirectories();
63
+ const found = await smC.getMemoryById(id);
64
+ assert.equal(found, null, "fresh instance must not see a deleted memory");
65
+ } finally {
66
+ resetStaticCaches();
67
+ await rm(dir, { recursive: true, force: true });
68
+ }
69
+ });
70
+
71
+ test("cache-coherence: readAllMemories reflects writes across instances after invalidation", async () => {
72
+ const { mkdtemp, rm } = await import("node:fs/promises");
73
+ const os = await import("node:os");
74
+ const path = await import("node:path");
75
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-cache-list-"));
76
+ try {
77
+ resetStaticCaches();
78
+ const smA = new StorageManager(dir);
79
+ await smA.ensureDirectories();
80
+ await smA.writeMemory("fact", "first");
81
+ await smA.writeMemory("decision", "second");
82
+
83
+ const smB = new StorageManager(dir);
84
+ await smB.ensureDirectories();
85
+ const all = await smB.readAllMemories();
86
+ assert.ok(all.length >= 2, "fresh instance must list all written memories");
87
+
88
+ // Write more via A
89
+ await smA.writeMemory("principle", "third");
90
+
91
+ const smC = new StorageManager(dir);
92
+ await smC.ensureDirectories();
93
+ const allAfter = await smC.readAllMemories();
94
+ assert.ok(allAfter.length >= 3, "fresh instance must see the additional write");
95
+ assert.ok(allAfter.some((m) => m.content === "third"));
96
+ } finally {
97
+ resetStaticCaches();
98
+ await rm(dir, { recursive: true, force: true });
99
+ }
100
+ });
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Issue #1533 — Phase A contract test: content hash identity.
3
+ *
4
+ * Rule 23: the write-side and dedup-side MUST hash the same `rawContent`,
5
+ * never the timestamped/cited form. `writeMemory` persists a `contentHash` on
6
+ * the frontmatter for facts; `hasFactContentHash` queries the dedup index.
7
+ * This test pins that the two sides agree so subsequent extractions of the
8
+ * same logical fact are deduped correctly even when citation timestamps differ.
9
+ */
10
+
11
+ import assert from "node:assert/strict";
12
+ import test from "node:test";
13
+
14
+ import { ContentHashIndex } from "../storage.js";
15
+ import { makeStorage } from "./harness.js";
16
+
17
+ test("content-hash: ContentHashIndex.computeHash is deterministic for the same input", () => {
18
+ const a = ContentHashIndex.computeHash("the sky is blue");
19
+ const b = ContentHashIndex.computeHash("the sky is blue");
20
+ assert.equal(a, b);
21
+ assert.notEqual(
22
+ ContentHashIndex.computeHash("the sky is blue"),
23
+ ContentHashIndex.computeHash("the sky is green"),
24
+ );
25
+ });
26
+
27
+ test("content-hash: writeMemory for fact registers the hash so hasFactContentHash returns true", async () => {
28
+ const { storage, cleanup } = await makeStorage();
29
+ try {
30
+ const rawFact = "PostgreSQL is our primary database";
31
+ await storage.writeMemory("fact", rawFact);
32
+
33
+ const has = await storage.hasFactContentHash(rawFact);
34
+ assert.equal(has, true, "hasFactContentHash must return true for a just-written fact");
35
+ } finally {
36
+ await cleanup();
37
+ }
38
+ });
39
+
40
+ test("content-hash: contentHashSource overrides the persisted body for dedup (rule 23)", async () => {
41
+ const { storage, cleanup } = await makeStorage();
42
+ try {
43
+ const rawFact = "We migrated to MySQL in March";
44
+ // Persist a citation-annotated body, but index the RAW fact text
45
+ await storage.writeMemory("fact", `${rawFact} [cited 2026-01-01]`, {
46
+ contentHashSource: rawFact,
47
+ });
48
+
49
+ // Querying with the RAW fact (not the cited form) must hit
50
+ assert.equal(
51
+ await storage.hasFactContentHash(rawFact),
52
+ true,
53
+ "hasFactContentHash must match on the raw fact, not the cited body",
54
+ );
55
+ // The cited form should NOT be separately indexed (it has the citation suffix)
56
+ assert.equal(
57
+ await storage.hasFactContentHash(`${rawFact} [cited 2026-01-01]`),
58
+ false,
59
+ "the cited/timestamped form must not be the indexed hash",
60
+ );
61
+ } finally {
62
+ await cleanup();
63
+ }
64
+ });
65
+
66
+ test("content-hash: non-fact categories do not register a content hash", async () => {
67
+ const { storage, cleanup } = await makeStorage();
68
+ try {
69
+ await storage.writeMemory("decision", "we chose option B");
70
+ assert.equal(
71
+ await storage.hasFactContentHash("we chose option B"),
72
+ false,
73
+ "non-fact categories must not enter the fact dedup index",
74
+ );
75
+ } finally {
76
+ await cleanup();
77
+ }
78
+ });
79
+
80
+ test("content-hash: normalizeContent produces stable text for equivalent inputs", () => {
81
+ const a = ContentHashIndex.normalizeContent(" Hello World ");
82
+ const b = ContentHashIndex.normalizeContent("Hello World");
83
+ assert.equal(a, b, "normalizeContent must collapse whitespace consistently");
84
+ });
85
+
86
+ test("content-hash: the frontmatter contentHash matches computeHash of the raw content", async () => {
87
+ const { storage, cleanup } = await makeStorage();
88
+ try {
89
+ const rawFact = "deterministic hash check";
90
+ const id = await storage.writeMemory("fact", rawFact);
91
+ const memory = await storage.getMemoryById(id);
92
+ assert.ok(memory);
93
+ assert.ok(memory!.frontmatter.contentHash, "frontmatter must carry a contentHash for facts");
94
+ assert.equal(
95
+ memory!.frontmatter.contentHash,
96
+ ContentHashIndex.computeHash(rawFact),
97
+ "frontmatter contentHash must equal computeHash of the raw content",
98
+ );
99
+ } finally {
100
+ await cleanup();
101
+ }
102
+ });
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Issue #1533 — Phase A contract test: failure semantics.
3
+ *
4
+ * Rule 34: reads distinguish empty-dir from unreadable-dir — `{ok:false}` vs
5
+ * `[]`. Where the current surface already does this, we pin it; where it
6
+ * doesn't, we record the gap (for the silent-failures issue) rather than
7
+ * change behavior here.
8
+ *
9
+ * Also pins containment: a FILE where a directory is expected is skipped via
10
+ * lstat().isDirectory(), not existsSync() (rule 24).
11
+ */
12
+
13
+ import assert from "node:assert/strict";
14
+ import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
15
+ import os from "node:os";
16
+ import path from "node:path";
17
+ import test from "node:test";
18
+
19
+ import { StorageManager } from "../storage.js";
20
+ import { makeStorage } from "./harness.js";
21
+
22
+ test("failure-semantics: readAllMemories on a fresh (empty) dir returns [] not null", async () => {
23
+ const { storage, cleanup } = await makeStorage();
24
+ try {
25
+ const result = await storage.readAllMemories();
26
+ assert.ok(Array.isArray(result), "readAllMemories must return an array");
27
+ assert.equal(result.length, 0, "empty store must return an empty array");
28
+ } finally {
29
+ await cleanup();
30
+ }
31
+ });
32
+
33
+ test("failure-semantics: getMemoryById on empty store returns null (not throw)", async () => {
34
+ const { storage, cleanup } = await makeStorage();
35
+ try {
36
+ const result = await storage.getMemoryById("anything");
37
+ assert.equal(result, null);
38
+ } finally {
39
+ await cleanup();
40
+ }
41
+ });
42
+
43
+ test("failure-semantics: readMemoryByPath on a non-existent file returns null (not throw)", async () => {
44
+ const { storage, baseDir, cleanup } = await makeStorage();
45
+ try {
46
+ const result = await storage.readMemoryByPath(path.join(baseDir, "facts", "2026-01-01", "nope.md"));
47
+ assert.equal(result, null);
48
+ } finally {
49
+ await cleanup();
50
+ }
51
+ });
52
+
53
+ test("failure-semantics: readMemoryByPath on a malformed markdown file returns null", async () => {
54
+ const { storage, baseDir, cleanup } = await makeStorage();
55
+ try {
56
+ const dir = path.join(baseDir, "facts", "2026-01-01");
57
+ await mkdir(dir, { recursive: true });
58
+ const filePath = path.join(dir, "garbage.md");
59
+ await writeFile(filePath, "this has no frontmatter at all\njust random text", "utf-8");
60
+
61
+ const result = await storage.readMemoryByPath(filePath);
62
+ assert.equal(result, null, "malformed file (no frontmatter, not entity) must return null");
63
+ } finally {
64
+ await cleanup();
65
+ }
66
+ });
67
+
68
+ test("failure-semantics: a FILE where a category directory is expected does not crash readAllMemories", async () => {
69
+ // Create a FILE named "facts" (not a directory) at the base dir, then scan.
70
+ // collectActiveMemoryPaths uses lstat().isDirectory() (not existsSync) so a
71
+ // file-as-directory entry is skipped without crashing (rule 24 containment).
72
+ // We do NOT call ensureDirectories — it would try to mkdir facts/<today> and
73
+ // fail on the file. The scan path must be robust independently of dir creation.
74
+ const baseDir = await mkdtemp(path.join(os.tmpdir(), "remnic-fail-file-as-dir-"));
75
+ try {
76
+ StorageManager.clearAllStaticCaches();
77
+ await writeFile(path.join(baseDir, "facts"), "I am a file not a directory", "utf-8");
78
+ const storage = new StorageManager(baseDir);
79
+
80
+ const result = await storage.readAllMemories();
81
+ assert.ok(Array.isArray(result));
82
+ assert.equal(result.length, 0, "a file-as-directory must not produce spurious memories");
83
+ } finally {
84
+ StorageManager.clearAllStaticCaches();
85
+ await rm(baseDir, { recursive: true, force: true });
86
+ }
87
+ });
88
+
89
+ test("failure-semantics: invalidateMemory on a missing memory returns false (not throw)", async () => {
90
+ const { storage, cleanup } = await makeStorage();
91
+ try {
92
+ const result = await storage.invalidateMemory("totally-missing-id-99999");
93
+ assert.equal(result, false);
94
+ } finally {
95
+ await cleanup();
96
+ }
97
+ });
98
+
99
+ test("failure-semantics: ensureDirectories is idempotent (safe to call multiple times)", async () => {
100
+ const { storage, cleanup } = await makeStorage();
101
+ try {
102
+ await storage.ensureDirectories();
103
+ await storage.ensureDirectories();
104
+ await storage.ensureDirectories();
105
+ } finally {
106
+ await cleanup();
107
+ }
108
+ });