@199-bio/engram 0.6.0 → 0.7.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/README.md CHANGED
@@ -2,41 +2,53 @@
2
2
 
3
3
  **Give your AI a perfect memory.**
4
4
 
5
- Engram remembers everything you tell it—names, relationships, preferences, conversations—and recalls exactly what's relevant when you need it. Memories naturally fade over time unless they're important or frequently accessed, just like real memory.
5
+ Every conversation you have with your AI disappears the moment it ends. Names you've mentioned, preferences you've shared, the context of your life—all gone. You find yourself repeating the same information, re-explaining who people are, reminding it of things you've already said.
6
6
 
7
- Works with any LLM that supports MCP (Model Context Protocol)—Claude, GPT, Gemini, local models, and more.
7
+ Engram changes that.
8
+
9
+ It gives your AI the ability to remember. Not just store text, but truly remember—the way you do. Important things stick. Trivial things fade. And everything connects to everything else.
8
10
 
9
11
  > *An engram is a unit of cognitive information imprinted in a physical substance—the biological basis of memory.*
10
12
 
11
13
  ---
12
14
 
13
- ## What It Does
15
+ ## How It Works
14
16
 
15
- Tell your AI about your world:
17
+ Tell your AI something once:
16
18
 
17
19
  > "My colleague Sarah is allergic to shellfish and prefers window seats. She's leading the Q1 product launch."
18
20
 
19
- Later, ask:
21
+ Weeks later, ask:
20
22
 
21
23
  > "I'm booking a team lunch and flights for the offsite—what should I know?"
22
24
 
23
- Engram connects the dots—*avoid seafood restaurants, book Sarah a window seat, and she's probably busy with the launch*—and gives your AI the context to truly help.
25
+ Engram connects the dots. It remembers Sarah, her allergy, her seating preference, and her current workload. Your AI can now actually help—suggesting restaurants without shellfish, booking her a window seat, and noting she might be busy with the launch.
26
+
27
+ This isn't keyword matching. It's understanding.
28
+
29
+ ---
30
+
31
+ ## Memory That Feels Real
32
+
33
+ Engram models memory the way your brain does.
34
+
35
+ **Things fade.** A memory from six months ago that you've never thought about becomes harder to find. But something important—a name, a birthday, a preference—stays accessible even as time passes.
24
36
 
25
- **It's not just search. It's understanding.**
37
+ **Recall strengthens.** Every time a memory surfaces, it becomes more permanent. The things you think about often become the things you'll never forget.
38
+
39
+ **Everything connects.** People link to places. Places link to events. When you ask about one thing, related things come along for the ride. Ask about Sarah, and her company, her projects, and her preferences all surface together.
26
40
 
27
41
  ---
28
42
 
29
43
  ## Quick Start
30
44
 
31
- ### Install
45
+ Install globally:
32
46
 
33
47
  ```bash
34
48
  npm install -g @199-bio/engram
35
49
  ```
36
50
 
37
- ### Add to Your MCP Client
38
-
39
- **Claude Desktop** — add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
51
+ Add to **Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
40
52
 
41
53
  ```json
42
54
  {
@@ -52,383 +64,173 @@ npm install -g @199-bio/engram
52
64
  }
53
65
  ```
54
66
 
55
- **Claude Code:**
67
+ Or with **Claude Code**:
56
68
 
57
69
  ```bash
58
70
  claude mcp add engram -- npx -y @199-bio/engram
59
71
  ```
60
72
 
61
- **Other MCP clients** — point to the `engram` command. It speaks standard MCP over stdio.
62
-
63
- That's it. Your AI now has memory.
73
+ That's it. Your AI now remembers.
64
74
 
65
75
  ---
66
76
 
67
- ## How Memory Works
68
-
69
- Engram models memory like your brain does—important things stick, unimportant things fade, and everything connects.
70
-
71
- ```mermaid
72
- flowchart LR
73
- subgraph Input
74
- A[Your Message] --> B[Remember]
75
- end
76
-
77
- subgraph Storage
78
- B --> C[Memory Store]
79
- B --> D[Knowledge Graph]
80
- end
81
-
82
- subgraph Retrieval
83
- E[Your Question] --> F[Recall]
84
- F --> G[Keyword Search]
85
- F --> H[Semantic Search]
86
- F --> I[Graph Traversal]
87
- G & H & I --> J[Combine Results]
88
- end
77
+ ## What You Can Do
89
78
 
90
- C --> F
91
- D --> F
92
- J --> K[Relevant Memories]
93
- ```
94
-
95
- ### Three Key Ideas
96
-
97
- **1. Memories Fade Over Time**
98
-
99
- Just like real memory, things you haven't thought about recently become harder to recall. But important or emotional memories resist fading.
100
-
101
- ```
102
- Fresh memory (today) → Easy to recall
103
- Old but important memory → Still accessible
104
- Old, trivial memory → Fades away
105
- ```
79
+ Just talk naturally. Your AI handles the rest.
106
80
 
107
- **2. Accessing Memories Strengthens Them**
108
-
109
- Every time a memory is recalled, it becomes stronger and lasts longer. Frequently accessed memories become permanent.
110
-
111
- **3. Everything Connects**
112
-
113
- People, places, and things form a web of relationships. When you ask about Sarah, Engram also knows she works at Acme Corp and is leading the Q1 launch.
114
-
115
- ---
116
-
117
- ## The Memory Lifecycle
118
-
119
- ```mermaid
120
- flowchart TD
121
- subgraph "1. Capture"
122
- A[Conversation] --> B[Extract Key Info]
123
- B --> C[Store Memory]
124
- B --> D[Create Entities]
125
- B --> E[Link Relationships]
126
- end
127
-
128
- subgraph "2. Search"
129
- F[Query] --> G[Find by Keywords]
130
- F --> H[Find by Meaning]
131
- F --> I[Follow Connections]
132
- G & H & I --> J[Rank by Relevance + Recency]
133
- end
134
-
135
- subgraph "3. Consolidate"
136
- K[Raw Memories] --> L[Compress & Summarize]
137
- L --> M[Detect Contradictions]
138
- M --> N[Update Knowledge]
139
- end
140
-
141
- C --> F
142
- J --> K
143
- ```
81
+ **Store memories** by mentioning things:
82
+ - "Remember that my anniversary is March 15th"
83
+ - "Sarah prefers async communication"
84
+ - "I'm allergic to penicillin"
144
85
 
