@remnic/core 9.50.0 → 9.50.1

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 (357) hide show
  1. package/dist/access-admin-ops-surface.d.ts +10 -10
  2. package/dist/access-admin-ops-surface.js +15 -15
  3. package/dist/access-authorization-probe.d.ts +10 -10
  4. package/dist/access-authorization-probe.js +16 -16
  5. package/dist/access-boundary.d.ts +10 -10
  6. package/dist/access-boundary.js +15 -15
  7. package/dist/access-cli.js +39 -39
  8. package/dist/access-coding-context-resolution.d.ts +1 -1
  9. package/dist/access-extraction-force-flush.d.ts +10 -10
  10. package/dist/access-extraction-force-flush.js +15 -15
  11. package/dist/access-health-types.d.ts +1 -1
  12. package/dist/access-http-lcm-compaction.d.ts +10 -10
  13. package/dist/access-http-lifecycle-flush.d.ts +10 -10
  14. package/dist/access-http-offline-stream.d.ts +10 -10
  15. package/dist/access-http-query.js +15 -15
  16. package/dist/access-http.d.ts +10 -10
  17. package/dist/access-http.js +21 -21
  18. package/dist/access-identity-continuity-surface.d.ts +8 -8
  19. package/dist/access-identity-continuity-surface.js +15 -15
  20. package/dist/access-lcm-surface.d.ts +10 -10
  21. package/dist/access-lcm-surface.js +15 -15
  22. package/dist/access-mcp.d.ts +10 -10
  23. package/dist/access-mcp.js +19 -19
  24. package/dist/access-memory-search-fanout.d.ts +2 -2
  25. package/dist/{access-namespace-preflight-DuDU301-.d.ts → access-namespace-preflight-Dj2QolFE.d.ts} +1 -1
  26. package/dist/access-namespace-preflight.d.ts +3 -3
  27. package/dist/access-namespace-preflight.js +15 -15
  28. package/dist/access-observe-write-surface.d.ts +10 -10
  29. package/dist/access-observe-write-surface.js +15 -15
  30. package/dist/access-offline-manifest.d.ts +8 -8
  31. package/dist/access-operations-batch.js +16 -16
  32. package/dist/access-operations.d.ts +10 -10
  33. package/dist/access-operations.js +18 -18
  34. package/dist/access-recall-concurrency.d.ts +10 -10
  35. package/dist/access-recall-concurrency.js +15 -15
  36. package/dist/access-recall-response.d.ts +10 -10
  37. package/dist/access-recall-response.js +15 -15
  38. package/dist/access-recall-surface.d.ts +10 -10
  39. package/dist/access-recall-surface.js +15 -15
  40. package/dist/{access-service--_3T_v23.d.ts → access-service-DesbnTSa.d.ts} +7 -7
  41. package/dist/access-service-helpers.d.ts +10 -10
  42. package/dist/access-service.d.ts +10 -10
  43. package/dist/access-service.js +15 -15
  44. package/dist/access-surface-catalog.d.ts +10 -10
  45. package/dist/access-wearables-meetings-surface.d.ts +3 -3
  46. package/dist/action-confidence.d.ts +1 -1
  47. package/dist/active-memory-bridge.d.ts +1 -1
  48. package/dist/active-recall.d.ts +1 -1
  49. package/dist/ambient-provenance.d.ts +1 -1
  50. package/dist/artifact-search.d.ts +1 -1
  51. package/dist/behavior-learner.d.ts +1 -1
  52. package/dist/behavior-signals.d.ts +1 -1
  53. package/dist/bootstrap.d.ts +8 -8
  54. package/dist/briefing.d.ts +2 -2
  55. package/dist/briefing.js +6 -6
  56. package/dist/buffer-surprise-report.d.ts +1 -1
  57. package/dist/buffer-turn-helpers.d.ts +1 -1
  58. package/dist/buffer.d.ts +2 -2
  59. package/dist/bulk-import/index.d.ts +3 -3
  60. package/dist/calibration.d.ts +1 -1
  61. package/dist/capabilities.d.ts +1 -1
  62. package/dist/{capsule-merge-KXTRZ23Z.js → capsule-merge-XMFBXVDP.js} +4 -4
  63. package/dist/{catalog-DjUmUIal.d.ts → catalog-D-G7RgXR.d.ts} +1 -1
  64. package/dist/causal-behavior.d.ts +1 -1
  65. package/dist/causal-consolidation.d.ts +1 -1
  66. package/dist/causal-consolidation.js +7 -7
  67. package/dist/causal-trajectory-graph.d.ts +1 -1
  68. package/dist/{chunk-N3CXAYPJ.js → chunk-2HGUDBRJ.js} +2 -2
  69. package/dist/{chunk-FV7YQTN3.js → chunk-2S72LWKX.js} +2 -2
  70. package/dist/{chunk-NKL25FCF.js → chunk-4MBPIL4V.js} +2 -2
  71. package/dist/{chunk-ZN4EYMKJ.js → chunk-67QG75MJ.js} +2 -2
  72. package/dist/{chunk-JI62YGPI.js → chunk-6QPHF6RR.js} +2 -2
  73. package/dist/{chunk-4EABKPZS.js → chunk-75XOWNRS.js} +4 -4
  74. package/dist/{chunk-O76MWTVS.js → chunk-7BAYBYY5.js} +2 -2
  75. package/dist/{chunk-4JWJMRPJ.js → chunk-7PKFPLLZ.js} +2 -2
  76. package/dist/{chunk-24BKGKGO.js → chunk-BOFVT6P3.js} +5 -5
  77. package/dist/{chunk-3LD4N2TF.js → chunk-D73B6HMN.js} +67 -81
  78. package/dist/chunk-D73B6HMN.js.map +1 -0
  79. package/dist/{chunk-SHYIMIX5.js → chunk-ESG7Z255.js} +2 -2
  80. package/dist/{chunk-P26YHAQH.js → chunk-EWMTOVMY.js} +51 -27
  81. package/dist/chunk-EWMTOVMY.js.map +1 -0
  82. package/dist/{chunk-WPZX7MEA.js → chunk-FXX6RQYC.js} +3 -3
  83. package/dist/{chunk-S75UQ3T3.js → chunk-GDDGZVFI.js} +2 -2
  84. package/dist/{chunk-S2SJEOTM.js → chunk-GFIKG4FR.js} +2 -2
  85. package/dist/{chunk-7NDM2PNK.js → chunk-GZICBRWK.js} +2 -2
  86. package/dist/{chunk-LQKXVQZR.js → chunk-HS4M6BZM.js} +5 -5
  87. package/dist/{chunk-7BLWBSRL.js → chunk-IB25FBMX.js} +2 -2
  88. package/dist/{chunk-GKEYXXKX.js → chunk-IXXABW5A.js} +2 -2
  89. package/dist/{chunk-GGBSF2X7.js → chunk-JB6D7EFB.js} +2 -2
  90. package/dist/{chunk-T35UJI5R.js → chunk-JFNRA7YJ.js} +18 -18
  91. package/dist/{chunk-E633OXMH.js → chunk-JPHEOE6Z.js} +8 -8
  92. package/dist/{chunk-ELQCS47S.js → chunk-LOW66JRO.js} +3 -3
  93. package/dist/{chunk-JDCQK3C5.js → chunk-M4A44LUA.js} +2 -2
  94. package/dist/{chunk-RBPH3GZV.js → chunk-NZ6UDRUA.js} +26 -29
  95. package/dist/chunk-NZ6UDRUA.js.map +1 -0
  96. package/dist/{chunk-PWLB4TRK.js → chunk-OQAL6SIR.js} +2 -2
  97. package/dist/{chunk-6XGEBQ3R.js → chunk-P7ADRREJ.js} +3 -3
  98. package/dist/{chunk-EURCX6PS.js → chunk-PWN32TAG.js} +2 -2
  99. package/dist/{chunk-P4QOYG52.js → chunk-QN7NTKRC.js} +1 -1
  100. package/dist/{chunk-ZJIFYUJI.js → chunk-SV3PSA7Y.js} +2 -2
  101. package/dist/{chunk-7VWZ4LRW.js → chunk-T6RATUV2.js} +2 -2
  102. package/dist/{chunk-GSEPRIZC.js → chunk-TS4AJ2GI.js} +2 -2
  103. package/dist/{chunk-PWIMF7LP.js → chunk-UWIDGVQM.js} +10 -10
  104. package/dist/{chunk-VFS3MK5F.js → chunk-V5H6OJCR.js} +2 -2
  105. package/dist/{chunk-4S3ECH3U.js → chunk-VJXY5ZCT.js} +2 -2
  106. package/dist/{chunk-PLCHU56K.js → chunk-YQ4IQ2GA.js} +2 -2
  107. package/dist/{chunk-LTOAJXZB.js → chunk-Z3RHPGSY.js} +3 -3
  108. package/dist/{chunk-QWLW3VQH.js → chunk-ZCO5F5FW.js} +2 -2
  109. package/dist/{chunk-5XCN4XBU.js → chunk-ZQOCRIGA.js} +3 -3
  110. package/dist/{cli-BLNmNEMT.d.ts → cli-D4vvtxjY.d.ts} +5 -5
  111. package/dist/cli.d.ts +12 -12
  112. package/dist/cli.js +32 -32
  113. package/dist/coding/pre-action-gate.d.ts +1 -1
  114. package/dist/compounding/engine.d.ts +2 -2
  115. package/dist/compounding/engine.js +6 -6
  116. package/dist/compounding/preference-consolidator.d.ts +1 -1
  117. package/dist/compression-optimizer.d.ts +1 -1
  118. package/dist/config.d.ts +1 -1
  119. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  120. package/dist/connectors/codex-materialize-runner.js +6 -6
  121. package/dist/connectors/codex-materialize.d.ts +1 -1
  122. package/dist/connectors/index.d.ts +1 -1
  123. package/dist/connectors/index.js +6 -6
  124. package/dist/consolidation-provenance-check.d.ts +2 -2
  125. package/dist/consolidation-provenance-check.js +4 -4
  126. package/dist/consolidation-undo.d.ts +2 -2
  127. package/dist/consolidation-undo.js +3 -3
  128. package/dist/contradiction/index.d.ts +2 -2
  129. package/dist/converge-config.d.ts +1 -1
  130. package/dist/convergence-refresh.d.ts +2 -2
  131. package/dist/convergence-refresh.js +8 -8
  132. package/dist/conversation-index/backend.d.ts +1 -1
  133. package/dist/conversation-index/chunker.d.ts +1 -1
  134. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  135. package/dist/conversation-index/indexer.d.ts +1 -1
  136. package/dist/conversation-index/search.d.ts +1 -1
  137. package/dist/corpus-watermark.d.ts +1 -1
  138. package/dist/corpus-watermark.js +8 -8
  139. package/dist/day-summary.d.ts +1 -1
  140. package/dist/delinearize.d.ts +1 -1
  141. package/dist/dependency-propagation-config.d.ts +1 -1
  142. package/dist/direct-answer-wiring.d.ts +1 -1
  143. package/dist/direct-answer.d.ts +1 -1
  144. package/dist/embedding-fallback.d.ts +1 -1
  145. package/dist/enrichment/index.d.ts +1 -1
  146. package/dist/entity-retrieval.d.ts +2 -2
  147. package/dist/entity-retrieval.js +6 -6
  148. package/dist/entity-schema.d.ts +1 -1
  149. package/dist/explicit-capture.d.ts +8 -8
  150. package/dist/explicit-capture.js +4 -4
  151. package/dist/external-wiki-access.d.ts +10 -10
  152. package/dist/external-wiki-access.js +16 -16
  153. package/dist/external-wiki-collection-registration.d.ts +1 -1
  154. package/dist/external-wiki-collection.d.ts +1 -1
  155. package/dist/external-wiki-mcp-tools.d.ts +10 -10
  156. package/dist/extraction-error-classification.d.ts +1 -1
  157. package/dist/extraction-faithfulness.d.ts +1 -1
  158. package/dist/extraction-judge-telemetry.d.ts +1 -1
  159. package/dist/extraction-judge-training.d.ts +1 -1
  160. package/dist/extraction-judge.d.ts +1 -1
  161. package/dist/extraction-liveness.d.ts +1 -1
  162. package/dist/extraction-normalization.d.ts +1 -1
  163. package/dist/extraction-prompt.d.ts +1 -1
  164. package/dist/extraction-source-grounding-rules.d.ts +1 -1
  165. package/dist/extraction-source-grounding.d.ts +1 -1
  166. package/dist/extraction.d.ts +17 -18
  167. package/dist/extraction.js +7 -7
  168. package/dist/fallback-llm.d.ts +1 -1
  169. package/dist/{forget-GJ42LCBW.js → forget-UJDJHG6R.js} +2 -2
  170. package/dist/graph-dashboard-diff.d.ts +1 -1
  171. package/dist/graph-dashboard-key.d.ts +1 -1
  172. package/dist/graph-dashboard-parser.d.ts +1 -1
  173. package/dist/graph-edge-reinforcement.d.ts +1 -1
  174. package/dist/graph-snapshot.d.ts +1 -1
  175. package/dist/graph.d.ts +1 -1
  176. package/dist/harmonic-retrieval.js +1 -1
  177. package/dist/importance.d.ts +1 -1
  178. package/dist/importers/index.d.ts +1 -1
  179. package/dist/in-flight-reads.d.ts +1 -1
  180. package/dist/index.d.ts +19 -19
  181. package/dist/index.js +39 -39
  182. package/dist/intent.d.ts +1 -1
  183. package/dist/lcm/engine.d.ts +1 -1
  184. package/dist/lcm/index.d.ts +1 -1
  185. package/dist/lcm/tools.d.ts +1 -1
  186. package/dist/lifecycle.d.ts +1 -1
  187. package/dist/live-connectors-runner.d.ts +1 -1
  188. package/dist/local-llm.d.ts +1 -1
  189. package/dist/local-model-endpoint.d.ts +1 -1
  190. package/dist/maintenance/memory-governance.d.ts +1 -1
  191. package/dist/maintenance/memory-governance.js +6 -6
  192. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  193. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +6 -6
  194. package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
  195. package/dist/maintenance/rebuild-memory-projection.js +7 -7
  196. package/dist/{maintenance-DKbKZi3u.d.ts → maintenance-ClmUVpGI.d.ts} +3 -3
  197. package/dist/mcp-memory-inspector-app.d.ts +10 -10
  198. package/dist/memory-action-policy.d.ts +1 -1
  199. package/dist/memory-cache.d.ts +1 -1
  200. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  201. package/dist/memory-projection-store.d.ts +1 -1
  202. package/dist/memory-provenance.d.ts +1 -1
  203. package/dist/memory-snapshot.d.ts +1 -1
  204. package/dist/memory-worth-outcomes.d.ts +2 -2
  205. package/dist/models-json.d.ts +1 -1
  206. package/dist/namespaces/migrate.d.ts +3 -3
  207. package/dist/namespaces/migrate.js +7 -7
  208. package/dist/namespaces/principal.d.ts +1 -1
  209. package/dist/namespaces/search.d.ts +1 -1
  210. package/dist/namespaces/storage.d.ts +3 -3
  211. package/dist/namespaces/storage.js +6 -6
  212. package/dist/native-knowledge.d.ts +1 -1
  213. package/dist/offline-sync-impression-drain.d.ts +1 -1
  214. package/dist/operator-doctor-corpus.d.ts +1 -1
  215. package/dist/operator-doctor-corpus.js +9 -9
  216. package/dist/operator-doctor-replica.d.ts +1 -1
  217. package/dist/operator-toolkit.d.ts +3 -3
  218. package/dist/operator-toolkit.js +14 -14
  219. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  220. package/dist/orchestration/maintenance.d.ts +4 -4
  221. package/dist/orchestration/maintenance.js +11 -11
  222. package/dist/{orchestrator-BRwD7ceU.d.ts → orchestrator-qJ07zI8H.d.ts} +8 -12
  223. package/dist/orchestrator.d.ts +8 -8
  224. package/dist/orchestrator.js +39 -39
  225. package/dist/page-versioning.js +3 -3
  226. package/dist/patterns-cli.d.ts +1 -1
  227. package/dist/{pipeline-DC-1C9T8.d.ts → pipeline-WFCxhHOn.d.ts} +1 -1
  228. package/dist/policy-runtime.d.ts +1 -1
  229. package/dist/proactive-contention.d.ts +1 -1
  230. package/dist/provenance.d.ts +1 -1
  231. package/dist/{qmd-DdeCEZdb.d.ts → qmd-5tzkQ44y.d.ts} +1 -1
  232. package/dist/qmd-preflight.d.ts +2 -2
  233. package/dist/qmd-recall-cache.d.ts +1 -1
  234. package/dist/qmd.d.ts +2 -2
  235. package/dist/recall-concurrency-config.d.ts +1 -1
  236. package/dist/recall-disclosure-escalation.d.ts +1 -1
  237. package/dist/recall-explain-renderer.d.ts +1 -1
  238. package/dist/recall-memory-map.d.ts +1 -1
  239. package/dist/recall-planner-llm.d.ts +1 -1
  240. package/dist/recall-state.d.ts +1 -1
  241. package/dist/recall-tag-filter.d.ts +1 -1
  242. package/dist/recall-timings.d.ts +1 -1
  243. package/dist/recall-xray-cli.d.ts +1 -1
  244. package/dist/recall-xray-renderer.d.ts +1 -1
  245. package/dist/recall-xray.d.ts +1 -1
  246. package/dist/reconcile/cursor.d.ts +1 -1
  247. package/dist/reconcile/manifest.d.ts +1 -1
  248. package/dist/reconcile/plan.d.ts +1 -1
  249. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  250. package/dist/replay/normalizers/claude.d.ts +1 -1
  251. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  252. package/dist/replay/normalizers/shared.d.ts +1 -1
  253. package/dist/replay/runner.d.ts +1 -1
  254. package/dist/replay/types.d.ts +1 -1
  255. package/dist/replica-divergence.d.ts +1 -1
  256. package/dist/replica-peers-config.d.ts +1 -1
  257. package/dist/resolve-auth-token.d.ts +1 -1
  258. package/dist/retrieval-agents.d.ts +2 -2
  259. package/dist/retrieval-tiers.d.ts +1 -1
  260. package/dist/routing/engine.d.ts +1 -1
  261. package/dist/routing/store.d.ts +1 -1
  262. package/dist/salvage-envelope.d.ts +1 -1
  263. package/dist/{scope-profiles-BXVq4Jv0.d.ts → scope-profiles--vfXPl3X.d.ts} +1 -1
  264. package/dist/search/embed-helper.d.ts +1 -1
  265. package/dist/search/factory.d.ts +1 -1
  266. package/dist/search/index.d.ts +1 -1
  267. package/dist/search/lancedb-backend.d.ts +1 -1
  268. package/dist/search/meilisearch-backend.d.ts +1 -1
  269. package/dist/search/noop-backend.d.ts +1 -1
  270. package/dist/search/orama-backend.d.ts +1 -1
  271. package/dist/search/port.d.ts +1 -1
  272. package/dist/search/remote-backend.d.ts +1 -1
  273. package/dist/{semantic-consolidation-CGYxfa8F.d.ts → semantic-consolidation-BBDuP9nJ.d.ts} +1 -1
  274. package/dist/semantic-consolidation.d.ts +2 -2
  275. package/dist/semantic-consolidation.js +7 -7
  276. package/dist/semantic-rule-promotion.js +6 -6
  277. package/dist/semantic-rule-verifier.d.ts +1 -1
  278. package/dist/semantic-rule-verifier.js +6 -6
  279. package/dist/{service-CBDBQFCN.d.ts → service-B8DtbpCM.d.ts} +2 -2
  280. package/dist/session-observer-bands.d.ts +1 -1
  281. package/dist/session-observer-state.d.ts +1 -1
  282. package/dist/shared-context/manager.d.ts +1 -1
  283. package/dist/signal.d.ts +1 -1
  284. package/dist/source-agent-qualifier.d.ts +1 -1
  285. package/dist/source-agent-qualifier.js +6 -6
  286. package/dist/{storage-V--MQi7w.d.ts → storage-BkYN1RIy.d.ts} +1 -1
  287. package/dist/storage.d.ts +2 -2
  288. package/dist/storage.js +5 -5
  289. package/dist/summarizer.d.ts +1 -1
  290. package/dist/summary-snapshot.d.ts +1 -1
  291. package/dist/temporal-supersession.d.ts +2 -2
  292. package/dist/temporal-supersession.js +1 -1
  293. package/dist/temporal-timeline-recall.d.ts +1 -1
  294. package/dist/temporal-validity.d.ts +1 -1
  295. package/dist/threading.d.ts +1 -1
  296. package/dist/tier-migration.d.ts +2 -2
  297. package/dist/tier-routing.d.ts +1 -1
  298. package/dist/topics.d.ts +1 -1
  299. package/dist/transcript.d.ts +1 -1
  300. package/dist/transfer/capsule-import.js +4 -4
  301. package/dist/trust-score-stage.d.ts +1 -1
  302. package/dist/trust-score.d.ts +1 -1
  303. package/dist/{types-D_a6Sqjz.d.ts → types-u2s9j1uD.d.ts} +1 -1
  304. package/dist/types.d.ts +1 -1
  305. package/dist/utility-runtime.d.ts +1 -1
  306. package/dist/verified-recall.js +6 -6
  307. package/dist/write-envelope.d.ts +1 -1
  308. package/package.json +2 -2
  309. package/src/dependency-revalidation-routing.test.ts +165 -0
  310. package/src/extraction.ts +46 -31
  311. package/src/orchestration/dependency-propagation.test.ts +89 -8
  312. package/src/orchestration/dependency-propagation.ts +60 -27
  313. package/src/orchestration/dependency-revalidation.test.ts +119 -0
  314. package/src/orchestration/dependency-revalidation.ts +31 -82
  315. package/src/orchestrator.ts +5 -7
  316. package/src/testing/subjects/dependency-propagation.test.ts +367 -0
  317. package/dist/chunk-3LD4N2TF.js.map +0 -1
  318. package/dist/chunk-P26YHAQH.js.map +0 -1
  319. package/dist/chunk-RBPH3GZV.js.map +0 -1
  320. /package/dist/{capsule-merge-KXTRZ23Z.js.map → capsule-merge-XMFBXVDP.js.map} +0 -0
  321. /package/dist/{chunk-N3CXAYPJ.js.map → chunk-2HGUDBRJ.js.map} +0 -0
  322. /package/dist/{chunk-FV7YQTN3.js.map → chunk-2S72LWKX.js.map} +0 -0
  323. /package/dist/{chunk-NKL25FCF.js.map → chunk-4MBPIL4V.js.map} +0 -0
  324. /package/dist/{chunk-ZN4EYMKJ.js.map → chunk-67QG75MJ.js.map} +0 -0
  325. /package/dist/{chunk-JI62YGPI.js.map → chunk-6QPHF6RR.js.map} +0 -0
  326. /package/dist/{chunk-4EABKPZS.js.map → chunk-75XOWNRS.js.map} +0 -0
  327. /package/dist/{chunk-O76MWTVS.js.map → chunk-7BAYBYY5.js.map} +0 -0
  328. /package/dist/{chunk-4JWJMRPJ.js.map → chunk-7PKFPLLZ.js.map} +0 -0
  329. /package/dist/{chunk-24BKGKGO.js.map → chunk-BOFVT6P3.js.map} +0 -0
  330. /package/dist/{chunk-SHYIMIX5.js.map → chunk-ESG7Z255.js.map} +0 -0
  331. /package/dist/{chunk-WPZX7MEA.js.map → chunk-FXX6RQYC.js.map} +0 -0
  332. /package/dist/{chunk-S75UQ3T3.js.map → chunk-GDDGZVFI.js.map} +0 -0
  333. /package/dist/{chunk-S2SJEOTM.js.map → chunk-GFIKG4FR.js.map} +0 -0
  334. /package/dist/{chunk-7NDM2PNK.js.map → chunk-GZICBRWK.js.map} +0 -0
  335. /package/dist/{chunk-LQKXVQZR.js.map → chunk-HS4M6BZM.js.map} +0 -0
  336. /package/dist/{chunk-7BLWBSRL.js.map → chunk-IB25FBMX.js.map} +0 -0
  337. /package/dist/{chunk-GKEYXXKX.js.map → chunk-IXXABW5A.js.map} +0 -0
  338. /package/dist/{chunk-GGBSF2X7.js.map → chunk-JB6D7EFB.js.map} +0 -0
  339. /package/dist/{chunk-T35UJI5R.js.map → chunk-JFNRA7YJ.js.map} +0 -0
  340. /package/dist/{chunk-E633OXMH.js.map → chunk-JPHEOE6Z.js.map} +0 -0
  341. /package/dist/{chunk-ELQCS47S.js.map → chunk-LOW66JRO.js.map} +0 -0
  342. /package/dist/{chunk-JDCQK3C5.js.map → chunk-M4A44LUA.js.map} +0 -0
  343. /package/dist/{chunk-PWLB4TRK.js.map → chunk-OQAL6SIR.js.map} +0 -0
  344. /package/dist/{chunk-6XGEBQ3R.js.map → chunk-P7ADRREJ.js.map} +0 -0
  345. /package/dist/{chunk-EURCX6PS.js.map → chunk-PWN32TAG.js.map} +0 -0
  346. /package/dist/{chunk-P4QOYG52.js.map → chunk-QN7NTKRC.js.map} +0 -0
  347. /package/dist/{chunk-ZJIFYUJI.js.map → chunk-SV3PSA7Y.js.map} +0 -0
  348. /package/dist/{chunk-7VWZ4LRW.js.map → chunk-T6RATUV2.js.map} +0 -0
  349. /package/dist/{chunk-GSEPRIZC.js.map → chunk-TS4AJ2GI.js.map} +0 -0
  350. /package/dist/{chunk-PWIMF7LP.js.map → chunk-UWIDGVQM.js.map} +0 -0
  351. /package/dist/{chunk-VFS3MK5F.js.map → chunk-V5H6OJCR.js.map} +0 -0
  352. /package/dist/{chunk-4S3ECH3U.js.map → chunk-VJXY5ZCT.js.map} +0 -0
  353. /package/dist/{chunk-PLCHU56K.js.map → chunk-YQ4IQ2GA.js.map} +0 -0
  354. /package/dist/{chunk-LTOAJXZB.js.map → chunk-Z3RHPGSY.js.map} +0 -0
  355. /package/dist/{chunk-QWLW3VQH.js.map → chunk-ZCO5F5FW.js.map} +0 -0
  356. /package/dist/{chunk-5XCN4XBU.js.map → chunk-ZQOCRIGA.js.map} +0 -0
  357. /package/dist/{forget-GJ42LCBW.js.map → forget-UJDJHG6R.js.map} +0 -0
