@remnic/core 9.6.1 → 9.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (332) hide show
  1. package/dist/access-admin-ops-surface.d.ts +7 -8
  2. package/dist/access-admin-ops-surface.js +8 -8
  3. package/dist/access-boundary.d.ts +9 -8
  4. package/dist/access-boundary.js +9 -9
  5. package/dist/access-cli.js +40 -40
  6. package/dist/access-http.d.ts +47 -16
  7. package/dist/access-http.js +13 -13
  8. package/dist/access-identity-continuity-surface.d.ts +6 -7
  9. package/dist/access-identity-continuity-surface.js +8 -8
  10. package/dist/access-lcm-surface.d.ts +7 -8
  11. package/dist/access-lcm-surface.js +8 -8
  12. package/dist/access-mcp.d.ts +13 -8
  13. package/dist/access-mcp.js +12 -12
  14. package/dist/access-observe-write-surface.d.ts +7 -8
  15. package/dist/access-observe-write-surface.js +8 -8
  16. package/dist/access-operations-batch.js +10 -10
  17. package/dist/access-operations.d.ts +8 -9
  18. package/dist/access-operations.js +11 -11
  19. package/dist/access-recall-surface.d.ts +7 -8
  20. package/dist/access-recall-surface.js +8 -8
  21. package/dist/access-schema.d.ts +6 -6
  22. package/dist/{access-service-CXJaql3A.d.ts → access-service-B_sc1w_k.d.ts} +9 -9
  23. package/dist/access-service.d.ts +7 -8
  24. package/dist/access-service.js +8 -8
  25. package/dist/access-surface-catalog.d.ts +7 -8
  26. package/dist/action-confidence.d.ts +1 -2
  27. package/dist/active-memory-bridge.d.ts +1 -2
  28. package/dist/active-recall.d.ts +1 -2
  29. package/dist/behavior-learner.d.ts +1 -2
  30. package/dist/behavior-signals.d.ts +1 -2
  31. package/dist/bootstrap.d.ts +6 -7
  32. package/dist/briefing.d.ts +2 -3
  33. package/dist/briefing.js +3 -3
  34. package/dist/buffer-surprise-report.d.ts +1 -2
  35. package/dist/buffer.d.ts +2 -3
  36. package/dist/buffer.js +1 -1
  37. package/dist/bulk-import/index.d.ts +3 -3
  38. package/dist/bulk-import/index.js +2 -2
  39. package/dist/calibration.d.ts +1 -2
  40. package/dist/capabilities.d.ts +1 -2
  41. package/dist/{catalog-C9cu67Ig.d.ts → catalog-9WanSGtt.d.ts} +1 -1
  42. package/dist/causal-behavior.d.ts +1 -2
  43. package/dist/causal-consolidation.d.ts +1 -2
  44. package/dist/causal-consolidation.js +4 -4
  45. package/dist/causal-trajectory-graph.d.ts +1 -2
  46. package/dist/{chunk-PNLFN2PZ.js → chunk-23G3T2Q5.js} +2 -2
  47. package/dist/{chunk-SIRPLF54.js → chunk-2K3OSLHE.js} +2 -2
  48. package/dist/{chunk-JS2HKKOK.js → chunk-3AC5UYKI.js} +1 -1
  49. package/dist/{chunk-JS2HKKOK.js.map → chunk-3AC5UYKI.js.map} +1 -1
  50. package/dist/{chunk-FKCRGLRD.js → chunk-4WRKKRTD.js} +266 -160
  51. package/dist/chunk-4WRKKRTD.js.map +1 -0
  52. package/dist/{chunk-LQHDIS7L.js → chunk-54BGIBCR.js} +2 -2
  53. package/dist/{chunk-PVUJ6WVV.js → chunk-5MH5NF3U.js} +5 -5
  54. package/dist/{chunk-PCLIWBGD.js → chunk-7ARV32PI.js} +8 -6
  55. package/dist/chunk-7ARV32PI.js.map +1 -0
  56. package/dist/{chunk-Y7UNNAZD.js → chunk-7ZYBDQ7V.js} +2 -2
  57. package/dist/{chunk-2PRQG7PV.js → chunk-AJVLDJU6.js} +1 -1
  58. package/dist/chunk-AJVLDJU6.js.map +1 -0
  59. package/dist/{chunk-LR5DZ56F.js → chunk-AXALUFAH.js} +2 -2
  60. package/dist/{chunk-AYZJID4S.js → chunk-B5GBWBCZ.js} +2 -2
  61. package/dist/{chunk-TLUDFPZL.js → chunk-CAZ3Z5M6.js} +2 -2
  62. package/dist/{chunk-RGK27ELN.js → chunk-DEKIXXIG.js} +2 -2
  63. package/dist/{chunk-2KK5WNQB.js → chunk-DQ42IQSK.js} +2 -2
  64. package/dist/{chunk-UFUKHGZD.js → chunk-F4I5NYBI.js} +89 -33
  65. package/dist/chunk-F4I5NYBI.js.map +1 -0
  66. package/dist/{chunk-HOJZMQ4J.js → chunk-FGDKJJZS.js} +2 -2
  67. package/dist/{chunk-ABP6QMVM.js → chunk-FQ7HAWWC.js} +2 -2
  68. package/dist/{chunk-CYWA2CXR.js → chunk-FVTLSFWU.js} +2 -2
  69. package/dist/{chunk-JJEJJ7RK.js → chunk-FXZTL7F7.js} +2 -2
  70. package/dist/{chunk-LADFWYHY.js → chunk-GOEV7CYF.js} +6 -6
  71. package/dist/chunk-GOEV7CYF.js.map +1 -0
  72. package/dist/{chunk-DANV2HTH.js → chunk-GQGDRLDM.js} +3 -3
  73. package/dist/{chunk-7F7Z6MOS.js → chunk-H4CKS5B2.js} +2 -2
  74. package/dist/{chunk-SVOHGUMI.js → chunk-H4FHR4UM.js} +174 -197
  75. package/dist/chunk-H4FHR4UM.js.map +1 -0
  76. package/dist/{chunk-CKITWVWJ.js → chunk-HSSERO3E.js} +2 -2
  77. package/dist/{chunk-23JFRB73.js → chunk-JODHPNLN.js} +2 -2
  78. package/dist/{chunk-DS7MM2ES.js → chunk-JZKR6ZAV.js} +2 -2
  79. package/dist/{chunk-BEM4D2QG.js → chunk-KJIJNLXT.js} +3 -3
  80. package/dist/{chunk-JSNVWNGX.js → chunk-KVSVVKHA.js} +2 -2
  81. package/dist/{chunk-4GNEDXDI.js → chunk-M67TLMP6.js} +2 -2
  82. package/dist/chunk-M67TLMP6.js.map +1 -0
  83. package/dist/{chunk-MC4FJXPA.js → chunk-MMACM5G2.js} +2 -2
  84. package/dist/{chunk-OUELRE5E.js → chunk-NOUJNHWG.js} +1 -1
  85. package/dist/chunk-NOUJNHWG.js.map +1 -0
  86. package/dist/{chunk-3XIY7MDQ.js → chunk-O6YI4CVI.js} +2 -2
  87. package/dist/{chunk-F53UF7CI.js → chunk-QLVLW7UD.js} +6 -2
  88. package/dist/{chunk-F53UF7CI.js.map → chunk-QLVLW7UD.js.map} +1 -1
  89. package/dist/{chunk-XCL57M6Q.js → chunk-RRKPLLCC.js} +21 -20
  90. package/dist/chunk-RRKPLLCC.js.map +1 -0
  91. package/dist/{chunk-72WG5QKN.js → chunk-SDAXPXER.js} +2 -2
  92. package/dist/{chunk-4LV23CHK.js → chunk-VHJARF5R.js} +2 -2
  93. package/dist/{chunk-H2DXDQCQ.js → chunk-VRJG3HIZ.js} +46 -42
  94. package/dist/chunk-VRJG3HIZ.js.map +1 -0
  95. package/dist/{chunk-6MMONPHM.js → chunk-W2AHSCKY.js} +2 -2
  96. package/dist/{chunk-P454KXD6.js → chunk-YAWJMR3Y.js} +21 -21
  97. package/dist/{chunk-K5LLP4CY.js → chunk-ZGPDSEKG.js} +20 -9
  98. package/dist/chunk-ZGPDSEKG.js.map +1 -0
  99. package/dist/{cli-CqdPA6z9.d.ts → cli-CnRsWREp.d.ts} +4 -5
  100. package/dist/cli.d.ts +9 -10
  101. package/dist/cli.js +32 -32
  102. package/dist/compounding/engine.d.ts +2 -3
  103. package/dist/compounding/engine.js +3 -3
  104. package/dist/compounding/preference-consolidator.d.ts +1 -2
  105. package/dist/compression-optimizer.d.ts +1 -2
  106. package/dist/config.d.ts +1 -2
  107. package/dist/connectors/codex-materialize-runner.d.ts +1 -2
  108. package/dist/connectors/codex-materialize-runner.js +3 -3
  109. package/dist/connectors/codex-materialize.d.ts +1 -2
  110. package/dist/connectors/index.d.ts +1 -2
  111. package/dist/connectors/index.js +3 -3
  112. package/dist/consolidation-provenance-check.d.ts +2 -3
  113. package/dist/consolidation-undo.d.ts +2 -3
  114. package/dist/contradiction/index.d.ts +3 -4
  115. package/dist/conversation-index/backend.d.ts +1 -2
  116. package/dist/conversation-index/chunker.d.ts +1 -2
  117. package/dist/conversation-index/faiss-adapter.d.ts +1 -2
  118. package/dist/conversation-index/indexer.d.ts +1 -2
  119. package/dist/conversation-index/search.d.ts +1 -2
  120. package/dist/day-summary.d.ts +1 -2
  121. package/dist/delinearize.d.ts +1 -2
  122. package/dist/direct-answer-wiring.d.ts +1 -2
  123. package/dist/direct-answer.d.ts +1 -2
  124. package/dist/embedding-fallback.d.ts +1 -2
  125. package/dist/enrichment/index.d.ts +1 -2
  126. package/dist/entity-retrieval.d.ts +2 -3
  127. package/dist/entity-retrieval.js +3 -3
  128. package/dist/entity-schema.d.ts +1 -2
  129. package/dist/explicit-capture.d.ts +8 -7
  130. package/dist/explicit-capture.js +1 -1
  131. package/dist/extraction-faithfulness.d.ts +1 -2
  132. package/dist/extraction-judge-telemetry.d.ts +1 -2
  133. package/dist/extraction-judge-training.d.ts +1 -2
  134. package/dist/extraction-judge.d.ts +1 -2
  135. package/dist/extraction.d.ts +1 -2
  136. package/dist/fallback-llm.d.ts +1 -2
  137. package/dist/graph-dashboard-diff.d.ts +1 -2
  138. package/dist/graph-dashboard-key.d.ts +1 -2
  139. package/dist/graph-dashboard-parser.d.ts +1 -2
  140. package/dist/graph-edge-reinforcement.d.ts +1 -2
  141. package/dist/graph-snapshot.d.ts +1 -2
  142. package/dist/graph.d.ts +1 -2
  143. package/dist/identity-continuity.d.ts +1 -2
  144. package/dist/importance.d.ts +1 -2
  145. package/dist/importers/index.d.ts +1 -1
  146. package/dist/importers/index.js +2 -2
  147. package/dist/index.d.ts +14 -15
  148. package/dist/index.js +40 -40
  149. package/dist/intent.d.ts +1 -2
  150. package/dist/lcm/engine.d.ts +1 -2
  151. package/dist/lcm/index.d.ts +1 -2
  152. package/dist/lcm/tools.d.ts +1 -2
  153. package/dist/lifecycle.d.ts +1 -2
  154. package/dist/live-connectors-runner.d.ts +1 -2
  155. package/dist/local-llm.d.ts +1 -2
  156. package/dist/local-model-endpoint.d.ts +1 -2
  157. package/dist/maintenance/memory-governance.d.ts +1 -2
  158. package/dist/maintenance/memory-governance.js +3 -3
  159. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +3 -3
  160. package/dist/maintenance/rebuild-memory-projection.js +4 -4
  161. package/dist/mcp-memory-inspector-app.d.ts +7 -8
  162. package/dist/memory-action-policy.d.ts +1 -2
  163. package/dist/memory-cache.d.ts +1 -2
  164. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -2
  165. package/dist/memory-projection-store.d.ts +1 -2
  166. package/dist/memory-provenance.d.ts +1 -2
  167. package/dist/memory-worth-outcomes.d.ts +2 -3
  168. package/dist/models-json.d.ts +1 -2
  169. package/dist/namespaces/migrate.d.ts +3 -4
  170. package/dist/namespaces/migrate.js +4 -4
  171. package/dist/namespaces/principal.d.ts +1 -2
  172. package/dist/namespaces/search.d.ts +1 -2
  173. package/dist/namespaces/storage.d.ts +3 -4
  174. package/dist/namespaces/storage.js +3 -3
  175. package/dist/native-knowledge.d.ts +1 -2
  176. package/dist/operator-toolkit.d.ts +2 -3
  177. package/dist/operator-toolkit.js +7 -7
  178. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -3
  179. package/dist/orchestration/maintenance.d.ts +2 -3
  180. package/dist/orchestration/maintenance.js +5 -5
  181. package/dist/{orchestrator-CWFrT8AF.d.ts → orchestrator-NRLVXPpH.d.ts} +40 -15
  182. package/dist/orchestrator.d.ts +6 -7
  183. package/dist/orchestrator.js +40 -40
  184. package/dist/patterns-cli.d.ts +1 -2
  185. package/dist/{pipeline-DmqtvION.d.ts → pipeline-CiD86zIk.d.ts} +1 -1
  186. package/dist/policy-runtime.d.ts +1 -2
  187. package/dist/provenance.d.ts +1 -2
  188. package/dist/{qmd--fnPMK60.d.ts → qmd-Bef7DDuQ.d.ts} +1 -1
  189. package/dist/qmd-preflight.d.ts +2 -3
  190. package/dist/qmd-recall-cache.d.ts +1 -2
  191. package/dist/qmd.d.ts +2 -3
  192. package/dist/recall-disclosure-escalation.d.ts +1 -2
  193. package/dist/recall-explain-renderer.d.ts +1 -2
  194. package/dist/recall-explain-renderer.js +3 -3
  195. package/dist/recall-planner-llm.d.ts +1 -2
  196. package/dist/recall-state.d.ts +1 -2
  197. package/dist/recall-tag-filter.d.ts +1 -2
  198. package/dist/recall-timings.d.ts +1 -2
  199. package/dist/recall-xray-cli.d.ts +1 -2
  200. package/dist/recall-xray-cli.js +4 -4
  201. package/dist/recall-xray-renderer.d.ts +1 -2
  202. package/dist/recall-xray-renderer.js +3 -3
  203. package/dist/recall-xray.d.ts +1 -2
  204. package/dist/recall-xray.js +2 -2
  205. package/dist/replay/normalizers/chatgpt.d.ts +1 -0
  206. package/dist/replay/normalizers/chatgpt.js +3 -3
  207. package/dist/replay/normalizers/claude.d.ts +1 -0
  208. package/dist/replay/normalizers/claude.js +3 -3
  209. package/dist/replay/normalizers/openclaw.d.ts +1 -0
  210. package/dist/replay/normalizers/openclaw.js +3 -3
  211. package/dist/replay/normalizers/shared.d.ts +1 -0
  212. package/dist/replay/normalizers/shared.js +2 -2
  213. package/dist/replay/runner.d.ts +1 -0
  214. package/dist/replay/runner.js +2 -2
  215. package/dist/replay/types.d.ts +2 -1
  216. package/dist/replay/types.js +1 -1
  217. package/dist/resolve-auth-token.d.ts +1 -2
  218. package/dist/retrieval-agents.d.ts +2 -3
  219. package/dist/retrieval-tiers.d.ts +1 -2
  220. package/dist/routing/engine.d.ts +1 -2
  221. package/dist/routing/store.d.ts +1 -2
  222. package/dist/schemas.d.ts +22 -22
  223. package/dist/search/embed-helper.d.ts +1 -2
  224. package/dist/search/factory.d.ts +1 -2
  225. package/dist/search/index.d.ts +1 -2
  226. package/dist/search/lancedb-backend.d.ts +1 -2
  227. package/dist/search/meilisearch-backend.d.ts +1 -2
  228. package/dist/search/noop-backend.d.ts +1 -2
  229. package/dist/search/orama-backend.d.ts +1 -2
  230. package/dist/search/port.d.ts +1 -2
  231. package/dist/search/remote-backend.d.ts +1 -2
  232. package/dist/{semantic-DJR8_DMQ.d.ts → semantic-TddhF0y7.d.ts} +17 -1
  233. package/dist/{semantic-consolidation-O5lMZ4LG.d.ts → semantic-consolidation-BTShF2Fo.d.ts} +1 -1
  234. package/dist/semantic-consolidation.d.ts +2 -3
  235. package/dist/semantic-consolidation.js +4 -4
  236. package/dist/semantic-rule-promotion.js +3 -3
  237. package/dist/semantic-rule-verifier.d.ts +1 -2
  238. package/dist/semantic-rule-verifier.js +3 -3
  239. package/dist/session-observer-bands.d.ts +1 -2
  240. package/dist/session-observer-state.d.ts +1 -2
  241. package/dist/shared-context/manager.d.ts +3 -4
  242. package/dist/signal.d.ts +1 -2
  243. package/dist/{storage-C1maCNbc.d.ts → storage-DxPDAiU9.d.ts} +5 -1
  244. package/dist/storage.d.ts +2 -3
  245. package/dist/storage.js +2 -2
  246. package/dist/summarizer.d.ts +1 -2
  247. package/dist/summary-snapshot.d.ts +1 -2
  248. package/dist/temporal-supersession.d.ts +2 -3
  249. package/dist/temporal-validity.d.ts +1 -2
  250. package/dist/threading.d.ts +1 -2
  251. package/dist/tier-migration.d.ts +2 -3
  252. package/dist/tier-routing.d.ts +1 -2
  253. package/dist/topics.d.ts +1 -2
  254. package/dist/transcript.d.ts +1 -2
  255. package/dist/trust-score-stage.d.ts +1 -2
  256. package/dist/trust-score.d.ts +1 -2
  257. package/dist/{types-CGgcNWCG.d.ts → types-DDUDgd7T.d.ts} +93 -4
  258. package/dist/types.d.ts +1 -2
  259. package/dist/types.js +1 -1
  260. package/dist/utility-runtime.d.ts +1 -2
  261. package/dist/verified-recall.js +3 -3
  262. package/package.json +2 -2
  263. package/src/access-admin-ops-surface.ts +1 -0
  264. package/src/access-boundary.ts +2 -0
  265. package/src/access-http.test.ts +129 -0
  266. package/src/access-http.ts +92 -23
  267. package/src/access-mcp.ts +11 -2
  268. package/src/access-observe-write-surface.ts +87 -113
  269. package/src/access-operations-batch.ts +2 -2
  270. package/src/access-operations.ts +17 -16
  271. package/src/access-service.ts +11 -3
  272. package/src/admin/admin-surfaces.ts +2 -0
  273. package/src/buffer.ts +6 -1
  274. package/src/bulk-import/types.ts +2 -1
  275. package/src/coding/architecture-surfaces.test.ts +79 -5
  276. package/src/coding/architecture-surfaces.ts +7 -1
  277. package/src/coding/decision-surfaces.test.ts +1 -0
  278. package/src/coding/decision-surfaces.ts +4 -0
  279. package/src/connector-provenance-lifecycle.test.ts +618 -0
  280. package/src/connector-provenance.test.ts +219 -0
  281. package/src/dedup/semantic.test.ts +105 -0
  282. package/src/dedup/semantic.ts +69 -1
  283. package/src/explicit-capture.ts +21 -4
  284. package/src/orchestration/extraction-persist.ts +154 -6
  285. package/src/orchestration/extraction-run.ts +138 -228
  286. package/src/orchestration/persistence-index.ts +40 -5
  287. package/src/orchestration/turn-ingestion.ts +16 -9
  288. package/src/orchestrator-connector-dedup.test.ts +1082 -0
  289. package/src/orchestrator.ts +7 -11
  290. package/src/replay/types.ts +2 -1
  291. package/src/storage.ts +426 -553
  292. package/src/types.ts +11 -2
  293. package/src/utils/yaml-scalar.ts +20 -0
  294. package/dist/chunk-2PRQG7PV.js.map +0 -1
  295. package/dist/chunk-4GNEDXDI.js.map +0 -1
  296. package/dist/chunk-FKCRGLRD.js.map +0 -1
  297. package/dist/chunk-H2DXDQCQ.js.map +0 -1
  298. package/dist/chunk-K5LLP4CY.js.map +0 -1
  299. package/dist/chunk-LADFWYHY.js.map +0 -1
  300. package/dist/chunk-OUELRE5E.js.map +0 -1
  301. package/dist/chunk-PCLIWBGD.js.map +0 -1
  302. package/dist/chunk-SVOHGUMI.js.map +0 -1
  303. package/dist/chunk-UFUKHGZD.js.map +0 -1
  304. package/dist/chunk-XCL57M6Q.js.map +0 -1
  305. package/dist/types-gb_SNtjR.d.ts +0 -85
  306. /package/dist/{chunk-PNLFN2PZ.js.map → chunk-23G3T2Q5.js.map} +0 -0
  307. /package/dist/{chunk-SIRPLF54.js.map → chunk-2K3OSLHE.js.map} +0 -0
  308. /package/dist/{chunk-LQHDIS7L.js.map → chunk-54BGIBCR.js.map} +0 -0
  309. /package/dist/{chunk-PVUJ6WVV.js.map → chunk-5MH5NF3U.js.map} +0 -0
  310. /package/dist/{chunk-Y7UNNAZD.js.map → chunk-7ZYBDQ7V.js.map} +0 -0
  311. /package/dist/{chunk-LR5DZ56F.js.map → chunk-AXALUFAH.js.map} +0 -0
  312. /package/dist/{chunk-AYZJID4S.js.map → chunk-B5GBWBCZ.js.map} +0 -0
  313. /package/dist/{chunk-TLUDFPZL.js.map → chunk-CAZ3Z5M6.js.map} +0 -0
  314. /package/dist/{chunk-RGK27ELN.js.map → chunk-DEKIXXIG.js.map} +0 -0
  315. /package/dist/{chunk-2KK5WNQB.js.map → chunk-DQ42IQSK.js.map} +0 -0
  316. /package/dist/{chunk-HOJZMQ4J.js.map → chunk-FGDKJJZS.js.map} +0 -0
  317. /package/dist/{chunk-ABP6QMVM.js.map → chunk-FQ7HAWWC.js.map} +0 -0
  318. /package/dist/{chunk-CYWA2CXR.js.map → chunk-FVTLSFWU.js.map} +0 -0
  319. /package/dist/{chunk-JJEJJ7RK.js.map → chunk-FXZTL7F7.js.map} +0 -0
  320. /package/dist/{chunk-DANV2HTH.js.map → chunk-GQGDRLDM.js.map} +0 -0
  321. /package/dist/{chunk-7F7Z6MOS.js.map → chunk-H4CKS5B2.js.map} +0 -0
  322. /package/dist/{chunk-CKITWVWJ.js.map → chunk-HSSERO3E.js.map} +0 -0
  323. /package/dist/{chunk-23JFRB73.js.map → chunk-JODHPNLN.js.map} +0 -0
  324. /package/dist/{chunk-DS7MM2ES.js.map → chunk-JZKR6ZAV.js.map} +0 -0
  325. /package/dist/{chunk-BEM4D2QG.js.map → chunk-KJIJNLXT.js.map} +0 -0
  326. /package/dist/{chunk-JSNVWNGX.js.map → chunk-KVSVVKHA.js.map} +0 -0
  327. /package/dist/{chunk-MC4FJXPA.js.map → chunk-MMACM5G2.js.map} +0 -0
  328. /package/dist/{chunk-3XIY7MDQ.js.map → chunk-O6YI4CVI.js.map} +0 -0
  329. /package/dist/{chunk-72WG5QKN.js.map → chunk-SDAXPXER.js.map} +0 -0
  330. /package/dist/{chunk-4LV23CHK.js.map → chunk-VHJARF5R.js.map} +0 -0
  331. /package/dist/{chunk-6MMONPHM.js.map → chunk-W2AHSCKY.js.map} +0 -0
  332. /package/dist/{chunk-P454KXD6.js.map → chunk-YAWJMR3Y.js.map} +0 -0
