@remnic/core 9.30.1 → 9.31.0

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 (493) hide show
  1. package/dist/access-admin-ops-surface.d.ts +10 -10
  2. package/dist/access-admin-ops-surface.js +50 -44
  3. package/dist/access-authorization-probe.d.ts +10 -10
  4. package/dist/access-authorization-probe.js +51 -45
  5. package/dist/access-boundary.d.ts +13 -11
  6. package/dist/access-boundary.js +50 -44
  7. package/dist/access-cli.js +168 -123
  8. package/dist/access-cli.js.map +1 -1
  9. package/dist/access-extraction-force-flush.d.ts +119 -0
  10. package/dist/access-extraction-force-flush.js +166 -0
  11. package/dist/access-health-types.d.ts +1 -1
  12. package/dist/access-http-lcm-compaction.d.ts +47 -13
  13. package/dist/access-http-lcm-compaction.js +7 -3
  14. package/dist/access-http-lifecycle-flush.d.ts +132 -0
  15. package/dist/access-http-lifecycle-flush.js +9 -0
  16. package/dist/access-http-lifecycle-flush.js.map +1 -0
  17. package/dist/access-http.d.ts +11 -10
  18. package/dist/access-http.js +66 -59
  19. package/dist/access-identity-continuity-surface.d.ts +8 -8
  20. package/dist/access-identity-continuity-surface.js +50 -44
  21. package/dist/access-lcm-surface.d.ts +10 -10
  22. package/dist/access-lcm-surface.js +50 -44
  23. package/dist/access-mcp-output-schemas.js +1 -1
  24. package/dist/access-mcp.d.ts +16 -10
  25. package/dist/access-mcp.js +58 -52
  26. package/dist/access-memory-search-fanout.d.ts +2 -2
  27. package/dist/{access-namespace-preflight-VfXssvoI.d.ts → access-namespace-preflight-D4vW97H1.d.ts} +1 -1
  28. package/dist/access-namespace-preflight.d.ts +3 -3
  29. package/dist/access-namespace-preflight.js +50 -44
  30. package/dist/access-observe-helpers.d.ts +22 -0
  31. package/dist/access-observe-helpers.js +10 -0
  32. package/dist/access-observe-helpers.js.map +1 -0
  33. package/dist/access-observe-write-surface.d.ts +51 -47
  34. package/dist/access-observe-write-surface.js +50 -44
  35. package/dist/access-operations-batch.js +53 -47
  36. package/dist/access-operations.d.ts +11 -11
  37. package/dist/access-operations.js +55 -49
  38. package/dist/access-recall-concurrency.d.ts +10 -10
  39. package/dist/access-recall-concurrency.js +50 -44
  40. package/dist/access-recall-response.d.ts +10 -10
  41. package/dist/access-recall-response.js +50 -44
  42. package/dist/access-recall-surface.d.ts +10 -10
  43. package/dist/access-recall-surface.js +50 -44
  44. package/dist/access-schema.d.ts +37 -7
  45. package/dist/access-schema.js +5 -3
  46. package/dist/{access-service-B5RwVsTV.d.ts → access-service-B6PZ_xUL.d.ts} +44 -20
  47. package/dist/access-service-helpers.d.ts +113 -0
  48. package/dist/access-service-helpers.js +8 -0
  49. package/dist/access-service-helpers.js.map +1 -0
  50. package/dist/access-service.d.ts +10 -10
  51. package/dist/access-service.js +50 -44
  52. package/dist/access-surface-catalog.d.ts +10 -10
  53. package/dist/access-surface-catalog.js +2 -0
  54. package/dist/access-surface-catalog.js.map +1 -1
  55. package/dist/access-wearables-meetings-surface.d.ts +3 -3
  56. package/dist/action-confidence.d.ts +1 -1
  57. package/dist/active-memory-bridge.d.ts +1 -1
  58. package/dist/active-recall.d.ts +1 -1
  59. package/dist/active-recall.js +1 -1
  60. package/dist/adapters/index.js +7 -7
  61. package/dist/adapters/registry.js +3 -3
  62. package/dist/behavior-learner.d.ts +1 -1
  63. package/dist/behavior-signals.d.ts +1 -1
  64. package/dist/bootstrap.d.ts +8 -8
  65. package/dist/briefing.d.ts +2 -2
  66. package/dist/briefing.js +14 -14
  67. package/dist/buffer-surprise-report.d.ts +1 -1
  68. package/dist/buffer-turn-helpers.d.ts +5 -0
  69. package/dist/buffer-turn-helpers.js +24 -0
  70. package/dist/buffer-turn-helpers.js.map +1 -0
  71. package/dist/buffer.d.ts +17 -4
  72. package/dist/buffer.js +21 -1
  73. package/dist/bulk-import/index.d.ts +3 -3
  74. package/dist/calibration.d.ts +1 -1
  75. package/dist/capabilities.d.ts +1 -1
  76. package/dist/{capsule-crypto-GWVG7LGC.js → capsule-crypto-7FJQINUR.js} +2 -2
  77. package/dist/capsule-crypto-7FJQINUR.js.map +1 -0
  78. package/dist/{capsule-merge-5KKJQBWQ.js → capsule-merge-2H2IUTBT.js} +4 -4
  79. package/dist/{catalog-V7inUQdp.d.ts → catalog-CGFYmTT8.d.ts} +1 -1
  80. package/dist/causal-behavior.d.ts +1 -1
  81. package/dist/causal-behavior.js +2 -2
  82. package/dist/causal-chain.js +2 -2
  83. package/dist/causal-consolidation.d.ts +1 -1
  84. package/dist/causal-consolidation.js +19 -19
  85. package/dist/causal-retrieval.js +2 -2
  86. package/dist/causal-trajectory-graph.d.ts +1 -1
  87. package/dist/causal-trajectory.js +1 -1
  88. package/dist/{chunk-LEPOP46W.js → chunk-227UFSEQ.js} +4 -4
  89. package/dist/{chunk-GIBMDSJ2.js → chunk-27P5YVDU.js} +2 -2
  90. package/dist/{chunk-2SA56WMI.js → chunk-2LTDWSR6.js} +4 -4
  91. package/dist/{chunk-OWHERGF2.js → chunk-2NLLXCJG.js} +2 -2
  92. package/dist/{chunk-YTSNAUXN.js → chunk-3F2LKUMU.js} +22 -8
  93. package/dist/chunk-3F2LKUMU.js.map +1 -0
  94. package/dist/chunk-3QL24R5Y.js +9 -0
  95. package/dist/chunk-3QL24R5Y.js.map +1 -0
  96. package/dist/chunk-4RNZMRAV.js +60 -0
  97. package/dist/chunk-4RNZMRAV.js.map +1 -0
  98. package/dist/{chunk-6U7F52DE.js → chunk-4XHR3I67.js} +60 -60
  99. package/dist/chunk-5IVLTFOV.js +65 -0
  100. package/dist/chunk-5IVLTFOV.js.map +1 -0
  101. package/dist/{chunk-2EUVEDF5.js → chunk-5XA7PQ5I.js} +2 -2
  102. package/dist/{chunk-ALSGMEJR.js → chunk-65ZPVYTA.js} +7 -7
  103. package/dist/{chunk-5VQJBH2G.js → chunk-6TAUHL33.js} +2 -2
  104. package/dist/{chunk-HUHX2OCX.js → chunk-7DRETLP5.js} +1 -1
  105. package/dist/chunk-7DRETLP5.js.map +1 -0
  106. package/dist/{chunk-RGIWINDN.js → chunk-7G5MH4NT.js} +30 -30
  107. package/dist/{chunk-WVO4G37W.js → chunk-7TEZUUGM.js} +3 -3
  108. package/dist/{chunk-MJCGLM25.js → chunk-7VUAG236.js} +5 -5
  109. package/dist/{chunk-M3C2V3PV.js → chunk-AJEMDQNA.js} +12 -12
  110. package/dist/{chunk-RKNJBZ55.js → chunk-AU7Q3LSC.js} +7 -7
  111. package/dist/{chunk-ACXPX4AG.js → chunk-BR2K43MK.js} +2 -2
  112. package/dist/{chunk-IYZ5K7MU.js → chunk-BUE5E2HL.js} +4 -187
  113. package/dist/chunk-BUE5E2HL.js.map +1 -0
  114. package/dist/{chunk-CZRYLSSW.js → chunk-BUGVGH4N.js} +2 -2
  115. package/dist/{chunk-YTQNQ4KB.js → chunk-C4VQMFVM.js} +3 -3
  116. package/dist/{chunk-ZPSVBSOF.js → chunk-CIVDJD6P.js} +3 -3
  117. package/dist/{chunk-K5GB36ME.js → chunk-CZDOT5I2.js} +2 -2
  118. package/dist/{chunk-VI2JQPNQ.js → chunk-E4PWACUN.js} +2 -2
  119. package/dist/{chunk-D2ANTDGO.js → chunk-E6BMSH7U.js} +2 -2
  120. package/dist/{chunk-P5PXWTW7.js → chunk-EJCAV3QZ.js} +3 -3
  121. package/dist/{chunk-CKMO4RDY.js → chunk-EMDLM3LW.js} +4 -4
  122. package/dist/chunk-ER7W6H4T.js +112 -0
  123. package/dist/chunk-ER7W6H4T.js.map +1 -0
  124. package/dist/{chunk-6FUPCJUL.js → chunk-FI4F5X2V.js} +54 -54
  125. package/dist/chunk-FI4F5X2V.js.map +1 -0
  126. package/dist/{chunk-LGHKJABJ.js → chunk-GOUILH55.js} +40 -18
  127. package/dist/chunk-GOUILH55.js.map +1 -0
  128. package/dist/{chunk-G5R3D5UP.js → chunk-GTTAHHD3.js} +2 -2
  129. package/dist/{chunk-H5CPS5JP.js → chunk-H36LJIPD.js} +3 -3
  130. package/dist/{chunk-AITCS2RC.js → chunk-IJ7HK452.js} +4 -4
  131. package/dist/{chunk-D3LMYTBV.js → chunk-J7WZUL2K.js} +9 -9
  132. package/dist/{chunk-5W4ZLBEZ.js → chunk-JCFTIQBR.js} +2 -2
  133. package/dist/{chunk-XOZ4ALGB.js → chunk-KE353EWI.js} +3 -3
  134. package/dist/{chunk-W5EG6LE3.js → chunk-KIT6M7EB.js} +3 -3
  135. package/dist/{chunk-5W7CTZ2E.js → chunk-KQAFEZQX.js} +4 -4
  136. package/dist/{chunk-Z54EM5YZ.js → chunk-MAXVH44E.js} +7 -7
  137. package/dist/{chunk-WZFAPHIK.js → chunk-MU7ZHSZE.js} +2 -2
  138. package/dist/chunk-N5Z4EVOR.js +123 -0
  139. package/dist/chunk-N5Z4EVOR.js.map +1 -0
  140. package/dist/{chunk-66772WBP.js → chunk-NFXQJHJD.js} +5 -5
  141. package/dist/{chunk-AZ5BTEKX.js → chunk-NQUF7XKR.js} +2 -2
  142. package/dist/{chunk-W4FBGYGK.js → chunk-NRV4Y3PM.js} +1 -1
  143. package/dist/{chunk-NHWB6HG2.js → chunk-NYCGIQRA.js} +2 -2
  144. package/dist/{chunk-4WKA6I65.js → chunk-OGCNZPKX.js} +1119 -309
  145. package/dist/chunk-OGCNZPKX.js.map +1 -0
  146. package/dist/{chunk-QJAQNXWI.js → chunk-ONHRRJZM.js} +6 -6
  147. package/dist/{chunk-FDAMS4PK.js → chunk-PNPOWBGC.js} +2 -2
  148. package/dist/{chunk-IC6CBHCA.js → chunk-QGENAYVS.js} +2 -2
  149. package/dist/{chunk-GBJ4RDFR.js → chunk-QIHIHZYC.js} +66 -91
  150. package/dist/chunk-QIHIHZYC.js.map +1 -0
  151. package/dist/{chunk-YNQBRLC5.js → chunk-RUYJ7TWU.js} +517 -2011
  152. package/dist/chunk-RUYJ7TWU.js.map +1 -0
  153. package/dist/{chunk-4A6YGWQN.js → chunk-TL4TO3EA.js} +115 -120
  154. package/dist/chunk-TL4TO3EA.js.map +1 -0
  155. package/dist/{chunk-SPJXKAVF.js → chunk-U52AKICI.js} +14 -13
  156. package/dist/chunk-U52AKICI.js.map +1 -0
  157. package/dist/chunk-ULARSG5Z.js +203 -0
  158. package/dist/chunk-ULARSG5Z.js.map +1 -0
  159. package/dist/{chunk-62U4J2GN.js → chunk-UR77VKF7.js} +2 -2
  160. package/dist/{chunk-JCBWCYEJ.js → chunk-VANLTS5F.js} +2 -2
  161. package/dist/{chunk-4Z4XNEWZ.js → chunk-VCD4PSK2.js} +56 -12
  162. package/dist/chunk-VCD4PSK2.js.map +1 -0
  163. package/dist/{chunk-RMMVQQA6.js → chunk-WDISJRMG.js} +2 -2
  164. package/dist/{chunk-B6JDYXL7.js → chunk-WWUVONGW.js} +7 -1
  165. package/dist/chunk-WWUVONGW.js.map +1 -0
  166. package/dist/{chunk-UVYI6VIX.js → chunk-X7Y7WX73.js} +1 -1
  167. package/dist/{chunk-TH2LSRMG.js → chunk-XHDIB4T4.js} +2 -2
  168. package/dist/{chunk-CVILPI77.js → chunk-XOQSEF7X.js} +6 -6
  169. package/dist/{chunk-LBOD4U5B.js → chunk-YFOMN2II.js} +6 -6
  170. package/dist/{chunk-RPW2NOU6.js → chunk-YIAA2X3D.js} +41 -1
  171. package/dist/chunk-YIAA2X3D.js.map +1 -0
  172. package/dist/chunk-YPC7W6GA.js +1604 -0
  173. package/dist/chunk-YPC7W6GA.js.map +1 -0
  174. package/dist/{chunk-I5NLEENJ.js → chunk-Z4SWV4VW.js} +3 -3
  175. package/dist/{chunk-PHWUFPCO.js → chunk-ZCPP5OW6.js} +61 -53
  176. package/dist/chunk-ZCPP5OW6.js.map +1 -0
  177. package/dist/{cli-C4GbETPC.d.ts → cli-BptUDTtp.d.ts} +5 -5
  178. package/dist/cli.d.ts +12 -12
  179. package/dist/cli.js +91 -84
  180. package/dist/compounding/engine.d.ts +2 -2
  181. package/dist/compounding/engine.js +14 -14
  182. package/dist/compounding/preference-consolidator.d.ts +1 -1
  183. package/dist/compression-optimizer.d.ts +1 -1
  184. package/dist/config.d.ts +1 -1
  185. package/dist/config.js +1 -1
  186. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  187. package/dist/connectors/codex-materialize-runner.js +14 -14
  188. package/dist/connectors/codex-materialize.d.ts +1 -1
  189. package/dist/connectors/index.d.ts +1 -1
  190. package/dist/connectors/index.js +17 -17
  191. package/dist/consolidation-provenance-check.d.ts +2 -2
  192. package/dist/consolidation-provenance-check.js +5 -5
  193. package/dist/consolidation-undo.d.ts +2 -2
  194. package/dist/consolidation-undo.js +3 -3
  195. package/dist/contradiction/index.d.ts +2 -2
  196. package/dist/contradiction/index.js +4 -4
  197. package/dist/conversation-index/backend.d.ts +1 -1
  198. package/dist/conversation-index/backend.js +2 -2
  199. package/dist/conversation-index/chunker.d.ts +1 -1
  200. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  201. package/dist/conversation-index/indexer.d.ts +1 -1
  202. package/dist/conversation-index/search.d.ts +1 -1
  203. package/dist/corpus-watermark.d.ts +1 -1
  204. package/dist/corpus-watermark.js +26 -26
  205. package/dist/day-summary.d.ts +1 -1
  206. package/dist/delinearize.d.ts +1 -1
  207. package/dist/direct-answer-wiring.d.ts +1 -1
  208. package/dist/direct-answer.d.ts +1 -1
  209. package/dist/embedding-fallback.d.ts +1 -1
  210. package/dist/enrichment/index.d.ts +1 -1
  211. package/dist/entity-retrieval.d.ts +2 -2
  212. package/dist/entity-retrieval.js +14 -14
  213. package/dist/entity-schema.d.ts +1 -1
  214. package/dist/explicit-capture.d.ts +8 -8
  215. package/dist/extraction-error-classification.d.ts +1 -1
  216. package/dist/extraction-faithfulness.d.ts +1 -1
  217. package/dist/extraction-judge-telemetry.d.ts +1 -1
  218. package/dist/extraction-judge-training.d.ts +1 -1
  219. package/dist/extraction-judge.d.ts +1 -1
  220. package/dist/extraction-liveness.d.ts +1 -1
  221. package/dist/extraction-source-grounding-rules.d.ts +1 -1
  222. package/dist/extraction-source-grounding.d.ts +15 -2
  223. package/dist/extraction-source-grounding.js +10 -1
  224. package/dist/extraction.d.ts +2 -15
  225. package/dist/extraction.js +5 -4
  226. package/dist/fallback-llm.d.ts +1 -1
  227. package/dist/graph-dashboard-diff.d.ts +1 -1
  228. package/dist/graph-dashboard-key.d.ts +1 -1
  229. package/dist/graph-dashboard-parser.d.ts +1 -1
  230. package/dist/graph-edge-reinforcement.d.ts +1 -1
  231. package/dist/graph-snapshot.d.ts +1 -1
  232. package/dist/graph.d.ts +1 -1
  233. package/dist/importance.d.ts +1 -1
  234. package/dist/importers/index.d.ts +1 -1
  235. package/dist/in-flight-reads.d.ts +1 -1
  236. package/dist/index.d.ts +19 -19
  237. package/dist/index.js +215 -207
  238. package/dist/intent.d.ts +1 -1
  239. package/dist/lcm/engine.d.ts +1 -1
  240. package/dist/lcm/engine.js +3 -3
  241. package/dist/lcm/index.d.ts +1 -1
  242. package/dist/lcm/index.js +6 -6
  243. package/dist/lcm/tools.d.ts +1 -1
  244. package/dist/lifecycle.d.ts +1 -1
  245. package/dist/live-connectors-runner.d.ts +1 -1
  246. package/dist/local-llm-helpers.d.ts +7 -0
  247. package/dist/local-llm-helpers.js +16 -0
  248. package/dist/local-llm-helpers.js.map +1 -0
  249. package/dist/local-llm.d.ts +3 -6
  250. package/dist/local-llm.js +2 -1
  251. package/dist/local-model-endpoint.d.ts +1 -1
  252. package/dist/maintenance/memory-governance.d.ts +1 -1
  253. package/dist/maintenance/memory-governance.js +14 -14
  254. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  255. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +14 -14
  256. package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
  257. package/dist/maintenance/rebuild-memory-projection.js +15 -15
  258. package/dist/{maintenance-CL30NWtc.d.ts → maintenance-b6f92bRz.d.ts} +3 -3
  259. package/dist/mcp-memory-inspector-app.d.ts +10 -10
  260. package/dist/memory-action-policy.d.ts +1 -1
  261. package/dist/memory-cache.d.ts +1 -1
  262. package/dist/memory-corpus-version.js +2 -2
  263. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  264. package/dist/memory-projection-store.d.ts +1 -1
  265. package/dist/memory-provenance.d.ts +1 -1
  266. package/dist/memory-worth-outcomes.d.ts +2 -2
  267. package/dist/models-json.d.ts +1 -1
  268. package/dist/namespaces/migrate.d.ts +3 -3
  269. package/dist/namespaces/migrate.js +24 -24
  270. package/dist/namespaces/principal.d.ts +1 -1
  271. package/dist/namespaces/search.d.ts +1 -1
  272. package/dist/namespaces/search.js +9 -9
  273. package/dist/namespaces/storage.d.ts +3 -3
  274. package/dist/namespaces/storage.js +14 -14
  275. package/dist/native-knowledge.d.ts +1 -1
  276. package/dist/offline-sync-impression-drain.d.ts +1 -1
  277. package/dist/offline-sync-impression-drain.js +6 -5
  278. package/dist/operator-doctor-corpus.d.ts +1 -1
  279. package/dist/operator-doctor-corpus.js +29 -29
  280. package/dist/operator-doctor-replica.d.ts +1 -1
  281. package/dist/operator-doctor-replica.js +3 -3
  282. package/dist/operator-toolkit.d.ts +3 -3
  283. package/dist/operator-toolkit.js +38 -37
  284. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  285. package/dist/orchestration/maintenance.d.ts +4 -4
  286. package/dist/orchestration/maintenance.js +22 -21
  287. package/dist/{orchestrator-CKTCRwWV.d.ts → orchestrator-3uGxC1qO.d.ts} +72 -34
  288. package/dist/orchestrator.d.ts +8 -8
  289. package/dist/orchestrator.js +167 -159
  290. package/dist/page-versioning.js +3 -3
  291. package/dist/patterns-cli.d.ts +1 -1
  292. package/dist/{pipeline-B7wj4rRZ.d.ts → pipeline-DepuNr-m.d.ts} +1 -1
  293. package/dist/policy-runtime.d.ts +1 -1
  294. package/dist/proactive-contention.d.ts +1 -1
  295. package/dist/provenance.d.ts +1 -1
  296. package/dist/{qmd-q9tg4dHp.d.ts → qmd-Dalr3Jby.d.ts} +1 -1
  297. package/dist/qmd-preflight.d.ts +2 -2
  298. package/dist/qmd-recall-cache.d.ts +1 -1
  299. package/dist/qmd.d.ts +2 -2
  300. package/dist/qmd.js +4 -4
  301. package/dist/recall-concurrency-config.d.ts +1 -1
  302. package/dist/recall-disclosure-escalation.d.ts +1 -1
  303. package/dist/recall-explain-renderer.d.ts +1 -1
  304. package/dist/recall-explain-renderer.js +3 -3
  305. package/dist/recall-memory-map.d.ts +1 -1
  306. package/dist/recall-planner-llm.d.ts +1 -1
  307. package/dist/recall-state.d.ts +1 -1
  308. package/dist/recall-tag-filter.d.ts +1 -1
  309. package/dist/recall-timings.d.ts +1 -1
  310. package/dist/recall-xray-cli.d.ts +1 -1
  311. package/dist/recall-xray-cli.js +4 -4
  312. package/dist/recall-xray-renderer.d.ts +1 -1
  313. package/dist/recall-xray-renderer.js +3 -3
  314. package/dist/recall-xray.d.ts +1 -1
  315. package/dist/recall-xray.js +2 -2
  316. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  317. package/dist/replay/normalizers/claude.d.ts +1 -1
  318. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  319. package/dist/replay/normalizers/shared.d.ts +1 -1
  320. package/dist/replay/runner.d.ts +1 -1
  321. package/dist/replay/types.d.ts +1 -1
  322. package/dist/replica-divergence.d.ts +1 -1
  323. package/dist/replica-divergence.js +2 -2
  324. package/dist/replica-peers-config.d.ts +1 -1
  325. package/dist/resolve-auth-token.d.ts +1 -1
  326. package/dist/resume-bundles.js +1 -1
  327. package/dist/retrieval-agents.d.ts +2 -2
  328. package/dist/retrieval-tiers.d.ts +1 -1
  329. package/dist/routing/engine.d.ts +1 -1
  330. package/dist/routing/store.d.ts +1 -1
  331. package/dist/salvage-envelope.d.ts +1 -1
  332. package/dist/{scope-profiles-CWDyglPM.d.ts → scope-profiles-BpiM5hTU.d.ts} +1 -1
  333. package/dist/search/embed-helper.d.ts +1 -1
  334. package/dist/search/factory.d.ts +1 -1
  335. package/dist/search/factory.js +8 -8
  336. package/dist/search/index.d.ts +1 -1
  337. package/dist/search/index.js +12 -12
  338. package/dist/search/lancedb-backend.d.ts +1 -1
  339. package/dist/search/meilisearch-backend.d.ts +1 -1
  340. package/dist/search/noop-backend.d.ts +1 -1
  341. package/dist/search/orama-backend.d.ts +1 -1
  342. package/dist/search/port.d.ts +1 -1
  343. package/dist/search/remote-backend.d.ts +1 -1
  344. package/dist/{semantic-consolidation-BUE3deLn.d.ts → semantic-consolidation-CMtVrf9f.d.ts} +1 -1
  345. package/dist/semantic-consolidation.d.ts +2 -2
  346. package/dist/semantic-consolidation.js +16 -16
  347. package/dist/semantic-rule-promotion.js +14 -14
  348. package/dist/semantic-rule-verifier.d.ts +1 -1
  349. package/dist/semantic-rule-verifier.js +14 -14
  350. package/dist/{service-CXgM0bQG.d.ts → service-DuAxwSgg.d.ts} +2 -2
  351. package/dist/session-observer-bands.d.ts +1 -1
  352. package/dist/session-observer-state.d.ts +1 -1
  353. package/dist/shared-context/manager.d.ts +1 -1
  354. package/dist/signal.d.ts +1 -1
  355. package/dist/{storage-DysJq-XH.d.ts → storage-CF0Ih8GP.d.ts} +1 -1
  356. package/dist/storage.d.ts +2 -2
  357. package/dist/storage.js +13 -13
  358. package/dist/summarizer.d.ts +1 -1
  359. package/dist/summarizer.js +3 -2
  360. package/dist/summary-snapshot.d.ts +1 -1
  361. package/dist/temporal-supersession.d.ts +2 -2
  362. package/dist/temporal-timeline-recall.d.ts +1 -1
  363. package/dist/temporal-validity.d.ts +1 -1
  364. package/dist/threading.d.ts +1 -1
  365. package/dist/tier-migration.d.ts +2 -2
  366. package/dist/tier-routing.d.ts +1 -1
  367. package/dist/tokens.js +2 -2
  368. package/dist/topics.d.ts +1 -1
  369. package/dist/transcript.d.ts +1 -1
  370. package/dist/transfer/backup.js +2 -2
  371. package/dist/transfer/capsule-export.js +2 -2
  372. package/dist/transfer/capsule-import.js +5 -5
  373. package/dist/trust-score-stage.d.ts +1 -1
  374. package/dist/trust-score.d.ts +1 -1
  375. package/dist/{types-aPPzhnjT.d.ts → types-BpaYvJ6v.d.ts} +22 -2
  376. package/dist/types.d.ts +1 -1
  377. package/dist/types.js +1 -1
  378. package/dist/utility-runtime.d.ts +1 -1
  379. package/dist/verified-recall.js +14 -14
  380. package/dist/write-envelope.d.ts +1 -1
  381. package/package.json +2 -2
  382. package/src/access-boundary.ts +3 -0
  383. package/src/access-cli.test.ts +93 -0
  384. package/src/access-cli.ts +56 -2
  385. package/src/access-extraction-force-flush.ts +177 -0
  386. package/src/access-http-extraction-force-flush.test.ts +132 -0
  387. package/src/access-http-lcm-compaction.ts +99 -22
  388. package/src/access-http-lifecycle-flush.ts +81 -0
  389. package/src/access-http.test.ts +227 -0
  390. package/src/access-http.ts +33 -40
  391. package/src/access-mcp-output-schemas.ts +6 -0
  392. package/src/access-mcp.test.ts +40 -0
  393. package/src/access-mcp.ts +32 -1
  394. package/src/access-observe-helpers.ts +180 -0
  395. package/src/access-observe-write-surface.ts +78 -19
  396. package/src/access-operations-batch.ts +2 -1
  397. package/src/access-schema-pi.test.ts +17 -0
  398. package/src/access-schema.ts +28 -11
  399. package/src/access-service-helpers.ts +46 -0
  400. package/src/access-service-observe-lcm-parity.test.ts +781 -31
  401. package/src/access-service-observe-scope.test.ts +88 -0
  402. package/src/access-service.ts +46 -128
  403. package/src/access-surface-catalog.test.ts +32 -0
  404. package/src/access-surface-catalog.ts +2 -0
  405. package/src/buffer-session.test.ts +148 -0
  406. package/src/buffer-turn-helpers.ts +152 -0
  407. package/src/buffer.ts +126 -201
  408. package/src/correction/correction-access-wiring.ts +79 -27
  409. package/src/correction/correction-executor.test.ts +542 -1
  410. package/src/correction/correction-executor.ts +309 -89
  411. package/src/correction/correction-planner.test.ts +35 -1
  412. package/src/correction/correction-planner.ts +50 -3
  413. package/src/correction/correction-service.ts +12 -7
  414. package/src/correction/correction-storage-integration.test.ts +172 -1
  415. package/src/correction/passive-capture.test.ts +35 -0
  416. package/src/correction/passive-capture.ts +17 -3
  417. package/src/extraction-source-grounding.ts +55 -1
  418. package/src/extraction-timeout.test.ts +51 -0
  419. package/src/extraction.ts +64 -90
  420. package/src/local-llm-headers-timeout.test.ts +48 -0
  421. package/src/local-llm-helpers.ts +58 -0
  422. package/src/local-llm.ts +62 -58
  423. package/src/orchestration/extraction-run-helpers.ts +309 -0
  424. package/src/orchestration/extraction-run.test.ts +350 -7
  425. package/src/orchestration/extraction-run.ts +327 -250
  426. package/src/orchestration/session-context.ts +193 -18
  427. package/src/orchestration/turn-ingestion.ts +61 -11
  428. package/src/orchestrator-flush.test.ts +511 -5
  429. package/src/orchestrator.ts +8 -6
  430. package/src/types.ts +2 -2
  431. package/dist/chunk-4A6YGWQN.js.map +0 -1
  432. package/dist/chunk-4WKA6I65.js.map +0 -1
  433. package/dist/chunk-4Z4XNEWZ.js.map +0 -1
  434. package/dist/chunk-6FUPCJUL.js.map +0 -1
  435. package/dist/chunk-B6JDYXL7.js.map +0 -1
  436. package/dist/chunk-GBJ4RDFR.js.map +0 -1
  437. package/dist/chunk-HUHX2OCX.js.map +0 -1
  438. package/dist/chunk-IYZ5K7MU.js.map +0 -1
  439. package/dist/chunk-LGHKJABJ.js.map +0 -1
  440. package/dist/chunk-PHWUFPCO.js.map +0 -1
  441. package/dist/chunk-RPW2NOU6.js.map +0 -1
  442. package/dist/chunk-SPJXKAVF.js.map +0 -1
  443. package/dist/chunk-YNQBRLC5.js.map +0 -1
  444. package/dist/chunk-YTSNAUXN.js.map +0 -1
  445. /package/dist/{capsule-crypto-GWVG7LGC.js.map → access-extraction-force-flush.js.map} +0 -0
  446. /package/dist/{capsule-merge-5KKJQBWQ.js.map → capsule-merge-2H2IUTBT.js.map} +0 -0
  447. /package/dist/{chunk-LEPOP46W.js.map → chunk-227UFSEQ.js.map} +0 -0
  448. /package/dist/{chunk-GIBMDSJ2.js.map → chunk-27P5YVDU.js.map} +0 -0
  449. /package/dist/{chunk-2SA56WMI.js.map → chunk-2LTDWSR6.js.map} +0 -0
  450. /package/dist/{chunk-OWHERGF2.js.map → chunk-2NLLXCJG.js.map} +0 -0
  451. /package/dist/{chunk-6U7F52DE.js.map → chunk-4XHR3I67.js.map} +0 -0
  452. /package/dist/{chunk-2EUVEDF5.js.map → chunk-5XA7PQ5I.js.map} +0 -0
  453. /package/dist/{chunk-ALSGMEJR.js.map → chunk-65ZPVYTA.js.map} +0 -0
  454. /package/dist/{chunk-5VQJBH2G.js.map → chunk-6TAUHL33.js.map} +0 -0
  455. /package/dist/{chunk-RGIWINDN.js.map → chunk-7G5MH4NT.js.map} +0 -0
  456. /package/dist/{chunk-WVO4G37W.js.map → chunk-7TEZUUGM.js.map} +0 -0
  457. /package/dist/{chunk-MJCGLM25.js.map → chunk-7VUAG236.js.map} +0 -0
  458. /package/dist/{chunk-M3C2V3PV.js.map → chunk-AJEMDQNA.js.map} +0 -0
  459. /package/dist/{chunk-RKNJBZ55.js.map → chunk-AU7Q3LSC.js.map} +0 -0
  460. /package/dist/{chunk-ACXPX4AG.js.map → chunk-BR2K43MK.js.map} +0 -0
  461. /package/dist/{chunk-CZRYLSSW.js.map → chunk-BUGVGH4N.js.map} +0 -0
  462. /package/dist/{chunk-YTQNQ4KB.js.map → chunk-C4VQMFVM.js.map} +0 -0
  463. /package/dist/{chunk-ZPSVBSOF.js.map → chunk-CIVDJD6P.js.map} +0 -0
  464. /package/dist/{chunk-K5GB36ME.js.map → chunk-CZDOT5I2.js.map} +0 -0
  465. /package/dist/{chunk-VI2JQPNQ.js.map → chunk-E4PWACUN.js.map} +0 -0
  466. /package/dist/{chunk-D2ANTDGO.js.map → chunk-E6BMSH7U.js.map} +0 -0
  467. /package/dist/{chunk-P5PXWTW7.js.map → chunk-EJCAV3QZ.js.map} +0 -0
  468. /package/dist/{chunk-CKMO4RDY.js.map → chunk-EMDLM3LW.js.map} +0 -0
  469. /package/dist/{chunk-G5R3D5UP.js.map → chunk-GTTAHHD3.js.map} +0 -0
  470. /package/dist/{chunk-H5CPS5JP.js.map → chunk-H36LJIPD.js.map} +0 -0
  471. /package/dist/{chunk-AITCS2RC.js.map → chunk-IJ7HK452.js.map} +0 -0
  472. /package/dist/{chunk-D3LMYTBV.js.map → chunk-J7WZUL2K.js.map} +0 -0
  473. /package/dist/{chunk-5W4ZLBEZ.js.map → chunk-JCFTIQBR.js.map} +0 -0
  474. /package/dist/{chunk-XOZ4ALGB.js.map → chunk-KE353EWI.js.map} +0 -0
  475. /package/dist/{chunk-W5EG6LE3.js.map → chunk-KIT6M7EB.js.map} +0 -0
  476. /package/dist/{chunk-5W7CTZ2E.js.map → chunk-KQAFEZQX.js.map} +0 -0
  477. /package/dist/{chunk-Z54EM5YZ.js.map → chunk-MAXVH44E.js.map} +0 -0
  478. /package/dist/{chunk-WZFAPHIK.js.map → chunk-MU7ZHSZE.js.map} +0 -0
  479. /package/dist/{chunk-66772WBP.js.map → chunk-NFXQJHJD.js.map} +0 -0
  480. /package/dist/{chunk-AZ5BTEKX.js.map → chunk-NQUF7XKR.js.map} +0 -0
  481. /package/dist/{chunk-W4FBGYGK.js.map → chunk-NRV4Y3PM.js.map} +0 -0
  482. /package/dist/{chunk-NHWB6HG2.js.map → chunk-NYCGIQRA.js.map} +0 -0
  483. /package/dist/{chunk-QJAQNXWI.js.map → chunk-ONHRRJZM.js.map} +0 -0
  484. /package/dist/{chunk-FDAMS4PK.js.map → chunk-PNPOWBGC.js.map} +0 -0
  485. /package/dist/{chunk-IC6CBHCA.js.map → chunk-QGENAYVS.js.map} +0 -0
  486. /package/dist/{chunk-62U4J2GN.js.map → chunk-UR77VKF7.js.map} +0 -0
  487. /package/dist/{chunk-JCBWCYEJ.js.map → chunk-VANLTS5F.js.map} +0 -0
  488. /package/dist/{chunk-RMMVQQA6.js.map → chunk-WDISJRMG.js.map} +0 -0
  489. /package/dist/{chunk-UVYI6VIX.js.map → chunk-X7Y7WX73.js.map} +0 -0
  490. /package/dist/{chunk-TH2LSRMG.js.map → chunk-XHDIB4T4.js.map} +0 -0
  491. /package/dist/{chunk-CVILPI77.js.map → chunk-XOQSEF7X.js.map} +0 -0
  492. /package/dist/{chunk-LBOD4U5B.js.map → chunk-YFOMN2II.js.map} +0 -0
  493. /package/dist/{chunk-I5NLEENJ.js.map → chunk-Z4SWV4VW.js.map} +0 -0
