nlm-memory 0.20.1 → 0.21.12

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 (475) hide show
  1. package/README.md +69 -12
  2. package/assets/replay-eval-dark.svg +26 -0
  3. package/assets/replay-eval-light.svg +26 -0
  4. package/dist/cli/digest.js +69 -4
  5. package/dist/cli/digest.js.map +1 -1
  6. package/dist/cli/nlm.d.ts +14 -0
  7. package/dist/cli/nlm.js +556 -71
  8. package/dist/cli/nlm.js.map +1 -1
  9. package/dist/cli/reprocess-job-adapters.d.ts +35 -0
  10. package/dist/cli/reprocess-job-adapters.js +80 -0
  11. package/dist/cli/reprocess-job-adapters.js.map +1 -0
  12. package/dist/cli/scope-backfill.d.ts +34 -0
  13. package/dist/cli/scope-backfill.js +237 -0
  14. package/dist/cli/scope-backfill.js.map +1 -0
  15. package/dist/cli/scope-coverage.d.ts +44 -0
  16. package/dist/cli/scope-coverage.js +208 -0
  17. package/dist/cli/scope-coverage.js.map +1 -0
  18. package/dist/cli/supersede.js +8 -7
  19. package/dist/cli/supersede.js.map +1 -1
  20. package/dist/core/actions/actions-log.d.ts +65 -8
  21. package/dist/core/actions/actions-log.js +134 -17
  22. package/dist/core/actions/actions-log.js.map +1 -1
  23. package/dist/core/actions/overlay.d.ts +11 -2
  24. package/dist/core/actions/overlay.js +18 -6
  25. package/dist/core/actions/overlay.js.map +1 -1
  26. package/dist/core/adapters/claude-code.d.ts +14 -0
  27. package/dist/core/adapters/claude-code.js +30 -0
  28. package/dist/core/adapters/claude-code.js.map +1 -1
  29. package/dist/core/adapters/from-source.js +11 -3
  30. package/dist/core/adapters/from-source.js.map +1 -1
  31. package/dist/core/adapters/fs-wiki-writer.d.ts +44 -0
  32. package/dist/core/adapters/fs-wiki-writer.js +132 -0
  33. package/dist/core/adapters/fs-wiki-writer.js.map +1 -0
  34. package/dist/core/adapters/hermes-agent.d.ts +15 -2
  35. package/dist/core/adapters/hermes-agent.js +57 -11
  36. package/dist/core/adapters/hermes-agent.js.map +1 -1
  37. package/dist/core/adapters/memory-wiki-writer.d.ts +8 -0
  38. package/dist/core/adapters/memory-wiki-writer.js +16 -0
  39. package/dist/core/adapters/memory-wiki-writer.js.map +1 -0
  40. package/dist/core/adapters/muse.d.ts +51 -0
  41. package/dist/core/adapters/muse.js +271 -0
  42. package/dist/core/adapters/muse.js.map +1 -0
  43. package/dist/core/adapters/opencode.d.ts +11 -4
  44. package/dist/core/adapters/opencode.js +41 -7
  45. package/dist/core/adapters/opencode.js.map +1 -1
  46. package/dist/core/alerts/alert-state.d.ts +17 -0
  47. package/dist/core/alerts/alert-state.js +45 -0
  48. package/dist/core/alerts/alert-state.js.map +1 -0
  49. package/dist/core/alerts/check-and-alert.d.ts +28 -0
  50. package/dist/core/alerts/check-and-alert.js +53 -0
  51. package/dist/core/alerts/check-and-alert.js.map +1 -0
  52. package/dist/core/alerts/drift-transition.d.ts +27 -0
  53. package/dist/core/alerts/drift-transition.js +56 -0
  54. package/dist/core/alerts/drift-transition.js.map +1 -0
  55. package/dist/core/alerts/embedder-transition.d.ts +23 -0
  56. package/dist/core/alerts/embedder-transition.js +40 -0
  57. package/dist/core/alerts/embedder-transition.js.map +1 -0
  58. package/dist/core/alerts/fire-alert.d.ts +24 -0
  59. package/dist/core/alerts/fire-alert.js +72 -0
  60. package/dist/core/alerts/fire-alert.js.map +1 -0
  61. package/dist/core/alerts/job-alert.d.ts +12 -0
  62. package/dist/core/alerts/job-alert.js +37 -0
  63. package/dist/core/alerts/job-alert.js.map +1 -0
  64. package/dist/core/alerts/types.d.ts +104 -0
  65. package/dist/core/alerts/types.js +11 -0
  66. package/dist/core/alerts/types.js.map +1 -0
  67. package/dist/core/alerts/wiki-alert.d.ts +15 -0
  68. package/dist/core/alerts/wiki-alert.js +31 -0
  69. package/dist/core/alerts/wiki-alert.js.map +1 -0
  70. package/dist/core/classifier/prompt.d.ts +13 -0
  71. package/dist/core/classifier/prompt.js +52 -2
  72. package/dist/core/classifier/prompt.js.map +1 -1
  73. package/dist/core/dataset/build-dataset.d.ts +16 -1
  74. package/dist/core/dataset/build-dataset.js +46 -13
  75. package/dist/core/dataset/build-dataset.js.map +1 -1
  76. package/dist/core/digest/compose.d.ts +11 -3
  77. package/dist/core/digest/compose.js +70 -17
  78. package/dist/core/digest/compose.js.map +1 -1
  79. package/dist/core/digest/hook-injection.d.ts +43 -0
  80. package/dist/core/digest/hook-injection.js +87 -0
  81. package/dist/core/digest/hook-injection.js.map +1 -0
  82. package/dist/core/digest/hook-version-parity.d.ts +31 -0
  83. package/dist/core/digest/hook-version-parity.js +56 -0
  84. package/dist/core/digest/hook-version-parity.js.map +1 -0
  85. package/dist/core/embedding/pg-embed-backfill.js +1 -1
  86. package/dist/core/embedding/pg-embed-backfill.js.map +1 -1
  87. package/dist/core/exemplars/capture-from-session.d.ts +2 -1
  88. package/dist/core/exemplars/capture-from-session.js +4 -4
  89. package/dist/core/exemplars/capture-from-session.js.map +1 -1
  90. package/dist/core/exemplars/embed-backfill.d.ts +9 -0
  91. package/dist/core/exemplars/embed-backfill.js +9 -1
  92. package/dist/core/exemplars/embed-backfill.js.map +1 -1
  93. package/dist/core/exemplars/extract-exemplar.d.ts +2 -0
  94. package/dist/core/exemplars/extract-exemplar.js +7 -1
  95. package/dist/core/exemplars/extract-exemplar.js.map +1 -1
  96. package/dist/core/exemplars/ingest-exemplar.d.ts +1 -0
  97. package/dist/core/exemplars/ingest-exemplar.js +1 -0
  98. package/dist/core/exemplars/ingest-exemplar.js.map +1 -1
  99. package/dist/core/exemplars/recall-code.d.ts +1 -1
  100. package/dist/core/exemplars/recall-code.js +2 -2
  101. package/dist/core/exemplars/recall-code.js.map +1 -1
  102. package/dist/core/facts/backfill-facts.d.ts +1 -1
  103. package/dist/core/facts/backfill-facts.js +4 -4
  104. package/dist/core/facts/backfill-facts.js.map +1 -1
  105. package/dist/core/facts/embed-backfill.d.ts +54 -0
  106. package/dist/core/facts/embed-backfill.js +109 -0
  107. package/dist/core/facts/embed-backfill.js.map +1 -0
  108. package/dist/core/facts/reconcile-facts.d.ts +42 -0
  109. package/dist/core/facts/reconcile-facts.js +133 -0
  110. package/dist/core/facts/reconcile-facts.js.map +1 -0
  111. package/dist/core/health/embed-failure-state.d.ts +4 -0
  112. package/dist/core/health/embed-failure-state.js +12 -0
  113. package/dist/core/health/embed-failure-state.js.map +1 -0
  114. package/dist/core/health/warmup-retry.d.ts +27 -0
  115. package/dist/core/health/warmup-retry.js +53 -0
  116. package/dist/core/health/warmup-retry.js.map +1 -0
  117. package/dist/core/health/warmup-state.d.ts +15 -0
  118. package/dist/core/health/warmup-state.js +15 -1
  119. package/dist/core/health/warmup-state.js.map +1 -1
  120. package/dist/core/hook/cite-memo.d.ts +11 -7
  121. package/dist/core/hook/cite-memo.js +25 -17
  122. package/dist/core/hook/cite-memo.js.map +1 -1
  123. package/dist/core/hook/claude-settings.d.ts +1 -1
  124. package/dist/core/hook/claude-settings.js +0 -1
  125. package/dist/core/hook/claude-settings.js.map +1 -1
  126. package/dist/core/hook/hook-log.d.ts +13 -2
  127. package/dist/core/hook/hook-log.js +16 -8
  128. package/dist/core/hook/hook-log.js.map +1 -1
  129. package/dist/core/hook/memo.d.ts +10 -6
  130. package/dist/core/hook/memo.js +25 -17
  131. package/dist/core/hook/memo.js.map +1 -1
  132. package/dist/core/hook/pointer-block.d.ts +8 -1
  133. package/dist/core/hook/pointer-block.js +17 -4
  134. package/dist/core/hook/pointer-block.js.map +1 -1
  135. package/dist/core/hook/resolve-conversation-by-query.d.ts +6 -5
  136. package/dist/core/hook/resolve-conversation-by-query.js +24 -12
  137. package/dist/core/hook/resolve-conversation-by-query.js.map +1 -1
  138. package/dist/core/ingest/backfill-derivables.d.ts +92 -0
  139. package/dist/core/ingest/backfill-derivables.js +175 -0
  140. package/dist/core/ingest/backfill-derivables.js.map +1 -0
  141. package/dist/core/ingest/ingest-session.d.ts +1 -1
  142. package/dist/core/ingest/ingest-session.js +23 -3
  143. package/dist/core/ingest/ingest-session.js.map +1 -1
  144. package/dist/core/ingest/reclassify-oversized.d.ts +1 -1
  145. package/dist/core/ingest/reclassify-oversized.js +8 -2
  146. package/dist/core/ingest/reclassify-oversized.js.map +1 -1
  147. package/dist/core/ingest/reprocess.d.ts +1 -1
  148. package/dist/core/ingest/reprocess.js +7 -2
  149. package/dist/core/ingest/reprocess.js.map +1 -1
  150. package/dist/core/ingest/transcript-derivables.d.ts +27 -0
  151. package/dist/core/ingest/transcript-derivables.js +138 -0
  152. package/dist/core/ingest/transcript-derivables.js.map +1 -0
  153. package/dist/core/jobs/job-supervisor.d.ts +168 -0
  154. package/dist/core/jobs/job-supervisor.js +335 -0
  155. package/dist/core/jobs/job-supervisor.js.map +1 -0
  156. package/dist/core/learner/model.d.ts +63 -0
  157. package/dist/core/learner/model.js +30 -0
  158. package/dist/core/learner/model.js.map +1 -0
  159. package/dist/core/learner/service.d.ts +42 -0
  160. package/dist/core/learner/service.js +74 -0
  161. package/dist/core/learner/service.js.map +1 -0
  162. package/dist/core/learner/summary.d.ts +28 -0
  163. package/dist/core/learner/summary.js +70 -0
  164. package/dist/core/learner/summary.js.map +1 -0
  165. package/dist/core/learner/validate.d.ts +18 -0
  166. package/dist/core/learner/validate.js +95 -0
  167. package/dist/core/learner/validate.js.map +1 -0
  168. package/dist/core/metrics/re-derivation.d.ts +31 -2
  169. package/dist/core/metrics/re-derivation.js +36 -4
  170. package/dist/core/metrics/re-derivation.js.map +1 -1
  171. package/dist/core/outcome/coverage.d.ts +33 -0
  172. package/dist/core/outcome/coverage.js +46 -0
  173. package/dist/core/outcome/coverage.js.map +1 -0
  174. package/dist/core/outcome/rollup.d.ts +33 -0
  175. package/dist/core/outcome/rollup.js +113 -0
  176. package/dist/core/outcome/rollup.js.map +1 -0
  177. package/dist/core/providers/provider-registry.d.ts +40 -23
  178. package/dist/core/providers/provider-registry.js +99 -81
  179. package/dist/core/providers/provider-registry.js.map +1 -1
  180. package/dist/core/recall/citation-log.d.ts +10 -5
  181. package/dist/core/recall/citation-log.js +21 -10
  182. package/dist/core/recall/citation-log.js.map +1 -1
  183. package/dist/core/recall/hook-recall-log.d.ts +7 -4
  184. package/dist/core/recall/hook-recall-log.js +14 -8
  185. package/dist/core/recall/hook-recall-log.js.map +1 -1
  186. package/dist/core/recall/miss-log.d.ts +7 -4
  187. package/dist/core/recall/miss-log.js +18 -11
  188. package/dist/core/recall/miss-log.js.map +1 -1
  189. package/dist/core/recall/query-log.d.ts +8 -5
  190. package/dist/core/recall/query-log.js +16 -9
  191. package/dist/core/recall/query-log.js.map +1 -1
  192. package/dist/core/recall/recall-service.d.ts +10 -1
  193. package/dist/core/recall/recall-service.js +31 -12
  194. package/dist/core/recall/recall-service.js.map +1 -1
  195. package/dist/core/recall/recent-log.d.ts +6 -1
  196. package/dist/core/recall/recent-log.js +13 -5
  197. package/dist/core/recall/recent-log.js.map +1 -1
  198. package/dist/core/recall/related-concepts.d.ts +29 -0
  199. package/dist/core/recall/related-concepts.js +67 -0
  200. package/dist/core/recall/related-concepts.js.map +1 -0
  201. package/dist/core/recall/related-exemplars.d.ts +1 -1
  202. package/dist/core/recall/related-exemplars.js +2 -2
  203. package/dist/core/recall/related-exemplars.js.map +1 -1
  204. package/dist/core/recall/related-facts.d.ts +1 -1
  205. package/dist/core/recall/related-facts.js +3 -3
  206. package/dist/core/recall/related-facts.js.map +1 -1
  207. package/dist/core/recall-facts/fact-query-log.d.ts +8 -4
  208. package/dist/core/recall-facts/fact-query-log.js +16 -8
  209. package/dist/core/recall-facts/fact-query-log.js.map +1 -1
  210. package/dist/core/recall-facts/fact-recall-service.d.ts +2 -2
  211. package/dist/core/recall-facts/fact-recall-service.js +13 -12
  212. package/dist/core/recall-facts/fact-recall-service.js.map +1 -1
  213. package/dist/core/scheduler/scan-once.js +15 -12
  214. package/dist/core/scheduler/scan-once.js.map +1 -1
  215. package/dist/core/scheduler/scheduler.d.ts +3 -0
  216. package/dist/core/scheduler/scheduler.js +45 -10
  217. package/dist/core/scheduler/scheduler.js.map +1 -1
  218. package/dist/core/scope/alias-map.d.ts +10 -0
  219. package/dist/core/scope/alias-map.js +41 -0
  220. package/dist/core/scope/alias-map.js.map +1 -0
  221. package/dist/core/scope/derive-scope.d.ts +8 -0
  222. package/dist/core/scope/derive-scope.js +54 -0
  223. package/dist/core/scope/derive-scope.js.map +1 -0
  224. package/dist/core/scope/scope-clause.d.ts +16 -0
  225. package/dist/core/scope/scope-clause.js +21 -0
  226. package/dist/core/scope/scope-clause.js.map +1 -0
  227. package/dist/core/scope/stamp-flag.d.ts +1 -0
  228. package/dist/core/scope/stamp-flag.js +4 -0
  229. package/dist/core/scope/stamp-flag.js.map +1 -0
  230. package/dist/core/signals/code-signal.d.ts +1 -0
  231. package/dist/core/signals/code-signal.js +1 -0
  232. package/dist/core/signals/code-signal.js.map +1 -1
  233. package/dist/core/signals/failure-mode-recall.d.ts +1 -1
  234. package/dist/core/signals/failure-mode-recall.js +2 -2
  235. package/dist/core/signals/failure-mode-recall.js.map +1 -1
  236. package/dist/core/signals/ingest-signal.d.ts +1 -1
  237. package/dist/core/signals/ingest-signal.js +3 -1
  238. package/dist/core/signals/ingest-signal.js.map +1 -1
  239. package/dist/core/signals/stamp-scope.d.ts +18 -0
  240. package/dist/core/signals/stamp-scope.js +28 -0
  241. package/dist/core/signals/stamp-scope.js.map +1 -0
  242. package/dist/core/sources/source-registry.d.ts +49 -28
  243. package/dist/core/sources/source-registry.js +113 -70
  244. package/dist/core/sources/source-registry.js.map +1 -1
  245. package/dist/core/storage/pg-code-exemplar-store.d.ts +19 -10
  246. package/dist/core/storage/pg-code-exemplar-store.js +62 -27
  247. package/dist/core/storage/pg-code-exemplar-store.js.map +1 -1
  248. package/dist/core/storage/pg-entity-store.d.ts +6 -1
  249. package/dist/core/storage/pg-entity-store.js +33 -18
  250. package/dist/core/storage/pg-entity-store.js.map +1 -1
  251. package/dist/core/storage/pg-fact-ingest.d.ts +1 -1
  252. package/dist/core/storage/pg-fact-ingest.js +10 -7
  253. package/dist/core/storage/pg-fact-ingest.js.map +1 -1
  254. package/dist/core/storage/pg-fact-store.d.ts +29 -17
  255. package/dist/core/storage/pg-fact-store.js +99 -46
  256. package/dist/core/storage/pg-fact-store.js.map +1 -1
  257. package/dist/core/storage/pg-learner-store.d.ts +27 -0
  258. package/dist/core/storage/pg-learner-store.js +174 -0
  259. package/dist/core/storage/pg-learner-store.js.map +1 -0
  260. package/dist/core/storage/pg-session-store.d.ts +25 -21
  261. package/dist/core/storage/pg-session-store.js +186 -114
  262. package/dist/core/storage/pg-session-store.js.map +1 -1
  263. package/dist/core/storage/pg-signal-store.d.ts +10 -5
  264. package/dist/core/storage/pg-signal-store.js +26 -13
  265. package/dist/core/storage/pg-signal-store.js.map +1 -1
  266. package/dist/core/storage/pg-storage.d.ts +4 -0
  267. package/dist/core/storage/pg-storage.js +6 -0
  268. package/dist/core/storage/pg-storage.js.map +1 -1
  269. package/dist/core/storage/pg-workstream-store.d.ts +17 -11
  270. package/dist/core/storage/pg-workstream-store.js +47 -29
  271. package/dist/core/storage/pg-workstream-store.js.map +1 -1
  272. package/dist/core/storage/sqlite-code-exemplar-store.d.ts +24 -10
  273. package/dist/core/storage/sqlite-code-exemplar-store.js +86 -40
  274. package/dist/core/storage/sqlite-code-exemplar-store.js.map +1 -1
  275. package/dist/core/storage/sqlite-entity-store.d.ts +8 -1
  276. package/dist/core/storage/sqlite-entity-store.js +46 -27
  277. package/dist/core/storage/sqlite-entity-store.js.map +1 -1
  278. package/dist/core/storage/sqlite-fact-store.d.ts +45 -19
  279. package/dist/core/storage/sqlite-fact-store.js +175 -64
  280. package/dist/core/storage/sqlite-fact-store.js.map +1 -1
  281. package/dist/core/storage/sqlite-learner-store.d.ts +33 -0
  282. package/dist/core/storage/sqlite-learner-store.js +178 -0
  283. package/dist/core/storage/sqlite-learner-store.js.map +1 -0
  284. package/dist/core/storage/sqlite-outcome-store.d.ts +104 -0
  285. package/dist/core/storage/sqlite-outcome-store.js +211 -0
  286. package/dist/core/storage/sqlite-outcome-store.js.map +1 -0
  287. package/dist/core/storage/sqlite-session-store.d.ts +50 -21
  288. package/dist/core/storage/sqlite-session-store.js +233 -149
  289. package/dist/core/storage/sqlite-session-store.js.map +1 -1
  290. package/dist/core/storage/sqlite-signal-store.d.ts +12 -5
  291. package/dist/core/storage/sqlite-signal-store.js +29 -15
  292. package/dist/core/storage/sqlite-signal-store.js.map +1 -1
  293. package/dist/core/storage/sqlite-storage.d.ts +4 -0
  294. package/dist/core/storage/sqlite-storage.js +10 -2
  295. package/dist/core/storage/sqlite-storage.js.map +1 -1
  296. package/dist/core/storage/sqlite-workstream-store.d.ts +17 -11
  297. package/dist/core/storage/sqlite-workstream-store.js +53 -31
  298. package/dist/core/storage/sqlite-workstream-store.js.map +1 -1
  299. package/dist/core/storage/supersedence-log.d.ts +10 -7
  300. package/dist/core/storage/supersedence-log.js +21 -14
  301. package/dist/core/storage/supersedence-log.js.map +1 -1
  302. package/dist/core/telemetry/probe-filter.d.ts +35 -0
  303. package/dist/core/telemetry/probe-filter.js +56 -0
  304. package/dist/core/telemetry/probe-filter.js.map +1 -0
  305. package/dist/core/tenancy/default-team.d.ts +6 -0
  306. package/dist/core/tenancy/default-team.js +7 -0
  307. package/dist/core/tenancy/default-team.js.map +1 -0
  308. package/dist/core/tenancy/hosted-mode.d.ts +9 -0
  309. package/dist/core/tenancy/hosted-mode.js +12 -0
  310. package/dist/core/tenancy/hosted-mode.js.map +1 -0
  311. package/dist/core/tenancy/team-auth.d.ts +16 -0
  312. package/dist/core/tenancy/team-auth.js +22 -0
  313. package/dist/core/tenancy/team-auth.js.map +1 -0
  314. package/dist/core/tenancy/team-token-store.d.ts +45 -0
  315. package/dist/core/tenancy/team-token-store.js +52 -0
  316. package/dist/core/tenancy/team-token-store.js.map +1 -0
  317. package/dist/core/tenancy/tenant-clause.d.ts +37 -0
  318. package/dist/core/tenancy/tenant-clause.js +11 -0
  319. package/dist/core/tenancy/tenant-clause.js.map +1 -0
  320. package/dist/core/tenancy/tenant-state-path.d.ts +28 -0
  321. package/dist/core/tenancy/tenant-state-path.js +49 -0
  322. package/dist/core/tenancy/tenant-state-path.js.map +1 -0
  323. package/dist/core/update-check/check.d.ts +6 -0
  324. package/dist/core/update-check/check.js +35 -0
  325. package/dist/core/update-check/check.js.map +1 -1
  326. package/dist/core/wiki/project.d.ts +17 -0
  327. package/dist/core/wiki/project.js +37 -0
  328. package/dist/core/wiki/project.js.map +1 -0
  329. package/dist/core/wiki/reconcile.d.ts +15 -0
  330. package/dist/core/wiki/reconcile.js +14 -0
  331. package/dist/core/wiki/reconcile.js.map +1 -0
  332. package/dist/core/wiki/render.d.ts +5 -0
  333. package/dist/core/wiki/render.js +84 -0
  334. package/dist/core/wiki/render.js.map +1 -0
  335. package/dist/core/wiki/rollup.d.ts +28 -0
  336. package/dist/core/wiki/rollup.js +91 -0
  337. package/dist/core/wiki/rollup.js.map +1 -0
  338. package/dist/core/wiki/select.d.ts +8 -0
  339. package/dist/core/wiki/select.js +7 -0
  340. package/dist/core/wiki/select.js.map +1 -0
  341. package/dist/core/wiki/slug.d.ts +36 -0
  342. package/dist/core/wiki/slug.js +71 -0
  343. package/dist/core/wiki/slug.js.map +1 -0
  344. package/dist/core/wiki/types.d.ts +37 -0
  345. package/dist/core/wiki/types.js +2 -0
  346. package/dist/core/wiki/types.js.map +1 -0
  347. package/dist/core/work-digest/build-work-digest.d.ts +1 -1
  348. package/dist/core/work-digest/build-work-digest.js +3 -3
  349. package/dist/core/work-digest/build-work-digest.js.map +1 -1
  350. package/dist/core/workstream/bind.d.ts +3 -2
  351. package/dist/core/workstream/bind.js +29 -10
  352. package/dist/core/workstream/bind.js.map +1 -1
  353. package/dist/core/workstream/model.d.ts +1 -0
  354. package/dist/core/workstream/model.js.map +1 -1
  355. package/dist/core/workstream/rollup.d.ts +1 -1
  356. package/dist/core/workstream/rollup.js +6 -6
  357. package/dist/core/workstream/rollup.js.map +1 -1
  358. package/dist/hook/hook-helpers.d.ts +14 -0
  359. package/dist/hook/hook-helpers.js +29 -2
  360. package/dist/hook/hook-helpers.js.map +1 -1
  361. package/dist/hook/pre-compact-hook.js +8 -5
  362. package/dist/hook/pre-compact-hook.js.map +1 -1
  363. package/dist/hook/prompt-recall-hook.d.ts +7 -3
  364. package/dist/hook/prompt-recall-hook.js +35 -20
  365. package/dist/hook/prompt-recall-hook.js.map +1 -1
  366. package/dist/hook/recall-over-http.d.ts +28 -3
  367. package/dist/hook/recall-over-http.js +58 -12
  368. package/dist/hook/recall-over-http.js.map +1 -1
  369. package/dist/hook/session-end-hook.js +7 -5
  370. package/dist/hook/session-end-hook.js.map +1 -1
  371. package/dist/hook/session-start-hook.d.ts +14 -1
  372. package/dist/hook/session-start-hook.js +57 -16
  373. package/dist/hook/session-start-hook.js.map +1 -1
  374. package/dist/hook/stop-hook.js +11 -8
  375. package/dist/hook/stop-hook.js.map +1 -1
  376. package/dist/http/app.d.ts +47 -3
  377. package/dist/http/app.js +379 -132
  378. package/dist/http/app.js.map +1 -1
  379. package/dist/install/claude-code.d.ts +17 -0
  380. package/dist/install/claude-code.js +44 -5
  381. package/dist/install/claude-code.js.map +1 -1
  382. package/dist/install/codex.d.ts +23 -2
  383. package/dist/install/codex.js +111 -14
  384. package/dist/install/codex.js.map +1 -1
  385. package/dist/install/cursor.d.ts +2 -2
  386. package/dist/install/cursor.js +7 -7
  387. package/dist/install/cursor.js.map +1 -1
  388. package/dist/install/hermes.d.ts +8 -0
  389. package/dist/install/hermes.js +28 -5
  390. package/dist/install/hermes.js.map +1 -1
  391. package/dist/install/local-source.d.ts +20 -0
  392. package/dist/install/local-source.js +28 -0
  393. package/dist/install/local-source.js.map +1 -0
  394. package/dist/install/mcp-block.d.ts +77 -0
  395. package/dist/install/mcp-block.js +99 -0
  396. package/dist/install/mcp-block.js.map +1 -0
  397. package/dist/install/pi.d.ts +26 -0
  398. package/dist/install/pi.js +43 -0
  399. package/dist/install/pi.js.map +1 -1
  400. package/dist/install/setup.js +6 -0
  401. package/dist/install/setup.js.map +1 -1
  402. package/dist/install/windsurf.d.ts +2 -2
  403. package/dist/install/windsurf.js +7 -7
  404. package/dist/install/windsurf.js.map +1 -1
  405. package/dist/llm/classifier-box.d.ts +5 -0
  406. package/dist/llm/classifier-box.js +5 -0
  407. package/dist/llm/classifier-box.js.map +1 -1
  408. package/dist/mcp/server.d.ts +94 -15
  409. package/dist/mcp/server.js +402 -79
  410. package/dist/mcp/server.js.map +1 -1
  411. package/dist/ports/clock.d.ts +12 -0
  412. package/dist/ports/clock.js +10 -0
  413. package/dist/ports/clock.js.map +1 -0
  414. package/dist/ports/code-exemplar-store.d.ts +13 -13
  415. package/dist/ports/entity-store.d.ts +5 -1
  416. package/dist/ports/fact-store.d.ts +0 -0
  417. package/dist/ports/fact-store.js +0 -0
  418. package/dist/ports/fact-store.js.map +1 -1
  419. package/dist/ports/learner-store.d.ts +80 -0
  420. package/dist/ports/learner-store.js +19 -0
  421. package/dist/ports/learner-store.js.map +1 -0
  422. package/dist/ports/outcome.d.ts +85 -0
  423. package/dist/ports/outcome.js +11 -0
  424. package/dist/ports/outcome.js.map +1 -0
  425. package/dist/ports/session-store.d.ts +20 -20
  426. package/dist/ports/session-store.js +5 -0
  427. package/dist/ports/session-store.js.map +1 -1
  428. package/dist/ports/signal-store.d.ts +12 -8
  429. package/dist/ports/spawn-child.d.ts +14 -0
  430. package/dist/ports/spawn-child.js +10 -0
  431. package/dist/ports/spawn-child.js.map +1 -0
  432. package/dist/ports/storage.d.ts +6 -0
  433. package/dist/ports/transcript-adapter.d.ts +11 -0
  434. package/dist/ports/wiki-writer.d.ts +19 -0
  435. package/dist/ports/wiki-writer.js +12 -0
  436. package/dist/ports/wiki-writer.js.map +1 -0
  437. package/dist/ports/workstream-store.d.ts +20 -12
  438. package/dist/shared/net.d.ts +3 -0
  439. package/dist/shared/net.js +4 -0
  440. package/dist/shared/net.js.map +1 -0
  441. package/dist/shared/types.d.ts +55 -0
  442. package/dist/shared/types.js +4 -1
  443. package/dist/shared/types.js.map +1 -1
  444. package/dist/shared/version.d.ts +9 -0
  445. package/dist/shared/version.js +11 -0
  446. package/dist/shared/version.js.map +1 -0
  447. package/dist/ui/assets/index-D6F3xAis.js +11 -0
  448. package/dist/ui/index.html +1 -1
  449. package/migrations/029_project_scope.sql +21 -0
  450. package/migrations/032_session_derivables.sql +11 -0
  451. package/migrations/033_actions_kind_check.sql +58 -0
  452. package/migrations/034_tenancy_teams_and_stamps.sql +40 -0
  453. package/migrations/035_tenancy_entity_rekey.sql +75 -0
  454. package/migrations/036_tenant_name_uniqueness.sql +59 -0
  455. package/migrations/037_sessions_covering_index.sql +25 -0
  456. package/migrations/038_sources_muse.sql +33 -0
  457. package/migrations/039_learner_evidence.sql +51 -0
  458. package/migrations/pg/030_project_scope.sql +23 -0
  459. package/migrations/pg/031_widen_session_edges_kind.sql +11 -0
  460. package/migrations/pg/032_session_derivables.sql +17 -0
  461. package/migrations/pg/033_actions_kind_check.sql +17 -0
  462. package/migrations/pg/034_tenancy.sql +77 -0
  463. package/migrations/pg/035_tenant_name_uniqueness.sql +15 -0
  464. package/migrations/pg/036_sources_muse.sql +12 -0
  465. package/migrations/pg/037_learner_evidence.sql +55 -0
  466. package/nlm/index.js +258 -59
  467. package/package.json +10 -5
  468. package/plugin/.codex-plugin/plugin.json +1 -1
  469. package/plugin/scripts/prompt-recall-hook.mjs +256 -58
  470. package/plugin/scripts/session-start-hook.mjs +283 -55
  471. package/plugin/scripts/stop-hook.mjs +199 -38
  472. package/dist/hook/subagent-start-hook.d.ts +0 -28
  473. package/dist/hook/subagent-start-hook.js +0 -81
  474. package/dist/hook/subagent-start-hook.js.map +0 -1
  475. package/dist/ui/assets/index-CMpQkbQy.js +0 -11
