@remnic/core 9.3.723 → 9.3.725

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 (270) hide show
  1. package/dist/access-boundary.d.ts +6 -11
  2. package/dist/access-boundary.js +9 -9
  3. package/dist/access-cli.js +29 -23
  4. package/dist/access-cli.js.map +1 -1
  5. package/dist/access-http.d.ts +5 -5
  6. package/dist/access-http.js +19 -14
  7. package/dist/access-mcp.d.ts +5 -5
  8. package/dist/access-mcp.js +18 -13
  9. package/dist/access-operations-batch.js +16 -10
  10. package/dist/access-operations.d.ts +5 -5
  11. package/dist/access-operations.js +17 -11
  12. package/dist/{access-service-Dj3I-VBS.d.ts → access-service-B2PlPtxh.d.ts} +3 -3
  13. package/dist/access-service.d.ts +5 -5
  14. package/dist/access-service.js +8 -8
  15. package/dist/access-surface-catalog.d.ts +5 -5
  16. package/dist/access-surface-catalog.js +82 -82
  17. package/dist/access-surface-catalog.js.map +1 -1
  18. package/dist/action-confidence.d.ts +1 -1
  19. package/dist/active-memory-bridge.d.ts +1 -1
  20. package/dist/active-recall.d.ts +1 -1
  21. package/dist/behavior-learner.d.ts +1 -1
  22. package/dist/behavior-signals.d.ts +1 -1
  23. package/dist/bootstrap.d.ts +4 -4
  24. package/dist/briefing.d.ts +1 -1
  25. package/dist/briefing.js +3 -3
  26. package/dist/buffer-surprise-report.d.ts +1 -1
  27. package/dist/buffer.d.ts +1 -1
  28. package/dist/calibration.d.ts +1 -1
  29. package/dist/capabilities.d.ts +1 -1
  30. package/dist/{catalog-F-OEVbRQ.d.ts → catalog-CzUb-GyD.d.ts} +1 -1
  31. package/dist/causal-behavior.d.ts +1 -1
  32. package/dist/causal-consolidation.d.ts +1 -1
  33. package/dist/causal-consolidation.js +4 -4
  34. package/dist/causal-trajectory-graph.d.ts +1 -1
  35. package/dist/{chunk-EVPNWN7Z.js → chunk-4R3CGCI5.js} +2 -2
  36. package/dist/{chunk-UO5ZHIIH.js → chunk-4WZ3J7M6.js} +2 -2
  37. package/dist/{chunk-PD42EYQN.js → chunk-5OPYYPJN.js} +2 -2
  38. package/dist/{chunk-EEJMD2L2.js → chunk-6CWGS2LY.js} +2 -2
  39. package/dist/{chunk-7CTNUVHZ.js → chunk-6SQQOJ4U.js} +2 -2
  40. package/dist/{chunk-ILFSEIRT.js → chunk-7AG54AF3.js} +1 -1
  41. package/dist/{chunk-ILFSEIRT.js.map → chunk-7AG54AF3.js.map} +1 -1
  42. package/dist/{chunk-UIXXE6NI.js → chunk-C6BWLYW5.js} +24 -15
  43. package/dist/chunk-C6BWLYW5.js.map +1 -0
  44. package/dist/{chunk-A5XNBK2D.js → chunk-DF3I6JGP.js} +6 -6
  45. package/dist/{chunk-GJSL4WG2.js → chunk-F3XZN24Z.js} +2 -2
  46. package/dist/{chunk-J3JPB7ZI.js → chunk-FACTVMXQ.js} +2 -2
  47. package/dist/{chunk-B7WISIAJ.js → chunk-FN6QP7JQ.js} +154 -1067
  48. package/dist/chunk-FN6QP7JQ.js.map +1 -0
  49. package/dist/{chunk-EBDA5TYA.js → chunk-FXCIOLRB.js} +2 -2
  50. package/dist/{chunk-TXQCG6SE.js → chunk-H7HUQIY7.js} +3 -3
  51. package/dist/{chunk-QD6QP5P6.js → chunk-I2HL5M6T.js} +17 -17
  52. package/dist/{chunk-M7R5R6OJ.js → chunk-IAIPNEHD.js} +20 -2
  53. package/dist/{chunk-M7R5R6OJ.js.map → chunk-IAIPNEHD.js.map} +1 -1
  54. package/dist/{chunk-Z4RU5VPM.js → chunk-J334N6QV.js} +2 -2
  55. package/dist/{chunk-NXRUUDUF.js → chunk-JJJCVCCQ.js} +2 -2
  56. package/dist/{chunk-GDUOCHCD.js → chunk-KGZF2VO3.js} +10 -10
  57. package/dist/{chunk-LA3HZKES.js → chunk-KLFYKC3K.js} +448 -49
  58. package/dist/chunk-KLFYKC3K.js.map +1 -0
  59. package/dist/{chunk-LLUGAZ3B.js → chunk-NT36GU6X.js} +30 -4
  60. package/dist/chunk-NT36GU6X.js.map +1 -0
  61. package/dist/{chunk-OHTOPFRF.js → chunk-SC2YAWRF.js} +2 -2
  62. package/dist/{chunk-YEE5KJQQ.js → chunk-T55EGZSJ.js} +2 -2
  63. package/dist/{chunk-N77ELJ2D.js → chunk-TT5U3UDO.js} +4 -4
  64. package/dist/{chunk-6FAMMBQ3.js → chunk-UBHVAGO7.js} +2 -2
  65. package/dist/chunk-VI5SGGXB.js +420 -0
  66. package/dist/chunk-VI5SGGXB.js.map +1 -0
  67. package/dist/{chunk-EDOQM4E4.js → chunk-WAFDHKES.js} +2 -2
  68. package/dist/{chunk-EXUCUPV2.js → chunk-WM7DNKBD.js} +2 -2
  69. package/dist/{dreams-ledger-3WSCI5V4.js → chunk-YF6FMLBT.js} +5 -6
  70. package/dist/{dreams-ledger-3WSCI5V4.js.map → chunk-YF6FMLBT.js.map} +1 -1
  71. package/dist/{chunk-WQYSDPMA.js → chunk-YQMPUYHE.js} +2 -2
  72. package/dist/{chunk-5O42UKXI.js → chunk-YUYPWJD3.js} +2 -2
  73. package/dist/{chunk-VCLGUIFM.js → chunk-Z3FGC3QE.js} +2 -2
  74. package/dist/{chunk-A7TJZLPK.js → chunk-ZK2L72VN.js} +2 -2
  75. package/dist/{chunk-BXEOHV7K.js → chunk-ZOHMPFOB.js} +3 -3
  76. package/dist/{graph-edge-decay-QPRJQ7DL.js → chunk-ZQNCHPTZ.js} +4 -5
  77. package/dist/{graph-edge-decay-QPRJQ7DL.js.map → chunk-ZQNCHPTZ.js.map} +1 -1
  78. package/dist/{cli-P_GaYcCY.d.ts → cli-IOlDHSBT.d.ts} +3 -3
  79. package/dist/cli.d.ts +6 -6
  80. package/dist/cli.js +31 -26
  81. package/dist/compounding/engine.d.ts +1 -1
  82. package/dist/compounding/engine.js +3 -3
  83. package/dist/compounding/preference-consolidator.d.ts +1 -1
  84. package/dist/compression-optimizer.d.ts +1 -1
  85. package/dist/config.d.ts +1 -1
  86. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  87. package/dist/connectors/codex-materialize-runner.js +3 -3
  88. package/dist/connectors/codex-materialize.d.ts +1 -1
  89. package/dist/connectors/index.d.ts +1 -1
  90. package/dist/connectors/index.js +3 -3
  91. package/dist/consolidation-provenance-check.d.ts +1 -1
  92. package/dist/consolidation-undo.d.ts +1 -1
  93. package/dist/contradiction/index.d.ts +1 -1
  94. package/dist/conversation-index/backend.d.ts +1 -1
  95. package/dist/conversation-index/chunker.d.ts +1 -1
  96. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  97. package/dist/conversation-index/indexer.d.ts +1 -1
  98. package/dist/conversation-index/search.d.ts +1 -1
  99. package/dist/day-summary.d.ts +1 -1
  100. package/dist/delinearize.d.ts +1 -1
  101. package/dist/direct-answer-wiring.d.ts +1 -1
  102. package/dist/direct-answer.d.ts +1 -1
  103. package/dist/dreams-ledger-6UODQOX6.js +22 -0
  104. package/dist/dreams-ledger-6UODQOX6.js.map +1 -0
  105. package/dist/embedding-fallback.d.ts +1 -1
  106. package/dist/enrichment/index.d.ts +1 -1
  107. package/dist/entity-retrieval.d.ts +1 -1
  108. package/dist/entity-retrieval.js +3 -3
  109. package/dist/entity-schema.d.ts +1 -1
  110. package/dist/event-time.d.ts +33 -1
  111. package/dist/event-time.js +3 -1
  112. package/dist/explicit-capture.d.ts +4 -4
  113. package/dist/extraction-faithfulness.d.ts +1 -1
  114. package/dist/extraction-judge-telemetry.d.ts +1 -1
  115. package/dist/extraction-judge-training.d.ts +1 -1
  116. package/dist/extraction-judge.d.ts +1 -1
  117. package/dist/extraction.d.ts +1 -1
  118. package/dist/fallback-llm.d.ts +1 -1
  119. package/dist/graph-dashboard-diff.d.ts +1 -1
  120. package/dist/graph-dashboard-key.d.ts +1 -1
  121. package/dist/graph-dashboard-parser.d.ts +1 -1
  122. package/dist/graph-edge-decay-RXUCJIN4.js +22 -0
  123. package/dist/graph-edge-decay-RXUCJIN4.js.map +1 -0
  124. package/dist/graph-edge-reinforcement.d.ts +1 -1
  125. package/dist/graph-snapshot.d.ts +1 -1
  126. package/dist/graph.d.ts +1 -1
  127. package/dist/identity-continuity.d.ts +1 -1
  128. package/dist/importance.d.ts +1 -1
  129. package/dist/index.d.ts +9 -9
  130. package/dist/index.js +58 -56
  131. package/dist/index.js.map +1 -1
  132. package/dist/intent.d.ts +1 -1
  133. package/dist/lcm/engine.d.ts +1 -1
  134. package/dist/lcm/index.d.ts +1 -1
  135. package/dist/lcm/tools.d.ts +1 -1
  136. package/dist/lifecycle.d.ts +1 -1
  137. package/dist/live-connectors-runner.d.ts +1 -1
  138. package/dist/local-llm.d.ts +1 -1
  139. package/dist/local-model-endpoint.d.ts +1 -1
  140. package/dist/maintenance/memory-governance.d.ts +1 -1
  141. package/dist/maintenance/memory-governance.js +3 -3
  142. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  143. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  144. package/dist/mcp-memory-inspector-app.d.ts +5 -5
  145. package/dist/memory-action-policy.d.ts +1 -1
  146. package/dist/memory-cache.d.ts +1 -1
  147. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  148. package/dist/memory-projection-store.d.ts +1 -1
  149. package/dist/memory-provenance.d.ts +1 -1
  150. package/dist/memory-worth-outcomes.d.ts +1 -1
  151. package/dist/models-json.d.ts +1 -1
  152. package/dist/namespaces/migrate.d.ts +2 -2
  153. package/dist/namespaces/migrate.js +4 -4
  154. package/dist/namespaces/principal.d.ts +1 -1
  155. package/dist/namespaces/search.d.ts +1 -1
  156. package/dist/namespaces/storage.d.ts +2 -2
  157. package/dist/namespaces/storage.js +3 -3
  158. package/dist/native-knowledge.d.ts +1 -1
  159. package/dist/operator-toolkit.d.ts +1 -1
  160. package/dist/operator-toolkit.js +8 -8
  161. package/dist/orchestration/maintenance.d.ts +2 -2
  162. package/dist/orchestration/maintenance.js +5 -5
  163. package/dist/{orchestrator-f5xv19kp.d.ts → orchestrator-C_OMSIMZ.d.ts} +34 -3
  164. package/dist/orchestrator.d.ts +4 -4
  165. package/dist/orchestrator.js +17 -17
  166. package/dist/patterns-cli.d.ts +1 -1
  167. package/dist/policy-runtime.d.ts +1 -1
  168. package/dist/provenance.d.ts +1 -1
  169. package/dist/qmd-recall-cache.d.ts +1 -1
  170. package/dist/qmd.d.ts +1 -1
  171. package/dist/recall-disclosure-escalation.d.ts +1 -1
  172. package/dist/recall-explain-renderer.d.ts +1 -1
  173. package/dist/recall-explain-renderer.js +3 -3
  174. package/dist/recall-planner-llm.d.ts +1 -1
  175. package/dist/recall-state.d.ts +1 -1
  176. package/dist/recall-tag-filter.d.ts +1 -1
  177. package/dist/recall-xray-cli.d.ts +1 -1
  178. package/dist/recall-xray-cli.js +4 -4
  179. package/dist/recall-xray-renderer.d.ts +1 -1
  180. package/dist/recall-xray-renderer.js +3 -3
  181. package/dist/recall-xray.d.ts +1 -1
  182. package/dist/recall-xray.js +2 -2
  183. package/dist/resolve-auth-token.d.ts +1 -1
  184. package/dist/retrieval-agents.d.ts +1 -1
  185. package/dist/retrieval-tiers.d.ts +1 -1
  186. package/dist/routing/engine.d.ts +1 -1
  187. package/dist/routing/store.d.ts +1 -1
  188. package/dist/schemas.d.ts +22 -22
  189. package/dist/search/embed-helper.d.ts +1 -1
  190. package/dist/search/factory.d.ts +1 -1
  191. package/dist/search/index.d.ts +1 -1
  192. package/dist/search/lancedb-backend.d.ts +1 -1
  193. package/dist/search/meilisearch-backend.d.ts +1 -1
  194. package/dist/search/noop-backend.d.ts +1 -1
  195. package/dist/search/orama-backend.d.ts +1 -1
  196. package/dist/search/port.d.ts +1 -1
  197. package/dist/search/remote-backend.d.ts +1 -1
  198. package/dist/{semantic-consolidation-BQm1e1Pn.d.ts → semantic-consolidation-B8LiYtvx.d.ts} +1 -1
  199. package/dist/semantic-consolidation.d.ts +2 -2
  200. package/dist/semantic-consolidation.js +4 -4
  201. package/dist/semantic-rule-promotion.js +3 -3
  202. package/dist/semantic-rule-verifier.d.ts +1 -1
  203. package/dist/semantic-rule-verifier.js +3 -3
  204. package/dist/session-observer-bands.d.ts +1 -1
  205. package/dist/session-observer-state.d.ts +1 -1
  206. package/dist/shared-context/manager.d.ts +1 -1
  207. package/dist/signal.d.ts +1 -1
  208. package/dist/storage.d.ts +1 -1
  209. package/dist/storage.js +2 -2
  210. package/dist/summarizer.d.ts +1 -1
  211. package/dist/summary-snapshot.d.ts +1 -1
  212. package/dist/temporal-supersession.d.ts +1 -1
  213. package/dist/temporal-validity.d.ts +1 -1
  214. package/dist/threading.d.ts +1 -1
  215. package/dist/tier-migration.d.ts +1 -1
  216. package/dist/tier-routing.d.ts +1 -1
  217. package/dist/topics.d.ts +1 -1
  218. package/dist/transcript.d.ts +1 -1
  219. package/dist/transfer/types.d.ts +12 -12
  220. package/dist/trust-score-stage.d.ts +1 -1
  221. package/dist/trust-score.d.ts +1 -1
  222. package/dist/{types-DwO1Ot4n.d.ts → types-DmTaqob2.d.ts} +14 -0
  223. package/dist/types.d.ts +1 -1
  224. package/dist/types.js +1 -1
  225. package/dist/utility-runtime.d.ts +1 -1
  226. package/dist/verified-recall.js +3 -3
  227. package/package.json +2 -2
  228. package/src/access-boundary.ts +36 -2
  229. package/src/access-http.ts +17 -8
  230. package/src/access-mcp.ts +149 -1189
  231. package/src/access-operations-batch.ts +227 -1439
  232. package/src/access-surface-catalog.test.ts +4 -15
  233. package/src/access-surface-catalog.ts +82 -82
  234. package/src/bitemporal-dedup-backfill.test.ts +169 -0
  235. package/src/event-time.ts +57 -0
  236. package/src/extraction-eventtime-wiring.test.ts +101 -1
  237. package/src/orchestrator.ts +238 -43
  238. package/src/recall/archive-scoring.ts +526 -0
  239. package/src/types.ts +14 -0
  240. package/dist/chunk-B7WISIAJ.js.map +0 -1
  241. package/dist/chunk-EM2LDXJ2.js +0 -1315
  242. package/dist/chunk-EM2LDXJ2.js.map +0 -1
  243. package/dist/chunk-LA3HZKES.js.map +0 -1
  244. package/dist/chunk-LLUGAZ3B.js.map +0 -1
  245. package/dist/chunk-UIXXE6NI.js.map +0 -1
  246. /package/dist/{chunk-EVPNWN7Z.js.map → chunk-4R3CGCI5.js.map} +0 -0
  247. /package/dist/{chunk-UO5ZHIIH.js.map → chunk-4WZ3J7M6.js.map} +0 -0
  248. /package/dist/{chunk-PD42EYQN.js.map → chunk-5OPYYPJN.js.map} +0 -0
  249. /package/dist/{chunk-EEJMD2L2.js.map → chunk-6CWGS2LY.js.map} +0 -0
  250. /package/dist/{chunk-7CTNUVHZ.js.map → chunk-6SQQOJ4U.js.map} +0 -0
  251. /package/dist/{chunk-A5XNBK2D.js.map → chunk-DF3I6JGP.js.map} +0 -0
  252. /package/dist/{chunk-GJSL4WG2.js.map → chunk-F3XZN24Z.js.map} +0 -0
  253. /package/dist/{chunk-J3JPB7ZI.js.map → chunk-FACTVMXQ.js.map} +0 -0
  254. /package/dist/{chunk-EBDA5TYA.js.map → chunk-FXCIOLRB.js.map} +0 -0
  255. /package/dist/{chunk-TXQCG6SE.js.map → chunk-H7HUQIY7.js.map} +0 -0
  256. /package/dist/{chunk-QD6QP5P6.js.map → chunk-I2HL5M6T.js.map} +0 -0
  257. /package/dist/{chunk-Z4RU5VPM.js.map → chunk-J334N6QV.js.map} +0 -0
  258. /package/dist/{chunk-NXRUUDUF.js.map → chunk-JJJCVCCQ.js.map} +0 -0
  259. /package/dist/{chunk-GDUOCHCD.js.map → chunk-KGZF2VO3.js.map} +0 -0
  260. /package/dist/{chunk-OHTOPFRF.js.map → chunk-SC2YAWRF.js.map} +0 -0
  261. /package/dist/{chunk-YEE5KJQQ.js.map → chunk-T55EGZSJ.js.map} +0 -0
  262. /package/dist/{chunk-N77ELJ2D.js.map → chunk-TT5U3UDO.js.map} +0 -0
  263. /package/dist/{chunk-6FAMMBQ3.js.map → chunk-UBHVAGO7.js.map} +0 -0
  264. /package/dist/{chunk-EDOQM4E4.js.map → chunk-WAFDHKES.js.map} +0 -0
  265. /package/dist/{chunk-EXUCUPV2.js.map → chunk-WM7DNKBD.js.map} +0 -0
  266. /package/dist/{chunk-WQYSDPMA.js.map → chunk-YQMPUYHE.js.map} +0 -0
  267. /package/dist/{chunk-5O42UKXI.js.map → chunk-YUYPWJD3.js.map} +0 -0
  268. /package/dist/{chunk-VCLGUIFM.js.map → chunk-Z3FGC3QE.js.map} +0 -0
  269. /package/dist/{chunk-A7TJZLPK.js.map → chunk-ZK2L72VN.js.map} +0 -0
  270. /package/dist/{chunk-BXEOHV7K.js.map → chunk-ZOHMPFOB.js.map} +0 -0
