nlm-memory 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. package/README.md +72 -34
  2. package/dist/cli/nlm.js +2 -1
  3. package/dist/cli/nlm.js.map +1 -1
  4. package/dist/http/app.js +2 -1
  5. package/dist/http/app.js.map +1 -1
  6. package/dist/mcp/server.js +20 -1
  7. package/dist/mcp/server.js.map +1 -1
  8. package/dist/ui/assets/{index-C8cpwbYJ.css → index-Beo8psd-.css} +1 -1
  9. package/dist/ui/assets/{index-CB50QnL-.js → index-CSPTTeeM.js} +8 -8
  10. package/dist/ui/index.html +2 -2
  11. package/package.json +26 -1
  12. package/.agents/plugins/marketplace.json +0 -20
  13. package/.github/workflows/ci.yml +0 -30
  14. package/docs/methodology/re-derivation-rate.md +0 -112
  15. package/docs/methodology/useful-hit-rate.md +0 -79
  16. package/docs/plans/2026-05-20-fts5-lexical-recall.md +0 -1088
  17. package/docs/plans/2026-05-20-recall-daemon-wedge-fix.md +0 -662
  18. package/docs/plans/2026-05-20-recall-hook-design.md +0 -131
  19. package/docs/plans/2026-05-20-recall-hook-implementation.md +0 -1222
  20. package/docs/plans/desktop-product.md +0 -69
  21. package/docs/plans/factstore-design.md +0 -236
  22. package/logs/CHANGELOG/CHANGELOG-2026.md +0 -1575
  23. package/logs/CHANGELOG/CHANGELOG.md +0 -209
  24. package/migrations/000_initial_schema.sql +0 -174
  25. package/migrations/001_entity_type_rename.sql +0 -17
  26. package/migrations/002_adapter_state_extend.sql +0 -12
  27. package/migrations/003_session_embeddings.sql +0 -11
  28. package/migrations/004_facts.sql +0 -46
  29. package/migrations/005_sources.sql +0 -31
  30. package/migrations/006_providers.sql +0 -33
  31. package/migrations/007_source_tokens.sql +0 -17
  32. package/migrations/008_fts_rebuild.sql +0 -9
  33. package/migrations/009_session_embedding_chunks.sql +0 -46
  34. package/migrations/010_sources_opencode.sql +0 -30
  35. package/migrations/011_sources_hermes_agent.sql +0 -30
  36. package/migrations/012_sources_aider.sql +0 -30
  37. package/migrations/013_adapter_state_failure_count.sql +0 -12
  38. package/migrations/014_sources_cursor.sql +0 -30
  39. package/migrations/015_sources_windsurf.sql +0 -30
  40. package/plugin-hermes-agent/README.md +0 -49
  41. package/plugin-hermes-agent/__init__.py +0 -75
  42. package/plugin-hermes-agent/plugin.yaml +0 -15
  43. package/scripts/backfill-citations.mjs +0 -0
  44. package/scripts/build-codex-plugin.mjs +0 -61
  45. package/scripts/deepseek-probe.mjs +0 -67
  46. package/scripts/extract-triples.mjs +0 -207
  47. package/scripts/longmemeval/embedding-cache.ts +0 -77
  48. package/scripts/longmemeval/fetch-dataset.sh +0 -25
  49. package/scripts/longmemeval/run-harness.ts +0 -315
  50. package/scripts/longmemeval/scorer.ts +0 -99
  51. package/scripts/longmemeval/tsconfig.json +0 -9
  52. package/scripts/longmemeval/types.ts +0 -35
  53. package/scripts/nlm-daily-digest.py +0 -239
  54. package/scripts/nlm-daily-digest.sh +0 -28
  55. package/src/cli/classify-parity.ts +0 -257
  56. package/src/cli/launchctl-helpers.ts +0 -49
  57. package/src/cli/nlm.ts +0 -1078
  58. package/src/core/actions/actions-log.ts +0 -118
  59. package/src/core/actions/overlay.ts +0 -117
  60. package/src/core/adapters/aider.ts +0 -205
  61. package/src/core/adapters/claude-code.ts +0 -293
  62. package/src/core/adapters/common.ts +0 -54
  63. package/src/core/adapters/cursor.ts +0 -486
  64. package/src/core/adapters/from-source.ts +0 -67
  65. package/src/core/adapters/hermes-agent.ts +0 -240
  66. package/src/core/adapters/hermes.ts +0 -277
  67. package/src/core/adapters/jsonl-generic.ts +0 -208
  68. package/src/core/adapters/opencode.ts +0 -281
  69. package/src/core/adapters/pi.ts +0 -264
  70. package/src/core/adapters/windsurf.ts +0 -386
  71. package/src/core/classifier/prompt.ts +0 -200
  72. package/src/core/dataset/build-dataset.ts +0 -463
  73. package/src/core/embedding/chunk-body.ts +0 -76
  74. package/src/core/embedding/embed-backfill.ts +0 -210
  75. package/src/core/embedding/embed-normalize.ts +0 -135
  76. package/src/core/facts/backfill-facts.ts +0 -254
  77. package/src/core/facts/extract-facts.ts +0 -50
  78. package/src/core/hook/citation-detect.ts +0 -124
  79. package/src/core/hook/cite-memo.ts +0 -68
  80. package/src/core/hook/claude-settings.ts +0 -187
  81. package/src/core/hook/gate.ts +0 -25
  82. package/src/core/hook/hook-log.ts +0 -41
  83. package/src/core/hook/memo-sweep.ts +0 -164
  84. package/src/core/hook/memo.ts +0 -67
  85. package/src/core/hook/pointer-block.ts +0 -26
  86. package/src/core/hook/select.ts +0 -32
  87. package/src/core/hook/transcript.ts +0 -121
  88. package/src/core/ingest/ingest-session.ts +0 -111
  89. package/src/core/providers/provider-models.ts +0 -100
  90. package/src/core/providers/provider-registry.ts +0 -196
  91. package/src/core/recall/citation-log.ts +0 -108
  92. package/src/core/recall/filter.ts +0 -27
  93. package/src/core/recall/index.ts +0 -6
  94. package/src/core/recall/match-fields.ts +0 -40
  95. package/src/core/recall/query-log.ts +0 -149
  96. package/src/core/recall/query-shape.ts +0 -66
  97. package/src/core/recall/recall-service.ts +0 -320
  98. package/src/core/recall/recent-log.ts +0 -59
  99. package/src/core/recall/tokenize.ts +0 -18
  100. package/src/core/recall/useful-scan.ts +0 -336
  101. package/src/core/recall-facts/fact-query-log.ts +0 -150
  102. package/src/core/recall-facts/fact-recall-service.ts +0 -327
  103. package/src/core/scheduler/scan-once.ts +0 -142
  104. package/src/core/scheduler/scheduler.ts +0 -225
  105. package/src/core/sources/source-registry.ts +0 -278
  106. package/src/core/storage/db-restore.ts +0 -133
  107. package/src/core/storage/live-status.ts +0 -45
  108. package/src/core/storage/migrate.ts +0 -72
  109. package/src/core/storage/sqlite-fact-store.ts +0 -304
  110. package/src/core/storage/sqlite-session-store.ts +0 -810
  111. package/src/hook/hook-auth.ts +0 -18
  112. package/src/hook/prompt-recall-hook.ts +0 -180
  113. package/src/hook/session-end-hook.ts +0 -81
  114. package/src/hook/session-start-hook.ts +0 -168
  115. package/src/hook/stop-hook.ts +0 -239
  116. package/src/http/app.ts +0 -1215
  117. package/src/install/claude-code.ts +0 -128
  118. package/src/install/codex.ts +0 -367
  119. package/src/install/cursor.ts +0 -68
  120. package/src/install/hermes-agent.ts +0 -76
  121. package/src/install/hermes.ts +0 -78
  122. package/src/install/nlm-dir-perms.ts +0 -55
  123. package/src/install/ollama.ts +0 -284
  124. package/src/install/setup.ts +0 -489
  125. package/src/install/windsurf.ts +0 -68
  126. package/src/llm/classifier-box.ts +0 -64
  127. package/src/llm/deepseek-client.ts +0 -150
  128. package/src/llm/env-autoload.ts +0 -55
  129. package/src/llm/ollama-client.ts +0 -189
  130. package/src/mcp/server.ts +0 -534
  131. package/src/ports/fact-store.ts +0 -102
  132. package/src/ports/llm-client.ts +0 -52
  133. package/src/ports/logger.ts +0 -16
  134. package/src/ports/session-store.ts +0 -45
  135. package/src/ports/transcript-adapter.ts +0 -55
  136. package/src/shared/types.ts +0 -149
  137. package/src/ui/App.tsx +0 -58
  138. package/src/ui/components/PromoteOpenButton.tsx +0 -65
  139. package/src/ui/components/SessionDrawer.tsx +0 -199
  140. package/src/ui/components/SideNav.tsx +0 -162
  141. package/src/ui/components/Skeleton.tsx +0 -107
  142. package/src/ui/index.html +0 -13
  143. package/src/ui/lib/actions.ts +0 -30
  144. package/src/ui/lib/api.ts +0 -92
  145. package/src/ui/lib/dataset.ts +0 -141
  146. package/src/ui/lib/registries.ts +0 -155
  147. package/src/ui/lib/view-settings.ts +0 -41
  148. package/src/ui/main.tsx +0 -15
  149. package/src/ui/pages/Live.tsx +0 -229
  150. package/src/ui/pages/Pulse.tsx +0 -415
  151. package/src/ui/pages/Recall.tsx +0 -190
  152. package/src/ui/pages/River.tsx +0 -354
  153. package/src/ui/pages/Search.tsx +0 -386
  154. package/src/ui/pages/Stub.tsx +0 -9
  155. package/src/ui/pages/Thread.tsx +0 -473
  156. package/src/ui/pages/settings/Classifier.tsx +0 -227
  157. package/src/ui/pages/settings/Data.tsx +0 -190
  158. package/src/ui/pages/settings/Index.tsx +0 -65
  159. package/src/ui/pages/settings/Labels.tsx +0 -224
  160. package/src/ui/pages/settings/Providers.tsx +0 -305
  161. package/src/ui/pages/settings/SettingsSubnav.tsx +0 -28
  162. package/src/ui/pages/settings/Sources.tsx +0 -326
  163. package/src/ui/pages/settings/Views.tsx +0 -96
  164. package/src/ui/styles.css +0 -1890
  165. package/src/ui/tsconfig.json +0 -21
  166. package/src/ui/vite.config.ts +0 -19
  167. package/tests/fixtures/claude_code/short_session.jsonl +0 -2
  168. package/tests/fixtures/claude_code/standard_iso.jsonl +0 -4
  169. package/tests/fixtures/claude_code/tool_heavy.jsonl +0 -8
  170. package/tests/fixtures/claude_code/with_subagent.jsonl +0 -7
  171. package/tests/fixtures/facts.ts +0 -17
  172. package/tests/fixtures/golden-corpus.ts +0 -85
  173. package/tests/fixtures/hermes/paired_request_dump.json +0 -24
  174. package/tests/fixtures/hermes/paired_session.json +0 -23
  175. package/tests/fixtures/hermes/request_dump.json +0 -28
  176. package/tests/fixtures/hermes/session_iso.json +0 -38
  177. package/tests/fixtures/hermes/session_unix.json +0 -38
  178. package/tests/fixtures/hermes/system_only.json +0 -18
  179. package/tests/fixtures/pi/error-connection-abort.jsonl +0 -8
  180. package/tests/fixtures/pi/short-successful.jsonl +0 -5
  181. package/tests/fixtures/pi/with-custom-message.jsonl +0 -6
  182. package/tests/fixtures/sessions.ts +0 -22
  183. package/tests/integration/backfill-facts.test.ts +0 -362
  184. package/tests/integration/citation-explicit.test.ts +0 -111
  185. package/tests/integration/cite-event.test.ts +0 -169
  186. package/tests/integration/cite-memo.test.ts +0 -87
  187. package/tests/integration/db-restore.test.ts +0 -153
  188. package/tests/integration/embed-backfill.test.ts +0 -176
  189. package/tests/integration/fact-supersedence.test.ts +0 -313
  190. package/tests/integration/fts-index.test.ts +0 -60
  191. package/tests/integration/getbyids-sqlite.test.ts +0 -100
  192. package/tests/integration/hermes-agent-hooks.test.ts +0 -248
  193. package/tests/integration/hook-claude-settings.test.ts +0 -218
  194. package/tests/integration/hook-log.test.ts +0 -54
  195. package/tests/integration/hook-memo.test.ts +0 -68
  196. package/tests/integration/hook-pre-compact.test.ts +0 -105
  197. package/tests/integration/hook-subagent-start.test.ts +0 -102
  198. package/tests/integration/http.test.ts +0 -401
  199. package/tests/integration/keyword-search-fts.test.ts +0 -66
  200. package/tests/integration/mcp-recall-logging.test.ts +0 -88
  201. package/tests/integration/mcp.test.ts +0 -260
  202. package/tests/integration/memo-sweep.test.ts +0 -91
  203. package/tests/integration/prompt-recall-hook.test.ts +0 -88
  204. package/tests/integration/provider-registry.test.ts +0 -107
  205. package/tests/integration/recall-golden.test.ts +0 -59
  206. package/tests/integration/recall-sqlite.test.ts +0 -169
  207. package/tests/integration/scheduler.test.ts +0 -391
  208. package/tests/integration/session-end-hook.test.ts +0 -48
  209. package/tests/integration/session-start-hook.test.ts +0 -126
  210. package/tests/integration/source-registry.test.ts +0 -122
  211. package/tests/integration/sqlite-fact-store.test.ts +0 -346
  212. package/tests/integration/stop-hook.test.ts +0 -560
  213. package/tests/integration/wal-checkpoint.test.ts +0 -49
  214. package/tests/unit/cli/launchctl-helpers.test.ts +0 -60
  215. package/tests/unit/core/adapters/aider.test.ts +0 -230
  216. package/tests/unit/core/adapters/claude-code.test.ts +0 -118
  217. package/tests/unit/core/adapters/cursor.test.ts +0 -485
  218. package/tests/unit/core/adapters/hermes-agent.test.ts +0 -329
  219. package/tests/unit/core/adapters/hermes.test.ts +0 -81
  220. package/tests/unit/core/adapters/jsonl-generic.test.ts +0 -142
  221. package/tests/unit/core/adapters/opencode.test.ts +0 -354
  222. package/tests/unit/core/adapters/pi.test.ts +0 -110
  223. package/tests/unit/core/adapters/windsurf.test.ts +0 -416
  224. package/tests/unit/core/classifier/prompt.test.ts +0 -126
  225. package/tests/unit/core/embedding/chunk-body.test.ts +0 -100
  226. package/tests/unit/core/facts/extract-facts.test.ts +0 -117
  227. package/tests/unit/core/filter.test.ts +0 -40
  228. package/tests/unit/core/hook/citation-detect-cite-session.test.ts +0 -96
  229. package/tests/unit/core/hook/citation-detect.test.ts +0 -124
  230. package/tests/unit/core/hook/gate.test.ts +0 -29
  231. package/tests/unit/core/hook/pointer-block.test.ts +0 -22
  232. package/tests/unit/core/hook/select.test.ts +0 -66
  233. package/tests/unit/core/match-fields.test.ts +0 -39
  234. package/tests/unit/core/mcp-cite-session.test.ts +0 -51
  235. package/tests/unit/core/providers/provider-models.test.ts +0 -101
  236. package/tests/unit/core/query-shape.test.ts +0 -92
  237. package/tests/unit/core/recall-facts/fact-recall-service.test.ts +0 -258
  238. package/tests/unit/core/recall-service.test.ts +0 -200
  239. package/tests/unit/core/storage/live-status.test.ts +0 -54
  240. package/tests/unit/core/tokenize.test.ts +0 -32
  241. package/tests/unit/core/useful-scan.test.ts +0 -537
  242. package/tests/unit/llm/embed.test.ts +0 -93
  243. package/tests/unit/llm/ollama-client.test.ts +0 -124
  244. package/tests/unit/scripts/longmemeval-scorer.test.ts +0 -114
  245. package/tsconfig.json +0 -31
  246. package/tsconfig.test.json +0 -11
  247. package/vitest.config.ts +0 -22
