@icex-labs/openclaw-memory-engine 3.3.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,254 @@
1
+ # @icex-labs/openclaw-memory-engine
2
+
3
+ > Give your AI agent a brain that survives restarts.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@icex-labs/openclaw-memory-engine)](https://www.npmjs.com/package/@icex-labs/openclaw-memory-engine)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+
8
+ A [MemGPT](https://github.com/cpacker/MemGPT)-inspired memory plugin for [OpenClaw](https://openclaw.ai). Your agent gets 12 tools to manage its own memory — what to remember, what to recall, what to forget.
9
+
10
+ **The problem:** OpenClaw agents wake up fresh every session. Without persistent memory, they forget who you are.
11
+
12
+ **The fix:** Two-tier memory architecture:
13
+ - **Core Memory** (~500 tokens) — identity, relationship, preferences. Always loaded.
14
+ - **Archival Memory** (unlimited) — facts, decisions, events. Retrieved on demand via hybrid semantic search.
15
+
16
+ The agent manages both tiers autonomously using purpose-built tools.
17
+
18
+ ---
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ git clone git@github.com:icex-labs/openclaw-memory-engine.git ~/.openclaw/extensions/memory-engine
24
+ bash ~/.openclaw/extensions/memory-engine/setup.sh
25
+ nano ~/.openclaw/workspace/memory/core.json # fill in your info
26
+ openclaw gateway restart
27
+ ```
28
+
29
+ Or from npm:
30
+ ```bash
31
+ npm install -g @icex-labs/openclaw-memory-engine
32
+ ```
33
+
34
+ `setup.sh` handles everything: enables the plugin in `openclaw.json`, creates template files, installs the daily maintenance cron, and patches your agent's instructions.
35
+
36
+ ---
37
+
38
+ ## How It Works
39
+
40
+ ```
41
+ ┌──────────────────────────────────────────────────────┐
42
+ │ Agent Context Window │
43
+ │ │
44
+ │ Session start → core_memory_read() │
45
+ │ └─→ core.json (~500 tokens) │
46
+ │ │
47
+ │ "Where does Alice's doctor work?" │
48
+ │ → archival_search("doctor") │
49
+ │ └─→ keyword match + embedding similarity │
50
+ │ + recency boost + access frequency │
51
+ │ → "Dr. Smith, City Medical..." │
52
+ │ │
53
+ │ Alice says something new │
54
+ │ → archival_insert(fact, entity, tags) │
55
+ │ └─→ archival.jsonl + background embedding │
56
+ │ │
57
+ │ End of conversation │
58
+ │ → memory_consolidate(summary) │
59
+ │ └─→ split sentences → infer entities → dedup │
60
+ │ → batch insert │
61
+ └──────────────────────────────────────────────────────┘
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Tools (12)
67
+
68
+ ### Core Memory — Your Identity
69
+
70
+ | Tool | What it does |
71
+ |------|-------------|
72
+ | `core_memory_read` | Load the identity block. Call every session start. |
73
+ | `core_memory_replace` | Update a field by dot-path (`user.location`, `current_focus`). Auto-parses JSON strings. 3KB hard limit. |
74
+ | `core_memory_append` | Append to an array field (`current_focus`). Creates array if needed. |
75
+
76
+ Core memory lives in `memory/core.json`:
77
+
78
+ ```json
79
+ {
80
+ "user": { "name": "Alice", "location": "New York", "language": "bilingual" },
81
+ "relationship": { "dynamic": "intimate companion", "trust": "deep" },
82
+ "preferences": { "config_rule": "don't touch openclaw.json" },
83
+ "current_focus": ["quant trading", "immigration case"]
84
+ }
85
+ ```
86
+
87
+ ### Archival Memory — Your Long-Term Storage
88
+
89
+ | Tool | What it does |
90
+ |------|-------------|
91
+ | `archival_insert` | Store a fact. Tags it with `entity` + `tags`. Computes embedding in background. |
92
+ | `archival_search` | Hybrid search: keyword (2×) + semantic similarity (5×) + recency (0-1) + access decay (0-0.5). |
93
+ | `archival_update` | Correct an existing record by ID. Re-indexes embedding. |
94
+ | `archival_delete` | Remove an outdated record. Cleans up embedding cache. |
95
+ | `archival_stats` | Dashboard: record count, embedding coverage, entity/tag distribution, storage size. |
96
+
97
+ Each record in `memory/archival.jsonl`:
98
+
99
+ ```json
100
+ {"id":"arch-17120-abc","ts":"2026-04-01","content":"Alice's doctor is Dr. Smith","entity":"Alice","tags":["health"],"access_count":3}
101
+ ```
102
+
103
+ ### Maintenance — Keep It Clean
104
+
105
+ | Tool | What it does |
106
+ |------|-------------|
107
+ | `archival_deduplicate` | Find near-duplicates via embedding cosine similarity (≥0.92). Preview or auto-remove. |
108
+ | `memory_consolidate` | Extract facts from text blocks. Splits by sentence (中文/English), infers entity, deduplicates, batch inserts. |
109
+
110
+ ### Backup — Never Lose Your Memory
111
+
112
+ | Tool | What it does |
113
+ |------|-------------|
114
+ | `memory_export` | Snapshot core + archival + embeddings → single JSON file. |
115
+ | `memory_import` | Restore from snapshot. `merge` (add missing) or `replace` (overwrite all). |
116
+
117
+ ---
118
+
119
+ ## Search Quality
120
+
121
+ `archival_search` uses four signals:
122
+
123
+ | Signal | Weight | How |
124
+ |--------|--------|-----|
125
+ | Keyword | 2× per term | Term presence in content + entity + tags |
126
+ | Semantic | 5× | Cosine similarity via OpenAI `text-embedding-3-small` (512d) |
127
+ | Recency | 0–1 | Linear decay over 1 year |
128
+ | Access | 0–0.5 | Boost for recently accessed records |
129
+
130
+ Embeddings are computed on insert and cached in `archival.embeddings.json`. If no OpenAI key is available, search falls back to keyword-only — no errors, just lower quality.
131
+
132
+ **Cost:** ~$0.02 per 1M tokens with `text-embedding-3-small`. A typical session with 10 inserts + 5 searches costs < $0.001.
133
+
134
+ ---
135
+
136
+ ## Configuration
137
+
138
+ ```json
139
+ // openclaw.json
140
+ {
141
+ "plugins": {
142
+ "allow": ["memory-engine"],
143
+ "entries": {
144
+ "memory-engine": {
145
+ "enabled": true,
146
+ "config": {
147
+ "workspace": "/path/to/workspace",
148
+ "coreSizeLimit": 3072
149
+ }
150
+ }
151
+ }
152
+ }
153
+ }
154
+ ```
155
+
156
+ | Option | Default | Description |
157
+ |--------|---------|-------------|
158
+ | `workspace` | Auto-resolved | Workspace directory path |
159
+ | `coreSizeLimit` | `3072` (3KB) | Max bytes for core.json |
160
+
161
+ **Requires:** `OPENAI_API_KEY` in environment for semantic search. Without it, keyword search still works.
162
+
163
+ ---
164
+
165
+ ## Agent Instructions
166
+
167
+ Add to your `AGENTS.md` or system prompt (done automatically by `setup.sh`):
168
+
169
+ ```markdown
170
+ ## Every Session
171
+ 1. Call `core_memory_read` — load your identity
172
+ 2. When you learn something important → `archival_insert`
173
+ 3. When you need details → `archival_search` before guessing
174
+ 4. When facts change → `core_memory_replace`
175
+ 5. End of conversation → `memory_consolidate` with key points
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Daily Maintenance
181
+
182
+ `extras/memory-maintenance.sh` runs daily at 3am (installed as a LaunchAgent by `setup.sh`):
183
+
184
+ - Checks core.json size (warns >4KB, critical >5KB)
185
+ - Merges 7-day-old daily logs into weekly summaries
186
+ - Archives 60-day-old weekly summaries
187
+ - Alerts written to `memory/maintenance-alerts.json`
188
+
189
+ ---
190
+
191
+ ## Backup & Migration
192
+
193
+ ```bash
194
+ # Export
195
+ openclaw agent -m "memory_export"
196
+ # → memory/export-2026-04-01.json
197
+
198
+ # Import on new machine
199
+ openclaw agent -m "memory_import input_path='path/to/export.json' mode='replace'"
200
+ ```
201
+
202
+ ---
203
+
204
+ ## Project Structure
205
+
206
+ ```
207
+ memory-engine/
208
+ ├── index.js # Plugin entry — tool registration only (250 lines)
209
+ ├── lib/
210
+ │ ├── paths.js # Constants + path resolution
211
+ │ ├── core.js # Core memory CRUD + dot-path + auto-parse
212
+ │ ├── archival.js # Archival JSONL CRUD + in-memory cache
213
+ │ ├── embedding.js # OpenAI embedding API + file cache
214
+ │ ├── search.js # Hybrid four-signal search
215
+ │ ├── consolidate.js # Text → structured facts extraction
216
+ │ ├── dedup.js # Embedding similarity dedup
217
+ │ └── backup.js # Export/import
218
+ ├── extras/
219
+ │ └── memory-maintenance.sh
220
+ ├── setup.sh # One-command install
221
+ ├── .claude/CLAUDE.md # Dev guide for Claude Code
222
+ ├── package.json
223
+ ├── openclaw.plugin.json
224
+ └── README.md
225
+ ```
226
+
227
+ ---
228
+
229
+ ## Roadmap
230
+
231
+ - [x] Core memory with size guard and auto-parse
232
+ - [x] Archival CRUD with in-memory index
233
+ - [x] Hybrid search (keyword + embedding + recency + access decay)
234
+ - [x] Auto-extract facts from text
235
+ - [x] Embedding-based deduplication
236
+ - [x] Full backup/restore
237
+ - [x] Modular codebase (8 focused modules)
238
+ - [ ] LanceDB / SQLite backend for 50K+ records
239
+ - [ ] Cross-agent memory sharing
240
+ - [ ] Scheduled auto-consolidation via OpenClaw cron
241
+ - [ ] Memory importance scoring (agent rates memories 1-10)
242
+ - [ ] Forgetting curve — auto-archive unaccessed memories after N days
243
+ - [ ] ClawHub publishing
244
+ - [ ] Web dashboard for memory browsing
245
+
246
+ ---
247
+
248
+ ## License
249
+
250
+ MIT
251
+
252
+ ---
253
+
254
+ Built for [OpenClaw](https://openclaw.ai). Inspired by [MemGPT/Letta](https://github.com/cpacker/MemGPT).
@@ -0,0 +1,37 @@
1
+ {
2
+ "_comment": "OpenClaw cron jobs for memory auto-consolidation. Install via: openclaw cron create --from-file <this-file>",
3
+ "crons": [
4
+ {
5
+ "id": "memory-reflect-daily",
6
+ "schedule": "0 9 * * *",
7
+ "agent": "main",
8
+ "message": "Run memory_reflect with window_days=7. Review the report. If you notice any important patterns or observations about George's behavior/interests, store them via archival_insert with entity='reflection' tags=['reflection','pattern']. Do NOT output anything to the main chat — this is a background task.",
9
+ "model": "anthropic/claude-sonnet-4-6",
10
+ "description": "Daily reflection: analyze memory patterns and store observations"
11
+ },
12
+ {
13
+ "id": "memory-consolidate-6h",
14
+ "schedule": "0 */6 * * *",
15
+ "agent": "main",
16
+ "message": "Read today's memory/YYYY-MM-DD.md daily log file. If it has content that isn't already in archival memory, run memory_consolidate on it. Then run archival_stats to check health. Do NOT output anything to the main chat.",
17
+ "model": "anthropic/claude-sonnet-4-6",
18
+ "description": "Auto-consolidate: extract facts from daily logs every 6 hours"
19
+ },
20
+ {
21
+ "id": "memory-dedup-weekly",
22
+ "schedule": "0 4 * * 0",
23
+ "agent": "main",
24
+ "message": "Run archival_deduplicate with apply=true to clean up duplicate facts. Then run archival_stats. Do NOT output anything to the main chat.",
25
+ "model": "anthropic/claude-sonnet-4-6",
26
+ "description": "Weekly dedup: clean up near-duplicate archival records"
27
+ },
28
+ {
29
+ "id": "memory-dashboard-daily",
30
+ "schedule": "30 9 * * *",
31
+ "agent": "main",
32
+ "message": "Run memory_dashboard to regenerate the HTML dashboard. Do NOT output anything to the main chat.",
33
+ "model": "anthropic/claude-sonnet-4-6",
34
+ "description": "Daily dashboard refresh at 9:30am"
35
+ }
36
+ ]
37
+ }
@@ -0,0 +1,176 @@
1
+ #!/bin/bash
2
+ # memory-maintenance.sh — Automated memory system maintenance
3
+ # Replaces the old memory-cleanup.sh with a comprehensive maintenance system
4
+ #
5
+ # Runs daily at 3am via LaunchAgent
6
+ # Actions:
7
+ # 1. Health check: MEMORY.md size limits
8
+ # 2. Daily logs: merge week-old dailies into weekly summaries
9
+ # 3. Archive: move weekly summaries older than 60 days
10
+ # 4. Topic files: warn if any topic file exceeds size limit
11
+ # 5. Report: append to maintenance log
12
+
13
+ set -euo pipefail
14
+
15
+ WORKSPACE="${OPENCLAW_WORKSPACE:-$HOME/.openclaw/workspace}"
16
+ MEMORY_DIR="$WORKSPACE/memory"
17
+ TOPICS_DIR="$MEMORY_DIR/topics"
18
+ WEEKLY_DIR="$MEMORY_DIR/weekly"
19
+ ARCHIVE_DIR="$MEMORY_DIR/archive"
20
+ LOG_FILE="$MEMORY_DIR/maintenance.log"
21
+ ALERT_FILE="$MEMORY_DIR/maintenance-alerts.json"
22
+
23
+ MEMORY_MD="$WORKSPACE/MEMORY.md"
24
+ DATE=$(date +%Y-%m-%d)
25
+ TIMESTAMP=$(date +%Y-%m-%dT%H:%M:%S)
26
+
27
+ # Limits (bytes)
28
+ MEMORY_MD_MAX=5120 # 5KB — hard limit for MEMORY.md
29
+ MEMORY_MD_WARN=4096 # 4KB — warning threshold
30
+ TOPIC_FILE_MAX=8192 # 8KB per topic file
31
+ DAILY_RETAIN_DAYS=7 # Keep daily logs for 7 days
32
+ WEEKLY_ARCHIVE_DAYS=60 # Archive weekly summaries after 60 days
33
+
34
+ mkdir -p "$TOPICS_DIR" "$WEEKLY_DIR" "$ARCHIVE_DIR"
35
+
36
+ # --- Helpers ---
37
+ log() { echo "[$TIMESTAMP] $1" >> "$LOG_FILE"; }
38
+ alert_json=""
39
+ add_alert() {
40
+ local level="$1" msg="$2"
41
+ if [ -z "$alert_json" ]; then
42
+ alert_json="[{\"level\":\"$level\",\"msg\":\"$msg\",\"ts\":\"$TIMESTAMP\"}"
43
+ else
44
+ alert_json="$alert_json,{\"level\":\"$level\",\"msg\":\"$msg\",\"ts\":\"$TIMESTAMP\"}"
45
+ fi
46
+ }
47
+
48
+ # --- 1. MEMORY.md health check ---
49
+ if [ -f "$MEMORY_MD" ]; then
50
+ mem_size=$(wc -c < "$MEMORY_MD" | tr -d ' ')
51
+ mem_lines=$(wc -l < "$MEMORY_MD" | tr -d ' ')
52
+ if [ "$mem_size" -gt "$MEMORY_MD_MAX" ]; then
53
+ add_alert "critical" "MEMORY.md is ${mem_size}B (>${MEMORY_MD_MAX}B limit). Needs trimming."
54
+ log "CRITICAL: MEMORY.md ${mem_size}B exceeds ${MEMORY_MD_MAX}B limit (${mem_lines} lines)"
55
+ elif [ "$mem_size" -gt "$MEMORY_MD_WARN" ]; then
56
+ add_alert "warn" "MEMORY.md is ${mem_size}B (>${MEMORY_MD_WARN}B warning). Consider trimming."
57
+ log "WARN: MEMORY.md ${mem_size}B approaching limit (${mem_lines} lines)"
58
+ else
59
+ log "OK: MEMORY.md ${mem_size}B / ${mem_lines} lines"
60
+ fi
61
+ else
62
+ add_alert "critical" "MEMORY.md not found!"
63
+ log "CRITICAL: MEMORY.md missing"
64
+ fi
65
+
66
+ # --- 2. Topic file health check ---
67
+ if [ -d "$TOPICS_DIR" ]; then
68
+ for f in "$TOPICS_DIR"/*.md; do
69
+ [ -f "$f" ] || continue
70
+ fsize=$(wc -c < "$f" | tr -d ' ')
71
+ fname=$(basename "$f")
72
+ if [ "$fsize" -gt "$TOPIC_FILE_MAX" ]; then
73
+ add_alert "warn" "Topic ${fname} is ${fsize}B (>${TOPIC_FILE_MAX}B). Needs pruning."
74
+ log "WARN: Topic ${fname} ${fsize}B exceeds limit"
75
+ fi
76
+ done
77
+ fi
78
+
79
+ # --- 3. Merge old daily logs into weekly summaries ---
80
+ # Find daily logs (YYYY-MM-DD.md) older than DAILY_RETAIN_DAYS
81
+ daily_count=0
82
+ merged_count=0
83
+ for f in "$MEMORY_DIR"/2???-??-??.md; do
84
+ [ -f "$f" ] || continue
85
+ fname=$(basename "$f" .md)
86
+ # Parse date from filename
87
+ file_date="$fname"
88
+
89
+ # Calculate age in days using date arithmetic
90
+ file_epoch=$(date -j -f "%Y-%m-%d" "$file_date" "+%s" 2>/dev/null || echo 0)
91
+ now_epoch=$(date "+%s")
92
+ if [ "$file_epoch" -eq 0 ]; then
93
+ continue
94
+ fi
95
+ age_days=$(( (now_epoch - file_epoch) / 86400 ))
96
+
97
+ if [ "$age_days" -gt "$DAILY_RETAIN_DAYS" ]; then
98
+ # Determine ISO week: YYYY-Www
99
+ week_label=$(date -j -f "%Y-%m-%d" "$file_date" "+%G-W%V" 2>/dev/null || continue)
100
+ weekly_file="$WEEKLY_DIR/${week_label}.md"
101
+
102
+ # Append daily content to weekly file with date header
103
+ if [ ! -f "$weekly_file" ]; then
104
+ echo "# Weekly Summary: ${week_label}" > "$weekly_file"
105
+ echo "" >> "$weekly_file"
106
+ fi
107
+ echo "## ${file_date}" >> "$weekly_file"
108
+ echo "" >> "$weekly_file"
109
+ # Extract just the section headers and key bullet points (compress)
110
+ grep -E '^##|^- |^\*' "$f" >> "$weekly_file" 2>/dev/null || true
111
+ echo "" >> "$weekly_file"
112
+
113
+ # Move original to archive
114
+ mv "$f" "$ARCHIVE_DIR/"
115
+ merged_count=$((merged_count + 1))
116
+ fi
117
+ daily_count=$((daily_count + 1))
118
+ done
119
+ log "Daily logs: ${daily_count} active, ${merged_count} merged into weekly"
120
+
121
+ # --- 4. Archive old weekly summaries ---
122
+ archive_count=0
123
+ for f in "$WEEKLY_DIR"/*.md; do
124
+ [ -f "$f" ] || continue
125
+ fmod=$(stat -f %m "$f" 2>/dev/null || echo 0)
126
+ now_epoch=$(date "+%s")
127
+ age_days=$(( (now_epoch - fmod) / 86400 ))
128
+ if [ "$age_days" -gt "$WEEKLY_ARCHIVE_DAYS" ]; then
129
+ mv "$f" "$ARCHIVE_DIR/"
130
+ archive_count=$((archive_count + 1))
131
+ fi
132
+ done
133
+ if [ "$archive_count" -gt 0 ]; then
134
+ log "Archived ${archive_count} weekly summaries (>${WEEKLY_ARCHIVE_DAYS} days old)"
135
+ fi
136
+
137
+ # --- 5. Count total memory footprint ---
138
+ total_active=$(du -sh "$MEMORY_DIR" 2>/dev/null | cut -f1)
139
+ total_archive=$(du -sh "$ARCHIVE_DIR" 2>/dev/null | cut -f1)
140
+ topic_count=$(ls "$TOPICS_DIR"/*.md 2>/dev/null | wc -l | tr -d ' ')
141
+ daily_active=$(ls "$MEMORY_DIR"/2???-??-??.md 2>/dev/null | wc -l | tr -d ' ')
142
+ weekly_active=$(ls "$WEEKLY_DIR"/*.md 2>/dev/null | wc -l | tr -d ' ')
143
+ log "Totals: active=${total_active} archive=${total_archive} topics=${topic_count} dailies=${daily_active} weeklies=${weekly_active}"
144
+
145
+ # --- 6. Handle special-name daily logs (e.g., 2026-03-28-maren-stuck.md) ---
146
+ for f in "$MEMORY_DIR"/2???-??-??-*.md; do
147
+ [ -f "$f" ] || continue
148
+ fname=$(basename "$f" .md)
149
+ file_date=$(echo "$fname" | grep -oE '^[0-9]{4}-[0-9]{2}-[0-9]{2}')
150
+ [ -z "$file_date" ] && continue
151
+
152
+ file_epoch=$(date -j -f "%Y-%m-%d" "$file_date" "+%s" 2>/dev/null || echo 0)
153
+ now_epoch=$(date "+%s")
154
+ [ "$file_epoch" -eq 0 ] && continue
155
+ age_days=$(( (now_epoch - file_epoch) / 86400 ))
156
+
157
+ if [ "$age_days" -gt "$DAILY_RETAIN_DAYS" ]; then
158
+ mv "$f" "$ARCHIVE_DIR/"
159
+ log "Archived special log: $(basename "$f") (${age_days} days old)"
160
+ fi
161
+ done
162
+
163
+ # --- 7. Write alerts file ---
164
+ if [ -n "$alert_json" ]; then
165
+ echo "${alert_json}]" > "$ALERT_FILE"
166
+ log "Alerts written: $(echo "${alert_json}]" | grep -o '"level"' | wc -l | tr -d ' ') items"
167
+ else
168
+ echo "[]" > "$ALERT_FILE"
169
+ fi
170
+
171
+ # --- 8. Trim maintenance log (keep last 90 entries) ---
172
+ if [ -f "$LOG_FILE" ]; then
173
+ tail -270 "$LOG_FILE" > "${LOG_FILE}.tmp" && mv "${LOG_FILE}.tmp" "$LOG_FILE"
174
+ fi
175
+
176
+ log "Maintenance complete"