145
- ### Consolidation (Optional)
146
-
147
- With an API key, Engram can periodically compress old memories into summaries—like how sleep consolidates your memories. This keeps important information while reducing storage.
148
-
149
- ```bash
150
- # Run consolidation manually
151
- consolidate # Compresses old memories, finds contradictions
152
- ```
153
-
154
- ---
155
-
156
- ## What Makes It Special
157
-
158
- | Feature | Why It Matters |
159
- |---------|----------------|
160
- | **Hybrid Search** | Finds memories by keywords AND meaning |
161
- | **Knowledge Graph** | Understands relationships between people, places, things |
162
- | **Natural Forgetting** | Old, unimportant memories fade; important ones persist |
163
- | **Strengthening** | Frequently recalled memories become permanent |
164
- | **Consolidation** | Compresses old memories, detects contradictions |
165
- | **Fast** | ~50ms to recall, feels instant |
166
-
167
- ---
168
-
169
- ## How to Use
170
-
171
- Just talk naturally. Your AI will remember what matters.
172
-
173
- ### Storing Memories
174
-
175
- Say things like:
176
- - "Remember that Sarah is allergic to shellfish"
177
- - "My anniversary is March 15th"
178
- - "I prefer morning meetings, never schedule anything before 9am"
179
-
180
- The AI automatically extracts:
181
- - **Importance**: Key facts get higher priority
182
- - **Entities**: People, places, organizations mentioned
183
- - **Relationships**: How entities connect to each other
184
-
185
- ### Recalling
186
-
187
- Just ask:
86
+ **Recall memories** by asking:
188
87
  - "What do you know about Sarah?"
88
+ - "What are my allergies?"
189
89
  - "When is my anniversary?"
190
- - "What are my meeting preferences?"
191
-
192
- Results are ranked by:
193
- - **Relevance**: How well it matches your question
194
- - **Recency**: Recent memories surface first
195
- - **Importance**: High-priority info stays accessible
196
-
197
- ### The Knowledge Graph
198
-
199
- Engram doesn't just store text—it understands relationships:
200
-
201
- - **People**: Sarah, John, Dr. Martinez
202
- - **Places**: Office, Conference Room A, Seattle HQ
203
- - **Organizations**: Acme Corp, Stanford, FDA
204
- - **Connections**: Sarah → works at → Acme Corp
205
- - **Observations**: Sarah is allergic to shellfish
206
-
207
- This means when you ask about Sarah's work, Engram knows to also surface relevant information about Acme Corp.
208
-
209
- ---
210
-
211
- ## The Tools
212
-
213
- Your AI gets these capabilities:
214
90
 
215
- | Tool | What It Does |
216
- |------|--------------|
217
- | `remember` | Store something new (with importance, emotions, timing) |
218
- | `recall` | Find relevant memories (ranked by relevance + recency) |
219
- | `forget` | Remove a memory |
220
- | `create_entity` | Add a person, place, or concept |
221
- | `observe` | Note something about an entity |
222
- | `relate` | Connect two entities |
223
- | `query_entity` | Get everything about someone/something |
224
- | `list_entities` | See all people, places, etc. |
225
- | `stats` | Check memory statistics |
226
- | `consolidate` | Compress old memories, find contradictions |
227
- | `engram_web` | Launch visual memory browser |
228
-
229
- ---
230
-
231
- ## Examples
232
-
233
- ### Personal Assistant
234
-
235
- ```
236
- You: "Remember that my anniversary is March 15th and my partner loves Italian food"
237
- AI: ✓ Stored. I've noted this as an important date with preferences.
238
-
239
- You: "I need to plan something special next month"
240
- AI: Your anniversary is coming up on March 15th! Given your partner's love for
241
- Italian food, perhaps a nice Italian restaurant reservation?
242
- ```
243
-
244
- ### Relationship Context
245
-
246
- ```
247
- You: "My friend David lives in Austin and always recommends great coffee shops."
248
- AI: ✓ Stored. Created entity: David (person), with observation about Austin and coffee expertise.
249
-
250
- You: "I'm visiting Austin next week"
251
- AI: David is in Austin—he'd probably have great coffee shop recommendations!
252
- Want me to remind you to ask him?
253
- ```
254
-
255
- ### Professional Memory
256
-
257
- ```
258
- You: "John Chen is our VP of Engineering. He prefers async communication and hates meetings before 11am."
259
- Claude: ✓ Stored. Created entity: John Chen (person, VP of Engineering).
260
-
261
- You: "I need to schedule a sync with John"
262
- Claude: Given John's preferences, I'd suggest a late morning slot, maybe 11:30am,
263
- or an async Loom video if it doesn't require real-time discussion.
264
- ```
91
+ **Build a knowledge graph** of your world:
92
+ - People, places, organizations, and how they connect
93
+ - Observations about each entity
94
+ - Relationships that span your entire life
265
95
 
266
96
  ---
267
97
 
268
98
  ## Privacy
269
99
 
270
- **Your memories stay on your machine.**
271
-
272
- - All data stored locally in `~/.engram/`
273
- - No external APIs required for core functionality
274
- - Consolidation uses Anthropic API (optional, requires key)
275
- - You own your data completely
100
+ Your memories stay on your machine. Everything is stored locally in `~/.engram/`. The only external call is optional—if you provide an API key, Engram can periodically compress old memories into summaries. But the core functionality works entirely offline.
276
101
 
277
102
  ---
278
103
 
279
- ## Technical Details
104
+ ## The Details
280
105
 
281
106
  <details>
282
- <summary>How Search Works</summary>
107
+ <summary><strong>Available Tools</strong></summary>
283
108
 
284
- Engram uses a three-layer retrieval system:
109
+ Your AI gets these capabilities:
285
110
 
286
- 1. **BM25 (Keyword Search)**: SQLite FTS5 finds exact matches—names, dates, specific phrases
287
- 2. **Semantic Search**: Neural embeddings find conceptually related memories
288
- 3. **Knowledge Graph**: Entity relationships expand context
111
+ | Tool | Purpose |
112
+ |------|---------|
113
+ | `remember` | Store new information with importance, emotions, and timing |
114
+ | `recall` | Find relevant memories ranked by relevance and recency |
115
+ | `forget` | Remove a specific memory |
116
+ | `create_entity` | Add a person, place, or concept to the knowledge graph |
117
+ | `observe` | Record a fact about an entity |
118
+ | `relate` | Connect two entities (e.g., "works at", "married to") |
119
+ | `query_entity` | Get everything known about someone or something |
120
+ | `list_entities` | See all tracked people, places, and things |
121
+ | `stats` | View memory statistics |
122
+ | `consolidate` | Compress old memories and detect contradictions |
123
+ | `engram_web` | Launch a visual memory browser |
289
124
 