@@ -4,6 +4,11 @@
4
4
  * Constructor takes the Pool from PgStorage. Also exposes recentWrites()
5
5
  * and recentMarkers() for the /live HTTP endpoints, and insertSession() +
6
6
  * insertSessionForTest() for ingest and test seeding.
7
+ *
8
+ * Tenancy: mirrors SqliteSessionStore — every method takes `tenantId` as its
9
+ * non-optional first parameter and routes STAMP-table WHERE fragments
10
+ * through `tenantClausePg`. See that file's header comment for the full
11
+ * per-table rationale.
7
12
  */
8
13
  import { tokenize } from "../recall/tokenize.js";
9
14
  import { ingestSessionFactsOnClient } from "./pg-fact-ingest.js";
@@ -11,28 +16,39 @@ import { chunkSessionText } from "../embedding/chunk-body.js";
11
16
  import { batchWinners } from "./fact-batch.js";
12
17
  import { loadActionOverlayPg, openQuestionId } from "../actions/overlay.js";
13
18
  import { liveSessionStatus } from "./live-status.js";
19
+ import { tenantClausePg } from "../tenancy/tenant-clause.js";
20
+ import { DEFAULT_TEAM_ID } from "../tenancy/default-team.js";
21
+ import { recordEmbedFailure } from "../health/embed-failure-state.js";
14
22
  /**
15
23
  * PG mirror of SQLite findContinuesPredecessor: the most recent prior session
16
24
  * whose distinct entity-set is identical to the new session's. Runs on the
17
25
  * ingest transaction's own client. Returns null when there is no entity-set or
18
26
  * no exact-set match, leaving the pair unlinked for the re_derivation_rate metric.
27
+ * Scoped to the same tenant as the new session.
19
28
  */
