nlm-memory 0.20.0 → 0.21.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 (471) hide show
  1. package/README.md +19 -10
  2. package/dist/cli/digest.js +60 -1
  3. package/dist/cli/digest.js.map +1 -1
  4. package/dist/cli/init.d.ts +12 -0
  5. package/dist/cli/init.js +49 -0
  6. package/dist/cli/init.js.map +1 -0
  7. package/dist/cli/nlm.d.ts +13 -1
  8. package/dist/cli/nlm.js +872 -61
  9. package/dist/cli/nlm.js.map +1 -1
  10. package/dist/cli/scope-backfill.d.ts +34 -0
  11. package/dist/cli/scope-backfill.js +237 -0
  12. package/dist/cli/scope-backfill.js.map +1 -0
  13. package/dist/cli/scope-coverage.d.ts +44 -0
  14. package/dist/cli/scope-coverage.js +205 -0
  15. package/dist/cli/scope-coverage.js.map +1 -0
  16. package/dist/core/actions/overlay.d.ts +20 -6
  17. package/dist/core/actions/overlay.js +32 -18
  18. package/dist/core/actions/overlay.js.map +1 -1
  19. package/dist/core/adapters/claude-code.d.ts +14 -0
  20. package/dist/core/adapters/claude-code.js +36 -0
  21. package/dist/core/adapters/claude-code.js.map +1 -1
  22. package/dist/core/classifier/prompt.js +3 -3
  23. package/dist/core/dataset/build-dataset.js +1 -0
  24. package/dist/core/dataset/build-dataset.js.map +1 -1
  25. package/dist/core/digest/compose.d.ts +13 -3
  26. package/dist/core/digest/compose.js +23 -18
  27. package/dist/core/digest/compose.js.map +1 -1
  28. package/dist/core/digest/hook-injection.d.ts +43 -0
  29. package/dist/core/digest/hook-injection.js +79 -0
  30. package/dist/core/digest/hook-injection.js.map +1 -0
  31. package/dist/core/embedding/embed-backfill.d.ts +13 -2
  32. package/dist/core/embedding/embed-backfill.js +181 -43
  33. package/dist/core/embedding/embed-backfill.js.map +1 -1
  34. package/dist/core/embedding/embedding-config.d.ts +17 -0
  35. package/dist/core/embedding/embedding-config.js +12 -0
  36. package/dist/core/embedding/embedding-config.js.map +1 -0
  37. package/dist/core/embedding/pg-embed-backfill.d.ts +31 -0
  38. package/dist/core/embedding/pg-embed-backfill.js +264 -0
  39. package/dist/core/embedding/pg-embed-backfill.js.map +1 -0
  40. package/dist/core/entities/dedup-suggest.d.ts +25 -0
  41. package/dist/core/entities/dedup-suggest.js +107 -0
  42. package/dist/core/entities/dedup-suggest.js.map +1 -0
  43. package/dist/core/eval/classifier-fixture-eval.d.ts +29 -0
  44. package/dist/core/eval/classifier-fixture-eval.js +217 -0
  45. package/dist/core/eval/classifier-fixture-eval.js.map +1 -0
  46. package/dist/core/eval/extraction-scoring.d.ts +63 -0
  47. package/dist/core/eval/extraction-scoring.js +79 -0
  48. package/dist/core/eval/extraction-scoring.js.map +1 -0
  49. package/dist/core/eval/run-eval.d.ts +40 -0
  50. package/dist/core/eval/run-eval.js +34 -0
  51. package/dist/core/eval/run-eval.js.map +1 -0
  52. package/dist/core/exemplars/capture-from-session.d.ts +2 -0
  53. package/dist/core/exemplars/capture-from-session.js +7 -3
  54. package/dist/core/exemplars/capture-from-session.js.map +1 -1
  55. package/dist/core/exemplars/diff-parse.d.ts +23 -0
  56. package/dist/core/exemplars/diff-parse.js +73 -0
  57. package/dist/core/exemplars/diff-parse.js.map +1 -0
  58. package/dist/core/exemplars/embed-backfill.d.ts +36 -0
  59. package/dist/core/exemplars/embed-backfill.js +85 -0
  60. package/dist/core/exemplars/embed-backfill.js.map +1 -0
  61. package/dist/core/exemplars/embed-text.d.ts +15 -0
  62. package/dist/core/exemplars/embed-text.js +18 -0
  63. package/dist/core/exemplars/embed-text.js.map +1 -0
  64. package/dist/core/exemplars/extract-exemplar.d.ts +2 -0
  65. package/dist/core/exemplars/extract-exemplar.js +9 -66
  66. package/dist/core/exemplars/extract-exemplar.js.map +1 -1
  67. package/dist/core/exemplars/ingest-exemplar.d.ts +1 -0
  68. package/dist/core/exemplars/ingest-exemplar.js +1 -0
  69. package/dist/core/exemplars/ingest-exemplar.js.map +1 -1
  70. package/dist/core/exemplars/recall-code.js +4 -0
  71. package/dist/core/exemplars/recall-code.js.map +1 -1
  72. package/dist/core/exemplars/warm-embedder.d.ts +14 -0
  73. package/dist/core/exemplars/warm-embedder.js +18 -0
  74. package/dist/core/exemplars/warm-embedder.js.map +1 -0
  75. package/dist/core/health/corpus-state.d.ts +8 -0
  76. package/dist/core/health/corpus-state.js +11 -0
  77. package/dist/core/health/corpus-state.js.map +1 -0
  78. package/dist/core/health/embed-inflight.d.ts +9 -0
  79. package/dist/core/health/embed-inflight.js +30 -0
  80. package/dist/core/health/embed-inflight.js.map +1 -0
  81. package/dist/core/health/embedding-lane-state.d.ts +6 -0
  82. package/dist/core/health/embedding-lane-state.js +18 -0
  83. package/dist/core/health/embedding-lane-state.js.map +1 -0
  84. package/dist/core/health/warmup-state.d.ts +12 -0
  85. package/dist/core/health/warmup-state.js +12 -0
  86. package/dist/core/health/warmup-state.js.map +1 -0
  87. package/dist/core/hook/claude-settings.d.ts +1 -1
  88. package/dist/core/hook/claude-settings.js +0 -1
  89. package/dist/core/hook/claude-settings.js.map +1 -1
  90. package/dist/core/hook/gate.d.ts +13 -6
  91. package/dist/core/hook/gate.js +47 -6
  92. package/dist/core/hook/gate.js.map +1 -1
  93. package/dist/core/hook/hook-log.d.ts +5 -0
  94. package/dist/core/hook/hook-log.js.map +1 -1
  95. package/dist/core/hook/memo.d.ts +12 -0
  96. package/dist/core/hook/memo.js +41 -1
  97. package/dist/core/hook/memo.js.map +1 -1
  98. package/dist/core/hook/pointer-block.d.ts +1 -0
  99. package/dist/core/hook/pointer-block.js +23 -2
  100. package/dist/core/hook/pointer-block.js.map +1 -1
  101. package/dist/core/hook/query-extract.d.ts +2 -1
  102. package/dist/core/hook/query-extract.js +10 -1
  103. package/dist/core/hook/query-extract.js.map +1 -1
  104. package/dist/core/hook/resolve-conversation-by-query.d.ts +28 -0
  105. package/dist/core/hook/resolve-conversation-by-query.js +98 -0
  106. package/dist/core/hook/resolve-conversation-by-query.js.map +1 -0
  107. package/dist/core/hook/select.d.ts +8 -0
  108. package/dist/core/hook/select.js +9 -2
  109. package/dist/core/hook/select.js.map +1 -1
  110. package/dist/core/hook/transcript.js +16 -2
  111. package/dist/core/hook/transcript.js.map +1 -1
  112. package/dist/core/ingest/backfill-derivables.d.ts +92 -0
  113. package/dist/core/ingest/backfill-derivables.js +175 -0
  114. package/dist/core/ingest/backfill-derivables.js.map +1 -0
  115. package/dist/core/ingest/ingest-session.d.ts +5 -0
  116. package/dist/core/ingest/ingest-session.js +23 -0
  117. package/dist/core/ingest/ingest-session.js.map +1 -1
  118. package/dist/core/ingest/reclassify-oversized.d.ts +5 -0
  119. package/dist/core/ingest/reclassify-oversized.js +9 -0
  120. package/dist/core/ingest/reclassify-oversized.js.map +1 -1
  121. package/dist/core/ingest/reprocess.d.ts +96 -0
  122. package/dist/core/ingest/reprocess.js +280 -0
  123. package/dist/core/ingest/reprocess.js.map +1 -0
  124. package/dist/core/ingest/transcript-derivables.d.ts +27 -0
  125. package/dist/core/ingest/transcript-derivables.js +138 -0
  126. package/dist/core/ingest/transcript-derivables.js.map +1 -0
  127. package/dist/core/integrity/check-invariants.d.ts +6 -1
  128. package/dist/core/integrity/check-invariants.js +209 -3
  129. package/dist/core/integrity/check-invariants.js.map +1 -1
  130. package/dist/core/metrics/corpus-stats.d.ts +49 -0
  131. package/dist/core/metrics/corpus-stats.js +111 -0
  132. package/dist/core/metrics/corpus-stats.js.map +1 -0
  133. package/dist/core/metrics/re-derivation.d.ts +48 -0
  134. package/dist/core/metrics/re-derivation.js +87 -0
  135. package/dist/core/metrics/re-derivation.js.map +1 -0
  136. package/dist/core/outcome/coverage.d.ts +33 -0
  137. package/dist/core/outcome/coverage.js +46 -0
  138. package/dist/core/outcome/coverage.js.map +1 -0
  139. package/dist/core/outcome/rollup.d.ts +33 -0
  140. package/dist/core/outcome/rollup.js +113 -0
  141. package/dist/core/outcome/rollup.js.map +1 -0
  142. package/dist/core/recall/citation-log.d.ts +8 -0
  143. package/dist/core/recall/citation-log.js +17 -0
  144. package/dist/core/recall/citation-log.js.map +1 -1
  145. package/dist/core/recall/hook-recall-log.d.ts +2 -2
  146. package/dist/core/recall/hook-recall-log.js +2 -2
  147. package/dist/core/recall/query-intent.d.ts +7 -0
  148. package/dist/core/recall/query-intent.js +25 -0
  149. package/dist/core/recall/query-intent.js.map +1 -0
  150. package/dist/core/recall/query-log.d.ts +4 -0
  151. package/dist/core/recall/query-log.js +4 -0
  152. package/dist/core/recall/query-log.js.map +1 -1
  153. package/dist/core/recall/recall-service.d.ts +37 -1
  154. package/dist/core/recall/recall-service.js +106 -63
  155. package/dist/core/recall/recall-service.js.map +1 -1
  156. package/dist/core/recall/related-exemplars.d.ts +1 -0
  157. package/dist/core/recall/related-exemplars.js +4 -1
  158. package/dist/core/recall/related-exemplars.js.map +1 -1
  159. package/dist/core/recall/related-facts.js +2 -2
  160. package/dist/core/recall/related-facts.js.map +1 -1
  161. package/dist/core/recall/reranker.d.ts +2 -1
  162. package/dist/core/recall/reranker.js +3 -3
  163. package/dist/core/recall/reranker.js.map +1 -1
  164. package/dist/core/recall-facts/fact-query-log.d.ts +7 -0
  165. package/dist/core/recall-facts/fact-query-log.js +2 -0
  166. package/dist/core/recall-facts/fact-query-log.js.map +1 -1
  167. package/dist/core/recall-facts/fact-recall-service.d.ts +9 -1
  168. package/dist/core/recall-facts/fact-recall-service.js +22 -8
  169. package/dist/core/recall-facts/fact-recall-service.js.map +1 -1
  170. package/dist/core/scheduler/scan-once.d.ts +6 -6
  171. package/dist/core/scheduler/scan-once.js +15 -49
  172. package/dist/core/scheduler/scan-once.js.map +1 -1
  173. package/dist/core/scheduler/scheduler.d.ts +10 -0
  174. package/dist/core/scheduler/scheduler.js +109 -13
  175. package/dist/core/scheduler/scheduler.js.map +1 -1
  176. package/dist/core/scope/alias-map.d.ts +10 -0
  177. package/dist/core/scope/alias-map.js +41 -0
  178. package/dist/core/scope/alias-map.js.map +1 -0
  179. package/dist/core/scope/derive-scope.d.ts +8 -0
  180. package/dist/core/scope/derive-scope.js +54 -0
  181. package/dist/core/scope/derive-scope.js.map +1 -0
  182. package/dist/core/scope/scope-clause.d.ts +16 -0
  183. package/dist/core/scope/scope-clause.js +21 -0
  184. package/dist/core/scope/scope-clause.js.map +1 -0
  185. package/dist/core/scope/stamp-flag.d.ts +1 -0
  186. package/dist/core/scope/stamp-flag.js +4 -0
  187. package/dist/core/scope/stamp-flag.js.map +1 -0
  188. package/dist/core/signals/code-signal.d.ts +50 -0
  189. package/dist/core/signals/code-signal.js +54 -0
  190. package/dist/core/signals/code-signal.js.map +1 -0
  191. package/dist/core/signals/ingest-signal.d.ts +1 -1
  192. package/dist/core/signals/ingest-signal.js +3 -1
  193. package/dist/core/signals/ingest-signal.js.map +1 -1
  194. package/dist/core/signals/stamp-scope.d.ts +18 -0
  195. package/dist/core/signals/stamp-scope.js +28 -0
  196. package/dist/core/signals/stamp-scope.js.map +1 -0
  197. package/dist/core/storage/db-restore.js +3 -1
  198. package/dist/core/storage/db-restore.js.map +1 -1
  199. package/dist/core/storage/fact-batch.d.ts +9 -0
  200. package/dist/core/storage/fact-batch.js +14 -0
  201. package/dist/core/storage/fact-batch.js.map +1 -0
  202. package/dist/core/storage/pg-code-exemplar-store.d.ts +1 -0
  203. package/dist/core/storage/pg-code-exemplar-store.js +13 -2
  204. package/dist/core/storage/pg-code-exemplar-store.js.map +1 -1
  205. package/dist/core/storage/pg-embedding-config.d.ts +29 -0
  206. package/dist/core/storage/pg-embedding-config.js +53 -0
  207. package/dist/core/storage/pg-embedding-config.js.map +1 -0
  208. package/dist/core/storage/pg-entity-store.d.ts +12 -0
  209. package/dist/core/storage/pg-entity-store.js +79 -0
  210. package/dist/core/storage/pg-entity-store.js.map +1 -0
  211. package/dist/core/storage/pg-fact-ingest.d.ts +15 -0
  212. package/dist/core/storage/pg-fact-ingest.js +34 -0
  213. package/dist/core/storage/pg-fact-ingest.js.map +1 -0
  214. package/dist/core/storage/pg-fact-store.d.ts +3 -0
  215. package/dist/core/storage/pg-fact-store.js +26 -30
  216. package/dist/core/storage/pg-fact-store.js.map +1 -1
  217. package/dist/core/storage/pg-migrate.d.ts +18 -0
  218. package/dist/core/storage/pg-migrate.js +73 -0
  219. package/dist/core/storage/pg-migrate.js.map +1 -0
  220. package/dist/core/storage/pg-session-store.d.ts +11 -2
  221. package/dist/core/storage/pg-session-store.js +224 -96
  222. package/dist/core/storage/pg-session-store.js.map +1 -1
  223. package/dist/core/storage/pg-signal-store.js +4 -3
  224. package/dist/core/storage/pg-signal-store.js.map +1 -1
  225. package/dist/core/storage/pg-storage.d.ts +9 -7
  226. package/dist/core/storage/pg-storage.js +20 -50
  227. package/dist/core/storage/pg-storage.js.map +1 -1
  228. package/dist/core/storage/pg-workstream-store.d.ts +25 -0
  229. package/dist/core/storage/pg-workstream-store.js +97 -0
  230. package/dist/core/storage/pg-workstream-store.js.map +1 -0
  231. package/dist/core/storage/sqlite-code-exemplar-store.d.ts +1 -0
  232. package/dist/core/storage/sqlite-code-exemplar-store.js +15 -2
  233. package/dist/core/storage/sqlite-code-exemplar-store.js.map +1 -1
  234. package/dist/core/storage/sqlite-embedding-config.d.ts +10 -0
  235. package/dist/core/storage/sqlite-embedding-config.js +27 -0
  236. package/dist/core/storage/sqlite-embedding-config.js.map +1 -0
  237. package/dist/core/storage/sqlite-entity-store.d.ts +13 -0
  238. package/dist/core/storage/sqlite-entity-store.js +96 -0
  239. package/dist/core/storage/sqlite-entity-store.js.map +1 -0
  240. package/dist/core/storage/sqlite-fact-store.d.ts +15 -7
  241. package/dist/core/storage/sqlite-fact-store.js +81 -27
  242. package/dist/core/storage/sqlite-fact-store.js.map +1 -1
  243. package/dist/core/storage/sqlite-outcome-store.d.ts +89 -0
  244. package/dist/core/storage/sqlite-outcome-store.js +158 -0
  245. package/dist/core/storage/sqlite-outcome-store.js.map +1 -0
  246. package/dist/core/storage/sqlite-session-store.d.ts +42 -7
  247. package/dist/core/storage/sqlite-session-store.js +208 -95
  248. package/dist/core/storage/sqlite-session-store.js.map +1 -1
  249. package/dist/core/storage/sqlite-signal-store.js +4 -2
  250. package/dist/core/storage/sqlite-signal-store.js.map +1 -1
  251. package/dist/core/storage/sqlite-storage.d.ts +8 -8
  252. package/dist/core/storage/sqlite-storage.js +21 -32
  253. package/dist/core/storage/sqlite-storage.js.map +1 -1
  254. package/dist/core/storage/sqlite-workstream-store.d.ts +25 -0
  255. package/dist/core/storage/sqlite-workstream-store.js +99 -0
  256. package/dist/core/storage/sqlite-workstream-store.js.map +1 -0
  257. package/dist/core/storage/supersedence-log.js +2 -0
  258. package/dist/core/storage/supersedence-log.js.map +1 -1
  259. package/dist/core/telemetry/probe-filter.d.ts +35 -0
  260. package/dist/core/telemetry/probe-filter.js +56 -0
  261. package/dist/core/telemetry/probe-filter.js.map +1 -0
  262. package/dist/core/update-check/check.d.ts +6 -0
  263. package/dist/core/update-check/check.js +35 -0
  264. package/dist/core/update-check/check.js.map +1 -1
  265. package/dist/core/work-digest/active-spans.d.ts +8 -0
  266. package/dist/core/work-digest/active-spans.js +29 -0
  267. package/dist/core/work-digest/active-spans.js.map +1 -0
  268. package/dist/core/work-digest/attribute.d.ts +15 -0
  269. package/dist/core/work-digest/attribute.js +76 -0
  270. package/dist/core/work-digest/attribute.js.map +1 -0
  271. package/dist/core/work-digest/build-work-digest.d.ts +12 -0
  272. package/dist/core/work-digest/build-work-digest.js +69 -0
  273. package/dist/core/work-digest/build-work-digest.js.map +1 -0
  274. package/dist/core/work-digest/compose-work-digest.d.ts +3 -0
  275. package/dist/core/work-digest/compose-work-digest.js +52 -0
  276. package/dist/core/work-digest/compose-work-digest.js.map +1 -0
  277. package/dist/core/work-digest/merge-active.d.ts +10 -0
  278. package/dist/core/work-digest/merge-active.js +22 -0
  279. package/dist/core/work-digest/merge-active.js.map +1 -0
  280. package/dist/core/work-digest/read-transcript-timestamps.d.ts +7 -0
  281. package/dist/core/work-digest/read-transcript-timestamps.js +40 -0
  282. package/dist/core/work-digest/read-transcript-timestamps.js.map +1 -0
  283. package/dist/core/work-digest/topics.d.ts +18 -0
  284. package/dist/core/work-digest/topics.js +26 -0
  285. package/dist/core/work-digest/topics.js.map +1 -0
  286. package/dist/core/work-digest/types.d.ts +45 -0
  287. package/dist/core/work-digest/types.js +2 -0
  288. package/dist/core/work-digest/types.js.map +1 -0
  289. package/dist/core/workstream/bind.d.ts +26 -0
  290. package/dist/core/workstream/bind.js +42 -0
  291. package/dist/core/workstream/bind.js.map +1 -0
  292. package/dist/core/workstream/compose-recall.d.ts +2 -0
  293. package/dist/core/workstream/compose-recall.js +39 -0
  294. package/dist/core/workstream/compose-recall.js.map +1 -0
  295. package/dist/core/workstream/merge-suggest.d.ts +19 -0
  296. package/dist/core/workstream/merge-suggest.js +61 -0
  297. package/dist/core/workstream/merge-suggest.js.map +1 -0
  298. package/dist/core/workstream/model.d.ts +21 -0
  299. package/dist/core/workstream/model.js +9 -0
  300. package/dist/core/workstream/model.js.map +1 -0
  301. package/dist/core/workstream/name-match.d.ts +8 -0
  302. package/dist/core/workstream/name-match.js +18 -0
  303. package/dist/core/workstream/name-match.js.map +1 -0
  304. package/dist/core/workstream/resolve.d.ts +10 -0
  305. package/dist/core/workstream/resolve.js +21 -0
  306. package/dist/core/workstream/resolve.js.map +1 -0
  307. package/dist/core/workstream/rollup.d.ts +12 -0
  308. package/dist/core/workstream/rollup.js +17 -0
  309. package/dist/core/workstream/rollup.js.map +1 -0
  310. package/dist/core/workstream/work-topics.d.ts +10 -0
  311. package/dist/core/workstream/work-topics.js +55 -0
  312. package/dist/core/workstream/work-topics.js.map +1 -0
  313. package/dist/hook/hook-helpers.d.ts +4 -0
  314. package/dist/hook/hook-helpers.js +36 -0
  315. package/dist/hook/hook-helpers.js.map +1 -0
  316. package/dist/hook/pre-compact-hook.d.ts +23 -0
  317. package/dist/hook/pre-compact-hook.js +67 -0
  318. package/dist/hook/pre-compact-hook.js.map +1 -0
  319. package/dist/hook/prompt-recall-hook.d.ts +38 -0
  320. package/dist/hook/prompt-recall-hook.js +124 -20
  321. package/dist/hook/prompt-recall-hook.js.map +1 -1
  322. package/dist/hook/recall-gate.d.ts +29 -0
  323. package/dist/hook/recall-gate.js +50 -0
  324. package/dist/hook/recall-gate.js.map +1 -0
  325. package/dist/hook/recall-over-http.d.ts +3 -2
  326. package/dist/hook/recall-over-http.js +26 -13
  327. package/dist/hook/recall-over-http.js.map +1 -1
  328. package/dist/hook/recent-context.d.ts +25 -0
  329. package/dist/hook/recent-context.js +104 -0
  330. package/dist/hook/recent-context.js.map +1 -0
  331. package/dist/hook/score-floor.d.ts +27 -0
  332. package/dist/hook/score-floor.js +42 -0
  333. package/dist/hook/score-floor.js.map +1 -0
  334. package/dist/hook/session-end-hook.js +8 -29
  335. package/dist/hook/session-end-hook.js.map +1 -1
  336. package/dist/hook/session-start-hook.js +26 -60
  337. package/dist/hook/session-start-hook.js.map +1 -1
  338. package/dist/hook/stop-hook.js +24 -60
  339. package/dist/hook/stop-hook.js.map +1 -1
  340. package/dist/http/app.d.ts +6 -0
  341. package/dist/http/app.js +52 -44
  342. package/dist/http/app.js.map +1 -1
  343. package/dist/install/setup.js +2 -0
  344. package/dist/install/setup.js.map +1 -1
  345. package/dist/llm/build-classifier.d.ts +22 -0
  346. package/dist/llm/build-classifier.js +42 -0
  347. package/dist/llm/build-classifier.js.map +1 -0
  348. package/dist/llm/build-embedder.d.ts +11 -0
  349. package/dist/llm/build-embedder.js +42 -0
  350. package/dist/llm/build-embedder.js.map +1 -0
  351. package/dist/llm/bundled-embedder-client.d.ts +37 -0
  352. package/dist/llm/bundled-embedder-client.js +90 -0
  353. package/dist/llm/bundled-embedder-client.js.map +1 -0
  354. package/dist/llm/classifier-box.d.ts +13 -5
  355. package/dist/llm/classifier-box.js +28 -6
  356. package/dist/llm/classifier-box.js.map +1 -1
  357. package/dist/llm/classifier-egress.d.ts +18 -0
  358. package/dist/llm/classifier-egress.js +59 -0
  359. package/dist/llm/classifier-egress.js.map +1 -0
  360. package/dist/llm/client-shared.d.ts +4 -0
  361. package/dist/llm/client-shared.js +39 -0
  362. package/dist/llm/client-shared.js.map +1 -0
  363. package/dist/llm/deepseek-client.d.ts +20 -0
  364. package/dist/llm/deepseek-client.js +59 -45
  365. package/dist/llm/deepseek-client.js.map +1 -1
  366. package/dist/llm/embedder-info.d.ts +15 -0
  367. package/dist/llm/embedder-info.js +27 -0
  368. package/dist/llm/embedder-info.js.map +1 -0
  369. package/dist/llm/model-quirks.d.ts +4 -0
  370. package/dist/llm/model-quirks.js +7 -0
  371. package/dist/llm/model-quirks.js.map +1 -0
  372. package/dist/llm/naming.d.ts +5 -0
  373. package/dist/llm/naming.js +20 -0
  374. package/dist/llm/naming.js.map +1 -0
  375. package/dist/llm/ollama-client.d.ts +7 -8
  376. package/dist/llm/ollama-client.js +59 -51
  377. package/dist/llm/ollama-client.js.map +1 -1
  378. package/dist/llm/ollama-code-embedder.d.ts +8 -3
  379. package/dist/llm/ollama-code-embedder.js +14 -24
  380. package/dist/llm/ollama-code-embedder.js.map +1 -1
  381. package/dist/llm/openai-code-embedder-client.d.ts +25 -0
  382. package/dist/llm/openai-code-embedder-client.js +52 -0
  383. package/dist/llm/openai-code-embedder-client.js.map +1 -0
  384. package/dist/llm/openai-embedder-client.d.ts +41 -0
  385. package/dist/llm/openai-embedder-client.js +83 -0
  386. package/dist/llm/openai-embedder-client.js.map +1 -0
  387. package/dist/mcp/server.d.ts +69 -2
  388. package/dist/mcp/server.js +404 -19
  389. package/dist/mcp/server.js.map +1 -1
  390. package/dist/ports/code-embedder.d.ts +6 -1
  391. package/dist/ports/code-exemplar-store.d.ts +5 -0
  392. package/dist/ports/entity-store.d.ts +35 -0
  393. package/dist/ports/entity-store.js +7 -0
  394. package/dist/ports/entity-store.js.map +1 -0
  395. package/dist/ports/fact-store.d.ts +0 -0
  396. package/dist/ports/fact-store.js.map +1 -1
  397. package/dist/ports/llm-client.d.ts +9 -1
  398. package/dist/ports/outcome.d.ts +85 -0
  399. package/dist/ports/outcome.js +11 -0
  400. package/dist/ports/outcome.js.map +1 -0
  401. package/dist/ports/session-store.d.ts +12 -6
  402. package/dist/ports/storage.d.ts +7 -30
  403. package/dist/ports/storage.js +3 -7
  404. package/dist/ports/storage.js.map +1 -1
  405. package/dist/ports/transcript-adapter.d.ts +2 -0
  406. package/dist/ports/workstream-store.d.ts +22 -0
  407. package/dist/ports/workstream-store.js +2 -0
  408. package/dist/ports/workstream-store.js.map +1 -0
  409. package/dist/shared/net.d.ts +3 -0
  410. package/dist/shared/net.js +4 -0
  411. package/dist/shared/net.js.map +1 -0
  412. package/dist/shared/types.d.ts +56 -2
  413. package/dist/shared/types.js +4 -1
  414. package/dist/shared/types.js.map +1 -1
  415. package/dist/ui/assets/{index-CMpQkbQy.js → index-CSaBtgXD.js} +1 -1
  416. package/dist/ui/index.html +1 -1
  417. package/fixtures/classifier-gold/README.md +94 -0
  418. package/fixtures/classifier-gold/reference.json +318 -0
  419. package/fixtures/classifier-gold/transcripts/01-bug-fix-null-pointer-api.txt +75 -0
  420. package/fixtures/classifier-gold/transcripts/02-bug-fix-race-condition-queue.txt +77 -0
  421. package/fixtures/classifier-gold/transcripts/03-bug-fix-jwt-clock-skew.txt +86 -0
  422. package/fixtures/classifier-gold/transcripts/04-feature-webhook-retry.txt +95 -0
  423. package/fixtures/classifier-gold/transcripts/05-feature-csv-export.txt +86 -0
  424. package/fixtures/classifier-gold/transcripts/06-feature-rate-limiting.txt +61 -0
  425. package/fixtures/classifier-gold/transcripts/07-refactor-db-abstraction.txt +72 -0
  426. package/fixtures/classifier-gold/transcripts/08-refactor-config-loading.txt +89 -0
  427. package/fixtures/classifier-gold/transcripts/09-ops-docker-staging.txt +94 -0
  428. package/fixtures/classifier-gold/transcripts/10-ops-nginx-ssl.txt +121 -0
  429. package/fixtures/classifier-gold/transcripts/11-ops-postgres-migration.txt +74 -0
  430. package/fixtures/classifier-gold/transcripts/12-ops-cron-debugging.txt +73 -0
  431. package/fixtures/classifier-gold/transcripts/13-research-vector-db-comparison.txt +59 -0
  432. package/fixtures/classifier-gold/transcripts/14-research-oauth-provider.txt +74 -0
  433. package/fixtures/classifier-gold/transcripts/15-writing-api-docs.txt +154 -0
  434. package/fixtures/classifier-gold/transcripts/16-meeting-sprint-planning.txt +69 -0
  435. package/fixtures/classifier-gold/transcripts/17-trivial-list-files.txt +14 -0
  436. package/fixtures/classifier-gold/transcripts/18-trivial-version-check.txt +21 -0
  437. package/fixtures/classifier-gold/transcripts/19-trivial-syntax-help.txt +29 -0
  438. package/fixtures/classifier-gold/transcripts/20-trivial-read-package-json.txt +41 -0
  439. package/migrations/000_initial_schema.sql +1 -1
  440. package/migrations/024_backfill_exemplar_repo_basename.sql +32 -0
  441. package/migrations/025_workstreams.sql +30 -0
  442. package/migrations/026_embedding_config.sql +12 -0
  443. package/migrations/027_drop_legacy_session_embeddings.sql +7 -0
  444. package/migrations/028_classifier_provenance.sql +5 -0
  445. package/migrations/029_project_scope.sql +21 -0
  446. package/migrations/032_session_derivables.sql +11 -0
  447. package/migrations/pg/001_initial.sql +34 -0
  448. package/migrations/pg/019_split_replaces.sql +16 -7
  449. package/migrations/pg/025_workstreams.sql +34 -0
  450. package/migrations/pg/026_embedding_config.sql +12 -0
  451. package/migrations/pg/028_classifier_provenance.sql +5 -0
  452. package/migrations/pg/029_entity_variants.sql +9 -0
  453. package/migrations/pg/030_project_scope.sql +23 -0
  454. package/migrations/pg/031_widen_session_edges_kind.sql +11 -0
  455. package/migrations/pg/032_session_derivables.sql +17 -0
  456. package/nlm/index.js +405 -39
  457. package/package.json +12 -2
  458. package/plugin/.codex-plugin/plugin.json +2 -2
  459. package/plugin/scripts/prompt-recall-hook.mjs +413 -43
  460. package/plugin/scripts/session-start-hook.mjs +295 -61
  461. package/plugin/scripts/stop-hook.mjs +89 -66
  462. package/plugin-hermes-agent/__init__.py +2 -1
  463. package/templates/agent-contract/README.md +24 -0
  464. package/templates/agent-contract/claude-code.md +40 -0
  465. package/templates/agent-contract/generic.md +38 -0
  466. package/dist/core/embedding/embed-normalize.d.ts +0 -28
  467. package/dist/core/embedding/embed-normalize.js +0 -98
  468. package/dist/core/embedding/embed-normalize.js.map +0 -1
  469. package/dist/core/storage/pg-tx-context.d.ts +0 -52
  470. package/dist/core/storage/pg-tx-context.js +0 -149
  471. package/dist/core/storage/pg-tx-context.js.map +0 -1