290
- These are fused using Reciprocal Rank Fusion (RRF), then adjusted for:
291
- - **Retention**: How fresh is this memory?
292
- - **Salience**: How important/emotional is it?
125
+ </details>
293
126
 
294
- ```mermaid
295
- flowchart LR
296
- Q[Query] --> BM25
297
- Q --> Semantic
298
- Q --> Graph
127
+ <details>
128
+ <summary><strong>How Search Works</strong></summary>
299
129
 
300
- BM25 --> RRF[Rank Fusion]
301
- Semantic --> RRF
302
- Graph --> RRF
130
+ Engram uses three search methods simultaneously:
303
131
 
304
- RRF --> Decay[Apply Time Decay]
305
- Decay --> Salience[Weight by Importance]
306
- Salience --> Results[Final Ranking]
307
- ```
132
+ 1. **Keywords** SQLite FTS5 finds exact matches for names, dates, and phrases
133
+ 2. **Meaning** Neural embeddings find conceptually related content
134
+ 3. **Connections** The knowledge graph expands to related entities
135
+
136
+ Results are fused together, then adjusted for how recent and important each memory is. Fresh memories surface first. Important memories resist fading.
308
137
 
309
138
  </details>
310
139
 
311
140
  <details>
312
- <summary>How Forgetting Works</summary>
141
+ <summary><strong>How Forgetting Works</strong></summary>
313
142
 
314
- Memories follow an exponential decay curve (inspired by the Ebbinghaus forgetting curve):
143
+ Memories follow an exponential decay curve:
315
144
 
316
145
  ```
317
146
  Retention = e^(-time / stability)
318
147
  ```
319
148
 
320
- Where:
321
- - **time**: Days since last access
322
- - **stability**: Memory strength (increases each time you recall it)
149
+ - **Time** is days since the memory was last accessed
150
+ - **Stability** is memory strength, which increases each time you recall something
323
151
 
324
- High-importance and high-emotion memories decay slower. Frequently accessed memories become essentially permanent.
152
+ High-importance and emotionally significant memories decay slower. Frequently accessed memories become essentially permanent.
325
153
 
326
154
  </details>
327
155
 
328
156
  <details>
329
- <summary>How Consolidation Works</summary>
330
-
331
- ```mermaid
332
- flowchart TD
333
- A[Old Memories] --> B{Important?}
334
- B -->|Yes| C[Keep as-is]
335
- B -->|No| D[Group Similar]
336
- D --> E[Summarize with AI]
337
- E --> F[Create Digest]
338
- F --> G[Archive Originals]
339
-
340
- H[All Memories] --> I[Find Contradictions]
341
- I --> J[Flag for Review]
342
- ```
157
+ <summary><strong>How Consolidation Works</strong></summary>
158
+
159
+ With an API key, Engram can compress old memories—like how sleep consolidates your experiences into long-term storage.
343
160
 
344
- Consolidation:
345
- 1. Groups related low-importance memories
161
+ 1. Groups related low-importance memories together
346
162
  2. Creates AI-generated summaries (digests)
347
163
  3. Detects contradictory information
348
- 4. Archives original memories
164
+ 4. Archives the originals
349
165
 
350
- Requires `ANTHROPIC_API_KEY` environment variable.
166
+ This keeps storage efficient while preserving everything important.
351
167
 
352
168
  </details>
353
169
 
354
170
  <details>
355
- <summary>Architecture</summary>
171
+ <summary><strong>Architecture</strong></summary>
356
172
 
357
173
  ```
358
174
  engram/
359
175
  ├── src/
360
- │ ├── index.ts # MCP server entry
361
- │ ├── storage/
362
- │ └── database.ts # SQLite + FTS5 + temporal fields
363
- │ ├── graph/
364
- │ │ └── knowledge-graph.ts
176
+ │ ├── index.ts # MCP server
177
+ │ ├── storage/database.ts # SQLite with temporal fields
178
+ ├── graph/knowledge-graph.ts
365
179
  │ ├── retrieval/
366
180
  │ │ ├── colbert.ts # Semantic search
367
- │ │ └── hybrid.ts # RRF + decay + salience
368
- │ ├── consolidation/
369
- └── consolidator.ts # Memory compression
370
- │ └── web/
371
- │ └── server.ts # Visual browser
181
+ │ │ └── hybrid.ts # Fusion + decay + salience
182
+ │ ├── consolidation/consolidator.ts
183
+ │ └── web/server.ts # Visual browser
372
184
  ```
373
185
 
374
186
  </details>
375
187
 
376
188
  <details>
377
- <summary>Building from Source</summary>
189
+ <summary><strong>Configuration</strong></summary>
190
+
191
+ Environment variables:
192
+
193
+ | Variable | Purpose | Default |
194
+ |----------|---------|---------|
195
+ | `ENGRAM_DB_PATH` | Where to store data | `~/.engram/` |
196
+ | `ANTHROPIC_API_KEY` | Enable consolidation | None (optional) |
197
+
198
+ </details>
199
+
200
+ <details>
201
+ <summary><strong>Building from Source</strong></summary>
378
202
 
379
203
  ```bash
380
204
  git clone https://github.com/199-biotechnologies/engram.git
381
205
  cd engram
382
206
  npm install
383
207
  npm run build
384
-
385
- # Install globally from local build
386
208
  npm install -g .
387
209
  ```
388
210
 
389
- **Python Dependencies** (for semantic search):
211
+ For semantic search, install Python dependencies:
212
+
390
213
  ```bash
391
214
  pip install ragatouille torch
392
215
  ```
393
216
 
394
- If Python isn't available, Engram falls back to a simpler retriever automatically.
217
+ If unavailable, Engram falls back to keyword-only search automatically.
395
218
 
396
219
  </details>
397
220
 
398
221
  <details>