20
- async function findContinuesPredecessorPg(client, newId, rawEntities) {
29
+ async function findContinuesPredecessorPg(client, tenantId, newId, rawEntities) {
21
30
  const entities = [...new Set(rawEntities.map((e) => e.trim()).filter(Boolean))];
22
31
  if (entities.length === 0)
23
32
  return null;
24
- const placeholders = entities.map((_, i) => `$${i + 2}`).join(",");
33
+ const placeholders = entities.map((_, i) => `$${i + 3}`).join(",");
34
+ // session_entities rows are always written with the same tenant_id as their
35
+ // owning session (every write site stamps both together), so scoping the
36
+ // outer `sessions` row by tenant is already sufficient. The `se.tenant_id =
37
+ // s.tenant_id` join condition below is defense in depth (guard test C4
38
+ // explicitly permits column-to-column tenant equality as a join form).
39
+ const tc = tenantClausePg(tenantId, 2, "s.tenant_id");
25
40
  const res = await client.query(`SELECT s.id AS id
26
41
  FROM sessions s
27
- JOIN session_entities se ON se.session_id = s.id
42
+ JOIN session_entities se ON se.session_id = s.id AND se.tenant_id = s.tenant_id
28
43
  WHERE s.id != $1
44
+ AND ${tc.sql}
29
45
  AND se.entity_canonical IN (${placeholders})
30
46
  GROUP BY s.id
31
- HAVING COUNT(DISTINCT se.entity_canonical) = $${entities.length + 2}
47
+ HAVING COUNT(DISTINCT se.entity_canonical) = $${entities.length + 3}
32
48
  AND COUNT(DISTINCT se.entity_canonical)
33
49
  = (SELECT COUNT(*) FROM session_entities x WHERE x.session_id = s.id)
34
50
  ORDER BY s.started_at DESC, s.id DESC
35
- LIMIT 1`, [newId, ...entities, entities.length]);
51
+ LIMIT 1`, [newId, tc.param, ...entities, entities.length]);
36
52
  return res.rows[0]?.id ?? null;
37
53
  }
38
54
  export class PgSessionStore {
@@ -42,98 +58,79 @@ export class PgSessionStore {
42
58
  constructor(pool) {
43
59
  this.pool = pool;
44
60
  }
45
- overlayCache = null;
46
- overlayCacheAt = 0;
61
+ // Keyed by tenantId — see SqliteSessionStore's identical cache for the
62
+ // tenancy rationale (program spec §4.6 hardening 1).
63
+ overlayCache = new Map();
47
64
  invalidateOverlayCache() {
48
- this.overlayCache = null;
65
+ this.overlayCache.clear();
49
66
  }
50
- async overlay() {
67
+ async overlay(tenantId) {
51
68
  // TTL backstop: explicit invalidation covers the daemon's own writers; the
52
69
  // 30s expiry bounds staleness if another process ever writes actions to
53
70
  // this database.
54
- if (this.overlayCache !== null && Date.now() - this.overlayCacheAt < 30_000) {
55
- return this.overlayCache;
71
+ const cached = this.overlayCache.get(tenantId);
72
+ if (cached !== undefined && Date.now() - cached.at < 30_000) {
73
+ return cached.overlay;
56
74
  }
57
- this.overlayCache = await loadActionOverlayPg(this.pool);
58
- this.overlayCacheAt = Date.now();
59
- return this.overlayCache;
75
+ const overlay = await loadActionOverlayPg(this.pool, tenantId);
76
+ this.overlayCache.set(tenantId, { overlay, at: Date.now() });
77
+ return overlay;
60
78
  }
61
- async list(filter) {
79
+ async getById(tenantId, sessionId) {
80
+ const tc = tenantClausePg(tenantId, 2);
62
81
  const result = await this.pool.query(`SELECT id, runtime, runtime_session_id, started_at, ended_at, duration_min,
63
82
  label, summary, status, transcript_kind, transcript_path, body,
64
- classifier_provider, classifier_model, classifier_confidence
65
- FROM sessions ORDER BY started_at ASC`);
66
- if (result.rows.length === 0)
67
- return [];
68
- const ids = result.rows.map((r) => r.id);
69
- const [entitiesMap, markersMap, overlay] = await Promise.all([
70
- this.loadEntities(ids),
71
- this.loadMarkers(ids),
72
- this.overlay(),
73
- ]);
74
- const sessions = result.rows.map((r) => rowToSession(r, entitiesMap, markersMap, overlay));
75
- if (!filter)
76
- return sessions;
77
- return sessions.filter((s) => {
78
- if (filter.entity !== undefined && !s.entities.includes(filter.entity))
79
- return false;
80
- if (filter.hasDecisions === true && s.decisions.length === 0)
81
- return false;
82
- if (filter.hasOpenQuestions === true && s.open.length === 0)
83
- return false;
84
- return true;
85
- });
86
- }
87
- async getById(sessionId) {
88
- const result = await this.pool.query(`SELECT id, runtime, runtime_session_id, started_at, ended_at, duration_min,
89
- label, summary, status, transcript_kind, transcript_path, body,
90
- classifier_provider, classifier_model, classifier_confidence
91
- FROM sessions WHERE id = $1`, [sessionId]);
83
+ classifier_provider, classifier_model, classifier_confidence,
84
+ agent_persona, parent_session_id,
85
+ primary_model, total_tokens, skill
86
+ FROM sessions WHERE id = $1 AND ${tc.sql}`, [sessionId, tc.param]);
92
87
  if (!result.rows[0])
93
88
  return null;
94
89
  const [entitiesMap, markersMap, edgesMap, overlay] = await Promise.all([
95
- this.loadEntities([sessionId]),
90
+ this.loadEntities(tenantId, [sessionId]),
96
91
  this.loadMarkers([sessionId]),
97
92
  this.loadEdges([sessionId]),
98
- this.overlay(),
93
+ this.overlay(tenantId),
99
94
  ]);
100
95
  const edges = edgesMap.get(sessionId);
101
96
  return rowToSession(result.rows[0], entitiesMap, markersMap, overlay, edges);
102
97
  }
