nlm-memory 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (472) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/.github/workflows/ci.yml +30 -0
  3. package/LICENSE +151 -0
  4. package/README.md +119 -0
  5. package/dist/cli/classify-parity.d.ts +48 -0
  6. package/dist/cli/classify-parity.js +182 -0
  7. package/dist/cli/classify-parity.js.map +1 -0
  8. package/dist/cli/launchctl-helpers.d.ts +26 -0
  9. package/dist/cli/launchctl-helpers.js +42 -0
  10. package/dist/cli/launchctl-helpers.js.map +1 -0
  11. package/dist/cli/nlm.d.ts +25 -0
  12. package/dist/cli/nlm.js +832 -0
  13. package/dist/cli/nlm.js.map +1 -0
  14. package/dist/core/actions/actions-log.d.ts +40 -0
  15. package/dist/core/actions/actions-log.js +72 -0
  16. package/dist/core/actions/actions-log.js.map +1 -0
  17. package/dist/core/actions/overlay.d.ts +30 -0
  18. package/dist/core/actions/overlay.js +101 -0
  19. package/dist/core/actions/overlay.js.map +1 -0
  20. package/dist/core/adapters/aider.d.ts +33 -0
  21. package/dist/core/adapters/aider.js +167 -0
  22. package/dist/core/adapters/aider.js.map +1 -0
  23. package/dist/core/adapters/claude-code.d.ts +32 -0
  24. package/dist/core/adapters/claude-code.js +270 -0
  25. package/dist/core/adapters/claude-code.js.map +1 -0
  26. package/dist/core/adapters/common.d.ts +20 -0
  27. package/dist/core/adapters/common.js +60 -0
  28. package/dist/core/adapters/common.js.map +1 -0
  29. package/dist/core/adapters/from-source.d.ts +11 -0
  30. package/dist/core/adapters/from-source.js +55 -0
  31. package/dist/core/adapters/from-source.js.map +1 -0
  32. package/dist/core/adapters/hermes-agent.d.ts +34 -0
  33. package/dist/core/adapters/hermes-agent.js +192 -0
  34. package/dist/core/adapters/hermes-agent.js.map +1 -0
  35. package/dist/core/adapters/hermes.d.ts +31 -0
  36. package/dist/core/adapters/hermes.js +247 -0
  37. package/dist/core/adapters/hermes.js.map +1 -0
  38. package/dist/core/adapters/jsonl-generic.d.ts +56 -0
  39. package/dist/core/adapters/jsonl-generic.js +185 -0
  40. package/dist/core/adapters/jsonl-generic.js.map +1 -0
  41. package/dist/core/adapters/opencode.d.ts +36 -0
  42. package/dist/core/adapters/opencode.js +213 -0
  43. package/dist/core/adapters/opencode.js.map +1 -0
  44. package/dist/core/adapters/pi.d.ts +32 -0
  45. package/dist/core/adapters/pi.js +233 -0
  46. package/dist/core/adapters/pi.js.map +1 -0
  47. package/dist/core/classifier/prompt.d.ts +60 -0
  48. package/dist/core/classifier/prompt.js +178 -0
  49. package/dist/core/classifier/prompt.js.map +1 -0
  50. package/dist/core/dataset/build-dataset.d.ts +87 -0
  51. package/dist/core/dataset/build-dataset.js +335 -0
  52. package/dist/core/dataset/build-dataset.js.map +1 -0
  53. package/dist/core/embedding/chunk-body.d.ts +30 -0
  54. package/dist/core/embedding/chunk-body.js +60 -0
  55. package/dist/core/embedding/chunk-body.js.map +1 -0
  56. package/dist/core/embedding/embed-backfill.d.ts +36 -0
  57. package/dist/core/embedding/embed-backfill.js +168 -0
  58. package/dist/core/embedding/embed-backfill.js.map +1 -0
  59. package/dist/core/embedding/embed-normalize.d.ts +28 -0
  60. package/dist/core/embedding/embed-normalize.js +98 -0
  61. package/dist/core/embedding/embed-normalize.js.map +1 -0
  62. package/dist/core/facts/backfill-facts.d.ts +58 -0
  63. package/dist/core/facts/backfill-facts.js +169 -0
  64. package/dist/core/facts/backfill-facts.js.map +1 -0
  65. package/dist/core/facts/extract-facts.d.ts +20 -0
  66. package/dist/core/facts/extract-facts.js +37 -0
  67. package/dist/core/facts/extract-facts.js.map +1 -0
  68. package/dist/core/hook/citation-detect.d.ts +32 -0
  69. package/dist/core/hook/citation-detect.js +105 -0
  70. package/dist/core/hook/citation-detect.js.map +1 -0
  71. package/dist/core/hook/cite-memo.d.ts +20 -0
  72. package/dist/core/hook/cite-memo.js +68 -0
  73. package/dist/core/hook/cite-memo.js.map +1 -0
  74. package/dist/core/hook/claude-settings.d.ts +34 -0
  75. package/dist/core/hook/claude-settings.js +117 -0
  76. package/dist/core/hook/claude-settings.js.map +1 -0
  77. package/dist/core/hook/gate.d.ts +11 -0
  78. package/dist/core/hook/gate.js +19 -0
  79. package/dist/core/hook/gate.js.map +1 -0
  80. package/dist/core/hook/hook-log.d.ts +25 -0
  81. package/dist/core/hook/hook-log.js +28 -0
  82. package/dist/core/hook/hook-log.js.map +1 -0
  83. package/dist/core/hook/memo-sweep.d.ts +55 -0
  84. package/dist/core/hook/memo-sweep.js +134 -0
  85. package/dist/core/hook/memo-sweep.js.map +1 -0
  86. package/dist/core/hook/memo.d.ts +20 -0
  87. package/dist/core/hook/memo.js +66 -0
  88. package/dist/core/hook/memo.js.map +1 -0
  89. package/dist/core/hook/pointer-block.d.ts +14 -0
  90. package/dist/core/hook/pointer-block.js +19 -0
  91. package/dist/core/hook/pointer-block.js.map +1 -0
  92. package/dist/core/hook/select.d.ts +21 -0
  93. package/dist/core/hook/select.js +15 -0
  94. package/dist/core/hook/select.js.map +1 -0
  95. package/dist/core/hook/transcript.d.ts +31 -0
  96. package/dist/core/hook/transcript.js +103 -0
  97. package/dist/core/hook/transcript.js.map +1 -0
  98. package/dist/core/ingest/ingest-session.d.ts +40 -0
  99. package/dist/core/ingest/ingest-session.js +71 -0
  100. package/dist/core/ingest/ingest-session.js.map +1 -0
  101. package/dist/core/providers/provider-models.d.ts +24 -0
  102. package/dist/core/providers/provider-models.js +72 -0
  103. package/dist/core/providers/provider-models.js.map +1 -0
  104. package/dist/core/providers/provider-registry.d.ts +62 -0
  105. package/dist/core/providers/provider-registry.js +143 -0
  106. package/dist/core/providers/provider-registry.js.map +1 -0
  107. package/dist/core/recall/citation-log.d.ts +28 -0
  108. package/dist/core/recall/citation-log.js +90 -0
  109. package/dist/core/recall/citation-log.js.map +1 -0
  110. package/dist/core/recall/filter.d.ts +11 -0
  111. package/dist/core/recall/filter.js +20 -0
  112. package/dist/core/recall/filter.js.map +1 -0
  113. package/dist/core/recall/index.d.ts +6 -0
  114. package/dist/core/recall/index.js +5 -0
  115. package/dist/core/recall/index.js.map +1 -0
  116. package/dist/core/recall/match-fields.d.ts +10 -0
  117. package/dist/core/recall/match-fields.js +37 -0
  118. package/dist/core/recall/match-fields.js.map +1 -0
  119. package/dist/core/recall/query-log.d.ts +36 -0
  120. package/dist/core/recall/query-log.js +112 -0
  121. package/dist/core/recall/query-log.js.map +1 -0
  122. package/dist/core/recall/query-shape.d.ts +22 -0
  123. package/dist/core/recall/query-shape.js +64 -0
  124. package/dist/core/recall/query-shape.js.map +1 -0
  125. package/dist/core/recall/recall-service.d.ts +19 -0
  126. package/dist/core/recall/recall-service.js +252 -0
  127. package/dist/core/recall/recall-service.js.map +1 -0
  128. package/dist/core/recall/recent-log.d.ts +16 -0
  129. package/dist/core/recall/recent-log.js +46 -0
  130. package/dist/core/recall/recent-log.js.map +1 -0
  131. package/dist/core/recall/tokenize.d.ts +7 -0
  132. package/dist/core/recall/tokenize.js +18 -0
  133. package/dist/core/recall/tokenize.js.map +1 -0
  134. package/dist/core/recall/useful-scan.d.ts +52 -0
  135. package/dist/core/recall/useful-scan.js +300 -0
  136. package/dist/core/recall/useful-scan.js.map +1 -0
  137. package/dist/core/recall-facts/fact-query-log.d.ts +42 -0
  138. package/dist/core/recall-facts/fact-query-log.js +115 -0
  139. package/dist/core/recall-facts/fact-query-log.js.map +1 -0
  140. package/dist/core/recall-facts/fact-recall-service.d.ts +34 -0
  141. package/dist/core/recall-facts/fact-recall-service.js +246 -0
  142. package/dist/core/recall-facts/fact-recall-service.js.map +1 -0
  143. package/dist/core/scheduler/scan-once.d.ts +32 -0
  144. package/dist/core/scheduler/scan-once.js +100 -0
  145. package/dist/core/scheduler/scan-once.js.map +1 -0
  146. package/dist/core/scheduler/scheduler.d.ts +59 -0
  147. package/dist/core/scheduler/scheduler.js +158 -0
  148. package/dist/core/scheduler/scheduler.js.map +1 -0
  149. package/dist/core/sources/source-registry.d.ts +68 -0
  150. package/dist/core/sources/source-registry.js +208 -0
  151. package/dist/core/sources/source-registry.js.map +1 -0
  152. package/dist/core/storage/db-restore.d.ts +53 -0
  153. package/dist/core/storage/db-restore.js +113 -0
  154. package/dist/core/storage/db-restore.js.map +1 -0
  155. package/dist/core/storage/live-status.d.ts +15 -0
  156. package/dist/core/storage/live-status.js +43 -0
  157. package/dist/core/storage/live-status.js.map +1 -0
  158. package/dist/core/storage/migrate.d.ts +14 -0
  159. package/dist/core/storage/migrate.js +52 -0
  160. package/dist/core/storage/migrate.js.map +1 -0
  161. package/dist/core/storage/sqlite-fact-store.d.ts +50 -0
  162. package/dist/core/storage/sqlite-fact-store.js +256 -0
  163. package/dist/core/storage/sqlite-fact-store.js.map +1 -0
  164. package/dist/core/storage/sqlite-session-store.d.ts +152 -0
  165. package/dist/core/storage/sqlite-session-store.js +587 -0
  166. package/dist/core/storage/sqlite-session-store.js.map +1 -0
  167. package/dist/hook/pre-compact-hook.d.ts +26 -0
  168. package/dist/hook/pre-compact-hook.js +94 -0
  169. package/dist/hook/pre-compact-hook.js.map +1 -0
  170. package/dist/hook/prompt-recall-hook.d.ts +23 -0
  171. package/dist/hook/prompt-recall-hook.js +141 -0
  172. package/dist/hook/prompt-recall-hook.js.map +1 -0
  173. package/dist/hook/session-end-hook.d.ts +18 -0
  174. package/dist/hook/session-end-hook.js +67 -0
  175. package/dist/hook/session-end-hook.js.map +1 -0
  176. package/dist/hook/session-start-hook.d.ts +25 -0
  177. package/dist/hook/session-start-hook.js +129 -0
  178. package/dist/hook/session-start-hook.js.map +1 -0
  179. package/dist/hook/stop-hook.d.ts +38 -0
  180. package/dist/hook/stop-hook.js +171 -0
  181. package/dist/hook/stop-hook.js.map +1 -0
  182. package/dist/hook/subagent-start-hook.d.ts +30 -0
  183. package/dist/hook/subagent-start-hook.js +108 -0
  184. package/dist/hook/subagent-start-hook.js.map +1 -0
  185. package/dist/http/app.d.ts +65 -0
  186. package/dist/http/app.js +1009 -0
  187. package/dist/http/app.js.map +1 -0
  188. package/dist/install/claude-code.d.ts +57 -0
  189. package/dist/install/claude-code.js +76 -0
  190. package/dist/install/claude-code.js.map +1 -0
  191. package/dist/install/codex.d.ts +82 -0
  192. package/dist/install/codex.js +277 -0
  193. package/dist/install/codex.js.map +1 -0
  194. package/dist/install/hermes-agent.d.ts +35 -0
  195. package/dist/install/hermes-agent.js +48 -0
  196. package/dist/install/hermes-agent.js.map +1 -0
  197. package/dist/install/hermes.d.ts +29 -0
  198. package/dist/install/hermes.js +52 -0
  199. package/dist/install/hermes.js.map +1 -0
  200. package/dist/install/ollama.d.ts +54 -0
  201. package/dist/install/ollama.js +178 -0
  202. package/dist/install/ollama.js.map +1 -0
  203. package/dist/install/setup.d.ts +37 -0
  204. package/dist/install/setup.js +339 -0
  205. package/dist/install/setup.js.map +1 -0
  206. package/dist/llm/classifier-box.d.ts +29 -0
  207. package/dist/llm/classifier-box.js +43 -0
  208. package/dist/llm/classifier-box.js.map +1 -0
  209. package/dist/llm/deepseek-client.d.ts +40 -0
  210. package/dist/llm/deepseek-client.js +114 -0
  211. package/dist/llm/deepseek-client.js.map +1 -0
  212. package/dist/llm/env-autoload.d.ts +8 -0
  213. package/dist/llm/env-autoload.js +54 -0
  214. package/dist/llm/env-autoload.js.map +1 -0
  215. package/dist/llm/ollama-client.d.ts +47 -0
  216. package/dist/llm/ollama-client.js +156 -0
  217. package/dist/llm/ollama-client.js.map +1 -0
  218. package/dist/mcp/server.d.ts +64 -0
  219. package/dist/mcp/server.js +430 -0
  220. package/dist/mcp/server.js.map +1 -0
  221. package/dist/ports/fact-store.d.ts +82 -0
  222. package/dist/ports/fact-store.js +16 -0
  223. package/dist/ports/fact-store.js.map +1 -0
  224. package/dist/ports/llm-client.d.ts +42 -0
  225. package/dist/ports/llm-client.js +14 -0
  226. package/dist/ports/llm-client.js.map +1 -0
  227. package/dist/ports/logger.d.ts +13 -0
  228. package/dist/ports/logger.js +8 -0
  229. package/dist/ports/logger.js.map +1 -0
  230. package/dist/ports/session-store.d.ts +29 -0
  231. package/dist/ports/session-store.js +9 -0
  232. package/dist/ports/session-store.js.map +1 -0
  233. package/dist/ports/transcript-adapter.d.ts +48 -0
  234. package/dist/ports/transcript-adapter.js +15 -0
  235. package/dist/ports/transcript-adapter.js.map +1 -0
  236. package/dist/shared/types.d.ts +129 -0
  237. package/dist/shared/types.js +6 -0
  238. package/dist/shared/types.js.map +1 -0
  239. package/dist/ui/assets/index-BA6IpU8g.css +1 -0
  240. package/dist/ui/assets/index-B_qIVV0k.js +69 -0
  241. package/dist/ui/index.html +13 -0
  242. package/docs/methodology/re-derivation-rate.md +112 -0
  243. package/docs/methodology/useful-hit-rate.md +79 -0
  244. package/docs/plans/2026-05-20-fts5-lexical-recall.md +1088 -0
  245. package/docs/plans/2026-05-20-recall-daemon-wedge-fix.md +662 -0
  246. package/docs/plans/2026-05-20-recall-hook-design.md +131 -0
  247. package/docs/plans/2026-05-20-recall-hook-implementation.md +1222 -0
  248. package/docs/plans/desktop-product.md +69 -0
  249. package/docs/plans/factstore-design.md +236 -0
  250. package/logs/CHANGELOG/CHANGELOG-2026.md +1389 -0
  251. package/logs/CHANGELOG/CHANGELOG.md +320 -0
  252. package/migrations/000_initial_schema.sql +174 -0
  253. package/migrations/001_entity_type_rename.sql +17 -0
  254. package/migrations/002_adapter_state_extend.sql +12 -0
  255. package/migrations/003_session_embeddings.sql +11 -0
  256. package/migrations/004_facts.sql +46 -0
  257. package/migrations/005_sources.sql +31 -0
  258. package/migrations/006_providers.sql +33 -0
  259. package/migrations/007_source_tokens.sql +17 -0
  260. package/migrations/008_fts_rebuild.sql +9 -0
  261. package/migrations/009_session_embedding_chunks.sql +46 -0
  262. package/migrations/010_sources_opencode.sql +30 -0
  263. package/migrations/011_sources_hermes_agent.sql +30 -0
  264. package/migrations/012_sources_aider.sql +30 -0
  265. package/migrations/013_adapter_state_failure_count.sql +12 -0
  266. package/package.json +56 -0
  267. package/plugin/.codex-plugin/plugin.json +22 -0
  268. package/plugin/.mcp.json +8 -0
  269. package/plugin/README.md +51 -0
  270. package/plugin/hooks/hooks.json +25 -0
  271. package/plugin/scripts/prompt-recall-hook.mjs +202 -0
  272. package/plugin/scripts/stop-hook.mjs +306 -0
  273. package/plugin-hermes-agent/README.md +49 -0
  274. package/plugin-hermes-agent/__init__.py +75 -0
  275. package/plugin-hermes-agent/plugin.yaml +15 -0
  276. package/scripts/backfill-citations.mjs +0 -0
  277. package/scripts/build-codex-plugin.mjs +61 -0
  278. package/scripts/deepseek-probe.mjs +67 -0
  279. package/scripts/extract-triples.mjs +207 -0
  280. package/scripts/longmemeval/embedding-cache.ts +77 -0
  281. package/scripts/longmemeval/fetch-dataset.sh +25 -0
  282. package/scripts/longmemeval/run-harness.ts +315 -0
  283. package/scripts/longmemeval/scorer.ts +99 -0
  284. package/scripts/longmemeval/tsconfig.json +9 -0
  285. package/scripts/longmemeval/types.ts +35 -0
  286. package/scripts/nlm-daily-digest.py +239 -0
  287. package/scripts/nlm-daily-digest.sh +28 -0
  288. package/src/cli/classify-parity.ts +257 -0
  289. package/src/cli/launchctl-helpers.ts +49 -0
  290. package/src/cli/nlm.ts +885 -0
  291. package/src/core/actions/actions-log.ts +118 -0
  292. package/src/core/actions/overlay.ts +117 -0
  293. package/src/core/adapters/aider.ts +205 -0
  294. package/src/core/adapters/claude-code.ts +293 -0
  295. package/src/core/adapters/common.ts +54 -0
  296. package/src/core/adapters/from-source.ts +57 -0
  297. package/src/core/adapters/hermes-agent.ts +240 -0
  298. package/src/core/adapters/hermes.ts +277 -0
  299. package/src/core/adapters/jsonl-generic.ts +208 -0
  300. package/src/core/adapters/opencode.ts +281 -0
  301. package/src/core/adapters/pi.ts +264 -0
  302. package/src/core/classifier/prompt.ts +200 -0
  303. package/src/core/dataset/build-dataset.ts +463 -0
  304. package/src/core/embedding/chunk-body.ts +76 -0
  305. package/src/core/embedding/embed-backfill.ts +210 -0
  306. package/src/core/embedding/embed-normalize.ts +135 -0
  307. package/src/core/facts/backfill-facts.ts +254 -0
  308. package/src/core/facts/extract-facts.ts +50 -0
  309. package/src/core/hook/citation-detect.ts +124 -0
  310. package/src/core/hook/cite-memo.ts +68 -0
  311. package/src/core/hook/claude-settings.ts +166 -0
  312. package/src/core/hook/gate.ts +25 -0
  313. package/src/core/hook/hook-log.ts +41 -0
  314. package/src/core/hook/memo-sweep.ts +164 -0
  315. package/src/core/hook/memo.ts +67 -0
  316. package/src/core/hook/pointer-block.ts +26 -0
  317. package/src/core/hook/select.ts +32 -0
  318. package/src/core/hook/transcript.ts +121 -0
  319. package/src/core/ingest/ingest-session.ts +111 -0
  320. package/src/core/providers/provider-models.ts +100 -0
  321. package/src/core/providers/provider-registry.ts +196 -0
  322. package/src/core/recall/citation-log.ts +108 -0
  323. package/src/core/recall/filter.ts +27 -0
  324. package/src/core/recall/index.ts +6 -0
  325. package/src/core/recall/match-fields.ts +40 -0
  326. package/src/core/recall/query-log.ts +149 -0
  327. package/src/core/recall/query-shape.ts +66 -0
  328. package/src/core/recall/recall-service.ts +320 -0
  329. package/src/core/recall/recent-log.ts +59 -0
  330. package/src/core/recall/tokenize.ts +18 -0
  331. package/src/core/recall/useful-scan.ts +336 -0
  332. package/src/core/recall-facts/fact-query-log.ts +150 -0
  333. package/src/core/recall-facts/fact-recall-service.ts +327 -0
  334. package/src/core/scheduler/scan-once.ts +142 -0
  335. package/src/core/scheduler/scheduler.ts +225 -0
  336. package/src/core/sources/source-registry.ts +260 -0
  337. package/src/core/storage/db-restore.ts +133 -0
  338. package/src/core/storage/live-status.ts +45 -0
  339. package/src/core/storage/migrate.ts +72 -0
  340. package/src/core/storage/sqlite-fact-store.ts +304 -0
  341. package/src/core/storage/sqlite-session-store.ts +765 -0
  342. package/src/hook/prompt-recall-hook.ts +174 -0
  343. package/src/hook/session-end-hook.ts +81 -0
  344. package/src/hook/session-start-hook.ts +165 -0
  345. package/src/hook/stop-hook.ts +236 -0
  346. package/src/http/app.ts +1114 -0
  347. package/src/install/claude-code.ts +128 -0
  348. package/src/install/codex.ts +367 -0
  349. package/src/install/hermes-agent.ts +76 -0
  350. package/src/install/hermes.ts +78 -0
  351. package/src/install/ollama.ts +208 -0
  352. package/src/install/setup.ts +368 -0
  353. package/src/llm/classifier-box.ts +64 -0
  354. package/src/llm/deepseek-client.ts +150 -0
  355. package/src/llm/env-autoload.ts +55 -0
  356. package/src/llm/ollama-client.ts +189 -0
  357. package/src/mcp/server.ts +534 -0
  358. package/src/ports/fact-store.ts +102 -0
  359. package/src/ports/llm-client.ts +52 -0
  360. package/src/ports/logger.ts +16 -0
  361. package/src/ports/session-store.ts +45 -0
  362. package/src/ports/transcript-adapter.ts +55 -0
  363. package/src/shared/types.ts +145 -0
  364. package/src/ui/App.tsx +58 -0
  365. package/src/ui/components/PromoteOpenButton.tsx +65 -0
  366. package/src/ui/components/SessionDrawer.tsx +136 -0
  367. package/src/ui/components/SideNav.tsx +162 -0
  368. package/src/ui/components/Skeleton.tsx +107 -0
  369. package/src/ui/index.html +13 -0
  370. package/src/ui/lib/actions.ts +30 -0
  371. package/src/ui/lib/api.ts +92 -0
  372. package/src/ui/lib/dataset.ts +141 -0
  373. package/src/ui/lib/registries.ts +155 -0
  374. package/src/ui/lib/view-settings.ts +41 -0
  375. package/src/ui/main.tsx +15 -0
  376. package/src/ui/pages/Live.tsx +229 -0
  377. package/src/ui/pages/Pulse.tsx +415 -0
  378. package/src/ui/pages/Recall.tsx +190 -0
  379. package/src/ui/pages/River.tsx +308 -0
  380. package/src/ui/pages/Search.tsx +93 -0
  381. package/src/ui/pages/Stub.tsx +9 -0
  382. package/src/ui/pages/Thread.tsx +262 -0
  383. package/src/ui/pages/settings/Classifier.tsx +227 -0
  384. package/src/ui/pages/settings/Data.tsx +190 -0
  385. package/src/ui/pages/settings/Index.tsx +65 -0
  386. package/src/ui/pages/settings/Labels.tsx +224 -0
  387. package/src/ui/pages/settings/Providers.tsx +305 -0
  388. package/src/ui/pages/settings/SettingsSubnav.tsx +28 -0
  389. package/src/ui/pages/settings/Sources.tsx +326 -0
  390. package/src/ui/pages/settings/Views.tsx +96 -0
  391. package/src/ui/styles.css +1766 -0
  392. package/src/ui/tsconfig.json +21 -0
  393. package/src/ui/vite.config.ts +19 -0
  394. package/tests/fixtures/claude_code/short_session.jsonl +2 -0
  395. package/tests/fixtures/claude_code/standard_iso.jsonl +4 -0
  396. package/tests/fixtures/claude_code/tool_heavy.jsonl +8 -0
  397. package/tests/fixtures/claude_code/with_subagent.jsonl +7 -0
  398. package/tests/fixtures/facts.ts +17 -0
  399. package/tests/fixtures/golden-corpus.ts +85 -0
  400. package/tests/fixtures/hermes/paired_request_dump.json +24 -0
  401. package/tests/fixtures/hermes/paired_session.json +23 -0
  402. package/tests/fixtures/hermes/request_dump.json +28 -0
  403. package/tests/fixtures/hermes/session_iso.json +38 -0
  404. package/tests/fixtures/hermes/session_unix.json +38 -0
  405. package/tests/fixtures/hermes/system_only.json +18 -0
  406. package/tests/fixtures/pi/error-connection-abort.jsonl +8 -0
  407. package/tests/fixtures/pi/short-successful.jsonl +5 -0
  408. package/tests/fixtures/pi/with-custom-message.jsonl +6 -0
  409. package/tests/fixtures/sessions.ts +22 -0
  410. package/tests/integration/backfill-facts.test.ts +362 -0
  411. package/tests/integration/citation-explicit.test.ts +111 -0
  412. package/tests/integration/cite-event.test.ts +169 -0
  413. package/tests/integration/cite-memo.test.ts +87 -0
  414. package/tests/integration/db-restore.test.ts +153 -0
  415. package/tests/integration/embed-backfill.test.ts +176 -0
  416. package/tests/integration/fact-supersedence.test.ts +313 -0
  417. package/tests/integration/fts-index.test.ts +60 -0
  418. package/tests/integration/getbyids-sqlite.test.ts +60 -0
  419. package/tests/integration/hermes-agent-hooks.test.ts +248 -0
  420. package/tests/integration/hook-claude-settings.test.ts +205 -0
  421. package/tests/integration/hook-log.test.ts +54 -0
  422. package/tests/integration/hook-memo.test.ts +68 -0
  423. package/tests/integration/hook-pre-compact.test.ts +105 -0
  424. package/tests/integration/hook-subagent-start.test.ts +102 -0
  425. package/tests/integration/http.test.ts +401 -0
  426. package/tests/integration/keyword-search-fts.test.ts +66 -0
  427. package/tests/integration/mcp-recall-logging.test.ts +88 -0
  428. package/tests/integration/mcp.test.ts +248 -0
  429. package/tests/integration/memo-sweep.test.ts +91 -0
  430. package/tests/integration/prompt-recall-hook.test.ts +88 -0
  431. package/tests/integration/provider-registry.test.ts +107 -0
  432. package/tests/integration/recall-golden.test.ts +59 -0
  433. package/tests/integration/recall-sqlite.test.ts +169 -0
  434. package/tests/integration/scheduler.test.ts +391 -0
  435. package/tests/integration/session-end-hook.test.ts +48 -0
  436. package/tests/integration/session-start-hook.test.ts +126 -0
  437. package/tests/integration/source-registry.test.ts +120 -0
  438. package/tests/integration/sqlite-fact-store.test.ts +346 -0
  439. package/tests/integration/stop-hook.test.ts +560 -0
  440. package/tests/integration/wal-checkpoint.test.ts +49 -0
  441. package/tests/unit/cli/launchctl-helpers.test.ts +60 -0
  442. package/tests/unit/core/adapters/aider.test.ts +230 -0
  443. package/tests/unit/core/adapters/claude-code.test.ts +118 -0
  444. package/tests/unit/core/adapters/hermes-agent.test.ts +329 -0
  445. package/tests/unit/core/adapters/hermes.test.ts +81 -0
  446. package/tests/unit/core/adapters/jsonl-generic.test.ts +142 -0
  447. package/tests/unit/core/adapters/opencode.test.ts +354 -0
  448. package/tests/unit/core/adapters/pi.test.ts +110 -0
  449. package/tests/unit/core/classifier/prompt.test.ts +126 -0
  450. package/tests/unit/core/embedding/chunk-body.test.ts +100 -0
  451. package/tests/unit/core/facts/extract-facts.test.ts +117 -0
  452. package/tests/unit/core/filter.test.ts +40 -0
  453. package/tests/unit/core/hook/citation-detect-cite-session.test.ts +96 -0
  454. package/tests/unit/core/hook/citation-detect.test.ts +124 -0
  455. package/tests/unit/core/hook/gate.test.ts +29 -0
  456. package/tests/unit/core/hook/pointer-block.test.ts +22 -0
  457. package/tests/unit/core/hook/select.test.ts +66 -0
  458. package/tests/unit/core/match-fields.test.ts +39 -0
  459. package/tests/unit/core/mcp-cite-session.test.ts +51 -0
  460. package/tests/unit/core/providers/provider-models.test.ts +101 -0
  461. package/tests/unit/core/query-shape.test.ts +92 -0
  462. package/tests/unit/core/recall-facts/fact-recall-service.test.ts +258 -0
  463. package/tests/unit/core/recall-service.test.ts +200 -0
  464. package/tests/unit/core/storage/live-status.test.ts +54 -0
  465. package/tests/unit/core/tokenize.test.ts +32 -0
  466. package/tests/unit/core/useful-scan.test.ts +537 -0
  467. package/tests/unit/llm/embed.test.ts +93 -0
  468. package/tests/unit/llm/ollama-client.test.ts +124 -0
  469. package/tests/unit/scripts/longmemeval-scorer.test.ts +114 -0
  470. package/tsconfig.json +31 -0
  471. package/tsconfig.test.json +11 -0
  472. package/vitest.config.ts +22 -0
