nlm-memory 0.21.0 → 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 (413) hide show
  1. package/README.md +67 -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 +34 -7
  5. package/dist/cli/digest.js.map +1 -1
  6. package/dist/cli/nlm.d.ts +14 -0
  7. package/dist/cli/nlm.js +450 -65
  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-coverage.js +4 -1
  13. package/dist/cli/scope-coverage.js.map +1 -1
  14. package/dist/cli/supersede.js +8 -7
  15. package/dist/cli/supersede.js.map +1 -1
  16. package/dist/core/actions/actions-log.d.ts +65 -8
  17. package/dist/core/actions/actions-log.js +134 -17
  18. package/dist/core/actions/actions-log.js.map +1 -1
  19. package/dist/core/actions/overlay.d.ts +11 -2
  20. package/dist/core/actions/overlay.js +18 -6
  21. package/dist/core/actions/overlay.js.map +1 -1
  22. package/dist/core/adapters/from-source.js +11 -3
  23. package/dist/core/adapters/from-source.js.map +1 -1
  24. package/dist/core/adapters/fs-wiki-writer.d.ts +44 -0
  25. package/dist/core/adapters/fs-wiki-writer.js +132 -0
  26. package/dist/core/adapters/fs-wiki-writer.js.map +1 -0
  27. package/dist/core/adapters/hermes-agent.d.ts +15 -2
  28. package/dist/core/adapters/hermes-agent.js +57 -11
  29. package/dist/core/adapters/hermes-agent.js.map +1 -1
  30. package/dist/core/adapters/memory-wiki-writer.d.ts +8 -0
  31. package/dist/core/adapters/memory-wiki-writer.js +16 -0
  32. package/dist/core/adapters/memory-wiki-writer.js.map +1 -0
  33. package/dist/core/adapters/muse.d.ts +51 -0
  34. package/dist/core/adapters/muse.js +271 -0
  35. package/dist/core/adapters/muse.js.map +1 -0
  36. package/dist/core/adapters/opencode.d.ts +11 -4
  37. package/dist/core/adapters/opencode.js +41 -7
  38. package/dist/core/adapters/opencode.js.map +1 -1
  39. package/dist/core/alerts/alert-state.d.ts +17 -0
  40. package/dist/core/alerts/alert-state.js +45 -0
  41. package/dist/core/alerts/alert-state.js.map +1 -0
  42. package/dist/core/alerts/check-and-alert.d.ts +28 -0
  43. package/dist/core/alerts/check-and-alert.js +53 -0
  44. package/dist/core/alerts/check-and-alert.js.map +1 -0
  45. package/dist/core/alerts/drift-transition.d.ts +27 -0
  46. package/dist/core/alerts/drift-transition.js +56 -0
  47. package/dist/core/alerts/drift-transition.js.map +1 -0
  48. package/dist/core/alerts/embedder-transition.d.ts +23 -0
  49. package/dist/core/alerts/embedder-transition.js +40 -0
  50. package/dist/core/alerts/embedder-transition.js.map +1 -0
  51. package/dist/core/alerts/fire-alert.d.ts +24 -0
  52. package/dist/core/alerts/fire-alert.js +72 -0
  53. package/dist/core/alerts/fire-alert.js.map +1 -0
  54. package/dist/core/alerts/job-alert.d.ts +12 -0
  55. package/dist/core/alerts/job-alert.js +37 -0
  56. package/dist/core/alerts/job-alert.js.map +1 -0
  57. package/dist/core/alerts/types.d.ts +104 -0
  58. package/dist/core/alerts/types.js +11 -0
  59. package/dist/core/alerts/types.js.map +1 -0
  60. package/dist/core/alerts/wiki-alert.d.ts +15 -0
  61. package/dist/core/alerts/wiki-alert.js +31 -0
  62. package/dist/core/alerts/wiki-alert.js.map +1 -0
  63. package/dist/core/classifier/prompt.d.ts +13 -0
  64. package/dist/core/classifier/prompt.js +52 -2
  65. package/dist/core/classifier/prompt.js.map +1 -1
  66. package/dist/core/dataset/build-dataset.d.ts +16 -1
  67. package/dist/core/dataset/build-dataset.js +45 -13
  68. package/dist/core/dataset/build-dataset.js.map +1 -1
  69. package/dist/core/digest/compose.d.ts +8 -0
  70. package/dist/core/digest/compose.js +54 -0
  71. package/dist/core/digest/compose.js.map +1 -1
  72. package/dist/core/digest/hook-injection.d.ts +1 -1
  73. package/dist/core/digest/hook-injection.js +9 -1
  74. package/dist/core/digest/hook-injection.js.map +1 -1
  75. package/dist/core/digest/hook-version-parity.d.ts +31 -0
  76. package/dist/core/digest/hook-version-parity.js +56 -0
  77. package/dist/core/digest/hook-version-parity.js.map +1 -0
  78. package/dist/core/embedding/pg-embed-backfill.js +1 -1
  79. package/dist/core/embedding/pg-embed-backfill.js.map +1 -1
  80. package/dist/core/exemplars/capture-from-session.d.ts +1 -1
  81. package/dist/core/exemplars/capture-from-session.js +3 -3
  82. package/dist/core/exemplars/capture-from-session.js.map +1 -1
  83. package/dist/core/exemplars/embed-backfill.d.ts +9 -0
  84. package/dist/core/exemplars/embed-backfill.js +9 -1
  85. package/dist/core/exemplars/embed-backfill.js.map +1 -1
  86. package/dist/core/exemplars/recall-code.d.ts +1 -1
  87. package/dist/core/exemplars/recall-code.js +2 -2
  88. package/dist/core/exemplars/recall-code.js.map +1 -1
  89. package/dist/core/facts/backfill-facts.d.ts +1 -1
  90. package/dist/core/facts/backfill-facts.js +4 -4
  91. package/dist/core/facts/backfill-facts.js.map +1 -1
  92. package/dist/core/facts/embed-backfill.d.ts +54 -0
  93. package/dist/core/facts/embed-backfill.js +109 -0
  94. package/dist/core/facts/embed-backfill.js.map +1 -0
  95. package/dist/core/facts/reconcile-facts.d.ts +42 -0
  96. package/dist/core/facts/reconcile-facts.js +133 -0
  97. package/dist/core/facts/reconcile-facts.js.map +1 -0
  98. package/dist/core/health/embed-failure-state.d.ts +4 -0
  99. package/dist/core/health/embed-failure-state.js +12 -0
  100. package/dist/core/health/embed-failure-state.js.map +1 -0
  101. package/dist/core/health/warmup-retry.d.ts +27 -0
  102. package/dist/core/health/warmup-retry.js +53 -0
  103. package/dist/core/health/warmup-retry.js.map +1 -0
  104. package/dist/core/health/warmup-state.d.ts +15 -0
  105. package/dist/core/health/warmup-state.js +15 -1
  106. package/dist/core/health/warmup-state.js.map +1 -1
  107. package/dist/core/hook/cite-memo.d.ts +11 -7
  108. package/dist/core/hook/cite-memo.js +25 -17
  109. package/dist/core/hook/cite-memo.js.map +1 -1
  110. package/dist/core/hook/hook-log.d.ts +13 -2
  111. package/dist/core/hook/hook-log.js +16 -8
  112. package/dist/core/hook/hook-log.js.map +1 -1
  113. package/dist/core/hook/memo.d.ts +10 -6
  114. package/dist/core/hook/memo.js +25 -17
  115. package/dist/core/hook/memo.js.map +1 -1
  116. package/dist/core/hook/pointer-block.d.ts +8 -1
  117. package/dist/core/hook/pointer-block.js +17 -4
  118. package/dist/core/hook/pointer-block.js.map +1 -1
  119. package/dist/core/hook/resolve-conversation-by-query.d.ts +6 -5
  120. package/dist/core/hook/resolve-conversation-by-query.js +24 -12
  121. package/dist/core/hook/resolve-conversation-by-query.js.map +1 -1
  122. package/dist/core/ingest/ingest-session.d.ts +1 -1
  123. package/dist/core/ingest/ingest-session.js +3 -3
  124. package/dist/core/ingest/ingest-session.js.map +1 -1
  125. package/dist/core/ingest/reclassify-oversized.d.ts +1 -1
  126. package/dist/core/ingest/reclassify-oversized.js +2 -2
  127. package/dist/core/ingest/reclassify-oversized.js.map +1 -1
  128. package/dist/core/ingest/reprocess.d.ts +1 -1
  129. package/dist/core/ingest/reprocess.js +2 -2
  130. package/dist/core/ingest/reprocess.js.map +1 -1
  131. package/dist/core/jobs/job-supervisor.d.ts +168 -0
  132. package/dist/core/jobs/job-supervisor.js +335 -0
  133. package/dist/core/jobs/job-supervisor.js.map +1 -0
  134. package/dist/core/learner/model.d.ts +63 -0
  135. package/dist/core/learner/model.js +30 -0
  136. package/dist/core/learner/model.js.map +1 -0
  137. package/dist/core/learner/service.d.ts +42 -0
  138. package/dist/core/learner/service.js +74 -0
  139. package/dist/core/learner/service.js.map +1 -0
  140. package/dist/core/learner/summary.d.ts +28 -0
  141. package/dist/core/learner/summary.js +70 -0
  142. package/dist/core/learner/summary.js.map +1 -0
  143. package/dist/core/learner/validate.d.ts +18 -0
  144. package/dist/core/learner/validate.js +95 -0
  145. package/dist/core/learner/validate.js.map +1 -0
  146. package/dist/core/metrics/re-derivation.d.ts +31 -2
  147. package/dist/core/metrics/re-derivation.js +36 -4
  148. package/dist/core/metrics/re-derivation.js.map +1 -1
  149. package/dist/core/outcome/coverage.d.ts +1 -1
  150. package/dist/core/outcome/coverage.js +2 -2
  151. package/dist/core/outcome/coverage.js.map +1 -1
  152. package/dist/core/outcome/rollup.d.ts +1 -1
  153. package/dist/core/outcome/rollup.js +4 -4
  154. package/dist/core/outcome/rollup.js.map +1 -1
  155. package/dist/core/providers/provider-registry.d.ts +40 -23
  156. package/dist/core/providers/provider-registry.js +99 -81
  157. package/dist/core/providers/provider-registry.js.map +1 -1
  158. package/dist/core/recall/citation-log.d.ts +10 -5
  159. package/dist/core/recall/citation-log.js +21 -10
  160. package/dist/core/recall/citation-log.js.map +1 -1
  161. package/dist/core/recall/hook-recall-log.d.ts +5 -2
  162. package/dist/core/recall/hook-recall-log.js +12 -6
  163. package/dist/core/recall/hook-recall-log.js.map +1 -1
  164. package/dist/core/recall/miss-log.d.ts +7 -4
  165. package/dist/core/recall/miss-log.js +18 -11
  166. package/dist/core/recall/miss-log.js.map +1 -1
  167. package/dist/core/recall/query-log.d.ts +8 -5
  168. package/dist/core/recall/query-log.js +16 -9
  169. package/dist/core/recall/query-log.js.map +1 -1
  170. package/dist/core/recall/recall-service.d.ts +10 -1
  171. package/dist/core/recall/recall-service.js +31 -12
  172. package/dist/core/recall/recall-service.js.map +1 -1
  173. package/dist/core/recall/recent-log.d.ts +6 -1
  174. package/dist/core/recall/recent-log.js +13 -5
  175. package/dist/core/recall/recent-log.js.map +1 -1
  176. package/dist/core/recall/related-concepts.d.ts +29 -0
  177. package/dist/core/recall/related-concepts.js +67 -0
  178. package/dist/core/recall/related-concepts.js.map +1 -0
  179. package/dist/core/recall/related-exemplars.d.ts +1 -1
  180. package/dist/core/recall/related-exemplars.js +2 -2
  181. package/dist/core/recall/related-exemplars.js.map +1 -1
  182. package/dist/core/recall/related-facts.d.ts +1 -1
  183. package/dist/core/recall/related-facts.js +3 -3
  184. package/dist/core/recall/related-facts.js.map +1 -1
  185. package/dist/core/recall-facts/fact-query-log.d.ts +8 -4
  186. package/dist/core/recall-facts/fact-query-log.js +16 -8
  187. package/dist/core/recall-facts/fact-query-log.js.map +1 -1
  188. package/dist/core/recall-facts/fact-recall-service.d.ts +2 -2
  189. package/dist/core/recall-facts/fact-recall-service.js +12 -12
  190. package/dist/core/recall-facts/fact-recall-service.js.map +1 -1
  191. package/dist/core/scheduler/scan-once.js +15 -14
  192. package/dist/core/scheduler/scan-once.js.map +1 -1
  193. package/dist/core/scheduler/scheduler.d.ts +3 -0
  194. package/dist/core/scheduler/scheduler.js +15 -11
  195. package/dist/core/scheduler/scheduler.js.map +1 -1
  196. package/dist/core/signals/failure-mode-recall.d.ts +1 -1
  197. package/dist/core/signals/failure-mode-recall.js +2 -2
  198. package/dist/core/signals/failure-mode-recall.js.map +1 -1
  199. package/dist/core/signals/stamp-scope.d.ts +2 -2
  200. package/dist/core/signals/stamp-scope.js +2 -2
  201. package/dist/core/signals/stamp-scope.js.map +1 -1
  202. package/dist/core/sources/source-registry.d.ts +49 -28
  203. package/dist/core/sources/source-registry.js +113 -70
  204. package/dist/core/sources/source-registry.js.map +1 -1
  205. package/dist/core/storage/pg-code-exemplar-store.d.ts +19 -10
  206. package/dist/core/storage/pg-code-exemplar-store.js +58 -27
  207. package/dist/core/storage/pg-code-exemplar-store.js.map +1 -1
  208. package/dist/core/storage/pg-entity-store.d.ts +6 -1
  209. package/dist/core/storage/pg-entity-store.js +33 -18
  210. package/dist/core/storage/pg-entity-store.js.map +1 -1
  211. package/dist/core/storage/pg-fact-ingest.d.ts +1 -1
  212. package/dist/core/storage/pg-fact-ingest.js +10 -7
  213. package/dist/core/storage/pg-fact-ingest.js.map +1 -1
  214. package/dist/core/storage/pg-fact-store.d.ts +29 -17
  215. package/dist/core/storage/pg-fact-store.js +99 -46
  216. package/dist/core/storage/pg-fact-store.js.map +1 -1
  217. package/dist/core/storage/pg-learner-store.d.ts +27 -0
  218. package/dist/core/storage/pg-learner-store.js +174 -0
  219. package/dist/core/storage/pg-learner-store.js.map +1 -0
  220. package/dist/core/storage/pg-session-store.d.ts +24 -20
  221. package/dist/core/storage/pg-session-store.js +162 -90
  222. package/dist/core/storage/pg-session-store.js.map +1 -1
  223. package/dist/core/storage/pg-signal-store.d.ts +10 -5
  224. package/dist/core/storage/pg-signal-store.js +24 -12
  225. package/dist/core/storage/pg-signal-store.js.map +1 -1
  226. package/dist/core/storage/pg-storage.d.ts +4 -0
  227. package/dist/core/storage/pg-storage.js +6 -0
  228. package/dist/core/storage/pg-storage.js.map +1 -1
  229. package/dist/core/storage/pg-workstream-store.d.ts +16 -11
  230. package/dist/core/storage/pg-workstream-store.js +46 -29
  231. package/dist/core/storage/pg-workstream-store.js.map +1 -1
  232. package/dist/core/storage/sqlite-code-exemplar-store.d.ts +24 -10
  233. package/dist/core/storage/sqlite-code-exemplar-store.js +84 -40
  234. package/dist/core/storage/sqlite-code-exemplar-store.js.map +1 -1
  235. package/dist/core/storage/sqlite-entity-store.d.ts +8 -1
  236. package/dist/core/storage/sqlite-entity-store.js +46 -27
  237. package/dist/core/storage/sqlite-entity-store.js.map +1 -1
  238. package/dist/core/storage/sqlite-fact-store.d.ts +45 -19
  239. package/dist/core/storage/sqlite-fact-store.js +172 -64
  240. package/dist/core/storage/sqlite-fact-store.js.map +1 -1
  241. package/dist/core/storage/sqlite-learner-store.d.ts +33 -0
  242. package/dist/core/storage/sqlite-learner-store.js +178 -0
  243. package/dist/core/storage/sqlite-learner-store.js.map +1 -0
  244. package/dist/core/storage/sqlite-outcome-store.d.ts +37 -22
  245. package/dist/core/storage/sqlite-outcome-store.js +90 -37
  246. package/dist/core/storage/sqlite-outcome-store.js.map +1 -1
  247. package/dist/core/storage/sqlite-session-store.d.ts +29 -20
  248. package/dist/core/storage/sqlite-session-store.js +207 -121
  249. package/dist/core/storage/sqlite-session-store.js.map +1 -1
  250. package/dist/core/storage/sqlite-signal-store.d.ts +12 -5
  251. package/dist/core/storage/sqlite-signal-store.js +27 -15
  252. package/dist/core/storage/sqlite-signal-store.js.map +1 -1
  253. package/dist/core/storage/sqlite-storage.d.ts +4 -0
  254. package/dist/core/storage/sqlite-storage.js +10 -2
  255. package/dist/core/storage/sqlite-storage.js.map +1 -1
  256. package/dist/core/storage/sqlite-workstream-store.d.ts +16 -11
  257. package/dist/core/storage/sqlite-workstream-store.js +52 -31
  258. package/dist/core/storage/sqlite-workstream-store.js.map +1 -1
  259. package/dist/core/storage/supersedence-log.d.ts +10 -7
  260. package/dist/core/storage/supersedence-log.js +19 -14
  261. package/dist/core/storage/supersedence-log.js.map +1 -1
  262. package/dist/core/tenancy/default-team.d.ts +6 -0
  263. package/dist/core/tenancy/default-team.js +7 -0
  264. package/dist/core/tenancy/default-team.js.map +1 -0
  265. package/dist/core/tenancy/hosted-mode.d.ts +9 -0
  266. package/dist/core/tenancy/hosted-mode.js +12 -0
  267. package/dist/core/tenancy/hosted-mode.js.map +1 -0
  268. package/dist/core/tenancy/team-auth.d.ts +16 -0
  269. package/dist/core/tenancy/team-auth.js +22 -0
  270. package/dist/core/tenancy/team-auth.js.map +1 -0
  271. package/dist/core/tenancy/team-token-store.d.ts +45 -0
  272. package/dist/core/tenancy/team-token-store.js +52 -0
  273. package/dist/core/tenancy/team-token-store.js.map +1 -0
  274. package/dist/core/tenancy/tenant-clause.d.ts +37 -0
  275. package/dist/core/tenancy/tenant-clause.js +11 -0
  276. package/dist/core/tenancy/tenant-clause.js.map +1 -0
  277. package/dist/core/tenancy/tenant-state-path.d.ts +28 -0
  278. package/dist/core/tenancy/tenant-state-path.js +49 -0
  279. package/dist/core/tenancy/tenant-state-path.js.map +1 -0
  280. package/dist/core/wiki/project.d.ts +17 -0
  281. package/dist/core/wiki/project.js +37 -0
  282. package/dist/core/wiki/project.js.map +1 -0
  283. package/dist/core/wiki/reconcile.d.ts +15 -0
  284. package/dist/core/wiki/reconcile.js +14 -0
  285. package/dist/core/wiki/reconcile.js.map +1 -0
  286. package/dist/core/wiki/render.d.ts +5 -0
  287. package/dist/core/wiki/render.js +84 -0
  288. package/dist/core/wiki/render.js.map +1 -0
  289. package/dist/core/wiki/rollup.d.ts +28 -0
  290. package/dist/core/wiki/rollup.js +91 -0
  291. package/dist/core/wiki/rollup.js.map +1 -0
  292. package/dist/core/wiki/select.d.ts +8 -0
  293. package/dist/core/wiki/select.js +7 -0
  294. package/dist/core/wiki/select.js.map +1 -0
  295. package/dist/core/wiki/slug.d.ts +36 -0
  296. package/dist/core/wiki/slug.js +71 -0
  297. package/dist/core/wiki/slug.js.map +1 -0
  298. package/dist/core/wiki/types.d.ts +37 -0
  299. package/dist/core/wiki/types.js +2 -0
  300. package/dist/core/wiki/types.js.map +1 -0
  301. package/dist/core/work-digest/build-work-digest.d.ts +1 -1
  302. package/dist/core/work-digest/build-work-digest.js +3 -3
  303. package/dist/core/work-digest/build-work-digest.js.map +1 -1
  304. package/dist/core/workstream/bind.d.ts +1 -1
  305. package/dist/core/workstream/bind.js +10 -10
  306. package/dist/core/workstream/bind.js.map +1 -1
  307. package/dist/core/workstream/rollup.d.ts +1 -1
  308. package/dist/core/workstream/rollup.js +6 -6
  309. package/dist/core/workstream/rollup.js.map +1 -1
  310. package/dist/hook/hook-helpers.d.ts +14 -0
  311. package/dist/hook/hook-helpers.js +29 -2
  312. package/dist/hook/hook-helpers.js.map +1 -1
  313. package/dist/hook/pre-compact-hook.js +6 -4
  314. package/dist/hook/pre-compact-hook.js.map +1 -1
  315. package/dist/hook/prompt-recall-hook.d.ts +5 -2
  316. package/dist/hook/prompt-recall-hook.js +28 -17
  317. package/dist/hook/prompt-recall-hook.js.map +1 -1
  318. package/dist/hook/recall-over-http.d.ts +27 -3
  319. package/dist/hook/recall-over-http.js +44 -11
  320. package/dist/hook/recall-over-http.js.map +1 -1
  321. package/dist/hook/session-end-hook.js +7 -5
  322. package/dist/hook/session-end-hook.js.map +1 -1
  323. package/dist/hook/session-start-hook.d.ts +14 -1
  324. package/dist/hook/session-start-hook.js +55 -15
  325. package/dist/hook/session-start-hook.js.map +1 -1
  326. package/dist/hook/stop-hook.js +9 -7
  327. package/dist/hook/stop-hook.js.map +1 -1
  328. package/dist/http/app.d.ts +41 -3
  329. package/dist/http/app.js +355 -96
  330. package/dist/http/app.js.map +1 -1
  331. package/dist/install/claude-code.d.ts +17 -0
  332. package/dist/install/claude-code.js +44 -5
  333. package/dist/install/claude-code.js.map +1 -1
  334. package/dist/install/codex.d.ts +23 -2
  335. package/dist/install/codex.js +111 -14
  336. package/dist/install/codex.js.map +1 -1
  337. package/dist/install/cursor.d.ts +2 -2
  338. package/dist/install/cursor.js +7 -7
  339. package/dist/install/cursor.js.map +1 -1
  340. package/dist/install/hermes.d.ts +8 -0
  341. package/dist/install/hermes.js +28 -5
  342. package/dist/install/hermes.js.map +1 -1
  343. package/dist/install/local-source.d.ts +20 -0
  344. package/dist/install/local-source.js +28 -0
  345. package/dist/install/local-source.js.map +1 -0
  346. package/dist/install/mcp-block.d.ts +77 -0
  347. package/dist/install/mcp-block.js +99 -0
  348. package/dist/install/mcp-block.js.map +1 -0
  349. package/dist/install/pi.d.ts +26 -0
  350. package/dist/install/pi.js +43 -0
  351. package/dist/install/pi.js.map +1 -1
  352. package/dist/install/setup.js +6 -0
  353. package/dist/install/setup.js.map +1 -1
  354. package/dist/install/windsurf.d.ts +2 -2
  355. package/dist/install/windsurf.js +7 -7
  356. package/dist/install/windsurf.js.map +1 -1
  357. package/dist/llm/classifier-box.d.ts +5 -0
  358. package/dist/llm/classifier-box.js +5 -0
  359. package/dist/llm/classifier-box.js.map +1 -1
  360. package/dist/mcp/server.d.ts +73 -16
  361. package/dist/mcp/server.js +306 -83
  362. package/dist/mcp/server.js.map +1 -1
  363. package/dist/ports/clock.d.ts +12 -0
  364. package/dist/ports/clock.js +10 -0
  365. package/dist/ports/clock.js.map +1 -0
  366. package/dist/ports/code-exemplar-store.d.ts +13 -13
  367. package/dist/ports/entity-store.d.ts +5 -1
  368. package/dist/ports/fact-store.d.ts +0 -0
  369. package/dist/ports/fact-store.js +0 -0
  370. package/dist/ports/fact-store.js.map +1 -1
  371. package/dist/ports/learner-store.d.ts +80 -0
  372. package/dist/ports/learner-store.js +19 -0
  373. package/dist/ports/learner-store.js.map +1 -0
  374. package/dist/ports/outcome.d.ts +3 -3
  375. package/dist/ports/session-store.d.ts +20 -14
  376. package/dist/ports/session-store.js +5 -0
  377. package/dist/ports/session-store.js.map +1 -1
  378. package/dist/ports/signal-store.d.ts +12 -8
  379. package/dist/ports/spawn-child.d.ts +14 -0
  380. package/dist/ports/spawn-child.js +10 -0
  381. package/dist/ports/spawn-child.js.map +1 -0
  382. package/dist/ports/storage.d.ts +6 -0
  383. package/dist/ports/transcript-adapter.d.ts +11 -0
  384. package/dist/ports/wiki-writer.d.ts +19 -0
  385. package/dist/ports/wiki-writer.js +12 -0
  386. package/dist/ports/wiki-writer.js.map +1 -0
  387. package/dist/ports/workstream-store.d.ts +19 -12
  388. package/dist/shared/types.d.ts +22 -0
  389. package/dist/shared/types.js.map +1 -1
  390. package/dist/shared/version.d.ts +9 -0
  391. package/dist/shared/version.js +11 -0
  392. package/dist/shared/version.js.map +1 -0
  393. package/dist/ui/assets/index-D6F3xAis.js +11 -0
  394. package/dist/ui/index.html +1 -1
  395. package/migrations/033_actions_kind_check.sql +58 -0
  396. package/migrations/034_tenancy_teams_and_stamps.sql +40 -0
  397. package/migrations/035_tenancy_entity_rekey.sql +75 -0
  398. package/migrations/036_tenant_name_uniqueness.sql +59 -0
  399. package/migrations/037_sessions_covering_index.sql +25 -0
  400. package/migrations/038_sources_muse.sql +33 -0
  401. package/migrations/039_learner_evidence.sql +51 -0
  402. package/migrations/pg/033_actions_kind_check.sql +17 -0
  403. package/migrations/pg/034_tenancy.sql +77 -0
  404. package/migrations/pg/035_tenant_name_uniqueness.sql +15 -0
  405. package/migrations/pg/036_sources_muse.sql +12 -0
  406. package/migrations/pg/037_learner_evidence.sql +55 -0
  407. package/nlm/index.js +246 -55
  408. package/package.json +10 -5
  409. package/plugin/.codex-plugin/plugin.json +2 -2
  410. package/plugin/scripts/prompt-recall-hook.mjs +244 -55
  411. package/plugin/scripts/session-start-hook.mjs +273 -53
  412. package/plugin/scripts/stop-hook.mjs +195 -37
  413. package/dist/ui/assets/index-CSaBtgXD.js +0 -11