@@ -0,0 +1,237 @@
1
+ /**
2
+ * `nlm scope backfill`: derive and stamp scope on legacy corpus rows.
3
+ *
4
+ * Cascade order: sessions -> facts -> exemplars -> signals -> workstreams.
5
+ * --dry-run (default): computes would-change counts and writes nothing.
6
+ * --apply: writes; only fills scope IS NULL rows; idempotent.
7
+ */
8
+ import { open } from "node:fs/promises";
9
+ import Database from "better-sqlite3";
10
+ import { deriveScope } from "../core/scope/derive-scope.js";
11
+ /** Read at most this many bytes from the head of a transcript to find cwd. */
12
+ const HEAD_BYTES = 8_192;
13
+ /**
14
+ * Bounded head-read that matches the claude-code adapter's cwd extraction
15
+ * exactly (src/core/adapters/claude-code.ts:144):
16
+ * if (!projectDir && typeof evt["cwd"] === "string") projectDir = evt["cwd"];
17
+ *
18
+ * Also covers pi-runtime JSONL: the pi adapter reads cwd from the
19
+ * `type: "session"` event on line 1 (src/core/adapters/pi.ts:124-126);
20
+ * taking the first `cwd` field on any line subsumes that shape.
21
+ *
22
+ * Reads at most HEAD_BYTES from the file start, splits into lines, parses
23
+ * each as JSON, and returns the value of the first "cwd" string field found.
24
+ * A first line longer than HEAD_BYTES arrives truncated, fails JSON.parse,
25
+ * and is skipped: fail-closed to NULL, never a mis-parse.
26
+ */
27
+ async function readCwdFromTranscript(transcriptPath) {
28
+ let fd;
29
+ try {
30
+ fd = await open(transcriptPath, "r");
31
+ const buf = Buffer.alloc(HEAD_BYTES);
32
+ const { bytesRead } = await fd.read(buf, 0, HEAD_BYTES, 0);
33
+ const head = buf.toString("utf8", 0, bytesRead);
34
+ for (const line of head.split("\n")) {
35
+ const trimmed = line.trim();
36
+ if (!trimmed)
37
+ continue;
38
+ let evt;
39
+ try {
40
+ evt = JSON.parse(trimmed);
41
+ }
42
+ catch {
43
+ continue;
44
+ }
45
+ if (typeof evt["cwd"] === "string" && evt["cwd"]) {
46
+ return { cwd: evt["cwd"] };
47
+ }
48
+ }
49
+ return { cwd: null };
50
+ }
51
+ catch (err) {
52
+ const code = err.code;
53
+ const skipReason = code === "ENOENT" || code === "EACCES" || code === "EPERM"
54
+ ? "missing"
55
+ : "malformed";
56
+ return { cwd: null, skipReason };
57
+ }
58
+ finally {
59
+ await fd?.close().catch(() => { });
60
+ }
61
+ }
62
+ function inc(counter, scope) {
63
+ counter[scope] = (counter[scope] ?? 0) + 1;
64
+ }
65
+ function emptyTable() {
66
+ return { total: 0, byScope: {} };
67
+ }
68
+ export async function runScopeBackfill(opts) {
69
+ const { db, apply, aliasMap } = opts;
70
+ const sessionRows = db
71
+ .prepare("SELECT id, transcript_path FROM sessions WHERE scope IS NULL AND transcript_path IS NOT NULL")
72
+ .all();
73
+ const derivedScopes = new Map();
74
+ let missingCount = 0;
75
+ let malformedCount = 0;
76
+ let noCwdCount = 0;
77
+ for (const row of sessionRows) {
78
+ const { cwd, skipReason } = await readCwdFromTranscript(row.transcript_path);
79
+ if (skipReason === "missing") {
80
+ missingCount++;
81
+ continue;
82
+ }
83
+ if (skipReason === "malformed") {
84
+ malformedCount++;
85
+ continue;
86
+ }
87
+ if (!cwd) {
88
+ noCwdCount++;
89
+ continue;
90
+ }
91
+ const scope = deriveScope(cwd, aliasMap);
92
+ if (scope === null)
93
+ continue;
94
+ derivedScopes.set(row.id, scope);
95
+ }
96
+ const result = {
97
+ dryRun: !apply,
98
+ sessions: emptyTable(),
99
+ facts: emptyTable(),
100
+ exemplars: emptyTable(),
101
+ signals: emptyTable(),
102
+ workstreams: emptyTable(),
103
+ skipped: { missingTranscript: missingCount, malformed: malformedCount, noCwdFound: noCwdCount },
104
+ };
105
+ for (const scope of derivedScopes.values()) {
106
+ result.sessions.total++;
107
+ inc(result.sessions.byScope, scope);
108
+ }
109
+ const performUpdates = () => {
110
+ const updateSession = db.prepare("UPDATE sessions SET scope = ? WHERE id = ? AND scope IS NULL");
111
+ for (const [id, scope] of derivedScopes) {
112
+ updateSession.run(scope, id);
113
+ }
114
+ const factRows = db
115
+ .prepare(`
116
+ SELECT s.scope, COUNT(f.id) AS n
117
+ FROM facts f
118
+ JOIN sessions s ON f.source_session_id = s.id
119
+ WHERE f.scope IS NULL AND s.scope IS NOT NULL
120
+ GROUP BY s.scope
121
+ `)
122
+ .all();
123
+ for (const { scope, n } of factRows) {
124
+ result.facts.total += n;
125
+ inc(result.facts.byScope, scope);
126
+ }
127
+ db.prepare(`
128
+ UPDATE facts
129
+ SET scope = (SELECT s.scope FROM sessions s WHERE s.id = facts.source_session_id)
130
+ WHERE scope IS NULL
131
+ AND source_session_id IN (SELECT id FROM sessions WHERE scope IS NOT NULL)
132
+ `).run();
133
+ const exemplarRows = db
134
+ .prepare(`
135
+ SELECT s.scope, COUNT(e.id) AS n
136
+ FROM code_exemplars e
137
+ JOIN sessions s ON e.session_id = s.id
138
+ WHERE e.scope IS NULL AND s.scope IS NOT NULL
139
+ GROUP BY s.scope
140
+ `)
141
+ .all();
142
+ for (const { scope, n } of exemplarRows) {
143
+ result.exemplars.total += n;
144
+ inc(result.exemplars.byScope, scope);
145
+ }
146
+ db.prepare(`
147
+ UPDATE code_exemplars
148
+ SET scope = (SELECT s.scope FROM sessions s WHERE s.id = code_exemplars.session_id)
149
+ WHERE scope IS NULL
150
+ AND session_id IN (SELECT id FROM sessions WHERE scope IS NOT NULL)
151
+ `).run();
152
+ const signalRows = db
153
+ .prepare(`
154
+ SELECT s.scope, COUNT(sig.id) AS n
155
+ FROM signals sig
156
+ JOIN sessions s ON sig.session_id = s.id
157
+ WHERE sig.scope IS NULL AND s.scope IS NOT NULL AND s.scope != 'global'
158
+ GROUP BY s.scope
159
+ `)
160
+ .all();
161
+ for (const { scope, n } of signalRows) {
162
+ result.signals.total += n;
163
+ inc(result.signals.byScope, scope);
164
+ }
165
+ db.prepare(`
166
+ UPDATE signals
167
+ SET scope = (SELECT s.scope FROM sessions s WHERE s.id = signals.session_id)
168
+ WHERE scope IS NULL
169
+ AND session_id IS NOT NULL
170
+ AND session_id IN (
171
+ SELECT id FROM sessions WHERE scope IS NOT NULL AND scope != 'global'
172
+ )
173
+ `).run();
174
+ const pendingWorkstreams = db
175
+ .prepare("SELECT id FROM workstreams WHERE scope IS NULL")
176
+ .all();
177
+ const getMemberScopes = db.prepare("SELECT scope FROM sessions WHERE workstream_id = ?");
178
+ const updateWs = db.prepare("UPDATE workstreams SET scope = ? WHERE id = ? AND scope IS NULL");
179
+ for (const ws of pendingWorkstreams) {
180
+ const members = getMemberScopes.all(ws.id);
181
+ if (members.length === 0)
182
+ continue;
183
+ if (members.some((m) => m.scope === null))
184
+ continue;
185
+ const unique = new Set(members.map((m) => m.scope));
186
+ if (unique.size !== 1)
187
+ continue;
188
+ const wsScope = [...unique][0];
189
+ result.workstreams.total++;
190
+ inc(result.workstreams.byScope, wsScope);
191
+ updateWs.run(wsScope, ws.id);
192
+ }
193
+ };
194
+ if (apply) {
195
+ db.transaction(performUpdates)();
196
+ }
197
+ else {
198
+ db.exec("BEGIN");
199
+ try {
200
+ performUpdates();
201
+ }
202
+ finally {
203
+ db.exec("ROLLBACK");
204
+ }
205
+ }
206
+ return result;
207
+ }
208
+ export function formatBackfillResult(result, write) {
209
+ const heading = result.dryRun
210
+ ? "scope backfill (dry run -- no writes):"
211
+ : "scope backfill applied:";
212
+ write(`${heading}\n`);
213
+ const rows = [
214
+ ["sessions", result.dryRun ? "would change" : "stamped", result.sessions],
215
+ ["facts", result.dryRun ? "would cascade" : "cascaded", result.facts],
216
+ ["exemplars", result.dryRun ? "would cascade" : "cascaded", result.exemplars],
217
+ ["signals", result.dryRun ? "would cascade" : "cascaded", result.signals],
218
+ ["workstreams", result.dryRun ? "would cascade" : "cascaded", result.workstreams],
219
+ ];
220
+ for (const [table, verb, data] of rows) {
221
+ const entries = Object.entries(data.byScope);
222
+ const breakdown = entries.length > 0
223
+ ? ` [${entries.map(([s, c]) => `${s}: ${c}`).join(", ")}]`
224
+ : "";
225
+ write(` ${table.padEnd(12)}${String(data.total).padStart(4)} ${verb}${breakdown}\n`);
226
+ }
227
+ const { missingTranscript, malformed, noCwdFound } = result.skipped;
228
+ const skipTotal = missingTranscript + malformed + noCwdFound;
229
+ if (skipTotal > 0) {
230
+ write(` skipped: ${skipTotal}` +
231
+ ` (missing transcript: ${missingTranscript}, malformed: ${malformed}, no cwd found: ${noCwdFound})\n`);
232
+ }
233
+ else {
234
+ write(` skipped: 0\n`);
235
+ }
236
+ }
237
+ //# sourceMappingURL=scope-backfill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-backfill.js","sourceRoot":"","sources":["../../src/cli/scope-backfill.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAG5D,8EAA8E;AAC9E,MAAM,UAAU,GAAG,KAAK,CAAC;AAEzB;;;;;;;;;;;;;GAaG;AACH,KAAK,UAAU,qBAAqB,CAClC,cAAsB;IAEtB,IAAI,EAAgD,CAAC;IACrD,IAAI,CAAC;QACH,EAAE,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;QACrC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QACrC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC;QAC3D,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC;QAChD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5B,IAAI,CAAC,OAAO;gBAAE,SAAS;YACvB,IAAI,GAA4B,CAAC;YACjC,IAAI,CAAC;gBACH,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAA4B,CAAC;YACvD,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;YACD,IAAI,OAAO,GAAG,CAAC,KAAK,CAAC,KAAK,QAAQ,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjD,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7B,CAAC;QACH,CAAC;QACD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;IACvB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,GAAI,GAA6B,CAAC,IAAI,CAAC;QACjD,MAAM,UAAU,GACd,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,OAAO;YACxD,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,WAAW,CAAC;QAClB,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IACnC,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;IACpC,CAAC;AACH,CAAC;AAID,SAAS,GAAG,CAAC,OAAmB,EAAE,KAAa;IAC7C,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;AAC7C,CAAC;AAiBD,SAAS,UAAU;IACjB,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAItC;IACC,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC;IAGrC,MAAM,WAAW,GAAG,EAAE;SACnB,OAAO,CACN,8FAA8F,CAC/F;SACA,GAAG,EAAE,CAAC;IAET,MAAM,aAAa,GAAG,IAAI,GAAG,EAAkB,CAAC;IAChD,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,MAAM,qBAAqB,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC7E,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,YAAY,EAAE,CAAC;YACf,SAAS;QACX,CAAC;QACD,IAAI,UAAU,KAAK,WAAW,EAAE,CAAC;YAC/B,cAAc,EAAE,CAAC;YACjB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,UAAU,EAAE,CAAC;YACb,SAAS;QACX,CAAC;QACD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACzC,IAAI,KAAK,KAAK,IAAI;YAAE,SAAS;QAC7B,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC;IAED,MAAM,MAAM,GAAmB;QAC7B,MAAM,EAAE,CAAC,KAAK;QACd,QAAQ,EAAE,UAAU,EAAE;QACtB,KAAK,EAAE,UAAU,EAAE;QACnB,SAAS,EAAE,UAAU,EAAE;QACvB,OAAO,EAAE,UAAU,EAAE;QACrB,WAAW,EAAE,UAAU,EAAE;QACzB,OAAO,EAAE,EAAE,iBAAiB,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE;KAChG,CAAC;IAEF,KAAK,MAAM,KAAK,IAAI,aAAa,CAAC,MAAM,EAAE,EAAE,CAAC;QAC3C,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACxB,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,cAAc,GAAG,GAAG,EAAE;QAC1B,MAAM,aAAa,GAAG,EAAE,CAAC,OAAO,CAC9B,8DAA8D,CAC/D,CAAC;QACF,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,aAAa,EAAE,CAAC;YACxC,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAC/B,CAAC;QAID,MAAM,QAAQ,GAAG,EAAE;aAChB,OAAO,CAAe;;;;;;OAMtB,CAAC;aACD,GAAG,EAAE,CAAC;QACT,KAAK,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,QAAQ,EAAE,CAAC;YACpC,MAAM,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;YACxB,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACnC,CAAC;QACD,EAAE,CAAC,OAAO,CAAC;;;;;KAKV,CAAC,CAAC,GAAG,EAAE,CAAC;QAET,MAAM,YAAY,GAAG,EAAE;aACpB,OAAO,CAAe;;;;;;OAMtB,CAAC;aACD,GAAG,EAAE,CAAC;QACT,KAAK,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,YAAY,EAAE,CAAC;YACxC,MAAM,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC,CAAC;YAC5B,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACvC,CAAC;QACD,EAAE,CAAC,OAAO,CAAC;;;;;KAKV,CAAC,CAAC,GAAG,EAAE,CAAC;QAET,MAAM,UAAU,GAAG,EAAE;aAClB,OAAO,CAAe;;;;;;OAMtB,CAAC;aACD,GAAG,EAAE,CAAC;QACT,KAAK,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,UAAU,EAAE,CAAC;YACtC,MAAM,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC;YAC1B,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACrC,CAAC;QACD,EAAE,CAAC,OAAO,CAAC;;;;;;;;KAQV,CAAC,CAAC,GAAG,EAAE,CAAC;QAGT,MAAM,kBAAkB,GAAG,EAAE;aAC1B,OAAO,CAAY,gDAAgD,CAAC;aACpE,GAAG,EAAE,CAAC;QAGT,MAAM,eAAe,GAAG,EAAE,CAAC,OAAO,CAChC,oDAAoD,CACrD,CAAC;QACF,MAAM,QAAQ,GAAG,EAAE,CAAC,OAAO,CACzB,iEAAiE,CAClE,CAAC;QAEF,KAAK,MAAM,EAAE,IAAI,kBAAkB,EAAE,CAAC;YACpC,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YAC3C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACnC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC;gBAAE,SAAS;YACpD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAe,CAAC,CAAC,CAAC;YAC9D,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC;gBAAE,SAAS;YAChC,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAE,CAAC;YAChC,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;YAC3B,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACzC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,KAAK,EAAE,CAAC;QACV,EAAE,CAAC,WAAW,CAAC,cAAc,CAAC,EAAE,CAAC;IACnC,CAAC;SAAM,CAAC;QACN,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACjB,IAAI,CAAC;YACH,cAAc,EAAE,CAAC;QACnB,CAAC;gBAAS,CAAC;YACT,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACtB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,MAAsB,EACtB,KAA0B;IAE1B,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM;QAC3B,CAAC,CAAC,wCAAwC;QAC1C,CAAC,CAAC,yBAAyB,CAAC;IAC9B,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;IAEtB,MAAM,IAAI,GAAiD;QACzD,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC;QACzE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC;QACrE,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,SAAS,CAAC;QAC7E,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC;QACzE,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,WAAW,CAAC;KAClF,CAAC;IAEF,KAAK,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,SAAS,GACb,OAAO,CAAC,MAAM,GAAG,CAAC;YAChB,CAAC,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;YAC3D,CAAC,CAAC,EAAE,CAAC;QACT,KAAK,CAAC,KAAK,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,GAAG,SAAS,IAAI,CAAC,CAAC;IACxF,CAAC;IAED,MAAM,EAAE,iBAAiB,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC;IACpE,MAAM,SAAS,GAAG,iBAAiB,GAAG,SAAS,GAAG,UAAU,CAAC;IAC7D,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QAClB,KAAK,CACH,kBAAkB,SAAS,EAAE;YAC3B,yBAAyB,iBAAiB,gBAAgB,SAAS,mBAAmB,UAAU,KAAK,CACxG,CAAC;IACJ,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,uBAAuB,CAAC,CAAC;IACjC,CAAC;AACH,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * `nlm scope coverage`: per-table and recall-weighted scope coverage fractions.
3
+ *
4
+ * READ-ONLY: opens the DB with { readonly: true }; never writes.
5
+ * No env flags consulted. No recall behavior change.
6
+ */
7
+ export interface TableCoverage {
8
+ total: number;
9
+ stamped: number;
10
+ unstamped: number;
11
+ stamped_fraction: number;
12
+ byScope: Record<string, number>;
13
+ }
14
+ export interface OverallCoverage {
15
+ total: number;
16
+ stamped: number;
17
+ unstamped: number;
18
+ stamped_fraction: number;
19
+ }
20
+ export interface RecallWeightedStats {
21
+ distinctReturnedIds: number;
22
+ foundInDb: number;
23
+ scoped: number;
24
+ fraction: number;
25
+ }
26
+ export interface RecallWeightedCoverage extends RecallWeightedStats {
27
+ bySurface: Record<string, RecallWeightedStats>;
28
+ }
29
+ export interface CoverageResult {
30
+ sessions: TableCoverage;
31
+ facts: TableCoverage;
32
+ code_exemplars: TableCoverage;
33
+ signals: TableCoverage;
34
+ workstreams: TableCoverage;
35
+ overall: OverallCoverage;
36
+ recallWeighted: RecallWeightedCoverage | null;
37
+ queryLogNote: string | null;
38
+ }
39
+ export declare function runScopeCoverage(opts: {
40
+ dbPath: string;
41
+ queryLogPath?: string;
42
+ window?: number;
43
+ }): Promise<CoverageResult>;
44
+ export declare function formatCoverageResult(result: CoverageResult, write: (s: string) => void): void;
@@ -0,0 +1,205 @@
1
+ /**
2
+ * `nlm scope coverage`: per-table and recall-weighted scope coverage fractions.
3
+ *
4
+ * READ-ONLY: opens the DB with { readonly: true }; never writes.
5
+ * No env flags consulted. No recall behavior change.
6
+ */
7
+ import Database from "better-sqlite3";
8
+ import { existsSync } from "node:fs";
9
+ import { homedir } from "node:os";
10
+ import { join } from "node:path";
11
+ import { recentQueryLog } from "../core/recall/recent-log.js";
12
+ function overallOf(tables) {
13
+ let total = 0;
14
+ let stamped = 0;
15
+ for (const t of tables) {
16
+ total += t.total;
17
+ stamped += t.stamped;
18
+ }
19
+ return {
20
+ total,
21
+ stamped,
22
+ unstamped: total - stamped,
23
+ stamped_fraction: total === 0 ? 0 : stamped / total,
24
+ };
25
+ }
26
+ function tableStats(db, table) {
27
+ const agg = db
28
+ .prepare(`SELECT COUNT(*) AS total, SUM(CASE WHEN scope IS NOT NULL THEN 1 ELSE 0 END) AS scoped FROM "${table}"`)
29
+ .get();
30
+ const total = agg.total;
31
+ const scoped = agg.scoped ?? 0;
32
+ const rows = db
33
+ .prepare(`SELECT scope, COUNT(*) AS n FROM "${table}" WHERE scope IS NOT NULL GROUP BY scope`)
34
+ .all();
35
+ const byScope = {};
36
+ for (const { scope, n } of rows) {
37
+ byScope[scope] = n;
38
+ }
39
+ return {
40
+ total,
41
+ stamped: scoped,
42
+ unstamped: total - scoped,
43
+ stamped_fraction: total === 0 ? 0 : scoped / total,
44
+ byScope,
45
+ };
46
+ }
47
+ function defaultQueryLogPath() {
48
+ return process.env["NLM_QUERY_LOG"] ?? join(homedir(), ".nlm", "query_log.jsonl");
49
+ }
50
+ const CHUNK_SIZE = 999;
51
+ export async function runScopeCoverage(opts) {
52
+ const entryWindow = opts.window ?? 200;
53
+ if (!Number.isFinite(entryWindow) || entryWindow <= 0) {
54
+ throw new Error(`invalid --window: ${String(opts.window)} (expected a positive integer)`);
55
+ }
56
+ const db = new Database(opts.dbPath, { readonly: true });
57
+ try {
58
+ const sessions = tableStats(db, "sessions");
59
+ const facts = tableStats(db, "facts");
60
+ const code_exemplars = tableStats(db, "code_exemplars");
61
+ const signals = tableStats(db, "signals");
62
+ const workstreams = tableStats(db, "workstreams");
63
+ const overall = overallOf([sessions, facts, code_exemplars, signals, workstreams]);
64
+ const logPath = opts.queryLogPath ?? defaultQueryLogPath();
65
+ if (!existsSync(logPath)) {
66
+ return {
67
+ sessions,
68
+ facts,
69
+ code_exemplars,
70
+ signals,
71
+ workstreams,
72
+ overall,
73
+ recallWeighted: null,
74
+ queryLogNote: `query log not found at ${logPath}`,
75
+ };
76
+ }
77
+ const entries = recentQueryLog(entryWindow, logPath);
78
+ const idSet = new Set();
79
+ const idsBySurface = new Map();
80
+ for (const entry of entries) {
81
+ let surfaceIds = idsBySurface.get(entry.source);
82
+ if (!surfaceIds) {
83
+ surfaceIds = new Set();
84
+ idsBySurface.set(entry.source, surfaceIds);
85
+ }
86
+ for (const id of entry.returnedIds) {
87
+ idSet.add(id);
88
+ surfaceIds.add(id);
89
+ }
90
+ }
91
+ if (idSet.size === 0) {
92
+ return {
93
+ sessions,
94
+ facts,
95
+ code_exemplars,
96
+ signals,
97
+ workstreams,
98
+ overall,
99
+ recallWeighted: { distinctReturnedIds: 0, foundInDb: 0, scoped: 0, fraction: 0, bySurface: {} },
100
+ queryLogNote: null,
101
+ };
102
+ }
103
+ // Recalled ids span the corpus tables (session recall returns session
104
+ // ids, fact recall fact ids, code recall exemplar ids); resolve each id
105
+ // to its scope across all three, first match wins.
106
+ const scopeById = new Map();
107
+ const ids = [...idSet];
108
+ for (const table of ["sessions", "facts", "code_exemplars"]) {
109
+ for (let i = 0; i < ids.length; i += CHUNK_SIZE) {
110
+ const chunk = ids.slice(i, i + CHUNK_SIZE).filter((id) => !scopeById.has(id));
111
+ if (chunk.length === 0)
112
+ continue;
113
+ const placeholders = chunk.map(() => "?").join(", ");
114
+ const rows = db
115
+ .prepare(`SELECT id, scope FROM "${table}" WHERE id IN (${placeholders})`)
116
+ .all(...chunk);
117
+ for (const row of rows) {
118
+ if (!scopeById.has(row.id))
119
+ scopeById.set(row.id, row.scope);
120
+ }
121
+ }
122
+ }
123
+ const statsFor = (set) => {
124
+ let foundInDb = 0;
125
+ let scoped = 0;
126
+ for (const id of set) {
127
+ const scope = scopeById.get(id);
128
+ if (scope === undefined)
129
+ continue;
130
+ foundInDb++;
131
+ if (scope !== null)
132
+ scoped++;
133
+ }
134
+ return {
135
+ distinctReturnedIds: set.size,
136
+ foundInDb,
137
+ scoped,
138
+ fraction: foundInDb === 0 ? 0 : scoped / foundInDb,
139
+ };
140
+ };
141
+ const bySurface = {};
142
+ for (const [source, surfaceIds] of idsBySurface) {
143
+ if (surfaceIds.size > 0)
144
+ bySurface[source] = statsFor(surfaceIds);
145
+ }
146
+ return {
147
+ sessions,
148
+ facts,
149
+ code_exemplars,
150
+ signals,
151
+ workstreams,
152
+ overall,
153
+ recallWeighted: { ...statsFor(idSet), bySurface },
154
+ queryLogNote: null,
155
+ };
156
+ }
157
+ finally {
158
+ db.close();
159
+ }
160
+ }
161
+ export function formatCoverageResult(result, write) {
162
+ write("scope coverage:\n\n");
163
+ write(" per-table:\n");
164
+ const tables = [
165
+ ["sessions", result.sessions],
166
+ ["facts", result.facts],
167
+ ["code_exemplars", result.code_exemplars],
168
+ ["signals", result.signals],
169
+ ["workstreams", result.workstreams],
170
+ ];
171
+ for (const [name, cov] of tables) {
172
+ const pct = `${(cov.stamped_fraction * 100).toFixed(1)}%`;
173
+ const counts = `${cov.stamped}/${cov.total} stamped (${pct})`;
174
+ const entries = Object.entries(cov.byScope);
175
+ const breakdown = entries.length > 0
176
+ ? ` [${entries.map(([s, c]) => `${s}: ${c}`).join(", ")}]`
177
+ : "";
178
+ write(` ${name.padEnd(15)} ${counts}${breakdown}\n`);
179
+ }
180
+ const o = result.overall;
181
+ const overallPct = `${(o.stamped_fraction * 100).toFixed(1)}%`;
182
+ write(` ${"overall".padEnd(15)} ${o.stamped}/${o.total} stamped (${overallPct})\n`);
183
+ write("\n");
184
+ if (result.queryLogNote !== null) {
185
+ write(` recall-weighted: skipped -- ${result.queryLogNote}\n`);
186
+ }
187
+ else if (result.recallWeighted !== null) {
188
+ const rw = result.recallWeighted;
189
+ const pct = `${(rw.fraction * 100).toFixed(1)}%`;
190
+ write(` recall-weighted (recalled ids from last window):\n`);
191
+ write(` distinct ids: ${rw.distinctReturnedIds}\n`);
192
+ write(` found in db: ${rw.foundInDb}\n`);
193
+ write(` scoped: ${rw.scoped}\n`);
194
+ write(` fraction: ${pct}\n`);
195
+ const surfaces = Object.entries(rw.bySurface);
196
+ if (surfaces.length > 0) {
197
+ write(` per-surface:\n`);
198
+ for (const [source, s] of surfaces) {
199
+ const sPct = `${(s.fraction * 100).toFixed(1)}%`;
200
+ write(` ${source.padEnd(15)} ${s.scoped}/${s.foundInDb} scoped (${sPct}, ${s.distinctReturnedIds} ids)\n`);
201
+ }
202
+ }
203
+ }
204
+ }
205
+ //# sourceMappingURL=scope-coverage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope-coverage.js","sourceRoot":"","sources":["../../src/cli/scope-coverage.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAwC9D,SAAS,SAAS,CAAC,MAAoC;IACrD,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,OAAO,GAAG,CAAC,CAAC;IAChB,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC;QACjB,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC;IACvB,CAAC;IACD,OAAO;QACL,KAAK;QACL,OAAO;QACP,SAAS,EAAE,KAAK,GAAG,OAAO;QAC1B,gBAAgB,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,KAAK;KACpD,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,EAAqB,EAAE,KAAa;IAEtD,MAAM,GAAG,GAAG,EAAE;SACX,OAAO,CACN,gGAAgG,KAAK,GAAG,CACzG;SACA,GAAG,EAAG,CAAC;IACV,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC;IACxB,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,CAAC,CAAC;IAG/B,MAAM,IAAI,GAAG,EAAE;SACZ,OAAO,CACN,qCAAqC,KAAK,0CAA0C,CACrF;SACA,GAAG,EAAE,CAAC;IACT,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,KAAK,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,IAAI,EAAE,CAAC;QAChC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAED,OAAO;QACL,KAAK;QACL,OAAO,EAAE,MAAM;QACf,SAAS,EAAE,KAAK,GAAG,MAAM;QACzB,gBAAgB,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,KAAK;QAClD,OAAO;KACR,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB;IAC1B,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,iBAAiB,CAAC,CAAC;AACpF,CAAC;AAED,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAItC;IACC,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC;IACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,WAAW,IAAI,CAAC,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,qBAAqB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,gCAAgC,CAAC,CAAC;IAC5F,CAAC;IACD,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAEzD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,UAAU,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,UAAU,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;QACtC,MAAM,cAAc,GAAG,UAAU,CAAC,EAAE,EAAE,gBAAgB,CAAC,CAAC;QACxD,MAAM,OAAO,GAAG,UAAU,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;QAC1C,MAAM,WAAW,GAAG,UAAU,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;QAClD,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;QAEnF,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,IAAI,mBAAmB,EAAE,CAAC;QAE3D,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,OAAO;gBACL,QAAQ;gBACR,KAAK;gBACL,cAAc;gBACd,OAAO;gBACP,WAAW;gBACX,OAAO;gBACP,cAAc,EAAE,IAAI;gBACpB,YAAY,EAAE,0BAA0B,OAAO,EAAE;aAClD,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,cAAc,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACrD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;QAChC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAuB,CAAC;QACpD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,UAAU,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YAChD,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;gBAC/B,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;YAC7C,CAAC;YACD,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;gBACnC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACd,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;QAED,IAAI,KAAK,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO;gBACL,QAAQ;gBACR,KAAK;gBACL,cAAc;gBACd,OAAO;gBACP,WAAW;gBACX,OAAO;gBACP,cAAc,EAAE,EAAE,mBAAmB,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE;gBAC/F,YAAY,EAAE,IAAI;aACnB,CAAC;QACJ,CAAC;QAED,sEAAsE;QACtE,wEAAwE;QACxE,mDAAmD;QACnD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;QACnD,MAAM,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC;QACvB,KAAK,MAAM,KAAK,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,gBAAgB,CAAC,EAAE,CAAC;YAC5D,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC;gBAChD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;gBAC9E,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;oBAAE,SAAS;gBACjC,MAAM,YAAY,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACrD,MAAM,IAAI,GAAG,EAAE;qBACZ,OAAO,CAAC,0BAA0B,KAAK,kBAAkB,YAAY,GAAG,CAAC;qBACzE,GAAG,CAAC,GAAG,KAAK,CAAgD,CAAC;gBAChE,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;oBACvB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;wBAAE,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;gBAC/D,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,QAAQ,GAAG,CAAC,GAAwB,EAAuB,EAAE;YACjE,IAAI,SAAS,GAAG,CAAC,CAAC;YAClB,IAAI,MAAM,GAAG,CAAC,CAAC;YACf,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;gBACrB,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBAChC,IAAI,KAAK,KAAK,SAAS;oBAAE,SAAS;gBAClC,SAAS,EAAE,CAAC;gBACZ,IAAI,KAAK,KAAK,IAAI;oBAAE,MAAM,EAAE,CAAC;YAC/B,CAAC;YACD,OAAO;gBACL,mBAAmB,EAAE,GAAG,CAAC,IAAI;gBAC7B,SAAS;gBACT,MAAM;gBACN,QAAQ,EAAE,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,SAAS;aACnD,CAAC;QACJ,CAAC,CAAC;QAEF,MAAM,SAAS,GAAwC,EAAE,CAAC;QAC1D,KAAK,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,YAAY,EAAE,CAAC;YAChD,IAAI,UAAU,CAAC,IAAI,GAAG,CAAC;gBAAE,SAAS,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;QACpE,CAAC;QAED,OAAO;YACL,QAAQ;YACR,KAAK;YACL,cAAc;YACd,OAAO;YACP,WAAW;YACX,OAAO;YACP,cAAc,EAAE,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE;YACjD,YAAY,EAAE,IAAI;SACnB,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,KAAK,EAAE,CAAC;IACb,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAsB,EAAE,KAA0B;IACrF,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC7B,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAExB,MAAM,MAAM,GAAmC;QAC7C,CAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC;QAC7B,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC;QACvB,CAAC,gBAAgB,EAAE,MAAM,CAAC,cAAc,CAAC;QACzC,CAAC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC;QAC3B,CAAC,aAAa,EAAE,MAAM,CAAC,WAAW,CAAC;KACpC,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,gBAAgB,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;QAC1D,MAAM,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,KAAK,aAAa,GAAG,GAAG,CAAC;QAC9D,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,SAAS,GACb,OAAO,CAAC,MAAM,GAAG,CAAC;YAChB,CAAC,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;YAC3D,CAAC,CAAC,EAAE,CAAC;QACT,KAAK,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,MAAM,GAAG,SAAS,IAAI,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC;IACzB,MAAM,UAAU,GAAG,GAAG,CAAC,CAAC,CAAC,gBAAgB,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IAC/D,KAAK,CAAC,OAAO,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK,aAAa,UAAU,KAAK,CAAC,CAAC;IAEvF,KAAK,CAAC,IAAI,CAAC,CAAC;IAEZ,IAAI,MAAM,CAAC,YAAY,KAAK,IAAI,EAAE,CAAC;QACjC,KAAK,CAAC,iCAAiC,MAAM,CAAC,YAAY,IAAI,CAAC,CAAC;IAClE,CAAC;SAAM,IAAI,MAAM,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;QAC1C,MAAM,EAAE,GAAG,MAAM,CAAC,cAAc,CAAC;QACjC,MAAM,GAAG,GAAG,GAAG,CAAC,EAAE,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;QACjD,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAC9D,KAAK,CAAC,qBAAqB,EAAE,CAAC,mBAAmB,IAAI,CAAC,CAAC;QACvD,KAAK,CAAC,qBAAqB,EAAE,CAAC,SAAS,IAAI,CAAC,CAAC;QAC7C,KAAK,CAAC,qBAAqB,EAAE,CAAC,MAAM,IAAI,CAAC,CAAC;QAC1C,KAAK,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;QACpC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC;QAC9C,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,KAAK,CAAC,kBAAkB,CAAC,CAAC;YAC1B,KAAK,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC;gBACnC,MAAM,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;gBACjD,KAAK,CAAC,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,SAAS,YAAY,IAAI,KAAK,CAAC,CAAC,mBAAmB,SAAS,CAAC,CAAC;YAChH,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -5,38 +5,52 @@
5
5
  * at read time without mutating the underlying store.