@@ -1,209 +0,0 @@
1
- # nlm-memory-ts CHANGELOG
2
-
3
- Session-level log per session protocol. Cap: 10 entries — archive older to `CHANGELOG-YYYY.md` when exceeded.
4
-
5
- ## 2026-05-29 — v0.5.0: cross-platform parity, security hardening, model picker
6
-
7
- **Cross-platform daemon + hooks** — the missing platforms now install cleanly:
8
-
9
- - **Windows hook command format.** `buildHookCommand` is platform-aware: emits `set NLM_HOOK_MODE=mode && "exec" "script"` on Windows so cmd.exe parses it correctly; `smokeTestHookCommand` dispatches via `cmd.exe /c` instead of `sh -c`. Adds `cmdQuote()` for cmd.exe double-quote escaping. Pre-fix, hook install would silently roll back on any Windows-native install because the POSIX `sh -c` smoke test failed at `spawnSync`.
10
- - **Linux systemd user unit.** New `~/.config/systemd/user/nlm.service` template — `Type=simple`, `Restart=on-failure`, logs to `~/.nlm/logs/daemon-{out,err}.log`. `nlm install` / `nlm uninstall` / `nlm setup` all branch into systemd on Linux (was hint-only). Detects `XDG_RUNTIME_DIR` + `systemctl --user` presence so headless servers get a `loginctl enable-linger` callout instead of a confusing error.
11
- - Setup wizard now branches on `process.platform` for all three OSes; macOS LaunchAgent flow unchanged.
12
-
13
- **Hook mode default flipped: shadow → live.** Shadow remains opt-in via `NLM_HOOK_MODE=shadow` in the command, but the wizard and `nlm hook install` now ship `live` so new users see pointer-block injection on the first prompt. Earlier behavior left the hook silent until the user found the toggle, which fails the recall hook's own value prop. Install message text + descriptions updated.
14
-
15
- **Security hardening** — closes three classes of exposure on top of the existing 127.0.0.1 bind:
16
-
17
- - **`~/.nlm/` perms backfill.** New `src/install/nlm-dir-perms.ts` recursively chmods dirs → `0o700` and files → `0o600`, idempotent, runs on every `nlm setup`, `nlm install`, and `nlm start`. Covers the upgrade path for installs from before v0.4.2 (when explicit chmod was added only to `writeClassifierConfig`'s output) — existing `~/.nlm/.env` and `canonical.sqlite` would otherwise stay `0o644` forever.
18
- - **Local-only HTTP middleware** on `/api/*`. Threat model: external network blocked by bind, but DNS rebinding, browser drive-by from cross-origin tabs, and port-forwarded clients on other machines remained. New middleware enforces (1) Host header on the allowed loopback list with or without port, (2) Origin header (when present) on the same loopback list, (3) Bearer token (`Authorization: Bearer ${NLM_MCP_TOKEN}`, timing-safe compared) when Origin is absent. `/api/health` bypasses Origin/Bearer for liveness probes but is still Host-checked. Skipped under Vitest via `!!process.env["VITEST"]` so in-process `app.request()` tests still work.
19
- - **Auto-generated `NLM_MCP_TOKEN`.** New `ensureMcpToken()` in `src/install/ollama.ts` generates 32 random bytes (64-char hex, 256-bit entropy) and persists to `~/.nlm/.env` if no token is set. Idempotent — re-reads file before writing to survive parallel setup runs. Called from `runSetup` and `nlm start` so existing installs upgrade without operator action.
20
- - **Hook auth headers.** New `src/hook/hook-auth.ts` exports `hookAuthHeaders()` that attaches Bearer when `NLM_MCP_TOKEN` is set. All three hooks (`prompt-recall-hook`, `session-start-hook`, `stop-hook`) now call `autoloadEnv()` at startup and route their fetch headers through it so they continue to reach `/api/recall` and `/api/recall/cite-event` after the gate goes on.
21
-
22
- **Classifier provider + model picker.** Wizard now asks for provider (DeepSeek cloud / Ollama local) and model:
23
-
24
- - DeepSeek path surfaces an explicit privacy callout before the API key prompt: "DeepSeek classification sends up to 30K chars of each session transcript to api.deepseek.com." Then picks from `deepseek-v4-flash` / `deepseek-v4-pro` / `deepseek-chat`.
25
- - Ollama path queries `localhost:11434/api/tags`, filters out embedding-only models (`nomic-embed`, `mxbai-embed`, `snowflake-arctic-embed`, `bge-*`), and shows the rest as a sorted list. Falls back to `phi4-mini:latest` with a warning if Ollama isn't reachable.
26
- - `writeClassifierConfig` is now overloaded: legacy `(choice, apiKey?)` signature still works; new `({choice, model, apiKey})` form persists `NLM_CLASSIFIER`, `NLM_CLASSIFIER_MODEL`, and `DEEPSEEK_API_KEY` in `~/.nlm/.env`.
27
-
28
- **Switch default-case exhaustiveness.** `runSetup`'s runtime-loop switch now has a `default: never` arm that warns on an unknown id — guards against the `multiselect<RuntimeId>` runtime cast silently producing an unmatched value.
29
-
30
- **Dry-run path fixes.** `nlm connect claude-code --dry-run` now calls `mcpConfigPath()` instead of hardcoding `~/.mcp.json`, so users with `NLM_MCP_CONFIG` set see the right path in the dry-run output.
31
-
32
- **Tests:** 601/601 passing. New tests cover the Windows hook command format and the cmd.exe smoke-test branch. The local-only middleware is exercised in-process via the existing Vitest harness (skip-gate active under `VITEST`).
33
-
34
- **Next:** publish v0.5.0 to npm + tag GitHub. Consider an `nlm hook live` / `nlm hook shadow` toggle command so existing v0.4.x installs can flip without re-running `hook install`.
35
-
36
- ## 2026-05-29 — Search rebuild, Thread runtime filters, SessionDrawer nav, pagination
37
-
38
- **Search page full rewrite** — replaced a hard-capped 50-result list with a production-grade search UI:
39
- - Pagination: `PAGE_SIZE_OPTIONS = [10, 25, 50, 100]`, default 25, prev/next/first/last controls
40
- - Filter chips: runtime, status, entity (top 12 + overflow `<select>`), sort mode (relevance/recent)
41
- - Match snippets: 120-char window anchored on first token hit, `<mark>` highlighting, XSS-safe via HTML escape before regex
42
- - Field-origin live-tag: shows which field (label/entity/decision/open/summary) matched
43
- - Score weights: label×3, entity-exact×4, entity-substring×2, decision×2, open×2, summary×1, phrase-bonus+5
44
- - Sticky search header, "clear filters" button, `anyFilterActive` empty-state hint
45
- - SessionDrawer integrated; prev/next from paged slice
46
-
47
- **Thread page** — runtime/agent filter chips:
48
- - `EntityPicker` now has: search input, sort chips (most-active/least-active/a-z/z-a), pagination [24,48,96] default 48, runtime filter chips
49
- - `ThreadSessionList` adds runtime filter chip row (only rendered when `threadRuntimes.length > 1`)
50
- - Bug fixed: runtime filter reset now depends on `entity` string prop, not `thread` object reference (was resetting on sort)
51
-
52
- **SessionDrawer** — keyboard and button navigation:
53
- - `prevSessionId` / `nextSessionId` props; ← / → arrow key nav
54
- - Chevron SVG buttons in drawer header
55
-
56
- **UI/UX review loop** — spec-first pass (Opus) before developer subagent; review pass after. Caught 3 runtime bugs: hooks ordering, Vitest env detection (`VITEST=1` not `"true"`), CSS currentColor misuse on dot-pulse.
57
-
58
- **Tests:** 601/601 passing.
59
-
60
- **Next:** Supersedence visible in River + `get_session` MCP response (editable timeline moat needs to be visible in the UI).
61
-
62
- ## 2026-05-29 — Workspace coverage, global DB mode, CLI connect/disconnect (9503042)
63
-
64
- **CursorAdapter expansion** — three-format coverage via prefix-based dispatch:
65
- - `cr_` — global `cursorDiskKV` (current, v1.x+; already shipped)
66
- - `crw_` — workspace `ItemTable` `composer.composerData` → `allComposers[]` (v0.43–v1.x migration artifact)
67
- - `crc_` — workspace `ItemTable` `chatdata` tabs (all versions)
68
-
69
- `parseSession()` routes by prefix; `workspaceStorageDir()` derived from global DB path parent-of-parent so no extra config needed. `discover()` deduplicates across global + all workspace DBs via a `seen` Set.
70
-
71
- **WindsurfAdapter expansion** — global DB agent/flow sessions (`wsg_` prefix):
72
- - Tries `cursorDiskKV` first (`composerData:*`, `agentData:*`, `flowData:*`); falls back to `ItemTable` LIKE query on `%agent%`/`%flow%`/`%cascade%` keys when `cursorDiskKV` absent
73
- - `since` filter bug fixed: `lastSendTime=0` previously matched `0 < cutoff` and was filtered out; guard changed to `ts > 0 && ts < cutoff` so zero-timestamps (unknown age) are always included
74
-
75
- **All discover() IDs now prefixed** — prefix is the routing token, not decoration. Legacy unprefixed IDs still accepted in `parseSession()` via fallthrough.
76
-
77
- **CLI commands wired** — `nlm connect cursor`, `nlm connect windsurf`, `nlm disconnect cursor`, `nlm disconnect windsurf`. Each opens the NLM `SqliteSessionStore`, creates a `SourceRegistry`, calls the appropriate install function, prints a one-line report. Supports `--dry-run` and `--db-path`/`--user-dir` overrides. `exactOptionalPropertyTypes` fix: optional CLI option values spread conditionally (`...(opts.x ? { x: opts.x } : {})`).
78
-
79
- **Tests** — 596/596 passing (up from 543). Adapter tests grew from 39 to 53. Added workspace composer (`crw_`), chat tab (`crc_`), Windsurf global DB (`wsg_`), since=0 fix, and since-filter for all three tab types.
80
-
81
- **State:** v0.4.2 on npm (no bump this session — workspace+CLI work is additive on 0.4.2). 596 tests green. Commit `9503042`.
82
-
83
- **Next:** Dedicated UI session — supersedence visible in River + `get_session` MCP response (editable timeline moat needs to be visible).
84
-
85
- ## 2026-05-29 — Cursor adapter + Windsurf adapter (NocoDB #182, #183)
86
-
87
- **CursorAdapter** (`src/core/adapters/cursor.ts`)
88
-
89
- Reads Cursor AI composer sessions from `globalStorage/state.vscdb` (macOS: `~/Library/Application Support/Cursor/User/globalStorage/state.vscdb`, Linux: `~/.config/Cursor/User/globalStorage/state.vscdb`). Schema: `cursorDiskKV` key-value table. Session = one `composerData:<composerId>` entry. Messages read from inline `conversation[]` (v1.x) or `bubbleId:<composerId>:*` separate storage (v1.5+). Type `1` = user, `2` = assistant. Session ID prefix `cr_`. Env override: `NLM_CURSOR_DB_PATH`. Migration 014 adds `'cursor'` to the `sources.kind` CHECK constraint.
90
-
91
- **WindsurfAdapter** (`src/core/adapters/windsurf.ts`)
92
-
93
- Reads Windsurf (Codeium Cascade) chat sessions from workspace-scoped SQLite DBs in `<UserDir>/workspaceStorage/<hash>/state.vscdb`. Schema: `ItemTable`, key `workbench.panel.aichat.view.aichat.chatdata`, value JSON with `tabs[]`. Each tab = one session. Bubble role: `type: 'user'`→user, `type: 'ai'`→assistant; prefers `rawText` over `text`. Session ID prefix `ws_`. `pathOrUrl` = User directory (adapter discovers all workspace DBs by scanning). Env override: `NLM_WINDSURF_USER_DIR`. Migration 015 adds `'windsurf'` to the constraint.
94
-
95
- **Wiring:**
96
- - `from-source.ts`: `cursor` and `windsurf` cases added
97
- - `source-registry.ts`: `SourceKind` extended; `seedDefaults()` now seeds 8 presets (cursor + windsurf auto-enabled if their paths exist)
98
- - `tests/integration/source-registry.test.ts`: preset assertions updated to 8
99
-
100
- **State:** 582 tests passing (was 543, +39 new). Build clean, typecheck clean.
101
-
102
- **Next:** Training pipeline (#185, deferred until `nlm useful-scan --stats` shows >50 useful-hit-log positives). Consider bumping to v0.5.0 after `nlm connect cursor` CLI wiring.
103
-
104
- ## 2026-05-29 — Credential file permissions hardening (v0.4.2)
105
-
106
- **Security fix (automated plugin review catch):** `src/install/ollama.ts` `writeClassifierConfig()` now creates `~/.nlm` with `mode: 0o700` and writes `.env` with `mode: 0o600`. Added `chmodSync` on both dir and file to repair permissions on pre-existing installations. This was flagged as HIGH by the `security-guidance@claude-code-plugins` stop hook after the manual audit and peer review had both noted the `644` file issue but the prior session ended before fixing it. Published as `nlm-memory@0.4.2`.
107
-
108
- **State:** 543 tests passing. GitHub tag `v0.4.2` pushed.
109
-
110
- **Next:** Cursor adapter (NocoDB task #182), Windsurf adapter (#183). Training pipeline (#185) deferred until >50 useful hits in `useful-hit-log.jsonl`.
111
-
112
- ## 2026-05-29 — Security hardening: bind address, timing-safe auth, backup/restore gate
113
-
114
- **Changes:**
115
- - `src/cli/nlm.ts`: `serve()` now passes `hostname: "127.0.0.1"` — daemon no longer listens on all interfaces. Verified: `lsof` shows `127.0.0.1:3940` only.
116
- - `src/http/app.ts`: `/mcp` bearer token comparison replaced with `timingSafeEqual` (was `!==`).
117
- - `src/http/app.ts`: `/api/data/backup` and `/api/data/restore` now require `Authorization: Bearer <NLM_MCP_TOKEN>` when that env var is set. When unset (local-only use), the `127.0.0.1` bind is the guard.
118
- - Import: `timingSafeEqual` from `node:crypto` added.
119
-
120
- **Findings that prompted this (peer-reviewed audit 2026-05-29):**
121
- - MEDIUM: `0.0.0.0` binding exposed all HTTP endpoints to LAN — root finding that amplified everything else.
122
- - MEDIUM: backup/restore unauthenticated — full DB exfiltration from LAN with one HTTP call (peer reviewer caught this; primary auditor missed).
123
- - LOW: non-timing-safe bearer comparison on `/mcp`.
124
-
125
- **State:** 543 tests passing (no regressions). Daemon reloaded; bind address confirmed.
126
-
127
- **Next:** Cursor adapter (NocoDB task #182), Windsurf adapter (#183). Deferred: training pipeline (#185, needs >50 useful hits).
128
-
129
- ## 2026-05-28 — Scheduler failure backoff (a5c29b0)
130
-
131
- **Bug fixed: infinite classify retry storm**
132
-
133
- Session `517ca931.jsonl` (230KB, Whtnxt Agent project) was failing DeepSeek classification every 30-min tick because no `adapter_state` row was ever written on failure — so `scanOnce` saw a "new" file every tick. The daemon log showed hundreds of `[scheduler] classifier error for cc_517ca931 — skipping` with no error detail.
134
-
135
- Migration 013 adds `failure_count INTEGER DEFAULT 0` to `adapter_state`. `recordFailed()` increments `failure_count` and writes the current `file_size`. `scanOnce` skips files whose size hasn't changed, whether clean or failed — so any stuck file stops retrying until new content arrives. When the file grows, `failure_count` resets to 0.
136
-
137
- The scheduler now logs the actual error message and a `failure N/M` counter (e.g. `error: LLM unreachable: deepseek for cc_517ca931 (failure 1/3)`). The real error was `LLM unreachable: deepseek` — now diagnosable. 2 new integration tests; 543 total passing.
138
-
139
- **State:** daemon reloaded, migration applied, first retry logged correctly. Session `517ca931` will hit ceiling at failure 3/3 and stop.
140
-
141
- **Outstanding:** npm publish v0.4.0 (needs `npm login` in terminal). Scheduler (Phase D) is already fully implemented in `nlm start` — confirm via NocoDB task #184 whether anything remains scoped there.
142
-
143
- ## 2026-05-28 — Aider adapter + useful-scan cron + v0.4.0 release (ece591a, a222f44, 708f49d)
144
-
145
- Three items shipped in this session.
146
-
147
- **Aider TranscriptAdapter (ece591a)**
148
-
149
- `AiderAdapter` reads `.aider.chat.history.md` (or `$AIDER_CHAT_HISTORY_FILE`). A single file may contain multiple sessions, each opened by a `# aider chat started at YYYY-MM-DD HH:MM:SS` header. User turns are H4 headings (`#### ...`); assistant responses are plain text; blockquote lines (`> ...`) become `[tool_action: ...]` summaries. Session IDs derive from the header timestamp as `ai_YYYYMMDD_HHMMSS`. `endedAt` uses the next session's `startedAt` when available (no per-turn timestamps in markdown). Migration `012_sources_aider.sql` adds `'aider'` to the `sources.kind` CHECK constraint. `seedDefaults()` now seeds 6 presets. 21 new unit tests; 541 total pass.
150
-
151
- **useful-scan cron wiring (a222f44)**
152
-
153
- `nlm-daily-digest.sh` now calls `nlm useful-scan --days 1` before the Python stats fetch so `useful_hit_rate` is populated in the Telegram digest instead of showing "pending". Scan output goes to `logs/daily-digest/useful-scan.log`; `|| true` keeps the digest alive on scan failure. Dry-run confirmed: 161 recalls scanned, 0% useful (accurate — no `cite_session` calls yet).
154
-
155
- **v0.4.0 (708f49d)**
156
-
157
- Version bump and `git tag v0.4.0`. npm publish requires `npm login` — tag is set, publish when authenticated.
158
-
159
- **State:** build clean, 541 tests green. Outstanding: npm publish (needs `npm login`); C2 Aider and useful-scan cron are now complete.
160
-
161
- **Next:** README is rewritten. No outstanding P1s remaining. Consider: npm publish, prose-citation soft labels (deferred), or new feature work.
162
-
163
- ## 2026-05-28 — HermesAgentAdapter: TranscriptAdapter for NousResearch Hermes Agent (7b9074b)
164
-
165
- `src/core/adapters/hermes-agent.ts` — reads `~/.hermes/state.db` (WAL mode, schema v11). Extracts user/assistant/tool turns; tool calls in assistant messages summarized as `[tool_use: <name>]`; tool-role messages summarized as `[tool_result: <name>: <preview>]`; system messages skipped. Label from session `title` field, fallback to first user turn. DB path overridable via `NLM_HERMES_AGENT_DB_PATH` or `HERMES_HOME`.
166
-
167
- **Migration 011** (`migrations/011_sources_hermes_agent.sql`) adds `'hermes-agent'` to the `sources.kind` CHECK constraint (same rename-recreate-copy pattern as migration 010).
168
-
169
- **Source registry:** `SourceKind` extended; `seedDefaults()` now seeds 5 presets (hermes-agent inserted between hermes and opencode, auto-enabled if state.db exists).
170
-
171
- **`from-source.ts`:** `'hermes-agent'` case added, delegates to `HermesAgentAdapter`.
172
-
173
- **Tests: 520 pass** (was 501, +19 new in `tests/unit/core/adapters/hermes-agent.test.ts`). `source-registry.test.ts` updated for 5 presets. Build clean, typecheck clean.
174
-
175
- **State:** NousResearch Hermes Agent is now fully integrated — plugin hooks (pre/post-turn, session lifecycle) from the previous session + transcript indexing from this session. End-to-end: sessions indexed in SQLite → recalled by the daemon → injected into Hermes Agent via the Python plugin.
176
-
177
- **Next:** C2 Aider adapter; B3 extract-triples improvements.
178
-
179
- ## 2026-05-28 — NousResearch Hermes adapter + README rewrite
180
-
181
- **README rewrite**
182
-
183
- Lead with the three moats (cross-runtime reach, editable timeline, 97.2% R@5). Dropped "self-improving accuracy" framing. Added OpenCode to the shipped runtime list. Added `nlm connect hermes-agent` to the install table.
184
-
185
- **NousResearch Hermes Agent plugin (#165)**
186
-
187
- Python plugin for NousResearch Hermes Agent's `plugin.yaml` lifecycle hook system. Covers all 6 events the plugin system exposes.
188
-
189
- New files:
190
- - `plugin-hermes-agent/plugin.yaml` — manifest (`kind: memory`, 6 hooks declared)
191
- - `plugin-hermes-agent/__init__.py` — Python shim; each hook POSTs to the local nlm daemon (stdlib only, no PyPI deps)
192
- - `plugin-hermes-agent/README.md` — install guide
193
-
194
- New HTTP endpoints in the nlm daemon:
195
- - `POST /api/hook/hermes-agent/pre-turn` — keyword recall for `pre_llm_call`; updates memo; returns `{"context": str|null}`
196
- - `POST /api/hook/hermes-agent/post-turn` — prose citation detect for `post_llm_call`; logs to citation-log.jsonl
197
- - `POST /api/hook/hermes-agent/session-lifecycle` — clears surfaced-ID memo on end/finalize/reset
198
-
199
- New install module: `src/install/hermes-agent.ts` — `connectHermesAgent` / `disconnectHermesAgent` (copies plugin dir to `~/.hermes/plugins/nlm-memory/`, enables via `hermes plugins enable` if available).
200
-
201
- CLI: `nlm connect hermes-agent` / `nlm disconnect hermes-agent` added to `src/cli/nlm.ts`.
202
-
203
- **Tests: 501 pass** (was 488, +13 new in `tests/integration/hermes-agent-hooks.test.ts`). Build clean, typecheck clean.
204
-
205
- **State:** all three hermes-agent endpoints tested end-to-end without a TTY. Python plugin is a thin HTTP shim — no Python test harness needed.
206
-
207
- **Next:** transcript adapter for NousResearch Hermes sessions (session files stored in `~/.hermes-agent/sessions/` or equivalent); C2 Aider adapter; B3 extract-triples improvements.
208
-
209
- _Older entries archived in CHANGELOG-2026.md_
@@ -1,174 +0,0 @@
1
- -- NLE Memory canonical store — SQLite schema
2
- -- Default zero-config backend. Postgres mirror lives in schema/postgres.sql (TBD).
3
- --
4
- -- Design principles:
5
- -- • Sessions are immutable once written — supersedence is via edges, never via UPDATE
6
- -- • Entity registry is mutable (canonical merges, retitles, retirements)
7
- -- • All timestamps are ISO 8601 strings in TEXT columns (SQLite lacks a native datetime type;
8
- -- ISO strings sort correctly and are dialect-portable to Postgres)
9
- -- • Foreign keys enforced; ON DELETE CASCADE only on edges, never on sessions
10
-
11
- PRAGMA foreign_keys = ON;
12
- PRAGMA journal_mode = WAL;
13
-
14
- -- ── Sessions ───────────────────────────────────────────────────────────────
15
- CREATE TABLE IF NOT EXISTS sessions (
16
- id TEXT PRIMARY KEY, -- e.g. sess_2026-05-07T14-32-CAMEL
17
- runtime TEXT NOT NULL, -- e.g. claude-code/1.0, hermes/0.5
18
- runtime_session_id TEXT, -- the runtime's own session identifier
19
- started_at TEXT NOT NULL, -- ISO 8601
20
- ended_at TEXT, -- ISO 8601; NULL while session is active
21
- duration_min INTEGER, -- computed; null while active
22
- label TEXT NOT NULL, -- human-readable session title
23
- summary TEXT NOT NULL, -- ~80-token classifier output
24
- body TEXT, -- full markdown body with inline markers
25
- status TEXT NOT NULL CHECK(status IN ('active','closed','superseded')),
26
- transcript_kind TEXT, -- e.g. claude-code-jsonl
27
- transcript_path TEXT, -- runtime-resolvable opaque pointer
28
- transcript_offset INTEGER, -- byte offset start
29
- transcript_length INTEGER, -- byte length
30
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
31
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
32
- );
33
-
34
- CREATE INDEX IF NOT EXISTS idx_sessions_started_at ON sessions(started_at DESC);
35
- CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
36
- CREATE INDEX IF NOT EXISTS idx_sessions_runtime ON sessions(runtime);
37
-
38
- -- ── Entities ───────────────────────────────────────────────────────────────
39
- CREATE TABLE IF NOT EXISTS entities (
40
- canonical TEXT PRIMARY KEY, -- preferred spelling — also the primary key
41
- type TEXT NOT NULL, -- 'candidate' until labeled. Built-in labels: project | tool | contact | service | concept. Custom labels are user-defined via the UI / `nle-daemon action label` and have no CHECK constraint.
42
- status TEXT NOT NULL CHECK(status IN ('active','dormant','retired','rejected','candidate')),
43
- source TEXT, -- e.g. 'property:.claude/properties/polysignal.yaml', 'auto-detected', 'user-registered'
44
- notes TEXT, -- freeform user notes
45
- first_seen_session TEXT REFERENCES sessions(id),
46
- last_seen_session TEXT REFERENCES sessions(id),
47
- session_count INTEGER NOT NULL DEFAULT 0,
48
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
49
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
50
- );
51
-
52
- CREATE INDEX IF NOT EXISTS idx_entities_type ON entities(type);
53
- CREATE INDEX IF NOT EXISTS idx_entities_status ON entities(status);
54
-
55
- -- ── Entity variants (case-insensitive normalization) ───────────────────────
56
- CREATE TABLE IF NOT EXISTS entity_variants (
57
- variant TEXT PRIMARY KEY, -- raw form as it appeared
58
- canonical TEXT NOT NULL REFERENCES entities(canonical) ON DELETE CASCADE,
59
- source_session_id TEXT REFERENCES sessions(id)
60
- );
61
-
62
- CREATE INDEX IF NOT EXISTS idx_entity_variants_canonical ON entity_variants(canonical);
63
-
64
- -- ── Session ↔ Entity (many-to-many) ───────────────────────────────────────
65
- CREATE TABLE IF NOT EXISTS session_entities (
66
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
67
- entity_canonical TEXT NOT NULL REFERENCES entities(canonical),
68
- PRIMARY KEY (session_id, entity_canonical)
69
- );
70
-
71
- CREATE INDEX IF NOT EXISTS idx_session_entities_entity ON session_entities(entity_canonical);
72
-
73
- -- ── Markers (decisions / open questions) ──────────────────────────────────
74
- -- Extracted from inline body markers. Body is canonical — these rows are a queryable cache.
75
- CREATE TABLE IF NOT EXISTS markers (
76
- id INTEGER PRIMARY KEY AUTOINCREMENT,
77
- session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
78
- kind TEXT NOT NULL CHECK(kind IN ('decision','open')),
79
- text TEXT NOT NULL,
80
- position INTEGER NOT NULL DEFAULT 0 -- ordering within session
81
- );
82
-
83
- CREATE INDEX IF NOT EXISTS idx_markers_session ON markers(session_id);
84
- CREATE INDEX IF NOT EXISTS idx_markers_kind ON markers(kind);
85
-
86
- -- ── Supersedence + continues edges ────────────────────────────────────────
87
- -- Edge table for non-destructive editing. A session can supersede or continue another.
88
- -- Bidirectional lookups handled by indexes on both columns.
89
- CREATE TABLE IF NOT EXISTS session_edges (
90
- from_session TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
91
- to_session TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
92
- kind TEXT NOT NULL CHECK(kind IN ('supersedes','continues','branched_from','merged_from')),
93
- PRIMARY KEY (from_session, to_session, kind)
94
- );
95
-
96
- CREATE INDEX IF NOT EXISTS idx_session_edges_from ON session_edges(from_session);
97
- CREATE INDEX IF NOT EXISTS idx_session_edges_to ON session_edges(to_session);
98
- CREATE INDEX IF NOT EXISTS idx_session_edges_kind ON session_edges(kind);
99
-
100
- -- ── Full-text search over sessions ────────────────────────────────────────
101
- -- FTS5 virtual table for label/summary/body search. Maintained via triggers.
102
- CREATE VIRTUAL TABLE IF NOT EXISTS sessions_fts USING fts5(
103
- label, summary, body,
104
- content='sessions',
105
- content_rowid='rowid'
106
- );
107
-
108
- CREATE TRIGGER IF NOT EXISTS sessions_ai AFTER INSERT ON sessions BEGIN
109
- INSERT INTO sessions_fts(rowid, label, summary, body)
110
- VALUES (new.rowid, new.label, new.summary, new.body);
111
- END;
112
-
113
- CREATE TRIGGER IF NOT EXISTS sessions_au AFTER UPDATE ON sessions BEGIN
114
- INSERT INTO sessions_fts(sessions_fts, rowid, label, summary, body)
115
- VALUES('delete', old.rowid, old.label, old.summary, old.body);
116
- INSERT INTO sessions_fts(rowid, label, summary, body)
117
- VALUES (new.rowid, new.label, new.summary, new.body);
118
- END;
119
-
120
- CREATE TRIGGER IF NOT EXISTS sessions_ad AFTER DELETE ON sessions BEGIN
121
- INSERT INTO sessions_fts(sessions_fts, rowid, label, summary, body)
122
- VALUES('delete', old.rowid, old.label, old.summary, old.body);
123
- END;
124
-
125
- -- ── Vector embeddings (sqlite-vec) ────────────────────────────────────────
126
- -- Loaded as an extension at runtime: SELECT load_extension('vec0');
127
- -- Schema declared here for reference; real CREATE happens at daemon startup
128
- -- after the extension is loaded.
129
- --
130
- -- CREATE VIRTUAL TABLE session_embeddings USING vec0(
131
- -- session_id TEXT PRIMARY KEY,
132
- -- embedding float[768] -- nomic-embed-text dim; configurable
133
- -- );
134
-
135
- -- ── Schema migrations tracker ─────────────────────────────────────────────
136
- -- Applied by SQLiteStore.migrate() on daemon start. Tracks which versioned
137
- -- migration files in daemon/migrations/ have been run on this database.
138
- CREATE TABLE IF NOT EXISTS schema_migrations (
139
- version INTEGER PRIMARY KEY,
140
- name TEXT NOT NULL,
141
- applied_at TEXT NOT NULL DEFAULT (datetime('now'))
142
- );
143
-
144
- -- ── Adapter state (per-runtime offsets for resumability) ──────────────────
145
- CREATE TABLE IF NOT EXISTS adapter_state (
146
- adapter_name TEXT NOT NULL, -- e.g. 'claude-code'
147
- source_path TEXT NOT NULL, -- e.g. ~/.claude/projects/foo/abc123.jsonl
148
- last_offset INTEGER NOT NULL DEFAULT 0,
149
- last_processed_at TEXT NOT NULL DEFAULT (datetime('now')),
150
- PRIMARY KEY (adapter_name, source_path)
151
- );
152
-
153
- -- ── Actions (event-sourced action log) ────────────────────────────────────
154
- -- Universal primitive for every interactive change: alert dismiss/snooze, entity
155
- -- retire, link supersedes/continues, mark intentional, undo, etc. Append-only.
156
- -- Computed tables (session_edges, entities.status) become projections of this log.
157
- -- Same schema across web UI, MCP tool calls, CLI, future mobile/api.
158
- CREATE TABLE IF NOT EXISTS actions (
159
- id TEXT PRIMARY KEY, -- act_<iso-ts>_<short-uuid>
160
- timestamp TEXT NOT NULL, -- ISO 8601 — when the action was taken
161
- kind TEXT NOT NULL, -- dismiss | snooze | retire_entity | label_entity | merge_entity | link_supersedes | link_continues | resolve_open | mark_intentional | undo | sync_localstorage
162
- subject_type TEXT NOT NULL, -- alert | entity | session | decision | open_question | action
163
- subject_id TEXT NOT NULL, -- e.g. 'stale_Squarespace', 'sess_002', 'NocoDB'
164
- payload TEXT, -- JSON: action-specific data (snoozed_until, target_session_id, new_type, ...)
165
- actor TEXT NOT NULL DEFAULT 'user', -- user | agent:claude-code | agent:hermes | system
166
- runtime TEXT, -- web-ui | mcp:claude-code | cli | mobile-ios | api
167
- reverted_by TEXT REFERENCES actions(id), -- the action that undid this one (null = active)
168
- created_at TEXT NOT NULL DEFAULT (datetime('now'))
169
- );
170
-
171
- CREATE INDEX IF NOT EXISTS idx_actions_subject ON actions(subject_type, subject_id);
172
- CREATE INDEX IF NOT EXISTS idx_actions_kind ON actions(kind);
173
- CREATE INDEX IF NOT EXISTS idx_actions_timestamp ON actions(timestamp DESC);
174
- CREATE INDEX IF NOT EXISTS idx_actions_active ON actions(subject_type, subject_id, reverted_by) WHERE reverted_by IS NULL;
@@ -1,17 +0,0 @@
1
- -- Migration 001: rename entity types and action kind
2
- --
3
- -- Renames entity.type values: property→project, person→contact, external→service
4
- -- (The CHECK constraint on entities.type was already dropped in the initial schema.)
5
- -- Safe to re-run: CASE expression only matches old values; INSERT OR IGNORE is a no-op
6
- -- if this migration has already been applied.
7
-
8
- UPDATE entities
9
- SET type = CASE
10
- WHEN type = 'property' THEN 'project'
11
- WHEN type = 'person' THEN 'contact'
12
- WHEN type = 'external' THEN 'service'
13
- ELSE type
14
- END
15
- WHERE type IN ('property', 'person', 'external');
16
-
17
- INSERT OR IGNORE INTO schema_migrations (version, name) VALUES (1, 'entity_type_rename');
@@ -1,12 +0,0 @@
1
- -- Migration 002: extend adapter_state with columns needed for supersede-on-resume.
2
- --
3
- -- file_size — bytes-on-disk at last classification (detects file growth)
4
- -- session_id — NLE session id produced by the last classification (target for supersede)
5
- --
6
- -- last_offset stays for future chunking use (Phase 3+). For supersede-on-resume in
7
- -- Phase 2, file_size carries the equivalent signal at whole-file granularity.
8
-
9
- ALTER TABLE adapter_state ADD COLUMN file_size INTEGER;
10
- ALTER TABLE adapter_state ADD COLUMN session_id TEXT;
11
-
12
- INSERT OR IGNORE INTO schema_migrations (version, name) VALUES (2, '002_adapter_state_extend');
@@ -1,11 +0,0 @@
1
- -- Migration 003: session_embeddings virtual table via sqlite-vec.
2
- --
3
- -- Requires sqlite-vec loaded at connection time (handled by SQLiteStore.connect()).
4
- -- 768 dims matches nomic-embed-text, the default embedding model.
5
-
6
- CREATE VIRTUAL TABLE IF NOT EXISTS session_embeddings USING vec0(
7
- session_id TEXT PRIMARY KEY,
8
- embedding float[768]
9
- );
10
-
11
- INSERT OR IGNORE INTO schema_migrations (version, name) VALUES (3, '003_session_embeddings');
@@ -1,46 +0,0 @@
1
- -- Migration 004: facts + fact_embeddings.
2
- --
3
- -- Facts are the agent-recall projection of session content: normalized
4
- -- (subject, predicate, value) triples derived from session classifier output,
5
- -- supersedence-aware via the tombstone pointer `superseded_by`. See
6
- -- docs/plans/factstore-design.md.
7
- --
8
- -- Phase B.1 creates the tables. Writes start in Phase B.2 (classifier prompt
9
- -- extension). The fact_embeddings vec0 table is created now so Phase B.3 can
10
- -- light up semantic fact recall without a second migration.
11
-
12
- CREATE TABLE IF NOT EXISTS facts (
13
- id TEXT PRIMARY KEY,
14
- kind TEXT NOT NULL CHECK (kind IN ('decision', 'open', 'attribute')),
15
- subject TEXT NOT NULL,
16
- predicate TEXT NOT NULL,
17
- value TEXT NOT NULL,
18
- source_session_id TEXT NOT NULL REFERENCES sessions(id) ON DELETE CASCADE,
19
- source_quote TEXT,
20
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
21
- superseded_by TEXT REFERENCES facts(id) ON DELETE SET NULL,
22
- confidence REAL NOT NULL CHECK (confidence >= 0.0 AND confidence <= 1.0)
23
- );
24
-
25
- -- Hot path: deterministic supersedence collision check on ingest
26
- -- (subject, predicate) lookups against current rows only.
27
- CREATE INDEX IF NOT EXISTS idx_facts_subject_predicate_current
28
- ON facts(subject, predicate)
29
- WHERE superseded_by IS NULL;
30
-
31
- -- "What do we know about X?" — subject-only browsing.
32
- CREATE INDEX IF NOT EXISTS idx_facts_subject_current
33
- ON facts(subject)
34
- WHERE superseded_by IS NULL;
35
-
36
- -- Reverse lookup: which facts came from this session?
37
- CREATE INDEX IF NOT EXISTS idx_facts_session
38
- ON facts(source_session_id);
39
-
40
- -- Semantic recall index (Phase B.3). 768 dims matches nomic-embed-text.
41
- CREATE VIRTUAL TABLE IF NOT EXISTS fact_embeddings USING vec0(
42
- fact_id TEXT PRIMARY KEY,
43
- embedding float[768]
44
- );
45
-
46
- INSERT OR IGNORE INTO schema_migrations (version, name) VALUES (4, '004_facts');
@@ -1,31 +0,0 @@
1
- -- Migration 005: sources registry.
2
- --
3
- -- Each row represents one transcript source the daemon should scan. The
4
- -- three hardcoded adapters (claude-code / hermes / pi) become seeded rows
5
- -- pointing at the same parse logic; future custom JSONL or webhook sources
6
- -- live alongside them.
7
- --
8
- -- Parse config is a JSON blob whose shape depends on `kind`:
9
- -- - "claude-code" / "hermes" / "pi": preset adapters. parse_config is
10
- -- reserved but unused — paths come from path_or_url.
11
- -- - "jsonl-generic": { sessionIdField, textField, startedAtField,
12
- -- roleField, runtimeLabel, ... }
13
- -- - "webhook": parse_config is empty; ingest is push-based.
14
- --
15
- -- See docs/plans/desktop-product.md (Phase 0).
16
-
17
- CREATE TABLE IF NOT EXISTS sources (
18
- id INTEGER PRIMARY KEY AUTOINCREMENT,
19
- kind TEXT NOT NULL CHECK (kind IN ('claude-code', 'hermes', 'pi', 'jsonl-generic', 'webhook')),
20
- name TEXT NOT NULL UNIQUE,
21
- path_or_url TEXT,
22
- runtime_label TEXT NOT NULL,
23
- parse_config TEXT NOT NULL DEFAULT '{}',
24
- enabled INTEGER NOT NULL DEFAULT 1,
25
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
26
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
27
- );
28
-
29
- CREATE INDEX IF NOT EXISTS idx_sources_enabled ON sources(enabled) WHERE enabled = 1;
30
-
31
- INSERT OR IGNORE INTO schema_migrations (version, name) VALUES (5, '005_sources');
@@ -1,33 +0,0 @@
1
- -- Migration 006: providers registry.
2
- --
3
- -- Each row represents one LLM endpoint configured for classification or
4
- -- (future) embedding. The classifier reads this table to choose a
5
- -- provider/model at boot; the UI mutates it through /api/providers.
6
- --
7
- -- API key storage. v0 stores keys in the api_key column. This is fine
8
- -- because the SQLite file already contains the user's transcripts —
9
- -- anyone with disk access already has everything sensitive. Phase 2
10
- -- (Tauri shell) migrates keys to the OS keychain and replaces the column
11
- -- with a keychain reference. The API shape stays the same.
12
- --
13
- -- `kind` is the structural family (openai-compatible, ollama,
14
- -- anthropic-native, deepseek). The classifier+model UI uses it to decide
15
- -- which client class to instantiate.
16
- --
17
- -- See docs/plans/desktop-product.md (Phase 0 task 3).
18
-
19
- CREATE TABLE IF NOT EXISTS providers (
20
- id INTEGER PRIMARY KEY AUTOINCREMENT,
21
- kind TEXT NOT NULL CHECK (kind IN ('deepseek', 'ollama', 'openai', 'anthropic', 'openrouter', 'openai-compatible')),
22
- name TEXT NOT NULL UNIQUE,
23
- base_url TEXT,
24
- api_key TEXT,
25
- default_model TEXT,
26
- enabled INTEGER NOT NULL DEFAULT 1,
27
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
28
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
29
- );
30
-
31
- CREATE INDEX IF NOT EXISTS idx_providers_enabled ON providers(enabled) WHERE enabled = 1;
32
-
33
- INSERT OR IGNORE INTO schema_migrations (version, name) VALUES (6, '006_providers');
@@ -1,17 +0,0 @@
1
- -- Migration 007: webhook token on sources.
2
- --
3
- -- Webhook sources need a bearer token so external tools (anything that
4
- -- pushes sessions via POST /api/ingest) can authenticate. The token is
5
- -- stored alongside the source row so one webhook = one token = one
6
- -- provenance label.
7
- --
8
- -- Storage policy mirrors providers.api_key: column in the canonical
9
- -- SQLite for v0 (the DB file already holds the user's transcripts,
10
- -- adding a token doesn't change the threat model). Phase 2 migrates to
11
- -- OS keychain without changing the API shape.
12
- --
13
- -- See docs/plans/desktop-product.md (Phase 0 task 5).
14
-
15
- ALTER TABLE sources ADD COLUMN token TEXT;
16
-
17
- INSERT OR IGNORE INTO schema_migrations (version, name) VALUES (7, '007_source_tokens');
@@ -1,9 +0,0 @@
1
- -- One-time safety rebuild of the sessions_fts external-content FTS5 index.
2
- -- The virtual table and its sync triggers (sessions_ai / sessions_au /
3
- -- sessions_ad) were declared in migration 000 and have fired on every write
4
- -- since, so the index is normally already in sync. This rebuild guarantees
5
- -- the index matches every existing sessions row before the recall path
6
- -- starts depending on FTS5 for keyword search. Safe and idempotent.
7
- INSERT INTO sessions_fts(sessions_fts) VALUES('rebuild');
8
-
9
- INSERT OR IGNORE INTO schema_migrations (version, name) VALUES (8, 'fts_rebuild');
@@ -1,46 +0,0 @@
1
- -- Migration 009: chunk + max-pool semantic index.
2
- --
3
- -- Replaces session_embeddings (one vector per session, truncated at
4
- -- MAX_EMBED_CHARS=8000) with per-chunk vectors. Recall-time score is
5
- -- max cosine across chunks per session.
6
- --
7
- -- The 2026-05-25 LongMemEval-S baseline showed 98% of gold sessions
8
- -- exceed 8000 chars and were silently truncated. Raising the per-call
9
- -- cap (#172) hit Ollama 500s on >50% of long inputs. Chunking sidesteps
10
- -- both: each chunk is well under the Ollama failure cliff, and the full
11
- -- body becomes searchable. Expected lift: semantic R@5 87.2 → >92,
12
- -- hybrid R@5 94.6 → >96.
13
- --
14
- -- Schema choices:
15
- -- * Auxiliary columns (+session_id, +chunk_idx) so KNN queries return
16
- -- session attribution without a join.
17
- -- * Separate session_chunk_map keyed on session_id supports
18
- -- `DELETE FROM session_embedding_chunks WHERE chunk_id IN
19
- -- (SELECT chunk_id FROM session_chunk_map WHERE session_id = ?)`
20
- -- since vec0 has no documented filtering on aux columns.
21
- --
22
- -- session_embeddings (single-vector) is intentionally left in place:
23
- -- * keeps rollback trivial (revert recall code, old vectors still there)
24
- -- * avoids forcing a multi-hour re-embed at deploy time; backfill
25
- -- populates chunks asynchronously
26
- -- * a future cleanup migration drops it once chunks are validated
27
-
28
- CREATE VIRTUAL TABLE IF NOT EXISTS session_embedding_chunks USING vec0(
29
- chunk_id INTEGER PRIMARY KEY,
30
- embedding float[768],
31
- +session_id TEXT,
32
- +chunk_idx INTEGER
33
- );
34
-
35
- CREATE TABLE IF NOT EXISTS session_chunk_map (
36
- chunk_id INTEGER PRIMARY KEY,
37
- session_id TEXT NOT NULL,
38
- chunk_idx INTEGER NOT NULL,
39
- FOREIGN KEY (session_id) REFERENCES sessions(id) ON DELETE CASCADE
40
- );
41
-
42
- CREATE INDEX IF NOT EXISTS idx_session_chunk_map_session
43
- ON session_chunk_map(session_id);
44
-
45
- INSERT OR IGNORE INTO schema_migrations (version, name)
46
- VALUES (9, '009_session_embedding_chunks');