nlm-memory 0.19.0 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (398) hide show
  1. package/README.md +17 -10
  2. package/dist/cli/digest.js +21 -0
  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 +766 -55
  9. package/dist/cli/nlm.js.map +1 -1
  10. package/dist/core/actions/overlay.d.ts +20 -6
  11. package/dist/core/actions/overlay.js +32 -18
  12. package/dist/core/actions/overlay.js.map +1 -1
  13. package/dist/core/adapters/claude-code.js +6 -0
  14. package/dist/core/adapters/claude-code.js.map +1 -1
  15. package/dist/core/classifier/hierarchical-classify.d.ts +6 -2
  16. package/dist/core/classifier/hierarchical-classify.js +10 -6
  17. package/dist/core/classifier/hierarchical-classify.js.map +1 -1
  18. package/dist/core/classifier/prompt.js +3 -3
  19. package/dist/core/digest/compose.d.ts +10 -0
  20. package/dist/core/digest/compose.js +7 -1
  21. package/dist/core/digest/compose.js.map +1 -1
  22. package/dist/core/embedding/embed-backfill.d.ts +13 -2
  23. package/dist/core/embedding/embed-backfill.js +181 -43
  24. package/dist/core/embedding/embed-backfill.js.map +1 -1
  25. package/dist/core/embedding/embedding-config.d.ts +17 -0
  26. package/dist/core/embedding/embedding-config.js +12 -0
  27. package/dist/core/embedding/embedding-config.js.map +1 -0
  28. package/dist/core/embedding/pg-embed-backfill.d.ts +31 -0
  29. package/dist/core/embedding/pg-embed-backfill.js +264 -0
  30. package/dist/core/embedding/pg-embed-backfill.js.map +1 -0
  31. package/dist/core/entities/dedup-suggest.d.ts +25 -0
  32. package/dist/core/entities/dedup-suggest.js +107 -0
  33. package/dist/core/entities/dedup-suggest.js.map +1 -0
  34. package/dist/core/eval/classifier-fixture-eval.d.ts +29 -0
  35. package/dist/core/eval/classifier-fixture-eval.js +217 -0
  36. package/dist/core/eval/classifier-fixture-eval.js.map +1 -0
  37. package/dist/core/eval/extraction-scoring.d.ts +63 -0
  38. package/dist/core/eval/extraction-scoring.js +79 -0
  39. package/dist/core/eval/extraction-scoring.js.map +1 -0
  40. package/dist/core/eval/run-eval.d.ts +40 -0
  41. package/dist/core/eval/run-eval.js +34 -0
  42. package/dist/core/eval/run-eval.js.map +1 -0
  43. package/dist/core/exemplars/capture-from-session.d.ts +1 -0
  44. package/dist/core/exemplars/capture-from-session.js +6 -2
  45. package/dist/core/exemplars/capture-from-session.js.map +1 -1
  46. package/dist/core/exemplars/diff-parse.d.ts +23 -0
  47. package/dist/core/exemplars/diff-parse.js +73 -0
  48. package/dist/core/exemplars/diff-parse.js.map +1 -0
  49. package/dist/core/exemplars/embed-backfill.d.ts +36 -0
  50. package/dist/core/exemplars/embed-backfill.js +85 -0
  51. package/dist/core/exemplars/embed-backfill.js.map +1 -0
  52. package/dist/core/exemplars/embed-text.d.ts +15 -0
  53. package/dist/core/exemplars/embed-text.js +18 -0
  54. package/dist/core/exemplars/embed-text.js.map +1 -0
  55. package/dist/core/exemplars/extract-exemplar.js +2 -65
  56. package/dist/core/exemplars/extract-exemplar.js.map +1 -1
  57. package/dist/core/exemplars/recall-code.js +4 -0
  58. package/dist/core/exemplars/recall-code.js.map +1 -1
  59. package/dist/core/exemplars/warm-embedder.d.ts +14 -0
  60. package/dist/core/exemplars/warm-embedder.js +18 -0
  61. package/dist/core/exemplars/warm-embedder.js.map +1 -0
  62. package/dist/core/health/corpus-state.d.ts +8 -0
  63. package/dist/core/health/corpus-state.js +11 -0
  64. package/dist/core/health/corpus-state.js.map +1 -0
  65. package/dist/core/health/embed-inflight.d.ts +9 -0
  66. package/dist/core/health/embed-inflight.js +30 -0
  67. package/dist/core/health/embed-inflight.js.map +1 -0
  68. package/dist/core/health/embedding-lane-state.d.ts +6 -0
  69. package/dist/core/health/embedding-lane-state.js +18 -0
  70. package/dist/core/health/embedding-lane-state.js.map +1 -0
  71. package/dist/core/health/warmup-state.d.ts +12 -0
  72. package/dist/core/health/warmup-state.js +12 -0
  73. package/dist/core/health/warmup-state.js.map +1 -0
  74. package/dist/core/hook/gate.d.ts +13 -6
  75. package/dist/core/hook/gate.js +47 -6
  76. package/dist/core/hook/gate.js.map +1 -1
  77. package/dist/core/hook/hook-log.d.ts +5 -0
  78. package/dist/core/hook/hook-log.js.map +1 -1
  79. package/dist/core/hook/memo.d.ts +12 -0
  80. package/dist/core/hook/memo.js +41 -1
  81. package/dist/core/hook/memo.js.map +1 -1
  82. package/dist/core/hook/pointer-block.d.ts +1 -0
  83. package/dist/core/hook/pointer-block.js +23 -2
  84. package/dist/core/hook/pointer-block.js.map +1 -1
  85. package/dist/core/hook/query-extract.d.ts +2 -1
  86. package/dist/core/hook/query-extract.js +10 -1
  87. package/dist/core/hook/query-extract.js.map +1 -1
  88. package/dist/core/hook/resolve-conversation-by-query.d.ts +28 -0
  89. package/dist/core/hook/resolve-conversation-by-query.js +98 -0
  90. package/dist/core/hook/resolve-conversation-by-query.js.map +1 -0
  91. package/dist/core/hook/select.d.ts +8 -0
  92. package/dist/core/hook/select.js +9 -2
  93. package/dist/core/hook/select.js.map +1 -1
  94. package/dist/core/hook/transcript.js +16 -2
  95. package/dist/core/hook/transcript.js.map +1 -1
  96. package/dist/core/ingest/ingest-session.d.ts +5 -0
  97. package/dist/core/ingest/ingest-session.js +3 -0
  98. package/dist/core/ingest/ingest-session.js.map +1 -1
  99. package/dist/core/ingest/reclassify-oversized.d.ts +5 -0
  100. package/dist/core/ingest/reclassify-oversized.js +3 -0
  101. package/dist/core/ingest/reclassify-oversized.js.map +1 -1
  102. package/dist/core/ingest/reprocess.d.ts +96 -0
  103. package/dist/core/ingest/reprocess.js +275 -0
  104. package/dist/core/ingest/reprocess.js.map +1 -0
  105. package/dist/core/integrity/check-invariants.d.ts +6 -1
  106. package/dist/core/integrity/check-invariants.js +209 -3
  107. package/dist/core/integrity/check-invariants.js.map +1 -1
  108. package/dist/core/metrics/corpus-stats.d.ts +49 -0
  109. package/dist/core/metrics/corpus-stats.js +111 -0
  110. package/dist/core/metrics/corpus-stats.js.map +1 -0
  111. package/dist/core/metrics/re-derivation.d.ts +48 -0
  112. package/dist/core/metrics/re-derivation.js +87 -0
  113. package/dist/core/metrics/re-derivation.js.map +1 -0
  114. package/dist/core/recall/citation-log.d.ts +8 -0
  115. package/dist/core/recall/citation-log.js +17 -0
  116. package/dist/core/recall/citation-log.js.map +1 -1
  117. package/dist/core/recall/query-intent.d.ts +7 -0
  118. package/dist/core/recall/query-intent.js +25 -0
  119. package/dist/core/recall/query-intent.js.map +1 -0
  120. package/dist/core/recall/query-log.d.ts +4 -0
  121. package/dist/core/recall/query-log.js +4 -0
  122. package/dist/core/recall/query-log.js.map +1 -1
  123. package/dist/core/recall/recall-service.d.ts +37 -1
  124. package/dist/core/recall/recall-service.js +106 -63
  125. package/dist/core/recall/recall-service.js.map +1 -1
  126. package/dist/core/recall/related-exemplars.d.ts +1 -0
  127. package/dist/core/recall/related-exemplars.js +4 -1
  128. package/dist/core/recall/related-exemplars.js.map +1 -1
  129. package/dist/core/recall/related-facts.js +2 -2
  130. package/dist/core/recall/related-facts.js.map +1 -1
  131. package/dist/core/recall/reranker.d.ts +2 -1
  132. package/dist/core/recall/reranker.js +3 -3
  133. package/dist/core/recall/reranker.js.map +1 -1
  134. package/dist/core/recall-facts/fact-query-log.d.ts +7 -0
  135. package/dist/core/recall-facts/fact-query-log.js +2 -0
  136. package/dist/core/recall-facts/fact-query-log.js.map +1 -1
  137. package/dist/core/recall-facts/fact-recall-service.d.ts +9 -1
  138. package/dist/core/recall-facts/fact-recall-service.js +21 -8
  139. package/dist/core/recall-facts/fact-recall-service.js.map +1 -1
  140. package/dist/core/scheduler/scan-once.d.ts +6 -6
  141. package/dist/core/scheduler/scan-once.js +13 -49
  142. package/dist/core/scheduler/scan-once.js.map +1 -1
  143. package/dist/core/scheduler/scheduler.d.ts +10 -0
  144. package/dist/core/scheduler/scheduler.js +80 -28
  145. package/dist/core/scheduler/scheduler.js.map +1 -1
  146. package/dist/core/signals/code-signal.d.ts +49 -0
  147. package/dist/core/signals/code-signal.js +53 -0
  148. package/dist/core/signals/code-signal.js.map +1 -0
  149. package/dist/core/storage/db-restore.js +3 -1
  150. package/dist/core/storage/db-restore.js.map +1 -1
  151. package/dist/core/storage/fact-batch.d.ts +9 -0
  152. package/dist/core/storage/fact-batch.js +14 -0
  153. package/dist/core/storage/fact-batch.js.map +1 -0
  154. package/dist/core/storage/pg-code-exemplar-store.d.ts +1 -0
  155. package/dist/core/storage/pg-code-exemplar-store.js +7 -0
  156. package/dist/core/storage/pg-code-exemplar-store.js.map +1 -1
  157. package/dist/core/storage/pg-embedding-config.d.ts +29 -0
  158. package/dist/core/storage/pg-embedding-config.js +53 -0
  159. package/dist/core/storage/pg-embedding-config.js.map +1 -0
  160. package/dist/core/storage/pg-entity-store.d.ts +12 -0
  161. package/dist/core/storage/pg-entity-store.js +79 -0
  162. package/dist/core/storage/pg-entity-store.js.map +1 -0
  163. package/dist/core/storage/pg-fact-ingest.d.ts +15 -0
  164. package/dist/core/storage/pg-fact-ingest.js +34 -0
  165. package/dist/core/storage/pg-fact-ingest.js.map +1 -0
  166. package/dist/core/storage/pg-fact-store.d.ts +3 -0
  167. package/dist/core/storage/pg-fact-store.js +26 -30
  168. package/dist/core/storage/pg-fact-store.js.map +1 -1
  169. package/dist/core/storage/pg-migrate.d.ts +18 -0
  170. package/dist/core/storage/pg-migrate.js +73 -0
  171. package/dist/core/storage/pg-migrate.js.map +1 -0
  172. package/dist/core/storage/pg-session-store.d.ts +9 -0
  173. package/dist/core/storage/pg-session-store.js +204 -76
  174. package/dist/core/storage/pg-session-store.js.map +1 -1
  175. package/dist/core/storage/pg-storage.d.ts +9 -7
  176. package/dist/core/storage/pg-storage.js +20 -50
  177. package/dist/core/storage/pg-storage.js.map +1 -1
  178. package/dist/core/storage/pg-workstream-store.d.ts +24 -0
  179. package/dist/core/storage/pg-workstream-store.js +96 -0
  180. package/dist/core/storage/pg-workstream-store.js.map +1 -0
  181. package/dist/core/storage/sqlite-code-exemplar-store.d.ts +1 -0
  182. package/dist/core/storage/sqlite-code-exemplar-store.js +11 -0
  183. package/dist/core/storage/sqlite-code-exemplar-store.js.map +1 -1
  184. package/dist/core/storage/sqlite-embedding-config.d.ts +10 -0
  185. package/dist/core/storage/sqlite-embedding-config.js +27 -0
  186. package/dist/core/storage/sqlite-embedding-config.js.map +1 -0
  187. package/dist/core/storage/sqlite-entity-store.d.ts +13 -0
  188. package/dist/core/storage/sqlite-entity-store.js +96 -0
  189. package/dist/core/storage/sqlite-entity-store.js.map +1 -0
  190. package/dist/core/storage/sqlite-fact-store.d.ts +15 -7
  191. package/dist/core/storage/sqlite-fact-store.js +76 -25
  192. package/dist/core/storage/sqlite-fact-store.js.map +1 -1
  193. package/dist/core/storage/sqlite-session-store.d.ts +20 -5
  194. package/dist/core/storage/sqlite-session-store.js +183 -68
  195. package/dist/core/storage/sqlite-session-store.js.map +1 -1
  196. package/dist/core/storage/sqlite-storage.d.ts +8 -8
  197. package/dist/core/storage/sqlite-storage.js +21 -32
  198. package/dist/core/storage/sqlite-storage.js.map +1 -1
  199. package/dist/core/storage/sqlite-workstream-store.d.ts +24 -0
  200. package/dist/core/storage/sqlite-workstream-store.js +98 -0
  201. package/dist/core/storage/sqlite-workstream-store.js.map +1 -0
  202. package/dist/core/util/with-timeout.d.ts +5 -0
  203. package/dist/core/util/with-timeout.js +20 -0
  204. package/dist/core/util/with-timeout.js.map +1 -0
  205. package/dist/core/work-digest/active-spans.d.ts +8 -0
  206. package/dist/core/work-digest/active-spans.js +29 -0
  207. package/dist/core/work-digest/active-spans.js.map +1 -0
  208. package/dist/core/work-digest/attribute.d.ts +15 -0
  209. package/dist/core/work-digest/attribute.js +76 -0
  210. package/dist/core/work-digest/attribute.js.map +1 -0
  211. package/dist/core/work-digest/build-work-digest.d.ts +12 -0
  212. package/dist/core/work-digest/build-work-digest.js +69 -0
  213. package/dist/core/work-digest/build-work-digest.js.map +1 -0
  214. package/dist/core/work-digest/compose-work-digest.d.ts +3 -0
  215. package/dist/core/work-digest/compose-work-digest.js +52 -0
  216. package/dist/core/work-digest/compose-work-digest.js.map +1 -0
  217. package/dist/core/work-digest/merge-active.d.ts +10 -0
  218. package/dist/core/work-digest/merge-active.js +22 -0
  219. package/dist/core/work-digest/merge-active.js.map +1 -0
  220. package/dist/core/work-digest/read-transcript-timestamps.d.ts +7 -0
  221. package/dist/core/work-digest/read-transcript-timestamps.js +40 -0
  222. package/dist/core/work-digest/read-transcript-timestamps.js.map +1 -0
  223. package/dist/core/work-digest/topics.d.ts +18 -0
  224. package/dist/core/work-digest/topics.js +26 -0
  225. package/dist/core/work-digest/topics.js.map +1 -0
  226. package/dist/core/work-digest/types.d.ts +45 -0
  227. package/dist/core/work-digest/types.js +2 -0
  228. package/dist/core/work-digest/types.js.map +1 -0
  229. package/dist/core/workstream/bind.d.ts +25 -0
  230. package/dist/core/workstream/bind.js +23 -0
  231. package/dist/core/workstream/bind.js.map +1 -0
  232. package/dist/core/workstream/compose-recall.d.ts +2 -0
  233. package/dist/core/workstream/compose-recall.js +39 -0
  234. package/dist/core/workstream/compose-recall.js.map +1 -0
  235. package/dist/core/workstream/merge-suggest.d.ts +19 -0
  236. package/dist/core/workstream/merge-suggest.js +61 -0
  237. package/dist/core/workstream/merge-suggest.js.map +1 -0
  238. package/dist/core/workstream/model.d.ts +20 -0
  239. package/dist/core/workstream/model.js +9 -0
  240. package/dist/core/workstream/model.js.map +1 -0
  241. package/dist/core/workstream/name-match.d.ts +8 -0
  242. package/dist/core/workstream/name-match.js +18 -0
  243. package/dist/core/workstream/name-match.js.map +1 -0
  244. package/dist/core/workstream/resolve.d.ts +10 -0
  245. package/dist/core/workstream/resolve.js +21 -0
  246. package/dist/core/workstream/resolve.js.map +1 -0
  247. package/dist/core/workstream/rollup.d.ts +12 -0
  248. package/dist/core/workstream/rollup.js +17 -0
  249. package/dist/core/workstream/rollup.js.map +1 -0
  250. package/dist/core/workstream/work-topics.d.ts +10 -0
  251. package/dist/core/workstream/work-topics.js +55 -0
  252. package/dist/core/workstream/work-topics.js.map +1 -0
  253. package/dist/hook/hook-helpers.d.ts +4 -0
  254. package/dist/hook/hook-helpers.js +36 -0
  255. package/dist/hook/hook-helpers.js.map +1 -0
  256. package/dist/hook/pre-compact-hook.d.ts +23 -0
  257. package/dist/hook/pre-compact-hook.js +66 -0
  258. package/dist/hook/pre-compact-hook.js.map +1 -0
  259. package/dist/hook/prompt-recall-hook.d.ts +37 -0
  260. package/dist/hook/prompt-recall-hook.js +120 -20
  261. package/dist/hook/prompt-recall-hook.js.map +1 -1
  262. package/dist/hook/recall-gate.d.ts +29 -0
  263. package/dist/hook/recall-gate.js +50 -0
  264. package/dist/hook/recall-gate.js.map +1 -0
  265. package/dist/hook/recall-over-http.d.ts +1 -1
  266. package/dist/hook/recall-over-http.js +11 -11
  267. package/dist/hook/recall-over-http.js.map +1 -1
  268. package/dist/hook/recent-context.d.ts +25 -0
  269. package/dist/hook/recent-context.js +104 -0
  270. package/dist/hook/recent-context.js.map +1 -0
  271. package/dist/hook/score-floor.d.ts +27 -0
  272. package/dist/hook/score-floor.js +42 -0
  273. package/dist/hook/score-floor.js.map +1 -0
  274. package/dist/hook/session-end-hook.js +8 -29
  275. package/dist/hook/session-end-hook.js.map +1 -1
  276. package/dist/hook/session-start-hook.js +24 -59
  277. package/dist/hook/session-start-hook.js.map +1 -1
  278. package/dist/hook/stop-hook.js +22 -59
  279. package/dist/hook/stop-hook.js.map +1 -1
  280. package/dist/hook/subagent-start-hook.d.ts +28 -0
  281. package/dist/hook/subagent-start-hook.js +81 -0
  282. package/dist/hook/subagent-start-hook.js.map +1 -0
  283. package/dist/http/app.js +28 -8
  284. package/dist/http/app.js.map +1 -1
  285. package/dist/install/setup.js +2 -0
  286. package/dist/install/setup.js.map +1 -1
  287. package/dist/llm/build-classifier.d.ts +22 -0
  288. package/dist/llm/build-classifier.js +42 -0
  289. package/dist/llm/build-classifier.js.map +1 -0
  290. package/dist/llm/build-embedder.d.ts +11 -0
  291. package/dist/llm/build-embedder.js +42 -0
  292. package/dist/llm/build-embedder.js.map +1 -0
  293. package/dist/llm/bundled-embedder-client.d.ts +37 -0
  294. package/dist/llm/bundled-embedder-client.js +90 -0
  295. package/dist/llm/bundled-embedder-client.js.map +1 -0
  296. package/dist/llm/classifier-box.d.ts +13 -5
  297. package/dist/llm/classifier-box.js +28 -6
  298. package/dist/llm/classifier-box.js.map +1 -1
  299. package/dist/llm/classifier-egress.d.ts +18 -0
  300. package/dist/llm/classifier-egress.js +59 -0
  301. package/dist/llm/classifier-egress.js.map +1 -0
  302. package/dist/llm/client-shared.d.ts +4 -0
  303. package/dist/llm/client-shared.js +39 -0
  304. package/dist/llm/client-shared.js.map +1 -0
  305. package/dist/llm/deepseek-client.d.ts +20 -0
  306. package/dist/llm/deepseek-client.js +59 -45
  307. package/dist/llm/deepseek-client.js.map +1 -1
  308. package/dist/llm/embedder-info.d.ts +15 -0
  309. package/dist/llm/embedder-info.js +27 -0
  310. package/dist/llm/embedder-info.js.map +1 -0
  311. package/dist/llm/model-quirks.d.ts +4 -0
  312. package/dist/llm/model-quirks.js +7 -0
  313. package/dist/llm/model-quirks.js.map +1 -0
  314. package/dist/llm/naming.d.ts +5 -0
  315. package/dist/llm/naming.js +20 -0
  316. package/dist/llm/naming.js.map +1 -0
  317. package/dist/llm/ollama-client.d.ts +7 -8
  318. package/dist/llm/ollama-client.js +59 -51
  319. package/dist/llm/ollama-client.js.map +1 -1
  320. package/dist/llm/ollama-code-embedder.d.ts +8 -3
  321. package/dist/llm/ollama-code-embedder.js +14 -24
  322. package/dist/llm/ollama-code-embedder.js.map +1 -1
  323. package/dist/llm/openai-code-embedder-client.d.ts +25 -0
  324. package/dist/llm/openai-code-embedder-client.js +52 -0
  325. package/dist/llm/openai-code-embedder-client.js.map +1 -0
  326. package/dist/llm/openai-embedder-client.d.ts +41 -0
  327. package/dist/llm/openai-embedder-client.js +83 -0
  328. package/dist/llm/openai-embedder-client.js.map +1 -0
  329. package/dist/mcp/server.d.ts +47 -2
  330. package/dist/mcp/server.js +303 -18
  331. package/dist/mcp/server.js.map +1 -1
  332. package/dist/ports/code-embedder.d.ts +6 -1
  333. package/dist/ports/code-exemplar-store.d.ts +5 -0
  334. package/dist/ports/entity-store.d.ts +35 -0
  335. package/dist/ports/entity-store.js +7 -0
  336. package/dist/ports/entity-store.js.map +1 -0
  337. package/dist/ports/fact-store.d.ts +0 -0
  338. package/dist/ports/fact-store.js.map +1 -1
  339. package/dist/ports/llm-client.d.ts +9 -1
  340. package/dist/ports/session-store.d.ts +12 -0
  341. package/dist/ports/storage.d.ts +7 -30
  342. package/dist/ports/storage.js +3 -7
  343. package/dist/ports/storage.js.map +1 -1
  344. package/dist/ports/transcript-adapter.d.ts +2 -0
  345. package/dist/ports/workstream-store.d.ts +21 -0
  346. package/dist/ports/workstream-store.js +2 -0
  347. package/dist/ports/workstream-store.js.map +1 -0
  348. package/dist/shared/types.d.ts +23 -2
  349. package/fixtures/classifier-gold/README.md +94 -0
  350. package/fixtures/classifier-gold/reference.json +318 -0
  351. package/fixtures/classifier-gold/transcripts/01-bug-fix-null-pointer-api.txt +75 -0
  352. package/fixtures/classifier-gold/transcripts/02-bug-fix-race-condition-queue.txt +77 -0
  353. package/fixtures/classifier-gold/transcripts/03-bug-fix-jwt-clock-skew.txt +86 -0
  354. package/fixtures/classifier-gold/transcripts/04-feature-webhook-retry.txt +95 -0
  355. package/fixtures/classifier-gold/transcripts/05-feature-csv-export.txt +86 -0
  356. package/fixtures/classifier-gold/transcripts/06-feature-rate-limiting.txt +61 -0
  357. package/fixtures/classifier-gold/transcripts/07-refactor-db-abstraction.txt +72 -0
  358. package/fixtures/classifier-gold/transcripts/08-refactor-config-loading.txt +89 -0
  359. package/fixtures/classifier-gold/transcripts/09-ops-docker-staging.txt +94 -0
  360. package/fixtures/classifier-gold/transcripts/10-ops-nginx-ssl.txt +121 -0
  361. package/fixtures/classifier-gold/transcripts/11-ops-postgres-migration.txt +74 -0
  362. package/fixtures/classifier-gold/transcripts/12-ops-cron-debugging.txt +73 -0
  363. package/fixtures/classifier-gold/transcripts/13-research-vector-db-comparison.txt +59 -0
  364. package/fixtures/classifier-gold/transcripts/14-research-oauth-provider.txt +74 -0
  365. package/fixtures/classifier-gold/transcripts/15-writing-api-docs.txt +154 -0
  366. package/fixtures/classifier-gold/transcripts/16-meeting-sprint-planning.txt +69 -0
  367. package/fixtures/classifier-gold/transcripts/17-trivial-list-files.txt +14 -0
  368. package/fixtures/classifier-gold/transcripts/18-trivial-version-check.txt +21 -0
  369. package/fixtures/classifier-gold/transcripts/19-trivial-syntax-help.txt +29 -0
  370. package/fixtures/classifier-gold/transcripts/20-trivial-read-package-json.txt +41 -0
  371. package/migrations/000_initial_schema.sql +1 -1
  372. package/migrations/024_backfill_exemplar_repo_basename.sql +32 -0
  373. package/migrations/025_workstreams.sql +30 -0
  374. package/migrations/026_embedding_config.sql +12 -0
  375. package/migrations/027_drop_legacy_session_embeddings.sql +7 -0
  376. package/migrations/028_classifier_provenance.sql +5 -0
  377. package/migrations/pg/001_initial.sql +34 -0
  378. package/migrations/pg/019_split_replaces.sql +16 -7
  379. package/migrations/pg/025_workstreams.sql +34 -0
  380. package/migrations/pg/026_embedding_config.sql +12 -0
  381. package/migrations/pg/028_classifier_provenance.sql +5 -0
  382. package/migrations/pg/029_entity_variants.sql +9 -0
  383. package/nlm/index.js +395 -37
  384. package/package.json +12 -2
  385. package/plugin/.codex-plugin/plugin.json +1 -1
  386. package/plugin/scripts/prompt-recall-hook.mjs +402 -41
  387. package/plugin/scripts/session-start-hook.mjs +286 -60
  388. package/plugin/scripts/stop-hook.mjs +85 -65
  389. package/plugin-hermes-agent/__init__.py +2 -1
  390. package/templates/agent-contract/README.md +24 -0
  391. package/templates/agent-contract/claude-code.md +40 -0
  392. package/templates/agent-contract/generic.md +38 -0
  393. package/dist/core/embedding/embed-normalize.d.ts +0 -28
  394. package/dist/core/embedding/embed-normalize.js +0 -98
  395. package/dist/core/embedding/embed-normalize.js.map +0 -1
  396. package/dist/core/storage/pg-tx-context.d.ts +0 -52
  397. package/dist/core/storage/pg-tx-context.js +0 -149
  398. package/dist/core/storage/pg-tx-context.js.map +0 -1