6
6
  *
7
7
  * Shared by buildDataset (UI projection) and SqliteSessionStore (recall
8
- * path), so the same overlay drives both surfaces. Append-only — every
8
+ * path), so the same overlay drives both surfaces. Append-only; every
9
9
  * mutation lives as a row in `actions`.
10
10
  */
11
11
  import type Database from "better-sqlite3";
12
+ import type { Pool } from "pg";
12
13
  export interface ActionOverlay {
13
14
  readonly dismissedAlerts: Set<string>;
14
15
  readonly snoozedAlerts: Map<string, string>;
15
16
  readonly retiredEntities: Set<string>;
16
17
  readonly snoozedEntities: Map<string, string>;
17
18
  readonly labeledEntities: Map<string, string>;
18
- /** entity canonical → new display label. Storage canonical is untouched;
19
+ /** entity canonical -> new display label. Storage canonical is untouched;
19
20
  * legacy-name recall still resolves because sessions stay tagged with
20
21
  * the original canonical. Last non-reverted rename per subject wins. */
21
22
  readonly renamedEntities: Map<string, string>;
22
23
  /** open-question ids resolved without becoming decisions */
23
24
  readonly resolvedOpens: Set<string>;
24
- /** open-question id → resolution text (becomes a decision at projection time) */
25
+ /** open-question id -> resolution text (becomes a decision at projection time) */
25
26
  readonly promotedOpens: Map<string, string>;
26
27
  /** decision ids hidden from the projection (the underlying session body keeps the original line). */
27
28
  readonly dismissedDecisions: Set<string>;
28
- /** decision id → corrected text shown in place of the original. */
29
+ /** decision id -> corrected text shown in place of the original. */
29
30
  readonly revisedDecisions: Map<string, string>;
30
- /** entity canonical → user-asserted coherence bucket. Overrides the
31
+ /** entity canonical -> user-asserted coherence bucket. Overrides the
31
32
  * computed (session_count + age) classification at projection time;
32
33
  * last non-reverted write wins. */
33
34
  readonly coherenceOverrides: Map<string, "active" | "sparse" | "stale">;
34
- /** source canonical → target canonical. Source is hidden at projection
35
+ /** source canonical -> target canonical. Source is hidden at projection
35
36
  * time (treated as retired); target absorbs the source's session count. */
36
37
  readonly mergedEntities: Map<string, string>;
37
38
  }