103
- async getByIds(ids) {
98
+ async getByIds(tenantId, ids) {
104
99
  if (ids.length === 0)
105
100
  return [];
106
101
  const placeholders = ids.map((_, i) => `$${i + 1}`).join(",");
102
+ const tc = tenantClausePg(tenantId, ids.length + 1);
107
103
  const result = await this.pool.query(`SELECT id, runtime, runtime_session_id, started_at, ended_at, duration_min,
108
104
  label, summary, status, transcript_kind, transcript_path
109
- FROM sessions WHERE id IN (${placeholders})`, [...ids]);
105
+ FROM sessions WHERE id IN (${placeholders}) AND ${tc.sql}`, [...ids, tc.param]);
110
106
  if (result.rows.length === 0)
111
107
  return [];
112
108
  const foundIds = result.rows.map((r) => r.id);
113
109
  const [entitiesMap, markersMap, overlay] = await Promise.all([
114
- this.loadEntities(foundIds),
110
+ this.loadEntities(tenantId, foundIds),
115
111
  this.loadMarkers(foundIds),
116
- this.overlay(),
112
+ this.overlay(tenantId),
117
113
  ]);
118
114
  return result.rows.map((r) => rowToSession({ ...r, body: null }, entitiesMap, markersMap, overlay));
119
115
  }
120
- async listByDateRange(fromIso, toIso) {
116
+ async listByDateRange(tenantId, fromIso, toIso) {
117
+ const tc = tenantClausePg(tenantId, 3);
121
118
  const result = await this.pool.query(`SELECT id, runtime, runtime_session_id, started_at, ended_at, duration_min,
122
119
  label, summary, status, transcript_kind, transcript_path, workstream_id
123
120
  FROM sessions
124
- WHERE started_at < $1 AND (ended_at IS NULL OR ended_at >= $2)
125
- ORDER BY started_at ASC`, [toIso, fromIso]);
121
+ WHERE started_at < $1 AND (ended_at IS NULL OR ended_at >= $2) AND ${tc.sql}
122
+ ORDER BY started_at ASC`, [toIso, fromIso, tc.param]);
126
123
  if (result.rows.length === 0)
127
124
  return [];
128
125
  const ids = result.rows.map((r) => r.id);
129
126
  const [entitiesMap, markersMap, overlay] = await Promise.all([
130
- this.loadEntities(ids),
127
+ this.loadEntities(tenantId, ids),
131
128
  this.loadMarkers(ids),
132
- this.overlay(),
129
+ this.overlay(tenantId),
133
130
  ]);
134
131
  return result.rows.map((r) => rowToSession({ ...r, body: null }, entitiesMap, markersMap, overlay));
135
132
  }
