@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
@@ -549,6 +549,10 @@ export const codegraphIngestTracesOperation = defineOperation<
549
549
  },
550
550
  });
551
551
 
552
+ // Batch-migrated operations (issue #1525): registers all remaining
553
+ // handlers through the boundary as a side effect of importing this module.
554
+ import "./access-operations-batch.js";
555
+
552
556
  export const REGISTERED_OPERATIONS = [
553
557
  memoryGetOperation.spec.name,
554
558
  memorySearchOperation.spec.name,
@@ -44,7 +44,7 @@ import {
44
44
  // catalog-completeness correction: adding routes that were always live but
45
45
  // omitted from the catalog (review-caught). Such a bump MUST be accompanied
46
46
  // by the newly-cataloged entries; the higher count is the honest baseline.
47
- const UNMIGRATED_HANDLER_BASELINE = 134;
47
+ const UNMIGRATED_HANDLER_BASELINE = 80;
48
48
 
49
49
  // Keep the import live — `getOperation` is the call surfaces use at dispatch
50
50
  // time; referencing it here pins the registry's lookup contract.
@@ -187,6 +187,7 @@ function extractHttpRouteDispatchMap(): Map<string, Set<string>> {
187
187
  const lines = source.split("\n");
188
188
  const routeOps = new Map<string, Set<string>>();
189
189
  let currentKey: string | null = null;
190
+ let lastSeenPathname: string | null = null;
190
191
 
191
192
  for (let i = 0; i < lines.length; i++) {
192
193
  const line = lines[i]!;
@@ -217,6 +218,7 @@ function extractHttpRouteDispatchMap(): Map<string, Set<string>> {
217
218
  }
218
219
 
219
220
  if (pathname) {
221
+ lastSeenPathname = pathname;
220
222
  // Determine the HTTP method: same line first, then backward for
221
223
  // exact-match routes, forward for dynamic routes. Check both positive
222
224
  // (===) and negated (!==) method patterns — `req.method !== "GET"` as
@@ -253,6 +255,20 @@ function extractHttpRouteDispatchMap(): Map<string, Set<string>> {
253
255
  continue;
254
256
  }
255
257
 
258
+ // Detect method changes within a shared route block (e.g. a single
259
+ // regex match with GET/PUT/DELETE branches). When a new method check
260
+ // appears after a route pattern was already detected, update currentKey
261
+ // to the new method + the same pathname so per-method getOperation
262
+ // calls are tracked against the right route key.
263
+ const methodChange = line.match(/req\.method\s*===\s*"(\w+)"/);
264
+ if (methodChange && lastSeenPathname) {
265
+ const newKey = methodChange[1]! + " " + lastSeenPathname;
266
+ if (!routeOps.has(newKey)) {
267
+ routeOps.set(newKey, new Set<string>());
268
+ }
269
+ currentKey = newKey;
270
+ }
271
+
256
272
  // Collect getOperation calls within the current route block.
257
273
  if (currentKey) {
258
274
  const opMatch = line.match(/getOperation\("(\w+)"\)/);
@@ -160,65 +160,68 @@ export const MCP_TOOLS: readonly McpToolEntry[] = [
160
160
  // (health, adapters, the /mcp delegate, admin console, SSE-only endpoints)
161
161
  // carry no user request envelope and are intentionally excluded.
162
162
  export const HTTP_ROUTES: readonly HttpRouteEntry[] = [
163
- { method: "POST", pathname: "/engram/v1/recall", operation: null },
164
- { method: "POST", pathname: "/engram/v1/coding-context", operation: null },
165
- { method: "POST", pathname: "/engram/v1/capsules/export", operation: null },
166
- { method: "POST", pathname: "/engram/v1/capsules/import", operation: null },
167
- { method: "GET", pathname: "/engram/v1/offline-sync/snapshot", operation: null },
168
- { method: "GET", pathname: "/engram/v1/offline-sync/snapshot-stream", operation: null },
169
- { method: "POST", pathname: "/engram/v1/offline-sync/snapshot", operation: null },
170
- { method: "POST", pathname: "/engram/v1/offline-sync/files", operation: null },
171
- { method: "POST", pathname: "/engram/v1/offline-sync/file-content", operation: null },
172
- { method: "POST", pathname: "/engram/v1/offline-sync/apply-file-content", operation: null },
173
- { method: "POST", pathname: "/engram/v1/offline-sync/apply", operation: null },
174
- { method: "POST", pathname: "/engram/v1/recall/explain", operation: null },
175
- { method: "POST", pathname: "/engram/v1/action-confidence", operation: null },
176
- { method: "GET", pathname: "/engram/v1/recall/tier-explain", operation: null },
177
- { method: "GET", pathname: "/engram/v1/recall/xray", operation: null },
178
- { method: "GET", pathname: "/engram/v1/wearables/status", operation: null },
179
- { method: "POST", pathname: "/engram/v1/wearables/sync", operation: null },
180
- { method: "GET", pathname: "/engram/v1/wearables/transcript", operation: null },
181
- { method: "GET", pathname: "/engram/v1/wearables/transcripts/search", operation: null },
182
- { method: "GET", pathname: "/engram/v1/wearables/memories", operation: null },
183
- { method: "POST", pathname: "/engram/v1/observe", operation: null },
184
- { method: "POST", pathname: "/engram/v1/lcm/search", operation: null },
185
- { method: "POST", pathname: "/engram/v1/lcm/compaction/flush", operation: null },
186
- { method: "POST", pathname: "/engram/v1/lcm/compaction/record", operation: null },
187
- { method: "GET", pathname: "/engram/v1/lcm/status", operation: null },
163
+ { method: "POST", pathname: "/engram/v1/recall", operation: "recall" },
164
+ { method: "POST", pathname: "/engram/v1/coding-context", operation: "set_coding_context" },
165
+ { method: "POST", pathname: "/engram/v1/capsules/export", operation: "capsule_export" },
166
+ { method: "POST", pathname: "/engram/v1/capsules/import", operation: "capsule_import" },
167
+ { method: "GET", pathname: "/engram/v1/offline-sync/snapshot", operation: "offline_sync_snapshot" },
168
+ { method: "POST", pathname: "/engram/v1/offline-sync/snapshot", operation: "offline_sync_snapshot" },
169
+ { method: "POST", pathname: "/engram/v1/offline-sync/files", operation: "offline_sync_files" },
170
+ { method: "POST", pathname: "/engram/v1/offline-sync/file-content", operation: "offline_sync_file_content" },
171
+ { method: "POST", pathname: "/engram/v1/offline-sync/apply-file-content", operation: "offline_sync_apply_file_content" },
172
+ { method: "POST", pathname: "/engram/v1/offline-sync/apply", operation: "offline_sync_apply" },
173
+ { method: "POST", pathname: "/engram/v1/recall/explain", operation: "recall_explain" },
174
+ { method: "POST", pathname: "/engram/v1/action-confidence", operation: "action_confidence" },
175
+ { method: "GET", pathname: "/engram/v1/recall/tier-explain", operation: "recall_tier_explain" },
176
+ { method: "GET", pathname: "/engram/v1/recall/xray", operation: "recall_xray" },
177
+ { method: "GET", pathname: "/engram/v1/wearables/status", operation: "wearables_status" },
178
+ { method: "POST", pathname: "/engram/v1/wearables/sync", operation: "wearables_sync" },
179
+ { method: "GET", pathname: "/engram/v1/wearables/transcript", operation: "transcript_day" },
180
+ { method: "GET", pathname: "/engram/v1/wearables/transcripts/search", operation: "transcript_search" },
181
+ { method: "GET", pathname: "/engram/v1/wearables/memories", operation: "transcript_memories" },
182
+ { method: "POST", pathname: "/engram/v1/observe", operation: "observe" },
183
+ { method: "POST", pathname: "/engram/v1/lcm/search", operation: "lcm_search" },
184
+ { method: "POST", pathname: "/engram/v1/lcm/compaction/flush", operation: "lcm_compaction_flush" },
185
+ { method: "POST", pathname: "/engram/v1/lcm/compaction/record", operation: "lcm_compaction_record" },
186
+ { method: "GET", pathname: "/engram/v1/lcm/status", operation: "lcm_status" },
188
187
  { method: "POST", pathname: "/engram/v1/memories", operation: "memory_store" },
189
188
  { method: "POST", pathname: "/engram/v1/coding/decisions", operation: "coding_decision" },
190
189
  { method: "POST", pathname: "/engram/v1/coding/architecture", operation: "coding_architecture" },
191
190
  { method: "POST", pathname: "/engram/v1/coding/delta", operation: "coding_delta" },
192
- { method: "POST", pathname: "/engram/v1/suggestions", operation: null },
193
- { method: "GET", pathname: "/engram/v1/memories", operation: null },
191
+ { method: "POST", pathname: "/engram/v1/suggestions", operation: "suggestion_submit" },
192
+ { method: "GET", pathname: "/engram/v1/memories", operation: "memory_list" },
194
193
  { method: "GET", pathname: "/engram/v1/memories/:id", operation: "memory_get" },
195
- { method: "GET", pathname: "/engram/v1/memories/:id/timeline", operation: null },
196
- { method: "GET", pathname: "/engram/v1/entities", operation: null },
197
- { method: "GET", pathname: "/engram/v1/entities/:id", operation: null },
198
- { method: "GET", pathname: "/engram/v1/review-queue", operation: null },
199
- { method: "GET", pathname: "/engram/v1/maintenance", operation: null },
200
- { method: "GET", pathname: "/engram/v1/quality", operation: null },
201
- { method: "GET", pathname: "/engram/v1/trust-zones/status", operation: null },
202
- { method: "GET", pathname: "/engram/v1/procedural/stats", operation: null },
203
- { method: "GET", pathname: "/engram/v1/trust-zones/records", operation: null },
204
- { method: "POST", pathname: "/engram/v1/review-disposition", operation: null },
205
- { method: "POST", pathname: "/engram/v1/trust-zones/promote", operation: null },
206
- { method: "POST", pathname: "/engram/v1/trust-zones/demo-seed", operation: null },
207
- { method: "POST", pathname: "/v1/citations/observed", operation: null },
208
- { method: "GET", pathname: "/engram/v1/review/contradictions", operation: null },
209
- { method: "GET", pathname: "/engram/v1/review/contradictions/:id", operation: null },
210
- { method: "POST", pathname: "/engram/v1/review/resolve", operation: null },
211
- { method: "GET", pathname: "/engram/v1/graph/snapshot", operation: null },
212
- { method: "POST", pathname: "/engram/v1/contradiction-scan", operation: null },
213
- { method: "GET", pathname: "/engram/v1/graph/events", operation: null },
214
- { method: "GET", pathname: "/engram/v1/console/state", operation: null },
215
- { method: "GET", pathname: "/engram/v1/peers", operation: null },
216
- { method: "GET", pathname: "/engram/v1/peers/:id/profile", operation: null },
217
- { method: "GET", pathname: "/engram/v1/peers/:id", operation: null },
218
- { method: "PUT", pathname: "/engram/v1/peers/:id", operation: null },
219
- { method: "DELETE", pathname: "/engram/v1/peers/:id", operation: null },
220
- { method: "GET", pathname: "/engram/v1/dreams/status", operation: null },
221
- { method: "POST", pathname: "/engram/v1/dreams/run", operation: null },
194
+ { method: "GET", pathname: "/engram/v1/memories/:id/timeline", operation: "memory_timeline" },
195
+ { method: "GET", pathname: "/engram/v1/entities", operation: "entity_list" },
196
+ { method: "GET", pathname: "/engram/v1/entities/:id", operation: "entity_get" },
197
+ { method: "GET", pathname: "/engram/v1/review-queue", operation: "review_queue_list" },
198
+ { method: "GET", pathname: "/engram/v1/maintenance", operation: "maintenance_status" },
199
+ { method: "GET", pathname: "/engram/v1/quality", operation: "quality_status" },
200
+ { method: "GET", pathname: "/engram/v1/trust-zones/status", operation: "trust_zones_status" },
201
+ { method: "GET", pathname: "/engram/v1/procedural/stats", operation: "procedural_stats" },
202
+ { method: "GET", pathname: "/engram/v1/trust-zones/records", operation: "trust_zones_records" },
203
+ { method: "POST", pathname: "/engram/v1/review-disposition", operation: "review_disposition" },
204
+ { method: "POST", pathname: "/engram/v1/trust-zones/promote", operation: "trust_zones_promote" },
205
+ { method: "POST", pathname: "/engram/v1/trust-zones/demo-seed", operation: "trust_zones_demo_seed" },
206
+ { method: "POST", pathname: "/v1/citations/observed", operation: "citations_observed" },
207
+ { method: "GET", pathname: "/engram/v1/review/contradictions", operation: "review_list" },
208
+ { method: "GET", pathname: "/engram/v1/review/contradictions/:id", operation: "contradiction_detail" },
209
+ { method: "POST", pathname: "/engram/v1/review/resolve", operation: "review_resolve" },
210
+ { method: "GET", pathname: "/engram/v1/graph/snapshot", operation: "graph_snapshot" },
211
+ { method: "POST", pathname: "/engram/v1/contradiction-scan", operation: "contradiction_scan_run" },
212
+ { method: "GET", pathname: "/engram/v1/console/state", operation: "console_state" },
213
+ { method: "GET", pathname: "/engram/v1/peers", operation: "peer_list" },
214
+ { method: "GET", pathname: "/engram/v1/peers/:id/profile", operation: "peer_profile_get" },
215
+ { method: "GET", pathname: "/engram/v1/peers/:id", operation: "peer_get" },
216
+ { method: "PUT", pathname: "/engram/v1/peers/:id", operation: "peer_set" },
217
+ { method: "DELETE", pathname: "/engram/v1/peers/:id", operation: "peer_delete" },
218
+ { method: "GET", pathname: "/engram/v1/dreams/status", operation: "dreams_status" },
219
+ { method: "POST", pathname: "/engram/v1/dreams/run", operation: "dreams_run" },
220
+ // SSE-only endpoints: carried in the catalog for completeness tracking.
221
+ // The operation registration makes them visible to the boundary; the
222
+ // streaming lifecycle is owned by the HTTP transport (handleGraphEventsSSE).
223
+ { method: "GET", pathname: "/engram/v1/offline-sync/snapshot-stream", operation: "offline_sync_snapshot_stream" },
224
+ { method: "GET", pathname: "/engram/v1/graph/events", operation: "graph_events" },
222
225
  ];
223
226
 
224
227
  /**
package/src/event-time.ts CHANGED
@@ -286,6 +286,14 @@ function resolveQuarter(
286
286
  */
287
287
  function resolveAbsolute(raw: string): number | null {
288
288
  const trimmed = raw.trim();
289
+ // YYYY-MM (ISO year-month, no day) → first day of that month, UTC
290
+ // (#1578 review r3: the prompt example "until 2025-06" must resolve, not
291
+ // fall back to "assumed").
292
+ const ym = /^(\d{4})-(\d{2})$/.exec(trimmed);
293
+ if (ym) {
294
+ const ms = buildDateMs(Number(ym[1]), Number(ym[2]), 1);
295
+ return ms === null ? null : startOfDayUtc(ms);
296
+ }
289
297
  const ms = parseFlexibleIsoTimestamp(trimmed);
290
298
  if (ms === null) return null;
291
299
  // Date-only inputs (no T) anchor to start-of-day UTC.
@@ -469,6 +477,17 @@ function resolveEndBound(anchorMs: number, period: string): number | null {
469
477
  return buildDateMs(Number(trimmed) + 1, 1, 1);
470
478
  }
471
479
 
480
+ // YYYY-MM (year-month) end bound: exclusive end = start of the FOLLOWING
481
+ // month, so "until 2025-06" spans all of June (#1578 review r3).
482
+ const ymEnd = /^(\d{4})-(\d{2})$/.exec(trimmed);
483
+ if (ymEnd) {
484
+ const startMs = buildDateMs(Number(ymEnd[1]), Number(ymEnd[2]), 1);
485
+ if (startMs === null) return null;
486
+ const nd = new Date(startMs);
487
+ nd.setUTCMonth(nd.getUTCMonth() + 1);
488
+ return startOfDayUtc(nd.getTime());
489
+ }
490
+
472
491
  // Absolute date/datetime end bound.
473
492
  const absMs = resolveAbsolute(trimmed);
474
493
  if (absMs !== null) {
@@ -546,3 +565,57 @@ function resolveEndBound(anchorMs: number, period: string): number | null {
546
565
  }
547
566
  return null;
548
567
  }
568
+
569
+ /**
570
+ * Resolved bi-temporal fields for a single extracted fact (issue #1578 PR2).
571
+ *
572
+ * - `validFrom` / `validUntil` — the event-time `[from, until)` interval.
573
+ * When `eventTimeSource === "assumed"`, `validFrom` is copied from
574
+ * `observedAt` (the ingestion anchor) so every bi-temporal-on fact carries
575
+ * at least a start bound.
576
+ * - `observedAt` — always the ingestion anchor (the source turn timestamp).
577
+ * - `eventTimeSource` — `"extracted"` when the expression resolved to a real
578
+ * event-time interval; `"assumed"` when the expression was absent or
579
+ * unresolvable.
580
+ *
581
+ * This is the single write-time helper every extraction entry path calls
582
+ * (rule 39). It is pure: (expression, anchor) in, interval out. Resolution
583
+ * never touches `Date.now()` — replay/import of old transcripts resolves
584
+ * against the old anchor.
585
+ */
586
+ export interface FactEventTime {
587
+ validFrom?: string;
588
+ validUntil?: string;
589
+ observedAt: string;
590
+ eventTimeSource: "extracted" | "assumed";
591
+ }
592
+
593
+ /**
594
+ * Resolve a single fact's event-time expression against the ingestion anchor.
595
+ *
596
+ * Returns the bi-temporal interval to persist alongside the fact. When the
597
+ * expression is absent, empty, or unresolvable, falls back to
598
+ * `eventTimeSource: "assumed"` with `validFrom = observedAt` — per the
599
+ * #1578 frontmatter contract. When the expression resolves but only pins one
600
+ * bound ("since 2024" → validFrom only; "until 2025-06" → validUntil only),
601
+ * only the resolved bound is returned.
602
+ */
603
+ export function resolveFactEventTime(
604
+ expression: string | undefined | null,
605
+ anchorIso: string,
606
+ ): FactEventTime {
607
+ const resolved = resolveEventTime(expression, anchorIso);
608
+ if (resolved.ok && (resolved.validFrom !== undefined || resolved.validUntil !== undefined)) {
609
+ return {
610
+ ...(resolved.validFrom !== undefined ? { validFrom: resolved.validFrom } : {}),
611
+ ...(resolved.validUntil !== undefined ? { validUntil: resolved.validUntil } : {}),
612
+ observedAt: anchorIso,
613
+ eventTimeSource: "extracted",
614
+ };
615
+ }
616
+ return {
617
+ validFrom: anchorIso,
618
+ observedAt: anchorIso,
619
+ eventTimeSource: "assumed",
620
+ };
621
+ }
@@ -0,0 +1,269 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { mkdtemp, rm } from "node:fs/promises";
6
+
7
+ import { resolveFactEventTime } from "./event-time.js";
8
+ import { ExtractionEngine } from "./extraction.js";
9
+ import { ExtractedFactSchema } from "./schemas.js";
10
+ import { StorageManager } from "./storage.js";
11
+
12
+ /**
13
+ * Issue #1578 PR 2 — extraction event-time wiring.
14
+ *
15
+ * Proves three contracts:
16
+ * 1. resolveFactEventTime anchors to the SOURCE TURN timestamp, never
17
+ * wall-clock — replay/import of an old transcript yields old-era event
18
+ * times (the core bi-temporal invariant from the #1578 design).
19
+ * 2. Extraction normalization passes the per-fact `eventTime` expression
20
+ * through from raw LLM JSON (camelCase and snake_case payloads) so the
21
+ * persist path can resolve it at write time.
22
+ * 3. Storage round-trips `invalid_at` so the bi-temporal interval survives
23
+ * write → read (complementing PR 1's observedAt/eventTimeSource test).
24
+ */
25
+
26
+ // ── 1. Replay anchoring ──────────────────────────────────────────────────
27
+
28
+ test("resolveFactEventTime: replay of a 2025 transcript anchors 'last March' to 2025-03, not today", () => {
29
+ // The anchor is the source turn timestamp from an old (2025) transcript.
30
+ const anchor2025 = "2025-04-10T12:00:00.000Z";
31
+ const result = resolveFactEventTime("last March", anchor2025);
32
+ assert.equal(result.eventTimeSource, "extracted");
33
+ assert.ok(result.validFrom, "validFrom must be set for a resolvable expression");
34
+ // "last March" relative to April 2025 → March 2025.
35
+ assert.ok(result.validFrom!.startsWith("2025-03"), `expected 2025-03, got ${result.validFrom}`);
36
+ // Critical bi-temporal invariant: NOT anchored to today's wall-clock.
37
+ assert.ok(!result.validFrom!.startsWith("2026"), "must not anchor to wall-clock");
38
+ assert.equal(result.observedAt, anchor2025);
39
+ });
40
+
41
+ test("resolveFactEventTime: same expression, different anchors → different validFrom", () => {
42
+ // The identical phrase resolves differently depending on the anchor —
43
+ // proving the resolver is anchor-driven, not Date.now()-driven.
44
+ const old = resolveFactEventTime("yesterday", "2025-01-15T10:00:00.000Z");
45
+ const recent = resolveFactEventTime("yesterday", "2026-07-05T10:00:00.000Z");
46
+ assert.ok(old.validFrom!.startsWith("2025-01-14"), `got ${old.validFrom}`);
47
+ assert.ok(recent.validFrom!.startsWith("2026-07-04"), `got ${recent.validFrom}`);
48
+ assert.notEqual(old.validFrom, recent.validFrom);
49
+ });
50
+
51
+ test("resolveFactEventTime: absent expression → 'assumed' with validFrom = anchor", () => {
52
+ const anchor = "2025-06-01T00:00:00.000Z";
53
+ const result = resolveFactEventTime(undefined, anchor);
54
+ assert.equal(result.eventTimeSource, "assumed");
55
+ assert.equal(result.validFrom, anchor);
56
+ assert.equal(result.observedAt, anchor);
57
+ assert.equal(result.validUntil, undefined);
58
+ });
59
+
60
+ test("resolveFactEventTime: 'since 2024' → validFrom only (open-ended start)", () => {
61
+ const result = resolveFactEventTime("since 2024", "2025-06-01T00:00:00.000Z");
62
+ assert.equal(result.eventTimeSource, "extracted");
63
+ assert.ok(result.validFrom!.startsWith("2024-01-01"));
64
+ assert.equal(result.validUntil, undefined);
65
+ });
66
+
67
+ test("resolveFactEventTime: unresolvable garbage → 'assumed' fallback", () => {
68
+ const anchor = "2025-06-01T00:00:00.000Z";
69
+ const result = resolveFactEventTime("sometime maybe???", anchor);
70
+ assert.equal(result.eventTimeSource, "assumed");
71
+ assert.equal(result.validFrom, anchor);
72
+ });
73
+
74
+ // ── 2. Extraction normalization passes eventTime through ─────────────────
75
+
76
+ function makeEngine(): ExtractionEngine {
77
+ // Minimal construction: the facts normalizer is a pure data transform
78
+ // (no LLM / I/O). Stub the optional deps so the constructor doesn't
79
+ // spin up real clients.
80
+ return new ExtractionEngine(
81
+ { memoryDir: "/tmp" } as any,
82
+ undefined, // profiler → safe default
83
+ {} as any, // localLlm stub
84
+ undefined, // gatewayConfig
85
+ {} as any, // modelRegistry stub
86
+ );
87
+ }
88
+
89
+ test("extraction normalization: camelCase 'eventTime' passes through to ExtractedFact", () => {
90
+ const engine = makeEngine();
91
+ const result = (engine as any).normalizeExtractionResultPayload({
92
+ facts: [
93
+ { content: "We moved offices in March.", category: "fact", eventTime: "last March" },
94
+ ],
95
+ entities: [],
96
+ });
97
+ assert.equal(result.facts.length, 1);
98
+ assert.equal(result.facts[0].eventTime, "last March");
99
+ });
100
+
101
+ test("extraction normalization: snake_case 'event_time' also captured", () => {
102
+ const engine = makeEngine();
103
+ const result = (engine as any).normalizeExtractionResultPayload({
104
+ facts: [
105
+ { content: "Switched to PostgreSQL.", category: "fact", event_time: "2025-01-15" },
106
+ ],
107
+ entities: [],
108
+ });
109
+ assert.equal(result.facts[0].eventTime, "2025-01-15");
110
+ });
111
+
112
+ test("extraction normalization: absent eventTime → undefined (assumed deferred to write time)", () => {
113
+ const engine = makeEngine();
114
+ const result = (engine as any).normalizeExtractionResultPayload({
115
+ facts: [{ content: "The sky is blue.", category: "fact" }],
116
+ entities: [],
117
+ });
118
+ assert.equal(result.facts[0].eventTime, undefined);
119
+ });
120
+
121
+ // ── 2b. Schema preserves snake_case event_time (gateway fix, chatgpt-codex) ─
122
+
123
+ test("ExtractedFactSchema: snake_case event_time survives schema parse (gateway strip-unknown fix)", () => {
124
+ // Zod .object() strips unknown keys by default. Before the event_time alias
125
+ // was added, the gateway fallback path (parseWithSchemaDetailed → schema.parse)
126
+ // silently dropped event_time, losing the temporal bound (#1578).
127
+ const parsed = ExtractedFactSchema.parse({
128
+ content: "Switched to PostgreSQL.",
129
+ category: "fact",
130
+ confidence: 0.9,
131
+ tags: [],
132
+ event_time: "2025-01-15",
133
+ });
134
+ assert.equal(parsed.event_time, "2025-01-15");
135
+ });
136
+
137
+ test("ExtractedFactSchema: camelCase eventTime also survives schema parse (no regression)", () => {
138
+ const parsed = ExtractedFactSchema.parse({
139
+ content: "We moved offices in March.",
140
+ category: "fact",
141
+ confidence: 0.8,
142
+ tags: [],
143
+ eventTime: "2025-03",
144
+ });
145
+ assert.equal(parsed.eventTime, "2025-03");
146
+ });
147
+
148
+ // ── 3. Storage round-trips invalid_at ────────────────────────────────────
149
+
150
+ test("storage: invalid_at round-trips through writeMemoryFrontmatter → readAllMemories", async () => {
151
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-et-invalidAt-"));
152
+ try {
153
+ const storage = new StorageManager(dir);
154
+ await storage.ensureDirectories();
155
+
156
+ const id = await storage.writeMemory("fact", "Old stack was Node 18.", {
157
+ validAt: "2025-01-01T00:00:00.000Z",
158
+ observedAt: "2025-06-01T00:00:00.000Z",
159
+ eventTimeSource: "extracted",
160
+ });
161
+
162
+ const before = await storage.readAllMemories();
163
+ const mem = before.find((m) => m.frontmatter.id === id);
164
+ assert.ok(mem);
165
+
166
+ const ok = await storage.writeMemoryFrontmatter(mem!, {
167
+ invalid_at: "2025-06-15T00:00:00.000Z",
168
+ });
169
+ assert.equal(ok, true);
170
+
171
+ const after = await storage.readAllMemories();
172
+ const patched = after.find((m) => m.frontmatter.id === id);
173
+ assert.ok(patched, "patched fact must be readable");
174
+ assert.equal(patched!.frontmatter.invalid_at, "2025-06-15T00:00:00.000Z");
175
+ // Bi-temporal provenance preserved through the frontmatter patch.
176
+ assert.equal(patched!.frontmatter.observedAt, "2025-06-01T00:00:00.000Z");
177
+ assert.equal(patched!.frontmatter.eventTimeSource, "extracted");
178
+ assert.equal(patched!.frontmatter.valid_at, "2025-01-01T00:00:00.000Z");
179
+ } finally {
180
+ await rm(dir, { recursive: true, force: true });
181
+ }
182
+ });
183
+
184
+ test("storage: writeMemory accepts invalidAt directly (end-bound at extraction write time)", async () => {
185
+ // Proves Fix C: writeMemory now accepts invalidAt so "until X" expressions
186
+ // persist invalid_at at write time without a separate updateMemoryFrontmatter call.
187
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-et-writeMem-invalid-"));
188
+ try {
189
+ const storage = new StorageManager(dir);
190
+ await storage.ensureDirectories();
191
+ const id = await storage.writeMemory("fact", "We used MongoDB until June 2025.", {
192
+ validAt: "2024-01-01T00:00:00.000Z",
193
+ invalidAt: "2025-06-01T00:00:00.000Z",
194
+ observedAt: "2025-06-20T00:00:00.000Z",
195
+ eventTimeSource: "extracted",
196
+ });
197
+ const mems = await storage.readAllMemories();
198
+ const mem = mems.find((m) => m.frontmatter.id === id);
199
+ assert.ok(mem);
200
+ assert.equal(mem!.frontmatter.valid_at, "2024-01-01T00:00:00.000Z");
201
+ assert.equal(mem!.frontmatter.invalid_at, "2025-06-01T00:00:00.000Z");
202
+ assert.equal(mem!.frontmatter.observedAt, "2025-06-20T00:00:00.000Z");
203
+ assert.equal(mem!.frontmatter.eventTimeSource, "extracted");
204
+ } finally {
205
+ await rm(dir, { recursive: true, force: true });
206
+ }
207
+ });
208
+
209
+ test("resolveFactEventTime: 'through 2026' → validUntil only (open-ended end bound)", () => {
210
+ const result = resolveFactEventTime("through 2026", "2025-06-20T00:00:00.000Z");
211
+ assert.equal(result.eventTimeSource, "extracted");
212
+ assert.equal(result.validFrom, undefined);
213
+ assert.ok(result.validUntil, "validUntil must be set for an end-bound expression");
214
+ // "through 2026" → exclusive end = start of 2027.
215
+ assert.equal(result.validUntil, "2027-01-01T00:00:00.000Z");
216
+ });
217
+ test("storage: writeChunk propagates invalidAt/observedAt/eventTimeSource from the parent fact (#1578 PR2)", async () => {
218
+ // Proves the cursor-bugbot fix: an independently-surfaced chunk expires at the
219
+ // same invalid_at window as its parent so recall's isValidityExpiredNow check
220
+ // fires on chunks too, not just the chunked parent write.
221
+ const dir = await mkdtemp(path.join(os.tmpdir(), "remnic-et-chunk-invalid-"));
222
+ try {
223
+ const storage = new StorageManager(dir);
224
+ await storage.ensureDirectories();
225
+ const parentId = await storage.writeMemory("fact", "We used MongoDB until June 2025.", {
226
+ validAt: "2024-01-01T00:00:00.000Z",
227
+ invalidAt: "2025-06-01T00:00:00.000Z",
228
+ observedAt: "2025-06-20T00:00:00.000Z",
229
+ eventTimeSource: "extracted",
230
+ });
231
+ const chunkId = await storage.writeChunk(
232
+ parentId,
233
+ 0,
234
+ 1,
235
+ "fact",
236
+ "We used MongoDB until June 2025.",
237
+ {
238
+ validAt: "2024-01-01T00:00:00.000Z",
239
+ invalidAt: "2025-06-01T00:00:00.000Z",
240
+ observedAt: "2025-06-20T00:00:00.000Z",
241
+ eventTimeSource: "extracted",
242
+ },
243
+ );
244
+ const mems = await storage.readAllMemories();
245
+ const chunk = mems.find((m) => m.frontmatter.id === chunkId);
246
+ assert.ok(chunk, "chunk must be readable");
247
+ assert.equal(chunk!.frontmatter.valid_at, "2024-01-01T00:00:00.000Z");
248
+ assert.equal(chunk!.frontmatter.invalid_at, "2025-06-01T00:00:00.000Z",
249
+ "chunk must carry the parent's end bound so it expires independently");
250
+ assert.equal(chunk!.frontmatter.observedAt, "2025-06-20T00:00:00.000Z");
251
+ assert.equal(chunk!.frontmatter.eventTimeSource, "extracted");
252
+ } finally {
253
+ await rm(dir, { recursive: true, force: true });
254
+ }
255
+ });
256
+ test("resolveFactEventTime: 'until 2025-06' (YYYY-MM) → invalidAt = start of following month (#1578 r3)", () => {
257
+ const result = resolveFactEventTime("until 2025-06", "2025-06-20T00:00:00.000Z");
258
+ assert.equal(result.eventTimeSource, "extracted");
259
+ assert.equal(result.validFrom, undefined);
260
+ assert.equal(result.validUntil, "2025-07-01T00:00:00.000Z",
261
+ "YYYY-MM end bound must span the whole month, not fall back to assumed");
262
+ });
263
+
264
+ test("resolveFactEventTime: bare '2025-03' (YYYY-MM) → validFrom = first of month (#1578 r3)", () => {
265
+ const result = resolveFactEventTime("2025-03", "2025-06-20T00:00:00.000Z");
266
+ assert.equal(result.eventTimeSource, "extracted");
267
+ assert.equal(result.validFrom, "2025-03-01T00:00:00.000Z");
268
+ assert.equal(result.validUntil, undefined);
269
+ });
package/src/extraction.ts CHANGED
@@ -279,6 +279,12 @@ export class ExtractionEngine {
279
279
  typeof f?.quote === "string" && f.quote.trim().length > 0
280
280
  ? f.quote
281
281
  : undefined,
282
+ eventTime:
283
+ typeof f?.eventTime === "string" && f.eventTime.trim().length > 0
284
+ ? f.eventTime.trim()
285
+ : typeof f?.event_time === "string" && f.event_time.trim().length > 0
286
+ ? f.event_time.trim()
287
+ : undefined,
282
288
  }))
283
289
  .filter((f: any) => f.content.length > 0)
284
290
  : [];
@@ -750,6 +756,11 @@ export class ExtractionEngine {
750
756
  this.config.provenance?.enabled
751
757
  ? '- Source quotes: For each fact, include a "quote" field with the EXACT verbatim words from the conversation that support the fact (a contiguous span from a single turn, not a paraphrase). Cap at ~300 chars.'
752
758
  : "",
759
+ // #1578: emit the same event-time guidance as the primary extraction
760
+ // paths so proactive-recovered facts also carry an optional eventTime
761
+ // (chatgpt-codex thread on extraction.ts:1607). Returns "" when the
762
+ // bi-temporal gate is off, keeping the prompt unchanged by default.
763
+ this.eventTimePromptInstruction(),
753
764
  "",
754
765
  "Base extracted facts (do not repeat):",
755
766
  factsPreview || "(none)",
@@ -1250,10 +1261,23 @@ export class ExtractionEngine {
1250
1261
  // reasoningTrace through normalizeReasoningTrace before passing it on so
1251
1262
  // gateway output matches the shape local/direct-client paths produce.
1252
1263
  const normalizedFacts = result.facts.map((f: any) => {
1253
- if (!f?.reasoningTrace) return f;
1264
+ if (!f) return f;
1265
+ // Gateway tolerance: collapse snake_case event_time → camelCase
1266
+ // eventTime so the gateway path matches the local/direct/proactive
1267
+ // normalization (#1578 r3 — cursor bugbot).
1268
+ const eventTime =
1269
+ typeof f.eventTime === "string" && f.eventTime.trim().length > 0
1270
+ ? f.eventTime.trim()
1271
+ : typeof f.event_time === "string" && f.event_time.trim().length > 0
1272
+ ? f.event_time.trim()
1273
+ : undefined;
1274
+ if (!f.reasoningTrace && eventTime === undefined) return f;
1254
1275
  return {
1255
1276
  ...f,
1256
- reasoningTrace: normalizeReasoningTrace(f.reasoningTrace) ?? undefined,
1277
+ ...(f.reasoningTrace
1278
+ ? { reasoningTrace: normalizeReasoningTrace(f.reasoningTrace) ?? undefined }
1279
+ : {}),
1280
+ ...(eventTime !== undefined ? { eventTime } : {}),
1257
1281
  };
1258
1282
  });
1259
1283
  const sanitized = this.sanitizeExtractionResult({
@@ -1394,7 +1418,7 @@ Examples of when to add structuredAttributes:
1394
1418
  - Decisions: {"chosen": "PostgreSQL", "rejected": "MongoDB", "reason": "ACID compliance"}
1395
1419
  - Quantities/measurements: {"budget": "50000", "team_size": "5", "deadline": "2024-06-01"}
1396
1420
  Only add structuredAttributes when there are concrete values. Skip for abstract or narrative facts.
1397
-
1421
+ ${this.eventTimePromptInstruction()}
1398
1422
  Also generate:
1399
1423
  1. 1-3 genuine questions you're curious about from this conversation
1400
1424
  2. Profile updates about user patterns/behaviors (if any)
@@ -1595,6 +1619,28 @@ ${truncatedConversation}`;
1595
1619
  return { facts, entities, profileUpdates: [], questions: [] };
1596
1620
  }
1597
1621
 
1622
+ /**
1623
+ * Bi-temporal event-time extraction instruction (#1578 PR2). Emitted on
1624
+ * every extraction entry path when `temporal.biTemporal` is on so the LLM
1625
+ * emits an optional per-fact `eventTime` expression. The expression is
1626
+ * resolved against the source turn timestamp at write time — never
1627
+ * wall-clock — so replay/import of old transcripts anchors correctly.
1628
+ * Returns an empty string when the gate is off (byte-identical prompt).
1629
+ */
1630
+ private eventTimePromptInstruction(): string {
1631
+ if (!this.config.temporalBiTemporal) return "";
1632
+ return `
1633
+ === Event Time (bi-temporal) ===
1634
+ When a fact has an explicit temporal anchor — a date, month, season, or relative time expression stating WHEN the fact became (or stopped being) true — capture it verbatim in an "eventTime" field on that fact. Examples:
1635
+ - "We moved offices in March" → "eventTime": "last March"
1636
+ - "The API has been rate-limited since 2024" → "eventTime": "since 2024"
1637
+ - "I switched to PostgreSQL on 2025-01-15" → "eventTime": "2025-01-15"
1638
+ - "We used MongoDB until June 2025" → "eventTime": "until 2025-06"
1639
+ Accepted forms: ISO dates ("2025-03-01"), year-month ("2025-03"), month/season + year ("March 2025", "summer 2024"), relative ("yesterday", "last week", "this month", "next year", "last December"), and open-ended ("since 2024", "until 2025-06").
1640
+ Omit "eventTime" when the fact has no explicit temporal anchor — do NOT guess or infer dates. The system resolves the expression against the conversation's own timestamp, not today's date.
1641
+ `;
1642
+ }
1643
+
1598
1644
  /**
1599
1645
  * Build extraction instructions shared between local and cloud LLM.
1600
1646
  */
@@ -1663,6 +1709,7 @@ ${existingEntities.join(", ")}
1663
1709
 
1664
1710
  When you see something that matches a known entity, use THAT name exactly. Only create a NEW entity if nothing in this list represents it.
1665
1711
  ` : ""}
1712
+ ${this.eventTimePromptInstruction()}
1666
1713
  Also extract relationships between entities mentioned in the conversation.
1667
1714
  - Format: {source: "entity-name", target: "entity-name", label: "relationship description"}
1668
1715
  - Max 5 relationships per extraction