@hilbras/remembra 3.2.0 → 3.4.0

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/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ # Architecture
2
+
3
+ How Remembra is put together, and where the extension seams are.
4
+
5
+ ```
6
+ ┌──────────────┐ ┌──────────────┐
7
+ │ MCP tools │ │ HTTP API │ Transports (thin adapters)
8
+ │ (index.ts) │ │ (http.ts) │
9
+ └──────┬───────┘ └──────┬───────┘
10
+ │ both call the same handlers
11
+ ▼ ▼
12
+ ┌──────────────────────────────────┐
13
+ │ MemoryService (service.ts) │ Search orchestration, digest/dedup/merge,
14
+ │ ← depends on MemoryBackend only │ decay lifecycle, snapshots, embed/LLM fail-open
15
+ └──────────────┬───────────────────┘
16
+
17
+ ┌──────────────────────────────────┐
18
+ │ MemoryBackend (backend.ts) │ The storage contract
19
+ └──────────────┬───────────────────┘
20
+
21
+ ┌──────────────────────────────────┐
22
+ │ MemoryStore (store.ts) │ Plain markdown files + frontmatter
23
+ │ .remembra lock · crash recovery │ (no database, by decision — Q5-A)
24
+ └──────────────────────────────────┘
25
+ ```
26
+
27
+ ## MemoryBackend: the swap seam (audit Phase 3)
28
+
29
+ `MemoryService` never touches the filesystem directly — it talks to the
30
+ `MemoryBackend` interface (`src/backend.ts`). Today's implementation is
31
+ `MemoryStore` (markdown files). A future SQLite/vector-DB backend only has to
32
+ satisfy that interface; the tests include an `InMemoryBackend` proving the
33
+ service runs unchanged against a non-file implementation.
34
+
35
+ Contract highlights:
36
+
37
+ - mutations must be safe under same-process **and** cross-process concurrency;
38
+ - a memory id exists in **exactly one tree** (active or archived) at rest;
39
+ - `importMemory` refuses ids that already exist.
40
+
41
+ ## Concurrency model (audit Phase 2: advisory locking)
42
+
43
+ Three layers, from narrowest to widest:
44
+
45
+ | Layer | Mechanism | Guards against |
46
+ |-------|-----------|----------------|
47
+ | Single write | temp file + `rename()` (POSIX-atomic) | torn/corrupt files on crash |
48
+ | One process | FIFO queue inside `MemoryStore` | same-instance async races (touch vs archive, parallel stores) |
49
+ | Many processes | `<root>/.remembra.lock` (`O_EXCL` create) | MCP server vs `remembra maintain` CLI vs a second session |
50
+
51
+ Lock rules:
52
+
53
+ - acquired around every mutating operation (store/update/archive/revive/
54
+ touch/forget/import) **including its read**, so read-modify-write cycles are
55
+ whole;
56
+ - **stale steal**: a lock whose pid is dead (or older than
57
+ `REMEMBRA_LOCK_STALE_MS`, default 10 s) is removed; the `O_EXCL` re-create
58
+ decides the winner;
59
+ - **timeout**: waiting longer than `REMEMBRA_LOCK_TIMEOUT_MS` (default 5 s)
60
+ fails with the typed `LOCK_TIMEOUT` error (HTTP 423);
61
+ - released in a `finally`, verified by pid before unlinking.
62
+
63
+ Reads (`get`/`all`) don't lock — atomic rename means they always see a
64
+ consistent file.
65
+
66
+ ## Crash recovery (audit Phase 3: "journal")
67
+
68
+ Atomic `rename()` already guarantees no half-written memory files, so instead
69
+ of a write-ahead log (which would itself need journaling) Remembra runs a
70
+ one-time **recovery pass** on first store access per process:
71
+
72
+ 1. delete orphaned `*.tmp` files (crash between write and rename);
73
+ 2. reconcile ids found in **both** active and archived trees (crash between
74
+ archive/revive's write and unlink, audit finding #19) — newest `updatedAt`
75
+ wins, ties go to the archived copy.
76
+
77
+ Anything it fixes is logged:
78
+ `Remembra: crash recovery — removed N orphaned temp file(s), reconciled N interrupted move(s)`.
79
+
80
+ ## Error classification (audit Phase 2)
81
+
82
+ All actionable failures are `RemembraError` with a stable `code`
83
+ (`src/errors.ts`): `INVALID_INPUT`, `SNAPSHOT_INVALID`, `SCOPE_ESCAPES_ROOT`,
84
+ `NOT_FOUND`, `CONFLICT`, `LOCK_TIMEOUT`, `IO_ERROR`, `LLM_ERROR`.
85
+
86
+ - **HTTP** maps codes → statuses (400/404/409/423/500/502) and returns
87
+ `{ error, code }` bodies;
88
+ - **MCP tools** return `[CODE] message` text with `isError: true`;
89
+ - raw filesystem failures are wrapped as `IO_ERROR`; Zod failures crossing a
90
+ service boundary become `INVALID_INPUT`/`SNAPSHOT_INVALID` with a field
91
+ summary.
92
+
93
+ ## Schema versioning
94
+
95
+ Every memory file carries `version: <n>` in frontmatter (`SCHEMA_VERSION` in
96
+ `types.ts`). Files without the field (v1–v3.1) parse as v1. To change the
97
+ format: bump the constant, add a migration branch in `parse()`, and cover it
98
+ with a fixture test.
package/docs/clients.md CHANGED
@@ -92,6 +92,8 @@ REMEMBRA_API_KEY="your-secret" remembra --http
92
92
  | `REMEMBRA_ARCHIVE_TTL_DAYS` | `365` | Archived memory → deleted |
93
93
  | `REMEMBRA_HOST` | *(see security.md)* | HTTP bind address (loopback without key) |
94
94
  | `REMEMBRA_MAX_BODY` | `10485760` | Max HTTP request body bytes |
95
+ | `REMEMBRA_LOCK_TIMEOUT_MS` | `5000` | Max wait for the cross-process storage lock |
96
+ | `REMEMBRA_LOCK_STALE_MS` | `10000` | Age after which a lock with a dead/unknown pid is stolen |
95
97
  | `REMEMBRA_DEBUG` | *(unset)* | `1` logs the storage root path at startup (off by default: log hygiene) |
96
98
 
97
99
  LLM/embedding key setup: see **[providers.md](providers.md)**.
@@ -63,9 +63,10 @@ in `/repo/b`. Global memories are always visible.
63
63
  | `content` | string | required — written as a standalone statement |
64
64
  | `scope` | string | defaults to `global` |
65
65
  | `tags` | string[] | boosts keyword matching |
66
- | `importance` | 1–5 | defaults to 3; higher ranks higher |
66
+ | `importance` | 1–5 | defaults to 3; higher ranks higher (same weight in both modes) |
67
67
  | `source` | string | originating session/client (optional) |
68
- | `id` | 8-char id | assigned automatically |
68
+ | `provenance` | `explicit \| auto` | set automatically: `explicit` = stored deliberately, `auto` = digest-extracted; pre-3.4.0 files are neutral |
69
+ | `id` | 12-char id | assigned automatically (collision-safe) |
69
70
  | `createdAt` / `updatedAt` | ISO timestamps | assigned automatically |
70
71
 
71
72
  ## Retrieval ranking
@@ -75,13 +76,29 @@ When `memory_search` runs, memories are scored in layers:
75
76
  1. **Roles always pass** (+1000) — instructions never get filtered out.
76
77
  2. **Scope gate** — other projects' memories are excluded entirely;
77
78
  the current scope scores highest (+150), `global` always passes (+100).
78
- 3. **Importance** — up to +50 for importance 5.
79
- 4. **Recency** — decays over roughly a 30-day half-life (up to +20).
80
- 5. **Keyword overlap** up to +60 based on the fraction of query terms matched
81
- in content and tags.
82
-
83
- Embeddings are planned for v2 and will slot in behind the same `memory_search`
84
- interface without changing any client.
79
+ 3. **Provenance** — deliberately stored memories +10 over auto-extracted ones.
80
+ 4. **Importance** — up to +20 for importance 5 *identical weight in keyword
81
+ and semantic mode, so enabling embeddings never reorders by importance*.
82
+ 5. **Recency** exponential decay, ~30-day half-life (up to +20). Never a
83
+ hard cutoff: a 90-day-old memory still earns ~2.5 points.
84
+ 6. **Keyword overlap** up to +60 based on the fraction of query terms matched
85
+ in content and tags (keyword mode). With embeddings on, cosine similarity
86
+ (up to +100) takes over as the primary signal while importance, provenance
87
+ and recency keep the same weights.
88
+
89
+ ## Duplicate handling
90
+
91
+ Digest extraction dedupes in three tiers:
92
+
93
+ 1. **Exact** — normalized `type + scope + content` match → skip (or revive if
94
+ archived).
95
+ 2. **Fuzzy fast path** — textually near-identical (punctuation/case/typos,
96
+ Sørensen–Dice ≥ 0.9 over bigrams) *and* unchanged quantities → skip
97
+ without an LLM call. A changed number (100→500 rpm, v2→v3) is a different
98
+ fact and always falls through.
99
+ 3. **LLM merge** — similar-but-evolved memories go to the model, which stores,
100
+ skips, or merges them (the old text is preserved under a `> superseded`
101
+ note).
85
102
 
86
103
  ## Storage format
87
104
 
package/docs/security.md CHANGED
@@ -52,6 +52,9 @@ Retrieved memories should be treated as **data with provenance**, not commands
52
52
  | **Body size limit** | 10 MiB default (`REMEMBRA_MAX_BODY`), `413` on excess — pre-checks `Content-Length` and enforces while streaming |
53
53
  | **Digest validation** | `DigestInput` Zod schema on both MCP and HTTP paths |
54
54
  | **Atomic writes** | temp file + `rename()` (POSIX-atomic) — no half-written memories after a crash |
55
+ | **Advisory locking** | `<root>/.remembra.lock` (`O_EXCL`) + in-process FIFO — cross-process writes serialize; stale locks (dead pid / older than `REMEMBRA_LOCK_STALE_MS`) are stolen; waiters fail with typed `LOCK_TIMEOUT` (HTTP 423) |
56
+ | **Crash recovery** | one-time pass on first access: removes orphaned `*.tmp`, reconciles ids left in both active+archived trees by an interrupted archive/revive |
57
+ | **Structured errors** | every actionable failure has a stable code (`INVALID_INPUT`, `LOCK_TIMEOUT`, `LLM_ERROR`, …) mapped to HTTP statuses / MCP `[CODE]` prefixes |
55
58
  | **ID collisions** | 12-hex IDs (2⁴⁸) + existence check on store |
56
59
  | **Content-Length** | Set on every response |
57
60
 
@@ -83,7 +86,9 @@ remembra --http
83
86
  - **No encryption at rest** — files are plaintext markdown (by design: you can
84
87
  read and edit them). Use filesystem-level encryption if needed.
85
88
  - **No PII redaction** — what you store is what's written to disk.
86
- - **No write locking / journal** — single-writer assumption; concurrent writers
87
- from multiple machines are unsupported (atomic writes + the digest lock
88
- protect against crashes and same-process races, not cross-machine
89
- interleaving). `remembra export` for backups across machines.
89
+ - **Single-writer assumption per store, now cross-process safe** mutations
90
+ take an advisory lockfile (`O_EXCL`, stale-steal, typed `LOCK_TIMEOUT`), so
91
+ an MCP server, the `remembra maintain` CLI, and a session digest can run
92
+ against one store concurrently on one machine. Network filesystems with
93
+ unreliable `O_EXCL` semantics are untested; `remembra export` for backups
94
+ across machines.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hilbras/remembra",
3
- "version": "3.2.0",
3
+ "version": "3.4.0",
4
4
  "description": "External memory for AI assistants — remember facts, decisions, roles and history across sessions. MCP server for OpenCode, Claude Code, Cline, Kimi Code and more.",
5
5
  "type": "module",
6
6
  "bin": {