@remnic/core 9.45.5 → 9.46.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (402) hide show
  1. package/dist/access-admin-ops-surface.d.ts +10 -10
  2. package/dist/access-admin-ops-surface.js +24 -24
  3. package/dist/access-authorization-probe.d.ts +10 -10
  4. package/dist/access-authorization-probe.js +25 -25
  5. package/dist/access-boundary.d.ts +10 -10
  6. package/dist/access-boundary.js +24 -24
  7. package/dist/access-cli.js +73 -71
  8. package/dist/access-cli.js.map +1 -1
  9. package/dist/access-coding-context-resolution.d.ts +1 -1
  10. package/dist/access-extraction-force-flush.d.ts +10 -10
  11. package/dist/access-extraction-force-flush.js +24 -24
  12. package/dist/access-health-types.d.ts +1 -1
  13. package/dist/access-http-lcm-compaction.d.ts +10 -10
  14. package/dist/access-http-lifecycle-flush.d.ts +10 -10
  15. package/dist/access-http-offline-stream.d.ts +10 -10
  16. package/dist/access-http-query.js +24 -24
  17. package/dist/access-http.d.ts +10 -10
  18. package/dist/access-http.js +31 -31
  19. package/dist/access-identity-continuity-surface.d.ts +8 -8
  20. package/dist/access-identity-continuity-surface.js +24 -24
  21. package/dist/access-lcm-surface.d.ts +10 -10
  22. package/dist/access-lcm-surface.js +24 -24
  23. package/dist/access-mcp.d.ts +10 -10
  24. package/dist/access-mcp.js +28 -28
  25. package/dist/access-memory-search-fanout.d.ts +2 -2
  26. package/dist/{access-namespace-preflight-CWUDwB4M.d.ts → access-namespace-preflight--H6HwN85.d.ts} +1 -1
  27. package/dist/access-namespace-preflight.d.ts +3 -3
  28. package/dist/access-namespace-preflight.js +24 -24
  29. package/dist/access-observe-write-surface.d.ts +10 -10
  30. package/dist/access-observe-write-surface.js +24 -24
  31. package/dist/access-offline-manifest.d.ts +8 -8
  32. package/dist/access-operations-batch.js +25 -25
  33. package/dist/access-operations.d.ts +13 -13
  34. package/dist/access-operations.js +27 -27
  35. package/dist/access-recall-concurrency.d.ts +10 -10
  36. package/dist/access-recall-concurrency.js +24 -24
  37. package/dist/access-recall-response.d.ts +10 -10
  38. package/dist/access-recall-response.js +24 -24
  39. package/dist/access-recall-surface.d.ts +10 -10
  40. package/dist/access-recall-surface.js +24 -24
  41. package/dist/access-schema.d.ts +84 -84
  42. package/dist/access-service-helpers.d.ts +10 -10
  43. package/dist/{access-service-CNN89xyn.d.ts → access-service-loJadYd7.d.ts} +7 -7
  44. package/dist/access-service.d.ts +10 -10
  45. package/dist/access-service.js +24 -24
  46. package/dist/access-surface-catalog.d.ts +10 -10
  47. package/dist/access-wearables-meetings-surface.d.ts +3 -3
  48. package/dist/action-confidence.d.ts +1 -1
  49. package/dist/active-memory-bridge.d.ts +1 -1
  50. package/dist/active-recall.d.ts +1 -1
  51. package/dist/ambient-provenance.d.ts +109 -0
  52. package/dist/ambient-provenance.js +22 -0
  53. package/dist/ambient-provenance.js.map +1 -0
  54. package/dist/{auto-sync-UPTUVE4X.js → auto-sync-7O6RP2RT.js} +3 -2
  55. package/dist/{auto-sync-UPTUVE4X.js.map → auto-sync-7O6RP2RT.js.map} +1 -1
  56. package/dist/behavior-learner.d.ts +1 -1
  57. package/dist/behavior-signals.d.ts +1 -1
  58. package/dist/bootstrap.d.ts +8 -8
  59. package/dist/briefing.d.ts +2 -2
  60. package/dist/briefing.js +5 -5
  61. package/dist/buffer-surprise-report.d.ts +1 -1
  62. package/dist/buffer-turn-helpers.d.ts +1 -1
  63. package/dist/buffer-turn-helpers.js +1 -1
  64. package/dist/buffer.d.ts +2 -2
  65. package/dist/buffer.js +2 -2
  66. package/dist/bulk-import/index.d.ts +3 -3
  67. package/dist/calibration.d.ts +1 -1
  68. package/dist/capabilities.d.ts +1 -1
  69. package/dist/{catalog-bctMURhN.d.ts → catalog-Iwbk9h5E.d.ts} +1 -1
  70. package/dist/causal-behavior.d.ts +1 -1
  71. package/dist/causal-consolidation.d.ts +1 -1
  72. package/dist/causal-consolidation.js +6 -6
  73. package/dist/causal-trajectory-graph.d.ts +1 -1
  74. package/dist/{chunk-LEPOP46W.js → chunk-227UFSEQ.js} +4 -4
  75. package/dist/{chunk-ER7W6H4T.js → chunk-2AD5SXO4.js} +5 -2
  76. package/dist/chunk-2AD5SXO4.js.map +1 -0
  77. package/dist/{chunk-CX4ZUMC6.js → chunk-2DSUT7DZ.js} +9 -9
  78. package/dist/{chunk-DZPFMQZ7.js → chunk-3S63QIFQ.js} +1 -1
  79. package/dist/chunk-3S63QIFQ.js.map +1 -0
  80. package/dist/{chunk-DAJPRXBR.js → chunk-47J4EDGR.js} +5 -5
  81. package/dist/{chunk-QVJPH7SH.js → chunk-5LRDM4R7.js} +40 -40
  82. package/dist/{chunk-XAGYS7X6.js → chunk-5R6S4DJZ.js} +7 -7
  83. package/dist/{chunk-QR6G5KF4.js → chunk-5VQPZ4GV.js} +3 -3
  84. package/dist/{chunk-E5XQIWAL.js → chunk-64BHFFKZ.js} +2 -2
  85. package/dist/{chunk-GRUYA45P.js → chunk-6H744ZBH.js} +3 -3
  86. package/dist/{chunk-6ISC4AVB.js → chunk-BDNL34CT.js} +2 -2
  87. package/dist/{chunk-GQL52GQ5.js → chunk-CDKHKZL5.js} +15 -12
  88. package/dist/chunk-CDKHKZL5.js.map +1 -0
  89. package/dist/{chunk-AC5OWJLV.js → chunk-E4FOAENZ.js} +4 -4
  90. package/dist/{chunk-UW66OZTD.js → chunk-FULKGK7P.js} +2 -2
  91. package/dist/{chunk-V2TNYF6Q.js → chunk-HJ3SVHUW.js} +3 -3
  92. package/dist/{chunk-A4IJCD2Z.js → chunk-HXXTXLKA.js} +2 -2
  93. package/dist/{chunk-FUCYDRE5.js → chunk-IDE5AR5W.js} +11 -11
  94. package/dist/{chunk-2FSTB6BJ.js → chunk-ILBLZ5Y5.js} +2 -2
  95. package/dist/chunk-J7T4MX7P.js +131 -0
  96. package/dist/chunk-J7T4MX7P.js.map +1 -0
  97. package/dist/{chunk-WZ3TOIDW.js → chunk-JFO5QZ7N.js} +2 -2
  98. package/dist/{chunk-FR27HBBA.js → chunk-KA47C7BF.js} +2 -2
  99. package/dist/{chunk-JZH4LWHB.js → chunk-KMWYZTIE.js} +2 -2
  100. package/dist/{chunk-LPYDOLSM.js → chunk-L7G72WY4.js} +2 -2
  101. package/dist/{chunk-AMAUBUKY.js → chunk-LI7OE7G6.js} +2 -2
  102. package/dist/{chunk-MO4HSXHZ.js → chunk-M2INOSTA.js} +2 -2
  103. package/dist/{chunk-GODSPTOT.js → chunk-M32URQXV.js} +652 -601
  104. package/dist/chunk-M32URQXV.js.map +1 -0
  105. package/dist/{chunk-HFPASSFV.js → chunk-P2OTGMTR.js} +2 -2
  106. package/dist/{chunk-W2V7UESE.js → chunk-PFJXPOD4.js} +2 -2
  107. package/dist/{chunk-R3KHKU7M.js → chunk-PKCLQ7KM.js} +43 -143
  108. package/dist/chunk-PKCLQ7KM.js.map +1 -0
  109. package/dist/chunk-QW27DOHG.js +84 -0
  110. package/dist/chunk-QW27DOHG.js.map +1 -0
  111. package/dist/{chunk-QUENPGWZ.js → chunk-RCU25Z66.js} +24 -24
  112. package/dist/{chunk-QUENPGWZ.js.map → chunk-RCU25Z66.js.map} +1 -1
  113. package/dist/{chunk-NEF7LR5J.js → chunk-RVPTS4U5.js} +2 -2
  114. package/dist/{chunk-W3IC7QOY.js → chunk-RVYVMQK4.js} +3 -3
  115. package/dist/{chunk-GUAL4PQH.js → chunk-ST5IEK3H.js} +2 -2
  116. package/dist/{chunk-O4KMGRXE.js → chunk-SXWHN7GC.js} +2 -2
  117. package/dist/{chunk-RNHVVMFN.js → chunk-T6JRMVOV.js} +2 -2
  118. package/dist/{chunk-5QJQHBLE.js → chunk-U6JGQ37L.js} +2 -2
  119. package/dist/{chunk-UV72DEZD.js → chunk-UYUYZ4WY.js} +36 -10
  120. package/dist/chunk-UYUYZ4WY.js.map +1 -0
  121. package/dist/{chunk-HC3PBX23.js → chunk-V5FE3UAI.js} +2 -2
  122. package/dist/{chunk-ERZ2R42X.js → chunk-W2ZSNIBP.js} +2 -2
  123. package/dist/{chunk-3COYXTGM.js → chunk-WHWDBTCJ.js} +2 -2
  124. package/dist/{chunk-FIK3CQNA.js → chunk-WOC6DXP5.js} +3 -3
  125. package/dist/{chunk-4FBVAGV3.js → chunk-XBMJ35IP.js} +2 -2
  126. package/dist/{chunk-GFCVUY6G.js → chunk-YWCOJOUI.js} +2 -2
  127. package/dist/{chunk-2X2T2UVE.js → chunk-ZDAYWJV6.js} +1 -1
  128. package/dist/chunk-ZDAYWJV6.js.map +1 -0
  129. package/dist/{cli-BHBswmUa.d.ts → cli-D_RcqXfZ.d.ts} +5 -5
  130. package/dist/cli.d.ts +12 -12
  131. package/dist/cli.js +50 -50
  132. package/dist/compat/checks.js +1 -1
  133. package/dist/compounding/engine.d.ts +2 -2
  134. package/dist/compounding/engine.js +5 -5
  135. package/dist/compounding/preference-consolidator.d.ts +1 -1
  136. package/dist/compression-optimizer.d.ts +1 -1
  137. package/dist/config.d.ts +1 -1
  138. package/dist/connectors/codex-materialize-runner.d.ts +1 -1
  139. package/dist/connectors/codex-materialize-runner.js +5 -5
  140. package/dist/connectors/codex-materialize.d.ts +1 -1
  141. package/dist/connectors/index.d.ts +1 -1
  142. package/dist/connectors/index.js +5 -5
  143. package/dist/consolidation-provenance-check.d.ts +2 -2
  144. package/dist/consolidation-undo.d.ts +2 -2
  145. package/dist/contradiction/index.d.ts +2 -2
  146. package/dist/converge-config.d.ts +1 -1
  147. package/dist/convergence-refresh.d.ts +2 -2
  148. package/dist/convergence-refresh.js +7 -7
  149. package/dist/conversation-index/backend.d.ts +1 -1
  150. package/dist/conversation-index/backend.js +2 -2
  151. package/dist/conversation-index/chunker.d.ts +1 -1
  152. package/dist/conversation-index/faiss-adapter.d.ts +1 -1
  153. package/dist/conversation-index/indexer.d.ts +1 -1
  154. package/dist/conversation-index/search.d.ts +1 -1
  155. package/dist/corpus-watermark.d.ts +1 -1
  156. package/dist/corpus-watermark.js +13 -13
  157. package/dist/day-summary.d.ts +1 -1
  158. package/dist/delinearize.d.ts +1 -1
  159. package/dist/direct-answer-wiring.d.ts +1 -1
  160. package/dist/direct-answer.d.ts +1 -1
  161. package/dist/embedding-fallback.d.ts +1 -1
  162. package/dist/enrichment/index.d.ts +1 -1
  163. package/dist/entity-retrieval.d.ts +2 -2
  164. package/dist/entity-retrieval.js +5 -5
  165. package/dist/entity-schema.d.ts +1 -1
  166. package/dist/explicit-capture.d.ts +8 -8
  167. package/dist/external-wiki-access.d.ts +10 -10
  168. package/dist/external-wiki-access.js +25 -25
  169. package/dist/external-wiki-collection-registration.d.ts +1 -1
  170. package/dist/external-wiki-collection.d.ts +1 -1
  171. package/dist/external-wiki-mcp-tools.d.ts +10 -10
  172. package/dist/extraction-error-classification.d.ts +1 -1
  173. package/dist/extraction-faithfulness.d.ts +1 -1
  174. package/dist/extraction-judge-telemetry.d.ts +1 -1
  175. package/dist/extraction-judge-training.d.ts +1 -1
  176. package/dist/extraction-judge.d.ts +1 -1
  177. package/dist/extraction-liveness.d.ts +1 -1
  178. package/dist/extraction-prompt.d.ts +36 -0
  179. package/dist/extraction-prompt.js +16 -0
  180. package/dist/extraction-prompt.js.map +1 -0
  181. package/dist/extraction-source-grounding-rules.d.ts +1 -1
  182. package/dist/extraction-source-grounding.d.ts +1 -1
  183. package/dist/extraction.d.ts +6 -14
  184. package/dist/extraction.js +9 -7
  185. package/dist/fallback-llm.d.ts +1 -1
  186. package/dist/graph-dashboard-diff.d.ts +1 -1
  187. package/dist/graph-dashboard-key.d.ts +1 -1
  188. package/dist/graph-dashboard-parser.d.ts +1 -1
  189. package/dist/graph-edge-reinforcement.d.ts +1 -1
  190. package/dist/graph-snapshot.d.ts +1 -1
  191. package/dist/graph.d.ts +1 -1
  192. package/dist/importance.d.ts +1 -1
  193. package/dist/importers/index.d.ts +1 -1
  194. package/dist/in-flight-reads.d.ts +1 -1
  195. package/dist/index.d.ts +699 -696
  196. package/dist/index.js +124 -122
  197. package/dist/intent.d.ts +1 -1
  198. package/dist/lcm/engine.d.ts +1 -1
  199. package/dist/lcm/index.d.ts +1 -1
  200. package/dist/lcm/tools.d.ts +1 -1
  201. package/dist/lifecycle.d.ts +1 -1
  202. package/dist/live-connectors-runner.d.ts +1 -1
  203. package/dist/local-llm.d.ts +1 -1
  204. package/dist/local-model-endpoint.d.ts +1 -1
  205. package/dist/maintenance/memory-governance.d.ts +1 -1
  206. package/dist/maintenance/memory-governance.js +5 -5
  207. package/dist/maintenance/rebuild-memory-lifecycle-ledger.d.ts +2 -2
  208. package/dist/maintenance/rebuild-memory-lifecycle-ledger.js +5 -5
  209. package/dist/maintenance/rebuild-memory-projection.d.ts +2 -2
  210. package/dist/maintenance/rebuild-memory-projection.js +6 -6
  211. package/dist/{maintenance-CoRikzIt.d.ts → maintenance-DQrL93Uu.d.ts} +3 -3
  212. package/dist/mcp-memory-inspector-app.d.ts +10 -10
  213. package/dist/memory-action-policy.d.ts +1 -1
  214. package/dist/memory-cache.d.ts +1 -1
  215. package/dist/memory-lifecycle-ledger-utils.d.ts +1 -1
  216. package/dist/memory-projection-store.d.ts +1 -1
  217. package/dist/memory-provenance.d.ts +1 -1
  218. package/dist/memory-snapshot.d.ts +1 -1
  219. package/dist/memory-worth-outcomes.d.ts +2 -2
  220. package/dist/models-json.d.ts +1 -1
  221. package/dist/namespaces/migrate.d.ts +3 -3
  222. package/dist/namespaces/migrate.js +12 -12
  223. package/dist/namespaces/principal.d.ts +1 -1
  224. package/dist/namespaces/search.d.ts +1 -1
  225. package/dist/namespaces/search.js +6 -6
  226. package/dist/namespaces/storage.d.ts +3 -3
  227. package/dist/namespaces/storage.js +5 -5
  228. package/dist/native-knowledge.d.ts +1 -1
  229. package/dist/offline-sync-impression-drain.d.ts +1 -1
  230. package/dist/operator-doctor-corpus.d.ts +1 -1
  231. package/dist/operator-doctor-corpus.js +14 -14
  232. package/dist/operator-doctor-replica.d.ts +1 -1
  233. package/dist/operator-toolkit.d.ts +3 -3
  234. package/dist/operator-toolkit.js +18 -18
  235. package/dist/orchestration/compression-guideline-coordinator.d.ts +2 -2
  236. package/dist/orchestration/maintenance.d.ts +4 -4
  237. package/dist/orchestration/maintenance.js +10 -10
  238. package/dist/{orchestrator-BJeq7Pw1.d.ts → orchestrator-LPGegii3.d.ts} +8 -8
  239. package/dist/orchestrator.d.ts +8 -8
  240. package/dist/orchestrator.js +73 -71
  241. package/dist/patterns-cli.d.ts +1 -1
  242. package/dist/{pipeline-CNjY4wk3.d.ts → pipeline-1h2GpiHP.d.ts} +1 -1
  243. package/dist/policy-runtime.d.ts +1 -1
  244. package/dist/proactive-contention.d.ts +1 -1
  245. package/dist/provenance.d.ts +1 -1
  246. package/dist/{qmd-C0UDZidt.d.ts → qmd-UdwoF71D.d.ts} +1 -1
  247. package/dist/qmd-preflight.d.ts +2 -2
  248. package/dist/qmd-recall-cache.d.ts +1 -1
  249. package/dist/qmd.d.ts +2 -2
  250. package/dist/recall-concurrency-config.d.ts +1 -1
  251. package/dist/recall-disclosure-escalation.d.ts +1 -1
  252. package/dist/recall-explain-renderer.d.ts +1 -1
  253. package/dist/recall-explain-renderer.js +3 -3
  254. package/dist/recall-memory-map.d.ts +1 -1
  255. package/dist/recall-planner-llm.d.ts +1 -1
  256. package/dist/recall-state.d.ts +1 -1
  257. package/dist/recall-tag-filter.d.ts +1 -1
  258. package/dist/recall-timings.d.ts +1 -1
  259. package/dist/recall-xray-cli.d.ts +1 -1
  260. package/dist/recall-xray-cli.js +4 -4
  261. package/dist/recall-xray-renderer.d.ts +1 -1
  262. package/dist/recall-xray-renderer.js +3 -3
  263. package/dist/recall-xray.d.ts +1 -1
  264. package/dist/recall-xray.js +2 -2
  265. package/dist/reconcile/cursor.d.ts +1 -1
  266. package/dist/reconcile/manifest.d.ts +1 -1
  267. package/dist/reconcile/plan.d.ts +1 -1
  268. package/dist/replay/normalizers/chatgpt.d.ts +1 -1
  269. package/dist/replay/normalizers/claude.d.ts +1 -1
  270. package/dist/replay/normalizers/openclaw.d.ts +1 -1
  271. package/dist/replay/normalizers/shared.d.ts +1 -1
  272. package/dist/replay/runner.d.ts +1 -1
  273. package/dist/replay/types.d.ts +1 -1
  274. package/dist/replica-divergence.d.ts +1 -1
  275. package/dist/replica-peers-config.d.ts +1 -1
  276. package/dist/resolve-auth-token.d.ts +1 -1
  277. package/dist/retrieval-agents.d.ts +2 -2
  278. package/dist/retrieval-tiers.d.ts +1 -1
  279. package/dist/routing/engine.d.ts +1 -1
  280. package/dist/routing/store.d.ts +1 -1
  281. package/dist/salvage-envelope.d.ts +1 -1
  282. package/dist/schemas.d.ts +76 -76
  283. package/dist/{scope-profiles-BJzp2F5A.d.ts → scope-profiles-APjhSe-w.d.ts} +1 -1
  284. package/dist/sdk-compat.js.map +1 -1
  285. package/dist/search/embed-helper.d.ts +1 -1
  286. package/dist/search/factory.d.ts +1 -1
  287. package/dist/search/factory.js +5 -5
  288. package/dist/search/index.d.ts +1 -1
  289. package/dist/search/index.js +9 -9
  290. package/dist/search/lancedb-backend.d.ts +1 -1
  291. package/dist/search/meilisearch-backend.d.ts +1 -1
  292. package/dist/search/noop-backend.d.ts +1 -1
  293. package/dist/search/orama-backend.d.ts +1 -1
  294. package/dist/search/port.d.ts +1 -1
  295. package/dist/search/remote-backend.d.ts +1 -1
  296. package/dist/{semantic-consolidation-D4KAZO2G.d.ts → semantic-consolidation-CrySDbk3.d.ts} +1 -1
  297. package/dist/semantic-consolidation.d.ts +2 -2
  298. package/dist/semantic-consolidation.js +6 -6
  299. package/dist/semantic-rule-promotion.js +5 -5
  300. package/dist/semantic-rule-verifier.d.ts +1 -1
  301. package/dist/semantic-rule-verifier.js +5 -5
  302. package/dist/{service-DWC4tq7p.d.ts → service-DJ0LDyuN.d.ts} +2 -2
  303. package/dist/session-observer-bands.d.ts +1 -1
  304. package/dist/session-observer-state.d.ts +1 -1
  305. package/dist/shared-context/manager.d.ts +9 -9
  306. package/dist/signal.d.ts +1 -1
  307. package/dist/source-agent-qualifier.d.ts +4 -1
  308. package/dist/source-agent-qualifier.js +6 -6
  309. package/dist/{storage-DPGjB-8e.d.ts → storage-y340tYKb.d.ts} +1 -1
  310. package/dist/storage.d.ts +2 -2
  311. package/dist/storage.js +4 -4
  312. package/dist/summarizer.d.ts +1 -1
  313. package/dist/summary-snapshot.d.ts +1 -1
  314. package/dist/temporal-supersession.d.ts +2 -2
  315. package/dist/temporal-timeline-recall.d.ts +1 -1
  316. package/dist/temporal-validity.d.ts +1 -1
  317. package/dist/threading.d.ts +1 -1
  318. package/dist/tier-migration.d.ts +2 -2
  319. package/dist/tier-routing.d.ts +1 -1
  320. package/dist/topics.d.ts +1 -1
  321. package/dist/transcript.d.ts +1 -1
  322. package/dist/transfer/types.d.ts +66 -66
  323. package/dist/trust-score-stage.d.ts +1 -1
  324. package/dist/trust-score.d.ts +1 -1
  325. package/dist/{types-yX_FwERf.d.ts → types-D_Au9qJO.d.ts} +13 -2
  326. package/dist/types.d.ts +1 -1
  327. package/dist/types.js +1 -1
  328. package/dist/utility-runtime.d.ts +1 -1
  329. package/dist/verified-recall.js +5 -5
  330. package/dist/write-envelope.d.ts +1 -1
  331. package/package.json +2 -12
  332. package/src/ambient-provenance.test.ts +510 -0
  333. package/src/ambient-provenance.ts +229 -0
  334. package/src/buffer-turn-helpers.ts +17 -1
  335. package/src/compat/checks.test.ts +45 -1
  336. package/src/compat/checks.ts +16 -16
  337. package/src/extraction-prompt.ts +142 -0
  338. package/src/extraction.ts +37 -148
  339. package/src/meetings/memory-gen.test.ts +51 -0
  340. package/src/meetings/memory-gen.ts +36 -8
  341. package/src/meetings/summary-extractor.ts +4 -2
  342. package/src/orchestration/recall-internal.ts +5 -148
  343. package/src/orchestration/recall-profile-storage.ts +194 -0
  344. package/src/orchestration/recall-search-pipeline.ts +13 -182
  345. package/src/orchestration/recall-search-prefilter.ts +254 -0
  346. package/src/orchestration/session-context.ts +1 -1
  347. package/src/orchestrator.ts +1 -1
  348. package/src/sdk-compat.ts +2 -4
  349. package/src/source-agent-qualifier.ts +3 -0
  350. package/src/testing/subjects/recall-profile-storage.test.ts +362 -0
  351. package/src/testing/subjects/recall-search-prefilter.test.ts +365 -0
  352. package/src/types.ts +2 -2
  353. package/src/wearables/memory-gen.test.ts +82 -0
  354. package/src/wearables/memory-gen.ts +54 -5
  355. package/src/wearables/trust.ts +19 -2
  356. package/dist/chunk-2X2T2UVE.js.map +0 -1
  357. package/dist/chunk-DZPFMQZ7.js.map +0 -1
  358. package/dist/chunk-ER7W6H4T.js.map +0 -1
  359. package/dist/chunk-GODSPTOT.js.map +0 -1
  360. package/dist/chunk-GQL52GQ5.js.map +0 -1
  361. package/dist/chunk-R3KHKU7M.js.map +0 -1
  362. package/dist/chunk-UV72DEZD.js.map +0 -1
  363. package/dist/legacy-hook-compat.d.ts +0 -3
  364. package/dist/legacy-hook-compat.js +0 -36
  365. package/dist/legacy-hook-compat.js.map +0 -1
  366. package/src/legacy-hook-compat.test.ts +0 -20
  367. package/src/legacy-hook-compat.ts +0 -45
  368. /package/dist/{chunk-LEPOP46W.js.map → chunk-227UFSEQ.js.map} +0 -0
  369. /package/dist/{chunk-CX4ZUMC6.js.map → chunk-2DSUT7DZ.js.map} +0 -0
  370. /package/dist/{chunk-DAJPRXBR.js.map → chunk-47J4EDGR.js.map} +0 -0
  371. /package/dist/{chunk-QVJPH7SH.js.map → chunk-5LRDM4R7.js.map} +0 -0
  372. /package/dist/{chunk-XAGYS7X6.js.map → chunk-5R6S4DJZ.js.map} +0 -0
  373. /package/dist/{chunk-QR6G5KF4.js.map → chunk-5VQPZ4GV.js.map} +0 -0
  374. /package/dist/{chunk-E5XQIWAL.js.map → chunk-64BHFFKZ.js.map} +0 -0
  375. /package/dist/{chunk-GRUYA45P.js.map → chunk-6H744ZBH.js.map} +0 -0
  376. /package/dist/{chunk-6ISC4AVB.js.map → chunk-BDNL34CT.js.map} +0 -0
  377. /package/dist/{chunk-AC5OWJLV.js.map → chunk-E4FOAENZ.js.map} +0 -0
  378. /package/dist/{chunk-UW66OZTD.js.map → chunk-FULKGK7P.js.map} +0 -0
  379. /package/dist/{chunk-V2TNYF6Q.js.map → chunk-HJ3SVHUW.js.map} +0 -0
  380. /package/dist/{chunk-A4IJCD2Z.js.map → chunk-HXXTXLKA.js.map} +0 -0
  381. /package/dist/{chunk-FUCYDRE5.js.map → chunk-IDE5AR5W.js.map} +0 -0
  382. /package/dist/{chunk-2FSTB6BJ.js.map → chunk-ILBLZ5Y5.js.map} +0 -0
  383. /package/dist/{chunk-WZ3TOIDW.js.map → chunk-JFO5QZ7N.js.map} +0 -0
  384. /package/dist/{chunk-FR27HBBA.js.map → chunk-KA47C7BF.js.map} +0 -0
  385. /package/dist/{chunk-JZH4LWHB.js.map → chunk-KMWYZTIE.js.map} +0 -0
  386. /package/dist/{chunk-LPYDOLSM.js.map → chunk-L7G72WY4.js.map} +0 -0
  387. /package/dist/{chunk-AMAUBUKY.js.map → chunk-LI7OE7G6.js.map} +0 -0
  388. /package/dist/{chunk-MO4HSXHZ.js.map → chunk-M2INOSTA.js.map} +0 -0
  389. /package/dist/{chunk-HFPASSFV.js.map → chunk-P2OTGMTR.js.map} +0 -0
  390. /package/dist/{chunk-W2V7UESE.js.map → chunk-PFJXPOD4.js.map} +0 -0
  391. /package/dist/{chunk-NEF7LR5J.js.map → chunk-RVPTS4U5.js.map} +0 -0
  392. /package/dist/{chunk-W3IC7QOY.js.map → chunk-RVYVMQK4.js.map} +0 -0
  393. /package/dist/{chunk-GUAL4PQH.js.map → chunk-ST5IEK3H.js.map} +0 -0
  394. /package/dist/{chunk-O4KMGRXE.js.map → chunk-SXWHN7GC.js.map} +0 -0
  395. /package/dist/{chunk-RNHVVMFN.js.map → chunk-T6JRMVOV.js.map} +0 -0
  396. /package/dist/{chunk-5QJQHBLE.js.map → chunk-U6JGQ37L.js.map} +0 -0
  397. /package/dist/{chunk-HC3PBX23.js.map → chunk-V5FE3UAI.js.map} +0 -0
  398. /package/dist/{chunk-ERZ2R42X.js.map → chunk-W2ZSNIBP.js.map} +0 -0
  399. /package/dist/{chunk-3COYXTGM.js.map → chunk-WHWDBTCJ.js.map} +0 -0
  400. /package/dist/{chunk-FIK3CQNA.js.map → chunk-WOC6DXP5.js.map} +0 -0
  401. /package/dist/{chunk-4FBVAGV3.js.map → chunk-XBMJ35IP.js.map} +0 -0
  402. /package/dist/{chunk-GFCVUY6G.js.map → chunk-YWCOJOUI.js.map} +0 -0
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Ambient-capture provenance for extraction (issue #2294).
3
+ *
4
+ * Always-on capture devices — wearable recorders, room microphones — hand the
5
+ * extraction engine speech the user never authored: television and film
6
+ * dialogue, podcasts, radio, music with spoken segments, and conversations the
7
+ * user merely walked past. The extraction prompt otherwise reads every turn as
8
+ * first-person user input, so a scripted line about a relative's birthday or a
9
+ * medical diagnosis lands as a high-confidence personal fact and auto-promotes
10
+ * into recall. That output is plausible, personally relevant, and wrong, which
11
+ * makes it materially worse than a missed fact.
12
+ *
13
+ * This module owns both halves of the fix:
14
+ *
15
+ * 1. The prompt text that teaches the model to separate the user's own speech
16
+ * from background media, drop high-consequence personal claims it cannot
17
+ * attribute to the user, and score the remainder speculatively. One source
18
+ * of truth, shared by the cloud, direct-client, and local-LLM prompts so the
19
+ * three paths cannot drift.
20
+ * 2. A deterministic backstop — `clampAmbientCaptureConfidence` — for when the
21
+ * model ignores the prompt anyway. It clamps ONLY high-impact personal facts
22
+ * (family, milestones, medical) to the speculative ceiling. Clamping every
23
+ * ambient fact would gut the wearable integration, which is explicitly not
24
+ * the goal; clamping this class costs a birthday landing in the review queue
25
+ * instead of active recall.
26
+ *
27
+ * The provenance signal itself is `BufferTurn.ambientCapture`, set by the
28
+ * ingesting subsystem (the wearable memory pass), never by tool arguments.
29
+ */
30
+
31
+ import type { ExtractionResult } from "./types.js";
32
+
33
+ /**
34
+ * Upper bound of the speculative confidence tier, matching the tier table in
35
+ * the extraction prompt ("Speculative (0.00-0.39)"). A fact at or below this
36
+ * value cannot auto-promote through the wearable trust bands.
37
+ */
38
+ export const SPECULATIVE_CONFIDENCE_CEILING = 0.39;
39
+
40
+ /**
41
+ * Appended to the speculative tier definition when the input is ambient
42
+ * capture, so the tier itself carries the rule rather than relying on the
43
+ * separate section below being obeyed in isolation.
44
+ */
45
+ export const AMBIENT_SPECULATIVE_TIER_CLAUSE =
46
+ " Speculative also applies to any fact whose only evidence is ambient or background audio from an always-on capture device, even when the content is concrete and specific.";
47
+
48
+ /**
49
+ * Full provenance section for the cloud and direct-client extraction prompts.
50
+ */
51
+ export const AMBIENT_CAPTURE_PROMPT_SECTION = `
52
+ Source provenance — always-on capture audio:
53
+ - This transcript came from an always-on capture device (a wearable recorder or room microphone). It may contain audio the user did not author: television, film, podcasts, radio, advertisements, music with spoken segments, and conversations the user merely overheard or walked past.
54
+ - Separate the user's own speech from background media before extracting anything. Narrative, scripted, performed, or third-person speech — a show's dialogue, a podcast host, an ad read, a lecture playing in the room — is media, not user input. Never attribute it to the user or to anyone the user knows.
55
+ - High-consequence personal claims must be dropped entirely unless the user's own voice is clearly identifiable as their source: family relationships, personal milestones (births, birthdays, anniversaries, weddings, funerals, divorces), and any medical or health detail. Omit them; do not emit them at reduced confidence as a compromise.
56
+ - When you cannot tell whether a statement was spoken by the user or captured passively, skip it or set its confidence in the speculative tier (0.00-0.39). Never resolve that doubt upward.`;
57
+
58
+ /**
59
+ * Compact variant for the local-LLM prompt, which is deliberately small to fit
60
+ * 4k-8k context windows.
61
+ */
62
+ export const AMBIENT_CAPTURE_PROMPT_SECTION_COMPACT = `
63
+ Ambient capture:
64
+ - This transcript came from an always-on capture device and may contain TV, film, podcast, music, or overheard speech the user did not author.
65
+ - Treat narrative, scripted, or third-person speech as media, not user input.
66
+ - Drop family, milestone (birth, birthday, anniversary, wedding, funeral), and medical claims unless the user's own voice is clearly the source.
67
+ - Otherwise, when unsure whether the user said it, set confidence in the speculative tier (0.00-0.39).`;
68
+
69
+ /**
70
+ * Single rule for prompts that already have their own rule list and only need
71
+ * the ambient warning (the meeting scribe, whose transcript is the same
72
+ * wearable audio).
73
+ */
74
+ export const AMBIENT_CAPTURE_PROMPT_RULE =
75
+ "- The transcript may contain ambient audio nobody in the meeting authored: television, podcasts, music, or a passing conversation. Treat scripted, performed, or third-person speech as background media and never record it as a decision, commitment, or question.";
76
+
77
+ /**
78
+ * Categories that are personal-claim-shaped by construction, independent of any
79
+ * vocabulary: `moment` is defined as an emotionally significant event or
80
+ * milestone, and on ambient audio a `relationship` claim is a link the model
81
+ * inferred between people it overheard. Non-lexical, so they still catch harm
82
+ * the term lists below miss.
83
+ */
84
+ const HIGH_IMPACT_CATEGORIES: Record<string, true> = { moment: true, relationship: true };
85
+
86
+ /**
87
+ * Kinship, milestone, and health vocabulary. Deliberately broad: a false
88
+ * positive costs one ambient fact a trip through the review queue, a false
89
+ * negative puts a fabricated medical claim into active recall. The list cannot
90
+ * be exhaustive and is not the primary defense — the prompt is, and the
91
+ * category signal above plus the trust-band cap in the wearable pass catch
92
+ * classes no word list enumerates. Extend it when a miss is observed.
93
+ *
94
+ * Word-bounded alternation only — no nesting, no backtracking risk.
95
+ */
96
+ const HIGH_IMPACT_TOPIC_PATTERN =
97
+ /\b(?:mother|mothers|mom|moms|mum|father|fathers|dad|dads|sister|sisters|brother|brothers|son|sons|daughter|daughters|wife|wives|husband|husbands|spouse|fiance|fiancee|grandmother|grandfather|grandma|grandpa|grandparent|grandparents|grandson|granddaughter|grandchild|grandchildren|aunt|uncle|cousin|niece|nephew|stepmother|stepfather|stepson|stepdaughter|stepchild|godmother|godfather|birthday|birthdays|anniversary|anniversaries|wedding|weddings|honeymoon|married|marriage|divorce|divorced|widow|widowed|funeral|memorial service|engagement party|baby shower|christening|bar mitzvah|pregnant|pregnancy|miscarriage|miscarried|newborn|gave birth|stillbirth|infertility|ivf|menopause|diagnosis|diagnosed|prognosis|tested positive|cancer|carcinoma|leukemia|tumor|tumour|biopsy|chemotherapy|chemo|radiation therapy|remission|hospice|palliative|terminal illness|surgery|surgeon|hospital|hospitalized|hospitalised|icu|emergency room|ambulance|stroke|heart attack|cardiac|arrhythmia|afib|seizure|epilepsy|asthma|allergy|allergic|diabetes|diabetic|insulin|cholesterol|blood pressure|hypertension|dementia|alzheimer|alzheimers|parkinson|parkinsons|hiv|aids diagnosis|medication|medications|prescription|prescribed|dosage|symptom|symptoms|illness|disease|disorder|syndrome|therapy|therapist|psychiatrist|psychologist|antidepressant|depression|anxiety|bipolar|schizophrenia|adhd|autism|ptsd|overdose|relapse|sobriety|rehab|addiction|alcoholism|concussion|transplant|dialysis|immunocompromised|mental health|covid|covid-19|coronavirus|flu|influenza|pneumonia|bronchitis|infection|infected|fever|migraine|ulcer|hernia|appendicitis|sepsis|sick leave|urgent care|broken leg|broken arm|broken wrist|broken hip|broken rib|broken ribs|sprained|dislocated|torn ligament|torn acl|whiplash|burn unit|chronic pain|disability|wheelchair|hearing aid|blind in|vaccinated|vaccination|immunization|blood test|scan results|mri|ct scan|x-ray|deaf|deafness|blind|blindness|mute|nonverbal|paralyzed|paralysed|paraplegic|quadriplegic|amputee|amputation|prosthetic|multiple sclerosis|lupus|crohn|celiac|fibromyalgia|cystic fibrosis|sickle cell|cerebral palsy|down syndrome|muscular dystrophy|chronic fatigue|chronic illness|terminally ill|life support|coma|special needs|caregiver|nursing home|assisted living|treatment|treatments|in treatment|medical leave|oncologist|cardiologist|neurologist|pediatrician|pediatric|als|copd|epilepsy|seizures|hospitalization|infirm|frail|bedridden|home health|physical therapy|occupational therapy|speech therapy)\b/i;
98
+
99
+ /**
100
+ * Bodily-harm and affliction SHAPES rather than nouns: "broke her leg", "tore
101
+ * his ACL", "caught something at the office". A vocabulary of conditions can
102
+ * never be complete, so this matches the sentence form instead — a verb of
103
+ * injury or contagion pointed at a person's POSSESSION. Articles are excluded
104
+ * on purpose: "the build broke the deploy pipeline" is not an injury.
105
+ */
106
+ const AFFLICTION_SHAPE_PATTERN =
107
+ /\b(?:broke|broken|fractured|sprained|dislocated|tore|torn|bruised|burned|scalded|injured|hurt)\s+(?:his|her|their|my|your|our)\s+\w+|\b(?:caught|contracted|came down with|is recovering from|was rushed to|passed away|died of|died from)\b/i;
108
+
109
+ /**
110
+ * Words that are personal only when someone possesses them: "child process",
111
+ * "parent component", "sibling node", and "customer family" are everyday
112
+ * technical speech, while "his child" and "Rachel's parents" are not.
113
+ */
114
+ const POSSESSED_KINSHIP_PATTERN =
115
+ /(?:\b(?:my|your|his|her|their|our)|'s)\s+(?:child|children|kid|kids|parent|parents|partner|family|sibling|siblings|in-law|in-laws|relatives)\b/i;
116
+
117
+ /** Tags that mark a fact as belonging to the same high-impact classes. */
118
+ const HIGH_IMPACT_TAGS: Record<string, true> = {
119
+ disability: true,
120
+ family: true,
121
+ health: true,
122
+ medical: true,
123
+ medication: true,
124
+ diagnosis: true,
125
+ birthday: true,
126
+ anniversary: true,
127
+ marriage: true,
128
+ pregnancy: true,
129
+ "mental-health": true,
130
+ };
131
+
132
+ /**
133
+ * True when this claim is personal in a way whose fabrication causes real
134
+ * harm — a family relationship, a personal milestone, or a medical detail.
135
+ *
136
+ * Read by every ambient guard: the post-extraction confidence clamp below, the
137
+ * wearable trust-band cap, and the meeting trust-band cap. A fact this flags
138
+ * can never auto-approve, on any path, however the boosts add up.
139
+ *
140
+ * Takes the shape rather than `ExtractedFact` so meeting candidates — which
141
+ * carry no tags — pass without a synthetic empty array.
142
+ *
143
+ * `personRefs` comes from `collectPersonEntityRefs` over the same extraction:
144
+ * pass it whenever the entity list is available, so personhood is read from
145
+ * the extractor's own `type: "person"` metadata instead of inferred from how
146
+ * it happened to spell the ref.
147
+ */
148
+ export function isHighImpactPersonalFact(
149
+ fact: {
150
+ category: string;
151
+ content: string;
152
+ tags?: readonly string[];
153
+ entityRef?: string;
154
+ },
155
+ personRefs?: ReadonlySet<string>,
156
+ ): boolean {
157
+ // The strongest non-lexical signal: a claim ABOUT A PERSON is high-impact
158
+ // whatever condition, relationship, or milestone the sentence names. This is
159
+ // what covers wording no list anticipates ("Dana is deaf").
160
+ if (isPersonRef(fact.entityRef, personRefs)) return true;
161
+ if (HIGH_IMPACT_CATEGORIES[fact.category] === true) return true;
162
+ if (HIGH_IMPACT_TOPIC_PATTERN.test(fact.content)) return true;
163
+ if (AFFLICTION_SHAPE_PATTERN.test(fact.content)) return true;
164
+ if (POSSESSED_KINSHIP_PATTERN.test(fact.content)) return true;
165
+ for (const tag of fact.tags ?? []) {
166
+ if (HIGH_IMPACT_TAGS[tag.trim().toLowerCase()] === true) return true;
167
+ }
168
+ return false;
169
+ }
170
+
171
+ /**
172
+ * Normalize an entity reference for comparison: casefold, strip a leading
173
+ * type prefix, and collapse separators. `Person-Jane_Doe`, `person:jane-doe`,
174
+ * and `jane doe` all reduce to `jane-doe`.
175
+ */
176
+ function normalizeEntityRef(ref: string): string {
177
+ return ref
178
+ .trim()
179
+ .toLowerCase()
180
+ .replace(/^(?:person|people)[-:_\s]+/, "")
181
+ .replace(/[\s_]+/g, "-");
182
+ }
183
+
184
+ /**
185
+ * Names of every `type: "person"` entity in an extraction, normalized for
186
+ * lookup. Personhood is the extractor's own metadata, not a guess from the
187
+ * shape of `entityRef` — the prompt asks for `person-jane-doe` but the field is
188
+ * optional and models routinely emit a bare `jane-doe`.
189
+ */
190
+ export function collectPersonEntityRefs(result: {
191
+ entities: ReadonlyArray<{ name: string; type: string }>;
192
+ }): ReadonlySet<string> {
193
+ const refs = new Set<string>();
194
+ for (const entity of result.entities) {
195
+ if (entity.type !== "person") continue;
196
+ const normalized = normalizeEntityRef(entity.name);
197
+ if (normalized.length > 0) refs.add(normalized);
198
+ }
199
+ return refs;
200
+ }
201
+
202
+ /** True when this ref names a person, by metadata or by explicit prefix. */
203
+ function isPersonRef(ref: string | undefined, personRefs?: ReadonlySet<string>): boolean {
204
+ if (ref === undefined || ref.trim().length === 0) return false;
205
+ if (/^(?:person|people)[-:_]/i.test(ref.trim())) return true;
206
+ return personRefs !== undefined && personRefs.has(normalizeEntityRef(ref));
207
+ }
208
+
209
+ /**
210
+ * Deterministic backstop applied after extraction on ambient-capture input:
211
+ * hold high-impact personal facts at or below the speculative ceiling so they
212
+ * cannot auto-promote to active recall on a model's unsupported confidence.
213
+ *
214
+ * Returns the input unchanged when nothing needs clamping, so the non-ambient
215
+ * path pays one predicate per fact and allocates nothing.
216
+ */
217
+ export function clampAmbientCaptureConfidence(
218
+ result: ExtractionResult,
219
+ ): ExtractionResult {
220
+ let clampedAny = false;
221
+ const personRefs = collectPersonEntityRefs(result);
222
+ const facts = result.facts.map((fact) => {
223
+ if (fact.confidence <= SPECULATIVE_CONFIDENCE_CEILING) return fact;
224
+ if (!isHighImpactPersonalFact(fact, personRefs)) return fact;
225
+ clampedAny = true;
226
+ return { ...fact, confidence: SPECULATIVE_CONFIDENCE_CEILING };
227
+ });
228
+ return clampedAny ? { ...result, facts } : result;
229
+ }
@@ -9,6 +9,18 @@ export interface BufferTurnOwner {
9
9
  sessionOwnerPrincipal?: string;
10
10
  }
11
11
 
12
+ export interface AmbientCaptureProvenance {
13
+ /**
14
+ * True when this turn's content came from an always-on ambient capture
15
+ * device (a wearable recorder, a room microphone) and may therefore carry
16
+ * speech the user never authored: TV, podcasts, music, overheard
17
+ * conversation (issue #2294). Set by the ingesting subsystem, never from
18
+ * tool arguments — extraction reads it to warn the model about media audio
19
+ * and to clamp high-impact personal facts to the speculative tier.
20
+ */
21
+ ambientCapture?: boolean;
22
+ }
23
+
12
24
  export function describeError(err: unknown): string {
13
25
  if (err instanceof Error) return err.message;
14
26
  if (err === null) return "null";
@@ -46,6 +58,9 @@ export function copyBufferTurn(turn: BufferTurn): BufferTurn {
46
58
  if (typeof turn.sourceConnector === "string") {
47
59
  copy.sourceConnector = turn.sourceConnector;
48
60
  }
61
+ if (typeof turn.ambientCapture === "boolean") {
62
+ copy.ambientCapture = turn.ambientCapture;
63
+ }
49
64
  return copy;
50
65
  }
51
66
 
@@ -61,7 +76,8 @@ export function bufferTurnsEqual(left: BufferTurn | undefined, right: BufferTurn
61
76
  left.providerThreadId === right.providerThreadId &&
62
77
  left.turnFingerprint === right.turnFingerprint &&
63
78
  left.persistProcessedFingerprint === right.persistProcessedFingerprint &&
64
- left.sourceConnector === right.sourceConnector
79
+ left.sourceConnector === right.sourceConnector &&
80
+ left.ambientCapture === right.ambientCapture
65
81
  );
66
82
  }
67
83
 
@@ -154,7 +154,11 @@ test("memory prompt section registration must be on the plugin api object", asyn
154
154
 
155
155
  const hookCheck = checkById(checks, "hook-registration-core");
156
156
  assert.equal(hookCheck.level, "error");
157
- assert.match(hookCheck.message, /before_prompt_build|before_agent_start/);
157
+ assert.match(hookCheck.message, /before_prompt_build/);
158
+ assert.doesNotMatch(
159
+ `${hookCheck.message} ${hookCheck.remediation ?? ""}`,
160
+ /before_agent_start/,
161
+ );
158
162
  });
159
163
 
160
164
  test("memory prompt section registration on api satisfies recall hook requirement", async () => {
@@ -173,6 +177,46 @@ test("memory prompt section registration on api satisfies recall hook requiremen
173
177
  assert.equal(hookCheck.level, "ok");
174
178
  });
175
179
 
180
+ test("memory prompt section remediation requests only missing required hooks", async () => {
181
+ const checks = await runFixture({
182
+ enginesNode: ">=22.12.0",
183
+ currentNodeVersion: "v22.12.0",
184
+ indexSource: [
185
+ "api.registerService({ start: async () => {} });",
186
+ "api.registerMemoryPromptSection(() => null);",
187
+ "registerCli(api, orchestrator);",
188
+ ].join("\n"),
189
+ });
190
+
191
+ const hookCheck = checkById(checks, "hook-registration-core");
192
+ assert.equal(hookCheck.level, "error");
193
+ assert.match(`${hookCheck.message} ${hookCheck.remediation ?? ""}`, /agent_end/);
194
+ assert.doesNotMatch(
195
+ `${hookCheck.message} ${hookCheck.remediation ?? ""}`,
196
+ /before_prompt_build|before_agent_start|gateway_start/,
197
+ );
198
+ });
199
+
200
+ test("memory prompt section remediation requests startup only when absent", async () => {
201
+ const checks = await runFixture({
202
+ enginesNode: ">=22.12.0",
203
+ currentNodeVersion: "v22.12.0",
204
+ indexSource: [
205
+ "api.on('agent_end', async () => {});",
206
+ "api.registerMemoryPromptSection(() => null);",
207
+ "registerCli(api, orchestrator);",
208
+ ].join("\n"),
209
+ });
210
+
211
+ const hookCheck = checkById(checks, "hook-registration-core");
212
+ assert.equal(hookCheck.level, "error");
213
+ assert.match(`${hookCheck.message} ${hookCheck.remediation ?? ""}`, /startup wiring/);
214
+ assert.doesNotMatch(
215
+ `${hookCheck.message} ${hookCheck.remediation ?? ""}`,
216
+ /before_prompt_build|before_agent_start/,
217
+ );
218
+ });
219
+
176
220
  test("service start detection ignores nested start properties", async () => {
177
221
  const checks = await runFixture({
178
222
  enginesNode: ">=22.12.0",
@@ -4,8 +4,7 @@ import type { CompatCheckOptions, CompatCheckResult, CompatReport, CompatRunner
4
4
  import { compareVersions } from "../version-utils.js";
5
5
  import { launchProcess } from "../runtime/child-process.js";
6
6
 
7
- const REQUIRED_HOOKS_LEGACY = ["before_agent_start", "agent_end"];
8
- const REQUIRED_HOOKS_NEW = ["before_prompt_build", "agent_end"];
7
+ const REQUIRED_HOOKS = ["before_prompt_build", "agent_end"];
9
8
  const OPENCLAW_REMNIC_PLUGIN_ID = "openclaw-remnic";
10
9
  const OPENCLAW_REMNIC_LEGACY_PLUGIN_ID = "openclaw-engram";
11
10
 
@@ -698,22 +697,16 @@ export async function runCompatChecks(options: CompatCheckOptions): Promise<Comp
698
697
  const indexRaw = await readFile(indexPath, "utf-8");
699
698
  const structuralSource = stripCommentsAndStrings(indexRaw);
700
699
  const hooks = parseHookRegistrations(indexRaw);
701
- const missingLegacy = REQUIRED_HOOKS_LEGACY.filter((hook) => !hooks.has(hook));
702
- const missingNew = REQUIRED_HOOKS_NEW.filter((hook) => !hooks.has(hook));
700
+ const missingHooks = REQUIRED_HOOKS.filter((hook) => !hooks.has(hook));
703
701
  // registerMemoryPromptSection is a valid alternative to the recall hook only
704
702
  // when it is registered on the OpenClaw plugin API object.
705
703
  const hasMemoryPromptSection = hasMemoryPromptSectionRegistration(structuralSource);
706
- const missingLegacyAdj = hasMemoryPromptSection
707
- ? missingLegacy.filter((h) => h !== "before_agent_start")
708
- : missingLegacy;
709
- const missingNewAdj = hasMemoryPromptSection
710
- ? missingNew.filter((h) => h !== "before_prompt_build")
711
- : missingNew;
712
- // Accept whichever hook set has fewer missing entries
713
- const missingHooks = missingNewAdj.length <= missingLegacyAdj.length ? missingNewAdj : missingLegacyAdj;
704
+ const missingRequiredHooks = hasMemoryPromptSection
705
+ ? missingHooks.filter((hook) => hook !== "before_prompt_build")
706
+ : missingHooks;
714
707
  const hasGatewayStartHook = hooks.has("gateway_start");
715
708
  const hasServiceStart = hasServiceStartRegistration(structuralSource);
716
- if (missingHooks.length === 0 && (hasGatewayStartHook || hasServiceStart)) {
709
+ if (missingRequiredHooks.length === 0 && (hasGatewayStartHook || hasServiceStart)) {
717
710
  checks.push({
718
711
  id: "hook-registration-core",
719
712
  title: "Core hook registration",
@@ -722,18 +715,25 @@ export async function runCompatChecks(options: CompatCheckOptions): Promise<Comp
722
715
  });
723
716
  } else {
724
717
  const missingParts: string[] = [];
725
- if (missingHooks.length > 0) {
726
- missingParts.push(`hooks: ${missingHooks.join(", ")}`);
718
+ if (missingRequiredHooks.length > 0) {
719
+ missingParts.push(`hooks: ${missingRequiredHooks.join(", ")}`);
727
720
  }
728
721
  if (!hasGatewayStartHook && !hasServiceStart) {
729
722
  missingParts.push("startup wiring: gateway_start hook or api.registerService({ start })");
730
723
  }
724
+ const remediationParts: string[] = [];
725
+ if (missingRequiredHooks.length > 0) {
726
+ remediationParts.push(`registers ${missingRequiredHooks.join(", ")}`);
727
+ }
728
+ if (!hasGatewayStartHook && !hasServiceStart) {
729
+ remediationParts.push("registers gateway_start or api.registerService({ start })");
730
+ }
731
731
  checks.push({
732
732
  id: "hook-registration-core",
733
733
  title: "Core hook registration",
734
734
  level: "error",
735
735
  message: `Missing expected registration(s): ${missingParts.join("; ")}`,
736
- remediation: "Ensure src/index.ts registers before_prompt_build (or before_agent_start) and agent_end, plus either gateway_start or api.registerService({ start }).",
736
+ remediation: `Ensure src/index.ts ${remediationParts.join(" and ")}.`,
737
737
  });
738
738
  }
739
739
 
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Extraction prompt construction (issue #1995 sibling extraction).
3
+ *
4
+ * The system prompt, its JSON response shape, and the placeholder table
5
+ * derived from that shape are prompt DATA, not engine behavior. They live here
6
+ * so `extraction.ts` holds the pipeline and one module owns the text every
7
+ * extraction path — local LLM, direct client, gateway fallback — sends.
8
+ */
9
+
10
+ import { AMBIENT_CAPTURE_PROMPT_SECTION, AMBIENT_SPECULATIVE_TIER_CLAUSE } from "./ambient-provenance.js";
11
+ import { resolveMemoryLifecycleCapabilities, resolveRecallAuxiliaryCapabilities } from "./capabilities.js";
12
+ import type { PluginConfig } from "./types.js";
13
+
14
+ export const EXTRACTION_RESPONSE_SHAPE = `{
15
+ "facts": [{
16
+ "category": "<category>",
17
+ "content": "<source-grounded statement>",
18
+ "confidence": 0.0,
19
+ "tags": ["<tag>"],
20
+ "entityRef": "<optional normalized-name>",
21
+ "promptedByQuestion": "<optional source-grounded question>",
22
+ "quote": "<optional exact contiguous source span>",
23
+ "scope": "<optional project-or-global>",
24
+ "structuredAttributes": {"<key>": "<value>"},
25
+ "procedureSteps": [{"order": 1, "intent": "<step>"}, {"order": 2, "intent": "<step>"}],
26
+ "reasoningTrace": {
27
+ "steps": [{"order": 1, "description": "<step>"}, {"order": 2, "description": "<step>"}],
28
+ "finalAnswer": "<answer>",
29
+ "observedOutcome": "<optional outcome>"
30
+ },
31
+ "eventTime": "<optional source temporal expression>"
32
+ }],
33
+ "entities": [{
34
+ "name": "<normalized-name>",
35
+ "type": "<entity-type>",
36
+ "facts": ["<source-grounded statement>"],
37
+ "promptedByQuestion": "<optional source-grounded question>",
38
+ "structuredSections": [{"key": "<section-key>", "title": "<section-title>", "facts": ["<source-grounded statement>"]}]
39
+ }],
40
+ "profileUpdates": ["<source-grounded profile update>"],
41
+ "questions": [{"question": "<source-grounded unresolved question>", "context": "<source-grounded context>", "priority": 0.0}],
42
+ "identityReflection": "<conversation-grounded agent reflection>",
43
+ "relationships": [{"source": "<normalized-name>", "target": "<normalized-name>", "label": "<source-grounded relationship>"}]
44
+ }`;
45
+ export const EXTRACTION_RESPONSE_PLACEHOLDERS: Record<string, true> = {};
46
+ for (const placeholder of EXTRACTION_RESPONSE_SHAPE.match(/<[^<>\r\n]+>/g) ?? []) {
47
+ EXTRACTION_RESPONSE_PLACEHOLDERS[placeholder] = true;
48
+ }
49
+
50
+ /**
51
+ * Bi-temporal event-time extraction instruction (#1578 PR2). Emitted on
52
+ * every extraction entry path when `temporal.biTemporal` is on so the LLM
53
+ * emits an optional per-fact `eventTime` expression. The expression is
54
+ * resolved against the source turn timestamp at write time — never
55
+ * wall-clock — so replay/import of old transcripts anchors correctly.
56
+ * Returns an empty string when the gate is off (byte-identical prompt).
57
+ */
58
+ export function eventTimePromptInstruction(config: PluginConfig): string {
59
+ if (!config.temporalBiTemporal) return "";
60
+ return `
61
+ When a fact states when it became or stopped being true, copy that explicit temporal expression verbatim into "eventTime". Omit "eventTime" when no such expression appears; never infer dates.`;
62
+ }
63
+
64
+ /**
65
+ * Build extraction instructions shared between local and cloud LLM.
66
+ *
67
+ * `ambientCapture` marks input from an always-on recorder, which may carry
68
+ * speech the user never authored (issue #2294).
69
+ */
70
+ export function buildExtractionInstructions(config: PluginConfig, existingEntities?: string[], ambientCapture = false): string {
71
+ const lifecycleCaps = resolveMemoryLifecycleCapabilities(config);
72
+ return `You are a memory extraction system. Analyze the following conversation and extract durable, reusable memories.
73
+
74
+ Memory categories:
75
+ - fact: Objective information about the world
76
+ - preference: User likes, dislikes, or stylistic choices
77
+ - correction: User correcting a mistake or misconception (highest priority)
78
+ - entity: Information about a specific person, project, tool, or company
79
+ - decision: A choice that was made with rationale
80
+ - relationship: How two entities relate to each other (e.g., "Alice is Bob's manager", "Acme Corp uses Shopify")
81
+ - principle: Durable rules, values, or operating beliefs (e.g., "never use Chat Completions API")
82
+ - commitment: Promises, obligations, or deadlines (e.g., "deploy by Friday", "call accountant Monday")
83
+ - moment: Emotionally significant events or milestones (e.g., "first successful deployment of engram")
84
+ - skill: Capabilities the user or agent has demonstrated (e.g., "user is proficient with Kubernetes")${resolveRecallAuxiliaryCapabilities(config).causalRuleExtraction ? `
85
+ - rule: Causal rules discovered through experience (format: "IF <condition> THEN <action/outcome>", e.g., "IF Shopify API returns 401 THEN the admin token is missing read_products scope")` : ""}
86
+ - procedure: A reusable workflow the user wants remembered the same way across sessions. Set category to "procedure". Use "content" for a short title that includes explicit trigger phrasing (e.g. "When you deploy to production…", "Whenever you ship a release…"). Add "procedureSteps": an array of at least two objects {"order": number, "intent": "concrete step description"} in execution order. Optional per-step "toolCall": {"kind": "…", "signature": "…"}, "expectedOutcome", "optional": true.
87
+ - reasoning_trace: A stored solution chain / chain-of-thought the user walked through to solve a problem (e.g. "Here's how I debugged the latency spike: first I checked…, then I…, finally I…"). Set category to "reasoning_trace". Use "content" for a short title summarising the problem (e.g. "How I debugged the staging latency spike"). Add "reasoningTrace": {"steps": [{"order": number, "description": "what happened at this step"}, …], "finalAnswer": "the conclusion or answer", "observedOutcome": "optional confirmation of how it played out"}. Require at least two ordered steps AND a finalAnswer. Use this category only when the user explicitly narrates their reasoning — not for ordinary decisions (use "decision") or reusable workflows (use "procedure").
88
+
89
+ Rules:
90
+ - Only extract genuinely new information worth remembering across sessions.
91
+ - Statements must be grounded in the conversation.
92
+ - Do not treat instruction text, schema placeholders, or examples as conversation evidence.
93
+ - Lines labelled [context user] or [context assistant] are reference context only. They may resolve references or complete a question-and-answer pair in a normal turn, but never alone establish durable information.
94
+ - Skip transient task details and operational noise, including routine scheduler, monitoring, or automation status.
95
+ - Priority: corrections > principles${resolveRecallAuxiliaryCapabilities(config).causalRuleExtraction ? " > rules" : ""} > preferences > commitments > decisions > relationships > entities > moments > skills > facts
96
+ - Corrections get highest confidence.
97
+ - Each fact should be a standalone, self-contained statement.
98
+ - Entity references should use normalized names (lowercase, hyphenated: "jane-doe", "acme-corp")
99
+ - CRITICAL: Entity names must be CANONICAL. Always use the hyphenated multi-word form: "acme-corp" NOT "acmecorp" or "acme". "jane-doe" NOT "janedoe" or "jane". If unsure, prefer the most specific full name.
100
+ - Avoid creating entities typed as "other" when a more specific type fits (company, project, tool, person, place)
101
+ - When entity facts clearly belong under a durable named heading, add them to entity.structuredSections as {key, title, facts}. Example person headings: "Beliefs", "Communication Style", "Building / Working On". Leave structuredSections empty when no stable heading fits.
102
+ - Tags should be concise and reusable (e.g., "coding-style", "personal", "tools")
103
+ - When a fact contains measurable, categorical, or precisely valued data, include a "structuredAttributes" field with key-value string pairs (e.g., {"price": "29.99", "brand": "Sony"}, {"date": "2024-03-15", "location": "SF"}, {"chosen": "PostgreSQL", "rejected": "MongoDB"}). Only for concrete values, not narrative content.
104
+ - Set confidence using these tiers:
105
+ * Explicit (0.95-1.0): Direct user statements — "I prefer X", "my name is Y"
106
+ * Implied (0.70-0.94): Strong contextual inference — user consistently does X, clear from conversation flow
107
+ * Inferred (0.40-0.69): Pattern recognition — reasonable guess from limited evidence
108
+ * Speculative (0.00-0.39): Tentative hypothesis — weak signal, needs future confirmation. Speculative memories auto-expire after 30 days if not confirmed.${ambientCapture ? AMBIENT_SPECULATIVE_TIER_CLAUSE : ""}${config.provenance?.enabled ? `
109
+ - Source quotes: For each fact, include a "quote" field containing the EXACT verbatim words from the conversation that support the fact. Copy a contiguous span from a single speaker turn (not a paraphrase, not a summary). Cap at ~300 characters. This grounds every memory in the literal utterance that created it.` : ""}
110
+ - For commitments: include any deadline or timeframe mentioned${lifecycleCaps.extractionScopeClassification ? `
111
+
112
+ Scope classification:
113
+ For each fact, set "scope" to one of:
114
+ - "global" — knowledge that applies across projects: core framework/library bugs, API behavior patterns, user preferences (editor, language, style), general coding patterns, infrastructure knowledge, technology facts not tied to one codebase
115
+ - "project" — knowledge specific to one codebase: file paths, environment configs, deployment details, project-specific workarounds, team/stakeholder info tied to one project, repo-specific conventions. Tool, command, or CLI-flag instructions TIED TO ONE AGENT are also "project", because the same tool name means different things in different agent integrations (a "search" tool may search repository code in one agent and the web in another); when keeping such an agent-tied instruction, the fact text MUST name the originating agent as a leading "In <agent>," clause (e.g. "In Pi, use the search tool with a repository path."). A tool/command fact that holds in every agent and every repo is NOT agent-tied — leave it "global" and do not add a qualifier (e.g. "\`git status --short\` emits compact output"). Examples: "In Pi, the search tool takes a repository path" -> "project"; "\`git status --short\` emits compact output" -> "global".
116
+ When in doubt, prefer "project" — it is safer to keep knowledge scoped narrowly.` : ""}
117
+ ${ambientCapture ? `${AMBIENT_CAPTURE_PROMPT_SECTION}
118
+ ` : ""}
119
+ Entity creation rules (STRICT):
120
+ - Only create entities for DURABLE things: real people, companies, products, tools, ongoing projects
121
+ - NEVER create entities for transient items: individual PRs, branches, Jira tickets, meetings, agent task IDs, log files, database tables, cron job runs, sessions
122
+ - When you learn something about a transient item (e.g., PR #58 fixed a bug), store it as a FACT with an entityRef to the parent project — do NOT create an entity for the PR itself
123
+ - Prefer attaching facts to broad parent entities rather than creating sub-entities. E.g., "acme-store uses Algolia for search" is a fact on entity "acme-store", NOT a new entity "acme-store-algolia-connector"
124
+ - The entity list should be SHORT — think "things that would have their own Wikipedia page" not "things mentioned in passing"
125
+
126
+ ${existingEntities && existingEntities.length > 0 ? `
127
+ KNOWN ENTITIES (use these exact names when referencing existing things):
128
+ ${existingEntities.join(", ")}
129
+
130
+ When you see something that matches a known entity, use THAT name exactly. Only create a NEW entity if nothing in this list represents it.
131
+ ` : ""}
132
+ ${eventTimePromptInstruction(config)}
133
+ Also extract relationships between entities mentioned in the conversation.
134
+ - Format: {source: "entity-name", target: "entity-name", label: "relationship description"}
135
+ - Max 5 relationships per extraction
136
+ - Only include clear, durable relationships (e.g., "works at", "created", "manages", "uses")
137
+ - Use normalized entity names (e.g., "person-jane-doe", "company-acme-corp")
138
+
139
+ Questions are optional. Include only source-grounded unresolved questions that would be useful in future sessions; otherwise return an empty array.
140
+
141
+ Finally, write a brief identity reflection about the agent who had this conversation, based only on the conversation. Do not write about the extraction process.`;
142
+ }