@@ -0,0 +1,300 @@
1
+ /**
2
+ * Batch scanner for the useful_hit_rate metric.
3
+ *
4
+ * Joins hook-log.jsonl (recall events) against Claude Code conversation
5
+ * transcripts, writes one useful-hit-log.jsonl entry per recall event,
6
+ * and returns aggregate counts.
7
+ *
8
+ * A recall is "useful" when ≥1 of the surfaced session IDs appears in the
9
+ * text or tool_use inputs of the next NEXT_TURNS_LIMIT assistant turns after
10
+ * the hook fired. Entries with no matching transcript get useful=null
11
+ * (unmeasurable).
12
+ *
13
+ * Probe entries (promptPreview matching PROBE_PATTERNS) are excluded from
14
+ * the scan to keep the metric clean.
15
+ */
16
+ import { readFile, appendFile, stat, mkdir, readdir } from "node:fs/promises";
17
+ import { existsSync, readFileSync } from "node:fs";
18
+ import { dirname, join } from "node:path";
19
+ import { homedir } from "node:os";
20
+ const NEXT_TURNS_LIMIT = 3;
21
+ const PROBE_PATTERNS = [
22
+ /concurrency probe/i,
23
+ /test probe/i,
24
+ /path test/i,
25
+ /recall test/i,
26
+ /smoke/i,
27
+ /cutover/i,
28
+ ];
29
+ export function defaultUsefulHitLogPath() {
30
+ return process.env["NLM_USEFUL_HIT_LOG"] ?? join(homedir(), ".nlm", "useful-hit-log.jsonl");
31
+ }
32
+ function defaultHookLogPath() {
33
+ return process.env["NLM_HOOK_LOG"] ?? join(homedir(), ".nlm", "hook-log.jsonl");
34
+ }
35
+ function defaultTranscriptsDir() {
36
+ return join(homedir(), ".claude", "projects");
37
+ }
38
+ export function isProbe(promptPreview) {
39
+ return PROBE_PATTERNS.some((re) => re.test(promptPreview));
40
+ }
41
+ async function findTranscriptPath(conversationId, transcriptsDir) {
42
+ let names;
43
+ try {
44
+ names = await readdir(transcriptsDir);
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ for (const name of names) {
50
+ const candidate = join(transcriptsDir, name, `${conversationId}.jsonl`);
51
+ if (existsSync(candidate))
52
+ return candidate;
53
+ }
54
+ return null;
55
+ }
56
+ /**
57
+ * Read assistant turns from a Claude Code transcript JSONL that have a
58
+ * timestamp >= afterTs. Returns up to `limit` turns, each as a single
59
+ * concatenated string of text + serialized tool_use inputs.
60
+ */
61
+ export function extractAssistantTurnsAfter(transcriptPath, afterTs, limit) {
62
+ let raw;
63
+ try {
64
+ raw = readFileSync(transcriptPath, "utf8");
65
+ }
66
+ catch {
67
+ return [];
68
+ }
69
+ const turns = [];
70
+ let pastCutoff = false;
71
+ for (const line of raw.split("\n")) {
72
+ const trimmed = line.trim();
73
+ if (!trimmed)
74
+ continue;
75
+ let entry;
76
+ try {
77
+ entry = JSON.parse(trimmed);
78
+ }
79
+ catch {
80
+ continue;
81
+ }
82
+ if (!pastCutoff) {
83
+ const tsRaw = entry["timestamp"];
84
+ if (typeof tsRaw === "string") {
85
+ const ts = Date.parse(tsRaw);
86
+ if (Number.isFinite(ts) && ts >= afterTs)
87
+ pastCutoff = true;
88
+ }
89
+ if (!pastCutoff)
90
+ continue;
91
+ }
92
+ if (entry["type"] !== "assistant")
93
+ continue;
94
+ const message = entry["message"];
95
+ if (!message)
96
+ continue;
97
+ const content = message["content"];
98
+ const parts = [];
99
+ if (typeof content === "string") {
100
+ if (content)
101
+ parts.push(content);
102
+ }
103
+ else if (Array.isArray(content)) {
104
+ for (const block of content) {
105
+ if (block["type"] === "text" && typeof block["text"] === "string") {
106
+ parts.push(block["text"]);
107
+ }
108
+ else if (block["type"] === "tool_use") {
109
+ parts.push(JSON.stringify(block["input"]));
110
+ }
111
+ }
112
+ }
113
+ if (parts.length > 0) {
114
+ turns.push(parts.join(" "));
115
+ if (turns.length >= limit)
116
+ break;
117
+ }
118
+ }
119
+ return turns;
120
+ }
121
+ export function findMatchedId(ids, turns) {
122
+ const haystack = turns.join(" ");
123
+ for (const id of ids) {
124
+ if (haystack.includes(id))
125
+ return id;
126
+ }
127
+ return null;
128
+ }
129
+ async function readHookRecalls(hookLogPath, cutoff) {
130
+ let raw;
131
+ try {
132
+ raw = await readFile(hookLogPath, "utf8");
133
+ }
134
+ catch {
135
+ return [];
136
+ }
137
+ const results = [];
138
+ for (const line of raw.split("\n")) {
139
+ const trimmed = line.trim();
140
+ if (!trimmed)
141
+ continue;
142
+ let entry;
143
+ try {
144
+ entry = JSON.parse(trimmed);
145
+ }
146
+ catch {
147
+ continue;
148
+ }
149
+ // Skip stop-hook entries (they have a "kind" field)
150
+ if (typeof entry["kind"] === "string")
151
+ continue;
152
+ const ts = typeof entry["ts"] === "string" ? entry["ts"] : null;
153
+ if (!ts)
154
+ continue;
155
+ const tsMs = Date.parse(ts);
156
+ if (!Number.isFinite(tsMs) || tsMs < cutoff)
157
+ continue;
158
+ const wouldInject = Array.isArray(entry["wouldInject"])
159
+ ? entry["wouldInject"].filter((x) => typeof x === "string")
160
+ : [];
161
+ if (wouldInject.length === 0)
162
+ continue;
163
+ const conversationId = typeof entry["conversationId"] === "string" ? entry["conversationId"] : null;
164
+ if (!conversationId)
165
+ continue;
166
+ const promptPreview = typeof entry["promptPreview"] === "string" ? entry["promptPreview"] : "";
167
+ if (isProbe(promptPreview))
168
+ continue;
169
+ results.push({ ts, conversationId, promptPreview, wouldInject });
170
+ }
171
+ return results;
172
+ }
173
+ async function readScannedKeys(usefulHitLogPath) {
174
+ const seen = new Set();
175
+ try {
176
+ await stat(usefulHitLogPath);
177
+ }
178
+ catch {
179
+ return seen;
180
+ }
181
+ let raw;
182
+ try {
183
+ raw = await readFile(usefulHitLogPath, "utf8");
184
+ }
185
+ catch {
186
+ return seen;
187
+ }
188
+ for (const line of raw.split("\n")) {
189
+ const trimmed = line.trim();
190
+ if (!trimmed)
191
+ continue;
192
+ let entry;
193
+ try {
194
+ entry = JSON.parse(trimmed);
195
+ }
196
+ catch {
197
+ continue;
198
+ }
199
+ const ts = typeof entry["ts"] === "string" ? entry["ts"] : null;
200
+ const convId = typeof entry["conversationId"] === "string" ? entry["conversationId"] : null;
201
+ if (ts && convId)
202
+ seen.add(`${ts}:${convId}`);
203
+ }
204
+ return seen;
205
+ }
206
+ export async function scanUsefulHits(opts) {
207
+ const days = opts.days ?? 1;
208
+ const hookLogPath = opts.hookLogPath ?? defaultHookLogPath();
209
+ const usefulHitLogPath = opts.usefulHitLogPath ?? defaultUsefulHitLogPath();
210
+ const transcriptsDir = opts.transcriptsDir ?? defaultTranscriptsDir();
211
+ const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
212
+ const recalls = await readHookRecalls(hookLogPath, cutoff);
213
+ const scannedKeys = await readScannedKeys(usefulHitLogPath);
214
+ let total = 0;
215
+ let measurable = 0;
216
+ let useful = 0;
217
+ const newEntries = [];
218
+ for (const recall of recalls) {
219
+ total += 1;
220
+ const key = `${recall.ts}:${recall.conversationId}`;
221
+ if (scannedKeys.has(key))
222
+ continue;
223
+ const transcriptPath = await findTranscriptPath(recall.conversationId, transcriptsDir);
224
+ const hookTs = Date.parse(recall.ts);
225
+ let usefulVal = null;
226
+ let matchedId = null;
227
+ if (transcriptPath !== null && Number.isFinite(hookTs)) {
228
+ const turns = extractAssistantTurnsAfter(transcriptPath, hookTs, NEXT_TURNS_LIMIT);
229
+ if (turns.length > 0) {
230
+ measurable += 1;
231
+ matchedId = findMatchedId(recall.wouldInject, turns);
232
+ usefulVal = matchedId !== null;
233
+ if (usefulVal)
234
+ useful += 1;
235
+ }
236
+ }
237
+ newEntries.push({
238
+ ts: recall.ts,
239
+ source: "hook",
240
+ conversationId: recall.conversationId,
241
+ returnedIds: recall.wouldInject,
242
+ useful: usefulVal,
243
+ matchedId,
244
+ scannedAt: new Date().toISOString(),
245
+ });
246
+ }
247
+ if (!opts.dryRun && newEntries.length > 0) {
248
+ await mkdir(dirname(usefulHitLogPath), { recursive: true });
249
+ await appendFile(usefulHitLogPath, newEntries.map((e) => JSON.stringify(e)).join("\n") + "\n", "utf8");
250
+ }
251
+ return { total, measurable, useful, appended: opts.dryRun ? 0 : newEntries.length };
252
+ }
253
+ /**
254
+ * Compute useful_hit_rate from an existing useful-hit-log.jsonl over a
255
+ * rolling window. Returns null if the log is absent or has no measurable
256
+ * entries in the window.
257
+ */
258
+ export async function readUsefulHitRate(usefulHitLogPath = defaultUsefulHitLogPath(), days = 1) {
259
+ try {
260
+ await stat(usefulHitLogPath);
261
+ }
262
+ catch {
263
+ return null;
264
+ }
265
+ let raw;
266
+ try {
267
+ raw = await readFile(usefulHitLogPath, "utf8");
268
+ }
269
+ catch {
270
+ return null;
271
+ }
272
+ const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
273
+ let measurable = 0;
274
+ let useful = 0;
275
+ for (const line of raw.split("\n")) {
276
+ const trimmed = line.trim();
277
+ if (!trimmed)
278
+ continue;
279
+ let entry;
280
+ try {
281
+ entry = JSON.parse(trimmed);
282
+ }
283
+ catch {
284
+ continue;
285
+ }
286
+ const tsRaw = typeof entry["ts"] === "string" ? entry["ts"] : null;
287
+ if (!tsRaw)
288
+ continue;
289
+ const ts = Date.parse(tsRaw);
290
+ if (!Number.isFinite(ts) || ts < cutoff)
291
+ continue;
292
+ if (entry["useful"] === null || entry["useful"] === undefined)
293
+ continue;
294
+ measurable += 1;
295
+ if (entry["useful"] === true)
296
+ useful += 1;
297
+ }
298
+ return measurable === 0 ? null : Math.round((useful / measurable) * 1000) / 1000;
299
+ }
300
+ //# sourceMappingURL=useful-scan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useful-scan.js","sourceRoot":"","sources":["../../../src/core/recall/useful-scan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Fact query log + stats. The measurement surface for "are agents actually
3
+ * using the FactStore" — mirrors core/recall/query-log.ts but for fact
4
+ * recall. Every /api/recall/facts call appends one line; /api/recall/facts/
5
+ * stats reads it back.
6
+ *
7
+ * Telemetry path — never raises. File format: one JSON object per line at
8
+ * $NLM_FACT_QUERY_LOG or ~/.nlm/fact_query_log.jsonl. Append-only.
9
+ *
10
+ * Without this, the FactStore is a write-only system: facts go in via
11
+ * ingest + backfill, but there's no signal on whether anything reads them.
12
+ */
13
+ import type { FactKind, RecallMode } from "../../shared/types.js";
14
+ export interface FactLogEntry {
15
+ readonly source: string;
16
+ readonly query: string | null;
17
+ readonly subject: string | null;
18
+ readonly predicate: string | null;
19
+ readonly kind: FactKind | null;
20
+ readonly mode: RecallMode;
21
+ readonly limit: number;
22
+ readonly nResults: number;
23
+ readonly returnedIds: ReadonlyArray<string>;
24
+ }
25
+ export interface FactStatsResult {
26
+ readonly days: number;
27
+ readonly total: number;
28
+ readonly with_results: number;
29
+ readonly hit_rate: number;
30
+ readonly by_source: Record<string, number>;
31
+ readonly top_subjects: ReadonlyArray<{
32
+ readonly subject: string;
33
+ readonly count: number;
34
+ }>;
35
+ readonly top_predicates: ReadonlyArray<{
36
+ readonly predicate: string;
37
+ readonly count: number;
38
+ }>;
39
+ readonly log_present: boolean;
40
+ }
41
+ export declare function logFactQuery(entry: FactLogEntry, logPath?: string): Promise<void>;
42
+ export declare function factRecallStats(days: number, logPath?: string): Promise<FactStatsResult>;
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Fact query log + stats. The measurement surface for "are agents actually
3
+ * using the FactStore" — mirrors core/recall/query-log.ts but for fact
4
+ * recall. Every /api/recall/facts call appends one line; /api/recall/facts/
5
+ * stats reads it back.
6
+ *
7
+ * Telemetry path — never raises. File format: one JSON object per line at
8
+ * $NLM_FACT_QUERY_LOG or ~/.nlm/fact_query_log.jsonl. Append-only.
9
+ *
10
+ * Without this, the FactStore is a write-only system: facts go in via
11
+ * ingest + backfill, but there's no signal on whether anything reads them.
12
+ */
13
+ import { appendFile, mkdir, readFile, stat } from "node:fs/promises";
14
+ import { dirname, join } from "node:path";
15
+ import { homedir } from "node:os";
16
+ function defaultLogPath() {
17
+ return process.env["NLM_FACT_QUERY_LOG"] ?? join(homedir(), ".nlm", "fact_query_log.jsonl");
18
+ }
19
+ export async function logFactQuery(entry, logPath = defaultLogPath()) {
20
+ try {
21
+ await mkdir(dirname(logPath), { recursive: true });
22
+ const payload = {
23
+ ts: new Date().toISOString(),
24
+ source: entry.source,
25
+ query: entry.query,
26
+ subject: entry.subject,
27
+ predicate: entry.predicate,
28
+ kind: entry.kind,
29
+ mode: entry.mode,
30
+ limit: entry.limit,
31
+ n_results: entry.nResults,
32
+ returned_ids: entry.returnedIds,
33
+ };
34
+ await appendFile(logPath, JSON.stringify(payload) + "\n", "utf8");
35
+ }
36
+ catch {
37
+ // Telemetry must never break the call path.
38
+ }
39
+ }
40
+ export async function factRecallStats(days, logPath = defaultLogPath()) {
41
+ const base = {
42
+ days,
43
+ total: 0,
44
+ with_results: 0,
45
+ hit_rate: 0,
46
+ by_source: {},
47
+ top_subjects: [],
48
+ top_predicates: [],
49
+ log_present: false,
50
+ };
51
+ try {
52
+ await stat(logPath);
53
+ }
54
+ catch {
55
+ return base;
56
+ }
57
+ const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
58
+ const bySource = new Map();
59
+ const subjectCounts = new Map();
60
+ const predicateCounts = new Map();
61
+ let total = 0;
62
+ let withResults = 0;
63
+ let raw;
64
+ try {
65
+ raw = await readFile(logPath, "utf8");
66
+ }
67
+ catch {
68
+ return { ...base, log_present: true };
69
+ }
70
+ for (const line of raw.split("\n")) {
71
+ const trimmed = line.trim();
72
+ if (!trimmed)
73
+ continue;
74
+ let entry;
75
+ try {
76
+ entry = JSON.parse(trimmed);
77
+ }
78
+ catch {
79
+ continue;
80
+ }
81
+ const tsRaw = entry["ts"];
82
+ if (typeof tsRaw !== "string")
83
+ continue;
84
+ const ts = Date.parse(tsRaw);
85
+ if (!Number.isFinite(ts) || ts < cutoff)
86
+ continue;
87
+ total += 1;
88
+ const n = typeof entry["n_results"] === "number" ? entry["n_results"] : 0;
89
+ if (n > 0)
90
+ withResults += 1;
91
+ const source = typeof entry["source"] === "string" ? entry["source"] : "unknown";
92
+ bySource.set(source, (bySource.get(source) ?? 0) + 1);
93
+ const subj = entry["subject"];
94
+ if (typeof subj === "string" && subj) {
95
+ const norm = subj.toLowerCase().trim();
96
+ subjectCounts.set(norm, (subjectCounts.get(norm) ?? 0) + 1);
97
+ }
98
+ const pred = entry["predicate"];
99
+ if (typeof pred === "string" && pred) {
100
+ predicateCounts.set(pred, (predicateCounts.get(pred) ?? 0) + 1);
101
+ }
102
+ }
103
+ const topN = (m) => [...m.entries()].sort((a, b) => b[1] - a[1]).slice(0, 10);
104
+ return {
105
+ days,
106
+ total,
107
+ with_results: withResults,
108
+ hit_rate: total === 0 ? 0 : Math.round((withResults / total) * 1000) / 1000,
109
+ by_source: Object.fromEntries([...bySource.entries()].sort((a, b) => b[1] - a[1])),
110
+ top_subjects: topN(subjectCounts).map(([subject, count]) => ({ subject, count })),
111
+ top_predicates: topN(predicateCounts).map(([predicate, count]) => ({ predicate, count })),
112
+ log_present: true,
113
+ };
114
+ }
115
+ //# sourceMappingURL=fact-query-log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fact-query-log.js","sourceRoot":"","sources":["../../../src/core/recall-facts/fact-query-log.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * FactRecallService — agent-facing recall over the FactStore.
3
+ *
4
+ * Mirrors RecallService's keyword / semantic / hybrid pattern but works on
5
+ * Fact records, not Session records. Sessions and facts answer different
6
+ * questions and have incompatibly-shaped results, so this is a separate
7
+ * service with its own MCP tool — see Section 4 of factstore-design.md.
8
+ *
9
+ * Filter pipeline:
10
+ * 1. Storage pre-filter (subject, predicate, kind, minConfidence,
11
+ * includeSuperseded). Cheap SQL.
12
+ * 2. Keyword scoring over (value, subject, predicate). Pure, in-memory.
13
+ * 3. Semantic KNN via fact_embeddings vec0 (when mode != keyword).
14
+ * 4. Hybrid merge: 0.6 semantic + 0.4 keyword, matching the session
15
+ * recall weights.
16
+ *
17
+ * Confidence policy: default `minConfidence` is 0.6 (Section 1 of the plan).
18
+ * Facts with classifier confidence in [0.4, 0.6) get written by
19
+ * extractFacts but stay out of agent recall unless the caller lowers the
20
+ * floor explicitly.
21
+ */
22
+ import type { FactStore } from "../../ports/fact-store.js";
23
+ import type { LLMClient } from "../../ports/llm-client.js";
24
+ import type { FactRecallQuery, FactRecallResult } from "../../shared/types.js";
25
+ export interface FactRecallServiceDeps {
26
+ readonly factStore: FactStore;
27
+ readonly llm: LLMClient;
28
+ }
29
+ export declare class FactRecallService {
30
+ private readonly deps;
31
+ constructor(deps: FactRecallServiceDeps);
32
+ search(input: FactRecallQuery): Promise<FactRecallResult>;
33
+ private runSemantic;
34
+ }