39
+ export interface ActionRow {
40
+ kind: string;
41
+ subject_type: string;
42
+ subject_id: string;
43
+ payload: string | null;
44
+ }
38
45
  export declare const EMPTY_OVERLAY: ActionOverlay;
46
+ /**
47
+ * Pure reducer: folds a sequence of action rows (ordered by id, reverted rows
48
+ * excluded by caller) into an ActionOverlay. Used by both the SQLite and PG
49
+ * loaders so the projection logic is shared and not duplicated per backend.
50
+ */
51
+ export declare function reduceActionRows(rows: ReadonlyArray<ActionRow>): ActionOverlay;
39
52
  export declare function loadActionOverlay(db: Database.Database): ActionOverlay;
53
+ export declare function loadActionOverlayPg(pool: Pool): Promise<ActionOverlay>;
40
54
  /**
41
55
  * Stable id for an open question: `${sessionId}::${hash12(text)}`. Both
42
56
  * sides (overlay creators and consumers) compute it the same way so action
@@ -5,7 +5,7 @@
5
5
  * at read time without mutating the underlying store.
6
6
  *
7
7
  * Shared by buildDataset (UI projection) and SqliteSessionStore (recall
8
- * path), so the same overlay drives both surfaces. Append-only — every
8
+ * path), so the same overlay drives both surfaces. Append-only; every
9
9
  * mutation lives as a row in `actions`.
10
10
  */
