@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 +851 -0
- package/dist/index.js +11880 -0
- package/dist/index.js.map +1 -0
- package/openclaw.plugin.json +1281 -0
- package/package.json +81 -0
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
|