@@ -0,0 +1,59 @@
1
+ User: For pulsearch I need to pick a vector database. The candidates are pgvector, Qdrant, and Weaviate. We're embedding 2-3 million documents, need sub-100ms p99 search, and I want to avoid managing a separate service if possible. Can you compare them?
2
+
3
+ Agent: Here's a structured comparison for the pulsearch use case:
4
+
5
+ pgvector
6
+ - Runs as a Postgres extension, so no separate service if you're already on Postgres
7
+ - At 2-3M vectors, IVFFlat and HNSW indexes both work but require tuning (lists parameter for IVFFlat, ef_construction for HNSW)
8
+ - Sub-100ms p99 is achievable at this scale with HNSW and appropriate connection pool sizing
9
+ - Filtering on metadata is native SQL, which is convenient
10
+ - Writes block on the HNSW index during bulk inserts; need to insert then create index, not the other way around
11
+
12
+ Qdrant
13
+ - Dedicated vector database, separate service (Docker or cloud)
14
+ - HNSW index is its primary structure, very well tuned; consistently strong benchmark numbers at 1-10M scale
15
+ - Payload filtering is first-class and done before the ANN step, not post-filter, which matters for selective queries
16
+ - REST and gRPC APIs; client libraries for Python, TypeScript, Go
17
+ - Requires running and operating another service
18
+
19
+ Weaviate
20
+ - Dedicated vector database with a built-in GraphQL query layer
21
+ - More opinionated about data modeling (schemas with classes)
22
+ - Larger operational footprint than Qdrant
23
+ - Better fit when you need hybrid BM25+vector search out of the box
24
+
25
+ For pulsearch specifically:
26
+ - If Postgres is already in your stack and ops simplicity is the priority, pgvector with HNSW is reasonable at 2-3M documents
27
+ - If you want best-in-class ANN performance and are comfortable running another service, Qdrant is the stronger choice at this scale
28
+ - Weaviate adds complexity without clear benefit unless you need its GraphQL layer
29
+
30
+ User: We're already running Postgres, but search quality is the top priority. What do you recommend?
31
+
32
+ Agent: Go with Qdrant. At 2-3M documents with sub-100ms p99 as a hard requirement, Qdrant's HNSW implementation is more predictable than pgvector's, and payload filtering before the ANN step means selective filters don't degrade recall. The operational overhead of one additional Docker container is low.
33
+
34
+ You can keep Postgres for everything else and run Qdrant alongside it. The pulsearch embedding pipeline writes to both: Postgres for the source record, Qdrant for the vector.
35
+
36
+ User: Agreed. What collection configuration would you recommend for pulsearch?
37
+
38
+ Agent: For 2-3M documents with 1536-dim embeddings (if using text-embedding-3-small) or 768-dim (if using a local model):
39
+
40
+ ```python
41
+ client.create_collection(
42
+ collection_name="pulsearch-docs",
43
+ vectors_config=VectorParams(
44
+ size=1536, # adjust to your embedding dim
45
+ distance=Distance.COSINE,
46
+ ),
47
+ hnsw_config=HnswConfigDiff(
48
+ m=16,
49
+ ef_construct=100,
50
+ ),
51
+ optimizers_config=OptimizersConfigDiff(
52
+ indexing_threshold=10_000, # don't index small batches
53
+ ),
54
+ )
55
+ ```
56
+
57
+ HNSW m=16 and ef_construct=100 are conservative defaults that hit sub-100ms comfortably at this scale. You can increase ef_construct for higher recall at the cost of slower indexing.
58
+
59
+ User: Perfect. Let's go with Qdrant.
@@ -0,0 +1,74 @@
1
+ User: irongate needs customer-facing OAuth 2.0. I'm evaluating Keycloak self-hosted versus Auth0 versus SuperTokens. The main requirements are: LDAP integration for our enterprise customers, self-hosted option, OIDC compliance. What do you think?
2
+
3
+ Agent: All three support OIDC. The differentiating factor for your requirements is the LDAP integration:
4
+
5
+ Keycloak
6
+ - Full LDAP and Active Directory federation, built-in and well-documented
7
+ - Self-hosted; runs as a Java service (Quarkus distribution, Docker-friendly)
8
+ - Enterprise-grade: major deployments at scale, active community
9
+ - Admin UI is functional but has a learning curve
10
+ - Free and open source (Apache 2.0)
11
+
12
+ Auth0
13
+ - LDAP integration via Enterprise Connections, which requires the Business tier ($800+/month for even modest user counts)
14
+ - Managed SaaS; a self-hosted option exists via Auth0 Private Cloud but is expensive and not truly self-hosted
15
+ - Developer experience is excellent; SDKs are polished
16
+ - Cost scales with monthly active users, which gets expensive fast
17
+
18
+ SuperTokens
19
+ - Self-hosted option available (open source core)
20
+ - No native LDAP integration; enterprise SSO via their Enterprise tier which supports SAML but LDAP is not supported as a user federation source
21
+ - Better for consumer apps; gaps in enterprise identity features
22
+
23
+ For irongate with the LDAP requirement, Keycloak is the clear choice. It is the only option that covers all three requirements (LDAP federation, self-hosted, OIDC) without additional cost.
24
+
25
+ User: What's the operational complexity of running Keycloak?
26
+
27
+ Agent: Moderate. Key considerations:
28
+
29
+ - Runs as a stateless application backed by Postgres (recommended) or H2 for dev
30
+ - Horizontal scaling works but requires a shared Postgres backend and sticky sessions for the admin console
31
+ - Startup time is 15-30 seconds cold; plan for that in your container orchestration health checks
32
+ - The default Keycloak Docker image needs a few environment variables: KEYCLOAK_ADMIN, KEYCLOAK_ADMIN_PASSWORD, KC_DB_URL, KC_DB_USERNAME, KC_DB_PASSWORD
33
+ - Realm export/import is the standard backup mechanism; automate this
34
+
35
+ For irongate at this stage (pre-scale), a single Keycloak instance behind your nginx proxy on a dedicated small VM is sufficient. You can add a replica later.
36
+
37
+ User: Let's go with Keycloak self-hosted backed by Postgres.
38
+
39
+ Agent: Good choice. I'll draft the Docker Compose configuration for the irongate auth stack.
40
+
41
+ [Write: docker-compose.auth.yml]
42
+
43
+ ```yaml
44
+ services:
45
+ keycloak:
46
+ image: quay.io/keycloak/keycloak:24.0
47
+ command: start
48
+ environment:
49
+ KC_DB: postgres
50
+ KC_DB_URL: jdbc:postgresql://auth-db:5432/keycloak
51
+ KC_DB_USERNAME: keycloak
52
+ KC_DB_PASSWORD: ${KC_DB_PASSWORD}
53
+ KC_HOSTNAME: auth.irongate.example.com
54
+ KEYCLOAK_ADMIN: admin
55
+ KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
56
+ depends_on:
57
+ - auth-db
58
+ ports:
59
+ - "8180:8080"
60
+
61
+ auth-db:
62
+ image: postgres:16
63
+ environment:
64
+ POSTGRES_DB: keycloak
65
+ POSTGRES_USER: keycloak
66
+ POSTGRES_PASSWORD: ${KC_DB_PASSWORD}
67
+ volumes:
68
+ - keycloak-pgdata:/var/lib/postgresql/data
69
+
70
+ volumes:
71
+ keycloak-pgdata:
72
+ ```
73
+
74
+ User: Perfect. We'll proceed with Keycloak.
@@ -0,0 +1,154 @@
1
+ User: I need to write API documentation for cobalt-api. The API has about 12 endpoints. I want to use OpenAPI 3.0 format and host the docs at /api-docs. Can you help me set this up and draft the first few endpoints?
2
+
3
+ Agent: [Read: src/routes/index.ts]
4
+
5
+ I can see the route structure. Let me read the auth and resources endpoints to understand the request/response shapes.
6
+
7
+ [Read: src/routes/auth.ts]
8
+ [Read: src/routes/resources.ts]
9
+
10
+ Good. I'll draft the OpenAPI 3.0 spec for the auth and resources sections. For hosting at /api-docs, Swagger UI is the standard choice and integrates cleanly with Express.
11
+
12
+ User: Use Swagger UI. Let's document the auth endpoints first: POST /auth/token and POST /auth/refresh.
13
+
14
+ Agent: [Write: docs/openapi.yaml]
15
+
16
+ ```yaml
17
+ openapi: "3.0.3"
18
+ info:
19
+ title: cobalt-api
20
+ version: "1.0.0"
21
+ description: REST API for the Cobalt platform
22
+
23
+ servers:
24
+ - url: https://api.cobalt.example.com/v1
25
+ description: Production
26
+ - url: http://localhost:3000/v1
27
+ description: Local development
28
+
29
+ components:
30
+ securitySchemes:
31
+ BearerAuth:
32
+ type: http
33
+ scheme: bearer
34
+ bearerFormat: JWT
35
+
36
+ schemas:
37
+ TokenResponse:
38
+ type: object
39
+ required: [access_token, refresh_token, expires_in]
40
+ properties:
41
+ access_token:
42
+ type: string
43
+ refresh_token:
44
+ type: string
45
+ expires_in:
46
+ type: integer
47
+ description: Seconds until access_token expires
48
+
49
+ Error:
50
+ type: object
51
+ required: [error, message]
52
+ properties:
53
+ error:
54
+ type: string
55
+ message:
56
+ type: string
57
+
58
+ paths:
59
+ /auth/token:
60
+ post:
61
+ summary: Exchange credentials for tokens
62
+ requestBody:
63
+ required: true
64
+ content:
65
+ application/json:
66
+ schema:
67
+ type: object
68
+ required: [email, password]
69
+ properties:
70
+ email:
71
+ type: string
72
+ format: email
73
+ password:
74
+ type: string
75
+ responses:
76
+ "200":
77
+ description: Token pair
78
+ content:
79
+ application/json:
80
+ schema:
81
+ $ref: "#/components/schemas/TokenResponse"
82
+ "401":
83
+ description: Invalid credentials
84
+ content:
85
+ application/json:
86
+ schema:
87
+ $ref: "#/components/schemas/Error"
88
+
89
+ /auth/refresh:
90
+ post:
91
+ summary: Exchange a refresh token for a new access token
92
+ requestBody:
93
+ required: true
94
+ content:
95
+ application/json:
96
+ schema:
97
+ type: object
98
+ required: [refresh_token]
99
+ properties:
100
+ refresh_token:
101
+ type: string
102
+ responses:
103
+ "200":
104
+ description: New token pair
105
+ content:
106
+ application/json:
107
+ schema:
108
+ $ref: "#/components/schemas/TokenResponse"
109
+ "401":
110
+ description: Invalid or expired refresh token
111
+ ```
112
+
113
+ User: Good. Now wire up Swagger UI to serve this at /api-docs.
114
+
115
+ Agent: [Bash: npm install swagger-ui-express @types/swagger-ui-express yaml -D]
116
+
117
+ [Read: src/app.ts]
118
+
119
+ [Edit: src/app.ts]
120
+
121
+ ```typescript
122
+ import swaggerUi from "swagger-ui-express";
123
+ import { readFileSync } from "node:fs";
124
+ import { parse } from "yaml";
125
+
126
+ const spec = parse(readFileSync("docs/openapi.yaml", "utf-8")) as object;
127
+ app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(spec));
128
+ ```
129
+
130
+ User: Does this need to be behind auth?
131
+
132
+ Agent: Typically no; API docs are public. But if cobalt-api exposes internal implementation details (internal error codes, admin endpoints), you might want to gate it. For now I'll leave it public and you can add middleware later if needed.
133
+
134
+ User: Fine as-is. Run the server and confirm /api-docs loads.
135
+
136
+ Agent: [Bash: npm run dev &]
137
+
138
+ ```
139
+ Server listening on http://localhost:3000
140
+ ```
141
+
142
+ [Bash: curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/api-docs/]
143
+
144
+ ```
145
+ 301
146
+ ```
147
+
148
+ [Bash: curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/api-docs]
149
+
150
+ ```
151
+ 200
152
+ ```
153
+
154
+ Redirects to trailing slash, returns 200. Swagger UI is serving the spec at /api-docs.
@@ -0,0 +1,69 @@
1
+ User: I need to capture the notes from our sprint planning meeting for orbitflow. Here's what we covered:
2
+
3
+ Attendees: product, backend, frontend squads
4
+
5
+ Sprint 5 goals we discussed:
6
+ - Webhook retry feature (backend priority, carries over from sprint 4 incomplete)
7
+ - Analytics dashboard (frontend wanted this but product deprioritized it)
8
+ - Rate limiting on the ingest API (security team flagged it last week)
9
+ - Mobile push notifications (new request, everyone agreed too large for this sprint)
10
+
11
+ Capacity: backend has 3 devs, one on PTO mid-sprint. Frontend has 2 devs full capacity.
12
+
13
+ Decisions made:
14
+ - Webhook retry moves to sprint 5 as the top backend priority
15
+ - Analytics dashboard deferred to sprint 6
16
+ - Rate limiting assigned to backend as secondary item
17
+ - Mobile push notifications deferred to sprint 6 backlog
18
+
19
+ Sprint 5 end date: July 18.
20
+
21
+ Can you format these into clean sprint notes?
22
+
23
+ Agent: Here are the sprint 5 planning notes for orbitflow:
24
+
25
+ ---
26
+
27
+ Sprint 5 Planning Notes
28
+ orbitflow | Sprint 5 | Ends July 18
29
+
30
+ Attendees: Product, Backend squad (3 devs, one PTO mid-sprint), Frontend squad (2 devs)
31
+
32
+ Committed Items
33
+
34
+ Backend (primary)
35
+ - Webhook retry with exponential backoff [carries over from sprint 4]
36
+
37
+ Backend (secondary)
38
+ - Rate limiting on ingest API [security flag from last week]
39
+
40
+ Deferred
41
+
42
+ - Analytics dashboard: pushed to sprint 6 [frontend request, deprioritized by product]
43
+ - Mobile push notifications: pushed to sprint 6 backlog [scope too large for current capacity]
44
+
45
+ Capacity Notes
46
+
47
+ Backend is constrained mid-sprint due to PTO; webhook retry is the single priority. Rate limiting is secondary and may slip if webhook retry runs long.
48
+
49
+ ---
50
+
51
+ User: Looks good. Can you also add a risks section?
52
+
53
+ Agent: Updated with risks:
54
+
55
+ ---
56
+
57
+ Risks
58
+
59
+ 1. Backend PTO mid-sprint reduces capacity by roughly 33% for one week. If webhook retry is not at code review by day 6, rate limiting will slip to sprint 6.
60
+ 2. Rate limiting scope is not fully defined. The security team flagged the need but did not specify limits or key type. Backend needs a brief sync with security before starting implementation.
61
+ 3. Analytics dashboard deferral may resurface as a blocker if frontend has no other committed work. Confirm frontend squad has sufficient sprint 5 items.
62
+
63
+ ---
64
+
65
+ User: Perfect. Save this to docs/sprints/sprint-5-planning.md.
66
+
67
+ Agent: [Write: docs/sprints/sprint-5-planning.md]
68
+
69
+ Saved. The sprint 5 notes are in docs/sprints/sprint-5-planning.md with attendees, committed items, deferrals, capacity notes, and risks.
@@ -0,0 +1,14 @@
1
+ User: What files are in the src/ directory?
2
+
3
+ Agent: [Bash: ls src/]
4
+
5
+ ```
6
+ app.ts
7
+ config.ts
8
+ index.ts
9
+ routes.ts
10
+ ```
11
+
12
+ Four files: app.ts, config.ts, index.ts, and routes.ts.
13
+
14
+ User: Thanks.
@@ -0,0 +1,21 @@
1
+ User: What version of Node.js is installed?
2
+
3
+ Agent: [Bash: node --version]
4
+
5
+ ```
6
+ v22.4.0
7
+ ```
8
+
9
+ Node.js v22.4.0.
10
+
11
+ User: And npm?
12
+
13
+ Agent: [Bash: npm --version]
14
+
15
+ ```
16
+ 10.8.1
17
+ ```
18
+
19
+ npm 10.8.1.
20
+
21
+ User: Good, that's what I needed.
@@ -0,0 +1,29 @@
1
+ User: How do I iterate over an array in TypeScript and get both the index and the value?
2
+
3
+ Agent: Use `Array.prototype.entries()` with a `for...of` loop:
4
+
5
+ ```typescript
6
+ const items = ["a", "b", "c"];
7
+ for (const [index, value] of items.entries()) {
8
+ console.log(index, value);
9
+ }
10
+ ```
11
+
12
+ Output:
13
+ ```
14
+ 0 a
15
+ 1 b
16
+ 2 c
17
+ ```
18
+
19
+ Alternatively, `forEach` with two parameters:
20
+
21
+ ```typescript
22
+ items.forEach((value, index) => {
23
+ console.log(index, value);
24
+ });
25
+ ```
26
+
27
+ The `for...of` with `.entries()` is generally preferred in modern TypeScript because it works with `break` and `continue`, which `forEach` does not.
28
+
29
+ User: Got it, thanks.
@@ -0,0 +1,41 @@
1
+ User: Read the package.json and tell me what dependencies are listed.
2
+
3
+ Agent: [Read: package.json]
4
+
5
+ ```json
6
+ {
7
+ "name": "my-project",
8
+ "version": "1.0.0",
9
+ "dependencies": {
10
+ "express": "^4.18.2",
11
+ "pg": "^8.11.0"
12
+ },
13
+ "devDependencies": {
14
+ "typescript": "^5.3.3",
15
+ "vitest": "^1.2.0"
16
+ }
17
+ }
18
+ ```
19
+
20
+ Dependencies:
21
+ - express ^4.18.2
22
+ - pg ^8.11.0
23
+
24
+ Dev dependencies:
25
+ - typescript ^5.3.3
26
+ - vitest ^1.2.0
27
+
28
+ User: Are they installed?
29
+
30
+ Agent: [Bash: ls node_modules | grep -E "^(express|pg|typescript|vitest)$"]
31
+
32
+ ```
33
+ express
34
+ pg
35
+ typescript
36
+ vitest
37
+ ```
38
+
39
+ All four are installed in node_modules.
40
+
41
+ User: Perfect, thanks.
@@ -40,7 +40,7 @@ CREATE TABLE IF NOT EXISTS entities (
40
40
  canonical TEXT PRIMARY KEY, -- preferred spelling — also the primary key
41
41
  type TEXT NOT NULL, -- 'candidate' until labeled. Built-in labels: project | tool | contact | service | concept. Custom labels are user-defined via the UI / `nle-daemon action label` and have no CHECK constraint.
42
42
  status TEXT NOT NULL CHECK(status IN ('active','dormant','retired','rejected','candidate')),
43
- source TEXT, -- e.g. 'property:.claude/properties/polysignal.yaml', 'auto-detected', 'user-registered'
43
+ source TEXT, -- e.g. 'property:.claude/properties/beacon.yaml', 'auto-detected', 'user-registered'
44
44
  notes TEXT, -- freeform user notes
45
45
  first_seen_session TEXT REFERENCES sessions(id),
46
46
  last_seen_session TEXT REFERENCES sessions(id),
@@ -0,0 +1,32 @@
1
+ -- Migration 024: backfill code_exemplars.repo to logical basenames.
2
+ --
3
+ -- The pre-#330 session-capture path (captureExemplarsFromSession) stamped the
4
+ -- absolute projectDir into the repo column, leaking a host filesystem path
5
+ -- (/Users/.../<name>) into the local store — meaningless on a client install
6
+ -- and a privacy concern (per-install scoping, synthesis item 4). The #330
7
+ -- producer (nlm code-signal) and the now-fixed session-capture path both
8
+ -- record the logical basename instead.
9
+ --
10
+ -- This strips any directory prefix from existing absolute-path repos in place,
11
+ -- generically (no hardcoded host path), via a recursive CTE that peels off
12
+ -- each segment up to and including the next '/' until none remains. Idempotent:
13
+ -- once every repo is a basename, `repo LIKE '/%'` matches nothing and re-running
14
+ -- is a no-op. Fresh installs have no leaked rows, so this is a no-op there too.
15
+ --
16
+ -- The primary key id (sha256 of install_scope|repo|code_hash|outcome) is NOT
17
+ -- recomputed — SQLite cannot sha256 in pure SQL. The stale id is harmless: it
18
+ -- stays a valid unique key and keeps its embedding link. The only consequence
19
+ -- is that re-capturing the exact same legacy commit via the basename path would
20
+ -- compute a fresh id and insert a duplicate rather than dedup-skip — a rare
21
+ -- edge (requires a full re-ingest of old sessions) and never wrong data.
22
+
23
+ WITH RECURSIVE basename(id, rest) AS (
24
+ SELECT id, repo FROM code_exemplars WHERE repo LIKE '/%'
25
+ UNION ALL
26
+ SELECT id, substr(rest, instr(rest, '/') + 1) FROM basename WHERE instr(rest, '/') > 0
27
+ )
28
+ UPDATE code_exemplars
29
+ SET repo = (SELECT rest FROM basename WHERE basename.id = code_exemplars.id AND instr(basename.rest, '/') = 0)
30
+ WHERE repo LIKE '/%';
31
+
32
+ INSERT OR IGNORE INTO schema_migrations (version, name) VALUES (24, '024_backfill_exemplar_repo_basename');
@@ -0,0 +1,30 @@
1
+ -- migrations/025_workstreams.sql
2
+ -- Workstream abstraction (#367) Plan A: a persistent container a session binds
3
+ -- to at end-of-session. See docs/superpowers/specs/2026-06-24-workstream-abstraction-design.md.
4
+
5
+ CREATE TABLE IF NOT EXISTS workstreams (
6
+ id TEXT PRIMARY KEY, -- ws_<uuid>
7
+ label TEXT NOT NULL,
8
+ status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active','merged','retired')),
9
+ merged_into TEXT REFERENCES workstreams(id),
10
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
11
+ updated_at TEXT NOT NULL DEFAULT (datetime('now')),
12
+ last_session_at TEXT
13
+ );
14
+
15
+ CREATE TABLE IF NOT EXISTS workstream_entities (
16
+ workstream_id TEXT NOT NULL REFERENCES workstreams(id) ON DELETE CASCADE,
17
+ entity_canonical TEXT NOT NULL REFERENCES entities(canonical),
18
+ session_count INTEGER NOT NULL DEFAULT 0,
19
+ PRIMARY KEY (workstream_id, entity_canonical)
20
+ );
21
+
22
+ CREATE INDEX IF NOT EXISTS idx_workstream_entities_entity ON workstream_entities(entity_canonical);
23
+
24
+ ALTER TABLE sessions ADD COLUMN workstream_id TEXT REFERENCES workstreams(id);
25
+ ALTER TABLE sessions ADD COLUMN binding_source TEXT;
26
+ ALTER TABLE sessions ADD COLUMN binding_confidence REAL;
27
+
28
+ CREATE INDEX IF NOT EXISTS idx_sessions_workstream ON sessions(workstream_id);
29
+
30
+ INSERT OR IGNORE INTO schema_migrations (version, name) VALUES (25, 'workstreams');
@@ -0,0 +1,12 @@
1
+ -- Records which embedder produced the stored vectors, per lane.
2
+ -- lane 'prose' covers session chunks and fact embeddings (same embedder);
3
+ -- lane 'code' covers code exemplar embeddings.
4
+ -- Absence of a row means pre-tracking corpus: reconcile treats the running
5
+ -- embedder as canonical and records it.
6
+ CREATE TABLE IF NOT EXISTS embedding_config (
7
+ lane TEXT PRIMARY KEY CHECK (lane IN ('prose', 'code')),
8
+ provider TEXT NOT NULL,
9
+ model TEXT NOT NULL,
10
+ dim INTEGER NOT NULL,
11
+ updated_at TEXT NOT NULL
12
+ );
@@ -0,0 +1,7 @@
1
+ -- Migration 027: drop the legacy session_embeddings virtual table.
2
+ -- Migration 003 created it; migration 009 superseded it with the chunk + max-pool index.
3
+
4
+ DROP TABLE IF EXISTS session_embeddings;
5
+
6
+ INSERT OR IGNORE INTO schema_migrations (version, name)
7
+ VALUES (27, '027_drop_legacy_session_embeddings');
@@ -0,0 +1,5 @@
1
+ -- Which classifier produced this session's classification. NULL = classified
2
+ -- before provenance tracking; nlm reprocess treats NULL as eligible.
3
+ ALTER TABLE sessions ADD COLUMN classifier_provider TEXT;
4
+ ALTER TABLE sessions ADD COLUMN classifier_model TEXT;
5
+ ALTER TABLE sessions ADD COLUMN classifier_confidence REAL;
@@ -26,6 +26,9 @@ CREATE TABLE IF NOT EXISTS sessions (
26
26
  transcript_length BIGINT,
27
27
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
28
28
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
29
+ workstream_id TEXT,
30
+ binding_source TEXT,
31
+ binding_confidence DOUBLE PRECISION,
29
32
  fts_vector TSVECTOR GENERATED ALWAYS AS (
30
33
  setweight(to_tsvector('english', coalesce(label, '')), 'A') ||
31
34
  setweight(to_tsvector('english', coalesce(summary, '')), 'B') ||
@@ -255,3 +258,34 @@ CREATE TABLE IF NOT EXISTS code_exemplar_embeddings (
255
258
  CREATE INDEX IF NOT EXISTS code_exemplar_embeddings_idx
256
259
  ON code_exemplar_embeddings USING ivfflat (embedding vector_l2_ops)
257
260
  WITH (lists = 100);
261
+
262
+ -- ── Workstreams (mirror of SQLite migration 025) ─────────────────────────────
263
+ CREATE TABLE IF NOT EXISTS workstreams (
264
+ id TEXT PRIMARY KEY,
265
+ label TEXT NOT NULL,
266
+ status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active','merged','retired')),
267
+ merged_into TEXT REFERENCES workstreams(id),
268
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
269
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
270
+ last_session_at TIMESTAMPTZ
271
+ );
272
+
273
+ CREATE TABLE IF NOT EXISTS workstream_entities (
274
+ workstream_id TEXT NOT NULL REFERENCES workstreams(id) ON DELETE CASCADE,
275
+ entity_canonical TEXT NOT NULL REFERENCES entities(canonical),
276
+ session_count INTEGER NOT NULL DEFAULT 0,
277
+ PRIMARY KEY (workstream_id, entity_canonical)
278
+ );
279
+
280
+ CREATE INDEX IF NOT EXISTS idx_workstream_entities_entity ON workstream_entities(entity_canonical);
281
+
282
+ -- FK from sessions.workstream_id to workstreams (deferred so workstreams exists first)
283
+ DO $$
284
+ BEGIN
285
+ ALTER TABLE sessions
286
+ ADD CONSTRAINT fk_sessions_workstream
287
+ FOREIGN KEY (workstream_id) REFERENCES workstreams(id);
288
+ EXCEPTION WHEN duplicate_object THEN NULL;
289
+ END $$;
290
+
291
+ CREATE INDEX IF NOT EXISTS idx_sessions_workstream ON sessions(workstream_id);
@@ -1,9 +1,10 @@
1
1
  -- PG parity for SQLite migration 019: split the mechanical `replaces` relation
2
2
  -- out of `supersedes`.
3
3
  --
4
- -- One-shot repair, applied manually by an operator against a PG canonical store
5
- -- (PgStorage.init only runs 001_initial.sql; there is no version-gated runner on
6
- -- the PG side). Mirrors migrations/019_split_replaces.sql.
4
+ -- Idempotent: safe to re-apply on a database that already has this shape.
5
+ -- DROP CONSTRAINT IF EXISTS removes any existing named constraint before the
6
+ -- DO-block re-adds it, so duplicate_object is never raised in practice;
7
+ -- the EXCEPTION clause is defensive for parity with the 001 pattern.
7
8
  --
8
9
  -- Widens the CHECK constraints on sessions.status and session_edges.kind to
9
10
  -- admit 'replaced' / 'replaces', then reclassifies existing mechanical edges:
@@ -13,12 +14,20 @@
13
14
  BEGIN;
14
15
 
15
16
  ALTER TABLE sessions DROP CONSTRAINT IF EXISTS sessions_status_check;
16
- ALTER TABLE sessions ADD CONSTRAINT sessions_status_check
17
- CHECK (status IN ('active', 'closed', 'superseded', 'replaced'));
17
+ DO $$
18
+ BEGIN
19
+ ALTER TABLE sessions ADD CONSTRAINT sessions_status_check
20
+ CHECK (status IN ('active', 'closed', 'superseded', 'replaced'));
21
+ EXCEPTION WHEN duplicate_object THEN NULL;
22
+ END $$;
18
23
 
19
24
  ALTER TABLE session_edges DROP CONSTRAINT IF EXISTS session_edges_kind_check;
20
- ALTER TABLE session_edges ADD CONSTRAINT session_edges_kind_check
21
- CHECK (kind IN ('supersedes', 'replaces', 'continues'));
25
+ DO $$
26
+ BEGIN
27
+ ALTER TABLE session_edges ADD CONSTRAINT session_edges_kind_check
28
+ CHECK (kind IN ('supersedes', 'replaces', 'continues'));
29
+ EXCEPTION WHEN duplicate_object THEN NULL;
30
+ END $$;
22
31
 
23
32
  UPDATE session_edges e SET kind = 'replaces'
24
33
  WHERE e.kind = 'supersedes'