@@ -0,0 +1,63 @@
1
+ export type EnrollmentStatus = "active" | "completed";
2
+ export type AttemptResult = "correct" | "partial" | "incorrect";
3
+ export type AttemptHelp = "none" | "hint" | "worked-example";
4
+ export type AttemptKind = "application" | "retrieval" | "transfer";
5
+ export interface Learner {
6
+ readonly id: string;
7
+ readonly label: string;
8
+ readonly profile: Record<string, unknown>;
9
+ readonly createdAt: string;
10
+ readonly updatedAt: string;
11
+ }
12
+ export interface Attempt {
13
+ readonly concept: string;
14
+ readonly task: string;
15
+ readonly response: string;
16
+ readonly result: AttemptResult;
17
+ readonly help: AttemptHelp;
18
+ readonly kind: AttemptKind;
19
+ }
20
+ export interface LearningEvent {
21
+ readonly id: string;
22
+ readonly date: string;
23
+ readonly covered: ReadonlyArray<string>;
24
+ readonly attempts: ReadonlyArray<Attempt>;
25
+ readonly interpretation: string;
26
+ readonly nextStep: string;
27
+ readonly createdAt: string;
28
+ }
29
+ export interface Enrollment {
30
+ readonly id: string;
31
+ readonly learnerId: string;
32
+ readonly courseId: string;
33
+ readonly workstreamId: string | null;
34
+ readonly planRef: string;
35
+ readonly planRevision: number;
36
+ readonly planFingerprint: string;
37
+ readonly status: EnrollmentStatus;
38
+ readonly completionHistory: ReadonlyArray<unknown>;
39
+ readonly createdAt: string;
40
+ readonly updatedAt: string;
41
+ }
42
+ export declare function makeEnrollmentId(): string;
43
+ /**
44
+ * Canonical JSON: object keys sorted recursively, arrays preserved. pg JSONB
45
+ * reorders object keys on write, so byte comparison of round-tripped JSON
46
+ * false-conflicts; canonical form is the only backend-agnostic equality.
47
+ */
48
+ export declare function canonicalJson(value: unknown): string;
49
+ export interface StoredEventContent {
50
+ readonly date: string;
51
+ readonly covered: ReadonlyArray<string>;
52
+ readonly attempts: ReadonlyArray<Attempt>;
53
+ readonly interpretation: string;
54
+ readonly nextStep: string;
55
+ }
56
+ /** Idempotent-record comparison: same event if all content fields match. */
57
+ export declare function learningEventContentEqual(input: {
58
+ date: string;
59
+ covered: ReadonlyArray<string>;
60
+ attempts: ReadonlyArray<Attempt>;
61
+ interpretation: string;
62
+ nextStep: string;
63
+ }, stored: StoredEventContent): boolean;
@@ -0,0 +1,30 @@
1
+ // src/core/learner/model.ts
2
+ import { randomUUID } from "node:crypto";
3
+ export function makeEnrollmentId() {
4
+ return `enr_${randomUUID()}`;
5
+ }
6
+ /**
7
+ * Canonical JSON: object keys sorted recursively, arrays preserved. pg JSONB
8
+ * reorders object keys on write, so byte comparison of round-tripped JSON
9
+ * false-conflicts; canonical form is the only backend-agnostic equality.
10
+ */
11
+ export function canonicalJson(value) {
12
+ if (Array.isArray(value))
13
+ return `[${value.map(canonicalJson).join(",")}]`;
14
+ if (value !== null && typeof value === "object") {
15
+ const entries = Object.entries(value)
16
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
17
+ .map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`);
18
+ return `{${entries.join(",")}}`;
19
+ }
20
+ return JSON.stringify(value) ?? "null";
21
+ }
22
+ /** Idempotent-record comparison: same event if all content fields match. */
23
+ export function learningEventContentEqual(input, stored) {
24
+ return (input.date === stored.date &&
25
+ canonicalJson(input.covered) === canonicalJson(stored.covered) &&
26
+ canonicalJson(input.attempts) === canonicalJson(stored.attempts) &&
27
+ input.interpretation === stored.interpretation &&
28
+ input.nextStep === stored.nextStep);
29
+ }
30
+ //# sourceMappingURL=model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/core/learner/model.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAiDzC,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,UAAU,EAAE,EAAE,CAAC;AAC/B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAC3E,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC;aAC7D,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aAChD,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IAClC,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC;AACzC,CAAC;AAUD,4EAA4E;AAC5E,MAAM,UAAU,yBAAyB,CACvC,KAAmI,EACnI,MAA0B;IAE1B,OAAO,CACL,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI;QAC1B,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC;QAC9D,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC;QAChE,KAAK,CAAC,cAAc,KAAK,MAAM,CAAC,cAAc;QAC9C,KAAK,CAAC,QAAQ,KAAK,MAAM,CAAC,QAAQ,CACnC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Learner service — orchestration over LearnerStore + WorkstreamStore.
3
+ *
4
+ * Owns validation (validate.ts), learner auto-init, workstream id-or-label
5
+ * resolution, and summary assembly. Idempotency, fingerprint matching, and
6
+ * tenant isolation stay in the store. The MCP layer (M3) calls these
7
+ * operations; unrecorded-session listing needs SessionStore and lands in M3.
8
+ */
9
+ import type { LearnerStore, LearningEventInput, RecordResult } from "../../ports/learner-store.js";
10
+ import type { WorkstreamStore } from "../../ports/workstream-store.js";
11
+ import type { Enrollment, EnrollmentStatus } from "./model.js";
12
+ import { type ConceptSummary } from "./summary.js";
13
+ export interface LearnerServiceDeps {
14
+ readonly learners: LearnerStore;
15
+ readonly workstreams: Pick<WorkstreamStore, "getById" | "findByNormalizedLabel">;
16
+ }
17
+ export interface EnrollRequest {
18
+ readonly learnerId: string;
19
+ readonly learnerLabel?: string;
20
+ readonly courseId: string;
21
+ /** Workstream id or label; resolved like recall_workstream. */
22
+ readonly workstream: string;
23
+ readonly planRef: string;
24
+ readonly planRevision: number;
25
+ readonly planFingerprint: string;
26
+ }
27
+ export interface CourseSummary {
28
+ readonly learnerId: string;
29
+ readonly courseId: string;
30
+ readonly status: EnrollmentStatus;
31
+ readonly workstreamId: string | null;
32
+ readonly planRevision: number;
33
+ readonly concepts: ReadonlyArray<ConceptSummary>;
34
+ /** Latest event's next step, or null when nothing is recorded yet. */
35
+ readonly nextStep: string | null;
36
+ readonly eventCount: number;
37
+ readonly lastEventDate: string | null;
38
+ }
39
+ export declare function enrollCourse(deps: LearnerServiceDeps, tenantId: string, req: EnrollRequest): Promise<Enrollment>;
40
+ export declare function recordLearningEvent(deps: LearnerServiceDeps, tenantId: string, learnerId: string, courseId: string, event: LearningEventInput): Promise<RecordResult>;
41
+ export declare function completeLearnerCourse(deps: LearnerServiceDeps, tenantId: string, learnerId: string, courseId: string): Promise<void>;
42
+ export declare function getCourseSummary(deps: LearnerServiceDeps, tenantId: string, learnerId: string, courseId: string): Promise<CourseSummary | null>;
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Learner service — orchestration over LearnerStore + WorkstreamStore.
3
+ *
4
+ * Owns validation (validate.ts), learner auto-init, workstream id-or-label
5
+ * resolution, and summary assembly. Idempotency, fingerprint matching, and
6
+ * tenant isolation stay in the store. The MCP layer (M3) calls these
7
+ * operations; unrecorded-session listing needs SessionStore and lands in M3.
8
+ */
9
+ import { normalizeLabel } from "../workstream/model.js";
10
+ import { assertValidEnrollmentInput, assertValidEventInput, assertValidLearnerInit, assertValidSlug, } from "./validate.js";
11
+ import { summarizeConcepts } from "./summary.js";
12
+ async function resolveWorkstreamId(deps, tenantId, idOrLabel) {
13
+ const trimmed = idOrLabel.trim();
14
+ if (!trimmed)
15
+ throw new Error("learner service: workstream id or label is required");
16
+ const found = (await deps.workstreams.getById(tenantId, trimmed)) ??
17
+ (await deps.workstreams.findByNormalizedLabel(tenantId, normalizeLabel(trimmed)));
18
+ if (!found)
19
+ throw new Error(`learner service: no workstream matches "${trimmed}"`);
20
+ return found.id;
21
+ }
22
+ export async function enrollCourse(deps, tenantId, req) {
23
+ assertValidSlug(req.learnerId, "enrollment.learnerId");
24
+ assertValidSlug(req.courseId, "enrollment.courseId");
25
+ const workstreamId = await resolveWorkstreamId(deps, tenantId, req.workstream);
26
+ const existing = await deps.learners.getLearner(tenantId, req.learnerId);
27
+ if (!existing) {
28
+ const init = { id: req.learnerId, label: req.learnerLabel ?? req.learnerId };
29
+ assertValidLearnerInit(init);
30
+ await deps.learners.initLearner(tenantId, init);
31
+ }
32
+ const input = {
33
+ learnerId: req.learnerId,
34
+ courseId: req.courseId,
35
+ workstreamId,
36
+ planRef: req.planRef,
37
+ planRevision: req.planRevision,
38
+ planFingerprint: req.planFingerprint,
39
+ };
40
+ assertValidEnrollmentInput(input);
41
+ return deps.learners.enroll(tenantId, input);
42
+ }
43
+ export async function recordLearningEvent(deps, tenantId, learnerId, courseId, event) {
44
+ assertValidSlug(learnerId, "enrollment.learnerId");
45
+ assertValidSlug(courseId, "enrollment.courseId");
46
+ assertValidEventInput(event);
47
+ return deps.learners.recordEvent(tenantId, learnerId, courseId, event);
48
+ }
49
+ export async function completeLearnerCourse(deps, tenantId, learnerId, courseId) {
50
+ assertValidSlug(learnerId, "enrollment.learnerId");
51
+ assertValidSlug(courseId, "enrollment.courseId");
52
+ return deps.learners.completeCourse(tenantId, learnerId, courseId);
53
+ }
54
+ export async function getCourseSummary(deps, tenantId, learnerId, courseId) {
55
+ assertValidSlug(learnerId, "enrollment.learnerId");
56
+ assertValidSlug(courseId, "enrollment.courseId");
57
+ const enrollment = await deps.learners.getEnrollment(tenantId, learnerId, courseId);
58
+ if (!enrollment)
59
+ return null;
60
+ const events = await deps.learners.listEvents(tenantId, learnerId, courseId);
61
+ const last = events.length > 0 ? events[events.length - 1] : null;
62
+ return {
63
+ learnerId,
64
+ courseId,
65
+ status: enrollment.status,
66
+ workstreamId: enrollment.workstreamId,
67
+ planRevision: enrollment.planRevision,
68
+ concepts: summarizeConcepts(events),
69
+ nextStep: last ? last.nextStep : null,
70
+ eventCount: events.length,
71
+ lastEventDate: last ? last.date : null,
72
+ };
73
+ }
74
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/core/learner/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Learner summary derivation — port of the Gnos deterministic summary rule
3
+ * (`skills/learner-tracking/references/evidence.md`, MIT, attribution kept).
4
+ *
5
+ * Per concept, from events in `listEvents` order ((date, id) ascending):
6
+ *
7
+ * - covered, never attempted: **exposed**
8
+ * - latest attempt assisted, partial, or incorrect: **practicing**
9
+ * - latest attempt correct + unassisted: **demonstrated**
10
+ * - latest success is retrieval/transfer on a strictly later date than an
11
+ * earlier unassisted success: **retained**
12
+ *
13
+ * Latest failure demotes back to practicing; earlier evidence is preserved in
14
+ * the events, not in the label. These are workflow labels, not calibrated
15
+ * scores — the summary never issues "mastered".
16
+ */
17
+ import type { LearningEvent } from "./model.js";
18
+ export type ConceptStatus = "exposed" | "practicing" | "demonstrated" | "retained";
19
+ export interface ConceptSummary {
20
+ readonly concept: string;
21
+ readonly status: ConceptStatus;
22
+ readonly attemptCount: number;
23
+ /** Latest attempt date, else the date the concept was last covered. */
24
+ readonly latestDate: string | null;
25
+ /** Latest attempt's task, or null when never attempted. */
26
+ readonly latestTask: string | null;
27
+ }
28
+ export declare function summarizeConcepts(events: ReadonlyArray<LearningEvent>): ReadonlyArray<ConceptSummary>;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Learner summary derivation — port of the Gnos deterministic summary rule
3
+ * (`skills/learner-tracking/references/evidence.md`, MIT, attribution kept).
4
+ *
5
+ * Per concept, from events in `listEvents` order ((date, id) ascending):
6
+ *
7
+ * - covered, never attempted: **exposed**
8
+ * - latest attempt assisted, partial, or incorrect: **practicing**
9
+ * - latest attempt correct + unassisted: **demonstrated**
10
+ * - latest success is retrieval/transfer on a strictly later date than an
11
+ * earlier unassisted success: **retained**
12
+ *
13
+ * Latest failure demotes back to practicing; earlier evidence is preserved in
14
+ * the events, not in the label. These are workflow labels, not calibrated
15
+ * scores — the summary never issues "mastered".
16
+ */
17
+ function isUnassistedSuccess(a) {
18
+ return a.result === "correct" && a.help === "none";
19
+ }
20
+ export function summarizeConcepts(events) {
21
+ const coveredOn = new Map();
22
+ const attemptsByConcept = new Map();
23
+ for (const event of events) {
24
+ for (const concept of event.covered)
25
+ coveredOn.set(concept, event.date);
26
+ for (const attempt of event.attempts) {
27
+ let list = attemptsByConcept.get(attempt.concept);
28
+ if (!list) {
29
+ list = [];
30
+ attemptsByConcept.set(attempt.concept, list);
31
+ }
32
+ list.push({ attempt, date: event.date });
33
+ if (!coveredOn.has(attempt.concept))
34
+ coveredOn.set(attempt.concept, event.date);
35
+ }
36
+ }
37
+ const concepts = [...new Set([...coveredOn.keys(), ...attemptsByConcept.keys()])].sort();
38
+ return concepts.map((concept) => {
39
+ const attempts = attemptsByConcept.get(concept) ?? [];
40
+ if (attempts.length === 0) {
41
+ return {
42
+ concept,
43
+ status: "exposed",
44
+ attemptCount: 0,
45
+ latestDate: coveredOn.get(concept) ?? null,
46
+ latestTask: null,
47
+ };
48
+ }
49
+ const latest = attempts[attempts.length - 1];
50
+ let status;
51
+ if (!isUnassistedSuccess(latest.attempt)) {
52
+ status = "practicing";
53
+ }
54
+ else if ((latest.attempt.kind === "retrieval" || latest.attempt.kind === "transfer") &&
55
+ attempts.some((a) => a.date < latest.date && isUnassistedSuccess(a.attempt))) {
56
+ status = "retained";
57
+ }
58
+ else {
59
+ status = "demonstrated";
60
+ }
61
+ return {
62
+ concept,
63
+ status,
64
+ attemptCount: attempts.length,
65
+ latestDate: latest.date,
66
+ latestTask: latest.attempt.task,
67
+ };
68
+ });
69
+ }
70
+ //# sourceMappingURL=summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"summary.js","sourceRoot":"","sources":["../../../src/core/learner/summary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAqBH,SAAS,mBAAmB,CAAC,CAAU;IACrC,OAAO,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAoC;IACpE,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC5C,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAA0B,CAAC;IAE5D,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,OAAO;YAAE,SAAS,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACxE,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACrC,IAAI,IAAI,GAAG,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAClD,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,IAAI,GAAG,EAAE,CAAC;gBACV,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YAC/C,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YACzC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC;gBAAE,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAClF,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,SAAS,CAAC,IAAI,EAAE,EAAE,GAAG,iBAAiB,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACzF,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE;QAC9B,MAAM,QAAQ,GAAG,iBAAiB,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QACtD,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO;gBACL,OAAO;gBACP,MAAM,EAAE,SAAkB;gBAC1B,YAAY,EAAE,CAAC;gBACf,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI;gBAC1C,UAAU,EAAE,IAAI;aACjB,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;QAC9C,IAAI,MAAqB,CAAC;QAC1B,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACzC,MAAM,GAAG,YAAY,CAAC;QACxB,CAAC;aAAM,IACL,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,WAAW,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,UAAU,CAAC;YAC3E,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,mBAAmB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,EAC5E,CAAC;YACD,MAAM,GAAG,UAAU,CAAC;QACtB,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,cAAc,CAAC;QAC1B,CAAC;QACD,OAAO;YACL,OAAO;YACP,MAAM;YACN,YAAY,EAAE,QAAQ,CAAC,MAAM;YAC7B,UAAU,EAAE,MAAM,CAAC,IAAI;YACvB,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI;SAChC,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Learner evidence validation — port of the Gnos evidence contract
3
+ * (`skills/learner-tracking/scripts/learner_state.py`, MIT, attribution kept).
4
+ *
5
+ * Shape rules only: slug/concept formats, required fields, enum membership,
6
+ * real calendar dates that are never in the future. Cross-row rules
7
+ * (fingerprint match, idempotent retry) live in the store; orchestration in
8
+ * service.ts. All failures throw `Error`; the MCP layer (M3) maps them to
9
+ * tool errors.
10
+ */
11
+ import type { EnrollmentInput, LearnerInit, LearningEventInput } from "../../ports/learner-store.js";
12
+ export declare function assertValidSlug(value: unknown, label: string): asserts value is string;
13
+ export declare function assertValidConceptId(value: unknown): asserts value is string;
14
+ export declare function assertNonempty(value: unknown, label: string): asserts value is string;
15
+ export declare function assertValidDate(value: unknown, label?: string): asserts value is string;
16
+ export declare function assertValidLearnerInit(input: LearnerInit): void;
17
+ export declare function assertValidEnrollmentInput(input: EnrollmentInput): void;
18
+ export declare function assertValidEventInput(event: LearningEventInput): void;
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Learner evidence validation — port of the Gnos evidence contract
3
+ * (`skills/learner-tracking/scripts/learner_state.py`, MIT, attribution kept).
4
+ *
5
+ * Shape rules only: slug/concept formats, required fields, enum membership,
6
+ * real calendar dates that are never in the future. Cross-row rules
7
+ * (fingerprint match, idempotent retry) live in the store; orchestration in
8
+ * service.ts. All failures throw `Error`; the MCP layer (M3) maps them to
9
+ * tool errors.
10
+ */
11
+ const SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
12
+ const CONCEPT_PATTERN = /^[a-z][a-z0-9-]*\.[a-z0-9]+(?:[.-][a-z0-9]+)*$/;
13
+ const DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
14
+ const ATTEMPT_RESULTS = ["correct", "partial", "incorrect"];
15
+ const ATTEMPT_HELPS = ["none", "hint", "worked-example"];
16
+ const ATTEMPT_KINDS = ["application", "retrieval", "transfer"];
17
+ export function assertValidSlug(value, label) {
18
+ if (typeof value !== "string" || value.length > 80 || !SLUG_PATTERN.test(value)) {
19
+ throw new Error(`learner validation: ${label} must be a lowercase slug of at most 80 characters`);
20
+ }
21
+ }
22
+ export function assertValidConceptId(value) {
23
+ if (typeof value !== "string" || !CONCEPT_PATTERN.test(value)) {
24
+ throw new Error(`learner validation: invalid concept ID: ${JSON.stringify(value)}`);
25
+ }
26
+ }
27
+ export function assertNonempty(value, label) {
28
+ if (typeof value !== "string" || value.trim().length === 0) {
29
+ throw new Error(`learner validation: ${label} must be nonempty text`);
30
+ }
31
+ }
32
+ export function assertValidDate(value, label = "event.date") {
33
+ if (typeof value !== "string") {
34
+ throw new Error(`learner validation: ${label} must be YYYY-MM-DD`);
35
+ }
36
+ const match = DATE_PATTERN.exec(value);
37
+ if (!match)
38
+ throw new Error(`learner validation: ${label} must be YYYY-MM-DD`);
39
+ const year = Number(match[1]);
40
+ const month = Number(match[2]);
41
+ const day = Number(match[3]);
42
+ const dt = new Date(Date.UTC(year, month - 1, day));
43
+ if (dt.getUTCFullYear() !== year || dt.getUTCMonth() !== month - 1 || dt.getUTCDate() !== day) {
44
+ throw new Error(`learner validation: ${label} is not a real calendar date: ${value}`);
45
+ }
46
+ const today = new Date().toISOString().slice(0, 10);
47
+ if (value > today) {
48
+ throw new Error(`learner validation: ${label} cannot be in the future: ${value}`);
49
+ }
50
+ }
51
+ export function assertValidLearnerInit(input) {
52
+ assertValidSlug(input.id, "learner.id");
53
+ assertNonempty(input.label, "learner.label");
54
+ }
55
+ export function assertValidEnrollmentInput(input) {
56
+ assertValidSlug(input.learnerId, "enrollment.learnerId");
57
+ assertValidSlug(input.courseId, "enrollment.courseId");
58
+ assertNonempty(input.workstreamId, "enrollment.workstreamId");
59
+ assertNonempty(input.planRef, "enrollment.planRef");
60
+ assertNonempty(input.planFingerprint, "enrollment.planFingerprint");
61
+ if (!Number.isInteger(input.planRevision) || input.planRevision < 1) {
62
+ throw new Error("learner validation: enrollment.planRevision must be a positive integer");
63
+ }
64
+ }
65
+ export function assertValidEventInput(event) {
66
+ assertValidSlug(event.id, "event.id");
67
+ assertValidDate(event.date);
68
+ if (!Array.isArray(event.covered)) {
69
+ throw new Error("learner validation: event.covered must be a list");
70
+ }
71
+ for (const concept of event.covered)
72
+ assertValidConceptId(concept);
73
+ assertNonempty(event.nextStep, "event.nextStep");
74
+ if (typeof event.interpretation !== "string") {
75
+ throw new Error("learner validation: event.interpretation must be text (may be empty)");
76
+ }
77
+ if (!Array.isArray(event.attempts)) {
78
+ throw new Error("learner validation: event.attempts must be a list");
79
+ }
80
+ for (const attempt of event.attempts) {
81
+ assertValidConceptId(attempt.concept);
82
+ assertNonempty(attempt.task, "attempt.task");
83
+ assertNonempty(attempt.response, "attempt.response");
84
+ if (!ATTEMPT_RESULTS.includes(attempt.result)) {
85
+ throw new Error(`learner validation: attempt.result must be one of ${ATTEMPT_RESULTS.join(", ")}`);
86
+ }
87
+ if (!ATTEMPT_HELPS.includes(attempt.help)) {
88
+ throw new Error(`learner validation: attempt.help must be one of ${ATTEMPT_HELPS.join(", ")}`);
89
+ }
90
+ if (!ATTEMPT_KINDS.includes(attempt.kind)) {
91
+ throw new Error(`learner validation: attempt.kind must be one of ${ATTEMPT_KINDS.join(", ")}`);
92
+ }
93
+ }
94
+ }
95
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.js","sourceRoot":"","sources":["../../../src/core/learner/validate.ts"],"names":[],"mappings":"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"}
@@ -31,8 +31,25 @@ export interface ReDerivationPair {
31
31
  export interface ReDerivationReport {
32
32
  readonly rate: number;
33
33
  readonly pairs: ReadonlyArray<ReDerivationPair>;
34
- /** Denominator: entity-sharing session pairs eligible for the rate. */
34
+ /**
35
+ * Denominator of the legacy pair-space rate: entity-sharing session pairs
36
+ * where BOTH sides carry at least one decision. A pair with a decisionless
37
+ * side cannot be a re-derivation by construction, and counting it inflated
38
+ * this figure roughly 3x - measured 2026-08-04 on the live 90-day window,
39
+ * 1,266,809 of 1,905,497 eligible pairs (66.5%) had a decisionless side.
40
+ */
35
41
  readonly eligible: number;
42
+ /** Sessions in the window carrying at least one decision. Linear in corpus size. */
43
+ readonly decisionBearingSessions: number;
44
+ /** Distinct later-side sessions appearing in `pairs`. Counted once each. */
45
+ readonly reDerivingSessions: number;
46
+ /**
47
+ * reDerivingSessions / decisionBearingSessions. This is the trendable metric:
48
+ * the pair-space `rate` divides by a denominator that grows quadratically with
49
+ * the corpus, so it mechanically falls the more the product is used, which is
50
+ * backwards for a number whose job is to show improvement.
51
+ */
52
+ readonly sessionRate: number;
36
53
  }
37
54
  interface SqliteLike {
38
55
  prepare(sql: string): {
@@ -42,7 +59,19 @@ interface SqliteLike {
42
59
  /**
43
60
  * SQLite-backed deps for the CLI readout. Reads sessions started within the
44
61
  * window along with their entity links and decision markers, plus all edges.
62
+ * `tenantId` scopes the sessions SELECT via tenantClause — the pairs this
63
+ * produces get persisted to a tenant-derived cache file (see
64
+ * `@core/storage/sqlite-outcome-store.js`), so an un-tenanted scan here would
65
+ * leak every tenant's sessions into whichever tenant's file reads it back.
45
66
  */
46
- export declare function sqliteReDerivationDeps(db: SqliteLike): ReDerivationDeps;
67
+ export declare function sqliteReDerivationDeps(db: SqliteLike, tenantId: string): ReDerivationDeps;
68
+ /**
69
+ * Parses the on-disk cache the 24h corpus-monitor job writes to
70
+ * (`~/.nlm/re-derivation-pairs.json`, see `src/cli/nlm.ts`) and the SQLite
71
+ * outcome adapters read back (`@core/storage/sqlite-outcome-store.js`).
72
+ * The file is a plain `ReDerivationPair[]` - anything else (corrupt JSON,
73
+ * a non-array shape) is treated as no pairs rather than thrown.
74
+ */
75
+ export declare function parseReDerivationPairsFile(raw: string): ReadonlyArray<ReDerivationPair>;
47
76
  export declare function computeReDerivationRate(deps: ReDerivationDeps, windowDays: number): Promise<ReDerivationReport>;
48
77
  export {};
@@ -7,6 +7,7 @@
7
7
  * entity, sit more than GAP_DAYS apart, have decision text overlapping at or
8
8
  * above JACCARD_FLOOR, and have no `continues`/`supersedes` edge linking them.
9
9
  */
10
+ import { tenantClause } from "../tenancy/tenant-clause.js";
10
11
  const JACCARD_FLOOR = 0.5;
11
12
  const GAP_DAYS = 7;
12
13
  const MS_PER_DAY = 86_400_000;
@@ -28,16 +29,21 @@ function jaccard(a, b) {
28
29
  /**
29
30
  * SQLite-backed deps for the CLI readout. Reads sessions started within the
30
31
  * window along with their entity links and decision markers, plus all edges.
32
+ * `tenantId` scopes the sessions SELECT via tenantClause — the pairs this
33
+ * produces get persisted to a tenant-derived cache file (see
34
+ * `@core/storage/sqlite-outcome-store.js`), so an un-tenanted scan here would
35
+ * leak every tenant's sessions into whichever tenant's file reads it back.
31
36
  */
32
- export function sqliteReDerivationDeps(db) {
37
+ export function sqliteReDerivationDeps(db, tenantId) {
38
+ const tc = tenantClause(tenantId);
33
39
  return {
34
40
  async listSessionsWithin(windowDays) {
35
41
  const rows = db
36
42
  .prepare(`SELECT id, started_at AS startedAt
37
43
  FROM sessions
38
- WHERE started_at >= datetime('now', ?)
44
+ WHERE started_at >= datetime('now', ?) AND ${tc.sql}
39
45
  ORDER BY started_at ASC`)
40
- .all(`-${windowDays} days`);
46
+ .all(`-${windowDays} days`, tc.param);
41
47
  const entStmt = db.prepare("SELECT entity_canonical AS e FROM session_entities WHERE session_id = ?");
42
48
  const decStmt = db.prepare("SELECT text AS t FROM markers WHERE session_id = ? AND kind = 'decision' ORDER BY position ASC");
43
49
  return rows.map((r) => ({
@@ -54,6 +60,17 @@ export function sqliteReDerivationDeps(db) {
54
60
  },
55
61
  };
56
62
  }
63
+ /**
64
+ * Parses the on-disk cache the 24h corpus-monitor job writes to
65
+ * (`~/.nlm/re-derivation-pairs.json`, see `src/cli/nlm.ts`) and the SQLite
66
+ * outcome adapters read back (`@core/storage/sqlite-outcome-store.js`).
67
+ * The file is a plain `ReDerivationPair[]` - anything else (corrupt JSON,
68
+ * a non-array shape) is treated as no pairs rather than thrown.
69
+ */
70
+ export function parseReDerivationPairsFile(raw) {
71
+ const parsed = JSON.parse(raw);
72
+ return Array.isArray(parsed) ? parsed : [];
73
+ }
57
74
  export async function computeReDerivationRate(deps, windowDays) {
58
75
  const sessions = await deps.listSessionsWithin(windowDays);
59
76
  const edges = await deps.listEdges();
@@ -61,11 +78,16 @@ export async function computeReDerivationRate(deps, windowDays) {
61
78
  .filter((e) => e.kind === "continues" || e.kind === "supersedes")
62
79
  .map((e) => [e.from_session, e.to_session].sort().join("|")));
63
80
  const pairs = [];
81
+ const reDeriving = new Set();
64
82
  let eligible = 0;
65
83
  for (let i = 0; i < sessions.length; i++) {
66
84
  for (let j = i + 1; j < sessions.length; j++) {
67
85
  const s = sessions[i];
68
86
  const t = sessions[j];
87
+ // A decisionless side can never be a re-derivation, so such a pair does
88
+ // not belong in the denominator at all.
89
+ if (s.decisions.length === 0 || t.decisions.length === 0)
90
+ continue;
69
91
  const shared = s.entities.filter((e) => t.entities.includes(e));
70
92
  if (shared.length === 0)
71
93
  continue;
@@ -79,9 +101,19 @@ export async function computeReDerivationRate(deps, windowDays) {
79
101
  const jac = jaccard(s.decisions, t.decisions);
80
102
  if (jac >= JACCARD_FLOOR) {
81
103
  pairs.push({ a: s.id, b: t.id, sharedEntities: shared, jaccard: round2(jac) });
104
+ const later = new Date(t.startedAt).getTime() >= new Date(s.startedAt).getTime() ? t.id : s.id;
105
+ reDeriving.add(later);
82
106
  }
83
107
  }
84
108
  }
85
- return { rate: eligible ? pairs.length / eligible : 0, pairs, eligible };
109
+ const decisionBearingSessions = sessions.filter((s) => s.decisions.length > 0).length;
110
+ return {
111
+ rate: eligible ? pairs.length / eligible : 0,
112
+ pairs,
113
+ eligible,
114
+ decisionBearingSessions,
115
+ reDerivingSessions: reDeriving.size,
116
+ sessionRate: decisionBearingSessions ? reDeriving.size / decisionBearingSessions : 0,
117
+ };
86
118
  }
87
119
  //# sourceMappingURL=re-derivation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"re-derivation.js","sourceRoot":"","sources":["../../../src/core/metrics/re-derivation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"re-derivation.js","sourceRoot":"","sources":["../../../src/core/metrics/re-derivation.ts"],"names":[],"mappings":"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"}
@@ -30,4 +30,4 @@ export interface OutcomeCoverage {
30
30
  readonly reDerivedLater: number;
31
31
  readonly unobserved: number;
32
32
  }
33
- export declare function computeOutcomeCoverage(input: OutcomeCoverageInput): Promise<OutcomeCoverage>;
33
+ export declare function computeOutcomeCoverage(tenantId: string, input: OutcomeCoverageInput): Promise<OutcomeCoverage>;
@@ -11,10 +11,10 @@
11
11
  * the precedence logic itself is never duplicated, only its I/O is batched.
12
12
  */
13
13
  import { deriveOutcome } from "./rollup.js";
14
- export async function computeOutcomeCoverage(input) {
14
+ export async function computeOutcomeCoverage(tenantId, input) {
15
15
  const counts = { held: 0, overturned: 0, builtUpon: 0, reDerivedLater: 0, unobserved: 0 };
16
16
  for (const session of input.sessions) {
17
- const verdict = await deriveOutcome(session.id, {
17
+ const verdict = await deriveOutcome(tenantId, session.id, {
18
18
  sessions: { getById: async () => session },
19
19
  signals: { listForSession: async () => input.signalsBySession.get(session.id) ?? [] },
20
20
  edges: { listForSession: async () => input.edgesBySession.get(session.id) ?? [] },