@@ -0,0 +1,165 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import { parseConfig } from "./config.js";
4
+ import { ExtractionEngine } from "./extraction.js";
5
+
6
+ type TestMessage = { role: "system" | "user" | "assistant"; content: string };
7
+ type TestOptions = { maxTokens?: number; signal?: AbortSignal };
8
+
9
+ const superseded = { id: "support-old", content: "The service uses port 4000." };
10
+ const replacement = { id: "support-new", content: "The service uses port 5000." };
11
+ const dependents = [{ id: "dependent", category: "fact", content: "Port 4000 must be open." }];
12
+ const response = {
13
+ content: JSON.stringify({
14
+ verdicts: [{ memoryId: "dependent", verdict: "invalidated", reason: "The port changed." }],
15
+ }),
16
+ };
17
+
18
+ function assertInvalidated(result: Awaited<ReturnType<ExtractionEngine["revalidateDependents"]>>): void {
19
+ assert.deepEqual(result, {
20
+ verdicts: [{ memoryId: "dependent", verdict: "invalidated", reason: "The port changed." }],
21
+ });
22
+ }
23
+
24
+ test("revalidation keeps the official OpenAI base URL on the Responses API", async () => {
25
+ const engine = new ExtractionEngine(
26
+ parseConfig({ openaiApiKey: "fixture-key", openaiBaseUrl: "https://api.openai.com/v1" }),
27
+ );
28
+ const signal = new AbortController().signal;
29
+ let calls = 0;
30
+ let request: Record<string, unknown> | undefined;
31
+ let requestOptions: { signal?: AbortSignal } | undefined;
32
+ Reflect.set(engine, "client", {
33
+ baseURL: "https://api.openai.com/v1",
34
+ responses: {
35
+ async create(body: Record<string, unknown>, options: { signal?: AbortSignal }) {
36
+ calls += 1;
37
+ request = body;
38
+ requestOptions = options;
39
+ return { output_text: response.content };
40
+ },
41
+ },
42
+ });
43
+ assertInvalidated(await engine.revalidateDependents(superseded, replacement, dependents, signal));
44
+ assert.equal(calls, 1);
45
+ assert.equal(request?.model, "gpt-5.5");
46
+ assert.equal(request?.max_output_tokens, 1024);
47
+ assert.match(String(request?.instructions), /revalidate dependent memory claims/i);
48
+ assert.match(String(request?.input), /SUPERSEDED MEMORY.*support-old/s);
49
+ assert.equal(requestOptions?.signal, signal);
50
+ });
51
+
52
+ test("revalidation uses the client's effective custom base URL", async () => {
53
+ const engine = new ExtractionEngine(
54
+ parseConfig({
55
+ openaiApiKey: "fixture-key",
56
+ openaiBaseUrl: "https://api.openai.com/v1",
57
+ }),
58
+ );
59
+ const signal = new AbortController().signal;
60
+ let chatRequest: Record<string, unknown> | undefined;
61
+ let chatOptions: { signal?: AbortSignal } | undefined;
62
+ let responsesCalls = 0;
63
+ Reflect.set(engine, "client", {
64
+ baseURL: "https://inference.example.test/v1",
65
+ chat: {
66
+ completions: {
67
+ async create(body: Record<string, unknown>, options: { signal?: AbortSignal }) {
68
+ chatRequest = body;
69
+ chatOptions = options;
70
+ return { choices: [{ message: response }] };
71
+ },
72
+ },
73
+ },
74
+ responses: {
75
+ async create() {
76
+ responsesCalls += 1;
77
+ throw new Error("custom endpoint does not implement /responses");
78
+ },
79
+ },
80
+ });
81
+
82
+ assertInvalidated(await engine.revalidateDependents(superseded, replacement, dependents, signal));
83
+ assert.equal(responsesCalls, 0);
84
+ assert.equal(chatRequest?.model, "gpt-5.5");
85
+ assert.equal(chatRequest?.max_tokens, 1024);
86
+ assert.ok(Array.isArray(chatRequest?.messages));
87
+ assert.equal(chatOptions?.signal, signal);
88
+ });
89
+
90
+ test("revalidation falls back to the direct route when the injected fast route rejects", async () => {
91
+ const engine = new ExtractionEngine(
92
+ parseConfig({ openaiApiKey: "fixture-key", openaiBaseUrl: "https://api.openai.com/v1" }),
93
+ undefined,
94
+ undefined,
95
+ undefined,
96
+ undefined,
97
+ async () => null,
98
+ );
99
+ const signal = new AbortController().signal;
100
+ let fastCalls = 0;
101
+ let directCalls = 0;
102
+ let fastMessages: TestMessage[] | undefined;
103
+ let fastOptions: TestOptions | undefined;
104
+ let request: Record<string, unknown> | undefined;
105
+ let requestOptions: { signal?: AbortSignal } | undefined;
106
+ Reflect.set(engine, "fastChatCompletion", async (messages: TestMessage[], options: TestOptions) => {
107
+ fastCalls += 1;
108
+ fastMessages = messages;
109
+ fastOptions = options;
110
+ throw new Error("fast route unavailable");
111
+ });
112
+ Reflect.set(engine, "client", {
113
+ baseURL: "https://api.openai.com/v1",
114
+ responses: {
115
+ async create(body: Record<string, unknown>, options: { signal?: AbortSignal }) {
116
+ directCalls += 1;
117
+ request = body;
118
+ requestOptions = options;
119
+ return { output_text: response.content };
120
+ },
121
+ },
122
+ });
123
+
124
+ assertInvalidated(await engine.revalidateDependents(superseded, replacement, dependents, signal));
125
+ assert.equal(fastCalls, 1);
126
+ assert.equal(directCalls, 1);
127
+ assert.equal(fastMessages?.[0]?.role, "system");
128
+ assert.match(fastMessages?.[1]?.content ?? "", /support-new/);
129
+ assert.equal(fastOptions?.maxTokens, 1024);
130
+ assert.equal(fastOptions?.signal, signal);
131
+ assert.equal(request?.max_output_tokens, 1024);
132
+ assert.equal(requestOptions?.signal, signal);
133
+ });
134
+
135
+ test("revalidation preserves the gateway fallback route for the legacy constructor", async () => {
136
+ const engine = new ExtractionEngine(parseConfig({ modelSource: "gateway" }));
137
+ const signal = new AbortController().signal;
138
+ let fastCalls = 0;
139
+ let fallbackCalls = 0;
140
+ let fallbackMessages: TestMessage[] | undefined;
141
+ let fallbackOptions: TestOptions | undefined;
142
+ Reflect.set(engine, "fastChatCompletion", async () => {
143
+ fastCalls += 1;
144
+ return null;
145
+ });
146
+ Reflect.set(engine, "fallbackLlm", {
147
+ async chatCompletion(
148
+ messages: TestMessage[],
149
+ options: TestOptions,
150
+ ) {
151
+ fallbackCalls += 1;
152
+ fallbackMessages = messages;
153
+ fallbackOptions = options;
154
+ return response;
155
+ },
156
+ });
157
+
158
+ assertInvalidated(await engine.revalidateDependents(superseded, replacement, dependents, signal));
159
+ assert.equal(fastCalls, 1);
160
+ assert.equal(fallbackCalls, 1);
161
+ assert.equal(fallbackMessages?.[0]?.role, "system");
162
+ assert.match(fallbackMessages?.[1]?.content ?? "", /dependent/);
163
+ assert.equal(fallbackOptions?.maxTokens, 1024);
164
+ assert.equal(fallbackOptions?.signal, signal);
165
+ });
package/src/extraction.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import OpenAI from "openai";
2
+ import { isAbortError } from "./abort-error.js";
2
3
  import { log } from "./logger.js";