@@ -51,6 +51,17 @@ export interface ExtractionRunResult {
51
51
  postPersistMetadataFailed?: boolean;
52
52
  }
53
53
 
54
+ export class ExtractionDeadlineError extends Error {
55
+ readonly stage: string;
56
+
57
+ constructor(stage: string) {
58
+ super(`replay extraction deadline exceeded (${stage})`);
59
+ this.name = "ExtractionDeadlineError";
60
+ this.stage = stage;
61
+ }
62
+ }
63
+
64
+
54
65
  /** Dependencies injected by the orchestrator. All stable references or
55
66
  * live accessors — lazy getters for anything tests reassign
56
67
  * post-construction (buffer, extraction, storageRouter, threading). */
@@ -81,6 +92,7 @@ export interface ExtractionRunCoordinatorDeps {
81
92
  namespace: string;
82
93
  bufferKey: string;
83
94
  isLiveSession: boolean;
95
+ abortSignal?: AbortSignal;
84
96
  }
85
97
  ) => Promise<void>;
86
98
 
@@ -112,83 +124,25 @@ export interface ExtractionRunCoordinatorDeps {
112
124
  // #1526 seam 25; this file previously carried duplicate copies
113
125
  // (AGENTS.md rule 8 — no duplicated helpers).
114
126
  import { raceRecallAbort, throwIfRecallAborted } from "./orchestrator-helpers.js";
127
+ import {
128
+ capExtractionRetryStateEntries,
129
+ combineExtractionAbortSignals,
130
+ computeExtractionRetryNextEligibleMs,
131
+ deriveSourceConnector,
132
+ deriveTopicsFromExtraction,
133
+ runExtractionPostPersist,
134
+ runExtractionPostPersistBestEffort,
135
+ EXTRACTION_RETRY_STATE_MAX_ENTRIES,
136
+ } from "./extraction-run-helpers.js";
137
+ import type { ExtractionResilienceStatus, ExtractionRetryStateEntry } from "./extraction-run-helpers.js";
115
138
 
116
- export function deriveTopicsFromExtraction(result: ExtractionResult): string[] {
117
- const topics = new Set<string>();
118
- for (const fact of result.facts ?? []) {
119
- for (const tag of fact.tags ?? []) {
120
- if (tag && tag.length >= 2) topics.add(tag.toLowerCase());
121
- }
122
- if (fact.entityRef) topics.add(fact.entityRef.toLowerCase());
123
- if (fact.category) topics.add(fact.category);
124
- }
125
- for (const entity of (result as any).entities ?? []) {
126
- if (typeof entity.name === "string" && entity.name.length >= 2) {
127
- topics.add(entity.name.toLowerCase());
128
- }
129
- }
130
- return [...topics].slice(0, 16);
131
- }
132
-
133
- /** Bounded cap on persisted per-fingerprint retry-state entries (per namespace meta). */
134
- const EXTRACTION_RETRY_STATE_MAX_ENTRIES = 500;
135
-
136
- interface ExtractionRetryStateEntry {
137
- attempts: number;
138
- nextEligibleAtMs: number;
139
- firstFailedAtMs: number;
140
- lastFailureClass: ExtractionFailureClass;
141
- }
142
-
143
- export interface ExtractionResilienceStatus {
144
- breaker: {
145
- state: "closed" | "open" | "half_open";
146
- openUntilMs: number;
147
- consecutiveFailures: number;
148
- lastReason: string;
149
- };
150
- backoffFingerprintCount: number;
151
- }
152
-
153
- /**
154
- * Pure backoff scheduler. Returns the absolute ms timestamp at which a
155
- * fingerprint that has failed `attempts` times becomes eligible again:
156
- * exponential via the configured schedule, clamped to `maxBackoffMs`, with
157
- * ±`jitterRatio` multiplicative jitter. `rng` is injectable for deterministic
158
- * tests. Never returns a timestamp before `now`.
159
- */
160
- export function computeExtractionRetryNextEligibleMs(
161
- attempts: number,
162
- scheduleMs: readonly number[],
163
- maxBackoffMs: number,
164
- jitterRatio: number,
165
- now: number,
166
- rng: () => number = Math.random,
167
- ): number {
168
- const cap = Number.isFinite(maxBackoffMs) && maxBackoffMs > 0 ? maxBackoffMs : 0;
169
- if (!Array.isArray(scheduleMs) || scheduleMs.length === 0) {
170
- return now + cap;
171
- }
172
- const idx = Math.min(Math.max(attempts - 1, 0), scheduleMs.length - 1);
173
- const step = scheduleMs[idx];
174
- const base = Math.min(typeof step === "number" && step > 0 ? step : cap, cap);
175
- const jitter = 1 + (rng() * 2 - 1) * jitterRatio;
176
- return now + Math.max(0, Math.round(base * jitter));
177
- }
178
-
179
- /**
180
- * Cap the persisted retry-state array to the newest `maxEntries` by
181
- * firstFailedAt. Guards `maxEntries <= 0` so a zero cap returns [] rather than
182
- * `slice(-0)`, which returns ALL entries (AGENTS.md rule 17).
183
- */
184
- export function capExtractionRetryStateEntries<T extends { firstFailedAt: string }>(
185
- entries: readonly T[],
186
- maxEntries: number,
187
- ): T[] {
188
- if (maxEntries <= 0) return [];
189
- const sorted = [...entries].sort((a, b) => a.firstFailedAt.localeCompare(b.firstFailedAt));
190
- return sorted.slice(-maxEntries);
191
- }
139
+ export {
140
+ capExtractionRetryStateEntries,
141
+ computeExtractionRetryNextEligibleMs,
142
+ deriveSourceConnector,
143
+ deriveTopicsFromExtraction,
144
+ };
145
+ export type { ExtractionResilienceStatus };
192
146
 
193
147
  /**
194
148
  * Coordinates the extraction run pipeline. Holds the dedupe fingerprint
@@ -496,6 +450,7 @@ export class ExtractionRunCoordinator {
496
450
  turns: BufferTurn[],
497
451
  options: {
498
452
  clearBufferAfterExtraction?: boolean;
453
+ clearMatchingTurns?: boolean;
499
454
  skipCharThreshold?: boolean;
500
455
  skipUserTurnThreshold?: boolean;
501
456
  deadlineMs?: number;
@@ -503,52 +458,118 @@ export class ExtractionRunCoordinator {
503
458
  abortSignal?: AbortSignal;
504
459
  failOnExtractionFailure?: boolean;
505
460
  /**
506
- * Explicit namespace override for the write path (#460). When set,
507
- * extraction writes go to this namespace instead of the one derived
461
+ * Called immediately after durable extraction persistence returns, before
462
+ * any metadata, retention, or helper work. Lifecycle callers use this
463
+ * as the write-rate-limit commit boundary.
464
+ */
465
+ onDurableCommit?: () => void;
466
+ /**
467
+ * Explicit namespace override for the write path (#460). When set, this
468
+ * is the namespace where extraction writes go instead of the one derived
508
469
  * from `defaultNamespaceForPrincipal(resolvePrincipal(sessionKey))`.
509
- * The resolved `principal` is still threaded into memory metadata
510
- * for provenance; only the storage target is overridden.
470
+ * The resolved `principal` is still threaded into memory metadata for
471
+ * provenance; only the storage target is overridden.
511
472
  */
512
473
  writeNamespaceOverride?: string;
513
474
  /**
514
475
  * Pin the provenance principal instead of deriving it from
515
- * `resolvePrincipal(sessionKey)` (#1495 thread 1). When set, this is the
516
- * identity an access surface already authenticated; used so observed-turn
517
- * provenance is correct even though `turn.sessionKey` is the ORIGINAL
518
- * (un-prefixed) key and storage is pinned via `writeNamespaceOverride`.
476
+ * `resolvePrincipal(sessionKey)`. When set, this is the identity an
477
+ * access surface already authenticated; used so observed-turn provenance
478
+ * is correct even though `turn.sessionKey` is the original key.
519
479
  */
520
480
  principalOverride?: string;
481
+ /**
482
+ * Access scope resolution may already have selected a profile while the
483
+ * session context remains intentionally unbound. Reuse that plan for
484
+ * persistence instead of deriving it from shared session state.
485
+ */
486
+ scopeProfileWritePlan?: ResolvedScopeProfilePlan | null;
521
487
  /**
522
488
  * Force the extractor call, bypassing the retry-backoff / circuit-breaker
523
- * gate (extraction hot-loop hardening). Set by explicit flush paths
524
- * (before_reset / session flush / replay / bulk import) that already pass
525
- * `skipDedupeCheck: true` — AGENTS.md rule 18. A forced attempt still
526
- * records its failure into retry-state/breaker.
489
+ * gate (extraction hot-loop hardening). Set by explicit flush paths.
527
490
  */
528
491
  forceExtractionAttempt?: boolean;
529
492
  } = {}