@@ -28,14 +28,8 @@ import type { ExtractionEngine } from "../extraction.js";
28
28
  import type { SmartBuffer } from "../buffer.js";
29
29
  import type { ThreadingManager } from "../threading.js";
30
30
  import type { BoxBuilder } from "../boxes.js";
31
- import {
32
- resolvePrincipal,
33
- defaultNamespaceForPrincipal,
34
- } from "../namespaces/principal.js";
35
- import {
36
- resolveScopeProfilePlan,
37
- type ResolvedScopeProfilePlan,
38
- } from "../namespaces/scope-profiles.js";
31
+ import { resolvePrincipal, defaultNamespaceForPrincipal } from "../namespaces/principal.js";
32
+ import { resolveScopeProfilePlan, type ResolvedScopeProfilePlan } from "../namespaces/scope-profiles.js";
39
33
  import { resolveCodingNamespaceOverlay } from "../coding/coding-namespace.js";
40
34
  import type { CodingContext } from "../types.js";
41
35
  import {
@@ -46,11 +40,7 @@ import {
46
40
  } from "../capabilities.js";
47
41
  import { parseFlexibleIsoTimestamp } from "../utils/iso-timestamp.js";
48
42
  import { log } from "../logger.js";
49
- import type {
50
- PluginConfig,
51
- BufferTurn,
52
- ExtractionResult,
53
- } from "../types.js";
43
+ import type { PluginConfig, BufferTurn, ExtractionResult } from "../types.js";
54
44
  import type { TierMigrationCycleSummary } from "../recall-state.js";
55
45
 
56
46
  export interface ExtractionRunResult {
@@ -67,20 +57,20 @@ export interface ExtractionRunResult {
67
57
  export interface ExtractionRunCoordinatorDeps {
68
58
  config: PluginConfig;
69
59
  getBuffer: () => SmartBuffer;
70
- getExtraction: () => ExtractionEngine;
71
- getStorageRouter: () => NamespaceStorageRouter;
72
- getThreading: () => ThreadingManager;
60
+ getExtraction: () => Pick<ExtractionEngine, "extract">;
61
+ getStorageRouter: () => Pick<NamespaceStorageRouter, "storageFor">;
62
+ getThreading: () => Pick<ThreadingManager, "processTurn" | "updateThreadTitle">;
73
63
 
74
64
  persistExtraction: (
75
65
  result: ExtractionResult,
76
66
  storage: StorageManager,
77
67
  threadIdForExtraction?: string | null,
78
- sourceContext?: { sessionKey?: string; principal?: string; validAt?: string },
68
+ sourceContext?: { sessionKey?: string; principal?: string; validAt?: string; sourceConnector?: string },
79
69
  baseNamespace?: string,
80
70
  scopeProfileWritePlan?: ResolvedScopeProfilePlan | null,
81
71
  sourceText?: string,
82
72
  graphCaps?: GraphConstructionCapabilitySet,
83
- lifecycleCaps?: MemoryLifecycleCapabilitySet,
73
+ lifecycleCaps?: MemoryLifecycleCapabilitySet
84
74
  ) => Promise<string[]>;
85
75
 
86
76
  maybeCapturePassiveCorrections: (
@@ -91,17 +81,14 @@ export interface ExtractionRunCoordinatorDeps {
91
81
  namespace: string;
92
82
  bufferKey: string;
93
83
  isLiveSession: boolean;
94
- },
84
+ }
95
85
  ) => Promise<void>;
96
86
 
97
87
  resolveSelfNamespace: (sessionKey?: string) => string;
98
88
  getCodingContextForSession: (sessionKey: string) => CodingContext | null;
99
89
  applyCodingNamespaceOverlay: (sessionKey: string, namespace: string) => string;
100
- boxBuilderFor: (storage: StorageManager) => BoxBuilder;
101
- appendPersistedThreadEpisodes: (
102
- threadId: string,
103
- ids: string[],
104
- ) => Promise<void>;
90
+ boxBuilderFor: (storage: StorageManager) => Pick<BoxBuilder, "onExtraction">;
91
+ appendPersistedThreadEpisodes: (threadId: string, ids: string[]) => Promise<void>;
105
92
  maybeScheduleConsolidation: (nonZero: boolean) => void;
106
93
  requestQmdMaintenance: () => void;
107
94
  runTierMigrationCycle: (
@@ -111,13 +98,13 @@ export interface ExtractionRunCoordinatorDeps {
111
98
  dryRun?: boolean;
112
99
  limitOverride?: number;
113
100
  force?: boolean;
114
- },
101
+ }
115
102
  ) => Promise<TierMigrationCycleSummary>;
116
103
  getLastPersistExtractionDeferredCount: () => number;
117
104
  recordProcessedExtractionFingerprint: (
118
105
  storage: StorageManager,
119
106
  fingerprint: string,
120
- preloadedMeta?: Awaited<ReturnType<StorageManager["loadMeta"]>>,
107
+ preloadedMeta?: Awaited<ReturnType<StorageManager["loadMeta"]>>
121
108
  ) => Promise<void>;
122
109
  }
123
110
 
@@ -151,9 +138,7 @@ export function deriveTopicsFromExtraction(result: ExtractionResult): string[] {
151
138
  export class ExtractionRunCoordinator {
152
139
  private readonly recentExtractionFingerprints = new Map<string, number>();
153
140
 
154
- constructor(
155
- private readonly deps: ExtractionRunCoordinatorDeps,
156
- ) {}
141
+ constructor(private readonly deps: ExtractionRunCoordinatorDeps) {}
157
142
 
158
143
  private get config(): PluginConfig {
159
144
  return this.deps.config;
@@ -177,7 +162,6 @@ export class ExtractionRunCoordinator {
177
162
  return latestMs === null ? undefined : new Date(latestMs).toISOString();
178
163
  }
179
164
 
180
-
181
165
  // -------------------------------------------------------------------------
182
166
  // Extraction dedupe fingerprint helpers
183
167
  // -------------------------------------------------------------------------
@@ -187,10 +171,7 @@ export class ExtractionRunCoordinator {
187
171
  return turns
188
172
  .filter((turn) => turn.role === "user" || turn.role === "assistant")
189
173
  .map((turn) => {
190
- if (
191
- typeof turn.turnFingerprint === "string" &&
192
- turn.turnFingerprint.length > 0
193
- ) {
174
+ if (typeof turn.turnFingerprint === "string" && turn.turnFingerprint.length > 0) {
194
175
  return `fp:${turn.turnFingerprint}`;
195
176
  }
196
177
  return `${turn.role}:${(turn.content ?? "").replace(/\s+/g, " ").trim().slice(0, this.config.extractionMaxTurnChars)}`;
@@ -198,24 +179,13 @@ export class ExtractionRunCoordinator {
198
179
  .filter((value) => value.length > 0);
199
180
  }
200
181
 
201
-
202
- buildExtractionFingerprint(
203
- turns: BufferTurn[],
204
- bufferKey: string,
205
- ): string | null {
182
+ buildExtractionFingerprint(turns: BufferTurn[], bufferKey: string): string | null {
206
183
  const normalized = this.normalizeExtractionFingerprintTurns(turns).join("\n");
207
184
  if (!normalized) return null;
208
- return createHash("sha256")
209
- .update(`${bufferKey}\n${normalized}`)
210
- .digest("hex");
185
+ return createHash("sha256").update(`${bufferKey}\n${normalized}`).digest("hex");
211
186
  }
212
187
 
213
-
214
-
215
- shouldQueueExtraction(
216
- turns: BufferTurn[],
217
- options: { commit?: boolean; bufferKey?: string } = {},
218
- ): boolean {
188
+ shouldQueueExtraction(turns: BufferTurn[], options: { commit?: boolean; bufferKey?: string } = {}): boolean {
219
189
  const lifecycleCaps = resolveMemoryLifecycleCapabilities(this.config);
220
190
  if (!lifecycleCaps.extractionDedupe) return true;
221
191
  if (!Array.isArray(turns) || turns.length === 0) return false;
@@ -234,13 +204,8 @@ export class ExtractionRunCoordinator {
234
204
  this.recentExtractionFingerprints.set(fingerprint, now);
235
205
  }
236
206
  // Keep this cache bounded to avoid unbounded growth.
237
- if (
238
- options.commit !== false &&
239
- this.recentExtractionFingerprints.size > 200
240
- ) {
241
- const entries = Array.from(
242
- this.recentExtractionFingerprints.entries(),
243
- ).sort((a, b) => a[1] - b[1]);
207
+ if (options.commit !== false && this.recentExtractionFingerprints.size > 200) {
208
+ const entries = Array.from(this.recentExtractionFingerprints.entries()).sort((a, b) => a[1] - b[1]);
244
209
  for (const [key] of entries.slice(0, entries.length - 200)) {
245
210
  this.recentExtractionFingerprints.delete(key);
246
211
  }
@@ -249,7 +214,6 @@ export class ExtractionRunCoordinator {
249
214
  return true;
250
215
  }
251
216
 
252
-
253
217
  // -------------------------------------------------------------------------
254
218
  // Processed-fingerprint recording
255
219
  // -------------------------------------------------------------------------
@@ -257,15 +221,12 @@ export class ExtractionRunCoordinator {
257
221
  async recordProcessedExtractionFingerprint(
258
222
  storage: StorageManager,
259
223
  fingerprint: string,
260
- preloadedMeta?: Awaited<ReturnType<StorageManager["loadMeta"]>>,
224
+ preloadedMeta?: Awaited<ReturnType<StorageManager["loadMeta"]>>
261
225
  ): Promise<void> {
262
226
  const meta = preloadedMeta ?? (await storage.loadMeta());
263
227
  const observedAt = new Date().toISOString();
264
228
  const seen = new Map(
265
- (meta.processedExtractionFingerprints ?? []).map((entry) => [
266
- entry.fingerprint,
267
- entry.observedAt,
268
- ]),
229
+ (meta.processedExtractionFingerprints ?? []).map((entry) => [entry.fingerprint, entry.observedAt])
269
230
  );
270
231
  seen.set(fingerprint, observedAt);
271
232
  meta.processedExtractionFingerprints = Array.from(seen.entries())
@@ -277,7 +238,6 @@ export class ExtractionRunCoordinator {
277
238
  }
278
239
  }
279
240
 
280
-
281
241
  // -------------------------------------------------------------------------
282
242
  // Main extraction pipeline
283
243
  // -------------------------------------------------------------------------
@@ -308,18 +268,14 @@ export class ExtractionRunCoordinator {
308
268
  * (un-prefixed) key and storage is pinned via `writeNamespaceOverride`.
309
269
  */
310
270
  principalOverride?: string;
311
- } = {},
271
+ } = {}
312
272
  ): Promise<ExtractionRunResult> {
313
273
  log.debug(`running extraction on ${turns.length} turns`);
314
- const clearBufferAfterExtraction =
315
- options.clearBufferAfterExtraction ?? true;
274
+ const clearBufferAfterExtraction = options.clearBufferAfterExtraction ?? true;
316
275
  const skipCharThreshold = options.skipCharThreshold ?? false;
317
276
  const skipUserTurnThreshold = options.skipUserTurnThreshold ?? false;
318
277
  const deadlineMs =
319
- typeof options.deadlineMs === "number" &&
320
- Number.isFinite(options.deadlineMs)
321
- ? options.deadlineMs
322
- : undefined;
278
+ typeof options.deadlineMs === "number" && Number.isFinite(options.deadlineMs) ? options.deadlineMs : undefined;
323
279
  const bufferKey = options.bufferKey ?? turns[0]?.sessionKey ?? "default";
324
280
  const throwIfDeadlineExceeded = (stage: string): void => {
325
281
  if (typeof deadlineMs === "number" && Date.now() > deadlineMs) {
@@ -352,25 +308,25 @@ export class ExtractionRunCoordinator {
352
308
  }
353
309
 
354
310
  const normalizedTurns = turns
355
- .filter(
356
- (t) =>
357
- (t.role === "user" || t.role === "assistant") &&
358
- typeof t.content === "string",
359
- )
311
+ .filter((t) => (t.role === "user" || t.role === "assistant") && typeof t.content === "string")
360
312
  .map((t) => ({
361
313
  ...t,
362
314
  content: t.content.trim().slice(0, this.config.extractionMaxTurnChars),
363
315
  }))
364
316
  .filter((t) => t.content.length > 0);
365
- const targetTurns = normalizedTurns.filter(
366
- (turn) => turn.extractionContextOnly !== true,
367
- );
317
+ const targetTurns = normalizedTurns.filter((turn) => turn.extractionContextOnly !== true);
368
318
  if (targetTurns.length === 0) {
369
319
  log.debug("skipping extraction: no non-context turns after normalization");
370
320
  // Context-only turns may still contain corrections (review: "context-only
371
321
  // turns skip capture"). Scan normalizedTurns before clearing the buffer.
372
322
  if (normalizedTurns.length > 0) {
373
- await this.deps.maybeCapturePassiveCorrections(normalizedTurns as BufferTurn[], { sessionKey, principal: resolvePrincipal(sessionKey, this.config), namespace: this.deps.resolveSelfNamespace(sessionKey), bufferKey, isLiveSession: clearBufferAfterExtraction });
323
+ await this.deps.maybeCapturePassiveCorrections(normalizedTurns as BufferTurn[], {
324
+ sessionKey,
325
+ principal: resolvePrincipal(sessionKey, this.config),
326
+ namespace: this.deps.resolveSelfNamespace(sessionKey),
327
+ bufferKey,
328
+ isLiveSession: clearBufferAfterExtraction,
329
+ });
374
330
  }
375
331
  await clearBuffer();
376
332
  return { status: "skipped", reason: "empty_normalized_turns", persistedCount: 0, durableOutputCount: 0 };
@@ -380,18 +336,11 @@ export class ExtractionRunCoordinator {
380
336
  throwIfAborted("before_extract");
381
337
 
382
338
  const userTurns = targetTurns.filter((t) => t.role === "user");
383
- const totalChars = targetTurns.reduce(
384
- (sum, t) => sum + t.content.length,
385
- 0,
386
- );
339
+ const totalChars = targetTurns.reduce((sum, t) => sum + t.content.length, 0);
387
340
  const belowCharThreshold = totalChars < this.config.extractionMinChars;
388
- const belowUserTurnThreshold =
389
- !skipUserTurnThreshold &&
390
- userTurns.length < this.config.extractionMinUserTurns;
341
+ const belowUserTurnThreshold = !skipUserTurnThreshold && userTurns.length < this.config.extractionMinUserTurns;
391
342
  if ((!skipCharThreshold && belowCharThreshold) || belowUserTurnThreshold) {
392
- log.debug(
393
- `skipping extraction: below threshold (totalChars=${totalChars}, userTurns=${userTurns.length})`,
394
- );
343
+ log.debug(`skipping extraction: below threshold (totalChars=${totalChars}, userTurns=${userTurns.length})`);
395
344
  await clearBuffer();
396
345
  // Passive correction capture runs even when extraction is skipped for
397
346
  // being below the char/user-turn threshold (review: "skipped extraction
@@ -407,14 +356,30 @@ export class ExtractionRunCoordinator {
407
356
  typeof options.principalOverride === "string" && options.principalOverride.length > 0
408
357
  ? options.principalOverride
409
358
  : resolvePrincipal(sessionKey, this.config);
410
- const captureWO = typeof options.writeNamespaceOverride === "string" && options.writeNamespaceOverride.length > 0
411
- ? options.writeNamespaceOverride : undefined;
359
+ const captureWO =
360
+ typeof options.writeNamespaceOverride === "string" && options.writeNamespaceOverride.length > 0
361
+ ? options.writeNamespaceOverride
362
+ : undefined;
412
363
  const captureCodingCtx = sessionKey ? this.deps.getCodingContextForSession(sessionKey) : null;
413
- const captureCodingOv = resolveCodingNamespaceOverlay(captureCodingCtx, this.config.codingMode, this.config.defaultNamespace);
414
- const captureScopePlan = resolveScopeProfilePlan({ config: this.config, principal: capturePrincipal, codingContext: captureCodingCtx, codingOverlay: captureCodingOv });
415
- const captureNamespace = captureWO ?? captureScopePlan?.writeNamespace ?? this.deps.resolveSelfNamespace(sessionKey);
364
+ const captureCodingOv = resolveCodingNamespaceOverlay(
365
+ captureCodingCtx,
366
+ this.config.codingMode,
367
+ this.config.defaultNamespace
368
+ );
369
+ const captureScopePlan = resolveScopeProfilePlan({
370
+ config: this.config,
371
+ principal: capturePrincipal,
372
+ codingContext: captureCodingCtx,
373
+ codingOverlay: captureCodingOv,
374
+ });
375
+ const captureNamespace =
376
+ captureWO ?? captureScopePlan?.writeNamespace ?? this.deps.resolveSelfNamespace(sessionKey);
416
377
  await this.deps.maybeCapturePassiveCorrections(normalizedTurns as BufferTurn[], {
417
- sessionKey, principal: capturePrincipal, namespace: captureNamespace, bufferKey, isLiveSession: clearBufferAfterExtraction,
378
+ sessionKey,
379
+ principal: capturePrincipal,
380
+ namespace: captureNamespace,
381
+ bufferKey,
382
+ isLiveSession: clearBufferAfterExtraction,
418
383
  });
419
384
  }
420
385
  return {
@@ -433,8 +398,7 @@ export class ExtractionRunCoordinator {
433
398
  // namespace-prefixed key would collapse to `default`. The ORIGINAL,
434
399
  // un-prefixed session key still drives threading.
435
400
  const principal =
436
- typeof options.principalOverride === "string" &&
437
- options.principalOverride.length > 0
401
+ typeof options.principalOverride === "string" && options.principalOverride.length > 0
438
402
  ? options.principalOverride
439
403
  : resolvePrincipal(sessionKey, this.config);
440
404
  // Write path — explicit callers still win. Otherwise, an active hosted
@@ -442,17 +406,14 @@ export class ExtractionRunCoordinator {
442
406
  // in the same layer that profile recall searches. Without a profile, preserve
443
407
  // the existing coding-agent overlay behavior (issue #569).
444
408
  const explicitWriteNamespace =
445
- typeof options.writeNamespaceOverride === "string" &&
446
- options.writeNamespaceOverride.length > 0
409
+ typeof options.writeNamespaceOverride === "string" && options.writeNamespaceOverride.length > 0
447
410
  ? options.writeNamespaceOverride
448
411
  : undefined;
449
- const codingContextForWrite = sessionKey
450
- ? this.deps.getCodingContextForSession(sessionKey)
451
- : null;
412
+ const codingContextForWrite = sessionKey ? this.deps.getCodingContextForSession(sessionKey) : null;
452
413
  const codingOverlayForWrite = resolveCodingNamespaceOverlay(
453
414
  codingContextForWrite,
454
415
  this.config.codingMode,
455
- this.config.defaultNamespace,
416
+ this.config.defaultNamespace
456
417
  );
457
418
  const scopeProfileGatePlan = resolveScopeProfilePlan({
458
419
  config: this.config,
@@ -462,15 +423,13 @@ export class ExtractionRunCoordinator {
462
423
  });
463
424
  const scopeProfileWritePlan = explicitWriteNamespace ? null : scopeProfileGatePlan;
464
425
  if (scopeProfileWritePlan) {
465
- const selectedLayer = scopeProfileWritePlan.layers.find(
466
- (layer) => layer.id === scopeProfileWritePlan.writeLayer,
467
- );
426
+ const selectedLayer = scopeProfileWritePlan.layers.find((layer) => layer.id === scopeProfileWritePlan.writeLayer);
468
427
  const writeNamespaceReadable = scopeProfileWritePlan.readNamespaces.includes(
469
- scopeProfileWritePlan.writeNamespace,
428
+ scopeProfileWritePlan.writeNamespace
470
429
  );
471
430
  if (!selectedLayer?.writable || !writeNamespaceReadable) {
472
431
  log.warn(
473
- `runExtraction: skipping scope profile ${scopeProfileWritePlan.profileId} because write layer ${scopeProfileWritePlan.writeLayer} is not writable inside the profile read stack`,
432
+ `runExtraction: skipping scope profile ${scopeProfileWritePlan.profileId} because write layer ${scopeProfileWritePlan.writeLayer} is not writable inside the profile read stack`
474
433
  );
475
434
  await clearBuffer();
476
435
  return {
@@ -484,32 +443,17 @@ export class ExtractionRunCoordinator {
484
443
  const selfNamespace =
485
444
  explicitWriteNamespace ??
486
445
  scopeProfileWritePlan?.writeNamespace ??
487
- this.deps.applyCodingNamespaceOverlay(
488
- sessionKey,
489
- defaultNamespaceForPrincipal(principal, this.config),
490
- );
446
+ this.deps.applyCodingNamespaceOverlay(sessionKey, defaultNamespaceForPrincipal(principal, this.config));
491
447
  const storage = await this.deps.getStorageRouter().storageFor(selfNamespace);
492
- const shouldPersistProcessedFingerprint = targetTurns.some(
493
- (turn) => turn.persistProcessedFingerprint === true,
494
- );
495
- const extractionFingerprint = this.buildExtractionFingerprint(
496
- targetTurns,
497
- bufferKey,
498
- );
499
- let meta =
500
- extractionFingerprint && shouldPersistProcessedFingerprint
501
- ? await storage.loadMeta()
502
- : null;
448
+ const shouldPersistProcessedFingerprint = targetTurns.some((turn) => turn.persistProcessedFingerprint === true);
449
+ const extractionFingerprint = this.buildExtractionFingerprint(targetTurns, bufferKey);
450
+ let meta = extractionFingerprint && shouldPersistProcessedFingerprint ? await storage.loadMeta() : null;
503
451
  if (
504
452
  extractionFingerprint &&
505
453
  shouldPersistProcessedFingerprint &&
506
- (meta?.processedExtractionFingerprints ?? []).some(
507
- (entry) => entry.fingerprint === extractionFingerprint,
508
- )
454
+ (meta?.processedExtractionFingerprints ?? []).some((entry) => entry.fingerprint === extractionFingerprint)
509
455
  ) {
510
- log.debug(
511
- `runExtraction: skipping already-processed extraction fingerprint for ${bufferKey}`,
512
- );
456
+ log.debug(`runExtraction: skipping already-processed extraction fingerprint for ${bufferKey}`);
513
457
  await clearBuffer();
514
458
  return {
515
459
  status: "skipped",
@@ -522,12 +466,9 @@ export class ExtractionRunCoordinator {
522
466
  // Pass existing entity names so the LLM can reuse them instead of inventing variants
523
467
  const existingEntities = await storage.listEntityNames();
524
468
  const result = await raceRecallAbort(
525
- this.deps.getExtraction().extract(
526
- normalizedTurns,
527
- existingEntities,
528
- ),
469
+ this.deps.getExtraction().extract(normalizedTurns, existingEntities),
529
470
  options.abortSignal,
530
- "extraction aborted (during_extract)",
471
+ "extraction aborted (during_extract)"
531
472
  );
532
473
  throwIfDeadlineExceeded("before_persist");
533
474
  throwIfAborted("before_persist");
@@ -557,16 +498,10 @@ export class ExtractionRunCoordinator {
557
498
  .filter(([, value]) => !Array.isArray(value))
558
499
  .map(([field]) => field);
559
500
  if (invalidExtractionResultFields.length > 0) {
560
- log.warn(
561
- "runExtraction: extraction returned invalid collection fields",
562
- {
563
- invalidFields: invalidExtractionResultFields,
564
- resultKeys:
565
- typeof result === "object" && result !== null
566
- ? Object.keys(result)
567
- : [],
568
- },
569
- );
501
+ log.warn("runExtraction: extraction returned invalid collection fields", {
502
+ invalidFields: invalidExtractionResultFields,
503
+ resultKeys: typeof result === "object" && result !== null ? Object.keys(result) : [],
504
+ });
570
505
  if (options.failOnExtractionFailure) {
571
506
  throw new Error("extraction failed: invalid_extraction_result");
572
507
  }
@@ -579,8 +514,7 @@ export class ExtractionRunCoordinator {
579
514
  };
580
515
  }
581
516
  const extractionFailure =
582
- typeof result.extractionFailure === "string" &&
583
- result.extractionFailure.trim().length > 0
517
+ typeof result.extractionFailure === "string" && result.extractionFailure.trim().length > 0
584
518
  ? result.extractionFailure
585
519
  : undefined;
586
520
  if (options.failOnExtractionFailure && extractionFailure) {
@@ -592,26 +526,16 @@ export class ExtractionRunCoordinator {
592
526
  result.questions.length === 0 &&
593
527
  result.profileUpdates.length === 0
594
528
  ) {
595
- log.debug(
596
- "runExtraction: extraction produced no durable outputs; skipping persistence",
597
- );
529
+ log.debug("runExtraction: extraction produced no durable outputs; skipping persistence");
598
530
  if (extractionFailure) {
599
531
  log.warn(
600
532
  "runExtraction: extraction reported failure with no durable outputs; not marking fingerprint processed",
601
- { extractionFailure },
533
+ { extractionFailure }
602
534
  );
603
535
  }
604
- if (
605
- extractionFingerprint &&
606
- shouldPersistProcessedFingerprint &&
607
- !extractionFailure
608
- ) {
536
+ if (extractionFingerprint && shouldPersistProcessedFingerprint && !extractionFailure) {
609
537
  meta ??= await storage.loadMeta();
610
- await this.deps.recordProcessedExtractionFingerprint(
611
- storage,
612
- extractionFingerprint,
613
- meta,
614
- );
538
+ await this.deps.recordProcessedExtractionFingerprint(storage, extractionFingerprint, meta);
615
539
  meta.extractionCount += 1;
616
540
  meta.lastExtractionAt = new Date().toISOString();
617
541
  await storage.saveMeta(meta);
@@ -619,7 +543,13 @@ export class ExtractionRunCoordinator {
619
543
  // Correction-only turns that meet char/user-turn thresholds but yield
620
544
  // zero facts still need passive capture (review: "empty extraction skips
621
545
  // capture"). selfNamespace/principal already resolved above.
622
- await this.deps.maybeCapturePassiveCorrections(normalizedTurns as BufferTurn[], { sessionKey, principal, namespace: selfNamespace, bufferKey, isLiveSession: clearBufferAfterExtraction });
546
+ await this.deps.maybeCapturePassiveCorrections(normalizedTurns as BufferTurn[], {
547
+ sessionKey,
548
+ principal,
549
+ namespace: selfNamespace,
550
+ bufferKey,
551
+ isLiveSession: clearBufferAfterExtraction,
552
+ });
623
553
  await clearBuffer();
624
554
  return { status: "skipped", reason: "empty_extraction_result", persistedCount: 0, durableOutputCount: 0 };
625
555
  }
@@ -631,10 +561,7 @@ export class ExtractionRunCoordinator {
631
561
  threadIdForExtraction = await this.deps.getThreading().processTurn(lastTurn, []);
632
562
  } catch (err) {
633
563
  // Fail-open: threading errors must not block memory persistence.
634
- log.warn(
635
- "[threading] processTurn failed before persistence (non-fatal)",
636
- err,
637
- );
564
+ log.warn("[threading] processTurn failed before persistence (non-fatal)", err);
638
565
  }
639
566
  }
640
567
 
@@ -642,28 +569,26 @@ export class ExtractionRunCoordinator {
642
569
  result,
643
570
  storage,
644
571
  threadIdForExtraction,
645
- { sessionKey, principal, validAt: sourceValidAt },
572
+ { sessionKey, principal, validAt: sourceValidAt, sourceConnector: deriveSourceConnector(targetTurns as BufferTurn[]) },
646
573
  // Pass the KNOWN base namespace (NHIdx) so the catalog write touch records the
647
574
  // real namespace rather than a guess decoded from the storage dir.
648
575
  selfNamespace,
649
576
  scopeProfileGatePlan,
650
577
  // Verbatim source turns for the faithfulness gate (#1576) so it can
651
578
  // locate a quote per fact when #1575 spans are absent.
652
- normalizedTurns.map((t) => t.content).join("\n\n"),
579
+ normalizedTurns
580
+ .map((t) => t.content)
581
+ .join("\n\n")
653
582
  );
654
583
  let postPersistMetadataFailed = false;
655
584
  meta ??= await storage.loadMeta();
656
585
  if (extractionFingerprint && shouldPersistProcessedFingerprint) {
657
586
  try {
658
- await this.deps.recordProcessedExtractionFingerprint(
659
- storage,
660
- extractionFingerprint,
661
- meta,
662
- );
587
+ await this.deps.recordProcessedExtractionFingerprint(storage, extractionFingerprint, meta);
663
588
  } catch (error) {
664
589
  log.warn(
665
590
  "runExtraction: failed to persist processed extraction fingerprint; continuing with buffer clear",
666
- error,
591
+ error
667
592
  );
668
593
  postPersistMetadataFailed = true;
669
594
  }
@@ -675,27 +600,20 @@ export class ExtractionRunCoordinator {
675
600
  // would duplicate them.
676
601
  meta.extractionCount += 1;
677
602
  meta.lastExtractionAt = new Date().toISOString();
678
- meta.totalMemories += Array.isArray(result?.facts)
679
- ? result.facts.length
680
- : 0;
681
- meta.totalEntities += Array.isArray(result?.entities)
682
- ? result.entities.length
683
- : 0;
603
+ meta.totalMemories += Array.isArray(result?.facts) ? result.facts.length : 0;
604
+ meta.totalEntities += Array.isArray(result?.entities) ? result.entities.length : 0;
684
605
  try {
685
606
  await storage.saveMeta(meta);
686
607
  } catch (error) {
687
608
  log.warn(
688
609
  "runExtraction: failed to save extraction metadata after durable persistence; continuing with buffer clear",
689
- error,
610
+ error
690
611
  );
691
612
  postPersistMetadataFailed = true;
692
613
  }
693
614
 
694
615
  const durableOutputCount =
695
- result.facts.length +
696
- result.entities.length +
697
- result.questions.length +
698
- result.profileUpdates.length;
616
+ result.facts.length + result.entities.length + result.questions.length + result.profileUpdates.length;
699
617
 
700
618
  // Buffer retention for defer verdicts (issue #562, PR 2). When the judge
701
619
  // deferred at least one candidate, retain the tail of the current turn
@@ -713,17 +631,10 @@ export class ExtractionRunCoordinator {
713
631
  // waste buffer space and cause the same facts to re-enter the
714
632
  // pipeline on the next pass.
715
633
  try {
716
- if (
717
- clearBufferAfterExtraction &&
718
- !this.config.extractionJudgeShadow
719
- ) {
634
+ if (clearBufferAfterExtraction && !this.config.extractionJudgeShadow) {
720
635
  const deferredCount = this.deps.getLastPersistExtractionDeferredCount();
721
636
  if (deferredCount > 0 && normalizedTurns.length > 0) {
722
- await this.deps.getBuffer().retainDeferredTurns(
723
- bufferKey,
724
- normalizedTurns as BufferTurn[],
725
- 10,
726
- );
637
+ await this.deps.getBuffer().retainDeferredTurns(bufferKey, normalizedTurns as BufferTurn[], 10);
727
638
  } else {
728
639
  await this.deps.getBuffer().retainDeferredTurns(bufferKey, [], 0);
729
640
  }
@@ -732,7 +643,7 @@ export class ExtractionRunCoordinator {
732
643
  // Fail-open: retention is a nice-to-have. If it fails the judge will
733
644
  // still cap deferrals and convert to reject on the next pass.
734
645
  log.debug(
735
- `extraction-judge: defer retention failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`,
646
+ `extraction-judge: defer retention failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`
736
647
  );
737
648
  }
738
649
 
@@ -759,46 +670,31 @@ export class ExtractionRunCoordinator {
759
670
  const extractionTopics = deriveTopicsFromExtraction(result);
760
671
  // Derive episodic metadata from buffer turns (REMem-inspired)
761
672
  const firstUserTurn = turns.find((t) => t.role === "user");
762
- const boxGoal =
763
- firstUserTurn?.content?.slice(0, 100)?.trim() || undefined;
764
- await this.deps.boxBuilderFor(storage)
673
+ const boxGoal = firstUserTurn?.content?.slice(0, 100)?.trim() || undefined;
674
+ await this.deps
675
+ .boxBuilderFor(storage)
765
676
  .onExtraction({
766
677
  topics: extractionTopics,
767
678
  memoryIds: persistedIds,
768
679
  timestamp: new Date().toISOString(),
769
680
  goal: boxGoal,
770
681
  })
771
- .catch((err) =>
772
- log.warn("[boxes] onExtraction failed (non-fatal)", err),
773
- );
682
+ .catch((err) => log.warn("[boxes] onExtraction failed (non-fatal)", err));
774
683
  }
775
684
 
776
685
  // Batch-append persisted IDs so non-fact memories (entities/questions) are
777
686
  // always attached to the thread. The helper excludes pending_review ids (#1635).
778
- if (
779
- resolvePresentationCapabilities(this.config).threading &&
780
- threadIdForExtraction &&
781
- persistedIds.length > 0
782
- ) {
783
- await this.deps.appendPersistedThreadEpisodes(
784
- threadIdForExtraction,
785
- persistedIds,
786
- );
687
+ if (resolvePresentationCapabilities(this.config).threading && threadIdForExtraction && persistedIds.length > 0) {
688
+ await this.deps.appendPersistedThreadEpisodes(threadIdForExtraction, persistedIds);
787
689
  }
788
690
 
789
691
  // Thread title update for the already-established thread context.
790
692
  if (resolvePresentationCapabilities(this.config).threading && threadIdForExtraction) {
791
693
  const conversationContent = turns.map((t) => t.content).join(" ");
792
694
  try {
793
- await this.deps.getThreading().updateThreadTitle(
794
- threadIdForExtraction,
795
- conversationContent,
796
- );
695
+ await this.deps.getThreading().updateThreadTitle(threadIdForExtraction, conversationContent);
797
696
  } catch (err) {
798
- log.warn(
799
- "[threading] updateThreadTitle failed after persistence (non-fatal)",
800
- err,
801
- );
697
+ log.warn("[threading] updateThreadTitle failed after persistence (non-fatal)", err);
802
698
  }
803
699
  }
804
700
 
@@ -809,28 +705,19 @@ export class ExtractionRunCoordinator {
809
705
  // the scheduler with the rest of the cadence state (issue #1526 PR1).
810
706
  this.deps.maybeScheduleConsolidation(nonZeroExtraction);
811
707
  } catch (err) {
812
- log.warn(
813
- "runExtraction: consolidation scheduling failed after persistence (non-fatal)",
814
- err,
815
- );
708
+ log.warn("runExtraction: consolidation scheduling failed after persistence (non-fatal)", err);
816
709
  }
817
710
 
818
711
  try {
819
712
  this.deps.requestQmdMaintenance();
820
713
  } catch (err) {
821
- log.warn(
822
- "runExtraction: QMD maintenance scheduling failed after persistence (non-fatal)",
823
- err,
824
- );
714
+ log.warn("runExtraction: QMD maintenance scheduling failed after persistence (non-fatal)", err);
825
715
  }
826
716
 
827
717
  try {
828
718
  await this.deps.runTierMigrationCycle(storage, "extraction");
829
719
  } catch (err) {
830
- log.warn(
831
- "runExtraction: tier migration failed after persistence (non-fatal)",
832
- err,
833
- );
720
+ log.warn("runExtraction: tier migration failed after persistence (non-fatal)", err);
834
721
  }
835
722
 
836
723
  return {
@@ -842,3 +729,26 @@ export class ExtractionRunCoordinator {
842
729
  }
843
730
  }
844
731
 
732
+ /**
733
+ * Derive a single `sourceConnector` from a batch of turns.
734
+ * Returns the connector ONLY when ALL turns carry the same connector.
735
+ * Mixed tagged/untagged batches return undefined — extraction facts are
736
+ * persisted at batch level, so attributing them to the single tagged
737
+ * connector would falsely label facts from untagged turns.
738
+ * Conflicting connectors (different values) also return undefined.
739
+ */
740
+ export function deriveSourceConnector(turns: readonly BufferTurn[]): string | undefined {
741
+ if (turns.length === 0) return undefined;
742
+ const connectors = new Set<string>();
743
+ let untaggedCount = 0;
744
+ for (const turn of turns) {
745
+ if (typeof turn.sourceConnector === "string" && turn.sourceConnector.length > 0) {
746
+ connectors.add(turn.sourceConnector);
747
+ } else {
748
+ untaggedCount += 1;
749
+ }
750
+ }
751
+ // All turns must agree on the same connector — no untagged turns mixed in.
752
+ if (untaggedCount > 0) return undefined;
753
+ return connectors.size === 1 ? [...connectors][0] : undefined;
754
+ }