11
11
  export const EMPTY_OVERLAY = {
@@ -22,12 +22,12 @@ export const EMPTY_OVERLAY = {
22
22
  coherenceOverrides: new Map(),
23
23
  mergedEntities: new Map(),
24
24
  };
25
- export function loadActionOverlay(db) {
26
- const hasActions = db
27
- .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='actions'")
28
- .get();
29
- if (!hasActions)
30
- return EMPTY_OVERLAY;
25
+ /**
26
+ * Pure reducer: folds a sequence of action rows (ordered by id, reverted rows
27
+ * excluded by caller) into an ActionOverlay. Used by both the SQLite and PG
28
+ * loaders so the projection logic is shared and not duplicated per backend.
29
+ */
30
+ export function reduceActionRows(rows) {
31
31
  const overlay = {
32
32
  dismissedAlerts: new Set(),
33
33
  snoozedAlerts: new Map(),
@@ -42,16 +42,6 @@ export function loadActionOverlay(db) {
42
42
  coherenceOverrides: new Map(),
43
43
  mergedEntities: new Map(),
44
44
  };
45
- // ORDER BY id keeps reducer deterministic: later writes overwrite earlier
46
- // ones, so the latest non-reverted rename per subject wins.
47
- const rows = db
48
- .prepare(`
49
- SELECT kind, subject_type, subject_id, payload
50
- FROM actions
51
- WHERE reverted_by IS NULL
52
- ORDER BY id
53
- `)
54
- .all();
55
45
  const now = new Date().toISOString();
56
46
  for (const r of rows) {
57
47
  const payload = r.payload ? safeParse(r.payload) : null;
@@ -114,13 +104,37 @@ export function loadActionOverlay(db) {
114
104
  overlay.coherenceOverrides.set(r.subject_id, state);
115
105
  }
116
106
  else {
117
- // Empty payload reverts to the computed bucket.
118
107
  overlay.coherenceOverrides.delete(r.subject_id);
119
108
  }
120
109
  }
121
110
  }
122
111
  return overlay;
123
112
  }
113
+ export function loadActionOverlay(db) {
114
+ const hasActions = db
115
+ .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='actions'")
116
+ .get();
117
+ if (!hasActions)
118
+ return EMPTY_OVERLAY;
119
+ // ORDER BY id keeps reducer deterministic: later writes overwrite earlier
120
+ // ones, so the latest non-reverted rename per subject wins.
121
+ const rows = db
122
+ .prepare(`
123
+ SELECT kind, subject_type, subject_id, payload
124
+ FROM actions
125
+ WHERE reverted_by IS NULL
126
+ ORDER BY id
127
+ `)
128
+ .all();
129
+ return reduceActionRows(rows);
130
+ }
131
+ export async function loadActionOverlayPg(pool) {
132
+ const result = await pool.query(`SELECT kind, subject_type, subject_id, payload
133
+ FROM actions
134
+ WHERE reverted_by IS NULL
135
+ ORDER BY id`);
136
+ return reduceActionRows(result.rows);
137
+ }
124
138
  /**
125
139
  * Stable id for an open question: `${sessionId}::${hash12(text)}`. Both
126
140
  * sides (overlay creators and consumers) compute it the same way so action