530
493
  ): Promise<ExtractionRunResult> {
531
494
  log.debug(`running extraction on ${turns.length} turns`);
532
495
  const clearBufferAfterExtraction = options.clearBufferAfterExtraction ?? true;
496
+ const clearMatchingTurns = options.clearMatchingTurns === true;
533
497
  const skipCharThreshold = options.skipCharThreshold ?? false;
534
498
  const skipUserTurnThreshold = options.skipUserTurnThreshold ?? false;
535
499
  const deadlineMs =
536
500
  typeof options.deadlineMs === "number" && Number.isFinite(options.deadlineMs) ? options.deadlineMs : undefined;
537
501
  const bufferKey = options.bufferKey ?? turns[0]?.sessionKey ?? "default";
538
502
  const throwIfDeadlineExceeded = (stage: string): void => {
539
- if (typeof deadlineMs === "number" && Date.now() > deadlineMs) {
540
- throw new Error(`replay extraction deadline exceeded (${stage})`);
503
+ if (typeof deadlineMs === "number" && Date.now() >= deadlineMs) {
504
+ throw new ExtractionDeadlineError(stage);
541
505
  }
542
506
  };
543
507
  const throwIfAborted = (stage: string): void => {
544
508
  throwIfRecallAborted(options.abortSignal, `extraction aborted (${stage})`);
545
509
  };
510
+ const runPassiveCapture = async (
511
+ captureTurns: readonly BufferTurn[],
512
+ captureOptions: {
513
+ sessionKey: string;
514
+ principal?: string;
515
+ namespace: string;
516
+ bufferKey: string;
517
+ isLiveSession: boolean;
518
+ abortSignal?: AbortSignal;
519
+ },
520
+ ): Promise<void> => {
521
+ const deadlineController =
522
+ typeof deadlineMs === "number" && Number.isFinite(deadlineMs) ? new AbortController() : undefined;
523
+ const captureSignal = combineExtractionAbortSignals(captureOptions.abortSignal ?? options.abortSignal,
524
+ deadlineController);
525
+ let deadlineTimer: NodeJS.Timeout | undefined;
526
+ if (deadlineController && typeof deadlineMs === "number") {
527
+ const scheduleDeadlineAbort = (): void => {
528
+ const remainingMs = deadlineMs - Date.now();
529
+ if (remainingMs <= 0) {
530
+ deadlineController.abort();
531
+ return;
532
+ }
533
+ deadlineTimer = setTimeout(
534
+ () => {
535
+ if (Date.now() >= deadlineMs) {
536
+ deadlineController.abort();
537
+ } else {
538
+ scheduleDeadlineAbort();
539
+ }
540
+ },
541
+ Math.min(remainingMs, 2_147_483_647),
542
+ );
543
+ };
544
+ scheduleDeadlineAbort();
545
+ }
546
+ try {
547
+ await raceRecallAbort(
548
+ this.deps.maybeCapturePassiveCorrections(captureTurns, {
549
+ ...captureOptions,
550
+ ...(captureSignal ? { abortSignal: captureSignal } : {}),
551
+ }),
552
+ captureSignal,
553
+ "extraction aborted (during_passive_capture)",
554
+ );
555
+ } catch (error) {
556
+ if (typeof deadlineMs === "number" && Date.now() >= deadlineMs) {
557
+ throw new ExtractionDeadlineError("during_passive_capture");
558
+ }
559
+ throw error;
560
+ } finally {
561
+ if (deadlineTimer) clearTimeout(deadlineTimer);
562
+ }
563
+ };
546
564
  const clearBuffer = async (options?: { ignoreAbort?: boolean }) => {
547
565
  if (options?.ignoreAbort !== true) {
566
+ throwIfDeadlineExceeded("before_clear_buffer");
548
567
  throwIfAborted("before_clear_buffer");
549
568
  }
550
569
  if (clearBufferAfterExtraction) {
551
- await this.deps.getBuffer().clearAfterExtraction(bufferKey, turns);
570
+ await this.deps
571
+ .getBuffer()
572
+ .clearAfterExtraction(bufferKey, turns, clearMatchingTurns ? { allowNonPrefix: true } : undefined);
552
573
  }
553
574
  };
554
575
 
@@ -578,13 +599,23 @@ export class ExtractionRunCoordinator {
578
599
  // Context-only turns may still contain corrections (review: "context-only
579
600
  // turns skip capture"). Scan normalizedTurns before clearing the buffer.
580
601
  if (normalizedTurns.length > 0) {
581
- await this.deps.maybeCapturePassiveCorrections(normalizedTurns as BufferTurn[], {
602
+ throwIfDeadlineExceeded("before_context_only_capture");
603
+ const capturePrincipal =
604
+ typeof options.principalOverride === "string" && options.principalOverride.length > 0
605
+ ? options.principalOverride
606
+ : resolvePrincipal(sessionKey, this.config);
607
+ const captureNamespace =
608
+ typeof options.writeNamespaceOverride === "string" && options.writeNamespaceOverride.length > 0
609
+ ? options.writeNamespaceOverride
610
+ : this.deps.resolveSelfNamespace(sessionKey);
611
+ await runPassiveCapture(normalizedTurns as BufferTurn[], {
582
612
  sessionKey,
583
- principal: resolvePrincipal(sessionKey, this.config),
584
- namespace: this.deps.resolveSelfNamespace(sessionKey),
613
+ principal: capturePrincipal,
614
+ namespace: captureNamespace,
585
615
  bufferKey,
586
616
  isLiveSession: clearBufferAfterExtraction,
587
617
  });
618
+ throwIfDeadlineExceeded("before_context_only_clear");
588
619
  }
589
620
  await clearBuffer();
590
621
  return { status: "skipped", reason: "empty_normalized_turns", persistedCount: 0, durableOutputCount: 0 };
@@ -599,7 +630,6 @@ export class ExtractionRunCoordinator {
599
630
  const belowUserTurnThreshold = !skipUserTurnThreshold && userTurns.length < this.config.extractionMinUserTurns;
600
631
  if ((!skipCharThreshold && belowCharThreshold) || belowUserTurnThreshold) {
601
632
  log.debug(`skipping extraction: below threshold (totalChars=${totalChars}, userTurns=${userTurns.length})`);
602
- await clearBuffer();
603
633
  // Passive correction capture runs even when extraction is skipped for
604
634
  // being below the char/user-turn threshold (review: "skipped extraction
605
635
  // skips capture"). A short correction like "stop using Vim" is under
@@ -632,7 +662,7 @@ export class ExtractionRunCoordinator {
632
662
  });
633
663
  const captureNamespace =
634
664
  captureWO ?? captureScopePlan?.writeNamespace ?? this.deps.resolveSelfNamespace(sessionKey);
635
- await this.deps.maybeCapturePassiveCorrections(normalizedTurns as BufferTurn[], {
665
+ await runPassiveCapture(normalizedTurns as BufferTurn[], {
636
666
  sessionKey,
637
667
  principal: capturePrincipal,
638
668
  namespace: captureNamespace,
@@ -640,6 +670,7 @@ export class ExtractionRunCoordinator {
640
670
  isLiveSession: clearBufferAfterExtraction,
641
671
  });
642
672
  }
673
+ await clearBuffer();
643
674
  return {
644
675
  status: "skipped",
645
676
  reason: "below_threshold",
@@ -673,13 +704,19 @@ export class ExtractionRunCoordinator {
673
704
  this.config.codingMode,
674
705
  this.config.defaultNamespace
675
706
  );
676
- const scopeProfileGatePlan = resolveScopeProfilePlan({
677
- config: this.config,
678
- principal,
679
- codingContext: codingContextForWrite,
680
- codingOverlay: codingOverlayForWrite,
681
- });
682
- const scopeProfileWritePlan = explicitWriteNamespace ? null : scopeProfileGatePlan;
707
+ const scopeProfileGatePlan =
708
+ options.scopeProfileWritePlan !== undefined
709
+ ? options.scopeProfileWritePlan
710
+ : resolveScopeProfilePlan({
711
+ config: this.config,
712
+ principal,
713
+ codingContext: codingContextForWrite,
714
+ codingOverlay: codingOverlayForWrite,
715
+ });
716
+ const scopeProfileWritePlan =
717
+ explicitWriteNamespace && options.scopeProfileWritePlan === undefined
718
+ ? null
719
+ : scopeProfileGatePlan;
683
720
  if (scopeProfileWritePlan) {
684
721
  const selectedLayer = scopeProfileWritePlan.layers.find((layer) => layer.id === scopeProfileWritePlan.writeLayer);
685
722
  const writeNamespaceReadable = scopeProfileWritePlan.readNamespaces.includes(
@@ -702,17 +739,84 @@ export class ExtractionRunCoordinator {
702
739
  explicitWriteNamespace ??
703
740
  scopeProfileWritePlan?.writeNamespace ??
704
741
  this.deps.applyCodingNamespaceOverlay(sessionKey, defaultNamespaceForPrincipal(principal, this.config));
705
- const storage = await this.deps.getStorageRouter().storageFor(selfNamespace);
742
+ const extractionDeadlineController =
743
+ typeof deadlineMs === "number" && Number.isFinite(deadlineMs)
744
+ ? new AbortController()
745
+ : undefined;
746
+ const extractionAbortSignal = extractionDeadlineController
747
+ ? options.abortSignal
748
+ ? AbortSignal.any([options.abortSignal, extractionDeadlineController.signal])
749
+ : extractionDeadlineController.signal
750
+ : options.abortSignal;
751
+ let extractionDeadlineTimer: NodeJS.Timeout | undefined;
752
+ const clearExtractionDeadlineTimer = (): void => {
753
+ if (extractionDeadlineTimer) {
754
+ clearTimeout(extractionDeadlineTimer);
755
+ extractionDeadlineTimer = undefined;
756
+ }
757
+ };
758
+ const runDeadlineAware = async <T>(
759
+ operation: () => Promise<T>,
760
+ phase: string,
761
+ clearTimerOnError = true,
762
+ options: { ignoreAbort?: boolean; ignoreDeadline?: boolean } = {},
763
+ ): Promise<T> => {
764
+ const operationSignal = options.ignoreAbort ? undefined : extractionAbortSignal;
765
+ try {
766
+ if (!options.ignoreDeadline && typeof deadlineMs === "number" && Date.now() >= deadlineMs) {
767
+ throwIfDeadlineExceeded(phase);
768
+ }
769
+ if (operationSignal?.aborted) {
770
+ throwIfAborted(phase);
771
+ }
772
+ return await raceRecallAbort(
773
+ operation(),
774
+ operationSignal,
775
+ `extraction aborted (${phase})`,
776
+ );
777
+ } catch (error) {
778
+ if (clearTimerOnError) clearExtractionDeadlineTimer();
779
+ if (!options.ignoreDeadline && typeof deadlineMs === "number" && Date.now() >= deadlineMs) {
780
+ throw new ExtractionDeadlineError(phase);
781
+ }
782
+ throw error;
783
+ }
784
+ };
785
+ if (extractionDeadlineController && typeof deadlineMs === "number") {
786
+ const deadline = deadlineMs;
787
+ const maxTimerDelayMs = 2_147_483_647;
788
+ const scheduleDeadlineAbort = (): void => {
789
+ const remainingMs = deadline - Date.now();
790
+ if (remainingMs <= 0) {
791
+ extractionDeadlineController.abort();
792
+ return;
793
+ }
794
+ extractionDeadlineTimer = setTimeout(() => {
795
+ if (Date.now() >= deadline) {
796
+ extractionDeadlineController.abort();
797
+ } else {
798
+ scheduleDeadlineAbort();
799
+ }
800
+ }, Math.min(remainingMs, maxTimerDelayMs));
801
+ };
802
+ scheduleDeadlineAbort();
803
+ }
804
+ const storage = await runDeadlineAware(
805
+ () => this.deps.getStorageRouter().storageFor(selfNamespace),
806
+ "during_storage",
807
+ );
706
808
  const shouldPersistProcessedFingerprint = targetTurns.some((turn) => turn.persistProcessedFingerprint === true);
707
809
  const extractionFingerprint = this.buildExtractionFingerprint(targetTurns, bufferKey);
708
- let meta = extractionFingerprint && shouldPersistProcessedFingerprint ? await storage.loadMeta() : null;
810
+ let meta = extractionFingerprint && shouldPersistProcessedFingerprint
811
+ ? await runDeadlineAware(() => storage.loadMeta(), "during_load_meta")
812
+ : null;
709
813
  if (
710
814
  extractionFingerprint &&
711
815
  shouldPersistProcessedFingerprint &&
712
816
  (meta?.processedExtractionFingerprints ?? []).some((entry) => entry.fingerprint === extractionFingerprint)
713
817
  ) {
714
- log.debug(`runExtraction: skipping already-processed extraction fingerprint for ${bufferKey}`);
715
- await clearBuffer();
818
+ await runDeadlineAware(() => clearBuffer(), "during_clear_buffer");
819
+ clearExtractionDeadlineTimer();
716
820
  return {
717
821
  status: "skipped",
718
822
  reason: "processed_fingerprint",
@@ -735,7 +839,7 @@ export class ExtractionRunCoordinator {
735
839
  const extractionRetryEnabled = resolveMemoryLifecycleCapabilities(this.config).extractionRetry;
736
840
  if (extractionFingerprint && !forcedExtractionAttempt && extractionRetryEnabled) {
737
841
  try {
738
- meta ??= await storage.loadMeta();
842
+ meta ??= await runDeadlineAware(() => storage.loadMeta(), "during_retry_gate");
739
843
  this.hydrateRetryStateFromMeta(selfNamespace, meta);
740
844
  const nowMs = Date.now();
741
845
  let suppressReason: "provider_circuit_open" | "extraction_backoff" | null = null;
@@ -746,38 +850,55 @@ export class ExtractionRunCoordinator {
746
850
  if (st && nowMs < st.nextEligibleAtMs) suppressReason = "extraction_backoff";
747
851
  }
748
852
  if (suppressReason) {
749
- // Parity with the other skip paths: passive correction capture is
750
- // local (no provider call) and must not be delayed by a provider
751
- // cooldown — "stop calling me X" style turns should register during
752
- // a 30-minute breaker window too (codex review). Fail-open.
853
+ // Passive correction capture is local and must not be delayed by a
854
+ // provider cooldown. It remains fail-open.
753
855
  try {
754
- await this.deps.maybeCapturePassiveCorrections(normalizedTurns as BufferTurn[], {
755
- sessionKey,
756
- principal,
757
- namespace: selfNamespace,
758
- bufferKey,
759
- isLiveSession: clearBufferAfterExtraction,
760
- });
856
+ await runDeadlineAware(
857
+ () => runPassiveCapture(normalizedTurns as BufferTurn[], {
858
+ sessionKey,
859
+ principal,
860
+ namespace: selfNamespace,
861
+ bufferKey,
862
+ isLiveSession: clearBufferAfterExtraction,
863
+ }),
864
+ "during_passive_capture",
865
+ );
761
866
  } catch (captureErr) {
867
+ if (captureErr instanceof ExtractionDeadlineError) throw captureErr;
762
868
  log.warn("runExtraction: passive correction capture failed on suppressed attempt (non-fatal)", captureErr);
763
869
  }
870
+ clearExtractionDeadlineTimer();
764
871
  return { status: "skipped", reason: suppressReason, persistedCount: 0, durableOutputCount: 0 };
765
872
  }
766
873
  } catch (err) {
874
+ if (err instanceof ExtractionDeadlineError) throw err;
767
875
  // Fail-open: a gate error must never block extraction.
768
876
  log.warn("runExtraction: extraction retry gate check failed; proceeding (fail-open)", err);
769
877
  }
770
878
  }
771
879
 
772
- // Pass existing entity names so the LLM can reuse them instead of inventing variants
773
- const existingEntities = await storage.listEntityNames();
774
- const result = await raceRecallAbort(
775
- this.deps.getExtraction().extract(normalizedTurns, existingEntities),
776
- options.abortSignal,
777
- "extraction aborted (during_extract)"
880
+ // Pass existing entity names so the LLM can reuse them instead of inventing variants.
881
+ const existingEntities = await runDeadlineAware(
882
+ () => storage.listEntityNames(),
883
+ "during_list_entity_names",
778
884
  );
779
- throwIfDeadlineExceeded("before_persist");
780
- throwIfAborted("before_persist");
885
+ let result: ExtractionResult;
886
+ try {
887
+ result = await raceRecallAbort(
888
+ this.deps.getExtraction().extract(normalizedTurns, existingEntities, extractionAbortSignal),
889
+ extractionAbortSignal,
890
+ "extraction aborted (during_extract)",
891
+ );
892
+ } catch (error) {
893
+ clearExtractionDeadlineTimer();
894
+ if (typeof deadlineMs === "number" && Date.now() >= deadlineMs) {
895
+ throw new ExtractionDeadlineError("during_extract");
896
+ }
897
+ throw error;
898
+ }
899
+ try {
900
+ throwIfDeadlineExceeded("before_persist");
901
+ throwIfAborted("before_persist");
781
902
 
782
903
  // Defensive: validate extraction result before processing. Explicit
783
904
  // fail-closed callers, such as flush-plan import, must not observe
@@ -831,20 +952,26 @@ export class ExtractionRunCoordinator {
831
952
  if (extractionFingerprint && extractionRetryEnabled) {
832
953
  if (extractionFailure) {
833
954
  try {
834
- meta ??= await storage.loadMeta();
835
- await this.recordExtractionFailure(
836
- storage,
837
- selfNamespace,
838
- extractionFingerprint,
839
- result.extractionFailureClass ?? "provider_retryable",
840
- meta,
841
- Date.now(),
955
+ meta ??= await runDeadlineAware(() => storage.loadMeta(), "during_retry_bookkeeping_load", false);
956
+ await runDeadlineAware(
957
+ () =>
958
+ this.recordExtractionFailure(
959
+ storage,
960
+ selfNamespace,
961
+ extractionFingerprint,
962
+ result.extractionFailureClass ?? "provider_retryable",
963
+ meta!,
964
+ Date.now(),
965
+ ),
966
+ "during_retry_bookkeeping_save",
967
+ false,
842
968
  );
843
969
  // The buffer is retained below so the backoff gate can re-attempt
844
970
  // these turns after nextEligibleAt — clearing would orphan the
845
971
  // retry state.
846
972
  recordedRetryFailure = true;
847
973
  } catch (err) {
974
+ if (err instanceof ExtractionDeadlineError) throw err;
848
975
  // Fail-open (codex review): retry bookkeeping is best-effort — a
849
976
  // corrupt or locked meta store must not reject the extraction task.
850
977
  // With no backoff recorded there is nothing to retain for, so the
@@ -856,7 +983,7 @@ export class ExtractionRunCoordinator {
856
983
  // parked backoff for this fingerprint so it proceeds normally.
857
984
  this.resetProviderBreakerOnSuccess();
858
985
  try {
859
- meta ??= await storage.loadMeta();
986
+ meta ??= await runDeadlineAware(() => storage.loadMeta(), "during_retry_bookkeeping_load", false);
860
987
  // Hydrate the in-memory mirror from persisted meta before clearing.
861
988
  // A forced flush (forceExtractionAttempt) bypasses the retry gate and
862
989
  // never hydrated, so without this a stale persisted backoff entry
@@ -864,9 +991,10 @@ export class ExtractionRunCoordinator {
864
991
  // extraction until the timer expired (cursor/codex review).
865
992
  this.hydrateRetryStateFromMeta(selfNamespace, meta);
866
993
  if (this.clearExtractionRetryEntry(selfNamespace, extractionFingerprint, meta)) {
867
- await storage.saveMeta(meta);
994
+ await runDeadlineAware(() => storage.saveMeta(meta!), "during_retry_bookkeeping_save", false);
868
995
  }
869
996
  } catch (err) {
997
+ if (err instanceof ExtractionDeadlineError) throw err;
870
998
  log.warn("runExtraction: failed to persist cleared retry state (non-fatal)", err);
871
999
  }
872
1000
  }
@@ -897,7 +1025,7 @@ export class ExtractionRunCoordinator {
897
1025
  // Correction-only turns that meet char/user-turn thresholds but yield
898
1026
  // zero facts still need passive capture (review: "empty extraction skips
899
1027
  // capture"). selfNamespace/principal already resolved above.
900
- await this.deps.maybeCapturePassiveCorrections(normalizedTurns as BufferTurn[], {
1028
+ await runPassiveCapture(normalizedTurns as BufferTurn[], {
901
1029
  sessionKey,
902
1030
  principal,
903
1031
  namespace: selfNamespace,
@@ -922,12 +1050,18 @@ export class ExtractionRunCoordinator {
922
1050
  if (resolvePresentationCapabilities(this.config).threading && turns.length > 0) {
923
1051
  const lastTurn = turns[turns.length - 1];
924
1052
  try {
925
- threadIdForExtraction = await this.deps.getThreading().processTurn(lastTurn, []);
1053
+ threadIdForExtraction = await runDeadlineAware(
1054
+ () => this.deps.getThreading().processTurn(lastTurn, []),
1055
+ "during_threading",
1056
+ );
926
1057
  } catch (err) {
1058
+ if (err instanceof ExtractionDeadlineError) throw err;
927
1059
  // Fail-open: threading errors must not block memory persistence.
928
1060
  log.warn("[threading] processTurn failed before persistence (non-fatal)", err);
929
1061
  }
930
1062
  }
1063
+ throwIfDeadlineExceeded("before_persist");
1064
+ throwIfAborted("before_persist");
931
1065
 
932
1066
  const { persistedIds } = await this.deps.persistExtraction(
933
1067
  result,
@@ -937,94 +1071,61 @@ export class ExtractionRunCoordinator {
937
1071
  // Pass the KNOWN base namespace (NHIdx) so the catalog write touch records the
938
1072
  // real namespace rather than a guess decoded from the storage dir.
939
1073
  selfNamespace,
940
- scopeProfileGatePlan,
1074
+ scopeProfileWritePlan,
941
1075
  // Verbatim source turns for the faithfulness gate (#1576) so it can
942
1076
  // locate a quote per fact when #1575 spans are absent.
943
1077
  normalizedTurns
944
1078
  .map((t) => t.content)
945
1079
  .join("\n\n")
946
1080
  );
947
- let postPersistMetadataFailed = false;
948
- meta ??= await storage.loadMeta();
949
- if (extractionFingerprint && shouldPersistProcessedFingerprint) {
950
- try {
951
- await this.deps.recordProcessedExtractionFingerprint(storage, extractionFingerprint, meta);
952
- } catch (error) {
953
- log.warn(
954
- "runExtraction: failed to persist processed extraction fingerprint; continuing with buffer clear",
955
- error
956
- );
957
- postPersistMetadataFailed = true;
958
- }
959
- }
960
- // Persist extraction counters and processed fingerprints before running
961
- // follow-on helpers so replay dedupe survives any later non-essential
962
- // failure. If this aggregate meta write fails, still clear the buffer:
963
- // the durable memories are already written and replaying the same turns
964
- // would duplicate them.
965
- meta.extractionCount += 1;
966
- meta.lastExtractionAt = new Date().toISOString();
967
- meta.totalMemories += Array.isArray(result?.facts) ? result.facts.length : 0;
968
- meta.totalEntities += Array.isArray(result?.entities) ? result.entities.length : 0;
969
- try {
970
- await storage.saveMeta(meta);
971
- } catch (error) {
972
- log.warn(
973
- "runExtraction: failed to save extraction metadata after durable persistence; continuing with buffer clear",
974
- error
975
- );
976
- postPersistMetadataFailed = true;
977
- }
978
-
979
1081
  const durableOutputCount =
980
1082
  result.facts.length + result.entities.length + result.questions.length + result.profileUpdates.length;
981
-
982
- // Buffer retention for defer verdicts (issue #562, PR 2). When the judge
983
- // deferred at least one candidate, retain the tail of the current turn
984
- // window so the next extraction pass has the surrounding context that
985
- // may disambiguate the deferred fact. Non-defer runs clear the slot.
986
- //
987
- // Gated on:
988
- // - `clearBufferAfterExtraction` — replay / bulk-import paths call
989
- // `runExtraction` with this false and do not operate on live buffer
990
- // state. Writing retention there would create synthetic buffer
991
- // entries and cross-contaminate future live extractions.
992
- // - NOT `extractionJudgeShadow` — in shadow mode the judge is only
993
- // advisory; facts are still persisted regardless of verdict, so
994
- // retaining the turn window on top of a persisted write would both
995
- // waste buffer space and cause the same facts to re-enter the
996
- // pipeline on the next pass.
997
1083
  try {
998
- if (clearBufferAfterExtraction && !this.config.extractionJudgeShadow) {
999
- const deferredCount = this.deps.getLastPersistExtractionDeferredCount();
1000
- if (deferredCount > 0 && normalizedTurns.length > 0) {
1001
- await this.deps.getBuffer().retainDeferredTurns(bufferKey, normalizedTurns as BufferTurn[], 10);
1002
- } else {
1003
- await this.deps.getBuffer().retainDeferredTurns(bufferKey, [], 0);
1004
- }
1005
- }
1006
- } catch (err) {
1007
- // Fail-open: retention is a nice-to-have. If it fails the judge will
1008
- // still cap deferrals and convert to reject on the next pass.
1009
- log.debug(
1010
- `extraction-judge: defer retention failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`
1011
- );
1084
+ options.onDurableCommit?.();
1085
+ } catch (error) {
1086
+ log.warn("runExtraction: durable-commit callback failed", error);
1012
1087
  }
1013
-
1014
- await clearBuffer({ ignoreAbort: true });
1088
+ const runPostPersistBestEffort = runExtractionPostPersistBestEffort.bind(null, runDeadlineAware);
1089
+ const postPersist = await runExtractionPostPersist({
1090
+ result,
1091
+ storage,
1092
+ meta: meta ?? undefined,
1093
+ extractionFingerprint: extractionFingerprint ?? undefined,
1094
+ shouldPersistProcessedFingerprint,
1095
+ recordProcessedExtractionFingerprint: this.deps.recordProcessedExtractionFingerprint,
1096
+ runDeadlineAware,
1097
+ isDeadlineError: (error) => error instanceof ExtractionDeadlineError,
1098
+ createDeadlineError: (stage) => new ExtractionDeadlineError(stage),
1099
+ deadlineMs,
1100
+ clearBufferAfterExtraction,
1101
+ runPostPersistBestEffort,
1102
+ extractionJudgeShadow: this.config.extractionJudgeShadow,
1103
+ getDeferredCount: () => this.deps.getLastPersistExtractionDeferredCount(),
1104
+ normalizedTurns: normalizedTurns as BufferTurn[],
1105
+ bufferKey,
1106
+ retainDeferredTurns: (key, retainedTurns, max) =>
1107
+ this.deps.getBuffer().retainDeferredTurns(key, retainedTurns, max),
1108
+ clearBuffer,
1109
+ failOnExtractionFailure: options.failOnExtractionFailure === true,
1110
+ });
1111
+ meta = postPersist.meta ?? null;
1112
+ const postPersistMetadataFailed = postPersist.metadataFailed;
1015
1113
 
1016
1114
  // Passive correction capture (issue #1581) — detect corrections expressed
1017
1115
  // passively in the extracted turns and route to the Correction Contract.
1018
1116
  // Runs AFTER persistence + buffer clear so a capture failure never blocks
1019
1117
  // the extraction return. `clearBufferAfterExtraction` gates live-session
1020
1118
  // auto-apply (replay/import → queue-only).
1021
- await this.deps.maybeCapturePassiveCorrections(normalizedTurns as BufferTurn[], {
1022
- sessionKey,
1023
- principal,
1024
- namespace: selfNamespace,
1025
- bufferKey,
1026
- isLiveSession: clearBufferAfterExtraction,
1027
- });
1119
+ await runPostPersistBestEffort("during_post_persist_passive_capture", () =>
1120
+ runPassiveCapture(normalizedTurns as BufferTurn[], {
1121
+ sessionKey,
1122
+ principal,
1123
+ namespace: selfNamespace,
1124
+ bufferKey,
1125
+ isLiveSession: clearBufferAfterExtraction,
1126
+ abortSignal: extractionAbortSignal,
1127
+ }),
1128
+ );
1028
1129
 
1029
1130
  // Build memory box from this extraction (v8.0 Phase 2A)
1030
1131
  // Topics are derived from the current extraction's facts and entities only —
@@ -1035,31 +1136,30 @@ export class ExtractionRunCoordinator {
1035
1136
  // Derive episodic metadata from buffer turns (REMem-inspired)
1036
1137
  const firstUserTurn = turns.find((t) => t.role === "user");
1037
1138
  const boxGoal = firstUserTurn?.content?.slice(0, 100)?.trim() || undefined;
1038
- await this.deps
1039
- .boxBuilderFor(storage)
1040
- .onExtraction({
1139
+ await runPostPersistBestEffort("during_memory_box", () =>
1140
+ this.deps.boxBuilderFor(storage).onExtraction({
1041
1141
  topics: extractionTopics,
1042
1142
  memoryIds: persistedIds,
1043
1143
  timestamp: new Date().toISOString(),
1044
1144
  goal: boxGoal,
1045
- })
1046
- .catch((err) => log.warn("[boxes] onExtraction failed (non-fatal)", err));
1145
+ }),
1146
+ );
1047
1147
  }
1048
1148
 
1049
1149
  // Batch-append persisted IDs so non-fact memories (entities/questions) are
1050
1150
  // always attached to the thread. The helper excludes pending_review ids (#1635).
1051
1151
  if (resolvePresentationCapabilities(this.config).threading && threadIdForExtraction && persistedIds.length > 0) {
1052
- await this.deps.appendPersistedThreadEpisodes(threadIdForExtraction, persistedIds);
1152
+ await runPostPersistBestEffort("during_thread_episode_append", () =>
1153
+ this.deps.appendPersistedThreadEpisodes(threadIdForExtraction, persistedIds),
1154
+ );
1053
1155
  }
1054
1156
 
1055
1157
  // Thread title update for the already-established thread context.
1056
1158
  if (resolvePresentationCapabilities(this.config).threading && threadIdForExtraction) {
1057
1159
  const conversationContent = turns.map((t) => t.content).join(" ");
1058
- try {
1059
- await this.deps.getThreading().updateThreadTitle(threadIdForExtraction, conversationContent);
1060
- } catch (err) {
1061
- log.warn("[threading] updateThreadTitle failed after persistence (non-fatal)", err);
1062
- }
1160
+ await runPostPersistBestEffort("during_thread_title_update", () =>
1161
+ this.deps.getThreading().updateThreadTitle(threadIdForExtraction, conversationContent),
1162
+ );
1063
1163
  }
1064
1164
 
1065
1165
  // Check if consolidation is needed (debounced + non-zero gated).
@@ -1078,11 +1178,9 @@ export class ExtractionRunCoordinator {
1078
1178
  log.warn("runExtraction: QMD maintenance scheduling failed after persistence (non-fatal)", err);
1079
1179
  }
1080
1180
 
1081
- try {
1082
- await this.deps.runTierMigrationCycle(storage, "extraction");
1083
- } catch (err) {
1084
- log.warn("runExtraction: tier migration failed after persistence (non-fatal)", err);
1085
- }
1181
+ await runPostPersistBestEffort("during_tier_migration", () =>
1182
+ this.deps.runTierMigrationCycle(storage, "extraction"),
1183
+ );
1086
1184
 
1087
1185
  return {
1088
1186
  status: "completed",
@@ -1090,29 +1188,8 @@ export class ExtractionRunCoordinator {
1090
1188
  durableOutputCount,
1091
1189
  postPersistMetadataFailed,
1092
1190
  };
1093
- }
1094
- }
1095
-
1096
- /**
1097
- * Derive a single `sourceConnector` from a batch of turns.
1098
- * Returns the connector ONLY when ALL turns carry the same connector.
1099
- * Mixed tagged/untagged batches return undefined — extraction facts are
1100
- * persisted at batch level, so attributing them to the single tagged
1101
- * connector would falsely label facts from untagged turns.
1102
- * Conflicting connectors (different values) also return undefined.
1103
- */
1104
- export function deriveSourceConnector(turns: readonly BufferTurn[]): string | undefined {
1105
- if (turns.length === 0) return undefined;
1106
- const connectors = new Set<string>();
1107
- let untaggedCount = 0;
1108
- for (const turn of turns) {
1109
- if (typeof turn.sourceConnector === "string" && turn.sourceConnector.length > 0) {
1110
- connectors.add(turn.sourceConnector);
1111
- } else {
1112
- untaggedCount += 1;
1191
+ } finally {
1192
+ clearExtractionDeadlineTimer();
1113
1193
  }
1114
1194
  }
1115
- // All turns must agree on the same connector — no untagged turns mixed in.
1116
- if (untaggedCount > 0) return undefined;
1117
- return connectors.size === 1 ? [...connectors][0] : undefined;
1118
1195
  }