399
- <summary>Configuration</summary>
400
-
401
- Environment variables:
402
- - `ENGRAM_DB_PATH`: Database location (default: `~/.engram/`)
403
- - `ANTHROPIC_API_KEY`: Required for consolidation features
404
-
405
- Claude Desktop full config:
406
- ```json
407
- {
408
- "mcpServers": {
409
- "engram": {
410
- "command": "engram",
411
- "env": {
412
- "ENGRAM_DB_PATH": "/custom/path/",
413
- "ANTHROPIC_API_KEY": "sk-ant-..."
414
- }
415
- }
416
- }
417
- }
418
- ```
419
-
420
- </details>
421
-
422
- <details>
423
- <summary>Performance</summary>
222
+ <summary><strong>Performance</strong></summary>
424
223
 
425
224
  On M1 MacBook Air:
426
- - **remember**: ~100ms
427
- - **recall**: ~50ms (includes decay calculation)
428
- - **graph queries**: ~5ms
429
- - **consolidate**: ~2-5s per batch (API call)
430
225
 
431
- Database size: ~1KB per memory (text + embeddings + graph data)
226
+ | Operation | Time |
227
+ |-----------|------|
228
+ | Remember | ~100ms |
229
+ | Recall | ~50ms |
230
+ | Graph queries | ~5ms |
231
+ | Consolidate | ~2-5s per batch |
232
+
233
+ Storage: ~1KB per memory.
432
234
 
433
235
  </details>
434
236
 
@@ -436,14 +238,12 @@ Database size: ~1KB per memory (text + embeddings + graph data)
436
238
 
437
239
  ## Roadmap
438
240
 
439
- - [x] Core MCP server
440
- - [x] Hybrid search (BM25 + Semantic)
441
- - [x] Knowledge graph
442
- - [x] Entity extraction
443
- - [x] Temporal memory decay
444
- - [x] Memory consolidation
445
- - [x] Web dashboard
446
- - [ ] Export/import
241
+ - [x] Hybrid search (keywords + semantics)
242
+ - [x] Knowledge graph with relationships
243
+ - [x] Memory decay and strengthening
244
+ - [x] Consolidation with contradiction detection
245
+ - [x] Web interface
246
+ - [ ] Export and import
447
247
  - [ ] Scheduled consolidation
448
248
 
449
249
  ---
