nlm-memory 0.5.0 → 0.5.2
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.
- package/README.md +89 -34
- package/dist/cli/digest.d.ts +20 -0
- package/dist/cli/digest.js +142 -0
- package/dist/cli/digest.js.map +1 -0
- package/dist/cli/nlm.d.ts +1 -0
- package/dist/cli/nlm.js +25 -1
- package/dist/cli/nlm.js.map +1 -1
- package/dist/core/digest/compose.d.ts +38 -0
- package/dist/core/digest/compose.js +93 -0
- package/dist/core/digest/compose.js.map +1 -0
- package/dist/core/digest/hook-liveness.d.ts +32 -0
- package/dist/core/digest/hook-liveness.js +54 -0
- package/dist/core/digest/hook-liveness.js.map +1 -0
- package/dist/http/app.js +2 -1
- package/dist/http/app.js.map +1 -1
- package/dist/mcp/server.js +20 -1
- package/dist/mcp/server.js.map +1 -1
- package/dist/ui/assets/{index-C8cpwbYJ.css → index-Beo8psd-.css} +1 -1
- package/dist/ui/assets/{index-CB50QnL-.js → index-CSPTTeeM.js} +8 -8
- package/dist/ui/index.html +2 -2
- package/package.json +26 -1
- package/.agents/plugins/marketplace.json +0 -20
- package/.github/workflows/ci.yml +0 -30
- package/docs/methodology/re-derivation-rate.md +0 -112
- package/docs/methodology/useful-hit-rate.md +0 -79
- package/docs/plans/2026-05-20-fts5-lexical-recall.md +0 -1088
- package/docs/plans/2026-05-20-recall-daemon-wedge-fix.md +0 -662
- package/docs/plans/2026-05-20-recall-hook-design.md +0 -131
- package/docs/plans/2026-05-20-recall-hook-implementation.md +0 -1222
- package/docs/plans/desktop-product.md +0 -69
- package/docs/plans/factstore-design.md +0 -236
- package/logs/CHANGELOG/CHANGELOG-2026.md +0 -1575
- package/logs/CHANGELOG/CHANGELOG.md +0 -209
- package/migrations/000_initial_schema.sql +0 -174
- package/migrations/001_entity_type_rename.sql +0 -17
- package/migrations/002_adapter_state_extend.sql +0 -12
- package/migrations/003_session_embeddings.sql +0 -11
- package/migrations/004_facts.sql +0 -46
- package/migrations/005_sources.sql +0 -31
- package/migrations/006_providers.sql +0 -33
- package/migrations/007_source_tokens.sql +0 -17
- package/migrations/008_fts_rebuild.sql +0 -9
- package/migrations/009_session_embedding_chunks.sql +0 -46
- package/migrations/010_sources_opencode.sql +0 -30
- package/migrations/011_sources_hermes_agent.sql +0 -30
- package/migrations/012_sources_aider.sql +0 -30
- package/migrations/013_adapter_state_failure_count.sql +0 -12
- package/migrations/014_sources_cursor.sql +0 -30
- package/migrations/015_sources_windsurf.sql +0 -30
- package/plugin-hermes-agent/README.md +0 -49
- package/plugin-hermes-agent/__init__.py +0 -75
- package/plugin-hermes-agent/plugin.yaml +0 -15
- package/scripts/backfill-citations.mjs +0 -0
- package/scripts/build-codex-plugin.mjs +0 -61
- package/scripts/deepseek-probe.mjs +0 -67
- package/scripts/extract-triples.mjs +0 -207
- package/scripts/longmemeval/embedding-cache.ts +0 -77
- package/scripts/longmemeval/fetch-dataset.sh +0 -25
- package/scripts/longmemeval/run-harness.ts +0 -315
- package/scripts/longmemeval/scorer.ts +0 -99
- package/scripts/longmemeval/tsconfig.json +0 -9
- package/scripts/longmemeval/types.ts +0 -35
- package/scripts/nlm-daily-digest.py +0 -239
- package/scripts/nlm-daily-digest.sh +0 -28
- package/src/cli/classify-parity.ts +0 -257
- package/src/cli/launchctl-helpers.ts +0 -49
- package/src/cli/nlm.ts +0 -1078
- package/src/core/actions/actions-log.ts +0 -118
- package/src/core/actions/overlay.ts +0 -117
- package/src/core/adapters/aider.ts +0 -205
- package/src/core/adapters/claude-code.ts +0 -293
- package/src/core/adapters/common.ts +0 -54
- package/src/core/adapters/cursor.ts +0 -486
- package/src/core/adapters/from-source.ts +0 -67
- package/src/core/adapters/hermes-agent.ts +0 -240
- package/src/core/adapters/hermes.ts +0 -277
- package/src/core/adapters/jsonl-generic.ts +0 -208
- package/src/core/adapters/opencode.ts +0 -281
- package/src/core/adapters/pi.ts +0 -264
- package/src/core/adapters/windsurf.ts +0 -386
- package/src/core/classifier/prompt.ts +0 -200
- package/src/core/dataset/build-dataset.ts +0 -463
- package/src/core/embedding/chunk-body.ts +0 -76
- package/src/core/embedding/embed-backfill.ts +0 -210
- package/src/core/embedding/embed-normalize.ts +0 -135
- package/src/core/facts/backfill-facts.ts +0 -254
- package/src/core/facts/extract-facts.ts +0 -50
- package/src/core/hook/citation-detect.ts +0 -124
- package/src/core/hook/cite-memo.ts +0 -68
- package/src/core/hook/claude-settings.ts +0 -187
- package/src/core/hook/gate.ts +0 -25
- package/src/core/hook/hook-log.ts +0 -41
- package/src/core/hook/memo-sweep.ts +0 -164
- package/src/core/hook/memo.ts +0 -67
- package/src/core/hook/pointer-block.ts +0 -26
- package/src/core/hook/select.ts +0 -32
- package/src/core/hook/transcript.ts +0 -121
- package/src/core/ingest/ingest-session.ts +0 -111
- package/src/core/providers/provider-models.ts +0 -100
- package/src/core/providers/provider-registry.ts +0 -196
- package/src/core/recall/citation-log.ts +0 -108
- package/src/core/recall/filter.ts +0 -27
- package/src/core/recall/index.ts +0 -6
- package/src/core/recall/match-fields.ts +0 -40
- package/src/core/recall/query-log.ts +0 -149
- package/src/core/recall/query-shape.ts +0 -66
- package/src/core/recall/recall-service.ts +0 -320
- package/src/core/recall/recent-log.ts +0 -59
- package/src/core/recall/tokenize.ts +0 -18
- package/src/core/recall/useful-scan.ts +0 -336
- package/src/core/recall-facts/fact-query-log.ts +0 -150
- package/src/core/recall-facts/fact-recall-service.ts +0 -327
- package/src/core/scheduler/scan-once.ts +0 -142
- package/src/core/scheduler/scheduler.ts +0 -225
- package/src/core/sources/source-registry.ts +0 -278
- package/src/core/storage/db-restore.ts +0 -133
- package/src/core/storage/live-status.ts +0 -45
- package/src/core/storage/migrate.ts +0 -72
- package/src/core/storage/sqlite-fact-store.ts +0 -304
- package/src/core/storage/sqlite-session-store.ts +0 -810
- package/src/hook/hook-auth.ts +0 -18
- package/src/hook/prompt-recall-hook.ts +0 -180
- package/src/hook/session-end-hook.ts +0 -81
- package/src/hook/session-start-hook.ts +0 -168
- package/src/hook/stop-hook.ts +0 -239
- package/src/http/app.ts +0 -1215
- package/src/install/claude-code.ts +0 -128
- package/src/install/codex.ts +0 -367
- package/src/install/cursor.ts +0 -68
- package/src/install/hermes-agent.ts +0 -76
- package/src/install/hermes.ts +0 -78
- package/src/install/nlm-dir-perms.ts +0 -55
- package/src/install/ollama.ts +0 -284
- package/src/install/setup.ts +0 -489
- package/src/install/windsurf.ts +0 -68
- package/src/llm/classifier-box.ts +0 -64
- package/src/llm/deepseek-client.ts +0 -150
- package/src/llm/env-autoload.ts +0 -55
- package/src/llm/ollama-client.ts +0 -189
- package/src/mcp/server.ts +0 -534
- package/src/ports/fact-store.ts +0 -102
- package/src/ports/llm-client.ts +0 -52
- package/src/ports/logger.ts +0 -16
- package/src/ports/session-store.ts +0 -45
- package/src/ports/transcript-adapter.ts +0 -55
- package/src/shared/types.ts +0 -149
- package/src/ui/App.tsx +0 -58
- package/src/ui/components/PromoteOpenButton.tsx +0 -65
- package/src/ui/components/SessionDrawer.tsx +0 -199
- package/src/ui/components/SideNav.tsx +0 -162
- package/src/ui/components/Skeleton.tsx +0 -107
- package/src/ui/index.html +0 -13
- package/src/ui/lib/actions.ts +0 -30
- package/src/ui/lib/api.ts +0 -92
- package/src/ui/lib/dataset.ts +0 -141
- package/src/ui/lib/registries.ts +0 -155
- package/src/ui/lib/view-settings.ts +0 -41
- package/src/ui/main.tsx +0 -15
- package/src/ui/pages/Live.tsx +0 -229
- package/src/ui/pages/Pulse.tsx +0 -415
- package/src/ui/pages/Recall.tsx +0 -190
- package/src/ui/pages/River.tsx +0 -354
- package/src/ui/pages/Search.tsx +0 -386
- package/src/ui/pages/Stub.tsx +0 -9
- package/src/ui/pages/Thread.tsx +0 -473
- package/src/ui/pages/settings/Classifier.tsx +0 -227
- package/src/ui/pages/settings/Data.tsx +0 -190
- package/src/ui/pages/settings/Index.tsx +0 -65
- package/src/ui/pages/settings/Labels.tsx +0 -224
- package/src/ui/pages/settings/Providers.tsx +0 -305
- package/src/ui/pages/settings/SettingsSubnav.tsx +0 -28
- package/src/ui/pages/settings/Sources.tsx +0 -326
- package/src/ui/pages/settings/Views.tsx +0 -96
- package/src/ui/styles.css +0 -1890
- package/src/ui/tsconfig.json +0 -21
- package/src/ui/vite.config.ts +0 -19
- package/tests/fixtures/claude_code/short_session.jsonl +0 -2
- package/tests/fixtures/claude_code/standard_iso.jsonl +0 -4
- package/tests/fixtures/claude_code/tool_heavy.jsonl +0 -8
- package/tests/fixtures/claude_code/with_subagent.jsonl +0 -7
- package/tests/fixtures/facts.ts +0 -17
- package/tests/fixtures/golden-corpus.ts +0 -85
- package/tests/fixtures/hermes/paired_request_dump.json +0 -24
- package/tests/fixtures/hermes/paired_session.json +0 -23
- package/tests/fixtures/hermes/request_dump.json +0 -28
- package/tests/fixtures/hermes/session_iso.json +0 -38
- package/tests/fixtures/hermes/session_unix.json +0 -38
- package/tests/fixtures/hermes/system_only.json +0 -18
- package/tests/fixtures/pi/error-connection-abort.jsonl +0 -8
- package/tests/fixtures/pi/short-successful.jsonl +0 -5
- package/tests/fixtures/pi/with-custom-message.jsonl +0 -6
- package/tests/fixtures/sessions.ts +0 -22
- package/tests/integration/backfill-facts.test.ts +0 -362
- package/tests/integration/citation-explicit.test.ts +0 -111
- package/tests/integration/cite-event.test.ts +0 -169
- package/tests/integration/cite-memo.test.ts +0 -87
- package/tests/integration/db-restore.test.ts +0 -153
- package/tests/integration/embed-backfill.test.ts +0 -176
- package/tests/integration/fact-supersedence.test.ts +0 -313
- package/tests/integration/fts-index.test.ts +0 -60
- package/tests/integration/getbyids-sqlite.test.ts +0 -100
- package/tests/integration/hermes-agent-hooks.test.ts +0 -248
- package/tests/integration/hook-claude-settings.test.ts +0 -218
- package/tests/integration/hook-log.test.ts +0 -54
- package/tests/integration/hook-memo.test.ts +0 -68
- package/tests/integration/hook-pre-compact.test.ts +0 -105
- package/tests/integration/hook-subagent-start.test.ts +0 -102
- package/tests/integration/http.test.ts +0 -401
- package/tests/integration/keyword-search-fts.test.ts +0 -66
- package/tests/integration/mcp-recall-logging.test.ts +0 -88
- package/tests/integration/mcp.test.ts +0 -260
- package/tests/integration/memo-sweep.test.ts +0 -91
- package/tests/integration/prompt-recall-hook.test.ts +0 -88
- package/tests/integration/provider-registry.test.ts +0 -107
- package/tests/integration/recall-golden.test.ts +0 -59
- package/tests/integration/recall-sqlite.test.ts +0 -169
- package/tests/integration/scheduler.test.ts +0 -391
- package/tests/integration/session-end-hook.test.ts +0 -48
- package/tests/integration/session-start-hook.test.ts +0 -126
- package/tests/integration/source-registry.test.ts +0 -122
- package/tests/integration/sqlite-fact-store.test.ts +0 -346
- package/tests/integration/stop-hook.test.ts +0 -560
- package/tests/integration/wal-checkpoint.test.ts +0 -49
- package/tests/unit/cli/launchctl-helpers.test.ts +0 -60
- package/tests/unit/core/adapters/aider.test.ts +0 -230
- package/tests/unit/core/adapters/claude-code.test.ts +0 -118
- package/tests/unit/core/adapters/cursor.test.ts +0 -485
- package/tests/unit/core/adapters/hermes-agent.test.ts +0 -329
- package/tests/unit/core/adapters/hermes.test.ts +0 -81
- package/tests/unit/core/adapters/jsonl-generic.test.ts +0 -142
- package/tests/unit/core/adapters/opencode.test.ts +0 -354
- package/tests/unit/core/adapters/pi.test.ts +0 -110
- package/tests/unit/core/adapters/windsurf.test.ts +0 -416
- package/tests/unit/core/classifier/prompt.test.ts +0 -126
- package/tests/unit/core/embedding/chunk-body.test.ts +0 -100
- package/tests/unit/core/facts/extract-facts.test.ts +0 -117
- package/tests/unit/core/filter.test.ts +0 -40
- package/tests/unit/core/hook/citation-detect-cite-session.test.ts +0 -96
- package/tests/unit/core/hook/citation-detect.test.ts +0 -124
- package/tests/unit/core/hook/gate.test.ts +0 -29
- package/tests/unit/core/hook/pointer-block.test.ts +0 -22
- package/tests/unit/core/hook/select.test.ts +0 -66
- package/tests/unit/core/match-fields.test.ts +0 -39
- package/tests/unit/core/mcp-cite-session.test.ts +0 -51
- package/tests/unit/core/providers/provider-models.test.ts +0 -101
- package/tests/unit/core/query-shape.test.ts +0 -92
- package/tests/unit/core/recall-facts/fact-recall-service.test.ts +0 -258
- package/tests/unit/core/recall-service.test.ts +0 -200
- package/tests/unit/core/storage/live-status.test.ts +0 -54
- package/tests/unit/core/tokenize.test.ts +0 -32
- package/tests/unit/core/useful-scan.test.ts +0 -537
- package/tests/unit/llm/embed.test.ts +0 -93
- package/tests/unit/llm/ollama-client.test.ts +0 -124
- package/tests/unit/scripts/longmemeval-scorer.test.ts +0 -114
- package/tsconfig.json +0 -31
- package/tsconfig.test.json +0 -11
- 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');
|
package/migrations/004_facts.sql
DELETED
|
@@ -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');
|