3
4
  import { delinearize } from "./delinearize.js";
4
5
  import { LocalLlmClient } from "./local-llm.js";
5
6
  import { shouldRunProactivePass } from "./proactive-contention.js";
6
7
  import { FallbackLlmClient, fallbackLlmRuntimeContextFromConfig, gatewayTaskChainOptions } from "./fallback-llm.js";
7
- import { revalidateDependentsViaLlm } from "./orchestration/dependency-revalidation.js";
8
+ import { revalidateDependentsViaLlm, type RevalidationFastChatCompletion } from "./orchestration/dependency-revalidation.js";
8
9
  import {
9
10
  ExtractionResultSchema,
10
11
  ConsolidationResultSchema,
@@ -79,7 +80,6 @@ const CONSOLIDATION_RESPONSE_SCHEMA = `{
79
80
  "entityUpdates": [{"name": "person-jane-doe", "type": "person", "facts": ["Now leads the backend team", "Recently migrated the user service to TypeScript"]}]
80
81
  }`;
81
82
 
82
-
83
83
  function extractionEntityType(value: unknown): ExtractedEntityResult["type"] | undefined {
84
84
  const type = extractionText(value);
85
85
  if (
@@ -152,6 +152,7 @@ export class ExtractionEngine {
152
152
  localLlm?: LocalLlmClient,
153
153
  gatewayConfig?: GatewayConfig,
154
154
  modelRegistry?: ModelRegistry,
155
+ private readonly fastChatCompletion?: RevalidationFastChatCompletion,
155
156
  ) {
156
157
  this.profiler = profilerArg ?? new ProfilingCollector({ enabled: false, storageDir: "/tmp/engram-profiler-disabled", maxTraces: 0 });
157
158
  if (config.openaiApiKey) {
@@ -186,26 +187,14 @@ export class ExtractionEngine {
186
187
  }
187
188
  }
188
189
 
189
- /**
190
- * Whether LLM calls should be routed through the gateway model chain
191
- * instead of the plugin's own local/OpenAI clients.
192
- */
193
190
  private get useGatewayModelSource(): boolean {
194
191
  return this.config.modelSource === "gateway";
195
192
  }
196
193
 
197
- /**
198
- * Whether the local LLM path should be attempted.
199
- * Disabled when gateway model source is active (gateway chain replaces local).
200
- */
201
194
  private get shouldUseLocalLlm(): boolean {
202
195
  return resolveLocalLlmCapabilities(this.config).localLlm && !this.useGatewayModelSource;
203
196
  }
204
197
 
205
- /**
206
- * Whether the direct OpenAI client should be used.
207
- * Disabled when gateway model source is active.
208
- */
209
198
  private get shouldUseDirectClient(): boolean {
210
199
  return !this.useGatewayModelSource && this.client !== null;
211
200
  }
@@ -2453,29 +2442,55 @@ Respond with valid JSON matching this schema:
2453
2442
  reason?: string;
2454
2443
  }>;
2455
2444
  }> {
2445
+ const complete: RevalidationFastChatCompletion = async (messages, options) => {
2446
+ try {
2447
+ const fast = await this.fastChatCompletion?.(messages, options);
2448
+ if (fast) return fast;
2449
+ } catch (error) {
2450
+ if (isAbortError(error) || options.signal?.aborted) throw error;
2451
+ log.warn(`dependency revalidation fast route failed: ${error}`);
2452
+ }
2453
+ if (this.shouldUseLocalLlm) {
2454
+ try {
2455
+ const local = await this.localLlm.chatCompletion(messages, options);
2456
+ if (local || !this.config.localLlmFallback) return local;
2457
+ } catch (error) {
2458
+ if (!this.config.localLlmFallback) throw error;
2459
+ }
2460
+ }
2461
+ if (!this.shouldUseDirectClient) {
2462
+ return this.fallbackLlm.chatCompletion(messages, this.withGatewayAgent(options));
2463
+ }
2464
+ const client = this.client;
2465
+ if (!client) return null;
2466
+ if (!shouldAssumeOpenAiChatCompletions(client.baseURL ?? this.config.openaiBaseUrl)) {
2467
+ const response = await client.chat.completions.create(
2468
+ {
2469
+ model: this.config.model,
2470
+ messages,
2471
+ max_tokens: options.maxTokens,
2472
+ },
2473
+ { signal: options.signal },
2474
+ );
2475
+ const content = response.choices[0]?.message?.content;
2476
+ return typeof content === "string" ? { content } : null;
2477
+ }
2478
+ const response = await client.responses.create({
2479
+ model: this.config.model,
2480
+ instructions: messages.find((message) => message.role === "system")?.content,
2481
+ input: messages.filter((message) => message.role !== "system").map((message) => message.content).join("\n\n"),
2482
+ max_output_tokens: options.maxTokens,
2483
+ }, { signal: options.signal });
2484
+ return typeof response.output_text === "string" ? { content: response.output_text } : null;
2485
+ };
2456
2486
  return revalidateDependentsViaLlm(
2457
- {
2458
- shouldUseLocalLlm: this.shouldUseLocalLlm,
2459
- shouldUseDirectClient: this.shouldUseDirectClient,
2460
- localLlm: this.localLlm,
2461
- fallbackLlm: this.fallbackLlm,
2462
- client: this.client,
2463
- config: this.config,
2464
- withGatewayAgent: this.withGatewayAgent.bind(this),
2465
- parseJsonObject: this.parseJsonObject,
2466
- },
2487
+ { fastChatCompletion: complete, parseJsonObject: this.parseJsonObject },
2467
2488
  superseded,
2468
2489
  replacement,
2469
2490
  dependents,
2470
- signal,
2491
+ { signal, timeoutMs: this.config.dependencyPropagation.timeoutMs },
2471
2492
  );
2472
2493
  }
2473
-
2474
-
2475
- /**
2476
- * Suggest links between a new memory and existing memories (Phase 3A).
2477
- * Called during extraction to build the knowledge graph.
2478
- */
2479
2494
  async suggestLinks(
2480
2495
  newMemory: { content: string; category: string },
2481
2496
  candidateMemories: Array<{ id: string; content: string; category: string }>,
@@ -79,7 +79,7 @@ function config(overrides: Record<string, unknown> = {}): PluginConfig {
79
79
 
80
80
  function fixture(
81
81
  initial: MemoryFile[],
82
- verdicts: Verdict[] | Error | ((signal?: AbortSignal) => Promise<never>),
82
+ verdicts: Verdict[] | Error | ((signal?: AbortSignal) => Promise<{ verdicts: Verdict[] }>),
83
83
  ): Fixture {
84
84
  const memories = new Map(initial.map((item) => [item.frontmatter.id, item]));
85
85
  const calls: Fixture["calls"] = { revalidate: [], supersede: [], frontmatter: [] };
@@ -267,13 +267,24 @@ test("disabled propagation is an immediate no-op for boolean and string false",
267
267
  },
268
268
  );
269
269
 
270
- assert.deepEqual(result, {
271
- dependentsFound: 0,
272
- invalidated: 0,
273
- stillValid: 0,
274
- uncertain: 0,
275
- skipped: "disabled",
276
- });
270
+ assert.deepEqual(
271
+ {
272
+ dependentsFound: result.dependentsFound,
273
+ invalidated: result.invalidated,
274
+ stillValid: result.stillValid,
275
+ uncertain: result.uncertain,
276
+ skipped: result.skipped,
277
+ },
278
+ {
279
+ dependentsFound: 0,
280
+ invalidated: 0,
281
+ stillValid: 0,
282
+ uncertain: 0,
283
+ skipped: "disabled",
284
+ },
285
+ );
286
+ assert.equal(result.route, null);
287
+ assert.equal(typeof result.durationMs, "number");
277
288
  assert.equal(fixtureValue.calls.revalidate.length, 0);
278
289
  }
279
290
  });
@@ -295,6 +306,12 @@ test("invalidated verdict supersedes the dependent and persists dependency front
295
306
  );
296
307
 
297
308
  assert.equal(result.invalidated, 1);
309
+ assert.equal(result.dependentsFound, 1);
310
+ assert.equal(result.stillValid, 0);
311
+ assert.equal(result.uncertain, 0);
312
+ assert.equal(result.skipped, null);
313
+ assert.equal(result.route, "fast-completion");
314
+ assert.equal(typeof result.durationMs, "number");
298
315
  assert.deepEqual(fixtureValue.calls.supersede, [
299
316
  {
300
317
  id: "dep",
@@ -387,6 +404,12 @@ test("LLM errors skip the event and perform zero writes", async () => {
387
404
  },
388
405
  );
389
406
 
407
+ assert.equal(result.dependentsFound, 1);
408
+ assert.equal(result.invalidated, 0);
409
+ assert.equal(result.stillValid, 0);
410
+ assert.equal(result.uncertain, 0);
411
+ assert.equal(result.route, "fast-completion");
412
+ assert.equal(typeof result.durationMs, "number");
390
413
  assert.equal(result.skipped, "llm_error");
391
414
  assert.equal(fixtureValue.calls.supersede.length, 0);
392
415
  assert.equal(fixtureValue.calls.frontmatter.length, 0);
@@ -411,11 +434,69 @@ test("LLM timeout aborts the batch and performs zero writes", async () => {
411
434
  },
412
435
  );
413
436
 
437
+ assert.equal(result.dependentsFound, 1);
438
+ assert.equal(result.route, "fast-completion");
439
+ assert.equal(typeof result.durationMs, "number");
414
440
  assert.equal(result.skipped, "timeout");
415
441
  assert.equal(fixtureValue.calls.supersede.length, 0);
416
442
  assert.equal(fixtureValue.calls.frontmatter.length, 0);
417
443
  });
418
444
 
445
+ test("a completion that ignores AbortSignal returns at the shared deadline", async (t) => {
446
+ t.mock.timers.enable({ apis: ["setTimeout", "Date"], now: 0 });
447
+ try {
448
+ const old = memory("old", { links: [{ targetId: "dep", linkType: "supports" }] });
449
+ const fixtureValue = fixture([old, memory("dep")], () => new Promise<never>(() => {}));
450
+ const resultPromise = propagateInvalidation(
451
+ deps(fixtureValue, { dependencyPropagation: { timeoutMs: 20 } }),
452
+ {
453
+ oldMemory: old,
454
+ replacementId: "new",
455
+ replacementContent: "replacement",
456
+ cause: "contradiction",
457
+ namespaceScope: "namespace-a",
458
+ },
459
+ );
460
+
461
+ await Promise.resolve();
462
+ t.mock.timers.tick(20);
463
+ const result = await resultPromise;
464
+
465
+ assert.equal(result.dependentsFound, 1);
466
+ assert.equal(result.invalidated, 0);
467
+ assert.equal(result.stillValid, 0);
468
+ assert.equal(result.uncertain, 0);
469
+ assert.equal(result.skipped, "timeout");
470
+ assert.equal(result.route, "fast-completion");
471
+ assert.equal(result.durationMs, 20);
472
+ assert.equal(fixtureValue.calls.supersede.length, 0);
473
+ assert.equal(fixtureValue.calls.frontmatter.length, 0);
474
+ } finally {
475
+ t.mock.timers.reset();
476
+ }
477
+ });
478
+
479
+ test("a zero timeout disables the deadline", async () => {
480
+ const old = memory("old", { links: [{ targetId: "dep", linkType: "supports" }] });
481
+ const fixtureValue = fixture([old, memory("dep")], () =>
482
+ new Promise((resolve) => setTimeout(() => resolve({
483
+ verdicts: [{ memoryId: "dep", verdict: "invalidated" }],
484
+ }), 5)),
485
+ );
486
+ const result = await propagateInvalidation(
487
+ deps(fixtureValue, { dependencyPropagation: { timeoutMs: 0 } }),
488
+ {
489
+ oldMemory: old,
490
+ replacementId: "new",
491
+ replacementContent: "replacement",
492
+ cause: "contradiction",
493
+ namespaceScope: "namespace-a",
494
+ },
495
+ );
496
+ assert.equal(result.invalidated, 1);
497
+ assert.equal(result.skipped, null);
498
+ });
499
+
419
500
  test("dryRun computes invalidation verdicts without writing", async () => {
420
501
  const old = memory("old", { links: [{ targetId: "dep", linkType: "supports" }] });
421
502
  const fixtureValue = fixture([old, memory("dep")], [{ memoryId: "dep", verdict: "invalidated" }]);
@@ -34,6 +34,8 @@ export interface PropagationResult {
34
34
  stillValid: number;
35
35
  uncertain: number;
36
36
  skipped: "disabled" | "no_dependents" | "llm_error" | "timeout" | null;
37
+ route: "fast-completion" | null;
38
+ durationMs: number;
37
39
  }
38
40
 
39
41
  function propagationConfig(config: PluginConfig): DependencyPropagationConfig {
@@ -96,8 +98,13 @@ export function findDependents(
96
98
  });
97
99
  }
98
100
 
99
- function emptyResult(skipped: PropagationResult["skipped"]): PropagationResult {
100
- return { dependentsFound: 0, invalidated: 0, stillValid: 0, uncertain: 0, skipped };
101
+ function emptyResult(
102
+ skipped: PropagationResult["skipped"],
103
+ durationMs: number,
104
+ route: PropagationResult["route"] = null,
105
+ dependentsFound = 0,
106
+ ): PropagationResult {
107
+ return { dependentsFound, invalidated: 0, stillValid: 0, uncertain: 0, skipped, route, durationMs };
101
108
  }
102
109
 
103
110
  function logResult(event: PropagationEvent, result: PropagationResult): void {
@@ -111,6 +118,8 @@ function logResult(event: PropagationEvent, result: PropagationResult): void {
111
118
  stillValid: result.stillValid,
112
119
  uncertain: result.uncertain,
113
120
  skipped: result.skipped,
121
+ route: result.route,
122
+ durationMs: result.durationMs,
114
123
  })}`,
115
124
  );
116
125
  }
@@ -136,16 +145,17 @@ export async function propagateInvalidation(
136
145
  deps: { storage: StorageManager; extraction: ExtractionEngine; config: PluginConfig },
137
146
  event: PropagationEvent,
138
147
  ): Promise<PropagationResult> {
148
+ const startedAt = Date.now();
139
149
  const config = propagationConfig(deps.config);
140
150
  if (!isEnabled(config.enabled)) {
141
- const result = emptyResult("disabled");
151
+ const result = emptyResult("disabled", Date.now() - startedAt);
142
152
  logResult(event, result);
143
153
  return result;
144
154
  }
145
155
 
146
156
  const maxDependents = config.maxDependents;
147
157
  if (maxDependents <= 0) {
148
- const result = emptyResult("no_dependents");
158
+ const result = emptyResult("no_dependents", Date.now() - startedAt);
149
159
  logResult(event, result);
150
160
  return result;
151
161
  }
@@ -155,7 +165,7 @@ export async function propagateInvalidation(
155
165
  memories = await deps.storage.readAllMemories();
156
166
  } catch (error) {
157
167
  log.warn(`dependency propagation discovery failed for ${event.oldMemory.frontmatter.id}: ${error}`);
158
- const result = emptyResult("llm_error");
168
+ const result = emptyResult("llm_error", Date.now() - startedAt);
159
169
  logResult(event, result);
160
170
  return result;
161
171
  }
@@ -171,46 +181,67 @@ export async function propagateInvalidation(
171
181
  const dependents = discovered.slice(0, Math.max(0, Math.floor(maxDependents)));
172
182
 
173
183
  if (dependents.length === 0) {
174
- const result = emptyResult("no_dependents");
184
+ const result = emptyResult("no_dependents", Date.now() - startedAt);
175
185
  logResult(event, result);
176
186
  return result;
177
187
  }
178
188
 
179
189
  const controller = new AbortController();
180
190
  const timeoutMs = config.timeoutMs;
181
- const timeout = Number.isFinite(timeoutMs) && timeoutMs > 0
182
- ? setTimeout(() => controller.abort(new PropagationTimeoutError()), timeoutMs)
191
+ let timedOut = false;
192
+ let timeout: ReturnType<typeof setTimeout> | undefined;
193
+ const deadline = Number.isFinite(timeoutMs) && timeoutMs > 0
194
+ ? new Promise<never>((_, reject) => {
195
+ timeout = setTimeout(() => {
196
+ timedOut = true;
197
+ const error = new PropagationTimeoutError();
198
+ controller.abort(error);
199
+ reject(error);
200
+ }, timeoutMs);
201
+ })
183
202
  : undefined;
203
+ const revalidation = Promise.resolve().then(() => deps.extraction.revalidateDependents(
204
+ { id: event.oldMemory.frontmatter.id, content: event.oldMemory.content },
205
+ event.replacementId && event.replacementContent !== null
206
+ ? { id: event.replacementId, content: event.replacementContent }
207
+ : null,
208
+ dependents.map((memory) => ({
209
+ id: memory.frontmatter.id,
210
+ category: memory.frontmatter.category,
211
+ content: memory.content,
212
+ })),
213
+ controller.signal,
214
+ ));
184
215
 
185
216
  let verdicts: Array<{ memoryId: string; verdict: "still_valid" | "invalidated" | "uncertain"; reason?: string }>;
186
217
  try {
187
- const response = await deps.extraction.revalidateDependents(
188
- { id: event.oldMemory.frontmatter.id, content: event.oldMemory.content },
189
- event.replacementId && event.replacementContent !== null
190
- ? { id: event.replacementId, content: event.replacementContent }
191
- : null,
192
- dependents.map((memory) => ({
193
- id: memory.frontmatter.id,
194
- category: memory.frontmatter.category,
195
- content: memory.content,
196
- })),
197
- controller.signal,
198
- );
199
- verdicts = response.verdicts;
200
- if (controller.signal.aborted) {
201
- if (timeout !== undefined) clearTimeout(timeout);
202
- const result = emptyResult("timeout");
218
+ const response = deadline
219
+ ? await Promise.race([revalidation, deadline])
220
+ : await revalidation;
221
+ if (timedOut || controller.signal.aborted) {
222
+ const result = emptyResult(
223
+ "timeout",
224
+ Date.now() - startedAt,
225
+ "fast-completion",
226
+ dependents.length,
227
+ );
203
228
  logResult(event, result);
204
229
  return result;
205
230
  }
231
+ verdicts = response.verdicts;
206
232
  } catch (error) {
207
- const result = emptyResult(isTimeoutError(error, controller.signal) ? "timeout" : "llm_error");
233
+ const result = emptyResult(
234
+ timedOut || isTimeoutError(error, controller.signal) ? "timeout" : "llm_error",
235
+ Date.now() - startedAt,
236
+ "fast-completion",
237
+ dependents.length,
238
+ );
208
239
  log.warn(`dependency propagation revalidation failed for ${event.oldMemory.frontmatter.id}: ${error}`);
209
240
  logResult(event, result);
210
- if (timeout !== undefined) clearTimeout(timeout);
211
241
  return result;
242
+ } finally {
243
+ clearTimeout(timeout!);
212
244
  }
213
- if (timeout !== undefined) clearTimeout(timeout);
214
245
 
215
246
  let invalidated = 0;
216
247
  let stillValid = 0;
@@ -257,6 +288,8 @@ export async function propagateInvalidation(
257
288
  stillValid,
258
289
  uncertain,
259
290
  skipped: null,
291
+ route: "fast-completion",
292
+ durationMs: Date.now() - startedAt,
260
293
  };
261
294
  logResult(event, result);
262
295
  return result;