@joshuaswarren/openclaw-engram 7.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,851 @@
1
+ # openclaw-engram
2
+
3
+ A local-first memory plugin for [OpenClaw](https://github.com/openclaw/openclaw) that gives AI agents persistent, searchable long-term memory across conversations.
4
+
5
+ Engram uses **LLM-powered extraction** (OpenAI Responses API) to intelligently identify what's worth remembering from each conversation, stores memories as plain **markdown files** on disk, and retrieves relevant context via **[QMD](https://github.com/tobi/qmd)** hybrid search (BM25 + vector + reranking).
6
+
7
+ ## Why Engram?
8
+
9
+ Most AI memory systems are either too noisy (store everything) or too lossy (store nothing useful). Engram takes a different approach:
10
+
11
+ - **Signal detection first** -- A fast local regex scan classifies each turn before any API call happens. High-signal turns (corrections, preferences, identity statements) trigger immediate extraction; low-signal turns are batched.
12
+ - **Structured extraction** -- An LLM analyzes buffered turns and extracts typed memories (facts, preferences, corrections, entities, decisions, relationships, principles, commitments, moments, skills) with confidence scores.
13
+ - **Automatic consolidation** -- Periodic consolidation passes merge duplicates, update entity profiles, refresh the behavioral profile, and expire stale memories.
14
+ - **Local-first storage** -- All memories are plain markdown files with YAML frontmatter. No database, no vendor lock-in. Grep them, version them, back them up however you like.
15
+ - **Privacy by default** -- Memories never leave your machine unless you choose to sync them. The LLM extraction call is the only external API call.
16
+
17
+ ## Features
18
+
19
+ ### Core Features
20
+ - **10 memory categories**: fact, preference, correction, entity, decision, relationship, principle, commitment, moment, skill
21
+ - **Confidence tiers**: explicit (0.95-1.0), implied (0.70-0.94), inferred (0.40-0.69), speculative (0.00-0.39)
22
+ - **TTL on speculative memories**: Auto-expire after 30 days if unconfirmed
23
+ - **Lineage tracking**: Memories track their parent IDs through consolidation merges and updates
24
+ - **Entity profiles**: Accumulates facts about people, projects, tools, and companies into per-entity files, with automatic name normalization and periodic deduplication
25
+ - **Behavioral profile**: A living `profile.md` that evolves as the system learns about the user, with automatic cap and pruning to control token usage
26
+ - **Identity reflection**: Optional self-reflection that helps the agent improve over sessions
27
+ - **Question generation**: Generates 1-3 curiosity questions per extraction to drive deeper engagement
28
+ - **Commitment lifecycle**: Tracks promises and deadlines with configurable decay (default 90 days)
29
+ - **Auto-consolidation**: IDENTITY.md reflections are automatically summarized when they exceed 8KB
30
+ - **Smart buffer**: Configurable trigger logic (signal-based, turn count, or time-based)
31
+ - **QMD integration**: Hybrid search with BM25, vector embeddings, and reranking
32
+ - **Graceful degradation**: Works without QMD (falls back to direct file reads) and without an API key (retrieval-only mode)
33
+ - **Portability**: Import/export/backup your memory store via CLI (v2.3)
34
+ - **CLI**: Search, inspect, and manage memories from the command line
35
+ - **Agent tools**: `memory_search`, `memory_store`, `memory_profile`, `memory_entities`, `memory_promote`
36
+
37
+ ### v1.2.0 Advanced Features
38
+
39
+ All advanced features are **disabled by default** for gradual adoption. Enable them in your config as needed.
40
+
41
+ #### Importance Scoring (Zero-LLM)
42
+ - **Local heuristic scoring** at extraction time — no API calls
43
+ - Five tiers: `critical` (0.9-1.0), `high` (0.7-0.9), `normal` (0.4-0.7), `low` (0.2-0.4), `trivial` (0.0-0.2)
44
+ - Scores based on: explicit importance markers, personal info, instructions, emotional content, factual density
45
+ - Extracts salient keywords for improved search relevance
46
+ - Used for **ranking** (not exclusion) — all memories are still stored and searchable
47
+
48
+ #### Access Tracking
49
+ - Tracks `accessCount` and `lastAccessed` for each memory
50
+ - Batched updates during consolidation (zero retrieval latency impact)
51
+ - Enables "working set" prioritization — frequently accessed memories surface higher
52
+ - CLI: `openclaw engram access` to view most accessed memories
53
+
54
+ #### Recency Boosting
55
+ - Recent memories ranked higher in search results
56
+ - Configurable weight (0-1, default 0.2)
57
+ - Exponential decay with 7-day half-life
58
+
59
+ #### Automatic Chunking
60
+ - Sentence-boundary splitting for long memories (>150 tokens)
61
+ - Target ~200 tokens per chunk with 2-sentence overlap
62
+ - Each chunk maintains `parentId` and `chunkIndex` for context reconstruction
63
+ - Preserves coherent thoughts — never splits mid-sentence
64
+
65
+ #### Contradiction Detection
66
+ - QMD similarity search finds candidate conflicts (fast, cheap)
67
+ - LLM verification confirms actual contradictions (prevents false positives)
68
+ - Auto-resolve when confidence > 0.9
69
+ - Full audit trail: old memory marked `status: superseded` with `supersededBy` link
70
+ - Nothing is deleted — superseded memories remain searchable explicitly
71
+
72
+ #### Memory Linking (Knowledge Graph)
73
+ - Typed relationships: `follows`, `references`, `contradicts`, `supports`, `related`
74
+ - LLM suggests links during extraction based on semantic connections
75
+ - Links stored in frontmatter with strength scores (0-1)
76
+ - Enables graph traversal between related memories
77
+
78
+ #### Conversation Threading
79
+ - Auto-detect thread boundaries (session change or 30-minute gap)
80
+ - Auto-generate thread titles from top TF-IDF keywords
81
+ - Group memories into conversation threads for context reconstruction
82
+ - CLI: `openclaw engram threads` to view threads
83
+
84
+ #### Memory Summarization
85
+ - Triggered when memory count exceeds threshold (default 1000)
86
+ - Compresses old, low-importance, unprotected memories into summaries
87
+ - **Archive, not delete** — source memories marked `status: archived`, still searchable
88
+ - Protected: recent memories, high-importance, entities, commitments/preferences/decisions
89
+ - Summaries stored in `summaries/` directory
90
+
91
+ #### Topic Extraction
92
+ - TF-IDF analysis of the entire memory corpus
93
+ - Extracts top N topics (default 50) during consolidation
94
+ - Stored in `state/topics.json`
95
+ - CLI: `openclaw engram topics` to view extracted topics
96
+
97
+ ### v2.2 Advanced Retrieval
98
+
99
+ All v2.2 retrieval features are **disabled by default**. Enable them only if you can tolerate a small latency increase.
100
+
101
+ - **Heuristic query expansion** (`queryExpansionEnabled`): Runs a few deterministic, cheap expanded queries (no LLM calls) and merges results.
102
+ - **LLM re-ranking** (`rerankEnabled`): Re-scores the top N retrieved memories using a short, timeboxed request.
103
+ - Default mode: **local-only** (`rerankProvider: "local"`), fail-open on errors/timeouts.
104
+ - **Note:** If QMD is enabled (`qmdEnabled: true`), QMD's `query` command already includes built-in reranking via its bundled reranker model. Enabling `rerankEnabled` on top of QMD results in **redundant double reranking**, which adds latency for marginal quality gain. **Recommendation: keep `rerankEnabled: false` when using QMD.**
105
+ - **Feedback loop** (`feedbackEnabled` + `memory_feedback` tool): Store thumbs up/down locally and apply it as a small ranking bias.
106
+ - **Negative examples** (`negativeExamplesEnabled` + `memory_feedback_last_recall` tool): Track retrieved-but-not-useful memories and apply a small ranking penalty.
107
+ - **Slow query log** (`slowLogEnabled` + `slowLogThresholdMs`): Logs durations and metadata (never content) for local LLM and QMD operations.
108
+
109
+ ### v2.3 Import / Export / Backup
110
+
111
+ Engram supports **portable exports** and **safe backups** via CLI:
112
+
113
+ ```bash
114
+ openclaw engram export --format json --out /tmp/engram-export
115
+ openclaw engram export --format sqlite --out /tmp/engram.sqlite
116
+ openclaw engram export --format md --out /tmp/engram-md
117
+
118
+ openclaw engram import --from /tmp/engram-export --format auto
119
+ openclaw engram backup --out-dir /tmp/engram-backups --retention-days 14
120
+ ```
121
+
122
+ If namespaces are enabled (v3.0+), the CLI accepts `--namespace <ns>` for export/import/backup.
123
+
124
+ Details: `docs/import-export.md`
125
+
126
+ ### v2.4 Context Retention Hardening
127
+
128
+ - **Extended hourly summaries** (structured topics/decisions/action items/rejections) are optional:
129
+ - Config: `hourlySummariesExtendedEnabled`, `hourlySummariesIncludeToolStats`
130
+ - **Conversation semantic recall hook** (optional): index transcript chunks and inject top-K relevant past chunks:
131
+ - Config: `conversationIndexEnabled`, `conversationIndexQmdCollection`, `conversationRecallTopK`, `conversationIndexMinUpdateIntervalMs`, `conversationIndexEmbedOnUpdate`
132
+ - Tool: `conversation_index_update` (optional `embed: true` override)
133
+
134
+ Details: `docs/context-retention.md`
135
+
136
+ ### v3.0 Namespaces (Multi-Agent Memory)
137
+
138
+ Optional namespaces let multiple agents share a memory store with isolation:
139
+
140
+ - Config: `namespacesEnabled`, `defaultNamespace`, `sharedNamespace`, `namespacePolicies`
141
+ - Tooling: `memory_store` supports `namespace`; `memory_promote` copies curated items into the shared namespace.
142
+
143
+ Details: `docs/namespaces.md`
144
+
145
+ ### v4.0 Shared Context (Cross-Agent Shared Intelligence)
146
+
147
+ Optional shared-context is a **file-based shared brain** (priorities, agent outputs, feedback, roundtables):
148
+
149
+ - Config: `sharedContextEnabled`, `sharedContextDir`, `sharedContextMaxInjectChars`
150
+ - Tools: `shared_context_write_output`, `shared_priorities_append`, `shared_feedback_record`, `shared_context_curate_daily`
151
+
152
+ Details: `docs/shared-context.md`
153
+
154
+ ### v5.0 Compounding Engine
155
+
156
+ Optional compounding turns shared feedback into persistent learning:
157
+
158
+ - Writes: `memoryDir/compounding/weekly/<YYYY-Www>.md`, `memoryDir/compounding/mistakes.json`
159
+ - Tool: `compounding_weekly_synthesize`
160
+ - Injection: `compoundingInjectEnabled` (default true when compounding is enabled)
161
+
162
+ Details: `docs/compounding.md`
163
+
164
+ ### v6.0 Fact Deduplication & Archival
165
+
166
+ Two features to keep the memory store lean and fast as it grows:
167
+
168
+ #### Content-Hash Deduplication
169
+
170
+ Prevents storing semantically identical facts. Before writing any new fact, Engram computes a normalized SHA-256 hash of the content (lowercase, strip punctuation, collapse whitespace) and checks it against a persistent index. Duplicates are silently skipped.
171
+
172
+ - **Zero false positives** — exact content match only (after normalization)
173
+ - **Persistent index** — stored as `state/fact-hashes.txt`, survives restarts
174
+ - **Seeding** — on first enable, the index auto-loads from existing facts on disk
175
+ - Config: `factDeduplicationEnabled` (default `true`)
176
+
177
+ #### Fact Archival
178
+
179
+ Automatically moves old, low-importance, rarely-accessed facts out of the hot search index into an `archive/` directory. Archived facts are still on disk but excluded from QMD queries, keeping retrieval fast.
180
+
181
+ Archival runs during the periodic consolidation pass. A fact is archived when **all** of these are true:
182
+ - Age exceeds `factArchivalAgeDays` (default 90)
183
+ - Importance score is below `factArchivalMaxImportance` (default 0.3)
184
+ - Access count is at or below `factArchivalMaxAccessCount` (default 2)
185
+ - Category is not in `factArchivalProtectedCategories` (default: commitment, preference, decision, principle)
186
+ - Status is `active` (not already superseded/archived)
187
+ - Not a correction memory
188
+
189
+ Config: `factArchivalEnabled` (default `false`), plus the threshold settings above.
190
+
191
+ ## Architecture
192
+
193
+ ```
194
+ Conversation turn arrives
195
+ |
196
+ v
197
+ Signal scan (local regex, <10ms, free)
198
+ |
199
+ v
200
+ Append to smart buffer
201
+ |
202
+ v
203
+ Trigger check:
204
+ HIGH signal? --> Extract NOW (single LLM call)
205
+ Buffer >= N? --> Extract BATCH
206
+ Time > T? --> Extract BATCH
207
+ else --> Keep buffering
208
+ |
209
+ v
210
+ If extracted: content-hash dedup check (skip duplicates)
211
+ |
212
+ v
213
+ Write new markdown files to disk
214
+ |
215
+ v
216
+ Every Nth extraction: Consolidation pass
217
+ - Merge/dedup memories
218
+ - Merge fragmented entity files
219
+ - Update entity profiles
220
+ - Update behavioral profile (with cap enforcement)
221
+ - Clean expired commitments and TTL memories
222
+ - Archive old, low-importance facts (v6.0)
223
+ - Auto-consolidate identity reflections
224
+ |
225
+ v
226
+ Background: qmd update (re-index new files)
227
+ ```
228
+
229
+ Performance note for conversation indexing:
230
+ - `conversation_index_update` now runs `qmd update` only by default.
231
+ - `qmd embed` is optional (`conversationIndexEmbedOnUpdate: true` or tool param `embed: true`).
232
+ - Re-indexing is min-interval gated per session (`conversationIndexMinUpdateIntervalMs`, default 15m).
233
+
234
+ ### Retrieval Flow
235
+
236
+ ```
237
+ Agent session starts
238
+ |
239
+ v
240
+ Read profile.md directly (free, instant)
241
+ |
242
+ v
243
+ QMD search memory collection (relevant memories)
244
+ |
245
+ v
246
+ QMD search global collections (workspace context)
247
+ |
248
+ v
249
+ Optionally inject highest-priority open question
250
+ |
251
+ v
252
+ Combine and inject into system prompt
253
+ ```
254
+
255
+ ## Hourly Summaries (Cron)
256
+
257
+ Engram can generate **hourly summaries** of conversation activity, written to disk under the configured `memoryDir` summaries folder.
258
+
259
+ In most installs, the safest setup is to drive this via OpenClaw cron using an **agent turn** (not a tool call directly):
260
+ - `sessionTarget: "isolated"`
261
+ - `payload.kind: "agentTurn"` that calls `memory_summarize_hourly`
262
+ - `delivery.mode: "none"` (so it never posts to Discord)
263
+
264
+ Why: some OpenClaw installations restrict `sessionTarget: "main"` to `payload.kind: "systemEvent"` only. If you configure `main` + `toolCall`, it may be repeatedly skipped and summaries will silently stop.
265
+
266
+ ## Storage Layout
267
+
268
+ All memories are stored as markdown files with YAML frontmatter:
269
+
270
+ ```
271
+ ~/.openclaw/workspace/memory/local/
272
+ ├── profile.md # Living behavioral profile (auto-updated)
273
+ ├── entities/ # One markdown file per tracked entity
274
+ │ ├── person-jane-doe.md
275
+ │ ├── project-my-app.md
276
+ │ └── tool-qmd.md
277
+ ├── facts/ # Memory entries organized by date
278
+ │ └── YYYY-MM-DD/
279
+ │ ├── fact-1738789200000-a1b2.md
280
+ │ └── preference-1738789200000-c3d4.md
281
+ ├── corrections/ # High-weight correction memories
282
+ │ └── correction-1738789200000-e5f6.md
283
+ ├── archive/ # Archived low-value facts (v6.0)
284
+ │ └── YYYY-MM-DD/
285
+ │ └── fact-1738789200000-a1b2.md
286
+ ├── questions/ # Generated curiosity questions
287
+ │ └── q-m1abc-xy.md
288
+ ├── threads/ # Conversation threads (v1.2.0)
289
+ │ └── thread-1738789200000-a1b2.json
290
+ ├── summaries/ # Memory summaries (v1.2.0)
291
+ │ └── summary-1738789200000-a1b2.json
292
+ ├── config/
293
+ │ └── aliases.json # Entity name aliases
294
+ └── state/
295
+ ├── buffer.json # Current unbatched turns (survives restarts)
296
+ ├── meta.json # Extraction count, timestamps, totals
297
+ ├── topics.json # Extracted topics (v1.2.0)
298
+ └── fact-hashes.txt # Content-hash dedup index (v6.0)
299
+ ```
300
+
301
+ ### Memory File Format
302
+
303
+ Each memory file uses YAML frontmatter:
304
+
305
+ ```yaml
306
+ ---
307
+ id: fact-1738789200000-a1b2
308
+ category: fact
309
+ created: 2026-02-05T12:00:00.000Z
310
+ updated: 2026-02-05T12:00:00.000Z
311
+ source: extraction
312
+ confidence: 0.85
313
+ confidenceTier: implied
314
+ tags: ["tools", "preferences"]
315
+ entityRef: tool-qmd
316
+ ---
317
+
318
+ QMD supports hybrid search combining BM25 and vector embeddings with reranking.
319
+ ```
320
+
321
+ ## Installation
322
+
323
+ ### Prerequisites
324
+
325
+ - [OpenClaw](https://github.com/openclaw/openclaw) gateway
326
+ - An OpenAI API key (for extraction; retrieval works without one)
327
+ - [QMD](https://github.com/tobi/qmd) (optional, for hybrid search)
328
+
329
+ ### Quick Start (Recommended: npm via OpenClaw)
330
+
331
+ ```bash
332
+ # Install from npm (records install provenance in openclaw.json.plugins.installs)
333
+ openclaw plugins install @joshuaswarren/openclaw-engram --pin
334
+ ```
335
+
336
+ Then enable and configure in `openclaw.json`:
337
+
338
+ ```jsonc
339
+ {
340
+ "plugins": {
341
+ "allow": ["openclaw-engram"],
342
+ "slots": {
343
+ "memory": "openclaw-engram"
344
+ },
345
+ "entries": {
346
+ "openclaw-engram": {
347
+ "enabled": true,
348
+ "config": {
349
+ "openaiApiKey": "${OPENAI_API_KEY}"
350
+ }
351
+ }
352
+ }
353
+ }
354
+ }
355
+ ```
356
+
357
+ Reload the gateway:
358
+
359
+ ```bash
360
+ kill -USR1 $(pgrep openclaw-gateway)
361
+ ```
362
+
363
+ ### Developer Install (from Git)
364
+
365
+ Use this only if you are actively developing the plugin.
366
+
367
+ ```bash
368
+ # Clone into the OpenClaw extensions directory
369
+ git clone https://github.com/joshuaswarren/openclaw-engram.git \
370
+ ~/.openclaw/extensions/openclaw-engram
371
+
372
+ # Install dependencies and build
373
+ cd ~/.openclaw/extensions/openclaw-engram
374
+ npm ci
375
+ npm run build
376
+ ```
377
+
378
+ ### Set Up QMD Collection (Optional)
379
+
380
+ If you have QMD installed, add a collection pointing at the memory directory. Add to `~/.config/qmd/index.yml`:
381
+
382
+ ```yaml
383
+ openclaw-engram:
384
+ path: ~/.openclaw/workspace/memory/local
385
+ extensions: [.md]
386
+ ```
387
+
388
+ Then index:
389
+
390
+ ```bash
391
+ qmd update && qmd embed
392
+ ```
393
+
394
+ ### Recommended QMD Patches (as of 2026-02-14)
395
+
396
+ The following upstream QMD pull requests contain important performance and stability fixes that have not yet been merged. We recommend applying them locally to your QMD installation at `~/.bun/install/global/node_modules/qmd/`:
397
+
398
+ 1. **[PR #166](https://github.com/tobi/qmd/pull/166) — HTTP daemon crash fix** (fixes [#163](https://github.com/tobi/qmd/issues/163))
399
+ - The HTTP daemon (`qmd mcp --http --daemon`) crashes on the second MCP request due to transport reuse.
400
+ - **Fix**: In `src/mcp.ts`, add `sessionIdGenerator: () => crypto.randomUUID()` to the `WebStandardStreamableHTTPServerTransport` constructor.
401
+ - Without this fix, the daemon is unusable for sequential requests.
402
+
403
+ 2. **[PR #112](https://github.com/tobi/qmd/pull/112) — Model override environment variables**
404
+ - Adds `QMD_EMBED_MODEL`, `QMD_GENERATE_MODEL`, `QMD_RERANK_MODEL`, and `QMD_MODEL_CACHE_DIR` env vars.
405
+ - Allows swapping in smaller/faster models at runtime (e.g., Jina Reranker v1-tiny for 185x faster cold-start reranking).
406
+ - **Fix**: In `src/llm.ts`, change the `LlamaCpp` constructor to check `process.env.QMD_*` vars as fallback between config and defaults.
407
+
408
+ 3. **[PR #117](https://github.com/tobi/qmd/pull/117) — SQLite pathological join fix**
409
+ - SQLite may choose a disastrous join order for FTS queries with collection filters, making `qmd search -c <collection>` extremely slow on large indexes.
410
+ - **Fix**: In `src/store.ts`, change `JOIN` to `CROSS JOIN` in `searchFTS()` and move join predicates into the `WHERE` clause to preserve left-to-right evaluation order starting from the FTS MATCH.
411
+ - Particularly important for large collections (90K+ files like the engram memory store).
412
+
413
+ Check [tobi/qmd](https://github.com/tobi/qmd) periodically — once these PRs are merged upstream, a simple `bun install -g github:tobi/qmd` will include them and these local patches can be removed.
414
+
415
+ ### Restart the Gateway
416
+
417
+ ```bash
418
+ kill -USR1 $(pgrep openclaw-gateway)
419
+ ```
420
+
421
+ Check the logs to confirm:
422
+
423
+ ```bash
424
+ tail -f ~/.openclaw/logs/gateway.log
425
+ # Should see: [gateway] openclaw-engram: started
426
+ ```
427
+
428
+ ## Configuration
429
+
430
+ All settings are defined in `openclaw.json` under `plugins.entries.openclaw-engram.config`:
431
+
432
+ For a full v2.3-v5 setup (including cron and QMD conversation-index collections) and tuning guidance, see:
433
+ - `docs/setup-config-tuning.md`
434
+ - `docs/import-export.md`
435
+ - `docs/context-retention.md`
436
+ - `docs/namespaces.md`
437
+ - `docs/shared-context.md`
438
+ - `docs/compounding.md`
439
+
440
+ Bootstrap config path override (for service environments) can be set via env var:
441
+ - `OPENCLAW_ENGRAM_CONFIG_PATH=/absolute/path/to/openclaw.json`
442
+ - Fallback: `OPENCLAW_CONFIG_PATH`
443
+
444
+ ### Core Settings
445
+
446
+ | Setting | Default | Description |
447
+ |---------|---------|-------------|
448
+ | `openaiApiKey` | `(env fallback)` | OpenAI API key or `${ENV_VAR}` reference |
449
+ | `model` | `gpt-5.2` | OpenAI model for extraction/consolidation |
450
+ | `reasoningEffort` | `low` | Reasoning effort: `none`, `low`, `medium`, `high` |
451
+ | `memoryDir` | `~/.openclaw/workspace/memory/local` | Memory storage directory |
452
+ | `workspaceDir` | `~/.openclaw/workspace` | Workspace directory (for IDENTITY.md) |
453
+ | `debug` | `false` | Enable debug logging |
454
+
455
+ ### File Hygiene (Memory File Limits / Truncation Risk)
456
+
457
+ OpenClaw may bootstrap workspace markdown files (for example `IDENTITY.md`, `MEMORY.md`) into the prompt on every message.
458
+ If those files become large, they can be silently truncated by the gateway's bootstrap budget, which causes "memory loss" without an explicit error.
459
+
460
+ Engram can optionally:
461
+ - Lint selected workspace files and warn when they are approaching a configured size budget.
462
+ - Rotate oversized markdown files into an archive directory, replacing the original with a lean index plus a small tail excerpt for continuity.
463
+
464
+ This is **off by default** because it can modify workspace files.
465
+
466
+ Example config:
467
+
468
+ ```json
469
+ {
470
+ "fileHygiene": {
471
+ "enabled": true,
472
+ "lintEnabled": true,
473
+ "lintPaths": ["IDENTITY.md", "MEMORY.md"],
474
+ "lintBudgetBytes": 20000,
475
+ "lintWarnRatio": 0.8,
476
+ "rotateEnabled": true,
477
+ "rotatePaths": ["IDENTITY.md"],
478
+ "rotateMaxBytes": 18000,
479
+ "rotateKeepTailChars": 2000,
480
+ "archiveDir": ".engram-archive",
481
+ "runMinIntervalMs": 300000,
482
+ "warningsLogEnabled": false
483
+ }
484
+ }
485
+ ```
486
+
487
+ ### Buffer & Trigger Settings
488
+
489
+ | Setting | Default | Description |
490
+ |---------|---------|-------------|
491
+ | `triggerMode` | `smart` | `smart`, `every_n`, or `time_based` |
492
+ | `bufferMaxTurns` | `5` | Max buffered turns before forced extraction |
493
+ | `bufferMaxMinutes` | `15` | Max minutes before forced extraction |
494
+ | `highSignalPatterns` | `[]` | Custom regex patterns for immediate extraction |
495
+ | `consolidateEveryN` | `3` | Run consolidation every N extractions |
496
+
497
+ ### Retrieval Settings
498
+
499
+ | Setting | Default | Description |
500
+ |---------|---------|-------------|
501
+ | `maxMemoryTokens` | `2000` | Max tokens injected into system prompt |
502
+ | `qmdEnabled` | `true` | Use QMD for hybrid search |
503
+ | `qmdCollection` | `openclaw-engram` | QMD collection name |
504
+ | `qmdMaxResults` | `8` | Max QMD results per search |
505
+ | `qmdPath` | `(auto)` | Optional absolute path to `qmd` binary (bypasses PATH discovery) |
506
+
507
+ ### Local / OpenAI-Compatible Endpoint Settings
508
+
509
+ | Setting | Default | Description |
510
+ |---------|---------|-------------|
511
+ | `localLlmEnabled` | `false` | Enable local/compatible endpoint for extraction + consolidation |
512
+ | `localLlmUrl` | `http://localhost:1234/v1` | Base URL for OpenAI-compatible endpoint |
513
+ | `localLlmModel` | `local-model` | Model ID to use on the endpoint |
514
+ | `localLlmApiKey` | `(unset)` | Optional API key for authenticated endpoints |
515
+ | `localLlmHeaders` | `(unset)` | Optional extra headers (for proxy/provider-specific auth/routing) |
516
+ | `localLlmAuthHeader` | `true` | Send `Authorization: Bearer <localLlmApiKey>` when key is set |
517
+ | `localLlmFallback` | `true` | Fall back to gateway model chain when local endpoint fails |
518
+
519
+ Example (`openclaw.json` plugin config):
520
+
521
+ ```json
522
+ {
523
+ "localLlmEnabled": true,
524
+ "localLlmUrl": "https://your-openai-compatible-endpoint.example/v1",
525
+ "localLlmModel": "your-model-id",
526
+ "localLlmApiKey": "${YOUR_ENDPOINT_API_KEY}",
527
+ "localLlmHeaders": {
528
+ "X-Provider-Routing": "engram-extraction"
529
+ },
530
+ "localLlmAuthHeader": true,
531
+ "localLlmFallback": true
532
+ }
533
+ ```
534
+
535
+ ### V2 Feature Settings
536
+
537
+ | Setting | Default | Description |
538
+ |---------|---------|-------------|
539
+ | `identityEnabled` | `true` | Enable agent identity reflections |
540
+ | `injectQuestions` | `false` | Inject open questions into the system prompt |
541
+ | `commitmentDecayDays` | `90` | Days before fulfilled/expired commitments are removed |
542
+
543
+ ### v2.2 Advanced Retrieval Settings
544
+
545
+ See `docs/advanced-retrieval.md` for details and recommended safe defaults.
546
+
547
+ | Setting | Default | Description |
548
+ |---------|---------|-------------|
549
+ | `queryExpansionEnabled` | `false` | Heuristic query expansion (no LLM calls) |
550
+ | `queryExpansionMaxQueries` | `4` | Max expanded queries (including original) |
551
+ | `queryExpansionMinTokenLen` | `3` | Minimum token length for expansion |
552
+ | `rerankEnabled` | `false` | Enable LLM re-ranking (timeboxed; fail-open) |
553
+ | `rerankProvider` | `local` | `local` (no cloud calls). `cloud` is reserved/experimental (no-op in v2.2.0). |
554
+ | `rerankMaxCandidates` | `20` | Max candidates sent to re-ranker |
555
+ | `rerankTimeoutMs` | `8000` | Rerank timeout (ms) |
556
+ | `rerankCacheEnabled` | `true` | Cache reranks in-memory |
557
+ | `rerankCacheTtlMs` | `3600000` | Rerank cache TTL (ms) |
558
+ | `feedbackEnabled` | `false` | Enable `memory_feedback` tool and ranking bias |
559
+ | `negativeExamplesEnabled` | `false` | Enable negative examples + ranking penalty (opt-in) |
560
+ | `negativeExamplesPenaltyPerHit` | `0.05` | Penalty per "not useful" hit |
561
+ | `negativeExamplesPenaltyCap` | `0.25` | Maximum total penalty applied |
562
+ | `localLlmHomeDir` | `(auto)` | Optional home directory override for LM Studio settings + helper paths |
563
+ | `localLmsCliPath` | `(auto)` | Optional absolute path to `lms` CLI |
564
+ | `localLmsBinDir` | `(auto)` | Optional bin dir prepended to PATH for `lms` execution |
565
+
566
+ ### v1.2.0 Advanced Feature Settings
567
+
568
+ #### Access Tracking & Retrieval
569
+
570
+ | Setting | Default | Description |
571
+ |---------|---------|-------------|
572
+ | `accessTrackingEnabled` | `true` | Track memory access counts and recency |
573
+ | `accessTrackingBufferMaxSize` | `100` | Max entries in access buffer before flush |
574
+ | `recencyWeight` | `0.2` | Weight for recency boosting (0-1) |
575
+ | `boostAccessCount` | `true` | Boost frequently accessed memories in search |
576
+
577
+ #### Chunking
578
+
579
+ | Setting | Default | Description |
580
+ |---------|---------|-------------|
581
+ | `chunkingEnabled` | `false` | Enable automatic chunking of long memories |
582
+ | `chunkingTargetTokens` | `200` | Target tokens per chunk |
583
+ | `chunkingMinTokens` | `150` | Minimum tokens to trigger chunking |
584
+ | `chunkingOverlapSentences` | `2` | Number of sentences to overlap between chunks |
585
+
586
+ #### Contradiction Detection
587
+
588
+ | Setting | Default | Description |
589
+ |---------|---------|-------------|
590
+ | `contradictionDetectionEnabled` | `false` | Enable LLM-verified contradiction detection |
591
+ | `contradictionSimilarityThreshold` | `0.7` | QMD similarity threshold to trigger check |
592
+ | `contradictionMinConfidence` | `0.9` | Minimum LLM confidence to auto-resolve |
593
+ | `contradictionAutoResolve` | `true` | Automatically supersede contradicted memories |
594
+
595
+ #### Memory Linking
596
+
597
+ | Setting | Default | Description |
598
+ |---------|---------|-------------|
599
+ | `memoryLinkingEnabled` | `false` | Enable automatic memory linking |
600
+
601
+ #### Conversation Threading
602
+
603
+ | Setting | Default | Description |
604
+ |---------|---------|-------------|
605
+ | `threadingEnabled` | `false` | Enable conversation threading |
606
+ | `threadingGapMinutes` | `30` | Minutes of gap to start a new thread |
607
+
608
+ #### Memory Summarization
609
+
610
+ | Setting | Default | Description |
611
+ |---------|---------|-------------|
612
+ | `summarizationEnabled` | `false` | Enable automatic memory compression |
613
+ | `summarizationTriggerCount` | `1000` | Memory count threshold to trigger |
614
+ | `summarizationRecentToKeep` | `300` | Number of recent memories to keep uncompressed |
615
+ | `summarizationImportanceThreshold` | `0.3` | Only compress memories with importance below this |
616
+ | `summarizationProtectedTags` | `["commitment", "preference", "decision", "principle"]` | Tags that protect memories from compression |
617
+
618
+ #### Topic Extraction
619
+
620
+ | Setting | Default | Description |
621
+ |---------|---------|-------------|
622
+ | `topicExtractionEnabled` | `true` | Enable topic extraction during consolidation |
623
+ | `topicExtractionTopN` | `50` | Number of top topics to extract |
624
+
625
+ ### v6.0 Deduplication & Archival Settings
626
+
627
+ | Setting | Default | Description |
628
+ |---------|---------|-------------|
629
+ | `factDeduplicationEnabled` | `true` | Content-hash dedup prevents storing identical facts |
630
+ | `factArchivalEnabled` | `false` | Automatically archive old, low-value facts |
631
+ | `factArchivalAgeDays` | `90` | Minimum age (days) before a fact is eligible for archival |
632
+ | `factArchivalMaxImportance` | `0.3` | Only archive facts with importance below this threshold |
633
+ | `factArchivalMaxAccessCount` | `2` | Only archive facts accessed this many times or fewer |
634
+ | `factArchivalProtectedCategories` | `["commitment", "preference", "decision", "principle"]` | Categories that are never archived |
635
+
636
+ ### Trigger Modes
637
+
638
+ - **`smart`** (default): Extracts immediately on high-signal turns (corrections, preferences, identity statements). Batches low-signal turns until buffer-full or time-elapsed.
639
+ - **`every_n`**: Extracts every N turns. Simple and predictable.
640
+ - **`time_based`**: Extracts when `bufferMaxMinutes` elapsed since last extraction.
641
+
642
+ ### API Key Configuration
643
+
644
+ The plugin resolves the OpenAI API key in this order:
645
+
646
+ 1. `config.openaiApiKey` with `${VAR}` syntax -- resolved from environment
647
+ 2. `config.openaiApiKey` as literal string -- used directly
648
+ 3. `process.env.OPENAI_API_KEY` -- implicit fallback
649
+ 4. None -- extraction disabled, retrieval-only mode (plugin still loads and serves memories)
650
+
651
+ **Gateway note:** The OpenClaw gateway runs as a launchd service with its own environment. If you use `${VAR}` syntax, make sure the variable is in the gateway's launchd plist `EnvironmentVariables`, not just your shell profile.
652
+
653
+ ## Agent Tools
654
+
655
+ The plugin registers tools that agents can call during conversations:
656
+
657
+ | Tool | Description |
658
+ |------|-------------|
659
+ | `memory_search` | Search memories by query string via QMD hybrid search |
660
+ | `memory_store` | Explicitly store a memory with category, confidence, and tags |
661
+ | `memory_promote` | Promote/copy a curated memory to shared namespace (v3.0+) |
662
+ | `memory_profile` | View the current behavioral profile |
663
+ | `memory_entities` | List all tracked entities or view a specific entity's facts |
664
+ | `memory_summarize_hourly` | Generate hourly summaries |
665
+ | `conversation_index_update` | Refresh conversation chunk index (v2.4) |
666
+ | `shared_context_write_output` | Write an agent output into shared-context (v4.0) |
667
+ | `shared_priorities_append` | Append priorities proposal to inbox (v4.0) |
668
+ | `shared_feedback_record` | Record approval/rejection feedback for compounding (v4/v5) |
669
+ | `shared_context_curate_daily` | Curate daily roundtable in shared-context (v4.0) |
670
+ | `compounding_weekly_synthesize` | Build weekly compounding report + mistakes file (v5.0) |
671
+
672
+ ## CLI Commands
673
+
674
+ ```bash
675
+ # Core commands
676
+ openclaw engram stats # Memory statistics (counts, last extraction, etc.)
677
+ openclaw engram search "query" # Search memories via QMD
678
+ openclaw engram export --format json --out /tmp/engram-export
679
+ openclaw engram import --from /tmp/engram-export --format auto
680
+ openclaw engram backup --out-dir /tmp/engram-backups --retention-days 14
681
+
682
+ # Namespace-aware (v3.0+, when namespacesEnabled=true)
683
+ openclaw engram export --format json --out /tmp/engram-shared --namespace shared
684
+ openclaw engram import --from /tmp/engram-shared --format auto --namespace shared
685
+ openclaw engram backup --out-dir /tmp/engram-backups --namespace main
686
+ openclaw engram profile # Display the behavioral profile
687
+ openclaw engram entities # List all tracked entities
688
+ openclaw engram entities person-name # View specific entity details
689
+ openclaw engram questions # List open curiosity questions
690
+ openclaw engram identity # Show agent identity reflections
691
+
692
+ # v1.2.0 commands
693
+ openclaw engram access # Show most accessed memories
694
+ openclaw engram access -n 30 # Show top 30 most accessed
695
+ openclaw engram flush-access # Manually flush access tracking buffer
696
+
697
+ openclaw engram importance # Show importance score distribution
698
+ openclaw engram importance -l high # Filter by importance level
699
+ openclaw engram importance -n 20 # Show top 20 most important
700
+
701
+ openclaw engram chunks # Show chunking statistics
702
+ openclaw engram chunks -p <id> # Show chunks for a specific parent
703
+
704
+ openclaw engram threads # List conversation threads
705
+ openclaw engram threads -t <id> # Show details for a specific thread
706
+
707
+ openclaw engram topics # Show extracted topics
708
+ openclaw engram topics -n 30 # Show top 30 topics
709
+
710
+ openclaw engram summaries # Show memory summaries
711
+ openclaw engram summaries -n 10 # Show top 10 most recent summaries
712
+ ```
713
+
714
+ ## Migration
715
+
716
+ Import memories from existing OpenClaw memory systems:
717
+
718
+ ```bash
719
+ cd ~/.openclaw/extensions/openclaw-engram
720
+
721
+ # Full migration (context files + Supermemory + Honcho)
722
+ npx tsx scripts/migrate.ts
723
+
724
+ # Preview without writing anything
725
+ npx tsx scripts/migrate.ts --dry-run
726
+
727
+ # Migrate specific sources
728
+ npx tsx scripts/migrate.ts --source=context # Context files only
729
+ npx tsx scripts/migrate.ts --source=supermemory # Supermemory daily logs
730
+ npx tsx scripts/migrate.ts --source=honcho # Honcho API conclusions
731
+ ```
732
+
733
+ The migration script:
734
+ - Deduplicates against existing engram memories
735
+ - Categorizes each memory (fact, preference, correction, decision)
736
+ - Writes proper frontmatter with source attribution
737
+ - Seeds `profile.md` from context files (if it doesn't exist yet)
738
+ - Prints a detailed report with counts per source
739
+
740
+ After migration, re-index QMD:
741
+
742
+ ```bash
743
+ qmd update && qmd embed
744
+ ```
745
+
746
+ ## How It Works
747
+
748
+ ### Extraction
749
+
750
+ When a trigger fires, the buffered conversation turns are sent to the OpenAI Responses API with a structured output schema (Zod). Empty or whitespace-only turns are filtered out before the API call to avoid errors. The LLM returns:
751
+
752
+ - **Facts**: Typed memories with category, content, confidence score, tags, and optional entity reference
753
+ - **Entities**: Named entities with their type and newly learned facts
754
+ - **Profile updates**: Standalone behavioral statements about the user
755
+ - **Questions**: 1-3 curiosity questions the agent wants answered in future sessions
756
+ - **Identity reflection**: A brief self-reflection on the agent's own behavior
757
+
758
+ ### Consolidation
759
+
760
+ Every N extractions, a consolidation pass:
761
+
762
+ 1. Compares recent memories against older ones
763
+ 2. For each memory, decides: ADD, MERGE, UPDATE, INVALIDATE, or SKIP
764
+ 3. MERGE and UPDATE actions track lineage (parent memory IDs)
765
+ 4. Updates entity profiles and the behavioral profile
766
+ 5. **Merges fragmented entity files** — entities with variant names that resolve to the same canonical form are automatically merged
767
+ 6. Cleans expired commitments (fulfilled/expired + past decay period)
768
+ 7. Removes TTL-expired speculative memories
769
+ 8. **Archives old, low-importance, rarely-accessed facts** (v6.0, when `factArchivalEnabled`)
770
+ 9. Auto-consolidates IDENTITY.md if it exceeds 8KB
771
+
772
+ ### Entity Normalization
773
+
774
+ Entity names are automatically normalized to prevent fragmentation:
775
+
776
+ - Names are lowercased and hyphenated (`BlendSupply` → `blend-supply`)
777
+ - A configurable alias table maps common variants to canonical names
778
+ - Type preferences resolve cross-type duplicates (e.g., `company` wins over `other`)
779
+ - The periodic merge pass consolidates any entities that escaped normalization
780
+
781
+ ### Profile Management
782
+
783
+ The behavioral profile (`profile.md`) is injected into every agent's system prompt to provide user context. To prevent unbounded growth:
784
+
785
+ - **Smart consolidation** (threshold: 600 lines): When the profile exceeds this limit during a consolidation pass, the LLM consolidates it — merging duplicate or near-duplicate bullets, removing stale information, and preserving `##` section headers
786
+ - Consolidation targets roughly 400 lines, prioritizing quality and durability of observations
787
+ - All section structure is preserved; only redundant or superseded bullets are removed
788
+
789
+ ### Confidence Tiers
790
+
791
+ | Tier | Range | Meaning | TTL |
792
+ |------|-------|---------|-----|
793
+ | Explicit | 0.95-1.0 | Direct user statement ("I prefer X") | None |
794
+ | Implied | 0.70-0.94 | Strong contextual inference | None |
795
+ | Inferred | 0.40-0.69 | Pattern recognition from limited evidence | None |
796
+ | Speculative | 0.00-0.39 | Tentative hypothesis, needs confirmation | 30 days |
797
+
798
+ ## Development
799
+
800
+ ```bash
801
+ # Watch mode (rebuilds on file changes)
802
+ npm run dev
803
+
804
+ # Type checking
805
+ npm run check-types
806
+
807
+ # Build for production
808
+ npm run build
809
+ ```
810
+
811
+ ### Project Structure
812
+
813
+ ```
814
+ src/
815
+ ├── index.ts # Plugin entry point (hooks, tools, CLI registration)
816
+ ├── orchestrator.ts # Central coordinator (extraction, consolidation, retrieval)
817
+ ├── extraction.ts # OpenAI Responses API client
818
+ ├── storage.ts # File-based storage manager (markdown + YAML frontmatter)
819
+ ├── buffer.ts # Smart buffer with configurable trigger logic
820
+ ├── signal.ts # Local signal detection (regex, zero cost)
821
+ ├── schemas.ts # Zod schemas for structured LLM output
822
+ ├── types.ts # TypeScript type definitions
823
+ ├── config.ts # Config parser with env var resolution
824
+ ├── qmd.ts # QMD CLI client (search, update, collection management)
825
+ ├── tools.ts # Agent tool definitions
826
+ ├── cli.ts # CLI subcommand definitions
827
+ ├── logger.ts # Logging utilities
828
+ ├── chunking.ts # [v1.2.0] Sentence-boundary chunking for long memories
829
+ ├── importance.ts # [v1.2.0] Zero-LLM heuristic importance scoring
830
+ ├── threading.ts # [v1.2.0] Conversation threading with TF-IDF titles
831
+ └── topics.ts # [v1.2.0] TF-IDF topic extraction across corpus
832
+ scripts/
833
+ └── migrate.ts # Migration from Honcho, Supermemory, context files
834
+ ```
835
+
836
+ ## Contributing
837
+
838
+ We welcome both issues and pull requests, including AI-assisted contributions.
839
+
840
+ - Report bugs: open a GitHub issue (use templates)
841
+ - Propose features: open a GitHub issue with scope and expected impact
842
+ - Submit PRs: keep scope focused, include tests, and update the changelog
843
+
844
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for full guidelines, review expectations, and changelog policy.
845
+ See [CONTRIBUTORS.md](CONTRIBUTORS.md) for contributor recognition.
846
+
847
+ **Important:** This is a public repository. Never commit personal data, API keys, memory content, or user-specific configuration. See [CLAUDE.md](CLAUDE.md) and [SECURITY.md](SECURITY.md).
848
+
849
+ ## License
850
+
851
+ MIT