@@ -129,7 +129,7 @@ import {
129
129
  shouldFilterSupersededFromRecall,
130
130
  } from "./temporal-supersession.js";
131
131
  import { isValidAsOf, isValidityExpiredNow } from "./temporal-validity.js";
132
- import { resolveFactEventTime } from "./event-time.js";
132
+ import { pickFactEventTimeAnchor, resolveFactEventTime } from "./event-time.js";
133
133
  import { RelevanceStore } from "./relevance.js";
134
134
  import { NegativeExampleStore } from "./negative.js";
135
135
  import {
@@ -438,6 +438,7 @@ import type {
438
438
  EntityStructuredSection,
439
439
  EntityTimelineEntry,
440
440
  } from "./types.js";
441
+ import { disposeDefaultArchiveScoring, getDefaultArchiveScoring, memoryFileToScoreItem } from "./recall/archive-scoring.js";
441
442
 
442
443
  export interface BulkImportBatchIngestResult {
443
444
  attemptedTurnCount: number;
@@ -1828,6 +1829,7 @@ export function resolvePersistedMemoryRelativePath(options: {
1828
1829
  }
1829
1830
 
1830
1831
  export class Orchestrator {
1832
+
1831
1833
  readonly storage: StorageManager;
1832
1834
  private readonly storageRouter: NamespaceStorageRouter;
1833
1835
  /** Rebuildable namespace catalog (issue #1499). Inert unless namespaces enabled. */
@@ -2111,6 +2113,8 @@ export class Orchestrator {
2111
2113
  if (this.conversationQmd && this.conversationQmd !== this.qmd) {
2112
2114
  await (this.conversationQmd as { dispose?: () => void | Promise<void> }).dispose?.();
2113
2115
  }
2116
+ // Issue #1674: terminate archive-scoring worker threads on destroy.
2117
+ await disposeDefaultArchiveScoring();
2114
2118
  }
2115
2119
 
2116
2120
  /** Set per-session workspace for the next recall() call (compaction reset). @internal */
@@ -2727,6 +2731,123 @@ export class Orchestrator {
2727
2731
  index.remove(memory.content);
2728
2732
  }
2729
2733
 
2734
+ /**
2735
+ * Issue #1671 — backfill bi-temporal bounds onto an existing promoted/deduped
2736
+ * copy that was written BEFORE the source fact carried a resolved
2737
+ * `invalid_at`/`observedAt`/`eventTimeSource`.
2738
+ *
2739
+ * On re-extraction/backfill, a fact may now carry a resolved end bound (e.g.
2740
+ * "until June 2025") that the existing copy lacks because it was promoted
2741
+ * before bi-temporal wiring existed. Without this backfill, recall keeps
2742
+ * surfacing an expired fact even though the source copy now expires correctly.
2743
+ *
2744
+ * Finds the active fact in `targetStorage` matching `dedupContent`, then
2745
+ * patches the temporal frontmatter the existing copy is missing. Best-effort
2746
+ * / fail-open — any I/O error is logged and swallowed so the dedup
2747
+ * short-circuit is never blocked by a backfill failure.
2748
+ *
2749
+ * Matching: the stored `frontmatter.contentHash` is compared against
2750
+ * `ContentHashIndex.computeHash(dedupContent)` first (the exact hash the
2751
+ * content-hash index uses), then falls back to stripping citations and
2752
+ * comparing normalized bodies. This handles inline-attribution deployments
2753
+ * where the persisted body carries a citation marker the dedup key does not.
2754
+ *
2755
+ * I/O gate: only triggers when `invalidAt` is present (the end bound that
2756
+ * actually changes recall behavior by expiring the fact). `observedAt` and
2757
+ * `eventTimeSource` alone don'\''t expire a fact, so backfilling them without
2758
+ * an end bound would cause a full readAllMemories scan on every dedup hit
2759
+ * under biTemporal for no recall benefit.
2760
+ *
2761
+ * Only patches fields the existing copy LACKS — never overwrites a bound the
2762
+ * copy already carries.
2763
+ */
2764
+ private async backfillTemporalBoundsOnDedupHit(
2765
+ targetStorage: StorageManager,
2766
+ dedupContent: string,
2767
+ bounds: {
2768
+ invalidAt?: string;
2769
+ observedAt?: string;
2770
+ eventTimeSource?: "extracted" | "assumed";
2771
+ },
2772
+ entityRef?: string,
2773
+ ): Promise<void> {
2774
+ // I/O gate: only scan when there is an end bound to backfill. Without
2775
+ // invalidAt there is nothing that changes recall behavior — observedAt
2776
+ // and eventTimeSource alone do not expire a fact. This avoids a full
2777
+ // readAllMemories on every dedup short-circuit under biTemporal.
2778
+ if (!bounds.invalidAt) return;
2779
+ try {
2780
+ const incomingHash = ContentHashIndex.computeHash(dedupContent);
2781
+ const normalizedIncoming = ContentHashIndex.normalizeContent(dedupContent);
2782
+ // Normalize the entity for same-entity scoping when provided — two
2783
+ // entities can share identical fact text, and patching a different
2784
+ // entity'\''s fact would corrupt its temporal bounds (cursor review).
2785
+ const incomingEntityNorm = entityRef
2786
+ ? normalizeSupersessionKey(entityRef)
2787
+ : undefined;
2788
+ const all = await targetStorage.readAllMemories();
2789
+ const existing = all.find((m) => {
2790
+ if (m.frontmatter.category !== "fact") return false;
2791
+ if ((m.frontmatter.status ?? "active") !== "active") return false;
2792
+ // Same-entity guard: reject only when the stored fact carries a
2793
+ // DIFFERENT entity (two entities can share identical fact text, so
2794
+ // patching the other entity's copy would corrupt its bounds — codex
2795
+ // P2 PRRT_OvB4A). Legacy facts written before entity linkage (no
2796
+ // entityRef) have no entity to conflict with, so they stay eligible
2797
+ // for backfill (cursor PRRT_OvKnV: the guard must NOT silently
2798
+ // no-op for older promoted copies that predate entity linkage).
2799
+ if (
2800
+ incomingEntityNorm &&
2801
+ m.frontmatter.entityRef &&
2802
+ normalizeSupersessionKey(m.frontmatter.entityRef) !== incomingEntityNorm
2803
+ ) {
2804
+ return false;
2805
+ }
2806
+ // Prefer the stored contentHash (what the hash index actually keys
2807
+ // on) — it is computed from contentHashSource (the raw/enriched
2808
+ // body before citation), matching the dedupContent the caller passes.
2809
+ if (m.frontmatter.contentHash) {
2810
+ return m.frontmatter.contentHash === incomingHash;
2811
+ }
2812
+ // Legacy facts without a stored hash: strip citations then compare
2813
+ // normalized bodies so inline-attribution markers don'\''t prevent
2814
+ // a match.
2815
+ return (
2816
+ ContentHashIndex.normalizeContent(
2817
+ stripCitationForTemplate(m.content ?? "", this.config.inlineSourceAttributionFormat),
2818
+ ) === normalizedIncoming
2819
+ );
2820
+ });
2821
+ if (!existing) return;
2822
+ // Build a patch containing ONLY the fields the existing copy lacks.
2823
+ const patch: Partial<MemoryFrontmatter> = {};
2824
+ const fm = existing.frontmatter;
2825
+ if (bounds.invalidAt && (!fm.invalid_at || fm.invalid_at.length === 0)) {
2826
+ patch.invalid_at = bounds.invalidAt;
2827
+ }
2828
+ if (bounds.observedAt && (!fm.observedAt || fm.observedAt.length === 0)) {
2829
+ patch.observedAt = bounds.observedAt;
2830
+ }
2831
+ if (
2832
+ bounds.eventTimeSource &&
2833
+ (!fm.eventTimeSource || fm.eventTimeSource.length === 0)
2834
+ ) {
2835
+ patch.eventTimeSource = bounds.eventTimeSource;
2836
+ }
2837
+ if (Object.keys(patch).length === 0) return;
2838
+ const ok = await targetStorage.writeMemoryFrontmatter(existing, patch);
2839
+ if (ok) {
2840
+ log.debug(
2841
+ `bitemporal-backfill: patched ${Object.keys(patch).join(",")} onto existing fact ${fm.id ?? "(unknown)"} in ${targetStorage.dir}`,
2842
+ );
2843
+ }
2844
+ } catch (err) {
2845
+ log.warn(
2846
+ `bitemporal-backfill: failed open for ${targetStorage.dir}: ${err}`,
2847
+ );
2848
+ }
2849
+ }
2850
+
2730
2851
  private async saveContentHashIndexes(): Promise<void> {
2731
2852
  const indexes = new Set<ContentHashIndex>();
2732
2853
  if (this.contentHashIndex) indexes.add(this.contentHashIndex);
@@ -14155,6 +14276,22 @@ export class Orchestrator {
14155
14276
  options.category === "fact" &&
14156
14277
  (await targetStorage.hasFactContentHash(dedupContent))
14157
14278
  ) {
14279
+ // #1671 — backfill bi-temporal bounds the existing promoted copy
14280
+ // lacks (re-extraction with a now-resolved invalidAt). Best-effort,
14281
+ // fail-open; the helper gates on invalidAt to avoid I/O when no
14282
+ // end bound is present.
14283
+ if (options.invalidAt) {
14284
+ await this.backfillTemporalBoundsOnDedupHit(
14285
+ targetStorage,
14286
+ dedupContent,
14287
+ {
14288
+ invalidAt: options.invalidAt,
14289
+ ...(options.observedAt ? { observedAt: options.observedAt } : {}),
14290
+ ...(options.eventTimeSource ? { eventTimeSource: options.eventTimeSource } : {}),
14291
+ },
14292
+ options.entityRef,
14293
+ );
14294
+ }
14158
14295
  continue;
14159
14296
  }
14160
14297
  const promotedId = await targetStorage.writeMemory(
@@ -14318,6 +14455,22 @@ export class Orchestrator {
14318
14455
  options.category === "fact" &&
14319
14456
  (await sharedStorage.hasFactContentHash(dedupContent))
14320
14457
  ) {
14458
+ // #1671 — backfill bi-temporal bounds onto the existing shared copy
14459
+ // before the supersession short-circuit. Covers all return paths below
14460
+ // (supersession-hit, catch-skip, and the no-supersession short-circuit)
14461
+ // in one shot. Best-effort / fail-open; the helper gates on invalidAt.
14462
+ if (options.invalidAt) {
14463
+ await this.backfillTemporalBoundsOnDedupHit(
14464
+ sharedStorage,
14465
+ dedupContent,
14466
+ {
14467
+ invalidAt: options.invalidAt,
14468
+ ...(options.observedAt ? { observedAt: options.observedAt } : {}),
14469
+ ...(options.eventTimeSource ? { eventTimeSource: options.eventTimeSource } : {}),
14470
+ },
14471
+ options.entityRef,
14472
+ );
14473
+ }
14321
14474
  // Uj6H fix: shared-namespace temporal supersession must also run when
14322
14475
  // the hash-dedup short-circuit fires. Without this, an existing shared
14323
14476
  // fact whose structuredAttributes are stale (or an older conflicting
@@ -14885,7 +15038,14 @@ export class Orchestrator {
14885
15038
  typeof (fact as any).confidence === "number"
14886
15039
  ? (fact as any).confidence
14887
15040
  : 0.7;
14888
- const biTemporal = this.config.temporalBiTemporal && sourceContext?.validAt ? resolveFactEventTime(fact.eventTime, sourceContext.validAt) : undefined;
15041
+ // #1670 anchor each fact's event-time to its SOURCE TURN timestamp,
15042
+ // not the batch-wide latest. When a buffered conversation spans a date
15043
+ // boundary, a relative expression ("yesterday") on an early-turn fact
15044
+ // must resolve against that early turn's date. Prefer the fact's
15045
+ // explicit sourceTurnTimestamp, then the earliest provenance span's
15046
+ // observedAt, then fall back to the batch anchor (legacy extractors).
15047
+ const factAnchor = pickFactEventTimeAnchor(fact, sourceContext?.validAt);
15048
+ const biTemporal = this.config.temporalBiTemporal && factAnchor ? resolveFactEventTime(fact.eventTime, factAnchor) : undefined;
14889
15049
  // Content-hash dedup check (v6.0)
14890
15050
  //
14891
15051
  // Canonicalize pre-tagged facts before hashing (Codex P2 — issue #369).
@@ -14990,6 +15150,15 @@ export class Orchestrator {
14990
15150
  writeCategory === "procedure"
14991
15151
  ? buildProcedurePersistBody(fact.content, fact.procedureSteps)
14992
15152
  : canonicalContentForHash;
15153
+ // Importance is scored before the dedup short-circuit so #1671 backfill
15154
+ // can gate on it (cursor PRRT_OvKnS): a low-value duplicate that the
15155
+ // importance write-gate (#372) or the judge pre-filter would drop must
15156
+ // not expire an active fact.
15157
+ const importance = scoreImportance(
15158
+ fact.content,
15159
+ writeCategory,
15160
+ fact.tags,
15161
+ );
14993
15162
  let exactDuplicate = false;
14994
15163
  try {
14995
15164
  exactDuplicate = await this.hasContentHashDedup(
@@ -15002,6 +15171,47 @@ export class Orchestrator {
15002
15171
  );
15003
15172
  }
15004
15173
  if (exactDuplicate) {
15174
+ // #1671 — before short-circuiting, backfill bi-temporal bounds
15175
+ // onto the existing source-namespace copy if it lacks bounds the
15176
+ // incoming fact now carries (re-extraction with a resolved invalidAt).
15177
+ // Skip when the fact would be rejected/deferred/pending by downstream
15178
+ // gates — a non-durable candidate must not expire an active fact
15179
+ // (chatgpt-codex P1: faithfulness, requireSpans, extraction judge).
15180
+ if (biTemporal && biTemporal.validUntil) {
15181
+ const fr = faithfulnessResultsByFactIndex?.get(factLoopIndex);
15182
+ const faithfulnessWouldPending =
15183
+ faithfulnessMode === "enforce" &&
15184
+ fr?.ok === true &&
15185
+ (fr.verdict === "unsupported" || fr.verdict === "contradicted");
15186
+ const requireSpansWouldPending =
15187
+ this.config.provenance?.requireSpans === true &&
15188
+ fact.requireSpansPending === true;
15189
+ const judgeVerdict = judgeVerdictsByFactIndex?.get(factLoopIndex);
15190
+ const judgeWouldGate =
15191
+ !this.config.extractionJudgeShadow &&
15192
+ judgeVerdict !== undefined &&
15193
+ !judgeVerdict.durable;
15194
+ // Importance gate (cursor PRRT_OvKnS): below-threshold duplicates
15195
+ // would never persist (#372) and carry no judge verdict (the
15196
+ // pre-filter skips them), so they must not expire an active fact.
15197
+ if (
15198
+ isAboveImportanceThreshold(importance.level, this.config.extractionMinImportanceLevel) &&
15199
+ !faithfulnessWouldPending &&
15200
+ !requireSpansWouldPending &&
15201
+ !judgeWouldGate
15202
+ ) {
15203
+ await this.backfillTemporalBoundsOnDedupHit(
15204
+ targetStorage,
15205
+ contentHashDedupKey,
15206
+ {
15207
+ invalidAt: biTemporal.validUntil,
15208
+ observedAt: biTemporal.observedAt,
15209
+ eventTimeSource: biTemporal.eventTimeSource,
15210
+ },
15211
+ fact.entityRef,
15212
+ );
15213
+ }
15214
+ }
15005
15215
  log.debug(
15006
15216
  `dedup: skipping duplicate fact "${fact.content.slice(0, 60)}…" in storage ${targetStorage.dir}`,
15007
15217
  );
@@ -15009,14 +15219,6 @@ export class Orchestrator {
15009
15219
  continue;
15010
15220
  }
15011
15221
 
15012
- // Score importance using local heuristics (Phase 1B).
15013
- // writeCategory / targetStorage already reflect routing.
15014
- const importance = scoreImportance(
15015
- fact.content,
15016
- writeCategory,
15017
- fact.tags,
15018
- );
15019
-
15020
15222
  if (writeCategory === "procedure" && this.config.procedural?.enabled !== true) {
15021
15223
  log.debug("persistExtraction: skip procedure memory (procedural.enabled is false)");
15022
15224
  continue;
@@ -18915,29 +19117,22 @@ export class Orchestrator {
18915
19117
  await this.readArchivedMemoriesForNamespaces(recallNamespaces);
18916
19118
  if (archivedMemories.length === 0) return scopedSeedResults;
18917
19119
 
18918
- const scored: QmdSearchResult[] = [];
18919
- for (const memory of archivedMemories) {
18920
- throwIfRecallAborted(abortSignal);
18921
- const haystack = [
18922
- memory.content,
18923
- memory.frontmatter.category,
18924
- ...(memory.frontmatter.tags ?? []),
18925
- ]
18926
- .join(" ")
18927
- .toLowerCase();
18928
- let hits = 0;
18929
- for (const token of tokens) {
18930
- if (haystack.includes(token)) hits += 1;
18931
- }
18932
- if (hits === 0) continue;
18933
- const normalized = hits / tokens.length;
18934
- scored.push({
18935
- docid: memory.frontmatter.id,
18936
- path: memory.path,
18937
- score: normalized,
18938
- snippet: memory.content.slice(0, 400).replace(/\n/g, " "),
18939
- });
18940
- }
19120
+ // Issue #1674: off-load the CPU-bound archive-scoring loop to a
19121
+ // worker_threads pool so concurrent recall requests run on separate
19122
+ // cores instead of serializing on the main JS thread. The pure scoring
19123
+ // function is identical to the old inline loop — only the execution
19124
+ // context changed. Aborts are checked at the boundaries (before submit
19125
+ // and after result); the worker's work is bounded by the file count.
19126
+ throwIfRecallAborted(abortSignal);
19127
+ const scoring = getDefaultArchiveScoring();
19128
+ const scoredResults = await scoring.score(archivedMemories.map(memoryFileToScoreItem), tokens, abortSignal);
19129
+ throwIfRecallAborted(abortSignal);
19130
+ const scored: QmdSearchResult[] = scoredResults.map((r) => ({
19131
+ docid: r.docid,
19132
+ path: r.path,
19133
+ score: r.score,
19134
+ snippet: r.snippet,
19135
+ }));
18941
19136
 
18942
19137
  const mergedByPath = new Map<string, QmdSearchResult>();
18943
19138
  for (const result of [...scopedSeedResults, ...scored]) {
@@ -19138,17 +19333,17 @@ export class Orchestrator {
19138
19333
  }
19139
19334
  }
19140
19335
  if (longTerm.length === 0) {
19336
+ // Deadline-aware abort: terminate the scoring worker when the shared
19337
+ // assembly deadline wins, not just when the caller aborts (#1674).
19338
+ const da = new AbortController();
19339
+ if (options.abortSignal?.aborted) da.abort();
19340
+ else options.abortSignal?.addEventListener("abort", () => da.abort(), { once: true });
19141
19341
  longTerm = await runColdStepWithinDeadline(
19142
- "archive scan",
19143
- [],
19144
- () =>
19145
- this.searchLongTermArchiveFallback(
19146
- options.prompt,
19147
- options.recallNamespaces,
19148
- options.recallResultLimit,
19149
- options.queryAwarePrefilter,
19150
- options.abortSignal,
19151
- ),
19342
+ "archive scan", [],
19343
+ () => this.searchLongTermArchiveFallback(
19344
+ options.prompt, options.recallNamespaces, options.recallResultLimit,
19345
+ options.queryAwarePrefilter, da.signal),
19346
+ () => da.abort(),
19152
19347
  );
19153
19348
  if (longTerm.length > 0) {
19154
19349
  log.debug("cold-tier recall source=archive-scan");