@@ -455,10 +255,10 @@ Founder, [199 Biotechnologies](https://199bio.com)
455
255
 
456
256
  ## License
457
257
 
458
- MIT — use it however you want.
258
+ MIT
459
259
 
460
260
  ---
461
261
 
462
262
  <p align="center">
463
- <i>Built with care by <a href="https://github.com/199-biotechnologies">199 Biotechnologies</a></i>
263
+ <i>Built by <a href="https://github.com/199-biotechnologies">199 Biotechnologies</a></i>
464
264
  </p>
package/dist/index.js CHANGED
@@ -59,7 +59,7 @@ const server = new Server({
59
59
  const TOOLS = [
60
60
  {
61
61
  name: "remember",
62
- description: "Store information with entities and relationships. Extract key people, organizations, and places from the content and pass them as entities. Include relationships between entities when mentioned (e.g., 'works_at', 'lives_in', 'knows').",
62
+ description: "Store NEW information the user shares. Extract entities (people, organizations, places) and relationships. Do NOT use for corrections - use edit_memory instead. Do NOT use before checking if info already exists - use recall first.",
63
63
  inputSchema: {
64
64
  type: "object",
65
65
  properties: {
@@ -69,14 +69,14 @@ const TOOLS = [
69
69
  },
70
70
  importance: {
71
71
  type: "number",
72
- description: "0-1 score. Use 0.8+ for key facts (names, preferences, important events), 0.5 for general info, 0.3- for trivial mentions",
72
+ description: "0-1 score with anchors: 0.9=core identity (name, birthday, family), 0.8=major preferences/life events, 0.6=notable facts, 0.5=general info (default), 0.3=casual mentions, 0.1=trivial/ephemeral",
73
73
  minimum: 0,
74
74
  maximum: 1,
75
75
  default: 0.5,
76
76
  },
77
77
  emotional_weight: {
78
78
  type: "number",
79
- description: "0-1 emotional significance. Use 0.8+ for emotionally charged content, celebrations, losses. Affects memory retention.",
79
+ description: "0-1 emotional significance with anchors: 0.9=major life events (births, deaths, achievements), 0.7=celebrations/disappointments, 0.5=neutral (default), 0.3=mild sentiment, 0.1=purely factual",
80
80
  minimum: 0,
81
81
  maximum: 1,
82
82
  default: 0.5,
@@ -91,7 +91,7 @@ const TOOLS = [
91
91
  items: {
92
92
  type: "object",
93
93
  properties: {
94
- name: { type: "string", description: "Entity name (e.g., 'Boris Djordjevic', 'Google', 'Paris')" },
94
+ name: { type: "string", description: "Entity name (e.g., 'John Smith', 'Google', 'Paris')" },
95
95
  type: { type: "string", enum: ["person", "organization", "place"], description: "Entity type" },
96
96
  },
97
97
  required: ["name", "type"],
@@ -123,7 +123,7 @@ const TOOLS = [
123
123
  },
124
124
  {
125
125
  name: "recall",
126
- description: "PRIMARY SEARCH TOOL. Use this FIRST when answering any question about stored information. Searches across all memories using semantic understanding, keywords, and knowledge graph connections. Returns relevant memories with context.",
126
+ description: "Search stored memories. Use FIRST before answering questions about the user, their preferences, history, or anything previously discussed. Also use before remember to check if information already exists.",
127
127
  inputSchema: {
128
128
  type: "object",
129
129
  properties: {
@@ -154,13 +154,13 @@ const TOOLS = [
154
154
  },
155
155
  {
156
156
  name: "forget",
157
- description: "Delete a specific memory by its ID. Use only when explicitly asked to remove information.",
157
+ description: "Delete a memory by its ID. Use when user explicitly asks to remove or forget specific stored information. Get the memory ID from recall results first.",
158
158
  inputSchema: {
159
159
  type: "object",
160
160
  properties: {
161
161
  id: {
162
162
  type: "string",
163
- description: "The memory ID to remove",
163
+ description: "The memory ID to delete (get this from recall results)",
164
164
  },
165
165
  },
166
166
  required: ["id"],
@@ -173,6 +173,37 @@ const TOOLS = [
173
173
  openWorldHint: false,
174
174
  },
175
175
  },
176
+ {
177
+ name: "edit_memory",
178
+ description: "Correct or update an existing memory. Use instead of forget+remember when fixing mistakes, updating outdated info, or adjusting importance. Preserves the memory's history and relationships.",
179
+ inputSchema: {
180
+ type: "object",
181
+ properties: {
182
+ id: {
183
+ type: "string",
184
+ description: "The memory ID to edit (get this from recall results)",
185
+ },
186
+ content: {
187
+ type: "string",
188
+ description: "New content for the memory (replaces existing). Omit to keep current content.",
189
+ },
190
+ importance: {
191
+ type: "number",
192
+ description: "New importance (0-1): 0.9=core identity, 0.8=major, 0.5=normal, 0.3=minor. Omit to keep current.",
193
+ minimum: 0,
194
+ maximum: 1,
195
+ },
196
+ },
197
+ required: ["id"],
198
+ },
199
+ annotations: {
200
+ title: "Edit Memory",
201
+ readOnlyHint: false,
202
+ destructiveHint: false,
203
+ idempotentHint: true,
204
+ openWorldHint: false,
205
+ },
206
+ },
176
207
  {
177
208
  name: "engram_web",
178
209
  description: "Launch the Engram web interface for browsing, searching, and editing memories visually. Returns a URL to open in your browser.",
@@ -196,7 +227,7 @@ const TOOLS = [
196
227
  },
197
228
  {
198
229
  name: "consolidate",
199
- description: "Run memory consolidation to compress episodes into memories and memories into digests. Like sleep for the memory system. Use periodically or when requested.",
230
+ description: "Run memory consolidation to compress episodes into memories and memories into digests. Like sleep for the memory system. Requires ANTHROPIC_API_KEY. Use periodically or when explicitly requested.",
200
231
  inputSchema: {
201
232
  type: "object",
202
233
  properties: {
@@ -213,7 +244,7 @@ const TOOLS = [
213
244
  readOnlyHint: false,
214
245
  destructiveHint: false,
215
246
  idempotentHint: false,
216
- openWorldHint: false,
247
+ openWorldHint: true, // Calls Anthropic API for consolidation
217
248
  },
218
249
  },
219
250
  ];
@@ -317,13 +348,62 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
317
348
  }
318
349
  // Remove from semantic index
319
350
  await search.removeFromIndex(id);
320
- // Delete from database
321
- db.deleteMemory(id);
351
+ // Soft-delete: mark as disabled instead of hard delete
352
+ db.updateMemory(id, { disabled: true });
353
+ return {
354
+ content: [
355
+ {
356
+ type: "text",
357
+ text: JSON.stringify({ success: true, disabled_id: id, message: "Memory disabled (soft-deleted)" }),
358
+ },
359
+ ],
360
+ };
361
+ }
362
+ case "edit_memory": {
363
+ const { id, content, importance } = args;
364
+ const memory = db.getMemory(id);
365
+ if (!memory) {
366
+ return {
367
+ content: [
368
+ {
369
+ type: "text",
370
+ text: JSON.stringify({ success: false, error: "Memory not found" }),
371
+ },
372
+ ],
373
+ };
374
+ }
375
+ // Build updates object
376
+ const updates = {};
377
+ if (content !== undefined)
378
+ updates.content = content;
379
+ if (importance !== undefined)
380
+ updates.importance = importance;
381
+ if (Object.keys(updates).length === 0) {
382
+ return {
383
+ content: [
384
+ {
385
+ type: "text",
386
+ text: JSON.stringify({ success: false, error: "No updates provided" }),
387
+ },
388
+ ],
389
+ };
390
+ }
391
+ // Update the memory
392
+ const updated = db.updateMemory(id, updates);
393
+ // Re-index if content changed
394
+ if (content !== undefined && updated) {
395
+ await search.removeFromIndex(id);
396
+ await search.indexMemory(updated);
397
+ }
322
398
  return {
323
399
  content: [
324
400
  {
325
401
  type: "text",
326
- text: JSON.stringify({ success: true, deleted_id: id }),
402
+ text: JSON.stringify({
403
+ success: true,
404
+ memory_id: id,
405
+ updated_fields: Object.keys(updates),
406
+ }),
327
407
  },
328
408
  ],
329
409
  };
@@ -1 +1 @@
1
- {"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/retrieval/hybrid.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAA0B,MAAM,cAAc,CAAC;AAEzF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAwDD,qBAAa,YAAY;IAErB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,SAAS;gBAFT,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,gBAAgB,GAAG,eAAe;IAGvD;;;;;OAKG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,OAAO,CAAC;KACnB,GACL,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAyIhC;;OAEG;YACW,UAAU;IASxB;;OAEG;YACW,cAAc;IAS5B;;OAEG;YACW,WAAW;IAmBzB;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhD;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAYhD;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGvD"}
1
+ {"version":3,"file":"hybrid.d.ts","sourceRoot":"","sources":["../../src/retrieval/hybrid.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAA0B,MAAM,cAAc,CAAC;AAEzF,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAwDD,qBAAa,YAAY;IAErB,OAAO,CAAC,EAAE;IACV,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,SAAS;gBAFT,EAAE,EAAE,cAAc,EAClB,KAAK,EAAE,cAAc,EACrB,SAAS,EAAE,gBAAgB,GAAG,eAAe;IAGvD;;;;;OAKG;IACG,MAAM,CACV,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,OAAO,CAAC;KACnB,GACL,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAyIhC;;OAEG;YACW,UAAU;IASxB;;OAEG;YACW,cAAc;IAS5B;;;OAGG;IACH,OAAO,CAAC,aAAa;IAQrB;;;OAGG;YACW,WAAW;IA8BzB;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOhD;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAYhD;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGvD"}
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/storage/database.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@199-bio/engram",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Give Claude a perfect memory. Local-first MCP server with hybrid search.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -74,8 +74,8 @@ const EPISODE_EXTRACTION_SYSTEM = `You are extracting structured memories from a
74
74
  "importance": 0.5,
75
75
  "emotional_weight": 0.5,
76
76
  "event_time": "2024-12-01 or null if not mentioned",
77
- "entities": [{"name": "Boris", "type": "person"}],
78
- "relationships": [{"from": "Boris", "to": "Google", "type": "works_at"}]
77
+ "entities": [{"name": "John", "type": "person"}],
78
+ "relationships": [{"from": "John", "to": "Acme Corp", "type": "works_at"}]
79
79
  }
80
80
  ]
81
81
  }
package/src/index.ts CHANGED
@@ -81,7 +81,7 @@ const TOOLS = [
81
81
  {
82
82
  name: "remember",
83
83
  description:
84
- "Store information with entities and relationships. Extract key people, organizations, and places from the content and pass them as entities. Include relationships between entities when mentioned (e.g., 'works_at', 'lives_in', 'knows').",
84
+ "Store NEW information the user shares. Extract entities (people, organizations, places) and relationships. Do NOT use for corrections - use edit_memory instead. Do NOT use before checking if info already exists - use recall first.",
85
85
  inputSchema: {
86
86
  type: "object" as const,
87
87
  properties: {
@@ -91,14 +91,14 @@ const TOOLS = [
91
91
  },
92
92
  importance: {
93
93
  type: "number",
94
- description: "0-1 score. Use 0.8+ for key facts (names, preferences, important events), 0.5 for general info, 0.3- for trivial mentions",
94
+ description: "0-1 score with anchors: 0.9=core identity (name, birthday, family), 0.8=major preferences/life events, 0.6=notable facts, 0.5=general info (default), 0.3=casual mentions, 0.1=trivial/ephemeral",
95
95
  minimum: 0,
96
96
  maximum: 1,
97
97
  default: 0.5,
98
98
  },
99
99
  emotional_weight: {
100
100
  type: "number",
101
- description: "0-1 emotional significance. Use 0.8+ for emotionally charged content, celebrations, losses. Affects memory retention.",
101
+ description: "0-1 emotional significance with anchors: 0.9=major life events (births, deaths, achievements), 0.7=celebrations/disappointments, 0.5=neutral (default), 0.3=mild sentiment, 0.1=purely factual",
102
102
  minimum: 0,
103
103
  maximum: 1,
104
104
  default: 0.5,
@@ -113,7 +113,7 @@ const TOOLS = [
113
113
  items: {
114
114
  type: "object",
115
115
  properties: {
116
- name: { type: "string", description: "Entity name (e.g., 'Boris Djordjevic', 'Google', 'Paris')" },
116
+ name: { type: "string", description: "Entity name (e.g., 'John Smith', 'Google', 'Paris')" },
117
117
  type: { type: "string", enum: ["person", "organization", "place"], description: "Entity type" },
118
118
  },
119
119
  required: ["name", "type"],
@@ -146,7 +146,7 @@ const TOOLS = [
146
146
  {
147
147
  name: "recall",
148
148
  description:
149
- "PRIMARY SEARCH TOOL. Use this FIRST when answering any question about stored information. Searches across all memories using semantic understanding, keywords, and knowledge graph connections. Returns relevant memories with context.",
149
+ "Search stored memories. Use FIRST before answering questions about the user, their preferences, history, or anything previously discussed. Also use before remember to check if information already exists.",
150
150
  inputSchema: {
151
151
  type: "object" as const,
152
152
  properties: {
@@ -177,13 +177,13 @@ const TOOLS = [
177
177
  },
178
178
  {
179
179
  name: "forget",
180
- description: "Delete a specific memory by its ID. Use only when explicitly asked to remove information.",
180
+ description: "Delete a memory by its ID. Use when user explicitly asks to remove or forget specific stored information. Get the memory ID from recall results first.",
181
181
  inputSchema: {
182
182
  type: "object" as const,
183
183
  properties: {
184
184
  id: {
185
185
  type: "string",
186
- description: "The memory ID to remove",
186
+ description: "The memory ID to delete (get this from recall results)",
187
187
  },
188
188
  },
189
189
  required: ["id"],
@@ -196,6 +196,37 @@ const TOOLS = [
196
196
  openWorldHint: false,
197
197
  },
198
198
  },
199
+ {
200
+ name: "edit_memory",
201
+ description: "Correct or update an existing memory. Use instead of forget+remember when fixing mistakes, updating outdated info, or adjusting importance. Preserves the memory's history and relationships.",
202
+ inputSchema: {
203
+ type: "object" as const,
204
+ properties: {
205
+ id: {
206
+ type: "string",
207
+ description: "The memory ID to edit (get this from recall results)",
208
+ },
209
+ content: {
210
+ type: "string",
211
+ description: "New content for the memory (replaces existing). Omit to keep current content.",
212
+ },
213
+ importance: {
214
+ type: "number",
215
+ description: "New importance (0-1): 0.9=core identity, 0.8=major, 0.5=normal, 0.3=minor. Omit to keep current.",
216
+ minimum: 0,
217
+ maximum: 1,
218
+ },
219
+ },
220
+ required: ["id"],
221
+ },
222
+ annotations: {
223
+ title: "Edit Memory",
224
+ readOnlyHint: false,
225
+ destructiveHint: false,
226
+ idempotentHint: true,
227
+ openWorldHint: false,
228
+ },
229
+ },
199
230
  {
200
231
  name: "engram_web",
201
232
  description: "Launch the Engram web interface for browsing, searching, and editing memories visually. Returns a URL to open in your browser.",
@@ -219,7 +250,7 @@ const TOOLS = [
219
250
  },
220
251
  {
221
252
  name: "consolidate",
222
- description: "Run memory consolidation to compress episodes into memories and memories into digests. Like sleep for the memory system. Use periodically or when requested.",
253
+ description: "Run memory consolidation to compress episodes into memories and memories into digests. Like sleep for the memory system. Requires ANTHROPIC_API_KEY. Use periodically or when explicitly requested.",
223
254
  inputSchema: {
224
255
  type: "object" as const,
225
256
  properties: {
@@ -236,7 +267,7 @@ const TOOLS = [
236
267
  readOnlyHint: false,
237
268
  destructiveHint: false,
238
269
  idempotentHint: false,
239
- openWorldHint: false,
270
+ openWorldHint: true, // Calls Anthropic API for consolidation
240
271
  },
241
272
  },
242
273
  ];
@@ -375,14 +406,72 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
375
406
  // Remove from semantic index
376
407
  await search.removeFromIndex(id);
377
408
 
378
- // Delete from database
379
- db.deleteMemory(id);
409
+ // Soft-delete: mark as disabled instead of hard delete
410
+ db.updateMemory(id, { disabled: true });
380
411
 
381
412
  return {
382
413
  content: [
383
414
  {
384
415
  type: "text" as const,
385
- text: JSON.stringify({ success: true, deleted_id: id }),
416
+ text: JSON.stringify({ success: true, disabled_id: id, message: "Memory disabled (soft-deleted)" }),
417
+ },
418
+ ],
419
+ };
420
+ }
421
+
422
+ case "edit_memory": {
423
+ const { id, content, importance } = args as {
424
+ id: string;
425
+ content?: string;
426
+ importance?: number;
427
+ };
428
+
429
+ const memory = db.getMemory(id);
430
+ if (!memory) {
431
+ return {
432
+ content: [
433
+ {
434
+ type: "text" as const,
435
+ text: JSON.stringify({ success: false, error: "Memory not found" }),
436
+ },
437
+ ],
438
+ };
439
+ }
440
+
441
+ // Build updates object
442
+ const updates: { content?: string; importance?: number } = {};
443
+ if (content !== undefined) updates.content = content;
444
+ if (importance !== undefined) updates.importance = importance;
445
+
446
+ if (Object.keys(updates).length === 0) {
447
+ return {
448
+ content: [
449
+ {
450
+ type: "text" as const,
451
+ text: JSON.stringify({ success: false, error: "No updates provided" }),
452
+ },
453
+ ],
454
+ };
455
+ }
456
+
457
+ // Update the memory
458
+ const updated = db.updateMemory(id, updates);
459
+
460
+ // Re-index if content changed
461
+ if (content !== undefined && updated) {
462
+ await search.removeFromIndex(id);
463
+ await search.indexMemory(updated);
464
+ }
465
+
466
+ return {
467
+ content: [
468
+ {
469
+ type: "text" as const,
470
+ text: JSON.stringify({
471
+ success: true,
472
+ memory_id: id,
473
+ updated_fields: Object.keys(updates),
474
+ }),
386
475
  },
387
476
  ],
388
477
  };
@@ -102,7 +102,7 @@ export class HybridSearch {
102
102
  includeGraph = true,
103
103
  bm25Weight = 1.0,
104
104
  semanticWeight = 1.0,
105
- graphWeight = 0.5,
105
+ graphWeight = 0.3,
106
106
  useReranking = true, // Default: reranking mode for better quality
107
107
  } = options;
108
108
 
@@ -257,22 +257,46 @@ export class HybridSearch {
257
257
  }
258
258
  }
259
259
 
260
+ /**
261
+ * Normalize a word for matching: lowercase, strip punctuation, handle possessives
262
+ * "John's" → "john", "Paris!" → "paris", "U.S.A." → "usa"
263
+ */
264
+ private normalizeWord(word: string): string {
265
+ return word
266
+ .toLowerCase()
267
+ .replace(/'s$/, '') // Remove possessives: john's → john
268
+ .replace(/[^\p{L}\p{N}]/gu, '') // Keep only letters/numbers (Unicode-safe)
269
+ .trim();
270
+ }
271
+
260
272
  /**
261
273
  * Graph-based search: find known entities in query, traverse graph
274
+ * Uses normalized word matching to handle punctuation and possessives
262
275
  */
263
276
  private async searchGraph(query: string): Promise<string[]> {
264
- // Find known entities whose names appear in the query
265
- const queryLower = query.toLowerCase();
277
+ // Tokenize and normalize query words
278
+ const queryWords = query.split(/\s+/)
279
+ .map(w => this.normalizeWord(w))
280
+ .filter(w => w.length > 0);
281
+ const queryWordSet = new Set(queryWords);
282
+
283
+ // Find entities whose normalized names match query words
266
284
  const allEntities = this.graph.listEntities(undefined, 500);
267
- const matchedEntities = allEntities.filter(e =>
268
- queryLower.includes(e.name.toLowerCase())
269
- );
285
+ const matchedEntities = allEntities.filter(e => {
286
+ const entityWords = e.name.split(/\s+/)
287
+ .map(w => this.normalizeWord(w))
288
+ .filter(w => w.length > 0);
289
+ // Entity matches if ALL its words appear in the query
290
+ // e.g., "john" matches entity "John" but not "John Smith"
291
+ // e.g., "john's friend" matches entity "John" (possessive handled)
292
+ return entityWords.every(w => queryWordSet.has(w));
293
+ });
270
294
 
271
295
  const memoryIds = new Set<string>();
272
296
 
273
297
  for (const entity of matchedEntities) {
274
- // Find related memory IDs through graph traversal
275
- const relatedIds = this.graph.findRelatedMemoryIds(entity.name, 2);
298
+ // Depth 1: only directly related memories, not transitive connections
299
+ const relatedIds = this.graph.findRelatedMemoryIds(entity.name, 1);
276
300
  relatedIds.forEach(id => memoryIds.add(id));
277
301
  }
278
302
 
@@ -19,6 +19,7 @@ export interface Memory {
19
19
  last_accessed: Date | null;
20
20
  stability: number; // Ebbinghaus stability score (increases with recalls)
21
21
  emotional_weight: number; // Salience: emotional significance 0-1
22
+ disabled: boolean; // Soft-deleted: excluded from search results
22
23
  }
23
24
 
24
25
  /**
@@ -112,23 +113,17 @@ export class EngramDatabase {
112
113
 
113
114
  private initialize(): void {
114
115
  // Memories table (Semantic Memory - neocortex analog)
116
+ // Create table with base columns first (for new databases)
115
117
  this.db.exec(`
116
118
  CREATE TABLE IF NOT EXISTS memories (
117
119
  id TEXT PRIMARY KEY,
118
120
  content TEXT NOT NULL,
119
121
  source TEXT DEFAULT 'conversation',
120
122
  timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
121
- event_time DATETIME,
122
123
  importance REAL DEFAULT 0.5,
123
124
  access_count INTEGER DEFAULT 0,
124
- last_accessed DATETIME,
125
- stability REAL DEFAULT 1.0,
126
- emotional_weight REAL DEFAULT 0.5
125
+ last_accessed DATETIME
127
126
  );
128
-
129
- CREATE INDEX IF NOT EXISTS idx_memories_timestamp ON memories(timestamp);
130
- CREATE INDEX IF NOT EXISTS idx_memories_importance ON memories(importance);
131
- CREATE INDEX IF NOT EXISTS idx_memories_event_time ON memories(event_time);
132
127
  `);
133
128
 
134
129
  // Episodes table (Episodic Memory - hippocampal buffer)
@@ -142,15 +137,22 @@ export class EngramDatabase {
142
137
  timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
143
138
  consolidated INTEGER DEFAULT 0
144
139
  );
140
+ `);
141
+
142
+ // IMPORTANT: Migrate schema BEFORE creating indexes on new columns
143
+ // This adds event_time, stability, emotional_weight to existing databases
144
+ this.migrateSchema();
145
145
 
146
+ // Now create all indexes (after migration ensures columns exist)
147
+ this.db.exec(`
148
+ CREATE INDEX IF NOT EXISTS idx_memories_timestamp ON memories(timestamp);
149
+ CREATE INDEX IF NOT EXISTS idx_memories_importance ON memories(importance);
150
+ CREATE INDEX IF NOT EXISTS idx_memories_event_time ON memories(event_time);
146
151
  CREATE INDEX IF NOT EXISTS idx_episodes_session ON episodes(session_id);
147
152
  CREATE INDEX IF NOT EXISTS idx_episodes_consolidated ON episodes(consolidated);
148
153
  CREATE INDEX IF NOT EXISTS idx_episodes_timestamp ON episodes(timestamp);
149
154
  `);
150
155
 
151
- // Migrate existing tables: add new columns if they don't exist
152
- this.migrateSchema();
153
-
154
156
  // FTS5 for BM25 search
155
157
  this.db.exec(`
156
158
  CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
@@ -286,6 +288,9 @@ export class EngramDatabase {
286
288
  if (!memoryColumns.has("emotional_weight")) {
287
289
  this.db.exec("ALTER TABLE memories ADD COLUMN emotional_weight REAL DEFAULT 0.5");
288
290
  }
291
+ if (!memoryColumns.has("disabled")) {
292
+ this.db.exec("ALTER TABLE memories ADD COLUMN disabled INTEGER DEFAULT 0");
293
+ }
289
294
  }
290
295
 
291
296
  // ============ Memory Operations ============
@@ -321,7 +326,7 @@ export class EngramDatabase {
321
326
  return row ? this.rowToMemory(row) : null;
322
327
  }
323
328
 
324
- updateMemory(id: string, updates: Partial<Pick<Memory, "content" | "importance">>): Memory | null {
329
+ updateMemory(id: string, updates: Partial<Pick<Memory, "content" | "importance" | "disabled">>): Memory | null {
325
330
  const sets: string[] = [];
326
331
  const values: unknown[] = [];
327
332
 
@@ -333,6 +338,10 @@ export class EngramDatabase {
333
338
  sets.push("importance = ?");
334
339
  values.push(updates.importance);
335
340
  }
341
+ if (updates.disabled !== undefined) {
342
+ sets.push("disabled = ?");
343
+ values.push(updates.disabled ? 1 : 0);
344
+ }
336
345
 
337
346
  if (sets.length === 0) return this.getMemory(id);
338
347
 
@@ -363,8 +372,11 @@ export class EngramDatabase {
363
372
  `).run(id);
364
373
  }
365
374
 
366
- getAllMemories(limit: number = 1000): Memory[] {
367
- const rows = this.stmt("SELECT * FROM memories ORDER BY timestamp DESC LIMIT ?").all(limit) as Record<string, unknown>[];
375
+ getAllMemories(limit: number = 1000, includeDisabled: boolean = false): Memory[] {
376
+ const sql = includeDisabled
377
+ ? "SELECT * FROM memories ORDER BY timestamp DESC LIMIT ?"
378
+ : "SELECT * FROM memories WHERE disabled = 0 ORDER BY timestamp DESC LIMIT ?";
379
+ const rows = this.stmt(sql).all(limit) as Record<string, unknown>[];
368
380
  return rows.map((row) => this.rowToMemory(row));
369
381
  }
370
382
 
@@ -474,7 +486,7 @@ export class EngramDatabase {
474
486
  SELECT m.*, bm25(memories_fts) as score
475
487
  FROM memories_fts fts
476
488
  JOIN memories m ON fts.rowid = m.rowid
477
- WHERE memories_fts MATCH ?
489
+ WHERE memories_fts MATCH ? AND m.disabled = 0
478
490
  ORDER BY score
479
491
  LIMIT ?
480
492
  `).all(escapedQuery, limit) as Array<Record<string, unknown>>;
@@ -529,7 +541,7 @@ export class EngramDatabase {
529
541
 
530
542
  /**
531
543
  * Find a similar entity using fuzzy matching
532
- * Catches "Boris D" matching "Boris Djordjevic", "John" matching "John Smith"
544
+ * Catches "John D" matching "John Doe", "Jane" matching "Jane Smith"
533
545
  */
534
546
  findSimilarEntity(name: string, threshold: number = 0.8): Entity | null {
535
547
  const normalizedName = name.toLowerCase().trim();
@@ -575,7 +587,7 @@ export class EngramDatabase {
575
587
  // Exact match
576
588
  if (full1 === full2) return 1.0;
577
589
 
578
- // One is prefix of the other (e.g., "Boris" vs "Boris Djordjevic")
590
+ // One is prefix of the other (e.g., "John" vs "John Doe")
579
591
  if (full1.startsWith(full2 + " ") || full2.startsWith(full1 + " ")) {
580
592
  return 0.9;
581
593
  }
@@ -588,7 +600,7 @@ export class EngramDatabase {
588
600
  return 0.7 + (0.2 * shorter / longer);
589
601
  }
590
602
 
591
- // Check for abbreviated names (e.g., "Boris D" vs "Boris Djordjevic")
603
+ // Check for abbreviated names (e.g., "John D" vs "John Doe")
592
604
  if (words1.length >= 2 && words2.length >= 2) {
593
605
  const last1 = words1[words1.length - 1];
594
606
  const last2 = words2[words2.length - 1];
@@ -1098,6 +1110,7 @@ export class EngramDatabase {
1098
1110
  last_accessed: row.last_accessed ? new Date(row.last_accessed as string) : null,
1099
1111
  stability: (row.stability as number) ?? 1.0,
1100
1112
  emotional_weight: (row.emotional_weight as number) ?? 0.5,
1113
+ disabled: Boolean(row.disabled),
1101
1114
  };
1102
1115
  }
1103
1116