136
- async semanticSearch(queryVector, limit, opts) {
133
+ async semanticSearch(tenantId, queryVector, limit, opts) {
137
134
  const wsIds = opts?.workstreamIds;
138
135
  if (wsIds !== undefined && wsIds.length === 0)
139
136
  return [];
@@ -148,19 +145,24 @@ export class PgSessionStore {
148
145
  params.push(wsIds);
149
146
  wsClause = `AND s.workstream_id = ANY($${params.length}::text[])`;
150
147
  }
148
+ // Tenant filter is applied in the id-resolution join against `sessions`
149
+ // (program spec §4.3, vector-path rule) — the `<->` KNN scan over
150
+ // session_embedding_chunks has no tenant column of its own.
151
+ const tc = tenantClausePg(tenantId, params.length + 1, "s.tenant_id");
152
+ params.push(tc.param);
153
+ const limitParam = `$${params.length + 1}`;
151
154
  params.push(k);
152
- const limitParam = `$${params.length}`;
153
155
  const result = await this.pool.query(`SELECT sec.session_id, MIN(sec.embedding <-> $1::vector) AS distance, s.status
154
156
  FROM session_embedding_chunks sec
155
157
  JOIN sessions s ON s.id = sec.session_id
156
- WHERE TRUE ${wsClause}
158
+ WHERE ${tc.sql} ${wsClause}
157
159
  GROUP BY sec.session_id, s.status
158
160
  HAVING ${statusFilter}
159
161
  ORDER BY distance
160
162
  LIMIT ${limitParam}`, params);
161
163
  return result.rows.map((r) => ({ sessionId: r.session_id, distance: r.distance }));
162
164
  }
163
- async keywordSearch(query, limit, opts) {
165
+ async keywordSearch(tenantId, query, limit, opts) {
164
166
  const wsIds = opts?.workstreamIds;
165
167
  if (wsIds !== undefined && wsIds.length === 0)
166
168
  return [];
@@ -178,6 +180,8 @@ export class PgSessionStore {
178
180
  params.push(wsIds);
179
181
  wsClause = `AND workstream_id = ANY($${params.length}::text[])`;
180
182
  }
183
+ const tc = tenantClausePg(tenantId, params.length + 1);
184
+ params.push(tc.param);
181
185
  params.push(k);
182
186
  const limitParam = `$${params.length}`;
183
187
  const result = await this.pool.query(`SELECT id AS session_id,
@@ -186,39 +190,46 @@ export class PgSessionStore {
186
190
  WHERE fts_vector @@ websearch_to_tsquery('english', $1)
187
191
  AND ${statusFilter}
188
192
  ${wsClause}
193
+ AND ${tc.sql}
189
194
  ORDER BY score DESC
190
195
  LIMIT ${limitParam}`, params);
191
196
  return result.rows.map((r) => ({ sessionId: r.session_id, score: r.score }));
192
197
  }
193
- async resolveSuccessors(ids) {
198
+ async resolveSuccessors(tenantId, ids) {
194
199
  if (ids.length === 0)
195
200
  return new Map();
196
201
  const placeholders = ids.map((_, i) => `$${i + 1}`).join(",");
197
- const result = await this.pool.query(`SELECT from_session, to_session FROM session_edges
198
- WHERE kind = 'supersedes' AND to_session IN (${placeholders})`, [...ids]);
202
+ const tc = tenantClausePg(tenantId, ids.length + 1, "s.tenant_id");
203
+ const result = await this.pool.query(`SELECT se.from_session, se.to_session
204
+ FROM session_edges se
205
+ JOIN sessions s ON s.id = se.to_session
206
+ WHERE se.kind = 'supersedes' AND se.to_session IN (${placeholders}) AND ${tc.sql}`, [...ids, tc.param]);
199
207
  const out = new Map();
200
208
  for (const r of result.rows)
201
209
  out.set(r.to_session, r.from_session);
202
210
  return out;
203
211
  }
204
- async updateStatus(sessionId, status) {
212
+ async updateStatus(tenantId, sessionId, status) {
205
213
  if (status === "idle") {
206
214
  throw new Error("Cannot persist derived status 'idle' — only active/closed/superseded");
207
215
  }
208
- await this.pool.query("UPDATE sessions SET status = $1, updated_at = NOW() WHERE id = $2", [status, sessionId]);
216
+ const tc = tenantClausePg(tenantId, 3);
217
+ await this.pool.query(`UPDATE sessions SET status = $1, updated_at = NOW() WHERE id = $2 AND ${tc.sql}`, [status, sessionId, tc.param]);
209
218
  }
210
- async markSuperseded(predecessorId, successorId) {
219
+ async markSuperseded(tenantId, predecessorId, successorId) {
211
220
  if (predecessorId === successorId) {
212
221
  throw new Error("A session cannot supersede itself");
213
222
  }
214
223
  const client = await this.pool.connect();
215
224
  try {
216
225
  await client.query("BEGIN");
217
- const predExists = await client.query("SELECT COUNT(*) AS c FROM sessions WHERE id = $1", [predecessorId]);
226
+ const predExistsTc = tenantClausePg(tenantId, 2);
227
+ const predExists = await client.query(`SELECT COUNT(*) AS c FROM sessions WHERE id = $1 AND ${predExistsTc.sql}`, [predecessorId, predExistsTc.param]);
218
228
  if (Number(predExists.rows[0]?.c) === 0) {
219
229
  throw new Error(`predecessor session ${predecessorId} not found`);
220
230
  }
221
- const succExists = await client.query("SELECT COUNT(*) AS c FROM sessions WHERE id = $1", [successorId]);
231
+ const succExistsTc = tenantClausePg(tenantId, 2);
232
+ const succExists = await client.query(`SELECT COUNT(*) AS c FROM sessions WHERE id = $1 AND ${succExistsTc.sql}`, [successorId, succExistsTc.param]);
222
233
  if (Number(succExists.rows[0]?.c) === 0) {
223
234
  throw new Error(`successor session ${successorId} not found`);
224
235
  }
@@ -247,8 +258,12 @@ export class PgSessionStore {
247
258
  await client.query(`INSERT INTO session_edges (from_session, to_session, kind)
248
259
  VALUES ($1, $2, 'supersedes')
249
260
  ON CONFLICT DO NOTHING`, [successorId, predecessorId]);
250
- await client.query("UPDATE sessions SET status = 'superseded', updated_at = NOW() WHERE id = $1", [predecessorId]);
251
- // Cascade supersedence to facts in a single correlated UPDATE
261
+ const statusTc = tenantClausePg(tenantId, 2);
262
+ await client.query(`UPDATE sessions SET status = 'superseded', updated_at = NOW() WHERE id = $1 AND ${statusTc.sql}`, [predecessorId, statusTc.param]);
263
+ // Cascade supersedence to facts in a single correlated UPDATE, scoped to
264
+ // the caller's tenant on both sides of the join.
265
+ const sTc = tenantClausePg(tenantId, 3, "s.tenant_id");
266
+ const pTc = tenantClausePg(tenantId, 3, "p.tenant_id");
252
267
  const cascadeSQL = `
253
268
  UPDATE facts AS p
254
269
  SET superseded_by = (
@@ -257,18 +272,21 @@ export class PgSessionStore {
257
272
  AND s.subject = p.subject
258
273
  AND s.predicate = p.predicate
259
274
  AND s.superseded_by IS NULL
275
+ AND ${sTc.sql}
260
276
  LIMIT 1
261
277
  )
262
278
  WHERE p.source_session_id = $1
279
+ AND ${pTc.sql}
263
280
  AND EXISTS (
264
281
  SELECT 1 FROM facts s
265
282
  WHERE s.source_session_id = $2
266
283
  AND s.subject = p.subject
267
284
  AND s.predicate = p.predicate
268
285
  AND s.superseded_by IS NULL
286
+ AND ${sTc.sql}
269
287
  )
270
288
  `;
271
- const cascaded = await client.query(cascadeSQL + " RETURNING p.id", [predecessorId, successorId]);
289
+ const cascaded = await client.query(cascadeSQL + " RETURNING p.id", [predecessorId, successorId, sTc.param]);
272
290
  const cascadedIds = cascaded.rows.map((r) => r.id);
273
291
  if (cascadedIds.length > 0) {
274
292
  await client.query("DELETE FROM fact_embeddings WHERE fact_id = ANY($1)", [cascadedIds]);
@@ -283,39 +301,49 @@ export class PgSessionStore {
283
301
  client.release();
284
302
  }
285
303
  }
286
- async setWorkstreamBinding(sessionId, workstreamId, source, confidence) {
287
- await this.pool.query("UPDATE sessions SET workstream_id = $1, binding_source = $2, binding_confidence = $3, updated_at = NOW() WHERE id = $4", [workstreamId, source, confidence, sessionId]);
304
+ async getSessionScopeById(tenantId, id) {
305
+ const tc = tenantClausePg(tenantId, 2);
306
+ const r = await this.pool.query(`SELECT scope FROM sessions WHERE id = $1 AND ${tc.sql}`, [id, tc.param]);
307
+ return r.rows[0]?.scope ?? null;
308
+ }
309
+ async setWorkstreamBinding(tenantId, sessionId, workstreamId, source, confidence) {
310
+ const tc = tenantClausePg(tenantId, 5);
311
+ await this.pool.query(`UPDATE sessions SET workstream_id = $1, binding_source = $2, binding_confidence = $3, updated_at = NOW() WHERE id = $4 AND ${tc.sql}`, [workstreamId, source, confidence, sessionId, tc.param]);
288
312
  }
289
- async listSessionIdsByWorkstreams(workstreamIds) {
313
+ async listSessionIdsByWorkstreams(tenantId, workstreamIds) {
290
314
  if (workstreamIds.length === 0)
291
315
  return [];
292
316
  const ph = workstreamIds.map((_, i) => `$${i + 1}`).join(",");
293
- const r = await this.pool.query(`SELECT id FROM sessions WHERE workstream_id IN (${ph}) ORDER BY started_at ASC`, [...workstreamIds]);
317
+ const tc = tenantClausePg(tenantId, workstreamIds.length + 1);
318
+ const r = await this.pool.query(`SELECT id FROM sessions WHERE workstream_id IN (${ph}) AND ${tc.sql} ORDER BY started_at ASC`, [...workstreamIds, tc.param]);
294
319
  return r.rows.map((row) => row.id);
295
320
  }
296
- async getEntities(sessionId) {
297
- return (await this.loadEntities([sessionId])).get(sessionId) ?? [];
321
+ async getEntities(tenantId, sessionId) {
322
+ return (await this.loadEntities(tenantId, [sessionId])).get(sessionId) ?? [];
298
323
  }
299
- async getWorkstreamIds(sessionIds) {
324
+ async getWorkstreamIds(tenantId, sessionIds) {
300
325
  const out = new Map();
301
326
  if (sessionIds.length === 0)
302
327
  return out;
303
328
  const ph = sessionIds.map((_, i) => `$${i + 1}`).join(",");
304
- const result = await this.pool.query(`SELECT id, workstream_id FROM sessions WHERE id IN (${ph})`, [...sessionIds]);
329
+ const tc = tenantClausePg(tenantId, sessionIds.length + 1);
330
+ const result = await this.pool.query(`SELECT id, workstream_id FROM sessions WHERE id IN (${ph}) AND ${tc.sql}`, [...sessionIds, tc.param]);
305
331
  for (const r of result.rows)
306
332
  out.set(r.id, r.workstream_id);
307
333
  return out;
308
334
  }
309
- async recentWrites(limit) {
335
+ async recentWrites(tenantId, limit) {
336
+ const tc = tenantClausePg(tenantId, 2);
310
337
  const result = await this.pool.query(`SELECT id, runtime, label, summary, created_at AS "createdAt"
311
- FROM sessions ORDER BY created_at DESC LIMIT $1`, [limit]);
338
+ FROM sessions WHERE ${tc.sql} ORDER BY created_at DESC LIMIT $1`, [limit, tc.param]);
312
339
  if (result.rows.length === 0)
313
340
  return [];
314
341
  const ids = result.rows.map((r) => r.id);
342
+ const entTc = tenantClausePg(tenantId, 2);
315
343
  const entityResult = await this.pool.query(`SELECT session_id, entity_canonical
316
344
  FROM session_entities
317
- WHERE session_id = ANY($1)
318
- ORDER BY entity_canonical`, [ids]);
345
+ WHERE session_id = ANY($1) AND ${entTc.sql}
346
+ ORDER BY entity_canonical`, [ids, entTc.param]);
319
347
  const byId = new Map();
320
348
  for (const e of entityResult.rows) {
321
349
  const list = byId.get(e.session_id);
@@ -326,15 +354,17 @@ export class PgSessionStore {
326
354
  }
327
355
  return result.rows.map((r) => ({ ...r, entities: byId.get(r.id) ?? [] }));
328
356
  }
329
- async recentMarkers(limit) {
357
+ async recentMarkers(tenantId, limit) {
358
+ const tc = tenantClausePg(tenantId, 2, "s.tenant_id");
330
359
  const result = await this.pool.query(`SELECT m.session_id AS "sessionId", m.kind, m.text, s.label, s.created_at AS "createdAt"
331
360
  FROM markers m
332
361
  JOIN sessions s ON s.id = m.session_id
362
+ WHERE ${tc.sql}
333
363
  ORDER BY s.created_at DESC, m.position ASC
334
- LIMIT $1`, [limit]);
364
+ LIMIT $1`, [limit, tc.param]);
335
365
  return result.rows;
336
366
  }
337
- async insertSession(record, embedder = null, supersedes = null, factSink = null) {
367
+ async insertSession(tenantId, record, embedder = null, supersedes = null, factSink = null) {
338
368
  const client = await this.pool.connect();
339
369
  try {
340
370
  await client.query("BEGIN");
@@ -342,8 +372,10 @@ export class PgSessionStore {
342
372
  id, runtime, runtime_session_id, started_at, ended_at, duration_min,
343
373
  label, summary, body, status, transcript_kind, transcript_path,
344
374
  transcript_offset, transcript_length,
345
- classifier_provider, classifier_model, classifier_confidence
346
- ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)
375
+ classifier_provider, classifier_model, classifier_confidence,
376
+ scope, agent_persona, parent_session_id,
377
+ primary_model, total_tokens, skill, tenant_id
378
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24)
347
379
  ON CONFLICT (id) DO UPDATE SET
348
380
  ended_at = EXCLUDED.ended_at,
349
381
  duration_min = EXCLUDED.duration_min,
@@ -354,6 +386,12 @@ export class PgSessionStore {
354
386
  classifier_provider = EXCLUDED.classifier_provider,
355
387
  classifier_model = EXCLUDED.classifier_model,
356
388
  classifier_confidence = EXCLUDED.classifier_confidence,
389
+ scope = COALESCE(EXCLUDED.scope, sessions.scope),
390
+ agent_persona = COALESCE(EXCLUDED.agent_persona, sessions.agent_persona),
391
+ parent_session_id = COALESCE(EXCLUDED.parent_session_id, sessions.parent_session_id),
392
+ primary_model = COALESCE(EXCLUDED.primary_model, sessions.primary_model),
393
+ total_tokens = COALESCE(EXCLUDED.total_tokens, sessions.total_tokens),
394
+ skill = COALESCE(EXCLUDED.skill, sessions.skill),
357
395
  updated_at = NOW()`, [
358
396
  record.id, record.runtime, record.runtimeSessionId,
359
397
  record.startedAt, record.endedAt, record.durationMin,
@@ -364,6 +402,13 @@ export class PgSessionStore {
364
402
  record.classifier?.provider ?? null,
365
403
  record.classifier?.model ?? null,
366
404
  record.classifier?.confidence ?? null,
405
+ record.scope,
406
+ record.agentPersona ?? null,
407
+ record.parentSessionId ?? null,
408
+ record.primaryModel ?? null,
409
+ record.totalTokens ?? null,
410
+ record.skill ?? null,
411
+ tenantId,
367
412
  ]);
368
413
  await client.query("DELETE FROM markers WHERE session_id = $1", [record.id]);
369
414
  for (let i = 0; i < record.decisions.length; i++) {
@@ -379,36 +424,43 @@ export class PgSessionStore {
379
424
  const rawNewEntities = [...new Set(record.entities.map((e) => e.trim()).filter(Boolean))];
380
425
  // Resolve each extracted entity through entity_variants so merged surface
381
426
  // forms bind to the canonical instead of resurrecting the retired source.
382
- const variantRes = await client.query(`SELECT variant, canonical FROM entity_variants WHERE variant = ANY($1)`, [rawNewEntities]);
427
+ const variantTc = tenantClausePg(tenantId, 1);
428
+ const variantRes = await client.query(`SELECT variant, canonical FROM entity_variants WHERE ${variantTc.sql} AND variant = ANY($2)`, [variantTc.param, rawNewEntities]);
383
429
  const variantMap = new Map(variantRes.rows.map((r) => [r.variant, r.canonical]));
384
430
  const newEntities = rawNewEntities.map((name) => variantMap.get(name) ?? name);
385
- const oldRes = await client.query("SELECT entity_canonical FROM session_entities WHERE session_id = $1", [record.id]);
431
+ const oldTc = tenantClausePg(tenantId, 1);
432
+ const oldRes = await client.query(`SELECT entity_canonical FROM session_entities WHERE ${oldTc.sql} AND session_id = $2`, [oldTc.param, record.id]);
386
433
  const oldEntities = new Set(oldRes.rows.map((r) => r.entity_canonical));
387
- await client.query("DELETE FROM session_entities WHERE session_id = $1", [record.id]);
434
+ const deleteTc = tenantClausePg(tenantId, 1);
435
+ await client.query(`DELETE FROM session_entities WHERE ${deleteTc.sql} AND session_id = $2`, [deleteTc.param, record.id]);
388
436
  for (const name of newEntities) {
389
- await client.query(`INSERT INTO entities (canonical, type, status, source, first_seen_session, last_seen_session, session_count)
390
- VALUES ($1, 'candidate', 'candidate', 'auto-detected', $2, $2, 0)
391
- ON CONFLICT (canonical) DO NOTHING`, [name, record.id]);
437
+ const insertTc = tenantClausePg(tenantId, 1);
438
+ await client.query(`INSERT INTO entities (tenant_id, canonical, type, status, source, first_seen_session, last_seen_session, session_count)
439
+ VALUES ($1, $2, 'candidate', 'candidate', 'auto-detected', $3, $3, 0)
440
+ ON CONFLICT (tenant_id, canonical) DO NOTHING`, [insertTc.param, name, record.id]);
392
441
  // Update last_seen for entities newly added to this session; matches prior touch semantics.
393
442
  if (!oldEntities.has(name)) {
394
- await client.query("UPDATE entities SET last_seen_session = $1, updated_at = NOW() WHERE canonical = $2", [record.id, name]);
443
+ const touchTc = tenantClausePg(tenantId, 2);
444
+ await client.query(`UPDATE entities SET last_seen_session = $1, updated_at = NOW() WHERE ${touchTc.sql} AND canonical = $3`, [record.id, touchTc.param, name]);
395
445
  }
396
- await client.query("INSERT INTO session_entities (session_id, entity_canonical) VALUES ($1, $2)", [record.id, name]);
446
+ await client.query("INSERT INTO session_entities (tenant_id, session_id, entity_canonical) VALUES ($1, $2, $3)", [tenantId, record.id, name]);
397
447
  }
398
448
  // Recompute session_count exactly for every entity in (old union new) so
399
449
  // counts reflect reality regardless of prior drift.
400
450
  const allTouched = [...new Set([...oldEntities, ...newEntities])];
401
451
  for (const name of allTouched) {
402
- await client.query("UPDATE entities SET session_count = (SELECT COUNT(*) FROM session_entities WHERE entity_canonical = $1), updated_at = NOW() WHERE canonical = $1", [name]);
452
+ const recomputeTc = tenantClausePg(tenantId, 2);
453
+ await client.query(`UPDATE entities SET session_count = (SELECT COUNT(*) FROM session_entities WHERE ${recomputeTc.sql} AND entity_canonical = $1), updated_at = NOW() WHERE ${recomputeTc.sql} AND canonical = $1`, [name, recomputeTc.param]);
403
454
  }
404
455
  if (supersedes && supersedes.priorSessionId !== record.id) {
405
456
  const predecessorStatus = supersedes.kind === "replaces" ? "replaced" : "superseded";
406
457
  await client.query(`INSERT INTO session_edges (from_session, to_session, kind)
407
458
  VALUES ($1, $2, $3) ON CONFLICT DO NOTHING`, [record.id, supersedes.priorSessionId, supersedes.kind]);
408
- await client.query("UPDATE sessions SET status = $1, updated_at = NOW() WHERE id = $2", [predecessorStatus, supersedes.priorSessionId]);
459
+ const predStatusTc = tenantClausePg(tenantId, 3);
460
+ await client.query(`UPDATE sessions SET status = $1, updated_at = NOW() WHERE id = $2 AND ${predStatusTc.sql}`, [predecessorStatus, supersedes.priorSessionId, predStatusTc.param]);
409
461
  }
410
462
  else {
411
- const priorId = await findContinuesPredecessorPg(client, record.id, record.entities);
463
+ const priorId = await findContinuesPredecessorPg(client, tenantId, record.id, record.entities);
412
464
  if (priorId !== null) {
413
465
  await client.query(`INSERT INTO session_edges (from_session, to_session, kind)
414
466
  VALUES ($1, $2, 'continues') ON CONFLICT DO NOTHING`, [record.id, priorId]);
@@ -416,7 +468,7 @@ export class PgSessionStore {
416
468
  }
417
469
  // Atomic session+facts ingest on the session's own client. Single source of truth: pg-fact-ingest.ts.
418
470
  if (factSink !== null) {
419
- await ingestSessionFactsOnClient(client, record.id, factSink.facts);
471
+ await ingestSessionFactsOnClient(client, tenantId, record.id, factSink.facts, record.scope);
420
472
  }
421
473
  await client.query("COMMIT");
422
474
  }
@@ -443,6 +495,7 @@ export class PgSessionStore {
443
495
  await this.pool.query("INSERT INTO session_chunk_map (chunk_id, session_id, chunk_idx) VALUES ($1, $2, $3)", [chunkId, record.id, chunkIdx]);
444
496
  }
445
497
  catch (err) {
498
+ recordEmbedFailure("chunk");
446
499
  process.stderr.write(`[nlm] embedding chunk failed session=${record.id} chunk=${chunkIdx}: ${String(err)}\n`);
447
500
  }
448
501
  }
@@ -456,27 +509,30 @@ export class PgSessionStore {
456
509
  continue;
457
510
  try {
458
511
  const { vector } = await embedder.embed(factText, "document");
459
- await factSink.factStore.upsertEmbedding(fact.id, vector);
512
+ await factSink.factStore.upsertEmbedding(tenantId, fact.id, vector);
460
513
  }
461
- catch {
462
- // Tolerated; see comment above.
514
+ catch (err) {
515
+ // Tolerated; the fact row stays current, semantic recall misses
516
+ // it until a future re-ingest.
517
+ recordEmbedFailure("fact");
518
+ process.stderr.write(`[nlm] embedding fact failed fact=${fact.id}: ${String(err)}\n`);
463
519
  }
464
520
  }
465
521
  }
466
522
  }
467
523
  }
468
- async insertSessionForTest(session) {
524
+ async insertSessionForTest(session, tenantId = DEFAULT_TEAM_ID) {
469
525
  const status = session.status === "idle" ? "active" : session.status;
470
526
  await this.pool.query(`INSERT INTO sessions (id, runtime, runtime_session_id, started_at, ended_at,
471
- duration_min, label, summary, body, status, transcript_kind, transcript_path)
472
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)`, [
527
+ duration_min, label, summary, body, status, transcript_kind, transcript_path, tenant_id)
528
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`, [
473
529
  session.id, session.runtime, session.runtimeSessionId, session.startedAt,
474
530
  session.endedAt, session.durationMin, session.label, session.summary,
475
- session.body, status, session.transcriptKind, session.transcriptPath,
531
+ session.body, status, session.transcriptKind, session.transcriptPath, tenantId,
476
532
  ]);
477
533
  for (const e of session.entities) {
478
- await this.pool.query("INSERT INTO entities (canonical, type, status) VALUES ($1, 'candidate', 'active') ON CONFLICT DO NOTHING", [e]);
479
- await this.pool.query("INSERT INTO session_entities (session_id, entity_canonical) VALUES ($1, $2) ON CONFLICT DO NOTHING", [session.id, e]);
534
+ await this.pool.query("INSERT INTO entities (tenant_id, canonical, type, status) VALUES ($1, $2, 'candidate', 'active') ON CONFLICT DO NOTHING", [tenantId, e]);
535
+ await this.pool.query("INSERT INTO session_entities (tenant_id, session_id, entity_canonical) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING", [tenantId, session.id, e]);
480
536
  }
481
537
  for (let i = 0; i < session.decisions.length; i++) {
482
538
  await this.pool.query("INSERT INTO markers (session_id, kind, text, position) VALUES ($1, 'decision', $2, $3)", [session.id, session.decisions[i], i]);
@@ -486,31 +542,41 @@ export class PgSessionStore {
486
542
  }
487
543
  }
488
544
  /** PG counterpart of SqliteSessionStore.listBackfillCandidates. */
489
- async listBackfillCandidates(filter) {
545
+ async listBackfillCandidates(tenantId, filter) {
490
546
  const params = [filter.cutoff];
491
547
  let fromClause = "";
492
548
  if (filter.from) {
493
549
  params.push(filter.from);
494
550
  fromClause = `AND s.id > $${params.length}`;
495
551
  }
552
+ // facts.tenant_id is always stamped to match its source_session_id's
553
+ // session tenant (every write site stamps both together), so scoping by
554
+ // source_session_id = s.id against an already tenant-filtered `s` is
555
+ // already sufficient. The f.tenant_id = s.tenant_id condition below is
556
+ // defense in depth (guard test C4 permits column-to-column tenant
557
+ // equality as a join form).
496
558
  const existingFactsClause = filter.reprocess
497
559
  ? ""
498
- : "AND NOT EXISTS (SELECT 1 FROM facts f WHERE f.source_session_id = s.id)";
560
+ : "AND NOT EXISTS (SELECT 1 FROM facts f WHERE f.source_session_id = s.id AND f.tenant_id = s.tenant_id)";
561
+ const tc = tenantClausePg(tenantId, params.length + 1, "s.tenant_id");
562
+ params.push(tc.param);
499
563
  const result = await this.pool.query(`SELECT s.id, s.started_at, s.body FROM sessions s
500
564
  WHERE s.started_at < $1 AND s.body IS NOT NULL AND length(s.body) > 0
501
565
  ${existingFactsClause}
502
566
  ${fromClause}
567
+ AND ${tc.sql}
503
568
  ORDER BY s.started_at ASC, s.id ASC`, params);
504
569
  return result.rows.map((r) => ({ id: r.id, startedAt: r.started_at, body: r.body }));
505
570
  }
506
571
  /** PG counterpart of SqliteSessionStore.insertFactsForSession — writes facts
507
572
  * for an EXISTING session row (deterministic supersedence + best-effort
508
573
  * embeddings) in one transaction. Used by the fact backfill. */
509
- async insertFactsForSession(sessionId, factStore, facts, embedder = null) {
574
+ async insertFactsForSession(tenantId, sessionId, factStore, facts, embedder = null) {
575
+ const sessionScope = await this.getSessionScopeById(tenantId, sessionId);
510
576
  const client = await this.pool.connect();
511
577
  try {
512
578
  await client.query("BEGIN");
513
- await ingestSessionFactsOnClient(client, sessionId, facts);
579
+ await ingestSessionFactsOnClient(client, tenantId, sessionId, facts, sessionScope);
514
580
  await client.query("COMMIT");
515
581
  }
516
582
  catch (err) {
@@ -527,7 +593,7 @@ export class PgSessionStore {
527
593
  continue;
528
594
  try {
529
595
  const { vector } = await embedder.embed(factText, "document");
530
- await factStore.upsertEmbedding(fact.id, vector);
596
+ await factStore.upsertEmbedding(tenantId, fact.id, vector);
531
597
  }
532
598
  catch {
533
599
  // Best-effort; a per-fact embed failure leaves the fact current but
@@ -536,12 +602,13 @@ export class PgSessionStore {
536
602
  }
537
603
  }
538
604
  }
539
- async loadEntities(ids) {
605
+ async loadEntities(tenantId, ids) {
540
606
  if (ids.length === 0)
541
607
  return new Map();
542
608
  const placeholders = ids.map((_, i) => `$${i + 1}`).join(",");
609
+ const tc = tenantClausePg(tenantId, ids.length + 1);
543
610
  const result = await this.pool.query(`SELECT session_id, entity_canonical FROM session_entities
544
- WHERE session_id IN (${placeholders}) ORDER BY session_id`, [...ids]);
611
+ WHERE session_id IN (${placeholders}) AND ${tc.sql} ORDER BY session_id`, [...ids, tc.param]);
545
612
  const out = new Map();
546
613
  for (const r of result.rows) {
547
614
  const list = out.get(r.session_id);
@@ -637,6 +704,11 @@ function rowToSession(row, entitiesById, markersById, overlay, edges) {
637
704
  classifierProvider: row.classifier_provider ?? null,
638
705
  classifierModel: row.classifier_model ?? null,
639
706
  classifierConfidence: row.classifier_confidence ?? null,
707
+ agentPersona: row.agent_persona ?? null,
708
+ parentSessionId: row.parent_session_id ?? null,
709
+ primaryModel: row.primary_model ?? null,
710
+ totalTokens: row.total_tokens ?? null,
711
+ skill: row.skill ?? null,
640
712
  };
641
713
  }
642
714
  //# sourceMappingURL=pg-session-store.js.map